Section 5 : Usages finaux de l’énergie

5.1. Module AHU - Centrales de Traitement d’Air (CTA)

Le module AHU permet de modéliser les centrales de traitement d’air avec leurs différents composants.

5.1.1. FreshAir - Mélange d’air neuf et air recyclé

Schéma mélange air neuf
from energysystemmodels.AHU import FreshAir

# Configuration du mélange
fresh_air = FreshAir(
    debit_air_neuf_m3_h=3000,
    debit_air_recycle_m3_h=7000,
    temperature_ext_C=5,
    humidite_ext_pct=80,
    temperature_reprise_C=22,
    humidite_reprise_pct=45
)

# Calculer l'état du mélange
etat_melange = fresh_air.calculer_etat_melange()

print(f"Température du mélange : {etat_melange['temperature_C']:.1f}°C")
print(f"Humidité relative : {etat_melange['humidite_relative_pct']:.1f}%")
print(f"Humidité absolue : {etat_melange['humidite_absolue_g_kg']:.2f} g/kg")
print(f"Enthalpie : {etat_melange['enthalpie_kJ_kg']:.2f} kJ/kg")

Diagramme psychrométrique

Diagramme psychrométrique
from energysystemmodels.AHU import FreshAir
import matplotlib.pyplot as plt

fresh_air = FreshAir(
    debit_air_neuf_m3_h=3000,
    debit_air_recycle_m3_h=7000,
    temperature_ext_C=5,
    humidite_ext_pct=80,
    temperature_reprise_C=22,
    humidite_reprise_pct=45
)

# Tracer le diagramme psychrométrique
fig = fresh_air.plot_psychrometric_chart()
plt.show()

5.1.2. HeatingCoil - Batterie chaude

from energysystemmodels.AHU import HeatingCoil

# Batterie chaude à eau
batterie = HeatingCoil(
    type_batterie="eau_chaude",
    puissance_nominale_kW=50,
    temperature_entree_eau_C=80,
    temperature_sortie_eau_C=60,
    efficacite=0.85
)

# Air à chauffer
debit_air = 10000  # m³/h
T_air_entree = 10  # °C

# Calculer le chauffage
resultats = batterie.calculer_chauffage(
    debit_air_m3_h=debit_air,
    temperature_air_entree_C=T_air_entree
)

print(f"Puissance de chauffage : {resultats['puissance_kW']:.2f} kW")
print(f"Température air sortie : {resultats['temperature_air_sortie_C']:.1f}°C")
print(f"Débit d'eau : {resultats['debit_eau_m3_h']:.2f} m³/h")

Exemple : Dimensionnement d’une batterie chaude

from energysystemmodels.AHU import HeatingCoil

# Conditions de dimensionnement
debit_air = 15000       # m³/h
T_air_entree = 5        # °C
T_air_sortie_voulue = 18  # °C
T_eau_aller = 80        # °C
T_eau_retour = 60       # °C

# Calculer la puissance nécessaire
rho_air = 1.2  # kg/m³
cp_air = 1.005  # kJ/kg.K

debit_massique_air = debit_air / 3600 * rho_air  # kg/s
puissance_requise = debit_massique_air * cp_air * (T_air_sortie_voulue - T_air_entree)

print(f"Puissance requise : {puissance_requise:.2f} kW")

# Créer la batterie avec cette puissance
batterie = HeatingCoil(
    type_batterie="eau_chaude",
    puissance_nominale_kW=puissance_requise,
    temperature_entree_eau_C=T_eau_aller,
    temperature_sortie_eau_C=T_eau_retour,
    efficacite=0.90
)

# Vérifier les performances
resultats = batterie.calculer_chauffage(debit_air, T_air_entree)
print(f"Température air sortie obtenue : {resultats['temperature_air_sortie_C']:.1f}°C")

5.1.3. Humidifier - Humidificateur

from energysystemmodels.AHU import Humidifier

# Humidificateur à vapeur
humidificateur = Humidifier(
    type_humidificateur="vapeur",
    capacite_kg_h=30,
    efficacite=0.95
)

# Air à humidifier
debit_air = 10000  # m³/h
T_air = 20         # °C
HR_entree = 30     # %
HR_souhaitee = 50  # %

# Calculer l'humidification
resultats = humidificateur.calculer_humidification(
    debit_air_m3_h=debit_air,
    temperature_C=T_air,
    humidite_relative_entree_pct=HR_entree,
    humidite_relative_sortie_pct=HR_souhaitee
)

print(f"Débit de vapeur nécessaire : {resultats['debit_vapeur_kg_h']:.2f} kg/h")
print(f"Puissance consommée : {resultats['puissance_kW']:.2f} kW")
print(f"Humidité absolue entrée : {resultats['humidite_abs_entree_g_kg']:.2f} g/kg")
print(f"Humidité absolue sortie : {resultats['humidite_abs_sortie_g_kg']:.2f} g/kg")

Exemple complet : CTA complète

from energysystemmodels.AHU import FreshAir, HeatingCoil, CoolingCoil, Humidifier, Fan

# Conditions extérieures hiver
T_ext = 5      # °C
HR_ext = 80    # %

# Conditions intérieures
T_reprise = 22  # °C
HR_reprise = 45  # %

# Consigne soufflage
T_soufflage = 18  # °C
HR_soufflage = 50  # %

# Débits
debit_air_neuf = 3000    # m³/h
debit_air_recycle = 7000  # m³/h
debit_total = debit_air_neuf + debit_air_recycle

print("=== SIMULATION CTA HIVER ===\n")

# 1. Mélange air neuf / air recyclé
fresh_air = FreshAir(
    debit_air_neuf_m3_h=debit_air_neuf,
    debit_air_recycle_m3_h=debit_air_recycle,
    temperature_ext_C=T_ext,
    humidite_ext_pct=HR_ext,
    temperature_reprise_C=T_reprise,
    humidite_reprise_pct=HR_reprise
)

etat_melange = fresh_air.calculer_etat_melange()
print(f"1. Après mélange :")
print(f"   T = {etat_melange['temperature_C']:.1f}°C")
print(f"   HR = {etat_melange['humidite_relative_pct']:.1f}%\n")

# 2. Chauffage
batterie_chaude = HeatingCoil(
    type_batterie="eau_chaude",
    puissance_nominale_kW=80,
    temperature_entree_eau_C=80,
    temperature_sortie_eau_C=60,
    efficacite=0.90
)

resultats_chauffage = batterie_chaude.calculer_chauffage(
    debit_air_m3_h=debit_total,
    temperature_air_entree_C=etat_melange['temperature_C']
)

print(f"2. Après batterie chaude :")
print(f"   T = {resultats_chauffage['temperature_air_sortie_C']:.1f}°C")
print(f"   Puissance = {resultats_chauffage['puissance_kW']:.2f} kW\n")

# 3. Humidification
humidificateur = Humidifier(
    type_humidificateur="vapeur",
    capacite_kg_h=50,
    efficacite=0.95
)

# Calculer HR après chauffage (approximation)
HR_apres_chauffage = etat_melange['humidite_relative_pct'] * \
                     etat_melange['temperature_C'] / \
                     resultats_chauffage['temperature_air_sortie_C']

resultats_humidif = humidificateur.calculer_humidification(
    debit_air_m3_h=debit_total,
    temperature_C=resultats_chauffage['temperature_air_sortie_C'],
    humidite_relative_entree_pct=HR_apres_chauffage,
    humidite_relative_sortie_pct=HR_soufflage
)

print(f"3. Après humidification :")
print(f"   HR = {HR_soufflage}%")
print(f"   Vapeur = {resultats_humidif['debit_vapeur_kg_h']:.2f} kg/h")
print(f"   Puissance = {resultats_humidif['puissance_kW']:.2f} kW\n")

# 4. Ventilateur
ventilateur = Fan(
    type_ventilateur="centrifuge",
    debit_nominal_m3_h=debit_total,
    pression_statique_Pa=800,
    rendement=0.75
)

puissance_ventilateur = ventilateur.calculer_puissance()

print(f"4. Ventilateur :")
print(f"   Puissance = {puissance_ventilateur:.2f} kW\n")

# Bilan énergétique total
print("=== BILAN ÉNERGÉTIQUE ===")
puissance_totale = (resultats_chauffage['puissance_kW'] +
                    resultats_humidif['puissance_kW'] +
                    puissance_ventilateur)
print(f"Puissance totale CTA : {puissance_totale:.2f} kW")

Exemple issu des tests : GenericAHU (Recycling)

from AHU.GenericAHU import GenericAHU

ahu = GenericAHU()
results = ahu.run_simulation(
    file_path='test/AHU/data/config_recycling.xlsx',
    sheet_name='1. Air Recycling AHU Input',
    output_file='test/AHU/data/results_recycling.xlsx'
)

print("Nombre de points simulés:", len(results))
print("Colonnes de sortie:", results.shape[1])
print("Puissance Chauffage moyenne:", results['HC_Q_th[kW]'].mean())

5.2. Module PinchAnalysis - Analyse Pinch

L’analyse Pinch permet d’optimiser les réseaux d’échangeurs de chaleur et de minimiser la consommation énergétique.

Exemple : Analyse Pinch simple

from energysystemmodels.PinchAnalysis import PinchAnalysis, Stream

# Définir les courants chauds
hot_streams = [
    Stream(name="H1", T_in=180, T_out=60, heat_flow=300),  # kW
    Stream(name="H2", T_in=150, T_out=40, heat_flow=200)
]

# Définir les courants froids
cold_streams = [
    Stream(name="C1", T_in=20, T_out=135, heat_flow=250),
    Stream(name="C2", T_in=80, T_out=140, heat_flow=150)
]

# Créer l'analyse Pinch
pinch = PinchAnalysis(
    hot_streams=hot_streams,
    cold_streams=cold_streams,
    delta_T_min=10  # Pincement minimal 10°C
)

# Calculer les résultats
resultats = pinch.analyze()

print(f"Température de pincement : {resultats['pinch_temperature']}°C")
print(f"Besoin minimum de chauffage : {resultats['hot_utility_min']:.0f} kW")
print(f"Besoin minimum de refroidissement : {resultats['cold_utility_min']:.0f} kW")
print(f"Récupération de chaleur possible : {resultats['heat_recovery']:.0f} kW")
print(f"Économie potentielle : {resultats['energy_savings_pct']:.1f}%")

Courbes composites

from energysystemmodels.PinchAnalysis import PinchAnalysis
import matplotlib.pyplot as plt

# Utiliser l'analyse précédente
fig = pinch.plot_composite_curves()
plt.show()

Exemple complet : Optimisation d’un procédé industriel

from energysystemmodels.PinchAnalysis import PinchAnalysis, Stream

# Procédé industriel avec 4 courants chauds et 3 courants froids
hot_streams = [
    Stream(name="Reactor outlet", T_in=220, T_out=40, heat_flow=500),
    Stream(name="Distillation", T_in=180, T_out=60, heat_flow=400),
    Stream(name="Product cooling", T_in=150, T_out=80, heat_flow=300),
    Stream(name="Exhaust gases", T_in=350, T_out=120, heat_flow=250)
]

cold_streams = [
    Stream(name="Feed preheating", T_in=20, T_out=180, heat_flow=450),
    Stream(name="Reboiler", T_in=140, T_out=145, heat_flow=350),
    Stream(name="Process water", T_in=15, T_out=90, heat_flow=200)
]

# Analyse avec différents pincements
delta_T_values = [5, 10, 15, 20]

print("Analyse de sensibilité au pincement :")
print("-" * 80)
print(f"{'ΔTmin (°C)':<12} {'Chauffage (kW)':<18} {'Refroid. (kW)':<18} {'Récup. (%)':<15}")
print("-" * 80)

for delta_T in delta_T_values:
    pinch = PinchAnalysis(hot_streams, cold_streams, delta_T_min=delta_T)
    resultats = pinch.analyze()

    print(f"{delta_T:<12.0f} {resultats['hot_utility_min']:<18.0f} "
          f"{resultats['cold_utility_min']:<18.0f} "
          f"{resultats['energy_savings_pct']:<15.1f}")

# Analyse détaillée avec ΔTmin = 10°C
print("\n=== Configuration optimale (ΔTmin = 10°C) ===")
pinch_optimal = PinchAnalysis(hot_streams, cold_streams, delta_T_min=10)
resultats_optimal = pinch_optimal.analyze()

print(f"\nBilan énergétique :")
print(f"  Chaleur disponible : {resultats_optimal['total_hot_available']:.0f} kW")
print(f"  Chaleur requise : {resultats_optimal['total_cold_required']:.0f} kW")
print(f"  Récupération interne : {resultats_optimal['heat_recovery']:.0f} kW")
print(f"  Utilité chaude nécessaire : {resultats_optimal['hot_utility_min']:.0f} kW")
print(f"  Utilité froide nécessaire : {resultats_optimal['cold_utility_min']:.0f} kW")

# Tracer les courbes composites
fig = pinch_optimal.plot_composite_curves()
plt.show()

Exemple issu des tests : PinchAnalysis sur données réelles

from PinchAnalysis import PinchAnalysis
import pandas as pd

data = {
    'id': [1, 2, 3, 4],
    'name': ['C1', 'H1', 'C2', 'H2'],
    'mCp': [2.0, 3.0, 4.0, 1.5],
    'Ti': [20, 170, 80, 150],
    'To': [135, 60, 140, 30],
    'dTmin2': [5, 5, 5, 5],
}

liste_flux = pd.DataFrame(data)
Pinch_study = PinchAnalysis.Object(liste_flux)
print(Pinch_study.Pinch_Temperature)
print(Pinch_study.Heating_duty)
print(Pinch_study.Cooling_duty)

5.3. Module IPMVP - International Performance Measurement and Verification Protocol

Le module IPMVP mesure et vérifie les économies d’énergie (Option C). La fonction Mathematical_Models ajuste un modèle de baseline puis quantifie les économies ; elle retourne un tuple de 9 éléments (il n’existe pas de classe IPMVPModel ni IPMVPReport).

import pandas as pd
from datetime import datetime
from IPMVP.IPMVP import Mathematical_Models, incertitude_savings

df = pd.read_excel("src/IPMVP/IPMVP_input.xlsx")
df["Mois"] = pd.to_datetime(df["Mois"])
df = df.set_index("Mois")
col_conso = [c for c in df.columns if c.lower().startswith("consommation")][0]
X, y = df[["DJU"]], df[col_conso]

(y_pred, df_bl, conformite, table_inc,
 y_pred_report, df_report, conformite_report, table_inc_report,
 df_savings) = Mathematical_Models(
    y, X,
    datetime(2016, 9, 1), datetime(2021, 5, 1),    # période de référence
    datetime(2021, 10, 1), datetime(2022, 10, 1),  # période de suivi
    degree=1, seuil_z_scores=3, site="exemple_site",
    print_report=True,   # rapport .docx + figures matplotlib
)

print(df_bl)          # coefficients + indicateurs (r2, rmse, cv_rmse, stat_t…)
print(conformite)     # verdict de conformité IPMVP
print(df_savings)     # économies ANTE-POST / POST-ANTE

Sortie réelle (extrait) : r2 = 0,81, économie ANTE-POST = 18,15 %.

Chapitre détaillé : exemple complet, mesure des économies, incertitude propagée (incertitude_savings), paramètres et théorie dans IPMVP - Mesure et Vérification.

5.4. Modèle RC de bâtiment

Le modèle RC (Résistance-Capacité) permet de simuler le comportement thermique dynamique d’un bâtiment.

Modèle RC simple (1R1C)

from energysystemmodels.BuildingModel import RC_Model

# Paramètres du bâtiment
modele_rc = RC_Model(
    resistance_thermique=0.01,  # K/W
    capacite_thermique=50e6,     # J/K
    surface_vitree_m2=30,
    orientation_vitrage=180,     # Sud
    apports_internes_W=500
)

# Conditions initiales
T_interieure_initiale = 20  # °C

# Simulation sur 24h
import numpy as np

heures = np.arange(0, 24, 1)
temperatures_ext = 10 + 5 * np.sin(2 * np.pi * (heures - 6) / 24)

temperatures_int = []
T_int = T_interieure_initiale

for h, T_ext in zip(heures, temperatures_ext):
    T_int = modele_rc.simuler_pas_de_temps(
        T_interieure=T_int,
        T_exterieure=T_ext,
        rayonnement_solaire_W_m2=max(0, 500 * np.sin(np.pi * (h - 6) / 12)),
        dt_seconds=3600
    )
    temperatures_int.append(T_int)

# Affichage
import matplotlib.pyplot as plt

plt.figure(figsize=(12, 6))
plt.plot(heures, temperatures_ext, label='T° extérieure', linewidth=2)
plt.plot(heures, temperatures_int, label='T° intérieure', linewidth=2)
plt.xlabel('Heure')
plt.ylabel('Température (°C)')
plt.title('Modèle RC 1R1C - Évolution des températures')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

Modèle RC avancé (2R2C)

from energysystemmodels.BuildingModel import RC_Model_Advanced

# Modèle 2R2C avec inertie lourde et légère
modele_2r2c = RC_Model_Advanced(
    R_envelope=0.005,      # K/W - Résistance enveloppe
    R_internal=0.003,      # K/W - Résistance interne
    C_light=10e6,          # J/K - Capacité légère (air)
    C_heavy=100e6,         # J/K - Capacité lourde (structure)
    surface_vitree_m2=40,
    apports_internes_W=800
)

# Simulation avec chauffage
T_consigne = 20  # °C

resultats_simulation = modele_2r2c.simuler_periode(
    T_ext_serie=temperatures_ext,
    T_consigne=T_consigne,
    puissance_chauffage_max_W=5000,
    dt_seconds=3600
)

print(f"Consommation de chauffage : {resultats_simulation['energie_chauffage_kWh']:.1f} kWh")
print(f"Température moyenne : {np.mean(resultats_simulation['temperatures_int']):.1f}°C")