ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/branches/development/src/nonbonded/Electrostatic.cpp
Revision: 1718
Committed: Thu May 24 01:29:59 2012 UTC (12 years, 11 months ago) by gezelter
File size: 36016 byte(s)
Log Message:
Adding fluctuating charges, still a work in progress

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

Properties

Name Value
svn:eol-style native