6.5 KiB
Story 10.4: AirSource et AirSink avec Propriétés Psychrométriques
Epic: 10 - Enhanced Boundary Conditions
Priorité: P1-HIGH
Estimation: 4h
Statut: backlog
Dépendances: Story 10-1 (Nouveaux types physiques)
Story
En tant que moteur de simulation thermodynamique,
Je veux queAirSourceetAirSinksupportent les propriétés psychrométriques,
Afin de pouvoir simuler les côtés air des échangeurs de chaleur (évaporateurs, condenseurs).
Contexte
Les composants côté air (évaporateur air/air, condenseur air/réfrigérant) nécessitent des conditions aux limites avec:
- Température sèche (dry bulb temperature)
- Humidité relative ou température bulbe humide
- Débit massique d'air
Ces propriétés sont essentielles pour:
- Calcul des échanges thermiques et massiques (condensation sur évaporateur)
- Dimensionnement des batteries froides/chaudes
- Simulation des pompes à chaleur air/air et air/eau
Spécifications Techniques
AirSource
/// Source pour air humide (côté air des échangeurs).
///
/// Impose les conditions de l'air entrant avec propriétés psychrométriques.
#[derive(Debug, Clone)]
pub struct AirSource {
/// Température sèche [K]
t_dry: Temperature,
/// Humidité relative [%]
rh: RelativeHumidity,
/// Température bulbe humide optionnelle [K]
t_wet_bulb: Option<Temperature>,
/// Pression atmosphérique [Pa]
pressure: Pressure,
/// Débit massique d'air sec optionnel [kg/s]
mass_flow: Option<MassFlow>,
/// Port de sortie connecté
outlet: ConnectedPort,
}
impl AirSource {
/// Crée une source d'air avec température sèche et humidité relative.
pub fn from_dry_bulb_rh(
temperature_dry: Temperature,
relative_humidity: RelativeHumidity,
pressure: Pressure,
outlet: ConnectedPort,
) -> Result<Self, ComponentError>;
/// Crée une source d'air avec températures sèche et bulbe humide.
/// L'humidité relative est calculée automatiquement.
pub fn from_dry_and_wet_bulb(
temperature_dry: Temperature,
temperature_wet_bulb: Temperature,
pressure: Pressure,
outlet: ConnectedPort,
) -> Result<Self, ComponentError>;
/// Définit le débit massique d'air sec.
pub fn set_mass_flow(&mut self, mass_flow: MassFlow);
/// Retourne l'enthalpie spécifique de l'air humide [J/kg_air_sec].
pub fn specific_enthalpy(&self) -> Result<Enthalpy, ComponentError>;
/// Retourne le rapport d'humidité (kg_vapeur / kg_air_sec).
pub fn humidity_ratio(&self) -> Result<f64, ComponentError>;
}
AirSink
/// Puits pour air humide.
#[derive(Debug, Clone)]
pub struct AirSink {
/// Pression atmosphérique [Pa]
pressure: Pressure,
/// Température de retour optionnelle [K]
t_back: Option<Temperature>,
/// Port d'entrée connecté
inlet: ConnectedPort,
}
impl AirSink {
/// Crée un puits d'air à pression atmosphérique.
pub fn new(pressure: Pressure, inlet: ConnectedPort) -> Result<Self, ComponentError>;
/// Définit une température de retour fixe.
pub fn set_return_temperature(&mut self, temperature: Temperature);
}
Calculs Psychrométriques
Formules Utilisées
/// Pression de saturation de vapeur d'eau (formule de Magnus-Tetens)
fn saturation_vapor_pressure(t: Temperature) -> Pressure {
// P_sat = 610.78 * exp(17.27 * T_celsius / (T_celsius + 237.3))
let t_c = t.to_celsius();
Pressure::from_pascals(610.78 * (17.27 * t_c / (t_c + 237.3)).exp())
}
/// Rapport d'humidité depuis humidité relative
fn humidity_ratio_from_rh(
rh: RelativeHumidity,
t_dry: Temperature,
p_atm: Pressure,
) -> f64 {
// W = 0.622 * (P_v / (P_atm - P_v))
// où P_v = RH * P_sat
let p_sat = saturation_vapor_pressure(t_dry);
let p_v = p_sat * rh.to_fraction();
0.622 * p_v.to_pascals() / (p_atm.to_pascals() - p_v.to_pascals())
}
/// Enthalpie spécifique de l'air humide
fn specific_enthalpy(t_dry: Temperature, w: f64) -> Enthalpy {
// h = 1.006 * T_celsius + W * (2501 + 1.86 * T_celsius) [kJ/kg]
let t_c = t_dry.to_celsius();
Enthalpy::from_joules_per_kg((1.006 * t_c + w * (2501.0 + 1.86 * t_c)) * 1000.0)
}
Fichiers à Créer/Modifier
| Fichier | Action |
|---|---|
crates/components/src/flow_boundary/air.rs |
Créer AirSource, AirSink |
crates/components/src/flow_boundary/mod.rs |
Ajouter ré-exports |
Critères d'Acceptation
AirSource::from_dry_bulb_rh()crée une source avec T sèche et HRAirSource::from_dry_and_wet_bulb()calcule HR depuis T bulbe humidespecific_enthalpy()retourne l'enthalpie de l'air humidehumidity_ratio()retourne le rapport d'humiditéAirSink::new()crée un puits à pression atmosphériqueenergy_transfers()retourne(Power(0), Power(0))- Validation de l'humidité relative (0-100%)
- Tests unitaires avec valeurs de référence ASHRAE
Tests Requis
#[cfg(test)]
mod tests {
#[test]
fn test_air_source_from_dry_bulb_rh() { /* ... */ }
#[test]
fn test_air_source_from_wet_bulb() { /* ... */ }
#[test]
fn test_saturation_vapor_pressure() { /* ... */ }
#[test]
fn test_humidity_ratio_calculation() { /* ... */ }
#[test]
fn test_specific_enthalpy_calculation() { /* ... */ }
#[test]
fn test_air_source_psychrometric_consistency() {
// Vérifier que les calculs sont cohérents avec les tables ASHRAE
}
}
Notes d'Implémentation
Alternative: Utiliser CoolProp
CoolProp supporte l'air humide via:
// Air humide avec rapport d'humidité W
let fluid = format!("Air-W-{}", w);
PropsSI("H", "T", T, "P", P, &fluid)
Cependant, les formules analytiques (Magnus-Tetens) sont plus rapides et suffisantes pour la plupart des applications.
Performance
Les calculs psychrométriques doivent être optimisés car ils sont appelés fréquemment dans les boucles de résolution. Éviter les allocations et utiliser des formules approchées si nécessaire.