Skip to content

Commit f366916

Browse files
Deprecate top-level cutoff, always deduce it from bonds::rcut
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
1 parent b143ac3 commit f366916

15 files changed

Lines changed: 107 additions & 27 deletions

File tree

‎bin/pacemaker‎

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,9 @@ def main(args):
159159

160160
assert isinstance(args_yaml, dict)
161161
if "cutoff" in args_yaml:
162-
cutoff = args_yaml["cutoff"]
163-
else:
164-
log.warning("No 'cutoff' provided in YAML file, please specify it")
165-
raise ValueError("No 'cutoff' provided in YAML file, please specify it")
162+
log.warning("Top-level 'cutoff' is deprecated and is ignored. "
163+
"The cutoff for the neighbour list construction is deduced from the potential "
164+
"as the maximum over `potential::bonds::rcut`.")
166165

167166
if "seed" in args_yaml:
168167
seed = args_yaml["seed"]
@@ -325,8 +324,8 @@ def generate_template_input():
325324
"""Enter number of functions per element ([ENTER] - default 700): """) or 700)
326325
print("Number of functions per element: ", number_of_functions_per_element)
327326

328-
cutoff = float(input("Enter cutoff (Angstrom, default:7.0): ") or 7.0)
329-
print("Cutoff: ", cutoff)
327+
rcut = float(input("Enter outer cutoff `rcut` (Angstrom, default:7.0): ") or 7.0)
328+
print("Outer cutoff rcut: ", rcut)
330329

331330
# weighting scheme
332331
default_energy_based_weighting = """{ type: EnergyBasedWeightingPolicy, DElow: 1.0, DEup: 10.0, DFup: 50.0, DE: 1.0, DF: 1.0, wlow: 0.75, energy: convex_hull, reftype: all,seed: 42}"""
@@ -349,7 +348,7 @@ def generate_template_input():
349348
input_yaml_text = f.read()
350349

351350
input_yaml_text = input_yaml_text.replace("{{ELEMENTS}}", str(elements))
352-
input_yaml_text = input_yaml_text.replace("{{CUTOFF}}", str(cutoff))
351+
input_yaml_text = input_yaml_text.replace("{{RCUT}}", str(rcut))
353352
input_yaml_text = input_yaml_text.replace("{{DATAFILENAME}}", train_filename)
354353
input_yaml_text = input_yaml_text.replace("{{number_of_functions_per_element}}",
355354
"number_of_functions_per_element: {}".format(

‎docs/pacemaker/faq.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ data:
163163
## I want to change the cutoff, what should I do ?
164164
165165
If you change cutoff, i.e. from `rcut: 7` to `rcut: 6.5`, then potential should be refitted from the scratch.
166-
`pacemaker` will recompute neighbourlists on every run, so, no need to extra options except for specifying cutoff.
166+
Change `potential::bonds::rcut` only - the cutoff for the neighbour lists is deduced from it automatically.
167+
`pacemaker` recomputes the neighbour lists on every run, so no extra options are needed.
167168

168169

169170

‎docs/pacemaker/inputfile.md‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ particular settings of `pacemaker`. The sections are listed below.
55

66
## Cutoff and (optional) metadata
77

8-
* Global cutoff for the neighborlist constructor is setup as:
8+
* The cutoff for the neighbour list construction is **not** specified separately. It is deduced from the potential
9+
as the maximum of the bonds' outer cutoffs `potential::bonds::rcut` (see the `potential` section below),
10+
which is exactly the cutoff that the ACE calculators (ASE, LAMMPS) use. The atomic environments seen during
11+
fitting are therefore identical to those seen in production, and changing `rcut` is enough - nothing else
12+
has to be kept in sync.
913

10-
```YAML
11-
cutoff: 10.0
12-
```
14+
A top-level `cutoff` key is deprecated: it is ignored and only produces a warning.
1315

1416
* Metadata (optional)
1517

‎docs/pacemaker/quickstart.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ In order to interactively generate default `pacemaker` input file `input.yaml`,
3434
```
3535
pacemaker -t
3636
```
37-
and enter requested information, such as dataset filename, test set size (optional), list of elements, cutoff,
38-
number of functions. Doing so will produce an `input.yaml` file with the most general
37+
and enter requested information, such as dataset filename, test set size (optional), list of elements,
38+
outer cutoff `rcut`, number of functions. Doing so will produce an `input.yaml` file with the most general
3939
settings that can be adjusted for a particular task. Detailed overview of the input file parameters can be found in the [Input file](inputfile.md) section below.
4040

4141
## (optional) Manual fitting dataset preparation

‎examples/Cu-I/input.yaml‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
cutoff: 7 # cutoff for neighbour list construction
21
seed: 42 # random seed
32

43

‎examples/Cu-II/input.yaml‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
cutoff: 7.5 # cutoff for neighbour list construction
21
seed: 42 # random seed
32

43

‎examples/Ethanol/input.yaml‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
cutoff: 4 # cutoff for neighbour list construction
21
seed: 42 # random seed
32

43

‎examples/HEA/input.yaml‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
cutoff: 7 # cutoff for neighbour list construction
21
seed: 42 # random seed
32

43

‎src/pyace/data/input_template.yaml‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
cutoff: {{CUTOFF}} # cutoff for neighbour list construction
21
seed: 42 # random seed
32

43
#################################################################
@@ -25,7 +24,7 @@ potential:
2524
ALL: {
2625
radbase: SBessel,
2726
radparameters: [ 5.25 ],
28-
rcut: {{CUTOFF}},
27+
rcut: {{RCUT}},
2928
dcut: 0.01,
3029
NameOfCutoffFunction: cos,
3130
}

‎src/pyace/generalfit.py‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,19 @@ def get_initial_potential(start_potential):
113113
return initial_bbasisconfig
114114

115115

116+
def get_maximal_rcut(bbasisconfig: BBasisConfiguration) -> float:
117+
"""
118+
Maximal outer cutoff `rcut` over all bond (i.e. one- and two-species) blocks.
119+
120+
This is the same quantity as `ACEBBasisSet.cutoffmax`, which the ACE calculators
121+
(ASE, LAMMPS) use to construct their neighbour lists. Deducing the fitting cutoff
122+
from it guarantees that the atomic environments seen during the fit are identical
123+
to those seen in production.
124+
"""
125+
return max(block.rcutij for block in bbasisconfig.funcspecs_blocks
126+
if len(block.elements_vec) <= 2)
127+
128+
116129
def save_dataset(dataframe, fname):
117130
# columns to save: w_energy, w_forces, NUMBER_OF_ATOMS, PROTOTYPE_NAME, prop_id,structure_id, gen_id, if any
118131
# columns_to_save = ["PROTOTYPE_NAME", "NUMBER_OF_ATOMS", "prop_id", "structure_id", "gen_id", "pbc"] + \
@@ -143,6 +156,11 @@ class GeneralACEFit:
143156
:param data_config: training data specification
144157
:param backend_config: specification of potential evaluation and fitting backend (pyace / tensorpot)
145158
- dict ['evaluator']
159+
:param cutoff: cutoff for the neighbour lists construction. If None (default), it is deduced from
160+
`potential_config` as the maximum over the bonds' outer cutoffs `rcut`, which is what
161+
the ACE calculators use in production. Provide it explicitly only if the neighbour
162+
lists have to extend beyond the potential's own cutoff; note that neighbours
163+
further than the corresponding bond's `rcut` contribute exactly zero to the fit.
146164
"""
147165

148166
def __init__(self,
@@ -265,11 +283,13 @@ def __init__(self,
265283
log.info("Ladder-scheme fitting is ON")
266284

267285
if cutoff is None:
268-
rcut = max(
269-
[block.rcutij for block in self.target_bbasisconfig.funcspecs_blocks if len(block.elements_vec) <= 2])
270-
self.cutoff = rcut
286+
self.cutoff = get_maximal_rcut(self.target_bbasisconfig)
287+
log.info("Cutoff for neighbour lists construction is deduced from the potential: "
288+
"{:.3f} A (maximum over bonds::rcut)".format(self.cutoff))
271289
else:
272290
self.cutoff = cutoff
291+
log.info("Cutoff for neighbour lists construction is provided explicitly: "
292+
"{:.3f} A".format(self.cutoff))
273293

274294
if self.ladder_scheme:
275295
if FIT_LADDER_TYPE_KW in fit_config:

0 commit comments

Comments
 (0)