Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
98b46bf
Relocate Level into its own arc/level/ package
alongd Apr 22, 2026
793ac31
Add sp_composite data model under arc/level/
alongd Apr 22, 2026
c72356b
Wire sp_composite end-to-end: main, Scheduler, species, Arkane
alongd Apr 22, 2026
8236283
Docs: sp_composite user guide, examples, and parseability tests
alongd Apr 22, 2026
d2ff605
MRCC route in Molpro
alongd Apr 27, 2026
a99626a
Drop misleading "ARC ignores user-specified options" warning
alongd Apr 28, 2026
493c491
Improve species classification for thermo in processor
alongd May 7, 2026
99ba82a
Treat atoms (e.g., H) correctly in the arkane adapter
alongd May 7, 2026
1e49e5f
Add zombie-job detection (1hr no-output -> kill + resubmit once)
alongd May 7, 2026
177c77a
Added MRCC support to trsh
alongd May 9, 2026
2960347
Improve imports in mapping engine
alongd May 9, 2026
84cba77
Changed molecule error and exception logging into debug
alongd May 9, 2026
df07063
filter_real_stderr_lines in arkane adapter
alongd May 9, 2026
0045d6f
Changed max barrier for rotor threshold from 40 to 60 kJ/mol
alongd May 9, 2026
80cc0d8
scheduler: mutate species_list in place when deleting IRC species
alongd May 16, 2026
7473a1c
level: postpone annotation eval so Level can self-reference
alongd May 16, 2026
180dafd
ssh: postpone annotation eval so SSHClient can self-reference
alongd May 16, 2026
d9e4d6b
postpone annotation eval in classes that self-reference
alongd May 16, 2026
85b00cd
postpone annotation eval where TYPE_CHECKING imports are used at runtime
alongd May 16, 2026
e0525e7
parser: postpone annotation eval to tolerate missing ARCSpecies import
alongd May 16, 2026
f2a7ade
revert blanket from __future__ import annotations
alongd May 16, 2026
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
1 change: 1 addition & 0 deletions arc/checks/nmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
A module for checking the normal mode displacement of a TS.
"""


import numpy as np
from collections import Counter
from itertools import product
Expand Down
1 change: 1 addition & 0 deletions arc/checks/ts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
A module for checking the quality of TS-related calculations, contains helper functions for Scheduler.
"""


from itertools import product
import os

Expand Down
1 change: 1 addition & 0 deletions arc/family/family.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
A module for working with RMG reaction families.
"""


from typing import TYPE_CHECKING
import ast
import os
Expand Down
1 change: 1 addition & 0 deletions arc/job/adapters/cfour.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
http://slater.chemie.uni-mainz.de/cfour/index.php?n=Main.ListOfKeywordsInAlphabeticalOrder
"""


import datetime
import math
import os
Expand Down
23 changes: 14 additions & 9 deletions arc/job/adapters/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
As such, it should not import any other ARC modules to avoid circular imports.
"""


import datetime
import os
import shutil
import sys
import re

from pprint import pformat
from typing import TYPE_CHECKING

from arc.common import get_logger
Expand Down Expand Up @@ -470,21 +470,26 @@ def set_job_args(args: dict | None,
"""
Set the job args considering args from ``level`` and from ``trsh``.

The caller (e.g. :meth:`arc.scheduler.Scheduler.run_job`) is expected to
have already merged any ``level.args`` content into ``args`` before calling
this function — ``run_job`` does so via ``args.update(level.args)``. When
the caller passes empty ``args`` and the level supplies ``args``, we fall
back to ``level.args`` for convenience.

Args:
args (dict): The job specific arguments.
args (dict): The job-specific arguments.
level (Level): The level of theory.
job_name (str): The job name.

Returns:
dict: The initialized job specific arguments.
dict: The initialized job-specific arguments, guaranteed to carry the
``'keyword'``, ``'block'``, and ``'trsh'`` buckets (each a dict).
"""
# Ignore user-specified additional job arguments when troubleshooting.
if args is not None and args and any(val for val in args.values()) \
and level is not None and level.args and any(val for val in level.args.values()):
logger.warning(f'When troubleshooting {job_name}, ARC ignores the following user-specified options:\n'
f'{pformat(level.args)}')
elif not args and level is not None:
# Convenience fallback: empty (or None) caller-args inherits level.args.
if not args and level is not None and level.args is not None:
args = level.args
if args is None:
args = dict()
for key in ['keyword', 'block', 'trsh']:
if key not in args.keys():
args[key] = dict()
Expand Down
24 changes: 24 additions & 0 deletions arc/job/adapters/common_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
This module contains unit tests of the arc.job.adapters.common module
"""

import logging
import os
import shutil
import unittest
Expand Down Expand Up @@ -166,6 +167,29 @@ def test_set_job_args(self):
args = common.set_job_args(args={'keyword': 'k1'}, level=Level(repr='CBS-QB3'), job_name='j1')
self.assertEqual(args, {'keyword':'k1', 'block': dict(), 'trsh': dict()})

def test_set_job_args_no_spurious_warning_when_level_has_args(self):
"""Regression: the previous "ARC ignores user-specified options" warning
fired on every first-run job whose level carried args, because
``run_job`` had already merged ``level.args`` into ``args`` before
calling — nothing was actually being ignored. The warning should now
be silent on a normal first-run path."""
merged_args = {'keyword': {'core': 'core,0,0,0,0,0,0,0,0;'}, 'block': {}}
level_with_args = Level(method='ccsd(t)', basis='cc-pCVTZ',
args=merged_args)
with self.assertNoLogs(logger='arc', level=logging.WARNING):
result = common.set_job_args(args=merged_args,
level=level_with_args, job_name='j_first_run')
# Args content is preserved (not dropped).
self.assertEqual(result['keyword'], {'core': 'core,0,0,0,0,0,0,0,0;'})
self.assertEqual(result['trsh'], {}) # bucket added by guarantee

def test_set_job_args_args_none_preserves_level_args(self):
"""When the caller passes None, fall back to level.args (legacy convenience)."""
level = Level(method='ccsd(t)', basis='cc-pVTZ',
args={'keyword': {'general': 'foo'}, 'block': {}})
result = common.set_job_args(args=None, level=level, job_name='j1')
self.assertEqual(result['keyword'], {'general': 'foo'})

def test_which(self):
"""Test the which() function"""
ans = common.which(command='python', return_bool=True, raise_error=False)
Expand Down
1 change: 1 addition & 0 deletions arc/job/adapters/gaussian.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
https://gaussian.com/
"""


import datetime
import math
import os
Expand Down
1 change: 1 addition & 0 deletions arc/job/adapters/mockter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
An adapter for dummy jobs, meant for testing and debugging only.
"""


import datetime
import os
from typing import TYPE_CHECKING
Expand Down
44 changes: 43 additions & 1 deletion arc/job/adapters/molpro.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
https://www.molpro.net/
"""


import datetime
import math
import os
Expand Down Expand Up @@ -35,6 +36,20 @@
settings['default_job_settings'], settings['global_ess_settings'], settings['input_filenames'], \
settings['output_filenames'], settings['servers'], settings['submit_filenames']

# Methods that native Molpro does not support but its MRCC plugin does.
# When the level's method matches one of these (case-insensitive), the adapter
# emits a ``{mrcc,method=...}`` plugin call instead of a bare directive that
# Molpro's input parser would reject with "Unknown command or directive".
# Compared against the lowercased ``Level.method``.
MRCC_ROUTED_METHODS = frozenset({
'ccsdt',
'ccsdt(q)',
'ccsdtq',
'ccsdtq(p)',
'ccsdtqp',
})


input_template = """***,${label}
memory,Total=${memory},m;

Expand All @@ -47,7 +62,7 @@
${cabs}
int;

{hf;${shift}
{${hf_method};${shift}
maxit,999;
wf,spin=${spin},charge=${charge};
}
Expand Down Expand Up @@ -229,10 +244,37 @@ def write_input_file(self) -> None:
input_dict['spin'] = self.multiplicity - 1
input_dict['xyz'] = xyz_to_str(self.xyz)
input_dict['orbitals'] = '\ngprint,orbitals;\n'
input_dict['hf_method'] = 'hf' # default; overridden below for open-shell MRCC

if not is_restricted(self):
input_dict['restricted'] = 'u'

if self.level.method in MRCC_ROUTED_METHODS:
# Restriction is implicit from the preceding {hf;...} block; the
# MRCC plugin call does not accept a 'u'/'r' prefix.
input_dict['method'] = '{mrcc,method=' + self.level.method.upper() + '}'
input_dict['restricted'] = ''
if not is_restricted(self):
# Open-shell wavefunction + MRCC's approximate-CC family
# (CCSDT(Q), CCSDTQ(P), and the perturbative-(T) variants)
# refuses standard ROHF orbitals:
# "Approximate CC methods are not implemented for standard
# ROHF orbitals! Use semicanonical orbitals!"
# Solution: use UHF instead of (RO)HF as the SCF reference.
# UHF orbitals are semicanonical by construction (alpha and
# beta Fock matrices are separately diagonal) and live at the
# default record 2100.2, which MRCC reads. MRCC then reports
# ``Type=UHF/CANONICAL`` and accepts.
#
# An earlier attempt at this fix prepended ``{uccsd}`` to the
# MRCC call. {uccsd} does run UCCSD on top of ROHF, but the
# post-UCCSD canonical orbitals go to a separate record while
# the default 2100.2 still holds the original ROHF orbitals —
# MRCC reads 2100.2 by default and complained. Switching the
# SCF reference to UHF avoids this orbital-record bookkeeping
# entirely.
input_dict['hf_method'] = 'uhf'

# Job type specific options
if self.job_type in ['opt', 'optfreq', 'conf_opt']:
keywords = ['optg', 'root=2', 'method=qsd', 'readhess', "savexyz='geometry.xyz'"] if self.is_ts \
Expand Down
119 changes: 119 additions & 0 deletions arc/job/adapters/molpro_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,24 @@ def setUpClass(cls):
'closed': [1, 0, 0, 0, 0, 0, 0, 0]})],
testing=True,
)
cls.job_mrcc_ccsdt = MolproAdapter(execution_type='queue',
job_type='sp',
level=Level(method='CCSDT', basis='cc-pVDZ'),
project='test',
project_directory=os.path.join(ARC_TESTING_PATH,
'test_MolproAdapter_mrcc_ccsdt'),
species=[ARCSpecies(label='spc1', xyz=['O 0 0 1'], multiplicity=3)],
testing=True,
)
cls.job_mrcc_ccsdtq = MolproAdapter(execution_type='queue',
job_type='sp',
level=Level(method='CCSDT(Q)', basis='cc-pVDZ'),
project='test',
project_directory=os.path.join(ARC_TESTING_PATH,
'test_MolproAdapter_mrcc_ccsdtq'),
species=[ARCSpecies(label='spc1', xyz=['O 0 0 1'], multiplicity=1)],
testing=True,
)

def test_set_cpu_and_mem(self):
"""Test assigning number of cpu's and memory"""
Expand Down Expand Up @@ -441,6 +459,107 @@ def test_write_mrci_input_file(self):
"""
self.assertEqual(content_7, job_7_expected_input_file)

def test_write_input_file_mrcc_routing(self):
"""Methods unsupported by native Molpro but supported by MRCC are routed through the MRCC plugin.

For an open-shell wavefunction, the SCF reference is switched from
``{hf;...}`` (which gives Molpro's ROHF for open-shell) to
``{uhf;...}``. MRCC's approximate-CC family (``CCSDT(Q)``,
``CCSDTQ(P)``, and the perturbative-``(T)`` variants) refuses
standard ROHF orbitals with the error::

Approximate CC methods are not implemented for standard ROHF orbitals!
Use semicanonical orbitals!

UHF orbitals are semicanonical by construction (alpha and beta Fock
matrices are separately diagonal), saved to the default record 2100.2
which MRCC reads — MRCC then reports ``Type=UHF/CANONICAL`` and runs
the requested approximate-CC method.
"""
self.job_mrcc_ccsdt.cpu_cores = 48
self.job_mrcc_ccsdt.set_input_file_memory()
self.job_mrcc_ccsdt.write_input_file()
with open(os.path.join(self.job_mrcc_ccsdt.local_path,
input_filenames[self.job_mrcc_ccsdt.job_adapter]), 'r') as f:
content_ccsdt = f.read()
# spc1 has multiplicity=3 (open-shell triplet) — UHF reference expected.
expected_ccsdt = """***,spc1
memory,Total=438,m;

geometry={angstrom;
O 0.00000000 0.00000000 1.00000000}

gprint,orbitals;

basis=cc-pvdz



int;

{uhf;
maxit,999;
wf,spin=2,charge=0;
}

{mrcc,method=CCSDT}



---;

"""
self.assertEqual(content_ccsdt, expected_ccsdt)
# Sanity: the bare directive Molpro rejects must NOT appear on its own line.
self.assertNotIn('\nccsdt;\n', content_ccsdt)
self.assertNotIn('\nuccsdt;\n', content_ccsdt)
# An earlier (insufficient) fix used `{uccsd}` between HF and MRCC —
# this contract has been replaced with UHF, so {uccsd} must NOT appear.
self.assertNotIn('{uccsd}', content_ccsdt)
# UHF must replace HF as the only SCF reference (no {hf;...} block).
self.assertNotIn('{hf;', content_ccsdt)
self.assertIn('{uhf;', content_ccsdt)

self.job_mrcc_ccsdtq.cpu_cores = 48
self.job_mrcc_ccsdtq.set_input_file_memory()
self.job_mrcc_ccsdtq.write_input_file()
with open(os.path.join(self.job_mrcc_ccsdtq.local_path,
input_filenames[self.job_mrcc_ccsdtq.job_adapter]), 'r') as f:
content_ccsdtq = f.read()
expected_ccsdtq = """***,spc1
memory,Total=438,m;

geometry={angstrom;
O 0.00000000 0.00000000 1.00000000}

gprint,orbitals;

basis=cc-pvdz



int;

{hf;
maxit,999;
wf,spin=0,charge=0;
}

{mrcc,method=CCSDT(Q)}



---;

"""
self.assertEqual(content_ccsdtq, expected_ccsdtq)
self.assertNotIn('\nccsdt(q);\n', content_ccsdtq)
# spc1 here has multiplicity=1 (closed-shell) — RHF gives canonical
# orbitals MRCC accepts directly. No UHF/UCCSD pre-step needed.
self.assertNotIn('{uccsd}', content_ccsdtq)
self.assertNotIn('{uhf;', content_ccsdtq)
self.assertIn('{hf;', content_ccsdtq)

def test_set_files(self):
"""Test setting files"""
job_1_files_to_upload = [{'file_name': 'submit.sub',
Expand Down
1 change: 1 addition & 0 deletions arc/job/adapters/obabel.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
http://openbabel.org/docs/current/
"""


import datetime
import os
import subprocess
Expand Down
1 change: 1 addition & 0 deletions arc/job/adapters/orca.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
https://orcaforum.kofo.mpg.de/app.php/portal
"""


import datetime
import math
import os
Expand Down
1 change: 1 addition & 0 deletions arc/job/adapters/psi_4.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
https://www.psicode.org/
"""


import datetime
import math
import os
Expand Down
1 change: 1 addition & 0 deletions arc/job/adapters/qchem.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
https://www.q-chem.com/
"""


import datetime
import os
from typing import TYPE_CHECKING
Expand Down
1 change: 1 addition & 0 deletions arc/job/adapters/terachem.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
http://www.petachem.com/products.html
"""


import datetime
import math
import os
Expand Down
1 change: 1 addition & 0 deletions arc/job/adapters/torch_ani.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
ASE: https://wiki.fysik.dtu.dk/ase/index.html, https://core.ac.uk/download/84004505.pdf
"""


import datetime
import os
from typing import TYPE_CHECKING
Expand Down
Loading