conspire/domain/fem/block/
mod.rs

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}