1#[cfg(test)]
2mod test;
3
4use crate::{
5 fem::block::element::{
6 ElementNodalEitherCoordinates, FRAC_SQRT_3_5, FiniteElement, ParametricCoordinate,
7 ParametricCoordinates, ParametricReference, ShapeFunctions, ShapeFunctionsGradients,
8 quadratic::{M, QuadraticElement, QuadraticFiniteElement},
9 },
10 math::{Scalar, ScalarList, TensorRank1},
11};
12
13const G: usize = 27;
14const N: usize = 13;
15const P: usize = N;
16
17pub type Pyramid = QuadraticElement<G, N>;
18
19impl FiniteElement<G, M, N, P> for Pyramid {
20 fn integration_points() -> ParametricCoordinates<G, M> {
21 const X: [f64; 3] = [0.294997790111502, 0.652996233961648, 0.927005975926850];
22 let u1_2d = [
23 -FRAC_SQRT_3_5,
24 0.0,
25 FRAC_SQRT_3_5,
26 -FRAC_SQRT_3_5,
27 0.0,
28 FRAC_SQRT_3_5,
29 -FRAC_SQRT_3_5,
30 0.0,
31 FRAC_SQRT_3_5,
32 ];
33 let u2_2d = [
34 -FRAC_SQRT_3_5,
35 -FRAC_SQRT_3_5,
36 -FRAC_SQRT_3_5,
37 0.0,
38 0.0,
39 0.0,
40 FRAC_SQRT_3_5,
41 FRAC_SQRT_3_5,
42 FRAC_SQRT_3_5,
43 ];
44 X.into_iter()
45 .flat_map(|x| {
46 u1_2d
47 .into_iter()
48 .zip(u2_2d)
49 .map(move |(u1, u2)| TensorRank1::from([x * u1, x * u2, 1.0 - x]))
50 })
51 .collect()
52 }
53 fn integration_weights(&self) -> &ScalarList<G> {
54 &self.integration_weights
55 }
56 fn parametric_reference() -> ParametricReference<M, N> {
57 [
58 [-1.0, -1.0, 0.0],
59 [1.0, -1.0, 0.0],
60 [1.0, 1.0, 0.0],
61 [-1.0, 1.0, 0.0],
62 [0.0, 0.0, 1.0],
63 [0.0, -1.0, 0.0],
64 [1.0, 0.0, 0.0],
65 [0.0, 1.0, 0.0],
66 [-1.0, 0.0, 0.0],
67 [-0.5, -0.5, 0.5],
68 [0.5, -0.5, 0.5],
69 [0.5, 0.5, 0.5],
70 [-0.5, 0.5, 0.5],
71 ]
72 .into()
73 }
74 fn parametric_weights() -> ScalarList<G> {
75 const B: [f64; 3] = [0.029950703008581, 0.146246269259866, 0.157136361064887];
76 const W1: f64 = 5.0 / 9.0;
77 const W2: f64 = 8.0 / 9.0;
78 let w_2d = [
79 W1 * W1,
80 W2 * W1,
81 W1 * W1,
82 W1 * W2,
83 W2 * W2,
84 W1 * W2,
85 W1 * W1,
86 W2 * W1,
87 W1 * W1,
88 ];
89 B.into_iter()
90 .flat_map(|b| w_2d.into_iter().map(move |w| w * b))
91 .collect()
92 }
93 fn scaled_jacobians<const I: usize>(
94 _nodal_coordinates: ElementNodalEitherCoordinates<I, N>,
95 ) -> ScalarList<P> {
96 todo!()
97 }
98 fn shape_functions(parametric_coordinate: ParametricCoordinate<M>) -> ShapeFunctions<N> {
99 let [xi_1, xi_2, xi_3] = parametric_coordinate.into();
100 let bottom = bottom(xi_3);
101 [
102 0.25 * (-xi_1 - xi_2 - 1.0)
103 * ((1.0 - xi_1) * (1.0 - xi_2) - xi_3 + xi_1 * xi_2 * xi_3 / bottom),
104 0.25 * (xi_1 - xi_2 - 1.0)
105 * ((1.0 + xi_1) * (1.0 - xi_2) - xi_3 - xi_1 * xi_2 * xi_3 / bottom),
106 0.25 * (xi_1 + xi_2 - 1.0)
107 * ((1.0 + xi_1) * (1.0 + xi_2) - xi_3 + xi_1 * xi_2 * xi_3 / bottom),
108 0.25 * (-xi_1 + xi_2 - 1.0)
109 * ((1.0 - xi_1) * (1.0 + xi_2) - xi_3 - xi_1 * xi_2 * xi_3 / bottom),
110 xi_3 * (2.0 * xi_3 - 1.0),
111 0.5 * (1.0 + xi_1 - xi_3) * (1.0 - xi_1 - xi_3) * (1.0 - xi_2 - xi_3) / bottom,
112 0.5 * (1.0 + xi_2 - xi_3) * (1.0 - xi_2 - xi_3) * (1.0 + xi_1 - xi_3) / bottom,
113 0.5 * (1.0 + xi_1 - xi_3) * (1.0 - xi_1 - xi_3) * (1.0 + xi_2 - xi_3) / bottom,
114 0.5 * (1.0 + xi_2 - xi_3) * (1.0 - xi_2 - xi_3) * (1.0 - xi_1 - xi_3) / bottom,
115 xi_3 * (1.0 - xi_1 - xi_3) * (1.0 - xi_2 - xi_3) / bottom,
116 xi_3 * (1.0 + xi_1 - xi_3) * (1.0 - xi_2 - xi_3) / bottom,
117 xi_3 * (1.0 + xi_1 - xi_3) * (1.0 + xi_2 - xi_3) / bottom,
118 xi_3 * (1.0 - xi_1 - xi_3) * (1.0 + xi_2 - xi_3) / bottom,
119 ]
120 .into()
121 }
122 fn shape_functions_gradients(
123 parametric_coordinate: ParametricCoordinate<M>,
124 ) -> ShapeFunctionsGradients<M, N> {
125 let [xi_1, xi_2, xi_3] = parametric_coordinate.into();
126 let bottom = bottom(xi_3);
127 let bottom_squared = bottom * bottom;
128 [
129 [
130 0.25 * ((-xi_1 - xi_2 - 1.0) * (-1.0 + xi_2 + xi_2 * xi_3 / bottom)
131 - ((1.0 - xi_1) * (1.0 - xi_2) - xi_3 + xi_1 * xi_2 * xi_3 / bottom)),
132 0.25 * ((-xi_1 - xi_2 - 1.0) * (-1.0 + xi_1 + xi_1 * xi_3 / bottom)
133 - ((1.0 - xi_1) * (1.0 - xi_2) - xi_3 + xi_1 * xi_2 * xi_3 / bottom)),
134 0.25 * (-xi_1 - xi_2 - 1.0)
135 * (-1.0 + xi_1 * xi_2 / bottom + xi_1 * xi_2 * xi_3 / bottom_squared),
136 ],
137 [
138 0.25 * ((xi_1 - xi_2 - 1.0) * (1.0 - xi_2 - xi_2 * xi_3 / bottom)
139 + ((1.0 + xi_1) * (1.0 - xi_2) - xi_3 - xi_1 * xi_2 * xi_3 / bottom)),
140 0.25 * ((xi_1 - xi_2 - 1.0) * (-1.0 - xi_1 - xi_1 * xi_3 / bottom)
141 - ((1.0 + xi_1) * (1.0 - xi_2) - xi_3 - xi_1 * xi_2 * xi_3 / bottom)),
142 0.25 * (xi_1 - xi_2 - 1.0)
143 * (-1.0 - xi_1 * xi_2 / bottom - xi_1 * xi_2 * xi_3 / bottom_squared),
144 ],
145 [
146 0.25 * ((xi_1 + xi_2 - 1.0) * (1.0 + xi_2 + xi_2 * xi_3 / bottom)
147 + ((1.0 + xi_1) * (1.0 + xi_2) - xi_3 + xi_1 * xi_2 * xi_3 / bottom)),
148 0.25 * ((xi_1 + xi_2 - 1.0) * (1.0 + xi_1 + xi_1 * xi_3 / bottom)
149 + ((1.0 + xi_1) * (1.0 + xi_2) - xi_3 + xi_1 * xi_2 * xi_3 / bottom)),
150 0.25 * (xi_1 + xi_2 - 1.0)
151 * (-1.0 + xi_1 * xi_2 / bottom + xi_1 * xi_2 * xi_3 / bottom_squared),
152 ],
153 [
154 0.25 * ((-xi_1 + xi_2 - 1.0) * (-1.0 - xi_2 - xi_2 * xi_3 / bottom)
155 - ((1.0 - xi_1) * (1.0 + xi_2) - xi_3 - xi_1 * xi_2 * xi_3 / bottom)),
156 0.25 * ((-xi_1 + xi_2 - 1.0) * (1.0 - xi_1 - xi_1 * xi_3 / bottom)
157 + ((1.0 - xi_1) * (1.0 + xi_2) - xi_3 - xi_1 * xi_2 * xi_3 / bottom)),
158 0.25 * (-xi_1 + xi_2 - 1.0)
159 * (-1.0 - xi_1 * xi_2 / bottom - xi_1 * xi_2 * xi_3 / bottom_squared),
160 ],
161 [0.0, 0.0, 4.0 * xi_3 - 1.0],
162 [
163 -xi_1 * (1.0 - xi_2 - xi_3) / bottom,
164 -0.5 * (1.0 - xi_1 - xi_3) * (1.0 + xi_1 - xi_3) / bottom,
165 0.5 * (xi_1 * xi_1 * xi_2 / bottom_squared + xi_2) - 1.0 + xi_3,
166 ],
167 [
168 0.5 * (1.0 - xi_2 - xi_3) * (1.0 + xi_2 - xi_3) / bottom,
169 -xi_2 * (1.0 + xi_1 - xi_3) / bottom,
170 -0.5 * (xi_1 * xi_2 * xi_2 / bottom_squared + xi_1) - 1.0 + xi_3,
171 ],
172 [
173 -xi_1 * (1.0 + xi_2 - xi_3) / bottom,
174 0.5 * (1.0 - xi_1 - xi_3) * (1.0 + xi_1 - xi_3) / bottom,
175 -0.5 * (xi_1 * xi_1 * xi_2 / bottom_squared + xi_2) - 1.0 + xi_3,
176 ],
177 [
178 -0.5 * (1.0 - xi_2 - xi_3) * (1.0 + xi_2 - xi_3) / bottom,
179 -xi_2 * (1.0 - xi_1 - xi_3) / bottom,
180 0.5 * (xi_1 * xi_2 * xi_2 / bottom_squared + xi_1) - 1.0 + xi_3,
181 ],
182 [
183 -(1.0 - xi_2 - xi_3) * xi_3 / bottom,
184 -(1.0 - xi_1 - xi_3) * xi_3 / bottom,
185 xi_1 * xi_2 / bottom_squared + 1.0 - xi_1 - xi_2 - 2.0 * xi_3,
186 ],
187 [
188 (1.0 - xi_2 - xi_3) * xi_3 / bottom,
189 -(1.0 + xi_1 - xi_3) * xi_3 / bottom,
190 -xi_1 * xi_2 / bottom_squared + 1.0 + xi_1 - xi_2 - 2.0 * xi_3,
191 ],
192 [
193 (1.0 + xi_2 - xi_3) * xi_3 / bottom,
194 (1.0 + xi_1 - xi_3) * xi_3 / bottom,
195 xi_1 * xi_2 / bottom_squared + 1.0 + xi_1 + xi_2 - 2.0 * xi_3,
196 ],
197 [
198 -(1.0 + xi_2 - xi_3) * xi_3 / bottom,
199 (1.0 - xi_1 - xi_3) * xi_3 / bottom,
200 -xi_1 * xi_2 / bottom_squared + 1.0 - xi_1 + xi_2 - 2.0 * xi_3,
201 ],
202 ]
203 .into()
204 }
205}
206
207fn bottom(xi_3: Scalar) -> Scalar {
208 const SMALL: Scalar = 4e1 * f64::EPSILON;
209 if (1.0 - xi_3).abs() > SMALL {
210 1.0 - xi_3
211 } else {
212 SMALL
213 }
214}
215
216impl QuadraticFiniteElement<G, N> for Pyramid {}