--- stepsCompleted: - step-01-validate-prerequisites - step-02-design-epics - step-03-create-stories inputDocuments: - /Users/sepehr/dev/Entropyk/_bmad-output/planning-artifacts/prd.md - /Users/sepehr/dev/Entropyk/_bmad-output/planning-artifacts/architecture.md --- # Entropyk - Epic Breakdown ## Overview This document provides the complete epic and story breakdown for Entropyk, decomposing the requirements from the PRD and Architecture requirements into implementable stories. ## Requirements Inventory ### Functional Requirements **FR1:** The system can model a Compressor according to AHRI 540 standard with manufacturer coefficients **FR2:** The system can model a Condenser with heat transfer calculation **FR3:** The system can model an Expansion Valve (isenthalpic expansion) **FR4:** The system can model an Evaporator with phase change **FR5:** The system can model an Economizer (internal heat exchanger) with Bypass mode **FR6:** Each component can be in OperationalState (On, Off, Bypass) **FR7:** In Off mode, an active component contributes zero mass flow to the system **FR8:** In Bypass mode, a component behaves as an adiabatic pipe (P_in = P_out, h_in = h_out) **FR9:** User can define a Machine containing N independent fluid circuits **FR10:** User can connect components via Ports (inlet/outlet) **FR11:** System supports connections between circuits (thermal coupling) **FR12:** System can solve N circuits simultaneously or sequentially **FR13:** System mathematically handles zero-flow branches without division by zero **FR14:** System can solve equations using Newton-Raphson method **FR15:** System can solve equations using Sequential Substitution (Picard) method **FR16:** Solver automatically switches to Sequential Substitution if Newton-Raphson diverges **FR17:** Solver respects configurable time budget (timeout) **FR18:** On timeout, solver returns best known state with NonConverged status **FR19:** Solver can freeze Jacobian calculation to accelerate (Jacobian Freezing) **FR20:** Convergence criterion checks Delta Pressure < 1 Pa (1e-5 bar) **FR21:** For multi-circuits, global convergence is achieved when ALL circuits converge **FR22:** User can define output constraints (e.g., Superheat = 5K) **FR23:** System calculates necessary inputs (e.g., valve opening) respecting Bounded Constraints (0.0 ≤ Valve ≤ 1.0). If solution is out of bounds, solver returns "Saturation" or "ControlLimitReached" error **FR24:** Inverse Control is solved simultaneously with cycle equations (One-Shot) **FR25:** System can load fluid properties via CoolProp **FR26:** System supports Tabular Interpolation tables for 100x performance **FR27:** System handles pure fluids and mixtures (R32/R125, R454B) **FR28:** System handles Temperature Glide for zeotropic mixtures **FR29:** System uses automatic damping near critical point (CO2 R744) **FR30:** Native Rust API with types and ownership safety **FR31:** Python bindings via PyO3 with tespy-compatible API **FR32:** C FFI for integration with external systems (PLC, LabView) **FR33:** WebAssembly compilation support for web interfaces **FR34:** CLI for batch simulation execution **FR35:** System automatically checks mass balance (Σ ṁ_in - Σ ṁ_out < 1e-9 kg/s) **FR36:** System automatically checks energy balance (Σ Q̇ + Ẇ - Σ (ṁ · h) < 1e-6 kW) **FR37:** Each result contains traceability metadata (solver version, fluid version, SHA-256 input hash) **FR38:** Debug Verbose mode to display residuals and convergence history **FR39:** Error handling via Result (Zero-Panic Policy) **FR40:** System handles Incompressible Fluids (Water, Glycol, Humid Air simplified) via lightweight models (constant Cp or polynomial) for heat sources and sinks **FR41:** Complete graph (Topology + Parameters + Fluid State) is serializable/deserializable to JSON (or TOML) **FR42:** System includes Automatic Initialization Heuristic (Smart Guesser) proposing coherent initial pressure values based on source/sink temperatures **FR43:** Components support calibration parameters (Calib: f_m, f_dp, f_ua, f_power, f_etav) to match simulation to real machine test data **FR44:** System can validate results against ASHRAE 140 / BESTEST test cases (post-MVP) **FR45:** System supports inverse calibration (parameter estimation from test bench data) **FR46:** Explicit Air Coil components (EvaporatorCoil, CondenserCoil) for finned air heat exchangers (post-MVP) **FR47:** Each refrigeration component natively exposes a complete thermodynamic state (Pressure, Temperature, T_sat, Quality, Superheat, Subcooling, Mass flow, Reynolds, Enthalpy, Entropy) easily accessible without complex recalculations. **FR48:** Hierarchical Subsystems (MacroComponents) - encapsulate complete systems into reusable blocks **FR49:** Flow Junctions (FlowSplitter 1→N, FlowMerger N→1) for compressible & incompressible fluids **FR50:** Boundary Conditions (RefrigerantSource, RefrigerantSink) for compressible & incompressible fluids **FR51:** Swappable Calibration Variables - swap calibration factors (f_m, f_ua, f_power, etc.) into solver unknowns and measured values (Tsat, capacity, power) into constraints for one-shot inverse calibration **FR52:** Bounded Variable Step Clipping - during Newton-Raphson iterations, bounded control variables are clipped to [min, max] at EVERY iteration, preventing physically impossible values (e.g., valve > 100%) and improving convergence stability **FR53:** Node - Passive probe component (0 equations) for extracting P, h, T, quality, superheat, subcooling at any point in the circuit without affecting the solver **FR54:** FloodedEvaporator - Flooded evaporator where liquid refrigerant completely floods the tubes via a low-pressure receiver, producing a two-phase mixture (50-80% vapor) at the outlet **FR55:** FloodedCondenser - Accumulation condenser where condensed refrigerant forms a liquid bath around the cooling tubes to regulate condensing pressure **FR56:** Drum - Recirculation drum for evaporator recirculation cycles (2 inlets → 2 outlets: saturated liquid + saturated vapor) **FR57:** BphxExchanger - Brazed Plate Heat Exchanger configurable as DX evaporator, flooded evaporator, or condenser with plate-specific correlations **FR58:** MovingBoundaryHX - Zone-discretized heat exchanger with phase zones (superheated/two-phase/subcooled) and configurable correlation (Longo, Shah, Kandlikar, etc.) **FR59:** VendorBackend - API for vendor data (Copeland, Danfoss, SWEP, Bitzer) with compressor coefficients and heat exchanger parameters loaded from JSON/CSV **FR60:** CorrelationSelector - Heat transfer correlation selection with Longo (2004) as default for BPHX, supporting Shah (1979, 2021), Kandlikar (1990), Gungor-Winterton (1986), Gnielinski (1976), and others ### NonFunctional Requirements **NFR1:** Steady State convergence time < **1 second** for standard cycle in Cold Start **NFR2:** Simple cycle (Single-stage) solved in **< 100 ms** **NFR3:** Complex cycle (Multi-circuits) solved in **< 1 second** **NFR4:** No dynamic allocation in solver loop (pre-calculated allocation only) **NFR5:** Guaranteed determinism: same inputs → same results within 1e-9 precision on all platforms (x86, ARM, WASM) **NFR6:** HIL latency < **20 ms** for real-time integration with PLC **NFR7:** Zero-Panic Policy: no crash even with invalid inputs or impossible states **NFR8:** Memory Safety guaranteed by Rust ownership system (no memory leaks, no use-after-free) **NFR9:** Capability to run **48h+** without interruption or degradation (HIL/Embedded) **NFR10:** Graceful error handling: timeout, non-convergence, saturation return explicit Result **NFR11:** CoolProp 6.4+ compatibility for fluid properties **NFR12:** Stable C FFI: auto-generated .h headers via cbindgen, C99/C++ compatible **NFR13:** Deterministic WebAssembly: same behavior as native, no non-determinism sources **NFR14:** Python bindings PyO3: tespy-compatible API for facilitated migration **NFR15:** 100% documentation of public APIs (rustdoc) **NFR16:** Automated CI/CD: tests, benchmarks, memory checks (Valgrind + Miri) **NFR17:** Zero tolerance warnings: `cargo clippy -- -D warnings` ### Additional Requirements **Architecture Requirements:** - Workspace-based multi-crate architecture with 4 crates (core, solver, components, fluids) and 3 bindings (python, c, wasm) - Trait-based static polymorphism with enum dispatch for zero-cost abstraction - NewType pattern for all physical quantities (Pressure, Temperature, Enthalpy, MassFlow) - never bare f64 in public APIs - Type-State pattern for connection safety at compile-time - Pre-allocated buffers only - no heap allocation in hot paths - `#![deny(warnings)]` in lib.rs for all crates - KaTeX configuration in `.cargo/config.toml` for mathematical documentation - `tracing` for structured logging (never println!) - `thiserror` for error handling with ThermoError enum - `approx` crate for floating-point assertions with explicit tolerances - Sys-crate pattern for CoolProp C++ integration - cbindgen for C header generation - Miri validation in CI for undefined behavior detection - Mass balance tolerance: 1e-9 kg/s - Energy balance tolerance: 1e-6 kW - Convergence pressure tolerance: 1 Pa **Standards Compliance:** - AHRI 540 standard implementation for compressor modeling with manufacturer coefficients (Bitzer, Copeland, Danfoss) - NIST REFPROP as gold standard for fluid properties - CoolProp as open-source reference - Tabular interpolation achieving < 0.01% deviation from NIST while being 100x faster than direct EOS calls **Domain-Specific Requirements:** - Critical Point handling (CO2 R744) with automatic damping to prevent NaN in partial derivatives - Temperature Glide handling for zeotropic mixtures (R454B, R32/R125) integrating Bubble Point → Dew Point without approximation - Traçability: Each SimulationResult contains solver_version, fluid_backend version, input_hash (SHA-256) - Smart initialization heuristic for initial pressure guesses based on source/sink temperatures **CI/CD Requirements:** - `cargo test`: Unit tests - `cargo bench`: Performance regression (failure if > 5% degradation) - `valgrind`: Memory leaks (FFI) - `cargo clippy -- -D warnings`: Zero tolerance style - `miri test`: Undefined behavior detection - 100% public documentation coverage - Compiled and tested examples in CI **Packaging & Distribution:** - Rust: crates.io - Python: pip install rust-thermo-cycle (Wheels PyPI, manylinux) - C/C++: Headers + dynamic libraries - Docker: Image rust-thermo-lab (JupyterLab + Rust kernel + CoolProp) ### FR Coverage Map | FR | Epic | Description | |----|------|-------------| | FR1 | Epic 1 | Compressor AHRI 540 modeling | | FR2 | Epic 1 | Condenser heat transfer | | FR3 | Epic 1 | Expansion valve isenthalpic | | FR4 | Epic 1 | Evaporator phase change | | FR5 | Epic 1 | Economizer with bypass | | FR6 | Epic 1 | Component states ON/OFF/BYPASS | | FR7 | Epic 1 | Zero mass flow in OFF mode | | FR8 | Epic 1 | Adiabatic pipe in BYPASS mode | | FR9 | Epic 3 | Multi-circuit machine definition | | FR10 | Epic 3 | Component connection via Ports | | FR11 | Epic 3 | Thermal coupling between circuits | | FR12 | Epic 3 | Simultaneous/sequential circuit solving | | FR13 | Epic 3 | Zero-flow branch handling | | FR14 | Epic 4 | Newton-Raphson solver | | FR15 | Epic 4 | Sequential Substitution solver | | FR16 | Epic 4 | Auto-fallback solver switching | | FR17 | Epic 4 | Configurable timeout | | FR18 | Epic 4 | Best state on timeout | | FR19 | Epic 4 | Jacobian freezing | | FR20 | Epic 4 | Delta Pressure < 1 Pa convergence | | FR21 | Epic 4 | Global multi-circuit convergence | | FR22 | Epic 5 | Output constraints definition | | FR23 | Epic 5 | Bounded input calculation | | FR24 | Epic 5 | One-shot inverse control | | FR25 | Epic 2 | CoolProp integration | | FR26 | Epic 2 | Tabular interpolation tables | | FR27 | Epic 2 | Pure fluids and mixtures support | | FR28 | Epic 2 | Temperature glide handling | | FR29 | Epic 2 | Critical point damping | | FR30 | Epic 6 | Native Rust API | | FR31 | Epic 6 | Python PyO3 bindings | | FR32 | Epic 6 | C FFI bindings | | FR33 | Epic 6 | WebAssembly support | | FR34 | Epic 6 | CLI for batch execution | | FR35 | Epic 7 | Mass balance validation | | FR36 | Epic 7 | Energy balance validation | | FR37 | Epic 7 | Traceability metadata | | FR38 | Epic 7 | Debug verbose mode | | FR39 | Epic 7 | Zero-panic error handling | | FR40 | Epic 2 | Incompressible fluids support | | FR41 | Epic 7 | JSON serialization | | FR42 | Epic 4 | Smart initialization heuristic | | FR43 | Epic 7 | Component calibration parameters (Calib) | | FR44 | Epic 7 | ASHRAE 140 / BESTEST validation | | FR45 | Epic 7 | Inverse calibration (parameter estimation) | | FR46 | Epic 1 | Air Coils (EvaporatorCoil, CondenserCoil) | | FR47 | Epic 2 | Rich Thermodynamic State Abstraction | | FR48 | Epic 3 | Hierarchical Subsystems (MacroComponents) | | FR49 | Epic 1 | Flow Junctions (FlowSplitter 1→N, FlowMerger N→1) for compressible & incompressible fluids | | FR50 | Epic 1 | Boundary Conditions (RefrigerantSource, RefrigerantSink) for compressible & incompressible fluids | | FR51 | Epic 5 | Swappable Calibration Variables (inverse calibration one-shot) | | FR52 | Epic 6 | Python Solver Configuration Parity - expose all Rust solver options in Python bindings | | FR53 | Epic 11 | Node passive probe for state extraction | | FR54 | Epic 11 | FloodedEvaporator | | FR55 | Epic 11 | FloodedCondenser | | FR56 | Epic 11 | Drum recirculation | | FR57 | Epic 11 | BphxExchanger | | FR58 | Epic 11 | MovingBoundaryHX | | FR59 | Epic 11 | VendorBackend API | | FR60 | Epic 11 | CorrelationSelector | ## Epic List ### Epic 1: Extensible Component Framework **Goal:** Create the foundation that allows adding any component (VFD, Battery, Pump, Pipe) by simply implementing a Rust Trait, without touching the calculation engine. **Innovation:** Trait-based "Lego" architecture to add Compressors, Pumps, VFDs, Pipes, etc. **FRs covered:** FR1, FR2, FR3, FR4, FR5, FR6, FR7, FR8, FR46, FR49, FR50 --- ### Epic 2: Fluid Properties Backend **Goal:** Provide precise thermodynamic properties via CoolProp, tabular tables, mixture handling, and critical point management. **Innovation:** 100x performance with tabular tables, automatic CO2 damping. **FRs covered:** FR25, FR26, FR27, FR28, FR29, FR40, FR47 --- ### Epic 3: System Topology (Graph) **Goal:** Enable component assembly via Ports and manage multi-circuits with thermal coupling, and support hierarchical subsystems. **Innovation:** Multi-fluid directed graph in a single model, with natively supported hierarchical sub-blocks. **FRs covered:** FR9, FR10, FR11, FR12, FR13, FR48 --- ### Epic 4: Intelligent Solver Engine **Goal:** Solve any system with < 1s guarantee, Newton-Raphson ↔ Sequential Substitution fallback. **Innovation:** Solver-agnostic with intelligent fallback - guaranteed convergence. **FRs covered:** FR14, FR15, FR16, FR17, FR18, FR19, FR20, FR21, FR42 --- ### Epic 5: Inverse Control & Optimization **Goal:** Transform component parameters (VFD speed, Valve position) into simultaneously solved unknowns. **Innovation:** Native Inverse Control via Residual Embedding - "One-Shot". **FRs covered:** FR22, FR23, FR24, FR51 --- ### Epic 6: Multi-Platform APIs **Goal:** Distribute the library via Python (PyO3), WebAssembly, C FFI, and CLI. **Innovation:** Multi-target without code duplication (Rust → Python/WASM/C). **FRs covered:** FR30, FR31, FR32, FR33, FR34 --- ### Epic 7: Validation & Persistence **Goal:** Guarantee trust via mass/energy balances, SHA-256 traceability, and JSON persistence. **Innovation:** Complete traceability and scientific reproducibility. **FRs covered:** FR35, FR36, FR37, FR38, FR39, FR41, FR43, FR44, FR45 --- ### Epic 8: Component-Fluid Integration **Goal:** Integrate real thermodynamic fluid properties directly into component Residual calculation models. **Innovation:** True physical interaction between solver mathematics and state equations. **FRs covered:** FR47 --- ### Epic 9: Coherence Corrections (Post-Audit) **Goal:** Fix coherence issues identified during the post-development audit to ensure complete and correct thermodynamic validation. **Innovation:** Systematic remediation of type duplications and incomplete implementations. **FRs covered:** FR35, FR36 (completion of Epic 7 validation) **Status:** 🔄 In Progress (2026-02-22) **Source:** [Coherence Audit Report](../implementation-artifacts/coherence-audit-remediation-plan.md) --- ## Epic 1: Extensible Component Framework ### Story 1.1: Component Trait Definition **As a** library developer, **I want** to define a generic Component trait with methods for residuals, ports, and state management, **So that** future components can be implemented by simply implementing this trait. **Acceptance Criteria:** **Given** a new component struct **When** it implements the Component trait **Then** it must provide compute_residuals(), get_ports(), and get_state() methods **And** the trait must be object-safe for dynamic dispatch --- ### Story 1.2: Physical Types (NewType Pattern) **As a** simulation user, **I want** type-safe physical quantities (Pressure, Temperature, Enthalpy, MassFlow), **So that** I cannot accidentally mix units. **Acceptance Criteria:** **Given** a function accepting Pressure and Temperature **When** I try to pass Temperature where Pressure is expected **Then** the code must not compile (compile-time safety) **And** conversions must be explicit (e.g., pressure_bar.to_pascals()) --- ### Story 1.3: Port and Connection System **As a** system modeler, **I want** to define inlet/outlet ports for components and connect them bidirectionally, **So that** I can build fluid circuit topologies. **Acceptance Criteria:** **Given** two components with compatible ports **When** I call connect(port_a, port_b) **Then** the connection is established with state validation **And** attempting to connect incompatible ports fails at compile-time --- ### Story 1.4: Compressor Component (AHRI 540) **As a** thermodynamic engineer, **I want** to model a compressor using AHRI 540 standard coefficients, **So that** I can simulate real compressor behavior with manufacturer data. **Acceptance Criteria:** **Given** a compressor with 10 AHRI 540 coefficients **When** I compute residuals for a given operating point **Then** the power consumption and mass flow are calculated per AHRI 540 equations **And** the result matches certified AHRI test data within 1% --- ### Story 1.5: Generic Heat Exchanger Framework **As a** thermal systems engineer, **I want** a pluggable heat exchanger framework supporting multiple calculation models (Pinch point, LMTD, ε-NTU), **So that** I can add new exchanger types without modifying the solver. **Acceptance Criteria:** **Given** a heat exchanger component **When** I plug in a calculation model (Pinch, LMTD, or ε-NTU) **Then** the residuals are computed using the selected model **And** new models can be added by implementing a HeatTransferModel trait **And** Condenser, Evaporator, and Economizer are implemented as specific configurations --- ### Story 1.6: Expansion Valve Component **As a** control engineer, **I want** to model an expansion valve with isenthalpic expansion, **So that** I can simulate pressure reduction in the refrigeration cycle. **Acceptance Criteria:** **Given** an expansion valve with inlet subcooled liquid at high pressure **When** I compute residuals **Then** the outlet is two-phase or saturated liquid at lower pressure **And** enthalpy is conserved (h_in = h_out) --- ### Story 1.7: Component State Machine **As a** simulation user, **I want** components to support ON, OFF, and BYPASS states, **So that** I can simulate system configurations and failures. **Acceptance Criteria:** **Given** a component in ON state **When** I switch it to OFF **Then** it contributes zero mass flow to the system **And** when in BYPASS, it behaves as an adiabatic pipe (P_in = P_out, h_in = h_out) --- ### Story 1.8: Auxiliary & Transport Components **As a** system integrator, **I want** to model Pumps, VFDs, and Pipes, **So that** I can simulate complete HVAC systems. **Acceptance Criteria:** **Given** a Pump with specified hydraulic efficiency **When** I compute residuals **Then** power consumption and pressure rise match the pump curve **And** a VFD can modify Pump or Compressor speed via affinity laws **And** a Pipe calculates pressure drop via Darcy-Weisbach --- ### Story 1.11: Flow Junctions — FlowSplitter & FlowMerger **As a** system modeler, **I want** `FlowSplitter` (1 inlet → N outlets) and `FlowMerger` (N inlets → 1 outlet) components, **So that** I can build parallel branches in hydraulic and refrigerant circuits without manually writing junction equations. **Status:** ✅ Done (2026-02-20) **FRs covered:** FR49 **Acceptance Criteria:** **Given** a refrigerant or water circuit with parallel branches **When** I instantiate `FlowSplitter::compressible("R410A", inlet, vec![out_a, out_b])` **Then** the splitter contributes `2N−1` equations (isobaric + isenthalpic constraints) **And** validation rejects incompatible fluid types (`::incompressible` rejects refrigerants) **And** `FlowMerger` contributes `N+1` equations with weighted enthalpy mixing via `with_mass_flows` **And** both implement `Box` (object-safe) **And** type aliases `Incompressible/CompressibleSplitter` and `Incompressible/CompressibleMerger` are available **Implementation:** - `crates/components/src/flow_junction.rs` — `FlowSplitter`, `FlowMerger`, `FluidKind` - 16 unit tests passing --- ### Story 1.12: Boundary Conditions — RefrigerantSource & RefrigerantSink **As a** simulation user, **I want** `RefrigerantSource` and `RefrigerantSink` boundary condition components, **So that** I can define the entry and exit points of a fluid circuit without manually managing pressure and enthalpy constraints. **Status:** ✅ Done (2026-02-20) **FRs covered:** FR50 **Acceptance Criteria:** **Given** a fluid circuit with an entry point **When** I instantiate `BrineSource::water("Water", 3.0e5, 63_000.0, port)` **Then** the source imposes `P_edge − P_set = 0` and `h_edge − h_set = 0` (2 equations) **And** `BrineSink::water("Water", 1.5e5, None, port)` imposes a back-pressure (1 equation) **And** `RefrigerantSink` with `Some(h_back)` adds a second enthalpy constraint (2 equations) **And** `set_return_enthalpy` / `clear_return_enthalpy` toggle the second equation dynamically **And** validation rejects incompatible fluid + constructor combinations **And** type aliases `Incompressible/RefrigerantSource` and `Incompressible/RefrigerantSink` are available **Implementation:** - `crates/components/src/refrigerant_boundary.rs` — `RefrigerantSource`, `RefrigerantSink` - 17 unit tests passing --- ### Story 2.1: Fluid Backend Trait Abstraction **As a** library developer, **I want** to define a FluidBackend trait, **So that** the solver can switch between CoolProp, tabular, and mock backends. **Acceptance Criteria:** **Given** a fluid property query **When** I call backend.property(fluid, property, state) **Then** it returns correct values regardless of backend implementation **And** CoolPropBackend, TabularBackend, and TestBackend all implement the trait --- ### Story 2.2: CoolProp Integration (sys-crate) **As a** simulation user, **I want** CoolProp as the primary backend, **So that** I get NIST-quality thermodynamic data. **Acceptance Criteria:** **Given** a refrigerant and thermodynamic state **When** I query via CoolPropBackend **Then** results match CoolProp 6.4+ within machine precision **And** C++ CoolProp is statically linked via sys-crate --- ### Story 2.3: Tabular Interpolation Backend **As a** performance-critical user, **I want** pre-computed NIST tables with fast interpolation, **So that** queries are 100x faster than direct EOS. **Acceptance Criteria:** **Given** a tabular data file for a fluid **When** I query via TabularBackend **Then** results deviate < 0.01% from NIST REFPROP **And** query time is < 1μs --- ### Story 2.4: LRU Cache for Fluid Properties **As a** solver developer, **I want** lock-free or thread-local caching, **So that** redundant calculations are avoided without mutex contention. **Acceptance Criteria:** **Given** repeated property queries **When** cache is enabled **Then** Thread-Local or lock-free (dashmap) caching is used **And** no mutex contention under high parallelism (Rayon) **And** cache invalidates on state changes --- ### Story 2.5: Mixture and Temperature Glide Support **As a** refrigeration engineer, **I want** robust (P, h) and (P, x) inputs for zeotropic mixtures, **So that** the solver handles temperature glide reliably. **Acceptance Criteria:** **Given** a zeotropic mixture at constant pressure **When** calculating evaporation **Then** temperature glide from bubble to dew point is correct **And** backend supports (P, h) and (P, x) inputs robustly **And** (P, h) is preferred over (P, T) for two-phase mixtures --- ### Story 2.6: Critical Point Damping (CO2 R744) **As a** CO2 systems designer, **I want** smooth, differentiable damping near critical point, **So that** Newton-Raphson converges without discontinuities. **Acceptance Criteria:** **Given** CO2 near critical point (Tc=304.13K, Pc=7.3773 MPa) **When** querying within 5% of critical point **Then** damping is applied to partial derivatives **And** no NaN values **And** damping function is C1-continuous (sigmoid transition) --- ### Story 2.7: Incompressible Fluids Support **As a** HVAC engineer, **I want** water, glycol, and moist air as incompressible fluids, **So that** heat sources/sinks are fast to compute. **Acceptance Criteria:** **Given** an incompressible fluid **When** querying density, Cp, or enthalpy **Then** lightweight polynomial models are used **And** results match references within 0.1% **And** computation is 1000x faster than compressible EOS --- ### Story 2.8: Rich Thermodynamic State Abstraction **As a** system engineer, **I want** components to expose a comprehensive `ThermoState` structure (P, T, T_sat, Quality, tsh, Reynolds, Enthalpy, Entropy, etc.), **So that** I don't have to manually calculate these from raw state arrays after solver convergence. **Acceptance Criteria:** **Given** a converged component (e.g., Compressor or Condenser) **When** I call `component.outlet_thermo_state()` **Then** it returns a `ThermoState` object **And** the object contains dynamically resolved saturated temperature, vapor quality, superheat, and phase **And** `FluidBackend` natively supports resolving this full snapshot in one trait call `full_state(p, h)` --- ## Epic 3: System Topology (Graph) ### Story 3.1: System Graph Structure **As a** system modeler, **I want** edges to index the solver's state vector, **So that** P and h unknowns assemble into the Jacobian. **Acceptance Criteria:** **Given** components with ports **When** adding to System graph **Then** edges serve as indices into solver's state vector **And** each flow edge represents P and h unknowns **And** solver traverses graph to assemble Jacobian **And** cycles are detected and validated --- ### Story 3.2: Port Compatibility Validation **As a** system designer, **I want** port connection validation at build time, **So that** incompatible connections are caught early. **Acceptance Criteria:** **Given** two ports with incompatible fluids **When** attempting to connect **Then** connection fails with clear error **And** valid connections are accepted **And** pressure/enthalpy continuity is enforced --- ### Story 3.3: Multi-Circuit Machine Definition **As a** R&D engineer (Marie), **I want** machines with N independent circuits, **So that** I simulate complex heat pumps. **Acceptance Criteria:** **Given** a machine with 2+ circuits **When** defining topology **Then** each circuit is tracked independently **And** circuits can be solved simultaneously or sequentially **And** supports up to N=5 circuits --- ### Story 3.4: Thermal Coupling Between Circuits **As a** systems engineer, **I want** thermal coupling with circular dependency detection, **So that** solver knows to solve simultaneously vs sequentially. **Acceptance Criteria:** **Given** two circuits with a heat exchanger **When** defining thermal coupling **Then** heat transfer equations link circuits **And** energy is conserved (Q_hot = -Q_cold) **And** circular dependencies force simultaneous solving **And** coupling represented as additional residuals --- ### Story 3.5: Zero-Flow Branch Handling **As a** simulation user, **I want** zero-flow handling with regularization, **So that** OFF components don't cause numerical instabilities. **Acceptance Criteria:** **Given** a branch with mass flow = 0 **When** computing residuals **Then** no division-by-zero **And** regularization applied (ε minimum in divisions) **And** auto-switch to pressure continuity when OFF **And** Jacobian remains well-conditioned --- ### Story 3.6: Hierarchical Subsystems (MacroComponents) **As a** system designer, **I want** to encapsulate a complete system (e.g., a Chiller with compressor, condenser, valve, evaporator) into a single reusable block, **So that** I can compose larger models (like buildings or parallel chiller plants) using these blocks, just like in Modelica. **Acceptance Criteria:** **Given** a fully defined `System` with internal components and connections **When** I wrap it in a `MacroComponent` **Then** I can expose specific internal ports (e.g., Evaporator Water In/Out, Condenser Water In/Out) as the `MacroComponent`'s external ports **And** this `MacroComponent` implements the `Component` trait **And** I can add it to a higher-level `System` just like any regular component **And** the global solver correctly flattens or delegates the residual and jacobian computations down to the nested components. --- ## Epic 4: Intelligent Solver Engine ### Story 4.1: Solver Trait Abstraction **As a** numerical developer, **I want** a generic Solver trait, **So that** strategies are interchangeable. **Acceptance Criteria:** **Given** a system of equations **When** instantiating solver strategies **Then** all implement common Solver trait **And** provides solve() and with_timeout() methods **And** zero-cost abstraction via enum dispatch --- ### Story 4.2: Newton-Raphson Implementation **As a** simulation engineer, **I want** Newton-Raphson with analytical Jacobian support, **So that** HIL performance is optimized. **Acceptance Criteria:** **Given** a system with residuals **When** running Newton-Raphson **Then** quadratic convergence near solution **And** line search prevents overshooting **And** supports both numerical and analytical Jacobian **And** components provide analytical Jacobian entries --- ### Story 4.3: Sequential Substitution (Picard) Implementation **As a** fallback solver user, **I want** Sequential Substitution for robust convergence, **So that** when Newton diverges, I have a stable alternative. **Acceptance Criteria:** **Given** a system where Newton diverges **When** using Sequential Substitution **Then** it converges reliably **And** variables updated sequentially **And** relaxation factors configurable --- ### Story 4.4: Intelligent Fallback Strategy **As a** simulation user, **I want** automatic fallback with smart return conditions, **So that** convergence is guaranteed without solver oscillation. **Acceptance Criteria:** **Given** Newton-Raphson diverging **When** divergence detected (> 3 increasing residuals) **Then** auto-switch to Sequential Substitution **And** return to Newton ONLY when convergence radius reached **And** prevent oscillation by requiring stable Picard first --- ### Story 4.5: Time-Budgeted Solving **As a** HIL engineer (Sarah), **I want** strict timeout with graceful degradation, **So that** real-time constraints are never violated. **Acceptance Criteria:** **Given** solver with timeout = 1000ms **When** time budget exceeded **Then** solver stops immediately **And** returns best state with ComputationStatus::Timeout **And** for HIL, returns previous state with ZOH --- ### Story 4.6: Smart Initialization Heuristic **As a** R&D engineer (Marie), **I want** automatic initial guesses from temperatures, **So that** cold start convergence is fast. **Acceptance Criteria:** **Given** source and sink temperatures **When** initializing system **Then** pressures estimated via Antoine equation **And** evaporator pressure < P_critical **And** condenser pressure from T_sink + ΔT_approach --- ### Story 4.7: Convergence Criteria & Validation **As a** simulation user, **I want** strict criteria with sparse Jacobian for multi-circuit, **So that** large systems remain tractable. **Acceptance Criteria:** **Given** system approaching solution **When** checking convergence **Then** max |ΔP| < 1 Pa **And** mass error < 1e-9 kg/s, energy < 1e-6 kW **And** ALL circuits converge for global convergence **And** Jacobian uses sparse/block structure **And** uncoupled circuits give block-diagonal --- ### Story 4.8: Jacobian-Freezing Optimization **As a** performance-critical user, **I want** to freeze Jacobian updates, **So that** CPU time is reduced. **Acceptance Criteria:** **Given** nearly-converged system **When** Jacobian freezing enabled **Then** Jacobian computed once, reused **And** speed increases ~80% **And** auto-disabled if residuals increase --- ## Epic 5: Inverse Control & Optimization ### Story 5.1: Constraint Definition Framework **As a** control engineer, **I want** to define output constraints, **So that** I specify target operating conditions. **Acceptance Criteria:** **Given** measurable outputs **When** defining constraint (output - target = 0) **Then** constraint added to residuals **And** can reference any component output **And** multiple constraints supported --- ### Story 5.2: Bounded Control Variables **As a** control engineer, **I want** Box Constraints or Step Clipping, **So that** Newton steps stay physically possible. **Acceptance Criteria:** **Given** control variable with bounds [min, max] **When** computing Newton step (Δx) **Then** step scaled/clipped if exceeding bounds **And** variable never outside bounds during iterations **And** ControlSaturation error if converged solution exceeds bounds --- ### Story 5.3: Residual Embedding for Inverse Control **As a** systems engineer, **I want** constraints embedded with DoF validation, **So that** system is well-posed. **Acceptance Criteria:** **Given** constraint and control variable **When** solving system **Then** residual added to residual vector **And** control variable added to unknowns **And** solved simultaneously (one-shot) **And** Jacobian includes ∂constraint/∂control **And** DoF validated (equations = unknowns) **And** OverConstrainedSystem error if mismatch --- ### Story 5.4: Multi-Variable Control **As a** control engineer, **I want** to control multiple outputs simultaneously, **So that** I optimize complete operation. **Acceptance Criteria:** **Given** multiple constraints **When** defining control problem **Then** each constraint maps to one control variable **And** all solved simultaneously **And** all constraints satisfied within tolerance --- ### Story 5.5: Swappable Calibration Variables (Inverse Calibration One-Shot) **As a** R&D engineer calibrating a machine model against test bench data, **I want** to swap calibration coefficients (f_m, f_ua, f_power, etc.) into unknowns and measured values (Tsat, capacity, power) into constraints, **So that** the solver directly computes the calibration coefficients in one shot without external optimizer. **Context:** Each component has specific calibration factors. In normal simulation, f_ is fixed and outputs are computed. In calibration mode, measured values become constraints and f_ become unknowns. **Component → Calibration Factor Mapping:** | Component | f_ Factors | Measurable Values (can swap) | |-----------|------------|------------------------------| | Condenser | f_ua, f_dp | Tsat_cond, Q_cond (capacity), ΔP_cond | | Evaporator | f_ua, f_dp | Tsat_evap, Q_evap (capacity), ΔP_evap | | Compressor | f_m, f_power, f_etav | ṁ, Power, η_v | | Expansion Valve | f_m | ṁ | | Pipe | f_dp | ΔP | **Acceptance Criteria:** **Given** a Condenser with f_ua as calibration factor **When** I enable calibration mode and fix Tsat_cond to measured value **Then** f_ua becomes an unknown in solver state vector **And** residual added: Tsat_cond_computed - Tsat_cond_measured = 0 **And** solver computes f_ua directly **Given** an Evaporator with f_ua as calibration factor **When** I enable calibration mode and fix Tsat_evap to measured value **Then** same swap mechanism: f_ua → unknown, Tsat_evap → constraint **Given** a Compressor with f_power as calibration factor **When** I enable calibration mode and fix Power to measured value **Then** f_power becomes unknown **And** residual: Power_computed - Power_measured = 0 **Given** a Compressor with f_m as calibration factor **When** I enable calibration mode and fix mass flow ṁ to measured value **Then** f_m becomes unknown **And** residual: ṁ_computed - ṁ_measured = 0 **Given** a machine in cooling mode calibration **When** I impose evaporator cooling capacity Q_evap_measured **Then** Q_evap becomes constraint (Q_evap_computed - Q_evap_measured = 0) **And** corresponding f_ (typically f_ua on evaporator) becomes unknown **Given** a machine in heating mode calibration **When** I impose condenser heating capacity Q_cond_measured **Then** Q_cond becomes constraint **And** corresponding f_ (typically f_ua on condenser) becomes unknown **Given** multiple calibration swaps on same system **When** solver runs **Then** all f_ unknowns solved simultaneously with cycle equations (One-Shot) **And** Jacobian includes ∂constraint/∂f_ partial derivatives **And** DoF validated: (equations + calibration_constraints) = (unknowns + swapped_f_factors) **Given** a calibration swap configuration **When** serializing system to JSON **Then** swap state persisted (which f_ are unknowns, which values are fixed) **And** deserialization restores exact calibration mode --- ### Story 5.6: Control Variable Step Clipping in Solver **As a** control engineer, **I want** bounded control variables to be clipped at each Newton iteration, **So that** the solver never proposes physically impossible values (e.g., valve > 100%, frequency < min). **Context:** Story 5.2 implemented `BoundedVariable` and `clip_step()`, but the Newton-Raphson solver in Story 4.2 doesn't use them. The solver applies `x += delta` without checking bounds, allowing impossible values. **Acceptance Criteria:** **Given** a bounded variable with bounds [min, max] **When** the solver computes a Newton step Δx **Then** the new value is clipped: `x_new = clamp(x + Δx, min, max)` **And** the variable never goes outside bounds during ANY iteration **Given** control variables in the state vector at indices [2*edge_count, ...] **When** the solver updates the state vector **Then** bounded variables are clipped **And** regular edge states (P, h) are NOT clipped **Given** a converged solution with one or more bounded variables at bounds **When** checking convergence status **Then** `ConvergenceStatus::ControlSaturation` is returned **And** `saturated_variables()` returns the list of saturated variables **Given** existing code that doesn't use bounded variables **When** solving **Then** behavior is unchanged (no clipping applied) --- ## Epic 6: Multi-Platform APIs ### Story 6.1: Rust Native API **As a** Rust developer, **I want** clean, idiomatic Rust API, **So that** I integrate into Rust applications. **Acceptance Criteria:** **Given** a Rust application **When** using entropyk crate **Then** builder pattern for systems **And** all functions return Result **And** follows Rust conventions **And** KaTeX documentation --- ### Story 6.2: Python Bindings (PyO3) **As a** Python data scientist (Alice), **I want** zero-copy NumPy support, **So that** 100x speedup isn't wasted on conversion. **Acceptance Criteria:** **Given** Python script with rust_thermo_cycle **When** creating components **Then** API similar to tespy **And** errors mapped to Python exceptions **And** wheels on PyPI (manylinux) **And** Buffer Protocol and NumPy arrays supported **And** zero-copy for 10k+ element vectors **And** 100x faster than tespy --- ### Story 6.3: C FFI Bindings (cbindgen) **As a** HIL engineer (Sarah), **I want** C headers with explicit memory management, **So that** PLC integration has no memory leaks. **Acceptance Criteria:** **Given** C/C++ application **When** including headers **Then** extern "C" interface available **And** headers auto-generated via cbindgen **And** error codes as enum values **And** opaque pointers for complex types **And** every allocation has free function (e.g., entropyk_free_system) **And** latency < 20ms --- ### Story 6.4: WebAssembly Compilation **As a** web developer (Charlie), **I want** WASM with TabularBackend default, **So that** it works without CoolProp in browser. **Acceptance Criteria:** **Given** web application **When** importing WASM module **Then** can create and solve in browser **And** results JSON-serializable **And** deterministic performance **And** package on npm **And** defaults to TabularBackend (CoolProp unavailable in WASM) **And** works out-of-box in Chrome/Edge **And** cycle time < 100ms --- ### Story 6.5: CLI for Batch Execution **As a** data engineer (David), **I want** CLI for batch simulations, **So that** I process millions of scenarios. **Acceptance Criteria:** **Given** JSON config file **When** running entropyk-cli run config.json **Then** simulation executes **And** outputs results to JSON **And** batch mode supports parallel execution **And** progress reported **And** exit codes indicate success/failure --- ### Story 6.6: Python Solver Configuration Parity **As a** Python data scientist (Alice), **I want** full access to all solver configuration options from Python, **So that** I can optimize convergence for complex thermodynamic systems. **Problem Statement:** The current Python bindings expose only a subset of the Rust solver configuration options. This prevents Python users from: - Setting initial states for cold-start solving (critical for convergence) - Configuring Jacobian freezing for performance optimization - Using advanced convergence criteria for multi-circuit systems - Accessing timeout behavior configuration (ZOH fallback for HIL) **Gap Analysis:** | Rust Field | Python Exposed | Impact | |------------|----------------|--------| | `initial_state` | ❌ | Cannot warm-start solver | | `use_numerical_jacobian` | ❌ | Cannot debug Jacobian issues | | `jacobian_freezing` | ❌ | Missing 80% performance optimization | | `convergence_criteria` | ❌ | Cannot configure multi-circuit convergence | | `timeout_config` | ❌ | Cannot configure ZOH fallback | | `previous_state` | ❌ | Cannot use HIL zero-order hold | | `line_search_armijo_c` | ❌ | Cannot tune line search | | `divergence_threshold` | ❌ | Cannot adjust divergence detection | **Acceptance Criteria:** **Given** Python script using entropyk **When** configuring NewtonConfig or PicardConfig **Then** all Rust configuration fields are accessible: - [ ] `initial_state: Optional[List[float]]` - warm-start from previous solution - [ ] `use_numerical_jacobian: bool` - finite difference Jacobian - [ ] `jacobian_freezing: Optional[JacobianFreezingConfig]` - reuse Jacobian optimization - [ ] `convergence_criteria: Optional[ConvergenceCriteria]` - multi-circuit criteria - [ ] `timeout_config: TimeoutConfig` - detailed timeout behavior - [ ] `previous_state: Optional[List[float]]` - for HIL ZOH fallback - [ ] `line_search_armijo_c: float` - Armijo constant - [ ] `line_search_max_backtracks: int` - max backtracking iterations - [ ] `divergence_threshold: float` - divergence detection threshold **And** `SolverStrategy` enum is exposed: - [ ] `SolverStrategy.newton()` - create Newton strategy - [ ] `SolverStrategy.picard()` - create Picard strategy - [ ] `SolverStrategy.default()` - default strategy (Newton) - [ ] `strategy.solve(system)` - uniform solve interface **And** supporting types are exposed: - [ ] `JacobianFreezingConfig(max_frozen_iters, threshold)` - [ ] `TimeoutConfig(return_best_state_on_timeout, zoh_fallback)` - [ ] `ConvergenceCriteria` with per-circuit tolerances **And** backward compatibility is maintained: - [ ] Existing Python code continues to work - [ ] New fields have sensible defaults - [ ] Deprecation warnings for removed fields **Technical Notes:** 1. **Priority Order:** - P0: `initial_state` (critical for convergence) - P0: `SolverStrategy` (architectural consistency) - P1: `jacobian_freezing` (performance) - P1: `convergence_criteria` (multi-circuit) - P2: Other fields (advanced tuning) 2. **Implementation Approach:** - Extend `PyNewtonConfig` and `PyPicardConfig` structs - Add new `PySolverStrategy` wrapper class - Create `PyJacobianFreezingConfig`, `PyTimeoutConfig`, `PyConvergenceCriteria` - Update `lib.rs` to register new classes 3. **Testing:** - Unit tests for each new configuration field - Integration test: solve with `initial_state` from previous solution - Performance test: verify Jacobian freezing speedup **References:** - FR52: Python Solver Configuration Parity - Story 6.2: Python Bindings (PyO3) - foundation - Epic 4: Intelligent Solver Engine - Rust solver implementation --- ## Epic 7: Validation & Persistence ### Story 7.1: Mass Balance Validation **As a** simulation engineer, **I want** automatic mass conservation verification, **So that** I trust physical correctness. **Acceptance Criteria:** **Given** converged solution **When** computing mass balance **Then** error Σ ṁ_in - Σ ṁ_out < 1e-9 kg/s **And** violations trigger validation error **And** check performed after every solve --- ### Story 7.2: Energy Balance Validation **Status:** ✅ Done (2026-02-22) **As a** simulation engineer, **I want** First AND Second Law verification, **So that** thermodynamic consistency is guaranteed. **Acceptance Criteria:** **Given** converged solution **When** computing balances **Then** energy error < 1e-6 kW **And** violations trigger error with breakdown **And** Second Law check: entropy generation ≥ 0 **And** warning if negative entropy destruction **And** violations flagged --- ### Story 7.3: Traceability Metadata **As a** researcher (Robert), **I want** complete traceability metadata, **So that** simulations are reproducible. **Acceptance Criteria:** **Given** simulation result **When** accessing metadata **Then** includes solver_version, fluid_backend_version, input_hash (SHA-256) **And** SHA-256 uniquely identifies input **And** metadata in structured JSON --- ### Story 7.4: Debug Verbose Mode **As a** researcher (Robert), **I want** detailed convergence diagnostics, **So that** I debug non-converging systems. **Acceptance Criteria:** **Given** non-converging system **When** verbose mode enabled **Then** residuals logged each iteration **And** Jacobian condition number reported **And** solver switches logged **And** final state dumped --- ### Story 7.5: JSON Serialization & Deserialization **As a** system designer, **I want** complete system serialization with backend definition, **So that** models are reproducible across machines. **Acceptance Criteria:** **Given** system with components, connections, parameters **When** serializing to JSON **Then** complete state saved (Topology + Parameters + Fluid State) **And** fluid backend fully included (version, hash) **And** deserialization reconstructs identical system **And** round-trip produces identical results **And** human-readable, versioned format **And** explicit error if backend missing on load **And** error specifies required backend version --- ### Story 7.6: Component Calibration Parameters (Calib) **As a** R&D engineer matching simulation to real machine test data, **I want** calibration factors (Calib: f_m, f_dp, f_ua, f_power, f_etav) on components, **So that** simulation results align with manufacturer test data and field measurements. **Acceptance Criteria:** **Given** a component with nominal model parameters **When** Calib (calibration factors) are set (default 1.0 = no correction) **Then** f_m scales mass flow: ṁ_eff = f_m × ṁ_nominal (Compressor, Expansion Valve) **And** f_dp scales pressure drop: ΔP_eff = f_dp × ΔP_nominal (Pipe, Heat Exchanger) **And** f_ua scales thermal conductance: UA_eff = f_ua × UA_nominal (Evaporator, Condenser) **And** f_power scales compressor power: Ẇ_eff = f_power × Ẇ_nominal (Compressor) **And** f_etav scales volumetric efficiency: η_v,eff = f_etav × η_v,nominal (Compressor, displacement models) **Given** calibration factors from test data optimization **When** running simulation with calibrated components **Then** results match test data within configurable tolerance (e.g., capacity ±2%, power ±3%) **And** Calib values are serializable in JSON (persisted with system definition) **And** calibration workflow order documented: f_m → f_dp → f_ua, then f_power (prevents parameter fighting) **Given** a calibrated system **When** loading from JSON **Then** Calib parameters are restored **And** traceability metadata includes calibration source (test data hash or identifier) --- ### Story 7.7: ASHRAE 140 / BESTEST Validation (post-MVP) **As a** simulation engineer seeking industrial credibility, **I want** to validate Entropyk against ASHRAE Standard 140 and BESTEST test cases, **So that** results are comparable to EnergyPlus, TRNSYS, and Modelica. **Acceptance Criteria:** **Given** ASHRAE 140 / Airside HVAC BESTEST (AE101–AE445) test case definitions **When** running Entropyk on equivalent cycle configurations **Then** results fall within documented tolerance bands vs reference **And** discrepancies are documented (algorithmic, modeling assumptions) **And** CI includes regression tests for selected cases **Given** a new Entropyk release **When** running validation suite **Then** no regression beyond tolerance **And** validation report generated (JSON or markdown) --- ### Story 7.8: Inverse Calibration (Parameter Estimation) **As a** R&D engineer with test bench data, **I want** to estimate Calib (or component) parameters from measured data, **So that** the model matches my machine without manual tuning. **Acceptance Criteria:** **Given** test data (P, T, ṁ, Ẇ, Q at multiple operating points) **When** running inverse calibration **Then** optimizer minimizes error (e.g., MAPE) between model and data **And** estimated Calib (or coefficients) are returned **And** supports constraints (e.g., 0.8 ≤ f_ua ≤ 1.2) **And** calibration order respected (f_m → f_dp → f_ua → f_power) **Given** calibrated parameters **When** saving system **Then** Calib values and calibration_source (data hash) persisted in JSON --- ## Epic 8: Component-Fluid Integration ### Story 8.1: Fluid Backend Component Integration **As a** systems engineer, **I want** the thermodynamic components (Compressor, Condenser, etc.) to use the real `FluidBackend`, **So that** the residuals sent to the solver reflect accurate physical states. **Acceptance Criteria:** **Given** a component needing physical properties **When** the solver computes its residuals **Then** it natively references the configured `FluidBackend` **And** returns real values vs placeholders **And** handles missing backends gracefully with fallbacks --- ### Story 1.9: Air Coils (EvaporatorCoil, CondenserCoil) (post-MVP) **As a** HVAC engineer modeling split systems or air-source heat pumps, **I want** explicit EvaporatorCoil and CondenserCoil components, **So that** air-side heat exchangers (finned) are clearly distinguished from water-cooled. **Acceptance Criteria:** **Given** an EvaporatorCoil (refrigerant + air) **When** defining the component **Then** 4 ports: refrigerant in/out, air in/out **And** UA or geometry (fins, tubes) configurable **And** integrates with Fan for air flow **And** Calib (f_ua, f_dp) applicable **Given** a CondenserCoil **Then** same structure as EvaporatorCoil **And** refrigerant condenses on hot side, air on cold side --- ### Story 1.10: Pipe Helpers for Water and Refrigerant **As a** HVAC engineer modeling refrigerant and incompressible fluid circuits (water, seawater, glycol), **I want** convenient constructors `Pipe::for_incompressible()` and `Pipe::for_refrigerant()` with explicit ρ/μ from a fluid backend, **So that** I can create pipes without hardcoding fluid properties in the component. **Acceptance Criteria:** **Given** an incompressible fluid circuit (water, seawater, glycol) **When** calling `Pipe::for_incompressible(geometry, port_inlet, port_outlet, density, viscosity)` **Then** accepts explicit ρ, μ obtained from IncompressibleBackend (Story 2.7) **And** no hardcoded fluid properties in components crate **And** doc examples show water and glycol usage **Given** a refrigerant circuit (R134a, R410A, CO2, etc.) **When** calling `Pipe::for_refrigerant(geometry, port_inlet, port_outlet, density, viscosity)` **Then** accepts explicit ρ, μ (from CoolProp/tabular at design point) **And** doc examples show refrigerant circuit usage **And** doc states that ρ, μ vary with P,T — design-point values are typical **Given** the Pipe module documentation **When** reading the crate-level and `Pipe` docs **Then** explicitly states that Pipe serves for both refrigerant and incompressible fluids **And** includes a "Fluid Support" section: refrigerant (ρ/μ from backend) vs incompressible (ρ/μ from IncompressibleBackend) --- ## Epic 9: Coherence Corrections (Post-Audit) **Goal:** Fix coherence issues identified during the post-development audit to ensure complete and correct thermodynamic validation. **Innovation:** Systematic remediation of type duplications and incomplete implementations. **FRs covered:** FR35, FR36 (completion of Epic 7 validation) **Status:** 🔄 In Progress (2026-02-22) **Source:** [Coherence Audit Report](../implementation-artifacts/coherence-audit-remediation-plan.md) ### Story 9.1: CircuitId Type Unification **As a** Rust developer, **I want** a single, coherent `CircuitId` type, **So that** I avoid compilation errors when using solver and components modules together. **Acceptance Criteria:** **Given** the codebase currently has two `CircuitId` definitions **When** the unification is complete **Then** only one `CircuitId` exists in `entropyk_core` **And** `From` and `From<&str>` conversions are available **And** all modules re-export from `entropyk_core` --- ### Story 9.2: FluidId Type Unification **As a** Rust developer, **I want** a single `FluidId` type with a consistent API, **So that** I avoid confusion between `fluids::FluidId` and `components::port::FluidId`. **Acceptance Criteria:** **Given** the codebase currently has two `FluidId` definitions **When** the unification is complete **Then** only one `FluidId` exists in `entropyk_fluids` **And** both `as_str()` method and public field access work **And** all modules re-export from `entropyk_fluids` --- ### Story 9.3: ExpansionValve Energy Methods **As a** thermodynamic simulation engine, **I want** `ExpansionValve` to implement `energy_transfers()` and `port_enthalpies()`, **So that** the energy balance is correctly validated for refrigeration cycles. **Acceptance Criteria:** **Given** an ExpansionValve in a refrigeration cycle **When** `check_energy_balance()` is called **Then** the valve is included in the validation (not skipped) **And** `energy_transfers()` returns `(Power(0), Power(0))` (isenthalpic) **And** `port_enthalpies()` returns `[h_in, h_out]` --- ### Story 9.4: RefrigerantSource/RefrigerantSink Energy Methods **As a** thermodynamic simulation engine, **I want** `RefrigerantSource` and `RefrigerantSink` to implement `energy_transfers()` and `port_enthalpies()`, **So that** boundary conditions are correctly accounted for in the energy balance. **Acceptance Criteria:** **Given** RefrigerantSource or RefrigerantSink in a system **When** `check_energy_balance()` is called **Then** the component is included in the validation **And** `energy_transfers()` returns `(Power(0), Power(0))` **And** `port_enthalpies()` returns the port enthalpy --- ### Story 9.5: FlowSplitter/FlowMerger Energy Methods **As a** thermodynamic simulation engine, **I want** `FlowSplitter` and `FlowMerger` to implement `energy_transfers()` and `port_enthalpies()`, **So that** junctions are correctly accounted for in the energy balance. **Acceptance Criteria:** **Given** FlowSplitter or FlowMerger in a system **When** `check_energy_balance()` is called **Then** the component is included in the validation **And** `energy_transfers()` returns `(Power(0), Power(0))` (adiabatic) **And** `port_enthalpies()` returns all port enthalpies in order --- ### Story 9.6: Energy Validation Logging Improvement **As a** developer debugging a simulation, **I want** an explicit warning when components are skipped in energy validation, **So that** I can quickly identify missing implementations. **Acceptance Criteria:** **Given** a component without `energy_transfers()` or `port_enthalpies()` **When** `check_energy_balance()` is called **Then** a WARN-level log is emitted with component name and type **And** a summary lists all skipped components --- ### Story 9.7: Solver Refactoring - Split Files **As a** developer maintaining the code, **I want** solver strategies to be in separate files, **So that** code maintainability is improved. **Acceptance Criteria:** **Given** the current `solver.rs` is ~2800 lines **When** refactoring is complete **Then** each file is < 500 lines **And** public API is unchanged **And** `cargo test --workspace` passes --- ### Story 9.8: SystemState Dedicated Struct **As a** Rust developer, **I want** a dedicated `SystemState` struct instead of a type alias, **So that** I have layout validation and typed access methods. **Acceptance Criteria:** **Given** `SystemState` is currently `Vec` **When** the struct is created **Then** `pressure(edge_idx)` returns `Pressure` **And** `enthalpy(edge_idx)` returns `Enthalpy` **And** compatibility with `AsRef<[f64]>` is maintained --- ## Epic 11: Advanced HVAC Components **Goal:** Implement components for industrial chillers and heat pumps (flooded, BPHX, MovingBoundary, vendor data integration) **Innovation:** Native integration of vendor data and advanced heat transfer correlations **FRs covered:** FR53, FR54, FR55, FR56, FR57, FR58, FR59, FR60 **Status:** 📋 Backlog (2026-02-22) **Source:** [Technical Specifications](epic-11-technical-specifications.md) --- ### Story 11.1: Node - Passive Probe **As a** system modeler, **I want** a passive Node component (0 equations), **So that** I can extract P, h, T, quality, superheat, subcooling at any point in the circuit. **Status:** 📋 Backlog **FRs covered:** FR53 **Acceptance Criteria:** **Given** a Node with 1 inlet and 1 outlet **When** the system converges **Then** the Node contributes 0 equations (passive) **And** I can read pressure, temperature, enthalpy, quality **And** I can read superheat (if superheated) or subcooling (if subcooled) **And** the Node can be used as a junction in the graph topology **And** NodeMeasurements struct provides all extracted values --- ### Story 11.2: Drum - Recirculation Drum **As a** chiller engineer, **I want** a Drum component for evaporator recirculation, **So that** I can simulate flooded evaporator cycles. **Status:** 📋 Backlog **FRs covered:** FR56 **Acceptance Criteria:** **Given** a Drum with 2 inlets (feed + evaporator return) and 2 outlets (liquid + vapor) **When** the system converges **Then** liquid outlet is saturated (x=0) **And** vapor outlet is saturated (x=1) **And** mass and energy balances are satisfied **And** pressure equality across all ports **And** n_equations() returns 8 --- ### Story 11.3: FloodedEvaporator **As a** chiller engineer, **I want** a FloodedEvaporator component, **So that** I can simulate chillers with flooded evaporators. **Status:** 📋 Backlog **FRs covered:** FR54 **Acceptance Criteria:** **Given** a FloodedEvaporator with refrigerant side (flooded) and fluid side (water/glycol) **When** computing heat transfer **Then** refrigerant outlet is two-phase (50-80% vapor) **And** UA uses flooded-specific correlations (Longo default for BPHX) **And** Calib factors (f_ua, f_dp) are applicable **And** outlet_quality() method returns vapor quality **And** supports both LMTD and ε-NTU models --- ### Story 11.4: FloodedCondenser **As a** chiller engineer, **I want** a FloodedCondenser component, **So that** I can simulate chillers with accumulation condensers. **Status:** 📋 Backlog **FRs covered:** FR55 **Acceptance Criteria:** **Given** a FloodedCondenser with refrigerant side (flooded) and fluid side (water/glycol) **When** computing heat transfer **Then** liquid bath regulates condensing pressure **And** outlet is subcooled liquid **And** UA uses flooded-specific correlations **And** subcooling is calculated and accessible --- ### Story 11.5: BphxExchanger Base **As a** thermal engineer, **I want** a base BphxExchanger component, **So that** I can configure brazed plate heat exchangers for various applications. **Status:** 📋 Backlog **FRs covered:** FR57 **Acceptance Criteria:** **Given** a BphxExchanger with geometry parameters (plates, chevron angle, area) **When** computing heat transfer **Then** Longo (2004) correlation is used by default **And** alternative correlations can be selected via CorrelationSelector **And** both single-phase and two-phase zones are handled **And** HeatExchangerGeometry struct defines dh, area, chevron_angle, exchanger_type --- ### Story 11.6: BphxEvaporator **As a** heat pump engineer, **I want** a BphxEvaporator configured for DX or flooded operation, **So that** I can simulate plate evaporators accurately. **Status:** 📋 Backlog **FRs covered:** FR57 **Acceptance Criteria:** **Given** a BphxEvaporator in DX mode **When** computing heat transfer **Then** superheat is calculated **And** outlet quality ≥ 1 **Given** a BphxEvaporator in flooded mode **When** computing heat transfer **Then** outlet is two-phase **And** works with Drum for recirculation --- ### Story 11.7: BphxCondenser **As a** heat pump engineer, **I want** a BphxCondenser for refrigerant condensation, **So that** I can simulate plate condensers accurately. **Status:** 📋 Backlog **FRs covered:** FR57 **Acceptance Criteria:** **Given** a BphxCondenser **When** computing heat transfer **Then** outlet is subcooled liquid **And** subcooling is calculated **And** Longo condensation correlation is used by default --- ### Story 11.8: CorrelationSelector **As a** simulation engineer, **I want** to select from multiple heat transfer correlations, **So that** I can compare different models or use the most appropriate one. **Status:** 📋 Backlog **FRs covered:** FR60 **Acceptance Criteria:** **Given** a heat exchanger with CorrelationSelector **When** selecting a correlation **Then** available correlations include: - Longo (2004) - Default for BPHX evaporation/condensation - Shah (1979) - Tubes condensation - Shah (2021) - Plates condensation, recent - Kandlikar (1990) - Tubes evaporation - Gungor-Winterton (1986) - Tubes evaporation - Ko (2021) - Low-GWP plates - Dittus-Boelter (1930) - Single-phase turbulent - Gnielinski (1976) - Single-phase turbulent (more accurate) **And** each correlation implements HeatTransferCorrelation trait **And** each correlation documents its validity range (Re, quality, mass flux) **And** CorrelationResult includes h, Re, Pr, Nu, and validity status --- ### Story 11.9: MovingBoundaryHX - Zone Discretization **As a** precision engineer, **I want** a MovingBoundaryHX with phase zone discretization, **So that** I can model heat exchangers with accurate zone-by-zone calculations. **Status:** 📋 Backlog **FRs covered:** FR58 **Acceptance Criteria:** **Given** a MovingBoundaryHX **When** computing heat transfer **Then** zones are identified: superheated / two-phase / subcooled (refrigerant side) **And** each zone has its own UA calculated **And** total UA = Σ UA_zone **And** pinch temperature is calculated at zone boundaries **And** zone_boundaries vector contains relative positions [0.0, ..., 1.0] **And** supports configurable number of discretization points (default 51) --- ### Story 11.10: MovingBoundaryHX - Cache Optimization **As a** performance-critical user, **I want** the MovingBoundaryHX to cache zone calculations, **So that** iterations 2+ are much faster. **Status:** 📋 Backlog **FRs covered:** FR58 **Acceptance Criteria:** **Given** a MovingBoundaryHX with cache enabled **When** running iteration 1 **Then** full zone calculation is performed (~50ms) **And** zone boundaries and UA are cached in MovingBoundaryCache **When** running iterations 2+ **Then** cache is used if ΔP < 5% and Δm < 10% **And** computation is ~2ms (25x faster) **And** cache is invalidated on significant condition changes **And** cache includes: zone_boundaries, ua_per_zone, h_sat values, p_ref, m_ref --- ### Story 11.11: VendorBackend Trait **As a** library developer, **I want** a VendorBackend trait, **So that** vendor data can be loaded from multiple sources. **Status:** 📋 Backlog **FRs covered:** FR59 **Acceptance Criteria:** **Given** the VendorBackend trait in new `entropyk-vendors` crate **When** implementing a vendor **Then** methods include: - `vendor_name()` → vendor identifier - `list_compressor_models()` → available models - `get_compressor_coefficients(model)` → AHRI 540 coefficients - `list_bphx_models()` → available BPHX models - `get_bphx_parameters(model)` → geometry and UA curves - `compute_ua(model, params)` → vendor-specific UA calculation (optional) **And** data is loaded from JSON/CSV files in data/ directory **And** errors are handled via VendorError enum --- ### Story 11.12: Copeland Parser **As a** compressor engineer, **I want** Copeland compressor data integration, **So that** I can use Copeland coefficients in simulations. **Status:** 📋 Backlog **FRs covered:** FR59 **Acceptance Criteria:** **Given** Copeland compressor data files (JSON format) **When** loading via CopelandBackend **Then** AHRI 540 coefficients (10 capacity + 10 power) are extracted **And** valid models are discoverable via list_compressor_models() **And** errors for missing/invalid models are clear **And** data files follow format: data/copeland/compressors/{model}.json **And** index.json lists all available models --- ### Story 11.13: SWEP Parser **As a** heat exchanger engineer, **I want** SWEP BPHX data integration, **So that** I can use SWEP parameters in simulations. **Status:** 📋 Backlog **FRs covered:** FR59 **Acceptance Criteria:** **Given** SWEP BPHX data files (JSON + CSV) **When** loading via SwepBackend **Then** geometry (plates, area, dh, chevron_angle) is extracted **And** UA nominal value is available **And** UA curves (mass_flow_ratio vs ua_ratio) are loaded if available **And** model selection is supported via list_bphx_models() **And** data files follow format: data/swep/bphx/{model}.json --- ### Story 11.14: Danfoss Parser **As a** refrigeration engineer, **I want** Danfoss compressor data integration, **So that** I can use Danfoss coefficients in simulations. **Status:** 📋 Backlog **FRs covered:** FR59 **Acceptance Criteria:** **Given** Danfoss compressor data files **When** loading via DanfossBackend **Then** coefficients are extracted (AHRI 540 or Coolselector2 format) **And** compatible with standard compressor models **And** list_compressor_models() returns available models --- ### Story 11.15: Bitzer Parser **As a** refrigeration engineer, **I want** Bitzer compressor data integration, **So that** I can use Bitzer coefficients in simulations. **Status:** 📋 Backlog **FRs covered:** FR59 **Acceptance Criteria:** **Given** Bitzer compressor data files (CSV format) **When** loading via BitzerBackend **Then** coefficients are extracted **And** compatible with standard compressor models **And** supports Bitzer polynomial format **And** list_compressor_models() returns available models --- ## Future Epics (Vision – littérature HVAC) *Non planifiés – alignement avec EnergyPlus, Modelica, TRNSYS :* | Epic | Thème | Référence littérature | |------|-------|------------------------| | **Transient** | Simulation dynamique, start-up/shutdown, ODEs | Reddy, Purdue IIAR | | **Part-load** | Courbes PLF, pertes de cyclage | EnergyPlus PLF | | **Frost/Defrost** | Givre, dégivrage, enthalpy method | arXiv 2412.00017 | | **Moving Boundary** | Échangeurs discretisés, zones phase | Modelica Buildings, TIL Suite | | **Export ML** | Données synthétiques pour surrogates | arXiv 2505.15041 |