From 32b5e8aee9185de8d9b055398e79b591d332064f Mon Sep 17 00:00:00 2001 From: He Wang Date: Thu, 9 Mar 2023 15:12:18 -0500 Subject: [PATCH 01/17] Separate SAL from tidal_forcing Calculation for self-attraction and loading (SAL) is separated as a new module (MOM_self_attr_load) from module MOM_tidal_forcing, as SAL is a process not limited to tides. The new module includes both online spherical harmonics method and scalar approximation. Read-in method (TIDAL_SAL_FROM_FILE) is kept in the tidal forcing module as it is specific to tides. For the iterative method (USE_PREV_TIDES), the updating part that is tied to the scalar approximation is moved to the new SAL model, while the read-in part remains in the tidal_forcing module. The tidal forcing module now only contains calculations independent from the ocean's state and the only input variables is the current time. * A new parameter CALCULATE_SAL is added, which controls SAL calculation in PressureForce independent of whether tides is on or not. The default of CALCULATE_SAL is TIDES to avoid making changes in old MOM_input. * For the unplit mode, runtime parameters calculate_SAL and use_tides are moved from init subroutines to control structures. This allows safe deallocations of the corresponding module CSs. * A new control structure for the SAL module is used by the dynamical cores and pressure force modules. * For SAL related parameters, their names still incorrectly contain TIDE or TIDAL. This will be addressed in the following commits. * A new diagnostic is added in PressureForce to output calculated SAL fields. Note that the 'e_tidal' diagnostic is unchanged and still includes SAL field for backward compatibility. * Subroutine tidal_forcing_sensitivity, which is used by the barotropic solver to take into account the scalar approximation, is renamed to scalar_SAL_sensitivity. * Documentations are updated for the cited papers. --- src/core/MOM_PressureForce.F90 | 10 +- src/core/MOM_PressureForce_FV.F90 | 81 ++++-- src/core/MOM_PressureForce_Montgomery.F90 | 70 +++-- src/core/MOM_barotropic.F90 | 34 ++- src/core/MOM_dynamics_split_RK2.F90 | 13 +- src/core/MOM_dynamics_unsplit.F90 | 20 +- src/core/MOM_dynamics_unsplit_RK2.F90 | 20 +- .../lateral/MOM_load_love_numbers.F90 | 24 +- .../lateral/MOM_self_attr_load.F90 | 271 ++++++++++++++++++ .../lateral/MOM_spherical_harmonics.F90 | 16 +- .../lateral/MOM_tidal_forcing.F90 | 215 ++------------ 11 files changed, 491 insertions(+), 283 deletions(-) create mode 100644 src/parameterizations/lateral/MOM_self_attr_load.F90 diff --git a/src/core/MOM_PressureForce.F90 b/src/core/MOM_PressureForce.F90 index 844d9db4bc..ad76a9a9f5 100644 --- a/src/core/MOM_PressureForce.F90 +++ b/src/core/MOM_PressureForce.F90 @@ -13,6 +13,7 @@ module MOM_PressureForce use MOM_PressureForce_Mont, only : PressureForce_Mont_Bouss, PressureForce_Mont_nonBouss use MOM_PressureForce_Mont, only : PressureForce_Mont_init use MOM_PressureForce_Mont, only : PressureForce_Mont_CS +use MOM_self_attr_load, only : SAL_CS use MOM_tidal_forcing, only : tidal_forcing_CS use MOM_unit_scaling, only : unit_scale_type use MOM_variables, only : thermo_var_ptrs @@ -80,7 +81,7 @@ subroutine PressureForce(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm, pbce, e end subroutine Pressureforce !> Initialize the pressure force control structure -subroutine PressureForce_init(Time, G, GV, US, param_file, diag, CS, tides_CSp) +subroutine PressureForce_init(Time, G, GV, US, param_file, diag, CS, SAL_CSp, tides_CSp) type(time_type), target, intent(in) :: Time !< Current model time type(ocean_grid_type), intent(in) :: G !< Ocean grid structure type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure @@ -88,7 +89,8 @@ subroutine PressureForce_init(Time, G, GV, US, param_file, diag, CS, tides_CSp) type(param_file_type), intent(in) :: param_file !< Parameter file handles type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure type(PressureForce_CS), intent(inout) :: CS !< Pressure force control structure - type(tidal_forcing_CS), intent(inout), optional :: tides_CSp !< Tide control structure + type(SAL_CS), intent(in), optional :: SAL_CSp !< SAL control structure + type(tidal_forcing_CS), intent(in), optional :: tides_CSp !< Tide control structure #include "version_variable.h" character(len=40) :: mdl = "MOM_PressureForce" ! This module's name. @@ -103,10 +105,10 @@ subroutine PressureForce_init(Time, G, GV, US, param_file, diag, CS, tides_CSp) if (CS%Analytic_FV_PGF) then call PressureForce_FV_init(Time, G, GV, US, param_file, diag, & - CS%PressureForce_FV, tides_CSp) + CS%PressureForce_FV, SAL_CSp, tides_CSp) else call PressureForce_Mont_init(Time, G, GV, US, param_file, diag, & - CS%PressureForce_Mont, tides_CSp) + CS%PressureForce_Mont, SAL_CSp, tides_CSp) endif end subroutine PressureForce_init diff --git a/src/core/MOM_PressureForce_FV.F90 b/src/core/MOM_PressureForce_FV.F90 index 281623ae84..27a4e3ae5a 100644 --- a/src/core/MOM_PressureForce_FV.F90 +++ b/src/core/MOM_PressureForce_FV.F90 @@ -9,6 +9,7 @@ module MOM_PressureForce_FV use MOM_file_parser, only : get_param, log_param, log_version, param_file_type use MOM_grid, only : ocean_grid_type use MOM_PressureForce_Mont, only : set_pbce_Bouss, set_pbce_nonBouss +use MOM_self_attr_load, only : calc_SAL, SAL_CS use MOM_tidal_forcing, only : calc_tidal_forcing, tidal_forcing_CS use MOM_unit_scaling, only : unit_scale_type use MOM_variables, only : thermo_var_ptrs @@ -35,6 +36,7 @@ module MOM_PressureForce_FV !> Finite volume pressure gradient control structure type, public :: PressureForce_FV_CS ; private logical :: initialized = .false. !< True if this control structure has been initialized. + logical :: calculate_SAL !< If true, calculate self-attraction and loading. logical :: tides !< If true, apply tidal momentum forcing. real :: Rho0 !< The density used in the Boussinesq !! approximation [R ~> kg m-3]. @@ -61,9 +63,11 @@ module MOM_PressureForce_FV logical :: use_stanley_pgf !< If true, turn on Stanley parameterization in the PGF integer :: id_e_tidal = -1 !< Diagnostic identifier + integer :: id_e_sal = -1 !< Diagnostic identifier integer :: id_rho_pgf = -1 !< Diagnostic identifier integer :: id_rho_stanley_pgf = -1 !< Diagnostic identifier integer :: id_p_stanley = -1 !< Diagnostic identifier + type(SAL_CS), pointer :: SAL_CSp => NULL() !< SAL control structure type(tidal_forcing_CS), pointer :: tides_CSp => NULL() !< Tides control structure end type PressureForce_FV_CS @@ -115,8 +119,9 @@ subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_ real, dimension(SZI_(G),SZJ_(G)) :: & dp, & ! The (positive) change in pressure across a layer [R L2 T-2 ~> Pa]. SSH, & ! The sea surface height anomaly, in depth units [Z ~> m]. - e_tidal, & ! The bottom geopotential anomaly due to tidal forces from - ! astronomical sources and self-attraction and loading [Z ~> m]. + e_sal, & ! The bottom geopotential anomaly due to self-attraction and loading [Z ~> m]. + e_tidal, & ! The bottom geopotential anomaly due to tidal forces from astronomical sources + ! and harmonic self-attraction and loading specific to tides [Z ~> m]. dM, & ! The barotropic adjustment to the Montgomery potential to ! account for a reduced gravity model [L2 T-2 ~> m2 s-2]. za ! The geopotential anomaly (i.e. g*e + alpha_0*pressure) at the @@ -301,16 +306,33 @@ subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_ enddo ; enddo enddo + ! The following two if-statements are arranged in a way that answers are not + ! changed from old versions in which SAL is part of the tidal forcing module. + if (CS%calculate_SAL) then + ! Find and add the self-attraction and loading geopotential anomaly. + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + SSH(i,j) = (za(i,j) - alpha_ref*p(i,j,1)) * I_gEarth - G%Z_ref + enddo ; enddo + call calc_SAL(SSH, e_sal, G, CS%SAL_CSp) + else + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e_sal(i,j) = 0.0 + enddo ; enddo + endif + if (CS%tides) then ! Find and add the tidal geopotential anomaly. + call calc_tidal_forcing(CS%Time, e_tidal, G, US, CS%tides_CSp) !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - SSH(i,j) = (za(i,j) - alpha_ref*p(i,j,1)) * I_gEarth - G%Z_ref + za(i,j) = za(i,j) - GV%g_Earth * (e_sal(i,j) + e_tidal(i,j)) enddo ; enddo - call calc_tidal_forcing(CS%Time, SSH, e_tidal, G, US, CS%tides_CSp) + else !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - za(i,j) = za(i,j) - GV%g_Earth * e_tidal(i,j) + za(i,j) = za(i,j) - GV%g_Earth * e_sal(i,j) enddo ; enddo endif @@ -408,7 +430,9 @@ subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_ endif endif - if (CS%id_e_tidal>0) call post_data(CS%id_e_tidal, e_tidal, CS%diag) + ! To be consistent with old runs, tidal forcing diagnostic also includes SAL. + if (CS%id_e_tidal>0) call post_data(CS%id_e_tidal, e_sal+e_tidal, CS%diag) + if (CS%id_e_sal>0) call post_data(CS%id_e_sal, e_sal, CS%diag) end subroutine PressureForce_FV_nonBouss @@ -441,8 +465,9 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm ! Local variables real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: e ! Interface height in depth units [Z ~> m]. real, dimension(SZI_(G),SZJ_(G)) :: & - e_tidal, & ! The bottom geopotential anomaly due to tidal forces from - ! astronomical sources and self-attraction and loading [Z ~> m]. + e_sal, & ! The bottom geopotential anomaly due to self-attraction and loading [Z ~> m]. + e_tidal, & ! The bottom geopotential anomaly due to tidal forces from astronomical sources + ! and harmonic self-attraction and loading specific to tides [Z ~> m]. SSH, & ! The sea surface height anomaly, in depth units [Z ~> m]. dM ! The barotropic adjustment to the Montgomery potential to ! account for a reduced gravity model [L2 T-2 ~> m2 s-2]. @@ -524,7 +549,9 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm G_Rho0 = GV%g_Earth / GV%Rho0 rho_ref = CS%Rho0 - if (CS%tides) then + ! The following two if-statements are arranged in a way that answers are not + ! changed from old versions in which SAL is part of the tidal forcing module. + if (CS%calculate_SAL) then ! Determine the surface height anomaly for calculating self attraction ! and loading. This should really be based on bottom pressure anomalies, ! but that is not yet implemented, and the current form is correct for @@ -538,21 +565,27 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm SSH(i,j) = SSH(i,j) + h(i,j,k)*GV%H_to_Z enddo ; enddo enddo - call calc_tidal_forcing(CS%Time, SSH, e_tidal, G, US, CS%tides_CSp) + call calc_SAL(SSH, e_sal, G, CS%SAL_CSp) + else + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e_sal(i,j) = 0.0 + enddo ; enddo endif -! Here layer interface heights, e, are calculated. if (CS%tides) then + call calc_tidal_forcing(CS%Time, e_tidal, G, US, CS%tides_CSp) !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e(i,j,nz+1) = -(G%bathyT(i,j) + e_tidal(i,j)) + e(i,j,nz+1) = -(G%bathyT(i,j) + (e_sal(i,j) + e_tidal(i,j))) enddo ; enddo else !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e(i,j,nz+1) = -G%bathyT(i,j) + e(i,j,nz+1) = -(G%bathyT(i,j) + e_sal(i,j)) enddo ; enddo endif + !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do k=nz,1,-1 ; do i=Isq,Ieq+1 e(i,j,K) = e(i,j,K+1) + h(i,j,k)*GV%H_to_Z @@ -750,12 +783,12 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm ! what is used for eta in btstep. See how e was calculated about 200 lines above. !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta(i,j) = e(i,j,1)*GV%Z_to_H + e_tidal(i,j)*GV%Z_to_H + eta(i,j) = e(i,j,1)*GV%Z_to_H + (e_sal(i,j) + e_tidal(i,j))*GV%Z_to_H enddo ; enddo else !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta(i,j) = e(i,j,1)*GV%Z_to_H + eta(i,j) = (e(i,j,1) + e_sal(i,j))*GV%Z_to_H enddo ; enddo endif endif @@ -797,12 +830,17 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm endif endif - if (CS%id_e_tidal>0) call post_data(CS%id_e_tidal, e_tidal, CS%diag) + ! To be consistent with old runs, tidal forcing diagnostic also includes SAL. + if (CS%id_e_tidal>0) call post_data(CS%id_e_tidal, e_sal+e_tidal, CS%diag) + if (CS%id_e_sal>0) call post_data(CS%id_e_sal, e_sal, CS%diag) + if (CS%id_rho_pgf>0) call post_data(CS%id_rho_pgf, rho_pgf, CS%diag) + if (CS%id_rho_stanley_pgf>0) call post_data(CS%id_rho_stanley_pgf, rho_stanley_pgf, CS%diag) + if (CS%id_p_stanley>0) call post_data(CS%id_p_stanley, p_stanley, CS%diag) end subroutine PressureForce_FV_Bouss !> Initializes the finite volume pressure gradient control structure -subroutine PressureForce_FV_init(Time, G, GV, US, param_file, diag, CS, tides_CSp) +subroutine PressureForce_FV_init(Time, G, GV, US, param_file, diag, CS, SAL_CSp, tides_CSp) type(time_type), target, intent(in) :: Time !< Current model time type(ocean_grid_type), intent(in) :: G !< Ocean grid structure type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure @@ -810,6 +848,7 @@ subroutine PressureForce_FV_init(Time, G, GV, US, param_file, diag, CS, tides_CS type(param_file_type), intent(in) :: param_file !< Parameter file handles type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure type(PressureForce_FV_CS), intent(inout) :: CS !< Finite volume PGF control structure + type(SAL_CS), intent(in), target, optional :: SAL_CSp !< SAL control structure type(tidal_forcing_CS), intent(in), target, optional :: tides_CSp !< Tides control structure ! Local variables @@ -824,6 +863,8 @@ subroutine PressureForce_FV_init(Time, G, GV, US, param_file, diag, CS, tides_CS CS%diag => diag ; CS%Time => Time if (present(tides_CSp)) & CS%tides_CSp => tides_CSp + if (present(SAL_CSp)) & + CS%SAL_CSp => SAL_CSp mdl = "MOM_PressureForce_FV" call log_version(param_file, mdl, version, "") @@ -834,6 +875,8 @@ subroutine PressureForce_FV_init(Time, G, GV, US, param_file, diag, CS, tides_CS units="kg m-3", default=GV%Rho0*US%R_to_kg_m3, scale=US%kg_m3_to_R) call get_param(param_file, mdl, "TIDES", CS%tides, & "If true, apply tidal momentum forcing.", default=.false.) + call get_param(param_file, mdl, "CALCULATE_SAL", CS%calculate_SAL, & + "If true, calculate self-attraction and loading.", default=CS%tides) call get_param(param_file, "MOM", "USE_REGRIDDING", use_ALE, & "If True, use the ALE algorithm (regridding/remapping). "//& "If False, use the layered isopycnal algorithm.", default=.false. ) @@ -878,6 +921,10 @@ subroutine PressureForce_FV_init(Time, G, GV, US, param_file, diag, CS, tides_CS CS%id_p_stanley = register_diag_field('ocean_model', 'p_stanley', diag%axesTL, & Time, 'p in PGF with Stanley correction', 'Pa', conversion=US%RL2_T2_to_Pa) endif + if (CS%calculate_SAL) then + CS%id_e_sal = register_diag_field('ocean_model', 'e_SAL', diag%axesT1, & + Time, 'Self-attraction and loading height Anomaly', 'meter', conversion=US%Z_to_m) + endif if (CS%tides) then CS%id_e_tidal = register_diag_field('ocean_model', 'e_tidal', diag%axesT1, & Time, 'Tidal Forcing Astronomical and SAL Height Anomaly', 'meter', conversion=US%Z_to_m) diff --git a/src/core/MOM_PressureForce_Montgomery.F90 b/src/core/MOM_PressureForce_Montgomery.F90 index 424e9b1a32..5223afbccb 100644 --- a/src/core/MOM_PressureForce_Montgomery.F90 +++ b/src/core/MOM_PressureForce_Montgomery.F90 @@ -9,6 +9,7 @@ module MOM_PressureForce_Mont use MOM_error_handler, only : MOM_error, MOM_mesg, FATAL, WARNING, is_root_pe use MOM_file_parser, only : get_param, log_param, log_version, param_file_type use MOM_grid, only : ocean_grid_type +use MOM_self_attr_load, only : calc_SAL, SAL_CS use MOM_tidal_forcing, only : calc_tidal_forcing, tidal_forcing_CS use MOM_unit_scaling, only : unit_scale_type use MOM_variables, only : thermo_var_ptrs @@ -31,6 +32,7 @@ module MOM_PressureForce_Mont !> Control structure for the Montgomery potential form of pressure gradient type, public :: PressureForce_Mont_CS ; private logical :: initialized = .false. !< True if this control structure has been initialized. + logical :: calculate_SAL !< If true, calculate self-attraction and loading. logical :: tides !< If true, apply tidal momentum forcing. real :: Rho0 !< The density used in the Boussinesq !! approximation [R ~> kg m-3]. @@ -45,8 +47,9 @@ module MOM_PressureForce_Mont real, allocatable :: PFv_bc(:,:,:) !< Meridional accelerations due to pressure gradients !! deriving from density gradients within layers [L T-2 ~> m s-2]. !>@{ Diagnostic IDs - integer :: id_PFu_bc = -1, id_PFv_bc = -1, id_e_tidal = -1 + integer :: id_PFu_bc = -1, id_PFv_bc = -1, id_e_tidal = -1, id_e_sal = -1 !>@} + type(SAL_CS), pointer :: SAL_CSp => NULL() !< SAL control structure type(tidal_forcing_CS), pointer :: tides_CSp => NULL() !< The tidal forcing control structure end type PressureForce_Mont_CS @@ -103,8 +106,9 @@ subroutine PressureForce_Mont_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pb ! of a reduced gravity form of the equations [L2 T-2 ~> m2 s-2]. dp_star, & ! Layer thickness after compensation for compressibility [R L2 T-2 ~> Pa]. SSH, & ! The sea surface height anomaly, in depth units [Z ~> m]. - e_tidal, & ! Bottom geopotential anomaly due to tidal forces from - ! astronomical sources and self-attraction and loading [Z ~> m]. + e_sal, & ! Bottom geopotential anomaly due to self-attraction and loading [Z ~> m]. + e_tidal, & ! Bottom geopotential anomaly due to tidal forces from astronomical sources + ! and harmonic self-attraction and loading specific to tides [Z ~> m]. geopot_bot ! Bottom geopotential relative to a temporally fixed reference value, ! including any tidal contributions [L2 T-2 ~> m2 s-2]. real :: p_ref(SZI_(G)) ! The pressure used to calculate the coordinate @@ -180,7 +184,7 @@ subroutine PressureForce_Mont_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pb endif endif - if (CS%tides) then + if (CS%calculate_SAL) then ! Determine the sea surface height anomalies, to enable the calculation ! of self-attraction and loading. !$OMP parallel do default(shared) @@ -203,16 +207,23 @@ subroutine PressureForce_Mont_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pb SSH(i,j) = SSH(i,j) + GV%H_to_RZ * h(i,j,k) * alpha_Lay(k) enddo ; enddo ; enddo endif + call calc_SAL(SSH, e_sal, G, CS%SAL_CSp) + else + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e_sal(i,j) = 0.0 + enddo ; enddo + endif - call calc_tidal_forcing(CS%Time, SSH, e_tidal, G, US, CS%tides_CSp) + if (CS%tides) then + call calc_tidal_forcing(CS%Time, e_tidal, G, US, CS%tides_CSp) !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - geopot_bot(i,j) = -GV%g_Earth*(e_tidal(i,j) + G%bathyT(i,j)) + geopot_bot(i,j) = -GV%g_Earth*(e_sal(i,j) + e_tidal(i,j) + G%bathyT(i,j)) enddo ; enddo else !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - geopot_bot(i,j) = -GV%g_Earth*G%bathyT(i,j) + geopot_bot(i,j) = -GV%g_Earth*(e_sal(i,j) + G%bathyT(i,j)) enddo ; enddo endif @@ -348,7 +359,9 @@ subroutine PressureForce_Mont_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pb if (CS%id_PFu_bc>0) call post_data(CS%id_PFu_bc, CS%PFu_bc, CS%diag) if (CS%id_PFv_bc>0) call post_data(CS%id_PFv_bc, CS%PFv_bc, CS%diag) - if (CS%id_e_tidal>0) call post_data(CS%id_e_tidal, e_tidal, CS%diag) + ! To be consistent with old runs, tidal forcing diagnostic also includes SAL. + if (CS%id_e_tidal>0) call post_data(CS%id_e_tidal, e_sal+e_tidal, CS%diag) + if (CS%id_e_sal>0) call post_data(CS%id_e_sal, e_sal, CS%diag) end subroutine PressureForce_Mont_nonBouss @@ -396,9 +409,9 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, real :: h_star(SZI_(G),SZJ_(G)) ! Layer thickness after compensation ! for compressibility [Z ~> m]. real :: SSH(SZI_(G),SZJ_(G)) ! The sea surface height anomaly, in depth units [Z ~> m]. - real :: e_tidal(SZI_(G),SZJ_(G)) ! Bottom geopotential anomaly due to tidal - ! forces from astronomical sources and self- - ! attraction and loading, in depth units [Z ~> m]. + real :: e_sal(SZI_(G),SZJ_(G)) ! The bottom geopotential anomaly due to self-attraction and loading [Z ~> m]. + real :: e_tidal(SZI_(G),SZJ_(G)) ! Bottom geopotential anomaly due to tidal forces from astronomical sources + ! and harmonic self-attraction and loading specific to tides, in depth units [Z ~> m]. real :: p_ref(SZI_(G)) ! The pressure used to calculate the coordinate ! density [R L2 T-2 ~> Pa] (usually 2e7 Pa = 2000 dbar). real :: I_Rho0 ! 1/Rho0 [R-1 ~> m3 kg-1]. @@ -440,7 +453,8 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, I_Rho0 = 1.0/CS%Rho0 G_Rho0 = GV%g_Earth / GV%Rho0 - if (CS%tides) then + + if (CS%calculate_SAL) then ! Determine the surface height anomaly for calculating self attraction ! and loading. This should really be based on bottom pressure anomalies, ! but that is not yet implemented, and the current form is correct for @@ -452,21 +466,26 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, SSH(i,j) = SSH(i,j) + h(i,j,k)*GV%H_to_Z enddo ; enddo enddo - call calc_tidal_forcing(CS%Time, SSH, e_tidal, G, US, CS%tides_CSp) + call calc_SAL(SSH, e_sal, G, CS%SAL_CSp) + else + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e_sal(i,j) = 0.0 + enddo ; enddo endif -! Here layer interface heights, e, are calculated. if (CS%tides) then + call calc_tidal_forcing(CS%Time, e_tidal, G, US, CS%tides_CSp) !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e(i,j,nz+1) = -(G%bathyT(i,j) + e_tidal(i,j)) + e(i,j,nz+1) = -(G%bathyT(i,j) + (e_sal(i,j) + e_tidal(i,j))) enddo ; enddo else !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e(i,j,nz+1) = -G%bathyT(i,j) + e(i,j,nz+1) = -(G%bathyT(i,j) + e_sal(i,j)) enddo ; enddo endif + !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do k=nz,1,-1 ; do i=Isq,Ieq+1 e(i,j,K) = e(i,j,K+1) + h(i,j,k)*GV%H_to_Z @@ -588,19 +607,21 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, ! about 200 lines above. !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta(i,j) = e(i,j,1)*GV%Z_to_H + e_tidal(i,j)*GV%Z_to_H + eta(i,j) = e(i,j,1)*GV%Z_to_H + (e_sal(i,j)+e_tidal(i,j))*GV%Z_to_H enddo ; enddo else !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta(i,j) = e(i,j,1)*GV%Z_to_H + eta(i,j) = e(i,j,1)*GV%Z_to_H + e_sal(i,j)*GV%Z_to_H enddo ; enddo endif endif if (CS%id_PFu_bc>0) call post_data(CS%id_PFu_bc, CS%PFu_bc, CS%diag) if (CS%id_PFv_bc>0) call post_data(CS%id_PFv_bc, CS%PFv_bc, CS%diag) - if (CS%id_e_tidal>0) call post_data(CS%id_e_tidal, e_tidal, CS%diag) + ! To be consistent with old runs, tidal forcing diagnostic also includes SAL. + if (CS%id_e_tidal>0) call post_data(CS%id_e_tidal, e_sal+e_tidal, CS%diag) + if (CS%id_e_sal>0) call post_data(CS%id_e_sal, e_sal, CS%diag) end subroutine PressureForce_Mont_Bouss @@ -821,7 +842,7 @@ subroutine Set_pbce_nonBouss(p, tv, G, GV, US, GFS_scale, pbce, alpha_star) end subroutine Set_pbce_nonBouss !> Initialize the Montgomery-potential form of PGF control structure -subroutine PressureForce_Mont_init(Time, G, GV, US, param_file, diag, CS, tides_CSp) +subroutine PressureForce_Mont_init(Time, G, GV, US, param_file, diag, CS, SAL_CSp, tides_CSp) type(time_type), target, intent(in) :: Time !< Current model time type(ocean_grid_type), intent(in) :: G !< ocean grid structure type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure @@ -829,6 +850,7 @@ subroutine PressureForce_Mont_init(Time, G, GV, US, param_file, diag, CS, tides_ type(param_file_type), intent(in) :: param_file !< Parameter file handles type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure type(PressureForce_Mont_CS), intent(inout) :: CS !< Montgomery PGF control structure + type(SAL_CS), intent(in), target, optional :: SAL_CSp !< SAL control structure type(tidal_forcing_CS), intent(in), target, optional :: tides_CSp !< Tides control structure ! Local variables @@ -841,6 +863,8 @@ subroutine PressureForce_Mont_init(Time, G, GV, US, param_file, diag, CS, tides_ CS%diag => diag ; CS%Time => Time if (present(tides_CSp)) & CS%tides_CSp => tides_CSp + if (present(SAL_CSp)) & + CS%SAL_CSp => SAL_CSp mdl = "MOM_PressureForce_Mont" call log_version(param_file, mdl, version, "") @@ -852,6 +876,8 @@ subroutine PressureForce_Mont_init(Time, G, GV, US, param_file, diag, CS, tides_ units="kg m-3", default=1035.0, scale=US%R_to_kg_m3) call get_param(param_file, mdl, "TIDES", CS%tides, & "If true, apply tidal momentum forcing.", default=.false.) + call get_param(param_file, mdl, "CALCULATE_SAL", CS%calculate_SAL, & + "If true, calculate self-attraction and loading.", default=CS%tides) call get_param(param_file, mdl, "USE_EOS", use_EOS, default=.true., & do_not_log=.true.) ! Input for diagnostic use only. @@ -866,6 +892,10 @@ subroutine PressureForce_Mont_init(Time, G, GV, US, param_file, diag, CS, tides_ allocate(CS%PFv_bc(G%isd:G%ied,G%JsdB:G%JedB,GV%ke), source=0.) endif + if (CS%calculate_SAL) then + CS%id_e_sal = register_diag_field('ocean_model', 'e_SAL', diag%axesT1, & + Time, 'SAL Height Anomaly', 'meter', conversion=US%Z_to_m) + endif if (CS%tides) then CS%id_e_tidal = register_diag_field('ocean_model', 'e_tidal', diag%axesT1, & Time, 'Tidal Forcing Astronomical and SAL Height Anomaly', 'meter', conversion=US%Z_to_m) diff --git a/src/core/MOM_barotropic.F90 b/src/core/MOM_barotropic.F90 index adbbd3b4dd..c814c563e3 100644 --- a/src/core/MOM_barotropic.F90 +++ b/src/core/MOM_barotropic.F90 @@ -23,7 +23,8 @@ module MOM_barotropic use MOM_open_boundary, only : OBC_DIRECTION_N, OBC_DIRECTION_S, OBC_segment_type use MOM_restart, only : register_restart_field, register_restart_pair use MOM_restart, only : query_initialized, MOM_restart_CS -use MOM_tidal_forcing, only : tidal_forcing_sensitivity, tidal_forcing_CS +use MOM_self_attr_load, only : scalar_SAL_sensitivity +use MOM_self_attr_load, only : SAL_CS use MOM_time_manager, only : time_type, real_to_time, operator(+), operator(-) use MOM_unit_scaling, only : unit_scale_type use MOM_variables, only : BT_cont_type, alloc_bt_cont_type @@ -229,7 +230,7 @@ module MOM_barotropic real :: const_dyn_psurf !< The constant that scales the dynamic surface !! pressure [nondim]. Stable values are < ~1.0. !! The default is 0.9. - logical :: tides !< If true, apply tidal momentum forcing. + logical :: calculate_SAL !< If true, calculate self-attration and loading. logical :: tidal_sal_bug !< If true, the tidal self-attraction and loading anomaly in the !! barotropic solver has the wrong sign, replicating a long-standing !! bug. @@ -283,7 +284,7 @@ module MOM_barotropic !! the timing of diagnostic output. type(MOM_domain_type), pointer :: BT_Domain => NULL() !< Barotropic MOM domain type(hor_index_type), pointer :: debug_BT_HI => NULL() !< debugging copy of horizontal index_type - type(tidal_forcing_CS), pointer :: tides_CSp => NULL() !< Control structure for tides + type(SAL_CS), pointer :: SAL_CSp => NULL() !< Control structure for SAL logical :: module_is_initialized = .false. !< If true, module has been initialized integer :: isdw !< The lower i-memory limit for the wide halo arrays. @@ -1088,8 +1089,8 @@ subroutine btstep(U_in, V_in, eta_in, dt, bc_accel_u, bc_accel_v, forces, pbce, enddo endif - if (CS%tides) then - call tidal_forcing_sensitivity(G, CS%tides_CSp, det_de) + if (CS%calculate_SAL) then + call scalar_SAL_sensitivity(CS%SAL_CSp, det_de) if (CS%tidal_sal_bug) then dgeo_de = 1.0 + det_de + CS%G_extra else @@ -2845,7 +2846,7 @@ subroutine set_dtbt(G, GV, US, CS, eta, pbce, BT_cont, gtot_est, SSH_add) endif det_de = 0.0 - if (CS%tides) call tidal_forcing_sensitivity(G, CS%tides_CSp, det_de) + if (CS%calculate_SAL) call scalar_SAL_sensitivity(CS%SAL_CSp, det_de) if (CS%tidal_sal_bug) then dgeo_de = 1.0 + max(0.0, det_de + CS%G_extra) else @@ -4297,7 +4298,7 @@ end subroutine bt_mass_source !! barotropic calculation and initializes any barotropic fields that have not !! already been initialized. subroutine barotropic_init(u, v, h, eta, Time, G, GV, US, param_file, diag, CS, & - restart_CS, calc_dtbt, BT_cont, tides_CSp) + restart_CS, calc_dtbt, BT_cont, SAL_CSp) type(ocean_grid_type), intent(inout) :: 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 @@ -4321,8 +4322,8 @@ subroutine barotropic_init(u, v, h, eta, Time, G, GV, US, param_file, diag, CS, type(BT_cont_type), pointer :: BT_cont !< A structure with elements that describe the !! effective open face areas as a function of !! barotropic flow. - type(tidal_forcing_CS), target, optional :: tides_CSp !< A pointer to the control structure of the - !! tide module. + type(SAL_CS), target, optional :: SAL_CSp !< A pointer to the control structure of the + !! SAL module. ! This include declares and sets the variable "version". # include "version_variable.h" @@ -4348,7 +4349,7 @@ subroutine barotropic_init(u, v, h, eta, Time, G, GV, US, param_file, diag, CS, real :: Z_to_H ! A local unit conversion factor [H Z-1 ~> nondim or kg m-3] real :: H_to_Z ! A local unit conversion factor [Z H-1 ~> nondim or m3 kg-1] real :: det_de ! The partial derivative due to self-attraction and loading of the reference - ! geopotential with the sea surface height when tides are enabled [nondim]. + ! geopotential with the sea surface height when scalar SAL are enabled [nondim]. ! This is typically ~0.09 or less. real, allocatable :: lin_drag_h(:,:) ! A spatially varying linear drag coefficient at tracer points ! that acts on the barotropic flow [H T-1 ~> m s-1 or kg m-2 s-1]. @@ -4362,6 +4363,7 @@ subroutine barotropic_init(u, v, h, eta, Time, G, GV, US, param_file, diag, CS, ! the answers from the end of 2018. Otherwise, use more efficient ! or general expressions. logical :: use_BT_cont_type + logical :: use_tides character(len=48) :: thickness_units, flux_units character*(40) :: hvel_str integer :: is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz @@ -4383,8 +4385,8 @@ subroutine barotropic_init(u, v, h, eta, Time, G, GV, US, param_file, diag, CS, CS%module_is_initialized = .true. CS%diag => diag ; CS%Time => Time - if (present(tides_CSp)) then - CS%tides_CSp => tides_CSp + if (present(SAL_CSp)) then + CS%SAL_CSp => SAL_CSp endif ! Read all relevant parameters and write them to the model log. @@ -4523,11 +4525,13 @@ subroutine barotropic_init(u, v, h, eta, Time, G, GV, US, param_file, diag, CS, "latter takes precedence.", default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701) - call get_param(param_file, mdl, "TIDES", CS%tides, & + call get_param(param_file, mdl, "TIDES", use_tides, & "If true, apply tidal momentum forcing.", default=.false.) + call get_param(param_file, mdl, "CALCULATE_SAL", CS%calculate_SAL, & + "If true, calculate self-attraction and loading.", default=use_tides) det_de = 0.0 - if (CS%tides .and. associated(CS%tides_CSp)) & - call tidal_forcing_sensitivity(G, CS%tides_CSp, det_de) + if (CS%calculate_SAL .and. associated(CS%SAL_CSp)) & + call scalar_SAL_sensitivity(CS%SAL_CSp, det_de) call get_param(param_file, mdl, "BAROTROPIC_TIDAL_SAL_BUG", CS%tidal_sal_bug, & "If true, the tidal self-attraction and loading anomaly in the barotropic "//& "solver has the wrong sign, replicating a long-standing bug with a scalar "//& diff --git a/src/core/MOM_dynamics_split_RK2.F90 b/src/core/MOM_dynamics_split_RK2.F90 index eebb7d6b8a..ae9e304736 100644 --- a/src/core/MOM_dynamics_split_RK2.F90 +++ b/src/core/MOM_dynamics_split_RK2.F90 @@ -60,6 +60,8 @@ module MOM_dynamics_split_RK2 use MOM_PressureForce, only : PressureForce_init use MOM_set_visc, only : set_viscous_ML, set_visc_CS use MOM_thickness_diffuse, only : thickness_diffuse_CS +use MOM_self_attr_load, only : SAL_CS +use MOM_self_attr_load, only : SAL_init, SAL_end use MOM_tidal_forcing, only : tidal_forcing_CS use MOM_tidal_forcing, only : tidal_forcing_init, tidal_forcing_end use MOM_unit_scaling, only : unit_scale_type @@ -159,6 +161,7 @@ module MOM_dynamics_split_RK2 !! end of the timestep have been stored for use in the next !! predictor step. This is used to accomodate various generations !! of restart files. + logical :: calculate_SAL !< If true, calculate self-attraction and loading. logical :: use_tides !< If true, tidal forcing is enabled. logical :: remap_aux !< If true, apply ALE remapping to all of the auxiliary 3-D !! variables that are needed to reproduce across restarts, @@ -233,6 +236,8 @@ module MOM_dynamics_split_RK2 type(set_visc_CS), pointer :: set_visc_CSp => NULL() !> A pointer to the barotropic stepping control structure type(barotropic_CS) :: barotropic_CSp + !> A pointer to the SAL control structure + type(SAL_CS) :: SAL_CSp !> A pointer to the tidal forcing control structure type(tidal_forcing_CS) :: tides_CSp !> A pointer to the ALE control structure. @@ -1276,6 +1281,8 @@ subroutine initialize_dyn_split_RK2(u, v, h, uh, vh, eta, Time, G, GV, US, param call log_version(param_file, mdl, version, "") call get_param(param_file, mdl, "TIDES", CS%use_tides, & "If true, apply tidal momentum forcing.", default=.false.) + call get_param(param_file, mdl, "CALCULATE_SAL", CS%calculate_SAL, & + "If true, calculate self-attraction and loading.", default=CS%use_tides) call get_param(param_file, mdl, "BE", CS%be, & "If SPLIT is true, BE determines the relative weighting "//& "of a 2nd-order Runga-Kutta baroclinic time stepping "//& @@ -1376,9 +1383,10 @@ subroutine initialize_dyn_split_RK2(u, v, h, uh, vh, eta, Time, G, GV, US, param call continuity_init(Time, G, GV, US, param_file, diag, CS%continuity_CSp) cont_stencil = continuity_stencil(CS%continuity_CSp) call CoriolisAdv_init(Time, G, GV, US, param_file, diag, CS%ADp, CS%CoriolisAdv) + if (CS%calculate_SAL) call SAL_init(G, US, param_file, CS%SAL_CSp) if (CS%use_tides) call tidal_forcing_init(Time, G, US, param_file, CS%tides_CSp) call PressureForce_init(Time, G, GV, US, param_file, diag, CS%PressureForce_CSp, & - CS%tides_CSp) + CS%SAL_CSp, CS%tides_CSp) call hor_visc_init(Time, G, GV, US, param_file, diag, CS%hor_visc, ADp=CS%ADp) call vertvisc_init(MIS, Time, G, GV, US, param_file, diag, CS%ADp, dirs, & ntrunc, CS%vertvisc_CSp) @@ -1414,7 +1422,7 @@ subroutine initialize_dyn_split_RK2(u, v, h, uh, vh, eta, Time, G, GV, US, param call barotropic_init(u, v, h, CS%eta, Time, G, GV, US, param_file, diag, & CS%barotropic_CSp, restart_CS, calc_dtbt, CS%BT_cont, & - CS%tides_CSp) + CS%SAL_CSp) if (.not. query_initialized(CS%diffu, "diffu", restart_CS) .or. & .not. query_initialized(CS%diffv, "diffv", restart_CS)) then @@ -1710,6 +1718,7 @@ subroutine end_dyn_split_RK2(CS) deallocate(CS%vertvisc_CSp) call hor_visc_end(CS%hor_visc) + if (CS%calculate_SAL) call SAL_end(CS%SAL_CSp) if (CS%use_tides) call tidal_forcing_end(CS%tides_CSp) call CoriolisAdv_end(CS%CoriolisAdv) diff --git a/src/core/MOM_dynamics_unsplit.F90 b/src/core/MOM_dynamics_unsplit.F90 index 80f7853744..c87e6e9958 100644 --- a/src/core/MOM_dynamics_unsplit.F90 +++ b/src/core/MOM_dynamics_unsplit.F90 @@ -87,7 +87,8 @@ module MOM_dynamics_unsplit use MOM_open_boundary, only : open_boundary_zero_normal_flow use MOM_PressureForce, only : PressureForce, PressureForce_init, PressureForce_CS use MOM_set_visc, only : set_viscous_ML, set_visc_CS -use MOM_tidal_forcing, only : tidal_forcing_init, tidal_forcing_CS +use MOM_self_attr_load, only : SAL_init, SAL_end, SAL_CS +use MOM_tidal_forcing, only : tidal_forcing_init, tidal_forcing_end, tidal_forcing_CS use MOM_unit_scaling, only : unit_scale_type use MOM_vert_friction, only : vertvisc, vertvisc_coef, vertvisc_init, vertvisc_CS use MOM_verticalGrid, only : verticalGrid_type, get_thickness_units @@ -120,6 +121,8 @@ module MOM_dynamics_unsplit !! and in the calculation of the turbulent mixed layer properties !! for viscosity. The default should be true, but it is false. logical :: debug !< If true, write verbose checksums for debugging purposes. + logical :: calculate_SAL !< If true, calculate self-attraction and loading. + logical :: use_tides !< If true, tidal forcing is enabled. logical :: module_is_initialized = .false. !< Record whether this module has been initialized. @@ -153,6 +156,8 @@ module MOM_dynamics_unsplit type(vertvisc_CS), pointer :: vertvisc_CSp => NULL() !> A pointer to the set_visc control structure type(set_visc_CS), pointer :: set_visc_CSp => NULL() + !> A pointer to the SAL control structure + type(SAL_CS) :: SAL_CSp !> A pointer to the tidal forcing control structure type(tidal_forcing_CS) :: tides_CSp !> A pointer to the ALE control structure. @@ -625,7 +630,6 @@ subroutine initialize_dyn_unsplit(u, v, h, Time, G, GV, US, param_file, diag, CS character(len=48) :: flux_units ! This include declares and sets the variable "version". # include "version_variable.h" - logical :: use_tides integer :: isd, ied, jsd, jed, nz, IsdB, IedB, JsdB, JedB isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed ; nz = GV%ke IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB @@ -650,8 +654,10 @@ subroutine initialize_dyn_unsplit(u, v, h, Time, G, GV, US, param_file, diag, CS call get_param(param_file, mdl, "DEBUG", CS%debug, & "If true, write out verbose debugging data.", & default=.false., debuggingParam=.true.) - call get_param(param_file, mdl, "TIDES", use_tides, & + call get_param(param_file, mdl, "TIDES", CS%use_tides, & "If true, apply tidal momentum forcing.", default=.false.) + call get_param(param_file, mdl, "CALCULATE_SAL", CS%calculate_SAL, & + "If true, calculate self-attraction and loading.", default=CS%use_tides) allocate(CS%taux_bot(IsdB:IedB,jsd:jed), source=0.0) allocate(CS%tauy_bot(isd:ied,JsdB:JedB), source=0.0) @@ -668,9 +674,10 @@ subroutine initialize_dyn_unsplit(u, v, h, Time, G, GV, US, param_file, diag, CS call continuity_init(Time, G, GV, US, param_file, diag, CS%continuity_CSp) cont_stencil = continuity_stencil(CS%continuity_CSp) call CoriolisAdv_init(Time, G, GV, US, param_file, diag, CS%ADp, CS%CoriolisAdv) - if (use_tides) call tidal_forcing_init(Time, G, US, param_file, CS%tides_CSp) + if (CS%calculate_SAL) call SAL_init(G, US, param_file, CS%SAL_CSp) + if (CS%use_tides) call tidal_forcing_init(Time, G, US, param_file, CS%tides_CSp) call PressureForce_init(Time, G, GV, US, param_file, diag, CS%PressureForce_CSp, & - CS%tides_CSp) + CS%SAL_CSp, CS%tides_CSp) call hor_visc_init(Time, G, GV, US, param_file, diag, CS%hor_visc) call vertvisc_init(MIS, Time, G, GV, US, param_file, diag, CS%ADp, dirs, & ntrunc, CS%vertvisc_CSp) @@ -722,6 +729,9 @@ subroutine end_dyn_unsplit(CS) DEALLOC_(CS%CAu) ; DEALLOC_(CS%CAv) DEALLOC_(CS%PFu) ; DEALLOC_(CS%PFv) + if (CS%calculate_SAL) call SAL_end(CS%SAL_CSp) + if (CS%use_tides) call tidal_forcing_end(CS%tides_CSp) + deallocate(CS) end subroutine end_dyn_unsplit diff --git a/src/core/MOM_dynamics_unsplit_RK2.F90 b/src/core/MOM_dynamics_unsplit_RK2.F90 index d1afca51d9..b515229566 100644 --- a/src/core/MOM_dynamics_unsplit_RK2.F90 +++ b/src/core/MOM_dynamics_unsplit_RK2.F90 @@ -86,7 +86,8 @@ module MOM_dynamics_unsplit_RK2 use MOM_open_boundary, only : open_boundary_zero_normal_flow use MOM_PressureForce, only : PressureForce, PressureForce_init, PressureForce_CS use MOM_set_visc, only : set_viscous_ML, set_visc_CS -use MOM_tidal_forcing, only : tidal_forcing_init, tidal_forcing_CS +use MOM_self_attr_load, only : SAL_init, SAL_end, SAL_CS +use MOM_tidal_forcing, only : tidal_forcing_init, tidal_forcing_end, tidal_forcing_CS use MOM_unit_scaling, only : unit_scale_type use MOM_vert_friction, only : vertvisc, vertvisc_coef, vertvisc_init, vertvisc_CS use MOM_verticalGrid, only : verticalGrid_type, get_thickness_units @@ -123,6 +124,8 @@ module MOM_dynamics_unsplit_RK2 !! turbulent mixed layer properties for viscosity. !! The default should be true, but it is false. logical :: debug !< If true, write verbose checksums for debugging purposes. + logical :: calculate_SAL !< If true, calculate self-attraction and loading. + logical :: use_tides !< If true, tidal forcing is enabled. logical :: module_is_initialized = .false. !< Record whether this module has been initialized. @@ -156,6 +159,8 @@ module MOM_dynamics_unsplit_RK2 type(vertvisc_CS), pointer :: vertvisc_CSp => NULL() !> A pointer to the set_visc control structure type(set_visc_CS), pointer :: set_visc_CSp => NULL() + !> A pointer to the SAL control structure + type(SAL_CS) :: SAL_CSp !> A pointer to the tidal forcing control structure type(tidal_forcing_CS) :: tides_CSp !> A pointer to the ALE control structure. @@ -573,7 +578,6 @@ subroutine initialize_dyn_unsplit_RK2(u, v, h, Time, G, GV, US, param_file, diag character(len=48) :: flux_units ! This include declares and sets the variable "version". # include "version_variable.h" - logical :: use_tides integer :: isd, ied, jsd, jed, nz, IsdB, IedB, JsdB, JedB isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed ; nz = GV%ke IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB @@ -614,8 +618,10 @@ subroutine initialize_dyn_unsplit_RK2(u, v, h, Time, G, GV, US, param_file, diag call get_param(param_file, mdl, "DEBUG", CS%debug, & "If true, write out verbose debugging data.", & default=.false., debuggingParam=.true.) - call get_param(param_file, mdl, "TIDES", use_tides, & + call get_param(param_file, mdl, "TIDES", CS%use_tides, & "If true, apply tidal momentum forcing.", default=.false.) + call get_param(param_file, mdl, "CALCULATE_SAL", CS%calculate_SAL, & + "If true, calculate self-attraction and loading.", default=CS%use_tides) allocate(CS%taux_bot(IsdB:IedB,jsd:jed), source=0.0) allocate(CS%tauy_bot(isd:ied,JsdB:JedB), source=0.0) @@ -632,9 +638,10 @@ subroutine initialize_dyn_unsplit_RK2(u, v, h, Time, G, GV, US, param_file, diag call continuity_init(Time, G, GV, US, param_file, diag, CS%continuity_CSp) cont_stencil = continuity_stencil(CS%continuity_CSp) call CoriolisAdv_init(Time, G, GV, US, param_file, diag, CS%ADp, CS%CoriolisAdv) - if (use_tides) call tidal_forcing_init(Time, G, US, param_file, CS%tides_CSp) + if (CS%calculate_SAL) call SAL_init(G, US, param_file, CS%SAL_CSp) + if (CS%use_tides) call tidal_forcing_init(Time, G, US, param_file, CS%tides_CSp) call PressureForce_init(Time, G, GV, US, param_file, diag, CS%PressureForce_CSp, & - CS%tides_CSp) + CS%SAL_CSp, CS%tides_CSp) call hor_visc_init(Time, G, GV, US, param_file, diag, CS%hor_visc) call vertvisc_init(MIS, Time, G, GV, US, param_file, diag, CS%ADp, dirs, & ntrunc, CS%vertvisc_CSp) @@ -685,6 +692,9 @@ subroutine end_dyn_unsplit_RK2(CS) DEALLOC_(CS%CAu) ; DEALLOC_(CS%CAv) DEALLOC_(CS%PFu) ; DEALLOC_(CS%PFv) + if (CS%calculate_SAL) call SAL_end(CS%SAL_CSp) + if (CS%use_tides) call tidal_forcing_end(CS%tides_CSp) + deallocate(CS) end subroutine end_dyn_unsplit_RK2 diff --git a/src/parameterizations/lateral/MOM_load_love_numbers.F90 b/src/parameterizations/lateral/MOM_load_love_numbers.F90 index 84819b5915..3d573d894d 100644 --- a/src/parameterizations/lateral/MOM_load_love_numbers.F90 +++ b/src/parameterizations/lateral/MOM_load_love_numbers.F90 @@ -1452,30 +1452,32 @@ module MOM_load_love_numbers /), (/4, lmax+1/)) !< Load Love numbers !> \namespace mom_load_love_numbers -!! This module serves the sole purpose of storing load Love number. The Love numbers are used for the self-attraction -!! and loading (SAL) calculation, which is currently embedded in MOM_tidal_forcing module. This separate module ensures -!! the readability of the tidal module. +!! This module serves the sole purpose of storing load Love number. The Love numbers are used for the spherical harmonic +!! self-attraction and loading (SAL) calculation in MOM_self_attr_load module. This separate module ensures readability +!! of the SAL module. !! !! Variable Love_Data stores the Love numbers up to degree 1440. From left to right: degree, h, l, and k. Data in this !! module is imported from SAL calculation in Model for Prediction Across Scales (MPAS)-Ocean developed by Los Alamos -!! National Laboratory and University of Michigan (Barton et al. (2022) and Brus et al. (2022)). The load Love numbers +!! National Laboratory and University of Michigan [Barton et al. (2022) and Brus et al. (2022)]. The load Love numbers !! are from Wang et al. (2012), which are in the center of mass of total Earth system reference frame (CM). When used, -!! Love numbers with degree<2 should be converted to center of mass solid Earth reference frame (CF) (Blewitt (2003)), +!! Love numbers with degree<2 should be converted to center of mass solid Earth reference frame (CF) [Blewitt (2003)], !! as in subroutine calc_love_scaling in MOM_tidal_forcing module. !! !! References: !! -!! Barton, K.N., Nairita, P., Brus, S.R., Petersen, M.R., Arbic, B.K., Engwirda, D., Roberts, A.F., Westerink, J., -!! Wirasaet, D., and Schindelegger, M., 2022: Performance of Model for Prediction Across Scales (MPAS) Ocean as a -!! Global Barotropic Tide Model. Journal of Advances in Modeling Earth Systems, in review. +!! Barton, K.N., Pal, N., Brus, S.R., Petersen, M.R., Arbic, B.K., Engwirda, D., Roberts, A.F., Westerink, J.J., +!! Wirasaet, D. and Schindelegger, M., 2022. Global Barotropic Tide Modeling Using Inline Self‐Attraction and Loading in +!! MPAS‐Ocean. Journal of Advances in Modeling Earth Systems, 14(11), p.e2022MS003207. +!! https://doi.org/10.1029/2022MS003207 !! !! Blewitt, G., 2003. Self‐consistency in reference frames, geocenter definition, and surface loading of the solid !! Earth. Journal of geophysical research: solid earth, 108(B2). !! https://doi.org/10.1029/2002JB002082 !! -!! Brus, S.R., Barton, K.N., Nairita, P., Roberts, A.F., Engwirda, D., Petersen, M.R., Arbic, B.K., Wirasaet, D., -!! Westerink, J., and Schindelegger, M., 2022: Scalable self attraction and loading calculations for unstructured ocean -!! models. Ocean Modelling, in review. +!! Brus, S.R., Barton, K.N., Pal, N., Roberts, A.F., Engwirda, D., Petersen, M.R., Arbic, B.K., Wirasaet, D., +!! Westerink, J.J. and Schindelegger, M., 2023. Scalable self attraction and loading calculations for unstructured ocean +!! tide models. Ocean Modelling, p.102160. +!! https://doi.org/10.1016/j.ocemod.2023.102160 !! !! Wang, H., Xiang, L., Jia, L., Jiang, L., Wang, Z., Hu, B. and Gao, P., 2012. Load Love numbers and Green's functions !! for elastic Earth models PREM, iasp91, ak135, and modified models with refined crustal structure from Crust 2.0. diff --git a/src/parameterizations/lateral/MOM_self_attr_load.F90 b/src/parameterizations/lateral/MOM_self_attr_load.F90 new file mode 100644 index 0000000000..fb27cfa346 --- /dev/null +++ b/src/parameterizations/lateral/MOM_self_attr_load.F90 @@ -0,0 +1,271 @@ +module MOM_self_attr_load + +use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, CLOCK_MODULE +use MOM_domains, only : pass_var +use MOM_error_handler, only : MOM_error, FATAL, WARNING +use MOM_file_parser, only : get_param, log_version, param_file_type +use MOM_grid, only : ocean_grid_type +use MOM_unit_scaling, only : unit_scale_type +use MOM_spherical_harmonics, only : spherical_harmonics_init, spherical_harmonics_end, order2index, calc_lmax +use MOM_spherical_harmonics, only : spherical_harmonics_forward, spherical_harmonics_inverse +use MOM_spherical_harmonics, only : sht_CS +use MOM_load_love_numbers, only : Love_Data + +implicit none ; private + +public calc_SAL, scalar_SAL_sensitivity, SAL_init, SAL_end + +#include + +!> The control structure for the MOM_self_attr_load module +type, public :: SAL_CS ; private + logical :: use_sal_scalar !< If true, use the scalar approximation when + !! calculating self-attraction and loading. + real :: sal_scalar !< The constant of proportionality between sea surface + !! height (really it should be bottom pressure) anomalies + !! and bottom geopotential anomalies [nondim]. + logical :: use_prev_tides !< If true, use the SAL from the previous iteration of the tides + !! to facilitate convergence. + logical :: use_sal_sht !< If true, use online spherical harmonics to calculate SAL + type(sht_CS) :: sht !< Spherical harmonic transforms (SHT) for SAL + integer :: sal_sht_Nd !< Maximum degree for SHT [nodim] + real, allocatable :: Love_Scaling(:) !< Love number for each SHT mode [nodim] + real, allocatable :: Snm_Re(:), & !< Real and imaginary SHT coefficient for SHT SAL + Snm_Im(:) !< [Z ~> m] +end type SAL_CS + +integer :: id_clock_SAL !< CPU clock for self-attraction and loading + +contains + +!> This subroutine calculates seawater self-attraction and loading based on sea surface height. This should +!! be changed into bottom pressure anomaly in the future. Note that the SAL calculation applies to all motions +!! across the spectrum. Tidal-specific methods that assume periodicity, i.e. iterative and read-in SAL, are +!! stored in MOM_tidal_forcing module. +subroutine calc_SAL(eta, eta_sal, G, CS) + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + real, dimension(SZI_(G),SZJ_(G)), intent(in) :: eta !< The sea surface height anomaly from + !! a time-mean geoid [Z ~> m]. + real, dimension(SZI_(G),SZJ_(G)), intent(out) :: eta_sal !< The sea surface height anomaly from + !! self-attraction and loading [Z ~> m]. + type(SAL_CS), intent(inout) :: CS !< The control structure returned by a previous call to SAL_init. + + ! Local variables + integer :: n, m, l + integer :: Isq, Ieq, Jsq, Jeq + integer :: i, j + real :: eta_prop + + call cpu_clock_begin(id_clock_SAL) + + Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB + + ! use the scalar approximation, iterative tidal SAL or no SAL + call scalar_SAL_sensitivity(CS, eta_prop) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + eta_sal(i,j) = eta_prop*eta(i,j) + enddo ; enddo + + if (CS%use_sal_sht) then ! use the spherical harmonics method + call spherical_harmonics_forward(G, CS%sht, eta, CS%Snm_Re, CS%Snm_Im, CS%sal_sht_Nd) + + ! Multiply scaling factors to each mode + do m = 0,CS%sal_sht_Nd + l = order2index(m, CS%sal_sht_Nd) + do n = m,CS%sal_sht_Nd + CS%Snm_Re(l+n-m) = CS%Snm_Re(l+n-m) * CS%Love_Scaling(l+n-m) + CS%Snm_Im(l+n-m) = CS%Snm_Im(l+n-m) * CS%Love_Scaling(l+n-m) + enddo + enddo + + call spherical_harmonics_inverse(G, CS%sht, CS%Snm_Re, CS%Snm_Im, eta_sal, CS%sal_sht_Nd) + + call pass_var(eta_sal, G%domain) + endif + + call cpu_clock_end(id_clock_SAL) +end subroutine calc_SAL + +!> This subroutine calculates the partial derivative of the local geopotential height with the input +!! sea surface height due to the scalar approximation of self-attraction and loading. +subroutine scalar_SAL_sensitivity(CS, deta_sal_deta) + type(SAL_CS), intent(in) :: CS !< The control structure returned by a previous call to SAL_init. + real, intent(out) :: deta_sal_deta !< The partial derivative of eta_sal with + !! the local value of eta [nondim]. + + if (CS%USE_SAL_SCALAR .and. CS%USE_PREV_TIDES) then + deta_sal_deta = 2.0*CS%SAL_SCALAR + elseif (CS%USE_SAL_SCALAR .or. CS%USE_PREV_TIDES) then + deta_sal_deta = CS%SAL_SCALAR + else + deta_sal_deta = 0.0 + endif +end subroutine scalar_SAL_sensitivity + +!> This subroutine calculates coefficients of the spherical harmonic modes for self-attraction and loading. +!! The algorithm is based on the SAL implementation in MPAS-ocean, which was modified by Kristin Barton from +!! routine written by K. Quinn (March 2010) and modified by M. Schindelegger (May 2017). +subroutine calc_love_scaling(nlm, rhoW, rhoE, Love_Scaling) + integer, intent(in) :: nlm !< Maximum spherical harmonics degree [nondim] + real, intent(in) :: rhoW !< The average density of sea water [R ~> kg m-3] + real, intent(in) :: rhoE !< The average density of Earth [R ~> kg m-3] + real, dimension(:), intent(out) :: Love_Scaling !< Scaling factors for inverse SHT [nondim] + + ! Local variables + real, dimension(:), allocatable :: HDat, LDat, KDat ! Love numbers converted in CF reference frames + real :: H1, L1, K1 ! Temporary variables to store degree 1 Love numbers + integer :: n_tot ! Size of the stored Love numbers + integer :: n, m, l + + n_tot = size(Love_Data, dim=2) + + if (nlm+1 > n_tot) call MOM_error(FATAL, "MOM_tidal_forcing " // & + "calc_love_scaling: maximum spherical harmonics degree is larger than " // & + "the size of the stored Love numbers in MOM_load_love_number.") + + allocate(HDat(nlm+1), LDat(nlm+1), KDat(nlm+1)) + HDat(:) = Love_Data(2,1:nlm+1) ; LDat(:) = Love_Data(3,1:nlm+1) ; KDat(:) = Love_Data(4,1:nlm+1) + + ! Convert reference frames from CM to CF + if (nlm > 0) then + H1 = HDat(2) ; L1 = LDat(2) ; K1 = KDat(2) + HDat(2) = ( 2.0 / 3.0) * (H1 - L1) + LDat(2) = (-1.0 / 3.0) * (H1 - L1) + KDat(2) = (-1.0 / 3.0) * H1 - (2.0 / 3.0) * L1 - 1.0 + endif + + do m=0,nlm ; do n=m,nlm + l = order2index(m,nlm) + Love_Scaling(l+n-m) = (3.0 / real(2*n+1)) * (rhoW / rhoE) * (1.0 + KDat(n+1) - HDat(n+1)) + enddo ; enddo +end subroutine calc_love_scaling + +!> This subroutine initializeds the self-attraction and loading control structure. +subroutine SAL_init(G, US, param_file, CS) + type(ocean_grid_type), intent(inout) :: G !< The ocean's 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(SAL_CS), intent(inout) :: CS !< Self-attraction and loading control structure + +# include "version_variable.h" + character(len=40) :: mdl = "MOM_self_attr_load" ! This module's name. + integer :: lmax ! Total modes of the real spherical harmonics [nondim] + real :: rhoW ! The average density of sea water [R ~> kg m-3]. + real :: rhoE ! The average density of Earth [R ~> kg m-3]. + + logical :: calculate_sal + logical :: tides, tidal_sal_from_file + + ! Read all relevant parameters and write them to the model log. + call log_version(param_file, mdl, version, "") + + call get_param(param_file, '', "TIDES", tides, default=.false., do_not_log=.True.) + + CS%use_prev_tides = .false. + tidal_sal_from_file = .false. + if (tides) then + call get_param(param_file, '', "USE_PREVIOUS_TIDES", CS%use_prev_tides,& + default=.false., do_not_log=.True.) + call get_param(param_file, '', "TIDAL_SAL_FROM_FILE", tidal_sal_from_file,& + default=.false., do_not_log=.True.) + endif + + call get_param(param_file, mdl, "TIDE_USE_SAL_SCALAR", CS%use_sal_scalar, & + "If true and TIDES is true, use the scalar approximation "//& + "when calculating self-attraction and loading.", & + default=.not.tidal_sal_from_file) + if (CS%use_sal_scalar .or. CS%use_prev_tides) & + call get_param(param_file, mdl, "TIDE_SAL_SCALAR_VALUE", CS%sal_scalar, & + "The constant of proportionality between sea surface "//& + "height (really it should be bottom pressure) anomalies "//& + "and bottom geopotential anomalies. This is only used if "//& + "TIDES and TIDE_USE_SAL_SCALAR are true.", units="m m-1", & + fail_if_missing=.true.) + + call get_param(param_file, mdl, "TIDAL_SAL_SHT", CS%use_sal_sht, & + "If true, use the online spherical harmonics method to calculate "//& + "self-attraction and loading term in tides.", default=.false.) + + call get_param(param_file, mdl, "CALCULATE_SAL", calculate_sal, & + "If true, calculate self-attraction and loading.", default=tides) + + ! ! Default USE_SAL is TRUE for now to keep backward compatibility with old MOM_INPUT files. It should be changed to + ! ! FALSE in the future (mostly to avoid the SSH calculations in MOM_PressureForce). In that case, the following check + ! ! informs prior tidal experiments that use scalar or iterative SAL to include USE_SAL flag, as the USE_SAL flag + ! ! overrules the option flags. + ! if ((.not. calculate_sal) .and. (CS%use_prev_tides .or. CS%use_sal_scalar .or. CS%use_sal_sht)) & + ! call MOM_error(FATAL, trim(mdl)//": USE_SAL is False but one of the options is True. Nothing will happen.") + + if (CS%use_sal_sht) then + call get_param(param_file, mdl, "TIDAL_SAL_SHT_DEGREE", CS%sal_sht_Nd, & + "The maximum degree of the spherical harmonics transformation used for "// & + "calculating the self-attraction and loading term.", & + default=0, do_not_log=.not. CS%use_sal_sht) + call get_param(param_file, mdl, "RHO_0", rhoW, default=1035.0, scale=US%kg_m3_to_R, do_not_log=.True.) + call get_param(param_file, mdl, "RHO_E", rhoE, & + "The mean solid earth density. This is used for calculating the "// & + "self-attraction and loading term.", units="kg m-3", & + default=5517.0, scale=US%kg_m3_to_R, do_not_log=.not. CS%use_sal_sht) + lmax = calc_lmax(CS%sal_sht_Nd) + allocate(CS%Snm_Re(lmax)); CS%Snm_Re(:) = 0.0 + allocate(CS%Snm_Im(lmax)); CS%Snm_Im(:) = 0.0 + + allocate(CS%Love_Scaling(lmax)); CS%Love_Scaling(:) = 0.0 + call calc_love_scaling(CS%sal_sht_Nd, rhoW, rhoE, CS%Love_Scaling) + call spherical_harmonics_init(G, param_file, CS%sht) + endif + + id_clock_SAL = cpu_clock_id('(Ocean SAL)', grain=CLOCK_MODULE) + +end subroutine SAL_init + +!> This subroutine deallocates memory associated with the SAL module. +subroutine SAL_end(CS) + type(SAL_CS), intent(inout) :: CS !< The control structure returned by a previous call + !! to SAL_init; it is deallocated here. + if (CS%use_sal_sht) then + if (allocated(CS%Love_Scaling)) deallocate(CS%Love_Scaling) + if (allocated(CS%Snm_Re)) deallocate(CS%Snm_Re) + if (allocated(CS%Snm_Im)) deallocate(CS%Snm_Im) + call spherical_harmonics_end(CS%sht) + endif +end subroutine SAL_end + +!> \namespace self_attr_load +!! +!! This module contains methods to calculate self-attraction and loading (SAL) as a function of sea surface height (SSH) +!! (rather, it should be bottom pressure anomaly). SAL is primarily used for fast evolving processes like tides or +!! storm surges, but the effect applys to all motions. +!! +!! If TIDE_USE_SAL_SCALAR is true, a scalar approximiation is applied (Accad and Pekeris 1978) and the SAL is simply +!! a fraction (set by TIDE_SAL_SCALAR_VALUE, usualy around 10% for global tides) of local SSH . For the tides, the +!! scalar approximation can also be used to iterate the SAL to convergence [see USE_PREVIOUS_TIDES in MOM_tidal_forcing, +!! Arbic et al. (2004)]. +!! +!! If TIDAL_SAL_SHT is true, a more accurate online spherical harmonic transforms are used to calculate SAL. +!! Subroutines in module MOM_spherical_harmonics are called and the degree of spherical harmonic transforms is set by +!! TIDAL_SAL_SHT_DEGREE. The algorithm is based on SAL calculation in Model for Prediction Across Scales (MPAS)-Ocean +!! developed by Los Alamos National Laboratory and University of Michigan [Barton et al. (2022) and Brus et al. (2023)]. +!! +!! References: +!! +!! Accad, Y. and Pekeris, C.L., 1978. Solution of the tidal equations for the M2 and S2 tides in the world oceans from a +!! knowledge of the tidal potential alone. Philosophical Transactions of the Royal Society of London. Series A, +!! Mathematical and Physical Sciences, 290(1368), pp.235-266. +!! https://doi.org/10.1098/rsta.1978.0083 +!! +!! Arbic, B.K., Garner, S.T., Hallberg, R.W. and Simmons, H.L., 2004. The accuracy of surface elevations in forward +!! global barotropic and baroclinic tide models. Deep Sea Research Part II: Topical Studies in Oceanography, 51(25-26), +!! pp.3069-3101. +!! https://doi.org/10.1016/j.dsr2.2004.09.014 +!! +!! Barton, K.N., Pal, N., Brus, S.R., Petersen, M.R., Arbic, B.K., Engwirda, D., Roberts, A.F., Westerink, J.J., +!! Wirasaet, D. and Schindelegger, M., 2022. Global Barotropic Tide Modeling Using Inline Self‐Attraction and Loading in +!! MPAS‐Ocean. Journal of Advances in Modeling Earth Systems, 14(11), p.e2022MS003207. +!! https://doi.org/10.1029/2022MS003207 +!! +!! Brus, S.R., Barton, K.N., Pal, N., Roberts, A.F., Engwirda, D., Petersen, M.R., Arbic, B.K., Wirasaet, D., +!! Westerink, J.J. and Schindelegger, M., 2023. Scalable self attraction and loading calculations for unstructured ocean +!! tide models. Ocean Modelling, p.102160. +!! https://doi.org/10.1016/j.ocemod.2023.102160 +end module MOM_self_attr_load diff --git a/src/parameterizations/lateral/MOM_spherical_harmonics.F90 b/src/parameterizations/lateral/MOM_spherical_harmonics.F90 index 95a9df808c..b20df036e0 100644 --- a/src/parameterizations/lateral/MOM_spherical_harmonics.F90 +++ b/src/parameterizations/lateral/MOM_spherical_harmonics.F90 @@ -330,7 +330,7 @@ end function order2index !! Currently, the transforms are for t-cell fields only. !! !! This module is stemmed from SAL calculation in Model for Prediction Across Scales (MPAS)-Ocean developed by Los -!! Alamos National Laboratory and University of Michigan (Barton et al. (2022) and Brus et al. (2022)). The algorithm +!! Alamos National Laboratory and University of Michigan [Barton et al. (2022) and Brus et al. (2023)]. The algorithm !! for forward and inverse transforms loosely follows Schaeffer (2013). !! !! In forward transform, a two-dimensional physical field can be projected into a series of spherical harmonics. The @@ -368,13 +368,15 @@ end function order2index !! !! References: !! -!! Barton, K.N., Nairita, P., Brus, S.R., Petersen, M.R., Arbic, B.K., Engwirda, D., Roberts, A.F., Westerink, J., -!! Wirasaet, D., and Schindelegger, M., 2022: Performance of Model for Prediction Across Scales (MPAS) Ocean as a -!! Global Barotropic Tide Model. Journal of Advances in Modeling Earth Systems, in review. +!! Barton, K.N., Pal, N., Brus, S.R., Petersen, M.R., Arbic, B.K., Engwirda, D., Roberts, A.F., Westerink, J.J., +!! Wirasaet, D. and Schindelegger, M., 2022. Global Barotropic Tide Modeling Using Inline Self‐Attraction and Loading in +!! MPAS‐Ocean. Journal of Advances in Modeling Earth Systems, 14(11), p.e2022MS003207. +!! https://doi.org/10.1029/2022MS003207 !! -!! Brus, S.R., Barton, K.N., Nairita, P., Roberts, A.F., Engwirda, D., Petersen, M.R., Arbic, B.K., Wirasaet, D., -!! Westerink, J., and Schindelegger, M., 2022: Scalable self attraction and loading calculations for unstructured ocean -!! models. Ocean Modelling, in review. +!! Brus, S.R., Barton, K.N., Pal, N., Roberts, A.F., Engwirda, D., Petersen, M.R., Arbic, B.K., Wirasaet, D., +!! Westerink, J.J. and Schindelegger, M., 2023. Scalable self attraction and loading calculations for unstructured ocean +!! tide models. Ocean Modelling, p.102160. +!! https://doi.org/10.1016/j.ocemod.2023.102160 !! !! Schaeffer, N., 2013. Efficient spherical harmonic transforms aimed at pseudospectral numerical simulations. !! Geochemistry, Geophysics, Geosystems, 14(3), pp.751-758. diff --git a/src/parameterizations/lateral/MOM_tidal_forcing.F90 b/src/parameterizations/lateral/MOM_tidal_forcing.F90 index b2fd8f0ea5..358ec3dc57 100644 --- a/src/parameterizations/lateral/MOM_tidal_forcing.F90 +++ b/src/parameterizations/lateral/MOM_tidal_forcing.F90 @@ -12,15 +12,10 @@ module MOM_tidal_forcing use MOM_io, only : field_exists, file_exists, MOM_read_data use MOM_time_manager, only : set_date, time_type, time_type_to_real, operator(-) use MOM_unit_scaling, only : unit_scale_type -use MOM_spherical_harmonics, only : spherical_harmonics_init, spherical_harmonics_end, order2index, calc_lmax -use MOM_spherical_harmonics, only : spherical_harmonics_forward, spherical_harmonics_inverse -use MOM_spherical_harmonics, only : sht_CS -use MOM_load_love_numbers, only : Love_Data implicit none ; private public calc_tidal_forcing, tidal_forcing_init, tidal_forcing_end -public tidal_forcing_sensitivity ! MOM_open_boundary uses the following to set tides on the boundary. public astro_longitudes_init, eq_phase, nodal_fu, tidal_frequency @@ -38,8 +33,6 @@ module MOM_tidal_forcing !> The control structure for the MOM_tidal_forcing module type, public :: tidal_forcing_CS ; private - logical :: use_sal_scalar !< If true, use the scalar approximation when - !! calculating self-attraction and loading. logical :: tidal_sal_from_file !< If true, Read the tidal self-attraction !! and loading from input files, specified !! by TIDAL_INPUT_FILE. @@ -49,7 +42,6 @@ module MOM_tidal_forcing !! equilibrium tide. Set to false if providing tidal phases !! that have already been shifted by the !! astronomical/equilibrium argument. - logical :: tidal_sal_sht !< If true, use online spherical harmonics to calculate SAL real :: sal_scalar !< The constant of proportionality between sea surface !! height (really it should be bottom pressure) anomalies !! and bottom geopotential anomalies [nondim]. @@ -76,15 +68,9 @@ module MOM_tidal_forcing cosphase_prev(:,:,:), & !< The cosine of the phase of the amphidromes in the previous tidal solutions [nondim]. sinphase_prev(:,:,:), & !< The sine of the phase of the amphidromes in the previous tidal solutions [nondim]. amp_prev(:,:,:) !< The amplitude of the previous tidal solution [Z ~> m]. - type(sht_CS) :: sht !< Spherical harmonic transforms (SHT) for SAL - integer :: sal_sht_Nd !< Maximum degree for SHT [nondim] - real, allocatable :: Love_Scaling(:) !< Love number for each SHT mode [nondim] - real, allocatable :: Snm_Re(:), & !< Real SHT coefficient for SHT SAL [Z ~> m] - Snm_Im(:) !< Imaginary SHT coefficient for SHT SAL [Z ~> m] end type tidal_forcing_CS integer :: id_clock_tides !< CPU clock for tides -integer :: id_clock_SAL !< CPU clock for self-attraction and loading contains @@ -366,12 +352,8 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) "If true, use the SAL from the previous iteration of the "//& "tides to facilitate convergent iteration. "//& "This is only used if TIDES is true.", default=.false.) - call get_param(param_file, mdl, "TIDE_USE_SAL_SCALAR", CS%use_sal_scalar, & - "If true and TIDES is true, use the scalar approximation "//& - "when calculating self-attraction and loading.", & - default=.not.CS%tidal_sal_from_file) ! If it is being used, sal_scalar MUST be specified in param_file. - if (CS%use_sal_scalar .or. CS%use_prev_tides) & + if (CS%use_prev_tides) & call get_param(param_file, mdl, "TIDE_SAL_SCALAR_VALUE", CS%sal_scalar, & "The constant of proportionality between sea surface "//& "height (really it should be bottom pressure) anomalies "//& @@ -379,10 +361,6 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) "TIDES and TIDE_USE_SAL_SCALAR are true.", units="m m-1", & fail_if_missing=.true.) - call get_param(param_file, mdl, "TIDAL_SAL_SHT", CS%tidal_sal_sht, & - "If true, use the online spherical harmonics method to calculate "//& - "self-attraction and loading term in tides.", default=.false.) - if (nc > MAX_CONSTITUENTS) then write(mesg,'("Increase MAX_CONSTITUENTS in MOM_tidal_forcing.F90 to at least",I3, & &"to accommodate all the registered tidal constituents.")') nc @@ -542,74 +520,10 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) enddo endif - if (CS%tidal_sal_sht) then - call get_param(param_file, mdl, "TIDAL_SAL_SHT_DEGREE", CS%sal_sht_Nd, & - "The maximum degree of the spherical harmonics transformation used for "// & - "calculating the self-attraction and loading term for tides.", & - default=0, do_not_log=.not.CS%tidal_sal_sht) - call get_param(param_file, mdl, "RHO_0", rhoW, & - "The mean ocean density used with BOUSSINESQ true to "//& - "calculate accelerations and the mass for conservation "//& - "properties, or with BOUSSINSEQ false to convert some "//& - "parameters from vertical units of m to kg m-2.", & - units="kg m-3", default=1035.0, scale=US%kg_m3_to_R, do_not_log=.True.) - call get_param(param_file, mdl, "RHO_E", rhoE, & - "The mean solid earth density. This is used for calculating the "// & - "self-attraction and loading term.", & - units="kg m-3", default=5517.0, scale=US%kg_m3_to_R, & - do_not_log=.not.CS%tidal_sal_sht) - lmax = calc_lmax(CS%sal_sht_Nd) - allocate(CS%Snm_Re(lmax)); CS%Snm_Re(:) = 0.0 - allocate(CS%Snm_Im(lmax)); CS%Snm_Im(:) = 0.0 - - allocate(CS%Love_Scaling(lmax)); CS%Love_Scaling(:) = 0.0 - call calc_love_scaling(CS%sal_sht_Nd, rhoW, rhoE, CS%Love_Scaling) - call spherical_harmonics_init(G, param_file, CS%sht) - id_clock_SAL = cpu_clock_id('(Ocean SAL)', grain=CLOCK_ROUTINE) - endif - id_clock_tides = cpu_clock_id('(Ocean tides)', grain=CLOCK_MODULE) end subroutine tidal_forcing_init -!> This subroutine calculates coefficients of the spherical harmonic modes for self-attraction and loading. -!! The algorithm is based on the SAL implementation in MPAS-ocean, which was modified by Kristin Barton from -!! routine written by K. Quinn (March 2010) and modified by M. Schindelegger (May 2017). -subroutine calc_love_scaling(nlm, rhoW, rhoE, Love_Scaling) - integer, intent(in) :: nlm !< Maximum spherical harmonics degree [nondim] - real, intent(in) :: rhoW !< The average density of sea water [R ~> kg m-3] - real, intent(in) :: rhoE !< The average density of Earth [R ~> kg m-3] - real, dimension(:), intent(out) :: Love_Scaling !< Scaling factors for inverse SHT [nondim] - - ! Local variables - real, dimension(:), allocatable :: HDat, LDat, KDat ! Love numbers converted in CF reference frames [nondim] - real :: H1, L1, K1 ! Temporary variables to store degree 1 Love numbers [nondim] - integer :: n_tot ! Size of the stored Love numbers - integer :: n, m, l - - n_tot = size(Love_Data, dim=2) - - if (nlm+1 > n_tot) call MOM_error(FATAL, "MOM_tidal_forcing " // & - "calc_love_scaling: maximum spherical harmonics degree is larger than " // & - "the size of the stored Love numbers in MOM_load_love_number.") - - allocate(HDat(nlm+1), LDat(nlm+1), KDat(nlm+1)) - HDat(:) = Love_Data(2,1:nlm+1) ; LDat(:) = Love_Data(3,1:nlm+1) ; KDat(:) = Love_Data(4,1:nlm+1) - - ! Convert reference frames from CM to CF - if (nlm > 0) then - H1 = HDat(2) ; L1 = LDat(2) ; K1 = KDat(2) - HDat(2) = ( 2.0 / 3.0) * (H1 - L1) - LDat(2) = (-1.0 / 3.0) * (H1 - L1) - KDat(2) = (-1.0 / 3.0) * H1 - (2.0 / 3.0) * L1 - 1.0 - endif - - do m=0,nlm ; do n=m,nlm - l = order2index(m,nlm) - Love_Scaling(l+n-m) = (3.0 / real(2*n+1)) * (rhoW / rhoE) * (1.0 + KDat(n+1) - HDat(n+1)) - enddo ; enddo -end subroutine calc_love_scaling - !> This subroutine finds a named variable in a list of files and reads its !! values into a domain-decomposed 2-d array subroutine find_in_files(filenames, varname, array, G, scale) @@ -643,47 +557,21 @@ subroutine find_in_files(filenames, varname, array, G, scale) end subroutine find_in_files -!> This subroutine calculates returns the partial derivative of the local -!! geopotential height with the input sea surface height due to self-attraction -!! and loading. -subroutine tidal_forcing_sensitivity(G, CS, deta_tidal_deta) - type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. - type(tidal_forcing_CS), intent(in) :: CS !< The control structure returned by a previous call to tidal_forcing_init. - real, intent(out) :: deta_tidal_deta !< The partial derivative of eta_tidal with - !! the local value of eta [nondim]. - - if (CS%USE_SAL_SCALAR .and. CS%USE_PREV_TIDES) then - deta_tidal_deta = 2.0*CS%SAL_SCALAR - elseif (CS%USE_SAL_SCALAR .or. CS%USE_PREV_TIDES) then - deta_tidal_deta = CS%SAL_SCALAR - else - deta_tidal_deta = 0.0 - endif -end subroutine tidal_forcing_sensitivity - !> This subroutine calculates the geopotential anomalies that drive the tides, -!! including self-attraction and loading. Optionally, it also returns the -!! partial derivative of the local geopotential height with the input sea surface -!! height. For now, eta and eta_tidal are both geopotential heights in depth -!! units, but probably the input for eta should really be replaced with the -!! column mass anomalies. -subroutine calc_tidal_forcing(Time, eta, eta_tidal, G, US, CS) +!! including tidal self-attraction and loading from previous solutions. +subroutine calc_tidal_forcing(Time, eta_tidal, G, US, CS) type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. - type(time_type), intent(in) :: Time !< The time for the calculation. - real, dimension(SZI_(G),SZJ_(G)), intent(in) :: eta !< The sea surface height anomaly from - !! a time-mean geoid [Z ~> m]. + type(time_type), intent(in) :: Time !< The time for the caluculation. real, dimension(SZI_(G),SZJ_(G)), intent(out) :: eta_tidal !< The tidal forcing geopotential height !! anomalies [Z ~> m]. type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type - type(tidal_forcing_CS), intent(inout) :: CS !< The control structure returned by a + type(tidal_forcing_CS), intent(in) :: CS !< The control structure returned by a !! previous call to tidal_forcing_init. ! Local variables - real, dimension(SZI_(G),SZJ_(G)) :: eta_sal !< SAL calculated by spherical harmonics real :: now ! The relative time compared with the tidal reference [T ~> s] real :: amp_cosomegat, amp_sinomegat ! The tidal amplitudes times the components of phase [Z ~> m] real :: cosomegat, sinomegat ! The components of the phase [nondim] - real :: eta_prop ! The nondimenional constant of proportionality between eta and eta_tidal [nondim] integer :: i, j, c, m, is, ie, js, je, Isq, Ieq, Jsq, Jeq is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB @@ -697,16 +585,8 @@ subroutine calc_tidal_forcing(Time, eta, eta_tidal, G, US, CS) now = US%s_to_T * time_type_to_real(Time - cs%time_ref) - if (CS%USE_SAL_SCALAR .and. CS%USE_PREV_TIDES) then - eta_prop = 2.0*CS%SAL_SCALAR - elseif (CS%USE_SAL_SCALAR .or. CS%USE_PREV_TIDES) then - eta_prop = CS%SAL_SCALAR - else - eta_prop = 0.0 - endif - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta_tidal(i,j) = eta_prop*eta(i,j) + eta_tidal(i,j) = 0.0 enddo ; enddo do c=1,CS%nc @@ -737,50 +617,10 @@ subroutine calc_tidal_forcing(Time, eta, eta_tidal, G, US, CS) enddo ; enddo enddo ; endif - if (CS%tidal_sal_sht) then - eta_sal(:,:) = 0.0 - call calc_SAL_sht(eta, eta_sal, G, CS) - - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta_tidal(i,j) = eta_tidal(i,j) + eta_sal(i,j) - enddo ; enddo - endif call cpu_clock_end(id_clock_tides) end subroutine calc_tidal_forcing -!> This subroutine calculates self-attraction and loading using the spherical harmonics method. -subroutine calc_SAL_sht(eta, eta_sal, G, CS) - type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. - real, dimension(SZI_(G),SZJ_(G)), intent(in) :: eta !< The sea surface height anomaly from - !! a time-mean geoid [Z ~> m]. - real, dimension(SZI_(G),SZJ_(G)), intent(out) :: eta_sal !< The sea surface height anomaly from - !! self-attraction and loading [Z ~> m]. - type(tidal_forcing_CS), intent(inout) :: CS !< Tidal forcing control structure - - ! Local variables - integer :: n, m, l - - call cpu_clock_begin(id_clock_SAL) - - call spherical_harmonics_forward(G, CS%sht, eta, CS%Snm_Re, CS%Snm_Im, CS%sal_sht_Nd) - - ! Multiply scaling factors to each mode - do m = 0,CS%sal_sht_Nd - l = order2index(m, CS%sal_sht_Nd) - do n = m,CS%sal_sht_Nd - CS%Snm_Re(l+n-m) = CS%Snm_Re(l+n-m) * CS%Love_Scaling(l+n-m) - CS%Snm_Im(l+n-m) = CS%Snm_Im(l+n-m) * CS%Love_Scaling(l+n-m) - enddo - enddo - - call spherical_harmonics_inverse(G, CS%sht, CS%Snm_Re, CS%Snm_Im, eta_sal, CS%sal_sht_Nd) - - call pass_var(eta_sal, G%domain) - - call cpu_clock_end(id_clock_SAL) -end subroutine calc_SAL_sht - !> This subroutine deallocates memory associated with the tidal forcing module. subroutine tidal_forcing_end(CS) type(tidal_forcing_CS), intent(inout) :: CS !< The control structure returned by a previous call @@ -796,13 +636,6 @@ subroutine tidal_forcing_end(CS) if (allocated(CS%cosphase_prev)) deallocate(CS%cosphase_prev) if (allocated(CS%sinphase_prev)) deallocate(CS%sinphase_prev) if (allocated(CS%amp_prev)) deallocate(CS%amp_prev) - - if (CS%tidal_sal_sht) then - if (allocated(CS%Love_Scaling)) deallocate(CS%Love_Scaling) - if (allocated(CS%Snm_Re)) deallocate(CS%Snm_Re) - if (allocated(CS%Snm_Im)) deallocate(CS%Snm_Im) - call spherical_harmonics_end(CS%sht) - endif end subroutine tidal_forcing_end !> \namespace tidal_forcing @@ -823,28 +656,16 @@ end subroutine tidal_forcing_end !! can be changed at run time by setting variables like TIDE_M2_FREQ, !! TIDE_M2_AMP and TIDE_M2_PHASE_T0 (for M2). !! -!! In addition, the approach to calculating self-attraction and -!! loading is set at run time. The default is to use the scalar -!! approximation, with a coefficient TIDE_SAL_SCALAR_VALUE that must -!! be set in the run-time file (for global runs, 0.094 is typical). -!! Alternately, TIDAL_SAL_FROM_FILE can be set to read the SAL from -!! a file containing the results of a previous simulation. To iterate -!! the SAL to convergence, USE_PREVIOUS_TIDES may be useful (for -!! details, see Arbic et al., 2004, DSR II). With TIDAL_SAL_FROM_FILE -!! or USE_PREVIOUS_TIDES,a list of input files must be provided to -!! describe each constituent's properties from a previous solution. -!! -!! This module also contains a method to calculate self-attraction -!! and loading using spherical harmonic transforms. The algorithm is -!! based on SAL calculation in Model for Prediction Across Scales -!! (MPAS)-Ocean developed by Los Alamos National Laboratory and -!! University of Michigan (Barton et al. (2022) and Brus et al. (2022)). -!! -!! Barton, K.N., Nairita, P., Brus, S.R., Petersen, M.R., Arbic, B.K., Engwirda, D., Roberts, A.F., Westerink, J., -!! Wirasaet, D., and Schindelegger, M., 2022: Performance of Model for Prediction Across Scales (MPAS) Ocean as a -!! Global Barotropic Tide Model. Journal of Advances in Modeling Earth Systems, in review. -!! -!! Brus, S.R., Barton, K.N., Nairita, P., Roberts, A.F., Engwirda, D., Petersen, M.R., Arbic, B.K., Wirasaet, D., -!! Westerink, J., and Schindelegger, M., 2022: Scalable self attraction and loading calculations for unstructured ocean -!! models. Ocean Modelling, in review. +!! In addition, approaches to calculate self-attraction and loading +!! due to tides (harmonics of astronomical forcing frequencies) +!! are provided. TIDAL_SAL_FROM_FILE can be set to read the phase and +!! amplitude of the tidal SAL. USE_PREVIOUS_TIDES may be useful in +!! combination with the scalar approximation to iterate the SAL to +!! convergence (for details, see Arbic et al., 2004, DSR II). With +!! TIDAL_SAL_FROM_FILE or USE_PREVIOUS_TIDES, a list of input files +!! must be provided to describe each constituent's properties from +!! a previous solution. The online SAL calculations that are functions +!! of SSH (rather should be bottom pressure anmoaly), either a scalar +!! approximation or with spherical harmonic transforms, are located in +!! MOM_self_attr_load. end module MOM_tidal_forcing From 4fec906695f8dc7ae16ead8d9d2b9b7f2a5d3b88 Mon Sep 17 00:00:00 2001 From: He Wang Date: Wed, 26 Apr 2023 09:54:15 -0400 Subject: [PATCH 02/17] Decompose output from calc_tidal_forcing The output field eta_tidal from subroutine calc_tidal_forcing originally contains both equilibrium tides and tidal SAL that is based on previous solutions (TIDAL_SAL_FROM_FILE and USE_PREV_TIDES). This commit decompose the two fields for better diagnostics. This also makes answers using USE_PREV_TIDES is not changed if only one tidal constituent is used. New diagnostics ('e_tidal_eq' and 'e_tidal_sal') are added in pressure force modules for the decomposed fields. Note that 'e_tidal' still includes all tidal fields + SAL fields to be backward compatible. --- src/core/MOM_PressureForce_FV.F90 | 54 ++++++++++++------- src/core/MOM_PressureForce_Montgomery.F90 | 50 ++++++++++------- .../lateral/MOM_tidal_forcing.F90 | 36 +++++++------ 3 files changed, 86 insertions(+), 54 deletions(-) diff --git a/src/core/MOM_PressureForce_FV.F90 b/src/core/MOM_PressureForce_FV.F90 index 27a4e3ae5a..4dc354b01c 100644 --- a/src/core/MOM_PressureForce_FV.F90 +++ b/src/core/MOM_PressureForce_FV.F90 @@ -62,7 +62,9 @@ module MOM_PressureForce_FV !! By the default (1) is for a piecewise linear method logical :: use_stanley_pgf !< If true, turn on Stanley parameterization in the PGF - integer :: id_e_tidal = -1 !< Diagnostic identifier + integer :: id_e_tide = -1 !< Diagnostic identifier + integer :: id_e_tide_eq = -1 !< Diagnostic identifier + integer :: id_e_tide_sal = -1 !< Diagnostic identifier integer :: id_e_sal = -1 !< Diagnostic identifier integer :: id_rho_pgf = -1 !< Diagnostic identifier integer :: id_rho_stanley_pgf = -1 !< Diagnostic identifier @@ -120,8 +122,9 @@ subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_ dp, & ! The (positive) change in pressure across a layer [R L2 T-2 ~> Pa]. SSH, & ! The sea surface height anomaly, in depth units [Z ~> m]. e_sal, & ! The bottom geopotential anomaly due to self-attraction and loading [Z ~> m]. - e_tidal, & ! The bottom geopotential anomaly due to tidal forces from astronomical sources - ! and harmonic self-attraction and loading specific to tides [Z ~> m]. + e_tide_eq, & ! The bottom geopotential anomaly due to tidal forces from astronomical sources [Z ~> m]. + e_tide_sal, & ! The bottom geopotential anomaly due to harmonic self-attraction and loading + ! specific to tides [Z ~> m]. dM, & ! The barotropic adjustment to the Montgomery potential to ! account for a reduced gravity model [L2 T-2 ~> m2 s-2]. za ! The geopotential anomaly (i.e. g*e + alpha_0*pressure) at the @@ -324,10 +327,10 @@ subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_ if (CS%tides) then ! Find and add the tidal geopotential anomaly. - call calc_tidal_forcing(CS%Time, e_tidal, G, US, CS%tides_CSp) + call calc_tidal_forcing(CS%Time, e_tide_eq, e_tide_sal, G, US, CS%tides_CSp) !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - za(i,j) = za(i,j) - GV%g_Earth * (e_sal(i,j) + e_tidal(i,j)) + za(i,j) = za(i,j) - GV%g_Earth * (e_sal(i,j) + e_tide_eq(i,j) + e_tide_sal(i,j)) enddo ; enddo else !$OMP parallel do default(shared) @@ -430,9 +433,12 @@ subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_ endif endif - ! To be consistent with old runs, tidal forcing diagnostic also includes SAL. - if (CS%id_e_tidal>0) call post_data(CS%id_e_tidal, e_sal+e_tidal, CS%diag) + ! To be consistent with old runs, tidal forcing diagnostic also includes total SAL. + ! New diagnostics are given for each individual field. + if (CS%id_e_tide>0) call post_data(CS%id_e_tide, e_sal+e_tide_eq+e_tide_sal, CS%diag) if (CS%id_e_sal>0) call post_data(CS%id_e_sal, e_sal, CS%diag) + if (CS%id_e_tide_eq>0) call post_data(CS%id_e_tide_eq, e_tide_eq, CS%diag) + if (CS%id_e_tide_sal>0) call post_data(CS%id_e_tide_sal, e_tide_sal, CS%diag) end subroutine PressureForce_FV_nonBouss @@ -466,13 +472,15 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: e ! Interface height in depth units [Z ~> m]. real, dimension(SZI_(G),SZJ_(G)) :: & e_sal, & ! The bottom geopotential anomaly due to self-attraction and loading [Z ~> m]. - e_tidal, & ! The bottom geopotential anomaly due to tidal forces from astronomical sources - ! and harmonic self-attraction and loading specific to tides [Z ~> m]. + e_tide_eq, & ! The bottom geopotential anomaly due to tidal forces from astronomical sources + ! [Z ~> m]. + e_tide_sal, & ! The bottom geopotential anomaly due to harmonic self-attraction and loading + ! specific to tides [Z ~> m]. SSH, & ! The sea surface height anomaly, in depth units [Z ~> m]. dM ! The barotropic adjustment to the Montgomery potential to ! account for a reduced gravity model [L2 T-2 ~> m2 s-2]. real, dimension(SZI_(G)) :: & - Rho_cv_BL ! The coordinate potential density in the deepest variable + Rho_cv_BL ! The coordinate potential density in the deepest variable ! density near-surface layer [R ~> kg m-3]. real, dimension(SZI_(G),SZJ_(G)) :: & dz_geo, & ! The change in geopotential thickness through a layer [L2 T-2 ~> m2 s-2]. @@ -574,10 +582,10 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm endif if (CS%tides) then - call calc_tidal_forcing(CS%Time, e_tidal, G, US, CS%tides_CSp) + call calc_tidal_forcing(CS%Time, e_tide_eq, e_tide_sal, G, US, CS%tides_CSp) !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e(i,j,nz+1) = -(G%bathyT(i,j) + (e_sal(i,j) + e_tidal(i,j))) + e(i,j,nz+1) = -(G%bathyT(i,j) + (e_sal(i,j) + e_tide_eq(i,j) + e_tide_sal(i,j))) enddo ; enddo else !$OMP parallel do default(shared) @@ -783,7 +791,7 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm ! what is used for eta in btstep. See how e was calculated about 200 lines above. !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta(i,j) = e(i,j,1)*GV%Z_to_H + (e_sal(i,j) + e_tidal(i,j))*GV%Z_to_H + eta(i,j) = e(i,j,1)*GV%Z_to_H + (e_sal(i,j) + e_tide_eq(i,j) + e_tide_sal(i,j))*GV%Z_to_H enddo ; enddo else !$OMP parallel do default(shared) @@ -830,9 +838,13 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm endif endif - ! To be consistent with old runs, tidal forcing diagnostic also includes SAL. - if (CS%id_e_tidal>0) call post_data(CS%id_e_tidal, e_sal+e_tidal, CS%diag) + ! To be consistent with old runs, tidal forcing diagnostic also includes total SAL. + ! New diagnostics are given for each individual field. + if (CS%id_e_tide>0) call post_data(CS%id_e_tide, e_sal+e_tide_eq+e_tide_sal, CS%diag) if (CS%id_e_sal>0) call post_data(CS%id_e_sal, e_sal, CS%diag) + if (CS%id_e_tide_eq>0) call post_data(CS%id_e_tide_eq, e_tide_eq, CS%diag) + if (CS%id_e_tide_sal>0) call post_data(CS%id_e_tide_sal, e_tide_sal, CS%diag) + if (CS%id_rho_pgf>0) call post_data(CS%id_rho_pgf, rho_pgf, CS%diag) if (CS%id_rho_stanley_pgf>0) call post_data(CS%id_rho_stanley_pgf, rho_stanley_pgf, CS%diag) if (CS%id_p_stanley>0) call post_data(CS%id_p_stanley, p_stanley, CS%diag) @@ -922,12 +934,16 @@ subroutine PressureForce_FV_init(Time, G, GV, US, param_file, diag, CS, SAL_CSp, Time, 'p in PGF with Stanley correction', 'Pa', conversion=US%RL2_T2_to_Pa) endif if (CS%calculate_SAL) then - CS%id_e_sal = register_diag_field('ocean_model', 'e_SAL', diag%axesT1, & - Time, 'Self-attraction and loading height Anomaly', 'meter', conversion=US%Z_to_m) + CS%id_e_sal = register_diag_field('ocean_model', 'e_sal', diag%axesT1, & + Time, 'Self-attraction and loading height anomaly', 'meter', conversion=US%Z_to_m) endif if (CS%tides) then - CS%id_e_tidal = register_diag_field('ocean_model', 'e_tidal', diag%axesT1, & - Time, 'Tidal Forcing Astronomical and SAL Height Anomaly', 'meter', conversion=US%Z_to_m) + CS%id_e_tide = register_diag_field('ocean_model', 'e_tidal', diag%axesT1, Time, & + 'Tidal Forcing Astronomical and SAL Height Anomaly', 'meter', conversion=US%Z_to_m) + CS%id_e_tide_eq = register_diag_field('ocean_model', 'e_tide_eq', diag%axesT1, Time, & + 'Equilibrium tides height anomaly', 'meter', conversion=US%Z_to_m) + CS%id_e_tide_sal = register_diag_field('ocean_model', 'e_tide_sal', diag%axesT1, Time, & + 'Read-in tidal self-attraction and loading height anomaly', 'meter', conversion=US%Z_to_m) endif CS%GFS_scale = 1.0 diff --git a/src/core/MOM_PressureForce_Montgomery.F90 b/src/core/MOM_PressureForce_Montgomery.F90 index 5223afbccb..c687c0a40a 100644 --- a/src/core/MOM_PressureForce_Montgomery.F90 +++ b/src/core/MOM_PressureForce_Montgomery.F90 @@ -47,7 +47,8 @@ module MOM_PressureForce_Mont real, allocatable :: PFv_bc(:,:,:) !< Meridional accelerations due to pressure gradients !! deriving from density gradients within layers [L T-2 ~> m s-2]. !>@{ Diagnostic IDs - integer :: id_PFu_bc = -1, id_PFv_bc = -1, id_e_tidal = -1, id_e_sal = -1 + integer :: id_PFu_bc = -1, id_PFv_bc = -1, id_e_sal = -1 + integer :: id_e_tide = -1, id_e_tide_eq = -1, id_e_tide_sal = -1 !>@} type(SAL_CS), pointer :: SAL_CSp => NULL() !< SAL control structure type(tidal_forcing_CS), pointer :: tides_CSp => NULL() !< The tidal forcing control structure @@ -107,8 +108,9 @@ subroutine PressureForce_Mont_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pb dp_star, & ! Layer thickness after compensation for compressibility [R L2 T-2 ~> Pa]. SSH, & ! The sea surface height anomaly, in depth units [Z ~> m]. e_sal, & ! Bottom geopotential anomaly due to self-attraction and loading [Z ~> m]. - e_tidal, & ! Bottom geopotential anomaly due to tidal forces from astronomical sources - ! and harmonic self-attraction and loading specific to tides [Z ~> m]. + e_tide_eq, & ! Bottom geopotential anomaly due to tidal forces from astronomical sources [Z ~> m]. + e_tide_sal, & ! Bottom geopotential anomaly due to harmonic self-attraction and loading + ! specific to tides [Z ~> m]. geopot_bot ! Bottom geopotential relative to a temporally fixed reference value, ! including any tidal contributions [L2 T-2 ~> m2 s-2]. real :: p_ref(SZI_(G)) ! The pressure used to calculate the coordinate @@ -215,10 +217,10 @@ subroutine PressureForce_Mont_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pb endif if (CS%tides) then - call calc_tidal_forcing(CS%Time, e_tidal, G, US, CS%tides_CSp) + call calc_tidal_forcing(CS%Time, e_tide_eq, e_tide_sal, G, US, CS%tides_CSp) !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - geopot_bot(i,j) = -GV%g_Earth*(e_sal(i,j) + e_tidal(i,j) + G%bathyT(i,j)) + geopot_bot(i,j) = -GV%g_Earth*(e_sal(i,j) + e_tide_eq(i,j) + e_tide_sal(i,j) + G%bathyT(i,j)) enddo ; enddo else !$OMP parallel do default(shared) @@ -359,9 +361,12 @@ subroutine PressureForce_Mont_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pb if (CS%id_PFu_bc>0) call post_data(CS%id_PFu_bc, CS%PFu_bc, CS%diag) if (CS%id_PFv_bc>0) call post_data(CS%id_PFv_bc, CS%PFv_bc, CS%diag) - ! To be consistent with old runs, tidal forcing diagnostic also includes SAL. - if (CS%id_e_tidal>0) call post_data(CS%id_e_tidal, e_sal+e_tidal, CS%diag) + ! To be consistent with old runs, tidal forcing diagnostic also includes total SAL. + ! New diagnostics are given for each individual field. + if (CS%id_e_tide>0) call post_data(CS%id_e_tide, e_sal+e_tide_eq+e_tide_sal, CS%diag) if (CS%id_e_sal>0) call post_data(CS%id_e_sal, e_sal, CS%diag) + if (CS%id_e_tide_eq>0) call post_data(CS%id_e_tide_eq, e_tide_eq, CS%diag) + if (CS%id_e_tide_sal>0) call post_data(CS%id_e_tide_sal, e_tide_sal, CS%diag) end subroutine PressureForce_Mont_nonBouss @@ -410,8 +415,10 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, ! for compressibility [Z ~> m]. real :: SSH(SZI_(G),SZJ_(G)) ! The sea surface height anomaly, in depth units [Z ~> m]. real :: e_sal(SZI_(G),SZJ_(G)) ! The bottom geopotential anomaly due to self-attraction and loading [Z ~> m]. - real :: e_tidal(SZI_(G),SZJ_(G)) ! Bottom geopotential anomaly due to tidal forces from astronomical sources - ! and harmonic self-attraction and loading specific to tides, in depth units [Z ~> m]. + real :: e_tide_eq(SZI_(G),SZJ_(G)) ! Bottom geopotential anomaly due to tidal forces from astronomical sources + ! [Z ~> m]. + real :: e_tide_sal(SZI_(G),SZJ_(G)) ! Bottom geopotential anomaly due to harmonic self-attraction and loading + ! specific to tides, in depth units [Z ~> m]. real :: p_ref(SZI_(G)) ! The pressure used to calculate the coordinate ! density [R L2 T-2 ~> Pa] (usually 2e7 Pa = 2000 dbar). real :: I_Rho0 ! 1/Rho0 [R-1 ~> m3 kg-1]. @@ -474,10 +481,10 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, endif if (CS%tides) then - call calc_tidal_forcing(CS%Time, e_tidal, G, US, CS%tides_CSp) + call calc_tidal_forcing(CS%Time, e_tide_eq, e_tide_sal, G, US, CS%tides_CSp) !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e(i,j,nz+1) = -(G%bathyT(i,j) + (e_sal(i,j) + e_tidal(i,j))) + e(i,j,nz+1) = -(G%bathyT(i,j) + (e_sal(i,j) + e_tide_eq(i,j) + e_tide_sal(i,j))) enddo ; enddo else !$OMP parallel do default(shared) @@ -607,7 +614,7 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, ! about 200 lines above. !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta(i,j) = e(i,j,1)*GV%Z_to_H + (e_sal(i,j)+e_tidal(i,j))*GV%Z_to_H + eta(i,j) = e(i,j,1)*GV%Z_to_H + (e_sal(i,j)+e_tide_eq(i,j)+e_tide_sal(i,j))*GV%Z_to_H enddo ; enddo else !$OMP parallel do default(shared) @@ -619,9 +626,12 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, if (CS%id_PFu_bc>0) call post_data(CS%id_PFu_bc, CS%PFu_bc, CS%diag) if (CS%id_PFv_bc>0) call post_data(CS%id_PFv_bc, CS%PFv_bc, CS%diag) - ! To be consistent with old runs, tidal forcing diagnostic also includes SAL. - if (CS%id_e_tidal>0) call post_data(CS%id_e_tidal, e_sal+e_tidal, CS%diag) + ! To be consistent with old runs, tidal forcing diagnostic also includes total SAL. + ! New diagnostics are given for each individual field. + if (CS%id_e_tide>0) call post_data(CS%id_e_tide, e_sal+e_tide_eq+e_tide_sal, CS%diag) if (CS%id_e_sal>0) call post_data(CS%id_e_sal, e_sal, CS%diag) + if (CS%id_e_tide_eq>0) call post_data(CS%id_e_tide_eq, e_tide_eq, CS%diag) + if (CS%id_e_tide_sal>0) call post_data(CS%id_e_tide_sal, e_tide_sal, CS%diag) end subroutine PressureForce_Mont_Bouss @@ -893,12 +903,16 @@ subroutine PressureForce_Mont_init(Time, G, GV, US, param_file, diag, CS, SAL_CS endif if (CS%calculate_SAL) then - CS%id_e_sal = register_diag_field('ocean_model', 'e_SAL', diag%axesT1, & - Time, 'SAL Height Anomaly', 'meter', conversion=US%Z_to_m) + CS%id_e_sal = register_diag_field('ocean_model', 'e_sal', diag%axesT1, Time, & + 'Self-attraction and loading height anomaly', 'meter', conversion=US%Z_to_m) endif if (CS%tides) then - CS%id_e_tidal = register_diag_field('ocean_model', 'e_tidal', diag%axesT1, & - Time, 'Tidal Forcing Astronomical and SAL Height Anomaly', 'meter', conversion=US%Z_to_m) + CS%id_e_tide = register_diag_field('ocean_model', 'e_tidal', diag%axesT1, Time, & + 'Tidal Forcing Astronomical and SAL Height Anomaly', 'meter', conversion=US%Z_to_m) + CS%id_e_tide_eq = register_diag_field('ocean_model', 'e_tide_eq', diag%axesT1, Time, & + 'Equilibrium tides height anomaly', 'meter', conversion=US%Z_to_m) + CS%id_e_tide_sal = register_diag_field('ocean_model', 'e_tide_sal', diag%axesT1, Time, & + 'Read-in tidal self-attraction and loading height anomaly', 'meter', conversion=US%Z_to_m) endif CS%GFS_scale = 1.0 diff --git a/src/parameterizations/lateral/MOM_tidal_forcing.F90 b/src/parameterizations/lateral/MOM_tidal_forcing.F90 index 358ec3dc57..fcd90a4171 100644 --- a/src/parameterizations/lateral/MOM_tidal_forcing.F90 +++ b/src/parameterizations/lateral/MOM_tidal_forcing.F90 @@ -559,14 +559,16 @@ end subroutine find_in_files !> This subroutine calculates the geopotential anomalies that drive the tides, !! including tidal self-attraction and loading from previous solutions. -subroutine calc_tidal_forcing(Time, eta_tidal, G, US, CS) - type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. - type(time_type), intent(in) :: Time !< The time for the caluculation. - real, dimension(SZI_(G),SZJ_(G)), intent(out) :: eta_tidal !< The tidal forcing geopotential height - !! anomalies [Z ~> m]. - type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type - type(tidal_forcing_CS), intent(in) :: CS !< The control structure returned by a - !! previous call to tidal_forcing_init. +subroutine calc_tidal_forcing(Time, e_tide_eq, e_tide_sal, G, US, CS) + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + type(time_type), intent(in) :: Time !< The time for the caluculation. + real, dimension(SZI_(G),SZJ_(G)), intent(out) :: e_tide_eq !< The geopotential height anomalies + !! due to the equilibrium tides [Z ~> m]. + real, dimension(SZI_(G),SZJ_(G)), intent(out) :: e_tide_sal !< The geopotential height anomalies + !! due to the tidal SAL [Z ~> m]. + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + type(tidal_forcing_CS), intent(in) :: CS !< The control structure returned by a + !! previous call to tidal_forcing_init. ! Local variables real :: now ! The relative time compared with the tidal reference [T ~> s] @@ -578,23 +580,23 @@ subroutine calc_tidal_forcing(Time, eta_tidal, G, US, CS) call cpu_clock_begin(id_clock_tides) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e_tide_eq(i,j) = 0.0 + e_tide_sal(i,j) = 0.0 + enddo ; enddo + if (CS%nc == 0) then - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 ; eta_tidal(i,j) = 0.0 ; enddo ; enddo return endif now = US%s_to_T * time_type_to_real(Time - cs%time_ref) - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta_tidal(i,j) = 0.0 - enddo ; enddo - do c=1,CS%nc m = CS%struct(c) amp_cosomegat = CS%amp(c)*CS%love_no(c) * cos(CS%freq(c)*now + CS%phase0(c)) amp_sinomegat = CS%amp(c)*CS%love_no(c) * sin(CS%freq(c)*now + CS%phase0(c)) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta_tidal(i,j) = eta_tidal(i,j) + (amp_cosomegat*CS%cos_struct(i,j,m) + & + e_tide_eq(i,j) = e_tide_eq(i,j) + (amp_cosomegat*CS%cos_struct(i,j,m) + & amp_sinomegat*CS%sin_struct(i,j,m)) enddo ; enddo enddo @@ -603,8 +605,8 @@ subroutine calc_tidal_forcing(Time, eta_tidal, G, US, CS) cosomegat = cos(CS%freq(c)*now) sinomegat = sin(CS%freq(c)*now) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta_tidal(i,j) = eta_tidal(i,j) + CS%ampsal(i,j,c) * & - (cosomegat*CS%cosphasesal(i,j,c) + sinomegat*CS%sinphasesal(i,j,c)) + e_tide_sal(i,j) = e_tide_sal(i,j) + CS%ampsal(i,j,c) * & + (cosomegat*CS%cosphasesal(i,j,c) + sinomegat*CS%sinphasesal(i,j,c)) enddo ; enddo enddo ; endif @@ -612,7 +614,7 @@ subroutine calc_tidal_forcing(Time, eta_tidal, G, US, CS) cosomegat = cos(CS%freq(c)*now) sinomegat = sin(CS%freq(c)*now) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta_tidal(i,j) = eta_tidal(i,j) - CS%SAL_SCALAR*CS%amp_prev(i,j,c) * & + e_tide_sal(i,j) = e_tide_sal(i,j) - CS%sal_scalar * CS%amp_prev(i,j,c) * & (cosomegat*CS%cosphase_prev(i,j,c) + sinomegat*CS%sinphase_prev(i,j,c)) enddo ; enddo enddo ; endif From b69d6fbd5256cd773c18b13a63920a17b9d6531d Mon Sep 17 00:00:00 2001 From: He Wang Date: Tue, 6 Jun 2023 12:21:23 -0400 Subject: [PATCH 03/17] Fix SSH for calculating SAL with flooding points SSH for SAL is modified for grid points with topography above the reference height z_ref (assumed to be land that can be flooded). Instead of eta anomaly referenced to z_ref, eta anomaly referenced to the bottom depth is used for these grid points. --- src/core/MOM_PressureForce_FV.F90 | 5 +++-- src/core/MOM_PressureForce_Montgomery.F90 | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/MOM_PressureForce_FV.F90 b/src/core/MOM_PressureForce_FV.F90 index 4dc354b01c..25301ae31d 100644 --- a/src/core/MOM_PressureForce_FV.F90 +++ b/src/core/MOM_PressureForce_FV.F90 @@ -315,7 +315,8 @@ subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_ ! Find and add the self-attraction and loading geopotential anomaly. !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - SSH(i,j) = (za(i,j) - alpha_ref*p(i,j,1)) * I_gEarth - G%Z_ref + SSH(i,j) = (za(i,j) - alpha_ref*p(i,j,1)) * I_gEarth - G%Z_ref & + - max(-G%bathyT(i,j)-G%Z_ref, 0.0) enddo ; enddo call calc_SAL(SSH, e_sal, G, CS%SAL_CSp) else @@ -567,7 +568,7 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm !$OMP parallel do default(shared) do j=Jsq,Jeq+1 do i=Isq,Ieq+1 - SSH(i,j) = -G%bathyT(i,j) - G%Z_ref + SSH(i,j) = min(-G%bathyT(i,j) - G%Z_ref, 0.0) enddo do k=1,nz ; do i=Isq,Ieq+1 SSH(i,j) = SSH(i,j) + h(i,j,k)*GV%H_to_Z diff --git a/src/core/MOM_PressureForce_Montgomery.F90 b/src/core/MOM_PressureForce_Montgomery.F90 index c687c0a40a..53f6a5a925 100644 --- a/src/core/MOM_PressureForce_Montgomery.F90 +++ b/src/core/MOM_PressureForce_Montgomery.F90 @@ -191,7 +191,7 @@ subroutine PressureForce_Mont_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pb ! of self-attraction and loading. !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - SSH(i,j) = -G%bathyT(i,j) - G%Z_ref + SSH(i,j) = min(-G%bathyT(i,j) - G%Z_ref, 0.0) enddo ; enddo if (use_EOS) then !$OMP parallel do default(shared) @@ -468,7 +468,7 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, ! barotropic tides. !$OMP parallel do default(shared) do j=Jsq,Jeq+1 - do i=Isq,Ieq+1 ; SSH(i,j) = -G%bathyT(i,j) - G%Z_ref ; enddo + do i=Isq,Ieq+1 ; SSH(i,j) = min(-G%bathyT(i,j) - G%Z_ref, 0.0) ; enddo do k=1,nz ; do i=Isq,Ieq+1 SSH(i,j) = SSH(i,j) + h(i,j,k)*GV%H_to_Z enddo ; enddo From b94a32c35d9674f1fb476b8803e584744bac9d94 Mon Sep 17 00:00:00 2001 From: He Wang Date: Tue, 6 Jun 2023 13:02:47 -0400 Subject: [PATCH 04/17] Renaming input parameter names for the SAL module Remove "TIDAL" and "TIDE" from the relevant input names of the SAL module, as SAL is not specific to the tides. This affects both scalar approximation and the fully online spherical harmonic options. * For scalar SAL, old parameter names are still acceptable, but a WARNING is given if these old names appear in MOM_input. * For read-in SAL, no change is made. * For iterative method (use_prev_tides), the use of TIDE_SAL_SCALAR_VALUE is completely deprecated, as this is a feature that is rarely used. * For harmonic SAL, a relatively recent feature, a hard obsolete is applied, i.e. if the old parameters are specified, a FATAL error is given, unless the new parameters also exist and match the values of the old parameters. List of input names changed: * TIDE_USE_SAL_SCALAR -> USE_SAL_SCALAR * TIDE_SAL_SCALAR_VALUE -> SAL_SCALAR_VALUE * TIDAL_SAL_SHT -> USE_SAL_HARMONICS * TIDAL_SAL_SHT_DEGREE -> SAL_HARMONICS_DEGREE --- .../lateral/MOM_self_attr_load.F90 | 173 +++++++++++------- .../lateral/MOM_tidal_forcing.F90 | 28 ++- 2 files changed, 117 insertions(+), 84 deletions(-) diff --git a/src/parameterizations/lateral/MOM_self_attr_load.F90 b/src/parameterizations/lateral/MOM_self_attr_load.F90 index fb27cfa346..be45f64cfe 100644 --- a/src/parameterizations/lateral/MOM_self_attr_load.F90 +++ b/src/parameterizations/lateral/MOM_self_attr_load.F90 @@ -1,15 +1,16 @@ module MOM_self_attr_load -use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, CLOCK_MODULE -use MOM_domains, only : pass_var -use MOM_error_handler, only : MOM_error, FATAL, WARNING -use MOM_file_parser, only : get_param, log_version, param_file_type -use MOM_grid, only : ocean_grid_type -use MOM_unit_scaling, only : unit_scale_type -use MOM_spherical_harmonics, only : spherical_harmonics_init, spherical_harmonics_end, order2index, calc_lmax +use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, CLOCK_MODULE +use MOM_domains, only : pass_var +use MOM_error_handler, only : MOM_error, FATAL, WARNING +use MOM_file_parser, only : get_param, log_param, log_version, param_file_type +use MOM_obsolete_params, only : obsolete_logical, obsolete_int +use MOM_grid, only : ocean_grid_type +use MOM_unit_scaling, only : unit_scale_type +use MOM_spherical_harmonics, only : spherical_harmonics_init, spherical_harmonics_end use MOM_spherical_harmonics, only : spherical_harmonics_forward, spherical_harmonics_inverse -use MOM_spherical_harmonics, only : sht_CS -use MOM_load_love_numbers, only : Love_Data +use MOM_spherical_harmonics, only : sht_CS, order2index, calc_lmax +use MOM_load_love_numbers, only : Love_Data implicit none ; private @@ -19,19 +20,18 @@ module MOM_self_attr_load !> The control structure for the MOM_self_attr_load module type, public :: SAL_CS ; private - logical :: use_sal_scalar !< If true, use the scalar approximation when - !! calculating self-attraction and loading. - real :: sal_scalar !< The constant of proportionality between sea surface - !! height (really it should be bottom pressure) anomalies - !! and bottom geopotential anomalies [nondim]. - logical :: use_prev_tides !< If true, use the SAL from the previous iteration of the tides - !! to facilitate convergence. - logical :: use_sal_sht !< If true, use online spherical harmonics to calculate SAL - type(sht_CS) :: sht !< Spherical harmonic transforms (SHT) for SAL - integer :: sal_sht_Nd !< Maximum degree for SHT [nodim] + logical :: use_sal_scalar !< If true, use the scalar approximation to calculate SAL. + logical :: use_sal_sht !< If true, use online spherical harmonics to calculate SAL + logical :: use_tidal_sal_prev !< If true, read the tidal SAL from the previous iteration of + !! the tides to facilitate convergence. + real :: sal_scalar_value !< The constant of proportionality between sea surface height + !! (really it should be bottom pressure) anomalies and bottom + !! geopotential anomalies [nondim]. + type(sht_CS) :: sht !< Spherical harmonic transforms (SHT) control structure + integer :: sal_sht_Nd !< Maximum degree for SHT [nodim] real, allocatable :: Love_Scaling(:) !< Love number for each SHT mode [nodim] - real, allocatable :: Snm_Re(:), & !< Real and imaginary SHT coefficient for SHT SAL - Snm_Im(:) !< [Z ~> m] + real, allocatable :: Snm_Re(:), & !< Real SHT coefficient for SHT SAL [Z ~> m] + Snm_Im(:) !< Imaginary SHT coefficient for SHT SAL [Z ~> m] end type SAL_CS integer :: id_clock_SAL !< CPU clock for self-attraction and loading @@ -60,13 +60,15 @@ subroutine calc_SAL(eta, eta_sal, G, CS) Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB - ! use the scalar approximation, iterative tidal SAL or no SAL - call scalar_SAL_sensitivity(CS, eta_prop) - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta_sal(i,j) = eta_prop*eta(i,j) - enddo ; enddo + ! use the scalar approximation and/or iterative tidal SAL + if (CS%use_sal_scalar .or. CS%use_tidal_sal_prev) then + call scalar_SAL_sensitivity(CS, eta_prop) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + eta_sal(i,j) = eta_prop*eta(i,j) + enddo ; enddo - if (CS%use_sal_sht) then ! use the spherical harmonics method + ! use the spherical harmonics method + elseif (CS%use_sal_sht) then call spherical_harmonics_forward(G, CS%sht, eta, CS%Snm_Re, CS%Snm_Im, CS%sal_sht_Nd) ! Multiply scaling factors to each mode @@ -79,8 +81,13 @@ subroutine calc_SAL(eta, eta_sal, G, CS) enddo call spherical_harmonics_inverse(G, CS%sht, CS%Snm_Re, CS%Snm_Im, eta_sal, CS%sal_sht_Nd) - + ! Halo was not calculated in spherical harmonic transforms. call pass_var(eta_sal, G%domain) + + else + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + eta_sal(i,j) = 0.0 + enddo ; enddo endif call cpu_clock_end(id_clock_SAL) @@ -93,10 +100,10 @@ subroutine scalar_SAL_sensitivity(CS, deta_sal_deta) real, intent(out) :: deta_sal_deta !< The partial derivative of eta_sal with !! the local value of eta [nondim]. - if (CS%USE_SAL_SCALAR .and. CS%USE_PREV_TIDES) then - deta_sal_deta = 2.0*CS%SAL_SCALAR - elseif (CS%USE_SAL_SCALAR .or. CS%USE_PREV_TIDES) then - deta_sal_deta = CS%SAL_SCALAR + if (CS%use_sal_scalar .and. CS%use_tidal_sal_prev) then + deta_sal_deta = 2.0*CS%sal_scalar_value + elseif (CS%use_sal_scalar .or. CS%use_tidal_sal_prev) then + deta_sal_deta = CS%sal_scalar_value else deta_sal_deta = 0.0 endif @@ -140,13 +147,14 @@ subroutine calc_love_scaling(nlm, rhoW, rhoE, Love_Scaling) enddo ; enddo end subroutine calc_love_scaling -!> This subroutine initializeds the self-attraction and loading control structure. +!> This subroutine initializes the self-attraction and loading control structure. subroutine SAL_init(G, US, param_file, CS) type(ocean_grid_type), intent(inout) :: G !< The ocean's 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(SAL_CS), intent(inout) :: CS !< Self-attraction and loading control structure + ! Local variables # include "version_variable.h" character(len=40) :: mdl = "MOM_self_attr_load" ! This module's name. integer :: lmax ! Total modes of the real spherical harmonics [nondim] @@ -154,58 +162,87 @@ subroutine SAL_init(G, US, param_file, CS) real :: rhoE ! The average density of Earth [R ~> kg m-3]. logical :: calculate_sal - logical :: tides, tidal_sal_from_file + logical :: tides, use_tidal_sal_file + real :: tide_sal_scalar_value ! Read all relevant parameters and write them to the model log. call log_version(param_file, mdl, version, "") call get_param(param_file, '', "TIDES", tides, default=.false., do_not_log=.True.) - - CS%use_prev_tides = .false. - tidal_sal_from_file = .false. if (tides) then - call get_param(param_file, '', "USE_PREVIOUS_TIDES", CS%use_prev_tides,& + call get_param(param_file, '', "USE_PREVIOUS_TIDES", CS%use_tidal_sal_prev, & default=.false., do_not_log=.True.) - call get_param(param_file, '', "TIDAL_SAL_FROM_FILE", tidal_sal_from_file,& + call get_param(param_file, '', "TIDAL_SAL_FROM_FILE", use_tidal_sal_file, & default=.false., do_not_log=.True.) endif - call get_param(param_file, mdl, "TIDE_USE_SAL_SCALAR", CS%use_sal_scalar, & - "If true and TIDES is true, use the scalar approximation "//& - "when calculating self-attraction and loading.", & - default=.not.tidal_sal_from_file) - if (CS%use_sal_scalar .or. CS%use_prev_tides) & - call get_param(param_file, mdl, "TIDE_SAL_SCALAR_VALUE", CS%sal_scalar, & - "The constant of proportionality between sea surface "//& - "height (really it should be bottom pressure) anomalies "//& - "and bottom geopotential anomalies. This is only used if "//& - "TIDES and TIDE_USE_SAL_SCALAR are true.", units="m m-1", & - fail_if_missing=.true.) - - call get_param(param_file, mdl, "TIDAL_SAL_SHT", CS%use_sal_sht, & - "If true, use the online spherical harmonics method to calculate "//& - "self-attraction and loading term in tides.", default=.false.) + ! TIDE_USE_SAL_SCALAR is going to be replaced by USE_SAL_SCALAR. During the transition, the default of + ! USE_SAL_SCALAR is set to be consistent with TIDE_USE_SAL_SCALAR before the implementation of spherical + ! harmonics SAL. + ! A FATAL error is only issued when the user specified TIDE_USE_SAL_SCALAR contradicts USE_SAL_SCALAR. + call get_param(param_file, mdl, "USE_SAL_SCALAR", CS%use_sal_scalar, & + "If true, use the scalar approximation to calculate self-attraction and"//& + " loading. This parameter is to replace TIDE_USE_SAL_SCALAR, as SAL applies"//& + " to all motions. When both USE_SAL_SCALAR and TIDE_USE_SAL_SCALAR are"//& + " specified, USE_SAL_SCALAR overrides TIDE_USE_SAL_SCALAR.", & + default=tides .and. (.not.use_tidal_sal_file)) + if (tides) then + call obsolete_logical(param_file, "TIDE_USE_SAL_SCALAR", warning_val=CS%use_sal_scalar, & + hint="Use USE_SAL_SCALAR instead.") + endif + + call get_param(param_file, mdl, "USE_SAL_HARMONICS", CS%use_sal_sht, & + "If true, use the online spherical harmonics method to calculate"//& + " self-attraction and loading.", default=.false.) + ! This is a more of a hard obsolete but should only impact a handful of users. + call obsolete_logical(param_file, "TIDAL_SAL_SHT", warning_val=CS%use_sal_sht, & + hint="Use USE_SAL_HARMONICS instead.") call get_param(param_file, mdl, "CALCULATE_SAL", calculate_sal, & "If true, calculate self-attraction and loading.", default=tides) - - ! ! Default USE_SAL is TRUE for now to keep backward compatibility with old MOM_INPUT files. It should be changed to - ! ! FALSE in the future (mostly to avoid the SSH calculations in MOM_PressureForce). In that case, the following check - ! ! informs prior tidal experiments that use scalar or iterative SAL to include USE_SAL flag, as the USE_SAL flag - ! ! overrules the option flags. - ! if ((.not. calculate_sal) .and. (CS%use_prev_tides .or. CS%use_sal_scalar .or. CS%use_sal_sht)) & - ! call MOM_error(FATAL, trim(mdl)//": USE_SAL is False but one of the options is True. Nothing will happen.") + if ((.not. calculate_sal) .and. (CS%use_tidal_sal_prev .or. CS%use_sal_scalar .or. CS%use_sal_sht)) & + call MOM_error(FATAL, trim(mdl)//": CALCULATE_SAL is False but one of the options is True.") + + ! TIDE_SAL_SCALAR_VALUE is going to be replaced by SAL_SCALAR_VALUE. The following segment of codes + ! should eventually be replaced by the commented code below. + ! if (CS%use_sal_scalar .or. CS%use_tidal_sal_prev) & + ! call get_param(param_file, mdl, "SAL_SCALAR_VALUE", CS%sal_scalar_value, & + ! "The constant of proportionality between sea surface "//& + ! "height (really it should be bottom pressure) anomalies "//& + ! "and bottom geopotential anomalies. This is only used if "//& + ! "USE_SAL_SCALAR is true or USE_PREVIOUS_TIDES is true.", & + ! fail_if_missing=.true., units="m m-1") + ! endif + if (CS%use_sal_scalar .or. CS%use_tidal_sal_prev) then + CS%sal_scalar_value = -9e35 + call get_param(param_file, mdl, "SAL_SCALAR_VALUE", CS%sal_scalar_value, "", do_not_log=.True.) + if (CS%sal_scalar_value == -9e35) then + call get_param(param_file, '', "TIDE_SAL_SCALAR_VALUE", CS%sal_scalar_value, do_not_log=.True.) + if (CS%sal_scalar_value /= -9e35) & + call MOM_error(WARNING, "TIDE_SAL_SCALAR_VALUE is a deprecated parameter. "//& + "Use SAL_SCALAR_VALUE instead.") + endif + if (CS%sal_scalar_value == -9e35) & + call MOM_error(FATAL, trim(mdl)//": USE_SAL_SCALAR is true but SAL_SCALAR_VALUE is not set.") + call log_param(param_file, mdl, "SAL_SCALAR_VALUE", CS%sal_scalar_value, & + "The constant of proportionality between sea surface "//& + "height (really it should be bottom pressure) anomalies "//& + "and bottom geopotential anomalies. This is only used if "//& + "USE_SAL_SCALAR is true or USE_PREVIOUS_TIDES is true.", units="m m-1") + endif if (CS%use_sal_sht) then - call get_param(param_file, mdl, "TIDAL_SAL_SHT_DEGREE", CS%sal_sht_Nd, & + call get_param(param_file, mdl, "SAL_HARMONICS_DEGREE", CS%sal_sht_Nd, & "The maximum degree of the spherical harmonics transformation used for "// & "calculating the self-attraction and loading term.", & - default=0, do_not_log=.not. CS%use_sal_sht) - call get_param(param_file, mdl, "RHO_0", rhoW, default=1035.0, scale=US%kg_m3_to_R, do_not_log=.True.) + default=0) + call obsolete_int(param_file, "TIDAL_SAL_SHT_DEGREE", warning_val=CS%sal_sht_Nd, & + hint="Use SAL_HARMONICS_DEGREE instead.") + call get_param(param_file, '', "RHO_0", rhoW, default=1035.0, scale=US%kg_m3_to_R, do_not_log=.True.) call get_param(param_file, mdl, "RHO_E", rhoE, & "The mean solid earth density. This is used for calculating the "// & "self-attraction and loading term.", units="kg m-3", & - default=5517.0, scale=US%kg_m3_to_R, do_not_log=.not. CS%use_sal_sht) + default=5517.0, scale=US%kg_m3_to_R) lmax = calc_lmax(CS%sal_sht_Nd) allocate(CS%Snm_Re(lmax)); CS%Snm_Re(:) = 0.0 allocate(CS%Snm_Im(lmax)); CS%Snm_Im(:) = 0.0 @@ -235,10 +272,10 @@ end subroutine SAL_end !! !! This module contains methods to calculate self-attraction and loading (SAL) as a function of sea surface height (SSH) !! (rather, it should be bottom pressure anomaly). SAL is primarily used for fast evolving processes like tides or -!! storm surges, but the effect applys to all motions. +!! storm surges, but the effect applies to all motions. !! -!! If TIDE_USE_SAL_SCALAR is true, a scalar approximiation is applied (Accad and Pekeris 1978) and the SAL is simply -!! a fraction (set by TIDE_SAL_SCALAR_VALUE, usualy around 10% for global tides) of local SSH . For the tides, the +!! If TIDE_USE_SAL_SCALAR is true, a scalar approximation is applied (Accad and Pekeris 1978) and the SAL is simply +!! a fraction (set by TIDE_SAL_SCALAR_VALUE, usually around 10% for global tides) of local SSH . For the tides, the !! scalar approximation can also be used to iterate the SAL to convergence [see USE_PREVIOUS_TIDES in MOM_tidal_forcing, !! Arbic et al. (2004)]. !! diff --git a/src/parameterizations/lateral/MOM_tidal_forcing.F90 b/src/parameterizations/lateral/MOM_tidal_forcing.F90 index fcd90a4171..38286f20c5 100644 --- a/src/parameterizations/lateral/MOM_tidal_forcing.F90 +++ b/src/parameterizations/lateral/MOM_tidal_forcing.F90 @@ -33,10 +33,10 @@ module MOM_tidal_forcing !> The control structure for the MOM_tidal_forcing module type, public :: tidal_forcing_CS ; private - logical :: tidal_sal_from_file !< If true, Read the tidal self-attraction + logical :: use_tidal_sal_file !< If true, Read the tidal self-attraction !! and loading from input files, specified !! by TIDAL_INPUT_FILE. - logical :: use_prev_tides !< If true, use the SAL from the previous + logical :: use_tidal_sal_prev !< If true, use the SAL from the previous !! iteration of the tides to facilitate convergence. logical :: use_eq_phase !< If true, tidal forcing is phase-shifted to match !! equilibrium tide. Set to false if providing tidal phases @@ -344,22 +344,18 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) return endif - call get_param(param_file, mdl, "TIDAL_SAL_FROM_FILE", CS%tidal_sal_from_file, & + call get_param(param_file, mdl, "TIDAL_SAL_FROM_FILE", CS%use_tidal_sal_file, & "If true, read the tidal self-attraction and loading "//& "from input files, specified by TIDAL_INPUT_FILE. "//& "This is only used if TIDES is true.", default=.false.) - call get_param(param_file, mdl, "USE_PREVIOUS_TIDES", CS%use_prev_tides, & + call get_param(param_file, mdl, "USE_PREVIOUS_TIDES", CS%use_tidal_sal_prev, & "If true, use the SAL from the previous iteration of the "//& "tides to facilitate convergent iteration. "//& "This is only used if TIDES is true.", default=.false.) ! If it is being used, sal_scalar MUST be specified in param_file. - if (CS%use_prev_tides) & - call get_param(param_file, mdl, "TIDE_SAL_SCALAR_VALUE", CS%sal_scalar, & - "The constant of proportionality between sea surface "//& - "height (really it should be bottom pressure) anomalies "//& - "and bottom geopotential anomalies. This is only used if "//& - "TIDES and TIDE_USE_SAL_SCALAR are true.", units="m m-1", & - fail_if_missing=.true.) + if (CS%use_tidal_sal_prev) & + call get_param(param_file, mdl, "SAL_SCALAR_VALUE", CS%sal_scalar, fail_if_missing=.true., & + do_not_log=.True.) if (nc > MAX_CONSTITUENTS) then write(mesg,'("Increase MAX_CONSTITUENTS in MOM_tidal_forcing.F90 to at least",I3, & @@ -369,7 +365,7 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) do c=1,4*MAX_CONSTITUENTS ; tidal_input_files(c) = "" ; enddo - if (CS%tidal_sal_from_file .or. CS%use_prev_tides) then + if (CS%use_tidal_sal_file .or. CS%use_tidal_sal_prev) then call get_param(param_file, mdl, "TIDAL_INPUT_FILE", tidal_input_files, & "A list of input files for tidal information.", & default="", fail_if_missing=.true.) @@ -484,7 +480,7 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) " are true.", units="radians", default=phase0_def(c)) enddo - if (CS%tidal_sal_from_file) then + if (CS%use_tidal_sal_file) then allocate(CS%cosphasesal(isd:ied,jsd:jed,nc)) allocate(CS%sinphasesal(isd:ied,jsd:jed,nc)) allocate(CS%ampsal(isd:ied,jsd:jed,nc)) @@ -502,7 +498,7 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) enddo endif - if (CS%USE_PREV_TIDES) then + if (CS%use_tidal_sal_prev) then allocate(CS%cosphase_prev(isd:ied,jsd:jed,nc)) allocate(CS%sinphase_prev(isd:ied,jsd:jed,nc)) allocate(CS%amp_prev(isd:ied,jsd:jed,nc)) @@ -601,7 +597,7 @@ subroutine calc_tidal_forcing(Time, e_tide_eq, e_tide_sal, G, US, CS) enddo ; enddo enddo - if (CS%tidal_sal_from_file) then ; do c=1,CS%nc + if (CS%use_tidal_sal_file) then ; do c=1,CS%nc cosomegat = cos(CS%freq(c)*now) sinomegat = sin(CS%freq(c)*now) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 @@ -610,7 +606,7 @@ subroutine calc_tidal_forcing(Time, e_tide_eq, e_tide_sal, G, US, CS) enddo ; enddo enddo ; endif - if (CS%USE_PREV_TIDES) then ; do c=1,CS%nc + if (CS%use_tidal_sal_prev) then ; do c=1,CS%nc cosomegat = cos(CS%freq(c)*now) sinomegat = sin(CS%freq(c)*now) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 From 00a63e8484ba0a1eb978485c49bd479347170750 Mon Sep 17 00:00:00 2001 From: He Wang Date: Fri, 16 Jun 2023 15:07:57 -0400 Subject: [PATCH 05/17] Recover old answers with tides in Boussinesq mode This commit addresses the issue of bit level answer change with tides in Boussinesq mode. * A runtime parameter TIDES_ANSWER_DATE is added to restore old answers before the SAL module is added. The answer change is due to an reorder of summations of the SAL and tidal forcing terms. * The new version flag only applies to the analytical pressure force in Boussinesq mode, which is virtually the only configuration widely used and included in the test suite. * For Montgomery pressure and non-Boussinesq mode, the code is refactored in a more readable way. --- src/core/MOM_PressureForce_FV.F90 | 55 ++++++++---- src/core/MOM_PressureForce_Montgomery.F90 | 49 ++++++----- .../lateral/MOM_tidal_forcing.F90 | 83 +++++++++++++++++++ 3 files changed, 149 insertions(+), 38 deletions(-) diff --git a/src/core/MOM_PressureForce_FV.F90 b/src/core/MOM_PressureForce_FV.F90 index 25301ae31d..4314c1f7df 100644 --- a/src/core/MOM_PressureForce_FV.F90 +++ b/src/core/MOM_PressureForce_FV.F90 @@ -11,6 +11,7 @@ module MOM_PressureForce_FV use MOM_PressureForce_Mont, only : set_pbce_Bouss, set_pbce_nonBouss use MOM_self_attr_load, only : calc_SAL, SAL_CS use MOM_tidal_forcing, only : calc_tidal_forcing, tidal_forcing_CS +use MOM_tidal_forcing, only : calc_tidal_forcing_legacy use MOM_unit_scaling, only : unit_scale_type use MOM_variables, only : thermo_var_ptrs use MOM_verticalGrid, only : verticalGrid_type @@ -62,6 +63,7 @@ module MOM_PressureForce_FV !! By the default (1) is for a piecewise linear method logical :: use_stanley_pgf !< If true, turn on Stanley parameterization in the PGF + integer :: tide_answer_date !< Recover old answers with tides in Boussinesq mode integer :: id_e_tide = -1 !< Diagnostic identifier integer :: id_e_tide_eq = -1 !< Diagnostic identifier integer :: id_e_tide_sal = -1 !< Diagnostic identifier @@ -309,34 +311,27 @@ subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_ enddo ; enddo enddo - ! The following two if-statements are arranged in a way that answers are not - ! changed from old versions in which SAL is part of the tidal forcing module. + ! Calculate and add the self-attraction and loading geopotential anomaly. if (CS%calculate_SAL) then - ! Find and add the self-attraction and loading geopotential anomaly. !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 SSH(i,j) = (za(i,j) - alpha_ref*p(i,j,1)) * I_gEarth - G%Z_ref & - max(-G%bathyT(i,j)-G%Z_ref, 0.0) enddo ; enddo call calc_SAL(SSH, e_sal, G, CS%SAL_CSp) - else + !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e_sal(i,j) = 0.0 + za(i,j) = za(i,j) - GV%g_Earth * e_sal(i,j) enddo ; enddo endif + ! Calculate and add the tidal geopotential anomaly. if (CS%tides) then - ! Find and add the tidal geopotential anomaly. call calc_tidal_forcing(CS%Time, e_tide_eq, e_tide_sal, G, US, CS%tides_CSp) !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - za(i,j) = za(i,j) - GV%g_Earth * (e_sal(i,j) + e_tide_eq(i,j) + e_tide_sal(i,j)) - enddo ; enddo - else - !$OMP parallel do default(shared) - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - za(i,j) = za(i,j) - GV%g_Earth * e_sal(i,j) + za(i,j) = za(i,j) - GV%g_Earth * (e_tide_eq(i,j) + e_tide_sal(i,j)) enddo ; enddo endif @@ -472,7 +467,8 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm ! Local variables real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: e ! Interface height in depth units [Z ~> m]. real, dimension(SZI_(G),SZJ_(G)) :: & - e_sal, & ! The bottom geopotential anomaly due to self-attraction and loading [Z ~> m]. + e_sal_tide, & ! The summation of self-attraction and loading and tidal forcing [Z ~> m]. + e_sal, & ! The bottom geopotential anomaly due to self-attraction and loading [Z ~> m]. e_tide_eq, & ! The bottom geopotential anomaly due to tidal forces from astronomical sources ! [Z ~> m]. e_tide_sal, & ! The bottom geopotential anomaly due to harmonic self-attraction and loading @@ -560,6 +556,8 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm ! The following two if-statements are arranged in a way that answers are not ! changed from old versions in which SAL is part of the tidal forcing module. + + ! Calculate and add the self-attraction and loading geopotential anomaly. if (CS%calculate_SAL) then ! Determine the surface height anomaly for calculating self attraction ! and loading. This should really be based on bottom pressure anomalies, @@ -582,11 +580,21 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm enddo ; enddo endif + ! Calculate and add the tidal geopotential anomaly. if (CS%tides) then - call calc_tidal_forcing(CS%Time, e_tide_eq, e_tide_sal, G, US, CS%tides_CSp) + if (CS%tide_answer_date>20230630) then + call calc_tidal_forcing(CS%Time, e_tide_eq, e_tide_sal, G, US, CS%tides_CSp) + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e_sal_tide(i,j) = e_sal(i,j) + (e_tide_eq(i,j) + e_tide_sal(i,j)) + enddo ; enddo + else + call calc_tidal_forcing_legacy(CS%Time, e_sal, e_sal_tide, e_tide_eq, e_tide_sal, & + G, US, CS%tides_CSp) + endif !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e(i,j,nz+1) = -(G%bathyT(i,j) + (e_sal(i,j) + e_tide_eq(i,j) + e_tide_sal(i,j))) + e(i,j,nz+1) = -(G%bathyT(i,j) + e_sal_tide(i,j)) enddo ; enddo else !$OMP parallel do default(shared) @@ -787,12 +795,12 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm endif if (present(eta)) then - if (CS%tides) then ! eta is the sea surface height relative to a time-invariant geoid, for comparison with ! what is used for eta in btstep. See how e was calculated about 200 lines above. + if (CS%tides) then !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta(i,j) = e(i,j,1)*GV%Z_to_H + (e_sal(i,j) + e_tide_eq(i,j) + e_tide_sal(i,j))*GV%Z_to_H + eta(i,j) = e(i,j,1)*GV%Z_to_H + (e_sal_tide(i,j))*GV%Z_to_H enddo ; enddo else !$OMP parallel do default(shared) @@ -841,7 +849,7 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm ! To be consistent with old runs, tidal forcing diagnostic also includes total SAL. ! New diagnostics are given for each individual field. - if (CS%id_e_tide>0) call post_data(CS%id_e_tide, e_sal+e_tide_eq+e_tide_sal, CS%diag) + if (CS%id_e_tide>0) call post_data(CS%id_e_tide, e_sal_tide, CS%diag) if (CS%id_e_sal>0) call post_data(CS%id_e_sal, e_sal, CS%diag) if (CS%id_e_tide_eq>0) call post_data(CS%id_e_tide_eq, e_tide_eq, CS%diag) if (CS%id_e_tide_sal>0) call post_data(CS%id_e_tide_sal, e_tide_sal, CS%diag) @@ -867,6 +875,7 @@ subroutine PressureForce_FV_init(Time, G, GV, US, param_file, diag, CS, SAL_CSp, ! Local variables real :: Stanley_coeff ! Coefficient relating the temperature gradient and sub-gridscale ! temperature variance [nondim] + integer :: default_answer_date ! Global answer date ! This include declares and sets the variable "version". # include "version_variable.h" character(len=40) :: mdl ! This module's name. @@ -888,6 +897,16 @@ subroutine PressureForce_FV_init(Time, G, GV, US, param_file, diag, CS, SAL_CSp, units="kg m-3", default=GV%Rho0*US%R_to_kg_m3, scale=US%kg_m3_to_R) call get_param(param_file, mdl, "TIDES", CS%tides, & "If true, apply tidal momentum forcing.", default=.false.) + if (CS%tides .and. GV%Boussinesq) then + call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & + "This sets the default value for the various _ANSWER_DATE parameters.", & + default=99991231) + call get_param(param_file, mdl, "TIDES_ANSWER_DATE", CS%tide_answer_date, & + "The vintage of self-attraction and loading (SAL) and tidal forcing calculations in "//& + "Boussinesq mode. Values below 20230701 recover the old answers in which the SAL is "//& + "part of the tidal forcing calculation. The change is due to a reordered summation "//& + "and the difference is only at bit level.", default=20230630) + endif call get_param(param_file, mdl, "CALCULATE_SAL", CS%calculate_SAL, & "If true, calculate self-attraction and loading.", default=CS%tides) call get_param(param_file, "MOM", "USE_REGRIDDING", use_ALE, & diff --git a/src/core/MOM_PressureForce_Montgomery.F90 b/src/core/MOM_PressureForce_Montgomery.F90 index 53f6a5a925..a22c73fa4d 100644 --- a/src/core/MOM_PressureForce_Montgomery.F90 +++ b/src/core/MOM_PressureForce_Montgomery.F90 @@ -186,6 +186,12 @@ subroutine PressureForce_Mont_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pb endif endif + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + geopot_bot(i,j) = -GV%g_Earth * G%bathyT(i,j) + enddo ; enddo + + ! Calculate and add the self-attraction and loading geopotential anomaly. if (CS%calculate_SAL) then ! Determine the sea surface height anomalies, to enable the calculation ! of self-attraction and loading. @@ -209,23 +215,20 @@ subroutine PressureForce_Mont_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pb SSH(i,j) = SSH(i,j) + GV%H_to_RZ * h(i,j,k) * alpha_Lay(k) enddo ; enddo ; enddo endif + call calc_SAL(SSH, e_sal, G, CS%SAL_CSp) - else + !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e_sal(i,j) = 0.0 + geopot_bot(i,j) = geopot_bot(i,j) - GV%g_Earth*e_sal(i,j) enddo ; enddo endif + ! Calculate and add the tidal geopotential anomaly. if (CS%tides) then call calc_tidal_forcing(CS%Time, e_tide_eq, e_tide_sal, G, US, CS%tides_CSp) !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - geopot_bot(i,j) = -GV%g_Earth*(e_sal(i,j) + e_tide_eq(i,j) + e_tide_sal(i,j) + G%bathyT(i,j)) - enddo ; enddo - else - !$OMP parallel do default(shared) - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - geopot_bot(i,j) = -GV%g_Earth*(e_sal(i,j) + G%bathyT(i,j)) + geopot_bot(i,j) = geopot_bot(i,j) - GV%g_Earth*(e_tide_eq(i,j) + e_tide_sal(i,j)) enddo ; enddo endif @@ -460,7 +463,12 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, I_Rho0 = 1.0/CS%Rho0 G_Rho0 = GV%g_Earth / GV%Rho0 + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e(i,j,nz+1) = -G%bathyT(i,j) + enddo ; enddo + ! Calculate and add the self-attraction and loading geopotential anomaly. if (CS%calculate_SAL) then ! Determine the surface height anomaly for calculating self attraction ! and loading. This should really be based on bottom pressure anomalies, @@ -474,22 +482,18 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, enddo ; enddo enddo call calc_SAL(SSH, e_sal, G, CS%SAL_CSp) - else + !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e_sal(i,j) = 0.0 + e(i,j,nz+1) = e(i,j,nz+1) - e_sal(i,j) enddo ; enddo endif + ! Calculate and add the tidal geopotential anomaly. if (CS%tides) then call calc_tidal_forcing(CS%Time, e_tide_eq, e_tide_sal, G, US, CS%tides_CSp) !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e(i,j,nz+1) = -(G%bathyT(i,j) + (e_sal(i,j) + e_tide_eq(i,j) + e_tide_sal(i,j))) - enddo ; enddo - else - !$OMP parallel do default(shared) - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e(i,j,nz+1) = -(G%bathyT(i,j) + e_sal(i,j)) + e(i,j,nz+1) = e(i,j,nz+1) - (e_tide_eq(i,j) + e_tide_sal(i,j)) enddo ; enddo endif @@ -608,18 +612,23 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, endif ! use_EOS if (present(eta)) then - if (CS%tides) then ! eta is the sea surface height relative to a time-invariant geoid, for ! comparison with what is used for eta in btstep. See how e was calculated ! about 200 lines above. + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + eta(i,j) = e(i,j,1)*GV%Z_to_H + enddo ; enddo + if (CS%tides) then !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta(i,j) = e(i,j,1)*GV%Z_to_H + (e_sal(i,j)+e_tide_eq(i,j)+e_tide_sal(i,j))*GV%Z_to_H + eta(i,j) = eta(i,j) + (e_tide_eq(i,j)+e_tide_sal(i,j))*GV%Z_to_H enddo ; enddo - else + endif + if (CS%calculate_SAL) then !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta(i,j) = e(i,j,1)*GV%Z_to_H + e_sal(i,j)*GV%Z_to_H + eta(i,j) = eta(i,j) + e_sal(i,j)*GV%Z_to_H enddo ; enddo endif endif diff --git a/src/parameterizations/lateral/MOM_tidal_forcing.F90 b/src/parameterizations/lateral/MOM_tidal_forcing.F90 index 38286f20c5..948a39f2dc 100644 --- a/src/parameterizations/lateral/MOM_tidal_forcing.F90 +++ b/src/parameterizations/lateral/MOM_tidal_forcing.F90 @@ -16,6 +16,7 @@ module MOM_tidal_forcing implicit none ; private public calc_tidal_forcing, tidal_forcing_init, tidal_forcing_end +public calc_tidal_forcing_legacy ! MOM_open_boundary uses the following to set tides on the boundary. public astro_longitudes_init, eq_phase, nodal_fu, tidal_frequency @@ -619,6 +620,88 @@ subroutine calc_tidal_forcing(Time, e_tide_eq, e_tide_sal, G, US, CS) end subroutine calc_tidal_forcing +!> This subroutine functions the same as calc_tidal_forcing but outputs a field that combines +!! previously calculated self-attraction and loading (SAL) and tidal forcings, so that old answers +!! can be preserved bitwise before SAL is separated out as an individual module. +subroutine calc_tidal_forcing_legacy(Time, e_sal, e_sal_tide, e_tide_eq, e_tide_sal, G, US, CS) + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + type(time_type), intent(in) :: Time !< The time for the caluculation. + real, dimension(SZI_(G),SZJ_(G)), intent(in) :: e_sal !< The self-attraction and loading fields + !! calculated previously used to + !! initialized e_sal_tide [Z ~> m]. + real, dimension(SZI_(G),SZJ_(G)), intent(out) :: e_sal_tide !< The total geopotential height anomalies + !! due to both SAL and tidal forcings [Z ~> m]. + real, dimension(SZI_(G),SZJ_(G)), intent(out) :: e_tide_eq !< The geopotential height anomalies + !! due to the equilibrium tides [Z ~> m]. + real, dimension(SZI_(G),SZJ_(G)), intent(out) :: e_tide_sal !< The geopotential height anomalies + !! due to the tidal SAL [Z ~> m]. + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + type(tidal_forcing_CS), intent(in) :: CS !< The control structure returned by a + !! previous call to tidal_forcing_init. + + ! Local variables + real :: now ! The relative time compared with the tidal reference [T ~> s] + real :: amp_cosomegat, amp_sinomegat ! The tidal amplitudes times the components of phase [Z ~> m] + real :: cosomegat, sinomegat ! The components of the phase [nondim] + real :: amp_cossin ! A temporary field that adds cosines and sines [nondim] + integer :: i, j, c, m, is, ie, js, je, Isq, Ieq, Jsq, Jeq + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB + + call cpu_clock_begin(id_clock_tides) + + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e_sal_tide(i,j) = 0.0 + e_tide_eq(i,j) = 0.0 + e_tide_sal(i,j) = 0.0 + enddo ; enddo + + if (CS%nc == 0) then + return + endif + + now = US%s_to_T * time_type_to_real(Time - cs%time_ref) + + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e_sal_tide(i,j) = e_sal(i,j) + enddo ; enddo + + do c=1,CS%nc + m = CS%struct(c) + amp_cosomegat = CS%amp(c)*CS%love_no(c) * cos(CS%freq(c)*now + CS%phase0(c)) + amp_sinomegat = CS%amp(c)*CS%love_no(c) * sin(CS%freq(c)*now + CS%phase0(c)) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + amp_cossin = (amp_cosomegat*CS%cos_struct(i,j,m) + amp_sinomegat*CS%sin_struct(i,j,m)) + e_sal_tide(i,j) = e_sal_tide(i,j) + amp_cossin + e_tide_eq(i,j) = e_tide_eq(i,j) + amp_cossin + enddo ; enddo + enddo + + if (CS%use_tidal_sal_file) then ; do c=1,CS%nc + cosomegat = cos(CS%freq(c)*now) + sinomegat = sin(CS%freq(c)*now) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + amp_cossin = CS%ampsal(i,j,c) & + * (cosomegat*CS%cosphasesal(i,j,c) + sinomegat*CS%sinphasesal(i,j,c)) + e_sal_tide(i,j) = e_sal_tide(i,j) + amp_cossin + e_tide_sal(i,j) = e_tide_sal(i,j) + amp_cossin + enddo ; enddo + enddo ; endif + + if (CS%use_tidal_sal_prev) then ; do c=1,CS%nc + cosomegat = cos(CS%freq(c)*now) + sinomegat = sin(CS%freq(c)*now) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + amp_cossin = -CS%sal_scalar * CS%amp_prev(i,j,c) & + * (cosomegat*CS%cosphase_prev(i,j,c) + sinomegat*CS%sinphase_prev(i,j,c)) + e_sal_tide(i,j) = e_sal_tide(i,j) + amp_cossin + e_tide_sal(i,j) = e_tide_sal(i,j) + amp_cossin + enddo ; enddo + enddo ; endif + call cpu_clock_end(id_clock_tides) + +end subroutine calc_tidal_forcing_legacy + !> This subroutine deallocates memory associated with the tidal forcing module. subroutine tidal_forcing_end(CS) type(tidal_forcing_CS), intent(inout) :: CS !< The control structure returned by a previous call From 3061166a1a19a52036aa007065f07dd2efd15b0e Mon Sep 17 00:00:00 2001 From: He Wang Date: Mon, 19 Jun 2023 22:15:43 -0400 Subject: [PATCH 06/17] Refactor SAL and tides calls in Boussinesq mode Consistent with the non-Boussinesq option with the new answer option --- src/core/MOM_PressureForce_FV.F90 | 133 ++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 44 deletions(-) diff --git a/src/core/MOM_PressureForce_FV.F90 b/src/core/MOM_PressureForce_FV.F90 index 4314c1f7df..da94add51e 100644 --- a/src/core/MOM_PressureForce_FV.F90 +++ b/src/core/MOM_PressureForce_FV.F90 @@ -63,7 +63,7 @@ module MOM_PressureForce_FV !! By the default (1) is for a piecewise linear method logical :: use_stanley_pgf !< If true, turn on Stanley parameterization in the PGF - integer :: tide_answer_date !< Recover old answers with tides in Boussinesq mode + integer :: tides_answer_date !< Recover old answers with tides in Boussinesq mode integer :: id_e_tide = -1 !< Diagnostic identifier integer :: id_e_tide_eq = -1 !< Diagnostic identifier integer :: id_e_tide_sal = -1 !< Diagnostic identifier @@ -554,53 +554,79 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm G_Rho0 = GV%g_Earth / GV%Rho0 rho_ref = CS%Rho0 - ! The following two if-statements are arranged in a way that answers are not - ! changed from old versions in which SAL is part of the tidal forcing module. + if (CS%tides_answer_date>20230630) then + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e(i,j,nz+1) = -G%bathyT(i,j) + enddo ; enddo - ! Calculate and add the self-attraction and loading geopotential anomaly. - if (CS%calculate_SAL) then - ! Determine the surface height anomaly for calculating self attraction - ! and loading. This should really be based on bottom pressure anomalies, - ! but that is not yet implemented, and the current form is correct for - ! barotropic tides. - !$OMP parallel do default(shared) - do j=Jsq,Jeq+1 - do i=Isq,Ieq+1 - SSH(i,j) = min(-G%bathyT(i,j) - G%Z_ref, 0.0) + ! Calculate and add the self-attraction and loading geopotential anomaly. + if (CS%calculate_SAL) then + ! Determine the surface height anomaly for calculating self attraction + ! and loading. This should really be based on bottom pressure anomalies, + ! but that is not yet implemented, and the current form is correct for + ! barotropic tides. + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 + do i=Isq,Ieq+1 + SSH(i,j) = min(-G%bathyT(i,j) - G%Z_ref, 0.0) + enddo + do k=1,nz ; do i=Isq,Ieq+1 + SSH(i,j) = SSH(i,j) + h(i,j,k)*GV%H_to_Z + enddo ; enddo enddo - do k=1,nz ; do i=Isq,Ieq+1 - SSH(i,j) = SSH(i,j) + h(i,j,k)*GV%H_to_Z + call calc_SAL(SSH, e_sal, G, CS%SAL_CSp) + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e(i,j,nz+1) = e(i,j,nz+1) - e_sal(i,j) enddo ; enddo - enddo - call calc_SAL(SSH, e_sal, G, CS%SAL_CSp) - else - !$OMP parallel do default(shared) - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e_sal(i,j) = 0.0 - enddo ; enddo - endif + endif - ! Calculate and add the tidal geopotential anomaly. - if (CS%tides) then - if (CS%tide_answer_date>20230630) then + ! Calculate and add the tidal geopotential anomaly. + if (CS%tides) then call calc_tidal_forcing(CS%Time, e_tide_eq, e_tide_sal, G, US, CS%tides_CSp) !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e_sal_tide(i,j) = e_sal(i,j) + (e_tide_eq(i,j) + e_tide_sal(i,j)) + e(i,j,nz+1) = e(i,j,nz+1) - (e_tide_eq(i,j) + e_tide_sal(i,j)) enddo ; enddo + endif + else ! Old answers + ! Calculate and add the self-attraction and loading geopotential anomaly. + if (CS%calculate_SAL) then + ! Determine the surface height anomaly for calculating self attraction + ! and loading. This should really be based on bottom pressure anomalies, + ! but that is not yet implemented, and the current form is correct for + ! barotropic tides. + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 + do i=Isq,Ieq+1 + SSH(i,j) = min(-G%bathyT(i,j) - G%Z_ref, 0.0) + enddo + do k=1,nz ; do i=Isq,Ieq+1 + SSH(i,j) = SSH(i,j) + h(i,j,k)*GV%H_to_Z + enddo ; enddo + enddo + call calc_SAL(SSH, e_sal, G, CS%SAL_CSp) else + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e_sal(i,j) = 0.0 + enddo ; enddo + endif + + ! Calculate and add the tidal geopotential anomaly. + if (CS%tides) then call calc_tidal_forcing_legacy(CS%Time, e_sal, e_sal_tide, e_tide_eq, e_tide_sal, & G, US, CS%tides_CSp) + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e(i,j,nz+1) = -(G%bathyT(i,j) + e_sal_tide(i,j)) + enddo ; enddo + else + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + e(i,j,nz+1) = -(G%bathyT(i,j) + e_sal(i,j)) + enddo ; enddo endif - !$OMP parallel do default(shared) - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e(i,j,nz+1) = -(G%bathyT(i,j) + e_sal_tide(i,j)) - enddo ; enddo - else - !$OMP parallel do default(shared) - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - e(i,j,nz+1) = -(G%bathyT(i,j) + e_sal(i,j)) - enddo ; enddo endif !$OMP parallel do default(shared) @@ -797,16 +823,35 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm if (present(eta)) then ! eta is the sea surface height relative to a time-invariant geoid, for comparison with ! what is used for eta in btstep. See how e was calculated about 200 lines above. - if (CS%tides) then - !$OMP parallel do default(shared) - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta(i,j) = e(i,j,1)*GV%Z_to_H + (e_sal_tide(i,j))*GV%Z_to_H - enddo ; enddo - else + if (CS%tides_answer_date>20230630) then !$OMP parallel do default(shared) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - eta(i,j) = (e(i,j,1) + e_sal(i,j))*GV%Z_to_H + eta(i,j) = e(i,j,1)*GV%Z_to_H enddo ; enddo + if (CS%tides) then + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + eta(i,j) = eta(i,j) + (e_tide_eq(i,j)+e_tide_sal(i,j))*GV%Z_to_H + enddo ; enddo + endif + if (CS%calculate_SAL) then + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + eta(i,j) = eta(i,j) + e_sal(i,j)*GV%Z_to_H + enddo ; enddo + endif + else ! Old answers + if (CS%tides) then + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + eta(i,j) = e(i,j,1)*GV%Z_to_H + (e_sal_tide(i,j))*GV%Z_to_H + enddo ; enddo + else + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + eta(i,j) = (e(i,j,1) + e_sal(i,j))*GV%Z_to_H + enddo ; enddo + endif endif endif @@ -901,7 +946,7 @@ subroutine PressureForce_FV_init(Time, G, GV, US, param_file, diag, CS, SAL_CSp, call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "TIDES_ANSWER_DATE", CS%tide_answer_date, & + call get_param(param_file, mdl, "TIDES_ANSWER_DATE", CS%tides_answer_date, & "The vintage of self-attraction and loading (SAL) and tidal forcing calculations in "//& "Boussinesq mode. Values below 20230701 recover the old answers in which the SAL is "//& "part of the tidal forcing calculation. The change is due to a reordered summation "//& From 3515b804ecb0c98f06b50a56ab95d997efc78a69 Mon Sep 17 00:00:00 2001 From: He Wang Date: Thu, 17 Aug 2023 01:30:25 -0400 Subject: [PATCH 07/17] Change SAL related parameter names This commit revises changes of some parameter names from a previous commit. * Logical switch TIDE_USE_SAL_SCALAR is obsolete and replaced by SAL_SCALAR_APPROX. * TIDE_SAL_SCALAR_VALUE is replaced by SAL_SCALAR_VALUE. The old parameter name is still accepted but a warning is given. * Logical switch TIDAL_SAL_SHT is obsolete and replaced by SAL_HARMONICS. * TIDAL_SAL_SHT_DEGREE is obsolete and replaced by SAL_HARMONICS_DEGREE. * RHO_E is replaced by RHO_SOLID_EARTH. --- src/diagnostics/MOM_obsolete_params.F90 | 5 ++ .../lateral/MOM_self_attr_load.F90 | 86 ++++++------------- .../lateral/MOM_spherical_harmonics.F90 | 11 +-- .../lateral/MOM_tidal_forcing.F90 | 2 +- 4 files changed, 38 insertions(+), 66 deletions(-) diff --git a/src/diagnostics/MOM_obsolete_params.F90 b/src/diagnostics/MOM_obsolete_params.F90 index 21a09dfdbb..7614eb210c 100644 --- a/src/diagnostics/MOM_obsolete_params.F90 +++ b/src/diagnostics/MOM_obsolete_params.F90 @@ -111,6 +111,11 @@ subroutine find_obsolete_params(param_file) call obsolete_logical(param_file, "SMOOTH_RI", hint="Instead use N_SMOOTH_RI.") + call obsolete_logical(param_file, "TIDE_USE_SAL_SCALAR", hint="Use SAL_SCALAR_APPROX instead.") + call obsolete_logical(param_file, "TIDAL_SAL_SHT", hint="Use SAL_HARMONICS instead.") + call obsolete_int(param_file, "TIDAL_SAL_SHT_DEGREE", hint="Use SAL_HARMONICS_DEGREE instead.") + call obsolete_real(param_file, "RHO_E", hint="Use RHO_SOLID_EARTH instead.") + ! Write the file version number to the model log. call log_version(param_file, mdl, version) diff --git a/src/parameterizations/lateral/MOM_self_attr_load.F90 b/src/parameterizations/lateral/MOM_self_attr_load.F90 index be45f64cfe..fa5d973989 100644 --- a/src/parameterizations/lateral/MOM_self_attr_load.F90 +++ b/src/parameterizations/lateral/MOM_self_attr_load.F90 @@ -3,7 +3,7 @@ module MOM_self_attr_load use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, CLOCK_MODULE use MOM_domains, only : pass_var use MOM_error_handler, only : MOM_error, FATAL, WARNING -use MOM_file_parser, only : get_param, log_param, log_version, param_file_type +use MOM_file_parser, only : read_param, get_param, log_version, param_file_type use MOM_obsolete_params, only : obsolete_logical, obsolete_int use MOM_grid, only : ocean_grid_type use MOM_unit_scaling, only : unit_scale_type @@ -169,6 +169,10 @@ subroutine SAL_init(G, US, param_file, CS) call log_version(param_file, mdl, version, "") call get_param(param_file, '', "TIDES", tides, default=.false., do_not_log=.True.) + call get_param(param_file, mdl, "CALCULATE_SAL", calculate_sal, "If true, calculate "//& + " self-attraction and loading.", default=tides, do_not_log=.True.) + if (.not. calculate_sal) return + if (tides) then call get_param(param_file, '', "USE_PREVIOUS_TIDES", CS%use_tidal_sal_prev, & default=.false., do_not_log=.True.) @@ -176,73 +180,39 @@ subroutine SAL_init(G, US, param_file, CS) default=.false., do_not_log=.True.) endif - ! TIDE_USE_SAL_SCALAR is going to be replaced by USE_SAL_SCALAR. During the transition, the default of - ! USE_SAL_SCALAR is set to be consistent with TIDE_USE_SAL_SCALAR before the implementation of spherical - ! harmonics SAL. - ! A FATAL error is only issued when the user specified TIDE_USE_SAL_SCALAR contradicts USE_SAL_SCALAR. - call get_param(param_file, mdl, "USE_SAL_SCALAR", CS%use_sal_scalar, & + call get_param(param_file, mdl, "SAL_SCALAR_APPROX", CS%use_sal_scalar, & "If true, use the scalar approximation to calculate self-attraction and"//& - " loading. This parameter is to replace TIDE_USE_SAL_SCALAR, as SAL applies"//& - " to all motions. When both USE_SAL_SCALAR and TIDE_USE_SAL_SCALAR are"//& - " specified, USE_SAL_SCALAR overrides TIDE_USE_SAL_SCALAR.", & - default=tides .and. (.not.use_tidal_sal_file)) - if (tides) then - call obsolete_logical(param_file, "TIDE_USE_SAL_SCALAR", warning_val=CS%use_sal_scalar, & - hint="Use USE_SAL_SCALAR instead.") - endif - - call get_param(param_file, mdl, "USE_SAL_HARMONICS", CS%use_sal_sht, & + " loading.", default=tides .and. (.not.use_tidal_sal_file)) + call get_param(param_file, mdl, "SAL_HARMONICS", CS%use_sal_sht, & "If true, use the online spherical harmonics method to calculate"//& " self-attraction and loading.", default=.false.) - ! This is a more of a hard obsolete but should only impact a handful of users. - call obsolete_logical(param_file, "TIDAL_SAL_SHT", warning_val=CS%use_sal_sht, & - hint="Use USE_SAL_HARMONICS instead.") - - call get_param(param_file, mdl, "CALCULATE_SAL", calculate_sal, & - "If true, calculate self-attraction and loading.", default=tides) - if ((.not. calculate_sal) .and. (CS%use_tidal_sal_prev .or. CS%use_sal_scalar .or. CS%use_sal_sht)) & - call MOM_error(FATAL, trim(mdl)//": CALCULATE_SAL is False but one of the options is True.") - - ! TIDE_SAL_SCALAR_VALUE is going to be replaced by SAL_SCALAR_VALUE. The following segment of codes - ! should eventually be replaced by the commented code below. - ! if (CS%use_sal_scalar .or. CS%use_tidal_sal_prev) & - ! call get_param(param_file, mdl, "SAL_SCALAR_VALUE", CS%sal_scalar_value, & - ! "The constant of proportionality between sea surface "//& - ! "height (really it should be bottom pressure) anomalies "//& - ! "and bottom geopotential anomalies. This is only used if "//& - ! "USE_SAL_SCALAR is true or USE_PREVIOUS_TIDES is true.", & - ! fail_if_missing=.true., units="m m-1") - ! endif + if (CS%use_sal_scalar .or. CS%use_tidal_sal_prev) then - CS%sal_scalar_value = -9e35 - call get_param(param_file, mdl, "SAL_SCALAR_VALUE", CS%sal_scalar_value, "", do_not_log=.True.) - if (CS%sal_scalar_value == -9e35) then - call get_param(param_file, '', "TIDE_SAL_SCALAR_VALUE", CS%sal_scalar_value, do_not_log=.True.) - if (CS%sal_scalar_value /= -9e35) & - call MOM_error(WARNING, "TIDE_SAL_SCALAR_VALUE is a deprecated parameter. "//& - "Use SAL_SCALAR_VALUE instead.") - endif - if (CS%sal_scalar_value == -9e35) & - call MOM_error(FATAL, trim(mdl)//": USE_SAL_SCALAR is true but SAL_SCALAR_VALUE is not set.") - call log_param(param_file, mdl, "SAL_SCALAR_VALUE", CS%sal_scalar_value, & + call get_param(param_file, '', "TIDE_SAL_SCALAR_VALUE", tide_sal_scalar_value, & + units="m m-1", default=0.0, do_not_log=.True.) + if (tide_sal_scalar_value/=0.0) & + call MOM_error(WARNING, "TIDE_SAL_SCALAR_VALUE is a deprecated parameter. "//& + "Use SAL_SCALAR_VALUE instead." ) + call get_param(param_file, mdl, "SAL_SCALAR_VALUE", CS%sal_scalar_value, & "The constant of proportionality between sea surface "//& "height (really it should be bottom pressure) anomalies "//& "and bottom geopotential anomalies. This is only used if "//& - "USE_SAL_SCALAR is true or USE_PREVIOUS_TIDES is true.", units="m m-1") + "USE_SAL_SCALAR is true or USE_PREVIOUS_TIDES is true.", & + default=tide_sal_scalar_value, units="m m-1", & + do_not_log=(.not. CS%use_sal_scalar) .and. (.not. CS%use_tidal_sal_prev)) endif if (CS%use_sal_sht) then call get_param(param_file, mdl, "SAL_HARMONICS_DEGREE", CS%sal_sht_Nd, & "The maximum degree of the spherical harmonics transformation used for "// & "calculating the self-attraction and loading term.", & - default=0) - call obsolete_int(param_file, "TIDAL_SAL_SHT_DEGREE", warning_val=CS%sal_sht_Nd, & - hint="Use SAL_HARMONICS_DEGREE instead.") - call get_param(param_file, '', "RHO_0", rhoW, default=1035.0, scale=US%kg_m3_to_R, do_not_log=.True.) - call get_param(param_file, mdl, "RHO_E", rhoE, & + default=0, do_not_log=.not.CS%use_sal_sht) + call get_param(param_file, '', "RHO_0", rhoW, default=1035.0, scale=US%kg_m3_to_R, & + units="kg m-3", do_not_log=.True.) + call get_param(param_file, mdl, "RHO_SOLID_EARTH", rhoE, & "The mean solid earth density. This is used for calculating the "// & "self-attraction and loading term.", units="kg m-3", & - default=5517.0, scale=US%kg_m3_to_R) + default=5517.0, scale=US%kg_m3_to_R, do_not_log=.not. CS%use_sal_sht) lmax = calc_lmax(CS%sal_sht_Nd) allocate(CS%Snm_Re(lmax)); CS%Snm_Re(:) = 0.0 allocate(CS%Snm_Im(lmax)); CS%Snm_Im(:) = 0.0 @@ -274,14 +244,14 @@ end subroutine SAL_end !! (rather, it should be bottom pressure anomaly). SAL is primarily used for fast evolving processes like tides or !! storm surges, but the effect applies to all motions. !! -!! If TIDE_USE_SAL_SCALAR is true, a scalar approximation is applied (Accad and Pekeris 1978) and the SAL is simply -!! a fraction (set by TIDE_SAL_SCALAR_VALUE, usually around 10% for global tides) of local SSH . For the tides, the -!! scalar approximation can also be used to iterate the SAL to convergence [see USE_PREVIOUS_TIDES in MOM_tidal_forcing, +!! If SAL_SCALAR_APPROX is true, a scalar approximation is applied (Accad and Pekeris 1978) and the SAL is simply +!! a fraction (set by SAL_SCALAR_VALUE, usually around 10% for global tides) of local SSH . For tides, the scalar +!! approximation can also be used to iterate the SAL to convergence [see USE_PREVIOUS_TIDES in MOM_tidal_forcing, !! Arbic et al. (2004)]. !! -!! If TIDAL_SAL_SHT is true, a more accurate online spherical harmonic transforms are used to calculate SAL. +!! If SAL_HARMONICS is true, a more accurate online spherical harmonic transforms are used to calculate SAL. !! Subroutines in module MOM_spherical_harmonics are called and the degree of spherical harmonic transforms is set by -!! TIDAL_SAL_SHT_DEGREE. The algorithm is based on SAL calculation in Model for Prediction Across Scales (MPAS)-Ocean +!! SAL_HARMONICS_DEGREE. The algorithm is based on SAL calculation in Model for Prediction Across Scales (MPAS)-Ocean !! developed by Los Alamos National Laboratory and University of Michigan [Barton et al. (2022) and Brus et al. (2023)]. !! !! References: diff --git a/src/parameterizations/lateral/MOM_spherical_harmonics.F90 b/src/parameterizations/lateral/MOM_spherical_harmonics.F90 index b20df036e0..2a72d26a20 100644 --- a/src/parameterizations/lateral/MOM_spherical_harmonics.F90 +++ b/src/parameterizations/lateral/MOM_spherical_harmonics.F90 @@ -217,7 +217,7 @@ subroutine spherical_harmonics_init(G, param_file, CS) integer :: is, ie, js, je integer :: i, j, k integer :: m, n - integer :: Nd_tidal_SAL ! Maximum degree for tidal SAL + integer :: Nd_SAL ! Maximum degree for SAL ! This include declares and sets the variable "version". # include "version_variable.h" character(len=40) :: mdl = "MOM_spherical_harmonics" ! This module's name. @@ -228,11 +228,8 @@ subroutine spherical_harmonics_init(G, param_file, CS) is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec call log_version(param_file, mdl, version, "") - call get_param(param_file, mdl, "TIDAL_SAL_SHT_DEGREE", Nd_tidal_SAL, & - "The maximum degree of the spherical harmonics transformation used for "// & - "calculating the self-attraction and loading term for tides.", & - default=0, do_not_log=.true.) - CS%ndegree = Nd_tidal_SAL + call get_param(param_file, mdl, "SAL_HARMONICS_DEGREE", Nd_SAL, "", default=0, do_not_log=.true.) + CS%ndegree = Nd_SAL CS%lmax = calc_lmax(CS%ndegree) call get_param(param_file, mdl, "SHT_REPRODUCING_SUM", CS%reprod_sum, & "If true, use reproducing sums (invariant to PE layout) in inverse transform "// & @@ -361,7 +358,7 @@ end function order2index !! array vectorization. !! !! The maximum degree of the spherical harmonics is a runtime parameter and the maximum used by all SHT applications. -!! At the moment, it is only decided by TIDAL_SAL_SHT_DEGREE. +!! At the moment, it is only decided by SAL_HARMONICS_DEGREE. !! !! The forward transforms involve a global summation. Runtime flag SHT_REPRODUCING_SUM controls whether this is done !! in a bit-wise reproducing way or not. diff --git a/src/parameterizations/lateral/MOM_tidal_forcing.F90 b/src/parameterizations/lateral/MOM_tidal_forcing.F90 index 948a39f2dc..1ef55fff7f 100644 --- a/src/parameterizations/lateral/MOM_tidal_forcing.F90 +++ b/src/parameterizations/lateral/MOM_tidal_forcing.F90 @@ -356,7 +356,7 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) ! If it is being used, sal_scalar MUST be specified in param_file. if (CS%use_tidal_sal_prev) & call get_param(param_file, mdl, "SAL_SCALAR_VALUE", CS%sal_scalar, fail_if_missing=.true., & - do_not_log=.True.) + units="m m-1", do_not_log=.True.) if (nc > MAX_CONSTITUENTS) then write(mesg,'("Increase MAX_CONSTITUENTS in MOM_tidal_forcing.F90 to at least",I3, & From c6b6143373daf3cb13ecfea8e92f11a72d616f69 Mon Sep 17 00:00:00 2001 From: He Wang Date: Thu, 17 Aug 2023 14:46:21 -0400 Subject: [PATCH 08/17] Modify reading SAL related parameters * An incident of incorrect indent is fixed in SAL_int. * Parameters read in SAL_int is moved out from if statements. DO_NOT_LOG is used to prevent logging unused parameters. * Reading SAL_SCALAR_VALUE in tidal_forcing_init is now consistent with SAL_init. * Some unused variables in tidal_forcing_init are removed. --- .../lateral/MOM_self_attr_load.F90 | 55 +++++++++---------- .../lateral/MOM_tidal_forcing.F90 | 20 ++++--- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/parameterizations/lateral/MOM_self_attr_load.F90 b/src/parameterizations/lateral/MOM_self_attr_load.F90 index fa5d973989..20d239eb53 100644 --- a/src/parameterizations/lateral/MOM_self_attr_load.F90 +++ b/src/parameterizations/lateral/MOM_self_attr_load.F90 @@ -181,38 +181,35 @@ subroutine SAL_init(G, US, param_file, CS) endif call get_param(param_file, mdl, "SAL_SCALAR_APPROX", CS%use_sal_scalar, & - "If true, use the scalar approximation to calculate self-attraction and"//& - " loading.", default=tides .and. (.not.use_tidal_sal_file)) + "If true, use the scalar approximation to calculate self-attraction and "//& + "loading.", default=tides .and. (.not. use_tidal_sal_file)) + call get_param(param_file, '', "TIDE_SAL_SCALAR_VALUE", tide_sal_scalar_value, & + units="m m-1", default=0.0, do_not_log=.True.) + if (tide_sal_scalar_value/=0.0) & + call MOM_error(WARNING, "TIDE_SAL_SCALAR_VALUE is a deprecated parameter. "//& + "Use SAL_SCALAR_VALUE instead." ) + call get_param(param_file, mdl, "SAL_SCALAR_VALUE", CS%sal_scalar_value, & + "The constant of proportionality between sea surface "//& + "height (really it should be bottom pressure) anomalies "//& + "and bottom geopotential anomalies. This is only used if "//& + "USE_SAL_SCALAR is true or USE_PREVIOUS_TIDES is true.", & + default=tide_sal_scalar_value, units="m m-1", & + do_not_log=(.not. CS%use_sal_scalar) .and. (.not. CS%use_tidal_sal_prev)) call get_param(param_file, mdl, "SAL_HARMONICS", CS%use_sal_sht, & - "If true, use the online spherical harmonics method to calculate"//& - " self-attraction and loading.", default=.false.) - - if (CS%use_sal_scalar .or. CS%use_tidal_sal_prev) then - call get_param(param_file, '', "TIDE_SAL_SCALAR_VALUE", tide_sal_scalar_value, & - units="m m-1", default=0.0, do_not_log=.True.) - if (tide_sal_scalar_value/=0.0) & - call MOM_error(WARNING, "TIDE_SAL_SCALAR_VALUE is a deprecated parameter. "//& - "Use SAL_SCALAR_VALUE instead." ) - call get_param(param_file, mdl, "SAL_SCALAR_VALUE", CS%sal_scalar_value, & - "The constant of proportionality between sea surface "//& - "height (really it should be bottom pressure) anomalies "//& - "and bottom geopotential anomalies. This is only used if "//& - "USE_SAL_SCALAR is true or USE_PREVIOUS_TIDES is true.", & - default=tide_sal_scalar_value, units="m m-1", & - do_not_log=(.not. CS%use_sal_scalar) .and. (.not. CS%use_tidal_sal_prev)) - endif + "If true, use the online spherical harmonics method to calculate "//& + "self-attraction and loading.", default=.false.) + call get_param(param_file, mdl, "SAL_HARMONICS_DEGREE", CS%sal_sht_Nd, & + "The maximum degree of the spherical harmonics transformation used for "// & + "calculating the self-attraction and loading term.", & + default=0, do_not_log=(.not. CS%use_sal_sht)) + call get_param(param_file, '', "RHO_0", rhoW, default=1035.0, scale=US%kg_m3_to_R, & + units="kg m-3", do_not_log=.True.) + call get_param(param_file, mdl, "RHO_SOLID_EARTH", rhoE, & + "The mean solid earth density. This is used for calculating the "// & + "self-attraction and loading term.", units="kg m-3", & + default=5517.0, scale=US%kg_m3_to_R, do_not_log=(.not. CS%use_sal_sht)) if (CS%use_sal_sht) then - call get_param(param_file, mdl, "SAL_HARMONICS_DEGREE", CS%sal_sht_Nd, & - "The maximum degree of the spherical harmonics transformation used for "// & - "calculating the self-attraction and loading term.", & - default=0, do_not_log=.not.CS%use_sal_sht) - call get_param(param_file, '', "RHO_0", rhoW, default=1035.0, scale=US%kg_m3_to_R, & - units="kg m-3", do_not_log=.True.) - call get_param(param_file, mdl, "RHO_SOLID_EARTH", rhoE, & - "The mean solid earth density. This is used for calculating the "// & - "self-attraction and loading term.", units="kg m-3", & - default=5517.0, scale=US%kg_m3_to_R, do_not_log=.not. CS%use_sal_sht) lmax = calc_lmax(CS%sal_sht_Nd) allocate(CS%Snm_Re(lmax)); CS%Snm_Re(:) = 0.0 allocate(CS%Snm_Im(lmax)); CS%Snm_Im(:) = 0.0 diff --git a/src/parameterizations/lateral/MOM_tidal_forcing.F90 b/src/parameterizations/lateral/MOM_tidal_forcing.F90 index 1ef55fff7f..eb481f2131 100644 --- a/src/parameterizations/lateral/MOM_tidal_forcing.F90 +++ b/src/parameterizations/lateral/MOM_tidal_forcing.F90 @@ -256,10 +256,8 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) character(len=40) :: mdl = "MOM_tidal_forcing" ! This module's name. character(len=128) :: mesg character(len=200) :: tidal_input_files(4*MAX_CONSTITUENTS) + real :: tide_sal_scalar_value integer :: i, j, c, is, ie, js, je, isd, ied, jsd, jed, nc - integer :: lmax ! Total modes of the real spherical harmonics [nondim] - real :: rhoW ! The average density of sea water [R ~> kg m-3]. - real :: rhoE ! The average density of Earth [R ~> kg m-3]. is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec isd = G%isd ; ied = G%ied ; jsd = G%jsd; jed = G%jed @@ -353,10 +351,18 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) "If true, use the SAL from the previous iteration of the "//& "tides to facilitate convergent iteration. "//& "This is only used if TIDES is true.", default=.false.) - ! If it is being used, sal_scalar MUST be specified in param_file. - if (CS%use_tidal_sal_prev) & - call get_param(param_file, mdl, "SAL_SCALAR_VALUE", CS%sal_scalar, fail_if_missing=.true., & - units="m m-1", do_not_log=.True.) + call get_param(param_file, '', "TIDE_SAL_SCALAR_VALUE", tide_sal_scalar_value, & + units="m m-1", default=0.0, do_not_log=.True.) + if (tide_sal_scalar_value/=0.0) & + call MOM_error(WARNING, "TIDE_SAL_SCALAR_VALUE is a deprecated parameter. "//& + "Use SAL_SCALAR_VALUE instead." ) + call get_param(param_file, mdl, "SAL_SCALAR_VALUE", CS%sal_scalar, & + "The constant of proportionality between sea surface "//& + "height (really it should be bottom pressure) anomalies "//& + "and bottom geopotential anomalies. This is only used if "//& + "USE_SAL_SCALAR is true or USE_PREVIOUS_TIDES is true.", & + default=tide_sal_scalar_value, units="m m-1", & + do_not_log=(.not. CS%use_tidal_sal_prev)) if (nc > MAX_CONSTITUENTS) then write(mesg,'("Increase MAX_CONSTITUENTS in MOM_tidal_forcing.F90 to at least",I3, & From 095a3b5fe7a60a2eb796232d0c97ea345ef1b87e Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Tue, 22 Aug 2023 16:44:30 -0400 Subject: [PATCH 09/17] *+Use TIDES_ANSWER_DATE with semi-Boussinesq tides Use TIDES_ANSWER_DATE with an additional if-block to recover the dev/gfdl answers in semi-Boussinesq mode runs with tides enabled, similarly to what is already being done in Boussinesq mode. This changes some answers for the nonBous_global and tides_025 test cases back to those that would be obtained with the code on dev/gfdl. TIDES_ANSWER_DATE is now logged in all cases with tides, so the MOM_parameter_doc files change in some non-Boussinesq cases. --- src/core/MOM_PressureForce_FV.F90 | 33 +++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/core/MOM_PressureForce_FV.F90 b/src/core/MOM_PressureForce_FV.F90 index da94add51e..64df200f31 100644 --- a/src/core/MOM_PressureForce_FV.F90 +++ b/src/core/MOM_PressureForce_FV.F90 @@ -127,6 +127,7 @@ subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_ e_tide_eq, & ! The bottom geopotential anomaly due to tidal forces from astronomical sources [Z ~> m]. e_tide_sal, & ! The bottom geopotential anomaly due to harmonic self-attraction and loading ! specific to tides [Z ~> m]. + e_sal_tide, & ! The summation of self-attraction and loading and tidal forcing [Z ~> m]. dM, & ! The barotropic adjustment to the Montgomery potential to ! account for a reduced gravity model [L2 T-2 ~> m2 s-2]. za ! The geopotential anomaly (i.e. g*e + alpha_0*pressure) at the @@ -320,19 +321,31 @@ subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_ enddo ; enddo call calc_SAL(SSH, e_sal, G, CS%SAL_CSp) - !$OMP parallel do default(shared) - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - za(i,j) = za(i,j) - GV%g_Earth * e_sal(i,j) - enddo ; enddo + if ((CS%tides_answer_date>20230630) .or. (.not.GV%semi_Boussinesq) .or. (.not.CS%tides)) then + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + za(i,j) = za(i,j) - GV%g_Earth * e_sal(i,j) + enddo ; enddo + endif endif ! Calculate and add the tidal geopotential anomaly. if (CS%tides) then - call calc_tidal_forcing(CS%Time, e_tide_eq, e_tide_sal, G, US, CS%tides_CSp) - !$OMP parallel do default(shared) - do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - za(i,j) = za(i,j) - GV%g_Earth * (e_tide_eq(i,j) + e_tide_sal(i,j)) - enddo ; enddo + if ((CS%tides_answer_date>20230630) .or. (.not.GV%semi_Boussinesq)) then + call calc_tidal_forcing(CS%Time, e_tide_eq, e_tide_sal, G, US, CS%tides_CSp) + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + za(i,j) = za(i,j) - GV%g_Earth * (e_tide_eq(i,j) + e_tide_sal(i,j)) + enddo ; enddo + else ! This block recreates older answers with tides. + if (.not.CS%calculate_SAL) e_sal(:,:) = 0.0 + call calc_tidal_forcing_legacy(CS%Time, e_sal, e_sal_tide, e_tide_eq, e_tide_sal, & + G, US, CS%tides_CSp) + !$OMP parallel do default(shared) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + za(i,j) = za(i,j) - GV%g_Earth * e_sal_tide(i,j) + enddo ; enddo + endif endif if (CS%GFS_scale < 1.0) then @@ -942,7 +955,7 @@ subroutine PressureForce_FV_init(Time, G, GV, US, param_file, diag, CS, SAL_CSp, units="kg m-3", default=GV%Rho0*US%R_to_kg_m3, scale=US%kg_m3_to_R) call get_param(param_file, mdl, "TIDES", CS%tides, & "If true, apply tidal momentum forcing.", default=.false.) - if (CS%tides .and. GV%Boussinesq) then + if (CS%tides) then call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) From 24160d59976e87c2aeef68238a0d34b5d46b9e10 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Tue, 15 Aug 2023 09:26:42 -0400 Subject: [PATCH 10/17] *Non-Boussinesq revision of MOM.F90 for restarts This commit revises the internal code in step_MOM and extract_surface_state to work in fully non-Boussinesq mode without any dependencies on the Boussinesq reference density and provide updates needed to reproduce answers across restarts in non-Boussinesq mode. The subroutine calls from this module have already been revised, so only this file needs to be updated. The specific changes include: - Rescaled the nominal mixed layer depth variables HFREEZE, HMIX_SFC_PROP and HMIX_UV_SFC_PROP for determining surface properties to eliminate unit conversion factors during the run. This changes the units of 3 elements of the MOM_control_struct and one internal variable and adds 3 new internal variables. - Call calc_derived_thermo to update SpV_avg at the start of step_MOM if the internal ocean state depends on the surface pressure and that surface pressure has been passed in from the driver. There are 3 new internal variables and in some cases a new blocking halo update to enable these changes. All Boussinesq solutions are bitwise identical with this commit. It changes answers in some non-Boussinesq fully coupled or ice-ocean configurations. Other non-Boussinesq mode cases are mathematically equivalent in the case where RHO_0 = RHO_KV_CONVERT and bitwise identical if RHO_KV_CONVERT is an integer power of 2. This commit corrects issues with reproducability across restarts in the non-Boussinesq cases where the answer changes. --- src/core/MOM.F90 | 87 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 25 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index c9b8ea42c0..e59c9ac60a 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -337,14 +337,14 @@ module MOM ! These elements are used to control the calculation and error checking of the surface state real :: Hmix !< Diagnostic mixed layer thickness over which to !! average surface tracer properties when a bulk - !! mixed layer is not used [Z ~> m], or a negative value + !! mixed layer is not used [H ~> m or kg m-2], or a negative value !! if a bulk mixed layer is being used. - real :: HFrz !< If HFrz > 0, the nominal depth over which melt potential is - !! computed [Z ~> m]. The actual depth over which melt potential is + real :: HFrz !< If HFrz > 0, the nominal depth over which melt potential is computed + !! [H ~> m or kg m-2]. The actual depth over which melt potential is !! computed is min(HFrz, OBLD), where OBLD is the boundary layer depth. !! If HFrz <= 0 (default), melt potential will not be computed. real :: Hmix_UV !< Depth scale over which to average surface flow to - !! feedback to the coupler/driver [Z ~> m] when + !! feedback to the coupler/driver [H ~> m or kg m-2] when !! bulk mixed layer is not used, or a negative value !! if a bulk mixed layer is being used. logical :: check_bad_sfc_vals !< If true, scan surface state for ridiculous values. @@ -516,6 +516,7 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS ! various unit conversion factors integer :: ntstep ! time steps between tracer updates or diabatic forcing integer :: n_max ! number of steps to take in this call + integer :: halo_sz, dynamics_stencil integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, n integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB @@ -538,6 +539,8 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS ! multiple dynamic timesteps. logical :: do_dyn ! If true, dynamics are updated with this call. logical :: do_thermo ! If true, thermodynamics and remapping may be applied with this call. + logical :: nonblocking_p_surf_update ! A flag to indicate whether surface properties + ! can use nonblocking halo updates logical :: cycle_start ! If true, do calculations that are only done at the start of ! a stepping cycle (whatever that may mean). logical :: cycle_end ! If true, do calculations and diagnostics that are only done at @@ -647,13 +650,11 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS dt_therm = dt*ntstep endif - if (associated(forces%p_surf)) p_surf => forces%p_surf - if (.not.associated(forces%p_surf)) CS%interp_p_surf = .false. - CS%tv%p_surf => NULL() - if (CS%use_p_surf_in_EOS .and. associated(forces%p_surf)) CS%tv%p_surf => forces%p_surf - !---------- Initiate group halo pass of the forcing fields call cpu_clock_begin(id_clock_pass) + nonblocking_p_surf_update = G%nonblocking_updates .and. & + .not.(CS%use_p_surf_in_EOS .and. associated(forces%p_surf) .and. & + allocated(CS%tv%SpV_avg) .and. associated(CS%tv%T)) if (.not.associated(forces%taux) .or. .not.associated(forces%tauy)) & call MOM_error(FATAL,'step_MOM:forces%taux,tauy not associated') call create_group_pass(pass_tau_ustar_psurf, forces%taux, forces%tauy, G%Domain) @@ -663,12 +664,26 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS call create_group_pass(pass_tau_ustar_psurf, forces%tau_mag, G%Domain) if (associated(forces%p_surf)) & call create_group_pass(pass_tau_ustar_psurf, forces%p_surf, G%Domain) - if (G%nonblocking_updates) then + if (nonblocking_p_surf_update) then call start_group_pass(pass_tau_ustar_psurf, G%Domain) else call do_group_pass(pass_tau_ustar_psurf, G%Domain) endif call cpu_clock_end(id_clock_pass) + + if (associated(forces%p_surf)) p_surf => forces%p_surf + if (.not.associated(forces%p_surf)) CS%interp_p_surf = .false. + CS%tv%p_surf => NULL() + if (CS%use_p_surf_in_EOS .and. associated(forces%p_surf)) then + CS%tv%p_surf => forces%p_surf + + if (allocated(CS%tv%SpV_avg) .and. associated(CS%tv%T)) then + ! The internal ocean state depends on the surface pressues, so update SpV_avg. + dynamics_stencil = min(3, G%Domain%nihalo, G%Domain%njhalo) + call calc_derived_thermo(CS%tv, h, G, GV, US, halo=dynamics_stencil, debug=CS%debug) + endif + endif + else ! This step only updates the thermodynamics so setting timesteps is simpler. n_max = 1 @@ -687,7 +702,13 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS CS%tv%p_surf => NULL() if (CS%use_p_surf_in_EOS .and. associated(fluxes%p_surf)) then CS%tv%p_surf => fluxes%p_surf - if (allocated(CS%tv%SpV_avg)) call pass_var(fluxes%p_surf, G%Domain, clock=id_clock_pass) + if (allocated(CS%tv%SpV_avg)) then + call pass_var(fluxes%p_surf, G%Domain, clock=id_clock_pass) + ! The internal ocean state depends on the surface pressues, so update SpV_avg. + call extract_diabatic_member(CS%diabatic_CSp, diabatic_halo=halo_sz) + halo_sz = max(halo_sz, 1) + call calc_derived_thermo(CS%tv, h, G, GV, US, halo=halo_sz, debug=CS%debug) + endif endif endif @@ -714,7 +735,7 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS if (CS%stoch_CS%do_sppt .OR. CS%stoch_CS%pert_epbl) call update_stochastics(CS%stoch_CS) if (do_dyn) then - if (G%nonblocking_updates) & + if (nonblocking_p_surf_update) & call complete_group_pass(pass_tau_ustar_psurf, G%Domain, clock=id_clock_pass) if (CS%interp_p_surf) then @@ -1987,6 +2008,8 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & logical, allocatable, dimension(:,:,:) :: PCM_cell ! If true, PCM remapping should be used in a cell. type(group_pass_type) :: tmp_pass_uv_T_S_h, pass_uv_T_S_h + real :: Hmix_z, Hmix_UV_z ! Temporary variables with averaging depths [Z ~> m] + real :: HFrz_z ! Temporary variable with the melt potential depth [Z ~> m] real :: default_val ! default value for a parameter logical :: write_geom_files ! If true, write out the grid geometry files. logical :: new_sim ! If true, this has been determined to be a new simulation @@ -2223,22 +2246,23 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & if (bulkmixedlayer) then CS%Hmix = -1.0 ; CS%Hmix_UV = -1.0 else - call get_param(param_file, "MOM", "HMIX_SFC_PROP", CS%Hmix, & + call get_param(param_file, "MOM", "HMIX_SFC_PROP", Hmix_z, & "If BULKMIXEDLAYER is false, HMIX_SFC_PROP is the depth "//& "over which to average to find surface properties like "//& "SST and SSS or density (but not surface velocities).", & units="m", default=1.0, scale=US%m_to_Z) - call get_param(param_file, "MOM", "HMIX_UV_SFC_PROP", CS%Hmix_UV, & + call get_param(param_file, "MOM", "HMIX_UV_SFC_PROP", Hmix_UV_z, & "If BULKMIXEDLAYER is false, HMIX_UV_SFC_PROP is the depth "//& "over which to average to find surface flow properties, "//& "SSU, SSV. A non-positive value indicates no averaging.", & units="m", default=0.0, scale=US%m_to_Z) endif - call get_param(param_file, "MOM", "HFREEZE", CS%HFrz, & + call get_param(param_file, "MOM", "HFREEZE", HFrz_z, & "If HFREEZE > 0, melt potential will be computed. The actual depth "//& "over which melt potential is computed will be min(HFREEZE, OBLD), "//& "where OBLD is the boundary layer depth. If HFREEZE <= 0 (default), "//& - "melt potential will not be computed.", units="m", default=-1.0, scale=US%m_to_Z) + "melt potential will not be computed.", & + units="m", default=-1.0, scale=US%m_to_Z) call get_param(param_file, "MOM", "INTERPOLATE_P_SURF", CS%interp_p_surf, & "If true, linearly interpolate the surface pressure "//& "over the coupling time step, using the specified value "//& @@ -2525,6 +2549,15 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & call verticalGridInit( param_file, CS%GV, US ) GV => CS%GV + ! Now that the vertical grid has been initialized, rescale parameters that depend on factors + ! that are set with the vertical grid to their desired units. This added rescaling step would + ! be unnecessary if the vertical grid were initialized earlier in this routine. + if (.not.bulkmixedlayer) then + CS%Hmix = (US%Z_to_m * GV%m_to_H) * Hmix_z + CS%Hmix_UV = (US%Z_to_m * GV%m_to_H) * Hmix_UV_z + endif + CS%HFrz = (US%Z_to_m * GV%m_to_H) * HFrz_z + ! Shift from using the temporary dynamic grid type to using the final (potentially static) ! and properly rotated ocean-specific grid type and horizontal index type. if (CS%rotate_index) then @@ -3168,6 +3201,7 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & ! Update derived thermodynamic quantities. if (allocated(CS%tv%SpV_avg)) then + !### There may be a restart issue here with the surface pressure not being updated? call calc_derived_thermo(CS%tv, CS%h, G, GV, US, halo=dynamics_stencil, debug=CS%debug) endif @@ -3415,7 +3449,7 @@ subroutine set_restart_fields(GV, US, param_file, CS, restart_CSp) do_not_log=.true.) if (use_ice_shelf .and. associated(CS%Hml)) then call register_restart_field(CS%Hml, "hML", .false., restart_CSp, & - "Mixed layer thickness", "meter", conversion=US%Z_to_m) + "Mixed layer thickness", "m", conversion=US%Z_to_m) endif ! Register scalar unit conversion factors. @@ -3497,7 +3531,7 @@ subroutine extract_surface_state(CS, sfc_state_in) ! After the ANSWERS_2018 flag has been obsoleted, H_rescale will be 1. real :: T_freeze(SZI_(CS%G)) !< freezing temperature [C ~> degC] real :: pres(SZI_(CS%G)) !< Pressure to use for the freezing temperature calculation [R L2 T-2 ~> Pa] - real :: delT(SZI_(CS%G)) !< Depth integral of T-T_freeze [Z C ~> m degC] + real :: delT(SZI_(CS%G)) !< Depth integral of T-T_freeze [H C ~> m degC or degC kg m-2] logical :: use_temperature !< If true, temperature and salinity are used as state variables. integer :: i, j, k, is, ie, js, je, nz, numberOfErrors, ig, jg integer :: isd, ied, jsd, jed @@ -3572,9 +3606,12 @@ subroutine extract_surface_state(CS, sfc_state_in) enddo ; enddo else ! (CS%Hmix >= 0.0) - H_rescale = 1.0 ; if (CS%answer_date < 20190101) H_rescale = GV%H_to_Z + H_rescale = 1.0 depth_ml = CS%Hmix - if (CS%answer_date >= 20190101) depth_ml = CS%Hmix*GV%Z_to_H + if (CS%answer_date < 20190101) then + H_rescale = GV%H_to_Z + depth_ml = GV%H_to_Z*CS%Hmix + endif ! Determine the mean tracer properties of the uppermost depth_ml fluid. !$OMP parallel do default(shared) private(depth,dh) @@ -3645,7 +3682,7 @@ subroutine extract_surface_state(CS, sfc_state_in) ! This assumes that u and v halos have already been updated. if (CS%Hmix_UV>0.) then depth_ml = CS%Hmix_UV - if (CS%answer_date >= 20190101) depth_ml = CS%Hmix_UV*GV%Z_to_H + if (CS%answer_date < 20190101) depth_ml = GV%H_to_Z*CS%Hmix_UV !$OMP parallel do default(shared) private(depth,dh,hv) do J=js-1,ie do i=is,ie @@ -3719,9 +3756,9 @@ subroutine extract_surface_state(CS, sfc_state_in) do k=1,nz call calculate_TFreeze(CS%tv%S(is:ie,j,k), pres(is:ie), T_freeze(is:ie), CS%tv%eqn_of_state) do i=is,ie - depth_ml = min(CS%HFrz, CS%visc%MLD(i,j)) - if (depth(i) + h(i,j,k)*GV%H_to_Z < depth_ml) then - dh = h(i,j,k)*GV%H_to_Z + depth_ml = min(CS%HFrz, (US%Z_to_m*GV%m_to_H)*CS%visc%MLD(i,j)) + if (depth(i) + h(i,j,k) < depth_ml) then + dh = h(i,j,k) elseif (depth(i) < depth_ml) then dh = depth_ml - depth(i) else @@ -3743,7 +3780,7 @@ subroutine extract_surface_state(CS, sfc_state_in) if (G%mask2dT(i,j)>0.) then ! instantaneous melt_potential [Q R Z ~> J m-2] - sfc_state%melt_potential(i,j) = CS%tv%C_p * GV%Rho0 * delT(i) + sfc_state%melt_potential(i,j) = CS%tv%C_p * GV%H_to_RZ * delT(i) endif enddo enddo ! end of j loop From 1872d3b03d0c47b5b118f0cd50d3407efc40188b Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Fri, 7 Jul 2023 10:32:16 -0400 Subject: [PATCH 11/17] *Revise BFB_set_coord and BFB_buoyancy_forcing Revised BFB_set_coord and BFB_buoyancy_forcing to be consistent with other instances of a linear equation of state, and to set g_prime in non-Boussinesq mode similarly to how it is set in other places. Also use RESTORE_FLUX_RHO in place of RHO_0 when to specify the densities that are used to convert the piston velocities into restoring heat or salt fluxes with BFB_buoyancy_forcing, like in other places in the MOM6 code. This change will change answers by default when BFB_set_coord is used, but the old Boussinesq answers can be recovered by setting the reference salinity S_REF to 38.75 ppt or by setting RHO_T0_S0 to 1003.0 kg m-3. --- src/user/BFB_initialization.F90 | 31 ++++++++++++++++++++-------- src/user/BFB_surface_forcing.F90 | 35 ++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/src/user/BFB_initialization.F90 b/src/user/BFB_initialization.F90 index 3efc908ffb..67381bfdc5 100644 --- a/src/user/BFB_initialization.F90 +++ b/src/user/BFB_initialization.F90 @@ -38,8 +38,11 @@ subroutine BFB_set_coord(Rlay, g_prime, GV, US, param_file) 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 + real :: Rho_T0_S0 ! The density at T=0, S=0 [R ~> kg m-3] real :: dRho_dT ! The partial derivative of density with temperature [R C-1 ~> kg m-3 degC-1] + real :: dRho_dS ! The partial derivative of density with salinity [R S-1 ~> kg m-3 ppt-1] real :: SST_s, T_bot ! Temperatures at the surface and seafloor [C ~> degC] + real :: S_ref ! Reference salinity [S ~> ppt] real :: rho_top, rho_bot ! Densities at the surface and seafloor [R ~> kg m-3] integer :: k, nz ! This include declares and sets the variable "version". @@ -47,23 +50,33 @@ subroutine BFB_set_coord(Rlay, g_prime, GV, US, param_file) character(len=40) :: mdl = "BFB_initialization" ! This module's name. call log_version(param_file, mdl, version, "") - call get_param(param_file, mdl, "DRHO_DT", drho_dt, & - "Rate of change of density with temperature.", & - units="kg m-3 K-1", default=-0.2, scale=US%kg_m3_to_R*US%C_to_degC) + call get_param(param_file, mdl, "DRHO_DT", dRho_dT, & + "The partial derivative of density with temperature.", & + units="kg m-3 K-1", default=-0.2, scale=US%kg_m3_to_R*US%C_to_degC) + call get_param(param_file, mdl, "DRHO_DS", dRho_dS, & + "The partial derivative of density with salinity.", & + units="kg m-3 PSU-1", default=0.8, scale=US%kg_m3_to_R*US%S_to_ppt) + call get_param(param_file, mdl, "RHO_T0_S0", Rho_T0_S0, & + "The density at T=0, S=0.", units="kg m-3", default=1000.0, scale=US%kg_m3_to_R) call get_param(param_file, mdl, "SST_S", SST_s, & - "SST at the southern edge of the domain.", units="degC", default=20.0, scale=US%degC_to_C) + "SST at the southern edge of the domain.", & + units="degC", default=20.0, scale=US%degC_to_C) call get_param(param_file, mdl, "T_BOT", T_bot, & "Bottom temperature", units="degC", default=5.0, scale=US%degC_to_C) - rho_top = GV%Rho0 + drho_dt*SST_s - rho_bot = GV%Rho0 + drho_dt*T_bot + call get_param(param_file, mdl, "S_REF", S_ref, & + "The initial salinities.", units="PSU", default=35.0, scale=US%ppt_to_S) + rho_top = (Rho_T0_S0 + dRho_dS*S_ref) + dRho_dT*SST_s + rho_bot = (Rho_T0_S0 + dRho_dS*S_ref) + dRho_dT*T_bot nz = GV%ke do k = 1,nz Rlay(k) = (rho_bot - rho_top)/(nz-1)*real(k-1) + rho_top - if (k >1) then - g_prime(k) = (Rlay(k) - Rlay(k-1)) * GV%g_Earth / (GV%Rho0) - else + if (k==1) then g_prime(k) = GV%g_Earth + elseif (GV%Boussinesq) then + g_prime(k) = (Rlay(k) - Rlay(k-1)) * GV%g_Earth / GV%Rho0 + else + g_prime(k) = (Rlay(k) - Rlay(k-1)) * GV%g_Earth / (0.5*(Rlay(k) + Rlay(k-1))) endif enddo diff --git a/src/user/BFB_surface_forcing.F90 b/src/user/BFB_surface_forcing.F90 index f3d04980f6..fcbd66e1d8 100644 --- a/src/user/BFB_surface_forcing.F90 +++ b/src/user/BFB_surface_forcing.F90 @@ -29,12 +29,17 @@ module BFB_surface_forcing real :: Rho0 !< The density used in the Boussinesq approximation [R ~> kg m-3]. real :: G_Earth !< The gravitational acceleration [L2 Z-1 T-2 ~> m s-2] real :: Flux_const !< The restoring rate at the surface [Z T-1 ~> m s-1]. + real :: rho_restore !< The density that is used to convert piston velocities into salt + !! or heat fluxes with salinity or temperature restoring [R ~> kg m-3] real :: SST_s !< SST at the southern edge of the linear forcing ramp [C ~> degC] real :: SST_n !< SST at the northern edge of the linear forcing ramp [C ~> degC] + real :: S_ref !< Reference salinity used throughout the domain [S ~> ppt] real :: lfrslat !< Southern latitude where the linear forcing ramp begins [degrees_N] or [km] real :: lfrnlat !< Northern latitude where the linear forcing ramp ends [degrees_N] or [km] - real :: drho_dt !< Rate of change of density with temperature [R C-1 ~> kg m-3 degC-1]. - !! Note that temperature is being used as a dummy variable here. + real :: Rho_T0_S0 !< The density at T=0, S=0 [R ~> kg m-3] + real :: dRho_dT !< The partial derivative of density with temperature [R C-1 ~> kg m-3 degC-1] + real :: dRho_dS !< The partial derivative of density with salinity [R S-1 ~> kg m-3 ppt-1] + !! Note that temperature and salinity are being used as dummy variables here. !! All temperatures are converted into density. type(diag_ctrl), pointer :: diag => NULL() !< A structure that is used to @@ -125,7 +130,7 @@ subroutine BFB_buoyancy_forcing(sfc_state, fluxes, day, dt, G, US, CS) call MOM_error(FATAL, "User_buoyancy_surface_forcing: " // & "Temperature and salinity restoring used without modification." ) - rhoXcp = CS%Rho0 * fluxes%C_p + rhoXcp = CS%rho_restore * fluxes%C_p do j=js,je ; do i=is,ie ! Set Temp_restore and Salin_restore to the temperature (in [C ~> degC]) and ! salinity (in [S ~> ppt]) that are being restored toward. @@ -134,7 +139,7 @@ subroutine BFB_buoyancy_forcing(sfc_state, fluxes, day, dt, G, US, CS) fluxes%heat_added(i,j) = (G%mask2dT(i,j) * (rhoXcp * CS%Flux_const)) * & (Temp_restore - sfc_state%SST(i,j)) - fluxes%vprec(i,j) = - (G%mask2dT(i,j) * (CS%Rho0*CS%Flux_const)) * & + fluxes%vprec(i,j) = - (G%mask2dT(i,j) * (CS%rho_restore*CS%Flux_const)) * & ((Salin_restore - sfc_state%SSS(i,j)) / (0.5 * (Salin_restore + sfc_state%SSS(i,j)))) enddo ; enddo else @@ -144,7 +149,7 @@ subroutine BFB_buoyancy_forcing(sfc_state, fluxes, day, dt, G, US, CS) ! "Buoyancy restoring used without modification." ) ! The -1 is because density has the opposite sign to buoyancy. - buoy_rest_const = -1.0 * (CS%G_Earth * CS%Flux_const) / CS%Rho0 + buoy_rest_const = -1.0 * (CS%G_Earth * CS%Flux_const) / CS%rho_restore Temp_restore = 0.0 do j=js,je ; do i=is,ie ! Set density_restore to an expression for the surface potential @@ -158,8 +163,7 @@ subroutine BFB_buoyancy_forcing(sfc_state, fluxes, day, dt, G, US, CS) (G%geoLatT(i,j) - CS%lfrslat) + CS%SST_s endif - density_restore = Temp_restore*CS%drho_dt + CS%Rho0 - + density_restore = (CS%Rho_T0_S0 + CS%dRho_dS*CS%S_ref) + CS%dRho_dT*Temp_restore fluxes%buoy(i,j) = G%mask2dT(i,j) * buoy_rest_const * & (density_restore - sfc_state%sfc_density(i,j)) enddo ; enddo @@ -216,9 +220,17 @@ subroutine BFB_surface_forcing_init(Time, G, US, param_file, diag, CS) call get_param(param_file, mdl, "SST_N", CS%SST_n, & "SST at the northern edge of the linear forcing ramp.", & units="degC", default=10.0, scale=US%degC_to_C) - call get_param(param_file, mdl, "DRHO_DT", CS%drho_dt, & - "The rate of change of density with temperature.", & + call get_param(param_file, mdl, "DRHO_DT", CS%dRho_dT, & + "The partial derivative of density with temperature.", & units="kg m-3 K-1", default=-0.2, scale=US%kg_m3_to_R*US%C_to_degC) + call get_param(param_file, mdl, "DRHO_DS", CS%dRho_dS, & + "The partial derivative of density with salinity.", & + units="kg m-3 PSU-1", default=0.8, scale=US%kg_m3_to_R*US%S_to_ppt) + call get_param(param_file, mdl, "RHO_T0_S0", CS%Rho_T0_S0, & + "The density at T=0, S=0.", units="kg m-3", default=1000.0, scale=US%kg_m3_to_R) + call get_param(param_file, mdl, "S_REF", CS%S_ref, & + "The reference salinity used here throughout the domain.", & + units="PSU", default=35.0, scale=US%ppt_to_S) call get_param(param_file, mdl, "RESTOREBUOY", CS%restorebuoy, & "If true, the buoyancy fluxes drive the model back "//& @@ -231,6 +243,11 @@ subroutine BFB_surface_forcing_init(Time, G, US, param_file, diag, CS) default=0.0, units="m day-1", scale=US%m_to_Z*US%T_to_s) ! Convert CS%Flux_const from m day-1 to m s-1. CS%Flux_const = CS%Flux_const / 86400.0 + call get_param(param_file, mdl, "RESTORE_FLUX_RHO", CS%rho_restore, & + "The density that is used to convert piston velocities into salt or heat "//& + "fluxes with RESTORE_SALINITY or RESTORE_TEMPERATURE.", & + units="kg m-3", default=CS%Rho0*US%R_to_kg_m3, scale=US%kg_m3_to_R, & + do_not_log=(CS%Flux_const==0.0)) endif end subroutine BFB_surface_forcing_init From 279ee1cb14b31dae69dc0d25e094987e6ee43aab Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Mon, 14 Aug 2023 18:38:11 -0400 Subject: [PATCH 12/17] Use GV%dZ_subroundoff Use the minimal vertical distance GV%dZ_subroundoff in 8 spots in 2 files in place of GV%H_to_Z*GV%H_subroundoff. Four internal variables were renamed for clarity of purpose in porous_widths_layer and PressureForce_Mont_Bouss. Also modified the non-Boussinesq units in a description of two integrated energy diagnostics from [H L4 T-3 ~> kg m2 s-3] to [H L4 T-3 ~> W]; these units are equivalent but the latter is more informative. Answers in all Boussinesq test cases and the existing non-Boussinesq test case in the MOM6-examples test suite are bitwise identical. --- src/core/MOM_PressureForce_Montgomery.F90 | 16 ++++++++-------- src/core/MOM_porous_barriers.F90 | 16 ++++++++-------- src/diagnostics/MOM_diagnostics.F90 | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/core/MOM_PressureForce_Montgomery.F90 b/src/core/MOM_PressureForce_Montgomery.F90 index a22c73fa4d..3de713c801 100644 --- a/src/core/MOM_PressureForce_Montgomery.F90 +++ b/src/core/MOM_PressureForce_Montgomery.F90 @@ -428,7 +428,7 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, real :: G_Rho0 ! G_Earth / Rho0 [L2 Z-1 T-2 R-1 ~> m4 s-2 kg-1]. real :: PFu_bc, PFv_bc ! The pressure gradient force due to along-layer ! compensated density gradients [L T-2 ~> m s-2] - real :: h_neglect ! A thickness that is so small it is usually lost + real :: dz_neglect ! A vertical distance that is so small it is usually lost ! in roundoff and can be neglected [Z ~> m]. logical :: use_p_atm ! If true, use the atmospheric pressure. logical :: use_EOS ! If true, density is calculated from T & S using @@ -459,7 +459,7 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, "can no longer be used with a compressible EOS. Use #define ANALYTIC_FV_PGF.") endif - h_neglect = GV%H_subroundoff * GV%H_to_Z + dz_neglect = GV%dZ_subroundoff I_Rho0 = 1.0/CS%Rho0 G_Rho0 = GV%g_Earth / GV%Rho0 @@ -582,7 +582,7 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, !$OMP parallel do default(shared) private(h_star,PFu_bc,PFv_bc) do k=1,nz do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 - h_star(i,j) = (e(i,j,K) - e(i,j,K+1)) + h_neglect + h_star(i,j) = (e(i,j,K) - e(i,j,K+1)) + dz_neglect enddo ; enddo do j=js,je ; do I=Isq,Ieq PFu_bc = -1.0*(rho_star(i+1,j,k) - rho_star(i,j,k)) * (G%IdxCu(I,j) * & @@ -676,7 +676,7 @@ subroutine Set_pbce_Bouss(e, tv, G, GV, US, Rho0, GFS_scale, pbce, rho_star) real :: Rho0xG ! g_Earth * Rho0 [R L2 Z-1 T-2 ~> kg s-2 m-2] logical :: use_EOS ! If true, density is calculated from T & S using ! an equation of state. - real :: z_neglect ! A thickness that is so small it is usually lost + real :: dz_neglect ! A vertical distance that is so small it is usually lost ! in roundoff and can be neglected [Z ~> m]. integer, dimension(2) :: EOSdom ! The computational domain for the equation of state integer :: Isq, Ieq, Jsq, Jeq, nz, i, j, k @@ -687,14 +687,14 @@ subroutine Set_pbce_Bouss(e, tv, G, GV, US, Rho0, GFS_scale, pbce, rho_star) Rho0xG = Rho0 * GV%g_Earth G_Rho0 = GV%g_Earth / GV%Rho0 use_EOS = associated(tv%eqn_of_state) - z_neglect = GV%H_subroundoff*GV%H_to_Z + dz_neglect = GV%dZ_subroundoff if (use_EOS) then if (present(rho_star)) then !$OMP parallel do default(shared) private(Ihtot) do j=Jsq,Jeq+1 do i=Isq,Ieq+1 - Ihtot(i) = GV%H_to_Z / ((e(i,j,1)-e(i,j,nz+1)) + z_neglect) + Ihtot(i) = GV%H_to_Z / ((e(i,j,1)-e(i,j,nz+1)) + dz_neglect) pbce(i,j,1) = GFS_scale * rho_star(i,j,1) * GV%H_to_Z enddo do k=2,nz ; do i=Isq,Ieq+1 @@ -706,7 +706,7 @@ subroutine Set_pbce_Bouss(e, tv, G, GV, US, Rho0, GFS_scale, pbce, rho_star) !$OMP parallel do default(shared) private(Ihtot,press,rho_in_situ,T_int,S_int,dR_dT,dR_dS) do j=Jsq,Jeq+1 do i=Isq,Ieq+1 - Ihtot(i) = GV%H_to_Z / ((e(i,j,1)-e(i,j,nz+1)) + z_neglect) + Ihtot(i) = GV%H_to_Z / ((e(i,j,1)-e(i,j,nz+1)) + dz_neglect) press(i) = -Rho0xG*(e(i,j,1) - G%Z_ref) enddo call calculate_density(tv%T(:,j,1), tv%S(:,j,1), press, rho_in_situ, & @@ -735,7 +735,7 @@ subroutine Set_pbce_Bouss(e, tv, G, GV, US, Rho0, GFS_scale, pbce, rho_star) !$OMP parallel do default(shared) private(Ihtot) do j=Jsq,Jeq+1 do i=Isq,Ieq+1 - Ihtot(i) = 1.0 / ((e(i,j,1)-e(i,j,nz+1)) + z_neglect) + Ihtot(i) = 1.0 / ((e(i,j,1)-e(i,j,nz+1)) + dz_neglect) pbce(i,j,1) = GV%g_prime(1) * GV%H_to_Z enddo do k=2,nz ; do i=Isq,Ieq+1 diff --git a/src/core/MOM_porous_barriers.F90 b/src/core/MOM_porous_barriers.F90 index d73d96b242..e212581993 100644 --- a/src/core/MOM_porous_barriers.F90 +++ b/src/core/MOM_porous_barriers.F90 @@ -80,7 +80,7 @@ subroutine porous_widths_layer(h, tv, G, GV, US, pbv, CS, eta_bt) logical, dimension(SZIB_(G),SZJB_(G)) :: do_I ! Booleans for calculation at u or v points ! updated while moving up layers real :: A_layer ! Integral of fractional open width from bottom to current layer [Z ~> m] - real :: h_min ! ! The minimum layer thickness [Z ~> m] + real :: dz_min ! The minimum layer thickness [Z ~> m] real :: dmask ! The depth below which porous barrier is not applied [Z ~> m] integer :: i, j, k, nk, is, ie, js, je, Isq, Ieq, Jsq, Jeq @@ -100,7 +100,7 @@ subroutine porous_widths_layer(h, tv, G, GV, US, pbv, CS, eta_bt) call calc_eta_at_uv(eta_u, eta_v, CS%eta_interp, dmask, h, tv, G, GV, US) - h_min = GV%Angstrom_H * GV%H_to_Z + dz_min = GV%Angstrom_Z ! u-points do j=js,je ; do I=Isq,Ieq ; do_I(I,j) = .False. ; enddo ; enddo @@ -125,7 +125,7 @@ subroutine porous_widths_layer(h, tv, G, GV, US, pbv, CS, eta_bt) do k=nk,1,-1 ; do j=js,je ; do I=Isq,Ieq ; if (do_I(I,j)) then call calc_por_layer(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & eta_u(I,j,K), A_layer, do_I(I,j)) - if (eta_u(I,j,K) - (eta_u(I,j,K+1)+h_min) > 0.0) then + if (eta_u(I,j,K) - (eta_u(I,j,K+1)+dz_min) > 0.0) then pbv%por_face_areaU(I,j,k) = min(1.0, (A_layer - A_layer_prev(I,j)) / (eta_u(I,j,K) - eta_u(I,j,K+1))) else pbv%por_face_areaU(I,j,k) = 0.0 ! use calc_por_interface() might be a better choice @@ -157,7 +157,7 @@ subroutine porous_widths_layer(h, tv, G, GV, US, pbv, CS, eta_bt) do k=nk,1,-1 ; do J=Jsq,Jeq ; do i=is,ie ; if (do_I(i,J)) then call calc_por_layer(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & eta_v(i,J,K), A_layer, do_I(i,J)) - if (eta_v(i,J,K) - (eta_v(i,J,K+1)+h_min) > 0.0) then + if (eta_v(i,J,K) - (eta_v(i,J,K+1)+dz_min) > 0.0) then pbv%por_face_areaV(i,J,k) = min(1.0, (A_layer - A_layer_prev(i,J)) / (eta_v(i,J,K) - eta_v(i,J,K+1))) else pbv%por_face_areaV(i,J,k) = 0.0 ! use calc_por_interface() might be a better choice @@ -286,7 +286,7 @@ subroutine calc_eta_at_uv(eta_u, eta_v, interp, dmask, h, tv, G, GV, US, eta_bt) ! local variables real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: eta ! Layer interface heights [Z ~> m]. - real :: h_neglect ! Negligible thicknesses [Z ~> m] + real :: dz_neglect ! A negligible height difference [Z ~> m] integer :: i, j, k, nk, is, ie, js, je, Isq, Ieq, Jsq, Jeq is = G%isc; ie = G%iec; js = G%jsc; je = G%jec; nk = GV%ke @@ -295,7 +295,7 @@ subroutine calc_eta_at_uv(eta_u, eta_v, interp, dmask, h, tv, G, GV, US, eta_bt) ! currently no treatment for using optional find_eta arguments if present call find_eta(h, tv, G, GV, US, eta, halo_size=1) - h_neglect = GV%H_subroundoff * GV%H_to_Z + dz_neglect = GV%dZ_subroundoff do K=1,nk+1 do j=js,je ; do I=Isq,Ieq ; eta_u(I,j,K) = dmask ; enddo ; enddo @@ -333,10 +333,10 @@ subroutine calc_eta_at_uv(eta_u, eta_v, interp, dmask, h, tv, G, GV, US, eta_bt) case (ETA_INTERP_HARM) ! Harmonic mean do K=1,nk+1 do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then - eta_u(I,j,K) = 2.0 * (eta(i,j,K) * eta(i+1,j,K)) / (eta(i,j,K) + eta(i+1,j,K) + h_neglect) + eta_u(I,j,K) = 2.0 * (eta(i,j,K) * eta(i+1,j,K)) / (eta(i,j,K) + eta(i+1,j,K) + dz_neglect) endif ; enddo ; enddo do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then - eta_v(i,J,K) = 2.0 * (eta(i,j,K) * eta(i,j+1,K)) / (eta(i,j,K) + eta(i,j+1,K) + h_neglect) + eta_v(i,J,K) = 2.0 * (eta(i,j,K) * eta(i,j+1,K)) / (eta(i,j,K) + eta(i,j+1,K) + dz_neglect) endif ; enddo ; enddo enddo case default diff --git a/src/diagnostics/MOM_diagnostics.F90 b/src/diagnostics/MOM_diagnostics.F90 index c23712d8e7..253b7189e3 100644 --- a/src/diagnostics/MOM_diagnostics.F90 +++ b/src/diagnostics/MOM_diagnostics.F90 @@ -957,9 +957,9 @@ subroutine calculate_energy_diagnostics(u, v, h, uh, vh, ADp, CDp, G, GV, US, CS real :: KE_term(SZI_(G),SZJ_(G),SZK_(GV)) ! A term in the kinetic energy budget ! [H L2 T-3 ~> m3 s-3 or W m-2] real :: KE_u(SZIB_(G),SZJ_(G)) ! The area integral of a KE term in a layer at u-points - ! [H L4 T-3 ~> m5 s-3 or kg m2 s-3] + ! [H L4 T-3 ~> m5 s-3 or W] real :: KE_v(SZI_(G),SZJB_(G)) ! The area integral of a KE term in a layer at v-points - ! [H L4 T-3 ~> m5 s-3 or kg m2 s-3] + ! [H L4 T-3 ~> m5 s-3 or W] real :: KE_h(SZI_(G),SZJ_(G)) ! A KE term contribution at tracer points ! [H L2 T-3 ~> m3 s-3 or W m-2] From 7d199ca052f5934b9717ca1247b40971468d61b8 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Mon, 31 Jul 2023 03:20:04 -0400 Subject: [PATCH 13/17] +*Revise non-Boussinesq offline tracer diffusivity Rescale the units diapycnal diffusivity used in the offline tracer mode to work in [H Z T-1 ~> m2 s-1 or kg m-1 s-1], making it consistent with what has already been done for other diapycnal diffusivities. This change involves changes to the units of arguments to update_offline_from_files and ALE_offline_inputs and to two elements of offline_transport_CS. It also includes a new thermo_vary_type argument to offline_diabatic_ale, as well as a call to thickness_to_dz in that same routine to set the values of the newly added 3-d array of the vertical distances across layers and a change in name and units of the inverse thickness variable used to find the fluxes. All answers are bitwise identical in Boussinesq mode, but in offline tracer calculations in non-Boussinesq mode they will change to become independent of the Boussinesq reference density. --- src/ALE/MOM_ALE.F90 | 5 +++-- src/core/MOM.F90 | 2 +- src/tracer/MOM_offline_aux.F90 | 5 +++-- src/tracer/MOM_offline_main.F90 | 24 +++++++++++++++--------- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/ALE/MOM_ALE.F90 b/src/ALE/MOM_ALE.F90 index b8f60b2830..4d7445093a 100644 --- a/src/ALE/MOM_ALE.F90 +++ b/src/ALE/MOM_ALE.F90 @@ -504,13 +504,14 @@ subroutine ALE_offline_inputs(CS, G, GV, US, h, tv, Reg, uhtr, vhtr, Kd, debug, type(ALE_CS), pointer :: CS !< Regridding parameters and options type(ocean_grid_type), intent(in ) :: G !< Ocean grid informations type(verticalGrid_type), intent(in ) :: GV !< Ocean vertical grid structure - type(unit_scale_type), intent(in ) :: US !< A dimensional unit scaling type + type(unit_scale_type), intent(in ) :: US !< A dimensional unit scaling type real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: h !< Layer thicknesses [H ~> m or kg m-2] type(thermo_var_ptrs), intent(inout) :: tv !< Thermodynamic variable structure type(tracer_registry_type), pointer :: Reg !< Tracer registry structure real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(inout) :: uhtr !< Zonal mass fluxes [H L2 ~> m3 or kg] real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(inout) :: vhtr !< Meridional mass fluxes [H L2 ~> m3 or kg] - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: Kd !< Input diffusivities [Z2 T-1 ~> m2 s-1] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: Kd !< Input diffusivities + !! [H Z T-1 ~> m2 s-1 or kg m-1 s-1] logical, intent(in ) :: debug !< If true, then turn checksums type(ocean_OBC_type), pointer :: OBC !< Open boundary structure ! Local variables diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index e59c9ac60a..86040e8969 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -1826,7 +1826,7 @@ subroutine step_offline(forces, fluxes, sfc_state, Time_start, time_interval, CS ! The functions related to column physics of tracers is performed separately in ALE mode if (do_vertical) then call offline_diabatic_ale(fluxes, Time_start, Time_end, G, GV, US, CS%offline_CSp, & - CS%h, eatr, ebtr) + CS%h, CS%tv, eatr, ebtr) endif ! Last thing that needs to be done is the final ALE remapping diff --git a/src/tracer/MOM_offline_aux.F90 b/src/tracer/MOM_offline_aux.F90 index 7619cac2bd..bd105439c7 100644 --- a/src/tracer/MOM_offline_aux.F90 +++ b/src/tracer/MOM_offline_aux.F90 @@ -642,7 +642,8 @@ subroutine update_offline_from_files(G, GV, US, nk_input, mean_file, sum_file, s real, dimension(SZI_(G),SZJ_(G)), & intent(inout) :: mld !< Averaged mixed layer depth [Z ~> m] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), & - intent(inout) :: Kd !< Diapycnal diffusivities at interfaces [Z2 T-1 ~> m2 s-1] + intent(inout) :: Kd !< Diapycnal diffusivities at interfaces + !! [H Z T-1 ~> m2 s-1 or kg m-1 s-1] type(forcing), intent(inout) :: fluxes !< Fields with surface fluxes integer, intent(in ) :: ridx_sum !< Read index for sum, mean, and surf files integer, intent(in ) :: ridx_snap !< Read index for snapshot file @@ -696,7 +697,7 @@ subroutine update_offline_from_files(G, GV, US, nk_input, mean_file, sum_file, s ! Check if reading vertical diffusivities or entrainment fluxes call MOM_read_data( mean_file, 'Kd_interface', Kd(:,:,1:nk_input+1), G%Domain, & - timelevel=ridx_sum, position=CENTER, scale=US%m2_s_to_Z2_T) + timelevel=ridx_sum, position=CENTER, scale=GV%m2_s_to_HZ_T) ! This block makes sure that the fluxes control structure, which may not be used in the solo_driver, ! contains netMassIn and netMassOut which is necessary for the applyTracerBoundaryFluxesInOut routine diff --git a/src/tracer/MOM_offline_main.F90 b/src/tracer/MOM_offline_main.F90 index dcce81ef73..06af35cefd 100644 --- a/src/tracer/MOM_offline_main.F90 +++ b/src/tracer/MOM_offline_main.F90 @@ -22,7 +22,7 @@ module MOM_offline_main use MOM_file_parser, only : read_param, get_param, log_version, param_file_type use MOM_forcing_type, only : forcing use MOM_grid, only : ocean_grid_type -use MOM_interface_heights, only : calc_derived_thermo +use MOM_interface_heights, only : calc_derived_thermo, thickness_to_dz use MOM_io, only : MOM_read_data, MOM_read_vector, CENTER use MOM_offline_aux, only : update_offline_from_arrays, update_offline_from_files use MOM_offline_aux, only : next_modulo_time, offline_add_diurnal_sw @@ -121,7 +121,8 @@ module MOM_offline_main real :: minimum_forcing_depth !< The smallest depth over which fluxes can be applied [H ~> m or kg m-2]. !! This is copied from diabatic_CS controlling how tracers follow freshwater fluxes - real :: Kd_max !< Runtime parameter specifying the maximum value of vertical diffusivity [Z2 T-1 ~> m2 s-1] + real :: Kd_max !< Runtime parameter specifying the maximum value of vertical diffusivity + !! [H Z T-1 ~> m2 s-1 or kg m-1 s-1] real :: min_residual !< The minimum amount of total mass flux before exiting the main advection !! routine [H L2 ~> m3 or kg] !>@{ Diagnostic manager IDs for some fields that may be of interest when doing offline transport @@ -169,7 +170,7 @@ module MOM_offline_main !< Amount of fluid entrained from the layer below within !! one time step [H ~> m or kg m-2] ! Fields at T-points on interfaces - real, allocatable, dimension(:,:,:) :: Kd !< Vertical diffusivity [Z2 T-1 ~> m2 s-1] + real, allocatable, dimension(:,:,:) :: Kd !< Vertical diffusivity [H Z T-1 ~> m2 s-1 or kg m-1 s-1] real, allocatable, dimension(:,:,:) :: h_end !< Thicknesses at the end of offline timestep [H ~> m or kg m-2] real, allocatable, dimension(:,:) :: mld !< Mixed layer depths at thickness points [Z ~> m] @@ -651,7 +652,7 @@ end function remaining_transport_sum !> The vertical/diabatic driver for offline tracers. First the eatr/ebtr associated with the interpolated !! vertical diffusivities are calculated and then any tracer column functions are done which can include !! vertical diffuvities and source/sink terms. -subroutine offline_diabatic_ale(fluxes, Time_start, Time_end, G, GV, US, CS, h_pre, eatr, ebtr) +subroutine offline_diabatic_ale(fluxes, Time_start, Time_end, G, GV, US, CS, h_pre, tv, eatr, ebtr) type(forcing), intent(inout) :: fluxes !< pointers to forcing fields type(time_type), intent(in) :: Time_start !< starting time of a segment, as a time type @@ -662,17 +663,20 @@ subroutine offline_diabatic_ale(fluxes, Time_start, Time_end, G, GV, US, CS, h_p type(offline_transport_CS), pointer :: CS !< control structure from initialize_MOM real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & intent(inout) :: h_pre !< layer thicknesses before advection [H ~> m or kg m-2] + type(thermo_var_ptrs), intent(in ) :: tv !< A structure pointing to various thermodynamic variables real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & intent(inout) :: eatr !< Entrainment from layer above [H ~> m or kg m-2] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & intent(inout) :: ebtr !< Entrainment from layer below [H ~> m or kg m-2] + ! Local variables real, dimension(SZI_(G),SZJ_(G)) :: & sw, sw_vis, sw_nir !< Save old values of shortwave radiation [Q R Z T-1 ~> W m-2] - real :: I_hval ! An inverse thickness [H-1 ~> m2 kg-1] + real :: dz(SZI_(G),SZJ_(G),SZK_(GV)) ! Vertical distance across layers [Z ~> m] + real :: I_dZval ! An inverse distance between layer centers [Z-1 ~> m] integer :: i, j, k, is, ie, js, je, nz integer :: k_nonzero - real :: Kd_bot ! Near-bottom diffusivity [Z2 T-1 ~> m2 s-1] + real :: Kd_bot ! Near-bottom diffusivity [H Z T-1 ~> m2 s-1 or kg m-1 s-1] nz = GV%ke is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec @@ -687,6 +691,8 @@ subroutine offline_diabatic_ale(fluxes, Time_start, Time_end, G, GV, US, CS, h_p call MOM_tracer_chkinv("Before offline_diabatic_ale", G, GV, h_pre, CS%tracer_reg) endif + call thickness_to_dz(h_pre, tv, dz, G, GV, US) + eatr(:,:,:) = 0. ebtr(:,:,:) = 0. ! Calculate eatr and ebtr if vertical diffusivity is read @@ -713,8 +719,8 @@ subroutine offline_diabatic_ale(fluxes, Time_start, Time_end, G, GV, US, CS, h_p eatr(i,j,1) = 0. enddo ; enddo do k=2,nz ; do j=js,je ; do i=is,ie - I_hval = 1.0 / (GV%H_subroundoff + 0.5*(h_pre(i,j,k-1) + h_pre(i,j,k))) - eatr(i,j,k) = GV%Z_to_H**2 * CS%dt_offline_vertical * I_hval * CS%Kd(i,j,k) + I_dZval = 1.0 / (GV%dZ_subroundoff + 0.5*(dz(i,j,k-1) + dz(i,j,k))) + eatr(i,j,k) = CS%dt_offline_vertical * I_dZval * CS%Kd(i,j,k) ebtr(i,j,k-1) = eatr(i,j,k) enddo ; enddo ; enddo do j=js,je ; do i=is,ie @@ -1418,7 +1424,7 @@ subroutine offline_transport_init(param_file, CS, diabatic_CSp, G, GV, US) call get_param(param_file, mdl, "KD_MAX", CS%Kd_max, & "The maximum permitted increment for the diapycnal "//& "diffusivity from TKE-based parameterizations, or a "//& - "negative value for no limit.", units="m2 s-1", default=-1.0, scale=US%m2_s_to_Z2_T) + "negative value for no limit.", units="m2 s-1", default=-1.0, scale=GV%m2_s_to_HZ_T) call get_param(param_file, mdl, "MIN_RESIDUAL_TRANSPORT", CS%min_residual, & "How much remaining transport before the main offline advection is exited. "//& "The default value corresponds to about 1 meter of difference in a grid cell", & From 72fbee0e92abd1caa858b0cd24f0e0e97673587f Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Sat, 12 Aug 2023 11:23:32 -0400 Subject: [PATCH 14/17] +Obsolete 18 2018_ANSWERS runtime parameters Obsoleted 18 2018_ANSWERS parameters in 26 modules, with obsoleting message hints indicating the corresponding ANSWER_DATE parameters. The runtime parameters that have been obsoleted are DEFAULT_2018_ANSWERS, SURFACE_FORCING_2018_ANSWERS, WIND_GYRES_2018_ANSWERS, BAROTROPIC_2018_ANSWERS, EPBL_2018_ANSWERS, HOR_REGRID_2018_ANSWERS, HOR_VISC_2018_ANSWERS, IDL_HURR_2018_ANSWERS, MEKE_GEOMETRIC_2018_ANSWERS, ODA_2018_ANSWERS, OPTICS_2018_ANSWERS, REGULARIZE_LAYERS_2018_ANSWERS, REMAPPING_2018_ANSWERS, SET_DIFF_2018_ANSWERS, SET_VISC_2018_ANSWERS, SURFACE_2018_ANSWERS, TIDAL_MIXING_2018_ANSWERS and VERT_FRICTION_2018_ANSWERS. These changes will cause cases that use these older parameters to fail with a useful error message, but this change has been discussed and agreed to by the MOM6 community on July 17, 2023. All answers are bitwise identical in any cases that work. --- .../FMS_cap/MOM_surface_forcing_gfdl.F90 | 19 +---- .../solo_driver/MOM_surface_forcing.F90 | 21 +---- src/ALE/MOM_ALE.F90 | 21 +---- src/ALE/MOM_regridding.F90 | 19 +---- src/core/MOM.F90 | 21 +---- src/core/MOM_barotropic.F90 | 21 +---- src/core/MOM_open_boundary.F90 | 19 +---- src/diagnostics/MOM_diagnostics.F90 | 19 +---- src/diagnostics/MOM_obsolete_params.F90 | 33 ++++++++ src/framework/MOM_diag_mediator.F90 | 21 +---- .../MOM_state_initialization.F90 | 65 ++------------- .../MOM_tracer_initialization_from_Z.F90 | 40 +-------- src/ocean_data_assim/MOM_oda_driver.F90 | 22 +---- .../lateral/MOM_hor_visc.F90 | 19 +---- .../lateral/MOM_lateral_mixing_coeffs.F90 | 19 +---- .../lateral/MOM_thickness_diffuse.F90 | 20 +---- .../vertical/MOM_ALE_sponge.F90 | 82 ++----------------- .../vertical/MOM_energetic_PBL.F90 | 19 +---- .../vertical/MOM_opacity.F90 | 21 +---- .../vertical/MOM_regularize_layers.F90 | 19 +---- .../vertical/MOM_set_diffusivity.F90 | 21 +---- .../vertical/MOM_set_viscosity.F90 | 21 +---- .../vertical/MOM_tidal_mixing.F90 | 42 +--------- .../vertical/MOM_vert_friction.F90 | 22 +---- src/tracer/MOM_neutral_diffusion.F90 | 19 +---- src/user/Idealized_Hurricane.F90 | 20 +---- 26 files changed, 90 insertions(+), 595 deletions(-) diff --git a/config_src/drivers/FMS_cap/MOM_surface_forcing_gfdl.F90 b/config_src/drivers/FMS_cap/MOM_surface_forcing_gfdl.F90 index 713f04dc18..164193f6d7 100644 --- a/config_src/drivers/FMS_cap/MOM_surface_forcing_gfdl.F90 +++ b/config_src/drivers/FMS_cap/MOM_surface_forcing_gfdl.F90 @@ -1284,10 +1284,6 @@ subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, wind_stagger) ! or other equivalent files. logical :: iceberg_flux_diags ! If true, diagnostics of fluxes from icebergs are available. integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: answers_2018 ! If true, use the order of arithmetic and expressions that recover - ! the answers from the end of 2018. Otherwise, use a simpler - ! expression to calculate gustiness. type(time_type) :: Time_frc type(directories) :: dirs ! A structure containing relevant directory paths and input filenames. character(len=200) :: TideAmp_file, gust_file, salt_file, temp_file ! Input file names. @@ -1586,22 +1582,11 @@ subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, wind_stagger) call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101)) - call get_param(param_file, mdl, "SURFACE_FORCING_2018_ANSWERS", answers_2018, & - "If true, use the order of arithmetic and expressions that recover the answers "//& - "from the end of 2018. Otherwise, use a simpler expression to calculate gustiness.", & - default=default_2018_answers) - ! Revise inconsistent default answer dates. - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 call get_param(param_file, mdl, "SURFACE_FORCING_ANSWER_DATE", CS%answer_date, & "The vintage of the order of arithmetic and expressions in the gustiness "//& "calculations. Values below 20190101 recover the answers from the end "//& - "of 2018, while higher values use a simpler expression to calculate gustiness. "//& - "If both SURFACE_FORCING_2018_ANSWERS and SURFACE_FORCING_ANSWER_DATE are "//& - "specified, the latter takes precedence.", default=default_answer_date) + "of 2018, while higher values use a simpler expression to calculate gustiness.", & + default=default_answer_date) call get_param(param_file, mdl, "FIX_USTAR_GUSTLESS_BUG", CS%fix_ustar_gustless_bug, & "If true correct a bug in the time-averaging of the gustless wind friction velocity", & diff --git a/config_src/drivers/solo_driver/MOM_surface_forcing.F90 b/config_src/drivers/solo_driver/MOM_surface_forcing.F90 index 8d46a80cae..5a37b18604 100644 --- a/config_src/drivers/solo_driver/MOM_surface_forcing.F90 +++ b/config_src/drivers/solo_driver/MOM_surface_forcing.F90 @@ -1529,11 +1529,6 @@ subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, tracer_flow_C # include "version_variable.h" real :: flux_const_default ! The unscaled value of FLUXCONST [m day-1] integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: answers_2018 ! If true, use the order of arithmetic and expressions that recover - ! the answers from the end of 2018. Otherwise, use a form of the gyre - ! wind stresses that are rotationally invariant and more likely to be - ! the same between compilers. character(len=40) :: mdl = "MOM_surface_forcing" ! This module's name. character(len=200) :: filename, gust_file ! The name of the gustiness input file. @@ -1769,24 +1764,12 @@ subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, tracer_flow_C call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101)) - call get_param(param_file, mdl, "WIND_GYRES_2018_ANSWERS", answers_2018, & - "If true, use the order of arithmetic and expressions that recover the answers "//& - "from the end of 2018. Otherwise, use expressions for the gyre friction velocities "//& - "that are rotationally invariant and more likely to be the same between compilers.", & - default=default_2018_answers) - ! Revise inconsistent default answer dates. - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 call get_param(param_file, mdl, "WIND_GYRES_ANSWER_DATE", CS%answer_date, & "The vintage of the expressions used to set gyre wind stresses. "//& "Values below 20190101 recover the answers from the end of 2018, "//& "while higher values use a form of the gyre wind stresses that are "//& - "rotationally invariant and more likely to be the same between compilers. "//& - "If both WIND_GYRES_2018_ANSWERS and WIND_GYRES_ANSWER_DATE are specified, "//& - "the latter takes precedence.", default=default_answer_date) + "rotationally invariant and more likely to be the same between compilers.", & + default=default_answer_date) else CS%answer_date = 20190101 endif diff --git a/src/ALE/MOM_ALE.F90 b/src/ALE/MOM_ALE.F90 index 4d7445093a..e1c8e6911e 100644 --- a/src/ALE/MOM_ALE.F90 +++ b/src/ALE/MOM_ALE.F90 @@ -170,10 +170,6 @@ subroutine ALE_init( param_file, GV, US, max_depth, CS) character(len=80) :: string, vel_string ! Temporary strings real :: filter_shallow_depth, filter_deep_depth ! Depth ranges of filtering [H ~> m or kg m-2] integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: answers_2018 ! If true, use the order of arithmetic and expressions for remapping - ! that recover the answers from the end of 2018. Otherwise, use more - ! robust and accurate forms of mathematically equivalent expressions. logical :: check_reconstruction logical :: check_remapping logical :: force_bounds_in_subcell @@ -231,25 +227,12 @@ subroutine ALE_init( param_file, GV, US, max_depth, CS) call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "REMAPPING_2018_ANSWERS", answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for remapping. - if (GV%Boussinesq) then - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "REMAPPING_ANSWER_DATE", CS%answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& "that were in use at the end of 2018. Higher values result in the use of more "//& - "robust and accurate forms of mathematically equivalent expressions. "//& - "If both REMAPPING_2018_ANSWERS and REMAPPING_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_answer_date, do_not_log=.not.GV%Boussinesq) + "robust and accurate forms of mathematically equivalent expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701) call initialize_remapping( CS%remapCS, string, & diff --git a/src/ALE/MOM_regridding.F90 b/src/ALE/MOM_regridding.F90 index c238c2aa61..1b006dbbd3 100644 --- a/src/ALE/MOM_regridding.F90 +++ b/src/ALE/MOM_regridding.F90 @@ -209,8 +209,6 @@ subroutine initialize_regridding(CS, GV, US, max_depth, param_file, mdl, coord_m logical :: tmpLogical, do_sum, main_parameters logical :: coord_is_state_dependent, ierr integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: remap_answers_2018 integer :: remap_answer_date ! The vintage of the remapping expressions to use. integer :: regrid_answer_date ! The vintage of the regridding expressions to use. real :: tmpReal ! A temporary variable used in setting other variables [various] @@ -275,25 +273,12 @@ subroutine initialize_regridding(CS, GV, US, max_depth, param_file, mdl, coord_m call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "REMAPPING_2018_ANSWERS", remap_answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for remapping. - if (GV%Boussinesq) then - if (remap_answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.remap_answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "REMAPPING_ANSWER_DATE", remap_answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& "that were in use at the end of 2018. Higher values result in the use of more "//& - "robust and accurate forms of mathematically equivalent expressions. "//& - "If both REMAPPING_2018_ANSWERS and REMAPPING_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_answer_date, do_not_log=.not.GV%Boussinesq) + "robust and accurate forms of mathematically equivalent expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) remap_answer_date = max(remap_answer_date, 20230701) call set_regrid_params(CS, remap_answer_date=remap_answer_date) call get_param(param_file, mdl, "REGRIDDING_ANSWER_DATE", regrid_answer_date, & diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 86040e8969..ce001483ff 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -2028,10 +2028,6 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & logical :: bound_salinity ! If true, salt is added to keep salinity above ! a minimum value, and the deficit is reported. integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: answers_2018 ! If true, use expressions for the surface properties that recover - ! the answers from the end of 2018. Otherwise, use more appropriate - ! expressions that differ at roundoff for non-Boussinesq cases. logical :: use_conT_absS ! If true, the prognostics T & S are conservative temperature ! and absolute salinity. Care should be taken to convert them ! to potential temperature and practical salinity before @@ -2383,24 +2379,11 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & call get_param(param_file, "MOM", "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, "MOM", "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=non_Bous) - call get_param(param_file, "MOM", "SURFACE_2018_ANSWERS", answers_2018, & - "If true, use expressions for the surface properties that recover the answers "//& - "from the end of 2018. Otherwise, use more appropriate expressions that differ "//& - "at roundoff for non-Boussinesq cases.", default=default_2018_answers, do_not_log=non_Bous) - ! Revise inconsistent default answer dates. - if (.not.non_Bous) then - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, "MOM", "SURFACE_ANSWER_DATE", CS%answer_date, & "The vintage of the expressions for the surface properties. Values below "//& "20190101 recover the answers from the end of 2018, while higher values "//& - "use updated and more robust forms of the same expressions. "//& - "If both SURFACE_2018_ANSWERS and SURFACE_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_answer_date, do_not_log=non_Bous) + "use updated and more robust forms of the same expressions.", & + default=default_answer_date, do_not_log=non_Bous) if (non_Bous) CS%answer_date = 99991231 call get_param(param_file, "MOM", "USE_DIABATIC_TIME_BUG", CS%use_diabatic_time_bug, & diff --git a/src/core/MOM_barotropic.F90 b/src/core/MOM_barotropic.F90 index c814c563e3..e6f243a11c 100644 --- a/src/core/MOM_barotropic.F90 +++ b/src/core/MOM_barotropic.F90 @@ -4358,10 +4358,6 @@ subroutine barotropic_init(u, v, h, eta, Time, G, GV, US, param_file, diag, CS, type(group_pass_type) :: pass_static_data, pass_q_D_Cor type(group_pass_type) :: pass_bt_hbt_btav, pass_a_polarity integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: answers_2018 ! If true, use expressions for the barotropic solver that recover - ! the answers from the end of 2018. Otherwise, use more efficient - ! or general expressions. logical :: use_BT_cont_type logical :: use_tides character(len=48) :: thickness_units, flux_units @@ -4505,24 +4501,11 @@ subroutine barotropic_init(u, v, h, eta, Time, G, GV, US, param_file, diag, CS, call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "BAROTROPIC_2018_ANSWERS", answers_2018, & - "If true, use expressions for the barotropic solver that recover the answers "//& - "from the end of 2018. Otherwise, use more efficient or general expressions.", & - default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates. - if (GV%Boussinesq) then - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "BAROTROPIC_ANSWER_DATE", CS%answer_date, & "The vintage of the expressions in the barotropic solver. "//& "Values below 20190101 recover the answers from the end of 2018, "//& - "while higher values uuse more efficient or general expressions. "//& - "If both BAROTROPIC_2018_ANSWERS and BAROTROPIC_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_answer_date, do_not_log=.not.GV%Boussinesq) + "while higher values uuse more efficient or general expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701) call get_param(param_file, mdl, "TIDES", use_tides, & diff --git a/src/core/MOM_open_boundary.F90 b/src/core/MOM_open_boundary.F90 index ba8b8ce818..36a71e3d52 100644 --- a/src/core/MOM_open_boundary.F90 +++ b/src/core/MOM_open_boundary.F90 @@ -422,11 +422,7 @@ subroutine open_boundary_config(G, US, param_file, OBC) character(len=1024) :: segment_str ! The contents (rhs) for parameter "segment_param_str" character(len=200) :: config1 ! String for OBC_USER_CONFIG real :: Lscale_in, Lscale_out ! parameters controlling tracer values at the boundaries [L ~> m] - logical :: answers_2018 ! If true, use the order of arithmetic and expressions for remapping - ! that recover the answers from the end of 2018. Otherwise, use more - ! robust and accurate forms of mathematically equivalent expressions. integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. logical :: check_reconstruction, check_remapping, force_bounds_in_subcell character(len=64) :: remappingScheme ! This include declares and sets the variable "version". @@ -676,23 +672,12 @@ subroutine open_boundary_config(G, US, param_file, OBC) call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101)) - call get_param(param_file, mdl, "REMAPPING_2018_ANSWERS", answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers) - ! Revise inconsistent default answer dates for remapping. - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 call get_param(param_file, mdl, "REMAPPING_ANSWER_DATE", OBC%remap_answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& "that were in use at the end of 2018. Higher values result in the use of more "//& - "robust and accurate forms of mathematically equivalent expressions. "//& - "If both REMAPPING_2018_ANSWERS and REMAPPING_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_answer_date) + "robust and accurate forms of mathematically equivalent expressions.", & + default=default_answer_date) allocate(OBC%remap_CS) call initialize_remapping(OBC%remap_CS, remappingScheme, boundary_extrapolation = .false., & diff --git a/src/diagnostics/MOM_diagnostics.F90 b/src/diagnostics/MOM_diagnostics.F90 index 253b7189e3..aeb25bc351 100644 --- a/src/diagnostics/MOM_diagnostics.F90 +++ b/src/diagnostics/MOM_diagnostics.F90 @@ -1572,12 +1572,10 @@ subroutine MOM_diagnostics_init(MIS, ADp, CDp, Time, G, GV, US, param_file, diag character(len=48) :: thickness_units, flux_units logical :: use_temperature, adiabatic integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. integer :: remap_answer_date ! The vintage of the order of arithmetic and expressions to use ! for remapping. Values below 20190101 recover the remapping ! answers from 2018, while higher values use more robust ! forms of the same remapping expressions. - logical :: remap_answers_2018 CS%initialized = .true. @@ -1608,25 +1606,12 @@ subroutine MOM_diagnostics_init(MIS, ADp, CDp, Time, G, GV, US, param_file, diag call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "REMAPPING_2018_ANSWERS", remap_answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for remapping. - if (GV%Boussinesq) then - if (remap_answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.remap_answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "REMAPPING_ANSWER_DATE", remap_answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& "that were in use at the end of 2018. Higher values result in the use of more "//& - "robust and accurate forms of mathematically equivalent expressions. "//& - "If both REMAPPING_2018_ANSWERS and REMAPPING_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_answer_date, do_not_log=.not.GV%Boussinesq) + "robust and accurate forms of mathematically equivalent expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) remap_answer_date = max(remap_answer_date, 20230701) call get_param(param_file, mdl, "SPLIT", split, default=.true., do_not_log=.true.) diff --git a/src/diagnostics/MOM_obsolete_params.F90 b/src/diagnostics/MOM_obsolete_params.F90 index 7614eb210c..b1f4444b1b 100644 --- a/src/diagnostics/MOM_obsolete_params.F90 +++ b/src/diagnostics/MOM_obsolete_params.F90 @@ -115,6 +115,39 @@ subroutine find_obsolete_params(param_file) call obsolete_logical(param_file, "TIDAL_SAL_SHT", hint="Use SAL_HARMONICS instead.") call obsolete_int(param_file, "TIDAL_SAL_SHT_DEGREE", hint="Use SAL_HARMONICS_DEGREE instead.") call obsolete_real(param_file, "RHO_E", hint="Use RHO_SOLID_EARTH instead.") + call obsolete_logical(param_file, "DEFAULT_2018_ANSWERS", hint="Instead use DEFAULT_ANSWER_DATE.") + + call obsolete_logical(param_file, "SURFACE_FORCING_2018_ANSWERS", & + hint="Instead use SURFACE_FORCING_ANSWER_DATE.") + call obsolete_logical(param_file, "WIND_GYRES_2018_ANSWERS", & + hint="Instead use WIND_GYRES_ANSWER_DATE.") + + call obsolete_logical(param_file, "BAROTROPIC_2018_ANSWERS", & + hint="Instead use BAROTROPIC_ANSWER_DATE.") + call obsolete_logical(param_file, "EPBL_2018_ANSWERS", hint="Instead use EPBL_ANSWER_DATE.") + call obsolete_logical(param_file, "HOR_REGRID_2018_ANSWERS", & + hint="Instead use HOR_REGRID_ANSWER_DATE.") + call obsolete_logical(param_file, "HOR_VISC_2018_ANSWERS", & + hint="Instead use HOR_VISC_ANSWER_DATE.") + call obsolete_logical(param_file, "IDL_HURR_2018_ANSWERS", & + hint="Instead use IDL_HURR_ANSWER_DATE.") + call obsolete_logical(param_file, "MEKE_GEOMETRIC_2018_ANSWERS", & + hint="Instead use MEKE_GEOMETRIC_ANSWER_DATE.") + call obsolete_logical(param_file, "ODA_2018_ANSWERS", hint="Instead use ODA_ANSWER_DATE.") + call obsolete_logical(param_file, "OPTICS_2018_ANSWERS", hint="Instead use OPTICS_ANSWER_DATE.") + call obsolete_logical(param_file, "REGULARIZE_LAYERS_2018_ANSWERS", & + hint="Instead use REGULARIZE_LAYERS_ANSWER_DATE.") + call obsolete_logical(param_file, "REMAPPING_2018_ANSWERS", & + hint="Instead use REMAPPING_ANSWER_DATE.") + call obsolete_logical(param_file, "SET_DIFF_2018_ANSWERS", & + hint="Instead use SET_DIFF_ANSWER_DATE.") + call obsolete_logical(param_file, "SET_VISC_2018_ANSWERS", & + hint="Instead use SET_VISC_ANSWER_DATE.") + call obsolete_logical(param_file, "SURFACE_2018_ANSWERS", hint="Instead use SURFACE_ANSWER_DATE.") + call obsolete_logical(param_file, "TIDAL_MIXING_2018_ANSWERS", & + hint="Instead use TIDAL_MIXING_ANSWER_DATE.") + call obsolete_logical(param_file, "VERT_FRICTION_2018_ANSWERS", & + hint="Instead use VERT_FRICTION_ANSWER_DATE.") ! Write the file version number to the model log. call log_version(param_file, mdl, version) diff --git a/src/framework/MOM_diag_mediator.F90 b/src/framework/MOM_diag_mediator.F90 index 58511c866b..61290cb579 100644 --- a/src/framework/MOM_diag_mediator.F90 +++ b/src/framework/MOM_diag_mediator.F90 @@ -3144,15 +3144,11 @@ subroutine diag_mediator_init(G, GV, US, nz, param_file, diag_cs, doc_file_dir) ! Local variables integer :: ios, i, new_unit logical :: opened, new_file - logical :: remap_answers_2018 ! If true, use the order of arithmetic and expressions that - ! recover the remapping answers from 2018. If false, use more - ! robust forms of the same remapping expressions. integer :: remap_answer_date ! The vintage of the order of arithmetic and expressions to use ! for remapping. Values below 20190101 recover the remapping ! answers from 2018, while higher values use more robust ! forms of the same remapping expressions. integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. character(len=8) :: this_pe character(len=240) :: doc_file, doc_file_dflt, doc_path character(len=240), allocatable :: diag_coords(:) @@ -3182,25 +3178,12 @@ subroutine diag_mediator_init(G, GV, US, nz, param_file, diag_cs, doc_file_dir) call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "REMAPPING_2018_ANSWERS", remap_answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for remapping. - if (GV%Boussinesq) then - if (remap_answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.remap_answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "REMAPPING_ANSWER_DATE", remap_answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& "that were in use at the end of 2018. Higher values result in the use of more "//& - "robust and accurate forms of mathematically equivalent expressions. "//& - "If both REMAPPING_2018_ANSWERS and REMAPPING_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_answer_date, do_not_log=.not.GV%Boussinesq) + "robust and accurate forms of mathematically equivalent expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) remap_answer_date = max(remap_answer_date, 20230701) call get_param(param_file, mdl, 'USE_GRID_SPACE_DIAGNOSTIC_AXES', diag_cs%grid_space_axes, & 'If true, use a grid index coordinate convention for diagnostic axes. ',& diff --git a/src/initialization/MOM_state_initialization.F90 b/src/initialization/MOM_state_initialization.F90 index ddccf4a754..fc676781bc 100644 --- a/src/initialization/MOM_state_initialization.F90 +++ b/src/initialization/MOM_state_initialization.F90 @@ -1130,10 +1130,6 @@ subroutine trim_for_ice(PF, G, GV, US, ALE_CSp, tv, h, just_read) real :: z_tolerance ! The tolerance with which to find the depth matching a specified pressure [Z ~> m]. integer :: i, j, k integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: remap_answers_2018 ! If true, use the order of arithmetic and expressions that - ! recover the remapping answers from 2018. If false, use more - ! robust forms of the same remapping expressions. integer :: remap_answer_date ! The vintage of the order of arithmetic and expressions to use ! for remapping. Values below 20190101 recover the remapping ! answers from 2018, while higher values use more robust @@ -1169,26 +1165,11 @@ subroutine trim_for_ice(PF, G, GV, US, ALE_CSp, tv, h, just_read) call get_param(PF, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231, do_not_log=just_read) - call get_param(PF, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=just_read.or.(.not.GV%Boussinesq)) - call get_param(PF, mdl, "REMAPPING_2018_ANSWERS", remap_answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", & - default=default_2018_answers, do_not_log=just_read.or.(.not.GV%Boussinesq)) - ! Revise inconsistent default answer dates for remapping. - if (GV%Boussinesq) then - if (remap_answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.remap_answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(PF, mdl, "REMAPPING_ANSWER_DATE", remap_answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& "that were in use at the end of 2018. Higher values result in the use of more "//& - "robust and accurate forms of mathematically equivalent expressions. "//& - "If both REMAPPING_2018_ANSWERS and REMAPPING_ANSWER_DATE are specified, the "//& - "latter takes precedence.", & + "robust and accurate forms of mathematically equivalent expressions.", & default=default_answer_date, do_not_log=just_read.or.(.not.GV%Boussinesq)) if (.not.GV%Boussinesq) remap_answer_date = max(remap_answer_date, 20230701) else @@ -2505,17 +2486,10 @@ subroutine MOM_temp_salt_initialize_from_Z(h, tv, depth_tot, G, GV, US, PF, just logical :: homogenize, useALEremapping, remap_full_column, remap_general, remap_old_alg integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: remap_answers_2018 ! If true, use the order of arithmetic and expressions that - ! recover the remapping answers from 2018. If false, use more - ! robust forms of the same remapping expressions. - integer :: default_remap_ans_date ! The default setting for remap_answer_date integer :: remap_answer_date ! The vintage of the order of arithmetic and expressions to use ! for remapping. Values below 20190101 recover the remapping ! answers from 2018, while higher values use more robust ! forms of the same remapping expressions. - logical :: hor_regrid_answers_2018 - integer :: default_hor_reg_ans_date ! The default setting for hor_regrid_answer_date integer :: hor_regrid_answer_date ! The vintage of the order of arithmetic and expressions to use ! for horizontal regridding. Values below 20190101 recover the ! answers from 2018, while higher values use expressions that have @@ -2596,55 +2570,26 @@ subroutine MOM_temp_salt_initialize_from_Z(h, tv, depth_tot, G, GV, US, PF, just call get_param(PF, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231, do_not_log=just_read) - call get_param(PF, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=just_read.or.(.not.GV%Boussinesq)) call get_param(PF, mdl, "TEMP_SALT_INIT_VERTICAL_REMAP_ONLY", pre_gridded, & "If true, initial conditions are on the model horizontal grid. " //& "Extrapolation over missing ocean values is done using an ICE-9 "//& "procedure with vertical ALE remapping .", & default=.false., do_not_log=just_read) if (useALEremapping) then - call get_param(PF, mdl, "REMAPPING_2018_ANSWERS", remap_answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", & - default=default_2018_answers, do_not_log=just_read.or.(.not.GV%Boussinesq)) - ! Revise inconsistent default answer dates for remapping. - default_remap_ans_date = default_answer_date - if (GV%Boussinesq) then - if (remap_answers_2018 .and. (default_remap_ans_date >= 20190101)) default_remap_ans_date = 20181231 - if (.not.remap_answers_2018 .and. (default_remap_ans_date < 20190101)) default_remap_ans_date = 20190101 - endif call get_param(PF, mdl, "REMAPPING_ANSWER_DATE", remap_answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& "that were in use at the end of 2018. Higher values result in the use of more "//& - "robust and accurate forms of mathematically equivalent expressions. "//& - "If both REMAPPING_2018_ANSWERS and REMAPPING_ANSWER_DATE are specified, the "//& - "latter takes precedence.", & - default=default_remap_ans_date, do_not_log=just_read.or.(.not.GV%Boussinesq)) + "robust and accurate forms of mathematically equivalent expressions.", & + default=default_answer_date, do_not_log=just_read.or.(.not.GV%Boussinesq)) if (.not.GV%Boussinesq) remap_answer_date = max(remap_answer_date, 20230701) endif - call get_param(PF, mdl, "HOR_REGRID_2018_ANSWERS", hor_regrid_answers_2018, & - "If true, use the order of arithmetic for horizontal regridding that recovers "//& - "the answers from the end of 2018. Otherwise, use rotationally symmetric "//& - "forms of the same expressions.", & - default=default_2018_answers, do_not_log=just_read.or.(.not.GV%Boussinesq)) - ! Revise inconsistent default answer dates for horizontal regridding. - default_hor_reg_ans_date = default_answer_date - if (GV%Boussinesq) then - if (hor_regrid_answers_2018 .and. (default_hor_reg_ans_date >= 20190101)) default_hor_reg_ans_date = 20181231 - if (.not.hor_regrid_answers_2018 .and. (default_hor_reg_ans_date < 20190101)) default_hor_reg_ans_date = 20190101 - endif call get_param(PF, mdl, "HOR_REGRID_ANSWER_DATE", hor_regrid_answer_date, & "The vintage of the order of arithmetic for horizontal regridding. "//& "Dates before 20190101 give the same answers as the code did in late 2018, "//& "while later versions add parentheses for rotational symmetry. "//& - "Dates after 20230101 use reproducing sums for global averages. "//& - "If both HOR_REGRID_2018_ANSWERS and HOR_REGRID_ANSWER_DATE are specified, the "//& - "latter takes precedence.", & - default=default_hor_reg_ans_date, do_not_log=just_read.or.(.not.GV%Boussinesq)) + "Dates after 20230101 use reproducing sums for global averages.", & + default=default_answer_date, do_not_log=just_read.or.(.not.GV%Boussinesq)) if (.not.GV%Boussinesq) hor_regrid_answer_date = max(hor_regrid_answer_date, 20230701) if (.not.useALEremapping) then diff --git a/src/initialization/MOM_tracer_initialization_from_Z.F90 b/src/initialization/MOM_tracer_initialization_from_Z.F90 index decd197b2b..808430df2c 100644 --- a/src/initialization/MOM_tracer_initialization_from_Z.F90 +++ b/src/initialization/MOM_tracer_initialization_from_Z.F90 @@ -87,17 +87,10 @@ subroutine MOM_initialize_tracer_from_Z(h, tr, G, GV, US, PF, src_file, src_var_ integer :: nPoints ! The number of valid input data points in a column integer :: id_clock_routine, id_clock_ALE integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: remap_answers_2018 ! If true, use the order of arithmetic and expressions that - ! recover the remapping answers from 2018. If false, use more - ! robust forms of the same remapping expressions. - integer :: default_remap_ans_date ! The default setting for remap_answer_date integer :: remap_answer_date ! The vintage of the order of arithmetic and expressions to use ! for remapping. Values below 20190101 recover the remapping ! answers from 2018, while higher values use more robust ! forms of the same remapping expressions. - logical :: hor_regrid_answers_2018 - integer :: default_hor_reg_ans_date ! The default setting for hor_regrid_answer_date integer :: hor_regrid_answer_date ! The vintage of the order of arithmetic and expressions to use ! for horizontal regridding. Values below 20190101 recover the ! answers from 2018, while higher values use expressions that have @@ -125,46 +118,21 @@ subroutine MOM_initialize_tracer_from_Z(h, tr, G, GV, US, PF, src_file, src_var_ call get_param(PF, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(PF, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) if (useALE) then - call get_param(PF, mdl, "REMAPPING_2018_ANSWERS", remap_answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for remapping. - default_remap_ans_date = default_answer_date - if (GV%Boussinesq) then - if (remap_answers_2018 .and. (default_remap_ans_date >= 20190101)) default_remap_ans_date = 20181231 - if (.not.remap_answers_2018 .and. (default_remap_ans_date < 20190101)) default_remap_ans_date = 20190101 - endif call get_param(PF, mdl, "REMAPPING_ANSWER_DATE", remap_answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& "that were in use at the end of 2018. Higher values result in the use of more "//& - "robust and accurate forms of mathematically equivalent expressions. "//& - "If both REMAPPING_2018_ANSWERS and REMAPPING_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_remap_ans_date, do_not_log=.not.GV%Boussinesq) + "robust and accurate forms of mathematically equivalent expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) remap_answer_date = max(remap_answer_date, 20230701) endif - call get_param(PF, mdl, "HOR_REGRID_2018_ANSWERS", hor_regrid_answers_2018, & - "If true, use the order of arithmetic for horizonal regridding that recovers "//& - "the answers from the end of 2018. Otherwise, use rotationally symmetric "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for horizontal regridding. - default_hor_reg_ans_date = default_answer_date - if (GV%Boussinesq) then - if (hor_regrid_answers_2018 .and. (default_hor_reg_ans_date >= 20190101)) default_hor_reg_ans_date = 20181231 - if (.not.hor_regrid_answers_2018 .and. (default_hor_reg_ans_date < 20190101)) default_hor_reg_ans_date = 20190101 - endif call get_param(PF, mdl, "HOR_REGRID_ANSWER_DATE", hor_regrid_answer_date, & "The vintage of the order of arithmetic for horizontal regridding. "//& "Dates before 20190101 give the same answers as the code did in late 2018, "//& "while later versions add parentheses for rotational symmetry. "//& - "Dates after 20230101 use reproducing sums for global averages. "//& - "If both HOR_REGRID_2018_ANSWERS and HOR_REGRID_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_hor_reg_ans_date, do_not_log=.not.GV%Boussinesq) + "Dates after 20230101 use reproducing sums for global averages.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) hor_regrid_answer_date = max(hor_regrid_answer_date, 20230701) if (PRESENT(homogenize)) homog=homogenize diff --git a/src/ocean_data_assim/MOM_oda_driver.F90 b/src/ocean_data_assim/MOM_oda_driver.F90 index fc67b20e87..875051b6c7 100644 --- a/src/ocean_data_assim/MOM_oda_driver.F90 +++ b/src/ocean_data_assim/MOM_oda_driver.F90 @@ -182,11 +182,7 @@ subroutine init_oda(Time, G, GV, US, diag_CS, CS) character(len=80) :: basin_var character(len=80) :: remap_scheme character(len=80) :: bias_correction_file, inc_file - logical :: answers_2018 ! If true, use the order of arithmetic and expressions that recover the - ! answers from the end of 2018. Otherwise, use updated and more robust - ! forms of the same expressions. integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. if (associated(CS)) call MOM_error(FATAL, 'Calling oda_init with associated control structure') allocate(CS) @@ -253,25 +249,11 @@ subroutine init_oda(Time, G, GV, US, diag_CS, CS) call get_param(PF, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(PF, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(PF, mdl, "ODA_2018_ANSWERS", answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from original version of the ODA driver. Otherwise, use updated and "//& - "more robust forms of the same expressions.", & - default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates. - if (GV%Boussinesq) then - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(PF, mdl, "ODA_ANSWER_DATE", CS%answer_date, & "The vintage of the order of arithmetic and expressions used by the ODA driver "//& "Values below 20190101 recover the answers from the end of 2018, while higher "//& - "values use updated and more robust forms of the same expressions. "//& - "If both ODA_2018_ANSWERS and ODA_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_answer_date, do_not_log=.not.GV%Boussinesq) + "values use updated and more robust forms of the same expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701) inputdir = slasher(inputdir) diff --git a/src/parameterizations/lateral/MOM_hor_visc.F90 b/src/parameterizations/lateral/MOM_hor_visc.F90 index f9a35c1e3d..2d1c38abf9 100644 --- a/src/parameterizations/lateral/MOM_hor_visc.F90 +++ b/src/parameterizations/lateral/MOM_hor_visc.F90 @@ -1758,11 +1758,7 @@ subroutine hor_visc_init(Time, G, GV, US, param_file, diag, CS, ADp) logical :: split ! If true, use the split time stepping scheme. ! If false and USE_GME = True, issue a FATAL error. logical :: use_MEKE ! If true, the MEKE parameterization is in use. - logical :: answers_2018 ! If true, use the order of arithmetic and expressions that recover the - ! answers from the end of 2018. Otherwise, use updated and more robust - ! forms of the same expressions. integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags character(len=200) :: inputdir, filename ! Input file names and paths character(len=80) :: Kh_var ! Input variable names real :: deg2rad ! Converts degrees to radians [radians degree-1] @@ -1793,24 +1789,11 @@ subroutine hor_visc_init(Time, G, GV, US, param_file, diag, CS, ADp) call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "HOR_VISC_2018_ANSWERS", answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for horizontal viscosity. - if (GV%Boussinesq) then - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "HOR_VISC_ANSWER_DATE", CS%answer_date, & "The vintage of the order of arithmetic and expressions in the horizontal "//& "viscosity calculations. Values below 20190101 recover the answers from the "//& "end of 2018, while higher values use updated and more robust forms of the "//& - "same expressions. If both HOR_VISC_2018_ANSWERS and HOR_VISC_ANSWER_DATE are "//& - "specified, the latter takes precedence.", & + "same expressions.", & default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701) diff --git a/src/parameterizations/lateral/MOM_lateral_mixing_coeffs.F90 b/src/parameterizations/lateral/MOM_lateral_mixing_coeffs.F90 index d3ee675269..1bf416b00a 100644 --- a/src/parameterizations/lateral/MOM_lateral_mixing_coeffs.F90 +++ b/src/parameterizations/lateral/MOM_lateral_mixing_coeffs.F90 @@ -1110,8 +1110,6 @@ subroutine VarMix_init(Time, G, GV, US, param_file, diag, CS) ! default value is roughly (pi / (the age of the universe)). logical :: Gill_equatorial_Ld, use_FGNV_streamfn, use_MEKE, in_use integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: remap_answers_2018 integer :: remap_answer_date ! The vintage of the order of arithmetic and expressions to use ! for remapping. Values below 20190101 recover the remapping ! answers from 2018, while higher values use more robust @@ -1505,25 +1503,12 @@ subroutine VarMix_init(Time, G, GV, US, param_file, diag, CS) call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "REMAPPING_2018_ANSWERS", remap_answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for remapping. - if (GV%Boussinesq) then - if (remap_answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.remap_answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "REMAPPING_ANSWER_DATE", remap_answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& "that were in use at the end of 2018. Higher values result in the use of more "//& - "robust and accurate forms of mathematically equivalent expressions. "//& - "If both REMAPPING_2018_ANSWERS and REMAPPING_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_answer_date, do_not_log=.not.GV%Boussinesq) + "robust and accurate forms of mathematically equivalent expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) remap_answer_date = max(remap_answer_date, 20230701) call get_param(param_file, mdl, "INTERNAL_WAVE_SPEED_TOL", wave_speed_tol, & diff --git a/src/parameterizations/lateral/MOM_thickness_diffuse.F90 b/src/parameterizations/lateral/MOM_thickness_diffuse.F90 index 1de4c9ba6b..248a90d76a 100644 --- a/src/parameterizations/lateral/MOM_thickness_diffuse.F90 +++ b/src/parameterizations/lateral/MOM_thickness_diffuse.F90 @@ -2090,10 +2090,6 @@ subroutine thickness_diffuse_init(Time, G, GV, US, param_file, diag, CDp, CS) real :: Stanley_coeff ! Coefficient relating the temperature gradient and sub-gridscale ! temperature variance [nondim] integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: MEKE_GEOM_answers_2018 ! If true, use expressions in the MEKE_GEOMETRIC calculation - ! that recover the answers from the original implementation. - ! Otherwise, use expressions that satisfy rotational symmetry. integer :: i, j CS%initialized = .true. @@ -2246,24 +2242,10 @@ subroutine thickness_diffuse_init(Time, G, GV, US, param_file, diag, CDp, CS) call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "MEKE_GEOMETRIC_2018_ANSWERS", MEKE_GEOM_answers_2018, & - "If true, use expressions in the MEKE_GEOMETRIC calculation that recover the "//& - "answers from the original implementation. Otherwise, use expressions that "//& - "satisfy rotational symmetry.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for MEKE_geometric. - if (GV%Boussinesq) then - if (MEKE_GEOM_answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.MEKE_GEOM_answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "MEKE_GEOMETRIC_ANSWER_DATE", CS%MEKE_GEOM_answer_date, & "The vintage of the expressions in the MEKE_GEOMETRIC calculation. "//& "Values below 20190101 recover the answers from the original implementation, "//& - "while higher values use expressions that satisfy rotational symmetry. "//& - "If both MEKE_GEOMETRIC_2018_ANSWERS and MEKE_GEOMETRIC_ANSWER_DATE are "//& - "specified, the latter takes precedence.", & + "while higher values use expressions that satisfy rotational symmetry.", & default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%MEKE_GEOM_answer_date = max(CS%MEKE_GEOM_answer_date, 20230701) endif diff --git a/src/parameterizations/vertical/MOM_ALE_sponge.F90 b/src/parameterizations/vertical/MOM_ALE_sponge.F90 index 1faeed00ba..508362c4cc 100644 --- a/src/parameterizations/vertical/MOM_ALE_sponge.F90 +++ b/src/parameterizations/vertical/MOM_ALE_sponge.F90 @@ -180,15 +180,6 @@ subroutine initialize_ALE_sponge_fixed(Iresttime, G, GV, param_file, CS, data_h, logical :: use_sponge logical :: bndExtrapolation = .true. ! If true, extrapolate boundaries integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: remap_answers_2018 ! If true, use the order of arithmetic and expressions that - ! recover the remapping answers from 2018. If false, use more - ! robust forms of the same remapping expressions. - integer :: default_remap_ans_date ! The default setting for remap_answer_date - logical :: hor_regrid_answers_2018 ! If true, use the order of arithmetic for horizontal regridding - ! that recovers the answers from the end of 2018. Otherwise, use - ! rotationally symmetric forms of the same expressions. - integer :: default_hor_reg_ans_date ! The default setting for hor_regrid_answer_date integer :: i, j, k, col, total_sponge_cols, total_sponge_cols_u, total_sponge_cols_v if (associated(CS)) then @@ -226,45 +217,20 @@ subroutine initialize_ALE_sponge_fixed(Iresttime, G, GV, param_file, CS, data_h, call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "REMAPPING_2018_ANSWERS", remap_answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for remapping. - default_remap_ans_date = default_answer_date - if (GV%Boussinesq) then - if (remap_answers_2018 .and. (default_remap_ans_date >= 20190101)) default_remap_ans_date = 20181231 - endif - if (.not.remap_answers_2018 .and. (default_remap_ans_date < 20190101)) default_remap_ans_date = 20190101 call get_param(param_file, mdl, "REMAPPING_ANSWER_DATE", CS%remap_answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& "that were in use at the end of 2018. Higher values result in the use of more "//& - "robust and accurate forms of mathematically equivalent expressions. "//& - "If both REMAPPING_2018_ANSWERS and REMAPPING_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_remap_ans_date, do_not_log=.not.GV%Boussinesq) + "robust and accurate forms of mathematically equivalent expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%remap_answer_date = max(CS%remap_answer_date, 20230701) - call get_param(param_file, mdl, "HOR_REGRID_2018_ANSWERS", hor_regrid_answers_2018, & - "If true, use the order of arithmetic for horizontal regridding that recovers "//& - "the answers from the end of 2018. Otherwise, use rotationally symmetric "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for horizontal regridding. - default_hor_reg_ans_date = default_answer_date - if (GV%Boussinesq) then - if (hor_regrid_answers_2018 .and. (default_hor_reg_ans_date >= 20190101)) default_hor_reg_ans_date = 20181231 - if (.not.hor_regrid_answers_2018 .and. (default_hor_reg_ans_date < 20190101)) default_hor_reg_ans_date = 20190101 - endif call get_param(param_file, mdl, "HOR_REGRID_ANSWER_DATE", CS%hor_regrid_answer_date, & "The vintage of the order of arithmetic for horizontal regridding. "//& "Dates before 20190101 give the same answers as the code did in late 2018, "//& "while later versions add parentheses for rotational symmetry. "//& - "Dates after 20230101 use reproducing sums for global averages. "//& - "If both HOR_REGRID_2018_ANSWERS and HOR_REGRID_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_hor_reg_ans_date, do_not_log=.not.GV%Boussinesq) + "Dates after 20230101 use reproducing sums for global averages.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%hor_regrid_answer_date = max(CS%hor_regrid_answer_date, 20230701) CS%time_varying_sponges = .false. @@ -477,15 +443,6 @@ subroutine initialize_ALE_sponge_varying(Iresttime, G, GV, param_file, CS, Irest logical :: use_sponge logical :: bndExtrapolation = .true. ! If true, extrapolate boundaries integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: remap_answers_2018 ! If true, use the order of arithmetic and expressions that - ! recover the remapping answers from 2018. If false, use more - ! robust forms of the same remapping expressions. - integer :: default_remap_ans_date ! The default setting for remap_answer_date - logical :: hor_regrid_answers_2018 ! If true, use the order of arithmetic for horizontal regridding - ! that recovers the answers from the end of 2018. Otherwise, use - ! rotationally symmetric forms of the same expressions. - integer :: default_hor_reg_ans_date ! The default setting for hor_regrid_answer_date integer :: i, j, col, total_sponge_cols, total_sponge_cols_u, total_sponge_cols_v if (associated(CS)) then @@ -522,41 +479,18 @@ subroutine initialize_ALE_sponge_varying(Iresttime, G, GV, param_file, CS, Irest call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101)) - call get_param(param_file, mdl, "REMAPPING_2018_ANSWERS", remap_answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers) - ! Revise inconsistent default answer dates for remapping. - default_remap_ans_date = default_answer_date - if (remap_answers_2018 .and. (default_remap_ans_date >= 20190101)) default_remap_ans_date = 20181231 - if (.not.remap_answers_2018 .and. (default_remap_ans_date < 20190101)) default_remap_ans_date = 20190101 call get_param(param_file, mdl, "REMAPPING_ANSWER_DATE", CS%remap_answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& "that were in use at the end of 2018. Higher values result in the use of more "//& - "robust and accurate forms of mathematically equivalent expressions. "//& - "If both REMAPPING_2018_ANSWERS and REMAPPING_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_remap_ans_date) - call get_param(param_file, mdl, "HOR_REGRID_2018_ANSWERS", hor_regrid_answers_2018, & - "If true, use the order of arithmetic for horizontal regridding that recovers "//& - "the answers from the end of 2018 and retain a bug in the 3-dimensional mask "//& - "returned in certain cases. Otherwise, use rotationally symmetric "//& - "forms of the same expressions and initialize the mask properly.", & - default=default_2018_answers) - ! Revise inconsistent default answer dates for horizontal regridding. - default_hor_reg_ans_date = default_answer_date - if (hor_regrid_answers_2018 .and. (default_hor_reg_ans_date >= 20190101)) default_hor_reg_ans_date = 20181231 - if (.not.hor_regrid_answers_2018 .and. (default_hor_reg_ans_date < 20190101)) default_hor_reg_ans_date = 20190101 + "robust and accurate forms of mathematically equivalent expressions.", & + default=default_answer_date) call get_param(param_file, mdl, "HOR_REGRID_ANSWER_DATE", CS%hor_regrid_answer_date, & "The vintage of the order of arithmetic for horizontal regridding. "//& "Dates before 20190101 give the same answers as the code did in late 2018, "//& "while later versions add parentheses for rotational symmetry. "//& - "Dates after 20230101 use reproducing sums for global averages. "//& - "If both HOR_REGRID_2018_ANSWERS and HOR_REGRID_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_hor_reg_ans_date) + "Dates after 20230101 use reproducing sums for global averages.", & + default=default_answer_date) call get_param(param_file, mdl, "SPONGE_DATA_ONGRID", CS%spongeDataOngrid, & "When defined, the incoming sponge data are "//& "assumed to be on the model grid " , & diff --git a/src/parameterizations/vertical/MOM_energetic_PBL.F90 b/src/parameterizations/vertical/MOM_energetic_PBL.F90 index 06c3915d84..17da7aceb3 100644 --- a/src/parameterizations/vertical/MOM_energetic_PBL.F90 +++ b/src/parameterizations/vertical/MOM_energetic_PBL.F90 @@ -1962,10 +1962,6 @@ subroutine energetic_PBL_init(Time, G, GV, US, param_file, diag, CS) integer :: isd, ied, jsd, jed integer :: mstar_mode, LT_enhance, wT_mode integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: answers_2018 ! If true, use the order of arithmetic and expressions that recover the - ! answers from the end of 2018. Otherwise, use updated and more robust - ! forms of the same expressions. logical :: use_temperature, use_omega logical :: use_la_windsea isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed @@ -2006,24 +2002,11 @@ subroutine energetic_PBL_init(Time, G, GV, US, param_file, diag, CS) call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "EPBL_2018_ANSWERS", answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for horizontal viscosity. - if (GV%Boussinesq) then - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "EPBL_ANSWER_DATE", CS%answer_date, & "The vintage of the order of arithmetic and expressions in the energetic "//& "PBL calculations. Values below 20190101 recover the answers from the "//& "end of 2018, while higher values use updated and more robust forms of the "//& - "same expressions. If both EPBL_2018_ANSWERS and EPBL_ANSWER_DATE are "//& - "specified, the latter takes precedence.", & + "same expressions.", & default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701) diff --git a/src/parameterizations/vertical/MOM_opacity.F90 b/src/parameterizations/vertical/MOM_opacity.F90 index ac93e54785..c48308a912 100644 --- a/src/parameterizations/vertical/MOM_opacity.F90 +++ b/src/parameterizations/vertical/MOM_opacity.F90 @@ -962,11 +962,7 @@ subroutine opacity_init(Time, G, GV, US, param_file, diag, CS, optics) real :: PenSW_absorb_minthick ! A thickness that is used to absorb the remaining shortwave heat ! flux when that flux drops below PEN_SW_FLUX_ABSORB [H ~> m or kg m-2] real :: PenSW_minthick_dflt ! The default for PenSW_absorb_minthick [m] - logical :: answers_2018 ! If true, use the order of arithmetic and expressions that recover the - ! answers from the end of 2018. Otherwise, use updated and more robust - ! forms of the same expressions. integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags integer :: isd, ied, jsd, jed, nz, n isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed ; nz = GV%ke @@ -1067,25 +1063,10 @@ subroutine opacity_init(Time, G, GV, US, param_file, diag, CS, optics) call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "OPTICS_2018_ANSWERS", answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated expressions for "//& - "handling the absorption of small remaining shortwave fluxes.", & - default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for optics. - if (GV%Boussinesq) then - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "OPTICS_ANSWER_DATE", optics%answer_date, & "The vintage of the order of arithmetic and expressions in the optics calculations. "//& "Values below 20190101 recover the answers from the end of 2018, while "//& - "higher values use updated and more robust forms of the same expressions. "//& - "If both OPTICS_2018_ANSWERS and OPTICS_ANSWER_DATE are "//& - "specified, the latter takes precedence.", & + "higher values use updated and more robust forms of the same expressions.", & default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) optics%answer_date = max(optics%answer_date, 20230701) diff --git a/src/parameterizations/vertical/MOM_regularize_layers.F90 b/src/parameterizations/vertical/MOM_regularize_layers.F90 index d4034d699c..b00238f60c 100644 --- a/src/parameterizations/vertical/MOM_regularize_layers.F90 +++ b/src/parameterizations/vertical/MOM_regularize_layers.F90 @@ -719,10 +719,6 @@ subroutine regularize_layers_init(Time, G, GV, param_file, diag, CS) # include "version_variable.h" character(len=40) :: mdl = "MOM_regularize_layers" ! This module's name. integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags - logical :: answers_2018 ! If true, use the order of arithmetic and expressions that recover the - ! answers from the end of 2018. Otherwise, use updated and more robust - ! forms of the same expressions. logical :: just_read integer :: isd, ied, jsd, jed isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed @@ -760,24 +756,11 @@ subroutine regularize_layers_init(Time, G, GV, param_file, diag, CS) call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231, do_not_log=just_read) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=just_read.or.(.not.GV%Boussinesq)) - call get_param(param_file, mdl, "REGULARIZE_LAYERS_2018_ANSWERS", answers_2018, & - "If true, use the order of arithmetic and expressions that recover the answers "//& - "from the end of 2018. Otherwise, use updated and more robust forms of the "//& - "same expressions.", default=default_2018_answers, do_not_log=just_read.or.(.not.GV%Boussinesq)) - ! Revise inconsistent default answer dates. - if (GV%Boussinesq) then - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "REGULARIZE_LAYERS_ANSWER_DATE", CS%answer_date, & "The vintage of the order of arithmetic and expressions in the regularize "//& "layers calculations. Values below 20190101 recover the answers from the "//& "end of 2018, while higher values use updated and more robust forms of the "//& - "same expressions. If both REGULARIZE_LAYERS_2018_ANSWERS and "//& - "REGULARIZE_LAYERS_ANSWER_DATE are specified, the latter takes precedence.", & + "same expressions.", & default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701) endif diff --git a/src/parameterizations/vertical/MOM_set_diffusivity.F90 b/src/parameterizations/vertical/MOM_set_diffusivity.F90 index c792f5200e..c404e94459 100644 --- a/src/parameterizations/vertical/MOM_set_diffusivity.F90 +++ b/src/parameterizations/vertical/MOM_set_diffusivity.F90 @@ -2067,10 +2067,6 @@ subroutine set_diffusivity_init(Time, G, GV, US, param_file, diag, CS, int_tide_ real :: decay_length ! The maximum decay scale for the BBL diffusion [H ~> m or kg m-2] logical :: ML_use_omega integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: answers_2018 ! If true, use the order of arithmetic and expressions that recover the - ! answers from the end of 2018. Otherwise, use updated and more robust - ! forms of the same expressions. ! This include declares and sets the variable "version". # include "version_variable.h" character(len=40) :: mdl = "MOM_set_diffusivity" ! This module's name. @@ -2124,24 +2120,11 @@ subroutine set_diffusivity_init(Time, G, GV, US, param_file, diag, CS, int_tide_ call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "SET_DIFF_2018_ANSWERS", answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates. - if (GV%Boussinesq) then - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "SET_DIFF_ANSWER_DATE", CS%answer_date, & "The vintage of the order of arithmetic and expressions in the set diffusivity "//& "calculations. Values below 20190101 recover the answers from the end of 2018, "//& - "while higher values use updated and more robust forms of the same expressions. "//& - "If both SET_DIFF_2018_ANSWERS and SET_DIFF_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_answer_date, do_not_log=.not.GV%Boussinesq) + "while higher values use updated and more robust forms of the same expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701) ! CS%use_tidal_mixing is set to True if an internal tidal dissipation scheme is to be used. diff --git a/src/parameterizations/vertical/MOM_set_viscosity.F90 b/src/parameterizations/vertical/MOM_set_viscosity.F90 index 9a99bc6b26..a6c463a7b9 100644 --- a/src/parameterizations/vertical/MOM_set_viscosity.F90 +++ b/src/parameterizations/vertical/MOM_set_viscosity.F90 @@ -2233,10 +2233,6 @@ subroutine set_visc_init(Time, G, GV, US, param_file, diag, visc, CS, restart_CS integer :: i, j, k, is, ie, js, je integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, nz integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: answers_2018 ! If true, use the order of arithmetic and expressions that recover the - ! answers from the end of 2018. Otherwise, use updated and more robust - ! forms of the same expressions. logical :: adiabatic, use_omega, MLE_use_PBL_MLD logical :: use_KPP logical :: use_regridding ! If true, use the ALE algorithm rather than layered @@ -2266,24 +2262,11 @@ subroutine set_visc_init(Time, G, GV, US, param_file, diag, visc, CS, restart_CS call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "SET_VISC_2018_ANSWERS", answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates. - if (GV%Boussinesq) then - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "SET_VISC_ANSWER_DATE", CS%answer_date, & "The vintage of the order of arithmetic and expressions in the set viscosity "//& "calculations. Values below 20190101 recover the answers from the end of 2018, "//& - "while higher values use updated and more robust forms of the same expressions. "//& - "If both SET_VISC_2018_ANSWERS and SET_VISC_ANSWER_DATE are specified, "//& - "the latter takes precedence.", default=default_answer_date, do_not_log=.not.GV%Boussinesq) + "while higher values use updated and more robust forms of the same expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701) call get_param(param_file, mdl, "BOTTOMDRAGLAW", CS%bottomdraglaw, & "If true, the bottom stress is calculated with a drag "//& diff --git a/src/parameterizations/vertical/MOM_tidal_mixing.F90 b/src/parameterizations/vertical/MOM_tidal_mixing.F90 index 95ffe19afb..6e53679549 100644 --- a/src/parameterizations/vertical/MOM_tidal_mixing.F90 +++ b/src/parameterizations/vertical/MOM_tidal_mixing.F90 @@ -230,15 +230,6 @@ logical function tidal_mixing_init(Time, G, GV, US, param_file, int_tide_CSp, di logical :: int_tide_dissipation logical :: read_tideamp integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: remap_answers_2018 ! If true, use the order of arithmetic and expressions that - ! recover the remapping answers from 2018. If false, use more - ! robust forms of the same remapping expressions. - integer :: default_remap_ans_date ! The default setting for remap_answer_date - integer :: default_tide_ans_date ! The default setting for tides_answer_date - logical :: tide_answers_2018 ! If true, use the order of arithmetic and expressions that recover the - ! answers from the end of 2018. Otherwise, use updated and more robust - ! forms of the same expressions. character(len=20) :: tmpstr, int_tide_profile_str character(len=20) :: CVMix_tidal_scheme_str, tidal_energy_type character(len=200) :: filename, h2_file, Niku_TKE_input_file ! Input file names @@ -295,44 +286,19 @@ logical function tidal_mixing_init(Time, G, GV, US, param_file, int_tide_CSp, di call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "TIDAL_MIXING_2018_ANSWERS", tide_answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for the tidal mixing. - default_tide_ans_date = default_answer_date - if (GV%Boussinesq) then - if (tide_answers_2018 .and. (default_tide_ans_date >= 20190101)) default_tide_ans_date = 20181231 - if (.not.tide_answers_2018 .and. (default_tide_ans_date < 20190101)) default_tide_ans_date = 20190101 - endif call get_param(param_file, mdl, "TIDAL_MIXING_ANSWER_DATE", CS%tidal_answer_date, & "The vintage of the order of arithmetic and expressions in the tidal mixing "//& "calculations. Values below 20190101 recover the answers from the end of 2018, "//& - "while higher values use updated and more robust forms of the same expressions. "//& - "If both TIDAL_MIXING_2018_ANSWERS and TIDAL_MIXING_ANSWER_DATE are specified, "//& - "the latter takes precedence.", default=default_tide_ans_date, do_not_log=.not.GV%Boussinesq) + "while higher values use updated and more robust forms of the same expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%tidal_answer_date = max(CS%tidal_answer_date, 20230701) - call get_param(param_file, mdl, "REMAPPING_2018_ANSWERS", remap_answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for remapping. - default_remap_ans_date = default_answer_date - if (GV%Boussinesq) then - if (remap_answers_2018 .and. (default_remap_ans_date >= 20190101)) default_remap_ans_date = 20181231 - if (.not.remap_answers_2018 .and. (default_remap_ans_date < 20190101)) default_remap_ans_date = 20190101 - endif call get_param(param_file, mdl, "REMAPPING_ANSWER_DATE", CS%remap_answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& "that were in use at the end of 2018. Higher values result in the use of more "//& - "robust and accurate forms of mathematically equivalent expressions. "//& - "If both REMAPPING_2018_ANSWERS and REMAPPING_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_remap_ans_date, do_not_log=.not.GV%Boussinesq) + "robust and accurate forms of mathematically equivalent expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%remap_answer_date = max(CS%remap_answer_date, 20230701) if (CS%int_tide_dissipation) then diff --git a/src/parameterizations/vertical/MOM_vert_friction.F90 b/src/parameterizations/vertical/MOM_vert_friction.F90 index b0b47bf2b1..212512dabf 100644 --- a/src/parameterizations/vertical/MOM_vert_friction.F90 +++ b/src/parameterizations/vertical/MOM_vert_friction.F90 @@ -2247,11 +2247,6 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & real :: Kv_BBL ! A viscosity in the bottom boundary layer with a simple scheme [H Z T-1 ~> m2 s-1 or Pa s] real :: Kv_back_z ! A background kinematic viscosity [Z2 T-1 ~> m2 s-1] integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: answers_2018 !< If true, use the order of arithmetic and expressions that recover the - !! answers from the end of 2018. Otherwise, use expressions that do not - !! use an arbitrary and hard-coded maximum viscous coupling coefficient - !! between layers. integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, nz character(len=200) :: kappa_gl90_file, inputdir, kdgl90_varname ! This include declares and sets the variable "version". @@ -2282,28 +2277,13 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "VERT_FRICTION_2018_ANSWERS", answers_2018, & - "If true, use the order of arithmetic and expressions that recover the answers "//& - "from the end of 2018. Otherwise, use expressions that do not use an arbitrary "//& - "hard-coded maximum viscous coupling coefficient between layers.", & - default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates. - if (GV%Boussinesq) then - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 - endif call get_param(param_file, mdl, "VERT_FRICTION_ANSWER_DATE", CS%answer_date, & "The vintage of the order of arithmetic and expressions in the viscous "//& "calculations. Values below 20190101 recover the answers from the end of 2018, "//& "while higher values use expressions that do not use an arbitrary hard-coded "//& "maximum viscous coupling coefficient between layers. Values below 20230601 "//& "recover a form of the viscosity within the mixed layer that breaks up the "//& - "magnitude of the wind stress in some non-Boussinesq cases. "//& - "If both VERT_FRICTION_2018_ANSWERS and VERT_FRICTION_ANSWER_DATE are "//& - "specified, the latter takes precedence.", & + "magnitude of the wind stress in some non-Boussinesq cases.", & default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701) diff --git a/src/tracer/MOM_neutral_diffusion.F90 b/src/tracer/MOM_neutral_diffusion.F90 index 21201db590..720e0012b0 100644 --- a/src/tracer/MOM_neutral_diffusion.F90 +++ b/src/tracer/MOM_neutral_diffusion.F90 @@ -129,10 +129,6 @@ logical function neutral_diffusion_init(Time, G, GV, US, param_file, diag, EOS, ! Local variables character(len=80) :: string ! Temporary strings integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: remap_answers_2018 ! If true, use the order of arithmetic and expressions that - ! recover the answers for remapping from the end of 2018. - ! Otherwise, use more robust forms of the same expressions. logical :: boundary_extrap if (associated(CS)) then @@ -191,23 +187,12 @@ logical function neutral_diffusion_init(Time, G, GV, US, param_file, diag, EOS, call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101), do_not_log=.not.GV%Boussinesq) - call get_param(param_file, mdl, "REMAPPING_2018_ANSWERS", remap_answers_2018, & - "If true, use the order of arithmetic and expressions that recover the "//& - "answers from the end of 2018. Otherwise, use updated and more robust "//& - "forms of the same expressions.", default=default_2018_answers, do_not_log=.not.GV%Boussinesq) - ! Revise inconsistent default answer dates for remapping. - if (remap_answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.remap_answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 call get_param(param_file, mdl, "REMAPPING_ANSWER_DATE", CS%remap_answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& "that were in use at the end of 2018. Higher values result in the use of more "//& - "robust and accurate forms of mathematically equivalent expressions. "//& - "If both REMAPPING_2018_ANSWERS and REMAPPING_ANSWER_DATE are specified, the "//& - "latter takes precedence.", default=default_answer_date, do_not_log=.not.GV%Boussinesq) + "robust and accurate forms of mathematically equivalent expressions.", & + default=default_answer_date, do_not_log=.not.GV%Boussinesq) if (.not.GV%Boussinesq) CS%remap_answer_date = max(CS%remap_answer_date, 20230701) call initialize_remapping( CS%remap_CS, string, boundary_extrapolation=boundary_extrap, & answer_date=CS%remap_answer_date ) diff --git a/src/user/Idealized_Hurricane.F90 b/src/user/Idealized_Hurricane.F90 index 5dd8084fbd..0c9d5cd330 100644 --- a/src/user/Idealized_Hurricane.F90 +++ b/src/user/Idealized_Hurricane.F90 @@ -104,10 +104,6 @@ subroutine idealized_hurricane_wind_init(Time, G, US, param_file, CS) real :: dP ! The pressure difference across the hurricane [R L2 T-2 ~> Pa] real :: C ! A temporary variable [nondim] integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. - logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. - logical :: answers_2018 ! If true, use expressions driving the idealized hurricane test - ! case that recover the answers from the end of 2018. Otherwise use - ! expressions that are rescalable and respect rotational symmetry. ! This include declares and sets the variable "version". # include "version_variable.h" @@ -174,23 +170,11 @@ subroutine idealized_hurricane_wind_init(Time, G, US, param_file, CS) call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) - call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, & - "This sets the default value for the various _2018_ANSWERS parameters.", & - default=(default_answer_date<20190101)) - call get_param(param_file, mdl, "IDL_HURR_2018_ANSWERS", answers_2018, & - "If true, use expressions driving the idealized hurricane test case that recover "//& - "the answers from the end of 2018. Otherwise use expressions that are rescalable "//& - "and respect rotational symmetry.", default=default_2018_answers) - - ! Revise inconsistent default answer dates. - if (answers_2018 .and. (default_answer_date >= 20190101)) default_answer_date = 20181231 - if (.not.answers_2018 .and. (default_answer_date < 20190101)) default_answer_date = 20190101 call get_param(param_file, mdl, "IDL_HURR_ANSWER_DATE", CS%answer_date, & "The vintage of the expressions in the idealized hurricane test case. "//& "Values below 20190101 recover the answers from the end of 2018, while higher "//& - "values use expressions that are rescalable and respect rotational symmetry. "//& - "If both IDL_HURR_2018_ANSWERS and IDL_HURR_ANSWER_DATE are specified, "//& - "the latter takes precedence.", default=default_answer_date) + "values use expressions that are rescalable and respect rotational symmetry.", & + default=default_answer_date) ! The following parameters are model run-time parameters which are used ! and logged elsewhere and so should not be logged here. The default From 9f7f86d304d664e5a7ed63271cfb8ef32644d876 Mon Sep 17 00:00:00 2001 From: Marshall Ward Date: Tue, 29 Aug 2023 10:37:01 -0400 Subject: [PATCH 15/17] Autoconf: Update deps m4 macros This patch updates the macros used in the `ac/deps` directory. It includes a bugfix in the library testing, and also bundles the macro for testing Fortran links to C libraries. Both of these macros were already in `ac/m4` but unused by `ac/deps`. No problems were observed in any recent FMS builds, although in principle they could have been affected by these errors. The issues were only detected when the macros were used in an unrelated project (LM3 land model). These issues are due to code duplication in `ac/m4` and `ac/deps/m4`. This separation was intended to draw a clear logical separation between MOM6 and its FMS dependency, and only provide what is required by each respective build. But perhaps there are some flaws in this thinking, and may warrant further discussion. --- ac/deps/m4/ax_fc_check_c_lib.m4 | 45 +++++++++++++++++++++++++++++++++ ac/deps/m4/ax_fc_check_lib.m4 | 13 +++++----- 2 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 ac/deps/m4/ax_fc_check_c_lib.m4 diff --git a/ac/deps/m4/ax_fc_check_c_lib.m4 b/ac/deps/m4/ax_fc_check_c_lib.m4 new file mode 100644 index 0000000000..af5765282a --- /dev/null +++ b/ac/deps/m4/ax_fc_check_c_lib.m4 @@ -0,0 +1,45 @@ +dnl AX_FC_CHECK_C_LIB(LIBRARY, FUNCTION, +dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], +dnl [OTHER-LDFLAGS], [OTHER-LIBS]) +dnl +dnl This macro checks if a C library can be referenced by a Fortran compiler. +dnl +dnl Results are cached in `ax_fc_cv_c_lib_LIBRARY_FUNCTION`. +dnl +dnl NOTE: Might be possible to rewrite this to use `AX_FC_CHECK_BIND_C`. +dnl +AC_DEFUN([AX_FC_CHECK_C_LIB], [ + AS_VAR_PUSHDEF([ax_fc_C_Lib], [ax_fc_cv_c_lib_$1_$2]) + m4_ifval([$5], + [ax_fc_c_lib_msg_LDFLAGS=" with $5"], + [ax_fc_c_lib_msg_LDFLAGS=""] + ) + AC_CACHE_CHECK( + [for $2 in -l$1$ax_fc_c_lib_msg_LDFLAGS], [ax_fc_cv_c_lib_$1_$2], [ + ax_fc_check_c_lib_save_LDFLAGS=$LDFLAGS + LDFLAGS="$6 $LDFLAGS" + ax_fc_check_c_lib_save_LIBS=$LIBS + LIBS="-l$1 $7 $LIBS" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([],[dnl +dnl begin code block + interface + subroutine test() bind(c, name="$2") + end subroutine test + end interface + call test]) +dnl end code block + ], + [AS_VAR_SET([ax_fc_C_Lib], [yes])], + [AS_VAR_SET([ax_fc_C_Lib], [no])] + ) + LDFLAGS=$ax_fc_check_c_lib_save_LDFLAGS + LIBS=$ax_fc_check_c_lib_save_LIBS + ] + ) + AS_VAR_IF([ax_fc_C_Lib], [yes], + [m4_default([$3], [LIBS="-l$1 $LIBS"])], + [$4] + ) + AS_VAR_POPDEF([ax_fc_C_Lib]) +]) diff --git a/ac/deps/m4/ax_fc_check_lib.m4 b/ac/deps/m4/ax_fc_check_lib.m4 index c0accab6cd..a7f848cd60 100644 --- a/ac/deps/m4/ax_fc_check_lib.m4 +++ b/ac/deps/m4/ax_fc_check_lib.m4 @@ -18,7 +18,7 @@ dnl library with different -L flags, or perhaps other ld configurations. dnl dnl Results are cached in the ax_fc_cv_lib_LIBRARY_FUNCTION variable. dnl -AC_DEFUN([AX_FC_CHECK_LIB],[dnl +AC_DEFUN([AX_FC_CHECK_LIB],[ AS_VAR_PUSHDEF([ax_fc_Lib], [ax_fc_cv_lib_$1_$2]) m4_ifval([$6], [ax_fc_lib_msg_LDFLAGS=" with $6"], @@ -29,14 +29,15 @@ AC_DEFUN([AX_FC_CHECK_LIB],[dnl LDFLAGS="$6 $LDFLAGS" ax_fc_check_lib_save_LIBS=$LIBS LIBS="-l$1 $7 $LIBS" - AS_IF([test -n $3], + AS_IF([test -n "$3"], [ax_fc_use_mod="use $3"], [ax_fc_use_mod=""]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([], [dnl + AC_LINK_IFELSE([dnl +dnl Begin 7-column code block +AC_LANG_PROGRAM([], [dnl $ax_fc_use_mod - call $2]dnl - ) + call $2])dnl +dnl End code block ], [AS_VAR_SET([ax_fc_Lib], [yes])], [AS_VAR_SET([ax_fc_Lib], [no])] From 1577ae1c516ba72d1b0f287666527b6cfcfbbcda Mon Sep 17 00:00:00 2001 From: alex-huth Date: Wed, 23 Aug 2023 15:59:33 -0400 Subject: [PATCH 16/17] Ice-shelf bugfixes for restarts and halo updates This commit fixes a combination of bugs related to halo update errors, improperly-defined iteration bounds, and an unexpected change in ice-shelf bed_elev after a restart. These issues caused crashes during the ice-shelf velocity (SSA) solution, and ice shelf dynamics restarts were not bitwise identical. The issues were resolved with the following changes: -Added bed_elev to the ice shelf restart file (previously, bed_elev was instead calculated incorrectly after a restart). Also fixed the subsequent bed_elev pass_var call, where the halo update was failing due to an error in how an optional argument (CENTER) was passed. -Added additional pass_var/pass_vector calls upon ice shelf restarts to guarantee that all halos are properly filled. -Disabled the additional ice shelf flow solve (ice_shelf_solve_outer) after a restart, which was unnecessary and prevented bitwise identical restarts. -Fixed incorrect bounds for iterations within ice_shelf_solve_inner and for the basis functions in calc_shelf_visc This commit also sets the mol_wt argument to optional in the dummy function aof_set_coupler_flux (from ice_solo_driver/atmost_ocean_fluxes.F90), as that is how it is called from the FMS coupler. This is required for compilation in ice-shelf-only mode. --- .../ice_solo_driver/atmos_ocean_fluxes.F90 | 3 +- src/ice_shelf/MOM_ice_shelf_dynamics.F90 | 63 +++++++++++-------- src/ice_shelf/MOM_ice_shelf_initialize.F90 | 15 ++--- 3 files changed, 48 insertions(+), 33 deletions(-) diff --git a/config_src/drivers/ice_solo_driver/atmos_ocean_fluxes.F90 b/config_src/drivers/ice_solo_driver/atmos_ocean_fluxes.F90 index 5494954398..4a4ddf6da3 100644 --- a/config_src/drivers/ice_solo_driver/atmos_ocean_fluxes.F90 +++ b/config_src/drivers/ice_solo_driver/atmos_ocean_fluxes.F90 @@ -13,7 +13,7 @@ module atmos_ocean_fluxes_mod !> This subroutine duplicates an interface used by the FMS coupler, but only !! returns a value of -1. None of the arguments are used for anything. function aof_set_coupler_flux(name, flux_type, implementation, atm_tr_index, & - param, flag, ice_restart_file, ocean_restart_file, & + param, flag, mol_wt, ice_restart_file, ocean_restart_file, & units, caller, verbosity) result (coupler_index) character(len=*), intent(in) :: name !< An unused argument @@ -22,6 +22,7 @@ function aof_set_coupler_flux(name, flux_type, implementation, atm_tr_index, integer, optional, intent(in) :: atm_tr_index !< An unused argument real, dimension(:), optional, intent(in) :: param !< An unused argument logical, dimension(:), optional, intent(in) :: flag !< An unused argument + real, optional, intent(in) :: mol_wt !< An unused argument character(len=*), optional, intent(in) :: ice_restart_file !< An unused argument character(len=*), optional, intent(in) :: ocean_restart_file !< An unused argument character(len=*), optional, intent(in) :: units !< An unused argument diff --git a/src/ice_shelf/MOM_ice_shelf_dynamics.F90 b/src/ice_shelf/MOM_ice_shelf_dynamics.F90 index 9b584ae0f9..cefe251edd 100644 --- a/src/ice_shelf/MOM_ice_shelf_dynamics.F90 +++ b/src/ice_shelf/MOM_ice_shelf_dynamics.F90 @@ -265,23 +265,23 @@ subroutine register_ice_shelf_dyn_restarts(G, US, param_file, CS, restart_CS) call get_param(param_file, mdl, "MISSING_SHELF_TEMPERATURE", T_shelf_missing, & "An ice shelf temperature to use where there is no ice shelf.",& units="degC", default=-10.0, scale=US%degC_to_C, do_not_log=.true.) - allocate( CS%u_shelf(IsdB:IedB,JsdB:JedB), source=0.0 ) - allocate( CS%v_shelf(IsdB:IedB,JsdB:JedB), source=0.0 ) - allocate( CS%t_shelf(isd:ied,jsd:jed), source=T_shelf_missing ) ! [C ~> degC] - allocate( CS%ice_visc(isd:ied,jsd:jed), source=0.0 ) - allocate( CS%AGlen_visc(isd:ied,jsd:jed), source=2.261e-25 ) ! [Pa-3 s-1] - allocate( CS%basal_traction(isd:ied,jsd:jed), source=0.0 ) ! [R L2 T-2 ~> Pa] - allocate( CS%C_basal_friction(isd:ied,jsd:jed), source=5.0e10 ) ! [Pa (m-1 s)^n_sliding] - allocate( CS%OD_av(isd:ied,jsd:jed), source=0.0 ) - allocate( CS%ground_frac(isd:ied,jsd:jed), source=0.0 ) - allocate( CS%taudx_shelf(IsdB:IedB,JsdB:JedB), source=0.0 ) - allocate( CS%taudy_shelf(IsdB:IedB,JsdB:JedB), source=0.0 ) - allocate( CS%bed_elev(isd:ied,jsd:jed) ) ; CS%bed_elev(:,:) = G%bathyT(:,:) + G%Z_ref - allocate( CS%u_bdry_val(IsdB:IedB,JsdB:JedB), source=0.0 ) - allocate( CS%v_bdry_val(IsdB:IedB,JsdB:JedB), source=0.0 ) - allocate( CS%u_face_mask_bdry(IsdB:IedB,JsdB:JedB), source=-2.0 ) - allocate( CS%v_face_mask_bdry(IsdB:iedB,JsdB:JedB), source=-2.0 ) - allocate( CS%h_bdry_val(isd:ied,jsd:jed), source=0.0 ) + allocate(CS%u_shelf(IsdB:IedB,JsdB:JedB), source=0.0) + allocate(CS%v_shelf(IsdB:IedB,JsdB:JedB), source=0.0) + allocate(CS%t_shelf(isd:ied,jsd:jed), source=T_shelf_missing) ! [C ~> degC] + allocate(CS%ice_visc(isd:ied,jsd:jed), source=0.0) + allocate(CS%AGlen_visc(isd:ied,jsd:jed), source=2.261e-25) ! [Pa-3 s-1] + allocate(CS%basal_traction(isd:ied,jsd:jed), source=0.0) ! [R L2 T-2 ~> Pa] + allocate(CS%C_basal_friction(isd:ied,jsd:jed), source=5.0e10) ! [Pa (m-1 s)^n_sliding] + allocate(CS%OD_av(isd:ied,jsd:jed), source=0.0) + allocate(CS%ground_frac(isd:ied,jsd:jed), source=0.0) + allocate(CS%taudx_shelf(IsdB:IedB,JsdB:JedB), source=0.0) + allocate(CS%taudy_shelf(IsdB:IedB,JsdB:JedB), source=0.0) + allocate(CS%bed_elev(isd:ied,jsd:jed), source=0.0) + allocate(CS%u_bdry_val(IsdB:IedB,JsdB:JedB), source=0.0) + allocate(CS%v_bdry_val(IsdB:IedB,JsdB:JedB), source=0.0) + allocate(CS%u_face_mask_bdry(IsdB:IedB,JsdB:JedB), source=-2.0) + allocate(CS%v_face_mask_bdry(IsdB:iedB,JsdB:JedB), source=-2.0) + allocate(CS%h_bdry_val(isd:ied,jsd:jed), source=0.0) ! additional restarts for ice shelf state call register_restart_field(CS%u_shelf, "u_shelf", .false., restart_CS, & "ice sheet/shelf u-velocity", & @@ -310,6 +310,8 @@ subroutine register_ice_shelf_dyn_restarts(G, US, param_file, CS, restart_CS) "ice-stiffness parameter", "Pa-3 s-1") call register_restart_field(CS%h_bdry_val, "h_bdry_val", .false., restart_CS, & "ice thickness at the boundary", "m", conversion=US%Z_to_m) + call register_restart_field(CS%bed_elev, "bed elevation", .true., restart_CS, & + "bed elevation", "m", conversion=US%Z_to_m) endif end subroutine register_ice_shelf_dyn_restarts @@ -509,7 +511,15 @@ subroutine initialize_ice_shelf_dyn(param_file, Time, ISS, CS, G, US, diag, new_ call pass_var(CS%ice_visc,G%domain) call pass_var(CS%basal_traction, G%domain) call pass_var(CS%AGlen_visc, G%domain) + call pass_var(CS%bed_elev, G%domain) + call pass_var(CS%C_basal_friction, G%domain) + call pass_var(CS%h_bdry_val, G%domain) + call pass_var(CS%thickness_bdry_val, G%domain) + call pass_vector(CS%u_shelf, CS%v_shelf, G%domain, TO_ALL, BGRID_NE) + call pass_vector(CS%u_bdry_val, CS%v_bdry_val, G%domain, TO_ALL, BGRID_NE) + call pass_vector(CS%u_face_mask_bdry, CS%v_face_mask_bdry, G%domain, TO_ALL, BGRID_NE) + call update_velocity_masks(CS, G, ISS%hmask, CS%umask, CS%vmask, CS%u_face_mask, CS%v_face_mask) endif if (active_shelf_dynamics) then @@ -561,7 +571,8 @@ subroutine initialize_ice_shelf_dyn(param_file, Time, ISS, CS, G, US, diag, new_ call initialize_ice_flow_from_file(CS%bed_elev,CS%u_shelf, CS%v_shelf, CS%ground_frac, & G, US, param_file) call pass_vector(CS%u_shelf, CS%v_shelf, G%domain, TO_ALL, BGRID_NE) - call pass_var(CS%bed_elev, G%domain,CENTER) + call pass_var(CS%ground_frac, G%domain) + call pass_var(CS%bed_elev, G%domain) call update_velocity_masks(CS, G, ISS%hmask, CS%umask, CS%vmask, CS%u_face_mask, CS%v_face_mask) endif ! Register diagnostics. @@ -590,8 +601,9 @@ subroutine initialize_ice_shelf_dyn(param_file, Time, ISS, CS, G, US, diag, new_ 'intermediate ocean column thickness passed to ice model', 'm', conversion=US%Z_to_m) endif call MOM_mesg("MOM_ice_shelf.F90, initialize_ice_shelf: initialize ice velocity.") - call update_OD_ffrac_uncoupled(CS, G, ISS%h_shelf(:,:)) - call ice_shelf_solve_outer(CS, ISS, G, US, CS%u_shelf, CS%v_shelf,CS%taudx_shelf,CS%taudy_shelf, iters, Time) + if (new_sim) then + call update_OD_ffrac_uncoupled(CS, G, ISS%h_shelf(:,:)) + endif end subroutine initialize_ice_shelf_dyn @@ -955,7 +967,7 @@ subroutine ice_shelf_solve_outer(CS, ISS, G, US, u_shlf, v_shlf, taudx, taudy, i call CG_action(Au, Av, u_shlf, v_shlf, Phi, Phisub, CS%umask, CS%vmask, ISS%hmask, H_node, & CS%ice_visc, float_cond, CS%bed_elev, CS%basal_traction, & G, US, G%isc-1, G%iec+1, G%jsc-1, G%jec+1, rhoi_rhow) - call pass_vector(Au,Av,G%domain,TO_ALL,BGRID_NE) + call pass_vector(Au, Av, G%domain, TO_ALL, BGRID_NE) if (CS%nonlin_solve_err_mode == 1) then err_init = 0 ; err_tempu = 0 ; err_tempv = 0 @@ -1012,6 +1024,8 @@ subroutine ice_shelf_solve_outer(CS, ISS, G, US, u_shlf, v_shlf, taudx, taudy, i CS%ice_visc, float_cond, CS%bed_elev, CS%basal_traction, & G, US, G%isc-1, G%iec+1, G%jsc-1, G%jec+1, rhoi_rhow) + call pass_vector(Au, Av, G%domain, TO_ALL, BGRID_NE) + err_max = 0 if (CS%nonlin_solve_err_mode == 1) then @@ -1225,7 +1239,7 @@ subroutine ice_shelf_solve_inner(CS, ISS, G, US, u_shlf, v_shlf, taudx, taudy, H ! the computational domain - this is their state in the initial iteration - is = isc - cg_halo ; ie = iecq + cg_halo + is = iscq - cg_halo ; ie = iecq + cg_halo js = jscq - cg_halo ; je = jecq + cg_halo Au(:,:) = 0 ; Av(:,:) = 0 @@ -2595,10 +2609,9 @@ subroutine calc_shelf_visc(CS, ISS, G, US, u_shlf, v_shlf) is = iscq - 1; js = jscq - 1 i_off = G%idg_offset ; j_off = G%jdg_offset - allocate(Phi(1:8,1:4,isd:ied,jsd:jed), source=0.0) + allocate(Phi(1:8,1:4,isc:iec,jsc:jec), source=0.0) -! do j=jsc,jec ; do i=isc,iec - do j=jscq,jecq ; do i=iscq,iecq + do j=jsc,jec ; do i=isc,iec call bilinear_shape_fn_grid(G, i, j, Phi(:,:,i,j)) enddo ; enddo diff --git a/src/ice_shelf/MOM_ice_shelf_initialize.F90 b/src/ice_shelf/MOM_ice_shelf_initialize.F90 index e49fb03aaf..dce6e53982 100644 --- a/src/ice_shelf/MOM_ice_shelf_initialize.F90 +++ b/src/ice_shelf/MOM_ice_shelf_initialize.F90 @@ -429,31 +429,32 @@ subroutine initialize_ice_flow_from_file(bed_elev,u_shelf, v_shelf,float_cond,& filename = trim(inputdir)//trim(vel_file) call log_param(PF, mdl, "INPUTDIR/THICKNESS_FILE", filename) call get_param(PF, mdl, "ICE_U_VEL_VARNAME", ushelf_varname, & - "The name of the thickness variable in ICE_VELOCITY_FILE.", & + "The name of the u velocity variable in ICE_VELOCITY_FILE.", & default="u_shelf") call get_param(PF, mdl, "ICE_V_VEL_VARNAME", vshelf_varname, & - "The name of the thickness variable in ICE_VELOCITY_FILE.", & + "The name of the v velocity variable in ICE_VELOCITY_FILE.", & default="v_shelf") call get_param(PF, mdl, "ICE_VISC_VARNAME", ice_visc_varname, & - "The name of the thickness variable in ICE_VELOCITY_FILE.", & + "The name of the ice viscosity variable in ICE_VELOCITY_FILE.", & default="viscosity") + call get_param(PF, mdl, "ICE_FLOAT_FRAC_VARNAME", floatfr_varname, & + "The name of the ice float fraction (grounding fraction) variable in ICE_VELOCITY_FILE.", & + default="float_frac") call get_param(PF, mdl, "BED_TOPO_FILE", bed_topo_file, & "The file from which the bed elevation is read.", & default="ice_shelf_vel.nc") call get_param(PF, mdl, "BED_TOPO_VARNAME", bed_varname, & - "The name of the thickness variable in ICE_INPUT_FILE.", & + "The name of the bed elevation variable in ICE_INPUT_FILE.", & default="depth") if (.not.file_exists(filename, G%Domain)) call MOM_error(FATAL, & " initialize_ice_shelf_velocity_from_file: Unable to open "//trim(filename)) - floatfr_varname = "float_frac" - call MOM_read_data(filename, trim(ushelf_varname), u_shelf, G%Domain, position=CORNER, scale=US%m_s_to_L_T) call MOM_read_data(filename, trim(vshelf_varname), v_shelf, G%Domain, position=CORNER, scale=US%m_s_to_L_T) call MOM_read_data(filename, trim(floatfr_varname), float_cond, G%Domain, scale=1.) filename = trim(inputdir)//trim(bed_topo_file) - call MOM_read_data(filename,trim(bed_varname), bed_elev, G%Domain, scale=1.0) + call MOM_read_data(filename, trim(bed_varname), bed_elev, G%Domain, scale=US%m_to_Z) end subroutine initialize_ice_flow_from_file From 1d35fa1c2f984b94041029f6857e6dfaa410198f Mon Sep 17 00:00:00 2001 From: raphael dussin Date: Fri, 8 Sep 2023 14:02:48 -0400 Subject: [PATCH 17/17] implement restart for internal tides (#463) * implement restart for internal tides * add a call to register restart diabatic in MOM * register restart diabatic call register restart internal tides * internal tides restart uses the extra_axes optional argument * support for extra_axes added to MOM_restart/MOM_io * implement 4d restart/ parallel_restart not working * add proper documentation for extra axes * fix read restart for 4d array * passes global_file optional arg to allow reading in parallel restart files * write one restart variable per vertical mode vertical modes >= 6 do not propagate hence there is no need for dynamic array size on this dimension --------- Co-authored-by: Raphael Dussin --- src/core/MOM.F90 | 11 +- src/framework/MOM_io.F90 | 71 +++-- src/framework/MOM_restart.F90 | 80 +++++- .../lateral/MOM_internal_tides.F90 | 272 ++++++++++++++---- .../vertical/MOM_diabatic_driver.F90 | 38 ++- 5 files changed, 371 insertions(+), 101 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index ce001483ff..d112de07b7 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -62,6 +62,7 @@ module MOM use MOM_coord_initialization, only : MOM_initialize_coord, write_vertgrid_file use MOM_diabatic_driver, only : diabatic, diabatic_driver_init, diabatic_CS, extract_diabatic_member use MOM_diabatic_driver, only : adiabatic, adiabatic_driver_init, diabatic_driver_end +use MOM_diabatic_driver, only : register_diabatic_restarts use MOM_stochastics, only : stochastics_init, update_stochastics, stochastic_CS use MOM_diagnostics, only : calculate_diagnostic_fields, MOM_diagnostics_init use MOM_diagnostics, only : register_transport_diags, post_transport_diagnostics @@ -94,6 +95,7 @@ module MOM use MOM_interface_heights, only : find_eta, calc_derived_thermo, thickness_to_dz use MOM_interface_filter, only : interface_filter, interface_filter_init, interface_filter_end use MOM_interface_filter, only : interface_filter_CS +use MOM_internal_tides, only : int_tide_CS use MOM_lateral_mixing_coeffs, only : calc_slope_functions, VarMix_init, VarMix_end use MOM_lateral_mixing_coeffs, only : calc_resoln_function, calc_depth_function, VarMix_CS use MOM_MEKE, only : MEKE_alloc_register_restart, step_forward_MEKE @@ -409,6 +411,8 @@ module MOM type(ALE_sponge_CS), pointer :: ALE_sponge_CSp => NULL() !< Pointer to the oda incremental update control structure type(oda_incupd_CS), pointer :: oda_incupd_CSp => NULL() + !< Pointer to the internal tides control structure + type(int_tide_CS), pointer :: int_tide_CSp => NULL() !< Pointer to the ALE-mode sponge control structure type(ALE_CS), pointer :: ALE_CSp => NULL() !< Pointer to the Arbitrary Lagrangian Eulerian (ALE) vertical coordinate control structure @@ -1964,6 +1968,7 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & type(ice_shelf_CS), optional, pointer :: ice_shelf_CSp !< A pointer to an ice shelf control structure type(Wave_parameters_CS), & optional, pointer :: Waves_CSp !< An optional pointer to a wave property CS + ! local variables type(ocean_grid_type), pointer :: G => NULL() ! A pointer to the metric grid use for the run type(ocean_grid_type), pointer :: G_in => NULL() ! Pointer to the input grid @@ -2774,6 +2779,10 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & call stoch_EOS_register_restarts(HI, param_file, CS%stoch_eos_CS, restart_CSp) endif + if (.not. CS%adiabatic) then + call register_diabatic_restarts(G, US, param_file, CS%int_tide_CSp, restart_CSp) + endif + call callTree_waypoint("restart registration complete (initialize_MOM)") call restart_registry_lock(restart_CSp) @@ -3139,7 +3148,7 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & else call diabatic_driver_init(Time, G, GV, US, param_file, CS%use_ALE_algorithm, diag, & CS%ADp, CS%CDp, CS%diabatic_CSp, CS%tracer_flow_CSp, & - CS%sponge_CSp, CS%ALE_sponge_CSp, CS%oda_incupd_CSp) + CS%sponge_CSp, CS%ALE_sponge_CSp, CS%oda_incupd_CSp, CS%int_tide_CSp) endif if (associated(CS%sponge_CSp)) & diff --git a/src/framework/MOM_io.F90 b/src/framework/MOM_io.F90 index 220a7d6bcf..27d244b226 100644 --- a/src/framework/MOM_io.F90 +++ b/src/framework/MOM_io.F90 @@ -148,6 +148,20 @@ module MOM_io module procedure read_attribute_int32, read_attribute_int64 end interface read_attribute +!> Type that stores information that can be used to create a non-decomposed axis. +type :: axis_info + character(len=32) :: name = "" !< The name of this axis for use in files + character(len=256) :: longname = "" !< A longer name describing this axis + character(len=48) :: units = "" !< The units of the axis labels + character(len=8) :: cartesian = "N" !< A variable indicating which direction + !! this axis corresponds with. Valid values + !! include 'X', 'Y', 'Z', 'T', and 'N' for none. + integer :: sense = 0 !< This is 1 for axes whose values increase upward, or -1 + !! if they increase downward. The default, 0, is ignored. + integer :: ax_size = 0 !< The number of elements in this axis + real, allocatable, dimension(:) :: ax_data !< The values of the data on the axis [arbitrary] +end type axis_info + !> Type for describing a 3-d variable for output type, public :: vardesc character(len=64) :: name !< Variable name in a NetCDF file @@ -165,22 +179,9 @@ module MOM_io character(len=32) :: dim_names(5) !< The names in the file of the axes for this variable integer :: position = -1 !< An integer encoding the horizontal position, it may !! CENTER, CORNER, EAST_FACE, NORTH_FACE, or 0. + type(axis_info) :: extra_axes(5) !< dimensions other than space-time end type vardesc -!> Type that stores information that can be used to create a non-decomposed axis. -type :: axis_info ; private - character(len=32) :: name = "" !< The name of this axis for use in files - character(len=256) :: longname = "" !< A longer name describing this axis - character(len=48) :: units = "" !< The units of the axis labels - character(len=8) :: cartesian = "N" !< A variable indicating which direction - !! this axis corresponds with. Valid values - !! include 'X', 'Y', 'Z', 'T', and 'N' for none. - integer :: sense = 0 !< This is 1 for axes whose values increase upward, or -1 - !! if they increase downward. The default, 0, is ignored. - integer :: ax_size = 0 !< The number of elements in this axis - real, allocatable, dimension(:) :: ax_data !< The values of the data on the axis [arbitrary] -end type axis_info - !> Type that stores for a global file attribute type :: attribute_info ; private character(len=:), allocatable :: name !< The name of this attribute @@ -271,7 +272,8 @@ subroutine create_MOM_file(IO_handle, filename, vars, novars, fields, & !! required if the new file uses any !! vertical grid axes. integer(kind=int64), optional, intent(in) :: checksums(:,:) !< checksums of vars - type(axis_info), optional, intent(in) :: extra_axes(:) !< Types with information about + type(axis_info), dimension(:), & + optional, intent(in) :: extra_axes !< Types with information about !! some axes that might be used in this file type(attribute_info), optional, intent(in) :: global_atts(:) !< Global attributes to !! write to this file @@ -1751,7 +1753,8 @@ end subroutine verify_variable_units !! have default values that are empty strings or are appropriate for a 3-d !! tracer field at the tracer cell centers. function var_desc(name, units, longname, hor_grid, z_grid, t_grid, cmor_field_name, & - cmor_units, cmor_longname, conversion, caller, position, dim_names, fixed) result(vd) + cmor_units, cmor_longname, conversion, caller, position, dim_names, & + extra_axes, fixed) result(vd) character(len=*), intent(in) :: name !< variable name character(len=*), optional, intent(in) :: units !< variable units character(len=*), optional, intent(in) :: longname !< variable long name @@ -1772,6 +1775,8 @@ function var_desc(name, units, longname, hor_grid, z_grid, t_grid, cmor_field_na !! NORTH_FACE, and 0 for no horizontal dimensions. character(len=*), dimension(:), & optional, intent(in) :: dim_names !< The names of the dimensions of this variable + type(axis_info), dimension(:), & + optional, intent(in) :: extra_axes !< dimensions other than space-time logical, optional, intent(in) :: fixed !< If true, this does not evolve with time type(vardesc) :: vd !< vardesc type that is created @@ -1795,7 +1800,8 @@ function var_desc(name, units, longname, hor_grid, z_grid, t_grid, cmor_field_na call modify_vardesc(vd, units=units, longname=longname, hor_grid=hor_grid, & z_grid=z_grid, t_grid=t_grid, position=position, dim_names=dim_names, & cmor_field_name=cmor_field_name, cmor_units=cmor_units, & - cmor_longname=cmor_longname, conversion=conversion, caller=cllr) + cmor_longname=cmor_longname, conversion=conversion, caller=cllr, & + extra_axes=extra_axes) end function var_desc @@ -1803,7 +1809,8 @@ end function var_desc !> This routine modifies the named elements of a vardesc type. !! All arguments are optional, except the vardesc type to be modified. subroutine modify_vardesc(vd, name, units, longname, hor_grid, z_grid, t_grid, & - cmor_field_name, cmor_units, cmor_longname, conversion, caller, position, dim_names) + cmor_field_name, cmor_units, cmor_longname, conversion, caller, position, dim_names, & + extra_axes) type(vardesc), intent(inout) :: vd !< vardesc type that is modified character(len=*), optional, intent(in) :: name !< name of variable character(len=*), optional, intent(in) :: units !< units of variable @@ -1825,6 +1832,8 @@ subroutine modify_vardesc(vd, name, units, longname, hor_grid, z_grid, t_grid, & !! NORTH_FACE, and 0 for no horizontal dimensions. character(len=*), dimension(:), & optional, intent(in) :: dim_names !< The names of the dimensions of this variable + type(axis_info), dimension(:), & + optional, intent(in) :: extra_axes !< dimensions other than space-time character(len=120) :: cllr integer :: n @@ -1877,6 +1886,12 @@ subroutine modify_vardesc(vd, name, units, longname, hor_grid, z_grid, t_grid, & endif ; enddo endif + if (present(extra_axes)) then + do n=1,size(extra_axes) ; if (len_trim(extra_axes(n)%name) > 0) then + vd%extra_axes(n) = extra_axes(n) + endif ; enddo + endif + end subroutine modify_vardesc integer function position_from_horgrid(hor_grid) @@ -2020,7 +2035,7 @@ end function cmor_long_std !> This routine queries vardesc subroutine query_vardesc(vd, name, units, longname, hor_grid, z_grid, t_grid, & cmor_field_name, cmor_units, cmor_longname, conversion, caller, & - position, dim_names) + extra_axes, position, dim_names) type(vardesc), intent(in) :: vd !< vardesc type that is queried character(len=*), optional, intent(out) :: name !< name of variable character(len=*), optional, intent(out) :: units !< units of variable @@ -2035,6 +2050,8 @@ subroutine query_vardesc(vd, name, units, longname, hor_grid, z_grid, t_grid, & !! convert from intensive to extensive !! [various] or [a A-1 ~> 1] character(len=*), optional, intent(in) :: caller !< calling routine? + type(axis_info), dimension(5), & + optional, intent(out) :: extra_axes !< dimensions other than space-time integer, optional, intent(out) :: position !< A coded integer indicating the horizontal position !! of this variable if it has such dimensions. !! Valid values include CORNER, CENTER, EAST_FACE @@ -2043,7 +2060,8 @@ subroutine query_vardesc(vd, name, units, longname, hor_grid, z_grid, t_grid, & optional, intent(out) :: dim_names !< The names of the dimensions of this variable integer :: n - character(len=120) :: cllr + integer, parameter :: nmax_extraaxes = 5 + character(len=120) :: cllr, varname cllr = "mod_vardesc" if (present(caller)) cllr = trim(caller) @@ -2076,6 +2094,19 @@ subroutine query_vardesc(vd, name, units, longname, hor_grid, z_grid, t_grid, & enddo endif + if (present(extra_axes)) then + ! save_restart expects 5 extra axes (can be empty) + do n=1, nmax_extraaxes + if (vd%extra_axes(n)%ax_size>=1) then + extra_axes(n) = vd%extra_axes(n) + else + ! return an empty axis + write(varname,"('dummy',i1.1)") n + call set_axis_info(extra_axes(n), name=trim(varname), ax_size=1) + endif + enddo + endif + end subroutine query_vardesc diff --git a/src/framework/MOM_restart.F90 b/src/framework/MOM_restart.F90 index 75051c32ba..252f14bfac 100644 --- a/src/framework/MOM_restart.F90 +++ b/src/framework/MOM_restart.F90 @@ -14,6 +14,7 @@ module MOM_restart use MOM_io, only : vardesc, var_desc, query_vardesc, modify_vardesc, get_filename_appendix use MOM_io, only : MULTIPLE, READONLY_FILE, SINGLE_FILE use MOM_io, only : CENTER, CORNER, NORTH_FACE, EAST_FACE +use MOM_io, only : axis_info, get_axis_info use MOM_string_functions, only : lowercase use MOM_time_manager, only : time_type, time_type_to_real, real_to_time use MOM_time_manager, only : days_in_month, get_date, set_date @@ -26,6 +27,7 @@ module MOM_restart public restart_registry_lock, restart_init_end, vardesc public restart_files_exist, determine_is_new_run, is_new_run public register_restart_field_as_obsolete, register_restart_pair +public lock_check ! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional ! consistency testing. These are noted in comments with units like Z, H, L, and T, along with @@ -445,7 +447,7 @@ end subroutine register_restart_pair_ptr4d !> Register a 4-d field for restarts, providing the metadata as individual arguments subroutine register_restart_field_4d(f_ptr, name, mandatory, CS, longname, units, conversion, & - hor_grid, z_grid, t_grid) + hor_grid, z_grid, t_grid, extra_axes) real, dimension(:,:,:,:), & target, intent(in) :: f_ptr !< A pointer to the field to be read or written !! in arbitrary rescaled units [A ~> a] @@ -460,8 +462,26 @@ subroutine register_restart_field_4d(f_ptr, name, mandatory, CS, longname, units character(len=*), optional, intent(in) :: hor_grid !< variable horizontal staggering, 'h' if absent character(len=*), optional, intent(in) :: z_grid !< variable vertical staggering, 'L' if absent character(len=*), optional, intent(in) :: t_grid !< time description: s, p, or 1, 's' if absent + type(axis_info), dimension(:), & + optional, intent(in) :: extra_axes !< dimensions other than space-time type(vardesc) :: vd + character(len=32), dimension(:), allocatable :: dim_names + integer :: n, n_extradims + + ! first 2 dimensions in dim_names are reserved for i,j + ! so extra_dimensions are shifted to index 3. + ! this is designed not to break the behavior in SIS2 + ! (see register_restart_field_4d in SIS_restart.F90) + if (present(extra_axes)) then + n_extradims = size(extra_axes) + allocate(dim_names(n_extradims+2)) + dim_names(1) = "" + dim_names(2) = "" + do n=3,n_extradims+2 + dim_names(n) = extra_axes(n-2)%name + enddo + endif if (.not.CS%initialized) call MOM_error(FATAL, "MOM_restart: " // & "register_restart_field_4d: Module must be initialized before "//& @@ -469,8 +489,13 @@ subroutine register_restart_field_4d(f_ptr, name, mandatory, CS, longname, units call lock_check(CS, name=name) - vd = var_desc(name, units=units, longname=longname, hor_grid=hor_grid, & - z_grid=z_grid, t_grid=t_grid) + if (present(extra_axes)) then + vd = var_desc(name, units=units, longname=longname, hor_grid=hor_grid, & + z_grid=z_grid, t_grid=t_grid, dim_names=dim_names, extra_axes=extra_axes) + else + vd = var_desc(name, units=units, longname=longname, hor_grid=hor_grid, & + z_grid=z_grid, t_grid=t_grid) + endif call register_restart_field_ptr4d(f_ptr, vd, mandatory, CS, conversion) @@ -478,7 +503,7 @@ end subroutine register_restart_field_4d !> Register a 3-d field for restarts, providing the metadata as individual arguments subroutine register_restart_field_3d(f_ptr, name, mandatory, CS, longname, units, conversion, & - hor_grid, z_grid, t_grid) + hor_grid, z_grid, t_grid, extra_axes) real, dimension(:,:,:), & target, intent(in) :: f_ptr !< A pointer to the field to be read or written !! in arbitrary rescaled units [A ~> a] @@ -493,8 +518,26 @@ subroutine register_restart_field_3d(f_ptr, name, mandatory, CS, longname, units character(len=*), optional, intent(in) :: hor_grid !< variable horizontal staggering, 'h' if absent character(len=*), optional, intent(in) :: z_grid !< variable vertical staggering, 'L' if absent character(len=*), optional, intent(in) :: t_grid !< time description: s, p, or 1, 's' if absent + type(axis_info), dimension(:), & + optional, intent(in) :: extra_axes !< dimensions other than space-time type(vardesc) :: vd + character(len=32), dimension(:), allocatable :: dim_names + integer :: n, n_extradims + + ! first 2 dimensions in dim_names are reserved for i,j + ! so extra_dimensions are shifted to index 3. + ! this is designed not to break the behavior in SIS2 + ! (see register_restart_field_4d in SIS_restart.F90) + if (present(extra_axes)) then + n_extradims = size(extra_axes) + allocate(dim_names(n_extradims+2)) + dim_names(1) = "" + dim_names(2) = "" + do n=3,n_extradims+2 + dim_names(n) = extra_axes(n-2)%name + enddo + endif if (.not.CS%initialized) call MOM_error(FATAL, "MOM_restart: " // & "register_restart_field_3d: Module must be initialized before "//& @@ -502,8 +545,13 @@ subroutine register_restart_field_3d(f_ptr, name, mandatory, CS, longname, units call lock_check(CS, name=name) - vd = var_desc(name, units=units, longname=longname, hor_grid=hor_grid, & - z_grid=z_grid, t_grid=t_grid) + if (present(extra_axes)) then + vd = var_desc(name, units=units, longname=longname, hor_grid=hor_grid, & + z_grid=z_grid, t_grid=t_grid, dim_names=dim_names, extra_axes=extra_axes) + else + vd = var_desc(name, units=units, longname=longname, hor_grid=hor_grid, & + z_grid=z_grid, t_grid=t_grid) + endif call register_restart_field_ptr3d(f_ptr, vd, mandatory, CS, conversion) @@ -1309,7 +1357,7 @@ subroutine save_restart(directory, time, G, CS, time_stamped, filename, GV, num_ integer :: start_var, next_var ! The starting variables of the ! current and next files. type(MOM_infra_file) :: IO_handle ! The I/O handle of the open fileset - integer :: m, nz + integer :: m, nz, na integer :: num_files ! The number of restart files that will be used. integer :: seconds, days, year, month, hour, minute character(len=8) :: hor_grid, z_grid, t_grid ! Variable grid info. @@ -1320,9 +1368,13 @@ subroutine save_restart(directory, time, G, CS, time_stamped, filename, GV, num_ integer(kind=8) :: check_val(CS%max_fields,1) integer :: isL, ieL, jsL, jeL, pos integer :: turns + integer, parameter :: nmax_extradims = 5 + type(axis_info), dimension(:), allocatable :: extra_axes turns = CS%turns + allocate (extra_axes(nmax_extradims)) + if (.not.CS%initialized) call MOM_error(FATAL, "MOM_restart " // & "save_restart: Module must be initialized before it is used.") @@ -1361,8 +1413,14 @@ subroutine save_restart(directory, time, G, CS, time_stamped, filename, GV, num_ do m=start_var,CS%novars call query_vardesc(CS%restart_field(m)%vars, hor_grid=hor_grid, & - z_grid=z_grid, t_grid=t_grid, caller="save_restart") + z_grid=z_grid, t_grid=t_grid, caller="save_restart", & + extra_axes=extra_axes) + var_sz = get_variable_byte_size(hor_grid, z_grid, t_grid, G, nz) + ! factor in size of extra axes, or multiply by 1 + do na=1,nmax_extradims + var_sz = var_sz*extra_axes(na)%ax_size + enddo if ((m==start_var) .OR. (size_in_file < max_file_size-var_sz)) then size_in_file = size_in_file + var_sz @@ -1445,10 +1503,10 @@ subroutine save_restart(directory, time, G, CS, time_stamped, filename, GV, num_ if (CS%parallel_restartfiles) then call create_MOM_file(IO_handle, trim(restartpath), vars, next_var-start_var, & - fields, MULTIPLE, G=G, GV=GV, checksums=check_val) + fields, MULTIPLE, G=G, GV=GV, checksums=check_val, extra_axes=extra_axes) else call create_MOM_file(IO_handle, trim(restartpath), vars, next_var-start_var, & - fields, SINGLE_FILE, G=G, GV=GV, checksums=check_val) + fields, SINGLE_FILE, G=G, GV=GV, checksums=check_val, extra_axes=extra_axes) endif do m=start_var,next_var-1 @@ -1650,7 +1708,7 @@ subroutine restore_state(filename, directory, day, G, CS) elseif (associated(CS%var_ptr4d(m)%p)) then ! Read a 4d array. if (pos /= 0) then call MOM_read_data(unit_path(n), varname, CS%var_ptr4d(m)%p, & - G%Domain, timelevel=1, position=pos, scale=scale) + G%Domain, timelevel=1, position=pos, scale=scale, global_file=unit_is_global(n)) else ! This array is not domain-decomposed. This variant may be under-tested. call MOM_error(FATAL, & "MOM_restart does not support 4-d arrays without domain decomposition.") diff --git a/src/parameterizations/lateral/MOM_internal_tides.F90 b/src/parameterizations/lateral/MOM_internal_tides.F90 index 788e922ff2..83910e6690 100644 --- a/src/parameterizations/lateral/MOM_internal_tides.F90 +++ b/src/parameterizations/lateral/MOM_internal_tides.F90 @@ -16,8 +16,10 @@ module MOM_internal_tides use MOM_error_handler, only : MOM_error, FATAL, WARNING, MOM_mesg, is_root_pe use MOM_file_parser, only : read_param, get_param, log_param, log_version, param_file_type use MOM_grid, only : ocean_grid_type -use MOM_io, only : slasher, MOM_read_data, file_exists +use MOM_io, only : slasher, MOM_read_data, file_exists, axis_info +use MOM_io, only : set_axis_info, get_axis_info use MOM_restart, only : register_restart_field, MOM_restart_CS, restart_init, save_restart +use MOM_restart, only : lock_check, restart_registry_lock use MOM_spatial_means, only : global_area_integral use MOM_time_manager, only : time_type, time_type_to_real, operator(+), operator(/), operator(-) use MOM_unit_scaling, only : unit_scale_type @@ -29,12 +31,13 @@ module MOM_internal_tides #include -public propagate_int_tide !, register_int_tide_restarts +public propagate_int_tide, register_int_tide_restarts public internal_tides_init, internal_tides_end public get_lowmode_loss !> This control structure has parameters for the MOM_internal_tides module type, public :: int_tide_CS ; private + logical :: initialized = .false. !< True if this control structure has been initialized. logical :: do_int_tides !< If true, use the internal tide code. integer :: nFreq = 0 !< The number of internal tide frequency bands integer :: nMode = 1 !< The number of internal tide vertical modes @@ -137,8 +140,17 @@ module MOM_internal_tides real, allocatable :: En(:,:,:,:,:) !< The internal wave energy density as a function of (i,j,angle,frequency,mode) !! integrated within an angular and frequency band [R Z3 T-2 ~> J m-2] - real, allocatable :: En_restart(:,:,:) - !< The internal wave energy density as a function of (i,j,angle); temporary for restart + real, allocatable :: En_restart_mode1(:,:,:,:) + !< The internal wave energy density as a function of (i,j,angle,freq) for mode 1 + real, allocatable :: En_restart_mode2(:,:,:,:) + !< The internal wave energy density as a function of (i,j,angle,freq) for mode 2 + real, allocatable :: En_restart_mode3(:,:,:,:) + !< The internal wave energy density as a function of (i,j,angle,freq) for mode 3 + real, allocatable :: En_restart_mode4(:,:,:,:) + !< The internal wave energy density as a function of (i,j,angle,freq) for mode 4 + real, allocatable :: En_restart_mode5(:,:,:,:) + !< The internal wave energy density as a function of (i,j,angle,freq) for mode 5 + real, allocatable, dimension(:) :: frequency !< The frequency of each band [T-1 ~> s-1]. type(wave_speed_CS) :: wave_speed !< Wave speed control structure @@ -266,6 +278,35 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, cn(:,:,:) = 0. + ! Rebuild energy density array from multiple restarts + do fr=1,CS%nFreq ; do a=1,CS%nAngle ; do j=jsd,jed ; do i=isd,ied + CS%En(i,j,a,fr,1) = CS%En_restart_mode1(i,j,a,fr) + enddo ; enddo ; enddo ; enddo + + if (CS%nMode >= 2) then + do fr=1,CS%nFreq ; do a=1,CS%nAngle ; do j=jsd,jed ; do i=isd,ied + CS%En(i,j,a,fr,2) = CS%En_restart_mode2(i,j,a,fr) + enddo ; enddo ; enddo ; enddo + endif + + if (CS%nMode >= 3) then + do fr=1,CS%nFreq ; do a=1,CS%nAngle ; do j=jsd,jed ; do i=isd,ied + CS%En(i,j,a,fr,3) = CS%En_restart_mode3(i,j,a,fr) + enddo ; enddo ; enddo ; enddo + endif + + if (CS%nMode >= 4) then + do fr=1,CS%nFreq ; do a=1,CS%nAngle ; do j=jsd,jed ; do i=isd,ied + CS%En(i,j,a,fr,4) = CS%En_restart_mode4(i,j,a,fr) + enddo ; enddo ; enddo ; enddo + endif + + if (CS%nMode >= 5) then + do fr=1,CS%nFreq ; do a=1,CS%nAngle ; do j=jsd,jed ; do i=isd,ied + CS%En(i,j,a,fr,5) = CS%En_restart_mode5(i,j,a,fr) + enddo ; enddo ; enddo ; enddo + endif + ! Set properties related to the internal tides, such as the wave speeds, storing some ! of them in the control structure for this module. if (CS%uniform_test_cg > 0.0) then @@ -323,7 +364,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, enddo ; enddo ! Check for En<0 - for debugging, delete later - do m=1,CS%NMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle + do m=1,CS%nMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle do j=js,je ; do i=is,ie if (CS%En(i,j,a,fr,m)<0.0) then id_g = i + G%idg_offset ; jd_g = j + G%jdg_offset ! for debugging @@ -344,7 +385,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, call correct_halo_rotation(CS%En, test, G, CS%nAngle) ! Propagate the waves. - do m=1,CS%NMode ; do fr=1,CS%Nfreq + do m=1,CS%nMode ; do fr=1,CS%Nfreq CS%TKE_residual_loss(:,:,:,fr,m) = 0. @@ -353,7 +394,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, enddo ; enddo ! Check for En<0 - for debugging, delete later - do m=1,CS%NMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle + do m=1,CS%nMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle do j=js,je ; do i=is,ie if (CS%En(i,j,a,fr,m)<0.0) then id_g = i + G%idg_offset ; jd_g = j + G%jdg_offset @@ -369,13 +410,13 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, enddo ; enddo ; enddo ! Apply the other half of the refraction. - do m=1,CS%NMode ; do fr=1,CS%Nfreq + do m=1,CS%nMode ; do fr=1,CS%Nfreq call refract(CS%En(:,:,:,fr,m), cn(:,:,m), CS%frequency(fr), 0.5*dt, & G, US, CS%NAngle, CS%use_PPMang) enddo ; enddo ! Check for En<0 - for debugging, delete later - do m=1,CS%NMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle + do m=1,CS%nMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle do j=js,je ; do i=is,ie if (CS%En(i,j,a,fr,m)<0.0) then id_g = i + G%idg_offset ; jd_g = j + G%jdg_offset ! for debugging @@ -394,7 +435,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, .or. (CS%id_tot_En > 0)) then tot_En(:,:) = 0.0 tot_En_mode(:,:,:,:) = 0.0 - do m=1,CS%NMode ; do fr=1,CS%Nfreq + do m=1,CS%nMode ; do fr=1,CS%Nfreq do j=jsd,jed ; do i=isd,ied ; do a=1,CS%nAngle tot_En(i,j) = tot_En(i,j) + CS%En(i,j,a,fr,m) tot_En_mode(i,j,fr,m) = tot_En_mode(i,j,fr,m) + CS%En(i,j,a,fr,m) @@ -412,7 +453,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, enddo ; enddo ; enddo ; enddo ; enddo endif ! Check for En<0 - for debugging, delete later - do m=1,CS%NMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle + do m=1,CS%nMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle do j=js,je ; do i=is,ie if (CS%En(i,j,a,fr,m)<0.0) then id_g = i + G%idg_offset ; jd_g = j + G%jdg_offset ! for debugging @@ -453,7 +494,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, enddo ; enddo ; enddo ; enddo ; enddo endif ! Check for En<0 - for debugging, delete later - do m=1,CS%NMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle + do m=1,CS%nMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle do j=js,je ; do i=is,ie if (CS%En(i,j,a,fr,m)<0.0) then id_g = i + G%idg_offset ; jd_g = j + G%jdg_offset ! for debugging @@ -471,7 +512,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, ! still need to allow a portion of the extracted energy to go to higher modes. ! First, find velocity profiles if (CS%apply_wave_drag .or. CS%apply_Froude_drag) then - do m=1,CS%NMode ; do fr=1,CS%Nfreq + do m=1,CS%nMode ; do fr=1,CS%Nfreq ! compute near-bottom and max horizontal baroclinic velocity values at each point do j=jsd,jed ; do i=isd,ied @@ -519,7 +560,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, CS%TKE_itidal_loss, dt, full_halos=.false.) endif ! Check for En<0 - for debugging, delete later - do m=1,CS%NMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle + do m=1,CS%nMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle do j=js,je ; do i=is,ie if (CS%En(i,j,a,fr,m)<0.0) then id_g = i + G%idg_offset ; jd_g = j + G%jdg_offset ! for debugging @@ -535,7 +576,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, ! Extract the energy for mixing due to wave breaking----------------------------- if (CS%apply_Froude_drag) then ! Pick out maximum baroclinic velocity values; calculate Fr=max(u)/cg - do m=1,CS%NMode ; do fr=1,CS%Nfreq + do m=1,CS%nMode ; do fr=1,CS%Nfreq freq2 = CS%frequency(fr)**2 do j=js,je ; do i=is,ie id_g = i + G%idg_offset ; jd_g = j + G%jdg_offset ! for debugging @@ -586,7 +627,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, enddo ; enddo endif ! Check for En<0 - for debugging, delete later - do m=1,CS%NMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle + do m=1,CS%nMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle do j=js,je ; do i=is,ie if (CS%En(i,j,a,fr,m)<0.0) then id_g = i + G%idg_offset ; jd_g = j + G%jdg_offset @@ -618,7 +659,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, ! Check for energy conservation on computational domain.************************* - do m=1,CS%NMode ; do fr=1,CS%Nfreq + do m=1,CS%nMode ; do fr=1,CS%Nfreq call sum_En(G, US, CS, CS%En(:,:,:,fr,m), 'prop_int_tide') enddo ; enddo @@ -638,7 +679,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, TKE_itidal_input, CS%diag) ! Output 2-D energy density (summed over angles) for each frequency and mode - do m=1,CS%NMode ; do fr=1,CS%Nfreq ; if (CS%id_En_mode(fr,m) > 0) then + do m=1,CS%nMode ; do fr=1,CS%Nfreq ; if (CS%id_En_mode(fr,m) > 0) then tot_En(:,:) = 0.0 do a=1,CS%nAngle ; do j=js,je ; do i=is,ie tot_En(i,j) = tot_En(i,j) + CS%En(i,j,a,fr,m) @@ -646,8 +687,37 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, call post_data(CS%id_En_mode(fr,m), tot_En, CS%diag) endif ; enddo ; enddo + ! split energy array into multiple restarts + do fr=1,CS%Nfreq ; do a=1,CS%nAngle ; do j=jsd,jed ; do i=isd,ied + CS%En_restart_mode1(i,j,a,fr) = CS%En(i,j,a,fr,1) + enddo ; enddo ; enddo ; enddo + + if (CS%nMode >= 2) then + do fr=1,CS%Nfreq ; do a=1,CS%nAngle ; do j=jsd,jed ; do i=isd,ied + CS%En_restart_mode2(i,j,a,fr) = CS%En(i,j,a,fr,2) + enddo ; enddo ; enddo ; enddo + endif + + if (CS%nMode >= 3) then + do fr=1,CS%Nfreq ; do a=1,CS%nAngle ; do j=jsd,jed ; do i=isd,ied + CS%En_restart_mode3(i,j,a,fr) = CS%En(i,j,a,fr,3) + enddo ; enddo ; enddo ; enddo + endif + + if (CS%nMode >= 4) then + do fr=1,CS%Nfreq ; do a=1,CS%nAngle ; do j=jsd,jed ; do i=isd,ied + CS%En_restart_mode4(i,j,a,fr) = CS%En(i,j,a,fr,4) + enddo ; enddo ; enddo ; enddo + endif + + if (CS%nMode >= 5) then + do fr=1,CS%Nfreq ; do a=1,CS%nAngle ; do j=jsd,jed ; do i=isd,ied + CS%En_restart_mode5(i,j,a,fr) = CS%En(i,j,a,fr,5) + enddo ; enddo ; enddo ; enddo + endif + ! Output 3-D (i,j,a) energy density for each frequency and mode - do m=1,CS%NMode ; do fr=1,CS%Nfreq ; if (CS%id_En_ang_mode(fr,m) > 0) then + do m=1,CS%nMode ; do fr=1,CS%Nfreq ; if (CS%id_En_ang_mode(fr,m) > 0) then call post_data(CS%id_En_ang_mode(fr,m), CS%En(:,:,:,fr,m) , CS%diag) endif ; enddo ; enddo @@ -658,7 +728,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, tot_Froude_loss(:,:) = 0.0 tot_residual_loss(:,:) = 0.0 tot_allprocesses_loss(:,:) = 0.0 - do m=1,CS%NMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle ; do j=js,je ; do i=is,ie + do m=1,CS%nMode ; do fr=1,CS%Nfreq ; do a=1,CS%nAngle ; do j=js,je ; do i=is,ie tot_leak_loss(i,j) = tot_leak_loss(i,j) + CS%TKE_leak_loss(i,j,a,fr,m) tot_quad_loss(i,j) = tot_quad_loss(i,j) + CS%TKE_quad_loss(i,j,a,fr,m) tot_itidal_loss(i,j) = tot_itidal_loss(i,j) + CS%TKE_itidal_loss(i,j,a,fr,m) @@ -696,7 +766,7 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, endif ! Output 2-D energy loss (summed over angles) for each frequency and mode - do m=1,CS%NMode ; do fr=1,CS%Nfreq + do m=1,CS%nMode ; do fr=1,CS%Nfreq if (CS%id_itidal_loss_mode(fr,m) > 0 .or. CS%id_allprocesses_loss_mode(fr,m) > 0) then itidal_loss_mode(:,:) = 0.0 ! wave-drag processes (could do others as well) allprocesses_loss_mode(:,:) = 0.0 ! all processes summed together @@ -712,37 +782,37 @@ subroutine propagate_int_tide(h, tv, TKE_itidal_input, vel_btTide, Nb, Rho_bot, endif ; enddo ; enddo ! Output 3-D (i,j,a) energy loss for each frequency and mode - do m=1,CS%NMode ; do fr=1,CS%Nfreq ; if (CS%id_itidal_loss_ang_mode(fr,m) > 0) then + do m=1,CS%nMode ; do fr=1,CS%Nfreq ; if (CS%id_itidal_loss_ang_mode(fr,m) > 0) then call post_data(CS%id_itidal_loss_ang_mode(fr,m), CS%TKE_itidal_loss(:,:,:,fr,m) , CS%diag) endif ; enddo ; enddo ! Output 2-D period-averaged horizontal near-bottom mode velocity for each frequency and mode - do m=1,CS%NMode ; do fr=1,CS%Nfreq ; if (CS%id_Ub_mode(fr,m) > 0) then + do m=1,CS%nMode ; do fr=1,CS%Nfreq ; if (CS%id_Ub_mode(fr,m) > 0) then call post_data(CS%id_Ub_mode(fr,m), Ub(:,:,fr,m), CS%diag) endif ; enddo ; enddo - do m=1,CS%NMode ; if (CS%id_Ustruct_mode(m) > 0) then + do m=1,CS%nMode ; if (CS%id_Ustruct_mode(m) > 0) then call post_data(CS%id_Ustruct_mode(m), CS%u_struct(:,:,:,m), CS%diag) endif ; enddo - do m=1,CS%NMode ; if (CS%id_Wstruct_mode(m) > 0) then + do m=1,CS%nMode ; if (CS%id_Wstruct_mode(m) > 0) then call post_data(CS%id_Wstruct_mode(m), CS%w_struct(:,:,:,m), CS%diag) endif ; enddo - do m=1,CS%NMode ; if (CS%id_int_w2_mode(m) > 0) then + do m=1,CS%nMode ; if (CS%id_int_w2_mode(m) > 0) then call post_data(CS%id_int_w2_mode(m), CS%int_w2(:,:,m), CS%diag) endif ; enddo - do m=1,CS%NMode ; if (CS%id_int_U2_mode(m) > 0) then + do m=1,CS%nMode ; if (CS%id_int_U2_mode(m) > 0) then call post_data(CS%id_int_U2_mode(m), CS%int_U2(:,:,m), CS%diag) endif ; enddo - do m=1,CS%NMode ; if (CS%id_int_N2w2_mode(m) > 0) then + do m=1,CS%nMode ; if (CS%id_int_N2w2_mode(m) > 0) then call post_data(CS%id_int_N2w2_mode(m), CS%int_N2w2(:,:,m), CS%diag) endif ; enddo ! Output 2-D horizontal phase velocity for each frequency and mode - do m=1,CS%NMode ; do fr=1,CS%Nfreq ; if (CS%id_cp_mode(fr,m) > 0) then + do m=1,CS%nMode ; do fr=1,CS%Nfreq ; if (CS%id_cp_mode(fr,m) > 0) then call post_data(CS%id_cp_mode(fr,m), CS%cp(:,:,fr,m), CS%diag) endif ; enddo ; enddo @@ -2276,43 +2346,120 @@ subroutine PPM_limit_pos(h_in, h_L, h_R, h_min, G, iis, iie, jis, jie) enddo ; enddo end subroutine PPM_limit_pos -! subroutine register_int_tide_restarts(G, param_file, CS, restart_CS) -! type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure -! type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time parameters -! type(int_tide_CS), intent(in) :: CS !< Internal tide control structure -! type(MOM_restart_CS), intent(inout) :: restart_CS !< MOM restart control structure +subroutine register_int_tide_restarts(G, US, param_file, CS, restart_CS) + type(ocean_grid_type), intent(in) :: G !< The ocean's 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(int_tide_CS), pointer :: CS !< Internal tide control structure + type(MOM_restart_CS), pointer :: restart_CS !< MOM restart control structure + + ! This subroutine is used to allocate and register any fields in this module + ! that should be written to or read from the restart file. + logical :: use_int_tides + integer :: num_freq, num_angle , num_mode, period_1 + integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, i, j, a, fr + character(64) :: var_name, cfr + + type(axis_info) :: axes_inttides(2) + real, dimension(:), allocatable :: angles, freqs + + isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed + + if (associated(CS)) then + call MOM_error(WARNING, "register_int_tide_restarts called "//& + "with an associated control structure.") + return + endif + + allocate(CS) + + ! write extra axes + call get_param(param_file, "MOM", "INTERNAL_TIDE_ANGLES", num_angle, default=24) + call get_param(param_file, "MOM", "INTERNAL_TIDE_FREQS", num_freq, default=1) + call get_param(param_file, "MOM", "INTERNAL_TIDE_MODES", num_mode, default=1) + + allocate (angles(num_angle)) + allocate (freqs(num_freq)) + + do a=1,num_angle ; angles(a)= a ; enddo + do fr=1,num_freq ; freqs(fr)= fr ; enddo + + call set_axis_info(axes_inttides(1), "angle", "", "angle direction", num_angle, angles, "N", 1) + call set_axis_info(axes_inttides(2), "freq", "", "wave frequency", num_freq, freqs, "N", 1) + + ! full energy array + allocate(CS%En(isd:ied, jsd:jed, num_angle, num_freq, num_mode), source=0.0) + + ! restart strategy: support for 5d restart is not yet available so we split into + ! 4d restarts. Vertical modes >= 6 are dissipated locally and do not propagate + ! so we only allow for 5 vertical modes and each has its own variable + + ! allocate restart arrays + allocate(CS%En_restart_mode1(isd:ied, jsd:jed, num_angle, num_freq), source=0.0) + if (num_mode >= 2) allocate(CS%En_restart_mode2(isd:ied, jsd:jed, num_angle, num_freq), source=0.0) + if (num_mode >= 3) allocate(CS%En_restart_mode3(isd:ied, jsd:jed, num_angle, num_freq), source=0.0) + if (num_mode >= 4) allocate(CS%En_restart_mode4(isd:ied, jsd:jed, num_angle, num_freq), source=0.0) + if (num_mode >= 5) allocate(CS%En_restart_mode5(isd:ied, jsd:jed, num_angle, num_freq), source=0.0) + + ! register all 4d restarts and copy into full Energy array when restarting from previous state + call register_restart_field(CS%En_restart_mode1(:,:,:,:), "IW_energy_mode1", .false., restart_CS, & + longname="The internal wave energy density f(i,j,angle,freq) for mode 1", & + units="J m-2", conversion=US%RZ3_T3_to_W_m2*US%T_to_s, z_grid='1', t_grid="s", & + extra_axes=axes_inttides) + + do fr=1,num_freq ; do a=1,num_angle ; do j=jsd,jed ; do i=isd,ied + CS%En(i,j,a,fr,1) = CS%En_restart_mode1(i,j,a,fr) + enddo ; enddo ; enddo ; enddo + + if (num_mode >= 2) then + call register_restart_field(CS%En_restart_mode2(:,:,:,:), "IW_energy_mode2", .false., restart_CS, & + longname="The internal wave energy density f(i,j,angle,freq) for mode 2", & + units="J m-2", conversion=US%RZ3_T3_to_W_m2*US%T_to_s, z_grid='1', t_grid="s", & + extra_axes=axes_inttides) + + do fr=1,num_freq ; do a=1,num_angle ; do j=jsd,jed ; do i=isd,ied + CS%En(i,j,a,fr,2) = CS%En_restart_mode2(i,j,a,fr) + enddo ; enddo ; enddo ; enddo + + endif + + if (num_mode >= 3) then + call register_restart_field(CS%En_restart_mode3(:,:,:,:), "IW_energy_mode3", .false., restart_CS, & + longname="The internal wave energy density f(i,j,angle,freq) for mode 3", & + units="J m-2", conversion=US%RZ3_T3_to_W_m2*US%T_to_s, z_grid='1', t_grid="s", & + extra_axes=axes_inttides) -! ! This subroutine is not currently in use!! + do fr=1,num_freq ; do a=1,num_angle ; do j=jsd,jed ; do i=isd,ied + CS%En(i,j,a,fr,3) = CS%En_restart_mode3(i,j,a,fr) + enddo ; enddo ; enddo ; enddo + + endif -! ! This subroutine is used to allocate and register any fields in this module -! ! that should be written to or read from the restart file. -! logical :: use_int_tides -! integer :: num_freq, num_angle , num_mode, period_1 -! integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, a -! isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed -! IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB + if (num_mode >= 4) then + call register_restart_field(CS%En_restart_mode4(:,:,:,:), "IW_energy_mode4", .false., restart_CS, & + longname="The internal wave energy density f(i,j,angle,freq) for mode 4", & + units="J m-2", conversion=US%RZ3_T3_to_W_m2*US%T_to_s, z_grid='1', t_grid="s", & + extra_axes=axes_inttides) -! if (associated(CS)) then -! call MOM_error(WARNING, "register_int_tide_restarts called "//& -! "with an associated control structure.") -! return -! endif + do fr=1,num_freq ; do a=1,num_angle ; do j=jsd,jed ; do i=isd,ied + CS%En(i,j,a,fr,4) = CS%En_restart_mode4(i,j,a,fr) + enddo ; enddo ; enddo ; enddo -! use_int_tides = .false. -! call read_param(param_file, "INTERNAL_TIDES", use_int_tides) -! if (.not.use_int_tides) return + endif -! allocate(CS) + if (num_mode >= 5) then + call register_restart_field(CS%En_restart_mode5(:,:,:,:), "IW_energy_mode5", .false., restart_CS, & + longname="The internal wave energy density f(i,j,angle,freq) for mode 5", & + units="J m-2", conversion=US%RZ3_T3_to_W_m2*US%T_to_s, z_grid='1', t_grid="s", & + extra_axes=axes_inttides) -! num_angle = 24 -! call read_param(param_file, "INTERNAL_TIDE_ANGLES", num_angle) -! allocate(CS%En_restart(isd:ied, jsd:jed, num_angle), source=0.0) + do fr=1,num_freq ; do a=1,num_angle ; do j=jsd,jed ; do i=isd,ied + CS%En(i,j,a,fr,5) = CS%En_restart_mode5(i,j,a,fr) + enddo ; enddo ; enddo ; enddo -! call register_restart_field(CS%En_restart, "En_restart", .false., restart_CS, & -! longname="The internal wave energy density as a function of (i,j,angle,frequency,mode)", & -! units="J m-2", conversion=US%RZ3_T3_to_W_m2*US%T_to_s, z_grid='1') + endif -! end subroutine register_int_tide_restarts +end subroutine register_int_tide_restarts !> This subroutine initializes the internal tides module. subroutine internal_tides_init(Time, G, GV, US, param_file, diag, CS) @@ -2324,7 +2471,7 @@ subroutine internal_tides_init(Time, G, GV, US, param_file, diag, CS) !! parameters. type(diag_ctrl), target, intent(in) :: diag !< A structure that is used to regulate !! diagnostic output. - type(int_tide_CS), intent(inout) :: CS !< Internal tide control structure + type(int_tide_CS), pointer :: CS !< Internal tide control structure ! Local variables real :: Angle_size ! size of wedges [rad] @@ -2358,6 +2505,8 @@ subroutine internal_tides_init(Time, G, GV, US, param_file, diag, CS) isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed nz = GV%ke + CS%initialized = .true. + use_int_tides = .false. call read_param(param_file, "INTERNAL_TIDES", use_int_tides) CS%do_int_tides = use_int_tides @@ -2376,9 +2525,6 @@ subroutine internal_tides_init(Time, G, GV, US, param_file, diag, CS) if (.not.((num_freq > 0) .and. (num_angle > 0) .and. (num_mode > 0))) return CS%nFreq = num_freq ; CS%nAngle = num_angle ; CS%nMode = num_mode - ! Allocate energy density array - allocate(CS%En(isd:ied, jsd:jed, num_angle, num_freq, num_mode), source=0.0) - ! Allocate phase speed array allocate(CS%cp(isd:ied, jsd:jed, num_freq, num_mode), source=0.0) @@ -2430,7 +2576,7 @@ subroutine internal_tides_init(Time, G, GV, US, param_file, diag, CS) "Inconsistent number of frequencies.") if (CS%NAngle /= num_angle) call MOM_error(FATAL, "Internal_tides_init: "//& "Inconsistent number of angles.") - if (CS%NMode /= num_mode) call MOM_error(FATAL, "Internal_tides_init: "//& + if (CS%nMode /= num_mode) call MOM_error(FATAL, "Internal_tides_init: "//& "Inconsistent number of modes.") if (4*(num_angle/4) /= num_angle) call MOM_error(FATAL, & "Internal_tides_init: INTERNAL_TIDE_ANGLES must be a multiple of 4.") diff --git a/src/parameterizations/vertical/MOM_diabatic_driver.F90 b/src/parameterizations/vertical/MOM_diabatic_driver.F90 index b2b8527819..1ccd6a7fb2 100644 --- a/src/parameterizations/vertical/MOM_diabatic_driver.F90 +++ b/src/parameterizations/vertical/MOM_diabatic_driver.F90 @@ -45,7 +45,7 @@ module MOM_diabatic_driver use MOM_int_tide_input, only : set_int_tide_input, int_tide_input_init use MOM_int_tide_input, only : int_tide_input_end, int_tide_input_CS, int_tide_input_type use MOM_interface_heights, only : find_eta, calc_derived_thermo, thickness_to_dz -use MOM_internal_tides, only : propagate_int_tide +use MOM_internal_tides, only : propagate_int_tide, register_int_tide_restarts use MOM_internal_tides, only : internal_tides_init, internal_tides_end, int_tide_CS use MOM_kappa_shear, only : kappa_shear_is_used use MOM_CVMix_KPP, only : KPP_CS, KPP_init, KPP_compute_BLD, KPP_calculate @@ -56,6 +56,7 @@ module MOM_diabatic_driver use MOM_opacity, only : absorbRemainingSW, optics_type, optics_nbands use MOM_open_boundary, only : ocean_OBC_type use MOM_regularize_layers, only : regularize_layers, regularize_layers_init, regularize_layers_CS +use MOM_restart, only : MOM_restart_CS use MOM_set_diffusivity, only : set_diffusivity, set_BBL_TKE use MOM_set_diffusivity, only : set_diffusivity_init, set_diffusivity_end use MOM_set_diffusivity, only : set_diffusivity_CS @@ -81,6 +82,7 @@ module MOM_diabatic_driver public extract_diabatic_member public adiabatic public adiabatic_driver_init +public register_diabatic_restarts ! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional ! consistency testing. These are noted in comments with units like Z, H, L, and T, along with @@ -226,12 +228,13 @@ module MOM_diabatic_driver type(KPP_CS), pointer :: KPP_CSp => NULL() !< Control structure for a child module type(diapyc_energy_req_CS), pointer :: diapyc_en_rec_CSp => NULL() !< Control structure for a child module type(oda_incupd_CS), pointer :: oda_incupd_CSp => NULL() !< Control structure for a child module + type(int_tide_CS), pointer :: int_tide_CSp => NULL() !< Control structure for a child module + type(bulkmixedlayer_CS) :: bulkmixedlayer !< Bulk mixed layer control structure type(CVMix_conv_CS) :: CVMix_conv !< CVMix convection control structure type(energetic_PBL_CS) :: ePBL !< Energetic PBL control structure type(entrain_diffusive_CS) :: entrain_diffusive !< Diffusive entrainment control structure type(geothermal_CS) :: geothermal !< Geothermal control structure - type(int_tide_CS) :: int_tide !< Internal tide control structure type(opacity_CS) :: opacity !< Opacity control structure type(regularize_layers_CS) :: regularize_layers !< Regularize layer control structure @@ -386,7 +389,7 @@ subroutine diabatic(u, v, h, tv, Hml, fluxes, visc, ADp, CDp, dt, Time_end, & CS%int_tide_input_CSp) call propagate_int_tide(h, tv, CS%int_tide_input%TKE_itidal_input, CS%int_tide_input%tideamp, & - CS%int_tide_input%Nb, CS%int_tide_input%Rho_bot, dt, G, GV, US, CS%int_tide) + CS%int_tide_input%Nb, CS%int_tide_input%Rho_bot, dt, G, GV, US, CS%int_tide_CSp) if (showCallTree) call callTree_waypoint("done with propagate_int_tide (diabatic)") endif ! end CS%use_int_tides @@ -2980,7 +2983,7 @@ end subroutine adiabatic_driver_init !> This routine initializes the diabatic driver module. subroutine diabatic_driver_init(Time, G, GV, US, param_file, useALEalgorithm, diag, & ADp, CDp, CS, tracer_flow_CSp, sponge_CSp, & - ALE_sponge_CSp, oda_incupd_CSp) + ALE_sponge_CSp, oda_incupd_CSp, int_tide_CSp) type(time_type), target :: Time !< model time type(ocean_grid_type), intent(inout) :: G !< model grid structure type(verticalGrid_type), intent(in) :: GV !< model vertical grid structure @@ -2998,6 +3001,7 @@ subroutine diabatic_driver_init(Time, G, GV, US, param_file, useALEalgorithm, di type(ALE_sponge_CS), pointer :: ALE_sponge_CSp !< pointer to the ALE sponge module control structure type(oda_incupd_CS), pointer :: oda_incupd_CSp !< pointer to the ocean data assimilation incremental !! update module control structure + type(int_tide_CS), pointer :: int_tide_CSp !< pointer to the internal tide structure ! Local variables real :: Kd ! A diffusivity used in the default for other tracer diffusivities [Z2 T-1 ~> m2 s-1] @@ -3032,6 +3036,7 @@ subroutine diabatic_driver_init(Time, G, GV, US, param_file, useALEalgorithm, di if (associated(sponge_CSp)) CS%sponge_CSp => sponge_CSp if (associated(ALE_sponge_CSp)) CS%ALE_sponge_CSp => ALE_sponge_CSp if (associated(oda_incupd_CSp)) CS%oda_incupd_CSp => oda_incupd_CSp + if (associated(int_tide_CSp)) CS%int_tide_CSp => int_tide_CSp CS%useALEalgorithm = useALEalgorithm CS%use_bulkmixedlayer = (GV%nkml > 0) @@ -3497,12 +3502,14 @@ subroutine diabatic_driver_init(Time, G, GV, US, param_file, useALEalgorithm, di if (CS%use_int_tides) then call int_tide_input_init(Time, G, GV, US, param_file, diag, CS%int_tide_input_CSp, & CS%int_tide_input) - call internal_tides_init(Time, G, GV, US, param_file, diag, CS%int_tide) + call internal_tides_init(Time, G, GV, US, param_file, diag, int_tide_CSp) endif + !if (associated(int_tide_CSp)) CS%int_tide_CSp => int_tide_CSp + physical_OBL_scheme = (CS%use_bulkmixedlayer .or. CS%use_KPP .or. CS%use_energetic_PBL) ! initialize module for setting diffusivities - call set_diffusivity_init(Time, G, GV, US, param_file, diag, CS%set_diff_CSp, CS%int_tide, & + call set_diffusivity_init(Time, G, GV, US, param_file, diag, CS%set_diff_CSp, CS%int_tide_CSp, & halo_TS=CS%halo_TS_diff, double_diffuse=CS%double_diffuse, & physical_OBL_scheme=physical_OBL_scheme) @@ -3558,6 +3565,25 @@ subroutine diabatic_driver_init(Time, G, GV, US, param_file, useALEalgorithm, di end subroutine diabatic_driver_init +!> Routine to register restarts, pass-through to children modules +subroutine register_diabatic_restarts(G, US, param_file, int_tide_CSp, restart_CSp) + type(ocean_grid_type), intent(in) :: G !< The ocean's 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(int_tide_CS), pointer :: int_tide_CSp !< Internal tide control structure + type(MOM_restart_CS), pointer :: restart_CSp !< MOM restart control structure + + logical :: use_int_tides + + use_int_tides=.false. + + call read_param(param_file, "INTERNAL_TIDES", use_int_tides) + + if (use_int_tides) then + call register_int_tide_restarts(G, US, param_file, int_tide_CSp, restart_CSp) + endif + +end subroutine register_diabatic_restarts !> Routine to close the diabatic driver module subroutine diabatic_driver_end(CS)