1#[cfg(test)]
2mod test;
3
4pub mod element;
5pub mod solid;
6pub mod surface;
7pub mod thermal;
8
9use crate::{
10 constitutive::Constitutive,
11 defeat_message,
12 fem::{
13 NodalReferenceCoordinates,
14 block::element::{ElementNodalReferenceCoordinates, FiniteElement},
15 },
16 math::{
17 Banded, InverseSets, Scalar, Scalars, Sets, Tensor, TestError, disjoint_set_union,
18 optimize::{
19 EqualityConstraint, FirstOrderOptimization, FirstOrderRootFinding, OptimizationError,
20 SecondOrderOptimization, ZerothOrderRootFinding,
21 },
22 },
23 mechanics::{CoordinateList, Coordinates},
24};
25use std::{
26 any::type_name,
27 array::from_fn,
28 fmt::{self, Debug, Display, Formatter},
29 iter::repeat_n,
30};
31
32pub type Connectivity<const N: usize> = Vec<[usize; N]>;
33pub type Graph<const N: usize> = Sets<Vec<[usize; N]>, [usize; N], usize, Vec<usize>, usize>;
34
35pub struct Block<C, F, const G: usize, const M: usize, const N: usize, const P: usize> {
36 constitutive_model: C,
37 connectivity: Graph<N>,
38 coordinates: NodalReferenceCoordinates,
39 elements: Vec<F>,
40}
41
42impl<C, F, const G: usize, const M: usize, const N: usize, const P: usize> Block<C, F, G, M, N, P>
43where
44 F: FiniteElement<G, M, N, P>,
45{
46 fn constitutive_model(&self) -> &C {
47 &self.constitutive_model
48 }
49 fn connectivity(&self) -> &Connectivity<N> {
50 self.connectivity.members()
51 }
52 fn coordinates(&self) -> &NodalReferenceCoordinates {
53 &self.coordinates
54 }
55 fn elements(&self) -> &[F] {
56 &self.elements
57 }
58 fn element_coordinates<const I: usize>(
59 coordinates: &Coordinates<I>,
60 nodes: &[usize; N],
61 ) -> CoordinateList<I, N> {
62 nodes
63 .iter()
64 .map(|&node| coordinates[node].clone())
65 .collect()
66 }
67 pub fn minimum_scaled_jacobians<const I: usize>(
68 &self,
69 coordinates: &Coordinates<I>,
70 ) -> Scalars {
71 self.connectivity()
72 .iter()
73 .map(|nodes| F::minimum_scaled_jacobian(Self::element_coordinates(coordinates, nodes)))
74 .collect()
75 }
76 pub fn volume(&self) -> Scalar {
77 self.elements().iter().map(|element| element.volume()).sum()
78 }
79}
80
81impl<C, F, const G: usize, const M: usize, const N: usize, const P: usize> Debug
82 for Block<C, F, G, M, N, P>
83where
84 F: FiniteElement<G, M, N, P>,
85{
86 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
87 write!(
88 f,
89 "Block {{ constitutive model: {}, {} elements }}",
90 type_name::<C>()
91 .rsplit("::")
92 .next()
93 .unwrap()
94 .split("<")
95 .next()
96 .unwrap(),
97 self.elements().len()
98 )
99 }
100}
101
102pub trait FiniteElementBlock<C, F, const G: usize, const N: usize>
103where
104 Self: From<(C, Connectivity<N>, NodalReferenceCoordinates)>,
105{
106 fn isolate(self, elements: &[usize]) -> Vec<(Self, [Vec<usize>; 3])>;
107 fn reset(&mut self);
108}
109
110impl<C, F, const G: usize, const N: usize, const P: usize>
111 From<(C, Connectivity<N>, NodalReferenceCoordinates)> for Block<C, F, G, 3, N, P>
112where
113 F: FiniteElement<G, 3, N, P> + From<ElementNodalReferenceCoordinates<N>>,
114{
115 fn from(
116 (constitutive_model, connectivity, coordinates): (
117 C,
118 Connectivity<N>,
119 NodalReferenceCoordinates,
120 ),
121 ) -> Self {
122 let elements = connectivity
123 .iter()
124 .map(|nodes| Self::element_coordinates(&coordinates, nodes).into())
125 .collect();
126 let connectivity = connectivity.into();
127 Self {
128 constitutive_model,
129 connectivity,
130 coordinates,
131 elements,
132 }
133 }
134}
135
136impl<C, F, const G: usize, const N: usize, const P: usize> FiniteElementBlock<C, F, G, N>
137 for Block<C, F, G, 3, N, P>
138where
139 C: Constitutive,
140 F: Default + FiniteElement<G, 3, N, P> + From<ElementNodalReferenceCoordinates<N>>,
141{
142 fn isolate(self, isolated_elements: &[usize]) -> Vec<(Self, [Vec<usize>; 3])> {
143 let (graph, map) = self.connectivity.inverse();
144 let (_, node_elements) = graph.into();
145 let (_, element_nodes) = self.connectivity.into();
146 let isolated_element_nodes: Connectivity<N> = isolated_elements
147 .iter()
148 .map(|&isolated_element| element_nodes[isolated_element])
149 .collect();
150 disjoint_set_union(&isolated_element_nodes, self.coordinates.len())
151 .into_iter()
152 .map(|isolated_nodes| {
153 let mut block_elements = isolated_nodes
154 .iter()
155 .flat_map(|&node| node_elements[map[node]].iter().copied())
156 .collect::<Vec<_>>();
157 block_elements.sort_unstable();
158 block_elements.dedup();
159 let mut global_nodes = block_elements
160 .iter()
161 .flat_map(|&element| element_nodes[element])
162 .collect::<Vec<_>>();
163 global_nodes.sort_unstable();
164 global_nodes.dedup();
165 let constitutive_model = self.constitutive_model.clone();
166 let mut node_num = 0;
167 let mut local_nodes = vec![0; global_nodes.iter().max().unwrap() + 1];
168 let coordinates = global_nodes
169 .iter()
170 .map(|&node| {
171 local_nodes[node] = node_num;
172 node_num += 1;
173 self.coordinates[node].clone()
174 })
175 .collect();
176 let connectivity = block_elements
177 .iter()
178 .map(|&element| from_fn(|node| local_nodes[element_nodes[element][node]]))
179 .collect::<Vec<_>>()
180 .into();
181 let elements = block_elements
182 .into_iter()
183 .map(|element| self.elements[element].clone())
184 .collect();
185 let mut global_boundary_nodes = global_nodes.clone();
186 global_boundary_nodes.retain(|node| isolated_nodes.binary_search(node).is_err());
187 let boundary_nodes = global_boundary_nodes
188 .into_iter()
189 .map(|node| local_nodes[node])
190 .collect();
191 (
192 Self {
193 constitutive_model,
194 connectivity,
195 coordinates,
196 elements,
197 },
198 [boundary_nodes, local_nodes, global_nodes],
199 )
200 })
201 .collect()
202 }
203 fn reset(&mut self) {
204 self.elements
205 .iter_mut()
206 .for_each(|element| *element = F::default())
207 }
208}
209
210pub enum FiniteElementBlockError {
211 Upstream(String, String),
212}
213
214impl From<FiniteElementBlockError> for String {
215 fn from(error: FiniteElementBlockError) -> Self {
216 match error {
217 FiniteElementBlockError::Upstream(error, block) => {
218 format!(
219 "{error}\x1b[0;91m\n\
220 In finite element block: {block}."
221 )
222 }
223 }
224 }
225}
226
227impl From<FiniteElementBlockError> for TestError {
228 fn from(error: FiniteElementBlockError) -> Self {
229 Self {
230 message: error.to_string(),
231 }
232 }
233}
234
235impl Debug for FiniteElementBlockError {
236 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
237 let error = match self {
238 Self::Upstream(error, block) => {
239 format!(
240 "{error}\x1b[0;91m\n\
241 In block: {block}."
242 )
243 }
244 };
245 write!(f, "\n{error}\n\x1b[0;2;31m{}\x1b[0m\n", defeat_message())
246 }
247}
248
249impl Display for FiniteElementBlockError {
250 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
251 let error = match self {
252 Self::Upstream(error, block) => {
253 format!(
254 "{error}\x1b[0;91m\n\
255 In block: {block}."
256 )
257 }
258 };
259 write!(f, "{error}\x1b[0m")
260 }
261}
262
263pub trait ZerothOrderRoot<C, E, const G: usize, const M: usize, const N: usize, X> {
264 fn root(
265 &self,
266 equality_constraint: EqualityConstraint,
267 solver: impl ZerothOrderRootFinding<X>,
268 ) -> Result<X, OptimizationError>;
269}
270
271pub trait FirstOrderRoot<C, E, const G: usize, const M: usize, const N: usize, F, J, X> {
272 fn root(
273 &self,
274 equality_constraint: EqualityConstraint,
275 solver: impl FirstOrderRootFinding<F, J, X>,
276 ) -> Result<X, OptimizationError>;
277}
278
279pub trait FirstOrderMinimize<C, E, const G: usize, const M: usize, const N: usize, X> {
280 fn minimize(
281 &self,
282 equality_constraint: EqualityConstraint,
283 solver: impl FirstOrderOptimization<Scalar, X>,
284 ) -> Result<X, OptimizationError>;
285}
286
287pub trait SecondOrderMinimize<C, E, const G: usize, const M: usize, const N: usize, J, H, X> {
288 fn minimize(
289 &self,
290 equality_constraint: EqualityConstraint,
291 solver: impl SecondOrderOptimization<Scalar, J, H, X>,
292 ) -> Result<X, OptimizationError>;
293}
294
295fn band<const N: usize>(
296 connectivity: &Connectivity<N>,
297 equality_constraint: &EqualityConstraint,
298 number_of_nodes: usize,
299 dimension: usize,
300) -> Banded {
301 match equality_constraint {
302 EqualityConstraint::Fixed(indices) => {
303 let neighbors: Vec<Vec<usize>> = invert(connectivity, number_of_nodes)
304 .iter()
305 .map(|elements| {
306 let mut nodes: Vec<usize> = elements
307 .iter()
308 .flat_map(|&element| connectivity[element])
309 .collect();
310 nodes.sort();
311 nodes.dedup();
312 nodes
313 })
314 .collect();
315 let structure: Vec<Vec<bool>> = neighbors
316 .iter()
317 .map(|nodes| (0..number_of_nodes).map(|b| nodes.contains(&b)).collect())
318 .collect();
319 let structure_nd: Vec<Vec<bool>> = structure
320 .iter()
321 .flat_map(|row| {
322 repeat_n(
323 row.iter()
324 .flat_map(|entry| repeat_n(*entry, dimension))
325 .collect(),
326 dimension,
327 )
328 })
329 .collect();
330 let mut keep = vec![true; structure_nd.len()];
331 indices.iter().for_each(|&index| keep[index] = false);
332 let banded = structure_nd
333 .into_iter()
334 .zip(keep.iter())
335 .filter(|(_, keep)| **keep)
336 .map(|(structure_nd_a, _)| {
337 structure_nd_a
338 .into_iter()
339 .zip(keep.iter())
340 .filter(|(_, keep)| **keep)
341 .map(|(structure_nd_ab, _)| structure_nd_ab)
342 .collect::<Vec<bool>>()
343 })
344 .collect::<Vec<Vec<bool>>>();
345 Banded::from(banded)
346 }
347 EqualityConstraint::Linear(matrix, _) => {
348 let neighbors: Vec<Vec<usize>> = invert(connectivity, number_of_nodes)
349 .iter()
350 .map(|elements| {
351 let mut nodes: Vec<usize> = elements
352 .iter()
353 .flat_map(|&element| connectivity[element])
354 .collect();
355 nodes.sort();
356 nodes.dedup();
357 nodes
358 })
359 .collect();
360 let structure: Vec<Vec<bool>> = neighbors
361 .iter()
362 .map(|nodes| (0..number_of_nodes).map(|b| nodes.contains(&b)).collect())
363 .collect();
364 let structure_nd: Vec<Vec<bool>> = structure
365 .iter()
366 .flat_map(|row| {
367 repeat_n(
368 row.iter()
369 .flat_map(|entry| repeat_n(*entry, dimension))
370 .collect(),
371 dimension,
372 )
373 })
374 .collect();
375 let num_coords = dimension * number_of_nodes;
376 assert_eq!(matrix.width(), num_coords);
377 let num_dof = matrix.len() + matrix.width();
378 let mut banded = vec![vec![false; num_dof]; num_dof];
379 structure_nd
380 .iter()
381 .zip(banded.iter_mut())
382 .for_each(|(structure_nd_i, banded_i)| {
383 structure_nd_i
384 .iter()
385 .zip(banded_i.iter_mut())
386 .for_each(|(structure_nd_ij, banded_ij)| *banded_ij = *structure_nd_ij)
387 });
388 let mut index = num_coords;
389 matrix.iter().for_each(|matrix_i| {
390 matrix_i.iter().enumerate().for_each(|(j, matrix_ij)| {
391 if matrix_ij != &0.0 {
392 banded[index][j] = true;
393 banded[j][index] = true;
394 index += 1;
395 }
396 })
397 });
398 Banded::from(banded)
399 }
400 EqualityConstraint::None => {
401 let neighbors: Vec<Vec<usize>> = invert(connectivity, number_of_nodes)
402 .iter()
403 .map(|elements| {
404 let mut nodes: Vec<usize> = elements
405 .iter()
406 .flat_map(|&element| connectivity[element])
407 .collect();
408 nodes.sort();
409 nodes.dedup();
410 nodes
411 })
412 .collect();
413 let structure: Vec<Vec<bool>> = neighbors
414 .iter()
415 .map(|nodes| (0..number_of_nodes).map(|b| nodes.contains(&b)).collect())
416 .collect();
417 let structure_nd: Vec<Vec<bool>> = structure
418 .iter()
419 .flat_map(|row| {
420 repeat_n(
421 row.iter()
422 .flat_map(|entry| repeat_n(*entry, dimension))
423 .collect(),
424 dimension,
425 )
426 })
427 .collect();
428 Banded::from(structure_nd)
429 }
430 }
431}
432
433fn invert<const N: usize>(
434 connectivity: &Connectivity<N>,
435 number_of_nodes: usize,
436) -> Vec<Vec<usize>> {
437 let mut inverse_connectivity = vec![vec![]; number_of_nodes];
438 connectivity
439 .iter()
440 .enumerate()
441 .for_each(|(element, nodes)| {
442 nodes
443 .iter()
444 .for_each(|&node| inverse_connectivity[node].push(element))
445 });
446 inverse_connectivity
447}