Skip to content

Refactor quasistationary code into magnetics.core#51

Open
priyanshlunia wants to merge 5 commits into
developfrom
refactor-quasistationary
Open

Refactor quasistationary code into magnetics.core#51
priyanshlunia wants to merge 5 commits into
developfrom
refactor-quasistationary

Conversation

@priyanshlunia

Copy link
Copy Markdown
Collaborator

Retire quasistationary.py; promote the SLCONTOUR pipeline into shim-free core/qs_*

Draft PR body for refactor-quasistationarydevelop. Delete this file after copying.

Summary

Reverses the direction of #40: instead of finishing the half-built pure-numpy
core/quasistationary.py port, this retires that dead module and promotes the
actually-served SLCONTOUR quasi-stationary pipeline (_slcontour/) into core/ under a
unified qs_ namespace, deleting the omfit_compat OMFIT shim entirely. Net −356 lines
(635 insertions / 991 deletions).

Three commits, each self-contained:

  1. refactor(core): promote SLCONTOUR pipeline into shim-free core/qs_* — the move + shim removal.
  2. docs: relocate + modernize the QS reference notebook & README — reference-material cleanup.
  3. Adjusted stale docstring references, clarified each script's context and role — docstring pass.

Why

An audit turned up two facts:

  • core/quasistationary.py was dead weight — a partial pure-numpy port of only the fit step
    (sinusoidal only; no prep, no loader, no Gaussian/vertical geometry), not wired into
    production
    , and its reconstruct_grid used exp(+i(nφ+mθ)), which mirrors the toroidal
    phase / flips helicity and is wrong for any mode with a non-zero spatial phase. It even
    shipped a test that falsely certified it, because the test only exercised zero-phase modes
    where +i and −i happen to agree.
  • The active pipeline is correct_slcontour (io_data → prep → fit) driven through
    core/qs_bridge is what the service serves, and its −i reconstruction matches the original
    DIII-D SLCONTOUR reference.

So the maintainable move is to make the working pipeline first-class and delete the misleading port.

What changed

Retire the dead port

  • Delete core/quasistationary.py and its degenerate test.

Promote _slcontourcore/qs_* (shim-free)

  • _slcontour/{fit,prep,io_data,run,plots}.pycore/qs_{fit,prep,io_data,run,plots}.py.
  • Delete omfit_compat.py, folding each symbol to its owner: printv/w/e → stdlib logging;
    OMFITexceptionValueError; delta_degreesqs_fit; cornernote/ubandqs_plots.
  • New core/qs_device.py — the shared device-metadata leaf. It delegates to the data layer
    (data.devices, data.diiid_geometry) and keeps only the QS-specific extras: the derived
    theta/*_end1/2 sensor_geometry, the resolve_channel_filter semantics, and the
    "DIII-D" → diiid alias. data/ is left untouched.

Lock the reconstruction sign convention

  • Invariant comments at the three reconstruction sites (qs_bridge._reconstruct_grid,
    qs_bridge.fit_to_phi_t_node, qs_plots.plot_slice) stating: fit basis exp(+i(nφ+mθ)) with a
    real/imag column split ⇒ reconstruction must use exp(−i(…)).
  • New discriminating regression test in tests/test_qs_bridge.py (non-zero spatial phase δ)
    that fails against +i and passes against −i — the test the old one lacked.

Fixes a latent bug

  • qs_device.load_wall now delegates to devices.feature_at("first_wall"); the old shim read a
    non-existent top-level wall key and returned (None, None) for every shipped device, so the QS
    sensor-map wall silently never rendered. It now returns the real outline (nodes.py threads shot).

Rewire + config + docs

  • service/nodes.pycore.qs_run.run_steps and core.qs_device.load_wall.
  • pyproject.toml → drop the ruff exclude (the qs_* files are ruff-clean); a targeted ty exclude
    is retained on the qs_* files as a documented follow-up (see below).
  • qs_plots.py docstring reframed as standalone matplotlib (verified: nothing in the served path
    imports it — qs_bridge only ports its conventions).
  • Reference notebook → examples/example_magnetics.ipynb, rewired onto magnetics.core.qs_*
    (no more sys.path/omfit_compat imports). README rewritten as
    docs/specs/quasistationary-mode-analysis.md (no OMFIT-magnetics plumbing).
  • Module docstrings across qs_* recontextualized (OMFIT script provenance + pipeline step).

Verification

  • uv run ruff format --check . — clean.
  • uv run ruff check . — clean.
  • uv run --extra service ty check src/magnetics — clean.
  • uv run --extra service pytest209 passed, 1 skipped (the 3-test drop is the deleted
    degenerate quasistationary suite; the new sign-convention regression test is included).

Follow-ups (intentionally out of scope)

  • Make the qs_* files ty-clean and drop their targeted ty exclude.
  • Dedupe the pre-existing set-flattening copies in data/ against qs_device.
  • Per-sensor σ / helicity from the data layer (the constant 2e-5 T σ inflates χ²); a device-agnostic
    pure-numpy core fit.
  • docs/magnetics-code-architecture.md is still OMFIT-era and stale — later docs sweep.

Reviewer notes

🤖 Generated with Claude Code

priyanshlunia and others added 3 commits July 1, 2026 12:12
Retire the dead core/quasistationary.py (a partial, unwired port whose
reconstruct_grid used exp(+i(nφ+mθ)) — wrong for any phased mode) plus its
falsely-certifying test, and promote the *active* SLCONTOUR pipeline into core
under unified qs_ naming, deleting the omfit_compat OMFIT shim.

- Move _slcontour/{fit,prep,io_data,run,plots}.py -> core/qs_*.py.
- Delete omfit_compat.py, folding each symbol to its owner: printv/w/e -> logging;
  OMFITexception -> ValueError; delta_degrees -> qs_fit; cornernote/uband ->
  qs_plots; device readers -> new core/qs_device.py, which delegates to the data
  layer (data.devices / data.diiid_geometry) and keeps only the QS-specific extras
  (derived theta/end-coord sensor_geometry, resolve_channel_filter semantics).
- qs_device.load_wall now delegates to devices.feature_at, fixing the segmented-
  schema bug (the shim read a nonexistent top-level `wall` key -> (None, None)).
- Lock the -i reconstruction convention: invariant comments at the three sites
  + a discriminating δ≠0 regression test the old zero-phase test missed.
- qs_plots docstring clarified as standalone matplotlib (not GUI/service wired).
- Rewire service/nodes.py + tests; pyproject drops the ruff exclude and retargets
  a deferred ty exclude onto the qs_* files. Relocate the reference notebook/README
  to docs/qs_slcontour/.

ruff format/check + ty (with service extra) clean; 209 passed, 1 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Finish the core/qs_* promotion by cleaning up its reference material, which was
still written for the old OMFIT `magnetics-code` layout.

- Move example_magnetics.ipynb into examples/ and rewire it onto the new
  infrastructure: imports become magnetics.core.qs_{io_data,device,run} plus
  `qs_plots as plots` (so the plot cells are unchanged); drop the sys.path hack
  and the OMFIT-script mapping / "ported OMFIT scripts" framing.
- Replace docs/qs_slcontour/README.md with docs/quasistationary-mode-analysis.md:
  no OMFIT-magnetics plumbing (module↔OMFIT-script table, omfit_compat, cd
  analysis), instead a walkthrough of the qs_* pipeline (io_data → prep → fit →
  run, plus qs_bridge→service and standalone qs_plots), data inputs, quick start,
  and the -i reconstruction sign convention. Physics provenance (SLCONTOUR,
  VISION §4.1) kept.
- Remove the now-empty docs/qs_slcontour/.

Docs-only; no src/ behavior change. ruff format --check clean.
(docs/magnetics-code-architecture.md is also OMFIT-era — left for a later sweep.)

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR consolidates the quasi-stationary (SLCONTOUR-style) analysis into magnetics.core under a qs_* namespace, retiring the unused pure-numpy quasistationary.py port and removing the _slcontour/omfit_compat shim, while updating the service, tests, and docs to point at the promoted core pipeline.

Changes:

  • Promote the active SLCONTOUR pipeline into src/magnetics/core/qs_{io_data,prep,fit,run,device,plots}.py and keep qs_bridge as the service/GUI adapter.
  • Remove the dead src/magnetics/core/quasistationary.py path and the _slcontour shim/docs.
  • Rewire service + tests + docs to the new modules and adjust tooling excludes in pyproject.toml.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/test_slcontour_geometry.py Update imports/docs to validate geometry via core.qs_device instead of _slcontour.omfit_compat.
tests/test_slcontour_fit.py Point basis-function tests at core.qs_fit and update expected exception type.
tests/test_quasistationary.py Remove tests for the retired core/quasistationary.py port.
tests/test_qs_pipeline.py Update QS end-to-end test wiring to qs_* modules.
tests/test_qs_bridge.py Add regression coverage for the reconstruction sign convention using a non-zero spatial phase.
src/magnetics/service/nodes.py Switch QS pipeline orchestration to core.qs_run.run_steps and wall loading to core.qs_device.load_wall(device, shot).
src/magnetics/core/quasistationary.py Delete the unused pure-numpy QS port.
src/magnetics/core/qs_run.py Ensure QS orchestrator imports qs_fit/qs_prep/qs_io_data modules.
src/magnetics/core/qs_prep.py Replace OMFIT shim prints with stdlib logging; keep prep logic in core.
src/magnetics/core/qs_plots.py Replace OMFIT shim plot helpers with local matplotlib helpers; keep sign convention comments.
src/magnetics/core/qs_io_data.py Repoint loader dependencies to qs_device and replace shim prints with logging.
src/magnetics/core/qs_fit.py Replace OMFIT exception type + logging changes; (contains a Python-syntax issue noted in comments).
src/magnetics/core/qs_device.py New device adapter delegating to magnetics.data.* for segment resolution/sets; (contains Python-syntax issues noted in comments).
src/magnetics/core/qs_bridge.py Update docs/comments to reflect qs_fit provenance; pin reconstruction sign convention with invariant comments.
src/magnetics/_slcontour/README.md Remove deprecated reference README.
src/magnetics/_slcontour/omfit_compat.py Remove OMFIT compatibility shim (replaced by qs_device + logging).
src/magnetics/_slcontour/init.py Remove deprecated _slcontour package init.
pyproject.toml Drop ruff exclude for _slcontour; add targeted ty excludes for qs_* modules.
examples/example_magnetics.ipynb Rewire notebook to import/run magnetics.core.qs_* directly (no sys.path hacks).
docs/specs/quasistationary-mode-analysis.md Add updated pipeline spec, inputs, and explicit reconstruction sign convention documentation.
Comments suppressed due to low confidence (1)

src/magnetics/core/qs_fit.py:334

  • Invalid Python exception syntax: except ValueError, np.linalg.LinAlgError: will raise a SyntaxError and prevent this module from importing. Use a tuple of exception types instead.

Comment on lines +122 to +126
def _to_float(s):
try:
return float(s)
except TypeError, ValueError:
return float("nan")
Comment on lines +203 to +207
"""
try:
dev = load_device(device)
except FileNotFoundError, OSError, ValueError:
return None, None
priyanshlunia and others added 2 commits July 1, 2026 16:21
Brings develop through #46 (gui-sensors): NSTX/NSTX-U device-generic fetch,
per-node plot + HDF5 export, EFIT tree-fetch, and the data-layer device_geom
refactor. No merge conflicts; the qs_* pipeline works against develop's
refactored data layer.

Verified on the merged tree: ruff format/check clean, ty clean, 290 passed /
5 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…core

Two device-specific bits removed from the QS core (issue #40 device-agnostic
goal), without touching data/device/*.json or the data layer:

- qs_device: delete the hardcoded `_DEVICE_ALIAS = {"DIII-D": "diiid"}` and
  `_device_key`; `load_device` now delegates to `devices.resolve_device_id`,
  which derives the config id from each device JSON's `name` field.
- qs_fit: remove the advisory `is_device("DIII-D")` + `C.*/IL.*/IU.*` coil-basis
  warning and its now-unused `is_device` import.

The 2019 ESLD wiring swap in qs_prep is intentionally kept for now (migrating it
needs a JSON `corrections` block + reader, deferred). ruff/ty clean; 290 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
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.

2 participants