diff --git a/config_src/drivers/nuopc_cap/mom_cap.F90 b/config_src/drivers/nuopc_cap/mom_cap.F90
index 8d04ba19c7..c86a225b5b 100644
--- a/config_src/drivers/nuopc_cap/mom_cap.F90
+++ b/config_src/drivers/nuopc_cap/mom_cap.F90
@@ -940,6 +940,7 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc)
call fld_list_add(fldsToOcn_num, fldsToOcn, "Sa_pslv" , "will provide")
call fld_list_add(fldsToOcn_num, fldsToOcn, "Foxx_rofl" , "will provide") !-> liquid runoff
call fld_list_add(fldsToOcn_num, fldsToOcn, "Foxx_rofi" , "will provide") !-> ice runoff
+ call fld_list_add(fldsToOcn_num, fldsToOcn, "Foxx_rofs" , "will provide") !-> submarine melt
if (cesm_coupled) then
call fld_list_add(fldsToOcn_num, fldsToOcn, "Forr_rofl_glc" , "will provide") !-> liquid glc runoff
call fld_list_add(fldsToOcn_num, fldsToOcn, "Forr_rofi_glc" , "will provide") !-> frozen glc runoff
@@ -1391,6 +1392,7 @@ subroutine InitializeRealize(gcomp, importState, exportState, clock, rc)
call MOM_RealizeFields(importState, fldsToOcn_num, fldsToOcn, "Ocn import", &
ice_ocean_boundary=Ice_ocean_boundary, mesh=Emesh, rc=rc)
if (ChkErr(rc,__LINE__,u_FILE_u)) return
+ call allocate_optional_iob_fields(importState, Ice_ocean_boundary, isc, iec, jsc, jec)
call MOM_RealizeFields(exportState, fldsFrOcn_num, fldsFrOcn, "Ocn export", mesh=Emesh, rc=rc)
if (ChkErr(rc,__LINE__,u_FILE_u)) return
@@ -1682,6 +1684,7 @@ subroutine InitializeRealize(gcomp, importState, exportState, clock, rc)
call MOM_RealizeFields(importState, fldsToOcn_num, fldsToOcn, "Ocn import", &
ice_ocean_boundary=Ice_ocean_boundary, grid=gridIn, rc=rc)
if (ChkErr(rc,__LINE__,u_FILE_u)) return
+ call allocate_optional_iob_fields(importState, Ice_ocean_boundary, isc, iec, jsc, jec)
call MOM_RealizeFields(exportState, fldsFrOcn_num, fldsFrOcn, "Ocn export", grid=gridOut, rc=rc)
if (ChkErr(rc,__LINE__,u_FILE_u)) return
@@ -2523,6 +2526,24 @@ subroutine State_SetScalar(value, scalar_id, State, mytask, scalar_name, scalar_
end subroutine State_SetScalar
+!> Allocate ice_ocean_boundary fields whose presence depends on whether the coupler
+!! actually connects the corresponding field, rather than on a compile-time flag.
+subroutine allocate_optional_iob_fields(importState, ice_ocean_boundary, isc, iec, jsc, jec)
+ type(ESMF_State) , intent(in) :: importState !< Import ESMF_State object.
+ type(ice_ocean_boundary_type), intent(inout) :: ice_ocean_boundary !< Fields from the mediator.
+ integer , intent(in) :: isc !< Start i-index of the computational domain.
+ integer , intent(in) :: iec !< End i-index of the computational domain.
+ integer , intent(in) :: jsc !< Start j-index of the computational domain.
+ integer , intent(in) :: jec !< End j-index of the computational domain.
+ character(len=*), parameter :: subname = '(MOM_cap:allocate_optional_iob_fields)'
+
+ if (NUOPC_IsConnected(importState, fieldName='Foxx_rofs')) then
+ if (is_root_pe()) write(stdout,*) subname // " Allocating ice_ocean_boundary%srunoff"
+ allocate(ice_ocean_boundary%srunoff(isc:iec,jsc:jec), source=0.0)
+ endif
+
+end subroutine allocate_optional_iob_fields
+
!> Realize the import and export fields using either a grid or a mesh.
subroutine MOM_RealizeFields(state, nfields, field_defs, tag, ice_ocean_boundary, grid, mesh, rc)
type(ESMF_State) , intent(inout) :: state !< ESMF_State object for
@@ -3082,6 +3103,14 @@ end subroutine shr_log_setLogUnit
!!
|
!!
!!
+!! | Foxx_rofs |
+!! kg m-2 s-1 |
+!! runoff |
+!! mass flux of submarine melt. Only allocated in Ice_ocean_boundary if
+!! advertised by the coupler. |
+!! |
+!!
+!!
!! | Forr_rofl_glc |
!! kg m-2 s-1 |
!! runoff |
diff --git a/config_src/drivers/nuopc_cap/mom_cap_methods.F90 b/config_src/drivers/nuopc_cap/mom_cap_methods.F90
index 1accf7472a..47bf52818c 100644
--- a/config_src/drivers/nuopc_cap/mom_cap_methods.F90
+++ b/config_src/drivers/nuopc_cap/mom_cap_methods.F90
@@ -239,6 +239,13 @@ subroutine mom_import(ocean_public, ocean_grid, importState, ice_ocean_boundary,
isc, iec, jsc, jec, ice_ocean_boundary%frunoff, areacor=med2mod_areacor, rc=rc)
if (ChkErr(rc,__LINE__,u_FILE_u)) return
+ ! submarine melt. This is not preset to 0.0 since it is only allocated if connected.
+ if ( associated(ice_ocean_boundary%srunoff) ) then
+ call state_getimport(importState, 'Foxx_rofs', &
+ isc, iec, jsc, jec, ice_ocean_boundary%srunoff, areacor=med2mod_areacor, rc=rc)
+ if (ChkErr(rc,__LINE__,u_FILE_u)) return
+ endif
+
! liquid glc runoff
if ( associated(ice_ocean_boundary%lrunoff_glc) ) then
ice_ocean_boundary%lrunoff_glc (:,:) = 0._ESMF_KIND_R8
diff --git a/config_src/drivers/nuopc_cap/mom_ocean_model_nuopc.F90 b/config_src/drivers/nuopc_cap/mom_ocean_model_nuopc.F90
index b8bb87bff3..65b506fc3d 100644
--- a/config_src/drivers/nuopc_cap/mom_ocean_model_nuopc.F90
+++ b/config_src/drivers/nuopc_cap/mom_ocean_model_nuopc.F90
@@ -390,7 +390,7 @@ subroutine ocean_model_init(Ocean_sfc, OS, Time_init, Time_in, gas_fields_ocn, i
do_integrals=.true., gas_fields_ocn=gas_fields_ocn, &
use_meltpot=use_melt_pot, use_marbl_tracers=OS%use_MARBL)
- call surface_forcing_init(Time_in, OS%grid, OS%US, param_file, OS%diag, &
+ call surface_forcing_init(Time_in, OS%grid, OS%GV, OS%US, param_file, OS%diag, &
OS%forcing_CSp, OS%restore_salinity, OS%restore_temp, OS%use_waves)
if (OS%use_ice_shelf) then
diff --git a/config_src/drivers/nuopc_cap/mom_surface_forcing_nuopc.F90 b/config_src/drivers/nuopc_cap/mom_surface_forcing_nuopc.F90
index c83a47d498..3e62abfc44 100644
--- a/config_src/drivers/nuopc_cap/mom_surface_forcing_nuopc.F90
+++ b/config_src/drivers/nuopc_cap/mom_surface_forcing_nuopc.F90
@@ -37,6 +37,7 @@ module MOM_surface_forcing_nuopc
use MOM_spatial_means, only : adjust_area_mean_to_zero
use MOM_unit_scaling, only : unit_scale_type
use MOM_variables, only : surface
+use MOM_verticalGrid, only : verticalGrid_type
use user_revise_forcing, only : user_alter_forcing, user_revise_forcing_init
use user_revise_forcing, only : user_revise_forcing_CS
use iso_fortran_env, only : int64
@@ -86,6 +87,14 @@ module MOM_surface_forcing_nuopc
logical :: use_marbl_tracers !< enables the MARBL tracer package.
logical :: enthalpy_cpl !< Controls if enthalpy terms are provided by the coupler or computed
!! internally.
+ logical :: srunoff_latent_heat !< If true, give submarine melt (srunoff) an effective temperature
+ !! offset by latent_heat_fusion/C_p, following the Gade (1979)
+ !! meltwater mixing line, so that melting the ice consumes latent
+ !! heat. If false, srunoff enters the ocean at the ambient
+ !! temperature.
+ real :: srunoff_depth_max !< The maximum depth over which the mass and heat from coupled
+ !! submarine melt are spread, distributed uniformly by thickness. If 0,
+ !! srunoff is applied entirely within the top layer [H ~> m or kg m-2].
real :: gust_const !< constant unresolved background gustiness for ustar [R L Z T-2 ~> Pa]
logical :: read_gust_2d !< If true, use a 2-dimensional gustiness supplied
!! from an input file.
@@ -166,6 +175,7 @@ module MOM_surface_forcing_nuopc
type, public :: ice_ocean_boundary_type
real, pointer, dimension(:,:) :: lrunoff =>NULL() !< liquid runoff [km m-2 s-1]
real, pointer, dimension(:,:) :: frunoff =>NULL() !< ice runoff [km m-2 s-1]
+ real, pointer, dimension(:,:) :: srunoff =>NULL() !< submarine melt [km m-2 s-1]
real, pointer, dimension(:,:) :: lrunoff_glc =>NULL() !< liquid glc runoff via rof [km m-2 s-1]
real, pointer, dimension(:,:) :: frunoff_glc =>NULL() !< frozen glc runoff via rof [km m-2 s-1]
real, pointer, dimension(:,:) :: u_flux =>NULL() !< i-direction wind stress [Pa]
@@ -329,7 +339,10 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,
call allocate_forcing_type(G, fluxes, water=.true., heat=.true., ustar=.true., &
press=.true., fix_accum_bug=.not.CS%ustar_gustless_bug, &
cfc=CS%use_CFC, marbl=CS%use_marbl_tracers, hevap=CS%enthalpy_cpl, &
- tau_mag=.true., ice_ncat=IOB%ice_ncat)
+ tau_mag=.true., ice_ncat=IOB%ice_ncat, srunoff=associated(IOB%srunoff))
+ fluxes%latent_heat_fusion = CS%latent_heat_fusion
+ fluxes%srunoff_latent_heat = CS%srunoff_latent_heat
+ fluxes%srunoff_depth_max = CS%srunoff_depth_max
call safe_alloc_ptr(fluxes%omega_w2x,isd,ied,jsd,jed)
call safe_alloc_ptr(fluxes%sw_vis_dir,isd,ied,jsd,jed)
call safe_alloc_ptr(fluxes%sw_vis_dif,isd,ied,jsd,jed)
@@ -503,6 +516,15 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,
fluxes%frunoff(i,j) = kg_m2_s_conversion * IOB%frunoff(i-i0,j-j0) * G%mask2dT(i,j)
endif
+ ! submarine melt flux
+ if (associated(IOB%srunoff)) then
+ fluxes%srunoff(i,j) = kg_m2_s_conversion * IOB%srunoff(i-i0,j-j0) * G%mask2dT(i,j)
+ ! Total latent heat extracted by submarine melt (not depth-dependent)
+ if (CS%srunoff_latent_heat) &
+ fluxes%latent_srunoff(i,j) = - G%mask2dT(i,j) * &
+ IOB%srunoff(i-i0,j-j0) * US%W_m2_to_QRZ_T * CS%latent_heat_fusion
+ endif
+
! add liquid glc runoff flux via rof
if (associated(IOB%lrunoff_glc)) then
fluxes%lrunoff_glc(i,j) = kg_m2_s_conversion * IOB%lrunoff_glc(i-i0,j-j0) * G%mask2dT(i,j)
@@ -557,6 +579,8 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,
fluxes%latent_frunoff_glc_diag(i,j) = fluxes%latent_frunoff_glc_diag(i,j) - G%mask2dT(i,j) * &
IOB%frunoff_glc(i-i0,j-j0) * US%W_m2_to_QRZ_T * CS%latent_heat_fusion
endif
+ ! Submarine melt (srunoff) is not included in fluxes%latent since it is depth-dependent not a
+ ! surface flux. The latent heat associated with srunoff is included in latent heat diagnostics
if (associated(IOB%q_flux)) then
fluxes%latent(i,j) = fluxes%latent(i,j) + &
IOB%q_flux(i-i0,j-j0)*US%W_m2_to_QRZ_T*CS%latent_heat_vapor
@@ -670,6 +694,14 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,
G%areaT(i,j)
net_FW2(i,j) = net_FW(i,j) / G%areaT(i,j)
enddo ; enddo
+ ! fluxes%srunoff is only associated when submarine-melt coupling is actually configured,
+ ! unlike the other water fluxes above, so it needs its own guard here.
+ if (associated(fluxes%srunoff)) then
+ do j=js,je ; do i=is,ie
+ net_FW(i,j) = net_FW(i,j) + fluxes%srunoff(i,j) * G%areaT(i,j)
+ net_FW2(i,j) = net_FW(i,j) / G%areaT(i,j)
+ enddo ; enddo
+ endif
if (CS%adjust_net_fresh_water_by_scaling) then
call adjust_area_mean_to_zero(net_FW2, G, fluxes%netFWGlobalScl, unscale=US%RZ_T_to_kg_m2s)
@@ -1149,9 +1181,10 @@ subroutine forcing_save_restart(CS, G, Time, directory, time_stamped, &
end subroutine forcing_save_restart
!> Initialize the surface forcing, including setting parameters and allocating permanent memory.
-subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, restore_salt, restore_temp, use_waves)
+subroutine surface_forcing_init(Time, G, GV, US, param_file, diag, CS, restore_salt, restore_temp, use_waves)
type(time_type), intent(in) :: Time !< The current model time
type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
+ type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time parameters
type(diag_ctrl), target, intent(inout) :: diag !< A structure that is used to regulate
@@ -1216,6 +1249,15 @@ subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, restore_salt,
"The latent heat of fusion.", units="J/kg", default=hlf)
call get_param(param_file, mdl, "LATENT_HEAT_VAPORIZATION", CS%latent_heat_vapor, &
"The latent heat of fusion.", units="J/kg", default=hlv)
+ call get_param(param_file, mdl, "SRUNOFF_LATENT_HEAT", CS%srunoff_latent_heat, &
+ "If true, give submarine melt (srunoff) an effective temperature offset by "//&
+ "LATENT_HEAT_FUSION/C_p, following the Gade (1979) meltwater mixing line, "//&
+ "so that melting the ice consumes latent heat. If false, srunoff enters "// &
+ "the ocean at the ambient temperature.", default=.false.)
+ call get_param(param_file, mdl, "SRUNOFF_DEPTH_MAX", CS%srunoff_depth_max, &
+ "The maximum depth over which the mass and heat from coupled submarine melt are spread, "//&
+ "distributed uniformly by thickness. If 0, srunoff is applied entirely "//&
+ "within the top layer.", units="m", default=0.0, scale=GV%m_to_H)
call get_param(param_file, mdl, "MAX_P_SURF", CS%max_p_surf, &
"The maximum surface pressure that can be exerted by the "//&
"atmosphere and floating sea-ice or ice shelves. This is "//&
@@ -1581,6 +1623,9 @@ subroutine ice_ocn_bnd_type_chksum(id, timestep, iobt)
chks = field_chksum( iobt%fprec ) ; if (root) write(outunit,100) 'iobt%fprec ', chks
chks = field_chksum( iobt%lrunoff ) ; if (root) write(outunit,100) 'iobt%lrunoff ', chks
chks = field_chksum( iobt%frunoff ) ; if (root) write(outunit,100) 'iobt%frunoff ', chks
+ if (associated(iobt%srunoff)) then
+ chks = field_chksum( iobt%srunoff ) ; if (root) write(outunit,100) 'iobt%srunoff ', chks
+ endif
chks = field_chksum( iobt%lrunoff_glc ) ; if (root) write(outunit,100) 'iobt%lrunoff_glc ', chks
chks = field_chksum( iobt%frunoff_glc ) ; if (root) write(outunit,100) 'iobt%frunoff_glc ', chks
chks = field_chksum( iobt%p ) ; if (root) write(outunit,100) 'iobt%p ', chks
diff --git a/src/core/MOM_forcing_type.F90 b/src/core/MOM_forcing_type.F90
index 90a483ec30..9827f3dc05 100644
--- a/src/core/MOM_forcing_type.F90
+++ b/src/core/MOM_forcing_type.F90
@@ -30,7 +30,7 @@ module MOM_forcing_type
#include
-public extractFluxes1d, extractFluxes2d, optics_type
+public extractFluxes1d, extractFluxes2d, optics_type, distribute_srunoff
public MOM_forcing_chksum, MOM_mech_forcing_chksum
public calculateBuoyancyFlux1d, calculateBuoyancyFlux2d, find_ustar
public forcing_accumulate, fluxes_accumulate
@@ -114,7 +114,9 @@ module MOM_forcing_type
latent => NULL(), & !< latent [Q R Z T-1 ~> W m-2] (typically < 0)
sens => NULL(), & !< sensible [Q R Z T-1 ~> W m-2] (typically negative)
seaice_melt_heat => NULL(), & !< sea ice and snow melt or formation [Q R Z T-1 ~> W m-2] (typically negative)
- heat_added => NULL() !< additional heat flux from SST restoring or flux adjustments [Q R Z T-1 ~> W m-2]
+ heat_added => NULL(), & !< additional heat flux from SST restoring or flux adjustments [Q R Z T-1 ~> W m-2]
+ latent_srunoff => NULL() !< latent from submarine melt (srunoff), which can be distributed over depth
+ !! and so is not included in latent (typically < 0) [Q R Z T-1 ~> W m-2]
! components of latent heat fluxes used for diagnostic purposes
real, pointer, dimension(:,:) :: &
@@ -131,6 +133,7 @@ module MOM_forcing_type
vprec => NULL(), & !< virtual liquid precip associated w/ SSS restoring [R Z T-1 ~> kg m-2 s-1]
lrunoff => NULL(), & !< liquid river runoff entering ocean [R Z T-1 ~> kg m-2 s-1]
frunoff => NULL(), & !< frozen river runoff (calving) entering ocean [R Z T-1 ~> kg m-2 s-1]
+ srunoff => NULL(), & !< submarine melt entering ocean [R Z T-1 ~> kg m-2 s-1]
lrunoff_glc => NULL(), & !< liquid river glacier runoff entering ocean [R Z T-1 ~> kg m-2 s-1]
frunoff_glc => NULL(), & !< frozen river glacier runoff entering ocean [R Z T-1 ~> kg m-2 s-1]
seaice_melt => NULL() !< snow/seaice melt (positive) or formation (negative) [R Z T-1 ~> kg m-2 s-1]
@@ -151,6 +154,9 @@ module MOM_forcing_type
heat_content_vprec => NULL(), & !< heat content associated with virtual >0 precip [Q R Z T-1 ~> W m-2]
heat_content_lrunoff => NULL(), & !< heat content associated with liquid runoff [Q R Z T-1 ~> W m-2]
heat_content_frunoff => NULL(), & !< heat content associated with frozen runoff [Q R Z T-1 ~> W m-2]
+ heat_content_srunoff => NULL(), & !< heat content associated with submarine melt [Q R Z T-1 ~> W m-2]
+ heat_content_eff_srunoff => NULL(), & !< effective heat content associated with submarine melt,
+ !! i.e. heat content plus e.g. latent heat [Q R Z T-1 ~> W m-2]
heat_content_lrunoff_glc => NULL(), & !< heat content associated with liquid runoff [Q R Z T-1 ~> W m-2]
heat_content_frunoff_glc => NULL(), & !< heat content associated with frozen runoff [Q R Z T-1 ~> W m-2]
heat_content_massout => NULL(), & !< heat content associated with mass leaving ocean [Q R Z T-1 ~> W m-2]
@@ -225,6 +231,17 @@ module MOM_forcing_type
!! average of salt_left_behind
real :: C_p !< heat capacity of seawater [Q C-1 ~> J kg-1 degC-1].
!! C_p is is the same value as in thermovar_ptrs_type.
+ real :: latent_heat_fusion !< The latent heat of fusion, the same value as
+ !! CS%latent_heat_fusion in surface_forcing_CS [J kg-1].
+ logical :: srunoff_latent_heat = .false. !< If true, give submarine melt (srunoff) an effective
+ !! temperature offset by latent_heat_fusion/C_p, following the Gade
+ !! (1979) meltwater mixing line, so that melting the ice consumes
+ !! latent heat. If false, srunoff enters the ocean at the ambient
+ !! temperature.
+ real :: srunoff_depth_max = 0.0 !< The maximum depth over which the mass and heat from coupled
+ !! submarine melt are spread, distributed uniformly by thickness. If 0,
+ !! srunoff is applied entirely within the top layer. The same value as
+ !! CS%srunoff_depth_max in surface_forcing_CS [H ~> m or kg m-2].
! arrays needed in the some tracer modules, e.g., MOM_CFC_cap
real, pointer, dimension(:,:) :: &
@@ -336,6 +353,7 @@ module MOM_forcing_type
integer :: id_precip = -1, id_vprec = -1
integer :: id_lprec = -1, id_fprec = -1
integer :: id_lrunoff = -1, id_frunoff = -1
+ integer :: id_srunoff = -1
integer :: id_lrunoff_glc = -1, id_frunoff_glc = -1
integer :: id_net_massout = -1, id_net_massin = -1
integer :: id_massout_flux = -1, id_massin_flux = -1
@@ -346,6 +364,7 @@ module MOM_forcing_type
integer :: id_total_precip = -1, id_total_vprec = -1
integer :: id_total_lprec = -1, id_total_fprec = -1
integer :: id_total_lrunoff = -1, id_total_frunoff = -1
+ integer :: id_total_srunoff = -1
integer :: id_total_lrunoff_glc = -1, id_total_frunoff_glc = -1
integer :: id_total_net_massout = -1, id_total_net_massin = -1
integer :: id_total_seaice_melt = -1
@@ -361,9 +380,10 @@ module MOM_forcing_type
integer :: id_sw = -1, id_lw = -1
integer :: id_sw_vis = -1, id_sw_nir = -1
integer :: id_lat_evap = -1, id_lat_frunoff = -1
- integer :: id_lat_frunoff_glc = -1
+ integer :: id_lat_frunoff_glc = -1, id_lat_srunoff = -1
integer :: id_lat = -1, id_lat_fprec = -1
integer :: id_heat_content_lrunoff = -1, id_heat_content_frunoff = -1
+ integer :: id_heat_content_srunoff = -1, id_heat_content_eff_srunoff = -1
integer :: id_heat_content_lrunoff_glc= -1, id_heat_content_frunoff_glc= -1
integer :: id_heat_content_lprec = -1, id_heat_content_fprec = -1
integer :: id_heat_content_cond = -1, id_heat_content_surfwater = -1
@@ -378,9 +398,11 @@ module MOM_forcing_type
integer :: id_total_sens = -1, id_total_LwLatSens = -1
integer :: id_total_sw = -1, id_total_lw = -1
integer :: id_total_lat_evap = -1, id_total_lat_frunoff = -1
+ integer :: id_total_lat_srunoff = -1
integer :: id_total_lat_frunoff_glc = -1
integer :: id_total_lat = -1, id_total_lat_fprec = -1
integer :: id_total_heat_content_lrunoff = -1, id_total_heat_content_frunoff = -1
+ integer :: id_total_heat_content_srunoff = -1, id_total_heat_content_eff_srunoff = -1
integer :: id_total_heat_content_lrunoff_glc= -1, id_total_heat_content_frunoff_glc=-1
integer :: id_total_heat_content_lprec = -1, id_total_heat_content_fprec = -1
integer :: id_total_heat_content_cond = -1, id_total_heat_content_surfwater = -1
@@ -1030,6 +1052,147 @@ subroutine extractFluxes2d(G, GV, US, fluxes, optics, nsw, dt, FluxRescaleDepth,
end subroutine extractFluxes2d
+!> Distribute the mass and (optionally) heat content of submarine melt (srunoff) uniformly by
+!! thickness over the upper fluxes%srunoff_depth_max of a column. If fluxes%srunoff_depth_max is 0,
+!! the distribution depth for a column instead falls back to that column's own top layer thickness,
+!! so that srunoff is deposited entirely within the top layer.
+!! This routine is called from both:
+!! - applyBoundaryFluxesInOut, to update thickness and temperature and dilute salinity.
+!! - applyTracerBoundaryFluxesInOut, to dilute a tracer concentration.
+subroutine distribute_srunoff(G, GV, dt, fluxes, j, h2d, C2d, T2d, C_p, US, &
+ g_Hconv2, cTKE, dSV_dT, dSV_dS, dHeat_total, dTempxPmE_total)
+ type(ocean_grid_type), intent(in) :: G !< Grid structure
+ type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure
+ real, intent(in) :: dt !< Time step [T ~> s]
+ type(forcing), intent(in) :: fluxes !< Surface fluxes, including srunoff
+ integer, intent(in) :: j !< j-index to work on
+ real, dimension(SZI_(G),SZK_(GV)), &
+ intent(inout) :: h2d !< Layer thicknesses [H ~> m or kg m-2]
+ real, dimension(SZI_(G),SZK_(GV)), &
+ intent(inout) :: C2d !< A concentration diluted by srunoff's
+ !! fresh water, e.g. salinity or a tracer's own
+ !! concentration [CU]
+ real, dimension(SZI_(G),SZK_(GV)), &
+ optional, intent(inout) :: T2d !< Layer temperatures [C ~> degC]
+ real, optional, intent(in) :: C_p !< The heat capacity of seawater
+ !! [Q C-1 ~> J kg-1 degC-1]
+ type(unit_scale_type), optional, intent(in) :: US !< A dimensional unit scaling type
+ real, optional, intent(in) :: g_Hconv2 !< A conversion factor for use in the
+ !! TKE calculation.
+ !! [Z3 R2 T-2 H-2 ~> kg2 m-5 s-2 or m s-2]
+ real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
+ optional, intent(inout) :: cTKE !< Turbulent kinetic energy requirement
+ !! to mix forcing through each layer.
+ !! [R Z3 T-2 ~> J m-2]
+ real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
+ optional, intent(in) :: dSV_dT !< Partial derivative of specific
+ !! volume with potential temperature.
+ !! [R-1 C-1 ~> m3 kg-1 degC-1]
+ real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
+ optional, intent(in) :: dSV_dS !< Partial derivative of specific
+ !! volume with salinity.
+ !! [R-1 S-1 ~> m3 kg-1 ppt-1]
+ real, dimension(SZI_(G)), &
+ optional, intent(out) :: dHeat_total !< The total heat added to each
+ !! column by srunoff.
+ !! [Q H ~> J m-2]
+ real, dimension(SZI_(G)), &
+ optional, intent(out) :: dTempxPmE_total !< The total srunoff mass
+ !! added to each column,
+ !! weighted by ambient
+ !! temperature.
+ !! [C H ~> degC m or degC kg m-2]
+
+ ! Local variables
+ real :: dM_tot ! Total mass of srunoff to be distributed this timestep [H ~> m or kg m-2]
+ real :: dM_k ! The portion of dM_tot applied to a layer [H ~> m or kg m-2]
+ real :: dHeat ! The heat added to the column so far, accumulated for diagnostics [Q H ~> J m-2]
+ real :: dTempxPmE ! The ambient-temperature-weighted mass added to the column so far,
+ ! accumulated for diagnostics [C H ~> degC m or degC kg m-2]
+ real :: depth ! The requested depth over which srunoff is distributed [H ~> m or kg m-2]
+ real :: total_h ! The full thickness of the water column [H ~> m or kg m-2]
+ real :: eff_depth ! The depth actually spanned in this column, capped at total_h so that the
+ ! full amount of srunoff is always distributed even in a column shallower
+ ! than depth [H ~> m or kg m-2]
+ real :: h_above ! Cumulative thickness above the current layer, before adding srunoff [H ~> m or kg m-2]
+ real :: h_in_range ! The portion of a layer's thickness within eff_depth [H ~> m or kg m-2]
+ real :: T_eff ! The effective temperature carried by srunoff mass added to a layer [C ~> degC]
+ real :: hOld ! The layer thickness before adding srunoff mass [H ~> m or kg m-2]
+ real :: I_Habs ! The inverse of eff_depth [H-1 ~> m-1 or m2 kg-1]
+ logical :: do_heat ! If true, T2d and C_p are present and get updated
+ logical :: do_energetics ! If true, cTKE and its supporting arguments are also present
+ integer :: i, k, is, ie, nz
+
+ is = G%isc ; ie = G%iec ; nz = GV%ke
+ do_heat = present(T2d) .and. present(C_p)
+ do_energetics = do_heat .and. present(cTKE) .and. present(dSV_dT) .and. present(dSV_dS) &
+ .and. present(g_Hconv2)
+ if (do_heat .and. fluxes%srunoff_latent_heat .and. .not.present(US)) call MOM_error(FATAL, &
+ "distribute_srunoff: US must be present when T2d is present and "//&
+ "fluxes%srunoff_latent_heat is true.")
+
+ do i=is,ie
+ if (present(dHeat_total)) dHeat_total(i) = 0.0
+ if (present(dTempxPmE_total)) dTempxPmE_total(i) = 0.0
+
+ if ((G%mask2dT(i,j) > 0.0) .and. (fluxes%srunoff(i,j) > 0.0)) then
+
+ dM_tot = GV%RZ_to_H * dt * fluxes%srunoff(i,j)
+
+ ! When fluxes%srunoff_depth_max is 0 (the default), fall back to this column's own top layer
+ ! thickness as the distribution depth, so srunoff is deposited within the top layer.
+ depth = fluxes%srunoff_depth_max
+ if (depth <= 0.0) depth = max(h2d(i,1), GV%H_subroundoff)
+
+ ! Cap the depth actually used at the full column thickness, so that a column shallower
+ ! than the requested depth still receives all of dM_tot.
+ ! Note, with the currently implementation, if the column is zero thickness any srunoff
+ ! is just dropped.
+ total_h = 0.0
+ do k=1,nz ; total_h = total_h + h2d(i,k) ; enddo
+ eff_depth = min(depth, total_h)
+ I_Habs = 0.0 ; if (eff_depth > 0.0) I_Habs = 1.0 / eff_depth
+
+ h_above = 0.0
+ dHeat = 0.0
+ dTempxPmE = 0.0
+
+ ! Spread dM_tot over the upper eff_depth of the column, in proportion to how much of each
+ ! layer's thickness falls within that range.
+ do k=1,nz
+ h_in_range = max(0.0, min(h_above + h2d(i,k), eff_depth) - h_above)
+ h_above = h_above + h2d(i,k)
+ if (h_in_range > 0.0) then
+ dM_k = dM_tot * (h_in_range * I_Habs)
+ hOld = h2d(i,k)
+
+ if (do_heat) then
+ T_eff = T2d(i,k)
+ if (fluxes%srunoff_latent_heat) &
+ T_eff = T_eff - fluxes%latent_heat_fusion*US%J_kg_to_Q/C_p
+ dHeat = dHeat + C_p*dM_k*T_eff
+ dTempxPmE = dTempxPmE + dM_k*T2d(i,k)
+
+ if (do_energetics) then
+ ! Calculate the energy required to mix the srunoff added to this layer.
+ cTKE(i,j,k) = cTKE(i,j,k) + 0.5*g_Hconv2*(hOld*dM_k) * &
+ ((T2d(i,k) - T_eff) * dSV_dT(i,j,k) + C2d(i,k) * dSV_dS(i,j,k))
+ endif
+ endif
+
+ h2d(i,k) = hOld + dM_k
+ if (do_heat) T2d(i,k) = (hOld*T2d(i,k) + dM_k*T_eff) / h2d(i,k)
+ C2d(i,k) = (hOld*C2d(i,k)) / h2d(i,k)
+ endif
+ enddo ! k-loop for layer distribution
+
+ if (present(dHeat_total)) dHeat_total(i) = dHeat
+ if (present(dTempxPmE_total)) dTempxPmE_total(i) = dTempxPmE
+
+ endif
+ enddo
+
+end subroutine distribute_srunoff
!> This routine calculates surface buoyancy flux by adding up the heat, FW & salt fluxes.
!! These are actual fluxes, with units of stuff per time. Setting dt=1 in the call to
@@ -1363,6 +1526,9 @@ subroutine MOM_forcing_chksum(mesg, fluxes, G, US, haloshift)
if (associated(fluxes%latent_frunoff_diag)) &
call hchksum(fluxes%latent_frunoff_diag, mesg//" fluxes%latent_frunoff_diag", G%HI, &
haloshift=hshift, unscale=US%QRZ_T_to_W_m2)
+ if (associated(fluxes%latent_srunoff)) &
+ call hchksum(fluxes%latent_srunoff, mesg//" fluxes%latent_srunoff", G%HI, &
+ haloshift=hshift, unscale=US%QRZ_T_to_W_m2)
if (associated(fluxes%latent_frunoff_glc_diag)) &
call hchksum(fluxes%latent_frunoff_glc_diag, mesg//" fluxes%latent_frunoff_glc_diag", G%HI, &
haloshift=hshift, unscale=US%QRZ_T_to_W_m2)
@@ -1402,6 +1568,8 @@ subroutine MOM_forcing_chksum(mesg, fluxes, G, US, haloshift)
call hchksum(fluxes%frunoff, mesg//" fluxes%frunoff", G%HI, haloshift=hshift, unscale=US%RZ_T_to_kg_m2s)
if (associated(fluxes%frunoff_glc)) &
call hchksum(fluxes%frunoff_glc, mesg//" fluxes%frunoff_glc", G%HI, haloshift=hshift, unscale=US%RZ_T_to_kg_m2s)
+ if (associated(fluxes%srunoff)) &
+ call hchksum(fluxes%srunoff, mesg//" fluxes%srunoff", G%HI, haloshift=hshift, unscale=US%RZ_T_to_kg_m2s)
if (associated(fluxes%heat_content_lrunoff)) &
call hchksum(fluxes%heat_content_lrunoff, mesg//" fluxes%heat_content_lrunoff", G%HI, &
haloshift=hshift, unscale=US%QRZ_T_to_W_m2)
@@ -1414,6 +1582,12 @@ subroutine MOM_forcing_chksum(mesg, fluxes, G, US, haloshift)
if (associated(fluxes%heat_content_frunoff_glc)) &
call hchksum(fluxes%heat_content_frunoff_glc, mesg//" fluxes%heat_content_frunoff_glc", G%HI, &
haloshift=hshift, unscale=US%QRZ_T_to_W_m2)
+ if (associated(fluxes%heat_content_srunoff)) &
+ call hchksum(fluxes%heat_content_srunoff, mesg//" fluxes%heat_content_srunoff", G%HI, &
+ haloshift=hshift, unscale=US%QRZ_T_to_W_m2)
+ if (associated(fluxes%heat_content_eff_srunoff)) &
+ call hchksum(fluxes%heat_content_eff_srunoff, mesg//" fluxes%heat_content_eff_srunoff", G%HI, &
+ haloshift=hshift, unscale=US%QRZ_T_to_W_m2)
if (associated(fluxes%heat_content_lprec)) &
call hchksum(fluxes%heat_content_lprec, mesg//" fluxes%heat_content_lprec", G%HI, &
haloshift=hshift, unscale=US%QRZ_T_to_W_m2)
@@ -1517,6 +1691,7 @@ subroutine forcing_SinglePointPrint(fluxes, G, i, j, mesg)
call locMsg(fluxes%latent_fprec_diag,'latent_fprec_diag')
call locMsg(fluxes%latent_frunoff_diag,'latent_frunoff_diag')
call locMsg(fluxes%latent_frunoff_glc_diag,'latent_frunoff_glc_diag')
+ call locMsg(fluxes%latent_srunoff,'latent_srunoff')
call locMsg(fluxes%sens,'sens')
call locMsg(fluxes%evap,'evap')
call locMsg(fluxes%lprec,'lprec')
@@ -1532,10 +1707,13 @@ subroutine forcing_SinglePointPrint(fluxes, G, i, j, mesg)
call locMsg(fluxes%lrunoff_glc,'lrunoff_glc')
call locMsg(fluxes%frunoff,'frunoff')
call locMsg(fluxes%frunoff_glc,'frunoff_glc')
+ call locMsg(fluxes%srunoff,'srunoff')
call locMsg(fluxes%heat_content_lrunoff,'heat_content_lrunoff')
call locMsg(fluxes%heat_content_lrunoff_glc,'heat_content_lrunoff_glc')
call locMsg(fluxes%heat_content_frunoff,'heat_content_frunoff')
call locMsg(fluxes%heat_content_frunoff_glc,'heat_content_frunoff_glc')
+ call locMsg(fluxes%heat_content_srunoff,'heat_content_srunoff')
+ call locMsg(fluxes%heat_content_eff_srunoff,'heat_content_eff_srunoff')
call locMsg(fluxes%heat_content_lprec,'heat_content_lprec')
call locMsg(fluxes%heat_content_fprec,'heat_content_fprec')
call locMsg(fluxes%heat_content_vprec,'heat_content_vprec')
@@ -1653,7 +1831,7 @@ subroutine register_forcing_type_diags(Time, diag, US, use_temperature, handles,
! surface mass flux maps
handles%id_prcme = register_diag_field('ocean_model', 'PRCmE', diag%axesT1, Time, &
- 'Net surface water flux (precip+melt+lrunoff+ice calving-evap)', &
+ 'Net surface water flux (precip+melt+lrunoff+srunoff+ice calving-evap)', &
'kg m-2 s-1', conversion=US%RZ_T_to_kg_m2s, &
standard_name='water_flux_into_sea_water', cmor_field_name='wfo', &
cmor_standard_name='water_flux_into_sea_water',cmor_long_name='Water Flux Into Sea Water')
@@ -1709,6 +1887,10 @@ subroutine register_forcing_type_diags(Time, diag, US, use_temperature, handles,
cmor_standard_name='water_flux_into_sea_water_from_rivers', &
cmor_long_name='Water Flux into Sea Water From Rivers')
+ handles%id_srunoff = register_diag_field('ocean_model', 'srunoff', diag%axesT1, Time, &
+ 'Submarine melt into ocean', &
+ units='kg m-2 s-1', conversion=US%RZ_T_to_kg_m2s)
+
if (present(use_glc_runoff)) then
handles%id_frunoff_glc = register_diag_field('ocean_model', 'frunoff_glc', diag%axesT1, Time, &
'Frozen glacier runoff (calving) and iceberg melt into ocean', &
@@ -1730,11 +1912,13 @@ subroutine register_forcing_type_diags(Time, diag, US, use_temperature, handles,
'kg m-2 s-1', conversion=US%RZ_T_to_kg_m2s)
handles%id_massout_flux = register_diag_field('ocean_model', 'massout_flux', diag%axesT1, Time, &
- 'Net mass flux of freshwater out of the ocean (used in the boundary flux calculation)', &
+ 'Net mass flux of freshwater out of the ocean (used in the boundary flux calculation). '//&
+ 'Excludes coupled submarine melt (srunoff).', &
'kg m-2', conversion=diag%GV%H_to_kg_m2)
handles%id_massin_flux = register_diag_field('ocean_model', 'massin_flux', diag%axesT1, Time, &
- 'Net mass flux of freshwater into the ocean (used in boundary flux calculation)', &
+ 'Net mass flux of freshwater into the ocean (used in boundary flux calculation). '//&
+ 'Excludes coupled submarine melt (srunoff).', &
'kg m-2', conversion=diag%GV%H_to_kg_m2)
!=========================================================================
@@ -1803,6 +1987,10 @@ subroutine register_forcing_type_diags(Time, diag, US, use_temperature, handles,
cmor_standard_name='water_flux_into_sea_water_from_rivers_area_integrated', &
cmor_long_name='Water Flux into Sea Water From Rivers Area Integrated')
+ handles%id_total_srunoff = register_scalar_field('ocean_model', 'total_srunoff', Time, diag, &
+ long_name='Area integrated submarine melt into ocean', &
+ units='kg s-1', conversion=US%RZL2_to_kg*US%s_to_T)
+
if (present(use_glc_runoff)) then
handles%id_total_frunoff_glc = register_scalar_field('ocean_model', 'total_frunoff_glc', Time, diag, &
long_name='Area integrated frozen glacier runoff (calving) & iceberg melt into ocean', &
@@ -1873,6 +2061,14 @@ subroutine register_forcing_type_diags(Time, diag, US, use_temperature, handles,
'W m-2', conversion=US%QRZ_T_to_W_m2, &
standard_name='temperature_flux_due_to_runoff_expressed_as_heat_flux_into_sea_water')
+ handles%id_heat_content_srunoff = register_diag_field('ocean_model', 'heat_content_srunoff', &
+ diag%axesT1, Time, 'Heat content (relative to 0C) of submarine melt into ocean', &
+ 'W m-2', conversion=US%QRZ_T_to_W_m2)
+
+ handles%id_heat_content_eff_srunoff = register_diag_field('ocean_model', 'heat_content_eff_srunoff', &
+ diag%axesT1, Time, 'Effective heat content (relative to 0C) of submarine melt into ocean, '//&
+ 'i.e. heat content plus e.g. latent heat', 'W m-2', conversion=US%QRZ_T_to_W_m2)
+
if (present(use_glc_runoff)) then
handles%id_heat_content_frunoff_glc = register_diag_field('ocean_model', 'heat_content_frunoff_glc', &
diag%axesT1, Time, 'Heat content (relative to 0C) of solid glacier runoff into ocean', &
@@ -1927,7 +2123,8 @@ subroutine register_forcing_type_diags(Time, diag, US, use_temperature, handles,
cmor_long_name='Heat Content (relative to 0degC) of Water Leaving Ocean via Evaporation and Ice Formation')
handles%id_heat_content_massin = register_diag_field('ocean_model', 'heat_content_massin', &
- diag%axesT1, Time,'Heat content (relative to 0degC) of net mass entering ocean ocean',&
+ diag%axesT1, Time,'Heat content (relative to 0degC) of net mass entering ocean ocean. '//&
+ 'Excludes coupled submarine melt (srunoff).',&
'W m-2', conversion=US%QRZ_T_to_W_m2)
handles%id_net_heat_coupler = register_diag_field('ocean_model', 'net_heat_coupler', &
@@ -1992,6 +2189,10 @@ subroutine register_forcing_type_diags(Time, diag, US, use_temperature, handles,
'Latent heat flux into ocean due to melting of frozen glacier runoff', 'W m-2', conversion=US%QRZ_T_to_W_m2)
endif
+ handles%id_lat_srunoff = register_diag_field('ocean_model', 'latent_srunoff', diag%axesT1, Time, &
+ 'Latent heat flux into ocean due to melting of coupled submarine melt (srunoff)', &
+ 'W m-2', conversion=US%QRZ_T_to_W_m2)
+
handles%id_sens = register_diag_field('ocean_model', 'sensible', diag%axesT1, Time, &
'Sensible heat flux into ocean', 'W m-2', conversion=US%QRZ_T_to_W_m2, &
standard_name='surface_downward_sensible_heat_flux', &
@@ -2031,6 +2232,16 @@ subroutine register_forcing_type_diags(Time, diag, US, use_temperature, handles,
cmor_long_name= &
'Temperature Flux due to Runoff Expressed as Heat Flux into Sea Water Area Integrated')
+ handles%id_total_heat_content_srunoff = register_scalar_field('ocean_model', &
+ 'total_heat_content_srunoff', Time, diag, &
+ long_name='Area integrated heat content (relative to 0C) of submarine melt', &
+ units='W', conversion=US%QRZ_T_to_W_m2*US%L_to_m**2)
+
+ handles%id_total_heat_content_eff_srunoff = register_scalar_field('ocean_model', &
+ 'total_heat_content_eff_srunoff', Time, diag, &
+ long_name='Area integrated effective heat content (relative to 0C) of submarine melt', &
+ units='W', conversion=US%QRZ_T_to_W_m2*US%L_to_m**2)
+
if (present(use_glc_runoff)) then
handles%id_total_heat_content_frunoff_glc = register_scalar_field('ocean_model', &
'total_heat_content_frunoff_glc', Time, diag, &
@@ -2167,6 +2378,11 @@ subroutine register_forcing_type_diags(Time, diag, US, use_temperature, handles,
units='W', conversion=US%QRZ_T_to_W_m2*US%L_to_m**2) ! todo: update cmor names
endif
+ handles%id_total_lat_srunoff = register_scalar_field('ocean_model', &
+ 'total_lat_srunoff', Time, diag, &
+ long_name='Area integrated latent heat flux due to melting coupled submarine melt (srunoff)', &
+ units='W', conversion=US%QRZ_T_to_W_m2*US%L_to_m**2)
+
handles%id_total_sens = register_scalar_field('ocean_model', &
'total_sens', Time, diag, &
long_name='Area integrated downward sensible heat flux', &
@@ -2446,6 +2662,19 @@ subroutine fluxes_accumulate(flux_tmp, fluxes, G, wt2, forces)
fluxes%salt_flux(i,j) = wt1*fluxes%salt_flux(i,j) + wt2*flux_tmp%salt_flux(i,j)
enddo ; enddo
+ ! Unlike the other water-group and heat fields above, fluxes%srunoff and fluxes%latent_srunoff
+ ! are only associated when submarine-melt coupling is actually configured, so they need their
+ ! own guards here.
+ if (associated(fluxes%srunoff) .and. associated(flux_tmp%srunoff)) then
+ do j=js,je ; do i=is,ie
+ fluxes%srunoff(i,j) = wt1*fluxes%srunoff(i,j) + wt2*flux_tmp%srunoff(i,j)
+ enddo ; enddo
+ endif
+ if (associated(fluxes%latent_srunoff) .and. associated(flux_tmp%latent_srunoff)) then
+ do j=js,je ; do i=is,ie
+ fluxes%latent_srunoff(i,j) = wt1*fluxes%latent_srunoff(i,j) + wt2*flux_tmp%latent_srunoff(i,j)
+ enddo ; enddo
+ endif
if (associated(fluxes%salt_flux_in) .and. associated(flux_tmp%salt_flux_in)) then
do j=js,je ; do i=is,ie
fluxes%salt_flux_in(i,j) = wt1*fluxes%salt_flux_in(i,j) + wt2*flux_tmp%salt_flux_in(i,j)
@@ -2504,6 +2733,17 @@ subroutine fluxes_accumulate(flux_tmp, fluxes, G, wt2, forces)
fluxes%heat_content_frunoff(i,j) = wt1*fluxes%heat_content_frunoff(i,j) + wt2*flux_tmp%heat_content_frunoff(i,j)
enddo ; enddo
endif
+ if (associated(fluxes%heat_content_srunoff) .and. associated(flux_tmp%heat_content_srunoff)) then
+ do j=js,je ; do i=is,ie
+ fluxes%heat_content_srunoff(i,j) = wt1*fluxes%heat_content_srunoff(i,j) + wt2*flux_tmp%heat_content_srunoff(i,j)
+ enddo ; enddo
+ endif
+ if (associated(fluxes%heat_content_eff_srunoff) .and. associated(flux_tmp%heat_content_eff_srunoff)) then
+ do j=js,je ; do i=is,ie
+ fluxes%heat_content_eff_srunoff(i,j) = wt1*fluxes%heat_content_eff_srunoff(i,j) + &
+ wt2*flux_tmp%heat_content_eff_srunoff(i,j)
+ enddo ; enddo
+ endif
if (associated(fluxes%heat_content_lrunoff_glc) .and. associated(flux_tmp%heat_content_lrunoff_glc)) then
do j=js,je ; do i=is,ie
fluxes%heat_content_lrunoff_glc(i,j) = wt1*fluxes%heat_content_lrunoff_glc(i,j) + &
@@ -2687,6 +2927,9 @@ subroutine get_net_mass_forcing(fluxes, G, US, net_mass_src)
if (associated(fluxes%frunoff)) then ; do j=js,je ; do i=is,ie
net_mass_src(i,j) = net_mass_src(i,j) + fluxes%frunoff(i,j)
enddo ; enddo ; endif
+ if (associated(fluxes%srunoff)) then ; do j=js,je ; do i=is,ie
+ net_mass_src(i,j) = net_mass_src(i,j) + fluxes%srunoff(i,j)
+ enddo ; enddo ; endif
if (associated(fluxes%lrunoff_glc)) then ; do j=js,je ; do i=is,ie
net_mass_src(i,j) = net_mass_src(i,j) + fluxes%lrunoff_glc(i,j)
enddo ; enddo ; endif
@@ -2854,6 +3097,7 @@ subroutine forcing_diagnostics(fluxes_in, sfc_state, G_in, US, time_end, diag, h
if (associated(fluxes%evap)) res(i,j) = res(i,j) + fluxes%evap(i,j)
if (associated(fluxes%lrunoff)) res(i,j) = res(i,j) + fluxes%lrunoff(i,j)
if (associated(fluxes%frunoff)) res(i,j) = res(i,j) + fluxes%frunoff(i,j)
+ if (associated(fluxes%srunoff)) res(i,j) = res(i,j) + fluxes%srunoff(i,j)
if (associated(fluxes%lrunoff_glc)) res(i,j) = res(i,j) + fluxes%lrunoff_glc(i,j)
if (associated(fluxes%frunoff_glc)) res(i,j) = res(i,j) + fluxes%frunoff_glc(i,j)
if (associated(fluxes%vprec)) res(i,j) = res(i,j) + fluxes%vprec(i,j)
@@ -2902,6 +3146,7 @@ subroutine forcing_diagnostics(fluxes_in, sfc_state, G_in, US, time_end, diag, h
if (associated(fluxes%fprec)) res(i,j) = res(i,j) + fluxes%fprec(i,j)
if (associated(fluxes%lrunoff)) res(i,j) = res(i,j) + fluxes%lrunoff(i,j)
if (associated(fluxes%frunoff)) res(i,j) = res(i,j) + fluxes%frunoff(i,j)
+ if (associated(fluxes%srunoff)) res(i,j) = res(i,j) + fluxes%srunoff(i,j)
if (associated(fluxes%lrunoff_glc)) res(i,j) = res(i,j) + fluxes%lrunoff_glc(i,j)
if (associated(fluxes%frunoff_glc)) res(i,j) = res(i,j) + fluxes%frunoff_glc(i,j)
@@ -3023,6 +3268,14 @@ subroutine forcing_diagnostics(fluxes_in, sfc_state, G_in, US, time_end, diag, h
endif
endif
+ if (associated(fluxes%srunoff)) then
+ if (handles%id_srunoff > 0) call post_data(handles%id_srunoff, fluxes%srunoff, diag)
+ if (handles%id_total_srunoff > 0) then
+ total_mass_flux = global_area_integral(fluxes%srunoff, G, tmp_scale=US%RZ_T_to_kg_m2s)
+ call post_data(handles%id_total_srunoff, total_mass_flux, diag)
+ endif
+ endif
+
if (associated(fluxes%seaice_melt)) then
if (handles%id_seaice_melt > 0) call post_data(handles%id_seaice_melt, fluxes%seaice_melt, diag)
if (handles%id_total_seaice_melt > 0) then
@@ -3061,6 +3314,20 @@ subroutine forcing_diagnostics(fluxes_in, sfc_state, G_in, US, time_end, diag, h
call post_data(handles%id_total_heat_content_frunoff_glc, total_heat_flux, diag)
endif
+ if ((handles%id_heat_content_srunoff > 0) .and. associated(fluxes%heat_content_srunoff)) &
+ call post_data(handles%id_heat_content_srunoff, fluxes%heat_content_srunoff, diag)
+ if ((handles%id_total_heat_content_srunoff > 0) .and. associated(fluxes%heat_content_srunoff)) then
+ total_heat_flux = global_area_integral(fluxes%heat_content_srunoff, G, tmp_scale=US%QRZ_T_to_W_m2)
+ call post_data(handles%id_total_heat_content_srunoff, total_heat_flux, diag)
+ endif
+
+ if ((handles%id_heat_content_eff_srunoff > 0) .and. associated(fluxes%heat_content_eff_srunoff)) &
+ call post_data(handles%id_heat_content_eff_srunoff, fluxes%heat_content_eff_srunoff, diag)
+ if ((handles%id_total_heat_content_eff_srunoff > 0) .and. associated(fluxes%heat_content_eff_srunoff)) then
+ total_heat_flux = global_area_integral(fluxes%heat_content_eff_srunoff, G, tmp_scale=US%QRZ_T_to_W_m2)
+ call post_data(handles%id_total_heat_content_eff_srunoff, total_heat_flux, diag)
+ endif
+
if ((handles%id_heat_content_lprec > 0) .and. associated(fluxes%heat_content_lprec)) &
call post_data(handles%id_heat_content_lprec, fluxes%heat_content_lprec, diag)
if ((handles%id_total_heat_content_lprec > 0) .and. associated(fluxes%heat_content_lprec)) then
@@ -3116,6 +3383,7 @@ subroutine forcing_diagnostics(fluxes_in, sfc_state, G_in, US, time_end, diag, h
res(i,j) = 0.0
if (associated(fluxes%LW)) res(i,j) = res(i,j) + fluxes%lw(i,j)
if (associated(fluxes%latent)) res(i,j) = res(i,j) + fluxes%latent(i,j)
+ if (associated(fluxes%latent_srunoff)) res(i,j) = res(i,j) + fluxes%latent_srunoff(i,j)
if (associated(fluxes%sens)) res(i,j) = res(i,j) + fluxes%sens(i,j)
if (associated(fluxes%SW)) res(i,j) = res(i,j) + fluxes%sw(i,j)
if (associated(fluxes%seaice_melt_heat)) res(i,j) = res(i,j) + fluxes%seaice_melt_heat(i,j)
@@ -3145,6 +3413,9 @@ subroutine forcing_diagnostics(fluxes_in, sfc_state, G_in, US, time_end, diag, h
res(i,j) = res(i,j) + fluxes%heat_content_lrunoff(i,j)
if (associated(fluxes%heat_content_frunoff)) &
res(i,j) = res(i,j) + fluxes%heat_content_frunoff(i,j)
+ ! The effective heat content of submarine melt includes its latent contribution.
+ if (associated(fluxes%heat_content_eff_srunoff)) &
+ res(i,j) = res(i,j) + fluxes%heat_content_eff_srunoff(i,j)
if (associated(fluxes%heat_content_lrunoff_glc)) &
res(i,j) = res(i,j) + fluxes%heat_content_lrunoff_glc(i,j)
if (associated(fluxes%heat_content_frunoff_glc)) &
@@ -3183,6 +3454,7 @@ subroutine forcing_diagnostics(fluxes_in, sfc_state, G_in, US, time_end, diag, h
res(i,j) = 0.0
if (associated(fluxes%heat_content_lrunoff)) res(i,j) = res(i,j) + fluxes%heat_content_lrunoff(i,j)
if (associated(fluxes%heat_content_frunoff)) res(i,j) = res(i,j) + fluxes%heat_content_frunoff(i,j)
+ if (associated(fluxes%heat_content_srunoff)) res(i,j) = res(i,j) + fluxes%heat_content_srunoff(i,j)
if (associated(fluxes%heat_content_lrunoff_glc)) res(i,j) = res(i,j) + fluxes%heat_content_lrunoff_glc(i,j)
if (associated(fluxes%heat_content_frunoff_glc)) res(i,j) = res(i,j) + fluxes%heat_content_frunoff_glc(i,j)
if (associated(fluxes%heat_content_lprec)) res(i,j) = res(i,j) + fluxes%heat_content_lprec(i,j)
@@ -3208,6 +3480,7 @@ subroutine forcing_diagnostics(fluxes_in, sfc_state, G_in, US, time_end, diag, h
res(i,j) = 0.0
if (associated(fluxes%heat_content_lrunoff)) res(i,j) = res(i,j) + fluxes%heat_content_lrunoff(i,j)
if (associated(fluxes%heat_content_frunoff)) res(i,j) = res(i,j) + fluxes%heat_content_frunoff(i,j)
+ if (associated(fluxes%heat_content_srunoff)) res(i,j) = res(i,j) + fluxes%heat_content_srunoff(i,j)
if (associated(fluxes%heat_content_lrunoff_glc)) res(i,j) = res(i,j) + fluxes%heat_content_lrunoff_glc(i,j)
if (associated(fluxes%heat_content_frunoff_glc)) res(i,j) = res(i,j) + fluxes%heat_content_frunoff_glc(i,j)
enddo ; enddo
@@ -3230,6 +3503,9 @@ subroutine forcing_diagnostics(fluxes_in, sfc_state, G_in, US, time_end, diag, h
do j=js,je ; do i=is,ie
res(i,j) = (fluxes%lw(i,j) + fluxes%latent(i,j)) + fluxes%sens(i,j)
enddo ; enddo
+ if (associated(fluxes%latent_srunoff)) then ; do j=js,je ; do i=is,ie
+ res(i,j) = res(i,j) + fluxes%latent_srunoff(i,j)
+ enddo ; enddo ; endif
call post_data(handles%id_LwLatSens, res, diag)
endif
@@ -3238,6 +3514,9 @@ subroutine forcing_diagnostics(fluxes_in, sfc_state, G_in, US, time_end, diag, h
do j=js,je ; do i=is,ie
res(i,j) = (fluxes%lw(i,j) + fluxes%latent(i,j)) + fluxes%sens(i,j)
enddo ; enddo
+ if (associated(fluxes%latent_srunoff)) then ; do j=js,je ; do i=is,ie
+ res(i,j) = res(i,j) + fluxes%latent_srunoff(i,j)
+ enddo ; enddo ; endif
total_heat_flux = global_area_integral(res, G, tmp_scale=US%QRZ_T_to_W_m2)
call post_data(handles%id_total_LwLatSens, total_heat_flux, diag)
endif
@@ -3247,6 +3526,9 @@ subroutine forcing_diagnostics(fluxes_in, sfc_state, G_in, US, time_end, diag, h
do j=js,je ; do i=is,ie
res(i,j) = ((fluxes%lw(i,j) + fluxes%latent(i,j)) + fluxes%sens(i,j))
enddo ; enddo
+ if (associated(fluxes%latent_srunoff)) then ; do j=js,je ; do i=is,ie
+ res(i,j) = res(i,j) + fluxes%latent_srunoff(i,j)
+ enddo ; enddo ; endif
ave_heat_flux = global_area_mean(res, G, tmp_scale=US%QRZ_T_to_W_m2)
call post_data(handles%id_LwLatSens_ga, ave_heat_flux, diag)
endif
@@ -3283,16 +3565,33 @@ subroutine forcing_diagnostics(fluxes_in, sfc_state, G_in, US, time_end, diag, h
call post_data(handles%id_lw_ga, ave_heat_flux, diag)
endif
- if ((handles%id_lat > 0) .and. associated(fluxes%latent)) then
- call post_data(handles%id_lat, fluxes%latent, diag)
- endif
- if ((handles%id_total_lat > 0) .and. associated(fluxes%latent)) then
- total_heat_flux = global_area_integral(fluxes%latent, G, tmp_scale=US%QRZ_T_to_W_m2)
- call post_data(handles%id_total_lat, total_heat_flux, diag)
- endif
- if ((handles%id_lat_ga > 0) .and. associated(fluxes%latent)) then
- ave_heat_flux = global_area_mean(fluxes%latent, G, tmp_scale=US%QRZ_T_to_W_m2)
- call post_data(handles%id_lat_ga, ave_heat_flux, diag)
+ if (associated(fluxes%latent_srunoff)) then
+ if ((handles%id_lat > 0) .or. (handles%id_total_lat > 0) .or. (handles%id_lat_ga > 0)) then
+ ! fluxes%latent is guaranteed to be associated here too, since latent_srunoff
+ ! is only allocated when heat is true, which is also required for fluxes%latent.
+ do j=js,je ; do i=is,ie
+ res(i,j) = fluxes%latent(i,j) + fluxes%latent_srunoff(i,j)
+ enddo ; enddo
+ if (handles%id_lat > 0) call post_data(handles%id_lat, res, diag)
+ if (handles%id_total_lat > 0) then
+ total_heat_flux = global_area_integral(res, G, tmp_scale=US%QRZ_T_to_W_m2)
+ call post_data(handles%id_total_lat, total_heat_flux, diag)
+ endif
+ if (handles%id_lat_ga > 0) then
+ ave_heat_flux = global_area_mean(res, G, tmp_scale=US%QRZ_T_to_W_m2)
+ call post_data(handles%id_lat_ga, ave_heat_flux, diag)
+ endif
+ endif
+ elseif (associated(fluxes%latent)) then
+ if (handles%id_lat > 0) call post_data(handles%id_lat, fluxes%latent, diag)
+ if (handles%id_total_lat > 0) then
+ total_heat_flux = global_area_integral(fluxes%latent, G, tmp_scale=US%QRZ_T_to_W_m2)
+ call post_data(handles%id_total_lat, total_heat_flux, diag)
+ endif
+ if (handles%id_lat_ga > 0) then
+ ave_heat_flux = global_area_mean(fluxes%latent, G, tmp_scale=US%QRZ_T_to_W_m2)
+ call post_data(handles%id_lat_ga, ave_heat_flux, diag)
+ endif
endif
if ((handles%id_lat_evap > 0) .and. associated(fluxes%latent_evap_diag)) then
@@ -3327,6 +3626,14 @@ subroutine forcing_diagnostics(fluxes_in, sfc_state, G_in, US, time_end, diag, h
call post_data(handles%id_total_lat_frunoff_glc, total_heat_flux, diag)
endif
+ if ((handles%id_lat_srunoff > 0) .and. associated(fluxes%latent_srunoff)) then
+ call post_data(handles%id_lat_srunoff, fluxes%latent_srunoff, diag)
+ endif
+ if (handles%id_total_lat_srunoff > 0 .and. associated(fluxes%latent_srunoff)) then
+ total_heat_flux = global_area_integral(fluxes%latent_srunoff, G, tmp_scale=US%QRZ_T_to_W_m2)
+ call post_data(handles%id_total_lat_srunoff, total_heat_flux, diag)
+ endif
+
if ((handles%id_sens > 0) .and. associated(fluxes%sens)) then
call post_data(handles%id_sens, fluxes%sens, diag)
endif
@@ -3455,7 +3762,7 @@ end subroutine forcing_diagnostics
subroutine allocate_forcing_by_group(G, fluxes, water, heat, ustar, press, &
shelf, iceberg, salt, fix_accum_bug, fix_excess_salt_accum_bug, &
cfc, marbl, waves, shelf_sfc_accumulation, lamult, hevap, &
- ice_ncat, tau_mag)
+ ice_ncat, tau_mag, srunoff)
type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
type(forcing), intent(inout) :: fluxes !< A structure containing thermodynamic forcing fields
logical, optional, intent(in) :: water !< If present and true, allocate water fluxes
@@ -3483,6 +3790,8 @@ subroutine allocate_forcing_by_group(G, fluxes, water, heat, ustar, press, &
!! via coupler.
integer, optional, intent(in) :: ice_ncat !< number of ice categories
logical, optional, intent(in) :: tau_mag !< If present and true, allocate tau_mag and related fields
+ logical, optional, intent(in) :: srunoff !< If present and true, allocate fields for
+ !! submarine melt
! Local variables
integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB
@@ -3512,6 +3821,7 @@ subroutine allocate_forcing_by_group(G, fluxes, water, heat, ustar, press, &
call myAlloc(fluxes%vprec,isd,ied,jsd,jed, water)
call myAlloc(fluxes%lrunoff,isd,ied,jsd,jed, water)
call myAlloc(fluxes%frunoff,isd,ied,jsd,jed, water)
+ call myAlloc(fluxes%srunoff,isd,ied,jsd,jed, srunoff)
call myAlloc(fluxes%lrunoff_glc,isd,ied,jsd,jed, water)
call myAlloc(fluxes%frunoff_glc,isd,ied,jsd,jed, water)
call myAlloc(fluxes%seaice_melt,isd,ied,jsd,jed, water)
@@ -3543,6 +3853,12 @@ subroutine allocate_forcing_by_group(G, fluxes, water, heat, ustar, press, &
call myAlloc(fluxes%heat_content_massin,isd,ied,jsd,jed, enthalpy_mom)
endif ; endif
+ if (present(heat) .and. present(srunoff)) then ; if (heat .and. srunoff) then
+ call myAlloc(fluxes%latent_srunoff,isd,ied,jsd,jed, .true.)
+ call myAlloc(fluxes%heat_content_srunoff,isd,ied,jsd,jed, .true.)
+ call myAlloc(fluxes%heat_content_eff_srunoff,isd,ied,jsd,jed, .true.)
+ endif ; endif
+
call myAlloc(fluxes%p_surf,isd,ied,jsd,jed, press)
! These fields should only be allocated if ice shelf is enabled.
@@ -3629,6 +3945,15 @@ subroutine allocate_forcing_by_ref(fluxes_ref, G, fluxes, turns)
call myAlloc(fluxes%buoy, G%isd, G%ied, G%jsd, G%jed, &
associated(fluxes_ref%buoy))
+ call myAlloc(fluxes%srunoff, G%isd, G%ied, G%jsd, G%jed, &
+ associated(fluxes_ref%srunoff))
+ call myAlloc(fluxes%heat_content_srunoff, G%isd, G%ied, G%jsd, G%jed, &
+ associated(fluxes_ref%heat_content_srunoff))
+ call myAlloc(fluxes%heat_content_eff_srunoff, G%isd, G%ied, G%jsd, G%jed, &
+ associated(fluxes_ref%heat_content_eff_srunoff))
+ call myAlloc(fluxes%latent_srunoff, G%isd, G%ied, G%jsd, G%jed, &
+ associated(fluxes_ref%latent_srunoff))
+
call myAlloc(fluxes%BBL_tidal_dis, G%isd, G%ied, G%jsd, G%jed, &
associated(fluxes_ref%BBL_tidal_dis))
call myAlloc(fluxes%ustar_tidal, G%isd, G%ied, G%jsd, G%jed, &
@@ -3842,10 +4167,13 @@ subroutine deallocate_forcing_type(fluxes)
if (associated(fluxes%latent_fprec_diag)) deallocate(fluxes%latent_fprec_diag)
if (associated(fluxes%latent_frunoff_diag)) deallocate(fluxes%latent_frunoff_diag)
if (associated(fluxes%latent_frunoff_glc_diag)) deallocate(fluxes%latent_frunoff_glc_diag)
+ if (associated(fluxes%latent_srunoff)) deallocate(fluxes%latent_srunoff)
if (associated(fluxes%sens)) deallocate(fluxes%sens)
if (associated(fluxes%heat_added)) deallocate(fluxes%heat_added)
if (associated(fluxes%heat_content_lrunoff)) deallocate(fluxes%heat_content_lrunoff)
if (associated(fluxes%heat_content_frunoff)) deallocate(fluxes%heat_content_frunoff)
+ if (associated(fluxes%heat_content_srunoff)) deallocate(fluxes%heat_content_srunoff)
+ if (associated(fluxes%heat_content_eff_srunoff)) deallocate(fluxes%heat_content_eff_srunoff)
if (associated(fluxes%heat_content_lrunoff_glc)) deallocate(fluxes%heat_content_lrunoff_glc)
if (associated(fluxes%heat_content_frunoff_glc)) deallocate(fluxes%heat_content_frunoff_glc)
if (associated(fluxes%heat_content_lprec)) deallocate(fluxes%heat_content_lprec)
@@ -3860,6 +4188,7 @@ subroutine deallocate_forcing_type(fluxes)
if (associated(fluxes%vprec)) deallocate(fluxes%vprec)
if (associated(fluxes%lrunoff)) deallocate(fluxes%lrunoff)
if (associated(fluxes%frunoff)) deallocate(fluxes%frunoff)
+ if (associated(fluxes%srunoff)) deallocate(fluxes%srunoff)
if (associated(fluxes%lrunoff_glc)) deallocate(fluxes%lrunoff_glc)
if (associated(fluxes%frunoff_glc)) deallocate(fluxes%frunoff_glc)
if (associated(fluxes%seaice_melt)) deallocate(fluxes%seaice_melt)
@@ -4008,6 +4337,15 @@ subroutine rotate_forcing(fluxes_in, fluxes, turns)
endif
! The following fields are handled by drivers rather than control flags.
+ if (associated(fluxes_in%srunoff)) &
+ call rotate_array(fluxes_in%srunoff, turns, fluxes%srunoff)
+ if (associated(fluxes_in%heat_content_srunoff)) &
+ call rotate_array(fluxes_in%heat_content_srunoff, turns, fluxes%heat_content_srunoff)
+ if (associated(fluxes_in%heat_content_eff_srunoff)) &
+ call rotate_array(fluxes_in%heat_content_eff_srunoff, turns, fluxes%heat_content_eff_srunoff)
+ if (associated(fluxes_in%latent_srunoff)) &
+ call rotate_array(fluxes_in%latent_srunoff, turns, fluxes%latent_srunoff)
+
if (associated(fluxes_in%sw_vis_dir)) &
call rotate_array(fluxes_in%sw_vis_dir, turns, fluxes%sw_vis_dir)
if (associated(fluxes_in%sw_vis_dif)) &
@@ -4052,6 +4390,9 @@ subroutine rotate_forcing(fluxes_in, fluxes, turns)
fluxes%fluxes_used = fluxes_in%fluxes_used
fluxes%dt_buoy_accum = fluxes_in%dt_buoy_accum
fluxes%C_p = fluxes_in%C_p
+ fluxes%latent_heat_fusion = fluxes_in%latent_heat_fusion
+ fluxes%srunoff_latent_heat = fluxes_in%srunoff_latent_heat
+ fluxes%srunoff_depth_max = fluxes_in%srunoff_depth_max
! NOTE: gustless_accum_bug is set during allocation
fluxes%num_msg = fluxes_in%num_msg
@@ -4224,6 +4565,10 @@ subroutine homogenize_forcing(fluxes, G, GV, US)
call homogenize_field_t(fluxes%vprec, G, tmp_scale=US%RZ_T_to_kg_m2s)
call homogenize_field_t(fluxes%lrunoff, G, tmp_scale=US%RZ_T_to_kg_m2s)
call homogenize_field_t(fluxes%frunoff, G, tmp_scale=US%RZ_T_to_kg_m2s)
+ ! Unlike the other water-group fields above, fluxes%srunoff is only associated when
+ ! submarine-melt coupling is actually configured, so it needs its own guard here.
+ if (associated(fluxes%srunoff)) &
+ call homogenize_field_t(fluxes%srunoff, G, tmp_scale=US%RZ_T_to_kg_m2s)
call homogenize_field_t(fluxes%lrunoff_glc, G, tmp_scale=US%RZ_T_to_kg_m2s)
call homogenize_field_t(fluxes%frunoff_glc, G, tmp_scale=US%RZ_T_to_kg_m2s)
call homogenize_field_t(fluxes%seaice_melt, G, tmp_scale=US%RZ_T_to_kg_m2s)
@@ -4247,6 +4592,11 @@ subroutine homogenize_forcing(fluxes, G, GV, US)
call homogenize_field_t(fluxes%latent_frunoff_glc_diag, G, tmp_scale=US%QRZ_T_to_W_m2)
endif
+ ! Unlike the other heat fluxes above, fluxes%latent_srunoff is only associated when
+ ! submarine-melt coupling is actually configured, so it needs its own guard here.
+ if (associated(fluxes%latent_srunoff)) &
+ call homogenize_field_t(fluxes%latent_srunoff, G, tmp_scale=US%QRZ_T_to_W_m2)
+
if (do_salt) call homogenize_field_t(fluxes%salt_flux, G, tmp_scale=US%RZ_T_to_kg_m2s)
if (do_heat .and. do_water) then
@@ -4256,6 +4606,13 @@ subroutine homogenize_forcing(fluxes, G, GV, US)
call homogenize_field_t(fluxes%heat_content_vprec, G, tmp_scale=US%QRZ_T_to_W_m2)
call homogenize_field_t(fluxes%heat_content_lrunoff, G, tmp_scale=US%QRZ_T_to_W_m2)
call homogenize_field_t(fluxes%heat_content_frunoff, G, tmp_scale=US%QRZ_T_to_W_m2)
+ ! Unlike the other heat_content fields above, fluxes%heat_content_srunoff and
+ ! fluxes%heat_content_eff_srunoff are only associated when submarine-melt coupling is
+ ! actually configured, so they need their own guards here.
+ if (associated(fluxes%heat_content_srunoff)) &
+ call homogenize_field_t(fluxes%heat_content_srunoff, G, tmp_scale=US%QRZ_T_to_W_m2)
+ if (associated(fluxes%heat_content_eff_srunoff)) &
+ call homogenize_field_t(fluxes%heat_content_eff_srunoff, G, tmp_scale=US%QRZ_T_to_W_m2)
call homogenize_field_t(fluxes%heat_content_lrunoff_glc, G, tmp_scale=US%QRZ_T_to_W_m2)
call homogenize_field_t(fluxes%heat_content_frunoff_glc, G, tmp_scale=US%QRZ_T_to_W_m2)
call homogenize_field_t(fluxes%heat_content_massout, G, tmp_scale=US%QRZ_T_to_W_m2)
diff --git a/src/diagnostics/MOM_sum_output.F90 b/src/diagnostics/MOM_sum_output.F90
index fba33fe0ee..c241aff51c 100644
--- a/src/diagnostics/MOM_sum_output.F90
+++ b/src/diagnostics/MOM_sum_output.F90
@@ -1061,6 +1061,13 @@ subroutine accumulate_net_input(fluxes, sfc_state, tv, dt, G, US, CS)
(((fluxes%lprec(i,j) + fluxes%vprec(i,j)) + (fluxes%lrunoff(i,j) + fluxes%lrunoff_glc(i,j))) + &
(fluxes%fprec(i,j) + (fluxes%frunoff(i,j) + fluxes%frunoff_glc(i,j)))))
enddo ; enddo
+ ! fluxes%srunoff is only associated when submarine-melt coupling is actually configured,
+ ! unlike the other water-group fields above, so it needs its own guard here.
+ if (associated(fluxes%srunoff)) then
+ do j=js,je ; do i=is,ie
+ FW_in(i,j) = FW_in(i,j) + dt*G%areaT(i,j)*fluxes%srunoff(i,j)
+ enddo ; enddo
+ endif
else
call MOM_error(WARNING, &
"accumulate_net_input called with associated evap field, but no precip field.")
@@ -1102,6 +1109,14 @@ subroutine accumulate_net_input(fluxes, sfc_state, tv, dt, G, US, CS)
fluxes%heat_content_cond(i,j) + fluxes%heat_content_fprec(i,j) + &
fluxes%heat_content_lrunoff(i,j) + fluxes%heat_content_frunoff(i,j))
enddo ; enddo
+ ! fluxes%heat_content_eff_srunoff is only associated when submarine-melt coupling is actually
+ ! configured, unlike the other heat_content fields above, so it needs its own guard here.
+ ! The effective heat content of submarine melt includes its latent contribution.
+ if (associated(fluxes%heat_content_eff_srunoff)) then
+ do j=js,je ; do i=is,ie
+ heat_in(i,j) = heat_in(i,j) + dt * G%areaT(i,j) * fluxes%heat_content_eff_srunoff(i,j)
+ enddo ; enddo
+ endif
elseif (associated(tv%TempxPmE)) then
do j=js,je ; do i=is,ie
heat_in(i,j) = heat_in(i,j) + (tv%C_p * G%areaT(i,j)) * tv%TempxPmE(i,j)
@@ -1112,6 +1127,15 @@ subroutine accumulate_net_input(fluxes, sfc_state, tv, dt, G, US, CS)
enddo ; enddo
endif
+ ! Without coupler enthalpies, only the sensible heat content of submarine melt is included above,
+ ! so add the rest of the heat it applies (e.g. the latent heat of melting).
+ if (.not. associated(fluxes%heat_content_evap)) then
+ if (associated(fluxes%heat_content_eff_srunoff)) then ; do j=js,je ; do i=is,ie
+ heat_in(i,j) = heat_in(i,j) + dt * G%areaT(i,j) * &
+ (fluxes%heat_content_eff_srunoff(i,j) - fluxes%heat_content_srunoff(i,j))
+ enddo ; enddo ; endif
+ endif
+
! The following heat sources may or may not be used.
if (associated(tv%internal_heat)) then
do j=js,je ; do i=is,ie
diff --git a/src/parameterizations/vertical/MOM_diabatic_aux.F90 b/src/parameterizations/vertical/MOM_diabatic_aux.F90
index 25c5fdef14..1f1eaa664c 100644
--- a/src/parameterizations/vertical/MOM_diabatic_aux.F90
+++ b/src/parameterizations/vertical/MOM_diabatic_aux.F90
@@ -15,7 +15,7 @@ module MOM_diabatic_aux
use MOM_error_handler, only : MOM_error, FATAL, WARNING, callTree_showQuery
use MOM_error_handler, only : callTree_enter, callTree_leave, callTree_waypoint
use MOM_file_parser, only : get_param, log_param, log_version, param_file_type
-use MOM_forcing_type, only : forcing, extractFluxes1d, forcing_SinglePointPrint
+use MOM_forcing_type, only : forcing, extractFluxes1d, forcing_SinglePointPrint, distribute_srunoff
use MOM_grid, only : ocean_grid_type
use MOM_interface_heights, only : thickness_to_dz
use MOM_interpolate, only : init_external_field, time_interp_external, time_interp_external_init
@@ -762,7 +762,12 @@ subroutine applyBoundaryFluxesInOut(CS, G, GV, US, dt, fluxes, optics, nsw, h, t
! [S H T-1 ~> ppt m s-1 or ppt kg m-2 s-1]
netMassInOut_rate, & ! netmassinout but for dt=1 [H T-1 ~> m s-1 or kg m-2 s-1]
mixing_depth, & ! The mixing depth for brine plumes [H ~> m or kg m-2]
- total_h ! Total thickness of the water column [H ~> m or kg m-2]
+ total_h, & ! Total thickness of the water column [H ~> m or kg m-2]
+ dHeat_srunoff, & ! The heat added to each column by srunoff, from distribute_srunoff, for
+ ! the heat_content_eff_srunoff diagnostic [Q H ~> J m-2]
+ dTempxPmE_srunoff ! The ambient-temperature-weighted mass added to each column by srunoff,
+ ! from distribute_srunoff, for the tv%TempxPmE and heat_content_srunoff
+ ! diagnostics [C H ~> degC m or degC kg m-2]
real, dimension(SZI_(G), SZK_(GV)) :: &
h2d, & ! A 2-d copy of the thicknesses [H ~> m or kg m-2]
! dz, & ! Layer thicknesses in depth units [Z ~> m]
@@ -869,7 +874,8 @@ subroutine applyBoundaryFluxesInOut(CS, G, GV, US, dt, fluxes, optics, nsw, h, t
!$OMP drhodt,drhods,pen_sw_bnd_rate, &
!$OMP pen_TKE_2d,Temp_in,Salin_in,RivermixConst, &
!$OMP mixing_depth,A_brine,fraction_left_brine, &
- !$OMP plume_fraction,dK,total_h) &
+ !$OMP plume_fraction,dK,total_h,dHeat_srunoff, &
+ !$OMP dTempxPmE_srunoff) &
!$OMP firstprivate(SurfPressure,plume_flux)
do j=js,je
! Work in vertical slices for efficiency
@@ -990,6 +996,42 @@ subroutine applyBoundaryFluxesInOut(CS, G, GV, US, dt, fluxes, optics, nsw, h, t
endif
enddo
+ if (associated(fluxes%srunoff)) then
+ ! Add srunoff's contribution to netmassinout_rate/net_heat_rate, used for the surface
+ ! buoyancy flux. This means that for the purposes of the surface buoyancy flux, the mass
+ ! and heat associated with srunoff are treated as if they were applied at the surface,
+ ! analogous to how penetrating SW is handled. srunoff enters at the ambient temperature, so
+ ! only its latent heat of melting changes the temperature and is included in net_heat_rate.
+ if (calculate_buoyancy) then
+ do i=is,ie
+ if ((G%mask2dT(i,j) > 0.0) .and. (fluxes%srunoff(i,j) > 0.0)) then
+ netmassinout_rate(i) = netmassinout_rate(i) + GV%RZ_to_H * fluxes%srunoff(i,j)
+ if (associated(fluxes%latent_srunoff)) &
+ netheat_rate(i) = netheat_rate(i) + fluxes%latent_srunoff(i,j) * (GV%RZ_to_H / tv%C_p)
+ endif
+ enddo ! i
+ endif
+
+ ! Distribute srunoff's mass and heat over a range of depths, if requested,
+ ! updating temperature and salinity together. This is done before loops A and B
+ ! below so loop B sees the srunoff-fattened column, making grounding less likely.
+ call distribute_srunoff(G, GV, dt, fluxes, j, h2d, tv%S(:,j,:), T2d=T2d, C_p=tv%C_p, US=US, &
+ g_Hconv2=g_Hconv2, cTKE=cTKE, dSV_dT=dSV_dT, dSV_dS=dSV_dS, &
+ dHeat_total=dHeat_srunoff, dTempxPmE_total=dTempxPmE_srunoff)
+
+ ! Add srunoff's contributions to relevant diagnostics. heat_content_eff_srunoff is all of
+ ! the heat applied (i.e. heat content plus e.g. latent heat), while heat_content_srunoff is
+ ! only the heat content of the melt water at the ambient temperatures.
+ do i=is,ie
+ if (associated(fluxes%heat_content_eff_srunoff)) &
+ fluxes%heat_content_eff_srunoff(i,j) = dHeat_srunoff(i) * GV%H_to_RZ / dt
+ if (associated(fluxes%heat_content_srunoff)) &
+ fluxes%heat_content_srunoff(i,j) = tv%C_p * dTempxPmE_srunoff(i) * GV%H_to_RZ / dt
+ if (associated(tv%TempxPmE)) &
+ tv%TempxPmE(i,j) = tv%TempxPmE(i,j) + dTempxPmE_srunoff(i) * GV%H_to_RZ
+ enddo
+ endif
+
! Apply the surface boundary fluxes in three steps:
! A/ update mass, temp, and salinity due to all terms except mass leaving
! ocean (and corresponding outward heat content), and ignoring penetrative SW.
@@ -1240,7 +1282,6 @@ subroutine applyBoundaryFluxesInOut(CS, G, GV, US, dt, fluxes, optics, nsw, h, t
.false., .true., T2d, Pen_SW_bnd)
endif
-
! Step D/ copy updated thickness and temperature
! 2d slice now back into model state.
do k=1,nz ; do i=is,ie
diff --git a/src/parameterizations/vertical/MOM_diabatic_driver.F90 b/src/parameterizations/vertical/MOM_diabatic_driver.F90
index 5389de3e1d..287751c18c 100644
--- a/src/parameterizations/vertical/MOM_diabatic_driver.F90
+++ b/src/parameterizations/vertical/MOM_diabatic_driver.F90
@@ -333,6 +333,16 @@ subroutine diabatic(u, v, h, tv, BLD, fluxes, visc, ADp, CDp, dt, Time_end, &
if (dt < 0.0) call MOM_error(FATAL, "MOM_diabatic_driver: "// &
"diabatic was called with a negative timestep.")
+ if (associated(fluxes%srunoff)) then
+ ! KPP code does not account for submarine-melt contributions to the surface buoyancy fluxes.
+ if (CS%useKPP) call MOM_error(FATAL, &
+ "MOM_diabatic_driver: submarine melt (srunoff) coupling is not supported with USE_KPP=True.")
+
+ ! Distribution of submarine melt does not handle negative values, which is not physical anyway.
+ if (any(fluxes%srunoff(G%isc:G%iec,G%jsc:G%jec) < 0.0)) call MOM_error(FATAL, &
+ "MOM_diabatic_driver: coupled submarine melt (srunoff) contains negative values.")
+ endif
+
showCallTree = callTree_showQuery()
! Offer diagnostics of various state variables at the start of diabatic
@@ -1999,6 +2009,10 @@ subroutine layered_diabatic(u, v, h, tv, BLD, fluxes, visc, ADp, CDp, dt, Time_e
showCallTree = callTree_showQuery()
if (showCallTree) call callTree_enter("layered_diabatic(), MOM_diabatic_driver.F90")
+ if (associated(fluxes%srunoff)) call MOM_error(FATAL, &
+ "layered_diabatic: submarine melt (srunoff) coupling requires the ALE algorithm; "//&
+ "it is not supported with the layered (non-ALE) diabatic driver.")
+
! set equivalence between the same bits of memory for these arrays
eaml => eatr ; ebml => ebtr
@@ -3334,7 +3348,8 @@ subroutine diabatic_driver_init(Time, G, GV, US, param_file, useALEalgorithm, di
"and applied as either incoming or outgoing depending on the sign of the net. "//&
"If false, the net incoming fresh water flux is added to the model and "//&
"thereafter the net outgoing is removed from the topmost non-vanished "//&
- "layers of the updated state.", default=.true.)
+ "layers of the updated state. This does not apply to submarine melt (srunoff) "//&
+ "which is handled separately.", default=.true.)
call get_param(param_file, mdl, "DEBUG", CS%debug, &
"If true, write out verbose debugging data.", &
diff --git a/src/tracer/MOM_tracer_diabatic.F90 b/src/tracer/MOM_tracer_diabatic.F90
index ae6d98e3a7..32b30784d4 100644
--- a/src/tracer/MOM_tracer_diabatic.F90
+++ b/src/tracer/MOM_tracer_diabatic.F90
@@ -9,7 +9,7 @@ module MOM_tracer_diabatic
use MOM_grid, only : ocean_grid_type
use MOM_verticalGrid, only : verticalGrid_type
-use MOM_forcing_type, only : forcing
+use MOM_forcing_type, only : forcing, distribute_srunoff
use MOM_error_handler, only : MOM_error, FATAL, WARNING
implicit none ; private
@@ -530,6 +530,11 @@ subroutine applyTracerBoundaryFluxesInOut(G, GV, Tr, dt, fluxes, h, evap_CFL_lim
netMassIn(i) = fluxes%netMassIn(i,j)
enddo
+ ! Distribute submarine melt (srunoff) mass over a range of depths, if requested, diluting
+ ! this tracer accordingly. This is done before loops A and B below so loop B sees the
+ ! srunoff-fattened column, making grounding less likely.
+ if (associated(fluxes%srunoff)) call distribute_srunoff(G, GV, dt, fluxes, j, h2d, Tr2d)
+
! Apply the surface boundary fluxes in three steps:
! A/ update concentration from mass entering the ocean
! B/ update concentration from mass leaving ocean.