Skip to main content

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