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