1196 lines
38 KiB
Markdown

---
stepsCompleted: [1, 2, 3, 4, 5, 6, 7, 8]
inputDocuments:
- /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
workflowType: architecture
project_name: Entropyk
user_name: Sepehr
date: 2026-02-12
lastStep: 8
status: complete
completedAt: 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
```bash
# 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:**
```rust
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:**
```rust
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:**
```rust
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:**
```rust
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
### Hierarchical Subsystems (MacroComponents)
**Decision:** Wrapper Pattern matching the `Component` trait
**Core Pattern:**
```rust
struct MacroComponent {
internal_system: System,
port_mapping: HashMap<PortId, InternalLocation>, /* e.g., Exposes 'Condenser Water In' */
}
impl Component for MacroComponent {
fn compute_residuals(&self, state: &SystemState, residuals: &mut ResidualVector) {
// Delegates or flattens computation to internal_system
self.internal_system.compute_residuals(state, residuals);
}
// ... maps external ports to internal boundary ports ...
}
```
**Rationale:**
- Allows users to build reusable blocks (like a full Chiller, Air Handling Unit)
- Mimics Modelica/Simulink ecosystem composability
- The global solver treats `MacroComponent` exactly like a basic Component, preserving zero-cost abstractions
- `SystemState` flattening ensures equations are solved simultaneously globally, avoiding nested numerical solver delays.
### Fluid Properties Backend
**Decision:** Trait abstraction with multiple backends
**Architecture:**
```rust
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<Cache>>) pour parallélisation future
**Critical Point Handling (CO2 R744):**
```rust
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:**
```rust
#[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:**
```rust
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:**
```rust
// 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é:**
```rust
// DANGER - Ne jamais faire ceci
fn solve(p: f64, t: f64) // Alice peut appeler solve(300.0, 100000.0) - inversion T/P!
```
**Solution:**
```rust
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:**
```toml
# .cargo/config.toml
[build]
rustdocflags = ["--html-in-header", "docs/katex-header.html"]
```
```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:**
```rust
/// 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`
```rust
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:**
```rust
// 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:**
```rust
// 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: `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:*
```rust
// 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:*
```rust
// 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, FR48 | `crates/solver/src/system.rs` & `macro_component.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:*
```rust
// 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:*
```rust
#[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:**
```bash
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**
- [x] Project context thoroughly analyzed
- [x] Scale and complexity assessed (High)
- [x] Technical constraints identified
- [x] Cross-cutting concerns mapped
** Architectural Decisions**
- [x] Critical decisions documented with versions
- [x] Technology stack fully specified
- [x] Integration patterns defined
- [x] Performance considerations addressed
** Implementation Patterns**
- [x] Naming conventions established
- [x] Structure patterns defined
- [x] Communication patterns specified
- [x] Process patterns documented
** Project Structure**
- [x] Complete directory structure defined
- [x] Component boundaries established
- [x] Integration points mapped
- [x] 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:**
```bash
# 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.*