API Overview
Everyday API
Choose the entry point by the result you need. Use generate_grid() when you
want an in-memory object for analysis or reuse:
from grid_generator import generate_grid
grid = generate_grid("R2B4")
grid.to_netcdf("icon_grid_R02B04.nc")
Use generate_grid_to_netcdf() when the file itself is the result:
from grid_generator import TorusGridSpec, generate_grid_to_netcdf
generate_grid_to_netcdf(
TorusGridSpec(nx=12, ny=6, edge_length=1_000.0),
"torus.nc",
)
Both entry points accept every public grid spec and the same grid options.
IconGrid.to_netcdf() is a conversion method for an object already in memory;
generate_grid_to_netcdf() owns generation and chooses the appropriate writing
strategy.
The root package exports the primary grid specifications and generators:
from grid_generator import (
generate_grid,
generate_grid_to_netcdf,
IconGrid,
IconGridOptions,
GlobalGridSpec,
TorusGridSpec,
ChannelGridSpec,
ParallelogramGridSpec,
LimitedAreaGridSpec,
Region,
)
Use the file-oriented path whenever the generated IconGrid is not itself
needed. It validates every grid family through one entry point, computes
NetCDF-only fields in chunks, and publishes the completed file atomically.
Global grids whose complete IconGrid would exceed the base-stage budget use
compact export-first generation:
from grid_generator import generate_grid_to_netcdf
generate_grid_to_netcdf(
"R2B8",
"icon_grid_R02B08.nc",
max_cells=None,
accelerator="numba",
chunk_size=1_000_000,
work_dir="icon-grid-R2B08-work",
fields="reduced",
)
For large global grids this path keeps compact staged topology and checkpoints
completed bisection levels. Limited-area requests use the same mechanism for
their global construction parent, then select cells in chunk_size batches and
materialize only the regional result. This removes the complete global parent
IconGrid from the regional peak. High-resolution global stages, including
those used by a limited-area request, require the accelerate extra.
Planar requests share the same chunked NetCDF writer and use array-based
geometry/topology construction without per-entity Python containers. They have
no recursive refinement stages, so work_dir and resume have no work to do.
chunk_size applies to every family; work_dir and resume apply whenever a
global or limited-area request needs multilevel compact stages. Grid options may
be supplied through options, or directly as keywords as above; direct
keywords take precedence.
If work_dir is omitted, checkpoints are stored in a hidden directory beside
the requested output file. For large grids, put the output or an explicit
work_dir on disk-backed scratch storage rather than a memory-backed temporary
filesystem. Set resume=False to ignore existing checkpoints. chunk_size
limits regional-selection and export-only work arrays, but does not partition
the compact core topology or the final regional/planar topology.
Checkpoint manifests atomically select immutable array snapshots; an interrupted
overwrite therefore leaves the preceding completed snapshot resumable.
If writing fails, the requested output is not published; the .partial file is
left in place for diagnosis.
Allow capacity beyond the completed sizes in the resource tables. Replacing a
checkpoint temporarily retains both snapshots, and replacing an existing output
temporarily retains both the old file and the new .partial file. Successful
checkpoint directories can be deleted unless they are needed to resume or extend
the same configuration. Use distinct output and work-directory paths for
concurrent jobs.
The in-memory base stage is selected by cell count, not by the spelling of the
R<n>B<k> decomposition. By default the largest stage with at most 1,310,720
cells is built as an IconGrid, then later stages use compact refinement. If the
unrefined B0 root already exceeds that budget, export-first generation rejects
the request. A smaller-root decomposition with the same frequency is a different
grid: it changes the canonical spec and UUID, changes the refinement hierarchy,
and can change staged optimization geometry. Use that alternative only when
those differences are acceptable; otherwise use the exact spec through the
in-memory workflow with sufficient memory.
NetCDF field profiles
Both grid.to_netcdf(..., fields=...) and the file-oriented
generate_grid_to_netcdf(..., fields=...) accept the same selector. The default
is the complete schema. They share the same canonical chunked, atomic writer;
the difference is whether the caller already owns an in-memory IconGrid.
| Profile | Fields | Consumer scope | Approximate R2B12 payload |
|---|---|---|---|
"full" |
88 | Complete schema for general exchange | about 1,122.5 GiB |
"reduced" |
46 | Union required by standard ICON and icon4py global-grid readers | 485 GiB |
"icon" |
38 | Standard ICON triangular-grid importer | 410 GiB |
"icon4py" |
26 | icon4py spherical GridManager |
337.5 GiB |
"icon4py_torus" |
35 | icon4py torus GridManager, including Cartesian positions |
Not a spherical storage target |
For icon4py torus input, select "icon4py_torus" or "full":
from grid_generator import TorusGridSpec, generate_grid_to_netcdf
generate_grid_to_netcdf(
TorusGridSpec(nx=4, ny=4, edge_length=1_000.0),
"icon4py_torus.nc",
fields="icon4py_torus",
)
"icon4py_torus" adds cartesian_{x,y,z}_vertices,
cell_circumcenter_cartesian_{x,y,z}, and edge_middle_cartesian_{x,y,z}
to the 26-field spherical profile. These positions retain the planar spec's
physical length units. The profile covers the torus reader's input fields; it
does not certify every torus variant for every model operator.
All named profiles select fixed field sets, independent of grid family. In
particular, "icon4py" and "reduced" do not contain the Cartesian positions
required by icon4py's torus reader. The separate torus profile preserves their
spherical storage sizes and the reduced = icon ∪ icon4py field-set contract.
It is specific to icon4py and does not include the full Cartesian bundle used
by ICON; choose "full" for exchange with other consumers.
The full profile includes the three established compatibility fields
quadrilateral_area, vlon_vertices, and vlat_vertices; the first is an
all-zero compatibility placeholder used by established grid descriptions. The
reduced and ICON profiles omit the complete Cartesian bundle so ICON uses
its existing reconstruction fallback; a partial Cartesian bundle is not
emitted. These profiles cover the standard global readers, not every ocean,
coupling, output-copying, remapping, or institutional tool. Use "full" for
general exchange.
Expert callers can pass an iterable of exact variable names instead of a profile. Unknown names and non-string members fail before expensive generation, duplicates are collapsed, and variables retain the established schema order:
grid.to_netcdf(
"reconstructable_mesh.nc",
fields=["vlon", "vlat", "vertex_of_cell"],
)
Such a custom file is only as compatible as the selected fields. Dimensions,
identity, and configuration metadata are retained, and dangling bounds or
coordinates attributes are removed. On the export-first path, metric-summary
attributes are emitted only when the selected fields require those metrics;
computing omitted metrics would defeat exact field selection. Reduced exports
also skip avoidable bounds, Cartesian, placeholder, hierarchy, and connectivity
work rather than materializing the complete schema first.
Grid Specifications
GlobalGridSpecdescribes spherical ICONR<n>B<k>grids. Strings such as"R2B4"are shorthand for this common path.LimitedAreaGridSpec(parent=..., region=..., selection=..., boundary=...)selects at the preceding global bisection level and refines the compact region once by default.construction="cut_final"selects directly from the requested final-resolution parent.TorusGridSpecdescribes planar doubly periodic triangular torus grids. Its defaultperiodic_layout="rectangular"wraps x and y independently and requires an evenny;periodic_layout="skew"retains a coupled lattice and permits odd row counts.ChannelGridSpecdescribes a planar triangular channel with open boundaries in one direction and periodic boundaries in the other.ParallelogramGridSpecdescribes a skewed planar triangular parallelogram.
Advanced but supported planar variants live in grid_generator.planar:
from grid_generator.planar import RaggedOrthogonalGridSpec, StretchedTorusGridSpec
Specification Signatures
| Object | Parameters |
|---|---|
GlobalGridSpec |
root, bisections, optional name |
TorusGridSpec |
nx, ny, edge_length, optional name, periodic_layout |
ChannelGridSpec |
nx, ny, edge_length, optional name |
ParallelogramGridSpec |
nx, ny, edge_length, optional shear, name |
StretchedTorusGridSpec |
nx, ny, edge_length, optional stretch_x, stretch_y, name, periodic_layout |
RaggedOrthogonalGridSpec |
nx, ny, dx, dy, optional raggedness, name |
LimitedAreaGridSpec |
parent, region, optional boundary_depth, construction, selection, boundary, local_optimization_iterations, name |
Use generate_grid("R2B4") for the common global-grid case. Use explicit spec
objects when the grid family has parameters beyond the standard R<n>B<k>
name.
Planar counts and boundary conditions differ by family:
| Spec | Minimum size | Boundary condition |
|---|---|---|
TorusGridSpec |
nx >= 3, ny >= 3; default layout requires even ny |
Independently periodic x/y by default; optional coupled skew layout |
ChannelGridSpec |
nx >= 3, ny >= 2 |
Periodic in x, open in y |
ParallelogramGridSpec |
nx >= 1, ny >= 1 |
Open |
StretchedTorusGridSpec |
nx >= 3, ny >= 3; default layout requires even ny |
Independently periodic x/y by default; positive x/y stretch; optional coupled skew layout |
RaggedOrthogonalGridSpec |
nx >= 1, ny >= 1 |
Open; 0 <= raggedness < 0.45 |
The regular planar families interpret nx and ny as numbers of rectangular
lattice patches, each split into two triangles. edge_length, dx, and dy
are physical planar coordinate values; supply metres when metre-labelled export
is expected. The package does not convert another planar unit to metres.
stretch_x and stretch_y multiply the regular torus coordinates; shear is
a dimensionless extra x shift per row height; raggedness is a deterministic
fraction of dx/dy used to perturb interior vertices.
Every planar specification exposes domain_length and domain_height. These
are the periodic extents for tori, the x period and open height for channels,
and the nominal unperturbed extents for open parallelogram and ragged grids.
NetCDF export writes these physical values into the geometry attributes
consumed by ICON; it does not substitute spherical-Earth dimensions for planar
domains.
Both torus specs record periodic_layout in metadata and grid UUID identity.
Use the rectangular default for consumers that apply a separate minimum image
to each Cartesian axis. The skew layout is an explicit alternative for coupled
lattice algorithms or odd ny.
Regions
Use Region constructors for limited-area extraction and cutting:
Region.lonlat_box(lon_min=..., lon_max=..., lat_min=..., lat_max=...)Region.circle(lon=..., lat=..., radius_degrees=...)Region.rectangle(center_lon=..., center_lat=..., width_degrees=..., height_degrees=..., angle_degrees=...)Region.rotated_lonlat_box(pole_lon=..., pole_lat=..., center_lon=..., center_lat=..., half_width_lon=..., half_width_lat=...)
Regional policies are explicit public values:
RegionSelectionOptions(inclusion="circumradius", cleanup="remove_ears", buffer_rings=0)controls coverage, mask cleanup, and selection expansion. The default uses circumdisk intersection for circles and lon/lat boxes. Use"overlap","center", orcleanup="none"only for an explicitly different coverage/raw-mask interpretation.OpenBoundaryOptions(metric_closure="clipped", indexing_depth=14, ordering="icon")controls physical versus mirrored dual closure and ICON boundary indexing. Source ordering is intended for in-memory analysis and cannot be exported as a valid ICON regional NetCDF.Region.polygon(((lon0, lat0), (lon1, lat1), ...))
Longitudes and latitudes are in degrees. The default predicate expands circles
and lon/lat boxes by each cell's spherical circumradius.
Package-specific oriented rectangles use the analogous expanded local-plane
predicate, while polygons retain center-or-vertex overlap semantics. For a
longitude box, lon_min > lon_max intentionally selects across the antimeridian.
Options
Pass common options directly to generate_grid():
from grid_generator import generate_grid
grid = generate_grid("R2B4", sphere_radius=6_371_229.0)
raw_grid = generate_grid("R2B4", optimize_global=False)
large_grid = generate_grid("R2B4", max_cells=None)
Use IconGridOptions when the same configuration is reused:
from grid_generator import IconGridOptions, generate_grid
options = IconGridOptions(sphere_radius=6_371_229.0, spring_iterations=2_000)
grid = generate_grid("R2B4", options=options)
options may be an IconGridOptions instance or a mapping. Explicit keyword
arguments take precedence over values in options; omitted keywords retain the
mapping, dataclass, or default value. Unknown option names fail immediately.
options = IconGridOptions(max_cells=1_000, sphere_radius=6_000_000.0)
grid = generate_grid(
"R1B1",
options=options,
max_cells=None, # overrides only this field
)
Common options:
max_cells: generation safety limit. Set toNonefor intentional large grids.sphere_radius: radius used for spherical metric fields.optimize_global: global grids are optimized by default. Limited-area grids use the same setting for their generated global parent. Planar grids do not support global optimization; omit the option or passFalsefor planar specs.north_pole_lon,north_pole_lat, androtation_angle_degrees: spherical orientation controls.
Advanced options:
accelerator:"auto","numpy", or"numba"."auto"uses NumPy for smaller work and uses Numba for selected larger kernels when it is installed; explicit"numba"raises if theaccelerateextra is unavailable. Without Numba,"auto"falls back to the correct deterministic NumPy implementation, but that fallback is not performance-equivalent and is not practical for high-resolution global grids. Installicon-grid-generator[accelerate]for the measured large-grid performance.spring_betaandspring_iterations: global spring relaxation controls.indexing: accepts"new"or"old". It is compatibility metadata and part of grid identity; both values use the same deterministic in-memory ordering.centre,subcentre, andnumber_of_grid_used: exported metadata fields.
Prefer sphere_radius for physical grid metrics. The lower-level radius
option controls the displayed Cartesian coordinate radius and is mainly useful
for tests and visualization.
Planar generation automatically disables the default global optimizer when no
value was supplied. Passing optimize_global=True explicitly for a planar spec
is rejected; omit it or pass False. A default LimitedAreaGridSpec generates
the preceding bisection-level global parent with the same options, compacts the
selection, and performs the final refinement locally. max_cells, orientation,
and global optimization therefore apply to that construction parent.
IconGridOptions.fixed_boundary belongs to the global spring configuration and
has no practical effect on a closed global mesh. It does not configure
post-generation optimize_grid() or diffuse_grid(); set fixed_boundary on
OptimizationOptions or DiffusionOptions for those transforms.
Grid Optimization
“Optimization” refers to three different geometry updates. None of them changes the grid topology: cell/edge/vertex connectivity and refinement relationships remain fixed. Geometry-dependent arrays are rebuilt after vertices move.
Default Global Spring Relaxation
generate_grid("R2B4") optimizes spherical global grids by default. Generation
refines the spherical triangular mesh in stages and spring-relaxes the grid
after refinement stages. Each edge acts as a spherical spring with target
angular length
target_angle = 1.164 * spring_beta * initial_mean_edge_angle
The optimizer integrates a damped velocity and spring-force system,
projects vertices back to the configured Cartesian radius after every step,
and stops early when its force/kinetic-energy criteria are met. The intended
effect is a more uniform distribution of edge lengths and cell areas than raw
bisection; topology is identical.
B0 grids have no bisection stage, so the staged generator has no relaxation
pass to apply even when optimize_global=True. The direct helper below relaxes
an already completed spherical grid.
optimize_global=Trueenables the staged relaxation and is the default.spring_betacontrols the spring rest length. The default0.9is the compatibility setting and is recommended unless results are independently validated.spring_iterationsis a requested iteration cap, not a promise that exactly that many steps run. Smaller refinement stages may receive a larger internal cap, and convergence criteria can stop a stage early.fixed_boundaryhas no practical effect on a closed global grid because it has no open boundary vertices.
Use optimize_global=False for raw bisection geometry diagnostics. It is not
the normal grid-file path.
The direct helper applies one spring-relaxation pass to an already generated spherical global grid:
from grid_generator import generate_grid
from grid_generator.transforms import optimize_global_grid
raw = generate_grid("R1B1", optimize_global=False)
relaxed = optimize_global_grid(
raw,
{"method": "spring", "iterations": 20},
)
This single completed-grid pass is not numerically equivalent to staged
optimization during generate_grid(). The method strings "spring" and
"none" are accepted as shorthands. Passing "none",
{"method": "none"}, or zero iterations returns the input object unchanged.
General Laplacian and Target-Length Smoothing
optimize_grid() works on generated spherical, planar, limited-area, and cut
grids. With the default target_edge_length=None, every movable vertex uses the
simultaneous update
neighbor_mean = vertex + mean(minimum_image(neighbor - vertex))
new_vertex = vertex + relaxation * (neighbor_mean - vertex)
This is Jacobi graph-Laplacian smoothing; it is not a global objective-function
solver. When target_edge_length is positive, each incident edge instead
proposes a point at that length along its current direction, and their mean is
the target. For planar grids the target length uses planar coordinate units. On
spherical grids it is a Cartesian chord length in the configured display
radius, not metres derived from sphere_radius.
from grid_generator import ChannelGridSpec, generate_grid
from grid_generator.transforms import OptimizationOptions, optimize_grid
grid = generate_grid(ChannelGridSpec(nx=8, ny=5, edge_length=1_000.0))
smoothed = optimize_grid(
grid,
OptimizationOptions(
iterations=5,
relaxation=0.15,
fixed_boundary=True,
target_edge_length=None,
),
)
relaxation lies in [0, 1]; smaller values move more cautiously. Zero
iterations or zero relaxation returns the input object unchanged.
Explicit Diffusion
diffuse_grid() uses the same simultaneous neighbor mean but expresses the
fractional move as
effective_step = diffusion_constant * dt * neighbor_weight
new_vertex = vertex + effective_step * (neighbor_mean - vertex)
The defaults give effective_step = 0.1. Values between zero and one interpolate
toward the neighbor mean. Values above one extrapolate beyond it and can degrade
or invert cells; no automatic stability or cell-inversion limiter is applied.
from grid_generator.transforms import DiffusionOptions, diffuse_grid
diffused = diffuse_grid(
grid,
DiffusionOptions(
iterations=5,
diffusion_constant=0.05,
dt=1.0,
neighbor_weight=1.0,
fixed_boundary=True,
),
)
Boundaries, Projection, and Provenance
fixed_boundary=Truefreezes every vertex belonging to an edge with only one adjacent cell. Set it toFalseonly when boundary motion is intended.- Spherical results are projected back to
radius; planar results retain their z coordinate; doubly or horizontally periodic grids use minimum-image neighbor directions and wrap back into the source domain. - Cuts retain their source geometry, so transforming a cut across a periodic seam does not create long seam edges.
- Nontrivial public transforms receive a deterministic new
uuidOfHGridbased on the input UUID and normalized options.uuidOfParHGridremains unchanged, andgeometry_transform_source_uuidrecords the immediate input. - Centers, metric fields, normal vectors, coordinate arrays, and metric-summary metadata are recomputed. Connectivity and refinement arrays are copied unchanged.
- The algorithms do not guarantee monotonic improvement, positive cell area, or application-specific numerical quality for arbitrary settings. Validate the returned grid.
Choosing an Operation
| Need | Recommended operation |
|---|---|
| Normal global ICON-style generation | Keep optimize_global=True |
| Exact raw refinement geometry | optimize_global=False |
| Smooth an existing irregular grid | optimize_grid() with small relaxation |
| Encourage a planar target edge length | optimize_grid(..., target_edge_length=...) |
| Experiment with explicit diffusion | diffuse_grid() with effective step at most one |
Resource Expectations
| Grid | Cells | Edges | Vertices |
|---|---|---|---|
R1B0 |
20 | 30 | 12 |
R1B1 |
80 | 120 | 42 |
R2B3 |
5,120 | 7,680 | 2,562 |
R2B4 |
20,480 | 30,720 | 10,242 |
R2B6 |
327,680 | 491,520 | 163,842 |
R2B8 |
5,242,880 | 7,864,320 | 2,621,442 |
R2B9 |
20,971,520 | 31,457,280 | 10,485,762 |
R2B10 |
83,886,080 | 125,829,120 | 41,943,042 |
R2B11 |
335,544,320 | 503,316,480 | 167,772,162 |
R2B12 |
1,342,177,280 | 2,013,265,920 | 671,088,642 |
Each additional global bisection multiplies all leading counts and approximate
memory work by four. The default max_cells=2_000_000 rejects larger requests
before allocation. max_cells=None removes that safety cap, but not the signed
32-bit index limit. See Performance and Scaling
for measured time, memory, and storage requirements. Large global grids also
require the optional accelerate extra for practical runtime.
R2B12 is the last standard R2 grid whose zero-based and exported one-based cell, edge, and vertex identifiers fit in signed 32-bit integers. R2B13 is rejected before allocation.
Coordinates, Units, and Indexing
The in-memory object deliberately carries both computational Cartesian coordinates and ICON-compatible angular coordinates:
| Field group | Spherical grid | Planar grid |
|---|---|---|
vertices, cell_center_xyz, edge_center_xyz |
Cartesian points on radius |
Cartesian planar coordinates in spec length units |
lon, lat, vertex_lon, vertex_lat, edge_lon, edge_lat |
Geographic degrees | Values linearly scaled into degree-like plotting ranges; not geodetic |
| Length metrics | Metres derived from sphere_radius |
Spec coordinate values, exported as metres |
| Area metrics | Square metres derived from sphere_radius |
Squared spec coordinate values, exported as square metres |
Consequences that matter in downstream code:
- On spherical grids, changing
radiusonly rescales Cartesian display coordinates. Changingsphere_radiusrescales length and area metrics. - Planar
lon/latexists for visualization and ICON field compatibility. Do not use it for geographic region interpretation or map projection. - In-memory
cells,edges,cell_edges,edge_cells, connectivity, and neighbor tables are zero-based. Missing open-boundary neighbors use-1. - Parent-provenance arrays in
refinementalready use ICON's one-based convention and use0for no parent. - NetCDF longitude/latitude variables are radians, and NetCDF connectivity is converted to ICON's one-based representation.
- Spherical NetCDF
edgequad_areais divided bysphere_radius**2for ICON compatibility and is dimensionless. The in-memory and xarray value remains the physical square-metre value. Planaredgequad_areais not normalized.
ICON geometry metadata
grid_geometry uses ICON's geometry enum and is independent of whether a mesh
has an open boundary:
| Grid family | grid_geometry |
open_boundary |
|---|---|---|
| Global spherical | 1 | absent |
| Limited-area or cut spherical | 1 | 1 |
| Planar torus, including stretched torus | 2 | absent |
| Planar channel | 3 | absent |
| General planar parallelogram or ragged grid | 4 | absent |
| Cut of a planar grid | inherited from its source | 1 |
The separate flag matters to ICON: geometry value 3 means planar channel and
would disable spherical behavior if used for a regional spherical grid. The
package uses open_boundary=1 to enable strict regional ordering, metric, and
missing-neighbor checks while preserving the correct coordinate geometry.
The enum values describe files accurately, but they do not imply that every
ICON component implements every geometry. In ICON 2024.10 the standard NWP
least-squares and tangent-plane interpolation dispatch accepts spherical (1)
and planar-torus (2) geometry and rejects channel (3) and general planar
(4) geometry. Treat the latter families as diagnostic or consumer-specific
meshes unless the selected model path explicitly supports them.
Grid Object
IconGrid is the in-memory object returned by generate_grid. It exposes:
dims: cell, edge, and vertex counts.vertices,cells,edges,cell_edges, andedge_cells: core topology.lon,lat,vertex_lon,vertex_lat,edge_lon, andedge_lat: geographic or projected coordinates.geometry: metric fields such as cell area and edge length.connectivityandneighbor_tables: equivalent public connectivity tables using ICON variable names and short aliases, respectively.refinement: ICON refinement and parent-index fields.metadata: scalar grid attributes used for export and provenance.to_dict(),to_xarray(), andto_netcdf(path): conversion helpers.
In-memory consumer contract
The entries listed below in connectivity, neighbor_tables, geometry,
refinement, and metadata are supported public API for completed grids
returned by generation, cutting, and geometry transforms. Their documented
meaning, layout, dtype, and units are compatibility contracts. The dictionaries
may gain additional keys; consumers should select the entries they need rather
than require an exact dictionary key set.
Let C = grid.dims["cell"], E = grid.dims["edge"], and
V = grid.dims["vertex"]. Entity arrays are entity-major: the first axis identifies
the cell, edge, or vertex, and the second axis, where present, identifies a
local neighbor or Cartesian component. No particular memory contiguity or
ownership is guaranteed. Treat completed arrays as immutable; copy before
modifying them, including when passing them to a consumer that writes in place.
Connectivity
Every table below has NumPy dtype int32. Valid indices are zero-based;
-1 marks an absent neighbor or unused slot. Padding occurs both at open
boundaries and at the five-neighbor vertices of closed spherical grids.
connectivity key |
neighbor_tables key |
Shape | Indexed entity |
|---|---|---|---|
edge_of_cell |
c2e |
(C, 3) |
Edge |
vertex_of_cell |
c2v |
(C, 3) |
Vertex |
neighbor_cell_index |
c2e2c |
(C, 3) |
Cell |
adjacent_cell_of_edge |
e2c |
(E, 2) |
Cell |
edge_vertices |
e2v |
(E, 2) |
Vertex |
cells_of_vertex |
v2c |
(V, 6) |
Cell |
edges_of_vertex |
v2e |
(V, 6) |
Edge |
vertices_of_vertex |
v2e2v |
(V, 6) |
Vertex |
The two spellings have equal values; object identity and shared storage are
not part of the contract. cells, edges, cell_edges, and edge_cells
correspond to c2v, e2v, c2e, and e2c, respectively. Local slots retain
their generated ordering: for example, c2e2c[:, j] is the neighbor across
c2e[:, j]. Do not reorder a connectivity independently of its associated
orientation or distance arrays. Mask -1 entries before using a table to index
another array, since NumPy otherwise interprets -1 as the last element.
icon_connectivity is raw implementation storage, retained under its existing
name for compatibility. It mixes zero-based c2e/c2c with one-based,
zero-padded v2c/v2e/v2v, and also contains orientation arrays. Use the
public tables above and geometry for consumer code.
Geometry and coordinates
geometry key |
Shape | NumPy dtype | Quantity |
|---|---|---|---|
cell_area |
(C,) |
float64 |
Area |
dual_area |
(V,) |
float64 |
Area |
edgequad_area |
(E,) |
float64 |
Area |
edge_length, dual_edge_length |
(E,) |
float64 |
Length |
edge_cell_distance, edge_vert_distance |
(E, 2) |
float64 |
Length |
orientation_of_normal |
(C, 3) |
int32 |
Signed orientation |
edge_system_orientation |
(E,) |
int32 |
Signed orientation |
edge_orientation |
(V, 6) |
int32 |
Signed orientation; zero in unused slots |
edge_primal_normal_cartesian, edge_dual_normal_cartesian |
(E, 3) |
float64 |
Dimensionless vector components |
zonal_normal_primal_edge, meridional_normal_primal_edge, zonal_normal_dual_edge, meridional_normal_dual_edge |
(E,) |
float64 |
Dimensionless components |
Spherical lengths and areas use metres and square metres from sphere_radius.
Planar lengths and areas use the spec's length units and their squares; supply
metres when using consumers that expect metres. The in-memory edgequad_area
is a physical area. Its spherical NetCDF normalization by sphere_radius**2
is deliberate and does not apply to the in-memory or xarray value.
vertices, cell_center_xyz, and edge_center_xyz are float64 arrays of
shape (V, 3), (C, 3), and (E, 3), with x/y/z components. Spherical
positions lie on options.radius, which defaults to 1; they are not always
unit vectors. Planar positions use spec length units. The six one-dimensional
angular arrays (lon/lat, edge_lon/edge_lat, and
vertex_lon/vertex_lat) are float64 with shapes (C,), (E,), and (V,),
respectively. They contain degrees, with the planar visualization semantics
described under Coordinates, Units, and Indexing.
Consumers reproducing NetCDF coordinates must convert angles to radians and
normalize spherical Cartesian positions; planar positions are not normalized.
Refinement and metadata
The following refinement arrays have dtype int32 and retain their ICON
conventions in memory; they are not zero-based neighbor tables:
| Keys | Shapes | Meaning |
|---|---|---|
refin_c_ctrl, refin_e_ctrl, refin_v_ctrl |
(C,), (E,), (V,) |
Refinement/boundary control codes, not indices |
parent_cell_index, parent_edge_index, parent_vertex_index |
(C,), (E,), (V,) |
Parent provenance; one-based magnitudes, zero for no parent |
parent_cell_type, edge_parent_type |
(C,), (E,) |
ICON parent-type codes |
start_idx_c, end_idx_c |
(1, 14) |
ICON cell interval tables |
start_idx_e, end_idx_e |
(1, 24) |
ICON edge interval tables |
start_idx_v, end_idx_v |
(1, 13) |
ICON vertex interval tables |
In bisection provenance, a positive parent_vertex_index identifies a parent
vertex and a negative value identifies a parent edge by its one-based absolute
value. Do not interpret all negative provenance as missing. Parent references
address the source grid, not necessarily the returned compact regional mesh.
Interval tables retain ICON's one-based inclusive bounds and empty-interval
encoding; consumers needing local decomposition bounds must derive those on
their side.
metadata always provides string UUIDs uuidOfHGrid and uuidOfParHGrid,
integer grid_geometry, grid_root, and grid_level, and numeric
sphere_radius. A grid without a parent uses the all-zero parent UUID. Planar
root and level values are zero; they are not spherical subdivision settings.
Planar grids also provide domain_length and domain_height in spec length
units. open_boundary=1 marks limited-area and cut grids as described in the
geometry metadata table above; it is not a universal key for every open planar
family. Other metadata may describe the family, provenance, or diagnostics.
For example, a consumer can obtain its own arrays without file I/O:
import numpy as np
from grid_generator import TorusGridSpec, generate_grid
generated = generate_grid(TorusGridSpec(nx=4, ny=4, edge_length=1_000.0))
cell_edges = generated.neighbor_tables["c2e"].copy() # int32, (32, 3)
cell_area = generated.geometry["cell_area"].copy() # float64, m²
cell_lon_radians = np.radians(generated.lon)
cell_xyz = generated.cell_center_xyz.copy() # (32, 3), metres for this spec
Consumer-specific allocation, dtype conversion, domain decomposition, halo
construction, and derived neighbor tables remain the consumer's responsibility.
These arrays do not by themselves supply an in-memory input API to icon4py's
file-oriented GridManager.
Conversion and identity
to_xarray() includes topology, metric, connectivity, refinement, provenance,
metadata, and unit annotations. Its connectivity indices retain the in-memory
zero-based convention and boundary sentinel -1. Parent-provenance fields are
already one-based and use 0 for no parent. Index variables expose these
conventions through start_index and missing_value attributes.
to_netcdf() performs the ICON-specific one-based connectivity conversion and
accepts the field profiles described above.
dataset = grid.to_xarray()
print(dataset[["cell_area", "edge_length", "parent_cell_index"]])
Install the optional xarray dependency with
python -m pip install "icon-grid-generator[xarray]", or add it to an existing
uv project with uv add "icon-grid-generator[xarray]".
to_dict() returns references to the grid's existing NumPy arrays and nested
dictionaries; it is not a deep copy. Although IconGrid and its option/spec
dataclasses are frozen, NumPy buffers remain mutable. Treat completed grids as
immutable values, or copy an array explicitly before modifying it.
to_netcdf(path, fields="full") creates missing parent directories and returns
the resulting Path. Its optional sphere_radius argument must match the
radius used during generation; regenerate with the desired sphere_radius
rather than relabeling already computed metrics during export. If no in-memory
object is needed, call generate_grid_to_netcdf(spec, path, ...) directly.
Grid identity is deterministic: equal canonical specs and options produce the same UUID. A limited-area grid, cut, or geometry transform also incorporates its source UUID, preventing unrelated parents from sharing a derived-grid identity.
Cutting
Cutting an existing grid is an advanced workflow kept in a focused module:
from grid_generator import Region, generate_grid
from grid_generator.cutting import CutGridSpec, cut_grid
parent = generate_grid("R2B4")
cut = cut_grid(
parent,
CutGridSpec(regions=Region.circle(lon=8.0, lat=47.0, radius_degrees=10.0)),
)
For a single-region cut, pass the region directly:
from grid_generator import Region, generate_grid
from grid_generator.cutting import cut_grid
parent = generate_grid("R2B4")
cut = cut_grid(parent, Region.circle(lon=8.0, lat=47.0, radius_degrees=10.0))
Selection is cell based and deterministic:
- The default circumradius policy evaluates spherical cell circumcenters and circumradii, then removes one-neighbor ears. The center-only and center-or-vertex overlap policies are available explicitly. Multiple regions are combined by union.
mode="keep"retains the selected union.mode="remove"takes its complement.RegionSelectionOptions.buffer_rings=Nthen addsNcell-neighbor rings.boundary_depth=Nis the backwards-compatible spelling. On a remove cut, this expands the retained complement, not the removed region.smoothing_depthdoes not move vertices or smooth geometry. It fills the ICONsmooth_c_ctrlrefinement field and records the requested depth as metadata for downstream consumers.- The result is compacted and given ICON boundary-control ordering. Use its parent-index refinement fields to map values back to the source; do not assume that a source cell keeps the same local index.
- Boundary metrics are recomputed. Clipped duals are the default; the mirrored ghost closure is an explicit option.
- A selection containing no cells raises
ValueError.
When passing a CutGridSpec, put all cut options in that object; supplying the
same options again as cut_grid() keywords is rejected. LimitedAreaGridSpec
supports one region and local final refinement; cut_grid() supports multiple
regions, keep/remove mode, and smoothing_depth on an existing grid.
Diagnostics And Transforms
Diagnostics and postprocessing utilities are available from focused modules:
from grid_generator.diagnostics import (
check_grid,
grid_statistics,
triangle_properties,
cell_divergence,
cell_vorticity_fnorm,
)
from grid_generator.transforms import (
diffuse_grid,
optimize_global_grid,
optimize_grid,
)
Their result and option dataclasses are exported from the same submodules. See Grid Optimization for transform behavior and option semantics.
The diagnostic helpers have intentionally narrow meanings:
check_grid()checks core shapes, finite coordinates, index bounds, duplicate edges, reciprocal cell/edge incidence, agreement between cell vertices and their edges, boundary presence, and the expected closed-mesh Euler characteristic.ok=Trueis structural validation, not proof of positive areas, correct scientific metrics, or suitability for a numerical scheme.grid_statistics()summarizes counts plus extrema and means of existing area and edge-length fields.triangle_properties()applies the Euclidean cosine rule to stored edge lengths. Its angles are a practical shape diagnostic, not exact spherical interior angles on coarse global cells.cell_divergence()expects one edge-normal flux value per edge and applies the stored edge orientation, edge length, and cell area.cell_vorticity_fnorm()takes one value per vertex, arithmetically averages the three cell vertices, and divides by a nonzero scalar or per-cell Coriolis field. It is a lightweight postprocessor, not a complete dynamical operator.
A useful minimum validation pattern is:
import numpy as np
from grid_generator.diagnostics import check_grid, triangle_properties
result = check_grid(grid)
if not result.ok:
raise ValueError(result.errors)
triangles = triangle_properties(grid)
assert np.all(np.isfinite(triangles.area))
assert np.all(triangles.area > 0.0)
Visualization
Use the lightweight SVG helper for quick dependency-free grid previews:
from grid_generator import generate_grid
from grid_generator.visualization import write_svg
grid = generate_grid("R1B1", spring_iterations=20)
write_svg(grid, "global_r1b1.svg")
This is a dependency-free diagnostic edge plot, not a map projection or a
metric-preserving scientific visualization. It uses the grid's angular plotting
coordinates, omits seam-crossing segments, and deterministically subsamples
when the edge count exceeds max_edges (default 20,000). Create the destination
directory before calling it.
Root Package Exports
The following names are available directly from grid_generator:
ChannelGridSpecGlobalGridSpecIconGridIconGridOptionsLimitedAreaGridSpecOpenBoundaryOptionsParallelogramGridSpecRegionRegionSelectionOptionsTorusGridSpecgenerate_gridgenerate_grid_to_netcdf