Entropyk/_bmad-output/implementation-artifacts/1-9-air-coils-evaporator-condenser.md

6.0 KiB

Story 1.9: Air Coils (EvaporatorCoil, CondenserCoil)

Status: done

Story

As a HVAC engineer modeling split systems or air-source heat pumps, I want explicit EvaporatorCoil and CondenserCoil components, so that air-side heat exchangers (finned) are clearly distinguished from water-cooled.

Acceptance Criteria

  1. EvaporatorCoil (AC: #1)

    • 4 ports: refrigerant in/out, air in/out (via inner HeatExchanger; ports TODO in framework)
    • UA configurable (geometry/fins deferred)
    • Integrates with Fan for air flow (compatible FluidId::Air on air ports)
    • Calib (f_ua, f_dp) applicable when Story 7.6 is done
  2. CondenserCoil (AC: #2)

    • Same structure as EvaporatorCoil
    • Refrigerant condenses on hot side, air on cold side
    • UA configurable
    • Compatible with Fan
  3. Component Trait (AC: #3)

    • Both implement Component trait
    • n_equations() = 3 (same as Evaporator/Condenser)
    • Exported from heat_exchanger module

Tasks / Subtasks

  • Create EvaporatorCoil (AC: #1)
    • New type in crates/components/src/heat_exchanger/evaporator_coil.rs
    • Wraps Evaporator with name "EvaporatorCoil"
    • 4 ports: hot (air), cold (refrigerant) — refrigerant evaporates on cold side
    • UA from constructor; ua() accessor
  • Create CondenserCoil (AC: #2)
    • New type in condenser_coil.rs wrapping Condenser
    • Name "CondenserCoil"; refrigerant hot, air cold
    • UA configurable
  • Export and lib (AC: #3)
    • Add to heat_exchanger/mod.rs
    • Add to components lib.rs pub use
  • Tests
    • test_evaporator_coil_creation
    • test_condenser_coil_creation
    • test_coil_n_equations
    • test_coil_ua_accessor

Dev Notes

Previous Story Intelligence

From Story 1-5 (Heat Exchanger Framework):

  • HeatExchanger with 4 ports (hot_inlet, hot_outlet, cold_inlet, cold_outlet)
  • LmtdModel, EpsNtuModel
  • Condenser uses LmtdModel; Evaporator uses EpsNtuModel

From Story 1-8 (Fan):

  • Fan uses FluidId::new("Air")
  • Fan has inlet/outlet ports for air circuit
  • Coil air ports connect to Fan in system topology

Port Convention

  • EvaporatorCoil: Cold side = refrigerant (evaporates), Hot side = air (heat source)
  • CondenserCoil: Hot side = refrigerant (condenses), Cold side = air (heat sink)

Architecture

crates/components/src/heat_exchanger/
├── evaporator_coil.rs   # NEW - EvaporatorCoil
├── condenser_coil.rs    # NEW - CondenserCoil
├── evaporator.rs       # Existing
├── condenser.rs        # Existing
└── mod.rs              # Export EvaporatorCoil, CondenserCoil

References

  • Epic 1.9: planning-artifacts/epics.md
  • Story 1-5: heat_exchanger framework
  • Story 1-8: Fan component

Dev Agent Record

Agent Model Used

Cursor/Composer

Debug Log References

N/A

Completion Notes List

  • EvaporatorCoil: wrapper around Evaporator, name "EvaporatorCoil", 4 ports (hot=air, cold=refrigerant)
  • CondenserCoil: wrapper around Condenser, name "CondenserCoil", refrigerant hot, air cold
  • Both implement Component trait, n_equations()=3
  • Exported from heat_exchanger and lib.rs
  • All tests pass (64 heat_exchanger tests)

Code Review (2026-02-15)

Findings: 2 HIGH (fixed), 2 MEDIUM (fixed), 1 LOW

  • HIGH [FIXED]: EvaporatorCoil/CondenserCoil manquaient les setters set_saturation_temp, set_superheat_target (Evaporator) et set_saturation_temp (Condenser) — parité API avec Evaporator/Condenser.
  • HIGH [FIXED]: Tests compute_residuals trop faibles (seulement is_ok()) — ajout de assert!(residuals.iter().all(|r| r.is_finite())).
  • MEDIUM [FIXED]: Pas de test pour jacobian_entries — ajout de test_*_coil_jacobian_entries.
  • MEDIUM [FIXED]: Pas de test des setters — ajout de test_evaporator_coil_setters, test_condenser_coil_set_saturation_temp.
  • LOW: Pas de test d'intégration Coil+Fan (action future).

Code Review (2026-02-15) — Auto-fix

Findings: 2 MEDIUM (fixed), 4 LOW (noted)

  • MEDIUM [FIXED]: Tests jacobian_entries trop faibles — ajout de assert!(jacobian.is_empty()) pour documenter le comportement actuel (HeatExchanger base retourne vide).
  • MEDIUM [FIXED]: EvaporatorCoil/CondenserCoil n'implémentaient pas StateManageable — ajout de l'implémentation (délégation vers inner) pour compatibilité Fan (Off/Bypass). Également ajouté à Evaporator et Condenser.
  • LOW: Clone, notes obsolètes, nommage tests, state dans compute_residuals — non corrigés.

Code Review (AI) — Auto-fix

Findings: 2 HIGH (fixed), 2 MEDIUM (fixed), 1 LOW (fixed)

  • HIGH [FIXED]: Evaporator quality validation logic correctly checks quality >= 1.0 and test added.
  • HIGH [FIXED]: Condenser quality validation logic correctly checks quality <= 0.0 and test added.
  • MEDIUM [FIXED]: Optimized performance by caching fluid validation in EvaporatorCoil and CondenserCoil using AtomicBool.
  • MEDIUM [FIXED]: Added negative tests for non-Air fluids in both Coil components.
  • LOW [FIXED]: Renamed misleading test and added correct test for fully condensed state.

File List

  1. crates/components/src/heat_exchanger/evaporator_coil.rs - EvaporatorCoil
  2. crates/components/src/heat_exchanger/condenser_coil.rs - CondenserCoil
  3. crates/components/src/heat_exchanger/evaporator.rs - StateManageable (code review fix)
  4. crates/components/src/heat_exchanger/condenser.rs - StateManageable (code review fix)
  5. crates/components/src/heat_exchanger/mod.rs - exports
  6. crates/components/src/lib.rs - pub use

Change Log

  • 2026-02-15: Story 1.9 implementation - EvaporatorCoil, CondenserCoil, tests, exports
  • 2026-02-15: Code review auto-fix - StateManageable sur coils/evaporator/condenser, tests jacobian_entries renforcés
  • 2026-02-21: Code review (AI) auto-fix - High/Medium quality validation and performance optimizations for Coils