OpenMD 3.0
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 appropriate papers when you publish your
33 * work. Good starting points are:
34 *
35 * [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
36 * [2] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
37 * [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
38 * [4] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
39 * [5] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
40 * [6] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
41 * [7] Lamichhane, Newman & Gezelter, J. Chem. Phys. 141, 134110 (2014).
42 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
43 */
44
45#include "applications/staticProps/BondOrderParameter.hpp"
46
47#include <sstream>
48
49#include "io/DumpReader.hpp"
50#include "math/Wigner3jm.hpp"
52#include "utils/Constants.hpp"
53#include "utils/Revision.hpp"
54#include "utils/simError.h"
55
56using namespace MATPACK;
57namespace OpenMD {
58
59 BondOrderParameter::BondOrderParameter(SimInfo* info,
60 const std::string& filename,
61 const std::string& sele, double rCut,
62 int nbins) :
63 StaticAnalyser(info, filename, nbins),
64 selectionScript_(sele), seleMan_(info), evaluator_(info) {
65 setAnalysisType("Bond Order Parameters");
66 setOutputName(getPrefix(filename) + ".bo");
67
68 evaluator_.loadScriptString(sele);
69 if (!evaluator_.isDynamic()) {
70 seleMan_.setSelectionSet(evaluator_.evaluate());
71 }
72
73 // Set up cutoff radius and order of the Legendre Polynomial:
74
75 rCut_ = rCut;
76
77 std::stringstream params;
78 params << " rcut = " << rCut_ << ", nbins = " << nBins_;
79 const std::string paramString = params.str();
80 setParameterString(paramString);
81
82 Qcount_.resize(lMax_ + 1);
83 Wcount_.resize(lMax_ + 1);
84
85 // Q can take values from 0 to 1
86
87 MinQ_ = 0.0;
88 MaxQ_ = 1.1;
89 deltaQ_ = (MaxQ_ - MinQ_) / nbins;
90
91 // W_6 for icosahedral clusters is 11 / sqrt(4199) = 0.169754, so we'll
92 // use values for MinW_ and MaxW_ that are slightly larger than this:
93
94 MinW_ = -1.1;
95 MaxW_ = 1.1;
96 deltaW_ = (MaxW_ - MinW_) / nbins;
97
98 // Make arrays for Wigner3jm
99 RealType* THRCOF = new RealType[2 * lMax_ + 1];
100 // Variables for Wigner routine
101 RealType lPass, m1Pass, m2m, m2M;
102 int error, mSize;
103 mSize = 2 * lMax_ + 1;
104
105 for (int l = 0; l <= lMax_; l++) {
106 lPass = (RealType)l;
107 for (int m1 = -l; m1 <= l; m1++) {
108 m1Pass = (RealType)m1;
109
110 std::pair<int, int> lm = std::make_pair(l, m1);
111
112 // Zero work array
113 for (int ii = 0; ii < 2 * l + 1; ii++) {
114 THRCOF[ii] = 0.0;
115 }
116
117 // Get Wigner coefficients
118 Wigner3jm(lPass, lPass, lPass, m1Pass, m2m, m2M, THRCOF, mSize, error);
119
120 m2Min[lm] = (int)floor(m2m);
121 m2Max[lm] = (int)floor(m2M);
122
123 for (int mmm = 0; mmm <= (int)(m2M - m2m); mmm++) {
124 w3j[lm].push_back(THRCOF[mmm]);
125 }
126 }
127 }
128 delete[] THRCOF;
129 THRCOF = NULL;
130 }
131
132 void BondOrderParameter::initializeHistogram() {
133 for (int bin = 0; bin < nBins_; bin++) {
134 for (int l = 0; l <= lMax_; l++) {
135 Q_histogram_[std::make_pair(bin, l)] = 0;
136 W_histogram_[std::make_pair(bin, l)] = 0;
137 }
138 }
139 }
140
141 void BondOrderParameter::process() {
142 Molecule* mol;
143 Atom* atom;
144 int myIndex;
145 SimInfo::MoleculeIterator mi;
146 Molecule::AtomIterator ai;
147 StuntDouble* sd;
148 Vector3d vec;
149 RealType costheta;
150 RealType phi;
151 RealType r;
152 std::map<std::pair<int, int>, ComplexType> q;
153 std::vector<RealType> q_l;
154 std::vector<RealType> q2;
155 std::vector<ComplexType> w;
156 std::vector<ComplexType> w_hat;
157 std::map<std::pair<int, int>, ComplexType> QBar;
158 std::vector<RealType> Q2;
159 std::vector<RealType> Q;
160 std::vector<ComplexType> W;
161 std::vector<ComplexType> W_hat;
162 int nBonds, Nbonds;
163 SphericalHarmonic sphericalHarmonic;
164 int i;
165 bool usePeriodicBoundaryConditions_ =
166 info_->getSimParams()->getUsePeriodicBoundaryConditions();
167
168 DumpReader reader(info_, dumpFilename_);
169 int nFrames = reader.getNFrames();
170 frameCounter_ = 0;
171
172 q_l.resize(lMax_ + 1);
173 q2.resize(lMax_ + 1);
174 w.resize(lMax_ + 1);
175 w_hat.resize(lMax_ + 1);
176
177 Q2.resize(lMax_ + 1);
178 Q.resize(lMax_ + 1);
179 W.resize(lMax_ + 1);
180 W_hat.resize(lMax_ + 1);
181 Nbonds = 0;
182
183 for (int istep = 0; istep < nFrames; istep += step_) {
184 reader.readFrame(istep);
185 frameCounter_++;
186 currentSnapshot_ = info_->getSnapshotManager()->getCurrentSnapshot();
187
188 if (evaluator_.isDynamic()) {
189 seleMan_.setSelectionSet(evaluator_.evaluate());
190 }
191
192 // outer loop is over the selected StuntDoubles:
193
194 for (sd = seleMan_.beginSelected(i); sd != NULL;
195 sd = seleMan_.nextSelected(i)) {
196 myIndex = sd->getGlobalIndex();
197 nBonds = 0;
198
199 for (int l = 0; l <= lMax_; l++) {
200 for (int m = -l; m <= l; m++) {
201 q[std::make_pair(l, m)] = 0.0;
202 }
203 }
204
205 // inner loop is over all other atoms in the system:
206
207 for (mol = info_->beginMolecule(mi); mol != NULL;
208 mol = info_->nextMolecule(mi)) {
209 for (atom = mol->beginAtom(ai); atom != NULL;
210 atom = mol->nextAtom(ai)) {
211 if (atom->getGlobalIndex() != myIndex) {
212 vec = sd->getPos() - atom->getPos();
213
214 if (usePeriodicBoundaryConditions_)
215 currentSnapshot_->wrapVector(vec);
216
217 // Calculate "bonds" and build Q_lm(r) where
218 // Q_lm = Y_lm(theta(r),phi(r))
219 // The spherical harmonics are wrt any arbitrary coordinate
220 // system, we choose standard spherical coordinates
221
222 r = vec.length();
223
224 // Check to see if neighbor is in bond cutoff
225
226 if (r < rCut_) {
227 costheta = vec.z() / r;
228 phi = atan2(vec.y(), vec.x());
229
230 for (int l = 0; l <= lMax_; l++) {
231 sphericalHarmonic.setL(l);
232 for (int m = -l; m <= l; m++) {
233 sphericalHarmonic.setM(m);
234 q[std::make_pair(l, m)] +=
235 sphericalHarmonic.getValueAt(costheta, phi);
236 }
237 }
238 nBonds++;
239 }
240 }
241 }
242 }
243
244 for (int l = 0; l <= lMax_; l++) {
245 q2[l] = 0.0;
246 for (int m = -l; m <= l; m++) {
247 q[std::make_pair(l, m)] /= (RealType)nBonds;
248
249 q2[l] += norm(q[std::make_pair(l, m)]);
250 }
251 q_l[l] = sqrt(q2[l] * 4.0 * Constants::PI / (RealType)(2 * l + 1));
252 }
253
254 // Find Third Order Invariant W_l
255
256 for (int l = 0; l <= lMax_; l++) {
257 w[l] = 0.0;
258 for (int m1 = -l; m1 <= l; m1++) {
259 std::pair<int, int> lm = std::make_pair(l, m1);
260 for (int mmm = 0; mmm <= (m2Max[lm] - m2Min[lm]); mmm++) {
261 int m2 = m2Min[lm] + mmm;
262 int m3 = -m1 - m2;
263 w[l] += w3j[lm][mmm] * q[lm] * q[std::make_pair(l, m2)] *
264 q[std::make_pair(l, m3)];
265 }
266 }
267
268 w_hat[l] = w[l] / pow(q2[l], RealType(1.5));
269 }
270
271 collectHistogram(q_l, w_hat);
272
273 Nbonds += nBonds;
274 for (int l = 0; l <= lMax_; l++) {
275 for (int m = -l; m <= l; m++) {
276 QBar[std::make_pair(l, m)] +=
277 (RealType)nBonds * q[std::make_pair(l, m)];
278 }
279 }
280 }
281 }
282
283 // Normalize Qbar2
284 for (int l = 0; l <= lMax_; l++) {
285 for (int m = -l; m <= l; m++) {
286 QBar[std::make_pair(l, m)] /= Nbonds;
287 }
288 }
289
290 // Find second order invariant Q_l
291
292 for (int l = 0; l <= lMax_; l++) {
293 Q2[l] = 0.0;
294 for (int m = -l; m <= l; m++) {
295 Q2[l] += norm(QBar[std::make_pair(l, m)]);
296 }
297 Q[l] = sqrt(Q2[l] * 4.0 * Constants::PI / (RealType)(2 * l + 1));
298 }
299
300 // Find Third Order Invariant W_l
301
302 for (int l = 0; l <= lMax_; l++) {
303 W[l] = 0.0;
304 for (int m1 = -l; m1 <= l; m1++) {
305 std::pair<int, int> lm = std::make_pair(l, m1);
306 for (int mmm = 0; mmm <= (m2Max[lm] - m2Min[lm]); mmm++) {
307 int m2 = m2Min[lm] + mmm;
308 int m3 = -m1 - m2;
309 W[l] += w3j[lm][mmm] * QBar[lm] * QBar[std::make_pair(l, m2)] *
310 QBar[std::make_pair(l, m3)];
311 }
312 }
313
314 W_hat[l] = W[l] / pow(Q2[l], RealType(1.5));
315 }
316
317 writeOrderParameter(Q, W_hat);
318 }
319
320 void BondOrderParameter::collectHistogram(std::vector<RealType> q,
321 std::vector<ComplexType> what) {
322 for (int l = 0; l <= lMax_; l++) {
323 if (q[l] >= MinQ_ && q[l] < MaxQ_) {
324 int qbin = int((q[l] - MinQ_) / deltaQ_);
325 Q_histogram_[std::make_pair(qbin, l)] += 1;
326 Qcount_[l]++;
327 } else {
328 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
329 "q_l value outside reasonable range\n");
330 painCave.severity = OPENMD_ERROR;
331 painCave.isFatal = 1;
332 simError();
333 }
334 }
335
336 for (int l = 0; l <= lMax_; l++) {
337 if (real(what[l]) >= MinW_ && real(what[l]) < MaxW_) {
338 int wbin = int((real(what[l]) - MinW_) / deltaW_);
339 W_histogram_[std::make_pair(wbin, l)] += 1;
340 Wcount_[l]++;
341 } else {
342 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
343 "Re[w_hat] value (%lf) outside reasonable range\n",
344 real(what[l]));
345 painCave.severity = OPENMD_ERROR;
346 painCave.isFatal = 1;
347 simError();
348 }
349 }
350 }
351
352 void BondOrderParameter::writeOrderParameter(std::vector<RealType> Q,
353 std::vector<ComplexType> What) {
354 Revision rev;
355 std::ofstream osq((getOutputFileName() + "q").c_str());
356
357 if (osq.is_open()) {
358 osq << "# " << getAnalysisType() << "\n";
359 osq << "# OpenMD " << rev.getFullRevision() << "\n";
360 osq << "# " << rev.getBuildDate() << "\n";
361 osq << "# selection script: \"" << selectionScript_ << "\"\n";
362 if (!paramString_.empty())
363 osq << "# parameters: " << paramString_ << "\n";
364
365 for (int l = 0; l <= lMax_; l++) {
366 osq << "# <Q_" << l << ">: " << Q[l] << "\n";
367 }
368 // Normalize by number of frames and write it out:
369 for (int i = 0; i < nBins_; ++i) {
370 RealType Qval = MinQ_ + (i + 0.5) * deltaQ_;
371 osq << Qval;
372 for (int l = 0; l <= lMax_; l++) {
373 osq << "\t"
374 << (RealType)Q_histogram_[std::make_pair(i, l)] /
375 (RealType)Qcount_[l] / deltaQ_;
376 }
377 osq << "\n";
378 }
379
380 osq.close();
381
382 } else {
383 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
384 "BondOrderParameter: unable to open %s\n",
385 (getOutputFileName() + "q").c_str());
386 painCave.isFatal = 1;
387 simError();
388 }
389
390 std::ofstream osw((getOutputFileName() + "w").c_str());
391
392 if (osw.is_open()) {
393 osq << "# " << getAnalysisType() << "\n";
394 osq << "# OpenMD " << rev.getFullRevision() << "\n";
395 osq << "# " << rev.getBuildDate() << "\n";
396 osq << "# selection script: \"" << selectionScript_ << "\"\n";
397 if (!paramString_.empty())
398 osq << "# parameters: " << paramString_ << "\n";
399
400 for (int l = 0; l <= lMax_; l++) {
401 osw << "# <W_" << l << ">: " << real(What[l]) << "\t" << imag(What[l])
402 << "\n";
403 }
404 // Normalize by number of frames and write it out:
405 for (int i = 0; i < nBins_; ++i) {
406 RealType Wval = MinW_ + (i + 0.5) * deltaW_;
407 osw << Wval;
408 for (int l = 0; l <= lMax_; l++) {
409 osw << "\t"
410 << (RealType)W_histogram_[std::make_pair(i, l)] /
411 (RealType)Wcount_[l] / deltaW_;
412 }
413 osw << "\n";
414 }
415
416 osw.close();
417 } else {
418 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
419 "BondOrderParameter: unable to open %s\n",
420 (getOutputFileName() + "w").c_str());
421 painCave.isFatal = 1;
422 simError();
423 }
424 }
425} // namespace OpenMD
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
std::string getPrefix(const std::string &str)