Feat/bouquet class api#8
Merged
Merged
Conversation
Stub-level scaffold for the class-based bouquet API. Composable Bouquet class (setup_solver -> prepare_baseline -> generate -> filter -> export) plus a thin run() convenience, driven by typed config dataclasses. Two independent axes: - Baseline source: ReconstructionSource (g-file + IDA/p-file -> GS recon) or ImasSource (FUSE dd_sim.json, currents pre-separated, no recon). Both resolve to a common Baseline. - Uncertainty envelope: kinetic sigmas (from IDA) + j_phi sigma + GPR length scales, in UncertaintyConfig. Conventions baked in: - All current components stored as toroidal j_phi; physics.parallel_to_toroidal converts IMAS/neoclassical parallel currents and TokaMaker solve_with_bootstrap output (analytic vs j_tor/j_total-ratio methods). - Fixed additive components (p_fast, j_NBI, j_RF) summed into every draw, never GPR-perturbed; all accept plain psi_N arrays. j_RF is user-array-only. - p_fast isotropization defaults to trace/3 = (2 p_perp + p_par)/3. - IDA reader uses h5py (file is netCDF4/HDF5); FUSE reader uses stdlib json. No OMFIT / IMAS / OMAS install required. All modules are import-validated stubs (NotImplementedError bodies); no backend wiring yet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
physics.py: - isotropize_fast_pressure: trace/mean/perp reductions, default trace=(2p_perp+p_par)/3 - parallel_to_toroidal: ratio method (c=j_tor/j_total) with on-axis zero-crossing guard; analytic method deferred to the solve_with_bootstrap integration io/ida.py (read_ida, h5py only): - handles the operational multi-time direct-*_err IDA_*.cdf layout - time selection by nearest slice; ni from carbon quasineutrality (Z=6); stored Zeff returned as-is; raw bytes captured - verified against IDA_204441_.cdf @ 4.4s io/imas.py (read_imas_baseline, stdlib json only): - FUSE dd_sim.json -> separated Baseline, provenance "imas", no GS recon - per-IDS nearest-time selection; psi_N from grid.psi; parallel->toroidal via ratio; j_phi=j_tor with inductive=residual (decomposition closes, Ip preserved) - j_NBI = sum of beam-source j_parallel; j_RF zeros (user-override only) - p_fast = sum of per-species isotropized fast pressure - verified against a D3D FUSE run @ 2.0s config: sigma_mode default -> "direct" (matches operational IDA files) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- read_ida: ni = ne (quasi-neutrality approx) to match the operational IDA workflow (204441_4400 feeds ni=ne, not the carbon-corrected density); sigma_ni tracks sigma_ne. Drops the unused carbon-quasineutrality path. - resolve_baseline(config, mygs=None): dispatch implemented. ImasSource branch delegates to read_imas_baseline, threading fixed_components and p_fast_reduction; ReconstructionSource branch raises NotImplementedError (step 3); unknown types raise TypeError. - BouquetConfig.__post_init__: validates output_header, source-specific required paths, p_fast_reduction, and sigma_mode -- configs are now constructible. - Bouquet.prepare_baseline: wired to resolve_baseline; caches self.baseline. Verified the full IMAS path through Bouquet(config).prepare_baseline() against a D3D FUSE run: provenance imas, Ip/l_i correct, decomposition closes (~2e-10). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Bouquet.setup_solver: stand up TokaMaker (mesh, regions, F0 from g-file, VSC, isoflux from LCFS, weak coil regularisation) per the operational recipe. - resolve_baseline ReconstructionSource branch (_resolve_reconstruction): read g-file + IDA profiles (ni=ne), interpolate to the g-file psi_N grid, run reconstruct_equilibrium, package a toroidal-current Baseline on dual grids (equilibrium psi_N + IDA psi_N_kinetic). Fixed components default to zero (the reconstructed j_phi_fit already contains driven current); user-supplied j_NBI/j_RF/p_fast re-partition the inductive residual. - ReconstructionSource.time field for multi-time IDA selection. - .gitignore: never commit FUSE dd_sim.json (proprietary DIII-D data). Verified against the 204441_4400 testbed (live GS solve): recon converged (l_i err 1e-4, Ip err 0.035%, bnd RMS 2.8mm); decomposition closes; and a get_jphi_from_GS cross-check confirms j_phi_fit is TOROIDAL to ~1% -- same convention as the IMAS path, resolving the solve_with_bootstrap question. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
_load_kinetic_profiles dispatches on profiles_path: IDA .cdf (ni=ne,
quasi-neutrality) or Osborne p-file (real ni via compute_quasineutrality,
Zeff from the ion mix; ne/ni 1e20 m^-3, Te/Ti keV -> SI). _resolve_reconstruction
now consumes that helper, so both sources flow through one path.
This makes the synthetic, non-proprietary examples/D3D-like fixtures a
committable end-to-end test of the reconstruction path. Verified against
D3Dlike_Hmode_baseline.{geqdsk,peqdsk}: recon converged (l_i err 3e-5,
Ip err 0.027%, bnd RMS 2.55mm), ni<ne (real ion density), decomposition closes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hase 2, step 3-generate) generate_bouquet / perturb_kinetic_equilibrium (approach A, backward-compatible): - New optional kwargs p_fast / j_NBI / j_RF (default None -> provable no-op). - Fixed driven currents (j_NBI+j_RF) are treated exactly like the bootstrap spike: additive, non-scaled, summed in at the 5 j_phi assembly/Ip-constraint points (recon-anchor, Ip root-solve, _matched, find_optimal_scale spike_prof, corrective target). Gated by recalculate_j_BS so the inductive base isn't double-counted. True bootstrap reporting (shelf-blend) is untouched. - p_fast (fast-ion pressure) is added to the pressure after the perturbation; the thermal pressure-match diagnostic stays thermal-only. Bouquet.generate: - Resolves the uncertainty envelope (baseline.resolve_uncertainty: kinetic sigmas from IDA or fractional fallback + sigma_jphi + GPR lengths), threads the Baseline + configs into generate_bouquet, passes the fixed components. - Zeff interpolated to the equilibrium grid (solve_with_bootstrap consumes it on psi_N, not the kinetic grid). GenerationConfig reconciled with the actual generate_bouquet API and proven operational defaults: homotopy_passes schedule [(0.05,0.10),(0.02,0.05), (0.01,0.01)] (a single tight ±1% solve is infeasible), l_i_tolerance 0.05, jBS_scale_range (0.99,1.01), coil_drift 0.01. Verified end-to-end against the synthetic D3D-like fixture (live GS solves): - zero fixed components: draw archived IN_SPEC (backward-compatible path); - nonzero synthetic j_NBI: re-partitions inductive (j_phi unchanged), Ip constraint absorbs it (Ip err 0.029%), l_i matched, archived IN_SPEC. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e 2, steps 1-3)
- Bouquet.filter(): wraps filter_coil_currents + filter_boundaries (coil-current
spec + LCFS boundary RMS), writing pass flags into the HDF5; returns the
summaries.
- Bouquet.export(): wraps export_filtered -> pruned {header}_selected.h5 of the
in-spec subset.
- setup_solver extended to ImasSource: F0 = |r0*b0(t)| and the isoflux LCFS
derived from the FUSE IDS (vacuum_toroidal_field + boundary.outline) via the
new io.imas.read_imas_geometry, replacing the g-file path.
- __init__.py: export the orchestrator API (Bouquet, BouquetConfig, the
sources/configs, read_ida, read_imas_baseline, read_imas_geometry, the physics
reductions, resolve_baseline/resolve_uncertainty). run.py keeps OFT imports
lazy so package import stays headless-safe.
Verified: `bouquet.Bouquet(cfg).run()` (setup -> baseline -> generate -> filter
-> export) on the synthetic D3D-like fixture archives 3 draws and exports the
in-spec subset (full [0,1,2] -> selected [0,2]); IMAS setup_solver + baseline
resolve confirmed against a D3D FUSE run.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
D3Dlike_baseline_omas.json: a minimal FUSE-style OMAS data dictionary (equilibrium + core_profiles + core_sources, single slice) built from the synthetic D3Dlike geqdsk + p-file with smooth generic fits for the parallel/toroidal ratio, bootstrap split, beam current, and fast pressure. Non-proprietary; exercises the ImasSource path. .gitignore also blocks dd_exp.json.
- Bouquet.prepare_baseline forward-solves the IMAS baseline on mygs (the recon path gets this from reconstruct_equilibrium): init_psi from the IDS LCFS shape, then jphi-linterp solve with the IMAS j_phi + thermal+fast pressure. - l_i_target set to the TokaMaker-solved li_1; IDS li_1/li_3 kept in baseline.li_metrics for sanity (read_imas_baseline captures them). - recalculate_j_BS=True path: the forward-solve rebuilds the baseline on the SWB-bootstrap basis the draws use (inductive*scale + SWB spike + fixed, Ip-matched), so the target l_i is reachable. recalc=False keeps the IDS bootstrap. Verified: draws now land in-band (l_i err ~1.6%). - read_imas_baseline: pfile_bytes=None (the OMAS JSON is not an Osborne p-file). - Baseline gains an li_metrics field. Known follow-up: IMAS draws currently exceed the coil/VSC homotopy bounds (VSC drift ~15%) and are filtered out; needs VSC/coil conditioning work.
…otting overhaul Working-tree checkpoint of the class-API branch before the pre-merge cleanup pass (audit 2026-06-12): extra-profile perturbation switchboard, captured solver output (capture_native_output), reconstruction/generation summaries, IMAS forward-solve li-target alignment, and the plotting refactor. Untracked notebook/scaffolding files intentionally excluded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Remove the shot-named legacy notebook and the superseded baseline derived from an experimental boundary; genericize all shot references in code comments and docs. Rewrite README Quick Start around bq.Bouquet (old example passed removed/percent-valued parameters), add architecture.md \xC2\xA71.5 for the class-API layer, mark CHANGES_SUMMARY historical, gitignore working notes, and track the tests/data fixtures CI needs.
…at-if, sigma-alias plot, uniform config idiom
…er-draw nz1 Perturb what is measured, derive what is inferred: with the zeff channel active, each draw samples (ne, Zeff) and derives ni via single-impurity quasineutrality (physics.effective_impurity_charge / main_ion_density_from_zeff; Zeff clipped to [1, Z_imp] so ni, nz >= 0). sigma_ni is unused in this mode (one-time NOTE). One Zeff per draw across the archive, aux datasets, and per-draw p-file (verified to ~2e-4). Rename the switchboard API extra_* -> aux_* (plural dicts: aux_sigmas / aux_baselines / aux_length_scales; Baseline.aux; h5 aux_<name> with legacy extra_* read fallback; plot_aux_profiles, plot_transport_profiles alias). Fixes surfaced while validating: generate_bouquet now purges its own header/scan's draw groups up front (append-mode archives let a previous run's draws survive under failed counts -- mixed-baseline ghosts); the per-draw p-file nz1 is recomputed from the draw's own (ne, ni) instead of frozen at baseline (the source of the second, uncorrelated Zeff family).
…urity_Z IDA reader now derives ni from its measured Zeff (single-impurity quasineutrality) instead of ni=ne, making it information-equivalent to a p-file and fixing the latent pressure-vs-bootstrap inconsistency. New ReconstructionSource.impurity_Z (carbon 6.0; from_geqdsk kwarg) controls the mapping; OMAS takes Z_imp from IDS species. zeff channel is on by default for every source (UncertaintyConfig.zeff_scalar_sigma=0.05, auto-injected in resolve_uncertainty; 0.0 disables). Reconstruction Baseline now exposes baseline.aux['zeff'] like IMAS. Plotting: opaque legend frames (fix marker ghosting), P'/FF' in gold/orange. +impurity round-trip tests (151 pass).
…eqdsk geqdsk example documents the Zeff-consistent density default and impurity_Z. OMAS example rebuilt to mirror the geqdsk arc (honest OFT guard, version print, seed, plot_traces li_band, plot_spec_summary, knob table, aux naming) with OMAS-specific calls preserved (from_imas/prepare_baseline, plot_imas_bouquet, IDS-auto-filled aux, 3-slice time evolution). Both re-executed clean: 0 errors/warnings/personal-paths.
…e fixes in FUSE mode
…/IMAS 148798 diff+C: 15/15 yield, FUSE-faithful q=2 bootstrap, F9B +/-1.05%.
…+ robustness guards from_imas auto-applies diff+C (jBS_baseline_mode='diff', perturb_jind_in_anchor=True); from_geqdsk auto-applies the standard flagship l_i loop (perturb_jind_in_anchor=False). generate() now hard-guards known-bad combos via _validate_workflow (geqdsk+FixC, IMAS-without-FixC, jphi_scalar_sigma<=0 which freezes j_inductive); bypass with config.generation.allow_unsafe_workflow=True for backend tests. Config defaults: floor_j_BS=False (a no-op with isolate_edge_jBS=True; was a red herring on 204441), perturb_jind_in_anchor=False (geqdsk default -- the standard l_i loop is the flagship GPR j_inductive perturbation; both paths perturb j_ind). Guard per-draw get_stats against degenerate draws (Ip->0 after Sauter corrector failures) so one bad draw no longer crashes the whole run (was losing all stored draws). Add FLOOR_DIAG / BQ_SKIPLOG env diagnostics. Validated by an n=20 robustness campaign: IMAS diff+C 65-100% in-spec at 20/20 completion; geqdsk standard-loop 18-20/20 completion (in-spec tracks coil headroom).
…ds=1 default Make the IMAS-path baseline reproduce the FUSE equilibrium GPEC reads, so per-draw 2/1 gamma ensembles frame the FUSE baseline (148798: 8/8 slices within 1 sigma). Pressure: - physics.impurity_pressure: add the impurity (carbon) thermal pressure nz*Ti (single-impurity model) that single-ion e*(ne*Te+ni*Ti) omitted (~10% low at high Zeff). io/imas, perturb, and the run.py baseline solve all include it. - p_diff anchor: baseline solve pressure == dd equilibrium.pressure exactly (fixed offset added to baseline + every draw, mirrors jBS_diff); absorbs the fast-ion treatment residual. - _validate_pressure_completeness: fail-fast if the source carries thermal/fast pressure the reconstruction omits; GenerationConfig.allow_incomplete_pressure bypasses (backend tests only). Current: - jphi_diff anchor: target the GS-consistent equilibrium.j_tor (carries the pedestal current) instead of the smooth core_profiles.j_tor. Fixed offset folded into j_fixed_eff so it rides under every per-draw build; the SWB bootstrap + perturbed j_ind still drive per-draw variation. GenerationConfig.anchor_jtor_to_equilibrium (default True), IMAS path only. - from_imas: isolate_edge_jBS=False -- FUSE/IMAS carries a full Sauter bootstrap (core hump + edge), so the DIII-D g-file isolated-edge-spike decomposition is skipped (it mislabels a redundant j_BS,edge and mangles per-draw j_inductive). Reproducibility: - SolverConfig.nthreads default 4 -> 1: GS solves bit-reproducible. nthreads>1 OpenMP nondeterminism caused ~1% li_1 scatter AND intermittent DLSODE non-convergence hangs at stiff slices; parallelize across slices as separate single-threaded BLAS-pinned processes instead. Stored-field fixes (diagnostics only): _baseline now stores the ACTUAL solved quantities (pressure_solve incl. carbon/fast/p_diff; j_phi incl. jphi_diff) rather than the un-anchored inputs, so archived diagnostics match what was solved.
from_imas gains ida_path/efit01_geqdsk/impurity_Z/kinetic_source/ anchor_pressure_to_equilibrium. kinetic_source='ida_hybrid' resamples IDA ne/Te/Ti/omega onto the FUSE psi_N grid (single-grid; psi_N==psi_N_kinetic), keeps FUSE Z_eff/Z_imp/ni-dilution (IDA Z_eff is unreliable), and derives ni via single-impurity quasineutrality. p_diff equilibrium-pressure anchor gated behind anchor_pressure_to_equilibrium (default False). read_imas_geometry swaps the LCFS isoflux target to a magnetics-only EFIT01 g-file when efit01_geqdsk is set. Sigma envelopes auto-sourced from the same IDA .cdf. Validated on 148798: 2/1 baseline gamma jitter ~2.5x lower than the FUSE-fit baseline.
…1-thread doctrine - run_shard removes pre-existing shard h5 (stale-draw contamination fix) - merge_archives verifies cross-shard l_i/Ip targets before copying (covers SLURM path) - CLI merge counts expected-vs-found shards; --allow-missing for deliberate partial merges - warn on threads_per_worker>1; re-emit SLURM example at nthreads=1 (was 4)
…adial_field_from_cer) IDACERProfiles carbon channels + sigma envelopes; three-term E_r with uncertainty propagation. Unit tests for terms/units, diamagnetic sign, sigma positivity.
…g serialization, de-threaded readers Bare dataset names with units attrs, fixed eqdsk/pfile names, unified coil storage (golden regenerated); BouquetConfig.to_dict/from_dict + config_json provenance (incl. merged archives; load_config scan-aware); BouquetArchive reader with cached views and O(N) flags; schema.find_bytes_dataset replaces 8 duplicated lookups; workflow presets; JSON SLURM bundle; review fixes: solver-marked tests un-broken, unique eqdsk extraction filenames, legacy-archive warnings/errors.
There was a problem hiding this comment.
Pull request overview
Refactors Bouquet’s workflow into a class-based orchestrator API (BouquetConfig → Bouquet.run() stages), introduces a clean-break schema v2 HDF5 archive layout (self-describing datasets + fixed blob names), and adds supporting readers/physics utilities plus parallel generation tooling. This PR also updates tests and documentation to align with the new API and schema conventions.
Changes:
- Added the class-based config/orchestrator stack (
bouquet/config.py,bouquet/baseline.py,bouquet/archive.py, updated__init__.py) and schema v2 helpers (bouquet/schema.py). - Added new physics + IO utilities (
bouquet/physics.py,bouquet/io/ida.py,bouquet/paths.py,bouquet/parallel.py) and extensive new/updated tests. - Updated docs/examples and migrated codepaths from
scan_value→scan_keyand bracketed HDF5 dataset names → bare names +unitsattrs.
Reviewed changes
Copilot reviewed 38 out of 49 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_systematics.py | Updates golden replay to schema v2 dataset names and coil-name reading helper. |
| tests/test_physics.py | Adds pure-numpy unit tests for new physics reductions and derived quantities. |
| tests/test_ida.py | Adds synthetic IDA .cdf tests for both direct and ensemble layouts (incl. CER channels). |
| tests/test_golden_bouquet.py | Updates golden fixture tests for scan_key, schema v2 blob/dataset names, and filter return shapes. |
| tests/test_core.py | Updates tests to discover_scan_keys and schema v2 baseline keys. |
| tests/test_config.py | Adds serialization/provenance tests for BouquetConfig and schema v2 provenance storage. |
| tests/test_archive.py | Adds tests for BouquetArchive reader, suffix-scan fallback, selection partitioning, and coil schema v2 regression. |
| README.md | Updates docs to describe class API, schema v2, and IMAS/OMAS pipeline; refreshes examples. |
| pyproject.toml | Switches to dynamic version sourced from bouquet.__version__. |
| examples/README.md | Adds guidance for running the synthetic examples and OFT path setup. |
| examples/D3D-like/slurm_jobs/bouquet_2000ms_submit.sh | Adds SLURM submission helper chaining array + merge. |
| examples/D3D-like/slurm_jobs/bouquet_2000ms_merge.sbatch | Adds SLURM merge job script for parallel generation. |
| examples/D3D-like/slurm_jobs/bouquet_2000ms_array.sbatch | Adds SLURM job-array script for shard generation. |
| examples/D3D-like/legacy/superseded_0p8MA_baseline/D3Dlike_Hmode_baseline_RECIPE.md | Removes superseded legacy baseline recipe. |
| examples/D3D-like/generate_baseline.py | Pins fixture generation knob (isolate_edge_jBS=True) for reproducibility. |
| examples/D3D-like/D3Dlike_Hmode_baseline_RECIPE.md | Updates baseline recipe narrative to remove obsolete references. |
| docs/gui-display-guide.md | Renames notebook plotting entrypoints to plot_bouquet. |
| docs/CHANGES_SUMMARY.md | Marks change summary as historical and updates solver-test invocation note. |
| bouquet/uncertainties.py | Updates comments to avoid proprietary shot-specific wording. |
| bouquet/schema.py | Adds schema v2 canonical constants + profile IO helpers + blob dataset resolver. |
| bouquet/sampling.py | Adjusts monotonic draw constraint handling for non-monotonic baselines. |
| bouquet/physics.py | Adds shared physics reductions (fast pressure isotropization, parallel→toroidal conversion, impurity/fast-pressure helpers, CER E_r). |
| bouquet/paths.py | Adds OFT and mesh path resolution helpers for portable environments. |
| bouquet/parallel.py | Adds process-parallel generation + SLURM emission + robust merge/baseline checks. |
| bouquet/io/ida.py | Adds IDA .cdf reader + CER channel reader with dual-layout support. |
| bouquet/gui.py | Migrates GUI browsing to discover_scan_keys and schema v2 dataset naming. |
| bouquet/filtering.py | Migrates selection/filter APIs to scan_key, improves scan-key validation, and uses schema v2 blob resolution. |
| bouquet/config.py | Introduces typed config dataclasses with JSON round-trip serialization. |
| bouquet/baseline.py | Adds Baseline abstraction + source resolution and uncertainty resolution. |
| bouquet/archive.py | Adds high-level BouquetArchive reader abstraction for schema-aware access. |
| bouquet/init.py | Exposes new public API surface, adds __version__, and updates exports. |
| architecture.md | Documents class API/baseline sources and updates wording to avoid proprietary references. |
| .gitignore | Tightens proprietary data ignores and explicitly keeps required test fixtures. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+233
to
236
| # Visualise any run from its HDF5 archive | ||
| from bouquet import plot_bouquet, plot_traces | ||
| plot_bouquet("my_run.h5", scan_value=0, mode="all") | ||
| plot_traces("my_run", scan_value="all") |
Comment on lines
+176
to
+179
| fig, axes = plot_bouquet("equilibria.h5", scan_value=1.0, mode="kinetic") | ||
|
|
||
| # All plot types at once (returns lists) | ||
| figs, axes_list = plot_family("equilibria.h5", scan_value=1.0, mode="all") | ||
| figs, axes_list = plot_bouquet("equilibria.h5", scan_value=1.0, mode="all") |
| export OMP_NUM_THREADS=1 | ||
| export OMP_PROC_BIND=close OMP_PLACES=cores | ||
| export OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 | ||
| python -m bouquet.parallel shard bouquet_2000ms_bundle.pkl $SLURM_ARRAY_TASK_ID |
| #SBATCH --mem=16G | ||
| # compute-node environment (adjust for your cluster), e.g.: | ||
| # export OFT_PYTHONPATH=/path/to/OpenFUSIONToolkit/build_release/python | ||
| python -m bouquet.parallel merge bouquet_2000ms_bundle.pkl |
Comment on lines
+371
to
+387
| if source.profile_overrides: | ||
| raise NotImplementedError("profile_overrides is not yet applied") | ||
|
|
||
| with open(source.geqdsk_path, "rb") as fh: | ||
| eqdsk_bytes = fh.read() | ||
| eqdsk = read_geqdsk(source.geqdsk_path, cocos=source.cocos) | ||
| psi_N = np.asarray(eqdsk.psi_N, dtype=float) | ||
|
|
||
| kin = _load_kinetic_profiles(source) | ||
| psi_N_kin = kin["psi_N"] | ||
|
|
||
| # kinetic profiles (native SI) interpolated onto the equilibrium psi_N grid | ||
| def to_eq(arr): | ||
| return np.interp(psi_N, psi_N_kin, np.asarray(arr, dtype=float)) | ||
|
|
||
| ne_eq, te_eq, ni_eq, ti_eq = to_eq(kin["ne"]), to_eq(kin["te"]), to_eq(kin["ni"]), to_eq(kin["ti"]) | ||
| Zeff_eq = np.clip(to_eq(kin["Zeff"]), 1.0, None) |
Comment on lines
+132
to
+134
| Opens with ``h5py.File(path, "r")`` -- the file is netCDF4/HDF5, so no | ||
| OMFIT or netCDF4 package is needed. Units are already SI; ``T_12C6`` maps to | ||
| Ti, and ``ni`` is reconstructed as ``n_e - Z_imp * n_12C6``. |
RectBivariateSpline.ev(scalar, scalar) returns shape (1,) as of scipy 1.18, and numpy 2.5 makes float() of a 1-d size-1 array a TypeError. Use .item() (valid for 0-d and (1,)) at the single scalar .ev() call site; all other .ev() uses take arrays. Verified against numpy 2.5.0 + scipy 1.18.0 (CI versions) and 2.4.2/1.17.1.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the hand-threaded functional workflow with a class-based orchestrator, and moves archives to a self-describing v2 schema.
bq.Bouquet(config).run()— typedBouquetConfig(validated at construction, JSON-serializable), stagedsetup_solver → prepare_baseline → generate → filter → exportwith inspection gates, validated per-path workflow presets (geqdsk standard l_i loop / IMAS diff+C), multi-slice sweeps viarun_slices.ReconstructionSource(g-file + IDA .cdf / p-file) andImasSource(FUSE IMAS/OMAS, incl. IDA-hybrid kinetics + EFIT01 separatrix).eqdsk/pfilenames, unified coil storage,scan/<key>/layout only, and full provenance (schema_version, package version,config_json— including merged cluster archives). NewBouquetArchivereader replaces downstream h5 tree-walking; readers accept aBouquet/archive/header/path uniformly and fail loudly on bad scan keys or pre-v2 files.ProcessPoolExecutorand SLURM job-array backends over onerun_shard; strict one-core-per-TokaMaker (nthreads=1) doctrine, slice-decorrelated seeding, fresh-shard + merge-side baseline guards, loud partial-merge accounting.read_ida_cer+radial_field_from_cer).Validation
docs/ux-review-feat-bouquet-class-api.md, which also records the design decisions — delete or fold into CHANGES_SUMMARY at merge).