use crate::allocators::TriDimAllocator;
use crate::assembly::buffers::{BufferUpdate, InterpolationBuffer};
use crate::space::{FindClosestElement, FiniteElementSpace, VolumetricFiniteElementSpace};
use crate::{Real, SmallDim};
use davenport::{define_thread_local_workspace, with_thread_local_workspace};
use itertools::izip;
use nalgebra::{DVectorView, DefaultAllocator, OMatrix, OPoint, OVector};
use std::array;
pub trait InterpolateInSpace<T: Real, SolutionDim: SmallDim>: FiniteElementSpace<T>
where
DefaultAllocator: TriDimAllocator<T, Self::GeometryDim, Self::ReferenceDim, SolutionDim>,
{
fn interpolate_at_point(
&self,
point: &OPoint<T, Self::GeometryDim>,
interpolation_weights: DVectorView<T>,
) -> OVector<T, SolutionDim> {
let mut buffer = [OVector::<_, SolutionDim>::zeros()];
self.interpolate_at_points_into(array::from_ref(point), interpolation_weights, &mut buffer);
let [result] = buffer;
result
}
fn interpolate_at_points_into(
&self,
points: &[OPoint<T, Self::GeometryDim>],
interpolation_weights: DVectorView<T>,
result_buffer: &mut [OVector<T, SolutionDim>],
);
fn interpolate_at_points(
&self,
points: &[OPoint<T, Self::GeometryDim>],
interpolation_weights: DVectorView<T>,
) -> Vec<OVector<T, SolutionDim>> {
let mut result = vec![OVector::<T, SolutionDim>::zeros(); points.len()];
self.interpolate_at_points_into(points, interpolation_weights, result.as_mut_slice());
result
}
}
pub trait InterpolateGradientInSpace<T: Real, SolutionDim: SmallDim>:
VolumetricFiniteElementSpace<T> + InterpolateInSpace<T, SolutionDim>
where
DefaultAllocator: TriDimAllocator<T, Self::GeometryDim, Self::ReferenceDim, SolutionDim>,
{
fn interpolate_gradient_at_point(
&self,
point: &OPoint<T, Self::GeometryDim>,
interpolation_weights: DVectorView<T>,
) -> OMatrix<T, Self::GeometryDim, SolutionDim> {
let mut buffer = [OMatrix::<_, Self::GeometryDim, SolutionDim>::zeros()];
self.interpolate_gradient_at_points_into(array::from_ref(point), interpolation_weights, &mut buffer);
let [result] = buffer;
result
}
fn interpolate_gradient_at_points_into(
&self,
points: &[OPoint<T, Self::GeometryDim>],
interpolation_weights: DVectorView<T>,
result_buffer: &mut [OMatrix<T, Self::GeometryDim, SolutionDim>],
);
fn interpolate_gradient_at_points(
&self,
points: &[OPoint<T, Self::GeometryDim>],
interpolation_weights: DVectorView<T>,
) -> Vec<OMatrix<T, Self::GeometryDim, SolutionDim>> {
let mut result = vec![OMatrix::<_, Self::GeometryDim, SolutionDim>::zeros(); points.len()];
self.interpolate_gradient_at_points_into(points, interpolation_weights, result.as_mut_slice());
result
}
}
define_thread_local_workspace!(INTERPOLATE_WORKSPACE);
pub fn interpolate_at_points<T, SolutionDim, Space>(
space: &Space,
points: &[OPoint<T, Space::GeometryDim>],
interpolation_weights: DVectorView<T>,
result_buffer: &mut [OVector<T, SolutionDim>],
) where
T: Real,
SolutionDim: SmallDim,
Space: FindClosestElement<T>,
DefaultAllocator: TriDimAllocator<T, Space::GeometryDim, Space::ReferenceDim, SolutionDim>,
{
assert_eq!(points.len(), result_buffer.len());
let u = interpolation_weights;
let d = SolutionDim::dim();
with_thread_local_workspace(&INTERPOLATE_WORKSPACE, |buf: &mut InterpolationBuffer<T>| {
for (point, interpolation) in izip!(points, result_buffer.iter_mut()) {
let closest = space.find_closest_element_and_reference_coords(point);
if let Some((element, ref_coords)) = closest {
let mut element_buf = buf.prepare_element_in_space(element, space, u, d);
element_buf.update_reference_point(&ref_coords, BufferUpdate::BasisValues);
*interpolation = element_buf.interpolate();
} else {
*interpolation = OVector::<T, SolutionDim>::zeros();
}
}
})
}
pub fn interpolate_gradient_at_points<T, SolutionDim, Space>(
space: &Space,
points: &[OPoint<T, Space::GeometryDim>],
interpolation_weights: DVectorView<T>,
result_buffer: &mut [OMatrix<T, Space::GeometryDim, SolutionDim>],
) where
T: Real,
SolutionDim: SmallDim,
Space: FindClosestElement<T> + VolumetricFiniteElementSpace<T>,
DefaultAllocator: TriDimAllocator<T, Space::GeometryDim, Space::ReferenceDim, SolutionDim>,
{
assert_eq!(points.len(), result_buffer.len());
let u = interpolation_weights;
let d = SolutionDim::dim();
with_thread_local_workspace(&INTERPOLATE_WORKSPACE, |buf: &mut InterpolationBuffer<T>| {
for (point, gradient) in izip!(points, result_buffer.iter_mut()) {
let closest = space.find_closest_element_and_reference_coords(point);
if let Some((element, ref_coords)) = closest {
let mut element_buf = buf.prepare_element_in_space(element, space, u, d);
element_buf.update_reference_point(&ref_coords, BufferUpdate::BasisGradients);
let ref_gradient = element_buf.interpolate_ref_gradient();
let j = element_buf.element_reference_jacobian();
let inv_j_t = j.try_inverse().expect("TODO: Fix this").transpose();
*gradient = inv_j_t * ref_gradient;
} else {
*gradient = OMatrix::<T, Space::GeometryDim, SolutionDim>::zeros();
}
}
})
}