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 permet de mesurer et vérifier les économies d’énergie selon le protocole international.

5.3.1. Modèle de régression journalière

from energysystemmodels.IPMVP import IPMVPModel
import pandas as pd
import numpy as np

# Générer des données de référence (baseline)
dates_baseline = pd.date_range('2023-01-01', '2023-12-31', freq='D')
temperatures = 15 + 10 * np.sin(2 * np.pi * np.arange(len(dates_baseline)) / 365)

# Consommation corrélée à la température
consommation_baseline = 1000 + 50 * (20 - temperatures) * (temperatures < 20)

df_baseline = pd.DataFrame({
    'date': dates_baseline,
    'temperature': temperatures,
    'consommation_kWh': consommation_baseline
})

# Créer le modèle IPMVP
model = IPMVPModel(periode_baseline=df_baseline)

# Entraîner le modèle
model.fit(variable_independante='temperature', variable_dependante='consommation_kWh')

print(f"R² du modèle : {model.r_squared:.3f}")
print(f"RMSE : {model.rmse:.2f} kWh")

Exemple issu des tests : IPMVP avec Excel

from IPMVP.IPMVP import Mathematical_Models
import os
from datetime import datetime
import pandas as pd

directory = os.getcwd()
directory = directory + "\src\IPMVP\" + "IPMVP_input.xlsx"
df = pd.read_excel(directory)
df['Mois'] = pd.to_datetime(df['Mois'])
df = df.set_index(df['Mois'])

start_baseline_period = datetime(2016, 9, 1, hour=0)
end_baseline_period = datetime(2021, 5, 1, hour=0)
start_reporting_period = datetime(2021, 10, 1, hour=0)
end_reporting_period = datetime(2022, 10, 1, hour=0)

X = df[["DJU"]]
y = df["Consommation (kWh) - Relevé"]

result = Mathematical_Models(
    y, X,
    start_baseline_period, end_baseline_period,
    start_reporting_period, end_reporting_period,
    degree=3, print_report=True, seuil_z_scores=3, site="exemple_site"
)
print(result)

Calcul des économies

# Données de la période de reporting (après travaux)
dates_reporting = pd.date_range('2024-01-01', '2024-12-31', freq='D')
temperatures_reporting = 15 + 10 * np.sin(2 * np.pi * np.arange(len(dates_reporting)) / 365)

# Consommation réelle après travaux (réduction de 20%)
consommation_reporting = (1000 + 50 * (20 - temperatures_reporting) *
                           (temperatures_reporting < 20)) * 0.80

df_reporting = pd.DataFrame({
    'date': dates_reporting,
    'temperature': temperatures_reporting,
    'consommation_kWh': consommation_reporting
})

# Calculer les économies
economies = model.calculer_economies(df_reporting)

print(f"\n=== RÉSULTATS IPMVP ===")
print(f"Consommation baseline ajustée : {economies['baseline_ajustee_kWh']:.0f} kWh")
print(f"Consommation réelle : {economies['consommation_reelle_kWh']:.0f} kWh")
print(f"Économies réalisées : {economies['economies_kWh']:.0f} kWh")
print(f"Taux d'économie : {economies['taux_economie_pct']:.1f}%")
print(f"Incertitude : ±{economies['incertitude_pct']:.1f}%")

5.3.2. Modèle hebdomadaire

from energysystemmodels.IPMVP import IPMVPModel
import pandas as pd

# Données hebdomadaires
dates_hebdo = pd.date_range('2023-01-01', '2023-12-31', freq='W')

df_hebdo_baseline = pd.DataFrame({
    'semaine': range(len(dates_hebdo)),
    'temperature_moy': 15 + 8 * np.sin(2 * np.pi * np.arange(len(dates_hebdo)) / 52),
    'production_unite': 1000 + 200 * np.random.random(len(dates_hebdo)),
    'consommation_kWh': 7000 + np.random.normal(0, 500, len(dates_hebdo))
})

# Modèle multi-variables
model_hebdo = IPMVPModel(periode_baseline=df_hebdo_baseline)
model_hebdo.fit(
    variables_independantes=['temperature_moy', 'production_unite'],
    variable_dependante='consommation_kWh'
)

print(f"R² du modèle hebdomadaire : {model_hebdo.r_squared:.3f}")

5.3.3. Modèle mensuel

from energysystemmodels.IPMVP import IPMVPModel
import pandas as pd

# Données mensuelles de consommation
mois = ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Jun',
        'Jul', 'Aoû', 'Sep', 'Oct', 'Nov', 'Déc']

df_mensuel = pd.DataFrame({
    'mois': mois,
    'dju_chauffage': [450, 380, 310, 180, 80, 20, 0, 0, 50, 150, 280, 400],
    'consommation_gaz_kWh': [45000, 38000, 31000, 18000, 8000, 2000,
                              0, 0, 5000, 15000, 28000, 40000]
})

# Modèle mensuel
model_mensuel = IPMVPModel(periode_baseline=df_mensuel)
model_mensuel.fit(
    variable_independante='dju_chauffage',
    variable_dependante='consommation_gaz_kWh'
)

print(f"\nModèle mensuel :")
print(f"R² : {model_mensuel.r_squared:.3f}")
print(f"Coefficient DJU : {model_mensuel.coefficients['dju_chauffage']:.2f} kWh/DJU")

Rapport IPMVP complet

from energysystemmodels.IPMVP import IPMVPReport

# Générer un rapport complet
rapport = IPMVPReport(
    model=model,
    periode_baseline=df_baseline,
    periode_reporting=df_reporting,
    description_projet="Isolation de l'enveloppe et optimisation CVC",
    cout_travaux=150000,
    cout_energie=0.10  # €/kWh
)

# Générer le rapport
rapport.generer_rapport(fichier='rapport_ipmvp.pdf')

# Afficher le résumé
print(rapport.get_summary())

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")