conspire/fem/block/element/linear/hexahedron/
mod.rs1#[cfg(test)]
2mod test;
3
4use super::*;
5use crate::{
6 constitutive::{Constitutive, Parameters},
7 math::{tensor_rank_1, tensor_rank_1_list, tensor_rank_1_list_2d},
8 mechanics::Scalar,
9};
10use std::array::from_fn;
11
12const G: usize = 8;
13const M: usize = 3;
14const N: usize = 8;
15const P: usize = N;
16
17#[cfg(test)]
18const Q: usize = N;
19
20const SQRT_3: Scalar = 1.732_050_807_568_877_2;
21
22pub type Hexahedron<C> = Element<C, G, N>;
23
24impl<C, Y> FiniteElement<C, G, N, Y> for Hexahedron<C>
25where
26 C: Constitutive<Y>,
27 Y: Parameters,
28{
29 fn new(
30 constitutive_model_parameters: Y,
31 reference_nodal_coordinates: ReferenceNodalCoordinates<N>,
32 ) -> Self {
33 let (gradient_vectors, integration_weights) = Self::initialize(reference_nodal_coordinates);
34 Self {
35 constitutive_models: from_fn(|_| <C>::new(constitutive_model_parameters)),
36 gradient_vectors,
37 integration_weights,
38 }
39 }
40 fn reference() -> ReferenceNodalCoordinates<N> {
41 tensor_rank_1_list([
42 tensor_rank_1([-1.0, -1.0, -1.0]),
43 tensor_rank_1([1.0, -1.0, -1.0]),
44 tensor_rank_1([1.0, 1.0, -1.0]),
45 tensor_rank_1([-1.0, 1.0, -1.0]),
46 tensor_rank_1([-1.0, -1.0, 1.0]),
47 tensor_rank_1([1.0, -1.0, 1.0]),
48 tensor_rank_1([1.0, 1.0, 1.0]),
49 tensor_rank_1([-1.0, 1.0, 1.0]),
50 ])
51 }
52 fn reset(&mut self) {
53 let (gradient_vectors, integration_weights) = Self::initialize(Self::reference());
54 self.gradient_vectors = gradient_vectors;
55 self.integration_weights = integration_weights;
56 }
57}
58
59impl<C> Hexahedron<C> {
60 fn initialize(
61 reference_nodal_coordinates: ReferenceNodalCoordinates<N>,
62 ) -> (GradientVectors<G, N>, Scalars<G>) {
63 let standard_gradient_operators = Self::standard_gradient_operators();
64 let gradient_vectors = standard_gradient_operators
65 .iter()
66 .map(|standard_gradient_operator| {
67 (&reference_nodal_coordinates * standard_gradient_operator).inverse_transpose()
68 * standard_gradient_operator
69 })
70 .collect();
71 let integration_weights = standard_gradient_operators
72 .iter()
73 .map(|standard_gradient_operator| {
74 (&reference_nodal_coordinates * standard_gradient_operator).determinant()
75 * Self::integration_weight()
76 })
77 .collect();
78 (gradient_vectors, integration_weights)
79 }
80 const fn integration_point(point: usize) -> [Scalar; M] {
81 match point {
82 0 => [-SQRT_3 / 3.0, -SQRT_3 / 3.0, -SQRT_3 / 3.0],
83 1 => [-SQRT_3 / 3.0, -SQRT_3 / 3.0, SQRT_3 / 3.0],
84 2 => [-SQRT_3 / 3.0, SQRT_3 / 3.0, -SQRT_3 / 3.0],
85 3 => [-SQRT_3 / 3.0, SQRT_3 / 3.0, SQRT_3 / 3.0],
86 4 => [SQRT_3 / 3.0, -SQRT_3 / 3.0, -SQRT_3 / 3.0],
87 5 => [SQRT_3 / 3.0, -SQRT_3 / 3.0, SQRT_3 / 3.0],
88 6 => [SQRT_3 / 3.0, SQRT_3 / 3.0, -SQRT_3 / 3.0],
89 7 => [SQRT_3 / 3.0, SQRT_3 / 3.0, SQRT_3 / 3.0],
90 _ => panic!(),
91 }
92 }
93 const fn integration_weight() -> Scalar {
94 1.0
95 }
96 #[cfg(test)]
97 const fn shape_functions([xi_1, xi_2, xi_3]: [Scalar; M]) -> ShapeFunctions<N> {
98 tensor_rank_1([
99 (1.0 - xi_1) * (1.0 - xi_2) * (1.0 - xi_3) / 8.0,
100 (1.0 + xi_1) * (1.0 - xi_2) * (1.0 - xi_3) / 8.0,
101 (1.0 + xi_1) * (1.0 + xi_2) * (1.0 - xi_3) / 8.0,
102 (1.0 - xi_1) * (1.0 + xi_2) * (1.0 - xi_3) / 8.0,
103 (1.0 - xi_1) * (1.0 - xi_2) * (1.0 + xi_3) / 8.0,
104 (1.0 + xi_1) * (1.0 - xi_2) * (1.0 + xi_3) / 8.0,
105 (1.0 + xi_1) * (1.0 + xi_2) * (1.0 + xi_3) / 8.0,
106 (1.0 - xi_1) * (1.0 + xi_2) * (1.0 + xi_3) / 8.0,
107 ])
108 }
109 #[cfg(test)]
110 const fn shape_functions_at_integration_points() -> ShapeFunctionsAtIntegrationPoints<G, Q> {
111 tensor_rank_1_list([
112 Self::shape_functions(Self::integration_point(0)),
113 Self::shape_functions(Self::integration_point(1)),
114 Self::shape_functions(Self::integration_point(2)),
115 Self::shape_functions(Self::integration_point(3)),
116 Self::shape_functions(Self::integration_point(4)),
117 Self::shape_functions(Self::integration_point(5)),
118 Self::shape_functions(Self::integration_point(6)),
119 Self::shape_functions(Self::integration_point(7)),
120 ])
121 }
122 const fn shape_functions_gradients(
123 [xi_1, xi_2, xi_3]: [Scalar; M],
124 ) -> ShapeFunctionsGradients<M, N> {
125 tensor_rank_1_list([
126 tensor_rank_1([
127 -(1.0 - xi_2) * (1.0 - xi_3) / 8.0,
128 -(1.0 - xi_1) * (1.0 - xi_3) / 8.0,
129 -(1.0 - xi_1) * (1.0 - xi_2) / 8.0,
130 ]),
131 tensor_rank_1([
132 (1.0 - xi_2) * (1.0 - xi_3) / 8.0,
133 -(1.0 + xi_1) * (1.0 - xi_3) / 8.0,
134 -(1.0 + xi_1) * (1.0 - xi_2) / 8.0,
135 ]),
136 tensor_rank_1([
137 (1.0 + xi_2) * (1.0 - xi_3) / 8.0,
138 (1.0 + xi_1) * (1.0 - xi_3) / 8.0,
139 -(1.0 + xi_1) * (1.0 + xi_2) / 8.0,
140 ]),
141 tensor_rank_1([
142 -(1.0 + xi_2) * (1.0 - xi_3) / 8.0,
143 (1.0 - xi_1) * (1.0 - xi_3) / 8.0,
144 -(1.0 - xi_1) * (1.0 + xi_2) / 8.0,
145 ]),
146 tensor_rank_1([
147 -(1.0 - xi_2) * (1.0 + xi_3) / 8.0,
148 -(1.0 - xi_1) * (1.0 + xi_3) / 8.0,
149 (1.0 - xi_1) * (1.0 - xi_2) / 8.0,
150 ]),
151 tensor_rank_1([
152 (1.0 - xi_2) * (1.0 + xi_3) / 8.0,
153 -(1.0 + xi_1) * (1.0 + xi_3) / 8.0,
154 (1.0 + xi_1) * (1.0 - xi_2) / 8.0,
155 ]),
156 tensor_rank_1([
157 (1.0 + xi_2) * (1.0 + xi_3) / 8.0,
158 (1.0 + xi_1) * (1.0 + xi_3) / 8.0,
159 (1.0 + xi_1) * (1.0 + xi_2) / 8.0,
160 ]),
161 tensor_rank_1([
162 -(1.0 + xi_2) * (1.0 + xi_3) / 8.0,
163 (1.0 - xi_1) * (1.0 + xi_3) / 8.0,
164 (1.0 - xi_1) * (1.0 + xi_2) / 8.0,
165 ]),
166 ])
167 }
168 const fn standard_gradient_operators() -> StandardGradientOperators<M, N, P> {
169 tensor_rank_1_list_2d([
170 Self::shape_functions_gradients(Self::integration_point(0)),
171 Self::shape_functions_gradients(Self::integration_point(1)),
172 Self::shape_functions_gradients(Self::integration_point(2)),
173 Self::shape_functions_gradients(Self::integration_point(3)),
174 Self::shape_functions_gradients(Self::integration_point(4)),
175 Self::shape_functions_gradients(Self::integration_point(5)),
176 Self::shape_functions_gradients(Self::integration_point(6)),
177 Self::shape_functions_gradients(Self::integration_point(7)),
178 ])
179 }
180}