Skip to main content

aeon_tk/geometry/tree/
blocks.rs

1use std::{array, ops::Range};
2
3use super::{ActiveCellId, Tree};
4use crate::geometry::{Face, FaceMask, HyperBox, IndexSpace};
5use bitvec::prelude::*;
6use datasize::DataSize;
7
8#[derive(
9    Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, serde::Serialize, serde::Deserialize,
10)]
11pub struct BlockId(pub usize);
12
13/// Groups cells of a `Tree` into uniform blocks, for more efficient inter-cell communication and multithreading.
14#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
15pub struct TreeBlocks<const N: usize> {
16    /// Stores each cell's position within its parent's block.
17    #[serde(with = "crate::array::vec")]
18    active_cell_positions: Vec<[usize; N]>,
19    /// Maps cell to the block that contains it.
20    active_cell_to_block: Vec<usize>,
21    /// Stores the size of each block.
22    #[serde(with = "crate::array::vec")]
23    block_sizes: Vec<[usize; N]>,
24    /// A flattened list of lists (for each block) that stores
25    /// a local cell index to global cell index map.
26    block_active_indices: Vec<ActiveCellId>,
27    /// The offsets for the aforementioned flattened list of lists.
28    block_active_offsets: Vec<usize>,
29    /// The physical bounds of each block.
30    block_bounds: Vec<HyperBox<N>>,
31    /// The level of refinement of each block.
32    block_levels: Vec<usize>,
33    /// Stores whether block face is on physical boundary.
34    boundaries: BitVec,
35    /// Number of subdivisions for each axis.
36    #[serde(with = "crate::array")]
37    width: [usize; N],
38    /// Ghost vertices along each face.
39    ghost: usize,
40    /// Stores a map from blocks to ranges of vertices.
41    offsets: Vec<usize>,
42}
43
44impl<const N: usize> TreeBlocks<N> {
45    pub fn new(width: [usize; N], ghost: usize) -> Self {
46        Self {
47            active_cell_positions: Default::default(),
48            active_cell_to_block: Default::default(),
49            block_sizes: Default::default(),
50            block_active_indices: Default::default(),
51            block_active_offsets: Default::default(),
52            block_bounds: Default::default(),
53            block_levels: Default::default(),
54            boundaries: Default::default(),
55            width,
56            ghost,
57            offsets: Default::default(),
58        }
59    }
60
61    /// Rebuilds the tree block structure from existing geometric information. Performs greedy meshing
62    /// to group cells into blocks.
63    pub fn build(&mut self, tree: &Tree<N>) {
64        self.build_blocks(tree);
65        self.build_bounds(tree);
66        self.build_boundaries(tree);
67        self.build_levels(tree);
68        self.build_nodes();
69    }
70
71    // Number of blocks in the mesh.
72    pub fn len(&self) -> usize {
73        self.block_sizes.len()
74    }
75
76    pub fn indices(&self) -> impl Iterator<Item = BlockId> + use<N> {
77        (0..self.len()).map(BlockId)
78    }
79
80    pub fn is_empty(&self) -> bool {
81        self.len() == 0
82    }
83
84    /// Returns the cells associated with the given block.
85    pub fn active_cells(&self, block: BlockId) -> &[ActiveCellId] {
86        &self.block_active_indices
87            [self.block_active_offsets[block.0]..self.block_active_offsets[block.0 + 1]]
88    }
89
90    /// Size of a given block, measured in cells.
91    pub fn size(&self, block: BlockId) -> [usize; N] {
92        self.block_sizes[block.0]
93    }
94
95    /// Number of nodes along each axis of a given block, not including ghost nodes.
96    pub fn node_size(&self, block: BlockId) -> [usize; N] {
97        array::from_fn(|axis| self.block_sizes[block.0][axis] * self.width[axis] + 1)
98    }
99
100    /// Returns the bounds of the given block.
101    pub fn bounds(&self, block: BlockId) -> HyperBox<N> {
102        self.block_bounds[block.0]
103    }
104
105    /// Returns the level of a block.
106    pub fn level(&self, block: BlockId) -> usize {
107        self.block_levels[block.0]
108    }
109
110    /// Returns boundary flags for a block.
111    pub fn boundary_flags(&self, block: BlockId) -> FaceMask<N> {
112        let mut flags = [[false; 2]; N];
113
114        for face in Face::<N>::iterate() {
115            flags[face.axis][face.side as usize] =
116                self.boundaries[block.0 * 2 * N + face.to_linear()];
117        }
118
119        FaceMask::pack(flags)
120    }
121
122    /// Returns the position of the cell within the block.
123    pub fn active_cell_position(&self, cell: ActiveCellId) -> [usize; N] {
124        self.active_cell_positions[cell.0]
125    }
126
127    /// Retrieves the block associated with a given active cell.
128    pub fn active_cell_block(&self, cell: ActiveCellId) -> BlockId {
129        BlockId(self.active_cell_to_block[cell.0])
130    }
131
132    /// The width of each cell along each axis.
133    pub fn width(&self) -> [usize; N] {
134        self.width
135    }
136
137    /// Number of ghost nodes on each face.
138    pub fn ghost(&self) -> usize {
139        self.ghost
140    }
141
142    // /// Sets the width of cells in the block.
143    // pub fn set_width(&mut self, width: [usize; N]) {
144    //     self.width = width;
145    // }
146
147    // /// Sets the number of ghost nodes in cells in the block.
148    // pub fn set_ghost(&mut self, ghost: usize) {
149    //     self.ghost = ghost;
150    // }
151
152    /// Returns the total number of nodes in the tree.
153    pub fn num_nodes(&self) -> usize {
154        *self.offsets.last().unwrap()
155    }
156
157    /// The range of dofs associated with the given block.
158    pub fn nodes(&self, block: BlockId) -> Range<usize> {
159        self.offsets[block.0]..self.offsets[block.0 + 1]
160    }
161
162    fn build_blocks(&mut self, tree: &Tree<N>) {
163        let num_active_cells = tree.num_active_cells();
164
165        // Resize/reset various maps
166        self.active_cell_positions.resize(num_active_cells, [0; N]);
167        self.active_cell_positions.fill([0; N]);
168
169        self.active_cell_to_block
170            .resize(num_active_cells, usize::MAX);
171        self.active_cell_to_block.fill(usize::MAX);
172
173        self.block_sizes.clear();
174        self.block_active_indices.clear();
175        self.block_active_offsets.clear();
176
177        // Loop over each cell in the tree
178        for active in tree.active_cell_indices() {
179            if self.active_cell_to_block[active.0] != usize::MAX {
180                // This cell already belongs to a block, continue.
181                continue;
182            }
183
184            // Get index of next block
185            let block = self.block_sizes.len();
186
187            self.active_cell_positions[active.0] = [0; N];
188            self.active_cell_to_block[active.0] = block;
189
190            self.block_sizes.push([1; N]);
191            let block_cell_offset = self.block_active_indices.len();
192
193            self.block_active_offsets.push(block_cell_offset);
194            self.block_active_indices.push(active);
195
196            // Try expanding the block along each axis.
197            for axis in 0..N {
198                // Perform greedy meshing.
199                'expand: loop {
200                    let face = Face::<N>::positive(axis);
201
202                    let size = self.block_sizes[block];
203                    let space = IndexSpace::new(size);
204
205                    // Make sure every cell on face is suitable for expansion.
206                    for index in space.face_window(Face::positive(axis)).iter() {
207                        // Retrieves the cell on this face
208                        let cell = tree.cell_from_active_index(
209                            self.block_active_indices
210                                [block_cell_offset + space.linear_from_cartesian(index)],
211                        );
212                        let level = tree.level(cell);
213                        // We can only expand if:
214                        // 1. We are not on a physical boundary.
215                        // 2. We did not pass over a periodic boundary.
216                        // 3. The neighbor is the same level of refinement.
217                        // 4. The neighbor does not already belong to another block.
218                        let Some(neighbor) = tree.neighbor(cell, face) else {
219                            break 'expand;
220                        };
221
222                        if tree.is_boundary_face(cell, face) {
223                            break 'expand;
224                        }
225
226                        if level != tree.level(neighbor) || !tree.is_active(neighbor) {
227                            break 'expand;
228                        }
229
230                        if self.active_cell_to_block
231                            [tree.active_index_from_cell(neighbor).unwrap().0]
232                            != usize::MAX
233                        {
234                            break 'expand;
235                        }
236                    }
237
238                    // We may now expand along this axis
239                    for index in space.face_window(Face::positive(axis)).iter() {
240                        let active = self.block_active_indices
241                            [block_cell_offset + space.linear_from_cartesian(index)];
242
243                        let cell = tree.cell_from_active_index(active);
244                        let cell_neighbor = tree.neighbor(cell, face).unwrap();
245                        debug_assert!(tree.is_active(cell_neighbor));
246                        let active_neighbor = tree.active_index_from_cell(cell_neighbor).unwrap();
247
248                        self.active_cell_positions[active_neighbor.0] = index;
249                        self.active_cell_positions[active_neighbor.0][axis] += 1;
250                        self.active_cell_to_block[active_neighbor.0] = block;
251
252                        self.block_active_indices.push(active_neighbor);
253                    }
254
255                    self.block_sizes[block][axis] += 1;
256                }
257            }
258        }
259
260        self.block_active_offsets
261            .push(self.block_active_indices.len());
262    }
263
264    fn build_bounds(&mut self, tree: &Tree<N>) {
265        self.block_bounds.clear();
266
267        for block in self.indices() {
268            let size = self.size(block);
269            let a = *self.active_cells(block).first().unwrap();
270
271            let cell_bounds = tree.bounds(tree.cell_from_active_index(a));
272
273            self.block_bounds.push(HyperBox {
274                origin: cell_bounds.origin,
275                size: array::from_fn(|axis| cell_bounds.size[axis] * size[axis] as f64),
276            })
277        }
278    }
279
280    fn build_boundaries(&mut self, tree: &Tree<N>) {
281        self.boundaries.clear();
282
283        for block in self.indices() {
284            let a = 0;
285            let b: usize = self.active_cells(block).len() - 1;
286
287            for face in Face::<N>::iterate() {
288                let active = if face.side {
289                    self.active_cells(block)[b]
290                } else {
291                    self.active_cells(block)[a]
292                };
293                let cell = tree.cell_from_active_index(active);
294                self.boundaries.push(tree.is_boundary_face(cell, face))
295            }
296        }
297    }
298
299    fn build_levels(&mut self, tree: &Tree<N>) {
300        self.block_levels.resize(self.len(), 0);
301        for block in self.indices() {
302            let active = self.active_cells(block)[0];
303            self.block_levels[block.0] = tree.active_level(active);
304        }
305    }
306
307    fn build_nodes(&mut self) {
308        for axis in 0..N {
309            assert!(self.width[axis] % 2 == 0);
310        }
311
312        // Reset map
313        self.offsets.clear();
314        self.offsets.reserve(self.len() + 1);
315
316        // Start cursor at 0.
317        let mut cursor = 0;
318        self.offsets.push(cursor);
319
320        for block in self.indices() {
321            let size = self.size(block);
322            // Width of block in nodes.
323            let block_width: [usize; N] =
324                array::from_fn(|axis| self.width[axis] * size[axis] + 1 + 2 * self.ghost);
325
326            cursor += block_width.iter().product::<usize>();
327            self.offsets.push(cursor);
328        }
329    }
330}
331
332impl<const N: usize> DataSize for TreeBlocks<N> {
333    const IS_DYNAMIC: bool = true;
334    const STATIC_HEAP_SIZE: usize = 0;
335
336    fn estimate_heap_size(&self) -> usize {
337        self.active_cell_positions.estimate_heap_size()
338            + self.active_cell_to_block.estimate_heap_size()
339            + self.block_sizes.estimate_heap_size()
340            + self.block_active_offsets.estimate_heap_size()
341            + self.block_active_indices.estimate_heap_size()
342            + self.block_bounds.estimate_heap_size()
343            + self.block_levels.estimate_heap_size()
344            + self.boundaries.capacity() / size_of::<usize>()
345            + self.offsets.estimate_heap_size()
346    }
347}
348
349#[cfg(test)]
350mod tests {
351    use super::*;
352    use crate::{geometry::Tree, prelude::HyperBox};
353
354    #[test]
355    fn greedy_meshing() {
356        let mut tree = Tree::new(HyperBox::<2>::UNIT);
357        tree.refine(&[true]);
358        tree.refine(&[true, false, false, false]);
359
360        let mut blocks = TreeBlocks::new([4; 2], 2);
361        blocks.build(&tree);
362
363        assert!(blocks.len() == 3);
364        assert_eq!(blocks.level(BlockId(0)), 2);
365        assert_eq!(blocks.size(BlockId(0)), [2, 2]);
366        assert_eq!(
367            blocks.active_cells(BlockId(0)),
368            [
369                ActiveCellId(0),
370                ActiveCellId(1),
371                ActiveCellId(2),
372                ActiveCellId(3)
373            ]
374        );
375        assert_eq!(blocks.level(BlockId(1)), 1);
376        assert_eq!(blocks.size(BlockId(1)), [1, 2]);
377        assert_eq!(
378            blocks.active_cells(BlockId(1)),
379            [ActiveCellId(4), ActiveCellId(6),]
380        );
381        assert_eq!(blocks.level(BlockId(2)), 1);
382        assert_eq!(blocks.size(BlockId(2)), [1, 1]);
383        assert_eq!(blocks.active_cells(BlockId(2)), [ActiveCellId(5)]);
384
385        tree.refine(&[false, false, false, false, true, false, false]);
386        blocks.build(&tree);
387        assert!(blocks.len() == 2);
388        assert_eq!(blocks.level(BlockId(0)), 2);
389        assert_eq!(blocks.size(BlockId(0)), [4, 2]);
390        assert_eq!(
391            blocks.active_cells(BlockId(0)),
392            [
393                ActiveCellId(0),
394                ActiveCellId(1),
395                ActiveCellId(4),
396                ActiveCellId(5),
397                ActiveCellId(2),
398                ActiveCellId(3),
399                ActiveCellId(6),
400                ActiveCellId(7),
401            ]
402        );
403        assert_eq!(blocks.level(BlockId(1)), 1);
404        assert_eq!(blocks.size(BlockId(1)), [2, 1]);
405        assert_eq!(
406            blocks.active_cells(BlockId(1)),
407            [ActiveCellId(8), ActiveCellId(9),]
408        );
409    }
410
411    #[test]
412    fn node_ranges() {
413        let mut tree = Tree::new(HyperBox::<2>::UNIT);
414        let mut blocks = TreeBlocks::new([8; 2], 3);
415        tree.refine(&[true]);
416        tree.refine(&[true, false, false, false]);
417        tree.build();
418
419        blocks.build(&tree);
420
421        assert_eq!(blocks.len(), 3);
422
423        assert_eq!(blocks.nodes(BlockId(0)), 0..529);
424        assert_eq!(blocks.nodes(BlockId(1)), 529..874);
425        assert_eq!(blocks.nodes(BlockId(2)), 874..1099);
426    }
427}