Skip to content

Commit d984a22

Browse files
committed
Merge branch 'main' into cubic
2 parents e0bebac + d90d356 commit d984a22

18 files changed

+238
-168
lines changed

Cargo.toml

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ninterp"
3-
version = "0.5.1"
3+
version = "0.5.2"
44
edition = "2021"
55
description = "Numerical interpolation for N-dimensional rectilinear grids"
66
repository = "https://github.com/NREL/ninterp"
@@ -15,10 +15,11 @@ keywords = [
1515
categories = ["mathematics"]
1616

1717
[dependencies]
18+
dyn-clone = "1.0.19"
1819
itertools = "0.13.0"
19-
ndarray = "0.16.1"
20+
ndarray = ">=0.15, <0.17"
2021
num-traits = "0.2.19"
21-
serde = { version = "1.0.210", optional = true, features = ["derive"] }
22+
serde = { version = "1", optional = true, features = ["derive"] }
2223
thiserror = "1.0.64"
2324

2425
[dev-dependencies]

README.md

+13-9
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ cargo add ninterp
1515
```
1616

1717
#### Cargo Features
18-
- `serde`: support for serde
18+
- `serde`: support for serde ([caveat](https://github.com/NREL/ninterp/issues/5))
1919
```
2020
cargo add ninterp --features serde
2121
```
@@ -28,8 +28,8 @@ See examples in `new` method documentation:
2828
- [`Interp3D::new`](https://docs.rs/ninterp/latest/ninterp/three/struct.Interp3D.html#method.new)
2929
- [`InterpND::new`](https://docs.rs/ninterp/latest/ninterp/n/struct.InterpND.html#method.new)
3030

31-
Also see the [`examples`](https://github.com/NREL/ninterp/tree/62a62ccd2b3c285919baae609089dee287dc3842/examples) directory for advanced examples:
32-
- Strategy dynamic dispatch: [`dynamic_strategy.rs`](https://github.com/NREL/ninterp/blob/62a62ccd2b3c285919baae609089dee287dc3842/examples/dynamic_strategy.rs)
31+
Also see the [`examples`](examples) directory for advanced examples:
32+
- Strategy dynamic dispatch: [`dynamic_strategy.rs`](examples/dynamic_strategy.rs)
3333

3434
By default, construction of interpolators uses *static dispatch*,
3535
meaning strategy concrete types are determined at compilation.
@@ -38,9 +38,11 @@ Also see the [`examples`](https://github.com/NREL/ninterp/tree/62a62ccd2b3c28591
3838
use *dynamic dispatch* by providing a boxed trait object
3939
`Box<dyn Strategy1D>`/etc. to the `new` method.
4040

41-
- Interpolator dynamic dispatch using `Box<dyn Interpolator>`: [`dynamic_interpolator.rs`](https://github.com/NREL/ninterp/blob/62a62ccd2b3c285919baae609089dee287dc3842/examples/dynamic_interpolator.rs)
41+
- Interpolator dynamic dispatch using `Box<dyn Interpolator>`: [`dynamic_interpolator.rs`](examples/dynamic_interpolator.rs)
4242

43-
- Defining custom strategies: [`custom_strategy.rs`](https://github.com/NREL/ninterp/blob/62a62ccd2b3c285919baae609089dee287dc3842/examples/custom_strategy.rs)
43+
- Defining custom strategies: [`custom_strategy.rs`](examples/custom_strategy.rs)
44+
45+
- Using transmutable (transparent) types, such as `uom::si::Quantity`: [`uom.rs`](examples/uom.rs)
4446

4547
- Using transmutable (transparent) types, such as `uom::si::Quantity`: [`uom.rs`](https://github.com/NREL/ninterp/blob/de2c770dc3614ba43af9e015481fecdc20538380/examples/uom.rs)
4648

@@ -63,7 +65,9 @@ This is useful when working with a `Box<dyn Interpolator>`
6365
Instantiation is done by calling an interpolator's `new` method.
6466
For dimensionalities N ≥ 1, this executes a validation step, preventing runtime panics.
6567
After editing interpolator data,
66-
call [`Interpolator::validate`](https://docs.rs/ninterp/latest/ninterp/trait.Interpolator.html#tymethod.validate) to rerun these checks.
68+
call the InterpData's `validate` method
69+
or [`Interpolator::validate`](https://docs.rs/ninterp/latest/ninterp/trait.Interpolator.html#tymethod.validate)
70+
to rerun these checks.
6771

6872
To change the extrapolation setting, call `set_extrapolate`.
6973

@@ -82,19 +86,19 @@ Not all interpolation strategies are implemented for every dimensionality.
8286
`Linear` and `Nearest` are implemented for all dimensionalities.
8387

8488
Custom strategies can be defined. See
85-
[`examples/custom_strategy.rs`](https://github.com/NREL/ninterp/blob/62a62ccd2b3c285919baae609089dee287dc3842/examples/custom_strategy.rs)
89+
[`examples/custom_strategy.rs`](examples/custom_strategy.rs)
8690
for an example.
8791

8892
### Extrapolation
8993
An [`Extrapolate`](https://docs.rs/ninterp/latest/ninterp/enum.Extrapolate.html)
9094
setting must be provided in the `new` method.
9195
This controls what happens when a point is beyond the range of supplied coordinates.
92-
The following setttings are applicable for all interpolators:
96+
The following settings are applicable for all interpolators:
9397
- `Extrapolate::Fill(T)`
9498
- `Extrapolate::Clamp`
9599
- `Extrapolate::Error`
96100

97-
`Extrapolate::Enable` is valid for `Linear` in all dimensionalities.
101+
`Extrapolate::Enable` is valid for `Linear` for all dimensionalities.
98102

99103
If you are unsure which variant to choose, `Extrapolate::Error` is likely what you want.
100104

examples/custom_strategy.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
use ndarray::prelude::*;
2-
1+
use ninterp::data::InterpData2D;
32
use ninterp::prelude::*;
43
use ninterp::strategy::*;
54

5+
// Note: ninterp also re-exposes the internally used `ndarray` crate
6+
// `use ninterp::ndarray;`
7+
use ndarray::prelude::*;
8+
use ndarray::{Data, RawDataClone};
9+
610
// Debug must be derived for custom strategies
7-
#[derive(Debug)]
11+
#[derive(Debug, Clone)]
812
struct CustomStrategy;
913

1014
// Implement strategy for 2-D f32 interpolation
@@ -14,7 +18,7 @@ where
1418
// e.g. `Array2<f32>`, `ArrayView2<f32>`, `CowArray<<'a, f32>, Ix2>`, etc.
1519
// For a more generic bound, consider introducing a bound for D::Elem
1620
// e.g. D::Elem: num_traits::Num + PartialOrd
17-
D: ndarray::Data<Elem = f32>,
21+
D: Data<Elem = f32> + RawDataClone + Clone,
1822
{
1923
fn interpolate(
2024
&self,

examples/dynamic_interpolator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ fn main() {
2525
)
2626
.unwrap(),
2727
);
28-
assert_eq!(boxed.interpolate(&[1.75]).unwrap(), 8.)
28+
assert_eq!(boxed.interpolate(&[1.75]).unwrap(), 8.);
2929
}

examples/uom.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ fn main() {
1212
let f_x = array![Power::new::<kilowatt>(0.25), Power::new::<kilowatt>(0.75)];
1313
// `uom::si::Quantity` is repr(transparent), meaning it has the same memory layout as its contained type.
1414
// This means we can get the contained type via transmuting.
15-
let interp: Interp1D<ndarray::OwnedRepr<f64>, _> = unsafe {
15+
let interp: ninterp::one::Interp1DOwned<f64, _> = unsafe {
1616
Interp1D::new(
1717
std::mem::transmute(x),
1818
std::mem::transmute(f_x),

src/data.rs

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use super::*;
2+
3+
pub use crate::n::{InterpDataND, InterpDataNDOwned, InterpDataNDViewed};
4+
pub use crate::one::{InterpData1D, InterpData1DOwned, InterpData1DViewed};
5+
pub use crate::three::{InterpData3D, InterpData3DOwned, InterpData3DViewed};
6+
pub use crate::two::{InterpData2D, InterpData2DOwned, InterpData2DViewed};
7+
8+
#[derive(Debug, Clone, PartialEq)]
9+
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
10+
#[cfg_attr(
11+
feature = "serde",
12+
serde(bound = "
13+
D: DataOwned,
14+
D::Elem: Serialize + DeserializeOwned,
15+
Dim<[usize; N]>: Serialize + DeserializeOwned,
16+
[ArrayBase<D, Ix1>; N]: Serialize + DeserializeOwned,
17+
")
18+
)]
19+
pub struct InterpData<D, const N: usize>
20+
where
21+
Dim<[Ix; N]>: Dimension,
22+
D: Data + RawDataClone + Clone,
23+
D::Elem: Num + PartialOrd + Copy + Debug,
24+
{
25+
pub grid: [ArrayBase<D, Ix1>; N],
26+
pub values: ArrayBase<D, Dim<[Ix; N]>>,
27+
}
28+
pub type InterpDataViewed<T, const N: usize> = InterpData<ndarray::ViewRepr<T>, N>;
29+
pub type InterpDataOwned<T, const N: usize> = InterpData<ndarray::ViewRepr<T>, N>;
30+
31+
impl<D, const N: usize> InterpData<D, N>
32+
where
33+
Dim<[Ix; N]>: Dimension,
34+
D: Data + RawDataClone + Clone,
35+
D::Elem: Num + PartialOrd + Copy + Debug,
36+
{
37+
pub fn validate(&self) -> Result<(), ValidateError> {
38+
for i in 0..N {
39+
let i_grid_len = self.grid[i].len();
40+
// Check that each grid dimension has elements
41+
// Indexing `grid` directly is okay because empty dimensions are caught at compilation
42+
if i_grid_len == 0 {
43+
return Err(ValidateError::EmptyGrid(i));
44+
}
45+
// Check that grid points are monotonically increasing
46+
if !self.grid[i].windows(2).into_iter().all(|w| w[0] <= w[1]) {
47+
return Err(ValidateError::Monotonicity(i));
48+
}
49+
// Check that grid and values are compatible shapes
50+
if i_grid_len != self.values.shape()[i] {
51+
return Err(ValidateError::IncompatibleShapes(i));
52+
}
53+
}
54+
Ok(())
55+
}
56+
}

src/error.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use thiserror::Error;
44

55
/// Error in interpolator data validation
6-
#[derive(Error, Debug, Clone)]
6+
#[derive(Error, Debug, Clone, PartialEq)]
77
pub enum ValidateError {
88
#[error("selected `Strategy` ({0}) is unimplemented/inapplicable for interpolator")]
99
StrategySelection(&'static str),
@@ -20,7 +20,7 @@ pub enum ValidateError {
2020
}
2121

2222
/// Error in interpolation call
23-
#[derive(Error, Debug, Clone)]
23+
#[derive(Error, Debug, Clone, PartialEq)]
2424
pub enum InterpolateError {
2525
#[error("attempted to interpolate at point beyond grid data: {0}")]
2626
ExtrapolateError(String),

0 commit comments

Comments
 (0)