Entropyk/_bmad-output/implementation-artifacts/10-2-refrigerant-source-sink.md

5.5 KiB

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

/// 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<VaporQuality>,
    /// Débit massique optionnel [kg/s]
    mass_flow: Option<MassFlow>,
    /// 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<String>,
        pressure: Pressure,
        enthalpy: Enthalpy,
        outlet: ConnectedPort,
    ) -> Result<Self, ComponentError>;
    
    /// 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<String>,
        pressure: Pressure,
        vapor_quality: VaporQuality,
        outlet: ConnectedPort,
    ) -> Result<Self, ComponentError>;
    
    /// Définit le débit massique imposé.
    pub fn set_mass_flow(&mut self, mass_flow: MassFlow);
}

RefrigerantSink

/// 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<Enthalpy>,
    /// 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<String>,
        pressure: Pressure,
        inlet: ConnectedPort,
    ) -> Result<Self, ComponentError>;
    
    /// Définit une enthalpie de retour fixe.
    pub fn set_return_enthalpy(&mut self, enthalpy: Enthalpy);
}

Implémentation du Trait Component

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<Vec<Enthalpy>, ComponentError> {
        Ok(vec![self.h_set])
    }
    
    fn port_mass_flows(&self, _state: &SystemState) -> Result<Vec<MassFlow>, 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

#[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