From 48f45801b474c2789217d045eaacd2c7b61df701 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Tue, 20 Jan 2026 15:31:01 -0500 Subject: [PATCH 01/31] ignore build folder --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 86adcd8..c77ceb1 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ meta_production_sample.pkl development/**/*.csv development/**/*.mol2 -pymol_renders/ \ No newline at end of file +pymol_renders/ +build/ \ No newline at end of file From 71b96dad101c46d6e64d77a270617826b2ba9eca Mon Sep 17 00:00:00 2001 From: orionarcher Date: Tue, 20 Jan 2026 15:31:14 -0500 Subject: [PATCH 02/31] new CI yml files --- .github/workflows/test-tutorials.yml | 108 +++++++++++++++++++++++++++ .github/workflows/test.yml | 53 +++++++++++++ .github/workflows/unittests.yml | 37 --------- 3 files changed, 161 insertions(+), 37 deletions(-) create mode 100644 .github/workflows/test-tutorials.yml create mode 100644 .github/workflows/test.yml delete mode 100644 .github/workflows/unittests.yml diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml new file mode 100644 index 0000000..74d585e --- /dev/null +++ b/.github/workflows/test-tutorials.yml @@ -0,0 +1,108 @@ +name: Tutorial Tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + tutorials: + runs-on: ubuntu-latest + timeout-minutes: 60 + defaults: + run: + shell: bash -el {0} + + steps: + - name: Check out repo + uses: actions/checkout@v5 + + - name: Setup micromamba + uses: mamba-org/setup-micromamba@v2 + with: + micromamba-version: 'latest' + environment-name: tutorial-env + create-args: >- + python=3.12 + xtb + ase + numpy + openbabel + scipy + numba + py3Dmol + pandas + crest + pynauty + tqdm + mendeleev + tblite-python + jupytext + matplotlib + pytorch + init-shell: bash + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + version: "latest" + + - name: Install package and mace-torch + run: | + uv pip install --no-deps -e . + uv pip install mace-torch + + - name: Convert notebooks to Python scripts + run: | + cd documentation/tutorials + for notebook in *.ipynb; do + jupytext --to py:percent "$notebook" + done + + - name: Run tutorials + run: | + cd documentation/tutorials + + # Tutorials to skip + SKIP_TUTORIALS=( + "10-Conformer_Searching.py" # CREST sampling takes 6+ minutes + "12-Using_View_Structures.py" # Visualization-focused, pymol renders + ) + + # Mock view_structures to avoid py3Dmol rendering issues + cat > mock_view.py << 'EOF' + import architector.visualization as viz + _original = viz.view_structures + def mock_view_structures(*args, **kwargs): + return None + viz.view_structures = mock_view_structures + import architector + architector.view_structures = mock_view_structures + EOF + + # Run each tutorial + for script in *.py; do + skip=false + for skip_tutorial in "${SKIP_TUTORIALS[@]}"; do + if [[ "$script" == "$skip_tutorial" ]]; then + skip=true + echo "Skipping $script" + break + fi + done + + if [[ "$skip" == "false" && "$script" != "mock_view.py" ]]; then + echo "Running $script..." + timeout 600 python -c " + import mock_view + exec(open('$script').read()) + " + fi + done + - name: Cleanup + run: | + rm -rf documentation/tutorials/pymol_renders + rm -f documentation/tutorials/*.xyz + rm -f documentation/tutorials/*.py + if: always() diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..2f4ea6c --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,53 @@ +name: Tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + defaults: + run: + shell: bash -el {0} + + steps: + - name: Check out repo + uses: actions/checkout@v5 + + - name: Setup micromamba + uses: mamba-org/setup-micromamba@v2 + with: + micromamba-version: 'latest' + environment-name: test-env + create-args: >- + python=3.12 + xtb + ase + numpy + openbabel + scipy + numba + py3Dmol + pandas + crest + pynauty + tqdm + mendeleev + tblite-python + init-shell: bash + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + version: "latest" + + - name: Install package + run: | + uv pip install --no-deps -e . + + - name: Run tests + run: | + python -m unittest discover tests diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml deleted file mode 100644 index f9617a9..0000000 --- a/.github/workflows/unittests.yml +++ /dev/null @@ -1,37 +0,0 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - -name: Python package - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ['3.8', '3.9', '3.10'] - - steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2.2.0 - with: - python-version: ${{ matrix.python-version }} - mamba-version: "*" - channels: conda-forge - channel-priority: strict - auto-update-conda: true - environment-file: .ci_support/environment.yml - miniforge-variant: Mambaforge - - name: Setup - shell: bash -l {0} - run: | - pip install --no-deps . - - name: Test - shell: bash -l {0} - run: python -m unittest discover tests From eff0f2cefbe0e0deb160b10c10ecd6cac56afcac Mon Sep 17 00:00:00 2001 From: orionarcher Date: Tue, 20 Jan 2026 16:04:43 -0500 Subject: [PATCH 03/31] run tutorials in parallel --- .github/workflows/test-tutorials.yml | 84 +++++++++++++++++----------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index 74d585e..d327d13 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -7,9 +7,48 @@ on: branches: [main] jobs: - tutorials: + find-tutorials: runs-on: ubuntu-latest - timeout-minutes: 60 + outputs: + tutorials: ${{ steps.set-matrix.outputs.tutorials }} + steps: + - name: Check out repo + uses: actions/checkout@v5 + + - name: Find tutorial notebooks + id: set-matrix + run: | + # Tutorials to skip + SKIP_TUTORIALS=( + "10-Conformer_Searching.ipynb" # CREST sampling takes 6+ minutes + "12-Using_View_Structures.ipynb" # Visualization-focused, pymol renders + ) + + # Find all notebooks and filter out skipped ones + TUTORIALS=$(find documentation/tutorials -name "*.ipynb" | while read notebook; do + basename=$(basename "$notebook") + skip=false + for skip_tutorial in "${SKIP_TUTORIALS[@]}"; do + if [[ "$basename" == "$skip_tutorial" ]]; then + skip=true + break + fi + done + if [[ "$skip" == "false" ]]; then + echo "$notebook" + fi + done | jq -R -s -c 'split("\n")[:-1]') + + echo "tutorials=$TUTORIALS" >> $GITHUB_OUTPUT + + run-tutorial: + needs: find-tutorials + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + tutorial: ${{ fromJson(needs.find-tutorials.outputs.tutorials) }} defaults: run: shell: bash -el {0} @@ -53,24 +92,16 @@ jobs: uv pip install --no-deps -e . uv pip install mace-torch - - name: Convert notebooks to Python scripts + - name: Convert notebook to Python script run: | - cd documentation/tutorials - for notebook in *.ipynb; do - jupytext --to py:percent "$notebook" - done + jupytext --to py:percent "${{ matrix.tutorial }}" - - name: Run tutorials + - name: Run tutorial run: | - cd documentation/tutorials - - # Tutorials to skip - SKIP_TUTORIALS=( - "10-Conformer_Searching.py" # CREST sampling takes 6+ minutes - "12-Using_View_Structures.py" # Visualization-focused, pymol renders - ) + cd $(dirname "${{ matrix.tutorial }}") + SCRIPT=$(basename "${{ matrix.tutorial }}" .ipynb).py - # Mock view_structures to avoid py3Dmol rendering issues + # Create mock for view_structures to avoid py3Dmol rendering issues cat > mock_view.py << 'EOF' import architector.visualization as viz _original = viz.view_structures @@ -81,25 +112,12 @@ jobs: architector.view_structures = mock_view_structures EOF - # Run each tutorial - for script in *.py; do - skip=false - for skip_tutorial in "${SKIP_TUTORIALS[@]}"; do - if [[ "$script" == "$skip_tutorial" ]]; then - skip=true - echo "Skipping $script" - break - fi - done - - if [[ "$skip" == "false" && "$script" != "mock_view.py" ]]; then - echo "Running $script..." - timeout 600 python -c " + echo "Running $SCRIPT..." + timeout 600 python -c " import mock_view - exec(open('$script').read()) + exec(open('$SCRIPT').read()) " - fi - done + - name: Cleanup run: | rm -rf documentation/tutorials/pymol_renders From ac6c5ce4192c3fe4360fc3398cc636edadd8d9c9 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Wed, 21 Jan 2026 08:36:34 -0500 Subject: [PATCH 04/31] Add Secondary_Solvation_Shell to CI workflow triggers --- .github/workflows/test-tutorials.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index d327d13..c355f81 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -2,9 +2,9 @@ name: Tutorial Tests on: push: - branches: [main] + branches: [main, Secondary_Solvation_Shell] pull_request: - branches: [main] + branches: [main, Secondary_Solvation_Shell] jobs: find-tutorials: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2f4ea6c..e750a65 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,9 +2,9 @@ name: Tests on: push: - branches: [main] + branches: [main, Secondary_Solvation_Shell] pull_request: - branches: [main] + branches: [main, Secondary_Solvation_Shell] jobs: test: From 9c3fc73dcd646289ae904876a5d974ab2bdb50b4 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Wed, 21 Jan 2026 11:04:45 -0500 Subject: [PATCH 05/31] Remove view_structures mock from tutorial tests --- .github/workflows/test-tutorials.yml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index c355f81..97c478b 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -101,22 +101,8 @@ jobs: cd $(dirname "${{ matrix.tutorial }}") SCRIPT=$(basename "${{ matrix.tutorial }}" .ipynb).py - # Create mock for view_structures to avoid py3Dmol rendering issues - cat > mock_view.py << 'EOF' - import architector.visualization as viz - _original = viz.view_structures - def mock_view_structures(*args, **kwargs): - return None - viz.view_structures = mock_view_structures - import architector - architector.view_structures = mock_view_structures - EOF - echo "Running $SCRIPT..." - timeout 600 python -c " - import mock_view - exec(open('$SCRIPT').read()) - " + timeout 600 python "$SCRIPT" - name: Cleanup run: | From a9b13e5abb692277e34ceca1e1da075036fe2e6b Mon Sep 17 00:00:00 2001 From: orionarcher Date: Wed, 21 Jan 2026 11:38:30 -0500 Subject: [PATCH 06/31] insignificant change to test permissions --- .github/workflows/test-tutorials.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index 97c478b..3c1d6e5 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -101,7 +101,7 @@ jobs: cd $(dirname "${{ matrix.tutorial }}") SCRIPT=$(basename "${{ matrix.tutorial }}" .ipynb).py - echo "Running $SCRIPT..." + echo "Running $SCRIPT...!" timeout 600 python "$SCRIPT" - name: Cleanup From 3bf77a183064377894decdee49c33cac0667e90d Mon Sep 17 00:00:00 2001 From: orionarcher Date: Wed, 21 Jan 2026 11:44:08 -0500 Subject: [PATCH 07/31] Add XTB verification step to tutorial tests --- .github/workflows/test-tutorials.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index 3c1d6e5..91ebd12 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -92,6 +92,20 @@ jobs: uv pip install --no-deps -e . uv pip install mace-torch + - name: Verify XTB is working + run: | + xtb --version + python -c " + from architector import build_complex + out = build_complex({ + 'core': {'metal': 'Fe', 'coreCN': 6}, + 'ligands': [{'smiles': 'O', 'coordList': [0]}] * 6, + 'parameters': {'metal_ox': 2} + }) + assert len(out) > 0, 'XTB/build_complex failed - returned empty dict' + print('XTB verification passed:', list(out.keys())) + " + - name: Convert notebook to Python script run: | jupytext --to py:percent "${{ matrix.tutorial }}" From 5e1f3585977b4f36469be1f54d2f257bbae1e1f1 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Wed, 21 Jan 2026 11:47:41 -0500 Subject: [PATCH 08/31] Fix: use xtb-python instead of xtb CLI package --- .github/workflows/test-tutorials.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index 91ebd12..329c907 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -64,7 +64,7 @@ jobs: environment-name: tutorial-env create-args: >- python=3.12 - xtb + xtb-python ase numpy openbabel diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e750a65..42496f7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,7 @@ jobs: environment-name: test-env create-args: >- python=3.12 - xtb + xtb-python ase numpy openbabel From dde23d5b19d8d44e5125c8bee69bb3dea68acc80 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Wed, 21 Jan 2026 11:52:24 -0500 Subject: [PATCH 09/31] Remove XTB verification step --- .github/workflows/test-tutorials.yml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index 329c907..a0afc92 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -92,20 +92,6 @@ jobs: uv pip install --no-deps -e . uv pip install mace-torch - - name: Verify XTB is working - run: | - xtb --version - python -c " - from architector import build_complex - out = build_complex({ - 'core': {'metal': 'Fe', 'coreCN': 6}, - 'ligands': [{'smiles': 'O', 'coordList': [0]}] * 6, - 'parameters': {'metal_ox': 2} - }) - assert len(out) > 0, 'XTB/build_complex failed - returned empty dict' - print('XTB verification passed:', list(out.keys())) - " - - name: Convert notebook to Python script run: | jupytext --to py:percent "${{ matrix.tutorial }}" From 6c26ef35fa9c6257e2919877285ff270faedb43e Mon Sep 17 00:00:00 2001 From: orionarcher Date: Wed, 21 Jan 2026 11:52:59 -0500 Subject: [PATCH 10/31] Add torch-dftd for MACE dispersion corrections --- .github/workflows/test-tutorials.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index a0afc92..6b971c4 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -90,7 +90,7 @@ jobs: - name: Install package and mace-torch run: | uv pip install --no-deps -e . - uv pip install mace-torch + uv pip install mace-torch torch-dftd - name: Convert notebook to Python script run: | From 1764cc63b1eaef101b0e4aa141b8285cfc8a64ee Mon Sep 17 00:00:00 2001 From: Michael Taylor Date: Wed, 21 Jan 2026 17:51:41 -0700 Subject: [PATCH 11/31] Update to remove xtb-python --- .github/workflows/test-tutorials.yml | 1 - .github/workflows/test.yml | 1 - README.md | 4 +- architector/io_calc.py | 20 +- architector/io_lig.py | 13 +- architector/io_xtb_calc.py | 609 --------------------------- 6 files changed, 4 insertions(+), 644 deletions(-) delete mode 100644 architector/io_xtb_calc.py diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index 6b971c4..8627617 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -64,7 +64,6 @@ jobs: environment-name: tutorial-env create-args: >- python=3.12 - xtb-python ase numpy openbabel diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 42496f7..c29906e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,6 @@ jobs: environment-name: test-env create-args: >- python=3.12 - xtb-python ase numpy openbabel diff --git a/README.md b/README.md index ffbd993..09778cf 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,8 @@ pip install -e . ## XTB (backend) Potentially Useful References: * [Available Solvents](https://xtb-docs.readthedocs.io/en/latest/gbsa.html) -* [Available Methods](https://xtb-python.readthedocs.io/en/latest/general-api.html) -* [ASE Calculator](https://xtb-python.readthedocs.io/en/latest/ase-calculator.html) +* [Available Methods](https://xtb-docs.readthedocs.io/en/latest/commandline.html) +* [ASE Calculator](https://tblite.readthedocs.io/en/latest/users/ase.html#tblite.ase.TBLite) * [XTB Documentation](https://xtb-docs.readthedocs.io/en/latest/contents.html) ## Basic Use of complex construction functionality: diff --git a/architector/io_calc.py b/architector/io_calc.py index 58bd999..590944c 100644 --- a/architector/io_calc.py +++ b/architector/io_calc.py @@ -21,15 +21,6 @@ from ase.optimize import LBFGS from ase.constraints import FixAtoms, FixBondLengths, FixInternals -# Add any other ASE calculator here. -# To extend to other methods. -has_xtb_python = False -try: - from xtb.ase.calculator import XTB - has_xtb_python = True -except ImportError: - pass - from architector.arch_xtb_text_ase_calc import XTB_Calculator from tblite.ase import TBLite @@ -455,7 +446,7 @@ def calculate(self): electronic_temperature=self.xtb_electronic_temperature, verbosity=-1, ) - elif (not has_xtb_python) or (self.xtb_relax): + else: calc = XTB_Calculator( xtb_method=self.method, xtb_accuracy=self.xtb_accuracy, @@ -464,15 +455,6 @@ def calculate(self): xtb_solvent=self.xtb_solvent, xtb_relax=self.xtb_relax, ) - else: # legacy xtb-python - calc = XTB( - method=self.method, - solvent=self.xtb_solvent, - max_iterations=self.xtb_max_iterations, - electronic_temperature=self.xtb_electronic_temperature, - accuracy=self.xtb_accuracy, - ) - # verbosity=0) # Difference of more than 1. Still perform a ff_preoptimization if requested. if np.abs(self.mol.xtb_charge - self.mol.charge) > 1: if len(self.trans_oxo_triples) > 0: diff --git a/architector/io_lig.py b/architector/io_lig.py index b3c6922..e6cbc0e 100644 --- a/architector/io_lig.py +++ b/architector/io_lig.py @@ -38,14 +38,6 @@ from ase.optimize.bfgslinesearch import BFGSLineSearch import ase.constraints as ase_con -has_xtb_python = False -try: - from xtb.ase.calculator import XTB - - has_xtb_python = True -except: - pass - from architector.arch_xtb_text_ase_calc import XTB_Calculator # from tblite.ase import TBLite -> No GFN-FF support yet. @@ -119,10 +111,7 @@ def set_XTB_calc(ase_atoms): """ ase_atoms.set_initial_charges(np.zeros(len(ase_atoms))) ase_atoms.set_initial_magnetic_moments(np.zeros(len(ase_atoms))) - if has_xtb_python: - calc = XTB(method="GFN-FF") - else: - calc = XTB_Calculator(xtb_method="GFN-FF") + calc = XTB_Calculator(xtb_method="GFN-FF") # Default to only GFN-FF for ligand conformer relaxation. ase_atoms.calc = calc return ase_atoms diff --git a/architector/io_xtb_calc.py b/architector/io_xtb_calc.py deleted file mode 100644 index 797a532..0000000 --- a/architector/io_xtb_calc.py +++ /dev/null @@ -1,609 +0,0 @@ -# """ -# Assign XTB calculator to ase_atoms object following defualts or user input! -# Written by Michael Taylor - -# This module is largely depricated in architector in favor of io_calc.py -# It is useful still for xtb evalulations away from architector's main codebase. -# """ - -import numpy as np -from xtb.ase.calculator import XTB -from architector import arch_context_manage, io_molecule -import architector.io_ptable as io_ptable -import ase -from ase.optimize import BFGSLineSearch -from ase.atoms import Atom, Atoms - - -def set_XTB_calc(ase_atoms, parameters=dict(), assembly=False, isCp_lig=False): - """set_XTB_calc - assign xtb calculator to atoms instance! - - Parameters - ---------- - ase_atoms : ase.atoms.Atoms - atoms to assign calculator to - parameters : dict, optional - parameters from input/ io_process_input! - assembly : bool, optional - whether or not this is assembly or final relaxation, by default False - isCP_lig : bool, optional - whether this is cP ligand evaluation or not, by default False - """ - if isCp_lig: - ase_atoms.set_initial_charges(np.zeros(len(ase_atoms))) - ase_atoms.set_initial_magnetic_moments(np.zeros(len(ase_atoms))) - calc = XTB( - method="GFN-FF" - ) # ,verbosity=0) # Defaul to only GFN-FF for ligand conformer relaxation. - else: - # Charge -> charges already assigned to components during assembly - if (parameters["full_charge"] is not None) and (not assembly): - charge_vect = np.zeros(len(ase_atoms)) - charge_vect[0] = parameters["full_charge"] - ase_atoms.set_initial_charges(charge_vect) - else: - charge_vect = ase_atoms.get_initial_charges() - - mol_charge = np.sum(charge_vect) - symbols = ase_atoms.get_chemical_symbols() - metals = [x for x in symbols if x in io_ptable.all_metals] - - f_in_core = False - - if len(metals) == 1: - if metals[0] in io_ptable.heavy_metals: - f_in_core = True - else: - print( - "No metals - continuing anyway with obviously no f in core elements." - ) - - # Handle spin / magnetism - even_odd_electrons = ( - np.sum([atom.number for atom in ase_atoms]) - mol_charge - ) % 2 - if (parameters["full_spin"] is not None) and (not assembly): - uhf = parameters["full_spin"] - uhf_start = np.zeros(len(ase_atoms)) - uhf_start[0] = uhf - ase_atoms.set_initial_magnetic_moments(uhf_start) - else: - uhf = parameters[ - "metal_spin" - ] # Metal spin set by io_process_input to defaults. - if (even_odd_electrons == 1) and (uhf == 0): - uhf = 1 - elif (even_odd_electrons == 1) and (uhf < 7) and (uhf % 2 == 0): - uhf += 1 - elif (even_odd_electrons == 1) and (uhf >= 7) and (uhf % 2 == 0): - uhf -= 1 - if (even_odd_electrons == 0) and (uhf % 2 == 1): - uhf = uhf - 1 - elif (even_odd_electrons == 1) and (uhf % 2 == 0): - uhf = uhf + 1 - uhf_start = np.zeros(len(ase_atoms)) - if not f_in_core: - uhf_start[0] = uhf - else: # F in core assumes for a 3+ lanthanide there are 11 valence electrons (8 once the 3+ is taken into account) - even_odd_electrons = np.sum( - [atom.number for atom in ase_atoms] - ) - even_odd_electrons = ( - even_odd_electrons - - io_ptable.elements.index(metals[0]) - + 11 - - mol_charge - ) - even_odd_electrons = even_odd_electrons % 2 - if even_odd_electrons == 0: - uhf_start[0] = 0 - else: - uhf_start[0] = 1 - ase_atoms.set_initial_magnetic_moments(uhf_start) - - if assembly: - if ( - parameters["assemble_method"] == "GFN-FF" - ): # Need to turn off charges for GFN-FF evaluation. Probably an XTB-end bug. - ase_atoms.set_initial_charges(np.zeros(len(ase_atoms))) - ase_atoms.set_initial_magnetic_moments( - np.zeros(len(ase_atoms)) - ) - calc = XTB( - method=parameters["assemble_method"], - solvent=parameters["solvent"], - ) - # verbosity=0) - else: - if ( - parameters["full_method"] == "GFN-FF" - ): # Need to turn off charges for GFN-FF evaluation. Probably an XTB-end bug. - ase_atoms.set_initial_charges(np.zeros(len(ase_atoms))) - ase_atoms.set_initial_magnetic_moments( - np.zeros(len(ase_atoms)) - ) - calc = XTB( - method=parameters["full_method"], solvent=parameters["solvent"] - ) - # verbosity=0) - - ######################################################### - ########### Calculator Now Set! ######################### - ######################################################### - ase_atoms.calc = calc - return ase_atoms - - -def set_XTB_calc_lig( - ase_atoms, charge=None, uhf=None, method="GFN2-xTB", solvent="none" -): - """set_XTB_calc - assign xtb calculator to ligand atoms instance! - - Parameters - ---------- - ase_atoms : ase.atoms.Atoms - atoms to assign calculator to - charge : int, optional - charge of the species, default to initial charges set on ase_atoms - uhf : int, optional - number of unpaired electrons in the system, default to 0 - method : str, optional - which gfn family method to use, default GFN2-xTB - solvent: str, optional - use a solvent?, default 'none' - """ - if charge: - mol_charge = charge - else: - mol_charge = np.sum(ase_atoms.get_initial_charges()) - - charge_vect = np.zeros(len(ase_atoms)) - charge_vect[0] = mol_charge - ase_atoms.set_initial_charges(charge_vect) - - # Handle spin / magnetism - even_odd_electrons = ( - np.sum([atom.number for atom in ase_atoms]) - mol_charge - ) % 2 - if uhf is not None: - uhf = uhf - if (even_odd_electrons == 1) and (uhf == 0): - uhf = 1 - elif (even_odd_electrons == 1) and (uhf < 7) and (uhf % 2 == 0): - uhf += 1 - elif (even_odd_electrons == 1) and (uhf >= 7) and (uhf % 2 == 0): - uhf -= 1 - if (even_odd_electrons == 0) and (uhf % 2 == 1): - uhf = uhf - 1 - elif (even_odd_electrons == 1) and (uhf % 2 == 0): - uhf = uhf + 1 - uhf_start = np.zeros(len(ase_atoms)) - uhf_start[0] = uhf - ase_atoms.set_initial_magnetic_moments(uhf_start) - else: - uhf = 0 # Set spin to LS by default - if (even_odd_electrons == 1) and (uhf == 0): - uhf = 1 - elif (even_odd_electrons == 1) and (uhf < 7) and (uhf % 2 == 0): - uhf += 1 - elif (even_odd_electrons == 1) and (uhf >= 7) and (uhf % 2 == 0): - uhf -= 1 - if (even_odd_electrons == 0) and (uhf % 2 == 1): - uhf = uhf - 1 - elif (even_odd_electrons == 1) and (uhf % 2 == 0): - uhf = uhf + 1 - uhf_start = np.zeros(len(ase_atoms)) - uhf_start[0] = uhf - ase_atoms.set_initial_magnetic_moments(uhf_start) - - if ( - method == "GFN-FF" - ): # Need to turn off charges for GFN-FF evaluation. Probably an XTB-end bug. - ase_atoms.set_initial_charges(np.zeros(len(ase_atoms))) - ase_atoms.set_initial_magnetic_moments(np.zeros(len(ase_atoms))) - calc = XTB(method=method, solvent=solvent, verbosity=0) - - ######################################################### - ########### Calculator Now Set! ######################### - ######################################################### - ase_atoms.calc = calc - - -def set_XTB_calc_straight( - ase_atoms, charge=None, uhf=None, method="GFN2-xTB", solvent="none" -): - """set_XTB_calc_straight - assign xtb calculator atoms object with exaclty spin/charge requeste - - Parameters - ---------- - ase_atoms : ase.atoms.Atoms - atoms to assign calculator to - charge : int, optional - charge of the species, default to initial charges set on ase_atoms - uhf : int, optional - number of unpaired electrons in the system, default to 0 - method : str, optional - which gfn family method to use, default GFN2-xTB - solvent: str, optional - use a solvent?, default 'none' - """ - if charge: - mol_charge = charge - else: - mol_charge = np.sum(ase_atoms.get_initial_charges()) - - charge_vect = np.zeros(len(ase_atoms)) - charge_vect[0] = mol_charge - ase_atoms.set_initial_charges(charge_vect) - - uhf_start = np.zeros(len(ase_atoms)) - uhf_start[0] = uhf - ase_atoms.set_initial_magnetic_moments(uhf_start) - - if ( - method == "GFN-FF" - ): # Need to turn off charges for GFN-FF evaluation. Probably an XTB-end bug. - ase_atoms.set_initial_charges(np.zeros(len(ase_atoms))) - ase_atoms.set_initial_magnetic_moments(np.zeros(len(ase_atoms))) - calc = XTB(method=method, solvent=solvent) - # verbosity=0) - - ######################################################### - ########### Calculator Now Set! ######################### - ######################################################### - ase_atoms.calc = calc - - -def xtb_relax( - structure, - charge=None, - uhf=None, - method="GFN2-xTB", - solvent="none", - fmax=0.05, - detect_charge_spin=False, -): - """xtb_relax relax the structure with xtb - - Parameters - ---------- - structure : any 3D structure - xyz, mol2string, ase atoms ... - charge :int, optional - total charge on the system, by default None - uhf : int, optional - number of unpaired electrons in the system, by default None - method : str, optional - which method to use, by default 'GFN2-xTB' - solvent : str, optional - any name xtb solvent, by default 'none' - fmax : float, optional - default 0.05 eV/Angstrom - detect_charge_spin : bool, optional - Use obmol and io_ptable metal defaults to assign charges and spins?, default False. - - Returns - ------- - ase_atoms : ase.atoms.Atoms - relaxed structure - good : bool - whether the relaxation was succesful! - """ - if isinstance(structure, ase.atoms.Atoms): - ase_atoms = structure - if ase_atoms.calc is None: - set_XTB_calc_straight( - ase_atoms, - charge=charge, - uhf=uhf, - method=method, - solvent=solvent, - ) - else: - mol = io_molecule.convert_io_molecule( - structure, detect_charge_spin=detect_charge_spin - ) - ase_atoms = mol.ase_atoms - if detect_charge_spin: - set_XTB_calc_straight( - ase_atoms, - charge=mol.charge, - uhf=mol.xtb_uhf, - method=method, - solvent=solvent, - ) - else: - set_XTB_calc_straight( - ase_atoms, - charge=charge, - uhf=uhf, - method=method, - solvent=solvent, - ) - good = True - with arch_context_manage.make_temp_directory() as _: - try: - dyn = BFGSLineSearch(ase_atoms) - dyn.run(fmax=fmax) - except: - good = False - return ase_atoms, good - - -def xtb_sp( - structure, - charge=None, - uhf=None, - method="GFN2-xTB", - solvent="none", - detect_charge_spin=False, -): - """xtb_sp singlepoint on the structure with xtb - - Parameters - ---------- - structure : any 3D structure - xyz, mol2string, ase atoms ... - charge :int, optional - total charge on the system, by default None - uhf : int, optional - number of unpaired electrons in the system, by default None - method : str, optional - which method to use, by default 'GFN2-xTB' - solvent : str, optional - any name xtb solvent, by default 'none' - - Returns - ------- - ase_atoms : ase.atoms.Atoms - structure with calculator/energy calculated - good : bool - whether the relaxation was succesful! - """ - if isinstance(structure, ase.atoms.Atoms): - ase_atoms = structure - if ase_atoms.calc is None: - set_XTB_calc_straight( - ase_atoms, - charge=charge, - uhf=uhf, - method=method, - solvent=solvent, - ) - else: - mol = io_molecule.convert_io_molecule( - structure, detect_charge_spin=detect_charge_spin - ) - ase_atoms = mol.ase_atoms - if detect_charge_spin: - set_XTB_calc_straight( - ase_atoms, - charge=mol.charge, - uhf=mol.xtb_uhf, - method=method, - solvent=solvent, - ) - else: - set_XTB_calc_straight( - ase_atoms, - charge=charge, - uhf=uhf, - method=method, - solvent=solvent, - ) - good = True - with arch_context_manage.make_temp_directory() as _: - try: - ase_atoms.get_total_energy() - except: - good = False - return ase_atoms, good - - -eV2Hartree = 1 / 27.2114 - - -def get_rxyz_string(ase_atoms): - """dump the ase_atoms to rxyz file string""" - natom = len(ase_atoms) - ss = "" - # write an xyz file first - ss += "%d\n\n" % natom - for symb, coord in zip( - ase_atoms.get_chemical_symbols(), ase_atoms.positions - ): - ss += "%2s %12.6f %12.6f %12.6f\n" % (symb, *coord[:3]) - ss += "\n" - # then force - ss += "FORCES\n" - for symb, force in zip( - ase_atoms.get_chemical_symbols(), ase_atoms.get_forces() - ): - ss += "%3s %22.14e %22.14e %22.14e\n" % (symb, *(force * eV2Hartree)) - ss += "\n" - # then pbc, if needed - # Potentially usefull - ase_atoms.cell, ase_atoms.get_pbc(), ase_atoms.get_cell_lengths_and_angles(), get_celldisp(), get_cell() - # if ase_atoms.cell is not None: - # ss += "PBC\n" - # ss += "%12.6f %12.6f %12.6f\n" % (chemical.pbc.boxhi[0] - - # chemical.pbc.boxlo[0], 0.0, 0.0)) - # ss += "%12.6f %12.6f %12.6f\n" % (chemical.pbc.xy, - # chemical.pbc.boxhi[1] - chemical.pbc.boxlo[1], 0.0)) - # fp.write("%12.6f %12.6f %12.6f\n" % (chemical.pbc.xz, - # chemical.pbc.yz, - # chemical.pbc.boxhi[2] - chemical.pbc.boxlo[2])) - # fp.write('\n') - # then charge and spin - ss += "ATOM-CHARGE-SPIN\n" - for symb, charge, spin in zip( - ase_atoms.get_chemical_symbols(), - ase_atoms.get_charges(), - ase_atoms.get_initial_magnetic_moments(), - ): - ss += "%2s %12.6f %12.6f\n" % (symb, charge, spin) - ss += "\n" - # then different properties - ss += "ENERGY %22.14f\n" % (ase_atoms.get_total_energy() * eV2Hartree) - ss += "PA_BINDING_ENERGY %22.14f\n" % ( - ase_atoms.get_total_energy() * eV2Hartree / len(ase_atoms) - ) - dipole_vect = ase_atoms.get_dipole_moment() - ss += "DIPOLE_VEC %12.6f %12.6f %12.6f\n" % ( - dipole_vect[0], - dipole_vect[1], - dipole_vect[2], - ) - ss += "DIPOLE %12.6f\n" % np.linalg.norm(dipole_vect) - ss += "CHARGE %d\n" % ase_atoms.get_initial_charges().sum() - ss += "MULTIPLICITY %d\n" % round( - sum(ase_atoms.get_initial_magnetic_moments) + 1 - ) - # fp.write("DIFFAB %f\n" % sum(result['atomspin'])) - # fp.write('BAND_GAP %12.6f\n' % (result["egap"] * eV2Hartree)) - # fp.write('CONVERGED %s\n' % str(result["qconverged"])) - # for prop in more_props: - # fp.write('%s %f\n' % (prop.upper(), result[prop])) - ss += "SOURCE %s\n" % ase_atoms.get_calculator().name - return ss - - -def calc_xtb_ref_dict(): - energydict = dict() - print("--------1---------") - for i, elem in enumerate(io_ptable.elements): - if i > 0 and i < 87: - atoms = Atoms([Atom(elem, (0, 0, 0))]) - if elem in io_ptable.all_metals: - if elem in io_ptable.lanthanides: - spin = 0 - charge = 3 - else: - spin = io_ptable.metal_spin_dict[elem] - charge = io_ptable.metal_charge_dict[elem] - else: - if i % 2 == 0: - spin = 0 - else: - spin = 1 - charge = 0 - set_XTB_calc_straight(atoms, charge=charge, uhf=spin) - try: - energy = atoms.get_total_energy() - energydict[elem] = energy - except: - energydict[elem] = None - print("--------2---------") - for elem, spin in io_ptable.second_choice_metal_spin_dict.items(): - print(elem) - if io_ptable.elements.index(elem) < 87: - atoms = Atoms([Atom(elem, (0, 0, 0))]) - charge = io_ptable.metal_charge_dict[elem] - set_XTB_calc_straight(atoms, charge=charge, uhf=spin) - try: - energy = atoms.get_total_energy() - except: - energy = None - failed = False - if (energydict[elem] is None) and isinstance(energy, float): - energydict[elem] = energy - print("Fixed {} with alternate spin".format(elem)) - elif (energydict[elem] is None) and (energy is None): - failed = True - print("{}".format(elem) + " STILL FAILED") - if (not failed) and (energy is not None): - if energydict[elem] > energy: - energydict[elem] = energy - print("replaced {} with alternate spin".format(elem)) - print("--------3---------") - for elem, spin in io_ptable.metal_spin_dict.items(): - print(elem) - if io_ptable.elements.index(elem) < 87: - atoms = Atoms([Atom(elem, (0, 0, 0))]) - charge = 0 - if (i % 2 == 0) or (elem not in io_ptable.lanthanides): - spin = 0 - else: - spin = 1 - set_XTB_calc_straight(atoms, charge=charge, uhf=spin) - try: - energy = atoms.get_total_energy() - except: - energy = None - failed = False - if (energydict[elem] is None) and isinstance(energy, float): - energydict[elem] = energy - print("Fixed {} with 0 charge low spin".format(elem)) - elif (energydict[elem] is None) and (energy is None): - failed = True - print("{}".format(elem) + " STILL FAILED") - if (not failed) and (energy is not None): - if energydict[elem] > (energy): - energydict[elem] = energy - print( - "replaced {} with 0 charge alternate spin".format(elem) - ) - print("--------4---------") - for elem, spin in io_ptable.metal_spin_dict.items(): - print(elem) - if io_ptable.elements.index(elem) < 87: - atoms = Atoms([Atom(elem, (0, 0, 0))]) - charge = 0 - if (i % 2 == 0) or (elem not in io_ptable.lanthanides): - spin = 2 - else: - spin = 3 - set_XTB_calc_straight(atoms, charge=charge, uhf=spin) - try: - energy = atoms.get_total_energy() - except: - energy = None - failed = False - if (energydict[elem] is None) and isinstance(energy, float): - energydict[elem] = energy - print("Fixed {} with 0 charge 2 spin".format(elem)) - elif (energydict[elem] is None) and (energy is None): - failed = True - print("{}".format(elem) + " STILL FAILED") - if (not failed) and (energy is not None): - if energydict[elem] > energy: - energydict[elem] = energy - print("replaced {} with 0 charge 2 spin".format(elem)) - print("--------5---------") - for elem, spin in io_ptable.metal_spin_dict.items(): - print(elem) - if io_ptable.elements.index(elem) < 87: - atoms = Atoms([Atom(elem, (0, 0, 0))]) - charge = 0 - if (i % 2 == 0) or (elem not in io_ptable.lanthanides): - spin = 4 - else: - spin = 5 - set_XTB_calc_straight(atoms, charge=charge, uhf=spin) - try: - energy = atoms.get_total_energy() - except: - print("Failed 5 spin", elem) - energy = None - failed = False - if (energydict[elem] is None) and isinstance(energy, float): - energydict[elem] = energy - print("Fixed {} with 0 charge 4 spin".format(elem)) - elif (energydict[elem] is None) and (energy is None): - failed = True - print("{}".format(elem) + " STILL FAILED") - if (not failed) and (energy is not None): - if energydict[elem] > energy: - energydict[elem] = energy - print("replaced {} with 0 charge 5 spin".format(elem)) - - # Fix Mo - atoms = Atoms([Atom("Mo", (0, 0, 0))]) - charge = 0 - set_XTB_calc_straight(atoms, charge=charge, uhf=6) - e = atoms.get_total_energy() - energydict["Mo"] = e - # energydict_scaled_ha = {sym:val/Hartree for sym,val in energydict.items()} - return energydict From 661d6b553c4ac848d77d1a1fe693c7d8d3a252c6 Mon Sep 17 00:00:00 2001 From: Michael Taylor Date: Wed, 21 Jan 2026 18:21:27 -0700 Subject: [PATCH 12/31] Switch to pyproject.toml --- pyproject.toml | 34 ++++++++++++++++++++++++++++++++++ setup.py | 30 ------------------------------ 2 files changed, 34 insertions(+), 30 deletions(-) create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a681997 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,34 @@ +[build-system] +requires = ["setuptools>=45", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "architector" +version = "0.1.0" +description = "The architector python package - for 3D Coordination Complex Design." +readme = "README.md" +license = {text = "BSD 3-Clause License"} +authors = [ + {name = "Michael G. Taylor et al."} +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Science/Research", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: Chemistry" +] +dependencies = [ + "ase", + "numpy", + "py3Dmol", + "pynauty", + "scipy", + "pandas", + "mendeleev" +] + +[tool.setuptools] +packages = ["architector"] + +[tool.setuptools.package-data] +"*" = ["data/*.csv"] diff --git a/setup.py b/setup.py deleted file mode 100644 index 57af8c6..0000000 --- a/setup.py +++ /dev/null @@ -1,30 +0,0 @@ -from setuptools import setup - -with open("README.md", "r") as fh: - long_description = fh.read() - -# 'xtb', # Xtb as install requirement is broken, installs correctly through conda. -setup( - name='architector', - version='0.1.0', - author='Michael G. Taylor et al.', - packages=['architector'], - package_data={"": ["data/*.csv"]}, - install_requires=[ - 'ase', - 'numpy', - 'py3Dmol', - 'pynauty', - 'scipy', - 'pandas', - 'mendeleev' - ], - license="BSD 3-Clause License", - classifiers=["Development Status :: 4 - Beta", - "Intended Audience :: Science/Research", - "Programming Language :: Python :: 3", - "Topic :: Scientific/Engineering :: Chemistry"], - description="The architector python package - for 3D Coordination Complex Design.", - long_description=long_description, - long_description_content_type='text/markdown', -) From 764cf0dbb93f2018cb044866f96ed5eda9028ab1 Mon Sep 17 00:00:00 2001 From: Michael Taylor Date: Thu, 22 Jan 2026 10:36:42 -0700 Subject: [PATCH 13/31] Attempt XTB version pinning --- .ci_support/environment.yml | 2 +- .github/workflows/test-tutorials.yml | 1 + .github/workflows/test.yml | 1 + environment.yml | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.ci_support/environment.yml b/.ci_support/environment.yml index 5c43b6c..04eabf4 100644 --- a/.ci_support/environment.yml +++ b/.ci_support/environment.yml @@ -3,7 +3,7 @@ channels: - conda-forge dependencies: - python - - xtb + - xtb>6.4 - ase - numpy - openbabel diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index 8627617..49ec507 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -73,6 +73,7 @@ jobs: pandas crest pynauty + xtb>6.4 tqdm mendeleev tblite-python diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c29906e..a82b084 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,6 +32,7 @@ jobs: py3Dmol pandas crest + xtb>6.4 pynauty tqdm mendeleev diff --git a/environment.yml b/environment.yml index 8d8e532..6f45604 100644 --- a/environment.yml +++ b/environment.yml @@ -4,7 +4,7 @@ channels: dependencies: - python - crest - - xtb + - xtb>6.4 - ase - numpy - py3Dmol From 58ecd1cfc9ccf21ebdbcdd02f10106301a83186d Mon Sep 17 00:00:00 2001 From: Michael Taylor Date: Thu, 22 Jan 2026 10:39:56 -0700 Subject: [PATCH 14/31] Attempt 2 xtb bump --- .ci_support/environment.yml | 2 +- .github/workflows/test-tutorials.yml | 2 +- .github/workflows/test.yml | 2 +- environment.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.ci_support/environment.yml b/.ci_support/environment.yml index 04eabf4..7b9bbf0 100644 --- a/.ci_support/environment.yml +++ b/.ci_support/environment.yml @@ -3,7 +3,7 @@ channels: - conda-forge dependencies: - python - - xtb>6.4 + - xtb>6.5 - ase - numpy - openbabel diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index 49ec507..bd3264a 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -73,7 +73,7 @@ jobs: pandas crest pynauty - xtb>6.4 + xtb>6.5 tqdm mendeleev tblite-python diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a82b084..15ba68f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,7 +32,7 @@ jobs: py3Dmol pandas crest - xtb>6.4 + xtb>6.5 pynauty tqdm mendeleev diff --git a/environment.yml b/environment.yml index 6f45604..11cc471 100644 --- a/environment.yml +++ b/environment.yml @@ -4,7 +4,7 @@ channels: dependencies: - python - crest - - xtb>6.4 + - xtb>6.5 - ase - numpy - py3Dmol From 3165e714d1a8c834028f991344968f3476bb8288 Mon Sep 17 00:00:00 2001 From: Michael Taylor Date: Thu, 22 Jan 2026 11:02:29 -0700 Subject: [PATCH 15/31] Generalize Types for view_structures labels --- architector/visualization.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/architector/visualization.py b/architector/visualization.py index 2e25646..ebcda16 100644 --- a/architector/visualization.py +++ b/architector/visualization.py @@ -168,10 +168,10 @@ def add_bonds( for i, row in bondsdf.iterrows(): # Allow for multiple different colors of interatomic distances. if (row["atom_pair"][0] in visited) and ( - isinstance(distcolor, (list, np.ndarray)) + hasattr(distcolor, "__len__") ): tcolor = distcolor[visited.index(row["atom_pair"][0])] - elif isinstance(distcolor, (list, np.ndarray)): + elif hasattr(distcolor, "__len__"): tcolor = distcolor[count] visited.append(row["atom_pair"][0]) count += 1 @@ -442,7 +442,7 @@ def view_structures( ] else: labels = [str(i) for i in range(len(mols))] - elif isinstance(labels, list) or isinstance(labels, np.ndarray): + elif hasattr(labels, "__len__"): if len(labels) != len(mols): print( "Wrong amount of labels passed, defaulting to chemical formulas." @@ -742,7 +742,7 @@ def view_structures( label = [x.ase_atoms.get_chemical_formula() for x in mols] else: label = [] - elif isinstance(labels, list) or isinstance(labels, np.ndarray): + elif hasattr(labels, "__len__"): if len(labels) != len(mols): print( "Wrong amount of labels passed, defaulting to chemical formulas." From a11d5e735f7cf63f016494543d6d08bd24198cd2 Mon Sep 17 00:00:00 2001 From: Michael Taylor Date: Thu, 22 Jan 2026 11:02:41 -0700 Subject: [PATCH 16/31] Fix tutorial 11 plots. --- documentation/tutorials/11-Distance_Analysis.ipynb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/documentation/tutorials/11-Distance_Analysis.ipynb b/documentation/tutorials/11-Distance_Analysis.ipynb index 957cdff..db8688a 100644 --- a/documentation/tutorials/11-Distance_Analysis.ipynb +++ b/documentation/tutorials/11-Distance_Analysis.ipynb @@ -331,8 +331,9 @@ "import numpy as np\n", "g = df.groupby('atom_symbols')['distance']\n", "axes = g.plot(kind='hist',bins=np.arange(0.5,7.5,0.5),alpha=0.5,align='left')\n", - "for i, (groupname, group) in enumerate(g):\n", - " axes[i].set_label(groupname)\n", + "# axes is keyed by group name; iterate key,value\n", + "for groupname, ax in axes.items():\n", + " ax.set_label(groupname)\n", "plt.legend()\n", "plt.xlabel('Interatomic Distance ($\\AA$)')\n", "plt.title('Distance Plot')" From d51d91e06d42277d92116d23f8134bd6fe777acb Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 13:33:11 -0500 Subject: [PATCH 17/31] Modernize deploy.yml and pyproject.toml --- .github/workflows/deploy.yml | 41 ++++++++++++++++++------------------ pyproject.toml | 31 +++++++++++++++++++++------ 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 991cc15..e33b289 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,29 +4,30 @@ name: PyPi Release on: - push: - pull_request: - workflow_dispatch: + push: + pull_request: + workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: "3.10" - - - name: Install dependencies - run: >- - python -m pip install --user --upgrade setuptools wheel - - name: Build - run: >- - python setup.py sdist bdist_wheel - - name: Publish distribution 📦 to PyPI - if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release' - uses: pypa/gh-action-pypi-publish@master - with: - user: __token__ - password: ${{ secrets.pypi_password }} + - uses: actions/checkout@v5 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install build dependencies + run: python -m pip install --upgrade build + + - name: Build package + run: python -m build + + - name: Publish distribution to PyPI + if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release' + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.pypi_password }} diff --git a/pyproject.toml b/pyproject.toml index a681997..c88c9f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,26 +7,45 @@ name = "architector" version = "0.1.0" description = "The architector python package - for 3D Coordination Complex Design." readme = "README.md" -license = {text = "BSD 3-Clause License"} -authors = [ - {name = "Michael G. Taylor et al."} +license = { file = "LICENSE.txt" } +authors = [{ name = "Michael G. Taylor et al." }] +keywords = [ + "chemistry", + "computational-chemistry", + "coordination-complexes", + "inorganic-chemistry", + "molecular-design", ] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Topic :: Scientific/Engineering :: Chemistry" + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering :: Chemistry", ] +requires-python = ">=3.10" dependencies = [ "ase", + "mendeleev", "numpy", + "pandas", "py3Dmol", "pynauty", "scipy", - "pandas", - "mendeleev" ] +[project.optional-dependencies] +mace = ["mace-torch", "torch-dftd"] + +[project.urls] +Homepage = "https://github.com/lanl/Architector" +Repository = "https://github.com/lanl/Architector" +Documentation = "https://lanl.github.io/Architector/" + [tool.setuptools] packages = ["architector"] From fbf3fe53db656067d1c72b9429810f234ca22e85 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 13:41:57 -0500 Subject: [PATCH 18/31] Minimize conda deps in test.yml, install package deps via pip --- .github/workflows/test.yml | 13 ++----------- pyproject.toml | 2 ++ 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 15ba68f..a347ec2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,18 +24,9 @@ jobs: environment-name: test-env create-args: >- python=3.12 - ase - numpy + xtb>6.5 openbabel - scipy - numba - py3Dmol - pandas crest - xtb>6.5 - pynauty - tqdm - mendeleev tblite-python init-shell: bash @@ -46,7 +37,7 @@ jobs: - name: Install package run: | - uv pip install --no-deps -e . + uv pip install -e . - name: Run tests run: | diff --git a/pyproject.toml b/pyproject.toml index c88c9f5..efe556f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,11 +31,13 @@ requires-python = ">=3.10" dependencies = [ "ase", "mendeleev", + "numba", "numpy", "pandas", "py3Dmol", "pynauty", "scipy", + "tqdm", ] [project.optional-dependencies] From f85fbb3e5b646679f635cb435c607bc3f4bbdc54 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 13:43:39 -0500 Subject: [PATCH 19/31] Minimize conda deps in test-tutorials.yml, add optional dependency groups --- .github/workflows/test-tutorials.yml | 18 +++--------------- pyproject.toml | 4 +++- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index bd3264a..6c69a57 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -64,21 +64,10 @@ jobs: environment-name: tutorial-env create-args: >- python=3.12 - ase - numpy + xtb>6.5 openbabel - scipy - numba - py3Dmol - pandas crest - pynauty - xtb>6.5 - tqdm - mendeleev tblite-python - jupytext - matplotlib pytorch init-shell: bash @@ -87,10 +76,9 @@ jobs: with: version: "latest" - - name: Install package and mace-torch + - name: Install package with optional dependencies run: | - uv pip install --no-deps -e . - uv pip install mace-torch torch-dftd + uv pip install -e ".[mace,dftd,tutorials]" - name: Convert notebook to Python script run: | diff --git a/pyproject.toml b/pyproject.toml index efe556f..7935f3a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,9 @@ dependencies = [ ] [project.optional-dependencies] -mace = ["mace-torch", "torch-dftd"] +mace = ["mace-torch"] +dftd = ["torch-dftd"] +tutorials = ["jupytext", "matplotlib"] [project.urls] Homepage = "https://github.com/lanl/Architector" From 83360a29e6e62518340267bbe59f3c7ea9474117 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 13:45:22 -0500 Subject: [PATCH 20/31] Remove unnecessary pytorch and tblite-python from tutorial conda deps --- .github/workflows/test-tutorials.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index 6c69a57..4ce43d4 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -67,8 +67,6 @@ jobs: xtb>6.5 openbabel crest - tblite-python - pytorch init-shell: bash - name: Install uv From d7950c89fe5b8c31ff849a175991b80d296b6121 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 13:46:51 -0500 Subject: [PATCH 21/31] Move tblite to pyproject.toml optional dependency --- .github/workflows/test.yml | 3 +-- pyproject.toml | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a347ec2..de2129c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,6 @@ jobs: xtb>6.5 openbabel crest - tblite-python init-shell: bash - name: Install uv @@ -37,7 +36,7 @@ jobs: - name: Install package run: | - uv pip install -e . + uv pip install -e ".[tblite]" - name: Run tests run: | diff --git a/pyproject.toml b/pyproject.toml index 7935f3a..ffce340 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,8 +41,9 @@ dependencies = [ ] [project.optional-dependencies] -mace = ["mace-torch"] dftd = ["torch-dftd"] +mace = ["mace-torch"] +tblite = ["tblite"] tutorials = ["jupytext", "matplotlib"] [project.urls] From a76e92905e8b9313175612e11e78e6f4e42685b7 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 13:49:32 -0500 Subject: [PATCH 22/31] clean up dependency placement --- .github/workflows/test-tutorials.yml | 1 + .github/workflows/test.yml | 2 +- pyproject.toml | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index 4ce43d4..5b1b0c6 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -67,6 +67,7 @@ jobs: xtb>6.5 openbabel crest + jupytext init-shell: bash - name: Install uv diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index de2129c..82ed15f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,7 +36,7 @@ jobs: - name: Install package run: | - uv pip install -e ".[tblite]" + uv pip install -e . - name: Run tests run: | diff --git a/pyproject.toml b/pyproject.toml index ffce340..7e93864 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,13 +38,13 @@ dependencies = [ "pynauty", "scipy", "tqdm", + "tblite", ] [project.optional-dependencies] dftd = ["torch-dftd"] mace = ["mace-torch"] -tblite = ["tblite"] -tutorials = ["jupytext", "matplotlib"] +tutorials = ["matplotlib"] [project.urls] Homepage = "https://github.com/lanl/Architector" From da5ac6a42f4810dbdc88d9d632a2c83fde1d6c71 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 13:51:24 -0500 Subject: [PATCH 23/31] swap tblite dep back --- .github/workflows/test-tutorials.yml | 1 + .github/workflows/test.yml | 1 + pyproject.toml | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index 5b1b0c6..eb94106 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -68,6 +68,7 @@ jobs: openbabel crest jupytext + tblite-python init-shell: bash - name: Install uv diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 82ed15f..a347ec2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,6 +27,7 @@ jobs: xtb>6.5 openbabel crest + tblite-python init-shell: bash - name: Install uv diff --git a/pyproject.toml b/pyproject.toml index 7e93864..ce02c54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,6 @@ dependencies = [ "pynauty", "scipy", "tqdm", - "tblite", ] [project.optional-dependencies] From 8b074b0fec435566622b2002c873b53c5bb20ede Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 13:59:41 -0500 Subject: [PATCH 24/31] Revert test-tutorials.yml to working state from fbf3fe5 --- .github/workflows/test-tutorials.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index eb94106..bd3264a 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -64,11 +64,22 @@ jobs: environment-name: tutorial-env create-args: >- python=3.12 - xtb>6.5 + ase + numpy openbabel + scipy + numba + py3Dmol + pandas crest - jupytext + pynauty + xtb>6.5 + tqdm + mendeleev tblite-python + jupytext + matplotlib + pytorch init-shell: bash - name: Install uv @@ -76,9 +87,10 @@ jobs: with: version: "latest" - - name: Install package with optional dependencies + - name: Install package and mace-torch run: | - uv pip install -e ".[mace,dftd,tutorials]" + uv pip install --no-deps -e . + uv pip install mace-torch torch-dftd - name: Convert notebook to Python script run: | From 537c65687cec652b515ec5bbc7faec6f32e0c634 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 14:01:19 -0500 Subject: [PATCH 25/31] Minimize conda deps, install pyproject.toml deps via pip Keep in conda (required): - openbabel (C++ bindings) - crest (CLI) - xtb>6.5 (CLI) - tblite-python (Fortran) - jupytext (needed before pip install) Install via pip from pyproject.toml: - Core deps: ase, numpy, scipy, etc. - Optional: mace, dftd, tutorials --- .github/workflows/test-tutorials.yml | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index bd3264a..e96eedd 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -64,22 +64,11 @@ jobs: environment-name: tutorial-env create-args: >- python=3.12 - ase - numpy openbabel - scipy - numba - py3Dmol - pandas crest - pynauty xtb>6.5 - tqdm - mendeleev tblite-python jupytext - matplotlib - pytorch init-shell: bash - name: Install uv @@ -87,10 +76,9 @@ jobs: with: version: "latest" - - name: Install package and mace-torch + - name: Install package with optional dependencies run: | - uv pip install --no-deps -e . - uv pip install mace-torch torch-dftd + uv pip install -e ".[mace,dftd,tutorials]" - name: Convert notebook to Python script run: | From 1d6eb434bd05fb39a7778aada44a964bd5ebe6a3 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 14:03:58 -0500 Subject: [PATCH 26/31] Revert "Minimize conda deps, install pyproject.toml deps via pip" This reverts commit 537c65687cec652b515ec5bbc7faec6f32e0c634. --- .github/workflows/test-tutorials.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index e96eedd..bd3264a 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -64,11 +64,22 @@ jobs: environment-name: tutorial-env create-args: >- python=3.12 + ase + numpy openbabel + scipy + numba + py3Dmol + pandas crest + pynauty xtb>6.5 + tqdm + mendeleev tblite-python jupytext + matplotlib + pytorch init-shell: bash - name: Install uv @@ -76,9 +87,10 @@ jobs: with: version: "latest" - - name: Install package with optional dependencies + - name: Install package and mace-torch run: | - uv pip install -e ".[mace,dftd,tutorials]" + uv pip install --no-deps -e . + uv pip install mace-torch torch-dftd - name: Convert notebook to Python script run: | From beb459ac9a86a010a1aabe9be908b5a34c775312 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 14:08:25 -0500 Subject: [PATCH 27/31] Minimize conda deps, add ipython to tutorials optional deps The conda py3Dmol package depends on ipython, but pip py3Dmol doesn't. Adding ipython to [tutorials] optional deps fixes the headless CI issue. Conda (required): - openbabel (C++ bindings) - crest (CLI) - xtb>6.5 (CLI) - tblite-python (Fortran) - jupytext (needed before pip install) Pip via pyproject.toml: - Core deps + [mace,dftd,tutorials] --- .github/workflows/test-tutorials.yml | 16 ++-------------- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test-tutorials.yml b/.github/workflows/test-tutorials.yml index bd3264a..e96eedd 100644 --- a/.github/workflows/test-tutorials.yml +++ b/.github/workflows/test-tutorials.yml @@ -64,22 +64,11 @@ jobs: environment-name: tutorial-env create-args: >- python=3.12 - ase - numpy openbabel - scipy - numba - py3Dmol - pandas crest - pynauty xtb>6.5 - tqdm - mendeleev tblite-python jupytext - matplotlib - pytorch init-shell: bash - name: Install uv @@ -87,10 +76,9 @@ jobs: with: version: "latest" - - name: Install package and mace-torch + - name: Install package with optional dependencies run: | - uv pip install --no-deps -e . - uv pip install mace-torch torch-dftd + uv pip install -e ".[mace,dftd,tutorials]" - name: Convert notebook to Python script run: | diff --git a/pyproject.toml b/pyproject.toml index ce02c54..850a26c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,7 @@ dependencies = [ [project.optional-dependencies] dftd = ["torch-dftd"] mace = ["mace-torch"] -tutorials = ["matplotlib"] +tutorials = ["ipython", "matplotlib"] [project.urls] Homepage = "https://github.com/lanl/Architector" From 80610ad6a21aff42a58e0af836d450caa2255ced Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 14:10:01 -0500 Subject: [PATCH 28/31] Test Python 3.11, 3.12, and 3.13 in matrix --- .github/workflows/test.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a347ec2..98fbb92 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,6 +9,10 @@ on: jobs: test: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ['3.11', '3.12', '3.13'] defaults: run: shell: bash -el {0} @@ -23,7 +27,7 @@ jobs: micromamba-version: 'latest' environment-name: test-env create-args: >- - python=3.12 + python=${{ matrix.python-version }} xtb>6.5 openbabel crest From 8f4aca82dd1b623fcc93ccfad52b60e0d2b22613 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 14:16:33 -0500 Subject: [PATCH 29/31] Drop Python 3.13 - blocked by openbabel conda-forge availability --- .github/workflows/test.yml | 3 ++- pyproject.toml | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 98fbb92..e85dfb4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,8 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.11', '3.12', '3.13'] + # Python 3.13 blocked by openbabel not yet available on conda-forge + python-version: ['3.11', '3.12'] defaults: run: shell: bash -el {0} diff --git a/pyproject.toml b/pyproject.toml index 850a26c..950f768 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,12 +22,11 @@ classifiers = [ "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Topic :: Scientific/Engineering :: Chemistry", ] -requires-python = ">=3.10" +requires-python = ">=3.11" dependencies = [ "ase", "mendeleev", From c7e659ce92fee343006c922eb7057511ffaeec95 Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 14:20:03 -0500 Subject: [PATCH 30/31] Test 3.11 with lowest-direct resolution, 3.12 with highest --- .github/workflows/test.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e85dfb4..9c4b679 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,9 @@ jobs: fail-fast: false matrix: # Python 3.13 blocked by openbabel not yet available on conda-forge - python-version: ['3.11', '3.12'] + version: + - { python: '3.11', resolution: lowest-direct } + - { python: '3.12', resolution: highest } defaults: run: shell: bash -el {0} @@ -28,7 +30,7 @@ jobs: micromamba-version: 'latest' environment-name: test-env create-args: >- - python=${{ matrix.python-version }} + python=${{ matrix.version.python }} xtb>6.5 openbabel crest @@ -42,7 +44,7 @@ jobs: - name: Install package run: | - uv pip install -e . + uv pip install -e . --resolution=${{ matrix.version.resolution }} - name: Run tests run: | From dbd4c375c9664ce5ea5b4df9ab3c12abd984d88b Mon Sep 17 00:00:00 2001 From: orionarcher Date: Thu, 22 Jan 2026 14:23:21 -0500 Subject: [PATCH 31/31] Simplify test matrix: Python 3.11 and 3.12 with default resolution --- .github/workflows/test.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9c4b679..e85dfb4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,9 +13,7 @@ jobs: fail-fast: false matrix: # Python 3.13 blocked by openbabel not yet available on conda-forge - version: - - { python: '3.11', resolution: lowest-direct } - - { python: '3.12', resolution: highest } + python-version: ['3.11', '3.12'] defaults: run: shell: bash -el {0} @@ -30,7 +28,7 @@ jobs: micromamba-version: 'latest' environment-name: test-env create-args: >- - python=${{ matrix.version.python }} + python=${{ matrix.python-version }} xtb>6.5 openbabel crest @@ -44,7 +42,7 @@ jobs: - name: Install package run: | - uv pip install -e . --resolution=${{ matrix.version.resolution }} + uv pip install -e . - name: Run tests run: |