1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 Copyright (c) 2012-2021 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 "MultiColvarBase.h"
23 #include "AtomValuePack.h"
24 #include "core/ActionRegister.h"
25 #include "vesselbase/LessThan.h"
26 #include "vesselbase/Between.h"
27
28 #include <string>
29 #include <cmath>
30
31 using namespace std;
32
33 namespace PLMD {
34 namespace multicolvar {
35
36 //+PLUMEDOC MCOLVAR DISTANCES
37 /*
38 Calculate the distances between one or many pairs of atoms. You can then calculate functions of the distribution of
39 distances such as the minimum, the number less than a certain quantity and so on.
40
41 \par Examples
42
43 The following input tells plumed to calculate the distances between atoms 3 and 5 and between atoms 1 and 2 and to
44 print the minimum for these two distances.
45 \plumedfile
46 d1: DISTANCES ATOMS1=3,5 ATOMS2=1,2 MIN={BETA=0.1}
47 PRINT ARG=d1.min
48 \endplumedfile
49 (See also \ref PRINT).
50
51 The following input tells plumed to calculate the distances between atoms 3 and 5 and between atoms 1 and 2
52 and then to calculate the number of these distances that are less than 0.1 nm. The number of distances
53 less than 0.1nm is then printed to a file.
54 \plumedfile
55 d1: DISTANCES ATOMS1=3,5 ATOMS2=1,2 LESS_THAN={RATIONAL R_0=0.1}
56 PRINT ARG=d1.lessthan
57 \endplumedfile
58 (See also \ref PRINT \ref switchingfunction).
59
60 The following input tells plumed to calculate all the distances between atoms 1, 2 and 3 (i.e. the distances between atoms
61 1 and 2, atoms 1 and 3 and atoms 2 and 3). The average of these distances is then calculated.
62 \plumedfile
63 d1: DISTANCES GROUP=1-3 MEAN
64 PRINT ARG=d1.mean
65 \endplumedfile
66 (See also \ref PRINT)
67
68 The following input tells plumed to calculate all the distances between the atoms in GROUPA and the atoms in GROUPB.
69 In other words the distances between atoms 1 and 2 and the distance between atoms 1 and 3. The number of distances
70 more than 0.1 is then printed to a file.
71 \plumedfile
72 d1: DISTANCES GROUPA=1 GROUPB=2,3 MORE_THAN={RATIONAL R_0=0.1}
73 PRINT ARG=d1.morethan
74 \endplumedfile
75 (See also \ref PRINT \ref switchingfunction)
76
77
78 \par Calculating minimum distances
79
80 To calculate and print the minimum distance between two groups of atoms you use the following commands
81
82 \plumedfile
83 d1: DISTANCES GROUPA=1-10 GROUPB=11-20 MIN={BETA=500.}
84 PRINT ARG=d1.min FILE=colvar STRIDE=10
85 \endplumedfile
86 (see \ref DISTANCES and \ref PRINT)
87
88 In order to ensure that the minimum value has continuous derivatives we use the following function:
89
90 \f[
91 s = \frac{\beta}{ \log \sum_i \exp\left( \frac{\beta}{s_i} \right) }
92 \f]
93
94 where \f$\beta\f$ is a user specified parameter.
95
96 This input is used rather than a separate MINDIST colvar so that the same routine and the same input style can be
97 used to calculate minimum coordination numbers (see \ref COORDINATIONNUMBER), minimum
98 angles (see \ref ANGLES) and many other variables.
99
100 This new way of calculating mindist is part of plumed 2's multicolvar functionality. These special actions
101 allow you to calculate multiple functions of a distribution of simple collective variables. As an example you
102 can calculate the number of distances less than 1.0, the minimum distance, the number of distances more than
103 2.0 and the number of distances between 1.0 and 2.0 by using the following command:
104
105 \plumedfile
106 d1: DISTANCES ...
107 GROUPA=1-10 GROUPB=11-20
108 LESS_THAN={RATIONAL R_0=1.0}
109 MORE_THAN={RATIONAL R_0=2.0}
110 BETWEEN={GAUSSIAN LOWER=1.0 UPPER=2.0}
111 MIN={BETA=500.}
112 ...
113 PRINT ARG=d1.lessthan,d1.morethan,d1.between,d1.min FILE=colvar STRIDE=10
114 \endplumedfile
115 (see \ref DISTANCES and \ref PRINT)
116
117 A calculation performed this way is fast because the expensive part of the calculation - the calculation of all the distances - is only
118 done once per step. Furthermore, it can be made faster by using the TOL keyword to discard those distance that make only a small contributions
119 to the final values together with the NL_STRIDE keyword, which ensures that the distances that make only a small contribution to the final values aren't
120 calculated at every step.
121
122 */
123 //+ENDPLUMEDOC
124
125
126 class Distances : public MultiColvarBase {
127 private:
128 public:
129 static void registerKeywords( Keywords& keys );
130 explicit Distances(const ActionOptions&);
131 // active methods:
132 double compute( const unsigned& tindex, AtomValuePack& myatoms ) const override;
133 /// Returns the number of coordinates of the field
isPeriodic()134 bool isPeriodic() override { return false; }
135 };
136
137 PLUMED_REGISTER_ACTION(Distances,"DISTANCES")
138
registerKeywords(Keywords & keys)139 void Distances::registerKeywords( Keywords& keys ) {
140 MultiColvarBase::registerKeywords( keys );
141 keys.use("ALT_MIN"); keys.use("LOWEST"); keys.use("HIGHEST");
142 keys.use("MEAN"); keys.use("MIN"); keys.use("MAX"); keys.use("LESS_THAN"); // keys.use("DHENERGY");
143 keys.use("MORE_THAN"); keys.use("BETWEEN"); keys.use("HISTOGRAM"); keys.use("MOMENTS");
144 keys.add("numbered","ATOMS","the atoms involved in each of the distances you wish to calculate. "
145 "Keywords like ATOMS1, ATOMS2, ATOMS3,... should be listed and one distance will be "
146 "calculated for each ATOM keyword you specify (all ATOM keywords should "
147 "specify the indices of two atoms). The eventual number of quantities calculated by this "
148 "action will depend on what functions of the distribution you choose to calculate.");
149 keys.reset_style("ATOMS","atoms");
150 keys.add("atoms-1","GROUP","Calculate the distance between each distinct pair of atoms in the group");
151 keys.add("atoms-2","GROUPA","Calculate the distances between all the atoms in GROUPA and all "
152 "the atoms in GROUPB. This must be used in conjunction with GROUPB.");
153 keys.add("atoms-2","GROUPB","Calculate the distances between all the atoms in GROUPA and all the atoms "
154 "in GROUPB. This must be used in conjunction with GROUPA.");
155 }
156
Distances(const ActionOptions & ao)157 Distances::Distances(const ActionOptions&ao):
158 Action(ao),
159 MultiColvarBase(ao)
160 {
161 // Read in the atoms
162 std::vector<AtomNumber> all_atoms;
163 readTwoGroups( "GROUP", "GROUPA", "GROUPB", all_atoms );
164 if( atom_lab.size()==0 ) readAtomsLikeKeyword( "ATOMS", 2, all_atoms );
165 setupMultiColvarBase( all_atoms );
166 // And check everything has been read in correctly
167 checkRead();
168
169 // Now check if we can use link cells
170 if( getNumberOfVessels()>0 ) {
171 bool use_link=false; double rcut;
172 vesselbase::LessThan* lt=dynamic_cast<vesselbase::LessThan*>( getPntrToVessel(0) );
173 if( lt ) {
174 use_link=true; rcut=lt->getCutoff();
175 } else {
176 vesselbase::Between* bt=dynamic_cast<vesselbase::Between*>( getPntrToVessel(0) );
177 if( bt ) { use_link=true; rcut=bt->getCutoff(); }
178 }
179 if( use_link ) {
180 for(unsigned i=1; i<getNumberOfVessels(); ++i) {
181 vesselbase::LessThan* lt2=dynamic_cast<vesselbase::LessThan*>( getPntrToVessel(i) );
182 vesselbase::Between* bt=dynamic_cast<vesselbase::Between*>( getPntrToVessel(i) );
183 if( lt2 ) {
184 double tcut=lt2->getCutoff();
185 if( tcut>rcut ) rcut=tcut;
186 } else if( bt ) {
187 double tcut=bt->getCutoff();
188 if( tcut>rcut ) rcut=tcut;
189 } else {
190 use_link=false;
191 }
192 }
193 }
194 if( use_link ) setLinkCellCutoff( rcut );
195 }
196 }
197
compute(const unsigned & tindex,AtomValuePack & myatoms) const198 double Distances::compute( const unsigned& tindex, AtomValuePack& myatoms ) const {
199 Vector distance;
200 distance=getSeparation( myatoms.getPosition(0), myatoms.getPosition(1) );
201 const double value=distance.modulo();
202 const double invvalue=1.0/value;
203
204 // And finish the calculation
205 addAtomDerivatives( 1, 0,-invvalue*distance, myatoms );
206 addAtomDerivatives( 1, 1, invvalue*distance, myatoms );
207 myatoms.addBoxDerivatives( 1, -invvalue*Tensor(distance,distance) );
208 return value;
209 }
210
211 }
212 }
213
214