1#![doc = include_str!("doc.md")]
6
7#[cfg(feature = "doc")]
8pub mod doc;
9
10mod canonical;
11
12use crate::{
13 constitutive::{
14 ConstitutiveError,
15 fluid::viscoplastic::{
16 Viscoplastic, ViscoplasticEvolution, ViscoplasticEvolutionHistory,
17 ViscoplasticStateVariables, ViscoplasticStateVariablesHistory,
18 },
19 },
20 math::{
21 ContractWith, Derivative, Differentiable, Intermediate, Quantity, Rank2, Reference, Scalar,
22 Tensor, TensorArray, TensorRank2, TensorTuple, TensorVec, Vector,
23 integrate::{
24 ButcherTableau, EmbeddedTableau, EvolvedIncrement, ExplicitDaeFirstOrderRoot,
25 ExplicitDaeZerothOrderRoot, Flat, Integrable, Product, StateEvolution, Unimodular,
26 integrate_rkmk_dae_adaptive_first_order_root, rkmk_dae_step_first_order_root,
27 },
28 optimize::{EqualityConstraint, FirstOrderRootFinding, ZerothOrderRootFinding},
29 },
30 mechanics::{
31 DeformationGradient, DeformationGradients, FirstPiolaKirchhoffStress,
32 FirstPiolaKirchhoffTangentStiffness, Times,
33 },
34 units::{Dissipation, Rate, Time},
35};
36use std::ops::{Add, Mul};
37
38use crate::constitutive::solid::elastic_plastic::bcs;
39pub use crate::constitutive::solid::elastic_plastic::{
40 AppliedLoad, ElasticPlasticOrViscoplastic, PlasticTangents,
41};
42
43pub trait ElasticViscoplastic<Y>
45where
46 Self: ElasticPlasticOrViscoplastic + Viscoplastic<Y>,
47 Y: Differentiable + Tensor,
48{
49 fn internal_dissipation(
55 &self,
56 deformation_gradient: &DeformationGradient,
57 state_variables: &ViscoplasticStateVariables<Y>,
58 ) -> Result<Quantity<Dissipation>, ConstitutiveError> {
59 let deformation_gradient_p = &state_variables.0;
60 let plastic_stretching_rate = self
61 .state_variables_evolution(deformation_gradient, state_variables)?
62 .0
63 * deformation_gradient_p.inverse();
64 Ok(self
65 .mandel_stress(deformation_gradient, deformation_gradient_p)?
66 .deviatoric()
67 .contract_with(&plastic_stretching_rate))
68 }
69 fn state_variables_evolution(
71 &self,
72 deformation_gradient: &DeformationGradient,
73 state_variables: &ViscoplasticStateVariables<Y>,
74 ) -> Result<ViscoplasticEvolution<Y>, ConstitutiveError> {
75 self.plastic_evolution(
76 self.mandel_stress(deformation_gradient, &state_variables.0)?,
77 state_variables,
78 )
79 }
80}
81
82pub trait ZerothOrderRoot<Y>
84where
85 Y: Differentiable + Tensor,
86{
87 fn root(
93 &self,
94 applied_load: AppliedLoad,
95 integrator: impl ExplicitDaeZerothOrderRoot<
96 FirstPiolaKirchhoffStress,
97 ViscoplasticStateVariables<Y>,
98 DeformationGradient,
99 ViscoplasticStateVariablesHistory<Y>,
100 DeformationGradients,
101 ViscoplasticEvolutionHistory<Y>,
102 >,
103 solver: impl ZerothOrderRootFinding<FirstPiolaKirchhoffStress, DeformationGradient>,
104 ) -> Result<
105 (
106 Times,
107 DeformationGradients,
108 ViscoplasticStateVariablesHistory<Y>,
109 ),
110 ConstitutiveError,
111 >;
112}
113
114pub trait FirstOrderRoot<Y>
116where
117 Y: Differentiable + Tensor,
118{
119 fn root(
125 &self,
126 applied_load: AppliedLoad,
127 integrator: impl ExplicitDaeFirstOrderRoot<
128 FirstPiolaKirchhoffStress,
129 FirstPiolaKirchhoffTangentStiffness,
130 ViscoplasticStateVariables<Y>,
131 DeformationGradient,
132 ViscoplasticStateVariablesHistory<Y>,
133 DeformationGradients,
134 ViscoplasticEvolutionHistory<Y>,
135 >,
136 solver: impl FirstOrderRootFinding<
137 FirstPiolaKirchhoffStress,
138 FirstPiolaKirchhoffTangentStiffness,
139 DeformationGradient,
140 >,
141 ) -> Result<
142 (
143 Times,
144 DeformationGradients,
145 ViscoplasticStateVariablesHistory<Y>,
146 ),
147 ConstitutiveError,
148 >;
149}
150
151impl<C, Y> ZerothOrderRoot<Y> for C
152where
153 C: ElasticViscoplastic<Y>,
154 Y: Differentiable + Tensor,
155{
156 fn root(
157 &self,
158 applied_load: AppliedLoad,
159 integrator: impl ExplicitDaeZerothOrderRoot<
160 FirstPiolaKirchhoffStress,
161 ViscoplasticStateVariables<Y>,
162 DeformationGradient,
163 ViscoplasticStateVariablesHistory<Y>,
164 DeformationGradients,
165 ViscoplasticEvolutionHistory<Y>,
166 >,
167 solver: impl ZerothOrderRootFinding<FirstPiolaKirchhoffStress, DeformationGradient>,
168 ) -> Result<
169 (
170 Times,
171 DeformationGradients,
172 ViscoplasticStateVariablesHistory<Y>,
173 ),
174 ConstitutiveError,
175 > {
176 let (matrix, prescribed, time) = bcs(applied_load);
177 let mut vector = Vector::zero(matrix.len());
178 let (times, state_variables, _, deformation_gradients) = integrator
179 .integrate(
180 |_: Quantity<Time>,
181 state_variables: &ViscoplasticStateVariables<Y>,
182 deformation_gradient: &DeformationGradient| {
183 Ok(self.state_variables_evolution(deformation_gradient, state_variables)?)
184 },
185 |_: Quantity<Time>,
186 state_variables: &ViscoplasticStateVariables<Y>,
187 deformation_gradient: &DeformationGradient| {
188 let deformation_gradient_p = &state_variables.0;
189 Ok(self.first_piola_kirchhoff_stress(
190 deformation_gradient,
191 deformation_gradient_p,
192 )?)
193 },
194 solver,
195 time,
196 (self.initial_state(), DeformationGradient::identity()),
197 |t: Quantity<Time>| {
198 prescribed
199 .iter()
200 .for_each(|(index, function)| vector[*index] = function(t));
201 EqualityConstraint::Linear(matrix.clone(), vector.clone())
202 },
203 )
204 .map_err(|error| ConstitutiveError::upstream(error, self))?;
205 Ok((times, deformation_gradients, state_variables))
206 }
207}
208
209impl<C, Y> FirstOrderRoot<Y> for C
210where
211 C: ElasticViscoplastic<Y>,
212 Y: Differentiable + Tensor,
213{
214 fn root(
215 &self,
216 applied_load: AppliedLoad,
217 integrator: impl ExplicitDaeFirstOrderRoot<
218 FirstPiolaKirchhoffStress,
219 FirstPiolaKirchhoffTangentStiffness,
220 ViscoplasticStateVariables<Y>,
221 DeformationGradient,
222 ViscoplasticStateVariablesHistory<Y>,
223 DeformationGradients,
224 ViscoplasticEvolutionHistory<Y>,
225 >,
226 solver: impl FirstOrderRootFinding<
227 FirstPiolaKirchhoffStress,
228 FirstPiolaKirchhoffTangentStiffness,
229 DeformationGradient,
230 >,
231 ) -> Result<
232 (
233 Times,
234 DeformationGradients,
235 ViscoplasticStateVariablesHistory<Y>,
236 ),
237 ConstitutiveError,
238 > {
239 let (matrix, prescribed, time) = bcs(applied_load);
240 let mut vector = Vector::zero(matrix.len());
241 let (times, state_variables, _, deformation_gradients) = integrator
242 .integrate(
243 |_: Quantity<Time>,
244 state_variables: &ViscoplasticStateVariables<Y>,
245 deformation_gradient: &DeformationGradient| {
246 Ok(self.state_variables_evolution(deformation_gradient, state_variables)?)
247 },
248 |_: Quantity<Time>,
249 state_variables: &ViscoplasticStateVariables<Y>,
250 deformation_gradient: &DeformationGradient| {
251 let deformation_gradient_p = &state_variables.0;
252 Ok(self.first_piola_kirchhoff_stress(
253 deformation_gradient,
254 deformation_gradient_p,
255 )?)
256 },
257 |_: Quantity<Time>,
258 state_variables: &ViscoplasticStateVariables<Y>,
259 deformation_gradient: &DeformationGradient| {
260 let deformation_gradient_p = &state_variables.0;
261 Ok(self.first_piola_kirchhoff_tangent_stiffness(
262 deformation_gradient,
263 deformation_gradient_p,
264 )?)
265 },
266 solver,
267 time,
268 (self.initial_state(), DeformationGradient::identity()),
269 |t: Quantity<Time>| {
270 prescribed
271 .iter()
272 .for_each(|(index, function)| vector[*index] = function(t));
273 EqualityConstraint::Linear(matrix.clone(), vector.clone())
274 },
275 )
276 .map_err(|error| ConstitutiveError::upstream(error, self))?;
277 Ok((times, deformation_gradients, state_variables))
278 }
279}
280
281impl<C, Y> StateEvolution<Time, Y> for C
292where
293 C: ElasticViscoplastic<Y>,
294 Y: Clone + Differentiable<Time> + Tensor,
295 for<'a> Y: Add<&'a Y, Output = Y>,
296 TensorTuple<TensorRank2<3, Intermediate, Intermediate>, Y>: Differentiable<
297 Time,
298 Derivative = TensorTuple<
299 TensorRank2<3, Intermediate, Intermediate, Rate>,
300 Derivative<Y>,
301 >,
302 >,
303{
304 type Field = Product<Unimodular<Intermediate, Reference>, Flat<Y>>;
305 type Drive = DeformationGradient;
306 fn initial_state(&self) -> ViscoplasticStateVariables<Y> {
307 <Self as Viscoplastic<Y>>::initial_state(self)
308 }
309 fn state_rate(
310 &self,
311 _time: Quantity<Time>,
312 deformation_gradient: &DeformationGradient,
313 state: &ViscoplasticStateVariables<Y>,
314 ) -> Result<Derivative<<Self::Field as Integrable>::Increment, Time>, String> {
315 let mandel_stress = self.mandel_stress(deformation_gradient, &state.0)?;
316 let evolution = self.plastic_evolution(mandel_stress, state)?;
317 let plastic_stretching_rate = evolution.0 * state.0.inverse();
318 Ok(TensorTuple(plastic_stretching_rate, evolution.1))
319 }
320}
321
322pub trait RootRkmkDae<Y>
329where
330 Y: Differentiable + Tensor,
331{
332 fn root_rkmk_dae<Tab: ButcherTableau>(
343 &self,
344 applied_load: AppliedLoad,
345 solver: impl FirstOrderRootFinding<
346 FirstPiolaKirchhoffStress,
347 FirstPiolaKirchhoffTangentStiffness,
348 DeformationGradient,
349 >,
350 ) -> Result<
351 (
352 Times,
353 DeformationGradients,
354 ViscoplasticStateVariablesHistory<Y>,
355 ),
356 ConstitutiveError,
357 >;
358 fn root_rkmk_dae_adaptive<Tab: EmbeddedTableau>(
370 &self,
371 applied_load: AppliedLoad,
372 solver: impl FirstOrderRootFinding<
373 FirstPiolaKirchhoffStress,
374 FirstPiolaKirchhoffTangentStiffness,
375 DeformationGradient,
376 >,
377 abs_tol: Scalar,
378 rel_tol: Scalar,
379 ) -> Result<
380 (
381 Times,
382 DeformationGradients,
383 ViscoplasticStateVariablesHistory<Y>,
384 ),
385 ConstitutiveError,
386 >;
387}
388
389impl<C, Y> RootRkmkDae<Y> for C
390where
391 C: ElasticViscoplastic<Y>
392 + StateEvolution<
393 Time,
394 Y,
395 Drive = DeformationGradient,
396 Field: Integrable<Point = ViscoplasticStateVariables<Y>>,
397 >,
398 Y: Differentiable + Tensor,
399 EvolvedIncrement<C, Time, Y>: Clone + Differentiable<Time>,
400 for<'a> &'a Derivative<EvolvedIncrement<C, Time, Y>, Time>:
401 Mul<Quantity<Time>, Output = EvolvedIncrement<C, Time, Y>>,
402{
403 #[allow(clippy::type_complexity)]
404 fn root_rkmk_dae<Tab: ButcherTableau>(
405 &self,
406 applied_load: AppliedLoad,
407 solver: impl FirstOrderRootFinding<
408 FirstPiolaKirchhoffStress,
409 FirstPiolaKirchhoffTangentStiffness,
410 DeformationGradient,
411 >,
412 ) -> Result<
413 (
414 Times,
415 DeformationGradients,
416 ViscoplasticStateVariablesHistory<Y>,
417 ),
418 ConstitutiveError,
419 > {
420 let (matrix, prescribed, time) = bcs(applied_load);
421 let mut state = <Self as StateEvolution<Time, Y>>::initial_state(self);
422 let mut scratch = Vec::new();
423 let equality_constraint = |t: Quantity<Time>| {
424 let mut vector = Vector::zero(matrix.len());
425 prescribed
426 .iter()
427 .for_each(|(index, function)| vector[*index] = function(t));
428 EqualityConstraint::Linear(matrix.clone(), vector)
429 };
430 let function = |_: Quantity<Time>,
431 state: &ViscoplasticStateVariables<Y>,
432 deformation_gradient: &DeformationGradient|
433 -> Result<FirstPiolaKirchhoffStress, String> {
434 Ok(self.first_piola_kirchhoff_stress(deformation_gradient, &state.0)?)
435 };
436 let jacobian = |_: Quantity<Time>,
437 state: &ViscoplasticStateVariables<Y>,
438 deformation_gradient: &DeformationGradient|
439 -> Result<FirstPiolaKirchhoffTangentStiffness, String> {
440 Ok(self.first_piola_kirchhoff_tangent_stiffness(deformation_gradient, &state.0)?)
441 };
442 let mut deformation_gradient = solver
443 .root(
444 |deformation_gradient: &DeformationGradient| {
445 function(time[0], &state, deformation_gradient)
446 },
447 |deformation_gradient: &DeformationGradient| {
448 jacobian(time[0], &state, deformation_gradient)
449 },
450 DeformationGradient::identity(),
451 equality_constraint(time[0]),
452 None,
453 )
454 .map_err(|error| ConstitutiveError::upstream(String::from(error), self))?;
455 let mut times = Times::new();
456 let mut deformation_gradients = DeformationGradients::new();
457 let mut state_variables = ViscoplasticStateVariablesHistory::new();
458 let mut carry = None;
459 times.push(time[0]);
460 deformation_gradients.push(deformation_gradient.clone());
461 state_variables.push(state.clone());
462 for step in time.windows(2) {
463 let advanced = rkmk_dae_step_first_order_root::<
464 <Self as StateEvolution<Time, Y>>::Field,
465 Tab,
466 FirstPiolaKirchhoffStress,
467 FirstPiolaKirchhoffTangentStiffness,
468 DeformationGradient,
469 Time,
470 >(
471 &mut |t, state, deformation_gradient| {
472 self.state_rate(t, deformation_gradient, state)
473 },
474 function,
475 jacobian,
476 &solver,
477 &state,
478 &deformation_gradient,
479 step[0],
480 step[1] - step[0],
481 &mut scratch,
482 carry.as_ref(),
483 equality_constraint,
484 )
485 .map_err(|error| ConstitutiveError::upstream(error, self))?;
486 state = advanced.0;
487 deformation_gradient = advanced.1;
488 carry = advanced.2;
489 times.push(step[1]);
490 deformation_gradients.push(deformation_gradient.clone());
491 state_variables.push(state.clone());
492 }
493 Ok((times, deformation_gradients, state_variables))
494 }
495 #[allow(clippy::type_complexity)]
496 fn root_rkmk_dae_adaptive<Tab: EmbeddedTableau>(
497 &self,
498 applied_load: AppliedLoad,
499 solver: impl FirstOrderRootFinding<
500 FirstPiolaKirchhoffStress,
501 FirstPiolaKirchhoffTangentStiffness,
502 DeformationGradient,
503 >,
504 abs_tol: Scalar,
505 rel_tol: Scalar,
506 ) -> Result<
507 (
508 Times,
509 DeformationGradients,
510 ViscoplasticStateVariablesHistory<Y>,
511 ),
512 ConstitutiveError,
513 > {
514 let (matrix, prescribed, time) = bcs(applied_load);
515 let state = <Self as StateEvolution<Time, Y>>::initial_state(self);
516 let equality_constraint = |t: Quantity<Time>| {
517 let mut vector = Vector::zero(matrix.len());
518 prescribed
519 .iter()
520 .for_each(|(index, function)| vector[*index] = function(t));
521 EqualityConstraint::Linear(matrix.clone(), vector)
522 };
523 let function = |_: Quantity<Time>,
524 state: &ViscoplasticStateVariables<Y>,
525 deformation_gradient: &DeformationGradient|
526 -> Result<FirstPiolaKirchhoffStress, String> {
527 Ok(self.first_piola_kirchhoff_stress(deformation_gradient, &state.0)?)
528 };
529 let jacobian = |_: Quantity<Time>,
530 state: &ViscoplasticStateVariables<Y>,
531 deformation_gradient: &DeformationGradient|
532 -> Result<FirstPiolaKirchhoffTangentStiffness, String> {
533 Ok(self.first_piola_kirchhoff_tangent_stiffness(deformation_gradient, &state.0)?)
534 };
535 let deformation_gradient = solver
536 .root(
537 |deformation_gradient: &DeformationGradient| {
538 function(time[0], &state, deformation_gradient)
539 },
540 |deformation_gradient: &DeformationGradient| {
541 jacobian(time[0], &state, deformation_gradient)
542 },
543 DeformationGradient::identity(),
544 equality_constraint(time[0]),
545 None,
546 )
547 .map_err(|error| ConstitutiveError::upstream(String::from(error), self))?;
548 let (times, state_variables, deformation_gradients) =
549 integrate_rkmk_dae_adaptive_first_order_root::<
550 <Self as StateEvolution<Time, Y>>::Field,
551 Tab,
552 FirstPiolaKirchhoffStress,
553 FirstPiolaKirchhoffTangentStiffness,
554 DeformationGradient,
555 ViscoplasticStateVariablesHistory<Y>,
556 DeformationGradients,
557 Time,
558 >(
559 |t, state, deformation_gradient| self.state_rate(t, deformation_gradient, state),
560 function,
561 jacobian,
562 &solver,
563 time,
564 (state, deformation_gradient),
565 abs_tol,
566 rel_tol,
567 equality_constraint,
568 )
569 .map_err(|error| ConstitutiveError::upstream(error, self))?;
570 Ok((times, deformation_gradients, state_variables))
571 }
572}