ocean_vcoord_zstar_target Subroutine

public pure subroutine ocean_vcoord_zstar_target(target_h, total_h, eta, nx, ny, nz, h_nominal, use_profile, zi, h_min)

VCOORD_ZSTAR target grid — MOM6 z* (REGRIDDING_COORDINATE_MODE = "Z*", build_zstar_column, MOM6 src/ALE/coord_zlike.F90 lines 65-146): the FIXED nominal z profile of z_fixed (&vcoord_nml z_fixed_profile uniform / list / tanh — the same z_fixed_zi table, not a parallel one) DILATED per column by the free-surface stretching, over a partial bed cell and inert bed fillers.

MOM6

Without a rigid top MOM6 sets stretching = (H+η)/H (coord_zlike.F90:109), lays the interfaces top-down from the free surface at z_k = η − stretching·Z_k (Z_k the nominal depth, l.130-134), pins the bottom interface at −H and clamps upward so no layer is thinner than min_thickness (l.138-143). The height of nominal interface k above the bed is therefore stretching·(H − Z_k): a layer lies below the bed iff its nominal top Z_k ≥ H, WHATEVER η is (stretching > 0). The live/filler pattern is exactly static in η, of either sign.

This kernel

Two passes per column, both the z_fixed bed walk (ocean_vcoord_z_fixed_target with z_top = 0):

  1. at η = 0, count the bed fillers n_f — the layers whose nominal top is at or below the bed less the partial-cell floor (Z_FIXED_BED_PARTIAL_MIN), exactly z_fixed’s rule;
  2. lay the column again from H + η: layers k <= n_f at h_min, every live interface at the DILATED nominal depth s·Z_k below the free surface, with s = (H + η − n_f·h_min)/(H − n_f·h_min).

s is MOM6’s stretching with the filler stack taken out of the dilation (MOM6 dilates all of H and then clamps the fillers back to min_thickness, which leaves (s−1)·n_f·h_min in the partial cell — under 1 mm per metre of η on the 1-degree Southern Ocean, python_prototypes/mom6_zstar/mom6_zstar.py). Consequences:

  • the liveness of every layer is decided at η = 0, so the pattern is static BY CONSTRUCTION and the static face mask (zfixed_closed_faces) applies with every consumer unchanged;
  • every live layer, the partial bed cell included, is its η = 0 thickness times s (to round-off) — the dilation keeps ratios, so a partial cell (p0 > Z_FIXED_BED_PARTIAL_MIN at η = 0 by construction) stays above H_VANISHED for every s > H_VANISHED/p0, i.e. for every η that does not all but dry the column;
  • at η = 0, s == 1 exactly (numerator and denominator are the same expression), s·Z_k == Z_k, and the walk is z_fixed’s operation for operation: the η = 0 z* target IS the η = 0 z_fixed target, bit for bit.

Σ_k target_h = H + η by construction (each step assigns what it takes from z_below, and the surface layer closes the column). A degenerate column (H − n_f·h_min <= h_min: land, or thinner than its filler stack) is laid with s = 1, z_fixed’s own degenerate overshoot. s is floored at 0: a column drained past its fillers is a dry column, which this coordinate does not support (validate_config refuses it with wet/dry).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(out) :: target_h(nx,ny,nz)

Target layer thickness (m), bottom-up.

real(kind=wp), intent(in) :: total_h(nx,ny)

Column reference thickness H (m) — Σ h_layer − η.

real(kind=wp), intent(in) :: eta(nx,ny)

Free-surface anomaly η (m).

integer, intent(in) :: nx

i-extent of every array (total, incl. halos).

integer, intent(in) :: ny

j-extent of every array (total, incl. halos).

integer, intent(in) :: nz

Number of layers; k = 1 is the bed, k = nz the surface.

real(kind=wp), intent(in) :: h_nominal

Uniform nominal spacing z_fixed_h_ref/nz (m). Unused when use_profile.

logical, intent(in) :: use_profile

Take the nominal interfaces from zi.

real(kind=wp), intent(in) :: zi(0:nz)

Nominal interface depths (m), bottom-up, zi(k) = top of layer k, zi(nz) = 0. Read only when use_profile.

real(kind=wp), intent(in) :: h_min

Inert-filler thickness (zstar_h_min, <= H_VANISHED).


Calls

proc~~ocean_vcoord_zstar_target~~CallsGraph proc~ocean_vcoord_zstar_target ocean_vcoord_zstar_target local local proc~ocean_vcoord_zstar_target->local

Called by

proc~~ocean_vcoord_zstar_target~~CalledByGraph proc~ocean_vcoord_zstar_target ocean_vcoord_zstar_target proc~ocean_vcoord_compute_target_h_impl ocean_vcoord_compute_target_h_impl proc~ocean_vcoord_compute_target_h_impl->proc~ocean_vcoord_zstar_target proc~ocean_vcoord_eta0_target ocean_vcoord_eta0_target proc~ocean_vcoord_eta0_target->proc~ocean_vcoord_zstar_target proc~configure_ocean_closed_faces configure_ocean_closed_faces proc~configure_ocean_closed_faces->proc~ocean_vcoord_eta0_target proc~ocean_state_seed_from_cfg ocean_state_seed_from_cfg proc~ocean_state_seed_from_cfg->proc~ocean_vcoord_eta0_target proc~ocean_vcoord_compute_target_h ocean_vcoord_t%ocean_vcoord_compute_target_h proc~ocean_vcoord_compute_target_h->proc~ocean_vcoord_compute_target_h_impl proc~engine_setup engine_setup proc~engine_setup->proc~configure_ocean_closed_faces proc~engine_setup->proc~ocean_state_seed_from_cfg proc~ocean_apply_ale_remap_centres ocean_apply_ale_remap_centres proc~ocean_apply_ale_remap_centres->proc~ocean_vcoord_compute_target_h proc~ocean_apply_ale_remap_step ocean_apply_ale_remap_step proc~ocean_apply_ale_remap_step->proc~ocean_vcoord_compute_target_h proc~complete_ocean_create complete_ocean_create proc~complete_ocean_create->proc~engine_setup proc~driver_run_ocean driver_run_ocean proc~driver_run_ocean->proc~engine_setup proc~driver_validate driver_validate proc~driver_validate->proc~engine_setup proc~ocean_dyn_step_split ocean_dyn_step_split proc~ocean_dyn_step_split->proc~ocean_apply_ale_remap_step proc~driver_run driver_run proc~driver_run->proc~driver_run_ocean proc~engine_step engine_step proc~engine_step->proc~ocean_dyn_step_split proc~rdb_ocean_create_finalize rdb_ocean_create_finalize proc~rdb_ocean_create_finalize->proc~complete_ocean_create proc~rdb_ocean_create_from_string rdb_ocean_create_from_string proc~rdb_ocean_create_from_string->proc~complete_ocean_create

Variables

Type Visibility Attributes Name Initial
real(kind=wp), private :: bed_partial_min
integer, private :: i
integer, private :: j
integer, private :: k
real(kind=wp), private :: l0_loc
integer, private :: n_f
real(kind=wp), private :: stretch_loc
logical, private :: walking_loc
real(kind=wp), private :: z_above_loc
real(kind=wp), private :: z_below_loc
real(kind=wp), private :: z_nom_loc

Source Code

   pure subroutine ocean_vcoord_zstar_target(target_h, total_h, eta, nx, ny, nz, &
                                             h_nominal, use_profile, zi, h_min)
      !! `VCOORD_ZSTAR` target grid — MOM6 z* (`REGRIDDING_COORDINATE_MODE
      !! = "Z*"`, `build_zstar_column`, MOM6 `src/ALE/coord_zlike.F90`
      !! lines 65-146): the FIXED nominal z profile of `z_fixed`
      !! (`&vcoord_nml z_fixed_profile` uniform / list / tanh — the same
      !! `z_fixed_zi` table, not a parallel one) DILATED per column by the
      !! free-surface stretching, over a partial bed cell and inert bed
      !! fillers.
      !!
      !! ### MOM6
      !!
      !! Without a rigid top MOM6 sets `stretching = (H+η)/H`
      !! (coord_zlike.F90:109), lays the interfaces top-down from the free
      !! surface at `z_k = η − stretching·Z_k` (`Z_k` the nominal depth,
      !! l.130-134), pins the bottom interface at `−H` and clamps upward so
      !! no layer is thinner than `min_thickness` (l.138-143).  The height
      !! of nominal interface `k` above the bed is therefore
      !! `stretching·(H − Z_k)`: a layer lies below the bed iff its nominal
      !! top `Z_k ≥ H`, WHATEVER `η` is (`stretching > 0`).  The live/filler
      !! pattern is exactly static in `η`, of either sign.
      !!
      !! ### This kernel
      !!
      !! Two passes per column, both the `z_fixed` bed walk
      !! (`ocean_vcoord_z_fixed_target` with `z_top = 0`):
      !!
      !!   1. at `η = 0`, count the bed fillers `n_f` — the layers whose
      !!      nominal top is at or below the bed less the partial-cell
      !!      floor (`Z_FIXED_BED_PARTIAL_MIN`), exactly `z_fixed`'s rule;
      !!   2. lay the column again from `H + η`: layers `k <= n_f` at
      !!      `h_min`, every live interface at the DILATED nominal depth
      !!      `s·Z_k` below the free surface, with
      !!      `s = (H + η − n_f·h_min)/(H − n_f·h_min)`.
      !!
      !! `s` is MOM6's `stretching` with the filler stack taken out of the
      !! dilation (MOM6 dilates all of `H` and then clamps the fillers back
      !! to `min_thickness`, which leaves `(s−1)·n_f·h_min` in the partial
      !! cell — under 1 mm per metre of `η` on the 1-degree Southern Ocean,
      !! `python_prototypes/mom6_zstar/mom6_zstar.py`).  Consequences:
      !!
      !!   * the liveness of every layer is decided at `η = 0`, so the
      !!     pattern is static BY CONSTRUCTION and the static face mask
      !!     (`zfixed_closed_faces`) applies with every consumer unchanged;
      !!   * every live layer, the partial bed cell included, is its
      !!     `η = 0` thickness times `s` (to round-off) — the dilation keeps
      !!     ratios, so a partial cell (`p0 > Z_FIXED_BED_PARTIAL_MIN` at
      !!     `η = 0` by construction) stays above `H_VANISHED` for every
      !!     `s > H_VANISHED/p0`, i.e. for every `η` that does not all but
      !!     dry the column;
      !!   * at `η = 0`, `s == 1` exactly (numerator and denominator are the
      !!     same expression), `s·Z_k == Z_k`, and the walk is `z_fixed`'s
      !!     operation for operation: the `η = 0` z* target IS the `η = 0`
      !!     `z_fixed` target, bit for bit.
      !!
      !! `Σ_k target_h = H + η` by construction (each step assigns what it
      !! takes from `z_below`, and the surface layer closes the column).
      !! A degenerate column (`H − n_f·h_min <= h_min`: land, or thinner
      !! than its filler stack) is laid with `s = 1`, `z_fixed`'s own
      !! degenerate overshoot.  `s` is floored at 0: a column drained past
      !! its fillers is a dry column, which this coordinate does not
      !! support (`validate_config` refuses it with wet/dry).
      integer, intent(in) :: nx
         !! i-extent of every array (total, incl. halos).
      integer, intent(in) :: ny
         !! j-extent of every array (total, incl. halos).
      integer, intent(in) :: nz
         !! Number of layers; `k = 1` is the bed, `k = nz` the surface.
      real(wp), intent(out) :: target_h(nx, ny, nz)
         !! Target layer thickness (m), bottom-up.
      real(wp), intent(in) :: total_h(nx, ny)
         !! Column reference thickness `H` (m) — `Σ h_layer − η`.
      real(wp), intent(in) :: eta(nx, ny)
         !! Free-surface anomaly `η` (m).
      real(wp), intent(in) :: h_nominal
         !! Uniform nominal spacing `z_fixed_h_ref/nz` (m).  Unused when
         !! `use_profile`.
      logical, intent(in) :: use_profile
         !! Take the nominal interfaces from `zi`.
      real(wp), intent(in) :: zi(0:nz)
         !! Nominal interface depths (m), bottom-up, `zi(k)` = top of layer
         !! `k`, `zi(nz) = 0`.  Read only when `use_profile`.
      real(wp), intent(in) :: h_min
         !! Inert-filler thickness (`zstar_h_min`, `<= H_VANISHED`).
      integer :: i, j, k, n_f
      real(wp) :: z_below_loc, z_nom_loc, z_above_loc, stretch_loc, l0_loc
      real(wp) :: bed_partial_min
      logical :: walking_loc

      bed_partial_min = max(h_min, Z_FIXED_BED_PARTIAL_MIN)
      do concurrent(j=1:ny, i=1:nx) &
         local(k, n_f, z_below_loc, z_nom_loc, z_above_loc, stretch_loc, l0_loc, &
               walking_loc)
         ! Pass 1 — the eta = 0 bed fillers (z_fixed's bed rule, z_top = 0).
         n_f = 0
         z_below_loc = total_h(i, j)
         walking_loc = .true.
         do k = 1, nz - 1
            if (walking_loc) then
               if (use_profile) then
                  z_nom_loc = zi(k)
               else
                  z_nom_loc = real(nz - k, wp)*h_nominal
               end if
               if (z_nom_loc >= z_below_loc - bed_partial_min) then
                  n_f = n_f + 1
                  z_below_loc = z_below_loc - h_min
               else
                  walking_loc = .false.
               end if
            end if
         end do
         ! The dilation of the live column.
         l0_loc = total_h(i, j) - real(n_f, wp)*h_min
         if (l0_loc > h_min) then
            stretch_loc = max(((total_h(i, j) + eta(i, j)) - real(n_f, wp)*h_min)/l0_loc, &
                              0.0_wp)
         else
            stretch_loc = 1.0_wp
         end if
         ! Pass 2 — the z_fixed walk on the dilated nominal interfaces.
         z_below_loc = total_h(i, j) + eta(i, j)
         do k = 1, nz
            if (k <= n_f) then
               target_h(i, j, k) = h_min
               z_below_loc = z_below_loc - h_min
            else if (k == nz) then
               ! The surface layer closes the column (z_fixed's top rule).
               if (0.0_wp > z_below_loc - h_min) then
                  target_h(i, j, k) = h_min
               else
                  target_h(i, j, k) = z_below_loc
               end if
            else
               if (use_profile) then
                  z_nom_loc = zi(k)
               else
                  z_nom_loc = real(nz - k, wp)*h_nominal
               end if
               z_above_loc = stretch_loc*z_nom_loc
               target_h(i, j, k) = z_below_loc - z_above_loc
               z_below_loc = z_above_loc
            end if
         end do
      end do
   end subroutine ocean_vcoord_zstar_target