#[cfg(test)]
mod test;
use crate::{
constitutive::{ConstitutiveError, fluid::plastic::Plastic},
math::{
Derivative, Differentiate, Quantity, Rank2, Scalar, Tensor, TensorArray, TensorTuple,
TensorTupleVec,
},
mechanics::{
DeformationGradientPlastic, DeformationGradientRatePlastic, MandelStressElastic,
StretchingRatePlastic,
},
units::{Dissipation, Rate, Stress},
};
pub type ViscoplasticStateVariables<Y> = TensorTuple<DeformationGradientPlastic, Y>;
pub type ViscoplasticStateVariablesHistory<Y> = TensorTupleVec<DeformationGradientPlastic, Y>;
pub type ViscoplasticEvolution<Y> = Derivative<ViscoplasticStateVariables<Y>>;
pub type ViscoplasticEvolutionHistory<Y> =
TensorTupleVec<DeformationGradientRatePlastic, Derivative<Y>>;
pub trait Viscoplastic<Y>
where
Self: Plastic,
Y: Differentiate + Tensor,
{
fn initial_state(&self) -> ViscoplasticStateVariables<Y>;
fn plastic_evolution(
&self,
mandel_stress: MandelStressElastic,
state_variables: &ViscoplasticStateVariables<Y>,
) -> Result<ViscoplasticEvolution<Y>, ConstitutiveError>;
fn plastic_stretching_rate(
&self,
deviatoric_mandel_stress: MandelStressElastic,
yield_stress: Quantity<Stress>,
) -> Result<StretchingRatePlastic, ConstitutiveError> {
let magnitude = deviatoric_mandel_stress.norm();
if magnitude.is_zero() {
Ok(StretchingRatePlastic::zero())
} else {
let reference_flow_rate = self.reference_flow_rate();
Ok(deviatoric_mandel_stress
* (reference_flow_rate / magnitude
* (magnitude / yield_stress).powf(1.0 / self.rate_sensitivity())))
}
}
fn dissipation_potential(
&self,
plastic_stretching_rate: StretchingRatePlastic,
yield_stress: Quantity<Stress>,
) -> Result<Quantity<Dissipation>, ConstitutiveError> {
let rate_sensitivity = self.rate_sensitivity();
let reference_flow_rate = self.reference_flow_rate();
Ok(
reference_flow_rate * yield_stress / (1.0 + rate_sensitivity)
* (plastic_stretching_rate.norm() / reference_flow_rate)
.powf(1.0 + rate_sensitivity),
)
}
fn dual_dissipation_potential(
&self,
deviatoric_mandel_stress: MandelStressElastic,
yield_stress: Quantity<Stress>,
) -> Result<Quantity<Dissipation>, ConstitutiveError> {
let rate_sensitivity = self.rate_sensitivity();
Ok(self.reference_flow_rate()
* yield_stress
* (rate_sensitivity / (1.0 + rate_sensitivity))
* (deviatoric_mandel_stress.norm() / yield_stress)
.powf((1.0 + rate_sensitivity) / rate_sensitivity))
}
fn rate_sensitivity(&self) -> Scalar;
fn reference_flow_rate(&self) -> Quantity<Rate>;
}
#[derive(Clone, Debug)]
pub struct ViscoplasticFlow {
pub yield_stress: Quantity<Stress>,
pub hardening_slope: Quantity<Stress>,
pub rate_sensitivity: Scalar,
pub reference_flow_rate: Quantity<Rate>,
}
impl Plastic for ViscoplasticFlow {
fn initial_yield_stress(&self) -> Quantity<Stress> {
self.yield_stress
}
fn hardening_slope(&self) -> Quantity<Stress> {
self.hardening_slope
}
}
impl Viscoplastic<Quantity> for ViscoplasticFlow {
fn initial_state(&self) -> ViscoplasticStateVariables<Quantity> {
(DeformationGradientPlastic::identity(), Quantity::default()).into()
}
fn plastic_evolution(
&self,
mandel_stress: MandelStressElastic,
state_variables: &ViscoplasticStateVariables<Quantity>,
) -> Result<ViscoplasticEvolution<Quantity>, ConstitutiveError> {
default_plastic_evolution(self, mandel_stress, state_variables)
}
fn rate_sensitivity(&self) -> Scalar {
self.rate_sensitivity
}
fn reference_flow_rate(&self) -> Quantity<Rate> {
self.reference_flow_rate
}
}
pub fn default_plastic_evolution<C>(
model: &C,
mandel_stress: MandelStressElastic,
state_variables: &ViscoplasticStateVariables<Quantity>,
) -> Result<ViscoplasticEvolution<Quantity>, ConstitutiveError>
where
C: Viscoplastic<Quantity>,
{
let (deformation_gradient_p, &equivalent_plastic_strain) = state_variables.into();
let plastic_stretching_rate = model.plastic_stretching_rate(
mandel_stress.deviatoric(),
model.yield_stress(equivalent_plastic_strain)?,
)?;
let equivalent_plastic_strain_rate = plastic_stretching_rate.norm();
Ok((
plastic_stretching_rate * deformation_gradient_p,
equivalent_plastic_strain_rate,
)
.into())
}