272 lines
8.8 KiB
Rust
272 lines
8.8 KiB
Rust
//! WASM component bindings.
|
|
//!
|
|
//! Provides JavaScript-friendly wrappers for thermodynamic components.
|
|
|
|
use entropyk_components::port::{FluidId, Port};
|
|
use entropyk_components::Component;
|
|
use wasm_bindgen::prelude::*;
|
|
|
|
/// WASM wrapper for a thermodynamic component.
|
|
#[wasm_bindgen]
|
|
pub struct WasmComponent {
|
|
pub(crate) inner: Box<dyn Component>,
|
|
name: String,
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
impl WasmComponent {
|
|
/// Get component type name.
|
|
pub fn name(&self) -> String {
|
|
self.name.clone()
|
|
}
|
|
}
|
|
|
|
/// WASM wrapper for Compressor.
|
|
#[wasm_bindgen]
|
|
pub struct WasmCompressor {
|
|
pub(crate) inner: entropyk_components::Compressor<entropyk_components::port::Connected>,
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
impl WasmCompressor {
|
|
/// Create a new Compressor with AHRI 540 performance model.
|
|
///
|
|
/// # Arguments
|
|
/// * `fluid` - Fluid identifier (e.g. "R134a", "R410A")
|
|
/// * `speed_rpm` - Rotational speed in RPM
|
|
/// * `displacement_m3_per_rev` - Displacement volume in m³/rev
|
|
/// * `efficiency` - Mechanical efficiency (0.0 to 1.0)
|
|
/// * `m1`..`m10` - AHRI 540 performance coefficients
|
|
#[wasm_bindgen(constructor)]
|
|
pub fn new(
|
|
fluid: String,
|
|
speed_rpm: f64,
|
|
displacement_m3_per_rev: f64,
|
|
efficiency: f64,
|
|
m1: f64,
|
|
m2: f64,
|
|
m3: f64,
|
|
m4: f64,
|
|
m5: f64,
|
|
m6: f64,
|
|
m7: f64,
|
|
m8: f64,
|
|
m9: f64,
|
|
m10: f64,
|
|
) -> Result<WasmCompressor, JsValue> {
|
|
if speed_rpm <= 0.0 {
|
|
return Err(js_sys::Error::new("speed_rpm must be positive").into());
|
|
}
|
|
if displacement_m3_per_rev <= 0.0 {
|
|
return Err(js_sys::Error::new("displacement_m3_per_rev must be positive").into());
|
|
}
|
|
if !(0.0..=1.0).contains(&efficiency) {
|
|
return Err(js_sys::Error::new("efficiency must be between 0.0 and 1.0").into());
|
|
}
|
|
|
|
let coeffs =
|
|
entropyk_components::Ahri540Coefficients::new(m1, m2, m3, m4, m5, m6, m7, m8, m9, m10);
|
|
let fluid_id = FluidId::new(&fluid);
|
|
let p = entropyk_core::Pressure::from_bar(5.0);
|
|
let h = entropyk_core::Enthalpy::from_joules_per_kg(400000.0);
|
|
let suction = Port::new(fluid_id.clone(), p, h);
|
|
let discharge = Port::new(fluid_id.clone(), p, h);
|
|
|
|
let comp = entropyk_components::Compressor::new(
|
|
coeffs,
|
|
suction,
|
|
discharge,
|
|
speed_rpm,
|
|
displacement_m3_per_rev,
|
|
efficiency,
|
|
)
|
|
.map_err(|e| js_sys::Error::new(&format!("Compressor creation failed: {}", e)))?;
|
|
|
|
let suction_p = Port::new(fluid_id.clone(), p, h);
|
|
let discharge_p = Port::new(fluid_id, p, h);
|
|
let connected = comp
|
|
.connect(suction_p, discharge_p)
|
|
.map_err(|e| js_sys::Error::new(&format!("Compressor connect failed: {}", e)))?;
|
|
|
|
Ok(WasmCompressor { inner: connected })
|
|
}
|
|
|
|
/// Convert to a generic WasmComponent.
|
|
pub fn into_component(self) -> WasmComponent {
|
|
WasmComponent {
|
|
inner: Box::new(self.inner),
|
|
name: "Compressor".to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// WASM wrapper for Condenser.
|
|
#[wasm_bindgen]
|
|
pub struct WasmCondenser {
|
|
inner: entropyk_components::Condenser,
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
impl WasmCondenser {
|
|
/// Create a new condenser with thermal conductance UA (W/K).
|
|
///
|
|
/// Rejects NaN, negative, zero, and infinite values.
|
|
#[wasm_bindgen(constructor)]
|
|
pub fn new(ua: f64) -> Result<WasmCondenser, JsValue> {
|
|
if ua.is_nan() || ua.is_infinite() {
|
|
return Err(js_sys::Error::new("UA must be a finite number").into());
|
|
}
|
|
if ua <= 0.0 {
|
|
return Err(js_sys::Error::new("UA must be positive").into());
|
|
}
|
|
Ok(WasmCondenser {
|
|
inner: entropyk_components::Condenser::new(ua),
|
|
})
|
|
}
|
|
|
|
/// Convert to a generic WasmComponent.
|
|
pub fn into_component(self) -> WasmComponent {
|
|
WasmComponent {
|
|
inner: Box::new(self.inner),
|
|
name: "Condenser".to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// WASM wrapper for Evaporator.
|
|
#[wasm_bindgen]
|
|
pub struct WasmEvaporator {
|
|
inner: entropyk_components::Evaporator,
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
impl WasmEvaporator {
|
|
/// Create a new evaporator with thermal conductance UA (W/K).
|
|
///
|
|
/// Rejects NaN, negative, zero, and infinite values.
|
|
#[wasm_bindgen(constructor)]
|
|
pub fn new(ua: f64) -> Result<WasmEvaporator, JsValue> {
|
|
if ua.is_nan() || ua.is_infinite() {
|
|
return Err(js_sys::Error::new("UA must be a finite number").into());
|
|
}
|
|
if ua <= 0.0 {
|
|
return Err(js_sys::Error::new("UA must be positive").into());
|
|
}
|
|
Ok(WasmEvaporator {
|
|
inner: entropyk_components::Evaporator::new(ua),
|
|
})
|
|
}
|
|
|
|
/// Convert to a generic WasmComponent.
|
|
pub fn into_component(self) -> WasmComponent {
|
|
WasmComponent {
|
|
inner: Box::new(self.inner),
|
|
name: "Evaporator".to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// WASM wrapper for ExpansionValve.
|
|
#[wasm_bindgen]
|
|
pub struct WasmExpansionValve {
|
|
pub(crate) inner: entropyk_components::ExpansionValve<entropyk_components::port::Connected>,
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
impl WasmExpansionValve {
|
|
/// Create a new expansion valve.
|
|
///
|
|
/// # Arguments
|
|
/// * `fluid` - Fluid identifier (e.g. "R134a", "R410A")
|
|
/// * `capacity` - Optional valve capacity (kW). Pass 0.0 for no capacity limit.
|
|
#[wasm_bindgen(constructor)]
|
|
pub fn new(fluid: String, capacity: f64) -> Result<WasmExpansionValve, JsValue> {
|
|
let fluid_id = FluidId::new(&fluid);
|
|
let p = entropyk_core::Pressure::from_bar(10.0);
|
|
let h = entropyk_core::Enthalpy::from_joules_per_kg(400000.0);
|
|
let inlet = Port::new(fluid_id.clone(), p, h);
|
|
let outlet = Port::new(fluid_id.clone(), p, h);
|
|
|
|
let cap = if capacity > 0.0 { Some(capacity) } else { None };
|
|
let valve = entropyk_components::ExpansionValve::new(inlet, outlet, cap)
|
|
.map_err(|e| js_sys::Error::new(&format!("ExpansionValve creation failed: {}", e)))?;
|
|
|
|
let inlet_p = Port::new(fluid_id.clone(), p, h);
|
|
let outlet_p = Port::new(fluid_id, p, h);
|
|
let connected = valve
|
|
.connect(inlet_p, outlet_p)
|
|
.map_err(|e| js_sys::Error::new(&format!("ExpansionValve connect failed: {}", e)))?;
|
|
|
|
Ok(WasmExpansionValve { inner: connected })
|
|
}
|
|
|
|
/// Convert to a generic WasmComponent.
|
|
pub fn into_component(self) -> WasmComponent {
|
|
WasmComponent {
|
|
inner: Box::new(self.inner),
|
|
name: "ExpansionValve".to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// WASM wrapper for Pipe.
|
|
#[wasm_bindgen]
|
|
pub struct WasmPipe {
|
|
pub(crate) inner: entropyk_components::Pipe<entropyk_components::port::Connected>,
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
impl WasmPipe {
|
|
/// Create a new pipe.
|
|
///
|
|
/// # Arguments
|
|
/// * `fluid` - Fluid identifier (e.g. "Water")
|
|
/// * `length` - Pipe length in meters (must be positive)
|
|
/// * `diameter` - Pipe inner diameter in meters (must be positive)
|
|
/// * `density` - Fluid density in kg/m³ (default: 1000.0 for water)
|
|
/// * `viscosity` - Fluid dynamic viscosity in Pa·s (default: 0.001 for water)
|
|
#[wasm_bindgen(constructor)]
|
|
pub fn new(
|
|
fluid: String,
|
|
length: f64,
|
|
diameter: f64,
|
|
density: f64,
|
|
viscosity: f64,
|
|
) -> Result<WasmPipe, JsValue> {
|
|
if length.is_nan() || length <= 0.0 {
|
|
return Err(js_sys::Error::new("Pipe length must be a positive number").into());
|
|
}
|
|
if diameter.is_nan() || diameter <= 0.0 {
|
|
return Err(js_sys::Error::new("Pipe diameter must be a positive number").into());
|
|
}
|
|
|
|
let geometry = entropyk_components::PipeGeometry::smooth(length, diameter)
|
|
.map_err(|e| js_sys::Error::new(&format!("Invalid pipe geometry: {}", e)))?;
|
|
|
|
let fluid_id = FluidId::new(&fluid);
|
|
let p = entropyk_core::Pressure::from_bar(1.0);
|
|
let h = entropyk_core::Enthalpy::from_joules_per_kg(100000.0);
|
|
let inlet = Port::new(fluid_id.clone(), p, h);
|
|
let outlet = Port::new(fluid_id.clone(), p, h);
|
|
|
|
let pipe = entropyk_components::Pipe::new(geometry, inlet, outlet, density, viscosity)
|
|
.map_err(|e| js_sys::Error::new(&format!("Pipe creation failed: {}", e)))?;
|
|
|
|
let inlet_p = Port::new(fluid_id.clone(), p, h);
|
|
let outlet_p = Port::new(fluid_id, p, h);
|
|
let connected = pipe
|
|
.connect(inlet_p, outlet_p)
|
|
.map_err(|e| js_sys::Error::new(&format!("Pipe connect failed: {}", e)))?;
|
|
|
|
Ok(WasmPipe { inner: connected })
|
|
}
|
|
|
|
/// Convert to a generic WasmComponent.
|
|
pub fn into_component(self) -> WasmComponent {
|
|
WasmComponent {
|
|
inner: Box::new(self.inner),
|
|
name: "Pipe".to_string(),
|
|
}
|
|
}
|
|
}
|