Grids

FlowGeometries.Grids.AllActive — Type
AllActive(size)

The mask of a grid where every cell participates, stored as its size alone. getindex is a constant the compiler can fold, and count is length without a scan.

source
FlowGeometries.Grids.ArcEmbedding — Type
ArcEmbedding(R)

Points on the reference sphere of radius R, where the metric is the great-circle arc. A radius has to be converted to the chord it subtends.

source
FlowGeometries.Grids.AxisSummary — Type
AxisSummary{T}

One direction's reductions: the values origin, bounds, extent, minimum_spacing and maximum_spacing report.

A stretched axis answers each of these by scanning, and a distance query reads them through Connectivity.MetricTopology, which is the default topology on every per-cell entry point. Holding them keeps that construction O(1) and keeps a query off the coordinates.

isbits, five numbers per direction, filled once by _axis_summary: one pass per axis, the O(∑ Nᵈ) the measure factors already cost. first is kept apart from lo because a descending axis starts at its largest value.

source
FlowGeometries.Grids.CartesianCells — Type
CartesianCells()

A cell is an NTuple{N,Int} into an N-dimensional array of cells, so a traversal walks CartesianIndices and converts to a linear index to report. Rectilinear, curvilinear and every panel layout.

source
FlowGeometries.Grids.CellListIndex — Type
CellListIndex

A uniform-bin spatial index over the embedded cell centres: points are bucketed by which bin of side h they fall in, and a query visits the bins its ball can reach.

Three properties let it run on a device, where a tree cannot:

  • it is arrays only — bin offsets, point ids and the bin each sits in — so Adapt moves it like any other field, and it holds no copy of the coordinates, which stay on the grid;
  • every point lands in exactly one bin, periodicity wrapping the bin coordinate and leaving the point where it is, so a query emits each cell once and needs no candidate buffer to deduplicate into. fold_candidates is therefore a fold, with no list to materialize;
  • bins are hashed into O(n) buckets, so the memory is independent of h. A dense lattice over a sphere binned at 100 km is (2R/h)³ ≈ 2×10⁶ mostly empty cells, and grows as h shrinks.

Build it for the radius you intend to query at: h is that radius, so a query touches 3ᴰ bins. A much larger radius still works and costs (2⌈r/h⌉+1)ᴰ bins.

source
FlowGeometries.Grids.CellMesh — Type
CellMesh(cell_ptr, cell_nodes, node_ptr, node_cells)
CellMesh(cell_ptr, cell_nodes, nnodes)

A node set's cells, and the nodes each one joins — both directions, each as CSR.

Node→node adjacency says which nodes are linked; it does not say which nodes bound a face. The cells do, and an area, a flux through an edge, or an interpolation inside a triangle is defined on them. A triangulation produces them, and the tessellation that computes a node set's Voronoi areas builds one, which is the mesh held here.

  • cell_nodes[cell_ptr[c] : cell_ptr[c+1]-1] are the nodes of cell c, in order around it
  • node_cells[node_ptr[i] : node_ptr[i+1]-1] are the cells incident on node i

Both are O(n): a triangulation of n nodes has about 2n triangles and 6n entries either way. The second is the transpose of the first and the two-argument form builds it.

The cells are a triangulation or the caller's own. A k-nearest graph is neither planar nor symmetric once truncated, and its cells tile nothing, so it cannot serve here.

source
FlowGeometries.Grids.ChordEmbedding — Type
ChordEmbedding()

The embedded distance already is the metric, or a lower bound on it: (λ, φ, r) on a sphere, where the metric is the 3-D chord, and geodetic coordinates on a spheroid, where the ECEF chord is at most the geodesic. A radius passes through unchanged; under-approximating means the query over-returns, and an index is allowed to over-return.

source
FlowGeometries.Grids.CubedSphereGrid — Type
CubedSphereGrid(geometry, n; mask = nothing)

The gnomonic cubed sphere as a grid: six n × n panels of equiangular cells, 6n² in all.

It stores n, the geometry and the mask. A cell's coordinates are its panel-local angles through the gnomonic map, its neighbours are the panel-interior offsets and the exact seam fold, and its area is the closed-form solid angle of its gnomonic rectangle — so all three are arithmetic in (n, cell), and Base.summarysize is the same at every n.

Cells are numbered panel by panel with i fastest, matching SphericalSampling.cubed_sphere_points. panel_cell and cell_id convert between a cell id and its (panel, i, j).

Coordinates are (λ, φ): coords(grid, k) reads one cell, and materialize gives the whole cloud as dense vectors.

source
FlowGeometries.Grids.CurvilinearGrid — Type
CurvilinearGrid{T, G, N, TP, C, KC, MA, B}

Curvilinear grid whose cell-center coordinates are N-dimensional arrays (e.g. an orthogonal curvilinear mesh). coordinates holds one N-D cell-center array per direction.

At N = 2 the cell measure is the exact quadrilateral area through the cell's four vertices. Those vertex arrays are what corners holds, one per direction and one larger in every direction. They are retained when the caller supplies them or asks for them with keep_corners = true, and are otherwise construction input to the area kernel alone — so corners is nothing on a grid built from centres, and has_corners reports which. In any dimension other than 2 the measure is the caller's to supply: the corner-area kernel is a 2-D algorithm with no N-D generalization here.

Type parameters

  • T: coordinate float type. G<:AbstractGeometry{T} is tied to it, so a mismatched-eltype geometry raises a type error. T therefore precedes G: Julia binds type parameters left to right, and G<:AbstractGeometry{T} requires T already bound.
  • N: number of coordinate directions.
  • C: tuple type of the center coordinate arrays.
  • KC: tuple type of the cell-vertex arrays, or Nothing where they were not retained.
  • MA: array type of the derived measure field — independent of C, since it is a computed field with no reason to match the coordinate arrays' storage type.
  • B: array type of the active mask.
source
FlowGeometries.Grids.CurvilinearGrid — Method
CurvilinearGrid(geometry, coords..., mask; measure=nothing, corners=nothing, …)
CurvilinearGrid(geometry, coords..., measure, mask; corners=nothing, …)

Build a curvilinear grid in any number of directions from one N-D cell-center coordinate array per direction. mask is the trailing array; a measure array before it is used verbatim (common when a dataset ships its own cell areas), and may equally be given as the measure keyword.

With no measure supplied, one is computed from the cell-vertex arrays — at N = 2 only, as the exact quadrilateral cell area. Spherical cells use the exact spherical-quadrilateral area, the spherical excess of the two triangles through the cell's four corner directions (see Geometry.spherical_excess); Cartesian cells the exact planar shoelace area. That kernel is a 2-D algorithm with no N-D generalization here, so in any other dimension the measure must be given.

Pass corners (a tuple of arrays, each one larger than the centers in every direction) for exact cell vertices, e.g. from the source mesh's own vertex grid; otherwise they are reconstructed from the centers per direction (see _centers_to_corners), which requires at least 2 cells across.

Corners supplied this way are kept on the grid, and reconstructed ones are input to the area kernel — pass keep_corners = true to keep those too, at one array per direction. Reconstruction happens only where something needs it, so a grid given its own measure derives none.

periodic is a Bool (applied to direction 1) or an NTuple{N,Bool}. When omitted, direction-1 periodicity is auto-detected the same way as StructuredGrid (full-circle spherical longitude), and every other direction is bounded.

source
FlowGeometries.Grids.FlatCells — Type
FlatCells()

A cell is one integer into a flat list, so the index a traversal walks and the index it reports are the same number. Node sets, and the pixelizations whose cells are enumerated by a single id.

source
FlowGeometries.Grids.FormulaNeighbors — Type
FormulaNeighbors()

Neighbours are closed-form arithmetic on the cell id, with no graph and no coordinates stored: a pixelization's face tables, a ring grid's in-ring and adjacent-ring maps, a panel seam.

A layout with this trait supplies formula_neighbors.

source
FlowGeometries.Grids.GridMeasure — Type
GridMeasure(grid)

The cell measure of a layout whose measure is a formula, computed per entry and stored nowhere.

A genuine AbstractVector: indexing, broadcasting and collect behave as for the dense equivalent, and measure(grid, k) is what an entry is. sum goes through _total_measure, which a layout covering a known region answers in O(1).

source
FlowGeometries.Grids.HEALPixGrid — Type
HEALPixGrid(geometry, nside; scheme = SphericalSampling.Ring(), mask = nothing)

The HEALPix pixelization as a grid: 12·nside² equal-area pixels on 4·nside − 1 iso-latitude rings.

It stores nside, the scheme, the geometry and the mask. A pixel's coordinates, its neighbours and its area are each closed-form in (nside, pixel), so those four fields are the whole grid: Base.summarysize is the same at nside = 1024 — 12.6 million pixels — as at nside = 1.

nlon varies by ring, so this is a layout of its own, and it answers the three traits a neighbourhood algorithm asks of one: cells are FlatCells (a pixel is one id), adjacency is FormulaNeighbors (the face tables of Górski et al.), and a distance query enumerates through IndexedCandidates.

Coordinates are (λ, φ): coords(grid, i) reads one pixel, and materialize gives the whole cloud as dense vectors.

source
FlowGeometries.Grids.IcosahedralGrid — Type
IcosahedralGrid(geometry, frequency; mask = nothing)

The frequency-ν icosahedral geodesic as a grid: 10ν² + 2 vertices, twelve of them pentagonal and the rest hexagonal.

It stores ν, the geometry and the mask. Vertex ids are assigned by topology — the twelve corners, then each macro-edge's interior, then each face's — so a vertex's position, its neighbours and its dual-cell area are each arithmetic in (ν, id): reading the numbering backwards gives the lattice positions the vertex occupies, and everything follows from those.

A vertex sits on one face if a face owns it, two if a macro-edge does, and five if it is a corner. That count is its valence, so the twelve corners are the grid's twelve pentagons.

The measure is the spherical Voronoi dual, computed from the vertex's own incident triangles — each triangle split among its three vertices by the arcs from its circumcenter to its edge midpoints. It costs a fan of spherical excesses per cell, so use measure_array when sweeping every cell.

Coordinates are (λ, φ): coords(grid, id) reads one vertex, and materialize gives the whole cloud as dense vectors.

source
FlowGeometries.Grids.IndexStencilNeighbors — Type
IndexStencilNeighbors()

Neighbours are offsets in the cell index space, wrapped or clipped per direction — the whole of what Connectivity.IndexTopology carries. Coordinates never enter.

source
FlowGeometries.Grids.RingGrid — Type
RingGrid(geometry, sampling::AbstractReducedGaussianSampling; mask = nothing)
RingGrid(geometry, latitudes, nlon_per_ring, ring_area; mask = nothing)

Iso-latitude rings whose longitude count varies by ring: the reduced Gaussian family, of which the octahedral grid is the operational case.

Storage is O(nrings) = O(√npoints) — one latitude, one longitude count, one offset and one cell area per ring. A point's longitude is (j−1)·2π/nlon[r], and its latitude and area are its ring's, so all three come from the ring's four numbers. At N = 1280 that is four vectors of 2560 entries, covering 6.6 million points.

Cells are numbered ring by ring from the north, matching SphericalSampling.ring_range and the order spherical_points writes.

Coordinates are (λ, φ): coords(grid, i) reads one point, and materialize gives the whole cloud as dense vectors.

source
FlowGeometries.Grids.RingwiseVector — Type
RingwiseVector(value, offset)

The cell measure of a ring grid, held as its per-ring values.

Every cell of an iso-latitude ring has the same area, so npoints entries carry only nrings numbers — O(√npoints). A genuine AbstractVector: indexing, broadcasting and collect behave as for the dense equivalent.

sum is specialized to Σᵣ nlonᵣ·valueᵣ, O(nrings) against the dense O(npoints).

source
FlowGeometries.Grids.RotatedGrid — Type
RotatedGrid(base, rotation, map)

A rectilinear grid whose cells are reported in a rotated frame: the mesh is base's, and a cell's position is map(rotation, λ, φ) evaluated where it is asked for.

rotate and unrotate build one. map is which direction the frame goes — Geometry.rotate for a mesh given in geographic coordinates and reported in the rotated frame, Geometry.unrotate for the reverse — and it is a singleton function type, so the choice resolves at compile time and the grid stays isbits apart from what base already holds.

Nothing is materialized. A rotation is an isometry of the sphere, so the cell measure carries over exactly and this shares base's array. The mesh, the mask, the index topology and the wrap lengths are base's too, all being properties of the cell lattice. What differs is where each cell sits, which costs two sincos pairs per coordinate access.

source
FlowGeometries.Grids.SeparableMeasure — Type
SeparableMeasure(factors)

The cell measure of a rectilinear grid, held as its per-axis factors.

Every measure this package supports on such a grid is a product of one factor per axis (see _measure_factors), so the ∏ Nᵈ entries carry only ∑ Nᵈ numbers. It is a genuine AbstractArray: indexing, broadcasting and collect behave as for the dense equivalent.

sum is specialized to ∏ᵈ ∑ᵢ wᵈᵢ, O(∑ Nᵈ) against the dense O(∏ Nᵈ).

source
FlowGeometries.Grids.SeparableWindow — Type
SeparableWindow()

The grid has separable axes, so a bounding index window per direction contains every cell within a given distance and is O(1) to compute — see Connectivity.metric_window. No index is needed and nothing is buffered.

source
FlowGeometries.Grids.SlabMeasure — Type
SlabMeasure(lead, slab, rest)

The cell measure of a rectilinear grid with a single coupled pair of directions: measure[i, j, k, l…] == lead[i] · slab[j, k] · rest[1][l] · ….

A geodetic (λ, φ, h) volume element is (N(φ)+h)·cosφ·(M(φ)+h)·Δλ·Δφ·Δh. The height offsets both curvature radii, so no product of per-axis factors reproduces it and SeparableMeasure cannot hold it. Longitude enters none of that coupling, so only (φ, h) are stored together, giving Nφ·Nh + Nλ + … numbers against the dense ∏ Nᵈ.

sum and extrema are specialized on the same argument as the separable case: the whole is a product of independent groups, so its total is the product of their totals and its extremes are attained with every group at one of its own.

source
FlowGeometries.Grids.StaggeredGrid — Type
StaggeredGrid(geometry, axes...; mask = nothing, topology = nothing, period = nothing)
StaggeredGrid(center::StructuredGrid)

One rectilinear mesh, readable at any staggered location: a family of grids.

A location is one FlowGeometries.Discretization.AbstractLocation per direction — Center() where the samples are the cell centres, Face() where they are the cell boundaries. On an Arakawa C arrangement the tracer sits at all-centres, the velocity component along direction d at Face() in d and Center() elsewhere, and the vorticity at Face() in the two directions it circulates about. grid_at returns the ordinary StructuredGrid at any of them, so every operator that already works on a rectilinear grid works at every location without knowing this type exists.

The face axes are built once, at construction. A face axis derived from a uniform primal axis is itself an Axes.UniformAxis — see FlowGeometries.Discretization.faces — so a uniform mesh stays uniform to every method that dispatches on spacing.

A periodic direction of N cells has N faces, its last face being its first; a bounded direction has N+1.

A mask is given over the centre cells, and a staggered point is active where every centre it is built from is. This is the finite-volume rule: a velocity face between an active and an inactive cell is a boundary, on the same footing as the least-squares gradient's unresolved direction and a stencil's masked cell.

source
FlowGeometries.Grids.StructuredGrid — Type
StructuredGrid{T, G, N, S, TP, C, AT, BT}

Rectilinear N-dimensional grid, for any N: one coordinate vector per direction (coordinates), an N-D cell measure (length in 1-D, area in 2-D, volume in 3-D, the N-D measure in general), an N-D active mask, per-direction topology, and the wrap period of each periodic direction.

Type parameters

  • T: coordinate float type. G<:AbstractGeometry{T} is tied to it, so a mismatched-eltype geometry raises a type error. T therefore precedes G: Julia binds type parameters left to right, and G<:AbstractGeometry{T} requires T already bound. CurvilinearGrid and UnstructuredGrid carry the same convention.
  • N: number of coordinate directions.
  • S: the SphericalSampling recipe the axes came from, or Nothing for axes given directly. A zero-size singleton, so it costs nothing to carry and lets quadrature exactness, the matching latitude weights and a rebuild at another resolution dispatch on which node set this is — none of which the coordinates alone determine. See sampling.
  • TP: the per-direction AbstractTopology — singletons, so no storage, readable from the type.
  • C: a heterogeneous NTuple{N,AbstractVector{T}}. Each axis independently keeps whatever concrete AbstractVector{T} type it was constructed with — an Axes.UniformAxis, a plain Vector, a device array — so one axis can be uniform while another is stretched. A UniformAxis's type is a compile-time proof of constant spacing that spacing_trait and spacing read without touching a coordinate, and a common vector type across the axes erases it.
  • AT, BT: array types of the derived measure and of the active mask.
source
FlowGeometries.Grids.StructuredGrid — Method
StructuredGrid(geometry, axes...; mask = nothing, topology = nothing, period = nothing)
StructuredGrid(geometry, axes..., mask; topology = nothing, period = nothing)

Build a rectilinear grid in any number of dimensions from one coordinate vector per direction, pre-computing the separable cell measure from the geometry.

Each axis may independently be uniform or stretched, and keeps whichever it is in its own type — see isuniform. Axes are adapted to the geometry's float type T by _to_axis, which preserves uniformity and keeps a device-resident axis on its device.

For a SphericalGeometry the directions are (λ, φ, r, …): longitude, geographic latitude, and — in 3-D and above — the absolute radius from the origin. (A Geometry.SpheroidGeometry's third direction is a height above the surface instead.) Measures are the metric elements R·Δλ, R²cosφ·Δλ·Δφ and r²cosφ·Δλ·Δφ·Δr, with further directions entering as plain widths. A CartesianGeometry measure is the product of the per-direction widths.

Keywords

  • mask: an N-D Bool array of active cells. Omit it (or pass nothing) for an all-active grid, which stores only its size — see AllActive. It may also be given positionally, after the axes.
  • topology: per-direction closure. Accepts Periodic/Bounded instances, a tuple of them, a Bool, or a tuple of Bools; a single value or a short tuple applies to the leading directions and the rest are Bounded. When omitted, direction 1 is auto-detected — on a spherical grid a longitude axis spanning the full circle is Periodic and a regional span is not, in either storage order — and every other direction is Bounded.
  • period: the wrap length of each periodic direction. Omit it and the axis's own closure is used: 2π for spherical longitude, and extent + one spacing for a Cartesian direction, which is exact for a uniform axis (n·|Δ|). A nonuniform periodic Cartesian direction has no closure to infer, its seam gap being undetermined by its samples, so period is required there.
source
FlowGeometries.Grids.UnstructuredGrid — Type
UnstructuredGrid(geometry, x, y, mask; k=6, radius=nothing, areas=nothing)

Build an UnstructuredGrid whose adjacency comes from a k-d-tree nearest-neighbour search over the nodes: either the k nearest per node (default k = 6), or every neighbour within a physical radius. The two are mutually exclusive. Requires NearestNeighbors.

For SphericalGeometry the tree is built on the 3-D Cartesian embedding of the nodes, where nearest by chord distance and nearest by great-circle distance give the same ordering.

areas supplies per-node cell areas, as a dataset that ships its own does; nothing derives exact Voronoi-cell areas from a Delaunay or convex-hull tessellation (DelaunayTriangulation.jl for Cartesian, Quickhull.jl for spherical — see _voronoi_areas).

periodic/period declare a wrapping domain, and the neighbour search honours it: a node near one face finds the nodes across the opposite face. Spherical longitude wraps by default; a Cartesian box is opt-in and needs its period.

source
FlowGeometries.Grids.UnstructuredGrid — Type
UnstructuredGrid{T, G, V, VA, B, VI}

Unstructured mesh (e.g. radial data, finite volume, or triangular mesh) where coords are 1D vectors.

Type parameters

  • T: coordinate float type. G<:AbstractGeometry{T} is tied to it, so a mismatched-eltype geometry raises a type error. T therefore precedes G: Julia binds type parameters left to right, and G<:AbstractGeometry{T} requires T already bound. CurvilinearGrid follows the same convention.
  • C: tuple type of the per-direction node-coordinate vectors.
  • VA: vector type of the derived measure field, independent of C, a Voronoi tessellation having no reason to match the coordinate vectors' storage type.
  • B: mask storage type.
  • VN/VP: CSR neighbour-list and offset storage types, independent of each other. Their element type is a free Integer, so a large mesh carries Int32 indices — half the memory and bandwidth of Int64, and the width a GPU kernel wants — through this same type.

Neighbour adjacency is CSR: a flat neighbor_nbrs with neighbor_ptr offsets, node t owning neighbor_ptr[t]:neighbor_ptr[t+1]-1. The adjacency is immutable after construction, so the whole graph is two contiguous allocations and a traversal reads them in order.

source
FlowGeometries.Grids.UnstructuredGrid — Method
UnstructuredGrid(geometry, coords::Tuple, measure, mask[, neighbor_nbrs, neighbor_ptr]; periodic, period)
UnstructuredGrid(geometry, x, y, measure, mask[, neighbor_nbrs, neighbor_ptr]; periodic, period)

Build a node grid in any number of directions from one coordinate vector per direction and CSR adjacency. Coordinates come as a tuple; the two-direction case may pass x, y positionally.

Omitting the CSR pair gives a grid with no adjacency — every node reports zero neighbours, which serves a scattered-point spectral method that never queries it. A real-space neighbourhood operation needs adjacency: build it (e.g. through the k-d-tree constructor below) and pass it in, or query by distance with Connectivity.neighbors_within, which reads coordinates.

periodic declares that the enclosing domain wraps in a direction, and period gives the wrap length there. A scattered point set carries no axis to infer this from, so both are explicit — except on a sphere, where longitude wraps at 2π by construction and is the default. See isperiodic and period.

source
FlowGeometries.Grids.YinYangGrid — Type
YinYangGrid(geometry, nlon, nlat; mask = nothing)

The Kageyama–Sato Yin–Yang grid: two nlon × nlat lat–lon panels covering [-3π/4, 3π/4] × [-π/4, π/4] in their own frames, 2·nlon·nlat cells in all.

It stores nlon, nlat, the geometry and the mask. In its own frame each panel is a separable lat–lon patch, and yang is yin rigidly rotated, so a cell's coordinates are the panel formula composed with SphericalSampling._yin_yang_rotate. A cell's area depends on its latitude row alone.

Cells are numbered yin first with i fastest, then yang the same way, matching SphericalSampling.spherical_points(YinYangSampling(), nlon, nlat). panel_cell and cell_id convert between a cell id and its (panel, i, j).

The panels overlap

The two panels overlap by construction, so the cell areas sum to 3√2πR² — 6.07% more than the sphere, at every resolution. That excess is the grid's real geometry, and integrating over both panels needs a partition-of-unity weight for the shared region, which is a modelling choice made on top of these areas. The panels are also not cross-linked: a cell's neighbours are its own panel's, which is the standard Yin–Yang discrete topology — the panels couple through interpolation.

source
FlowGeometries.Geometry.distance — Method
Geometry.distance(grid, I, J) -> T
Geometry.distance(grid::UnstructuredGrid, i, j) -> T

Distance between the centres of two cells under the grid's own geometry and topology: the coordinates are resolved from the indices, reduced to the nearest image in every periodic direction, and handed to the point form of Geometry.distance.

Across a periodic seam this is the short way round: one spacing between the first and last cell of a periodic direction, where the point form on the raw coordinates gives the full extent. A bounded direction contributes its plain coordinate difference. See displacement for the offset it was taken from.

source
FlowGeometries.Grids._build_kdtree_neighbors — Method
_build_kdtree_neighbors(geometry, coords::Tuple; k=6, radius=nothing) -> (nbrs, ptr)

Extension hook: build CSR neighbor adjacency via a k-d tree. Overridden by a consumer NearestNeighbors extension (load using NearestNeighbors). radius, if given, switches to an all-neighbors-within-radius query (mutually exclusive with k); radius is in the grid's physical distance units — Geometry.distance, so meters for SphericalGeometry and the geometry's own units for CartesianGeometry.

source
FlowGeometries.Grids._cell_from_linear — Method
_cell_from_linear(grid, lin) -> cell

The cell a linear index names: the inverse of the linear index a traversal reports, and what turns a spatial index's answer back into a cell.

source
FlowGeometries.Grids._cell_indices — Method
_cell_indices(grid, cell) -> Tuple{Vararg{Int}}

A cell as the index tuple every mask and coordinate accessor here takes: the tuple itself where cells are CartesianCells, and (i,) where they are FlatCells.

Both the trait and the cell's own type are dispatched on, because they answer different questions: the trait says how this layout names a cell, the cell's type how the caller named it. A pair that does not agree — an integer for a tuple-addressed grid — reaches the message below, at the entry point the caller wrote.

source
FlowGeometries.Grids._centers_to_corners — Method
_centers_to_corners(C) -> K

Reconstruct a cell-vertex array one larger in every direction from the N-D cell-center array C, by averaging the (up to 2^N) surrounding centers with a linearly-extrapolated one-cell ghost ring, so the true domain-boundary vertices land a half-cell outside the outermost centers. Used only when the caller does not supply explicit corner arrays; requires at least 2 centers across every direction.

Unlike the corner-area kernel this is dimension-generic: it is a multilinear midpoint, and the ghost ring is a per-direction linear extension of it.

source
FlowGeometries.Grids._curvilinear_periods — Method
_curvilinear_periods(geometry, centers, topology, period) -> NTuple{N,T}

Wrap length per periodic direction, zero where bounded. Spherical longitude closes at 2π; a Cartesian direction's comes from that direction's own line of centres.

source
FlowGeometries.Grids._embedded_floor — Function
_embedded_floor(grid, embedding, h, ::Val{D}) -> NTuple{D,T}

A lower bound on the embedded coordinate in each direction, less one bin, which is where the lattice starts. O(1): no cell is read.

Any bound serves: the lattice is unbounded along a direction that does not wrap, and bins are hashed, so a loose origin shifts every bin index equally. Taking it from the geometry indexes a layout without a pass over its cells.

source
FlowGeometries.Grids._embedding_for — Method
_embedding_for(geo, ncoords) -> AbstractEmbedding

The space a geometry's ncoords-coordinate points sit in. embedding_of is this for a grid, which knows its own coordinate count; a caller holding loose coordinate vectors states it.

One definition, so an index built from a grid and one built from bare coordinates convert a radius through the same code.

source
FlowGeometries.Grids._ghost_points — Method
_ghost_points(pts, periodic, period) -> (all_pts, nghost)

Replicate the D × N point matrix once per combination of periodic image offsets, originals in the first N columns. A wrapping domain is then searched by an ordinary Euclidean query over the images.

source
FlowGeometries.Grids._gnomonic_solid_angle — Method
_gnomonic_solid_angle(X1, X2, Y1, Y2) -> T

The solid angle subtended by the gnomonic rectangle [X1, X2] × [Y1, Y2] on the unit sphere, exactly.

atan(XY / √(1 + X² + Y²)) at a corner is the solid angle of the rectangle from the panel centre to that corner, so the four corners combine by inclusion–exclusion. Over a whole panel it gives 4π/6.

source
FlowGeometries.Grids._ico_class — Method
_ico_class(k, ν) -> (b, c)

Vertex k's symmetry class: the two smallest of its barycentric parts (i, j, ν−i−j), the largest being ν minus their sum.

All twenty faces are the same spherical triangle and the geodesic is built the same way on each, so a vertex's dual area depends only on its barycentric position within a face; the face being equilateral, only on that triple up to order. A vertex on a macro-edge or at a corner sits on several faces, and an isometry of the icosahedron carrying it to another vertex of its class carries those faces with it, so the areas agree there too.

The classes are the partitions of ν into three parts, O(ν²/12) of them against 10ν² + 2 vertices.

source
FlowGeometries.Grids._ico_dual_share — Method
_ico_dual_share(V, A, B) -> T

Vertex V's share of the spherical triangle (V, A, B): the quadrilateral (V, M_VA, O, M_VB) with O the circumcenter and M the edge midpoints, as two spherical excesses.

Those arcs are the triangle's perpendicular bisectors — O is equidistant from all three vertices and each midpoint from its two — so the share is exactly V's Voronoi cell restricted to that triangle, and the three shares tile the triangle.

source
FlowGeometries.Grids._max_gap — Method
_max_gap(x) -> maximum consecutive |gap|, or 0 if length(x) < 2

Largest spacing anywhere on axis x, the counterpart of _min_gap. Together they bound how far an index window must reach to cover a given physical distance.

source
FlowGeometries.Grids._measure_factors — Method
_measure_factors(geometry, axes, periods) -> NTuple{N,AbstractVector}

Per-axis factors whose outer product is the cell measure: measure[I...] == prod(w[d][I[d]]).

Every rectilinear cell measure this package supports is separable in exactly this way — Cartesian Δx·Δy·Δz, and spherical R²cosφ·Δλ·Δφ = (Δλ) · (R²cosφ·Δφ) or r²cosφ·Δλ·Δφ·Δr = (Δλ) · (cosφ·Δφ) · (r²·Δr). Building the measure as an outer product of these factors keeps the result in whatever array type the axes use, and exposes the separability to a caller that can exploit it.

A degenerate (length-1) angular axis drops its differential, so a zonal transect measures arc length R·cosφ·Δλ along its circle of latitude and a meridional one measures R·Δφ.

source
FlowGeometries.Grids._min_gap — Method
_min_gap(x) -> minimum consecutive |gap|, or Inf if length(x) < 2

Smallest spacing found anywhere on axis x. It bounds the search radius for a genuinely nonuniform axis: a distance check still gates what is included, so the smallest gap anywhere can only widen the search window, and no in-range cell is missed.

source
FlowGeometries.Grids._min_image — Method
_min_image(p0, pt, prd) -> NTuple

pt brought to the image nearest p0, per component, for each direction with a nonzero wrap length.

For an angular coordinate the geometry's own distance is already 2π-periodic and this changes nothing (it also keeps Vincenty inside its |Δλ| ≤ π regime); on a periodic Cartesian coordinate it carries a pair across the seam. Per-component minimum image is the global minimum for a separable metric, which the Euclidean one is.

source
FlowGeometries.Grids._sep_extrema — Method
_sep_extrema(f, m) -> (lo, hi)

Smallest and largest f(cell) over a SeparableMeasure, from the per-axis extremes.

A product's extremes are attained with every factor at one of its own endpoints, so all 2^N endpoint combinations are formed and the best taken. This holds for factors of either sign, where ∏ maximum alone holds only for non-negative ones, and it costs O(∑ Nᵈ + 2^N) against the dense O(∏ Nᵈ).

source
FlowGeometries.Grids._shift_set — Method
_shift_set(periodic, period)

Offsets to replicate a point set by: (0,) in a non-wrapping direction, (0, -L, +L) in a wrapping one. Zero first, so the originals occupy the first block of the replicated set.

source
FlowGeometries.Grids._to_axis — Method
_to_axis(T, x) -> AbstractVector{T}

Adapt axis input x to element type T, keeping whatever is known about its spacing and never collapsing a provably uniform axis into a plain Vector.

An axis already of element type T is kept exactly as it is, whatever type it is. A caller's own range subtype, a StepRangeLen whose TwicePrecision internals they want, a BigFloat-backed range — all pass through untouched. Nothing needs converting to get the uniform fast paths: those dispatch on Axes.spacing_trait, which is UniformSpacing() for every AbstractRange, so the caller's own range takes them.

Conversion happens only where the element type must change, and there an arbitrary range subtype cannot generically be rebuilt at a new eltype. That case becomes an Axes.UniformAxis{T}, which is also how a Float32 axis stops carrying Float64 internals. Call Axes.uniform_axis to convert at the call site.

Four methods, ordered so nothing is ambiguous (a StepRangeLen{T} is both an AbstractRange and an AbstractVector{T}, so the parameterized range form is needed to break that tie):

  • AbstractRange{T} / AbstractVector{T}: passthrough, zero cost, type preserved.
  • AbstractRange (wrong eltype): rebuilt as UniformAxis{T}, still uniform and now isbits.
  • AbstractVector (wrong eltype): copied with similar, so a device-resident array stays in its own storage.
source
FlowGeometries.Grids._total_measure — Method
_total_measure(grid) -> T

sum(measure(grid)), from whatever the layout knows about the region its cells tile. The fallback adds the cells up; a layout tiling the whole sphere returns 4πR² without visiting one.

source
FlowGeometries.Grids._voronoi_tessellation — Method
_voronoi_tessellation(geometry, x, y) -> (areas, mesh::CellMesh)

Extension hook: exact per-node Voronoi-cell area from a Delaunay/convex-hull tessellation of the node coordinates, together with the triangulation it came from. Dispatched on the geometry type (each needs a different tessellation library): overridden for CartesianGeometry by a consumer DelaunayTriangulation extension (load using DelaunayTriangulation, planar Voronoi clipped to the point set's convex hull) and for SphericalGeometry by a consumer Quickhull extension (load using Quickhull, spherical Voronoi from the dual of the 3D convex hull of the unit-sphere embedding).

An Geometry.AbstractEllipsoidalGeometry has no such construction and says so: its cell areas are the caller's to supply.

source
FlowGeometries.Grids._wrap_sign — Method
_wrap_sign(x) -> ±1

+1 for an ascending axis and -1 for a descending one: the sign that turns a period magnitude into the wrapped neighbour's offset in index order. Defined in Axes — it is a property of the axis alone, and the discretization layer needs the same answer.

source
FlowGeometries.Grids.axis — Method
axis(grid::AbstractStructuredGrid, d::Integer) -> AbstractVector

Direction d's coordinate axis. Only rectilinear grids have axes; this is coordinates under the name that is exact for them.

source
FlowGeometries.Grids.base_grid — Method
base_grid(grid::RotatedGrid) -> AbstractStructuredGrid

The rectilinear mesh underneath. Its axes are the coordinates a derivative differences along; the rotation changes where cells are reported, leaving the lattice as it is.

source
FlowGeometries.Grids.bounds — Method
bounds(grid, d) -> (lo, hi)

Smallest and largest coordinate along direction d, ordered lo ≤ hi regardless of whether the direction is stored ascending or descending. These are the extreme sample positions, i.e. cell centres.

O(1) on a rectilinear grid: an axis is monotone, as every search here bisects it, so its extremes are its endpoints. O(N) where the coordinates are per-cell fields, which carry no such order; a query that reads this repeatedly takes it from a MetricTopology, built once.

source
FlowGeometries.Grids.cell_at — Method
cell_at(grid, c)

One element of cells as the cell spelling the grid's own entry points take: an index tuple where cells are addressed Cartesian-wise, a linear id where they are flat.

source
FlowGeometries.Grids.cell_list — Method
cell_list(grid; ball, active_only = false) -> CellListIndex

Build a CellListIndex over grid's cell centres, binned at side ball — the radius you mean to query at. Needs no external package.

The default indexes every cell, which serves a query at either mask policy: an active_only = true query filters what it is handed.

active_only = true indexes the active region alone, sizing a mostly-masked grid — a basin, a catchment — by that region. Such an index answers queries at that same policy, and raises for one asking to see a masked cell. Ask for it where the policy is fixed, as a sweep does.

source
FlowGeometries.Grids.cell_mesh — Method
cell_mesh(grid) -> CellMesh | Nothing

The grid's CellMesh, or nothing where it has none.

A node set built by tessellation keeps the cells that tessellation produced; one given its adjacency directly has only that adjacency, and no cells to speak of unless it is handed some.

source
FlowGeometries.Grids.cell_width — Method
cell_width(grid, d, i) -> T

The coordinate width of cell i along direction d: Discretization.cell_width on that direction's axis, with the grid's own wrap period. Non-negative whichever way the axis is stored.

A coordinate width, distinct from the cell measure: on a sphere measure is R²cosφ·Δλ·Δφ and this is the Δλ or Δφ in it. measure_factors folds the metric into each factor, so it does not expose these separately.

source
FlowGeometries.Grids.cells — Method
cells(grid)

What a traversal of every cell iterates, and cell_at turns one of its elements into a cell. Together they are how a layout is walked without knowing how it names a cell — see cell_address.

source
FlowGeometries.Grids.coordinates — Method
coordinates(grid) -> NTuple{N,AbstractArray}
coordinates(grid, d::Integer) -> AbstractArray

The grid's coordinate arrays, or just direction d's. Shape depends on the grid architecture: a StructuredGrid stores one 1-D axis vector per direction, a CurvilinearGrid one N-D array per direction, an UnstructuredGrid one value per node. Direction order matches Geometry.point_names — (x, y, z) for Cartesian, (λ, φ, r) for spherical.

See also axis (the rectilinear spelling), coords (a single point).

source
FlowGeometries.Grids.corner_coords — Method
corner_coords(grid::CurvilinearGrid, I...) -> NamedTuple
corner_coords(S, grid::CurvilinearGrid, I...) -> S

Vertex I of the cell-vertex array, named by the geometry exactly as coords names cell centers.

source
FlowGeometries.Grids.corners — Method
corners(grid::CurvilinearGrid) -> NTuple{N,AbstractArray}
corners(grid::CurvilinearGrid, d::Integer) -> AbstractArray

The cell-vertex coordinate arrays — one larger than coordinates in every direction, and in the same direction order.

Available on a grid that was given them or built with keep_corners = true; see has_corners.

source
FlowGeometries.Grids.displacement — Function
displacement(grid, I, J) -> NTuple{N,T}
displacement(grid::UnstructuredGrid, i, j) -> NTuple{N,T}

The signed per-direction coordinate offset from cell I to cell J, reduced to the nearest image in every periodic direction — the offset Geometry.distance is taken from.

A coordinate quantity, so it lives here, while the distance extends Geometry.distance. Across a periodic seam the two cells' stored coordinates differ by nearly a full period, and this reports the short way round.

source
FlowGeometries.Grids.embedded_at — Method
embedded_at(grid, cell) -> NTuple{D,T}

A cell's centre in the space an index searches, taken from the grid: O(1) arithmetic where the coordinates are a formula, a read where they are data.

source
FlowGeometries.Grids.embedded_points — Function
embedded_points(grid) -> (pts, nghost, embedding)

The cell centres in the space an index searches, the number of periodic replications they carry, and the AbstractEmbedding saying what a radius means there.

One definition, so every index searches the same space as every other and as the k-d-tree construction path — the guarantee that an indexed query and a scan return the same cells rests on it.

source
FlowGeometries.Grids.embedding_of — Function
embedding_of(grid) -> AbstractEmbedding

The Euclidean space this grid's cell centres sit in, and therefore what a physical radius means there.

Named separately from embedded_points because an index that streams the centres still has to know the space before it reads the first one.

source
FlowGeometries.Grids.fold_candidates — Function
fold_candidates(f, acc, index, grid, I, r) -> acc

Thread acc = f(acc, k) over every cell k the index reports near cell I, without building a list. A superset of the ball, each cell exactly once; the caller's exact distance gate decides membership.

A fold allocates nothing and needs no per-query buffer, so it runs inside a kernel. A tree walk needs one, having to deduplicate the periodic images it searches over.

source
FlowGeometries.Grids.fold_candidates_at — Method
fold_candidates_at(f, acc, index, q, r, scratch) -> acc

fold_candidates around an arbitrary point q, already in the index's embedding. A cell query is this one at the cell's own centre, so there is a single traversal.

scratch is a candidate buffer for an index that has to materialize one — a tree does, since it must deduplicate the periodic images it searches over. A cell list folds directly and ignores it.

source
FlowGeometries.Grids.formula_neighbors — Function
formula_neighbors(grid, cell) -> (ids::NTuple{K,Int}, n)

A cell's neighbours as linear indices, in the first n entries of a fixed-width tuple, where K is max_neighbors(grid).

The tuple is a stack value, so a traversal over every cell of a layout whose adjacency is arithmetic allocates nothing and threads no scratch through. n varies where a layout has singular cells: a HEALPix pixel at a face corner has seven neighbours where the rest have eight.

The one method a FormulaNeighbors layout supplies.

source
FlowGeometries.Grids.has_spatial_index — Method
has_spatial_index(grid) -> Bool

Whether the k-d tree behind spatial_index can be built for this grid — false until the NearestNeighbors extension is loaded. Answers "is the tree available?" without calling spatial_index speculatively and catching its error.

This is not a test for whether a grid can be indexed at all: cell_list needs no extension and is what the sweeps build.

source
FlowGeometries.Grids.has_symmetric_adjacency — Function
has_symmetric_adjacency(grid) -> Bool

Whether the graph grid builds satisfies j ∈ N(i) ⟺ i ∈ N(j), answered from the layout.

A symmetric graph with sorted rows has the same arrays read as CSR and as CSC, so Connectivity.sparse_adjacency_matrix hands its buffers to the matrix without transposing. Connectivity.is_symmetric_adjacency answers the same question about a built graph, and pays a transpose to do it; this answers before one exists.

false by default, so a layout claims the shortcut only where its own formula gives it. Masking preserves it either way: an edge and its reverse are the same pair of cells, and both are dropped together. A layout whose neighbours are an index stencil answers through the stencil, which carries its own Stencils.is_symmetric.

source
FlowGeometries.Grids.incident_nodes — Method
incident_nodes(grid, idx::Integer) -> AbstractVector{<:Integer}

The nodes stored as incident to node idx, as a zero-copy view into the CSR adjacency.

This is storage: it reports what the mesh holds, with no regard for the mask. neighbors is the query, and it honours active_only on every layout.

source
FlowGeometries.Grids.index_within! — Method
index_within!(buffer, index, grid, I, r) -> candidate cell indices
index_within(index, grid, I, r) -> candidate cell indices

Extension hook: the cells an index reports near I, as linear indices. It must return a superset of the cells within r; the caller applies the exact distance gate, so over-returning is safe and under-returning is not.

index_within! overwrites and returns buffer, so a sweep over many cells allocates nothing per query. index_within is the same query into a fresh vector.

source
FlowGeometries.Grids.isuniform — Method
isuniform(grid, d) -> Bool
isuniform(grid) -> Bool

Whether coordinate direction d has constant spacing known from its type (all directions, for the no-d form). The answer comes from the type alone; no code path inspects coordinate values to decide it. See spacing_trait for the form a method can dispatch on.

A curvilinear or unstructured grid is never uniform: its coordinates are per-cell fields with no axes.

source
FlowGeometries.Grids.local_spacing — Method
local_spacing(grid, d, i) -> (h_m, h_p)

The one-sided coordinate gaps around index i along direction d: Discretization.local_spacing on that direction's axis, with the wrap period taken from the grid, so a periodic seam is right without the caller supplying it.

Signed, so a descending axis reports negative gaps — see the axis-level form for why, and cell_width for the non-negative width built from them. Allocation-free, so this is the per-point form to call inside a loop assembling a finite-difference operator.

source
FlowGeometries.Grids.locate — Function
locate(grid, p) -> cell index
locate(grid, p; active_only=false, topology, scratch) -> cell index

The cell of grid that p belongs to, as an NTuple of indices on a rectilinear or curvilinear grid and a node number on an UnstructuredGrid. p is a coordinate tuple in the grid's own coordinates.

On a StructuredGrid this is Discretization.locate per direction, so the answer is the cell whose faces bracket p — an O(1) lookup on a uniform axis and a bisection otherwise, with a periodic direction wrapped first. A direction p lies outside reports 0 for that direction.

Elsewhere there are no axes to bracket along and it is the nearest cell centre, which is exactly the containing cell for a node set, whose cells are the Voronoi regions of its nodes. On a curvilinear grid the two can differ where cells are strongly sheared, so the contract is nearest-centre. That form takes the keywords: topology carrying an index — cell_list — makes it a bin lookup, scratch is a Connectivity.ball_scratch buffer, and active_only restricts the answer to unmasked cells. It defaults to false here, where the ball queries default to true: which cell a point falls in is a question about the grid, answerable over the masked region.

source
FlowGeometries.Grids.location_at — Method
location_at(sg, d) -> NTuple{N,AbstractLocation}

Where the velocity component along direction d sits: Face() in d, Center() in every other direction.

source
FlowGeometries.Grids.mask — Method
mask(grid) -> AbstractArray{Bool}

Which cells/nodes participate, as an array over the grid's own shape. Ask isactive for one cell; this is the whole array, and it is what fixes the grid's size.

source
FlowGeometries.Grids.materialize — Method
materialize(grid) -> NTuple{D,Vector}

Every cell's coordinates, as one dense vector per direction.

How to ask a layout whose coordinates are a formula for them as data — for writing a file, or handing them to something that takes point clouds. It allocates D·n numbers, so it is an explicit call.

source
FlowGeometries.Grids.measure — Method
measure(grid, I...) -> T

Cell measure at index I: length in 1-D, area in 2-D, volume in 3-D, or the node's control-volume size on an unstructured grid. area is the 2-D spelling of the same quantity.

source
FlowGeometries.Grids.measure_array — Method
measure_array(grid) -> Array

The cell measure materialized densely. This is ∏ Nᵈ values — only ask for it when a dense array is genuinely required; measure already indexes and broadcasts.

source
FlowGeometries.Grids.measure_array — Method
measure_array(grid::IcosahedralGrid) -> Vector

The dual-cell areas of every vertex, the fan of spherical excesses evaluated once per symmetry class — see _ico_class. The two smallest barycentric parts key a (ν+1)² table.

source
FlowGeometries.Grids.measure_factors — Method
measure_factors(grid) -> NTuple{N,AbstractVector} or nothing

The grid's per-axis measure factors when it has them, else nothing. Callers that can exploit separability (a zonal mean weights by one factor only, a global integral is a product of sums) can avoid touching ∏ Nᵈ values at all.

source
FlowGeometries.Grids.minimum_spacing — Method
minimum_spacing(grid, d) -> T

Smallest gap between consecutive samples along direction d, as a non-negative magnitude. O(1) when the direction is isuniform and O(N_d) otherwise. With maximum_spacing it bounds how far an index window must reach to cover a given physical distance, as a neighbourhood-by-distance query on a stretched axis needs. The two are also the exact test of whether a stretched axis happens to be equally spaced: its gaps are identical when they are equal.

A direction of fewer than two samples has no gap, and reports Inf — the identity for min.

source
FlowGeometries.Grids.ncoordinates — Method
ncoordinates(grid) -> Int

How many coordinate directions a cell of grid has — 2 for (λ, φ), 3 for (x, y, z).

Separate from counting coordinates(grid) because a layout whose coordinates are a formula stores no arrays to count, and answers this from its parameters instead.

source
FlowGeometries.Grids.neighbor_nbrs — Method
neighbor_nbrs(grid::UnstructuredGrid) -> AbstractVector{<:Integer}
neighbor_ptr(grid::UnstructuredGrid) -> AbstractVector{<:Integer}

The CSR adjacency arrays: the flat neighbour indices, and the per-node offsets into them.

source
FlowGeometries.Grids.neighbors — Function
neighbors(grid, I...; stencil = Stencils.Axial(1), active_only = true)

The neighbours of cell I, as a lazy sequence of linear indices that allocates nothing.

Where they come from is the layout's adjacency_source: index-space offsets, which stencil selects among, or the mesh's own stored incidence, over which a stencil has no meaning.

active_only applies either way: a masked cell has no neighbours and is nobody's neighbour. See incident_nodes for the unfiltered storage behind a stored graph.

source
FlowGeometries.Grids.npixels — Method
npixels(grid::HEALPixGrid) -> Int

12·nside², the pixelization's own count — which is length(grid) whether or not a mask leaves some of them inactive.

source
FlowGeometries.Grids.rebuild — Method
rebuild(grid, fields::NamedTuple) -> grid

grid with the named fields replaced, its type parameters re-derived from what the new fields are.

The hook a storage change goes through — moving a grid's arrays to a device, rewrapping them — so one generic method serves every layout and no reconstruction elsewhere spells out a parameter list of its own.

fields need only name what changes; everything else is carried over.

source
FlowGeometries.Grids.reorder — Method
reorder(grid, perm) -> UnstructuredGrid

grid with its nodes in the order perm gives — coordinates, measure and mask permuted, and the adjacency and any CellMesh renumbered to match, so every index the grid reports is an index into the new order.

Pair it with spatial_order, and permute your own fields by the same perm: f[perm] is that field on the reordered grid.

source
FlowGeometries.Grids.ring_of — Method
ring_of(grid::RingGrid, i::Integer) -> Int

Which ring cell i belongs to.

O(log nrings) by bisection of the cumulative counts. A closed form exists for the octahedral rule alone; a tabulated reduced grid's counts are arbitrary, so the search is what serves both.

source
FlowGeometries.Grids.rotate — Method
rotate(grid::StructuredGrid, rot) -> RotatedGrid
unrotate(grid::StructuredGrid, rot) -> RotatedGrid

The same mesh with its coordinates expressed in the other frame of Geometry.PoleRotation rot — unrotate being the usual direction, taking a rotated-pole grid's rectilinear (λ′, φ′) axes to the geographic coordinates of each cell.

A rotated lat–lon mesh is logically rectangular and geometrically warped, and only its own frame's axes are separable. The warping is a formula, so the result stores the mesh and the rotation and evaluates a cell's position where it is asked for; see RotatedGrid. Rotating a grid therefore costs one PoleRotation, against two centre arrays, two corner arrays and a dense measure to materialize it.

Two things carry over. The cell measure is exact under a rotation, an isometry of the sphere, so it is shared; recomputing it from rotated corners adds round-off. The index topology carries over too: the same mesh has the same neighbours, so a direction that wrapped still wraps, and longitude remains an angle mod 2π in either frame.

To difference along the mesh's own axes, ask base_grid for them: the frame changes where a cell is reported, leaving the lattice as it is.

source
FlowGeometries.Grids.sampling — Method
sampling(grid) -> AbstractSphericalSampling | Nothing

The node-set recipe grid's axes were built from, or nothing where they were given directly.

Coordinates do not determine it: Gauss–Legendre and an arbitrary lat–lon grid are the same numbers to within round-off, and only the recipe says which quadrature is exact on them. Keeping it means a grid can be asked for its matching weights, or rebuilt at another resolution, after construction.

source
FlowGeometries.Grids.spacing_trait — Method
spacing_trait(grid, ::Val{d}) -> UniformSpacing() | NonuniformSpacing()

Whether direction d's spacing is known from its type, as a value a method can dispatch on.

isuniform answers the same question as a Bool, which a method can only branch on. A kernel that selects the uniform form of an expression — a stencil row that is the same at every interior sample, an O(1) locate — selects it by dispatch, and that needs the direction in the type, hence the Val.

isuniform(grid, d::Integer) serves a direction chosen at run time.

source
FlowGeometries.Grids.spatial_order — Method
spatial_order(grid) -> Vector{Int}

A permutation of the grid's nodes along a Morton (Z-order) curve: perm[k] is the node that belongs in position k.

Scattered nodes usually arrive in whatever order they were generated, and a neighbour traversal then jumps across the whole array per edge. Along a space-filling curve, neighbours in space are neighbours in memory, so neighbor_nbrs[ptr[i]:ptr[i+1]-1] reads mostly-adjacent addresses.

The order is a property of the points: the same set, differently shuffled, sorts to the same sequence. So this repairs an incoherent input order and leaves a coherent one as it is.

The permutation is returned, and construction does not apply it. The node index is the caller's handle on their own data, and a renumbering here leaves every field they hold pointing at the wrong node. Apply it with reorder and permute those fields the same way.

source
FlowGeometries.Grids.topology — Method
topology(grid) -> NTuple{N,AbstractTopology}
topology(grid, d) -> AbstractTopology

The grid's per-direction topology. Singletons, so this occupies no storage.

source
FlowGeometries.Grids.unrotate — Method
unrotate(grid::StructuredGrid, rot) -> RotatedGrid

rotate the other way: the mesh's axes are the rotated frame's, and each cell is reported at its geographic position. The usual direction for a rotated-pole grid.

source