subroutine register_derived(state, name, time_op, dt_out, coord)
!! Register ONE derived diagnostic by catalog `name` (used by
!! `apply_diag_selection`): look it up and forward to
!! `state%diag%register(...)` with the catalog metadata + correct buffer
!! shape. Error-stops on an unknown name. `coord` (a `DIAG_VGRID_*`)
!! sets the output vgrid for a LAYERED diag + attaches the conservative
!! remap (2D entries ignore it); default LAYER. Remaps INTENSIVE.
type(ocean_state_t), intent(inout), target :: state
character(len=*), intent(in) :: name
integer, intent(in), optional :: time_op
real(wp), intent(in), optional :: dt_out
integer, intent(in), optional :: coord
integer :: i, idx, nx, ny, nz, n3, ocoord
type(derived_entry_t) :: entry
procedure(diag_remap_proc), pointer :: remap
call ensure_catalog_initialised()
idx = 0
do i = 1, N_CATALOG
if (trim(CATALOG(i)%name) == trim(name)) then
idx = i
exit
end if
end do
if (idx == 0) then
call error_ring_push("unknown derived diagnostic '"//trim(name)// &
"'; valid names: "//catalog_name_list())
call global_logger%error("unknown derived diagnostic '"//trim(name)// &
"'; valid names: "//catalog_name_list())
error stop "register_derived: unknown derived diagnostic name"
end if
entry = CATALOG(idx)
! Prerequisite gate. FAIL LOUD, never a silent plane of zeros or
! of missing values: a user who asked for `melt` on a run with no
! melt slot has made a configuration error, and handing them a
! NetCDF variable full of `_FillValue` would let it reach a figure.
select case (entry%requires)
case (DERIVED_REQ_CAVITY_DYN)
if (.not. state%metrics%use_cavity) then
call derived_requires_fail(trim(entry%name), "&ocean_cavity_dyn_nml enable", &
"the ice-shelf draft geometry")
end if
case (DERIVED_REQ_CAVITY_MELT)
if (.not. state%cavity_flux%enable) then
call derived_requires_fail(trim(entry%name), "&ocean_cavity_melt_nml enable", &
"the basal-melt interface slot")
end if
case default
continue
end select
nx = size(state%barotropic%h, 1)
ny = size(state%barotropic%h, 2)
nz = state%multilayer%nz_ml
n3 = nz
if (.not. entry%is_layered) n3 = 1
ocoord = DIAG_VGRID_LAYER
if (present(coord) .and. entry%is_layered) ocoord = coord
call coord_remap_proc(ocoord, remap)
if (associated(remap)) then
call state%diag%register(name=trim(entry%name), units=trim(entry%units), &
fill=entry%fill, n1=nx, n2=ny, n3=n3, &
long_name=trim(entry%long_name), &
standard_name=trim(entry%standard_name), &
time_op=time_op, dt_out=dt_out, &
output_vgrid=ocoord, remap=remap, is_extensive=.false., &
! A `masks_land` entry (`vorticity_z`) writes
! `DIAG_MISSING_VALUE` at every land T-cell on this
! (remapped, layered) path too, so it must advertise
! `_FillValue` here as well as below.
has_missing=((diag_mask_vanished_is_on() .and. &
ocoord /= DIAG_VGRID_DENSITY) .or. &
entry%masks_land))
else
! Every cavity entry writes the NaN sentinel outside the cover
! (`cavity_mask_impl`), so the NetCDF variable must advertise a
! `_FillValue` — unconditionally, not via
! `diag_mask_vanished_is_on()`, which gates the REMAP path's
! below-target fill and has nothing to do with a calving front.
! A `masks_land` entry (`vorticity_z`, `fill_vorticity_z_impl`)
! writes `DIAG_MISSING_VALUE` at every land T-cell regardless of
! `mask_vanished_layers`, so it advertises unconditionally too.
call state%diag%register(name=trim(entry%name), units=trim(entry%units), &
fill=entry%fill, n1=nx, n2=ny, n3=n3, &
long_name=trim(entry%long_name), &
standard_name=trim(entry%standard_name), &
time_op=time_op, dt_out=dt_out, &
has_missing=(entry%requires /= DERIVED_REQ_NONE .or. &
entry%masks_land))
end if
end subroutine register_derived