Skip to content

Hydraulic network - #2

Closed
lamdangelo wants to merge 42 commits into
mainfrom
hydraulic-network
Closed

lamdangelo wants to merge 42 commits into
mainfrom
hydraulic-network

Conversation

@lamdangelo

Copy link
Copy Markdown
Owner

No description provided.

lamdangelo and others added 30 commits July 7, 2026 13:40
Stage 0 of the hydraulic field-circuit coupling: a modified-nodal-analysis
solver for a lumped pressure/mass-flow network (nodes, pipe/valve/pump
branches), sharing the CoolProp property interface and friction-factor
models with the 1D channel solver and mirroring its theta/BDF2 time
integration. This is a standalone module with no wiring into the conductor
solver yet; that bordered (Schur-complement) port coupling is stage 1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stage 1 of the field-circuit coupling: channel ends declared as ports in
the hydraulic_network section are coupled to network nodes through a
monolithic bordered (Schur complement) solve around the existing banded
system. Ports are pressure-driven overlays on the standard boundary
conditions: the port-end pressure row becomes the continuity constraint
p_port = p_node with the network node column as a border, and the port
mass flow rho_old*A*v enters the node mass balance implicitly as the
Lagrange multiplier of that constraint. The network is solved in
increment form to avoid the cancellation of a zero-pressure particular
solution, inherits the conductor's time integration method (coupled node
rows forced backward Euler under Crank-Nicolson/Galerkin), and is
initialized by a steady solve with the conductor's initial port flows.

The degenerate-limit test runs CASE_1 with the CHAN_1 outlet discharging
through an almost ideal valve into a reservoir at the imposed outlet
pressure and matches the fixed-boundary baseline to solver roundoff; the
committed CASE_1/2/3 regressions are unchanged (the uncoupled path is
untouched).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stage 2 of the field-circuit coupling: both ends of a channel (and several
channels sharing manifold nodes) coupled to a pump + bypass loop. The
stage-1 bordered solve already handled multiple ports; the changes fix the
two issues the pump loop exposed:

- Network initialization now honors the declared initial node pressures:
  branch flows are initialized from each branch's own characteristic at
  those pressures (initialize_branch_flows_from_pressures) instead of
  relocating the nodes to the network's steady operating point, which
  applied a pressure step to the coupled channel ends on the first time
  step. Residual node imbalance is resolved by the transient itself,
  through manifold compliance volumes or the algebraic junction balances.

- resolve-time warning when a ported channel has hydraulic-parallel
  partners (open interface) not ported to the same node at the same end: a
  differential plenum pressure between openly connected channels is
  strongly amplified by the transverse exchange terms (measured on CASE_1:
  a common-mode 10 Pa boundary shift responds with ~1e-5 K, the same shift
  on one channel only with ~1 K scale), so parallel groups should be
  manifolded together.

Two new integration tests: a droop-free pump with a near-ideal drain
reproduces the INTIAL = 1 imposed-pressure-drop baseline to solver
roundoff, and the boosted CASE_1 heat pulse redistributes ~70% of the
channel intake into the bypass while the pump rides up its droop curve --
the loop physics no fixed-boundary run can represent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The node pressures and branch mass flow rates of the coupled hydraulic
network are recorded every time step and written to
hydraulic_network_te.tsv in the coupled conductor's Time_evolution
directory, following the conventions of the channel inlet/outlet record
(header at initialization, rows appended in CHUNCK_SIZE batches or at the
end of the transient). Node temperatures are prescribed inputs until the
network-side energy transport stage and are not recorded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stage 3 of the field-circuit coupling: conductors coupled to the network
keep their own banded systems and meet only in the shared dense Schur
complement -- each contributes C_k A_k^-1 B_k from its stacked-right-hand-
side banded solve, one small dense solve closes all conductors and the
network together, and the network state advances exactly once per step.

To support this, the transient step is split into its natural phases:
assemble_thermal_hydraulic_system (through boundary conditions and row
scaling), the banded solve, and finalize_step (error estimate, history,
checks, solution reorganization). step() composes them for uncoupled
conductors and rejects ported ones; the simulation loop routes coupled
conductors to solve_coupled_conductors_step after preparing every
conductor, and forces the smallest proposed time step on all coupled
conductors (they share the network unknowns). The network build validates
that coupled conductors share the time integration method, and the network
state file is written once per step by the first coupled conductor.

The two-conductor test clones the CASE_1 conductor and discharges one
channel of each into a shared manifold: in the degenerate limit both
conductors match the fixed-boundary baseline and the recovery line carries
the sum of the two outlet flows. The committed regression suites are
unchanged, confirming the step() split is behavior-preserving.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stage 4 of the hydraulic field-circuit coupling: internal node
temperatures become dynamic. After every pressure/flow solve a
segregated, fully implicit step of the node enthalpy balances advances
them -- inflows carry the upstream temperature (upwind by the
just-computed flows, branch cp frozen at the upstream state), outflows
carry the node's own well-mixed temperature and cancel, zero-volume
nodes reduce to the algebraic flow-weighted inflow mixture. The
resulting temperature rate feeds the thermal-expansion part of the node
mass storage, d(rho V)/dt = V (rho kappa_T dp/dt - rho beta dT/dt), into
the next step's mass balance as a lagged source. Conductor ports report
their new-state mass flow and temperature to the balances, so back-flow
and recirculation now carry physically consistent enthalpy into the
manifolds and back into the channels through the existing inflow
temperature boundary condition. Node temperatures join the pressure and
flow columns of the network state output.

The energy transport changes the coupled pulse scenario qualitatively:
the strong heat pulse makes the channels expel hot fluid into the
manifolds, whose near-incompressible helium (thermal pressure
coefficient beta/kappa_T of order 1e6 Pa/K) pressurizes the rigid loop
by tens of kPa, pushing the pump backward through its droop curve and
venting the expansion surplus through the drain -- enthalpy the static
node temperatures used to discard silently. The pump-loop test is
renamed and rewritten around that complete physics; the degenerate-limit
regressions are untouched and new unit tests pin the algebraic inflow
mixing and the volume-node relaxation time constant to closed-form
references.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…state

Valve branches accept a reverse_linear_resistance (check-valve
semantics, switched on the previous-step flow sign); reservoirs can be
re-stated at runtime (set_reservoir_state) and internal nodes accept
external mass/enthalpy sources, so drivers can impose measured boundary
schedules and tie-ins. Node enthalpy balances carry the transported
energy through junctions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Auxiliary current/field tables can be a single csv/tsv keyed by the
component identifier in the first column, equivalent to the legacy
one-sheet-per-component xlsx layout (ragged blocks padded and trimmed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…grity

Simulation accepts a step_callback invoked at the top of every transient
iteration (drivers use it for measured boundary schedules, quench
detection and source switching); a truthy return stops the run. Runs
that stop before TEND now flush the partially filled time-evolution
buffers and reorganize the spatial files by the saves actually
performed, so no recorded data is lost and plots label the true save
times.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ents

File-driven patch exchange between winding-geometry-adjacent positions
(turn-to-turn, layer-to-layer, and across conductors) that the 1D metric
cannot see: each CSV row maps a local x-interval onto a partner
component interval and exchanges g_eff*(T_partner - T_local) per metre,
with an optional (min(T_mean, T_sat)/T_ref)^n law for the insulation
conductivity. Patch pairs listed on both partners are exactly
antisymmetric (previous-step fields on both sides). A reserved BOUNDARY
partner couples patches to a fixed-temperature reservoir (casing sink).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Opt-in strand/jacket heat sources for the field-transient losses, all
default-off and bit-identical to the prior model when disabled:
  * coupling loss (n*tau/mu0)*(dB/dt)^2*A via coupling_loss_time_constant
    (refactored to the shared per-step _field_rate helper);
  * eddy-current loss sigma(T[,B])*(dB/dt)^2*C via
    eddy_loss_geometry_constant (jacket-material sigma(T) on jackets,
    Cu sigma(T,B,RRR) on strand matrices; material dispatcher in
    properties_of_materials.electrical_conductivity);
  * NbTi hysteresis loss (2/3pi)*Jc(B,T)*d_f*|dB/dt|*A_sc via
    filament_diameter (linear in |dB/dt|: energy set by the field swing).
A FROM_FILE field profile can scale with I(t)/I0
(magnetic_field_scales_with_current), which drives the dB/dt of these
sources during a discharge. All sources (incl. the transverse coupling)
are wired into the nodal/Gauss heat-source assembly, the yaml schema and
the input loaders.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
gitignore treats '#' as a comment only at the start of a line, so
patterns like 'TDD_examples/**/ # input files ...' were literal
(unmatchable) paths and their targets showed up as untracked. Move
every trailing comment onto its own line.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A pressure-triggered by-pass with hysteresis (quench protection):
kind: relief_valve with trip_pressure / reseat_pressure /
monitored_node (default: the from-node) in the network input. Blocked
until the monitored node exceeds the trip, then a one-directional
valve with its open-state resistances until the pressure falls below
the reseat. State switches once per step on the previous-step
pressures, mirroring the check-valve convention. Replaces the fragile
driver-managed pattern (a plain closed valve mutated by a step
callback keyed on the branch NAME), which left model files
non-self-describing and silently unprotected under drivers without
the callback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two new YAML-only simulation settings: autosave_interval stores a
checkpoint of the complete evolving state every N accepted time steps
(atomic npz under Checkpoints/, two most recent kept, time-evolution
buffers flushed first so output files are always complete up to a
checkpoint); restart resumes an interrupted run from the newest
checkpoint after the normal deterministic initialization, truncating the
output files to the checkpoint time.

The checkpoint state surface covers the time-integration history
(theta/BDF2/AM4 levels), per-conductor counters and diagnostic
accumulators, coolant primary fields, solid multi-level heat-source
arrays and the dB/dt cache, the electric solution and sub-step counters,
and the full hydraulic-network state including the relief-valve
hysteresis latches. A restarted run continues bit-identically to an
uninterrupted one (regression-tested per-file by sha256 for a plain and
a network-coupled CASE_1 scenario).

Restart refuses finalized runs (post-processing consumes the raw
spatial saves), missing checkpoints, and manifests written by a
different integration method or checkpoint schema.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… conjugation

New per-conductor input HYDRAULIC_FORMULATION (auto | velocity |
mass_flow, YAML-only). The mass-flow formulation makes the channel mass
flow rate the first fluid unknown - the native variable of the hydraulic
network coupling - by an in-place similarity transform M^-1 X M of the
assembled Gauss-point flux/upwind/source blocks with the analytic
change-of-variables Jacobian frozen at the linearization state
(hydraulics/formulation.py). The fluid transient block is the identity
and the fluid load-vector rows are zero, so both are invariant; boundary
rows are overwritten after assembly and carry the native Dirichlet value
(an imposed mass flow becomes exact instead of lagged-density).

auto resolves at setup: network coupling declared -> mass_flow, else
velocity; explicit velocity on a coupled conductor warns; conductors
sharing a network must resolve identically. In mass-flow mode the port
coupling coefficient is the exact +-sigma (no frozen-density Picard
linearization), the port flow report reads the native unknown, Reynolds
numbers use the density-free mass-flow expression, and the adaptive-step
flow-slot floor is the dimensional image rho*A*(1 m/s) of the velocity
floor.

The velocity path is untouched (guard hook only) and verified
byte-identical on the full CASE_1 decks, yaml and excel. Eigenvalue
invariance {v, v+-c} and kernel-vs-dense-reference agreement are unit
tested; the coupled degenerate limit matches the fixed-pressure baseline
to the 1e-6 roundoff floor under BOTH formulations.

Known, measured limitation (documented in the tests and dev notes): the
frozen-M route omits the coefficient-derivative terms
M^-1 (dM/dt + A dM/dx) W', so during density transients the two
formulations differ at O(v*drho/rho) in the flow fields (~12-14% on the
100 W/m CASE_1 pulse), invariant under mesh AND time-step refinement;
steady/near-steady agreement is 1e-6..5e-3. Mass-conservation drift on a
flow-reversal scenario (BE-consistent quadrature): velocity 6.19e-2,
mass_flow 3.24e-2 of inlet throughput.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every run - regardless of the active hydraulic formulation - now writes
the channel mass flow rate alongside the velocity: a mass_flow_rate
column in the spatial-distribution files (and their reorganized
per-property form) and a per-channel mass_flow_rate time-evolution file.
The inlet/outlet records and the Solution files already carried both.
Existing columns are unchanged (verified column-wise byte-identical
against the pre-change CASE_1 baselines, yaml and excel).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Key reference: the new conductor input (auto resolution rule) and the two
simulation-level checkpoint settings. Developer notes record the M / M^-1
definitions, the conjugation route and its verified invariants, the
measured frozen-M transient limitation (refinement-invariant O(v*drho/rho)
flow difference, with the numbers), the flow-slot semantics table, the
checkpoint/restart design, and the restart formulation policy (fail on
mismatch, no state conversion).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rmulation

The pump-loop degenerate limit, the shared-node two-conductor test and
the heat-pulse redistribution test compare against uncoupled baselines
(or validate loop physics) that predate the formulation selection; with
auto now resolving coupled decks to mass_flow they measured the known
cross-formulation transient difference (1.2e-1 / 5.3e-4) instead of the
coupling mechanics, and the boosted 500 W/m pulse drives the mass-flow
coupled loop to an invalid negative pressure at t = 0.7 s (the mass-flow
counterpart of the velocity path's own blow-up threshold on this pulse,
documented in the dev notes). Pinning both runs of each test to velocity
restores their original meaning; the mass_flow coupled path stays covered
by the parametrized single-port degenerate test and the formulation
suite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ot the Jacobian

The Gauss-point source block multiplies the state, not its derivative, so
the chain-rule similarity M^-1 S M evaluates the source at a parasitic
state (1 + beta*T - kappa_T*p ~ 2.5 for supercritical helium at 6 K),
inflating the effective channel friction and producing an 18-27 % steady
flow deficit on the AAB51 deck. The right factor must be the Picard
secant P = diag(1/(rho*A), 1, 1), which reproduces Phi(W') exactly at
the frozen state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A hard Dirichlet on the inflow temperature injects a many-kelvin step in
one time step when a port reverses against a quench-heated header; the
consistent-mass Galerkin scheme answers with spatial ringing that can
drive the neighbouring node below 0 K. The imposed value now relaxes
from the channel end temperature toward the node temperature at the
physical boundary-cell renewal rate v*dt/dz, reducing to the hard
Dirichlet whenever v*dt >= dz. Built from current fields only, so
checkpoint identity is preserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FluidType.CONSTANT serves user-given constant properties (beta = 0, so
kappa_s = kappa_T and cp = cv) from the coolprop_interface entry points,
letting the solvers run against closed-form solutions unchanged. The
suite covers Kirchhoff splits, RLC discharge, water hammer, transmission
line and thermal lag, with runner scripts under scripts/verification.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ewton

New opt-in ELECTRIC_CURRENT_CONSISTENCY conductor flag: the quasi-static
electric solve iterates F(x) = K(|I|)x - b = 0 with a damped Newton
method so the power-law strand resistances are exactly consistent with
the solved network currents. The Jacobian replaces only the diagonal
resistance block with per-regime differential resistances (SC n*R,
sharing via implicit differentiation of the current divider, normal
R_stab, chained jacket-parallel combination), keeping K's sparsity and
the cached RCM permutation; an Armijo backtracking line search
globalizes the iteration and blockwise volt/ampere tolerances with an
E0*L voltage floor define convergence. Components store their electric
regime partition and strand-only resistance for the derivative
builders; get_current binds current_for_resistance aliases so the loop
can rebind solved currents without touching op_current semantics.
Equipotential groups containing a fixed-potential node are now grounded
joints (all members take the fixed value) instead of merged free DOFs.
Also fixes the ind_array.any() guards in strand_mixed
get_electric_resistance (an index set containing only element 0 was
silently skipped) and exempts mode-none strands from imposed-current
bookkeeping.

Replaces the Picard fixed-point loop, which oscillates without
converging (100-cap, ~4 min/step) once a real quench creates an
extended dissipative zone at power-law exponent n = 20.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The quasi-static path never advanced conductor.electric_time (only the
transient sub-stepping loop does) and never rebuilt the nodal
current-source vector after initialization, so CURRENT_IS_FROM_FILE
waveforms were interpolated at t = 0 forever: time-varying transport
currents - dump/discharge templates in particular - were silently
frozen at their initial value for every steady-solver run. Set the
electric clock to the thermal time before the steady solve and rebuild
the current source via eval_total_operating_current after the
operating-condition update, mirroring the per-sub-step sequence of the
transient loop. Constant-current decks are bit-identical (the rebuild
rewrites the same two boundary entries).

Found by a dump-time-constant scan whose tau = 0.15 s and tau = 0.65 s
transients were bit-identical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…search

A wildly scaled Newton direction near a singular Jacobian (~zero
transport current during a dump tail) crashed the run twice over:
first scipy.bisect probes the current-divider residual with Python
floats, where i**n raises OverflowError instead of saturating; with
that saturated, the stalled line search then adopted a non-finite
iterate whose Joule power crashed the thermal banded solve. The
divider residual and its derivatives now compute through np.float64
under errstate(over=ignore) so overflow yields a sign-correct inf, the
line search rejects non-finite trial norms, and if every backtracked
trial is non-finite the solver keeps the best iterate (currents
rebound for consistent resistances) with a warning; a non-finite
Newton step bails out the same way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New SimulationSummary writes simulation.log at run end (runtime timers,
feature flags, per-solid extrema) plus a per-step hotspot_log.tsv with
per-strand max temperatures and normal-zone lengths (from the electric
regime partition). A Normal zone section reports onset, maximum length
and the normal-zone propagation velocity, fitted as half the growth rate
over the 25-75 % window and restricted to t past heater-off with growth
beyond the disturbance-made baseline - heater ignition of the heated
zone otherwise reads as an absurd 60-90 m/s front. Restart re-seeds the
extrema from the restored state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The final stop-time save has no scheduled num_step_save slot, so the
spatial reorganization indexed past the performed saves and aliased the
step-0 file (unwritten slots are 0). _performed_saves now returns the
per-save step numbers, appending the final step counter for the
stop-time save.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All integration methods now share the simulation_results/<name>/...
folder instead of per-method BE/CN/AM4/GAL/BDF2 subtrees (the checkpoint
manifest already guards restarts against a method change), and the
legacy GUI results root is no longer created eagerly on construction -
headless runs override it with the model directory anyway.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
get_registry's suffix heuristic sent existing directories like
"..._return1.8L" to their parent, resolving the registry against the
wrong folder. An existing directory is now always taken as-is; the
heuristic only serves (possibly non-existent) workbook paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Figures and tables under analysis/sensitivity/results* are exempt from
the global *.png / *.csv rules; the simulation cache and per-sample run
clones stay untracked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lamdangelo and others added 12 commits September 1, 2026 10:30
…ts fit range

The NIST copper magnetoresistance correction is a log10 polynomial fitted
for xxx >~ 1. Extrapolated to vanishing field (a field map scaled by an
exponentially dumped current) it diverges, 10**aaa overflows to inf and
poisons the copper conductivity and the thermal system matrix. The
correction at xxx = 1 is only ~2e-3, so it is skipped below that, in
copper.py and in the duplicate inside magnesium_diboride.py.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ty dispatch

electrical_resistivity_of accepts 'ss' / 'stainless_steel' and returns
the existing stainless-steel correlation, so jackets can be used in the
eddy-current loss and the resistance floor like copper and Al6063.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ernel

assemble_system_matrices looped over (local row, local column) pairs with
fancy indexing per pair. The scatter is now one strided-view addition per
local row in pure numpy, and a compiled numba kernel (about 2.5x faster at
10^4 elements) when numba is importable. Both reproduce the original
accumulation bit for bit (each band entry receives at most two
contributions), which tests/test_band_assembly.py checks against the
reference loop. numba is added to the dependencies; the numpy scatter is
the fallback without it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e -1)

ConductorMesh accepts a mesh_file (grid key MESH_FILE, resolved against
the deck directory) for ITYMSH = -1: a one-column text file with the node
z coordinates, validated (finite, strictly increasing, 0 to the conductor
length, MAXNOD when given), with NELEMS derived from the file. The
refinement keys may be omitted on such a deck. Straight and helicoidal
component coordinates follow the file nodes; the electric topology is
built once since the mesh is static. The legacy per-component
external_grid workbook path is kept as the fallback when no mesh_file is
given.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…mulation

A second route to the mass-flow unknowns, selected by the boolean
conductor input explicit_mass_flow_formulation (EXPLICIT_MASS_FLOW_
FORMULATION, default false): the Gauss-point flux-Jacobian, upwind and
fluid-row source blocks are assembled from the written-out (mdot, p, T)
coefficients in hydraulics/mass_flow_equations.py instead of conjugating
the velocity-form blocks at runtime. The resolved formulation is the new
internal HydraulicFormulation.MASS_FLOW_EXPLICIT (not an input value);
MASS_FLOW_FORMULATIONS groups both mass-flow routes wherever the first
fluid slot is the mass flow rate (boundary conditions, initial seeding,
network ports, error controller floors, solution reorganization).
OPENSC2_DEBUG_CHECKS=1 asserts the heat-source consistency
q_p = phi rho c_v q_T on the assembled block.

Checkpoints now store the native mass flow rate (restored, or rebuilt
from v and rho(p, T) for older archives): a restart under an evolving
flow otherwise refreshed the velocity and the Reynolds number from the
stale initialization value on its first step.

With the flag off every deck is bit-identical. Unit tests check the
eigenstructure and the similarity to the transform route, the
integration tests the steady channel, the pressure pulse and CASE_1;
docs/mass_flow_formulation_notes.md records the measured differences
(stabilisation block only, open TODO).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… under network coupling

tests/verification/formulation_suite/ compares the velocity (v, p, T) and
the explicit mass-flow (mdot, p, T) formulations on a W7-X NbTi channel,
three conductors in parallel between two volumes fed through linear
valves: steady flow split, low-Mach acoustic step, symmetric and
asymmetric heating with Richardson extrapolation, and a manufactured
solution. The latter uses the new opt-in Conductor.fluid_source_callback
hook, which adds nodal sources to the fluid rows of the Gauss-point source
vector and is None on every production path. Solver-free references
(steady compressible network ODE, linear transmission line with
characteristic closure, MMS residual) live in tests/reference/ with their
own unit tests. scripts/run_formulation_matrix.sh runs the full matrix,
scripts/make_plots.py draws figures/ from results/ (both ignored).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…solve

New conductor operation electric_resistance_floor (ELECTRIC_RESISTANCE_
FLOOR, default 0 = off). Below Ic the power law gives R -> 0, so the split
of the transport current among parallel superconducting strands or
stacks has zero residual for any loop current and the steady Newton solve
drifts from step to step until a loop current exceeds Ic (cold-DL drift
seen on the HELIAS-VIPER 2D decks). With the floor the Gauss-element
resistance of the superconducting regime is bounded below by
floor * E0 * L_e / Ic, which pins the split to the resistive share and
leaves the sharing and normal regimes untouched; the differential
resistance is R_floor on floored elements. Applied by the mixed-strand
and the stack resistance builders; tests/test_resistance_floor.py checks
the floor, the derivative and the no-op without the key.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…-stack hysteresis

Coupling loss: coupling_loss_time_constant (n*tau) may now be a list, one
entry per loop family, with the new key coupling_loss_relaxation_time
(tau, scalar or list). With tau > 0 the coupling magnetisation follows
tau dM/dt + M = -(n*tau/mu0) dB/dt (backward Euler over the field-rate
sampling interval) and q = mu0 M^2/(n*tau) A, so an exponential dump
deposits the fraction n*tau/(tau + tau_d) of B^2/2mu0 instead of
n*tau/tau_d; tau = 0 keeps the lumped formula bit for bit. Optional
coupling_loss_copper_scaling scales n*tau with rho_Cu(T_ref,B_ref)/
rho_Cu(T,B) (NIST, component RRR). The per-family magnetisation and the
rate interval are checkpointed.

Hysteresis: tape_hysteresis_loss (STACK only) replaces the round-filament
Bean loss by the thin-strip loss of the tape stack, q = min((2/pi) N Ic
w/4, B A_stack/mu0) |dB/dt|, bounded by full shielding of the stack. The
strand formula gains the ybco Jc dispatch and a cap of the magnetisation
scale at the local field. The stack and stabilizer loaders now read the
optional AC-loss and B_SCALES_WITH_CURRENT keys (they silently defaulted
them before), and the end-of-run feature checklist accepts lists.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… fields

The proportional field models (LINEAR_WITH_TRANSIENT and from-file with
magnetic_field_scales_with_current) read the conductor total operating
current. The interleaved current/field loop showed the first strand only
its own share, a spurious 4x field collapse on the 4-stack HELIAS decks.
The current share now skips strands without an imposed current
(CURRENT_NOT_DEFINED or no op_current yet during initialization) and
falls back to 1 when none exists.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A run stopped cleanly by its driver (burnout guard, machine shutdown) has
had its raw per-step spatial saves consolidated and deleted by the
post-processing, so its checkpoint was refused. When the consolidated
files exist the restart is now accepted: their time-labelled columns are
truncated at the checkpoint time and the continuation's post-processing
appends its own saves to them (conductor.restart_after_finalization);
reorganize_heat_sd no longer assumes the step-0 file exists. The restart
is refused only when neither the raw nor the consolidated files remain.
A new test checks that the two-leg run is bit-identical to the straight
run.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Conductors exchanging heat through transverse coupling patches read each
other's state every step. With independent adaptive steps a fast-stepping
quenched conductor lagged the others in its own clock while the
simulation clock followed the largest step. When any transverse coupling
is declared every conductor now shares the smallest proposed step, as
the hydraulically coupled group already did.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@lamdangelo lamdangelo closed this Oct 2, 2026
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