11 KiB
Story 6.4: WebAssembly Compilation
Status: in-progress
Story
As a web developer (Charlie), I want WebAssembly compilation support with TabularBackend as default, So that I can run thermodynamic simulations directly in the browser without server-side dependencies.
Acceptance Criteria
-
Given a web application with the WASM module imported When creating a cycle and calling solve() Then it executes successfully in Chrome/Edge/Firefox And results are JSON-serializable for JavaScript consumption
-
Given the WASM build configuration When compiling with
wasm-pack buildThen it defaults to TabularBackend (CoolProp unavailable in WASM) And pre-loaded fluid tables (R134a, R410A, etc.) are embedded -
Given a simple refrigeration cycle in WASM When measuring cycle solve time Then convergence completes in < 100ms (NFR2) And deterministic behavior matches native builds (NFR5)
-
Given the compiled WASM package When publishing to npm Then package is installable via
npm install @entropyk/wasmAnd TypeScript type definitions are included -
Given browser error conditions (invalid inputs, non-convergence) When an error occurs Then JavaScript exceptions are thrown (not panics) And error messages are human-readable
Tasks / Subtasks
-
Task 1: Create WASM bindings crate structure (AC: #1, #2)
- Create
bindings/wasm/Cargo.tomlwith wasm-bindgen dependencies - Create
bindings/wasm/src/lib.rswith module initialization - Add
bindings/wasmto workspace members in root Cargo.toml - Configure
crate-type = ["cdylib"]for WASM target
- Create
-
Task 2: Implement WASM type wrappers (AC: #1)
- Create
bindings/wasm/src/types.rs- wrapper types for Pressure, Temperature, Enthalpy, MassFlow - Create
bindings/wasm/src/errors.rs- JsError mapping from ThermoError - Implement
#[wasm_bindgen]for all public types with JSON serialization
- Create
-
Task 3: Implement WASM component bindings (AC: #1)
- Create
bindings/wasm/src/components.rs- wrappers for Compressor, Condenser, Evaporator, etc. - Expose component constructors with JavaScript-friendly APIs
- Implement JSON serialization for component states
- Create
-
Task 4: Implement WASM solver bindings (AC: #1, #3)
- Create
bindings/wasm/src/solver.rs- System and solver wrappers - Expose NewtonConfig, PicardConfig, FallbackConfig
- Implement ConvergedState with JSON output
- Add performance timing helpers for benchmarking
- Create
-
Task 5: Configure TabularBackend as default (AC: #2)
- Create
bindings/wasm/src/backend.rs- WASM-specific backend initialization - Embed fluid table data (R134a.json) using
include_str! - Implement lazy-loading for additional fluid tables
- Document fluid table embedding process for custom fluids
- Create
-
Task 6: Add panic hook and error handling (AC: #5)
- Add
console_error_panic_hookdependency - Configure panic hook in lib.rs initialization
- Map all ThermoError variants to descriptive JsError messages
- Add
-
Task 7: Create npm package configuration (AC: #4)
- Create
bindings/wasm/package.jsonwith npm metadata - Create
bindings/wasm/README.mdwith usage examples - Configure
wasm-packfor--target weband--target nodejs - Generate TypeScript type definitions
- Create
-
Task 8: Write WASM tests and examples (AC: #1, #3)
- Create
bindings/wasm/tests/simple_cycle.js- basic cycle test - Create
bindings/wasm/examples/browser/- HTML/JS demo - Add performance benchmark test (< 100ms verification)
- Add determinism test (compare vs native)
- Create
Dev Notes
Architecture Compliance
- Crate Location:
bindings/wasm/(follows existingbindings/python/andbindings/c/patterns) - Naming: Crate name
entropyk-wasm, lib name via[lib]in Cargo.toml - Dependencies: Reuse internal crates (entropyk, entropyk-core, entropyk-components, entropyk-solver, entropyk-fluids)
- Error Handling: Map errors to
JsError- NEVER panic in WASM - Observability: Use
tracing-wasmfor browser console logging (neverprintln!)
Critical: TabularBackend is REQUIRED
CoolProp C++ cannot compile to WASM. The WASM build MUST use TabularBackend with embedded fluid tables.
Project Structure Notes
- Follow the exact pattern from
bindings/python/src/lib.rsfor module organization - Type wrappers mirror Python's
types.rspattern but with#[wasm_bindgen]instead of#[pyclass] - Component wrappers mirror Python's
components.rspattern - Solver wrappers mirror Python's
solver.rspattern
References
- [Source: _bmad-output/planning-artifacts/epics.md#L1102-L1119] - Story 6.4 acceptance criteria
- [Source: _bmad-output/planning-artifacts/architecture.md#L711-L715] - WASM binding structure
- [Source: _bmad-output/planning-artifacts/architecture.md#L41-L44] - Technical stack (wasm-bindgen)
- [Source: bindings/python/Cargo.toml] - Reference binding structure
- [Source: crates/fluids/src/tabular_backend.rs] - TabularBackend implementation
- [Source: crates/fluids/data/r134a.json] - Embedded fluid table
Dev Agent Record
Agent Model Used
zai-coding-plan/glm-5
Debug Log References
- Initial compilation errors with wasm-bindgen builder pattern (fixed by using setters instead of returning &mut Self)
- ThermoError import location issue (in entropyk crate, not entropyk-core)
- System generic parameter removed (not needed in current API)
- SolverStrategy enum variants (NewtonRaphson, SequentialSubstitution) - no Fallback variant
- FallbackSolver::default_solver() instead of ::default()
Completion Notes List
- Created complete WASM bindings crate structure following Python/C binding patterns
- Implemented type wrappers (Pressure, Temperature, Enthalpy, MassFlow) with JSON serialization
- Created stub component bindings (full integration requires additional component API work)
- Implemented solver bindings using SolverStrategy and FallbackSolver
- Configured TabularBackend with embedded R134a table
- Added console_error_panic_hook for browser error handling
- Created npm package.json and README for distribution
- Added browser example and Node.js test file
File List
- bindings/wasm/Cargo.toml
- bindings/wasm/src/lib.rs
- bindings/wasm/src/types.rs
- bindings/wasm/src/errors.rs
- bindings/wasm/src/components.rs
- bindings/wasm/src/solver.rs
- bindings/wasm/src/backend.rs
- bindings/wasm/package.json
- bindings/wasm/README.md
- bindings/wasm/tests/simple_cycle.js
- bindings/wasm/examples/browser/index.html
- Cargo.toml (updated workspace members)
Review Findings
-
[Review][Decision→Patch] Component bindings are non-functional stubs — constructors hardcode
R410A, dummy port state. Resolved (1a): completed component bindings with proper fluid-param constructors, Result returns, and input validation. -
[Review][Decision→Patch] Only R134a fluid table embedded — spec AC#2 says "R134a, R410A, etc." Resolved (2a): created global backend infrastructure with lazy init. Additional tables (R410A, R32) require generation from TabularBackend tool — only R134a data file exists.
-
[Review][Decision→Patch] TypeScript type definitions not included — AC#4. Resolved (3b): documented TypeScript generation workflow in README. Types are auto-generated by
wasm-pack build. -
[Review][Decision→Patch] Missing
tracing-wasmdependency. Resolved (4a): addedtracing+tracing-wasmto Cargo.toml. -
[Review][Patch]
load_fluid_tableis a no-op — fixed: validates table, warns about registration limitation. [backend.rs] -
[Review][Patch]
get_node_resultfabricates placeholder data — fixed: usesFluidBackend::full_state()to compute real ThermoState from P and h. [solver.rs] -
[Review][Patch]
solve()ignoresWasmFallbackConfig— fixed: wraps realFallbackConfig, passes toFallbackSolver::new(config). [solver.rs] -
[Review][Patch]
timeout_mssilently discarded — fixed: removed no-op, replaced with actualFallbackConfigfields (set_fallback_enabled,set_return_to_newton_threshold,set_max_fallback_switches). [solver.rs] -
[Review][Patch] Component constructors panic on
.unwrap()— fixed: all constructors returnResult<T, JsValue>with descriptive errors. [components.rs] -
[Review][Patch]
add_edgeno bounds validation — fixed: checks node indices againstnode_count()before creating NodeIndex. [solver.rs] -
[Review][Patch]
get_node_resultno bounds checking — fixed: validatesp_idx/h_idxagainststate.len(). [solver.rs] -
[Review][Patch] README examples mismatch actual API — fixed: all examples rewritten with correct constructor signatures. [README.md]
-
[Review][Patch] No UA validation on Condenser/Evaporator — fixed: rejects NaN, infinity, zero, and negative values. [components.rs]
-
[Review][Patch]
WasmPipe::newpanics on invalid geometry — fixed: validates length/diameter, returns Result. [components.rs] -
[Review][Patch]
toJsonuses wrong serializer — fixed: replacedserde_wasm_bindgenwithserde_json::to_string(). Removedserde-wasm-bindgendependency. [types.rs] -
[Review][Patch]
WasmConvergedStateloses metadata — fixed: addedstatusfield with ConvergenceStatus mapping (Converged/TimedOut/ControlSaturation). [solver.rs, types.rs] -
[Review][Patch]
errors.rsis dead code — fixed: expanded withthermo_error_to_js()that maps all ThermoError variants to descriptive messages. [errors.rs] -
[Review][Patch]
WasmComponent::name()always returns "Component" — fixed: stores component type name in WasmComponent wrapper. [components.rs] -
[Review][Patch] No physical validation on type constructors — fixed: WasmPressure rejects negative/NaN, WasmTemperature rejects negative/NaN, all types reject NaN. Constructors return Result. [types.rs]
-
[Review][Patch]
set_relaxation_factorno bounds — fixed: clamps to (0, 1] with tracing warning. [solver.rs] -
[Review][Patch]
WasmSystem::Defaultusesexpect— fixed: removed Default impl for WasmSystem. Users must usenew()which returns Result. [solver.rs] -
[Review][Defer] No performance benchmark/timing helpers — Task 4 (AC#3) requires < 100ms convergence verification. No automated benchmark exists. Deferred: feature not implemented, not a bug in current code.
-
[Review][Defer] No determinism test — Task 8 requires "compare vs native" determinism test. No test exists. Deferred: feature not implemented.
-
[Review][Defer] Test file
simple_cycle.jscannot run without manualwasm-pack build— no CI target or npm pretest hook. Deferred: needs CI/build setup. -
[Review][Defer]
into_component()consumes self — prevents configure-then-add workflow from JS. Deferred: design choice, not a bug. -
[Review][Defer]
list_available_fluidscreates fresh backend on every call — re-parses embedded R134a JSON each time. Deferred: minor perf, not a correctness issue. -
[Review][Defer] Missing
tracing-wasmintegration — no logging infrastructure in WASM crate beyondconsole_error_panic_hook. Deferred alongside Decision item.