Skip to main content

aeon_tk/mesh/
mod.rs

1//! Module containing the `Mesh` API, the main datastructure through which one
2//! writes finite difference programs.
3//!
4//! `Mesh`s are the central driver of all finite difference codes, and provide many methods
5//! for discretizing domains, approximating differential operators, applying boundary conditions,
6//! filling interior interfaces, and adaptively regridding a domain based on various error heuristics.
7
8use crate::geometry::{
9    ActiveCellId, BlockId, Face, FaceArray, FaceMask, HyperBox, Split, Tree, TreeBlocks,
10    TreeInterfaces, TreeNeighbors, TreeSer,
11};
12use crate::kernel::{BoundaryClass, DirichletParams, Element, node_from_vertex};
13use crate::kernel::{BoundaryKind, NodeSpace, NodeWindow, SystemBoundaryConds};
14use crate::prelude::IndexSpace;
15use datasize::DataSize;
16
17#[cfg(feature = "parallel")]
18use rayon::iter::{IntoParallelIterator, ParallelBridge, ParallelIterator};
19
20use std::collections::HashMap;
21use std::{array, fmt::Write, ops::Range};
22
23mod checkpoint;
24mod evaluate;
25mod function;
26mod regrid;
27mod store;
28mod transfer;
29
30pub use checkpoint::{Checkpoint, ExportStride, ExportVtuConfig};
31pub use function::{Engine, Function, FunctionBorrowMut, Gaussian, Projection, TanH};
32pub use store::{MeshStore, UnsafeThreadCache};
33
34use crate::image::ImageRef;
35
36/// A discretization of a rectangular axis aligned grid into a collection of uniform grids of nodes
37/// with different spacings. A `Mesh` is built on top of a Quadtree, allowing one to selectively
38/// refine areas of interest without wasting computational power on smoother regions of the domain.
39///
40/// This abstraction also handles multithread dispatch and sharing nodes between threads in a
41/// an effecient manner. This allows the user to write generic, sequential, and straighforward code
42/// on the main thread, while still maximising performance and fully utilizing computational resources.
43#[derive(Debug, serde::Serialize, serde::Deserialize)]
44#[serde(from = "MeshSer<N>", into = "MeshSer<N>")]
45pub struct Mesh<const N: usize> {
46    /// Underlying Tree on which the mesh is built.
47    tree: Tree<N>,
48    /// The width of a cell on the mesh (i.e. how many subcells are in that cell).
49    width: usize,
50    /// The number of ghost cells used to facilitate inter-block communication.
51    ghost: usize,
52    /// `BoundaryClass` for each face. Restricts what kinds of boundary condition
53    /// (encoded in `BoundaryKind`) may be enforced on that face.
54    boundary: FaceArray<N, BoundaryClass>,
55
56    /// Block structure induced by the tree.
57    blocks: TreeBlocks<N>,
58    /// Neighbors of each block.
59    neighbors: TreeNeighbors<N>,
60    /// Neighbors translated into interfaces
61    interfaces: TreeInterfaces<N>,
62
63    /// Refinement flags for each cell on the mesh.
64    refine_flags: Vec<bool>,
65    /// Coarsening flags for each cell on the mesh.
66    coarsen_flags: Vec<bool>,
67
68    /// Map from cells before refinement to current cells.
69    regrid_map: Vec<ActiveCellId>,
70
71    /// Blocks before most recent refinement.
72    old_blocks: TreeBlocks<N>,
73    /// Cell splits from before most recent refinement.
74    ///
75    /// May be temporary if I can find a more elegant solution.
76    old_cell_splits: Vec<Split<N>>,
77
78    // ********************************
79    // Caches *************************
80    /// Thread-local stores used for allocation.
81    stores: UnsafeThreadCache<MeshStore>,
82    /// Cache for uniform elements
83    elements: HashMap<(usize, usize), Element<N>>,
84}
85
86impl<const N: usize> Mesh<N> {
87    /// Constructs a new `Mesh` covering the domain, with a number of nodes
88    /// defined by `width` and `ghost`.
89    pub fn new(
90        bounds: HyperBox<N>,
91        width: usize,
92        ghost: usize,
93        boundary: FaceArray<N, BoundaryClass>,
94    ) -> Self {
95        assert!(width % 2 == 0);
96        assert!(ghost >= width / 2);
97
98        let mut tree = Tree::new(bounds);
99
100        for axis in 0..N {
101            let negative_periodic =
102                matches!(boundary[Face::negative(axis)], BoundaryClass::Periodic);
103            let positive_periodic =
104                matches!(boundary[Face::positive(axis)], BoundaryClass::Periodic);
105
106            assert_eq!(
107                negative_periodic, positive_periodic,
108                "Periodicity on a given axis must match"
109            );
110
111            tree.set_periodic(axis, negative_periodic && positive_periodic);
112        }
113
114        let mut result = Self {
115            tree,
116            width,
117            ghost,
118            boundary,
119
120            blocks: TreeBlocks::new([width; N], ghost),
121            neighbors: TreeNeighbors::default(),
122            interfaces: TreeInterfaces::default(),
123
124            refine_flags: Vec::new(),
125            coarsen_flags: Vec::new(),
126
127            regrid_map: Vec::default(),
128            old_blocks: TreeBlocks::new([width; N], ghost),
129            old_cell_splits: Vec::default(),
130
131            stores: UnsafeThreadCache::new(),
132            elements: HashMap::default(),
133        };
134
135        result.build();
136
137        result
138    }
139
140    /// Retrieves width of individual cells in this mesh.
141    pub fn width(&self) -> usize {
142        self.width
143    }
144
145    /// Retrieves the number of ghost nodes on each cell of a mesh.
146    pub fn ghost(&self) -> usize {
147        self.ghost
148    }
149
150    /// Rebuilds mesh from current tree.
151    fn build(&mut self) {
152        debug_assert_eq!(self.blocks.width(), [self.width; N]);
153        debug_assert_eq!(self.blocks.ghost(), self.ghost());
154        debug_assert_eq!(self.old_blocks.width(), [self.width; N]);
155        debug_assert_eq!(self.old_blocks.ghost(), self.ghost());
156
157        // Rebuild tree
158        self.tree.build();
159        // Rebuild blocks
160        self.blocks.build(&self.tree);
161
162        // Rebuild neighbors
163        self.neighbors.build(&self.tree, &self.blocks);
164        // Rebuild interfaces
165        self.interfaces
166            .build(&self.tree, &self.blocks, &self.neighbors);
167        // Resize flags, clearing value to false.
168        self.build_flags();
169    }
170
171    /// Allocates requisite space for refinement and coarsening flags.
172    fn build_flags(&mut self) {
173        self.refine_flags.clear();
174        self.coarsen_flags.clear();
175
176        self.refine_flags
177            .resize(self.tree.num_active_cells(), false);
178        self.coarsen_flags
179            .resize(self.tree.num_active_cells(), false);
180    }
181
182    // *******************************
183    // Global Info *******************
184
185    /// Retrieves the Quadtree this mesh is built on top of.
186    pub fn tree(&self) -> &Tree<N> {
187        &self.tree
188    }
189
190    /// Returns the total number of blocks on the mesh.
191    pub fn num_blocks(&self) -> usize {
192        self.blocks.len()
193    }
194
195    /// Returns the total number of blocks on the mesh before the most recent refinement.
196    pub(crate) fn num_old_blocks(&self) -> usize {
197        self.old_blocks.len()
198    }
199
200    /// Returns the total number of cells on the mesh.
201    pub fn num_active_cells(&self) -> usize {
202        self.tree.num_active_cells()
203    }
204
205    /// Returns the total number of nodes on the mesh.
206    pub fn num_nodes(&self) -> usize {
207        self.blocks.num_nodes()
208    }
209
210    pub fn num_dofs(&self) -> usize {
211        let mut result = 0;
212
213        for block in 0..self.num_blocks() {
214            let mut size = self.blocks.size(BlockId(block));
215
216            for axis in 0..N {
217                size[axis] *= self.width;
218
219                if self
220                    .blocks
221                    .boundary_flags(BlockId(block))
222                    .is_set(Face::positive(axis))
223                {
224                    size[axis] += 1;
225                }
226            }
227
228            result += size.iter().product::<usize>();
229        }
230
231        result
232    }
233
234    /// Returns the total number of nodes on the mesh before the most recent refinement.
235    pub(crate) fn num_old_nodes(&self) -> usize {
236        self.old_blocks.num_nodes()
237    }
238
239    /// Returns the boundary classes associated with each boundary of the physical domain.
240    pub fn boundary_classes(&self) -> FaceArray<N, BoundaryClass> {
241        self.boundary.clone()
242    }
243
244    // *******************************
245    // Data for each block ***********
246
247    /// Returns underlying `TreeBlocks<N>` object.
248    pub fn blocks(&self) -> &TreeBlocks<N> {
249        &self.blocks
250    }
251
252    /// The range of nodes assigned to a given block.
253    pub fn block_nodes(&self, block: BlockId) -> Range<usize> {
254        self.blocks.nodes(block)
255    }
256
257    /// The range of nodes assigned to a given block on the mesh before the most recent refinement.
258    pub(crate) fn old_block_nodes(&self, block: BlockId) -> Range<usize> {
259        self.old_blocks.nodes(block)
260    }
261
262    /// Computes the nodespace corresponding to a block.
263    pub fn block_space(&self, block: BlockId) -> NodeSpace<N> {
264        let size = self.blocks.size(block);
265        let cell_size = array::from_fn(|axis| size[axis] * self.width);
266
267        NodeSpace {
268            size: cell_size,
269            ghost: self.ghost,
270            boundary: self.block_boundary_classes(block),
271            bounds: self.block_bounds(block),
272        }
273    }
274
275    /// Computes the nodespace corresponding to a block on the mesh before the most recent refinement.
276    pub(crate) fn old_block_space(&self, block: BlockId) -> NodeSpace<N> {
277        let size = self.old_blocks.size(block);
278        let cell_size = array::from_fn(|axis| size[axis] * self.width);
279
280        NodeSpace {
281            size: cell_size,
282            ghost: self.ghost,
283            bounds: HyperBox::UNIT,
284            boundary: self.old_block_boundary_classes(block),
285        }
286    }
287
288    /// Computes the bounds of a block.
289    pub fn block_bounds(&self, block: BlockId) -> HyperBox<N> {
290        self.blocks.bounds(block)
291    }
292
293    /// Computes flags indicating whether a particular face of a block borders a physical
294    /// boundary.
295    pub fn block_physical_boundary_flags(&self, block: BlockId) -> FaceMask<N> {
296        self.blocks.boundary_flags(block)
297    }
298
299    /// Indicates what class of boundary condition is enforced along each face of the block.
300    pub fn block_boundary_classes(&self, block: BlockId) -> FaceArray<N, BoundaryClass> {
301        let flag = self.block_physical_boundary_flags(block);
302
303        FaceArray::from_fn(|face| {
304            if flag.is_set(face) {
305                self.boundary[face]
306            } else {
307                BoundaryClass::Ghost
308            }
309        })
310    }
311
312    /// Produces a block boundary which correctly accounts for
313    /// interior interfaces.
314    pub fn block_bcs<B: SystemBoundaryConds<N>>(
315        &self,
316        block: BlockId,
317        bcs: B,
318    ) -> BlockBoundaryConds<N, B> {
319        BlockBoundaryConds {
320            inner: bcs,
321            physical_boundary_flags: self.block_physical_boundary_flags(block),
322        }
323    }
324
325    /// Produces a block ghost flags for the mesh before its most recent refinement.
326    pub(crate) fn old_block_boundary_classes(&self, block: BlockId) -> FaceArray<N, BoundaryClass> {
327        let flag = self.old_blocks.boundary_flags(block);
328
329        FaceArray::from_fn(|face| {
330            if flag.is_set(face) {
331                self.boundary[face]
332            } else {
333                BoundaryClass::Ghost
334            }
335        })
336    }
337
338    /// The level of a given block.
339    pub fn block_level(&self, block: BlockId) -> usize {
340        self.blocks.level(block)
341    }
342
343    /// The level of a block before the most recent refinement.
344    pub(crate) fn old_block_level(&self, block: BlockId) -> usize {
345        self.old_blocks.level(block)
346    }
347
348    // *******************************
349    // Node windows ******************
350
351    /// Finds bounds associated with a node window.
352    pub fn window_bounds(&self, block: BlockId, window: NodeWindow<N>) -> HyperBox<N> {
353        debug_assert!(self.block_space(block).contains_window(window));
354        let block_size = self.blocks.node_size(block);
355        let bounds = self.blocks.bounds(block);
356
357        HyperBox {
358            size: array::from_fn(|axis| {
359                bounds.size[axis] * window.size[axis] as f64 / block_size[axis] as f64
360            }),
361            origin: array::from_fn(|axis| {
362                bounds.size[axis] * window.origin[axis] as f64 / block_size[axis] as f64
363            }),
364        }
365    }
366
367    /// Retrieves the node window associated with a certain active cell on its block.
368    pub fn active_window(&self, cell: ActiveCellId) -> NodeWindow<N> {
369        NodeWindow {
370            origin: node_from_vertex(self.active_node_origin(cell)),
371            size: [self.width + 1; N],
372        }
373    }
374
375    /// Retrieves the node window that is optimal for interpolating values to the given position,
376    /// lying within the given active cell.
377    pub fn interpolate_window(&self, cell: ActiveCellId, position: [f64; N]) -> NodeWindow<N> {
378        let cell_offset = self.blocks.active_cell_position(cell);
379        let cell_bounds = self.tree().active_bounds(cell);
380        let cell_origin: [_; N] = array::from_fn(|axis| (self.width * cell_offset[axis]) as isize);
381
382        debug_assert!(cell_bounds.contains(position));
383
384        let local = cell_bounds.global_to_local(position);
385        let local_node: [_; N] =
386            array::from_fn(|axis| (local[axis] * self.width as f64).round() as isize);
387        let local_origin: [_; N] =
388            array::from_fn(|axis| local_node[axis] - self.width as isize / 2);
389
390        NodeWindow {
391            origin: array::from_fn(|axis| cell_origin[axis] + local_origin[axis]),
392            size: [self.width + 1; N],
393        }
394    }
395
396    /// Element associated with a given cell.
397    pub fn element_window(&self, cell: ActiveCellId) -> NodeWindow<N> {
398        let position = self.blocks.active_cell_position(cell);
399        // Round ghost to nearest even number to make sure diagonal coefficients of element
400        // actually correspond with newly refined points.
401        let buffer = 2 * (self.ghost / 2);
402        debug_assert!(buffer <= self.ghost);
403
404        let size = [self.width + 2 * buffer + 1; N];
405        let mut origin = [-(buffer as isize); N];
406
407        for axis in 0..N {
408            origin[axis] += (self.width * position[axis]) as isize
409        }
410
411        NodeWindow { origin, size }
412    }
413
414    /// Returns the window of nodes in a block corresponding to a given cell, including
415    /// no padding.
416    pub fn element_coarse_window(&self, cell: ActiveCellId) -> NodeWindow<N> {
417        let position = self.blocks.active_cell_position(cell);
418
419        let size = [self.width + 1; N];
420        let mut origin = [0; N];
421
422        for axis in 0..N {
423            origin[axis] += (self.width * position[axis]) as isize
424        }
425
426        NodeWindow { origin, size }
427    }
428
429    /// Retrieves an element from the mesh's element cache.
430    pub fn request_element(&mut self, width: usize, order: usize) -> Element<N> {
431        self.elements
432            .remove(&(width, order))
433            .unwrap_or_else(|| Element::uniform(width, order))
434    }
435
436    /// Reinserts an element into the mesh's element cache.
437    pub fn replace_element(&mut self, element: Element<N>) {
438        _ = self
439            .elements
440            .insert((element.width(), element.order()), element)
441    }
442
443    /// Retrieves the number of nodes along each axis of a cell.
444    /// This defaults to `[self.width; N]` but is increased by one
445    /// if the cell lies along a block boundary for a given axis.
446    pub fn cell_node_size(&self, cell: ActiveCellId) -> [usize; N] {
447        let block = self.blocks.active_cell_block(cell);
448        let size = self.blocks.size(block);
449        let position = self.blocks.active_cell_position(cell);
450
451        array::from_fn(|axis| {
452            if position[axis] == size[axis] - 1 {
453                self.width + 1
454            } else {
455                self.width
456            }
457        })
458    }
459
460    /// Returns the origin of an active cell in its block's `NodeSpace<N>`.
461    pub fn active_node_origin(&self, cell: ActiveCellId) -> [usize; N] {
462        let position = self.blocks.active_cell_position(cell);
463        array::from_fn(|axis| position[axis] * self.width)
464    }
465
466    /// Returns true if the given cell is on a boundary that does not contain
467    /// ghost nodes. If this is the case we must fall back to a lower order element
468    /// error approximation.
469    pub fn cell_needs_coarse_element(&self, cell: ActiveCellId) -> bool {
470        let block = self.blocks.active_cell_block(cell);
471        let block_size = self.blocks.size(block);
472        let boundary = self.block_boundary_classes(block);
473        let position = self.blocks.active_cell_position(cell);
474
475        for face in Face::iterate() {
476            let border = if face.side {
477                block_size[face.axis] - 1
478            } else {
479                0
480            };
481            let on_border = position[face.axis] == border;
482
483            if !boundary[face].has_ghost() && on_border {
484                return true;
485            }
486        }
487
488        false
489    }
490
491    // ***********************************
492    // Global info
493
494    /// Returns number of levels on the mesh.
495    pub fn num_levels(&self) -> usize {
496        self.tree().num_levels()
497    }
498
499    /// Returns the minimum spatial distance between any
500    /// two nodes on the mesh. Commonly used in conjunction
501    /// with a CFL factor to determine time step.
502    pub fn min_spacing(&self) -> f64 {
503        let max_level = self.num_levels() - 1;
504        let domain = self.tree.domain();
505
506        array::from_fn::<_, N, _>(|axis| {
507            domain.size[axis] / self.width as f64 / 2_f64.powi(max_level as i32)
508        })
509        .iter()
510        .min_by(|a, b| f64::total_cmp(a, b))
511        .cloned()
512        .unwrap_or(1.0)
513    }
514
515    /// Computes the spacing on a particular block (albeit not accounting for coarse-fine interfaces).
516    pub fn block_spacing(&self, block: BlockId) -> f64 {
517        let space = self.block_space(block);
518
519        space
520            .spacing()
521            .iter()
522            .min_by(|a, b| f64::total_cmp(a, b))
523            .cloned()
524            .unwrap_or(1.0)
525    }
526
527    /// Runs a computation in parallel on every single block in the mesh, providing
528    /// a `MeshStore` object for allocating scratch data.
529    pub fn block_compute<F: Fn(&Self, &MeshStore, BlockId) + Sync>(&mut self, f: F) {
530        #[cfg(feature = "parallel")]
531        self.blocks
532            .indices()
533            .par_bridge()
534            .into_par_iter()
535            .for_each(|block| {
536                let store = unsafe { self.stores.get_or_default() };
537                f(self, store, block);
538                store.reset();
539            });
540
541        #[cfg(not(feature = "parallel"))]
542        self.blocks.indices().for_each(|block| {
543            let store = unsafe { self.stores.get_or_default() };
544            f(self, store, block);
545            store.reset();
546        });
547    }
548
549    /// Runs a (possibily failable) computation in parallel on every single block in the mesh.
550    pub fn try_block_compute<E: Send, F: Fn(&Self, &MeshStore, BlockId) -> Result<(), E> + Sync>(
551        &mut self,
552        f: F,
553    ) -> Result<(), E> {
554        #[cfg(feature = "parallel")]
555        return self
556            .blocks
557            .indices()
558            .par_bridge()
559            .into_par_iter()
560            .try_for_each(|block| {
561                let store = unsafe { self.stores.get_or_default() };
562                let result = f(self, store, block);
563                store.reset();
564                result
565            });
566
567        #[cfg(not(feature = "parallel"))]
568        return self.blocks.indices().try_for_each(|block| {
569            let store = unsafe { self.stores.get_or_default() };
570            let result = f(self, store, block);
571            store.reset();
572            result
573        });
574    }
575
576    /// Runs a computation in parallel on every single old block in the mesh, providing
577    /// a `MeshStore` object for allocating scratch data.
578    pub(crate) fn old_block_compute<F: Fn(&Self, &MeshStore, BlockId) + Sync>(&mut self, f: F) {
579        #[cfg(feature = "parallel")]
580        (0..self.num_old_blocks())
581            .map(BlockId)
582            .par_bridge()
583            .into_par_iter()
584            .for_each(|block| {
585                let store = unsafe { self.stores.get_or_default() };
586                f(self, store, block);
587                store.reset();
588            });
589
590        #[cfg(not(feature = "parallel"))]
591        (0..self.num_old_blocks()).map(BlockId).for_each(|block| {
592            let store = unsafe { self.stores.get_or_default() };
593            f(self, store, block);
594            store.reset();
595        });
596    }
597
598    /// Computes the maximum l2 norm of all fields in the system.
599    pub fn l2_norm_system(&mut self, source: ImageRef) -> f64 {
600        source
601            .channels()
602            .map(|label| self.l2_norm(source.channel(label)))
603            .max_by(f64::total_cmp)
604            .unwrap()
605    }
606
607    /// Computes the maximum l-infinity norm of all fields in the system.
608    pub fn max_norm_system(&mut self, source: ImageRef) -> f64 {
609        source
610            .channels()
611            .map(|label| self.max_norm(source.channel(label)))
612            .max_by(f64::total_cmp)
613            .unwrap()
614    }
615
616    /// Returns the value of a function at the bottom left corner of
617    /// the mesh.
618    pub fn bottom_left_value(&self, src: &[f64]) -> f64 {
619        let space = self.block_space(BlockId(0));
620        let nodes = self.block_nodes(BlockId(0));
621
622        src[nodes][space.index_from_vertex([0; N])]
623    }
624
625    /// Computes the l2 norm of a field on the mesh.
626    pub fn l2_norm(&mut self, src: &[f64]) -> f64 {
627        let mut result = 0.0;
628
629        for block in self.blocks.indices() {
630            let space = self.block_space(block);
631            let size = space.cell_size();
632
633            let data = &src[self.block_nodes(block)];
634
635            let mut block_result = 0.0;
636
637            for node in space.inner_window() {
638                let index = space.index_from_node(node);
639                let mut value = data[index] * data[index];
640
641                for axis in 0..N {
642                    if node[axis] == 0 || node[axis] == size[axis] as isize {
643                        value *= 0.5;
644                    }
645                }
646
647                block_result += value;
648            }
649
650            for spacing in space.spacing() {
651                block_result *= spacing;
652            }
653
654            result += block_result;
655        }
656
657        result.sqrt()
658    }
659
660    pub fn oscillation_heuristic(&mut self, src: &[f64]) -> f64 {
661        let mut result: f64 = 0.0;
662
663        for block in self.blocks.indices() {
664            let space = self.block_space(block);
665            let cell_size = self.blocks.size(block);
666
667            let data = &src[self.block_nodes(block)];
668
669            for cell in IndexSpace::new(cell_size).iter() {
670                let node_offset: [usize; N] = array::from_fn(|i| self.width * cell[i]);
671                let node_size = [self.width + 1; N];
672
673                let mut maximum = f64::NEG_INFINITY;
674                let mut minimum = f64::INFINITY;
675
676                for local in IndexSpace::new(node_size).iter() {
677                    let global: [_; N] = array::from_fn(|axis| node_offset[axis] + local[axis]);
678                    let index = space.index_from_vertex(global);
679
680                    minimum = minimum.min(data[index]);
681                    maximum = maximum.max(data[index]);
682                }
683
684                let cell_result = (maximum - minimum).abs();
685
686                result += cell_result;
687            }
688        }
689
690        result
691    }
692
693    pub fn oscillation_heuristic_system(&mut self, source: ImageRef) -> f64 {
694        source
695            .channels()
696            .map(|cidx| self.oscillation_heuristic(source.channel(cidx)))
697            .max_by(f64::total_cmp)
698            .unwrap()
699    }
700
701    /// Computes the l-infinity norm of a field on a mesh.
702    pub fn max_norm(&mut self, src: &[f64]) -> f64 {
703        let mut result = 0.0f64;
704
705        for block in self.blocks.indices() {
706            let space = self.block_space(block);
707            let data = &src[self.block_nodes(block)];
708
709            let mut block_result = 0.0f64;
710
711            for node in space.inner_window() {
712                let index = space.index_from_node(node);
713                block_result = block_result.max(data[index]);
714            }
715
716            result = result.max(block_result);
717        }
718
719        result
720    }
721
722    /// Writes a textual summary of the Mesh to a sink. This is pimrarily used to
723    /// debug features of the mesh that can't be easily represented graphically (i.e in
724    /// .vtu files).
725    pub fn write_debug(&self, mut result: impl Write) {
726        writeln!(result, "// **********************").unwrap();
727        writeln!(result, "// Cells ****************").unwrap();
728        writeln!(result, "// **********************").unwrap();
729        writeln!(result).unwrap();
730
731        for cell in self.tree.active_cell_indices() {
732            writeln!(result, "Cell {}", cell.0).unwrap();
733            writeln!(result, "    Bounds {:?}", self.tree.active_bounds(cell)).unwrap();
734            writeln!(
735                result,
736                "    Block {:?}",
737                self.blocks.active_cell_block(cell)
738            )
739            .unwrap();
740            writeln!(
741                result,
742                "    Block Position {:?}",
743                self.blocks.active_cell_position(cell)
744            )
745            .unwrap();
746        }
747
748        writeln!(result).unwrap();
749        writeln!(result, "// **********************").unwrap();
750        writeln!(result, "// Blocks ***************").unwrap();
751        writeln!(result, "// **********************").unwrap();
752        writeln!(result).unwrap();
753
754        for block in self.blocks.indices() {
755            writeln!(result, "Block {block:?}").unwrap();
756            writeln!(result, "    Bounds {:?}", self.blocks.bounds(block)).unwrap();
757            writeln!(result, "    Size {:?}", self.blocks.size(block)).unwrap();
758            writeln!(result, "    Cells {:?}", self.blocks.active_cells(block)).unwrap();
759            writeln!(
760                result,
761                "    Vertices {:?}",
762                self.block_space(block).cell_size()
763            )
764            .unwrap();
765            writeln!(
766                result,
767                "    Boundary {:?}",
768                self.blocks.boundary_flags(block)
769            )
770            .unwrap();
771        }
772
773        writeln!(result).unwrap();
774        writeln!(result, "// **********************").unwrap();
775        writeln!(result, "// Neighbors ************").unwrap();
776        writeln!(result, "// **********************").unwrap();
777        writeln!(result).unwrap();
778
779        writeln!(result, "// Fine Neighbors").unwrap();
780
781        for neighbor in self.neighbors.fine() {
782            writeln!(result, "Fine Neighbor").unwrap();
783
784            writeln!(
785                result,
786                "    Block: {:?}, Neighbor: {:?}",
787                neighbor.block, neighbor.neighbor,
788            )
789            .unwrap();
790            writeln!(
791                result,
792                "    Lower: Cell {}, Neighbor {}, Region {}",
793                neighbor.a.cell.0, neighbor.a.neighbor.0, neighbor.a.region,
794            )
795            .unwrap();
796            writeln!(
797                result,
798                "    Upper: Cell {}, Neighbor {}, Region {}",
799                neighbor.b.cell.0, neighbor.b.neighbor.0, neighbor.b.region,
800            )
801            .unwrap();
802        }
803
804        writeln!(result).unwrap();
805        writeln!(result, "// Direct Neighbors").unwrap();
806
807        for neighbor in self.neighbors.direct() {
808            writeln!(result, "Direct Neighbor").unwrap();
809
810            writeln!(
811                result,
812                "    Block: {:?}, Neighbor: {:?}",
813                neighbor.block, neighbor.neighbor,
814            )
815            .unwrap();
816            writeln!(
817                result,
818                "    Lower: Cell {}, Neighbor {}, Region {}",
819                neighbor.a.cell.0, neighbor.a.neighbor.0, neighbor.a.region,
820            )
821            .unwrap();
822            writeln!(
823                result,
824                "    Upper: Cell {}, Neighbor {}, Region {}",
825                neighbor.b.cell.0, neighbor.b.neighbor.0, neighbor.b.region,
826            )
827            .unwrap();
828        }
829
830        writeln!(result).unwrap();
831        writeln!(result, "// Coarse Neighbors").unwrap();
832
833        for neighbor in self.neighbors.coarse() {
834            writeln!(result, "Coarse Neighbor").unwrap();
835
836            writeln!(
837                result,
838                "    Block: {:?}, Neighbor: {:?}",
839                neighbor.block, neighbor.neighbor,
840            )
841            .unwrap();
842            writeln!(
843                result,
844                "    Lower: Cell {}, Neighbor {}, Region {}",
845                neighbor.a.cell.0, neighbor.a.neighbor.0, neighbor.a.region,
846            )
847            .unwrap();
848            writeln!(
849                result,
850                "    Upper: Cell {}, Neighbor {}, Region {}",
851                neighbor.b.cell.0, neighbor.b.neighbor.0, neighbor.b.region,
852            )
853            .unwrap();
854        }
855
856        writeln!(result).unwrap();
857        writeln!(result, "// **********************").unwrap();
858        writeln!(result, "// Interfaces ***********").unwrap();
859        writeln!(result, "// **********************").unwrap();
860        writeln!(result).unwrap();
861    }
862}
863
864impl<const N: usize> Clone for Mesh<N> {
865    fn clone(&self) -> Self {
866        Self {
867            tree: self.tree.clone(),
868            width: self.width,
869            ghost: self.ghost,
870            boundary: self.boundary,
871
872            blocks: self.blocks.clone(),
873            neighbors: self.neighbors.clone(),
874            interfaces: self.interfaces.clone(),
875
876            refine_flags: self.refine_flags.clone(),
877            coarsen_flags: self.coarsen_flags.clone(),
878
879            regrid_map: self.regrid_map.clone(),
880            old_blocks: self.old_blocks.clone(),
881            old_cell_splits: self.old_cell_splits.clone(),
882
883            stores: UnsafeThreadCache::default(),
884            elements: HashMap::default(),
885        }
886    }
887}
888
889impl<const N: usize> Default for Mesh<N> {
890    fn default() -> Self {
891        let mut result = Self {
892            tree: Tree::new(HyperBox::UNIT),
893            width: 4,
894            ghost: 1,
895            boundary: FaceArray::default(),
896
897            blocks: TreeBlocks::new([4; N], 1),
898            neighbors: TreeNeighbors::default(),
899            interfaces: TreeInterfaces::default(),
900
901            refine_flags: Vec::default(),
902            coarsen_flags: Vec::default(),
903
904            regrid_map: Vec::default(),
905            old_blocks: TreeBlocks::new([4; N], 1),
906            old_cell_splits: Vec::default(),
907
908            stores: UnsafeThreadCache::default(),
909            elements: HashMap::default(),
910        };
911
912        result.build();
913
914        result
915    }
916}
917
918impl<const N: usize> DataSize for Mesh<N> {
919    const IS_DYNAMIC: bool = false;
920    const STATIC_HEAP_SIZE: usize = 0;
921
922    fn estimate_heap_size(&self) -> usize {
923        self.tree.estimate_heap_size()
924            + self.blocks.estimate_heap_size()
925            + self.neighbors.estimate_heap_size()
926            + self.interfaces.estimate_heap_size()
927            + self.refine_flags.estimate_heap_size()
928            + self.coarsen_flags.estimate_heap_size()
929            + self.regrid_map.estimate_heap_size()
930            + self.old_blocks.estimate_heap_size()
931            + self.old_cell_splits.estimate_heap_size()
932    }
933}
934
935/// Helper for serializing meshes using minimal data.
936#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
937struct MeshSer<const N: usize> {
938    tree: TreeSer<N>,
939    width: usize,
940    ghost: usize,
941    boundary: FaceArray<N, BoundaryClass>,
942}
943
944impl<const N: usize> From<Mesh<N>> for MeshSer<N> {
945    fn from(value: Mesh<N>) -> Self {
946        MeshSer {
947            tree: value.tree.into(),
948            width: value.width,
949            ghost: value.ghost,
950            boundary: value.boundary,
951        }
952    }
953}
954
955impl<const N: usize> From<MeshSer<N>> for Mesh<N> {
956    fn from(value: MeshSer<N>) -> Self {
957        let mut result = Mesh {
958            tree: value.tree.into(),
959            width: value.width,
960            ghost: value.ghost,
961            boundary: value.boundary,
962            blocks: TreeBlocks::new([value.width; N], value.ghost),
963            old_blocks: TreeBlocks::new([value.width; N], value.ghost),
964
965            ..Default::default()
966        };
967        result.build();
968        result
969    }
970}
971
972#[derive(Clone, Debug)]
973pub struct BlockBoundaryConds<const N: usize, I> {
974    inner: I,
975    /// Physical boundary mask for various faces.
976    physical_boundary_flags: FaceMask<N>,
977}
978
979impl<const N: usize, I: SystemBoundaryConds<N>> SystemBoundaryConds<N>
980    for BlockBoundaryConds<N, I>
981{
982    fn kind(&self, channel: usize, face: Face<N>) -> BoundaryKind {
983        if self.physical_boundary_flags.is_set(face) {
984            self.inner.kind(channel, face)
985        } else {
986            BoundaryKind::Custom
987        }
988    }
989
990    fn dirichlet(&self, channel: usize, position: [f64; N]) -> DirichletParams {
991        self.inner.dirichlet(channel, position)
992    }
993
994    fn radiative(&self, channel: usize, position: [f64; N]) -> crate::prelude::RadiativeParams {
995        self.inner.radiative(channel, position)
996    }
997}
998
999#[cfg(test)]
1000mod tests {
1001    use super::*;
1002    use rand::Rng;
1003    #[test]
1004    fn fuzzy_serialize() -> eyre::Result<()> {
1005        let mut mesh = Mesh::<2>::new(
1006            HyperBox::UNIT,
1007            6,
1008            3,
1009            [
1010                [BoundaryClass::Ghost, BoundaryClass::OneSided],
1011                [BoundaryClass::Ghost, BoundaryClass::OneSided],
1012            ]
1013            .into(),
1014        );
1015        mesh.refine_global();
1016
1017        // Randomly refine mesh
1018        let mut rng = rand::rng();
1019        for _ in 0..4 {
1020            // Randomaize refinement
1021            rng.fill(mesh.refine_flags.as_mut_slice());
1022            // Balance flags
1023            mesh.balance_flags();
1024            // Perform refinement
1025            mesh.regrid();
1026        }
1027
1028        // Serialize tree
1029        let data = ron::to_string(&mesh)?;
1030        let mesh2: Mesh<2> = ron::from_str(data.as_str())?;
1031
1032        assert_eq!(mesh.tree, mesh2.tree);
1033        assert_eq!(mesh.width, mesh2.width);
1034        assert_eq!(mesh.ghost, mesh2.ghost);
1035        assert_eq!(mesh.boundary, mesh2.boundary);
1036        assert_eq!(mesh.blocks, mesh2.blocks);
1037        assert_eq!(mesh.neighbors, mesh2.neighbors);
1038        assert_eq!(mesh.interfaces, mesh2.interfaces);
1039
1040        Ok(())
1041    }
1042}