|
6 | 6 | """ |
7 | 7 |
|
8 | 8 | import unittest |
9 | | -from unittest.mock import patch |
| 9 | +from unittest.mock import MagicMock, patch |
10 | 10 | import os |
11 | 11 | import shutil |
12 | 12 |
|
@@ -1033,6 +1033,156 @@ def test_run_sp_monoatomic_dlpno(self, mock_run_job): |
1033 | 1033 | self.assertEqual(o_level.method, 'dlpno-ccsd(t)-f12') |
1034 | 1034 | self.assertEqual(o_level.cabs, 'cc-pvtz-f12-cabs') |
1035 | 1035 |
|
| 1036 | + def test_check_directed_scan_job_skips_isomorphism_for_ts(self): |
| 1037 | + """check_directed_scan_job must not call check_xyz_isomorphism for a TS; is_isomorphic is recorded as True.""" |
| 1038 | + ts_xyz = str_to_xyz("""N 0.91779059 0.51946178 0.00000000 |
| 1039 | + H 1.81402049 1.03819414 0.00000000 |
| 1040 | + H 0.00000000 0.00000000 0.00000000 |
| 1041 | + H 0.91779059 1.22790192 0.72426890""") |
| 1042 | + ts_spc = ARCSpecies(label='TS_dirscan', is_ts=True, xyz=ts_xyz, multiplicity=1, charge=0, |
| 1043 | + compute_thermo=False) |
| 1044 | + ts_spc.rotors_dict = {0: {'pivots': [1, 2], 'directed_scan': {}}} |
| 1045 | + |
| 1046 | + project_directory = os.path.join(ARC_PATH, 'Projects', 'arc_project_ts_iso_dirscan') |
| 1047 | + self.addCleanup(shutil.rmtree, project_directory, ignore_errors=True) |
| 1048 | + sched = Scheduler(project='test_ts_iso_dirscan', ess_settings=self.ess_settings, |
| 1049 | + species_list=[ts_spc], |
| 1050 | + opt_level=Level(repr=default_levels_of_theory['opt']), |
| 1051 | + freq_level=Level(repr=default_levels_of_theory['freq']), |
| 1052 | + sp_level=Level(repr=default_levels_of_theory['sp']), |
| 1053 | + ts_guess_level=Level(repr=default_levels_of_theory['ts_guesses']), |
| 1054 | + project_directory=project_directory, |
| 1055 | + testing=True, |
| 1056 | + job_types=self.job_types1, |
| 1057 | + ) |
| 1058 | + |
| 1059 | + job_mock = MagicMock() |
| 1060 | + job_mock.job_status = [None, {'status': 'done'}] |
| 1061 | + job_mock.local_path_to_output_file = '/fake/path.log' |
| 1062 | + job_mock.pivots = [1, 2] |
| 1063 | + job_mock.dihedrals = [45.0] |
| 1064 | + job_mock.ess_trsh_methods = [] |
| 1065 | + |
| 1066 | + with patch('arc.species.species.ARCSpecies.check_xyz_isomorphism') as mock_iso, \ |
| 1067 | + patch('arc.scheduler.parser.parse_geometry', return_value=ts_xyz), \ |
| 1068 | + patch('arc.scheduler.parser.parse_e_elect', return_value=-123.45): |
| 1069 | + sched.check_directed_scan_job(label='TS_dirscan', job=job_mock) |
| 1070 | + |
| 1071 | + mock_iso.assert_not_called() |
| 1072 | + recorded = sched.species_dict['TS_dirscan'].rotors_dict[0]['directed_scan'][('45.00',)] |
| 1073 | + self.assertTrue(recorded['is_isomorphic']) |
| 1074 | + |
| 1075 | + @patch('arc.scheduler.Scheduler.run_opt_job') |
| 1076 | + def test_troubleshoot_scan_job_skips_isomorphism_for_ts(self, mock_run_opt): |
| 1077 | + """troubleshoot_scan_job must not call check_xyz_isomorphism for a TS when applying 'change conformer'.""" |
| 1078 | + ts_xyz = str_to_xyz("""N 0.91779059 0.51946178 0.00000000 |
| 1079 | + H 1.81402049 1.03819414 0.00000000 |
| 1080 | + H 0.00000000 0.00000000 0.00000000 |
| 1081 | + H 0.91779059 1.22790192 0.72426890""") |
| 1082 | + new_xyz = str_to_xyz("""N 0.91000000 0.52000000 0.00000000 |
| 1083 | + H 1.81000000 1.04000000 0.00000000 |
| 1084 | + H 0.00000000 0.00000000 0.00000000 |
| 1085 | + H 0.91000000 1.23000000 0.72000000""") |
| 1086 | + ts_spc = ARCSpecies(label='TS_trsh', is_ts=True, xyz=ts_xyz, multiplicity=1, charge=0, |
| 1087 | + compute_thermo=False) |
| 1088 | + ts_spc.rotors_dict = {0: {'pivots': [1, 2], 'scan': [3, 1, 2, 4], 'scan_path': '', |
| 1089 | + 'invalidation_reason': '', 'success': None, 'symmetry': None, |
| 1090 | + 'times_dihedral_set': 0, 'trsh_methods': [], 'trsh_counter': 0}} |
| 1091 | + |
| 1092 | + project_directory = os.path.join(ARC_PATH, 'Projects', 'arc_project_ts_iso_trsh') |
| 1093 | + self.addCleanup(shutil.rmtree, project_directory, ignore_errors=True) |
| 1094 | + sched = Scheduler(project='test_ts_iso_trsh', ess_settings=self.ess_settings, |
| 1095 | + species_list=[ts_spc], |
| 1096 | + opt_level=Level(repr=default_levels_of_theory['opt']), |
| 1097 | + freq_level=Level(repr=default_levels_of_theory['freq']), |
| 1098 | + sp_level=Level(repr=default_levels_of_theory['sp']), |
| 1099 | + ts_guess_level=Level(repr=default_levels_of_theory['ts_guesses']), |
| 1100 | + project_directory=project_directory, |
| 1101 | + testing=True, |
| 1102 | + job_types=self.job_types1, |
| 1103 | + ) |
| 1104 | + sched.trsh_ess_jobs = True |
| 1105 | + sched.trsh_rotors = True |
| 1106 | + |
| 1107 | + job_mock = MagicMock() |
| 1108 | + job_mock.species_label = 'TS_trsh' |
| 1109 | + job_mock.rotor_index = 0 |
| 1110 | + job_mock.torsions = [[3, 1, 2, 4]] |
| 1111 | + job_mock.job_name = 'scan_a200' |
| 1112 | + |
| 1113 | + with patch('arc.species.species.ARCSpecies.check_xyz_isomorphism') as mock_iso, \ |
| 1114 | + patch('arc.scheduler.Scheduler.delete_all_species_jobs'): |
| 1115 | + sched.troubleshoot_scan_job(job=job_mock, methods={'change conformer': new_xyz}) |
| 1116 | + |
| 1117 | + mock_iso.assert_not_called() |
| 1118 | + self.assertEqual(sched.species_dict['TS_trsh'].final_xyz, new_xyz) |
| 1119 | + mock_run_opt.assert_called_once() |
| 1120 | + |
| 1121 | + @patch('arc.scheduler.Scheduler.run_job') |
| 1122 | + def test_troubleshoot_scan_job_skips_isomorphism_for_ts_non_conformer(self, mock_run_job): |
| 1123 | + """troubleshoot_scan_job must not call check_xyz_isomorphism for a TS in the non-'change conformer' branch.""" |
| 1124 | + ts_xyz = str_to_xyz("""N 0.91779059 0.51946178 0.00000000 |
| 1125 | + H 1.81402049 1.03819414 0.00000000 |
| 1126 | + H 0.00000000 0.00000000 0.00000000 |
| 1127 | + H 0.91779059 1.22790192 0.72426890""") |
| 1128 | + ts_spc = ARCSpecies(label='TS_trsh_nc', is_ts=True, xyz=ts_xyz, multiplicity=1, charge=0, |
| 1129 | + compute_thermo=False) |
| 1130 | + ts_spc.rotors_dict = {0: {'pivots': [1, 2], 'scan': [3, 1, 2, 4], 'scan_path': '', |
| 1131 | + 'invalidation_reason': '', 'success': None, 'symmetry': None, |
| 1132 | + 'times_dihedral_set': 0, 'trsh_methods': [], 'trsh_counter': 0}} |
| 1133 | + |
| 1134 | + project_directory = os.path.join(ARC_PATH, 'Projects', 'arc_project_ts_iso_trsh_nc') |
| 1135 | + self.addCleanup(shutil.rmtree, project_directory, ignore_errors=True) |
| 1136 | + sched = Scheduler(project='test_ts_iso_trsh_nc', ess_settings=self.ess_settings, |
| 1137 | + species_list=[ts_spc], |
| 1138 | + opt_level=Level(repr=default_levels_of_theory['opt']), |
| 1139 | + freq_level=Level(repr=default_levels_of_theory['freq']), |
| 1140 | + sp_level=Level(repr=default_levels_of_theory['sp']), |
| 1141 | + ts_guess_level=Level(repr=default_levels_of_theory['ts_guesses']), |
| 1142 | + project_directory=project_directory, |
| 1143 | + testing=True, |
| 1144 | + job_types=self.job_types1, |
| 1145 | + ) |
| 1146 | + sched.trsh_ess_jobs = True |
| 1147 | + sched.trsh_rotors = True |
| 1148 | + |
| 1149 | + job_mock = MagicMock() |
| 1150 | + job_mock.species_label = 'TS_trsh_nc' |
| 1151 | + job_mock.rotor_index = 0 |
| 1152 | + job_mock.torsions = [[2, 0, 1, 3]] # 0-indexed; torsions_to_scans yields the 1-indexed scan [3, 1, 2, 4] |
| 1153 | + job_mock.job_name = 'scan_a201' |
| 1154 | + job_mock.scan_res = 8.0 |
| 1155 | + job_mock.xyz = ts_xyz |
| 1156 | + job_mock.level = Level(repr=default_levels_of_theory['scan']) |
| 1157 | + job_mock.local_path_to_output_file = '/fake/path.log' |
| 1158 | + |
| 1159 | + with patch('arc.species.species.ARCSpecies.check_xyz_isomorphism') as mock_iso, \ |
| 1160 | + patch('arc.scheduler.trsh_scan_job') as mock_trsh_scan: |
| 1161 | + mock_trsh_scan.return_value = [], 4.0 |
| 1162 | + |
| 1163 | + sched.troubleshoot_scan_job(job=job_mock, methods={'inc_res': None}) |
| 1164 | + |
| 1165 | + mock_iso.assert_not_called() |
| 1166 | + mock_trsh_scan.assert_called_once_with( |
| 1167 | + label='TS_trsh_nc', |
| 1168 | + scan_res=8.0, |
| 1169 | + scan=[3, 1, 2, 4], |
| 1170 | + scan_list=[[3, 1, 2, 4]], |
| 1171 | + methods={'inc_res': None}, |
| 1172 | + log_file='/fake/path.log', |
| 1173 | + ) |
| 1174 | + |
| 1175 | + mock_run_job.assert_called_once() |
| 1176 | + _, kwargs = mock_run_job.call_args |
| 1177 | + self.assertEqual(kwargs['label'], 'TS_trsh_nc') |
| 1178 | + self.assertEqual(kwargs['xyz'], ts_xyz) |
| 1179 | + self.assertEqual(kwargs['level_of_theory'], job_mock.level) |
| 1180 | + self.assertEqual(kwargs['job_type'], 'scan') |
| 1181 | + self.assertEqual(kwargs['torsions'], [[2, 0, 1, 3]]) |
| 1182 | + self.assertEqual(kwargs['scan_trsh'], []) |
| 1183 | + self.assertEqual(kwargs['trsh'], {'scan_res': 4.0}) |
| 1184 | + self.assertEqual(kwargs['rotor_index'], 0) |
| 1185 | + |
1036 | 1186 | @classmethod |
1037 | 1187 | def tearDownClass(cls): |
1038 | 1188 | """ |
|
0 commit comments