45#include "rnemd/RNEMD.hpp"
63#include "brains/Thermo.hpp"
64#include "io/Globals.hpp"
65#include "math/ConvexHull.hpp"
72#include "rnemd/RNEMDParameters.hpp"
73#include "types/FixedChargeAdapter.hpp"
74#include "types/FluctuatingChargeAdapter.hpp"
75#include "utils/Accumulator.hpp"
76#include "utils/AccumulatorView.hpp"
77#include "utils/Constants.hpp"
79using namespace OpenMD::Utils;
81namespace OpenMD::RNEMD {
84 info_(info), commonA_(info_), commonB_(info_), evaluator_(info_),
85 seleMan_(info_), evaluatorA_(info_), evaluatorB_(info_), seleManA_(info_),
86 seleManB_(info_), outputEvaluator_(info_), outputSeleMan_(info_) {
91 Globals* simParams = info->getSimParams();
92 RNEMDParameters* rnemdParams = simParams->getRNEMDParameters();
94 usePeriodicBoundaryConditions_ =
95 simParams->getUsePeriodicBoundaryConditions();
97 doRNEMD_ = rnemdParams->getUseRNEMD();
98 if (!doRNEMD_)
return;
101 std::map<std::string, RNEMDFluxType> stringToFluxType;
103 stringToFluxType[
"KE"] = rnemdKE;
104 stringToFluxType[
"Px"] = rnemdPx;
105 stringToFluxType[
"Py"] = rnemdPy;
106 stringToFluxType[
"Pz"] = rnemdPz;
107 stringToFluxType[
"Pvector"] = rnemdPvector;
108 stringToFluxType[
"Lx"] = rnemdLx;
109 stringToFluxType[
"Ly"] = rnemdLy;
110 stringToFluxType[
"Lz"] = rnemdLz;
111 stringToFluxType[
"Lvector"] = rnemdLvector;
112 stringToFluxType[
"Particle"] = rnemdParticle;
113 stringToFluxType[
"Particle+KE"] = rnemdParticleKE;
114 stringToFluxType[
"KE+Px"] = rnemdKePx;
115 stringToFluxType[
"KE+Py"] = rnemdKePy;
116 stringToFluxType[
"KE+Pvector"] = rnemdKePvector;
117 stringToFluxType[
"KE+Lx"] = rnemdKeLx;
118 stringToFluxType[
"KE+Ly"] = rnemdKeLy;
119 stringToFluxType[
"KE+Lz"] = rnemdKeLz;
120 stringToFluxType[
"KE+Lvector"] = rnemdKeLvector;
122 if (rnemdParams->haveFluxType()) {
123 rnemdFluxTypeLabel_ = rnemdParams->getFluxType();
124 rnemdFluxType_ = stringToFluxType.find(rnemdFluxTypeLabel_)->second;
126 std::string allowedFluxTypes;
127 int currentLineLength = 0;
129 for (std::map<std::string, RNEMDFluxType>::iterator fluxStrIter =
130 stringToFluxType.begin();
131 fluxStrIter != stringToFluxType.end(); ++fluxStrIter) {
132 allowedFluxTypes += fluxStrIter->first +
", ";
133 currentLineLength += fluxStrIter->first.length() + 2;
135 if (currentLineLength >= 50) {
136 allowedFluxTypes +=
"\n\t\t";
137 currentLineLength = 0;
141 allowedFluxTypes.erase(allowedFluxTypes.length() - 2, 2);
143 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
144 "RNEMD: No fluxType was set in the omd file. This parameter\n"
145 "\tmust be set to use RNEMD, and can take any of these values:\n"
147 allowedFluxTypes.c_str());
148 painCave.isFatal = 1;
149 painCave.severity = OPENMD_ERROR;
154 const std::string privAxis = rnemdParams->getPrivilegedAxis();
156 if (privAxis ==
"x") {
157 rnemdAxisLabel_ =
"x";
158 rnemdPrivilegedAxis_ = rnemdX;
159 }
else if (privAxis ==
"y") {
160 rnemdAxisLabel_ =
"y";
161 rnemdPrivilegedAxis_ = rnemdY;
163 rnemdAxisLabel_ =
"z";
164 rnemdPrivilegedAxis_ = rnemdZ;
167 runTime_ = simParams->getRunTime();
168 statusTime_ = simParams->getStatusTime();
170 rnemdObjectSelection_ = rnemdParams->getObjectSelection();
172 bool hasSlabWidth = rnemdParams->haveSlabWidth();
173 bool hasSlabACenter = rnemdParams->haveSlabACenter();
174 bool hasSlabBCenter = rnemdParams->haveSlabBCenter();
175 bool hasSphereARadius = rnemdParams->haveSphereARadius();
176 bool hasSphereBRadius = rnemdParams->haveSphereBRadius();
178 hasSelectionA_ = rnemdParams->haveSelectionA();
179 hasSelectionB_ = rnemdParams->haveSelectionB();
181 hasDividingArea_ = rnemdParams->haveDividingArea();
182 dividingArea_ = rnemdParams->getDividingArea();
184 bool hasCoordinateOrigin = rnemdParams->haveCoordinateOrigin();
185 bool hasOutputFileName = rnemdParams->haveOutputFileName();
186 bool hasOutputFields = rnemdParams->haveOutputFields();
187 bool hasOutputSelection = rnemdParams->haveOutputSelection();
189 if (hasOutputSelection) {
190 outputSelection_ = rnemdParams->getOutputSelection();
192 outputSelection_ = rnemdObjectSelection_;
195 if (hasCoordinateOrigin) {
196 std::vector<RealType> co = rnemdParams->getCoordinateOrigin();
197 if (co.size() != 3) {
198 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
199 "RNEMD: Incorrect number of parameters specified for "
200 "coordinateOrigin.\n"
201 "\tthere should be 3 parameters, but %zu were specified.\n",
203 painCave.isFatal = 1;
206 coordinateOrigin_.x() = co[0];
207 coordinateOrigin_.y() = co[1];
208 coordinateOrigin_.z() = co[2];
210 coordinateOrigin_ = V3Zero;
213 outputEvaluator_.loadScriptString(outputSelection_);
214 outputSeleMan_.setSelectionSet(outputEvaluator_.evaluate());
217 outputSeleMan_.replaceRigidBodiesWithAtoms();
220 std::copy(osTypes.begin(), osTypes.end(), std::back_inserter(outputTypes_));
222 nBins_ = rnemdParams->getOutputBins();
223 binWidth_ = rnemdParams->getOutputBinWidth();
226 data_.resize(RNEMD::ENDINDEX);
229 z.units =
"Angstroms";
230 z.title = rnemdAxisLabel_;
231 for (
unsigned int i = 0; i < nBins_; i++)
232 z.accumulator.push_back(
234 data_[Z] = std::move(z);
238 r.units =
"Angstroms";
240 for (
unsigned int i = 0; i < nBins_; i++)
241 r.accumulator.push_back(
243 data_[R] = std::move(r);
246 OutputData temperature;
247 temperature.units =
"K";
248 temperature.title =
"Temperature";
249 for (
unsigned int i = 0; i < nBins_; i++)
250 temperature.accumulator.push_back(
252 data_[TEMPERATURE] = std::move(temperature);
253 outputMap_[
"TEMPERATURE"] = TEMPERATURE;
256 velocity.units =
"angstroms/fs";
257 velocity.title =
"Velocity";
258 for (
unsigned int i = 0; i < nBins_; i++)
259 velocity.accumulator.push_back(
261 data_[VELOCITY] = std::move(velocity);
262 outputMap_[
"VELOCITY"] = VELOCITY;
264 OutputData angularVelocity;
265 angularVelocity.units =
"angstroms^2/fs";
266 angularVelocity.title =
"AngularVelocity";
267 for (
unsigned int i = 0; i < nBins_; i++)
268 angularVelocity.accumulator.push_back(
270 data_[ANGULARVELOCITY] = std::move(angularVelocity);
271 outputMap_[
"ANGULARVELOCITY"] = ANGULARVELOCITY;
274 density.units =
"g cm^-3";
275 density.title =
"Density";
276 for (
unsigned int i = 0; i < nBins_; i++)
277 density.accumulator.push_back(
279 data_[DENSITY] = std::move(density);
280 outputMap_[
"DENSITY"] = DENSITY;
283 activity.units =
"unitless";
284 activity.title =
"Activity";
285 for (
unsigned int i = 0; i < nBins_; i++)
286 activity.accumulator.push_back(
288 data_[ACTIVITY] = std::move(activity);
289 outputMap_[
"ACTIVITY"] = ACTIVITY;
292 eField.units =
"kcal/mol/angstroms/e";
293 eField.title =
"Electric Field";
294 for (
unsigned int i = 0; i < nBins_; i++)
295 eField.accumulator.push_back(
297 data_[ELECTRICFIELD] = std::move(eField);
298 outputMap_[
"ELECTRICFIELD"] = ELECTRICFIELD;
301 ePot.units =
"kcal/mol/e";
302 ePot.title =
"Electrostatic Potential";
303 for (
unsigned int i = 0; i < nBins_; i++)
304 ePot.accumulator.push_back(
306 data_[ELECTROSTATICPOTENTIAL] = std::move(ePot);
307 outputMap_[
"ELECTROSTATICPOTENTIAL"] = ELECTROSTATICPOTENTIAL;
309 if (hasOutputFields) {
310 parseOutputFileFormat(rnemdParams->getOutputFields());
312 if (usePeriodicBoundaryConditions_)
316 switch (rnemdFluxType_) {
320 outputMask_.set(TEMPERATURE);
324 outputMask_.set(VELOCITY);
328 outputMask_.set(VELOCITY);
329 outputMask_.set(DENSITY);
335 outputMask_.set(ANGULARVELOCITY);
341 outputMask_.set(TEMPERATURE);
342 outputMask_.set(ANGULARVELOCITY);
346 outputMask_.set(TEMPERATURE);
347 outputMask_.set(VELOCITY);
350 outputMask_.set(TEMPERATURE);
351 outputMask_.set(VELOCITY);
352 outputMask_.set(DENSITY);
359 if (hasOutputFileName) {
360 rnemdFileName_ = rnemdParams->getOutputFileName();
362 rnemdFileName_ =
getPrefix(info->getFinalConfigFileName()) +
".rnemd";
367 exchangeTime_ = rnemdParams->getExchangeTime();
368 RealType dt = simParams->getDt();
369 RealType newET = std::ceil(exchangeTime_ / dt) * dt;
371 if (std::fabs(newET - exchangeTime_) > 1e-6) {
372 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
373 "RNEMD: The exchangeTime was reset to %lf,\n"
374 "\t\twhich is a multiple of dt, %lf.\n",
376 painCave.isFatal = 0;
377 painCave.severity = OPENMD_WARNING;
379 exchangeTime_ = newET;
383 hmat_ = currentSnap_->
getHmat();
386 std::ostringstream selectionAstream;
387 std::ostringstream selectionBstream;
389 if (hasSelectionA_) {
390 selectionA_ = rnemdParams->getSelectionA();
392 if (usePeriodicBoundaryConditions_) {
395 rnemdParams->getSlabWidth() :
396 hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_) / 10.0;
398 slabACenter_ = hasSlabACenter ? rnemdParams->getSlabACenter() : 0.0;
400 selectionA_ = this->setSelection(slabACenter_);
402 if (hasSphereARadius)
403 sphereARadius_ = rnemdParams->getSphereARadius();
408 RealType hVol = thermo.getHullVolume();
410 0.1 * pow((3.0 * hVol / (4.0 * Constants::PI)), 1.0 / 3.0);
412 selectionAstream <<
"select r < " << sphereARadius_;
413 selectionA_ = selectionAstream.str();
417 if (hasSelectionB_) {
418 selectionB_ = rnemdParams->getSelectionB();
420 if (usePeriodicBoundaryConditions_) {
423 rnemdParams->getSlabWidth() :
424 hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_) / 10.0;
428 rnemdParams->getSlabBCenter() :
429 hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_) / 2.0;
431 selectionB_ = this->setSelection(slabBCenter_);
433 if (hasSphereBRadius) {
434 sphereBRadius_ = rnemdParams->getSphereBRadius();
435 selectionBstream <<
"select r > " << sphereBRadius_;
436 selectionB_ = selectionBstream.str();
438 selectionB_ =
"select hull";
439 hasSelectionB_ =
true;
445 evaluator_.loadScriptString(rnemdObjectSelection_);
446 if (!evaluator_.isDynamic())
447 seleMan_.setSelectionSet(evaluator_.evaluate());
449 evaluatorA_.loadScriptString(selectionA_);
450 if (!evaluatorA_.isDynamic())
451 seleManA_.setSelectionSet(evaluatorA_.evaluate());
453 evaluatorB_.loadScriptString(selectionB_);
454 if (!evaluatorB_.isDynamic())
455 seleManB_.setSelectionSet(evaluatorB_.evaluate());
459 seleMan_.removeAtomsInRigidBodies().getSelectionCount();
462 if (selectionCount > nIntegrable) {
463 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
464 "RNEMD: The current objectSelection,\n"
466 "\thas resulted in %d selected objects. However,\n"
467 "\tthe total number of integrable objects in the system\n"
468 "\tis only %d. This is almost certainly not what you want\n"
470 rnemdObjectSelection_.c_str(), selectionCount, nIntegrable);
471 painCave.isFatal = 0;
472 painCave.severity = OPENMD_WARNING;
478 if (!doRNEMD_)
return;
480 if (worldRank == 0) {
490 void RNEMD::getStarted() {
491 if (!doRNEMD_)
return;
496 void RNEMD::doRNEMD() {
497 if (!doRNEMD_)
return;
499 hmat_ = currentSnap_->getHmat();
502 evaluator_.loadScriptString(rnemdObjectSelection_);
503 if (evaluator_.isDynamic()) seleMan_.setSelectionSet(evaluator_.evaluate());
505 evaluatorA_.loadScriptString(selectionA_);
506 if (evaluatorA_.isDynamic())
507 seleManA_.setSelectionSet(evaluatorA_.evaluate());
509 evaluatorB_.loadScriptString(selectionB_);
510 if (evaluatorB_.isDynamic())
511 seleManB_.setSelectionSet(evaluatorB_.evaluate());
513 commonA_ = seleManA_ & seleMan_;
514 commonB_ = seleManB_ & seleMan_;
516 auto reducedCommonA = commonA_.removeAtomsInRigidBodies();
517 auto reducedCommonB = commonB_.removeAtomsInRigidBodies();
523 RealType area = getDefaultDividingArea();
525 kineticTarget_ = kineticFlux_ * exchangeTime_ * area;
526 momentumTarget_ = momentumFluxVector_ * exchangeTime_ * area;
527 angularMomentumTarget_ = angularMomentumFluxVector_ * exchangeTime_ * area;
528 particleTarget_ = particleFlux_ * exchangeTime_ * area;
530 if (std::fabs(particleTarget_) > 1.0) {
531 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
532 "RNEMD: The current particleFlux,\n"
534 "\thas resulted in a target particle exchange of %f.\n"
535 "\tThis is equivalent to moving more than one particle\n"
536 "\tduring each exchange. Please reduce your particleFlux.\n",
537 particleFlux_, particleTarget_);
538 painCave.isFatal = 1;
539 painCave.severity = OPENMD_ERROR;
543 if (rnemdFluxType_ == rnemdParticle || rnemdFluxType_ == rnemdParticleKE) {
547 auto reducedTempCommonA = tempCommonA.removeAtomsInRigidBodies();
548 auto reducedTempCommonB = tempCommonB.removeAtomsInRigidBodies();
550 this->doRNEMDImpl(reducedTempCommonA, reducedTempCommonB);
552 this->doRNEMDImpl(reducedCommonA, reducedCommonB);
556 void RNEMD::collectData() {
557 if (!doRNEMD_)
return;
558 currentSnap_ = info_->getSnapshotManager()->getCurrentSnapshot();
559 hmat_ = currentSnap_->getHmat();
563 RealType area = getDefaultDividingArea();
564 areaAccumulator_.add(area);
566 Vector3d u = angularMomentumFluxVector_;
570 if (outputEvaluator_.isDynamic()) {
571 outputSeleMan_.setSelectionSet(outputEvaluator_.evaluate());
586 vector<RealType> binMass(nBins_, 0.0);
587 vector<Vector3d> binP(nBins_, V3Zero);
588 vector<RealType> binOmega(nBins_, 0.0);
589 vector<Vector3d> binL(nBins_, V3Zero);
590 vector<Mat3x3d> binI(nBins_);
591 vector<RealType> binKE(nBins_, 0.0);
592 vector<Vector3d> binEField(nBins_, V3Zero);
593 vector<int> binDOF(nBins_, 0);
594 vector<int> binCount(nBins_, 0);
595 vector<int> binEFieldCount(nBins_, 0);
596 vector<vector<int>> binTypeCounts;
598 if (outputMask_[ACTIVITY]) {
599 binTypeCounts.resize(nBins_);
600 for (
unsigned int i = 0; i < nBins_; i++) {
601 binTypeCounts[i].resize(outputTypes_.size(), 0);
605 SimInfo::MoleculeIterator miter;
606 std::vector<StuntDouble*>::iterator iiter;
607 std::vector<AtomType*>::iterator at;
612 Molecule::ConstraintPairIterator cpi;
614 for (mol = info_->beginMolecule(miter); mol != NULL;
615 mol = info_->nextMolecule(miter)) {
616 for (sd = mol->beginIntegrableObject(iiter); sd != NULL;
617 sd = mol->nextIntegrableObject(iiter)) {
618 if (outputSeleMan_.isSelected(sd)) {
624 rPos = sd->
getPos() - coordinateOrigin_;
635 KE += 0.5 * (angMom[j] * angMom[j] / Ia(j, j) +
636 angMom[k] * angMom[k] / Ia(k, k));
639 KE += 0.5 * (angMom[0] * angMom[0] / Ia(0, 0) +
640 angMom[1] * angMom[1] / Ia(1, 1) +
641 angMom[2] * angMom[2] / Ia(2, 2));
646 L = mass *
cross(rPos, vel);
647 I = outProduct(rPos, rPos) * mass;
648 r2 = rPos.lengthSquare();
649 I(0, 0) += mass * r2;
650 I(1, 1) += mass * r2;
651 I(2, 2) += mass * r2;
653 if (outputMask_[ACTIVITY]) {
658 std::vector<Atom*>::iterator ai;
660 for (atom = rb->beginAtom(ai); atom != NULL;
661 atom = rb->nextAtom(ai)) {
662 atomBinNo = getBin(atom->
getPos());
664 atype =
static_cast<Atom*
>(atom)->getAtomType();
665 at = std::find(outputTypes_.begin(), outputTypes_.end(), atype);
666 if (at != outputTypes_.end()) {
667 typeIndex = std::distance(outputTypes_.begin(), at);
670 if (atomBinNo >= 0 && atomBinNo <
int(nBins_)) {
671 if (typeIndex != -1) binTypeCounts[atomBinNo][typeIndex]++;
674 }
else if (sd->
isAtom()) {
675 atype =
static_cast<Atom*
>(sd)->getAtomType();
676 at = std::find(outputTypes_.begin(), outputTypes_.end(), atype);
677 if (at != outputTypes_.end()) {
678 typeIndex = std::distance(outputTypes_.begin(), at);
683 if (binNo >= 0 && binNo <
int(nBins_)) {
685 binMass[binNo] += mass;
686 binP[binNo] += mass * vel;
690 binDOF[binNo] += DOF;
692 if (outputMask_[ACTIVITY] && typeIndex != -1)
693 binTypeCounts[binNo][typeIndex]++;
699 if (outputMask_[ELECTRICFIELD]) {
703 std::vector<Atom*>::iterator ai;
705 for (atom = rb->beginAtom(ai); atom != NULL;
706 atom = rb->nextAtom(ai)) {
707 atomBinNo = getBin(atom->
getPos());
710 binEFieldCount[atomBinNo]++;
711 binEField[atomBinNo] += eField;
715 atomBinNo = getBin(sd->
getPos());
717 binEFieldCount[atomBinNo]++;
718 binEField[atomBinNo] += eField;
725 if (outputSeleMan_.isSelected(mol)) {
726 for (consPair = mol->beginConstraintPair(cpi); consPair != NULL;
727 consPair = mol->nextConstraintPair(cpi)) {
731 if (usePeriodicBoundaryConditions_) {
732 currentSnap_->wrapVector(posA);
733 currentSnap_->wrapVector(posB);
737 int binCons = getBin(coc);
738 binDOF[binCons] -= 1;
744 for (
unsigned int i = 0; i < nBins_; i++) {
745 MPI_Allreduce(MPI_IN_PLACE, &binCount[i], 1, MPI_INT, MPI_SUM,
747 MPI_Allreduce(MPI_IN_PLACE, &binMass[i], 1, MPI_REALTYPE, MPI_SUM,
749 MPI_Allreduce(MPI_IN_PLACE, binP[i].getArrayPointer(), 3, MPI_REALTYPE,
750 MPI_SUM, MPI_COMM_WORLD);
751 MPI_Allreduce(MPI_IN_PLACE, binL[i].getArrayPointer(), 3, MPI_REALTYPE,
752 MPI_SUM, MPI_COMM_WORLD);
753 MPI_Allreduce(MPI_IN_PLACE, binI[i].getArrayPointer(), 9, MPI_REALTYPE,
754 MPI_SUM, MPI_COMM_WORLD);
755 MPI_Allreduce(MPI_IN_PLACE, &binKE[i], 1, MPI_REALTYPE, MPI_SUM,
757 MPI_Allreduce(MPI_IN_PLACE, &binDOF[i], 1, MPI_INT, MPI_SUM,
759 MPI_Allreduce(MPI_IN_PLACE, &binEFieldCount[i], 1, MPI_INT, MPI_SUM,
762 if (outputMask_[ELECTRICFIELD]) {
763 MPI_Allreduce(MPI_IN_PLACE, binEField[i].getArrayPointer(), 3,
764 MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
766 if (outputMask_[ACTIVITY]) {
767 MPI_Allreduce(MPI_IN_PLACE, &binTypeCounts[i][0], outputTypes_.size(),
768 MPI_INT, MPI_SUM, MPI_COMM_WORLD);
774 RealType z, r, temp, binVolume, den(0.0), dz(0.0);
775 std::vector<RealType> nden(outputTypes_.size(), 0.0);
776 RealType boxVolume = currentSnap_->getVolume();
779 for (
unsigned int i = 0; i < nBins_; i++) {
780 if (usePeriodicBoundaryConditions_) {
781 z = (((RealType)i + 0.5) / (RealType)nBins_) *
782 hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_);
783 data_[Z].accumulator[i]->add(z);
785 binVolume = boxVolume / nBins_;
786 dz = hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_) /
789 r = (((RealType)i + 0.5) * binWidth_);
790 data_[R].accumulator[i]->add(r);
792 RealType rinner = (RealType)i * binWidth_;
793 RealType router = (RealType)(i + 1) * binWidth_;
795 (4.0 * Constants::PI * (pow(router, 3) - pow(rinner, 3))) / 3.0;
800 if (outputMask_[ELECTRICFIELD] && binEFieldCount[i] > 0) {
801 eField = binEField[i] / RealType(binEFieldCount[i]);
802 data_[ELECTRICFIELD].accumulator[i]->add(eField);
805 if (outputMask_[ELECTROSTATICPOTENTIAL]) {
806 if (usePeriodicBoundaryConditions_ && binEFieldCount[i] > 0) {
807 ePot += eField[rnemdPrivilegedAxis_] * dz;
808 data_[ELECTROSTATICPOTENTIAL].accumulator[i]->add(ePot);
814 if (outputMask_[DENSITY]) {
815 den = binMass[i] * Constants::densityConvert / binVolume;
816 data_[DENSITY].accumulator[i]->add(den);
819 if (outputMask_[ACTIVITY]) {
820 for (
unsigned int j = 0; j < outputTypes_.size(); j++) {
821 nden[j] = (binTypeCounts[i][j] / binVolume) *
822 Constants::concentrationConvert;
824 data_[ACTIVITY].accumulator[i]->add(nden);
827 if (binCount[i] > 0) {
830 if (outputMask_[VELOCITY]) {
831 vel = binP[i] / binMass[i];
832 data_[VELOCITY].accumulator[i]->add(vel);
835 if (outputMask_[ANGULARVELOCITY]) {
836 omega = binI[i].inverse() * binL[i];
837 data_[ANGULARVELOCITY].accumulator[i]->
add(omega);
840 if (outputMask_[TEMPERATURE]) {
842 temp = 2.0 * binKE[i] /
843 (binDOF[i] * Constants::kb * Constants::energyConvert);
844 data_[TEMPERATURE].accumulator[i]->add(temp);
846 std::cerr <<
"No degrees of freedom in this bin?\n";
855 void RNEMD::writeOutputFile() {
856 if (!doRNEMD_)
return;
857 if (!hasData_)
return;
862 MPI_Comm_rank(MPI_COMM_WORLD, &worldRank);
864 if (worldRank == 0) {
866 rnemdFile_.open(rnemdFileName_.c_str(), std::ios::out | std::ios::trunc);
869 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
870 "Could not open \"%s\" for RNEMD output.\n",
871 rnemdFileName_.c_str());
872 painCave.isFatal = 1;
876 RealType time = currentSnap_->getTime();
877 RealType avgArea = areaAccumulator_.getAverage();
884 if (time >= info_->getSimParams()->getDt()) {
885 Jz = kineticExchange_ / (time * avgArea) / Constants::energyConvert;
886 JzP = momentumExchange_ / (time * avgArea);
887 JzL = angularMomentumExchange_ / (time * avgArea);
888 Jpart = particleExchange_ / (time * avgArea);
891 rnemdFile_ <<
"#######################################################\n";
892 rnemdFile_ <<
"# RNEMD {\n";
893 rnemdFile_ <<
"# exchangeMethod = \"" << rnemdMethodLabel_ <<
"\";\n";
894 rnemdFile_ <<
"# fluxType = \"" << rnemdFluxTypeLabel_ <<
"\";\n";
896 if (usePeriodicBoundaryConditions_)
897 rnemdFile_ <<
"# privilegedAxis = " << rnemdAxisLabel_ <<
";\n";
899 rnemdFile_ <<
"# exchangeTime = " << exchangeTime_ <<
";\n";
900 rnemdFile_ <<
"# objectSelection = \"" << rnemdObjectSelection_
902 rnemdFile_ <<
"# selectionA = \"" << selectionA_ <<
"\";\n";
903 rnemdFile_ <<
"# selectionB = \"" << selectionB_ <<
"\";\n";
904 rnemdFile_ <<
"# outputSelection = \"" << outputSelection_ <<
"\";\n";
905 rnemdFile_ <<
"# }\n";
906 rnemdFile_ <<
"#######################################################\n";
907 rnemdFile_ <<
"# RNEMD report:\n";
908 rnemdFile_ <<
"# running time = " << time <<
" fs\n";
909 rnemdFile_ <<
"# Target flux:\n";
910 rnemdFile_ <<
"# kinetic = "
911 << kineticFlux_ / Constants::energyConvert
912 <<
" (kcal/mol/A^2/fs)\n";
913 rnemdFile_ <<
"# momentum = " << momentumFluxVector_
914 <<
" (amu/A/fs^2)\n";
915 rnemdFile_ <<
"# angular momentum = " << angularMomentumFluxVector_
916 <<
" (amu/A^2/fs^2)\n";
917 rnemdFile_ <<
"# particle = " << particleFlux_
918 <<
" (particles/A^2/fs)\n";
920 rnemdFile_ <<
"# Target one-time exchanges:\n";
921 rnemdFile_ <<
"# kinetic = "
922 << kineticTarget_ / Constants::energyConvert
924 rnemdFile_ <<
"# momentum = " << momentumTarget_
926 rnemdFile_ <<
"# angular momentum = " << angularMomentumTarget_
927 <<
" (amu*A^2/fs)\n";
928 rnemdFile_ <<
"# particle = " << particleTarget_
930 rnemdFile_ <<
"# Actual exchange totals:\n";
931 rnemdFile_ <<
"# kinetic = "
932 << kineticExchange_ / Constants::energyConvert
934 rnemdFile_ <<
"# momentum = " << momentumExchange_
936 rnemdFile_ <<
"# angular momentum = " << angularMomentumExchange_
937 <<
" (amu*A^2/fs)\n";
938 rnemdFile_ <<
"# particles = " << particleExchange_
941 rnemdFile_ <<
"# Actual flux:\n";
942 rnemdFile_ <<
"# kinetic = " << Jz <<
" (kcal/mol/A^2/fs)\n";
943 rnemdFile_ <<
"# momentum = " << JzP <<
" (amu/A/fs^2)\n";
944 rnemdFile_ <<
"# angular momentum = " << JzL <<
" (amu/A^2/fs^2)\n";
945 rnemdFile_ <<
"# particle = " << Jpart
946 <<
" (particles/A^2/fs)\n";
947 rnemdFile_ <<
"# Exchange statistics:\n";
948 rnemdFile_ <<
"# attempted = " << trialCount_ <<
"\n";
949 rnemdFile_ <<
"# failed = " << failTrialCount_ <<
"\n";
950 if (rnemdMethodLabel_ ==
"NIVS") {
951 rnemdFile_ <<
"# NIVS root-check errors = " << failRootCount_ <<
"\n";
953 rnemdFile_ <<
"#######################################################\n";
957 for (
unsigned int i = 0; i < outputMask_.size(); ++i) {
958 if (outputMask_[i]) {
959 rnemdFile_ <<
"\t" << data_[i].title <<
"(" << data_[i].units <<
")";
962 if (data_[i].accumulator[0]->getType() ==
963 std::type_index(
typeid(
Vector3d))) {
964 rnemdFile_ <<
"\t\t";
967 if (data_[i].accumulator[0]->getType() ==
968 std::type_index(
typeid(std::vector<RealType>))) {
970 for (
unsigned int type = 0; type < outputTypes_.size(); type++) {
971 rnemdFile_ << outputTypes_[type]->getName() <<
"\t";
978 rnemdFile_ << std::endl;
980 std::vector<int> nonEmptyAccumulators(nBins_);
981 int numberOfAccumulators {};
983 for (
unsigned int i = 0; i < outputMask_.size(); ++i) {
984 if (outputMask_[i]) {
985 for (
unsigned int bin = 0; bin < nBins_; bin++) {
986 nonEmptyAccumulators[bin] +=
987 static_cast<int>(data_[i].accumulator[bin]->getCount() != 0);
990 numberOfAccumulators++;
994 rnemdFile_.precision(8);
996 for (
unsigned int bin = 0; bin < nBins_; bin++) {
997 if (nonEmptyAccumulators[bin] == numberOfAccumulators) {
998 for (
unsigned int i = 0; i < outputMask_.size(); ++i) {
999 if (outputMask_[i]) {
1000 std::string message =
1001 "RNEMD detected a numerical error writing: " +
1002 data_[i].title +
" for bin " + std::to_string(bin);
1004 data_[i].accumulator[bin]->writeData(rnemdFile_, message);
1008 rnemdFile_ << std::endl;
1012 rnemdFile_ <<
"#######################################################\n";
1013 rnemdFile_ <<
"# 95% confidence intervals in those quantities follow:\n";
1014 rnemdFile_ <<
"#######################################################\n";
1016 for (
unsigned int bin = 0; bin < nBins_; bin++) {
1017 if (nonEmptyAccumulators[bin] == numberOfAccumulators) {
1020 for (
unsigned int i = 0; i < outputMask_.size(); ++i) {
1021 if (outputMask_[i]) {
1022 std::string message =
1023 "RNEMD detected a numerical error writing: " +
1024 data_[i].title +
" std. dev. for bin " + std::to_string(bin);
1026 data_[i].accumulator[bin]->writeErrorBars(rnemdFile_, message);
1030 rnemdFile_ << std::endl;
1041 void RNEMD::setKineticFlux(RealType kineticFlux) {
1044 kineticFlux_ = kineticFlux * Constants::energyConvert;
1047 void RNEMD::setParticleFlux(RealType particleFlux) {
1048 particleFlux_ = particleFlux;
1051 void RNEMD::setMomentumFluxVector(
1052 const std::vector<RealType>& momentumFluxVector) {
1053 if (momentumFluxVector.size() != 3) {
1054 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
1055 "RNEMD: Incorrect number of parameters specified for "
1056 "momentumFluxVector.\n"
1057 "\tthere should be 3 parameters, but %zu were specified.\n",
1058 momentumFluxVector.size());
1059 painCave.isFatal = 1;
1063 momentumFluxVector_.x() = momentumFluxVector[0];
1064 momentumFluxVector_.y() = momentumFluxVector[1];
1065 momentumFluxVector_.z() = momentumFluxVector[2];
1068 void RNEMD::setAngularMomentumFluxVector(
1069 const std::vector<RealType>& angularMomentumFluxVector) {
1070 if (angularMomentumFluxVector.size() != 3) {
1071 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
1072 "RNEMD: Incorrect number of parameters specified for "
1073 "angularMomentumFluxVector.\n"
1074 "\tthere should be 3 parameters, but %zu were specified.\n",
1075 angularMomentumFluxVector.size());
1076 painCave.isFatal = 1;
1080 angularMomentumFluxVector_.x() = angularMomentumFluxVector[0];
1081 angularMomentumFluxVector_.y() = angularMomentumFluxVector[1];
1082 angularMomentumFluxVector_.z() = angularMomentumFluxVector[2];
1085 void RNEMD::parseOutputFileFormat(
const std::string& format) {
1086 if (!doRNEMD_)
return;
1089 while (tokenizer.hasMoreTokens()) {
1090 std::string token(tokenizer.nextToken());
1092 OutputMapType::iterator i = outputMap_.find(token);
1093 if (i != outputMap_.end()) {
1094 outputMask_.set(i->second);
1096 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
1097 "RNEMD::parseOutputFileFormat: %s is not a recognized\n"
1098 "\toutputFileFormat keyword.\n",
1100 painCave.isFatal = 0;
1101 painCave.severity = OPENMD_ERROR;
1107 std::string RNEMD::setSelection(RealType& slabCenter) {
1108 bool printSlabCenterWarning {
false};
1111 tempSlabCenter[rnemdPrivilegedAxis_] = slabCenter;
1113 RealType hmat_2 = hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_) / 2.0;
1115 if (slabCenter > hmat_2) {
1116 currentSnap_->wrapVector(tempSlabCenter);
1117 printSlabCenterWarning =
true;
1118 }
else if (slabCenter < -hmat_2) {
1119 currentSnap_->wrapVector(tempSlabCenter);
1120 printSlabCenterWarning =
true;
1123 if (printSlabCenterWarning) {
1124 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
1125 "The given slab center was set to %0.2f. In the wrapped "
1127 "\t[-Hmat/2, +Hmat/2], this has been remapped to %0.2f.\n",
1128 slabCenter, tempSlabCenter[rnemdPrivilegedAxis_]);
1129 painCave.isFatal = 0;
1130 painCave.severity = OPENMD_WARNING;
1133 slabCenter = tempSlabCenter[rnemdPrivilegedAxis_];
1137 const RealType& leftSlabBoundary = leftSlab[rnemdPrivilegedAxis_];
1138 leftSlab[rnemdPrivilegedAxis_] = slabCenter - 0.5 * slabWidth_;
1139 currentSnap_->wrapVector(leftSlab);
1142 const RealType& rightSlabBoundary = rightSlab[rnemdPrivilegedAxis_];
1143 rightSlab[rnemdPrivilegedAxis_] = slabCenter + 0.5 * slabWidth_;
1144 currentSnap_->wrapVector(rightSlab);
1146 std::ostringstream selectionStream;
1148 selectionStream <<
"select wrapped" << rnemdAxisLabel_
1149 <<
" >= " << leftSlabBoundary;
1151 if (leftSlabBoundary > rightSlabBoundary)
1152 selectionStream <<
" || wrapped" << rnemdAxisLabel_ <<
" < "
1153 << rightSlabBoundary;
1155 selectionStream <<
" && wrapped" << rnemdAxisLabel_ <<
" < "
1156 << rightSlabBoundary;
1158 return selectionStream.str();
1161 RealType RNEMD::getDefaultDividingArea() {
1162 if (hasDividingArea_)
return dividingArea_;
1164 Snapshot* snap = info_->getSnapshotManager()->getCurrentSnapshot();
1166 if (hasSelectionA_) {
1167 if (evaluatorA_.hasSurfaceArea()) {
1168 areaA_ = evaluatorA_.getSurfaceArea();
1169 volumeA_ = evaluatorA_.getVolume();
1173 std::vector<StuntDouble*> aSites;
1174 seleManA_.setSelectionSet(evaluatorA_.evaluate());
1175 for (sd = seleManA_.beginSelected(isd); sd != NULL;
1176 sd = seleManA_.nextSelected(isd)) {
1177 aSites.push_back(sd);
1179#if defined(HAVE_QHULL)
1181 surfaceMeshA->computeHull(aSites);
1182 areaA_ = surfaceMeshA->getArea();
1183 volumeA_ = surfaceMeshA->getVolume();
1184 delete surfaceMeshA;
1187 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
1188 "RNEMD::getDividingArea : Hull calculation is not possible\n"
1189 "\twithout libqhull. Please rebuild OpenMD with qhull enabled.");
1190 painCave.severity = OPENMD_ERROR;
1191 painCave.isFatal = 1;
1196 if (usePeriodicBoundaryConditions_) {
1199 switch (rnemdPrivilegedAxis_) {
1201 areaA_ = 2.0 * snap->getYZarea();
1204 areaA_ = 2.0 * snap->getXZarea();
1208 areaA_ = 2.0 * snap->getXYarea();
1211 volumeA_ = areaA_ * slabWidth_;
1216 areaA_ = 4.0 * Constants::PI * std::pow(sphereARadius_, 2);
1217 volumeA_ = 4.0 * Constants::PI * std::pow(sphereARadius_, 3) / 3.0;
1221 if (hasSelectionB_) {
1222 if (evaluatorB_.hasSurfaceArea()) {
1223 areaB_ = evaluatorB_.getSurfaceArea();
1224 volumeB_ = evaluatorB_.getVolume();
1228 std::vector<StuntDouble*> bSites;
1229 seleManB_.setSelectionSet(evaluatorB_.evaluate());
1230 for (sd = seleManB_.beginSelected(isd); sd != NULL;
1231 sd = seleManB_.nextSelected(isd)) {
1232 bSites.push_back(sd);
1235#if defined(HAVE_QHULL)
1237 surfaceMeshB->computeHull(bSites);
1238 areaB_ = surfaceMeshB->getArea();
1239 volumeB_ = surfaceMeshB->getVolume();
1240 delete surfaceMeshB;
1243 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
1244 "RNEMD::getDividingArea : Hull calculation is not possible\n"
1245 "\twithout libqhull. Please rebuild OpenMD with qhull enabled.");
1246 painCave.severity = OPENMD_ERROR;
1247 painCave.isFatal = 1;
1252 if (usePeriodicBoundaryConditions_) {
1255 switch (rnemdPrivilegedAxis_) {
1257 areaB_ = 2.0 * snap->getYZarea();
1260 areaB_ = 2.0 * snap->getXZarea();
1264 areaB_ = 2.0 * snap->getXYarea();
1267 volumeB_ = areaB_ * slabWidth_;
1271 areaB_ = 4.0 * Constants::PI * pow(sphereBRadius_, 2);
1273 RealType hVol = thermo.getHullVolume();
1274 volumeB_ = hVol - 4.0 * Constants::PI * pow(sphereBRadius_, 3) / 3.0;
1278 dividingArea_ = min(areaA_, areaB_);
1279 hasDividingArea_ =
true;
1280 return dividingArea_;
1284 if (usePeriodicBoundaryConditions_) {
1285 currentSnap_->wrapVector(pos);
1288 (pos[rnemdPrivilegedAxis_] /
1289 hmat_(rnemdPrivilegedAxis_, rnemdPrivilegedAxis_) +
1293 Vector3d rPos = pos - coordinateOrigin_;
1294 return int(rPos.
length() / binWidth_);
AtomType is what OpenMD looks to for unchanging data about an atom.
Vector3d getPos()
Returns the current position of this stuntdouble.
ConstraintElem * getConsElem1()
Return the first constraint elemet.
ConstraintElem * getConsElem2()
Retunr the second constraint element.
ForceManager is responsible for calculating both the short range (bonded) interactions and long range...
AtomTypeSet getSelectedAtomTypes()
getSelectedAtomTypes
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
int getNGlobalIntegrableObjects()
Returns the total number of integrable objects (total number of rigid bodies plus the total number of...
SnapshotManager * getSnapshotManager()
Returns the snapshot manager.
The Snapshot class is a repository storing dynamic data during a Simulation.
Mat3x3d getHmat()
Returns the H-Matrix.
Snapshot * getCurrentSnapshot()
Returns the pointer of current snapshot.
The string tokenizer class allows an application to break a string into tokens The set of delimiters ...
"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.
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.
Real length()
Returns the length of this vector.
void normalize()
Normalizes this vector in place.
Real lengthSquare()
Returns the squared length of this vector.
void add(const Vector< Real, Dim > &v1)
Sets the value of this vector to the sum of itself and v1 (*this += v1).
Vector3< Real > cross(const Vector3< Real > &v1, const Vector3< Real > &v2)
Returns the cross product of two Vectors.
std::string getPrefix(const std::string &str)