Skip to content

Hand every force iteration to a callback and watch a solve converge - #839

Open
CharlesCNorton wants to merge 2 commits into
proximafusion:mainfrom
CharlesCNorton:live-solve-view
Open

Hand every force iteration to a callback and watch a solve converge#839
CharlesCNorton wants to merge 2 commits into
proximafusion:mainfrom
CharlesCNorton:live-solve-view

Conversation

@CharlesCNorton

Copy link
Copy Markdown
Contributor

Change - vmecpp.run takes an iteration_callback, called once per force iteration from the master thread with an IterationSnapshot: the iteration counter and multigrid stage, ns, the three invariant residuals and the stage's ftol, the time step, the restart reason, the Jacobian-reset count, whether the vacuum pressure is in the force balance, the MHD energy, and the R, Z and lambda coefficients of the state as a Geometry. Returning False stops the run, which returns the state reached with ier_flag = 2, VMEC 8.52's more_iter_flag, added as VmecStatus::MORE_ITERATIONS_NEEDED. vmecpp.watch(input) sits on it: the same solve as run, drawn while it runs as the flux surfaces at a few toroidal angles above the residual trace, with the multigrid stages and the restarts marked, into a window, a .gif or video file, or both; closing the window stops the solve. On the C++ side run, Vmec::FromIndata and the constructor take an IterationCallback beside the interrupt callback, GatherSpectralStateFromThreads collects the spectral state and the flux profiles without the real-space fields and shares its per-surface copy with the full gather, and MakeGeometry turns that into the snapshot's geometry.

Cause - the loop offered no view of an iteration: the residual history after the fact and the printout every nstep. The geometry of an iteration was reachable only through the Python-owned single-thread loop, which does not run free-boundary cases.

Evidence - examples/data/solovev.json (every 10th iteration), the free-boundary cth_like_free_bdy (every 4th) and examples/data/w7x.json (every 20th), recorded with vmecpp.watch:

solovev

cth free boundary

W7-X

The snapshot is a copy of the spectral state and one Python call, about a millisecond per iteration: cth_like_fixed_bdy takes 0.57 s without and 0.67 s with a callback that stores every snapshot, cth_like_fixed_bdy_asym 1.34 s and 1.39 s. A drawn frame costs 60 ms.

Tests - IterationCallbackSeesEveryIterationAndCanStop in vmec_test: one snapshot per entry of the residual history with bit-identical residuals, one converged snapshot per multigrid stage whose geometry equals the output's, and a stop at iteration 20 that returns ns_array[0] with ier_flag 2. tests/test_watch.py: the same through Python on solovev and the symmetric and asymmetric cth cases, an exception raised in the callback propagating, the drawn cross-sections against vmecpp.geometry.evaluate on every surface to 1e-12, the frame count of a recorded animation, the stop when the window closes, and the refusal to run with neither a window nor a file. bazel test --config=opt //vmecpp/... and pytest pass.

Scope - without a callback nothing changes: no barrier is added to the loop and the outputs are the ones the existing tests compare. matplotlib is imported inside watch, so it stays an optional dependency; the README gains a paragraph and examples/watch_solve.py records or shows a solve from the command line.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

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