Skip to content

Commit a8883bf

Browse files
committed
Updates
1 parent a100e52 commit a8883bf

File tree

7 files changed

+60762
-81
lines changed

7 files changed

+60762
-81
lines changed

arc/job/adapters/qchem.py

Lines changed: 73 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -383,85 +383,80 @@ def write_input_file(self) -> None:
383383
with open(os.path.join(self.local_path, input_filenames[self.job_adapter]), 'w') as f:
384384
f.write(Template(input_template).render(**input_dict))
385385
def generate_qchem_scan_angles(self,start_angle: int, step: int) -> (int, int, int, int):
386-
"""
387-
Generates the angles for a Q-Chem scan. The scan is split into two parts, one from start_angle to 180, and one from -180 to end_angle.
388-
389-
Parameters
390-
----------
391-
start_angle : int
392-
The starting angle for the scan
393-
step : int
394-
The step size for the scan
395-
396-
Returns
397-
-------
398-
scan1_start : int
399-
The starting angle for the first part of the scan
400-
scan1_end : int
401-
The ending angle for the first part of the scan
402-
scan2_start : int
403-
The starting angle for the second part of the scan
404-
scan2_end : int
405-
The ending angle for the second part of the scan
406-
"""
407-
408-
# First, we need to check that the start_angle is within the range of -180 to 180, and if not, convert it to be within that range
409-
if start_angle > 180:
410-
start_angle = start_angle - 360
411-
412-
413-
# This sets the end angle but does not take into account the limit of -180 to 180
414-
end_angle = start_angle - step
415-
416-
# This function wraps the scan2_start within the range of -180 to 180
417-
wrap_within_range = lambda number, addition: (number + addition) % 360 - 360 if (number + addition) % 360 > 180 else (number + addition) % 360
418-
419-
# This function converts the angles to be within the range of -180 to 180
420-
convert_angle = lambda angle: angle % 360 if angle >= 0 else ( angle % 360 if angle <= -180 else (angle % 360) - 360)
421-
422-
# This converts the angles to be within the range of -180 to 180
423-
start_angle = convert_angle(start_angle)
424-
end_angle = convert_angle(end_angle)
386+
"""Generates angles for a Q-Chem dihedral scan, split into two segments.
387+
388+
This function computes the angles for a Q-Chem dihedral scan. The scan is
389+
divided into two parts: one spanning from the start_angle to 180 degrees,
390+
and the other from -180 degrees to the calculated end_angle based on the
391+
step size.
392+
393+
Args:
394+
start_angle (int): The initial angle for the scan.
395+
step (int): The incremental step size for the scan.
396+
397+
Returns:
398+
tuple of int: A tuple containing the start and end angles for both
399+
scan segments. It includes scan1_start, scan1_end,
400+
scan2_start, and scan2_end.
401+
"""
402+
403+
# First, we need to check that the start_angle is within the range of -180 to 180, and if not, convert it to be within that range
404+
if start_angle > 180:
405+
start_angle = start_angle - 360
406+
407+
408+
# This sets the end angle but does not take into account the limit of -180 to 180
409+
end_angle = start_angle - step
410+
411+
# This function wraps the scan2_start within the range of -180 to 180
412+
wrap_within_range = lambda number, addition: (number + addition) % 360 - 360 if (number + addition) % 360 > 180 else (number + addition) % 360
413+
414+
# This function converts the angles to be within the range of -180 to 180
415+
convert_angle = lambda angle: angle % 360 if angle >= 0 else ( angle % 360 if angle <= -180 else (angle % 360) - 360)
416+
417+
# This converts the angles to be within the range of -180 to 180
418+
start_angle = convert_angle(start_angle)
419+
end_angle = convert_angle(end_angle)
420+
421+
if start_angle == 0 and end_angle == 0:
422+
scan1_start = start_angle
423+
scan1_end = 180
424+
scan2_start = -180
425+
scan2_end = end_angle
426+
elif start_angle == 180:
427+
# This is a special case because the scan will be from 180 to 180
428+
# This is not allowed in Q-Chem so we split it into two scans
429+
# Arguably this could be done in one scan but it is easier to do it this way
430+
# We will need to find the starting angle that when added by the step size will be 180
431+
target_sum = 180
432+
quotient = target_sum // step
433+
starting_number = target_sum - (quotient * step)
434+
scan1_start = starting_number
435+
scan1_end = 180
436+
scan2_start = -180
437+
scan2_end = scan1_start - step
438+
elif start_angle <= end_angle:
439+
scan1_start = start_angle
440+
scan1_end = start_angle + (step * ((180 - start_angle)//step))
441+
scan2_start = convert_angle(scan1_end)
442+
scan2_end = end_angle
443+
elif (start_angle + step) > 180:
444+
# This is a special case because the scan will be from, for example, 178 to 178 for the first scan. Therefore, we should make it a single scan from end angle, 178, step size
445+
scan1_end = start_angle
446+
scan1_start = wrap_within_range(scan1_end, step)
447+
scan2_start = 0
448+
scan2_end = 0
449+
else:
450+
scan1_start = start_angle
451+
scan1_end = start_angle + (step * ((180 - start_angle)//step))
452+
scan2_start = wrap_within_range(scan1_end, step)
453+
scan2_end = end_angle
425454

426-
if start_angle == 0 and end_angle == 0:
427-
scan1_start = start_angle
428-
scan1_end = 180
429-
scan2_start = -180
430-
scan2_end = end_angle
431-
elif start_angle == 180:
432-
# This is a special case because the scan will be from 180 to 180
433-
# This is not allowed in Q-Chem so we split it into two scans
434-
# Arguably this could be done in one scan but it is easier to do it this way
435-
# We will need to find the starting angle that when added by the step size will be 180
436-
target_sum = 180
437-
quotient = target_sum // step
438-
starting_number = target_sum - (quotient * step)
439-
scan1_start = starting_number
440-
scan1_end = 180
441-
scan2_start = -180
442-
scan2_end = scan1_start - step
443-
elif start_angle <= end_angle:
444-
scan1_start = start_angle
445-
scan1_end = start_angle + (step * ((180 - start_angle)//step))
446-
scan2_start = convert_angle(scan1_end)
447-
scan2_end = end_angle
448-
elif (start_angle + step) > 180:
449-
# This is a special case because the scan will be from, for example, 178 to 178 for the first scan. Therefore, we should make it a single scan from end angle, 178, step size
450-
scan1_end = start_angle
451-
scan1_start = wrap_within_range(scan1_end, step)
452-
scan2_start = 0
453-
scan2_end = 0
454-
else:
455-
scan1_start = start_angle
456-
scan1_end = start_angle + (step * ((180 - start_angle)//step))
457-
scan2_start = wrap_within_range(scan1_end, step)
458-
scan2_end = end_angle
459-
460-
if scan2_start == scan2_end:
461-
scan2_start = 0
462-
scan2_end = 0
463-
464-
return int(scan1_start), int(scan1_end), int(scan2_start), int(scan2_end)
455+
if scan2_start == scan2_end:
456+
scan2_start = 0
457+
scan2_end = 0
458+
459+
return int(scan1_start), int(scan1_end), int(scan2_start), int(scan2_end)
465460

466461
def generate_scan_angles(self, req_angle: int, step: int) -> (int, int):
467462

arc/level_test.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,18 @@ def test_deduce_software_irc_with_both(self):
7070
self.assertEqual(level.software, 'gaussian') # gaussian is also available
7171

7272
@patch('arc.level.supported_ess', new=['qchem'])
73-
def test_deduce_software_irc_with_only_gaussian(self):
73+
def test_deduce_software_irc_with_only_qchem(self):
7474
"""Test deducing software for IRC job when only gaussian is supported."""
7575
level = Level(method='B3LYP', basis='6-311g+(d,f)')
7676
level.deduce_software(job_type='irc')
7777
self.assertEqual(level.software, 'qchem') # Only qchem is available
78+
79+
@patch('arc.level.supported_ess', new=['gaussian'])
80+
def test_deduce_software_irc_with_only_gaussian(self):
81+
"""Test deducing software for IRC job when only qchem is supported."""
82+
level = Level(method='B3LYP', basis='6-311g+(d,f)')
83+
level.deduce_software(job_type='irc')
84+
self.assertEqual(level.software, 'gaussian')
7885

7986
@patch('arc.level.supported_ess', new=[])
8087
def test_deduce_software_value_errors(self):

arc/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def parse_frequencies(path: str,
149149

150150
def parse_normal_mode_displacement(path: str,
151151
software: Optional[str] = None,
152-
raise_error: bool = False, # TODO: Why is this true? What is it supposed to do?
152+
raise_error: bool = False,
153153
) -> Tuple[np.ndarray, np.ndarray]:
154154
"""
155155
Parse frequencies and normal mode displacement.

arc/parser_test.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,23 @@ def test_parse_normal_mode_displacement(self):
244244
[-0.16184923713199378, -0.3376354950974596, 0.787886990928027]], np.float64)
245245
np.testing.assert_almost_equal(normal_modes_disp[0], expected_normal_modes_disp_4_0)
246246

247+
# QChem
248+
path = os.path.join(ARC_PATH, 'arc', 'testing', 'normal_mode', 'HO2', 'qchem-freq.out')
249+
freqs, normal_modes_disp = parser.parse_normal_mode_displacement(path=path, software='qchem', raise_error=False)
250+
print(freqs)
251+
expected_freqs = np.array([1164.75, 1431.41, 3582.24], np.float64)
252+
np.testing.assert_allclose(freqs, expected_freqs, rtol=1e-5, atol=1e-8)
253+
expected_normal_modes_disp_3 = np.array([[[-0.584, 0.091, -0. ],
254+
[ 0.612, -0.107, 0. ],
255+
[-0.448, 0.253, 0. ]],
256+
[[-0.065, -0.039, -0. ],
257+
[ 0.005, 0.057, 0. ],
258+
[ 0.951, -0.294, -0. ]],
259+
[[-0.001, 0.001, 0. ],
260+
[-0.021, -0.06 , -0. ],
261+
[ 0.348, 0.935, 0. ]]])
262+
np.testing.assert_allclose(normal_modes_disp, expected_normal_modes_disp_3, rtol=1e-5, atol=1e-8)
263+
247264
def test_parse_xyz_from_file(self):
248265
"""Test parsing xyz from a file"""
249266
path1 = os.path.join(ARC_PATH, 'arc', 'testing', 'xyz', 'CH3C(O)O.gjf')
@@ -428,6 +445,12 @@ def test_parse_1d_scan_coords(self):
428445
'C', 'C', 'C', 'C', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',
429446
'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H'))
430447

448+
path_5 = os.path.join(ARC_PATH, 'arc', 'testing', 'rotor_scans', 'qchem-pes.out')
449+
traj_5 = parser.parse_1d_scan_coords(path_5)
450+
self.assertEqual(len(traj_5), 25)
451+
self.assertEqual(traj_5[0]['symbols'], ('C', 'C', 'C', 'C', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H'))
452+
self.assertEqual(traj_5[0]['coords'][13], (-2.1002861161, 0.7502495424, -0.8796160845))
453+
431454
def test_parse_t1(self):
432455
"""Test T1 diagnostic parsing"""
433456
path = os.path.join(ARC_PATH, 'arc', 'testing', 'sp', 'mehylamine_CCSD(T).out')

arc/settings/submit_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class TestSubmit(unittest.TestCase):
1818
def test_servers(self):
1919
"""Test server keys in submit_scripts"""
2020
for server in submit_scripts.keys():
21-
self.assertTrue(server in ['local', 'atlas', 'txe1', 'pbs_sample', 'server1', 'server2', 'azure'])
21+
self.assertIn(server, ['local', 'atlas', 'txe1', 'pbs_sample', 'server1', 'server2', 'azure'])
2222

2323

2424
if __name__ == '__main__':

0 commit comments

Comments
 (0)