rdb_ice_itd Module

Port of SIS2 adjust_ice_categories (SIS_transport.F90:611-891, Apache-2.0). SIS2’s shipped algorithm is NOT the Lipscomb (2001) linear-profile remap — that is a SIS_transport.F90:737 TODO comment, never code. What SIS2 actually runs (and what this module ports) is the “for now move all of it” WHOLE-CATEGORY shift: when a category’s mean ice mass crosses a bin boundary, its ENTIRE area+mass moves to the adjacent category, merged into that category’s existing area+mass via an area-weighted destination- thickness average (mass AND area conserving, SIS2:730-741) with upwind mass-weighted tracer mixing (equivalent in effect to SIS2’s deferred advect_tracers_thicker, SIS_tracer_advect.F90:1791-1850 — each category boundary is visited once per pass in a fixed order with running masses, so a single closed-form mass-weighted mix at the transfer site reproduces the same result; verified by the validated Python prototype, tmp_local_artifacts/proto_itd_adjust.py).

Ported with ice_cover_discard = -1 (off, SIS2 default), ocean_part_min = 0, inconsistent_cover_bug = .false. (i.e. DO the open-water resum).

Algorithm per cell (all steps operate on the SAME category array — order matters, see the docstrings of each impl step below): 1. Massless cleanup (SIS2:664-685): any category with m_ice <= 0 has its area returned to open water (flagged for the final resum, not applied immediately). 2. Upward pass, c = 1..ncat-1 ascending (SIS2:714-767): a category whose ice mass exceeds ITS OWN upper bin edge (mh_lim(c+1)) moves ALL its area+mass into category c+1. 3. Downward pass, c = ncat..2 descending (SIS2:786-839): mirror of step 2 — a category whose ice mass falls below its OWN lower bin edge (mh_lim(c)) moves ALL its area+mass into category c-1. 4. Cat-1 minimum-thickness compress (SIS2:850-864): category 1 has no category below it to demote into (mh_lim(1) > 0 is the physical floor), so instead of a transfer it AREA-SHRINKS: part(1) *= m_ice(1)/mh_lim(1), pinning m_ice(1) at the floor (mass- and per-remaining-area-tracer-conserving; the lost area returns to open water via the resum). 5. Open-water resum (SIS2:874-889): only if step 1 or step 4 fired (the up/down transfers of steps 2-3 preserve Σ part exactly, add/subtract the same part_trans on both sides, so they never need a resum) — part(0) = max(1 - Σ_{c=1..ncat} part(c), 0).

Deliberately NOT ported (SIS2 features orthogonal to the “for now move all of it” shift, or infra this branch does not have yet): melt ponds (no pond state on ocean_sea_ice_t), t_surf category remap (tsurf_out here is per-thermo-window scratch, recomputed from scratch every step — nothing to remap), ice_cover_discard (SIS2 default off), the FATAL input-consistency checks (a GPU do concurrent kernel cannot abort mid-loop — negative mass / snow-on-no-ice is treated as “massless”, the same taxonomy as step 1), and SIS2’s do_j per-row early-exit optimisation (the per-cell if gate below is the DC-kernel equivalent). Ridging is a SEPARATE, still-unported gap — NOT closed by compress_ice (PR 4b, rdb_ice_transport): compress_ice is SIS2’s own DO_RIDGING=.false. fallback (area compaction, in-category, zero energetic cost), not the participation/redistribution ridging scheme SIS2 ships as an Icepack wrapper (ice_ridge.F90, default off). See docs/CAPABILITIES_AND_LIMITATIONS.md “Sea ice” for the physical consequence.

ncat==1 short-circuit (bit-identity contract, module docstring of rdb_ice_state): the outer shim returns immediately when ice%ncat == 1 — the legacy lumped mode never runs any part of this module, by construction (not by an exactness argument). SIS2 itself would still run the cat-1 compress at nCat=1 (mh_lim(1) = 1e-10 m makes it physically unreachable in practice), but skipping it outright is the simplest statement of “ncat=1 is untouched” and is the explicit contract this branch commits to.


Uses

  • module~~rdb_ice_itd~~UsesGraph module~rdb_ice_itd rdb_ice_itd module~rdb_constants rdb_constants module~rdb_ice_itd->module~rdb_constants module~rdb_grid rdb_grid module~rdb_ice_itd->module~rdb_grid module~rdb_ice_state rdb_ice_state module~rdb_ice_itd->module~rdb_ice_state module~rdb_multilayer_state rdb_multilayer_state module~rdb_ice_itd->module~rdb_multilayer_state pic_types pic_types module~rdb_constants->pic_types module~rdb_grid->module~rdb_constants module~rdb_ice_state->module~rdb_constants module~rdb_ice_state->module~rdb_grid iso_fortran_env iso_fortran_env module~rdb_ice_state->iso_fortran_env module~rdb_ice_column rdb_ice_column module~rdb_ice_state->module~rdb_ice_column module~rdb_ice_enthalpy rdb_ice_enthalpy module~rdb_ice_state->module~rdb_ice_enthalpy module~rdb_mem_report rdb_mem_report module~rdb_ice_state->module~rdb_mem_report module~rdb_multilayer_state->module~rdb_constants module~rdb_multilayer_state->module~rdb_grid module~rdb_multilayer_state->iso_fortran_env module~rdb_efp rdb_efp module~rdb_multilayer_state->module~rdb_efp module~rdb_error_ring rdb_error_ring module~rdb_multilayer_state->module~rdb_error_ring module~rdb_multilayer_state->module~rdb_mem_report module~rdb_tracer rdb_tracer module~rdb_multilayer_state->module~rdb_tracer pic_logger pic_logger module~rdb_multilayer_state->pic_logger module~rdb_efp->iso_fortran_env ieee_arithmetic ieee_arithmetic module~rdb_efp->ieee_arithmetic module~rdb_error_ring->pic_logger module~rdb_ice_column->module~rdb_constants module~rdb_ice_column->module~rdb_ice_enthalpy module~rdb_ice_mass rdb_ice_mass module~rdb_ice_column->module~rdb_ice_mass module~rdb_ice_optics rdb_ice_optics module~rdb_ice_column->module~rdb_ice_optics module~rdb_ice_enthalpy->module~rdb_constants module~rdb_mem_report->module~rdb_constants module~rdb_mem_report->iso_fortran_env module~rdb_mem_report->pic_logger pic_strings pic_strings module~rdb_mem_report->pic_strings module~rdb_tracer->module~rdb_constants module~rdb_tracer->module~rdb_grid module~rdb_tracer->iso_fortran_env module~rdb_tracer->module~rdb_mem_report module~rdb_ice_mass->module~rdb_constants module~rdb_ice_mass->module~rdb_ice_enthalpy module~rdb_ice_optics->module~rdb_constants module~rdb_ice_optics->module~rdb_ice_enthalpy

Used by

  • module~~rdb_ice_itd~~UsedByGraph module~rdb_ice_itd rdb_ice_itd module~rdb_ice_transport rdb_ice_transport module~rdb_ice_transport->module~rdb_ice_itd module~rdb_ocean_engine rdb_ocean_engine module~rdb_ocean_engine->module~rdb_ice_itd module~rdb_ocean_engine->module~rdb_ice_transport module~rdb_driver rdb_driver module~rdb_driver->module~rdb_ocean_engine module~rdb_handle rdb_handle module~rdb_handle->module~rdb_ocean_engine module~rdb_ocean_api rdb_ocean_api module~rdb_ocean_api->module~rdb_ocean_engine module~rdb_ocean_api->module~rdb_handle

Subroutines

public pure subroutine ice_adjust_categories(grid, ms, ice)

Outer shim (outer-shim + flat-impl pattern). No-op when the ice slot is not live (is_init gate) or when ice%ncat == 1 (the legacy-mode bit-identity contract — see module docstring). No dt/eos needed: this is a pure area/mass reshuffle, no thermodynamics.

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(multilayer_state_t), intent(in) :: ms

READ-ONLY: only wet_mask is read (the gate).

type(ocean_sea_ice_t), intent(inout) :: ice

private pure subroutine ice_adjust_categories_impl(wet_mask, part_size, m_ice, m_snow, enth_ice, enth_snow, sal_ice, mh_lim, nghost, ncat, nk, nx, ny)

Device kernel: ONE do concurrent(j, i) over PHYSICAL cells (ghosts excluded), wet_mask > 0.5 inner gate (never a masked DC header), serial cat loops inside — each cell touches only its own (i, j, :) slice, race-free. No per-cell gather into local category-sized arrays (avoids register pressure and any compile-time ncat cap, unlike the ICE_NK_MAX-capped PER-LAYER arrays elsewhere in the ice model) — scalar temporaries only, all declared in local(...).

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: wet_mask(nx,ny)
real(kind=wp), intent(inout) :: part_size(nx,ny,0:ncat)
real(kind=wp), intent(inout) :: m_ice(nx,ny,ncat)
real(kind=wp), intent(inout) :: m_snow(nx,ny,ncat)
real(kind=wp), intent(inout) :: enth_ice(nx,ny,ncat,nk)
real(kind=wp), intent(inout) :: enth_snow(nx,ny,ncat,1)
real(kind=wp), intent(inout) :: sal_ice(nx,ny,ncat,nk)
real(kind=wp), intent(in) :: mh_lim(ncat+1)
integer, intent(in) :: nghost

Grid + category + layer extents (declared first — decl-order).

integer, intent(in) :: ncat

Grid + category + layer extents (declared first — decl-order).

integer, intent(in) :: nk

Grid + category + layer extents (declared first — decl-order).

integer, intent(in) :: nx

Grid + category + layer extents (declared first — decl-order).

integer, intent(in) :: ny

Grid + category + layer extents (declared first — decl-order).