Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2017-2020 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 "core/ActionRegister.h"
23 : #include "tools/Pbc.h"
24 : #include "tools/SwitchingFunction.h"
25 : #include "tools/LinkCells.h"
26 : #include "ActionVolume.h"
27 : #include "VolumeShortcut.h"
28 : #include <memory>
29 :
30 : //+PLUMEDOC VOLUMES INENVELOPE
31 : /*
32 : This quantity can be used to calculate functions of the distribution of collective variables for the atoms that lie in a region where the density of a certain type of atom is high.
33 :
34 : This collective variable can be used to determine whether colvars are within region where the density
35 : of a particular atom is high. This is achieved by calculating the following function at the point where
36 : the atom is located \f$(x,y,z)\f$:
37 :
38 : \f[
39 : w_j = 1 - \sigma\left[ \sum_{i=1}^N K\left( \frac{x-x_i}{\sigma_x},\frac{y-y_i}{\sigma_y},\frac{z-z_i}{\sigma_z} \right) \right]
40 : \f]
41 :
42 : Here \f$\sigma\f$ is a \ref switchingfunction and \f$K\f$ is a \ref kernelfunctions. The sum runs over the atoms
43 : specified using the ATOMS keyword and a \f$w_j\f$ value is calculated for each of the central atoms of the input
44 : multicolvar.
45 :
46 : \par Examples
47 :
48 : The input below calculates a density field from the positions of atoms 1-14400. The number of the atoms
49 : that are specified in the DENSITY action that are within a region where the density field is greater than
50 : 2.0 is then calculated.
51 :
52 : \plumedfile
53 : d1: DENSITY SPECIES=14401-74134:3 LOWMEM
54 : fi: INENVELOPE DATA=d1 ATOMS=1-14400 CONTOUR={RATIONAL D_0=2.0 R_0=1.0} BANDWIDTH=0.1,0.1,0.1 LOWMEM
55 : PRINT ARG=fi FILE=colvar
56 : \endplumedfile
57 :
58 : */
59 : //+ENDPLUMEDOC
60 :
61 : //+PLUMEDOC VOLUMES INENVELOPE_CALC
62 : /*
63 : This quantity can be used to calculate functions of the distribution of collective variables for the atoms that lie in a region where the density of a certain type of atom is high.
64 :
65 : \par Examples
66 :
67 : */
68 : //+ENDPLUMEDOC
69 :
70 : namespace PLMD {
71 : namespace volumes {
72 :
73 : class VolumeInEnvelope : public ActionVolume {
74 : private:
75 : LinkCells mylinks;
76 : double gvol;
77 : std::vector<std::unique_ptr<Value>> pos;
78 : std::vector<Vector> ltmp_pos;
79 : std::vector<unsigned> ltmp_ind;
80 : std::vector<double> bandwidth;
81 : SwitchingFunction sfunc, switchingFunction;
82 : public:
83 : static void registerKeywords( Keywords& keys );
84 : explicit VolumeInEnvelope(const ActionOptions& ao);
85 : void setupRegions() override;
86 : double calculateNumberInside( const Vector& cpos, Vector& derivatives, Tensor& vir, std::vector<Vector>& refders ) const override;
87 : };
88 :
89 : PLUMED_REGISTER_ACTION(VolumeInEnvelope,"INENVELOPE_CALC")
90 : char glob_contours[] = "INENVELOPE";
91 : typedef VolumeShortcut<glob_contours> VolumeInEnvelopeShortcut;
92 : PLUMED_REGISTER_ACTION(VolumeInEnvelopeShortcut,"INENVELOPE")
93 :
94 7 : void VolumeInEnvelope::registerKeywords( Keywords& keys ) {
95 7 : ActionVolume::registerKeywords( keys );
96 7 : keys.remove("SIGMA");
97 7 : keys.setDisplayName("INENVELOPE");
98 14 : keys.add("atoms","FIELD_ATOMS","the atom whose positions we are constructing a field from");
99 14 : keys.add("compulsory","BANDWIDTH","the bandwidths for kernel density esimtation");
100 14 : keys.add("compulsory","CONTOUR","a switching funciton that tells PLUMED how large the density should be");
101 14 : keys.add("compulsory","CUTOFF","6.25","the cutoff at which to stop evaluating the kernel functions is set equal to sqrt(2*x)*bandwidth in each direction where x is this number");
102 7 : }
103 :
104 1 : VolumeInEnvelope::VolumeInEnvelope(const ActionOptions& ao):
105 : Action(ao),
106 : ActionVolume(ao),
107 1 : mylinks(comm) {
108 : std::vector<AtomNumber> atoms;
109 1 : parseAtomList("FIELD_ATOMS",atoms);
110 1 : log.printf(" creating density field from atoms : ");
111 9 : for(unsigned i=0; i<atoms.size(); ++i) {
112 8 : log.printf("%d ",atoms[i].serial() );
113 : }
114 1 : log.printf("\n");
115 1 : ltmp_ind.resize( atoms.size() );
116 1 : ltmp_pos.resize( atoms.size() );
117 9 : for(unsigned i=0; i<atoms.size(); ++i) {
118 8 : ltmp_ind[i]=i;
119 : }
120 :
121 : std::string sw, errors;
122 2 : parse("CONTOUR",sw);
123 1 : if(sw.length()==0) {
124 0 : error("missing CONTOUR keyword");
125 : }
126 1 : sfunc.set(sw,errors);
127 1 : if( errors.length()!=0 ) {
128 0 : error("problem reading RADIUS keyword : " + errors );
129 : }
130 1 : log.printf(" density at atom must be larger than %s \n", ( sfunc.description() ).c_str() );
131 :
132 1 : std::vector<double> pp(3,0.0);
133 1 : bandwidth.resize(3);
134 1 : parseVector("BANDWIDTH",bandwidth);
135 2 : log.printf(" using %s kernel with bandwidths %f %f %f \n",getKernelType().c_str(),bandwidth[0],bandwidth[1],bandwidth[2] );
136 : std::string errors2;
137 2 : switchingFunction.set("GAUSSIAN R_0=1.0 NOSTRETCH", errors2 );
138 1 : if( errors2.length()!=0 ) {
139 0 : error("problem reading switching function description " + errors2);
140 : }
141 : double det=1;
142 4 : for(unsigned i=0; i<bandwidth.size(); ++i) {
143 3 : det*=bandwidth[i]*bandwidth[i];
144 : }
145 1 : gvol=1.0;
146 1 : if( getKernelType()=="gaussian" ) {
147 1 : gvol=pow( 2*pi, 0.5*bandwidth.size() ) * pow( det, 0.5 );
148 : } else {
149 0 : error("cannot use kernel other than gaussian");
150 : }
151 : double dp2cutoff;
152 1 : parse("CUTOFF",dp2cutoff);
153 1 : double maxs = sqrt(2*dp2cutoff)*bandwidth[0];
154 3 : for(unsigned j=1; j<bandwidth.size(); ++j) {
155 2 : if( sqrt(2*dp2cutoff)*bandwidth[j]>maxs ) {
156 0 : maxs=sqrt(2*dp2cutoff)*bandwidth[j];
157 : }
158 : }
159 1 : checkRead();
160 1 : requestAtoms(atoms);
161 1 : mylinks.setCutoff( maxs );
162 1 : }
163 :
164 5 : void VolumeInEnvelope::setupRegions() {
165 45 : for(unsigned i=0; i<ltmp_ind.size(); ++i) {
166 40 : ltmp_pos[i]=getPosition(i);
167 : }
168 5 : mylinks.buildCellLists( ltmp_pos, ltmp_ind, getPbc() );
169 5 : }
170 :
171 1000 : double VolumeInEnvelope::calculateNumberInside( const Vector& cpos, Vector& derivatives, Tensor& vir, std::vector<Vector>& refders ) const {
172 1000 : unsigned ncells_required=0, natoms=1;
173 1000 : std::vector<unsigned> cells_required( mylinks.getNumberOfCells() ), indices( 1 + getNumberOfAtoms() );
174 1000 : mylinks.addRequiredCells( mylinks.findMyCell( cpos ), ncells_required, cells_required );
175 1000 : indices[0]=getNumberOfAtoms();
176 1000 : mylinks.retrieveAtomsInCells( ncells_required, cells_required, natoms, indices );
177 : double value=0;
178 1000 : std::vector<double> der(3);
179 1000 : Vector tder;
180 :
181 : // convert pointer once
182 1000 : auto pos_ptr=Tools::unique2raw(pos);
183 9000 : for(unsigned i=1; i<natoms; ++i) {
184 8000 : Vector dist = pbcDistance( cpos, getPosition( indices[i] ) );
185 : double dval=0;
186 32000 : for(unsigned j=0; j<3; ++j) {
187 24000 : der[j] = dist[j]/bandwidth[j];
188 24000 : dval += der[j]*der[j];
189 24000 : der[j] = der[j] / bandwidth[j];
190 : }
191 : double dfunc;
192 8000 : value += switchingFunction.calculateSqr( dval, dfunc ) / gvol;
193 8000 : double tmp = dfunc / gvol;
194 32000 : for(unsigned j=0; j<3; ++j) {
195 24000 : derivatives[j] -= tmp*der[j];
196 24000 : refders[ indices[i] ][j] += tmp*der[j];
197 24000 : tder[j]=tmp*der[j];
198 : }
199 8000 : vir -= Tensor( tder, dist );
200 : }
201 1000 : double deriv, fval = sfunc.calculate( value, deriv );
202 1000 : derivatives *= -deriv*value;
203 1000 : vir *= -deriv*value;
204 9000 : for(unsigned i=1; i<natoms; ++i) {
205 8000 : refders[ indices[i] ] *= -deriv*value;
206 : }
207 2000 : return 1.0 - fval;
208 : }
209 :
210 : }
211 : }
|