OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
GofRAngle2.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/GofRAngle2.hpp"
49
50#include <algorithm>
51#include <fstream>
52#include <sstream>
53
54#include "primitives/Atom.hpp"
55#include "types/MultipoleAdapter.hpp"
56#include "utils/Revision.hpp"
57#include "utils/simError.h"
58
59namespace OpenMD {
60
61 GofRAngle2::GofRAngle2(SimInfo* info, const std::string& filename,
62 const std::string& sele1, const std::string& sele2,
63 RealType len, int nrbins, int nangleBins) :
64 RadialDistrFunc(info, filename, sele1, sele2, nrbins),
65 nAngleBins_(nangleBins), len_(len), doSele3_(false), seleMan3_(info),
66 evaluator3_(info) {
67 setAnalysisType("Radial Distribution Function");
68 setOutputName(getPrefix(filename) + ".grto");
69
70 deltaR_ = len_ / (double)nBins_;
71 deltaCosAngle_ = 2.0 / nAngleBins_;
72
73 std::stringstream params;
74 params << " nBins = " << nBins_ << ", maxLen = " << len_
75 << ", deltaR = " << deltaR_ << ", nAngleBins = " << nAngleBins_
76 << ", deltaCosAngle = " << deltaCosAngle_;
77 const std::string paramString = params.str();
78 setParameterString(paramString);
79
80 histogram_.resize(nBins_);
81 avgGofr_.resize(nBins_);
82 for (unsigned int i = 0; i < nBins_; ++i) {
83 histogram_[i].resize(nAngleBins_);
84 avgGofr_[i].resize(nAngleBins_);
85 for (unsigned int j = 0; j < nAngleBins_; ++j) {
86 histogram_[i][j].resize(nAngleBins_);
87 avgGofr_[i][j].resize(nAngleBins_);
88 }
89 }
90 }
91
92 GofRAngle2::GofRAngle2(SimInfo* info, const std::string& filename,
93 const std::string& sele1, const std::string& sele2,
94 const std::string& sele3, RealType len, int nrbins,
95 int nangleBins) :
96 RadialDistrFunc(info, filename, sele1, sele2, nrbins),
97 nAngleBins_(nangleBins), len_(len), doSele3_(true), seleMan3_(info),
98 evaluator3_(info), selectionScript3_(sele3) {
99 setOutputName(getPrefix(filename) + ".grto");
100
101 deltaR_ = len_ / (double)nBins_;
102 deltaCosAngle_ = 2.0 / nAngleBins_;
103
104 std::stringstream params;
105 params << " nBins = " << nBins_ << ", maxLen = " << len_
106 << ", deltaR = " << deltaR_ << ", nAngleBins = " << nAngleBins_
107 << ", deltaCosAngle = " << deltaCosAngle_;
108 const std::string paramString = params.str();
109 setParameterString(paramString);
110
111 histogram_.resize(nBins_);
112 avgGofr_.resize(nBins_);
113 for (unsigned int i = 0; i < nBins_; ++i) {
114 histogram_[i].resize(nAngleBins_);
115 avgGofr_[i].resize(nAngleBins_);
116 for (unsigned int j = 0; j < nAngleBins_; ++j) {
117 histogram_[i][j].resize(nAngleBins_);
118 avgGofr_[i][j].resize(nAngleBins_);
119 }
120 }
121
122 evaluator3_.loadScriptString(sele3);
123 if (!evaluator3_.isDynamic()) {
124 seleMan3_.setSelectionSet(evaluator3_.evaluate());
125 }
126 }
127
128 void GofRAngle2::processNonOverlapping(SelectionManager& sman1,
129 SelectionManager& sman2) {
130 StuntDouble* sd1;
131 StuntDouble* sd2;
132 StuntDouble* sd3;
133 int i;
134 int j;
135 int k;
136
137 // This is the same as a non-overlapping pairwise loop structure:
138 // for (int i = 0; i < ni ; ++i ) {
139 // for (int j = 0; j < nj; ++j) {}
140 // }
141
142 if (doSele3_) {
143 if (evaluator3_.isDynamic()) {
144 seleMan3_.setSelectionSet(evaluator3_.evaluate());
145 }
146 if (sman1.getSelectionCount() != seleMan3_.getSelectionCount()) {
147 RadialDistrFunc::processNonOverlapping(sman1, sman2);
148 }
149
150 for (sd1 = sman1.beginSelected(i), sd3 = seleMan3_.beginSelected(k);
151 sd1 != NULL && sd3 != NULL;
152 sd1 = sman1.nextSelected(i), sd3 = seleMan3_.nextSelected(k)) {
153 for (sd2 = sman2.beginSelected(j); sd2 != NULL;
154 sd2 = sman2.nextSelected(j)) {
155 collectHistogram(sd1, sd2, sd3);
156 }
157 }
158 } else {
159 RadialDistrFunc::processNonOverlapping(sman1, sman2);
160 }
161 }
162
163 void GofRAngle2::processOverlapping(SelectionManager& sman) {
164 StuntDouble* sd1;
165 StuntDouble* sd2;
166 StuntDouble* sd3;
167 int i;
168 int j;
169 int k;
170
171 // This is the same as a pairwise loop structure:
172 // for (int i = 0; i < n-1 ; ++i ) {
173 // for (int j = i + 1; j < n; ++j) {}
174 // }
175
176 if (doSele3_) {
177 if (evaluator3_.isDynamic()) {
178 seleMan3_.setSelectionSet(evaluator3_.evaluate());
179 }
180 if (sman.getSelectionCount() != seleMan3_.getSelectionCount()) {
181 RadialDistrFunc::processOverlapping(sman);
182 }
183 for (sd1 = sman.beginSelected(i), sd3 = seleMan3_.beginSelected(k);
184 sd1 != NULL && sd3 != NULL;
185 sd1 = sman.nextSelected(i), sd3 = seleMan3_.nextSelected(k)) {
186 for (j = i, sd2 = sman.nextSelected(j); sd2 != NULL;
187 sd2 = sman.nextSelected(j)) {
188 collectHistogram(sd1, sd2, sd3);
189 }
190 }
191 } else {
192 RadialDistrFunc::processOverlapping(sman);
193 }
194 }
195
196 void GofRAngle2::preProcess() {
197 for (unsigned int i = 0; i < avgGofr_.size(); ++i) {
198 for (unsigned int j = 0; j < avgGofr_[i].size(); ++j) {
199 std::fill(avgGofr_[i][j].begin(), avgGofr_[i][j].end(), 0.0);
200 }
201 }
202 }
203
204 void GofRAngle2::initializeHistogram() {
205 for (unsigned int i = 0; i < histogram_.size(); ++i) {
206 for (unsigned int j = 0; j < histogram_.size(); ++j) {
207 std::fill(histogram_[i][j].begin(), histogram_[i][j].end(), 0);
208 }
209 }
210 }
211
212 void GofRAngle2::processHistogram() {
213 int nPairs = getNPairs();
214 RealType volume =
215 info_->getSnapshotManager()->getCurrentSnapshot()->getVolume();
216 RealType pairDensity = nPairs / volume;
217 RealType pairConstant = (4.0 * Constants::PI * pairDensity) /
218 (3.0 * (double)nAngleBins_ * (double)nAngleBins_);
219
220 for (unsigned int i = 0; i < histogram_.size(); ++i) {
221 RealType rLower = i * deltaR_;
222 RealType rUpper = rLower + deltaR_;
223 RealType volSlice =
224 (rUpper * rUpper * rUpper) - (rLower * rLower * rLower);
225 RealType nIdeal = volSlice * pairConstant;
226
227 for (unsigned int j = 0; j < histogram_[i].size(); ++j) {
228 for (unsigned int k = 0; k < histogram_[i][j].size(); ++k) {
229 avgGofr_[i][j][k] += RealType(histogram_[i][j][k]) / nIdeal;
230 }
231 }
232 }
233 }
234
235 void GofRAngle2::collectHistogram(StuntDouble* sd1, StuntDouble* sd2) {
236 bool usePeriodicBoundaryConditions_ =
237 info_->getSimParams()->getUsePeriodicBoundaryConditions();
238
239 if (sd1 == sd2) { return; }
240
241 Vector3d pos1 = sd1->getPos();
242 Vector3d pos2 = sd2->getPos();
243 Vector3d r12 = pos1 - pos2;
244 if (usePeriodicBoundaryConditions_) currentSnapshot_->wrapVector(r12);
245
246 RealType distance = r12.length();
247 int whichRBin = int(distance / deltaR_);
248
249 if (distance <= len_) {
250 if (!sd1->isDirectional()) {
251 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
252 "GofAngle2: attempted to use a non-directional object: %s\n",
253 sd1->getType().c_str());
254 painCave.isFatal = 1;
255 simError();
256 }
257
258 if (!sd2->isDirectional()) {
259 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
260 "GofAngle2: attempted to use a non-directional object: %s\n",
261 sd2->getType().c_str());
262 painCave.isFatal = 1;
263 simError();
264 }
265
266 Vector3d dipole1, dipole2;
267
268 if (sd1->isAtom()) {
269 AtomType* atype1 = static_cast<Atom*>(sd1)->getAtomType();
270 MultipoleAdapter ma1 = MultipoleAdapter(atype1);
271 if (ma1.isDipole())
272 dipole1 = sd1->getDipole();
273 else
274 dipole1 = sd1->getA().transpose() * V3Z;
275
276 } else {
277 dipole1 = sd1->getA().transpose() * V3Z;
278 }
279
280 if (sd2->isAtom()) {
281 AtomType* atype2 = static_cast<Atom*>(sd2)->getAtomType();
282 MultipoleAdapter ma2 = MultipoleAdapter(atype2);
283 if (ma2.isDipole())
284 dipole2 = sd2->getDipole();
285 else
286 dipole2 = sd2->getA().transpose() * V3Z;
287 } else {
288 dipole2 = sd2->getA().transpose() * V3Z;
289 }
290
291 r12.normalize();
292 dipole1.normalize();
293 dipole2.normalize();
294
295 RealType cosAngle1 = dot(r12, dipole1);
296 RealType cosAngle2 = dot(dipole1, dipole2);
297
298 RealType halfBin = (nAngleBins_ - 1) * 0.5;
299 int angleBin1 = int(halfBin * (cosAngle1 + 1.0));
300 int angleBin2 = int(halfBin * (cosAngle2 + 1.0));
301
302 ++histogram_[whichRBin][angleBin1][angleBin2];
303 }
304 }
305
306 void GofRAngle2::collectHistogram(StuntDouble* sd1, StuntDouble* sd2,
307 StuntDouble* sd3) {
308 bool usePeriodicBoundaryConditions_ =
309 info_->getSimParams()->getUsePeriodicBoundaryConditions();
310
311 if (sd1 == sd2) { return; }
312
313 Vector3d p1 = sd1->getPos();
314 Vector3d p3 = sd3->getPos();
315
316 Vector3d c = 0.5 * (p1 + p3);
317 Vector3d r13 = p3 - p1;
318
319 Vector3d r12 = sd2->getPos() - c;
320
321 if (usePeriodicBoundaryConditions_) {
322 currentSnapshot_->wrapVector(r12);
323 currentSnapshot_->wrapVector(r13);
324 }
325
326 RealType distance = r12.length();
327 int whichRBin = int(distance / deltaR_);
328
329 if (distance <= len_) {
330 r12.normalize();
331 r13.normalize();
332
333 if (!sd2->isDirectional()) {
334 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
335 "GofAngle2: attempted to use a non-directional object: %s\n",
336 sd2->getType().c_str());
337 painCave.isFatal = 1;
338 simError();
339 }
340
341 Vector3d dipole2;
342
343 if (sd2->isAtom()) {
344 AtomType* atype2 = static_cast<Atom*>(sd2)->getAtomType();
345 MultipoleAdapter ma2 = MultipoleAdapter(atype2);
346
347 if (ma2.isDipole())
348 dipole2 = sd2->getDipole();
349 else
350 dipole2 = sd2->getA().transpose() * V3Z;
351
352 } else {
353 // Rigid Body:
354 dipole2 = sd2->getA().transpose() * V3Z;
355 }
356
357 dipole2.normalize();
358
359 RealType cosAngle1 = dot(r12, r13);
360 RealType cosAngle2 = dot(r13, dipole2);
361
362 RealType halfBin = (nAngleBins_ - 1) * 0.5;
363 int angleBin1 = int(halfBin * (cosAngle1 + 1.0));
364 int angleBin2 = int(halfBin * (cosAngle2 + 1.0));
365
366 ++histogram_[whichRBin][angleBin1][angleBin2];
367 }
368 }
369
370 void GofRAngle2::writeRdf() {
371 std::ofstream ofs(outputFilename_.c_str());
372 if (ofs.is_open()) {
373 Revision r;
374 ofs << "# " << getAnalysisType() << "\n";
375 ofs << "# OpenMD " << r.getFullRevision() << "\n";
376 ofs << "# " << r.getBuildDate() << "\n";
377 ofs << "# selection script1: \"" << selectionScript1_;
378 ofs << "\"\tselection script2: \"" << selectionScript2_ << "\"";
379 if (doSele3_) {
380 ofs << "\tselection script3: \"" << selectionScript3_ << "\"\n";
381 } else {
382 ofs << "\n";
383 }
384
385 if (!paramString_.empty())
386 ofs << "# parameters: " << paramString_ << "\n";
387
388 for (unsigned int i = 0; i < avgGofr_.size(); ++i) {
389 // RealType r = deltaR_ * (i + 0.5);
390 for (unsigned int j = 0; j < avgGofr_[i].size(); ++j) {
391 // RealType cosAngle1 = -1.0 + (j + 0.5)*deltaCosAngle_;
392 for (unsigned int k = 0; k < avgGofr_[i][j].size(); ++k) {
393 // RealType cosAngle2 = -1.0 + (k + 0.5)*deltaCosAngle_;
394
395 ofs << avgGofr_[i][j][k] / nProcessed_ << "\t";
396 }
397 ofs << "\n";
398 }
399 ofs << "\n";
400 }
401
402 } else {
403 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
404 "GofRAngle2: unable to open %s\n", outputFilename_.c_str());
405 painCave.isFatal = 1;
406 simError();
407 }
408
409 ofs.close();
410 }
411
412} // namespace OpenMD
Radial Distribution Function.
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
Definition SimInfo.hpp:96
void normalize()
Normalizes this vector in place.
Definition Vector.hpp:406
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
Real dot(const DynamicVector< Real > &v1, const DynamicVector< Real > &v2)
Returns the dot product of two DynamicVectors.
std::string getPrefix(const std::string &str)
Real distance(const DynamicVector< Real > &v1, const DynamicVector< Real > &v2)
Returns the distance between two DynamicVectors.