OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
BondOrderParameter.cpp
1/*
2 * Copyright (c) 2004-present, The University of Notre Dame. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
32 * research, please cite the following paper when you publish your work:
33 *
34 * [1] Drisko et al., J. Open Source Softw. 9, 7004 (2024).
35 *
36 * Good starting points for code and simulation methodology are:
37 *
38 * [2] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
39 * [3] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
40 * [4] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
41 * [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
42 * [6] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
43 * [7] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
44 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
45 * [9] Drisko & Gezelter, J. Chem. Theory Comput. 20, 4986-4997 (2024).
46 */
47
48#include "applications/staticProps/BondOrderParameter.hpp"
49
50#include <sstream>
51
52#include "io/DumpReader.hpp"
53#include "math/Wigner3jm.hpp"
55#include "utils/Constants.hpp"
56#include "utils/Revision.hpp"
57#include "utils/simError.h"
58
59using namespace MATPACK;
60namespace OpenMD {
61
62 BondOrderParameter::BondOrderParameter(SimInfo* info,
63 const std::string& filename,
64 const std::string& sele, double rCut,
65 unsigned int nbins) :
66 StaticAnalyser(info, filename, nbins),
67 selectionScript_(sele), seleMan_(info), evaluator_(info) {
68 setAnalysisType("Bond Order Parameters");
69 setOutputName(getPrefix(filename) + ".bo");
70
71 evaluator_.loadScriptString(sele);
72 if (!evaluator_.isDynamic()) {
73 seleMan_.setSelectionSet(evaluator_.evaluate());
74 }
75
76 // Set up cutoff radius and order of the Legendre Polynomial:
77
78 rCut_ = rCut;
79 nBins_ = nbins;
80
81 std::stringstream params;
82 params << " rcut = " << rCut_ << ", nbins = " << nBins_;
83 const std::string paramString = params.str();
84 setParameterString(paramString);
85
86 Qcount_.resize(lMax_ + 1);
87 Wcount_.resize(lMax_ + 1);
88
89 // Q can take values from 0 to 1
90
91 MinQ_ = 0.0;
92 MaxQ_ = 1.1;
93 deltaQ_ = (MaxQ_ - MinQ_) / (nBins_);
94
95 // W_6 for icosahedral clusters is 11 / sqrt(4199) = 0.169754, so we'll
96 // use values for MinW_ and MaxW_ that are slightly larger than this:
97
98 MinW_ = -1.1;
99 MaxW_ = 1.1;
100 deltaW_ = (MaxW_ - MinW_) / (nBins_);
101
102 // Make arrays for Wigner3jm
103 RealType* THRCOF = new RealType[2 * lMax_ + 1];
104 // Variables for Wigner routine
105 RealType lPass, m1Pass, m2m, m2M;
106 int error, mSize;
107 mSize = 2 * lMax_ + 1;
108
109 for (int l = 0; l <= lMax_; l++) {
110 lPass = (RealType)l;
111 for (int m1 = -l; m1 <= l; m1++) {
112 m1Pass = (RealType)m1;
113
114 std::pair<int, int> lm = std::make_pair(l, m1);
115
116 // Zero work array
117 for (int ii = 0; ii < 2 * l + 1; ii++) {
118 THRCOF[ii] = 0.0;
119 }
120
121 // Get Wigner coefficients
122 Wigner3jm(lPass, lPass, lPass, m1Pass, m2m, m2M, THRCOF, mSize, error);
123
124 m2Min[lm] = (int)floor(m2m);
125 m2Max[lm] = (int)floor(m2M);
126
127 for (int mmm = 0; mmm <= (int)(m2M - m2m); mmm++) {
128 w3j[lm].push_back(THRCOF[mmm]);
129 }
130 }
131 }
132 delete[] THRCOF;
133 THRCOF = NULL;
134 }
135
136 void BondOrderParameter::initializeHistogram() {
137 for (int bin = 0; bin < nBins_; bin++) {
138 for (int l = 0; l <= lMax_; l++) {
139 Q_histogram_[std::make_pair(bin, l)] = 0;
140 W_histogram_[std::make_pair(bin, l)] = 0;
141 }
142 }
143 }
144
145 void BondOrderParameter::process() {
146 Molecule* mol;
147 Atom* atom;
148 int myIndex;
149 SimInfo::MoleculeIterator mi;
150 Molecule::AtomIterator ai;
151 StuntDouble* sd;
152 Vector3d vec;
153 RealType costheta;
154 RealType phi;
155 RealType r;
156 std::map<std::pair<int, int>, ComplexType> q;
157 std::vector<RealType> q_l;
158 std::vector<RealType> q2;
159 std::vector<ComplexType> w;
160 std::vector<ComplexType> w_hat;
161 std::map<std::pair<int, int>, ComplexType> QBar;
162 std::vector<RealType> Q2;
163 std::vector<RealType> Q;
164 std::vector<ComplexType> W;
165 std::vector<ComplexType> W_hat;
166 int nBonds, Nbonds;
167 SphericalHarmonic sphericalHarmonic;
168 int i;
169 bool usePeriodicBoundaryConditions_ =
170 info_->getSimParams()->getUsePeriodicBoundaryConditions();
171
172 DumpReader reader(info_, dumpFilename_);
173 int nFrames = reader.getNFrames();
174 frameCounter_ = 0;
175
176 q_l.resize(lMax_ + 1);
177 q2.resize(lMax_ + 1);
178 w.resize(lMax_ + 1);
179 w_hat.resize(lMax_ + 1);
180
181 Q2.resize(lMax_ + 1);
182 Q.resize(lMax_ + 1);
183 W.resize(lMax_ + 1);
184 W_hat.resize(lMax_ + 1);
185 Nbonds = 0;
186
187 for (int istep = 0; istep < nFrames; istep += step_) {
188 reader.readFrame(istep);
189 frameCounter_++;
190 currentSnapshot_ = info_->getSnapshotManager()->getCurrentSnapshot();
191
192 if (evaluator_.isDynamic()) {
193 seleMan_.setSelectionSet(evaluator_.evaluate());
194 }
195
196 // outer loop is over the selected StuntDoubles:
197
198 for (sd = seleMan_.beginSelected(i); sd != NULL;
199 sd = seleMan_.nextSelected(i)) {
200 myIndex = sd->getGlobalIndex();
201 nBonds = 0;
202
203 for (int l = 0; l <= lMax_; l++) {
204 for (int m = -l; m <= l; m++) {
205 q[std::make_pair(l, m)] = 0.0;
206 }
207 }
208
209 // inner loop is over all other atoms in the system:
210
211 for (mol = info_->beginMolecule(mi); mol != NULL;
212 mol = info_->nextMolecule(mi)) {
213 for (atom = mol->beginAtom(ai); atom != NULL;
214 atom = mol->nextAtom(ai)) {
215 if (atom->getGlobalIndex() != myIndex) {
216 vec = sd->getPos() - atom->getPos();
217
218 if (usePeriodicBoundaryConditions_)
219 currentSnapshot_->wrapVector(vec);
220
221 // Calculate "bonds" and build Q_lm(r) where
222 // Q_lm = Y_lm(theta(r),phi(r))
223 // The spherical harmonics are wrt any arbitrary coordinate
224 // system, we choose standard spherical coordinates
225
226 r = vec.length();
227
228 // Check to see if neighbor is in bond cutoff
229
230 if (r < rCut_) {
231 costheta = vec.z() / r;
232 phi = atan2(vec.y(), vec.x());
233
234 for (int l = 0; l <= lMax_; l++) {
235 sphericalHarmonic.setL(l);
236 for (int m = -l; m <= l; m++) {
237 sphericalHarmonic.setM(m);
238 q[std::make_pair(l, m)] +=
239 sphericalHarmonic.getValueAt(costheta, phi);
240 }
241 }
242 nBonds++;
243 }
244 }
245 }
246 }
247
248 for (int l = 0; l <= lMax_; l++) {
249 q2[l] = 0.0;
250 for (int m = -l; m <= l; m++) {
251 q[std::make_pair(l, m)] /= (RealType)nBonds;
252
253 q2[l] += norm(q[std::make_pair(l, m)]);
254 }
255 q_l[l] = sqrt(q2[l] * 4.0 * Constants::PI / (RealType)(2 * l + 1));
256 }
257
258 // Find Third Order Invariant W_l
259
260 for (int l = 0; l <= lMax_; l++) {
261 w[l] = 0.0;
262 for (int m1 = -l; m1 <= l; m1++) {
263 std::pair<int, int> lm = std::make_pair(l, m1);
264 for (int mmm = 0; mmm <= (m2Max[lm] - m2Min[lm]); mmm++) {
265 int m2 = m2Min[lm] + mmm;
266 int m3 = -m1 - m2;
267 w[l] += w3j[lm][mmm] * q[lm] * q[std::make_pair(l, m2)] *
268 q[std::make_pair(l, m3)];
269 }
270 }
271
272 w_hat[l] = w[l] / pow(q2[l], RealType(1.5));
273 }
274
275 collectHistogram(q_l, w_hat);
276
277 Nbonds += nBonds;
278 for (int l = 0; l <= lMax_; l++) {
279 for (int m = -l; m <= l; m++) {
280 QBar[std::make_pair(l, m)] +=
281 (RealType)nBonds * q[std::make_pair(l, m)];
282 }
283 }
284 }
285 }
286
287 // Normalize Qbar2
288 for (int l = 0; l <= lMax_; l++) {
289 for (int m = -l; m <= l; m++) {
290 QBar[std::make_pair(l, m)] /= Nbonds;
291 }
292 }
293
294 // Find second order invariant Q_l
295
296 for (int l = 0; l <= lMax_; l++) {
297 Q2[l] = 0.0;
298 for (int m = -l; m <= l; m++) {
299 Q2[l] += norm(QBar[std::make_pair(l, m)]);
300 }
301 Q[l] = sqrt(Q2[l] * 4.0 * Constants::PI / (RealType)(2 * l + 1));
302 }
303
304 // Find Third Order Invariant W_l
305
306 for (int l = 0; l <= lMax_; l++) {
307 W[l] = 0.0;
308 for (int m1 = -l; m1 <= l; m1++) {
309 std::pair<int, int> lm = std::make_pair(l, m1);
310 for (int mmm = 0; mmm <= (m2Max[lm] - m2Min[lm]); mmm++) {
311 int m2 = m2Min[lm] + mmm;
312 int m3 = -m1 - m2;
313 W[l] += w3j[lm][mmm] * QBar[lm] * QBar[std::make_pair(l, m2)] *
314 QBar[std::make_pair(l, m3)];
315 }
316 }
317
318 W_hat[l] = W[l] / pow(Q2[l], RealType(1.5));
319 }
320
321 writeOrderParameter(Q, W_hat);
322 }
323
324 void BondOrderParameter::collectHistogram(std::vector<RealType> q,
325 std::vector<ComplexType> what) {
326 for (int l = 0; l <= lMax_; l++) {
327 if (q[l] >= MinQ_ && q[l] < MaxQ_) {
328 int qbin = int((q[l] - MinQ_) / deltaQ_);
329 Q_histogram_[std::make_pair(qbin, l)] += 1;
330 Qcount_[l]++;
331 } else {
332 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
333 "q_l value outside reasonable range\n");
334 painCave.severity = OPENMD_ERROR;
335 painCave.isFatal = 1;
336 simError();
337 }
338 }
339
340 for (int l = 0; l <= lMax_; l++) {
341 if (real(what[l]) >= MinW_ && real(what[l]) < MaxW_) {
342 int wbin = int((real(what[l]) - MinW_) / deltaW_);
343 W_histogram_[std::make_pair(wbin, l)] += 1;
344 Wcount_[l]++;
345 } else {
346 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
347 "Re[w_hat] value (%lf) outside reasonable range\n",
348 real(what[l]));
349 painCave.severity = OPENMD_ERROR;
350 painCave.isFatal = 1;
351 simError();
352 }
353 }
354 }
355
356 void BondOrderParameter::writeOrderParameter(std::vector<RealType> Q,
357 std::vector<ComplexType> What) {
358 Revision rev;
359 std::ofstream osq((getOutputFileName() + "q").c_str());
360
361 if (osq.is_open()) {
362 osq << "# " << getAnalysisType() << "\n";
363 osq << "# OpenMD " << rev.getFullRevision() << "\n";
364 osq << "# " << rev.getBuildDate() << "\n";
365 osq << "# selection script: \"" << selectionScript_ << "\"\n";
366 if (!paramString_.empty())
367 osq << "# parameters: " << paramString_ << "\n";
368
369 for (int l = 0; l <= lMax_; l++) {
370 osq << "# <Q_" << l << ">: " << Q[l] << "\n";
371 }
372 // Normalize by number of frames and write it out:
373 for (int i = 0; i < nBins_; ++i) {
374 RealType Qval = MinQ_ + (i + 0.5) * deltaQ_;
375 osq << Qval;
376 for (int l = 0; l <= lMax_; l++) {
377 osq << "\t"
378 << (RealType)Q_histogram_[std::make_pair(i, l)] /
379 (RealType)Qcount_[l] / deltaQ_;
380 }
381 osq << "\n";
382 }
383
384 osq.close();
385
386 } else {
387 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
388 "BondOrderParameter: unable to open %s\n",
389 (getOutputFileName() + "q").c_str());
390 painCave.isFatal = 1;
391 simError();
392 }
393
394 std::ofstream osw((getOutputFileName() + "w").c_str());
395
396 if (osw.is_open()) {
397 osw << "# " << getAnalysisType() << "\n";
398 osw << "# OpenMD " << rev.getFullRevision() << "\n";
399 osw << "# " << rev.getBuildDate() << "\n";
400 osw << "# selection script: \"" << selectionScript_ << "\"\n";
401 if (!paramString_.empty())
402 osw << "# parameters: " << paramString_ << "\n";
403
404 for (int l = 0; l <= lMax_; l++) {
405 osw << "# <W_" << l << ">: " << real(What[l]) << "\t" << imag(What[l])
406 << "\n";
407 }
408 // Normalize by number of frames and write it out:
409 for (int i = 0; i < nBins_; ++i) {
410 RealType Wval = MinW_ + (i + 0.5) * deltaW_;
411 osw << Wval;
412 for (int l = 0; l <= lMax_; l++) {
413 osw << "\t"
414 << (RealType)W_histogram_[std::make_pair(i, l)] /
415 (RealType)Wcount_[l] / deltaW_;
416 }
417 osw << "\n";
418 }
419
420 osw.close();
421 } else {
422 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
423 "BondOrderParameter: unable to open %s\n",
424 (getOutputFileName() + "w").c_str());
425 painCave.isFatal = 1;
426 simError();
427 }
428 }
429} // namespace OpenMD
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
Definition SimInfo.hpp:96
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
std::string getPrefix(const std::string &str)