Skip to content
Merged
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
4 changes: 3 additions & 1 deletion arc/job/adapters/xtb_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,9 @@ def opt_ts(self):
'futurewarning:',
'userwarning:',
'deprecationwarning:',
'warnings.warn('
'warnings.warn(',
'pjrt_executable.cc',
'cpu_aot_loader.cc',
]
real_errors = []
for line in stderr:
Expand Down
29 changes: 28 additions & 1 deletion arc/level.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Level(object):
args (Dict[Dict[str, str]], optional): Additional arguments provided to the software.
Different than the ``args`` in ``LevelOfTheory``.
compatible_ess (list, optional): Entries are names of compatible ESS. Not in ``LevelOfTheory``.
year (int, optional): Optional 4-digit year suffix for differentiating methods such as b97d3/b97d32023.
"""

def __init__(self,
Expand All @@ -56,6 +57,7 @@ def __init__(self,
solvent: Optional[str] = None,
solvation_scheme_level: Optional['Level'] = None,
args: Optional[Union[Dict[str, str], Iterable, str]] = None,
year: Optional[int] = None,
):
self.repr = repr
self.method = method
Expand Down Expand Up @@ -88,6 +90,12 @@ def __init__(self,
'Both solvation method and solvent must be defined together, or both must be None. '
f'Got solvation method = "{self.solvation_method}", solvent = "{self.solvent}".'
)
if year is not None:
self.year = int(year)
if self.year < 1000 or self.year > 9999:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why limit ARC just up to 9999 AD? :)

raise ValueError(f'year must be a 4-digit integer (1000-9999), got {self.year}.')
else:
self.year = None
self.args = args or {'keyword': dict(), 'block': dict()}

if self.repr is not None:
Expand Down Expand Up @@ -122,6 +130,8 @@ def __str__(self) -> str:
str_ = self.method
if self.basis is not None:
str_ += f'/{self.basis}'
if self.year is not None:
str_ += f', year: {self.year}'
if self.auxiliary_basis is not None:
str_ += f', auxiliary_basis: {self.auxiliary_basis}'
if self.dispersion is not None:
Expand Down Expand Up @@ -165,6 +175,8 @@ def simple(self) -> str:
str_ = self.method
if self.basis is not None:
str_ += f'/{self.basis}'
if self.year is not None:
str_ += f' ({self.year})'
return str_

def as_dict(self) -> dict:
Expand Down Expand Up @@ -196,7 +208,8 @@ def build(self):
'solvation_method': None,
'solvent': None,
'solvation_scheme_level': None,
'args': None}
'args': None,
'year': None}
allowed_keys = list(level_dict.keys())

if isinstance(self.repr, str):
Expand Down Expand Up @@ -304,6 +317,20 @@ def lower(self):

self.args = args

def warn_if_year_set(self, attr_name: str):
"""
Warn if ``year`` is set on this Level. The ``year`` attribute only affects Arkane
database matching and is ignored on non-``arkane_level_of_theory`` levels.

Args:
attr_name (str): The name of the attribute this Level is assigned to (for the warning message).
"""
if self.year is not None:
logger.warning(
f'The "year" attribute on {attr_name} ({self.simple()}) has no effect. '
f'Year is only used for Arkane database matching via arkane_level_of_theory.'
)

def deduce_method_type(self):
"""
Determine the model chemistry type:
Expand Down
21 changes: 21 additions & 0 deletions arc/level_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,27 @@ def test_build(self):
"dlpno-ccsd(t)/def2-tzvp, auxiliary_basis: def2-tzvp/c, solvation_method: smd, "
"solvent: water, solvation_scheme_level: 'apfd/def2-tzvp, software: gaussian', software: orca")

def test_year_validation(self):
"""Test year validation for Level"""
with self.assertRaises(ValueError):
Level(method='b97d3', basis='def2tzvp', year=23)
level = Level(method='b97d3', basis='def2tzvp', year=2023)
self.assertEqual(level.year, 2023)

def test_warn_if_year_set(self):
"""Test that warn_if_year_set logs a warning when year is set."""
level_with_year = Level(method='b97d3', basis='def2tzvp', year=2023)
with self.assertLogs('arc', level='WARNING') as cm:
level_with_year.warn_if_year_set('sp_level')
self.assertEqual(len(cm.output), 1)
self.assertIn('sp_level', cm.output[0])
self.assertIn('has no effect', cm.output[0])

level_no_year = Level(method='b97d3', basis='def2tzvp')
with self.assertRaises(AssertionError):
with self.assertLogs('arc', level='WARNING'):
level_no_year.warn_if_year_set('opt_level')

def test_ess_methods_yml(self):
"""Test reading the ess_methods.yml file"""
ess_methods = read_yaml_file(path=os.path.join(ARC_PATH, 'data', 'ess_methods.yml'))
Expand Down
12 changes: 12 additions & 0 deletions arc/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ def __init__(self,
self.job_types['opt'] = True # Run the optimizations, self.fine_only will make sure that they are fine.

self.set_levels_of_theory() # All level of theories should be Level types after this call.
self._warn_year_on_non_arkane_levels()
if self.thermo_adapter == 'arkane':
self.check_arkane_level_of_theory()

Expand Down Expand Up @@ -1155,6 +1156,17 @@ def process_level_of_theory(self):

self.level_of_theory = '' # Reset the level_of_theory argument to avoid conflicts upon restarting ARC.

def _warn_year_on_non_arkane_levels(self):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you implement this in level.py, and call it here? just so we store level-related-things at one place

"""
Warn if ``year`` was specified on any Level other than ``arkane_level_of_theory``.
The ``year`` attribute only affects Arkane database matching and is ignored elsewhere.
"""
for attr_name in ('sp_level', 'opt_level', 'freq_level', 'scan_level', 'irc_level',
'conformer_opt_level', 'conformer_sp_level', 'orbitals_level'):
level = getattr(self, attr_name, None)
if isinstance(level, Level):
level.warn_if_year_set(attr_name)

def check_arkane_level_of_theory(self):
"""
Check that the level of theory has AEC in Arkane.
Expand Down
18 changes: 18 additions & 0 deletions arc/main_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,24 @@ def test_determine_model_chemistry_for_job_types(self):
self.assertEqual(arc14.freq_level.simple(), 'pm6')
self.assertEqual(arc14.sp_level.simple(), 'amber')

# Test explicit year in arkane_level_of_theory dictionary
arc15 = ARC(project='test',
sp_level='wb97xd/def2tzvp',
opt_level='wb97xd/def2tzvp',
arkane_level_of_theory={'method': 'wb97xd', 'basis': 'def2tzvp', 'year': 2023},
bac_type=None,
calc_freq_factor=False, compute_thermo=False)
self.assertEqual(arc15.arkane_level_of_theory.year, 2023)

# Test warning when year is specified on sp_level instead of arkane_level_of_theory
arc16 = ARC(project='test',
sp_level={'method': 'wb97xd', 'basis': 'def2tzvp', 'year': 2023},
opt_level='wb97xd/def2tzvp',
calc_freq_factor=False, compute_thermo=False)
with open(os.path.join(arc16.project_directory, 'arc.log'), 'r') as f:
log_content = f.read()
self.assertIn('"year" attribute on sp_level', log_content)

def test_determine_unique_species_labels(self):
"""Test the determine_unique_species_labels method"""
spc0 = ARCSpecies(label='spc0', smiles='CC', compute_thermo=False)
Expand Down
Loading
Loading