Files
Entropyk/HANDOFF.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

22 KiB
Raw Blame History

Entropyk — Development Handoff

Purpose. This document hands off the current state of Entropyk to a new coding tool / session. It summarizes what was done, why it was done that way (key technical decisions), and what remains to do, so work can resume without loss of context.

Conventions (from AGENTS.md). Communicate with the user (Sepehr) in French; write all code / docs / commit messages in English. Rust package manager = cargo; Python = uv (venv at repo-root .venv).

Last updated: 2026-07-07


1. Project overview

Entropyk is a Rust steady-state thermodynamic-cycle simulation library for refrigeration, heat pumps and HVAC. The goal (Sepehr's) is to make it a best-in-class HVAC design + control simulator — very accurate and very fast — to replace an internal tool. Hard rule from the user: no "bricolage" (no hacks such as auto-injecting design-point temperatures). Every model must be genuinely physically coupled and must respect the First Law.

Workspace layout

entropyk/
├── crates/
│   ├── core/          # Strong types (Pressure, Temperature, Enthalpy…), smoothing primitives
│   ├── components/    # Thermodynamic components (Compressor, HX, valves, ReversingValve…)
│   ├── fluids/        # Fluid backends (CoolProp, tabular, incompressible)
│   ├── solver/        # Newton-Raphson / Picard / Homotopy, System topology, DoF, controls
│   ├── entropyk/      # Main library facade (System, SystemBuilder, rating)
│   ├── cli/           # CLI: run / qualify / rate / scop / seer commands
│   ├── vendors/       # Vendor data parsers
│   └── bindings/      # Python (PyO3), C (cbindgen), WASM  — DO NOT build in this env
├── apps/web/          # Dymola-style web UI (SvelteKit + Vitest)
├── _bmad/ , _bmad-output/   # BMAD methodology workflows + artifacts
└── AGENTS.md          # Project instructions (READ FIRST)

2. What has been DONE (verified)

2.1 Genuine secondary-side coupling of heat exchangers (ε-NTU)

The core goal: HX respond to secondary (water / brine / air) temperature & flow.

  • Condenser, Evaporator and FloodedEvaporator brought to the same coupled ε-NTU level. Secondary fields (secondary_inlet_temp_k, secondary_capacity_rate), builders with_secondary_stream / set_secondary_stream, helpers coupled_ready, coupled_duty, effectiveness, cond_temperature / evap_temperature.
  • Coupled residual path active only when a secondary stream is set AND indices resolved AND P_in > 10 kPa (otherwise legacy path — backward compatible, DoF unchanged). Q = ε·C_sec·ΔT, ε = 1 exp(UA/C_sec) (C_min = C_sec for a phase-changing refrigerant). Analytical Jacobian (dT/dP via central FD).
  • Files: crates/components/src/heat_exchanger/{condenser,evaporator}.rs.

2.2 Emergent pressures (end-to-end coupling) — the "vrais modèles échangeurs" epic

Condensing / evaporating pressures are now emergent from the HX↔secondary balance instead of fixed by compressor design points. Opt-in emergent_pressure mode on each component:

  • Compressor (comp-emergent): mass-flow closure ṁ = ρ_suc·V_s·N·η_vol; r0 = ṁ_state ṁ_calc.
  • Condenser (cond-emergent) / Evaporator (evap-emergent): 3 thermo eqs (ΔP, energy balance, outlet closure), n_equations +1.
  • EXV (exv-emergent): drops the P_evap fix, keeps only isenthalpic h3=h2, n_equations 1.
  • Integration test: 4-component emergent loop converges; varying secondary T/flow changes COP/capacity; DoF balanced (9=9).

2.3 Modular control architecture (arch-1 … arch-6) — ALL DONE

  • arch-1 Saturated-PI controller wired into System DoF (2 residuals + 2 unknowns per loop; actuator u → CalibIndices, measurement y ← measure_output; co-solved).
  • arch-2 Typed ports PortKind{Refrigerant, Secondary, Mechanical, Signal} + edge validation by kind; Signal ports for control wiring.
  • arch-3 controls[] schema + control-block library (Min/Max/Setpoint/Limiter), CLI design/control command co-solving design + control.
  • arch-4 subsystems/instances hierarchical templates (reusable parameterized assemblies flattened to graph; multi-circuit A/B).
  • arch-5 unified model IR across CLI / Python / WASM / UI (schema v2 loader, v1-compatible).
  • arch-6 Physical actuators — all 6 done, each with exact analytic Jacobian (FD only for property derivatives) and CLI wiring + example + integration test:
    1. EXV orificeṁ = Cd·A(θ)·√(2ρΔP), opening as bounded solver var.
    2. Fan head-pressure — air-cooled condenser: secondary air capacity rate scales with fan speed; fan speed controls condensing pressure. UA_eff=(1λ)·UA_nom.
    3. Slide-valve (screw compressor) — capacity modulation of swept volume.
    4. Liquid-injection EXV — economized screw, controls discharge temperature (factor-actuator: closed by a controls[] loop, does NOT grow n_equations).
    5. Flooded-condenser level (cond-level) — head-pressure via liquid level; mirrors the fan actuator, mutually exclusive with it (shares fan_actuator_idx). Exact flood Jacobian ∂r1/∂λ = +UA_nom·(T_condT_sec)·exp(UA_eff/C_sec).
    6. Four-way reversing valve — see §2.6.
  • p0a real constraint measurement via fluid backend (removed mocked superheat / subcooling / capacity in system.rs).
  • p0b compressor variable-speed capacity actuator (bounded speed_hz DoF).

2.4 Modular seasonal rating (SEER / SCOP / IPLV / NPLV / ESEER) — DONE

Per Sepehr's request that the standards be swappable when norms change:

  • rate.rs — AHRI 550/590 IPLV/NPLV/ESEER: re-solve base scenario at each part-load point overriding secondary temps + compressor speed; rayon-parallel points.
  • seasonal.rs — EN 14825 bin method (SCOP/SEER): per-bin coupled re-solve driven by outdoor temp, linear building load line, Cd cycling degradation, backup heater. Modular climate selection (BinClimateStandard) so climates/standards are pluggable.
  • CLI subcommands rate, scop, seer + example configs + unit & integration tests.
  • French CLI_TUTORIAL section for these commands.
  • Full documentation written (per user request).

2.5 Circuit on/off staging — DONE

  • CircuitConfig.enabled: bool (#[serde(default = "default_true")]) in crates/cli/src/config.rs. A disabled circuit's components / edges / thermal couplings are never added to the System → excluded from the solve entirely (zero mass flow / duty). Physically-correct steady-state for an unenergized circuit, not a hack.
  • 5 circuit-iteration loops in run.rs skip-guarded; all-disabled → early rejection.
  • Example chiller_r134a_dual_circuit_staging.json. Verified e2e: both on = 15.139 kW / COP 4.379 (8 edges); circuit B off = 7.569 kW (4 edges) = exactly half.
  • 2 CLI integration tests pass.
  • NOTE: the component-level OperationalState{On,Off,Bypass} in state_machine.rs exists but is dead code w.r.t. the solver — staging is done at CLI/config level, not via that enum.

2.6 Four-way reversing valve (arch-6 #6) — DONE

  • crates/components/src/reversing_valve.rs: ReversingValve + ReversingMode{Cooling, Heating}. Deterministic inline 2-port pass-through:
    • r0 = P_out (P_in ΔP), ΔP = ΔP_base + k·ṁ·|ṁ|
    • r1 = h_out (h_in + Δh_leak)
    • r2 = ṁ_out ṁ_in (dropped when the inlet/outlet share a mass-flow index)
    • n_equations = 2 or 3. Exact analytic Jacobian (∂ΔP/∂ṁ = k·2|ṁ|).
    • DoF-neutral: emits a pressure relation (not a fix), so it works unchanged in both fixed-pressure and emergent-pressure cycles.
  • Key modeling decision (honest, no bricolage): the validated cycle uses Δh_leak = 0 (isenthalpic ΔP-only). A non-zero single-stream leak enthalpy injects ṁ·Δh_leak with no matching source → it broke machine-level First Law (measured 0.077 kW imbalance, wrong sign on the discharge line). The rigorous discharge→suction leak needs a bypass-mass split (two streams) — deferred to a future topology layer. The ΔP alone (~0.25 bar valve-body drop) already reproduces the cited ~13 % COP/capacity penalty and closes the First Law exactly. The leak_enthalpy parameter is retained + documented for the future bypass-mass model.
  • CLI arm "ReversingValve" | "FourWayValve". Example heatpump_r410a_reversing_valve.json (reversible R410A air→water HP, heating mode, RV on discharge line, all emergent). Verified e2e: converges 5 iters, ΔP 39.068 → 38.788 bar, Q_cool 9.918 kW / Q_heat 15.486 kW / Power 5.491 kW, EER 1.806, First Law closes. 5 unit tests + 1 CLI integration test (test_reversing_valve_imposes_pressure_drop_penalty).

2.7 Fluids accuracy fix — DONE

  • entropyk-fluids :: tabular_backend::tests::test_tabular_vs_coolprop_accuracy was failing (embedded r134a.json density >1 % off vs CoolProp). Fixed properly by regenerating the table from CoolProp (not by widening epsilon).

2.8 Web UI (apps/web) — DONE (earlier turns)

  • Secondary ports (secondary_inlet / secondary_outlet, labeled htf·in / htf·out) on all HX; resolveSecondaryStreams maps a connected boundary source into HX secondary params and hides secondary edges from the solver graph.
  • Dymola-style light UI, grid snap, node rotation. Vitest suite green (61 tests).

2.9 Workspace warning cleanup — 0 warnings

  • cargo build --workspace --all-targets (bindings excluded) emits 0 warnings.
  • Notable real fix: crates/components/src/brine_boundary.rs mod tests was missing #[cfg(test)] (compiled into normal builds). Migrated deprecated TEMP_MASS_FLOW_SEED_KG_SDEFAULT_MASS_FLOW_SEED_KG_S across 8 solver test files.

3. Key technical patterns & conventions (carry these forward)

  • Free-actuator DoF pattern: +1 unknown appended at the END of the state vector. Setpoint actuators (slide / fan / cond-level) grow n_equations() by 1; factor actuators (liquid-injection) do NOT — a controls[] loop closes them.
  • Two Component conventions coexist:
    • Port-based (e.g. ExpansionValve<Connected> reads self.port_inlet).
    • Index-based (IsenthalpicExpansionValve, Condenser, ReversingValve): set_system_context(offset, &[(m_idx,p_idx,h_idx); edges]), edge[0]=inlet, edge[1]=outlet, then reads state[idx]. The emergent CLI cycle uses the index-based convention. IsenthalpicExpansionValve is the template for new inline components (its get_ports() returns &[]).
  • same_branch_m: true when inlet_m_idx == outlet_m_idx (series-branch collapse, CM1.4) → drop the mass-conservation residual.
  • Jacobian policy: exact analytic required. FD allowed ONLY for property derivatives (dT/dP, dρ/dP). Never numerical-diff a whole residual.
  • Emergent vs fixed pressure: emit pressure relations (ΔP), not absolute pressure fixes, to stay DoF-neutral across both topologies.
  • First Law is a hard gate: System::cycle_performance sums every component's energy_transfers; any unsourced enthalpy injection breaks closure. Do not add energy without a physical source (this is what killed the leak-enthalpy model).
  • Smoothing: use entropyk_core::smoothing (smoothstep, smootherstep, cubic/quintic_blend, smooth_max/min/abs/clamp) for C¹/C² correlation transitions — never ad-hoc if/tanh — to keep the Newton Jacobian continuous.
  • CRLF gotcha: example JSONs are written with CRLF on Windows; test string-patches using \n fail silently. Split on a distinctive marker (e.g. "Circuit B") then replacen.
  • edit tool gotcha: verify match-arm headers ("Pipe" => {) survive edits; re-view files after risky edits.
  • AGENTS.md integration protocol: a new component must be integrated across Rust core+solver, CLI, Python (PyO3), and WASM. Several new components currently have Rust core + CLI only (bindings pending — see §5).

4. Build / test / run

# Build & test core crates (bindings excluded — they DON'T build in this env)
cargo build --workspace --all-targets --exclude entropyk-python --exclude entropyk-c --exclude entropyk-wasm
cargo test  -p entropyk-core -p entropyk-components -p entropyk-solver -p entropyk-cli -p entropyk

# Targeted (fast) examples
cargo test -p entropyk-components reversing_valve
cargo test -p entropyk-cli --test single_run test_reversing_valve_imposes_pressure_drop_penalty
cargo run  -q -p entropyk-cli -- run --config crates/cli/examples/heatpump_r410a_reversing_valve.json

# Frontend
cd apps/web && npm run test        # Vitest
cd apps/web && npm run dev         # http://localhost:3000
cargo run --package entropyk-demo --bin ui-server   # backend http://localhost:3030

# Python bindings (uv only — never pip)
uv run maturin develop --release
uv run pytest crates/bindings/python/tests/

Guardrails: NEVER cargo test --workspace (pyo3 fails on Python 3.14). CLI integration tests are slow (~12 min each, real CoolProp solve) and self-skip when CoolProp is unavailable (if status != Converged { return; }). cargo test accepts only ONE positional TESTNAME filter per invocation.


5. What REMAINS to do (prioritized)

5.1 In progress / immediate

  • adv-override-control (DONE 2026-07-07) — Modelica-style override/selector control + offset-free saturated-PI. (1) Fixed the control law to the canonical offset-free form r_y = K(y_refy) (x S(x)) (was x + S(x), a droop with steady-state offset that forced fragile high gains → the reported convergence/slowness). (2) New inverse/override_network.rs: one actuator arbitrated by several Objective{output,setpoint,gain,combine:min|max} folded through C^∞ softMin/softMax with exact analytic selector weights (Jacobian- smoothing; literature-backed robustness). (3) SaturatedController gains opt-in objectives+alpha; system.rs residual/Jacobian branch for network mode (∂E/∂col = Σ w_i(gain_i)∂y_i/∂col). (4) Config controls[].objectives[]+alpha (schema-exposed), run.rs wiring. Example chiller_r134a_exv_override.json; unit + FD-Jacobian + CLI integration tests (286 solver tests pass). Spec: _bmad-output/implementation-artifacts/spec-advanced-override-control.md. ROBUSTNESS (done): warm-started ADAPTIVE activation continuation (SaturatedController::activation λ walked 0→1 by run.rs: primary-only baseline → full network). The λ-step now uses the same predictorcorrector control as strategies::homotopy — start 0.5, halve on any failed solve (retry from the last good λ), grow on success, floor MIN_LAMBDA_STEP=5e-3 — so the walk auto-refines through the hard switching region and takes big steps elsewhere; each step is seeded from the previous solution. CLI also wires solver.max_iterations/tolerance into every solver stage (was capped at 100). Measured effect: markedly wider convergence range vs the old fixed [0,0.5,1] schedule (aggressive EXV overrides now converge through many more intermediate λ). Known physical limit (not a solver defect): with a superheat_regulated evaporator and the EXV opening as the only actuator, forcing capacity well below the natural duty drives ∂capacity/∂opening → 0 → the plant Jacobian goes singular (loss of controllability). The robust demonstrated path uses a well-conditioned actuator (compressor f_m), covered by the passing test test_override_capacity_limit_takes_authority_via_continuation. NEXT (optional): a second actuator (e.g. compressor speed) to restore controllability for deep EXV duty cuts; optional setpoint scheduling (Table1D/2D/Poly) per the PPTX/CombiTable patterns.
  • p0b-exv-opening (DONE 2026-07-07) — controllable EXV opening regulates evaporator superheat. Evaporator gains an opt-in superheat_regulated mode (drops the emergent outlet-closure r2 → superheat emerges from the ε-NTU energy balance); a controls[] saturated loop with actuator.factor:"opening" drives the opening (wired to CalibIndices.actuator), the EXV orifice residual closes it, DoF stays square (orifice +1, drop r2 1, saturated +2/+2). Real measure_output(Superheat) added. Example chiller_r134a_superheat_control.json, integration + unit tests, + fail-fast config guards. Spec: _bmad-output/implementation-artifacts/spec-p0b-exv-opening.md.
  • p0c-json-schema (LARGELY DONE — audit before extending) — the CLI schema command already emits the canonical Model IR JSON Schema (emit_schemaScenarioConfig::json_schema() via schemars); controls[] (saturated loops) parse and co-solve; bounded control variables are expressed in actuator{initial,min,max}; parse_component_output supports capacity/superheat/subcooling/saturationTemperature/ massflow/pressure/temperature. REMAINING (optional): a dedicated top-level hard-inverse constraints[] array + a distinct design/control CLI command (today run co-solves everything).
  • p0c-ui-align — make the control schema map cleanly to the apps/web node/port model so the UI can generate/consume it. (User note: a UI is being developed in parallel — keep schema UI-friendly.)
  • p0-tests-docs — unit + integration tests proving control actually regulates (varying target changes state physically); update docs.

5.2 Bindings (AGENTS.md protocol — currently Rust+CLI only)

  • Python (PyO3) + WASM wrappers for the new components/actuators, notably ReversingValve, flooded-level, slide-valve, liquid-injection.
    • ⚠️ Blocked in this env: PyO3 max supported Python is 3.13 but the interpreter is 3.14. Fix by upgrading pyo3 in crates/bindings/python OR pinning the uv env to 3.13, then verify uv run maturin develop --release + uv run pytest. The bindings crates may be absent from the current snapshot — confirm before starting.

5.3 Water-cooled machines (workspace 61XW_VFD_EMEA, 30XF, 61AQ references)

  • wc-single-circuit-e2e — compressor + condenser + EXV + flooded-evap with water loops both sides; validate convergence + secondary sensitivity. Easiest first-class target (no air/fan/defrost).
  • wc-multicircuit — parallel dual-circuit (A/B) compressors/HX with a shared secondary manifold (System_2C).
  • wc-pass-count — 1P/2P/3P evaporator & condenser pass configuration.
  • wc-slide-valve — second actuator alongside speed (CompressorControl_double). (A screw slide-valve actuator already exists — this is the water-cooled twin-actuator variant.)
  • exv-sst-actuator — make EXV opening physically move suction saturated temp / mass flow so a SaturatedController can regulate SST.

5.4 Real-machine feature blocks (30XF / 61AQ parity)

  • rm-staging — 14 module staging state machine (61AQ StateMachine). (Circuit on/off staging at config level is done; this is the multi-module controller.)
  • rm-fan-headp — air-cooled condenser SDT limit → fan speed via SaturatedController. (A fan head-pressure actuator exists — this is the closed-loop controller variant.)
  • rm-exv-orificeṁ=Cd·A(θ)·√(2ρΔP) opening sets emergent P_evap=SST (matches 61AQ EXVControl_cooling). (Orifice actuator exists — this is the SST loop.)
  • rm-freecool — free-cooling exchanger + glycol routing + OAT changeover (30XF).
  • rm-defrost — defrost seasonal correction block (Auxiliary.Defrost.Defrost_factor).
  • rm-en14511 — EN 14511 net-from-gross corrections (pump/fan penalties on net capacity/power).
  • rm-moistair — moist-air (RH / wet-bulb) psychrometric properties for air-side HX.
  • rm-surrogate — MCHX/compressor surrogate-map (performance-map) backend for HX and compressor (speed/accuracy trade-off).

5.5 Web UI

  • Circuit enabled toggle in apps/web configBuilder (emit "enabled": false).
  • Keep the control JSON schema aligned with the UI node/port model (see p0c-ui-align).

5.6 Bigger / architectural (research-backed)

  • Consider a modular Modelica-style model/controller composition system (user's explicit interest) — arch-1…6 laid the groundwork (typed ports, subsystems, unified IR, controls[]); next is a first-class reusable controller/model library and possibly acausal connectors.
  • Performance: exploit research-improve findings (surrogate maps, warm-starting, Anderson acceleration already present, parallel rating points already via rayon).

6. Known environment limitations

  • Python bindings do not build here: PyO3 max 3.13 vs interpreter 3.14.
  • cargo test --workspace fails for the same reason — test crates individually.
  • CoolProp is enabled via Cargo features; CLI/solver runs need no --features flag. CLI integration tests self-skip without CoolProp.

7. Key files map

Area Path
Project instructions AGENTS.md
Coupled HX crates/components/src/heat_exchanger/{condenser,evaporator,flooded_evaporator}.rs
Reversing valve crates/components/src/reversing_valve.rs
Inline-component template crates/components/src/isenthalpic_expansion_valve.rs
Compressor + actuators crates/components/src/isentropic_compressor.rs
System / DoF / performance crates/solver/src/system.rs
Controls / saturated PI crates/solver/src/inverse/saturated_control.rs
Smoothing primitives crates/core/src/smoothing.rs
CLI wiring hub crates/cli/src/run.rs
CLI config schema crates/cli/src/config.rs
Rating / seasonal crates/cli/src/{rate,seasonal,qualify}.rs
CLI integration tests crates/cli/tests/single_run.rs
Examples crates/cli/examples/*.json
Web UI apps/web/src/lib/{componentMeta,configBuilder}.ts
Session artifacts _bmad-output/implementation-artifacts/sprint-status.yaml

8. Test status snapshot (at handoff)

  • PASS: entropyk-core, entropyk-components (792), entropyk-solver (+integration), entropyk, entropyk-cli, web vitest (61). 0 warnings on core crates.
  • Fixed: entropyk-fluids tabular-accuracy test (regenerated r134a.json).
  • Not buildable in this env: bindings/python (PyO3 vs Python 3.14).