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


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

Properties

Name Value
svn:eol-style native