Entropyk/_bmad-output/implementation-artifacts/2-1-fluid-backend-trait-abstraction.md

7.9 KiB

Story 2.1: Fluid Backend Trait Abstraction

Status: done

Story

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

  1. FluidBackend Trait Definition (AC: #1)

    • Define FluidBackend trait in crates/fluids/src/backend.rs
    • Trait must include property() method for thermodynamic property queries
    • Trait must include critical_point() method
    • Trait must support multiple backend implementations
  2. Method Signatures (AC: #2)

    • property(&self, fluid: FluidId, property: Property, state: ThermoState) -> Result<f64, FluidError>
    • critical_point(&self, fluid: FluidId) -> Result<CriticalPoint, FluidError>
  3. Backend Implementations (AC: #3)

    • CoolPropBackend - wraps CoolProp C++ library via sys-crate
    • TabularBackend - NIST tables with interpolation
    • TestBackend - mock backend for unit tests (no C++ dependency)

Tasks / Subtasks

  • Create crates/fluids crate structure (AC: #1)
    • Create crates/fluids/Cargo.toml with dependencies
    • Create crates/fluids/build.rs for CoolProp C++ compilation
    • Create crates/fluids/src/lib.rs with module structure
  • Create coolprop-sys sys-crate for C++ FFI (AC: #2)
    • Set up crates/fluids/coolprop-sys/ directory
    • Configure static linking for CoolProp
    • Create safe Rust wrappers
  • Define FluidBackend trait with required methods (AC: #1, #2)
    • Add documentation comments with examples
    • Ensure method signatures match architecture spec
  • Implement CoolPropBackend (AC: #3)
    • Wrap CoolProp C++ calls safely
    • Handle error translation between C++ and Rust
  • Implement TabularBackend (AC: #3)
    • Design table structure for fluid properties
    • Implement interpolation algorithm
    • Verify < 0.01% deviation from NIST
  • Implement TestBackend (AC: #3)
    • Simple mock implementation for testing
    • No external dependencies
  • Add comprehensive tests (AC: #3)
    • Unit tests for all backends
    • Integration tests comparing backends

Dev Notes

Architecture Context

FluidBackend Trait Design (from Architecture Decision Document):

trait FluidBackend {
    fn property(&self, fluid: FluidId, property: Property, state: ThermoState) 
        -> Result<f64, FluidError>;
    fn critical_point(&self, fluid: FluidId) -> Result<CriticalPoint, FluidError>;
}

struct CoolPropBackend { /* sys-crate wrapper */ }
struct TabularBackend { /* NIST tables with interpolation */ }
struct TestBackend { /* mocks for unit tests */ }

Caching Strategy:

  • LRU cache in backends to avoid redundant CoolProp calls
  • Cache invalidation on temperature/pressure changes
  • Thread-safe (Arc<Mutex>) for future parallelization

Critical Point Handling (CO2 R744):

fn property_with_damping(&self, state: ThermoState) -> Result<f64, FluidError> {
    if self.near_critical_point(state) {
        // Automatic damping to avoid NaN in partial derivatives
        self.compute_with_damping(state)
    } else {
        self.property(state)
    }
}

Workspace Structure

Crate Location: crates/fluids/

crates/fluids/
├── Cargo.toml
├── build.rs                    # Compilation CoolProp C++
├── coolprop-sys/               # Sys-crate C++
│   ├── Cargo.toml
│   ├── build.rs
│   └── src/
│       └── lib.rs
└── src/
    ├── lib.rs
    ├── backend.rs             # FluidBackend trait HERE
    ├── coolprop.rs             # FR25: CoolProp integration
    ├── tabular.rs             # FR26: Tables NIST
    ├── cache.rs               # LRU cache
    └── damping.rs             # FR29: Critical point

Inter-crate Dependencies:

  • fluids crate depends on core crate for types
  • solver and components will depend on fluids for properties
  • This is the foundation for Epic 2 (all other stories depend on this)

Technical Requirements

Rust Naming Conventions (MUST FOLLOW):

  • snake_case : modules, functions, variables
  • CamelCase : types, traits, enum variants
  • NO prefix I for traits (use FluidBackend, not IFluidBackend)

Required Dependencies (from Architecture):

  • CoolProp C++ (via sys-crate)
  • nalgebra (for future vector operations)
  • thiserror (for FluidError enum)
  • serde (for serialization)
  • lru-cache or dashmap (for caching)

Error Handling Pattern:

#[derive(Error, Debug)]
pub enum FluidError {
    #[error("Fluid {fluid} not found")]
    UnknownFluid { fluid: String },
    
    #[error("Invalid state for property calculation: {reason}")]
    InvalidState { reason: String },
    
    #[error("CoolProp error: {0}")]
    CoolPropError(String),
    
    #[error("Critical point not available for {fluid}")]
    NoCriticalPoint { fluid: String },
}

pub type FluidResult<T> = Result<T, FluidError>;

Implementation Strategy

  1. Create crate structure first with Cargo.toml and build.rs
  2. Set up coolprop-sys for C++ compilation
  3. Define FluidBackend trait with property() and critical_point()
  4. Implement TestBackend first - easiest, no dependencies
  5. Implement CoolPropBackend - wraps sys-crate
  6. Implement TabularBackend - interpolation tables
  7. Add caching layer for performance
  8. Add critical point damping for CO2

Testing Requirements

Required Tests:

  • Unit tests for each backend implementation
  • Test that all backends return consistent results for known states
  • Test error handling for invalid fluids/states
  • Test caching behavior
  • Test critical point damping for CO2

Test Pattern:

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_backend_consistency() {
        let coolprop = CoolPropBackend::new();
        let test = TestBackend::new();
        
        // Same input should give same output
        let state = ThermoState::from_pressure_temperature(101325.0, 300.0);
        let cp_result = coolprop.property("R134a", Property::Density, state);
        let test_result = test.property("R134a", Property::Density, state);
        
        assert_relative_eq!(cp_result.unwrap(), test_result.unwrap(), epsilon = 0.01);
    }
}

Project Structure Notes

Alignment with Unified Structure:

  • Follows workspace-based multi-crate architecture
  • Uses trait-based design as specified in Architecture
  • Located in crates/fluids/ per project structure
  • Sys-crate pattern for CoolProp C++ integration

Dependencies to Core Crate:

  • Will need types from crates/core: Pressure, Temperature, Enthalpy, etc.
  • Story 1.2 (Physical Types) created NewTypes that should be used here

References

  • Architecture Fluid Properties Backend: [Source: planning-artifacts/architecture.md#Fluid Properties Backend]
  • Project Structure: [Source: planning-artifacts/architecture.md#Project Structure & Boundaries]
  • FR25-FR29, FR40: Fluid requirements in Epic 2 [Source: planning-artifacts/epics.md#Epic 2]
  • Naming Conventions: [Source: planning-artifacts/architecture.md#Naming Patterns]
  • Sys-crate Pattern: [Source: planning-artifacts/architecture.md#C++ Integration]

Dev Agent Record

Agent Model Used

opencode/minimax-m2.5-free

Debug Log References

N/A - Story just created

Completion Notes List

  • Story file created with comprehensive context from epics and architecture
  • All acceptance criteria defined with checkboxes
  • Dev notes include architecture patterns, code examples, testing requirements
  • References to source documents provided

File List

  1. 2-1-fluid-backend-trait-abstraction.md - New story file (this file)

Ultimate context engine analysis completed - comprehensive developer guide created