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.
This commit is contained in:
199
bindings/c/src/system.rs
Normal file
199
bindings/c/src/system.rs
Normal file
@@ -0,0 +1,199 @@
|
||||
//! 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
|
||||
}
|
||||
Reference in New Issue
Block a user