ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/branches/development/src/nonbonded/Electrostatic.cpp
Revision: 1665
Committed: Tue Nov 22 20:38:56 2011 UTC (13 years, 5 months ago) by gezelter
File size: 37363 byte(s)
Log Message:
updated copyright notices

File Contents

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

Properties

Name Value
svn:eol-style native