OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
Sticky.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/Sticky.hpp"
49
50#include <cmath>
51#include <cstdio>
52#include <cstring>
53
54#include "nonbonded/LJ.hpp"
55#include "types/StickyAdapter.hpp"
56#include "utils/simError.h"
57
58using namespace std;
59namespace OpenMD {
60
61 Sticky::Sticky() : initialized_(false), forceField_(NULL), name_("Sticky") {}
62
63 void Sticky::initialize() {
64 Stypes.clear();
65 Stids.clear();
66 MixingMap.clear();
67 nSticky_ = 0;
68
69 Stids.resize(forceField_->getNAtomType(), -1);
70
71 // Sticky handles all of the Sticky-Sticky interactions
72
73 AtomTypeSet::iterator at;
74 for (at = simTypes_.begin(); at != simTypes_.end(); ++at) {
75 if ((*at)->isSticky()) nSticky_++;
76 }
77
78 MixingMap.resize(nSticky_);
79
80 for (at = simTypes_.begin(); at != simTypes_.end(); ++at) {
81 if ((*at)->isSticky()) addType(*at);
82 }
83
84 initialized_ = true;
85 }
86
87 void Sticky::addType(AtomType* atomType) {
88 StickyAdapter sticky1 = StickyAdapter(atomType);
89
90 // add it to the map:
91
92 int atid = atomType->getIdent();
93 int stid = Stypes.size();
94
95 pair<set<int>::iterator, bool> ret;
96 ret = Stypes.insert(atid);
97 if (ret.second == false) {
98 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
99 "Sticky already had a previous entry with ident %d\n", atid);
100 painCave.severity = OPENMD_INFO;
101 painCave.isFatal = 0;
102 simError();
103 }
104
105 Stids[atid] = stid;
106 MixingMap[stid].resize(nSticky_);
107
108 // Now, iterate over all known types and add to the mixing map:
109
110 std::set<int>::iterator it;
111 for (it = Stypes.begin(); it != Stypes.end(); ++it) {
112 int stid2 = Stids[(*it)];
113 AtomType* atype2 = forceField_->getAtomType((*it));
114 StickyAdapter sticky2 = StickyAdapter(atype2);
115
116 StickyInteractionData mixer;
117
118 // Mixing two different sticky types is silly, but if you want 2
119 // sticky types in your simulation, we'll let you do it with the
120 // Lorentz- Berthelot mixing rules (which happen to do the right thing
121 // when atomType and atype2 happen to be the same.
122
123 mixer.rl = 0.5 * (sticky1.getRl() + sticky2.getRl());
124 mixer.ru = 0.5 * (sticky1.getRu() + sticky2.getRu());
125 mixer.rlp = 0.5 * (sticky1.getRlp() + sticky2.getRlp());
126 mixer.rup = 0.5 * (sticky1.getRup() + sticky2.getRup());
127 mixer.rbig = max(mixer.ru, mixer.rup);
128 mixer.w0 = sqrt(sticky1.getW0() * sticky2.getW0());
129 mixer.v0 = sqrt(sticky1.getV0() * sticky2.getV0());
130 mixer.v0p = sqrt(sticky1.getV0p() * sticky2.getV0p());
131 mixer.isPower = sticky1.isStickyPower() && sticky2.isStickyPower();
132
133 CubicSpline* s = new CubicSpline();
134 s->addPoint(mixer.rl, 1.0);
135 s->addPoint(mixer.ru, 0.0);
136 mixer.s = s;
137
138 CubicSpline* sp = new CubicSpline();
139 sp->addPoint(mixer.rlp, 1.0);
140 sp->addPoint(mixer.rup, 0.0);
141 mixer.sp = sp;
142
143 MixingMap[stid2].resize(nSticky_);
144
145 MixingMap[stid][stid2] = mixer;
146 if (stid2 != stid) { MixingMap[stid2][stid] = mixer; }
147 }
148 }
149
150 /**
151 * This function does the sticky portion of the SSD potential
152 * [Chandra and Ichiye, Journal of Chemical Physics 111, 2701
153 * (1999)]. The Lennard-Jones and dipolar interaction must be
154 * handled separately. We assume that the rotation matrices have
155 * already been calculated and placed in the A1 & A2 entries in the
156 * idat structure.
157 */
158
160 if (!initialized_) initialize();
161
162 StickyInteractionData& mixer =
163 MixingMap[Stids[idat.atid1]][Stids[idat.atid2]];
164
165 RealType w0 = mixer.w0;
166 RealType v0 = mixer.v0;
167 RealType v0p = mixer.v0p;
168 RealType rl = mixer.rl;
169 RealType ru = mixer.ru;
170 RealType rlp = mixer.rlp;
171 RealType rup = mixer.rup;
172 RealType rbig = mixer.rbig;
173 bool isPower = mixer.isPower;
174
175 if (idat.rij <= rbig) {
176 RealType r3 = idat.r2 * idat.rij;
177 RealType r5 = r3 * idat.r2;
178
179 RotMat3x3d A1trans = idat.A1.transpose();
180 RotMat3x3d A2trans = idat.A2.transpose();
181
182 // rotate the inter-particle separation into the two different
183 // body-fixed coordinate systems:
184
185 Vector3d ri = idat.A1 * idat.d;
186
187 // negative sign because this is the vector from j to i:
188
189 Vector3d rj = -idat.A2 * idat.d;
190
191 RealType xi = ri.x();
192 RealType yi = ri.y();
193 RealType zi = ri.z();
194
195 RealType xj = rj.x();
196 RealType yj = rj.y();
197 RealType zj = rj.z();
198
199 RealType xi2 = xi * xi;
200 RealType yi2 = yi * yi;
201 RealType zi2 = zi * zi;
202
203 RealType xj2 = xj * xj;
204 RealType yj2 = yj * yj;
205 RealType zj2 = zj * zj;
206
207 // calculate the switching info. from the splines
208
209 RealType s = 0.0;
210 RealType dsdr = 0.0;
211 RealType sp = 0.0;
212 RealType dspdr = 0.0;
213
214 if (idat.rij < ru) {
215 if (idat.rij < rl) {
216 s = 1.0;
217 dsdr = 0.0;
218 } else {
219 // we are in the switching region
220 mixer.s->getValueAndDerivativeAt(idat.rij, s, dsdr);
221 }
222 }
223
224 if (idat.rij < rup) {
225 if (idat.rij < rlp) {
226 sp = 1.0;
227 dspdr = 0.0;
228 } else {
229 // we are in the switching region
230 mixer.sp->getValueAndDerivativeAt(idat.rij, sp, dspdr);
231 }
232 }
233
234 RealType wi = 2.0 * (xi2 - yi2) * zi / r3;
235 RealType wj = 2.0 * (xj2 - yj2) * zj / r3;
236 RealType w = wi + wj;
237
238 RealType zif = zi / idat.rij - 0.6;
239 RealType zis = zi / idat.rij + 0.8;
240
241 RealType zjf = zj / idat.rij - 0.6;
242 RealType zjs = zj / idat.rij + 0.8;
243
244 RealType wip = zif * zif * zis * zis - w0;
245 RealType wjp = zjf * zjf * zjs * zjs - w0;
246 RealType wp = wip + wjp;
247
248 Vector3d dwi(4.0 * xi * zi / r3 - 6.0 * xi * zi * (xi2 - yi2) / r5,
249 -4.0 * yi * zi / r3 - 6.0 * yi * zi * (xi2 - yi2) / r5,
250 2.0 * (xi2 - yi2) / r3 - 6.0 * zi2 * (xi2 - yi2) / r5);
251
252 Vector3d dwj(4.0 * xj * zj / r3 - 6.0 * xj * zj * (xj2 - yj2) / r5,
253 -4.0 * yj * zj / r3 - 6.0 * yj * zj * (xj2 - yj2) / r5,
254 2.0 * (xj2 - yj2) / r3 - 6.0 * zj2 * (xj2 - yj2) / r5);
255
256 RealType uglyi = zif * zif * zis + zif * zis * zis;
257 RealType uglyj = zjf * zjf * zjs + zjf * zjs * zjs;
258
259 Vector3d dwip(-2.0 * xi * zi * uglyi / r3, -2.0 * yi * zi * uglyi / r3,
260 2.0 * (1.0 / idat.rij - zi2 / r3) * uglyi);
261
262 Vector3d dwjp(-2.0 * xj * zj * uglyj / r3, -2.0 * yj * zj * uglyj / r3,
263 2.0 * (1.0 / idat.rij - zj2 / r3) * uglyj);
264
265 Vector3d dwidu(4.0 * (yi * zi2 + 0.5 * yi * (xi2 - yi2)) / r3,
266 4.0 * (xi * zi2 - 0.5 * xi * (xi2 - yi2)) / r3,
267 -8.0 * xi * yi * zi / r3);
268
269 Vector3d dwjdu(4.0 * (yj * zj2 + 0.5 * yj * (xj2 - yj2)) / r3,
270 4.0 * (xj * zj2 - 0.5 * xj * (xj2 - yj2)) / r3,
271 -8.0 * xj * yj * zj / r3);
272
273 Vector3d dwipdu(2.0 * yi * uglyi / idat.rij, -2.0 * xi * uglyi / idat.rij,
274 0.0);
275
276 Vector3d dwjpdu(2.0 * yj * uglyj / idat.rij, -2.0 * xj * uglyj / idat.rij,
277 0.0);
278
279 if (isPower) {
280 cerr << "This is probably an error!\n";
281 RealType frac1 = 0.25;
282 RealType frac2 = 0.75;
283 RealType wi2 = wi * wi;
284 RealType wj2 = wj * wj;
285 // sticky power has no w' function:
286 w = frac1 * wi * wi2 + frac2 * wi + frac1 * wj * wj2 + frac2 * wj + v0p;
287 wp = 0.0;
288 dwi = frac1 * RealType(3.0) * wi2 * dwi + frac2 * dwi;
289 dwj = frac1 * RealType(3.0) * wj2 * dwi + frac2 * dwi;
290 dwip = V3Zero;
291 dwjp = V3Zero;
292 dwidu = frac1 * RealType(3.0) * wi2 * dwidu + frac2 * dwidu;
293 dwidu = frac1 * RealType(3.0) * wj2 * dwjdu + frac2 * dwjdu;
294 dwipdu = V3Zero;
295 dwjpdu = V3Zero;
296 sp = 0.0;
297 dspdr = 0.0;
298 }
299
300 idat.vpair += 0.5 * (v0 * s * w + v0p * sp * wp);
302 0.5 * (v0 * s * w + v0p * sp * wp) * idat.sw;
303 if (idat.isSelected)
305 0.5 * (v0 * s * w + v0p * sp * wp) * idat.sw;
306
307 // do the torques first since they are easy:
308 // remember that these are still in the body-fixed axes
309
310 Vector3d ti = 0.5 * idat.sw * (v0 * s * dwidu + v0p * sp * dwipdu);
311 Vector3d tj = 0.5 * idat.sw * (v0 * s * dwjdu + v0p * sp * dwjpdu);
312
313 // go back to lab frame using transpose of rotation matrix:
314
315 idat.t1 += A1trans * ti;
316 idat.t2 += A2trans * tj;
317
318 // Now, on to the forces:
319
320 // first rotate the i terms back into the lab frame:
321
322 Vector3d radcomi = (v0 * s * dwi + v0p * sp * dwip) * idat.sw;
323 Vector3d radcomj = (v0 * s * dwj + v0p * sp * dwjp) * idat.sw;
324
325 Vector3d fii = A1trans * radcomi;
326 Vector3d fjj = A2trans * radcomj;
327
328 // now assemble these with the radial-only terms:
329
330 idat.f1 += 0.5 * ((v0 * dsdr * w + v0p * dspdr * wp) * idat.d / idat.rij +
331 fii - fjj);
332 }
333
334 return;
335 }
336
337 RealType Sticky::getSuggestedCutoffRadius(pair<AtomType*, AtomType*> atypes) {
338 if (!initialized_) initialize();
339 int atid1 = atypes.first->getIdent();
340 int atid2 = atypes.second->getIdent();
341 int stid1 = Stids[atid1];
342 int stid2 = Stids[atid2];
343
344 if (stid1 == -1 || stid2 == -1)
345 return 0.0;
346 else { return MixingMap[stid1][stid2].rbig; }
347 }
348} // namespace OpenMD
virtual void calcForce(InteractionData &idat)
This function does the sticky portion of the SSD potential [Chandra and Ichiye, Journal of Chemical P...
Definition Sticky.cpp:159
Real & z()
Returns reference of the third element of Vector3.
Definition Vector3.hpp:123
Real & x()
Returns reference of the first element of Vector3.
Definition Vector3.hpp:99
Real & y()
Returns reference of the second element of Vector3.
Definition Vector3.hpp:111
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
@ HYDROGENBONDING_FAMILY
Short-range directional interactions.
The InteractionData struct.
Vector3d d
interatomic vector (already wrapped into box)
int atid1
atomType ident for atom 1
potVec selePot
potential energies of the selected sites
RealType sw
switching function value at rij
RotMat3x3d A2
rotation matrix of second atom
bool isSelected
one of the particles has been selected for selection potential
RotMat3x3d A1
rotation matrix of first atom
Vector3d t1
torque on first atom
Vector3d t2
torque on second atom
RealType vpair
pair potential
int atid2
atomType ident for atom 2
Vector3d f1
force between the two atoms
RealType rij
interatomic separation