1use crate::{
4 constitutive::{
5 ConstitutiveError,
6 solid::{Solid, elastic::AppliedLoad},
7 },
8 math::{
9 ContractFirstSecondIndicesWithSecondIndicesOf, ContractSecondIndexWithFirstIndexOf,
10 IDENTITY, Matrix, Rank2, Tensor, TensorArray, TensorTuple, Vector,
11 optimize::{EqualityConstraint, FirstOrderRootFinding, ZerothOrderRootFinding},
12 },
13 mechanics::{
14 CauchyStress, CauchyTangentStiffness, DeformationGradient, FirstPiolaKirchhoffStress,
15 FirstPiolaKirchhoffTangentStiffness, SecondPiolaKirchhoffStress,
16 SecondPiolaKirchhoffTangentStiffness,
17 },
18};
19
20pub trait ElasticIV<V, T1, T2, T3>
22where
23 Self: Solid,
24{
25 fn cauchy_stress(
31 &self,
32 deformation_gradient: &DeformationGradient,
33 internal_variables: &V,
34 ) -> Result<CauchyStress, ConstitutiveError> {
35 Ok(deformation_gradient
36 * self.second_piola_kirchhoff_stress(deformation_gradient, internal_variables)?
37 * deformation_gradient.transpose()
38 / deformation_gradient.determinant())
39 }
40 fn cauchy_tangent_stiffness(
46 &self,
47 deformation_gradient: &DeformationGradient,
48 internal_variables: &V,
49 ) -> Result<CauchyTangentStiffness, ConstitutiveError> {
50 let deformation_gradient_inverse_transpose = deformation_gradient.inverse_transpose();
51 let cauchy_stress = self.cauchy_stress(deformation_gradient, internal_variables)?;
52 let some_stress = &cauchy_stress * &deformation_gradient_inverse_transpose;
53 Ok(self
54 .second_piola_kirchhoff_tangent_stiffness(deformation_gradient, internal_variables)?
55 .contract_first_second_indices_with_second_indices_of(
56 deformation_gradient,
57 deformation_gradient,
58 )
59 / deformation_gradient.determinant()
60 - CauchyTangentStiffness::dyad_ij_kl(
61 &cauchy_stress,
62 &deformation_gradient_inverse_transpose,
63 )
64 + CauchyTangentStiffness::dyad_il_kj(&some_stress, &IDENTITY)
65 + CauchyTangentStiffness::dyad_ik_jl(&IDENTITY, &some_stress))
66 }
67 fn first_piola_kirchhoff_stress(
73 &self,
74 deformation_gradient: &DeformationGradient,
75 internal_variables: &V,
76 ) -> Result<FirstPiolaKirchhoffStress, ConstitutiveError> {
77 Ok(
78 self.cauchy_stress(deformation_gradient, internal_variables)?
79 * deformation_gradient.inverse_transpose()
80 * deformation_gradient.determinant(),
81 )
82 }
83 fn first_piola_kirchhoff_tangent_stiffness(
89 &self,
90 deformation_gradient: &DeformationGradient,
91 internal_variables: &V,
92 ) -> Result<FirstPiolaKirchhoffTangentStiffness, ConstitutiveError> {
93 let deformation_gradient_inverse_transpose = deformation_gradient.inverse_transpose();
94 let first_piola_kirchhoff_stress =
95 self.first_piola_kirchhoff_stress(deformation_gradient, internal_variables)?;
96 Ok(self
97 .cauchy_tangent_stiffness(deformation_gradient, internal_variables)?
98 .contract_second_index_with_first_index_of(&deformation_gradient_inverse_transpose)
99 * deformation_gradient.determinant()
100 + FirstPiolaKirchhoffTangentStiffness::dyad_ij_kl(
101 &first_piola_kirchhoff_stress,
102 &deformation_gradient_inverse_transpose,
103 )
104 - FirstPiolaKirchhoffTangentStiffness::dyad_il_kj(
105 &first_piola_kirchhoff_stress,
106 &deformation_gradient_inverse_transpose,
107 ))
108 }
109 fn second_piola_kirchhoff_stress(
115 &self,
116 deformation_gradient: &DeformationGradient,
117 internal_variables: &V,
118 ) -> Result<SecondPiolaKirchhoffStress, ConstitutiveError> {
119 Ok(deformation_gradient.inverse()
120 * self.first_piola_kirchhoff_stress(deformation_gradient, internal_variables)?)
121 }
122 fn second_piola_kirchhoff_tangent_stiffness(
128 &self,
129 deformation_gradient: &DeformationGradient,
130 internal_variables: &V,
131 ) -> Result<SecondPiolaKirchhoffTangentStiffness, ConstitutiveError> {
132 let deformation_gradient_inverse_transpose = deformation_gradient.inverse_transpose();
133 let deformation_gradient_inverse = deformation_gradient_inverse_transpose.transpose();
134 let second_piola_kirchhoff_stress =
135 self.second_piola_kirchhoff_stress(deformation_gradient, internal_variables)?;
136 Ok(self
137 .cauchy_tangent_stiffness(deformation_gradient, internal_variables)?
138 .contract_first_second_indices_with_second_indices_of(
139 &deformation_gradient_inverse,
140 &deformation_gradient_inverse,
141 )
142 * deformation_gradient.determinant()
143 + SecondPiolaKirchhoffTangentStiffness::dyad_ij_kl(
144 &second_piola_kirchhoff_stress,
145 &deformation_gradient_inverse_transpose,
146 )
147 - SecondPiolaKirchhoffTangentStiffness::dyad_il_kj(
148 &second_piola_kirchhoff_stress,
149 &deformation_gradient_inverse_transpose,
150 )
151 - SecondPiolaKirchhoffTangentStiffness::dyad_ik_jl(
152 &deformation_gradient_inverse,
153 &second_piola_kirchhoff_stress,
154 ))
155 }
156 fn internal_variables_initial(&self) -> V;
158 fn internal_variables_residual(
160 &self,
161 deformation_gradient: &DeformationGradient,
162 internal_variables: &V,
163 ) -> Result<V, ConstitutiveError>;
164 fn internal_variables_tangents(
166 &self,
167 deformation_gradient: &DeformationGradient,
168 internal_variables: &V,
169 ) -> Result<(T1, T2, T3), ConstitutiveError>;
170 fn internal_variables_constraints(&self) -> (&[usize], usize);
172}
173
174pub trait ZerothOrderRoot<V, T1, T2, T3>
176where
177 V: Tensor,
178{
179 type Variables;
181 fn root(
187 &self,
188 applied_load: AppliedLoad,
189 solver: impl ZerothOrderRootFinding<Self::Variables>,
190 ) -> Result<(DeformationGradient, V), ConstitutiveError>;
191}
192
193pub trait FirstOrderRoot<V, T1, T2, T3>
195where
196 T1: Tensor,
197 T2: Tensor,
198 T3: Tensor,
199 V: Tensor,
200{
201 type Variables;
203 fn root(
209 &self,
210 applied_load: AppliedLoad,
211 solver: impl FirstOrderRootFinding<
212 Self::Variables,
213 TensorTuple<FirstPiolaKirchhoffTangentStiffness, TensorTuple<T1, TensorTuple<T2, T3>>>,
214 Self::Variables,
215 >,
216 ) -> Result<(DeformationGradient, V), ConstitutiveError>;
217}
218
219impl<T, V, T1, T2, T3> ZerothOrderRoot<V, T1, T2, T3> for T
220where
221 T: ElasticIV<V, T1, T2, T3>,
222 V: Tensor,
223{
224 type Variables = TensorTuple<DeformationGradient, V>;
225 fn root(
226 &self,
227 applied_load: AppliedLoad,
228 solver: impl ZerothOrderRootFinding<Self::Variables>,
229 ) -> Result<(DeformationGradient, V), ConstitutiveError> {
230 let (matrix, vector) = bcs(self, applied_load);
231 match solver.root(
232 |variables: &Self::Variables| {
233 let (deformation_gradient, internal_variables) = variables.into();
234 Ok(TensorTuple::from((
235 self.first_piola_kirchhoff_stress(deformation_gradient, internal_variables)?,
236 self.internal_variables_residual(deformation_gradient, internal_variables)?,
237 )))
238 },
239 Self::Variables::from((
240 DeformationGradient::identity(),
241 self.internal_variables_initial(),
242 )),
243 EqualityConstraint::Linear(matrix, vector),
244 ) {
245 Ok(solution) => Ok(solution.into()),
246 Err(error) => Err(ConstitutiveError::Upstream(
247 format!("{error}"),
248 format!("{self:?}"),
249 )),
250 }
251 }
252}
253
254impl<T, V, T1, T2, T3> FirstOrderRoot<V, T1, T2, T3> for T
255where
256 T1: Tensor,
257 T2: Tensor,
258 T3: Tensor,
259 T: ElasticIV<V, T1, T2, T3>,
260 V: Tensor,
261{
262 type Variables = TensorTuple<DeformationGradient, V>;
263 fn root(
264 &self,
265 applied_load: AppliedLoad,
266 solver: impl FirstOrderRootFinding<
267 Self::Variables,
268 TensorTuple<FirstPiolaKirchhoffTangentStiffness, TensorTuple<T1, TensorTuple<T2, T3>>>,
269 Self::Variables,
270 >,
271 ) -> Result<(DeformationGradient, V), ConstitutiveError> {
272 let (matrix, vector) = bcs(self, applied_load);
273 match solver.root(
274 |variables: &Self::Variables| {
275 let (deformation_gradient, internal_variables) = variables.into();
276 Ok(TensorTuple::from((
277 self.first_piola_kirchhoff_stress(deformation_gradient, internal_variables)?,
278 self.internal_variables_residual(deformation_gradient, internal_variables)?,
279 )))
280 },
281 |variables: &Self::Variables| {
282 let (deformation_gradient, internal_variables) = variables.into();
283 let tangent_0 = self.first_piola_kirchhoff_tangent_stiffness(
284 deformation_gradient,
285 internal_variables,
286 )?;
287 let (tangent_1, tangent_2, tangent_3) =
288 self.internal_variables_tangents(deformation_gradient, internal_variables)?;
289 Ok((tangent_0, (tangent_1, (tangent_2, tangent_3).into()).into()).into())
290 },
291 Self::Variables::from((
292 DeformationGradient::identity(),
293 self.internal_variables_initial(),
294 )),
295 EqualityConstraint::Linear(matrix, vector),
296 ) {
297 Ok(solution) => Ok(solution.into()),
298 Err(error) => Err(ConstitutiveError::Upstream(
299 format!("{error}"),
300 format!("{self:?}"),
301 )),
302 }
303 }
304}
305
306#[doc(hidden)]
307pub fn bcs<C, V, T1, T2, T3>(model: &C, applied_load: AppliedLoad) -> (Matrix, Vector)
308where
309 C: ElasticIV<V, T1, T2, T3>,
310{
311 let (extra_constraints, num_vars) = model.internal_variables_constraints();
312 match applied_load {
313 AppliedLoad::UniaxialStress(deformation_gradient_11) => {
314 let num_constraints = 4;
315 let num_constraints_vars = extra_constraints.len();
316 let mut matrix = Matrix::zero(num_constraints + num_constraints_vars, 9 + num_vars);
317 let mut vector = Vector::zero(num_constraints + num_constraints_vars);
318 matrix[0][0] = 1.0;
319 matrix[1][1] = 1.0;
320 matrix[2][2] = 1.0;
321 matrix[3][5] = 1.0;
322 let mut i = num_constraints;
323 extra_constraints.iter().for_each(|&j| {
324 matrix[i][j] = 1.0;
325 i += 1;
326 });
327 vector[0] = deformation_gradient_11;
328 (matrix, vector)
329 }
330 AppliedLoad::BiaxialStress(_, _) => {
331 todo!()
332 }
333 }
334}