Skip to content

solid: estimate spring constants from the unrounded MSD - #287

Merged
srmnitc merged 1 commit into
mainfrom
spring-constant-msd
Nov 4, 2026
Merged

srmnitc merged 1 commit into
mainfrom
spring-constant-msd

Conversation

@srmnitc

@srmnitc srmnitc commented Nov 4, 2026

Copy link
Copy Markdown
Member

Problem

Solid.analyse_spring_constants rounded the mean MSD to 2 decimals. Below 0.005 Ų (low temperature, heavy or stiff elements) the MSD rounded to 0. The code then substituted 1 Ų, giving k = 3kT / 1 Ų. With an MSD of 0.004 Ų at 100 K, that makes k 0.026 eV/Ų instead of 6.46 eV/Ų. validate_spring_constants accepted the value because it lies inside [1e-4, 1e3], so the run went on with a badly matched Einstein crystal, large dissipation and a biased free energy. Only a warning was logged.

Two related problems:

  • The convergence check compared the rounded MSD standard deviation against tolerance.spring_constant. That standard deviation is almost always rounded to 0, so the loop stopped on the first cycle.
  • If the loop ran out of n_cycles, self.k was never set and the run crashed later.

Between 0.005 and 0.05 Ų, the rounding made k coarse (up to about ±50 %) but did not bias the free energy.

Fix

  • Use the unrounded MSD. An MSD that is zero or not finite gives k = nan for that element. validate_spring_constants replaces it with another element's k (the case of an element without atoms, which the old fallback was there for), or raises when no element has a usable value.
  • Converge on the relative change of k between cycles, compared against tolerance.spring_constant (default 0.1). k only needs to be approximate, so this normally stops at cycle 2.
  • The loop is still bounded by md.n_cycles. If that runs out, the last estimate is used and a warning is logged.

Workaround for affected versions: set spring_constants explicitly.

Behaviour change

  • With default settings, the spring constant loop now runs two cycles instead of one, so one extra n_small_steps run.
  • Spring constants differ slightly from earlier versions because the MSD is no longer rounded.
  • Single-element runs with a zero MSD now stop with an error instead of running with k = 3kT / 1 Ų.

Tests

  • tests/test_spring_constants.py: 7 tests covering a small MSD, no rounding, zero MSD for one or all elements, user-supplied k overriding the estimate, stopping after two cycles, and the n_cycles cap. All 7 fail on main.
  • Golden command streams are unchanged.
  • Full suite including the LAMMPS lane: 474 passed, 3 skipped.

The mean MSD was rounded to 2 decimals. Below 0.005 A^2 (low T, heavy or
stiff elements) it rounded to 0 and was replaced by 1 A^2, giving k = 3kT/1
A^2, orders of magnitude too soft (bcc W at 100 K: 0.026 instead of 6.46
eV/A^2). validate_spring_constants accepted it, so the run went on with a
biased free energy and only a warning.

- use the unrounded MSD; an MSD that is zero or not finite gives nan, which
  validate_spring_constants replaces by another element's k (elements without
  atoms) or rejects with an error when no element has a usable value
- converge on the relative change of k between cycles against
  tolerance.spring_constant; the old check compared the rounded MSD std and
  always passed on the first cycle
- if n_cycles runs out, use the last estimate with a warning instead of
  leaving self.k unset
@srmnitc
srmnitc merged commit 196ace6 into main Nov 4, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant