Skip to main content

aeon_tk/geometry/tree/
interfaces.rs

1use std::{array, cmp::Ordering, ops::Range};
2
3use super::{
4    NeighborId, Tree, TreeBlockNeighbor, TreeBlocks, TreeCellNeighbor, TreeNeighbors,
5    blocks::BlockId,
6};
7use crate::{
8    geometry::{IndexSpace, Side},
9    kernel::NodeSpace,
10    prelude::{FaceArray, HyperBox},
11};
12
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct TreeInterface<const N: usize> {
15    /// Block to be filled
16    pub block: BlockId,
17    /// Neighbor block.
18    pub neighbor: BlockId,
19    /// Source node in neighbor block.
20    pub source: [isize; N],
21    /// Destination node in target block.
22    pub dest: [isize; N],
23    /// Number of nodes to be filled along each axis.
24    pub size: [usize; N],
25}
26
27impl<const N: usize> datasize::DataSize for TreeInterface<N> {
28    const IS_DYNAMIC: bool = false;
29    const STATIC_HEAP_SIZE: usize = 0;
30
31    fn estimate_heap_size(&self) -> usize {
32        0
33    }
34}
35
36struct TransferAABB<const N: usize> {
37    /// Source node in neighbor block.
38    source: [isize; N],
39    /// Destination node in target block.
40    dest: [isize; N],
41    /// Number of nodes to be filled along each axis.
42    size: [usize; N],
43}
44
45#[derive(Clone, Debug, PartialEq, Eq, Default)]
46pub struct TreeInterfaces<const N: usize> {
47    /// Interfaces build from neighbors.
48    interfaces: Vec<TreeInterface<N>>,
49    /// Offsets linking each interface to a range of ghost/face nodes.
50    interface_node_offsets: Vec<usize>,
51    /// Mask that keeps different interfaces disjoint.
52    interface_masks: Vec<bool>,
53}
54
55impl<const N: usize> TreeInterfaces<N> {
56    pub fn iter(&self) -> impl Iterator<Item = &TreeInterface<N>> {
57        self.interfaces.iter()
58    }
59
60    /// Returns the total number of interface nodes.
61    pub fn _num_interface_nodes(&self) -> usize {
62        *self.interface_node_offsets.last().unwrap()
63    }
64
65    /// Retrieves the interface corresponding to the given neighbor.
66    pub fn interface(&self, neighbor: NeighborId) -> &TreeInterface<N> {
67        &self.interfaces[neighbor.0]
68    }
69
70    /// Returns the range of nodes associated with a given interface.
71    pub fn interface_nodes(&self, interface: NeighborId) -> Range<usize> {
72        self.interface_node_offsets[interface.0]..self.interface_node_offsets[interface.0 + 1]
73    }
74
75    /// Returns a mask of nodes for a given interface.
76    pub fn interface_mask(&self, interface: NeighborId) -> &[bool] {
77        &self.interface_masks[self.interface_nodes(interface)]
78    }
79
80    /// Returns an index space corresponding to a given interface.
81    pub fn interface_space(&self, interface: NeighborId) -> IndexSpace<N> {
82        IndexSpace::new(self.interfaces[interface.0].size)
83    }
84
85    /// Iterates over active index nodes.
86    pub fn interface_nodes_active(
87        &self,
88        interface: NeighborId,
89        _extent: usize,
90    ) -> impl Iterator<Item = [usize; N]> + '_ {
91        let space = self.interface_space(interface);
92        let mask = self.interface_mask(interface);
93
94        space
95            .iter()
96            .filter(move |&offset| mask[space.linear_from_cartesian(offset)])
97    }
98
99    pub fn build(&mut self, tree: &Tree<N>, blocks: &TreeBlocks<N>, neighbors: &TreeNeighbors<N>) {
100        self.interfaces.clear();
101        self.interface_node_offsets.clear();
102        self.interface_masks.clear();
103
104        for neighbor in neighbors.iter() {
105            let aabb = self.transfer_aabb(tree, blocks, neighbor);
106
107            self.interfaces.push(TreeInterface {
108                block: neighbor.block,
109                neighbor: neighbor.neighbor,
110                source: aabb.source,
111                dest: aabb.dest,
112                size: aabb.size,
113            });
114        }
115
116        // ************************
117        // Compute node offsets
118
119        let mut cursor = 0;
120
121        for interface in self.interfaces.iter() {
122            self.interface_node_offsets.push(cursor);
123            cursor += IndexSpace::new(interface.size).index_count();
124        }
125
126        self.interface_node_offsets.push(cursor);
127
128        // **************************
129        // Compute interface masks
130
131        self.interface_masks.resize(cursor, false);
132
133        let mut buffer = Vec::default();
134
135        for block in blocks.indices() {
136            let block_size = blocks.size(block);
137            let block_level = blocks.level(block);
138            let block_space = NodeSpace {
139                size: array::from_fn(|axis| block_size[axis] * blocks.width()[axis]),
140                ghost: blocks.ghost(),
141                bounds: HyperBox::<N>::UNIT,
142                boundary: FaceArray::default(),
143            };
144
145            buffer.resize(block_space.num_nodes(), usize::MAX);
146            buffer.fill(usize::MAX);
147
148            // Fill buffer
149            for iidx in neighbors.block_range(block) {
150                let interface = &self.interfaces[iidx];
151                let interface_level = blocks.level(interface.neighbor);
152
153                debug_assert!(interface.block == block);
154
155                for offset in IndexSpace::new(interface.size).iter() {
156                    let node = array::from_fn(|axis| interface.dest[axis] + offset[axis] as isize);
157                    let index = block_space.index_from_node(node);
158
159                    if buffer[index] == usize::MAX {
160                        buffer[index] = iidx;
161                        continue;
162                    }
163
164                    let other_interface = &self.interfaces[buffer[index]];
165                    let other_neighbor = other_interface.neighbor;
166
167                    debug_assert!(other_interface.block == interface.block);
168
169                    let other_level = blocks.level(other_neighbor);
170
171                    // If new interface is coarser than old interface, prefer it
172                    //
173                    // Otherwise if they are the same level, but the other interface
174                    // points to a block with a smaller index, prefer it.
175                    if interface_level < other_level
176                        || (interface_level == other_level && interface.neighbor < other_neighbor)
177                    {
178                        buffer[index] = iidx;
179                    }
180                }
181            }
182
183            // Now compute masks
184            for iidx in neighbors.block_range(block) {
185                let interface = &self.interfaces[iidx];
186                let interface_level = blocks.level(interface.neighbor);
187
188                debug_assert!(interface.block == block);
189
190                let interface_mask_offset = self.interface_node_offsets[iidx];
191
192                for (dst, offset) in IndexSpace::new(interface.size).iter().enumerate() {
193                    let node = array::from_fn(|axis| interface.dest[axis] + offset[axis] as isize);
194                    let src = block_space.index_from_node(node);
195
196                    // Only fill if this point belongs to interface.
197                    let mut mask = buffer[src] == iidx;
198
199                    if interface_level == block_level
200                        && interface.block <= interface.neighbor
201                        && block_space.is_interior(node)
202                    {
203                        mask = false;
204                    }
205
206                    // Set mask value
207                    self.interface_masks[interface_mask_offset + dst] = mask;
208                }
209            }
210        }
211    }
212
213    /// Computes transfer aabb for nodes that lie outside the node space.
214    fn transfer_aabb(
215        &self,
216        tree: &Tree<N>,
217        blocks: &TreeBlocks<N>,
218        neighbor: &TreeBlockNeighbor<N>,
219    ) -> TransferAABB<N> {
220        let a = neighbor.a.clone();
221        let b = neighbor.b.clone();
222
223        let block_level = tree.active_level(a.cell);
224        let neighbor_level = tree.active_level(a.neighbor);
225
226        // ********************************
227        // Dest
228
229        let (mut anode, mut bnode) = self.transfer_dest(tree, blocks, neighbor);
230
231        // **************************
232        // Source
233
234        let mut source = self.transfer_source(tree, blocks, &neighbor.a);
235
236        for axis in 0..N {
237            if b.region.side(axis) == Side::Left && block_level < neighbor_level {
238                debug_assert!(a.region.side(axis) == Side::Left);
239                bnode[axis] -= 1;
240            }
241
242            if a.region.side(axis) == Side::Right && block_level < neighbor_level {
243                debug_assert!(b.region.side(axis) == Side::Right);
244
245                anode[axis] += 1;
246                source[axis] += 1;
247            }
248        }
249
250        let size = array::from_fn(|axis| {
251            if bnode[axis] >= anode[axis] {
252                (bnode[axis] - anode[axis] + 1) as usize
253            } else {
254                0
255            }
256        });
257
258        TransferAABB {
259            source,
260            dest: anode,
261            size,
262        }
263    }
264
265    #[inline]
266    fn transfer_dest(
267        &self,
268        tree: &Tree<N>,
269        blocks: &TreeBlocks<N>,
270        interface: &TreeBlockNeighbor<N>,
271    ) -> ([isize; N], [isize; N]) {
272        let a = interface.a.clone();
273        let b = interface.b.clone();
274
275        // Find ghost region that must be filled
276        let aindex = blocks.active_cell_position(a.cell);
277        let bindex = blocks.active_cell_position(b.cell);
278
279        let block_level = tree.active_level(a.cell);
280        let neighbor_level = tree.active_level(a.neighbor);
281
282        // ********************************
283        // A node
284
285        // Compute bottom left corner of A cell.
286        let mut anode: [_; N] =
287            array::from_fn(|axis| (aindex[axis] * blocks.width()[axis]) as isize);
288
289        if block_level < neighbor_level {
290            let split = tree.most_recent_active_split(a.neighbor).unwrap();
291            (0..N)
292                .filter(|&axis| a.region.side(axis) == Side::Middle && split.is_set(axis))
293                .for_each(|axis| anode[axis] += (blocks.width()[axis] / 2) as isize);
294        }
295
296        // Offset by appropriate ghost nodes/width
297        for axis in 0..N {
298            match a.region.side(axis) {
299                Side::Left => {
300                    anode[axis] -= blocks.ghost() as isize;
301                }
302                Side::Right => {
303                    anode[axis] += blocks.width()[axis] as isize;
304                }
305                Side::Middle => {}
306            }
307        }
308
309        // ***********************************
310        // B Node
311
312        // Compute top right corner of B cell
313        let mut bnode: [_; N] =
314            array::from_fn(|axis| ((bindex[axis] + 1) * blocks.width()[axis]) as isize);
315
316        if block_level < neighbor_level {
317            let split = tree.most_recent_active_split(b.neighbor).unwrap();
318            (0..N)
319                .filter(|&axis| b.region.side(axis) == Side::Middle && !split.is_set(axis))
320                .for_each(|axis| bnode[axis] -= (blocks.width()[axis] / 2) as isize);
321        }
322
323        // Offset by appropriate ghost nodes/width
324        for axis in 0..N {
325            match b.region.side(axis) {
326                Side::Right => {
327                    bnode[axis] += blocks.ghost() as isize;
328                }
329                Side::Left => {
330                    bnode[axis] -= blocks.width()[axis] as isize;
331                }
332                Side::Middle => {}
333            }
334        }
335
336        (anode, bnode)
337    }
338
339    /// Computes the source node on the neighboring block from which we fill the current block.
340    #[inline]
341    fn transfer_source(
342        &self,
343        tree: &Tree<N>,
344        blocks: &TreeBlocks<N>,
345        a: &TreeCellNeighbor<N>,
346    ) -> [isize; N] {
347        let block_level = tree.active_level(a.cell);
348        let neighbor_level = tree.active_level(a.neighbor);
349
350        // Find source node
351        let nindex = blocks.active_cell_position(a.neighbor);
352        let mut source: [isize; N] =
353            array::from_fn(|axis| (nindex[axis] * blocks.width()[axis]) as isize);
354
355        match block_level.cmp(&neighbor_level) {
356            Ordering::Equal => {
357                for axis in 0..N {
358                    if a.region.side(axis) == Side::Left {
359                        source[axis] += (blocks.width()[axis] - blocks.ghost()) as isize;
360                    }
361                }
362            }
363            Ordering::Greater => {
364                // Source is stored in subnodes
365                for axis in 0..N {
366                    source[axis] *= 2;
367                }
368
369                let split = tree.most_recent_active_split(a.cell).unwrap();
370
371                for axis in 0..N {
372                    if split.is_set(axis) {
373                        match a.region.side(axis) {
374                            Side::Left => {
375                                source[axis] +=
376                                    blocks.width()[axis] as isize - blocks.ghost() as isize
377                            }
378                            Side::Middle => source[axis] += blocks.width()[axis] as isize,
379                            Side::Right => {}
380                        }
381                    } else {
382                        match a.region.side(axis) {
383                            Side::Left => {
384                                source[axis] +=
385                                    2 * blocks.width()[axis] as isize - blocks.ghost() as isize
386                            }
387                            Side::Middle => {}
388                            Side::Right => source[axis] += blocks.width()[axis] as isize,
389                        }
390                    }
391                }
392            }
393            Ordering::Less => {
394                // Source is stored in supernodes
395                for axis in 0..N {
396                    source[axis] /= 2;
397                }
398
399                for axis in 0..N {
400                    if a.region.side(axis) == Side::Left {
401                        source[axis] += blocks.width()[axis] as isize / 2 - blocks.ghost() as isize;
402                    }
403                }
404            }
405        }
406
407        source
408    }
409}
410
411impl<const N: usize> datasize::DataSize for TreeInterfaces<N> {
412    const IS_DYNAMIC: bool = true;
413    const STATIC_HEAP_SIZE: usize = 0;
414
415    fn estimate_heap_size(&self) -> usize {
416        self.interfaces.estimate_heap_size()
417            + self.interface_node_offsets.estimate_heap_size()
418            + self.interface_masks.estimate_heap_size()
419    }
420}