1995 lines
68 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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 (Σ + - Σ ( · 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<T, ThermoError> (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 (FlowSource, FlowSink) 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<T, Error>
**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 1N, FlowMerger N1) for compressible & incompressible fluids |
| FR50 | Epic 1 | Boundary Conditions (FlowSource, FlowSink) 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)
---
<!-- ALL EPICS AND STORIES -->
## 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 `2N1` 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<dyn Component>` (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 — FlowSource & FlowSink
**As a** simulation user,
**I want** `FlowSource` and `FlowSink` 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 `FlowSource::incompressible("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** `FlowSink::incompressible("Water", 1.5e5, None, port)` imposes a back-pressure (1 equation)
**And** `FlowSink` 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/CompressibleSource` and `Incompressible/CompressibleSink` are available
**Implementation:**
- `crates/components/src/flow_boundary.rs``FlowSource`, `FlowSink`
- 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<T, ThermoError>
**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 (AE101AE445) 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<u8>` 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: FlowSource/FlowSink Energy Methods
**As a** thermodynamic simulation engine,
**I want** `FlowSource` and `FlowSink` to implement `energy_transfers()` and `port_enthalpies()`,
**So that** boundary conditions are correctly accounted for in the energy balance.
**Acceptance Criteria:**
**Given** FlowSource or FlowSink 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<f64>`
**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 |