use crate::errors::{BmiIndexOutOfBounds, BmiNotImplementedError};
use std::error::Error;
pub const MAX_COMPONENT_NAME: u32 = 2048;
pub const MAX_VAR_NAME: u32 = 2048;
pub const MAX_UNITS_NAME: u32 = 2048;
pub const MAX_TYPE_NAME: u32 = 2048;
#[derive(Debug, Clone, Copy)]
pub enum Location {
Node,
Edge,
Face,
}
impl std::fmt::Display for Location {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Location::Node => write!(f, "node"),
Location::Edge => write!(f, "edge"),
Location::Face => write!(f, "face"),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum GridType {
Scalar,
Points,
Vector,
Unstructured,
StructuredQuadrilateral,
Rectilinear,
UniformRectilinear,
}
impl std::fmt::Display for GridType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GridType::Scalar => write!(f, "scalar"),
GridType::Points => write!(f, "points"),
GridType::Vector => write!(f, "vector"),
GridType::Unstructured => write!(f, "unstructured"),
GridType::StructuredQuadrilateral => write!(f, "structured_quadrilateral"),
GridType::Rectilinear => write!(f, "rectilinear"),
GridType::UniformRectilinear => write!(f, "uniform_rectilinear"),
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum ValueType {
I16,
U16,
I32,
U32,
I64,
U64,
F32,
F64,
}
impl ValueType {
pub fn bytes(&self) -> usize {
match self {
ValueType::I16 | ValueType::U16 => 2,
ValueType::I32 | ValueType::U32 | ValueType::F32 => 4,
ValueType::I64 | ValueType::U64 | ValueType::F64 => 8,
}
}
}
#[derive(Debug, Clone)]
pub enum Values {
I16(Vec<i16>), U16(Vec<u16>), I32(Vec<i32>), U32(Vec<u32>), I64(Vec<i64>), U64(Vec<u64>), F32(Vec<f32>), F64(Vec<f64>), }
impl<'a> From<&'a Values> for RefValues<'a> {
fn from(value: &'a Values) -> Self {
match value {
Values::I16(items) => RefValues::I16(&items),
Values::U16(items) => RefValues::U16(&items),
Values::I32(items) => RefValues::I32(&items),
Values::U32(items) => RefValues::U32(&items),
Values::I64(items) => RefValues::I64(&items),
Values::U64(items) => RefValues::U64(&items),
Values::F32(items) => RefValues::F32(&items),
Values::F64(items) => RefValues::F64(&items),
}
}
}
macro_rules! impl_value_type {
($t:ty; $($name:ident),*$(,)?) => {
impl $t {
pub fn value_type(&self) -> ValueType {
match self {
$(Self::$name(_) => ValueType::$name,)*
}
}
}
};
}
macro_rules! impl_len {
($t:ty; $($name:ident),*$(,)?) => {
impl $t {
pub fn len(&self) -> usize {
match self {
$(Self::$name(v) => v.len(),)*
}
}
}
};
}
macro_rules! impl_from_vec_for_values {
($($name:ident; $t:ty),*$(,)?) => {
$(
impl From<Vec<$t>> for Values {
fn from(v: Vec<$t>) -> Self {
Values::$name(v)
}
}
)*
};
}
impl_from_vec_for_values!(
I16;i16,
U16;u16,
I32;i32,
U32;u32,
I64;i64,
U64;u64,
F32;f32,
F64;f64,
);
impl_value_type!(Values; I16, U16, I32, U32, I64, U64, F32, F64,);
impl_len!(Values; I16, U16, I32, U32, I64, U64, F32, F64,);
#[derive(Debug)]
pub enum RefValues<'a> {
I16(&'a [i16]), U16(&'a [u16]), I32(&'a [i32]), U32(&'a [u32]), I64(&'a [i64]), U64(&'a [u64]), F32(&'a [f32]), F64(&'a [f64]), }
macro_rules! impl_from_ref_t_for_ref_values {
($container:ident; $($name:ident; $t:ty),*$(,)?) => {
$(
impl<'a> From<&'a $container<$t>> for RefValues<'a> {
fn from(v: &'a Vec<$t>) -> Self {
RefValues::$name(v)
}
}
)*
};
($($name:ident; $t:ty),*$(,)?) => {
$(
impl<'a> From<&'a [$t]> for RefValues<'a> {
fn from(v: &'a [$t]) -> Self {
RefValues::$name(v)
}
}
)*
};
}
impl_from_ref_t_for_ref_values!(
Vec;
I16;i16,
U16;u16,
I32;i32,
U32;u32,
I64;i64,
U64;u64,
F32;f32,
F64;f64,
);
impl_from_ref_t_for_ref_values!(
I16;i16,
U16;u16,
I32;i32,
U32;u32,
I64;i64,
U64;u64,
F32;f32,
F64;f64,
);
impl_len!(RefValues<'_>; I16, U16, I32, U32, I64, U64, F32, F64,);
impl_value_type!(RefValues<'_>; I16, U16, I32, U32, I64, U64, F32, F64,);
pub type BmiResult<T> = Result<T, Box<dyn Error>>;
macro_rules! values_at_indices {
($t:ty, $inds:expr, $values:expr) => {{
let mut v = Vec::<$t>::with_capacity($inds.len());
for i in $inds {
if *i >= $values.len() as u32 {
return Err(Box::new(BmiIndexOutOfBounds));
}
v.push($values[*i as usize]);
}
Ok(Values::from(v))
}};
}
pub trait Bmi {
fn initialize(&mut self, config_file: &str) -> BmiResult<()>;
fn update(&mut self) -> BmiResult<()>;
fn update_until(&mut self, then: f64) -> BmiResult<()>;
fn finalize(&mut self) -> BmiResult<()>;
fn get_component_name(&self) -> &str;
fn get_input_item_count(&self) -> u32 {
self.get_input_var_names().len() as u32
}
fn get_output_item_count(&self) -> u32 {
self.get_output_var_names().len() as u32
}
fn get_input_var_names(&self) -> &[&str];
fn get_output_var_names(&self) -> &[&str];
fn get_var_grid(&self, name: &str) -> BmiResult<i32>;
fn get_var_type(&self, name: &str) -> BmiResult<ValueType>;
fn get_var_units(&self, name: &str) -> BmiResult<&str>;
fn get_var_itemsize(&self, name: &str) -> BmiResult<u32> {
Ok(self.get_var_type(name)?.bytes() as u32)
}
fn get_var_nbytes(&self, name: &str) -> BmiResult<u32>;
fn get_var_location(&self, name: &str) -> BmiResult<Location>;
fn get_current_time(&self) -> f64;
fn get_start_time(&self) -> f64 {
0.
}
fn get_end_time(&self) -> f64 {
f64::MAX
}
fn get_time_units(&self) -> &str;
fn get_time_step(&self) -> f64;
fn get_value_ptr(&self, name: &str) -> BmiResult<RefValues<'_>>;
fn get_value_at_indices(&self, name: &str, inds: &[u32]) -> BmiResult<Values> {
match self.get_value_ptr(name)? {
RefValues::I16(items) => values_at_indices!(i16, inds, items),
RefValues::U16(items) => values_at_indices!(u16, inds, items),
RefValues::I32(items) => values_at_indices!(i32, inds, items),
RefValues::U32(items) => values_at_indices!(u32, inds, items),
RefValues::I64(items) => values_at_indices!(i64, inds, items),
RefValues::U64(items) => values_at_indices!(u64, inds, items),
RefValues::F32(items) => values_at_indices!(f32, inds, items),
RefValues::F64(items) => values_at_indices!(f64, inds, items),
}
}
fn set_value(&mut self, name: &str, src: RefValues) -> BmiResult<()>;
fn set_value_at_indices(&mut self, name: &str, inds: &[u32], src: RefValues) -> BmiResult<()>;
#[allow(unused_variables)]
fn get_grid_type(&self, grid: i32) -> BmiResult<GridType> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_rank(&self, grid: i32) -> BmiResult<u32> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_size(&self, grid: i32) -> BmiResult<u32> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_shape(&self, grid: i32) -> BmiResult<&[u32]> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_spacing(&self, grid: i32) -> BmiResult<&[f64]> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_origin(&self, grid: i32) -> BmiResult<&[f64]> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_x(&self, grid: i32) -> BmiResult<&[f64]> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_y(&self, grid: i32) -> BmiResult<&[f64]> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_z(&self, grid: i32) -> BmiResult<&[f64]> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_node_count(&self, grid: i32) -> BmiResult<u32> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_edge_count(&self, grid: i32) -> BmiResult<u32> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_face_count(&self, grid: i32) -> BmiResult<u32> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_edge_nodes(&self, grid: i32) -> BmiResult<&[u32]> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_face_edges(&self, grid: i32) -> BmiResult<&[u32]> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_face_nodes(&self, grid: i32) -> BmiResult<&[u32]> {
BmiNotImplementedError.into()
}
#[allow(unused_variables)]
fn get_grid_nodes_per_face(&self, grid: i32) -> BmiResult<&[u32]> {
BmiNotImplementedError.into()
}
}
pub fn register_model<T: Bmi>(handle: *mut ffi::Bmi, model: T) {
assert!(!handle.is_null(), "pointer is null");
let handle: &mut ffi::Bmi = unsafe { handle.as_mut() }.unwrap();
setup_fn_ptrs::<T>(handle);
let data: Box<T> = Box::new(model);
let data = Box::into_raw(data);
handle.data = data as *mut std::ffi::c_void;
}
fn setup_fn_ptrs<T: Bmi>(handle: &mut ffi::Bmi) {
handle.initialize = Some(crate::wrapper::initialize::<T>);
handle.update = Some(crate::wrapper::update::<T>);
handle.update_until = Some(crate::wrapper::update_until::<T>);
handle.finalize = Some(crate::wrapper::finalize::<T>);
handle.get_component_name = Some(crate::wrapper::get_component_name::<T>);
handle.get_input_item_count = Some(crate::wrapper::get_input_item_count::<T>);
handle.get_output_item_count = Some(crate::wrapper::get_output_item_count::<T>);
handle.get_input_var_names = Some(crate::wrapper::get_input_var_names::<T>);
handle.get_output_var_names = Some(crate::wrapper::get_output_var_names::<T>);
handle.get_var_grid = Some(crate::wrapper::get_var_grid::<T>);
handle.get_var_type = Some(crate::wrapper::get_var_type::<T>);
handle.get_var_units = Some(crate::wrapper::get_var_units::<T>);
handle.get_var_itemsize = Some(crate::wrapper::get_var_itemsize::<T>);
handle.get_var_nbytes = Some(crate::wrapper::get_var_nbytes::<T>);
handle.get_var_location = Some(crate::wrapper::get_var_location::<T>);
handle.get_current_time = Some(crate::wrapper::get_current_time::<T>);
handle.get_start_time = Some(crate::wrapper::get_start_time::<T>);
handle.get_end_time = Some(crate::wrapper::get_end_time::<T>);
handle.get_time_units = Some(crate::wrapper::get_time_units::<T>);
handle.get_time_step = Some(crate::wrapper::get_time_step::<T>);
handle.get_value = Some(crate::wrapper::get_value::<T>);
handle.get_value_ptr = Some(crate::wrapper::get_value_ptr::<T>);
handle.get_value_at_indices = Some(crate::wrapper::get_value_at_indices::<T>);
handle.set_value = Some(crate::wrapper::set_value::<T>);
handle.set_value_at_indices = Some(crate::wrapper::set_value_at_indices::<T>);
handle.get_grid_rank = Some(crate::wrapper::get_grid_rank::<T>);
handle.get_grid_size = Some(crate::wrapper::get_grid_size::<T>);
handle.get_grid_type = Some(crate::wrapper::get_grid_type::<T>);
handle.get_grid_shape = Some(crate::wrapper::get_grid_shape::<T>);
handle.get_grid_spacing = Some(crate::wrapper::get_grid_spacing::<T>);
handle.get_grid_origin = Some(crate::wrapper::get_grid_origin::<T>);
handle.get_grid_x = Some(crate::wrapper::get_grid_x::<T>);
handle.get_grid_y = Some(crate::wrapper::get_grid_y::<T>);
handle.get_grid_z = Some(crate::wrapper::get_grid_z::<T>);
handle.get_grid_node_count = Some(crate::wrapper::get_grid_node_count::<T>);
handle.get_grid_edge_count = Some(crate::wrapper::get_grid_edge_count::<T>);
handle.get_grid_face_count = Some(crate::wrapper::get_grid_face_count::<T>);
handle.get_grid_edge_nodes = Some(crate::wrapper::get_grid_edge_nodes::<T>);
handle.get_grid_face_edges = Some(crate::wrapper::get_grid_face_edges::<T>);
handle.get_grid_face_nodes = Some(crate::wrapper::get_grid_face_nodes::<T>);
handle.get_grid_nodes_per_face = Some(crate::wrapper::get_grid_nodes_per_face::<T>);
}
#[cfg(test)]
mod tests {
use super::*;
fn case(vs: &[u16], idx: &[u32]) -> Result<Values, Box<dyn Error>> {
values_at_indices!(u16, idx, vs)
}
#[test]
fn test_empty() {
let vs: [u16; 0] = [];
let inds: [u32; 0] = [];
assert!(case(&vs, &inds).is_ok());
let vs: [u16; 1] = [42];
let inds: [u32; 0] = [];
assert!(case(&vs, &inds).is_ok());
}
#[test]
fn test_one() {
let vs: [u16; 1] = [42];
let inds: [u32; 1] = [0];
match case(&vs, &inds) {
Ok(values) => match values {
Values::U16(values) => {
let i = inds[0] as usize;
assert_eq!(values[i], vs[i]);
}
_ => assert!(false),
},
_ => assert!(false),
}
}
#[test]
fn test_many() {
let vs: [u16; 2] = [0, 1];
let inds: [u32; 2] = [0, 1];
match case(&vs, &inds) {
Ok(values) => match values {
Values::U16(values) => {
for i in &inds {
let i = *i as usize;
assert_eq!(values[i], vs[i]);
}
}
_ => assert!(false),
},
_ => assert!(false),
}
}
#[test]
fn test_out_of_bounds() {
let vs: [u16; 0] = [];
let inds: [u32; 1] = [1];
match case(&vs, &inds) {
Err(err) => {
assert!(err.is::<crate::errors::BmiIndexOutOfBounds>());
}
_ => assert!(false),
}
}
}