Deprecate top-level cutoff, always deduce it from bonds::rcut - #104
Conversation
The top-level `cutoff` of the input YAML has never reached the fit: it was read in `bin/pacemaker` and required to be present, but was never passed to `GeneralACEFit`, so the `cutoff is None` branch always applied and the neighbour lists were always built with max(bonds::rcut). The last consumer of the value was the `--query-data` path removed in 29ba519. This left a mandatory parameter whose value was silently discarded: users could set it inconsistently with `bonds::rcut` and never learn about it, while omitting it aborted the run with a ValueError. Rather than start honouring the value - which would make an under-sized `cutoff` silently truncate the training neighbour lists, and an over-sized one waste memory for identically zero contributions - the parameter is dropped. Deducing the cutoff from max(bonds::rcut) makes the fitting neighbour lists agree with `ACEBBasisSet.cutoffmax`, which the ASE and LAMMPS calculators use, so the two cannot drift apart. - bin/pacemaker: `cutoff` is no longer required; a deprecation warning is logged if it is still present, so existing input files keep working. - generalfit.py: extract `get_maximal_rcut()` and log the resolved cutoff, so the value actually used is always visible in log.txt. The `cutoff` argument of `GeneralACEFit` is kept as a programmatic escape hatch and documented. - input_template.yaml / `pacemaker -t`: the dialog set both `cutoff` and `bonds::rcut` from one answer; it now asks for `rcut` only and the generated file has no top-level `cutoff`. - examples and CLI tests: drop the redundant `cutoff` line. All eight files had `cutoff == max(bonds::rcut)` exactly, so the deduced value is unchanged. - docs: describe the deduction in inputfile.md, quickstart.md and faq.md. The inputfile.md example was itself inconsistent (`cutoff: 10.0` against rcut 5/5.5). - tests: cover `get_maximal_rcut`, including that it agrees with `ACEBBasisSet.cutoffmax` for per-bond cutoffs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017QbfG2UVKxkeuiz9GtDLo4
|
CI is red on both Both jobs abort during collection, before a single test runs: This is pre-existing. The Root cause, for whoever picks it up: The smallest CI-side fix is an explicit step in - name: Install maxvolpy
run: |
pip install Cython
pip install ./lib/maxvolpyI have deliberately left this out of this PR rather than widen it — happy to open it as a separate one if that is useful. What this means for reviewing this PR: CI currently gives no signal on it either way. Because pytest exits during collection, the new Generated by Claude Code |
Both CI jobs aborted during pytest collection with
pyace/activelearning.py:9: from maxvolpy.maxvol import maxvol
E ModuleNotFoundError: No module named 'maxvolpy'
Interrupted: 2 errors during collection
so no test ran at all. This is pre-existing and unrelated to any particular
change - run 31264271622 (17 Aug, PR #103) fails identically.
`maxvolpy` is installed by the custom InstallMaxVolPyLocalPackage(install)
command in setup.py, which shells out to `cd lib/maxvolpy; python setup.py
install`. The workflow installs with `pip install .`, which builds a wheel and
never invokes the legacy `install` command, so that hook never runs.
(`python setup.py install` is also gone in setuptools >= 80.)
Build it explicitly instead. `--no-build-isolation` is required because
lib/maxvolpy/setup.py imports Cython and numpy at module level while declaring
no pyproject.toml build-system, so an isolated build environment fails with
ModuleNotFoundError: No module named 'Cython'. Cython and scipy are installed
first: the extension cimports scipy/linalg/cython_blas.pxd. numpy is
deliberately not installed here, to avoid resolving past setup.py's
`numpy<=1.26.4` pin; it is already present from the python-ace install.
Verified locally in a clean venv: with build isolation the build fails on the
missing Cython, without scipy it fails cythonizing _maxvol.pyx, and with the
command used here it builds a wheel and `from maxvolpy.maxvol import maxvol`
succeeds.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017QbfG2UVKxkeuiz9GtDLo4
|
Update after
The maxvolpy problem from my earlier comment is resolved, and the changes in this PR are exercised: The single remaining failure is unrelated to this PR and I have not fixed it here:
Proposed patch, if you would like it in a separate PR: try:
from ase.filters import UnitCellFilter
except ImportError: # ASE < 3.23
from ase.constraints import UnitCellFilterI have kept it out of this branch so the cutoff change stays reviewable on its own; the same fix would also unblock #100, #101, #102 and #103, which all hit the maxvolpy wall and would hit this one next. Happy to open it separately, or to fold it in here if you would rather see this PR fully green — just say which. Generated by Claude Code |
…nstraints
test_relaxation failed on the Python 3.10 job with
ImportError: cannot import name 'UnitCellFilter' from 'ase.constraints'
ASE moved UnitCellFilter to ase.filters in 3.23 and has since removed the
alias from ase.constraints. `ase` is unpinned in requirements.txt, so the 3.10
job resolves a recent ASE without it, while the 3.9 job - which recent ASE no
longer supports - still gets a version that has it. That is why only one of
the two jobs failed.
Try the new location first and fall back to the old one, so both ASE
generations keep working. Verified against ase 3.29: ase.filters.UnitCellFilter
resolves and importing it from ase.constraints raises exactly the error above.
This failure is unrelated to the cutoff change in this branch; it only became
visible once the maxvolpy fix in 3bfa4df let the suite be collected at all.
It is the sole remaining failure - the run before this commit reported
289 passed, 1 failed on 3.10 and a fully green 3.9.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017QbfG2UVKxkeuiz9GtDLo4
Pull Request Template
Description of Changes
The top-level
cutoffof the input YAML has never reached the fit. It is read inbin/pacemaker:161and required to be present, but it is never passed toGeneralACEFit, so thecutoff is Nonebranch always applies and the neighbour lists are always built withmax(bonds::rcut). The last consumer of the value was the--query-datapath removed in 29ba519 (Nov 2023); checking every commit that ever touchedbin/pacemaker, theGeneralACEFit(...)call has never carried acutoffargument.That leaves a mandatory parameter whose value is silently discarded: a user can set it inconsistently with
bonds::rcutand never find out, while omitting it aborts the run with aValueError.Rather than start honouring the value — which would make an under-sized
cutoffsilently truncate the training neighbour lists, and an over-sized one waste memory on neighbours that contribute exactly zero — this PR drops the parameter. Deducing the cutoff frommax(bonds::rcut)makes the fitting neighbour lists agree withACEBBasisSet.cutoffmax, which the ASE and LAMMPS calculators use, so the two cannot drift apart.bin/pacemaker—cutoffis no longer required; a deprecation warning is logged if it is still present, so existing input files keep working unchanged.generalfit.py— extractget_maximal_rcut()and log the resolved cutoff, so the value actually used is always visible inlog.txt. Thecutoffargument ofGeneralACEFitis kept as a programmatic escape hatch and documented.input_template.yaml/pacemaker -t— the dialog set bothcutoffandbonds::rcutfrom a single answer; it now asks forrcutonly, and the generated file has no top-levelcutoff.cutoffline. All eight files hadcutoff == max(bonds::rcut)exactly, so the deduced value is unchanged in every case.inputfile.md,quickstart.mdandfaq.md. Theinputfile.mdexample was itself inconsistent (cutoff: 10.0against rcut 5 / 5.5).tests/test_cutoff_deduction.pycoveringget_maximal_rcut, including that it agrees withACEBBasisSet.cutoffmaxfor per-bond cutoffs.Runtime behaviour is unchanged for every existing input file: the deduced cutoff is exactly what was already being used.
Two CI fixes carried along
CI on this repository was red before this branch, and in a way that hid all test results, so two unrelated fixes are included to make the change verifiable. Both are self-contained and can be split out if preferred.
3bfa4df— installlib/maxvolpyin the workflow. Both jobs aborted during pytest collection withModuleNotFoundError: No module named 'maxvolpy', so no test ran at all — pre-existing and identical on run 31264271622 (17 Aug, #103).maxvolpyis installed by the customInstallMaxVolPyLocalPackage(install)hook insetup.py, whichpip install .never invokes because it builds a wheel. The workflow now builds it explicitly; the commit message records why--no-build-isolationandscipyare each required and whynumpyis deliberately not installed there.d63466f—UnitCellFilterimport intest_PyACECalculator.py. ASE moved it toase.filtersin 3.23 and has since dropped thease.constraintsalias.aseis unpinned, so the 3.10 job resolved a recent ASE without it while the 3.9 job — which recent ASE no longer supports — still got one that has it; that is why only one job failed. The test now tries the new location and falls back to the old, so both ASE generations work.These two also unblock #100, #101, #102 and #103, which hit the same walls.
Checklist
build (3.9)andbuild (3.10)both green, each covering the full pytest suite and thepacemakerCLI integration tests, which exercise the modifiedinput.yamlfiles end to end.License Agreement
By submitting this pull request, I agree that:
🤖 Generated with Claude Code
https://claude.ai/code/session_017QbfG2UVKxkeuiz9GtDLo4