554 lines
15 KiB
Markdown
554 lines
15 KiB
Markdown
# 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*
|