QMCPACK
test_RotatedSPOs_LCAO.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) 2022 QMCPACK developers.
6 //
7 // File developed by: Mark Dewing, mdewing@anl.gov Argonne National Laboratory
8 //
9 // File created by: Mark Dewing, mdewing@anl.gov Argonne National Laboratory
10 //////////////////////////////////////////////////////////////////////////////////////
11 
12 
13 #include "catch.hpp"
14 
15 
16 #include "Message/Communicate.h"
17 #include "OhmmsData/Libxml2Doc.h"
23 #include "checkMatrix.hpp"
24 #include "Utilities/ProjectData.h"
25 
26 #include <stdio.h>
27 #include <string>
28 #include <sstream>
29 
30 
31 // Reference values from gen_rotated_lcao_wf.py
32 
33 
34 namespace qmcplusplus
35 {
37 {
38  // See ParticleIO/tests/test_xml_io.cpp for particle parsing
39  const char* particles = R"(<tmp>
40  <particleset name="ion0" size="1">
41  <group name="He">
42  <parameter name="charge">2</parameter>
43  </group>
44  <attrib name="position" datatype="posArray">
45  0.0 0.0 0.0
46  </attrib>
47  </particleset>
48 
49  <particleset name="e" random="no">
50  <group name="u" size="1">
51  <parameter name="charge">-1</parameter>
52  <attrib name="position" datatype="posArray">
53  1.0 2.0 3.0
54  </attrib>
55  </group>
56  <group name="d" size="1">
57  <parameter name="charge">-1</parameter>
58  <attrib name="position" datatype="posArray">
59  0.0 1.1 2.2
60  </attrib>
61  </group>
62  </particleset>
63 </tmp>)";
65 
66  bool okay = doc.parseFromString(particles);
67  REQUIRE(okay);
68 
69  xmlNodePtr root = doc.getRoot();
70 
71  xmlNodePtr part_ion = xmlFirstElementChild(root);
72  pp.put(part_ion);
73  xmlNodePtr part_elec = xmlNextElementSibling(part_ion);
74  pp.put(part_elec);
75 }
76 
77 // Set particles for Be atom
79 {
80  // See ParticleIO/tests/test_xml_io.cpp for particle parsing
81  const char* particles = R"(<qmcsystem>
82  <particleset name="ion0" size="1">
83  <group name="Be">
84  <parameter name="charge">4</parameter>
85  <parameter name="valence">4</parameter>
86  <parameter name="atomicnumber">4</parameter>
87  </group>
88  <attrib name="position" datatype="posArray">
89  0.0000000000e+00 0.0000000000e+00 0.0000000000e+00
90 </attrib>
91  <attrib name="ionid" datatype="stringArray">
92  Be
93 </attrib>
94  </particleset>
95 
96  <particleset name="e" random="no">
97  <group name="u" size="2">
98  <parameter name="charge">-1</parameter>
99  <attrib name="position" datatype="posArray">
100  0.7 2.0 3.0
101  1.2 1.5 0.5
102  </attrib>
103  </group>
104  <group name="d" size="2">
105  <parameter name="charge">-1</parameter>
106  <attrib name="position" datatype="posArray">
107  1.5 1.6 1.5
108  0.7 1.0 1.2
109  </attrib>
110  </group>
111  </particleset>
112 </qmcsystem>)";
113 
115 
116  bool okay = doc.parseFromString(particles);
117  REQUIRE(okay);
118 
119  xmlNodePtr root = doc.getRoot();
120 
121  xmlNodePtr part_ion = xmlFirstElementChild(root);
122  pp.put(part_ion);
123  xmlNodePtr part_elec = xmlNextElementSibling(part_ion);
124  pp.put(part_elec);
125 }
126 
127 std::string setupRotationXML(const std::string& rot_angle_up,
128  const std::string& rot_angle_down,
129  const std::string& coeff_up,
130  const std::string& coeff_down)
131 {
132  // Replace with std::format when minimum standard is switched to C++20
133 
134  const std::string wf_input1 = R"(<wavefunction target='e'>
135  <sposet_collection type="MolecularOrbital">
136  <!-- Use a single Slater Type Orbital (STO) for the basis. Cusp condition is correct. -->
137  <basisset keyword="STO" transform="no">
138  <atomicBasisSet type="STO" elementType="He" normalized="no">
139  <basisGroup rid="R0" l="0" m="0" type="Slater">
140  <radfunc n="1" exponent="2.0"/>
141  </basisGroup>
142  <basisGroup rid="R1" l="0" m="0" type="Slater">
143  <radfunc n="2" exponent="1.0"/>
144  </basisGroup>
145  </atomicBasisSet>
146  </basisset>
147  <rotated_sposet name="rot-spo-up">
148  <sposet basisset="LCAOBSet" name="spo-up">)";
149 
150  // Opt vars for up determinant
151  // <opt_vars>0.1</opt_vars>
152  const std::string opt_vars_start_tag("<opt_vars>");
153  const std::string opt_vars_end_tag("</opt_vars>");
154 
155  std::string rot_angle_up_element = opt_vars_start_tag + rot_angle_up + opt_vars_end_tag + "\n";
156 
157  // Construct the coefficient matrix XML element for the up determinant
158  // <coefficient id="updetC" type="Array" size="2">
159  // 1.0 0.0
160  // 0.0 1.0
161  // </coefficient>
162  const std::string wf_input_coeff_up_start = R"(<coefficient id="updetC" type="Array" size="2">)";
163 
164  const std::string wf_input_coeff_up_end("</coefficient>");
165 
166  std::string coeff_up_element = wf_input_coeff_up_start + coeff_up + wf_input_coeff_up_end;
167 
168  const std::string sposet_end = R"(</sposet>)";
169 
170  // Middle part of XML input block
171  const std::string wf_input2 = R"(
172  </rotated_sposet>
173  <rotated_sposet name="rot-spo-down">
174  <sposet basisset="LCAOBSet" name="spo-down">)";
175 
176  // Opt vars for down determinant
177  // <opt_vars>0.2</opt_vars>
178  std::string rot_angle_down_element = opt_vars_start_tag + rot_angle_down + opt_vars_end_tag + "\n";
179 
180  // Construct the coefficient matrix XML element for the down determinant
181  // <coefficient id="downdetC" type="Array" size="2">
182  // 1.0 0.0
183  // 0.0 1.0
184  // </coefficient>
185  const std::string wf_input_coeff_down_start = R"(<coefficient id="downdetC" type="Array" size="2">)";
186 
187  const std::string wf_input_coeff_down_end("</coefficient>");
188 
189  std::string coeff_down_element = wf_input_coeff_down_start + coeff_down + wf_input_coeff_down_end;
190 
191  const std::string wf_input3 = R"(
192  </rotated_sposet>
193  </sposet_collection>
194  <determinantset type="MO" key="STO" transform="no" source="ion0">
195  <slaterdeterminant>
196  <determinant sposet="rot-spo-up"/>
197  <determinant sposet="rot-spo-down"/>
198  </slaterdeterminant>
199  </determinantset>
200  </wavefunction>)";
201 
202 
203  // clang-format off
204  std::string wf_input = std::string(wf_input1) + "\n" +
205  coeff_up_element + "\n" +
206  sposet_end + "\n" +
207  (rot_angle_up.empty() ? std::string() : rot_angle_up_element) +
208  wf_input2 + "\n" +
209  coeff_down_element + "\n" +
210  sposet_end + "\n" +
211  (rot_angle_down.empty() ? std::string() : rot_angle_down_element) +
212  std::string(wf_input3);
213  // clang-format on
214 
215  return wf_input;
216 }
217 
218 const std::string identity_coeff = R"(
219  1.0 0.0
220  0.0 1.0
221  )";
222 
223 // No Jastrow, rotation angle of 0. Identity coefficients.
224 TEST_CASE("Rotated LCAO WF0 zero angle", "[qmcapp]")
225 {
227  Communicate* c;
228  c = OHMMS::Controller;
229 
230  ParticleSetPool pp(c);
232 
234 
235  REQUIRE(wp.empty() == true);
236 
237 
238  std::string wf_input = setupRotationXML("", "", identity_coeff, identity_coeff);
239 
241  bool okay = doc.parseFromString(wf_input);
242  REQUIRE(okay);
243 
244  xmlNodePtr root = doc.getRoot();
245 
246  wp.put(root);
247 
248  TrialWaveFunction* psi = wp.getWaveFunction("psi0");
249  REQUIRE(psi != nullptr);
250  REQUIRE(psi->getOrbitals().size() == 1);
251 
252  opt_variables_type opt_vars;
253  psi->checkInVariables(opt_vars);
254  opt_vars.resetIndex();
255  psi->checkOutVariables(opt_vars);
256  psi->resetParameters(opt_vars);
257 
258  ParticleSet* elec = pp.getParticleSet("e");
259  elec->update();
260 
261 
262  double logval = psi->evaluateLog(*elec);
263  CHECK(logval == Approx(-11.467952668216984));
264 
265  CHECK(elec->G[0][0] == ValueApprox(-0.5345224838248487));
266  CHECK(elec->L[0] == ValueApprox(-1.0690449676496974));
267  CHECK(elec->L[1] == ValueApprox(-1.626231256363484));
268 
270  Vector<ValueType> dlogpsi(2);
271  Vector<ValueType> dhpsioverpsi(2);
272  psi->evaluateDerivatives(*elec, opt_vars, dlogpsi, dhpsioverpsi);
273 
274  CHECK(dlogpsi[0] == ValueApprox(32.2062050179872));
275  CHECK(dlogpsi[1] == ValueApprox(5.87482925187464));
276  CHECK(dhpsioverpsi[0] == ValueApprox(46.0088643114103));
277  CHECK(dhpsioverpsi[1] == ValueApprox(7.84119772047731));
278 
279  RefVectorWithLeader<TrialWaveFunction> wf_list(*psi, {*psi});
280  RefVectorWithLeader<ParticleSet> p_list(*elec, {*elec});
281 
282  // Test list with one wavefunction
283 
284  int nparam = 2;
285  int nentry = 1;
286  RecordArray<ValueType> dlogpsi_list(nentry, nparam);
287  RecordArray<ValueType> dhpsi_over_psi_list(nentry, nparam);
288 
289  TrialWaveFunction::mw_evaluateParameterDerivatives(wf_list, p_list, opt_vars, dlogpsi_list, dhpsi_over_psi_list);
290 
291  CHECK(dlogpsi_list[0][0] == ValueApprox(32.2062050179872));
292  CHECK(dlogpsi_list[0][1] == ValueApprox(5.87482925187464));
293  CHECK(dhpsi_over_psi_list[0][0] == ValueApprox(46.0088643114103));
294  CHECK(dhpsi_over_psi_list[0][1] == ValueApprox(7.84119772047731));
295 }
296 
297 // No Jastrow, rotation angle theta1=0.1 and theta2=0.2 from identity coefficients
298 TEST_CASE("Rotated LCAO WF1", "[qmcapp]")
299 {
301  Communicate* c;
302  c = OHMMS::Controller;
303 
304  ParticleSetPool pp(c);
306 
308 
309  REQUIRE(wp.empty() == true);
310 
311 
312  std::string wf_input = setupRotationXML("0.1", "0.2", identity_coeff, identity_coeff);
313 
315  bool okay = doc.parseFromString(wf_input);
316  REQUIRE(okay);
317 
318  xmlNodePtr root = doc.getRoot();
319 
320  wp.put(root);
321 
322  TrialWaveFunction* psi = wp.getWaveFunction("psi0");
323  REQUIRE(psi != nullptr);
324  REQUIRE(psi->getOrbitals().size() == 1);
325 
326  opt_variables_type opt_vars;
327  psi->checkInVariables(opt_vars);
328  psi->checkOutVariables(opt_vars);
329  psi->resetParameters(opt_vars);
330 
331  ParticleSet* elec = pp.getParticleSet("e");
332  elec->update();
333 
334 
335  double logval = psi->evaluateLog(*elec);
336  CHECK(logval == Approx(-9.26625670653773));
337 
338  CHECK(elec->G[0][0] == ValueApprox(-0.2758747113720909));
339  CHECK(elec->L[0] == ValueApprox(-0.316459652026054));
340  CHECK(elec->L[1] == ValueApprox(-0.6035591598540904));
341 
343  Vector<ValueType> dlogpsi(2);
344  Vector<ValueType> dhpsioverpsi(2);
345  psi->evaluateDerivatives(*elec, opt_vars, dlogpsi, dhpsioverpsi);
346 
347 
348  CHECK(dlogpsi[0] == ValueApprox(7.58753078998516));
349  CHECK(dlogpsi[1] == ValueApprox(2.58896036829191));
350  CHECK(dhpsioverpsi[0] == ValueApprox(2.59551625714144));
351  CHECK(dhpsioverpsi[1] == ValueApprox(1.70071425070404));
352 }
353 
354 // Rotation angle of 0 and add Jastrow factory
355 TEST_CASE("Rotated LCAO WF2 with jastrow", "[qmcapp]")
356 {
358  Communicate* c;
359  c = OHMMS::Controller;
360 
361  ParticleSetPool pp(c);
363 
365 
366  REQUIRE(wp.empty() == true);
367 
368 
369  const char* wf_input = R"(<wavefunction target='e'>
370 
371  <sposet_collection type="MolecularOrbital">
372  <!-- Use a single Slater Type Orbital (STO) for the basis. Cusp condition is correct. -->
373  <basisset keyword="STO" transform="no">
374  <atomicBasisSet type="STO" elementType="He" normalized="no">
375  <basisGroup rid="R0" l="0" m="0" type="Slater">
376  <radfunc n="1" exponent="2.0"/>
377  </basisGroup>
378  <basisGroup rid="R1" l="0" m="0" type="Slater">
379  <radfunc n="2" exponent="1.0"/>
380  </basisGroup>
381  </atomicBasisSet>
382  </basisset>
383  <rotated_sposet name="rot-spo-up">
384  <sposet basisset="LCAOBSet" name="spo-up" method="history">
385  <coefficient id="updetC" type="Array" size="2">
386  1.0 0.0
387  0.0 1.0
388  </coefficient>
389  </sposet>
390  </rotated_sposet>
391  <rotated_sposet name="rot-spo-down">
392  <sposet basisset="LCAOBSet" name="spo-down" method="history">
393  <coefficient id="updetC" type="Array" size="2">
394  1.0 0.0
395  0.0 1.0
396  </coefficient>
397  </sposet>
398  </rotated_sposet>
399  </sposet_collection>
400  <determinantset type="MO" key="STO" transform="no" source="ion0">
401  <slaterdeterminant>
402  <determinant sposet="rot-spo-up"/>
403  <determinant sposet="rot-spo-down"/>
404  </slaterdeterminant>
405  </determinantset>
406  <jastrow name="Jee" type="Two-Body" function="pade">
407  <correlation speciesA="u" speciesB="d">
408  <var id="jud_b" name="B">0.1</var>
409  </correlation>
410  </jastrow>
411  </wavefunction>)";
412 
414  bool okay = doc.parseFromString(wf_input);
415  REQUIRE(okay);
416 
417  xmlNodePtr root = doc.getRoot();
418 
419  wp.put(root);
420 
421  TrialWaveFunction* psi = wp.getWaveFunction("psi0");
422  REQUIRE(psi != nullptr);
423  REQUIRE(psi->getOrbitals().size() == 2);
424 
425  opt_variables_type opt_vars;
426  psi->checkInVariables(opt_vars);
427  opt_vars.resetIndex();
428  psi->checkOutVariables(opt_vars);
429  psi->resetParameters(opt_vars);
430 
431  ParticleSet* elec = pp.getParticleSet("e");
432  elec->update();
433 
434 
435  double logval = psi->evaluateLog(*elec);
436  CHECK(logval == Approx(-15.791249652199634));
437 
438  CHECK(elec->G[0][0] == ValueApprox(-0.2956989647881321));
439  CHECK(elec->L[0] == ValueApprox(-0.6560429678274734));
440  CHECK(elec->L[1] == ValueApprox(-1.2132292565412577));
441 
443  Vector<ValueType> dlogpsi(3);
444  Vector<ValueType> dhpsioverpsi(3);
445  psi->evaluateDerivatives(*elec, opt_vars, dlogpsi, dhpsioverpsi);
446 
447  CHECK(dlogpsi[0] == ValueApprox(32.206205017987166));
448  CHECK(dlogpsi[1] == ValueApprox(5.874829251874641));
449  CHECK(dlogpsi[2] == ValueApprox(49.08414605622605));
450  CHECK(dhpsioverpsi[0] == ValueApprox(32.462519534916666));
451  CHECK(dhpsioverpsi[1] == ValueApprox(10.047601212881027));
452  CHECK(dhpsioverpsi[2] == ValueApprox(2.0820644399551895));
453 
454  // Check for out-of-bounds array access when a variable is disabled.
455  // When a variable is disabled, myVars.where() returns -1.
456  opt_vars.insert("rot-spo-up_orb_rot_0000_0001", 0.0, false);
457  psi->checkOutVariables(opt_vars);
458  psi->evaluateDerivatives(*elec, opt_vars, dlogpsi, dhpsioverpsi);
459 }
460 
461 // Test the case where the rotation has already been applied to
462 // the MO coefficients in the input file.
463 // Should give the same results as the "Rotated LCAO WF1 zero angle" test case
464 
465 const std::string coeff_rot_by_point1 = R"(
466  0.995004165278026 0.0998334166468282
467  -0.0998334166468282 0.995004165278026
468  )";
469 
470 const std::string coeff_rot_by_point2 = R"(
471  0.980066577841242 0.198669330795061
472  -0.198669330795061 0.980066577841242
473  )";
474 
475 TEST_CASE("Rotated LCAO WF1, MO coeff rotated, zero angle", "[qmcapp]")
476 {
478  Communicate* c;
479  c = OHMMS::Controller;
480 
481  ParticleSetPool pp(c);
483 
485 
486  REQUIRE(wp.empty() == true);
487 
488  std::string wf_input = setupRotationXML("", "", coeff_rot_by_point1, coeff_rot_by_point2);
489 
491  bool okay = doc.parseFromString(wf_input);
492  REQUIRE(okay);
493 
494  xmlNodePtr root = doc.getRoot();
495 
496  wp.put(root);
497 
498  TrialWaveFunction* psi = wp.getWaveFunction("psi0");
499  REQUIRE(psi != nullptr);
500  REQUIRE(psi->getOrbitals().size() == 1);
501 
502  opt_variables_type opt_vars;
503  psi->checkInVariables(opt_vars);
504  opt_vars.resetIndex();
505  psi->checkOutVariables(opt_vars);
506  psi->resetParameters(opt_vars);
507 
508  ParticleSet* elec = pp.getParticleSet("e");
509  elec->update();
510 
511  double logval = psi->evaluateLog(*elec);
512  CHECK(logval == Approx(-9.26625670653773));
513 
514  CHECK(elec->G[0][0] == ValueApprox(-0.2758747113720909));
515  CHECK(elec->L[0] == ValueApprox(-0.316459652026054));
516  CHECK(elec->L[1] == ValueApprox(-0.6035591598540904));
517 
519  Vector<ValueType> dlogpsi(2);
520  Vector<ValueType> dhpsioverpsi(2);
521  psi->evaluateDerivatives(*elec, opt_vars, dlogpsi, dhpsioverpsi);
522 
523 
524  CHECK(dlogpsi[0] == ValueApprox(7.58753078998516));
525  CHECK(dlogpsi[1] == ValueApprox(2.58896036829191));
526  CHECK(dhpsioverpsi[0] == ValueApprox(2.59551625714144));
527  CHECK(dhpsioverpsi[1] == ValueApprox(1.70071425070404));
528 }
529 // Test the case where half the rotation has already been applied to
530 // the MO coefficients in the input file and half the rotation is
531 // applied through the input.
532 // Should give the same results as the "Rotated LCAO WF1 zero angle" test case
533 
534 const std::string coeff_rot_by_point05 = R"(
535  0.998750260394966 0.0499791692706783
536  -0.0499791692706783 0.998750260394966
537  )";
538 
539 TEST_CASE("Rotated LCAO WF1 MO coeff rotated, half angle", "[qmcapp]")
540 {
542  Communicate* c;
543  c = OHMMS::Controller;
544 
545  ParticleSetPool pp(c);
547 
549 
550  REQUIRE(wp.empty() == true);
551 
552  std::string wf_input = setupRotationXML("0.05", "0.1", coeff_rot_by_point05, coeff_rot_by_point1);
553 
555  bool okay = doc.parseFromString(wf_input);
556  REQUIRE(okay);
557 
558  xmlNodePtr root = doc.getRoot();
559 
560  wp.put(root);
561 
562  TrialWaveFunction* psi = wp.getWaveFunction("psi0");
563  REQUIRE(psi != nullptr);
564  REQUIRE(psi->getOrbitals().size() == 1);
565 
566  opt_variables_type opt_vars;
567  psi->checkInVariables(opt_vars);
568  opt_vars.resetIndex();
569  psi->checkOutVariables(opt_vars);
570  psi->resetParameters(opt_vars);
571 
572  ParticleSet* elec = pp.getParticleSet("e");
573  elec->update();
574 
575  double logval = psi->evaluateLog(*elec);
576  CHECK(logval == Approx(-9.26625670653773));
577 
578  CHECK(elec->G[0][0] == ValueApprox(-0.2758747113720909));
579  CHECK(elec->L[0] == ValueApprox(-0.316459652026054));
580  CHECK(elec->L[1] == ValueApprox(-0.6035591598540904));
581 
583  Vector<ValueType> dlogpsi(2);
584  Vector<ValueType> dhpsioverpsi(2);
585  psi->evaluateDerivatives(*elec, opt_vars, dlogpsi, dhpsioverpsi);
586 
587 
588  CHECK(dlogpsi[0] == ValueApprox(7.58753078998516));
589  CHECK(dlogpsi[1] == ValueApprox(2.58896036829191));
590  CHECK(dhpsioverpsi[0] == ValueApprox(2.59551625714144));
591  CHECK(dhpsioverpsi[1] == ValueApprox(1.70071425070404));
592 }
593 
594 // Test rotation using stored coefficients
595 // and test consistency between history list and global rotation
596 TEST_CASE("Rotated LCAO rotation consistency", "[qmcapp]")
597 {
600  using ValueMatrix = SPOSet::ValueMatrix;
601 
603  Communicate* c;
604  c = OHMMS::Controller;
605 
606  ParticleSetPool pp(c);
608 
610  REQUIRE(wp.empty() == true);
611 
612  // Only care that this wavefunction has 3 SPOs and a 3x3 coefficient matrix
613  const char* wf_input = R"(<wavefunction target='e'>
614 
615  <sposet_collection type="MolecularOrbital">
616  <!-- Use a single Slater Type Orbital (STO) for the basis. Cusp condition is correct. -->
617  <basisset keyword="STO" transform="no">
618  <atomicBasisSet type="STO" elementType="He" normalized="no">
619  <basisGroup rid="R0" l="0" m="0" type="Slater">
620  <radfunc n="1" exponent="2.0"/>
621  </basisGroup>
622  <basisGroup rid="R1" l="0" m="0" type="Slater">
623  <radfunc n="2" exponent="1.0"/>
624  </basisGroup>
625  <basisGroup rid="R2" l="0" m="0" type="Slater">
626  <radfunc n="3" exponent="1.0"/>
627  </basisGroup>
628  </atomicBasisSet>
629  </basisset>
630  <rotated_sposet name="rot-spo-up">
631  <sposet basisset="LCAOBSet" name="spo-up">
632  <coefficient id="updetC" type="Array" size="3">
633  1.0 0.0 0.0
634  0.0 1.0 0.0
635  0.0 0.0 1.0
636  </coefficient>
637  </sposet>
638  </rotated_sposet>
639  <rotated_sposet name="rot-spo-down">
640  <sposet basisset="LCAOBSet" name="spo-down">
641  <coefficient id="updetC" type="Array" size="3">
642  1.0 0.0 0.0
643  0.0 1.0 0.0
644  0.0 0.0 1.0
645  </coefficient>
646  </sposet>
647  </rotated_sposet>
648  </sposet_collection>
649  <determinantset type="MO" key="STO" transform="no" source="ion0">
650  <slaterdeterminant>
651  <determinant id="rot-spo-up"/>
652  <determinant id="rot-spo-down"/>
653  </slaterdeterminant>
654  </determinantset>
655  </wavefunction>)";
656 
658  bool okay = doc.parseFromString(wf_input);
659  REQUIRE(okay);
660 
661  xmlNodePtr root = doc.getRoot();
662 
663  wp.put(root);
664 
665  TrialWaveFunction* psi = wp.getWaveFunction("psi0");
666  REQUIRE(psi != nullptr);
667  REQUIRE(psi->getOrbitals().size() == 1);
668 
669  // Type should be pointer to SlaterDet
670  auto orb1 = psi->getOrbitals()[0].get();
671  SlaterDet* sdet = dynamic_cast<SlaterDet*>(orb1);
672  REQUIRE(sdet != nullptr);
673 
674  // Use the SPOs from different spins to separately track rotation applied using the stored coefficients
675  // versus the regular coefficients.
676  // The coefficient matrices should match after the same rotations are applied to each.
677  SPOSetPtr spoptr = sdet->getPhi(0);
678  RotatedSPOs* rot_spo = dynamic_cast<RotatedSPOs*>(spoptr);
679  REQUIRE(rot_spo != nullptr);
680 
681  SPOSetPtr spoptr1 = sdet->getPhi(1);
682  RotatedSPOs* global_rot_spo = dynamic_cast<RotatedSPOs*>(spoptr1);
683  REQUIRE(global_rot_spo != nullptr);
684 
685  std::vector<RealType> params1 = {0.1, 0.2};
686 
687  // Apply against the existing coefficients
688  rot_spo->apply_rotation(params1, false);
689 
690  // Apply against the stored coefficients
691  global_rot_spo->apply_rotation(params1, true);
692 
693  // Value after first rotation, computed from gen_matrix_ops.py
694  // clang-format off
695  std::vector<ValueType> rot_data0 =
696  { 0.975103993210479, 0.0991687475215628, 0.198337495043126,
697  -0.0991687475215628, 0.995020798642096, -0.00995840271580824,
698  -0.198337495043126, -0.00995840271580824, 0.980083194568384 };
699  // clang-format on
700 
701  ValueMatrix new_rot_m0(rot_data0.data(), 3, 3);
702 
703  LCAOrbitalSet* lcao1 = dynamic_cast<LCAOrbitalSet*>(rot_spo->Phi_.get());
704  LCAOrbitalSet* lcao_global = dynamic_cast<LCAOrbitalSet*>(global_rot_spo->Phi_.get());
705 
706  CheckMatrixResult check_matrix_result = checkMatrix(*lcao1->C, *lcao_global->C, true);
707  CHECKED_ELSE(check_matrix_result.result) { FAIL(check_matrix_result.result_message); }
708 
709  CheckMatrixResult check_matrix_result0 = checkMatrix(*lcao_global->C, new_rot_m0, true);
710  CHECKED_ELSE(check_matrix_result0.result) { FAIL(check_matrix_result0.result_message); }
711 
712  std::vector<RealType> old_params = {0.0, 0.0, 0.0};
713  std::vector<RealType> new_params(3);
714  global_rot_spo->applyDeltaRotation(params1, old_params, new_params);
715 
716  std::vector<RealType> params2 = {0.3, 0.15};
717  rot_spo->apply_rotation(params2, false);
718 
719  std::vector<RealType> new_params2(3);
720  global_rot_spo->applyDeltaRotation(params2, new_params, new_params2);
721  CheckMatrixResult check_matrix_result2 = checkMatrix(*lcao1->C, *lcao_global->C, true);
722  CHECKED_ELSE(check_matrix_result2.result) { FAIL(check_matrix_result2.result_message); }
723 
724  // Value after two rotations, computed from gen_matrix_ops.py
725  // clang-format off
726  std::vector<ValueType> rot_data3 =
727  { 0.862374825309137, 0.38511734273482, 0.328624851461217,
728  -0.377403929117215, 0.921689108007811, -0.0897522281988318,
729  -0.337455085840952, -0.046624248032951, 0.940186281826872 };
730  // clang-format on
731 
732  ValueMatrix new_rot_m3(rot_data3.data(), 3, 3);
733 
734  CheckMatrixResult check_matrix_result3 = checkMatrix(*lcao1->C, new_rot_m3, true);
735  CHECKED_ELSE(check_matrix_result3.result) { FAIL(check_matrix_result3.result_message); }
736 
737  // Need to flip the sign on the first two entries to match the output from gen_matrix_ops.py
738  std::vector<ValueType> expected_param = {0.3998099017676912, 0.34924318065960236, -0.02261313113492491};
739  for (int i = 0; i < expected_param.size(); i++)
740  CHECK(new_params2[i] == Approx(expected_param[i]));
741 }
742 
743 // Reference values from rot_be_sto_wf.py
744 // Uses single determinant code path
745 TEST_CASE("Rotated LCAO Be single determinant", "[qmcapp]")
746 {
748  Communicate* c;
749  c = OHMMS::Controller;
750 
751  ParticleSetPool pp(c);
753 
755 
756  REQUIRE(wp.empty() == true);
757 
759  bool okay = doc.parse("rot_Be_STO.wfnoj.xml");
760  REQUIRE(okay);
761  xmlNodePtr root = doc.getRoot();
762 
763  wp.put(xmlFirstElementChild(root));
764 
765 
766  TrialWaveFunction* psi = wp.getWaveFunction("psi0");
767  REQUIRE(psi != nullptr);
768  REQUIRE(psi->getOrbitals().size() == 1);
769 
770  opt_variables_type opt_vars;
771  psi->checkInVariables(opt_vars);
772  opt_vars.resetIndex();
773  psi->checkOutVariables(opt_vars);
774  psi->resetParameters(opt_vars);
775 
776  ParticleSet* elec = pp.getParticleSet("e");
777  elec->update();
778 
779 
780  double logval = psi->evaluateLog(*elec);
781  CHECK(logval == Approx(-17.768474132175342));
782 
784  Vector<ValueType> dlogpsi(10);
785  Vector<ValueType> dhpsioverpsi(10);
786  psi->evaluateDerivatives(*elec, opt_vars, dlogpsi, dhpsioverpsi);
787 
788  CHECK(dlogpsi[0] == ValueApprox(0.24797938203759148));
789  CHECK(dlogpsi[1] == ValueApprox(0.41454059122930453));
790  CHECK(dlogpsi[2] == ValueApprox(0.7539626161586822));
791  CHECK(dlogpsi[3] == ValueApprox(3.13489394217799));
792  CHECK(dlogpsi[4] == ValueApprox(8.47176722646749));
793  CHECK(dlogpsi[5] == ValueApprox(-0.48182453464906033));
794  CHECK(dlogpsi[6] == ValueApprox(2.269206401396164));
795  CHECK(dlogpsi[7] == ValueApprox(-1.883221269688377));
796  CHECK(dlogpsi[8] == ValueApprox(-19.450964163527598));
797  CHECK(dlogpsi[9] == ValueApprox(-47.28198556252034));
798 
799  CHECK(dhpsioverpsi[0] == ValueApprox(0.3662586398420111));
800  CHECK(dhpsioverpsi[1] == ValueApprox(-5.544323554018982));
801  CHECK(dhpsioverpsi[2] == ValueApprox(-0.7790656028274846));
802  CHECK(dhpsioverpsi[3] == ValueApprox(24.930187483208087));
803  CHECK(dhpsioverpsi[4] == ValueApprox(71.30301022344871));
804  CHECK(dhpsioverpsi[5] == ValueApprox(-1.1614358798793771));
805  CHECK(dhpsioverpsi[6] == ValueApprox(17.678711245652913));
806  CHECK(dhpsioverpsi[7] == ValueApprox(2.491238469662668));
807  CHECK(dhpsioverpsi[8] == ValueApprox(-79.37464297365679));
808  CHECK(dhpsioverpsi[9] == ValueApprox(-227.0976672502695));
809 }
810 
811 // Reference values from rot_be_sto_wf.py
812 // Uses multi-determinant code path with one determinant
813 TEST_CASE("Rotated LCAO Be multi determinant with one determinant", "[qmcapp]")
814 {
816  Communicate* c;
817  c = OHMMS::Controller;
818 
819  ParticleSetPool pp(c);
821 
823 
824  REQUIRE(wp.empty() == true);
825 
827  bool okay = doc.parse("rot_multi_1det_Be_STO.wfnoj.xml");
828  REQUIRE(okay);
829  xmlNodePtr root = doc.getRoot();
830 
831  wp.put(xmlFirstElementChild(root));
832 
833  TrialWaveFunction* psi = wp.getWaveFunction("psi0");
834  REQUIRE(psi != nullptr);
835  REQUIRE(psi->getOrbitals().size() == 1);
836 
837  opt_variables_type opt_vars;
838  psi->checkInVariables(opt_vars);
839  opt_vars.resetIndex();
840  psi->checkOutVariables(opt_vars);
841  psi->resetParameters(opt_vars);
842 
843  ParticleSet* elec = pp.getParticleSet("e");
844  elec->update();
845 
846 
847  double logval = psi->evaluateLog(*elec);
848  CHECK(logval == Approx(-17.768474132175342));
849 
851  Vector<ValueType> dlogpsi(10);
852  Vector<ValueType> dhpsioverpsi(10);
853  psi->evaluateDerivatives(*elec, opt_vars, dlogpsi, dhpsioverpsi);
854 
855  CHECK(dlogpsi[0] == ValueApprox(0.24797938203759148));
856  CHECK(dlogpsi[1] == ValueApprox(0.41454059122930453));
857  CHECK(dlogpsi[2] == ValueApprox(0.7539626161586822));
858  CHECK(dlogpsi[3] == ValueApprox(3.13489394217799));
859  CHECK(dlogpsi[4] == ValueApprox(8.47176722646749));
860  CHECK(dlogpsi[5] == ValueApprox(-0.48182453464906033));
861  CHECK(dlogpsi[6] == ValueApprox(2.269206401396164));
862  CHECK(dlogpsi[7] == ValueApprox(-1.883221269688377));
863  CHECK(dlogpsi[8] == ValueApprox(-19.450964163527598));
864  CHECK(dlogpsi[9] == ValueApprox(-47.28198556252034));
865 
866  CHECK(dhpsioverpsi[0] == ValueApprox(0.3662586398420111));
867  CHECK(dhpsioverpsi[1] == ValueApprox(-5.544323554018982));
868  CHECK(dhpsioverpsi[2] == ValueApprox(-0.7790656028274846));
869  CHECK(dhpsioverpsi[3] == ValueApprox(24.930187483208087));
870  CHECK(dhpsioverpsi[4] == ValueApprox(71.30301022344871));
871  CHECK(dhpsioverpsi[5] == ValueApprox(-1.1614358798793771));
872  CHECK(dhpsioverpsi[6] == ValueApprox(17.678711245652913));
873  CHECK(dhpsioverpsi[7] == ValueApprox(2.491238469662668));
874  CHECK(dhpsioverpsi[8] == ValueApprox(-79.37464297365679));
875  CHECK(dhpsioverpsi[9] == ValueApprox(-227.0976672502695));
876 }
877 
878 // Reference values from rot_multi_be_sto_wf.py
879 // Uses multi-determinant code path with two determinants
880 TEST_CASE("Rotated LCAO Be two determinant", "[qmcapp]")
881 {
883  Communicate* c;
884  c = OHMMS::Controller;
885 
886  ParticleSetPool pp(c);
888 
890 
891  REQUIRE(wp.empty() == true);
892 
894  bool okay = doc.parse("rot_multi_2det_Be_STO.wfnoj.xml");
895  REQUIRE(okay);
896  xmlNodePtr root = doc.getRoot();
897 
898  wp.put(xmlFirstElementChild(root));
899 
900  TrialWaveFunction* psi = wp.getWaveFunction("psi0");
901  REQUIRE(psi != nullptr);
902  REQUIRE(psi->getOrbitals().size() == 1);
903 
904  opt_variables_type opt_vars;
905  psi->checkInVariables(opt_vars);
906  opt_vars.resetIndex();
907  psi->checkOutVariables(opt_vars);
908  psi->resetParameters(opt_vars);
909 
910  ParticleSet* elec = pp.getParticleSet("e");
911  elec->update();
912 
913 
914  double logval = psi->evaluateLog(*elec);
915  CHECK(logval == Approx(-17.762687110866413));
916 
918  Vector<ValueType> dlogpsi(16);
919  Vector<ValueType> dhpsioverpsi(16);
920  psi->evaluateDerivatives(*elec, opt_vars, dlogpsi, dhpsioverpsi);
921 
922  CHECK(dlogpsi[0] == ValueApprox(0.05770308755290168));
923  CHECK(dlogpsi[1] == ValueApprox(0.00593995768443123));
924  CHECK(dlogpsi[2] == ValueApprox(0.24654846443828843));
925  CHECK(dlogpsi[3] == ValueApprox(0.4214539468865001));
926  CHECK(dlogpsi[4] == ValueApprox(0.7484015451192123));
927  CHECK(dlogpsi[5] == ValueApprox(3.076586144487743));
928  CHECK(dlogpsi[6] == ValueApprox(8.329621106110908));
929  CHECK(dlogpsi[7] == ValueApprox(-0.4311398324864351));
930  CHECK(dlogpsi[8] == ValueApprox(2.2561123798306273));
931  CHECK(dlogpsi[9] == ValueApprox(-1.8723545015077454));
932  CHECK(dlogpsi[10] == ValueApprox(-19.33872609471596));
933  CHECK(dlogpsi[11] == ValueApprox(-47.00915390726143));
934  CHECK(dlogpsi[12] == ValueApprox(-0.05463186141658209));
935  CHECK(dlogpsi[13] == ValueApprox(0.045055811131004785));
936  CHECK(dlogpsi[14] == ValueApprox(0.46675941272234));
937  CHECK(dlogpsi[15] == ValueApprox(1.1352711502777513));
938 
939 
940  CHECK(dhpsioverpsi[0] == ValueApprox(0.2761674423047662));
941  CHECK(dhpsioverpsi[1] == ValueApprox(0.022999975062422046));
942  CHECK(dhpsioverpsi[2] == ValueApprox(0.3572968312376671));
943  CHECK(dhpsioverpsi[3] == ValueApprox(-5.459873357259045));
944  CHECK(dhpsioverpsi[4] == ValueApprox(-0.792225084691375));
945  CHECK(dhpsioverpsi[5] == ValueApprox(24.453138754349123));
946  CHECK(dhpsioverpsi[6] == ValueApprox(70.0280297306038));
947  CHECK(dhpsioverpsi[7] == ValueApprox(-1.0272848501840672));
948  CHECK(dhpsioverpsi[8] == ValueApprox(17.514031530576368));
949  CHECK(dhpsioverpsi[9] == ValueApprox(2.52887169464403));
950  CHECK(dhpsioverpsi[10] == ValueApprox(-78.37945447401765));
951  CHECK(dhpsioverpsi[11] == ValueApprox(-224.4814690906403));
952  CHECK(dhpsioverpsi[12] == ValueApprox(-0.6346957697642424));
953  CHECK(dhpsioverpsi[13] == ValueApprox(0.03270289146243591));
954  CHECK(dhpsioverpsi[14] == ValueApprox(3.263830358386392));
955  CHECK(dhpsioverpsi[15] == ValueApprox(8.944714289946793));
956 }
957 
958 } // namespace qmcplusplus
base class for Single-particle orbital sets
Definition: SPOSet.h:46
RealType evaluateLog(ParticleSet &P)
evalaute the log (internally gradients and laplacian) of the trial wavefunction.
class that handles xmlDoc
Definition: Libxml2Doc.h:76
const std::string coeff_rot_by_point05
helper functions for EinsplineSetBuilder
Definition: Configuration.h:43
void evaluateDerivatives(ParticleSet &P, const opt_variables_type &optvars, Vector< ValueType > &dlogpsi, Vector< ValueType > &dhpsioverpsi)
evaluate derivatives of KE wrt optimizable varibles
QTBase::RealType RealType
Definition: Configuration.h:58
bool put(xmlNodePtr cur)
process an xml element
class ProjectData
Definition: ProjectData.h:36
TEST_CASE("complex_helper", "[type_traits]")
void resetParameters(const opt_variables_type &active)
Set values of parameters in each component from the global list.
xmlNodePtr getRoot()
Definition: Libxml2Doc.h:88
OrbitalSetTraits< ValueType >::ValueMatrix ValueMatrix
Definition: SPOSet.h:50
CHECKED_ELSE(check_matrix_result.result)
ProjectData test_project("test", ProjectData::DriverVersion::BATCH)
auto check_matrix_result
void update(bool skipSK=false)
update the internal data
const std::string coeff_rot_by_point1
Communicate * Controller
Global Communicator for a process.
Definition: Communicate.cpp:35
ParticleLaplacian L
laplacians of the particles
Definition: ParticleSet.h:85
const std::string identity_coeff
const RuntimeOptions & getRuntimeOptions() const noexcept
Wrapping information on parallelism.
Definition: Communicate.h:68
Specialized paritlce class for atomistic simulations.
Definition: ParticleSet.h:55
const std::string coeff_rot_by_point2
QTBase::ValueType ValueType
Definition: Configuration.h:60
REQUIRE(std::filesystem::exists(filename))
SPOSetPtr getPhi(int i=0)
Definition: SlaterDet.h:251
ParticleSet * getParticleSet(const std::string &pname)
get a named ParticleSet
void setupParticleSetPool(ParticleSetPool &pp)
static void mw_evaluateParameterDerivatives(const RefVectorWithLeader< TrialWaveFunction > &wf_list, const RefVectorWithLeader< ParticleSet > &p_list, const opt_variables_type &optvars, RecordArray< ValueType > &dlogpsi, RecordArray< ValueType > &dhpsioverpsi)
Declaration of WaveFunctionPool.
void applyDeltaRotation(const std::vector< ValueType > &delta_param, const std::vector< ValueType > &old_param, std::vector< ValueType > &new_param)
Manage a collection of ParticleSet objects.
ParticleGradient G
gradients of the particles
Definition: ParticleSet.h:83
std::shared_ptr< OffloadValueMatrix > C
pointer to matrix containing the coefficients
Definition: LCAOrbitalSet.h:42
class to handle a set of variables that can be modified during optimizations
Definition: VariableSet.h:49
std::vector< std::unique_ptr< WaveFunctionComponent > > const & getOrbitals()
void checkOutVariables(const opt_variables_type &o)
Check out optimizable variables Assign index mappings from global list (o) to local values in each co...
void setupParticleSetPoolBe(ParticleSetPool &pp)
void apply_rotation(const std::vector< ValueType > &param, bool use_stored_copy)
Class to represent a many-body trial wave function.
bool parseFromString(const std::string_view data)
Definition: Libxml2Doc.cpp:204
CheckMatrixResult checkMatrix(M1 &a_mat, M2 &b_mat, const bool check_all=false, std::optional< const double > eps=std::nullopt)
This function checks equality a_mat and b_mat elements M1, M2 need to have their element type declare...
Definition: checkMatrix.hpp:63
std::unique_ptr< SPOSet > Phi_
Definition: RotatedSPOs.h:119
CHECK(log_values[0]==ComplexApprox(std::complex< double >{ 5.603777579195571, -6.1586603331188225 }))
bool parse(const std::string &fname)
Definition: Libxml2Doc.cpp:180
LatticeGaussianProduct::ValueType ValueType
class to handle linear combinations of basis orbitals used to evaluate the Dirac determinants.
Definition: LCAOrbitalSet.h:30
std::string setupRotationXML(const std::string &rot_angle_up, const std::string &rot_angle_down, const std::string &coeff_up, const std::string &coeff_down)
Manage a collection of TrialWaveFunction objects.
Declaration of ParticleSetPool.
void checkInVariables(opt_variables_type &o)
Check in an optimizable parameter.