Files
Entropyk/bindings/c/src/system.rs
Sepehr fa480ed303 feat: implement mass balance validation for Story 7.1
- Added port_mass_flows to Component trait and implements for core components.
- Added System::check_mass_balance and integrated it into the solver.
- Restored connect methods for ExpansionValve, Compressor, and Pipe to fix integration tests.
- Updated Python and C bindings for validation errors.
- Updated sprint status and story documentation.
2026-02-21 23:21:34 +01:00

200 lines
5.3 KiB
Rust

//! System lifecycle FFI functions.
//!
//! Provides opaque pointer wrappers for `entropyk_solver::System`.
use std::os::raw::c_uint;
use crate::components::EntropykComponent;
use crate::error::EntropykErrorCode;
/// Opaque handle to a thermodynamic system.
///
/// Create with `entropyk_system_create()`.
/// MUST call `entropyk_system_free()` when done.
#[repr(C)]
pub struct EntropykSystem {
_private: [u8; 0],
}
impl EntropykSystem {
fn from_inner(inner: entropyk_solver::System) -> *mut Self {
Box::into_raw(Box::new(inner)) as *mut Self
}
}
/// Create a new thermodynamic system.
///
/// # Returns
///
/// Pointer to the new system, or null on allocation failure.
///
/// # Ownership
///
/// Caller owns the returned pointer. MUST call `entropyk_system_free()` when done.
#[no_mangle]
pub extern "C" fn entropyk_system_create() -> *mut EntropykSystem {
let system = entropyk_solver::System::new();
EntropykSystem::from_inner(system)
}
/// Free a system created by `entropyk_system_create()`.
///
/// # Safety
///
/// - `system` must be a valid pointer returned by `entropyk_system_create()`, or null
/// - After this call, `system` is invalid and must not be used
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_free(system: *mut EntropykSystem) {
if !system.is_null() {
drop(Box::from_raw(system as *mut entropyk_solver::System));
}
}
/// Add a component to the system.
///
/// # Arguments
///
/// - `system`: The system (must not be null)
/// - `component`: The component to add (must not be null)
///
/// # Returns
///
/// Node index on success, or `UINT32_MAX` (0xFFFFFFFF) on error.
/// Use this index for `entropyk_system_add_edge()`.
///
/// # Safety
///
/// - `system` must be a valid pointer
/// - `component` must be a valid pointer
/// - After this call, `component` is consumed and must not be used again
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_add_component(
system: *mut EntropykSystem,
component: *mut EntropykComponent,
) -> c_uint {
const ERROR_INDEX: c_uint = u32::MAX;
if system.is_null() || component.is_null() {
return ERROR_INDEX;
}
let sys = &mut *(system as *mut entropyk_solver::System);
let comp_ptr = component as *mut Box<dyn entropyk_components::Component>;
let comp = Box::from_raw(comp_ptr);
let node_index = sys.add_component(*comp);
node_index.index() as c_uint
}
/// Add a flow edge from source to target node.
///
/// # Arguments
///
/// - `system`: The system (must not be null)
/// - `from`: Source node index (returned by `entropyk_system_add_component`)
/// - `to`: Target node index (returned by `entropyk_system_add_component`)
///
/// # Returns
///
/// - `ENTROPYK_OK` on success
/// - `ENTROPYK_NULL_POINTER` if system is null
/// - `ENTROPYK_TOPOLOGY_ERROR` if edge cannot be added
///
/// # Safety
///
/// `system` must be a valid pointer.
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_add_edge(
system: *mut EntropykSystem,
from: c_uint,
to: c_uint,
) -> EntropykErrorCode {
if system.is_null() {
return EntropykErrorCode::EntropykNullPointer;
}
let sys = &mut *(system as *mut entropyk_solver::System);
let src = petgraph::graph::NodeIndex::new(from as usize);
let tgt = petgraph::graph::NodeIndex::new(to as usize);
match sys.add_edge(src, tgt) {
Ok(_) => EntropykErrorCode::EntropykOk,
Err(e) => EntropykErrorCode::from(e),
}
}
/// Finalize the system for solving.
///
/// Must be called before any solve function.
///
/// # Arguments
///
/// - `system`: The system (must not be null)
///
/// # Returns
///
/// - `ENTROPYK_OK` on success
/// - `ENTROPYK_NULL_POINTER` if system is null
/// - `ENTROPYK_TOPOLOGY_ERROR` if topology is invalid
///
/// # Safety
///
/// `system` must be a valid pointer.
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_finalize(
system: *mut EntropykSystem,
) -> EntropykErrorCode {
if system.is_null() {
return EntropykErrorCode::EntropykNullPointer;
}
let sys = &mut *(system as *mut entropyk_solver::System);
match sys.finalize() {
Ok(_) => EntropykErrorCode::EntropykOk,
Err(e) => EntropykErrorCode::from(e),
}
}
/// Get the number of components (nodes) in the system.
///
/// # Safety
///
/// `system` must be a valid pointer or null.
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_node_count(system: *const EntropykSystem) -> c_uint {
if system.is_null() {
return 0;
}
let sys = &*(system as *const entropyk_solver::System);
sys.node_count() as c_uint
}
/// Get the number of edges in the system.
///
/// # Safety
///
/// `system` must be a valid pointer or null.
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_edge_count(system: *const EntropykSystem) -> c_uint {
if system.is_null() {
return 0;
}
let sys = &*(system as *const entropyk_solver::System);
sys.edge_count() as c_uint
}
/// Get the length of the state vector (after finalization).
///
/// # Safety
///
/// `system` must be a valid pointer or null.
#[no_mangle]
pub unsafe extern "C" fn entropyk_system_state_vector_len(system: *const EntropykSystem) -> c_uint {
if system.is_null() {
return 0;
}
let sys = &*(system as *const entropyk_solver::System);
sys.state_vector_len() as c_uint
}