Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
7ecd736
Remove Julia installation from install_rmg
alongd Mar 26, 2026
0779fb2
Organize teardown in functional and common tests
alongd Apr 18, 2026
0d96b66
Added Claude to .gitignore
alongd Apr 18, 2026
c83457d
Added a debug message if fragment mapping fails
alongd Apr 18, 2026
346c24f
Modifications to CI
alongd Apr 18, 2026
e44166b
Add pytest configuration with parallel execution and test paths
alongd Apr 25, 2026
5a50586
Added Reaction.is_unimolecular()
alongd Aug 21, 2023
d46ce8e
Tests: Reaction.is_unimolecular()
alongd May 15, 2024
0555981
Added 'linear' to ts_adapters_by_rmg_family
alongd Aug 21, 2023
55c8c8d
Added check_ordered_zmats()
alongd Aug 21, 2023
3b79874
Added linear to JobEnum
alongd Aug 21, 2023
14c2549
Added the Linear TS search job adapter and related utils
alongd May 15, 2024
60b1837
Tests: Linear TS Job Adapter and utils
alongd May 15, 2024
e106e3a
Added order_xyz_by_atom_map to converter
alongd May 26, 2024
bc0a32b
Added update_zmat_by_xyz() to zmat
alongd May 26, 2024
8360979
Use the round_to arg in common get_angle_in_180_range()
alongd Jan 1, 2026
2ac0032
Add atom_order and anchors arguments to xyz_to_zmat()
alongd Aug 21, 2023
302f24b
Added converter order_mol_by_atom_map()
alongd Mar 31, 2026
c029ca8
Added zmat find_smart_anchors()
alongd Mar 31, 2026
6c21b0c
Updated the ts_adapters_by_rmg_family dict
alongd Mar 31, 2026
975bc8b
Tests: call self.job_3.execute() in OB tests
alongd Apr 18, 2026
69856bd
Preserve radical sites when perceiving mol from xyz with an existing mol
alongd Mar 31, 2026
f0fcc8b
Added an iPY notebook to showcase the linear adapter
alongd Apr 18, 2026
63fb641
Docs: Added descriptions of TS search methods implemented in ARC
alongd Apr 18, 2026
3260ee4
Added split_entries() to family
alongd Apr 18, 2026
15751a4
Minor: Style modifications
alongd Apr 23, 2026
ac49eb4
Tests: Added family tests
alongd Apr 23, 2026
c9258a4
Implement ts_adapters_for_unknown_unimolecular() in Scheduler
alongd Apr 23, 2026
d290687
Added economic_generation to conformers
alongd Apr 24, 2026
971d9c2
Tests: economic conformer generation
alongd Apr 24, 2026
2d058ba
Use economic_generation in scissors
alongd Apr 24, 2026
ac53159
Minor: Added exist_ok to project directory os.makedirs in main
alongd Apr 24, 2026
63c1f82
Tests: Extended perception test
alongd Apr 25, 2026
bfc3a9c
cache ReactionFamily instances and groups.py reads per process
alongd Apr 25, 2026
035d6f3
Type hints leftover fixes
alongd Apr 28, 2026
ad4dab2
f! ci
alongd Apr 28, 2026
14b2ea3
f common tst
alongd Apr 28, 2026
5877052
f common 14
alongd Apr 28, 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
26 changes: 5 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,21 @@ jobs:
path: ARC

- name: Clean Ubuntu Image
uses: jlumbroso/free-disk-space@main
uses: jlumbroso/free-disk-space@v1.3.1
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
swap-storage: true
swap-storage: false

- name: Set up micromamba (arc_env)
uses: mamba-org/setup-micromamba@v3
with:
environment-name: arc_env
environment-file: ARC/environment.yml
cache-environment: true
cache-environment: ${{ github.event_name != 'schedule' }}
cache-downloads: true
generate-run-shell: true

Expand All @@ -55,22 +55,6 @@ jobs:
echo "PATH=$PATH:$PWD" >> "$GITHUB_ENV"
echo "PYTHONPATH=$PYTHONPATH:$PWD" >> "$GITHUB_ENV"

- name: Install Julia 1.10
shell: micromamba-shell {0}
run: |
echo "::group::Install juliaup + Julia 1.10"
# 1) Bootstrap juliaup non-interactively (-y)
curl -fsSL https://install.julialang.org | sh -s -- -y
# 2) Make juliaup visible *now* (CI shells won’t re-source rc files)
export PATH="$HOME/.juliaup/bin:$PATH"
# 3) Install & select Julia 1.10
juliaup add 1.10
juliaup default 1.10
# 4) Persist for subsequent steps
echo "PATH=$HOME/.juliaup/bin:$PATH" >> "$GITHUB_ENV"
echo "::endgroup::"
julia --version # Check that Julia is installed correctly

- name: Install all extras - CI
shell: micromamba-shell {0}
working-directory: ARC
Expand Down Expand Up @@ -103,7 +87,7 @@ jobs:
run: |
echo "Running Unit Tests..."
export PYTHONPATH="${{ github.workspace }}/AutoTST:${{ github.workspace }}/KinBot:$PYTHONPATH"
pytest arc/ --cov --cov-report=xml -ra -vv -n auto
pytest arc/ --cov --cov-report=xml -ra -vv -n 6 --dist worksteal

- name: Run Functional Tests
shell: micromamba-shell {0}
Expand All @@ -115,7 +99,7 @@ jobs:
run: |
echo "Running Functional Tests from $(pwd)..."
export PYTHONPATH="${{ github.workspace }}/AutoTST:${{ github.workspace }}/KinBot:$PYTHONPATH"
pytest functional/ --cov --cov-append --cov-report=xml -ra -vv -n auto
pytest functional/ --cov --cov-append --cov-report=xml -ra -vv -n 6 --dist loadfile

- name: Upload coverage data
uses: codecov/codecov-action@v6
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ timer.dat
# .vscode
.vscode

# Claude
CLAUDE.md
.claude/*

# .trunk folder
.trunk

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ install-all: install

install:
@echo "Installing all external ARC dependencies..."
bash $(DEVTOOLS_DIR)/install_all.sh --rmg-rms
bash $(DEVTOOLS_DIR)/install_all.sh

install-ci:
@echo "Installing all external ARC dependencies for CI (no clean)..."
bash $(DEVTOOLS_DIR)/install_all.sh --no-clean

install-lite:
@echo "Installing ARC's lite version (no external dependencies)..."
bash $(DEVTOOLS_DIR)/install_all.sh --no-ext --rmg-rms
bash $(DEVTOOLS_DIR)/install_all.sh --no-ext

install-rmg:
bash $(DEVTOOLS_DIR)/install_rmg.sh $(RMG_ARGS)
Expand Down
20 changes: 10 additions & 10 deletions arc/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from arc.imports import settings

if TYPE_CHECKING:
from arc.molecule.molecule import Molecule
from arc.molecule.molecule import Atom, Molecule

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'Atom' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Atom occurs after the cyclic
import
of arc.common.
'Atom' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Atom occurs after the cyclic
import
of arc.common.
'Atom' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Atom occurs after the cyclic
import
of arc.common.
'Atom' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Atom occurs after the cyclic
import
of arc.common.
'Atom' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Atom occurs after the cyclic
import
of arc.common.
'Atom' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Atom occurs after the cyclic
import
of arc.common.
'Atom' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Atom occurs after the cyclic
import
of arc.common.
'Atom' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Atom occurs after the cyclic
import
of arc.common.

Copilot Autofix

AI 20 minutes ago

To break the reported module-level cycle without changing runtime behavior, replace the TYPE_CHECKING import of Atom/Molecule with forward-reference type aliases that are only strings at analysis time. This preserves type annotation usability while removing the explicit import edge from arc.common to arc.molecule.molecule.

Best single change in arc/common.py:

  • Keep TYPE_CHECKING imported from typing (already present).
  • Remove:
    • if TYPE_CHECKING:
    • from arc.molecule.molecule import Atom, Molecule
  • Add simple conditional aliases:
    • if TYPE_CHECKING: Atom = Any; Molecule = Any
    • else: Atom = Any; Molecule = Any
      This ensures names exist for annotations and avoids importing ARC molecule module from common.
Suggested changeset 1
arc/common.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/arc/common.py b/arc/common.py
--- a/arc/common.py
+++ b/arc/common.py
@@ -31,7 +31,11 @@
 from arc.imports import settings
 
 if TYPE_CHECKING:
-    from arc.molecule.molecule import Atom, Molecule
+    Atom = Any
+    Molecule = Any
+else:
+    Atom = Any
+    Molecule = Any
 
 logger = logging.getLogger('arc')
 logging.getLogger('matplotlib.font_manager').disabled = True
EOF
@@ -31,7 +31,11 @@
from arc.imports import settings

if TYPE_CHECKING:
from arc.molecule.molecule import Atom, Molecule
Atom = Any
Molecule = Any
else:
Atom = Any
Molecule = Any

logger = logging.getLogger('arc')
logging.getLogger('matplotlib.font_manager').disabled = True
Copilot is powered by AI and may make mistakes. Always verify output.

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'Molecule' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Molecule occurs after the cyclic
import
of arc.common.
'Molecule' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Molecule occurs after the cyclic
import
of arc.common.
'Molecule' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Molecule occurs after the cyclic
import
of arc.common.
'Molecule' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Molecule occurs after the cyclic
import
of arc.common.
'Molecule' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Molecule occurs after the cyclic
import
of arc.common.
'Molecule' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Molecule occurs after the cyclic
import
of arc.common.
'Molecule' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Molecule occurs after the cyclic
import
of arc.common.
'Molecule' may not be defined if module
arc.molecule.molecule
is imported before module
arc.common
, as the
definition
of Molecule occurs after the cyclic
import
of arc.common.

Copilot Autofix

AI 20 minutes ago

General fix: remove the direct type-only import edge from arc.common to arc.molecule.molecule and use forward-reference string annotations (or Any) for those types in this module.

Best single fix here: in arc/common.py, delete the TYPE_CHECKING import block that imports Atom, Molecule. Keep TYPE_CHECKING if it may be used elsewhere in unseen parts of the same file, but do not import from arc.molecule.molecule at module scope. This breaks the static cycle at the reported location and addresses all alert variants pointing to that same import.

Suggested changeset 1
arc/common.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/arc/common.py b/arc/common.py
--- a/arc/common.py
+++ b/arc/common.py
@@ -30,9 +30,6 @@
 from arc.exceptions import InputError, SettingsError
 from arc.imports import settings
 
-if TYPE_CHECKING:
-    from arc.molecule.molecule import Atom, Molecule
-
 logger = logging.getLogger('arc')
 logging.getLogger('matplotlib.font_manager').disabled = True
 
EOF
@@ -30,9 +30,6 @@
from arc.exceptions import InputError, SettingsError
from arc.imports import settings

if TYPE_CHECKING:
from arc.molecule.molecule import Atom, Molecule

logger = logging.getLogger('arc')
logging.getLogger('matplotlib.font_manager').disabled = True

Copilot is powered by AI and may make mistakes. Always verify output.

logger = logging.getLogger('arc')
logging.getLogger('matplotlib.font_manager').disabled = True
Expand All @@ -44,6 +44,8 @@

R = 8.31446261815324 # J/(mol*K)
EA_UNIT_CONVERSION = {'J/mol': 1, 'kJ/mol': 1e+3, 'cal/mol': 4.184, 'kcal/mol': 4.184e+3}
FULL_CIRCLE = 360.0
HALF_CIRCLE = 180.0

default_job_types, servers, supported_ess = settings['default_job_types'], settings['servers'], settings['supported_ess']

Expand Down Expand Up @@ -825,7 +827,7 @@
return sorted(bonds)


def determine_top_group_indices(mol: 'Molecule', atom1: 'Atom', atom2: 'Atom', index: int = 1) -> tuple[list, bool]:
def determine_top_group_indices(mol: Molecule, atom1: Atom, atom2: Atom, index: int = 1) -> tuple[list, bool]:
"""
Determine the indices of a "top group" in a molecule.
The top is defined as all atoms connected to atom2, including atom2, excluding the direction of atom1.
Expand Down Expand Up @@ -1507,14 +1509,11 @@
return True


FULL_CIRCLE = 360.0
HALF_CIRCLE = 180.0

def get_angle_in_180_range(angle: float,
round_to: int | None = 2,
) -> float:
"""
Get the corresponding angle in the -180 to +180 degree range.
Get the corresponding angle in the -180 to +180 degree range, (-180,180]

Args:
angle (float): An angle in degrees.
Expand All @@ -1524,7 +1523,8 @@
Returns:
float: The corresponding angle in the -180 to +180 degree range.
"""
return (angle + HALF_CIRCLE) % FULL_CIRCLE - HALF_CIRCLE
wrapped = (angle + HALF_CIRCLE) % FULL_CIRCLE - HALF_CIRCLE
return round(wrapped, round_to) if round_to is not None else wrapped


def signed_angular_diff(phi_1: float, phi_2: float) -> float:
Expand Down Expand Up @@ -1736,7 +1736,7 @@
break


def dfs(mol: 'Molecule',
def dfs(mol: Molecule,
start: int,
sort_result: bool = True,
) -> list[int]:
Expand Down Expand Up @@ -1767,7 +1767,7 @@
return visited


def sort_atoms_in_descending_label_order(mol: 'Molecule') -> None:
def sort_atoms_in_descending_label_order(mol: Molecule) -> None:
"""
If all atoms in the molecule object have a label, this function reassign the
.atoms in Molecule with a list of atoms with the orders based on the labels of the atoms.
Expand All @@ -1786,7 +1786,7 @@
return None


def is_xyz_mol_match(mol: 'Molecule',
def is_xyz_mol_match(mol: Molecule,
xyz: dict) -> bool:
"""
A helper function that matches RMG's Molecule object to an xyz,
Expand Down
32 changes: 26 additions & 6 deletions arc/common_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,33 @@
"""
Contains unit tests for ARC's common module
"""
@classmethod
def _clean_globalized_restart_artifact(cls):
"""Remove the globalized restart-paths artifact written by
:meth:`test_globalize_paths`.

Called from BOTH ``setUpClass`` (defensive: wipes a stale
artifact left behind by a previously interrupted run) and
``tearDownClass`` (the normal cleanup path). This makes the
cleanup self-healing: a Ctrl+C, ``kill``, or hard error during
a previous run cannot leave the next run inheriting the prior
``restart_paths_globalized.yml``.
"""
globalized_restart_path = os.path.join(
common.ARC_TESTING_PATH, 'restart', '4_globalized_paths',
'restart_paths_globalized.yml')
if os.path.isfile(globalized_restart_path):
try:
os.remove(path=globalized_restart_path)
except OSError:

Check notice

Code scanning / CodeQL

Empty except Note

'except' clause does nothing but pass and there is no explanatory comment.

Copilot Autofix

AI 21 minutes ago

To fix this without changing functionality, keep the cleanup best-effort but make the exception handling explicit and self-documenting.

Best single fix in arc/common_test.py (within _clean_globalized_restart_artifact):

  • Replace the empty except OSError: pass with:
    • an explanatory comment clarifying why failures are intentionally ignored in test cleanup, and
    • capture the exception object and only ignore FileNotFoundError (benign race), re-raising other OSError values so real issues are not silently hidden.

No new imports or dependencies are required.

Suggested changeset 1
arc/common_test.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/arc/common_test.py b/arc/common_test.py
--- a/arc/common_test.py
+++ b/arc/common_test.py
@@ -48,8 +48,12 @@
         if os.path.isfile(globalized_restart_path):
             try:
                 os.remove(path=globalized_restart_path)
-            except OSError:
-                pass
+            except OSError as e:
+                # Best-effort cleanup for test artifacts: ignore only a
+                # missing-file race (e.g., another cleanup path removed it),
+                # but surface other filesystem problems.
+                if not isinstance(e, FileNotFoundError):
+                    raise
 
     @classmethod
     def setUpClass(cls):
EOF
@@ -48,8 +48,12 @@
if os.path.isfile(globalized_restart_path):
try:
os.remove(path=globalized_restart_path)
except OSError:
pass
except OSError as e:
# Best-effort cleanup for test artifacts: ignore only a
# missing-file race (e.g., another cleanup path removed it),
# but surface other filesystem problems.
if not isinstance(e, FileNotFoundError):
raise

@classmethod
def setUpClass(cls):
Copilot is powered by AI and may make mistakes. Always verify output.
pass

@classmethod
def setUpClass(cls):
"""
A method that is run before all unit tests in this class.
"""
cls._clean_globalized_restart_artifact()
cls.maxDiff = None
cls.default_job_types = {'conf_opt': True,
'opt': True,
Expand Down Expand Up @@ -1077,8 +1099,8 @@
"""Test the getting a corresponding angle in the -180 to +180 range"""
self.assertEqual(common.get_angle_in_180_range(0), 0)
self.assertEqual(common.get_angle_in_180_range(10), 10)
self.assertAlmostEqual(common.get_angle_in_180_range(-5.364589), -5.364589)
self.assertAlmostEqual(common.get_angle_in_180_range(-5.364589), -5.364589)
self.assertAlmostEqual(common.get_angle_in_180_range(-5.364589, round_to=None), -5.364589)
self.assertAlmostEqual(common.get_angle_in_180_range(-5.364589, round_to=None), -5.364589)
self.assertEqual(common.get_angle_in_180_range(-120), -120)
self.assertAlmostEqual(common.get_angle_in_180_range(179.999), 180, 2)
self.assertEqual(common.get_angle_in_180_range(180), -180)
Expand All @@ -1098,6 +1120,7 @@
self.assertEqual(common.get_angle_in_180_range(-270), 90)
self.assertAlmostEqual(common.get_angle_in_180_range(45.5), 45.5, places=7)
self.assertAlmostEqual(common.get_angle_in_180_range(719.9), -0.1, places=7)
self.assertAlmostEqual(common.get_angle_in_180_range(-5.364589, round_to=2), -5.36)

def test_signed_angular_diff(self):
"""Test the signed angular difference between two angles"""
Expand Down Expand Up @@ -1396,10 +1419,7 @@
"""
A function that is run ONCE after all unit tests in this class.
"""
globalized_restart_path = os.path.join(common.ARC_TESTING_PATH, 'restart', '4_globalized_paths',
'restart_paths_globalized.yml')
if os.path.isfile(globalized_restart_path):
os.remove(path=globalized_restart_path)
cls._clean_globalized_restart_artifact()


if __name__ == '__main__':
Expand Down
Loading
Loading