From 70df920e6c84f0e0dee5f82c96944a66b53f6548 Mon Sep 17 00:00:00 2001 From: Kyle Carow Date: Fri, 24 Jan 2025 16:34:03 -0700 Subject: [PATCH] improve ExtrapolationError messages --- src/lib.rs | 88 +++++++++++++++++++++++++++++------------------------- src/n.rs | 48 ++++++++++++++--------------- 2 files changed, 72 insertions(+), 64 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8ce33f1..11c0b5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,15 +22,15 @@ mod error; mod n; mod one; mod three; -mod two; mod traits; +mod two; pub use error::*; pub(crate) use n::*; pub(crate) use one::*; pub(crate) use three::*; -pub(crate) use two::*; pub use traits::*; +pub(crate) use two::*; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -80,9 +80,9 @@ fn find_nearest_index(arr: &[f64], target: f64) -> usize { /// - An [`Extrapolate`] setting must be specified. /// This controls what happens when a point is beyond the range of supplied coordinates. /// If you are unsure which variant to choose, [`Extrapolate::Error`] is likely what you want. -/// +/// /// For 0D (constant-value) interpolators, instantiate directly, e.g. `Interpolator::Interp0D(0.5)` -/// +/// #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub enum Interpolator { @@ -619,8 +619,8 @@ impl InterpMethods for Interpolator { Extrapolate::Error => { if !(interp.x[0] <= point[0] && &point[0] <= interp.x.last().unwrap()) { return Err(InterpolationError::ExtrapolationError(format!( - "point = {point:?}, grid = {:?}", - interp.x + "\n point[0] = {:?} is out of bounds for x-grid = {:?}", + point[0], interp.x ))); } } @@ -638,17 +638,21 @@ impl InterpMethods for Interpolator { return interp.interpolate(clamped_point); } Extrapolate::Error => { - if !(interp.x[0] <= point[0] && &point[0] <= interp.x.last().unwrap()) { - return Err(InterpolationError::ExtrapolationError(format!( - "point = {point:?}, x grid = {:?}", - interp.x - ))); + let grid = [&interp.x, &interp.y]; + let grid_names = ["x", "y"]; + let mut errors = Vec::new(); + for dim in 0..2 { + if !(grid[dim][0] <= point[dim] + && &point[dim] <= grid[dim].last().unwrap()) + { + errors.push(format!( + "\n point[{dim}] = {:?} is out of bounds for {}-grid = {:?}", + point[dim], grid_names[dim], grid[dim], + )); + } } - if !(interp.y[0] <= point[1] && &point[1] <= interp.y.last().unwrap()) { - return Err(InterpolationError::ExtrapolationError(format!( - "point = {point:?}, y grid = {:?}", - interp.y - ))); + if !errors.is_empty() { + return Err(InterpolationError::ExtrapolationError(errors.join(""))); } } _ => {} @@ -666,23 +670,21 @@ impl InterpMethods for Interpolator { return interp.interpolate(clamped_point); } Extrapolate::Error => { - if !(interp.x[0] <= point[0] && &point[0] <= interp.x.last().unwrap()) { - return Err(InterpolationError::ExtrapolationError(format!( - "point = {point:?}, x grid = {:?}", - interp.x - ))); + let grid = [&interp.x, &interp.y, &interp.z]; + let grid_names = ["x", "y", "z"]; + let mut errors = Vec::new(); + for dim in 0..3 { + if !(grid[dim][0] <= point[dim] + && &point[dim] <= grid[dim].last().unwrap()) + { + errors.push(format!( + "\n point[{dim}] = {:?} is out of bounds for {}-grid = {:?}", + point[dim], grid_names[dim], grid[dim], + )); + } } - if !(interp.y[0] <= point[1] && &point[1] <= interp.y.last().unwrap()) { - return Err(InterpolationError::ExtrapolationError(format!( - "point = {point:?}, y grid = {:?}", - interp.y - ))); - } - if !(interp.z[0] <= point[2] && &point[2] <= interp.z.last().unwrap()) { - return Err(InterpolationError::ExtrapolationError(format!( - "point = {point:?}, z grid = {:?}", - interp.z - ))); + if !errors.is_empty() { + return Err(InterpolationError::ExtrapolationError(errors.join(""))); } } _ => {} @@ -702,14 +704,20 @@ impl InterpMethods for Interpolator { return interp.interpolate(&clamped_point); } Extrapolate::Error => { - if !point.iter().enumerate().all(|(dim, pt_dim)| { - &interp.grid[dim][0] <= pt_dim - && pt_dim <= interp.grid[dim].last().unwrap() - }) { - return Err(InterpolationError::ExtrapolationError(format!( - "point = {point:?}, grid: {:?}", - interp.grid, - ))); + let mut errors = Vec::new(); + for dim in 0..interp.ndim() { + if !(interp.grid[dim][0] <= point[dim] + && &point[dim] <= interp.grid[dim].last().unwrap()) + { + errors.push(format!( + "\n point[{dim}] = {:?} is out of bounds for grid[{dim}] = {:?}", + point[dim], + interp.grid[dim], + )); + } + } + if !errors.is_empty() { + return Err(InterpolationError::ExtrapolationError(errors.join(""))); } } _ => {} diff --git a/src/n.rs b/src/n.rs index d3931f1..7bbc7df 100644 --- a/src/n.rs +++ b/src/n.rs @@ -208,12 +208,12 @@ mod tests { ] .into_dyn(); let interp = Interpolator::new_nd( - grid.clone(), - f_xyz.clone(), - Strategy::Linear, - Extrapolate::Error, - ) - .unwrap(); + grid.clone(), + f_xyz.clone(), + Strategy::Linear, + Extrapolate::Error, + ) + .unwrap(); // Check that interpolating at grid points just retrieves the value for i in 0..grid[0].len() { for j in 0..grid[1].len() { @@ -258,12 +258,12 @@ mod tests { #[test] fn test_linear_offset() { let interp = Interpolator::new_nd( - vec![vec![0., 1.], vec![0., 1.], vec![0., 1.]], - ndarray::array![[[0., 1.], [2., 3.]], [[4., 5.], [6., 7.]],].into_dyn(), - Strategy::Linear, - Extrapolate::Error, - ) - .unwrap(); + vec![vec![0., 1.], vec![0., 1.], vec![0., 1.]], + ndarray::array![[[0., 1.], [2., 3.]], [[4., 5.], [6., 7.]],].into_dyn(), + Strategy::Linear, + Extrapolate::Error, + ) + .unwrap(); assert_eq!( interp.interpolate(&[0.25, 0.65, 0.9]).unwrap(), 3.1999999999999997 @@ -286,12 +286,12 @@ mod tests { )); // Extrapolate::Error let interp = Interpolator::new_nd( - vec![vec![0., 1.], vec![0., 1.], vec![0., 1.]], - ndarray::array![[[0., 1.], [2., 3.]], [[4., 5.], [6., 7.]],].into_dyn(), - Strategy::Linear, - Extrapolate::Error, - ) - .unwrap(); + vec![vec![0., 1.], vec![0., 1.], vec![0., 1.]], + ndarray::array![[[0., 1.], [2., 3.]], [[4., 5.], [6., 7.]],].into_dyn(), + Strategy::Linear, + Extrapolate::Error, + ) + .unwrap(); assert!(matches!( interp.interpolate(&[-1., -1., -1.]).unwrap_err(), InterpolationError::ExtrapolationError(_) @@ -305,12 +305,12 @@ mod tests { #[test] fn test_extrapolate_clamp() { let interp = Interpolator::new_nd( - vec![vec![0., 1.], vec![0., 1.], vec![0., 1.]], - ndarray::array![[[0., 1.], [2., 3.]], [[4., 5.], [6., 7.]],].into_dyn(), - Strategy::Linear, - Extrapolate::Clamp, - ) - .unwrap(); + vec![vec![0., 1.], vec![0., 1.], vec![0., 1.]], + ndarray::array![[[0., 1.], [2., 3.]], [[4., 5.], [6., 7.]],].into_dyn(), + Strategy::Linear, + Extrapolate::Clamp, + ) + .unwrap(); assert_eq!(interp.interpolate(&[-1., -1., -1.]).unwrap(), 0.); assert_eq!(interp.interpolate(&[2., 2., 2.]).unwrap(), 7.); }