1use crate::{
4 constitutive::{
5 ConstitutiveError,
6 solid::{Solid, elastic::AppliedLoad},
7 },
8 math::{
9 ContractFirstSecondWithSecond, ContractSecondWithFirst, Hessian, HessianBlock, IDENTITY,
10 Jacobian, Matrix, Rank2, Tensor, TensorArray, TensorTuple, Vector,
11 optimize::{
12 EqualityConstraint, FirstOrderRootFindingBlock, SolveStrategy, ZerothOrderRootFinding,
13 },
14 sparse::CscMatrix,
15 },
16 mechanics::{
17 CauchyStress, CauchyTangentStiffness, DeformationGradient, FirstPiolaKirchhoffStress,
18 FirstPiolaKirchhoffTangentStiffness, SecondPiolaKirchhoffStress,
19 SecondPiolaKirchhoffTangentStiffness,
20 },
21};
22
23pub type Tangents<C, V> = (
25 FirstPiolaKirchhoffTangentStiffness,
26 <C as ElasticIV<V>>::TangentVu,
27 <C as ElasticIV<V>>::TangentUv,
28 <C as ElasticIV<V>>::TangentVv,
29);
30
31pub trait ElasticIV<V>
33where
34 Self: Solid,
35{
36 type Residual: Jacobian;
39 type TangentVu: HessianBlock;
41 type TangentUv: HessianBlock;
43 type TangentVv: Hessian + HessianBlock;
45 fn cauchy_stress(
51 &self,
52 deformation_gradient: &DeformationGradient,
53 internal_variables: &V,
54 ) -> Result<CauchyStress, ConstitutiveError> {
55 Ok(deformation_gradient
56 * self.second_piola_kirchhoff_stress(deformation_gradient, internal_variables)?
57 * deformation_gradient.transpose()
58 / deformation_gradient.determinant())
59 }
60 fn cauchy_tangent_stiffness(
66 &self,
67 deformation_gradient: &DeformationGradient,
68 internal_variables: &V,
69 ) -> Result<CauchyTangentStiffness, ConstitutiveError> {
70 let deformation_gradient_inverse_transpose = deformation_gradient.inverse_transpose();
71 let cauchy_stress = self.cauchy_stress(deformation_gradient, internal_variables)?;
72 let some_stress = &cauchy_stress * &deformation_gradient_inverse_transpose;
73 Ok(self
74 .second_piola_kirchhoff_tangent_stiffness(deformation_gradient, internal_variables)?
75 .contract_first_second_with_second(deformation_gradient, deformation_gradient)
76 / deformation_gradient.determinant()
77 - CauchyTangentStiffness::dyad_ij_kl(
78 &cauchy_stress,
79 &deformation_gradient_inverse_transpose,
80 )
81 + CauchyTangentStiffness::dyad_il_kj(&some_stress, &IDENTITY)
82 + CauchyTangentStiffness::dyad_ik_jl(&IDENTITY, &some_stress))
83 }
84 fn first_piola_kirchhoff_stress(
90 &self,
91 deformation_gradient: &DeformationGradient,
92 internal_variables: &V,
93 ) -> Result<FirstPiolaKirchhoffStress, ConstitutiveError> {
94 Ok(
95 self.cauchy_stress(deformation_gradient, internal_variables)?
96 * deformation_gradient.inverse_transpose()
97 * deformation_gradient.determinant(),
98 )
99 }
100 fn first_piola_kirchhoff_tangent_stiffness(
106 &self,
107 deformation_gradient: &DeformationGradient,
108 internal_variables: &V,
109 ) -> Result<FirstPiolaKirchhoffTangentStiffness, ConstitutiveError> {
110 let deformation_gradient_inverse_transpose = deformation_gradient.inverse_transpose();
111 let first_piola_kirchhoff_stress =
112 self.first_piola_kirchhoff_stress(deformation_gradient, internal_variables)?;
113 Ok(self
114 .cauchy_tangent_stiffness(deformation_gradient, internal_variables)?
115 .contract_second_with_first(&deformation_gradient_inverse_transpose)
116 * deformation_gradient.determinant()
117 + FirstPiolaKirchhoffTangentStiffness::dyad_ij_kl(
118 &first_piola_kirchhoff_stress,
119 &deformation_gradient_inverse_transpose,
120 )
121 - FirstPiolaKirchhoffTangentStiffness::dyad_il_kj(
122 &first_piola_kirchhoff_stress,
123 &deformation_gradient_inverse_transpose,
124 ))
125 }
126 fn second_piola_kirchhoff_stress(
132 &self,
133 deformation_gradient: &DeformationGradient,
134 internal_variables: &V,
135 ) -> Result<SecondPiolaKirchhoffStress, ConstitutiveError> {
136 Ok(deformation_gradient.inverse()
137 * self.first_piola_kirchhoff_stress(deformation_gradient, internal_variables)?)
138 }
139 fn second_piola_kirchhoff_tangent_stiffness(
145 &self,
146 deformation_gradient: &DeformationGradient,
147 internal_variables: &V,
148 ) -> Result<SecondPiolaKirchhoffTangentStiffness, ConstitutiveError> {
149 let deformation_gradient_inverse_transpose = deformation_gradient.inverse_transpose();
150 let deformation_gradient_inverse = deformation_gradient_inverse_transpose.transpose();
151 let second_piola_kirchhoff_stress =
152 self.second_piola_kirchhoff_stress(deformation_gradient, internal_variables)?;
153 Ok(self
154 .cauchy_tangent_stiffness(deformation_gradient, internal_variables)?
155 .contract_first_second_with_second(
156 &deformation_gradient_inverse,
157 &deformation_gradient_inverse,
158 )
159 * deformation_gradient.determinant()
160 + SecondPiolaKirchhoffTangentStiffness::dyad_ij_kl(
161 &second_piola_kirchhoff_stress,
162 &deformation_gradient_inverse_transpose,
163 )
164 - SecondPiolaKirchhoffTangentStiffness::dyad_il_kj(
165 &second_piola_kirchhoff_stress,
166 &deformation_gradient_inverse_transpose,
167 )
168 - SecondPiolaKirchhoffTangentStiffness::dyad_ik_jl(
169 &deformation_gradient_inverse,
170 &second_piola_kirchhoff_stress,
171 ))
172 }
173 fn internal_variables_initial(&self) -> V;
175 fn internal_variables_residual(
177 &self,
178 deformation_gradient: &DeformationGradient,
179 internal_variables: &V,
180 ) -> Result<Self::Residual, ConstitutiveError>;
181 fn internal_variables_fixed(&self) -> &[usize];
183 fn tangents(
185 &self,
186 deformation_gradient: &DeformationGradient,
187 internal_variables: &V,
188 ) -> Result<Tangents<Self, V>, ConstitutiveError>;
189}
190
191pub trait ZerothOrderRoot<V>
193where
194 V: Tensor,
195{
196 type Residuals;
198 type Variables;
200 fn root(
206 &self,
207 applied_load: AppliedLoad,
208 solver: impl ZerothOrderRootFinding<Self::Residuals, Self::Variables>,
209 ) -> Result<(DeformationGradient, V), ConstitutiveError>;
210}
211
212pub trait FirstOrderRoot<V>
214where
215 Self: ElasticIV<V>,
216 V: Tensor,
217{
218 fn root(
224 &self,
225 applied_load: AppliedLoad,
226 solver: impl FirstOrderRootFindingBlock<
227 DeformationGradient,
228 V,
229 FirstPiolaKirchhoffStress,
230 <Self as ElasticIV<V>>::Residual,
231 FirstPiolaKirchhoffTangentStiffness,
232 Self::TangentVu,
233 Self::TangentUv,
234 Self::TangentVv,
235 >,
236 strategy: SolveStrategy,
237 ) -> Result<(DeformationGradient, V), ConstitutiveError>;
238}
239
240impl<T, V> ZerothOrderRoot<V> for T
241where
242 T: ElasticIV<V>,
243 V: Tensor,
244{
245 type Residuals = TensorTuple<FirstPiolaKirchhoffStress, <T as ElasticIV<V>>::Residual>;
246 type Variables = TensorTuple<DeformationGradient, V>;
247 fn root(
248 &self,
249 applied_load: AppliedLoad,
250 solver: impl ZerothOrderRootFinding<Self::Residuals, Self::Variables>,
251 ) -> Result<(DeformationGradient, V), ConstitutiveError> {
252 let (matrix, vector) = bcs(self, applied_load);
253 match solver.root(
254 |variables: &Self::Variables| {
255 let (deformation_gradient, internal_variables) = variables.into();
256 Ok(TensorTuple::from((
257 self.first_piola_kirchhoff_stress(deformation_gradient, internal_variables)?,
258 self.internal_variables_residual(deformation_gradient, internal_variables)?,
259 )))
260 },
261 Self::Variables::from((
262 DeformationGradient::identity(),
263 self.internal_variables_initial(),
264 )),
265 EqualityConstraint::Linear(matrix, vector),
266 ) {
267 Ok(solution) => Ok(solution.into()),
268 Err(error) => Err(ConstitutiveError::Upstream(
269 format!("{error}"),
270 format!("{self:?}"),
271 )),
272 }
273 }
274}
275
276impl<T, V> FirstOrderRoot<V> for T
277where
278 T: ElasticIV<V>,
279 V: Tensor,
280{
281 fn root(
282 &self,
283 applied_load: AppliedLoad,
284 solver: impl FirstOrderRootFindingBlock<
285 DeformationGradient,
286 V,
287 FirstPiolaKirchhoffStress,
288 <Self as ElasticIV<V>>::Residual,
289 FirstPiolaKirchhoffTangentStiffness,
290 Self::TangentVu,
291 Self::TangentUv,
292 Self::TangentVv,
293 >,
294 strategy: SolveStrategy,
295 ) -> Result<(DeformationGradient, V), ConstitutiveError> {
296 let (constraint_global, constraint_local) = bcs_block(self, applied_load);
297 match solver.root_block(
298 |deformation_gradient: &DeformationGradient, internal_variables: &V| {
299 Ok(self.first_piola_kirchhoff_stress(deformation_gradient, internal_variables)?)
300 },
301 |deformation_gradient: &DeformationGradient, internal_variables: &V| {
302 Ok(self.internal_variables_residual(deformation_gradient, internal_variables)?)
303 },
304 |deformation_gradient: &DeformationGradient, internal_variables: &V| {
305 Ok(self.tangents(deformation_gradient, internal_variables)?)
306 },
307 (
308 DeformationGradient::identity(),
309 self.internal_variables_initial(),
310 ),
311 constraint_global,
312 constraint_local,
313 None,
314 strategy,
315 ) {
316 Ok(solution) => Ok(solution),
317 Err(error) => Err(ConstitutiveError::Upstream(
318 format!("{error}"),
319 format!("{self:?}"),
320 )),
321 }
322 }
323}
324
325#[doc(hidden)]
326pub fn bcs_block<C, V>(
327 model: &C,
328 applied_load: AppliedLoad,
329) -> ((CscMatrix, Vector), (CscMatrix, Vector))
330where
331 C: ElasticIV<V>,
332 V: Tensor,
333{
334 let fixed = model.internal_variables_fixed();
335 let num_internal_variables = model.internal_variables_initial().size();
336 let pattern_vars = fixed.iter().enumerate().map(|(i, &j)| (i, j)).collect();
337 let mut matrix_vars =
338 CscMatrix::from_pattern(fixed.len(), num_internal_variables, pattern_vars);
339 matrix_vars.fill(|_, _| 1.0);
340 let local = (matrix_vars, Vector::zero(fixed.len()));
341 let (vector, pattern) = match applied_load {
342 AppliedLoad::UniaxialStress(deformation_gradient_11) => {
343 let mut vector = Vector::zero(4);
344 vector[0] = deformation_gradient_11;
345 (vector, vec![(0, 0), (1, 1), (2, 2), (3, 5)])
346 }
347 AppliedLoad::BiaxialStress(deformation_gradient_11, deformation_gradient_22) => {
348 let mut vector = Vector::zero(5);
349 vector[0] = deformation_gradient_11;
350 vector[4] = deformation_gradient_22;
351 (vector, vec![(0, 0), (1, 1), (2, 2), (3, 5), (4, 4)])
352 }
353 };
354 let mut matrix = CscMatrix::from_pattern(vector.len(), 9, pattern);
355 matrix.fill(|_, _| 1.0);
356 ((matrix, vector), local)
357}
358
359#[doc(hidden)]
360pub fn bcs<C, V>(model: &C, applied_load: AppliedLoad) -> (Matrix, Vector)
361where
362 C: ElasticIV<V>,
363 V: Tensor,
364{
365 let fixed = model.internal_variables_fixed();
366 let num_internal_variables = model.internal_variables_initial().size();
367 let num_deformation_gradient = 9;
368 let (num_constraints, prescribed) = match applied_load {
369 AppliedLoad::UniaxialStress(deformation_gradient_11) => (
370 4,
371 vec![
372 (0, 0, deformation_gradient_11),
373 (1, 1, 0.0),
374 (2, 2, 0.0),
375 (3, 5, 0.0),
376 ],
377 ),
378 AppliedLoad::BiaxialStress(deformation_gradient_11, deformation_gradient_22) => (
379 5,
380 vec![
381 (0, 0, deformation_gradient_11),
382 (1, 1, 0.0),
383 (2, 2, 0.0),
384 (3, 5, 0.0),
385 (4, 4, deformation_gradient_22),
386 ],
387 ),
388 };
389 let mut matrix = Matrix::zero(
390 num_constraints + fixed.len(),
391 num_deformation_gradient + num_internal_variables,
392 );
393 let mut vector = Vector::zero(num_constraints + fixed.len());
394 prescribed.iter().for_each(|&(row, column, value)| {
395 matrix[row][column] = 1.0;
396 vector[row] = value
397 });
398 fixed
399 .iter()
400 .enumerate()
401 .for_each(|(i, &j)| matrix[num_constraints + i][num_deformation_gradient + j] = 1.0);
402 (matrix, vector)
403}