Entropyk/EXAMPLES.md

554 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)](#1-types-physiques-entropyk-core)
2. [Ports et connexions](#2-ports-et-connexions)
3. [Compresseur (AHRI 540)](#3-compresseur-ahri-540)
4. [Détendeur (expansion valve)](#4-détendeur-expansion-valve)
5. [Conduite (pipe)](#5-conduite-pipe)
6. [Pompe](#6-pompe)
7. [Ventilateur](#7-ventilateur)
8. [Échangeurs de chaleur](#8-échangeurs-de-chaleur)
9. [Machine à états (ON/OFF/BYPASS)](#9-machine-à-états-onoffbypass)
10. [Polynômes et courbes de performance](#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.
```rust
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.
```rust
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
```rust
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.
```rust
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)
```rust
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).
```rust
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.
```rust
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
```rust
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.
```rust
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.
```rust
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)
```rust
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
```rust
use entropyk_components::heat_exchanger::Evaporator;
let evaporator = Evaporator::new(8_000.0);
```
### Économiseur
```rust
use entropyk_components::heat_exchanger::Economizer;
let economizer = Economizer::new(5_000.0);
```
### Modèles de transfert (LMTD vs ε-NTU)
```rust
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).
```rust
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
```rust
// 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
```rust
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
```rust
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)
```rust
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
```bash
# 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
```bash
cargo test --workspace
cargo test -p entropyk-components
cargo test -p entropyk-core
```
### Documentation
```bash
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*