1use crate::{
4 constitutive::{ConstitutiveError, fluid::plastic::Plastic, solid::Solid},
5 math::{
6 ContractFirstSecondWithSecond, ContractSecondWithFirst, IDENTITY, Matrix, Quantity, Rank2,
7 TensorArray, TensorRank2, TensorRank4,
8 },
9 mechanics::{
10 CauchyStress, CauchyTangentStiffness, CauchyTangentStiffnessPlastic, DeformationGradient,
11 DeformationGradientPlastic, FirstPiolaKirchhoffStress, FirstPiolaKirchhoffTangentStiffness,
12 FirstPiolaKirchhoffTangentStiffnessPlastic, MandelStressElastic,
13 MandelStressTangentElastic, MandelStressTangentElasticPlastic, Scalar,
14 SecondPiolaKirchhoffStress, SecondPiolaKirchhoffTangentStiffness,
15 },
16 units::Time,
17};
18use std::array::from_fn;
19
20pub enum AppliedLoad<'a> {
22 UniaxialStress(fn(Quantity<Time>) -> Scalar, &'a [Quantity<Time>]),
24 BiaxialStress(
26 fn(Quantity<Time>) -> Scalar,
27 fn(Quantity<Time>) -> Scalar,
28 &'a [Quantity<Time>],
29 ),
30}
31
32type Prescribed = Vec<(usize, fn(Quantity<Time>) -> Scalar)>;
33
34#[doc(hidden)]
35pub fn bcs(applied_load: AppliedLoad<'_>) -> (Matrix, Prescribed, &'_ [Quantity<Time>]) {
36 let (mut matrix, prescribed, time) = match applied_load {
37 AppliedLoad::UniaxialStress(deformation_gradient_11, time) => {
38 (Matrix::zero(4, 9), vec![(0, deformation_gradient_11)], time)
39 }
40 AppliedLoad::BiaxialStress(deformation_gradient_11, deformation_gradient_22, time) => (
41 Matrix::zero(5, 9),
42 vec![(0, deformation_gradient_11), (4, deformation_gradient_22)],
43 time,
44 ),
45 };
46 matrix[0][0] = 1.0;
47 matrix[1][1] = 1.0;
48 matrix[2][2] = 1.0;
49 matrix[3][5] = 1.0;
50 if matrix.len() == 5 {
51 matrix[4][4] = 1.0
52 }
53 (matrix, prescribed, time)
54}
55
56pub trait ElasticPlasticOrViscoplastic
58where
59 Self: Solid + Plastic,
60{
61 fn cauchy_stress(
67 &self,
68 deformation_gradient: &DeformationGradient,
69 deformation_gradient_p: &DeformationGradientPlastic,
70 ) -> Result<CauchyStress, ConstitutiveError> {
71 Ok(deformation_gradient
72 * self.second_piola_kirchhoff_stress(deformation_gradient, deformation_gradient_p)?
73 * deformation_gradient.transpose()
74 / deformation_gradient.determinant())
75 }
76 fn cauchy_tangent_stiffness(
82 &self,
83 deformation_gradient: &DeformationGradient,
84 deformation_gradient_p: &DeformationGradientPlastic,
85 ) -> Result<CauchyTangentStiffness, ConstitutiveError> {
86 let deformation_gradient_inverse_transpose = deformation_gradient.inverse_transpose();
87 let cauchy_stress = self.cauchy_stress(deformation_gradient, deformation_gradient_p)?;
88 let some_stress = &cauchy_stress * &deformation_gradient_inverse_transpose;
89 Ok(self
90 .second_piola_kirchhoff_tangent_stiffness(deformation_gradient, deformation_gradient_p)?
91 .contract_first_second_with_second(deformation_gradient, deformation_gradient)
92 / deformation_gradient.determinant()
93 - CauchyTangentStiffness::dyad_ij_kl(
94 &cauchy_stress,
95 &deformation_gradient_inverse_transpose,
96 )
97 + CauchyTangentStiffness::dyad_il_kj(&some_stress, &IDENTITY)
98 + CauchyTangentStiffness::dyad_ik_jl(&IDENTITY, &some_stress))
99 }
100 fn first_piola_kirchhoff_stress(
106 &self,
107 deformation_gradient: &DeformationGradient,
108 deformation_gradient_p: &DeformationGradientPlastic,
109 ) -> Result<FirstPiolaKirchhoffStress, ConstitutiveError> {
110 Ok(
111 self.cauchy_stress(deformation_gradient, deformation_gradient_p)?
112 * deformation_gradient.inverse_transpose()
113 * deformation_gradient.determinant(),
114 )
115 }
116 fn first_piola_kirchhoff_tangent_stiffness(
122 &self,
123 deformation_gradient: &DeformationGradient,
124 deformation_gradient_p: &DeformationGradientPlastic,
125 ) -> Result<FirstPiolaKirchhoffTangentStiffness, ConstitutiveError> {
126 let deformation_gradient_inverse_transpose = deformation_gradient.inverse_transpose();
127 let first_piola_kirchhoff_stress =
128 self.first_piola_kirchhoff_stress(deformation_gradient, deformation_gradient_p)?;
129 Ok(self
130 .cauchy_tangent_stiffness(deformation_gradient, deformation_gradient_p)?
131 .contract_second_with_first(&deformation_gradient_inverse_transpose)
132 * deformation_gradient.determinant()
133 + FirstPiolaKirchhoffTangentStiffness::dyad_ij_kl(
134 &first_piola_kirchhoff_stress,
135 &deformation_gradient_inverse_transpose,
136 )
137 - FirstPiolaKirchhoffTangentStiffness::dyad_il_kj(
138 &first_piola_kirchhoff_stress,
139 &deformation_gradient_inverse_transpose,
140 ))
141 }
142 fn mandel_stress(
148 &self,
149 deformation_gradient: &DeformationGradient,
150 deformation_gradient_p: &DeformationGradientPlastic,
151 ) -> Result<MandelStressElastic, ConstitutiveError> {
152 let jacobian = self.jacobian(deformation_gradient)?;
153 let deformation_gradient_e = deformation_gradient * deformation_gradient_p.inverse();
154 let cauchy_stress = self.cauchy_stress(deformation_gradient, deformation_gradient_p)?;
155 Ok((deformation_gradient_e.transpose()
156 * cauchy_stress
157 * deformation_gradient_e.inverse_transpose())
158 * jacobian)
159 }
160 fn second_piola_kirchhoff_stress(
166 &self,
167 deformation_gradient: &DeformationGradient,
168 deformation_gradient_p: &DeformationGradientPlastic,
169 ) -> Result<SecondPiolaKirchhoffStress, ConstitutiveError> {
170 Ok(deformation_gradient.inverse()
171 * self.first_piola_kirchhoff_stress(deformation_gradient, deformation_gradient_p)?)
172 }
173 fn second_piola_kirchhoff_tangent_stiffness(
179 &self,
180 deformation_gradient: &DeformationGradient,
181 deformation_gradient_p: &DeformationGradientPlastic,
182 ) -> Result<SecondPiolaKirchhoffTangentStiffness, ConstitutiveError> {
183 let deformation_gradient_inverse_transpose = deformation_gradient.inverse_transpose();
184 let deformation_gradient_inverse = deformation_gradient_inverse_transpose.transpose();
185 let second_piola_kirchhoff_stress =
186 self.second_piola_kirchhoff_stress(deformation_gradient, deformation_gradient_p)?;
187 Ok(self
188 .cauchy_tangent_stiffness(deformation_gradient, deformation_gradient_p)?
189 .contract_first_second_with_second(
190 &deformation_gradient_inverse,
191 &deformation_gradient_inverse,
192 )
193 * deformation_gradient.determinant()
194 + SecondPiolaKirchhoffTangentStiffness::dyad_ij_kl(
195 &second_piola_kirchhoff_stress,
196 &deformation_gradient_inverse_transpose,
197 )
198 - SecondPiolaKirchhoffTangentStiffness::dyad_il_kj(
199 &second_piola_kirchhoff_stress,
200 &deformation_gradient_inverse_transpose,
201 )
202 - SecondPiolaKirchhoffTangentStiffness::dyad_ik_jl(
203 &deformation_gradient_inverse,
204 &second_piola_kirchhoff_stress,
205 ))
206 }
207}
208
209pub(crate) type Matrix3 = [[Scalar; 3]; 3];
210pub(crate) type Entries4 = [[[[Scalar; 3]; 3]; 3]; 3];
211
212pub(crate) fn matrix_3<I, J, U>(tensor: &TensorRank2<3, I, J, U>) -> Matrix3 {
213 from_fn(|i| from_fn(|j| tensor[i][j].value()))
214}
215
216pub(crate) fn entries_4<I, J, K, L, U>(tensor: &TensorRank4<3, I, J, K, L, U>) -> Entries4 {
217 from_fn(|i| from_fn(|j| from_fn(|k| from_fn(|l| tensor[i][j][k][l].value()))))
218}
219
220pub(crate) fn rank_4<I, J, K, L, U>(entries: &Entries4) -> TensorRank4<3, I, J, K, L, U> {
221 let mut tensor = TensorRank4::zero();
222 (0..3).for_each(|i| {
223 (0..3).for_each(|j| {
224 (0..3).for_each(|k| {
225 (0..3).for_each(|l| tensor[i][j][k][l] = Quantity::new(entries[i][j][k][l]))
226 })
227 })
228 });
229 tensor
230}
231
232#[allow(clippy::too_many_arguments)]
238fn mandel_stress_tangent_entries(
239 jacobian: Scalar,
240 mandel_stress: &Matrix3,
241 cauchy_stress: &Matrix3,
242 deformation_gradient_e: &Matrix3,
243 deformation_gradient_e_inverse: &Matrix3,
244 deformation_gradient_p_inverse: &Matrix3,
245 g: &Matrix3,
246 d_jacobian: Option<&Matrix3>,
247 d_cauchy_stress: &Entries4,
248) -> Entries4 {
249 let w: Matrix3 = from_fn(|a| {
250 from_fn(|j| {
251 (0..3)
252 .map(|k| {
253 (0..3)
254 .map(|l| {
255 g[k][a] * cauchy_stress[k][l] * deformation_gradient_e_inverse[j][l]
256 })
257 .sum::<Scalar>()
258 })
259 .sum()
260 })
261 });
262 let v: Matrix3 = from_fn(|j| {
263 from_fn(|a| {
264 (0..3)
265 .map(|m| deformation_gradient_e_inverse[j][m] * g[m][a])
266 .sum()
267 })
268 });
269 let u: Matrix3 = from_fn(|i| {
270 from_fn(|b| {
271 (0..3)
272 .map(|n| mandel_stress[i][n] * deformation_gradient_p_inverse[b][n])
273 .sum()
274 })
275 });
276 from_fn(|i| {
277 from_fn(|j| {
278 from_fn(|a| {
279 from_fn(|b| {
280 let elastic = (0..3)
281 .map(|k| {
282 (0..3)
283 .map(|l| {
284 deformation_gradient_e[k][i]
285 * d_cauchy_stress[k][l][a][b]
286 * deformation_gradient_e_inverse[j][l]
287 })
288 .sum::<Scalar>()
289 })
290 .sum::<Scalar>();
291 d_jacobian.map_or(0.0, |d_jacobian| d_jacobian[a][b] * mandel_stress[i][j])
292 + jacobian * (deformation_gradient_p_inverse[b][i] * w[a][j] + elastic)
293 - u[i][b] * v[j][a]
294 })
295 })
296 })
297 })
298}
299
300pub trait PlasticTangents
304where
305 Self: ElasticPlasticOrViscoplastic,
306{
307 fn cauchy_tangent_stiffness_p(
314 &self,
315 deformation_gradient: &DeformationGradient,
316 deformation_gradient_p: &DeformationGradientPlastic,
317 ) -> Result<CauchyTangentStiffnessPlastic, ConstitutiveError>;
318 fn first_piola_kirchhoff_tangent_stiffness_p(
325 &self,
326 deformation_gradient: &DeformationGradient,
327 deformation_gradient_p: &DeformationGradientPlastic,
328 ) -> Result<FirstPiolaKirchhoffTangentStiffnessPlastic, ConstitutiveError>;
329 fn mandel_stress_tangent(
332 &self,
333 deformation_gradient: &DeformationGradient,
334 deformation_gradient_p: &DeformationGradientPlastic,
335 ) -> Result<MandelStressTangentElastic, ConstitutiveError> {
336 let deformation_gradient_p_inverse = deformation_gradient_p.inverse();
337 let deformation_gradient_e = deformation_gradient * &deformation_gradient_p_inverse;
338 Ok(rank_4(&mandel_stress_tangent_entries(
339 self.jacobian(deformation_gradient)?,
340 &matrix_3(&self.mandel_stress(deformation_gradient, deformation_gradient_p)?),
341 &matrix_3(&self.cauchy_stress(deformation_gradient, deformation_gradient_p)?),
342 &matrix_3(&deformation_gradient_e),
343 &matrix_3(&deformation_gradient_e.inverse()),
344 &matrix_3(&deformation_gradient_p_inverse),
345 &matrix_3(&IDENTITY),
346 Some(&matrix_3(&deformation_gradient.inverse_transpose())),
347 &entries_4(
348 &self.cauchy_tangent_stiffness(deformation_gradient, deformation_gradient_p)?,
349 ),
350 )))
351 }
352 fn mandel_stress_tangent_p(
355 &self,
356 deformation_gradient: &DeformationGradient,
357 deformation_gradient_p: &DeformationGradientPlastic,
358 ) -> Result<MandelStressTangentElasticPlastic, ConstitutiveError> {
359 let deformation_gradient_p_inverse = deformation_gradient_p.inverse();
360 let deformation_gradient_e = deformation_gradient * &deformation_gradient_p_inverse;
361 Ok(rank_4(&mandel_stress_tangent_entries(
362 self.jacobian(deformation_gradient)?,
363 &matrix_3(&self.mandel_stress(deformation_gradient, deformation_gradient_p)?),
364 &matrix_3(&self.cauchy_stress(deformation_gradient, deformation_gradient_p)?),
365 &matrix_3(&deformation_gradient_e),
366 &matrix_3(&deformation_gradient_e.inverse()),
367 &matrix_3(&deformation_gradient_p_inverse),
368 &matrix_3(&(&deformation_gradient_e * -1.0)),
369 None,
370 &entries_4(
371 &self.cauchy_tangent_stiffness_p(deformation_gradient, deformation_gradient_p)?,
372 ),
373 )))
374 }
375}
376
377pub trait ElasticPlastic
379where
380 Self: ElasticPlasticOrViscoplastic,
381{
382}