260 lines
7.0 KiB
Rust
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)
|
|
}
|