Skip to content

fix: projection convergence loop referenced previous time step's energy - #1254

Merged
syamada0 merged 2 commits into
develop-2.0.0from
fix/projection-convergence
Aug 28, 2026
Merged

syamada0 merged 2 commits into
develop-2.0.0from
fix/projection-convergence

Conversation

@MZKC

@MZKC MZKC commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

The projection routine re-converges the projection orbitals (Houston /
instantaneous eigenstates) at each output step with a CG loop, exiting when the
kinetic-energy change dE = energy%E_kin - rt%E_old drops below
threshold_projection. rt%E_old is a persistent (s_rt) value carried over
from the previous time step, and it was not reset to the current step's starting
E_kin before the loop
. The first iteration therefore measured dE against the
previous time step rather than against the current step's starting orbitals.

When E_kin after a single CG step happens to land within threshold_projection
of the previous step's value, the loop can exit after one iteration without having
converged the projection orbitals — a premature exit. This is a coincidence, so it
shows up intermittently and is more likely for large systems / long runs (matching
the report that the loop "sometimes" stops without converging).

Fix

Reset rt%E_old to the current step's E_kin before the loop, and update it
before the exit test so it holds the converged value for the next step's
"already converged" cross-step check. dE inside the loop is now the change
within this projection's own iterations.

Verification (Wisteria-O)

GS → RT with projection_option='gs', comparing the fixed binary against the
pre-fix (develop-2.0.0) binary:

  • cubic Si (8 atoms, 2x2x2 k) and SiO2 (18 atoms, 4x4x4 k,
    I = 1e14 W/cm^2):
    • RT dynamics are unchanged. Si_rt_energy.data / SiO2_rt_energy.data
      are identical (Eall bit-for-bit); the macroscopic current differs only at
      the 1e-19–1e-21 (denormal) level. The projection is a read-only analysis,
      and the fix does not alter the dynamics.
    • The projection converged in the same number of iterations for both
      binaries; the per-iteration dE for iter >= 2 is bit-for-bit identical, and
      only the first iteration's printed dE changes (reflecting the corrected
      reference). The premature exit is a rare coincidence that did not trigger in
      these short runs, so base and fix converged identically.

The change is therefore a safe correctness fix: it removes a rare premature exit
(the reported intermittent under-convergence) and is a no-op in the common case.

🤖 Generated with Claude Code

The projection routine re-converges the projection orbitals at each output step
with a CG loop, exiting when the kinetic-energy change dE = E_kin - rt%E_old
drops below threshold_projection. rt%E_old is a persistent (s_rt) value carried
over from the previous time step and was not reset to the current step's starting
E_kin before the loop. The first iteration's dE was therefore measured against
the previous time step rather than the current step's starting orbitals, so when
E_kin changes little between steps (large systems / small dt) the loop could exit
after a single CG iteration without having converged the projection orbitals.

Reset rt%E_old to the current step's E_kin before the loop, and update it before
the exit test so it holds the converged value for the next step's "already
converged" check. dE in the loop is now the change within this projection's own
iterations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jenkins-diana

Copy link
Copy Markdown

Can one of the admins verify this patch?

@MZKC MZKC left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no correctness issues found. Seeding rt%E_old = energy%E_kin before the projection CG loop is the right fix: the first iteration's dE is now measured within this step (against the freshly computed starting-orbital E_kin) instead of against the previous time step, so the loop no longer exits after a single CG step. Moving the in-loop rt%E_old = energy%E_kin ahead of the exit check is behavior-neutral.

@syamada0
syamada0 merged commit a72135b into develop-2.0.0 Aug 28, 2026
1 check passed
@syamada0
syamada0 deleted the fix/projection-convergence branch August 28, 2026 05:22
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.

3 participants