Entropyk/EXAMPLES.md

15 KiB
Raw Blame History

Exemples d'utilisation - Entropyk

Ce document présente des exemples d'utilisation de la bibliothèque Entropyk, basés sur les composants actuellement développés (Epic 1 : Extensible Component Framework).

Table des matières

  1. Types physiques (entropyk-core)
  2. Ports et connexions
  3. Compresseur (AHRI 540)
  4. Détendeur (expansion valve)
  5. Conduite (pipe)
  6. Pompe
  7. Ventilateur
  8. Échangeurs de chaleur
  9. Machine à états (ON/OFF/BYPASS)
  10. Polynômes et courbes de performance

1. Types physiques (entropyk-core)

Les types physiques utilisent le pattern NewType pour la sécurité des unités à la compilation.

use entropyk_core::{Pressure, Enthalpy, Temperature, MassFlow, Power};

fn main() {
    // Pression : Pascals, bar, PSI
    let p = Pressure::from_bar(3.5);
    println!("Pression: {} Pa = {:.2} bar", p.to_pascals(), p.to_bar());

    // Enthalpie : J/kg
    let h = Enthalpy::from_joules_per_kg(400_000.0);
    println!("Enthalpie: {:.0} J/kg", h.to_joules_per_kg());

    // Température : Kelvin, Celsius, Fahrenheit
    let t = Temperature::from_celsius(25.0);
    println!("Température: {:.2} K = {:.1} °C", t.to_kelvin(), t.to_celsius());

    // Débit massique : kg/s
    let m = MassFlow::from_kg_per_s(0.05);
    println!("Débit: {:.4} kg/s", m.to_kg_per_s());

    // Puissance : Watts
    let w = Power::from_watts(1500.0);
    println!("Puissance: {:.0} W", w.to_watts());

    // Opérations arithmétiques
    let p2 = p + Pressure::from_bar(1.0);
    let p3 = p * 2.0;
}

2. Ports et connexions

Le système de ports utilise le Type-State pattern : les ports doivent être connectés avant utilisation dans le solveur.

use entropyk_components::port::{Port, FluidId, ConnectionError};
use entropyk_core::{Pressure, Enthalpy};

fn main() -> Result<(), ConnectionError> {
    // Créer deux ports déconnectés (même fluide, pression et enthalpie pour validation)
    let port1 = Port::new(
        FluidId::new("R134a"),
        Pressure::from_bar(1.0),
        Enthalpy::from_joules_per_kg(400_000.0),
    );

    let port2 = Port::new(
        FluidId::new("R134a"),
        Pressure::from_bar(1.0),
        Enthalpy::from_joules_per_kg(400_000.0),
    );

    // Connecter les ports
    let (mut connected1, mut connected2) = port1.connect(port2)?;

    // Une fois connectés, on peut lire et modifier les valeurs
    println!("Pression port 1: {:.2} bar", connected1.pressure().to_bar());
    connected1.set_pressure(Pressure::from_bar(1.5));
    connected1.set_enthalpy(Enthalpy::from_joules_per_kg(450_000.0));

    Ok(())
}

Gestion des erreurs de connexion

use entropyk_components::port::{Port, FluidId, ConnectionError};
use entropyk_core::{Pressure, Enthalpy};

fn main() {
    // Erreur : fluides incompatibles
    let r134a = Port::new(
        FluidId::new("R134a"),
        Pressure::from_bar(1.0),
        Enthalpy::from_joules_per_kg(400_000.0),
    );
    let water = Port::new(
        FluidId::new("Water"),
        Pressure::from_bar(1.0),
        Enthalpy::from_joules_per_kg(400_000.0),
    );

    match r134a.connect(water) {
        Err(ConnectionError::IncompatibleFluid { from, to }) => {
            println!("Erreur: {} et {} sont incompatibles", from, to);
        }
        _ => {}
    }

    // Erreur : pression différente
    let p1 = Port::new(
        FluidId::new("R134a"),
        Pressure::from_pascals(100_000.0),
        Enthalpy::from_joules_per_kg(400_000.0),
    );
    let p2 = Port::new(
        FluidId::new("R134a"),
        Pressure::from_pascals(200_000.0),
        Enthalpy::from_joules_per_kg(400_000.0),
    );

    match p1.connect(p2) {
        Err(ConnectionError::PressureMismatch { .. }) => {
            println!("Erreur: pression non compatible");
        }
        _ => {}
    }
}

3. Compresseur (AHRI 540)

Le compresseur utilise les coefficients AHRI 540 pour le débit massique et la puissance.

use entropyk_components::compressor::{Compressor, Ahri540Coefficients};
use entropyk_components::port::{FluidId, Port};
use entropyk_core::{Pressure, Enthalpy};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Coefficients AHRI 540 (exemple typique)
    let coeffs = Ahri540Coefficients::new(
        0.85, 2.5,      // M1, M2 (débit)
        500.0, 1500.0, -2.5, 1.8,  // M3-M6 (puissance refroidissement)
        600.0, 1600.0, -3.0, 2.0   // M7-M10 (puissance chauffage)
    );

    // Créer les ports (même P et h pour validation)
    let suction = Port::new(
        FluidId::new("R134a"),
        Pressure::from_bar(3.5),
        Enthalpy::from_joules_per_kg(400_000.0),
    );
    let discharge = Port::new(
        FluidId::new("R134a"),
        Pressure::from_bar(3.5),
        Enthalpy::from_joules_per_kg(400_000.0),
    );

    // Créer le compresseur déconnecté
    let compressor = Compressor::new(
        coeffs,
        suction,
        discharge,
        2900.0,    // RPM
        0.0001,    // Volume balayé (m³/tour)
        0.85       // Rendement mécanique
    )?;

    println!("Compresseur créé: fluide={}, vitesse={} RPM",
        compressor.fluid_id(),
        compressor.speed_rpm()
    );

    Ok(())
}

Modèle SST/SDT (températures de saturation)

use entropyk_components::compressor::{Compressor, SstSdtCoefficients};
use entropyk_components::polynomials::Polynomial2D;

// Modèle bilinéaire: ṁ = a00 + a10*SST + a01*SDT + a11*SST*SDT
let mass_coeffs = SstSdtCoefficients::bilinear(
    0.05, 0.001, 0.0005, 0.00001,  // coefficients débit
    1000.0, 50.0, 30.0, 0.5         // coefficients puissance
);

// Évaluer à SST=273K, SDT=313K
let mass_flow = mass_coeffs.mass_flow_at(273.15, 313.15);
let power = mass_coeffs.power_at(273.15, 313.15);

4. Détendeur (expansion valve)

Le détendeur modélise une détente isenthalpique (h_out = h_in).

use entropyk_components::expansion_valve::ExpansionValve;
use entropyk_components::port::{FluidId, Port};
use entropyk_core::{Pressure, Enthalpy};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let inlet = Port::new(
        FluidId::new("R134a"),
        Pressure::from_bar(10.0),
        Enthalpy::from_joules_per_kg(250_000.0),
    );
    let outlet = Port::new(
        FluidId::new("R134a"),
        Pressure::from_bar(10.0),
        Enthalpy::from_joules_per_kg(250_000.0),
    );

    // Ouverture optionnelle: None = variable, Some(1.0) = pleinement ouvert
    let valve = ExpansionValve::new(inlet, outlet, Some(1.0))?;

    println!("Détendeur créé: fluide={}", valve.fluid_id());

    // États opérationnels: On, Off, Bypass
    // valve.set_operational_state(OperationalState::Off);

    Ok(())
}

5. Conduite (pipe)

Modélisation de la perte de charge avec l'équation de Darcy-Weisbach.

use entropyk_components::pipe::{Pipe, PipeGeometry, roughness};
use entropyk_components::port::{FluidId, Port};
use entropyk_core::{Pressure, Enthalpy};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Géométrie: 10m de tuyau cuivre 22mm, lisse
    let geometry = PipeGeometry::new(
        10.0,                    // longueur (m)
        0.022,                   // diamètre intérieur (m)
        roughness::SMOOTH,       // rugosité (m)
    )?;

    let inlet = Port::new(
        FluidId::new("Water"),
        Pressure::from_bar(2.0),
        Enthalpy::from_joules_per_kg(84_000.0),
    );
    let outlet = Port::new(
        FluidId::new("Water"),
        Pressure::from_bar(2.0),
        Enthalpy::from_joules_per_kg(84_000.0),
    );

    // Eau à 20°C: ρ≈998 kg/m³, μ≈0.001 Pa·s
    let pipe = Pipe::new(
        geometry,
        inlet,
        outlet,
        998.0,   // densité
        0.001,   // viscosité
    )?;

    println!("Conduite créée: L={}m, D={}m",
        pipe.geometry().length_m,
        pipe.geometry().diameter_m
    );

    Ok(())
}

Rugosités typiques

use entropyk_components::pipe::roughness;

// roughness::SMOOTH         // Cuivre, plastique (0.0015 mm)
// roughness::STEEL_COMMERCIAL  // Acier commercial (0.045 mm)
// roughness::GALVANIZED_IRON   // Fer galvanisé (0.15 mm)
// roughness::CAST_IRON         // Fonte (0.26 mm)
// roughness::PLASTIC           // PVC/HDPE (0.0015 mm)

6. Pompe

Courbes de performance polynomiales + lois d'affinité pour la variation de vitesse.

use entropyk_components::pump::{Pump, PumpCurves};
use entropyk_components::port::{FluidId, Port};
use entropyk_core::{Pressure, Enthalpy};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Courbe hauteur: H = 30 - 10*Q - 50*Q² (m)
    // Courbe rendement: η = 0.5 + 0.3*Q - 0.5*Q²
    let curves = PumpCurves::quadratic(
        30.0, -10.0, -50.0,  // H = h0 + h1*Q + h2*Q²
        0.5, 0.3, -0.5,     // η = e0 + e1*Q + e2*Q²
    )?;

    let inlet = Port::new(
        FluidId::new("Water"),
        Pressure::from_bar(1.0),
        Enthalpy::from_joules_per_kg(100_000.0),
    );
    let outlet = Port::new(
        FluidId::new("Water"),
        Pressure::from_bar(1.0),
        Enthalpy::from_joules_per_kg(100_000.0),
    );

    let pump = Pump::new(curves, inlet, outlet, 1000.0)?;

    // Évaluer la courbe à un débit donné (pompe connectée requise pour pressure_rise)
    let head = pump.curves().head_at_flow(0.05);
    let eff = pump.curves().efficiency_at_flow(0.05);
    println!("À Q=0.05 m³/s: H={:.2} m, η={:.2}%", head, eff * 100.0);

    Ok(())
}

7. Ventilateur

Similaire à la pompe, avec courbe de pression statique.

use entropyk_components::fan::{Fan, FanCurves};
use entropyk_components::port::{FluidId, Port};
use entropyk_core::{Pressure, Enthalpy};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Courbe pression statique (Pa) et rendement
    let curves = FanCurves::quadratic(
        500.0, -100.0, -50.0,   // P_s = p0 + p1*Q + p2*Q²
        0.4, 0.2, -0.3,         // η
    )?;

    let inlet = Port::new(
        FluidId::new("Air"),
        Pressure::from_pascals(101_325.0),
        Enthalpy::from_joules_per_kg(300_000.0),
    );
    let outlet = Port::new(
        FluidId::new("Air"),
        Pressure::from_pascals(101_325.0),
        Enthalpy::from_joules_per_kg(300_000.0),
    );

    let fan = Fan::new(curves, inlet, outlet, 1.2)?;  // ρ_air ≈ 1.2 kg/m³

    let pressure = fan.curves().static_pressure_at_flow(1.0);
    println!("À Q=1 m³/s: ΔP={:.0} Pa", pressure);

    Ok(())
}

8. Échangeurs de chaleur

Condenseur (LMTD)

use entropyk_components::heat_exchanger::Condenser;

// UA = 10 kW/K
let condenser = Condenser::new(10_000.0);

// Avec température de saturation personnalisée
let condenser = Condenser::with_saturation_temp(15_000.0, 323.15);

println!("Condenseur: UA={} W/K, n_equations={}", condenser.ua(), condenser.n_equations());

Évaporateur

use entropyk_components::heat_exchanger::Evaporator;

let evaporator = Evaporator::new(8_000.0);

Économiseur

use entropyk_components::heat_exchanger::Economizer;

let economizer = Economizer::new(5_000.0);

Modèles de transfert (LMTD vs ε-NTU)

use entropyk_components::heat_exchanger::{LmtdModel, EpsNtuModel, FlowConfiguration, ExchangerType};

// LMTD - contre-courant
let lmtd = LmtdModel::new(5000.0, FlowConfiguration::CounterFlow);

// ε-NTU - échangeur à plaques
let eps_ntu = EpsNtuModel::new(5000.0, ExchangerType::CounterFlow);

9. Machine à états (ON/OFF/BYPASS)

Les composants supportent trois états opérationnels (FR6-FR8).

use entropyk_components::state_machine::{OperationalState, CircuitId, StateManageable};

fn main() {
    // États disponibles
    let on = OperationalState::On;      // Opération normale
    let off = OperationalState::Off;   // Débit nul
    let bypass = OperationalState::Bypass;  // Conduite adiabatique (P_in=P_out, h_in=h_out)

    // Propriétés des états
    println!("On actif: {}", on.is_active());
    println!("Multiplicateur débit Off: {}", off.mass_flow_multiplier());  // 0.0
    println!("Multiplicateur débit Bypass: {}", bypass.mass_flow_multiplier());  // 1.0

    // Transitions
    assert!(on.can_transition_to(OperationalState::Off));

    // Identification des circuits (multi-circuit, ex: PAC double circuit)
    let circuit_primary = CircuitId::new("primary");
    let circuit_secondary = CircuitId::new("secondary");
    println!("Circuit: {}", circuit_primary);
}

Utilisation avec un composant

// Sur un compresseur, pompe, détendeur, etc.
// compressor.set_operational_state(OperationalState::Off);
// pump.set_speed_ratio(0.5);  // 50% vitesse pour VFD

10. Polynômes et courbes de performance

Polynôme 1D

use entropyk_components::polynomials::Polynomial1D;

// P(x) = 1 + 2x + 3x²
let poly = Polynomial1D::new(vec![1.0, 2.0, 3.0]);
let y = poly.evaluate(2.0);  // 1 + 4 + 12 = 17

Polynôme 2D

use entropyk_components::polynomials::Polynomial2D;

// Modèle bilinéaire: f(x,y) = a00 + a10*x + a01*y + a11*x*y
let poly = Polynomial2D::bilinear(1.0, 0.1, 0.2, 0.01);
let z = poly.evaluate(10.0, 20.0);

Lois d'affinité (pompes/ventilateurs)

use entropyk_components::polynomials::AffinityLaws;

// À 50% de vitesse: Q₂/Q₁=0.5, H₂/H₁=0.25, P₂/P₁=0.125
let ratio = 0.5;
let flow_ratio = AffinityLaws::flow_ratio(ratio);   // 0.5
let head_ratio = AffinityLaws::head_ratio(ratio);   // 0.25
let power_ratio = AffinityLaws::power_ratio(ratio); // 0.125

Exécution des exemples

Binaires de démonstration

# State Machine (compresseur ON/OFF/BYPASS)
cargo run -p entropyk-demo --bin compressor-test

# Ports et connexions
cargo run -p entropyk-demo --bin ports

# Détendeur
cargo run -p entropyk-demo --bin expansion_valve

# Conduite
cargo run -p entropyk-demo --bin pipe

# Pompe
cargo run -p entropyk-demo --bin pump

Tests

cargo test --workspace
cargo test -p entropyk-components
cargo test -p entropyk-core

Documentation

cargo doc --workspace --open

Composants développés (état actuel)

Composant Statut Trait Component
Port/Connexion -
Compresseur (AHRI 540, SST/SDT)
Détendeur
Conduite
Pompe
Ventilateur
Condenseur/Évaporateur/Économiseur
Modèle externe
State Machine -

Document généré à partir du code et de README_STORY_1_3.md - Février 2026