Grids

FlowGeometries.Grids.AbstractTopologyType
AbstractTopology

How a coordinate direction closes: Periodic or Bounded.

Carried in the grid's type, so isperiodic is a compile-time answer and callers can dispatch on it. The cell measure depends on it — a wrapped boundary cell has a width a bounded one does not — so it is part of what the grid is, not a flag attached to it.

source
FlowGeometries.Grids.AllActiveType
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.ArcEmbeddingType
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.AxisStatsType
AxisStats

Everything about one axis that does not depend on the query, reduced once when the grid is built: gaps, span, and whether the axis type proves constant spacing. Each of these is otherwise an O(N_d) scan — or a dynamic lookup, since the coordinate tuple is heterogeneous — for an answer that cannot change over the grid's life.

source
FlowGeometries.Grids.CellListIndexType
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 distinguish it from a tree, and all three are why it is the one that runs on a device:

  • it is arrays only — bin offsets and point ids — so Adapt moves it like any other field;
  • every point lands in exactly one bin, because periodicity wraps the bin coordinate rather than replicating the point, so a query emits each cell once and needs no candidate buffer to deduplicate into. That is what lets fold_candidates be a fold rather than a list;
  • bins are hashed into O(n) buckets, so the memory does not depend on h. A sphere binned at 100 km would otherwise need (2R/h)³ ≈ 2×10⁶ mostly empty cells, and far more 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.ChordEmbeddingType
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, which is what an index is allowed to do.

source
FlowGeometries.Grids.CurvilinearGridType
CurvilinearGrid{T, G, N, TP, C, 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 and corners the matching cell-vertex arrays, each one larger in every direction.

At N = 2 the cell measure is computed from those corners as the exact quadrilateral area rather than by a cell-center spacing approximation. In any other dimension the measure is the caller's to supply: the corner-area kernel is a genuinely 2-D algorithm, not a 2-D special case of an N-D one.

Type parameters

  • T: coordinate float type. G<:AbstractGeometry{T} is tied to it (a mismatched-eltype geometry is a type error, not a silent promotion) — hence T precedes G (Julia forbids the forward reference G<:AbstractGeometry{T}, T needed to keep the {G,T} order).
  • N: number of coordinate directions.
  • C: tuple type shared by the center and corner coordinate arrays — a mesh's own coordinate arrays are legitimately almost always the same concrete type.
  • 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.CurvilinearGridMethod
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 rather than the 2-D case of an N-D one, 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.

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.SeparableMeasureType
SeparableMeasure(factors)

The cell measure of a rectilinear grid, stored as its per-axis factors rather than materialized.

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ᵈᵢ, which is O(∑ Nᵈ) rather than O(∏ Nᵈ).

source
FlowGeometries.Grids.StructuredGridType
StructuredGrid{G, T, N, 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.

TP is the per-direction AbstractTopology: singletons, so no storage, and readable from the type.

C is 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, or any other subtype); there is deliberately no shared vector type forcing the axes to match. This matters beyond storage: a UniformAxis's type is a compile-time proof of constant spacing that isuniform and spacing read without touching a coordinate, and forcing the axes into a common type would destroy it. One axis can be uniform while another is stretched.

source
FlowGeometries.Grids.StructuredGridMethod
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, not an offset from a reference radius. 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: 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 well-defined closure to infer — its seam gap is not determined by its samples — so period is required there determine.
source
FlowGeometries.Grids.UnstructuredGridType
UnstructuredGrid(geometry, x, y, mask; k=6, radius=nothing, areas=nothing)

Build an UnstructuredGrid with REAL neighbor adjacency, via a k-d-tree nearest-neighbor query (NearestNeighbors.jl; brute-force O(N²) doesn't scale) — either the k nearest neighbors per node (default k=6), or every neighbor within a physical radius (pass radius to switch; mutually exclusive with k). For SphericalGeometry the tree is built on the 3D Cartesian embedding of the nodes (nearest-by-chord-distance is exactly nearest-by-great-circle-distance — exact, not an approximation).

areas: supply per-node cell areas explicitly (common for a real dataset that ships its own), or leave nothing to auto-compute exact Voronoi-cell areas from a Delaunay/convex-hull tessellation (DelaunayTriangulation.jl for Cartesian, Quickhull.jl for spherical — see _voronoi_areas).

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

source
FlowGeometries.Grids.UnstructuredGridType
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 (a mismatched-eltype geometry is a type error, not a silent promotion) — hence T precedes G (Julia forbids the forward reference G<:AbstractGeometry{T}, T needed to keep the {G,T} order), matching the same convention CurvilinearGrid uses.
  • C: tuple type of the per-direction node-coordinate vectors (a node set's own coordinate vectors are legitimately almost always the same concrete type).
  • VA: vector type of the derived measure field — independent of C, since it is frequently a computed field (Voronoi tessellation) with no reason to match the coordinate vectors' storage type.
  • B: mask storage type.
  • VN/VP: CSR neighbor-list and offset storage types, independent of each other. Their element type is a free Integer, so a large mesh can carry Int32 indices (half the memory and bandwidth of Int64, and the width GPU kernels want) without needing a separate grid type.

Neighbor adjacency is stored CSR-style (flat neighbor_nbrs + neighbor_ptr offsets, node t owns neighbor_ptr[t]:neighbor_ptr[t+1]-1) rather than as a vector of per-node vectors — the data is immutable after construction, so there's no reason to pay for Nnodes separately-heap-allocated Vectors (cache-unfriendly pointer-chasing, one allocation per node) when one contiguous block (two allocations total) holds the same information.

source
FlowGeometries.Grids.UnstructuredGridMethod
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 is enough for scattered-point spectral methods that never query it. Real-space neighbourhood operations need 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 rather than edges.

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.Discretization.apply_stencil!Method
apply_stencil!(out, field, grid, indices, weights, dim; order=1, active_only=true,
               masked=zero, policy=BlankMasked(), backend=nothing) -> out

Apply a stencil table built by Discretization.axis_stencils — the mask, the wrap period and the axis all come from grid, which is what the bare (indices, weights) form cannot do.

Because the axis comes too, any mask policy works here. The bare form has no axis to rebuild a window from at a mask edge, so it accepts only BlankMasked; a caller wanting ReduceInRun on a masked grid would otherwise have to give up the table and pay its rebuild on every call.

This is the form to use in a loop over fields: the table is the same for all of them, and building it is the one part of the work that does not depend on the field.

source
FlowGeometries.Discretization.apply_stencil!Method
apply_stencil!(out, field, grid, dim; order=1, nodes=order+1, active_only=true, masked=zero) -> out

Discretization.apply_stencil! with the axis, wrap period and mask taken from grid, so a periodic direction wraps and an inactive cell is honoured without restating any of it.

Only a rectilinear direction has a 1-D axis to difference along, so this is a StructuredGrid method.

source
FlowGeometries.Discretization.axis_stencilsMethod
axis_stencils(grid, dim; order=1, nodes=order+1) -> (indices, weights)

Discretization.axis_stencils for direction dim of grid, taking that direction's axis and wrap period from the grid.

The table depends only on the grid, not on any field, so a caller differencing many fields along the same direction should build it once and hand it to the (out, field, grid, indices, weights, dim) form — the (out, field, grid, dim) form above rebuilds it on every call.

source
FlowGeometries.Geometry.distanceMethod
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, not the full extent, which is what the point form on the raw coordinates would give. A bounded direction contributes its plain coordinate difference. See displacement for the offset it was taken from.

source
FlowGeometries.Grids._build_kdtree_neighborsMethod
_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 — meters for SphericalGeometry, geometry's own units for CartesianGeometry), NOT a raw chord/angle.

source
FlowGeometries.Grids._centers_to_cornersMethod
_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_periodsMethod
_curvilinear_periods(geometry, centers, topology, period) -> NTuple{N,T}

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

source
FlowGeometries.Grids._ghost_pointsMethod
_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._max_gapMethod
_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_factorsMethod
_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 rather than by a nested scalar loop keeps the result in whatever array type the axes use, and makes the separability available to callers that can exploit it.

Degenerate (length-1) angular axes are handled by dropping the differential that no longer exists, so a zonal transect measures arc length R·cosφ·Δλ along its circle of latitude and a meridional one measures R·Δφ — not an area formula with a placeholder substituted in.

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

Smallest spacing found anywhere on axis x. Used to build a conservative (safe, never under-covering) search-radius bound for a genuinely nonuniform axis: since real distance checks still gate what's actually included, using the smallest gap anywhere can only widen the search window, never cause a missed in-range cell.

source
FlowGeometries.Grids._min_imageMethod
_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 -periodic and this changes nothing (it also keeps Vincenty inside its |Δλ| ≤ π regime); for a periodic Cartesian coordinate it is what makes the seam invisible. Per-component minimum image is the global minimum for a separable metric, which the Euclidean one is.

source
FlowGeometries.Grids._sep_extremaMethod
_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. That is exact for factors of ANY sign — ∏ maximum alone would be wrong the moment a factor could go negative — and it costs O(∑ Nᵈ + 2^N) against the dense O(∏ Nᵈ).

source
FlowGeometries.Grids._shift_setMethod
_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_axisMethod
_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. To convert deliberately rather than by side effect, call Axes.uniform_axis.

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._voronoi_areasMethod
_voronoi_areas(geometry, x, y) -> Vector{T}

Extension hook: exact per-node Voronoi-cell area from a Delaunay/convex-hull tessellation of the node coordinates. 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).

source
FlowGeometries.Grids._wrap_signMethod
_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.axisMethod
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.axis_statsMethod
axis_stats(grid) -> NTuple{N,AxisStats}
axis_stats(grid, d) -> AxisStats

The cached per-axis reductions. Homogeneous whatever the axis types are, so reading one with a runtime direction index stays type-stable.

source
FlowGeometries.Grids.boundsMethod
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 (cell centres), not the outer cell boundaries.

source
FlowGeometries.Grids.cell_widthMethod
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.

This is the coordinate width, not the cell measure — on a sphere measure is R²cosφ·Δλ·Δφ and this is the Δλ or Δφ in it, which measure_factors does not expose separately because it folds the metric into the factor it multiplies.

source
FlowGeometries.Grids.coordinatesMethod
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_coordsMethod
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.cornersMethod
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.

source
FlowGeometries.Grids.displacementFunction
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 rather than a metric one, which is why it lives here while the distance itself 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 instead.

source
FlowGeometries.Grids.embedded_pointsFunction
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.fold_candidatesFunction
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 rather than a returned list is the whole point: it allocates nothing and needs no per-query buffer, which is what a kernel requires and what a tree cannot offer, since a tree walk has to deduplicate the periodic images it searches over.

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

fold_candidates around an arbitrary point q, already in the index's embedding, rather than around a cell. A cell query is this one at the cell's own centre, so there is one 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.has_spatial_indexMethod
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.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, which is how a sweep over many cells avoids one heap allocation per query — nothing at all, against 480 bytes on a small ball and 6.1 KB on a 310-candidate one. index_within is the same query into a fresh vector.

source
FlowGeometries.Grids.isuniformMethod
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). This is the compile-time answer, so it can select a fast path by dispatch rather than by a runtime scan — see Axes.spacing_trait for the trait it reads and No code path inspects coordinate VALUES to decide this; the answer comes from the type alone.

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

source
FlowGeometries.Grids.local_spacingMethod
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.locateFunction
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 read it as nearest-centre rather than point-in-quadrilateral. That form takes the keywords: topology carrying an index — cell_list — makes it a bin lookup rather than a scan, scratch is a Connectivity.ball_scratch buffer, and active_only restricts the answer to unmasked cells (false here, unlike the ball queries, since the cell a point falls in is a question about the grid rather than about the active region).

source
FlowGeometries.Grids.measureMethod
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_arrayMethod
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_factorsMethod
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_spacingMethod
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, which is what a neighbourhood-by-distance query needs on a stretched axis. They are also the exact test of whether a stretched axis happens to be equally spaced: its gaps are identical when the two are equal.

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

source
FlowGeometries.Grids.neighbor_nbrsMethod
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.neighborsMethod
neighbors(grid::UnstructuredGrid, idx::Integer) -> AbstractVector{<:Integer}

Neighbor node indices of node idx, as a zero-copy view into the CSR-flattened adjacency storage.

source
FlowGeometries.Grids.rotateMethod
rotate(grid::StructuredGrid, rot) -> CurvilinearGrid
unrotate(grid::StructuredGrid, rot) -> CurvilinearGrid

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

The result is curvilinear because that is what it is: a rotated lat–lon mesh is logically rectangular and geometrically warped, and only its own frame's axes are separable.

Two things carry over rather than being recomputed. The cell measure is exact, because a rotation is an isometry of the sphere — recomputing it from the rotated corners would only add roundoff. The index topology is too: it is the same mesh with the same neighbours, so a direction that wrapped still wraps. Longitude remains an angle mod in either frame, so the wrap length is unchanged.

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

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

source