Files
Entropyk/docs/audits/heat_exchanger_architecture_audit.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

26 KiB
Raw Blame History

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<Box<dyn Component>, 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 = 4at most 5 refrigerant circuits (IDs 04) (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:

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<f64>; (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<M>. 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_plates1.
correlation_registry.rs (1290) Evidence-aware registry (no formulas): CorrelationId, CorrelationMetadata{domain,evidence}, SelectionContext, assess_candidateCandidateAssessment{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<LmtdModel>, 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<EpsNtuModel> 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_outh_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<f64> (: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 <crate> 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 FAILconvergence_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 B1B5 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 BF 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_outh_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 LowMedium
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.rsevaporator.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_dewT_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 (B1B5). These must be unblocked before Phase BF 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.