Sepehr 1fdfefe631 Initial commit: BMAD framework + Story 1.1 Component Trait Definition
Features:
- BMAD (Build Modular AI-driven Development) framework setup
- BMM, BMB, CIS, Core modules configured
- Story 1.1: Component trait with error handling
- Workspace Cargo.toml with components crate
- 31 tests passing (19 unit + 12 doc tests)

Technical:
- Component trait with compute_residuals, jacobian_entries, n_equations
- ComponentError enum with thiserror
- JacobianBuilder for sparse matrix construction
- Object-safe trait supporting Box<dyn Component>
- Comprehensive documentation and examples
2026-02-14 13:54:04 +01:00

37 KiB

stepsCompleted inputDocuments workflowType project_name user_name date lastStep status completedAt
1
2
3
4
5
6
7
8
/Users/sepehr/dev/Entropyk/_bmad-output/planning-artifacts/prd.md
/Users/sepehr/dev/Entropyk/_bmad-output/planning-artifacts/product-brief-Entropyk-2026-02-12.md
architecture Entropyk Sepehr 2026-02-12 8 complete 2026-02-12

Architecture Decision Document

This document builds collaboratively through step-by-step discovery. Sections are appended as we work through each architectural decision together.

Input Context

Documents Referenced

  • PRD: Product Requirements Document with 42 FRs, 17 NFRs, 5 personas
  • Product Brief: Core vision and MVP scope definition

Key Architectural Drivers from PRD

Performance Requirements:

  • Steady State convergence < 1 second
  • Simple cycle < 100ms
  • Hard real-time guarantees for HIL (Hardware-in-the-Loop)
  • No dynamic allocation in solver loop

Innovation Areas:

  1. Solver-Agnostic Architecture with Intelligent Fallback
  2. Multi-Circuit Topology with Thermal Coupling
  3. Zero-Panic Policy + No-Allocation guarantees
  4. Native Inverse Control via Residual Embedding
  5. WebAssembly support for browser interfaces

Technical Stack:

  • Rust native (core)
  • Python bindings (PyO3)
  • C FFI (cbindgen)
  • WebAssembly (wasm-bindgen)

Document initialized. Ready for collaborative architectural decision making.


Project Context Analysis

Requirements Overview

Functional Requirements (42 FRs): The project requires a thermodynamic simulation library with:

  • Component modeling (Compressor AHRI 540, Condenser, Expansion Valve, Evaporator, Economizer)
  • Multi-circuit topology system with thermal coupling between circuits
  • Dual solver strategy (Newton-Raphson + Sequential Substitution with automatic fallback)
  • Inverse Control framework for control-oriented simulations
  • Multi-backend fluid properties (CoolProp, tabular interpolation)
  • Comprehensive API suite (Rust native, Python PyO3, C FFI, WebAssembly)

Non-Functional Requirements (17 NFRs):

  • Performance: Steady state < 1s, simple cycle < 100ms, HIL latency < 20ms
  • Reliability: Zero-panic policy, 48h+ continuous operation, memory safety guaranteed
  • Determinism: Same inputs → same results (1e-9 precision) across all platforms
  • No dynamic allocation in solver loop (pre-allocated only)

Scale & Complexity:

  • Primary domain: Scientific computing library (thermodynamic cycles)
  • Complexity level: High (solver-agnostic architecture, real-time constraints, multi-target compilation)
  • Target platforms: Native (x86/ARM), WebAssembly (browser), Python ecosystem, C/PLC integration
  • Estimated architectural components: 8-12 major subsystems

Technical Constraints & Dependencies

Hard Constraints:

  • CoolProp integration for fluid properties (or fallback to tabular data)
  • AHRI 540 compliance for compressor modeling
  • Cross-platform determinism (x86, ARM, WASM)
  • Memory safety through Rust ownership system
  • No garbage collection (deterministic performance)

Dependencies:

  • CoolProp C++ library ( thermodynamic properties )
  • nalgebra (linear algebra for Newton-Raphson)
  • petgraph (graph topology representation)
  • PyO3 (Python bindings)
  • wasm-bindgen (WebAssembly target)
  • cbindgen (C FFI headers)

Cross-Cutting Concerns Identified

  1. Solver Abstraction: Uniform interface for Sequential Substitution and Newton-Raphson
  2. Error Handling Strategy: Result<T, ThermoError> throughout, zero-panic guarantee
  3. Memory Management: Pre-allocation strategy, no heap allocation in hot paths
  4. API Consistency: Rust/Python/C/WASM APIs must remain synchronized
  5. Validation & Verification: Mass/energy balance checks, benchmark regression testing
  6. Documentation: Physics manual + API docs + Architecture Decision Records

Starter Template Evaluation

Primary Technology Domain

Rust Scientific Library with multi-target compilation (Native, Python, C, WebAssembly)

Based on project requirements analysis: thermodynamic cycle simulation requiring deterministic performance, memory safety, and cross-platform bindings.

Selected Architecture: Workspace-Based Multi-Crate

Rationale for Selection:

  • Separation of concerns: core logic isolated from bindings
  • Independent versioning per crate
  • Faster compilation (parallel crate builds)
  • Clean API boundaries for FFI safety

Workspace Structure:

entropyk/
├── Cargo.toml              # Workspace root
├── crates/
│   ├── core/               # Logique métier pure
│   ├── solver/             # Moteurs de résolution (Newton-Raphson, Picard)
│   ├── components/         # Modèles de composants (AHRI 540, etc.)
│   └── fluids/             # Propriétés thermodynamiques + coolprop-sys
├── bindings/
│   ├── python/             # PyO3 bindings (Maturin)
│   ├── c/                  # FFI C (cbindgen)
│   └── wasm/               # WebAssembly (wasm-bindgen)
├── benches/                # Benchmarks criterion.rs
└── docs/                   # mdBook documentation

Initialization Commands

# Initialisation Workspace
cargo new entropyk --lib
# (Configuration manuelle: Cargo.toml workspace avec members = ["crates/*", "bindings/*"])

# Dépendances Core (Maths, Graphe, Observabilité)
cargo add nalgebra petgraph thiserror serde tracing tracing-subscriber

# Dépendances Dev (Tests Scientifiques)
cargo add --dev criterion approx

Architectural Decisions Provided by Structure

Language & Runtime:

  • Rust Edition 2021 with strict compiler settings
  • #![deny(warnings)] pour qualité code
  • Zero-cost abstractions garanties

C++ Integration (CoolProp):

  • sys-crate pattern: crates/fluids/coolprop-sys/
  • build.rs gère compilation statique CoolProp C++
  • Linking statique pour distribution simplifiée
  • Wrappers Rust safe au-dessus du FFI brut

Observability Strategy:

  • tracing pour structured logging (pas de println!)
  • Spans pour suivre convergence solver
  • Compatible avec subscriber Python (pyo3-log) et WASM (console_error_panic_hook)
  • Niveaux: ERROR (divergence), INFO (convergence), DEBUG (résidus), TRACE (Jacobien)

Testing Framework:

  • approx pour assertions flottantes (assert_relative_eq!)
  • Tolérance par défaut: 1e-6 (matching NFR bilans énergétiques)
  • criterion.rs pour benchmarks de régression performance
  • Tests de validation contre données AHRI certifiées

Code Organization:

  • Traits pour abstraction solver (Solver trait)
  • Type-state pattern pour sécurité thermodynamique
  • Zero-allocation en hot path (allocation pré-calculée)
  • Error types exhaustifs (pas de panics)

Development Experience:

  • cargo test : Tests unitaires + validation scientifique
  • cargo bench : Suivi performance (régression < 5% = échec CI)
  • cargo clippy -- -D warnings : Tolérance zéro style
  • miri test : Détection undefined behavior
  • valgrind : Vérification fuites mémoire FFI

Core Architectural Decisions

Decision Priority Analysis

Critical Decisions (Block Implementation):

  1. Solver Architecture Pattern
  2. Component Model Design
  3. Fluid Properties Backend Abstraction
  4. Error Handling Strategy

Important Decisions (Shape Architecture):

  • Graph topology representation
  • Inverse Control implementation pattern
  • Multi-binding API consistency

Deferred Decisions (Post-MVP):

  • Advanced optimization strategies
  • Parallel execution (Rayon integration)
  • WebAssembly-specific optimizations

Solver Architecture

Decision: Trait-based static polymorphism with enum dispatch

Pattern:

enum SolverStrategy {
    NewtonRaphson(NewtonConfig),
    SequentialSubstitution(PicardConfig),
}

impl SolverStrategy {
    fn solve(&self, system: &mut System) -> Result<ConvergedState, SolverError> {
        match self {
            NewtonRaphson(cfg) => newton_solve(system, cfg),
            SequentialSubstitution(cfg) => picard_solve(system, cfg),
        }
    }
}

Rationale:

  • Zero-cost abstraction via monomorphisation (performance critique < 1s)
  • Enum allows runtime fallback without vtable overhead
  • Type-safe solver switching (Newton diverge → fallback Picard)
  • No allocation in solve loop (pre-allocated buffers)

Fallback Strategy:

pub fn solve_with_fallback(system: &mut System) -> Result<ConvergedState, SolverError> {
    let mut strategy = SolverStrategy::NewtonRaphson(NewtonConfig::default());
    
    match strategy.solve(system) {
        Ok(state) => return Ok(state),
        Err(SolverError::Divergence) => {
            tracing::warn!("Newton-Raphson diverged, switching to Sequential Substitution");
            strategy = SolverStrategy::SequentialSubstitution(PicardConfig::default());
            strategy.solve(system)
        }
        Err(e) => Err(e),
    }
}

Component Model

Decision: Trait-based with Type-State pattern

Core Trait:

trait Component {
    fn compute_residuals(&self, state: &SystemState, residuals: &mut ResidualVector);
    fn jacobian_entries(&self, state: &SystemState, jacobian: &mut JacobianBuilder);
    fn n_equations(&self) -> usize;
}

Type-State for Connection Safety:

struct Compressor<State> { port_suction: Port<State>, port_discharge: Port<State> }
struct Disconnected; struct Connected;

impl Compressor<Disconnected> {
    fn connect(self, suction: NodeId, discharge: NodeId) -> Compressor<Connected> { ... }
}

Rationale:

  • Compile-time validation des connexions (pas de runtime errors)
  • Residual embedding natif pour Inverse Control
  • Extensible pour composants custom (e.g., Ejecteur de Robert)
  • AHRI 540 coefficients intégrés dans struct Compressor

Fluid Properties Backend

Decision: Trait abstraction with multiple backends

Architecture:

trait FluidBackend {
    fn property(&self, fluid: FluidId, property: Property, state: ThermoState) 
        -> Result<f64, FluidError>;
    fn critical_point(&self, fluid: FluidId) -> Result<CriticalPoint, FluidError>;
}

struct CoolPropBackend { /* sys-crate wrapper */ }
struct TabularBackend { /* NIST tables with interpolation */ }
struct TestBackend { /* mocks for unit tests */ }

Caching Strategy:

  • LRU cache dans backends (avoid redundant CoolProp calls)
  • Cache invalidation on temperature/pressure changes
  • Thread-safe (Arc<Mutex>) pour parallélisation future

Critical Point Handling (CO2 R744):

fn property_with_damping(&self, state: ThermoState) -> Result<f64, FluidError> {
    if self.near_critical_point(state) {
        // Damping automatique pour éviter NaN dans dérivées
        self.compute_with_damping(state)
    } else {
        self.property(state)
    }
}

Rationale:

  • Fallback CoolProp → Tabular si CoolProp instable
  • Mock backend pour tests (pas de dépendance C++ en CI)
  • Cache pour atteindre objectif 100x plus rapide que Python
  • Gestion robuste point critique (divergences classiques)

Error Handling Strategy

Decision: Centralized error enum with thiserror

Hierarchy:

#[derive(Error, Debug)]
pub enum ThermoError {
    #[error("Solver failed to converge after {iterations} iterations")]
    NonConvergence { iterations: usize, final_residual: f64 },
    
    #[error("Fluid property calculation failed: {0}")]
    FluidProperty(#[from] FluidError),
    
    #[error("Invalid thermodynamic state: {reason}")]
    InvalidState { reason: String },
    
    #[error("Solver timeout: exceeded {timeout_ms}ms")]
    Timeout { timeout_ms: u64 },
    
    #[error("Control saturation: variable {var} hit bounds [{min}, {max}]")]
    ControlSaturation { var: String, min: f64, max: f64 },
    
    #[error("Validation failed: mass balance error {mass_error:.2e} kg/s")]
    Validation { mass_error: f64, energy_error: f64 },
}

pub type ThermoResult<T> = Result<T, ThermoError>;

Zero-Panic Policy Enforcement:

  • #![cfg_attr(not(test), deny(panic))] en release
  • All operations return Result (pas de unwrap/expect sauf tests)
  • Miri validation en CI pour undefined behavior

Degradation Strategy:

match solver.solve_with_timeout(system, Duration::from_millis(1000)) {
    Ok(state) => ComputationStatus::Converged(state),
    Err(ThermoError::Timeout { .. }) => {
        tracing::warn!("Solver timeout - returning last known state");
        ComputationStatus::Timeout(system.last_known_state())
    }
    Err(e) => return Err(e),
}

Rationale:

  • Erreurs exhaustives pour debugging solver
  • Distinction timeout vs non-convergence vs saturation
  • Compatible avec bindings Python (PyO3 exception mapping)
  • Traçabilité via tracing spans

Implementation Patterns & Consistency Rules

Pattern Categories Defined

Critical Conflict Points Identified: 7 areas where AI agents could make different choices

Naming Patterns

Rust Naming Conventions:

  • snake_case : modules, fonctions, variables
  • CamelCase : types, traits, enum variants
  • SCREAMING_SNAKE_CASE : constantes
  • NO prefix I for traits (just Solver, not ISolver)
  • NO prefix E for enums (just Error, not EError)

Examples:

// Correct
pub struct Compressor { ... }
pub trait Solver { ... }
pub fn compute_residuals() { ... }

// Incorrect
pub struct compressor { ... }  // lowercase struct
pub trait ISolver { ... }       // I-prefix

Structure Patterns

Project Organization:

  • Feature-based organization: crates/solver/src/strategies/newton.rs
  • Tests co-localisés: newton.rs + newton/tests.rs (inline)
  • Pas de modules "flat" - hiérarchie logique par domaine physique

Format Patterns

API Response Formats:

  • Rust: Result<T, ThermoError> avec thiserror
  • Python: PyErr via PyO3 (exceptions Python natives)
  • WASM: Result<T, JsError> avec wasm-bindgen

Data Exchange Formats:

  • JSON: camelCase pour compatibilité JavaScript/Python
  • Dates: ISO 8601 strings (pas de timestamps)
  • Nombres: f64 partout (pas de f32 pour éviter perte précision)

Communication Patterns

Event System Patterns:

  • tracing spans pour suivre convergence: span!(Level::INFO, "solver_iteration", iteration = i)
  • Levels: ERROR (divergence), INFO (convergence), DEBUG (résidus), TRACE (Jacobien complet)
  • Pas de println! - jamais

State Management Patterns:

  • Immutable updates uniquement (pas de mutation in-place)
  • Clone explicite pour changements d'état
  • Pre-allocation obligatoire (pas de Vec::new() en hot path)

Process Patterns

Error Handling Patterns:

  • Tous les cas d'erreur retournent Result<T, ThermoError>
  • unwrap() et expect() interdits sauf dans tests/
  • Dégradation gracieuse: timeout → retour état partiel avec warning

Loading State Patterns:

  • Pas applicable (bibliothèque synchrone)
  • Pour bindings async: exposer callbacks/progress via channels

Critical Pattern: NewType for Unit Safety

Pattern Requis: Utiliser des "Tuple Structs" pour l'API publique - jamais de f64 nus

Problème évité:

// DANGER - Ne jamais faire ceci
fn solve(p: f64, t: f64) // Alice peut appeler solve(300.0, 100000.0) - inversion T/P!

Solution:

pub struct Pressure(pub f64);    // Pascal
pub struct Temperature(pub f64); // Kelvin
pub struct Enthalpy(pub f64);    // J/kg
pub struct MassFlow(pub f64);    // kg/s

// API publique type-safe
fn solve(p: Pressure, t: Temperature) -> Result<State, Error> {
    // Impossible de mélanger Bar et Pascal, ou Kelvin et Celsius
}

// Usage
let state = solve(Pressure(101325.0), Temperature(293.15))?;

Rationale:

  • Zero-cost abstraction (compile-time only)
  • Empêche "Optimization Alice" de mélanger unités
  • Clarté API: Pressure est explicite vs f64 ambigu
  • Conversion explicite: pressure_bar.to_pascals() plutôt que * 100000.0

Critical Pattern: LaTeX Configuration for Rustdoc

Pattern Requis: Configuration KaTeX dans .cargo/config.toml pour formules mathématiques

Configuration:

# .cargo/config.toml
[build]
rustdocflags = ["--html-in-header", "docs/katex-header.html"]
<!-- docs/katex-header.html -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js"
    onload="renderMathInElement(document.body);"></script>

Documentation avec formules:

/// Computes compressor work using isentropic efficiency
/// 
/// # Equation
/// $$W = \dot{m} \cdot \frac{h_{out,s} - h_{in}}{\eta_{isen}}$$
/// 
/// Where:
/// - $$W$$ is the power input (W)
/// - $$\dot{m}$$ is the mass flow rate (kg/s)
/// - $$h$$ is the specific enthalpy (J/kg)
/// - $$\eta_{isen}$$ is the isentropic efficiency
pub fn compute_work(&self, inlet: State, outlet: State) -> Power { ... }

Consigne Agent:

"Create a .cargo/config.toml or build.rs that injects the KaTeX script into the generated HTML docs."

Sans cette configuration: La documentation affichera le code LaTeX brut ($$P = \rho R T$$) - illisible.

Scientific Testing Patterns

Pattern Requis: Tolérances explicites avec approx

use approx::assert_relative_eq;

// Tests bilans énergétiques (tolérance 1e-6 kW)
assert_relative_eq!(
    energy_balance_error, 
    0.0, 
    epsilon = 1e-6
);

// Tests bilans de masse (tolérance 1e-9 kg/s)
assert_relative_eq!(
    mass_balance_error,
    0.0,
    epsilon = 1e-9
);

Validation par domaine:

Domaine Tolérance Raison
Bilan énergie 1e-6 kW NFR PRD
Bilan masse 1e-9 kg/s NFR PRD
Convergence pression 1 Pa (1e-5 bar) NFR PRD
Tests généraux 1e-6 Par défaut

Enforcement Guidelines

All AI Agents MUST:

  1. NEVER use bare f64 in public APIs - Always use NewType pattern for physical quantities
  2. NEVER use println! - Use tracing with appropriate levels
  3. NEVER use unwrap or expect in production code - Always return Result
  4. ALWAYS include LaTeX header for mathematical documentation
  5. ALWAYS use approx for float assertions - Never assert_eq! with f64
  6. ALWAYS follow naming conventions - snake_case functions, CamelCase types
  7. ALWAYS pre-allocate - No dynamic allocation in solver hot paths

Pattern Verification:

  • cargo clippy -- -D warnings : Détecte unwrap/expect
  • cargo doc : Vérifie formules KaTeX rendues correctement
  • cargo test : Échoue si assertions flottantes sans approx
  • CI : Échec si patterns violés

Pattern Examples

Good Examples:

// NewType safety
let p = Pressure(101325.0);
let t = Temperature(293.15);
let state = solver.solve(p, t)?;

// Tracing structured
span!(Level::INFO, "solver_convergence", iteration = iter, residual = res);

// Error handling
match result {
    Ok(state) => state,
    Err(ThermoError::NonConvergence { .. }) => {
        tracing::warn!("Fallback to Sequential Substitution");
        fallback_solve(system)?
    }
    Err(e) => return Err(e),
}

Anti-Patterns:

// NEVER: Bare f64
fn solve(p: f64, t: f64) // Ambiguous units!

// NEVER: println!
println!("Converged at iteration {}", iter); // Use tracing::info!

// NEVER: unwrap
let value = result.unwrap(); // Use ? operator or match

// NEVER: assert_eq with floats
assert_eq!(a, b); // Use approx::assert_relative_eq!

Project Structure & Boundaries

Complete Project Directory Structure

entropyk/
├── Cargo.toml                          # Workspace root
├── .cargo/
│   └── config.toml                     # KaTeX header pour docs
├── Cargo.lock                          # Généré
│
├── crates/
│   ├── core/
│   │   ├── Cargo.toml
│   │   └── src/
│   │       ├── lib.rs                  # Re-exports
│   │       ├── types.rs                # NewTypes: Pressure, Temperature...
│   │       ├── state.rs                # FluidState, ThermodynamicState
│   │       └── errors.rs               # ThermoError enum
│   │
│   ├── solver/
│   │   ├── Cargo.toml
│   │   └── src/
│   │       ├── lib.rs
│   │       ├── strategies/
│   │       │   ├── mod.rs
│   │       │   ├── newton_raphson.rs   # FR14: Newton-Raphson
│   │       │   ├── sequential_substitution.rs  # FR15: Picard
│   │       │   └── fallback.rs         # FR16: Auto-fallback
│   │       ├── inverse/
│   │       │   ├── mod.rs
│   │       │   └── control.rs          # FR22-FR24: Inverse Control
│   │       ├── system.rs               # System state management
│   │       └── jacobian.rs             # Jacobian assembly
│   │
│   ├── components/
│   │   ├── Cargo.toml
│   │   └── src/
│   │       ├── lib.rs
│   │       ├── compressor.rs           # FR1: AHRI 540
│   │       ├── condenser.rs            # FR2
│   │       ├── expansion_valve.rs      # FR3: Détendeur
│   │       ├── evaporator.rs           # FR4
│   │       ├── economizer.rs           # FR5
│   │       ├── port.rs                 # FR10: Ports/connexions
│   │       └── state_machine.rs        # FR6-FR8: ON/OFF/BYPASS
│   │
│   └── fluids/
│       ├── Cargo.toml
│       ├── build.rs                    # Compilation CoolProp C++
│       ├── coolprop-sys/               # Sys-crate C++
│       │   ├── Cargo.toml
│       │   ├── build.rs
│       │   └── src/
│       │       └── lib.rs
│       └── src/
│           ├── lib.rs
│           ├── backend.rs              # Trait FluidBackend
│           ├── coolprop.rs             # FR25: CoolProp integration
│           ├── tabular.rs              # FR26: Tables NIST
│           ├── cache.rs                # LRU cache
│           └── damping.rs              # FR29: Critical point
│
├── bindings/
│   ├── python/
│   │   ├── Cargo.toml
│   │   ├── pyproject.toml              # Maturin config
│   │   └── src/
│   │       └── lib.rs                  # PyO3 bindings FR31
│   │
│   ├── c/
│   │   ├── Cargo.toml
│   │   ├── build.rs                    # cbindgen
│   │   └── src/
│   │       └── lib.rs                  # FFI C FR32
│   │
│   └── wasm/
│       ├── Cargo.toml
│       ├── package.json                # npm integration
│       └── src/
│           └── lib.rs                  # wasm-bindgen FR33
│
├── benches/
│   ├── Cargo.toml
│   └── criterion/
│       ├── simple_cycle.rs             # Benchmark cycle simple
│       └── complex_cycle.rs            # Benchmark multi-circuits
│
├── tests/
│   ├── validation/                     # Tests vs données AHRI
│   │   ├── compressor_validation.rs
│   │   └── energy_balance.rs           # FR35-FR36
│   └── integration/
│       └── multi_circuit.rs            # FR9: Multi-circuits
│
├── docs/
│   ├── katex-header.html               # KaTeX config
│   └── src/                            # mdBook source
│       ├── SUMMARY.md
│       ├── physics/
│       │   ├── thermodynamics.md
│       │   └── compressors.md          # AHRI 540 equations
│       └── api/
│           └── quickstart.md
│
└── .github/
    └── workflows/
        ├── ci.yml                      # Tests + clippy + miri
        └── release.yml                 # Multi-platform builds

Architectural Boundaries

API Boundaries:

Public Rust API (crates/core/src/lib.rs):

  • Types: Pressure, Temperature, Enthalpy, MassFlow (NewType pattern)
  • Traits: Component, Solver, FluidBackend
  • Functions: solve(), solve_with_fallback(), inverse_control()

Python Bindings Boundary (bindings/python/src/lib.rs):

  • Expose via #[pyfunction] and #[pymodule]
  • Map ThermoError to Python exceptions via PyErr
  • Type conversions: f64float, structs ↔ dict

C FFI Boundary (bindings/c/src/lib.rs):

  • Expose via extern "C" functions
  • Handle opaque pointers for complex types
  • Error codes via enum ThermoErrorCode

WASM Boundary (bindings/wasm/src/lib.rs):

  • Expose via #[wasm_bindgen]
  • JSON serialization for complex objects
  • Error handling via Result<T, JsError>

Component Boundaries:

Component Trait Interface:

// crates/components/src/lib.rs
pub trait Component {
    fn compute_residuals(&self, state: &SystemState, residuals: &mut ResidualVector);
    fn jacobian_entries(&self, state: &SystemState, jacobian: &mut JacobianBuilder);
    fn n_equations(&self) -> usize;
    fn get_ports(&self) -> &[Port];
}

Communication Pattern:

  • Components only communicate via SystemState (immutable)
  • Residuals written to pre-allocated vectors (no allocation)
  • Jacobian entries assembled by solver

Solver Boundaries:

Solver Trait Interface:

// crates/solver/src/lib.rs
pub trait Solver {
    fn solve(&self, system: &mut System) -> Result<ConvergedState, SolverError>;
    fn with_timeout(self, timeout: Duration) -> Self;
}

Strategy Pattern:

  • NewtonRaphson and SequentialSubstitution implement Solver
  • SolverStrategy enum for runtime selection
  • Fallback logic in solve_with_fallback() coordinator

Data Boundaries:

Fluid Properties:

  • FluidBackend trait abstracts CoolProp vs Tabular
  • Cache layer transparent aux appelants
  • Damping appliqué automatiquement près point critique

State Management:

  • SystemState immutable, cloné à chaque itération
  • ConvergedState résultat final validé
  • Validation bilans masse/énergie avant retour

Requirements to Structure Mapping

Feature/Epic Mapping:

Feature FRs Location
Component Modeling FR1-FR8 crates/components/src/
System Topology FR9-FR13 crates/solver/src/system.rs
Solver Engine FR14-FR21 crates/solver/src/strategies/
Inverse Control FR22-FR24 crates/solver/src/inverse/
Fluid Properties FR25-FR29 crates/fluids/src/
Multi-Platform APIs FR30-FR34 bindings/{python,c,wasm}/
Validation FR35-FR39 tests/validation/

Cross-Cutting Concerns:

Concern Location Implementation
Error Handling crates/core/src/errors.rs ThermoError enum
Type Safety crates/core/src/types.rs NewType pattern
Observability All crates tracing spans
Testing tests/ + inline approx + criterion
Documentation docs/ + rustdoc KaTeX + mdBook

Integration Points

Internal Communication:

Crate Dependencies:

core (no deps)
  ↑
fluids → coolprop-sys (C++ FFI)
  ↑
components → core + fluids
  ↑
solver → core + fluids + components
  ↑
bindings → solver (re-export public API)

Pre-allocated Buffers:

  • Solver alloue buffers une fois au démarrage
  • Composants écrivent dans slices pré-allouées
  • Pas d'allocation dans la boucle de convergence

External Integrations:

CoolProp C++:

  • Sys-crate coolprop-sys avec build.rs
  • Compilation statique CoolProp
  • Wrappers Rust safe au-dessus

Python Ecosystem:

  • Maturin pour build wheels
  • PyPI distribution
  • API compatible tespy (migration facilitée)

HIL Systems:

  • Headers C auto-générés (cbindgen)
  • Compatible PLC/LabView
  • Latence < 20ms garantie

Data Flow:

User Input (Rust/Python/C/WASM)
    ↓
API Boundary (bindings)
    ↓
System Construction (components)
    ↓
Solver Initialization (solver)
    ↓
Fluid Properties (fluids → CoolProp)
    ↓
Residual Computation (components)
    ↓
Jacobian Assembly (solver)
    ↓
Linear Solve (nalgebra)
    ↓
Convergence Check
    ↓
Result + Validation (core)
    ↓
Return to User

File Organization Patterns

Configuration Files:

Root Level:

  • Cargo.toml : Workspace members, dependencies communes
  • .cargo/config.toml : KaTeX pour docs, build flags

Per-Crate:

  • Cargo.toml : Dependencies spécifiques au crate
  • build.rs : Compilation C++ (fluids), génération headers (c)

Source Organization:

Module Pattern:

// lib.rs
pub mod types;
pub mod errors;
pub use types::*;
pub use errors::*;

// types.rs
pub struct Pressure(pub f64);
// ... impls

Feature-Based:

  • components/ : Un fichier par composant physique
  • solver/strategies/ : Un fichier par algorithme
  • fluids/ : Backend + implementations

Test Organization:

Inline Tests:

#[cfg(test)]
mod tests {
    use super::*;
    use approx::assert_relative_eq;
    
    #[test]
    fn test_compressor_efficiency() {
        // Test code
    }
}

Integration Tests:

  • tests/validation/ : Tests vs données certifiées
  • tests/integration/ : Tests scénarios complets
  • benches/criterion/ : Performance benchmarks

Asset Organization:

Documentation:

  • docs/src/ : Contenu mdBook
  • docs/katex-header.html : Configuration Math
  • Généré dans target/doc/ (rustdoc) ou docs/book/ (mdBook)

Development Workflow Integration

Development Build:

cargo build --workspace              # Build tous les crates
cargo test --workspace               # Tests complets
cargo doc --workspace --open         # Documentation locale

CI/CD Pipeline:

  • cargo clippy -- -D warnings : Linting strict
  • cargo test --workspace : Tests unitaires
  • cargo test --test validation : Tests scientifiques
  • cargo bench : Benchmarks performance
  • miri test : Détection undefined behavior
  • cargo doc : Vérification docs + KaTeX

Release Process:

  • Multi-platform builds (Linux, macOS, Windows)
  • Wheels Python (manylinux)
  • WASM package npm
  • Headers C (.h)

Deployment Targets:

Target Output Distribution
Rust entropyk crate crates.io
Python .whl wheels PyPI
C .h + .a/.so GitHub Releases
WASM .wasm + .js npm

Structure validated and ready for implementation.


Architecture Validation Results

Coherence Validation

Decision Compatibility: All architectural decisions work together seamlessly:

  • Workspace multi-crate structure supports separation of solver/components/fluids concerns
  • Trait-based solver with enum dispatch enables zero-cost abstraction + runtime fallback
  • NewType pattern provides compile-time unit safety without runtime overhead
  • Sys-crate CoolProp isolates C++ FFI complexity within fluids/ crate
  • Tracing observability works across all platforms (native/Python/WASM)
  • KaTeX configuration enables scientific documentation with LaTeX math

Pattern Consistency:

  • Naming conventions follow Rust standards (snake_case, CamelCase)
  • NewType pattern (Pressure(f64)) prevents unit mixing errors
  • Error handling unified through ThermoError enum with thiserror
  • Testing uses approx with explicit tolerances (1e-6 energy, 1e-9 mass)
  • Documentation uses KaTeX for mathematical formulas

Structure Alignment:

  • Project structure maps directly to FR categories (components, solver, fluids, bindings)
  • API boundaries clearly defined for Rust/Python/C/WASM
  • Integration points specified (CoolProp FFI, nalgebra linear algebra)
  • Pre-allocation pattern enables zero-dynamic-allocation solver loop

Requirements Coverage Validation

Epic/Feature Coverage: All 5 user personas from PRD are supported:

  • Marie (R&D): Multi-circuit + optimization via solver/ + components/
  • Charlie (Web): WebAssembly bindings in bindings/wasm/
  • Robert (Researcher): Fallback solver + debug verbose via tracing
  • Sarah (HIL): FFI C bindings + deterministic performance
  • David (Batch): Thread-safe design + CLI (future)

Functional Requirements Coverage:

FR Category Count Coverage
FR1-FR8: Components 8 crates/components/src/
FR9-FR13: Topology 5 solver/src/system.rs
FR14-FR21: Solver 8 solver/src/strategies/
FR22-FR24: Inverse Control 3 solver/src/inverse/
FR25-FR29: Fluids 5 fluids/src/
FR30-FR34: APIs 5 bindings/{python,c,wasm}/
FR35-FR42: Validation 8 tests/validation/

Non-Functional Requirements Coverage:

NFR Category Coverage
NFR1-NFR3: Performance Solver trait + pre-allocation
NFR4: No dynamic allocation Pre-allocated buffers pattern
NFR5: Determinism Rust ownership + no GC
NFR6: HIL latency FFI C + deterministic ops
NFR7-NFR10: Zero-Panic ThermoError + Result<T,E>
NFR11-NFR14: Integration Multi-platform bindings
NFR15-NFR17: Maintainability CI/CD + docs + patterns

Implementation Readiness Validation

Decision Completeness:

  • All critical decisions documented with rationale
  • Technology versions specified (nalgebra, petgraph, tracing)
  • Implementation patterns documented with examples
  • Consistency rules established (7 MUST rules)
  • Anti-patterns documented with explanations

Structure Completeness:

  • Complete directory tree defined (all files/directories)
  • API boundaries specified for all platforms
  • Integration points mapped (CoolProp, nalgebra, bindings)
  • Component boundaries well-defined (traits + type-state)

Pattern Completeness:

  • 7 critical patterns identified and addressed
  • NewType pattern for unit safety
  • KaTeX configuration for documentation
  • Scientific testing tolerances defined
  • Error handling patterns across all bindings

Gap Analysis Results

Critical Gaps: None All PRD requirements are architecturally covered.

Important Gaps: None All necessary patterns are documented.

Nice-to-Have Gaps (Post-MVP):

  1. Rayon parallelization for multi-core optimization
  2. Web GUI for cycle visualization
  3. Pre-configured industrial cycle library

Validation Issues Addressed

No critical issues found during validation. Architecture is coherent and complete.

Architecture Completeness Checklist

Requirements Analysis

  • Project context thoroughly analyzed
  • Scale and complexity assessed (High)
  • Technical constraints identified
  • Cross-cutting concerns mapped

Architectural Decisions

  • Critical decisions documented with versions
  • Technology stack fully specified
  • Integration patterns defined
  • Performance considerations addressed

Implementation Patterns

  • Naming conventions established
  • Structure patterns defined
  • Communication patterns specified
  • Process patterns documented

Project Structure

  • Complete directory structure defined
  • Component boundaries established
  • Integration points mapped
  • Requirements to structure mapping complete

Architecture Readiness Assessment

Overall Status: READY FOR IMPLEMENTATION

Confidence Level: HIGH

Key Strengths:

  1. Solver-agnostic architecture with intelligent fallback
  2. Zero-cost abstractions (NewType, enum dispatch)
  3. Memory safety guaranteed by Rust ownership
  4. Scientific documentation with KaTeX math support
  5. Multi-platform bindings (Rust/Python/C/WASM)
  6. Zero-panic policy with comprehensive error handling
  7. Pre-allocation pattern for deterministic performance

Areas for Future Enhancement:

  1. Parallel execution (Rayon) - Post-MVP
  2. Web-based GUI - Phase 3
  3. Surrogate models for ultra-fast approximations - Future

Implementation Handoff

AI Agent Guidelines:

  1. Follow all architectural decisions exactly as documented

    • Use workspace structure with 4 crates + 3 bindings
    • Implement Solver trait for both Newton-Raphson and Sequential Substitution
    • Use NewType pattern for all physical quantities in public APIs
  2. Use implementation patterns consistently across all components

    • NEVER use bare f64 for physical quantities
    • NEVER use println! - always use tracing
    • NEVER use unwrap/expect in production code
    • ALWAYS use approx for float assertions
  3. Respect project structure and boundaries

    • Keep CoolProp FFI isolated in fluids/coolprop-sys/
    • Implement Component trait for all thermodynamic components
    • Map errors appropriately for each binding target
  4. Refer to this document for all architectural questions

    • First implementation: crates/core (types + errors)
    • Then: crates/fluids (CoolProp integration)
    • Then: crates/components (compressor, condenser, etc.)
    • Finally: crates/solver and bindings

First Implementation Priority:

# Step 1: Initialize workspace
cargo new entropyk --lib

# Step 2: Configure workspace Cargo.toml
# members = ["crates/*", "bindings/*"]

# Step 3: Create crates/core with:
# - NewTypes (Pressure, Temperature, Enthalpy, MassFlow)
# - ThermoError enum
# - FluidState struct

# Step 4: Configure KaTeX
# .cargo/config.toml with rustdocflags

# Step 5: Implement coolprop-sys
# build.rs for C++ compilation

Architecture document complete. Ready for implementation phase.