rdb_ice_thermo_driver Module

Bridges ice_thermo_columns (PR 3a) into the ocean-coupling seam: runs the column, then reduces h2o_ocn_to_ice/h2o_ice_to_ocn/heat_to_ocn over categories into the per-cell heat_flux_diag/m_melt_diag and the net-melt salt contribution added into salt_flux_diag. Q_heat = sum_cat(heat_to_ocn)/dt_therm - fb [W/m^2, +down] m_net = sum_cat(h2o_ocn_to_ice - h2o_ice_to_ocn) [kg/m^2, +freeze] salt += m_net*(s_surf - ICE_BULK_SALINITY)/dt_therm (ADD, not set) sw_thru_diag = sum_cat(sw_thru) [W/m^2, +down] PR 31: sw_thru (the shortwave penetrating the ice to the water) is now reduced to the per-cell sw_thru_diag and coupled to the ocean by ice_ocean_sw_flux (rdb_ice_ocean_coupler). It is DELIBERATELY kept OUT of the heat_flux_diag sum above — heat_flux_diag carries only the NON-shortwave heat; the coupler delivers the shortwave via a separate path (the q_sw component, or a direct Q_heat add) so the energy reaches Q_heat exactly once in either component mode.

Ordering contract (driver-enforced, see rdb_driver.F90): this module must run AFTER ice_frazil_uptake in the thermo-cadence block. ice_frazil_uptake_impl OVERWRITES salt_flux_diag at loop top (unconditional zero, then a gated write) — this module’s reduction kernel ADDS its net-melt term on top of that write, never zeroing salt_flux_diag itself. heat_flux_diag/m_melt_diag ARE zeroed unconditionally here (this module owns them outright).

fb double-count guard: the column already folds fb into bmelt (rdb_ice_column TRAP #3) and reports heat_to_ocn/ h2o_ice_to_ocn net of that. The only place fb re-enters the energy accounting is the Q_heat formula above — never add it anywhere else.

Reads the sst/s_surf/tfw sample seam filled by rdb_ice_basal_flux%ice_compute_basal_flux earlier in the same thermo-cadence block (one sample, reused — see that module’s docstring).

PR 4a multicat dispatch: ncat == 1 runs the EXISTING ice_thermo_driver_reduce_impl byte-for-byte UNCHANGED (bit-identity contract). ncat > 1 additionally fills ice%fb_part_sum — the fb-charged ice-cover-fraction snapshot — in a small DC kernel BEFORE ice_thermo_columns runs (using the SAME entry gate the column itself uses, m_ice(i,j,c) > ICE_RHO_ICE*H_VANISHED, evaluated PRE-column so a category that melts out entirely this step is still counted in the weight it was actually charged fb under — counting POST-column would leak part*fb*dt_therm of energy from the books for exactly that category), then dispatches to ice_thermo_driver_reduce_multicat_impl, which part-weights the per-category sums and subtracts fb_part_sum*fb (rather than the bare fb the ncat==1 path subtracts) from the heat diag.

PR 26 snowfall: when ice%has_snowfall, ice_snow_part_ocn_ fill_impl runs (same PRE-column-snapshot contract and reasoning as ice_fb_part_sum_fill_impl — a category that melts out this window must still be counted in the cover it actually caught snow under) for BOTH ncat==1 and ncat>1 (unlike fb_part_sum, which is ncat>1-only: at ncat==1 the ice-free share is a binary 0/1, not a part-weighted sum, so there is no “skip at ncat==1” case here). ice%atm_fprec is threaded into ice_thermo_columns unconditionally (harmless zeros when has_snowfall is false). The two reduce impls above are NOT touched by PR 26 — the ocean-bound share is delivered separately by rdb_ice_snow%ice_snowfall_ocean_share, called by the driver AFTER this step (mandated order, rdb_driver.F90).

PR 27 snow-ice flooding: ice%snow_ice is threaded into ice_thermo_columns as do_snow_ice unconditionally (harmless .false. when the knob is off); the flood is entirely column-internal (SIS2’s ice_resize_SIS2, no ocean mass/heat/salt exchange — see rdb_ice_mass%ice_snow_ice_flood’s docstring) and therefore does NOT enter the Q_heat/m_net/salt reduction above; ice%snow_to_ice is filled but not yet reduced/coupled (same “filled, not consumed” contract as sw_thru).


Uses

  • module~~rdb_ice_thermo_driver~~UsesGraph module~rdb_ice_thermo_driver rdb_ice_thermo_driver module~rdb_constants rdb_constants module~rdb_ice_thermo_driver->module~rdb_constants module~rdb_eos rdb_eos module~rdb_ice_thermo_driver->module~rdb_eos module~rdb_grid rdb_grid module~rdb_ice_thermo_driver->module~rdb_grid module~rdb_ice_column rdb_ice_column module~rdb_ice_thermo_driver->module~rdb_ice_column module~rdb_ice_state rdb_ice_state module~rdb_ice_thermo_driver->module~rdb_ice_state module~rdb_multilayer_state rdb_multilayer_state module~rdb_ice_thermo_driver->module~rdb_multilayer_state pic_types pic_types module~rdb_constants->pic_types module~rdb_eos->module~rdb_constants module~rdb_eos->module~rdb_grid module~rdb_grid->module~rdb_constants module~rdb_ice_column->module~rdb_constants module~rdb_ice_enthalpy rdb_ice_enthalpy module~rdb_ice_column->module~rdb_ice_enthalpy module~rdb_ice_mass rdb_ice_mass module~rdb_ice_column->module~rdb_ice_mass module~rdb_ice_optics rdb_ice_optics module~rdb_ice_column->module~rdb_ice_optics module~rdb_ice_state->module~rdb_constants module~rdb_ice_state->module~rdb_grid module~rdb_ice_state->module~rdb_ice_column iso_fortran_env iso_fortran_env module~rdb_ice_state->iso_fortran_env module~rdb_ice_state->module~rdb_ice_enthalpy module~rdb_mem_report rdb_mem_report module~rdb_ice_state->module~rdb_mem_report module~rdb_multilayer_state->module~rdb_constants module~rdb_multilayer_state->module~rdb_grid module~rdb_multilayer_state->iso_fortran_env module~rdb_efp rdb_efp module~rdb_multilayer_state->module~rdb_efp module~rdb_error_ring rdb_error_ring module~rdb_multilayer_state->module~rdb_error_ring module~rdb_multilayer_state->module~rdb_mem_report module~rdb_tracer rdb_tracer module~rdb_multilayer_state->module~rdb_tracer pic_logger pic_logger module~rdb_multilayer_state->pic_logger module~rdb_efp->iso_fortran_env ieee_arithmetic ieee_arithmetic module~rdb_efp->ieee_arithmetic module~rdb_error_ring->pic_logger module~rdb_ice_enthalpy->module~rdb_constants module~rdb_ice_mass->module~rdb_constants module~rdb_ice_mass->module~rdb_ice_enthalpy module~rdb_ice_optics->module~rdb_constants module~rdb_ice_optics->module~rdb_ice_enthalpy module~rdb_mem_report->module~rdb_constants module~rdb_mem_report->iso_fortran_env module~rdb_mem_report->pic_logger pic_strings pic_strings module~rdb_mem_report->pic_strings module~rdb_tracer->module~rdb_constants module~rdb_tracer->module~rdb_grid module~rdb_tracer->iso_fortran_env module~rdb_tracer->module~rdb_mem_report

Used by

  • module~~rdb_ice_thermo_driver~~UsedByGraph module~rdb_ice_thermo_driver rdb_ice_thermo_driver module~rdb_ocean_engine rdb_ocean_engine module~rdb_ocean_engine->module~rdb_ice_thermo_driver module~rdb_driver rdb_driver module~rdb_driver->module~rdb_ocean_engine module~rdb_handle rdb_handle module~rdb_handle->module~rdb_ocean_engine module~rdb_ocean_api rdb_ocean_api module~rdb_ocean_api->module~rdb_ocean_engine module~rdb_ocean_api->module~rdb_handle

Subroutines

public pure subroutine ice_thermo_driver_step(grid, eos, ms, ice, dt_therm)

Outer shim (outer-shim + flat-impl pattern): ms/eos are accepted for call-site parity with the other thermo-cadence kernels (ice_compute_basal_flux, ice_frazil_uptake) even though this step reads its ocean-surface sample from the ice%sst_seam/ssurf_seam/tfw_seam scratch (filled by ice_compute_basal_flux earlier in the same window) rather than re-deriving it from ms/eos directly — eos is unused here but kept in the signature so a future revision that DOES need a fresh EOS evaluation does not have to change every call site.

Arguments

Type IntentOptional Attributes Name
type(hgrid_t), intent(in) :: grid
type(eos_t), intent(in) :: eos
type(multilayer_state_t), intent(in) :: ms
type(ocean_sea_ice_t), intent(inout) :: ice
real(kind=wp), intent(in) :: dt_therm

Effective thermo timestep (s) — ocean_dyn_t%therm_dt(dt).

private pure subroutine ice_fb_part_sum_fill_impl(wet_mask, part_size, m_ice, fb_part_sum, nghost, ncat, nx, ny)

Fill fb_part_sum(i,j) = Σ_c part_size(i,j,c) restricted to categories passing the column’s OWN entry gate (m_ice(i,j,c) > ICE_RHO_ICE*H_VANISHED) — MUST run BEFORE ice_thermo_columns mutates m_ice (module docstring). Device kernel over PHYSICAL cells, inner if/serial do cat — never a masked do concurrent header.

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: wet_mask(nx,ny)
real(kind=wp), intent(in) :: part_size(nx,ny,0:ncat)
real(kind=wp), intent(in) :: m_ice(nx,ny,ncat)
real(kind=wp), intent(inout) :: fb_part_sum(nx,ny)
integer, intent(in) :: nghost
integer, intent(in) :: ncat
integer, intent(in) :: nx
integer, intent(in) :: ny

private pure subroutine ice_snow_part_ocn_fill_impl(wet_mask, part_size, m_ice, snow_part_ocn, nghost, ncat, nx, ny)

PR 26: fill snow_part_ocn(i,j) — the ice-FREE share of the cell that a uniform snowfall lands on with no ice underneath — under the SAME PRE-column-snapshot contract as ice_fb_part_sum_fill_ impl (module docstring, this module docstring’s PR-26 paragraph): MUST run BEFORE ice_thermo_columns mutates m_ice, using the column’s OWN entry gate (m_ice(i,j,c) > ICE_RHO_ICE*H_VANISHED).

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: wet_mask(nx,ny)
real(kind=wp), intent(in) :: part_size(nx,ny,0:ncat)
real(kind=wp), intent(in) :: m_ice(nx,ny,ncat)
real(kind=wp), intent(inout) :: snow_part_ocn(nx,ny)
integer, intent(in) :: nghost
integer, intent(in) :: ncat
integer, intent(in) :: nx
integer, intent(in) :: ny

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).

Read more…

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

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

ncat>1 SIS2 ITD-mode reduce (module docstring). Same zero/gate/ ordering contract as ice_thermo_driver_reduce_impl — the only change is PART-WEIGHTING the per-category sums (the column’s h2o_*/heat_to_ocn/sw_thru outputs are per unit ICE-COVERED area in this mode, so a per-cell total needs the part_size weight) and subtracting fb_part_sum*fb instead of the bare fb the ncat==1 path subtracts (fb is a per-cell flux; fb_part_sum is the fraction of the cell it was actually charged against — see the module docstring and rdb_ice_state%fb_part_sum).

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: wet_mask(nx,ny)
real(kind=wp), intent(in) :: part_size(nx,ny,0:ncat)
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(in) :: fb_part_sum(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