ocean_top_drag_t Derived Type

type, public :: ocean_top_drag_t

Ice-shelf top-drag slot. Every array is full size when enable, a (1,1) / (1,1,1) placeholder otherwise — the ocean_cavity_flux_t gating convention, latched in ocean_state_init_from_config BEFORE init so the allocation gate can read it.


Inherits

type~~ocean_top_drag_t~~InheritsGraph type~ocean_top_drag_t ocean_top_drag_t type~scratch_3d_buffer_t scratch_3d_buffer_t type~ocean_top_drag_t->type~scratch_3d_buffer_t du_drag, dv_drag

Inherited by

type~~ocean_top_drag_t~~InheritedByGraph type~ocean_top_drag_t ocean_top_drag_t type~ocean_state_t ocean_state_t type~ocean_state_t->type~ocean_top_drag_t tdrag type~ocean_engine_t ocean_engine_t type~ocean_engine_t->type~ocean_state_t state type~ocean_handle_t ocean_handle_t type~ocean_handle_t->type~ocean_state_t state type~ocean_handle_t->type~ocean_engine_t engine

Components

Type Visibility Attributes Name Initial
real(kind=wp), public :: c_drag = 0.0_wp

Quadratic drag coefficient (dimensionless). ISOMIP+ 2.5e-3. Zero disables the quadratic branch.

real(kind=wp), public, allocatable :: cover_t(:,:)

CELL-CENTRED ice-cover mask, shape (nx, ny) — a configure-time copy of metrics%cover_frac. Held on the slot (rather than reaching into metrics per step) so the kernel signature carries exactly the fields it reads. Used only by the stress_top diagnostic, which is cell-centred.

real(kind=wp), public, allocatable :: cover_u(:,:)

Face ice-cover mask at east faces, shape (nx+1, ny): the OR of the two abutting cells’ cover_frac (see the module docstring). STATIC — filled once at configure.

real(kind=wp), public, allocatable :: cover_v(:,:)

Face ice-cover mask at north faces, shape (nx, ny+1).

real(kind=wp), public :: drag_bg_vel = 0.0_wp

Background velocity floor (m/s) in the quadratic speed, |U_eff| = max(drag_bg_vel, |U_tbl|) (the mirror of MOM6 DRAG_BG_VEL). UNLIKE the bottom-drag slot, this is honoured in BOTH the layer-only and distributed modes — the two kernels are one code path here. Zero (default) ⇒ no floor ⇒ the layer-only quadratic branch is the exact algebraic mirror of ocean_bottom_drag_compute_tendencies.

type(scratch_3d_buffer_t), public :: du_drag

Top-drag tendency at east faces, shape (nx+1, ny, nz). Only layers inside the top boundary layer carry a non-zero value.

type(scratch_3d_buffer_t), public :: dv_drag

Top-drag tendency at north faces, shape (nx, ny+1, nz).

logical, public :: enable = .false.

&ocean_tdrag_nml enable, latched before init. Off ⇒ placeholders, no kernel launch, byte-identical.

real(kind=wp), public :: h_min = 1.0e-3_wp

Floor on the top-layer face thickness inside the u/h_top division — keeps the kernel finite when the top layer pinches out. Matches the bottom-drag h_min.

real(kind=wp), public :: htbl = 0.0_wp

Top-boundary-layer thickness (m) the stress is distributed over (the mirror of hbbl). Zero (default) = layer-nz-only mode.

logical, public :: implicit = .false.

Backward-Euler top drag in this kernel: the tendency is formed as -lambda*u/(1 + dt*lambda) so the standalone apply gives u^{n+1} = u/(1 + dt*lambda). Unconditionally stable for any layer thickness. Default .false. = explicit forward Euler.

logical, public :: implicit_fold = .false.

&ocean_vdiff_nml implicit_top_drag: fill lambda_top_u/v so the vdiff solver can fold the drag into its k = nz diagonal, and let the driver SKIP the explicit apply. Default .false. ⇒ the rate fields stay zero.

logical, public :: is_init = .false.

True between init and destroy. Always test this, never allocated(...).

real(kind=wp), public, allocatable :: lambda_top_u(:,:)

Top-layer (k=nz) Rayleigh RATE lambda (1/s) at east faces, shape (nx+1, ny): C_d*|U|/h_nz (quadratic, |U| frozen at u^n) or r (linear), already cover- and wet-masked. Consumed by vdiff_apply_momentum as the +dt*lambda add on the k = nz diagonal. Zero unless implicit_fold.

real(kind=wp), public, allocatable :: lambda_top_v(:,:)

Top-layer Rayleigh rate at north faces, shape (nx, ny+1).

real(kind=wp), public :: r_linear = 0.0_wp

Linear Rayleigh coefficient (1/s). Zero disables the linear branch even when the variant tag selects it.

real(kind=wp), public :: rho0 = 0.0_wp

Boussinesq reference density (kg/m^3) — eos%rho0 via configure_ocean_reference_density. Used ONLY to turn the kinematic drag into the stress_top diagnostic; no dynamics reads it.

real(kind=wp), public, allocatable :: stress_top(:,:)

Cell-centred magnitude of the top stress (N/m^2), rho_0 * |a_drag| * h_tbl — see the module docstring. Write only here; the KPP/EPBL ustar_shelf consumer is a later PR.

real(kind=wp), public :: tbl_thick_min = 0.0_wp

Minimum effective TBL thickness (m) in the stress / h_tbl denominator (mirror of BBL_THICK_MIN). Zero (default) falls back to h_min.

integer, public :: variant = TDRAG_QUADRATIC

Active drag variant (TDRAG_*).


Type-Bound Procedures

procedure, public, non_overridable :: bytes => ocean_top_drag_bytes

  • private pure function ocean_top_drag_bytes(this) result(nbytes)

    Counted allocatable footprint of the top-drag slot (0 when unallocated). One arr_bytes term per array — add a term here when a new allocatable joins the type.

    Arguments

    Type IntentOptional Attributes Name
    class(ocean_top_drag_t), intent(in) :: this

    Return Value integer(kind=int64)

procedure, public, non_overridable :: destroy => ocean_top_drag_destroy

procedure, public, non_overridable :: enter_data => ocean_top_drag_enter_data

  • private subroutine ocean_top_drag_enter_data(this)

    Type-bound wrapper — delegates to the non-polymorphic impl so the device-attach map base is the heap object, not a polymorphic box.

    Arguments

    Type IntentOptional Attributes Name
    class(ocean_top_drag_t), intent(inout) :: this

procedure, public, non_overridable :: exit_data => ocean_top_drag_exit_data

procedure, public, non_overridable :: init => ocean_top_drag_init

  • private subroutine ocean_top_drag_init(this, grid, nz_ml)

    Allocate the slot. Gated on enable (latched before this runs), so a run without an ice shelf pays five (1,1) placeholders and two (1,1,1) scratch buffers.

    Arguments

    Type IntentOptional Attributes Name
    class(ocean_top_drag_t), intent(inout) :: this
    type(hgrid_t), intent(in) :: grid
    integer, intent(in), optional :: nz_ml

Source Code

   type :: ocean_top_drag_t
      !! Ice-shelf top-drag slot.  Every array is full size when
      !! `enable`, a `(1,1)` / `(1,1,1)` placeholder otherwise — the
      !! `ocean_cavity_flux_t` gating convention, latched in
      !! `ocean_state_init_from_config` BEFORE `init` so the allocation
      !! gate can read it.
      logical :: is_init = .false.
         !! True between `init` and `destroy`.  Always test this, never
         !! `allocated(...)`.
      logical :: enable = .false.
         !! `&ocean_tdrag_nml enable`, latched before `init`.  Off ⇒
         !! placeholders, no kernel launch, byte-identical.
      integer :: variant = TDRAG_QUADRATIC
         !! Active drag variant (`TDRAG_*`).
      real(wp) :: r_linear = 0.0_wp
         !! Linear Rayleigh coefficient (1/s).  Zero disables the linear
         !! branch even when the variant tag selects it.
      real(wp) :: c_drag = 0.0_wp
         !! Quadratic drag coefficient (dimensionless).  ISOMIP+ 2.5e-3.
         !! Zero disables the quadratic branch.
      real(wp) :: h_min = 1.0e-3_wp
         !! Floor on the top-layer face thickness inside the `u/h_top`
         !! division — keeps the kernel finite when the top layer pinches
         !! out.  Matches the bottom-drag `h_min`.
      real(wp) :: htbl = 0.0_wp
         !! Top-boundary-layer thickness (m) the stress is distributed
         !! over (the mirror of `hbbl`).  Zero (default) = layer-`nz`-only
         !! mode.
      real(wp) :: drag_bg_vel = 0.0_wp
         !! Background velocity floor (m/s) in the quadratic speed,
         !! `|U_eff| = max(drag_bg_vel, |U_tbl|)` (the mirror of MOM6
         !! DRAG_BG_VEL).  UNLIKE the bottom-drag slot, this is honoured
         !! in BOTH the layer-only and distributed modes — the two kernels
         !! are one code path here.  Zero (default) ⇒ no floor ⇒ the
         !! layer-only quadratic branch is the exact algebraic mirror of
         !! `ocean_bottom_drag_compute_tendencies`.
      real(wp) :: tbl_thick_min = 0.0_wp
         !! Minimum effective TBL thickness (m) in the `stress / h_tbl`
         !! denominator (mirror of BBL_THICK_MIN).  Zero (default) falls
         !! back to `h_min`.
      logical :: implicit = .false.
         !! Backward-Euler top drag in this kernel: the tendency is formed
         !! as `-lambda*u/(1 + dt*lambda)` so the standalone apply gives
         !! `u^{n+1} = u/(1 + dt*lambda)`.  Unconditionally stable for any
         !! layer thickness.  Default `.false.` = explicit forward Euler.
      logical :: implicit_fold = .false.
         !! `&ocean_vdiff_nml implicit_top_drag`: fill `lambda_top_u/v`
         !! so the vdiff solver can fold the drag into its `k = nz`
         !! diagonal, and let the driver SKIP the explicit apply.  Default
         !! `.false.` ⇒ the rate fields stay zero.
      real(wp) :: rho0 = 0.0_wp
         !! Boussinesq reference density (kg/m^3) — `eos%rho0` via
         !! `configure_ocean_reference_density`.  Used ONLY to turn the
         !! kinematic drag into the `stress_top` diagnostic; no dynamics
         !! reads it.

      type(scratch_3d_buffer_t) :: du_drag
         !! Top-drag tendency at east faces, shape (nx+1, ny, nz).  Only
         !! layers inside the top boundary layer carry a non-zero value.
      type(scratch_3d_buffer_t) :: dv_drag
         !! Top-drag tendency at north faces, shape (nx, ny+1, nz).

      real(wp), allocatable :: cover_u(:, :)
         !! Face ice-cover mask at east faces, shape (nx+1, ny): the OR of
         !! the two abutting cells' `cover_frac` (see the module
         !! docstring).  STATIC — filled once at configure.
      real(wp), allocatable :: cover_v(:, :)
         !! Face ice-cover mask at north faces, shape (nx, ny+1).
      real(wp), allocatable :: cover_t(:, :)
         !! CELL-CENTRED ice-cover mask, shape (nx, ny) — a configure-time
         !! copy of `metrics%cover_frac`.  Held on the slot (rather than
         !! reaching into `metrics` per step) so the kernel signature
         !! carries exactly the fields it reads.  Used only by the
         !! `stress_top` diagnostic, which is cell-centred.
      real(wp), allocatable :: lambda_top_u(:, :)
         !! Top-layer (k=nz) Rayleigh RATE lambda (1/s) at east faces,
         !! shape (nx+1, ny): `C_d*|U|/h_nz` (quadratic, `|U|` frozen at
         !! u^n) or `r` (linear), already cover- and wet-masked.  Consumed
         !! by `vdiff_apply_momentum` as the `+dt*lambda` add on the
         !! `k = nz` diagonal.  Zero unless `implicit_fold`.
      real(wp), allocatable :: lambda_top_v(:, :)
         !! Top-layer Rayleigh rate at north faces, shape (nx, ny+1).
      real(wp), allocatable :: stress_top(:, :)
         !! Cell-centred magnitude of the top stress (N/m^2),
         !! `rho_0 * |a_drag| * h_tbl` — see the module docstring.  Write
         !! only here; the KPP/EPBL `ustar_shelf` consumer is a later PR.
   contains
      procedure, non_overridable :: init => ocean_top_drag_init
      procedure, non_overridable :: destroy => ocean_top_drag_destroy
      procedure, non_overridable :: enter_data => ocean_top_drag_enter_data
      procedure, non_overridable :: exit_data => ocean_top_drag_exit_data
      procedure, non_overridable :: bytes => ocean_top_drag_bytes
   end type ocean_top_drag_t