#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
ErrorsGrid1D, Grid1D, IntervalPartition, SubIntervalInPartition, Topology1D,
coords::Coords1D,
intervals::bounded::{IntervalFinitePositiveLength, IntervalFinitePositiveLengthTrait},
operations::refinement::{Grid1DNonUniformRefinement, Grid1DUniformRefinement},
scalars::{IntervalId, NumIntervals, PositiveNumPoints1D},
topology::{Grid1DIndexSpaces, IndexSpace1D},
traits::{BuildIntervalInPartition, HasCoords1D, HasDomain1D},
};
use derive_more::Into;
use num_valid::{Constants, core::errors::capture_backtrace};
use serde::{Deserialize, Serialize};
use std::backtrace::Backtrace;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ErrorsGrid1DUnion<Domain1D: IntervalFinitePositiveLengthTrait> {
#[error("The 1D domains of the two grids do not match!")]
DomainsMismatch {
domain_grid_a: Domain1D,
domain_grid_b: Domain1D,
backtrace: Backtrace,
},
#[error("The topologies of the two grids do not match!")]
TopologyMismatch {
interval_topology_grid_a: Topology1D,
interval_topology_grid_b: Topology1D,
point_topology_grid_a: Topology1D,
point_topology_grid_b: Topology1D,
backtrace: Backtrace,
},
#[error("Error from the Grid1D constructor")]
FromGrid1DConstructor {
#[from]
source: ErrorsGrid1D<Domain1D>,
},
}
#[derive(Debug, Clone, PartialEq, Into, Serialize, Deserialize)]
#[serde(bound(deserialize = "Domain1D: for<'d> serde::Deserialize<'d>, \
Domain1D::RealType: for<'d> serde::Deserialize<'d>"))]
pub struct Grid1DUnion<Domain1D: BuildIntervalInPartition> {
unified_grid: Grid1D<Domain1D>,
mapping_to_grid_a: Vec<IntervalId>,
mapping_to_grid_b: Vec<IntervalId>,
}
impl<Domain1D: BuildIntervalInPartition> Grid1DUnion<Domain1D> {
pub fn try_new(
grid1d_a: &Grid1D<Domain1D>,
grid1d_b: &Grid1D<Domain1D>,
) -> Result<Self, ErrorsGrid1DUnion<Domain1D>> {
let domain = grid1d_a.domain().clone();
if &domain != grid1d_b.domain() {
return Err(ErrorsGrid1DUnion::DomainsMismatch {
domain_grid_a: domain,
domain_grid_b: grid1d_b.domain().clone(),
backtrace: capture_backtrace(),
});
}
let index_spaces_grid_a = grid1d_a.index_spaces();
let interval_topology_a = index_spaces_grid_a.interval_index_space().topology();
let point_topology_a = index_spaces_grid_a.point_index_space().topology();
let index_spaces_grid_b = grid1d_b.index_spaces();
let interval_topology_b = index_spaces_grid_b.interval_index_space().topology();
let point_topology_b = index_spaces_grid_b.point_index_space().topology();
if interval_topology_a != interval_topology_b || point_topology_a != point_topology_b {
return Err(ErrorsGrid1DUnion::TopologyMismatch {
interval_topology_grid_a: *interval_topology_a,
interval_topology_grid_b: *interval_topology_b,
point_topology_grid_a: *point_topology_a,
point_topology_grid_b: *point_topology_b,
backtrace: capture_backtrace(),
});
}
let coords_a = grid1d_a.coords();
let coords_b = grid1d_b.coords();
let merged_coords = coords_a.union(coords_b);
let unified_grid = Grid1D::try_from_coords(merged_coords)?;
let compute_interval_map =
|unified_grid: &Grid1D<Domain1D>, grid1d_coarse: &Grid1D<Domain1D>| {
let n_intervals = *unified_grid.num_intervals().as_ref();
let mut map = Vec::with_capacity(n_intervals);
let one_div_2 = Domain1D::RealType::one_div_2();
let v = unified_grid.coords().as_ref();
for i in 0..n_intervals {
let midpoint_interval_unified_grid = (v[i].clone() + &v[i + 1]) * &one_div_2;
let coarse_interval_id =
grid1d_coarse.find_interval_id_of_point(&midpoint_interval_unified_grid);
map.push(coarse_interval_id);
}
map
};
let mapping_to_grid_a = compute_interval_map(&unified_grid, grid1d_a);
let mapping_to_grid_b = compute_interval_map(&unified_grid, grid1d_b);
Ok(Self {
unified_grid,
mapping_to_grid_a,
mapping_to_grid_b,
})
}
pub fn unified_grid(&self) -> &Grid1D<Domain1D> {
&self.unified_grid
}
pub fn mapping_to_grid_a(&self) -> &Vec<IntervalId> {
&self.mapping_to_grid_a
}
pub fn mapping_to_grid_b(&self) -> &Vec<IntervalId> {
&self.mapping_to_grid_b
}
pub fn num_refined_intervals(&self) -> NumIntervals {
self.unified_grid.num_intervals()
}
pub fn find_original_intervals(&self, refined_id: &IntervalId) -> (IntervalId, IntervalId) {
let refined_id = *refined_id.as_ref();
let grid_a_id = self.mapping_to_grid_a[refined_id];
let grid_b_id = self.mapping_to_grid_b[refined_id];
(grid_a_id, grid_b_id)
}
pub fn iter_interval_mappings(
&self,
) -> impl Iterator<Item = (IntervalId, IntervalId, IntervalId)> + '_ {
(0..*self.num_refined_intervals().as_ref()).map(move |i| {
let refined_id = IntervalId::new(i);
let (a_id, b_id) = self.find_original_intervals(&refined_id);
(refined_id, a_id, b_id)
})
}
pub fn iter_intervals_with_mappings(
&self,
) -> impl Iterator<
Item = (
IntervalId,
SubIntervalInPartition<Domain1D>,
IntervalId,
IntervalId,
),
> + '_ {
self.unified_grid()
.iter_intervals()
.map(move |(refined_id, interval)| {
let (a_id, b_id) = self.find_original_intervals(&refined_id);
(refined_id, interval, a_id, b_id)
})
}
pub fn find_intersections_with_mappings<
IntervalType: IntervalFinitePositiveLengthTrait<RealType = Domain1D::RealType>,
>(
&self,
domain_in: &IntervalType,
) -> Vec<(
IntervalId,
IntervalFinitePositiveLength<Domain1D::RealType>,
IntervalId,
IntervalId,
)> {
self.unified_grid()
.intervals_in_intersection(domain_in)
.into_iter()
.map(|(refined_id, intersection)| {
let (a_id, b_id) = self.find_original_intervals(&refined_id);
(refined_id, intersection, a_id, b_id)
})
.collect()
}
pub fn into_parts(self) -> (Grid1D<Domain1D>, Vec<IntervalId>, Vec<IntervalId>) {
(
self.unified_grid,
self.mapping_to_grid_a,
self.mapping_to_grid_b,
)
}
}
impl<Domain1D: BuildIntervalInPartition> HasDomain1D for Grid1DUnion<Domain1D> {
type Domain1D = Domain1D;
fn domain(&self) -> &Self::Domain1D {
self.unified_grid.domain()
}
}
impl<Domain1D: BuildIntervalInPartition> HasCoords1D for Grid1DUnion<Domain1D> {
type Point1DType = Domain1D::RealType;
fn coords(&self) -> &Coords1D<Self::Point1DType> {
self.unified_grid.coords()
}
}
impl<Domain1D: BuildIntervalInPartition> IntervalPartition for Grid1DUnion<Domain1D> {
type UniformlyRefinedGrid1DType = Grid1D<Domain1D>;
fn index_spaces(&self) -> &Grid1DIndexSpaces {
self.unified_grid.index_spaces()
}
fn try_find_interval_id_of_point(&self, x: &Self::Point1DType) -> Option<IntervalId> {
self.unified_grid.try_find_interval_id_of_point(x)
}
fn refine_uniform(
self,
num_extra_points_each_interval: &PositiveNumPoints1D,
) -> Grid1DUniformRefinement<Self> {
let unified_grid = self.unified_grid;
let mapping_to_grid_a = self.mapping_to_grid_a;
let mapping_to_grid_b = self.mapping_to_grid_b;
let (
refined_grid,
original_unified_grid,
refined_to_original_interval_mapping,
original_to_refined_interval_mapping,
) = unified_grid
.refine_uniform(num_extra_points_each_interval)
.into();
let original_grid = Grid1DUnion {
unified_grid: original_unified_grid,
mapping_to_grid_a,
mapping_to_grid_b,
};
Grid1DUniformRefinement::new(
refined_grid,
original_grid,
refined_to_original_interval_mapping,
original_to_refined_interval_mapping,
)
}
fn refine(
self,
intervals_to_refine: &std::collections::BTreeMap<IntervalId, PositiveNumPoints1D>,
) -> Grid1DNonUniformRefinement<Self> {
let unified_grid = self.unified_grid;
let mapping_to_grid_a = self.mapping_to_grid_a;
let mapping_to_grid_b = self.mapping_to_grid_b;
let (
refined_grid,
original_unified_grid,
refined_to_original_interval_mapping,
original_to_refined_interval_mapping,
) = unified_grid.refine(intervals_to_refine).into();
let original_grid = Grid1DUnion {
unified_grid: original_unified_grid,
mapping_to_grid_a,
mapping_to_grid_b,
};
Grid1DNonUniformRefinement::new(
refined_grid,
original_grid,
refined_to_original_interval_mapping,
original_to_refined_interval_mapping,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
intervals::{IntervalClosed, IntervalLowerClosedUpperOpen},
scalars::NumIntervals,
traits::{HasCoords1D, HasDomain1D, IntervalPartition},
};
use num_valid::RealNative64StrictFinite;
use sorted_vec::partial::SortedSet;
use std::ops::Deref;
use try_create::TryNew;
type Real = RealNative64StrictFinite;
fn real(x: f64) -> Real {
Real::try_new(x).unwrap()
}
mod construction {
use super::*;
#[test]
fn union_of_identical_grids() {
let grid = Grid1D::uniform(
IntervalClosed::new(real(0.0), real(2.0)),
NumIntervals::try_new(2).unwrap(),
);
let union = Grid1DUnion::try_new(&grid, &grid).unwrap();
assert_eq!(union.unified_grid().coords(), grid.coords());
assert_eq!(union.num_refined_intervals(), grid.num_intervals());
for i in 0..*grid.num_intervals().as_ref() {
assert_eq!(union.mapping_to_grid_a()[i], IntervalId::new(i));
assert_eq!(union.mapping_to_grid_b()[i], IntervalId::new(i));
}
}
#[test]
fn union_with_disjoint_points() {
let grid_a =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(2.0),
real(4.0),
]))
.unwrap();
let grid_b =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(1.0),
real(3.0),
real(4.0),
]))
.unwrap();
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
assert_eq!(
union.unified_grid().coords().deref(),
&[real(0.0), real(1.0), real(2.0), real(3.0), real(4.0)]
);
assert_eq!(union.num_refined_intervals().as_ref(), &4);
}
#[test]
fn union_preserves_domain() {
let domain = IntervalClosed::new(real(-5.0), real(10.0));
let grid_a = Grid1D::uniform(domain.clone(), NumIntervals::try_new(3).unwrap());
let grid_b = Grid1D::uniform(domain.clone(), NumIntervals::try_new(5).unwrap());
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
assert_eq!(union.domain(), &domain);
}
#[test]
fn union_with_uniform_and_non_uniform() {
let domain = IntervalClosed::new(real(0.0), real(1.0));
let uniform_grid = Grid1D::uniform(domain, NumIntervals::try_new(4).unwrap());
let non_uniform_grid =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(0.1),
real(0.9),
real(1.0),
]))
.unwrap();
let union = Grid1DUnion::try_new(&uniform_grid, &non_uniform_grid).unwrap();
assert!(union.unified_grid().coords().len() >= 5); }
}
mod domain_mismatch {
use super::*;
#[test]
fn different_upper_bounds() {
let grid_a = Grid1D::uniform(
IntervalClosed::new(real(0.0), real(1.0)),
NumIntervals::try_new(2).unwrap(),
);
let grid_b = Grid1D::uniform(
IntervalClosed::new(real(0.0), real(2.0)),
NumIntervals::try_new(2).unwrap(),
);
let result = Grid1DUnion::try_new(&grid_a, &grid_b);
assert!(matches!(
result,
Err(ErrorsGrid1DUnion::DomainsMismatch { .. })
));
}
#[test]
fn different_lower_bounds() {
let grid_a = Grid1D::uniform(
IntervalClosed::new(real(-1.0), real(1.0)),
NumIntervals::try_new(2).unwrap(),
);
let grid_b = Grid1D::uniform(
IntervalClosed::new(real(0.0), real(1.0)),
NumIntervals::try_new(2).unwrap(),
);
let result = Grid1DUnion::try_new(&grid_a, &grid_b);
assert!(matches!(
result,
Err(ErrorsGrid1DUnion::DomainsMismatch { .. })
));
}
#[test]
fn completely_different_domains() {
let grid_a = Grid1D::uniform(
IntervalClosed::new(real(0.0), real(1.0)),
NumIntervals::try_new(2).unwrap(),
);
let grid_b = Grid1D::uniform(
IntervalClosed::new(real(10.0), real(20.0)),
NumIntervals::try_new(2).unwrap(),
);
let result = Grid1DUnion::try_new(&grid_a, &grid_b);
assert!(matches!(
result,
Err(ErrorsGrid1DUnion::DomainsMismatch { .. })
));
}
}
mod topology_mismatch {
use super::*;
#[test]
fn real_line_vs_circle_topology() {
let domain = IntervalLowerClosedUpperOpen::new(real(0.0), real(1.0));
let grid_real_line = Grid1D::uniform(domain.clone(), NumIntervals::try_new(4).unwrap());
let grid_circle: Grid1D<_> =
crate::Grid1DUniform::new_periodic(domain, NumIntervals::try_new(4).unwrap())
.into();
let result = Grid1DUnion::try_new(&grid_real_line, &grid_circle);
match result {
Err(ErrorsGrid1DUnion::TopologyMismatch {
interval_topology_grid_a,
interval_topology_grid_b,
point_topology_grid_a,
point_topology_grid_b,
..
}) => {
assert_eq!(interval_topology_grid_a, Topology1D::RealLine);
assert_eq!(interval_topology_grid_b, Topology1D::Circle);
assert_eq!(point_topology_grid_a, Topology1D::RealLine);
assert_eq!(point_topology_grid_b, Topology1D::Circle);
}
_ => panic!("Expected TopologyMismatch error"),
}
}
}
mod mappings {
use super::*;
#[test]
fn find_original_intervals() {
let grid_a =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(1.0),
real(2.0),
]))
.unwrap();
let grid_b =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(0.5),
real(2.0),
]))
.unwrap();
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
for i in 0..*union.num_refined_intervals().as_ref() {
let refined_id = IntervalId::new(i);
let (a_id, b_id) = union.find_original_intervals(&refined_id);
assert_eq!(a_id, union.mapping_to_grid_a()[i]);
assert_eq!(b_id, union.mapping_to_grid_b()[i]);
}
}
#[test]
fn mapping_to_grid_a() {
let domain = IntervalClosed::new(real(0.0), real(2.0));
let grid_a = Grid1D::uniform(domain.clone(), NumIntervals::try_new(2).unwrap());
let grid_b = Grid1D::uniform(domain, NumIntervals::try_new(4).unwrap());
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
assert_eq!(union.mapping_to_grid_a()[0], IntervalId::new(0));
assert_eq!(union.mapping_to_grid_a()[1], IntervalId::new(0));
assert_eq!(union.mapping_to_grid_a()[2], IntervalId::new(1));
assert_eq!(union.mapping_to_grid_a()[3], IntervalId::new(1));
}
}
mod iteration {
use super::*;
#[test]
fn iter_interval_mappings() {
let grid_a =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(1.0),
real(2.0),
]))
.unwrap();
let grid_b =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(0.5),
real(2.0),
]))
.unwrap();
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
let mappings: Vec<(IntervalId, IntervalId, IntervalId)> =
union.iter_interval_mappings().collect();
assert_eq!(mappings.len(), *union.num_refined_intervals().as_ref());
for (refined_id, expected_a, expected_b) in &mappings {
let (actual_a, actual_b) = union.find_original_intervals(refined_id);
assert_eq!(actual_a, *expected_a);
assert_eq!(actual_b, *expected_b);
}
}
#[test]
fn iter_intervals_with_mappings() {
let domain = IntervalClosed::new(real(0.0), real(2.0));
let grid_a = Grid1D::uniform(domain.clone(), NumIntervals::try_new(2).unwrap());
let grid_b = Grid1D::uniform(domain, NumIntervals::try_new(4).unwrap());
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
let interval_mappings: Vec<_> = union.iter_intervals_with_mappings().collect();
assert_eq!(
interval_mappings.len(),
*union.num_refined_intervals().as_ref()
);
for (refined_id, _interval, a_id, b_id) in &interval_mappings {
let (check_a, check_b) = union.find_original_intervals(refined_id);
assert_eq!(&check_a, a_id);
assert_eq!(&check_b, b_id);
}
}
}
mod intersection_queries {
use super::*;
#[test]
fn find_intersections_with_mappings() {
let domain = IntervalClosed::new(real(0.0), real(3.0));
let grid_a = Grid1D::uniform(domain, NumIntervals::try_new(3).unwrap());
let grid_b =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(0.5),
real(1.5),
real(3.0),
]))
.unwrap();
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
let subdomain = IntervalClosed::new(real(0.7), real(2.2));
let intersections = union.find_intersections_with_mappings(&subdomain);
assert!(!intersections.is_empty());
for (refined_id, _intersection, a_id, b_id) in &intersections {
let (check_a, check_b) = union.find_original_intervals(refined_id);
assert_eq!(check_a, *a_id);
assert_eq!(check_b, *b_id);
}
}
#[test]
fn intersection_subdomain_at_boundary() {
let domain = IntervalClosed::new(real(0.0), real(2.0));
let grid_a = Grid1D::uniform(domain.clone(), NumIntervals::try_new(4).unwrap());
let grid_b = Grid1D::uniform(domain.clone(), NumIntervals::try_new(4).unwrap());
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
let subdomain = IntervalClosed::new(real(0.5), real(1.5));
let intersections = union.find_intersections_with_mappings(&subdomain);
assert!(!intersections.is_empty());
}
}
mod decomposition {
use super::*;
#[test]
fn into_parts() {
let domain = IntervalClosed::new(real(0.0), real(1.0));
let grid_a = Grid1D::uniform(domain.clone(), NumIntervals::try_new(2).unwrap());
let grid_b = Grid1D::uniform(domain, NumIntervals::try_new(4).unwrap());
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
let num_intervals = *union.num_refined_intervals().as_ref();
let (unified_grid, map_a, map_b) = union.into_parts();
assert_eq!(unified_grid.num_intervals().as_ref(), &num_intervals);
assert_eq!(map_a.len(), num_intervals);
assert_eq!(map_b.len(), num_intervals);
}
#[test]
fn decomposition_preserves_data() {
let domain = IntervalClosed::new(real(0.0), real(2.0));
let grid_a = Grid1D::uniform(domain.clone(), NumIntervals::try_new(2).unwrap());
let grid_b = Grid1D::uniform(domain, NumIntervals::try_new(4).unwrap());
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
let expected_coords = union.unified_grid().coords().deref().to_vec();
let expected_map_a: Vec<_> = union.mapping_to_grid_a().to_vec();
let expected_map_b: Vec<_> = union.mapping_to_grid_b().to_vec();
let (unified_grid, map_a, map_b) = union.into_parts();
assert_eq!(unified_grid.coords().deref(), &expected_coords);
assert_eq!(map_a, expected_map_a);
assert_eq!(map_b, expected_map_b);
}
}
mod refinement_of_union {
use super::*;
use std::collections::BTreeMap;
#[test]
fn uniform_refinement_of_union() {
let domain = IntervalClosed::new(real(0.0), real(2.0));
let grid_a = Grid1D::uniform(domain.clone(), NumIntervals::try_new(2).unwrap());
let grid_b = Grid1D::uniform(domain, NumIntervals::try_new(4).unwrap());
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
let original_intervals = *union.num_refined_intervals().as_ref();
let uniform_refinement =
union.refine_uniform(&PositiveNumPoints1D::try_new(1).unwrap());
assert_eq!(
uniform_refinement.refined_grid().num_intervals().as_ref(),
&(original_intervals * 2)
);
}
#[test]
fn selective_refinement_of_union() {
let domain = IntervalClosed::new(real(0.0), real(2.0));
let grid_a = Grid1D::uniform(domain.clone(), NumIntervals::try_new(2).unwrap());
let grid_b = Grid1D::uniform(domain, NumIntervals::try_new(4).unwrap());
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
let refinement_plan =
BTreeMap::from([(IntervalId::new(1), PositiveNumPoints1D::try_new(1).unwrap())]);
let selective_refinement = union.refine(&refinement_plan);
assert_eq!(
selective_refinement.refined_grid().num_intervals().as_ref(),
&5
);
}
#[test]
fn chained_union_refinements() {
let domain = IntervalClosed::new(real(0.0), real(2.0));
let grid_a = Grid1D::uniform(domain.clone(), NumIntervals::try_new(2).unwrap());
let grid_b = Grid1D::uniform(domain, NumIntervals::try_new(4).unwrap());
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
let first_refinement = union.refine_uniform(&PositiveNumPoints1D::try_new(1).unwrap());
assert_eq!(first_refinement.refined_grid().num_intervals().as_ref(), &8);
let refinement_plan =
BTreeMap::from([(IntervalId::new(1), PositiveNumPoints1D::try_new(1).unwrap())]);
let second_refinement = first_refinement
.into_original_grid()
.refine(&refinement_plan);
assert_eq!(
second_refinement.refined_grid().num_intervals().as_ref(),
&5
);
}
}
mod trait_implementations {
use super::*;
#[test]
fn has_domain_1d() {
let domain = IntervalClosed::new(real(0.0), real(2.0));
let grid_a = Grid1D::uniform(domain.clone(), NumIntervals::try_new(2).unwrap());
let grid_b = Grid1D::uniform(domain.clone(), NumIntervals::try_new(4).unwrap());
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
assert_eq!(union.domain(), &domain);
}
#[test]
fn has_coords_1d() {
let grid_a =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(1.0),
real(2.0),
]))
.unwrap();
let grid_b =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(0.5),
real(2.0),
]))
.unwrap();
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
assert_eq!(
union.coords().deref(),
&[real(0.0), real(0.5), real(1.0), real(2.0)]
);
}
#[test]
fn interval_partition_num_intervals() {
let domain = IntervalClosed::new(real(0.0), real(2.0));
let grid_a = Grid1D::uniform(domain.clone(), NumIntervals::try_new(2).unwrap());
let grid_b = Grid1D::uniform(domain, NumIntervals::try_new(4).unwrap());
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
assert_eq!(union.num_intervals().as_ref(), &4);
}
}
mod edge_cases {
use super::*;
#[test]
fn union_with_single_interval_grids() {
let domain = IntervalClosed::new(real(0.0), real(1.0));
let grid_a = Grid1D::uniform(domain.clone(), NumIntervals::try_new(1).unwrap());
let grid_b = Grid1D::uniform(domain, NumIntervals::try_new(1).unwrap());
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
assert_eq!(union.num_refined_intervals().as_ref(), &1);
}
#[test]
fn union_with_very_different_resolutions() {
let domain = IntervalClosed::new(real(0.0), real(1.0));
let coarse_grid = Grid1D::uniform(domain.clone(), NumIntervals::try_new(2).unwrap());
let fine_grid = Grid1D::uniform(domain, NumIntervals::try_new(100).unwrap());
let union = Grid1DUnion::try_new(&coarse_grid, &fine_grid).unwrap();
assert!(*union.num_refined_intervals().as_ref() >= 100);
}
#[test]
fn union_of_non_uniform_grids() {
let grid_a =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(0.1),
real(0.2),
real(1.0),
]))
.unwrap();
let grid_b =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(0.8),
real(0.9),
real(1.0),
]))
.unwrap();
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
let expected_points = vec![
real(0.0),
real(0.1),
real(0.2),
real(0.8),
real(0.9),
real(1.0),
];
assert_eq!(union.unified_grid().coords().deref(), &expected_points);
}
#[test]
fn union_with_overlapping_points() {
let grid_a =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(0.5),
real(1.0),
real(2.0),
]))
.unwrap();
let grid_b =
Grid1D::<IntervalClosed<Real>>::try_from_sorted(SortedSet::from_unsorted(vec![
real(0.0),
real(1.0),
real(1.5),
real(2.0),
]))
.unwrap();
let union = Grid1DUnion::try_new(&grid_a, &grid_b).unwrap();
assert_eq!(
union.unified_grid().coords().deref(),
&[real(0.0), real(0.5), real(1.0), real(1.5), real(2.0)]
);
assert_eq!(union.num_refined_intervals().as_ref(), &4);
}
}
}