bmi_rs/bmi.rs
1use crate::errors::{BmiIndexOutOfBounds, BmiNotImplementedError};
2use std::error::Error;
3
4pub const MAX_COMPONENT_NAME: u32 = 2048;
5pub const MAX_VAR_NAME: u32 = 2048;
6pub const MAX_UNITS_NAME: u32 = 2048;
7pub const MAX_TYPE_NAME: u32 = 2048;
8
9/// Bmi variable grid
10/// [element location](https://bmi.csdms.io/en/stable/bmi.var_funcs.html#get-var-location).
11#[derive(Debug, Clone, Copy)]
12pub enum Location {
13 Node,
14 Edge,
15 Face,
16}
17
18impl std::fmt::Display for Location {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 match self {
21 Location::Node => write!(f, "node"),
22 Location::Edge => write!(f, "edge"),
23 Location::Face => write!(f, "face"),
24 }
25 }
26}
27
28/// Bmi
29/// [grid type](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-type).
30#[derive(Debug, Clone, Copy)]
31pub enum GridType {
32 Scalar,
33 Points,
34 Vector,
35 Unstructured,
36 StructuredQuadrilateral,
37 Rectilinear,
38 UniformRectilinear,
39}
40
41impl std::fmt::Display for GridType {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 match self {
44 GridType::Scalar => write!(f, "scalar"),
45 GridType::Points => write!(f, "points"),
46 GridType::Vector => write!(f, "vector"),
47 GridType::Unstructured => write!(f, "unstructured"),
48 GridType::StructuredQuadrilateral => write!(f, "structured_quadrilateral"),
49 GridType::Rectilinear => write!(f, "rectilinear"),
50 GridType::UniformRectilinear => write!(f, "uniform_rectilinear"),
51 }
52 }
53}
54
55// TODO: how to add isize and usize?
56/// Represents the numeric data type of an item in a [`Bmi`] variable's array.
57#[derive(Debug, Copy, Clone)]
58pub enum ValueType {
59 /// signed 16 bit int
60 I16,
61 /// unsigned 16 bit int
62 U16,
63 /// signed 32 bit int
64 I32,
65 /// unsigned 32 bit int
66 U32,
67 /// signed 64 bit int
68 I64,
69 /// unsigned 64 bit int
70 U64,
71 /// signed 32 bit float
72 F32,
73 /// signed 64 bit float
74 F64,
75}
76
77impl ValueType {
78 /// Return the size in bytes of the variant's analogous numeric type.
79 pub fn bytes(&self) -> usize {
80 match self {
81 ValueType::I16 | ValueType::U16 => 2,
82 ValueType::I32 | ValueType::U32 | ValueType::F32 => 4,
83 ValueType::I64 | ValueType::U64 | ValueType::F64 => 8,
84 }
85 }
86}
87
88// NOTE: consider a more generic container type than Vec<T>, maybe Box<[T]>?
89/// An owned `Vec` of a numeric type wrapped with type information.
90#[derive(Debug, Clone)]
91pub enum Values {
92 I16(Vec<i16>), // short
93 U16(Vec<u16>), // unsigned short
94 I32(Vec<i32>), // usually int
95 U32(Vec<u32>), // usually unsigned int
96 I64(Vec<i64>), // long or usually long long
97 U64(Vec<u64>), // unsigned long or usually unsigned long long
98 F32(Vec<f32>), // float
99 F64(Vec<f64>), // double
100}
101
102impl<'a> From<&'a Values> for RefValues<'a> {
103 fn from(value: &'a Values) -> Self {
104 match value {
105 Values::I16(items) => RefValues::I16(&items),
106 Values::U16(items) => RefValues::U16(&items),
107 Values::I32(items) => RefValues::I32(&items),
108 Values::U32(items) => RefValues::U32(&items),
109 Values::I64(items) => RefValues::I64(&items),
110 Values::U64(items) => RefValues::U64(&items),
111 Values::F32(items) => RefValues::F32(&items),
112 Values::F64(items) => RefValues::F64(&items),
113 }
114 }
115}
116
117macro_rules! impl_value_type {
118 ($t:ty; $($name:ident),*$(,)?) => {
119 impl $t {
120 pub fn value_type(&self) -> ValueType {
121 match self {
122 $(Self::$name(_) => ValueType::$name,)*
123 }
124 }
125 }
126 };
127}
128
129macro_rules! impl_len {
130 ($t:ty; $($name:ident),*$(,)?) => {
131 impl $t {
132 pub fn len(&self) -> usize {
133 match self {
134 $(Self::$name(v) => v.len(),)*
135 }
136 }
137 }
138 };
139}
140
141macro_rules! impl_from_vec_for_values {
142 ($($name:ident; $t:ty),*$(,)?) => {
143 $(
144 impl From<Vec<$t>> for Values {
145 fn from(v: Vec<$t>) -> Self {
146 Values::$name(v)
147 }
148 }
149 )*
150 };
151}
152
153impl_from_vec_for_values!(
154 I16;i16,
155 U16;u16,
156 I32;i32,
157 U32;u32,
158 I64;i64,
159 U64;u64,
160 F32;f32,
161 F64;f64,
162);
163impl_value_type!(Values; I16, U16, I32, U32, I64, U64, F32, F64,);
164impl_len!(Values; I16, U16, I32, U32, I64, U64, F32, F64,);
165
166// See: https://github.com/NOAA-OWP/ngen/blob/52f43540239e202328c7c9350149f9f5b8f1f409/include/realizations/catchment/Bmi_Module_Formulation.hpp#L779
167/// A ref to a slice of numerics wrapped with type information.
168#[derive(Debug)]
169pub enum RefValues<'a> {
170 I16(&'a [i16]), // short
171 U16(&'a [u16]), // unsigned short
172 I32(&'a [i32]), // usually int
173 U32(&'a [u32]), // usually unsigned int
174 I64(&'a [i64]), // long or usually long long
175 U64(&'a [u64]), // unsigned long or usually unsigned long long
176 F32(&'a [f32]), // float
177 F64(&'a [f64]), // double
178}
179
180macro_rules! impl_from_ref_t_for_ref_values {
181 ($container:ident; $($name:ident; $t:ty),*$(,)?) => {
182 $(
183 impl<'a> From<&'a $container<$t>> for RefValues<'a> {
184 fn from(v: &'a Vec<$t>) -> Self {
185 RefValues::$name(v)
186 }
187 }
188 )*
189 };
190 ($($name:ident; $t:ty),*$(,)?) => {
191 $(
192 impl<'a> From<&'a [$t]> for RefValues<'a> {
193 fn from(v: &'a [$t]) -> Self {
194 RefValues::$name(v)
195 }
196 }
197 )*
198 };
199}
200impl_from_ref_t_for_ref_values!(
201 Vec;
202 I16;i16,
203 U16;u16,
204 I32;i32,
205 U32;u32,
206 I64;i64,
207 U64;u64,
208 F32;f32,
209 F64;f64,
210);
211
212impl_from_ref_t_for_ref_values!(
213 I16;i16,
214 U16;u16,
215 I32;i32,
216 U32;u32,
217 I64;i64,
218 U64;u64,
219 F32;f32,
220 F64;f64,
221);
222
223impl_len!(RefValues<'_>; I16, U16, I32, U32, I64, U64, F32, F64,);
224impl_value_type!(RefValues<'_>; I16, U16, I32, U32, I64, U64, F32, F64,);
225
226pub type BmiResult<T> = Result<T, Box<dyn Error>>;
227
228macro_rules! values_at_indices {
229 ($t:ty, $inds:expr, $values:expr) => {{
230 let mut v = Vec::<$t>::with_capacity($inds.len());
231 for i in $inds {
232 if *i >= $values.len() as u32 {
233 return Err(Box::new(BmiIndexOutOfBounds));
234 }
235 v.push($values[*i as usize]);
236 }
237 Ok(Values::from(v))
238 }};
239}
240
241/// [CSDMS Basic Model Interface (BMI)](https://bmi.csdms.io/en/latest/index.html)
242/// _like_ trait.
243///
244/// Types that implement this trait can be exposed over the
245/// [bmi-c interface](https://github.com/csdms/bmi-c) via
246/// [`register_model`].
247pub trait Bmi {
248 /// [`Bmi`] implementations should perform the majority of tasks that are to take place before
249 /// entering the model’s time loop in this method.
250 /// Exceptions to this are [`Bmi`] implementations that expose model parameters settable for
251 /// configuration or calibration purposes.
252 ///
253 /// Code using [`Bmi`] implementations are expected to call the implementation's `initialize`
254 /// member before any other [`Bmi`] trait members.
255 ///
256 /// See
257 /// [csdms bmi `initialize`](https://bmi.csdms.io/en/stable/bmi.control_funcs.html#initialize)
258 /// docs for more info.
259 fn initialize(&mut self, config_file: &str) -> BmiResult<()>;
260
261 /// Advance the model by a single [`get_time_step`] sized time step.
262 ///
263 /// See
264 /// [csdms bmi `update`](https://bmi.csdms.io/en/stable/bmi.control_funcs.html#update)
265 /// docs for more info.
266 ///
267 /// [`get_time_step`]: #tymethod.get_time_step
268 fn update(&mut self) -> BmiResult<()>;
269
270 // TODO: consider using something like Chrono instead of f64
271 /// Advance the model to the time at `then`.
272 /// Once called, the value returned by the [`get_current_time`] function must return the
273 /// provided time to reflect that the model was updated to the requested time.
274 ///
275 /// See
276 /// [csdms bmi `update_until`](https://bmi.csdms.io/en/stable/bmi.control_funcs.html#update-until)
277 /// docs for more info.
278 ///
279 /// [`get_current_time`]: #tymethod.get_current_time
280 fn update_until(&mut self, then: f64) -> BmiResult<()>;
281
282 /// Perform any necessary tasks after exiting the model’s time loop.
283 /// Note, the implementing type is not consumed and therefore not dropped.
284 ///
285 /// Code using [`Bmi`] implementations are expected to call the implementation's [`finalize`]
286 /// member as the last _[`Bmi`]_ interaction with the implementing type.
287 ///
288 /// FFI methods that wrap [`finalize`] _should_ drop the implementing type.
289 ///
290 /// See
291 /// [csdms bmi `finalize`](https://bmi.csdms.io/en/stable/bmi.control_funcs.html#finalize)
292 /// docs for more info.
293 ///
294 /// [`finalize`]: #tymethod.finalize
295 fn finalize(&mut self) -> BmiResult<()>;
296
297 /* Exchange items */
298 /// Return the model's name.
299 ///
300 /// See
301 /// [csdms bmi `get_component_name`](https://bmi.csdms.io/en/stable/bmi.info_funcs.html#get-component-name)
302 /// docs for more info.
303 fn get_component_name(&self) -> &str;
304
305 /// Return the number of model _input variables_ settable via [`set_value`].
306 /// The count is given by the length of slice returned by [`get_input_var_names`].
307 ///
308 /// Note, [`Bmi`] implementations that expose model parameters settable strictly for
309 /// configuration or calibration purposes should not include these parameters in their
310 /// [`get_input_item_count`] count.
311 ///
312 /// See
313 /// [csdms bmi `get_input_item_count`](https://bmi.csdms.io/en/stable/bmi.info_funcs.html#get-input-item-count)
314 /// docs for more info.
315 ///
316 /// [`set_value`]: #tymethod.set_value
317 /// [`get_input_var_names`]: #tymethod.get_input_var_names
318 /// [`get_input_item_count`]: #tymethod.get_input_item_count
319 fn get_input_item_count(&self) -> u32 {
320 self.get_input_var_names().len() as u32
321 }
322
323 /// Return the number of _model output_ variables retrievable via [`get_value_ptr`].
324 /// The count is given by the length of slice returned by [`get_output_var_names`].
325 ///
326 /// See
327 /// [csdms bmi `get_output_item_count`](https://bmi.csdms.io/en/stable/bmi.info_funcs.html#get-output-item-count)
328 /// docs for more info.
329 ///
330 /// [`get_value_ptr`]: #tymethod.get_value_ptr
331 /// [`get_output_var_names`]: #tymethod.get_output_var_names
332 fn get_output_item_count(&self) -> u32 {
333 self.get_output_var_names().len() as u32
334 }
335
336 /// Return the implementing model's input variable names.
337 /// The length of the array is given by [`get_input_item_count`].
338 ///
339 /// Names are preferably in the form of
340 /// [CSDMS Standard Names](https://csdms.colorado.edu/wiki/CSDMS_Standard_Names).
341 ///
342 /// See
343 /// [csdms bmi `get_input_var_names`](https://bmi.csdms.io/en/stable/bmi.info_funcs.html#get-input-var-names)
344 /// docs for more info.
345 ///
346 /// [`get_input_item_count`]: #tymethod.get_input_item_count
347 fn get_input_var_names(&self) -> &[&str];
348
349 /// Return the implementing model's output variable names.
350 /// The length of the array is given by [`get_output_item_count`].
351 ///
352 /// See
353 /// [csdms bmi `get_output_var_names`](https://bmi.csdms.io/en/stable/bmi.info_funcs.html#get-output-var-names)
354 /// docs for more info.
355 ///
356 /// [`get_output_item_count`]: #tymethod.get_output_item_count
357 fn get_output_var_names(&self) -> &[&str];
358
359 /* Variable information */
360 /// Return the input or output variable's grid type.
361 ///
362 /// See
363 /// [csdms bmi `get_var_grid`](https://bmi.csdms.io/en/stable/bmi.var_funcs.html#get-var-grid)
364 /// docs for more info.
365 fn get_var_grid(&self, name: &str) -> BmiResult<i32>;
366
367 /// Return a variable's inner type.
368 ///
369 /// See
370 /// [csdms bmi `get_var_type`](https://bmi.csdms.io/en/stable/bmi.var_funcs.html#get-var-type)
371 /// docs for more info.
372 fn get_var_type(&self, name: &str) -> BmiResult<ValueType>;
373
374 /// Return a variable's units.
375 /// Units should be returned in lower case in
376 /// [UDUNIT2 format](https://docs.unidata.ucar.edu/udunits/current/#Database).
377 /// For example, `"meters"` / `"m"` or `"seconds"` / `"s"`.
378 ///
379 /// See
380 /// [csdms bmi `get_var_units`](https://bmi.csdms.io/en/stable/bmi.var_funcs.html#get-var-units)
381 /// docs for more info.
382 fn get_var_units(&self, name: &str) -> BmiResult<&str>;
383
384 /// Return the size, in bytes, of a single element of the variable.
385 /// For example, a variable stored as a `Vec<f64>` has an itemsize of 8.
386 ///
387 /// See
388 /// [csdms bmi `get_var_itemsize`](https://bmi.csdms.io/en/stable/bmi.var_funcs.html#get-var-itemsize)
389 /// docs for more info.
390 fn get_var_itemsize(&self, name: &str) -> BmiResult<u32> {
391 Ok(self.get_var_type(name)?.bytes() as u32)
392 }
393
394 /// Return the total number of bytes used to store a variable.
395 /// i.e., the number of items multiplied by the size of each item.
396 ///
397 /// See
398 /// [csdms bmi `get_var_nbytes`](https://bmi.csdms.io/en/stable/bmi.var_funcs.html#get-var-nbytes)
399 /// docs for more info.
400 fn get_var_nbytes(&self, name: &str) -> BmiResult<u32>;
401
402 /// Return a variable's grid element type.
403 ///
404 /// See
405 /// [csdms bmi `get_var_location`](https://bmi.csdms.io/en/stable/bmi.var_funcs.html#get-var-location)
406 /// docs for more info.
407 fn get_var_location(&self, name: &str) -> BmiResult<Location>;
408
409 /* Time information */
410 /// Return the model's current time.
411 ///
412 /// See
413 /// [csdms bmi `get_current_time`](https://bmi.csdms.io/en/stable/bmi.time_funcs.html#get-current-time)
414 /// docs for more info.
415 fn get_current_time(&self) -> f64;
416
417 /// Return the model's simulation start time.
418 /// Default: `0.0`.
419 ///
420 /// Note, the start time is typically `0.0`.
421 ///
422 /// See
423 /// [csdms bmi `get_start_time`](https://bmi.csdms.io/en/stable/bmi.time_funcs.html#get-start-time)
424 /// docs for more info.
425 fn get_start_time(&self) -> f64 {
426 0.
427 }
428
429 /// Return the model's simulation end time.
430 /// Default: [`f64::MAX`].
431 ///
432 /// Note, if an end time does not conceptually make sense, [`f64::MAX`] should be used.
433 ///
434 /// See
435 /// [csdms bmi `get_end_time`](https://bmi.csdms.io/en/stable/bmi.time_funcs.html#get-end-time)
436 /// docs for more info.
437 ///
438 /// [`f64::MAX`]: https://doc.rust-lang.org/std/primitive.f64.html#associatedconstant.MAX
439 fn get_end_time(&self) -> f64 {
440 f64::MAX
441 }
442
443 /// Return the model's time unit.
444 ///
445 /// Units in CF conventions are recommended.
446 ///
447 /// e.g. `s` | `sec` | `second`, `min` | `minute`, `h` | `hr` | `hour`, or `d` | `day`.
448 ///
449 /// See
450 /// [csdms bmi `get_time_units`](https://bmi.csdms.io/en/stable/bmi.time_funcs.html#get-time-units)
451 /// docs for more info.
452 fn get_time_units(&self) -> &str;
453
454 /// Return the model's time step size in [`get_time_units`] units.
455 ///
456 /// See
457 /// [csdms bmi `get_time_step`](https://bmi.csdms.io/en/stable/bmi.time_funcs.html#get-time-step)
458 /// docs for more info.
459 ///
460 /// [`get_time_units`]: #tymethod.get_time_units
461 fn get_time_step(&self) -> f64;
462
463 /* Getters */
464 /// Return a reference to a flattened slice of values for a given variable.
465 ///
466 /// Note, [`Bmi`] does not include the BMI `get_value` method in its method set.
467 /// This may change in the future.
468 /// Likewise, the return type of [`get_value_ptr`] may change in future versions.
469 /// See discussion in [#3](https://github.com/aaraney/bmi-rs/issues/3).
470 ///
471 /// See
472 /// [csdms bmi `get_value_ptr`](https://bmi.csdms.io/en/stable/bmi.getter_setter.html#get-value-ptr)
473 /// docs for more info.
474 ///
475 /// [`get_value_ptr`]: #tymethod.get_value_ptr
476 fn get_value_ptr(&self, name: &str) -> BmiResult<RefValues<'_>>;
477
478 /// Return an owned copy of a variable’s values at the `inds` specified.
479 ///
480 /// Note, the default implementation copies from values via [`get_value_ptr`].
481 ///
482 /// See
483 /// [csdms bmi `get_value_at_indices`](https://bmi.csdms.io/en/stable/bmi.getter_setter.html#get-value-at-indices)
484 /// docs for more info.
485 ///
486 /// [`get_value_ptr`]: #tymethod.get_value_ptr
487 fn get_value_at_indices(&self, name: &str, inds: &[u32]) -> BmiResult<Values> {
488 match self.get_value_ptr(name)? {
489 RefValues::I16(items) => values_at_indices!(i16, inds, items),
490 RefValues::U16(items) => values_at_indices!(u16, inds, items),
491 RefValues::I32(items) => values_at_indices!(i32, inds, items),
492 RefValues::U32(items) => values_at_indices!(u32, inds, items),
493 RefValues::I64(items) => values_at_indices!(i64, inds, items),
494 RefValues::U64(items) => values_at_indices!(u64, inds, items),
495 RefValues::F32(items) => values_at_indices!(f32, inds, items),
496 RefValues::F64(items) => values_at_indices!(f64, inds, items),
497 }
498 }
499
500 /* Setters */
501 /// Copy values from `src` into the model's `name` variable.
502 /// `src`'s [`RefValues`] variant and slice length _must_ match the analogous type _and_ length of
503 /// the model's internal `name` variable.
504 /// For example, if `src` is a [`RefValues::F64`] an _item_ in model's `name` variable array
505 /// must be an `f64`.
506 ///
507 /// The type and length of a model's variable can be determined through calls to
508 /// [`get_var_type`] and [`get_var_nbytes`].
509 ///
510 /// See
511 /// [csdms bmi `set_value`](https://bmi.csdms.io/en/stable/bmi.getter_setter.html#set-value)
512 /// docs for more info.
513 ///
514 /// [`get_var_type`]: #tymethod.get_var_type
515 /// [`get_var_nbytes`]: #tymethod.get_var_nbytes
516 fn set_value(&mut self, name: &str, src: RefValues) -> BmiResult<()>;
517
518 /// Copy values from `src` into the model's `name` variable at the provided `inds` indices.
519 ///
520 /// See
521 /// [csdms bmi `set_value_at_indices`](https://bmi.csdms.io/en/stable/bmi.getter_setter.html#set-value-at-indices)
522 /// docs for more info.
523 fn set_value_at_indices(&mut self, name: &str, inds: &[u32], src: RefValues) -> BmiResult<()>;
524
525 // NOTE: can we implement a default here?
526 /// Return the [`GridType`] for a given grid identifier.
527 ///
528 /// Default implementation returns Err([`BmiNotImplementedError`]).
529 ///
530 /// See
531 /// [csdms bmi `get_grid_type`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-type)
532 /// docs for more info.
533 #[allow(unused_variables)]
534 fn get_grid_type(&self, grid: i32) -> BmiResult<GridType> {
535 BmiNotImplementedError.into()
536 }
537
538 /* Grid information */
539 /// Return the grid
540 /// [rank](https://bmi.csdms.io/en/stable/glossary.html#term-rank)
541 /// for a given grid identifier.
542 ///
543 /// This function is needed for every
544 /// [grid type](https://bmi.csdms.io/en/stable/model_grids.html#model-grids).
545 /// Default implementation returns Err([`BmiNotImplementedError`]).
546 ///
547 /// See
548 /// [csdms bmi `get_grid_rank`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-rank)
549 /// docs for more info.
550 #[allow(unused_variables)]
551 fn get_grid_rank(&self, grid: i32) -> BmiResult<u32> {
552 BmiNotImplementedError.into()
553 }
554
555 /// Return the total number of elements (or
556 /// [nodes](https://bmi.csdms.io/en/stable/glossary.html#term-node)
557 /// ) for a given grid identifier.
558 ///
559 /// This function is needed for every
560 /// [grid type](https://bmi.csdms.io/en/stable/model_grids.html#model-grids).
561 ///
562 /// Default implementation returns Err([`BmiNotImplementedError`]).
563 ///
564 /// See
565 /// [csdms bmi `get_grid_size`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-size)
566 /// docs for more info.
567 #[allow(unused_variables)]
568 fn get_grid_size(&self, grid: i32) -> BmiResult<u32> {
569 BmiNotImplementedError.into()
570 }
571
572 /* Uniform rectilinear */
573 /// Return the dimensions of the model grid for a given a grid identifier.
574 /// The length of the returned slice is [`get_grid_rank`] long.
575 ///
576 /// This function is used for describing all
577 /// [structured grids](https://bmi.csdms.io/en/stable/model_grids.html#structured-grids).
578 ///
579 /// Default implementation returns Err([`BmiNotImplementedError`]).
580 ///
581 /// See
582 /// [csdms bmi `get_grid_shape`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-shape)
583 /// docs for more info.
584 ///
585 /// [`get_grid_rank`]: #tymethod.get_grid_rank
586 #[allow(unused_variables)]
587 fn get_grid_shape(&self, grid: i32) -> BmiResult<&[u32]> {
588 BmiNotImplementedError.into()
589 }
590
591 /// Return the distance between the
592 /// [nodes](https://bmi.csdms.io/en/stable/glossary.html#term-node)
593 /// of the model grid.
594 ///
595 /// This function is used for describing
596 /// [uniform rectilinear](https://bmi.csdms.io/en/stable/model_grids.html#uniform-rectilinear)
597 /// grids.
598 ///
599 /// Default implementation returns Err([`BmiNotImplementedError`]).
600 ///
601 /// See
602 /// [csdms bmi `get_grid_spacing`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-spacing)
603 /// docs for more info.
604 #[allow(unused_variables)]
605 fn get_grid_spacing(&self, grid: i32) -> BmiResult<&[f64]> {
606 BmiNotImplementedError.into()
607 }
608
609 /// Return the coordinates of the lower-left corner of the model grid.
610 ///
611 /// This function is used for describing
612 /// [uniform rectilinear](https://bmi.csdms.io/en/stable/model_grids.html#uniform-rectilinear)
613 /// grids.
614 ///
615 /// Default implementation returns Err([`BmiNotImplementedError`]).
616 ///
617 /// See
618 /// [csdms bmi `get_grid_origin`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-origin)
619 /// docs for more info.
620 #[allow(unused_variables)]
621 fn get_grid_origin(&self, grid: i32) -> BmiResult<&[f64]> {
622 BmiNotImplementedError.into()
623 }
624
625 /* Non-uniform rectilinear, curvilinear */
626 /// Return locations of the grid
627 /// [nodes](https://bmi.csdms.io/en/stable/glossary.html#term-node)
628 /// in the first coordinate direction.
629 ///
630 /// The length of the resulting one-dimensional array depends on the grid type.
631 ///
632 /// This function is used for describing
633 /// [rectilinear](https://bmi.csdms.io/en/stable/model_grids.html#rectilinear),
634 /// [structured quadrilateral](https://bmi.csdms.io/en/stable/model_grids.html#structured-quad),
635 /// and all
636 /// [unstructured](https://bmi.csdms.io/en/stable/model_grids.html#unstructured-grids)
637 /// grids.
638 ///
639 /// Default implementation returns Err([`BmiNotImplementedError`]).
640 ///
641 /// See
642 /// [csdms bmi `get_grid_rank`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-x)
643 /// docs for more info.
644 #[allow(unused_variables)]
645 fn get_grid_x(&self, grid: i32) -> BmiResult<&[f64]> {
646 BmiNotImplementedError.into()
647 }
648
649 /// Return locations of the grid
650 /// [nodes](https://bmi.csdms.io/en/stable/glossary.html#term-node)
651 /// in the second coordinate direction.
652 ///
653 /// The length of the resulting one-dimensional array depends on the grid type.
654 ///
655 /// This function is used for describing
656 /// [rectilinear](https://bmi.csdms.io/en/stable/model_grids.html#rectilinear),
657 /// [structured quadrilateral](https://bmi.csdms.io/en/stable/model_grids.html#structured-quad),
658 /// and all
659 /// [unstructured](https://bmi.csdms.io/en/stable/model_grids.html#unstructured-grids)
660 /// grids.
661 ///
662 /// Default implementation returns Err([`BmiNotImplementedError`]).
663 ///
664 /// See
665 /// [csdms bmi `get_grid_rank`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-y)
666 /// docs for more info.
667 #[allow(unused_variables)]
668 fn get_grid_y(&self, grid: i32) -> BmiResult<&[f64]> {
669 BmiNotImplementedError.into()
670 }
671
672 /// Return locations of the grid
673 /// [nodes](https://bmi.csdms.io/en/stable/glossary.html#term-node)
674 /// in the third coordinate direction.
675 ///
676 /// The length of the resulting one-dimensional array depends on the grid type.
677 ///
678 /// This function is used for describing
679 /// [rectilinear](https://bmi.csdms.io/en/stable/model_grids.html#rectilinear),
680 /// [structured quadrilateral](https://bmi.csdms.io/en/stable/model_grids.html#structured-quad),
681 /// and all
682 /// [unstructured](https://bmi.csdms.io/en/stable/model_grids.html#unstructured-grids)
683 /// grids.
684 ///
685 /// Default implementation returns Err([`BmiNotImplementedError`]).
686 ///
687 /// See
688 /// [csdms bmi `get_grid_rank`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-z)
689 /// docs for more info.
690 #[allow(unused_variables)]
691 fn get_grid_z(&self, grid: i32) -> BmiResult<&[f64]> {
692 BmiNotImplementedError.into()
693 }
694
695 /* Unstructured */
696 /// Get the number of
697 /// [nodes](https://bmi.csdms.io/en/stable/glossary.html#term-node)
698 /// in the grid.
699 ///
700 /// This function is used for describing
701 /// [unstructured](https://bmi.csdms.io/en/stable/model_grids.html#unstructured-grids)
702 /// grids.
703 ///
704 /// Default implementation returns Err([`BmiNotImplementedError`]).
705 ///
706 /// See
707 /// [csdms bmi `get_grid_node_count`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-node-count)
708 /// docs for more info.
709 #[allow(unused_variables)]
710 fn get_grid_node_count(&self, grid: i32) -> BmiResult<u32> {
711 BmiNotImplementedError.into()
712 }
713
714 /// Get the number of
715 /// [edges](https://bmi.csdms.io/en/stable/glossary.html#term-edge)
716 /// in the grid.
717 ///
718 /// This function is used for describing
719 /// [unstructured](https://bmi.csdms.io/en/stable/model_grids.html#unstructured-grids)
720 /// grids.
721 ///
722 /// Default implementation returns Err([`BmiNotImplementedError`]).
723 ///
724 /// See
725 /// [csdms bmi `get_grid_edge_count`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-edge-count)
726 /// docs for more info.
727 #[allow(unused_variables)]
728 fn get_grid_edge_count(&self, grid: i32) -> BmiResult<u32> {
729 BmiNotImplementedError.into()
730 }
731
732 /// Get the number of
733 /// [faces](https://bmi.csdms.io/en/stable/glossary.html#term-face)
734 /// in the grid.
735 ///
736 /// This function is used for describing
737 /// [unstructured](https://bmi.csdms.io/en/stable/model_grids.html#unstructured-grids)
738 /// grids.
739 ///
740 /// Default implementation returns Err([`BmiNotImplementedError`]).
741 ///
742 /// See
743 /// [csdms bmi `get_grid_face_count`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-face-count)
744 /// docs for more info.
745 #[allow(unused_variables)]
746 fn get_grid_face_count(&self, grid: i32) -> BmiResult<u32> {
747 BmiNotImplementedError.into()
748 }
749
750 /// Return the edge-node connectivity.
751 /// The total length of the slice is 2 * [`get_grid_edge_count`].
752 ///
753 /// This function is used for describing
754 /// [unstructured](https://bmi.csdms.io/en/stable/model_grids.html#unstructured-grids)
755 /// grids.
756 ///
757 /// Default implementation returns Err([`BmiNotImplementedError`]).
758 ///
759 /// See
760 /// [csdms bmi `get_grid_edge_nodes`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-edge-nodes)
761 /// docs for more info.
762 ///
763 /// [`get_grid_edge_count`]: #tymethod.get_grid_edge_count
764 #[allow(unused_variables)]
765 fn get_grid_edge_nodes(&self, grid: i32) -> BmiResult<&[u32]> {
766 BmiNotImplementedError.into()
767 }
768
769 /// Return the face-edge connectivity.
770 /// The length of the returned slice is the sum of the values of [`get_grid_nodes_per_face`].
771 ///
772 /// This function is used for describing
773 /// [unstructured](https://bmi.csdms.io/en/stable/model_grids.html#unstructured-grids)
774 /// grids.
775 ///
776 /// Default implementation returns Err([`BmiNotImplementedError`]).
777 ///
778 /// See
779 /// [csdms bmi `get_grid_face_edges`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-face-edges)
780 /// docs for more info.
781 ///
782 /// [`get_grid_nodes_per_face`]: #tymethod.get_grid_nodes_per_face
783 #[allow(unused_variables)]
784 fn get_grid_face_edges(&self, grid: i32) -> BmiResult<&[u32]> {
785 BmiNotImplementedError.into()
786 }
787
788 /// Return the face-node connectivity.
789 ///
790 /// This function is used for describing
791 /// [unstructured](https://bmi.csdms.io/en/stable/model_grids.html#unstructured-grids)
792 /// grids.
793 ///
794 /// Default implementation returns Err([`BmiNotImplementedError`]).
795 ///
796 /// See
797 /// [csdms bmi `get_grid_face_nodes`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-face-nodes)
798 /// docs for more info.
799 #[allow(unused_variables)]
800 fn get_grid_face_nodes(&self, grid: i32) -> BmiResult<&[u32]> {
801 BmiNotImplementedError.into()
802 }
803
804 /// Return the number of nodes for each face.
805 /// The returned array has a length of [`get_grid_face_count`].
806 ///
807 /// This function is used for describing
808 /// [unstructured](https://bmi.csdms.io/en/stable/model_grids.html#unstructured-grids)
809 /// grids.
810 ///
811 /// Default implementation returns Err([`BmiNotImplementedError`]).
812 ///
813 /// See
814 /// [csdms bmi `get_grid_nodes_per_face`](https://bmi.csdms.io/en/stable/bmi.grid_funcs.html#get-grid-nodes-per-face)
815 /// docs for more info.
816 ///
817 /// [`get_grid_face_count`]: #tymethod.get_grid_face_count
818 #[allow(unused_variables)]
819 fn get_grid_nodes_per_face(&self, grid: i32) -> BmiResult<&[u32]> {
820 BmiNotImplementedError.into()
821 }
822}
823
824/// Bootstraps the `model` so it can be called through the
825/// [bmi-c](https://github.com/csdms/bmi-c/blob/031c5abf0ff0e75bec7aea48a064611138a0de64/bmi.h)
826/// interface.
827///
828/// Example:
829/// ```compile_fail
830/// #[unsafe(no_mangle)]
831/// pub extern "C" fn register_bmi_simple(handle: *mut ffi::Bmi) -> *mut ffi::Bmi {
832/// let model = Model::new();
833/// bmi_rs::register_model(handle, model);
834/// return handle;
835/// }
836/// ```
837pub fn register_model<T: Bmi>(handle: *mut ffi::Bmi, model: T) {
838 assert!(!handle.is_null(), "pointer is null");
839 let handle: &mut ffi::Bmi = unsafe { handle.as_mut() }.unwrap();
840 setup_fn_ptrs::<T>(handle);
841
842 let data: Box<T> = Box::new(model);
843 let data = Box::into_raw(data);
844 handle.data = data as *mut std::ffi::c_void;
845}
846
847fn setup_fn_ptrs<T: Bmi>(handle: &mut ffi::Bmi) {
848 handle.initialize = Some(crate::wrapper::initialize::<T>);
849 handle.update = Some(crate::wrapper::update::<T>);
850 handle.update_until = Some(crate::wrapper::update_until::<T>);
851 handle.finalize = Some(crate::wrapper::finalize::<T>);
852 handle.get_component_name = Some(crate::wrapper::get_component_name::<T>);
853 handle.get_input_item_count = Some(crate::wrapper::get_input_item_count::<T>);
854 handle.get_output_item_count = Some(crate::wrapper::get_output_item_count::<T>);
855 handle.get_input_var_names = Some(crate::wrapper::get_input_var_names::<T>);
856 handle.get_output_var_names = Some(crate::wrapper::get_output_var_names::<T>);
857 handle.get_var_grid = Some(crate::wrapper::get_var_grid::<T>);
858 handle.get_var_type = Some(crate::wrapper::get_var_type::<T>);
859 handle.get_var_units = Some(crate::wrapper::get_var_units::<T>);
860 handle.get_var_itemsize = Some(crate::wrapper::get_var_itemsize::<T>);
861 handle.get_var_nbytes = Some(crate::wrapper::get_var_nbytes::<T>);
862 handle.get_var_location = Some(crate::wrapper::get_var_location::<T>);
863 handle.get_current_time = Some(crate::wrapper::get_current_time::<T>);
864 handle.get_start_time = Some(crate::wrapper::get_start_time::<T>);
865 handle.get_end_time = Some(crate::wrapper::get_end_time::<T>);
866 handle.get_time_units = Some(crate::wrapper::get_time_units::<T>);
867 handle.get_time_step = Some(crate::wrapper::get_time_step::<T>);
868 handle.get_value = Some(crate::wrapper::get_value::<T>);
869 handle.get_value_ptr = Some(crate::wrapper::get_value_ptr::<T>);
870 handle.get_value_at_indices = Some(crate::wrapper::get_value_at_indices::<T>);
871 handle.set_value = Some(crate::wrapper::set_value::<T>);
872 handle.set_value_at_indices = Some(crate::wrapper::set_value_at_indices::<T>);
873 handle.get_grid_rank = Some(crate::wrapper::get_grid_rank::<T>);
874 handle.get_grid_size = Some(crate::wrapper::get_grid_size::<T>);
875 handle.get_grid_type = Some(crate::wrapper::get_grid_type::<T>);
876 handle.get_grid_shape = Some(crate::wrapper::get_grid_shape::<T>);
877 handle.get_grid_spacing = Some(crate::wrapper::get_grid_spacing::<T>);
878 handle.get_grid_origin = Some(crate::wrapper::get_grid_origin::<T>);
879 handle.get_grid_x = Some(crate::wrapper::get_grid_x::<T>);
880 handle.get_grid_y = Some(crate::wrapper::get_grid_y::<T>);
881 handle.get_grid_z = Some(crate::wrapper::get_grid_z::<T>);
882 handle.get_grid_node_count = Some(crate::wrapper::get_grid_node_count::<T>);
883 handle.get_grid_edge_count = Some(crate::wrapper::get_grid_edge_count::<T>);
884 handle.get_grid_face_count = Some(crate::wrapper::get_grid_face_count::<T>);
885 handle.get_grid_edge_nodes = Some(crate::wrapper::get_grid_edge_nodes::<T>);
886 handle.get_grid_face_edges = Some(crate::wrapper::get_grid_face_edges::<T>);
887 handle.get_grid_face_nodes = Some(crate::wrapper::get_grid_face_nodes::<T>);
888 handle.get_grid_nodes_per_face = Some(crate::wrapper::get_grid_nodes_per_face::<T>);
889}
890
891#[cfg(test)]
892mod tests {
893 use super::*;
894 fn case(vs: &[u16], idx: &[u32]) -> Result<Values, Box<dyn Error>> {
895 values_at_indices!(u16, idx, vs)
896 }
897 #[test]
898 fn test_empty() {
899 let vs: [u16; 0] = [];
900 let inds: [u32; 0] = [];
901 assert!(case(&vs, &inds).is_ok());
902
903 let vs: [u16; 1] = [42];
904 let inds: [u32; 0] = [];
905 assert!(case(&vs, &inds).is_ok());
906 }
907
908 #[test]
909 fn test_one() {
910 let vs: [u16; 1] = [42];
911 let inds: [u32; 1] = [0];
912 match case(&vs, &inds) {
913 Ok(values) => match values {
914 Values::U16(values) => {
915 let i = inds[0] as usize;
916 assert_eq!(values[i], vs[i]);
917 }
918 _ => assert!(false),
919 },
920 _ => assert!(false),
921 }
922 }
923
924 #[test]
925 fn test_many() {
926 let vs: [u16; 2] = [0, 1];
927 let inds: [u32; 2] = [0, 1];
928 match case(&vs, &inds) {
929 Ok(values) => match values {
930 Values::U16(values) => {
931 for i in &inds {
932 let i = *i as usize;
933 assert_eq!(values[i], vs[i]);
934 }
935 }
936 _ => assert!(false),
937 },
938 _ => assert!(false),
939 }
940 }
941
942 #[test]
943 fn test_out_of_bounds() {
944 let vs: [u16; 0] = [];
945 let inds: [u32; 1] = [1];
946 match case(&vs, &inds) {
947 Err(err) => {
948 assert!(err.is::<crate::errors::BmiIndexOutOfBounds>());
949 }
950 _ => assert!(false),
951 }
952 }
953}