Entropyk/_bmad-output/implementation-artifacts/10-3-brine-source-sink.md

5.8 KiB

Story 10.3: BrineSource et BrineSink avec Support Glycol

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 BrineSource et BrineSink supportent les mélanges eau-glycol avec concentration,
Afin de pouvoir simuler des circuits de caloporteurs avec propriétés thermophysiques correctes.


Contexte

Les caloporteurs liquides (eau, PEG, MEG, saumures) sont utilisés dans:

  • Circuits primaire/secondaire de chillers
  • Systèmes de chauffage urbain
  • Applications basse température avec protection antigel

La concentration en glycol affecte:

  • Viscosité (perte de charge)
  • Chaleur massique (capacité thermique)
  • Point de congélation (protection antigel)

Spécifications Techniques

BrineSource

/// Source pour fluides caloporteurs liquides (eau, PEG, MEG, saumures).
///
/// Impose une température et une pression fixées sur le port de sortie.
/// La concentration en glycol est prise en compte pour les propriétés.
#[derive(Debug, Clone)]
pub struct BrineSource {
    /// Identifiant du fluide (ex: "Water", "MEG", "PEG")
    fluid_id: String,
    /// Concentration en glycol (% massique, 0 = eau pure)
    concentration: Concentration,
    /// Température de set-point [K]
    t_set: Temperature,
    /// Pression de set-point [Pa]
    p_set: Pressure,
    /// Enthalpie calculée depuis T et concentration [J/kg]
    h_set: Enthalpy,
    /// Débit massique optionnel [kg/s]
    mass_flow: Option<MassFlow>,
    /// Débit volumique optionnel [m³/s]
    volume_flow: Option<VolumeFlow>,
    /// Port de sortie connecté
    outlet: ConnectedPort,
}

impl BrineSource {
    /// Crée une source d'eau pure.
    pub fn water(
        temperature: Temperature,
        pressure: Pressure,
        outlet: ConnectedPort,
    ) -> Result<Self, ComponentError>;
    
    /// Crée une source de mélange eau-glycol.
    pub fn glycol_mixture(
        fluid_id: impl Into<String>,
        concentration: Concentration,
        temperature: Temperature,
        pressure: Pressure,
        outlet: ConnectedPort,
    ) -> Result<Self, ComponentError>;
    
    /// Définit le débit massique imposé.
    pub fn set_mass_flow(&mut self, mass_flow: MassFlow);
    
    /// Définit le débit volumique imposé.
    /// Le débit massique est calculé avec la masse volumique du mélange.
    pub fn set_volume_flow(&mut self, volume_flow: VolumeFlow, density: f64);
}

BrineSink

/// Puits pour fluides caloporteurs liquides.
#[derive(Debug, Clone)]
pub struct BrineSink {
    /// Identifiant du fluide
    fluid_id: String,
    /// Concentration en glycol
    concentration: Concentration,
    /// Contre-pression [Pa]
    p_back: Pressure,
    /// Température de retour optionnelle [K]
    t_back: Option<Temperature>,
    /// Port d'entrée connecté
    inlet: ConnectedPort,
}

impl BrineSink {
    /// Crée un puits pour eau pure.
    pub fn water(
        pressure: Pressure,
        inlet: ConnectedPort,
    ) -> Result<Self, ComponentError>;
    
    /// Crée un puits pour mélange eau-glycol.
    pub fn glycol_mixture(
        fluid_id: impl Into<String>,
        concentration: Concentration,
        pressure: Pressure,
        inlet: ConnectedPort,
    ) -> Result<Self, ComponentError>;
}

Calcul des Propriétés

Enthalpie depuis Température et Concentration

/// Calcule l'enthalpie d'un mélange eau-glycol.
///
/// Utilise CoolProp avec la syntaxe de mélange:
/// - Eau pure: "Water"
/// - Mélange MEG: "MEG-MASS%" ou "INCOMP::MEG-MASS%"
fn calculate_enthalpy(
    fluid_id: &str,
    concentration: Concentration,
    temperature: Temperature,
    pressure: Pressure,
) -> Result<Enthalpy, ComponentError> {
    // Pour CoolProp, utiliser:
    // PropsSI("H", "T", T, "P", P, fluid_string)
    // où fluid_string = format!("INCOMP::{}-{}", fluid_id, concentration.to_percent())
}

Fichiers à Créer/Modifier

Fichier Action
crates/components/src/flow_boundary/brine.rs Créer BrineSource, BrineSink
crates/components/src/flow_boundary/mod.rs Ajouter ré-exports

Critères d'Acceptation

  • BrineSource::water() crée une source d'eau pure
  • BrineSource::glycol_mixture() crée une source avec concentration
  • L'enthalpie est calculée correctement depuis T et concentration
  • BrineSink::water() crée un puits pour eau
  • BrineSink::glycol_mixture() crée un puits avec concentration
  • energy_transfers() retourne (Power(0), Power(0))
  • port_enthalpies() retourne [h_set]
  • Validation de la concentration (0-100%)
  • Tests unitaires avec différents pourcentages de glycol

Tests Requis

#[cfg(test)]
mod tests {
    #[test]
    fn test_brine_source_water() { /* ... */ }
    
    #[test]
    fn test_brine_source_meg_30_percent() { /* ... */ }
    
    #[test]
    fn test_brine_source_enthalpy_calculation() { /* ... */ }
    
    #[test]
    fn test_brine_source_volume_flow_conversion() { /* ... */ }
    
    #[test]
    fn test_brine_sink_water() { /* ... */ }
    
    #[test]
    fn test_brine_sink_meg_mixture() { /* ... */ }
}

Notes d'Implémentation

Support CoolProp pour Mélanges

CoolProp supporte les mélanges incompressibles via la syntaxe:

INCOMP::MEG-30  // MEG à 30% massique
INCOMP::PEG-40  // PEG à 40% massique

Vérifier que le backend CoolProp utilisé dans le projet supporte cette syntaxe.


Références