251 lines
8.4 KiB
Python
251 lines
8.4 KiB
Python
"""
|
|
Service pour les calculs thermodynamiques
|
|
"""
|
|
|
|
from typing import Dict, Optional, Any
|
|
from app.core.refrigerant_loader import get_refrigerant
|
|
|
|
|
|
class ThermodynamicsService:
|
|
"""Service pour effectuer les calculs thermodynamiques"""
|
|
|
|
def calculate_from_px(
|
|
self,
|
|
refrigerant: str,
|
|
pressure: float,
|
|
quality: float
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Calcule les proprietes thermodynamiques a partir de P et x
|
|
|
|
Args:
|
|
refrigerant: Nom du refrigerant (ex: "R134a")
|
|
pressure: Pression en Pa
|
|
quality: Qualite (0-1)
|
|
|
|
Returns:
|
|
Dictionnaire avec toutes les proprietes
|
|
"""
|
|
lib = get_refrigerant(refrigerant)
|
|
|
|
# Calculs des proprietes principales
|
|
temperature = lib.T_px(pressure, quality)
|
|
enthalpy = lib.h_px(pressure, quality)
|
|
entropy = lib.s_px(pressure, quality)
|
|
density = lib.rho_px(pressure, quality)
|
|
temp_sat = lib.Ts_px(pressure, quality)
|
|
|
|
# Proprietes de saturation
|
|
enthalpy_liquid = lib.hsl_px(pressure, 0)
|
|
enthalpy_vapor = lib.hsv_px(pressure, 1)
|
|
density_liquid = lib.rhosl_px(pressure, 0)
|
|
density_vapor = lib.rhosv_px(pressure, 1)
|
|
|
|
return {
|
|
"refrigerant": refrigerant,
|
|
"inputs": {
|
|
"pressure": pressure,
|
|
"pressure_bar": pressure / 1e5,
|
|
"quality": quality
|
|
},
|
|
"properties": {
|
|
"temperature": temperature,
|
|
"temperature_celsius": temperature - 273.15,
|
|
"enthalpy": enthalpy,
|
|
"enthalpy_kj_kg": enthalpy / 1000,
|
|
"entropy": entropy,
|
|
"entropy_kj_kgK": entropy / 1000,
|
|
"density": density,
|
|
"specific_volume": 1 / density if density > 0 else None,
|
|
"quality": quality
|
|
},
|
|
"saturation": {
|
|
"temperature": temp_sat,
|
|
"temperature_celsius": temp_sat - 273.15,
|
|
"enthalpy_liquid": enthalpy_liquid,
|
|
"enthalpy_liquid_kj_kg": enthalpy_liquid / 1000,
|
|
"enthalpy_vapor": enthalpy_vapor,
|
|
"enthalpy_vapor_kj_kg": enthalpy_vapor / 1000,
|
|
"density_liquid": density_liquid,
|
|
"density_vapor": density_vapor,
|
|
"latent_heat": enthalpy_vapor - enthalpy_liquid,
|
|
"latent_heat_kj_kg": (enthalpy_vapor - enthalpy_liquid) / 1000
|
|
}
|
|
}
|
|
|
|
def calculate_from_pT(
|
|
self,
|
|
refrigerant: str,
|
|
pressure: float,
|
|
temperature: float
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Calcule les proprietes a partir de P et T
|
|
|
|
Args:
|
|
refrigerant: Nom du refrigerant
|
|
pressure: Pression en Pa
|
|
temperature: Temperature en K
|
|
|
|
Returns:
|
|
Dictionnaire avec les proprietes
|
|
"""
|
|
lib = get_refrigerant(refrigerant)
|
|
|
|
enthalpy = lib.h_pT(pressure, temperature)
|
|
|
|
# Determiner la qualite approximativement
|
|
temp_sat = lib.Ts_px(pressure, 0.5)
|
|
|
|
# Si proche de la saturation, calculer la qualite
|
|
quality = None
|
|
if abs(temperature - temp_sat) < 0.1:
|
|
# En zone diphasique
|
|
h_liquid = lib.hsl_px(pressure, 0)
|
|
h_vapor = lib.hsv_px(pressure, 1)
|
|
if h_vapor > h_liquid:
|
|
quality = (enthalpy - h_liquid) / (h_vapor - h_liquid)
|
|
quality = max(0, min(1, quality))
|
|
|
|
# Si qualite determinee, utiliser px pour avoir toutes les proprietes
|
|
if quality is not None:
|
|
return self.calculate_from_px(refrigerant, pressure, quality)
|
|
|
|
# Sinon, calculer les proprietes de base (vapeur surchauffee ou liquide sous-refroidi)
|
|
# Approximation: utiliser x=1 pour vapeur ou x=0 pour liquide
|
|
if temperature > temp_sat:
|
|
# Vapeur surchauffee, utiliser x=1 comme approximation
|
|
quality_approx = 1.0
|
|
else:
|
|
# Liquide sous-refroidi, utiliser x=0 comme approximation
|
|
quality_approx = 0.0
|
|
|
|
result = self.calculate_from_px(refrigerant, pressure, quality_approx)
|
|
result["properties"]["temperature"] = temperature
|
|
result["properties"]["temperature_celsius"] = temperature - 273.15
|
|
result["properties"]["enthalpy"] = enthalpy
|
|
result["properties"]["enthalpy_kj_kg"] = enthalpy / 1000
|
|
result["properties"]["quality"] = quality
|
|
result["inputs"]["temperature"] = temperature
|
|
result["inputs"]["temperature_celsius"] = temperature - 273.15
|
|
result["note"] = "Calcul a partir de P et T - proprietes approximatives hors saturation"
|
|
|
|
return result
|
|
|
|
def calculate_from_ph(
|
|
self,
|
|
refrigerant: str,
|
|
pressure: float,
|
|
enthalpy: float
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Calcule les proprietes a partir de P et h
|
|
|
|
Args:
|
|
refrigerant: Nom du refrigerant
|
|
pressure: Pression en Pa
|
|
enthalpy: Enthalpie en J/kg
|
|
|
|
Returns:
|
|
Dictionnaire avec les proprietes
|
|
"""
|
|
lib = get_refrigerant(refrigerant)
|
|
|
|
# Calculer la qualite
|
|
quality = lib.x_ph(pressure, enthalpy)
|
|
|
|
# Utiliser la qualite pour calculer le reste
|
|
return self.calculate_from_px(refrigerant, pressure, quality)
|
|
|
|
def calculate_from_Tx(
|
|
self,
|
|
refrigerant: str,
|
|
temperature: float,
|
|
quality: float
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Calcule les proprietes a partir de T et x
|
|
|
|
Args:
|
|
refrigerant: Nom du refrigerant
|
|
temperature: Temperature en K
|
|
quality: Qualite (0-1)
|
|
|
|
Returns:
|
|
Dictionnaire avec les proprietes
|
|
"""
|
|
lib = get_refrigerant(refrigerant)
|
|
|
|
# Calculer la pression
|
|
pressure = lib.p_Tx(temperature, quality)
|
|
|
|
# Utiliser la pression pour calculer le reste
|
|
return self.calculate_from_px(refrigerant, pressure, quality)
|
|
|
|
def get_saturation_properties(
|
|
self,
|
|
refrigerant: str,
|
|
pressure: float
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Obtient les proprietes de saturation a une pression donnee
|
|
|
|
Args:
|
|
refrigerant: Nom du refrigerant
|
|
pressure: Pression en Pa
|
|
|
|
Returns:
|
|
Proprietes de saturation (liquide et vapeur)
|
|
"""
|
|
lib = get_refrigerant(refrigerant)
|
|
|
|
# Temperature de saturation
|
|
temp_sat = lib.Ts_px(pressure, 0.5)
|
|
|
|
# Proprietes liquide (x=0)
|
|
h_liquid = lib.hsl_px(pressure, 0)
|
|
rho_liquid = lib.rhosl_px(pressure, 0)
|
|
s_liquid = lib.s_px(pressure, 0)
|
|
|
|
# Proprietes vapeur (x=1)
|
|
h_vapor = lib.hsv_px(pressure, 1)
|
|
rho_vapor = lib.rhosv_px(pressure, 1)
|
|
s_vapor = lib.s_px(pressure, 1)
|
|
|
|
return {
|
|
"refrigerant": refrigerant,
|
|
"pressure": pressure,
|
|
"pressure_bar": pressure / 1e5,
|
|
"temperature_saturation": temp_sat,
|
|
"temperature_saturation_celsius": temp_sat - 273.15,
|
|
"liquid": {
|
|
"enthalpy": h_liquid,
|
|
"enthalpy_kj_kg": h_liquid / 1000,
|
|
"density": rho_liquid,
|
|
"specific_volume": 1 / rho_liquid if rho_liquid > 0 else None,
|
|
"entropy": s_liquid,
|
|
"entropy_kj_kgK": s_liquid / 1000
|
|
},
|
|
"vapor": {
|
|
"enthalpy": h_vapor,
|
|
"enthalpy_kj_kg": h_vapor / 1000,
|
|
"density": rho_vapor,
|
|
"specific_volume": 1 / rho_vapor if rho_vapor > 0 else None,
|
|
"entropy": s_vapor,
|
|
"entropy_kj_kgK": s_vapor / 1000
|
|
},
|
|
"latent_heat": h_vapor - h_liquid,
|
|
"latent_heat_kj_kg": (h_vapor - h_liquid) / 1000
|
|
}
|
|
|
|
|
|
# Instance globale du service
|
|
_service: Optional[ThermodynamicsService] = None
|
|
|
|
|
|
def get_thermodynamics_service() -> ThermodynamicsService:
|
|
"""Obtient l'instance du service thermodynamique"""
|
|
global _service
|
|
if _service is None:
|
|
_service = ThermodynamicsService()
|
|
return _service |