icon_sc.icon.components — ICON process components

Public modules only; solver-internal helpers are not part of the reference.

ICON components hosted on icon_sc.core (architecture §4.3; S07+).

Subpackages mirror ICON’s process taxonomy: icon_sc.icon.components.fast (fast physics, every Δt, sequential-update split); icon_sc.icon.components.dycore (S12: the icon4py nonhydrostatic solver as a DynamicalCore); icon_sc.icon.components.diffusion (S13: the icon4py horizontal-diffusion granule as a Stepper). Real slow physics and transport arrive with their own steps; icon_sc.icon.components.idealized carries the S09 analytic stand-ins that exercise the slow-tendency bus.

ICON fast-physics components (every Δt; tutorial §3.7.2 sequential-update split).

SaturationAdjustment(Stepper) — the icon4py satad granule on ICON-sc state (S07).

The scientific kernel is icon4py’s saturation-adjustment granule (microphysics package, REFERENCES.lock id icon4py-satad): Newton iteration on T at constant total density with the latent-heat closure lwdocvd = L_v(T)/cvd (cvd bookkeeping — satad’s ICON path implies cvd, never cpd; mo_satad.f90, REFERENCES.lock id icon-fortran-satad). ICON-sc does not re-plumb the granule’s internals: array_call builds zero-copy gt4py views of the boundary buffers (icon_sc.core.ingress.gt4py) and invokes granule.run(...) exactly as icon4py’s own integration test does, then applies the returned tendencies over the timestep — x_new = x + dx/dt·Δt is icon4py’s own verification arithmetic against the satad-exit savepoints.

Coupling position (tutorial §3.7.2, REFERENCES.lock id icon-tutorial-2025): fast physics is time-split (“also known as sequential-update split”); satad appears twice in the NWP fast-physics sequence — after the surface-transfer scheme, and again after microphysics so that vapor and liquid are in equilibrium before the slow physics. Hence coupling_constraints.admissible_operators = {"sequential_update_splitting"}: satad as an adjustment-type Stepper enters SUS chains directly and is not admissible under the tendency-summing operator families.

Differentiability: differentiable: "custom" (architecture §8.6) — since S10 the granule is paired with a functional core co-located in this module (_satad_functional(), invoked through functional_call): the saturation fixed point f(T*) = T* - T + (L(T)/cvd)·(qsat_rho(T*, rho) - qv) = 0 is solved by the granule’s own masked Newton iteration (same per-point freeze/convergence semantics, bounded while_loop) wrapped in the icon_sc.core.functional.rules.implicit_fixed_point() lax.custom_root rule, so both AD modes differentiate through the implicit function — the adjoint of the Newton solve via the IFT, never through recorded iterations (which also sidesteps while_loop’s reverse-mode prohibition). Closures and constants come from satad_constants.py (one scheme-constants module per scheme, §11.8). jax imports stay inside the functional entry points.

class icon_sc.icon.components.fast.satad.SaturationAdjustment(grid_or_column, cfg=None, ctx=None, *, name=None)

Saturation adjustment on the column/grid state (frozen interface, SPEC S07).

SaturationAdjustment(grid_or_column, cfg, ctx) — grid_or_column is either

  • a icon_sc.icon.grid.vertical.VerticalGrid (column path): the horizontal extent is discovered from the state at the first call and a trivial pointwise icon4py grid is built around it; or

  • an (icon4py_grid, icon4py_vertical_grid) pair (host-grid path): the granule is hosted on an existing icon4py grid exactly as icon4py’s own tests build it (the L2 datatest path).

State names are the S06 registry rows (air_temperature, specific_humidity, specific_cloud_content, air_density); the T-based granule formulation was chosen because it is the variant with serialized ICON reference data (PLAN pitfall; the θv/exner-coupled variant does not exist as a granule — the muphys variant differs in physics, see REFERENCES.lock icon4py-muphys-satad).

Adjustment happens on levels kstart_moist ≤ k < nlev (moist physics domain); above it the outputs equal the inputs.

Parameters:
coupling_constraints: ClassVar[CouplingConstraints] = CouplingConstraints(must_follow=(), must_precede=(), admissible_operators=('sequential_update_splitting',))

fast physics is sequential-update split; see module docstring.

Type:

tutorial §3.7.2

array_call(inputs, outputs, timestep)

Compute on raw buffers, writing every output in place (frozen ABI).

inputs/outputs are keyed by contract field names (aliases already resolved); outputs is the flat union of the kind’s output dicts.

Parameters:
Return type:

None

functional_call(inputs, params, *, dt)

Pure JAX evaluation of one satad step (§8.6 custom; SPEC S10).

Same boundary as array_call(); reverse/forward derivatives cross the saturation fixed point through the IFT custom_root rule, not the Newton iterations. No tunable params: max_iter/tolerance are solver controls, not scheme physics.

Parameters:
Return type:

dict[str, Any]

class icon_sc.icon.components.fast.satad.SaturationAdjustmentConfig(max_iter=10, tolerance=0.001)

satad configuration (mirrors icon4py SaturationAdjustmentConfig).

Defaults transcribed from icon4py v0.2.0 (REFERENCES.lock icon4py-satad): “in ICON, 10 is always used for max iteration when subroutine satad_v_3D is called” / “1.e-3 is always used for the tolerance”.

Parameters:
max_iter: int = 10

Maximum Newton iterations before ConvergenceError.

tolerance: float = 0.001

Convergence tolerance on the iterated temperature [K].

Microphysics(Stepper) — the icon4py graupel granule on ICON-sc state (S08).

The scientific kernel is icon4py’s single-moment six-class graupel granule (single_moment_six_class_gscp_graupel.py, REFERENCES.lock id icon4py-graupel), the gt4py port of ICON’s gscp_graupel.f90 (now at src/granules/microphysics_1mom_schemes/, REFERENCES.lock id icon-fortran-graupel). ICON-sc does not re-plumb the granule’s internals — in particular the sedimentation/level-loop structure (one forward K-scan carrying the rhoq{r,s,g,i}v_old_kup/vnew_* sedimentation state, plus the two flux programs) stays exactly as the granule has it (SPEC S08 in-scope clause): array_call builds zero-copy gt4py views of the boundary buffers (icon_sc.core.ingress.gt4py) and invokes granule.run(...) exactly as icon4py’s own integration test does, then applies the returned tendencies over the timestep — x_new = x + dx/dt·Δt is icon4py’s own verification arithmetic against the microphysics-exit savepoints.

Scheme selection (SPEC: “scheme selectable by registry name”): concrete schemes subclass Microphysics and register under their name class attribute via the S02 Factory machinery; Microphysics(grid, cfg, ctx, scheme="graupel") (the architecture-§4.3 usage) and Microphysics.factory("graupel", ...) both resolve through that registry. "graupel" is the only registered scheme for now.

Coupling position (tutorial §3.7.2, REFERENCES.lock id icon-tutorial-2025): fast physics is time-split (“also known as sequential-update split”); microphysics sits between turbulent diffusion and the second saturation adjustment in the NWP fast-physics sequence. Hence coupling_constraints.admissible_operators = {"sequential_update_splitting"}.

Differentiability: differentiable: "native" (architecture §8.6) — since S10 the granule is paired with a JAX functional core co-located in this module (_graupel_functional(), invoked through Graupel.functional_call): a line-by-line port of the granule’s scan/flux programs (REFERENCES.lock icon4py-graupel-stencils) sharing this scheme’s constants module (graupel_constants.py) so the two implementations cannot drift (§11.8). Tunable scheme constants are exposed as the §8.6 params declaration (functional_params): the Seifert-Beheng autoconversion/accretion kernel coefficients and the ICON tuning-namelist knobs of GraupelConfig; coefficients the granule precomputes from them (math.gamma expressions) are re-derived in-trace via gammaln so ParamTree gradients flow through them. jax imports stay inside the functional entry points — the component itself never requires jax (the [jax] extra is optional).

class icon_sc.icon.components.fast.microphysics.Graupel(*args, scheme=None, **kwargs)

The single-moment six-class graupel scheme (registry name "graupel").

State names are the S06/S08 registry rows; the scheme steps T and the six moisture tracers, consumes p, rho, the layer thickness icon:ddqz_z_full (the SPEC’s “dz from VerticalGrid via static-state input” — the S06 column builders put it in the state; the datatest takes it from the metrics savepoint) and the cloud droplet number concentration icon:qnc, and diagnoses the four grid-scale surface precipitation rates (ground-level values of the granule’s precipitation-flux fields — icon4py’s own verification arithmetic; icon:*_gsp_rate per ICON prm_nwp_diag).

Stepping happens on levels kstart_moist ≤ k < nlev (moist physics domain); above it the outputs equal the inputs and the tendencies are an exact zero.

Parameters:
coupling_constraints: ClassVar[CouplingConstraints] = CouplingConstraints(must_follow=(), must_precede=(), admissible_operators=('sequential_update_splitting',))

fast physics is sequential-update split; see module docstring.

Type:

tutorial §3.7.2

array_call(inputs, outputs, timestep)

Compute on raw buffers, writing every output in place (frozen ABI).

inputs/outputs are keyed by contract field names (aliases already resolved); outputs is the flat union of the kind’s output dicts.

Parameters:
Return type:

None

functional_params()

Tunable scheme constants exposed to the ParamTree (§8.6 params).

The Seifert-Beheng kernel coefficients (scheme constants, single source graupel_constants.py) plus the ICON tuning-namelist knobs carried by GraupelConfig; every granule coefficient derived from these is re-derived in-trace by the functional core, so their gradients are real.

Return type:

dict[str, float]

functional_call(inputs, params, *, dt)

Pure JAX evaluation of one graupel step (§8.6 native; SPEC S10).

Same boundary as array_call(): contract-named inputs in, the flat union of outputs + diagnostics out (x_new = x + dx/dt·Δt on the moist domain, surface precipitation rates from the ground-level fluxes).

Parameters:
Return type:

dict[str, Any]

class icon_sc.icon.components.fast.microphysics.GraupelConfig(liquid_autoconversion_option=1, snow_intercept_option=2, do_latent_heat_nudging=False, use_constant_latent_heat=True, ice_stickeff_min=0.075, power_law_coeff_for_ice_mean_fall_speed=1.25, exponent_for_density_factor_in_ice_sedimentation=0.33, power_law_coeff_for_snow_fall_speed=20.0, rain_mu=0.0, rain_n0=1.0, snow2graupel_riming_coeff=0.5)

graupel configuration (mirrors icon4py SingleMomentSixClassIconGraupelConfig).

Field-for-field transcription of the icon4py v0.2.0 config (REFERENCES.lock icon4py-graupel), whose defaults are the ICON namelist defaults; the two option enums are carried as their integer values so icon4py stays a lazy import (LiquidAutoConversionType/SnowInterceptParameterization). Options hardcoded to True in ICON (lsedi_ice/lstickeff/ lred_depgrow) are removed by icon4py and therefore absent here too.

Parameters:
  • liquid_autoconversion_option (int)

  • snow_intercept_option (int)

  • do_latent_heat_nudging (bool)

  • use_constant_latent_heat (bool)

  • ice_stickeff_min (float)

  • power_law_coeff_for_ice_mean_fall_speed (float)

  • exponent_for_density_factor_in_ice_sedimentation (float)

  • power_law_coeff_for_snow_fall_speed (float)

  • rain_mu (float)

  • rain_n0 (float)

  • snow2graupel_riming_coeff (float)

liquid_autoconversion_option: int = 1

0 = Kessler (1969), 1 = Seifert & Beheng (2006).

Type:

Liquid autoconversion mode

snow_intercept_option: int = 2

1/2 = Field et al. (2005) best-fit/general moment.

Type:

Snow intercept parameterization

do_latent_heat_nudging: bool = False

Do latent heat nudging (ICON dass_lhn).

use_constant_latent_heat: bool = True

Fixed latent heats for water (ICON ithermo_water == 0).

ice_stickeff_min: float = 0.075

Minimum sticking efficiency (ICON tune_zceff_min).

power_law_coeff_for_ice_mean_fall_speed: float = 1.25

v-qi power-law coefficient for ice fall speed (ICON tune_zvz0i).

exponent_for_density_factor_in_ice_sedimentation: float = 0.33

Density-factor exponent in ice sedimentation (ICON tune_icesedi_exp).

power_law_coeff_for_snow_fall_speed: float = 20.0

v-D power-law coefficient for snow fall speed (ICON tune_v0snow).

rain_mu: float = 0.0

mu exponent in the rain gamma size distribution (ICON mu_rain).

rain_n0: float = 1.0

Rain intercept tuning factor (ICON rain_n0_factor).

snow2graupel_riming_coeff: float = 0.5

Snow-to-graupel riming conversion coefficient (ICON tune_zcsg).

classmethod from_icon4py(config)

Transcribe an icon4py SingleMomentSixClassIconGraupelConfig.

Used by the L2 datatest to consume experiment.config.graupel (the namelist the serialized data was produced with) through the ICON-sc component — the PLAN-pitfall path that pins the exact configuration.

Parameters:

config (Any)

Return type:

GraupelConfig

to_icon4py()

The equivalent icon4py granule config (lazy icon4py import).

Return type:

Any

class icon_sc.icon.components.fast.microphysics.Microphysics(*args, scheme=None, **kwargs)

Grid-scale microphysics on the column/grid state (SPEC S08).

Registry root (S02 Factory): concrete schemes subclass this and set the name class attribute; Microphysics(grid_or_column, cfg, ctx, scheme=<registry name>) dispatches construction to the registered scheme class (default "graupel"), and Microphysics.factory(name, ...) is the explicit registry path. Constructing a concrete subclass directly (Graupel(...)) is equivalent.

Microphysics(grid_or_column, cfg, ctx) — grid_or_column is either

  • a icon_sc.icon.grid.vertical.VerticalGrid (column path): the horizontal extent is discovered from the state at the first call and a trivial pointwise icon4py grid is built around it; or

  • an (icon4py_grid, icon4py_vertical_grid) pair (host-grid path): the granule is hosted on an existing icon4py grid exactly as icon4py’s own tests build it (the L2 datatest path).

Parameters:
  • args (Any)

  • scheme (str | None)

  • kwargs (Any)

Return type:

Microphysics

NonhydroSolver(DynamicalCore) — icon4py solve_nonhydro hosted on ICON-sc (S12).

The icon4py nonhydrostatic solver + velocity advection as one ICON-sc component (architecture §4.3): the predictor-corrector is the stage structure, ndyn_substeps is the super-fast tier, slow physics enters through the tendency-bus port, and the two prognostic time levels plus the velocity-advection carry-over are component-private state behind the restart/functional-state protocols.

Hosting policy (wrap-don’t-rewrite, §4.4): the ~50 predictor/corrector stencil programs stay icon4py granule internals (REFERENCES.lock icon4py-solve-nonhydro); ICON-sc invokes run_predictor_step/run_corrector_step exactly the way icon4py’s own integration tests and driver do (REFERENCES.lock icon4py-solve-nonhydro-tests, icon4py-driver-dyn-substepping).

Tier nesting (declared deviation from the S04 default orchestration). The base DynamicalCore unrolls Fig. 3.10 stage-outer (each stage runs its own substep block). ICON nests the other way round: each of the ndyn_substeps substeps runs the full predictor→corrector stage pair (mo_nh_stepping.f90::perform_dyn_substepping, REFERENCES.lock icon-fortran-solve-nonhydro-stepping). NonhydroSolver therefore overrides array_call — the base class explicitly routes tier orchestration through that method — while keeping the frozen S04 subclass hooks: substep_array_call(stage, substep, ...) executes one stage of one substep and stage_array_call is its degenerate single-substep form. Data flows through the component-private icon4py state objects (communicates_internally=True, §4.3): the hook inputs/outputs dicts are the boundary buffers ingested/egressed once per step by array_call.

Slow-tendency bus port (tutorial §3.7.2, REFERENCES.lock icon-fortran-solve-nonhydro-stepping): the slots icon:ddt_vn_phy and icon:ddt_exner_phy are ordinary input fields, held constant across the step and consumed inside the substeps at the points ICON’s numerics dictate — ddt_vn_phy in the vn update of every predictor and corrector (mo_solve_nonhydro.f90 l.1365, l.1410), ddt_exner_phy in the explicit part of the vertically implicit solver (l.2316/2340). Absent slots default to zeros so the S13 JW run needs no physics.

class icon_sc.icon.components.dycore.NonhydroConfig(itime_scheme=4, iadv_rhotheta=2, igradp_method=3, rayleigh_type=2, divdamp_order=24, divdamp_type=3, ndyn_substeps=5, ndyn_substeps_max=12, rhotheta_offctr=-0.1, veladv_offctr=0.25, divdamp_fac=0.0025, divdamp_fac2=0.004, divdamp_fac3=0.004, divdamp_fac4=0.004, divdamp_z=32500.0, divdamp_z2=40000.0, divdamp_z3=60000.0, divdamp_z4=80000.0, extra_diffu=True, max_nudging_coefficient=0.1, l_vert_nested=False, deepatmos_mode=False, iau_init=False, second_order_divdamp_factor=0.032, prepare_advection=False)

solve_nonhydro configuration (SPEC S12: mirrors icon4py NonHydrostaticConfig).

Every field carries an icon_namelist_origin annotation (dataclass metadata, machine-readable via icon_namelist_origins()). Defaults are icon4py v0.2.0 defaults, which transcribe the ICON Fortran namelist defaults (REFERENCES.lock icon4py-solve-nonhydro). The icon4py granule accepts only the operational slice (itime_scheme=4, iadv_rhotheta=2, igradp_method=3, Klemp Rayleigh damping, extra_diffu=True); to_icon4py() re-runs its validation, so an unsupported knob fails at component construction.

Parameters:
  • itime_scheme (int)

  • iadv_rhotheta (int)

  • igradp_method (int)

  • rayleigh_type (int)

  • divdamp_order (int)

  • divdamp_type (int)

  • ndyn_substeps (int)

  • ndyn_substeps_max (int)

  • rhotheta_offctr (float)

  • veladv_offctr (float)

  • divdamp_fac (float)

  • divdamp_fac2 (float)

  • divdamp_fac3 (float)

  • divdamp_fac4 (float)

  • divdamp_z (float)

  • divdamp_z2 (float)

  • divdamp_z3 (float)

  • divdamp_z4 (float)

  • extra_diffu (bool)

  • max_nudging_coefficient (float)

  • l_vert_nested (bool)

  • deepatmos_mode (bool)

  • iau_init (bool)

  • second_order_divdamp_factor (float)

  • prepare_advection (bool)

ndyn_substeps: int = 5

Δτ = Δt/N (the DynamicalCore substeps default).

Type:

The super-fast tier ratio N

ndyn_substeps_max: int = 12

CFL-escalation bound for adaptive ratio providers (ICON caps at 12).

max_nudging_coefficient: float = 0.1

icon4py default = DEFAULT_DYNAMICS_TO_PHYSICS_TIMESTEP_RATIO(5) * 0.02.

second_order_divdamp_factor: float = 0.032

ICON derives divdamp_fac_o2 from the spinup ramp at runtime; fixed per run here.

prepare_advection: bool = False

ICON derives lprep_adv from ltransport; S12 hosts no transport — default off.

to_icon4py()

The icon4py NonHydrostaticConfig (runs the granule’s own validation).

Return type:

Any

classmethod from_icon4py(cfg, *, ndyn_substeps, second_order_divdamp_factor=0.032, prepare_advection=False)

Mirror an icon4py NonHydrostaticConfig (e.g. a datatest archive’s).

Parameters:
  • cfg (Any)

  • ndyn_substeps (int)

  • second_order_divdamp_factor (float)

  • prepare_advection (bool)

Return type:

NonhydroConfig

class icon_sc.icon.components.dycore.NonhydroSolver(grid, vgrid, static, cfg=None, ctx=None, *, substeps=0, ratio_provider=None, fast_tendency_component=None, edge_geometry=None, cell_geometry=None, owner_mask=None, exchange=None, name=None)

The ICON nonhydrostatic solver as a ICON-sc DynamicalCore (SPEC S12).

NonhydroSolver(grid, vgrid, static, cfg, ctx, *, substeps=..., ...) —

  • grid: a icon_sc.icon.grid.IconGrid (production path: geometry and owner mask derived from it) or a raw icon4py IconGrid (host-grid path, as icon4py’s own tests build it — then edge_geometry/cell_geometry/ owner_mask must be passed explicitly);

  • vgrid: a icon_sc.icon.grid.VerticalGrid or a raw icon4py VerticalGrid;

  • static: mapping of the STATIC_FIELDS registry names to S11 static-state DataArrays (metrics(grid, vgrid) | interpolation(grid)) or to ready icon4py fields (savepoint parity path — used as-is, zero conversion);

  • cfg: NonhydroConfig (namelist knobs, annotated origins);

  • substep tier: substeps fixes the ratio N (default cfg.ndyn_substeps); ratio_provider(state) -> int makes it adaptive (called once per step, S04 semantics; bounded by cfg.ndyn_substeps_max). CFL exposure for adaptive providers is stubbed to the fixed ratio in this slice — the carry field carry/max_vertical_cfl (ICON max_vcfl_dyn) is serialized and readable via max_vertical_cfl, but no provider ships until the CFL diagnostics land (declared, SPEC S12).

The component is a first-class sympl-style citizen: constructible and callable without any federation (acceptance 5). One __call__ advances one full Δt = N substeps of Δτ = Δt/N, each substep running predictor→corrector with the velocity-advection reuse of ICON’s itime_scheme=4 (MOST_EFFICIENT).

Parameters:
n_stages: ClassVar[int] = 2

Number of stages of the time-marching scheme (subclass contract).

substep_fraction: ClassVar[float] = 1.0

Per-stage fraction of the total substep count N (scalar broadcasts).

substep_nesting: ClassVar[str] = 'substep_outer'

ICON nests substeps outer (module docstring); the S14 plan compiler unrolls the step through the plan-hook quartet below (contract documented on DynamicalCore.substep_nesting).

tendency_port: ClassVar[Mapping[str, str]] = {'icon:exner_function': 'icon:ddt_exner_phy', 'icon:normal_wind': 'icon:ddt_vn_phy'}

Prognostic field name -> input slot carrying its slow tendency (may be partial).

communicates_internally: ClassVar[bool] = True

halo exchanges happen inside the hosted granule.

Type:

§4.3 table

hook_log: list[tuple[Any, ...]] | None

set to a list to record; None = off.

Type:

Hook-order recording (acceptance 2)

property max_vertical_cfl: float

ICON max_vcfl_dyn — the CFL diagnostic an adaptive ratio provider reads.

visit(plan_builder)

S05/S14 plan hook: unroll the ICON substep-outer nesting.

substep_nesting = "substep_outer" routes the compiler to the S14 per-substep op sequence (ingress → N x [carry swaps → predictor → corrector → time-level swap] → egress) via the plan-hook quartet; the component-private icon4py state never reaches the vault (§4.5, PLAN S14 pitfall) — the vault sees only the boundary prognostics’ step-level ping-pong.

Parameters:

plan_builder (PlanBuilder)

Return type:

None

plan_ingress(n_substeps, inputs, outputs, timestep)

T1 step entry: boundary buffers → private time levels + bus (S14).

Mirrors the array_call preamble exactly (the substep-count guards ran at bind; they are re-checked here so a mutated component fails loudly).

Parameters:
Return type:

None

plan_substep_begin(substep, inputs, outputs, timestep)

T1 substep entry: the MOST_EFFICIENT advective-tendency pair swaps.

Component-private carry swaps (never vault swaps); the initial-timestep exception lives in the private _at_initial_timestep flag exactly as at T0 (§4.5 restart semantics are preserved verbatim).

Parameters:
Return type:

None

plan_substep_end(substep, inputs, outputs, timestep)

T1 inter-substep boundary: private nnow/nnew time-level swap.

Parameters:
Return type:

None

plan_egress(inputs, outputs, timestep)

T1 step exit: bookkeeping + private state → boundary output buffers.

Parameters:
Return type:

None

array_call(inputs, outputs, timestep)

ICON tier orchestration: substeps outer, predictor→corrector inner.

Mirrors mo_nh_stepping.f90::perform_dyn_substepping and the icon4py driver _do_dyn_substepping (REFERENCES.lock icon-fortran-solve-nonhydro-stepping, icon4py-driver-dyn-substepping): Δτ = Δt/N; tendency-pair swaps implement the MOST_EFFICIENT velocity-advection reuse; nnow/nnew swap between substeps but not after the last.

Parameters:
Return type:

None

substep_array_call(stage, substep, inputs, outputs, dt)

One stage (0 = predictor, 1 = corrector) of one substep, Δτ = dt.

Data flows through the component-private icon4py states (communicates_internally); inputs/outputs are the step-level boundary buffers, ingested/egressed by array_call().

Parameters:
Return type:

None

stage_array_call(stage, inputs, outputs, dt)

Degenerate single-substep form of substep_array_call() (S04 hook).

Not used by the ICON orchestration (array_call always substeps); provided so the frozen S04 subclass contract is complete.

Parameters:
Return type:

None

restart_state()

Both prognostic time levels + the velocity-advection carry (SPEC S12).

Return type:

dict[str, DataArray]

load_restart_state(restart)

Restore the full private-state schema (strict: all keys, no extras).

Parameters:

restart (Mapping[str, DataArray])

Return type:

None

functional_state()

The restart schema as PropertySpecs (F-tier declaration; consumption is P6).

Return type:

Mapping[str, PropertySpec]

icon_sc.icon.components.dycore.icon_namelist_origins(config)

Field name -> ICON namelist origin, from the dataclass metadata annotations.

Works for any origin-annotated config dataclass (NonhydroConfig, S13’s DiffusionConfig, …) — a backward-compatible widening of the S12 signature (declared in the S13 STATUS).

Parameters:

config (Any)

Return type:

dict[str, str]

HorizontalDiffusion(Stepper) — icon4py Diffusion hosted on ICON-sc (S13).

The ICON horizontal diffusion granule (Smagorinsky + 4th-order background, mo_nh_diffusion.f90 / hdiff_order=5) as one ICON-sc component, following the S12 hosting policy (wrap-don’t-rewrite, architecture §4.4): the ~16 stencil programs stay icon4py granule internals (REFERENCES.lock icon4py-diffusion); ICON-sc invokes the granule’s single public entry run(diagnostic_state, prognostic_state, dtime, initial_run) exactly the way icon4py’s own integration tests and driver do (REFERENCES.lock icon4py-diffusion-tests, icon4py-driver-jw).

Boundary: prognostics in/out on their locations — icon:normal_wind (edgexK), upward_air_velocity_on_interface_levels (cellxK+1), icon:exner_function and icon:virtual_potential_temperature (cellxK). air_density is deliberately not part of the boundary: the granule never reads or writes rho (its PrognosticState slot is satisfied by a private zero field). One __call__ = one diffusion step over the full Δt — ICON applies diffusion once per physics timestep, after the dynamics substepping (driver _integrate_one_time_step; mo_nh_stepping.f90).

ICON’s initial stabilization run (“for real-data runs, perform an extra diffusion call before the first time step”) is exposed as initial_stabilization() — an extra, explicitly requested call that uses the granule’s initial_run=True special coefficients; it never happens implicitly.

The turbulence-coupling diagnostics the granule accumulates (hdef_ic, div_ic, dwdx, dwdy; consumed by NWP turbulence, a P3 component) are component-private state behind the restart/functional-state protocols (§4.5), zero-initialized at construction exactly like the icon4py driver does.

class icon_sc.icon.components.diffusion.DiffusionConfig(diffusion_type=5, hdiff_w=True, hdiff_vn=True, hdiff_temp=True, hdiff_smag_w=False, type_vn_diffu=1, smag_3d=False, type_t_diffu=2, hdiff_efdt_ratio=36.0, hdiff_w_efdt_ratio=15.0, smagorinski_scaling_factor=0.015, smagorinski_scaling_factor2=0.07137250670656087, smagorinski_scaling_factor3=0.0, smagorinski_scaling_factor4=1.0, smagorinski_scaling_height=32500.0, smagorinski_scaling_height2=60686.25335328044, smagorinski_scaling_height3=50000.0, smagorinski_scaling_height4=90000.0, ndyn_substeps=5, zdiffu_t=True, velocity_boundary_diffusion_denom=200.0, temperature_boundary_diffusion_denom=135.0, max_nudging_coefficient=0.1, shear_type=0, iforcing=0, a_hshr=1.0, loutshs=False)

Horizontal-diffusion configuration (SPEC S13: mirrors icon4py DiffusionConfig).

Every field carries an icon_namelist_origin annotation (machine-readable via icon_namelist_origins()). Defaults are icon4py v0.2.0 defaults, which transcribe the ICON Fortran namelist defaults (REFERENCES.lock icon4py-diffusion). The icon4py granule accepts only the operational slice (diffusion_type=5, type_vn_diffu=1, type_t_diffu=2, no Smagorinsky-w, no 3d Smagorinsky, shear_type in (0, 1, 2)); to_icon4py() re-runs its validation, so an unsupported knob fails at component construction.

Parameters:
  • diffusion_type (int)

  • hdiff_w (bool)

  • hdiff_vn (bool)

  • hdiff_temp (bool)

  • hdiff_smag_w (bool)

  • type_vn_diffu (int)

  • smag_3d (bool)

  • type_t_diffu (int)

  • hdiff_efdt_ratio (float)

  • hdiff_w_efdt_ratio (float)

  • smagorinski_scaling_factor (float)

  • smagorinski_scaling_factor2 (float)

  • smagorinski_scaling_factor3 (float)

  • smagorinski_scaling_factor4 (float)

  • smagorinski_scaling_height (float)

  • smagorinski_scaling_height2 (float)

  • smagorinski_scaling_height3 (float)

  • smagorinski_scaling_height4 (float)

  • ndyn_substeps (int)

  • zdiffu_t (bool)

  • velocity_boundary_diffusion_denom (float)

  • temperature_boundary_diffusion_denom (float)

  • max_nudging_coefficient (float)

  • shear_type (int)

  • iforcing (int)

  • a_hshr (float)

  • loutshs (bool)

ndyn_substeps: int = 5

The diffusion coefficients scale with the substep ratio (must equal the dycore’s).

max_nudging_coefficient: float = 0.1

icon4py default = DEFAULT_DYNAMICS_TO_PHYSICS_TIMESTEP_RATIO(5) * 0.02.

to_icon4py()

The icon4py DiffusionConfig (runs the granule’s own slice validation).

Return type:

Any

classmethod from_icon4py(cfg)

Mirror an icon4py DiffusionConfig (e.g. a datatest archive’s namelist).

Parameters:

cfg (Any)

Return type:

DiffusionConfig

class icon_sc.icon.components.diffusion.HorizontalDiffusion(grid, vgrid, static, cfg=None, ctx=None, *, edge_geometry=None, cell_geometry=None, exchange=None, name=None)

The ICON horizontal diffusion as a ICON-sc Stepper (SPEC S13).

HorizontalDiffusion(grid, vgrid, static, cfg, ctx, *, ...) — constructor mirrors the S12 NonhydroSolver shape (architecture §5.1 sketches HorizontalDiffusion(grid, static, cfg.diffusion, ctx); the hosted granule additionally requires the vertical grid for its Smagorinsky enhancement profile, so vgrid sits in the S12 position):

  • grid: a icon_sc.icon.grid.IconGrid (production path — geometry derived from it) or a raw icon4py IconGrid (host-grid path, geometry passed explicitly — how the parity tests mirror upstream);

  • vgrid: a icon_sc.icon.grid.VerticalGrid or a raw icon4py VerticalGrid;

  • static: mapping of the STATIC_FIELDS registry names to S11 static-state DataArrays (metrics(...) | interpolation(...)) or ready icon4py fields (savepoint parity path — zero conversion);

  • cfg: DiffusionConfig.

One __call__ advances one full Δt (diffusion runs once per physics timestep, after the dynamics substepping — icon4py driver order). The granule mutates its state in place; ICON-sc ingests/egresses the boundary buffers around it (communicates_internally=True: halo exchanges happen inside the granule).

Parameters:
communicates_internally: ClassVar[bool] = True

halo exchanges happen inside the hosted granule.

Type:

§4.3 table

initial_stabilization(state, timestep, *, out=None)

ICON’s extra pre-timeloop diffusion call (linit=.TRUE.).

“For real-data runs, perform an extra diffusion call before the first time step because no other filtering of the interpolated velocity field is done” (icon4py Diffusion.run docstring). Uses the granule’s initial_run=True coefficients (setup_fields_for_initial_step, smag_offset=0); never invoked implicitly — the caller decides (driver apply_initial_stabilization).

Parameters:
Return type:

tuple[dict[str, DataArray], dict[str, DataArray]]

array_call(inputs, outputs, timestep)

One diffusion step over the full Δt (granule run, in-place).

Parameters:
Return type:

None

restart_state()

The turbulence-coupling diagnostics the granule accumulates (SPEC S13).

Return type:

dict[str, DataArray]

load_restart_state(restart)

Restore the full private-state schema (strict: all keys, no extras).

Parameters:

restart (Mapping[str, DataArray])

Return type:

None

functional_state()

The restart schema as PropertySpecs (F-tier declaration; consumption is P6).

Return type:

Mapping[str, PropertySpec]

icon_sc.icon.components.diffusion.icon_namelist_origins(config)

Field name -> ICON namelist origin, from the dataclass metadata annotations.

Works for any origin-annotated config dataclass (NonhydroConfig, S13’s DiffusionConfig, …) — a backward-compatible widening of the S12 signature (declared in the S13 STATUS).

Parameters:

config (Any)

Return type:

dict[str, str]

Idealized forcing components for column presets (S09).

The S09 SCM composition exercises the slow-tendency bus end-to-end before a real dynamical core exists to consume it (architecture §4.2 “the bus, reframed”; SPEC S09 in-scope): a PrescribedCooling slow process publishes a piecewise-constant temperature tendency into the icon:ddt_temperature_slow slot (seeded for exactly this purpose in icon_sc.icon.names, S06), and a trivial ApplySlowTendencies stepper stands in for the consumer the tutorial describes (§3.7.2, REFERENCES.lock icon-tutorial-2025: slow-physics tendencies are “kept constant between two successive calls” and act as forcing terms integrated inside the dynamical core / tracer transport — here, at column scale, a forward-Euler application is that integration).

PrescribedCooling is analytic (PLAN S09 item 1): Newtonian relaxation of the temperature toward an offset ICON reference-atmosphere profile,

dT/dt = (T_eq(z) - T) / tau, T_eq(z) = T_ref(z) - offset,

with T_ref the decaying-isothermal reference temperature mined in S06 (mo_vertical_grid.f90; icon_sc.icon.grid.vertical.reference_temperature()). The shape is the standard idealized radiative-cooling stand-in (relaxation-type forcing); the default timescale/offset are test-fixture magnitudes chosen at typical tropospheric cooling rates (~1-5 K/day), not mined scientific constants (S06 moist_test_column precedent).

Neither component is an ICON scheme; real slow physics (radiation, convection, …) arrives in P3, the real consumer (the dycore’s slow port) in S12.

class icon_sc.icon.components.idealized.ApplySlowTendencies(*, ctx=None, name=None)

Trivial bus consumer: forward-Euler application of the slow T tendency.

Stands in for the dynamical core’s slow-tendency input port (architecture §4.2/§4.3) until S12: T_new = T + Δt · ddt_temperature_slow, with the slot value held piecewise-constant by the publisher’s CallingFrequency wrapper. Declared as an input (not recomputed), so the S04 bus checker can verify the publish/consume wiring of the preset.

Parameters:
array_call(inputs, outputs, timestep)

Compute on raw buffers, writing every output in place (frozen ABI).

inputs/outputs are keyed by contract field names (aliases already resolved); outputs is the flat union of the kind’s output dicts.

Parameters:
Return type:

None

functional_call(inputs, params, *, dt)

Pure forward-Euler application of the bus slot (§8.6 native; S10).

Parameters:
Return type:

dict[str, Any]

class icon_sc.icon.components.idealized.PrescribedCooling(cfg=None, ctx=None, *, name=None)

Analytic radiative-cooling stand-in publishing to the tendency bus (SPEC S09).

dT/dt = (T_eq(z) - T)/tau with T_eq(z) = T_ref(z) - offset (module docstring). The tendency is published under the bus-slot name icon:ddt_temperature_slow (canonical units K s-1), not as a plain air_temperature tendency: slow-process output rides the §4.2 bus and is consumed by exactly one consumer, checked at composition time by SlowTendencyBus.

Parameters:
array_call(inputs, outputs, timestep)

Compute on raw buffers, writing every output in place (frozen ABI).

inputs/outputs are keyed by contract field names (aliases already resolved); outputs is the flat union of the kind’s output dicts.

Parameters:
Return type:

None

functional_call(inputs, params, *, dt)

Pure evaluation of the relaxation tendency (§8.6 native; S10).

reference_temperature is array-namespace generic (S06), so the same formula traces under jax unchanged — one shared source for both tiers.

Parameters:
Return type:

dict[str, Any]

class icon_sc.icon.components.idealized.PrescribedCoolingConfig(relaxation_timescale=datetime.timedelta(days=1), equilibrium_offset=5.0)

Newtonian-relaxation parameters (test-fixture magnitudes, see module note).

Parameters:
relaxation_timescale: timedelta = datetime.timedelta(days=1)

Relaxation (e-folding) timescale of the Newtonian cooling.

equilibrium_offset: float = 5.0

Equilibrium profile offset below the ICON reference atmosphere [K].