---
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.*

---

## Crate-to-Pillar Mapping

The codebase is organized into Rust crates that map directly to the 6 business-driven pillars defined in `pillars.md` and `epic-restructuring.md`.

| Crate | Pillar | Responsibility | Stories |
|-------|--------|----------------|---------|
| `entropyk-core` | **P1 Foundation** | Physical types (NewType), Component trait, Port/Connection system, SystemState, graph topology, state machine | P1-01 to P1-14 |
| `entropyk-components` | **P2 Real Components** | All thermodynamic components (compressor, HX, valve, pump, fan, pipe, boundary conditions, BPHX, flooded equipment, drums) | P2-01 to P2-39 |
| `entropyk-vendors` | **P2 Real Components** | VendorBackend trait, Copeland/Danfoss/Bitzer/SWEP parsers, equipment catalog | P2-26 to P2-30, P2-37, P2-39 |
| `entropyk-fluids` | **P3 Fluid Properties** | FluidBackend trait, CoolProp sys-crate, tabular interpolation, LRU cache, mixtures, CO2 damping, incompressible fluids, ThermoState, psychrometrics | P3-01 to P3-09 |
| `entropyk-solver` | **P4 Simulation & Solver** | Newton-Raphson, Picard, fallback strategy, inverse control, constraints, calibration, validation, JSON serialization, energy methods | P4-01 to P4-26 |
| `entropyk-cli` | **P5 User Interfaces** | CLI for batch execution, component configuration, output formatting | P5-05, P5-07 to P5-14, P5-22 to P5-41 |
| `bindings/python` | **P5 User Interfaces** | PyO3 bindings, Python API parity | P5-02, P5-06, P5-42 to P5-54 |
| `bindings/c` | **P5 User Interfaces** | C FFI via cbindgen, header generation | P5-03 |
| `bindings/wasm` | **P5 User Interfaces** | WebAssembly compilation via wasm-bindgen | P5-04, P5-55 to P5-59 |
| *(none yet)* | **P6 HVAC Applications** | Free cooling, mode switching, control applications, Eurovent, ASHRAE, calibration workflow | P6-01 to P6-14 |

### Dependency Flow (Pillar Order)

```
P1 (entropyk-core)
    |       \
    v        v
P3 (entropyk-fluids)  P2 (entropyk-components + entropyk-vendors)
    |       /      |
    v      v       v
    P4 (entropyk-solver)
         |
         v
    P5 (entropyk-cli + bindings/*)
         |
         v
    P6 (no dedicated crate yet — uses P5 interfaces)
```

### Cross-Chain Dependencies

| From Crate | To Crate | Nature |
|------------|----------|--------|
| `entropyk-components` | `entropyk-core` | Component trait, types, ports |
| `entropyk-components` | `entropyk-fluids` | FluidBackend for residual computation |
| `entropyk-vendors` | `entropyk-core` | VendorBackend trait |
| `entropyk-solver` | `entropyk-core` | SystemState, graph, types |
| `entropyk-solver` | `entropyk-components` | Components provide residuals/Jacobian |
| `entropyk-solver` | `entropyk-fluids` | Property evaluation during solve |
| `entropyk-cli` | `entropyk-solver` | Solve API, result types |
| `bindings/*` | `entropyk-solver` | Re-export public API |

This mapping replaces the old "Feature/Epic Mapping" table. See `pillars.md` for the authoritative story-to-pillar assignment.
