ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/branches/development/src/nonbonded/Electrostatic.cpp
Revision: 1710
Committed: Fri May 18 21:44:02 2012 UTC (12 years, 11 months ago) by gezelter
File size: 33878 byte(s)
Log Message:
Added an adapter layer between the AtomType and the rest of the code to 
handle the bolt-on capabilities of new types. 

Fixed a long-standing bug in how storageLayout was being set to the maximum
possible value.

Started to add infrastructure for Polarizable and fluc-Q calculations.

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

Properties

Name Value
svn:eol-style native