Geometric target-grid kernels (EULERIAN_Z, SIGMA, ZSIGMA,
ZSTAR_SIGMA, ZSTAR_FULL; formulae documented on
ocean_vcoord_compute_target_h_impl). Flat on purpose — every
array an explicit-shape dummy, every knob a scalar dummy — so no
derived-type component and no associate-name reaches a
do concurrent (see ocean_vcoord_rho_target for the GPU fault
that shape caused there).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in), | value | :: | coord_type |
|
|
| integer, | intent(in), | value | :: | nx |
i-extent of every horizontal array (total, incl. halos). |
|
| integer, | intent(in), | value | :: | ny |
j-extent of every horizontal array (total, incl. halos). |
|
| integer, | intent(in), | value | :: | nz |
Number of layers; |
|
| real(kind=wp), | intent(inout) | :: | target_h(nx,ny,nz) |
Target layer thickness (m), bottom-up. |
||
| real(kind=wp), | intent(in) | :: | total_h(nx,ny) |
Column-total depth H (m). |
||
| real(kind=wp), | intent(in) | :: | eta(nx,ny) |
Free-surface anomaly η (m). |
||
| real(kind=wp), | intent(in) | :: | dsig(nz) |
Nominal layer fractions, bottom-up. |
||
| real(kind=wp), | intent(in) | :: | z_ref_global(0:nz) |
Global reference interface depths (ZSIGMA / ZSTAR_SIGMA). |
||
| real(kind=wp), | intent(in) | :: | z_ref(nx,ny,0:nz) |
Per-column reference interface depths (ZSTAR_FULL). |
||
| real(kind=wp), | intent(in), | value | :: | zsigma_depth_transition |
Sigma → z* transition depth (m). |
|
| real(kind=wp), | intent(in), | value | :: | zsigma_blend_width |
Smoothstep blend width (m). |
|
| real(kind=wp), | intent(in), | value | :: | zstar_h_min |
Vanished-layer thickness (m). |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| real(kind=wp), | private | :: | H_eff | ||||
| real(kind=wp), | private | :: | alpha | ||||
| real(kind=wp), | private | :: | column_total | ||||
| real(kind=wp), | private | :: | deficit | ||||
| real(kind=wp), | private | :: | dz_sum | ||||
| real(kind=wp), | private | :: | dz_z | ||||
| real(kind=wp), | private | :: | eta_loc | ||||
| real(kind=wp), | private | :: | h_bed_ref | ||||
| integer, | private | :: | i | ||||
| integer, | private | :: | j | ||||
| integer, | private | :: | k | ||||
| real(kind=wp), | private | :: | sum_dz | ||||
| real(kind=wp), | private | :: | x | ||||
| real(kind=wp), | private | :: | z_bot_k | ||||
| real(kind=wp), | private | :: | z_lower | ||||
| real(kind=wp), | private | :: | z_ref_nz_inv | ||||
| real(kind=wp), | private | :: | z_top_k | ||||
| real(kind=wp), | private | :: | z_upper |
pure subroutine ocean_vcoord_geometric_target(coord_type, nx, ny, nz, target_h, & total_h, eta, dsig, z_ref_global, & z_ref, zsigma_depth_transition, & zsigma_blend_width, zstar_h_min) !! Geometric target-grid kernels (EULERIAN_Z, SIGMA, ZSIGMA, !! ZSTAR_SIGMA, ZSTAR_FULL; formulae documented on !! `ocean_vcoord_compute_target_h_impl`). Flat on purpose — every !! array an explicit-shape dummy, every knob a scalar dummy — so no !! derived-type component and no `associate`-name reaches a !! `do concurrent` (see `ocean_vcoord_rho_target` for the GPU fault !! that shape caused there). integer, intent(in), value :: coord_type !! `VCOORD_*` family (not LAGRANGIAN / Z_FIXED / ZSTAR: the !! dispatcher owns those). integer, intent(in), value :: nx !! i-extent of every horizontal array (total, incl. halos). integer, intent(in), value :: ny !! j-extent of every horizontal array (total, incl. halos). integer, intent(in), value :: nz !! Number of layers; `k = 1` is the bed, `k = nz` the surface. real(wp), intent(inout) :: target_h(nx, ny, nz) !! Target layer thickness (m), bottom-up. real(wp), intent(in) :: total_h(nx, ny) !! Column-total depth H (m). real(wp), intent(in) :: eta(nx, ny) !! Free-surface anomaly η (m). real(wp), intent(in) :: dsig(nz) !! Nominal layer fractions, bottom-up. real(wp), intent(in) :: z_ref_global(0:nz) !! Global reference interface depths (ZSIGMA / ZSTAR_SIGMA). real(wp), intent(in) :: z_ref(nx, ny, 0:nz) !! Per-column reference interface depths (ZSTAR_FULL). real(wp), intent(in), value :: zsigma_depth_transition !! Sigma → z* transition depth (m). real(wp), intent(in), value :: zsigma_blend_width !! Smoothstep blend width (m). real(wp), intent(in), value :: zstar_h_min !! Vanished-layer thickness (m). integer :: i, j, k real(wp) :: column_total, alpha, x, z_top_k, z_bot_k, dz_z, dz_sum, deficit real(wp) :: z_ref_nz_inv real(wp) :: h_bed_ref, eta_loc, H_eff, z_upper, z_lower, sum_dz select case (coord_type) case (VCOORD_EULERIAN_Z) do concurrent(k=1:nz, j=1:ny, i=1:nx) target_h(i, j, k) = total_h(i, j)*dsig(k) end do case (VCOORD_SIGMA) do concurrent(k=1:nz, j=1:ny, i=1:nx) column_total = total_h(i, j) + eta(i, j) target_h(i, j, k) = column_total*dsig(k) end do case (VCOORD_ZSIGMA) ! Smoothstep blend: sigma in shallow, fixed z-levels in deep. ! Mirrors `vcoord_target_dz_column` in `src/ALE/rdb_vcoord.F90` ! but built directly on the 2D (H, η) fields. The deep-branch ! z-level intervals are clipped to the local column total so ! sum_k target_h = H + η exactly even when the column is shallower ! than the deepest reference interface; any residual deficit is ! deposited in the bed-side layer (k=1) to preserve the sum. do concurrent(j=1:ny, i=1:nx) & local(column_total, alpha, x, k, z_top_k, z_bot_k, dz_z, dz_sum, deficit) column_total = total_h(i, j) + eta(i, j) if (column_total <= zsigma_depth_transition) then do k = 1, nz target_h(i, j, k) = dsig(k)*column_total end do else if (zsigma_blend_width > 0.0_wp) then x = (column_total - zsigma_depth_transition)/zsigma_blend_width x = max(0.0_wp, min(1.0_wp, x)) alpha = x*x*(3.0_wp - 2.0_wp*x) else alpha = 1.0_wp end if dz_sum = 0.0_wp do k = 1, nz z_top_k = min(z_ref_global(nz - k), column_total) z_bot_k = min(z_ref_global(nz - k + 1), column_total) dz_z = max(z_bot_k - z_top_k, 0.0_wp) target_h(i, j, k) = (1.0_wp - alpha)*dsig(k)*column_total & + alpha*dz_z dz_sum = dz_sum + target_h(i, j, k) end do deficit = column_total - dz_sum target_h(i, j, 1) = target_h(i, j, 1) + deficit end if end do case (VCOORD_ZSTAR_SIGMA) z_ref_nz_inv = 0.0_wp if (z_ref_global(nz) > 0.0_wp) then z_ref_nz_inv = 1.0_wp/z_ref_global(nz) end if do concurrent(j=1:ny, i=1:nx) & local(column_total, alpha, x, k, dz_z) column_total = total_h(i, j) + eta(i, j) if (column_total <= zsigma_depth_transition .or. z_ref_nz_inv == 0.0_wp) then do k = 1, nz target_h(i, j, k) = dsig(k)*column_total end do else if (zsigma_blend_width > 0.0_wp) then x = (column_total - zsigma_depth_transition)/zsigma_blend_width x = max(0.0_wp, min(1.0_wp, x)) alpha = x*x*(3.0_wp - 2.0_wp*x) else alpha = 1.0_wp end if do k = 1, nz dz_z = (z_ref_global(nz - k + 1) - z_ref_global(nz - k)) & *column_total*z_ref_nz_inv target_h(i, j, k) = (1.0_wp - alpha)*dsig(k)*column_total & + alpha*dz_z end do end if end do case (VCOORD_ZSTAR_FULL) ! Per-column z*-full: walk the cached `z_ref(i, j, 0:nz)` from ! `build_zref_full`. Surface layer absorbs η when η ≥ 0; ! bed-side layers vanish to `zstar_h_min` and the surface gets ! trimmed for exact conservation when η < 0. Mirrors ! `vcoord_target_dz_column_zstar_full` (coastal) but emits ! ROMS order (k=1 bed, k=nz surface) directly. do concurrent(j=1:ny, i=1:nx) & local(k, h_bed_ref, eta_loc, H_eff, z_upper, z_lower, sum_dz, deficit) h_bed_ref = z_ref(i, j, nz) eta_loc = (total_h(i, j) + eta(i, j)) - h_bed_ref H_eff = max(total_h(i, j) + eta(i, j), 0.0_wp) if (h_bed_ref <= 0.0_wp) then ! Degenerate column: emit a single vanishing-layer stack. do k = 1, nz target_h(i, j, k) = zstar_h_min end do else if (eta_loc >= 0.0_wp) then ! Column at or above reference: subsurface = z_ref intervals, ! surface (k=nz) gets the +η. do k = 1, nz ! k_top = nz - k + 1 in the top-down z_ref convention. target_h(i, j, k) = max( & z_ref(i, j, nz - k + 1) - z_ref(i, j, nz - k), & 0.0_wp) end do target_h(i, j, nz) = target_h(i, j, nz) + eta_loc else ! Column shallower than reference (η < 0). Walk top-down, ! clip layers to H_eff, vanish below. do k = 1, nz z_upper = z_ref(i, j, nz - k) z_lower = z_ref(i, j, nz - k + 1) if (z_lower <= H_eff) then target_h(i, j, k) = z_lower - z_upper else if (z_upper < H_eff) then target_h(i, j, k) = H_eff - z_upper else target_h(i, j, k) = zstar_h_min end if end do ! Surface trim: drop the vanishing-layer overhead from the ! surface to make sum = H exactly. If the surface would ! itself fall below h_min, leave it at h_min and let the ! downstream dry-cell guards handle the deficit. sum_dz = 0.0_wp do k = 1, nz sum_dz = sum_dz + target_h(i, j, k) end do deficit = sum_dz - H_eff if (deficit > 0.0_wp) then if (target_h(i, j, nz) - deficit >= zstar_h_min) then target_h(i, j, nz) = target_h(i, j, nz) - deficit else target_h(i, j, nz) = zstar_h_min end if end if end if end do case default error stop "ocean_vcoord_geometric_target: unknown coord_type." end select end subroutine ocean_vcoord_geometric_target