ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/branches/development/src/nonbonded/Electrostatic.cpp
Revision: 1718
Committed: Thu May 24 01:29:59 2012 UTC (12 years, 11 months ago) by gezelter
File size: 36016 byte(s)
Log Message:
Adding fluctuating charges, still a work in progress

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

Properties

Name Value
svn:eol-style native