278 lines
8.8 KiB
Markdown
278 lines
8.8 KiB
Markdown
# 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 + ½ρv²
|
||
```
|
||
|
||
### 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
|