Entropyk/_bmad-output/implementation-artifacts/2-5-mixture-and-temperature-glide-support.md

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

  1. 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
  2. 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
  3. 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
  4. 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.rs with: 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 PropsSI with 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 PropsSI with 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 types
  • crates/fluids/src/backend.rs - Extend FluidBackend trait
  • crates/fluids/src/coolprop.rs - Implement mixture support
  • crates/fluids/src/tabular_backend.rs - Fallback handling
  • crates/fluids/src/cache.rs - Cache key must handle mixtures
  • crates/fluids/src/lib.rs - Export new types

Testing Requirements

Required Tests:

  • test_r454b_bubble_dew_points - Verify against CoolProp reference
  • test_temperature_glide - glide > 0 for zeotropic mixtures
  • test_mixture_ph_px_inputs - (P,h) and (P,x) work for mixtures
  • test_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

  1. crates/fluids/src/mixture.rs - NEW: Mixture struct, MixtureError, predefined mixtures
  2. crates/fluids/src/types.rs - MODIFIED: Added mixture variants to ThermoState
  3. crates/fluids/src/backend.rs - MODIFIED: Extended FluidBackend trait with mixture methods
  4. crates/fluids/src/coolprop.rs - MODIFIED: Implemented mixture support in CoolPropBackend
  5. crates/fluids/src/tabular_backend.rs - MODIFIED: Added mixture fallback handling
  6. crates/fluids/src/cache.rs - MODIFIED: Cache key includes mixture hash
  7. crates/fluids/src/errors.rs - MODIFIED: Added MixtureNotSupported variant
  8. crates/fluids/src/lib.rs - MODIFIED: Export Mixture and MixtureError
  9. 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