evp_truncate_final_impl Subroutine

public pure subroutine evp_truncate_final_impl(areaT, dy_cu, dx_cv, mi_u, mi_v, ui, vi, cfl_trunc, dt_tr, m_neglect, nghost, nx_phys, ny_phys, nx, ny, n_trunc, count_w, count_s)

PR 36: the FINAL CFL clip (SIS2 :1443-1500) – TRUNC_BACKOFF (0.95) back-off instead of the exact bound, PLUS a count of the ice-bearing faces it touched (mi > m_neglect, SIS2 :1466,1469 – massless faces clip silently, matching SIS2: counting them would flood the driver’s warning with meaningless ice-free clips at every margin). Not a do concurrent: reductions use !$acc parallel loop reduction(...) (ice_compress_impl is the local precedent for a reduction that also mutates the arrays it walks). Same bound algebra as evp_truncate_velocity_impl, duplicated rather than shared: the in-loop variant runs evp_sub_steps (432 by default) times per outer step and must NOT carry a reduction (each would be a device->host sync); this variant runs once and must. CLAUDE.md’s “duplicate explicitly” rule – merging the two costs 432 syncs per outer step.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: areaT(nx,ny)
real(kind=wp), intent(in) :: dy_cu(nx+1,ny)
real(kind=wp), intent(in) :: dx_cv(nx,ny+1)
real(kind=wp), intent(in) :: mi_u(nx+1,ny)
real(kind=wp), intent(in) :: mi_v(nx,ny+1)
real(kind=wp), intent(inout) :: ui(nx+1,ny)
real(kind=wp), intent(inout) :: vi(nx,ny+1)
real(kind=wp), intent(in) :: cfl_trunc
real(kind=wp), intent(in) :: dt_tr
real(kind=wp), intent(in) :: m_neglect
integer, intent(in) :: nghost
integer, intent(in) :: nx_phys
integer, intent(in) :: ny_phys
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(out) :: n_trunc
logical, intent(in), optional :: count_w

Count the WEST (i = nghost+1) / SOUTH (j = nghost+1) edge face. That face is clipped either way; it is COUNTED only when this tile owns it — a physical, non-periodic edge. Across an MPI seam the west/south neighbour owns it (D1), and across a periodic seam it is the same face as the east/north edge face, so counting it there would count one face twice in the rank-summed total. Absent => .true. (count every face).

logical, intent(in), optional :: count_s

Count the WEST (i = nghost+1) / SOUTH (j = nghost+1) edge face. That face is clipped either way; it is COUNTED only when this tile owns it — a physical, non-periodic edge. Across an MPI seam the west/south neighbour owns it (D1), and across a periodic seam it is the same face as the east/north edge face, so counting it there would count one face twice in the rank-summed total. Absent => .true. (count every face).


Calls

proc~~evp_truncate_final_impl~~CallsGraph proc~evp_truncate_final_impl evp_truncate_final_impl local local proc~evp_truncate_final_impl->local reduce reduce proc~evp_truncate_final_impl->reduce

Called by

proc~~evp_truncate_final_impl~~CalledByGraph proc~evp_truncate_final_impl evp_truncate_final_impl proc~ice_evp_dynamics_impl ice_evp_dynamics_impl proc~ice_evp_dynamics_impl->proc~evp_truncate_final_impl proc~ice_evp_dynamics ice_evp_dynamics proc~ice_evp_dynamics->proc~ice_evp_dynamics_impl proc~ice_evp_step ice_evp_step proc~ice_evp_step->proc~ice_evp_dynamics proc~engine_step_ice engine_step_ice proc~engine_step_ice->proc~ice_evp_step proc~driver_run_ocean driver_run_ocean proc~driver_run_ocean->proc~engine_step_ice proc~rdb_ocean_step rdb_ocean_step proc~rdb_ocean_step->proc~engine_step_ice

Variables

Type Visibility Attributes Name Initial
integer, private :: i
integer, private :: i_cnt
integer, private :: i_hi
integer, private :: i_lo
integer, private :: j
integer, private :: j_cnt
integer, private :: j_hi
integer, private :: j_lo
real(kind=wp), private :: loc_scale
integer, private :: n_acc
real(kind=wp), private :: u_hi
real(kind=wp), private :: u_lo
real(kind=wp), private :: v_hi
real(kind=wp), private :: v_lo

Source Code

   pure subroutine evp_truncate_final_impl(areaT, dy_cu, dx_cv, mi_u, mi_v, ui, vi, &
                                           cfl_trunc, dt_tr, m_neglect, nghost, nx_phys, &
                                           ny_phys, nx, ny, n_trunc, count_w, count_s)
      !! PR 36: the FINAL CFL clip (SIS2 `:1443-1500`) -- `TRUNC_BACKOFF`
      !! (0.95) back-off instead of the exact bound, PLUS a count of the
      !! ice-bearing faces it touched (`mi > m_neglect`, SIS2 `:1466,1469`
      !! -- massless faces clip silently, matching SIS2: counting them
      !! would flood the driver's warning with meaningless ice-free clips
      !! at every margin). Not a `do concurrent`: reductions use
      !! `!$acc parallel loop reduction(...)` (`ice_compress_impl` is the
      !! local precedent for a reduction that also mutates the arrays it
      !! walks). Same bound algebra as `evp_truncate_velocity_impl`,
      !! duplicated rather than shared: the in-loop variant runs
      !! `evp_sub_steps` (432 by default) times per outer step and must
      !! NOT carry a reduction (each would be a device->host sync); this
      !! variant runs once and must. `CLAUDE.md`'s "duplicate explicitly"
      !! rule -- merging the two costs 432 syncs per outer step.
      integer, intent(in) :: nghost, nx_phys, ny_phys, nx, ny
      real(wp), intent(in) :: areaT(nx, ny)
      real(wp), intent(in) :: dy_cu(nx + 1, ny), dx_cv(nx, ny + 1)
      real(wp), intent(in) :: mi_u(nx + 1, ny), mi_v(nx, ny + 1)
      real(wp), intent(inout) :: ui(nx + 1, ny), vi(nx, ny + 1)
      real(wp), intent(in) :: cfl_trunc, dt_tr, m_neglect
      integer, intent(out) :: n_trunc
      logical, intent(in), optional :: count_w, count_s
         !! Count the WEST (`i = nghost+1`) / SOUTH (`j = nghost+1`) edge
         !! face.  That face is clipped either way; it is COUNTED only when
         !! this tile owns it — a physical, non-periodic edge.  Across an
         !! MPI seam the west/south neighbour owns it (D1), and across a
         !! periodic seam it is the same face as the east/north edge face,
         !! so counting it there would count one face twice in the
         !! rank-summed total.  Absent => `.true.` (count every face).
      integer :: i, j, i_lo, i_hi, j_lo, j_hi, i_cnt, j_cnt
      real(wp) :: u_hi, u_lo, v_hi, v_lo, loc_scale
      integer :: n_acc

      n_acc = 0
      ! First counted edge face: nghost+1 when this tile owns it, else +1.
      i_cnt = nghost + 1
      if (present(count_w)) then
         if (.not. count_w) i_cnt = nghost + 2
      end if
      j_cnt = nghost + 1
      if (present(count_s)) then
         if (.not. count_s) j_cnt = nghost + 2
      end if

      i_lo = nghost + 1
      i_hi = nghost + nx_phys + 1
      j_lo = nghost + 1
      j_hi = nghost + ny_phys
      do concurrent(j=j_lo:j_hi, i=i_lo:i_hi) local(u_hi, u_lo, loc_scale) reduce(+:n_acc)
         u_hi = 0.0_wp
         u_lo = 0.0_wp
         if (dy_cu(i, j) > 0.0_wp) then
            loc_scale = cfl_trunc/(dt_tr*dy_cu(i, j))
            u_hi = TRUNC_BACKOFF*loc_scale*areaT(i - 1, j)
            u_lo = -TRUNC_BACKOFF*loc_scale*areaT(i, j)
         end if
         if (ui(i, j) > u_hi .or. ui(i, j) < u_lo) then
            if (mi_u(i, j) > m_neglect .and. i >= i_cnt) n_acc = n_acc + 1
            ui(i, j) = merge(u_hi, u_lo, ui(i, j) > u_hi)
         end if
      end do

      i_lo = nghost + 1
      i_hi = nghost + nx_phys
      j_lo = nghost + 1
      j_hi = nghost + ny_phys + 1
      do concurrent(j=j_lo:j_hi, i=i_lo:i_hi) local(v_hi, v_lo, loc_scale) reduce(+:n_acc)
         v_hi = 0.0_wp
         v_lo = 0.0_wp
         if (dx_cv(i, j) > 0.0_wp) then
            loc_scale = cfl_trunc/(dt_tr*dx_cv(i, j))
            v_hi = TRUNC_BACKOFF*loc_scale*areaT(i, j - 1)
            v_lo = -TRUNC_BACKOFF*loc_scale*areaT(i, j)
         end if
         if (vi(i, j) > v_hi .or. vi(i, j) < v_lo) then
            if (mi_v(i, j) > m_neglect .and. j >= j_cnt) n_acc = n_acc + 1
            vi(i, j) = merge(v_hi, v_lo, vi(i, j) > v_hi)
         end if
      end do

      n_trunc = n_acc
   end subroutine evp_truncate_final_impl