+
+
+
+ Concept — One-Shot Inverse Control
+
+
FR24: Inverse Control solved simultaneously with cycle equations
+
+
+
+
+
+ ❌ Approche Traditionnelle
+
+
+
1. Fixer valve → Simuler
+
2. Mesurer superheat
+
3. Ajuster valve
+
4. Répéter (optimisation externe)
+
+ → Lent, coûteux, non garanti
+
+
+
+
+
+
+ ✅ Approche One-Shot (Entropyk)
+
+
+
1. Définir contrainte: superheat = 5K
+
2. Lier à variable: valve position
+
3. Valve devient inconnue du solveur
+
4. Résolution simultanée (1 appel)
+
+ → Rapide, garanti, élégant
+
+
+
+
+
+
+
Vecteur de résidus étendu
+
+ rtotal = [ rcycle, rconstraint ]T = 0
+
+
+ rconstraint = superheatmesuré − superheatcible
+
+
+
+
+
+
+
+ Mapping — Contrainte → Variable de Contrôle
+
+
+
+
📐 Constraint
+
superheat_control
+
target: 5.0 K
+
+
+
+
+
link_constraint_to_control()
+
+
+
+
🎚 BoundedVar
+
valve_position
+
[0.1, 1.0]
+
+
+
+
+
+
✓ Résolu
+
valve = 38%
+
SH = 5.02 K
+
+
+
+
+
+
+ Validation des Degrés de Liberté (DoF)
+
+
+
+
Équations
+
9
+
composants: 8
contraintes: +1
+
+
+
=
+
+
+
Inconnues
+
9
+
états bords: 8
contrôles: +1
+
+
+
+
+ ✓ Système bien posé — validate_inverse_control_dof() = Ok
+
+
+
+
+
Équilibré
+
n_eqs == n_unknowns
+
Résolvable
+
+
+
Sur-contraint
+
n_eqs > n_unknowns
+
Erreur
+
+
+
Sous-contraint
+
n_eqs < n_unknowns
+
Warning
+
+
+
+
+
+
+ Équations du système étendu
+
+
+
+
📦 Équations du cycle (8 éq.)
+
+ | Composant | Équation | État |
+
+ | Compresseur | r₁(P,h) = 0 | 2 éq. |
+ | Condenseur | r₂(P,h) = 0 | 2 éq. |
+ | EXV | r₃(P,h) = 0 | 2 éq. |
+ | Évaporateur | r₄(P,h) = 0 | 2 éq. |
+
+
+
+
+
+
📐 Équations de contrainte (+1 éq.)
+
+ | Type | Équation | Valeur |
+
+ | Superheat | r_c = SH − 5.0 | 5.02 − 5.0 |
+ | Jacobian | ∂r_c/∂valve | ≈ 1.0 |
+
+ | State idx | 2·edges + i | idx = 8 |
+
+
+
+
+
+
+
+
+
+ Convergence — Newton-Raphson
+
+
+
+
+
+
+ Valeurs au point de fonctionnement
+
+
+
+
Contrainte Superheat
+
IDsuperheat_control
+
OutputSuperheat(evaporator)
+
Target5.0 K
+
Mesuré5.02 K
+
Résidu0.02 K
+
Satisfait✓
+
+
+
+
Variable de Contrôle
+
IDvalve_position
+
Initial50.0%
+
Final38.0%
+
Bounds[10%, 100%]
+
SaturéNon
+
State idx8
+
+
+
+
Solveur Newton-Raphson
+
Itérations7
+
Tolérance1e-8
+
‖r‖ final3.2e-9
+
MéthodeOne-Shot
+
Temps12 ms
+
+
+
+
DoF Validation
+
Edges4 (×2 = 8)
+
Controls+1
+
Total unknowns9
+
Components4 (×2 = 8)
+
Constraints+1
+
Total equations9
+
Balance9 = 9 ✓
+
+
+
+
+
+
+
+ API Rust — Utilisation
+ // ══════════════════════════════════════════════════════════════════════
+// Story 5.3: Residual Embedding for Inverse Control
+// ══════════════════════════════════════════════════════════════════════
+
+use entropyk_solver::inverse::{
+ Constraint, ConstraintId, ComponentOutput,
+ BoundedVariable, BoundedVariableId,
+};
+
+// 1. Définir la contrainte: superheat = 5K
+let constraint = Constraint::new(
+ ConstraintId::new("superheat_control"),
+ ComponentOutput::Superheat { component_id: "evaporator".into() },
+ 5.0, // target: 5 Kelvin
+);
+system.add_constraint(constraint)?;
+
+// 2. Définir la variable de contrôle bornée
+let control = BoundedVariable::new(
+ BoundedVariableId::new("valve_position"),
+ 0.5, 0.1, 1.0, // init, min, max
+)?;
+system.add_bounded_variable(control)?;
+
+// 3. Lier contrainte → contrôle (One-Shot!)
+system.link_constraint_to_control(
+ &ConstraintId::new("superheat_control"),
+ &BoundedVariableId::new("valve_position"),
+)?;
+
+// 4. Valider DoF + Finalize
+system.validate_inverse_control_dof()?;
+system.finalize()?;
+
+// 5. Résoudre (One-Shot)
+let result = NewtonRaphson::new().solve(&system)?;
+
+// 6. Résultat
+let valve = system.get_bounded_variable(&BoundedVariableId::new("valve_position")).unwrap();
+println!("Valve: {:.1}% SH: {:.2} K", valve.value()*100.0, sh);
+
+
+
+
+ API — Méthodes System
+
+
+ | Méthode | Description | Retour |
+
+ | add_constraint(c) | Ajoute une contrainte | Result<(), ConstraintError> |
+ | add_bounded_variable(v) | Ajoute variable bornée | Result<(), BoundedVariableError> |
+ | link_constraint_to_control(cid, vid) | Lie contrainte → contrôle | Result<(), DoFError> |
+ | unlink_constraint(cid) | Supprime le lien | Option<BoundedVariableId> |
+ | validate_inverse_control_dof() | Valide éq == inconnues | Result<(), DoFError> |
+ | control_variable_state_index(id) | Index vecteur d'état | Option<usize> |
+ | full_state_vector_len() | Longueur totale | usize |
+ | compute_constraint_residuals(...) | Calcule résidus contraintes | usize |
+ | compute_inverse_control_jacobian(...) | Jacobian ∂r/∂control | Vec<(row,col,val)> |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/src/bin/chiller.rs b/demo/src/bin/chiller.rs
index 4c40452..e5955b0 100644
--- a/demo/src/bin/chiller.rs
+++ b/demo/src/bin/chiller.rs
@@ -49,6 +49,7 @@ struct PlaceholderComponent {
}
impl PlaceholderComponent {
+ #[allow(clippy::new_ret_no_self)]
fn new(name: &str) -> Box