OpenMD 3.0
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
RNEMDStats.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/RNEMDStats.hpp"
46
47#include <algorithm>
48#include <fstream>
49#include <iostream>
50#include <iterator>
51#include <set>
52#include <string>
53#include <utility>
54#include <vector>
55
56#include "applications/staticProps/SpatialStatistics.hpp"
57#include "brains/DataStorage.hpp"
58#include "brains/SimInfo.hpp"
59#include "io/Globals.hpp"
61#include "math/Vector3.hpp"
62#include "primitives/Atom.hpp"
66#include "rnemd/RNEMDParameters.hpp"
67#include "types/AtomType.hpp"
68#include "types/FixedChargeAdapter.hpp"
69#include "types/FluctuatingChargeAdapter.hpp"
70#include "utils/Accumulator.hpp"
71#include "utils/AccumulatorView.hpp"
72#include "utils/BaseAccumulator.hpp"
73#include "utils/Constants.hpp"
74#include "utils/StringUtils.hpp"
75
76using namespace OpenMD::Utils;
77
78namespace OpenMD {
79
80 RNEMDZ::RNEMDZ(SimInfo* info, const std::string& filename,
81 const std::string& sele, int nzbins, int axis) :
82 SlabStatistics(info, filename, sele, nzbins, axis) {
83 setOutputName(getPrefix(filename) + ".rnemdZ");
84
85 evaluator_.loadScriptString(sele);
86 seleMan_.setSelectionSet(evaluator_.evaluate());
87 AtomTypeSet osTypes = seleMan_.getSelectedAtomTypes();
88 std::copy(osTypes.begin(), osTypes.end(), std::back_inserter(outputTypes_));
89 bool usePeriodicBoundaryConditions_ =
90 info_->getSimParams()->getUsePeriodicBoundaryConditions();
91
92 data_.resize(RNEMDZ::ENDINDEX);
93
94 OutputData z;
95 z.units = "Angstroms";
96 z.title = axisLabel_;
97 z.dataHandling = DataHandling::Average;
98 for (unsigned int i = 0; i < nBins_; i++)
99 z.accumulator.push_back(
100 std::make_unique<AccumulatorView<RealAccumulator>>());
101 data_[Z] = std::move(z);
102
103 OutputData temperature;
104 temperature.units = "K";
105 temperature.title = "Temperature";
106 temperature.dataHandling = DataHandling::Average;
107 for (unsigned int i = 0; i < nBins_; i++)
108 temperature.accumulator.push_back(
109 std::make_unique<AccumulatorView<RealAccumulator>>());
110 data_[TEMPERATURE] = std::move(temperature);
111
112 OutputData velocity;
113 velocity.units = "angstroms/fs";
114 velocity.title = "Velocity";
115 velocity.dataHandling = DataHandling::Average;
116 for (unsigned int i = 0; i < nBins_; i++)
117 velocity.accumulator.push_back(
118 std::make_unique<AccumulatorView<Vector3dAccumulator>>());
119 data_[VELOCITY] = std::move(velocity);
120
121 OutputData density;
122 density.units = "g cm^-3";
123 density.title = "Density";
124 density.dataHandling = DataHandling::Average;
125 for (unsigned int i = 0; i < nBins_; i++)
126 density.accumulator.push_back(
127 std::make_unique<AccumulatorView<RealAccumulator>>());
128 data_[DENSITY] = std::move(density);
129
130 OutputData activity;
131 activity.units = "unitless";
132 activity.title = "Activity";
133 activity.dataHandling = DataHandling::Average;
134 unsigned int nTypes = outputTypes_.size();
135 // Only do activities if we have atoms in the selection
136 if (nTypes > 0) {
137 for (unsigned int i = 0; i < nBins_; i++)
138 activity.accumulator.push_back(
139 std::make_unique<AccumulatorView<StdVectorAccumulator>>());
140 data_[ACTIVITY] = std::move(activity);
141 }
142
143 OutputData eField;
144 eField.units = "kcal/mol/angstroms/e";
145 eField.title = "Electric Field";
146 eField.dataHandling = DataHandling::Average;
147 for (unsigned int i = 0; i < nBins_; i++)
148 eField.accumulator.push_back(
149 std::make_unique<AccumulatorView<Vector3dAccumulator>>());
150
151 OutputData ePot;
152 ePot.units = "kcal/mol/e";
153 ePot.title = "Electrostatic Potential";
154 ePot.dataHandling = DataHandling::Average;
155 for (unsigned int i = 0; i < nBins_; i++)
156 ePot.accumulator.push_back(
157 std::make_unique<AccumulatorView<RealAccumulator>>());
158
159 OutputData charge;
160 charge.units = "e";
161 charge.title = "Charge";
162 charge.dataHandling = DataHandling::Average;
163 for (unsigned int i = 0; i < nBins_; i++)
164 charge.accumulator.push_back(
165 std::make_unique<AccumulatorView<RealAccumulator>>());
166
167 OutputData chargeVelocity;
168 chargeVelocity.units = "e/fs";
169 chargeVelocity.title = "Charge_Velocity";
170 chargeVelocity.dataHandling = DataHandling::Average;
171 for (unsigned int i = 0; i < nBins_; i++)
172 chargeVelocity.accumulator.push_back(
173 std::make_unique<AccumulatorView<RealAccumulator>>());
174
175 outputMask_.set(Z);
176 outputMask_.set(TEMPERATURE);
177 outputMask_.set(VELOCITY);
178 outputMask_.set(DENSITY);
179 outputMask_.set(ACTIVITY);
180
181 int atomStorageLayout = info_->getAtomStorageLayout();
182 int rigidBodyStorageLayout = info->getRigidBodyStorageLayout();
183 int cutoffGroupStorageLayout = info->getCutoffGroupStorageLayout();
184
185 if (atomStorageLayout & DataStorage::dslElectricField) {
186 outputMask_.set(ELECTRICFIELD);
187 outputMask_.set(ELECTROSTATICPOTENTIAL);
188
189 data_[ELECTRICFIELD] = std::move(eField);
190 data_[ELECTROSTATICPOTENTIAL] = std::move(ePot);
191 }
192
193 if (info_->usesElectrostaticAtoms() ||
194 atomStorageLayout & DataStorage::dslFlucQPosition) {
195 outputMask_.set(CHARGE);
196
197 data_[CHARGE] = std::move(charge);
198 }
199
200 if (atomStorageLayout & DataStorage::dslFlucQVelocity) {
201 outputMask_.set(CHARGEVELOCITY);
202
203 data_[CHARGEVELOCITY] = std::move(chargeVelocity);
204 }
205 }
206
207 void RNEMDZ::processFrame(int istep) {
208 SlabStatistics::processFrame(istep);
209
210 if (evaluator_.isDynamic()) {
211 seleMan_.setSelectionSet(evaluator_.evaluate());
212 }
213
214 int binNo {};
215 int typeIndex(-1);
216 RealType mass {};
217 Vector3d vel {};
218 RealType KE {};
219 RealType q {};
220 RealType w {};
221 Vector3d eField {};
222 int DOF {};
223
224 std::vector<RealType> binMass(nBins_, 0.0);
225 std::vector<Vector3d> binP(nBins_, V3Zero);
226 std::vector<RealType> binCharge(nBins_, 0.0);
227 std::vector<RealType> binChargeVelocity(nBins_, 0.0);
228 std::vector<RealType> binKE(nBins_, 0.0);
229 std::vector<Vector3d> binEField(nBins_, V3Zero);
230 std::vector<int> binDOF(nBins_, 0);
231 std::vector<int> binCount(nBins_, 0);
232 std::vector<std::vector<int>> binTypeCounts;
233 std::vector<int> binEFieldCount(nBins_, 0);
234
235 if (outputMask_[ACTIVITY]) {
236 binTypeCounts.resize(nBins_);
237 for (unsigned int i = 0; i < nBins_; i++) {
238 binTypeCounts[i].resize(outputTypes_.size(), 0);
239 }
240 }
241
242 SimInfo::MoleculeIterator miter;
243 std::vector<StuntDouble*>::iterator iiter;
244 std::vector<AtomType*>::iterator at;
245 Molecule* mol;
246 StuntDouble* sd;
247 AtomType* atype;
248 ConstraintPair* consPair;
249 Molecule::ConstraintPairIterator cpi;
250
251 for (mol = info_->beginMolecule(miter); mol != NULL;
252 mol = info_->nextMolecule(miter)) {
253 for (sd = mol->beginIntegrableObject(iiter); sd != NULL;
254 sd = mol->nextIntegrableObject(iiter)) {
255 if (seleMan_.isSelected(sd)) {
256 Vector3d pos = sd->getPos();
257 binNo = getBin(pos);
258
259 mass = sd->getMass();
260 vel = sd->getVel();
261 KE = 0.5 * mass * vel.lengthSquare();
262 DOF = 3;
263
264 if (sd->isDirectional()) {
265 Vector3d angMom = sd->getJ();
266 Mat3x3d Ia = sd->getI();
267 if (sd->isLinear()) {
268 int i = sd->linearAxis();
269 int j = (i + 1) % 3;
270 int k = (i + 2) % 3;
271 KE += 0.5 * (angMom[j] * angMom[j] / Ia(j, j) +
272 angMom[k] * angMom[k] / Ia(k, k));
273 DOF += 2;
274 } else {
275 KE += 0.5 * (angMom[0] * angMom[0] / Ia(0, 0) +
276 angMom[1] * angMom[1] / Ia(1, 1) +
277 angMom[2] * angMom[2] / Ia(2, 2));
278 DOF += 3;
279 }
280 }
281
282 if (outputMask_[ACTIVITY]) {
283 typeIndex = -1;
284 if (sd->isAtom()) {
285 atype = static_cast<Atom*>(sd)->getAtomType();
286 at = std::find(outputTypes_.begin(), outputTypes_.end(), atype);
287 if (at != outputTypes_.end()) {
288 typeIndex = std::distance(outputTypes_.begin(), at);
289 }
290 }
291 }
292
293 if (binNo >= 0 && binNo < int(nBins_)) {
294 binCount[binNo]++;
295 binMass[binNo] += mass;
296 binP[binNo] += mass * vel;
297 binKE[binNo] += KE;
298 binDOF[binNo] += DOF;
299
300 if (outputMask_[ACTIVITY] && typeIndex != -1)
301 binTypeCounts[binNo][typeIndex]++;
302
303 if (outputMask_[CHARGE] || outputMask_[CHARGEVELOCITY]) {
304 if (sd->isAtom()) {
305 AtomType* atomType = static_cast<Atom*>(sd)->getAtomType();
307 if (fca.isFixedCharge()) { q = fca.getCharge(); }
309 FluctuatingChargeAdapter(atomType);
310 if (fqa.isFluctuatingCharge()) {
311 q += sd->getFlucQPos();
312 w += sd->getFlucQVel();
313 }
314
315 if (outputMask_[CHARGE]) binCharge[binNo] += q;
316 if (outputMask_[CHARGEVELOCITY]) binChargeVelocity[binNo] += w;
317 } else if (sd->isRigidBody()) {
318 RigidBody* rb = static_cast<RigidBody*>(sd);
319 std::vector<Atom*>::iterator ai;
320 Atom* atom;
321 for (atom = rb->beginAtom(ai); atom != NULL;
322 atom = rb->nextAtom(ai)) {
323 binNo = getBin(atom->getPos());
324 AtomType* atomType = atom->getAtomType();
326 if (fca.isFixedCharge()) { q = fca.getCharge(); }
327
329 FluctuatingChargeAdapter(atomType);
330 if (fqa.isFluctuatingCharge()) {
331 q += sd->getFlucQPos();
332 w += sd->getFlucQVel();
333 }
334
335 if (outputMask_[CHARGE]) binCharge[binNo] += q;
336 if (outputMask_[CHARGEVELOCITY])
337 binChargeVelocity[binNo] += w;
338 }
339 }
340 }
341 }
342 }
343
344 // Calculate the electric field (kcal/mol/e/Angstrom) for all atoms in
345 // the box
346 if (outputMask_[ELECTRICFIELD]) {
347 if (sd->isRigidBody()) {
348 RigidBody* rb = static_cast<RigidBody*>(sd);
349 std::vector<Atom*>::iterator ai;
350 Atom* atom;
351 for (atom = rb->beginAtom(ai); atom != NULL;
352 atom = rb->nextAtom(ai)) {
353 binNo = getBin(atom->getPos());
354 eField = atom->getElectricField();
355 binEFieldCount[binNo]++;
356 binEField[binNo] += eField;
357 }
358 } else {
359 eField = sd->getElectricField();
360 binNo = getBin(sd->getPos());
361
362 binEFieldCount[binNo]++;
363 binEField[binNo] += eField;
364 }
365 }
366 }
367 if (seleMan_.isSelected(mol)) {
368 for (consPair = mol->beginConstraintPair(cpi); consPair != NULL;
369 consPair = mol->nextConstraintPair(cpi)) {
370 Vector3d posA = consPair->getConsElem1()->getPos();
371 Vector3d posB = consPair->getConsElem2()->getPos();
372
373 if (usePeriodicBoundaryConditions_) {
374 currentSnapshot_->wrapVector(posA);
375 currentSnapshot_->wrapVector(posB);
376 }
377
378 Vector3d coc = 0.5 * (posA + posB);
379 int binCons = getBin(coc);
380 binDOF[binCons] -= 1;
381 }
382 }
383 }
384
385 for (unsigned int i = 0; i < nBins_; i++) {
386 RealType temp(0.0), ePot(0.0);
387 Vector3d vel(0.0), eField(0.0);
388 RealType z, den(0.0), binVolume(0.0), dz(0.0);
389 std::vector<RealType> nden(outputTypes_.size(), 0.0);
390
391 z = (((RealType)i + 0.5) / (RealType)nBins_) * hmat_(axis_, axis_);
392 data_[Z].accumulator[i]->add(z);
393
394 binVolume = volume_ / nBins_;
395 dz = hmat_(axis_, axis_) / (RealType)nBins_;
396
397 // The calculations of the following properties are done regardless
398 // of whether or not the selected species are present in the bin
399 if (outputMask_[ELECTRICFIELD] && binEFieldCount[i] > 0) {
400 eField = binEField[i] / RealType(binEFieldCount[i]);
401 data_[ELECTRICFIELD].accumulator[i]->add(eField);
402 }
403
404 if (outputMask_[ELECTROSTATICPOTENTIAL] && binEFieldCount[i] > 0) {
405 ePot += eField[axis_] * dz;
406 data_[ELECTROSTATICPOTENTIAL].accumulator[i]->add(ePot);
407 }
408
409 // For the following properties, zero should be added if the selected
410 // species is not present in the bin
411 if (outputMask_[DENSITY]) {
412 den = binMass[i] * Constants::densityConvert / binVolume;
413 data_[DENSITY].accumulator[i]->add(den);
414 }
415
416 if (outputMask_[ACTIVITY]) {
417 for (unsigned int j = 0; j < outputTypes_.size(); j++) {
418 nden[j] = (binTypeCounts[i][j] / binVolume) *
419 Constants::concentrationConvert;
420 }
421 data_[ACTIVITY].accumulator[i]->add(nden);
422 }
423
424 if (binCount[i] > 0) {
425 // The calculations of the following properties are undefined if
426 // the selected species is not found in the bin
427 if (outputMask_[VELOCITY]) {
428 vel = binP[i] / binMass[i];
429 data_[VELOCITY].accumulator[i]->add(vel);
430 }
431
432 if (outputMask_[TEMPERATURE]) {
433 if (binDOF[i] > 0) {
434 temp = 2.0 * binKE[i] /
435 (binDOF[i] * Constants::kb * Constants::energyConvert);
436 data_[TEMPERATURE].accumulator[i]->add(temp);
437 } else {
438 std::cerr << "No degrees of freedom in this bin?\n";
439 }
440 }
441
442 if (outputMask_[CHARGE])
443 data_[CHARGE].accumulator[i]->add(binCharge[i]);
444
445 if (outputMask_[CHARGEVELOCITY])
446 data_[CHARGEVELOCITY].accumulator[i]->add(binChargeVelocity[i]);
447 }
448 }
449 }
450
451 RNEMDR::RNEMDR(SimInfo* info, const std::string& filename,
452 const std::string& sele, const std::string& comsele,
453 int nrbins, RealType binWidth) :
454 ShellStatistics(info, filename, sele, comsele, nrbins, binWidth) {
455 setOutputName(getPrefix(filename) + ".rnemdR");
456
457 // Pre-load the OutputData
458 data_.resize(RNEMDR::ENDINDEX);
459
460 OutputData r;
461 r.units = "Angstroms";
462 r.title = "R";
463 r.dataHandling = DataHandling::Average;
464 for (int i = 0; i < nBins_; i++)
465 r.accumulator.push_back(
466 std::make_unique<AccumulatorView<RealAccumulator>>());
467 data_[R] = std::move(r);
468
469 OutputData temperature;
470 temperature.units = "K";
471 temperature.title = "Temperature";
472 temperature.dataHandling = DataHandling::Average;
473 for (unsigned int i = 0; i < nBins_; i++)
474 temperature.accumulator.push_back(
475 std::make_unique<AccumulatorView<RealAccumulator>>());
476 data_[TEMPERATURE] = std::move(temperature);
477
478 OutputData angularVelocity;
479 angularVelocity.units = "angstroms/fs";
480 angularVelocity.title = "Velocity";
481 angularVelocity.dataHandling = DataHandling::Average;
482 for (unsigned int i = 0; i < nBins_; i++)
483 angularVelocity.accumulator.push_back(
484 std::make_unique<AccumulatorView<Vector3dAccumulator>>());
485 data_[ANGULARVELOCITY] = std::move(angularVelocity);
486
487 OutputData density;
488 density.units = "g cm^-3";
489 density.title = "Density";
490 density.dataHandling = DataHandling::Average;
491 for (unsigned int i = 0; i < nBins_; i++)
492 density.accumulator.push_back(
493 std::make_unique<AccumulatorView<RealAccumulator>>());
494 data_[DENSITY] = std::move(density);
495
496 outputMask_.set(R);
497 outputMask_.set(TEMPERATURE);
498 outputMask_.set(ANGULARVELOCITY);
499 outputMask_.set(DENSITY);
500 }
501
502 void RNEMDR::processFrame(int istep) {
503 ShellStatistics::processFrame(istep);
504
505 if (evaluator_.isDynamic()) {
506 seleMan_.setSelectionSet(evaluator_.evaluate());
507 }
508
509 int binNo {};
510 RealType mass {};
511 Vector3d vel {};
512 Vector3d rPos {};
513 RealType KE {};
514 Vector3d L {};
515 Mat3x3d I {};
516 RealType r2 {};
517 int DOF {};
518
519 std::vector<int> binCount(nBins_, 0);
520 std::vector<RealType> binMass(nBins_, 0.0);
521 std::vector<Vector3d> binP(nBins_, V3Zero);
522 std::vector<RealType> binOmega(nBins_, 0.0);
523 std::vector<Vector3d> binL(nBins_, V3Zero);
524 std::vector<Mat3x3d> binI(nBins_);
525 std::vector<RealType> binKE(nBins_, 0.0);
526 std::vector<int> binDOF(nBins_, 0);
527
528 SimInfo::MoleculeIterator miter;
529 std::vector<StuntDouble*>::iterator iiter;
530 std::vector<AtomType*>::iterator at;
531 Molecule* mol;
532 StuntDouble* sd;
533 ConstraintPair* consPair;
534 Molecule::ConstraintPairIterator cpi;
535
536 // loop over the selected atoms:
537 for (mol = info_->beginMolecule(miter); mol != NULL;
538 mol = info_->nextMolecule(miter)) {
539 for (sd = mol->beginIntegrableObject(iiter); sd != NULL;
540 sd = mol->nextIntegrableObject(iiter)) {
541 if (seleMan_.isSelected(sd)) {
542 // figure out where that object is:
543 binNo = getBin(sd->getPos());
544
545 if (binNo >= 0 && binNo < int(nBins_)) {
546 mass = sd->getMass();
547 vel = sd->getVel();
548 rPos = sd->getPos() - coordinateOrigin_;
549 KE = 0.5 * mass * vel.lengthSquare();
550 DOF = 3;
551
552 if (sd->isDirectional()) {
553 Vector3d angMom = sd->getJ();
554 Mat3x3d Ia = sd->getI();
555 if (sd->isLinear()) {
556 int i = sd->linearAxis();
557 int j = (i + 1) % 3;
558 int k = (i + 2) % 3;
559 KE += 0.5 * (angMom[j] * angMom[j] / Ia(j, j) +
560 angMom[k] * angMom[k] / Ia(k, k));
561 DOF += 2;
562 } else {
563 KE += 0.5 * (angMom[0] * angMom[0] / Ia(0, 0) +
564 angMom[1] * angMom[1] / Ia(1, 1) +
565 angMom[2] * angMom[2] / Ia(2, 2));
566 DOF += 3;
567 }
568 }
569
570 L = mass * cross(rPos, vel);
571 I = outProduct(rPos, rPos) * mass;
572 r2 = rPos.lengthSquare();
573 I(0, 0) += mass * r2;
574 I(1, 1) += mass * r2;
575 I(2, 2) += mass * r2;
576
577 binCount[binNo]++;
578 binMass[binNo] += mass;
579 binP[binNo] += mass * vel;
580 binKE[binNo] += KE;
581 binI[binNo] += I;
582 binL[binNo] += L;
583 binDOF[binNo] += DOF;
584 }
585 }
586 }
587 if (seleMan_.isSelected(mol)) {
588 for (consPair = mol->beginConstraintPair(cpi); consPair != NULL;
589 consPair = mol->nextConstraintPair(cpi)) {
590 Vector3d posA = consPair->getConsElem1()->getPos();
591 Vector3d posB = consPair->getConsElem2()->getPos();
592
593 Vector3d coc = 0.5 * (posA + posB);
594 int binCons = getBin(coc);
595 if (binCons >= 0 && binCons < int(nBins_)) { binDOF[binCons] -= 1; }
596 }
597 }
598 }
599
600 for (unsigned int i = 0; i < nBins_; i++) {
601 RealType r, rinner, router, den(0.0), binVolume(0.0), temp(0.0);
602 Vector3d omega(0.0);
603
604 r = (((RealType)i + 0.5) * binWidth_);
605 rinner = (RealType)i * binWidth_;
606 router = (RealType)(i + 1) * binWidth_;
607 binVolume =
608 (4.0 * Constants::PI * (pow(router, 3) - pow(rinner, 3))) / 3.0;
609
610 data_[R].accumulator[i]->add(r);
611
612 // For the following properties, zero should be added if the selected
613 // species is not present in the bin
614 den = binMass[i] * Constants::densityConvert / binVolume;
615 data_[DENSITY].accumulator[i]->add(den);
616
617 if (binDOF[i] > 0) {
618 // The calculations of the following properties are undefined if
619 // the selected species is not found in the bin
620 omega = binI[i].inverse() * binL[i];
621 data_[ANGULARVELOCITY].accumulator[i]->add(omega);
622
623 temp = 2.0 * binKE[i] /
624 (binDOF[i] * Constants::kb * Constants::energyConvert);
625 data_[TEMPERATURE].accumulator[i]->add(temp);
626 }
627 }
628 }
629
630 RNEMDRTheta::RNEMDRTheta(SimInfo* info, const std::string& filename,
631 const std::string& sele, const std::string& comsele,
632 int nrbins, RealType binWidth, int nangleBins) :
633 ShellStatistics(info, filename, sele, comsele, nrbins, binWidth),
634 nAngleBins_(nangleBins) {
635 Globals* simParams = info->getSimParams();
636 RNEMD::RNEMDParameters* rnemdParams = simParams->getRNEMDParameters();
637 bool hasAngularMomentumFluxVector =
638 rnemdParams->haveAngularMomentumFluxVector();
639
640 if (hasAngularMomentumFluxVector) {
641 std::vector<RealType> amf = rnemdParams->getAngularMomentumFluxVector();
642
643 if (amf.size() != 3) {
644 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
645 "RNEMDRTheta: Incorrect number of parameters specified for "
646 "angularMomentumFluxVector.\n"
647 "\tthere should be 3 parameters, but %lu were specified.\n",
648 amf.size());
649 painCave.isFatal = 1;
650 simError();
651 }
652 fluxVector_.x() = amf[0];
653 fluxVector_.y() = amf[1];
654 fluxVector_.z() = amf[2];
655 } else {
656 std::string fluxStr = rnemdParams->getFluxType();
657
658 if (fluxStr.find("Lx") != std::string::npos) {
659 fluxVector_ = V3X;
660 } else if (fluxStr.find("Ly") != std::string::npos) {
661 fluxVector_ = V3Y;
662 } else {
663 fluxVector_ = V3Z;
664 }
665 }
666
667 fluxVector_.normalize();
668
669 setOutputName(getPrefix(filename) + ".rnemdRTheta");
670
671 // Pre-load the OutputData
672 r_.units = "Angstroms";
673 r_.title = "R";
674 for (int i = 0; i < nBins_; i++)
675 r_.accumulator.push_back(
676 std::make_unique<AccumulatorView<RealAccumulator>>());
677
678 angularVelocity_.units = "1/fs";
679 angularVelocity_.title = "Projected Angular Velocity";
680 for (unsigned int i = 0; i < nBins_; i++) {
681 angularVelocity_.accumulator.push_back(
682 std::make_unique<AccumulatorView<StdVectorAccumulator>>());
683 }
684 }
685
686 std::pair<int, int> RNEMDRTheta::getBins(Vector3d pos) {
687 std::pair<int, int> result;
688
689 Vector3d rPos = pos - coordinateOrigin_;
690 RealType cosAngle = dot(rPos, fluxVector_) / rPos.length();
691
692 result.first = int(rPos.length() / binWidth_);
693 result.second = int((nAngleBins_)*0.5 * (cosAngle + 1.0));
694 return result;
695 }
696
697 void RNEMDRTheta::processFrame(int istep) {
698 ShellStatistics::processFrame(istep);
699
700 if (evaluator_.isDynamic()) {
701 seleMan_.setSelectionSet(evaluator_.evaluate());
702 }
703
704 StuntDouble* sd;
705 int i;
706
707 std::vector<std::vector<int>> binCount(nBins_);
708 std::vector<std::vector<Mat3x3d>> binI(nBins_);
709 std::vector<std::vector<Vector3d>> binL(nBins_);
710
711 for (std::size_t i {}; i < nBins_; ++i) {
712 binCount[i].resize(nAngleBins_);
713 binI[i].resize(nAngleBins_);
714 binL[i].resize(nAngleBins_);
715 }
716
717 // loop over the selected atoms:
718 for (sd = seleMan_.beginSelected(i); sd != NULL;
719 sd = seleMan_.nextSelected(i)) {
720 // figure out where that object is:
721 std::pair<int, int> bins = getBins(sd->getPos());
722
723 if (bins.first >= 0 && bins.first < int(nBins_)) {
724 if (bins.second >= 0 && bins.second < nAngleBins_) {
725 Vector3d rPos = sd->getPos() - coordinateOrigin_;
726 Vector3d vel = sd->getVel();
727 RealType m = sd->getMass();
728 Vector3d L = m * cross(rPos, vel);
729 Mat3x3d I(0.0);
730 I = outProduct(rPos, rPos) * m;
731 RealType r2 = rPos.lengthSquare();
732 I(0, 0) += m * r2;
733 I(1, 1) += m * r2;
734 I(2, 2) += m * r2;
735
736 binI[bins.first][bins.second] += I;
737 binL[bins.first][bins.second] += L;
738 binCount[bins.first][bins.second]++;
739 }
740 }
741 }
742
743 for (unsigned int i = 0; i < nBins_; i++) {
744 RealType r = (((RealType)i + 0.5) * binWidth_);
745 r_.accumulator[i]->add(r);
746
747 std::vector<RealType> projections(nAngleBins_);
748
749 for (int j = 0; j < nAngleBins_; j++) {
750 Vector3d omega(0.0);
751
752 if (binCount[i][j] > 0) { omega = binI[i][j].inverse() * binL[i][j]; }
753
754 // RealType omegaProj = dot(omega, fluxVector_);
755 projections[j] = dot(omega, fluxVector_);
756 }
757
758 angularVelocity_.accumulator[i]->add(projections);
759 }
760 }
761
762 void RNEMDRTheta::writeOutput() {
763 std::ofstream outStream(outputFilename_.c_str());
764
765 if (outStream.is_open()) {
766 // write title
767 outStream << "# SPATIAL STATISTICS\n";
768 outStream << "#nBins = " << nBins_ << "\t binWidth = " << binWidth_
769 << " maxR = " << nBins_ * binWidth_ << "\n";
770 outStream << "#fluxVector = " << fluxVector_ << "\tBins = " << nAngleBins_
771 << "\n";
772 outStream << "#\t" << angularVelocity_.title << "("
773 << angularVelocity_.units << ")\t\t";
774
775 outStream << std::endl;
776
777 outStream.precision(8);
778
779 for (unsigned int i = 0; i < nBins_; i++) {
780 std::size_t n {r_.accumulator[i]->getCount()};
781
782 if (n != 0) {
783 std::string message =
784 "StaticAnalyser detected a numerical error writing: " +
785 angularVelocity_.title + " for bin " + std::to_string(i);
786
787 angularVelocity_.accumulator[i]->writeData(outStream, message);
788 }
789
790 outStream << std::endl;
791 }
792 }
793 }
794} // namespace OpenMD
AtomType is what OpenMD looks to for unchanging data about an atom.
Definition AtomType.hpp:66
bool isDynamic()
Tests if the result from evaluation of script is dynamic.
StuntDouble * nextSelected(int &i)
Finds the next selected StuntDouble in the selection.
StuntDouble * beginSelected(int &i)
Finds the first selected StuntDouble in the selection.
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
Definition SimInfo.hpp:93
Molecule * beginMolecule(MoleculeIterator &i)
Returns the first molecule in this SimInfo and intialize the iterator.
Definition SimInfo.cpp:240
Molecule * nextMolecule(MoleculeIterator &i)
Returns the next avaliable Molecule based on the iterator.
Definition SimInfo.cpp:245
void wrapVector(Vector3d &v)
Wrapping the vector according to periodic boundary condition.
Definition Snapshot.cpp:337
"Don't move, or you're dead! Stand up! Captain, we've got them!"
Vector3d getVel()
Returns the current velocity of this stuntDouble.
int linearAxis()
Returns the linear axis of the rigidbody, atom and directional atom will always return -1.
RealType getMass()
Returns the mass of this stuntDouble.
virtual Mat3x3d getI()=0
Returns the inertia tensor of this stuntDouble.
bool isLinear()
Tests the if this stuntDouble is a linear rigidbody.
Vector3d getPos()
Returns the current position of this stuntDouble.
RealType getFlucQPos()
Returns the current fluctuating charge of this stuntDouble.
Vector3d getElectricField()
Returns the current electric field of this stuntDouble.
bool isRigidBody()
Tests if this stuntDouble is a rigid body.
Vector3d getJ()
Returns the current angular momentum of this stuntDouble (body -fixed).
bool isAtom()
Tests if this stuntDouble is an atom.
bool isDirectional()
Tests if this stuntDouble is a directional one.
RealType getFlucQVel()
Returns the current charge velocity of this stuntDouble.
Real lengthSquare()
Returns the squared length of this vector.
Definition Vector.hpp:399
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)