Skip to content

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

  • GlobalGridSpec describes spherical ICON R<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.
  • TorusGridSpec describes planar doubly periodic triangular torus grids. Its default periodic_layout="rectangular" wraps x and y independently and requires an even ny; periodic_layout="skew" retains a coupled lattice and permits odd row counts.
  • ChannelGridSpec describes a planar triangular channel with open boundaries in one direction and periodic boundaries in the other.
  • ParallelogramGridSpec describes 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", or cleanup="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 to None for 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 pass False for planar specs.
  • north_pole_lon, north_pole_lat, and rotation_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 the accelerate extra 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. Install icon-grid-generator[accelerate] for the measured large-grid performance.
  • spring_beta and spring_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, and number_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=True enables the staged relaxation and is the default.
  • spring_beta controls the spring rest length. The default 0.9 is the compatibility setting and is recommended unless results are independently validated.
  • spring_iterations is 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_boundary has 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=True freezes every vertex belonging to an edge with only one adjacent cell. Set it to False only 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 uuidOfHGrid based on the input UUID and normalized options. uuidOfParHGrid remains unchanged, and geometry_transform_source_uuid records 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 radius only rescales Cartesian display coordinates. Changing sphere_radius rescales length and area metrics.
  • Planar lon/lat exists 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 refinement already use ICON's one-based convention and use 0 for no parent.
  • NetCDF longitude/latitude variables are radians, and NetCDF connectivity is converted to ICON's one-based representation.
  • Spherical NetCDF edgequad_area is divided by sphere_radius**2 for ICON compatibility and is dimensionless. The in-memory and xarray value remains the physical square-metre value. Planar edgequad_area is 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, and edge_cells: core topology.
  • lon, lat, vertex_lon, vertex_lat, edge_lon, and edge_lat: geographic or projected coordinates.
  • geometry: metric fields such as cell area and edge length.
  • connectivity and neighbor_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(), and to_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=N then adds N cell-neighbor rings. boundary_depth=N is the backwards-compatible spelling. On a remove cut, this expands the retained complement, not the removed region.
  • smoothing_depth does not move vertices or smooth geometry. It fills the ICON smooth_c_ctrl refinement 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=True is 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:

  • ChannelGridSpec
  • GlobalGridSpec
  • IconGrid
  • IconGridOptions
  • LimitedAreaGridSpec
  • OpenBoundaryOptions
  • ParallelogramGridSpec
  • Region
  • RegionSelectionOptions
  • TorusGridSpec
  • generate_grid
  • generate_grid_to_netcdf