QMCPACK
MPC.cpp
Go to the documentation of this file.
1 //////////////////////////////////////////////////////////////////////////////////////
2 // This file is distributed under the University of Illinois/NCSA Open Source License.
3 // See LICENSE file in top directory for details.
4 //
5 // Copyright (c) 2016 Jeongnim Kim and QMCPACK developers.
6 //
7 // File developed by: Ken Esler, kpesler@gmail.com, University of Illinois at Urbana-Champaign
8 // Jeremy McMinnis, jmcminis@gmail.com, University of Illinois at Urbana-Champaign
9 // Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
10 // Mark A. Berrill, berrillma@ornl.gov, Oak Ridge National Laboratory
11 //
12 // File created by: Ken Esler, kpesler@gmail.com, University of Illinois at Urbana-Champaign
13 //////////////////////////////////////////////////////////////////////////////////////
14 
15 
16 #include "MPC.h"
17 #include "Lattice/ParticleBConds.h"
18 #include "OhmmsPETE/OhmmsArray.h"
19 #include "OhmmsData/AttributeSet.h"
20 #include "Particle/DistanceTable.h"
23 
24 #if defined(HAVE_LIBFFTW)
25 #include <fftw3.h>
26 #endif
27 
28 #include <array>
29 #include <string_view>
30 
31 namespace qmcplusplus
32 {
34 
35 MPC::MPC(ParticleSet& ptcl, double cutoff)
36  : Ecut(cutoff), d_aa_ID(ptcl.addTable(ptcl, DTModes::NEED_FULL_TABLE_ON_HOST_AFTER_DONEPBYP)), FirstTime(true)
37 {
38  initBreakup(ptcl);
39 }
40 
41 MPC::~MPC() = default;
42 
43 void MPC::init_gvecs(const ParticleSet& ptcl)
44 {
45  TinyVector<int, OHMMS_DIM> maxIndex(0);
46  PosType b[OHMMS_DIM];
47  for (int j = 0; j < OHMMS_DIM; j++)
48  b[j] = static_cast<RealType>(2.0 * M_PI) * ptcl.getLattice().b(j);
49  int numG1 = ptcl.Density_G.size();
50  int numG2 = ptcl.DensityReducedGvecs.size();
51  assert(ptcl.Density_G.size() == ptcl.DensityReducedGvecs.size());
52  // Loop through all the G-vectors, and find the largest
53  // indices in each direction with energy less than the cutoff
54  for (int iG = 0; iG < ptcl.DensityReducedGvecs.size(); iG++)
55  {
57  PosType G = (double)gint[0] * b[0];
58  for (int j = 1; j < OHMMS_DIM; j++)
59  G += (double)gint[j] * b[j];
60  if (0.5 * dot(G, G) < Ecut)
61  {
62  for (int j = 0; j < OHMMS_DIM; j++)
63  maxIndex[j] = std::max(maxIndex[j], std::abs(gint[j]));
64  Gvecs.push_back(G);
65  Gints.push_back(gint);
66  Rho_G.push_back(ptcl.Density_G[iG]);
67  }
68  }
69  for (int idim = 0; idim < OHMMS_DIM; idim++)
70  SplineDim[idim] = 4 * maxIndex[idim];
71  MaxDim = std::max(maxIndex[0], std::max(maxIndex[1], maxIndex[2]));
72  app_log() << " Using " << Gvecs.size() << " G-vectors for MPC interaction.\n";
73  app_log() << " Using real-space box of size [" << SplineDim[0] << "," << SplineDim[1] << "," << SplineDim[2]
74  << "] for MPC spline.\n";
75 }
76 
77 
78 void MPC::compute_g_G(const ParticleSet& ptcl, double& g_0, std::vector<double>& g_G, int N)
79 {
80  double L = ptcl.getLattice().WignerSeitzRadius;
81  double Linv = 1.0 / L;
82  double Linv3 = Linv * Linv * Linv;
83  // create an FFTW plan
84  Array<std::complex<double>, 3> rBox(N, N, N);
85  Array<std::complex<double>, 3> GBox(N, N, N);
86  // app_log() << "Doing " << N << " x " << N << " x " << N << " FFT.\n";
87  //create BC handler
89  // Fill the real-space array with f(r)
90  double Ninv = 1.0 / (double)N;
92  for (int ix = 0; ix < N; ix++)
93  {
94  u[0] = Ninv * ix;
95  for (int iy = 0; iy < N; iy++)
96  {
97  u[1] = Ninv * iy;
98  for (int iz = 0; iz < N; iz++)
99  {
100  u[2] = Ninv * iz;
101  r = ptcl.getLattice().toCart(u);
102  //DTD_BConds<double,3,SUPERCELL_BULK>::apply (ptcl.getLattice(), r);
103  //double rmag = std::sqrt(dot(r,r));
104  double rmag = std::sqrt(mybc.apply_bc(r));
105  if (rmag < L)
106  rBox(ix, iy, iz) = -0.5 * rmag * rmag * Linv3 + 1.5 * Linv;
107  else
108  rBox(ix, iy, iz) = 1.0 / rmag;
109  }
110  }
111  }
112  fftw_plan fft = fftw_plan_dft_3d(N, N, N, (fftw_complex*)rBox.data(), (fftw_complex*)GBox.data(), 1, FFTW_ESTIMATE);
113  fftw_execute(fft);
114  fftw_destroy_plan(fft);
115  // Now, copy data into output, and add on analytic part
116  double norm = Ninv * Ninv * Ninv;
117  int numG = Gints.size();
118  for (int iG = 0; iG < numG; iG++)
119  {
121  for (int j = 0; j < OHMMS_DIM; j++)
122  gint[j] = (gint[j] + N) % N;
123  g_G[iG] = norm * real(GBox(gint[0], gint[1], gint[2]));
124  }
125  g_0 = norm * real(GBox(0, 0, 0));
126 }
127 
128 
129 inline double extrap(int N, TinyVector<double, 3> g_124)
130 {
131  Tensor<double, 3> A, Ainv;
132  double N1inv2 = 1.0 / (double)(N * N);
133  double N2inv2 = 1.0 / (double)((2 * N) * (2 * N));
134  double N4inv2 = 1.0 / (double)((4 * N) * (4 * N));
135  A(0, 0) = 1.0;
136  A(0, 1) = N1inv2;
137  A(0, 2) = N1inv2 * N1inv2;
138  A(1, 0) = 1.0;
139  A(1, 1) = N2inv2;
140  A(1, 2) = N2inv2 * N2inv2;
141  A(2, 0) = 1.0;
142  A(2, 1) = N4inv2;
143  A(2, 2) = N4inv2 * N4inv2;
144  Ainv = inverse(A);
145  TinyVector<double, 3> abc = dot(Ainv, g_124);
146  return abc[0];
147 }
148 
149 
150 inline double extrap(int N, TinyVector<double, 2> g_12)
151 {
152  Tensor<double, 2> A, Ainv;
153  double N1inv2 = 1.0 / (double)(N * N);
154  double N2inv2 = 1.0 / (double)((2 * N) * (2 * N));
155  A(0, 0) = 1.0;
156  A(0, 1) = N1inv2;
157  A(1, 0) = 1.0;
158  A(1, 1) = N2inv2;
159  Ainv = inverse(A);
160  TinyVector<double, 2> abc = dot(Ainv, g_12);
161  return abc[0];
162 }
163 
164 
165 void MPC::init_f_G(const ParticleSet& ptcl)
166 {
167  int numG = Gints.size();
168  f_G.resize(numG);
169  int N = std::max(64, 2 * MaxDim + 1);
170  std::vector<double> g_G_N(numG), g_G_2N(numG), g_G_4N(numG);
171  double g_0_N, g_0_2N, g_0_4N;
172  compute_g_G(ptcl, g_0_N, g_G_N, 1 * N);
173  compute_g_G(ptcl, g_0_2N, g_G_2N, 2 * N);
174  compute_g_G(ptcl, g_0_4N, g_G_4N, 4 * N);
175  // fprintf (stderr, "g_G_1N[0] = %18.14e\n", g_G_N[0]);
176  // fprintf (stderr, "g_G_2N[0] = %18.14e\n", g_G_2N[0]);
177  // fprintf (stderr, "g_G_4N[0] = %18.14e\n", g_G_4N[0]);
178  double volInv = 1.0 / ptcl.getLattice().Volume;
179  double L = ptcl.getLattice().WignerSeitzRadius;
180  TinyVector<double, 2> g0_12(g_0_2N, g_0_4N);
181  TinyVector<double, 3> g0_124(g_0_N, g_0_2N, g_0_4N);
182  f_0 = extrap(N, g0_124);
183  app_log().precision(12);
184  app_log() << " Linear extrap = " << std::scientific << extrap(2 * N, g0_12) << std::endl;
185  app_log() << " Quadratic extrap = " << std::scientific << f_0 << std::endl;
186  f_0 += 0.4 * M_PI * L * L * volInv;
187  // std::cerr << "f_0 = " << f_0/volInv << std::endl;
188  double worst = 0.0, worstLin = 0.0, worstQuad = 0.0;
189  for (int iG = 0; iG < numG; iG++)
190  {
191  TinyVector<double, 2> g_12(g_G_2N[iG], g_G_4N[iG]);
192  TinyVector<double, 3> g_124(g_G_N[iG], g_G_2N[iG], g_G_4N[iG]);
193  double linearExtrap = extrap(2 * N, g_12);
194  double quadExtrap = extrap(N, g_124);
195  double diff = std::abs(linearExtrap - quadExtrap);
196  if (diff > worst)
197  {
198  worst = diff;
199  worstLin = linearExtrap;
200  worstQuad = quadExtrap;
201  }
202  f_G[iG] = quadExtrap;
203  double G2 = dot(Gvecs[iG], Gvecs[iG]);
204  double G = std::sqrt(G2);
205  f_G[iG] += volInv * M_PI * (4.0 / G2 + 12.0 / (L * L * G2 * G2) * (std::cos(G * L) - std::sin(G * L) / (G * L)));
206  // std::cerr << "f_G = " << f_G[iG]/volInv << std::endl;
207  // std::cerr << "f_G - 4*pi/G2= " << f_G[iG]/volInv - 4.0*M_PI/G2 << std::endl;
208  }
209  std::array<char, 1000> buff;
210  int length = std::snprintf(buff.data(), buff.size(),
211  " Worst MPC discrepancy:\n"
212  " Linear Extrap : %18.14e\n"
213  " Quadratic Extrap: %18.14e\n",
214  worstLin, worstQuad);
215  if (length < 0)
216  throw std::runtime_error("Error generating buffer string");
217  app_log() << std::string_view(buff.data(), length);
218 }
219 
220 
221 void MPC::init_spline(const ParticleSet& ptcl)
222 {
224  Array<double, 3> splineData(SplineDim);
225  GBox = std::complex<double>();
226  Vconst = 0.0;
227  // Now fill in elements of GBox
228  const RealType vol = ptcl.getLattice().Volume;
229  const RealType volInv = 1.0 / ptcl.getLattice().Volume;
230  const RealType halfvol = vol / 2.0;
231  for (int iG = 0; iG < Gvecs.size(); iG++)
232  {
234  PosType G = Gvecs[iG];
235  double G2 = dot(G, G);
237  for (int j = 0; j < OHMMS_DIM; j++)
238  index[j] = (gint[j] + SplineDim[j]) % SplineDim[j];
239 
240  const RealType xxx(vol * (4.0 * M_PI * volInv / G2 - f_G[iG]));
241  if (!(index[0] == 0 && index[1] == 0 && index[2] == 0))
242  {
243  GBox(index[0], index[1], index[2]) = xxx * Rho_G[iG];
244  Vconst -= halfvol * xxx * norm(Rho_G[iG]);
245  //GBox(index[0], index[1], index[2]) = vol *
246  // Rho_G[iG] * (4.0*M_PI*volInv/G2 - f_G[iG]);
247  //Vconst -= 0.5 * vol * vol * norm(Rho_G[iG])
248  // * (4.0*M_PI*volInv/G2 - f_G[iG]);
249  }
250  }
251  // G=0 component calculated separately
252  //GBox(0,0,0) = -vol * f_0 * Rho_G[0];
253  //Vconst += 0.5 * vol * vol * f_0 * norm(Rho_G[0]);
254  const RealType volf = vol * f_0;
255  GBox(0, 0, 0) = -volf * Rho_G[0];
256  Vconst += halfvol * volf * norm(Rho_G[0]);
257 
258  app_log() << " Constant potential = " << Vconst << std::endl;
259  fftw_plan fft = fftw_plan_dft_3d(SplineDim[0], SplineDim[1], SplineDim[2], (fftw_complex*)GBox.data(),
260  (fftw_complex*)rBox.data(), -1, FFTW_ESTIMATE);
261  fftw_execute(fft);
262  fftw_destroy_plan(fft);
263  for (int i0 = 0; i0 < SplineDim[0]; i0++)
264  for (int i1 = 0; i1 < SplineDim[1]; i1++)
265  for (int i2 = 0; i2 < SplineDim[2]; i2++)
266  splineData(i0, i1, i2) = real(rBox(i0, i1, i2));
267  BCtype_d bc0, bc1, bc2;
268  Ugrid grid0, grid1, grid2;
269  grid0.start = 0.0;
270  grid0.end = 1.0;
271  grid0.num = SplineDim[0];
272  grid1.start = 0.0;
273  grid1.end = 1.0;
274  grid1.num = SplineDim[1];
275  grid2.start = 0.0;
276  grid2.end = 1.0;
277  grid2.num = SplineDim[2];
278  bc0.lCode = bc0.rCode = PERIODIC;
279  bc1.lCode = bc1.rCode = PERIODIC;
280  bc2.lCode = bc2.rCode = PERIODIC;
281  VlongSpline =
282  std::shared_ptr<UBspline_3d_d>(create_UBspline_3d_d(grid0, grid1, grid2, bc0, bc1, bc2, splineData.data()),
283  destroy_Bspline);
284  // grid0.num = ptcl.Density_r.size(0);
285  // grid1.num = ptcl.Density_r.size(1);
286  // grid2.num = ptcl.Density_r.size(2);
287  // DensitySpline = create_UBspline_3d_d (grid0, grid1, grid2, bc0, bc1, bc2,
288  // ptcl.Density_r.data());
289 }
290 
291 void MPC::initBreakup(const ParticleSet& ptcl)
292 {
293  NParticles = ptcl.getTotalNum();
294  app_log() << "\n === Initializing MPC interaction === " << std::endl;
295  init_gvecs(ptcl);
296  init_f_G(ptcl);
297  init_spline(ptcl);
298  // FILE *fout = fopen ("MPC.dat", "w");
299  // double vol = ptcl.getLattice().Volume;
300  // PosType r0 (0.0, 0.0, 0.0);
301  // PosType r1 (10.26499236, 10.26499236, 10.26499236);
302  // int nPoints=1001;
303  // for (int i=0; i<nPoints; i++) {
304  // double s = (double)i/(double)(nPoints-1);
305  // PosType r = (1.0-s)*r0 + s*r1;
306  // PosType u = ptcl.getLattice().toUnit(r);
307  // double V, rho(0.0);
308  // eval_UBspline_3d_d (VlongSpline, u[0], u[1], u[2], &V);
309  // // eval_UBspline_3d_d (DensitySpline, u[0], u[1], u[2], &rho);
310  // fprintf (fout, "%6.4f %14.10e %14.10e\n", s, V, rho);
311  // }
312  // fclose(fout);
313  app_log() << " === MPC interaction initialized === \n\n";
314 }
315 
316 std::unique_ptr<OperatorBase> MPC::makeClone(ParticleSet& qp, TrialWaveFunction& psi)
317 {
318  auto newMPC = std::make_unique<MPC>(*this);
319  return newMPC;
320 }
321 
323 {
324  const auto& d_aa = P.getDistTableAA(d_aa_ID);
325  RealType SR = 0.0;
326  const RealType cone(1);
327  for (size_t ipart = 0; ipart < NParticles; ipart++)
328  {
329  RealType esum(0);
330  const auto& dist = d_aa.getDistRow(ipart);
331  for (size_t j = 0; j < ipart; ++j)
332  esum += cone / dist[j];
333  SR += esum;
334  }
335  return SR;
336 }
337 
339 {
340  RealType LR = 0.0;
341  double val;
342  for (int i = 0; i < NParticles; i++)
343  {
344  //PosType r = P.R[i];
345  //PosType u = P.getLattice().toUnit(r);
346  PosType u = P.getLattice().toUnit(P.R[i]);
347  for (int j = 0; j < OHMMS_DIM; j++)
348  u[j] -= std::floor(u[j]);
349  eval_UBspline_3d_d(VlongSpline.get(), u[0], u[1], u[2], &val);
350  LR += val;
351  }
352  return LR;
353 }
354 
356 {
357  value_ = evalSR(P) + evalLR(P) + Vconst;
358  return value_;
359 }
360 
361 bool MPC::put(xmlNodePtr cur)
362 {
363  Ecut = -1.0;
364  OhmmsAttributeSet attribs;
365  attribs.add(Ecut, "cutoff");
366  attribs.put(cur);
367  if (Ecut < 0.0)
368  {
369  Ecut = 30.0;
370  app_log() << " MPC cutoff not found. Set using \"cutoff\" attribute.\n"
371  << " Setting to default value of " << Ecut << std::endl;
372  }
373  return true;
374 }
375 
376 } // namespace qmcplusplus
double Ecut
Definition: MPC.h:39
void resetTargetParticleSet(ParticleSet &P) override
Reset the data with the target ParticleSet.
Definition: MPC.cpp:33
std::shared_ptr< UBspline_3d_d > VlongSpline
Definition: MPC.h:36
std::array< size_t, OHMMS_DIM > SplineDim
Definition: MPC.h:43
MPC(ParticleSet &ref, double cutoff)
Definition: MPC.cpp:35
std::vector< ComplexType > Rho_G
Definition: MPC.h:42
helper functions for EinsplineSetBuilder
Definition: Configuration.h:43
const DistanceTableAA & getDistTableAA(int table_ID) const
get a distance table by table_ID and dyanmic_cast to DistanceTableAA
Return_t evaluate(ParticleSet &P) override
Evaluate the local energy contribution of this component.
Definition: MPC.cpp:355
const int d_aa_ID
Definition: MPC.h:46
MakeReturn< UnaryNode< FnFabs, typename CreateLeaf< Vector< T1, C1 > >::Leaf_t > >::Expression_t abs(const Vector< T1, C1 > &l)
double extrap(int N, TinyVector< double, 3 > g_124)
Definition: MPC.cpp:129
size_t getTotalNum() const
Definition: ParticleSet.h:493
std::ostream & app_log()
Definition: OutputManager.h:65
bool put(xmlNodePtr cur)
assign attributes to the set
Definition: AttributeSet.h:55
MakeReturn< UnaryNode< FnSin, typename CreateLeaf< Vector< T1, C1 > >::Leaf_t > >::Expression_t sin(const Vector< T1, C1 > &l)
Type_t * data()
Definition: OhmmsArray.h:87
constexpr std::complex< float > cone
Definition: BLAS.hpp:50
Return_t evalLR(ParticleSet &P) const
Definition: MPC.cpp:338
float real(const float &c)
real part of a scalar. Cannot be replaced by std::real due to AFQMC specific needs.
void init_f_G(const ParticleSet &ptcl)
Definition: MPC.cpp:165
double f_0
Definition: MPC.h:61
#define OHMMS_DIM
Definition: config.h:64
void init_gvecs(const ParticleSet &ptcl)
Definition: MPC.cpp:43
Specialized paritlce class for atomistic simulations.
Definition: ParticleSet.h:55
MakeReturn< UnaryNode< FnCos, typename CreateLeaf< Vector< T1, C1 > >::Leaf_t > >::Expression_t cos(const Vector< T1, C1 > &l)
double norm(const zVec &c)
Definition: VectorOps.h:118
class to handle a set of attributes of an xmlNode
Definition: AttributeSet.h:24
Return_t evalSR(ParticleSet &P) const
Definition: MPC.cpp:322
int MaxDim
Definition: MPC.h:44
std::vector< ComplexType > Density_G
Definition: ParticleSet.h:98
ParticlePos R
Position.
Definition: ParticleSet.h:79
void init_spline(const ParticleSet &ptcl)
Definition: MPC.cpp:221
int NParticles
Definition: MPC.h:67
~MPC() override
copy constructor
std::vector< RealType > f_G
Definition: MPC.h:59
FullPrecRealType Return_t
type of return value of evaluate
Definition: OperatorBase.h:64
MakeReturn< UnaryNode< FnSqrt, typename CreateLeaf< Vector< T1, C1 > >::Leaf_t > >::Expression_t sqrt(const Vector< T1, C1 > &l)
Class to represent a many-body trial wave function.
Tensor< T, D > inverse(const Tensor< T, D > &a)
Definition: TensorOps.h:879
Return_t value_
current value
Definition: OperatorBase.h:524
void initBreakup(const ParticleSet &ptcl)
Definition: MPC.cpp:291
const auto & getLattice() const
Definition: ParticleSet.h:251
Tensor< typename BinaryReturn< T1, T2, OpMultiply >::Type_t, D > dot(const AntiSymTensor< T1, D > &lhs, const AntiSymTensor< T2, D > &rhs)
std::vector< PosType > Gvecs
Definition: MPC.h:41
std::unique_ptr< OperatorBase > makeClone(ParticleSet &qp, TrialWaveFunction &psi) override
Definition: MPC.cpp:316
double Vconst
Definition: MPC.h:38
A D-dimensional Array class based on PETE.
Definition: OhmmsArray.h:25
void add(PDT &aparam, const std::string &aname, std::vector< PDT > candidate_values={}, TagStatus status=TagStatus::OPTIONAL)
add a new attribute
Definition: AttributeSet.h:42
whether full table needs to be ready at anytime or not after donePbyP Optimization can be implemented...
void compute_g_G(const ParticleSet &ptcl, double &g_0_N, std::vector< double > &g_G_N, int N)
Definition: MPC.cpp:78
std::vector< TinyVector< int, OHMMS_DIM > > DensityReducedGvecs
Particle density in G-space for MPC interaction.
Definition: ParticleSet.h:97
Declaration of a MCWalkerConfiguration.
std::vector< TinyVector< int, OHMMS_DIM > > Gints
Definition: MPC.h:40
MakeReturn< UnaryNode< FnFloor, typename CreateLeaf< Vector< T1, C1 > >::Leaf_t > >::Expression_t floor(const Vector< T1, C1 > &l)
bool put(xmlNodePtr cur) override
Do nothing.
Definition: MPC.cpp:361