Skip to content

Rotating live wiring#65

Merged
matt-pharr merged 7 commits into
developfrom
rotating-live-wiring
Jul 1, 2026
Merged

Rotating live wiring#65
matt-pharr merged 7 commits into
developfrom
rotating-live-wiring

Conversation

@samuel-frei

Copy link
Copy Markdown
Collaborator

Summary

Adds optional 2-D Gaussian pre-smoothing to the rotating spectrogram, applied before
the coherence/power gates so contiguous coherent mode ridges survive aggressive gating.
σ is expressed in grid cells (not ms/kHz, which rounded to a sub-bin no-op on the coarse
STFT grid) with a live physical-units readout, and linear power is smoothed (arithmetic
mean) so a bright ridge fills its dim/dropout neighbours instead of staying dark.

Related issue

N/A

Type of change

  • Analysis core (src/magnetics/core)
  • Data source (src/magnetics/data)
  • Service / API (src/magnetics/service)
  • Web GUI (gui/web)
  • Docs
  • Other:

How I tested it

  • uv run pytest222 passed, 1 skipped (incl. test_ridge_gap_is_reinforced, which the
    linear-power switch turns green; plus node smooth smoke/no-op and medianStep tests).
  • uv run ruff format --check . + uv run ruff check → clean.
  • cd gui/web && npx tsc --noEmit → clean; npx vitest run26 passed.
  • In-process effect check on real shot 164671: spectrogram cell-to-cell roughness
    0.518 (off) → 0.024 (σ 3/1.5 cells) → 0.007 (σ 8/5 cells) — strong, monotonic.
  • Ridge-gap reinforcement: a punched-out gap along a ridge recovers to ≈ridge level
    (−0.06 log10) under linear smoothing vs staying dark (−0.80) under the old log-power mean.
  • Browser (live backend): readout renders correctly, e.g. σ time: 3.0 cells ≈ 6.0 ms,
    σ freq: 1.5 cells ≈ 0.75 kHz, computed from the live grid via medianStep.

Checklist

  • CI is green (analysis: ruff + pytest; web: lint + build)
  • No secrets/credentials (tokens, SSH keys, MDSplus/cluster logins) committed
  • No large files or tokamak data committed (tests use synthetic fixtures)
  • Physics kept in core (device-agnostic); none added to service routes
  • Docs updated if behavior/usage changed (docs/CONTRACT.md + plan docs)

samuel-frei and others added 2 commits July 1, 2026 14:00
Optional pre-gate 2-D Gaussian blur of the rotating spectrogram so contiguous
coherent structure survives aggressive gating: smooth_spectrogram (power +
coherence) and smooth_mode_spectrogram (n-map quality + amplitude) in core, run
before denoise_spectrogram and applied consistently across the spectrogram /
coherence / n-spectrum / n-map nodes.

Two fixes make it actually visible:

- σ in grid cells, not ms/kHz. Physical widths mapped to sub-bin σ on the coarse
  STFT grid (2 ms slice → 0.5 kHz bins; time-decimated columns), so the defaults
  rounded to a near-no-op and moving the sliders did nothing. Cells are one STFT
  bin, resolution-relative, and can't collapse below a bin. The GUI shows the
  physical equivalent per live grid (e.g. "3.0 cells ≈ 6.0 ms") via the new pure
  medianStep helper. Params: smooth_t_cells / smooth_f_cells.

- Smooth LINEAR power (arithmetic mean), not log power. A log/geometric mean is
  dominated by the darkest cell, so a punched-out ridge gap stayed dark and never
  survived the gate. Linear averaging is pulled toward the bright cells, so a
  ridge bleeds into and fills its dim neighbours — the whole point of the pass.

Contract (CONTRACT.md) + plan docs updated. Tests: core smooth incl. ridge-gap
reinforcement, node smoke/no-op, medianStep. Full suite 222 passed / 1 skipped;
frontend tsc + 26 vitest green; ruff clean.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Add an `api` entry to .claude/launch.json (uvicorn on :8000, --reload) so the
FastAPI backend can be launched alongside the web dev server. Re-resolve uv.lock
(drop the stale exclude-newer pin; loosen a pexpect marker).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@samuel-frei samuel-frei marked this pull request as ready for review July 1, 2026 18:38

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.

Copilot reviewed 11 out of 12 changed files in this pull request and generated no comments.

matt-pharr and others added 5 commits July 1, 2026 15:18
Follow-up patch to the 2-D Gaussian pre-smoothing in this feature: it
blurred *linear* power, which haloed bright ridges and, viewed on the log
heatmap, *raised* the visible log-variance instead of lowering it — so the
blur was effectively invisible. Smooth log10 power (then map back) so the
blur is uniform across the dynamic range and matches both the display and
the log-power gate; coherence stays linear. Real-shot log-variance now
drops monotonically with sigma.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
…ooth test

- app.py: drop the raw exception text from the /download 500 body (keep it in
  logger.exception) so an h5py/OSError message can't leak to the client
- contract.ts: type HeatmapNode.z as (number | null)[][] to match the
  denoise-gated cells the server now emits as null
- test_nodes.py: assert the smoothed spectrogram z actually differs from the
  baseline, so a no-op regression in the smooth-param wiring fails
The contract change typed HeatmapNode.z as (number | null)[][] to match the
denoise-gated cells the server emits as null. tsc -b (strict project build)
then flagged three RotatingTab consumers that assumed number[][]:

- cursor-column power trace: null cell → NaN so the log trace gaps there
- live coherence column: coalesce a gated/absent cell to 0
- stripePlot: widen its param to (number | null)[][]; Plotly renders null as gaps
@matt-pharr matt-pharr merged commit d2e32ca into develop Jul 1, 2026
4 checks passed
@samuel-frei samuel-frei deleted the rotating-live-wiring branch July 1, 2026 20:22
matt-pharr added a commit that referenced this pull request Jul 1, 2026
…relock)

Third develop→kstar_int sync. develop advanced with PR #65 (rotating-live
wiring: 2-D Gaussian pre-smoothing, download-500 hardening, heatmap null-cell
handling) and PR #46 (gui-sensors: device-agnostic coil overlay, saddle-loop
tilt). Only one conflict — uv.lock — since pyproject.toml is identical on both
sides; resolved by taking develop's lockfile and regenerating with `uv lock`
(135 packages, `uv lock --check` clean). app.py, nodes.py, and test_nodes.py
auto-merged.

Verified: ruff/ty/pytest (312 passed, 5 skipped)/eslint/tsc/vitest (29)/vite
build all green.

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.

3 participants