meke_step Subroutine

public subroutine meke_step(grid, metrics, this, gm, varmix, wavespeed, ms, dt, ke_diss_ext)

Advance the MEKE field one thermo step (Strang split), update the derived diffusivity kh_diff, and feed the geometric-mean kh into VarMix’s per-face KhTh/KhTr (the GM↔MEKE feedback). Run once per outer step at thermo cadence, after varmix_compute and before gm_compute_transports (MEKE reads gm%gm_src from the previous thermo step — a one-step lag). No-op when enable=.false., uninitialised, or the GM slot is absent.

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_metrics_t), intent(in) :: metrics
type(ocean_meke_t), intent(inout) :: this
type(ocean_gm_t), intent(in) :: gm
type(ocean_varmix_t), intent(inout), optional :: varmix
type(ocean_wave_speed_t), intent(in), optional :: wavespeed
type(multilayer_state_t), intent(in) :: ms
real(kind=wp), intent(in) :: dt
real(kind=wp), intent(in), optional :: ke_diss_ext(:,:)

hvisc KE-dissipation rate (nx,ny) for the frictional source; absent ⇒ the source is inert (staged to 0).


Calls

proc~~meke_step~~CallsGraph proc~meke_step meke_step proc~meke_advect meke_advect proc~meke_step->proc~meke_advect proc~meke_baro_transport meke_baro_transport proc~meke_step->proc~meke_baro_transport proc~meke_bbl_speed2 meke_bbl_speed2 proc~meke_step->proc~meke_bbl_speed2 proc~meke_drag meke_drag proc~meke_step->proc~meke_drag proc~meke_feed_khth meke_feed_khth proc~meke_step->proc~meke_feed_khth proc~meke_kh_closure meke_kh_closure proc~meke_step->proc~meke_kh_closure proc~meke_ku_closure meke_ku_closure proc~meke_step->proc~meke_ku_closure proc~meke_lateral meke_lateral proc~meke_step->proc~meke_lateral proc~meke_length_scales meke_length_scales proc~meke_step->proc~meke_length_scales proc~meke_mass meke_mass proc~meke_step->proc~meke_mass proc~meke_source meke_source proc~meke_step->proc~meke_source proc~meke_stage_rd meke_stage_rd proc~meke_step->proc~meke_stage_rd proc~meke_stage_sn meke_stage_sn proc~meke_step->proc~meke_stage_sn proc~meke_zero_2d meke_zero_2d proc~meke_step->proc~meke_zero_2d local local proc~meke_advect->local proc~meke_baro_transport->local proc~meke_bbl_speed2->local proc~meke_drag->local proc~meke_feed_khth->local proc~meke_kh_closure->local proc~meke_ku_closure->local proc~meke_lateral->local proc~meke_length_scales->local proc~meke_inv_lmix meke_inv_lmix proc~meke_length_scales->proc~meke_inv_lmix proc~meke_mass->local proc~meke_source->local

Called by

proc~~meke_step~~CalledByGraph proc~meke_step meke_step proc~run_meke_step run_meke_step proc~run_meke_step->proc~meke_step proc~ocean_dyn_step_split ocean_dyn_step_split proc~ocean_dyn_step_split->proc~run_meke_step proc~engine_step engine_step proc~engine_step->proc~ocean_dyn_step_split proc~driver_run_ocean driver_run_ocean proc~driver_run_ocean->proc~engine_step proc~rdb_ocean_step rdb_ocean_step proc~rdb_ocean_step->proc~engine_step proc~driver_run driver_run proc~driver_run->proc~driver_run_ocean

Variables

Type Visibility Attributes Name Initial
real(kind=wp), private :: damp_step
logical, private :: have_rho
logical, private :: have_vm
logical, private :: have_ws
logical, private :: kh_flux_enabled
integer, private :: nx
integer, private :: ny
integer, private :: nz
real(kind=wp), private :: sdt
real(kind=wp), private :: sdt_damp

Source Code

   subroutine meke_step(grid, metrics, this, gm, varmix, wavespeed, ms, dt, ke_diss_ext)
      !! Advance the MEKE field one thermo step (Strang split), update the
      !! derived diffusivity `kh_diff`, and feed the geometric-mean kh into
      !! VarMix's per-face KhTh/KhTr (the GM↔MEKE feedback).  Run once per
      !! outer step at thermo cadence, after `varmix_compute` and before
      !! `gm_compute_transports` (MEKE reads `gm%gm_src` from the previous
      !! thermo step — a one-step lag).  No-op when `enable=.false.`,
      !! uninitialised, or the GM slot is absent.
      type(hgrid_t), intent(in) :: grid
      type(ocean_metrics_t), intent(in) :: metrics
      type(ocean_meke_t), intent(inout) :: this
      type(ocean_gm_t), intent(in) :: gm
      type(ocean_varmix_t), intent(inout), optional :: varmix
      type(ocean_wave_speed_t), intent(in), optional :: wavespeed
      type(multilayer_state_t), intent(in) :: ms
      real(wp), intent(in) :: dt
      real(wp), intent(in), optional :: ke_diss_ext(:, :)
         !! hvisc KE-dissipation rate `(nx,ny)` for the frictional source;
         !! absent ⇒ the source is inert (staged to 0).

      integer :: nx, ny, nz
      real(wp) :: sdt, sdt_damp, damp_step
      logical :: have_rho, kh_flux_enabled, have_ws, have_vm

      if (.not. this%is_init) return
      if (.not. this%enable) return
      if (.not. gm%is_init) return
      if (.not. allocated(ms%h_layer)) return
      if (.not. allocated(gm%gm_src)) return

      nx = grid%nx_total
      ny = grid%ny_total
      nz = ms%nz_ml
      if (this%nz_ml /= nz) return

      sdt = dt*this%dtscale
      damp_step = 1.0_wp
      if (this%kh >= 0.0_wp .or. this%k4 >= 0.0_wp) damp_step = 0.5_wp
      sdt_damp = sdt*damp_step
      kh_flux_enabled = (this%kh >= 0.0_wp)
      have_rho = allocated(ms%rho_layer)
      have_ws = .false.
      if (present(wavespeed)) have_ws = wavespeed%is_init .and. allocated(wavespeed%rd_over_dx)
      have_vm = .false.
      if (present(varmix)) have_vm = varmix%is_init .and. allocated(varmix%sn_u)

      ! ---- 0. stage the optional inputs into device-resident workspaces.
      ! Copy ON-DEVICE from the slot arrays (which are device-resident) so the
      ! length-scale kernel reads explicit-shape slot args, never an
      ! optional-or-host-staged actual.  When the source slot is absent the
      ! workspace is zeroed (Ldeform->0 / Eady inert).  `f_centre` is the
      ! slot's own |f| (filled at setup via set_f_centre); when never filled
      ! it is zero ⇒ beta=0 ⇒ Rhines inert (alpha_rhines default 0).
      if (have_ws) then
         call meke_stage_rd(nx, ny, wavespeed%rd_over_dx, this%rd_ws)
      else
         call meke_zero_2d(nx, ny, this%rd_ws)
      end if
      if (have_vm) then
         call meke_stage_sn(nx, ny, varmix%sn_u, varmix%sn_v, &
                            this%sn_u_ws, this%sn_v_ws)
      else
         call meke_zero_2d(nx + 1, ny, this%sn_u_ws)
         call meke_zero_2d(nx, ny + 1, this%sn_v_ws)
      end if

      ! ---- 1. column mass + depth (I_mass, depth_tot, mass_ws). ----
      ! Pass rho_layer only when allocated; otherwise feed h_layer as a
      ! harmless placeholder (gated off by have_rho ⇒ mass uses gm%rho0).
      if (have_rho) then
         call meke_mass(nx, ny, nz, gm%rho0, .true., ms%h_layer, ms%rho_layer, &
                        this%i_mass, this%depth_tot, this%mass_ws)
      else
         call meke_mass(nx, ny, nz, gm%rho0, .false., ms%h_layer, ms%h_layer, &
                        this%i_mass, this%depth_tot, this%mass_ws)
      end if

      ! ---- 2. structure factors (bottomFac2, barotrFac2) + Lmix into le. ----
      call meke_length_scales(nx, ny, this%cd_scale, this%cb, this%ct, &
                              this%min_gamma2, this%cdrag, &
                              this%alpha_deform, this%alpha_rhines, &
                              this%alpha_eady, this%alpha_frict, this%alpha_grid, &
                              metrics%areaT, metrics%idxT, metrics%idyT, &
                              this%f_centre, &
                              this%depth_tot, this%meke, &
                              this%rd_ws, this%sn_u_ws, this%sn_v_ws, &
                              this%bottom_fac2, this%barotr_fac2, this%le)

      ! ---- 3. explicit source bump: E += sdt*src. ----
      ! Stage the hvisc KE-dissipation rate for the frictional source
      ! (-frcoeff·i_mass·ke_diss); 0 when the seam is not wired ⇒ inert.
      if (present(ke_diss_ext)) then
         call meke_stage_rd(nx, ny, ke_diss_ext, this%ke_diss_ws)
      else
         call meke_zero_2d(nx, ny, this%ke_diss_ws)
      end if
      call meke_source(nx, ny, this%bgsrc, this%gmcoeff, this%frcoeff, sdt, &
                       this%i_mass, gm%gm_src, this%ke_diss_ws, this%src, this%meke)

      ! ---- 4. implicit drag half: E <- E/(1+sdt_damp*damp_rate). ----
      ! Resolved bed-layer eddy velocity (MOM6 drag_rate_visc); 0 when off.
      if (this%use_bbl_drag) then
         call meke_bbl_speed2(nx, ny, nz, ms%u_face_x_layer, ms%v_face_y_layer, &
                              ms%k_bot_u, ms%k_bot_v, this%u_bbl2)
      end if
      call meke_drag(nx, ny, sdt_damp, this%damping, this%cdrag, this%uscale, gm%rho0, &
                     this%i_mass, this%bottom_fac2, this%u_bbl2, this%meke)

      ! ---- 5. lateral diffusion (+ biharmonic). ----
      if (kh_flux_enabled .or. this%k4 >= 0.0_wp) then
         call meke_lateral(nx, ny, sdt, this%kh, this%k4, this%khmeke_fac, &
                           kh_flux_enabled, &
                           metrics%dy_cu, metrics%dx_cv, metrics%idxCu, &
                           metrics%idyCv, metrics%iareaT, &
                           this%i_mass, this%mass_ws, &
                           this%kh_diff, this%uflux, this%vflux, this%del2, &
                           this%meke)
      end if

      ! ---- 5b. upwind advection of E by the barotropic transport. ----
      ! Self-contained transport stage: baroHu = Sum_k (mass-weighted face
      ! transport), upwind flux E*baroHu, single conservative divergence.
      ! `advection_factor = 0` (default) ⇒ no-op ⇒ bit-identical.
      if (this%advection_factor > 0.0_wp) then
         if (have_rho) then
            call meke_baro_transport(nx, ny, nz, gm%rho0, .true., &
                                     ms%mass_flux_x_layer, ms%mass_flux_y_layer, &
                                     ms%rho_layer, this%baro_hu, this%baro_hv)
         else
            call meke_baro_transport(nx, ny, nz, gm%rho0, .false., &
                                     ms%mass_flux_x_layer, ms%mass_flux_y_layer, &
                                     ms%rho_layer, this%baro_hu, this%baro_hv)
         end if
         call meke_advect(nx, ny, sdt, this%advection_factor, metrics%iareaT, &
                          this%i_mass, this%baro_hu, this%baro_hv, &
                          this%uflux, this%vflux, this%meke)
      end if

      ! ---- 6. implicit drag half (only when Strang-split, damp_step=0.5). ----
      ! `u_bbl2` already filled in step 4 (or held at 0 when off).
      if (this%kh >= 0.0_wp .or. this%k4 >= 0.0_wp) then
         call meke_drag(nx, ny, sdt_damp, this%damping, this%cdrag, this%uscale, gm%rho0, &
                        this%i_mass, this%bottom_fac2, this%u_bbl2, this%meke)
      end if

      ! ---- 7. MEKE -> KhTh closure: kh = khcoeff*sqrt(2*gamma_t2*E)*Lmix. ----
      call meke_kh_closure(nx, ny, this%khcoeff, &
                           this%barotr_fac2, this%meke, this%le, this%kh_diff)

      ! ---- 7b. MEKE -> Ku backscatter coefficient (v1, harmonic only). ----
      ! ku = visc_coeff_ku*sqrt(2*gamma_t2*E)*Lmix; same eddy-velocity ×
      ! mixing-length form as kh.  Filled only when `backscatter`; the
      ! driver subtracts a face-average of `ku` from the resolved harmonic
      ! viscosity (stability-floored) ⇒ a negative-viscosity energy return.
      ! Default off ⇒ ku stays 0 ⇒ bit-identical.
      call meke_ku_closure(nx, ny, this%backscatter, this%visc_coeff_ku, &
                           this%meke, this%le, this%ku)

      ! ---- 8. feedback seam: add geom-mean kh into VarMix face KhTh/KhTr. ----
      if (present(varmix)) then
         if (varmix%is_init .and. (this%khth_fac /= 0.0_wp .or. &
                                   this%khtr_fac /= 0.0_wp)) then
            call meke_feed_khth(nx, ny, this%khth_fac, this%khtr_fac, &
                                this%kh_diff, varmix%khth_u, varmix%khth_v, &
                                varmix%khtr_u, varmix%khtr_v)
         end if
      end if
   end subroutine meke_step