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