ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/branches/development/src/nonbonded/Electrostatic.cpp
Revision: 1616
Committed: Tue Aug 30 15:45:35 2011 UTC (13 years, 8 months ago) by gezelter
File size: 37297 byte(s)
Log Message:
Fixing cutoff groups

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

Properties

Name Value
svn:eol-style native