Skip to main content

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

1#[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    /// The bulk modulus $`\kappa`$.
26    pub bulk_modulus: Quantity<Stress>,
27    /// The shear modulus $`\mu`$.
28    pub shear_modulus: Quantity<Stress>,
29    /// The single-chain model.
30    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    /// Returns the nondimensional force in a single chain.
59    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    /// Returns the nondimensional stiffness in a single chain.
70    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    /// Returns the number of links in a single chain.
81    pub fn number_of_links(&self) -> Scalar {
82        self.single_chain_model.number_of_links() as Scalar
83    }
84    /// Returns the single-chain model.
85    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        //
195        // If end up re-using so much of ArrudaBoyce should make helper function to share.
196        //
197        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}