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

278 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
- [x] Pump with polynomial performance curves (head, efficiency)
- [x] Affinity laws for variable speed operation (Q ∝ N, H ∝ N², P ∝ N³)
- [x] Component trait implementation with compute_residuals, jacobian_entries
- [x] StateManageable trait (ON/OFF/BYPASS states)
- [x] Hydraulic power calculation: P = Q × ΔP / η
2. **Pipe Component** (AC: #2) ✅ IMPLEMENTED
- [x] Darcy-Weisbach pressure drop calculation
- [x] Haaland friction factor (laminar + turbulent)
- [x] `for_incompressible()` and `for_refrigerant()` constructors
- [x] PipeGeometry with roughness constants
- [x] Calib (f_dp) for calibration
- [x] Component and StateManageable traits
3. **Fan Component** (AC: #3) ✅ IMPLEMENTED
- [x] Fan with polynomial performance curves (static pressure, efficiency)
- [x] Affinity laws for variable speed operation
- [x] Static pressure and total pressure (with velocity pressure)
- [x] 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
- [x] Pump unit tests (20+ tests in pump.rs)
- [x] Pipe unit tests (25+ tests in pipe.rs)
- [x] Fan unit tests (15+ tests in fan.rs)
- [ ] VFD integration tests
- [ ] Full circuit simulation with Pump + Pipe + VFD
## Tasks / Subtasks
- [x] Pump Implementation (AC: #1)
- [x] Create PumpCurves struct with quadratic/cubic curves
- [x] Implement AffinityLaws scaling in pump.rs
- [x] Add Component trait for Pump<Connected>
- [x] Add StateManageable trait for Pump<Connected>
- [x] Implement compute_residuals with ON/OFF/BYPASS handling
- [x] Write 20+ unit tests
- [x] Pipe Implementation (AC: #2)
- [x] Create PipeGeometry struct with roughness constants
- [x] Implement Darcy-Weisbach equation
- [x] Implement Haaland friction factor
- [x] Add `for_incompressible()` and `for_refrigerant()` constructors
- [x] Add Component and StateManageable traits
- [x] Write 25+ unit tests
- [x] Fan Implementation (AC: #3)
- [x] Create FanCurves struct with polynomial curves
- [x] Implement static_pressure_rise and total_pressure_rise
- [x] Add Component and StateManageable traits
- [x] Write 15+ unit tests
- [ ] VFD Abstraction (AC: #4)
- [ ] Create Vfd<T> 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)**
```rust
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**
```rust
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:
```rust
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):**
```rust
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):**
```rust
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):**
```rust
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