# eOn 3.2.0 An audit release. Of the 331 commits since `v3.1.0`, 170 are fixes to the client and server IO paths, the potential backends, and the Python bindings. Three changes need a decision from anyone upgrading; the rest are repairs. See monorepo `CHANGELOG.md` section 3.2.0 for the full fragment list. ## Compatibility: eOn writes CON spec 3 The C++ wrap and the Python `readcon` pin both move to **0.14.5**, and the 0.14 writer emits `con_spec_version = 3`. A reader linked against readcon 0.13.x rejects those files with `UnsupportedSpecVersion`. What this means in practice: - A `.con` written by eOn 3.2.0 is not readable by an eOn 3.1.x client, nor by a downstream tool pinned to `readcon>=0.13,<0.14`. - Pin downstream consumers to `readcon>=0.14.5` in the same change that moves them to eOn 3.2.0. `chemparseplot` and anything reading trajectory output through it are the ones to check. - Reading older spec-2 files is unaffected; the change is on the write side. ## Constraints are stored per Cartesian axis `Matter` holds the CON column-4 bitmask as an `N x 3` matrix rather than one flag per atom, and `getFree()` derives the optimizer mask per axis instead of broadcasting. A slab or reaction coordinate fixed along one axis keeps the two degrees of freedom it declared free. This depends on readcon 0.14.5: the 0.13 decoder read column-4 value 1 as fully fixed, so an x-only constraint could not survive a write and read (readcon-core #24, fixed in #25). eOn 3.2.0 round-trips every mask value. `getFixed(atom)` keeps its whole-atom meaning and now answers 1 only when all three axes are fixed. Code branching on it for a partially constrained atom sees a different answer than it did in 3.1.x. ## Movie writers no longer grow quadratically Appending a frame to a `.con` serializes that frame and concatenates it instead of reading and rewriting the whole file. An N-frame trajectory costs N frame writes rather than N(N+1)/2. `dynamics.con`, `movie.con` and the basin hopping trial movies are the visible beneficiaries. Output bytes are unchanged. Gzip and zstd targets keep the rewrite path, because a compressed member cannot be extended in place. ## External-program potentials check their IO `AMS`, `AMS_IO`, `ExtPot` and `VASP` now check every file open, read and shell command. A missing, truncated or stale result file used to leave the force and energy arrays holding whatever they held before, and those values went into the optimizer unremarked. Two behavioural corrections in the same area: - `ExtPot` and `VASP` no longer declare themselves safe to call from several threads on one instance. Both exchange structures through files at fixed names, so parallel NEB images were overwriting each other's input and reading each other's forces. `ExtPot` asks for an instance per image and stays parallel; `VASP` drives one process through one set of files, so its image evaluation is now sequential. - The `VASP` constructor no longer deletes the working directory's contents. It removed `WAVECAR`, `CHGCAR` and `TMPCAR` among others, which is precisely what an `ISTART`/`ICHARG` restart reads, and it never removed the `STOPCAR` that aborts the next run. ## Batched force evaluation `RgpotAdapter` reports `PotCaps::batched` through `supportsBatchEvaluation()` and forwards a batch to rgpot's `forceBatchImpl`, so a kernel that evaluates many systems in one pass gets the chance to. Kernels that do not override it take a per-system loop, so every existing potential answers a batch call correctly. Requires rgpot carrying `ForceBatch`. ## Tests run on Unix CI `tests/` runs after the client install on Linux and macOS. It had not run in CI before, which is why several of the fixes in this release are for defects that existed for some time: `test_one_pt` died at collection on a shell builtin, and `min_mode_method = gprdimer` in a build without `-Dwith_gprd` silently fell through to ImprovedDimer and reported a nonnegative-mode abort rather than refusing. ## Upgrade checklist 1. Move `readcon` to `>=0.14.5` wherever a downstream tool reads eOn output. 2. Re-check any code branching on `Matter::getFixed(atom)` for partially constrained atoms. 3. If you drive `ExtPot` from parallel NEB, note that each image now runs in `extpot__`; a wrapper reaching for the client's working directory should read `EON_EXTPOT_RUN_DIR`.