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

373 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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_S` → `DEFAULT_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
```bash
# 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_schema` →
`ScenarioConfig::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).