evp_truncate_velocity_impl Subroutine

private pure subroutine evp_truncate_velocity_impl(areaT, dy_cu, dx_cv, ui, vi, cfl_trunc, dt_tr, backoff, nghost, nx_phys, ny_phys, nx, ny)

PR 36: the shared CFL-clip algebra – the transport-CFL bound on the ice velocity (SIS2 SIS_dyn_cgrid.F90:839-870, the in-loop half at :1338-1361, the final half at :1443-1500; this routine is the counting-free, caller-chosen-backoff form both reuse; evp_truncate_final_impl below wraps it with the 0.95 back-off and the mi > m_neglect count).

u_max(face) = +cfl_trunc*areaT(donor for u>0)/(dt_tr*dy_cu(face)), u_min(face) = -cfl_trunc*areaT(donor for u<0)/(dt_tr*dy_cu(face)) – “the flux out of a cell in one slow step cannot exceed cfl_trunc of its volume”. The donor asymmetry is load-bearing: +u at rdb u-face (i,j) (the WEST face of cell (i,j), module docstring §1) drains the WEST cell (i-1,j); -u drains the EAST cell (i,j). v-mirror: +v drains the SOUTH cell (i,j-1), -v drains the NORTH cell (i,j).

dy_cu/dx_cv (NOT the unmasked dyCu/dxCv) are the topography-aware OPEN face widths – zero at a closed/land face, which is why the bound is guarded > 0.0: a closed face gets u_hi = u_lo = 0 (forces ui = 0 there), not a finite spurious bound from dividing by a nonzero length at land.

Loop ranges are copied VERBATIM from evp_u_momentum_impl (u) and evp_v_momentum_impl (v) – physical faces only. Over that range i-1 >= nghost >= 1 (resp. j-1 >= nghost >= 1) always, so no array-edge branch is needed (unlike evp_mi_face_impl, which loops the full 1:nx+1/1:ny+1 and does need one).

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(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) :: backoff
integer, intent(in) :: nghost
integer, intent(in) :: nx_phys
integer, intent(in) :: ny_phys
integer, intent(in) :: nx
integer, intent(in) :: ny

Calls

proc~~evp_truncate_velocity_impl~~CallsGraph proc~evp_truncate_velocity_impl evp_truncate_velocity_impl local local proc~evp_truncate_velocity_impl->local

Called by

proc~~evp_truncate_velocity_impl~~CalledByGraph proc~evp_truncate_velocity_impl evp_truncate_velocity_impl proc~ice_evp_dynamics_impl ice_evp_dynamics_impl proc~ice_evp_dynamics_impl->proc~evp_truncate_velocity_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_hi
integer, private :: i_lo
integer, private :: j
integer, private :: j_hi
integer, private :: j_lo
real(kind=wp), private :: loc_scale
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_velocity_impl(areaT, dy_cu, dx_cv, ui, vi, cfl_trunc, dt_tr, &
                                              backoff, nghost, nx_phys, ny_phys, nx, ny)
      !! PR 36: the shared CFL-clip algebra -- the transport-CFL bound on
      !! the ice velocity (SIS2 `SIS_dyn_cgrid.F90:839-870`, the in-loop
      !! half at `:1338-1361`, the final half at `:1443-1500`; this
      !! routine is the counting-free, caller-chosen-backoff form both
      !! reuse; `evp_truncate_final_impl` below wraps it with the 0.95
      !! back-off and the `mi > m_neglect` count).
      !!
      !! `u_max(face) = +cfl_trunc*areaT(donor for u>0)/(dt_tr*dy_cu(face))`,
      !! `u_min(face) = -cfl_trunc*areaT(donor for u<0)/(dt_tr*dy_cu(face))`
      !! -- "the flux out of a cell in one slow step cannot exceed
      !! cfl_trunc of its volume". The donor asymmetry is load-bearing:
      !! `+u` at rdb u-face `(i,j)` (the WEST face of cell `(i,j)`,
      !! module docstring §1) drains the WEST cell `(i-1,j)`; `-u` drains
      !! the EAST cell `(i,j)`. v-mirror: `+v` drains the SOUTH cell
      !! `(i,j-1)`, `-v` drains the NORTH cell `(i,j)`.
      !!
      !! `dy_cu`/`dx_cv` (NOT the unmasked `dyCu`/`dxCv`) are the
      !! topography-aware OPEN face widths -- zero at a closed/land face,
      !! which is why the bound is guarded `> 0.0`: a closed face gets
      !! `u_hi = u_lo = 0` (forces `ui = 0` there), not a finite spurious
      !! bound from dividing by a nonzero length at land.
      !!
      !! Loop ranges are copied VERBATIM from `evp_u_momentum_impl` (u) and
      !! `evp_v_momentum_impl` (v) -- physical faces only. Over that range
      !! `i-1 >= nghost >= 1` (resp. `j-1 >= nghost >= 1`) always, so no
      !! array-edge branch is needed (unlike `evp_mi_face_impl`, which
      !! loops the full `1:nx+1`/`1:ny+1` and does need one).
      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(inout) :: ui(nx + 1, ny), vi(nx, ny + 1)
      real(wp), intent(in) :: cfl_trunc, dt_tr, backoff
      integer :: i, j, i_lo, i_hi, j_lo, j_hi
      real(wp) :: u_hi, u_lo, v_hi, v_lo, loc_scale

      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)
         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 = backoff*loc_scale*areaT(i - 1, j)
            u_lo = -backoff*loc_scale*areaT(i, j)
         end if
         if (ui(i, j) > u_hi) then
            ui(i, j) = u_hi
         else if (ui(i, j) < u_lo) then
            ui(i, j) = u_lo
         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)
         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 = backoff*loc_scale*areaT(i, j - 1)
            v_lo = -backoff*loc_scale*areaT(i, j)
         end if
         if (vi(i, j) > v_hi) then
            vi(i, j) = v_hi
         else if (vi(i, j) < v_lo) then
            vi(i, j) = v_lo
         end if
      end do
   end subroutine evp_truncate_velocity_impl