Skip to main content

conspire/constitutive/fluid/viscoplastic/
mod.rs

1//! Viscoplastic fluid constitutive models.
2
3#[cfg(feature = "autodiff")]
4pub mod autodiff;
5
6#[cfg(test)]
7mod test;
8
9use crate::{
10    constitutive::{ConstitutiveError, fluid::plastic::Plastic},
11    math::{
12        Derivative, Differentiable, IDENTITY_22, Intermediate, Quantity, Rank2, Scalar, Tensor,
13        TensorArray, TensorRank4, TensorTuple, TensorTupleVec,
14    },
15    mechanics::{
16        DeformationGradientPlastic, DeformationGradientRatePlastic, MandelStressElastic,
17        StretchingRatePlastic, StretchingRatePlasticTangent, StretchingRatePlasticTangentYield,
18    },
19    units::{Dissipation, Rate, Stress},
20};
21
22type IntermediateRank4 = TensorRank4<3, Intermediate, Intermediate, Intermediate, Intermediate>;
23
24/// Viscoplastic state variables.
25pub type ViscoplasticStateVariables<Y> = TensorTuple<DeformationGradientPlastic, Y>;
26
27/// Viscoplastic state variables history.
28pub type ViscoplasticStateVariablesHistory<Y> = TensorTupleVec<DeformationGradientPlastic, Y>;
29
30/// The evolution of the viscoplastic state variables.
31pub type ViscoplasticEvolution<Y> = Derivative<ViscoplasticStateVariables<Y>>;
32
33/// The viscoplastic state's Lie-algebra rate `(D_p, Ẏ)`, as consumed by the
34/// field time-integration drivers — the plastic stretching rate itself, not the
35/// group velocity `Ḟ_p = D_p F_p`, paired with the hardening-variable rate.
36pub type ViscoplasticAlgebraRate<Y = Quantity> = TensorTuple<StretchingRatePlastic, Derivative<Y>>;
37
38/// The history of the evolution of the viscoplastic state variables.
39pub type ViscoplasticEvolutionHistory<Y> =
40    TensorTupleVec<DeformationGradientRatePlastic, Derivative<Y>>;
41
42/// Required methods for viscoplastic fluid constitutive models.
43pub trait Viscoplastic<Y>
44where
45    Self: Plastic,
46    Y: Differentiable + Tensor,
47{
48    /// Returns the initial state of the variables.
49    fn initial_state(&self) -> ViscoplasticStateVariables<Y>;
50    /// Calculates and returns the plastic evolution.
51    ///
52    /// ```math
53    /// \dot{\mathbf{F}}_\mathrm{p} = \mathbf{D}_\mathrm{p}\cdot\mathbf{F}_\mathrm{p}\quad\text{and}\quad\dot{\varepsilon}_\mathrm{p} = |\mathbf{D}_\mathrm{p}|
54    /// ```
55    fn plastic_evolution(
56        &self,
57        mandel_stress: MandelStressElastic,
58        state_variables: &ViscoplasticStateVariables<Y>,
59    ) -> Result<ViscoplasticEvolution<Y>, ConstitutiveError>;
60    /// Calculates and returns the rate of plastic stretching.
61    ///
62    /// ```math
63    /// \mathbf{D}_\mathrm{p}(\mathbf{M}_\mathrm{e}') = d_0\left(\frac{|\mathbf{M}_\mathrm{e}'|}{Y(S)}\right)^{\footnotesize\tfrac{1}{m}}\frac{\mathbf{M}_\mathrm{e}'}{|\mathbf{M}_\mathrm{e}'|}
64    /// ```
65    fn plastic_stretching_rate(
66        &self,
67        deviatoric_mandel_stress: MandelStressElastic,
68        yield_stress: Quantity<Stress>,
69    ) -> Result<StretchingRatePlastic, ConstitutiveError> {
70        let magnitude = deviatoric_mandel_stress.norm();
71        if magnitude.is_zero() {
72            Ok(StretchingRatePlastic::zero())
73        } else {
74            let reference_flow_rate = self.reference_flow_rate();
75            Ok(deviatoric_mandel_stress
76                * (reference_flow_rate / magnitude
77                    * (magnitude / yield_stress).powf(1.0 / self.rate_sensitivity())))
78        }
79    }
80    /// Calculates and returns the tangent of the plastic stretching rate with
81    /// respect to the deviatoric Mandel stress.
82    ///
83    /// ```math
84    /// \frac{\partial D^\mathrm{p}_{ij}}{\partial M'_{kl}} = g\,\delta_{ik}\delta_{jl} + g\left(\frac{1}{m} - 1\right)\frac{M'_{ij}M'_{kl}}{|\mathbf{M}'|^2},
85    /// \qquad g = \frac{d_0}{|\mathbf{M}'|}\left(\frac{|\mathbf{M}'|}{Y}\right)^{\footnotesize\tfrac{1}{m}}
86    /// ```
87    fn plastic_stretching_rate_tangent(
88        &self,
89        deviatoric_mandel_stress: &MandelStressElastic,
90        yield_stress: Quantity<Stress>,
91    ) -> Result<StretchingRatePlasticTangent, ConstitutiveError> {
92        let magnitude = deviatoric_mandel_stress.norm();
93        if magnitude.is_zero() {
94            Ok(StretchingRatePlasticTangent::zero())
95        } else {
96            let rate_sensitivity = self.rate_sensitivity();
97            let flow = self.reference_flow_rate() / magnitude
98                * (magnitude / yield_stress).powf(1.0 / rate_sensitivity);
99            let normal = deviatoric_mandel_stress / magnitude;
100            Ok(
101                IntermediateRank4::dyad_ik_jl(&IDENTITY_22, &IDENTITY_22) * flow
102                    + IntermediateRank4::dyad_ij_kl(&normal, &normal)
103                        * (flow * (1.0 / rate_sensitivity - 1.0)),
104            )
105        }
106    }
107    /// Calculates and returns the tangent of the plastic stretching rate with
108    /// respect to the yield stress.
109    ///
110    /// ```math
111    /// \frac{\partial\mathbf{D}^\mathrm{p}}{\partial Y} = -\frac{\mathbf{D}^\mathrm{p}}{mY}
112    /// ```
113    fn plastic_stretching_rate_tangent_yield(
114        &self,
115        deviatoric_mandel_stress: MandelStressElastic,
116        yield_stress: Quantity<Stress>,
117    ) -> Result<StretchingRatePlasticTangentYield, ConstitutiveError> {
118        Ok(
119            self.plastic_stretching_rate(deviatoric_mandel_stress, yield_stress)? / yield_stress
120                * (-1.0 / self.rate_sensitivity()),
121        )
122    }
123    /// Calculates and returns the dissipation potential.
124    ///
125    /// ```math
126    /// \phi(\mathbf{D}_\mathrm{p}) = \frac{Yd_0}{1+m}\left(\frac{|\mathbf{D}_\mathrm{p}|}{d_0}\right)^{1+m}
127    /// ```
128    fn dissipation_potential(
129        &self,
130        plastic_stretching_rate: StretchingRatePlastic,
131        yield_stress: Quantity<Stress>,
132    ) -> Result<Quantity<Dissipation>, ConstitutiveError> {
133        let rate_sensitivity = self.rate_sensitivity();
134        let reference_flow_rate = self.reference_flow_rate();
135        Ok(
136            reference_flow_rate * yield_stress / (1.0 + rate_sensitivity)
137                * (plastic_stretching_rate.norm() / reference_flow_rate)
138                    .powf(1.0 + rate_sensitivity),
139        )
140    }
141    /// Calculates and returns the dual dissipation potential.
142    ///
143    /// ```math
144    /// \phi^*(\mathbf{M}_\mathrm{e}') = \frac{Yd_0m}{1+m}\left(\frac{|\mathbf{M}_\mathrm{e}'|}{Y(S)}\right)^{\footnotesize\tfrac{1+m}{m}}
145    /// ```
146    fn dual_dissipation_potential(
147        &self,
148        deviatoric_mandel_stress: MandelStressElastic,
149        yield_stress: Quantity<Stress>,
150    ) -> Result<Quantity<Dissipation>, ConstitutiveError> {
151        let rate_sensitivity = self.rate_sensitivity();
152        Ok(self.reference_flow_rate()
153            * yield_stress
154            * (rate_sensitivity / (1.0 + rate_sensitivity))
155            * (deviatoric_mandel_stress.norm() / yield_stress)
156                .powf((1.0 + rate_sensitivity) / rate_sensitivity))
157    }
158    /// Returns the rate_sensitivity parameter.
159    fn rate_sensitivity(&self) -> Scalar;
160    /// Returns the reference flow rate.
161    fn reference_flow_rate(&self) -> Quantity<Rate>;
162}
163
164/// The viscoplastic flow model.
165#[derive(Clone, Debug)]
166pub struct ViscoplasticFlow {
167    /// The initial yield stress $`Y_0`$.
168    pub yield_stress: Quantity<Stress>,
169    /// The isotropic hardening slope $`H`$.
170    pub hardening_slope: Quantity<Stress>,
171    /// The rate sensitivity parameter $`m`$.
172    pub rate_sensitivity: Scalar,
173    /// The reference flow rate $`d_0`$.
174    pub reference_flow_rate: Quantity<Rate>,
175}
176
177impl Plastic for ViscoplasticFlow {
178    fn initial_yield_stress(&self) -> Quantity<Stress> {
179        self.yield_stress
180    }
181    fn hardening_slope(&self) -> Quantity<Stress> {
182        self.hardening_slope
183    }
184}
185
186impl Viscoplastic<Quantity> for ViscoplasticFlow {
187    fn initial_state(&self) -> ViscoplasticStateVariables<Quantity> {
188        (DeformationGradientPlastic::identity(), Quantity::default()).into()
189    }
190    fn plastic_evolution(
191        &self,
192        mandel_stress: MandelStressElastic,
193        state_variables: &ViscoplasticStateVariables<Quantity>,
194    ) -> Result<ViscoplasticEvolution<Quantity>, ConstitutiveError> {
195        default_plastic_evolution(self, mandel_stress, state_variables)
196    }
197    fn rate_sensitivity(&self) -> Scalar {
198        self.rate_sensitivity
199    }
200    fn reference_flow_rate(&self) -> Quantity<Rate> {
201        self.reference_flow_rate
202    }
203}
204
205pub fn default_plastic_evolution<C>(
206    model: &C,
207    mandel_stress: MandelStressElastic,
208    state_variables: &ViscoplasticStateVariables<Quantity>,
209) -> Result<ViscoplasticEvolution<Quantity>, ConstitutiveError>
210where
211    C: Viscoplastic<Quantity>,
212{
213    let (deformation_gradient_p, &equivalent_plastic_strain) = state_variables.into();
214    let plastic_stretching_rate = model.plastic_stretching_rate(
215        mandel_stress.deviatoric(),
216        model.yield_stress(equivalent_plastic_strain)?,
217    )?;
218    let equivalent_plastic_strain_rate = plastic_stretching_rate.norm();
219    Ok((
220        plastic_stretching_rate * deformation_gradient_p,
221        equivalent_plastic_strain_rate,
222    )
223        .into())
224}