# Story 10.2: RefrigerantSource et RefrigerantSink **Epic:** 10 - Enhanced Boundary Conditions **Priorité:** P0-CRITIQUE **Estimation:** 3h **Statut:** backlog **Dépendances:** Story 10-1 (Nouveaux types physiques) --- ## Story > En tant que moteur de simulation thermodynamique, > Je veux que `RefrigerantSource` et `RefrigerantSink` implémentent le trait `Component`, > Afin de pouvoir définir des conditions aux limites pour les fluides frigorigènes avec titre. --- ## Contexte Les fluides frigorigènes (R410A, R134a, CO2, etc.) nécessitent des conditions aux limites spécifiques: - Possibilité de spécifier le **titre** (vapor quality) au lieu de l'enthalpie - Validation que le fluide est bien un réfrigérant - Support des propriétés thermodynamiques via CoolProp --- ## Spécifications Techniques ### RefrigerantSource ```rust /// Source pour fluides frigorigènes compressibles. /// /// Impose une pression et une enthalpie (ou titre) fixées sur le port de sortie. #[derive(Debug, Clone)] pub struct RefrigerantSource { /// Identifiant du fluide frigorigène (ex: "R410A", "R134a", "CO2") fluid_id: String, /// Pression de set-point [Pa] p_set: Pressure, /// Enthalpie de set-point [J/kg] h_set: Enthalpy, /// Titre optionnel (vapor quality, 0-1) vapor_quality: Option, /// Débit massique optionnel [kg/s] mass_flow: Option, /// Port de sortie connecté outlet: ConnectedPort, } impl RefrigerantSource { /// Crée une source réfrigérant avec pression et enthalpie fixées. pub fn new( fluid_id: impl Into, pressure: Pressure, enthalpy: Enthalpy, outlet: ConnectedPort, ) -> Result; /// Crée une source réfrigérant avec pression et titre fixés. /// L'enthalpie est calculée automatiquement via CoolProp. pub fn with_vapor_quality( fluid_id: impl Into, pressure: Pressure, vapor_quality: VaporQuality, outlet: ConnectedPort, ) -> Result; /// Définit le débit massique imposé. pub fn set_mass_flow(&mut self, mass_flow: MassFlow); } ``` ### RefrigerantSink ```rust /// Puits pour fluides frigorigènes compressibles. /// /// Impose une contre-pression fixe sur le port d'entrée. #[derive(Debug, Clone)] pub struct RefrigerantSink { /// Identifiant du fluide frigorigène fluid_id: String, /// Contre-pression [Pa] p_back: Pressure, /// Enthalpie de retour optionnelle [J/kg] h_back: Option, /// Port d'entrée connecté inlet: ConnectedPort, } impl RefrigerantSink { /// Crée un puits réfrigérant avec contre-pression fixe. pub fn new( fluid_id: impl Into, pressure: Pressure, inlet: ConnectedPort, ) -> Result; /// Définit une enthalpie de retour fixe. pub fn set_return_enthalpy(&mut self, enthalpy: Enthalpy); } ``` --- ## Implémentation du Trait Component ```rust impl Component for RefrigerantSource { fn n_equations(&self) -> usize { 2 } fn compute_residuals(&self, _state: &SystemState, residuals: &mut ResidualVector) -> Result<(), ComponentError> { residuals[0] = self.outlet.pressure().to_pascals() - self.p_set.to_pascals(); residuals[1] = self.outlet.enthalpy().to_joules_per_kg() - self.h_set.to_joules_per_kg(); Ok(()) } fn energy_transfers(&self, _state: &SystemState) -> Option<(Power, Power)> { Some((Power::from_watts(0.0), Power::from_watts(0.0))) } fn port_enthalpies(&self, _state: &SystemState) -> Result, ComponentError> { Ok(vec![self.h_set]) } fn port_mass_flows(&self, _state: &SystemState) -> Result, ComponentError> { match self.mass_flow { Some(mdot) => Ok(vec![MassFlow::from_kg_per_s(-mdot.to_kg_per_s())]), None => Ok(vec![]), } } } ``` --- ## Fichiers à Créer/Modifier | Fichier | Action | |---------|--------| | `crates/components/src/flow_boundary/mod.rs` | Créer module avec ré-exports | | `crates/components/src/flow_boundary/refrigerant.rs` | Créer `RefrigerantSource`, `RefrigerantSink` | | `crates/components/src/lib.rs` | Exporter les nouveaux types | --- ## Critères d'Acceptation - [ ] `RefrigerantSource::new()` crée une source avec P et h fixées - [ ] `RefrigerantSource::with_vapor_quality()` calcule l'enthalpie depuis le titre - [ ] `RefrigerantSink::new()` crée un puits avec contre-pression - [ ] `energy_transfers()` retourne `(Power(0), Power(0))` - [ ] `port_enthalpies()` retourne `[h_set]` - [ ] `port_mass_flows()` retourne le débit si spécifié - [ ] Validation que le fluide est un réfrigérant valide - [ ] Tests unitaires complets --- ## Tests Requis ```rust #[cfg(test)] mod tests { #[test] fn test_refrigerant_source_new() { /* ... */ } #[test] fn test_refrigerant_source_with_vapor_quality() { /* ... */ } #[test] fn test_refrigerant_source_energy_transfers_zero() { /* ... */ } #[test] fn test_refrigerant_source_port_enthalpies() { /* ... */ } #[test] fn test_refrigerant_sink_new() { /* ... */ } #[test] fn test_refrigerant_sink_with_return_enthalpy() { /* ... */ } } ``` --- ## Références - [Architecture Document](../../plans/boundary-condition-refactoring-architecture.md) - [Story 10-1: Nouveaux types physiques](./10-1-new-physical-types.md)