Entropyk/bindings/c/src/components.rs

260 lines
7.0 KiB
Rust

//! 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<dyn Component>) -> *mut EntropykComponent {
let boxed: Box<Box<dyn Component>> = 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<dyn Component> = 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<dyn Component>);
}
}
/// 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<dyn Component> = 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<dyn Component> = 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<dyn Component> = 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<dyn Component>);
}
}
/// 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<dyn Component> = 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<dyn Component> = Box::new(SimpleAdapter::new("Pipe", 1));
component_to_ptr(component)
}