Simpatico  v1.10
MdEwaldPotential.cpp
1 #ifndef MD_EWALD_POTENTIAL_CPP
2 #define MD_EWALD_POTENTIAL_CPP
3 
4 /*
5 * Simpatico - Simulation Package for Polymeric and Molecular Liquids
6 *
7 * Copyright 2010 - 2017, David Morse (morse012@umn.edu)
8 * Distributed under the terms of the GNU General Public License.
9 */
10 
11 #include "MdEwaldPotential.h"
12 #include <mcMd/simulation/System.h>
13 #include <mcMd/simulation/Simulation.h>
14 #include <mcMd/chemistry/AtomType.h>
15 #include <simp/boundary/Boundary.h>
16 #include <util/space/Vector.h>
17 #include <util/space/Tensor.h>
18 #include <util/math/Constants.h>
19 #include <util/containers/Array.h>
20 
21 #include <cmath>
22 #include <stdlib.h>
23 
24 namespace McMd
25 {
26 
27  using namespace Util;
28  using namespace Simp;
29 
30  /*
31  * Constructor.
32  */
35  ewaldInteraction_(),
36  simulationPtr_(&system.simulation()),
37  systemPtr_(&system),
38  boundaryPtr_(&system.boundary()),
39  atomTypesPtr_(&system.simulation().atomTypes())
40  {
41  // Note: Don't setClassName - using "CoulombPotential" base class name
42  }
43 
44  /*
45  * Destructor (does nothing)
46  */
48  {}
49 
50  /*
51  * Read parameters and initialize.
52  */
53  void MdEwaldPotential::readParameters(std::istream& in)
54  {
55  // Read EwaldInteraction block containing parameters
56  bool nextIndent = false;
57  addParamComposite(ewaldInteraction_, nextIndent);
58  ewaldInteraction_.readParameters(in);
59 
60  read<double>(in, "kSpaceCutoff", kSpaceCutoff_);
61  }
62 
63  /*
64  * Load internal state from an archive.
65  */
67  {
68  bool nextIndent = false;
69  addParamComposite(ewaldInteraction_, nextIndent);
70  ewaldInteraction_.loadParameters(ar);
71 
72  loadParameter<double>(ar, "kSpaceCutoff", kSpaceCutoff_);
73  }
74 
75  /*
76  * Save internal state to an archive.
77  */
79  {
80  ewaldInteraction_.save(ar);
81  ar << kSpaceCutoff_;
82  }
83 
87  void MdEwaldPotential::set(std::string name, double value)
88  {
89  if (name == "kSpaceCutoff") {
90  kSpaceCutoff_ = value;
91  } else {
92  ewaldInteraction_.set(name, value);
93  }
94  unsetWaves();
95  }
96 
97  /*
98  * Get a parameter value, identified by a string.
99  */
100  double MdEwaldPotential::get(std::string name) const
101  {
102  double value;
103  if (name == "kSpaceCutoff") {
104  value = kSpaceCutoff_;
105  } else {
106  value = ewaldInteraction_.get(name);
107  }
108  return value;
109  }
110 
111  /*
112  * Get cutfoff wavenumber for long range interaction.
113  */
114  inline int MdEwaldPotential::nWave() const
115  { return intWaves_.size(); }
116 
117  /*
118  * Generate waves using kCutoff; allocate memories for associated variables.
119  * Generate vectors for image cell in real sapce using rSpaceCutoff.
120  * Comments:
121  * Only half of waves are stored.
122  * The first index is always non-negative.
123  *
124  */
126  {
127  Vector b0, b1, b2; // Recprocal basis vectors.
128  Vector q0, q1, q2, q; // Partial and complete wavevectors.
129  Vector kv; // Wavevector (as real vector)
130  double ksq;
131  IntVector maxK, k; // Max and running wave indices.
132  int mink1, mink2; // Minimum k-indices
133  int j;
134 
135  b0 = boundaryPtr_->reciprocalBasisVector(0);
136  b1 = boundaryPtr_->reciprocalBasisVector(1);
137  b2 = boundaryPtr_->reciprocalBasisVector(2);
138 
139  // Get max wave indices and reserve arrays
140  double pi2 = 2.0*Constants::Pi;
141  for (j=0; j < Dimension; ++j) {
142  maxK[j] =
143  ceil(kSpaceCutoff_*boundaryPtr_->bravaisBasisVector(j).abs()/pi2);
144  UTIL_CHECK(maxK[j] > 0);
145  }
146 
147  if (intWaves_.capacity() == 0) {
148  int capacity;
149  capacity = ((2*maxK[0] + 1)*(2*maxK[1] + 1)*(2*maxK[2] + 1) - 1)/2;
150  UTIL_CHECK(capacity > 0);
151  intWaves_.reserve(capacity);
152  ksq_.reserve(capacity);
153  g_.reserve(capacity);
154  rho_.reserve(capacity);
155  fexp0_.reserve(maxK[0] + 1);
156  fexp1_.reserve(2*maxK[1] + 1);
157  fexp2_.reserve(2*maxK[2] + 1);
158  } else {
159  intWaves_.clear();
160  ksq_.clear();
161  g_.clear();
162  rho_.clear();
163  fexp0_.clear();
164  fexp1_.clear();
165  fexp2_.clear();
166  }
167 
168  // Accumulate waves, and wave-related properties.
169  base0_ = 0;
170  upper0_ = -maxK[0];
171  base1_ = maxK[1];
172  upper1_ = -base1_;
173  base2_ = maxK[2];
174  upper2_ = -base2_;
175  double kSpaceCutoffSq = kSpaceCutoff_*kSpaceCutoff_;
176 
177  q0.multiply(b0, -1);
178  for (k[0] = 0; k[0] <= maxK[0]; ++k[0]) {
179 
180  // Note: First index always non-negative.
181  q0 += b0;
182 
183  mink1 = (k[0] == 0 ? 0 : -maxK[1]);
184  q1.multiply(b1, mink1 - 1);
185  q1 += q0;
186  for (k[1] = mink1; k[1] <= maxK[1]; ++k[1]) {
187  q1 += b1;
188 
189  mink2 = (k[0] == 0 && k[1] == 0 ? 1 : -maxK[2]);
190  q.multiply(b2, mink2 - 1);
191  q += q1;
192 
193  for (k[2] = mink2; k[2] <= maxK[2]; ++k[2]) {
194  q += b2;
195 
196  ksq = double(q.square());
197  if (ksq <= kSpaceCutoffSq) {
198 
199  if (k[0] > upper0_) upper0_ = k[0];
200 
201  if (k[1] < base1_ ) base1_ = k[1];
202  if (k[1] > upper1_) upper1_ = k[1];
203 
204  if (k[2] < base2_ ) base2_ = k[2];
205  if (k[2] > upper2_) upper2_ = k[2];
206 
207  intWaves_.append(k);
208  ksq_.append(ksq);
209  g_.append(ewaldInteraction_.kSpacePotential(ksq));
210  }
211 
212  } // for k[2]
213  } // for k[1]
214  } // for k[0]
215 
216  // Resize work arrays
217  UTIL_CHECK(intWaves_.size() > 0);
218  UTIL_CHECK(upper0_ - base0_ + 1 > 0);
219  UTIL_CHECK(upper1_ - base1_ + 1 > 0);
220  UTIL_CHECK(upper2_ - base2_ + 1 > 0);
221  rho_.resize(intWaves_.size());
222  fexp0_.resize(upper0_ - base0_ + 1);
223  fexp1_.resize(upper1_ - base1_ + 1);
224  fexp2_.resize(upper2_ - base2_ + 1);
225 
226  // Mark waves as updated
227  hasWaves_ = true;
228  }
229 
230  /*
231  * Calculate fourier modes of charge density.
232  */
233  void MdEwaldPotential::computeKSpaceCharge()
234  {
235  // Compute waves if necessary
236  if (!hasWaves()) {
237  makeWaves();
238  }
239 
240  System::MoleculeIterator molIter;
241  Molecule::AtomIterator atomIter;
242  Vector rg; // scaled atom position vector
243  IntVector q; // wave indices
244  Vector qv; // wave vector
245  double dotqr; // dot product between q and r
246  double x, y; // real and imaginary parts of phasor
247  double charge; // atom charge
248  double TwoPi; // 2.0*pi
249  int i, j; // array index
250 
251  TwoPi = 2.0*Constants::Pi;
252 
253  // Clear rho for all waves
254  for (i = 0; i < rho_.size(); ++i) {
255  rho_[i] = DCMPLX(0.0, 0.0);
256  }
257 
258  // Loop over species, molecules atoms
259  int nSpecies = simulationPtr_->nSpecies();
260  for (int iSpecies = 0; iSpecies < nSpecies; ++iSpecies) {
261  systemPtr_->begin(iSpecies, molIter);
262  for ( ; molIter.notEnd(); ++molIter) {
263  for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {
264  boundaryPtr_->transformCartToGen(atomIter->position(), rg);
265  charge = (*atomTypesPtr_)[atomIter->typeId()].charge();
266 
267  // Loop over waves
268  for (i = 0; i < intWaves_.size(); ++i) {
269  q = intWaves_[i];
270  for (j = 0; j < Dimension; ++j) {
271  qv[j] = double(q[j]);
272  }
273  dotqr = rg.dot(qv)*TwoPi;
274  x = charge*cos(dotqr);
275  y = charge*sin(dotqr);
276  rho_[i] += DCMPLX(x, y);
277  }
278 
279  } // For atoms.
280  } // For molecules.
281  } // For species.
282 
283  }
284 
285  /*
286  * Add k-space Coulomb forces for all atoms.
287  */
289  {
290  System::MoleculeIterator molIter, imolIter, jmolIter;
291  Molecule::AtomIterator atomIter, iatomIter, jatomIter;
292  Vector b[Dimension]; // array of reciprocal basis vectors
293  Vector rg; // scaled atom position vector
294  Vector fg; // generalized force on atom
295  Vector df; // force contribution
296  IntVector q; // wave index
297  double charge; // atom charge
298  double x, y; // Real and imaginary parts of phasor
299 
300  DCMPLX TwoPiIm; // 2.0*pi*I
301  DCMPLX de, expfactor;
302  double EPS(1.0E-10); // Tiny number to check if is charge
303  double volume = boundaryPtr_->volume();
304  double TwoPi = 2.0*Constants::Pi;
305  TwoPiIm = TwoPi * Constants::Im;
306  int nSpecies = simulationPtr_->nSpecies();
307  int type;
308  int i, j;
309 
310  // Compute Fourier components of charge density.
311  computeKSpaceCharge();
312 
313  // Store reciprocal lattice vectors
314  for (j = 0; j < Dimension; ++j) {
315  b[j] = boundaryPtr_->reciprocalBasisVector(j);
316  }
317 
318  // Loop over species, molecules atoms
319  for (int iSpecies = 0; iSpecies < nSpecies; ++iSpecies) {
320  systemPtr_->begin(iSpecies, molIter);
321  for ( ; molIter.notEnd(); ++molIter) {
322  for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {
323  type = atomIter->typeId();
324  charge = (*atomTypesPtr_)[type].charge();
325 
326  if (fabs(charge) > EPS) {
327  boundaryPtr_->transformCartToGen(atomIter->position(), rg);
328 
329  // Tabulate the exponential factors.
330  fexp0_[0] = exp(TwoPiIm * rg[0] * double(base0_));
331  de = exp(TwoPiIm * rg[0]);
332  for (i = 1; i < fexp0_.size(); ++i) {
333  fexp0_[i] = fexp0_[i-1] * de;
334  }
335 
336  fexp1_[0] = exp(TwoPiIm * rg[1] * double(base1_));
337  de = exp(TwoPiIm * rg[1]);
338  for (i = 1; i < fexp1_.size(); ++i) {
339  fexp1_[i] = fexp1_[i-1] * de;
340  }
341 
342  fexp2_[0] = exp(TwoPiIm * rg[2] * double(base2_));
343  de = exp(TwoPiIm * rg[2]);
344  for (i = 1; i < fexp2_.size(); ++i) {
345  fexp2_[i] = fexp2_[i-1] * de;
346  }
347 
348  // Accumulating forces.
349  fg.zero();
350  for (i = 0; i < intWaves_.size(); ++i) {
351  q = intWaves_[i];
352  for (j = 0; j < Dimension; ++j) {
353  df[j] = double(q[j]);
354  }
355  expfactor = fexp0_[q[0]-base0_] * fexp1_[q[1]-base1_] * fexp2_[q[2]-base2_];
356  x = expfactor.real();
357  y = expfactor.imag();
358  df *= g_[i]*( x*rho_[i].imag() - y*rho_[i].real() );
359  fg += df;
360  }
361  fg *= -2.0*charge/volume;
362 
363  // Transform to Cartesian coordinates
364  for (j = 0; j < Dimension; ++j) {
365  df.multiply(b[j], fg[j]);
366  atomIter->force() += df;
367  }
368  }
369 
370  } // for atoms
371  } // for molecules
372  } // for species
373  }
374 
375  /*
376  * Calculate the k-space contribution to the Coulomb energy.
377  */
379  {
380 
381  // Compute Fourier components of charge density.
382  computeKSpaceCharge();
383 
384  // Main loop over wavevectors
385  double x, y,rhoSq;
386  double energy = 0.0;
387  for (int i = 0; i < intWaves_.size(); ++i) {
388  x = rho_[i].real();
389  y = rho_[i].imag();
390  rhoSq = x*x + y*y;
391  energy += rhoSq*g_[i];
392  }
393  double volume = boundaryPtr_->volume();
394  energy /= volume;
395  // Note: A factor of 0.5 in the expression for the kspace energy
396  // kpart is cancelled our use of only half the wavevectors
397 
398  // Calculate self-energy correction to Ewald summation.
399  System::MoleculeIterator molIter;
400  Molecule::AtomIterator atomiter;
401  double charge;
402  double selfEnergy = 0.0;
403  int nSpecies = simulationPtr_->nSpecies();
404  int iAtomType;
405  for (int iSpecies = 0; iSpecies < nSpecies; ++iSpecies) {
406  systemPtr_->begin(iSpecies, molIter);
407  for ( ; molIter.notEnd(); ++molIter) {
408  for (molIter->begin(atomiter); atomiter.notEnd(); ++atomiter) {
409  iAtomType = atomiter->typeId();
410  charge = (*atomTypesPtr_)[iAtomType].charge();
411  selfEnergy += charge*charge;
412  }
413  }
414  }
415  double pi = Constants::Pi;
416  double alpha = ewaldInteraction_.alpha();
417  double epsilon = ewaldInteraction_.epsilon();
418  selfEnergy *= alpha/(4.0*sqrt(pi)*pi*epsilon);
419 
420  // Correct for conjugate wave contribution in k-part.
421  kSpaceEnergy_.set(energy - selfEnergy);
422  }
423 
424  /*
425  * Compute the k contribution to stress.
426  * computeStress() for coulomb part in MdSystem.cpp is off.
427  */
429  {
430  // Compute Fourier components of charge density.
431  computeKSpaceCharge();
432 
433  int i;
434  double x, y; //real and image part of rho[i].
435  Tensor K,stressTensor;// temp stress tensor.
436  IntVector q;//vector indices.
437  Vector qv,q0,q1,q2; //reciprocalVector.
438  Vector b0, b1, b2; // reciprocalBasisVector.
439  b0 = boundaryPtr_->reciprocalBasisVector(0);
440  b1 = boundaryPtr_->reciprocalBasisVector(1);
441  b2 = boundaryPtr_->reciprocalBasisVector(2);
442 
443  stressTensor.zero();
444  double alpha = ewaldInteraction_.alpha();
445  double ca = 0.25/(alpha*alpha);
446  for (i = 0; i < intWaves_.size(); ++i) {
447  q = intWaves_[i];
448  q0.multiply(b0, q[0]);
449  q1.multiply(b1, q[1]);
450  q2.multiply(b2, q[2]);
451  qv = q0;
452  qv += q1;
453  qv += q2;
454 
455  K.dyad(qv,qv);
456  K *= -2.0 * (ca + (1.0/ksq_[i]));
457  K.add(Tensor::Identity, K);
458  x = rho_[i].real();
459  y = rho_[i].imag();
460  K *= g_[i]*(x*x + y*y);
461  stressTensor += K;
462  }
463  double volume = boundaryPtr_->volume();
464  stressTensor /= volume*volume;
465  // Note: A factor of 0.5 in usual expression for stress is
466  // cancelled by our use of only half of the wavevectors.
467 
468  kSpaceStress_.set(stressTensor);
469  }
470 
471 }
472 #endif
virtual void makeWaves()
Generate wavevectors for the current boundary and kCutoff.
Vector & zero()
Set all elements of a 3D vector to zero.
Definition: Vector.h:514
Coulomb potential for an Md simulation.
const int Dimension
Dimensionality of space.
Definition: Dimension.h:19
double kSpacePotential(double kSq) const
Return regularized Fourier-space potential.
Setable< double > kSpaceEnergy_
K-space part of Coulomb energy.
A Vector is a Cartesian vector.
Definition: Vector.h:75
void begin(int speciesId, MoleculeIterator &iterator)
Initialize an iterator for molecules of one species in this System.
Definition: System.h:1147
void append(const Data &data)
Append an element to the end of the sequence.
Definition: GArray.h:306
void clear()
Reset to empty state.
Definition: GArray.h:299
bool notEnd() const
Is the current pointer not at the end of the array?
Definition: ArrayIterator.h:83
double volume() const
Return unit cell volume.
double dot(const Vector &v) const
Return dot product of this vector and vector v.
Definition: Vector.h:632
void set(const T &value)
Set the value and mark as set.
Definition: Setable.h:107
MdEwaldPotential(System &system)
Constructor.
Vector & multiply(const Vector &v, double s)
Multiply a vector v by a scalar s.
Definition: Vector.h:686
const Vector & bravaisBasisVector(int i) const
Return Bravais lattice vector i.
Tensor & zero()
Set all elements of this tensor to zero.
Definition: Tensor.h:441
A set of interacting Molecules enclosed by a Boundary.
Definition: System.h:115
void reserve(int capacity)
Reserve memory for specified number of elements.
Definition: GArray.h:255
int nWave() const
Current number of wavevectors with |k| < kCutoff.
int size() const
Return logical size of this array (i.e., current number of elements).
Definition: GArray.h:455
virtual void save(Serializable::OArchive &ar)
Save internal state to an archive.
Classes used by all simpatico molecular simulations.
A Tensor represents a Cartesian tensor.
Definition: Tensor.h:32
Tensor & add(const Tensor &t1, const Tensor &t2)
Add tensors t1 and t2.
Definition: Tensor.h:567
virtual void save(Serializable::OArchive &ar)
Save internal state to an archive.
Saving / output archive for binary ostream.
Setable< Tensor > kSpaceStress_
K-space part of Coulomb stress.
const Vector & reciprocalBasisVector(int i) const
Return reciprocal lattice basis vector i.
double alpha() const
Get Ewald smearing parameter alpha (inverse length).
bool notEnd() const
Is the current pointer not at the end of the PArray?
Utility classes for scientific computation.
Definition: accumulators.mod:1
Forward iterator for an Array or a C array.
Definition: ArrayIterator.h:39
Forward iterator for a PArray.
Definition: ArraySet.h:19
void set(std::string name, double value)
Set a parameter value, identified by a string.
void set(std::string name, double value)
Modify a parameter, identified by a string.
double get(std::string name) const
Get a parameter value, identified by a string.
Saving archive for binary istream.
virtual void loadParameters(Serializable::IArchive &ar)
Load internal state from an archive.
Single-processor Monte Carlo (MC) and molecular dynamics (MD).
int nSpecies() const
Get the number of Species in this Simulation.
bool hasWaves_
Are waves and k-space potential up to date? Unset if boundary or parameters change.
void addParamComposite(ParamComposite &child, bool next=true)
Add a child ParamComposite object to the format array.
double abs() const
Return absolute magnitude of this vector.
Definition: Vector.h:625
static const double Pi
Trigonometric constant Pi.
Definition: Constants.h:35
double epsilon() const
Get dielectric permittivity.
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition: global.h:68
An IntVector is an integer Cartesian vector.
Definition: IntVector.h:73
virtual void computeStress()
Compute kspace part of Coulomb pressure.
virtual void addForces()
Add k-space Coulomb forces for all atoms.
static const Tensor Identity
Constant idenity Tensor (diagonal diagonal elements all 1).
Definition: Tensor.h:302
virtual void computeEnergy()
Calculate the long range kspace part of Coulomb energy.
Tensor & dyad(const Vector &v1, const Vector &v2)
Create dyad of two vectors.
Definition: Tensor.h:763
double energy()
Get total Coulomb energy.
static const std::complex< double > Im
Square root of -1.
Definition: Constants.h:40
virtual void loadParameters(Serializable::IArchive &ar)
Load internal state from an archive.
void unsetWaves()
Unset all data that depends on the Boundary.
double get(std::string name) const
Get a parameter value, identified by a string.
virtual void readParameters(std::istream &in)
Read parameters and initialize.
double square() const
Return square magnitude of this vector.
Definition: Vector.h:616
void resize(int n)
Resizes array so that it contains n elements.
Definition: GArray.h:339
void readParameters(std::istream &in)
Read epsilon, alpha, and rCutoff.
bool hasWaves()
Are wavevectors and k-space influence function up to date?
virtual ~MdEwaldPotential()
Destructor (does nothing).
void transformCartToGen(const Vector &Rc, Vector &Rg) const
Transform Cartesian Vector to scaled / generalized coordinates.