#[derive(Debug, Clone, Copy)]
pub struct QuadraturePoint {
pub coords: [f64; 3],
pub weight: f64,
}
impl QuadraturePoint {
pub fn new_1d(xi: f64, weight: f64) -> Self {
Self {
coords: [xi, 0.0, 0.0],
weight,
}
}
pub fn new_2d(xi: f64, eta: f64, weight: f64) -> Self {
Self {
coords: [xi, eta, 0.0],
weight,
}
}
pub fn new_3d(xi: f64, eta: f64, zeta: f64, weight: f64) -> Self {
Self {
coords: [xi, eta, zeta],
weight,
}
}
#[inline]
pub fn xi(&self) -> f64 {
self.coords[0]
}
#[inline]
pub fn eta(&self) -> f64 {
self.coords[1]
}
#[inline]
pub fn zeta(&self) -> f64 {
self.coords[2]
}
}
pub fn gauss_legendre_1d(order: usize) -> Vec<QuadraturePoint> {
match order {
1 => vec![QuadraturePoint::new_1d(0.0, 2.0)],
2 => {
let x = 1.0 / 3.0_f64.sqrt();
vec![
QuadraturePoint::new_1d(-x, 1.0),
QuadraturePoint::new_1d(x, 1.0),
]
}
3 => {
let x = (3.0 / 5.0_f64).sqrt();
vec![
QuadraturePoint::new_1d(-x, 5.0 / 9.0),
QuadraturePoint::new_1d(0.0, 8.0 / 9.0),
QuadraturePoint::new_1d(x, 5.0 / 9.0),
]
}
4 => {
let a = (3.0 / 7.0 - 2.0 / 7.0 * (6.0 / 5.0_f64).sqrt()).sqrt();
let b = (3.0 / 7.0 + 2.0 / 7.0 * (6.0 / 5.0_f64).sqrt()).sqrt();
let wa = (18.0 + 30.0_f64.sqrt()) / 36.0;
let wb = (18.0 - 30.0_f64.sqrt()) / 36.0;
vec![
QuadraturePoint::new_1d(-b, wb),
QuadraturePoint::new_1d(-a, wa),
QuadraturePoint::new_1d(a, wa),
QuadraturePoint::new_1d(b, wb),
]
}
5 => {
let a = (5.0 - 2.0 * (10.0 / 7.0_f64).sqrt()).sqrt() / 3.0;
let b = (5.0 + 2.0 * (10.0 / 7.0_f64).sqrt()).sqrt() / 3.0;
let wa = (322.0 + 13.0 * 70.0_f64.sqrt()) / 900.0;
let wb = (322.0 - 13.0 * 70.0_f64.sqrt()) / 900.0;
vec![
QuadraturePoint::new_1d(-b, wb),
QuadraturePoint::new_1d(-a, wa),
QuadraturePoint::new_1d(0.0, 128.0 / 225.0),
QuadraturePoint::new_1d(a, wa),
QuadraturePoint::new_1d(b, wb),
]
}
_ => {
gauss_legendre_1d(5)
}
}
}
pub fn gauss_triangle(order: usize) -> Vec<QuadraturePoint> {
match order {
1 => {
vec![QuadraturePoint::new_2d(1.0 / 3.0, 1.0 / 3.0, 0.5)]
}
2 | 3 => {
let a = 1.0 / 6.0;
let b = 2.0 / 3.0;
let w = 1.0 / 6.0;
vec![
QuadraturePoint::new_2d(a, a, w),
QuadraturePoint::new_2d(b, a, w),
QuadraturePoint::new_2d(a, b, w),
]
}
4 | 5 => {
let a1 = 1.0 / 3.0;
let w1 = 9.0 / 80.0;
let a2 = (6.0 - 15.0_f64.sqrt()) / 21.0;
let b2 = (9.0 + 2.0 * 15.0_f64.sqrt()) / 21.0;
let w2 = (155.0 - 15.0_f64.sqrt()) / 2400.0;
let a3 = (6.0 + 15.0_f64.sqrt()) / 21.0;
let b3 = (9.0 - 2.0 * 15.0_f64.sqrt()) / 21.0;
let w3 = (155.0 + 15.0_f64.sqrt()) / 2400.0;
vec![
QuadraturePoint::new_2d(a1, a1, w1),
QuadraturePoint::new_2d(a2, a2, w2),
QuadraturePoint::new_2d(b2, a2, w2),
QuadraturePoint::new_2d(a2, b2, w2),
QuadraturePoint::new_2d(a3, a3, w3),
QuadraturePoint::new_2d(b3, a3, w3),
QuadraturePoint::new_2d(a3, b3, w3),
]
}
_ => {
let a1 = 0.063089014491502;
let b1 = 0.873821971016996;
let w1 = 0.025422453185103;
let a2 = 0.249286745170910;
let b2 = 0.501426509658179;
let w2 = 0.058393137863189;
let a3 = 0.310352451033785;
let b3 = 0.053145049844816;
let c3 = 0.636502499121399;
let w3 = 0.041425537809187;
vec![
QuadraturePoint::new_2d(a1, a1, w1),
QuadraturePoint::new_2d(b1, a1, w1),
QuadraturePoint::new_2d(a1, b1, w1),
QuadraturePoint::new_2d(a2, a2, w2),
QuadraturePoint::new_2d(b2, a2, w2),
QuadraturePoint::new_2d(a2, b2, w2),
QuadraturePoint::new_2d(a3, b3, w3),
QuadraturePoint::new_2d(b3, a3, w3),
QuadraturePoint::new_2d(a3, c3, w3),
QuadraturePoint::new_2d(c3, a3, w3),
QuadraturePoint::new_2d(b3, c3, w3),
QuadraturePoint::new_2d(c3, b3, w3),
]
}
}
}
pub fn gauss_quadrilateral(order: usize) -> Vec<QuadraturePoint> {
let pts_1d = gauss_legendre_1d(order);
let mut result = Vec::with_capacity(pts_1d.len() * pts_1d.len());
for pi in &pts_1d {
for pj in &pts_1d {
result.push(QuadraturePoint::new_2d(
pi.xi(),
pj.xi(),
pi.weight * pj.weight,
));
}
}
result
}
pub fn gauss_tetrahedron(order: usize) -> Vec<QuadraturePoint> {
match order {
1 => {
vec![QuadraturePoint::new_3d(0.25, 0.25, 0.25, 1.0 / 6.0)]
}
2 => {
let a = (5.0 - 5.0_f64.sqrt()) / 20.0;
let b = (5.0 + 3.0 * 5.0_f64.sqrt()) / 20.0;
let w = 1.0 / 24.0;
vec![
QuadraturePoint::new_3d(a, a, a, w),
QuadraturePoint::new_3d(b, a, a, w),
QuadraturePoint::new_3d(a, b, a, w),
QuadraturePoint::new_3d(a, a, b, w),
]
}
3 | 4 => {
let w1 = -4.0 / 30.0;
let w2 = 9.0 / 120.0;
vec![
QuadraturePoint::new_3d(0.25, 0.25, 0.25, w1),
QuadraturePoint::new_3d(1.0 / 6.0, 1.0 / 6.0, 1.0 / 6.0, w2),
QuadraturePoint::new_3d(0.5, 1.0 / 6.0, 1.0 / 6.0, w2),
QuadraturePoint::new_3d(1.0 / 6.0, 0.5, 1.0 / 6.0, w2),
QuadraturePoint::new_3d(1.0 / 6.0, 1.0 / 6.0, 0.5, w2),
]
}
_ => {
let a1 = 0.25;
let w1 = 8.0 / 405.0;
let a2 = 1.0 / 3.0;
let b2 = 0.0;
let w2 = 5.0 / 567.0;
let a3 = (1.0 + 15.0_f64.sqrt()) / 8.0;
let b3 = (1.0 - 15.0_f64.sqrt()) / 8.0;
let w3 = 20.0 / 2187.0;
vec![
QuadraturePoint::new_3d(a1, a1, a1, w1),
QuadraturePoint::new_3d(a2, a2, a2, w2),
QuadraturePoint::new_3d(b2, a2, a2, w2),
QuadraturePoint::new_3d(a2, b2, a2, w2),
QuadraturePoint::new_3d(a2, a2, b2, w2),
QuadraturePoint::new_3d(a3, a3, a3, w3),
QuadraturePoint::new_3d(b3, a3, a3, w3),
QuadraturePoint::new_3d(a3, b3, a3, w3),
QuadraturePoint::new_3d(a3, a3, b3, w3),
QuadraturePoint::new_3d(a3, a3, b3, w3),
QuadraturePoint::new_3d(a3, b3, a3, w3),
QuadraturePoint::new_3d(b3, a3, a3, w3),
QuadraturePoint::new_3d(a3, b3, b3, w3),
QuadraturePoint::new_3d(b3, a3, b3, w3),
QuadraturePoint::new_3d(b3, b3, a3, w3),
]
}
}
}
pub fn gauss_hexahedron(order: usize) -> Vec<QuadraturePoint> {
let pts_1d = gauss_legendre_1d(order);
let mut result = Vec::with_capacity(pts_1d.len().pow(3));
for pi in &pts_1d {
for pj in &pts_1d {
for pk in &pts_1d {
result.push(QuadraturePoint::new_3d(
pi.xi(),
pj.xi(),
pk.xi(),
pi.weight * pj.weight * pk.weight,
));
}
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gauss_1d_integrates_polynomials() {
let pts = gauss_legendre_1d(2);
let integral: f64 = pts.iter().map(|p| p.xi().powi(2) * p.weight).sum();
assert!((integral - 2.0 / 3.0).abs() < 1e-14);
let integral: f64 = pts.iter().map(|p| p.xi().powi(3) * p.weight).sum();
assert!(integral.abs() < 1e-14);
}
#[test]
fn test_triangle_centroid_rule() {
let pts = gauss_triangle(1);
assert_eq!(pts.len(), 1);
assert!((pts[0].xi() - 1.0 / 3.0).abs() < 1e-14);
assert!((pts[0].eta() - 1.0 / 3.0).abs() < 1e-14);
assert!((pts[0].weight - 0.5).abs() < 1e-14); }
#[test]
fn test_quad_weights_sum() {
for order in 1..=5 {
let pts = gauss_quadrilateral(order);
let sum: f64 = pts.iter().map(|p| p.weight).sum();
assert!(
(sum - 4.0).abs() < 1e-14,
"Order {} failed: sum = {}",
order,
sum
);
}
}
#[test]
fn test_tet_weights_sum() {
let pts = gauss_tetrahedron(1);
let sum: f64 = pts.iter().map(|p| p.weight).sum();
assert!((sum - 1.0 / 6.0).abs() < 1e-14);
}
#[test]
fn test_hex_weights_sum() {
for order in 1..=3 {
let pts = gauss_hexahedron(order);
let sum: f64 = pts.iter().map(|p| p.weight).sum();
assert!(
(sum - 8.0).abs() < 1e-14,
"Order {} failed: sum = {}",
order,
sum
);
}
}
}