ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/branches/development/src/nonbonded/Electrostatic.cpp
Revision: 1505
Committed: Sun Oct 3 22:18:59 2010 UTC (14 years, 7 months ago) by gezelter
File size: 32776 byte(s)
Log Message:
Less busted than it was on last check-in, but still won't completely
build.


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

Properties

Name Value
svn:eol-style native