Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2012-2023 The plumed team
3 : (see the PEOPLE file at the root of the distribution for a list of names)
4 :
5 : See http://www.plumed.org for more information.
6 :
7 : This file is part of plumed, version 2.
8 :
9 : plumed is free software: you can redistribute it and/or modify
10 : it under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation, either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : plumed is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with plumed. If not, see <http://www.gnu.org/licenses/>.
21 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 : #include "FunctionShortcut.h"
23 : #include "FunctionOfScalar.h"
24 : #include "FunctionOfVector.h"
25 : #include "core/ActionRegister.h"
26 : #include "FunctionSetup.h"
27 :
28 : namespace PLMD {
29 : namespace function {
30 :
31 : //+PLUMEDOC FUNCTION SORT
32 : /*
33 : This function can be used to sort colvars according to their magnitudes.
34 :
35 : This action sorts the input arguments according to their magnitudes. It will output the same number of values as it has arguments.
36 : The lowest argument input will be output as a value labelled _label_.1, the second lowest input argument will be output as a value labelled _label_.2 and so on.
37 : Thus, for example, the following input can be used to print the distance of the closest and of the farthest atoms to atom 1, chosen among atoms from 2 to 5
38 :
39 : ```plumed
40 : d12: DISTANCE ATOMS=1,2
41 : d13: DISTANCE ATOMS=1,3
42 : d14: DISTANCE ATOMS=1,4
43 : d15: DISTANCE ATOMS=1,5
44 : sort: SORT ARG=d12,d13,d14,d15
45 : PRINT ARG=sort.1,sort.4
46 : ```
47 :
48 : Notice that you can also achieve the same result using the following input:
49 :
50 : ```plumed
51 : d: DISTANCE ATOMS1=1,2 ATOMS2=1,3 ATOMS3=1,4 ATOMS4=1,5
52 : sort: SORT ARG=d
53 : PRINT ARG=sort.1,sort.4
54 : ```
55 :
56 : In this second case the four distances are passed to the SORT action as a vector. The SORT action then outputs 4 components - the same number of components as there
57 : are elements in the input vector - that contain the elements of the input vector in order of increasing magnitude.
58 :
59 : These examples are representative the only two ways you can use this action. In input it can accept either a list of scalars or a single vector.
60 : It does not accept matrices or a list of vectors in input.
61 :
62 : ## Using multiple vectors in input
63 :
64 : If you input multiple vectors with the same numbers of elements to this action, as shown below, the output will be a set of vectors.
65 :
66 : ```plumed
67 : d1: DISTANCE ATOMS1=1,2 ATOMS2=3,4
68 : d2: DISTANCE ATOMS1=5,6 ATOMS2=7,8
69 : d3: DISTANCE ATOMS1=9,10 ATOMS2=11,12
70 : sort: SORT ARG=d1,d2,d3
71 : PRINT ARG=sort.1,sort.2,sort.3 FILE=colvar
72 : ```
73 :
74 : The elements of the three output vector here are determined by doing an elementwise comparison of the elements in the input vectors. In the above
75 : input the first element of `sort.1` is equal to the distance between atoms 1 and 2 if this distance is smaller than the distances between atoms 5 and 6 and the distance between atoms 9 and 10.
76 : By the same token the second element of `sort.1` is equal to the distance between atoms 3 and 4 if this is smaller than the distance between atoms 7 and 8 and the distance between atoms 11 and 12.
77 : The elements of `sort.2` are the second smallest of these distances, while the elements of `sort.3` are the largest distances.
78 :
79 : Notice that you can also use a combination of scalars and vectors in the input to this action as shown below:
80 :
81 : ```plumed
82 : c: CONSTANT VALUE=0.5
83 : d: DISTANCE ATOMS1=1,2 ATOMS2=3,4
84 : sort: SORT ARG=d,c
85 : PRINT ARG=sort.1,sort.2 FILE=colvar
86 : ```
87 :
88 : For the input above the SORT action outputs two vectors with two elements. The elements of `sort.1` are equal to the distances between the pairs
89 : of atoms that are specified in the DISTANCE command as long as those distances are less than 0.5 nm. If either of the two input distances is more
90 : than 0.5 nm then the corresponding value in the vector `h` is set equal to 0.5 nm. Similarly, the elements of `sort.2` are equal to the distances between
91 : of the atoms that are specified in the DISTANCE command as long as those distances are more than 0.5 nm.
92 :
93 : ## The MASK keyword
94 :
95 : You should only use the MASK keyword for this action if you are using multiple arguments and at least one vector in the input to this action.
96 : The following input illustrates how this keyword works:
97 :
98 : ```plumed
99 : m: CONSTANT VALUES=1,0
100 : d1: DISTANCE ATOMS1=1,2 ATOMS2=3,4
101 : d2: DISTANCE ATOMS1=5,6 ATOMS2=7,8
102 : d3: DISTANCE ATOMS1=9,10 ATOMS2=11,12
103 : sort: SORT ARG=d1,d2,d3 MASK=m
104 : ```
105 :
106 : By using the MASK keyword here you ensure that only the first elements of the output vectors for `sort` is calculated. These elements of the output vectors
107 : are evaluated as the corresponding element of the vector that is passed in the input to the MASK keyword is one. By the same logic the second element
108 : of the output vectors are not computed because the corresponding element in the mask vector is 0.
109 :
110 : The input above illustrates the key idea for the MASK keyword and is probably not very useful. To see an example where using a MASK for this type of action
111 : is useful you should look at the expanded shortcuts in the documentation for [PARABETARMSD](PARABETARMSD.md).
112 :
113 : */
114 : //+ENDPLUMEDOC
115 :
116 : class Sort {
117 : public:
118 : static void registerKeywords(Keywords& keys);
119 : static void read( Sort& func, ActionWithArguments* action, FunctionOptions& options );
120 : static void calc( const Sort& func, bool noderiv, View<const double> args, FunctionOutput& funcout );
121 : };
122 :
123 : typedef FunctionShortcut<Sort> SortShortcut;
124 : PLUMED_REGISTER_ACTION(SortShortcut,"SORT")
125 : typedef FunctionOfScalar<Sort> ScalarSort;
126 : PLUMED_REGISTER_ACTION(ScalarSort,"SORT_SCALAR")
127 : typedef FunctionOfVector<Sort> VectorSort;
128 : PLUMED_REGISTER_ACTION(VectorSort,"SORT_VECTOR")
129 :
130 80 : void Sort::registerKeywords(Keywords& keys) {
131 160 : keys.setValueDescription("vector","sorted");
132 80 : keys.setComponentsIntroduction("The names of the components in this action will be customized in accordance with the contents of the input file. "
133 : "The smallest value is called label.1, the second smallest label.2, the third smallest label.3 and so on");
134 80 : }
135 :
136 :
137 14 : void Sort::read( Sort& func, ActionWithArguments* action, FunctionOptions& options ) {
138 14 : if( action->getNumberOfArguments()==1 ) {
139 16 : action->warning("if there is only one argument to sort is this function really needed?");
140 : }
141 :
142 14 : options.derivativeZeroIfValueIsZero=true;
143 42 : for(unsigned i=0; i<action->getNumberOfArguments(); ++i) {
144 29 : if((action->getPntrToArgument(i))->isPeriodic()) {
145 2 : action->error("Cannot sort periodic values (check argument "+ (action->getPntrToArgument(i))->getName() +")");
146 : }
147 28 : if(!(action->getPntrToArgument(i))->isDerivativeZeroWhenValueIsZero() ) {
148 26 : options.derivativeZeroIfValueIsZero=false;
149 : }
150 : }
151 13 : }
152 :
153 12 : void Sort::calc( const Sort& func, bool noderiv, const View<const double> args, FunctionOutput& funcout ) {
154 12 : if( !noderiv ) {
155 45 : for(unsigned i=0; i<args.size(); ++i) {
156 160 : for(unsigned j=0; j<args.size(); ++j) {
157 125 : funcout.derivs[i][j] = 0;
158 : }
159 : }
160 : }
161 12 : std::vector<std::pair<double,int> > data(args.size());
162 57 : for(unsigned i=0; i<args.size(); ++i) {
163 45 : data[i].first=args[i];
164 : // In this manner I remember from which argument the component depends:
165 45 : data[i].second=i;
166 : }
167 : // STL sort sorts based on first element (value) then second (index)
168 12 : std::sort(data.begin(),data.end());
169 57 : for(unsigned i=0; i<funcout.values.size(); ++i) {
170 45 : funcout.values[i] = data[i].first;
171 45 : if( !noderiv ) {
172 35 : funcout.derivs[i][data[i].second] = 1;
173 : }
174 : }
175 12 : }
176 :
177 : }
178 : }
179 :
180 :
|