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