Skip to content

Analysis - FEATURE! - Plot from a result or a gpec.h5 path and list the available plots - #650

Open
matt-pharr wants to merge 4 commits into
developfrom
feature/analysis-result-plots
Open

matt-pharr wants to merge 4 commits into
developfrom
feature/analysis-result-plots

Conversation

@matt-pharr

@matt-pharr matt-pharr commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Release note

  • Audience: users
  • Numerical impact: none (harness @ bd8fe97)
  • Migration: A plot whose data the run did not produce now throws an ArgumentError naming what to change in the run, instead of drawing a "No … data" placeholder, returning nothing or warning (plot_cond_fbar, plot_gse_by_theta and plot_gse_integrated used to return nothing without their data). plot_delta_prime plots the Δ′ matrix of a Riccati or Galerkin solve, so it throws on Forward results. available_plots(r) shows beforehand what a result can draw.

Every Analysis plot now takes the result in memory as well as the path of the gpec.h5 it was saved to: plot_ffs_summary(ffs) works like plot_ffs_summary("gpec.h5"). available_plots(r) lists the plots for a result, a file, or the named tuple main returns, with what to change for each one the run cannot draw; a Riccati run, for example, offers no solution-profile plot and says why.

What changed

  • One data path per plot. Each plot reads its data through one extractor method per source (the result's fields, or the same datasets from the file) and draws from that, so both sources go through one drawing body. The tests check that the two sources give the same data for every plot.
  • Registry (src/Analysis/Registry.jl): each plot registers the sources it takes and the capabilities it needs. capabilities(src) is the single record of what a result or file holds, and REASONS gives one sentence per capability saying what to change in the run. available_plots is exported at the top level.
  • Missing data throws. Each plot starts with _require, which throws the registry's reason. The FFS, PE and ErrorFields summaries skip the panels a run cannot supply and throw only when no panel can be drawn.
  • plot_delta_prime plots the diagonal Δ′_ii of the inter-surface Δ′ matrix from a Riccati or Galerkin solve, which is what the REPL summary shows. It used to plot a per-surface estimate from the asymptotic coefficients, which OdeState's docstring calls a stub that is not physically valid.
  • ErrorFields plots also accept an ErrorFieldsResult, and the coil-forcing plots (plot_coil_geometry_3d, plot_coil_geometry_rz, plot_mode_spectrum) a PerturbedEquilibriumState.
  • REPL summaries for PlasmaEquilibrium, LocalStabilityResult, ForceFreeStatesResult, PerturbedEquilibriumState and ErrorFieldsResult end with a Plots k of n available (see available_plots) row.
  • Docs and guidance: docs/src/api.md has a new "Plotting a result" section and docs/src/analysis.md documents the registry. CLAUDE.md says how to add a plot, a capability and a summary row. examples/Solovev_ideal_example/analyze_example.jl plots results in memory, which also fixes its reference to an undefined p_pe.

Example: available_plots on a Riccati result

Plots for ForceFreeStatesResult (riccati): 11 of 13 available
  ✓ ForceFreeStates.plot_ffs_summary                         the stability panels the run supplies
  ✗ ForceFreeStates.plot_mode_displacement                   needs stored ξ profiles: use the Forward integrator or a matched Galerkin solve (Riccati and unmatched Galerkin store none)
  ✓ ForceFreeStates.plot_fixed_boundary_stability_criterion  critical determinant against ψ_N
  ✓ ForceFreeStates.plot_energy_eigenvectors                 free-boundary energy eigenvectors per m
  ✓ ForceFreeStates.plot_eigenvalues                         free-boundary energy eigenvalues
  ✓ ForceFreeStates.plot_edge_stability_scan                 edge energies against the truncation q
  ✓ ForceFreeStates.plot_delta_prime                         Δ′_ii at each rational surface
  ✗ ForceFreeStates.plot_cond_fbar                           needs the kinetic singular-surface scan: run with a kinetic source and singfac_min > 0
  ✓ Equilibrium.plot_equilibrium_summary                     flux surfaces and profiles
  ✓ Equilibrium.plot_qprofile                                q against ψ_N with the rational surfaces
  ✓ Equilibrium.plot_pressure_profile                        μ₀p against ψ_N
  ✓ Equilibrium.plot_f_profile                               2πF against ψ_N
  ✓ Equilibrium.plot_flux_surfaces                           flux surfaces and θ spokes in (R, Z)
julia> A.ForceFreeStates.plot_mode_displacement(ffs)
ERROR: ArgumentError: plot_mode_displacement: needs stored ξ profiles: use the Forward integrator or a matched Galerkin solve (Riccati and unmatched Galerkin store none)

Regression report

develop @ a1149c5c2 against feature/analysis-result-plots @ bd8fe977d (fan-out on ITER's SDCC cluster, Julia 1.12.6, pinned manifest): 27 cases, 385 quantities, all unchanged.

Per-case report
diiid_error_field            10 unchanged
diiid_kinetic_multi_n        8 unchanged
diiid_multi_n                19 unchanged
diiid_n1                     53 unchanged
diiid_n1_riccati             17 unchanged
diiid_n1_riccati_precompiled 17 unchanged
diiid_slayer_n1              22 unchanged
diiid_slayer_n1_chi_perp_e   22 unchanged
diiid_slayer_n1_coupled      7 unchanged
diiid_slayer_n1_toroidal     5 unchanged
diiid_slayer_n1_winding      22 unchanged
diiid_truncation_n1          12 unchanged
efit_fixedbdy_separatrix     5 unchanged
gal_resistive_diiid          11 unchanged
gal_resistive_diiid_galinner 11 unchanged
ggj_ray_q500i                4 unchanged
ggj_reference                4 unchanged
lar_ggj_tearing_n1           3 unchanged
lar_reversed_shear_n1        22 unchanged
solovev_kinetic_nuzero       14 unchanged
solovev_multi_n              15 unchanged
solovev_n1                   22 unchanged
tj_kinetic_calculated        20 unchanged
tj_kinetic_multiion          6 unchanged
tj_kinetic_ntv               7 unchanged
tj_kinetic_ntv_rates         2 unchanged
tj_n1                        25 unchanged

Tests on ITER's SDCC cluster at b8c6526 plus the 2d7718c fix: analysis plots 207/207, ForceFreeStatesResult 280/280, solve API 182/182, error fields 397/397, Tearing 166/166.

Notes for reviewers

  • Closes Analysis UX: introduce a GPECResult object + precondition-aware plot registry #326. With Display - FEATURE - Give every result type a concise REPL summary #649 this delivers its plot registry, the ✓/✗ catalog with reasons, actionable
    errors in place of placeholder plots, and the result summaries with plot availability. Its
    GPECResult wrapper and plot(result, :name) were dropped by design: the typed stage results are
    the objects, every plot takes one or a gpec.h5 path, and available_plots lists real function names.
  • No change to the figures. The drawing bodies are the existing ones, moved into _plot_x(data) functions. A follow-up plots-overhaul PR will restyle them and add plot(r)/plot!(p, r) overlay recipes; this PR leaves them alone so that work is not done twice.
  • Tests: test/runtests_analysis_plots.jl runs a Forward run with coil forcing, PE and local stability, a Riccati run and a kinetic run. For every registered plot it checks that the plot has a method for each source it declares, reads the same data from the result and the file, and draws from both or throws the registry's reason from both. test/runtests_error_fields.jl does the same for ErrorFieldsResult, and test/display_check.jl checks the summaries' Plots row is present exactly when the type has registered plots.
  • Error types: the component and matrix_type checks now throw ArgumentError instead of ErrorException.

🤖 Generated with Claude Code

@matt-pharr matt-pharr self-assigned this Nov 4, 2026
@github-actions github-actions Bot added changed-results Results move or an interface breaks - read before upgrading feature New capability labels Nov 4, 2026
@matt-pharr
matt-pharr added this pull request to stack #651 November 4, 2026 12:55
@matt-pharr
matt-pharr requested a review from logan-nc November 4, 2026 13:59
@matt-pharr
matt-pharr marked this pull request as ready for review November 4, 2026 13:59
@matt-pharr matt-pharr added this to the GPEC v2.1.0 milestone Nov 4, 2026
@matt-pharr

Copy link
Copy Markdown
Collaborator Author

@logan-nc this is PR 2/3 of my overhaul of how users interact with outputs. It allows for the Analysis library to do plotting through either a saved gpec.h5 file like before, or through an in-memory result object obtained by running one of our modules. This means you are able to do something like

>>> ffs = solve(eq, Riccati());
>>> ffs
ForceFreeStatesResult  Riccati, ideal closure
  Physics            ideal MHD
  Modes              n = 1, m = -4:3 (8 total)
  Domain             ψ ∈ [0.0001, 0.9995], q_lim = 3.262
  Surfaces           2: 2/1 @ ψ = 0.1221, 3/1 @ ψ = 0.8804
  Fixed boundary     unstable (6 zero crossings of the critical determinant)
  Free boundary      stable: et₁ = 16.4, ep₁ = 14.04, ev₁ = 2.365, vacuum λ = 2.315
  Solution           ξ profiles not stored
  Wall               ConformalWall, standoff = 0.2415 a
>>> available_plots(ffs)
Plots for ForceFreeStatesResult (riccati): 11 of 13 available
  ✓ ForceFreeStates.plot_ffs_summary                         the stability panels the run supplies
  ✗ ForceFreeStates.plot_mode_displacement                   needs stored ξ profiles: use the Forward integrator or a matched Galerkin solve (Riccati and unmatched Galerkin store none)
  ✓ ForceFreeStates.plot_fixed_boundary_stability_criterion  critical determinant against ψ_N
  ✓ ForceFreeStates.plot_energy_eigenvectors                 free-boundary energy eigenvectors per m
  ✓ ForceFreeStates.plot_eigenvalues                         free-boundary energy eigenvalues
  ✓ ForceFreeStates.plot_edge_stability_scan                 edge energies against the truncation q
  ✓ ForceFreeStates.plot_delta_prime                         Δ′_ii at each rational surface
  ✗ ForceFreeStates.plot_cond_fbar                           needs the kinetic singular-surface scan: run with a kinetic source and singfac_min > 0
  ✓ Equilibrium.plot_equilibrium_summary                     flux surfaces and profiles
  ✓ Equilibrium.plot_qprofile                                q against ψ_N with the rational surfaces
  ✓ Equilibrium.plot_pressure_profile                        μ₀p against ψ_N
  ✓ Equilibrium.plot_f_profile                               2πF against ψ_N
  ✓ Equilibrium.plot_flux_surfaces                           flux surfaces and θ spokes in (R, Z)
  
>>> Analysis.Equilibrium.plot_equilibrium_summary(ffs)
(a plot of the equilibrium appears)
>>> Analysis.ForceFreeStates.plot_ffs_summary(ffs)
(ForceFreeStates summary plot appears)

@matt-pharr
matt-pharr force-pushed the feature/analysis-result-plots branch from 2d7718c to 8f40f6a Compare November 5, 2026 22:01
Base automatically changed from feature/result-display to develop November 7, 2026 12:10

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changed-results Results move or an interface breaks - read before upgrading feature New capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Analysis UX: introduce a GPECResult object + precondition-aware plot registry

1 participant