ice_thermo_driver_reduce_impl Subroutine

private pure subroutine ice_thermo_driver_reduce_impl(wet_mask, h2o_ocn_to_ice, h2o_ice_to_ocn, heat_to_ocn, sw_thru, ssurf_seam, fb, heat_flux_diag, sw_thru_diag, m_melt_diag, salt_flux_diag, dt_therm, nghost, ncat, nx, ny)

Device kernel over PHYSICAL cells (ghosts excluded). Reduces the per-category column outputs into the per-cell coupling diags. heat_flux_diag/sw_thru_diag/m_melt_diag are zeroed unconditionally at loop top (this module owns them outright); salt_flux_diag is NOT zeroed — ice_frazil_uptake (which runs first this window, see the module docstring) already zeroed + wrote it, and this kernel ADDS its net-melt contribution on top, gated on wet to avoid land noise (an unwetted cell adds exactly 0).

PR 31: sw_thru_diag(i,j) = Σ_cat sw_thru(i,j,cat) (W/m^2, +down) — the lumped ncat==1 reduction (no part-weight, matching heat_flux_diag). It is written to its OWN field, NEVER folded into heat_flux_diag: heat_flux_diag stays the non-shortwave heat share, and ice_ocean_sw_flux delivers sw_thru_diag to Q_heat exactly once (see that routine for the no-double-count argument across both component modes).

ICE-FREE CELLS ARE A CLEAN NO-OP: on an ice-free wet cell the column zeroes all its per-cat outputs (h2o_*/heat_to_ocn = 0), and ice_compute_basal_flux gates fb = 0 there (no ice base ⇒ no basal flux — the same ice-presence threshold the column uses). So heat_flux_diag = 0/dt - 0 = 0, m_melt_diag = 0, and the net-melt salt add is 0*(...) = 0. The whole open-ocean surface therefore sees Q_heat = Q_salt = 0 from the ice path, as physics requires.

Decl-order: all integer dims declared before the explicit-shape arrays that use them. Inner if/serial do cat — never a masked do concurrent header.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: wet_mask(nx,ny)
real(kind=wp), intent(in) :: h2o_ocn_to_ice(nx,ny,ncat)
real(kind=wp), intent(in) :: h2o_ice_to_ocn(nx,ny,ncat)
real(kind=wp), intent(in) :: heat_to_ocn(nx,ny,ncat)
real(kind=wp), intent(in) :: sw_thru(nx,ny,ncat)
real(kind=wp), intent(in) :: ssurf_seam(nx,ny)
real(kind=wp), intent(in) :: fb(nx,ny)
real(kind=wp), intent(inout) :: heat_flux_diag(nx,ny)
real(kind=wp), intent(inout) :: sw_thru_diag(nx,ny)
real(kind=wp), intent(inout) :: m_melt_diag(nx,ny)
real(kind=wp), intent(inout) :: salt_flux_diag(nx,ny)
real(kind=wp), intent(in) :: dt_therm
integer, intent(in) :: nghost
integer, intent(in) :: ncat
integer, intent(in) :: nx
integer, intent(in) :: ny

Calls

proc~~ice_thermo_driver_reduce_impl~~CallsGraph proc~ice_thermo_driver_reduce_impl ice_thermo_driver_reduce_impl local local proc~ice_thermo_driver_reduce_impl->local

Called by

proc~~ice_thermo_driver_reduce_impl~~CalledByGraph proc~ice_thermo_driver_reduce_impl ice_thermo_driver_reduce_impl proc~ice_thermo_driver_step ice_thermo_driver_step proc~ice_thermo_driver_step->proc~ice_thermo_driver_reduce_impl proc~engine_step_ice engine_step_ice proc~engine_step_ice->proc~ice_thermo_driver_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 proc~driver_run driver_run proc~driver_run->proc~driver_run_ocean

Variables

Type Visibility Attributes Name Initial
integer, private :: cat
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 :: m_net
real(kind=wp), private :: sum_heat
real(kind=wp), private :: sum_ice
real(kind=wp), private :: sum_ocn
real(kind=wp), private :: sum_sw

Source Code

   pure subroutine ice_thermo_driver_reduce_impl(wet_mask, h2o_ocn_to_ice, h2o_ice_to_ocn, &
                                                 heat_to_ocn, sw_thru, ssurf_seam, fb, &
                                                 heat_flux_diag, sw_thru_diag, m_melt_diag, &
                                                 salt_flux_diag, &
                                                 dt_therm, nghost, ncat, nx, ny)
      !! Device kernel over PHYSICAL cells (ghosts excluded). Reduces the
      !! per-category column outputs into the per-cell coupling diags.
      !! `heat_flux_diag`/`sw_thru_diag`/`m_melt_diag` are zeroed
      !! unconditionally at loop top (this module owns them outright);
      !! `salt_flux_diag` is NOT zeroed — `ice_frazil_uptake` (which runs
      !! first this window, see the module docstring) already zeroed +
      !! wrote it, and this kernel ADDS its net-melt contribution on top,
      !! gated on wet to avoid land noise (an unwetted cell adds exactly 0).
      !!
      !! PR 31: `sw_thru_diag(i,j) = Σ_cat sw_thru(i,j,cat)` (W/m^2, +down)
      !! — the lumped ncat==1 reduction (no part-weight, matching
      !! `heat_flux_diag`). It is written to its OWN field, NEVER folded
      !! into `heat_flux_diag`: `heat_flux_diag` stays the non-shortwave
      !! heat share, and `ice_ocean_sw_flux` delivers `sw_thru_diag` to
      !! `Q_heat` exactly once (see that routine for the no-double-count
      !! argument across both component modes).
      !!
      !! ICE-FREE CELLS ARE A CLEAN NO-OP: on an ice-free wet cell the column
      !! zeroes all its per-cat outputs (h2o_*/heat_to_ocn = 0), and
      !! `ice_compute_basal_flux` gates `fb = 0` there (no ice base ⇒ no basal
      !! flux — the same ice-presence threshold the column uses). So
      !! `heat_flux_diag = 0/dt - 0 = 0`, `m_melt_diag = 0`, and the net-melt
      !! salt add is `0*(...) = 0`. The whole open-ocean surface therefore sees
      !! Q_heat = Q_salt = 0 from the ice path, as physics requires.
      !!
      !! Decl-order: all integer dims declared before the explicit-shape
      !! arrays that use them. Inner `if`/serial `do cat` — never a
      !! masked `do concurrent` header.
      integer, intent(in) :: nghost, ncat, nx, ny
      real(wp), intent(in) :: wet_mask(nx, ny)
      real(wp), intent(in) :: h2o_ocn_to_ice(nx, ny, ncat)
      real(wp), intent(in) :: h2o_ice_to_ocn(nx, ny, ncat)
      real(wp), intent(in) :: heat_to_ocn(nx, ny, ncat)
      real(wp), intent(in) :: sw_thru(nx, ny, ncat)
      real(wp), intent(in) :: ssurf_seam(nx, ny)
      real(wp), intent(in) :: fb(nx, ny)
      real(wp), intent(inout) :: heat_flux_diag(nx, ny)
      real(wp), intent(inout) :: sw_thru_diag(nx, ny)
      real(wp), intent(inout) :: m_melt_diag(nx, ny)
      real(wp), intent(inout) :: salt_flux_diag(nx, ny)
      real(wp), intent(in) :: dt_therm

      integer :: i, j, cat, i_lo, i_hi, j_lo, j_hi
      real(wp) :: sum_ocn, sum_ice, sum_heat, sum_sw, m_net

      i_lo = nghost + 1
      i_hi = nx - nghost
      j_lo = nghost + 1
      j_hi = ny - nghost

      do concurrent(j=j_lo:j_hi, i=i_lo:i_hi) &
         local(cat, sum_ocn, sum_ice, sum_heat, sum_sw, m_net)
         heat_flux_diag(i, j) = 0.0_wp
         sw_thru_diag(i, j) = 0.0_wp
         m_melt_diag(i, j) = 0.0_wp
         if (wet_mask(i, j) > 0.5_wp) then
            sum_ocn = 0.0_wp
            sum_ice = 0.0_wp
            sum_heat = 0.0_wp
            sum_sw = 0.0_wp
            do cat = 1, ncat
               sum_ocn = sum_ocn + h2o_ocn_to_ice(i, j, cat)
               sum_ice = sum_ice + h2o_ice_to_ocn(i, j, cat)
               sum_heat = sum_heat + heat_to_ocn(i, j, cat)
               sum_sw = sum_sw + sw_thru(i, j, cat)
            end do
            m_net = sum_ocn - sum_ice
            m_melt_diag(i, j) = sum_ice - sum_ocn
            heat_flux_diag(i, j) = sum_heat/dt_therm - fb(i, j)
            sw_thru_diag(i, j) = sum_sw
            salt_flux_diag(i, j) = salt_flux_diag(i, j) &
                                   + m_net*(ssurf_seam(i, j) - ICE_BULK_SALINITY)/dt_therm
         end if
      end do
   end subroutine ice_thermo_driver_reduce_impl