ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/branches/development/src/nonbonded/Electrostatic.cpp
Revision: 1710
Committed: Fri May 18 21:44:02 2012 UTC (13 years ago) by gezelter
File size: 33878 byte(s)
Log Message:
Added an adapter layer between the AtomType and the rest of the code to 
handle the bolt-on capabilities of new types. 

Fixed a long-standing bug in how storageLayout was being set to the maximum
possible value.

Started to add infrastructure for Polarizable and fluc-Q calculations.

File Contents

# User Rev Content
1 gezelter 1502 /*
2     * Copyright (c) 2005 The University of Notre Dame. All Rights Reserved.
3     *
4     * The University of Notre Dame grants you ("Licensee") a
5     * non-exclusive, royalty free, license to use, modify and
6     * redistribute this software in source and binary code form, provided
7     * that the following conditions are met:
8     *
9     * 1. Redistributions of source code must retain the above copyright
10     * notice, this list of conditions and the following disclaimer.
11     *
12     * 2. Redistributions in binary form must reproduce the above copyright
13     * notice, this list of conditions and the following disclaimer in the
14     * documentation and/or other materials provided with the
15     * distribution.
16     *
17     * This software is provided "AS IS," without a warranty of any
18     * kind. All express or implied conditions, representations and
19     * warranties, including any implied warranty of merchantability,
20     * fitness for a particular purpose or non-infringement, are hereby
21     * excluded. The University of Notre Dame and its licensors shall not
22     * be liable for any damages suffered by licensee as a result of
23     * using, modifying or distributing the software or its
24     * derivatives. In no event will the University of Notre Dame or its
25     * licensors be liable for any lost revenue, profit or data, or for
26     * direct, indirect, special, consequential, incidental or punitive
27     * damages, however caused and regardless of the theory of liability,
28     * arising out of the use of or inability to use software, even if the
29     * University of Notre Dame has been advised of the possibility of
30     * such damages.
31     *
32     * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
33     * research, please cite the appropriate papers when you publish your
34     * work. Good starting points are:
35     *
36     * [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
37 gezelter 1587 * [2] Fennell & Gezelter, J. Chem. Phys. 124 234104 (2006).
38 gezelter 1502 * [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 24107 (2008).
39 gezelter 1665 * [4] Kuang & Gezelter, J. Chem. Phys. 133, 164101 (2010).
40     * [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
41 gezelter 1502 */
42    
43     #include <stdio.h>
44     #include <string.h>
45    
46     #include <cmath>
47     #include "nonbonded/Electrostatic.hpp"
48     #include "utils/simError.h"
49     #include "types/NonBondedInteractionType.hpp"
50 gezelter 1710 #include "types/FixedChargeAdapter.hpp"
51     #include "types/MultipoleAdapter.hpp"
52 gezelter 1535 #include "io/Globals.hpp"
53 gezelter 1502
54     namespace OpenMD {
55    
56     Electrostatic::Electrostatic(): name_("Electrostatic"), initialized_(false),
57 gezelter 1587 forceField_(NULL), info_(NULL),
58     haveCutoffRadius_(false),
59     haveDampingAlpha_(false),
60     haveDielectric_(false),
61 gezelter 1586 haveElectroSpline_(false)
62 gezelter 1587 {}
63 gezelter 1502
64     void Electrostatic::initialize() {
65 gezelter 1587
66 gezelter 1584 Globals* simParams_ = info_->getSimParams();
67 gezelter 1535
68 gezelter 1528 summationMap_["HARD"] = esm_HARD;
69 gezelter 1616 summationMap_["NONE"] = esm_HARD;
70 gezelter 1528 summationMap_["SWITCHING_FUNCTION"] = esm_SWITCHING_FUNCTION;
71     summationMap_["SHIFTED_POTENTIAL"] = esm_SHIFTED_POTENTIAL;
72     summationMap_["SHIFTED_FORCE"] = esm_SHIFTED_FORCE;
73     summationMap_["REACTION_FIELD"] = esm_REACTION_FIELD;
74     summationMap_["EWALD_FULL"] = esm_EWALD_FULL;
75     summationMap_["EWALD_PME"] = esm_EWALD_PME;
76     summationMap_["EWALD_SPME"] = esm_EWALD_SPME;
77     screeningMap_["DAMPED"] = DAMPED;
78     screeningMap_["UNDAMPED"] = UNDAMPED;
79    
80 gezelter 1502 // these prefactors convert the multipole interactions into kcal / mol
81     // all were computed assuming distances are measured in angstroms
82     // Charge-Charge, assuming charges are measured in electrons
83     pre11_ = 332.0637778;
84     // Charge-Dipole, assuming charges are measured in electrons, and
85     // dipoles are measured in debyes
86     pre12_ = 69.13373;
87     // Dipole-Dipole, assuming dipoles are measured in debyes
88     pre22_ = 14.39325;
89     // Charge-Quadrupole, assuming charges are measured in electrons, and
90     // quadrupoles are measured in 10^-26 esu cm^2
91     // This unit is also known affectionately as an esu centi-barn.
92     pre14_ = 69.13373;
93    
94     // conversions for the simulation box dipole moment
95     chargeToC_ = 1.60217733e-19;
96     angstromToM_ = 1.0e-10;
97     debyeToCm_ = 3.33564095198e-30;
98    
99     // number of points for electrostatic splines
100     np_ = 100;
101    
102     // variables to handle different summation methods for long-range
103     // electrostatics:
104 gezelter 1528 summationMethod_ = esm_HARD;
105 gezelter 1502 screeningMethod_ = UNDAMPED;
106     dielectric_ = 1.0;
107     one_third_ = 1.0 / 3.0;
108    
109 gezelter 1528 // check the summation method:
110     if (simParams_->haveElectrostaticSummationMethod()) {
111     string myMethod = simParams_->getElectrostaticSummationMethod();
112     toUpper(myMethod);
113     map<string, ElectrostaticSummationMethod>::iterator i;
114     i = summationMap_.find(myMethod);
115     if ( i != summationMap_.end() ) {
116     summationMethod_ = (*i).second;
117     } else {
118     // throw error
119     sprintf( painCave.errMsg,
120 gezelter 1536 "Electrostatic::initialize: Unknown electrostaticSummationMethod.\n"
121 gezelter 1528 "\t(Input file specified %s .)\n"
122 gezelter 1616 "\telectrostaticSummationMethod must be one of: \"hard\",\n"
123 gezelter 1528 "\t\"shifted_potential\", \"shifted_force\", or \n"
124     "\t\"reaction_field\".\n", myMethod.c_str() );
125     painCave.isFatal = 1;
126     simError();
127     }
128     } else {
129     // set ElectrostaticSummationMethod to the cutoffMethod:
130     if (simParams_->haveCutoffMethod()){
131     string myMethod = simParams_->getCutoffMethod();
132     toUpper(myMethod);
133     map<string, ElectrostaticSummationMethod>::iterator i;
134     i = summationMap_.find(myMethod);
135     if ( i != summationMap_.end() ) {
136     summationMethod_ = (*i).second;
137     }
138     }
139     }
140    
141     if (summationMethod_ == esm_REACTION_FIELD) {
142     if (!simParams_->haveDielectric()) {
143     // throw warning
144     sprintf( painCave.errMsg,
145     "SimInfo warning: dielectric was not specified in the input file\n\tfor the reaction field correction method.\n"
146     "\tA default value of %f will be used for the dielectric.\n", dielectric_);
147     painCave.isFatal = 0;
148     painCave.severity = OPENMD_INFO;
149     simError();
150     } else {
151     dielectric_ = simParams_->getDielectric();
152     }
153     haveDielectric_ = true;
154     }
155    
156     if (simParams_->haveElectrostaticScreeningMethod()) {
157     string myScreen = simParams_->getElectrostaticScreeningMethod();
158     toUpper(myScreen);
159     map<string, ElectrostaticScreeningMethod>::iterator i;
160     i = screeningMap_.find(myScreen);
161     if ( i != screeningMap_.end()) {
162     screeningMethod_ = (*i).second;
163     } else {
164     sprintf( painCave.errMsg,
165     "SimInfo error: Unknown electrostaticScreeningMethod.\n"
166     "\t(Input file specified %s .)\n"
167     "\telectrostaticScreeningMethod must be one of: \"undamped\"\n"
168     "or \"damped\".\n", myScreen.c_str() );
169     painCave.isFatal = 1;
170     simError();
171     }
172     }
173    
174     // check to make sure a cutoff value has been set:
175     if (!haveCutoffRadius_) {
176     sprintf( painCave.errMsg, "Electrostatic::initialize has no Default "
177     "Cutoff value!\n");
178     painCave.severity = OPENMD_ERROR;
179     painCave.isFatal = 1;
180     simError();
181     }
182    
183     if (screeningMethod_ == DAMPED) {
184     if (!simParams_->haveDampingAlpha()) {
185     // first set a cutoff dependent alpha value
186     // we assume alpha depends linearly with rcut from 0 to 20.5 ang
187     dampingAlpha_ = 0.425 - cutoffRadius_* 0.02;
188     if (dampingAlpha_ < 0.0) dampingAlpha_ = 0.0;
189    
190     // throw warning
191     sprintf( painCave.errMsg,
192     "Electrostatic::initialize: dampingAlpha was not specified in the input file.\n"
193     "\tA default value of %f (1/ang) will be used for the cutoff of\n\t%f (ang).\n",
194     dampingAlpha_, cutoffRadius_);
195     painCave.severity = OPENMD_INFO;
196     painCave.isFatal = 0;
197     simError();
198     } else {
199     dampingAlpha_ = simParams_->getDampingAlpha();
200     }
201     haveDampingAlpha_ = true;
202     }
203    
204 gezelter 1502 // find all of the Electrostatic atom Types:
205     ForceField::AtomTypeContainer* atomTypes = forceField_->getAtomTypes();
206     ForceField::AtomTypeContainer::MapTypeIterator i;
207     AtomType* at;
208 gezelter 1528
209 gezelter 1502 for (at = atomTypes->beginType(i); at != NULL;
210     at = atomTypes->nextType(i)) {
211    
212     if (at->isElectrostatic())
213     addType(at);
214     }
215    
216    
217 gezelter 1528 cutoffRadius2_ = cutoffRadius_ * cutoffRadius_;
218     rcuti_ = 1.0 / cutoffRadius_;
219 gezelter 1502 rcuti2_ = rcuti_ * rcuti_;
220     rcuti3_ = rcuti2_ * rcuti_;
221     rcuti4_ = rcuti2_ * rcuti2_;
222    
223     if (screeningMethod_ == DAMPED) {
224 gezelter 1528
225 gezelter 1502 alpha2_ = dampingAlpha_ * dampingAlpha_;
226     alpha4_ = alpha2_ * alpha2_;
227     alpha6_ = alpha4_ * alpha2_;
228     alpha8_ = alpha4_ * alpha4_;
229    
230 gezelter 1528 constEXP_ = exp(-alpha2_ * cutoffRadius2_);
231 gezelter 1502 invRootPi_ = 0.56418958354775628695;
232     alphaPi_ = 2.0 * dampingAlpha_ * invRootPi_;
233    
234 gezelter 1528 c1c_ = erfc(dampingAlpha_ * cutoffRadius_) * rcuti_;
235 gezelter 1502 c2c_ = alphaPi_ * constEXP_ * rcuti_ + c1c_ * rcuti_;
236     c3c_ = 2.0 * alphaPi_ * alpha2_ + 3.0 * c2c_ * rcuti_;
237     c4c_ = 4.0 * alphaPi_ * alpha4_ + 5.0 * c3c_ * rcuti2_;
238     c5c_ = 8.0 * alphaPi_ * alpha6_ + 7.0 * c4c_ * rcuti2_;
239     c6c_ = 16.0 * alphaPi_ * alpha8_ + 9.0 * c5c_ * rcuti2_;
240     } else {
241     c1c_ = rcuti_;
242     c2c_ = c1c_ * rcuti_;
243     c3c_ = 3.0 * c2c_ * rcuti_;
244     c4c_ = 5.0 * c3c_ * rcuti2_;
245     c5c_ = 7.0 * c4c_ * rcuti2_;
246     c6c_ = 9.0 * c5c_ * rcuti2_;
247     }
248    
249 gezelter 1528 if (summationMethod_ == esm_REACTION_FIELD) {
250     preRF_ = (dielectric_ - 1.0) /
251     ((2.0 * dielectric_ + 1.0) * cutoffRadius2_ * cutoffRadius_);
252     preRF2_ = 2.0 * preRF_;
253 gezelter 1502 }
254 gezelter 1528
255 gezelter 1613 // Add a 2 angstrom safety window to deal with cutoffGroups that
256     // have charged atoms longer than the cutoffRadius away from each
257     // other. Splining may not be the best choice here. Direct calls
258     // to erfc might be preferrable.
259    
260     RealType dx = (cutoffRadius_ + 2.0) / RealType(np_ - 1);
261 gezelter 1502 RealType rval;
262     vector<RealType> rvals;
263     vector<RealType> yvals;
264     for (int i = 0; i < np_; i++) {
265     rval = RealType(i) * dx;
266     rvals.push_back(rval);
267     yvals.push_back(erfc(dampingAlpha_ * rval));
268     }
269     erfcSpline_ = new CubicSpline();
270     erfcSpline_->addPoints(rvals, yvals);
271     haveElectroSpline_ = true;
272    
273     initialized_ = true;
274     }
275    
276     void Electrostatic::addType(AtomType* atomType){
277    
278     ElectrostaticAtomData electrostaticAtomData;
279     electrostaticAtomData.is_Charge = false;
280     electrostaticAtomData.is_Dipole = false;
281     electrostaticAtomData.is_SplitDipole = false;
282     electrostaticAtomData.is_Quadrupole = false;
283    
284 gezelter 1710 FixedChargeAdapter fca = FixedChargeAdapter(atomType);
285 gezelter 1502
286 gezelter 1710 if (fca.isFixedCharge()) {
287 gezelter 1502 electrostaticAtomData.is_Charge = true;
288 gezelter 1710 electrostaticAtomData.charge = fca.getCharge();
289 gezelter 1502 }
290    
291 gezelter 1710 MultipoleAdapter ma = MultipoleAdapter(atomType);
292     if (ma.isMultipole()) {
293     if (ma.isDipole()) {
294 gezelter 1502 electrostaticAtomData.is_Dipole = true;
295 gezelter 1710 electrostaticAtomData.dipole_moment = ma.getDipoleMoment();
296 gezelter 1502 }
297 gezelter 1710 if (ma.isSplitDipole()) {
298 gezelter 1502 electrostaticAtomData.is_SplitDipole = true;
299 gezelter 1710 electrostaticAtomData.split_dipole_distance = ma.getSplitDipoleDistance();
300 gezelter 1502 }
301 gezelter 1710 if (ma.isQuadrupole()) {
302 gezelter 1505 // Quadrupoles in OpenMD are set as the diagonal elements
303     // of the diagonalized traceless quadrupole moment tensor.
304     // The column vectors of the unitary matrix that diagonalizes
305     // the quadrupole moment tensor become the eFrame (or the
306     // electrostatic version of the body-fixed frame.
307 gezelter 1502 electrostaticAtomData.is_Quadrupole = true;
308 gezelter 1710 electrostaticAtomData.quadrupole_moments = ma.getQuadrupoleMoments();
309 gezelter 1502 }
310     }
311    
312    
313     pair<map<int,AtomType*>::iterator,bool> ret;
314 gezelter 1710 ret = ElectrostaticList.insert( pair<int,AtomType*>(atomType->getIdent(),
315     atomType) );
316 gezelter 1502 if (ret.second == false) {
317     sprintf( painCave.errMsg,
318     "Electrostatic already had a previous entry with ident %d\n",
319 gezelter 1710 atomType->getIdent() );
320 gezelter 1502 painCave.severity = OPENMD_INFO;
321     painCave.isFatal = 0;
322     simError();
323     }
324    
325     ElectrostaticMap[atomType] = electrostaticAtomData;
326     return;
327     }
328    
329 gezelter 1584 void Electrostatic::setCutoffRadius( RealType rCut ) {
330     cutoffRadius_ = rCut;
331 gezelter 1528 rrf_ = cutoffRadius_;
332     haveCutoffRadius_ = true;
333 gezelter 1502 }
334 gezelter 1584
335     void Electrostatic::setSwitchingRadius( RealType rSwitch ) {
336     rt_ = rSwitch;
337     }
338 gezelter 1502 void Electrostatic::setElectrostaticSummationMethod( ElectrostaticSummationMethod esm ) {
339     summationMethod_ = esm;
340     }
341     void Electrostatic::setElectrostaticScreeningMethod( ElectrostaticScreeningMethod sm ) {
342     screeningMethod_ = sm;
343     }
344     void Electrostatic::setDampingAlpha( RealType alpha ) {
345     dampingAlpha_ = alpha;
346     haveDampingAlpha_ = true;
347     }
348     void Electrostatic::setReactionFieldDielectric( RealType dielectric ){
349     dielectric_ = dielectric;
350     haveDielectric_ = true;
351     }
352    
353 gezelter 1536 void Electrostatic::calcForce(InteractionData &idat) {
354 gezelter 1502
355     // utility variables. Should clean these up and use the Vector3d and
356     // Mat3x3d to replace as many as we can in future versions:
357    
358     RealType q_i, q_j, mu_i, mu_j, d_i, d_j;
359     RealType qxx_i, qyy_i, qzz_i;
360     RealType qxx_j, qyy_j, qzz_j;
361     RealType cx_i, cy_i, cz_i;
362     RealType cx_j, cy_j, cz_j;
363     RealType cx2, cy2, cz2;
364     RealType ct_i, ct_j, ct_ij, a1;
365     RealType riji, ri, ri2, ri3, ri4;
366     RealType pref, vterm, epot, dudr;
367 gezelter 1587 RealType vpair(0.0);
368 gezelter 1502 RealType scale, sc2;
369     RealType pot_term, preVal, rfVal;
370     RealType c2ri, c3ri, c4rij, cti3, ctj3, ctidotj;
371     RealType preSw, preSwSc;
372     RealType c1, c2, c3, c4;
373 gezelter 1587 RealType erfcVal(1.0), derfcVal(0.0);
374 gezelter 1502 RealType BigR;
375 gezelter 1668 RealType two(2.0), three(3.0);
376 gezelter 1502
377     Vector3d Q_i, Q_j;
378     Vector3d ux_i, uy_i, uz_i;
379     Vector3d ux_j, uy_j, uz_j;
380     Vector3d dudux_i, duduy_i, duduz_i;
381     Vector3d dudux_j, duduy_j, duduz_j;
382     Vector3d rhatdot2, rhatc4;
383     Vector3d dVdr;
384    
385 gezelter 1587 // variables for indirect (reaction field) interactions for excluded pairs:
386     RealType indirect_Pot(0.0);
387     RealType indirect_vpair(0.0);
388     Vector3d indirect_dVdr(V3Zero);
389     Vector3d indirect_duduz_i(V3Zero), indirect_duduz_j(V3Zero);
390    
391 gezelter 1502 pair<RealType, RealType> res;
392    
393     if (!initialized_) initialize();
394    
395 gezelter 1571 ElectrostaticAtomData data1 = ElectrostaticMap[idat.atypes.first];
396     ElectrostaticAtomData data2 = ElectrostaticMap[idat.atypes.second];
397 gezelter 1502
398     // some variables we'll need independent of electrostatic type:
399    
400 gezelter 1554 riji = 1.0 / *(idat.rij) ;
401     Vector3d rhat = *(idat.d) * riji;
402 gezelter 1502
403     // logicals
404    
405     bool i_is_Charge = data1.is_Charge;
406     bool i_is_Dipole = data1.is_Dipole;
407     bool i_is_SplitDipole = data1.is_SplitDipole;
408     bool i_is_Quadrupole = data1.is_Quadrupole;
409    
410     bool j_is_Charge = data2.is_Charge;
411     bool j_is_Dipole = data2.is_Dipole;
412     bool j_is_SplitDipole = data2.is_SplitDipole;
413     bool j_is_Quadrupole = data2.is_Quadrupole;
414    
415 gezelter 1587 if (i_is_Charge) {
416 gezelter 1502 q_i = data1.charge;
417 gezelter 1587 if (idat.excluded) {
418     *(idat.skippedCharge2) += q_i;
419     }
420     }
421 gezelter 1502
422     if (i_is_Dipole) {
423     mu_i = data1.dipole_moment;
424 gezelter 1554 uz_i = idat.eFrame1->getColumn(2);
425 gezelter 1502
426     ct_i = dot(uz_i, rhat);
427    
428     if (i_is_SplitDipole)
429     d_i = data1.split_dipole_distance;
430    
431     duduz_i = V3Zero;
432     }
433    
434     if (i_is_Quadrupole) {
435     Q_i = data1.quadrupole_moments;
436     qxx_i = Q_i.x();
437     qyy_i = Q_i.y();
438     qzz_i = Q_i.z();
439    
440 gezelter 1554 ux_i = idat.eFrame1->getColumn(0);
441     uy_i = idat.eFrame1->getColumn(1);
442     uz_i = idat.eFrame1->getColumn(2);
443 gezelter 1502
444     cx_i = dot(ux_i, rhat);
445     cy_i = dot(uy_i, rhat);
446     cz_i = dot(uz_i, rhat);
447    
448     dudux_i = V3Zero;
449     duduy_i = V3Zero;
450     duduz_i = V3Zero;
451     }
452    
453 gezelter 1587 if (j_is_Charge) {
454 gezelter 1502 q_j = data2.charge;
455 gezelter 1587 if (idat.excluded) {
456     *(idat.skippedCharge1) += q_j;
457     }
458     }
459 gezelter 1502
460 gezelter 1587
461 gezelter 1502 if (j_is_Dipole) {
462     mu_j = data2.dipole_moment;
463 gezelter 1554 uz_j = idat.eFrame2->getColumn(2);
464 gezelter 1502
465     ct_j = dot(uz_j, rhat);
466    
467     if (j_is_SplitDipole)
468     d_j = data2.split_dipole_distance;
469    
470     duduz_j = V3Zero;
471     }
472    
473     if (j_is_Quadrupole) {
474     Q_j = data2.quadrupole_moments;
475     qxx_j = Q_j.x();
476     qyy_j = Q_j.y();
477     qzz_j = Q_j.z();
478    
479 gezelter 1554 ux_j = idat.eFrame2->getColumn(0);
480     uy_j = idat.eFrame2->getColumn(1);
481     uz_j = idat.eFrame2->getColumn(2);
482 gezelter 1502
483     cx_j = dot(ux_j, rhat);
484     cy_j = dot(uy_j, rhat);
485     cz_j = dot(uz_j, rhat);
486    
487     dudux_j = V3Zero;
488     duduy_j = V3Zero;
489     duduz_j = V3Zero;
490     }
491    
492     epot = 0.0;
493     dVdr = V3Zero;
494    
495     if (i_is_Charge) {
496    
497     if (j_is_Charge) {
498     if (screeningMethod_ == DAMPED) {
499     // assemble the damping variables
500 gezelter 1616 //res = erfcSpline_->getValueAndDerivativeAt( *(idat.rij) );
501     //erfcVal = res.first;
502     //derfcVal = res.second;
503    
504     erfcVal = erfc(dampingAlpha_ * *(idat.rij));
505     derfcVal = - alphaPi_ * exp(-alpha2_ * *(idat.r2));
506    
507 gezelter 1502 c1 = erfcVal * riji;
508     c2 = (-derfcVal + c1) * riji;
509     } else {
510     c1 = riji;
511     c2 = c1 * riji;
512     }
513    
514 gezelter 1554 preVal = *(idat.electroMult) * pre11_ * q_i * q_j;
515 gezelter 1502
516 gezelter 1528 if (summationMethod_ == esm_SHIFTED_POTENTIAL) {
517 gezelter 1502 vterm = preVal * (c1 - c1c_);
518 gezelter 1554 dudr = - *(idat.sw) * preVal * c2;
519 gezelter 1502
520 gezelter 1528 } else if (summationMethod_ == esm_SHIFTED_FORCE) {
521 gezelter 1554 vterm = preVal * ( c1 - c1c_ + c2c_*( *(idat.rij) - cutoffRadius_) );
522     dudr = *(idat.sw) * preVal * (c2c_ - c2);
523 gezelter 1502
524 gezelter 1528 } else if (summationMethod_ == esm_REACTION_FIELD) {
525 gezelter 1587 rfVal = preRF_ * *(idat.rij) * *(idat.rij);
526    
527 gezelter 1502 vterm = preVal * ( riji + rfVal );
528 gezelter 1554 dudr = *(idat.sw) * preVal * ( 2.0 * rfVal - riji ) * riji;
529 gezelter 1587
530     // if this is an excluded pair, there are still indirect
531     // interactions via the reaction field we must worry about:
532 gezelter 1502
533 gezelter 1587 if (idat.excluded) {
534     indirect_vpair += preVal * rfVal;
535     indirect_Pot += *(idat.sw) * preVal * rfVal;
536 gezelter 1668 indirect_dVdr += *(idat.sw) * preVal * two * rfVal * riji * rhat;
537 gezelter 1587 }
538    
539 gezelter 1502 } else {
540    
541 gezelter 1587 vterm = preVal * riji * erfcVal;
542 gezelter 1554 dudr = - *(idat.sw) * preVal * c2;
543 gezelter 1502
544     }
545 gezelter 1587
546     vpair += vterm;
547 gezelter 1554 epot += *(idat.sw) * vterm;
548 gezelter 1587 dVdr += dudr * rhat;
549 gezelter 1502 }
550    
551     if (j_is_Dipole) {
552     // pref is used by all the possible methods
553 gezelter 1554 pref = *(idat.electroMult) * pre12_ * q_i * mu_j;
554     preSw = *(idat.sw) * pref;
555 gezelter 1502
556 gezelter 1528 if (summationMethod_ == esm_REACTION_FIELD) {
557 gezelter 1502 ri2 = riji * riji;
558     ri3 = ri2 * riji;
559    
560 gezelter 1554 vterm = - pref * ct_j * ( ri2 - preRF2_ * *(idat.rij) );
561 gezelter 1587 vpair += vterm;
562 gezelter 1554 epot += *(idat.sw) * vterm;
563 gezelter 1502
564 gezelter 1668 dVdr += -preSw * (ri3 * (uz_j - three * ct_j * rhat) - preRF2_*uz_j);
565 gezelter 1554 duduz_j += -preSw * rhat * (ri2 - preRF2_ * *(idat.rij) );
566 gezelter 1502
567 gezelter 1587 // Even if we excluded this pair from direct interactions,
568     // we still have the reaction-field-mediated charge-dipole
569     // interaction:
570    
571     if (idat.excluded) {
572     indirect_vpair += pref * ct_j * preRF2_ * *(idat.rij);
573     indirect_Pot += preSw * ct_j * preRF2_ * *(idat.rij);
574     indirect_dVdr += preSw * preRF2_ * uz_j;
575     indirect_duduz_j += preSw * rhat * preRF2_ * *(idat.rij);
576     }
577    
578 gezelter 1502 } else {
579     // determine the inverse r used if we have split dipoles
580     if (j_is_SplitDipole) {
581 gezelter 1554 BigR = sqrt( *(idat.r2) + 0.25 * d_j * d_j);
582 gezelter 1502 ri = 1.0 / BigR;
583 gezelter 1554 scale = *(idat.rij) * ri;
584 gezelter 1502 } else {
585     ri = riji;
586     scale = 1.0;
587     }
588    
589     sc2 = scale * scale;
590    
591     if (screeningMethod_ == DAMPED) {
592     // assemble the damping variables
593 gezelter 1616 //res = erfcSpline_->getValueAndDerivativeAt( *(idat.rij) );
594     //erfcVal = res.first;
595     //derfcVal = res.second;
596     erfcVal = erfc(dampingAlpha_ * *(idat.rij));
597     derfcVal = - alphaPi_ * exp(-alpha2_ * *(idat.r2));
598 gezelter 1502 c1 = erfcVal * ri;
599     c2 = (-derfcVal + c1) * ri;
600     c3 = -2.0 * derfcVal * alpha2_ + 3.0 * c2 * ri;
601     } else {
602     c1 = ri;
603     c2 = c1 * ri;
604     c3 = 3.0 * c2 * ri;
605     }
606    
607     c2ri = c2 * ri;
608    
609     // calculate the potential
610     pot_term = scale * c2;
611     vterm = -pref * ct_j * pot_term;
612 gezelter 1587 vpair += vterm;
613 gezelter 1554 epot += *(idat.sw) * vterm;
614 gezelter 1502
615     // calculate derivatives for forces and torques
616    
617     dVdr += -preSw * (uz_j * c2ri - ct_j * rhat * sc2 * c3);
618     duduz_j += -preSw * pot_term * rhat;
619    
620     }
621     }
622    
623     if (j_is_Quadrupole) {
624     // first precalculate some necessary variables
625     cx2 = cx_j * cx_j;
626     cy2 = cy_j * cy_j;
627     cz2 = cz_j * cz_j;
628 gezelter 1554 pref = *(idat.electroMult) * pre14_ * q_i * one_third_;
629 gezelter 1502
630     if (screeningMethod_ == DAMPED) {
631     // assemble the damping variables
632 gezelter 1616 //res = erfcSpline_->getValueAndDerivativeAt( *(idat.rij) );
633     //erfcVal = res.first;
634     //derfcVal = res.second;
635     erfcVal = erfc(dampingAlpha_ * *(idat.rij));
636     derfcVal = - alphaPi_ * exp(-alpha2_ * *(idat.r2));
637 gezelter 1502 c1 = erfcVal * riji;
638     c2 = (-derfcVal + c1) * riji;
639     c3 = -2.0 * derfcVal * alpha2_ + 3.0 * c2 * riji;
640     c4 = -4.0 * derfcVal * alpha4_ + 5.0 * c3 * riji * riji;
641     } else {
642     c1 = riji;
643     c2 = c1 * riji;
644     c3 = 3.0 * c2 * riji;
645     c4 = 5.0 * c3 * riji * riji;
646     }
647    
648     // precompute variables for convenience
649 gezelter 1554 preSw = *(idat.sw) * pref;
650 gezelter 1502 c2ri = c2 * riji;
651     c3ri = c3 * riji;
652 gezelter 1554 c4rij = c4 * *(idat.rij) ;
653 gezelter 1668 rhatdot2 = two * rhat * c3;
654 gezelter 1502 rhatc4 = rhat * c4rij;
655    
656     // calculate the potential
657     pot_term = ( qxx_j * (cx2*c3 - c2ri) +
658     qyy_j * (cy2*c3 - c2ri) +
659     qzz_j * (cz2*c3 - c2ri) );
660     vterm = pref * pot_term;
661 gezelter 1587 vpair += vterm;
662 gezelter 1554 epot += *(idat.sw) * vterm;
663 gezelter 1502
664     // calculate derivatives for the forces and torques
665    
666 gezelter 1668 dVdr += -preSw * ( qxx_j* (cx2*rhatc4 - (two*cx_j*ux_j + rhat)*c3ri) +
667     qyy_j* (cy2*rhatc4 - (two*cy_j*uy_j + rhat)*c3ri) +
668     qzz_j* (cz2*rhatc4 - (two*cz_j*uz_j + rhat)*c3ri));
669 gezelter 1502
670     dudux_j += preSw * qxx_j * cx_j * rhatdot2;
671     duduy_j += preSw * qyy_j * cy_j * rhatdot2;
672     duduz_j += preSw * qzz_j * cz_j * rhatdot2;
673     }
674     }
675    
676     if (i_is_Dipole) {
677    
678     if (j_is_Charge) {
679     // variables used by all the methods
680 gezelter 1554 pref = *(idat.electroMult) * pre12_ * q_j * mu_i;
681     preSw = *(idat.sw) * pref;
682 gezelter 1502
683 gezelter 1528 if (summationMethod_ == esm_REACTION_FIELD) {
684 gezelter 1502
685     ri2 = riji * riji;
686     ri3 = ri2 * riji;
687    
688 gezelter 1554 vterm = pref * ct_i * ( ri2 - preRF2_ * *(idat.rij) );
689 gezelter 1587 vpair += vterm;
690 gezelter 1554 epot += *(idat.sw) * vterm;
691 gezelter 1502
692 gezelter 1668 dVdr += preSw * (ri3 * (uz_i - three * ct_i * rhat) - preRF2_ * uz_i);
693 gezelter 1502
694 gezelter 1554 duduz_i += preSw * rhat * (ri2 - preRF2_ * *(idat.rij) );
695 gezelter 1587
696     // Even if we excluded this pair from direct interactions,
697     // we still have the reaction-field-mediated charge-dipole
698     // interaction:
699    
700     if (idat.excluded) {
701     indirect_vpair += -pref * ct_i * preRF2_ * *(idat.rij);
702     indirect_Pot += -preSw * ct_i * preRF2_ * *(idat.rij);
703     indirect_dVdr += -preSw * preRF2_ * uz_i;
704     indirect_duduz_i += -preSw * rhat * preRF2_ * *(idat.rij);
705     }
706 gezelter 1502
707     } else {
708    
709     // determine inverse r if we are using split dipoles
710     if (i_is_SplitDipole) {
711 gezelter 1554 BigR = sqrt( *(idat.r2) + 0.25 * d_i * d_i);
712 gezelter 1502 ri = 1.0 / BigR;
713 gezelter 1554 scale = *(idat.rij) * ri;
714 gezelter 1502 } else {
715     ri = riji;
716     scale = 1.0;
717     }
718    
719     sc2 = scale * scale;
720    
721     if (screeningMethod_ == DAMPED) {
722     // assemble the damping variables
723 gezelter 1616 //res = erfcSpline_->getValueAndDerivativeAt( *(idat.rij) );
724     //erfcVal = res.first;
725     //derfcVal = res.second;
726     erfcVal = erfc(dampingAlpha_ * *(idat.rij));
727     derfcVal = - alphaPi_ * exp(-alpha2_ * *(idat.r2));
728 gezelter 1502 c1 = erfcVal * ri;
729     c2 = (-derfcVal + c1) * ri;
730     c3 = -2.0 * derfcVal * alpha2_ + 3.0 * c2 * ri;
731     } else {
732     c1 = ri;
733     c2 = c1 * ri;
734     c3 = 3.0 * c2 * ri;
735     }
736    
737     c2ri = c2 * ri;
738    
739     // calculate the potential
740     pot_term = c2 * scale;
741     vterm = pref * ct_i * pot_term;
742 gezelter 1587 vpair += vterm;
743 gezelter 1554 epot += *(idat.sw) * vterm;
744 gezelter 1502
745     // calculate derivatives for the forces and torques
746     dVdr += preSw * (uz_i * c2ri - ct_i * rhat * sc2 * c3);
747     duduz_i += preSw * pot_term * rhat;
748     }
749     }
750    
751     if (j_is_Dipole) {
752     // variables used by all methods
753     ct_ij = dot(uz_i, uz_j);
754    
755 gezelter 1554 pref = *(idat.electroMult) * pre22_ * mu_i * mu_j;
756     preSw = *(idat.sw) * pref;
757 gezelter 1502
758 gezelter 1528 if (summationMethod_ == esm_REACTION_FIELD) {
759 gezelter 1502 ri2 = riji * riji;
760     ri3 = ri2 * riji;
761     ri4 = ri2 * ri2;
762    
763     vterm = pref * ( ri3 * (ct_ij - 3.0 * ct_i * ct_j) -
764     preRF2_ * ct_ij );
765 gezelter 1587 vpair += vterm;
766 gezelter 1554 epot += *(idat.sw) * vterm;
767 gezelter 1502
768     a1 = 5.0 * ct_i * ct_j - ct_ij;
769    
770 gezelter 1668 dVdr += preSw * three * ri4 * (a1 * rhat - ct_i * uz_j - ct_j * uz_i);
771 gezelter 1502
772 gezelter 1668 duduz_i += preSw * (ri3 * (uz_j - three * ct_j * rhat) - preRF2_*uz_j);
773     duduz_j += preSw * (ri3 * (uz_i - three * ct_i * rhat) - preRF2_*uz_i);
774 gezelter 1502
775 gezelter 1587 if (idat.excluded) {
776     indirect_vpair += - pref * preRF2_ * ct_ij;
777     indirect_Pot += - preSw * preRF2_ * ct_ij;
778     indirect_duduz_i += -preSw * preRF2_ * uz_j;
779     indirect_duduz_j += -preSw * preRF2_ * uz_i;
780     }
781    
782 gezelter 1502 } else {
783    
784     if (i_is_SplitDipole) {
785     if (j_is_SplitDipole) {
786 gezelter 1554 BigR = sqrt( *(idat.r2) + 0.25 * d_i * d_i + 0.25 * d_j * d_j);
787 gezelter 1502 } else {
788 gezelter 1554 BigR = sqrt( *(idat.r2) + 0.25 * d_i * d_i);
789 gezelter 1502 }
790     ri = 1.0 / BigR;
791 gezelter 1554 scale = *(idat.rij) * ri;
792 gezelter 1502 } else {
793     if (j_is_SplitDipole) {
794 gezelter 1554 BigR = sqrt( *(idat.r2) + 0.25 * d_j * d_j);
795 gezelter 1502 ri = 1.0 / BigR;
796 gezelter 1554 scale = *(idat.rij) * ri;
797 gezelter 1502 } else {
798     ri = riji;
799     scale = 1.0;
800     }
801     }
802     if (screeningMethod_ == DAMPED) {
803     // assemble damping variables
804 gezelter 1616 //res = erfcSpline_->getValueAndDerivativeAt( *(idat.rij) );
805     //erfcVal = res.first;
806     //derfcVal = res.second;
807     erfcVal = erfc(dampingAlpha_ * *(idat.rij));
808     derfcVal = - alphaPi_ * exp(-alpha2_ * *(idat.r2));
809 gezelter 1502 c1 = erfcVal * ri;
810     c2 = (-derfcVal + c1) * ri;
811     c3 = -2.0 * derfcVal * alpha2_ + 3.0 * c2 * ri;
812     c4 = -4.0 * derfcVal * alpha4_ + 5.0 * c3 * ri * ri;
813     } else {
814     c1 = ri;
815     c2 = c1 * ri;
816     c3 = 3.0 * c2 * ri;
817     c4 = 5.0 * c3 * ri * ri;
818     }
819    
820     // precompute variables for convenience
821     sc2 = scale * scale;
822     cti3 = ct_i * sc2 * c3;
823     ctj3 = ct_j * sc2 * c3;
824     ctidotj = ct_i * ct_j * sc2;
825     preSwSc = preSw * scale;
826     c2ri = c2 * ri;
827     c3ri = c3 * ri;
828 gezelter 1554 c4rij = c4 * *(idat.rij) ;
829 gezelter 1502
830     // calculate the potential
831     pot_term = (ct_ij * c2ri - ctidotj * c3);
832     vterm = pref * pot_term;
833 gezelter 1587 vpair += vterm;
834 gezelter 1554 epot += *(idat.sw) * vterm;
835 gezelter 1502
836     // calculate derivatives for the forces and torques
837     dVdr += preSwSc * ( ctidotj * rhat * c4rij -
838     (ct_i*uz_j + ct_j*uz_i + ct_ij*rhat) * c3ri);
839    
840     duduz_i += preSw * (uz_j * c2ri - ctj3 * rhat);
841     duduz_j += preSw * (uz_i * c2ri - cti3 * rhat);
842     }
843     }
844     }
845    
846     if (i_is_Quadrupole) {
847     if (j_is_Charge) {
848     // precompute some necessary variables
849     cx2 = cx_i * cx_i;
850     cy2 = cy_i * cy_i;
851     cz2 = cz_i * cz_i;
852    
853 gezelter 1554 pref = *(idat.electroMult) * pre14_ * q_j * one_third_;
854 gezelter 1502
855     if (screeningMethod_ == DAMPED) {
856     // assemble the damping variables
857 gezelter 1616 //res = erfcSpline_->getValueAndDerivativeAt( *(idat.rij) );
858     //erfcVal = res.first;
859     //derfcVal = res.second;
860     erfcVal = erfc(dampingAlpha_ * *(idat.rij));
861     derfcVal = - alphaPi_ * exp(-alpha2_ * *(idat.r2));
862 gezelter 1502 c1 = erfcVal * riji;
863     c2 = (-derfcVal + c1) * riji;
864     c3 = -2.0 * derfcVal * alpha2_ + 3.0 * c2 * riji;
865     c4 = -4.0 * derfcVal * alpha4_ + 5.0 * c3 * riji * riji;
866     } else {
867     c1 = riji;
868     c2 = c1 * riji;
869     c3 = 3.0 * c2 * riji;
870     c4 = 5.0 * c3 * riji * riji;
871     }
872    
873     // precompute some variables for convenience
874 gezelter 1554 preSw = *(idat.sw) * pref;
875 gezelter 1502 c2ri = c2 * riji;
876     c3ri = c3 * riji;
877 gezelter 1554 c4rij = c4 * *(idat.rij) ;
878 gezelter 1668 rhatdot2 = two * rhat * c3;
879 gezelter 1502 rhatc4 = rhat * c4rij;
880    
881     // calculate the potential
882     pot_term = ( qxx_i * (cx2 * c3 - c2ri) +
883     qyy_i * (cy2 * c3 - c2ri) +
884     qzz_i * (cz2 * c3 - c2ri) );
885    
886     vterm = pref * pot_term;
887 gezelter 1587 vpair += vterm;
888 gezelter 1554 epot += *(idat.sw) * vterm;
889 gezelter 1502
890     // calculate the derivatives for the forces and torques
891    
892 gezelter 1668 dVdr += -preSw * (qxx_i* (cx2*rhatc4 - (two*cx_i*ux_i + rhat)*c3ri) +
893     qyy_i* (cy2*rhatc4 - (two*cy_i*uy_i + rhat)*c3ri) +
894     qzz_i* (cz2*rhatc4 - (two*cz_i*uz_i + rhat)*c3ri));
895 gezelter 1502
896     dudux_i += preSw * qxx_i * cx_i * rhatdot2;
897     duduy_i += preSw * qyy_i * cy_i * rhatdot2;
898     duduz_i += preSw * qzz_i * cz_i * rhatdot2;
899     }
900     }
901    
902    
903 gezelter 1587 if (!idat.excluded) {
904     *(idat.vpair) += vpair;
905     (*(idat.pot))[ELECTROSTATIC_FAMILY] += epot;
906     *(idat.f1) += dVdr;
907    
908     if (i_is_Dipole || i_is_Quadrupole)
909     *(idat.t1) -= cross(uz_i, duduz_i);
910     if (i_is_Quadrupole) {
911     *(idat.t1) -= cross(ux_i, dudux_i);
912     *(idat.t1) -= cross(uy_i, duduy_i);
913     }
914    
915     if (j_is_Dipole || j_is_Quadrupole)
916     *(idat.t2) -= cross(uz_j, duduz_j);
917     if (j_is_Quadrupole) {
918     *(idat.t2) -= cross(uz_j, dudux_j);
919     *(idat.t2) -= cross(uz_j, duduy_j);
920     }
921    
922     } else {
923    
924     // only accumulate the forces and torques resulting from the
925     // indirect reaction field terms.
926 gezelter 1616
927 gezelter 1587 *(idat.vpair) += indirect_vpair;
928     (*(idat.pot))[ELECTROSTATIC_FAMILY] += indirect_Pot;
929     *(idat.f1) += indirect_dVdr;
930    
931     if (i_is_Dipole)
932     *(idat.t1) -= cross(uz_i, indirect_duduz_i);
933     if (j_is_Dipole)
934     *(idat.t2) -= cross(uz_j, indirect_duduz_j);
935 gezelter 1502 }
936    
937 gezelter 1587
938 gezelter 1502 return;
939     }
940    
941 gezelter 1545 void Electrostatic::calcSelfCorrection(SelfData &sdat) {
942 gezelter 1502 RealType mu1, preVal, chg1, self;
943    
944     if (!initialized_) initialize();
945 gezelter 1586
946 gezelter 1545 ElectrostaticAtomData data = ElectrostaticMap[sdat.atype];
947 gezelter 1502
948     // logicals
949     bool i_is_Charge = data.is_Charge;
950     bool i_is_Dipole = data.is_Dipole;
951    
952 gezelter 1528 if (summationMethod_ == esm_REACTION_FIELD) {
953 gezelter 1502 if (i_is_Dipole) {
954     mu1 = data.dipole_moment;
955     preVal = pre22_ * preRF2_ * mu1 * mu1;
956 gezelter 1586 (*(sdat.pot))[ELECTROSTATIC_FAMILY] -= 0.5 * preVal;
957 gezelter 1502
958     // The self-correction term adds into the reaction field vector
959 gezelter 1554 Vector3d uz_i = sdat.eFrame->getColumn(2);
960 gezelter 1502 Vector3d ei = preVal * uz_i;
961    
962     // This looks very wrong. A vector crossed with itself is zero.
963 gezelter 1554 *(sdat.t) -= cross(uz_i, ei);
964 gezelter 1502 }
965 gezelter 1528 } else if (summationMethod_ == esm_SHIFTED_FORCE || summationMethod_ == esm_SHIFTED_POTENTIAL) {
966 gezelter 1502 if (i_is_Charge) {
967     chg1 = data.charge;
968     if (screeningMethod_ == DAMPED) {
969 gezelter 1554 self = - 0.5 * (c1c_ + alphaPi_) * chg1 * (chg1 + *(sdat.skippedCharge)) * pre11_;
970 gezelter 1502 } else {
971 gezelter 1554 self = - 0.5 * rcuti_ * chg1 * (chg1 + *(sdat.skippedCharge)) * pre11_;
972 gezelter 1502 }
973 gezelter 1586 (*(sdat.pot))[ELECTROSTATIC_FAMILY] += self;
974 gezelter 1502 }
975     }
976     }
977 gezelter 1505
978 gezelter 1545 RealType Electrostatic::getSuggestedCutoffRadius(pair<AtomType*, AtomType*> atypes) {
979 gezelter 1505 // This seems to work moderately well as a default. There's no
980     // inherent scale for 1/r interactions that we can standardize.
981     // 12 angstroms seems to be a reasonably good guess for most
982     // cases.
983     return 12.0;
984     }
985 gezelter 1502 }

Properties

Name Value
svn:eol-style native