conspire/constitutive/solid/hyperelastic/internal_variables/
mod.rs

1//! Hyperelastic solid constitutive models with internal variables.
2
3use crate::{
4    constitutive::{
5        ConstitutiveError,
6        solid::elastic::{
7            AppliedLoad,
8            internal_variables::{ElasticIV, bcs},
9        },
10    },
11    math::{
12        Scalar, Tensor, TensorArray, TensorTuple,
13        optimize::{EqualityConstraint, FirstOrderOptimization, SecondOrderOptimization},
14    },
15    mechanics::{DeformationGradient, FirstPiolaKirchhoffTangentStiffness},
16};
17
18/// Required methods for hyperelastic solid constitutive models with internal variables.
19pub trait HyperelasticIV<V, T1, T2, T3>
20where
21    Self: ElasticIV<V, T1, T2, T3>,
22{
23    /// Calculates and returns the Helmholtz free energy density.
24    ///
25    /// ```math
26    /// a = a(\mathbf{F})
27    /// ```
28    fn helmholtz_free_energy_density(
29        &self,
30        deformation_gradient: &DeformationGradient,
31        internal_variables: &V,
32    ) -> Result<Scalar, ConstitutiveError>;
33}
34
35/// First-order minimization methods for hyperelastic solid constitutive models with internal variables.
36pub trait FirstOrderMinimize<V, T1, T2, T3> {
37    /// Type representing all variables.
38    type Variables;
39    /// Solve for the unknown components of the deformation gradient under an applied load.
40    ///
41    /// ```math
42    /// \Pi(\mathbf{F},\boldsymbol{\lambda}) = a(\mathbf{F}) - \boldsymbol{\lambda}:(\mathbf{F} - \mathbf{F}_0) - \mathbf{P}_0:\mathbf{F}
43    /// ```
44    fn minimize(
45        &self,
46        applied_load: AppliedLoad,
47        solver: impl FirstOrderOptimization<Scalar, Self::Variables>,
48    ) -> Result<(DeformationGradient, V), ConstitutiveError>;
49}
50
51/// Second-order minimization methods for hyperelastic solid constitutive models with internal variables.
52pub trait SecondOrderMinimize<V, T1, T2, T3>
53where
54    T1: Tensor,
55    T2: Tensor,
56    T3: Tensor,
57    V: Tensor,
58{
59    /// Type representing all variables.
60    type Variables;
61    /// Solve for the unknown components of the deformation gradient under an applied load.
62    ///
63    /// ```math
64    /// \Pi(\mathbf{F},\boldsymbol{\lambda}) = a(\mathbf{F}) - \boldsymbol{\lambda}:(\mathbf{F} - \mathbf{F}_0) - \mathbf{P}_0:\mathbf{F}
65    /// ```
66    fn minimize(
67        &self,
68        applied_load: AppliedLoad,
69        solver: impl SecondOrderOptimization<
70            Scalar,
71            Self::Variables,
72            TensorTuple<FirstPiolaKirchhoffTangentStiffness, TensorTuple<T1, TensorTuple<T2, T3>>>,
73            Self::Variables,
74        >,
75    ) -> Result<(DeformationGradient, V), ConstitutiveError>;
76}
77
78impl<T, V, T1, T2, T3> FirstOrderMinimize<V, T1, T2, T3> for T
79where
80    T: HyperelasticIV<V, T1, T2, T3>,
81    T1: Tensor,
82    T2: Tensor,
83    T3: Tensor,
84    T: ElasticIV<V, T1, T2, T3>,
85    V: Tensor,
86{
87    type Variables = TensorTuple<DeformationGradient, V>;
88    fn minimize(
89        &self,
90        applied_load: AppliedLoad,
91        solver: impl FirstOrderOptimization<Scalar, Self::Variables>,
92    ) -> Result<(DeformationGradient, V), ConstitutiveError> {
93        let (matrix, vector) = bcs(self, applied_load);
94        match solver.minimize(
95            |variables: &Self::Variables| {
96                let (deformation_gradient, internal_variables) = variables.into();
97                Ok(self.helmholtz_free_energy_density(deformation_gradient, internal_variables)?)
98            },
99            |variables: &Self::Variables| {
100                let (deformation_gradient, internal_variables) = variables.into();
101                Ok(TensorTuple::from((
102                    self.first_piola_kirchhoff_stress(deformation_gradient, internal_variables)?,
103                    self.internal_variables_residual(deformation_gradient, internal_variables)?,
104                )))
105            },
106            Self::Variables::from((
107                DeformationGradient::identity(),
108                self.internal_variables_initial(),
109            )),
110            EqualityConstraint::Linear(matrix, vector),
111        ) {
112            Ok(solution) => Ok(solution.into()),
113            Err(error) => Err(ConstitutiveError::Upstream(
114                format!("{error}"),
115                format!("{self:?}"),
116            )),
117        }
118    }
119}
120
121impl<T, V, T1, T2, T3> SecondOrderMinimize<V, T1, T2, T3> for T
122where
123    T1: Tensor,
124    T2: Tensor,
125    T3: Tensor,
126    T: HyperelasticIV<V, T1, T2, T3>,
127    V: Tensor,
128{
129    type Variables = TensorTuple<DeformationGradient, V>;
130    fn minimize(
131        &self,
132        applied_load: AppliedLoad,
133        solver: impl SecondOrderOptimization<
134            Scalar,
135            Self::Variables,
136            TensorTuple<FirstPiolaKirchhoffTangentStiffness, TensorTuple<T1, TensorTuple<T2, T3>>>,
137            Self::Variables,
138        >,
139    ) -> Result<(DeformationGradient, V), ConstitutiveError> {
140        let (matrix, vector) = bcs(self, applied_load);
141        match solver.minimize(
142            |variables: &Self::Variables| {
143                let (deformation_gradient, internal_variables) = variables.into();
144                Ok(self.helmholtz_free_energy_density(deformation_gradient, internal_variables)?)
145            },
146            |variables: &Self::Variables| {
147                let (deformation_gradient, internal_variables) = variables.into();
148                Ok(TensorTuple::from((
149                    self.first_piola_kirchhoff_stress(deformation_gradient, internal_variables)?,
150                    self.internal_variables_residual(deformation_gradient, internal_variables)?,
151                )))
152            },
153            |variables: &Self::Variables| {
154                let (deformation_gradient, internal_variables) = variables.into();
155                let tangent_0 = self.first_piola_kirchhoff_tangent_stiffness(
156                    deformation_gradient,
157                    internal_variables,
158                )?;
159                let (tangent_1, tangent_2, tangent_3) =
160                    self.internal_variables_tangents(deformation_gradient, internal_variables)?;
161                Ok((tangent_0, (tangent_1, (tangent_2, tangent_3).into()).into()).into())
162            },
163            Self::Variables::from((
164                DeformationGradient::identity(),
165                self.internal_variables_initial(),
166            )),
167            EqualityConstraint::Linear(matrix, vector),
168            None,
169        ) {
170            Ok(solution) => Ok(solution.into()),
171            Err(error) => Err(ConstitutiveError::Upstream(
172                format!("{error}"),
173                format!("{self:?}"),
174            )),
175        }
176    }
177}