42#include "types/GayBerneAdapter.hpp"
43#include "types/LennardJonesAdapter.hpp"
44#include "utils/Constants.hpp"
49 LHDForceModifier::LHDForceModifier(
SimInfo* info) :
50 ForceModifier {info}, maxIterNum_ {6}, forceTolerance_ {1e-6},
51 simParams_ {info->getSimParams()},
52 randNumGen_ {info->getRandomNumberGenerator()} {
53 dt_ = simParams_->getDt();
56 if (!simParams_->haveTargetTemp()) {
57 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
58 "LHDForceModifier: a targetTemp is required.\n");
62 if (!simParams_->haveViscosity()) {
63 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
64 "LHDForceModifier: a viscosity is required.\n");
73 viscosity_ = Constants::viscoConvert * simParams_->getViscosity();
74 kT_ = Constants::kb * simParams_->getTargetTemp();
76 velField_ = std::make_unique<VelocityField>(info);
77 veloMunge_ = std::make_unique<Velocitizer>(info_);
81 SimInfo::MoleculeIterator mi;
82 Molecule::IntegrableObjectIterator ii;
83 for (Molecule* mol = info_->beginMolecule(mi); mol != NULL;
84 mol = info_->nextMolecule(mi)) {
86 std::vector<RealType> radii;
87 for (StuntDouble* sd = mol->beginIntegrableObject(ii); sd != NULL;
88 sd = mol->nextIntegrableObject(ii)) {
90 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
91 "LHDForceModifier: only spherical atoms are supported as\n"
92 "\thydrodynamic beads (found a non-atom integrable object).\n");
96 hm.beads.push_back(sd);
97 hm.masses.push_back(sd->getMass());
98 radii.push_back(beadRadius(static_cast<Atom*>(sd)));
100 if (!hm.beads.empty()) {
101 hm.mobility = std::make_unique<RPYMobility>(radii, viscosity_);
102 molecules_.push_back(std::move(hm));
107 RealType LHDForceModifier::beadRadius(Atom* atom)
const {
108 AtomType* atomType = atom->getAtomType();
110 GayBerneAdapter gba = GayBerneAdapter(atomType);
111 if (gba.isGayBerne()) {
112 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
113 "LHDForceModifier: Gay-Berne (non-spherical) atoms are not\n"
114 "\tsupported by the translation-only bead model.\n");
115 painCave.isFatal = 1;
119 LennardJonesAdapter lja = LennardJonesAdapter(atomType);
120 if (lja.isLennardJones())
return lja.getSigma() / 2.0;
122 std::vector<AtomType*> atChain = atomType->allYourBase();
123 for (std::vector<AtomType*>::iterator i = atChain.begin();
124 i != atChain.end(); ++i) {
126 if (aNum != 0)
return etab.
GetVdwRad(aNum);
129 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
130 "LHDForceModifier: could not determine a hydrodynamic radius for\n"
132 atomType->getName().c_str());
133 painCave.isFatal = 1;
138 void LHDForceModifier::modifyForces() {
139 const RealType eConv = Constants::energyConvert;
140 bool useFlow = velField_->isActive();
141 Mat3x3d E = useFlow ? velField_->getRateOfStrain() : Mat3x3d(0.0);
143 std::size_t molIndex = 0;
144 for (HydroMolecule& hm : molecules_) {
145 std::size_t N = hm.beads.size();
146 RPYMobility& mob = *hm.mobility;
149 std::vector<Vector3d> pos(N), vel(N), ambient(N, V3Zero);
150 for (std::size_t i = 0; i < N; ++i) {
151 pos[i] = hm.beads[i]->getPos();
152 vel[i] = hm.beads[i]->getVel();
153 if (useFlow) ambient[i] = velField_->getVelocity(pos[i]);
162 if (!mob.update(pos)) {
163 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
164 "LHDForceModifier: the resistance tensor for molecule %lu\n"
165 "\t(%lu beads) is not positive definite. The RPYC mobility is\n"
166 "\tSPD by construction, so this indicates invalid coordinates\n"
167 "\t(NaN or overflow) or unphysical bead radii / viscosity.\n",
168 static_cast<unsigned long>(molIndex),
169 static_cast<unsigned long>(N));
170 painCave.severity = OPENMD_ERROR;
171 painCave.isFatal = 1;
177 std::vector<Vector3d> vEff =
178 useFlow ? mob.effectiveAmbient(pos, ambient, E) : ambient;
181 std::vector<RealType> Z(3 * N);
182 for (std::size_t k = 0; k < 3 * N; ++k) Z[k] = normal_(*randNumGen_);
183 std::vector<Vector3d> Frand = mob.randomForce(Z, kT_, dt_);
184 for (std::size_t i = 0; i < N; ++i) hm.beads[i]->addFrc(Frand[i]);
189 std::vector<Vector3d> frc(N), velStep(N), Ffric(N, V3Zero), oldF(N);
190 for (std::size_t i = 0; i < N; ++i) {
191 frc[i] = hm.beads[i]->getFrc();
192 velStep[i] = vel[i] + (dt2_ / hm.masses[i] * eConv) * frc[i];
195 for (
int k = 0; k < maxIterNum_; ++k) {
198 Ffric = mob.dragForce(vEff, velStep);
199 for (std::size_t i = 0; i < N; ++i)
201 vel[i] + (dt2_ / hm.masses[i] * eConv) * (frc[i] + Ffric[i]);
205 RealType worst = 0.0;
206 for (std::size_t i = 0; i < N; ++i) {
207 RealType f2 = Ffric[i].lengthSquare();
208 if (f2 < 1.0e-12)
continue;
209 RealType fdot =
dot(Ffric[i], oldF[i]) / f2;
210 worst = std::max(worst, std::fabs(1.0 - fdot));
212 if (worst <= forceTolerance_)
break;
215 for (std::size_t i = 0; i < N; ++i) hm.beads[i]->addFrc(Ffric[i]);
221 if (simParams_->getConserveLinearMomentum()) veloMunge_->removeComDrift();
222 if (!simParams_->getUsePeriodicBoundaryConditions() &&
223 simParams_->getConserveAngularMomentum())
224 veloMunge_->removeAngularDrift();
This basic Periodic Table class was originally taken from the data.h file in OpenBabel.
Langevin force modifier with intramolecular RPY hydrodynamic interactions for flexible bead molecules...
RealType GetVdwRad(int atomicnum)
int GetAtomicNum(const char *str)
Abstract class for external ForceModifier classes.
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
Real dot(const DynamicVector< Real > &v1, const DynamicVector< Real > &v2)
Returns the dot product of two DynamicVectors.