conspire/constitutive/solid/hyperelastic/ogden/
mod.rs1#[cfg(test)]
2mod test;
3
4use crate::{
5 constitutive::{
6 ConstitutiveError,
7 solid::{Solid, elastic::Elastic, hyperelastic::Hyperelastic},
8 },
9 math::{
10 ContractThirdFourthWithFirstSecond, Current, IDENTITY, Quantity, Rank2, Tensor,
11 TensorRank0List, TensorRank2, TensorRank4,
12 },
13 mechanics::{
14 CauchyStress, CauchyTangentStiffness, Deformation, DeformationGradient,
15 LeftCauchyGreenDeformation, Scalar,
16 },
17 units::{EnergyDensity, Stress},
18};
19
20#[doc = include_str!("doc.md")]
21#[derive(Clone, Debug)]
22pub struct Ogden {
23 pub bulk_modulus: Quantity<Stress>,
25 pub shear_moduli: Vec<Quantity<Stress>>,
27 pub exponents: Vec<Scalar>,
29}
30
31enum Spectrum {
32 Eigen(TensorRank0List<3>, LeftCauchyGreenDeformation),
33 Fallback(LeftCauchyGreenDeformation),
34}
35
36impl Spectrum {
37 fn new(tensor: &LeftCauchyGreenDeformation, model: &Ogden) -> Result<Self, ConstitutiveError> {
38 if tensor.is_diagonal() || (tensor - &IDENTITY).norm() < 1e-2 {
39 Ok(Self::Fallback(tensor.clone()))
40 } else {
41 let (eigenvalues, eigenvectors) = tensor
42 .eigen()
43 .map_err(|error| ConstitutiveError::upstream(error, model))?;
44 Ok(Self::Eigen(eigenvalues, eigenvectors))
45 }
46 }
47 fn powm(
48 &self,
49 exponent: Scalar,
50 model: &Ogden,
51 ) -> Result<LeftCauchyGreenDeformation, ConstitutiveError> {
52 match self {
53 Self::Eigen(eigenvalues, eigenvectors) => {
54 TensorRank2::powm_from_eigen(eigenvalues, eigenvectors, exponent)
55 }
56 Self::Fallback(tensor) => tensor.powm(exponent),
57 }
58 .map_err(|error| ConstitutiveError::upstream(error, model))
59 }
60 fn dpowm(
61 &self,
62 exponent: Scalar,
63 model: &Ogden,
64 ) -> Result<TensorRank4<3, Current, Current, Current, Current>, ConstitutiveError> {
65 match self {
66 Self::Eigen(eigenvalues, eigenvectors) => {
67 TensorRank2::dpowm_from_eigen(eigenvalues, eigenvectors, exponent)
68 }
69 Self::Fallback(tensor) => tensor.dpowm(exponent),
70 }
71 .map_err(|error| ConstitutiveError::upstream(error, model))
72 }
73}
74
75impl Solid for Ogden {
76 fn bulk_modulus(&self) -> Quantity<Stress> {
77 self.bulk_modulus
78 }
79 fn shear_modulus(&self) -> Quantity<Stress> {
80 self.shear_moduli
81 .iter()
82 .zip(self.exponents.iter())
83 .map(|(modulus, exponent)| *modulus * exponent)
84 .sum::<Quantity<Stress>>()
85 * 0.5
86 }
87}
88
89impl Elastic for Ogden {
90 #[doc = include_str!("cauchy_stress.md")]
91 fn cauchy_stress(
92 &self,
93 deformation_gradient: &DeformationGradient,
94 ) -> Result<CauchyStress, ConstitutiveError> {
95 let jacobian = self.jacobian(deformation_gradient)?;
96 let left_cauchy_green = deformation_gradient.left_cauchy_green();
97 let spectrum = Spectrum::new(&left_cauchy_green, self)?;
98 let mut cauchy_stress = IDENTITY * self.bulk_modulus() * 0.5 * (jacobian - 1.0 / jacobian);
99 for (modulus, exponent) in self.shear_moduli.iter().zip(self.exponents.iter()) {
100 let scaling = *modulus / jacobian.powf(exponent / 3.0 + 1.0);
101 cauchy_stress += spectrum.powm(exponent / 2.0, self)?.deviatoric() * scaling;
102 }
103 Ok(cauchy_stress)
104 }
105 #[doc = include_str!("cauchy_tangent_stiffness.md")]
106 fn cauchy_tangent_stiffness(
107 &self,
108 deformation_gradient: &DeformationGradient,
109 ) -> Result<CauchyTangentStiffness, ConstitutiveError> {
110 let jacobian = self.jacobian(deformation_gradient)?;
111 let left_cauchy_green = deformation_gradient.left_cauchy_green();
112 let spectrum = Spectrum::new(&left_cauchy_green, self)?;
113 let inverse_transpose_deformation_gradient = deformation_gradient.inverse_transpose();
114 let mut cauchy_tangent_stiffness = TensorRank4::dyad_ij_kl(
115 &(IDENTITY * (self.bulk_modulus() * 0.5 * (jacobian + 1.0 / jacobian))),
116 &inverse_transpose_deformation_gradient,
117 );
118 for (modulus, exponent) in self.shear_moduli.iter().zip(self.exponents.iter()) {
119 let half_exponent = exponent / 2.0;
120 let scaling = *modulus / jacobian.powf(exponent / 3.0 + 1.0);
121 let scaled_deformation_gradient = deformation_gradient * scaling;
122 let raw = spectrum
123 .dpowm(half_exponent, self)?
124 .contract_third_fourth_with_first_second(
125 &(TensorRank4::dyad_il_jk(&scaled_deformation_gradient, &IDENTITY)
126 + TensorRank4::dyad_ik_jl(&IDENTITY, &scaled_deformation_gradient)),
127 );
128 let trace_term = spectrum.powm(half_exponent - 1.0, self)?
129 * deformation_gradient
130 * (scaling * half_exponent * 2.0);
131 let cauchy_stress_n = spectrum.powm(half_exponent, self)?.deviatoric() * scaling;
132 cauchy_tangent_stiffness += raw
133 - TensorRank4::dyad_ij_kl(&(IDENTITY * (1.0 / 3.0)), &trace_term)
134 - TensorRank4::dyad_ij_kl(
135 &(cauchy_stress_n * (exponent / 3.0 + 1.0)),
136 &inverse_transpose_deformation_gradient,
137 );
138 }
139 Ok(cauchy_tangent_stiffness)
140 }
141}
142
143impl Hyperelastic for Ogden {
144 #[doc = include_str!("helmholtz_free_energy_density.md")]
145 fn helmholtz_free_energy_density(
146 &self,
147 deformation_gradient: &DeformationGradient,
148 ) -> Result<Quantity<EnergyDensity>, ConstitutiveError> {
149 let jacobian = self.jacobian(deformation_gradient)?;
150 let left_cauchy_green = deformation_gradient.left_cauchy_green();
151 let spectrum = Spectrum::new(&left_cauchy_green, self)?;
152 let mut helmholtz_free_energy_density =
153 self.bulk_modulus() * 0.5 * (0.5 * (jacobian.powi(2) - 1.0) - jacobian.ln());
154 for (modulus, exponent) in self.shear_moduli.iter().zip(self.exponents.iter()) {
155 helmholtz_free_energy_density += (*modulus / *exponent)
156 * (spectrum.powm(exponent / 2.0, self)?.trace() / jacobian.powf(exponent / 3.0)
157 - 3.0);
158 }
159 Ok(helmholtz_free_energy_density)
160 }
161}