6.7 KiB
Raw Permalink Blame History

Code Review Report Story 2.7: Incompressible Fluids Support

Date: 2026-02-15
Story: 2-7-incompressible-fluids-support
Status avant review: review
Fichiers concernés: crates/fluids/src/incompressible.rs (nouveau), crates/fluids/src/lib.rs (modifié)


Git vs Story

  • Story File List: incompressible.rs (new), lib.rs (modified)
  • Git: crates/fluids/ non suivi (??) pas de commit
  • Écart: Aucun la story ne prétend pas que types.rs a été modifié dans le File List final

Résumé des findings

Sévérité Nombre
HIGH 1
MEDIUM 4
LOW 3
Total 8

🔴 HIGH

1. AC #2 non conforme tolérance 1 % au lieu de 0.1 %

AC #2: « Results match reference data (IAPWS-IF97 for water, ASHRAE for glycol) within 0.1% »

Implémentation: Les tests utilisent une tolérance de 1 % (< 0.01), pas 0.1 % (< 0.001).

Fichier: crates/fluids/src/incompressible.rs
Lignes: 355356, 371

assert!((rho_20 - 998.2).abs() / 998.2 < 0.01, "rho_20={}", rho_20);  // 1% ≠ 0.1%
assert!((cp - 4182.0).abs() / 4182.0 < 0.01, "Cp={}", cp);            // 1% ≠ 0.1%

Correction: Remplacer 0.01 par 0.001 dans les assertions de précision, ou ajuster les modèles pour atteindre 0.1 % si nécessaire.


🟡 MEDIUM

2. Tests requis manquants (story § Testing Requirements)

Story: Les tests suivants sont listés comme requis mais absents :

  • test_water_enthalpy_reference h(T) par rapport à 0°C
  • test_glycol_concentration_effect propriétés vs concentration (seul EG30 > water est testé)
  • test_glycol_out_of_range température hors plage pour glycol
  • test_humid_air_psychrometrics enthalpie vs formule psychrométrique
  • test_performance_vs_coolprop benchmark 1000× (optionnel selon story)

Fichier: crates/fluids/src/incompressible.rs
Lignes: 302437 (section tests)

Correction: Ajouter au minimum test_water_enthalpy_reference, test_glycol_concentration_effect et test_glycol_out_of_range.


3. Phase incorrecte pour HumidAir

Implémentation: phase() retourne Phase::Liquid pour tous les fluides, y compris HumidAir.

Problème: HumidAir est un mélange gazeux, pas un liquide.

Fichier: crates/fluids/src/incompressible.rs
Lignes: 280286

fn phase(&self, fluid: FluidId, _state: ThermoState) -> FluidResult<Phase> {
    if IncompFluid::from_fluid_id(&fluid).is_some() {
        Ok(Phase::Liquid)  // HumidAir devrait être Vapor
    } else {
        Err(FluidError::UnknownFluid { fluid: fluid.0 })
    }
}

Correction: Retourner Phase::Vapor pour HumidAir, Phase::Liquid pour les autres.


4. Pas de validation NaN/Inf pour la température

Implémentation: Aucune vérification que t_k est fini (non NaN, non Inf).

Problème: Si t_k = f64::NAN, les comparaisons t_k < min_t || t_k > max_t sont fausses, la validation passe et les polynômes renvoient NaN.

Fichier: crates/fluids/src/incompressible.rs
Lignes: 129132, 161166, 218225

Correction: Ajouter en début de chaque property_* :

if !t_k.is_finite() {
    return Err(FluidError::InvalidState {
        reason: format!("Temperature {} K is not finite", t_k),
    });
}

5. critical_point pour fluides non supportés

Implémentation: critical_point() retourne toujours NoCriticalPoint pour tout FluidId.

Problème: Pour un fluide non supporté (ex. "R134a"), le backend retourne NoCriticalPoint alors que R134a a un point critique. Cohérence avec property() qui retourne UnknownFluid.

Fichier: crates/fluids/src/incompressible.rs
Lignes: 271273

Correction: Vérifier si le fluide est supporté et retourner UnknownFluid si non :

fn critical_point(&self, fluid: FluidId) -> FluidResult<CriticalPoint> {
    if IncompFluid::from_fluid_id(&fluid).is_none() {
        return Err(FluidError::UnknownFluid { fluid: fluid.0 });
    }
    Err(FluidError::NoCriticalPoint { fluid: fluid.0 })
}

🟢 LOW

6. ValidRange défini mais non utilisé

Implémentation: ValidRange est défini et exporté, mais le code utilise des tuples (min_t, max_t) via valid_temp_range().

Fichier: crates/fluids/src/incompressible.rs
Lignes: 7993

Correction: Soit utiliser ValidRange dans les méthodes property_*, soit le retirer sil nest pas nécessaire.


7. Formule de viscosité glycol redondante

Implémentation: conc_factor = (1.0 + 10.0 * concentration).ln().exp() est équivalent à 1.0 + 10.0 * concentration.

Fichier: crates/fluids/src/incompressible.rs
Ligne: 198

Correction: Simplifier en let conc_factor = 1.0 + 10.0 * concentration;


8. Story File List vs Technical Requirements

Story: « Modified files » inclut types.rs (Add IncompressibleFluid enum to FluidId), mais le File List final ne mentionne que incompressible.rs et lib.rs.

Constat: Limplémentation a mis IncompFluid dans incompressible.rs au lieu de types.rs. Cest cohérent et acceptable.

Correction: Mettre à jour la section « Modified files » de la story pour retirer types.rs et refléter la structure réelle.


Synthèse

# Sévérité Description
1 HIGH Tolérance tests 1 % au lieu de 0.1 % (AC #2)
2 MEDIUM Tests requis manquants
3 MEDIUM Phase HumidAir = Liquid au lieu de Vapor
4 MEDIUM Pas de validation NaN/Inf pour t_k
5 MEDIUM critical_point ne distingue pas fluide inconnu
6 LOW ValidRange non utilisé
7 LOW Formule viscosité glycol redondante
8 LOW Incohérence story File List vs Technical Requirements

Recommandation

  • Corriger les points HIGH et MEDIUM avant de passer la story en done.
  • Les points LOW peuvent être traités dans un suivi ultérieur.

Corrections appliquées (2026-02-15)

# Correction
1 Tolérance 0.01→0.001 ; polynôme densité eau recalibré (1001.7 - 0.107T - 0.00333T²)
2 Tests ajoutés : test_water_enthalpy_reference, test_glycol_concentration_effect, test_glycol_out_of_range, test_humid_air_psychrometrics, test_phase_humid_air_is_vapor, test_critical_point_unknown_fluid, test_nan_temperature_rejected
3 phase() retourne Phase::Vapor pour HumidAir
4 Validation t_k.is_finite() dans property_water, property_glycol, property_humid_air
5 critical_point() retourne UnknownFluid pour fluides non supportés
7 conc_factor simplifié : (1+10c).ln().exp() → 1+10c