conspire/constitutive/solid/hyperelastic/eight_chain/
mod.rs1#[cfg(test)]
2mod test;
3
4use crate::{
5 constitutive::{
6 ConstitutiveError,
7 solid::{FIVE_THIRDS, Solid, TWO_THIRDS, elastic::Elastic, hyperelastic::Hyperelastic},
8 },
9 math::{IDENTITY, Quantity, Rank2, TensorRank4},
10 mechanics::{CauchyStress, CauchyTangentStiffness, Deformation, DeformationGradient, Scalar},
11 physics::molecular::single_chain::Thermodynamics as SingleChainThermodynamics,
12 units::{EnergyDensity, Stress},
13};
14use std::{
15 any::type_name,
16 fmt::{self, Debug, Formatter},
17};
18
19#[doc = include_str!("doc.md")]
20#[derive(Clone)]
21pub struct EightChain<T>
22where
23 T: SingleChainThermodynamics,
24{
25 pub bulk_modulus: Quantity<Stress>,
27 pub shear_modulus: Quantity<Stress>,
29 pub single_chain_model: T,
31}
32
33impl<T> Debug for EightChain<T>
34where
35 T: SingleChainThermodynamics,
36{
37 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
38 write!(
39 f,
40 "EightChain {{ bulk_modulus: {}, shear_modulus: {}, single_chain_model: {} }}",
41 self.bulk_modulus,
42 self.shear_modulus,
43 type_name::<T>()
44 .rsplit("::")
45 .next()
46 .unwrap()
47 .split("<")
48 .next()
49 .unwrap(),
50 )
51 }
52}
53
54impl<T> EightChain<T>
55where
56 T: SingleChainThermodynamics,
57{
58 fn nondimensional_force(
60 &self,
61 nondimensional_extension: Scalar,
62 ) -> Result<Scalar, ConstitutiveError> {
63 SingleChainThermodynamics::nondimensional_force(
64 &self.single_chain_model,
65 nondimensional_extension,
66 )
67 .map_err(|error| ConstitutiveError::upstream(error, self))
68 }
69 fn nondimensional_stiffness(
71 &self,
72 nondimensional_extension: Scalar,
73 ) -> Result<Scalar, ConstitutiveError> {
74 SingleChainThermodynamics::nondimensional_stiffness(
75 &self.single_chain_model,
76 nondimensional_extension,
77 )
78 .map_err(|error| ConstitutiveError::upstream(error, self))
79 }
80 pub fn number_of_links(&self) -> Scalar {
82 self.single_chain_model.number_of_links() as Scalar
83 }
84 pub fn single_chain_model(&self) -> &T {
86 &self.single_chain_model
87 }
88}
89
90impl<T> Solid for EightChain<T>
91where
92 T: SingleChainThermodynamics,
93{
94 fn bulk_modulus(&self) -> Quantity<Stress> {
95 self.bulk_modulus
96 }
97 fn shear_modulus(&self) -> Quantity<Stress> {
98 self.shear_modulus
99 }
100}
101
102impl<T> Elastic for EightChain<T>
103where
104 T: SingleChainThermodynamics,
105{
106 #[doc = include_str!("cauchy_stress.md")]
107 fn cauchy_stress(
108 &self,
109 deformation_gradient: &DeformationGradient,
110 ) -> Result<CauchyStress, ConstitutiveError> {
111 let jacobian = self.jacobian(deformation_gradient)?;
112 let (
113 deviatoric_isochoric_left_cauchy_green_deformation,
114 isochoric_left_cauchy_green_deformation_trace,
115 ) = (deformation_gradient.left_cauchy_green() / jacobian.powf(TWO_THIRDS))
116 .deviatoric_and_trace();
117 let gamma = (isochoric_left_cauchy_green_deformation_trace / 3.0 / self.number_of_links())
118 .sqrt()
119 .value();
120 let gamma_0 = (1.0 / self.number_of_links()).sqrt();
121 Ok(deviatoric_isochoric_left_cauchy_green_deformation
122 * (self.shear_modulus() * self.nondimensional_force(gamma)?
123 / self.nondimensional_force(gamma_0)?
124 * gamma_0
125 / gamma
126 / jacobian)
127 + IDENTITY * self.bulk_modulus() * 0.5 * (jacobian - 1.0 / jacobian))
128 }
129 #[doc = include_str!("cauchy_tangent_stiffness.md")]
130 fn cauchy_tangent_stiffness(
131 &self,
132 deformation_gradient: &DeformationGradient,
133 ) -> Result<CauchyTangentStiffness, ConstitutiveError> {
134 let jacobian = self.jacobian(deformation_gradient)?;
135 let inverse_transpose_deformation_gradient = deformation_gradient.inverse_transpose();
136 let left_cauchy_green_deformation = deformation_gradient.left_cauchy_green();
137 let deviatoric_left_cauchy_green_deformation = left_cauchy_green_deformation.deviatoric();
138 let (
139 deviatoric_isochoric_left_cauchy_green_deformation,
140 isochoric_left_cauchy_green_deformation_trace,
141 ) = (left_cauchy_green_deformation / jacobian.powf(TWO_THIRDS)).deviatoric_and_trace();
142 let gamma = (isochoric_left_cauchy_green_deformation_trace / 3.0 / self.number_of_links())
143 .sqrt()
144 .value();
145 let gamma_0 = (1.0 / self.number_of_links()).sqrt();
146 let eta = self.nondimensional_force(gamma)?;
147 let scaled_shear_modulus =
148 gamma_0 / self.nondimensional_force(gamma_0)? * self.shear_modulus() * eta
149 / gamma
150 / jacobian.powf(FIVE_THIRDS);
151 let scaled_deviatoric_isochoric_left_cauchy_green_deformation =
152 deviatoric_left_cauchy_green_deformation * scaled_shear_modulus;
153 let term = TensorRank4::dyad_ij_kl(
154 &scaled_deviatoric_isochoric_left_cauchy_green_deformation,
155 &(deviatoric_isochoric_left_cauchy_green_deformation
156 * &inverse_transpose_deformation_gradient
157 * ((self.nondimensional_stiffness(gamma)? / eta - 1.0 / gamma)
158 / 3.0
159 / self.number_of_links()
160 / gamma)),
161 );
162 Ok((TensorRank4::dyad_ik_jl(&IDENTITY, deformation_gradient)
163 + TensorRank4::dyad_il_jk(deformation_gradient, &IDENTITY)
164 - TensorRank4::dyad_ij_kl(&IDENTITY, deformation_gradient) * (TWO_THIRDS))
165 * scaled_shear_modulus
166 + TensorRank4::dyad_ij_kl(
167 &(IDENTITY * (0.5 * self.bulk_modulus() * (jacobian + 1.0 / jacobian))
168 - scaled_deviatoric_isochoric_left_cauchy_green_deformation * (FIVE_THIRDS)),
169 &inverse_transpose_deformation_gradient,
170 )
171 + term)
172 }
173}
174
175impl<T> Hyperelastic for EightChain<T>
176where
177 T: SingleChainThermodynamics,
178{
179 #[doc = include_str!("helmholtz_free_energy_density.md")]
180 fn helmholtz_free_energy_density(
181 &self,
182 deformation_gradient: &DeformationGradient,
183 ) -> Result<Quantity<EnergyDensity>, ConstitutiveError> {
184 let jacobian = self.jacobian(deformation_gradient)?;
185 let isochoric_left_cauchy_green_deformation =
186 deformation_gradient.left_cauchy_green() / jacobian.powf(TWO_THIRDS);
187 let gamma =
188 (isochoric_left_cauchy_green_deformation.trace() / 3.0 / self.number_of_links())
189 .sqrt()
190 .value();
191 let eta = self.nondimensional_force(gamma)?;
192 let gamma_0 = (1.0 / self.number_of_links()).sqrt();
193 let eta_0 = self.nondimensional_force(gamma_0)?;
194 Ok(3.0 * gamma_0 / eta_0
198 * self.shear_modulus()
199 * self.number_of_links()
200 * (gamma * eta - gamma_0 * eta_0 - (eta_0 * eta.sinh() / (eta * eta_0.sinh())).ln())
201 + 0.5 * self.bulk_modulus() * (0.5 * (jacobian.powi(2) - 1.0) - jacobian.ln()))
202 }
203}