Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) envsim 2023-2024 The code team
3 : (see the PEOPLE-envsim file at the root of the distribution for a list of names)
4 :
5 : This file is part of envsim code module.
6 :
7 : The envsim code module is free software: you can redistribute it and/or modify
8 : it under the terms of the GNU Lesser General Public License as published by
9 : the Free Software Foundation, either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : The envsim code module is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU Lesser General Public License for more details.
16 :
17 : You should have received a copy of the GNU Lesser General Public License
18 : along with the envsim code module. If not, see <http://www.gnu.org/licenses/>.
19 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
20 : #include "core/ActionShortcut.h"
21 : #include "core/ActionRegister.h"
22 : #include "core/ActionWithValue.h"
23 : #include "core/PlumedMain.h"
24 : #include "core/ActionSet.h"
25 : #include "tools/PDB.h"
26 : #include "multicolvar/MultiColvarShortcuts.h"
27 : #include <string>
28 : #include <cmath>
29 :
30 : using namespace std;
31 :
32 : namespace PLMD {
33 : namespace envsim {
34 :
35 : // N.B. In this equation $\tilde{k}_ {\chi_0}(\chi_0)=1$ space after underscore ensures correct rendering.
36 : // I don't know why GAT
37 :
38 : //+PLUMEDOC MCOLVAR ENVIRONMENTSIMILARITY
39 : /*
40 : Measure how similar the environment around atoms is to that found in some reference crystal structure.
41 :
42 : The starting point for the definition of the CV is the local atomic density around an atom.
43 : We consider an environment $\chi$ around this atom and we define the density by
44 :
45 : $$
46 : \rho_{\chi}(\mathbf{r})=\sum\limits_{i\in\chi} \exp\left(- \frac{|r_i-r|^2} {2\sigma^2} \right),
47 : $$
48 :
49 : where $i$ runs over the neighbors in the environment $\chi$, $\sigma$ is a broadening parameter, and $r_i$ are the
50 : coordinates of the neighbors relative to the central atom.
51 : We now define a reference environment or template $\chi_0$ that contains $n$ reference positions $\{r^0_1,...,r^0_n\}$
52 : that describe, for instance, the nearest neighbors in a given lattice.
53 : $\sigma$ is set using the SIGMA keyword and $\chi_0$ is chosen with the CRYSTAL_STRUCTURE keyword.
54 : If only the SPECIES keyword is given then the atoms defined there will be the central and neighboring atoms.
55 : If instead the SPECIESA and SPECIESB keywords are given then SPECIESA determines the central atoms and SPECIESB the neighbors.
56 :
57 : The environments $\chi$ and $\chi_0$ are compared using the kernel,
58 :
59 : $$
60 : k_{\chi_0}(\chi)= \int d\mathbf{r} \rho_{\chi}(\mathbf{r}) \rho_{\chi_0}(\mathbf{r}) .
61 : $$
62 :
63 : Combining the two equations above and performing the integration analytically we obtain,
64 :
65 : $$
66 : k_{\chi_0}(\chi)= \sum\limits_{i\in\chi} \sum\limits_{j\in\chi_0} \pi^{3/2} \sigma^3 \exp\left(- \frac{|\mathbf{r}_i-\mathbf{r}^0_j|^2} {4\sigma^2} \right).
67 : $$
68 :
69 : The kernel is finally normalized,
70 :
71 : $$
72 : \tilde{k}_{\chi_0}(\chi) = \frac{1}{n} \sum\limits_{i\in\chi} \sum\limits_{j\in\chi_0} \exp\left( - \frac{|\mathbf{r}_i-\mathbf{r}^0_j|^2} {4\sigma^2} \right),
73 : $$
74 :
75 : such that $\tilde{k}_ {\chi_0}(\chi_0)=1$.
76 : The above kernel is computed for each atom in the SPECIES or SPECIESA keywords.
77 : This quantity is a multicolvar so you can compute it for multiple atoms using a single PLUMED action and then compute
78 : the average value for the atoms in your system, the number of atoms that have an $\tilde{k}_{\chi_0}$ value that is more that some target and
79 : so on.
80 :
81 : The kernel can be generalized to crystal structures described as a lattice with a basis of more than one atom.
82 : In this case there is more than one type of environment.
83 : We consider the case of $M$ environments $X = \chi_1,\chi_2,...,\chi_M$ and we define the kernel through a best match strategy:
84 :
85 :
86 : $$
87 : \tilde{k}_X(\chi)= \frac{1}{\lambda} \log \left ( \sum\limits_{l=1}^{M}\exp \left (\lambda \: \tilde{k}_{\chi_l}(\chi) \right ) \right ).
88 : $$
89 :
90 : For a large enough $\lambda$ this expression will select the largest $\tilde{k}_{\chi_l}(\chi)$ with $\chi_l \in X$.
91 : This approach can be used, for instance, to target the hexagonal closed packed (HCP keyword) or the diamond structure (DIAMOND keyword).
92 :
93 : The CRYSTAL_STRUCTURE keyword can take the values SC (simple cubic), BCC (body centered cubic), FCC (face centered cubic),
94 : HCP (hexagonal closed pack), DIAMOND (cubic diamond), and CUSTOM (user defined).
95 : All options follow the same conventions as in the [lattice command](https://lammps.sandia.gov/doc/lattice.html) of [LAMMPS](https://lammps.sandia.gov/).
96 : If a CRYSTAL_STRUCTURE other than CUSTOM is used, then the lattice constants have to be specified using the keyword LATTICE_CONSTANTS.
97 : One value has to be specified for SC, BCC, FCC, and DIAMOND and two values have to be set for HCP (a and c lattice constants in that order).
98 :
99 : If the CUSTOM option is used then the reference environments have to be specified by the user.
100 : The reference environments are specified in pdb files containing the distance vectors from the central atom to the neighbors.
101 : Make sure your PDB file is correctly formatted as explained in the documenation for [MOLINFO](MOLINFO.md)
102 : If only one reference environment is specified then the filename should be given as argument of the keyword REFERENCE.
103 : If instead several reference environments are given, then they have to be provided in separate pdb files and given as arguments for the
104 : keywords REFERENCE_1, REFERENCE_2, etc.
105 : If you have a reference crystal structure configuration you can use the [Environment Finder](https://github.com/PabloPiaggi/EnvironmentFinder) app to determine the reference environments that you should use.
106 :
107 : If multiple chemical species are involved in the calculation, it is possible to provide the atom types (names) both for atoms in the reference environments and in the simulation box.
108 : This information is provided in pdb files using the atom name field.
109 : The comparison between environments is performed taking into account whether the atom names match.
110 :
111 : ### Examples
112 :
113 : The following input calculates the ENVIRONMENTSIMILARITY kernel for 250 atoms in the system
114 : using the BCC atomic environment as target, and then calculates and prints the average value
115 : for this quantity.
116 :
117 : ```plumed
118 : es: ENVIRONMENTSIMILARITY SPECIES=1-250 SIGMA=0.05 LATTICE_CONSTANTS=0.423 CRYSTAL_STRUCTURE=BCC MEAN
119 :
120 : PRINT ARG=es.mean FILE=COLVAR
121 : ```
122 :
123 : If you want to use a different set of atoms in the environments to the atoms for which you are calculating
124 : the ENVIRONMENTSIMILARITY kernel you use the SPECIESA and SPECIESB keywords as shown below:
125 :
126 : ```plumed
127 : es: ENVIRONMENTSIMILARITY SPECIESA=1-100 SPECIESB=101-250 SIGMA=0.05 LATTICE_CONSTANTS=0.423 CRYSTAL_STRUCTURE=BCC MEAN
128 :
129 : PRINT ARG=es.mean FILE=COLVAR
130 : ```
131 :
132 : The next example compares the environments of the 96 selected atoms with a user specified reference
133 : environment. The reference environment is contained in the env1.pdb file. Once the kernel is computed
134 : the average and the number of atoms with a kernel larger than 0.5 are computed.
135 :
136 : ```plumed
137 : #SETTINGS INPUTFILES=regtest/envsim/rt-env-sim-atom-names-match/env1.pdb
138 : es: ENVIRONMENTSIMILARITY ...
139 : SPECIES=1-288:3
140 : SIGMA=0.05
141 : CRYSTAL_STRUCTURE=CUSTOM
142 : REFERENCE=regtest/envsim/rt-env-sim-atom-names-match/env1.pdb
143 : MEAN
144 : MORE_THAN={RATIONAL R_0=0.5 NN=12 MM=24}
145 : ...
146 :
147 : PRINT ARG=es.mean,es.morethan FILE=COLVAR
148 : ```
149 :
150 : The next example is similar to the one above but in this case 4 reference environments are specified.
151 : Each reference environment is given in a separate pdb file.
152 :
153 : ```plumed
154 : #SETTINGS INPUTFILES=regtest/envsim/rt-env-sim-atom-names-match/env1.pdb,regtest/envsim/rt-env-sim-atom-names-match/env2.pdb,regtest/envsim/rt-env-sim-atom-names-match/env3.pdb,regtest/envsim/rt-env-sim-atom-names-match/env4.pdb
155 : es: ENVIRONMENTSIMILARITY ...
156 : SPECIES=1-288:3
157 : SIGMA=0.05
158 : CRYSTAL_STRUCTURE=CUSTOM
159 : REFERENCE_1=regtest/envsim/rt-env-sim-atom-names-match/env1.pdb
160 : REFERENCE_2=regtest/envsim/rt-env-sim-atom-names-match/env2.pdb
161 : REFERENCE_3=regtest/envsim/rt-env-sim-atom-names-match/env3.pdb
162 : REFERENCE_4=regtest/envsim/rt-env-sim-atom-names-match/env4.pdb
163 : MEAN
164 : MORE_THAN={RATIONAL R_0=0.5 NN=12 MM=24}
165 : ...
166 :
167 : PRINT ARG=es.mean,es.morethan FILE=COLVAR
168 : ```
169 :
170 : The following examples illustrates the use of pdb files to provide information about different chemical species:
171 :
172 :
173 : ```plumed
174 : #SETTINGS INPUTFILES=regtest/envsim/rt-env-sim-custom-1env/env1.pdb,regtest/envsim/rt-env-sim-atom-names-match/IceIh-atom-names.pdb
175 : es: ENVIRONMENTSIMILARITY ...
176 : SPECIES=1-384
177 : SIGMA=0.05
178 : CRYSTAL_STRUCTURE=CUSTOM
179 : REFERENCE=regtest/envsim/rt-env-sim-custom-1env/env1.pdb
180 : MEAN
181 : MORE_THAN={RATIONAL R_0=0.5 NN=12 MM=24}
182 : ATOM_NAMES_FILE=regtest/envsim/rt-env-sim-atom-names-match/IceIh-atom-names.pdb
183 : ...
184 : ```
185 :
186 : In this case, all atoms are used as centers, but only neighbors of type O are taken into account.
187 :
188 : */
189 : //+ENDPLUMEDOC
190 :
191 : class EnvironmentSimilarity : public ActionShortcut {
192 : private:
193 : std::vector<std::pair<unsigned,Vector> > getReferenceEnvironment( const PDB& pdb, const std::vector<std::string>& anames, double& maxdist );
194 : public:
195 : static void registerKeywords( Keywords& keys );
196 : explicit EnvironmentSimilarity(const ActionOptions&);
197 : };
198 :
199 : PLUMED_REGISTER_ACTION(EnvironmentSimilarity,"ENVIRONMENTSIMILARITY")
200 :
201 27 : void EnvironmentSimilarity::registerKeywords( Keywords& keys ) {
202 27 : ActionShortcut::registerKeywords( keys );
203 27 : keys.add("atoms-3","SPECIES","this keyword is used for colvars such as coordination number. In that context it specifies that plumed should calculate "
204 : "one coordination number for each of the atoms specified. Each of these coordination numbers specifies how many of the "
205 : "other specified atoms are within a certain cutoff of the central atom. You can specify the atoms here as another multicolvar "
206 : "action or using a MultiColvarFilter or ActionVolume action. When you do so the quantity is calculated for those atoms specified "
207 : "in the previous multicolvar. This is useful if you would like to calculate the Steinhardt parameter for those atoms that have a "
208 : "coordination number more than four for example");
209 27 : keys.add("atoms-4","SPECIESA","this keyword is used for colvars such as the coordination number. In that context it species that plumed should calculate "
210 : "one coordination number for each of the atoms specified in SPECIESA. Each of these cooordination numbers specifies how many "
211 : "of the atoms specifies using SPECIESB is within the specified cutoff. As with the species keyword the input can also be specified "
212 : "using the label of another multicolvar");
213 27 : keys.add("atoms-4","SPECIESB","this keyword is used for colvars such as the coordination number. It must appear with SPECIESA. For a full explanation see "
214 : "the documentation for that keyword");
215 27 : keys.add("compulsory","CRYSTAL_STRUCTURE","FCC","Targeted crystal structure. Options are: "
216 : "SC: simple cubic, "
217 : "BCC: body center cubic, "
218 : "FCC: face centered cubic, "
219 : "HCP: hexagonal closed pack, "
220 : "DIAMOND: cubic diamond, "
221 : "CUSTOM: user defined "
222 : " ");
223 27 : keys.add("compulsory","LATTICE_CONSTANTS","Lattice constants. Two comma separated values for HCP, "
224 : "one value for all other CRYSTAL_STRUCTURES.");
225 27 : keys.add("compulsory","SIGMA","0.1","the width to use for the gaussian kernels");
226 27 : keys.add("compulsory","LCUTOFF","0.0001","any atoms separated by less than this tolerance should be ignored");
227 27 : keys.add("optional","REFERENCE","PDB files with relative distances from central atom. Use this keyword if you are targeting a single reference environment.");
228 27 : keys.add("numbered","REFERENCE_","PDB files with relative distances from central atom. Each file corresponds to one template. Use these keywords if you are targeting more than one reference environment.");
229 27 : keys.add("compulsory","LAMBDA","100","Lambda parameter. This is only used if you have more than one reference environment");
230 27 : keys.add("compulsory","CUTOFF","3","how many multiples of sigma would you like to consider beyond the maximum distance in the environment");
231 27 : keys.add("optional","ATOM_NAMES_FILE","PDB file with atom names for all atoms in SPECIES. Atoms in reference environments will be compared only if atom names match.");
232 54 : keys.setValueDescription("vector","the environmental similar parameter for each of the input atoms");
233 27 : multicolvar::MultiColvarShortcuts::shortcutKeywords( keys );
234 27 : keys.needsAction("GROUP");
235 27 : keys.needsAction("DISTANCE_MATRIX");
236 27 : keys.needsAction("ONES");
237 27 : keys.needsAction("CONSTANT");
238 27 : keys.needsAction("CUSTOM");
239 27 : keys.needsAction("MATRIX_VECTOR_PRODUCT");
240 27 : keys.needsAction("COMBINE");
241 27 : keys.addDOI("10.1063/1.5102104");
242 27 : }
243 :
244 10 : EnvironmentSimilarity::EnvironmentSimilarity(const ActionOptions&ao):
245 : Action(ao),
246 10 : ActionShortcut(ao) {
247 : std::string atomNamesFile;
248 10 : parse("ATOM_NAMES_FILE",atomNamesFile);
249 10 : PDB atomnamepdb;
250 10 : if( !atomNamesFile.empty() && !atomnamepdb.read(atomNamesFile,usingNaturalUnits(),0.1/getUnits().getLength()) ) {
251 0 : error("missing input file " + atomNamesFile);
252 : }
253 :
254 10 : double maxdist=0;
255 10 : std::vector<std::string> allspec(1);
256 : std::string crystal_structure;
257 20 : parse("CRYSTAL_STRUCTURE", crystal_structure);
258 : std::vector<std::vector<std::pair<unsigned,Vector> > > environments;
259 10 : if( crystal_structure=="CUSTOM" ) {
260 5 : if( !atomNamesFile.empty() ) {
261 1 : allspec[0]=atomnamepdb.getAtomName(atomnamepdb.getAtomNumbers()[0]);
262 1 : unsigned natoms=atomnamepdb.getPositions().size();
263 385 : for(unsigned i=0; i<natoms; ++i) {
264 : bool found=false;
265 576 : for(unsigned j=0; j<allspec.size(); ++j) {
266 575 : if( allspec[j]==atomnamepdb.getAtomName(atomnamepdb.getAtomNumbers()[i] ) ) {
267 : found=true;
268 : break;
269 : }
270 : }
271 384 : if( !found ) {
272 2 : allspec.push_back( atomnamepdb.getAtomName(atomnamepdb.getAtomNumbers()[i]) );
273 : }
274 : }
275 : }
276 : std::string reffile;
277 10 : parse("REFERENCE",reffile);
278 5 : if( reffile.length()>0 ) {
279 2 : PDB pdb;
280 2 : pdb.read(reffile,plumed.usingNaturalUnits(),0.1/plumed.getUnits().getLength());
281 2 : environments.push_back( getReferenceEnvironment( pdb, allspec, maxdist ) );
282 2 : log.printf(" reading %d reference vectors from %s \n", environments[0].size(), reffile.c_str() );
283 2 : } else {
284 12 : for(unsigned int i=1;; i++) {
285 15 : PDB pdb;
286 30 : if( !parseNumbered("REFERENCE_",i,reffile) ) {
287 : break;
288 : }
289 12 : if( !pdb.read(reffile,usingNaturalUnits(),0.1/getUnits().getLength()) ) {
290 0 : error("missing input file " + reffile );
291 : }
292 12 : environments.push_back( getReferenceEnvironment( pdb, allspec, maxdist ) );
293 12 : log.printf(" Reference environment %d : reading %d reference vectors from %s \n", i, environments[i-1].size(), reffile.c_str() );
294 15 : }
295 : }
296 : } else {
297 : std::vector<double> lattice_constants;
298 10 : parseVector("LATTICE_CONSTANTS", lattice_constants);
299 5 : if (crystal_structure == "FCC") {
300 1 : if (lattice_constants.size() != 1) {
301 0 : error("Number of LATTICE_CONSTANTS arguments must be one for FCC");
302 : }
303 1 : environments.resize(1);
304 1 : environments[0].resize(12);
305 1 : environments[0][0] = std::pair<unsigned,Vector>( 0, Vector(+0.5,+0.5,+0.0)*lattice_constants[0] );
306 1 : environments[0][1] = std::pair<unsigned,Vector>( 0, Vector(-0.5,-0.5,+0.0)*lattice_constants[0] );
307 1 : environments[0][2] = std::pair<unsigned,Vector>( 0, Vector(+0.5,-0.5,+0.0)*lattice_constants[0] );
308 1 : environments[0][3] = std::pair<unsigned,Vector>( 0, Vector(-0.5,+0.5,+0.0)*lattice_constants[0] );
309 1 : environments[0][4] = std::pair<unsigned,Vector>( 0, Vector(+0.5,+0.0,+0.5)*lattice_constants[0] );
310 1 : environments[0][5] = std::pair<unsigned,Vector>( 0, Vector(-0.5,+0.0,-0.5)*lattice_constants[0] );
311 1 : environments[0][6] = std::pair<unsigned,Vector>( 0, Vector(-0.5,+0.0,+0.5)*lattice_constants[0] );
312 1 : environments[0][7] = std::pair<unsigned,Vector>( 0, Vector(+0.5,+0.0,-0.5)*lattice_constants[0] );
313 1 : environments[0][8] = std::pair<unsigned,Vector>( 0, Vector(+0.0,+0.5,+0.5)*lattice_constants[0] );
314 1 : environments[0][9] = std::pair<unsigned,Vector>( 0, Vector(+0.0,-0.5,-0.5)*lattice_constants[0] );
315 1 : environments[0][10] = std::pair<unsigned,Vector>( 0, Vector(+0.0,-0.5,+0.5)*lattice_constants[0] );
316 1 : environments[0][11] = std::pair<unsigned,Vector>( 0, Vector(+0.0,+0.5,-0.5)*lattice_constants[0] );
317 1 : maxdist = std::sqrt(2)*lattice_constants[0]/2.;
318 4 : } else if (crystal_structure == "SC") {
319 0 : if (lattice_constants.size() != 1) {
320 0 : error("Number of LATTICE_CONSTANTS arguments must be one for SC");
321 : }
322 0 : environments.resize(1);
323 0 : environments[0].resize(6);
324 0 : environments[0][0] = std::pair<unsigned,Vector>( 0, Vector(+1.0,+0.0,+0.0)*lattice_constants[0] );
325 0 : environments[0][1] = std::pair<unsigned,Vector>( 0, Vector(-1.0,+0.0,+0.0)*lattice_constants[0] );
326 0 : environments[0][2] = std::pair<unsigned,Vector>( 0, Vector(+0.0,+1.0,+0.0)*lattice_constants[0] );
327 0 : environments[0][3] = std::pair<unsigned,Vector>( 0, Vector(+0.0,-1.0,+0.0)*lattice_constants[0] );
328 0 : environments[0][4] = std::pair<unsigned,Vector>( 0, Vector(+0.0,+0.0,+1.0)*lattice_constants[0] );
329 0 : environments[0][5] = std::pair<unsigned,Vector>( 0, Vector(+0.0,+0.0,-1.0)*lattice_constants[0] );
330 0 : maxdist = lattice_constants[0];
331 4 : } else if( crystal_structure == "BCC") {
332 2 : if (lattice_constants.size() != 1) {
333 0 : error("Number of LATTICE_CONSTANTS arguments must be one for BCC");
334 : }
335 2 : environments.resize(1);
336 2 : environments[0].resize(14);
337 2 : environments[0][0] = std::pair<unsigned,Vector>( 0, Vector(+0.5,+0.5,+0.5)*lattice_constants[0] );
338 2 : environments[0][1] = std::pair<unsigned,Vector>( 0, Vector(-0.5,-0.5,-0.5)*lattice_constants[0] );
339 2 : environments[0][2] = std::pair<unsigned,Vector>( 0, Vector(-0.5,+0.5,+0.5)*lattice_constants[0] );
340 2 : environments[0][3] = std::pair<unsigned,Vector>( 0, Vector(+0.5,-0.5,+0.5)*lattice_constants[0] );
341 2 : environments[0][4] = std::pair<unsigned,Vector>( 0, Vector(+0.5,+0.5,-0.5)*lattice_constants[0] );
342 2 : environments[0][5] = std::pair<unsigned,Vector>( 0, Vector(-0.5,-0.5,+0.5)*lattice_constants[0] );
343 2 : environments[0][6] = std::pair<unsigned,Vector>( 0, Vector(+0.5,-0.5,-0.5)*lattice_constants[0] );
344 2 : environments[0][7] = std::pair<unsigned,Vector>( 0, Vector(-0.5,+0.5,-0.5)*lattice_constants[0] );
345 2 : environments[0][8] = std::pair<unsigned,Vector>( 0, Vector(+1.0,+0.0,+0.0)*lattice_constants[0] );
346 2 : environments[0][9] = std::pair<unsigned,Vector>( 0, Vector(+0.0,+1.0,+0.0)*lattice_constants[0] );
347 2 : environments[0][10] = std::pair<unsigned,Vector>( 0, Vector(+0.0,+0.0,+1.0)*lattice_constants[0] );
348 2 : environments[0][11] = std::pair<unsigned,Vector>( 0, Vector(-1.0,+0.0,+0.0)*lattice_constants[0] );
349 2 : environments[0][12] = std::pair<unsigned,Vector>( 0, Vector(+0.0,-1.0,+0.0)*lattice_constants[0] );
350 2 : environments[0][13] = std::pair<unsigned,Vector>( 0, Vector(+0.0,+0.0,-1.0)*lattice_constants[0] );
351 2 : maxdist = lattice_constants[0];
352 2 : } else if (crystal_structure == "HCP") {
353 1 : if (lattice_constants.size() != 2) {
354 0 : error("Number of LATTICE_CONSTANTS arguments must be two for HCP");
355 : }
356 1 : environments.resize(2);
357 1 : environments[0].resize(12);
358 1 : environments[1].resize(12);
359 : double sqrt3=std::sqrt(3);
360 1 : environments[0][0] = std::pair<unsigned,Vector>( 0, Vector(+0.5,+sqrt3/2.0,+0.0)*lattice_constants[0] );
361 1 : environments[0][1] = std::pair<unsigned,Vector>( 0, Vector(-0.5,+sqrt3/2.0,+0.0)*lattice_constants[0] );
362 1 : environments[0][2] = std::pair<unsigned,Vector>( 0, Vector(+0.5,-sqrt3/2.0,+0.0)*lattice_constants[0] );
363 1 : environments[0][3] = std::pair<unsigned,Vector>( 0, Vector(-0.5,-sqrt3/2.0,+0.0)*lattice_constants[0] );
364 1 : environments[0][4] = std::pair<unsigned,Vector>( 0, Vector(+1.0,+0.0,+0.0) *lattice_constants[0] );
365 1 : environments[0][5] = std::pair<unsigned,Vector>( 0, Vector(-1.0,+0.0,+0.0) *lattice_constants[0] );
366 1 : environments[0][6] = std::pair<unsigned,Vector>( 0, Vector(+0.5,+sqrt3/6.0,+0.0)*lattice_constants[0] + Vector(+0.0,+0.0,+0.5)*lattice_constants[1] );
367 1 : environments[0][7] = std::pair<unsigned,Vector>( 0, Vector(-0.5,+sqrt3/6.0,+0.0)*lattice_constants[0] + Vector(+0.0,+0.0,+0.5)*lattice_constants[1] );
368 1 : environments[0][8] = std::pair<unsigned,Vector>( 0, Vector(+0.0,-sqrt3/3.0,+0.0)*lattice_constants[0] + Vector(+0.0,+0.0,+0.5)*lattice_constants[1] );
369 1 : environments[0][9] = std::pair<unsigned,Vector>( 0, Vector(+0.5,+sqrt3/6.0,+0.0)*lattice_constants[0] + Vector(+0.0,+0.0,-0.5)*lattice_constants[1] );
370 1 : environments[0][10] = std::pair<unsigned,Vector>( 0, Vector(-0.5,+sqrt3/6.0,+0.0)*lattice_constants[0] + Vector(+0.0,+0.0,-0.5)*lattice_constants[1] );
371 1 : environments[0][11] = std::pair<unsigned,Vector>( 0, Vector(+0.0,-sqrt3/3.0,+0.0)*lattice_constants[0] + Vector(+0.0,+0.0,-0.5)*lattice_constants[1] );
372 1 : environments[1][0] = std::pair<unsigned,Vector>( 0, Vector(+0.5,+sqrt3/2.0,+0.0)*lattice_constants[0] );
373 1 : environments[1][1] = std::pair<unsigned,Vector>( 0, Vector(-0.5,+sqrt3/2.0,+0.0)*lattice_constants[0] );
374 1 : environments[1][2] = std::pair<unsigned,Vector>( 0, Vector(+0.5,-sqrt3/2.0,+0.0)*lattice_constants[0] );
375 1 : environments[1][3] = std::pair<unsigned,Vector>( 0, Vector(-0.5,-sqrt3/2.0,+0.0)*lattice_constants[0] );
376 1 : environments[1][4] = std::pair<unsigned,Vector>( 0, Vector(+1.0,+0.0,+0.0) *lattice_constants[0] );
377 1 : environments[1][5] = std::pair<unsigned,Vector>( 0, Vector(-1.0,+0.0,+0.0) *lattice_constants[0] );
378 1 : environments[1][6] = std::pair<unsigned,Vector>( 0, Vector(+0.5,-sqrt3/6.0,+0.0)*lattice_constants[0] + Vector(+0.0,+0.0,+0.5)*lattice_constants[1] );
379 1 : environments[1][7] = std::pair<unsigned,Vector>( 0, Vector(-0.5,-sqrt3/6.0,+0.0)*lattice_constants[0] + Vector(+0.0,+0.0,+0.5)*lattice_constants[1] );
380 1 : environments[1][8] = std::pair<unsigned,Vector>( 0, Vector(+0.0,+sqrt3/3.0,+0.0)*lattice_constants[0] + Vector(+0.0,+0.0,+0.5)*lattice_constants[1] );
381 1 : environments[1][9] = std::pair<unsigned,Vector>( 0, Vector(+0.5,-sqrt3/6.0,+0.0)*lattice_constants[0] + Vector(+0.0,+0.0,-0.5)*lattice_constants[1] );
382 1 : environments[1][10] = std::pair<unsigned,Vector>( 0, Vector(-0.5,-sqrt3/6.0,+0.0)*lattice_constants[0] + Vector(+0.0,+0.0,-0.5)*lattice_constants[1] );
383 1 : environments[1][11] = std::pair<unsigned,Vector>( 0, Vector(+0.0,+sqrt3/3.0,+0.0)*lattice_constants[0] + Vector(+0.0,+0.0,-0.5)*lattice_constants[1] );
384 1 : maxdist = lattice_constants[0];
385 1 : } else if (crystal_structure == "DIAMOND") {
386 1 : if (lattice_constants.size() != 1) {
387 0 : error("Number of LATTICE_CONSTANTS arguments must be one for DIAMOND");
388 : }
389 1 : environments.resize(2);
390 1 : environments[0].resize(4);
391 1 : environments[1].resize(4);
392 1 : environments[0][0] = std::pair<unsigned,Vector>( 0, Vector(+1.0,+1.0,+1.0)*lattice_constants[0]/4.0 );
393 1 : environments[0][1] = std::pair<unsigned,Vector>( 0, Vector(-1.0,-1.0,+1.0)*lattice_constants[0]/4.0 );
394 1 : environments[0][2] = std::pair<unsigned,Vector>( 0, Vector(+1.0,-1.0,-1.0)*lattice_constants[0]/4.0 );
395 1 : environments[0][3] = std::pair<unsigned,Vector>( 0, Vector(-1.0,+1.0,-1.0)*lattice_constants[0]/4.0 );
396 1 : environments[1][0] = std::pair<unsigned,Vector>( 0, Vector(+1.0,-1.0,+1.0)*lattice_constants[0]/4.0 );
397 1 : environments[1][1] = std::pair<unsigned,Vector>( 0, Vector(-1.0,+1.0,+1.0)*lattice_constants[0]/4.0 );
398 1 : environments[1][2] = std::pair<unsigned,Vector>( 0, Vector(+1.0,+1.0,-1.0)*lattice_constants[0]/4.0 );
399 1 : environments[1][3] = std::pair<unsigned,Vector>( 0, Vector(-1.0,-1.0,-1.0)*lattice_constants[0]/4.0 );
400 1 : maxdist = std::sqrt(3)*lattice_constants[0]/4.0;
401 : } else {
402 0 : error( crystal_structure + " is not a valid input for keyword CRYSTAL_STRUCTURE");
403 : }
404 : }
405 10 : std::string matlab = getShortcutLabel() + "_cmat";
406 : double cutoff, sig;
407 10 : parse("SIGMA",sig);
408 20 : parse("CUTOFF",cutoff);
409 : std::string lcutoff;
410 20 : parse("LCUTOFF",lcutoff);
411 : std::string sig2;
412 10 : Tools::convert( sig*sig, sig2 );
413 10 : std::vector<std::vector<std::string> > funcstr(environments.size());
414 : std::string str_cutoff;
415 10 : Tools::convert( maxdist + cutoff*sig, str_cutoff );
416 : std::string str_natoms, xpos, ypos, zpos;
417 10 : Tools::convert( environments[0].size(), str_natoms );
418 31 : for(unsigned j=0; j<environments.size(); ++j) {
419 21 : funcstr[j].resize( allspec.size() );
420 46 : for(unsigned k=0; k<allspec.size(); ++k) {
421 177 : for(unsigned i=0; i<environments[j].size(); ++i) {
422 152 : if( environments[j][i].first!=k ) {
423 16 : continue ;
424 : }
425 136 : Tools::convert( environments[j][i].second[0], xpos );
426 136 : Tools::convert( environments[j][i].second[1], ypos );
427 136 : Tools::convert( environments[j][i].second[2], zpos );
428 136 : if( i==0 ) {
429 42 : funcstr[j][k] = "FUNC=(step(w-" + lcutoff + ")*step(" + str_cutoff + "-w)/" + str_natoms + ")*(exp(-((x-" + xpos + ")^2+(y-" + ypos + ")^2+(z-" + zpos + ")^2)/(4*" + sig2 + "))";
430 : } else {
431 230 : funcstr[j][k] += "+exp(-((x-" + xpos + ")^2+(y-" + ypos + ")^2+(z-" + zpos + ")^2)/(4*" + sig2 + "))";
432 : }
433 : }
434 25 : if( funcstr[j][k].length()>0 ) {
435 : funcstr[j][k] += ")";
436 : } else {
437 : funcstr[j][k] ="FUNC=0";
438 : }
439 : }
440 : }
441 :
442 : // Create the constact matrix
443 : std::string sp_str, specA, specB;
444 10 : parse("SPECIES",sp_str);
445 10 : parse("SPECIESA",specA);
446 20 : parse("SPECIESB",specB);
447 10 : if( sp_str.length()>0 ) {
448 18 : readInputLine( matlab + ": DISTANCE_MATRIX COMPONENTS GROUP=" + sp_str + " CUTOFF=" + str_cutoff );
449 18 : readInputLine( getShortcutLabel() + "_grp: GROUP ATOMS=" + sp_str );
450 : } else {
451 1 : if( specA.length()==0 ) {
452 0 : error("no atoms were specified use SPECIES or SPECIESA+SPECIESB");
453 : }
454 1 : if( specB.length()==0 ) {
455 0 : error("no atoms were specified for SPECIESB");
456 : }
457 2 : readInputLine( matlab + ": DISTANCE_MATRIX COMPONENTS GROUPA=" + specA + " GROUPB=" + specB + " CUTOFF=" + str_cutoff );
458 2 : readInputLine( getShortcutLabel() + "_grp: GROUP ATOMS=" + specA );
459 : }
460 :
461 : // Make a vector containing all ones
462 10 : ActionWithValue* av = plumed.getActionSet().selectWithLabel<ActionWithValue*>( matlab );
463 10 : plumed_assert( av && av->getNumberOfComponents()>0 && (av->copyOutput(0))->getRank()==2 );
464 : std::string size;
465 10 : Tools::convert( (av->copyOutput(0))->getShape()[1], size );
466 10 : if( allspec.size()==1 ) {
467 18 : readInputLine( getShortcutLabel() + "_ones: ONES SIZE=" + size );
468 : } else {
469 1 : unsigned natoms=atomnamepdb.getPositions().size();
470 : unsigned firstneigh=0;
471 1 : if( sp_str.length()==0 ) {
472 1 : firstneigh = (av->copyOutput(0))->getShape()[0];
473 : }
474 3 : for(unsigned i=0; i<allspec.size(); ++i) {
475 2 : std::string onesstr="0";
476 2 : if( atomnamepdb.getAtomName(atomnamepdb.getAtomNumbers()[firstneigh])==allspec[i] ) {
477 : onesstr = "1";
478 : }
479 576 : for(unsigned j=firstneigh+1; j<natoms; ++j) {
480 574 : if( atomnamepdb.getAtomName(atomnamepdb.getAtomNumbers()[j])==allspec[i] ) {
481 : onesstr += ",1";
482 : } else {
483 : onesstr += ",0";
484 : }
485 : }
486 4 : readInputLine( getShortcutLabel() + "_ones_" + allspec[i] + ": CONSTANT VALUES=" + onesstr );
487 : }
488 : }
489 :
490 : std::string envargstr,varstr, maxfuncstr, lambda;
491 10 : if( funcstr.size()>1 ) {
492 10 : parse("LAMBDA",lambda);
493 : }
494 : // And now do the funcstr bit
495 31 : for(unsigned j=0; j<funcstr.size(); ++j) {
496 : std::string jnum;
497 21 : Tools::convert( j+1, jnum );
498 21 : if(j==0) {
499 10 : varstr = "v" + jnum;
500 20 : maxfuncstr = "(1/" + lambda + ")*log(exp(" + lambda + "*v1)";
501 20 : envargstr = getShortcutLabel() + "_env" + jnum;
502 : } else {
503 11 : varstr += ",v" + jnum;
504 22 : maxfuncstr += "+exp(" + lambda + "*v" + jnum + ")";
505 22 : envargstr += "," + getShortcutLabel() + "_env" + jnum;
506 : }
507 : // And coordination numbers
508 21 : if( allspec.size()>1 ) {
509 : std::string argnames;
510 12 : for(unsigned i=0; i<allspec.size(); ++i) {
511 16 : readInputLine( getShortcutLabel() + "_" + allspec[i] + "_matenv" + jnum + ": CUSTOM ARG=" + matlab + ".x," + matlab + ".y," + matlab + ".z," + matlab + ".w VAR=x,y,z,w PERIODIC=NO " + funcstr[j][i] );
512 16 : readInputLine( getShortcutLabel() + "_" + allspec[i] + "_env" + jnum + ": MATRIX_VECTOR_PRODUCT ARG=" + getShortcutLabel() + "_" + allspec[i] + "_matenv" + jnum + "," + getShortcutLabel() + "_ones_" + allspec[i] );
513 8 : if( i==0 ) {
514 8 : argnames = getShortcutLabel() + "_" + allspec[i] + "_env" + jnum;
515 : } else {
516 8 : argnames += "," + getShortcutLabel() + "_" + allspec[i] + "_env" + jnum;
517 : }
518 : }
519 4 : if( funcstr.size()==1) {
520 0 : readInputLine( getShortcutLabel() + ": COMBINE PERIODIC=NO ARG=" + argnames );
521 : } else {
522 8 : readInputLine( getShortcutLabel() + "_env" + jnum + ": COMBINE PERIODIC=NO ARG=" + argnames );
523 : }
524 : } else {
525 34 : readInputLine( getShortcutLabel() + "_matenv" + jnum + ": CUSTOM ARG=" + matlab + ".x," + matlab + ".y," + matlab + ".z," + matlab + ".w VAR=x,y,z,w PERIODIC=NO " + funcstr[j][0] );
526 17 : if( funcstr.size()==1) {
527 10 : readInputLine( getShortcutLabel() + ": MATRIX_VECTOR_PRODUCT ARG=" + getShortcutLabel() + "_matenv" + jnum + "," + getShortcutLabel() + "_ones");
528 : } else {
529 24 : readInputLine( getShortcutLabel() + "_env" + jnum + ": MATRIX_VECTOR_PRODUCT ARG=" + getShortcutLabel() + "_matenv" + jnum + "," + getShortcutLabel() + "_ones");
530 : }
531 : }
532 : }
533 : // And get the maximum
534 10 : if( funcstr.size()>1 ) {
535 10 : readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + envargstr + " PERIODIC=NO VAR=" + varstr + " FUNC=" + maxfuncstr + ")" );
536 : }
537 : // Read in all the shortcut stuff
538 : std::map<std::string,std::string> keymap;
539 10 : multicolvar::MultiColvarShortcuts::readShortcutKeywords( keymap, this );
540 20 : multicolvar::MultiColvarShortcuts::expandFunctions( getShortcutLabel(), getShortcutLabel(), "", keymap, this );
541 40 : }
542 :
543 14 : std::vector<std::pair<unsigned,Vector> > EnvironmentSimilarity::getReferenceEnvironment( const PDB& pdb, const std::vector<std::string>& anames, double& maxdist ) {
544 14 : unsigned natoms = pdb.getPositions().size();
545 14 : std::vector<std::pair<unsigned,Vector> > env( natoms );
546 78 : for(unsigned i=0; i<natoms; ++i) {
547 : unsigned identity=0;
548 80 : for(unsigned j=1; j<anames.size(); ++j) {
549 16 : if( pdb.getAtomName(pdb.getAtomNumbers()[i])==anames[j] ) {
550 : identity=j;
551 : break;
552 : }
553 : }
554 64 : env[i] = std::pair<unsigned,Vector>( identity, pdb.getPositions()[i] );
555 64 : double dist = env[i].second.modulo();
556 64 : if( dist>maxdist ) {
557 13 : maxdist = dist;
558 : }
559 : }
560 14 : return env;
561 : }
562 :
563 : }
564 : }
|