Skip to content

Commit b7daee2

Browse files
committed
Tests: xTB parser
1 parent 305dd18 commit b7daee2

1 file changed

Lines changed: 78 additions & 1 deletion

File tree

arc/parser/parser_test.py

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,10 +732,12 @@ def test_parse_1d_scan_energies(self):
732732
8.397974544321187, 7.071194090611243, 5.214457982623571, 3.4362612986915337,
733733
1.958199294316728, 0.8766536693692615, 0.22504856466548517,
734734
0.0004629911018128041])
735+
# Length-matched: 44 energies -> 44 angles (step = 360/(n+1) = 8 deg)
736+
self.assertEqual(len(angles_3), len(energies_3))
735737
self.assertEqual(angles_3, [0.0, 8.0, 16.0, 24.0, 32.0, 40.0, 48.0, 56.0, 64.0, 72.0, 80.0, 88.0, 96.0,
736738
104.0, 112.0, 120.0, 128.0, 136.0, 144.0, 152.0, 160.0, 168.0, 176.0, 184.0, 192.0,
737739
200.0, 208.0, 216.0, 224.0, 232.0, 240.0, 248.0, 256.0, 264.0, 272.0, 280.0, 288.0,
738-
296.0, 304.0, 312.0, 320.0, 328.0, 336.0, 344.0, 352.0])
740+
296.0, 304.0, 312.0, 320.0, 328.0, 336.0, 344.0])
739741

740742
path_4 = os.path.join(ARC_TESTING_PATH, 'rotor_scans', 'orca', 'cc.txt')
741743
energies_4, angles_4 = parser.parse_1d_scan_energies(log_file_path=path_4)
@@ -1000,6 +1002,81 @@ def test_parse_active_space(self):
10001002
xyz="""N 0.0 0.0 0.0"""))
10011003
self.assertEqual(active, {'e_o': (5, 4), 'occ': [3, 1, 1, 0, 1, 0, 0, 0], 'closed': [1, 0, 0, 0, 0, 0, 0, 0]})
10021004

1005+
# ----- xTB-specific improvements -----
1006+
1007+
def test_xtb_logfile_contains_errors(self):
1008+
"""xTB error detection should return None for clean files."""
1009+
from arc.parser.adapters.xtb import XTBParser
1010+
clean_path = os.path.join(ARC_TESTING_PATH, 'sp', 'NCC_xTB.out')
1011+
adapter = XTBParser(log_file_path=clean_path)
1012+
self.assertIsNone(adapter.logfile_contains_errors())
1013+
1014+
def test_xtb_parse_e_elect_avoids_false_match(self):
1015+
"""parse_e_elect should not match 'total energy gain' lines from optimization deltas.
1016+
1017+
The CO2_xtb.out file contains both 'total energy gain : -0.1724555 Eh' (delta)
1018+
and ':: total energy -10.308452243026 Eh' (final). The parser must return the
1019+
final value, not the delta.
1020+
"""
1021+
from arc.constants import E_h_kJmol
1022+
path = os.path.join(ARC_TESTING_PATH, 'freq', 'CO2_xtb.out')
1023+
e_elect = parser.parse_e_elect(path)
1024+
self.assertAlmostEqual(e_elect, -10.308452243026 * E_h_kJmol, places=3)
1025+
# Make sure we did NOT pick up the delta value
1026+
delta_value = -0.1724555 * E_h_kJmol # ~-452 kJ/mol
1027+
self.assertNotAlmostEqual(e_elect, delta_value, places=1)
1028+
1029+
def test_xtb_parse_frequencies_takes_last_block(self):
1030+
"""parse_frequencies should return the LAST eigval block.
1031+
1032+
TS_NH2+N2H3_xtb.out prints frequencies twice. Both blocks should be identical;
1033+
if the parser stops after the first block, it would still get the right values,
1034+
but using the last block is more robust against truncated/duplicate blocks.
1035+
"""
1036+
path = os.path.join(ARC_TESTING_PATH, 'freq', 'TS_NH2+N2H3_xtb.out')
1037+
freqs = parser.parse_frequencies(log_file_path=path)
1038+
self.assertIsNotNone(freqs)
1039+
# Imaginary frequency should be the first non-zero one
1040+
self.assertAlmostEqual(freqs[0], -781.89, places=1)
1041+
# Last frequency
1042+
self.assertAlmostEqual(freqs[-1], 3467.59, places=1)
1043+
# 24 modes total (3*8 - 6 = 18 + 6 zero = 24, but only 18 non-zero in TS)
1044+
self.assertEqual(len(freqs), 18)
1045+
1046+
def test_xtb_parse_zpe_correction_regex(self):
1047+
"""parse_zpe_correction uses regex to find ZPE in scientific notation safely."""
1048+
from arc.constants import E_h_kJmol
1049+
path = os.path.join(ARC_TESTING_PATH, 'freq', 'TS_NH2+N2H3_xtb.out')
1050+
zpe = parser.parse_zpe_correction(log_file_path=path)
1051+
self.assertIsNotNone(zpe)
1052+
self.assertAlmostEqual(zpe, 0.056690417480 * E_h_kJmol, places=3)
1053+
1054+
def test_xtb_parse_1d_scan_energies_length_match(self):
1055+
"""Energies and angles returned by parse_1d_scan_energies must have matching lengths."""
1056+
path = os.path.join(ARC_TESTING_PATH, 'rotor_scans', 'xtb_1', 'output.out')
1057+
energies, angles = parser.parse_1d_scan_energies(log_file_path=path)
1058+
self.assertIsNotNone(energies)
1059+
self.assertIsNotNone(angles)
1060+
self.assertEqual(len(energies), len(angles))
1061+
# First angle = 0, all angles strictly increasing
1062+
self.assertEqual(angles[0], 0.0)
1063+
for i in range(1, len(angles)):
1064+
self.assertGreater(angles[i], angles[i - 1])
1065+
# All angles should be in [0, 360)
1066+
self.assertLess(angles[-1], 360.0)
1067+
1068+
def test_xtb_parse_geometry_resets_on_new_block(self):
1069+
"""parse_geometry should not accumulate atoms across multiple geometry blocks.
1070+
1071+
For CO2_xtb.out (Turbomol $coord with 3 atoms), the parser should return
1072+
exactly 3 atoms, not 6 or more even if the file mentions multiple blocks.
1073+
"""
1074+
path = os.path.join(ARC_TESTING_PATH, 'freq', 'CO2_xtb.out')
1075+
xyz = parser.parse_geometry(log_file_path=path)
1076+
self.assertIsNotNone(xyz)
1077+
self.assertEqual(len(xyz['symbols']), 3)
1078+
self.assertEqual(xyz['symbols'], ('O', 'C', 'O'))
1079+
10031080

10041081
def test_parse_opt_steps(self):
10051082
"""Test parsing the number of optimization steps from various ESS output files."""

0 commit comments

Comments
 (0)