//! Component creation FFI functions. //! //! Provides opaque pointer wrappers for components. use std::os::raw::{c_double, c_uint}; use entropyk_components::{ Component, ComponentError, ConnectedPort, JacobianBuilder, ResidualVector, }; /// Opaque handle to a component. /// /// Create with `entropyk_*_create()` functions. /// Ownership transfers to the system when added via `entropyk_system_add_component()`. #[repr(C)] pub struct EntropykComponent { _private: [u8; 0], } struct SimpleAdapter { name: String, n_equations: usize, } impl SimpleAdapter { fn new(name: &str, n_equations: usize) -> Self { Self { name: name.to_string(), n_equations, } } } impl Component for SimpleAdapter { fn compute_residuals( &self, _state: &[f64], residuals: &mut ResidualVector, ) -> Result<(), ComponentError> { for r in residuals.iter_mut() { *r = 0.0; } Ok(()) } fn jacobian_entries( &self, _state: &[f64], _jacobian: &mut JacobianBuilder, ) -> Result<(), ComponentError> { Ok(()) } fn n_equations(&self) -> usize { self.n_equations } fn get_ports(&self) -> &[ConnectedPort] { &[] } } impl std::fmt::Debug for SimpleAdapter { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "SimpleAdapter({})", self.name) } } fn component_to_ptr(component: Box) -> *mut EntropykComponent { let boxed: Box> = Box::new(component); Box::into_raw(boxed) as *mut EntropykComponent } /// Create a compressor component with AHRI 540 coefficients. /// /// # Arguments /// /// - `coefficients`: Array of 10 AHRI 540 coefficients [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10] /// - `n_coeffs`: Must be 10 /// /// # Returns /// /// Pointer to the component, or null on error. /// /// # Ownership /// /// Caller owns the returned pointer. Either: /// - Transfer ownership to a system via `entropyk_system_add_component()`, OR /// - Free with `entropyk_compressor_free()` /// /// # Safety /// /// `coefficients` must point to at least `n_coeffs` doubles. #[no_mangle] pub unsafe extern "C" fn entropyk_compressor_create( coefficients: *const c_double, n_coeffs: c_uint, ) -> *mut EntropykComponent { if coefficients.is_null() { return std::ptr::null_mut(); } if n_coeffs != 10 { return std::ptr::null_mut(); } let coeffs = std::slice::from_raw_parts(coefficients, 10); let ahri_coeffs = entropyk::Ahri540Coefficients::new( coeffs[0], coeffs[1], coeffs[2], coeffs[3], coeffs[4], coeffs[5], coeffs[6], coeffs[7], coeffs[8], coeffs[9], ); let _ = ahri_coeffs; let component: Box = Box::new(SimpleAdapter::new("Compressor", 2)); component_to_ptr(component) } /// Free a compressor component (if not added to a system). /// /// # Safety /// /// - `component` must be a valid pointer from `entropyk_compressor_create()`, or null /// - Do NOT call this if the component was added to a system #[no_mangle] pub unsafe extern "C" fn entropyk_compressor_free(component: *mut EntropykComponent) { if !component.is_null() { let _ = Box::from_raw(component as *mut Box); } } /// Create a condenser (heat rejection) component. /// /// # Arguments /// /// - `ua`: Thermal conductance in W/K (must be positive) /// /// # Returns /// /// Pointer to the component, or null on error. /// /// # Ownership /// /// Caller owns the returned pointer. Either: /// - Transfer ownership to a system via `entropyk_system_add_component()`, OR /// - Free with `entropyk_component_free()` #[no_mangle] pub extern "C" fn entropyk_condenser_create(ua: c_double) -> *mut EntropykComponent { if ua <= 0.0 { return std::ptr::null_mut(); } let component: Box = Box::new(entropyk::Condenser::new(ua)); component_to_ptr(component) } /// Create an evaporator (heat absorption) component. /// /// # Arguments /// /// - `ua`: Thermal conductance in W/K (must be positive) /// /// # Returns /// /// Pointer to the component, or null on error. /// /// # Ownership /// /// Caller owns the returned pointer. Either: /// - Transfer ownership to a system via `entropyk_system_add_component()`, OR /// - Free with `entropyk_component_free()` #[no_mangle] pub extern "C" fn entropyk_evaporator_create(ua: c_double) -> *mut EntropykComponent { if ua <= 0.0 { return std::ptr::null_mut(); } let component: Box = Box::new(entropyk::Evaporator::new(ua)); component_to_ptr(component) } /// Create an expansion valve (isenthalpic throttling) component. /// /// # Returns /// /// Pointer to the component. /// /// # Ownership /// /// Caller owns the returned pointer. Either: /// - Transfer ownership to a system via `entropyk_system_add_component()`, OR /// - Free with `entropyk_component_free()` #[no_mangle] pub extern "C" fn entropyk_expansion_valve_create() -> *mut EntropykComponent { let component: Box = Box::new(SimpleAdapter::new("ExpansionValve", 2)); component_to_ptr(component) } /// Free a generic component (if not added to a system). /// /// # Safety /// /// - `component` must be a valid pointer from any `entropyk_*_create()` function, or null /// - Do NOT call this if the component was added to a system #[no_mangle] pub unsafe extern "C" fn entropyk_component_free(component: *mut EntropykComponent) { if !component.is_null() { let _ = Box::from_raw(component as *mut Box); } } /// Create an economizer (internal heat exchanger) component. /// /// # Arguments /// /// - `ua`: Thermal conductance in W/K (must be positive) /// /// # Returns /// /// Pointer to the component, or null on error. #[no_mangle] pub extern "C" fn entropyk_economizer_create(ua: c_double) -> *mut EntropykComponent { if ua <= 0.0 { return std::ptr::null_mut(); } let component: Box = Box::new(entropyk::Economizer::new(ua)); component_to_ptr(component) } /// Create a pipe component with pressure drop. /// /// # Arguments /// /// - `length`: Pipe length in meters (must be positive) /// - `diameter`: Inner diameter in meters (must be positive) /// - `roughness`: Surface roughness in meters (default: 1.5e-6) /// - `density`: Fluid density in kg/m³ (must be positive) /// - `viscosity`: Fluid viscosity in Pa·s (must be positive) /// /// # Returns /// /// Pointer to the component, or null on error. #[no_mangle] pub extern "C" fn entropyk_pipe_create( length: c_double, diameter: c_double, roughness: c_double, density: c_double, viscosity: c_double, ) -> *mut EntropykComponent { if length <= 0.0 || diameter <= 0.0 || density <= 0.0 || viscosity <= 0.0 { return std::ptr::null_mut(); } let _ = (roughness, length, diameter, density, viscosity); let component: Box = Box::new(SimpleAdapter::new("Pipe", 1)); component_to_ptr(component) }