OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
Field.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/Field.hpp"
49
50#include <algorithm>
51#include <cmath>
52#include <fstream>
53#include <vector>
54
55#include "io/DumpReader.hpp"
58#include "types/FixedChargeAdapter.hpp"
59#include "types/FluctuatingChargeAdapter.hpp"
60#include "types/MultipoleAdapter.hpp"
61#include "utils/Constants.hpp"
62#include "utils/Revision.hpp"
63#include "utils/simError.h"
64
65using namespace std;
66namespace OpenMD {
67
68 template<class T>
69 Field<T>::Field(SimInfo* info, const std::string& filename,
70 const std::string& sele, RealType voxelSize) :
71 StaticAnalyser(info, filename, 1),
72 selectionScript_(sele), seleMan_(info), evaluator_(info),
73 voxelSize_(voxelSize) {
74 evaluator_.loadScriptString(sele);
75 if (!evaluator_.isDynamic()) {
76 seleMan_.setSelectionSet(evaluator_.evaluate());
77 }
78
79 int nSelected = seleMan_.getSelectionCount();
80
81 Mat3x3d box;
82 Mat3x3d invBox;
83
84 usePeriodicBoundaryConditions_ =
85 info_->getSimParams()->getUsePeriodicBoundaryConditions();
86
87 if (!usePeriodicBoundaryConditions_) {
88 box = snap_->getBoundingBox();
89 invBox = snap_->getInvBoundingBox();
90 } else {
91 box = snap_->getHmat();
92 invBox = snap_->getInvHmat();
93 }
94
95 // reff will be used in the gaussian weighting of the
96 // should be based on r_{effective}
97 snap_ = info_->getSnapshotManager()->getCurrentSnapshot();
98 if (nSelected == 0) {
99 reffective_ = 2.0 * voxelSize;
100 } else {
101 reffective_ = pow((snap_->getVolume() / nSelected), 1. / 3.);
102 }
103 rcut_ = 3.0 * reffective_;
104
105 Vector3d A = box.getColumn(0);
106 Vector3d B = box.getColumn(1);
107 Vector3d C = box.getColumn(2);
108
109 // Required for triclinic cells
110 Vector3d AxB = cross(A, B);
111 Vector3d BxC = cross(B, C);
112 Vector3d CxA = cross(C, A);
113
114 // unit vectors perpendicular to the faces of the triclinic cell:
115 AxB.normalize();
116 BxC.normalize();
117 CxA.normalize();
118
119 // A set of perpendicular lengths in triclinic cells:
120 RealType Wa = abs(dot(A, BxC));
121 RealType Wb = abs(dot(B, CxA));
122 RealType Wc = abs(dot(C, AxB));
123
124 nBins_.x() = int(Wa / voxelSize_);
125 nBins_.y() = int(Wb / voxelSize_);
126 nBins_.z() = int(Wc / voxelSize_);
127
128 // Build the field histogram and dens histogram and fill with zeros.
129 field_.resize(nBins_.x());
130 dens_.resize(nBins_.x());
131 for (int i = 0; i < nBins_.x(); ++i) {
132 field_[i].resize(nBins_.y());
133 dens_[i].resize(nBins_.y());
134 for (int j = 0; j < nBins_.y(); ++j) {
135 field_[i][j].resize(nBins_.z());
136 dens_[i][j].resize(nBins_.z());
137
138 std::fill(dens_[i][j].begin(), dens_[i][j].end(), 0.0);
139 std::fill(field_[i][j].begin(), field_[i][j].end(), T(0.0));
140 }
141 }
142
143 setOutputName(getPrefix(filename) + ".field");
144 }
145
146 template<class T>
147 void Field<T>::process() {
148 DumpReader reader(info_, dumpFilename_);
149 int nFrames = reader.getNFrames();
150 nProcessed_ = nFrames / step_;
151
152 for (int istep = 0; istep < nFrames; istep += step_) {
153 reader.readFrame(istep);
154 snap_ = info_->getSnapshotManager()->getCurrentSnapshot();
155 processFrame(istep);
156 }
157 postProcess();
158 writeField();
159 writeVisualizationScript();
160 }
161
162 template<class T>
163 void Field<T>::processFrame(int) {
164 Mat3x3d box;
165 Mat3x3d invBox;
166 int di, dj, dk;
167 int ibin, jbin, kbin;
168 int igrid, jgrid, kgrid;
169 Vector3d scaled;
170 RealType x, y, z;
171 RealType weight, Wa, Wb, Wc;
172
173 if (!usePeriodicBoundaryConditions_) {
174 box = snap_->getBoundingBox();
175 invBox = snap_->getInvBoundingBox();
176 } else {
177 box = snap_->getHmat();
178 invBox = snap_->getInvHmat();
179 }
180 Vector3d A = box.getColumn(0);
181 Vector3d B = box.getColumn(1);
182 Vector3d C = box.getColumn(2);
183
184 // Required for triclinic cells
185 Vector3d AxB = cross(A, B);
186 Vector3d BxC = cross(B, C);
187 Vector3d CxA = cross(C, A);
188
189 // unit vectors perpendicular to the faces of the triclinic cell:
190 AxB.normalize();
191 BxC.normalize();
192 CxA.normalize();
193
194 // A set of perpendicular lengths in triclinic cells:
195 Wa = abs(dot(A, BxC));
196 Wb = abs(dot(B, CxA));
197 Wc = abs(dot(C, AxB));
198
199 if (evaluator_.isDynamic()) {
200 seleMan_.setSelectionSet(evaluator_.evaluate());
201 }
202
203 // di = the magnitude of distance (in x-dimension) that we
204 // should loop through to add the density to.
205 di = (int)(rcut_ * nBins_.x() / Wa);
206 dj = (int)(rcut_ * nBins_.y() / Wb);
207 dk = (int)(rcut_ * nBins_.z() / Wc);
208
209 int isd;
210 StuntDouble* sd;
211
212 // Loop over the selected StuntDoubles:
213 for (sd = seleMan_.beginSelected(isd); sd != NULL;
214 sd = seleMan_.nextSelected(isd)) {
215 // Get the position of the sd
216 Vector3d pos = sd->getPos();
217
218 // Wrap the sd back into the box, positions now range from
219 // (-boxl/2, boxl/2)
220 if (usePeriodicBoundaryConditions_) { snap_->wrapVector(pos); }
221
222 // scaled positions relative to the box vectors
223 scaled = invBox * pos;
224
225 // wrap the vector back into the unit box by subtracting integer box
226 // numbers
227 for (int j = 0; j < 3; j++) {
228 // Convert to a scaled position vector, range (-1/2, 1/2)
229 // want range to be (0,1), so add 1/2
230 scaled[j] -= roundMe(scaled[j]);
231 scaled[j] += 0.5;
232 // Handle the special case when an object is exactly on the
233 // boundary (a scaled coordinate of 1.0 is the same as
234 // scaled coordinate of 0.0)
235 if (scaled[j] >= 1.0) scaled[j] -= 1.0;
236 }
237
238 // find xyz-indices of cell that stuntDouble is in.
239 ibin = (int)(nBins_.x() * scaled.x());
240 jbin = (int)(nBins_.y() * scaled.y());
241 kbin = (int)(nBins_.z() * scaled.z());
242
243 for (int i = -di; i <= di; i++) {
244 igrid = ibin + i;
245 while (igrid >= int(nBins_.x())) {
246 igrid -= int(nBins_.x());
247 }
248 while (igrid < 0) {
249 igrid += int(nBins_.x());
250 }
251
252 x = Wa * (RealType(i) / RealType(nBins_.x()));
253
254 for (int j = -dj; j <= dj; j++) {
255 jgrid = jbin + j;
256 while (jgrid >= int(nBins_.y())) {
257 jgrid -= int(nBins_.y());
258 }
259 while (jgrid < 0) {
260 jgrid += int(nBins_.y());
261 }
262
263 y = Wb * (RealType(j) / RealType(nBins_.y()));
264
265 for (int k = -dk; k <= dk; k++) {
266 kgrid = kbin + k;
267 while (kgrid >= int(nBins_.z())) {
268 kgrid -= int(nBins_.z());
269 }
270 while (kgrid < 0) {
271 kgrid += int(nBins_.z());
272 }
273
274 z = Wc * (RealType(k) / RealType(nBins_.z()));
275
276 RealType dist = sqrt(x * x + y * y + z * z);
277
278 weight = getDensity(dist, reffective_, rcut_);
279 dens_[igrid][jgrid][kgrid] += weight;
280 field_[igrid][jgrid][kgrid] += weight * getValue(sd);
281 }
282 }
283 }
284 }
285 }
286
287 template<class T>
288 RealType Field<T>::getDensity(RealType r, RealType sigma, RealType rcut) {
289 RealType sigma2 = sigma * sigma;
290 RealType dens =
291 exp(-r * r / (sigma2 * 2.0)) / (pow(2.0 * Constants::PI * sigma2, 3));
292 RealType dcut = exp(-rcut * rcut / (sigma2 * 2.0)) /
293 (pow(2.0 * Constants::PI * sigma2, 3));
294 if (r < rcut)
295 return dens - dcut;
296 else
297 return 0.0;
298 }
299
300 template<class T>
301 void Field<T>::postProcess() {
302 for (unsigned int i = 0; i < field_.size(); ++i) {
303 for (unsigned int j = 0; j < field_[i].size(); ++j) {
304 for (unsigned int k = 0; k < field_[i][j].size(); ++k) {
305 if (dens_[i][j][k] > 0.0) { field_[i][j][k] /= dens_[i][j][k]; }
306 }
307 }
308 }
309 }
310
311 template<class T>
312 void Field<T>::writeField() {
313 Mat3x3d hmat = info_->getSnapshotManager()->getCurrentSnapshot()->getHmat();
314 Vector3d scaled(0.0);
315
316 std::ofstream fs(outputFilename_.c_str());
317 if (fs.is_open()) {
318 Revision r;
319 fs << "# " << getAnalysisType() << "\n";
320 fs << "# OpenMD " << r.getFullRevision() << "\n";
321 fs << "# " << r.getBuildDate() << "\n";
322 fs << "# selection script: \"" << selectionScript_ << "\"\n";
323 fs << "#\n";
324 fs << "# Vector Field output file format has coordinates of the center\n";
325 fs << "# of the voxel followed by the value of field (either vector or\n";
326 fs << "# scalar).\n";
327
328 for (std::size_t k = 0; k < field_[0][0].size(); ++k) {
329 scaled.z() = RealType(k) / nBins_.z() - 0.5;
330 for (std::size_t j = 0; j < field_[0].size(); ++j) {
331 scaled.y() = RealType(j) / nBins_.y() - 0.5;
332 for (std::size_t i = 0; i < field_.size(); ++i) {
333 scaled.y() = RealType(j) / nBins_.y() - 0.5;
334
335 Vector3d voxelLoc = hmat * scaled;
336
337 fs << voxelLoc.x() << "\t";
338 fs << voxelLoc.y() << "\t";
339 fs << voxelLoc.z() << "\t";
340
341 fs << writeValue(field_[i][j][k]);
342
343 fs << std::endl;
344 }
345 }
346 }
347 }
348 }
349
350 template<class T>
351 void Field<T>::writeVisualizationScript() {
352 string t = " ";
353 string pythonFilename = outputFilename_ + ".py";
354 std::ofstream pss(pythonFilename.c_str());
355 if (pss.is_open()) {
356 pss << "#!/opt/local/bin/python\n\n";
357 pss << "__author__ = \"Patrick Louden (plouden@nd.edu)\" \n";
358 pss << "__copyright__ = \"Copyright (c) 2004-present The University of "
359 "Notre "
360 "Dame. All "
361 "Rights Reserved.\" \n";
362 pss << "__license__ = \"OpenMD\"\n\n";
363 pss << "import numpy as np\n";
364 pss << "from mayavi.mlab import * \n\n";
365 pss << "def plotField(inputFileName): \n";
366 pss << t + "inputFile = open(inputFileName, 'r') \n";
367 pss << t + "x = np.array([]) \n";
368 pss << t + "y = np.array([]) \n";
369 pss << t + "z = np.array([]) \n";
370 if (typeid(T) == typeid(int)) {
371 pss << t + "scalarVal = np.array([]) \n\n";
372 }
373 if (typeid(T) == typeid(Vector3d)) {
374 pss << t + "vectorVal.x = np.array([]) \n";
375 pss << t + "vectorVal.y = np.array([]) \n";
376 pss << t + "vectorVal.z = np.array([]) \n\n";
377 }
378 pss << t + "for line in inputFile:\n";
379 pss << t + t + "if line.split()[0] != \"#\": \n";
380 pss << t + t + t + "x = np.append(x, float(line.strip().split()[0])) \n";
381 pss << t + t + t + "y = np.append(y, float(line.strip().split()[1])) \n";
382 pss << t + t + t + "z = np.append(z, float(line.strip().split()[2])) \n";
383 if (typeid(T) == typeid(int)) {
384 pss << t + t + t +
385 "scalarVal = np.append(scalarVal, "
386 "float(line.strip().split()[3])) \n\n";
387 pss << t + "obj = quiver3d(x, y, z, scalarVal, line_width=2, "
388 "scale_factor=3) \n";
389 }
390 if (typeid(T) == typeid(Vector3d)) {
391 pss << t + t + t +
392 "vectorVal.x = np.append(vectorVal.x, "
393 "float(line.strip().split()[3])) "
394 "\n";
395 pss << t + t + t +
396 "vectorVal.y = np.append(vectorVal.y, "
397 "float(line.strip().split()[4])) "
398 "\n";
399 pss << t + t + t +
400 "vextorVal.z = np.append(vectorVal.z, "
401 "float(line.strip().split()[5])) "
402 "\n\n";
403 pss << t + "obj = quiver3d(x, y, z, vectorVal.x, vectorVal.y, "
404 "vectorVal.z, "
405 "line_width=2, scale_factor=3) \n";
406 }
407 pss << t + "return obj \n\n";
408 pss << "plotField(\"";
409 pss << outputFilename_.c_str();
410 pss << "\")";
411 }
412 }
413
414 template<>
415 std::string Field<RealType>::writeValue(RealType v) {
416 std::stringstream str;
417 if (std::isinf(v) || std::isnan(v)) {
418 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
419 "Field detected a numerical error.\n");
420 painCave.isFatal = 1;
421 simError();
422 }
423 str << v;
424 return str.str();
425 }
426
427 template<>
428 std::string Field<Vector3d>::writeValue(Vector3d v) {
429 std::stringstream str;
430 if (std::isinf(v[0]) || std::isnan(v[0]) || std::isinf(v[1]) ||
431 std::isnan(v[1]) || std::isinf(v[2]) || std::isnan(v[2])) {
432 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
433 "Field detected a numerical error.\n");
434 painCave.isFatal = 1;
435 simError();
436 }
437 str << v[0] << "\t" << v[1] << "\t" << v[2];
438 return str.str();
439 }
440
441 DensityField::DensityField(SimInfo* info, const std::string& filename,
442 const std::string& sele1, RealType voxelSize) :
443 Field<RealType>(info, filename, sele1, voxelSize) {
444 setOutputName(getPrefix(filename) + ".densityField");
445 }
446
447 RealType DensityField::getValue(StuntDouble* sd) { return sd->getMass(); }
448
449 ChargeField::ChargeField(SimInfo* info, const std::string& filename,
450 const std::string& sele1, RealType voxelSize) :
451 Field<RealType>(info, filename, sele1, voxelSize) {
452 setOutputName(getPrefix(filename) + ".chargeField");
453 }
454
455 RealType ChargeField::getValue(StuntDouble* sd) {
456 RealType charge = 0.0;
457 Atom* atom = static_cast<Atom*>(sd);
458
459 AtomType* atomType = atom->getAtomType();
460
461 FixedChargeAdapter fca = FixedChargeAdapter(atomType);
462 if (fca.isFixedCharge()) { charge = fca.getCharge(); }
463
464 FluctuatingChargeAdapter fqa = FluctuatingChargeAdapter(atomType);
465 if (fqa.isFluctuatingCharge()) { charge += atom->getFlucQPos(); }
466 return charge;
467 }
468
469 SDVelocityField::SDVelocityField(SimInfo* info, const std::string& filename,
470 const std::string& sele1, RealType voxelSize) :
471 Field<Vector3d>(info, filename, sele1, voxelSize) {
472 setOutputName(getPrefix(filename) + ".velocityField");
473 }
474
475 Vector3d SDVelocityField::getValue(StuntDouble* sd) { return sd->getVel(); }
476
477 DipoleField::DipoleField(SimInfo* info, const std::string& filename,
478 const std::string& sele1, RealType voxelSize) :
479 Field<Vector3d>(info, filename, sele1, voxelSize) {
480 setOutputName(getPrefix(filename) + ".dipoleField");
481 }
482
483 Vector3d DipoleField::getValue(StuntDouble* sd) {
484 const RealType eAtoDebye = 4.8032045444;
485 Vector3d dipoleVector(0.0);
486
487 if (sd->isDirectionalAtom()) {
488 dipoleVector += static_cast<DirectionalAtom*>(sd)->getDipole();
489 }
490
491 if (sd->isRigidBody()) {
492 RigidBody* rb = static_cast<RigidBody*>(sd);
493 Atom* atom;
494 RigidBody::AtomIterator ai;
495 for (atom = rb->beginAtom(ai); atom != NULL; atom = rb->nextAtom(ai)) {
496 RealType charge(0.0);
497 AtomType* atomType = atom->getAtomType();
498
499 FixedChargeAdapter fca = FixedChargeAdapter(atomType);
500 if (fca.isFixedCharge()) { charge = fca.getCharge(); }
501
502 FluctuatingChargeAdapter fqa = FluctuatingChargeAdapter(atomType);
503 if (fqa.isFluctuatingCharge()) { charge += atom->getFlucQPos(); }
504
505 Vector3d pos = atom->getPos();
506 dipoleVector += pos * charge * eAtoDebye;
507
508 if (atom->isDipole()) { dipoleVector += atom->getDipole(); }
509 }
510 }
511 return dipoleVector;
512 }
513} // namespace OpenMD
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
Definition SimInfo.hpp:96
Real & x()
Returns reference of the first element of Vector3.
Definition Vector3.hpp:99
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
Vector3< Real > cross(const Vector3< Real > &v1, const Vector3< Real > &v2)
Returns the cross product of two Vectors.
Definition Vector3.hpp:139
Real dot(const DynamicVector< Real > &v1, const DynamicVector< Real > &v2)
Returns the dot product of two DynamicVectors.
std::string getPrefix(const std::string &str)