|
15 | 15 | # You should have received a copy of the GNU Lesser General Public License |
16 | 16 | # along with xtb. If not, see <https://www.gnu.org/licenses/>. |
17 | 17 |
|
18 | | -from xtb.interface import Calculator, Param |
19 | | -from pytest import approx |
| 18 | +from xtb.interface import ( |
| 19 | + XTBException, |
| 20 | + Molecule, |
| 21 | + Calculator, |
| 22 | + Results, |
| 23 | + Param, |
| 24 | + VERBOSITY_MINIMAL, |
| 25 | +) |
| 26 | +from pytest import approx, raises |
20 | 27 | import numpy as np |
21 | 28 |
|
22 | 29 |
|
| 30 | +def test_molecule(): |
| 31 | + """check if the molecular structure data is working as expected.""" |
| 32 | + |
| 33 | + numbers = np.array( |
| 34 | + [6, 7, 6, 7, 6, 6, 6, 8, 7, 6, 8, 7, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] |
| 35 | + ) |
| 36 | + positions = np.array( |
| 37 | + [ |
| 38 | + [ 2.02799738646442, 0.09231312124713,-0.14310895950963], |
| 39 | + [ 4.75011007621000, 0.02373496014051,-0.14324124033844], |
| 40 | + [ 6.33434307654413, 2.07098865582721,-0.14235306905930], |
| 41 | + [ 8.72860718071825, 1.38002919517619,-0.14265542523943], |
| 42 | + [ 8.65318821103610,-1.19324866489847,-0.14231527453678], |
| 43 | + [ 6.23857175648671,-2.08353643730276,-0.14218299370797], |
| 44 | + [ 5.63266886875962,-4.69950321056008,-0.13940509630299], |
| 45 | + [ 3.44931709749015,-5.48092386085491,-0.14318454855466], |
| 46 | + [ 7.77508917214346,-6.24427872938674,-0.13107140408805], |
| 47 | + [10.30229550927022,-5.39739796609292,-0.13672168520430], |
| 48 | + [12.07410272485492,-6.91573621641911,-0.13666499342053], |
| 49 | + [10.70038521493902,-2.79078533715849,-0.14148379504141], |
| 50 | + [13.24597858727017,-1.76969072232377,-0.14218299370797], |
| 51 | + [ 7.40891694074004,-8.95905928176407,-0.11636933482904], |
| 52 | + [ 1.38702118184179, 2.05575746325296,-0.14178615122154], |
| 53 | + [ 1.34622199478497,-0.86356704498496, 1.55590600570783], |
| 54 | + [ 1.34624089204623,-0.86133716815647,-1.84340893849267], |
| 55 | + [ 5.65596919189118, 4.00172183859480,-0.14131371969009], |
| 56 | + [14.67430918222276,-3.26230980007732,-0.14344911021228], |
| 57 | + [13.50897177220290,-0.60815166181684, 1.54898960808727], |
| 58 | + [13.50780014200488,-0.60614855212345,-1.83214617078268], |
| 59 | + [ 5.41408424778406,-9.49239668625902,-0.11022772492007], |
| 60 | + [ 8.31919801555568,-9.74947502841788, 1.56539243085954], |
| 61 | + [ 8.31511620712388,-9.76854236502758,-1.79108242206824], |
| 62 | + ] |
| 63 | + ) |
| 64 | + filename = "xtb-error.log" |
| 65 | + message = "Expecting nuclear fusion warning" |
| 66 | + |
| 67 | + # Constructor should raise an error for nuclear fusion input |
| 68 | + with raises(XTBException, match="Could not initialize"): |
| 69 | + mol = Molecule(numbers, np.zeros((24, 3))) |
| 70 | + |
| 71 | + # The Python class should protect from garbage input like this |
| 72 | + with raises(ValueError, match="Dimension missmatch"): |
| 73 | + mol = Molecule(np.array([1, 1, 1]), positions) |
| 74 | + |
| 75 | + # Also check for sane coordinate input |
| 76 | + with raises(ValueError, match="Expected tripels"): |
| 77 | + mol = Molecule(numbers, np.random.rand(7)) |
| 78 | + |
| 79 | + # Construct real molecule |
| 80 | + mol = Molecule(numbers, positions) |
| 81 | + |
| 82 | + # Try to update a structure with missmatched coordinates |
| 83 | + with raises(ValueError, match="Dimension missmatch for positions"): |
| 84 | + mol.update(np.random.rand(7)) |
| 85 | + |
| 86 | + # Try to add a missmatched lattice |
| 87 | + with raises(ValueError, match="Invalid lattice provided"): |
| 88 | + mol.update(positions, np.random.rand(7)) |
| 89 | + |
| 90 | + # Try to update a structure with nuclear fusion coordinates |
| 91 | + with raises(XTBException, match="Could not update"): |
| 92 | + mol.update(np.zeros((24, 3))) |
| 93 | + |
| 94 | + # Redirect API output to file |
| 95 | + mol.set_output(filename) |
| 96 | + |
| 97 | + # Flush the error from the environment log |
| 98 | + mol.show(message) |
| 99 | + |
| 100 | + # Reset to correct positions, Molecule object should still be intact |
| 101 | + mol.update(positions) |
| 102 | + |
| 103 | + |
23 | 104 | def test_gfn2_xtb(): |
24 | 105 | """check if the GFN2-xTB interface is working correctly.""" |
25 | 106 | thr = 1.0e-8 |
| 107 | + thr2 = 1.0e-6 |
26 | 108 |
|
27 | 109 | numbers = np.array( |
28 | 110 | [6, 7, 6, 7, 6, 6, 6, 8, 7, 6, 8, 7, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] |
@@ -83,17 +165,29 @@ def test_gfn2_xtb(): |
83 | 165 | [ 3.81284918e-04,-1.28923994e-04,-2.34336886e-03], |
84 | 166 | ] |
85 | 167 | ) |
| 168 | + charges = np.array([ |
| 169 | + -0.05445590, -0.00457526, 0.08391889, -0.27870751, 0.11914924, |
| 170 | + -0.02621044, 0.26115960, -0.44071824, -0.10804747, 0.30411699, |
| 171 | + -0.44083760, -0.07457706, -0.04790859, -0.03738239, 0.06457802, |
| 172 | + 0.08293905, 0.08296802, 0.05698136, 0.09025556, 0.07152988, |
| 173 | + 0.07159003, 0.08590674, 0.06906357, 0.06926350]) |
86 | 174 |
|
87 | 175 | calc = Calculator(Param.GFN2xTB, numbers, positions) |
88 | | - res = calc.singlepoint() |
| 176 | + calc.set_verbosity(VERBOSITY_MINIMAL) |
| 177 | + assert calc.check() == 0 |
| 178 | + |
| 179 | + res = Results(calc) |
| 180 | + calc.singlepoint(res) |
89 | 181 |
|
90 | 182 | assert approx(res.get_energy(), thr) == -42.14746312757416 |
91 | 183 | assert approx(res.get_gradient(), thr) == gradient |
| 184 | + assert approx(res.get_charges(), thr2) == charges |
92 | 185 |
|
93 | 186 |
|
94 | 187 | def test_gfn1_xtb(): |
95 | 188 | """check if the GFN1-xTB interface is working correctly.""" |
96 | 189 | thr = 1.0e-8 |
| 190 | + thr2 = 1.0e-6 |
97 | 191 |
|
98 | 192 | numbers = np.array( |
99 | 193 | [6, 7, 6, 7, 6, 6, 6, 8, 7, 6, 8, 7, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] |
@@ -154,9 +248,24 @@ def test_gfn1_xtb(): |
154 | 248 | [ 2.89640303e-04,-2.09943109e-04,-1.35065134e-03], |
155 | 249 | ] |
156 | 250 | ) |
| 251 | + dipole = np.array([-0.81941935, 1.60912848, 0.00564382]) |
| 252 | + |
157 | 253 |
|
158 | 254 | calc = Calculator(Param.GFN1xTB, numbers, positions) |
159 | | - res = calc.singlepoint() |
| 255 | + |
| 256 | + res = Results(calc) |
| 257 | + |
| 258 | + # check if we cannot retrieve properties from the unallocated result |
| 259 | + with raises(XTBException, match="Virial is not available"): |
| 260 | + res.get_virial() |
| 261 | + res.show("Release error log") |
| 262 | + with raises(XTBException, match="Bond orders are not available"): |
| 263 | + res.get_bond_orders() |
| 264 | + res.show("Release error log") |
| 265 | + |
| 266 | + # Start calculation by restarting with result |
| 267 | + calc.singlepoint(res) |
160 | 268 |
|
161 | 269 | assert approx(res.get_energy(), thr) == -44.509702418208896 |
162 | 270 | assert approx(res.get_gradient(), thr) == gradient |
| 271 | + assert approx(res.get_dipole(), thr2) == dipole |
0 commit comments