conspire/domain/fem/block/element/surface/
mod.rs1pub mod linear;
2
3use crate::{
4 fem::block::element::{
5 ElementNodalCoordinates, ElementNodalReferenceCoordinates, ElementNodalVelocities,
6 GradientVectors, StandardGradientOperators,
7 },
8 math::{IDENTITY, LEVI_CIVITA, Scalar, Scalars, Tensor, TensorArray, TensorRank2},
9 mechanics::{
10 Coordinates, Normal, NormalGradients, NormalRates, Normals, ReferenceNormals, SurfaceBases,
11 },
12};
13use std::fmt::{self, Debug, Formatter};
14
15pub struct SurfaceElement<const G: usize, const N: usize, const P: usize> {
16 gradient_vectors: GradientVectors<G, N>,
17 integration_weights: Scalars<G>,
18 reference_normals: ReferenceNormals<P>,
19}
20
21impl<const G: usize, const N: usize, const P: usize> Debug for SurfaceElement<G, N, P> {
22 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
23 let element = match (G, N, P) {
24 (1, 3, 1) => "LinearTriangle",
25 _ => panic!(),
26 };
27 write!(f, "{element} {{ G: {G}, N: {N} , P: {P} }}",)
28 }
29}
30
31impl<const G: usize, const N: usize, const P: usize> SurfaceElement<G, N, P> {
32 fn gradient_vectors(&self) -> &GradientVectors<G, N> {
33 &self.gradient_vectors
34 }
35 fn integration_weights(&self) -> &Scalars<G> {
36 &self.integration_weights
37 }
38 fn reference_normals(&self) -> &ReferenceNormals<P> {
39 &self.reference_normals
40 }
41}
42
43pub trait SurfaceFiniteElement<const G: usize, const N: usize, const P: usize> {
44 fn new(
45 reference_nodal_coordinates: ElementNodalReferenceCoordinates<N>,
46 thickness: Scalar,
47 ) -> Self;
48}
49
50pub trait SurfaceFiniteElementMethods<
51 const G: usize,
52 const M: usize,
53 const N: usize,
54 const P: usize,
55> where
56 Self: SurfaceFiniteElementMethodsExtra<M, N, P>,
57{
58 fn bases<const I: usize>(nodal_coordinates: &Coordinates<I, N>) -> SurfaceBases<I, P>;
59 fn dual_bases<const I: usize>(nodal_coordinates: &Coordinates<I, N>) -> SurfaceBases<I, P>;
60 fn normals(nodal_coordinates: &ElementNodalCoordinates<N>) -> Normals<P>;
61 fn normal_gradients(nodal_coordinates: &ElementNodalCoordinates<N>) -> NormalGradients<N, P>;
62 fn normal_rates(
63 nodal_coordinates: &ElementNodalCoordinates<N>,
64 nodal_velocities: &ElementNodalVelocities<N>,
65 ) -> NormalRates<P>;
66}
67
68pub trait SurfaceFiniteElementMethodsExtra<const M: usize, const N: usize, const P: usize> {
70 fn standard_gradient_operators() -> StandardGradientOperators<M, N, P>;
71}
72
73impl<const G: usize, const M: usize, const N: usize, const P: usize>
74 SurfaceFiniteElementMethods<G, M, N, P> for SurfaceElement<G, N, P>
75where
76 Self: SurfaceFiniteElementMethodsExtra<M, N, P>,
77{
78 fn bases<const I: usize>(nodal_coordinates: &Coordinates<I, N>) -> SurfaceBases<I, P> {
79 Self::standard_gradient_operators()
80 .iter()
81 .map(|standard_gradient_operator| {
82 standard_gradient_operator
83 .iter()
84 .zip(nodal_coordinates.iter())
85 .map(|(standard_gradient_operator_a, nodal_coordinate_a)| {
86 standard_gradient_operator_a
87 .iter()
88 .map(|standard_gradient_operator_a_m| {
89 nodal_coordinate_a * standard_gradient_operator_a_m
90 })
91 .collect()
92 })
93 .sum()
94 })
95 .collect()
96 }
97 fn dual_bases<const I: usize>(nodal_coordinates: &Coordinates<I, N>) -> SurfaceBases<I, P> {
98 Self::bases(nodal_coordinates)
99 .iter()
100 .map(|basis_vectors| {
101 basis_vectors
102 .iter()
103 .map(|basis_vectors_m| {
104 basis_vectors
105 .iter()
106 .map(|basis_vectors_n| basis_vectors_m * basis_vectors_n)
107 .collect()
108 })
109 .collect::<TensorRank2<2, I, I>>()
110 .inverse()
111 .iter()
112 .map(|metric_tensor_m| {
113 metric_tensor_m
114 .iter()
115 .zip(basis_vectors.iter())
116 .map(|(metric_tensor_mn, basis_vectors_n)| {
117 basis_vectors_n * metric_tensor_mn
118 })
119 .sum()
120 })
121 .collect()
122 })
123 .collect()
124 }
125 fn normals(nodal_coordinates: &ElementNodalCoordinates<N>) -> Normals<P> {
126 Self::bases(nodal_coordinates)
127 .iter()
128 .map(|basis_vectors| basis_vectors[0].cross(&basis_vectors[1]).normalized())
129 .collect()
130 }
131 fn normal_gradients(nodal_coordinates: &ElementNodalCoordinates<N>) -> NormalGradients<N, P> {
132 let levi_civita_symbol = LEVI_CIVITA;
133 let mut normalization: Scalar = 0.0;
134 let mut normal_vector = Normal::zero();
135 Self::standard_gradient_operators().iter()
136 .zip(Self::bases(nodal_coordinates).iter())
137 .map(|(standard_gradient_operator, basis_vectors)|{
138 normalization = basis_vectors[0].cross(&basis_vectors[1]).norm();
139 normal_vector = basis_vectors[0].cross(&basis_vectors[1])/normalization;
140 standard_gradient_operator.iter()
141 .map(|standard_gradient_operator_a|
142 levi_civita_symbol.iter()
143 .map(|levi_civita_symbol_m|
144 IDENTITY.iter()
145 .zip(normal_vector.iter())
146 .map(|(identity_i, normal_vector_i)|
147 levi_civita_symbol_m.iter()
148 .zip(basis_vectors[0].iter()
149 .zip(basis_vectors[1].iter()))
150 .map(|(levi_civita_symbol_mn, (basis_vector_0_n, basis_vector_1_n))|
151 levi_civita_symbol_mn.iter()
152 .zip(identity_i.iter()
153 .zip(normal_vector.iter()))
154 .map(|(levi_civita_symbol_mno, (identity_io, normal_vector_o))|
155 levi_civita_symbol_mno * (identity_io - normal_vector_i * normal_vector_o)
156 ).sum::<Scalar>() * (
157 standard_gradient_operator_a[0] * basis_vector_1_n
158 - standard_gradient_operator_a[1] * basis_vector_0_n
159 )
160 ).sum::<Scalar>() / normalization
161 ).collect()
162 ).collect()
163 ).collect()
164 }).collect()
165 }
166 fn normal_rates(
167 nodal_coordinates: &ElementNodalCoordinates<N>,
168 nodal_velocities: &ElementNodalVelocities<N>,
169 ) -> NormalRates<P> {
170 let identity = IDENTITY;
171 let levi_civita_symbol = LEVI_CIVITA;
172 let mut normalization = 0.0;
173 Self::bases(nodal_coordinates)
174 .iter()
175 .zip(Self::normals(nodal_coordinates).iter()
176 .zip(Self::standard_gradient_operators().iter()))
177 .map(|(basis, (normal, standard_gradient_operator))| {
178 normalization = basis[0].cross(&basis[1]).norm();
179 identity.iter()
180 .zip(normal.iter())
181 .map(|(identity_i, normal_vector_i)|
182 nodal_velocities.iter()
183 .zip(standard_gradient_operator.iter())
184 .map(|(nodal_velocity_a, standard_gradient_operator_a)|
185 levi_civita_symbol.iter()
186 .zip(nodal_velocity_a.iter())
187 .map(|(levi_civita_symbol_m, nodal_velocity_a_m)|
188 levi_civita_symbol_m.iter()
189 .zip(basis[0].iter()
190 .zip(basis[1].iter()))
191 .map(|(levi_civita_symbol_mn, (basis_vector_0_n, basis_vector_1_n))|
192 levi_civita_symbol_mn.iter()
193 .zip(identity_i.iter()
194 .zip(normal.iter()))
195 .map(|(levi_civita_symbol_mno, (identity_io, normal_vector_o))|
196 levi_civita_symbol_mno * (identity_io - normal_vector_i * normal_vector_o)
197 ).sum::<Scalar>() * (
198 standard_gradient_operator_a[0] * basis_vector_1_n
199 - standard_gradient_operator_a[1] * basis_vector_0_n
200 )
201 ).sum::<Scalar>() * nodal_velocity_a_m
202 ).sum::<Scalar>()
203 ).sum::<Scalar>() / normalization
204 ).collect()
205 }).collect()
206 }
207}