Region boundaries
Use these operations to find the cells along a region's edge, fetch values just outside it, or enlarge it by a few layers of cells. A region is a collection at one level, such as a PartialGrid, CellVector or CellLookup.
| Operation | Cells returned |
|---|---|
border | Cells in the region with a neighbour outside it |
interior | Cells in the region whose neighbours all belong to it |
halo | Cells outside the region that touch it, at the same level |
grow | The region plus a requested number of neighbouring layers |
border and interior partition the region. Its halo supplies the outside values needed to compute a stencil at the border. Holes also have boundaries: cells just inside a hole can belong to the halo. A complete global grid has an empty border and halo, and all of its cells belong to the interior.
These operations return cells or their indices; cell geometry comes from the functions below, and Neighbours and stencils covers neighbours around individual cells and adjacency tables.
cell_polygongives one cell as a closed unit-sphereGI.Polygon,cell_boundaryits vertices.cell_polygonscollects the polygons of aMultiOrderCellSet; aCellVector,PartialGrid, orCellLookupmapscell_polygonover its own cells.
Find the inside and outside of an edge
This example uses the descendants of one IGeo7 cell as a region. The border and interior divide its cells; the halo contains the touching cells outside it.
import DiscreteGlobalGrids as DGG
sys = DGG.IGeo7System()
root = first(DGG.CellVector(DGG.levelgrid(sys, 1)))
region = DGG.subtree(sys, root, 3)
edge = collect(DGG.border(region; cells = true))
inside = collect(DGG.interior(region; cells = true))
outside = collect(DGG.halo(region; cells = true))
(; border = length(edge), interior = length(inside), halo = length(outside),
region = DGG.ncells(region))(border = 20, interior = 21, halo = 25, region = 41)connectivity = Edge() requires a shared edge; the default Vertex() also counts corner contact. Use the same connectivity for the border, halo and stencil that will consume them.
Use the indices with data
border, interior and halo return lazy iterators. By default they yield indices in ascending order; cells = true requests cell ids.
| Operation | Default index space |
|---|---|
border(region) and interior(region) | Local positions in the region's data array |
halo(region) | Positions in the complete level grid, where the outside cells live |
Use local border indices to read or update values stored on the region. Use halo indices to fetch context from the complete grid. For data on disk, chunked sweeps manage these reads and index translations.
collect(iterator) materialises the indices or ids; Set(iterator) makes a membership set. Some iterators have no cheap exact length, so collect them when you need to count the results. Contributor details are in Boundary traversal engines.
DiscreteGlobalGrids.halo Function
halo(region; connectivity = Vertex(), cells = false)The cells immediately OUTSIDE region that touch one of its members, lazily, each exactly once, in ascending global index.
A region is a subset of one complete level — PartialGrid, an AbstractCellVector or AbstractCellLookup — or a complete AbstractGrid, which has no outside and therefore an empty halo. Vertex() counts vertex contact, Edge() requires a shared edge.
Yields global indices: a halo cell is by definition absent from the region and has no local index in it. cells = true yields cell ids instead.
A cell punched out of the middle of a region is outside it and touches it, so it joins the halo.
collect gives a Vector and Set gives a membership-queryable set — both are Base's contracts over an iterator, and neither is overloaded to reach some other product. sizehint gives a cheap size estimate where one exists.
The walk is serial; adjacency is the verb that threads.
A MultiOrderCellSet has no halo: its members may sit at different levels, so there is no single level to answer at. Use member_neighbors.
See also border and interior, the same boundary from inside.
DiscreteGlobalGrids.border Function
border(region; connectivity = Vertex(), cells = false)
interior(region; connectivity = Vertex(), cells = false)The members of region that do (border) or do not (interior) have a neighbour outside it, lazily, in ascending local index. The two are disjoint and together are the region.
Yields local indices — 1:length(region), the index a data vector laid out against the region is read by. cells = true yields cell ids instead. (halo yields global indices instead, for the reason it gives.) The region types are halo's; a complete grid has no cell with an absent neighbour, so its border is empty and its interior is all of it.
A region holding a whole rooted subtree walks its system's O(border) automaton. Every other region scans its own cells and compares each clipped one-ring against the complete one, O(cells · degree).
Both walks are serial; adjacency is the verb that threads.
DiscreteGlobalGrids.interior Function
border(region; connectivity = Vertex(), cells = false)
interior(region; connectivity = Vertex(), cells = false)The members of region that do (border) or do not (interior) have a neighbour outside it, lazily, in ascending local index. The two are disjoint and together are the region.
Yields local indices — 1:length(region), the index a data vector laid out against the region is read by. cells = true yields cell ids instead. (halo yields global indices instead, for the reason it gives.) The region types are halo's; a complete grid has no cell with an absent neighbour, so its border is empty and its interior is all of it.
A region holding a whole rooted subtree walks its system's O(border) automaton. Every other region scans its own cells and compares each clipped one-ring against the complete one, O(cells · degree).
Both walks are serial; adjacency is the verb that threads.
Enlarge a region
grow(region, n) includes the original region and n layers of neighbours. Use it when a calculation needs a wider margin around an area of interest.
grown = DGG.grow(region, 1)
length(grown) == DGG.ncells(region) + length(outside)trueGrowth uses a single level. Expand a MultiOrderCellSet to the desired level with expand before applying it.
DiscreteGlobalGrids.Engine.grow Function
grow(region, n; connectivity = Vertex()) -> CellVectorThe region plus n rings of level-grid neighbours, at the region's own level. n == 0 returns the region as a CellVector and nothing else.
region is a PartialGrid, a CellVector, or a complete grid; a CellLookup is grown through parent(lk). Each ring is one halo walk, so a rooted subtree grid uses the subtree engine on its first step and the window walk afterwards — growth has no root.
Cost is n halo walks plus O(m log m) per step to merge that step's m windows and halo cells. A MultiOrderCellSet has no method here: it has no single level to walk at, so expand it first.
grow(subset, 2) # two rings of receptive field
grow(subset, 1; connectivity = Edge())Construct a region from an ancestor
subtree selects all descendants of one cell at a target level. It is useful for regions aligned with the grid hierarchy, as in the example above.
DiscreteGlobalGrids.subtree Function
subtree(sys::AbstractHierarchicalGridSystem, c::AbstractCellIndex, l::Integer; bucket_size = 0) -> PartialGridc's subtree at level l, as a region: the PartialGrid holding every level-l descendant of c, rooted at c.
This is the one spelling of a subtree. It is what gives the subtree family the same currency every other region has — halo(subtree(sys, c, l)), border(subtree(sys, c, l)), adjacency(subtree(sys, c, l); halo = 1) — rather than a parallel set of verbs taking (sys, c, l) argument tuples.
l < level(c) throws an ArgumentError. Construction is O(1) where has_sorted_subtrees holds, since the ids are then the level grid's own over a known index range; elsewhere it materialises descendants. bucket_size is PartialGrid's.