1use crate::{
2 domain::{
3 Blocks, ElasticViscoplasticAndElastic, ElementModel, ElementModelError, Model,
4 NodalCoordinates, NodalCoordinatesHistory,
5 block::element::Elements,
6 solid::{NodalForcesSolid, NodalStiffnessesSolid, elastic::ElasticElements},
7 },
8 math::{
9 Derivative, Differentiable, Quantity, Scalar, Tensor, TensorTuple, TensorTupleVec,
10 TensorVec, TensorVector,
11 integrate::{
12 ButcherTableau, EmbeddedTableau, Integrable, IntegrationError, Product,
13 integrate_rkmk_dae_adaptive_first_order_root, rkmk_dae_step_first_order_root,
14 },
15 optimize::{EqualityConstraint, FirstOrderRootFinding},
16 },
17 mechanics::Times,
18 units::Time,
19};
20use std::ops::Mul;
21
22pub type ElasticViscoplasticBCs = fn(Quantity<Time>) -> EqualityConstraint;
23
24pub trait ElasticViscoplasticElements<S, const D: usize>
25where
26 Self: Elements,
27 S: Differentiable,
28{
29 fn initial_state(&self) -> S;
30 fn nodal_forces_into(
31 &self,
32 nodal_coordinates: &NodalCoordinates<D>,
33 state_variables: &S,
34 nodal_forces: &mut NodalForcesSolid<D>,
35 ) -> Result<(), ElementModelError>;
36 fn nodal_forces(
37 &self,
38 nodal_coordinates: &NodalCoordinates<D>,
39 state_variables: &S,
40 ) -> Result<NodalForcesSolid<D>, ElementModelError> {
41 let mut nodal_forces = NodalForcesSolid::zero(nodal_coordinates.len());
42 self.nodal_forces_into(nodal_coordinates, state_variables, &mut nodal_forces)?;
43 Ok(nodal_forces)
44 }
45 fn nodal_stiffnesses_into(
46 &self,
47 nodal_coordinates: &NodalCoordinates<D>,
48 state_variables: &S,
49 nodal_stiffnesses: &mut NodalStiffnessesSolid<D>,
50 ) -> Result<(), ElementModelError>;
51 fn nodal_stiffnesses(
52 &self,
53 nodal_coordinates: &NodalCoordinates<D>,
54 state_variables: &S,
55 ) -> Result<NodalStiffnessesSolid<D>, ElementModelError> {
56 let mut nodal_stiffnesses = NodalStiffnessesSolid::zero(nodal_coordinates.len());
57 self.nodal_stiffnesses_into(nodal_coordinates, state_variables, &mut nodal_stiffnesses)?;
58 Ok(nodal_stiffnesses)
59 }
60 fn state_variables_evolution(
61 &self,
62 nodal_coordinates: &NodalCoordinates<D>,
63 state_variables: &S,
64 ) -> Result<Derivative<S>, ElementModelError>;
65}
66
67impl<B, S, const D: usize> ElasticViscoplasticElements<S, D> for Model<B, D>
68where
69 B: ElasticViscoplasticElements<S, D>,
70 S: Differentiable,
71{
72 fn initial_state(&self) -> S {
73 self.blocks.initial_state()
74 }
75 fn nodal_forces_into(
76 &self,
77 nodal_coordinates: &NodalCoordinates<D>,
78 state_variables: &S,
79 nodal_forces: &mut NodalForcesSolid<D>,
80 ) -> Result<(), ElementModelError> {
81 self.blocks
82 .nodal_forces_into(nodal_coordinates, state_variables, nodal_forces)
83 }
84 fn nodal_stiffnesses_into(
85 &self,
86 nodal_coordinates: &NodalCoordinates<D>,
87 state_variables: &S,
88 nodal_stiffnesses: &mut NodalStiffnessesSolid<D>,
89 ) -> Result<(), ElementModelError> {
90 self.blocks
91 .nodal_stiffnesses_into(nodal_coordinates, state_variables, nodal_stiffnesses)
92 }
93 fn state_variables_evolution(
94 &self,
95 nodal_coordinates: &NodalCoordinates<D>,
96 state_variables: &S,
97 ) -> Result<Derivative<S>, ElementModelError> {
98 self.blocks
99 .state_variables_evolution(nodal_coordinates, state_variables)
100 }
101}
102
103impl<B1, B2, S, const D: usize> ElasticViscoplasticElements<S, D>
104 for ElasticViscoplasticAndElastic<B1, B2>
105where
106 B1: ElasticViscoplasticElements<S, D>,
107 B2: ElasticElements<D>,
108 S: Differentiable,
109{
110 fn initial_state(&self) -> S {
111 self.0.initial_state()
112 }
113 fn nodal_forces_into(
114 &self,
115 nodal_coordinates: &NodalCoordinates<D>,
116 state_variables: &S,
117 nodal_forces: &mut NodalForcesSolid<D>,
118 ) -> Result<(), ElementModelError> {
119 self.0
120 .nodal_forces_into(nodal_coordinates, state_variables, nodal_forces)?;
121 self.1.nodal_forces_into(nodal_coordinates, nodal_forces)
122 }
123 fn nodal_stiffnesses_into(
124 &self,
125 nodal_coordinates: &NodalCoordinates<D>,
126 state_variables: &S,
127 nodal_stiffnesses: &mut NodalStiffnessesSolid<D>,
128 ) -> Result<(), ElementModelError> {
129 self.0
130 .nodal_stiffnesses_into(nodal_coordinates, state_variables, nodal_stiffnesses)?;
131 self.1
132 .nodal_stiffnesses_into(nodal_coordinates, nodal_stiffnesses)
133 }
134 fn state_variables_evolution(
135 &self,
136 nodal_coordinates: &NodalCoordinates<D>,
137 state_variables: &S,
138 ) -> Result<Derivative<S>, ElementModelError> {
139 self.0
140 .state_variables_evolution(nodal_coordinates, state_variables)
141 }
142}
143
144impl<B1, B2, S1, S2, const D: usize> ElasticViscoplasticElements<TensorTuple<S1, S2>, D>
145 for Blocks<B1, B2>
146where
147 B1: ElasticViscoplasticElements<S1, D>,
148 B2: ElasticViscoplasticElements<S2, D>,
149 S1: Differentiable + Tensor,
150 S2: Differentiable + Tensor,
151 Derivative<S1>: Tensor,
152 Derivative<S2>: Tensor,
153{
154 fn initial_state(&self) -> TensorTuple<S1, S2> {
155 (self.0.initial_state(), self.1.initial_state()).into()
156 }
157 fn nodal_forces_into(
158 &self,
159 nodal_coordinates: &NodalCoordinates<D>,
160 state_variables: &TensorTuple<S1, S2>,
161 nodal_forces: &mut NodalForcesSolid<D>,
162 ) -> Result<(), ElementModelError> {
163 self.0
164 .nodal_forces_into(nodal_coordinates, &state_variables.0, nodal_forces)?;
165 self.1
166 .nodal_forces_into(nodal_coordinates, &state_variables.1, nodal_forces)
167 }
168 fn nodal_stiffnesses_into(
169 &self,
170 nodal_coordinates: &NodalCoordinates<D>,
171 state_variables: &TensorTuple<S1, S2>,
172 nodal_stiffnesses: &mut NodalStiffnessesSolid<D>,
173 ) -> Result<(), ElementModelError> {
174 self.0
175 .nodal_stiffnesses_into(nodal_coordinates, &state_variables.0, nodal_stiffnesses)?;
176 self.1
177 .nodal_stiffnesses_into(nodal_coordinates, &state_variables.1, nodal_stiffnesses)
178 }
179 fn state_variables_evolution(
180 &self,
181 nodal_coordinates: &NodalCoordinates<D>,
182 state_variables: &TensorTuple<S1, S2>,
183 ) -> Result<Derivative<TensorTuple<S1, S2>>, ElementModelError> {
184 Ok((
185 self.0
186 .state_variables_evolution(nodal_coordinates, &state_variables.0)?,
187 self.1
188 .state_variables_evolution(nodal_coordinates, &state_variables.1)?,
189 )
190 .into())
191 }
192}
193
194pub trait RootRkmkDae<const D: usize, Y = Quantity> {
203 type History;
205 fn root_rkmk_dae<Tab: ButcherTableau>(
209 &self,
210 solver: impl FirstOrderRootFinding<
211 NodalForcesSolid<D>,
212 NodalStiffnessesSolid<D>,
213 NodalCoordinates<D>,
214 >,
215 time: &[Quantity<Time>],
216 bcs: ElasticViscoplasticBCs,
217 ) -> Result<(Times, NodalCoordinatesHistory<D>, Self::History), IntegrationError>;
218 fn root_rkmk_dae_adaptive<Tab: EmbeddedTableau>(
229 &self,
230 solver: impl FirstOrderRootFinding<
231 NodalForcesSolid<D>,
232 NodalStiffnessesSolid<D>,
233 NodalCoordinates<D>,
234 >,
235 time: &[Quantity<Time>],
236 bcs: ElasticViscoplasticBCs,
237 abs_tol: Scalar,
238 rel_tol: Scalar,
239 ) -> Result<(Times, NodalCoordinatesHistory<D>, Self::History), IntegrationError>;
240}
241
242pub trait ElasticViscoplasticDaeElements<Y, const D: usize>
252where
253 Self: Elements,
254{
255 type Field: Integrable<Increment: Differentiable<Time>>;
257 type State: Clone + Differentiable + Tensor;
260 type History: TensorVec<Item = Self::State>;
262 fn flatten(state: &Self::State) -> <Self::Field as Integrable>::Point;
264 fn unflatten(flat: &<Self::Field as Integrable>::Point) -> Self::State;
266 fn dae_rate(
268 &self,
269 t: Quantity<Time>,
270 nodal_coordinates: &NodalCoordinates<D>,
271 flat: &<Self::Field as Integrable>::Point,
272 ) -> Result<Derivative<<Self::Field as Integrable>::Increment, Time>, ElementModelError>;
273}
274
275impl<B1, B2, Y, const D: usize> ElasticViscoplasticDaeElements<Y, D> for Blocks<B1, B2>
276where
277 B1: ElasticViscoplasticDaeElements<Y, D>,
278 B2: ElasticViscoplasticDaeElements<Y, D>,
279 TensorTuple<<B1::Field as Integrable>::Point, <B2::Field as Integrable>::Point>: Tensor,
280 TensorTuple<<B1::Field as Integrable>::Increment, <B2::Field as Integrable>::Increment>: Tensor,
281 TensorTuple<B1::State, B2::State>: Clone + Differentiable + Tensor,
282 Derivative<B1::State>: Tensor,
283 Derivative<B2::State>: Tensor,
284 TensorTupleVec<B1::State, B2::State>: TensorVec<Item = TensorTuple<B1::State, B2::State>>,
285{
286 type Field = Product<B1::Field, B2::Field>;
287 type State = TensorTuple<B1::State, B2::State>;
288 type History = TensorTupleVec<B1::State, B2::State>;
289 fn flatten(state: &Self::State) -> <Self::Field as Integrable>::Point {
290 TensorTuple(B1::flatten(&state.0), B2::flatten(&state.1))
291 }
292 fn unflatten(flat: &<Self::Field as Integrable>::Point) -> Self::State {
293 TensorTuple(B1::unflatten(&flat.0), B2::unflatten(&flat.1))
294 }
295 fn dae_rate(
296 &self,
297 t: Quantity<Time>,
298 nodal_coordinates: &NodalCoordinates<D>,
299 flat: &<Self::Field as Integrable>::Point,
300 ) -> Result<Derivative<<Self::Field as Integrable>::Increment, Time>, ElementModelError> {
301 Ok((
302 self.0.dae_rate(t, nodal_coordinates, &flat.0)?,
303 self.1.dae_rate(t, nodal_coordinates, &flat.1)?,
304 )
305 .into())
306 }
307}
308
309impl<B1, B2, Y, const D: usize> ElasticViscoplasticDaeElements<Y, D>
310 for ElasticViscoplasticAndElastic<B1, B2>
311where
312 B1: ElasticViscoplasticDaeElements<Y, D>,
313 B2: ElasticElements<D>,
314{
315 type Field = B1::Field;
316 type State = B1::State;
317 type History = B1::History;
318 fn flatten(state: &Self::State) -> <Self::Field as Integrable>::Point {
319 B1::flatten(state)
320 }
321 fn unflatten(flat: &<Self::Field as Integrable>::Point) -> Self::State {
322 B1::unflatten(flat)
323 }
324 fn dae_rate(
325 &self,
326 t: Quantity<Time>,
327 nodal_coordinates: &NodalCoordinates<D>,
328 flat: &<Self::Field as Integrable>::Point,
329 ) -> Result<Derivative<<Self::Field as Integrable>::Increment, Time>, ElementModelError> {
330 self.0.dae_rate(t, nodal_coordinates, flat)
331 }
332}
333
334impl<B, Y> RootRkmkDae<3, Y> for Model<B, 3>
335where
336 B: ElasticViscoplasticDaeElements<Y, 3> + ElasticViscoplasticElements<B::State, 3>,
337 <B::Field as Integrable>::Point: Clone,
338 <B::Field as Integrable>::Increment: Clone + Differentiable<Time>,
339 for<'a> &'a Derivative<<B::Field as Integrable>::Increment, Time>:
340 Mul<Quantity<Time>, Output = <B::Field as Integrable>::Increment>,
341 Derivative<<B::Field as Integrable>::Increment, Time>:
342 Mul<Quantity<Time>, Output = <B::Field as Integrable>::Increment>,
343 B::State: Clone,
344 B::History: TensorVec<Item = B::State>,
345{
346 type History = B::History;
347 #[allow(clippy::type_complexity)]
348 fn root_rkmk_dae<Tab: ButcherTableau>(
349 &self,
350 solver: impl FirstOrderRootFinding<
351 NodalForcesSolid<3>,
352 NodalStiffnessesSolid<3>,
353 NodalCoordinates<3>,
354 >,
355 time: &[Quantity<Time>],
356 bcs: ElasticViscoplasticBCs,
357 ) -> Result<(Times, NodalCoordinatesHistory<3>, Self::History), IntegrationError> {
358 let blocks = self.blocks();
359 let function = |_: Quantity<Time>,
360 state: &<B::Field as Integrable>::Point,
361 nodal_coordinates: &NodalCoordinates<3>|
362 -> Result<NodalForcesSolid<3>, String> {
363 Ok(blocks.nodal_forces(nodal_coordinates, &B::unflatten(state))?)
364 };
365 let jacobian = |_: Quantity<Time>,
366 state: &<B::Field as Integrable>::Point,
367 nodal_coordinates: &NodalCoordinates<3>|
368 -> Result<NodalStiffnessesSolid<3>, String> {
369 Ok(blocks.nodal_stiffnesses(nodal_coordinates, &B::unflatten(state))?)
370 };
371 let rate =
372 |t: Quantity<Time>,
373 state: &<B::Field as Integrable>::Point,
374 nodal_coordinates: &NodalCoordinates<3>|
375 -> Result<Derivative<<B::Field as Integrable>::Increment, Time>, String> {
376 Ok(blocks.dae_rate(t, nodal_coordinates, state)?)
377 };
378 let equality_constraint = bcs;
379 let mut state = B::flatten(&ElasticViscoplasticElements::initial_state(blocks));
380 let guess: NodalCoordinates<3> = self.coordinates().clone().into();
381 let mut nodal_coordinates = solver
382 .root(
383 |x: &NodalCoordinates<3>| function(time[0], &state, x),
384 |x: &NodalCoordinates<3>| jacobian(time[0], &state, x),
385 guess,
386 equality_constraint(time[0]),
387 None,
388 )
389 .map_err(|error| IntegrationError::from(format!("{error:?}")))?;
390 let mut times = Times::new();
391 let mut nodal_coordinates_history = NodalCoordinatesHistory::new();
392 let mut state_variables_history = Self::History::new();
393 let mut scratch = Vec::new();
394 let mut carry = None;
395 times.push(time[0]);
396 nodal_coordinates_history.push(nodal_coordinates.clone());
397 state_variables_history.push(B::unflatten(&state));
398 for step in time.windows(2) {
399 let advanced = rkmk_dae_step_first_order_root::<
400 B::Field,
401 Tab,
402 NodalForcesSolid<3>,
403 NodalStiffnessesSolid<3>,
404 NodalCoordinates<3>,
405 Time,
406 >(
407 &mut |t, state, nodal_coordinates| rate(t, state, nodal_coordinates),
408 function,
409 jacobian,
410 &solver,
411 &state,
412 &nodal_coordinates,
413 step[0],
414 step[1] - step[0],
415 &mut scratch,
416 carry.as_ref(),
417 equality_constraint,
418 )
419 .map_err(|error| IntegrationError::from(format!("{error:?}")))?;
420 state = advanced.0;
421 nodal_coordinates = advanced.1;
422 carry = advanced.2;
423 times.push(step[1]);
424 nodal_coordinates_history.push(nodal_coordinates.clone());
425 state_variables_history.push(B::unflatten(&state));
426 }
427 Ok((times, nodal_coordinates_history, state_variables_history))
428 }
429 #[allow(clippy::type_complexity)]
430 fn root_rkmk_dae_adaptive<Tab: EmbeddedTableau>(
431 &self,
432 solver: impl FirstOrderRootFinding<
433 NodalForcesSolid<3>,
434 NodalStiffnessesSolid<3>,
435 NodalCoordinates<3>,
436 >,
437 time: &[Quantity<Time>],
438 bcs: ElasticViscoplasticBCs,
439 abs_tol: Scalar,
440 rel_tol: Scalar,
441 ) -> Result<(Times, NodalCoordinatesHistory<3>, Self::History), IntegrationError> {
442 let blocks = self.blocks();
443 let function = |_: Quantity<Time>,
444 state: &<B::Field as Integrable>::Point,
445 nodal_coordinates: &NodalCoordinates<3>|
446 -> Result<NodalForcesSolid<3>, String> {
447 Ok(blocks.nodal_forces(nodal_coordinates, &B::unflatten(state))?)
448 };
449 let jacobian = |_: Quantity<Time>,
450 state: &<B::Field as Integrable>::Point,
451 nodal_coordinates: &NodalCoordinates<3>|
452 -> Result<NodalStiffnessesSolid<3>, String> {
453 Ok(blocks.nodal_stiffnesses(nodal_coordinates, &B::unflatten(state))?)
454 };
455 let rate =
456 |t: Quantity<Time>,
457 state: &<B::Field as Integrable>::Point,
458 nodal_coordinates: &NodalCoordinates<3>|
459 -> Result<Derivative<<B::Field as Integrable>::Increment, Time>, String> {
460 Ok(blocks.dae_rate(t, nodal_coordinates, state)?)
461 };
462 let equality_constraint = bcs;
463 let state = B::flatten(&ElasticViscoplasticElements::initial_state(blocks));
464 let guess: NodalCoordinates<3> = self.coordinates().clone().into();
465 let nodal_coordinates = solver
466 .root(
467 |x: &NodalCoordinates<3>| function(time[0], &state, x),
468 |x: &NodalCoordinates<3>| jacobian(time[0], &state, x),
469 guess,
470 equality_constraint(time[0]),
471 None,
472 )
473 .map_err(|error| IntegrationError::from(format!("{error:?}")))?;
474 let (times, state_points_history, nodal_coordinates_history) =
475 integrate_rkmk_dae_adaptive_first_order_root::<
476 B::Field,
477 Tab,
478 NodalForcesSolid<3>,
479 NodalStiffnessesSolid<3>,
480 NodalCoordinates<3>,
481 TensorVector<<B::Field as Integrable>::Point>,
482 NodalCoordinatesHistory<3>,
483 Time,
484 >(
485 |t, state, nodal_coordinates| rate(t, state, nodal_coordinates),
486 function,
487 jacobian,
488 &solver,
489 time,
490 (state, nodal_coordinates),
491 abs_tol,
492 rel_tol,
493 equality_constraint,
494 )
495 .map_err(|error| IntegrationError::from(format!("{error:?}")))?;
496 let state_variables_history = state_points_history.iter().map(B::unflatten).collect();
497 Ok((times, nodal_coordinates_history, state_variables_history))
498 }
499}