ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/branches/development/src/nonbonded/Electrostatic.cpp
Revision: 1528
Committed: Fri Dec 17 20:11:05 2010 UTC (14 years, 5 months ago) by gezelter
File size: 36334 byte(s)
Log Message:
Doesn't build yet, but getting much closer...


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

Properties

Name Value
svn:eol-style native