11 KiB
Story 2.5: Mixture and Temperature Glide Support
Status: done
Story
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
-
Mixture Composition Support (AC: #1)
- Extend FluidId or create MixtureId to support multi-component fluids (e.g., R454B = R32/R125)
- Define mixture composition structure: Component fractions (mass or mole basis)
- Backend can query properties for mixtures
-
Temperature Glide Calculation (AC: #2)
- Given zeotropic mixture at constant pressure, calculate bubble point and dew point temperatures
- Temperature glide = T_dew - T_bubble (non-zero for zeotropic mixtures)
- Backend correctly handles glide in heat exchanger calculations
-
Robust (P, h) and (P, x) Inputs (AC: #3)
- (P, h) preferred over (P, T) for two-phase mixtures - implement correctly
- (P, x) works for any quality value 0-1 in two-phase region
- Handle edge cases: saturation boundary, critical point region
-
Backend Integration (AC: #4)
- CoolPropBackend handles mixtures via PropsSI with mixture strings
- TabularBackend extends to mixture tables (or graceful fallback to CoolProp)
- Existing pure fluid functionality unchanged
Tasks / Subtasks
- Design mixture composition model (AC: #1)
- Define Mixture struct with components and fractions
- Support both mass fraction and mole fraction
- Handle common refrigerants: R454B, R32/R125, R410A, etc.
- Extend FluidId or create MixtureId type (AC: #1)
- FluidId for pure fluids, MixtureId for mixtures
- Parse mixture strings like "R454B" into components
- Implement bubble/dew point calculation (AC: #2)
- bubble_point_T(P, mixture) -> T_bubble
- dew_point_T(P, mixture) -> T_dew
- temperature_glide = T_dew - T_bubble
- Extend ThermoState for mixtures (AC: #1, #3)
- Add mixture parameter to ThermoState variants
- Handle (P, h, mixture) and (P, x, mixture) states
- Update FluidBackend trait (AC: #4)
- property() accepts mixture-aware ThermoState
- Add bubble_point(), dew_point() methods
- Maintain backward compatibility for pure fluids
- CoolPropBackend mixture support (AC: #4)
- Use CoolProp mixture functions (PropsSI with mass fractions)
- Handle CO2-based mixtures (R744 blends)
- TabularBackend mixture handling (AC: #4)
- Option A: Generate mixture tables (complex)
- Option B: Fallback to CoolProp for mixtures
- Document behavior clearly
- Testing (AC: #1-4)
- Test R454B bubble/dew points at various pressures
- Test temperature glide calculation vs reference
- Test (P, h) and (P, x) inputs for mixtures
- Test backward compatibility: pure fluids unchanged
Dev Notes
Previous Story Intelligence
From Story 2-4 (LRU Cache):
- FluidBackend trait in
crates/fluids/src/backend.rswith: property(), critical_point(), is_fluid_available(), phase(), list_fluids() - ThermoState enum variants: PressureTemperature, PressureEnthalpy, PressureEntropy, PressureQuality
- Cache wraps existing backends; new mixture support must work with CachedBackend
- Code review learnings: Avoid unwrap(), panic risks; use proper error handling
From Story 2-1 (Fluid Backend Trait Abstraction):
- Trait requires
Send + Sync- mixture support must preserve this - Use FluidId, Property, ThermoState from
crates/fluids/src/types.rs
From Story 2-2 (CoolProp Integration):
- CoolPropBackend wraps coolprop-sys
- CoolProp supports mixtures natively via
PropsSIwith mixture strings
From Story 2-3 (Tabular Interpolation):
- TabularBackend in tabular_backend.rs
- No allocation in hot path
- Mixture support: Tabular is complex; fallback to CoolProp is acceptable
Architecture Context
Mixture Handling (from Architecture):
- Architecture mentions FR27 (mixtures) and FR28 (temperature glide)
- FluidBackend trait must be extended for mixture support
- CoolProp handles mixtures well; Tabular may fallback
Architecture Location:
crates/fluids/
├── src/
│ ├── types.rs # Extend with Mixture, MixtureId
│ ├── backend.rs # FluidBackend trait extension
│ ├── coolprop.rs # CoolPropBackend mixture support
│ ├── tabular_backend.rs # TabularBackend (fallback for mixtures)
│ ├── cached_backend.rs # Cache wrapper (must handle mixtures)
│ └── ...
Technical Requirements
Mixture Representation:
// Option 1: Extend FluidId
pub enum FluidId {
Pure(String), // "R134a", "CO2"
Mixture(Vec<(String, f64)>), // [("R32", 0.5), ("R125", 0.5)]
}
// Option 2: Separate MixtureId
pub struct Mixture {
components: Vec<Component>,
fractions: Vec<f64>, // mass or mole basis
}
pub enum FluidOrMixture {
Fluid(FluidId),
Mixture(Mixture),
}
// ThermoState extension
pub enum ThermoState {
// ... existing variants ...
PressureEnthalpyMixture(Pressure, Enthalpy, Mixture),
PressureQualityMixture(Pressure, Quality, Mixture),
}
Temperature Glide Calculation:
pub trait FluidBackend {
// ... existing methods ...
/// Calculate bubble point temperature (liquid saturated)
fn bubble_point(&self, pressure: Pressure, mixture: &Mixture) -> Result<Temperature, FluidError>;
/// Calculate dew point temperature (vapor saturated)
fn dew_point(&self, pressure: Pressure, mixture: &Mixture) -> Result<Temperature, FluidError>;
/// Calculate temperature glide (T_dew - T_bubble)
fn temperature_glide(&self, pressure: Pressure, mixture: &Mixture) -> Result<f64, FluidError> {
let t_bubble = self.bubble_point(pressure, mixture)?;
let t_dew = self.dew_point(pressure, mixture)?;
Ok(t_dew.to_kelvin() - t_bubble.to_kelvin())
}
}
CoolProp Mixture Usage:
// CoolProp mixture string format: "R32[0.5]&R125[0.5]"
fn coolprop_mixture_string(mixture: &Mixture) -> String {
mixture.components.iter()
.zip(mixture.fractions.iter())
.map(|(name, frac)| format!("{}[{}]", name, frac))
.collect::<Vec<_>>()
.join("&")
}
// PropsSI call:PropsSI("T", "P", p, "Q", x, "R32[0.5]&R125[0.5]")
Common Refrigerant Mixtures:
- R454B: R32 (50%) / R1234yf (50%)
- R410A: R32 (50%) / R125 (50%)
- R32/R125: various blends
- CO2/R744 blends (transcritical)
Library/Framework Requirements
CoolProp:
- Version: 6.4+ (as per NFR11)
- Mixture handling via
PropsSIwith mixture strings - Mole fraction vs mass fraction: CoolProp uses mole fraction internally
No new dependencies required - extend existing trait and implementations.
File Structure Requirements
New files:
crates/fluids/src/mixture.rs- Mixture struct, MixtureId, parsing
Modified files:
crates/fluids/src/types.rs- Add mixture-related typescrates/fluids/src/backend.rs- Extend FluidBackend traitcrates/fluids/src/coolprop.rs- Implement mixture supportcrates/fluids/src/tabular_backend.rs- Fallback handlingcrates/fluids/src/cache.rs- Cache key must handle mixturescrates/fluids/src/lib.rs- Export new types
Testing Requirements
Required Tests:
test_r454b_bubble_dew_points- Verify against CoolProp referencetest_temperature_glide- glide > 0 for zeotropic mixturestest_mixture_ph_px_inputs- (P,h) and (P,x) work for mixturestest_pure_fluids_unchanged- existing pure fluid tests still pass
Reference Data:
- R454B at 1 MPa: T_bubble ≈ 273K, T_dew ≈ 283K, glide ≈ 10K
- Compare against CoolProp reference values
Project Structure Notes
Alignment:
- Architecture specifies mixture support in FluidBackend
- Follows trait-based design from Story 2-1
- Backward compatibility: pure fluids work exactly as before
References
- Epic 2 Story 2.5: [Source: planning-artifacts/epics.md#Story 2.5]
- FR27: System handles pure fluids and mixtures [Source: planning-artifacts/epics.md]
- FR28: Temperature Glide for zeotropic mixtures [Source: planning-artifacts/epics.md]
- Architecture Fluid Backend: [Source: planning-artifacts/architecture.md#Fluid Properties Backend]
- Story 2-1: [Source: implementation-artifacts/2-1-fluid-backend-trait-abstraction.md]
- Story 2-2: [Source: implementation-artifacts/2-2-coolprop-integration-sys-crate.md]
- Story 2-3: [Source: implementation-artifacts/2-3-tabular-interpolation-backend.md]
- Story 2-4: [Source: implementation-artifacts/2-4-lru-cache-for-fluid-properties.md]
- NFR11: CoolProp 6.4+ compatibility [Source: planning-artifacts/epics.md]
Git Intelligence Summary
Recent work patterns:
- Story 2-4 implemented thread-local LRU cache
- Stories 2-2, 2-3 completed CoolProp and Tabular backends
- Trait-based architecture established in Story 2-1
- fluids crate structured with backend abstraction
Patterns: Workspace structure stable; fluids crate uses entropyk-core types; deny(warnings) in lib.rs.
Dev Agent Record
Agent Model Used
opencode/minimax-m2.5-free
Debug Log References
- Implementation issues: Fixed ThermoState Copy/Clone issues, updated cache to handle mixtures, added mixture variants to tabular backend
Completion Notes List
- Created new mixture.rs module with Mixture struct supporting mass and mole fractions
- Extended ThermoState enum with mixture variants: PressureTemperatureMixture, PressureEnthalpyMixture, PressureQualityMixture
- Extended FluidBackend trait with bubble_point(), dew_point(), temperature_glide(), is_mixture_supported() methods
- Implemented mixture support in CoolPropBackend using PropsSI with mixture strings
- Updated TabularBackend to return MixtureNotSupported error (fallback to CoolProp for mixtures)
- Updated cache.rs to handle mixture states in cache keys
- Added MixtureNotSupported error variant to FluidError
- Tests require state.clone() due to ThermoState no longer implementing Copy
File List
- crates/fluids/src/mixture.rs - NEW: Mixture struct, MixtureError, predefined mixtures
- crates/fluids/src/types.rs - MODIFIED: Added mixture variants to ThermoState
- crates/fluids/src/backend.rs - MODIFIED: Extended FluidBackend trait with mixture methods
- crates/fluids/src/coolprop.rs - MODIFIED: Implemented mixture support in CoolPropBackend
- crates/fluids/src/tabular_backend.rs - MODIFIED: Added mixture fallback handling
- crates/fluids/src/cache.rs - MODIFIED: Cache key includes mixture hash
- crates/fluids/src/errors.rs - MODIFIED: Added MixtureNotSupported variant
- crates/fluids/src/lib.rs - MODIFIED: Export Mixture and MixtureError
- crates/fluids/src/cached_backend.rs - MODIFIED: Fixed state.clone() for caching
Change Log
- 2026-02-15: Initial implementation - Mixture struct, ThermoState mixture variants, FluidBackend trait extension, CoolPropBackend mixture support, TabularBackend fallback, all tests pass