45#include "rnemd/SPF.hpp"
62#include "rnemd/RNEMD.hpp"
63#include "rnemd/RNEMDParameters.hpp"
64#include "rnemd/SPFForceManager.hpp"
65#include "selection/SelectionManager.hpp"
66#include "utils/Constants.hpp"
67#include "utils/RandNumGen.hpp"
68#include "utils/simError.h"
70namespace OpenMD::RNEMD {
73 RNEMD {info, forceMan}, selectedMoleculeMan_(info),
74 selectedMoleculeEvaluator_(info) {
75 rnemdMethodLabel_ =
"SPF";
77 selectedMoleculeStr_ =
"select none";
78 selectedMoleculeEvaluator_.loadScriptString(selectedMoleculeStr_);
79 selectedMoleculeMan_.setSelectionSet(selectedMoleculeEvaluator_.evaluate());
81 if (SPFForceManager* spfForceManager =
82 dynamic_cast<SPFForceManager*
>(forceMan)) {
83 forceManager_ = spfForceManager;
85 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
86 "SPF-RNEMD cannot be used with the default ForceManager.\n");
88 painCave.severity = OPENMD_ERROR;
92 RNEMDParameters* rnemdParams = info->getSimParams()->getRNEMDParameters();
94 bool hasParticleFlux = rnemdParams->haveParticleFlux();
95 bool hasKineticFlux = rnemdParams->haveKineticFlux();
97 bool methodFluxMismatch =
false;
98 bool hasCorrectFlux =
false;
100 switch (rnemdFluxType_) {
102 hasCorrectFlux = hasParticleFlux;
104 case rnemdParticleKE:
105 hasCorrectFlux = hasParticleFlux && hasKineticFlux;
108 methodFluxMismatch =
true;
112 if (methodFluxMismatch) {
113 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
114 "RNEMD: The current method, SPF\n"
115 "\tcannot be used with the current flux type, %s\n",
116 rnemdFluxTypeLabel_.c_str());
117 painCave.isFatal = 1;
118 painCave.severity = OPENMD_ERROR;
122 if (!hasCorrectFlux) {
123 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
124 "RNEMD: The current method, SPF, and flux type, %s,\n"
125 "\tdid not have the correct flux type specified. Options\n"
126 "\tinclude: particleFlux and particleFlux + kineticFlux.\n",
127 rnemdFluxTypeLabel_.c_str());
128 painCave.isFatal = 1;
129 painCave.severity = OPENMD_ERROR;
133 if (hasParticleFlux) {
134 setParticleFlux(rnemdParams->getParticleFlux());
136 setParticleFlux(0.0);
139 if (hasKineticFlux) {
140 setKineticFlux(rnemdParams->getKineticFlux());
145 uniformKineticScaling_ = rnemdParams->getSPFUniformKineticScaling();
151 if (!doRNEMD_)
return;
154 if (particleTarget_ > 0.0) {
155 smanA -= selectedMoleculeMan_;
157 smanB -= selectedMoleculeMan_;
160 failedLastTrial_ =
false;
162 int selei {}, selej {};
190 angMom[j] * angMom[j] / I(j, j) + angMom[k] * angMom[k] / I(k, k);
192 K_a += angMom[0] * angMom[0] / I(0, 0) +
193 angMom[1] * angMom[1] / I(1, 1) +
194 angMom[2] * angMom[2] / I(2, 2);
216 angMom[j] * angMom[j] / I(j, j) + angMom[k] * angMom[k] / I(k, k);
218 K_b += angMom[0] * angMom[0] / I(0, 0) +
219 angMom[1] * angMom[1] / I(1, 1) +
220 angMom[2] * angMom[2] / I(2, 2);
229 MPI_Allreduce(MPI_IN_PLACE, &P_a[0], 3, MPI_REALTYPE, MPI_SUM,
231 MPI_Allreduce(MPI_IN_PLACE, &P_b[0], 3, MPI_REALTYPE, MPI_SUM,
233 MPI_Allreduce(MPI_IN_PLACE, &M_a, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
234 MPI_Allreduce(MPI_IN_PLACE, &M_b, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
235 MPI_Allreduce(MPI_IN_PLACE, &K_a, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
236 MPI_Allreduce(MPI_IN_PLACE, &K_b, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
239 bool successfulExchange =
false;
240 bool doExchange =
false;
242 if ((M_a > 0.0) && (M_b > 0.0) &&
243 forceManager_->getHasSelectedMolecule()) {
247 RealType tempParticleTarget =
248 (particleTarget_ != deltaLambda_) ? deltaLambda_ : particleTarget_;
250 RealType deltaU = forceManager_->getScaledDeltaU(tempParticleTarget) *
251 Constants::energyConvert;
255 if (uniformKineticScaling_) {
256 RealType numerator = deltaU;
257 RealType denominator = K_a + K_b;
261 RealType a2 = (numerator / denominator) + 1.0;
268 if ((a > 0.999) && (a < 1.001)) { doExchange =
true; }
271 RealType aNumerator = deltaU - kineticTarget_;
272 RealType aDenominator = 2.0 * K_a;
275 RealType bNumerator = deltaU + kineticTarget_;
276 RealType bDenominator = 2.0 * K_b;
279 RealType a2 = (aNumerator / aDenominator) + 1.0;
280 RealType b2 = (bNumerator / bDenominator) + 1.0;
282 if (a2 > 0.0 && b2 > 0.0) {
287 if ((a > 0.999) && (a < 1.001) && (b > 0.999) && (b < 1.001)) {
296 int selei2 {}, selej2 {};
301 vel = (sd2->
getVel() - v_a) * a + v_a;
313 vel = (sd2->
getVel() - v_b) * b + v_b;
323 currentSnap_->hasTranslationalKineticEnergy =
false;
324 currentSnap_->hasRotationalKineticEnergy =
false;
325 currentSnap_->hasKineticEnergy =
false;
326 currentSnap_->hasTotalEnergy =
false;
328 RealType deltaLambda = particleTarget_;
330 bool updateSelectedMolecule =
331 forceManager_->updateLambda(deltaLambda, deltaLambda_);
333 if (updateSelectedMolecule) selectMolecule();
335 successfulExchange =
true;
336 particleExchange_ += deltaLambda;
337 kineticExchange_ += kineticTarget_;
341 if (!forceManager_->getHasSelectedMolecule()) {
343 deltaLambda_ = particleTarget_;
345 failedLastTrial_ =
true;
349 if (!successfulExchange) {
359 failedLastTrial_ =
true;
363 void SPFMethod::selectMolecule() {
364 bool hasSelectedMolecule = (currentSnap_->getSPFData()->globalID == -1) ?
365 setSelectedMolecule() :
366 getSelectedMolecule();
368 if (hasSelectedMolecule) {
369 selectedMoleculeStr_ =
370 "select " + std::to_string(currentSnap_->getSPFData()->globalID);
372 selectedMoleculeEvaluator_.loadScriptString(selectedMoleculeStr_);
373 selectedMoleculeMan_.setSelectionSet(
374 selectedMoleculeEvaluator_.evaluate());
377 forceManager_->setHasSelectedMolecule(hasSelectedMolecule);
380 bool SPFMethod::getSelectedMolecule() {
381 std::shared_ptr<SPFData> spfData = currentSnap_->getSPFData();
385 selectedMolecule = info_->getMoleculeByGlobalIndex(spfData->globalID);
387 if (selectedMolecule) {
388 forceManager_->setSelectedMolecule(selectedMolecule);
394 bool SPFMethod::setSelectedMolecule() {
396 RealType targetSlabCenter {};
400 if (particleTarget_ > 0.0) {
401 sourceSman = commonA_;
402 targetSlabCenter = slabBCenter_;
404 sourceSman = commonB_;
405 targetSlabCenter = slabACenter_;
408 if (sourceSman.getMoleculeSelectionCount() == 0) {
409 forceManager_->setSelectedMolecule(
nullptr);
413 int whichSelectedID {-1};
416 std::shared_ptr<SPFData> spfData = currentSnap_->getSPFData();
417 Utils::RandNumGenPtr randNumGen = info_->getRandomNumberGenerator();
420 int worldRank {}, size {};
422 MPI_Comm_rank(MPI_COMM_WORLD, &worldRank);
423 MPI_Comm_size(MPI_COMM_WORLD, &size);
426 if (worldRank == 0) {
428 std::uniform_int_distribution<> selectedMoleculeDistribution {
429 0, sourceSman.getMoleculeSelectionCount() - 1};
431 whichSelectedID = selectedMoleculeDistribution(*randNumGen);
435 MPI_Bcast(&whichSelectedID, 1, MPI_INT, 0, MPI_COMM_WORLD);
438 selectedMolecule = sourceSman.nthSelectedMolecule(whichSelectedID);
440 int globalSelectedID {-1};
442 if (selectedMolecule) {
446 for (
int i {}; i < size; ++i) {
447 if (i != worldRank) {
448 MPI_Send(&globalSelectedID, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
452 MPI_Recv(&globalSelectedID, 1, MPI_INT, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD,
457 int axis0 = (rnemdPrivilegedAxis_ + 1) % 3;
458 int axis1 = (rnemdPrivilegedAxis_ + 2) % 3;
459 int axis2 = rnemdPrivilegedAxis_;
461 spfData->globalID = globalSelectedID;
464 if (info_->getMolToProc(globalSelectedID) == worldRank) {
466 std::uniform_real_distribution<RealType> distr0 {0, hmat_(axis0, axis0)};
467 std::uniform_real_distribution<RealType> distr1 {0, hmat_(axis1, axis1)};
468 std::normal_distribution<RealType> distr2 {targetSlabCenter,
471 spfData->pos[axis0] = distr0(*randNumGen);
472 spfData->pos[axis1] = distr1(*randNumGen);
473 spfData->pos[axis2] = distr2(*randNumGen);
475 forceManager_->setSelectedMolecule(selectedMolecule);
479 MPI_Bcast(&spfData->pos[0], 3, MPI_REALTYPE,
480 info_->getMolToProc(globalSelectedID), MPI_COMM_WORLD);
ForceManager is responsible for calculating both the short range (bonded) interactions and long range...
int getGlobalIndex()
Returns the global index of this molecule.
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...
"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.
void setVel(const Vector3d &vel)
Sets the current velocity of this stuntDouble.
Vector3d getJ()
Returns the current angular momentum of this stuntDouble (body -fixed).
bool isDirectional()
Tests if this stuntDouble is a directional one.
void setJ(const Vector3d &angMom)
Sets the current angular momentum of this stuntDouble (body-fixed).
Real lengthSquare()
Returns the squared length of this vector.