Entropyk/_bmad-output/implementation-artifacts/1-8-auxiliary-transport-components.md

8.8 KiB
Raw Blame History

Story 1.8: Auxiliary & Transport Components

Status: ready-for-dev

Story

As a system integrator, I want to model Pumps, VFDs, and Pipes with complete HVAC transport components, So that I can simulate complete HVAC systems with water, glycol, and air circuits.

Acceptance Criteria

  1. Pump Component (AC: #1) IMPLEMENTED

    • Pump with polynomial performance curves (head, efficiency)
    • Affinity laws for variable speed operation (Q ∝ N, H ∝ N², P ∝ N³)
    • Component trait implementation with compute_residuals, jacobian_entries
    • StateManageable trait (ON/OFF/BYPASS states)
    • Hydraulic power calculation: P = Q × ΔP / η
  2. Pipe Component (AC: #2) IMPLEMENTED

    • Darcy-Weisbach pressure drop calculation
    • Haaland friction factor (laminar + turbulent)
    • for_incompressible() and for_refrigerant() constructors
    • PipeGeometry with roughness constants
    • Calib (f_dp) for calibration
    • Component and StateManageable traits
  3. Fan Component (AC: #3) IMPLEMENTED

    • Fan with polynomial performance curves (static pressure, efficiency)
    • Affinity laws for variable speed operation
    • Static pressure and total pressure (with velocity pressure)
    • Component and StateManageable traits
  4. VFD Abstraction (AC: #4) 🔴 NOT IMPLEMENTED

    • Create Vfd component that wraps Pump/Fan/Compressor
    • Vfd exposes speed_ratio as controllable parameter
    • Vfd implements Component trait (delegates to wrapped component)
    • Vfd speed is bounded [0.0, 1.0] for inverse control
  5. Integration Tests (AC: #5) ⚠️ PARTIAL

    • Pump unit tests (20+ tests in pump.rs)
    • Pipe unit tests (25+ tests in pipe.rs)
    • Fan unit tests (15+ tests in fan.rs)
    • VFD integration tests
    • Full circuit simulation with Pump + Pipe + VFD

Tasks / Subtasks

  • Pump Implementation (AC: #1)

    • Create PumpCurves struct with quadratic/cubic curves
    • Implement AffinityLaws scaling in pump.rs
    • Add Component trait for Pump
    • Add StateManageable trait for Pump
    • Implement compute_residuals with ON/OFF/BYPASS handling
    • Write 20+ unit tests
  • Pipe Implementation (AC: #2)

    • Create PipeGeometry struct with roughness constants
    • Implement Darcy-Weisbach equation
    • Implement Haaland friction factor
    • Add for_incompressible() and for_refrigerant() constructors
    • Add Component and StateManageable traits
    • Write 25+ unit tests
  • Fan Implementation (AC: #3)

    • Create FanCurves struct with polynomial curves
    • Implement static_pressure_rise and total_pressure_rise
    • Add Component and StateManageable traits
    • Write 15+ unit tests
  • VFD Abstraction (AC: #4)

    • Create Vfd generic wrapper in new file vfd.rs
    • Implement Vfd::new(wrapped_component, initial_speed)
    • Implement speed() and set_speed() methods
    • Implement Component trait (delegates to wrapped)
    • Implement BoundedVariable for speed [0.0, 1.0]
    • Write unit tests for Vfd
  • Integration Tests (AC: #5)

    • Test Pump + Pipe circuit
    • Test Fan in air handling system
    • Test Vfd controlling Pump speed

Dev Notes

🔥 CRITICAL: Most Components Already Implemented!

This story is mostly COMPLETE. The following components already exist:

Component File Lines Tests Status
Pump pump.rs 780 20+ Done
Pipe pipe.rs 1010 25+ Done
Fan fan.rs 636 15+ Done
Polynomials polynomials.rs 702 15+ Done
VFD - - - 🔴 Not Implemented

Remaining Work: VFD Abstraction

The only missing piece is the VFD (Variable Frequency Drive) abstraction. Currently:

  • Pump, Fan have speed_ratio field (0.0 to 1.0)
  • set_speed_ratio() method exists
  • But no Vfd component that wraps these for inverse control

VFD Implementation Approach

Option A: Wrapper Component (Recommended)

pub struct Vfd<T: Component> {
    wrapped: T,
    speed: BoundedVariable,  // [0.0, 1.0]
}
  • Wraps Pump, Fan, or Compressor
  • Delegates Component trait methods
  • Exposes speed as BoundedVariable for inverse control

Option B: Trait-based Approach

pub trait SpeedControllable: Component {
    fn speed_ratio(&self) -> f64;
    fn set_speed_ratio(&mut self, ratio: f64) -> Result<(), ComponentError>;
}
  • Pump, Fan, Compressor implement this trait
  • Vfd uses dynamic dispatch

Architecture Context

FR Coverage:

  • FR6-FR8: Component states ON/OFF/BYPASS
  • FR40: Incompressible fluids support (via for_incompressible)

Component Trait Pattern: All components follow the same pattern:

impl Component for Xxx<Connected> {
    fn compute_residuals(&self, state: &SystemState, residuals: &mut ResidualVector) -> Result<(), ComponentError>;
    fn jacobian_entries(&self, state: &SystemState, jacobian: &mut JacobianBuilder) -> Result<(), ComponentError>;
    fn n_equations(&self) -> usize;
    fn get_ports(&self) -> &[ConnectedPort];
}

impl StateManageable for Xxx<Connected> {
    fn state(&self) -> OperationalState;
    fn set_state(&mut self, state: OperationalState) -> Result<(), ComponentError>;
    // ...
}

Pump Details

Performance Curves:

Head: H = a₀ + a₁Q + a₂Q² + a₃Q³
Efficiency: η = b₀ + b₁Q + b₂Q²
Power: P_hydraulic = ρ × g × Q × H / η

Affinity Laws (implemented in polynomials.rs):

AffinityLaws::scale_flow(flow, speed_ratio)  // Q₂ = Q₁ × (N₂/N₁)
AffinityLaws::scale_head(head, speed_ratio)  // H₂ = H₁ × (N₂/N₁)²
AffinityLaws::scale_power(power, speed_ratio) // P₂ = P₁ × (N₂/N₁)³

Pipe Details

Darcy-Weisbach Equation:

ΔP = f × (L/D) × (ρ × v² / 2)

Haaland Friction Factor:

1/√f = -1.8 × log10[(ε/D/3.7)^1.11 + 6.9/Re]

Roughness Constants (pipe.rs:roughness):

SMOOTH: 1.5e-6          // Copper, plastic
STEEL_COMMERCIAL: 4.5e-5
GALVANIZED_IRON: 1.5e-4
CAST_IRON: 2.6e-4
CONCRETE: 1.0e-3
PLASTIC: 1.5e-6

Zero-Flow Regularization:

  • Re clamped to MIN_REYNOLDS = 1.0 (prevents division by zero)
  • Consistent with Story 3.5 zero-flow branch handling

Fan Details

Standard Air Properties (fan.rs:standard_air):

DENSITY: 1.204 kg/m³  // at 20°C, 101325 Pa
CP: 1005.0 J/(kg·K)

Total Pressure:

P_total = P_static + P_velocity = P_static + ½ρ

File Locations

crates/components/src/
├── pump.rs           # ✅ DONE - 780 lines
├── pipe.rs           # ✅ DONE - 1010 lines
├── fan.rs            # ✅ DONE - 636 lines
├── polynomials.rs    # ✅ DONE - 702 lines
├── vfd.rs            # 🔴 TODO - NEW FILE
└── lib.rs            # Add pub mod vfd; and re-exports

Testing Requirements

Existing Tests (do not modify):

  • pump.rs: 20+ unit tests covering curves, affinity laws, residuals
  • pipe.rs: 25+ unit tests covering geometry, friction, pressure drop
  • fan.rs: 15+ unit tests covering curves, pressure, power

New Tests Required for VFD:

  • Vfd creation and wrapping
  • Speed bounds enforcement
  • Component trait delegation
  • Integration: Vfd + Pump circuit

Common Pitfalls to Avoid

  • Do NOT reimplement Pump, Pipe, Fan - they are complete
  • Do NOT use bare f64 for physical quantities
  • Do NOT use unwrap/expect in production code
  • Do NOT break existing tests
  • Ensure Vfd is object-safe (if using trait objects)

References

  • FR6-FR8: Operational states ON/OFF/BYPASS [Source: epics.md#Story 1.7]
  • FR40: Incompressible fluids support [Source: epics.md#Story 2.7]
  • Component Model: Trait-based with Type-State [Source: architecture.md#Component Model]
  • Zero-Panic Policy: Result<T, ThermoError> [Source: architecture.md#Error Handling Strategy]
  • Story 1.7: StateManageable trait (implemented in Pump, Pipe, Fan)
  • Story 3.5: Zero-flow branch handling (implemented in pipe.rs)

Previous Story Intelligence

From Story 1.7 (Component State Machine):

  • StateManageable trait with state(), set_state(), can_transition_to()
  • OperationalState enum: On, Off, Bypass
  • State history for debugging
  • All implemented correctly in Pump, Pipe, Fan

From Story 4.2 (Newton-Raphson):

  • Components provide jacobian_entries()
  • Pump, Pipe, Fan already provide numerical Jacobians

From Story 5.2 (Bounded Control Variables):

  • BoundedVariable with clip_step()
  • Vfd speed should use BoundedVariable for inverse control

Dev Agent Record

Agent Model Used

{{agent_model_name_version}}

Debug Log References

Completion Notes List

File List

  • crates/components/src/vfd.rs - NEW: Vfd wrapper component
  • crates/components/src/lib.rs - Add vfd module and re-exports