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 //                    Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
9 //                    Jeremy McMinnis, jmcminis@gmail.com, University of Illinois at Urbana-Champaign
10 //                    Mark A. Berrill, berrillma@ornl.gov, Oak Ridge National Laboratory
11 //
12 // File created by: Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
13 //////////////////////////////////////////////////////////////////////////////////////
14 
15 
16 #include "QMCHamiltonians/ECPComponentBuilder.h"
17 #include "QMCHamiltonians/NonLocalECPComponent.h"
18 #include "Numerics/OneDimCubicSpline.h"
19 #include "OhmmsData/AttributeSet.h"
20 #include "Utilities/SimpleParser.h"
21 //#include "Utilities/IteratorUtility.h"
22 #ifdef QMC_CUDA
23 #include <cuda_runtime_api.h>
24 #endif
25 
26 namespace qmcplusplus
27 {
28 //this is abinit/siesta format
buildSemiLocalAndLocal(std::vector<xmlNodePtr> & semiPtr)29 void ECPComponentBuilder::buildSemiLocalAndLocal(std::vector<xmlNodePtr>& semiPtr)
30 {
31   app_log() << "    ECPComponentBuilder::buildSemiLocalAndLocal " << std::endl;
32   if (grid_global == 0)
33     myComm->barrier_and_abort("ECPComponentBuilder::buildSemiLocalAndLocal. Global grid needs to be defined.\n");
34   // There should only be one semilocal tag
35   if (semiPtr.size() > 1)
36   {
37     std::stringstream err_msg;
38     err_msg << "ECPComponentBuilder::buildSemiLocalAndLocal. "
39             << "We have more than one semilocal sections in the PP xml file";
40     myComm->barrier_and_abort(err_msg.str());
41   }
42   RealType rmax = -1;
43   //attributes: initailize by defaults
44   std::string eunits("hartree");
45   std::string format("r*V");
46   std::string lloc;
47   int ndown = 1;
48   int nup   = 0;
49   int nso   = 0;
50   Llocal    = -1;
51   OhmmsAttributeSet aAttrib;
52   int quadRule = -1;
53   aAttrib.add(eunits, "units");
54   aAttrib.add(format, "format");
55   aAttrib.add(ndown, "npots-down");
56   aAttrib.add(nup, "npots-up");
57   aAttrib.add(Llocal, "l-local");
58   aAttrib.add(quadRule, "nrule");
59   aAttrib.add(Srule, "srule");
60   aAttrib.add(nso, "npots-so");
61 
62   xmlNodePtr cur_semilocal = semiPtr[0];
63   aAttrib.put(cur_semilocal);
64 
65   if (quadRule > -1 && Nrule > -1)
66   {
67     app_warning() << " Nrule setting found in both qmcpack input (Nrule = " << Nrule
68                   << ") and pseudopotential file (Nrule = " << quadRule << ")."
69                   << " Using nrule setting in qmcpack input file." << std::endl;
70   }
71   else if (quadRule > -1 && Nrule == -1)
72   {
73     app_log() << " Nrule setting found in pseudopotential file and used." << std::endl;
74     Nrule = quadRule;
75   }
76   else if (quadRule == -1 && Nrule > -1)
77     app_log() << " Nrule setting found in qmcpack input file and used." << std::endl;
78   else
79   {
80     //Sperical quadrature rules set by exact integration up to lmax of
81     //nonlocal channels.
82     //From J. Chem. Phys. 95 (3467) (1991)
83     //Keeping Nrule = 4 as default for lmax <= 5.
84     switch (pp_nonloc->lmax)
85     {
86     case 0:
87     case 1:
88     case 2:
89     case 3:
90     case 4:
91     case 5:
92       Nrule = 4;
93       break;
94     case 6:
95     case 7:
96       Nrule = 6;
97       break;
98     case 8:
99     case 9:
100     case 10:
101     case 11:
102       Nrule = 7;
103       break;
104     default:
105       myComm->barrier_and_abort("Default value for pseudopotential nrule not determined.");
106       break;
107     }
108     app_warning() << "Nrule was not determined from qmcpack input or pseudopotential file. Setting sensible default."
109                   << std::endl;
110   }
111 
112 
113   RealType Vprefactor = 1.0;
114   if (eunits.find("ydberg") < eunits.size())
115   {
116     app_log() << "    Input energy unit = Rydberg " << std::endl;
117     Vprefactor = 0.5;
118   }
119   else
120   {
121     app_log() << "    Assuming Hartree unit" << std::endl;
122   }
123   bool is_r_times_V(true);
124   if (format == "r*V")
125     is_r_times_V = true;
126   else if (format == "V")
127     is_r_times_V = false;
128   else
129   {
130     std::stringstream err_msg;
131     err_msg << "ECPComponentBuilder::buildSemiLocalAndLocal."
132             << "Unrecognized format \"" << format << "\" in PP file.\n";
133     myComm->barrier_and_abort(err_msg.str());
134   }
135   // We cannot construct the potentials as we construct them since
136   // we may not know which one is local yet.
137 
138   std::vector<int> angList;
139   std::vector<int> angListSO; //For spin-orbit, if it exists
140   std::vector<xmlNodePtr> vpsPtr;
141   std::vector<xmlNodePtr> vpsoPtr; //For spin-orbit, if it exists.
142   Lmax   = -1;
143   LmaxSO = -1;
144   // Now read vps sections
145   xmlNodePtr cur_vps = cur_semilocal->children;
146   while (cur_vps != NULL)
147   {
148     std::string vname((const char*)cur_vps->name);
149     if (vname == "vps")
150     {
151       OhmmsAttributeSet aAttrib;
152       std::string lstr("s");
153       RealType rc = -1.0;
154       aAttrib.add(lstr, "l");
155       aAttrib.add(rc, "cutoff");
156       aAttrib.put(cur_vps);
157       rmax = std::max(rmax, rc);
158       if (angMon.find(lstr) == angMon.end())
159       {
160         std::stringstream err_msg;
161         err_msg << "ECPComponentBuilder::buildSemiLocalAndLocal. "
162                 << "Requested angular momentum " << lstr << " not available.\n";
163         myComm->barrier_and_abort(err_msg.str());
164       }
165       int l = angMon[lstr];
166       angList.push_back(l);
167       vpsPtr.push_back(cur_vps);
168       Lmax = std::max(Lmax, l); //count the maximum L
169     }
170     else if (vname == "vps_so") //This accumulates the spin-orbit corrections, if defined.
171     {
172       OhmmsAttributeSet aAttrib;
173       std::string lstr("s");
174       RealType rc = -1.0;
175       aAttrib.add(lstr, "l");
176       aAttrib.add(rc, "cutoff");
177       aAttrib.put(cur_vps);
178       rmax = std::max(rmax, rc);
179       if (angMon.find(lstr) == angMon.end())
180       {
181         std::stringstream err_msg;
182         err_msg << "ECPComponentBuilder::buildSemiLocalAndLocal. "
183                 << "Requested angular momentum " << lstr << " not available for SO.\n";
184         myComm->barrier_and_abort(err_msg.str());
185       }
186       int l = angMon[lstr];
187       angListSO.push_back(l);
188       vpsoPtr.push_back(cur_vps);
189       LmaxSO = std::max(LmaxSO, l); //count the maximum L
190     }
191     cur_vps = cur_vps->next;
192   }
193   if (rmax < 0)
194     rmax = 1.8;
195   if (angList.size() == 1)
196   {
197     Llocal = Lmax;
198     app_log() << "    Only one vps is found. Set the local component=" << Lmax << std::endl;
199   }
200 
201   if (angListSO.size() != nso)
202   {
203     std::stringstream ssout;
204     ssout << "Error. npots-so=" << angListSO.size() << " while declared number of SO channels is " << nso << std::endl;
205     std::string outstring("");
206     outstring = ssout.str();
207 
208     myComm->barrier_and_abort(outstring.c_str());
209   }
210   int npts = grid_global->size();
211   Matrix<mRealType> vnn(angList.size(), npts);
212   for (int l = 0; l < angList.size(); l++)
213   {
214     std::vector<mRealType> vt(npts);
215     xmlNodePtr c = vpsPtr[l]->children;
216     while (c != NULL)
217     {
218       if (xmlStrEqual(c->name, (const xmlChar*)"radfunc"))
219       {
220         xmlNodePtr c1 = c->children;
221         while (c1 != NULL)
222         {
223           if (xmlStrEqual(c1->name, (const xmlChar*)"data"))
224             putContent(vt, c1);
225           c1 = c1->next;
226         }
227       }
228       c = c->next;
229     }
230     //copy the numerical data with the correct map
231     copy(vt.begin(), vt.end(), vnn[angList[l]]);
232   }
233 
234   //Grabbing the spin-orbit functions from XML.
235   Matrix<mRealType> vnnso(angListSO.size(), npts);
236   for (int l = 0; l < angListSO.size(); l++)
237   {
238     std::vector<mRealType> vtso(npts);
239     xmlNodePtr c = vpsoPtr[l]->children;
240     while (c != NULL)
241     {
242       if (xmlStrEqual(c->name, (const xmlChar*)"radfunc"))
243       {
244         xmlNodePtr c1 = c->children;
245         while (c1 != NULL)
246         {
247           if (xmlStrEqual(c1->name, (const xmlChar*)"data"))
248             putContent(vtso, c1);
249           c1 = c1->next;
250         }
251       }
252       c = c->next;
253     }
254     //copy the numerical data with the correct map
255     //So this is weird, but I feel like l should be the proper index for vnnso,
256     //with angListSO[l] being the actual angular momentum channel referred to by l.
257     //This differs from the parsing of the nonlocal pseudopotential piece, but whatever.
258     copy(vtso.begin(), vtso.end(), vnnso[l]);
259   }
260 
261 
262   ////rather stupid to do this but necessary
263   //vector<RealType> temp(npts);
264   //for(int i=0; i<npts; i++) temp[i]=grid_global->r(i);
265   if (!is_r_times_V)
266   {
267     app_log() << "  Input pseudopotential is converted into r*V" << std::endl;
268     for (int i = 0; i < vnn.rows(); i++)
269       for (int j = 0; j < npts; j++)
270         vnn[i][j] *= grid_global->r(j);
271     for (int i = 0; i < vnnso.rows(); i++)
272       for (int j = 0; j < npts; j++)
273         vnnso[i][j] *= grid_global->r(j);
274   }
275   app_log() << "   Number of angular momentum channels " << angList.size() << std::endl;
276   app_log() << "   Maximum angular momentum channel " << Lmax << std::endl;
277   doBreakUp(angList, vnn, rmax, Vprefactor);
278 
279   //If any spinorbit terms are found...
280   if (nso > 0)
281     buildSO(angListSO, vnnso, rmax, 1.0);
282   else
283   {
284     //No SO channels found. Delete pp_so
285     pp_so.reset();
286   }
287 }
288 
289 //Most of this is copied directly from doBreakUp, but is separated to prevent from cluttering doBreakUp.
290 //This function takes the input grid and input SO potential table, interpolates it onto a linear grid if necessary
291 //via a cubic spline, and then adds the final splined RadialPotentialType object to the SOECPComponent object.
buildSO(const std::vector<int> & angList,const Matrix<mRealType> & vnnso,RealType rmax,mRealType Vprefactor)292 void ECPComponentBuilder::buildSO(const std::vector<int>& angList,
293                                   const Matrix<mRealType>& vnnso,
294                                   RealType rmax,
295                                   mRealType Vprefactor)
296 {
297   const int max_points = 100000;
298   app_log() << "   Creating a Linear Grid Rmax=" << rmax << std::endl;
299   //this is a new grid
300   mRealType d                 = 1e-4;
301   LinearGrid<RealType>* agrid = new LinearGrid<RealType>;
302   // If the global grid is already linear, do not interpolate the data
303   int ng;
304   if (grid_global->getGridTag() == LINEAR_1DGRID)
305   {
306     ng = (int)std::ceil(rmax * grid_global->DeltaInv) + 1;
307     if (ng <= max_points)
308     {
309       app_log() << "  Using global grid with delta = " << grid_global->Delta << std::endl;
310       rmax = grid_global->Delta * (ng - 1);
311       agrid->set(0.0, rmax, ng);
312     }
313     else
314       agrid->set(0.0, rmax, max_points);
315   }
316   else
317   {
318     ng = std::min(max_points, static_cast<int>(rmax / d) + 1);
319     agrid->set(0, rmax, ng);
320   }
321   // This is critical!!!
322   // If d is not reset, we generate an error in the interpolated PP!
323   d        = agrid->Delta;
324   int ngIn = vnnso.cols() - 2;
325   std::vector<RealType> newP(ng);
326   std::vector<mRealType> newPin(ngIn);
327   for (int l = 0; l < angList.size(); l++)
328   {
329     const mRealType* restrict vp = vnnso[l];
330     for (int i = 0; i < ngIn; i++)
331       newPin[i] = Vprefactor * vp[i];
332 
333     OneDimCubicSpline<mRealType> infunc(grid_global, newPin);
334     infunc.spline(0, 0.0, ngIn - 1, 0.0);
335     for (int i = 1; i < ng - 1; i++)
336     {
337       mRealType r = d * i;
338       newP[i]     = infunc.splint(r) / r;
339     }
340     newP[0]                  = newP[1];
341     newP[ng - 1]             = 0.0;
342     RadialPotentialType* app = new RadialPotentialType(agrid, newP);
343     app->spline();
344     pp_so->add(angList[l], app);
345   }
346   NumSO       = angList.size();
347   pp_so->Rmax = rmax;
348 }
349 
parseCasino(const std::string & fname,xmlNodePtr cur)350 bool ECPComponentBuilder::parseCasino(const std::string& fname, xmlNodePtr cur)
351 {
352   app_log() << "   Start ECPComponentBuilder::parseCasino" << std::endl;
353   RealType rmax = 2.0;
354   Llocal        = -1;
355   Lmax          = -1;
356   OhmmsAttributeSet aAttrib;
357   aAttrib.add(rmax, "cutoff");
358   aAttrib.add(Llocal, "l-local");
359   aAttrib.add(Lmax, "lmax");
360   aAttrib.add(Nrule, "nrule");
361   aAttrib.put(cur);
362 
363   std::ifstream fin(fname.c_str(), std::ios_base::in);
364   if (!fin)
365   {
366     app_error() << "Could not open file " << fname << std::endl;
367     myComm->barrier_and_abort("ECPComponentBuilder::parseCasino");
368   }
369   if (!pp_nonloc)
370     pp_nonloc = std::make_unique<NonLocalECPComponent>();
371   OhmmsAsciiParser aParser;
372   int npts = 0, idummy;
373   std::string eunits("rydberg");
374   app_log() << "    ECPComponentBuilder::parseCasino" << std::endl;
375   aParser.skiplines(fin, 1); //Header
376   aParser.skiplines(fin, 1); //Atomic number and pseudo-charge
377   aParser.getValue(fin, AtomicNumber, Zeff);
378   app_log() << "      Atomic number = " << AtomicNumber << "  Zeff = " << Zeff << std::endl;
379   aParser.skiplines(fin, 1); //Energy units (rydberg/hartree/ev):
380   aParser.getValue(fin, eunits);
381   app_log() << "      Unit of the potentials = " << eunits << std::endl;
382   mRealType Vprefactor = (eunits == "rydberg") ? 0.5 : 1.0;
383   aParser.skiplines(fin, 1); //Angular momentum of local component (0=s,1=p,2=d..)
384   aParser.getValue(fin, idummy);
385   if (Lmax < 0)
386     Lmax = idummy;
387   aParser.skiplines(fin, 1); //NLRULE override (1) VMC/DMC (2) config gen (0 ==> input/default value)
388   aParser.skiplines(fin, 1); //0 0, not sure what to do yet
389   aParser.skiplines(fin, 1); //Number of grid points
390   aParser.getValue(fin, npts);
391   app_log() << "      Input Grid size = " << npts << std::endl;
392   std::vector<mRealType> temp(npts);
393   aParser.skiplines(fin, 1); //R(i) in atomic units
394   aParser.getValues(fin, temp.begin(), temp.end());
395   //create a global grid of numerical type
396   grid_global = new NumericalGrid<mRealType>(temp);
397   Matrix<mRealType> vnn(Lmax + 1, npts);
398   for (int l = 0; l <= Lmax; l++)
399   {
400     aParser.skiplines(fin, 1);
401     aParser.getValues(fin, vnn[l], vnn[l] + npts);
402   }
403   std::vector<int> angList(Lmax + 1);
404   for (int l = 0; l <= Lmax; l++)
405     angList[l] = l;
406   // Now, check to see what maximum cutoff should be
407   if (vnn.size() > 1)
408   {
409     const double tolerance = 1.0e-5;
410     double rc_check        = grid_global->r(npts - 1);
411     for (int j = npts - 1; j > 0; j--)
412     {
413       bool closeEnough = true;
414       for (int i = 0; i < vnn.rows(); i++)
415         for (int k = i + 1; k < vnn.rows(); k++)
416           if (std::abs(vnn[i][j] - vnn[k][j]) > tolerance)
417             closeEnough = false;
418       if (!closeEnough)
419       {
420         rc_check = grid_global->r(j);
421         break;
422       }
423     }
424     app_log() << "  Maxium cutoff for non-local pseudopotentials " << rc_check << std::endl;
425   }
426   doBreakUp(angList, vnn, rmax, Vprefactor);
427   SetQuadratureRule(Nrule);
428   app_log() << "    Non-local pseudopotential parameters" << std::endl;
429   pp_nonloc->print(app_log());
430   return true;
431 }
432 
433 
434 /** Separate local from non-local potentials
435  * @param angList angular momentum list
436  * @param vnn semilocal tables of size (angList.size(),global_grid->size())
437  * @param rmax cutoff radius
438  * @param Vprefactor conversion factor to Hartree
439  *
440  * Note that local pseudopotential is r*V !!!
441  */
doBreakUp(const std::vector<int> & angList,const Matrix<mRealType> & vnn,RealType rmax,mRealType Vprefactor)442 void ECPComponentBuilder::doBreakUp(const std::vector<int>& angList,
443                                     const Matrix<mRealType>& vnn,
444                                     RealType rmax,
445                                     mRealType Vprefactor)
446 {
447 #ifdef QMC_CUDA
448   int device;
449   cudaGetDevice(&device);
450   cudaDeviceProp deviceProp;
451   cudaGetDeviceProperties(&deviceProp, device);
452   const int max_points = deviceProp.maxTexture1D - 1;
453 #else
454   const int max_points = 100000;
455 #endif
456   app_log() << "   Creating a Linear Grid Rmax=" << rmax << std::endl;
457   //this is a new grid
458   mRealType d                 = 1e-4;
459   LinearGrid<RealType>* agrid = new LinearGrid<RealType>;
460   // If the global grid is already linear, do not interpolate the data
461   int ng;
462   if (grid_global->getGridTag() == LINEAR_1DGRID)
463   {
464     ng = (int)std::ceil(rmax * grid_global->DeltaInv) + 1;
465     if (ng <= max_points)
466     {
467       app_log() << "  Using global grid with delta = " << grid_global->Delta << std::endl;
468       rmax = grid_global->Delta * (ng - 1);
469       agrid->set(0.0, rmax, ng);
470     }
471     else
472       agrid->set(0.0, rmax, max_points);
473   }
474   else
475   {
476     ng = std::min(max_points, static_cast<int>(rmax / d) + 1);
477     agrid->set(0, rmax, ng);
478   }
479   // This is critical!!!
480   // If d is not reset, we generate an error in the interpolated PP!
481   d        = agrid->Delta;
482   int ngIn = vnn.cols() - 2;
483   if (Llocal == -1 && Lmax > 0)
484   {
485     app_error() << "The local channel is not specified in the pseudopotential file.\n"
486                 << "Please add \'l-local=\"n\"\' attribute the semilocal section of the fsatom XML file.\n";
487     myComm->barrier_and_abort("ECPComponentBuilder::doBreakUp");
488     // Llocal = Lmax;
489   }
490   //find the index of local
491   int iLlocal = -1;
492   for (int l = 0; l < angList.size(); l++)
493     if (angList[l] == Llocal)
494       iLlocal = l;
495   std::vector<RealType> newP(ng);
496   std::vector<mRealType> newPin(ngIn);
497   for (int l = 0; l < angList.size(); l++)
498   {
499     if (angList[l] == Llocal)
500       continue;
501     const mRealType* restrict vp    = vnn[angList[l]];
502     const mRealType* restrict vpLoc = vnn[iLlocal];
503     int ll                          = angList[l];
504     for (int i = 0; i < ngIn; i++)
505       newPin[i] = Vprefactor * (vp[i] - vpLoc[i]);
506     OneDimCubicSpline<mRealType> infunc(grid_global, newPin);
507     infunc.spline(0, 0.0, ngIn - 1, 0.0);
508     for (int i = 1; i < ng - 1; i++)
509     {
510       mRealType r = d * i;
511       newP[i]     = infunc.splint(r) / r;
512     }
513     newP[0]                  = newP[1];
514     newP[ng - 1]             = 0.0;
515     RadialPotentialType* app = new RadialPotentialType(agrid, newP);
516     app->spline();
517     pp_nonloc->add(angList[l], app);
518   }
519   NumNonLocal = Lmax;
520   if (Llocal == Lmax)
521     Lmax--;
522   if (NumNonLocal)
523   {
524     pp_nonloc->lmax = Lmax;
525     pp_nonloc->Rmax = rmax;
526   }
527   else
528   {
529     //only one component, remove non-local potentials
530     pp_nonloc.reset();
531   }
532   {
533     // Spline local potential on original grid
534     newPin[0]                       = 0.0;
535     mRealType vfac                  = -Vprefactor / Zeff;
536     const mRealType* restrict vpLoc = vnn[iLlocal];
537     for (int i = 0; i < ngIn; i++)
538       newPin[i] = vfac * vpLoc[i];
539     double dy0 = (newPin[1] - newPin[0]) / ((*grid_global)[1] - (*grid_global)[0]);
540     OneDimCubicSpline<mRealType> infunc(grid_global, newPin);
541     infunc.spline(0, dy0, ngIn - 1, 0.0);
542     int m                          = grid_global->size();
543     double loc_max                 = grid_global->r(m - 1);
544     int nloc                       = (int)std::floor(loc_max / d);
545     loc_max                        = (nloc - 1) * d;
546     LinearGrid<RealType>* grid_loc = new LinearGrid<RealType>;
547     grid_loc->set(0.0, loc_max, nloc);
548     app_log() << "   Making L=" << Llocal << " a local potential with a radial cutoff of " << loc_max << std::endl;
549     std::vector<RealType> newPloc(nloc);
550     for (int i = 1; i < nloc - 1; i++)
551     {
552       mRealType r = d * i;
553       newPloc[i]  = infunc.splint(r);
554     }
555     newPloc[0]        = 0.0;
556     newPloc[nloc - 1] = 1.0;
557     pp_loc            = std::make_unique<RadialPotentialType>(grid_loc, newPloc);
558     pp_loc->spline(0, dy0, nloc - 1, 0.0);
559     // for (double r=0.0; r<3.50001; r+=0.001)
560     //   fprintf (stderr, "%10.5f %10.5f\n", r, pp_loc->splint(r));
561   }
562 }
563 
564 } // namespace qmcplusplus
565