48#include "rnemd/NIVS.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"
78#define HONKING_LARGE_VALUE 1.0e10
80namespace OpenMD::RNEMD {
82 NIVSMethod::NIVSMethod(SimInfo* info, ForceManager* forceMan) :
83 RNEMD {info, forceMan} {
84 rnemdMethodLabel_ =
"NIVS";
86 RNEMDParameters* rnemdParams = info->getSimParams()->getRNEMDParameters();
88 bool hasKineticFlux = rnemdParams->haveKineticFlux();
89 bool hasMomentumFlux = rnemdParams->haveMomentumFlux();
91 bool methodFluxMismatch =
false;
92 bool hasCorrectFlux =
false;
94 switch (rnemdFluxType_) {
98 hasCorrectFlux = hasKineticFlux;
103 hasCorrectFlux = hasMomentumFlux;
107 hasCorrectFlux = hasMomentumFlux && hasKineticFlux;
110 methodFluxMismatch =
true;
114 if (methodFluxMismatch) {
115 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
116 "RNEMD: The current method,\n"
118 "\tcannot be used with the current flux type, %s\n",
119 rnemdFluxTypeLabel_.c_str());
120 painCave.isFatal = 1;
121 painCave.severity = OPENMD_ERROR;
125 if (!hasCorrectFlux) {
126 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
127 "RNEMD: The current method, NIVS, and flux type, %s,\n"
128 "\tdid not have the correct flux value specified. Options\n"
129 "\tinclude: kineticFlux and momentumFlux.\n",
130 rnemdFluxTypeLabel_.c_str());
131 painCave.isFatal = 1;
132 painCave.severity = OPENMD_ERROR;
136 if (hasKineticFlux) {
137 setKineticFlux(rnemdParams->getKineticFlux());
142 if (hasMomentumFlux) {
143 RealType momentumFlux = rnemdParams->getMomentumFlux();
144 std::vector<RealType> momentumFluxVector(3);
146 switch (rnemdFluxType_) {
148 momentumFluxVector[0] = momentumFlux;
151 momentumFluxVector[1] = momentumFlux;
154 momentumFluxVector[2] = momentumFlux;
160 setMomentumFluxVector(momentumFluxVector);
164 void NIVSMethod::doRNEMDImpl(SelectionManager& smanA,
165 SelectionManager& smanB) {
166 if (!doRNEMD_)
return;
172 std::vector<StuntDouble*> hotBin, coldBin;
189 for (sd = smanA.beginSelected(selei); sd != NULL;
190 sd = smanA.nextSelected(selei)) {
191 Vector3d pos = sd->getPos();
195 if (usePeriodicBoundaryConditions_) currentSnap_->wrapVector(pos);
197 RealType mass = sd->getMass();
198 Vector3d vel = sd->getVel();
200 hotBin.push_back(sd);
201 Phx += mass * vel.x();
202 Phy += mass * vel.y();
203 Phz += mass * vel.z();
204 Khx += mass * vel.x() * vel.x();
205 Khy += mass * vel.y() * vel.y();
206 Khz += mass * vel.z() * vel.z();
207 if (sd->isDirectional()) {
208 Vector3d angMom = sd->getJ();
209 Mat3x3d I = sd->getI();
210 if (sd->isLinear()) {
211 int i = sd->linearAxis();
215 angMom[j] * angMom[j] / I(j, j) + angMom[k] * angMom[k] / I(k, k);
217 Khw += angMom[0] * angMom[0] / I(0, 0) +
218 angMom[1] * angMom[1] / I(1, 1) +
219 angMom[2] * angMom[2] / I(2, 2);
223 for (sd = smanB.beginSelected(selej); sd != NULL;
224 sd = smanB.nextSelected(selej)) {
225 Vector3d pos = sd->getPos();
229 if (usePeriodicBoundaryConditions_) currentSnap_->wrapVector(pos);
231 RealType mass = sd->getMass();
232 Vector3d vel = sd->getVel();
234 coldBin.push_back(sd);
235 Pcx += mass * vel.x();
236 Pcy += mass * vel.y();
237 Pcz += mass * vel.z();
238 Kcx += mass * vel.x() * vel.x();
239 Kcy += mass * vel.y() * vel.y();
240 Kcz += mass * vel.z() * vel.z();
241 if (sd->isDirectional()) {
242 Vector3d angMom = sd->getJ();
243 Mat3x3d I = sd->getI();
244 if (sd->isLinear()) {
245 int i = sd->linearAxis();
249 angMom[j] * angMom[j] / I(j, j) + angMom[k] * angMom[k] / I(k, k);
251 Kcw += angMom[0] * angMom[0] / I(0, 0) +
252 angMom[1] * angMom[1] / I(1, 1) +
253 angMom[2] * angMom[2] / I(2, 2);
268 MPI_Allreduce(MPI_IN_PLACE, &Phx, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
269 MPI_Allreduce(MPI_IN_PLACE, &Phy, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
270 MPI_Allreduce(MPI_IN_PLACE, &Phz, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
271 MPI_Allreduce(MPI_IN_PLACE, &Pcx, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
272 MPI_Allreduce(MPI_IN_PLACE, &Pcy, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
273 MPI_Allreduce(MPI_IN_PLACE, &Pcz, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
275 MPI_Allreduce(MPI_IN_PLACE, &Khx, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
276 MPI_Allreduce(MPI_IN_PLACE, &Khy, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
277 MPI_Allreduce(MPI_IN_PLACE, &Khz, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
278 MPI_Allreduce(MPI_IN_PLACE, &Khw, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
280 MPI_Allreduce(MPI_IN_PLACE, &Kcx, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
281 MPI_Allreduce(MPI_IN_PLACE, &Kcy, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
282 MPI_Allreduce(MPI_IN_PLACE, &Kcz, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
283 MPI_Allreduce(MPI_IN_PLACE, &Kcw, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
287 RealType px = Pcx / Phx;
288 RealType py = Pcy / Phy;
289 RealType pz = Pcz / Phz;
290 RealType c(0.0), x(0.0), y(0.0), z(0.0);
291 bool successfulScale =
false;
292 if ((rnemdFluxType_ == rnemdFullKE) || (rnemdFluxType_ == rnemdRotKE)) {
295 if (rnemdFluxType_ == rnemdFullKE) {
296 c = 1.0 - kineticTarget_ / (Kcx + Kcy + Kcz + Kcw);
298 c = 1.0 - kineticTarget_ / Kcw;
301 if ((c > 0.81) && (c < 1.21)) {
305 if (rnemdFluxType_ == rnemdFullKE) {
306 x = 1.0 + px * (1.0 - c);
307 y = 1.0 + py * (1.0 - c);
308 z = 1.0 + pz * (1.0 - c);
317 if ((std::fabs(x - 1.0) < 0.1) && (std::fabs(y - 1.0) < 0.1) &&
318 (std::fabs(z - 1.0) < 0.1)) {
319 w = 1.0 + (kineticTarget_ + Khx * (1.0 - x * x) +
320 Khy * (1.0 - y * y) + Khz * (1.0 - z * z)) /
324 w = 1.0 + kineticTarget_ / Khw;
326 if ((w > 0.81) && (w < 1.21)) {
328 std::vector<StuntDouble*>::iterator sdi;
330 for (sdi = coldBin.begin(); sdi != coldBin.end(); ++sdi) {
331 if (rnemdFluxType_ == rnemdFullKE) {
332 vel = (*sdi)->getVel() * c;
335 if ((*sdi)->isDirectional()) {
336 Vector3d angMom = (*sdi)->getJ() * c;
337 (*sdi)->setJ(angMom);
341 for (sdi = hotBin.begin(); sdi != hotBin.end(); ++sdi) {
342 if (rnemdFluxType_ == rnemdFullKE) {
343 vel = (*sdi)->getVel();
349 if ((*sdi)->isDirectional()) {
350 Vector3d angMom = (*sdi)->getJ() * w;
351 (*sdi)->setJ(angMom);
354 successfulScale =
true;
355 kineticExchange_ += kineticTarget_;
359 RealType a000(0.0), a110(0.0), c0(0.0);
360 RealType a001(0.0), a111(0.0), b01(0.0), b11(0.0), c1(0.0);
361 switch (rnemdFluxType_) {
378 c0 = kineticTarget_ - Kcx - Kcy - Kcz;
379 a001 = Khx * px * px + Khy * py * py;
380 a111 = Khz * pz * pz;
381 b01 = -2.0 * (Khx * px * (1.0 + px) + Khy * py * (1.0 + py));
382 b11 = -2.0 * Khz * pz * (1.0 + pz);
383 c1 = Khx * px * (2.0 + px) + Khy * py * (2.0 + py) +
384 Khz * pz * (2.0 + pz) - kineticTarget_;
387 c = 1 - momentumTarget_.x() / Pcx;
390 c0 = Kcx * c * c - Kcx - Kcy - Kcz;
391 a001 = py * py * Khy;
392 a111 = pz * pz * Khz;
393 b01 = -2.0 * Khy * py * (1.0 + py);
394 b11 = -2.0 * Khz * pz * (1.0 + pz);
395 c1 = Khy * py * (2.0 + py) + Khz * pz * (2.0 + pz) +
396 Khx * (fastpow(c * px - px - 1.0, 2) - 1.0);
399 c = 1 - momentumTarget_.y() / Pcy;
402 c0 = Kcy * c * c - Kcx - Kcy - Kcz;
403 a001 = px * px * Khx;
404 a111 = pz * pz * Khz;
405 b01 = -2.0 * Khx * px * (1.0 + px);
406 b11 = -2.0 * Khz * pz * (1.0 + pz);
407 c1 = Khx * px * (2.0 + px) + Khz * pz * (2.0 + pz) +
408 Khy * (fastpow(c * py - py - 1.0, 2) - 1.0);
411 c = 1 - momentumTarget_.z() / Pcz;
414 c0 = Kcz * c * c - Kcx - Kcy - Kcz;
415 a001 = px * px * Khx;
416 a111 = py * py * Khy;
417 b01 = -2.0 * Khx * px * (1.0 + px);
418 b11 = -2.0 * Khy * py * (1.0 + py);
419 c1 = Khx * px * (2.0 + px) + Khy * py * (2.0 + py) +
420 Khz * (fastpow(c * pz - pz - 1.0, 2) - 1.0);
426 RealType v1 = a000 * a111 - a001 * a110;
427 RealType v2 = a000 * b01;
428 RealType v3 = a000 * b11;
429 RealType v4 = a000 * c1 - a001 * c0;
430 RealType v8 = a110 * b01;
431 RealType v10 = -b01 * c0;
433 RealType u0 = v2 * v10 - v4 * v4;
434 RealType u1 = -2.0 * v3 * v4;
435 RealType u2 = -v2 * v8 - v3 * v3 - 2.0 * v1 * v4;
436 RealType u3 = -2.0 * v1 * v3;
437 RealType u4 = -v1 * v1;
439 RealType maxAbs = fabs(u0);
440 if (maxAbs < fabs(u1)) maxAbs = fabs(u1);
441 if (maxAbs < fabs(u2)) maxAbs = fabs(u2);
442 if (maxAbs < fabs(u3)) maxAbs = fabs(u3);
443 if (maxAbs < fabs(u4)) maxAbs = fabs(u4);
450 Polynomial<RealType> poly;
451 poly.setCoefficient(4, u4);
452 poly.setCoefficient(3, u3);
453 poly.setCoefficient(2, u2);
454 poly.setCoefficient(1, u1);
455 poly.setCoefficient(0, u0);
456 vector<RealType> realRoots = poly.FindRealRoots();
458 vector<RealType>::iterator ri;
459 RealType r1, r2, alpha0;
460 vector<pair<RealType, RealType>> rps;
461 for (ri = realRoots.begin(); ri != realRoots.end(); ++ri) {
464 if (fabs(u0 + r2 * (u1 + r2 * (u2 + r2 * (u3 + r2 * u4)))) > 1e-6) {
465 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
466 "RNEMD Warning: polynomial solve seems to have an error!");
467 painCave.isFatal = 0;
472 alpha0 = -c0 - a110 * r2 * r2;
474 r1 = sqrt(alpha0 / a000);
475 if (fabs(c1 + r1 * (b01 + r1 * a001) + r2 * (b11 + r2 * a111)) <
477 rps.push_back(make_pair(r1, r2));
481 if (fabs(c1 + r1 * (b01 + r1 * a001) + r2 * (b11 + r2 * a111)) <
483 rps.push_back(make_pair(r1, r2));
492 RealType smallestDiff = HONKING_LARGE_VALUE;
494 std::pair<RealType, RealType> bestPair = std::make_pair(1.0, 1.0);
495 std::vector<std::pair<RealType, RealType>>::iterator rpi;
496 for (rpi = rps.begin(); rpi != rps.end(); ++rpi) {
499 switch (rnemdFluxType_) {
501 diff = fastpow(1.0 - r1, 2) + fastpow(1.0 - r2, 2) +
502 fastpow(r1 * r1 / r2 / r2 - Kcz / Kcx, 2) +
503 fastpow(r1 * r1 / r2 / r2 - Kcz / Kcy, 2);
506 diff = fastpow(1.0 - r1, 2) + fastpow(1.0 - r2, 2) +
507 fastpow(r1 * r1 / r2 / r2 - Kcz / Kcy, 2);
510 diff = fastpow(1.0 - r1, 2) + fastpow(1.0 - r2, 2) +
511 fastpow(r1 * r1 / r2 / r2 - Kcz / Kcx, 2);
514 diff = fastpow(1.0 - r1, 2) + fastpow(1.0 - r2, 2) +
515 fastpow(r1 * r1 / r2 / r2 - Kcy / Kcx, 2);
519 if (diff < smallestDiff) {
525 if (worldRank == 0) {
537 switch (rnemdFluxType_) {
561 vector<StuntDouble*>::iterator sdi;
563 for (sdi = coldBin.begin(); sdi != coldBin.end(); ++sdi) {
564 vel = (*sdi)->getVel();
571 x = 1.0 + px * (1.0 - x);
572 y = 1.0 + py * (1.0 - y);
573 z = 1.0 + pz * (1.0 - z);
574 for (sdi = hotBin.begin(); sdi != hotBin.end(); ++sdi) {
575 vel = (*sdi)->getVel();
581 successfulScale =
true;
582 switch (rnemdFluxType_) {
584 kineticExchange_ += kineticTarget_;
589 momentumExchange_ += momentumTarget_;
596 if (successfulScale !=
true) {
597 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
598 "NIVS exchange NOT performed - roots that solve\n"
599 "\tthe constraint equations may not exist or there may be\n"
600 "\tno selected objects in one or both slabs.\n");
601 painCave.isFatal = 0;
602 painCave.severity = OPENMD_INFO;