16 KiB
Plan de Correction Post-Audit de Cohérence
Date: 2026-02-22
Auteur: Audit Architecture Rust & BMAD
Priorité: CRITIQUE
Statut: À implémenter
Résumé Exécutif
Cet audit a identifié 12 écarts de cohérence répartis en 3 catégories de priorité. Les problèmes les plus critiques concernent l'Epic 7 (Validation) où l'implémentation incomplète des méthodes energy_transfers() et port_enthalpies() empêche une validation thermodynamique correcte.
Impact métier: Sans ces corrections, un système thermodynamique contenant un détendeur, des jonctions ou des conditions aux limites NE sera PAS correctement validé, ce qui peut masquer des erreurs physiques graves.
Catalogue des User Stories de Correction
Epic 9: Correction de Cohérence (Nouvel Epic)
Story 9.1: Unification des Types Duplicats - CircuitId
Priorité: P1-CRITIQUE
Estimation: 2h
Dépendances: Aucune
Story:
En tant que développeur Rust,
Je veux un typeCircuitIdunique et cohérent,
Afin d'éviter les erreurs de compilation lors de l'utilisation conjointe des modules solver et components.
Problème actuel:
// crates/solver/src/system.rs:31
pub struct CircuitId(pub u8); // Représentation numérique
// crates/components/src/state_machine.rs:332
pub struct CircuitId(String); // Représentation textuelle
Solution proposée:
- Garder
CircuitId(u8)danscrates/core/src/types.rspour performance - Ajouter
impl From<&str> for CircuitIdavec hash interne - Supprimer
CircuitIddestate_machine.rs - Ré-exporter depuis
entropyk_core
Fichiers à modifier:
crates/core/src/types.rs(ajout)crates/solver/src/system.rs(suppression, ré-import)crates/components/src/state_machine.rs(suppression, ré-import)crates/entropyk/src/lib.rs(mise à jour exports)
Critères d'acceptation:
- Un seul
CircuitIddans la codebase - Conversion
From<&str>etFrom<u8>disponibles cargo test --workspacepassecargo clippy -- -D warningspasse
Story 9.2: Unification des Types Duplicats - FluidId
Priorité: P1-CRITIQUE
Estimation: 2h
Dépendances: Aucune
Story:
En tant que développeur Rust,
Je veux un typeFluidIdunique avec API cohérente,
Afin d'éviter la confusion entrefluids::FluidIdetcomponents::port::FluidId.
Problème actuel:
// crates/fluids/src/types.rs:35
pub struct FluidId(pub String); // Champ public
// crates/components/src/port.rs:137
pub struct FluidId(String); // Champ privé, méthode as_str()
Solution proposée:
- Garder
FluidIddanscrates/fluids/src/types.rscomme source unique - Exposer
as_str()et garder champ public pour compatibilité - Supprimer
FluidIddeport.rs - Ré-importer depuis
entropyk_fluids
Fichiers à modifier:
crates/fluids/src/types.rs(ajout méthodeas_str())crates/components/src/port.rs(suppression, ré-import)crates/components/src/lib.rs(mise à jour exports)
Critères d'acceptation:
- Un seul
FluidIddans la codebase - Méthode
as_str()disponible - Champ
0accessible pour compatibilité cargo test --workspacepasse
Story 9.3: Complétion Epic 7 - ExpansionValve Energy Methods
Priorité: P1-CRITIQUE
Estimation: 3h
Dépendances: Story 9.2
Story:
En tant que moteur de simulation thermodynamique,
Je veux queExpansionValveimplémenteenergy_transfers()etport_enthalpies(),
Afin que le bilan énergétique soit correctement validé pour les cycles frigorifiques.
Problème actuel:
ExpansionValveimplémente seulementport_mass_flows()- Le détendeur est ignoré dans
check_energy_balance() - Or, c'est un composant critique de tout cycle frigorifique
Solution proposée:
// Dans crates/components/src/expansion_valve.rs
impl Component for ExpansionValve<Connected> {
// ... existing code ...
fn port_enthalpies(
&self,
_state: &SystemState,
) -> Result<Vec<entropyk_core::Enthalpy>, ComponentError> {
// Retourne les enthalpies des ports (ordre: inlet, outlet)
Ok(vec![
self.port_inlet.enthalpy(),
self.port_outlet.enthalpy(),
])
}
fn energy_transfers(&self, _state: &SystemState) -> Option<(Power, Power)> {
// Détendeur isenthalpique: Q=0, W=0
// (Pas de transfert thermique, pas de travail)
Some((Power::from_watts(0.0), Power::from_watts(0.0)))
}
}
Fichiers à modifier:
crates/components/src/expansion_valve.rs
Critères d'acceptation:
energy_transfers()retourne(Power(0), Power(0))port_enthalpies()retourne[h_in, h_out]- Test unitaire
test_expansion_valve_energy_balancepasse check_energy_balance()ne skip plusExpansionValve
Story 9.4: Complétion Epic 7 - FlowSource/FlowSink Energy Methods
Priorité: P1-CRITIQUE
Estimation: 3h
Dépendances: Story 9.2
Story:
En tant que moteur de simulation thermodynamique,
Je veux queFlowSourceetFlowSinkimplémententenergy_transfers()etport_enthalpies(),
Afin que les conditions aux limites soient correctement prises en compte dans le bilan énergétique.
Problème actuel:
FlowSourceetFlowSinkimplémentent seulementport_mass_flows()- Ces composants sont ignorés dans la validation
Solution proposée:
// Dans crates/components/src/flow_boundary.rs
impl Component for FlowSource {
// ... existing code ...
fn port_enthalpies(
&self,
_state: &SystemState,
) -> Result<Vec<entropyk_core::Enthalpy>, ComponentError> {
Ok(vec![self.port.enthalpy()])
}
fn energy_transfers(&self, _state: &SystemState) -> Option<(Power, Power)> {
// Source: pas de transfert actif, le fluide "apparaît" avec son enthalpie
Some((Power::from_watts(0.0), Power::from_watts(0.0)))
}
}
impl Component for FlowSink {
// ... existing code ...
fn port_enthalpies(
&self,
_state: &SystemState,
) -> Result<Vec<entropyk_core::Enthalpy>, ComponentError> {
Ok(vec![self.port.enthalpy()])
}
fn energy_transfers(&self, _state: &SystemState) -> Option<(Power, Power)> {
// Sink: pas de transfert actif
Some((Power::from_watts(0.0), Power::from_watts(0.0)))
}
}
Fichiers à modifier:
crates/components/src/flow_boundary.rs
Critères d'acceptation:
FlowSourceetFlowSinkimplémentent les 3 méthodes- Tests unitaires associés passent
check_energy_balance()ne skip plus ces composants
Story 9.5: Complétion Epic 7 - FlowSplitter/FlowMerger Energy Methods
Priorité: P1-CRITIQUE
Estimation: 4h
Dépendances: Story 9.2
Story:
En tant que moteur de simulation thermodynamique,
Je veux queFlowSplitteretFlowMergerimplémententenergy_transfers()etport_enthalpies(),
Afin que les jonctions soient correctement prises en compte dans le bilan énergétique.
Problème actuel:
FlowSplitteretFlowMergerimplémentent seulementport_mass_flows()- Les jonctions sont ignorées dans la validation
Solution proposée:
// Dans crates/components/src/flow_junction.rs
impl Component for FlowSplitter {
// ... existing code ...
fn port_enthalpies(
&self,
_state: &SystemState,
) -> Result<Vec<entropyk_core::Enthalpy>, ComponentError> {
// Ordre: inlet, puis outlets
let mut enthalpies = vec![self.port_inlet.enthalpy()];
for outlet in &self.ports_outlet {
enthalpies.push(outlet.enthalpy());
}
Ok(enthalpies)
}
fn energy_transfers(&self, _state: &SystemState) -> Option<(Power, Power)> {
// Jonction adiabatique: Q=0, W=0
Some((Power::from_watts(0.0), Power::from_watts(0.0)))
}
}
impl Component for FlowMerger {
// ... existing code ...
fn port_enthalpies(
&self,
_state: &SystemState,
) -> Result<Vec<entropyk_core::Enthalpy>, ComponentError> {
// Ordre: inlets, puis outlet
let mut enthalpies = Vec::new();
for inlet in &self.ports_inlet {
enthalpies.push(inlet.enthalpy());
}
enthalpies.push(self.port_outlet.enthalpy());
Ok(enthalpies)
}
fn energy_transfers(&self, _state: &SystemState) -> Option<(Power, Power)> {
// Jonction adiabatique: Q=0, W=0
Some((Power::from_watts(0.0), Power::from_watts(0.0)))
}
}
Fichiers à modifier:
crates/components/src/flow_junction.rs
Critères d'acceptation:
FlowSplitteretFlowMergerimplémentent les 3 méthodes- Tests unitaires associés passent
check_energy_balance()ne skip plus ces composants
Story 9.6: Amélioration Logging Validation Énergie
Priorité: P2-IMPORTANTE
Estimation: 1h
Dépendances: Stories 9.3, 9.4, 9.5
Story:
En tant que développeur debuggant une simulation,
Je veux un avertissement explicite quand des composants sont ignorés dans la validation énergétique,
Afin d'identifier rapidement les implémentations manquantes.
Problème actuel:
// crates/solver/src/system.rs:1873-1879
_ => {
components_skipped += 1;
tracing::debug!( // ← Niveau DEBUG, pas WARNING
node_index = node_idx.index(),
"Component lacks full energy transfer or enthalpy data - skipping energy balance check"
);
}
Solution proposée:
_ => {
components_skipped += 1;
tracing::warn!(
node_index = node_idx.index(),
component_type = std::any::type_name_of_val(component),
"Component lacks energy_transfers() or port_enthalpies() - SKIPPED in energy balance validation"
);
}
Fichiers à modifier:
crates/solver/src/system.rs
Critères d'acceptation:
- Logging au niveau WARN (pas DEBUG)
- Inclut le type du composant dans le message
- Test que le warning est bien émis
Story 9.7: Refactoring Solver - Scinder solver.rs
Priorité: P3-AMÉLIORATION
Estimation: 4h
Dépendances: Aucune
Story:
En tant que développeur maintenant le code,
Je veux que les stratégies de solver soient dans des fichiers séparés,
Afin d'améliorer la maintenabilité du code.
Problème actuel:
solver.rsfait ~2800 lignes- Architecture.md spécifie
strategies/newton_raphson.rs,strategies/sequential_substitution.rs,strategies/fallback.rs
Solution proposée:
crates/solver/src/
├── lib.rs
├── solver.rs # Trait Solver, SolverStrategy enum
├── strategies/
│ ├── mod.rs
│ ├── newton_raphson.rs
│ ├── sequential_substitution.rs
│ └── fallback.rs
├── system.rs
├── jacobian.rs
└── ...
Fichiers à créer/modifier:
crates/solver/src/strategies/mod.rs(nouveau)crates/solver/src/strategies/newton_raphson.rs(nouveau)crates/solver/src/strategies/sequential_substitution.rs(nouveau)crates/solver/src/strategies/fallback.rs(nouveau)crates/solver/src/solver.rs(réduit)crates/solver/src/lib.rs(mise à jour exports)
Critères d'acceptation:
- Chaque fichier < 500 lignes
cargo test --workspacepasse- API publique inchangée
Story 9.8: Création SystemState Struct Dédié
Priorité: P3-AMÉLIORATION
Estimation: 6h
Dépendances: Stories 9.1, 9.2
Story:
En tant que développeur Rust,
Je veux un structSystemStatedédié au lieu d'un type alias,
Afin d'avoir une validation du layout et une meilleure sémantique.
Problème actuel:
// crates/components/src/lib.rs:180
pub type SystemState = Vec<f64>;
Solution proposée:
// crates/core/src/state.rs (nouveau fichier)
/// État du système thermodynamique.
///
/// Layout: [P_edge0, h_edge0, P_edge1, h_edge1, ...]
/// - P: Pression en Pascals
/// - h: Enthalpie en J/kg
#[derive(Debug, Clone)]
pub struct SystemState {
data: Vec<f64>,
edge_count: usize,
}
impl SystemState {
pub fn new(edge_count: usize) -> Self {
Self {
data: vec![0.0; edge_count * 2],
edge_count,
}
}
pub fn pressure(&self, edge_idx: usize) -> Option<Pressure> {
self.data.get(edge_idx * 2).map(|&p| Pressure::from_pascals(p))
}
pub fn enthalpy(&self, edge_idx: usize) -> Option<Enthalpy> {
self.data.get(edge_idx * 2 + 1).map(|&h| Enthalpy::from_joules_per_kg(h))
}
pub fn set_pressure(&mut self, edge_idx: usize, p: Pressure) {
if let Some(slot) = self.data.get_mut(edge_idx * 2) {
*slot = p.to_pascals();
}
}
pub fn set_enthalpy(&mut self, edge_idx: usize, h: Enthalpy) {
if let Some(slot) = self.data.get_mut(edge_idx * 2 + 1) {
*slot = h.to_joules_per_kg();
}
}
pub fn as_slice(&self) -> &[f64] {
&self.data
}
pub fn as_mut_slice(&mut self) -> &mut [f64] {
&mut self.data
}
}
Critères d'acceptation:
- Struct
SystemStateavec méthodes d'accès typées - Migration progressive (compatibilité avec
AsRef<[f64]>) - Tests unitaires pour accès par edge
Plan d'Exécution Recommandé
Sprint 1: Corrections Critiques (Semaine 1)
| Jour | Story | Durée |
|---|---|---|
| Lundi AM | 9.1 CircuitId Unification | 2h |
| Lundi PM | 9.2 FluidId Unification | 2h |
| Mardi AM | 9.3 ExpansionValve Energy | 3h |
| Mardi PM | 9.4 FlowSource/FlowSink Energy | 3h |
| Mercredi AM | 9.5 FlowSplitter/FlowMerger Energy | 4h |
| Mercredi PM | 9.6 Logging Improvement | 1h |
| Jeudi | Tests d'intégration complets | 4h |
| Vendredi | Code review & documentation | 4h |
Sprint 2: Améliorations (Semaine 2)
| Jour | Story | Durée |
|---|---|---|
| Lundi-Mardi | 9.7 Solver Refactoring | 4h |
| Mercredi-Vendredi | 9.8 SystemState Struct | 6h |
Tests de Validation Finale
Après implémentation de toutes les stories, exécuter:
# 1. Tests unitaires complets
cargo test --workspace
# 2. Tests d'intégration thermodynamique
cargo test --test refrigeration_cycle_integration
cargo test --test mass_balance_integration
# 3. Validation clippy stricte
cargo clippy -- -D warnings
# 4. Benchmark performance
cargo bench
# 5. Test de simulation complète
cargo run --example simple_cycle
Métriques de Succès
| Métrique | Avant | Après |
|---|---|---|
| Types duplicats | 2 (CircuitId, FluidId) |
0 |
Composants sans energy_transfers() |
5 | 0 |
Composants sans port_enthalpies() |
5 | 0 |
Lignes dans solver.rs |
~2800 | ~500 |
| Couverture validation énergie | Partielle | Complète |
Annexes
A. Liste Complète des Composants et Méthodes
| Composant | port_mass_flows() |
port_enthalpies() |
energy_transfers() |
|---|---|---|---|
| Compressor | ✅ | ✅ | ✅ |
| ExpansionValve | ✅ | ❌ → ✅ | ❌ → ✅ |
| Pipe | ✅ | ✅ | ✅ |
| Pump | ✅ | ✅ | ✅ |
| Fan | ✅ | ✅ | ✅ |
| FlowSource | ✅ | ❌ → ✅ | ❌ → ✅ |
| FlowSink | ✅ | ❌ → ✅ | ❌ → ✅ |
| FlowSplitter | ✅ | ❌ → ✅ | ❌ → ✅ |
| FlowMerger | ✅ | ❌ → ✅ | ❌ → ✅ |
| HeatExchanger | ✅ | ✅ | ✅ |
| Evaporator | ✅ | ✅ | ✅ |
| Condenser | ✅ | ✅ | ✅ |
| Economizer | ✅ | ✅ | ✅ |
| EvaporatorCoil | ✅ | ✅ | ✅ |
| CondenserCoil | ✅ | ✅ | ✅ |
B. Références Architecture
- Architecture.md - Component Model
- Architecture.md - Error Handling
- Architecture.md - Project Structure
- PRD - FR35-FR39 Validation Requirements
Document généré par audit automatique - 2026-02-22