# 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 type `CircuitId` unique et cohérent,  
> Afin d'éviter les erreurs de compilation lors de l'utilisation conjointe des modules solver et components.

**Problème actuel:**
```rust
// 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:**
1. Garder `CircuitId(u8)` dans `crates/core/src/types.rs` pour performance
2. Ajouter `impl From<&str> for CircuitId` avec hash interne
3. Supprimer `CircuitId` de `state_machine.rs`
4. 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 `CircuitId` dans la codebase
- [ ] Conversion `From<&str>` et `From<u8>` disponibles
- [ ] `cargo test --workspace` passe
- [ ] `cargo clippy -- -D warnings` passe

---

#### 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 type `FluidId` unique avec API cohérente,  
> Afin d'éviter la confusion entre `fluids::FluidId` et `components::port::FluidId`.

**Problème actuel:**
```rust
// 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:**
1. Garder `FluidId` dans `crates/fluids/src/types.rs` comme source unique
2. Exposer `as_str()` et garder champ public pour compatibilité
3. Supprimer `FluidId` de `port.rs`
4. Ré-importer depuis `entropyk_fluids`

**Fichiers à modifier:**
- `crates/fluids/src/types.rs` (ajout méthode `as_str()`)
- `crates/components/src/port.rs` (suppression, ré-import)
- `crates/components/src/lib.rs` (mise à jour exports)

**Critères d'acceptation:**
- [ ] Un seul `FluidId` dans la codebase
- [ ] Méthode `as_str()` disponible
- [ ] Champ `0` accessible pour compatibilité
- [ ] `cargo test --workspace` passe

---

#### 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 que `ExpansionValve` implémente `energy_transfers()` et `port_enthalpies()`,  
> Afin que le bilan énergétique soit correctement validé pour les cycles frigorifiques.

**Problème actuel:**
- `ExpansionValve` implémente seulement `port_mass_flows()`
- Le détendeur est **ignoré** dans `check_energy_balance()`
- Or, c'est un composant critique de tout cycle frigorifique

**Solution proposée:**

```rust
// 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_balance` passe
- [ ] `check_energy_balance()` ne skip plus `ExpansionValve`

---

#### Story 9.4: Complétion Epic 7 - RefrigerantSource/RefrigerantSink Energy Methods

**Priorité:** P1-CRITIQUE  
**Estimation:** 3h  
**Dépendances:** Story 9.2

**Story:**
> En tant que moteur de simulation thermodynamique,  
> Je veux que `RefrigerantSource` et `RefrigerantSink` implémentent `energy_transfers()` et `port_enthalpies()`,  
> Afin que les conditions aux limites soient correctement prises en compte dans le bilan énergétique.

**Problème actuel:**
- `RefrigerantSource` et `RefrigerantSink` implémentent seulement `port_mass_flows()`
- Ces composants sont ignorés dans la validation

**Solution proposée:**

```rust
// Dans crates/components/src/refrigerant_boundary.rs

impl Component for RefrigerantSource {
    // ... 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 RefrigerantSink {
    // ... 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/refrigerant_boundary.rs`

**Critères d'acceptation:**
- [ ] `RefrigerantSource` et `RefrigerantSink` implé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 que `FlowSplitter` et `FlowMerger` implémentent `energy_transfers()` et `port_enthalpies()`,  
> Afin que les jonctions soient correctement prises en compte dans le bilan énergétique.

**Problème actuel:**
- `FlowSplitter` et `FlowMerger` implémentent seulement `port_mass_flows()`
- Les jonctions sont ignorées dans la validation

**Solution proposée:**

```rust
// 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:**
- [ ] `FlowSplitter` et `FlowMerger` implé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:**
```rust
// 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:**
```rust
_ => {
    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.rs` fait ~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 --workspace` passe
- [ ] 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 struct `SystemState` dédié au lieu d'un type alias,  
> Afin d'avoir une validation du layout et une meilleure sémantique.

**Problème actuel:**
```rust
// crates/components/src/lib.rs:180
pub type SystemState = Vec<f64>;
```

**Solution proposée:**
```rust
// 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 `SystemState` avec 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 RefrigerantSource/RefrigerantSink 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:

```bash
# 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 | ✅ | ✅ | ✅ |
| RefrigerantSource | ✅ | ❌ → ✅ | ❌ → ✅ |
| RefrigerantSink | ✅ | ❌ → ✅ | ❌ → ✅ |
| FlowSplitter | ✅ | ❌ → ✅ | ❌ → ✅ |
| FlowMerger | ✅ | ❌ → ✅ | ❌ → ✅ |
| HeatExchanger | ✅ | ✅ | ✅ |
| Evaporator | ✅ | ✅ | ✅ |
| Condenser | ✅ | ✅ | ✅ |
| Economizer | ✅ | ✅ | ✅ |
| EvaporatorCoil | ✅ | ✅ | ✅ |
| CondenserCoil | ✅ | ✅ | ✅ |

### B. Références Architecture

- [Architecture.md - Component Model](../planning-artifacts/architecture.md#Component-Model)
- [Architecture.md - Error Handling](../planning-artifacts/architecture.md#Error-Handling-Strategy)
- [Architecture.md - Project Structure](../planning-artifacts/architecture.md#Project-Structure)
- [PRD - FR35-FR39 Validation Requirements](../planning-artifacts/prd.md#Validation)

---

*Document généré par audit automatique - 2026-02-22*
