48#include "rnemd/VSS.hpp"
63#include "brains/Thermo.hpp"
64#include "io/Globals.hpp"
65#include "math/ConvexHull.hpp"
72#include "rnemd/RNEMD.hpp"
73#include "rnemd/RNEMDParameters.hpp"
74#include "types/FixedChargeAdapter.hpp"
75#include "types/FluctuatingChargeAdapter.hpp"
76#include "utils/Constants.hpp"
78namespace OpenMD::RNEMD {
80 VSSMethod::VSSMethod(SimInfo* info, ForceManager* forceMan) :
81 RNEMD {info, forceMan} {
82 rnemdMethodLabel_ =
"VSS";
84 RNEMDParameters* rnemdParams = info->getSimParams()->getRNEMDParameters();
86 bool hasKineticFlux = rnemdParams->haveKineticFlux();
87 bool hasMomentumFlux = rnemdParams->haveMomentumFlux();
88 bool hasMomentumFluxVector = rnemdParams->haveMomentumFluxVector();
89 bool hasAngularMomentumFlux = rnemdParams->haveAngularMomentumFlux();
90 bool hasAngularMomentumFluxVector =
91 rnemdParams->haveAngularMomentumFluxVector();
93 bool methodFluxMismatch =
false;
94 bool hasCorrectFlux =
false;
96 switch (rnemdFluxType_) {
100 hasCorrectFlux = hasKineticFlux;
105 hasCorrectFlux = hasMomentumFlux;
110 hasCorrectFlux = hasAngularMomentumFlux;
113 hasCorrectFlux = hasMomentumFluxVector;
116 hasCorrectFlux = hasAngularMomentumFluxVector;
120 hasCorrectFlux = hasMomentumFlux && hasKineticFlux;
125 hasCorrectFlux = hasAngularMomentumFlux && hasKineticFlux;
128 hasCorrectFlux = hasMomentumFluxVector && hasKineticFlux;
131 hasCorrectFlux = hasAngularMomentumFluxVector && hasKineticFlux;
134 methodFluxMismatch =
true;
138 if (methodFluxMismatch) {
139 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
140 "RNEMD: The current method,\n"
142 "\tcannot be used with the current flux type, %s\n",
143 rnemdFluxTypeLabel_.c_str());
144 painCave.isFatal = 1;
145 painCave.severity = OPENMD_ERROR;
149 if (!hasCorrectFlux) {
150 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
151 "RNEMD: The current method, VSS, and flux type, %s,\n"
152 "\tdid not have the correct flux value specified. Options\n"
153 "\tinclude: kineticFlux, momentumFlux, angularMomentumFlux,\n"
154 "\tmomentumFluxVector, and angularMomentumFluxVector.\n",
155 rnemdFluxTypeLabel_.c_str());
156 painCave.isFatal = 1;
157 painCave.severity = OPENMD_ERROR;
161 if (hasKineticFlux) {
162 setKineticFlux(rnemdParams->getKineticFlux());
167 if (hasMomentumFluxVector) {
168 setMomentumFluxVector(rnemdParams->getMomentumFluxVector());
170 std::vector<RealType> momentumFluxVector(3);
172 if (hasMomentumFlux) {
173 RealType momentumFlux = rnemdParams->getMomentumFlux();
175 switch (rnemdFluxType_) {
178 momentumFluxVector[0] = momentumFlux;
182 momentumFluxVector[1] = momentumFlux;
185 momentumFluxVector[2] = momentumFlux;
192 setMomentumFluxVector(momentumFluxVector);
195 if (hasAngularMomentumFluxVector) {
196 setAngularMomentumFluxVector(rnemdParams->getAngularMomentumFluxVector());
198 std::vector<RealType> angularMomentumFluxVector(3);
200 if (hasAngularMomentumFlux) {
201 RealType angularMomentumFlux = rnemdParams->getAngularMomentumFlux();
203 switch (rnemdFluxType_) {
206 angularMomentumFluxVector[0] = angularMomentumFlux;
210 angularMomentumFluxVector[1] = angularMomentumFlux;
214 angularMomentumFluxVector[2] = angularMomentumFlux;
220 setAngularMomentumFluxVector(angularMomentumFluxVector);
224 void VSSMethod::doRNEMDImpl(SelectionManager& smanA,
225 SelectionManager& smanB) {
226 if (!doRNEMD_)
return;
232 vector<StuntDouble*> hotBin, coldBin;
249 bool doLinearPart =
false;
250 bool doAngularPart =
false;
252 switch (rnemdFluxType_) {
270 doAngularPart =
true;
276 if (usePeriodicBoundaryConditions_)
279 doAngularPart =
true;
283 for (sd = smanA.beginSelected(selei); sd != NULL;
284 sd = smanA.nextSelected(selei)) {
285 Vector3d pos = sd->getPos();
288 if (usePeriodicBoundaryConditions_) currentSnap_->wrapVector(pos);
290 RealType mass = sd->getMass();
291 Vector3d vel = sd->getVel();
292 Vector3d rPos = sd->getPos() - coordinateOrigin_;
295 hotBin.push_back(sd);
298 Kh += mass * vel.lengthSquare();
299 Lh += mass *
cross(rPos, vel);
300 Ih -= outProduct(rPos, rPos) * mass;
301 r2 = rPos.lengthSquare();
302 Ih(0, 0) += mass * r2;
303 Ih(1, 1) += mass * r2;
304 Ih(2, 2) += mass * r2;
306 if (rnemdFluxType_ == rnemdFullKE) {
307 if (sd->isDirectional()) {
308 Vector3d angMom = sd->getJ();
309 Mat3x3d I = sd->getI();
310 if (sd->isLinear()) {
311 int i = sd->linearAxis();
314 Kh += angMom[j] * angMom[j] / I(j, j) +
315 angMom[k] * angMom[k] / I(k, k);
317 Kh += angMom[0] * angMom[0] / I(0, 0) +
318 angMom[1] * angMom[1] / I(1, 1) +
319 angMom[2] * angMom[2] / I(2, 2);
325 for (sd = smanB.beginSelected(selej); sd != NULL;
326 sd = smanB.nextSelected(selej)) {
327 Vector3d pos = sd->getPos();
330 if (usePeriodicBoundaryConditions_) currentSnap_->wrapVector(pos);
332 RealType mass = sd->getMass();
333 Vector3d vel = sd->getVel();
334 Vector3d rPos = sd->getPos() - coordinateOrigin_;
337 coldBin.push_back(sd);
340 Kc += mass * vel.lengthSquare();
341 Lc += mass *
cross(rPos, vel);
342 Ic -= outProduct(rPos, rPos) * mass;
343 r2 = rPos.lengthSquare();
344 Ic(0, 0) += mass * r2;
345 Ic(1, 1) += mass * r2;
346 Ic(2, 2) += mass * r2;
348 if (rnemdFluxType_ == rnemdFullKE) {
349 if (sd->isDirectional()) {
350 Vector3d angMom = sd->getJ();
351 Mat3x3d I = sd->getI();
352 if (sd->isLinear()) {
353 int i = sd->linearAxis();
356 Kc += angMom[j] * angMom[j] / I(j, j) +
357 angMom[k] * angMom[k] / I(k, k);
359 Kc += angMom[0] * angMom[0] / I(0, 0) +
360 angMom[1] * angMom[1] / I(1, 1) +
361 angMom[2] * angMom[2] / I(2, 2);
371 MPI_Allreduce(MPI_IN_PLACE, &Ph[0], 3, MPI_REALTYPE, MPI_SUM,
373 MPI_Allreduce(MPI_IN_PLACE, &Pc[0], 3, MPI_REALTYPE, MPI_SUM,
375 MPI_Allreduce(MPI_IN_PLACE, &Lh[0], 3, MPI_REALTYPE, MPI_SUM,
377 MPI_Allreduce(MPI_IN_PLACE, &Lc[0], 3, MPI_REALTYPE, MPI_SUM,
379 MPI_Allreduce(MPI_IN_PLACE, &Mh, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
380 MPI_Allreduce(MPI_IN_PLACE, &Kh, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
381 MPI_Allreduce(MPI_IN_PLACE, &Mc, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
382 MPI_Allreduce(MPI_IN_PLACE, &Kc, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
383 MPI_Allreduce(MPI_IN_PLACE, Ih.getArrayPointer(), 9, MPI_REALTYPE, MPI_SUM,
385 MPI_Allreduce(MPI_IN_PLACE, Ic.getArrayPointer(), 9, MPI_REALTYPE, MPI_SUM,
389 Vector3d ac, acrec, bc, bcrec;
390 Vector3d ah, ahrec, bh, bhrec;
392 bool successfulExchange =
false;
393 if ((Mh > 0.0) && (Mc > 0.0)) {
395 Vector3d vc = Pc / Mc;
396 ac = -momentumTarget_ / Mc + vc;
397 acrec = -momentumTarget_ / Mc;
402 Vector3d omegac = Ici * Lc;
403 bc = -(Ici * angularMomentumTarget_) + omegac;
406 RealType cNumerator = Kc - kineticTarget_;
407 if (doLinearPart) cNumerator -= 0.5 * Mc * ac.
lengthSquare();
409 if (doAngularPart) cNumerator -= 0.5 * (
dot(bc, Ic * bc));
411 RealType cDenominator = Kc;
413 if (doLinearPart) cDenominator -= 0.5 * Mc * vc.lengthSquare();
415 if (doAngularPart) cDenominator -= 0.5 * (
dot(omegac, Ic * omegac));
417 if (cNumerator / cDenominator > 0.0) {
418 RealType c = sqrt(cNumerator / cDenominator);
420 if ((c > 0.9) && (c < 1.1)) {
422 Vector3d vh = Ph / Mh;
423 ah = momentumTarget_ / Mh + vh;
424 ahrec = momentumTarget_ / Mh;
429 Vector3d omegah = Ihi * Lh;
430 bh = (Ihi * angularMomentumTarget_) + omegah;
433 RealType hNumerator = Kh + kineticTarget_;
434 if (doLinearPart) hNumerator -= 0.5 * Mh * ah.
lengthSquare();
436 if (doAngularPart) hNumerator -= 0.5 * (
dot(bh, Ih * bh));
438 RealType hDenominator = Kh;
439 if (doLinearPart) hDenominator -= 0.5 * Mh * vh.lengthSquare();
440 if (doAngularPart) hDenominator -= 0.5 * (
dot(omegah, Ih * omegah));
442 if (hNumerator / hDenominator > 0.0) {
443 RealType h = sqrt(hNumerator / hDenominator);
445 if ((h > 0.9) && (h < 1.1)) {
446 vector<StuntDouble*>::iterator sdi;
450 for (sdi = coldBin.begin(); sdi != coldBin.end(); ++sdi) {
451 if (doLinearPart) vel = ((*sdi)->getVel() - vc) * c + ac;
453 rPos = (*sdi)->getPos() - coordinateOrigin_;
454 vel = ((*sdi)->getVel() -
cross(omegac, rPos)) * c +
460 if (rnemdFluxType_ == rnemdFullKE) {
461 if ((*sdi)->isDirectional()) {
462 Vector3d angMom = (*sdi)->getJ() * c;
463 (*sdi)->setJ(angMom);
468 for (sdi = hotBin.begin(); sdi != hotBin.end(); ++sdi) {
469 if (doLinearPart) vel = ((*sdi)->getVel() - vh) * h + ah;
471 rPos = (*sdi)->getPos() - coordinateOrigin_;
472 vel = ((*sdi)->getVel() -
cross(omegah, rPos)) * h +
478 if (rnemdFluxType_ == rnemdFullKE) {
479 if ((*sdi)->isDirectional()) {
480 Vector3d angMom = (*sdi)->getJ() * h;
481 (*sdi)->setJ(angMom);
486 successfulExchange =
true;
487 kineticExchange_ += kineticTarget_;
488 momentumExchange_ += momentumTarget_;
489 angularMomentumExchange_ += angularMomentumTarget_;
496 if (successfulExchange !=
true) {
497 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
498 "VSS exchange NOT performed - roots that solve\n"
499 "\tthe constraint equations may not exist or there may be\n"
500 "\tno selected objects in one or both slabs.\n");
501 painCave.isFatal = 0;
502 painCave.severity = OPENMD_INFO;
SquareMatrix3< Real > inverse() const
Sets the value of this matrix to the inverse of itself.
Real lengthSquare() const
Returns the squared length of this vector.
Vector3< Real > cross(const Vector3< Real > &v1, const Vector3< Real > &v2)
Returns the cross product of two Vectors.
Real dot(const DynamicVector< Real > &v1, const DynamicVector< Real > &v2)
Returns the dot product of two DynamicVectors.