Files
Entropyk/docs/architecture/dof_balance_and_real_machine_plan.md
sepehr 5bd180b5b8
Some checks failed
CI / check (push) Has been cancelled
Snapshot WIP: solver HP epic progress, BPHX/HX physics, BMAD skill refresh.
Capture uncommitted solver robustness work (regularization, domain errors, linear solver lifecycle, tube DP/MSH), web workbench updates, and synced BMAD skills across IDE agent folders before starting BPHX pressure-drop.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-19 16:35:31 +02:00

10 KiB
Raw Blame History

DoF Balance & Real-Machine Simulation Plan

Date: 2026-07-17
Status: Active implementation plan
Audience: architects + implementers
Related: docs/audits/heat_exchanger_architecture_audit.md, ADR-0001, mission HX


0. Non-negotiable principles

  1. A thermodynamic system is a square algebraic problem: [ n_{\text{equations}} = n_{\text{unknowns}} ]
  2. Fixing a quantity consumes one degree of freedom. Somewhere else an unknown must be freed (or an equation dropped). There is no free lunch.
  3. Parameters are not states. If a value is not in the Newton state vector, it is either (a) a true physical constant/input boundary, or (b) a cheat that must be labeled and forbidden in system mode.
  4. No bricolage. Silent clamping, fake residuals, scalar secondary streams that pretend to be a water loop, and design-point pressure pins are not “real machine” simulation.
  5. Hard gate before solve. Imbalance is an error, not a warning.

1. Diagnosis — what the codebase does today

1.1 State vector (unknowns)

Post CM1.4 layout (System::finalize):

[ ṁ_branch_0 … ṁ_branch_{B-1} | P_e0, h_e0, … P_e{|E|-1}, h_e{|E|-1}
  | inverse_controls | coupling_Q | saturated_(u,x) pairs | free_actuators ]
  • Series edges share one unknown per branch (topology.rs).
  • Full length: full_state_vector_len().

1.2 Equations

n_eq = Σ component.n_equations()
     + n_constraints
     + n_thermal_couplings
     + 2 × n_saturated_controllers

Free actuators contribute unknowns only; the owning component supplies the closing residual inside n_equations().

1.3 Existing Fix/Free mechanisms (good)

Mechanism Fix (equation) Free (unknown)
BrineSource P,h,(ṁ) Dirichlet on edge — (boundary input)
BrineSink P_back Dirichlet on P h free (emergent outlet)
Anchor + constraint +1 thermo spec must free elsewhere
add_constraint + BoundedVariable +1 measure residual +1 control
add_free_actuator component residual +1 state slot
superheat_regulated drops SH residual EXV opening closes it
Emergent HX outlet closure pins P via balance design-point P removed from compressor/EXV

1.4 Cheating patterns (forbidden for real-machine system solve)

Cheat Where Why it is wrong
Secondary as scalars T_sec, C_sec without edges FloodedEvaporator coupled path Water loop has no First-Law residual; h_sec,out never emerges
target_quality = 0.7 + quality residual Flooded default +1 equation without free actuator / level unknown
Fake residual -target_quality when quality fails Flooded Hides config error
unwrap_or(inlet_p_idx) for mass index Cond/Evap/Flooded Jacobian writes energy ∂/∂ṁ into pressure column
Design-point P pins (non-emergent) compressor/EXV legacy Pressures not free to respond to secondary
p_in > 10_000 hard model switch several HX Discontinuous Jacobian near seed
UI historically “hiding” secondary edges obsolete HANDOFF claim Must keep live edges in graph
Rating rate(p) used as if system solve qualification APIs OK only when labeled rating mode

1.5 Honest water-cooled chiller budget (reference)

Ref loop:  Comp ⇄ Cond ⇄ EXV ⇄ Evap     → 1 ṁ + 4×(P,h) = 9
CW loop:   BrineSrc → Cond.sec → Sink   → 1 ṁ + 2×(P,h) = 5
CHW loop:  BrineSrc → Evap.sec → Sink   → 1 ṁ + 2×(P,h) = 5
Total unknowns = 19

Eqs (emergent, same-branch):
  Comp 2 + Cond (3 thermo + 1 sec energy) 4 + EXV 1
  + Evap (3 + 1 sec) 4 + 2×(BrineSrc 3 + BrineSink 1) = 19

19 = 19. This is the template. Flooded + dual circuit must keep the same discipline.


2. Fix / Free ledger (the core abstraction)

2.1 Rules

USER FIX (non-boundary)  ⇒  must FREE something OR DROP a closing residual
BOUNDARY FIX (source)    ⇒  normal: physical input of the machine
OUTLET CLOSURE (SH/SC/x) ⇒  either free actuator elsewhere OR mode drop of that residual

Examples:

User intent Fix Free / drop
Fixed chilled-water supply T load-side T_set or capacity constraint free compressor speed or free EXV / free ṁ_ref
Fixed superheat 5 K SH residual OR anchor free EXV opening (superheat_regulated + orifice)
Fixed flooded level level residual free EXV / charge related
Fixed circuit capacity capacity constraint free speed or free slide valve
Quality target without actuator ILLEGAL refuse at validate

2.2 Equation roles (semantic)

pub enum EquationRole {
    MassConservation { stream: StreamId },
    MomentumOrPressureDrop { stream: StreamId },
    EnergyBalance { stream: StreamId },
    OutletClosure { kind: OutletClosureKind }, // SH | SC | Quality | Level
    BoundaryDirichlet { quantity: StateQuantity }, // P | H | M
    ActuatorClosure { name: String },
    CouplingDuty,
    ControlTracking { name: String },
    Unspecified, // legacy only; must disappear over time
}

2.3 Unknown kinds

pub enum UnknownKind {
    BranchMassFlow { branch_id: usize },
    EdgePressure { edge: usize },
    EdgeEnthalpy { edge: usize },
    InverseControl { id: String },
    CouplingHeat { index: usize },
    SaturatedActuator { index: usize },
    SaturatedIntegrator { index: usize },
    FreeActuator { id: String },
}

2.4 System-level report

DofReport {
  n_unknowns, n_equations, balance: Balanced | Over(k) | Under(k),
  per_component: [(name, n_eq, roles)],
  free_actuators, constraints, couplings,
  diagnostics: ["FloodedEvaporator quality_control +1 eq without free actuator", ...]
}

Hard fail when balance != Balanced before Newton.


3. Implementation phases (ordered, each leaves workspace green)

Phase D0 — Ledger + hard gate done (2026-07-17)

  • Study + this plan document
  • crates/solver/src/dof.rsDofReport, SystemDofBalance, errors
  • crates/components/src/dof.rsEquationRole + Component::equation_roles()
  • System::dof_report() / System::validate_system_dof() / total_equation_count()
  • Hard fail in finalize() on over-constrained (enforce_dof_gate, default on)
  • Full square check via validate_system_dof() (production path)
  • Roles on FloodedEvaporator, Condenser, BrineSource/Sink, Anchor
  • Unit tests: crates/solver/tests/dof_balance.rs (balanced / over / under)

Phase D1 — FloodedEvaporator honesty core done (2026-07-17)

  • 4-port live secondary via set_port_context (ports 2/3), flow_paths=[(0,1),(2,3)]
  • Secondary energy residual ṁ_sec(h_outh_in) + Q = 0
  • coupled_ready requires live secondary edges in system mode
  • Scalar with_secondary_stream documented as rating-mode only
  • Remove mass→pressure index fallback; error if ṁ index missing
  • Quality residual: return Err if quality uncomputable (no fake residual)
  • with_quality_control documents DoF cost
  • Equation roles complete for flooded
  • CLI example: flooded + BrineSrc/Sink secondary loop
  • Replace quality target with FloodedEvaporatorControl enum

Phase D2 — Shared DoF diagnostics in CLI + UI done (2026-07-17)

  • CLI run hard-fails on non-square validate_system_dof after finalize
  • SimulationResult.dof summary (n_eq, n_unk, balance, ledger text)
  • CLI validate builds system and reports DoF
  • Example chiller_flooded_4port_watercooled.json
  • Web UI status bar: live eqs vs unk estimate
  • Web UI Fix/Free badges on parameters + secondary wiring warnings
  • validateConfig requires live secondary ports for 4-port HX

Phase D3 — Zero-flow safe residuals (linked to HX mission §7)

  • Reuse entropyk_core::smoothing
  • Activity blend with documented scales; Jacobian parity tests at m=0

Phase D4 — Multi-circuit shared vessel (ADR-0001)

  • Solver contract for multi-circuit ports on one component
  • MultiCircuitFloodedEvaporator with N independent ref balances + shared secondary energy
  • OFF circuit: zero duty, fixed eq count, no singularity

Phase D5 — Geometry / correlations / product fixtures

  • As HX mission Phases BF, always under DoF ledger gate
  • No geometry mode ships without equation/unknown inventory table

4. Component checklist (every new residual)

Before adding a residual equation:

  1. Name its EquationRole.
  2. State which unknown it closes (or which free it requires).
  3. Update n_equations() and Jacobian.
  4. Add a DoF unit test (balance before/after).
  5. Never add a residual “to make it converge”.

Before adding an unknown (actuator, coupling Q, …):

  1. Name its UnknownKind.
  2. Provide the closing residual (component or system).
  3. Bounds if physical actuator.

5. Acceptance criteria (robust machine simulation)

A configuration is accepted as real-machine system mode only if:

  • validate_system_dof() passes (hard)
  • Secondary fluids of every HX are live edges (or explicitly marked rating-only)
  • Pressures of interest are emergent (no design-point pin unless documented fixed-P study)
  • First Law closes at cycle level (energy_transfers sum ≈ 0 within tolerance)
  • No fake residual on missing properties
  • Jacobian analytic except documented property derivatives
  • Staging / zero flow keeps constant equation count

6. Relation to inverse control

Inverse (Eurovent / calibration) already has a partial DoF check. It is incomplete (misses free actuators, couplings, saturated controllers). The new validate_system_dof supersedes it as the single square-system gate; inverse mapping remains the Fix/Free pairing API for user constraints.


7. Immediate deliverables (this implementation slice)

  1. This plan document.
  2. solver::dof module + public API.
  3. Hard DoF validation wired to System.
  4. Flooded evaporator path toward live secondary (D1 start).
  5. Tests proving overconstrained quality control is detected.

Last updated: 2026-07-17