1use crate::{
2 fem::{
3 ElementModel, ElementModelError, Elements, Model, NodalCoordinates,
4 block::{
5 element::solid::elastic::internal_variables::InternalVariables,
6 finalize_node_neighbors, solver_from_neighbors,
7 },
8 solid::{NodalForcesSolid, NodalStiffnessesSolid},
9 },
10 math::{
11 Scalar, Tensor, TensorVector, Vector,
12 optimize::{
13 EqualityConstraint, FirstOrderRootFinding, FirstOrderRootFindingIncremental,
14 NewtonRaphson, OptimizationError, SolveStrategy,
15 },
16 },
17};
18use std::cell::{Ref, RefCell};
19
20pub type InternalVariablesField<const G: usize, V> = TensorVector<InternalVariables<G, V>>;
22
23pub trait ElasticIVElements<const G: usize, V, const D: usize>
24where
25 Self: Elements,
26 V: Tensor,
27{
28 fn internal_variables_initial(&self) -> InternalVariablesField<G, V>;
30 fn internal_variables_increment(
37 &self,
38 nodal_coordinates: &NodalCoordinates<D>,
39 internal_variables: &InternalVariablesField<G, V>,
40 nodal_decrement: &NodalCoordinates<D>,
41 step: Scalar,
42 ) -> Result<InternalVariablesField<G, V>, ElementModelError>;
43 fn internal_variables_root(
48 &self,
49 local_solver: &NewtonRaphson,
50 nodal_coordinates: &NodalCoordinates<D>,
51 internal_variables: &InternalVariablesField<G, V>,
52 ) -> Result<InternalVariablesField<G, V>, ElementModelError>;
53 fn nodal_forces_into(
54 &self,
55 nodal_coordinates: &NodalCoordinates<D>,
56 internal_variables: &InternalVariablesField<G, V>,
57 nodal_forces: &mut NodalForcesSolid<D>,
58 ) -> Result<(), ElementModelError>;
59 fn nodal_forces(
60 &self,
61 nodal_coordinates: &NodalCoordinates<D>,
62 internal_variables: &InternalVariablesField<G, V>,
63 ) -> Result<NodalForcesSolid<D>, ElementModelError> {
64 let mut nodal_forces = NodalForcesSolid::zero(nodal_coordinates.len());
65 self.nodal_forces_into(nodal_coordinates, internal_variables, &mut nodal_forces)?;
66 Ok(nodal_forces)
67 }
68 fn nodal_forces_eliminated_into(
75 &self,
76 nodal_coordinates: &NodalCoordinates<D>,
77 internal_variables: &InternalVariablesField<G, V>,
78 nodal_forces: &mut NodalForcesSolid<D>,
79 ) -> Result<(), ElementModelError>;
80 fn nodal_forces_eliminated(
81 &self,
82 nodal_coordinates: &NodalCoordinates<D>,
83 internal_variables: &InternalVariablesField<G, V>,
84 ) -> Result<NodalForcesSolid<D>, ElementModelError> {
85 let mut nodal_forces = NodalForcesSolid::zero(nodal_coordinates.len());
86 self.nodal_forces_eliminated_into(
87 nodal_coordinates,
88 internal_variables,
89 &mut nodal_forces,
90 )?;
91 Ok(nodal_forces)
92 }
93 fn nodal_stiffnesses_into(
94 &self,
95 nodal_coordinates: &NodalCoordinates<D>,
96 internal_variables: &InternalVariablesField<G, V>,
97 nodal_stiffnesses: &mut NodalStiffnessesSolid<D>,
98 ) -> Result<(), ElementModelError>;
99 fn nodal_stiffnesses(
100 &self,
101 nodal_coordinates: &NodalCoordinates<D>,
102 internal_variables: &InternalVariablesField<G, V>,
103 ) -> Result<NodalStiffnessesSolid<D>, ElementModelError> {
104 let mut nodal_stiffnesses = NodalStiffnessesSolid::zero(nodal_coordinates.len());
105 self.nodal_stiffnesses_into(
106 nodal_coordinates,
107 internal_variables,
108 &mut nodal_stiffnesses,
109 )?;
110 Ok(nodal_stiffnesses)
111 }
112}
113
114impl<B, const G: usize, V, const D: usize> ElasticIVElements<G, V, D> for Model<B, D>
115where
116 B: ElasticIVElements<G, V, D>,
117 V: Tensor,
118{
119 fn internal_variables_initial(&self) -> InternalVariablesField<G, V> {
120 self.blocks.internal_variables_initial()
121 }
122 fn internal_variables_increment(
123 &self,
124 nodal_coordinates: &NodalCoordinates<D>,
125 internal_variables: &InternalVariablesField<G, V>,
126 nodal_decrement: &NodalCoordinates<D>,
127 step: Scalar,
128 ) -> Result<InternalVariablesField<G, V>, ElementModelError> {
129 self.blocks.internal_variables_increment(
130 nodal_coordinates,
131 internal_variables,
132 nodal_decrement,
133 step,
134 )
135 }
136 fn internal_variables_root(
137 &self,
138 local_solver: &NewtonRaphson,
139 nodal_coordinates: &NodalCoordinates<D>,
140 internal_variables: &InternalVariablesField<G, V>,
141 ) -> Result<InternalVariablesField<G, V>, ElementModelError> {
142 self.blocks
143 .internal_variables_root(local_solver, nodal_coordinates, internal_variables)
144 }
145 fn nodal_forces_eliminated_into(
146 &self,
147 nodal_coordinates: &NodalCoordinates<D>,
148 internal_variables: &InternalVariablesField<G, V>,
149 nodal_forces: &mut NodalForcesSolid<D>,
150 ) -> Result<(), ElementModelError> {
151 self.blocks.nodal_forces_eliminated_into(
152 nodal_coordinates,
153 internal_variables,
154 nodal_forces,
155 )
156 }
157 fn nodal_forces_into(
158 &self,
159 nodal_coordinates: &NodalCoordinates<D>,
160 internal_variables: &InternalVariablesField<G, V>,
161 nodal_forces: &mut NodalForcesSolid<D>,
162 ) -> Result<(), ElementModelError> {
163 self.blocks
164 .nodal_forces_into(nodal_coordinates, internal_variables, nodal_forces)
165 }
166 fn nodal_stiffnesses_into(
167 &self,
168 nodal_coordinates: &NodalCoordinates<D>,
169 internal_variables: &InternalVariablesField<G, V>,
170 nodal_stiffnesses: &mut NodalStiffnessesSolid<D>,
171 ) -> Result<(), ElementModelError> {
172 self.blocks
173 .nodal_stiffnesses_into(nodal_coordinates, internal_variables, nodal_stiffnesses)
174 }
175}
176
177pub struct SolvedInternalVariables<'a, B, const G: usize, V, const D: usize>
184where
185 V: Tensor,
186{
187 initial: InternalVariablesField<G, V>,
188 local_solver: &'a NewtonRaphson,
189 model: &'a Model<B, D>,
190 solved: RefCell<Option<(NodalCoordinates<D>, InternalVariablesField<G, V>)>>,
191}
192
193impl<'a, B, const G: usize, V, const D: usize> SolvedInternalVariables<'a, B, G, V, D>
194where
195 B: ElasticIVElements<G, V, D>,
196 V: Tensor,
197{
198 pub fn new(
199 model: &'a Model<B, D>,
200 local_solver: &'a NewtonRaphson,
201 initial: InternalVariablesField<G, V>,
202 ) -> Self {
203 Self {
204 initial,
205 local_solver,
206 model,
207 solved: RefCell::new(None),
208 }
209 }
210 pub fn at(
211 &self,
212 nodal_coordinates: &NodalCoordinates<D>,
213 ) -> Result<InternalVariablesField<G, V>, ElementModelError> {
214 if let Some((ref at, ref variables)) = *self.solved.borrow()
215 && at == nodal_coordinates
216 {
217 return Ok(variables.clone());
218 }
219 let warm = match *self.solved.borrow() {
220 Some((_, ref variables)) => variables.clone(),
221 None => self.initial.clone(),
222 };
223 let variables =
224 self.model
225 .internal_variables_root(self.local_solver, nodal_coordinates, &warm)?;
226 *self.solved.borrow_mut() = Some((nodal_coordinates.clone(), variables.clone()));
227 Ok(variables)
228 }
229}
230
231pub struct CarriedInternalVariables<'a, B, const G: usize, V, const D: usize>
239where
240 V: Tensor,
241{
242 committed: RefCell<InternalVariablesField<G, V>>,
243 model: &'a Model<B, D>,
244 stepped: RefCell<InternalVariablesField<G, V>>,
245}
246
247impl<'a, B, const G: usize, V, const D: usize> CarriedInternalVariables<'a, B, G, V, D>
248where
249 B: ElasticIVElements<G, V, D>,
250 V: Tensor,
251{
252 pub fn new(model: &'a Model<B, D>, initial: InternalVariablesField<G, V>) -> Self {
253 Self {
254 committed: RefCell::new(initial.clone()),
255 model,
256 stepped: RefCell::new(initial),
257 }
258 }
259 pub fn stepped(&self) -> Ref<'_, InternalVariablesField<G, V>> {
260 self.stepped.borrow()
261 }
262 pub fn step(
263 &self,
264 nodal_coordinates: &NodalCoordinates<D>,
265 nodal_decrement: &Vector,
266 step: Scalar,
267 commit: bool,
268 ) -> Result<(), ElementModelError> {
269 let stepped = self.model.internal_variables_increment(
270 nodal_coordinates,
271 &self.committed.borrow(),
272 &NodalCoordinates::from(nodal_decrement.clone()),
273 step,
274 )?;
275 if commit {
276 *self.committed.borrow_mut() = stepped.clone()
277 }
278 *self.stepped.borrow_mut() = stepped;
279 Ok(())
280 }
281}
282
283pub trait FirstOrderRootIV<const G: usize, V, const D: usize>
286where
287 V: Tensor,
288{
289 fn root(
290 &self,
291 equality_constraint: EqualityConstraint,
292 solver: impl FirstOrderRootFinding<
293 NodalForcesSolid<D>,
294 NodalStiffnessesSolid<D>,
295 NodalCoordinates<D>,
296 > + FirstOrderRootFindingIncremental<
297 NodalForcesSolid<D>,
298 NodalStiffnessesSolid<D>,
299 NodalCoordinates<D>,
300 >,
301 strategy: SolveStrategy,
302 ) -> Result<NodalCoordinates<D>, OptimizationError>;
303}
304
305impl<B, const G: usize, V, const D: usize> FirstOrderRootIV<G, V, D> for Model<B, D>
306where
307 B: ElasticIVElements<G, V, D>,
308 V: Tensor,
309{
310 fn root(
311 &self,
312 equality_constraint: EqualityConstraint,
313 solver: impl FirstOrderRootFinding<
314 NodalForcesSolid<D>,
315 NodalStiffnessesSolid<D>,
316 NodalCoordinates<D>,
317 > + FirstOrderRootFindingIncremental<
318 NodalForcesSolid<D>,
319 NodalStiffnessesSolid<D>,
320 NodalCoordinates<D>,
321 >,
322 strategy: SolveStrategy,
323 ) -> Result<NodalCoordinates<D>, OptimizationError> {
324 let mut neighbors = vec![Vec::new(); self.coordinates().len()];
325 self.node_neighbors(&mut neighbors);
326 finalize_node_neighbors(&mut neighbors);
327 let sparse = solver_from_neighbors(&neighbors, &equality_constraint, D, false);
334 let initial = self.internal_variables_initial();
335 match strategy {
336 SolveStrategy::Condensed(ref local_solver) => {
342 let solved = SolvedInternalVariables::new(self, local_solver, initial);
343 solver.root(
344 |nodal_coordinates: &NodalCoordinates<D>| {
345 Ok(self.nodal_forces(nodal_coordinates, &solved.at(nodal_coordinates)?)?)
346 },
347 |nodal_coordinates: &NodalCoordinates<D>| {
348 Ok(self
349 .nodal_stiffnesses(nodal_coordinates, &solved.at(nodal_coordinates)?)?)
350 },
351 self.coordinates().clone().into(),
352 equality_constraint,
353 Some(sparse),
354 )
355 }
356 SolveStrategy::Monolithic { elimination: true } => {
363 let carried = CarriedInternalVariables::new(self, initial);
364 solver.root_incremental(
365 |nodal_coordinates: &NodalCoordinates<D>| {
366 Ok(self.nodal_forces_eliminated(nodal_coordinates, &carried.stepped())?)
367 },
368 |nodal_coordinates: &NodalCoordinates<D>| {
369 Ok(self.nodal_stiffnesses(nodal_coordinates, &carried.stepped())?)
370 },
371 |nodal_coordinates: &NodalCoordinates<D>,
372 decrement: &Vector,
373 step: Scalar,
374 commit: bool| {
375 Ok(carried.step(nodal_coordinates, decrement, step, commit)?)
376 },
377 self.coordinates().clone().into(),
378 equality_constraint,
379 Some(sparse),
380 )
381 }
382 SolveStrategy::Monolithic { elimination: false } => unimplemented!(
383 "The internal variables must be unknowns of the solver to be solved with it."
384 ),
385 }
386 }
387}