conspire/domain/fem/block/element/solid/elastic/
mod.rs

1use 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}