ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/branches/development/src/nonbonded/Electrostatic.cpp
Revision: 1505
Committed: Sun Oct 3 22:18:59 2010 UTC (14 years, 7 months ago) by gezelter
File size: 32776 byte(s)
Log Message:
Less busted than it was on last check-in, but still won't completely
build.


File Contents

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

Properties

Name Value
svn:eol-style native