ocean_lateral_mix_compute_smag Subroutine

public pure subroutine ocean_lateral_mix_compute_smag(grid, metrics, this, ms, res_fn_u, res_fn_v)

Populate ah_face_x/ah_face_y (m^2/s) with the Smagorinsky Laplacian viscosity A_h(face) = max(ah_bg, min(ah_max, (C_S · dx)^2 · |D|)) where |D| = sqrt(D_T^2 + D_S^2) is the deformation-tensor magnitude — tension D_T = ∂u/∂x − ∂v/∂y (cell centred) and shear D_S = ∂v/∂x + ∂u/∂y (corner) — averaged onto the face. Wall faces get the background viscosity (wall-adjacent rows re-use the next interior row). Smagorinsky (1963); C_S ≈ 0.15–0.2.

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(ocean_metrics_t), intent(in) :: metrics
type(ocean_lateral_mix_t), intent(inout) :: this
type(multilayer_state_t), intent(in) :: ms
real(kind=wp), intent(in), optional :: res_fn_u(grid%nx_total+1,grid%ny_total)

VarMix resolution function at u-faces (nondim, [0,1]). When present AND this%resoln_scaled_visc, scales A_h before clamp.

real(kind=wp), intent(in), optional :: res_fn_v(grid%nx_total,grid%ny_total+1)

VarMix resolution function at v-faces.


Calls

proc~~ocean_lateral_mix_compute_smag~~CallsGraph proc~ocean_lateral_mix_compute_smag ocean_lateral_mix_compute_smag local local proc~ocean_lateral_mix_compute_smag->local

Called by

proc~~ocean_lateral_mix_compute_smag~~CalledByGraph proc~ocean_lateral_mix_compute_smag ocean_lateral_mix_compute_smag proc~ocean_lateral_mix_compute ocean_lateral_mix_compute proc~ocean_lateral_mix_compute->proc~ocean_lateral_mix_compute_smag proc~run_stage run_stage proc~run_stage->proc~ocean_lateral_mix_compute proc~run_stage_split run_stage_split proc~run_stage_split->proc~ocean_lateral_mix_compute proc~ocean_dyn_step ocean_dyn_step proc~ocean_dyn_step->proc~run_stage proc~ocean_dyn_step_split ocean_dyn_step_split proc~ocean_dyn_step_split->proc~run_stage_split proc~engine_step engine_step proc~engine_step->proc~ocean_dyn_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

Variables

Type Visibility Attributes Name Initial
real(kind=wp), private :: A_raw
real(kind=wp), private :: D_S_E
real(kind=wp), private :: D_S_N
real(kind=wp), private :: D_S_S
real(kind=wp), private :: D_S_W
real(kind=wp), private :: D_S_face
real(kind=wp), private :: D_T_E
real(kind=wp), private :: D_T_N
real(kind=wp), private :: D_T_S
real(kind=wp), private :: D_T_W
real(kind=wp), private :: D_T_face
real(kind=wp), private :: ah_bg_local
real(kind=wp), private :: ah_max_local
real(kind=wp), private :: c_smag_local
logical, private :: do_resoln
integer, private :: i
integer, private :: j
integer, private :: k
real(kind=wp), private :: ns
integer, private :: nx
integer, private :: ny
integer, private :: nz
real(kind=wp), private :: smag_scale
real(kind=wp), private :: strain_mag

Source Code

   pure subroutine ocean_lateral_mix_compute_smag(grid, metrics, this, ms, &
                                                  res_fn_u, res_fn_v)
      !! Populate `ah_face_x`/`ah_face_y` (m^2/s) with the Smagorinsky
      !! Laplacian viscosity
      !!     A_h(face) = max(ah_bg, min(ah_max, (C_S · dx)^2 · |D|))
      !! where `|D| = sqrt(D_T^2 + D_S^2)` is the deformation-tensor
      !! magnitude — tension `D_T = ∂u/∂x − ∂v/∂y` (cell centred) and
      !! shear `D_S = ∂v/∂x + ∂u/∂y` (corner) — averaged onto the face.
      !! Wall faces get the background viscosity (wall-adjacent rows
      !! re-use the next interior row).  Smagorinsky (1963); C_S ≈ 0.15–0.2.
      type(hgrid_t), intent(in) :: grid
      type(ocean_metrics_t), intent(in) :: metrics
      type(ocean_lateral_mix_t), intent(inout) :: this
      type(multilayer_state_t), intent(in) :: ms
      real(wp), intent(in), optional :: res_fn_u(grid%nx_total + 1, grid%ny_total)
         !! VarMix resolution function at u-faces (nondim, [0,1]).  When
         !! present AND `this%resoln_scaled_visc`, scales `A_h` before clamp.
      real(wp), intent(in), optional :: res_fn_v(grid%nx_total, grid%ny_total + 1)
         !! VarMix resolution function at v-faces.

      integer :: i, j, k, nx, ny, nz
      real(wp) :: c_smag_local, smag_scale, ah_bg_local, ah_max_local
      real(wp) :: D_T_W, D_T_E, D_T_S, D_T_N, D_T_face
      real(wp) :: D_S_S, D_S_N, D_S_W, D_S_E, D_S_face
      real(wp) :: strain_mag, A_raw, ns
      logical :: do_resoln

      if (.not. this%is_init) return
      if (.not. allocated(ms%u_face_x_layer)) return
      if (.not. allocated(ms%v_face_y_layer)) return

      nx = grid%nx_total
      ny = grid%ny_total
      nz = ms%nz_ml

      ! Hoist scalar reads off `this` to local variables (see Leith
      ! sibling) — guards against device-side descriptor walks.
      ah_bg_local = this%ah_bg
      ah_max_local = this%ah_max
      c_smag_local = this%c_smag
      ns = merge(1.0_wp, 0.0_wp, this%no_slip)   ! free-slip(0)/no-slip(1) selector (C1)
      ! Resolution-function scaling (Gap 1, Hallberg 2013) — see compute_leith.
      do_resoln = this%resoln_scaled_visc .and. present(res_fn_u) .and. &
                  present(res_fn_v)

      ! Strain components use per-stagger metric inverses (design §2):
      ! tension ∂u/∂x, ∂v/∂y on cell (idxT/idyT); shear ∂v/∂x, ∂u/∂y
      ! on the corner (idxBu/idyBu).  smag_scale = (C_S·sqrt(dxT·dyT))²
      ! per cell (= (C_S·dx)² on uniform square metrics, bit-reducing).

      ! ---- u-face viscosity (i-1/2, j) ----
      ! D_T at the face = mean of the two adjacent cell-centred values:
      !   D_T(i-1, j) and D_T(i, j).
      ! D_S at the face = mean of the two adjacent corner values along
      ! the face: D_S(i, j) at SW corner of (i, j) and D_S(i, j+1) at NW.
      !
      ! Curvilinear D_S at corner Bu(i,j) (= SW corner of T(i,j)):
      !   dvdx = dy_dxBu · (v(i,j)·idyCv(i,j)  - v(i-1,j)·idyCv(i-1,j))
      !   dudy = dx_dyBu · (u(i,j)·idxCu(i,j)  - u(i,j-1)·idxCu(i,j-1))
      !   D_S  = dvdx + dudy
      ! On uniform SQUARE metrics dy_dxBu=dx_dyBu=1 and idyCv=idxCu=1/dx,
      ! so this collapses to the old plain-difference form bit-for-bit.
      ! (design §2; mirrors MOM6 MOM_hor_visc shear-strain form)
      do concurrent(k=1:nz, j=2:ny - 1, i=2:nx) &
         local(D_T_W, D_T_E, D_S_S, D_S_N, D_T_face, D_S_face, &
               strain_mag, A_raw, smag_scale)
         smag_scale = (c_smag_local*sqrt(metrics%dxT(i, j)*metrics%dyT(i, j)))**2
         ! Cell-centred D_T at (i-1, j) and (i, j)
         D_T_W = (ms%u_face_x_layer(i, j, k) - ms%u_face_x_layer(i - 1, j, k))*metrics%idxT(i - 1, j) &
                 - (ms%v_face_y_layer(i - 1, j + 1, k) - ms%v_face_y_layer(i - 1, j, k))*metrics%idyT(i - 1, j)
         D_T_E = (ms%u_face_x_layer(i + 1, j, k) - ms%u_face_x_layer(i, j, k))*metrics%idxT(i, j) &
                 - (ms%v_face_y_layer(i, j + 1, k) - ms%v_face_y_layer(i, j, k))*metrics%idyT(i, j)
         D_T_face = 0.5_wp*(D_T_W + D_T_E)
         ! Corner D_S: ratio-bundle form (design §2).  Bu(i,j) = SW corner
         ! of T(i,j); v at Cv(i,j) / Cv(i-1,j), u at Cu(i,j) / Cu(i,j-1).
         ! Corner shear strain sh_xy masked by slip factor (C1): free-slip
         ! ×wet_q / no-slip ×(2-wet_q).  Bit-identical for all-wet.
         D_S_S = ((1.0_wp - 2.0_wp*ns)*metrics%wet_q(i, j) + 2.0_wp*ns)* &
                 (metrics%dy_dxBu(i, j)* &
                  (ms%v_face_y_layer(i, j, k)*metrics%idyCv(i, j) - &
                   ms%v_face_y_layer(i - 1, j, k)*metrics%idyCv(i - 1, j)) + &
                  metrics%dx_dyBu(i, j)* &
                  (ms%u_face_x_layer(i, j, k)*metrics%idxCu(i, j) - &
                   ms%u_face_x_layer(i, j - 1, k)*metrics%idxCu(i, j - 1)))
         D_S_N = ((1.0_wp - 2.0_wp*ns)*metrics%wet_q(i, j + 1) + 2.0_wp*ns)* &
                 (metrics%dy_dxBu(i, j + 1)* &
                  (ms%v_face_y_layer(i, j + 1, k)*metrics%idyCv(i, j + 1) - &
                   ms%v_face_y_layer(i - 1, j + 1, k)*metrics%idyCv(i - 1, j + 1)) + &
                  metrics%dx_dyBu(i, j + 1)* &
                  (ms%u_face_x_layer(i, j + 1, k)*metrics%idxCu(i, j + 1) - &
                   ms%u_face_x_layer(i, j, k)*metrics%idxCu(i, j)))
         D_S_face = 0.5_wp*(D_S_S + D_S_N)
         strain_mag = sqrt(D_T_face*D_T_face + D_S_face*D_S_face)
         if (do_resoln) then
            A_raw = smag_scale*strain_mag*res_fn_u(i, j)
         else
            A_raw = smag_scale*strain_mag
         end if
         this%ah_face_x(i, j, k) = min(ah_max_local, max(ah_bg_local, A_raw))
      end do
      ! j=1 and j=ny rows: re-use the j=2 / j=ny-1 values one row in.
      ! Avoids the j-1 / j+1 stencil walking into the wall.
      do concurrent(k=1:nz, i=2:nx)
         this%ah_face_x(i, 1, k) = this%ah_face_x(i, 2, k)
         this%ah_face_x(i, ny, k) = this%ah_face_x(i, ny - 1, k)
      end do
      ! Wall faces (i=1, i=nx+1): background.
      do concurrent(k=1:nz, j=1:ny)
         this%ah_face_x(1, j, k) = ah_bg_local
         this%ah_face_x(nx + 1, j, k) = ah_bg_local
      end do

      ! ---- v-face viscosity (i, j-1/2) ----
      ! Mirror of the u-face stencil.
      do concurrent(k=1:nz, j=2:ny, i=2:nx - 1) &
         local(D_T_S, D_T_N, D_S_W, D_S_E, D_T_face, D_S_face, &
               strain_mag, A_raw, smag_scale)
         smag_scale = (c_smag_local*sqrt(metrics%dxT(i, j)*metrics%dyT(i, j)))**2
         ! Cell-centred D_T at (i, j-1) and (i, j)
         D_T_S = (ms%u_face_x_layer(i + 1, j - 1, k) - ms%u_face_x_layer(i, j - 1, k))*metrics%idxT(i, j - 1) &
                 - (ms%v_face_y_layer(i, j, k) - ms%v_face_y_layer(i, j - 1, k))*metrics%idyT(i, j - 1)
         D_T_N = (ms%u_face_x_layer(i + 1, j, k) - ms%u_face_x_layer(i, j, k))*metrics%idxT(i, j) &
                 - (ms%v_face_y_layer(i, j + 1, k) - ms%v_face_y_layer(i, j, k))*metrics%idyT(i, j)
         D_T_face = 0.5_wp*(D_T_S + D_T_N)
         ! Corner D_S: ratio-bundle form (design §2).  Bu(i,j) = SW corner
         ! of T(i,j); v at Cv(i,j) / Cv(i-1,j), u at Cu(i,j) / Cu(i,j-1).
         ! Corner shear strain sh_xy masked by slip factor (C1).
         D_S_W = ((1.0_wp - 2.0_wp*ns)*metrics%wet_q(i, j) + 2.0_wp*ns)* &
                 (metrics%dy_dxBu(i, j)* &
                  (ms%v_face_y_layer(i, j, k)*metrics%idyCv(i, j) - &
                   ms%v_face_y_layer(i - 1, j, k)*metrics%idyCv(i - 1, j)) + &
                  metrics%dx_dyBu(i, j)* &
                  (ms%u_face_x_layer(i, j, k)*metrics%idxCu(i, j) - &
                   ms%u_face_x_layer(i, j - 1, k)*metrics%idxCu(i, j - 1)))
         D_S_E = ((1.0_wp - 2.0_wp*ns)*metrics%wet_q(i + 1, j) + 2.0_wp*ns)* &
                 (metrics%dy_dxBu(i + 1, j)* &
                  (ms%v_face_y_layer(i + 1, j, k)*metrics%idyCv(i + 1, j) - &
                   ms%v_face_y_layer(i, j, k)*metrics%idyCv(i, j)) + &
                  metrics%dx_dyBu(i + 1, j)* &
                  (ms%u_face_x_layer(i + 1, j, k)*metrics%idxCu(i + 1, j) - &
                   ms%u_face_x_layer(i + 1, j - 1, k)*metrics%idxCu(i + 1, j - 1)))
         D_S_face = 0.5_wp*(D_S_W + D_S_E)
         strain_mag = sqrt(D_T_face*D_T_face + D_S_face*D_S_face)
         if (do_resoln) then
            A_raw = smag_scale*strain_mag*res_fn_v(i, j)
         else
            A_raw = smag_scale*strain_mag
         end if
         this%ah_face_y(i, j, k) = min(ah_max_local, max(ah_bg_local, A_raw))
      end do
      ! i=1, i=nx columns: re-use the i=2 / i=nx-1 values.
      do concurrent(k=1:nz, j=2:ny)
         this%ah_face_y(1, j, k) = this%ah_face_y(2, j, k)
         this%ah_face_y(nx, j, k) = this%ah_face_y(nx - 1, j, k)
      end do
      ! Wall faces (j=1, j=ny+1): background.
      do concurrent(k=1:nz, i=1:nx)
         this%ah_face_y(i, 1, k) = ah_bg_local
         this%ah_face_y(i, ny + 1, k) = ah_bg_local
      end do
   end subroutine ocean_lateral_mix_compute_smag