Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion arc/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class ConverterError(Exception):

class DependencyError(Exception):
"""
An exception raised when converting molecular representations.
An exception raised when a required external dependency is not installed or configured.
"""
pass

Expand Down
3 changes: 2 additions & 1 deletion arc/job/adapters/ts/autotst_ts.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from typing import TYPE_CHECKING

from arc.common import almost_equal_coords, ARC_PATH, get_logger, read_yaml_file
from arc.exceptions import DependencyError
from arc.imports import settings
from arc.job.adapter import JobAdapter
from arc.job.adapters.common import _initialize_adapter
Expand Down Expand Up @@ -215,7 +216,7 @@ def execute_incore(self):
participates in its well, so a well with a repeated species stays atom-balanced.
"""
if not AUTOTST_PYTHON or not os.path.isfile(AUTOTST_PYTHON):
raise FileNotFoundError('AutoTST python executable was not found. '
raise DependencyError('AutoTST python executable was not found. '
'Make sure the tst_env exists and AUTOTST_PYTHON is configured. '
f'See {self.url} for more information, or use the Makefile provided with ARC.')
self._log_job_execution()
Expand Down
3 changes: 2 additions & 1 deletion arc/job/adapters/ts/kinbot_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from unittest import mock

from arc.common import ARC_TESTING_PATH, get_logger, read_yaml_file, save_yaml_file
from arc.exceptions import DependencyError
import arc.job.adapters.ts.kinbot_ts as kinbot_ts
from arc.reaction import ARCReaction
from arc.species import ARCSpecies
Expand Down Expand Up @@ -84,7 +85,7 @@ def test_missing_kinbot_python(self):
"""Test that execute_incore() raises if the kinbot_env python executable is missing."""
adapter = self.get_adapter(dir_name='tst_missing_python')
with mock.patch.object(kinbot_ts, 'KINBOT_PYTHON', None):
with self.assertRaises(FileNotFoundError):
with self.assertRaises(DependencyError):
adapter.execute_incore()

def test_intra_h_migration(self):
Expand Down
3 changes: 2 additions & 1 deletion arc/job/adapters/ts/kinbot_ts.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from typing import TYPE_CHECKING

from arc.common import ARC_PATH, almost_equal_coords, get_logger, read_yaml_file, save_yaml_file
from arc.exceptions import DependencyError
from arc.imports import settings
from arc.job.adapter import JobAdapter
from arc.job.adapters.common import _initialize_adapter
Expand Down Expand Up @@ -250,7 +251,7 @@ def execute_incore(self):
Execute a job incore.
"""
if not KINBOT_PYTHON or not os.path.isfile(KINBOT_PYTHON):
raise FileNotFoundError('The KinBot python executable was not found. '
raise DependencyError('The KinBot python executable was not found. '
'Make sure the kinbot_env exists and KINBOT_PYTHON is configured '
'(run devtools/install_kinbot.sh to create the environment). '
f'See {self.url} for more information.')
Expand Down
53 changes: 44 additions & 9 deletions arc/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
sort_two_lists_by_the_first,
torsions_to_scans,
)
from arc.exceptions import (InputError,
from arc.exceptions import (DependencyError,
InputError,
SchedulerError,
SpeciesError,
TrshError,
Expand Down Expand Up @@ -1855,10 +1856,9 @@ def spawn_ts_jobs(self):
logger.info(f'Not spawning TS search jobs for reaction {rxn} for which the multiplicity is unknown.')
else:
rxn.ts_species.tsg_spawned = True
tsg_index = 0
tsg_index, eligible_methods = 0, list()
family_known = rxn.family is not None and rxn.family in ts_adapters_by_rmg_family
for method in self.ts_adapters:
family_known = (rxn.family is not None
and rxn.family in ts_adapters_by_rmg_family)
admit_unknown_family = (not family_known
and method in ts_adapters_for_unknown_unimolecular
and rxn.is_unimolecular())
Expand All @@ -1870,12 +1870,47 @@ def spawn_ts_jobs(self):
logger.info(f'Admitting TS adapter {method!r} for reaction {rxn.label} '
f'via ts_adapters_for_unknown_unimolecular '
f'(RMG family is {rxn.family!r}).')
self.run_job(job_type='tsg',
job_adapter=method,
reactions=[rxn],
tsg=tsg_index,
)
eligible_methods.append(method)
try:
self.run_job(job_type='tsg',
job_adapter=method,
reactions=[rxn],
tsg=tsg_index,
)
except DependencyError as e:
# An optional adapter's backend (e.g. KinBot, AutoTST) is not installed;
# record it and carry on so one missing dependency can't abort the run.
logger.error(f'The {method!r} TS search adapter is not available and '
f'was skipped for reaction {rxn.label}: {e}')
if method not in rxn.ts_species.unsuccessful_methods:
rxn.ts_species.unsuccessful_methods.append(method)
# Roll back the job run_job() registered before it raised, so a
# never-run 'tsg<i>' entry isn't serialized and parsed as completed.
if f'tsg{tsg_index}' in self.running_jobs.get(rxn.ts_label, list()):
self.running_jobs[rxn.ts_label].remove(f'tsg{tsg_index}')
self.job_dict.get(rxn.ts_label, dict()).get('tsg', dict()).pop(tsg_index, None)
continue
tsg_index += 1
if not tsg_index and not rxn.ts_species.ts_guesses:
# No adapter ran and no user guess was given, and tsg_spawned is already
# latched True, so warn explicitly rather than fail silently much later.
eligible = ts_adapters_by_rmg_family.get(rxn.family) if family_known else None
if eligible_methods:
reason = (f'all of its eligible adapters {eligible_methods} are unavailable '
f'on this machine (see the errors above). Install one of them, or add '
f'an eligible adapter that is installed')
else:
reason = (f'none of the configured ts_adapters {self.ts_adapters} is eligible for it. '
+ (f'Its RMG family {rxn.family!r} admits {eligible}; the two lists do '
f'not intersect.' if eligible is not None else
f'Its RMG family {rxn.family!r} is not in ts_adapters_by_rmg_family, '
f'and it did not qualify for {ts_adapters_for_unknown_unimolecular} '
f'(is_unimolecular={rxn.is_unimolecular()}).')
+ ' Add an eligible adapter to ts_adapters')
logger.warning(f'Not spawning any TS search job for reaction {rxn.label}: {reason} '
f'(in the input file or in ~/.arc/settings.py) to compute this TS. '
f'No TS guess will be generated and this reaction will be reported '
f'as not converged.')
if all('user guess' in tsg.method for tsg in rxn.ts_species.ts_guesses):
rxn.ts_species.tsg_spawned = True
self.run_conformer_jobs(labels=[rxn.ts_label])
Expand Down
Loading
Loading