OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
Buckingham.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 "nonbonded/Buckingham.hpp"
49
50#include <cmath>
51#include <cstdio>
52#include <cstring>
53
54#include "types/BuckinghamInteractionType.hpp"
55#include "utils/simError.h"
56
57using namespace std;
58
59namespace OpenMD {
60
61 Buckingham::Buckingham() :
62 initialized_(false), forceField_(NULL), name_("Buckingham") {}
63
64 void Buckingham::initialize() {
65 Btypes.clear();
66 Btids.clear();
67 MixingMap.clear();
68 Btids.resize(forceField_->getNAtomType(), -1);
69
70 ForceField::NonBondedInteractionTypeContainer* nbiTypes =
71 forceField_->getNonBondedInteractionTypes();
72 ForceField::NonBondedInteractionTypeContainer::MapTypeIterator j;
73 ForceField::NonBondedInteractionTypeContainer::KeyType keys;
74 NonBondedInteractionType* nbt;
75 int btid1, btid2;
76
77 for (nbt = nbiTypes->beginType(j); nbt != NULL;
78 nbt = nbiTypes->nextType(j)) {
79 if (nbt->isBuckingham()) {
80 keys = nbiTypes->getKeys(j);
81 AtomType* at1 = forceField_->getAtomType(keys[0]);
82 if (at1 == NULL) {
83 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
84 "Buckingham::initialize could not find AtomType %s\n"
85 "\tto for for %s - %s interaction.\n",
86 keys[0].c_str(), keys[0].c_str(), keys[1].c_str());
87 painCave.severity = OPENMD_ERROR;
88 painCave.isFatal = 1;
89 simError();
90 }
91
92 AtomType* at2 = forceField_->getAtomType(keys[1]);
93 if (at2 == NULL) {
94 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
95 "Buckingham::initialize could not find AtomType %s\n"
96 "\tfor %s - %s nonbonded interaction.\n",
97 keys[1].c_str(), keys[0].c_str(), keys[1].c_str());
98 painCave.severity = OPENMD_ERROR;
99 painCave.isFatal = 1;
100 simError();
101 }
102
103 int atid1 = at1->getIdent();
104 if (Btids[atid1] == -1) {
105 btid1 = Btypes.size();
106 Btypes.insert(atid1);
107 Btids[atid1] = btid1;
108 }
109 int atid2 = at2->getIdent();
110 if (Btids[atid2] == -1) {
111 btid2 = Btypes.size();
112 Btypes.insert(atid2);
113 Btids[atid2] = btid2;
114 }
115
116 BuckinghamInteractionType* bit =
117 dynamic_cast<BuckinghamInteractionType*>(nbt);
118
119 if (bit == NULL) {
120 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
121 "Buckingham::initialize could not convert "
122 "NonBondedInteractionType\n"
123 "\tto BuckinghamInteractionType for %s - %s interaction.\n",
124 at1->getName().c_str(), at2->getName().c_str());
125 painCave.severity = OPENMD_ERROR;
126 painCave.isFatal = 1;
127 simError();
128 }
129
130 RealType A = bit->getA();
131 RealType B = bit->getB();
132 RealType C = bit->getC();
133
134 BuckinghamType variant = bit->getInteractionType();
135 addExplicitInteraction(at1, at2, A, B, C, variant);
136 }
137 }
138 initialized_ = true;
139 }
140
141 void Buckingham::addExplicitInteraction(AtomType* atype1, AtomType* atype2,
142 RealType A, RealType B, RealType C,
143 BuckinghamType bt) {
144 BuckinghamInteractionData mixer;
145 mixer.A = A;
146 mixer.B = B;
147 mixer.C = C;
148 mixer.variant = bt;
149
150 int btid1 = Btids[atype1->getIdent()];
151 int btid2 = Btids[atype2->getIdent()];
152 int nB = Btypes.size();
153
154 MixingMap.resize(nB);
155 MixingMap[btid1].resize(nB);
156
157 MixingMap[btid1][btid2] = mixer;
158 if (btid2 != btid1) {
159 MixingMap[btid2].resize(nB);
160 MixingMap[btid2][btid1] = mixer;
161 }
162 }
163
164 void Buckingham::addExplicitInteraction(AtomType* atype1, AtomType* atype2,
165 RealType A, RealType B, RealType C,
166 RealType sigma, RealType epsilon,
167 BuckinghamType bt) {
168 BuckinghamInteractionData mixer;
169 mixer.A = A;
170 mixer.B = B;
171 mixer.C = C;
172 mixer.sigma = sigma;
173 mixer.epsilon = epsilon;
174 mixer.variant = bt;
175
176 int btid1 = Btids[atype1->getIdent()];
177 int btid2 = Btids[atype2->getIdent()];
178 int nB = Btypes.size();
179
180 MixingMap.resize(nB);
181 MixingMap[btid1].resize(nB);
182
183 MixingMap[btid1][btid2] = mixer;
184 if (btid2 != btid1) {
185 MixingMap[btid2].resize(nB);
186 MixingMap[btid2][btid1] = mixer;
187 }
188 }
189
190 void Buckingham::calcForce(InteractionData& idat) {
191 if (!initialized_) initialize();
192
193 BuckinghamInteractionData& mixer =
194 MixingMap[Btids[idat.atid1]][Btids[idat.atid2]];
195
196 RealType myPot = 0.0;
197 RealType myPotC = 0.0;
198 RealType myDeriv = 0.0;
199 RealType myDerivC = 0.0;
200
201 RealType A = mixer.A;
202 RealType B = mixer.B;
203 RealType C = mixer.C;
204 RealType sigma = mixer.sigma;
205 RealType epsilon = mixer.epsilon;
206 BuckinghamType variant = mixer.variant;
207
208 RealType expt = -B * idat.rij;
209 RealType expfnc = exp(expt);
210 RealType fnc6 = 1.0 / pow(idat.rij, 6);
211 RealType fnc7 = fnc6 / idat.rij;
212
213 RealType exptC = 0.0;
214 RealType expfncC = 0.0;
215 RealType fnc6C = 0.0;
216 RealType fnc7C = 0.0;
217
218 if (idat.shiftedPot || idat.shiftedForce) {
219 exptC = -B * idat.rcut;
220 expfncC = exp(exptC);
221 fnc6C = 1.0 / pow(idat.rcut, 6);
222 fnc7C = fnc6C / idat.rcut;
223 }
224
225 switch (variant) {
226 case btTraditional: {
227 // V(r) = A exp(-B*r) - C/r^6
228 myPot = A * expfnc - C * fnc6;
229 myDeriv = -A * B * expfnc + C * fnc7;
230
231 if (idat.shiftedPot) {
232 myPotC = A * expfncC - C * fnc6C;
233 myDerivC = 0.0;
234 } else if (idat.shiftedForce) {
235 myPotC = A * expfncC - C * fnc6C;
236 myDerivC = -A * B * expfncC + C * fnc7C;
237 myPotC += myDerivC * (idat.rij - idat.rcut);
238 } else {
239 myPotC = 0.0;
240 myDerivC = 0.0;
241 }
242 break;
243 }
244 case btModified: {
245 RealType s6 = pow(sigma, 6);
246 RealType s7 = pow(sigma, 7);
247 RealType fnc30 = pow(sigma / idat.rij, 30);
248 RealType fnc31 = fnc30 * sigma / idat.rij;
249 RealType fnc30C = 0.0;
250 RealType fnc31C = 0.0;
251
252 if (idat.shiftedPot || idat.shiftedForce) {
253 fnc30C = pow(sigma / idat.rcut, 30);
254 fnc31C = fnc30C * sigma / idat.rcut;
255 }
256
257 // V(r) = A exp(-B*r) - C/r^6 + 4 epsilon ((sigma/r)^30 - (sigma/r)^6)
258 myPot = A * expfnc - C * fnc6 + 4.0 * epsilon * (fnc30 - s6 * fnc6);
259 myDeriv = -A * B * expfnc + C * fnc7 +
260 4.0 * epsilon * (-30.0 * fnc31 + 6.0 * s7 * fnc7) / sigma;
261
262 if (idat.shiftedPot) {
263 myPotC =
264 A * expfncC - C * fnc6C + 4.0 * epsilon * (fnc30C - s6 * fnc6C);
265 myDerivC = 0.0;
266 } else if (idat.shiftedForce) {
267 myPotC =
268 A * expfncC - C * fnc6C + 4.0 * epsilon * (fnc30C - s6 * fnc6C);
269 myDeriv = -A * B * expfncC + C * fnc7C +
270 4.0 * epsilon * (-30.0 * fnc31C + 6.0 * s7 * fnc7C) / sigma;
271 myPotC += myDerivC * (idat.rij - idat.rcut);
272 } else {
273 myPotC = 0.0;
274 myDerivC = 0.0;
275 }
276
277 break;
278 }
279 case btUnknown: {
280 // don't know what to do so don't do anything
281 break;
282 }
283 }
284
285 RealType pot_temp = idat.vdwMult * (myPot - myPotC);
286 idat.vpair += pot_temp;
287
288 RealType dudr = idat.sw * idat.vdwMult * (myDeriv - myDerivC);
289
290 idat.pot[VANDERWAALS_FAMILY] += idat.sw * pot_temp;
291 if (idat.isSelected) idat.selePot[VANDERWAALS_FAMILY] += idat.sw * pot_temp;
292
293 idat.f1 += idat.d * dudr / idat.rij;
294
295 return;
296 }
297
298 RealType Buckingham::getSuggestedCutoffRadius(
299 pair<AtomType*, AtomType*> atypes) {
300 if (!initialized_) initialize();
301
302 int atid1 = atypes.first->getIdent();
303 int atid2 = atypes.second->getIdent();
304 int btid1 = Btids[atid1];
305 int btid2 = Btids[atid2];
306
307 if (btid1 == -1 || btid2 == -1)
308 return 0.0;
309 else {
310 // Uncomment if we ever want to query the simulated atoms types
311 // for a suggested cutoff:
312 //
313 // BuckinghamInteractionData mixer = MixingMap[btid1][btid2];
314 //
315 // suggested cutoff for most implementations of the BKS potential are
316 // around 1 nm (10 angstroms):
317 return 10.0;
318 }
319 }
320} // namespace OpenMD
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
@ VANDERWAALS_FAMILY
Long-range dispersion and short-range pauli repulsion.