Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Option to homogenize forces and fluxes #51

Merged
25 changes: 25 additions & 0 deletions src/core/MOM.F90
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ module MOM
use MOM_forcing_type, only : allocate_forcing_type, allocate_mech_forcing
use MOM_forcing_type, only : deallocate_mech_forcing, deallocate_forcing_type
use MOM_forcing_type, only : rotate_forcing, rotate_mech_forcing
use MOM_forcing_type, only : copy_common_forcing_fields, set_derived_forcing_fields
use MOM_forcing_type, only : homogenize_forcing, homogenize_mech_forcing
use MOM_grid, only : ocean_grid_type, MOM_grid_init, MOM_grid_end
use MOM_grid, only : set_first_direction, rescale_grid_bathymetry
use MOM_hor_index, only : hor_index_type, hor_index_init
Expand Down Expand Up @@ -207,6 +209,8 @@ module MOM
type(ocean_grid_type) :: G_in !< Input grid metric
type(ocean_grid_type), pointer :: G => NULL() !< Model grid metric
logical :: rotate_index = .false. !< True if index map is rotated
logical :: homogenize_forcings = .false. !< True if all inputs are homogenized
logical :: update_ustar = .false. !< True to update ustar from homogenized tau

type(verticalGrid_type), pointer :: &
GV => NULL() !< structure containing vertical grid info
Expand Down Expand Up @@ -579,6 +583,20 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS
fluxes => fluxes_in
endif

! Homogenize the forces
if (CS%homogenize_forcings) then
! Homogenize all forcing and fluxes fields.
call homogenize_mech_forcing(forces, G, US, GV%Rho0, CS%update_ustar)
! Note the following computes the mean ustar as the mean of ustar rather than
! ustar of the mean of tau.
call homogenize_forcing(fluxes, G)
if (CS%update_ustar) then
! These calls corrects the ustar values
call copy_common_forcing_fields(forces, fluxes, G)
call set_derived_forcing_fields(forces, fluxes, G, US, GV%Rho0)
endif
endif

! First determine the time step that is consistent with this call and an
! integer fraction of time_interval.
if (do_dyn) then
Expand Down Expand Up @@ -2143,6 +2161,13 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, &

call callTree_waypoint("MOM parameters read (initialize_MOM)")

call get_param(param_file, "MOM", "HOMOGENIZE_FORCINGS", CS%homogenize_forcings, &
"If True, homogenize the forces and fluxes.", default=.false.)
call get_param(param_file, "MOM", "UPDATE_USTAR",CS%update_ustar, &
Hallberg-NOAA marked this conversation as resolved.
Show resolved Hide resolved
"If True, update ustar from homogenized tau when using the "//&
"HOMOGENIZE_FORCINGS option. Note that this will not work "//&
"with a non-zero gustiness factor.", default=.false.)

! Grid rotation test
call get_param(param_file, "MOM", "ROTATE_INDEX", CS%rotate_index, &
"Enable rotation of the horizontal indices.", default=.false., &
Expand Down
231 changes: 231 additions & 0 deletions src/core/MOM_forcing_type.F90
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module MOM_forcing_type
use MOM_grid, only : ocean_grid_type
use MOM_opacity, only : sumSWoverBands, optics_type, extract_optics_slice, optics_nbands
use MOM_spatial_means, only : global_area_integral, global_area_mean
use MOM_spatial_means, only : global_area_mean_u, global_area_mean_v
use MOM_unit_scaling, only : unit_scale_type
use MOM_variables, only : surface, thermo_var_ptrs
use MOM_verticalGrid, only : verticalGrid_type
Expand All @@ -35,6 +36,7 @@ module MOM_forcing_type
public set_derived_forcing_fields, copy_back_forcing_fields
public set_net_mass_forcing, get_net_mass_forcing
public rotate_forcing, rotate_mech_forcing
public homogenize_forcing, homogenize_mech_forcing

!> Allocate the fields of a (flux) forcing type, based on either a set of input
!! flags for each group of fields, or a pre-allocated reference forcing.
Expand Down Expand Up @@ -3358,6 +3360,7 @@ subroutine rotate_forcing(fluxes_in, fluxes, turns)
if (do_iceberg) then
call rotate_array(fluxes_in%ustar_berg, turns, fluxes%ustar_berg)
call rotate_array(fluxes_in%area_berg, turns, fluxes%area_berg)
!BGR: pretty sure the following line isn't supposed to be here.
call rotate_array(fluxes_in%iceshelf_melt, turns, fluxes%iceshelf_melt)
endif

Expand Down Expand Up @@ -3463,6 +3466,234 @@ subroutine rotate_mech_forcing(forces_in, turns, forces)
forces%initialized = forces_in%initialized
end subroutine rotate_mech_forcing

!< Homogenize the forcing fields from the input domain
subroutine homogenize_mech_forcing(forces, G, US, Rho0, UpdateUstar)
type(mech_forcing), intent(inout) :: forces !< Forcing on the input domain
type(ocean_grid_type), intent(in) :: G !< Grid metric of target forcing
type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
real, intent(in) :: Rho0 !< A reference density of seawater [R ~> kg m-3],
!! as used to calculate ustar.
logical, optional, intent(in) :: UpdateUstar !< A logical to determine if Ustar should be directly averaged
!! or updated from mean tau.

real :: tx_mean, ty_mean, avg
real :: iRho0
logical :: do_stress, do_ustar, do_shelf, do_press, do_iceberg, tau2ustar
integer :: i, j, is, ie, js, je, isB, ieB, jsB, jeB
is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
isB = G%iscB ; ieB = G%iecB ; jsB = G%jscB ; jeB = G%jecB

iRho0 = US%L_to_Z / Rho0

tau2ustar = .false.
if (present(UpdateUstar)) tau2ustar = UpdateUstar

call get_mech_forcing_groups(forces, do_stress, do_ustar, do_shelf, &
do_press, do_iceberg)

if (do_stress) then
tx_mean = global_area_mean_u(forces%taux, G)
do j=js,je ; do i=isB,ieB
if (G%mask2dCu(I,j) > 0.) forces%taux(I,j) = tx_mean
enddo ; enddo
ty_mean = global_area_mean_v(forces%tauy, G)
do j=jsB,jeB ; do i=is,ie
if (G%mask2dCv(i,J) > 0.) forces%tauy(i,J) = ty_mean
enddo ; enddo
if (tau2ustar) then
do j=js,je ; do i=is,ie
if (G%mask2dT(i,j) > 0.) forces%ustar(i,j) = sqrt(sqrt(tx_mean**2 + ty_mean**2)*iRho0)
enddo ; enddo
else
call homogenize_field_t(forces%ustar, G)
endif
else
if (do_ustar) then
call homogenize_field_t(forces%ustar, G)
endif
endif

if (do_shelf) then
call homogenize_field_u(forces%rigidity_ice_u, G)
call homogenize_field_v(forces%rigidity_ice_v, G)
call homogenize_field_u(forces%frac_shelf_u, G)
call homogenize_field_v(forces%frac_shelf_v, G)
endif

if (do_press) then
! NOTE: p_surf_SSH either points to p_surf or p_surf_full
call homogenize_field_t(forces%p_surf, G)
call homogenize_field_t(forces%p_surf_full, G)
call homogenize_field_t(forces%net_mass_src, G)
endif

if (do_iceberg) then
call homogenize_field_t(forces%area_berg, G)
call homogenize_field_t(forces%mass_berg, G)
endif

end subroutine homogenize_mech_forcing

!< Homogenize the fluxes
subroutine homogenize_forcing(fluxes, G)
type(forcing), intent(inout) :: fluxes !< Input forcing struct
type(ocean_grid_type), intent(in) :: G !< Grid metric of target forcing

real :: avg
logical :: do_ustar, do_water, do_heat, do_salt, do_press, do_shelf, &
do_iceberg, do_heat_added, do_buoy
integer :: i, j, is, ie, js, je, isB, ieB, jsB, jeB
is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
isB = G%iscB ; ieB = G%iecB ; jsB = G%jscB ; jeB = G%jecB

call get_forcing_groups(fluxes, do_water, do_heat, do_ustar, do_press, &
do_shelf, do_iceberg, do_salt, do_heat_added, do_buoy)

if (do_ustar) then
call homogenize_field_t(fluxes%ustar, G)
call homogenize_field_t(fluxes%ustar_gustless, G)
endif

if (do_water) then
call homogenize_field_t(fluxes%evap, G)
call homogenize_field_t(fluxes%lprec, G)
call homogenize_field_t(fluxes%lprec, G)
call homogenize_field_t(fluxes%fprec, G)
call homogenize_field_t(fluxes%vprec, G)
call homogenize_field_t(fluxes%lrunoff, G)
call homogenize_field_t(fluxes%frunoff, G)
call homogenize_field_t(fluxes%seaice_melt, G)
call homogenize_field_t(fluxes%netMassOut, G)
call homogenize_field_t(fluxes%netMassIn, G)
!This was removed and I don't think replaced. Not needed?
!call homogenize_field_t(fluxes%netSalt, G)
endif

if (do_heat) then
call homogenize_field_t(fluxes%seaice_melt_heat, G)
call homogenize_field_t(fluxes%sw, G)
call homogenize_field_t(fluxes%lw, G)
call homogenize_field_t(fluxes%latent, G)
call homogenize_field_t(fluxes%sens, G)
call homogenize_field_t(fluxes%latent_evap_diag, G)
call homogenize_field_t(fluxes%latent_fprec_diag, G)
call homogenize_field_t(fluxes%latent_frunoff_diag, G)
endif

if (do_salt) call homogenize_field_t(fluxes%salt_flux, G)

if (do_heat .and. do_water) then
call homogenize_field_t(fluxes%heat_content_cond, G)
call homogenize_field_t(fluxes%heat_content_icemelt, G)
call homogenize_field_t(fluxes%heat_content_lprec, G)
call homogenize_field_t(fluxes%heat_content_fprec, G)
call homogenize_field_t(fluxes%heat_content_vprec, G)
call homogenize_field_t(fluxes%heat_content_lrunoff, G)
call homogenize_field_t(fluxes%heat_content_frunoff, G)
call homogenize_field_t(fluxes%heat_content_massout, G)
call homogenize_field_t(fluxes%heat_content_massin, G)
endif

if (do_press) call homogenize_field_t(fluxes%p_surf, G)

if (do_shelf) then
call homogenize_field_t(fluxes%frac_shelf_h, G)
call homogenize_field_t(fluxes%ustar_shelf, G)
call homogenize_field_t(fluxes%iceshelf_melt, G)
endif

if (do_iceberg) then
call homogenize_field_t(fluxes%ustar_berg, G)
call homogenize_field_t(fluxes%area_berg, G)
endif

if (do_heat_added) then
call homogenize_field_t(fluxes%heat_added, G)
endif

! The following fields are handled by drivers rather than control flags.
if (associated(fluxes%sw_vis_dir)) &
call homogenize_field_t(fluxes%sw_vis_dir, G)

if (associated(fluxes%sw_vis_dif)) &
call homogenize_field_t(fluxes%sw_vis_dif, G)

if (associated(fluxes%sw_nir_dir)) &
call homogenize_field_t(fluxes%sw_nir_dir, G)

if (associated(fluxes%sw_nir_dif)) &
call homogenize_field_t(fluxes%sw_nir_dif, G)

if (associated(fluxes%salt_flux_in)) &
call homogenize_field_t(fluxes%salt_flux_in, G)

if (associated(fluxes%salt_flux_added)) &
call homogenize_field_t(fluxes%salt_flux_added, G)

if (associated(fluxes%p_surf_full)) &
call homogenize_field_t(fluxes%p_surf_full, G)

if (associated(fluxes%buoy)) &
call homogenize_field_t(fluxes%buoy, G)

if (associated(fluxes%TKE_tidal)) &
call homogenize_field_t(fluxes%TKE_tidal, G)

if (associated(fluxes%ustar_tidal)) &
call homogenize_field_t(fluxes%ustar_tidal, G)

! TODO: tracer flux homogenization
! Having a warning causes a lot of errors (each time step).
!if (coupler_type_initialized(fluxes%tr_fluxes)) &
! call MOM_error(WARNING, "Homogenization of tracer BC fluxes not yet implemented.")

end subroutine homogenize_forcing

subroutine homogenize_field_t(var, G)
type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
real, dimension(SZI_(G), SZJ_(G)), intent(inout) :: var !< The variable to homogenize

real :: avg
integer :: i, j, is, ie, js, je
is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec

avg = global_area_mean(var, G)
do j=js,je ; do i=is,ie
if (G%mask2dT(i,j) > 0.) var(i,j) = avg
enddo ; enddo

end subroutine homogenize_field_t

subroutine homogenize_field_v(var, G)
type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
real, dimension(SZI_(G), SZJB_(G)), intent(inout) :: var !< The variable to homogenize

real :: avg
integer :: i, j, is, ie, jsB, jeB
is = G%isc ; ie = G%iec ; jsB = G%jscB ; jeB = G%jecB

avg = global_area_mean_v(var, G)
do J=jsB,jeB ; do i=is,ie
if (G%mask2dCv(i,J) > 0.) var(i,J) = avg
enddo ; enddo

end subroutine homogenize_field_v

subroutine homogenize_field_u(var, G)
type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
real, dimension(SZI_(G), SZJB_(G)), intent(inout) :: var !< The variable to homogenize

real :: avg
integer :: i, j, isB, ieB, js, je
isB = G%iscB ; ieB = G%iecB ; js = G%jsc ; je = G%jec

avg = global_area_mean_u(var, G)
do j=js,je ; do I=isB,ieB
if (G%mask2dCu(I,j) > 0.) var(I,j) = avg
enddo ; enddo

end subroutine homogenize_field_u

!> \namespace mom_forcing_type
!!
!! \section section_fluxes Boundary fluxes
Expand Down
46 changes: 45 additions & 1 deletion src/diagnostics/MOM_spatial_means.F90
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module MOM_spatial_means
#include <MOM_memory.h>

public :: global_i_mean, global_j_mean
public :: global_area_mean, global_layer_mean
public :: global_area_mean, global_area_mean_u, global_area_mean_v, global_layer_mean
public :: global_area_integral
public :: global_volume_mean, global_mass_integral
public :: adjust_area_mean_to_zero
Expand Down Expand Up @@ -47,6 +47,50 @@ function global_area_mean(var, G, scale)

end function global_area_mean

!> Return the global area mean of a variable. This uses reproducing sums.
function global_area_mean_v(var, G)
type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
real, dimension(SZI_(G), SZJB_(G)), intent(in) :: var !< The variable to average

real, dimension(SZI_(G), SZJ_(G)) :: tmpForSumming
real :: global_area_mean_v
integer :: i, j, is, ie, js, je, isB, ieB, jsB, jeB

is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
isB = G%iscB ; ieB = G%iecB ; jsB = G%jscB ; jeB = G%jecB

tmpForSumming(:,:) = 0.
do J=js,je ; do i=is,ie
tmpForSumming(i,j) = G%areaT(i,j) * (var(i,J) * G%mask2dCv(i,J) + &
var(i,J-1) * G%mask2dCv(i,J-1)) &
/ max(1.e-20,G%mask2dCv(i,J)+G%mask2dCv(i,J-1))
enddo ; enddo
global_area_mean_v = reproducing_sum(tmpForSumming) * G%IareaT_global

end function global_area_mean_v

!> Return the global area mean of a variable on U grid. This uses reproducing sums.
function global_area_mean_u(var, G)
type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
real, dimension(SZIB_(G), SZJ_(G)), intent(in) :: var !< The variable to average

real, dimension(SZI_(G), SZJ_(G)) :: tmpForSumming
real :: global_area_mean_u
integer :: i, j, is, ie, js, je, isB, ieB, jsB, jeB

is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
isB = G%iscB ; ieB = G%iecB ; jsB = G%jscB ; jeB = G%jecB

tmpForSumming(:,:) = 0.
do j=js,je ; do i=is,ie
tmpForSumming(i,j) = G%areaT(i,j) * (var(I,j) * G%mask2dCu(I,j) + &
var(I-1,j) * G%mask2dCu(I-1,j)) &
/ max(1.e-20,G%mask2dCu(I,j)+G%mask2dCu(I-1,j))
enddo ; enddo
global_area_mean_u = reproducing_sum(tmpForSumming) * G%IareaT_global

end function global_area_mean_u

!> Return the global area integral of a variable, by default using the masked area from the
!! grid, but an alternate could be used instead. This uses reproducing sums.
function global_area_integral(var, G, scale, area)
Expand Down