use crate::mesh::Scalar;
use crate::mesh::quadrature::{GaussLegendreRule, gauss_legendre_interval};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QuadratureRule {
GaussLegendre3,
GaussLegendre4,
}
impl QuadratureRule {
pub fn from_code(code: u8) -> Result<Self, String> {
match code {
3 => Ok(Self::GaussLegendre3),
4 => Ok(Self::GaussLegendre4),
_ => Err(format!(
"unsupported quadrature code {code}; use 3 for GaussLegendre3 or 4 for GaussLegendre4"
)),
}
}
pub fn points_per_element(self) -> usize {
match self {
Self::GaussLegendre3 => 9,
Self::GaussLegendre4 => 16,
}
}
const fn line_rule(self) -> GaussLegendreRule {
match self {
Self::GaussLegendre3 => GaussLegendreRule::Gauss3,
Self::GaussLegendre4 => GaussLegendreRule::Gauss4,
}
}
}
pub fn gauss_1d<F: Scalar>(rule: QuadratureRule) -> Vec<(F, F)> {
gauss_legendre_interval::<F>(rule.line_rule())
}
pub fn gauss_volume<F: Scalar>(rule: QuadratureRule) -> Vec<([F; 2], F)> {
let line = gauss_1d::<F>(rule);
let mut out = Vec::with_capacity(line.len() * line.len());
for (xi, wx) in &line {
for (eta, wy) in &line {
out.push(([*xi, *eta], *wx * *wy));
}
}
out
}
pub fn gauss_face<F: Scalar>(rule: QuadratureRule) -> Vec<(F, F)> {
gauss_1d::<F>(rule)
}
#[cfg(test)]
mod tests {
use super::{QuadratureRule, gauss_volume};
#[test]
fn gauss_3x3_integrates_quadratic_exactly() {
let integral: f64 = gauss_volume::<f64>(QuadratureRule::GaussLegendre3)
.into_iter()
.map(|([xi, eta], w)| (xi * xi + eta * eta) * w)
.sum();
assert!((integral - (8.0 / 3.0)).abs() < 1.0e-12);
}
#[test]
fn gauss_4x4_integrates_constant_to_four() {
let weight_sum: f64 = gauss_volume::<f64>(QuadratureRule::GaussLegendre4)
.into_iter()
.map(|(_, w)| w)
.sum();
assert!((weight_sum - 4.0).abs() < 1.0e-12);
}
#[test]
fn gauss_4x4_integrates_degree_six_polynomial_exactly() {
let integral: f64 = gauss_volume::<f64>(QuadratureRule::GaussLegendre4)
.into_iter()
.map(|([xi, eta], w)| (xi.powi(6) + eta.powi(6)) * w)
.sum();
let exact = 8.0 / 7.0;
assert!((integral - exact).abs() < 1.0e-12);
}
}