# Story 11.2: Drum - Ballon de Recirculation **Epic:** 11 - Advanced HVAC Components **Priorité:** P0-CRITIQUE **Estimation:** 6h **Statut:** backlog **Dépendances:** Story 11.1 (Node) --- ## Story > En tant qu'ingénieur chiller, > Je veux un composant Drum pour la recirculation d'évaporateur, > Afin de simuler des cycles à évaporateur flooded. --- ## Contexte Le ballon de recirculation (Drum) est un composant essentiel des évaporateurs flooded. Il reçoit: 1. Le flux d'alimentation (feed) depuis l'économiseur 2. Le retour de l'évaporateur (mélange enrichi en vapeur) Et sépare en: 1. Liquide saturé (x=0) vers la pompe de recirculation 2. Vapeur saturée (x=1) vers le compresseur --- ## Équations Mathématiques ``` Ports: in1: Feed (depuis économiseur) in2: Retour évaporateur (diphasique) out1: Liquide saturé (x=0) out2: Vapeur saturée (x=1) Équations (8): 1. Mélange entrées: ṁ_total = ṁ_in1 + ṁ_in2 h_mixed = (ṁ_in1·h_in1 + ṁ_in2·h_in2) / ṁ_total 2. Bilan masse: ṁ_out1 + ṁ_out2 = ṁ_total 3. Bilan énergie: ṁ_out1·h_out1 + ṁ_out2·h_out2 = ṁ_total·h_mixed 4. Pression out1: P_out1 - P_in1 = 0 5. Pression out2: P_out2 - P_in1 = 0 6. Liquide saturé: h_out1 - h_sat(P, x=0) = 0 7. Vapeur saturée: h_out2 - h_sat(P, x=1) = 0 8. Continuité fluide (implicite via FluidId) ``` --- ## Fichiers à Créer/Modifier | Fichier | Action | |---------|--------| | `crates/components/src/drum.rs` | Créer | | `crates/components/src/lib.rs` | Ajouter `mod drum; pub use drum::*` | --- ## Implémentation ```rust // crates/components/src/drum.rs use entropyk_core::{Power, Calib}; use entropyk_fluids::{FluidBackend, FluidId}; use crate::{Component, ComponentError, ConnectedPort, JacobianBuilder, ResidualVector, SystemState}; use std::sync::Arc; /// Drum - Ballon de recirculation pour évaporateurs #[derive(Debug)] pub struct Drum { fluid_id: String, feed_inlet: ConnectedPort, evaporator_return: ConnectedPort, liquid_outlet: ConnectedPort, vapor_outlet: ConnectedPort, fluid_backend: Arc, calib: Calib, } impl Drum { pub fn new( fluid: impl Into, feed_inlet: ConnectedPort, evaporator_return: ConnectedPort, liquid_outlet: ConnectedPort, vapor_outlet: ConnectedPort, backend: Arc, ) -> Result { Ok(Self { fluid_id: fluid.into(), feed_inlet, evaporator_return, liquid_outlet, vapor_outlet, fluid_backend: backend, calib: Calib::default(), }) } /// Ratio de recirculation (m_liquid / m_feed) pub fn recirculation_ratio(&self, state: &SystemState) -> f64 { let m_liquid = self.liquid_outlet.mass_flow().to_kg_per_s(); let m_feed = self.feed_inlet.mass_flow().to_kg_per_s(); if m_feed > 0.0 { m_liquid / m_feed } else { 0.0 } } } impl Component for Drum { fn n_equations(&self) -> usize { 8 } fn compute_residuals( &self, state: &SystemState, residuals: &mut ResidualVector, ) -> Result<(), ComponentError> { // ... implémentation complète Ok(()) } fn energy_transfers(&self, _state: &SystemState) -> Option<(Power, Power)> { Some((Power::from_watts(0.0), Power::from_watts(0.0))) } } ``` --- ## Critères d'Acceptation - [ ] `Drum::n_equations()` retourne `8` - [ ] Liquide outlet est saturé (x=0) - [ ] Vapeur outlet est saturée (x=1) - [ ] Bilan masse satisfait - [ ] Bilan énergie satisfait - [ ] Pressions égales sur tous les ports - [ ] `recirculation_ratio()` retourne m_liq/m_feed - [ ] Validation: fluide pur requis --- ## Tests Requis ```rust #[test] fn test_drum_equations_count() { assert_eq!(drum.n_equations(), 8); } #[test] fn test_drum_saturated_outlets() { // Vérifier h_liq = h_sat(x=0), h_vap = h_sat(x=1) } #[test] fn test_drum_mass_balance() { // m_liq + m_vap = m_feed + m_return } #[test] fn test_drum_recirculation_ratio() { // ratio = m_liq / m_feed } ``` --- ## Références - [Epic 11 Technical Specifications](../planning-artifacts/epic-11-technical-specifications.md) - TESPy `tespy/components/nodes/drum.py`