Cubed-sphere topology (6-block)
The cubed-sphere topology is SHORE's production topology and the one the shore mesh CLI builds. It is the standard cubed-sphere construction from atmospheric and planetary CFD (Sadourny 1972; Ronchi, Iacono & Paolucci 1996): the body surface is decomposed into 6 conforming hexahedral blocks — 4 equatorial sub-blocks plus 2 polar caps — corresponding to the 6 faces of a cube projected outward onto the body. There is no singular axis, all seams are conforming, and each cap is a regular structured block in its own right.
It is not a wrapped O-grid: the cubed sphere does not extend the single-block O-grid by augmenting it with caps. They are two distinct constructions that share only the underlying spacing primitives.

This page describes the topology — the block layout, the multi-block connectivity, and the boundary conditions. The algorithmic details (how each block is built and marched) live in the Algorithm section.
Block decomposition
Six structured blocks:
cap_north (nc × nc × nk)
┌─────────────┐
│ │
sub3 ┌─────┤ N ├─────┐ sub1
(ni×nc) │ │ │ │ (ni×nc)
│ └─────────────┘ │
│ sub0 (ni×nc) │
└──────────────────────── ┘
┌─────────────┐
│ S │
│ │
└─────────────┘
cap_south (nc × nc × nk)with nc = nj // 4 + 1. Each sub-block spans one 90° azimuthal sector; nj must be a multiple of 4 (rounded down with a warning if not).
| Block | Shape | Role |
|---|---|---|
sub0 | (ni, nc, nk, 3) | Equatorial sector, |
sub1 | (ni, nc, nk, 3) | Equatorial sector, |
sub2 | (ni, nc, nk, 3) | Equatorial sector, |
sub3 | (ni, nc, nk, 3) | Equatorial sector, |
cap_north | (nc, nc, nk, 3) | North polar cap (z < 0 of body) |
cap_south | (nc, nc, nk, 3) | South polar cap (z > 0 of body) |
Top-down view
The cap is a square-celled patch that meets the four equatorial sub- blocks at the colatitude-theta_cap parallel. The four cap corners sit on that parallel; cap edges dip slightly poleward at midpoints (intrinsic to the gnomonic-square geometry):

Multi-block connectivity
Sub-sub seams (j-direction)
The four equatorial sub-blocks share their j_lo / j_hi columns with their azimuthal neighbours. The four sub-blocks live in a single contiguous ring allocation (ni, 4·(nc-1)+1, nk, 3); each sub-block is a numpy view into a 90° slice. Adjacent sub-blocks share their boundary j-column by memory aliasing (no reconciliation needed):
| Seam | Mechanism | Boundary condition |
|---|---|---|
| sub0.j_hi ↔ sub1.j_lo | Memory alias (numpy view) | FaceBC.SHARED |
| sub1.j_hi ↔ sub2.j_lo | Memory alias | FaceBC.SHARED |
| sub2.j_hi ↔ sub3.j_lo | Memory alias | FaceBC.SHARED |
| sub3.j_hi ↔ sub0.j_lo | Memory alias (periodic wrap) | FaceBC.SHARED |
Cap-equator seams (i-direction)
The cap's four boundary edges are DIRICHLET-bound to the four equatorial sub-block pole rows:
North cap (z<0 of body):
| Cap edge | ← Equator pole row |
|---|---|
cap_north[0, :] | sub0.nodes[ni-1, :] |
cap_north[:, -1] | sub1.nodes[ni-1, :] |
cap_north[-1, :] | sub2.nodes[ni-1, ::-1] |
cap_north[:, 0] | sub3.nodes[ni-1, ::-1] |
South cap (z>0 of body):
| Cap edge | ← Equator pole row |
|---|---|
cap_south[:, 0] | sub0.nodes[0, :] |
cap_south[-1, :] | sub1.nodes[0, :] |
cap_south[:, -1] | sub2.nodes[0, ::-1] |
cap_south[0, :] | sub3.nodes[0, ::-1] |
The _overwrite_cap_boundaries helper writes the sub-block pole rows into the cap edges at every layer of marching, keeping cap-equator conformity bit-exact regardless of what the cap's interior march would have produced.
Construction order
The CubedSphereTopology.build orchestrator:
- Builds both cap k=0 layers by gnomonic flat-square projection (see k=0 mesh construction). The cap edges are now known.
- Builds the equator k=0 by extracting the cap seam rings and slerp-interpolating great-circle meridians from south seam to north seam. Each meridian's first/last interval is per-j pinned to the cap-edge first cell at the corresponding seam vertex (C1 spacing match).
- Allocates the contiguous equator ring + cap arrays and wires the
HexBlocks withFaceBC.SHARED(sub-sub) andFaceBC.DIRICHLET(cap-equator) face BCs.
Mesh.march then advances all 6 blocks simultaneously layer by layer with seam-aware normals so spacing continuity holds across every multi-block seam at every layer.
Multi-block edge spacing rule
At each cap-equator seam vertex, three multi-block edges meet: two cap-seam edges and one equator meridional edge. The per-j C1 seam pin forces all three first-cells to share one length:

The pin is active when the cap radial step and the equator's natural radial step are within ~2× of each other. Beyond that, the equator falls back to its unpinned i_spacing law and emits one UserWarning per build. See k=0 mesh construction for the rule of thumb on choosing (ni, nj, theta_cap).
Volume seam continuity
Without seam-aware normals, each block computes its boundary normal from a one-sided difference internal to itself. The two sides of a seam disagree, and the disagreement accumulates over many layers as a visible spacing jump on the outer surface. With seam-aware normals (centred differences against ghost rows from the partner block) both sides agree on the normal at every shared node and the spacing stays continuous all the way to the outer layer:

The blue (inner) and orange (outer) layers both show smooth grids across the block seams; cell-size variation on the outer surface follows the natural growth law, not the block decomposition. See Seam-aware normals for the implementation and quantitative verification.
Cap geometry knobs
| Parameter | Effect |
|---|---|
theta_cap | Polar exclusion angle in radians. The 4 cap corners sit at colatitude theta_cap on a unit sphere; cap edges dip slightly poleward at midpoints. Default |
j_spacing | Cap-edge tangent-plane node distribution ('uniform' or 'tanh2'). The equator inherits the same distribution at the seam by DIRICHLET binding. |
j_beta | Clustering strength for tanh2 j-spacing. |
For the equator's meridional distribution (i_spacing, i_beta) and the wall-normal stepping (spacing_k), see Spacing laws.
Per-edge spacing
Per-edge spacing on the cubed-sphere topology has one viable axis: k.
k: the four k-edges of every block can be overridden with a shared
Spacing1Dinstance, andMesh.march()reads it instead of(ds, growth)— same identity-check resolver as the O-grid. The constraint is that all 6 blocks must agree, because the cap-equator DIRICHLET pins and sub-sub SHARED views are k-coupled (their layer positions must coincide layer by layer). Cross-block disagreement triggers aUserWarning; block 0's schedule wins.pythonfrom shore.mesh.spacing import Spacing1D override = Spacing1D(law="geometric", ds=5e-4, growth=1.2) for blk in m.blocks: for name in ("i_lo_j_lo", "i_hi_j_lo", "i_lo_j_hi", "i_hi_j_hi"): blk.edge(name).spacing = override m.march(ds=99, growth=99) # ds, growth ignored — override winsSee
examples/03-per-edge-spacing/per_edge_k_spacing_cubed_sphere.pyfor a worked comparison.i / j: deferred. The cap-equator C1 pinning is a per-edge construction internally — the equator's first and last meridional intervals are pinned per-j to the cap-edge first cell at the corresponding seam vertex — but it is geometric, not user-driven. Making
build_cap_k0andbuild_equatorhonour user-suppliedEdge.first_step/Edge.last_stepwould require a non-trivial rewrite of those builders, and is on the roadmap rather than wired today.
For the full mechanism see Per-edge spacing.
Public API
import numpy as np
from shore.mesh import CubedSphereTopology, Mesh, Spacing1D
m = Mesh.from_stl(
"sphere.stl",
topology=CubedSphereTopology(),
ni=40, nj=60, nk=20,
theta_cap=float(np.pi / 6),
spacing_k=Spacing1D(law="geometric", ds=0.02, growth=1.1),
)
m.march(ds=0.02, growth=1.1)
m.write_geo("sphere") # 6 sphere_<label>.geo filesThe 6 .geo files can be bundled into one ParaView MultiBlock with shore.io.vtk.write_caps_vtm:
from shore.io.geo import read_geo
from shore.io.vtk import write_caps_vtm
labels = ["sub0", "sub1", "sub2", "sub3", "cap_north", "cap_south"]
blocks = [read_geo(f"sphere_{lbl}.geo") for lbl in labels]
write_caps_vtm("sphere.vtm", blocks, with_jacobian=True)See also
- Surface projection — the ray-cast kernel
- k=0 mesh construction — gnomonic cap
- great-circle equator + per-j C1 seam pin
- Hyperbolic marching — outward extrusion
- Seam-aware normals — what keeps spacing continuous across multi-block seams during marching
- O-grid topology — the single-block sibling topology (structurally independent of the cubed sphere)
shore.surface.cap,shore.surface.equator,shore.topology.sphere_cap— Python API