# Heat-Exchanger Architecture Audit (Phase A) **Date:** 2026-07-16 **Scope:** Full Entropyk workspace, focused on the heat-exchanger family and the `FloodedEvaporator` upgrade target. **Method:** Code reading + live reproduction of `cargo fmt/check/clippy/test` on `C:\Users\serameza\impact\dev\Entropyk-main\entropyk`. All claims cite `file:line`. No code was modified during this audit. Legend for evidence: **[code]** = confirmed by repository source; **[build]** = confirmed by running a command this session; **[carrier]** = confirmed by authorized Carrier source (none used in this audit — no Carrier data was provided); **[assumption]** = engineering assumption requiring validation. --- ## 1. Workspace and architecture inventory [code] ### 1.1 Workspace members (`Cargo.toml`) 12 workspace members: `crates/{core, components, entropyk, fluids, solver, cli, vendors}`, `demo`, `bindings/{python, c, wasm}`, `tests/fluids`. Version `0.1.0`, edition 2021, workspace deps `thiserror`, `serde`, `serde_json`. ### 1.2 Crate roles and dependencies (relevant subset) | Crate | Role | CoolProp coupling | |-------|------|-------------------| | `entropyk-core` | Physical types, smoothing, calibration. **Does NOT own the `Component` trait** (see 1.4). | none | | `entropyk-fluids` | `FluidBackend` trait + backends (CoolProp, tabular, incompressible, damped, cached, dll, test). | optional features `coolprop`, `dll`; `default = []` | | `entropyk-components` | `Component` trait + all thermodynamic components + HX family. | depends on `entropyk-fluids` (default, no coolprop) | | `entropyk-solver` | `System` (petgraph topology, stride-3 state `[m,P,h]` per edge), Newton/Picard/Fallback, `ThermalCoupling`. | `coolprop` feature (default off) | | `entropyk` (facade) | Re-export library. | **forces `entropyk-fluids/coolprop` unconditionally** (`crates/entropyk/Cargo.toml:16`) | | `entropyk-cli` | CLI factory `create_component`, config IR v1/v2, subsystem templates. | **forces `entropyk-fluids/coolprop` unconditionally** (`crates/cli/Cargo.toml:23`) | | `entropyk-python` | PyO3 bindings. | optional; **broken on Python 3.14** (see §4.4-B1) | | `entropyk-vendors` | Compressor data (Bitzer/Copeland/Danfoss) + SWEP BPHX data. | none | ### 1.3 State vector and topology [code] - `System` holds `petgraph::Graph, FlowEdge, Directed>` (`crates/solver/src/system.rs:198`); nodes = components, edges = flow connections. - State vector is **stride-3**: `[m₀,P₀,h₀, m₁,P₁,h₁, …]` per edge (`system.rs:192`). `FlowEdge` carries `state_index_{m,p,h}`, `source_port`, `target_port`, `mass_flow_branch_id` (`system.rs:134`). - Series branches share one `state_index_m` via `presolve_mass_flow_topology` using `Component::flow_paths()` (default empty). - `MAX_CIRCUIT_ID = 4` → **at most 5 refrigerant circuits (IDs 0–4)** (`system.rs:30`). - Cross-circuit **edges are hard-rejected** (`TopologyError::CrossCircuitConnection`) — verified by test (`crates/solver/tests/multi_circuit.rs:111`). ### 1.4 `Component` trait — lives in `components`, not `core` [code] Defined at `crates/components/src/lib.rs:478`. Required methods: ```rust fn compute_residuals(&self, state: &StateSlice, residuals: &mut ResidualVector) -> Result<(), ComponentError>; fn jacobian_entries(&self, state: &StateSlice, jacobian: &mut JacobianBuilder) -> Result<(), ComponentError>; fn n_equations(&self) -> usize; fn get_ports(&self) -> &[ConnectedPort]; ``` Supporting types: `type StateSlice = [f64];` (`lib.rs:221`), `type ResidualVector = Vec;` (`lib.rs:227`), `JacobianBuilder` = **flat COO triplet accumulator** `add_entry(row,col,value)` (`lib.rs:235,271`) — not a blocked/sparse matrix. Relevant provided methods: `flow_paths()`, `set_port_context(&mut self, &[(m,p,h)])`, `set_system_context`, `energy_transfers`, `measure_output`, `signature`. ### 1.5 Strong physical types available [code] In `crates/core/src/types.rs` (re-exported `lib.rs:61`): `Pressure`, `Temperature`, `Enthalpy` (**specific, J/kg — no separate `SpecificEnthalpy`**), `MassFlow` (with `regularized()` clamp at `1e-12`, `types.rs:334,382`), `Power`, `Concentration`, `VolumeFlow`, `RelativeHumidity`, `VaporQuality`, `Entropy`, `ThermalConductance` (W/K), `CircuitId(pub u16)` (hashable). **Missing types needed by the mission (§6.1):** `Length`, `Mass`, `Area`, `TemperatureDifference`, `ThermalConductivity`, `Volume`. Geometry is currently raw `f64` inside BPHX/FinCoil components. (`TemperatureDelta` exists only in `crates/fluids/src/types.rs:13`, not in core.) ### 1.6 Smoothing / regularization toolkit already present [code] `crates/core/src/smoothing.rs`: `smoothstep`/deriv (C¹), `smootherstep` (C²), `cubic_blend`/`quintic_blend`, `smooth_max`/`smooth_min` (C^∞), `smooth_abs`/deriv, `smooth_clamp`. **All carry verified analytic derivatives.** This is the foundation the zero-flow regularization (mission §7.2) should reuse, not reimplement. --- ## 2. Heat-exchanger inventory [code] `crates/components/src/heat_exchanger/` — 23 files. Grouped below; line counts in parentheses. Detailed per-file fields (purpose/API/ports/equations/jacobian/geometry/ zero-flow/multi-circuit/dp/tests/defects) were captured and are summarized; the most decision-relevant items are inlined. ### 2.1 Framework / strategy layer | File (lines) | Role | Key facts | |--------------|------|-----------| | `model.rs` (206) | Object-safe `HeatTransferModel` trait + `FluidState`. | `n_equations()` per impl; UA is a single `f64`. Jacobian is the wrapper's job. | | `eps_ntu.rs` (349) | ε-NTU model. | **`ExchangerType::ShellAndTube{passes}`: `passes` is bound to `_` and unused** (`eps_ntu.rs:168-174`). The closed form used is actually the *cross-flow both-unmixed* formula, mislabelled. Zero-capacity guard: `c_min<1e-10 → Q=0` (`:204`). | | `lmtd.rs` (403) | LMTD model. | `ShellAndTube1_2` correction factor hard-coded `0.9` (`:53`). | | `exchanger.rs` (1431) | Generic 4-port `HeatExchanger`. | **Jacobian is finite-difference** by default (`:816-865`), re-evaluates backend property calls per column. Doc says 3 eqns, returns 2 (`:73-79` vs `:867`). `flow_paths=[(0,1),(2,3)]` only. | | `mod.rs` (108) | Barrel re-export. | — | ### 2.2 Refrigerant-side UA/ε-NTU components | File (lines) | Geometry | Jacobian | DP | Zero-flow | Multi-circuit | Notable defects | |--------------|----------|----------|----|-----------|---------------|-----------------| | `condenser.rs` (2169) | UA only | analytic (+ FD for `dT_cond/dP`, `:1265`) | optional lumped `quadratic_drop` | `c_sec<=1e-10→ε=0` | same-branch only | `p_in>10_000` switch (×4); **`unwrap_or(inlet_p_idx)` mass→pressure column fallback** (`:1057,1231`); `assert!` in `with_flooded_head_pressure` (`:572`) | | `evaporator.rs` (1897) | UA only | analytic (+ FD `dT_evap/dP`) | optional `quadratic_drop` | `c_sec` guard | same-branch only | same `p_in` switch + `unwrap_or(inlet_p_idx)` (`:851,1030`); heavy duplication with condenser (`SecondaryJacCtx`) | | `flooded_evaporator.rs` (939) | **UA only** | analytic + **FD `dT_sat/dP`** (`:568-572`) | **none** | `c_sec<=1e-10→ε=0` | **none** | see §3 (target component) | | `flooded_condenser.rs` (704) | UA only | delegates to inner (FD) | none | inner | none | **`n_equations=3` but only 2 non-trivial residuals** (`:247-266`); **interior mutability via `&self`** (`Cell`, `:45-46,269,280,283`); no `set_system_context` override; ~20 undocumented public items | ### 2.3 Air-side / coil components | File (lines) | Geometry | Notes | |--------------|----------|-------| | `condenser_coil.rs` (278) | inherits | Thin wrapper enforcing Air cold side; `name()` hard-coded. | | `evaporator_coil.rs` (292) | inherits | Mirror of above (Air hot side). | | `air_cooled_condenser.rs` (302) | approach-based | `T_cond = OAT + approach` (`:78`); UA unused in solver. | | `fin_coil_condenser.rs` (886) | **`CoilGeometry` (11 fields)** | **Richest geometry model** — Chang & Wang louvered-fin, fin efficiency, UA from geometry (`:517-597`); UA clamped `[1e2,1e6]` (`:596`); refrigerant resistance lumped `×0.92`. | | `mchx_condenser_coil.rs` (498) | variable UA | `UA_eff=UA_nom·(ρ/ρ_ref)·fan_speed_ratio^n` (`:234`); `coil_index` metadata only (no bank partition). | ### 2.4 Brazed-plate (BPHX) family — **the pattern to reuse** | File (lines) | Role | |--------------|------| | `bphx_geometry.rs` (504) | **Typed geometry**: `BphxGeometry` (plates, dimensions, chevron, dh, area), `mass_flux(m)`, `n_channels=n_plates−1`. | | `correlation_registry.rs` (1290) | **Evidence-aware registry** (no formulas): `CorrelationId`, `CorrelationMetadata{domain,evidence}`, `SelectionContext`, `assess_candidate`→`CandidateAssessment{InDomain/NearBoundary/Extrapolated}`, `select_correlation`. Every correlation is `EvidenceIncomplete` (enforced by test `:1267`). | | `bphx_correlation.rs` (1597) | Formula enum mirroring registry ids; `HeatTransferCorrelation` trait; `CorrelationSelector::select_and_compute` checked evaluation; Gnielinski with C¹ laminar→turbulent blend using `entropyk_core::smoothing`. | | `bphx_exchanger.rs` (846) | UA/DP from geometry+correlation; **interior mutability via `&self`** (`Cell` fields `:61-64,323-328`); DP helper **not wired into residual**. | | `bphx_evaporator.rs` (865) / `bphx_condenser.rs` (912) | DX variants; read outlet in `compute_residuals(&self)` writing `Cell` state; superheat/subcooling **not enforced as residual equations**. | ### 2.5 Other | File (lines) | Role | |--------------|------| | `moving_boundary_hx.rs` (851) | Zone discretization (SH/TP/SC). **Per-zone HTC hard-coded** (5000/500/1500, `:436-441`); `correlation_selector` stored but **unused** (`:97`); **Jacobian delegates to inner ignoring the UA scale it just computed** (`:219-225`) — real correctness gap. | | `two_phase_dp.rs` (380) | Friedel DP + lumped `quadratic_drop` (antisymmetric C¹). **Fully analytic, well-tested.** Rich Friedel model **never wired into any residual** — only `quadratic_drop` is. | | `economizer.rs` (280) | Suction/liquid IHX, delegates to `HeatExchanger`, ON/OFF/BYPASS. | --- ## 3. `FloodedEvaporator` — current-state verification (every claim) [code] Target file: `crates/components/src/heat_exchanger/flooded_evaporator.rs`. | Mission claim (§2) | Verdict | Evidence | |---|---|---| | Wraps `HeatExchanger` | ✅ | inner field; UA from `EpsNtuModel` fixed at `new(ua)` | | Nominal constant UA | ✅ | `self.ua()` returns fixed value (`:106,168-170,275`) | | `Q = ε·C_sec·(T_sec,in − T_sat)` | ✅ | `coupled_duty` (`:285-291`); `T_evap` from `evap_temperature(P)` at quality 0.5 (`:267`) | | `m_ref·(h_out−h_in) − Q = 0` | ✅ | `residuals[1]` (`:488`) | | No refrigerant pressure drop | ✅ | `residuals[0] = P_out − P_in` (`:486`); no `pressure_drop_coeff` field | | Optional outlet quality target | ✅ | `quality_control_enabled` adds `r2 = q_actual − q_target` (`:445-449,490-496`) | | Secondary stored as boundary values, not live ports | ✅ | `secondary_inlet_temp_k`, `secondary_capacity_rate` are `Option` (`:77-78`); never edges | | Zero secondary capacity → zero effectiveness | ✅ | `effectiveness()` returns 0 if `c_sec<=1e-10 \|\| ua<=0` (`:276-280`) | | Finite differences for `dT_sat/dP` | ✅ | central FD `dp=p_in·1e-4+100` (`:568-572`) | | No shell-and-tube geometry / tube count / passes / circuit partition | ✅ | struct carries only UA, fluid ids, indices, secondary scalars (`:57-79`) | ### 3.1 Defects in the current `FloodedEvaporator` 1. **`target_quality = 0.7` hard-coded default** (`:112`, doc `:148`) — the smell the mission §3 flags. 2. **Quality clamped to `[0,1]`** in `compute_quality` (`:375`) and setters (`:150,189`) — hides subcooled/superheated extrapolation from the solver. 3. **`p_in > 10_000.0` magic switch** (`:471,541`) — duplicated across condenser/evaporator/flooded. 4. **`unwrap_or(inlet_p_idx)`** mass-flow → pressure column fallback (`:478,548`) — if no mass index resolves, the energy residual is written against the *pressure* column (mission §16.3). 5. **Quality-control silent degradation**: `residuals[2] = -target_quality` when backend cannot compute quality (`:494,520,526`) — hides config error. 6. **`last_heat_transfer_w`/`last_outlet_quality` are dead state** — declared but never written on the coupled path; `heat_transfer()` always returns the initial `0.0`. 7. **`assert!(ua >= MIN_UA)` in `new`** (`:105`) — zero-panic policy violation (FloodedCondenser offers `try_new`; this one does not). --- ## 4. Current-state verification (reproduction) [build] Environment: Rust 1.95.0, cargo 1.95.0, Windows MSVC (VS 2022 BuildTools), CMake present, **Python 3.14** on PATH. Commands run in the working repo on 2026-07-16. ### 4.1 Commands and exact results | Command | Result | Exit | |---------|--------|------| | `cargo fmt --all -- --check` | **PASS** — no diffs | 0 | | `cargo check --workspace --all-targets --all-features` | **FAIL** | 101 | | `cargo check --workspace --all-targets --exclude entropyk-python` (default feats) | **FAIL** | 101 | | `cargo clippy --workspace --all-targets --all-features -- -D warnings` | **NOT RUN to completion** — clippy was not installed (installed during audit); after install, fails on fluids clippy errors + coolprop/pyo3 blockers | — | | `cargo test --workspace --all-features` | **NOT RUN** — blocked by build failures above | — | | `cargo test --doc --workspace` | **NOT RUN** — blocked by build failures above | — | **Per-crate reproducible baseline (crates that do not force CoolProp):** | Crate | `cargo test -p ` result | |-------|--------------------------------| | `entropyk-core` | **PASS** 160 + 38 = 198 tests | | `entropyk-fluids` (default, no coolprop) | **PASS** 102 + 4 = 106 tests | | `entropyk-components` | **PASS** 856 + 70 = 926 tests (18 ignored) | | `entropyk-solver` | **FAIL** — `convergence_criteria`: `test_newton_with_criteria_single_circuit` panics at `.expect("Solver should converge")` (`tests/convergence_criteria.rs:292`); other suites pass | | `entropyk-vendors` | **PASS** 66 + 4 = 70 tests | ### 4.2 Root causes of the workspace-wide failures (all pre-existing) **B1 — Python bindings (env).** `entropyk-python` pins `pyo3 = "0.23"` (`bindings/python/Cargo.toml:24`), which resolves to 0.23.5 (max Python 3.13). The installed interpreter is **Python 3.14**, so `pyo3-ffi` build-script exits 1: `configured Python interpreter version (3.14) is newer than PyO3's maximum supported version (3.13)`. Fixes: install Python 3.13; or `PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1`; or upgrade PyO3. **B2 — CoolProp from-source build (build).** `crates/entropyk` and `crates/cli` force `entropyk-fluids/coolprop` *unconditionally* (`crates/entropyk/Cargo.toml:16`, `crates/cli/Cargo.toml:23`). `entropyk-coolprop-sys/build.rs` **always recompiles CoolProp from source via CMake** because `vendor/coolprop/CMakeLists.txt` exists (`build.rs:32`), and **ignores the prebuilt static lib** at `vendor/coolprop/install_root/static_library/Windows/64bit_MSVC_19.44.35227.0/CoolProp.lib`. The CMake `generate_headers` custom step fails: `error MSB8066: la build personnalisée de '…generate_headers.rule' s'est arrêtée. Code 1` (MSBuild). The build **appeared to work once** this session (`cargo check -p entropyk-fluids --features coolprop` finished in 30.5 s and 115 tests passed) only because cached artifacts from a prior session were present; once that cache was invalidated the from-source header regeneration fails. **Consequence:** a clean checkout (`cargo clean && cargo build`) does NOT build; the facade, CLI, and any CoolProp code path are unbuildable once the cache is gone. **B3 — Clippy (lint).** Two deny-by-default clippy errors in `crates/fluids/src/tabular/interpolate.rs:124` and `:131` (`unnecessary '>= y + 1'`) block clippy on every fluids-dependent crate. **B4 — Components `http` feature + integration test crate (code).** Under `--all-features`: `entropyk-components` `http` feature → E0308 at `external_model.rs:675`; `tests/fluids` (`fluids-integration-tests`) → 89 errors (E0433) under its `coolprop` feature. **B5 — One red solver test (code).** `test_newton_with_criteria_single_circuit` fails (convergence expectation), independent of CoolProp. > None of B1–B5 was introduced by this audit. They confirm the warning in mission §4.4 > that the "historical coherence report is stale". **They must be resolved before the > Phase B–F implementation can be validated workspace-wide.** --- ## 5. Equation / unknown audit (per operating mode) [code] ### 5.1 `FloodedEvaporator` (single circuit, current) Unknowns on its 2 refrigerant edges: inlet `(m,P,h)` + outlet `(P,h)` — outlet mass flow is the same branch as inlet (series) so `m_out = m_in`. Effectively unknown: **m_in, P_in, h_in, P_out, h_out** (5), with secondary `T_sec,in` and `C_sec` imposed as boundary scalars. Equations: `n_equations = 2 (+1 if quality_control)` (`:443-450`): - r0: `P_out − P_in` (no DP) - r1: `m_ref·(h_out − h_in) − Q`, `Q = ε·C_sec·(T_sec,in − T_sat(P_in))` - r2 (optional): `q_actual − q_target` ### 5.2 Mode-by-mode DoF assessment for the *current* model | Mode | Behavior | Under/over-determined? | |------|----------|------------------------| | Both sides flowing | 2 eqns (or 3), 5 unknowns → completed by upstream/downstream component equations + boundary `C_sec,T_sec` | Determined within a closed cycle | | `C_sec = 0` (secondary zero) | `effectiveness→0`, `Q→0`; r1 becomes `m·(h_out−h_in)=0`. **No equation constrains `h_out` independently** — outlet enthalpy floats unless an upstream/downstream component closes it | **At risk of underdetermination** if nothing else fixes `h_out` | | Quality control ON | r2 adds an `h_out` constraint via `q_actual(h_out) = q_target` | Restores DoF | | Component OFF / BYPASS | not implemented for FloodedEvaporator (no `OperationalState` handling visible) | Gap | | Flow reversal during Newton | `m` may go negative; model uses `m` directly in r1 (sign carried) but `T_evap`/`effectiveness` are flow-magnitude independent → finite, but **no antisymmetric DP** since DP is absent | Finite but physically thin | ### 5.3 Multi-circuit modes — **not representable today** The current `FloodedEvaporator` has exactly one inlet edge and one outlet edge. The solver **rejects cross-circuit edges**, and the only inter-circuit heat mechanism is `ThermalCoupling` (`crates/solver/src/coupling.rs:52`) which transfers **duty via an external Q unknown**, never a shared fluid volume. Therefore none of these modes are representable: - shared-vessel two refrigerant circuits + one water circuit (mission §5.1); - one circuit ON while the other is OFF in the same vessel; - independent refrigerant mass/energy per circuit with a common water energy balance. The existing `Drum` (`components/src/drum.rs`) is a 4-port **single-circuit** separator (`n_equations` 8 legacy / 4 edge-coupled, `:333-338`); it does **not** override `flow_paths`/`set_port_context` and cannot model a shared multi-circuit vessel. ### 5.4 `tests/multi_circuit.rs` does NOT validate shared-vessel thermodynamics [code] Read in full (233 lines). It uses a `RefrigerantMock` whose `compute_residuals` **always returns 0.0** (`:23-26`). Its 4 tests assert only: circuit counting, cross-circuit edge rejection, max-5-circuits rejection, and one `ThermalCoupling` UA·ΔT arithmetic check (`coupling_residuals([(350,300)]) ≈ 50000 W`). **No real dual-circuit HX residuals, no shared vessel, no converged coupled solve.** Any claim of multi-circuit HX coverage from this file is false (mission §16.13). --- ## 6. Cross-cutting defect catalogue [code] | Pattern | Locations | Severity | |---|---|---| | `assert!` in non-test constructors (zero-panic violation) | `lmtd.rs:101`, `eps_ntu.rs:88`, `flooded_evaporator.rs:105`, `flooded_condenser.rs:70`, `condenser.rs:572`, `bphx_condenser.rs:141`, `mchx_condenser_coil.rs:116-117` | Medium | | Hard-coded `target_quality=0.7` | `flooded_evaporator.rs:112` | Low | | `p_in > 10_000.0` magic mode switch | `condenser.rs:1050,1164,1224,1401`; `evaporator.rs:844,947,1023,1149`; `flooded_evaporator.rs:471,541` | Medium | | `m_idx = … .unwrap_or(inlet_p_idx)` (mass→pressure column) | `condenser.rs:1057,1231`; `evaporator.rs:851,1030`; `flooded_evaporator.rs:478,548` | **High** | | Quality clamped to `[0,1]` | `flooded_evaporator.rs:150,189,375`; `moving_boundary_hx.rs:474`; `two_phase_dp.rs:108,132,151` | Low–Medium | | Interior mutability via `&self` in residual methods | `flooded_condenser.rs:45-46,269,280,283`; `bphx_exchanger.rs:61-64,323-328`; `bphx_evaporator.rs:94,348`; `bphx_condenser.rs:98-99` | Medium | | `n_equations` vs actual residual mismatch | `flooded_condenser.rs:247-266`; `moving_boundary_hx.rs:176`; `exchanger.rs:73-79` vs `:867` | Medium | | Jacobian ignores model state-dependence | `moving_boundary_hx.rs:219-225` | High (that component) | | `passes` unused in `ShellAndTube` ε-NTU | `eps_ntu.rs:168-174` | Medium | | Rich Friedel DP never wired into residuals | `two_phase_dp.rs` vs `condenser.rs:668-676` | Medium | | Duplicated `SecondaryJacCtx`/helpers | `condenser.rs` ↔ `evaporator.rs`; `compute_quality` ×3 | Medium | | `ShellAndTube1_2` F=0.9 hard-coded | `lmtd.rs:53` | Low | | `UA` clamping `[1e2,1e6]` | `fin_coil_condenser.rs:596` | Low | | `http` feature E0308 | `external_model.rs:675` | Medium (gatekeeps `--all-features`) | --- ## 7. Fluid backend — derivative capability (mission §9.2) [code] The `FluidBackend` trait (`crates/fluids/src/backend.rs:41`) exposes **point queries only**: `property`, `full_state`, `bubble_point`, `dew_point`, `temperature_glide` (= `T_dew−T_bubble`, two point queries), `saturation_pressure_t`, `saturation_enthalpy_t`. **`dT_sat/dP` is NOT exposed by any backend.** The `CoolPropBackend` (`crates/fluids/src/coolprop.rs:28`) uses only the single-output `PropsSI` FFI shims (`props_si_pt/ph/ps/px/tq`); it **does not call CoolProp's `AbstractState` derivative API** (no `first_partial_deriv`). `DampedBackend` smooths property *values* near the critical point but adds no derivative method. **Consequence:** every analytic Jacobian needing `dT_sat/dP` must use a central finite-difference fallback, exactly as `FloodedEvaporator` already does (`:568-572`, 3 CoolProp calls per Jacobian eval). Phase B should: (a) add a thin, documented backend method `saturation_temperature_derivative_dp` implemented as central FD over the backend (single source of truth, cacheable), and (b) optionally wire CoolProp's analytic derivative later. This satisfies mission §9.3 (explicit, documented fallback). --- ## 8. Bindings & examples coverage [code] - **Python:** `FloodedEvaporator` is **not exposed**; pyo3 0.23 broken on Python 3.14 (B1). - **C:** `FloodedEvaporator` **not exposed**; C bindings are largely `SimpleAdapter` stubs (e.g. `entropyk_compressor_create` ignores AHRI coeffs, `bindings/c/src/components.rs:107-115`). - **WASM:** `FloodedEvaporator` **not exposed**. - **CLI factory** (`crates/cli/src/run.rs:2432`): `FloodedEvaporator` arm exists (`:2590`) but reads only `ua`, `target_quality`, `refrigerant`, `secondary_fluid`, secondary stream — **no geometry, no circuit count, no shared-vessel config**. Not wired into the factory at all: `FloodedCondenser`, `Economizer`, `MchxCondenserCoil`, `ReversingValve`, `PumpController`. - **Examples (18):** `FloodedEvaporator` is used in **none**. `chiller_r134a_flooded_headpressure.json` is misnamed (uses DX `Evaporator` + a condenser parameter). `chiller_r134a_dual_circuit_staging.json` is two **independent** circuits with separate water loops — **no shared vessel**. - **Vendor data:** Copeland/Danfoss/Bitzer compressors + SWEP BPHX only. **No Carrier data, no shell-and-tube / flooded-HX selection data.** --- ## 9. Findings directly driving the implementation phases 1. **No shared-vessel / multi-circuit HX primitive exists** — must be built (mission §6.4, Phase D). Cross-circuit edges are rejected; only duty-transfer `ThermalCoupling` exists. 2. **Geometry domain types are missing from core** (`Length`, `Mass`, `Area`, `TemperatureDifference`, `ThermalConductivity`, `Volume`) — Phase B prerequisite. 3. **The BPHX registry/geometry/correlation pattern is the reuse target**, not a rewrite: `correlation_registry.rs` + `bphx_correlation.rs` + `bphx_geometry.rs` + `two_phase_dp.rs` form a clean, evidence-aware, single-source-of-truth pattern to extend to shell-and-tube flooded boiling/condensation (mission §6.2). 4. **Zero-flow regularization should build on `entropyk_core::smoothing`** (`smooth_abs`, `cubic_blend`, `smooth_max`) rather than hard `if m<ε` branches (mission §7.2). 5. **Backend `dT_sat/dP` is absent** — add a documented central-FD method in Phase B (§7). 6. **Baseline is red workspace-wide** (B1–B5). These must be unblocked before Phase B–F can be validated. Recommended pre-Phase-B fixes: - B2 (highest leverage): make `coolprop-sys` prefer the prebuilt `install_root` lib, or fix the `generate_headers` step, so the facade/CLI build reproducibly. - B3: fix the two clippy lints in `tabular/interpolate.rs`. - B5: fix or `#[ignore]` the `convergence_criteria` red test with a tracked issue. - B1: standardize on Python 3.13 for the bindings (Phase F, but unblock early). 7. **`ShellAndTube` ε-NTU `passes` is decorative** — Phase B/C must implement a real P-NTU/multipass effectiveness or prevent its use for unsupported arrangements (mission §8.4). 8. **Flooded evaporator outlet/control semantics** (mission §3) are wrong by default (`target_quality=0.7`) — Phase C replaces with an explicit `FloodedEvaporatorControl` enum. --- ## 10. Open questions requiring Carrier/product input (not answered here) No Carrier source documents were provided for this audit, so the following remain **Unknown** and must NOT be guessed (mission §1.4, §13): - 61XWHVZE flooded-evaporator geometry (shell ID, tube count per pass, active length per refrigerant circuit, tube type/enhancement, water-box layout, refrigerants). - 30XF-Z module/pass arrangement details. - Actual water-path layout through the shared-vessel refrigerant partitions (mission §6.4 `SharedSecondaryLayout`) — configurable until evidenced. - Validation datasets for any product. These are captured in the companion `montluel_machine_coverage.md` with `Unknown` status.