1use crate::{
4 constitutive::{ConstitutiveError, fluid::viscoplastic::Viscoplastic, solid::Solid},
5 math::{
6 ContractFirstSecondIndicesWithSecondIndicesOf, ContractSecondIndexWithFirstIndexOf,
7 IDENTITY, Matrix, Rank2, TensorArray, TensorTuple, TensorTupleVec, Vector,
8 integrate::ExplicitInternalVariables,
9 optimize::{
10 EqualityConstraint, FirstOrderRootFinding, OptimizationError, ZerothOrderRootFinding,
11 },
12 },
13 mechanics::{
14 CauchyStress, CauchyTangentStiffness, Deformation, DeformationGradient,
15 DeformationGradientPlastic, DeformationGradients, FirstPiolaKirchhoffStress,
16 FirstPiolaKirchhoffTangentStiffness, Scalar, SecondPiolaKirchhoffStress,
17 SecondPiolaKirchhoffTangentStiffness, Times,
18 },
19};
20
21pub type StateVariables = TensorTuple<DeformationGradientPlastic, Scalar>;
23
24pub type StateVariablesHistory = TensorTupleVec<DeformationGradientPlastic, Scalar>;
26
27pub enum AppliedLoad<'a> {
29 UniaxialStress(fn(Scalar) -> Scalar, &'a [Scalar]),
31 }
34
35pub trait ElasticViscoplastic
37where
38 Self: Solid + Viscoplastic,
39{
40 fn cauchy_stress(
46 &self,
47 deformation_gradient: &DeformationGradient,
48 deformation_gradient_p: &DeformationGradientPlastic,
49 ) -> Result<CauchyStress, ConstitutiveError> {
50 Ok(deformation_gradient
51 * self.second_piola_kirchhoff_stress(deformation_gradient, deformation_gradient_p)?
52 * deformation_gradient.transpose()
53 / deformation_gradient.determinant())
54 }
55 fn cauchy_tangent_stiffness(
61 &self,
62 deformation_gradient: &DeformationGradient,
63 deformation_gradient_p: &DeformationGradientPlastic,
64 ) -> Result<CauchyTangentStiffness, ConstitutiveError> {
65 let deformation_gradient_inverse_transpose = deformation_gradient.inverse_transpose();
66 let cauchy_stress = self.cauchy_stress(deformation_gradient, deformation_gradient_p)?;
67 let some_stress = &cauchy_stress * &deformation_gradient_inverse_transpose;
68 Ok(self
69 .second_piola_kirchhoff_tangent_stiffness(deformation_gradient, deformation_gradient_p)?
70 .contract_first_second_indices_with_second_indices_of(
71 deformation_gradient,
72 deformation_gradient,
73 )
74 / deformation_gradient.determinant()
75 - CauchyTangentStiffness::dyad_ij_kl(
76 &cauchy_stress,
77 &deformation_gradient_inverse_transpose,
78 )
79 + CauchyTangentStiffness::dyad_il_kj(&some_stress, &IDENTITY)
80 + CauchyTangentStiffness::dyad_ik_jl(&IDENTITY, &some_stress))
81 }
82 fn first_piola_kirchhoff_stress(
88 &self,
89 deformation_gradient: &DeformationGradient,
90 deformation_gradient_p: &DeformationGradientPlastic,
91 ) -> Result<FirstPiolaKirchhoffStress, ConstitutiveError> {
92 Ok(
93 self.cauchy_stress(deformation_gradient, deformation_gradient_p)?
94 * deformation_gradient.inverse_transpose()
95 * deformation_gradient.determinant(),
96 )
97 }
98 fn first_piola_kirchhoff_tangent_stiffness(
104 &self,
105 deformation_gradient: &DeformationGradient,
106 deformation_gradient_p: &DeformationGradientPlastic,
107 ) -> Result<FirstPiolaKirchhoffTangentStiffness, ConstitutiveError> {
108 let deformation_gradient_inverse_transpose = deformation_gradient.inverse_transpose();
109 let first_piola_kirchhoff_stress =
110 self.first_piola_kirchhoff_stress(deformation_gradient, deformation_gradient_p)?;
111 Ok(self
112 .cauchy_tangent_stiffness(deformation_gradient, deformation_gradient_p)?
113 .contract_second_index_with_first_index_of(&deformation_gradient_inverse_transpose)
114 * deformation_gradient.determinant()
115 + FirstPiolaKirchhoffTangentStiffness::dyad_ij_kl(
116 &first_piola_kirchhoff_stress,
117 &deformation_gradient_inverse_transpose,
118 )
119 - FirstPiolaKirchhoffTangentStiffness::dyad_il_kj(
120 &first_piola_kirchhoff_stress,
121 &deformation_gradient_inverse_transpose,
122 ))
123 }
124 fn second_piola_kirchhoff_stress(
130 &self,
131 deformation_gradient: &DeformationGradient,
132 deformation_gradient_p: &DeformationGradientPlastic,
133 ) -> Result<SecondPiolaKirchhoffStress, ConstitutiveError> {
134 Ok(deformation_gradient.inverse()
135 * self.first_piola_kirchhoff_stress(deformation_gradient, deformation_gradient_p)?)
136 }
137 fn second_piola_kirchhoff_tangent_stiffness(
143 &self,
144 deformation_gradient: &DeformationGradient,
145 deformation_gradient_p: &DeformationGradientPlastic,
146 ) -> Result<SecondPiolaKirchhoffTangentStiffness, ConstitutiveError> {
147 let deformation_gradient_inverse_transpose = deformation_gradient.inverse_transpose();
148 let deformation_gradient_inverse = deformation_gradient_inverse_transpose.transpose();
149 let second_piola_kirchhoff_stress =
150 self.second_piola_kirchhoff_stress(deformation_gradient, deformation_gradient_p)?;
151 Ok(self
152 .cauchy_tangent_stiffness(deformation_gradient, deformation_gradient_p)?
153 .contract_first_second_indices_with_second_indices_of(
154 &deformation_gradient_inverse,
155 &deformation_gradient_inverse,
156 )
157 * deformation_gradient.determinant()
158 + SecondPiolaKirchhoffTangentStiffness::dyad_ij_kl(
159 &second_piola_kirchhoff_stress,
160 &deformation_gradient_inverse_transpose,
161 )
162 - SecondPiolaKirchhoffTangentStiffness::dyad_il_kj(
163 &second_piola_kirchhoff_stress,
164 &deformation_gradient_inverse_transpose,
165 )
166 - SecondPiolaKirchhoffTangentStiffness::dyad_ik_jl(
167 &deformation_gradient_inverse,
168 &second_piola_kirchhoff_stress,
169 ))
170 }
171 fn state_variables_evolution(
177 &self,
178 deformation_gradient: &DeformationGradient,
179 state_variables: &StateVariables,
180 ) -> Result<StateVariables, ConstitutiveError> {
181 let (deformation_gradient_p, yield_stress) = state_variables.into();
182 let jacobian = deformation_gradient.jacobian().unwrap();
183 let deformation_gradient_e = deformation_gradient * deformation_gradient_p.inverse();
184 let cauchy_stress = self.cauchy_stress(deformation_gradient, deformation_gradient_p)?;
185 let mandel_stress_e = (deformation_gradient_e.transpose()
186 * cauchy_stress
187 * deformation_gradient_e.inverse_transpose())
188 * jacobian;
189 let plastic_stretching_rate =
190 self.plastic_stretching_rate(mandel_stress_e.deviatoric(), *yield_stress)?;
191 Ok(StateVariables::from((
192 &plastic_stretching_rate * deformation_gradient_p,
193 self.yield_stress_evolution(&plastic_stretching_rate)?,
194 )))
195 }
196}
197
198pub trait ZerothOrderRoot {
200 fn root(
206 &self,
207 applied_load: AppliedLoad,
208 integrator: impl ExplicitInternalVariables<
209 StateVariables,
210 DeformationGradient,
211 StateVariablesHistory,
212 DeformationGradients,
213 >,
214 solver: impl ZerothOrderRootFinding<DeformationGradient>,
215 ) -> Result<(Times, DeformationGradients, StateVariablesHistory), ConstitutiveError>;
216 #[doc(hidden)]
217 fn root_inner_0(
218 &self,
219 deformation_gradient_p: &DeformationGradientPlastic,
220 equality_constraint: EqualityConstraint,
221 solver: &impl ZerothOrderRootFinding<DeformationGradient>,
222 initial_guess: &DeformationGradient,
223 ) -> Result<DeformationGradient, OptimizationError>;
224}
225
226pub trait FirstOrderRoot {
228 fn root(
234 &self,
235 applied_load: AppliedLoad,
236 integrator: impl ExplicitInternalVariables<
237 StateVariables,
238 DeformationGradient,
239 StateVariablesHistory,
240 DeformationGradients,
241 >,
242 solver: impl FirstOrderRootFinding<
243 FirstPiolaKirchhoffStress,
244 FirstPiolaKirchhoffTangentStiffness,
245 DeformationGradient,
246 >,
247 ) -> Result<(Times, DeformationGradients, StateVariablesHistory), ConstitutiveError>;
248 #[doc(hidden)]
249 fn root_inner_1(
250 &self,
251 deformation_gradient_p: &DeformationGradientPlastic,
252 equality_constraint: EqualityConstraint,
253 solver: &impl FirstOrderRootFinding<
254 FirstPiolaKirchhoffStress,
255 FirstPiolaKirchhoffTangentStiffness,
256 DeformationGradient,
257 >,
258 initial_guess: &DeformationGradient,
259 ) -> Result<DeformationGradient, OptimizationError>;
260}
261
262impl<T> ZerothOrderRoot for T
263where
264 T: ElasticViscoplastic,
265{
266 fn root(
267 &self,
268 applied_load: AppliedLoad,
269 integrator: impl ExplicitInternalVariables<
270 StateVariables,
271 DeformationGradient,
272 StateVariablesHistory,
273 DeformationGradients,
274 >,
275 solver: impl ZerothOrderRootFinding<DeformationGradient>,
276 ) -> Result<(Times, DeformationGradients, StateVariablesHistory), ConstitutiveError> {
277 match match applied_load {
278 AppliedLoad::UniaxialStress(deformation_gradient_11, time) => {
279 let mut matrix = Matrix::zero(4, 9);
280 let mut vector = Vector::zero(4);
281 matrix[0][0] = 1.0;
282 matrix[1][1] = 1.0;
283 matrix[2][2] = 1.0;
284 matrix[3][5] = 1.0;
285 integrator.integrate_and_evaluate(
286 |_: Scalar,
287 state_variables: &StateVariables,
288 deformation_gradient: &DeformationGradient| {
289 Ok(self.state_variables_evolution(deformation_gradient, state_variables)?)
290 },
291 |t: Scalar,
292 state_variables: &StateVariables,
293 deformation_gradient: &DeformationGradient| {
294 let (deformation_gradient_p, _) = state_variables.into();
295 vector[0] = deformation_gradient_11(t);
296 Ok(self.root_inner_0(
297 deformation_gradient_p,
298 EqualityConstraint::Linear(matrix.clone(), vector.clone()),
299 &solver,
300 deformation_gradient,
301 )?)
302 },
303 time,
304 StateVariables::from((
305 DeformationGradientPlastic::identity(),
306 self.initial_yield_stress(),
307 )),
308 DeformationGradient::identity(),
309 )
310 }
311 } {
312 Ok((times, state_variables, _, deformation_gradients)) => {
313 Ok((times, deformation_gradients, state_variables))
314 }
315 Err(error) => Err(ConstitutiveError::Upstream(
316 format!("{error}"),
317 format!("{self:?}"),
318 )),
319 }
320 }
321 #[doc(hidden)]
322 fn root_inner_0(
323 &self,
324 deformation_gradient_p: &DeformationGradientPlastic,
325 equality_constraint: EqualityConstraint,
326 solver: &impl ZerothOrderRootFinding<DeformationGradient>,
327 initial_guess: &DeformationGradient,
328 ) -> Result<DeformationGradient, OptimizationError> {
329 solver.root(
330 |deformation_gradient: &DeformationGradient| {
331 Ok(self
332 .first_piola_kirchhoff_stress(deformation_gradient, deformation_gradient_p)?)
333 },
334 initial_guess.clone(),
335 equality_constraint,
336 )
337 }
338}
339
340impl<T> FirstOrderRoot for T
341where
342 T: ElasticViscoplastic,
343{
344 fn root(
345 &self,
346 applied_load: AppliedLoad,
347 integrator: impl ExplicitInternalVariables<
348 StateVariables,
349 DeformationGradient,
350 StateVariablesHistory,
351 DeformationGradients,
352 >,
353 solver: impl FirstOrderRootFinding<
354 FirstPiolaKirchhoffStress,
355 FirstPiolaKirchhoffTangentStiffness,
356 DeformationGradient,
357 >,
358 ) -> Result<(Times, DeformationGradients, StateVariablesHistory), ConstitutiveError> {
359 match match applied_load {
360 AppliedLoad::UniaxialStress(deformation_gradient_11, time) => {
361 let mut matrix = Matrix::zero(4, 9);
362 let mut vector = Vector::zero(4);
363 matrix[0][0] = 1.0;
364 matrix[1][1] = 1.0;
365 matrix[2][2] = 1.0;
366 matrix[3][5] = 1.0;
367 integrator.integrate_and_evaluate(
368 |_: Scalar,
369 state_variables: &StateVariables,
370 deformation_gradient: &DeformationGradient| {
371 Ok(self.state_variables_evolution(deformation_gradient, state_variables)?)
372 },
373 |t: Scalar,
374 state_variables: &StateVariables,
375 deformation_gradient: &DeformationGradient| {
376 let (deformation_gradient_p, _) = state_variables.into();
377 vector[0] = deformation_gradient_11(t);
378 Ok(self.root_inner_1(
379 deformation_gradient_p,
380 EqualityConstraint::Linear(matrix.clone(), vector.clone()),
381 &solver,
382 deformation_gradient,
383 )?)
384 },
385 time,
386 StateVariables::from((
387 DeformationGradientPlastic::identity(),
388 self.initial_yield_stress(),
389 )),
390 DeformationGradient::identity(),
391 )
392 }
393 } {
394 Ok((times, state_variables, _, deformation_gradients)) => {
395 Ok((times, deformation_gradients, state_variables))
396 }
397 Err(error) => Err(ConstitutiveError::Upstream(
398 format!("{error}"),
399 format!("{self:?}"),
400 )),
401 }
402 }
403 #[doc(hidden)]
404 fn root_inner_1(
405 &self,
406 deformation_gradient_p: &DeformationGradientPlastic,
407 equality_constraint: EqualityConstraint,
408 solver: &impl FirstOrderRootFinding<
409 FirstPiolaKirchhoffStress,
410 FirstPiolaKirchhoffTangentStiffness,
411 DeformationGradient,
412 >,
413 initial_guess: &DeformationGradient,
414 ) -> Result<DeformationGradient, OptimizationError> {
415 solver.root(
416 |deformation_gradient: &DeformationGradient| {
417 Ok(self
418 .first_piola_kirchhoff_stress(deformation_gradient, deformation_gradient_p)?)
419 },
420 |deformation_gradient: &DeformationGradient| {
421 Ok(self.first_piola_kirchhoff_tangent_stiffness(
422 deformation_gradient,
423 deformation_gradient_p,
424 )?)
425 },
426 initial_guess.clone(),
427 equality_constraint,
428 )
429 }
430}