Skip to content

EQUIL - BUG FIX - Initialize sol_run 4th profile column (cross-run auto-mpsi corruption) - #293

Merged
github-actions[bot] merged 2 commits into
developfrom
bugfix/equilibrium-pool-reentrancy
Jun 20, 2026
Merged

EQUIL - BUG FIX - Initialize sol_run 4th profile column (cross-run auto-mpsi corruption)#293
github-actions[bot] merged 2 commits into
developfrom
bugfix/equilibrium-pool-reentrancy

Conversation

@logan-nc

Copy link
Copy Markdown
Collaborator

Summary

Fixes a cross-run state corruption discovered while building the kinetic regression decks: calling main() repeatedly in one Julia process made the log_asymptotic auto-mpsi for the Solovev equilibrium produce wildly varying grids (mpsi 16 → 600–900, occasionally InexactError), only after a prior kinetic run in the same process. Fresh subprocesses (regression harness, CLI) are unaffected, so it was latent — but test/runtests_fullruns.jl calls main() 6× in one process.

Root cause

sol_run (AnalyticEquilibrium.jl) allocated the source-profile array sqfs = Array{Float64}(undef, ma+1, 4) but filled only columns 1–3, leaving column 4 uninitialized. In a fresh process that memory reads ≈0 (benign); after a kinetic run it holds stale data. The auto-mpsi curvature estimate (_estimate_mid_spacing) reads that 4th column, so the garbage spiked the estimated profile curvature → collapsed the mid-region knot spacing → inflated mpsi.

It is not a pool re-entrancy bug — the AdaptiveArrayPools task-local pool depth returns to 1 after every run (no @with_pool leak). This was plain use-of-uninitialized-memory.

Diagnosed by deterministic reproduction + bisection (a non-kinetic ideal full run does not corrupt; a kinetic full run does) and by instrumenting the per-profile second-derivative estimate (the spike was isolated to column 4).

Fixes

  • sol_run: allocate sqfs with zeros so column 4 is a deterministic 0.0, matching the benign fresh-process state the validated baselines were built on.
  • _estimate_mid_spacing: size the evaluation buffer to the actual profile count (size(sq_in.y, 2)) instead of a hardcoded 4, so it never reads beyond the real profiles — hardening the 3-profile (F, P, q) builders against the same class of bug. Mirrors InverseEquilibrium.jl, which already sizes this way.

Validation

  • Reproduction: kinetic-run-then-auto-mpsi ×4 in one process → before: mpsi 16, 934, 687, 632; after: stable 16 every iteration.
  • Full test suite (test/runtests.jl): green.
  • Regression harness (--refs develop,local): diiid_n1, solovev_n1, solovev_multi_n, solovev_kinetic_calculated all 0.0-diff (zero numerical impact — confirms column 4 is unused/zero-valued in the validated path).

🤖 Generated with Claude Code

…si buffer to actual profile count
Fixes a cross-run state corruption: calling main() repeatedly in one Julia
process made the log_asymptotic auto-mpsi for the Solovev equilibrium produce
wildly varying grids (mpsi 16 -> 600-900, occasionally InexactError), only after
a prior KINETIC run in the same process. Fresh subprocesses (harness/CLI) were
unaffected, so it was latent.
Root cause: `sol_run` (AnalyticEquilibrium.jl) allocated the source-profile array
`sqfs = Array{Float64}(undef, ma+1, 4)` but filled only columns 1-3, leaving
column 4 uninitialized. In a fresh process that memory reads ~0 (benign); after a
kinetic run it holds stale data. The auto-mpsi curvature estimate
(`_estimate_mid_spacing`) reads that 4th column, so the garbage spiked the
estimated profile curvature, collapsing the mid-region knot spacing and inflating
mpsi. Diagnosed by bisection (ideal full run does not corrupt; kinetic does) and
by instrumenting the per-profile second-derivative estimate (column 4 only).
Fixes:
- `sol_run`: allocate `sqfs` with `zeros` so column 4 is a deterministic 0.0,
matching the benign fresh-process state the validated baselines were built on.
- `_estimate_mid_spacing`: size the evaluation buffer to the actual profile count
(`size(sq_in.y, 2)`) instead of a hardcoded 4, so it never reads beyond the real
profiles — protecting the 3-profile (F, P, q) builders from the same class of
bug. Mirrors InverseEquilibrium.jl, which already sizes this way.
Not a pool re-entrancy bug: the AdaptiveArrayPools task-local pool depth returns
to 1 after every run (no @with_pool leak); this was use-of-uninitialized-memory.
Verified: deterministic repro (kinetic-then-auto x4) now stable at mpsi=16; full
test suite green; regression harness diiid_n1, solovev_n1, solovev_multi_n,
solovev_kinetic_calculated all 0.0-diff vs develop (zero numerical impact).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@logan-nc
logan-ncforce-pushed the bugfix/equilibrium-pool-reentrancy branch from 48454d4 to e8fdfedCompareJune 20, 2026 17:57
@logan-nclogan-nc self-assigned this Jun 20, 2026
@logan-nclogan-nc added bug Something isn't working auto-merge labels Jun 20, 2026
@github-actions
github-actionsBot enabled auto-merge June 20, 2026 18:03
@github-actions
github-actionsBot merged commit 76487b0 into developJun 20, 2026
4 checks passed
@github-actions
github-actionsBot deleted the bugfix/equilibrium-pool-reentrancy branch June 20, 2026 18:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@logan-nc