Snapshot WIP: solver HP epic progress, BPHX/HX physics, BMAD skill refresh.
Some checks failed
CI / check (push) Has been cancelled

Capture uncommitted solver robustness work (regularization, domain errors, linear solver lifecycle, tube DP/MSH), web workbench updates, and synced BMAD skills across IDE agent folders before starting BPHX pressure-drop.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-19 16:35:31 +02:00
parent 88620790d6
commit 5bd180b5b8
1363 changed files with 101041 additions and 58547 deletions

View File

@@ -9,7 +9,7 @@ depends_on: 0-1-jacobian-killing-pattern-audit-fd-test-harness
# Story 0.2: Phantom-Gradient Regularization Standard
Status: review
Status: done
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
<!-- Ultimate context engine analysis completed - comprehensive developer guide created -->
@@ -98,6 +98,19 @@ so that fixes are uniform and derivative-safe instead of ad-hoc.
- [x] 6.2 `cargo test -p entropyk-components` green — proves no accidental physics regressions from doc-only / non-touch policy.
- [x] 6.3 Diff review: production component residual files should show **zero** physics diffs attributable to this story.
### Review Findings
- [x] [Review][Patch] smooth_clamp overlapping bounds validation [crates/core/src/smoothing.rs:458]
- [x] [Review][Patch] normalize panic on NaN inputs [crates/core/src/smoothing.rs:69]
- [x] [Review][Patch] smooth_clamp panic on NaN inputs [crates/core/src/smoothing.rs:458]
- [x] [Review][Patch] smooth_max_derivative and smooth_min_derivative NaN division [crates/core/src/smoothing.rs:310]
- [x] [Review][Patch] smooth_abs_derivative NaN division and overflow handling [crates/core/src/smoothing.rs:418]
- [x] [Review][Patch] NaN propagation in derivatives under degenerate/fallback paths [crates/core/src/smoothing.rs:310]
- [x] [Review][Defer] smooth_max and smooth_min overflow for extremely large inputs [crates/core/src/smoothing.rs:293] — deferred, pre-existing
- [x] [Review][Defer] smooth_abs overflow for extremely large inputs [crates/core/src/smoothing.rs:396] — deferred, pre-existing
- [x] [Review][Defer] slope amplification for very small widths in step derivatives [crates/core/src/smoothing.rs:123] — deferred, pre-existing
- [x] [Review][Defer] cubic_blend and quintic_blend bound swapping [crates/core/src/smoothing.rs:205] — deferred, pre-existing
## Dev Notes
### Context & Positioning

View File

@@ -7,317 +7,272 @@ Status: done
## Story
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.
I want to model a compressor using AHRI 540 standard coefficients or SST/SDT polynomial curves,
so that I can simulate real compressor behavior from manufacturer data.
## Acceptance Criteria
1. **Compressor Structure** (AC: #1)
- [x] Define `Compressor` struct with suction and discharge ports
- [x] Store 10 AHRI 540 coefficients (M1, M2, M3, M4, M5, M6, M7, M8, M9, M10)
- [x] Include compressor speed (RPM) and displacement volume
- [x] Support Type-State pattern for port connection safety
- [x] Define `Compressor<State>` struct with suction and discharge ports (Type-State pattern)
- [x] Support two performance models via `CompressorModel` enum: `Ahri540(Ahri540Coefficients)` and `SstSdt(SstSdtCoefficients)`
- [x] Store 10 AHRI 540 coefficients (M1-M10) in `Ahri540Coefficients` struct
- [x] Store SST/SDT polynomial curves in `SstSdtCoefficients` (mass_flow_curve + power_curve as `Polynomial2D`)
- [x] Include rotational speed (RPM), displacement volume, and mechanical efficiency
- [x] Include `Calib` struct (f_m, f_power, f_etav) for calibration factors
- [x] Include `CalibIndices` for solver-level embedded calibration variables
- [x] Include `CircuitId` for multi-circuit machine support (FR9)
- [x] Include `OperationalState` (On, Off, Bypass) for FR6-FR8
- [x] Include edge state indices (`suction_m_idx`, `suction_h_idx`, `discharge_m_idx`, `discharge_h_idx`) for CM1.3
2. **AHRI 540 Equations Implementation** (AC: #2)
- [x] Implement mass flow rate calculation per AHRI 540 standard
- [x] Implement power consumption calculation per AHRI 540 standard
- [x] Implement capacity (cooling/heating) calculation
- [x] Handle both cooling and heating mode coefficients
2. **AHRI 540 Performance Equations** (AC: #2)
- [x] Mass flow: `ṁ = M1 × (1 - (P_suction/P_discharge)^(1/M2)) × ρ_suction × V_disp × N/60 × f_etav × f_m`
- [x] Power (cooling): `Ẇ = (M3 + M4 × (P_discharge/P_suction) + M5 × T_suction + M6 × T_discharge) × f_power`
- [x] Power (heating): `Ẇ = (M7 + M8 × (P_discharge/P_suction) + M9 × T_suction + M10 × T_discharge) × f_power`
- [x] Cooling capacity: `Q̇_cool = ṁ × (h_evap_out - h_evap_in)`
- [x] Heating capacity: `Q̇_heat = ṁ × (h_cond_out - h_cond_in)`
- [x] COP calculation: `COP = Q̇ / Ẇ`
3. **Residual Computation** (AC: #3)
- [x] Implement `compute_residuals()` for Component trait
- [x] Mass flow continuity: ṁ_suction = ṁ_discharge
- [x] Energy balance: Power_input = ṁ × (h_discharge - h_suction) / η_mech
- [x] Isentropic efficiency based on AHRI correlation
3. **SST/SDT Polynomial Model** (AC: #3)
- [x] Mass flow: `ṁ = Σ a_ij × SST^i × SDT^j` via `Polynomial2D`
- [x] Power: `Ẇ = Σ b_ij × SST^i × SDT^j` via `Polynomial2D`
- [x] Support bilinear and biquadratic configurations
- [x] `SstSdtCoefficients::bilinear()` and `::biquadratic()` factory methods
- [x] SST/SDT model does not distinguish cooling/heating (single power curve)
4. **Jacobian Entries** (AC: #4)
- [x] Provide analytical Jacobian entries for ∂residual/∂P
- [x] Provide analytical Jacobian entries for ∂residual/∂h
- [x] Derivatives respect to mass flow for power equation
- [x] Jacobian compatible with solver from Story 4.x
- [ ] **PARTIAL**: Complex derivatives use finite difference approximation (to be refined)
4. **Residual Computation — 3 equations** (AC: #4)
- [x] `n_equations()` returns **3** (upgraded from 2 to include CM1.3 mass conservation)
- [x] `r0`: Mass flow continuity — `ṁ_calc ṁ_state = 0`
- [x] `r1`: Energy balance — `Ẇ_calc ṁ_state × (h_discharge h_suction) / η_mech = 0`
- [x] `r2`: Mass conservation (CM1.3) — `ṁ_discharge ṁ_suction = 0`
- [x] `OperationalState::Off``r0 = ṁ_suction`, `r1 = 0`, `r2 = ṁ_discharge ṁ_suction`
- [x] `OperationalState::Bypass``r0 = P_suction P_discharge`, `r1 = h_suction h_discharge`, `r2 = ṁ_discharge ṁ_suction`
5. **Component Trait Integration** (AC: #5)
- [x] Implement `Component` trait for `Compressor`
- [x] Implement `get_ports()` returning suction and discharge ports
- [x] Implement `n_equations()` returning correct equation count
- [x] Compatible with existing Component trait from Story 1.1
5. **Jacobian Entries** (AC: #5)
- [x] Row 0: `∂r0/∂ṁ_suction = -1` (exact, CM1.3)
- [x] Row 0: `∂r0/∂h_suction` — numerical via `approximate_derivative` (density depends on h)
- [x] Row 1: `∂r1/∂ṁ_suction = (h_discharge h_suction) / η_mech` (exact)
- [x] Row 1: `∂r1/∂h_suction`, `∂r1/∂h_discharge` — numerical (power depends on temperature estimates)
- [x] Row 2: `∂r2/∂ṁ_discharge = +1`, `∂r2/∂ṁ_suction = -1` (exact, CM1.3)
- [x] **PARTIAL**: Derivatives `∂r0/∂h` and `∂r1/∂h` use finite-difference approximation (temperature estimation not differentiable analytically in current placeholder backend)
6. **Validation & Testing** (AC: #6)
- [x] Unit tests for AHRI 540 coefficient storage
- [x] Unit tests for mass flow calculation
- [x] Unit tests for power consumption calculation
- [x] Validation test with certified AHRI data (within 1% tolerance)
- [x] Test with different refrigerants (R134a, R410A, R454B)
6. **Component Trait Integration** (AC: #6)
- [x] Implement `Component` trait for `Compressor<Connected>`
- [x] `get_ports()` returns empty slice (lifetime constraint — use `get_ports_slice()` instead)
- [x] `get_ports_slice()` returns `[&Port<Connected>; 2]` as workaround
- [x] `set_system_context()` stores edge indices for CM1.3 integration
- [x] `energy_transfers()` returns `(electrical_power, heat_rejection)` for energy validation
- [x] `signature()` returns `"Compressor(fluid=X, circuit=Y)"`
7. **Validation & Testing** (AC: #7)
- [x] Unit tests for `Ahri540Coefficients` storage and validation
- [x] Unit tests for `SstSdtCoefficients` bilinear and biquadratic
- [x] Unit tests for mass flow calculation (AHRI 540 + SST/SDT)
- [x] Unit tests for power consumption (cooling + heating modes)
- [x] Unit tests for residuals at equilibrium
- [x] Jacobian finite-difference verification
- [x] Tests with R134a, R410A, R454B refrigerants
- [x] Tests for Off and Bypass operational states
## Tasks / Subtasks
- [x] Create `crates/components/src/compressor.rs` module (AC: #1, #5)
- [x] Define `Compressor<State>` struct with Type-State pattern
- [x] Add suction and discharge port fields (`port_suction`, `port_discharge`)
- [x] Add AHRI 540 coefficient fields (M1-M10)
- [x] Add speed and displacement fields
- [x] Implement constructor `Compressor::new()`
- [x] Implement AHRI 540 coefficient storage (AC: #1)
- [x] Define `Ahri540Coefficients` struct with M1-M10 fields
- [x] Add coefficient validation (range checks)
- [x] Support both cooling and heating coefficient sets
- [x] Add documentation with AHRI 540 reference
- [x] Implement mass flow calculation (AC: #2)
- [x] Equation: ṁ = M1 × (1 - (P_suction/P_discharge)^(1/M2)) × ρ_suction × V_disp × N/60
- [x] Implement density calculation via fluid backend
- [x] Handle edge cases (zero speed, invalid pressures)
- [x] Unit tests with known manufacturer data
- [x] Implement power consumption calculation (AC: #2)
- [x] Equation: Ẇ = M3 + M4 × (P_discharge/P_suction) + M5 × T_suction + M6 × T_discharge
- [x] Alternative form using M7-M10 for heating mode
- [x] Add mechanical efficiency factor
- [x] Unit tests with certified data
- [x] Implement capacity calculation (AC: #2)
- [x] Cooling capacity: Q̇_cool = ṁ × (h_evap_out - h_evap_in)
- [x] Heating capacity: Q̇_heat = ṁ × (h_cond_out - h_cond_in)
- [x] COP calculation: COP = Q̇ / Ẇ
- [x] Implement residual computation (AC: #3)
- [x] Mass flow residual: ṁ_calc - ṁ_state = 0
- [x] Energy residual: Ẇ_calc - ṁ × (h_out - h_in) / η = 0
- [x] Integration with Component::compute_residuals()
- [x] Error handling for invalid states
- [x] Implement Jacobian entries (AC: #4)
- [x] ∂(mass_residual)/∂P_suction and ∂(mass_residual)/∂P_discharge
- [x] ∂(energy_residual)/∂h_suction and ∂(energy_residual)/∂h_discharge
- [x] Derivative of density with respect to pressure
- [x] Integration with Component::jacobian_entries()
- [x] Implement Component trait (AC: #5)
- [x] `compute_residuals(&self, state: &SystemState, residuals: &mut ResidualVector)`
- [x] `jacobian_entries(&self, state: &SystemState, jacobian: &mut JacobianBuilder)`
- [x] `n_equations(&self) -> usize` (returns 2 for mass and energy)
- [x] `get_ports(&self) -> &[Port<Connected>]`
- [x] Write comprehensive tests (AC: #6)
- [x] Test coefficient storage and retrieval
- [x] Test mass flow with known AHRI test data (Bitzer, Copeland, Danfoss)
- [x] Test power consumption against certified data
- [x] Test residuals computation
- [x] Test Jacobian entries (finite difference verification)
- [x] Test Component trait implementation
- [x] Test with multiple refrigerants
- [x] Create `crates/components/src/compressor.rs` module (AC: #1)
- [x] Define `Ahri540Coefficients` struct with M1-M10 fields and `validate()`
- [x] Define `SstSdtCoefficients` struct wrapping `Polynomial2D`
- [x] Define `CompressorModel` enum (`Ahri540` | `SstSdt`)
- [x] Define `Compressor<State>` with all fields (model, ports, calib, circuit_id, edge indices)
- [x] Implement `Compressor::new()` for AHRI 540 constructor
- [x] Implement `Compressor::with_model()` for generic model constructor
- [x] Implement `connect()` method (Type-State transition Disconnected → Connected)
- [x] Create `crates/components/src/polynomials.rs` module (AC: #3)
- [x] Define `Polynomial2D` struct for 2D polynomial evaluation
- [x] Implement `evaluate(x, y)`, `validate()`, `bilinear()`, `biquadratic()`
- [x] Implement AHRI 540 calculations (AC: #2)
- [x] `mass_flow_rate(density, sst_k, sdt_k, state)` — dispatches to model
- [x] `power_consumption_cooling(t_suction, t_discharge, state)`
- [x] `power_consumption_heating(t_suction, t_discharge, state)`
- [x] `cooling_capacity()`, `heating_capacity()`, `cop()`
- [x] Implement residual computation (AC: #4)
- [x] 3-equation system: r0 mass flow, r1 energy, r2 mass conservation
- [x] OperationalState dispatch (On/Off/Bypass)
- [x] Calibration factors applied via `Calib` and `CalibIndices`
- [x] Implement Jacobian entries (AC: #5)
- [x] Exact entries where analytically tractable
- [x] `approximate_derivative()` helper for numerical differentiation
- [x] Implement Component trait (AC: #6)
- [x] `compute_residuals`, `jacobian_entries`, `n_equations`, `get_ports`
- [x] `set_system_context` for CM1.3 edge index injection
- [x] `energy_transfers`, `signature`
- [x] `get_ports_slice()` workaround
- [x] Register exports (AC: #6)
- [x] `crates/components/src/lib.rs` — pub mod compressor; pub mod polynomials; re-exports
- [x] `crates/entropyk/src/lib.rs` — re-export compressor types
- [x] Write comprehensive tests (AC: #7)
## Dev Notes
### Architecture Context
**Critical Pattern - Component Implementation:**
This is the FIRST concrete component implementation. It establishes patterns for ALL future components (Condenser, Evaporator, Expansion Valve, etc.).
**Critical patterns established here (followed by ALL subsequent components):**
```rust
// Pattern to follow for all components
// Type-State pattern: Disconnected → Connected
pub struct Compressor<State> {
model: CompressorModel,
port_suction: Port<State>,
port_discharge: Port<State>,
coefficients: Ahri540Coefficients,
speed: f64, // RPM
displacement: f64, // m³/rev
_state: PhantomData<State>,
}
impl Component for Compressor<Connected> {
fn compute_residuals(&self, state: &SystemState, residuals: &mut ResidualVector) {
// Implementation here
}
fn jacobian_entries(&self, state: &SystemState, jacobian: &mut JacobianBuilder) {
// Implementation here
}
fn n_equations(&self) -> usize { 2 }
fn get_ports(&self) -> &[Port<Connected>] {
&[self.port_suction, self.port_discharge]
}
}
```
**AHRI 540 Standard Equations:**
The AHRI 540 standard provides 10 coefficients (M1-M10) for compressor mapping:
**Mass Flow Rate:**
```
ṁ = M1 × (1 - (P_discharge/P_suction)^(1/M2)) × ρ_suction × V_disp × N/60
```
Where:
- M1: Flow coefficient
- M2: Pressure ratio exponent
- ρ_suction: Suction gas density (from fluid backend)
- V_disp: Displacement volume (m³/rev)
- N: Rotational speed (RPM)
**Power Consumption:**
```
Ẇ = M3 + M4 × (P_discharge/P_suction) + M5 × T_suction + M6 × T_discharge
```
Alternative for heating:
```
Ẇ = M7 + M8 × (P_discharge/P_suction) + M9 × T_suction + M10 × T_discharge
```
**Isentropic Efficiency:**
```
η_isen = (h_out,isentropic - h_in) / (h_out,actual - h_in)
```
### Technical Requirements
**Rust Naming Conventions (MUST FOLLOW):**
- Struct: `CamelCase` (Compressor, Ahri540Coefficients)
- Methods: `snake_case` (compute_residuals, mass_flow_rate)
- Fields: `snake_case` (port_suction, displacement_volume)
- Generic parameter: `State` (not `S` for clarity)
**Required Types:**
```rust
pub struct Ahri540Coefficients {
pub m1: f64,
pub m2: f64,
pub m3: f64,
pub m4: f64,
pub m5: f64,
pub m6: f64,
pub m7: f64,
pub m8: f64,
pub m9: f64,
pub m10: f64,
}
pub struct Compressor<State> {
port_suction: Port<State>,
port_discharge: Port<State>,
coefficients: Ahri540Coefficients,
speed_rpm: f64,
displacement_m3_per_rev: f64,
mechanical_efficiency: f64,
calib: Calib, // Static calibration factors
calib_indices: CalibIndices, // Dynamic solver-embedded indices
fluid_id: FluidId,
circuit_id: CircuitId,
operational_state: OperationalState,
suction_m_idx: Option<usize>, // CM1.3: injected by set_system_context
suction_h_idx: Option<usize>,
discharge_m_idx: Option<usize>,
discharge_h_idx: Option<usize>,
_state: PhantomData<State>,
}
```
**Location in Workspace:**
**Performance model dispatch:**
```rust
pub enum CompressorModel {
Ahri540(Ahri540Coefficients),
SstSdt(SstSdtCoefficients),
}
```
crates/components/
├── Cargo.toml
└── src/
├── lib.rs # Re-exports
├── port.rs # From Story 1.3
├── compressor.rs # THIS STORY
└── state_machine.rs # Future story
`Compressor::new()` takes `Ahri540Coefficients` directly; `Compressor::with_model()` takes `CompressorModel`.
**3-equation system (post CM1.3 upgrade):**
```
n_equations() = 3
r0: ṁ_calc(model) ṁ_state = 0 (mass flow matching)
r1: Ẇ_calc × Δh / η_mech = 0 (energy balance)
r2: ṁ_discharge ṁ_suction = 0 (mass conservation per edge)
```
### Implementation Strategy
**CM1.3 state variable layout (edge-based):**
- `set_system_context(state_offset, external_edge_state_indices)` injects `(m_idx, p_idx, h_idx)` per edge
- Edge 0 = suction (incoming), Edge 1 = discharge (outgoing)
- Fallback to positional defaults `[0,1,3,2]` when indices not set (unit tests)
1. **Create compressor module** - Define Compressor struct with Type-State
2. **Implement AHRI 540 equations** - Core thermodynamic calculations
3. **Implement Component trait** - Integration with solver framework
4. **Add comprehensive tests** - Validation against certified data
5. **Document with KaTeX** - Mathematical equations in rustdoc
**Calibration factors (FR43):**
```rust
// Calib struct (entropyk_core)
pub struct Calib {
pub f_m: f64, // Mass flow correction factor
pub f_power: f64, // Power correction factor
pub f_etav: f64, // Volumetric efficiency correction
// ... other factors
}
// Applied as: ṁ_eff = f_etav × ṁ_vol × f_m
// Ẇ_eff = f_power × Ẇ_nominal
```
`CalibIndices` holds `Option<usize>` indices into the state vector so calibration variables can be solver unknowns (inverse calibration, FR45/FR51).
**get_ports() FIXME — known limitation:**
```rust
// Component trait get_ports() returns empty slice — lifetime constraint.
// Use get_ports_slice() for actual port access:
pub fn get_ports_slice(&self) -> [&Port<Connected>; 2]
```
This is a known API limitation tracked for future refactor.
**Fluid property placeholders:**
Internal functions `estimate_density(fluid_id, p_pa, h_jkg)` and `estimate_temperature(fluid_id, p_pa, h_jkg)` use lookup tables for R134a, R410A, R454B. These are **temporary** and will be replaced by full CoolProp integration via `FluidBackend` (Story 2.2 / Epic 8). When real backend is available, `suction_state()` / `discharge_state()` methods provide full `ThermoState`.
### Technical Requirements
**AHRI 540 Mass Flow (note inverse ratio — p_suction/p_discharge):**
```
η_vol = 1 (P_suction / P_discharge)^(1/M2) ← inverse ratio ensures η_vol ∈ [0,1]
ṁ = M1 × η_vol × ρ_suction × V_disp × (N/60) × f_etav × f_m
```
**Do NOT use (P_discharge/P_suction) for volumetric efficiency** — negative η_vol would result.
**SST/SDT Polynomial2D:**
```rust
// Polynomial2D evaluates Σ c[i][j] × x^i × y^j
// bilinear: 2×2 coefficient matrix
// biquadratic: 3×3 coefficient matrix
pub struct Polynomial2D { coefficients: Vec<Vec<f64>> }
```
**Rust naming conventions:**
- Struct: `CamelCase``Compressor`, `Ahri540Coefficients`, `SstSdtCoefficients`, `CompressorModel`
- Methods: `snake_case``compute_residuals`, `mass_flow_rate`, `power_consumption_cooling`
- Generic param: `State` (not `S`)
### File Structure
```
crates/
├── components/
│ ├── Cargo.toml # [dev-dependencies] approx = "0.5"
│ └── src/
│ ├── lib.rs # pub mod compressor; pub mod polynomials; re-exports
│ ├── compressor.rs # This story (2276 lines)
│ ├── polynomials.rs # Polynomial2D (used by SstSdtCoefficients)
│ └── port.rs # Port<State> from Story 1.3
├── core/
│ └── src/
│ ├── calib.rs # Calib, CalibIndices types (Story 7.6)
│ └── types.rs # Pressure, Temperature, Enthalpy, MassFlow (Story 1.2)
└── entropyk/
└── src/
└── lib.rs # Re-exports: pub use entropyk_components::compressor::*
```
### Testing Requirements
**Required Tests:**
- Coefficient storage: Verify all 10 coefficients stored correctly
- Mass flow: Test against known Bitzer 4TES-9 data
- Power consumption: Test against AHRI certified values
- Residuals: Verify residual equations equal zero at equilibrium
- Jacobian: Finite difference verification of derivatives
- Component trait: Verify trait implementation works with solver
**Test Pattern:**
**Required test coverage:**
```rust
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
// Test data from AHRI 540 certified test
const TEST_COMPRESSOR_COEFFS: Ahri540Coefficients = Ahri540Coefficients {
m1: 0.85,
m2: 0.9,
m3: 500.0,
m4: 1500.0,
m5: -2.5,
m6: 1.8,
m7: 0.0, // Not used for cooling
m8: 0.0,
m9: 0.0,
m10: 0.0,
};
#[test]
fn test_mass_flow_calculation() {
let compressor = Compressor::new(TEST_COMPRESSOR_COEFFS, 2900.0, 0.0001);
// Test with known operating point
let p_suction = Pressure::from_bar(3.5);
let p_discharge = Pressure::from_bar(15.0);
let mass_flow = compressor.mass_flow_rate(p_suction, p_discharge);
// Expected value from certified data
assert_relative_eq!(mass_flow.to_kg_per_s(), 0.05, epsilon = 1e-4);
}
#[test]
fn test_power_consumption() {
let compressor = Compressor::new(TEST_COMPRESSOR_COEFFS, 2900.0, 0.0001);
let power = compressor.power_consumption(
Temperature::from_celsius(5.0),
Temperature::from_celsius(45.0),
Pressure::from_bar(3.5),
Pressure::from_bar(15.0),
);
// Expected value from certified data
assert_relative_eq!(power, 3500.0, epsilon = 50.0);
}
#[test]
fn test_component_trait_implementation() {
let compressor = create_test_compressor();
assert_eq!(compressor.n_equations(), 2);
let ports = compressor.get_ports();
assert_eq!(ports.len(), 2);
}
}
// Tests live in #[cfg(test)] mod tests at bottom of compressor.rs
// Helper: test_coefficients() returns standard Ahri540Coefficients for tests
// 1. Coefficient storage/validation
#[test] fn test_ahri540_coefficients_storage()
#[test] fn test_ahri540_coefficients_validate_invalid_m2()
#[test] fn test_sst_sdt_bilinear_creation()
#[test] fn test_sst_sdt_biquadratic_creation()
// 2. Mass flow
#[test] fn test_mass_flow_calculation()
#[test] fn test_mass_flow_with_high_pressure_ratio()
#[test] fn test_sst_sdt_mass_flow()
// 3. Power consumption
#[test] fn test_power_consumption_cooling()
#[test] fn test_power_consumption_heating()
// 4. Residuals
#[test] fn test_residuals_3_equations()
#[test] fn test_off_state_residuals()
#[test] fn test_bypass_state_residuals()
// 5. Jacobian
#[test] fn test_jacobian_entries() // finite difference verification
// 6. Refrigerants
#[test] fn test_compressor_with_r134a()
#[test] fn test_compressor_with_r410a()
#[test] fn test_compressor_with_r454b()
```
### Project Structure Notes
**Crate Location:** `crates/components/src/compressor.rs`
- First concrete component implementation
- Uses Port<State> from Story 1.3
- Uses Pressure, Temperature, Enthalpy from Story 1.2
- Implements Component trait from Story 1.1
**Inter-crate Dependencies:**
```
core (types: Pressure, Temperature, etc.)
components → core + fluids (for density calculation)
solver → components (uses Component trait)
```
**Alignment with Unified Structure:**
- ✅ Uses Type-State pattern from Story 1.3
- ✅ Located in `crates/components/` per architecture
- ✅ Implements Component trait from Story 1.1
- ✅ Uses NewType pattern from Story 1.2
- ✅ Follows naming conventions from architecture
### References
- **AHRI 540 Standard:** Air-Conditioning, Heating, and Refrigeration Institute Standard 540
- **Architecture Component Model:** [Source: planning-artifacts/architecture.md#Component Model]
- **Project Structure:** [Source: planning-artifacts/architecture.md#Project Structure & Boundaries]
- **Story 1.1 Component Trait:** Previous story established Component trait interface
- **Story 1.2 Physical Types:** NewType Pressure, Temperature, Enthalpy usage
- **Story 1.3 Port System:** Port<State> and connection patterns
- **FR1:** Compressor AHRI 540 modeling requirement [Source: planning-artifacts/epics.md#FR Coverage Map]
- **Inverse pressure ratio decision:** Using `P_suction/P_discharge` (not `P_discharge/P_suction`) ensures `η_vol ≥ 0` — see compressor.rs line ~806
- **Component Trait:** `[Source: crates/components/src/lib.rs]``Component`, `ConnectedPort`, `ResidualVector`, `JacobianBuilder`
- **CM1.3 edge-based mass flow:** `[Source: _bmad-output/implementation-artifacts/cm-1-3-mass-flow-newton-unknown.md]`
- **Calib / CalibIndices:** `[Source: crates/core/src/calib.rs]` — FR43, FR45, FR51
- **OperationalState:** `[Source: crates/components/src/lib.rs]` — FR6, FR7, FR8
- **CircuitId:** `[Source: crates/core/src/types.rs]` — FR9
- **Polynomial2D:** `[Source: crates/components/src/polynomials.rs]`
- **FR1:** Compressor AHRI 540 — `[Source: _bmad-output/planning-artifacts/epics.md#FR1]`
- **Architecture Component Model:** `[Source: _bmad-output/planning-artifacts/architecture.md#Component Model]`
## Senior Developer Review (AI)
@@ -328,24 +283,23 @@ solver → components (uses Component trait)
### Issues Found and Fixed
**🔴 HIGH (2 fixed):**
1. **Documentation Error** - Mass flow equation in docstring showed wrong pressure ratio direction. Fixed to show `P_suction/P_discharge`.
2. **AC #4 Partial Implementation** - Jacobian uses finite differences for complex derivatives, not fully analytical. Marked AC as PARTIAL with explanation.
1. **Documentation Error** Mass flow equation in docstring showed wrong pressure ratio direction. Fixed to show `P_suction/P_discharge`.
2. **AC #5 Partial Implementation** Jacobian uses finite differences for `∂r/∂h` derivatives. Marked AC as PARTIAL.
**🟡 MEDIUM (4 fixed):**
3. **get_ports() Returns Empty** - Component trait method returns empty slice due to lifetime constraints. Added `get_ports_slice()` method for actual access.
4. **Missing R454B Test** - AC #6 claimed R454B testing but only R134a/R410A tested. Added R454B test case.
5. **File List Incomplete** - `Cargo.toml` modifications not documented. Added to File List.
6. **Non-existent Dependency** - Story mentioned `entropyk-fluids` which doesn't exist. Removed reference.
3. **get_ports() Returns Empty** — Lifetime constraint. Added `get_ports_slice()` workaround.
4. **Missing R454B Test** Added R454B test case.
5. **File List Incomplete** `Cargo.toml` and `polynomials.rs` not documented. Added.
6. **Non-existent Dependency** — Removed `entropyk-fluids` reference (not a separate crate).
**🟢 LOW (2 fixed):**
7. **Placeholder Documentation** - Added explicit Story 2.2 references for CoolProp integration.
8. **Test Quality** - Improved `test_mass_flow_with_high_pressure_ratio` clarity.
7. **Placeholder Documentation** Added explicit Story 2.2 / Epic 8 references for CoolProp integration.
8. **Test Quality** Improved `test_mass_flow_with_high_pressure_ratio` clarity.
### Action Items
- [x] All HIGH and MEDIUM issues fixed
- [x] All tests pass (61 unit + 20 doc tests)
- [x] All tests pass (61 unit + 20 doc tests at time of original review)
- [x] Story status updated to "done"
- [x] Sprint status synced
---
@@ -359,57 +313,54 @@ opencode/kimi-k2.5-free
- Implementation completed: 2026-02-15
- Code review completed: 2026-02-15
- All tests passing: 61 unit tests + 20 doc tests
- Story context refreshed: 2026-06-26 (reflects evolved codebase: SST/SDT model, CM1.3 integration, Calib)
- All tests passing: 61+ unit tests
- Clippy validation: Zero warnings
- Issues fixed: 8 (2 HIGH, 4 MEDIUM, 2 LOW)
### Completion Notes List
**Implementation Summary:**
- ✅ Created `crates/components/src/compressor.rs` with complete Compressor component
- ✅ Implemented `Ahri540Coefficients` struct with validation for all 10 coefficients (M1-M10)
- ✅ Implemented Type-State pattern with `Compressor<Disconnected>` and `Compressor<Connected>`
- ✅ Implemented AHRI 540 mass flow equation with inverse pressure ratio for volumetric efficiency
- ✅ Implemented power consumption equations for both cooling (M3-M6) and heating (M7-M10) modes
- ✅ Implemented cooling/heating capacity calculations and COP computation
- ✅ Implemented full Component trait integration with 2 equations (mass flow + energy balance)
- ✅ Implemented analytical Jacobian entries with finite difference approximations for derivatives
- ✅ Added placeholder fluid property functions for density and temperature estimation (R134a, R410A)
- ✅ Comprehensive test suite: 34 unit tests covering all functionality
- ✅ All doc tests pass with working examples
**Original Implementation (2026-02-15):**
- ✅ Created `crates/components/src/compressor.rs` with complete `Compressor` component
- ✅ Implemented `Ahri540Coefficients` with validation for all 10 coefficients
- ✅ Implemented Type-State pattern `Compressor<Disconnected>` / `Compressor<Connected>`
- ✅ Implemented AHRI 540 mass flow with inverse pressure ratio for volumetric efficiency
- ✅ Implemented power equations for cooling (M3-M6) and heating (M7-M10) modes
- ✅ Implemented full `Component` trait integration
**Code Review Fixes Applied:**
-**Fix 1:** Corrected mass flow equation documentation (was showing wrong pressure ratio direction)
-**Fix 2:** Added `get_ports_slice()` method for actual port access (Component trait `get_ports()` has lifetime constraints)
-**Fix 3:** Added R454B refrigerant test and support in placeholder fluid functions
-**Fix 4:** Updated File List to include `Cargo.toml` modifications
-**Fix 5:** Marked AC #4 as PARTIAL (Jacobian uses finite differences for complex derivatives)
-**Fix 6:** Removed non-existent `entropyk-fluids` dependency reference
-**Fix 7:** Added explicit references to Story 2.2 for CoolProp integration
-**Fix 8:** Improved test coverage (61 tests vs 60 originally)
**Post-implementation Evolutions (reflected in current codebase):**
-**SST/SDT model added** (`SstSdtCoefficients`, `Polynomial2D`, `CompressorModel` enum)
-**CM1.3 upgrade**: `n_equations()` now returns 3 (added mass conservation residual r2)
-**`set_system_context()`** added: edge index injection for per-edge (ṁ,P,h) state variables
-**Calibration integration**: `Calib` and `CalibIndices` fields, factors applied in calculations
-**`energy_transfers()`** added for energy validation framework
-**`suction_state()` / `discharge_state()`** added for full ThermoState access via FluidBackend
-**`OperationalState`** support (On/Off/Bypass) per FR6-FR8
**Technical Decisions:**
- Used inverse pressure ratio (P_suction/P_discharge) for volumetric efficiency calculation to ensure positive values
- Set M2 coefficient to 2.5 in tests to allow reasonable pressure ratios (was 0.9 which caused negative efficiency)
- Implemented placeholder fluid property functions that will be replaced by real CoolProp integration in Story 2.2
- Used finite difference approximation for Jacobian derivatives where analytical forms are complex
- R454B uses R410A properties as approximation (both are similar zeotropic blends)
- Inverse pressure ratio (`P_suction/P_discharge`) for volumetric efficiency — ensures η_vol ∈ [0,1]
- `CompressorModel` enum rather than trait object — avoids object-safety issues with Clone/PartialEq
- Finite-difference approximation for `∂r/∂h` Jacobian — placeholder until real FluidBackend
- `get_ports()` returns empty slice (lifetime constraint) — `get_ports_slice()` as workaround
- R454B uses R410A properties as approximation in placeholder backend
### File List
Created files:
1. `crates/components/src/compressor.rs` - Compressor component with AHRI 540 implementation
**Created:**
1. `crates/components/src/compressor.rs` Compressor component (~2276 lines)
2. `crates/components/src/polynomials.rs``Polynomial2D` for SST/SDT model
Modified files:
1. `crates/components/src/lib.rs` - Added compressor module and re-exports
2. `crates/components/Cargo.toml` - Added `approx` dev-dependency for floating-point assertions
**Modified:**
1. `crates/components/src/lib.rs` Added `pub mod compressor`, `pub mod polynomials`, re-exports
2. `crates/components/Cargo.toml` Added `approx = "0.5"` dev-dependency
3. `crates/entropyk/src/lib.rs` — Re-exported compressor types
### Dependencies
**Cargo.toml dependencies (already present):**
```toml
# crates/components/Cargo.toml
[dependencies]
entropyk-core = { path = "../core" }
entropyk-fluids = { path = "../fluids" } # for suction_state()/discharge_state()
thiserror = "1.0"
serde = { version = "1.0", features = ["derive"] }
@@ -417,40 +368,6 @@ serde = { version = "1.0", features = ["derive"] }
approx = "0.5"
```
**Note:** Placeholder fluid property functions (`estimate_density`, `estimate_temperature`) will be replaced by actual CoolProp integration in Story 2.2.
## Story Context Summary
**Critical Implementation Points:**
1. This is THE FIRST concrete component - establishes patterns for ALL future components
2. MUST implement Component trait correctly for solver integration
3. AHRI 540 equations must be accurate (industry standard)
4. Jacobian entries must be analytical (not numerical) for performance
5. Tests must validate against certified data (within 1% tolerance)
**Common Pitfalls to Avoid:**
- ❌ Using numerical differentiation for Jacobian (too slow)
- ❌ Forgetting to validate coefficients (M1-M10 ranges)
- ❌ Incorrect density calculation (must use fluid backend)
- ❌ Breaking Component trait object safety
- ❌ Wrong equation form (cooling vs heating coefficients)
- ❌ Missing edge cases (zero speed, negative pressures)
**Success Criteria:**
- ✅ Compressor implements Component trait correctly
- ✅ AHRI 540 equations match certified data within 1%
- ✅ Analytical Jacobian entries verified with finite differences
- ✅ All tests pass including validation against manufacturer data
- ✅ Follows all architecture patterns and conventions
**Dependencies on Previous Stories:**
- **Story 1.1:** Component trait exists - implement it correctly
- **Story 1.2:** Physical types (Pressure, Temperature) exist - use them
- **Story 1.3:** Port<State> exists - use for suction/discharge ports
**Next Story (1.5) Dependencies:**
Story 1.5 (Generic Heat Exchanger Framework) will use similar patterns for Condenser/Evaporator. The Component trait implementation patterns established here will be reused.
---
**Ultimate context engine analysis completed - comprehensive developer guide created**
**Ultimate context engine analysis completed — story refreshed 2026-06-26 to reflect evolved implementation (SST/SDT model, CM1.3 3-equation system, Calib integration)**

View File

@@ -26,28 +26,30 @@
# - Dev moves story to 'review', then Dev runs code-review (fresh context, ideally different LLM)
generated: 2026-07-18T12:51:31Z
last_updated: 2026-07-18T16:30:00Z
last_updated: 2026-07-19T14:40:00Z
project: Entropyk
project_key: ENTROPYK
tracking_system: file-system
story_location: "{project-root}/_bmad-output/implementation-artifacts"
development_status:
epic-0: in-progress
epic-0: done
0-1-fix-failing-tests: done
epic-0-retrospective: optional
# --- Epic 0: Physical Regularization & Jacobian Health (epics-solver-hp.md) ---
0-1-jacobian-killing-pattern-audit-fd-test-harness: done
0-2-phantom-gradient-regularization-standard: review
0-3-valve-flow-exv-regularization: backlog
0-4-heat-exchanger-regularization: backlog
0-5-domain-sweep-jacobian-health-gate: backlog
0-2-phantom-gradient-regularization-standard: done
0-3-valve-flow-exv-regularization: done
0-4-heat-exchanger-regularization: done
0-5-domain-sweep-jacobian-health-gate: done
0-6-emergent-pressure-seed-robustness: review
0-7-bphx-property-call-reduction: review
# --- Epic 1: Performance Foundation - Linear Algebra Modernization ---
epic-1: in-progress
1-1-phase-0-baseline-golden-safety-net: in-progress
1-2-core-crate-typed-convergence-taxonomy: backlog
1-3-recoverable-domain-violation-errors: backlog
1-4-linearsolver-trait-explicit-lifecycle: backlog
1-1-phase-0-baseline-golden-safety-net: review
1-2-core-crate-typed-convergence-taxonomy: done
1-3-recoverable-domain-violation-errors: review
1-4-linearsolver-trait-explicit-lifecycle: review
1-5-dense-faer-backend-strangler-migration: backlog
1-6-factorization-scaling-reuse-frozen-jacobians: backlog
1-7-zero-allocation-residual-jacobian-assembly: backlog