Flat device kernel: explicit-shape dummies, no derived-type
dereference inside the do concurrent.
One code path covers both modes. htbl <= 0 is the LAYER-ONLY
mode: the band is layer k_top alone and h_in/h_face == 1, which
reduces the formulae below to the exact algebraic mirror of
ocean_bottom_drag_compute_tendencies’ bed-only branch (at the
default bg_vel = 0). htbl > 0 spreads the stress over the
top htbl metres, the mirror of compute_distributed_drag.
Per face, two sequential passes over k = nz downward:
1. band-mean velocity U_tbl = sum_k u_k*h_in_k / max(sum_k
h_in_k, tbl_min) and the band thickness;
2. per-layer rate and tendency.
Linear: rate_k = r * (h_in_k/h_face_k)
Quadratic: rate_k = C_d * |U_eff| * (h_in_k/h_face_k) / h_tbl
with |U_eff| = max(bg_vel, |U_tbl|), and the tendency
-rate_k*u_k/(1 + dt_imp*rate_k) — dt_imp = 0 gives the
explicit form bit-identically.
h_in_k/h_face_k is bounded by 1 by construction (h_in_k =
min(h_face_k, ...)) and the loop exits on h_face_k <= 0, so
the ratio needs no epsilon.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(out) | :: | du_drag(nx_u,ny_u,nz) | |||
| real(kind=wp), | intent(out) | :: | dv_drag(nx_v,ny_v,nz) | |||
| real(kind=wp), | intent(out) | :: | lambda_u(nx+1,ny) | |||
| real(kind=wp), | intent(out) | :: | lambda_v(nx,ny+1) | |||
| real(kind=wp), | intent(in) | :: | u_face(nx_u,ny_u,nz) | |||
| real(kind=wp), | intent(in) | :: | v_face(nx_v,ny_v,nz) | |||
| real(kind=wp), | intent(in) | :: | h_layer(nx,ny,nz) | |||
| real(kind=wp), | intent(in) | :: | wet_mask(nx,ny) | |||
| real(kind=wp), | intent(in) | :: | cover_u(nx+1,ny) | |||
| real(kind=wp), | intent(in) | :: | cover_v(nx,ny+1) | |||
| integer, | intent(in) | :: | k_top_u(nx+1,ny) |
|
||
| integer, | intent(in) | :: | k_top_v(nx,ny+1) |
|
||
| real(kind=wp), | intent(in) | :: | h_vanished |
|
||
| integer, | intent(in) | :: | variant | |||
| real(kind=wp), | intent(in) | :: | r | |||
| real(kind=wp), | intent(in) | :: | c_d | |||
| real(kind=wp), | intent(in) | :: | h_floor | |||
| real(kind=wp), | intent(in) | :: | htbl | |||
| real(kind=wp), | intent(in) | :: | bg_vel | |||
| real(kind=wp), | intent(in) | :: | tbl_min | |||
| real(kind=wp), | intent(in) | :: | dt_imp | |||
| logical, | intent(in) | :: | fold | |||
| integer, | intent(in) | :: | nx_u | |||
| integer, | intent(in) | :: | ny_u | |||
| integer, | intent(in) | :: | nx_v | |||
| integer, | intent(in) | :: | ny_v | |||
| integer, | intent(in) | :: | nx | |||
| integer, | intent(in) | :: | ny | |||
| integer, | intent(in) | :: | nz |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| real(kind=wp), | private | :: | abs_u_eff | ||||
| real(kind=wp), | private | :: | cumul_h | ||||
| real(kind=wp), | private | :: | frac | ||||
| real(kind=wp), | private | :: | h_eff | ||||
| real(kind=wp), | private | :: | h_face_k | ||||
| real(kind=wp), | private | :: | h_in | ||||
| real(kind=wp), | private | :: | h_in_total | ||||
| integer, | private | :: | i | ||||
| integer, | private | :: | j | ||||
| integer, | private | :: | k | ||||
| integer, | private | :: | kt | ||||
| logical, | private | :: | layer_only | ||||
| real(kind=wp), | private | :: | mask_face | ||||
| logical, | private | :: | quad | ||||
| real(kind=wp), | private | :: | rate | ||||
| real(kind=wp), | private | :: | u_at_v | ||||
| real(kind=wp), | private | :: | u_int | ||||
| real(kind=wp), | private | :: | u_tbl | ||||
| real(kind=wp), | private | :: | v_at_u | ||||
| real(kind=wp), | private | :: | v_int | ||||
| real(kind=wp), | private | :: | v_tbl |
pure subroutine top_drag_tendencies_impl(du_drag, dv_drag, lambda_u, lambda_v, & u_face, v_face, h_layer, wet_mask, & cover_u, cover_v, k_top_u, k_top_v, & h_vanished, & variant, r, c_d, h_floor, & htbl, bg_vel, tbl_min, dt_imp, fold, & nx_u, ny_u, nx_v, ny_v, & nx, ny, nz) !! Flat device kernel: explicit-shape dummies, no derived-type !! dereference inside the `do concurrent`. !! !! One code path covers both modes. `htbl <= 0` is the LAYER-ONLY !! mode: the band is layer `k_top` alone and `h_in/h_face == 1`, which !! reduces the formulae below to the exact algebraic mirror of !! `ocean_bottom_drag_compute_tendencies`' bed-only branch (at the !! default `bg_vel = 0`). `htbl > 0` spreads the stress over the !! top `htbl` metres, the mirror of `compute_distributed_drag`. !! !! Per face, two sequential passes over `k = nz` downward: !! 1. band-mean velocity `U_tbl = sum_k u_k*h_in_k / max(sum_k !! h_in_k, tbl_min)` and the band thickness; !! 2. per-layer rate and tendency. !! !! Linear: `rate_k = r * (h_in_k/h_face_k)` !! Quadratic: `rate_k = C_d * |U_eff| * (h_in_k/h_face_k) / h_tbl` !! with `|U_eff| = max(bg_vel, |U_tbl|)`, and the tendency !! `-rate_k*u_k/(1 + dt_imp*rate_k)` — `dt_imp = 0` gives the !! explicit form bit-identically. !! !! `h_in_k/h_face_k` is bounded by 1 by construction (`h_in_k = !! min(h_face_k, ...)`) and the loop exits on `h_face_k <= 0`, so !! the ratio needs no epsilon. integer, intent(in) :: nx_u, ny_u, nx_v, ny_v, nx, ny, nz, variant real(wp), intent(in) :: r, c_d, h_floor, htbl, bg_vel, tbl_min, dt_imp logical, intent(in) :: fold real(wp), intent(in) :: u_face(nx_u, ny_u, nz), v_face(nx_v, ny_v, nz) real(wp), intent(in) :: h_layer(nx, ny, nz) real(wp), intent(in) :: wet_mask(nx, ny) real(wp), intent(in) :: cover_u(nx + 1, ny), cover_v(nx, ny + 1) integer, intent(in) :: k_top_u(nx + 1, ny), k_top_v(nx, ny + 1) !! `ms%k_top_u` / `k_top_v` -- the first layer LIVE on BOTH !! sides of the face (`min` of the two columns' own `k_top`), !! `nz` wherever nothing vanishes against the top, so the walks !! below start exactly where they do today on every coordinate !! but `z_fixed` under a rigid top. real(wp), intent(in) :: h_vanished !! `H_VANISHED`. The band walks `exit` on a face thickness at !! or below this instead of at or below ZERO: a filler has !! `h = zstar_h_min > 0`, so the old `<= 0` gate let it into the !! band with `h_in/h_face = 1` -- FULL drag rate on a massless !! layer -- while contributing nothing to `cumul_h`. real(wp), intent(out) :: du_drag(nx_u, ny_u, nz), dv_drag(nx_v, ny_v, nz) real(wp), intent(out) :: lambda_u(nx + 1, ny), lambda_v(nx, ny + 1) integer :: i, j, k, kt logical :: layer_only, quad real(wp) :: cumul_h, h_face_k, h_in, mask_face, frac real(wp) :: h_in_total, u_int, v_int, u_tbl, v_tbl, u_at_v, v_at_u real(wp) :: abs_u_eff, h_eff, rate layer_only = (htbl <= 0.0_wp) quad = (variant == TDRAG_QUADRATIC) ! ---- Zero every level + both rate fields first ---- do concurrent(k=1:nz, j=1:ny_u, i=1:nx_u) du_drag(i, j, k) = 0.0_wp end do do concurrent(k=1:nz, j=1:ny_v, i=1:nx_v) dv_drag(i, j, k) = 0.0_wp end do do concurrent(j=1:ny, i=1:nx + 1) lambda_u(i, j) = 0.0_wp end do do concurrent(j=1:ny + 1, i=1:nx) lambda_v(i, j) = 0.0_wp end do if (variant == TDRAG_LINEAR .and. r <= 0.0_wp) return if (quad .and. c_d <= 0.0_wp) return if (.not. (quad .or. variant == TDRAG_LINEAR)) return ! ---- East (u) faces ---- do concurrent(j=1:ny, i=2:nx) & local(k, kt, cumul_h, h_face_k, h_in, mask_face, frac, & h_in_total, u_int, v_int, u_tbl, v_tbl, v_at_u, & abs_u_eff, h_eff, rate) mask_face = min(wet_mask(i - 1, j), wet_mask(i, j))*cover_u(i, j) kt = k_top_u(i, j) ! Pass 1: band mean. cumul_h = 0.0_wp u_int = 0.0_wp v_int = 0.0_wp h_in_total = 0.0_wp do k = kt, 1, -1 if (layer_only .and. k < kt) exit if ((.not. layer_only) .and. cumul_h >= htbl) exit h_face_k = 0.5_wp*(h_layer(i - 1, j, k) + h_layer(i, j, k)) if (h_face_k <= h_vanished) exit if (layer_only) then h_in = h_face_k else h_in = max(0.0_wp, min(h_face_k, htbl - cumul_h)) end if u_int = u_int + u_face(i, j, k)*h_in v_at_u = 0.25_wp*( & v_face(i - 1, j, k) + v_face(i, j, k) + & v_face(i - 1, j + 1, k) + v_face(i, j + 1, k)) v_int = v_int + v_at_u*h_in h_in_total = h_in_total + h_in cumul_h = cumul_h + h_face_k end do h_eff = max(h_in_total, tbl_min) h_eff = max(h_eff, h_floor) u_tbl = u_int/h_eff v_tbl = v_int/h_eff abs_u_eff = max(bg_vel, sqrt(u_tbl*u_tbl + v_tbl*v_tbl)) ! Pass 2: per-layer rate + tendency. cumul_h = 0.0_wp do k = kt, 1, -1 if (layer_only .and. k < kt) exit if ((.not. layer_only) .and. cumul_h >= htbl) exit h_face_k = 0.5_wp*(h_layer(i - 1, j, k) + h_layer(i, j, k)) if (h_face_k <= h_vanished) exit if (layer_only) then h_in = h_face_k else h_in = max(0.0_wp, min(h_face_k, htbl - cumul_h)) end if frac = h_in/h_face_k if (quad) then rate = mask_face*c_d*abs_u_eff*frac/h_eff else rate = mask_face*r*frac end if du_drag(i, j, k) = -rate*u_face(i, j, k)/(1.0_wp + dt_imp*rate) if (fold .and. k == kt) lambda_u(i, j) = rate cumul_h = cumul_h + h_face_k end do end do ! ---- North (v) faces ---- do concurrent(j=2:ny, i=1:nx) & local(k, kt, cumul_h, h_face_k, h_in, mask_face, frac, & h_in_total, u_int, v_int, u_tbl, v_tbl, u_at_v, & abs_u_eff, h_eff, rate) mask_face = min(wet_mask(i, j - 1), wet_mask(i, j))*cover_v(i, j) kt = k_top_v(i, j) cumul_h = 0.0_wp u_int = 0.0_wp v_int = 0.0_wp h_in_total = 0.0_wp do k = kt, 1, -1 if (layer_only .and. k < kt) exit if ((.not. layer_only) .and. cumul_h >= htbl) exit h_face_k = 0.5_wp*(h_layer(i, j - 1, k) + h_layer(i, j, k)) if (h_face_k <= h_vanished) exit if (layer_only) then h_in = h_face_k else h_in = max(0.0_wp, min(h_face_k, htbl - cumul_h)) end if v_int = v_int + v_face(i, j, k)*h_in u_at_v = 0.25_wp*( & u_face(i, j - 1, k) + u_face(i + 1, j - 1, k) + & u_face(i, j, k) + u_face(i + 1, j, k)) u_int = u_int + u_at_v*h_in h_in_total = h_in_total + h_in cumul_h = cumul_h + h_face_k end do h_eff = max(h_in_total, tbl_min) h_eff = max(h_eff, h_floor) u_tbl = u_int/h_eff v_tbl = v_int/h_eff abs_u_eff = max(bg_vel, sqrt(u_tbl*u_tbl + v_tbl*v_tbl)) cumul_h = 0.0_wp do k = kt, 1, -1 if (layer_only .and. k < kt) exit if ((.not. layer_only) .and. cumul_h >= htbl) exit h_face_k = 0.5_wp*(h_layer(i, j - 1, k) + h_layer(i, j, k)) if (h_face_k <= h_vanished) exit if (layer_only) then h_in = h_face_k else h_in = max(0.0_wp, min(h_face_k, htbl - cumul_h)) end if frac = h_in/h_face_k if (quad) then rate = mask_face*c_d*abs_u_eff*frac/h_eff else rate = mask_face*r*frac end if dv_drag(i, j, k) = -rate*v_face(i, j, k)/(1.0_wp + dt_imp*rate) if (fold .and. k == kt) lambda_v(i, j) = rate cumul_h = cumul_h + h_face_k end do end do end subroutine top_drag_tendencies_impl