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