use crate::element::{FiniteElement, Tri3d2Element};
use crate::integrate::volume_form;
use crate::quadrature::{
BorrowedQuadratureParts, Quadrature, Quadrature1d, Quadrature2d, QuadraturePair1d, QuadraturePair2d,
};
use crate::Real;
use itertools::izip;
use nalgebra::{point, vector, Point1, Point2, U2};
use numeric_literals::replace_float_literals;
pub fn subdivide_univariate<T>(quadrature: impl Quadrature1d<T>, subdivision_pieces: usize) -> QuadraturePair1d<T>
where
T: Real,
{
subdivide_univariate_(quadrature.weights(), quadrature.points(), subdivision_pieces)
}
#[replace_float_literals(T::from_f64(literal).unwrap())]
fn subdivide_univariate_<T>(
reference_weights: &[T],
reference_points: &[Point1<T>],
subdivision_pieces: usize,
) -> QuadraturePair1d<T>
where
T: Real,
{
let mut points = Vec::new();
let mut weights = Vec::new();
let pieces_as_scalar = T::from_usize(subdivision_pieces)
.expect("Internal error: Failed to convert usize to scalar type");
let subdivision_size = 2.0 / pieces_as_scalar;
for i in 0..subdivision_pieces {
let i = T::from_usize(i).expect("Internal error: Failed to convert usize to scalar type.");
let a = i * subdivision_size - 1.0;
let b = a + subdivision_size;
let jacobian = (b - a) / 2.0;
for (ref_weight, ref_point) in reference_weights.iter().zip(reference_points) {
let weight = ref_weight.clone() * jacobian;
let point = ((b - a) * ref_point[0] + (b + a)) / 2.0;
weights.push(weight);
points.push(Point1::new(point));
}
}
(weights, points)
}
pub fn subdivide_triangle<T>(quadrature: impl Quadrature2d<T, Data = ()>, subdivisions: usize) -> QuadraturePair2d<T>
where
T: Real,
{
subdivide_triangle_(quadrature.to_parts(), subdivisions)
}
#[replace_float_literals(T::from_f64(literal).unwrap())]
fn subdivide_triangle_<T>(
base_quadrature: BorrowedQuadratureParts<T, U2, ()>,
subdivisions: usize,
) -> QuadraturePair2d<T>
where
T: Real,
{
assert!(
subdivisions > 0,
"Number of subdivisions must be greater or equal to 1."
);
let cell_size = 2.0 / T::from_usize(subdivisions).unwrap();
let mut quadrature = QuadraturePair2d::default();
for i in 0..subdivisions {
for j in 0..=i {
let t_i = T::from_usize(i).unwrap();
let t_j = T::from_usize(j).unwrap();
let cell_center = point![-1.0 + cell_size * (t_j + 0.5), 1.0 - cell_size * (t_i + 0.5)];
let cell_vertices = [
&cell_center + vector![-1.0, -1.0] * 0.5 * cell_size,
&cell_center + vector![1.0, -1.0] * 0.5 * cell_size,
&cell_center + vector![1.0, 1.0] * 0.5 * cell_size,
&cell_center + vector![-1.0, 1.0] * 0.5 * cell_size,
];
let lower_verts = [cell_vertices[0], cell_vertices[1], cell_vertices[3]];
let upper_verts = [cell_vertices[1], cell_vertices[2], cell_vertices[3]];
add_triangle_quadrature(&mut quadrature, lower_verts, base_quadrature.to_parts());
if i != j {
add_triangle_quadrature(&mut quadrature, upper_verts, base_quadrature.to_parts());
}
}
}
quadrature
}
fn add_triangle_quadrature<T: Real>(
quadrature: &mut QuadraturePair2d<T>,
triangle_vertices: [Point2<T>; 3],
base_quadrature: BorrowedQuadratureParts<T, U2, ()>,
) {
let triangle = Tri3d2Element::from_vertices(triangle_vertices);
for (w_base, xi_base) in izip!(base_quadrature.weights(), base_quadrature.points()) {
let x = triangle.map_reference_coords(xi_base);
let j = triangle.reference_jacobian(xi_base);
let w = volume_form(&j) * w_base.clone();
quadrature.0.push(w);
quadrature.1.push(x);
}
}