1995 lines
68 KiB
Markdown
1995 lines
68 KiB
Markdown
---
|
||
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<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 (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<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 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)
|
||
|
||
---
|
||
|
||
<!-- 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 `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<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 — 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<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 (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<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: 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<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 |
|