OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
SPFForceManager.cpp
1/*
2 * Copyright (c) 2004-present, The University of Notre Dame. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
32 * research, please cite the following paper when you publish your work:
33 *
34 * [1] Drisko et al., J. Open Source Softw. 9, 7004 (2024).
35 *
36 * Good starting points for code and simulation methodology are:
37 *
38 * [2] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
39 * [3] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
40 * [4] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
41 * [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
42 * [6] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
43 * [7] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
44 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
45 * [9] Drisko & Gezelter, J. Chem. Theory Comput. 20, 4986-4997 (2024).
46 */
47
48#include "rnemd/SPFForceManager.hpp"
49
50#include <config.h>
51
52#include <cmath>
53#include <vector>
54
55#ifdef IS_MPI
56#include <mpi.h>
57#endif
58
60#include "brains/SimInfo.hpp"
61#include "brains/Snapshot.hpp"
62#include "brains/Thermo.hpp"
64#include "math/Vector3.hpp"
65#include "nonbonded/NonBondedInteraction.hpp"
68#include "rnemd/RNEMDParameters.hpp"
69#include "rnemd/SPF.hpp"
70#include "utils/CI_String.hpp"
71
72namespace OpenMD::RNEMD {
73
74 SPFForceManager::SPFForceManager(SimInfo* info) :
75 ForceManager {info}, potentialSource_ {}, potentialSink_ {} {
76 thermo_ = std::make_unique<Thermo>(info);
77 currentSnapshot_ = info_->getSnapshotManager()->getCurrentSnapshot();
78
79 k_ = info_->getSimParams()->getRNEMDParameters()->getSPFScalingPower();
80
81 int nAtoms = info_->getNAtoms();
82 int nRigidBodies = info_->getNRigidBodies();
83 int nCutoffGroups = info_->getNCutoffGroups();
84
85 int atomStorageLayout = info_->getSnapshotManager()->getAtomStorageLayout();
86 int rbStorageLayout =
87 info_->getSnapshotManager()->getRigidBodyStorageLayout();
88 int cgStorageLayout =
89 info_->getSnapshotManager()->getCutoffGroupStorageLayout();
90
91 bool usePBC = info_->getSimParams()->getUsePeriodicBoundaryConditions();
92
93 temporarySourceSnapshot_ =
94 new Snapshot(nAtoms, nRigidBodies, nCutoffGroups, atomStorageLayout,
95 rbStorageLayout, cgStorageLayout, usePBC);
96
97 temporarySinkSnapshot_ =
98 new Snapshot(nAtoms, nRigidBodies, nCutoffGroups, atomStorageLayout,
99 rbStorageLayout, cgStorageLayout, usePBC);
100 }
101
102 SPFForceManager::~SPFForceManager() {
103 delete temporarySourceSnapshot_;
104 delete temporarySinkSnapshot_;
105 }
106
107 void SPFForceManager::calcForces() {
108 std::shared_ptr<SPFData> currentSPFData = currentSnapshot_->getSPFData();
109
110 // Synced across processors
111 setDeltaLambda(spfRNEMD_->spfTarget_);
112
113 currentSPFData->lambda += deltaLambda_;
114
115 Vector3d v_a {};
116 Vector3d v_b {};
117 RealType a {};
118 RealType b {};
119
120 spfRNEMD_->isValidExchange(v_a, v_b, a, b);
121
122 // Current snapshot with selected molecule in source slab
123 ForceManager::calcForces();
124 potentialSource_ = currentSnapshot_->getPotentialEnergy();
125
126 if (hasSelectedMolecule_) {
127 Vector3d prevSourceCom {}, currentSourceCom {}, delta {};
128
129 // Only the processor with the selected molecule should do this step:
130 if (selectedMolecule_) {
131 prevSourceCom = selectedMolecule_->getPrevCom();
132 currentSourceCom = selectedMolecule_->getCom();
133
134 delta = currentSourceCom - prevSourceCom;
135 }
136
137 if (temporarySourceSnapshot_ && currentSnapshot_) {
138 *temporarySourceSnapshot_ = *currentSnapshot_;
139
140 // Save source Verlet neighbor list information:
141 sourceNeighborList_ = neighborList_;
142 sourcePoint_ = point_;
143 sourceSavedPositions_ = savedPositions_;
144 // Use sink Verlet neighbor list information:
145 neighborList_ = sinkNeighborList_;
146 point_ = sinkPoint_;
147 savedPositions_ = sinkSavedPositions_;
148
149 currentSnapshot_->clearDerivedProperties();
150 } else {
151 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
152 "Either temporarySourceSnapshot or currentSnapshot "
153 "has a null value.\n");
154 painCave.isFatal = 1;
155 painCave.severity = OPENMD_ERROR;
156 simError();
157 }
158
159 // Only the processor with the selected molecule should do this step:
160 if (selectedMolecule_) {
161 currentSPFData->pos += delta;
162 selectedMolecule_->setCom(currentSPFData->pos);
163 }
164
165#ifdef IS_MPI
166 int globalSelectedID = currentSPFData->globalID;
167
168 MPI_Bcast(&currentSPFData->pos[0], 3, MPI_REALTYPE,
169 info_->getMolToProc(globalSelectedID), MPI_COMM_WORLD);
170#endif
171
172 // Current snapshot with selected molecule in sink slab
173 ForceManager::calcForces();
174 potentialSink_ = currentSnapshot_->getPotentialEnergy();
175
176 if (temporarySinkSnapshot_ && currentSnapshot_) {
177 *temporarySinkSnapshot_ = *currentSnapshot_;
178
179 // Save sink Verlet neighbor list information:
180 sinkNeighborList_ = neighborList_;
181 sinkPoint_ = point_;
182 sinkSavedPositions_ = savedPositions_;
183 // Use source Verlet neighbor list information:
184 neighborList_ = sourceNeighborList_;
185 point_ = sourcePoint_;
186 savedPositions_ = sourceSavedPositions_;
187
188 currentSnapshot_->clearDerivedProperties();
189 } else {
190 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
191 "Either temporarySinkSnapshot or currentSnapshot "
192 "has a null value.\n");
193 painCave.isFatal = 1;
194 painCave.severity = OPENMD_ERROR;
195 simError();
196 }
197
198 // Only the processor with the selected molecule should do this step:
199 if (selectedMolecule_) { selectedMolecule_->setCom(currentSourceCom); }
200
201 bool doExchange {};
202
203 // restrict scaling coefficients
204 if ((a > 0.999) && (a < 1.001) && (b > 0.999) && (b < 1.001)) {
205 doExchange = true;
206 } else {
207 // roll back lambda
208 currentSPFData->lambda -= deltaLambda_;
209 deltaLambda_ = 0.0;
210 }
211
212 spfRNEMD_->failedLastTrial_ = !doExchange;
213
214 updateSPFState();
215 } else {
216 *temporarySourceSnapshot_ = *currentSnapshot_;
217 *temporarySinkSnapshot_ = *currentSnapshot_;
218 potentialSink_ = std::numeric_limits<RealType>::max();
219 }
220 }
221
222 void SPFForceManager::setSelectedMolecule(Molecule* selectedMolecule) {
223 if (selectedMolecule) {
224 selectedMolecule_ = selectedMolecule;
225 } else {
226 selectedMolecule_ = nullptr;
227 }
228 }
229
230 void SPFForceManager::setDeltaLambda(RealType spfTarget) {
231 if (hasSelectedMolecule_) {
232 std::shared_ptr<SPFData> currentSPFData = currentSnapshot_->getSPFData();
233
234 // Check to see if we are already fully in sink region
235 if (currentSPFData->lambda >= 1.0 ||
236 std::fabs(currentSPFData->lambda - 1.0) < 1e-6) {
237 // Only the processor with the selected molecule should do this step:
238 if (selectedMolecule_) {
239 selectedMolecule_->setCom(currentSPFData->pos);
240 selectedMolecule_ = nullptr;
241 }
242
243 currentSPFData->clear();
244 deltaLambda_ = 0.0;
245
246 neighborList_ = sinkNeighborList_;
247 point_ = sinkPoint_;
248 savedPositions_ = sinkSavedPositions_;
249
250 hasSelectedMolecule_ = false;
251
252 spfRNEMD_->selectMolecule();
253
254 return;
255 }
256
257 // survived the return true, so lambda is not >= 1:
258 // currentSPFTarget is an ion flux target, so can have sign:
259 deltaLambda_ = std::fabs(spfTarget);
260
261 if (currentSPFData->lambda + deltaLambda_ >= 1.0) {
262 /*
263 * New deltaLambda should be determined such that:
264 * f_lambda(lambda + deltaLambda) = 1
265 */
266 deltaLambda_ = 1.0 - currentSPFData->lambda;
267 }
268 }
269 }
270
271 RealType SPFForceManager::getScaledDeltaU() {
272 std::shared_ptr<SPFData> currentSPFData = currentSnapshot_->getSPFData();
273
274 RealType lambda = currentSPFData->lambda;
275
276 // Some checking against unreasonable potentials
277 if (std::isinf(potentialSink_) || std::isnan(potentialSink_) ||
278 std::isinf(potentialSource_) || std::isnan(potentialSource_)) {
279 snprintf(
280 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
281 "SPFForceManager detected a numerical error in the potential\n"
282 "\tenergy with a lambda value of %f. Selecting a new molecule.\n",
283 lambda);
284 painCave.isFatal = 0;
285 painCave.severity = OPENMD_WARNING;
286 simError();
287
288 hasSelectedMolecule_ = false;
289 currentSPFData->clear();
290 }
291
292 if (lambda < 1e-6) { return 0.0; }
293
294 return -(f_lambda(lambda) - f_lambda(lambda - deltaLambda_)) *
295 (potentialSink_ - potentialSource_);
296 }
297
298 void SPFForceManager::combineForcesAndTorques() {
299 // Calculate lambda-averaged forces on all atoms and potentials:
300 SimInfo::MoleculeIterator mi;
301 Molecule* mol;
302 Molecule::IntegrableObjectIterator ii;
303 StuntDouble* sd;
304
305 RealType result = f_lambda(currentSnapshot_->getSPFData()->lambda);
306
307 // Now scale forces and torques of all the sds
308 for (mol = info_->beginMolecule(mi); mol != NULL;
309 mol = info_->nextMolecule(mi)) {
310 for (sd = mol->beginIntegrableObject(ii); sd != NULL;
311 sd = mol->nextIntegrableObject(ii)) {
312 sd->combineForcesAndTorques(temporarySourceSnapshot_,
313 temporarySinkSnapshot_, 1.0 - result,
314 result);
315 }
316 }
317 }
318
319 void SPFForceManager::updatePotentials() {
320 updateLongRangePotentials();
321 updateShortRangePotentials();
322 updateSelfPotentials();
323 updateExcludedPotentials();
324 updateRestraintPotentials();
325 if (doPotentialSelection_) updateSelectionPotentials();
326 }
327
328 void SPFForceManager::updateLongRangePotentials() {
329 potVec longRangePotentials =
330 linearCombination(temporarySourceSnapshot_->getLongRangePotentials(),
331 temporarySinkSnapshot_->getLongRangePotentials());
332 currentSnapshot_->setLongRangePotentials(longRangePotentials);
333
334 RealType reciprocalPotential =
335 linearCombination(temporarySourceSnapshot_->getReciprocalPotential(),
336 temporarySinkSnapshot_->getReciprocalPotential());
337 currentSnapshot_->setReciprocalPotential(reciprocalPotential);
338
339 RealType surfacePotential =
340 linearCombination(temporarySourceSnapshot_->getSurfacePotential(),
341 temporarySinkSnapshot_->getSurfacePotential());
342 currentSnapshot_->setSurfacePotential(surfacePotential);
343 }
344
345 void SPFForceManager::updateShortRangePotentials() {
346 RealType bondPotential =
347 linearCombination(temporarySourceSnapshot_->getBondPotential(),
348 temporarySinkSnapshot_->getBondPotential());
349 currentSnapshot_->setBondPotential(bondPotential);
350
351 RealType bendPotential =
352 linearCombination(temporarySourceSnapshot_->getBendPotential(),
353 temporarySinkSnapshot_->getBendPotential());
354 currentSnapshot_->setBendPotential(bendPotential);
355
356 RealType torsionPotential =
357 linearCombination(temporarySourceSnapshot_->getTorsionPotential(),
358 temporarySinkSnapshot_->getTorsionPotential());
359 currentSnapshot_->setTorsionPotential(torsionPotential);
360
361 RealType inversionPotential =
362 linearCombination(temporarySourceSnapshot_->getInversionPotential(),
363 temporarySinkSnapshot_->getInversionPotential());
364 currentSnapshot_->setInversionPotential(inversionPotential);
365 }
366
367 void SPFForceManager::updateSelfPotentials() {
368 potVec selfPotentials =
369 linearCombination(temporarySourceSnapshot_->getSelfPotentials(),
370 temporarySinkSnapshot_->getSelfPotentials());
371 currentSnapshot_->setSelfPotentials(selfPotentials);
372 }
373
374 void SPFForceManager::updateExcludedPotentials() {
375 potVec excludedPotentials =
376 linearCombination(temporarySourceSnapshot_->getExcludedPotentials(),
377 temporarySinkSnapshot_->getExcludedPotentials());
378 currentSnapshot_->setExcludedPotentials(excludedPotentials);
379 }
380
381 void SPFForceManager::updateRestraintPotentials() {
382 RealType restraintPotential =
383 linearCombination(temporarySourceSnapshot_->getRestraintPotential(),
384 temporarySinkSnapshot_->getRestraintPotential());
385 currentSnapshot_->setRestraintPotential(restraintPotential);
386 }
387
388 void SPFForceManager::updateSelectionPotentials() {
389 potVec selectionPotentials =
390 linearCombination(temporarySourceSnapshot_->getSelectionPotentials(),
391 temporarySinkSnapshot_->getSelectionPotentials());
392 currentSnapshot_->setSelectionPotentials(selectionPotentials);
393 }
394
395 void SPFForceManager::updateVirialTensor() {
396 Mat3x3d virialTensor =
397 linearCombination(temporarySourceSnapshot_->getVirialTensor(),
398 temporarySinkSnapshot_->getVirialTensor());
399 currentSnapshot_->setVirialTensor(virialTensor);
400
401 Mat3x3d pressureTensor =
402 linearCombination(thermo_->getPressureTensor(temporarySourceSnapshot_),
403 thermo_->getPressureTensor(temporarySinkSnapshot_));
404 currentSnapshot_->setPressureTensor(pressureTensor);
405
406 RealType pressure =
407 linearCombination(thermo_->getPressure(temporarySourceSnapshot_),
408 thermo_->getPressure(temporarySinkSnapshot_));
409 currentSnapshot_->setPressure(pressure);
410 }
411} // namespace OpenMD::RNEMD