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
37 KiB
| stepsCompleted | inputDocuments | workflowType | project_name | user_name | date | lastStep | status | completedAt | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
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:
- Solver-Agnostic Architecture with Intelligent Fallback
- Multi-Circuit Topology with Thermal Coupling
- Zero-Panic Policy + No-Allocation guarantees
- Native Inverse Control via Residual Embedding
- 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
- Solver Abstraction: Uniform interface for Sequential Substitution and Newton-Raphson
- Error Handling Strategy: Result<T, ThermoError> throughout, zero-panic guarantee
- Memory Management: Pre-allocation strategy, no heap allocation in hot paths
- API Consistency: Rust/Python/C/WASM APIs must remain synchronized
- Validation & Verification: Mass/energy balance checks, benchmark regression testing
- 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.rsgère compilation statique CoolProp C++- Linking statique pour distribution simplifiée
- Wrappers Rust safe au-dessus du FFI brut
Observability Strategy:
tracingpour structured logging (pas deprintln!)- 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:
approxpour assertions flottantes (assert_relative_eq!)- Tolérance par défaut: 1e-6 (matching NFR bilans énergétiques)
criterion.rspour 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 scientifiquecargo bench: Suivi performance (régression < 5% = échec CI)cargo clippy -- -D warnings: Tolérance zéro stylemiri test: Détection undefined behaviorvalgrind: Vérification fuites mémoire FFI
Core Architectural Decisions
Decision Priority Analysis
Critical Decisions (Block Implementation):
- Solver Architecture Pattern
- Component Model Design
- Fluid Properties Backend Abstraction
- 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, variablesCamelCase: types, traits, enum variantsSCREAMING_SNAKE_CASE: constantes- NO prefix
Ifor traits (justSolver, notISolver) - NO prefix
Efor enums (justError, notEError)
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>avecthiserror - Python:
PyErrvia PyO3 (exceptions Python natives) - WASM:
Result<T, JsError>avecwasm-bindgen
Data Exchange Formats:
- JSON:
camelCasepour compatibilité JavaScript/Python - Dates: ISO 8601 strings (pas de timestamps)
- Nombres:
f64partout (pas def32pour éviter perte précision)
Communication Patterns
Event System Patterns:
tracingspans 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()etexpect()interdits sauf danstests/- 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:
Pressureest explicite vsf64ambigu - 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.tomlorbuild.rsthat 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:
- NEVER use bare
f64in public APIs - Always use NewType pattern for physical quantities - NEVER use
println!- Usetracingwith appropriate levels - NEVER use
unwraporexpectin production code - Always returnResult - ALWAYS include LaTeX header for mathematical documentation
- ALWAYS use
approxfor float assertions - Neverassert_eq!withf64 - ALWAYS follow naming conventions - snake_case functions, CamelCase types
- ALWAYS pre-allocate - No dynamic allocation in solver hot paths
Pattern Verification:
cargo clippy -- -D warnings: Détecte unwrap/expectcargo doc: Vérifie formules KaTeX rendues correctementcargo 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
ThermoErrorto Python exceptions viaPyErr - Type conversions:
f64↔float, 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:
NewtonRaphsonandSequentialSubstitutionimplementSolverSolverStrategyenum for runtime selection- Fallback logic in
solve_with_fallback()coordinator
Data Boundaries:
Fluid Properties:
FluidBackendtrait abstracts CoolProp vs Tabular- Cache layer transparent aux appelants
- Damping appliqué automatiquement près point critique
State Management:
SystemStateimmutable, cloné à chaque itérationConvergedStateré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-sysavecbuild.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 cratebuild.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 physiquesolver/strategies/: Un fichier par algorithmefluids/: 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éestests/integration/: Tests scénarios completsbenches/criterion/: Performance benchmarks
Asset Organization:
Documentation:
docs/src/: Contenu mdBookdocs/katex-header.html: Configuration Math- Généré dans
target/doc/(rustdoc) oudocs/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 strictcargo test --workspace: Tests unitairescargo test --test validation: Tests scientifiquescargo bench: Benchmarks performancemiri test: Détection undefined behaviorcargo 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
ThermoErrorenum withthiserror - Testing uses
approxwith 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):
- Rayon parallelization for multi-core optimization
- Web GUI for cycle visualization
- 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:
- Solver-agnostic architecture with intelligent fallback
- Zero-cost abstractions (NewType, enum dispatch)
- Memory safety guaranteed by Rust ownership
- Scientific documentation with KaTeX math support
- Multi-platform bindings (Rust/Python/C/WASM)
- Zero-panic policy with comprehensive error handling
- Pre-allocation pattern for deterministic performance
Areas for Future Enhancement:
- Parallel execution (Rayon) - Post-MVP
- Web-based GUI - Phase 3
- Surrogate models for ultra-fast approximations - Future
Implementation Handoff
AI Agent Guidelines:
-
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
-
Use implementation patterns consistently across all components
- NEVER use bare
f64for physical quantities - NEVER use
println!- always usetracing - NEVER use
unwrap/expectin production code - ALWAYS use
approxfor float assertions
- NEVER use bare
-
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
- Keep CoolProp FFI isolated in
-
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/solverand bindings
- First implementation:
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.