Skip to content

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.

OperationCells returned
borderCells in the region with a neighbour outside it
interiorCells in the region whose neighbours all belong to it
haloCells outside the region that touch it, at the same level
growThe 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_polygon gives one cell as a closed unit-sphere GI.Polygon, cell_boundary its vertices.

  • cell_polygons collects the polygons of a MultiOrderCellSet; a CellVector, PartialGrid, or CellLookup maps cell_polygon over 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.

julia
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.

OperationDefault 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
julia
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.

source
DiscreteGlobalGrids.border Function
julia
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.

source
DiscreteGlobalGrids.interior Function
julia
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.

source

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.

julia
grown = DGG.grow(region, 1)
length(grown) == DGG.ncells(region) + length(outside)
true

Growth uses a single level. Expand a MultiOrderCellSet to the desired level with expand before applying it.

DiscreteGlobalGrids.Engine.grow Function
julia
grow(region, n; connectivity = Vertex()) -> CellVector

The 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.

julia
grow(subset, 2)                      # two rings of receptive field
grow(subset, 1; connectivity = Edge())
source

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
julia
subtree(sys::AbstractHierarchicalGridSystem, c::AbstractCellIndex, l::Integer; bucket_size = 0) -> PartialGrid

c'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.

source

Index ​