carapace_core/model/continuum/
shape.rs1pub const QUAD4_CORNERS: [[f64; 2]; 4] = [[-1.0, -1.0], [1.0, -1.0], [1.0, 1.0], [-1.0, 1.0]];
8
9pub fn tri3_shape(xi: f64, eta: f64) -> [f64; 3] {
11 [1.0 - xi - eta, xi, eta]
12}
13
14pub fn tri3_dshape() -> [[f64; 2]; 3] {
16 [[-1.0, -1.0], [1.0, 0.0], [0.0, 1.0]]
17}
18
19pub fn quad4_shape(xi: f64, eta: f64) -> [f64; 4] {
21 std::array::from_fn(|i| {
22 let [xi_i, eta_i] = QUAD4_CORNERS[i];
23 0.25 * (1.0 + xi_i * xi) * (1.0 + eta_i * eta)
24 })
25}
26
27pub fn quad4_dshape(xi: f64, eta: f64) -> [[f64; 2]; 4] {
29 std::array::from_fn(|i| {
30 let [xi_i, eta_i] = QUAD4_CORNERS[i];
31 [
32 0.25 * xi_i * (1.0 + eta_i * eta),
33 0.25 * eta_i * (1.0 + xi_i * xi),
34 ]
35 })
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41
42 const POINTS: [[f64; 2]; 4] = [[0.1, 0.2], [0.3, 0.3], [-0.7, 0.4], [0.5, -0.9]];
43
44 #[test]
45 fn shape_functions_partition_unity_and_derivatives_sum_to_zero() {
46 for [xi, eta] in POINTS {
47 let tri = [xi.abs() * 0.4, eta.abs() * 0.4];
48 assert!((tri3_shape(tri[0], tri[1]).iter().sum::<f64>() - 1.0).abs() < 1e-15);
49 assert!((quad4_shape(xi, eta).iter().sum::<f64>() - 1.0).abs() < 1e-15);
50 for axis in 0..2 {
51 assert!(tri3_dshape().iter().map(|d| d[axis]).sum::<f64>().abs() < 1e-15);
52 assert!(
53 quad4_dshape(xi, eta)
54 .iter()
55 .map(|d| d[axis])
56 .sum::<f64>()
57 .abs()
58 < 1e-15
59 );
60 }
61 }
62 }
63
64 #[test]
65 fn shape_functions_are_one_at_their_own_node_and_zero_elsewhere() {
66 let tri_nodes = [[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]];
67 for (k, [xi, eta]) in tri_nodes.into_iter().enumerate() {
68 for (i, n) in tri3_shape(xi, eta).into_iter().enumerate() {
69 assert_eq!(n, if i == k { 1.0 } else { 0.0 });
70 }
71 }
72 for (k, [xi, eta]) in QUAD4_CORNERS.into_iter().enumerate() {
73 for (i, n) in quad4_shape(xi, eta).into_iter().enumerate() {
74 assert_eq!(n, if i == k { 1.0 } else { 0.0 });
75 }
76 }
77 }
78
79 #[test]
80 fn quad_derivatives_match_central_finite_differences() {
81 let h = 1e-6;
82 for [xi, eta] in POINTS {
83 let d = quad4_dshape(xi, eta);
84 let (dxi_p, dxi_m) = (quad4_shape(xi + h, eta), quad4_shape(xi - h, eta));
85 let (deta_p, deta_m) = (quad4_shape(xi, eta + h), quad4_shape(xi, eta - h));
86 for i in 0..4 {
87 assert!((d[i][0] - (dxi_p[i] - dxi_m[i]) / (2.0 * h)).abs() < 1e-9);
88 assert!((d[i][1] - (deta_p[i] - deta_m[i]) / (2.0 * h)).abs() < 1e-9);
89 }
90 }
91 let d = tri3_dshape();
92 let (a, b) = (tri3_shape(0.3 + h, 0.2), tri3_shape(0.3 - h, 0.2));
93 let (c, e) = (tri3_shape(0.3, 0.2 + h), tri3_shape(0.3, 0.2 - h));
94 for i in 0..3 {
95 assert!((d[i][0] - (a[i] - b[i]) / (2.0 * h)).abs() < 1e-9);
96 assert!((d[i][1] - (c[i] - e[i]) / (2.0 * h)).abs() < 1e-9);
97 }
98 }
99}