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: Miguel Morales, moralessilva2@llnl.gov, Lawrence Livermore National Laboratory
8 //                    Jeremy McMinnis, jmcminis@gmail.com, University of Illinois at Urbana-Champaign
9 //                    Jaron T. Krogel, krogeljt@ornl.gov, Oak Ridge National Laboratory
10 //                    Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
11 //                    Ye Luo, yeluo@anl.gov, Argonne National Laboratory
12 //                    Mark A. Berrill, berrillma@ornl.gov, Oak Ridge National Laboratory
13 //
14 // File created by: Jeongnim Kim, jeongnim.kim@intel.com, Intel Corp.
15 //////////////////////////////////////////////////////////////////////////////////////
16 
17 
18 #include "LCAOrbitalBuilder.h"
19 #include "OhmmsData/AttributeSet.h"
20 #include "QMCWaveFunctions/SPOSet.h"
21 #include "QMCWaveFunctions/LCAO/NGFunctor.h"
22 #include "QMCWaveFunctions/LCAO/MultiQuinticSpline1D.h"
23 #include "QMCWaveFunctions/LCAO/SoaCartesianTensor.h"
24 #include "QMCWaveFunctions/LCAO/SoaSphericalTensor.h"
25 #include "QMCWaveFunctions/LCAO/SoaAtomicBasisSet.h"
26 #include "QMCWaveFunctions/LCAO/SoaLocalizedBasisSet.h"
27 #include "QMCWaveFunctions/LCAO/LCAOrbitalSet.h"
28 //#include "QMCWaveFunctions/LCAO/RadialOrbitalSetBuilder.h"
29 #include "QMCWaveFunctions/LCAO/AOBasisBuilder.h"
30 #include "QMCWaveFunctions/LCAO/MultiFunctorAdapter.h"
31 #if !defined(QMC_COMPLEX)
32 #include "QMCWaveFunctions/LCAO/LCAOrbitalSetWithCorrection.h"
33 #include "QMCWaveFunctions/LCAO/CuspCorrectionConstruction.h"
34 #endif
35 #include "hdf/hdf_archive.h"
36 #include "Message/CommOperators.h"
37 #include "Utilities/ProgressReportEngine.h"
38 #include "config/stdlib/math.hpp"
39 
40 namespace qmcplusplus
41 {
42 /** traits for a localized basis set; used by createBasisSet
43    *
44    * T radial function value type
45    * ORBT orbital value type, can be complex
46    * ROT {0=numuerica;, 1=gto; 2=sto}
47    * SH {0=cartesian, 1=spherical}
48    * If too confusing, inroduce enumeration.
49    */
50 template<typename T, typename ORBT, int ROT, int SH>
51 struct ao_traits
52 {};
53 
54 /** specialization for numerical-cartesian AO */
55 template<typename T, typename ORBT>
56 struct ao_traits<T, ORBT, 0, 0>
57 {
58   using radial_type  = MultiQuinticSpline1D<T>;
59   using angular_type = SoaCartesianTensor<T>;
60   using ao_type      = SoaAtomicBasisSet<radial_type, angular_type>;
61   using basis_type   = SoaLocalizedBasisSet<ao_type, ORBT>;
62 };
63 
64 /** specialization for numerical-spherical AO */
65 template<typename T, typename ORBT>
66 struct ao_traits<T, ORBT, 0, 1>
67 {
68   using radial_type  = MultiQuinticSpline1D<T>;
69   using angular_type = SoaSphericalTensor<T>;
70   using ao_type      = SoaAtomicBasisSet<radial_type, angular_type>;
71   using basis_type   = SoaLocalizedBasisSet<ao_type, ORBT>;
72 };
73 
74 /** specialization for GTO-cartesian AO */
75 template<typename T, typename ORBT>
76 struct ao_traits<T, ORBT, 1, 0>
77 {
78   using radial_type  = MultiFunctorAdapter<GaussianCombo<T>>;
79   using angular_type = SoaCartesianTensor<T>;
80   using ao_type      = SoaAtomicBasisSet<radial_type, angular_type>;
81   using basis_type   = SoaLocalizedBasisSet<ao_type, ORBT>;
82 };
83 
84 /** specialization for GTO-cartesian AO */
85 template<typename T, typename ORBT>
86 struct ao_traits<T, ORBT, 1, 1>
87 {
88   using radial_type  = MultiFunctorAdapter<GaussianCombo<T>>;
89   using angular_type = SoaSphericalTensor<T>;
90   using ao_type      = SoaAtomicBasisSet<radial_type, angular_type>;
91   using basis_type   = SoaLocalizedBasisSet<ao_type, ORBT>;
92 };
93 
94 /** specialization for STO-spherical AO */
95 template<typename T, typename ORBT>
96 struct ao_traits<T, ORBT, 2, 1>
97 {
98   using radial_type  = MultiFunctorAdapter<SlaterCombo<T>>;
99   using angular_type = SoaSphericalTensor<T>;
100   using ao_type      = SoaAtomicBasisSet<radial_type, angular_type>;
101   using basis_type   = SoaLocalizedBasisSet<ao_type, ORBT>;
102 };
103 
104 
is_same(const xmlChar * a,const char * b)105 inline bool is_same(const xmlChar* a, const char* b) { return !strcmp((const char*)a, b); }
106 
107 using BasisSet_t = LCAOrbitalSet::basis_type;
108 
LCAOrbitalBuilder(ParticleSet & els,ParticleSet & ions,Communicate * comm,xmlNodePtr cur)109 LCAOrbitalBuilder::LCAOrbitalBuilder(ParticleSet& els, ParticleSet& ions, Communicate* comm, xmlNodePtr cur)
110     : SPOSetBuilder("LCAO", comm),
111       targetPtcl(els),
112       sourcePtcl(ions),
113       h5_path(""),
114       SuperTwist(0.0),
115       doCuspCorrection(false)
116 {
117   ClassName = "LCAOrbitalBuilder";
118   ReportEngine PRE(ClassName, "createBasisSet");
119 
120   std::string cuspC("no"); // cusp correction
121   OhmmsAttributeSet aAttrib;
122   aAttrib.add(cuspC, "cuspCorrection");
123   aAttrib.add(h5_path, "href");
124   aAttrib.add(PBCImages, "PBCimages");
125   aAttrib.add(SuperTwist, "twist");
126   aAttrib.put(cur);
127 
128   if (cuspC == "yes")
129     doCuspCorrection = true;
130   //Evaluate the Phase factor. Equals 1 for OBC.
131   EvalPeriodicImagePhaseFactors(SuperTwist, PeriodicImagePhaseFactors);
132 
133   // no need to wait but load the basis set
134   processChildren(cur, [&](const std::string& cname, const xmlNodePtr element) {
135     if (cname == "basisset")
136     {
137       XMLAttrString basisset_name_input(element, "name");
138       std::string basisset_name(basisset_name_input.empty() ? "LCAOBSet" : basisset_name_input.c_str());
139       if (basisset_map_.find(basisset_name) != basisset_map_.end())
140       {
141         std::ostringstream err_msg;
142         err_msg << "Cannot create basisset " << basisset_name << " which already exists." << std::endl;
143         throw std::runtime_error(err_msg.str());
144       }
145       if (h5_path != "")
146         basisset_map_[basisset_name] = loadBasisSetFromH5(element);
147       else
148         basisset_map_[basisset_name] = loadBasisSetFromXML(element, cur);
149     }
150   });
151 
152   // deprecated h5 basis set handling when basisset element is missing
153   if (basisset_map_.size() == 0 && h5_path != "")
154   {
155     app_warning() << "!!!!!!! Deprecated input style: missing basisset element. "
156                   << "LCAO needs an explicit basisset XML element. "
157                   << "Fallback on loading an implicit one." << std::endl;
158     basisset_map_["LCAOBSet"] = loadBasisSetFromH5(cur);
159   }
160 
161   if (basisset_map_.size() == 0)
162     throw std::runtime_error("No basisset found in the XML input!");
163 }
164 
~LCAOrbitalBuilder()165 LCAOrbitalBuilder::~LCAOrbitalBuilder()
166 {
167   //properly cleanup
168 }
169 
determineRadialOrbType(xmlNodePtr cur) const170 int LCAOrbitalBuilder::determineRadialOrbType(xmlNodePtr cur) const
171 {
172   std::string keyOpt;
173   std::string transformOpt;
174   OhmmsAttributeSet aAttrib;
175   aAttrib.add(keyOpt, "keyword");
176   aAttrib.add(keyOpt, "key");
177   aAttrib.add(transformOpt, "transform");
178   aAttrib.put(cur);
179 
180   int radialOrbType = -1;
181   if (transformOpt == "yes" || keyOpt == "NMO")
182     radialOrbType = 0;
183   else
184   {
185     if (keyOpt == "GTO")
186       radialOrbType = 1;
187     if (keyOpt == "STO")
188       radialOrbType = 2;
189   }
190   return radialOrbType;
191 }
192 
loadBasisSetFromXML(xmlNodePtr cur,xmlNodePtr parent)193 std::unique_ptr<BasisSet_t> LCAOrbitalBuilder::loadBasisSetFromXML(xmlNodePtr cur, xmlNodePtr parent)
194 {
195   ReportEngine PRE(ClassName, "loadBasisSetFromXML(xmlNodePtr)");
196   int ylm = -1;
197   {
198     xmlNodePtr cur1 = cur->xmlChildrenNode;
199     while (cur1 != NULL && ylm < 0)
200     {
201       if (is_same(cur1->name, "atomicBasisSet"))
202       {
203         std::string sph;
204         OhmmsAttributeSet att;
205         att.add(sph, "angular");
206         att.put(cur1);
207         ylm = (sph == "cartesian") ? 0 : 1;
208       }
209       cur1 = cur1->next;
210     }
211   }
212 
213   if (ylm < 0)
214     PRE.error("Missing angular attribute of atomicBasisSet.", true);
215 
216   int radialOrbType = determineRadialOrbType(cur);
217   if (radialOrbType < 0)
218   {
219     app_warning() << "Radial orbital type cannot be determined based on the attributes of basisset line. "
220                   << "Trying the parent element." << std::endl;
221     radialOrbType = determineRadialOrbType(parent);
222   }
223 
224   if (radialOrbType < 0)
225     PRE.error("Unknown radial function for LCAO orbitals. Specify keyword=\"NMO/GTO/STO\" .", true);
226 
227   BasisSet_t* myBasisSet = nullptr;
228   /** process atomicBasisSet per ion species */
229   switch (radialOrbType)
230   {
231   case (0): //numerical
232     app_log() << "  LCAO: SoaAtomicBasisSet<MultiQuintic," << ylm << ">" << std::endl;
233     if (ylm)
234       myBasisSet = createBasisSet<0, 1>(cur);
235     else
236       myBasisSet = createBasisSet<0, 0>(cur);
237     break;
238   case (1): //gto
239     app_log() << "  LCAO: SoaAtomicBasisSet<MultiGTO," << ylm << ">" << std::endl;
240     if (ylm)
241       myBasisSet = createBasisSet<1, 1>(cur);
242     else
243       myBasisSet = createBasisSet<1, 0>(cur);
244     break;
245   case (2): //sto
246     app_log() << "  LCAO: SoaAtomicBasisSet<MultiSTO," << ylm << ">" << std::endl;
247     myBasisSet = createBasisSet<2, 1>(cur);
248     break;
249   default:
250     PRE.error("Cannot construct SoaAtomicBasisSet<ROT,YLM>.", true);
251     break;
252   }
253 
254   return std::unique_ptr<BasisSet_t>(myBasisSet);
255 }
256 
loadBasisSetFromH5(xmlNodePtr parent)257 std::unique_ptr<BasisSet_t> LCAOrbitalBuilder::loadBasisSetFromH5(xmlNodePtr parent)
258 {
259   ReportEngine PRE(ClassName, "loadBasisSetFromH5()");
260 
261   hdf_archive hin(myComm);
262   int ylm = -1;
263   if (myComm->rank() == 0)
264   {
265     if (!hin.open(h5_path, H5F_ACC_RDONLY))
266       PRE.error("Could not open H5 file", true);
267     if (!hin.push("basisset"))
268       PRE.error("Could not open basisset group in H5; Probably Corrupt H5 file", true);
269 
270     std::string sph;
271     std::string ElemID0 = "atomicBasisSet0";
272     if (!hin.push(ElemID0.c_str()))
273       PRE.error("Could not open  group Containing atomic Basis set in H5; Probably Corrupt H5 file", true);
274     if (!hin.readEntry(sph, "angular"))
275       PRE.error("Could not find name of  basisset group in H5; Probably Corrupt H5 file", true);
276     ylm = (sph == "cartesian") ? 0 : 1;
277     hin.close();
278   }
279 
280   myComm->bcast(ylm);
281   if (ylm < 0)
282     PRE.error("Missing angular attribute of atomicBasisSet.", true);
283 
284   int radialOrbType = determineRadialOrbType(parent);
285   if (radialOrbType < 0)
286     PRE.error("Unknown radial function for LCAO orbitals. Specify keyword=\"NMO/GTO/STO\" .", true);
287 
288   BasisSet_t* myBasisSet = nullptr;
289   /** process atomicBasisSet per ion species */
290   switch (radialOrbType)
291   {
292   case (0): //numerical
293     app_log() << "  LCAO: SoaAtomicBasisSet<MultiQuintic," << ylm << ">" << std::endl;
294     if (ylm)
295       myBasisSet = createBasisSetH5<0, 1>();
296     else
297       myBasisSet = createBasisSetH5<0, 0>();
298     break;
299   case (1): //gto
300     app_log() << "  LCAO: SoaAtomicBasisSet<MultiGTO," << ylm << ">" << std::endl;
301     if (ylm)
302       myBasisSet = createBasisSetH5<1, 1>();
303     else
304       myBasisSet = createBasisSetH5<1, 0>();
305     break;
306   case (2): //sto
307     app_log() << "  LCAO: SoaAtomicBasisSet<MultiSTO," << ylm << ">" << std::endl;
308     myBasisSet = createBasisSetH5<2, 1>();
309     break;
310   default:
311     PRE.error("Cannot construct SoaAtomicBasisSet<ROT,YLM>.", true);
312     break;
313   }
314   return std::unique_ptr<BasisSet_t>(myBasisSet);
315 }
316 
317 
318 template<int I, int J>
createBasisSet(xmlNodePtr cur)319 LCAOrbitalBuilder::BasisSet_t* LCAOrbitalBuilder::createBasisSet(xmlNodePtr cur)
320 {
321   ReportEngine PRE(ClassName, "createBasisSet(xmlNodePtr)");
322 
323   using ao_type    = typename ao_traits<RealType, ValueType, I, J>::ao_type;
324   using basis_type = typename ao_traits<RealType, ValueType, I, J>::basis_type;
325 
326   basis_type* mBasisSet = new basis_type(sourcePtcl, targetPtcl);
327 
328   //list of built centers
329   std::vector<std::string> ao_built_centers;
330 
331   /** process atomicBasisSet per ion species */
332   cur = cur->xmlChildrenNode;
333   while (cur != NULL) //loop over unique ioons
334   {
335     std::string cname((const char*)(cur->name));
336 
337     if (cname == "atomicBasisSet")
338     {
339       std::string elementType;
340       std::string sph;
341       OhmmsAttributeSet att;
342       att.add(elementType, "elementType");
343       att.put(cur);
344 
345       if (elementType.empty())
346         PRE.error("Missing elementType attribute of atomicBasisSet.", true);
347 
348       auto it = std::find(ao_built_centers.begin(), ao_built_centers.end(), elementType);
349       if (it == ao_built_centers.end())
350       {
351         AOBasisBuilder<ao_type> any(elementType, myComm);
352         any.put(cur);
353         ao_type* aoBasis = any.createAOSet(cur);
354         if (aoBasis)
355         {
356           //add the new atomic basis to the basis set
357           int activeCenter = sourcePtcl.getSpeciesSet().findSpecies(elementType);
358           mBasisSet->add(activeCenter, aoBasis);
359         }
360         ao_built_centers.push_back(elementType);
361       }
362     }
363     cur = cur->next;
364   } // done with basis set
365   mBasisSet->setBasisSetSize(-1);
366   mBasisSet->setPBCParams(PBCImages, SuperTwist, PeriodicImagePhaseFactors);
367   return mBasisSet;
368 }
369 
370 
371 template<int I, int J>
createBasisSetH5()372 LCAOrbitalBuilder::BasisSet_t* LCAOrbitalBuilder::createBasisSetH5()
373 {
374   ReportEngine PRE(ClassName, "createBasisSetH5(xmlNodePtr)");
375 
376   using ao_type    = typename ao_traits<RealType, ValueType, I, J>::ao_type;
377   using basis_type = typename ao_traits<RealType, ValueType, I, J>::basis_type;
378 
379   basis_type* mBasisSet = new basis_type(sourcePtcl, targetPtcl);
380 
381   //list of built centers
382   std::vector<std::string> ao_built_centers;
383 
384   int Nb_Elements(0);
385   std::string basiset_name;
386 
387   /** process atomicBasisSet per ion species */
388   app_log() << "Reading BasisSet from HDF5 file:" << h5_path << std::endl;
389 
390   hdf_archive hin(myComm);
391   if (myComm->rank() == 0)
392   {
393     if (!hin.open(h5_path, H5F_ACC_RDONLY))
394       PRE.error("Could not open H5 file", true);
395     if (!hin.push("basisset"))
396       PRE.error("Could not open basisset group in H5; Probably Corrupt H5 file", true);
397     hin.read(Nb_Elements, "NbElements");
398   }
399 
400   myComm->bcast(Nb_Elements);
401   if (Nb_Elements < 1)
402     PRE.error("Missing elementType attribute of atomicBasisSet.", true);
403 
404   for (int i = 0; i < Nb_Elements; i++)
405   {
406     std::string elementType, dataset;
407     std::stringstream tempElem;
408     std::string ElemID0 = "atomicBasisSet", ElemType;
409     tempElem << ElemID0 << i;
410     ElemType = tempElem.str();
411 
412     if (myComm->rank() == 0)
413     {
414       if (!hin.push(ElemType.c_str()))
415         PRE.error("Could not open  group Containing atomic Basis set in H5; Probably Corrupt H5 file", true);
416       if (!hin.readEntry(basiset_name, "name"))
417         PRE.error("Could not find name of  basisset group in H5; Probably Corrupt H5 file", true);
418       if (!hin.readEntry(elementType, "elementType"))
419         PRE.error("Could not read elementType in H5; Probably Corrupt H5 file", true);
420     }
421     myComm->bcast(basiset_name);
422     myComm->bcast(elementType);
423 
424     auto it = std::find(ao_built_centers.begin(), ao_built_centers.end(), elementType);
425     if (it == ao_built_centers.end())
426     {
427       AOBasisBuilder<ao_type> any(elementType, myComm);
428       any.putH5(hin);
429       ao_type* aoBasis = any.createAOSetH5(hin);
430       if (aoBasis)
431       {
432         //add the new atomic basis to the basis set
433         int activeCenter = sourcePtcl.getSpeciesSet().findSpecies(elementType);
434         mBasisSet->add(activeCenter, aoBasis);
435       }
436       ao_built_centers.push_back(elementType);
437     }
438 
439     if (myComm->rank() == 0)
440       hin.pop();
441   }
442 
443   if (myComm->rank() == 0)
444   {
445     hin.pop();
446     hin.close();
447   }
448   mBasisSet->setBasisSetSize(-1);
449   mBasisSet->setPBCParams(PBCImages, SuperTwist, PeriodicImagePhaseFactors);
450   return mBasisSet;
451 }
452 
453 
createSPOSetFromXML(xmlNodePtr cur)454 SPOSet* LCAOrbitalBuilder::createSPOSetFromXML(xmlNodePtr cur)
455 {
456   ReportEngine PRE(ClassName, "createSPO(xmlNodePtr)");
457   std::string spo_name(""), id, cusp_file(""), optimize("no");
458   std::string basisset_name("LCAOBSet");
459   OhmmsAttributeSet spoAttrib;
460   spoAttrib.add(spo_name, "name");
461   spoAttrib.add(id, "id");
462   spoAttrib.add(cusp_file, "cuspInfo");
463   spoAttrib.add(optimize, "optimize");
464   spoAttrib.add(basisset_name, "basisset");
465   spoAttrib.put(cur);
466 
467   BasisSet_t* myBasisSet = nullptr;
468   if (basisset_map_.find(basisset_name) == basisset_map_.end())
469     myComm->barrier_and_abort("basisset \"" + basisset_name + "\" cannot be found\n");
470   else
471     myBasisSet = basisset_map_[basisset_name]->makeClone();
472 
473   if (optimize == "yes")
474     app_log() << "  SPOSet " << spo_name << " is optimizable\n";
475 
476   LCAOrbitalSet* lcos = nullptr;
477 #if !defined(QMC_COMPLEX)
478   LCAOrbitalSetWithCorrection* lcwc = nullptr;
479   if (doCuspCorrection)
480   {
481     app_summary() << "        Using cusp correction." << std::endl;
482     lcos = lcwc = new LCAOrbitalSetWithCorrection(sourcePtcl, targetPtcl, std::unique_ptr<BasisSet_t>(myBasisSet),
483                                                   optimize == "yes");
484   }
485   else
486     lcos = new LCAOrbitalSet(std::unique_ptr<BasisSet_t>(myBasisSet), optimize == "yes");
487 #else
488   lcos = new LCAOrbitalSet(std::unique_ptr<BasisSet_t>(myBasisSet), optimize == "yes");
489 #endif
490   loadMO(*lcos, cur);
491 
492 #if !defined(QMC_COMPLEX)
493   if (doCuspCorrection)
494   {
495     int num_centers = sourcePtcl.getTotalNum();
496 
497     // Sometimes sposet attribute is 'name' and sometimes it is 'id'
498     if (id == "")
499       id = spo_name;
500 
501     int orbital_set_size = lcos->getOrbitalSetSize();
502     Matrix<CuspCorrectionParameters> info(num_centers, orbital_set_size);
503 
504     bool valid = false;
505     if (myComm->rank() == 0)
506     {
507       valid = readCuspInfo(cusp_file, id, orbital_set_size, info);
508     }
509 #ifdef HAVE_MPI
510     myComm->comm.broadcast_value(valid);
511     if (valid)
512     {
513       for (int orb_idx = 0; orb_idx < orbital_set_size; orb_idx++)
514       {
515         for (int center_idx = 0; center_idx < num_centers; center_idx++)
516         {
517           broadcastCuspInfo(info(center_idx, orb_idx), *myComm, 0);
518         }
519       }
520     }
521 #endif
522     if (!valid)
523     {
524       generateCuspInfo(orbital_set_size, num_centers, info, targetPtcl, sourcePtcl, *lcwc, id, *myComm);
525     }
526 
527     applyCuspCorrection(info, num_centers, orbital_set_size, targetPtcl, sourcePtcl, *lcwc, id);
528   }
529 #endif
530 
531   return lcos;
532 }
533 
534 
535 /** Parse the xml file for information on the Dirac determinants.
536    *@param cur the current xmlNode
537    */
loadMO(LCAOrbitalSet & spo,xmlNodePtr cur)538 bool LCAOrbitalBuilder::loadMO(LCAOrbitalSet& spo, xmlNodePtr cur)
539 {
540 #undef FunctionName
541 #define FunctionName                                      \
542   printf("Calling FunctionName from %s\n", __FUNCTION__); \
543   FunctionNameReal
544   //Check if HDF5 present
545   ReportEngine PRE("LCAOrbitalBuilder", "put(xmlNodePtr)");
546 
547   //initialize the number of orbital by the basis set size
548   int norb = spo.getBasisSetSize();
549   std::string debugc("no");
550   double orbital_mix_magnitude = 0.0;
551   bool PBC                     = false;
552   OhmmsAttributeSet aAttrib;
553   aAttrib.add(norb, "orbitals");
554   aAttrib.add(norb, "size");
555   aAttrib.add(debugc, "debug");
556   aAttrib.add(orbital_mix_magnitude, "orbital_mix_magnitude");
557   aAttrib.put(cur);
558   xmlNodePtr occ_ptr   = NULL;
559   xmlNodePtr coeff_ptr = NULL;
560   cur                  = cur->xmlChildrenNode;
561   while (cur != NULL)
562   {
563     std::string cname((const char*)(cur->name));
564     if (cname == "occupation")
565     {
566       occ_ptr = cur;
567     }
568     else if (cname.find("coeff") < cname.size() || cname == "parameter" || cname == "Var")
569     {
570       coeff_ptr = cur;
571     }
572     cur = cur->next;
573   }
574   if (coeff_ptr == NULL)
575   {
576     app_log() << "   Using Identity for the LCOrbitalSet " << std::endl;
577     return true;
578   }
579   spo.setOrbitalSetSize(norb);
580   bool success = putOccupation(spo, occ_ptr);
581   if (h5_path == "")
582     success = putFromXML(spo, coeff_ptr);
583   else
584   {
585     hdf_archive hin(myComm);
586 
587     if (myComm->rank() == 0)
588     {
589       if (!hin.open(h5_path, H5F_ACC_RDONLY))
590         APP_ABORT("LCAOrbitalBuilder::putFromH5 missing or incorrect path to H5 file.");
591       //TO REVIEWERS:: IDEAL BEHAVIOUR SHOULD BE:
592       /*
593          if(!hin.push("PBC")
594              PBC=false;
595          else
596             if (!hin.readEntry(PBC,"PBC"))
597                 APP_ABORT("Could not read PBC dataset in H5 file. Probably corrupt file!!!.");
598         // However, it always succeeds to enter the if condition even if the group does not exists...
599         */
600       hin.push("PBC");
601       PBC = false;
602       hin.read(PBC, "PBC");
603       hin.close();
604     }
605     myComm->bcast(PBC);
606     if (PBC)
607       success = putPBCFromH5(spo, coeff_ptr);
608     else
609       success = putFromH5(spo, coeff_ptr);
610   }
611 
612   // Ye: used to construct cusp correction
613   //bool success2 = transformSPOSet();
614   if (debugc == "yes")
615   {
616     app_log() << "   Single-particle orbital coefficients dims=" << spo.C->rows() << " x " << spo.C->cols()
617               << std::endl;
618     app_log() << *spo.C << std::endl;
619   }
620 
621   return success;
622 }
623 
putFromXML(LCAOrbitalSet & spo,xmlNodePtr coeff_ptr)624 bool LCAOrbitalBuilder::putFromXML(LCAOrbitalSet& spo, xmlNodePtr coeff_ptr)
625 {
626   int norbs = 0;
627   OhmmsAttributeSet aAttrib;
628   aAttrib.add(norbs, "size");
629   aAttrib.add(norbs, "orbitals");
630   aAttrib.put(coeff_ptr);
631   if (norbs < spo.getOrbitalSetSize())
632   {
633     return false;
634     APP_ABORT("LCAOrbitalBuilder::putFromXML missing or incorrect size");
635   }
636   if (norbs)
637   {
638     std::vector<ValueType> Ctemp;
639     int BasisSetSize = spo.getBasisSetSize();
640     Ctemp.resize(norbs * BasisSetSize);
641     putContent(Ctemp, coeff_ptr);
642     int n = 0, i = 0;
643     std::vector<ValueType>::iterator cit(Ctemp.begin());
644     while (i < spo.getOrbitalSetSize())
645     {
646       if (Occ[n] > std::numeric_limits<RealType>::epsilon())
647       {
648         std::copy(cit, cit + BasisSetSize, (*spo.C)[i]);
649         i++;
650       }
651       n++;
652       cit += BasisSetSize;
653     }
654   }
655   return true;
656 }
657 
658 /** read data from a hdf5 file
659    * @param norb number of orbitals to be initialized
660    * @param coeff_ptr xmlnode for coefficients
661    */
putFromH5(LCAOrbitalSet & spo,xmlNodePtr coeff_ptr)662 bool LCAOrbitalBuilder::putFromH5(LCAOrbitalSet& spo, xmlNodePtr coeff_ptr)
663 {
664 #if defined(HAVE_LIBHDF5)
665   int neigs  = spo.getBasisSetSize();
666   int setVal = -1;
667   std::string setname;
668   OhmmsAttributeSet aAttrib;
669   aAttrib.add(setVal, "spindataset");
670   aAttrib.add(neigs, "size");
671   aAttrib.add(neigs, "orbitals");
672   aAttrib.put(coeff_ptr);
673   hdf_archive hin(myComm);
674   if (myComm->rank() == 0)
675   {
676     if (!hin.open(h5_path, H5F_ACC_RDONLY))
677       APP_ABORT("LCAOrbitalBuilder::putFromH5 missing or incorrect path to H5 file.");
678 
679     Matrix<RealType> Ctemp;
680     char name[72];
681 
682     //This is to make sure of Backward compatibility with previous tags.
683     sprintf(name, "%s%d", "/Super_Twist/eigenset_", setVal);
684     setname = name;
685     if (!hin.readEntry(Ctemp, setname))
686     {
687       sprintf(name, "%s%d", "/KPTS_0/eigenset_", setVal);
688       setname = name;
689       if (!hin.readEntry(Ctemp, setname))
690       {
691         setname = "LCAOrbitalBuilder::putFromH5 Missing " + setname + " from HDF5 File.";
692         APP_ABORT(setname.c_str());
693       }
694     }
695     hin.close();
696 
697     if (Ctemp.cols() != spo.getBasisSetSize())
698     {
699       std::ostringstream err_msg;
700       err_msg << "Basis set size " << spo.getBasisSetSize() << " mismatched the number of MO coefficients columns "
701               << Ctemp.cols() << " from h5." << std::endl;
702       myComm->barrier_and_abort(err_msg.str());
703     }
704 
705     int norbs = spo.getOrbitalSetSize();
706     if (Ctemp.rows() < norbs)
707     {
708       std::ostringstream err_msg;
709       err_msg << "Need " << norbs << " orbitals. Insufficient rows of MO coefficients " << Ctemp.rows() << " from h5."
710               << std::endl;
711       myComm->barrier_and_abort(err_msg.str());
712     }
713 
714     int n = 0, i = 0;
715     while (i < norbs)
716     {
717       if (Occ[n] > 0.0)
718       {
719         std::copy(Ctemp[n], Ctemp[n + 1], (*spo.C)[i]);
720         i++;
721       }
722       n++;
723     }
724   }
725   myComm->bcast(spo.C->data(), spo.C->size());
726 #else
727   APP_ABORT("LCAOrbitalBuilder::putFromH5 HDF5 is disabled.")
728 #endif
729   return true;
730 }
731 
732 
733 /** read data from a hdf5 file
734    * @param norb number of orbitals to be initialized
735    * @param coeff_ptr xmlnode for coefficients
736    */
putPBCFromH5(LCAOrbitalSet & spo,xmlNodePtr coeff_ptr)737 bool LCAOrbitalBuilder::putPBCFromH5(LCAOrbitalSet& spo, xmlNodePtr coeff_ptr)
738 {
739 #if defined(HAVE_LIBHDF5)
740   ReportEngine PRE("LCAOrbitalBuilder", "LCAOrbitalBuilder::putPBCFromH5");
741   int norbs      = spo.getOrbitalSetSize();
742   int neigs      = spo.getBasisSetSize();
743   int setVal     = -1;
744   bool IsComplex = false;
745   bool MultiDet  = false;
746   PosType SuperTwist(0.0);
747   PosType SuperTwistH5(0.0);
748   OhmmsAttributeSet aAttrib;
749   aAttrib.add(setVal, "spindataset");
750   aAttrib.add(neigs, "size");
751   aAttrib.add(neigs, "orbitals");
752   aAttrib.put(coeff_ptr);
753   hdf_archive hin(myComm);
754 
755   xmlNodePtr curtemp = coeff_ptr;
756 
757   std::string xmlTag("determinantset");
758   std::string MSDTag("sposet");
759   std::string SDTag("determinant");
760   std::string EndTag("qmcsystem");
761   std::string curname;
762 
763   do
764   {
765     std::stringstream ss;
766     curtemp = curtemp->parent;
767     ss << curtemp->name;
768     ss >> curname;
769     if (curname == MSDTag)
770       MultiDet = true; ///Used to know if running an MSD calculation - needed for order of Orbitals.
771     if (curname == SDTag)
772       MultiDet = false;
773 
774   } while ((xmlTag != curname) && (curname != EndTag));
775   if (curname == EndTag)
776   {
777     APP_ABORT(
778         "Could not find in wf file the \"sposet\" or \"determinant\" tags. Please verify input or contact developers");
779   }
780 
781   aAttrib.add(SuperTwist, "twist");
782   aAttrib.put(curtemp);
783 
784   if (myComm->rank() == 0)
785   {
786     if (!hin.open(h5_path, H5F_ACC_RDONLY))
787       APP_ABORT("LCAOrbitalBuilder::putFromH5 missing or incorrect path to H5 file.");
788     hin.push("parameters");
789     hin.read(IsComplex, "IsComplex");
790     hin.pop();
791 
792     std::string setname("/Super_Twist/Coord");
793     hin.read(SuperTwistH5, setname);
794     if (std::abs(SuperTwistH5[0] - SuperTwist[0]) >= 1e-6 || std::abs(SuperTwistH5[1] - SuperTwist[1]) >= 1e-6 ||
795         std::abs(SuperTwistH5[2] - SuperTwist[2]) >= 1e-6)
796     {
797       app_log() << "Super Twist in XML : " << SuperTwist[0] << "    In H5:" << SuperTwistH5[0] << std::endl;
798       app_log() << "                     " << SuperTwist[1] << "          " << SuperTwistH5[1] << std::endl;
799       app_log() << "                     " << SuperTwist[2] << "          " << SuperTwistH5[2] << std::endl;
800       app_log() << "Diff in Coord     x :" << std::abs(SuperTwistH5[0] - SuperTwist[0]) << std::endl;
801       app_log() << "                  y :" << std::abs(SuperTwistH5[1] - SuperTwist[1]) << std::endl;
802       app_log() << "                  z :" << std::abs(SuperTwistH5[2] - SuperTwist[2]) << std::endl;
803       APP_ABORT("Requested Super Twist in XML and Super Twist in HDF5 do not Match!!! Aborting.");
804     }
805     //SuperTwist=SuperTwistH5;
806     Matrix<ValueType> Ctemp;
807     LoadFullCoefsFromH5(hin, setVal, SuperTwist, Ctemp, MultiDet);
808 
809     int n = 0, i = 0;
810     while (i < norbs)
811     {
812       if (Occ[n] > 0.0)
813       {
814         std::copy(Ctemp[n], Ctemp[n + 1], (*spo.C)[i]);
815         i++;
816       }
817       n++;
818     }
819 
820     hin.close();
821   }
822 #ifdef HAVE_MPI
823   myComm->comm.broadcast_n(spo.C->data(), spo.C->size());
824 #endif
825 
826 #else
827   APP_ABORT("LCAOrbitalBuilder::putFromH5 HDF5 is disabled.")
828 #endif
829   return true;
830 }
831 
832 
putOccupation(LCAOrbitalSet & spo,xmlNodePtr occ_ptr)833 bool LCAOrbitalBuilder::putOccupation(LCAOrbitalSet& spo, xmlNodePtr occ_ptr)
834 {
835   //die??
836   if (spo.getBasisSetSize() == 0)
837   {
838     APP_ABORT("LCAOrbitalBuilder::putOccupation detected ZERO BasisSetSize");
839     return false;
840   }
841   Occ.resize(std::max(spo.getBasisSetSize(), spo.getOrbitalSetSize()));
842   Occ = 0.0;
843   for (int i = 0; i < spo.getOrbitalSetSize(); i++)
844     Occ[i] = 1.0;
845   std::vector<int> occ_in;
846   std::string occ_mode("table");
847   if (occ_ptr == NULL)
848   {
849     occ_mode = "ground";
850   }
851   else
852   {
853     const XMLAttrString o(occ_ptr, "mode");
854     if (!o.empty())
855       occ_mode = o;
856   }
857   //Do nothing if mode == ground
858   if (occ_mode == "excited")
859   {
860     putContent(occ_in, occ_ptr);
861     for (int k = 0; k < occ_in.size(); k++)
862     {
863       if (occ_in[k] < 0) //remove this, -1 is to adjust the base
864         Occ[-occ_in[k] - 1] = 0.0;
865       else
866         Occ[occ_in[k] - 1] = 1.0;
867     }
868   }
869   else if (occ_mode == "table")
870   {
871     putContent(Occ, occ_ptr);
872   }
873   return true;
874 }
875 
readRealMatrixFromH5(hdf_archive & hin,const std::string & setname,Matrix<LCAOrbitalBuilder::RealType> & Creal) const876 void LCAOrbitalBuilder::readRealMatrixFromH5(hdf_archive& hin,
877                                              const std::string& setname,
878                                              Matrix<LCAOrbitalBuilder::RealType>& Creal) const
879 {
880   if (!hin.readEntry(Creal, setname))
881   {
882     std::string error_msg = "LCAOrbitalBuilder::readRealMatrixFromH5 Missing " + setname + " from HDF5 File.";
883     APP_ABORT(error_msg.c_str());
884   }
885 }
886 
LoadFullCoefsFromH5(hdf_archive & hin,int setVal,PosType & SuperTwist,Matrix<std::complex<RealType>> & Ctemp,bool MultiDet)887 void LCAOrbitalBuilder::LoadFullCoefsFromH5(hdf_archive& hin,
888                                             int setVal,
889                                             PosType& SuperTwist,
890                                             Matrix<std::complex<RealType>>& Ctemp,
891                                             bool MultiDet)
892 {
893   Matrix<RealType> Creal;
894   Matrix<RealType> Ccmplx;
895 
896   char name[72];
897   std::string setname;
898   ///When running Single Determinant calculations, MO coeff loaded based on occupation and lowest eingenvalue.
899   ///However, for solids with multideterminants, orbitals are order by kpoints; first all MOs for kpoint 1, then 2 etc
900   /// The multideterminants occupation is specified in the input/HDF5 and theefore as long as there is consistency between
901   /// the order in which we read the orbitals and the occupation, we are safe. In the case of Multideterminants generated
902   /// by pyscf and Quantum Package, They are stored in the same order as generated for quantum package and one should use
903   /// the orbitals labelled eigenset_unsorted.
904 
905   if (MultiDet == false)
906     sprintf(name, "%s%d", "/Super_Twist/eigenset_", setVal);
907   else
908     sprintf(name, "%s%d", "/Super_Twist/eigenset_unsorted_", setVal);
909 
910   setname = name;
911   readRealMatrixFromH5(hin, setname, Creal);
912 
913   bool IsComplex = true;
914   hin.read(IsComplex, "/parameters/IsComplex");
915   if (IsComplex == false)
916   {
917     Ccmplx.resize(Creal.rows(), Creal.cols());
918     Ccmplx = 0.0;
919   }
920   else
921   {
922     setname = std::string(name) + "_imag";
923     readRealMatrixFromH5(hin, setname, Ccmplx);
924   }
925 
926   Ctemp.resize(Creal.rows(), Creal.cols());
927   for (int i = 0; i < Ctemp.rows(); i++)
928     for (int j = 0; j < Ctemp.cols(); j++)
929       Ctemp[i][j] = std::complex<RealType>(Creal[i][j], Ccmplx[i][j]);
930 }
931 
LoadFullCoefsFromH5(hdf_archive & hin,int setVal,PosType & SuperTwist,Matrix<RealType> & Creal,bool MultiDet)932 void LCAOrbitalBuilder::LoadFullCoefsFromH5(hdf_archive& hin,
933                                             int setVal,
934                                             PosType& SuperTwist,
935                                             Matrix<RealType>& Creal,
936                                             bool MultiDet)
937 {
938   bool IsComplex = false;
939   hin.read(IsComplex, "/parameters/IsComplex");
940   if (IsComplex &&
941       (std::abs(SuperTwist[0]) >= 1e-6 || std::abs(SuperTwist[1]) >= 1e-6 || std::abs(SuperTwist[2]) >= 1e-6))
942   {
943     std::string setname("This Wavefunction is Complex and you are using the real version of QMCPACK. "
944                         "Please re-run this job with the Complex build of QMCPACK.");
945     APP_ABORT(setname.c_str());
946   }
947 
948   char name[72];
949   bool PBC = false;
950   hin.read(PBC, "/PBC/PBC");
951   if (MultiDet && PBC)
952     sprintf(name, "%s%d", "/Super_Twist/eigenset_unsorted_", setVal);
953   else
954     sprintf(name, "%s%d", "/Super_Twist/eigenset_", setVal);
955   readRealMatrixFromH5(hin, name, Creal);
956 }
957 
958 /// Periodic Image Phase Factors computation to be determined
EvalPeriodicImagePhaseFactors(PosType SuperTwist,std::vector<RealType> & LocPeriodicImagePhaseFactors)959 void LCAOrbitalBuilder::EvalPeriodicImagePhaseFactors(PosType SuperTwist,
960                                                       std::vector<RealType>& LocPeriodicImagePhaseFactors)
961 {
962   const int NbImages = (PBCImages[0] + 1) * (PBCImages[1] + 1) * (PBCImages[2] + 1);
963   LocPeriodicImagePhaseFactors.resize(NbImages);
964   for (size_t i = 0; i < NbImages; i++)
965     LocPeriodicImagePhaseFactors[i] = 1.0;
966 }
967 
EvalPeriodicImagePhaseFactors(PosType SuperTwist,std::vector<std::complex<RealType>> & LocPeriodicImagePhaseFactors)968 void LCAOrbitalBuilder::EvalPeriodicImagePhaseFactors(PosType SuperTwist,
969                                                       std::vector<std::complex<RealType>>& LocPeriodicImagePhaseFactors)
970 {
971   // Allow computation to continue with no HDF file if the system has open boundary conditions.
972   // The complex build is usually only used with open BC for testing.
973   bool usesOpenBC = PBCImages[0] == 0 && PBCImages[1] == 0 && PBCImages[2] == 0;
974 
975   ///Exp(ik.g) where i is imaginary, k is the supertwist and g is the translation vector PBCImage.
976   if (h5_path != "" && !usesOpenBC)
977   {
978     hdf_archive hin(myComm);
979     if (myComm->rank() == 0)
980     {
981       if (!hin.open(h5_path, H5F_ACC_RDONLY))
982         APP_ABORT("Could not open H5 file");
983       if (!hin.push("Cell"))
984         APP_ABORT("Could not open Cell group in H5; Probably Corrupt H5 file; accessed from LCAOrbitalBuilder");
985       if (!hin.readEntry(Lattice, "LatticeVectors"))
986         APP_ABORT("Could not open Lattice vectors in H5; Probably Corrupt H5 file; accessed from LCAOrbitalBuilder");
987       hin.close();
988     }
989     for (int i = 0; i < 3; i++)
990       for (int j = 0; j < 3; j++)
991         myComm->bcast(Lattice(i, j));
992   }
993   else if (!usesOpenBC)
994   {
995     APP_ABORT("Attempting to run PBC LCAO with no HDF5 support. Behaviour is unknown. Safer to exit");
996   }
997 
998   int phase_idx = 0;
999   int TransX, TransY, TransZ;
1000   RealType phase;
1001 
1002   for (int i = 0; i <= PBCImages[0]; i++) //loop Translation over X
1003   {
1004     TransX = ((i % 2) * 2 - 1) * ((i + 1) / 2);
1005     for (int j = 0; j <= PBCImages[1]; j++) //loop Translation over Y
1006     {
1007       TransY = ((j % 2) * 2 - 1) * ((j + 1) / 2);
1008       for (int k = 0; k <= PBCImages[2]; k++) //loop Translation over Z
1009       {
1010         TransZ = ((k % 2) * 2 - 1) * ((k + 1) / 2);
1011         RealType s, c;
1012         PosType Val;
1013         Val[0] = TransX * Lattice(0, 0) + TransY * Lattice(1, 0) + TransZ * Lattice(2, 0);
1014         Val[1] = TransX * Lattice(0, 1) + TransY * Lattice(1, 1) + TransZ * Lattice(2, 1);
1015         Val[2] = TransX * Lattice(0, 2) + TransY * Lattice(1, 2) + TransZ * Lattice(2, 2);
1016 
1017         phase = dot(SuperTwist, Val);
1018         qmcplusplus::sincos(phase, &s, &c);
1019 
1020         LocPeriodicImagePhaseFactors.emplace_back(c, s);
1021       }
1022     }
1023   }
1024 }
1025 } // namespace qmcplusplus
1026