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

# 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 #include "io/Globals.hpp"
51
52 namespace OpenMD {
53
54 Electrostatic::Electrostatic(): name_("Electrostatic"), initialized_(false),
55 forceField_(NULL), info_(NULL),
56 haveCutoffRadius_(false),
57 haveDampingAlpha_(false),
58 haveDielectric_(false),
59 haveElectroSpline_(false)
60 {}
61
62 void Electrostatic::initialize() {
63
64 Globals* simParams_ = info_->getSimParams();
65
66 summationMap_["HARD"] = esm_HARD;
67 summationMap_["NONE"] = esm_HARD;
68 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 // 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 summationMethod_ = esm_HARD;
103 screeningMethod_ = UNDAMPED;
104 dielectric_ = 1.0;
105 one_third_ = 1.0 / 3.0;
106
107 // 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 "Electrostatic::initialize: Unknown electrostaticSummationMethod.\n"
119 "\t(Input file specified %s .)\n"
120 "\telectrostaticSummationMethod must be one of: \"hard\",\n"
121 "\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 // find all of the Electrostatic atom Types:
203 ForceField::AtomTypeContainer* atomTypes = forceField_->getAtomTypes();
204 ForceField::AtomTypeContainer::MapTypeIterator i;
205 AtomType* at;
206
207 for (at = atomTypes->beginType(i); at != NULL;
208 at = atomTypes->nextType(i)) {
209
210 if (at->isElectrostatic())
211 addType(at);
212 }
213
214
215 cutoffRadius2_ = cutoffRadius_ * cutoffRadius_;
216 rcuti_ = 1.0 / cutoffRadius_;
217 rcuti2_ = rcuti_ * rcuti_;
218 rcuti3_ = rcuti2_ * rcuti_;
219 rcuti4_ = rcuti2_ * rcuti2_;
220
221 if (screeningMethod_ == DAMPED) {
222
223 alpha2_ = dampingAlpha_ * dampingAlpha_;
224 alpha4_ = alpha2_ * alpha2_;
225 alpha6_ = alpha4_ * alpha2_;
226 alpha8_ = alpha4_ * alpha4_;
227
228 constEXP_ = exp(-alpha2_ * cutoffRadius2_);
229 invRootPi_ = 0.56418958354775628695;
230 alphaPi_ = 2.0 * dampingAlpha_ * invRootPi_;
231
232 c1c_ = erfc(dampingAlpha_ * cutoffRadius_) * rcuti_;
233 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 if (summationMethod_ == esm_REACTION_FIELD) {
248 preRF_ = (dielectric_ - 1.0) /
249 ((2.0 * dielectric_ + 1.0) * cutoffRadius2_ * cutoffRadius_);
250 preRF2_ = 2.0 * preRF_;
251 }
252
253 // 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 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 // 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 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 void Electrostatic::setCutoffRadius( RealType rCut ) {
418 cutoffRadius_ = rCut;
419 rrf_ = cutoffRadius_;
420 haveCutoffRadius_ = true;
421 }
422
423 void Electrostatic::setSwitchingRadius( RealType rSwitch ) {
424 rt_ = rSwitch;
425 }
426 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 void Electrostatic::calcForce(InteractionData &idat) {
442
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 RealType vpair(0.0);
456 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 RealType erfcVal(1.0), derfcVal(0.0);
462 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 // 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 pair<RealType, RealType> res;
479
480 if (!initialized_) initialize();
481
482 ElectrostaticAtomData data1 = ElectrostaticMap[idat.atypes.first];
483 ElectrostaticAtomData data2 = ElectrostaticMap[idat.atypes.second];
484
485 // some variables we'll need independent of electrostatic type:
486
487 riji = 1.0 / *(idat.rij) ;
488 Vector3d rhat = *(idat.d) * riji;
489
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 if (i_is_Charge) {
503 q_i = data1.charge;
504 if (idat.excluded) {
505 *(idat.skippedCharge2) += q_i;
506 }
507 }
508
509 if (i_is_Dipole) {
510 mu_i = data1.dipole_moment;
511 uz_i = idat.eFrame1->getColumn(2);
512
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 ux_i = idat.eFrame1->getColumn(0);
528 uy_i = idat.eFrame1->getColumn(1);
529 uz_i = idat.eFrame1->getColumn(2);
530
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 if (j_is_Charge) {
541 q_j = data2.charge;
542 if (idat.excluded) {
543 *(idat.skippedCharge1) += q_j;
544 }
545 }
546
547
548 if (j_is_Dipole) {
549 mu_j = data2.dipole_moment;
550 uz_j = idat.eFrame2->getColumn(2);
551
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 ux_j = idat.eFrame2->getColumn(0);
567 uy_j = idat.eFrame2->getColumn(1);
568 uz_j = idat.eFrame2->getColumn(2);
569
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 //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 c1 = erfcVal * riji;
595 c2 = (-derfcVal + c1) * riji;
596 } else {
597 c1 = riji;
598 c2 = c1 * riji;
599 }
600
601 preVal = *(idat.electroMult) * pre11_ * q_i * q_j;
602
603 if (summationMethod_ == esm_SHIFTED_POTENTIAL) {
604 vterm = preVal * (c1 - c1c_);
605 dudr = - *(idat.sw) * preVal * c2;
606
607 } else if (summationMethod_ == esm_SHIFTED_FORCE) {
608 vterm = preVal * ( c1 - c1c_ + c2c_*( *(idat.rij) - cutoffRadius_) );
609 dudr = *(idat.sw) * preVal * (c2c_ - c2);
610
611 } else if (summationMethod_ == esm_REACTION_FIELD) {
612 rfVal = preRF_ * *(idat.rij) * *(idat.rij);
613
614 vterm = preVal * ( riji + rfVal );
615 dudr = *(idat.sw) * preVal * ( 2.0 * rfVal - riji ) * riji;
616
617 // if this is an excluded pair, there are still indirect
618 // interactions via the reaction field we must worry about:
619
620 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 } else {
627
628 vterm = preVal * riji * erfcVal;
629 dudr = - *(idat.sw) * preVal * c2;
630
631 }
632
633 vpair += vterm;
634 epot += *(idat.sw) * vterm;
635 dVdr += dudr * rhat;
636 }
637
638 if (j_is_Dipole) {
639 // pref is used by all the possible methods
640 pref = *(idat.electroMult) * pre12_ * q_i * mu_j;
641 preSw = *(idat.sw) * pref;
642
643 if (summationMethod_ == esm_REACTION_FIELD) {
644 ri2 = riji * riji;
645 ri3 = ri2 * riji;
646
647 vterm = - pref * ct_j * ( ri2 - preRF2_ * *(idat.rij) );
648 vpair += vterm;
649 epot += *(idat.sw) * vterm;
650
651 dVdr += -preSw * (ri3 * (uz_j - 3.0 * ct_j * rhat) - preRF2_*uz_j);
652 duduz_j += -preSw * rhat * (ri2 - preRF2_ * *(idat.rij) );
653
654 // 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 } else {
666 // determine the inverse r used if we have split dipoles
667 if (j_is_SplitDipole) {
668 BigR = sqrt( *(idat.r2) + 0.25 * d_j * d_j);
669 ri = 1.0 / BigR;
670 scale = *(idat.rij) * ri;
671 } 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 //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 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 vpair += vterm;
700 epot += *(idat.sw) * vterm;
701
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 pref = *(idat.electroMult) * pre14_ * q_i * one_third_;
716
717 if (screeningMethod_ == DAMPED) {
718 // assemble the damping variables
719 //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 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 preSw = *(idat.sw) * pref;
737 c2ri = c2 * riji;
738 c3ri = c3 * riji;
739 c4rij = c4 * *(idat.rij) ;
740 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 vpair += vterm;
749 epot += *(idat.sw) * vterm;
750
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 pref = *(idat.electroMult) * pre12_ * q_j * mu_i;
768 preSw = *(idat.sw) * pref;
769
770 if (summationMethod_ == esm_REACTION_FIELD) {
771
772 ri2 = riji * riji;
773 ri3 = ri2 * riji;
774
775 vterm = pref * ct_i * ( ri2 - preRF2_ * *(idat.rij) );
776 vpair += vterm;
777 epot += *(idat.sw) * vterm;
778
779 dVdr += preSw * (ri3 * (uz_i - 3.0 * ct_i * rhat) - preRF2_ * uz_i);
780
781 duduz_i += preSw * rhat * (ri2 - preRF2_ * *(idat.rij) );
782
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
794 } else {
795
796 // determine inverse r if we are using split dipoles
797 if (i_is_SplitDipole) {
798 BigR = sqrt( *(idat.r2) + 0.25 * d_i * d_i);
799 ri = 1.0 / BigR;
800 scale = *(idat.rij) * ri;
801 } 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 //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 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 vpair += vterm;
830 epot += *(idat.sw) * vterm;
831
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 pref = *(idat.electroMult) * pre22_ * mu_i * mu_j;
843 preSw = *(idat.sw) * pref;
844
845 if (summationMethod_ == esm_REACTION_FIELD) {
846 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 vpair += vterm;
853 epot += *(idat.sw) * vterm;
854
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 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 } else {
870
871 if (i_is_SplitDipole) {
872 if (j_is_SplitDipole) {
873 BigR = sqrt( *(idat.r2) + 0.25 * d_i * d_i + 0.25 * d_j * d_j);
874 } else {
875 BigR = sqrt( *(idat.r2) + 0.25 * d_i * d_i);
876 }
877 ri = 1.0 / BigR;
878 scale = *(idat.rij) * ri;
879 } else {
880 if (j_is_SplitDipole) {
881 BigR = sqrt( *(idat.r2) + 0.25 * d_j * d_j);
882 ri = 1.0 / BigR;
883 scale = *(idat.rij) * ri;
884 } else {
885 ri = riji;
886 scale = 1.0;
887 }
888 }
889 if (screeningMethod_ == DAMPED) {
890 // assemble damping variables
891 //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 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 c4rij = c4 * *(idat.rij) ;
916
917 // calculate the potential
918 pot_term = (ct_ij * c2ri - ctidotj * c3);
919 vterm = pref * pot_term;
920 vpair += vterm;
921 epot += *(idat.sw) * vterm;
922
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 pref = *(idat.electroMult) * pre14_ * q_j * one_third_;
941
942 if (screeningMethod_ == DAMPED) {
943 // assemble the damping variables
944 //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 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 preSw = *(idat.sw) * pref;
962 c2ri = c2 * riji;
963 c3ri = c3 * riji;
964 c4rij = c4 * *(idat.rij) ;
965 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 vpair += vterm;
975 epot += *(idat.sw) * vterm;
976
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 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
1014 *(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 }
1023
1024
1025 return;
1026 }
1027
1028 void Electrostatic::calcSelfCorrection(SelfData &sdat) {
1029 RealType mu1, preVal, chg1, self;
1030
1031 if (!initialized_) initialize();
1032
1033 ElectrostaticAtomData data = ElectrostaticMap[sdat.atype];
1034
1035 // logicals
1036 bool i_is_Charge = data.is_Charge;
1037 bool i_is_Dipole = data.is_Dipole;
1038
1039 if (summationMethod_ == esm_REACTION_FIELD) {
1040 if (i_is_Dipole) {
1041 mu1 = data.dipole_moment;
1042 preVal = pre22_ * preRF2_ * mu1 * mu1;
1043 (*(sdat.pot))[ELECTROSTATIC_FAMILY] -= 0.5 * preVal;
1044
1045 // The self-correction term adds into the reaction field vector
1046 Vector3d uz_i = sdat.eFrame->getColumn(2);
1047 Vector3d ei = preVal * uz_i;
1048
1049 // This looks very wrong. A vector crossed with itself is zero.
1050 *(sdat.t) -= cross(uz_i, ei);
1051 }
1052 } else if (summationMethod_ == esm_SHIFTED_FORCE || summationMethod_ == esm_SHIFTED_POTENTIAL) {
1053 if (i_is_Charge) {
1054 chg1 = data.charge;
1055 if (screeningMethod_ == DAMPED) {
1056 self = - 0.5 * (c1c_ + alphaPi_) * chg1 * (chg1 + *(sdat.skippedCharge)) * pre11_;
1057 } else {
1058 self = - 0.5 * rcuti_ * chg1 * (chg1 + *(sdat.skippedCharge)) * pre11_;
1059 }
1060 (*(sdat.pot))[ELECTROSTATIC_FAMILY] += self;
1061 }
1062 }
1063 }
1064
1065 RealType Electrostatic::getSuggestedCutoffRadius(pair<AtomType*, AtomType*> atypes) {
1066 // 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 }

Properties

Name Value
svn:eol-style native