ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/branches/devel_omp/src/nonbonded/Electrostatic.cpp
Revision: 1614
Committed: Tue Aug 23 20:55:51 2011 UTC (13 years, 8 months ago) by mciznick
File size: 39606 byte(s)
Log Message:
Updated scalability of OpenMP threads.

File Contents

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

Properties

Name Value
svn:eol-style native