vcoord_target_dz_column Subroutine

public pure subroutine vcoord_target_dz_column(coord_type, nz, H, dsig, z_ref, depth_transition, blend_width, dz)

Compute target layer thicknesses for a single water column. Pure, called from do concurrent (one thread per column).

Output dz is bottom-up (ROMS): dz(1) = BOTTOM, dz(nz) = SURFACE; matches the solvers’ h_layer indexing (consumers write h_layer(k,…) = dz(k) with no reversal). sum(dz) = H in all cases. SIGMA: dz(k) = dsig(k)H (terrain-following). ZSIGMA: smooth blend sigma (shallow) → fixed z-levels (deep). ZSTAR: z-lite, dz(k) = (z_ref(nz-k+1)-z_ref(nz-k))H/z_ref(nz); interfaces stay at fixed relative position as η changes. ZSTAR_SIGMA: smoothstep blend of sigma (shallow) and z-lite (deep); each branch sums to H so no surface trim needed.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: coord_type
integer, intent(in) :: nz
real(kind=wp), intent(in) :: H

Total water depth at this column (m)

real(kind=wp), intent(in) :: dsig(nz)

Reference sigma fractions (sum = 1), ROMS-ordered: dsig(1) bottom, dsig(nz) surface. Currently uniform 1/nz.

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

Reference z-level interface depths (m, positive down). z_ref(0) = 0 is the surface; z_ref(nz) is the deepest reference interface. Used for VCOORD_ZSIGMA and VCOORD_ZSTAR.

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

Depth (m) below which blending begins

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

Width of the blending zone (m)

real(kind=wp), intent(out) :: dz(nz)

Output target layer thicknesses, ROMS-ordered (sum = H, dz(1) = bottom, dz(nz) = surface)


Variables

Type Visibility Attributes Name Initial
real(kind=wp), private :: alpha
real(kind=wp), private :: deficit
real(kind=wp), private :: dz_sum
real(kind=wp), private :: dz_z
integer, private :: k
real(kind=wp), private :: x
real(kind=wp), private :: z_bot_k
real(kind=wp), private :: z_top_k

Source Code

   pure subroutine vcoord_target_dz_column(coord_type, nz, H, dsig, z_ref, &
                                           depth_transition, blend_width, dz)
      !$acc routine seq
      !! Compute target layer thicknesses for a single water column.
      !! Pure, called from `do concurrent` (one thread per column).
      !!
      !! Output `dz` is bottom-up (ROMS): dz(1) = BOTTOM, dz(nz) = SURFACE;
      !! matches the solvers' `h_layer` indexing (consumers write
      !! h_layer(k,...) = dz(k) with no reversal). sum(dz) = H in all cases.
      !!   SIGMA:       dz(k) = dsig(k)*H (terrain-following).
      !!   ZSIGMA:      smooth blend sigma (shallow) → fixed z-levels (deep).
      !!   ZSTAR:       z*-lite, dz(k) = (z_ref(nz-k+1)-z_ref(nz-k))*H/z_ref(nz);
      !!                interfaces stay at fixed relative position as η changes.
      !!   ZSTAR_SIGMA: smoothstep blend of sigma (shallow) and z*-lite (deep);
      !!                each branch sums to H so no surface trim needed.
      integer, intent(in) :: coord_type
      integer, intent(in) :: nz
      real(wp), intent(in) :: H
         !! Total water depth at this column (m)
      real(wp), intent(in) :: dsig(nz)
         !! Reference sigma fractions (sum = 1), ROMS-ordered: dsig(1) bottom,
         !! dsig(nz) surface. Currently uniform 1/nz.
      real(wp), intent(in) :: z_ref(0:nz)
         !! Reference z-level interface depths (m, positive down).
         !! `z_ref(0) = 0` is the surface; `z_ref(nz)` is the deepest
         !! reference interface.  Used for VCOORD_ZSIGMA and VCOORD_ZSTAR.
      real(wp), intent(in) :: depth_transition
         !! Depth (m) below which blending begins
      real(wp), intent(in) :: blend_width
         !! Width of the blending zone (m)
      real(wp), intent(out) :: dz(nz)
         !! Output target layer thicknesses, ROMS-ordered (sum = H,
         !! `dz(1)` = bottom, `dz(nz)` = surface)

      real(wp) :: alpha, x, z_top_k, z_bot_k, dz_z, dz_sum, deficit
      integer :: k

      select case (coord_type)

      case (VCOORD_SIGMA)
         ! Pure terrain-following.  Uniform dsig means orientation is
         ! immaterial — the output is the same in either direction.
         do k = 1, nz
            dz(k) = dsig(k)*H
         end do

      case (VCOORD_ZSTAR)
         ! z*-lite: stretch the global reference pattern uniformly so
         ! sum(dz) = H (H = h_bed + η). ROMS-ordered: dz(1) deepest, dz(nz)
         ! surface. Reduces to uniform sigma for uniform z_ref.
         if (z_ref(nz) > 0.0_wp) then
            do k = 1, nz
               dz_z = z_ref(nz - k + 1) - z_ref(nz - k)
               dz(k) = dz_z*H/z_ref(nz)
            end do
         else
            ! Degenerate z_ref — fall back to uniform sigma
            do k = 1, nz
               dz(k) = dsig(k)*H
            end do
         end if

      case (VCOORD_ZSIGMA)
         ! Smooth blend sigma↔z-levels: alpha=0 shallow (H<=depth_transition,
         ! pure sigma) → alpha=1 deep (H>=depth_transition+blend_width, z-levels).
         ! ROMS order: layer k spans z_ref(nz-k)..z_ref(nz-k+1).
         if (H <= depth_transition) then
            ! Pure sigma — uniform dsig, orientation immaterial
            do k = 1, nz
               dz(k) = dsig(k)*H
            end do
         else
            ! Compute blending factor
            if (blend_width > 0.0_wp) then
               x = (H - depth_transition)/blend_width
               x = max(0.0_wp, min(1.0_wp, x))
               alpha = x*x*(3.0_wp - 2.0_wp*x)  ! smoothstep
            else
               alpha = 1.0_wp
            end if

            ! Compute z-level thicknesses (clip to column depth)
            dz_sum = 0.0_wp
            do k = 1, nz
               z_top_k = min(z_ref(nz - k), H)        ! shallower interface
               z_bot_k = min(z_ref(nz - k + 1), H)    ! deeper interface
               dz_z = max(z_bot_k - z_top_k, 0.0_wp)
               ! Blend: (1-alpha)*sigma + alpha*z-level
               dz(k) = (1.0_wp - alpha)*dsig(k)*H + alpha*dz_z
               dz_sum = dz_sum + dz(k)
            end do

            ! When H is shallower than the deepest z_ref, layers near the
            ! bottom (k=1 under ROMS) get clipped to zero.  Put the
            ! deficit back into the bottom layer so sum(dz) = H.
            deficit = H - dz_sum
            if (abs(deficit) > 0.0_wp) then
               dz(1) = dz(1) + deficit
            end if
         end if

      case (VCOORD_ZSTAR_SIGMA)
         ! z*/sigma smoothstep blend: alpha=0 shallow (pure sigma) → alpha=1 deep
         ! (pure z*-lite). Both branches sum to H so the blend does, no trim.
         if (H <= depth_transition .or. z_ref(nz) <= 0.0_wp) then
            ! Pure sigma — including the degenerate-z_ref fallback so
            ! mass conservation holds without an extra dz_sum fix-up.
            do k = 1, nz
               dz(k) = dsig(k)*H
            end do
         else
            if (blend_width > 0.0_wp) then
               x = (H - depth_transition)/blend_width
               x = max(0.0_wp, min(1.0_wp, x))
               alpha = x*x*(3.0_wp - 2.0_wp*x)  ! smoothstep
            else
               alpha = 1.0_wp
            end if
            ! z*-lite contribution: dz_z = (z_ref(nz-k+1)-z_ref(nz-k)) * H/z_ref(nz)
            do k = 1, nz
               dz_z = (z_ref(nz - k + 1) - z_ref(nz - k))*H/z_ref(nz)
               dz(k) = (1.0_wp - alpha)*dsig(k)*H + alpha*dz_z
            end do
         end if

      case default
         ! Fallback to sigma (uniform, orientation immaterial)
         do k = 1, nz
            dz(k) = dsig(k)*H
         end do

      end select
   end subroutine vcoord_target_dz_column