OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
RestraintForceModifier.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 "restraints/RestraintForceModifier.hpp"
49
50#include <config.h>
51
52#include <cmath>
53#include <memory>
54
55#ifdef IS_MPI
56#include <mpi.h>
57#endif
58
59#include "brains/ForceModifier.hpp"
60#include "io/RestReader.hpp"
61#include "restraints/MolecularRestraint.hpp"
62#include "restraints/ObjectRestraint.hpp"
63#include "selection/SelectionEvaluator.hpp"
64#include "selection/SelectionManager.hpp"
65#include "utils/Constants.hpp"
66#include "utils/MemoryUtils.hpp"
67#include "utils/StringUtils.hpp"
68#include "utils/simError.h"
69
70namespace OpenMD {
71
72 RestraintForceModifier::RestraintForceModifier(SimInfo* info) :
73 ForceModifier {info} {
74 // order of affairs:
75 //
76 // 1) create restraints from the restraintStamps found in the MD
77 // file.
78 //
79 // 2) Create RestraintReader to parse the input files for the ideal
80 // structures. This reader will set reference structures, and will
81 // calculate molecular centers of mass, etc.
82 //
83 // 3) sit around and wait for calcForces to be called. When it comes,
84 // call the normal force manager calcForces, then loop through the
85 // restrained objects and do their restraint forces.
86
87 Globals* simParam = info_->getSimParams();
88 currSnapshot_ = info_->getSnapshotManager()->getCurrentSnapshot();
89
90 currRestTime_ = currSnapshot_->getTime();
91
92 if (simParam->haveStatusTime()) {
93 restTime_ = simParam->getStatusTime();
94 } else {
95 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
96 "Restraint warning: If you use restraints without setting\n"
97 "\tstatusTime, no restraint data will be written to the rest\n"
98 "\tfile.\n");
99 painCave.isFatal = 0;
100 simError();
101 restTime_ = simParam->getRunTime();
102 }
103
104 int nRestraintStamps = simParam->getNRestraintStamps();
105 std::vector<RestraintStamp*> stamp = simParam->getRestraintStamps();
106
107 std::vector<int> stuntDoubleIndex;
108
109 for (int i = 0; i < nRestraintStamps; i++) {
110 std::string myType = toUpperCopy(stamp[i]->getType());
111
112 if (myType.compare("MOLECULAR") == 0) {
113 int molIndex(-1);
114 Vector3d refCom;
115
116 if (!stamp[i]->haveMolIndex()) {
117 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
118 "Restraint Error: A molecular restraint was specified\n"
119 "\twithout providing a value for molIndex.\n");
120 painCave.isFatal = 1;
121 simError();
122 } else {
123 molIndex = stamp[i]->getMolIndex();
124 }
125
126 if (molIndex < 0) {
127 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
128 "Restraint Error: A molecular restraint was specified\n"
129 "\twith a molIndex that was less than 0\n");
130 painCave.isFatal = 1;
131 simError();
132 }
133 if (molIndex >= info_->getNGlobalMolecules()) {
134 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
135 "Restraint Error: A molecular restraint was specified with\n"
136 "\ta molIndex that was greater than the total number of "
137 "molecules\n");
138 painCave.isFatal = 1;
139 simError();
140 }
141
142 Molecule* mol = info_->getMoleculeByGlobalIndex(molIndex);
143
144 if (mol == NULL) {
145#ifdef IS_MPI
146 // getMoleculeByGlobalIndex returns a NULL in parallel if
147 // this proc doesn't have the molecule. Do a quick check to
148 // make sure another processor is supposed to have it.
149
150 int myrank;
151 MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
152
153 if (info_->getMolToProc(molIndex) == myrank) {
154 // If we were supposed to have it but got a null, then freak out.
155#endif
156
157 snprintf(
158 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
159 "Restraint Error: A molecular restraint was specified, but\n"
160 "\tno molecule was found with global index %d.\n",
161 molIndex);
162 painCave.isFatal = 1;
163 simError();
164
165#ifdef IS_MPI
166 }
167#endif
168 }
169
170#ifdef IS_MPI
171 // only handle this molecular restraint if this processor owns the
172 // molecule
173 int myrank;
174 MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
175 if (info_->getMolToProc(molIndex) == myrank) {
176#endif
177
178 MolecularRestraint* rest = new MolecularRestraint();
179
180 std::string restPre("mol_");
181 std::stringstream restName;
182 restName << restPre << molIndex;
183 rest->setRestraintName(restName.str());
184
185 if (stamp[i]->haveDisplacementSpringConstant()) {
186 rest->setDisplacementForceConstant(
187 stamp[i]->getDisplacementSpringConstant());
188 }
189 if (stamp[i]->haveDisplacementXSpringConstant()) {
190 rest->setDisplacementXForceConstant(
191 stamp[i]->getDisplacementXSpringConstant());
192 }
193 if (stamp[i]->haveDisplacementYSpringConstant()) {
194 rest->setDisplacementYForceConstant(
195 stamp[i]->getDisplacementYSpringConstant());
196 }
197 if (stamp[i]->haveDisplacementZSpringConstant()) {
198 rest->setDisplacementZForceConstant(
199 stamp[i]->getDisplacementZSpringConstant());
200 }
201 if (stamp[i]->haveAbsoluteSpringConstant()) {
202 rest->setAbsoluteForceConstant(
203 stamp[i]->getAbsoluteSpringConstant());
204 }
205 if (stamp[i]->haveTwistSpringConstant()) {
206 rest->setTwistForceConstant(stamp[i]->getTwistSpringConstant());
207 }
208 if (stamp[i]->haveSwingXSpringConstant()) {
209 rest->setSwingXForceConstant(stamp[i]->getSwingXSpringConstant());
210 }
211 if (stamp[i]->haveSwingYSpringConstant()) {
212 rest->setSwingYForceConstant(stamp[i]->getSwingYSpringConstant());
213 }
214 if (stamp[i]->haveAbsolutePositionZ()) {
215 rest->setAbsolutePositionZ(stamp[i]->getAbsolutePositionZ());
216 }
217 if (stamp[i]->haveRestrainedTwistAngle()) {
218 rest->setRestrainedTwistAngle(stamp[i]->getRestrainedTwistAngle() *
219 Constants::PI / 180.0);
220 }
221 if (stamp[i]->haveRestrainedSwingYAngle()) {
222 rest->setRestrainedSwingYAngle(
223 stamp[i]->getRestrainedSwingYAngle() * Constants::PI / 180.0);
224 }
225 if (stamp[i]->haveRestrainedSwingXAngle()) {
226 rest->setRestrainedSwingXAngle(
227 stamp[i]->getRestrainedSwingXAngle() * Constants::PI / 180.0);
228 }
229 if (stamp[i]->havePrint()) {
230 rest->setPrintRestraint(stamp[i]->getPrint());
231 }
232
233 restraints_.push_back(rest);
234 mol->addProperty(std::make_shared<RestraintData>("Restraint", rest));
235 restrainedMols_.push_back(mol);
236#ifdef IS_MPI
237 }
238#endif
239 } else if (myType.compare("OBJECT") == 0) {
240 std::string objectSelection;
241
242 if (!stamp[i]->haveObjectSelection()) {
243 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
244 "Restraint Error: An object restraint was specified\n"
245 "\twithout providing a selection script in the\n"
246 "\tobjectSelection variable.\n");
247 painCave.isFatal = 1;
248 simError();
249 } else {
250 objectSelection = stamp[i]->getObjectSelection();
251 }
252
253 SelectionEvaluator evaluator(info);
254 SelectionManager seleMan(info);
255
256 evaluator.loadScriptString(objectSelection);
257 seleMan.setSelectionSet(evaluator.evaluate());
258 int selectionCount = seleMan.getSelectionCount();
259
260#ifdef IS_MPI
261 MPI_Allreduce(MPI_IN_PLACE, &selectionCount, 1, MPI_INT, MPI_SUM,
262 MPI_COMM_WORLD);
263#endif
264
265 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
266 "Restraint Info: The specified restraint objectSelection,\n"
267 "\t\t%s\n"
268 "\twill result in %d integrable objects being\n"
269 "\trestrained.\n",
270 objectSelection.c_str(), selectionCount);
271 painCave.severity = OPENMD_INFO;
272 painCave.isFatal = 0;
273 simError();
274
275 int selei;
276 StuntDouble* sd;
277
278 for (sd = seleMan.beginSelected(selei); sd != NULL;
279 sd = seleMan.nextSelected(selei)) {
280 stuntDoubleIndex.push_back(sd->getGlobalIntegrableObjectIndex());
281
282 ObjectRestraint* rest = new ObjectRestraint();
283
284 if (stamp[i]->haveDisplacementSpringConstant()) {
285 rest->setDisplacementForceConstant(
286 stamp[i]->getDisplacementSpringConstant());
287 }
288 if (stamp[i]->haveDisplacementXSpringConstant()) {
289 rest->setDisplacementXForceConstant(
290 stamp[i]->getDisplacementXSpringConstant());
291 }
292 if (stamp[i]->haveDisplacementYSpringConstant()) {
293 rest->setDisplacementYForceConstant(
294 stamp[i]->getDisplacementYSpringConstant());
295 }
296 if (stamp[i]->haveDisplacementZSpringConstant()) {
297 rest->setDisplacementZForceConstant(
298 stamp[i]->getDisplacementZSpringConstant());
299 }
300 if (stamp[i]->haveAbsoluteSpringConstant()) {
301 rest->setAbsoluteForceConstant(
302 stamp[i]->getAbsoluteSpringConstant());
303 }
304 if (stamp[i]->haveTwistSpringConstant()) {
305 rest->setTwistForceConstant(stamp[i]->getTwistSpringConstant());
306 }
307 if (stamp[i]->haveSwingXSpringConstant()) {
308 rest->setSwingXForceConstant(stamp[i]->getSwingXSpringConstant());
309 }
310 if (stamp[i]->haveSwingYSpringConstant()) {
311 rest->setSwingYForceConstant(stamp[i]->getSwingYSpringConstant());
312 }
313 if (stamp[i]->haveAbsolutePositionZ()) {
314 rest->setAbsolutePositionZ(stamp[i]->getAbsolutePositionZ());
315 }
316 if (stamp[i]->haveRestrainedTwistAngle()) {
317 rest->setRestrainedTwistAngle(stamp[i]->getRestrainedTwistAngle());
318 }
319 if (stamp[i]->haveRestrainedSwingXAngle()) {
320 rest->setRestrainedSwingXAngle(
321 stamp[i]->getRestrainedSwingXAngle());
322 }
323 if (stamp[i]->haveRestrainedSwingYAngle()) {
324 rest->setRestrainedSwingYAngle(
325 stamp[i]->getRestrainedSwingYAngle());
326 }
327 if (stamp[i]->havePrint()) {
328 rest->setPrintRestraint(stamp[i]->getPrint());
329 }
330
331 restraints_.push_back(rest);
332 sd->addProperty(std::make_shared<RestraintData>("Restraint", rest));
333 restrainedObjs_.push_back(sd);
334 }
335 }
336 }
337
338 // ThermodynamicIntegration subclasses RestraintForceManager, and there
339 // are times when it won't use restraints at all, so only open the
340 // restraint file if we are actually using restraints:
341 if (simParam->getUseRestraints()) {
342 std::string refFile = simParam->getRestraint_file();
343 RestReader* rr = new RestReader(info, refFile, stuntDoubleIndex);
344 rr->readReferenceStructure();
345 delete rr;
346 }
347
348 restOutput_ = getPrefix(info_->getFinalConfigFileName()) + ".rest";
349 restOut = new RestWriter(info_, restOutput_.c_str(), restraints_);
350 if (!restOut) {
351 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
352 "Restraint error: Failed to create RestWriter\n");
353 painCave.isFatal = 1;
354 simError();
355 }
356
357 // todo: figure out the scale factor. Right now, just scale them all to 1
358 std::vector<Restraint*>::const_iterator resti;
359 for (resti = restraints_.begin(); resti != restraints_.end(); ++resti) {
360 (*resti)->setScaleFactor(1.0);
361 }
362 }
363
364 RestraintForceModifier::~RestraintForceModifier() {
365 Utils::deletePointers(restraints_);
366
367 delete restOut;
368 }
369
370 void RestraintForceModifier::modifyForces() {
371 RealType restPot(0.0);
372
373 restPot = doRestraints(1.0);
374
375#ifdef IS_MPI
376 MPI_Allreduce(MPI_IN_PLACE, &restPot, 1, MPI_REALTYPE, MPI_SUM,
377 MPI_COMM_WORLD);
378#endif
379
380 currSnapshot_ = info_->getSnapshotManager()->getCurrentSnapshot();
381 RealType rp = currSnapshot_->getRestraintPotential();
382 currSnapshot_->setRestraintPotential(rp + restPot);
383
384 RealType pe = currSnapshot_->getPotentialEnergy();
385 currSnapshot_->setRawPotential(pe);
386 currSnapshot_->setPotentialEnergy(pe + restPot);
387
388 // write out forces and current positions of restrained molecules
389 if (currSnapshot_->getTime() >= currRestTime_) {
390 restOut->writeRest(restInfo_);
391 currRestTime_ += restTime_;
392 }
393 }
394
395 RealType RestraintForceModifier::doRestraints(RealType scalingFactor) {
396 std::vector<Molecule*>::const_iterator rm;
397 std::shared_ptr<GenericData> data;
398 Molecule::IntegrableObjectIterator ioi;
399 MolecularRestraint* mRest = NULL;
400 ObjectRestraint* oRest = NULL;
401 StuntDouble* sd;
402
403 std::vector<StuntDouble*>::const_iterator ro;
404
405 std::map<int, Restraint::RealPair> restInfo;
406
407 unscaledPotential_ = 0.0;
408
409 restInfo_.clear();
410
411 for (rm = restrainedMols_.begin(); rm != restrainedMols_.end(); ++rm) {
412 // make sure this molecule (*rm) has a generic data for restraints:
413 data = (*rm)->getPropertyByName("Restraint");
414 if (data != nullptr) {
415 // make sure we can reinterpret the generic data as restraint data:
416 std::shared_ptr<RestraintData> restData =
417 std::dynamic_pointer_cast<RestraintData>(data);
418 if (restData != nullptr) {
419 // make sure we can reinterpet the restraint data as a
420 // MolecularRestraint
421 mRest = dynamic_cast<MolecularRestraint*>(restData->getData());
422 if (mRest == NULL) {
423 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
424 "Can not cast RestraintData to MolecularRestraint\n");
425 painCave.severity = OPENMD_ERROR;
426 painCave.isFatal = 1;
427 simError();
428 }
429 } else {
430 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
431 "Can not cast GenericData to RestraintData\n");
432 painCave.severity = OPENMD_ERROR;
433 painCave.isFatal = 1;
434 simError();
435 }
436 } else {
437 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
438 "Can not find Restraint for RestrainedObject\n");
439 painCave.severity = OPENMD_ERROR;
440 painCave.isFatal = 1;
441 simError();
442 }
443
444 // phew. At this point, we should have the pointer to the
445 // correct MolecularRestraint in the variable mRest.
446
447 Vector3d molCom = (*rm)->getCom();
448
449 std::vector<Vector3d> struc;
450 std::vector<Vector3d> forces;
451
452 for (sd = (*rm)->beginIntegrableObject(ioi); sd != NULL;
453 sd = (*rm)->nextIntegrableObject(ioi)) {
454 struc.push_back(sd->getPos());
455 }
456
457 mRest->setScaleFactor(scalingFactor);
458 mRest->calcForce(struc, molCom);
459 forces = mRest->getRestraintForces();
460 int index = 0;
461
462 for (sd = (*rm)->beginIntegrableObject(ioi); sd != NULL;
463 sd = (*rm)->nextIntegrableObject(ioi)) {
464 sd->addFrc(forces[index]);
465 struc.push_back(sd->getPos());
466 index++;
467 }
468
469 unscaledPotential_ += mRest->getUnscaledPotential();
470
471 // only collect data on restraints that we're going to print:
472 if (mRest->getPrintRestraint()) {
473 restInfo = mRest->getRestraintInfo();
474 restInfo_.push_back(restInfo);
475 }
476 }
477
478 for (ro = restrainedObjs_.begin(); ro != restrainedObjs_.end(); ++ro) {
479 // make sure this object (*ro) has a generic data for restraints:
480 data = (*ro)->getPropertyByName("Restraint");
481 if (data != NULL) {
482 // make sure we can reinterpret the generic data as restraint data:
483 std::shared_ptr<RestraintData> restData =
484 std::dynamic_pointer_cast<RestraintData>(data);
485 if (restData != nullptr) {
486 // make sure we can reinterpet the restraint data as a pointer to
487 // an ObjectRestraint:
488 oRest = dynamic_cast<ObjectRestraint*>(restData->getData());
489 if (oRest == NULL) {
490 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
491 "Can not cast RestraintData to ObjectRestraint\n");
492 painCave.severity = OPENMD_ERROR;
493 painCave.isFatal = 1;
494 simError();
495 }
496 } else {
497 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
498 "Can not cast GenericData to RestraintData\n");
499 painCave.severity = OPENMD_ERROR;
500 painCave.isFatal = 1;
501 simError();
502 }
503 } else {
504 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
505 "Can not find Restraint for RestrainedObject\n");
506 painCave.severity = OPENMD_ERROR;
507 painCave.isFatal = 1;
508 simError();
509 }
510
511 // phew. At this point, we should have the pointer to the
512 // correct Object restraint in the variable oRest.
513 oRest->setScaleFactor(scalingFactor);
514
515 Vector3d pos = (*ro)->getPos();
516
517 if ((*ro)->isDirectional()) {
518 // directional objects may have orientational restraints as well
519 // as positional, so get the rotation matrix first:
520
521 RotMat3x3d A = (*ro)->getA();
522 oRest->calcForce(pos, A);
523 (*ro)->addFrc(oRest->getRestraintForce());
524 (*ro)->addTrq(oRest->getRestraintTorque());
525
526 } else {
527 // plain vanilla positional restraints:
528
529 oRest->calcForce(pos);
530 (*ro)->addFrc(oRest->getRestraintForce());
531 }
532
533 unscaledPotential_ += oRest->getUnscaledPotential();
534
535 // only collect data on restraints that we're going to print:
536 if (oRest->getPrintRestraint()) {
537 restInfo = oRest->getRestraintInfo();
538 restInfo_.push_back(restInfo);
539 }
540 }
541
542 return unscaledPotential_ * scalingFactor;
543 }
544} // namespace OpenMD
Abstract class for external ForceModifier classes.
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
Definition SimInfo.hpp:96
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
std::string getPrefix(const std::string &str)