conspire/domain/partition/feti/block/element/
mod.rs1use crate::{
2 domain::{Blocks, ElementModelError, Model, NodalCoordinates},
3 geometry::mesh::Partition,
4 math::{Scalar, SquareMatrix, Tensor, Vector},
5};
6use std::{array::from_fn, collections::HashMap};
7
8pub struct ElementSystems {
14 pub(crate) positions: Vec<[f64; 3]>,
15 pub(crate) elements: Vec<ElementSystem>,
16}
17
18pub(crate) struct ElementSystem {
19 pub(crate) nodes: Vec<usize>,
20 pub(crate) stiffness: SquareMatrix,
21 pub(crate) force: Vector,
22}
23
24impl ElementSystem {
25 pub(crate) fn pack(
26 nodes: Vec<usize>,
27 force: impl Fn(usize, usize) -> Scalar,
28 stiffness: impl Fn(usize, usize, usize, usize) -> Scalar,
29 ) -> Self {
30 const D: usize = 3;
31 let number_of_nodes = nodes.len();
32 let mut packed_stiffness = SquareMatrix::zero(D * number_of_nodes);
33 let mut packed_force = Vector::zero(D * number_of_nodes);
34 (0..number_of_nodes).for_each(|a| {
35 (0..D).for_each(|i| packed_force[D * a + i] = force(a, i));
36 (0..number_of_nodes).for_each(|b| {
37 (0..D).for_each(|i| {
38 (0..D).for_each(|j| {
39 packed_stiffness[D * a + i][D * b + j] = stiffness(a, b, i, j)
40 })
41 })
42 })
43 });
44 Self {
45 nodes,
46 stiffness: packed_stiffness,
47 force: packed_force,
48 }
49 }
50}
51
52pub(crate) fn positions<const D: usize>(nodal_coordinates: &NodalCoordinates<D>) -> Vec<[f64; D]> {
53 nodal_coordinates
54 .iter()
55 .map(|coordinate| from_fn(|axis| coordinate[axis].value()))
56 .collect()
57}
58
59pub trait DecomposableElements {
64 fn element_systems(
65 &self,
66 nodal_coordinates: &NodalCoordinates<3>,
67 ) -> Result<ElementSystems, ElementModelError>;
68}
69
70impl<B> DecomposableElements for Model<B, 3>
71where
72 B: DecomposableElements,
73{
74 fn element_systems(
75 &self,
76 nodal_coordinates: &NodalCoordinates<3>,
77 ) -> Result<ElementSystems, ElementModelError> {
78 self.blocks.element_systems(nodal_coordinates)
79 }
80}
81
82impl<B1, B2> DecomposableElements for Blocks<B1, B2>
83where
84 B1: DecomposableElements,
85 B2: DecomposableElements,
86{
87 fn element_systems(
88 &self,
89 nodal_coordinates: &NodalCoordinates<3>,
90 ) -> Result<ElementSystems, ElementModelError> {
91 let mut systems = self.0.element_systems(nodal_coordinates)?;
92 systems
93 .elements
94 .extend(self.1.element_systems(nodal_coordinates)?.elements);
95 Ok(systems)
96 }
97}
98
99impl ElementSystems {
100 pub(crate) fn positions(&self) -> &[[f64; 3]] {
101 &self.positions
102 }
103 #[allow(clippy::type_complexity)]
104 pub(crate) fn subdomains(
105 &self,
106 partition: &Partition,
107 ) -> Result<(Vec<SquareMatrix>, Vec<Vector>), String> {
108 const D: usize = 3;
109 if partition.elements_parts().len() != self.elements.len() {
110 return Err("The partition must assign every element to a subdomain.".to_string());
111 }
112 Ok((0..partition.number_of_parts())
113 .map(|part| {
114 let nodes = partition.part_nodes(part);
115 let local: HashMap<usize, usize> = nodes
116 .iter()
117 .enumerate()
118 .map(|(local, &node)| (node, local))
119 .collect();
120 let mut stiffness = SquareMatrix::zero(D * nodes.len());
121 let mut force = Vector::zero(D * nodes.len());
122 partition.part_elements(part).iter().for_each(|&element| {
123 let element = &self.elements[element];
124 let dofs: Vec<usize> = element
125 .nodes
126 .iter()
127 .flat_map(|node| {
128 let base = D * local[node];
129 (0..D).map(move |i| base + i)
130 })
131 .collect();
132 dofs.iter().enumerate().for_each(|(a, &dof_a)| {
133 force[dof_a] += element.force[a];
134 dofs.iter().enumerate().for_each(|(b, &dof_b)| {
135 stiffness[dof_a][dof_b] += element.stiffness[a][b]
136 })
137 })
138 });
139 (stiffness, force)
140 })
141 .unzip())
142 }
143}