OpenMD 3.0
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
SC.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 appropriate papers when you publish your
33 * work. Good starting points are:
34 *
35 * [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
36 * [2] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
37 * [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
38 * [4] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
39 * [5] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
40 * [6] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
41 * [7] Lamichhane, Newman & Gezelter, J. Chem. Phys. 141, 134110 (2014).
42 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
43 */
44
45#include "nonbonded/SC.hpp"
46
47#include <cmath>
48#include <cstdio>
49#include <cstring>
50
52#include "utils/simError.h"
53
54namespace OpenMD {
55
56 SC::SC() : name_("SC"), initialized_(false), forceField_(NULL), np_(3000) {}
57
58 SC::~SC() {
59 initialized_ = false;
60
61 MixingMap.clear();
62 SCtypes.clear();
63 SCdata.clear();
64 SCtids.clear();
65 }
66
67 RealType SC::getM(AtomType* atomType1, AtomType* atomType2) {
68 SuttonChenAdapter sca1 = SuttonChenAdapter(atomType1);
69 SuttonChenAdapter sca2 = SuttonChenAdapter(atomType2);
70 RealType m1 = sca1.getM();
71 RealType m2 = sca2.getM();
72 return 0.5 * (m1 + m2);
73 }
74
75 RealType SC::getN(AtomType* atomType1, AtomType* atomType2) {
76 SuttonChenAdapter sca1 = SuttonChenAdapter(atomType1);
77 SuttonChenAdapter sca2 = SuttonChenAdapter(atomType2);
78 RealType n1 = sca1.getN();
79 RealType n2 = sca2.getN();
80 return 0.5 * (n1 + n2);
81 }
82
83 RealType SC::getAlpha(AtomType* atomType1, AtomType* atomType2) {
84 SuttonChenAdapter sca1 = SuttonChenAdapter(atomType1);
85 SuttonChenAdapter sca2 = SuttonChenAdapter(atomType2);
86 RealType alpha1 = sca1.getAlpha();
87 RealType alpha2 = sca2.getAlpha();
88
89 ForceFieldOptions& fopts = forceField_->getForceFieldOptions();
90 std::string DistanceMix = fopts.getDistanceMixingRule();
91 toUpper(DistanceMix);
92
93 if (DistanceMix == "GEOMETRIC")
94 return sqrt(alpha1 * alpha2);
95 else
96 return 0.5 * (alpha1 + alpha2);
97 }
98
99 RealType SC::getEpsilon(AtomType* atomType1, AtomType* atomType2) {
100 SuttonChenAdapter sca1 = SuttonChenAdapter(atomType1);
101 SuttonChenAdapter sca2 = SuttonChenAdapter(atomType2);
102 RealType epsilon1 = sca1.getEpsilon();
103 RealType epsilon2 = sca2.getEpsilon();
104 return sqrt(epsilon1 * epsilon2);
105 }
106
107 void SC::initialize() {
108 // find all of the SC atom Types:
109 SCtypes.clear();
110 SCtids.clear();
111 SCdata.clear();
112 MixingMap.clear();
113 nSC_ = 0;
114
115 SCtids.resize(forceField_->getNAtomType(), -1);
116
117 AtomTypeSet::iterator at;
118 for (at = simTypes_.begin(); at != simTypes_.end(); ++at) {
119 if ((*at)->isSC()) nSC_++;
120 }
121 SCdata.resize(nSC_);
122 MixingMap.resize(nSC_);
123 for (at = simTypes_.begin(); at != simTypes_.end(); ++at) {
124 if ((*at)->isSC()) addType((*at));
125 }
126 initialized_ = true;
127 }
128
129 void SC::addType(AtomType* atomType) {
130 SuttonChenAdapter sca = SuttonChenAdapter(atomType);
131 SCAtomData scAtomData;
132
133 scAtomData.c = sca.getC();
134 scAtomData.m = sca.getM();
135 scAtomData.n = sca.getN();
136 scAtomData.alpha = sca.getAlpha();
137 scAtomData.epsilon = sca.getEpsilon();
138 scAtomData.rCut = 2.0 * scAtomData.alpha;
139
140 // add it to the map:
141 int atid = atomType->getIdent();
142 int sctid = SCtypes.size();
143
144 pair<set<int>::iterator, bool> ret;
145 ret = SCtypes.insert(atid);
146 if (ret.second == false) {
147 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
148 "SC already had a previous entry with ident %d\n", atid);
149 painCave.severity = OPENMD_INFO;
150 painCave.isFatal = 0;
151 simError();
152 }
153
154 SCtids[atid] = sctid;
155 SCdata[sctid] = scAtomData;
156 MixingMap[sctid].resize(nSC_);
157
158 // Now, iterate over all known types and add to the mixing map:
159
160 std::set<int>::iterator it;
161 for (it = SCtypes.begin(); it != SCtypes.end(); ++it) {
162 int sctid2 = SCtids[(*it)];
163 AtomType* atype2 = forceField_->getAtomType((*it));
164
165 SCInteractionData mixer;
166
167 mixer.alpha = getAlpha(atomType, atype2);
168 mixer.rCut = 2.0 * mixer.alpha;
169 mixer.epsilon = getEpsilon(atomType, atype2);
170 mixer.m = getM(atomType, atype2);
171 mixer.n = getN(atomType, atype2);
172
173 RealType dr = mixer.rCut / (np_ - 1);
174 vector<RealType> rvals;
175 vector<RealType> vvals;
176 vector<RealType> phivals;
177
178 rvals.push_back(0.0);
179 vvals.push_back(0.0);
180 phivals.push_back(0.0);
181
182 for (int k = 1; k < np_; k++) {
183 RealType r = dr * k;
184 rvals.push_back(r);
185 vvals.push_back(mixer.epsilon * pow(mixer.alpha / r, mixer.n));
186 phivals.push_back(pow(mixer.alpha / r, mixer.m));
187 }
188
189 mixer.vCut = mixer.epsilon * pow(mixer.alpha / mixer.rCut, mixer.n);
190
191 CubicSpline* V = new CubicSpline();
192 V->addPoints(rvals, vvals);
193
194 CubicSpline* phi = new CubicSpline();
195 phi->addPoints(rvals, phivals);
196
197 mixer.V = V;
198 mixer.phi = phi;
199
200 mixer.explicitlySet = false;
201
202 MixingMap[sctid2].resize(nSC_);
203
204 MixingMap[sctid][sctid2] = mixer;
205 if (sctid2 != sctid) { MixingMap[sctid2][sctid] = mixer; }
206 }
207 return;
208 }
209
210 void SC::addExplicitInteraction(AtomType* atype1, AtomType* atype2,
211 RealType epsilon, RealType m, RealType n,
212 RealType alpha) {
213 // in case these weren't already in the map
214 addType(atype1);
215 addType(atype2);
216
217 SCInteractionData mixer;
218
219 mixer.epsilon = epsilon;
220 mixer.m = m;
221 mixer.n = n;
222 mixer.alpha = alpha;
223 mixer.rCut = 2.0 * mixer.alpha;
224
225 RealType dr = mixer.rCut / (np_ - 1);
226 vector<RealType> rvals;
227 vector<RealType> vvals;
228 vector<RealType> phivals;
229
230 rvals.push_back(0.0);
231 vvals.push_back(0.0);
232 phivals.push_back(0.0);
233
234 for (int k = 1; k < np_; k++) {
235 RealType r = dr * k;
236 rvals.push_back(r);
237 vvals.push_back(mixer.epsilon * pow(mixer.alpha / r, mixer.n));
238 phivals.push_back(pow(mixer.alpha / r, mixer.m));
239 }
240
241 mixer.vCut = mixer.epsilon * pow(mixer.alpha / mixer.rCut, mixer.n);
242
243 CubicSpline* V = new CubicSpline();
244 V->addPoints(rvals, vvals);
245
246 CubicSpline* phi = new CubicSpline();
247 phi->addPoints(rvals, phivals);
248
249 mixer.V = V;
250 mixer.phi = phi;
251
252 mixer.explicitlySet = true;
253
254 int sctid1 = SCtids[atype1->getIdent()];
255 int sctid2 = SCtids[atype2->getIdent()];
256
257 MixingMap[sctid1][sctid2] = mixer;
258 if (sctid2 != sctid1) { MixingMap[sctid2][sctid1] = mixer; }
259 return;
260 }
261
262 void SC::calcDensity(InteractionData& idat) {
263 if (!initialized_) initialize();
264 int sctid1 = SCtids[idat.atid1];
265 int sctid2 = SCtids[idat.atid2];
266
267 SCInteractionData& mixer = MixingMap[sctid1][sctid2];
268
269 RealType rcij = mixer.rCut;
270
271 if (idat.rij < rcij) {
272 RealType rho = mixer.phi->getValueAt(idat.rij);
273 idat.rho1 += rho;
274 idat.rho2 += rho;
275 }
276
277 return;
278 }
279
280 void SC::calcFunctional(SelfData& sdat) {
281 if (!initialized_) initialize();
282
283 SCAtomData& data1 = SCdata[SCtids[sdat.atid]];
284
285 RealType u = -data1.c * data1.epsilon * sqrt(sdat.rho);
286 sdat.frho = u;
287 sdat.dfrhodrho = 0.5 * sdat.frho / sdat.rho;
288
289 sdat.selfPot[METALLIC_EMBEDDING_FAMILY] += u;
290
291 if (sdat.isSelected) sdat.selePot[METALLIC_EMBEDDING_FAMILY] += u;
292
293 if (sdat.doParticlePot) { sdat.particlePot += u; }
294
295 return;
296 }
297
298 void SC::calcForce(InteractionData& idat) {
299 if (!initialized_) initialize();
300
301 int& sctid1 = SCtids[idat.atid1];
302 int& sctid2 = SCtids[idat.atid2];
303
304 SCAtomData& data1 = SCdata[sctid1];
305 SCAtomData& data2 = SCdata[sctid2];
306
307 SCInteractionData& mixer = MixingMap[sctid1][sctid2];
308
309 RealType rcij = mixer.rCut;
310
311 if (idat.rij < rcij) {
312 RealType vcij = mixer.vCut;
313 RealType rhtmp, drhodr, vptmp, dvpdr;
314
315 mixer.phi->getValueAndDerivativeAt(idat.rij, rhtmp, drhodr);
316 mixer.V->getValueAndDerivativeAt(idat.rij, vptmp, dvpdr);
317
318 RealType pot_temp = vptmp - vcij;
319 idat.vpair += pot_temp;
320
321 RealType dudr = drhodr * (idat.dfrho1 + idat.dfrho2) + dvpdr;
322
323 idat.f1 += idat.d * dudr / idat.rij;
324
325 if (idat.doParticlePot) {
326 // particlePot is the difference between the full potential and
327 // the full potential without the presence of a particular
328 // particle (atom1).
329 //
330 // This reduces the density at other particle locations, so we
331 // need to recompute the density at atom2 assuming atom1 didn't
332 // contribute. This then requires recomputing the density
333 // functional for atom2 as well.
334
335 idat.particlePot1 -=
336 data2.c * data2.epsilon * sqrt(idat.rho2 - rhtmp) + idat.frho2;
337
338 idat.particlePot2 -=
339 data1.c * data1.epsilon * sqrt(idat.rho1 - rhtmp) + idat.frho1;
340 }
341
342 idat.pot[METALLIC_PAIR_FAMILY] += pot_temp;
343
344 if (idat.isSelected) idat.selePot[METALLIC_PAIR_FAMILY] += pot_temp;
345 }
346
347 return;
348 }
349
350 RealType SC::getSuggestedCutoffRadius(pair<AtomType*, AtomType*> atypes) {
351 if (!initialized_) initialize();
352
353 int atid1 = atypes.first->getIdent();
354 int atid2 = atypes.second->getIdent();
355 int& sctid1 = SCtids[atid1];
356 int& sctid2 = SCtids[atid2];
357
358 if (sctid1 == -1 || sctid2 == -1) {
359 return 0.0;
360 } else {
361 return MixingMap[sctid1][sctid2].rCut;
362 }
363 }
364} // namespace OpenMD
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
@ METALLIC_EMBEDDING_FAMILY
Transition metal interactions involving electron density.
@ METALLIC_PAIR_FAMILY
Transition metal interactions involving pair potentials.