1#[cfg(test)]
2mod test;
3
4pub mod composite;
5pub mod linear;
6
7use super::*;
8use crate::{
9 constitutive::{Constitutive, Parameters},
10 math::{IDENTITY, LEVI_CIVITA, tensor_rank_1_zero},
11 mechanics::Scalar,
12};
13
14pub struct Element<C, const G: usize, const N: usize> {
15 constitutive_models: [C; G],
16 gradient_vectors: GradientVectors<G, N>,
17 integration_weights: Scalars<G>,
18}
19
20pub struct SurfaceElement<C, const G: usize, const N: usize, const P: usize> {
21 constitutive_models: [C; G],
22 gradient_vectors: GradientVectors<G, N>,
23 integration_weights: Scalars<G>,
24 reference_normals: ReferenceNormals<P>,
25}
26
27pub trait FiniteElement<C, const G: usize, const N: usize, Y>
28where
29 C: Constitutive<Y>,
30 Self: FiniteElementMethods<C, G, N>,
31 Y: Parameters,
32{
33 fn new(
34 constitutive_model_parameters: Y,
35 reference_nodal_coordinates: ReferenceNodalCoordinates<N>,
36 ) -> Self;
37 fn reference() -> ReferenceNodalCoordinates<N>;
38 fn reset(&mut self);
39}
40
41pub trait SurfaceFiniteElement<C, const G: usize, const N: usize, const P: usize, Y>
42where
43 C: Constitutive<Y>,
44 Self: FiniteElementMethods<C, G, N>,
45 Y: Parameters,
46{
47 fn new(
48 constitutive_model_parameters: Y,
49 reference_nodal_coordinates: ReferenceNodalCoordinates<N>,
50 thickness: &Scalar,
51 ) -> Self;
52}
53
54pub trait FiniteElementMethods<C, const G: usize, const N: usize> {
55 fn constitutive_models(&self) -> &[C; G];
56 fn deformation_gradients(
57 &self,
58 nodal_coordinates: &NodalCoordinates<N>,
59 ) -> DeformationGradientList<G>;
60 fn deformation_gradient_rates(
61 &self,
62 nodal_coordinates: &NodalCoordinates<N>,
63 nodal_velocities: &NodalVelocities<N>,
64 ) -> DeformationGradientRateList<G>;
65 fn gradient_vectors(&self) -> &GradientVectors<G, N>;
66 fn integration_weights(&self) -> &Scalars<G>;
67}
68
69pub trait SurfaceFiniteElementMethods<
70 const G: usize,
71 const M: usize,
72 const N: usize,
73 const P: usize,
74> where
75 Self: SurfaceFiniteElementMethodsExtra<M, N, P>,
76{
77 fn bases<const I: usize>(nodal_coordinates: &Coordinates<I, N>) -> Bases<I, P>;
78 fn dual_bases<const I: usize>(nodal_coordinates: &Coordinates<I, N>) -> Bases<I, P>;
79 fn normals(nodal_coordinates: &NodalCoordinates<N>) -> Normals<P>;
80 fn normal_gradients(nodal_coordinates: &NodalCoordinates<N>) -> NormalGradients<N, P>;
81 fn normal_rates(
82 nodal_coordinates: &NodalCoordinates<N>,
83 nodal_velocities: &NodalVelocities<N>,
84 ) -> NormalRates<P>;
85 fn reference_normals(&self) -> &ReferenceNormals<P>;
86}
87
88pub trait SurfaceFiniteElementMethodsExtra<const M: usize, const N: usize, const P: usize> {
90 fn standard_gradient_operators() -> StandardGradientOperators<M, N, P>;
91}
92
93impl<C, const G: usize, const N: usize> FiniteElementMethods<C, G, N> for Element<C, G, N> {
94 fn constitutive_models(&self) -> &[C; G] {
95 &self.constitutive_models
96 }
97 fn deformation_gradients(
98 &self,
99 nodal_coordinates: &NodalCoordinates<N>,
100 ) -> DeformationGradientList<G> {
101 self.gradient_vectors()
102 .iter()
103 .map(|gradient_vectors| {
104 nodal_coordinates
105 .iter()
106 .zip(gradient_vectors.iter())
107 .map(|(nodal_coordinate, gradient_vector)| {
108 DeformationGradient::dyad(nodal_coordinate, gradient_vector)
109 })
110 .sum()
111 })
112 .collect()
113 }
114 fn deformation_gradient_rates(
115 &self,
116 _: &NodalCoordinates<N>,
117 nodal_velocities: &NodalVelocities<N>,
118 ) -> DeformationGradientRateList<G> {
119 self.gradient_vectors()
120 .iter()
121 .map(|gradient_vectors| {
122 nodal_velocities
123 .iter()
124 .zip(gradient_vectors.iter())
125 .map(|(nodal_velocity, gradient_vector)| {
126 DeformationGradientRate::dyad(nodal_velocity, gradient_vector)
127 })
128 .sum()
129 })
130 .collect()
131 }
132 fn gradient_vectors(&self) -> &GradientVectors<G, N> {
133 &self.gradient_vectors
134 }
135 fn integration_weights(&self) -> &Scalars<G> {
136 &self.integration_weights
137 }
138}
139
140impl<C, const G: usize, const M: usize, const N: usize, const P: usize>
141 SurfaceFiniteElementMethods<G, M, N, P> for SurfaceElement<C, G, N, P>
142where
143 Self: SurfaceFiniteElementMethodsExtra<M, N, P>,
144{
145 fn bases<const I: usize>(nodal_coordinates: &Coordinates<I, N>) -> Bases<I, P> {
146 Self::standard_gradient_operators()
147 .iter()
148 .map(|standard_gradient_operator| {
149 standard_gradient_operator
150 .iter()
151 .zip(nodal_coordinates.iter())
152 .map(|(standard_gradient_operator_a, nodal_coordinate_a)| {
153 standard_gradient_operator_a
154 .iter()
155 .map(|standard_gradient_operator_a_m| {
156 nodal_coordinate_a * standard_gradient_operator_a_m
157 })
158 .collect()
159 })
160 .sum()
161 })
162 .collect()
163 }
164 fn dual_bases<const I: usize>(nodal_coordinates: &Coordinates<I, N>) -> Bases<I, P> {
165 Self::bases(nodal_coordinates)
166 .iter()
167 .map(|basis_vectors| {
168 basis_vectors
169 .iter()
170 .map(|basis_vectors_m| {
171 basis_vectors
172 .iter()
173 .map(|basis_vectors_n| basis_vectors_m * basis_vectors_n)
174 .collect()
175 })
176 .collect::<TensorRank2<2, I, I>>()
177 .inverse()
178 .iter()
179 .map(|metric_tensor_m| {
180 metric_tensor_m
181 .iter()
182 .zip(basis_vectors.iter())
183 .map(|(metric_tensor_mn, basis_vectors_n)| {
184 basis_vectors_n * metric_tensor_mn
185 })
186 .sum()
187 })
188 .collect()
189 })
190 .collect()
191 }
192 fn normals(nodal_coordinates: &NodalCoordinates<N>) -> Normals<P> {
193 Self::bases(nodal_coordinates)
194 .iter()
195 .map(|basis_vectors| basis_vectors[0].cross(&basis_vectors[1]).normalized())
196 .collect()
197 }
198 fn normal_gradients(nodal_coordinates: &NodalCoordinates<N>) -> NormalGradients<N, P> {
199 let levi_civita_symbol = LEVI_CIVITA;
200 let mut normalization: Scalar = 0.0;
201 let mut normal_vector = tensor_rank_1_zero();
202 Self::standard_gradient_operators().iter()
203 .zip(Self::bases(nodal_coordinates).iter())
204 .map(|(standard_gradient_operator, basis_vectors)|{
205 normalization = basis_vectors[0].cross(&basis_vectors[1]).norm();
206 normal_vector = basis_vectors[0].cross(&basis_vectors[1])/normalization;
207 standard_gradient_operator.iter()
208 .map(|standard_gradient_operator_a|
209 levi_civita_symbol.iter()
210 .map(|levi_civita_symbol_m|
211 IDENTITY.iter()
212 .zip(normal_vector.iter())
213 .map(|(identity_i, normal_vector_i)|
214 levi_civita_symbol_m.iter()
215 .zip(basis_vectors[0].iter()
216 .zip(basis_vectors[1].iter()))
217 .map(|(levi_civita_symbol_mn, (basis_vector_0_n, basis_vector_1_n))|
218 levi_civita_symbol_mn.iter()
219 .zip(identity_i.iter()
220 .zip(normal_vector.iter()))
221 .map(|(levi_civita_symbol_mno, (identity_io, normal_vector_o))|
222 levi_civita_symbol_mno * (identity_io - normal_vector_i * normal_vector_o)
223 ).sum::<Scalar>() * (
224 standard_gradient_operator_a[0] * basis_vector_1_n
225 - standard_gradient_operator_a[1] * basis_vector_0_n
226 )
227 ).sum::<Scalar>() / normalization
228 ).collect()
229 ).collect()
230 ).collect()
231 }).collect()
232 }
233 fn normal_rates(
234 nodal_coordinates: &NodalCoordinates<N>,
235 nodal_velocities: &NodalVelocities<N>,
236 ) -> NormalRates<P> {
237 let identity = IDENTITY;
238 let levi_civita_symbol = LEVI_CIVITA;
239 let mut normalization = 0.0;
240 Self::bases(nodal_coordinates)
241 .iter()
242 .zip(Self::normals(nodal_coordinates).iter()
243 .zip(Self::standard_gradient_operators().iter()))
244 .map(|(basis, (normal, standard_gradient_operator))| {
245 normalization = basis[0].cross(&basis[1]).norm();
246 identity.iter()
247 .zip(normal.iter())
248 .map(|(identity_i, normal_vector_i)|
249 nodal_velocities.iter()
250 .zip(standard_gradient_operator.iter())
251 .map(|(nodal_velocity_a, standard_gradient_operator_a)|
252 levi_civita_symbol.iter()
253 .zip(nodal_velocity_a.iter())
254 .map(|(levi_civita_symbol_m, nodal_velocity_a_m)|
255 levi_civita_symbol_m.iter()
256 .zip(basis[0].iter()
257 .zip(basis[1].iter()))
258 .map(|(levi_civita_symbol_mn, (basis_vector_0_n, basis_vector_1_n))|
259 levi_civita_symbol_mn.iter()
260 .zip(identity_i.iter()
261 .zip(normal.iter()))
262 .map(|(levi_civita_symbol_mno, (identity_io, normal_vector_o))|
263 levi_civita_symbol_mno * (identity_io - normal_vector_i * normal_vector_o)
264 ).sum::<Scalar>() * (
265 standard_gradient_operator_a[0] * basis_vector_1_n
266 - standard_gradient_operator_a[1] * basis_vector_0_n
267 )
268 ).sum::<Scalar>() * nodal_velocity_a_m
269 ).sum::<Scalar>()
270 ).sum::<Scalar>() / normalization
271 ).collect()
272 }).collect()
273 }
274 fn reference_normals(&self) -> &ReferenceNormals<P> {
275 &self.reference_normals
276 }
277}
278
279pub trait ElasticFiniteElement<C, const G: usize, const N: usize>
280where
281 C: Elastic,
282 Self: FiniteElementMethods<C, G, N>,
283{
284 fn nodal_forces(
285 &self,
286 nodal_coordinates: &NodalCoordinates<N>,
287 ) -> Result<NodalForces<N>, ConstitutiveError>;
288 fn nodal_stiffnesses(
289 &self,
290 nodal_coordinates: &NodalCoordinates<N>,
291 ) -> Result<NodalStiffnesses<N>, ConstitutiveError>;
292}
293
294pub trait HyperelasticFiniteElement<C, const G: usize, const N: usize>
295where
296 C: Hyperelastic,
297 Self: ElasticFiniteElement<C, G, N>,
298{
299 fn helmholtz_free_energy(
300 &self,
301 nodal_coordinates: &NodalCoordinates<N>,
302 ) -> Result<Scalar, ConstitutiveError>;
303}
304
305pub trait ViscoelasticFiniteElement<C, const G: usize, const N: usize>
306where
307 C: Viscoelastic,
308 Self: FiniteElementMethods<C, G, N>,
309{
310 fn nodal_forces(
311 &self,
312 nodal_coordinates: &NodalCoordinates<N>,
313 nodal_velocities: &NodalVelocities<N>,
314 ) -> Result<NodalForces<N>, ConstitutiveError>;
315 fn nodal_stiffnesses(
316 &self,
317 nodal_coordinates: &NodalCoordinates<N>,
318 nodal_velocities: &NodalVelocities<N>,
319 ) -> Result<NodalStiffnesses<N>, ConstitutiveError>;
320}
321
322pub trait ElasticHyperviscousFiniteElement<C, const G: usize, const N: usize>
323where
324 C: ElasticHyperviscous,
325 Self: ViscoelasticFiniteElement<C, G, N>,
326{
327 fn viscous_dissipation(
328 &self,
329 nodal_coordinates: &NodalCoordinates<N>,
330 nodal_velocities: &NodalVelocities<N>,
331 ) -> Result<Scalar, ConstitutiveError>;
332 fn dissipation_potential(
333 &self,
334 nodal_coordinates: &NodalCoordinates<N>,
335 nodal_velocities: &NodalVelocities<N>,
336 ) -> Result<Scalar, ConstitutiveError>;
337}
338
339pub trait HyperviscoelasticFiniteElement<C, const G: usize, const N: usize>
340where
341 C: Hyperviscoelastic,
342 Self: ElasticHyperviscousFiniteElement<C, G, N>,
343{
344 fn helmholtz_free_energy(
345 &self,
346 nodal_coordinates: &NodalCoordinates<N>,
347 ) -> Result<Scalar, ConstitutiveError>;
348}
349
350impl<C, const G: usize, const N: usize> ElasticFiniteElement<C, G, N> for Element<C, G, N>
351where
352 C: Elastic,
353{
354 fn nodal_forces(
355 &self,
356 nodal_coordinates: &NodalCoordinates<N>,
357 ) -> Result<NodalForces<N>, ConstitutiveError> {
358 Ok(self
359 .constitutive_models()
360 .iter()
361 .zip(self.deformation_gradients(nodal_coordinates).iter())
362 .map(|(constitutive_model, deformation_gradient)| {
363 constitutive_model.first_piola_kirchhoff_stress(deformation_gradient)
364 })
365 .collect::<Result<FirstPiolaKirchhoffStresses<G>, _>>()?
366 .iter()
367 .zip(
368 self.gradient_vectors()
369 .iter()
370 .zip(self.integration_weights().iter()),
371 )
372 .map(
373 |(first_piola_kirchhoff_stress, (gradient_vectors, integration_weight))| {
374 gradient_vectors
375 .iter()
376 .map(|gradient_vector| {
377 (first_piola_kirchhoff_stress * gradient_vector) * integration_weight
378 })
379 .collect()
380 },
381 )
382 .sum())
383 }
384 fn nodal_stiffnesses(
385 &self,
386 nodal_coordinates: &NodalCoordinates<N>,
387 ) -> Result<NodalStiffnesses<N>, ConstitutiveError> {
388 Ok(self
389 .constitutive_models()
390 .iter()
391 .zip(self.deformation_gradients(nodal_coordinates).iter())
392 .map(|(constitutive_model, deformation_gradient)| {
393 constitutive_model.first_piola_kirchhoff_tangent_stiffness(deformation_gradient)
394 })
395 .collect::<Result<FirstPiolaKirchhoffTangentStiffnesses<G>, _>>()?
396 .iter()
397 .zip(
398 self.gradient_vectors()
399 .iter()
400 .zip(self.integration_weights().iter()),
401 )
402 .map(
403 |(
404 first_piola_kirchhoff_tangent_stiffness,
405 (gradient_vectors, integration_weight),
406 )| {
407 gradient_vectors
408 .iter()
409 .map(|gradient_vector_a| {
410 gradient_vectors
411 .iter()
412 .map(|gradient_vector_b| {
413 first_piola_kirchhoff_tangent_stiffness
414 .contract_second_fourth_indices_with_first_indices_of(
415 gradient_vector_a,
416 gradient_vector_b,
417 )
418 * integration_weight
419 })
420 .collect()
421 })
422 .collect()
423 },
424 )
425 .sum())
426 }
427}
428
429impl<C, const G: usize, const N: usize> HyperelasticFiniteElement<C, G, N> for Element<C, G, N>
430where
431 C: Hyperelastic,
432{
433 fn helmholtz_free_energy(
434 &self,
435 nodal_coordinates: &NodalCoordinates<N>,
436 ) -> Result<Scalar, ConstitutiveError> {
437 self.constitutive_models()
438 .iter()
439 .zip(
440 self.deformation_gradients(nodal_coordinates)
441 .iter()
442 .zip(self.integration_weights().iter()),
443 )
444 .map(
445 |(constitutive_model, (deformation_gradient, integration_weight))| {
446 Ok(
447 constitutive_model.helmholtz_free_energy_density(deformation_gradient)?
448 * integration_weight,
449 )
450 },
451 )
452 .sum()
453 }
454}
455
456impl<C, const G: usize, const N: usize> ViscoelasticFiniteElement<C, G, N> for Element<C, G, N>
457where
458 C: Viscoelastic,
459{
460 fn nodal_forces(
461 &self,
462 nodal_coordinates: &NodalCoordinates<N>,
463 nodal_velocities: &NodalVelocities<N>,
464 ) -> Result<NodalForces<N>, ConstitutiveError> {
465 Ok(self
466 .constitutive_models()
467 .iter()
468 .zip(
469 self.deformation_gradients(nodal_coordinates).iter().zip(
470 self.deformation_gradient_rates(nodal_coordinates, nodal_velocities)
471 .iter(),
472 ),
473 )
474 .map(
475 |(constitutive_model, (deformation_gradient, deformation_gradient_rate))| {
476 constitutive_model.first_piola_kirchhoff_stress(
477 deformation_gradient,
478 deformation_gradient_rate,
479 )
480 },
481 )
482 .collect::<Result<FirstPiolaKirchhoffStresses<G>, _>>()?
483 .iter()
484 .zip(
485 self.gradient_vectors()
486 .iter()
487 .zip(self.integration_weights().iter()),
488 )
489 .map(
490 |(first_piola_kirchhoff_stress, (gradient_vectors, integration_weight))| {
491 gradient_vectors
492 .iter()
493 .map(|gradient_vector| {
494 (first_piola_kirchhoff_stress * gradient_vector) * integration_weight
495 })
496 .collect()
497 },
498 )
499 .sum())
500 }
501 fn nodal_stiffnesses(
502 &self,
503 nodal_coordinates: &NodalCoordinates<N>,
504 nodal_velocities: &NodalVelocities<N>,
505 ) -> Result<NodalStiffnesses<N>, ConstitutiveError> {
506 Ok(self
507 .constitutive_models()
508 .iter()
509 .zip(
510 self.deformation_gradients(nodal_coordinates).iter().zip(
511 self.deformation_gradient_rates(nodal_coordinates, nodal_velocities)
512 .iter(),
513 ),
514 )
515 .map(
516 |(constitutive_model, (deformation_gradient, deformation_gradient_rate))| {
517 constitutive_model.first_piola_kirchhoff_rate_tangent_stiffness(
518 deformation_gradient,
519 deformation_gradient_rate,
520 )
521 },
522 )
523 .collect::<Result<FirstPiolaKirchhoffRateTangentStiffnesses<G>, _>>()?
524 .iter()
525 .zip(
526 self.gradient_vectors().iter().zip(
527 self.gradient_vectors()
528 .iter()
529 .zip(self.integration_weights().iter()),
530 ),
531 )
532 .map(
533 |(
534 first_piola_kirchhoff_rate_tangent_stiffness,
535 (gradient_vectors_a, (gradient_vectors_b, integration_weight)),
536 )| {
537 gradient_vectors_a
538 .iter()
539 .map(|gradient_vector_a| {
540 gradient_vectors_b
541 .iter()
542 .map(|gradient_vector_b| {
543 first_piola_kirchhoff_rate_tangent_stiffness
544 .contract_second_fourth_indices_with_first_indices_of(
545 gradient_vector_a,
546 gradient_vector_b,
547 )
548 * integration_weight
549 })
550 .collect()
551 })
552 .collect()
553 },
554 )
555 .sum())
556 }
557}
558
559impl<C, const G: usize, const N: usize> ElasticHyperviscousFiniteElement<C, G, N>
560 for Element<C, G, N>
561where
562 C: ElasticHyperviscous,
563{
564 fn viscous_dissipation(
565 &self,
566 nodal_coordinates: &NodalCoordinates<N>,
567 nodal_velocities: &NodalVelocities<N>,
568 ) -> Result<Scalar, ConstitutiveError> {
569 self.constitutive_models()
570 .iter()
571 .zip(
572 self.deformation_gradients(nodal_coordinates).iter().zip(
573 self.deformation_gradient_rates(nodal_coordinates, nodal_velocities)
574 .iter()
575 .zip(self.integration_weights().iter()),
576 ),
577 )
578 .map(
579 |(
580 constitutive_model,
581 (deformation_gradient, (deformation_gradient_rate, integration_weight)),
582 )| {
583 Ok(constitutive_model
584 .viscous_dissipation(deformation_gradient, deformation_gradient_rate)?
585 * integration_weight)
586 },
587 )
588 .sum()
589 }
590 fn dissipation_potential(
591 &self,
592 nodal_coordinates: &NodalCoordinates<N>,
593 nodal_velocities: &NodalVelocities<N>,
594 ) -> Result<Scalar, ConstitutiveError> {
595 self.constitutive_models()
596 .iter()
597 .zip(
598 self.deformation_gradients(nodal_coordinates).iter().zip(
599 self.deformation_gradient_rates(nodal_coordinates, nodal_velocities)
600 .iter()
601 .zip(self.integration_weights().iter()),
602 ),
603 )
604 .map(
605 |(
606 constitutive_model,
607 (deformation_gradient, (deformation_gradient_rate, integration_weight)),
608 )| {
609 Ok(constitutive_model
610 .dissipation_potential(deformation_gradient, deformation_gradient_rate)?
611 * integration_weight)
612 },
613 )
614 .sum()
615 }
616}
617
618impl<C, const G: usize, const N: usize> HyperviscoelasticFiniteElement<C, G, N> for Element<C, G, N>
619where
620 C: Hyperviscoelastic,
621{
622 fn helmholtz_free_energy(
623 &self,
624 nodal_coordinates: &NodalCoordinates<N>,
625 ) -> Result<Scalar, ConstitutiveError> {
626 self.constitutive_models()
627 .iter()
628 .zip(
629 self.deformation_gradients(nodal_coordinates)
630 .iter()
631 .zip(self.integration_weights().iter()),
632 )
633 .map(
634 |(constitutive_model, (deformation_gradient, integration_weight))| {
635 Ok(
636 constitutive_model.helmholtz_free_energy_density(deformation_gradient)?
637 * integration_weight,
638 )
639 },
640 )
641 .sum()
642 }
643}