use crate::data::atlas::Atlas;
use crate::data::section::Section;
use crate::data::storage::Storage;
use crate::mesh_error::MeshSieveError;
use crate::topology::point::PointId;
#[derive(Clone, Debug)]
pub struct HighOrderCoordinates<V, S: Storage<V>> {
dimension: usize,
section: Section<V, S>,
}
impl<V, S> HighOrderCoordinates<V, S>
where
S: Storage<V>,
{
#[inline]
pub fn dimension(&self) -> usize {
self.dimension
}
#[inline]
pub fn section(&self) -> &Section<V, S> {
&self.section
}
#[inline]
pub fn section_mut(&mut self) -> &mut Section<V, S> {
&mut self.section
}
}
impl<V, S> HighOrderCoordinates<V, S>
where
V: Clone + Default,
S: Storage<V> + Clone,
{
pub fn try_new(dimension: usize, atlas: Atlas) -> Result<Self, MeshSieveError> {
validate_high_order_dimension(dimension, &atlas)?;
Ok(Self {
dimension,
section: Section::new(atlas),
})
}
pub fn from_section(dimension: usize, section: Section<V, S>) -> Result<Self, MeshSieveError> {
validate_high_order_dimension(dimension, section.atlas())?;
Ok(Self { dimension, section })
}
}
#[derive(Clone, Debug)]
pub struct MeshVelocity<V, S: Storage<V>> {
dimension: usize,
section: Section<V, S>,
}
impl<V, S> MeshVelocity<V, S>
where
S: Storage<V>,
{
#[inline]
pub fn dimension(&self) -> usize {
self.dimension
}
#[inline]
pub fn section(&self) -> &Section<V, S> {
&self.section
}
#[inline]
pub fn section_mut(&mut self) -> &mut Section<V, S> {
&mut self.section
}
#[inline]
pub fn try_restrict(&self, p: PointId) -> Result<&[V], MeshSieveError> {
self.section.try_restrict(p)
}
#[inline]
pub fn try_restrict_mut(&mut self, p: PointId) -> Result<&mut [V], MeshSieveError> {
self.section.try_restrict_mut(p)
}
}
impl<V, S> MeshVelocity<V, S>
where
V: Clone + Default,
S: Storage<V> + Clone,
{
pub fn try_new(dimension: usize, atlas: Atlas) -> Result<Self, MeshSieveError> {
validate_dimension(dimension, &atlas)?;
Ok(Self {
dimension,
section: Section::new(atlas),
})
}
pub fn from_section(dimension: usize, section: Section<V, S>) -> Result<Self, MeshSieveError> {
validate_dimension(dimension, section.atlas())?;
Ok(Self { dimension, section })
}
pub fn try_add_point(&mut self, p: PointId) -> Result<(), MeshSieveError> {
self.section.try_add_point(p, self.dimension)
}
}
#[derive(Clone, Debug)]
pub struct Coordinates<V, S: Storage<V>> {
topological_dimension: usize,
embedding_dimension: usize,
section: Section<V, S>,
high_order: Option<HighOrderCoordinates<V, S>>,
}
impl<V, S> Coordinates<V, S>
where
S: Storage<V>,
{
#[inline]
pub fn dimension(&self) -> usize {
self.embedding_dimension
}
#[inline]
pub fn topological_dimension(&self) -> usize {
self.topological_dimension
}
#[inline]
pub fn embedding_dimension(&self) -> usize {
self.embedding_dimension
}
#[inline]
pub fn section(&self) -> &Section<V, S> {
&self.section
}
#[inline]
pub fn section_mut(&mut self) -> &mut Section<V, S> {
&mut self.section
}
#[inline]
pub fn high_order(&self) -> Option<&HighOrderCoordinates<V, S>> {
self.high_order.as_ref()
}
#[inline]
pub fn high_order_mut(&mut self) -> Option<&mut HighOrderCoordinates<V, S>> {
self.high_order.as_mut()
}
pub fn set_high_order(
&mut self,
high_order: HighOrderCoordinates<V, S>,
) -> Result<(), MeshSieveError> {
if high_order.dimension != self.embedding_dimension {
return Err(MeshSieveError::InvalidGeometry(format!(
"higher-order coordinate dimension {} does not match base dimension {}",
high_order.dimension, self.embedding_dimension
)));
}
self.high_order = Some(high_order);
Ok(())
}
#[inline]
pub fn into_section(self) -> Section<V, S> {
self.section
}
#[inline]
pub fn try_restrict(&self, p: PointId) -> Result<&[V], MeshSieveError> {
self.section.try_restrict(p)
}
#[inline]
pub fn try_restrict_mut(&mut self, p: PointId) -> Result<&mut [V], MeshSieveError> {
self.section.try_restrict_mut(p)
}
}
impl<S> Coordinates<f64, S>
where
S: Storage<f64>,
{
pub fn advance_with_velocity<St>(
&mut self,
velocity: &MeshVelocity<f64, St>,
dt: f64,
) -> Result<(), MeshSieveError>
where
St: Storage<f64>,
{
let dim = self.embedding_dimension;
let points: Vec<PointId> = self.section.atlas().points().collect();
for point in points {
let vel = velocity.try_restrict(point)?;
if vel.len() != dim {
return Err(MeshSieveError::SliceLengthMismatch {
point,
expected: dim,
found: vel.len(),
});
}
let coord = self.try_restrict_mut(point)?;
if coord.len() != dim {
return Err(MeshSieveError::SliceLengthMismatch {
point,
expected: dim,
found: coord.len(),
});
}
for (coord_value, vel_value) in coord.iter_mut().zip(vel.iter()) {
*coord_value += dt * vel_value;
}
}
Ok(())
}
}
impl<V, S> Coordinates<V, S>
where
V: Clone + Default,
S: Storage<V> + Clone,
{
pub fn try_new(
topological_dimension: usize,
embedding_dimension: usize,
atlas: Atlas,
) -> Result<Self, MeshSieveError> {
validate_coordinate_dimensions(topological_dimension, embedding_dimension, &atlas)?;
Ok(Self {
topological_dimension,
embedding_dimension,
section: Section::new(atlas),
high_order: None,
})
}
pub fn from_section(
topological_dimension: usize,
embedding_dimension: usize,
section: Section<V, S>,
) -> Result<Self, MeshSieveError> {
validate_coordinate_dimensions(
topological_dimension,
embedding_dimension,
section.atlas(),
)?;
Ok(Self {
topological_dimension,
embedding_dimension,
section,
high_order: None,
})
}
pub fn try_add_point(&mut self, p: PointId) -> Result<(), MeshSieveError> {
self.section.try_add_point(p, self.embedding_dimension)
}
}
fn validate_coordinate_dimensions(
topological_dimension: usize,
embedding_dimension: usize,
atlas: &Atlas,
) -> Result<(), MeshSieveError> {
if embedding_dimension == 0 {
return Err(MeshSieveError::ZeroLengthSlice);
}
if topological_dimension > embedding_dimension {
return Err(MeshSieveError::InvalidGeometry(format!(
"topological dimension {topological_dimension} exceeds embedding dimension {embedding_dimension}"
)));
}
for (point, (_offset, len)) in atlas.iter_entries() {
if len != embedding_dimension {
return Err(MeshSieveError::SliceLengthMismatch {
point,
expected: embedding_dimension,
found: len,
});
}
}
Ok(())
}
fn validate_dimension(dimension: usize, atlas: &Atlas) -> Result<(), MeshSieveError> {
if dimension == 0 {
return Err(MeshSieveError::ZeroLengthSlice);
}
for (point, (_offset, len)) in atlas.iter_entries() {
if len != dimension {
return Err(MeshSieveError::SliceLengthMismatch {
point,
expected: dimension,
found: len,
});
}
}
Ok(())
}
fn validate_high_order_dimension(dimension: usize, atlas: &Atlas) -> Result<(), MeshSieveError> {
if dimension == 0 {
return Err(MeshSieveError::ZeroLengthSlice);
}
for (point, (_offset, len)) in atlas.iter_entries() {
if len == 0 || len % dimension != 0 {
return Err(MeshSieveError::SliceLengthMismatch {
point,
expected: dimension,
found: len,
});
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::{Coordinates, MeshVelocity};
use crate::data::atlas::Atlas;
use crate::data::storage::VecStorage;
use crate::topology::point::PointId;
#[test]
fn advance_coordinates_over_multiple_steps() {
let mut atlas = Atlas::default();
let p1 = PointId::new(1).unwrap();
let p2 = PointId::new(2).unwrap();
atlas.try_insert(p1, 3).unwrap();
atlas.try_insert(p2, 3).unwrap();
let mut coords = Coordinates::<f64, VecStorage<f64>>::try_new(3, 3, atlas.clone()).unwrap();
let mut velocity = MeshVelocity::<f64, VecStorage<f64>>::try_new(3, atlas).unwrap();
coords.section_mut().try_set(p1, &[0.0, 0.0, 0.0]).unwrap();
coords.section_mut().try_set(p2, &[1.0, 1.0, 1.0]).unwrap();
velocity
.section_mut()
.try_set(p1, &[1.0, 0.0, -1.0])
.unwrap();
velocity
.section_mut()
.try_set(p2, &[0.5, -0.5, 1.0])
.unwrap();
let dt = 0.25;
for _ in 0..4 {
coords.advance_with_velocity(&velocity, dt).unwrap();
}
assert_eq!(coords.try_restrict(p1).unwrap(), &[1.0, 0.0, -1.0]);
assert_eq!(coords.try_restrict(p2).unwrap(), &[1.5, 0.5, 2.0]);
}
}