conspire/domain/fem/block/element/solid/elastic/
mod.rs1use crate::{
2 constitutive::solid::elastic::Elastic,
3 fem::block::element::{
4 Element, ElementNodalCoordinates, FiniteElementError,
5 solid::{ElementNodalForcesSolid, ElementNodalStiffnessesSolid, SolidFiniteElement},
6 },
7 math::{ContractSecondFourthIndicesWithFirstIndicesOf, Tensor},
8 mechanics::{FirstPiolaKirchhoffStresses, FirstPiolaKirchhoffTangentStiffnesses},
9};
10use std::fmt::Debug;
11
12pub trait ElasticFiniteElement<C, const G: usize, const N: usize>
13where
14 C: Elastic,
15 Self: Debug + SolidFiniteElement<G, N>,
16{
17 fn nodal_forces(
18 &self,
19 constitutive_model: &C,
20 nodal_coordinates: &ElementNodalCoordinates<N>,
21 ) -> Result<ElementNodalForcesSolid<N>, FiniteElementError>;
22 fn nodal_stiffnesses(
23 &self,
24 constitutive_model: &C,
25 nodal_coordinates: &ElementNodalCoordinates<N>,
26 ) -> Result<ElementNodalStiffnessesSolid<N>, FiniteElementError>;
27}
28
29impl<C, const G: usize, const N: usize> ElasticFiniteElement<C, G, N> for Element<G, N>
30where
31 C: Elastic,
32{
33 fn nodal_forces(
34 &self,
35 constitutive_model: &C,
36 nodal_coordinates: &ElementNodalCoordinates<N>,
37 ) -> Result<ElementNodalForcesSolid<N>, FiniteElementError> {
38 match self
39 .deformation_gradients(nodal_coordinates)
40 .iter()
41 .map(|deformation_gradient| {
42 constitutive_model.first_piola_kirchhoff_stress(deformation_gradient)
43 })
44 .collect::<Result<FirstPiolaKirchhoffStresses<G>, _>>()
45 {
46 Ok(first_piola_kirchhoff_stresses) => Ok(first_piola_kirchhoff_stresses
47 .iter()
48 .zip(
49 self.gradient_vectors()
50 .iter()
51 .zip(self.integration_weights().iter()),
52 )
53 .map(
54 |(first_piola_kirchhoff_stress, (gradient_vectors, integration_weight))| {
55 gradient_vectors
56 .iter()
57 .map(|gradient_vector| {
58 (first_piola_kirchhoff_stress * gradient_vector)
59 * integration_weight
60 })
61 .collect()
62 },
63 )
64 .sum()),
65 Err(error) => Err(FiniteElementError::Upstream(
66 format!("{error}"),
67 format!("{self:?}"),
68 )),
69 }
70 }
71 fn nodal_stiffnesses(
72 &self,
73 constitutive_model: &C,
74 nodal_coordinates: &ElementNodalCoordinates<N>,
75 ) -> Result<ElementNodalStiffnessesSolid<N>, FiniteElementError> {
76 match self
77 .deformation_gradients(nodal_coordinates)
78 .iter()
79 .map(|deformation_gradient| {
80 constitutive_model.first_piola_kirchhoff_tangent_stiffness(deformation_gradient)
81 })
82 .collect::<Result<FirstPiolaKirchhoffTangentStiffnesses<G>, _>>()
83 {
84 Ok(first_piola_kirchhoff_tangent_stiffnesses) => {
85 Ok(first_piola_kirchhoff_tangent_stiffnesses
86 .iter()
87 .zip(
88 self.gradient_vectors()
89 .iter()
90 .zip(self.integration_weights().iter()),
91 )
92 .map(
93 |(
94 first_piola_kirchhoff_tangent_stiffness,
95 (gradient_vectors, integration_weight),
96 )| {
97 gradient_vectors
98 .iter()
99 .map(|gradient_vector_a| {
100 gradient_vectors
101 .iter()
102 .map(|gradient_vector_b| {
103 first_piola_kirchhoff_tangent_stiffness
104 .contract_second_fourth_indices_with_first_indices_of(
105 gradient_vector_a,
106 gradient_vector_b,
107 )
108 * integration_weight
109 })
110 .collect()
111 })
112 .collect()
113 },
114 )
115 .sum())
116 }
117 Err(error) => Err(FiniteElementError::Upstream(
118 format!("{error}"),
119 format!("{self:?}"),
120 )),
121 }
122 }
123}