carapace_core/model/continuum/
quadrature.rs1const G: f64 = 0.577_350_269_189_625_8; pub const TRI3_GAUSS_1: [(f64, f64, f64); 1] = [(1.0 / 3.0, 1.0 / 3.0, 0.5)];
9
10pub const TRI3_GAUSS_3: [(f64, f64, f64); 3] = [
12 (1.0 / 6.0, 1.0 / 6.0, 1.0 / 6.0),
13 (2.0 / 3.0, 1.0 / 6.0, 1.0 / 6.0),
14 (1.0 / 6.0, 2.0 / 3.0, 1.0 / 6.0),
15];
16
17pub const QUAD4_GAUSS_2X2: [(f64, f64, f64); 4] =
20 [(-G, -G, 1.0), (G, -G, 1.0), (G, G, 1.0), (-G, G, 1.0)];
21
22pub const EDGE_GAUSS_2: [(f64, f64); 2] = [(-G, 1.0), (G, 1.0)];
24
25#[cfg(test)]
26mod tests {
27 use super::*;
28
29 fn integrate(rule: &[(f64, f64, f64)], f: impl Fn(f64, f64) -> f64) -> f64 {
30 rule.iter().map(|&(x, y, w)| w * f(x, y)).sum()
31 }
32
33 #[test]
34 fn weights_give_the_reference_areas() {
35 assert!((integrate(&TRI3_GAUSS_1, |_, _| 1.0) - 0.5).abs() < 1e-15);
36 assert!((integrate(&TRI3_GAUSS_3, |_, _| 1.0) - 0.5).abs() < 1e-15);
37 assert!((integrate(&QUAD4_GAUSS_2X2, |_, _| 1.0) - 4.0).abs() < 1e-15);
38 assert!((EDGE_GAUSS_2.iter().map(|p| p.1).sum::<f64>() - 2.0).abs() < 1e-15);
39 }
40
41 #[test]
42 fn triangle_rules_are_exact_to_their_degree() {
43 assert!((integrate(&TRI3_GAUSS_1, |x, y| x + 2.0 * y) - 3.0 / 6.0).abs() < 1e-15);
45 for (a, b, exact) in [(2, 0, 1.0 / 12.0), (1, 1, 1.0 / 24.0), (0, 2, 1.0 / 12.0)] {
46 let got = integrate(&TRI3_GAUSS_3, |x, y| x.powi(a) * y.powi(b));
47 assert!((got - exact).abs() < 1e-15, "x^{a} y^{b}: {got} vs {exact}");
48 }
49 }
50
51 #[test]
52 fn quad_and_edge_rules_integrate_cubics_exactly() {
53 assert!((integrate(&QUAD4_GAUSS_2X2, |x, y| x * x * y * y) - 4.0 / 9.0).abs() < 1e-15);
55 assert!(integrate(&QUAD4_GAUSS_2X2, |x, y| x.powi(3) * y).abs() < 1e-15);
56 assert!(
57 (integrate(&QUAD4_GAUSS_2X2, |x, y| x * x + y.powi(3) + 1.0) - (4.0 / 3.0 + 4.0)).abs()
58 < 1e-14
59 );
60 let edge = |f: fn(f64) -> f64| EDGE_GAUSS_2.iter().map(|&(t, w)| w * f(t)).sum::<f64>();
61 assert!((edge(|t| t * t) - 2.0 / 3.0).abs() < 1e-15);
62 assert!(edge(|t| t.powi(3)).abs() < 1e-15);
63 }
64}