portedportable, not yet portedexecuted, not portableexecutable, not hit by this run
| 1 | ! This file is part of MOM6, the Modular Ocean Model version 6. | |
| 2 | ! See the LICENSE file for licensing information. | |
| 3 | ! SPDX-License-Identifier: Apache-2.0 | |
| 4 | ||
| 5 | !> Finite volume pressure gradient (integrated by quadrature or analytically) | |
| 6 | module MOM_PressureForce_FV | |
| 7 | ||
| 8 | use MOM_debugging, only : hchksum, uvchksum | |
| 9 | use MOM_diag_mediator, only : post_data, register_diag_field | |
| 10 | use MOM_diag_mediator, only : safe_alloc_ptr, diag_ctrl, time_type | |
| 11 | use MOM_error_handler, only : MOM_error, FATAL, WARNING, is_root_pe | |
| 12 | use MOM_file_parser, only : get_param, log_param, log_version, param_file_type | |
| 13 | use MOM_grid, only : ocean_grid_type | |
| 14 | use MOM_PressureForce_Mont, only : set_pbce_Bouss, set_pbce_nonBouss | |
| 15 | use MOM_self_attr_load, only : calc_SAL, SAL_CS | |
| 16 | use MOM_tidal_forcing, only : calc_tidal_forcing, tidal_forcing_CS | |
| 17 | use MOM_tidal_forcing, only : calc_tidal_forcing_legacy | |
| 18 | use MOM_unit_scaling, only : unit_scale_type | |
| 19 | use MOM_variables, only : thermo_var_ptrs, accel_diag_ptrs | |
| 20 | use MOM_verticalGrid, only : verticalGrid_type | |
| 21 | use MOM_EOS, only : calculate_density, calculate_spec_vol, EOS_domain | |
| 22 | use MOM_density_integrals, only : int_density_dz, int_specific_vol_dp | |
| 23 | use MOM_density_integrals, only : int_density_dz_generic_plm, int_density_dz_generic_ppm | |
| 24 | use MOM_density_integrals, only : int_spec_vol_dp_generic_plm | |
| 25 | use MOM_density_integrals, only : int_density_dz_generic_pcm, int_spec_vol_dp_generic_pcm | |
| 26 | use MOM_density_integrals, only : diagnose_mass_weight_Z, diagnose_mass_weight_p | |
| 27 | use MOM_ALE, only : TS_PLM_edge_values, TS_PPM_edge_values, TS_PLM_WLS_edge_values, ALE_CS | |
| 28 | ||
| 29 | implicit none ; private | |
| 30 | ||
| 31 | #include <MOM_memory.h> | |
| 32 | ||
| 33 | public PressureForce_FV_init | |
| 34 | public PressureForce_FV_Bouss, PressureForce_FV_nonBouss | |
| 35 | ||
| 36 | ! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional | |
| 37 | ! consistency testing. These are noted in comments with units like Z, H, L, and T, along with | |
| 38 | ! their mks counterparts with notation like "a velocity [Z T-1 ~> m s-1]". If the units | |
| 39 | ! vary with the Boussinesq approximation, the Boussinesq variant is given first. | |
| 40 | ||
| 41 | #ifdef __NVCOMPILER_OPENMP_GPU | |
| 42 | integer, parameter :: default_nkblock = 0 !< Default k block size for PLM density integrals [nondim] | |
| 43 | integer, parameter :: default_njblock_plm = 0 !< Default j block size for PLM density integrals [nondim] | |
| 44 | #else | |
| 45 | integer, parameter :: default_nkblock = 1 !< Default k block size for PLM density integrals [nondim] | |
| 46 | integer, parameter :: default_njblock_plm = 1 !< Default j block size for PLM density integrals [nondim] | |
| 47 | #endif | |
| 48 | integer, parameter :: default_niblock_plm = 0 !< Default i block size for PLM density integrals [nondim] | |
| 49 | ||
| 50 | !> Finite volume pressure gradient control structure | |
| 51 | type, public :: PressureForce_FV_CS ; private | |
| 52 | logical :: initialized = .false. !< True if this control structure has been initialized. | |
| 53 | logical :: calculate_SAL = .false. !< If true, calculate self-attraction and loading. | |
| 54 | logical :: sal_use_bpa = .false. !< If true, use bottom pressure anomaly instead of SSH | |
| 55 | !! to calculate SAL. | |
| 56 | logical :: tides = .false. !< If true, apply tidal momentum forcing. | |
| 57 | real :: rho_ref !< The reference density that is subtracted off when calculating pressure | |
| 58 | !! gradient forces [R ~> kg m-3]. | |
| 59 | logical :: rho_ref_bug !< If true, recover a bug that mixes GV%Rho0 and CS%rho_ref in Boussinesq mode. | |
| 60 | real :: GFS_scale !< A scaling of the surface pressure gradients to | |
| 61 | !! allow the use of a reduced gravity model [nondim]. | |
| 62 | type(time_type), pointer :: Time !< A pointer to the ocean model's clock. | |
| 63 | type(diag_ctrl), pointer :: diag !< A structure that is used to regulate the | |
| 64 | !! timing of diagnostic output. | |
| 65 | integer :: MassWghtInterp !< A flag indicating whether and how to use mass weighting in T/S interpolation | |
| 66 | logical :: correction_intxpa !< If true, apply a correction to the value of intxpa at a selected | |
| 67 | !! interface under ice, using matching at the end values along with a | |
| 68 | !! 5-point quadrature integral of the hydrostatic pressure or height | |
| 69 | !! changes along that interface. The selected interface is either at the | |
| 70 | !! ocean's surface or in the interior, depending on reset_intxpa_integral. | |
| 71 | logical :: reset_intxpa_integral !< If true and the surface displacement between adjacent cells | |
| 72 | !! exceeds the vertical grid spacing, reset intxpa at the interface below | |
| 73 | !! a trusted interior cell. (This often applies in ice shelf cavities.) | |
| 74 | logical :: MassWghtInterpVanOnly !< If true, don't do mass weighting of T/S interpolation unless vanished | |
| 75 | logical :: reset_intxpa_flattest !< If true, use flattest interface rather than top for reset integral | |
| 76 | !! in cases where no best nonvanished interface | |
| 77 | real :: h_nonvanished !< A minimal layer thickness that indicates that a layer is thick enough | |
| 78 | !! to usefully reestimate the pressure integral across the interface | |
| 79 | !! below it [H ~> m or kg m-2] | |
| 80 | logical :: use_inaccurate_pgf_rho_anom !< If true, uses the older and less accurate | |
| 81 | !! method to calculate density anomalies, as used prior to | |
| 82 | !! March 2018. | |
| 83 | logical :: boundary_extrap !< Indicate whether high-order boundary | |
| 84 | !! extrapolation should be used within boundary cells | |
| 85 | ||
| 86 | logical :: reconstruct !< If true, polynomial profiles of T & S will be | |
| 87 | !! reconstructed and used in the integrals for the | |
| 88 | !! finite volume pressure gradient calculation. | |
| 89 | !! The default depends on whether regridding is being used. | |
| 90 | ||
| 91 | integer :: Recon_Scheme !< Order of the polynomial of the reconstruction of T & S | |
| 92 | !! for the finite volume pressure gradient calculation. | |
| 93 | !! By the default (1) is for a piecewise linear method | |
| 94 | ||
| 95 | logical :: debug !< If true, write verbose checksums for debugging purposes. | |
| 96 | logical :: use_SSH_in_Z0p !< If true, adjust the height at which the pressure used in the | |
| 97 | !! equation of state is 0 to account for the displacement of the sea | |
| 98 | !! surface including adjustments for atmospheric or sea-ice pressure. | |
| 99 | logical :: use_stanley_pgf !< If true, turn on Stanley parameterization in the PGF | |
| 100 | logical :: bq_sal_tides = .false. !< If true, use an alternative method for SAL and tides | |
| 101 | !! in Boussinesq mode | |
| 102 | integer :: tides_answer_date = 99991231 !< Recover old answers with tides | |
| 103 | integer :: id_e_tide = -1 !< Diagnostic identifier | |
| 104 | integer :: id_e_tidal_eq = -1 !< Diagnostic identifier | |
| 105 | integer :: id_e_tidal_sal = -1 !< Diagnostic identifier | |
| 106 | integer :: id_e_sal = -1 !< Diagnostic identifier | |
| 107 | integer :: id_rho_pgf = -1 !< Diagnostic identifier | |
| 108 | integer :: id_rho_stanley_pgf = -1 !< Diagnostic identifier | |
| 109 | integer :: id_p_stanley = -1 !< Diagnostic identifier | |
| 110 | integer :: id_MassWt_u = -1 !< Diagnostic identifier | |
| 111 | integer :: id_MassWt_v = -1 !< Diagnostic identifier | |
| 112 | integer :: id_sal_u = -1 !< Diagnostic identifier | |
| 113 | integer :: id_sal_v = -1 !< Diagnostic identifier | |
| 114 | integer :: id_tides_u = -1 !< Diagnostic identifier | |
| 115 | integer :: id_tides_v = -1 !< Diagnostic identifier | |
| 116 | integer :: nkblock !< Vertical block size for PLM density integrals [nondim] | |
| 117 | integer :: niblock_plm !< i-tile size for PLM density integrals, 0 = full domain [nondim] | |
| 118 | integer :: njblock_plm !< j-tile size for PLM density integrals, 0 = full domain [nondim] | |
| 119 | type(SAL_CS), pointer :: SAL_CSp => NULL() !< SAL control structure | |
| 120 | type(tidal_forcing_CS), pointer :: tides_CSp => NULL() !< Tides control structure | |
| 121 | end type PressureForce_FV_CS | |
| 122 | ||
| 123 | contains | |
| 124 | ||
| 125 | !> \brief Non-Boussinesq analytically-integrated finite volume form of pressure gradient | |
| 126 | !! | |
| 127 | !! Determines the acceleration due to hydrostatic pressure forces, using | |
| 128 | !! the analytic finite volume form of the Pressure gradient, and does not | |
| 129 | !! make the Boussinesq approximation. | |
| 130 | !! | |
| 131 | !! To work, the following fields must be set outside of the usual (is:ie,js:je) | |
| 132 | !! range before this subroutine is called: | |
| 133 | !! h(isB:ie+1,jsB:je+1), T(isB:ie+1,jsB:je+1), and S(isB:ie+1,jsB:je+1). | |
| 134 | 0 | subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, ADp, p_atm, pbce, eta) |
| 135 | type(ocean_grid_type), intent(in) :: G !< Ocean grid structure | |
| 136 | type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure | |
| 137 | type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type | |
| 138 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> kg m-2] | |
| 139 | type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamic variables | |
| 140 | real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(out) :: PFu !< Zonal acceleration [L T-2 ~> m s-2] | |
| 141 | real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(out) :: PFv !< Meridional acceleration [L T-2 ~> m s-2] | |
| 142 | type(PressureForce_FV_CS), intent(in) :: CS !< Finite volume PGF control structure | |
| 143 | type(ALE_CS), pointer :: ALE_CSp !< ALE control structure | |
| 144 | type(accel_diag_ptrs), pointer :: ADp !< Acceleration diagnostic pointers | |
| 145 | real, dimension(:,:), pointer :: p_atm !< The pressure at the ice-ocean | |
| 146 | !! or atmosphere-ocean interface [R L2 T-2 ~> Pa]. | |
| 147 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), optional, intent(out) :: pbce !< The baroclinic pressure | |
| 148 | !! anomaly in each layer due to eta anomalies | |
| 149 | !! [L2 T-2 H-1 ~> m4 s-2 kg-1]. | |
| 150 | real, dimension(SZI_(G),SZJ_(G)), optional, intent(out) :: eta !< The total column mass used to | |
| 151 | !! calculate PFu and PFv [H ~> kg m-2]. | |
| 152 | ! Local variables | |
| 153 | 0 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: p ! Interface pressure [R L2 T-2 ~> Pa]. |
| 154 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), target :: & | |
| 155 | 0 | T_tmp, & ! Temporary array of temperatures where layers that are lighter |
| 156 | ! than the mixed layer have the mixed layer's properties [C ~> degC]. | |
| 157 | 0 | S_tmp ! Temporary array of salinities where layers that are lighter |
| 158 | ! than the mixed layer have the mixed layer's properties [S ~> ppt]. | |
| 159 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: & | |
| 160 | 0 | S_t, S_b, & ! Top and bottom edge values for linear reconstructions |
| 161 | ! of salinity within each layer [S ~> ppt]. | |
| 162 | 0 | T_t, T_b ! Top and bottom edge values for linear reconstructions |
| 163 | ! of temperature within each layer [C ~> degC]. | |
| 164 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: & | |
| 165 | 0 | dza, & ! The change in geopotential anomaly between the top and bottom |
| 166 | ! of a layer [L2 T-2 ~> m2 s-2]. | |
| 167 | 0 | intp_dza ! The vertical integral in depth of the pressure anomaly less |
| 168 | ! the pressure anomaly at the top of the layer [R L4 T-4 ~> Pa m2 s-2]. | |
| 169 | real, dimension(SZI_(G),SZJ_(G)) :: & | |
| 170 | 0 | dp, & ! The (positive) change in pressure across a layer [R L2 T-2 ~> Pa]. |
| 171 | 0 | SSH, & ! Sea surfae height anomaly for self-attraction and loading. Used if |
| 172 | ! CALCULATE_SAL is True and SAL_USE_BPA is False [Z ~> m]. | |
| 173 | 0 | pbot, & ! Total bottom pressure for self-attraction and loading. Used if |
| 174 | ! CALCULATE_SAL is True and SAL_USE_BPA is True [R L2 T-2 ~> Pa]. | |
| 175 | 0 | e_sal, & ! The bottom geopotential anomaly due to self-attraction and loading [Z ~> m]. |
| 176 | 0 | e_tidal_eq, & ! The bottom geopotential anomaly due to tidal forces from astronomical sources [Z ~> m]. |
| 177 | 0 | e_tidal_sal, & ! The bottom geopotential anomaly due to harmonic self-attraction and loading |
| 178 | ! specific to tides [Z ~> m]. | |
| 179 | 0 | e_sal_and_tide, & ! The summation of self-attraction and loading and tidal forcing, used for recovering |
| 180 | ! old answers only [Z ~> m]. | |
| 181 | 0 | dM ! The barotropic adjustment to the Montgomery potential to |
| 182 | ! account for a reduced gravity model [L2 T-2 ~> m2 s-2]. | |
| 183 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: & | |
| 184 | 0 | za ! The geopotential anomaly (i.e. g*e + alpha_0*pressure) at the |
| 185 | ! interfaces [L2 T-2 ~> m2 s-2]. | |
| 186 | ||
| 187 | 0 | real, dimension(SZI_(G)) :: Rho_cv_BL ! The coordinate potential density in the deepest variable |
| 188 | ! density near-surface layer [R ~> kg m-3]. | |
| 189 | real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1) :: & | |
| 190 | 0 | intx_za ! The zonal integral of the geopotential anomaly along the |
| 191 | ! interfaces, divided by the grid spacing [L2 T-2 ~> m2 s-2]. | |
| 192 | real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: & | |
| 193 | 0 | intx_dza ! The change in intx_za through a layer [L2 T-2 ~> m2 s-2]. |
| 194 | real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1) :: & | |
| 195 | 0 | inty_za ! The meridional integral of the geopotential anomaly along the |
| 196 | ! interfaces, divided by the grid spacing [L2 T-2 ~> m2 s-2]. | |
| 197 | real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: & | |
| 198 | 0 | inty_dza ! The change in inty_za through a layer [L2 T-2 ~> m2 s-2]. |
| 199 | real, dimension(SZI_(G),SZJ_(G)) :: & | |
| 200 | 0 | T_top, & ! Temperature of top layer used with correction_intxpa [C ~> degC] |
| 201 | 0 | S_top ! Salinity of top layer used with correction_intxpa [S ~> ppt] |
| 202 | real, dimension(SZIB_(G),SZJ_(G)) :: & | |
| 203 | 0 | intx_za_cor ! Correction for curvature in intx_za [L2 T-2 ~> m2 s-2] |
| 204 | real, dimension(SZI_(G),SZJB_(G)) :: & | |
| 205 | 0 | inty_za_cor ! Correction for curvature in inty_za [L2 T-2 ~> m2 s-2] |
| 206 | ||
| 207 | ! These variables are used with reset_intxpa_integral. The values are taken from different | |
| 208 | ! interfaces as a function of position. | |
| 209 | real, dimension(SZIB_(G),SZJ_(G)) :: & | |
| 210 | 0 | T_int_W, T_int_E, & ! Temperatures on the reference interface to the east and west of a u-point [C ~> degC] |
| 211 | 0 | S_int_W, S_int_E, & ! Salinities on the reference interface to the east and west of a u-point [S ~> ppt] |
| 212 | 0 | p_int_W, p_int_E, & ! Pressures on the reference interface to the east and west of a u-point [R L2 T-2 ~> Pa] |
| 213 | 0 | intx_za_nonlin, & ! Deviations in the previous version of intx_pa for the reference interface |
| 214 | ! from the value that would be obtained from assuming that pressure varies | |
| 215 | ! linearly with depth along that interface [R L2 T-2 ~> Pa]. | |
| 216 | 0 | dp_int_x, & ! The change in x in pressure along the reference interface [R L2 T-2 ~> Pa] |
| 217 | 0 | intx_za_cor_ri ! The correction to intx_za based on the reference interface calculations [L2 T-2 ~> m2 s-2] |
| 218 | real, dimension(SZI_(G),SZJB_(G)) :: & | |
| 219 | 0 | T_int_S, T_int_N, & ! Temperatures on the reference interface to the north and south of a v-point [C ~> degC] |
| 220 | 0 | S_int_S, S_int_N, & ! Salinities on the reference interface to the north and south of a v-point [S ~> ppt] |
| 221 | 0 | p_int_S, p_int_N, & ! Pressures on the reference interface to the north and south of a v-point [R L2 T-2 ~> Pa] |
| 222 | 0 | inty_za_nonlin, & ! Deviations in the previous version of intx_pa for the reference interface |
| 223 | ! from the value that would be obtained from assuming that pressure varies | |
| 224 | ! linearly with depth along that interface [L2 T-2 ~> m2 s-2]. | |
| 225 | 0 | dp_int_y, & ! The change in y in geopotenial height along the reference interface [R L2 T-2 ~> Pa] |
| 226 | 0 | inty_za_cor_ri ! The correction to inty_za based on the reference interface calculations [L2 T-2 ~> m2 s-2] |
| 227 | logical, dimension(SZIB_(G),SZJ_(G)) :: & | |
| 228 | 0 | seek_x_cor ! If true, try to find a u-point interface that would provide a better estimate |
| 229 | ! of the curvature terms in the intx_pa. | |
| 230 | logical, dimension(SZI_(G),SZJB_(G)) :: & | |
| 231 | 0 | seek_y_cor ! If true, try to find a v-point interface that would provide a better estimate |
| 232 | ! of the curvature terms in the inty_pa. | |
| 233 | real, dimension(SZIB_(G),SZJ_(G)) :: & | |
| 234 | 0 | delta_p_x ! If using flattest interface for reset integral, store x interface |
| 235 | ! differences [R L2 T-2 ~> Pa] | |
| 236 | real, dimension(SZI_(G),SZJB_(G)) :: & | |
| 237 | 0 | delta_p_y ! If using flattest interface for reset integral, store y interface |
| 238 | ! differences [R L2 T-2 ~> Pa] | |
| 239 | real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: & | |
| 240 | 0 | MassWt_u ! The fractional mass weighting at a u-point [nondim]. |
| 241 | real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: & | |
| 242 | 0 | MassWt_v ! The fractional mass weighting at a v-point [nondim]. |
| 243 | 0 | real :: p_ref(SZI_(G)) ! The pressure used to calculate the coordinate |
| 244 | ! density, [R L2 T-2 ~> Pa] (usually 2e7 Pa = 2000 dbar). | |
| 245 | real :: dp_sfc ! The change in surface pressure between adjacent cells [R L2 T-2 ~> Pa] | |
| 246 | ||
| 247 | real :: dp_neglect ! A thickness that is so small it is usually lost | |
| 248 | ! in roundoff and can be neglected [R L2 T-2 ~> Pa]. | |
| 249 | real :: p_nonvanished ! nonvanshed pressure [R L2 T-2 ~> Pa] | |
| 250 | real :: I_gEarth ! The inverse of GV%g_Earth [T2 Z L-2 ~> s2 m-1] | |
| 251 | real :: alpha_anom ! The in-situ specific volume, averaged over a | |
| 252 | ! layer, less alpha_ref [R-1 ~> m3 kg-1]. | |
| 253 | logical :: use_p_atm ! If true, use the atmospheric pressure. | |
| 254 | logical :: use_ALE ! If true, use an ALE pressure reconstruction. | |
| 255 | logical :: use_EOS ! If true, density is calculated from T & S using an equation of state. | |
| 256 | logical :: do_more_k ! If true, there are still points where a flatter interface remains to be found. | |
| 257 | 0 | type(thermo_var_ptrs) :: tv_tmp! A structure of temporary T & S. |
| 258 | ||
| 259 | real :: alpha_ref ! A reference specific volume [R-1 ~> m3 kg-1] that is used | |
| 260 | ! to reduce the impact of truncation errors. | |
| 261 | 0 | real :: rho_in_situ(SZI_(G)) ! The in situ density [R ~> kg m-3]. |
| 262 | real :: Pa_to_H ! A factor to convert from Pa to the thickness units (H) | |
| 263 | ! [H T2 R-1 L-2 ~> m Pa-1 or kg m-2 Pa-1]. | |
| 264 | real :: H_to_RL2_T2 ! A factor to convert from thickness units (H) to pressure | |
| 265 | ! units [R L2 T-2 H-1 ~> Pa m-1 or Pa m2 kg-1]. | |
| 266 | real :: T5(5) ! Temperatures and salinities at five quadrature points [C ~> degC] | |
| 267 | real :: S5(5) ! Salinities at five quadrature points [S ~> ppt] | |
| 268 | real :: p5(5) ! Pressures at five quadrature points for use with the equation of state [R L2 T-2 ~> Pa] | |
| 269 | real :: SpV5(5) ! Specific volume anomalies at five quadrature points [R-1 ~> m3 kg-1] | |
| 270 | real :: wt_R ! A weighting factor [nondim] | |
| 271 | ||
| 272 | ! real :: oneatm ! 1 standard atmosphere of pressure in [R L2 T-2 ~> Pa] | |
| 273 | real, parameter :: C1_6 = 1.0/6.0 ! [nondim] | |
| 274 | real, parameter :: C1_90 = 1.0/90.0 ! A rational constant [nondim] | |
| 275 | integer :: is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, nkmb | |
| 276 | integer, dimension(2) :: EOSdom ! The i-computational domain for the equation of state | |
| 277 | integer, dimension(2) :: EOSdom_u ! The i-computational domain for the equation of state at u-velocity points | |
| 278 | integer, dimension(2) :: EOSdom_v ! The i-computational domain for the equation of state at v-velocity points | |
| 279 | integer :: i, j, k, m | |
| 280 | ||
| 281 | 0 | is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke |
| 282 | 0 | nkmb=GV%nk_rho_varies |
| 283 | 0 | Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB |
| 284 | 0 | EOSdom(1) = Isq - (G%isd-1) ; EOSdom(2) = G%iec+1 - (G%isd-1) |
| 285 | 0 | EOSdom_u(1) = Isq - (G%IsdB-1) ; EOSdom_u(2) = Ieq - (G%IsdB-1) |
| 286 | 0 | EOSdom_v(1) = is - (G%isd-1) ; EOSdom_v(2) = ie - (G%isd-1) |
| 287 | ||
| 288 | 0 | if (.not.CS%initialized) call MOM_error(FATAL, & |
| 289 | 0 | "MOM_PressureForce_FV_nonBouss: Module must be initialized before it is used.") |
| 290 | ||
| 291 | 0 | if (CS%use_stanley_pgf) call MOM_error(FATAL, & |
| 292 | "MOM_PressureForce_FV_nonBouss: The Stanley parameterization is not yet "//& | |
| 293 | 0 | "implemented in non-Boussinesq mode.") |
| 294 | ||
| 295 | 0 | use_p_atm = associated(p_atm) |
| 296 | 0 | use_EOS = associated(tv%eqn_of_state) |
| 297 | 0 | use_ALE = .false. |
| 298 | 0 | if (associated(ALE_CSp)) use_ALE = CS%reconstruct .and. use_EOS |
| 299 | ||
| 300 | 0 | H_to_RL2_T2 = GV%g_Earth*GV%H_to_RZ |
| 301 | 0 | dp_neglect = GV%g_Earth*GV%H_to_RZ * GV%H_subroundoff |
| 302 | 0 | alpha_ref = 1.0 / CS%rho_ref |
| 303 | 0 | I_gEarth = 1.0 / GV%g_Earth |
| 304 | 0 | p_nonvanished = GV%g_Earth*GV%H_to_RZ*CS%h_nonvanished |
| 305 | ||
| 306 | 0 | if ((CS%id_MassWt_u > 0) .or. (CS%id_MassWt_v > 0)) then |
| 307 | 0 | MassWt_u(:,:,:) = 0.0 ; MassWt_v(:,:,:) = 0.0 |
| 308 | endif | |
| 309 | ||
| 310 | 0 | if (use_p_atm) then |
| 311 | !$OMP parallel do default(shared) | |
| 312 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 313 | 0 | p(i,j,1) = p_atm(i,j) |
| 314 | enddo ; enddo | |
| 315 | else | |
| 316 | ! oneatm = 101325.0 * US%Pa_to_RL2_T2 ! 1 atm scaled to [R L2 T-2 ~> Pa] | |
| 317 | !$OMP parallel do default(shared) | |
| 318 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 319 | 0 | p(i,j,1) = 0.0 ! or oneatm |
| 320 | enddo ; enddo | |
| 321 | endif | |
| 322 | !$OMP parallel do default(shared) | |
| 323 | 0 | do j=Jsq,Jeq+1 ; do k=2,nz+1 ; do i=Isq,Ieq+1 |
| 324 | 0 | p(i,j,K) = p(i,j,K-1) + H_to_RL2_T2 * h(i,j,k-1) |
| 325 | enddo ; enddo ; enddo | |
| 326 | ||
| 327 | 0 | if (use_EOS) then |
| 328 | ! With a bulk mixed layer, replace the T & S of any layers that are | |
| 329 | ! lighter than the buffer layer with the properties of the buffer | |
| 330 | ! layer. These layers will be massless anyway, and it avoids any | |
| 331 | ! formal calculations with hydrostatically unstable profiles. | |
| 332 | 0 | if (nkmb>0) then |
| 333 | 0 | tv_tmp%T => T_tmp ; tv_tmp%S => S_tmp |
| 334 | 0 | tv_tmp%eqn_of_state => tv%eqn_of_state |
| 335 | 0 | do i=Isq,Ieq+1 ; p_ref(i) = tv%P_Ref ; enddo |
| 336 | !$OMP parallel do default(shared) private(Rho_cv_BL) | |
| 337 | 0 | do j=Jsq,Jeq+1 |
| 338 | 0 | do k=1,nkmb ; do i=Isq,Ieq+1 |
| 339 | 0 | tv_tmp%T(i,j,k) = tv%T(i,j,k) ; tv_tmp%S(i,j,k) = tv%S(i,j,k) |
| 340 | enddo ; enddo | |
| 341 | call calculate_density(tv%T(:,j,nkmb), tv%S(:,j,nkmb), p_ref, Rho_cv_BL(:), & | |
| 342 | 0 | tv%eqn_of_state, EOSdom) |
| 343 | 0 | do k=nkmb+1,nz ; do i=Isq,Ieq+1 |
| 344 | 0 | if (GV%Rlay(k) < Rho_cv_BL(i)) then |
| 345 | 0 | tv_tmp%T(i,j,k) = tv%T(i,j,nkmb) ; tv_tmp%S(i,j,k) = tv%S(i,j,nkmb) |
| 346 | else | |
| 347 | 0 | tv_tmp%T(i,j,k) = tv%T(i,j,k) ; tv_tmp%S(i,j,k) = tv%S(i,j,k) |
| 348 | endif | |
| 349 | enddo ; enddo | |
| 350 | enddo | |
| 351 | else | |
| 352 | 0 | tv_tmp%T => tv%T ; tv_tmp%S => tv%S |
| 353 | 0 | tv_tmp%eqn_of_state => tv%eqn_of_state |
| 354 | endif | |
| 355 | endif | |
| 356 | ||
| 357 | ! If regridding is activated, do a linear reconstruction of salinity | |
| 358 | ! and temperature across each layer. The subscripts 't' and 'b' refer | |
| 359 | ! to top and bottom values within each layer (these are the only degrees | |
| 360 | ! of freedom needed to know the linear profile). | |
| 361 | 0 | if ( use_ALE .and. (CS%Recon_Scheme == 1) ) then |
| 362 | 0 | call TS_PLM_edge_values(ALE_CSp, S_t, S_b, T_t, T_b, G, GV, tv, h, CS%boundary_extrap) |
| 363 | 0 | elseif ( use_ALE .and. (CS%Recon_Scheme == 2) ) then |
| 364 | 0 | call TS_PPM_edge_values(ALE_CSp, S_t, S_b, T_t, T_b, G, GV, tv, h, CS%boundary_extrap) |
| 365 | 0 | elseif ( use_ALE .and. (CS%Recon_Scheme == 3) ) then |
| 366 | 0 | call TS_PLM_WLS_edge_values(ALE_CSp, S_t, S_b, T_t, T_b, G, GV, tv, h) |
| 367 | 0 | elseif (CS%reset_intxpa_integral) then |
| 368 | 0 | do k=1,nz ; do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 369 | 0 | T_b(i,j,k) = tv%T(i,j,k) ; S_b(i,j,k) = tv%S(i,j,k) |
| 370 | enddo ; enddo ; enddo | |
| 371 | endif | |
| 372 | ||
| 373 | !$OMP parallel do default(shared) private(alpha_anom,dp) | |
| 374 | 0 | do k=1,nz |
| 375 | ! Calculate 4 integrals through the layer that are required in the | |
| 376 | ! subsequent calculation. | |
| 377 | 0 | if (use_EOS) then |
| 378 | 0 | if ( use_ALE .and. CS%Recon_Scheme > 0 ) then |
| 379 | 0 | if ( CS%Recon_Scheme == 1 .or. CS%Recon_Scheme == 3 ) then |
| 380 | call int_spec_vol_dp_generic_plm( T_t(:,:,k), T_b(:,:,k), S_t(:,:,k), S_b(:,:,k), & | |
| 381 | p(:,:,K), p(:,:,K+1), alpha_ref, dp_neglect, p(:,:,nz+1), G%HI, & | |
| 382 | tv%eqn_of_state, US, dza(:,:,k), intp_dza(:,:,k), intx_dza(:,:,k), inty_dza(:,:,k), & | |
| 383 | P_surf=p(:,:,1), MassWghtInterp=CS%MassWghtInterp, & | |
| 384 | 0 | MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, p_nv=p_nonvanished) |
| 385 | 0 | elseif ( CS%Recon_Scheme == 2 ) then |
| 386 | call MOM_error(FATAL, "PressureForce_FV_nonBouss: "//& | |
| 387 | 0 | "int_spec_vol_dp_generic_ppm does not exist yet.") |
| 388 | ! call int_spec_vol_dp_generic_ppm ( tv%T(:,:,k), T_t(:,:,k), T_b(:,:,k), & | |
| 389 | ! tv%S(:,:,k), S_t(:,:,k), S_b(:,:,k), p(:,:,K), p(:,:,K+1), & | |
| 390 | ! alpha_ref, G%HI, tv%eqn_of_state, dza(:,:,k), intp_dza(:,:,k), & | |
| 391 | ! intx_dza(:,:,k), inty_dza(:,:,k), P_surf=p(:,:,1), MassWghtInterp=CS%MassWghtInterp) | |
| 392 | endif | |
| 393 | else | |
| 394 | call int_specific_vol_dp(tv_tmp%T(:,:,k), tv_tmp%S(:,:,k), p(:,:,K), & | |
| 395 | p(:,:,K+1), alpha_ref, G%HI, tv%eqn_of_state, & | |
| 396 | US, dza(:,:,k), intp_dza(:,:,k), intx_dza(:,:,k), & | |
| 397 | inty_dza(:,:,k), bathyP=p(:,:,nz+1), P_surf=p(:,:,1), dP_tiny=dp_neglect, & | |
| 398 | MassWghtInterp=CS%MassWghtInterp, & | |
| 399 | 0 | MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, p_nv=p_nonvanished) |
| 400 | endif | |
| 401 | 0 | if ((CS%id_MassWt_u > 0) .or. (CS%id_MassWt_v > 0)) & |
| 402 | call diagnose_mass_weight_p(p(:,:,K), p(:,:,K+1), p(:,:,nz+1), p(:,:,1), dp_neglect, CS%MassWghtInterp, & | |
| 403 | G%HI, MassWt_u(:,:,k), MassWt_v(:,:,k), & | |
| 404 | 0 | MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, p_nv=p_nonvanished) |
| 405 | else | |
| 406 | 0 | alpha_anom = 1.0 / GV%Rlay(k) - alpha_ref |
| 407 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 408 | 0 | dp(i,j) = H_to_RL2_T2 * h(i,j,k) |
| 409 | 0 | dza(i,j,k) = alpha_anom * dp(i,j) |
| 410 | 0 | intp_dza(i,j,k) = 0.5 * alpha_anom * dp(i,j)**2 |
| 411 | enddo ; enddo | |
| 412 | 0 | do j=js,je ; do I=Isq,Ieq |
| 413 | 0 | intx_dza(i,j,k) = 0.5 * alpha_anom * (dp(i,j)+dp(i+1,j)) |
| 414 | enddo ; enddo | |
| 415 | 0 | do J=Jsq,Jeq ; do i=is,ie |
| 416 | 0 | inty_dza(i,j,k) = 0.5 * alpha_anom * (dp(i,j)+dp(i,j+1)) |
| 417 | enddo ; enddo | |
| 418 | endif | |
| 419 | enddo | |
| 420 | ||
| 421 | ! The bottom geopotential anomaly is calculated first so that the increments | |
| 422 | ! to the geopotential anomalies can be reused. Alternately, the surface | |
| 423 | ! geopotential could be calculated directly with separate calls to | |
| 424 | ! int_specific_vol_dp with alpha_ref=0, and the anomalies used going | |
| 425 | ! downward, which would relieve the need for dza, intp_dza, intx_dza, and | |
| 426 | ! inty_dza to be 3-D arrays. | |
| 427 | ||
| 428 | ! Sum vertically to determine the surface geopotential anomaly. | |
| 429 | !$OMP parallel do default(shared) | |
| 430 | 0 | do j=Jsq,Jeq+1 |
| 431 | 0 | do i=Isq,Ieq+1 |
| 432 | 0 | za(i,j,nz+1) = alpha_ref*p(i,j,nz+1) - GV%g_Earth*G%bathyT(i,j) |
| 433 | enddo | |
| 434 | 0 | do k=nz,1,-1 ; do i=Isq,Ieq+1 |
| 435 | 0 | za(i,j,K) = za(i,j,K+1) + dza(i,j,k) |
| 436 | enddo ; enddo | |
| 437 | enddo | |
| 438 | ||
| 439 | ! Calculate and add self-attraction and loading (SAL) geopotential height anomaly to interface height. | |
| 440 | 0 | if (CS%calculate_SAL) then |
| 441 | 0 | if (CS%sal_use_bpa) then |
| 442 | !$OMP parallel do default(shared) | |
| 443 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 444 | 0 | pbot(i,j) = p(i,j,nz+1) |
| 445 | enddo ; enddo | |
| 446 | 0 | call calc_SAL(pbot, e_sal, G, CS%SAL_CSp, tmp_scale=US%Z_to_m) |
| 447 | else | |
| 448 | !$OMP parallel do default(shared) | |
| 449 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 450 | 0 | SSH(i,j) = (za(i,j,1) - alpha_ref*p(i,j,1)) * I_gEarth - G%Z_ref |
| 451 | ! Remove above sea level topography at floodable cells | |
| 452 | 0 | SSH(i,j) = SSH(i,j) - max(-G%bathyT(i,j)-G%meanSL(i,j), 0.0) |
| 453 | enddo ; enddo | |
| 454 | 0 | call calc_SAL(SSH, e_sal, G, CS%SAL_CSp, tmp_scale=US%Z_to_m) |
| 455 | endif | |
| 456 | ||
| 457 | ! This gives new answers after the change of separating SAL from tidal forcing module. | |
| 458 | 0 | if ((CS%tides_answer_date>20230630) .or. (.not.GV%semi_Boussinesq) .or. (.not.CS%tides)) then |
| 459 | !$OMP parallel do default(shared) | |
| 460 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 461 | 0 | za(i,j,1) = za(i,j,1) - GV%g_Earth * e_sal(i,j) |
| 462 | enddo ; enddo | |
| 463 | endif | |
| 464 | endif | |
| 465 | ||
| 466 | ! Calculate and add tidal geopotential height anomaly to interface height. | |
| 467 | 0 | if (CS%tides) then |
| 468 | 0 | if ((CS%tides_answer_date>20230630) .or. (.not.GV%semi_Boussinesq)) then |
| 469 | 0 | call calc_tidal_forcing(CS%Time, e_tidal_eq, e_tidal_sal, G, US, CS%tides_CSp) |
| 470 | !$OMP parallel do default(shared) | |
| 471 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 472 | 0 | za(i,j,1) = za(i,j,1) - GV%g_Earth * (e_tidal_eq(i,j) + e_tidal_sal(i,j)) |
| 473 | enddo ; enddo | |
| 474 | else ! This block recreates older answers with tides. | |
| 475 | 0 | if (.not.CS%calculate_SAL) e_sal(:,:) = 0.0 |
| 476 | call calc_tidal_forcing_legacy(CS%Time, e_sal, e_sal_and_tide, e_tidal_eq, e_tidal_sal, & | |
| 477 | 0 | G, US, CS%tides_CSp) |
| 478 | !$OMP parallel do default(shared) | |
| 479 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 480 | 0 | za(i,j,1) = za(i,j,1) - GV%g_Earth * e_sal_and_tide(i,j) |
| 481 | enddo ; enddo | |
| 482 | endif | |
| 483 | endif | |
| 484 | ||
| 485 | ! Find the height anomalies at the interfaces. If there are no tides and no SAL, | |
| 486 | ! there is no need to correct za, but omitting this changes answers at roundoff. | |
| 487 | 0 | do k=1,nz |
| 488 | !$OMP parallel do default(shared) | |
| 489 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 490 | 0 | za(i,j,K+1) = za(i,j,K) - dza(i,j,k) |
| 491 | enddo ; enddo | |
| 492 | enddo | |
| 493 | ||
| 494 | 0 | if (CS%debug) then |
| 495 | 0 | call hchksum(za, "Pre-correction za", G%HI, haloshift=1, unscale=US%L_T_to_m_s**2) |
| 496 | 0 | call hchksum(p, "Pre-correction p", G%HI, haloshift=1, unscale=US%RL2_T2_to_Pa) |
| 497 | endif | |
| 498 | ||
| 499 | ! With an ice-shelf or icebergs, this linearity condition might need to be applied | |
| 500 | ! to a sub-surface interface. | |
| 501 | 0 | if (CS%correction_intxpa .or. CS%reset_intxpa_integral) then |
| 502 | ! Determine surface temperature and salinity for use in the pressure gradient corrections | |
| 503 | 0 | if (use_ALE .and. (CS%Recon_Scheme > 0)) then |
| 504 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 505 | 0 | T_top(i,j) = T_t(i,j,1) ; S_top(i,j) = S_t(i,j,1) |
| 506 | enddo ; enddo | |
| 507 | else | |
| 508 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 509 | 0 | T_top(i,j) = tv%T(i,j,1) ; S_top(i,j) = tv%S(i,j,1) |
| 510 | enddo ; enddo | |
| 511 | endif | |
| 512 | endif | |
| 513 | ||
| 514 | 0 | if (CS%correction_intxpa) then |
| 515 | ! This version makes a 5 point quadrature correction for hydrostatic variations in surface | |
| 516 | ! pressure under ice. | |
| 517 | !$OMP parallel do default(shared) private(dp_sfc,T5,S5,p5,wt_R,SpV5) | |
| 518 | 0 | do j=js,je ; do I=Isq,Ieq |
| 519 | 0 | intx_za_cor(I,j) = 0.0 |
| 520 | 0 | dp_sfc = (p(i+1,j,1) - p(i,j,1)) |
| 521 | ! If the changes in pressure and height anomaly were explicable by just a hydrostatic balance, | |
| 522 | ! the implied specific volume would be SpV_implied = alpha_ref - (dza_x / dp_x) | |
| 523 | 0 | if (dp_sfc * (alpha_ref*dp_sfc - (za(i+1,j,1)-za(i,j,1))) > 0.0) then |
| 524 | 0 | T5(1) = T_top(i,j) ; T5(5) = T_top(i+1,j) |
| 525 | 0 | S5(1) = S_top(i,j) ; S5(5) = S_top(i+1,j) |
| 526 | 0 | p5(1) = p(i,j,1) ; p5(5) = p(i+1,j,1) |
| 527 | 0 | do m=2,4 |
| 528 | 0 | wt_R = 0.25*real(m-1) |
| 529 | 0 | T5(m) = T5(1) + (T5(5)-T5(1))*wt_R |
| 530 | 0 | S5(m) = S5(1) + (S5(5)-S5(1))*wt_R |
| 531 | 0 | p5(m) = p5(1) + (p5(5)-p5(1))*wt_R |
| 532 | enddo !m | |
| 533 | 0 | call calculate_spec_vol(T5, S5, p5, SpV5, tv%eqn_of_state, spv_ref=alpha_ref) |
| 534 | ! See the Boussinesq calculation of inty_pa_cor for the derivation of the following expression. | |
| 535 | 0 | intx_za_cor(I,j) = C1_90 * (4.75*(SpV5(5)-SpV5(1)) + 5.5*(SpV5(4)-SpV5(2))) * dp_sfc |
| 536 | ! Note the consistency with the linear form below because (4.75 + 5.5/2) / 90 = 1/12 | |
| 537 | endif | |
| 538 | 0 | intx_za(I,j,1) = 0.5*(za(i,j,1) + za(i+1,j,1)) + intx_za_cor(I,j) |
| 539 | enddo ; enddo | |
| 540 | !$OMP parallel do default(shared) private(dp_sfc,T5,S5,p5,wt_R,SpV5) | |
| 541 | 0 | do J=Jsq,Jeq ; do i=is,ie |
| 542 | 0 | inty_za_cor(i,J) = 0.0 |
| 543 | 0 | dp_sfc = (p(i,j+1,1) - p(i,j,1)) |
| 544 | 0 | if (dp_sfc * (alpha_ref*dp_sfc - (za(i,j+1,1)-za(i,j,1))) > 0.0) then |
| 545 | ! The pressure/depth relationship has a positive implied specific volume. | |
| 546 | 0 | T5(1) = T_top(i,j) ; T5(5) = T_top(i,j+1) |
| 547 | 0 | S5(1) = S_top(i,j) ; S5(5) = S_top(i,j+1) |
| 548 | 0 | p5(1) = p(i,j,1) ; p5(5) = p(i,j+1,1) |
| 549 | 0 | do m=2,4 |
| 550 | 0 | wt_R = 0.25*real(m-1) |
| 551 | 0 | T5(m) = T5(1) + (T5(5)-T5(1))*wt_R |
| 552 | 0 | S5(m) = S5(1) + (S5(5)-S5(1))*wt_R |
| 553 | 0 | p5(m) = p5(1) + (p5(5)-p5(1))*wt_R |
| 554 | enddo !m | |
| 555 | 0 | call calculate_spec_vol(T5, S5, p5, SpV5, tv%eqn_of_state, spv_ref=alpha_ref) |
| 556 | ! See the Boussinesq calculation of inty_pa_cor for the derivation of the following expression. | |
| 557 | 0 | inty_za_cor(i,J) = C1_90 * (4.75*(SpV5(5)-SpV5(1)) + 5.5*(SpV5(4)-SpV5(2))) * dp_sfc |
| 558 | endif | |
| 559 | 0 | inty_za(i,J,1) = 0.5*(za(i,j,1) + za(i,j+1,1)) + inty_za_cor(i,J) |
| 560 | enddo ; enddo | |
| 561 | else | |
| 562 | ! This order of integrating upward and then downward again is necessary with | |
| 563 | ! a nonlinear equation of state, so that the surface geopotentials will go | |
| 564 | ! linearly between the values at thickness points, but the bottom geopotentials | |
| 565 | ! will not now be linear at the sub-grid-scale. Doing this ensures no motion | |
| 566 | ! with flat isopycnals, even with a nonlinear equation of state. | |
| 567 | !$OMP parallel do default(shared) | |
| 568 | 0 | do j=js,je ; do I=Isq,Ieq |
| 569 | 0 | intx_za(I,j,1) = 0.5*(za(i,j,1) + za(i+1,j,1)) |
| 570 | enddo ; enddo | |
| 571 | !$OMP parallel do default(shared) | |
| 572 | 0 | do J=Jsq,Jeq ; do i=is,ie |
| 573 | 0 | inty_za(i,J,1) = 0.5*(za(i,j,1) + za(i,j+1,1)) |
| 574 | enddo ; enddo | |
| 575 | endif | |
| 576 | ||
| 577 | 0 | do k=1,nz |
| 578 | !$OMP parallel do default(shared) | |
| 579 | 0 | do j=js,je ; do I=Isq,Ieq |
| 580 | 0 | intx_za(I,j,K+1) = intx_za(I,j,K) - intx_dza(I,j,k) |
| 581 | enddo ; enddo | |
| 582 | enddo | |
| 583 | 0 | do k=1,nz |
| 584 | !$OMP parallel do default(shared) | |
| 585 | 0 | do J=Jsq,Jeq ; do i=is,ie |
| 586 | 0 | inty_za(i,J,K+1) = inty_za(i,J,K) - inty_dza(i,J,k) |
| 587 | enddo ; enddo | |
| 588 | enddo | |
| 589 | ||
| 590 | 0 | if (CS%debug) then |
| 591 | call uvchksum("Prelim int[xy]_za", intx_za, inty_za, G%HI, haloshift=0, & | |
| 592 | 0 | symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%L_T_to_m_s**2) |
| 593 | call uvchksum("Prelim int[xy]_dza", intx_dza, inty_dza, G%HI, haloshift=0, & | |
| 594 | 0 | symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%L_T_to_m_s**2) |
| 595 | endif | |
| 596 | ||
| 597 | 0 | if (CS%reset_intxpa_integral) then |
| 598 | ! Having stored the pressure gradient info, we can work out where the first nonvanished layers is | |
| 599 | ! reset intx_za there, then adjust intx_za throughout the water column. | |
| 600 | ||
| 601 | ! Zero out the 2-d arrays that will be set from various reference interfaces. | |
| 602 | 0 | T_int_W(:,:) = 0.0 ; S_int_W(:,:) = 0.0 ; p_int_W(:,:) = 0.0 |
| 603 | 0 | T_int_E(:,:) = 0.0 ; S_int_E(:,:) = 0.0 ; p_int_E(:,:) = 0.0 |
| 604 | 0 | intx_za_nonlin(:,:) = 0.0 ; intx_za_cor_ri(:,:) = 0.0 ; dp_int_x(:,:) = 0.0 |
| 605 | 0 | do j=js,je ; do I=Isq,Ieq |
| 606 | 0 | seek_x_cor(I,j) = (G%mask2dCu(I,j) > 0.) |
| 607 | 0 | delta_p_x(I,j) = 0.0 |
| 608 | enddo ; enddo | |
| 609 | ||
| 610 | 0 | do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then |
| 611 | 0 | if ((p(i+1,j,2) >= p(i,j,1)) .and. (p(i,j,2) >= p(i+1,j,1))) then |
| 612 | ! This is the typical case in the open ocean, so use the topmost interface. | |
| 613 | 0 | T_int_W(I,j) = T_top(i,j) ; T_int_E(I,j) = T_top(i+1,j) |
| 614 | 0 | S_int_W(I,j) = S_top(i,j) ; S_int_E(I,j) = S_top(i+1,j) |
| 615 | 0 | p_int_W(I,j) = p(i,j,1) ; p_int_E(I,j) = p(i+1,j,1) |
| 616 | 0 | intx_za_nonlin(I,j) = intx_za(I,j,1) - 0.5*(za(i,j,1) + za(i+1,j,1)) |
| 617 | 0 | dp_int_x(I,j) = p(i+1,j,1)-p(i,j,1) |
| 618 | 0 | seek_x_cor(I,j) = .false. |
| 619 | endif | |
| 620 | endif ; enddo ; enddo | |
| 621 | ||
| 622 | 0 | do k=1,nz |
| 623 | 0 | do_more_k = .false. |
| 624 | 0 | do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then |
| 625 | ! Find the topmost layer for which both sides are nonvanished and mass-weighting is not | |
| 626 | ! activated in the subgrid interpolation. | |
| 627 | 0 | if (((h(i,j,k) > CS%h_nonvanished) .and. (h(i+1,j,k) > CS%h_nonvanished)) .and. & |
| 628 | (max(0., p(i,j,1)-p(i+1,j,K+1), p(i+1,j,1)-p(i,j,K+1)) <= 0.0)) then | |
| 629 | ! Store properties at the bottom of this cell to get a "good estimate" for intxpa at | |
| 630 | ! the interface below this cell (it might have quadratic pressure dependence if sloped) | |
| 631 | 0 | T_int_W(I,j) = T_b(i,j,k) ; T_int_E(I,j) = T_b(i+1,j,k) |
| 632 | 0 | S_int_W(I,j) = S_b(i,j,k) ; S_int_E(I,j) = S_b(i+1,j,k) |
| 633 | 0 | p_int_W(I,j) = p(i,j,K+1) ; p_int_E(I,j) = p(i+1,j,K+1) |
| 634 | ||
| 635 | 0 | intx_za_nonlin(I,j) = intx_za(I,j,K+1) - 0.5*(za(i,j,K+1) + za(i+1,j,K+1)) |
| 636 | 0 | dp_int_x(I,j) = p(i+1,j,K+1)-p(i,j,K+1) |
| 637 | 0 | seek_x_cor(I,j) = .false. |
| 638 | else | |
| 639 | 0 | do_more_k = .true. |
| 640 | endif | |
| 641 | endif ; enddo ; enddo | |
| 642 | 0 | if (.not.do_more_k) exit ! All reference interfaces have been found, so stop working downward. |
| 643 | enddo | |
| 644 | ||
| 645 | 0 | if (do_more_k) then |
| 646 | 0 | if (CS%reset_intxpa_flattest) then |
| 647 | ! There are still points where a correction is needed, so use flattest interface | |
| 648 | 0 | do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then |
| 649 | ! choose top layer first | |
| 650 | 0 | T_int_W(I,j) = T_top(i,j) ; T_int_E(I,j) = T_top(i+1,j) |
| 651 | 0 | S_int_W(I,j) = S_top(i,j) ; S_int_E(I,j) = S_top(i+1,j) |
| 652 | 0 | p_int_W(I,j) = p(i,j,1) ; p_int_E(I,j) = p(i+1,j,1) |
| 653 | 0 | intx_za_nonlin(I,j) = intx_za(I,j,1) - 0.5*(za(i,j,1) + za(i+1,j,1)) |
| 654 | 0 | dp_int_x(I,j) = p(i+1,j,1)-p(i,j,1) |
| 655 | 0 | delta_p_x(I,j) = abs(p(i+1,j,1)-p(i,j,1)) |
| 656 | 0 | do k=1,nz |
| 657 | 0 | if (abs(p(i+1,j,k+1)-p(i,j,k+1)) < delta_p_x(I,j)) then |
| 658 | ! bottom of layer is less sloped than top. Use this layer | |
| 659 | 0 | delta_p_x(I,j) = abs(p(i+1,j,k+1)-p(i,j,k+1)) |
| 660 | 0 | T_int_W(I,j) = T_b(i,j,k) ; T_int_E(I,j) = T_b(i+1,j,k) |
| 661 | 0 | S_int_W(I,j) = S_b(i,j,k) ; S_int_E(I,j) = S_b(i+1,j,k) |
| 662 | 0 | p_int_W(I,j) = p(i,j,K+1) ; p_int_E(I,j) = p(i+1,j,K+1) |
| 663 | 0 | intx_za_nonlin(I,j) = intx_za(I,j,K+1) - 0.5*(za(i,j,K+1) + za(i+1,j,K+1)) |
| 664 | 0 | dp_int_x(I,j) = p(i+1,j,K+1)-p(i,j,K+1) |
| 665 | endif | |
| 666 | enddo | |
| 667 | 0 | seek_x_cor(I,j) = .false. |
| 668 | endif ; enddo ; enddo | |
| 669 | else | |
| 670 | ! There are still points where a correction is needed, so use the top interface. | |
| 671 | 0 | do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then |
| 672 | 0 | T_int_W(I,j) = T_top(i,j) ; T_int_E(I,j) = T_top(i+1,j) |
| 673 | 0 | S_int_W(I,j) = S_top(i,j) ; S_int_E(I,j) = S_top(i+1,j) |
| 674 | 0 | p_int_W(I,j) = p(i,j,1) ; p_int_E(I,j) = p(i+1,j,1) |
| 675 | 0 | intx_za_nonlin(I,j) = intx_za(I,j,1) - 0.5*(za(i,j,1) + za(i+1,j,1)) |
| 676 | 0 | dp_int_x(I,j) = p(i+1,j,1)-p(i,j,1) |
| 677 | 0 | seek_x_cor(I,j) = .false. |
| 678 | endif ; enddo ; enddo | |
| 679 | endif | |
| 680 | endif | |
| 681 | ||
| 682 | 0 | do j=js,je |
| 683 | 0 | do I=Isq,Ieq |
| 684 | ! This expression assumes that temperature and salinity vary linearly with pressure | |
| 685 | ! between the corners of the reference interfaces found above to get a correction to | |
| 686 | ! intx_pa that takes nonlinearities in the equation of state into account. | |
| 687 | ! It is derived from a 5 point quadrature estimate of the integral with a large-scale | |
| 688 | ! linear correction so that the pressures and heights match at the end-points. It turns | |
| 689 | ! out that this linear correction cancels out the mid-point specific volume. | |
| 690 | ! This can be used without masking because dp_int_x and intx_za_nonlin are 0 over land. | |
| 691 | 0 | T5(1) = T_Int_W(I,j) ; S5(1) = S_Int_W(I,j) ; p5(1) = p_Int_W(I,j) |
| 692 | 0 | T5(5) = T_Int_E(I,j) ; S5(5) = S_Int_E(I,j) ; p5(5) = p_Int_E(I,j) |
| 693 | 0 | T5(2) = 0.25*(3.0*T5(1) + T5(5)) ; T5(4) = 0.25*(3.0*T5(5) + T5(1)) ; T5(3) = 0.5*(T5(5) + T5(1)) |
| 694 | 0 | S5(2) = 0.25*(3.0*S5(1) + S5(5)) ; S5(4) = 0.25*(3.0*S5(5) + S5(1)) ; S5(3) = 0.5*(S5(5) + S5(1)) |
| 695 | 0 | p5(2) = 0.25*(3.0*p5(1) + p5(5)) ; p5(4) = 0.25*(3.0*p5(5) + p5(1)) ; p5(3) = 0.5*(p5(5) + p5(1)) |
| 696 | 0 | call calculate_spec_vol(T5, S5, p5, SpV5, tv%eqn_of_state, spv_ref=alpha_ref) |
| 697 | ||
| 698 | ! Note the consistency with the linear form below because (4.75 + 5.5/2) / 90 = 1/12 | |
| 699 | intx_za_cor_ri(I,j) = C1_90 * (4.75*(SpV5(5)-SpV5(1)) + 5.5*(SpV5(4)-SpV5(2))) * & | |
| 700 | 0 | dp_int_x(I,j) - intx_za_nonlin(I,j) |
| 701 | enddo | |
| 702 | enddo | |
| 703 | ||
| 704 | ! Repeat the calculations above for v-velocity points. | |
| 705 | 0 | T_int_S(:,:) = 0.0 ; S_int_S(:,:) = 0.0 ; p_int_S(:,:) = 0.0 |
| 706 | 0 | T_int_N(:,:) = 0.0 ; S_int_N(:,:) = 0.0 ; p_int_N(:,:) = 0.0 |
| 707 | 0 | inty_za_nonlin(:,:) = 0.0 ; inty_za_cor_ri(:,:) = 0.0 ; dp_int_y(:,:) = 0.0 |
| 708 | 0 | do J=Jsq,Jeq ; do i=is,ie |
| 709 | 0 | seek_y_cor(i,J) = (G%mask2dCv(i,J) > 0.) |
| 710 | 0 | delta_p_y(i,J) = 0.0 |
| 711 | enddo ; enddo | |
| 712 | ||
| 713 | 0 | do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then |
| 714 | 0 | if ((p(i,j+1,2) >= p(i,j,1)) .and. (p(i,j,2) >= p(i,j+1,1))) then |
| 715 | ! This is the typical case in the open ocean, so use the topmost interface. | |
| 716 | 0 | T_int_S(i,J) = T_top(i,j) ; T_int_N(i,J) = T_top(i,j+1) |
| 717 | 0 | S_int_S(i,J) = S_top(i,j) ; S_int_N(i,J) = S_top(i,j+1) |
| 718 | 0 | p_int_S(i,J) = p(i,j,1) ; p_int_N(i,J) = p(i,j+1,1) |
| 719 | 0 | inty_za_nonlin(i,J) = inty_za(i,J,1) - 0.5*(za(i,j,1) + za(i,j+1,1)) |
| 720 | 0 | dp_int_y(i,J) = p(i,j+1,1) - p(i,j,1) |
| 721 | 0 | seek_y_cor(i,J) = .false. |
| 722 | endif | |
| 723 | endif ; enddo ; enddo | |
| 724 | ||
| 725 | 0 | do k=1,nz |
| 726 | 0 | do_more_k = .false. |
| 727 | 0 | do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then |
| 728 | ! Find the topmost layer for which both sides are nonvanished and mass-weighting is not | |
| 729 | ! activated in the subgrid interpolation. | |
| 730 | 0 | if (((h(i,j,k) > CS%h_nonvanished) .and. (h(i,j+1,k) > CS%h_nonvanished)) .and. & |
| 731 | (max(0., p(i,j,1)-p(i,j+1,K+1), p(i,j+1,1)-p(i,j,K+1)) <= 0.0)) then | |
| 732 | ! Store properties at the bottom of this cell to get a "good estimate" for intypa at | |
| 733 | ! the interface below this cell (it might have quadratic pressure dependence if sloped) | |
| 734 | 0 | T_int_S(i,J) = T_b(i,j,k) ; T_int_N(i,J) = T_b(i,j+1,k) |
| 735 | 0 | S_int_S(i,J) = S_b(i,j,k) ; S_int_N(i,J) = S_b(i,j+1,k) |
| 736 | 0 | p_int_S(i,J) = p(i,j,K+1) ; p_int_N(i,J) = p(i,j+1,K+1) |
| 737 | 0 | inty_za_nonlin(i,J) = inty_za(i,J,K+1) - 0.5*(za(i,j,K+1) + za(i,j+1,K+1)) |
| 738 | 0 | dp_int_y(i,J) = p(i,j+1,K+1) - p(i,j,K+1) |
| 739 | 0 | seek_y_cor(i,J) = .false. |
| 740 | else | |
| 741 | 0 | do_more_k = .true. |
| 742 | endif | |
| 743 | endif ; enddo ; enddo | |
| 744 | 0 | if (.not.do_more_k) exit ! All reference interfaces have been found, so stop working downward. |
| 745 | enddo | |
| 746 | ||
| 747 | 0 | if (do_more_k) then |
| 748 | 0 | if (CS%reset_intxpa_flattest) then |
| 749 | ! There are still points where a correction is needed, so use flattest interface. | |
| 750 | 0 | do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then |
| 751 | ! choose top interface first | |
| 752 | 0 | T_int_S(i,J) = T_top(i,j) ; T_int_N(i,J) = T_top(i,j+1) |
| 753 | 0 | S_int_S(i,J) = S_top(i,j) ; S_int_N(i,J) = S_top(i,j+1) |
| 754 | 0 | p_int_S(i,J) = p(i,j,1) ; p_int_N(i,J) = p(i,j+1,1) |
| 755 | 0 | inty_za_nonlin(i,J) = inty_za(i,J,1) - 0.5*(za(i,j,1) + za(i,j+1,1)) |
| 756 | 0 | dp_int_y(i,J) = p(i,j+1,1) - p(i,j,1) |
| 757 | 0 | delta_p_y(i,J) = abs(p(i,j+1,1)-p(i,j,1)) |
| 758 | 0 | do k=1,nz |
| 759 | 0 | if (abs(p(i,j+1,k+1)-p(i,j,k+1)) < delta_p_y(i,J)) then |
| 760 | ! bottom of layer is less sloped than top. Use this layer | |
| 761 | 0 | delta_p_y(i,J) = abs(p(i,j+1,k+1)-p(i,j,k+1)) |
| 762 | 0 | T_int_S(i,J) = T_b(i,j,k) ; T_int_N(i,J) = T_b(i,j+1,k) |
| 763 | 0 | S_int_S(i,J) = S_b(i,j,k) ; S_int_N(i,J) = S_b(i,j+1,k) |
| 764 | 0 | p_int_S(i,J) = p(i,j,K+1) ; p_int_N(i,J) = p(i,j+1,K+1) |
| 765 | 0 | inty_za_nonlin(i,J) = inty_za(i,J,K+1) - 0.5*(za(i,j,K+1) + za(i,j+1,K+1)) |
| 766 | 0 | dp_int_y(i,J) = p(i,j+1,K+1) - p(i,j,K+1) |
| 767 | endif | |
| 768 | enddo | |
| 769 | 0 | seek_y_cor(i,J) = .false. |
| 770 | endif ; enddo ; enddo | |
| 771 | else | |
| 772 | ! There are still points where a correction is needed, so use the top interface. | |
| 773 | 0 | do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then |
| 774 | 0 | T_int_S(i,J) = T_top(i,j) ; T_int_N(i,J) = T_top(i,j+1) |
| 775 | 0 | S_int_S(i,J) = S_top(i,j) ; S_int_N(i,J) = S_top(i,j+1) |
| 776 | 0 | p_int_S(i,J) = p(i,j,1) ; p_int_N(i,J) = p(i,j+1,1) |
| 777 | 0 | inty_za_nonlin(i,J) = inty_za(i,J,1) - 0.5*(za(i,j,1) + za(i,j+1,1)) |
| 778 | 0 | dp_int_y(i,J) = p(i,j+1,1) - p(i,j,1) |
| 779 | 0 | seek_y_cor(i,J) = .false. |
| 780 | endif ; enddo ; enddo | |
| 781 | endif | |
| 782 | endif | |
| 783 | ||
| 784 | 0 | do J=Jsq,Jeq |
| 785 | 0 | do i=is,ie |
| 786 | ! This expression assumes that temperature and salinity vary linearly with pressure | |
| 787 | ! between the corners of the reference interfaces found above to get a correction to | |
| 788 | ! intx_pa that takes nonlinearities in the equation of state into account. | |
| 789 | ! It is derived from a 5 point quadrature estimate of the integral with a large-scale | |
| 790 | ! linear correction so that the pressures and heights match at the end-points. It turns | |
| 791 | ! out that this linear correction cancels out the mid-point specific volume. | |
| 792 | ! This can be used without masking because dp_int_x and intx_za_nonlin are 0 over land. | |
| 793 | 0 | T5(1) = T_Int_S(i,J) ; S5(1) = S_Int_S(i,J) ; p5(1) = p_Int_S(i,J) |
| 794 | 0 | T5(5) = T_Int_N(i,J) ; S5(5) = S_Int_N(i,J) ; p5(5) = p_Int_N(i,J) |
| 795 | 0 | T5(2) = 0.25*(3.0*T5(1) + T5(5)) ; T5(4) = 0.25*(3.0*T5(5) + T5(1)) ; T5(3) = 0.5*(T5(5) + T5(1)) |
| 796 | 0 | S5(2) = 0.25*(3.0*S5(1) + S5(5)) ; S5(4) = 0.25*(3.0*S5(5) + S5(1)) ; S5(3) = 0.5*(S5(5) + S5(1)) |
| 797 | 0 | p5(2) = 0.25*(3.0*p5(1) + p5(5)) ; p5(4) = 0.25*(3.0*p5(5) + p5(1)) ; p5(3) = 0.5*(p5(5) + p5(1)) |
| 798 | 0 | call calculate_spec_vol(T5, S5, p5, SpV5, tv%eqn_of_state, spv_ref=alpha_ref) |
| 799 | ||
| 800 | ! Note the consistency with the linear form below because (4.75 + 5.5/2) / 90 = 1/12 | |
| 801 | inty_za_cor_ri(i,J) = C1_90 * (4.75*(SpV5(5)-SpV5(1)) + 5.5*(SpV5(4)-SpV5(2))) * & | |
| 802 | 0 | dp_int_y(i,J) - inty_za_nonlin(i,J) |
| 803 | enddo | |
| 804 | enddo | |
| 805 | ||
| 806 | 0 | if (CS%debug) then |
| 807 | call uvchksum("Pre-reset int[xy]_za", intx_za, inty_za, G%HI, haloshift=0, & | |
| 808 | 0 | symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%L_T_to_m_s**2) |
| 809 | call uvchksum("int[xy]_za_cor", intx_za_cor_ri, inty_za_cor_ri, G%HI, haloshift=0, & | |
| 810 | 0 | symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%L_T_to_m_s**2) |
| 811 | call uvchksum("int[xy]_za_nonlin", intx_za_nonlin, inty_za_nonlin, G%HI, haloshift=0, & | |
| 812 | 0 | symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%L_T_to_m_s**2) |
| 813 | call uvchksum("dp_int_[xy]", dp_int_x, dp_int_y, G%HI, haloshift=0, & | |
| 814 | 0 | symmetric=G%Domain%symmetric, unscale=US%RL2_T2_to_Pa) |
| 815 | endif | |
| 816 | ||
| 817 | ! Correct intx_pa and inty_pa at each interface using vertically constant corrections. | |
| 818 | 0 | do K=1,nz+1 ; do j=js,je ; do I=Isq,Ieq |
| 819 | 0 | intx_za(I,j,K) = intx_za(I,j,K) + intx_za_cor_ri(I,j) |
| 820 | enddo ; enddo ; enddo | |
| 821 | ||
| 822 | 0 | do K=1,nz+1 ; do J=Jsq,Jeq ; do i=is,ie |
| 823 | 0 | inty_za(i,J,K) = inty_za(i,J,K) + inty_za_cor_ri(i,J) |
| 824 | enddo ; enddo ; enddo | |
| 825 | ||
| 826 | 0 | if (CS%debug) then |
| 827 | call uvchksum("Post-reset int[xy]_za", intx_za, inty_za, G%HI, haloshift=0, & | |
| 828 | 0 | symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%L_T_to_m_s**2) |
| 829 | endif | |
| 830 | ||
| 831 | endif ! intx_za and inty_za have now been reset to reflect the properties of an unimpeded interface. | |
| 832 | ||
| 833 | !$OMP parallel do default(shared) private(dp) | |
| 834 | 0 | do k=1,nz |
| 835 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 836 | 0 | dp(i,j) = H_to_RL2_T2 * h(i,j,k) |
| 837 | enddo ; enddo | |
| 838 | ||
| 839 | ! Find the horizontal pressure gradient accelerations. | |
| 840 | ! These expressions for the accelerations have been carefully checked in | |
| 841 | ! a set of idealized cases, and should be bug-free. | |
| 842 | 0 | do j=js,je ; do I=Isq,Ieq |
| 843 | PFu(I,j,k) = ( ((za(i,j,K+1)*dp(i,j) + intp_dza(i,j,k)) - & | |
| 844 | (za(i+1,j,K+1)*dp(i+1,j) + intp_dza(i+1,j,k))) + & | |
| 845 | ((dp(i+1,j) - dp(i,j)) * intx_za(I,j,K+1) - & | |
| 846 | (p(i+1,j,K) - p(i,j,K)) * intx_dza(I,j,k)) ) * & | |
| 847 | 0 | (2.0*G%IdxCu(I,j) / ((dp(i,j) + dp(i+1,j)) + dp_neglect)) |
| 848 | enddo ; enddo | |
| 849 | ||
| 850 | 0 | do J=Jsq,Jeq ; do i=is,ie |
| 851 | PFv(i,J,k) = (((za(i,j,K+1)*dp(i,j) + intp_dza(i,j,k)) - & | |
| 852 | (za(i,j+1,K+1)*dp(i,j+1) + intp_dza(i,j+1,k))) + & | |
| 853 | ((dp(i,j+1) - dp(i,j)) * inty_za(i,J,K+1) - & | |
| 854 | (p(i,j+1,K) - p(i,j,K)) * inty_dza(i,J,k))) * & | |
| 855 | 0 | (2.0*G%IdyCv(i,J) / ((dp(i,j) + dp(i,j+1)) + dp_neglect)) |
| 856 | enddo ; enddo | |
| 857 | enddo | |
| 858 | ||
| 859 | 0 | if (CS%GFS_scale < 1.0) then |
| 860 | ! Adjust the Montgomery potential to make this a reduced gravity model. | |
| 861 | 0 | if (use_EOS) then |
| 862 | !$OMP parallel do default(shared) private(rho_in_situ) | |
| 863 | 0 | do j=Jsq,Jeq+1 |
| 864 | call calculate_density(tv_tmp%T(:,j,1), tv_tmp%S(:,j,1), p(:,j,1), rho_in_situ, & | |
| 865 | 0 | tv%eqn_of_state, EOSdom) |
| 866 | ||
| 867 | 0 | do i=Isq,Ieq+1 |
| 868 | 0 | dM(i,j) = (CS%GFS_scale - 1.0) * (p(i,j,1)*(1.0/rho_in_situ(i) - alpha_ref) + za(i,j,1)) |
| 869 | enddo | |
| 870 | enddo | |
| 871 | else | |
| 872 | !$OMP parallel do default(shared) | |
| 873 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 874 | 0 | dM(i,j) = (CS%GFS_scale - 1.0) * (p(i,j,1)*(1.0/GV%Rlay(1) - alpha_ref) + za(i,j,1)) |
| 875 | enddo ; enddo | |
| 876 | endif | |
| 877 | ||
| 878 | !$OMP parallel do default(shared) | |
| 879 | 0 | do k=1,nz |
| 880 | 0 | do j=js,je ; do I=Isq,Ieq |
| 881 | 0 | PFu(I,j,k) = PFu(I,j,k) - (dM(i+1,j) - dM(i,j)) * G%IdxCu(I,j) |
| 882 | enddo ; enddo | |
| 883 | 0 | do J=Jsq,Jeq ; do i=is,ie |
| 884 | 0 | PFv(i,J,k) = PFv(i,J,k) - (dM(i,j+1) - dM(i,j)) * G%IdyCv(i,J) |
| 885 | enddo ; enddo | |
| 886 | enddo | |
| 887 | endif | |
| 888 | ||
| 889 | 0 | if (present(pbce)) then |
| 890 | 0 | call set_pbce_nonBouss(p, tv_tmp, G, GV, US, CS%GFS_scale, pbce) |
| 891 | endif | |
| 892 | ||
| 893 | 0 | if (present(eta)) then |
| 894 | 0 | Pa_to_H = 1.0 / (GV%g_Earth * GV%H_to_RZ) |
| 895 | 0 | if (use_p_atm) then |
| 896 | !$OMP parallel do default(shared) | |
| 897 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 898 | 0 | eta(i,j) = (p(i,j,nz+1) - p_atm(i,j))*Pa_to_H ! eta has the same units as h. |
| 899 | enddo ; enddo | |
| 900 | else | |
| 901 | !$OMP parallel do default(shared) | |
| 902 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 903 | 0 | eta(i,j) = p(i,j,nz+1)*Pa_to_H ! eta has the same units as h. |
| 904 | enddo ; enddo | |
| 905 | endif | |
| 906 | endif | |
| 907 | ||
| 908 | 0 | if (CS%id_MassWt_u>0) call post_data(CS%id_MassWt_u, MassWt_u, CS%diag) |
| 909 | 0 | if (CS%id_MassWt_v>0) call post_data(CS%id_MassWt_v, MassWt_v, CS%diag) |
| 910 | ||
| 911 | ! Diagnostics for tidal forcing and SAL height anomaly | |
| 912 | 0 | if (CS%id_e_tide>0) then |
| 913 | ! To be consistent with old runs, tidal forcing diagnostic also includes total SAL. | |
| 914 | ! New diagnostics are given for each individual field. | |
| 915 | 0 | if (CS%tides_answer_date>20230630) then ; do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 916 | 0 | e_sal_and_tide(i,j) = e_sal(i,j) + e_tidal_eq(i,j) + e_tidal_sal(i,j) |
| 917 | enddo ; enddo ; endif | |
| 918 | 0 | call post_data(CS%id_e_tide, e_sal_and_tide, CS%diag) |
| 919 | endif | |
| 920 | 0 | if (CS%id_e_sal>0) call post_data(CS%id_e_sal, e_sal, CS%diag) |
| 921 | 0 | if (CS%id_e_tidal_eq>0) call post_data(CS%id_e_tidal_eq, e_tidal_eq, CS%diag) |
| 922 | 0 | if (CS%id_e_tidal_sal>0) call post_data(CS%id_e_tidal_sal, e_tidal_sal, CS%diag) |
| 923 | ||
| 924 | ! Diagnostics for tidal forcing and SAL horizontal gradients | |
| 925 | 0 | if (CS%calculate_SAL .and. (associated(ADp%sal_u) .or. associated(ADp%sal_v))) then |
| 926 | 0 | if (CS%tides) then ; do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 927 | 0 | e_sal(i,j) = e_sal(i,j) + e_tidal_sal(i,j) |
| 928 | enddo ; enddo ; endif | |
| 929 | 0 | if (associated(ADp%sal_u)) then ; do k=1,nz ; do j=js,je ; do I=Isq,Ieq |
| 930 | 0 | ADp%sal_u(I,j,k) = (e_sal(i+1,j) - e_sal(i,j)) * GV%g_Earth * G%IdxCu(I,j) |
| 931 | enddo ; enddo ; enddo ; endif | |
| 932 | 0 | if (associated(ADp%sal_v)) then ; do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie |
| 933 | 0 | ADp%sal_v(i,J,k) = (e_sal(i,j+1) - e_sal(i,j)) * GV%g_Earth * G%IdyCv(i,J) |
| 934 | enddo ; enddo ; enddo ; endif | |
| 935 | 0 | if (CS%id_sal_u>0) call post_data(CS%id_sal_u, ADp%sal_u, CS%diag) |
| 936 | 0 | if (CS%id_sal_v>0) call post_data(CS%id_sal_v, ADp%sal_v, CS%diag) |
| 937 | endif | |
| 938 | ||
| 939 | 0 | if (CS%tides .and. (associated(ADp%tides_u) .or. associated(ADp%tides_v))) then |
| 940 | 0 | if (associated(ADp%tides_u)) then ; do k=1,nz ; do j=js,je ; do I=Isq,Ieq |
| 941 | 0 | ADp%tides_u(I,j,k) = (e_tidal_eq(i+1,j) - e_tidal_eq(i,j)) * GV%g_Earth * G%IdxCu(I,j) |
| 942 | enddo ; enddo ; enddo ; endif | |
| 943 | 0 | if (associated(ADp%tides_v)) then ; do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie |
| 944 | 0 | ADp%tides_v(i,J,k) = (e_tidal_eq(i,j+1) - e_tidal_eq(i,j)) * GV%g_Earth * G%IdyCv(i,J) |
| 945 | enddo ; enddo ; enddo ; endif | |
| 946 | 0 | if (CS%id_tides_u>0) call post_data(CS%id_tides_u, ADp%tides_u, CS%diag) |
| 947 | 0 | if (CS%id_tides_v>0) call post_data(CS%id_tides_v, ADp%tides_v, CS%diag) |
| 948 | endif | |
| 949 | 0 | end subroutine PressureForce_FV_nonBouss |
| 950 | ||
| 951 | !> \brief Boussinesq analytically-integrated finite volume form of pressure gradient | |
| 952 | !! | |
| 953 | !! Determines the acceleration due to hydrostatic pressure forces, using | |
| 954 | !! the finite volume form of the terms and analytic integrals in depth. | |
| 955 | !! | |
| 956 | !! To work, the following fields must be set outside of the usual (is:ie,js:je) | |
| 957 | !! range before this subroutine is called: | |
| 958 | !! h(isB:ie+1,jsB:je+1), T(isB:ie+1,jsB:je+1), and S(isB:ie+1,jsB:je+1). | |
| 959 | 48 | subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, ADp, p_atm, pbce, eta) |
| 960 | type(ocean_grid_type), intent(in) :: G !< Ocean grid structure | |
| 961 | type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure | |
| 962 | type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type | |
| 963 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> m] | |
| 964 | type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamic variables | |
| 965 | real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(out) :: PFu !< Zonal acceleration [L T-2 ~> m s-2] | |
| 966 | real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(out) :: PFv !< Meridional acceleration [L T-2 ~> m s-2] | |
| 967 | type(PressureForce_FV_CS), intent(in) :: CS !< Finite volume PGF control structure | |
| 968 | type(ALE_CS), pointer :: ALE_CSp !< ALE control structure | |
| 969 | type(accel_diag_ptrs), pointer :: ADp !< Acceleration diagnostic pointers | |
| 970 | real, dimension(:,:), pointer :: p_atm !< The pressure at the ice-ocean | |
| 971 | !! or atmosphere-ocean interface [R L2 T-2 ~> Pa]. | |
| 972 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), optional, intent(out) :: pbce !< The baroclinic pressure | |
| 973 | !! anomaly in each layer due to eta anomalies | |
| 974 | !! [L2 T-2 H-1 ~> m s-2]. | |
| 975 | real, dimension(SZI_(G),SZJ_(G)), optional, intent(out) :: eta !< The sea-surface height used to | |
| 976 | !! calculate PFu and PFv [H ~> m], with any | |
| 977 | !! tidal contributions. | |
| 978 | ! Local variables | |
| 979 | 48 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: e ! Interface height in depth units [Z ~> m]. |
| 980 | real, dimension(SZI_(G),SZJ_(G)) :: & | |
| 981 | 48 | e_sal_and_tide, & ! The summation of self-attraction and loading and tidal forcing [Z ~> m]. |
| 982 | 48 | e_sal, & ! The bottom geopotential anomaly due to self-attraction and loading [Z ~> m]. |
| 983 | 48 | e_tidal_eq, & ! The bottom geopotential anomaly due to tidal forces from astronomical sources |
| 984 | ! [Z ~> m]. | |
| 985 | 48 | e_tidal_sal, & ! The bottom geopotential anomaly due to harmonic self-attraction and loading |
| 986 | ! specific to tides [Z ~> m]. | |
| 987 | 48 | Z_0p, & ! The height at which the pressure used in the equation of state is 0 [Z ~> m] |
| 988 | 48 | SSH, & ! Sea surfae height anomaly for self-attraction and loading. Used if |
| 989 | ! CALCULATE_SAL is True and SAL_USE_BPA is False [Z ~> m]. | |
| 990 | 48 | pbot, & ! Total bottom pressure for self-attraction and loading. Used if |
| 991 | ! CALCULATE_SAL is True and SAL_USE_BPA is True [R L2 T-2 ~> Pa]. | |
| 992 | 48 | dM ! The barotropic adjustment to the Montgomery potential to |
| 993 | ! account for a reduced gravity model [L2 T-2 ~> m2 s-2]. | |
| 994 | real, dimension(SZI_(G)) :: & | |
| 995 | 48 | Rho_cv_BL ! The coordinate potential density in the deepest variable |
| 996 | ! density near-surface layer [R ~> kg m-3]. | |
| 997 | real, dimension(SZI_(G),SZJ_(G)) :: & | |
| 998 | 48 | dz_geo ! The change in geopotential thickness through a layer [L2 T-2 ~> m2 s-2]. |
| 999 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: & | |
| 1000 | 48 | pa ! The pressure anomaly (i.e. pressure + g*RHO_0*e) at the |
| 1001 | ! the interface atop a layer [R L2 T-2 ~> Pa]. | |
| 1002 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: & | |
| 1003 | 48 | dpa, & ! The change in pressure anomaly between the top and bottom |
| 1004 | ! of a layer [R L2 T-2 ~> Pa]. | |
| 1005 | 48 | intz_dpa ! The vertical integral in depth of the pressure anomaly less the |
| 1006 | ! pressure anomaly at the top of the layer [H R L2 T-2 ~> m Pa]. | |
| 1007 | real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1) :: & | |
| 1008 | 48 | intx_pa ! The zonal integral of the pressure anomaly along the interface |
| 1009 | ! atop a layer, divided by the grid spacing [R L2 T-2 ~> Pa]. | |
| 1010 | real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: & | |
| 1011 | 48 | intx_dpa ! The change in intx_pa through a layer [R L2 T-2 ~> Pa]. |
| 1012 | real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1) :: & | |
| 1013 | 48 | inty_pa ! The meridional integral of the pressure anomaly along the |
| 1014 | ! interface atop a layer, divided by the grid spacing [R L2 T-2 ~> Pa]. | |
| 1015 | real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: & | |
| 1016 | 48 | inty_dpa ! The change in inty_pa through a layer [R L2 T-2 ~> Pa]. |
| 1017 | real, dimension(SZIB_(G),SZJ_(G)) :: & | |
| 1018 | 48 | intx_pa_cor ! Correction for curvature in intx_pa [R L2 T-2 ~> Pa] |
| 1019 | real, dimension(SZI_(G),SZJB_(G)) :: & | |
| 1020 | 48 | inty_pa_cor ! Correction for curvature in inty_pa [R L2 T-2 ~> Pa] |
| 1021 | ||
| 1022 | ! These variables are used with reset_intxpa_integral. The values are taken from different | |
| 1023 | ! interfaces as a function of position. | |
| 1024 | real, dimension(SZIB_(G),SZJ_(G)) :: & | |
| 1025 | 48 | T_int_W, T_int_E, & ! Temperatures on the reference interface to the east and west of a u-point [C ~> degC] |
| 1026 | 48 | S_int_W, S_int_E, & ! Salinities on the reference interface to the east and west of a u-point [S ~> ppt] |
| 1027 | 48 | p_int_W, p_int_E, & ! Pressures on the reference interface to the east and west of a u-point [R L2 T-2 ~> Pa] |
| 1028 | 48 | intx_pa_nonlin, & ! Deviations in the previous version of intx_pa for the reference interface |
| 1029 | ! from the value that would be obtained from assuming that pressure varies | |
| 1030 | ! linearly with depth along that interface [R L2 T-2 ~> Pa]. | |
| 1031 | 48 | dgeo_x, & ! The change in x in geopotenial height along the reference interface [L2 T-2 ~> m2 s-2] |
| 1032 | 48 | intx_pa_cor_ri ! The correction to intx_pa based on the reference interface calculations [R L2 T-2 ~> Pa] |
| 1033 | real, dimension(SZI_(G),SZJB_(G)) :: & | |
| 1034 | 48 | T_int_S, T_int_N, & ! Temperatures on the reference interface to the north and south of a v-point [C ~> degC] |
| 1035 | 48 | S_int_S, S_int_N, & ! Salinities on the reference interface to the north and south of a v-point [S ~> ppt] |
| 1036 | 48 | p_int_S, p_int_N, & ! Pressures on the reference interface to the north and south of a v-point [R L2 T-2 ~> Pa] |
| 1037 | 48 | inty_pa_nonlin, & ! Deviations in the previous version of intx_pa for the reference interface |
| 1038 | ! from the value that would be obtained from assuming that pressure varies | |
| 1039 | ! linearly with depth along that interface [R L2 T-2 ~> Pa]. | |
| 1040 | 48 | dgeo_y, & ! The change in y in geopotenial height along the reference interface [L2 T-2 ~> m2 s-2] |
| 1041 | 48 | inty_pa_cor_ri ! The correction to inty_pa based on the reference interface calculations [R L2 T-2 ~> Pa] |
| 1042 | logical, dimension(SZIB_(G),SZJ_(G)) :: & | |
| 1043 | 48 | seek_x_cor ! If true, try to find a u-point interface that would provide a better estimate |
| 1044 | ! of the curvature terms in the intx_pa. | |
| 1045 | logical, dimension(SZI_(G),SZJB_(G)) :: & | |
| 1046 | 48 | seek_y_cor ! If true, try to find a v-point interface that would provide a better estimate |
| 1047 | ! of the curvature terms in the inty_pa. | |
| 1048 | real, dimension(SZIB_(G),SZJ_(G)) :: & | |
| 1049 | 48 | delta_z_x ! If using flattest interface for reset integral, store x interface differences [Z ~> m] |
| 1050 | real, dimension(SZI_(G),SZJB_(G)) :: & | |
| 1051 | 48 | delta_z_y ! If using flattest interface for reset integral, store y interface differences [Z ~> m] |
| 1052 | ||
| 1053 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), target :: & | |
| 1054 | 48 | T_tmp, & ! Temporary array of temperatures where layers that are lighter |
| 1055 | ! than the mixed layer have the mixed layer's properties [C ~> degC]. | |
| 1056 | 48 | S_tmp ! Temporary array of salinities where layers that are lighter |
| 1057 | ! than the mixed layer have the mixed layer's properties [S ~> ppt]. | |
| 1058 | real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: & | |
| 1059 | 48 | S_t, S_b, & ! Top and bottom edge values for linear reconstructions |
| 1060 | ! of salinity within each layer [S ~> ppt]. | |
| 1061 | 48 | T_t, T_b ! Top and bottom edge values for linear reconstructions |
| 1062 | ! of temperature within each layer [C ~> degC]. | |
| 1063 | real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: & | |
| 1064 | 48 | MassWt_u ! The fractional mass weighting at a u-point [nondim]. |
| 1065 | real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: & | |
| 1066 | 48 | MassWt_v ! The fractional mass weighting at a v-point [nondim]. |
| 1067 | real, dimension(SZI_(G),SZJ_(G)) :: & | |
| 1068 | 48 | T_top, & ! Temperature of top layer used with correction_intxpa [C ~> degC] |
| 1069 | 48 | S_top, & ! Salinity of top layer used with correction_intxpa [S ~> ppt] |
| 1070 | 48 | rho_top ! Density anomaly of top layer used in calculating intx_pa_cor and inty_pa_cor |
| 1071 | real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: & | |
| 1072 | 48 | rho_pgf, rho_stanley_pgf ! Density [R ~> kg m-3] from EOS with and without SGS T variance |
| 1073 | ! in Stanley parameterization. | |
| 1074 | real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: & | |
| 1075 | 48 | p_stanley ! Pressure [R L2 T-2 ~> Pa] estimated with Rho_0 |
| 1076 | 48 | real :: zeros(SZI_(G)) ! An array of zero values that can be used as an argument [various] |
| 1077 | 48 | real :: rho_in_situ(SZI_(G),SZJ_(G)) ! The in situ density [R ~> kg m-3]. |
| 1078 | 48 | real :: p_ref(SZI_(G)) ! The pressure used to calculate the coordinate |
| 1079 | ! density, [R L2 T-2 ~> Pa] (usually 2e7 Pa = 2000 dbar). | |
| 1080 | 48 | real :: p_surf_EOS(SZI_(G)) ! The pressure at the ocean surface determined from the surface height, |
| 1081 | ! consistent with what is used in the density integral routines [R L2 T-2 ~> Pa] | |
| 1082 | 48 | real :: p0(SZI_(G), SZJ_(G)) ! An array of zeros to use for pressure [R L2 T-2 ~> Pa]. |
| 1083 | real :: dz_geo_sfc ! The change in surface geopotential height between adjacent cells [L2 T-2 ~> m2 s-2] | |
| 1084 | real :: GxRho0 ! The gravitational acceleration times mean ocean density [R L2 Z-1 T-2 ~> Pa m-1] | |
| 1085 | real :: GxRho_ref ! The gravitational acceleration times reference density [R L2 Z-1 T-2 ~> Pa m-1] | |
| 1086 | real :: rho0_int_density ! Rho0 used in int_density_dz_* subroutines [R ~> kg m-3] | |
| 1087 | real :: rho0_set_pbce ! Rho0 used in set_pbce_Bouss subroutine [R ~> kg m-3] | |
| 1088 | real :: h_neglect ! A thickness that is so small it is usually lost | |
| 1089 | ! in roundoff and can be neglected [H ~> m]. | |
| 1090 | real :: I_Rho0 ! The inverse of the Boussinesq reference density [R-1 ~> m3 kg-1]. | |
| 1091 | real :: G_Rho0 ! G_Earth / Rho_0 in [L2 Z-1 T-2 R-1 ~> m4 s-2 kg-1]. | |
| 1092 | real :: I_g_rho ! The inverse of the density times the gravitational acceleration [Z T2 L-2 R-1 ~> m Pa-1] | |
| 1093 | real :: rho_ref ! The reference density [R ~> kg m-3]. | |
| 1094 | real :: dz_neglect ! A minimal thickness [Z ~> m], like e. | |
| 1095 | real :: dz_nonvanished ! A small thickness considered to be vanished for mass weighting [Z ~> m] | |
| 1096 | real :: H_to_RL2_T2 ! A factor to convert from thickness units (H) to pressure | |
| 1097 | ! units [R L2 T-2 H-1 ~> Pa m-1 or Pa m2 kg-1]. | |
| 1098 | real :: T5(5) ! Temperatures and salinities at five quadrature points [C ~> degC] | |
| 1099 | real :: S5(5) ! Salinities at five quadrature points [S ~> ppt] | |
| 1100 | real :: p5(5) ! Full pressures at five quadrature points for use with the equation of state [R L2 T-2 ~> Pa] | |
| 1101 | real :: pa5(5) ! The pressure anomaly (i.e. pressure + g*RHO_0*e) at five quadrature points [R L2 T-2 ~> Pa]. | |
| 1102 | real :: r5(5) ! Densities at five quadrature points [R ~> kg m-3] | |
| 1103 | real :: wt_R ! A weighting factor [nondim] | |
| 1104 | real, parameter :: C1_6 = 1.0/6.0 ! A rational constant [nondim] | |
| 1105 | real, parameter :: C1_90 = 1.0/90.0 ! A rational constant [nondim] | |
| 1106 | logical :: use_p_atm ! If true, use the atmospheric pressure. | |
| 1107 | logical :: use_ALE ! If true, use an ALE pressure reconstruction. | |
| 1108 | logical :: use_EOS ! If true, density is calculated from T & S using an equation of state. | |
| 1109 | logical :: do_more_k ! If true, there are still points where a flatter interface remains to be found. | |
| 1110 | 24 | type(thermo_var_ptrs) :: tv_tmp! A structure of temporary T & S. |
| 1111 | integer, dimension(2) :: EOSdom ! The i-computational domain for the equation of state | |
| 1112 | integer, dimension(2) :: EOSdom_h ! The i-computational domain for the equation of state at tracer points | |
| 1113 | integer, dimension(2) :: EOSdom_u ! The i-computational domain for the equation of state at u-velocity points | |
| 1114 | integer, dimension(2) :: EOSdom_v ! The i-computational domain for the equation of state at v-velocity points | |
| 1115 | integer :: EOSdom2d(2,2) ! The 2D compute domain for the equation of state | |
| 1116 | integer :: is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, nkmb | |
| 1117 | integer :: i, j, k, m, kstart, kend | |
| 1118 | integer :: nkblock | |
| 1119 | ||
| 1120 | 24 | is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke |
| 1121 | 24 | nkblock = CS%nkblock ; if (nkblock <= 0) nkblock = nz |
| 1122 | 24 | nkmb=GV%nk_rho_varies |
| 1123 | 24 | Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB |
| 1124 | 24 | EOSdom(1) = Isq - (G%isd-1) ; EOSdom(2) = G%iec+1 - (G%isd-1) |
| 1125 | 24 | EOSdom_u(1) = Isq - (G%IsdB-1) ; EOSdom_u(2) = Ieq - (G%IsdB-1) |
| 1126 | 24 | EOSdom_v(1) = is - (G%isd-1) ; EOSdom_v(2) = ie - (G%isd-1) |
| 1127 | ||
| 1128 | 72 | EOSdom2d(1,:) = EOSdom(:) |
| 1129 | 72 | EOSdom2d(2,:) = [Jsq - (G%isd - 1), (je + 1) - (G%jsd - 1)] |
| 1130 | ||
| 1131 | ! TODO: This would be done outside of the function! | |
| 1132 | ||
| 1133 | 24 | if (.not.CS%initialized) call MOM_error(FATAL, & |
| 1134 | 0 | "MOM_PressureForce_FV_Bouss: Module must be initialized before it is used.") |
| 1135 | ||
| 1136 | 24 | use_p_atm = associated(p_atm) |
| 1137 | 24 | use_EOS = associated(tv%eqn_of_state) |
| 1138 | 24 | use_ALE = .false. |
| 1139 | 24 | if (associated(ALE_CSp)) use_ALE = CS%reconstruct .and. use_EOS |
| 1140 | ||
| 1141 | 24 | h_neglect = GV%H_subroundoff |
| 1142 | 24 | dz_neglect = GV%dZ_subroundoff |
| 1143 | 24 | dz_nonvanished = GV%H_to_Z*CS%h_nonvanished |
| 1144 | 24 | I_Rho0 = 1.0 / GV%Rho0 |
| 1145 | 24 | G_Rho0 = GV%g_Earth / GV%Rho0 |
| 1146 | 24 | GxRho0 = GV%g_Earth * GV%Rho0 |
| 1147 | 24 | rho_ref = CS%rho_ref |
| 1148 | ||
| 1149 | 24 | if (CS%rho_ref_bug) then |
| 1150 | 24 | rho0_int_density = rho_ref |
| 1151 | 24 | rho0_set_pbce = rho_ref |
| 1152 | 24 | GxRho_ref = GxRho0 |
| 1153 | 24 | I_g_rho = 1.0 / (rho_ref * GV%g_Earth) |
| 1154 | else | |
| 1155 | 0 | rho0_int_density = GV%Rho0 |
| 1156 | 0 | rho0_set_pbce = GV%Rho0 |
| 1157 | 0 | GxRho_ref = GV%g_Earth * rho_ref |
| 1158 | 0 | I_g_rho = 1.0 / (GV%rho0 * GV%g_Earth) |
| 1159 | endif | |
| 1160 | ||
| 1161 | 24 | if ((CS%id_MassWt_u > 0) .or. (CS%id_MassWt_v > 0)) then |
| 1162 | 0 | MassWt_u(:,:,:) = 0.0 ; MassWt_v(:,:,:) = 0.0 |
| 1163 | endif | |
| 1164 | ||
| 1165 | !$omp target enter data map(alloc: e, T_t, T_b, S_t, S_b) | |
| 1166 | ||
| 1167 | 2952 | do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1) |
| 1168 | 184488 | e(i,j,nz+1) = -G%bathyT(i,j) |
| 1169 | enddo | |
| 1170 | ||
| 1171 | ! The following two if-blocks are used to recover old answers for self-attraction and loading | |
| 1172 | ! (SAL) and tides only. The old algorithm moves interface heights before density calculations, | |
| 1173 | ! and therefore is incorrect without SSH_IN_EOS_PRESSURE_FOR_PGF=True (added in August 2024). | |
| 1174 | ! See the code right after Pa calculation loop for the new algorithm. | |
| 1175 | ||
| 1176 | ! Calculate and add SAL geopotential anomaly to interface height (old answers) | |
| 1177 | 24 | if (CS%calculate_SAL .and. CS%tides_answer_date<=20250131) then |
| 1178 | !$omp target update from(e(:,:,nz+1)) | |
| 1179 | ||
| 1180 | !$OMP parallel do default(shared) | |
| 1181 | 0 | do j=Jsq,Jeq+1 |
| 1182 | 0 | do i=Isq,Ieq+1 |
| 1183 | 0 | SSH(i,j) = min(-G%bathyT(i,j) - G%meanSL(i,j), 0.0) |
| 1184 | enddo | |
| 1185 | 0 | do k=1,nz ; do i=Isq,Ieq+1 |
| 1186 | 0 | SSH(i,j) = SSH(i,j) + h(i,j,k)*GV%H_to_Z |
| 1187 | enddo ; enddo | |
| 1188 | enddo | |
| 1189 | 0 | call calc_SAL(SSH, e_sal, G, CS%SAL_CSp, tmp_scale=US%Z_to_m) |
| 1190 | ||
| 1191 | 0 | if (CS%tides_answer_date>20230630) then ! answers_date between [20230701, 20250131] |
| 1192 | !$OMP parallel do default(shared) | |
| 1193 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 1194 | 0 | e(i,j,nz+1) = e(i,j,nz+1) - e_sal(i,j) |
| 1195 | enddo ; enddo | |
| 1196 | endif | |
| 1197 | ||
| 1198 | !$omp target update to(e(:,:,nz+1)) | |
| 1199 | endif | |
| 1200 | ||
| 1201 | ! Calculate and add tidal geopotential anomaly to interface height (old answers) | |
| 1202 | 24 | if (CS%tides .and. CS%tides_answer_date<=20250131) then |
| 1203 | !$omp target update from(e(:,:,nz+1)) | |
| 1204 | ||
| 1205 | 0 | if (CS%tides_answer_date>20230630) then ! answers_date between [20230701, 20250131] |
| 1206 | 0 | call calc_tidal_forcing(CS%Time, e_tidal_eq, e_tidal_sal, G, US, CS%tides_CSp) |
| 1207 | !$OMP parallel do default(shared) | |
| 1208 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 1209 | 0 | e(i,j,nz+1) = e(i,j,nz+1) - (e_tidal_eq(i,j) + e_tidal_sal(i,j)) |
| 1210 | enddo ; enddo | |
| 1211 | else ! answers_date before 20230701 | |
| 1212 | 0 | if (.not.CS%calculate_SAL) e_sal(:,:) = 0.0 |
| 1213 | call calc_tidal_forcing_legacy(CS%Time, e_sal, e_sal_and_tide, e_tidal_eq, e_tidal_sal, & | |
| 1214 | 0 | G, US, CS%tides_CSp) |
| 1215 | !$OMP parallel do default(shared) | |
| 1216 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 1217 | 0 | e(i,j,nz+1) = e(i,j,nz+1) - e_sal_and_tide(i,j) |
| 1218 | enddo ; enddo | |
| 1219 | endif | |
| 1220 | ||
| 1221 | !$omp target update to(e(:,:,nz+1)) | |
| 1222 | endif | |
| 1223 | ||
| 1224 | 24 | do concurrent(j=Jsq:Jeq+1) |
| 1225 | 113112 | do k=nz,1,-1 |
| 1226 | 1488 | do concurrent (i=Isq:Ieq+1) |
| 1227 | 13726800 | e(i,j,K) = e(i,j,K+1) + h(i,j,k)*GV%H_to_Z |
| 1228 | enddo | |
| 1229 | enddo | |
| 1230 | enddo | |
| 1231 | ||
| 1232 | 24 | if (use_EOS) then |
| 1233 | 24 | if (nkmb>0) then |
| 1234 | ! With a bulk mixed layer, replace the T & S of any layers that are lighter than the buffer | |
| 1235 | ! layer with the properties of the buffer layer. These layers will be massless anyway, and | |
| 1236 | ! it avoids any formal calculations with hydrostatically unstable profiles. | |
| 1237 | 0 | tv_tmp%T => T_tmp ; tv_tmp%S => S_tmp |
| 1238 | 0 | tv_tmp%eqn_of_state => tv%eqn_of_state |
| 1239 | ||
| 1240 | 0 | do i=Isq,Ieq+1 ; p_ref(i) = tv%P_Ref ; enddo |
| 1241 | !$OMP parallel do default(shared) private(Rho_cv_BL) | |
| 1242 | 0 | do j=Jsq,Jeq+1 |
| 1243 | 0 | do k=1,nkmb ; do i=Isq,Ieq+1 |
| 1244 | 0 | tv_tmp%T(i,j,k) = tv%T(i,j,k) ; tv_tmp%S(i,j,k) = tv%S(i,j,k) |
| 1245 | enddo ; enddo | |
| 1246 | call calculate_density(tv%T(:,j,nkmb), tv%S(:,j,nkmb), p_ref, Rho_cv_BL(:), & | |
| 1247 | 0 | tv%eqn_of_state, EOSdom) |
| 1248 | ||
| 1249 | 0 | do k=nkmb+1,nz ; do i=Isq,Ieq+1 |
| 1250 | 0 | if (GV%Rlay(k) < Rho_cv_BL(i)) then |
| 1251 | 0 | tv_tmp%T(i,j,k) = tv%T(i,j,nkmb) ; tv_tmp%S(i,j,k) = tv%S(i,j,nkmb) |
| 1252 | else | |
| 1253 | 0 | tv_tmp%T(i,j,k) = tv%T(i,j,k) ; tv_tmp%S(i,j,k) = tv%S(i,j,k) |
| 1254 | endif | |
| 1255 | enddo ; enddo | |
| 1256 | enddo | |
| 1257 | else | |
| 1258 | 24 | tv_tmp%T => tv%T ; tv_tmp%S => tv%S |
| 1259 | 24 | tv_tmp%eqn_of_state => tv%eqn_of_state |
| 1260 | endif | |
| 1261 | !$omp target enter data map(to: tv_tmp, tv_tmp%T, tv_tmp%S) | |
| 1262 | endif | |
| 1263 | ||
| 1264 | ! If regridding is activated, do a linear reconstruction of salinity | |
| 1265 | ! and temperature across each layer. The subscripts 't' and 'b' refer | |
| 1266 | ! to top and bottom values within each layer (these are the only degrees | |
| 1267 | ! of freedom needed to know the linear profile). | |
| 1268 | 24 | if ( use_ALE .and. (CS%Recon_Scheme == 1) ) then |
| 1269 | 24 | call TS_PLM_edge_values(ALE_CSp, S_t, S_b, T_t, T_b, G, GV, tv, h, CS%boundary_extrap) |
| 1270 | 0 | elseif ( use_ALE .and. (CS%Recon_Scheme == 2) ) then |
| 1271 | 0 | call TS_PPM_edge_values(ALE_CSp, S_t, S_b, T_t, T_b, G, GV, tv, h, CS%boundary_extrap) |
| 1272 | 0 | elseif ( use_ALE .and. (CS%Recon_Scheme == 3) ) then |
| 1273 | 0 | call TS_PLM_WLS_edge_values(ALE_CSp, S_t, S_b, T_t, T_b, G, GV, tv, h) |
| 1274 | 0 | elseif (CS%reset_intxpa_integral) then |
| 1275 | 0 | do k=1,nz ; do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 1276 | 0 | T_b(i,j,k) = tv%T(i,j,k) ; S_b(i,j,k) = tv%S(i,j,k) |
| 1277 | enddo ; enddo ; enddo | |
| 1278 | endif | |
| 1279 | ||
| 1280 | !$omp target enter data map(alloc: pa) | |
| 1281 | ||
| 1282 | ! Set the surface boundary conditions on pressure anomaly and its horizontal | |
| 1283 | ! integrals, assuming that the surface pressure anomaly varies linearly | |
| 1284 | ! in x and y. | |
| 1285 | 24 | if (use_p_atm) then |
| 1286 | 2952 | do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1) |
| 1287 | 184488 | pa(i,j,1) = GxRho_ref * (e(i,j,1) - G%Z_ref) + p_atm(i,j) |
| 1288 | enddo | |
| 1289 | else | |
| 1290 | 0 | do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1) |
| 1291 | 0 | pa(i,j,1) = GxRho_ref * (e(i,j,1) - G%Z_ref) |
| 1292 | enddo | |
| 1293 | endif | |
| 1294 | ||
| 1295 | 24 | if (use_EOS) then |
| 1296 | !$omp target enter data map(alloc: Z_0p) | |
| 1297 | 24 | if (CS%use_SSH_in_Z0p .and. use_p_atm) then |
| 1298 | 0 | do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1) |
| 1299 | 0 | Z_0p(i,j) = e(i,j,1) + p_atm(i,j) * I_g_rho |
| 1300 | enddo | |
| 1301 | 24 | elseif (CS%use_SSH_in_Z0p) then |
| 1302 | 0 | do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1) |
| 1303 | 0 | Z_0p(i,j) = e(i,j,1) |
| 1304 | enddo | |
| 1305 | else | |
| 1306 | 2952 | do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1) |
| 1307 | 184488 | Z_0p(i,j) = G%meanSL(i,j) |
| 1308 | enddo | |
| 1309 | endif | |
| 1310 | !$omp target update from(Z_0p) & | |
| 1311 | !$omp if((use_ALE .and. CS%Recon_Scheme == 2) .or. CS%reset_intxpa_integral .or. CS%correction_intxpa) | |
| 1312 | endif | |
| 1313 | ||
| 1314 | ! Calculate 4 integrals through the layer that are required in the | |
| 1315 | ! subsequent calculation. | |
| 1316 | !$omp target enter data map(alloc: dpa, intx_dpa, inty_dpa, intz_dpa) | |
| 1317 | ||
| 1318 | 24 | if (use_EOS) then |
| 1319 | !$omp target update from(e) if( (use_ALE .and. CS%Recon_Scheme == 2) .or. & | |
| 1320 | !$omp (CS%id_MassWt_u > 0) .or. (CS%id_MassWt_v > 0)) | |
| 1321 | ||
| 1322 | ! The following routine computes the integrals that are needed to | |
| 1323 | ! calculate the pressure gradient force. Linear profiles for T and S are | |
| 1324 | ! assumed when regridding is activated. Otherwise, the previous version | |
| 1325 | ! is used, whereby densities within each layer are constant no matter | |
| 1326 | ! where the layers are located. | |
| 1327 | !$omp target enter data map(to: tv) | |
| 1328 | 1824 | do kstart=1,nz,nkblock |
| 1329 | 1800 | kend = min(kstart+nkblock-1, nz) |
| 1330 | 1800 | if ( use_ALE .and. CS%Recon_Scheme > 0 ) then |
| 1331 | 1800 | if ( CS%Recon_Scheme == 1 .or. CS%Recon_Scheme == 3 ) then |
| 1332 | call int_density_dz_generic_plm(kstart, kend, tv, T_t, T_b, S_t, S_b, e, & | |
| 1333 | rho_ref, rho0_int_density, GV%g_Earth, dz_neglect, G%bathyT, & | |
| 1334 | G%HI, GV, tv%eqn_of_state, US, CS%use_stanley_pgf, dpa, intz_dpa, & | |
| 1335 | intx_dpa, inty_dpa, & | |
| 1336 | MassWghtInterp=CS%MassWghtInterp, & | |
| 1337 | use_inaccurate_form=CS%use_inaccurate_pgf_rho_anom, Z_0p=Z_0p, & | |
| 1338 | MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, h_nv=dz_nonvanished, & | |
| 1339 | 1800 | niblock=CS%niblock_plm, njblock=CS%njblock_plm) |
| 1340 | 0 | elseif ( CS%Recon_Scheme == 2 ) then |
| 1341 | 0 | do k=kstart,kend |
| 1342 | call int_density_dz_generic_ppm(k, tv, T_t, T_b, S_t, S_b, e, & | |
| 1343 | rho_ref, rho0_int_density, GV%g_Earth, dz_neglect, G%bathyT, & | |
| 1344 | G%HI, GV, tv%eqn_of_state, US, CS%use_stanley_pgf, dpa(:,:,k), intz_dpa(:,:,k), & | |
| 1345 | intx_dpa(:,:,k), inty_dpa(:,:,k), & | |
| 1346 | MassWghtInterp=CS%MassWghtInterp, Z_0p=Z_0p, & | |
| 1347 | 0 | MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, h_nv=dz_nonvanished) |
| 1348 | enddo | |
| 1349 | endif | |
| 1350 | else | |
| 1351 | 0 | do k=kstart,kend |
| 1352 | call int_density_dz(tv_tmp%T(:,:,k), tv_tmp%S(:,:,k), e(:,:,K), e(:,:,K+1), & | |
| 1353 | rho_ref, rho0_int_density, GV%g_Earth, G%HI, tv%eqn_of_state, US, dpa(:,:,k), & | |
| 1354 | intz_dpa(:,:,k), intx_dpa(:,:,k), inty_dpa(:,:,k), G%bathyT, e(:,:,1), dz_neglect, & | |
| 1355 | CS%MassWghtInterp, Z_0p=Z_0p, & | |
| 1356 | 0 | MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, h_nv=dz_nonvanished) |
| 1357 | enddo | |
| 1358 | endif | |
| 1359 | 1800 | if (GV%Z_to_H /= 1.0) then |
| 1360 | 0 | do concurrent (k=kstart:kend, j=Jsq:Jeq+1, i=Isq:Ieq+1) |
| 1361 | 0 | intz_dpa(i,j,k) = intz_dpa(i,j,k)*GV%Z_to_H |
| 1362 | enddo | |
| 1363 | endif | |
| 1364 | 1824 | if ((CS%id_MassWt_u > 0) .or. (CS%id_MassWt_v > 0)) then |
| 1365 | 0 | do k=kstart,kend |
| 1366 | call diagnose_mass_weight_Z(e(:,:,K), e(:,:,K+1), G%bathyT, e(:,:,1), dz_neglect, CS%MassWghtInterp, & | |
| 1367 | G%HI, MassWt_u(:,:,k), MassWt_v(:,:,k), & | |
| 1368 | 0 | MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, h_nv=CS%h_nonvanished) |
| 1369 | enddo | |
| 1370 | endif | |
| 1371 | enddo | |
| 1372 | !$omp target exit data map(release: tv) | |
| 1373 | else | |
| 1374 | !$omp target data map(alloc: dz_geo) | |
| 1375 | 0 | do k=1,nz |
| 1376 | 0 | do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1) |
| 1377 | 0 | dz_geo(i,j) = GV%g_Earth * GV%H_to_Z*h(i,j,k) |
| 1378 | 0 | dpa(i,j,k) = (GV%Rlay(k) - rho_ref) * dz_geo(i,j) |
| 1379 | 0 | intz_dpa(i,j,k) = 0.5*(GV%Rlay(k) - rho_ref) * dz_geo(i,j)*h(i,j,k) |
| 1380 | enddo | |
| 1381 | 0 | do concurrent (j=js:je, I=Isq:Ieq) |
| 1382 | 0 | intx_dpa(I,j,k) = 0.5*(GV%Rlay(k) - rho_ref) * (dz_geo(i,j) + dz_geo(i+1,j)) |
| 1383 | enddo | |
| 1384 | 0 | do concurrent (J=Jsq:Jeq, i=is:ie) |
| 1385 | 0 | inty_dpa(i,J,k) = 0.5*(GV%Rlay(k) - rho_ref) * (dz_geo(i,j) + dz_geo(i,j+1)) |
| 1386 | enddo | |
| 1387 | enddo | |
| 1388 | !$omp end target data | |
| 1389 | endif | |
| 1390 | ||
| 1391 | ! Set the pressure anomalies at the interfaces. | |
| 1392 | 24 | do concurrent (j=Jsq:Jeq+1) |
| 1393 | 113112 | do k=1,nz |
| 1394 | 1488 | do concurrent (i=Isq:Ieq+1) |
| 1395 | 13726800 | pa(i,j,K+1) = pa(i,j,K) + dpa(i,j,k) |
| 1396 | enddo | |
| 1397 | enddo | |
| 1398 | enddo | |
| 1399 | ||
| 1400 | ! Calculate and add SAL geopotential anomaly to interface height (new answers) | |
| 1401 | 24 | if (CS%calculate_SAL .and. CS%tides_answer_date>20250131) then |
| 1402 | 0 | if (CS%sal_use_bpa) then |
| 1403 | !$OMP parallel do default(shared) | |
| 1404 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 1405 | 0 | pbot(i,j) = pa(i,j,nz+1) - GxRho_ref * (e(i,j,nz+1) - G%Z_ref) |
| 1406 | enddo ; enddo | |
| 1407 | 0 | call calc_SAL(pbot, e_sal, G, CS%SAL_CSp, tmp_scale=US%Z_to_m) |
| 1408 | else | |
| 1409 | !$OMP parallel do default(shared) | |
| 1410 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 1411 | 0 | SSH(i,j) = e(i,j,1) - G%Z_ref |
| 1412 | ! Remove above sea level topography at floodable cells | |
| 1413 | 0 | SSH(i,j) = SSH(i,j) - max(-G%bathyT(i,j)-G%meanSL(i,j), 0.0) |
| 1414 | enddo ; enddo | |
| 1415 | 0 | call calc_SAL(SSH, e_sal, G, CS%SAL_CSp, tmp_scale=US%Z_to_m) |
| 1416 | endif | |
| 1417 | 0 | if (.not.CS%bq_sal_tides) then ; do K=1,nz+1 |
| 1418 | !$OMP parallel do default(shared) | |
| 1419 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 1420 | 0 | e(i,j,K) = e(i,j,K) - e_sal(i,j) |
| 1421 | 0 | pa(i,j,K) = pa(i,j,K) - GxRho_ref * e_sal(i,j) |
| 1422 | enddo ; enddo | |
| 1423 | enddo ; endif | |
| 1424 | endif | |
| 1425 | ||
| 1426 | ! Calculate and add tidal geopotential anomaly to interface height (new answers) | |
| 1427 | 24 | if (CS%tides .and. CS%tides_answer_date>20250131) then |
| 1428 | 0 | call calc_tidal_forcing(CS%Time, e_tidal_eq, e_tidal_sal, G, US, CS%tides_CSp) |
| 1429 | 0 | if (.not.CS%bq_sal_tides) then ; do K=1,nz+1 |
| 1430 | !$OMP parallel do default(shared) | |
| 1431 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 1432 | 0 | e(i,j,K) = e(i,j,K) - (e_tidal_eq(i,j) + e_tidal_sal(i,j)) |
| 1433 | 0 | pa(i,j,K) = pa(i,j,K) - GxRho_ref * (e_tidal_eq(i,j) + e_tidal_sal(i,j)) |
| 1434 | enddo ; enddo | |
| 1435 | enddo ; endif | |
| 1436 | endif | |
| 1437 | ||
| 1438 | 24 | if (CS%correction_intxpa .or. CS%reset_intxpa_integral) then |
| 1439 | ! Determine surface temperature and salinity for use in the pressure gradient corrections | |
| 1440 | 0 | if (use_ALE .and. (CS%Recon_Scheme > 0)) then |
| 1441 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 1442 | 0 | T_top(i,j) = T_t(i,j,1) ; S_top(i,j) = S_t(i,j,1) |
| 1443 | enddo ; enddo | |
| 1444 | else | |
| 1445 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 1446 | 0 | T_top(i,j) = tv%T(i,j,1) ; S_top(i,j) = tv%S(i,j,1) |
| 1447 | enddo ; enddo | |
| 1448 | endif | |
| 1449 | endif | |
| 1450 | ||
| 1451 | !$omp target enter data map(alloc: intx_pa, inty_pa) | |
| 1452 | 24 | if (CS%correction_intxpa) then |
| 1453 | ! TODO needs to be moved to GPU | |
| 1454 | ||
| 1455 | ! Determine surface density for use in the pressure gradient corrections | |
| 1456 | !$OMP parallel do default(shared) private(p_surf_EOS) | |
| 1457 | 0 | do j=Jsq,Jeq+1 |
| 1458 | ! P_surf_EOS here is consistent with the pressure that is used in the int_density_dz routines. | |
| 1459 | 0 | do i=Isq,Ieq+1 ; p_surf_EOS(i) = -GxRho0*(e(i,j,1) - Z_0p(i,j)) ; enddo |
| 1460 | call calculate_density(T_top(:,j), S_top(:,j), p_surf_EOS, rho_top(:,j), & | |
| 1461 | 0 | tv%eqn_of_state, EOSdom, rho_ref=rho_ref) |
| 1462 | enddo | |
| 1463 | ||
| 1464 | 0 | if (CS%debug) then |
| 1465 | 0 | call hchksum(rho_top, "intx_pa rho_top", G%HI, haloshift=1, unscale=US%R_to_kg_m3) |
| 1466 | 0 | call hchksum(e(:,:,1), "intx_pa e(1)", G%HI, haloshift=1, unscale=US%Z_to_m) |
| 1467 | 0 | call hchksum(pa(:,:,1), "intx_pa pa(1)", G%HI, haloshift=1, unscale=US%RL2_T2_to_Pa) |
| 1468 | endif | |
| 1469 | ||
| 1470 | ! This version attempts to correct for hydrostatic variations in surface pressure under ice. | |
| 1471 | !$OMP parallel do default(shared) private(dz_geo_sfc) | |
| 1472 | 0 | do j=js,je ; do I=Isq,Ieq |
| 1473 | 0 | intx_pa_cor(I,j) = 0.0 |
| 1474 | 0 | dz_geo_sfc = GV%g_Earth * (e(i+1,j,1)-e(i,j,1)) |
| 1475 | 0 | if ((dz_geo_sfc * rho_ref - (pa(i+1,j,1)-pa(i,j,1)))*dz_geo_sfc > 0.0) then |
| 1476 | ! The pressure/depth relationship has a positive implied density given by | |
| 1477 | ! rho_implied = rho_ref - (pa(i+1,j,1)-pa(i,j,1)) / dz_geo_sfc | |
| 1478 | 0 | if (-dz_geo_sfc * (pa(i+1,j,1)-pa(i,j,1)) > & |
| 1479 | 0.25*((rho_top(i+1,j)+rho_top(i,j))-2.0*rho_ref) * dz_geo_sfc**2) then | |
| 1480 | ! The pressure difference is at least half the size of the difference expected by hydrostatic | |
| 1481 | ! balance. This test gets rid of pressure differences that are small, e.g. open ocean. | |
| 1482 | ! Use 5 point quadrature to calculate intxpa | |
| 1483 | 0 | T5(1) = T_top(i,j) ; T5(5) = T_top(i+1,j) |
| 1484 | 0 | S5(1) = S_top(i,j) ; S5(5) = S_top(i+1,j) |
| 1485 | 0 | pa5(1) = pa(i,j,1) ; pa5(5) = pa(i+1,j,1) |
| 1486 | ! Pressure input to density EOS is consistent with the pressure used in the int_density_dz routines. | |
| 1487 | 0 | p5(1) = -GxRho0*(e(i,j,1) - Z_0p(i,j)) |
| 1488 | 0 | p5(5) = -GxRho0*(e(i+1,j,1) - Z_0p(i,j)) |
| 1489 | 0 | do m=2,4 |
| 1490 | 0 | wt_R = 0.25*real(m-1) |
| 1491 | 0 | T5(m) = T5(1) + (T5(5)-T5(1))*wt_R |
| 1492 | 0 | S5(m) = S5(1) + (S5(5)-S5(1))*wt_R |
| 1493 | 0 | p5(m) = p5(1) + (p5(5)-p5(1))*wt_R |
| 1494 | enddo !m | |
| 1495 | 0 | call calculate_density(T5, S5, p5, r5, tv%eqn_of_state, rho_ref=rho_ref) |
| 1496 | ||
| 1497 | ! Use a trapezoidal rule integral of the hydrostatic equation to determine the pressure | |
| 1498 | ! anomalies at 5 equally spaced points along the interface, and then use Boole's rule | |
| 1499 | ! quadrature to find the integrated correction to the integral of pressure along the interface. | |
| 1500 | ! The derivation for this expression is shown below in the y-direction version. | |
| 1501 | 0 | intx_pa_cor(I,j) = C1_90 * (4.75*(r5(5)-r5(1)) + 5.5*(r5(4)-r5(2))) * dz_geo_sfc |
| 1502 | ! Note that (4.75 + 5.5/2) / 90 = 1/12, so this is consistent with the linear result below. | |
| 1503 | endif | |
| 1504 | endif | |
| 1505 | 0 | intx_pa(I,j,1) = 0.5*(pa(i,j,1) + pa(i+1,j,1)) + intx_pa_cor(I,j) |
| 1506 | enddo ; enddo | |
| 1507 | !$OMP parallel do default(shared) private(dz_geo_sfc) | |
| 1508 | 0 | do J=Jsq,Jeq ; do i=is,ie |
| 1509 | 0 | inty_pa_cor(i,J) = 0.0 |
| 1510 | 0 | dz_geo_sfc = GV%g_Earth * (e(i,j+1,1)-e(i,j,1)) |
| 1511 | 0 | if ((dz_geo_sfc * rho_ref - (pa(i,j+1,1)-pa(i,j,1)))*dz_geo_sfc > 0.0) then |
| 1512 | ! The pressure/depth relationship has a positive implied density | |
| 1513 | 0 | if (-dz_geo_sfc * (pa(i,j+1,1)-pa(i,j,1)) > & |
| 1514 | 0.25*((rho_top(i,j+1)+rho_top(i,j))-2.0*rho_ref) * dz_geo_sfc**2) then | |
| 1515 | ! The pressure difference is at least half the size of the difference expected by hydrostatic | |
| 1516 | ! balance. This test gets rid of pressure differences that are small, e.g. open ocean. | |
| 1517 | ! Use 5 point quadrature to calculate intypa | |
| 1518 | 0 | T5(1) = T_top(i,j) ; T5(5) = T_top(i,j+1) |
| 1519 | 0 | S5(1) = S_top(i,j) ; S5(5) = S_top(i,j+1) |
| 1520 | 0 | pa5(1) = pa(i,j,1) ; pa5(5) = pa(i,j+1,1) |
| 1521 | ! Pressure input to density EOS is consistent with the pressure used in the int_density_dz routines. | |
| 1522 | 0 | p5(1) = -GxRho0*(e(i,j,1) - Z_0p(i,j)) |
| 1523 | 0 | p5(5) = -GxRho0*(e(i,j+1,1) - Z_0p(i,j)) |
| 1524 | ||
| 1525 | 0 | do m=2,4 |
| 1526 | 0 | wt_R = 0.25*real(m-1) |
| 1527 | 0 | T5(m) = T5(1) + (T5(5)-T5(1))*wt_R |
| 1528 | 0 | S5(m) = S5(1) + (S5(5)-S5(1))*wt_R |
| 1529 | 0 | p5(m) = p5(1) + (p5(5)-p5(1))*wt_R |
| 1530 | enddo !m | |
| 1531 | 0 | call calculate_density(T5, S5, p5, r5, tv%eqn_of_state, rho_ref=rho_ref) |
| 1532 | ||
| 1533 | ! Use a trapezoidal rule integral of the hydrostatic equation to determine the pressure | |
| 1534 | ! anomalies at 5 equally spaced points along the interface, and then use Boole's rule | |
| 1535 | ! quadrature to find the integrated correction to the integral of pressure along the interface. | |
| 1536 | 0 | inty_pa_cor(i,J) = C1_90 * (4.75*(r5(5)-r5(1)) + 5.5*(r5(4)-r5(2))) * dz_geo_sfc |
| 1537 | ||
| 1538 | ! The derivation of this correction follows: | |
| 1539 | ||
| 1540 | ! Make pressure curvature a difference from the linear fit of pressure between the two points | |
| 1541 | ! (which is equivalent to taking 4 trapezoidal rule integrals of the hydrostatic equation on | |
| 1542 | ! sub-segments), with a constant slope that is chosen so that the pressure anomalies at the | |
| 1543 | ! two ends of the segment agree with their known values. | |
| 1544 | ! d_geo_8 = 0.125*dz_geo_sfc | |
| 1545 | ! dpa_subseg = 0.25*(pa5(5)-pa5(1)) + & | |
| 1546 | ! 0.25*d_geo_8 * ((r5(5)+r5(1)) + 2.0*((r5(4)+r5(2)) + r5(3))) | |
| 1547 | ! do m=2,4 | |
| 1548 | ! pa5(m) = pa5(m-1) + dpa_subseg - d_geo_8*(r5(m)+r5(m-1))) | |
| 1549 | ! enddo | |
| 1550 | ||
| 1551 | ! Explicitly finding expressions for the incremental pressures from the recursion relation above: | |
| 1552 | ! pa5(2) = 0.25*(3.*pa5(1) + pa5(5)) + 0.25*d_geo_8 * ( (r5(5)-3.*r5(1)) + 2.0*((r5(4)-r5(2)) + r5(3)) ) | |
| 1553 | ! ! pa5(3) = 0.5*(pa5(1) + pa5(5)) + 0.25*d_geo_8 * & | |
| 1554 | ! ! ( (r5(5)+r5(1)) + 2.0*((r5(4)+r5(2)) + r5(3)) + & | |
| 1555 | ! ! (r5(5)-3.*r5(1)) + 2.0*((r5(4)-r5(2)) + r5(3)) - 4.*(r5(3)+r5(2)) ) | |
| 1556 | ! pa5(3) = 0.5*(pa5(1) + pa5(5)) + d_geo_8 * (0.5*(r5(5)-r5(1)) + (r5(4)-r5(2)) ) | |
| 1557 | ! ! pa5(4) = 0.25*(pa5(1) + 3.0*pa5(5)) + 0.25*d_geo_8 * & | |
| 1558 | ! ! (2.0*(r5(5)-r5(1)) + 4.0*(r5(4)-r5(2)) + (r5(5)+r5(1)) + & | |
| 1559 | ! ! 2.0*(r5(4)+r5(2)) + 2.0*r5(3) - 4.*(r5(4)+r5(3))) | |
| 1560 | ! pa5(4) = 0.25*(pa5(1) + 3.0*pa5(5)) + 0.25*d_geo_8 * ( (3.*r5(5)-r5(1)) + 2.0*((r5(4)-r5(2)) - r5(3)) ) | |
| 1561 | ! ! pa5(5) = pa5(5) + 0.25*d_geo_8 * & | |
| 1562 | ! ! ( (3.*r5(5)-r5(1)) + 2.0*((r5(4)-r5(2)) - r5(3)) + & | |
| 1563 | ! ! ((r5(5)+r5(1)) + 2.0*((r5(4)+r5(2)) + r5(3))) - 4.*(r5(5)+r5(4)) ) | |
| 1564 | ! pa5(5) = pa5(5) ! As it should. | |
| 1565 | ||
| 1566 | ! From these: | |
| 1567 | ! pa5(2) + pa5(4) = (pa5(1) + pa5(5)) + 0.25*d_geo_8 * & | |
| 1568 | ! ( (r5(5)-3.*r5(1)) + 2.0*((r5(4)-r5(2)) + r5(3)) + (3.*r5(5)-r5(1)) + 2.0*((r5(4)-r5(2)) - r5(3)) | |
| 1569 | ! pa5(2) + pa5(4) = (pa5(1) + pa5(5)) + d_geo_8 * ( (r5(5)-r5(1)) + (r5(4)-r5(2)) ) | |
| 1570 | ||
| 1571 | ! Get the correction from the difference between the 5-point quadrature integral of pa5 and | |
| 1572 | ! its trapezoidal rule integral as: | |
| 1573 | ! inty_pa_cor(i,J) = C1_90*(7.0*(pa5(1)+pa5(5)) + 32.0*(pa5(2)+pa5(4)) + 12.0*pa5(3)) - 0.5*(pa5(1)+pa5(5))) | |
| 1574 | ! inty_pa_cor(i,J) = C1_90*((32.0*(pa5(2)+pa5(4)) + 12.0*pa5(3)) - 38.0*(pa5(1)+pa5(5))) | |
| 1575 | ! inty_pa_cor(i,J) = C1_90*d_geo_8 * ((32.0*( (r5(5)-r5(1)) + (r5(4)-r5(2)) ) + & | |
| 1576 | ! (6.*(r5(5)-r5(1)) + 12.0*(r5(4)-r5(2)) )) | |
| 1577 | ! inty_pa_cor(i,J) = C1_90*d_geo_8 * ( 38.0*(r5(5)-r5(1)) + 44.0*(r5(4)-r5(2)) ) | |
| 1578 | endif | |
| 1579 | endif | |
| 1580 | 0 | inty_pa(i,J,1) = 0.5*(pa(i,j,1) + pa(i,j+1,1)) + inty_pa_cor(i,J) |
| 1581 | enddo ; enddo | |
| 1582 | ||
| 1583 | 0 | if (CS%debug) then |
| 1584 | call uvchksum("int[xy]_pa_cor", intx_pa_cor, inty_pa_cor, G%HI, haloshift=0, & | |
| 1585 | 0 | symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%RL2_T2_to_Pa) |
| 1586 | call uvchksum("int[xy]_pa(1)", intx_pa(:,:,1), inty_pa(:,:,1), G%HI, haloshift=0, & | |
| 1587 | 0 | symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%RL2_T2_to_Pa) |
| 1588 | endif | |
| 1589 | ||
| 1590 | ! TODO until moved to GPU | |
| 1591 | !$omp target update to(intx_pa, inty_pa) | |
| 1592 | else | |
| 1593 | ! Set the surface boundary conditions on the horizontally integrated pressure anomaly, | |
| 1594 | ! assuming that the surface pressure anomaly varies linearly in x and y. | |
| 1595 | ! If there is an ice-shelf or icebergs, this linear variation would need to be applied | |
| 1596 | ! to an interior interface. | |
| 1597 | 24 | do concurrent (j=js:je, I=Isq:Ieq) |
| 1598 | 177168 | intx_pa(I,j,1) = 0.5*(pa(i,j,1) + pa(i+1,j,1)) |
| 1599 | enddo | |
| 1600 | 24 | do concurrent (J=Jsq:Jeq, i=is:ie) |
| 1601 | 178584 | inty_pa(i,J,1) = 0.5*(pa(i,j,1) + pa(i,j+1,1)) |
| 1602 | enddo | |
| 1603 | endif | |
| 1604 | ||
| 1605 | 24 | do concurrent (j=js:je) |
| 1606 | 109464 | do k=1,nz |
| 1607 | 1440 | do concurrent (I=Isq:Ieq) |
| 1608 | 13176000 | intx_pa(I,j,K+1) = intx_pa(I,j,K) + intx_dpa(I,j,k) |
| 1609 | enddo | |
| 1610 | enddo | |
| 1611 | enddo | |
| 1612 | 24 | do concurrent (J=Jsq:Jeq) |
| 1613 | 111288 | do k=1,nz |
| 1614 | 1464 | do concurrent (i=is:ie) |
| 1615 | 13285800 | inty_pa(i,J,K+1) = inty_pa(i,J,K) + inty_dpa(i,J,k) |
| 1616 | enddo | |
| 1617 | enddo | |
| 1618 | enddo | |
| 1619 | ||
| 1620 | 24 | if (CS%reset_intxpa_integral) then |
| 1621 | ! Having stored the pressure gradient info, we can work out where the first nonvanished layers is | |
| 1622 | ! reset intxpa there, then adjust intxpa throughout the water column. | |
| 1623 | ||
| 1624 | ! TODO temporarily move back to CPU | |
| 1625 | !$omp target update from(intx_pa, inty_pa) | |
| 1626 | ||
| 1627 | ! Zero out the 2-d arrays that will be set from various reference interfaces. | |
| 1628 | 0 | T_int_W(:,:) = 0.0 ; S_int_W(:,:) = 0.0 ; p_int_W(:,:) = 0.0 |
| 1629 | 0 | T_int_E(:,:) = 0.0 ; S_int_E(:,:) = 0.0 ; p_int_E(:,:) = 0.0 |
| 1630 | 0 | intx_pa_nonlin(:,:) = 0.0 ; dgeo_x(:,:) = 0.0 ; intx_pa_cor_ri(:,:) = 0.0 |
| 1631 | 0 | do j=js,je ; do I=Isq,Ieq |
| 1632 | 0 | seek_x_cor(I,j) = (G%mask2dCu(I,j) > 0.) |
| 1633 | 0 | delta_z_x(I,j) = 0.0 |
| 1634 | enddo ; enddo | |
| 1635 | ||
| 1636 | 0 | do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then |
| 1637 | 0 | if ((e(i+1,j,2) <= e(i,j,1)) .and. (e(i,j,2) <= e(i+1,j,1))) then |
| 1638 | ! This is a typical case in the open ocean, so use the topmost interface. | |
| 1639 | 0 | T_int_W(I,j) = T_top(i,j) ; T_int_E(I,j) = T_top(i+1,j) |
| 1640 | 0 | S_int_W(I,j) = S_top(i,j) ; S_int_E(I,j) = S_top(i+1,j) |
| 1641 | 0 | p_int_W(I,j) = -GxRho0*(e(i,j,1) - Z_0p(i,j)) |
| 1642 | 0 | p_int_E(I,j) = -GxRho0*(e(i+1,j,1) - Z_0p(i,j)) |
| 1643 | 0 | intx_pa_nonlin(I,j) = intx_pa(I,j,1) - 0.5*(pa(i,j,1) + pa(i+1,j,1)) |
| 1644 | 0 | dgeo_x(I,j) = GV%g_Earth * (e(i+1,j,1)-e(i,j,1)) |
| 1645 | 0 | seek_x_cor(I,j) = .false. |
| 1646 | endif | |
| 1647 | endif ; enddo ; enddo | |
| 1648 | ||
| 1649 | 0 | do k=1,nz |
| 1650 | 0 | do_more_k = .false. |
| 1651 | 0 | do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then |
| 1652 | ! Find the topmost layer for which both sides are nonvanished and mass-weighting is not | |
| 1653 | ! activated in the subgrid interpolation. | |
| 1654 | 0 | if (((h(i,j,k) > CS%h_nonvanished) .and. (h(i+1,j,k) > CS%h_nonvanished)) .and. & |
| 1655 | (max(0., e(i+1,j,K+1)-e(i,j,1), e(i,j,K+1)-e(i+1,j,1)) <= 0.0)) then | |
| 1656 | ! Store properties at the bottom of this cell to get a "good estimate" for intxpa at | |
| 1657 | ! the interface below this cell (it might have quadratic pressure dependence if sloped) | |
| 1658 | 0 | T_int_W(I,j) = T_b(i,j,k) ; T_int_E(I,j) = T_b(i+1,j,k) |
| 1659 | 0 | S_int_W(I,j) = S_b(i,j,k) ; S_int_E(I,j) = S_b(i+1,j,k) |
| 1660 | ! These pressures are only used for the equation of state, and are only a function of | |
| 1661 | ! height, consistent with the expressions in the int_density_dz routines. | |
| 1662 | 0 | p_int_W(I,j) = -GxRho0*(e(i,j,K+1) - Z_0p(i,j)) |
| 1663 | 0 | p_int_E(I,j) = -GxRho0*(e(i+1,j,K+1) - Z_0p(i,j)) |
| 1664 | ||
| 1665 | 0 | intx_pa_nonlin(I,j) = intx_pa(I,j,K+1) - 0.5*(pa(i,j,K+1) + pa(i+1,j,K+1)) |
| 1666 | 0 | dgeo_x(I,j) = GV%g_Earth * (e(i+1,j,K+1)-e(i,j,K+1)) |
| 1667 | 0 | seek_x_cor(I,j) = .false. |
| 1668 | else | |
| 1669 | 0 | do_more_k = .true. |
| 1670 | endif | |
| 1671 | endif ; enddo ; enddo | |
| 1672 | 0 | if (.not.do_more_k) exit ! All reference interfaces have been found, so stop working downward. |
| 1673 | enddo | |
| 1674 | ||
| 1675 | 0 | if (do_more_k) then |
| 1676 | 0 | if (CS%reset_intxpa_flattest) then |
| 1677 | ! There are still points where a correction is needed, so use flattest interface | |
| 1678 | 0 | do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then |
| 1679 | ! choose top layer first | |
| 1680 | 0 | T_int_W(I,j) = T_top(i,j) ; T_int_E(I,j) = T_top(i+1,j) |
| 1681 | 0 | S_int_W(I,j) = S_top(i,j) ; S_int_E(I,j) = S_top(i+1,j) |
| 1682 | 0 | p_int_W(I,j) = -GxRho0*(e(i,j,1) - Z_0p(i,j)) |
| 1683 | 0 | p_int_E(I,j) = -GxRho0*(e(i+1,j,1) - Z_0p(i,j)) |
| 1684 | 0 | intx_pa_nonlin(I,j) = intx_pa(I,j,1) - 0.5*(pa(i,j,1) + pa(i+1,j,1)) |
| 1685 | 0 | dgeo_x(I,j) = GV%g_Earth * (e(i+1,j,1)-e(i,j,1)) |
| 1686 | 0 | delta_z_x(I,j) = abs(e(i+1,j,1)-e(i,j,1)) |
| 1687 | 0 | do k=1,nz |
| 1688 | 0 | if (abs(e(i+1,j,k+1)-e(i,j,k+1)) < delta_z_x(I,j)) then |
| 1689 | ! bottom of layer is less sloped than top. Use this layer | |
| 1690 | 0 | delta_z_x(I,j) = abs(e(i+1,j,k+1)-e(i,j,k+1)) |
| 1691 | 0 | T_int_W(I,j) = T_b(i,j,k) ; T_int_E(I,j) = T_b(i+1,j,k) |
| 1692 | 0 | S_int_W(I,j) = S_b(i,j,k) ; S_int_E(I,j) = S_b(i+1,j,k) |
| 1693 | 0 | p_int_W(I,j) = -GxRho0*(e(i,j,K+1) - Z_0p(i,j)) |
| 1694 | 0 | p_int_E(I,j) = -GxRho0*(e(i+1,j,K+1) - Z_0p(i,j)) |
| 1695 | 0 | intx_pa_nonlin(I,j) = intx_pa(I,j,K+1) - 0.5*(pa(i,j,K+1) + pa(i+1,j,K+1)) |
| 1696 | 0 | dgeo_x(I,j) = GV%g_Earth * (e(i+1,j,K+1)-e(i,j,K+1)) |
| 1697 | endif | |
| 1698 | enddo | |
| 1699 | 0 | seek_x_cor(I,j) = .false. |
| 1700 | endif ; enddo ; enddo | |
| 1701 | else | |
| 1702 | ! There are still points where a correction is needed, so use the top interface for lack of a better idea? | |
| 1703 | 0 | do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then |
| 1704 | 0 | T_int_W(I,j) = T_top(i,j) ; T_int_E(I,j) = T_top(i+1,j) |
| 1705 | 0 | S_int_W(I,j) = S_top(i,j) ; S_int_E(I,j) = S_top(i+1,j) |
| 1706 | 0 | p_int_W(I,j) = -GxRho0*(e(i,j,1) - Z_0p(i,j)) |
| 1707 | 0 | p_int_E(I,j) = -GxRho0*(e(i+1,j,1) - Z_0p(i,j)) |
| 1708 | 0 | intx_pa_nonlin(I,j) = intx_pa(I,j,1) - 0.5*(pa(i,j,1) + pa(i+1,j,1)) |
| 1709 | 0 | dgeo_x(I,j) = GV%g_Earth * (e(i+1,j,1)-e(i,j,1)) |
| 1710 | 0 | seek_x_cor(I,j) = .false. |
| 1711 | endif ; enddo ; enddo | |
| 1712 | endif | |
| 1713 | endif | |
| 1714 | ||
| 1715 | 0 | do j=js,je |
| 1716 | 0 | do I=Isq,Ieq |
| 1717 | ! This expression assumes that temperature and salinity vary linearly with hieght | |
| 1718 | ! between the corners of the reference interfaces found above to get a correction to | |
| 1719 | ! intx_pa that takes nonlinearities in the equation of state into account. | |
| 1720 | ! It is derived from a 5 point quadrature estimate of the integral with a large-scale | |
| 1721 | ! linear correction so that the pressures and heights match at the end-points. It turns | |
| 1722 | ! out that this linear correction cancels out the mid-point density anomaly. | |
| 1723 | ! This can be used without masking because dgeo_x and intx_pa_nonlin are 0 over land. | |
| 1724 | 0 | T5(1) = T_Int_W(I,j) ; S5(1) = S_Int_W(I,j) ; p5(1) = p_Int_W(I,j) |
| 1725 | 0 | T5(5) = T_Int_E(I,j) ; S5(5) = S_Int_E(I,j) ; p5(5) = p_Int_E(I,j) |
| 1726 | 0 | T5(2) = 0.25*(3.0*T5(1) + T5(5)) ; T5(4) = 0.25*(3.0*T5(5) + T5(1)) ; T5(3) = 0.5*(T5(5) + T5(1)) |
| 1727 | 0 | S5(2) = 0.25*(3.0*S5(1) + S5(5)) ; S5(4) = 0.25*(3.0*S5(5) + S5(1)) ; S5(3) = 0.5*(S5(5) + S5(1)) |
| 1728 | 0 | p5(2) = 0.25*(3.0*p5(1) + p5(5)) ; p5(4) = 0.25*(3.0*p5(5) + p5(1)) ; p5(3) = 0.5*(p5(5) + p5(1)) |
| 1729 | 0 | call calculate_density(T5, S5, p5, r5, tv%eqn_of_state, rho_ref=rho_ref) |
| 1730 | ||
| 1731 | ! Note the consistency with the linear form below because (4.75 + 5.5/2) / 90 = 1/12 | |
| 1732 | intx_pa_cor_ri(I,j) = C1_90 * (4.75*(r5(5)-r5(1)) + 5.5*(r5(4)-r5(2))) * dgeo_x(I,j) - & | |
| 1733 | 0 | intx_pa_nonlin(I,j) |
| 1734 | enddo | |
| 1735 | enddo | |
| 1736 | ||
| 1737 | ! Repeat the calculations above for v-velocity points. | |
| 1738 | 0 | T_int_S(:,:) = 0.0 ; S_int_S(:,:) = 0.0 ; p_int_S(:,:) = 0.0 |
| 1739 | 0 | T_int_N(:,:) = 0.0 ; S_int_N(:,:) = 0.0 ; p_int_N(:,:) = 0.0 |
| 1740 | 0 | inty_pa_nonlin(:,:) = 0.0 ; dgeo_y(:,:) = 0.0 ; inty_pa_cor_ri(:,:) = 0.0 |
| 1741 | 0 | do J=Jsq,Jeq ; do i=is,ie |
| 1742 | 0 | seek_y_cor(i,J) = (G%mask2dCv(i,J) > 0.) |
| 1743 | 0 | delta_z_y(i,J) = 0.0 |
| 1744 | enddo ; enddo | |
| 1745 | ||
| 1746 | 0 | do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then |
| 1747 | 0 | if ((e(i,j+1,2) <= e(i,j,1)) .and. (e(i,j,2) <= e(i,j+1,1))) then |
| 1748 | ! This is a typical case in the open ocean, so use the topmost interface. | |
| 1749 | 0 | T_int_S(i,J) = T_top(i,j) ; T_int_N(i,J) = T_top(i,j+1) |
| 1750 | 0 | S_int_S(i,J) = S_top(i,j) ; S_int_N(i,J) = S_top(i,j+1) |
| 1751 | 0 | p_int_S(i,J) = -GxRho0*(e(i,j,1) - Z_0p(i,j)) |
| 1752 | 0 | p_int_N(i,J) = -GxRho0*(e(i,j+1,1) - Z_0p(i,j)) |
| 1753 | 0 | inty_pa_nonlin(i,J) = inty_pa(i,J,1) - 0.5*(pa(i,j,1) + pa(i,j+1,1)) |
| 1754 | 0 | dgeo_y(i,J) = GV%g_Earth * (e(i,j+1,1)-e(i,j,1)) |
| 1755 | 0 | seek_y_cor(i,J) = .false. |
| 1756 | endif | |
| 1757 | endif ; enddo ; enddo | |
| 1758 | ||
| 1759 | 0 | do k=1,nz |
| 1760 | 0 | do_more_k = .false. |
| 1761 | 0 | do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then |
| 1762 | ! Find the topmost layer for which both sides are nonvanished and mass-weighting is not | |
| 1763 | ! activated in the subgrid interpolation. | |
| 1764 | 0 | if (((h(i,j,k) > CS%h_nonvanished) .and. (h(i,j+1,k) > CS%h_nonvanished)) .and. & |
| 1765 | (max(0., e(i,j+1,K+1)-e(i,j,1), e(i,j,K+1)-e(i,j+1,1)) <= 0.0)) then | |
| 1766 | ! Store properties at the bottom of this cell to get a "good estimate" for intypa at | |
| 1767 | ! the interface below this cell (it might have quadratic pressure dependence if sloped) | |
| 1768 | 0 | T_int_S(i,J) = T_b(i,j,k) ; T_int_N(i,J) = T_b(i,j+1,k) |
| 1769 | 0 | S_int_S(i,J) = S_b(i,j,k) ; S_int_N(i,J) = S_b(i,j+1,k) |
| 1770 | ! These pressures are only used for the equation of state, and are only a function of | |
| 1771 | ! height, consistent with the expressions in the int_density_dz routines. | |
| 1772 | 0 | p_int_S(i,J) = -GxRho0*(e(i,j,K+1) - Z_0p(i,j)) |
| 1773 | 0 | p_int_N(i,J) = -GxRho0*(e(i,j+1,K+1) - Z_0p(i,j)) |
| 1774 | 0 | inty_pa_nonlin(i,J) = inty_pa(i,J,K+1) - 0.5*(pa(i,j,K+1) + pa(i,j+1,K+1)) |
| 1775 | 0 | dgeo_y(i,J) = GV%g_Earth * (e(i,j+1,K+1)-e(i,j,K+1)) |
| 1776 | 0 | seek_y_cor(i,J) = .false. |
| 1777 | else | |
| 1778 | 0 | do_more_k = .true. |
| 1779 | endif | |
| 1780 | endif ; enddo ; enddo | |
| 1781 | 0 | if (.not.do_more_k) exit ! All reference interfaces have been found, so stop working downward. |
| 1782 | enddo | |
| 1783 | ||
| 1784 | 0 | if (do_more_k) then |
| 1785 | 0 | if (CS%reset_intxpa_flattest) then |
| 1786 | ! There are still points where a correction is needed, so use flattest interface. | |
| 1787 | 0 | do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then |
| 1788 | ! choose top interface first | |
| 1789 | 0 | T_int_S(i,J) = T_top(i,j) ; T_int_N(i,J) = T_top(i,j+1) |
| 1790 | 0 | S_int_S(i,J) = S_top(i,j) ; S_int_N(i,J) = S_top(i,j+1) |
| 1791 | 0 | p_int_S(i,J) = -GxRho0*(e(i,j,1) - Z_0p(i,j)) |
| 1792 | 0 | p_int_N(i,J) = -GxRho0*(e(i,j+1,1) - Z_0p(i,j)) |
| 1793 | 0 | inty_pa_nonlin(i,J) = inty_pa(i,J,1) - 0.5*(pa(i,j,1) + pa(i,j+1,1)) |
| 1794 | 0 | dgeo_y(i,J) = GV%g_Earth * (e(i,j+1,1)-e(i,j,1)) |
| 1795 | 0 | delta_z_y(i,J) = abs(e(i,j+1,1)-e(i,j,1)) |
| 1796 | 0 | do k=1,nz |
| 1797 | 0 | if (abs(e(i,j+1,k+1)-e(i,j,k+1)) < delta_z_y(i,J)) then |
| 1798 | ! bottom of layer is less sloped than top. Use this layer | |
| 1799 | 0 | delta_z_y(i,J) = abs(e(i,j+1,k+1)-e(i,j,k+1)) |
| 1800 | 0 | T_int_S(i,J) = T_b(i,j,k) ; T_int_N(i,J) = T_b(i,j+1,k) |
| 1801 | 0 | S_int_S(i,J) = S_b(i,j,k) ; S_int_N(i,J) = S_b(i,j+1,k) |
| 1802 | 0 | p_int_S(i,J) = -GxRho0*(e(i,j,k+1) - Z_0p(i,j)) |
| 1803 | 0 | p_int_N(i,J) = -GxRho0*(e(i,j+1,k+1) - Z_0p(i,j)) |
| 1804 | 0 | inty_pa_nonlin(i,J) = inty_pa(i,J,k+1) - 0.5*(pa(i,j,k+1) + pa(i,j+1,k+1)) |
| 1805 | 0 | dgeo_y(i,J) = GV%g_Earth * (e(i,j+1,k+1)-e(i,j,k+1)) |
| 1806 | endif | |
| 1807 | enddo | |
| 1808 | 0 | seek_y_cor(i,J) = .false. |
| 1809 | endif ; enddo ; enddo | |
| 1810 | else | |
| 1811 | ! There are still points where a correction is needed, so use the top interface for lack of a better idea? | |
| 1812 | 0 | do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then |
| 1813 | 0 | T_int_S(i,J) = T_top(i,j) ; T_int_N(i,J) = T_top(i,j+1) |
| 1814 | 0 | S_int_S(i,J) = S_top(i,j) ; S_int_N(i,J) = S_top(i,j+1) |
| 1815 | 0 | p_int_S(i,J) = -GxRho0*(e(i,j,1) - Z_0p(i,j)) |
| 1816 | 0 | p_int_N(i,J) = -GxRho0*(e(i,j+1,1) - Z_0p(i,j)) |
| 1817 | 0 | inty_pa_nonlin(i,J) = inty_pa(i,J,1) - 0.5*(pa(i,j,1) + pa(i,j+1,1)) |
| 1818 | 0 | dgeo_y(i,J) = GV%g_Earth * (e(i,j+1,1)-e(i,j,1)) |
| 1819 | 0 | seek_y_cor(i,J) = .false. |
| 1820 | endif ; enddo ; enddo | |
| 1821 | endif | |
| 1822 | endif | |
| 1823 | ||
| 1824 | 0 | do J=Jsq,Jeq |
| 1825 | 0 | do i=is,ie |
| 1826 | ! This expression assumes that temperature and salinity vary linearly with hieght | |
| 1827 | ! between the corners of the reference interfaces found above to get a correction to | |
| 1828 | ! intx_pa that takes nonlinearities in the equation of state into account. | |
| 1829 | ! It is derived from a 5 point quadrature estimate of the integral with a large-scale | |
| 1830 | ! linear correction so that the pressures and heights match at the end-points. It turns | |
| 1831 | ! out that this linear correction cancels out the mid-point density anomaly. | |
| 1832 | ! This can be used without masking because dgeo_y and inty_pa_nonlin are 0 over land. | |
| 1833 | 0 | T5(1) = T_Int_S(i,J) ; S5(1) = S_Int_S(i,J) ; p5(1) = p_Int_S(i,J) |
| 1834 | 0 | T5(5) = T_Int_N(i,J) ; S5(5) = S_Int_N(i,J) ; p5(5) = p_Int_N(i,J) |
| 1835 | 0 | T5(2) = 0.25*(3.0*T5(1) + T5(5)) ; T5(4) = 0.25*(3.0*T5(5) + T5(1)) ; T5(3) = 0.5*(T5(5) + T5(1)) |
| 1836 | 0 | S5(2) = 0.25*(3.0*S5(1) + S5(5)) ; S5(4) = 0.25*(3.0*S5(5) + S5(1)) ; S5(3) = 0.5*(S5(5) + S5(1)) |
| 1837 | 0 | p5(2) = 0.25*(3.0*p5(1) + p5(5)) ; p5(4) = 0.25*(3.0*p5(5) + p5(1)) ; p5(3) = 0.5*(p5(5) + p5(1)) |
| 1838 | 0 | call calculate_density(T5, S5, p5, r5, tv%eqn_of_state, rho_ref=rho_ref) |
| 1839 | ||
| 1840 | ! Note the consistency with the linear form below because (4.75 + 5.5/2) / 90 = 1/12 | |
| 1841 | inty_pa_cor_ri(i,J) = C1_90 * (4.75*(r5(5)-r5(1)) + 5.5*(r5(4)-r5(2))) * dgeo_y(i,J) - & | |
| 1842 | 0 | inty_pa_nonlin(i,J) |
| 1843 | enddo | |
| 1844 | enddo | |
| 1845 | ||
| 1846 | ! Correct intx_pa and inty_pa at each interface using vertically constant corrections. | |
| 1847 | 0 | do K=1,nz+1 ; do j=js,je ; do I=Isq,Ieq |
| 1848 | 0 | intx_pa(I,j,K) = intx_pa(I,j,K) + intx_pa_cor_ri(I,j) |
| 1849 | enddo ; enddo ; enddo | |
| 1850 | ||
| 1851 | 0 | do K=1,nz+1 ; do J=Jsq,Jeq ; do i=is,ie |
| 1852 | 0 | inty_pa(i,J,K) = inty_pa(i,J,K) + inty_pa_cor_ri(i,J) |
| 1853 | enddo ; enddo ; enddo | |
| 1854 | ||
| 1855 | ! TODO temporarily move back to CPU | |
| 1856 | !$omp target update to(intx_pa, inty_pa) | |
| 1857 | endif ! intx_pa and inty_pa have now been reset to reflect the properties of an unimpeded interface. | |
| 1858 | ||
| 1859 | ! NOTE: None of these `enter data` statements should be here. They are only | |
| 1860 | ! temporarily being used to set up the data regions below. | |
| 1861 | ! Eventually, they should be set up *outside* of the function. | |
| 1862 | ||
| 1863 | !$omp target enter data if(use_EOS) & | |
| 1864 | !$omp map(to: tv, EOSdom2d) | |
| 1865 | ||
| 1866 | ! NOTE: e_sal condition could be sharpened, but this is close enough. | |
| 1867 | !$omp target enter data map(to: e_tidal_eq, e_tidal_sal, e_sal_and_tide) if (CS%tides) | |
| 1868 | !$omp target enter data map(to: e_sal) if (CS%calculate_SAL) | |
| 1869 | ||
| 1870 | !$omp target data map(to: h) | |
| 1871 | ||
| 1872 | 24 | do concurrent (k=1:nz, j=js:je, I=Isq:Ieq) |
| 1873 | PFu(I,j,k) = (((pa(i,j,K)*h(i,j,k) + intz_dpa(i,j,k)) - & | |
| 1874 | (pa(i+1,j,K)*h(i+1,j,k) + intz_dpa(i+1,j,k))) + & | |
| 1875 | ((h(i+1,j,k) - h(i,j,k)) * intx_pa(I,j,K) - & | |
| 1876 | (e(i+1,j,K+1) - e(i,j,K+1)) * intx_dpa(I,j,k) * GV%Z_to_H)) * & | |
| 1877 | ((2.0*I_Rho0*G%IdxCu(I,j)) / & | |
| 1878 | 13245168 | ((h(i,j,k) + h(i+1,j,k)) + h_neglect)) |
| 1879 | enddo | |
| 1880 | ||
| 1881 | ! Compute pressure gradient in y direction | |
| 1882 | 24 | do concurrent (k=1:nz, J=Jsq:Jeq, i=is:ie) |
| 1883 | PFv(i,J,k) = (((pa(i,j,K)*h(i,j,k) + intz_dpa(i,j,k)) - & | |
| 1884 | (pa(i,j+1,K)*h(i,j+1,k) + intz_dpa(i,j+1,k))) + & | |
| 1885 | ((h(i,j+1,k) - h(i,j,k)) * inty_pa(i,J,K) - & | |
| 1886 | (e(i,j+1,K+1) - e(i,j,K+1)) * inty_dpa(i,J,k) * GV%Z_to_H)) * & | |
| 1887 | ((2.0*I_Rho0*G%IdyCv(i,J)) / & | |
| 1888 | 13354584 | ((h(i,j,k) + h(i,j+1,k)) + h_neglect)) |
| 1889 | enddo | |
| 1890 | ||
| 1891 | !$omp end target data | |
| 1892 | ||
| 1893 | 24 | if (CS%tides_answer_date>20230630 .and. CS%bq_sal_tides) then |
| 1894 | ! Calculate SAL geopotential anomaly and add its gradient to pressure | |
| 1895 | ! gradient force | |
| 1896 | 0 | if (CS%calculate_SAL) then |
| 1897 | 0 | do concurrent (k=1:nz, j=js:je, I=Isq:Ieq) |
| 1898 | 0 | PFu(I,j,k) = PFu(I,j,k) + (e_sal(i+1,j) - e_sal(i,j)) * GV%g_Earth * G%IdxCu(I,j) |
| 1899 | enddo | |
| 1900 | 0 | do concurrent (k=1:nz, J=Jsq:Jeq, i=is:ie) |
| 1901 | 0 | PFv(i,J,k) = PFv(i,J,k) + (e_sal(i,j+1) - e_sal(i,j)) * GV%g_Earth * G%IdyCv(i,J) |
| 1902 | enddo | |
| 1903 | endif | |
| 1904 | ||
| 1905 | ! Calculate tidal geopotential anomaly and add its gradient to pressure | |
| 1906 | ! gradient force | |
| 1907 | 0 | if (CS%tides) then |
| 1908 | 0 | do concurrent (k=1:nz, j=js:je, I=Isq:Ieq) |
| 1909 | PFu(I,j,k) = PFu(I,j,k) + ((e_tidal_eq(i+1,j) + e_tidal_sal(i+1,j)) & | |
| 1910 | 0 | - (e_tidal_eq(i,j) + e_tidal_sal(i,j))) * GV%g_Earth * G%IdxCu(I,j) |
| 1911 | enddo | |
| 1912 | 0 | do concurrent (k=1:nz, J=Jsq:Jeq, i=is:ie) |
| 1913 | PFv(i,J,k) = PFv(i,J,k) + ((e_tidal_eq(i,j+1) + e_tidal_sal(i,j+1)) & | |
| 1914 | 0 | - (e_tidal_eq(i,j) + e_tidal_sal(i,j))) * GV%g_Earth * G%IdyCv(i,J) |
| 1915 | enddo | |
| 1916 | endif | |
| 1917 | endif | |
| 1918 | ||
| 1919 | 24 | if (CS%GFS_scale < 1.0) then |
| 1920 | ! Adjust the Montgomery potential to make this a reduced gravity model. | |
| 1921 | !$omp target data map(alloc: dM) | |
| 1922 | ||
| 1923 | 0 | if (use_EOS) then |
| 1924 | !$omp target data map(alloc: rho_in_situ) | |
| 1925 | ||
| 1926 | 0 | if (use_p_atm) then |
| 1927 | call calculate_density(tv_tmp%T(:,:,1), tv_tmp%S(:,:,1), p_atm, rho_in_situ, & | |
| 1928 | 0 | tv%eqn_of_state, EOSdom2d) |
| 1929 | else | |
| 1930 | !$omp target data map(alloc: p0) | |
| 1931 | ||
| 1932 | !$omp target | |
| 1933 | !$omp parallel loop collapse(2) | |
| 1934 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 1935 | 0 | p0(i,j) = 0. |
| 1936 | enddo ; enddo | |
| 1937 | !$omp end target | |
| 1938 | ||
| 1939 | call calculate_density(tv_tmp%T(:,:,1), tv_tmp%S(:,:,1), p0, rho_in_situ, & | |
| 1940 | 0 | tv%eqn_of_state, EOSdom2d) |
| 1941 | !$omp end target data | |
| 1942 | endif | |
| 1943 | ||
| 1944 | !$omp target | |
| 1945 | !$omp parallel loop collapse(2) | |
| 1946 | 0 | do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 1947 | 0 | dM(i,j) = (CS%GFS_scale - 1.0) * (G_Rho0 * rho_in_situ(i,j)) * (e(i,j,1) - G%Z_ref) |
| 1948 | enddo ; enddo | |
| 1949 | !$omp end target | |
| 1950 | ||
| 1951 | !$omp end target data | |
| 1952 | else | |
| 1953 | 0 | do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1) |
| 1954 | 0 | dM(i,j) = (CS%GFS_scale - 1.0) * (G_Rho0 * GV%Rlay(1)) * (e(i,j,1) - G%Z_ref) |
| 1955 | enddo | |
| 1956 | endif | |
| 1957 | ||
| 1958 | 0 | do concurrent (k=1:nz, j=js:je, I=Isq:Ieq) |
| 1959 | 0 | PFu(I,j,k) = PFu(I,j,k) - (dM(i+1,j) - dM(i,j)) * G%IdxCu(I,j) |
| 1960 | enddo | |
| 1961 | 0 | do concurrent (k=1:nz, J=Jsq:Jeq, i=is:ie) |
| 1962 | 0 | PFv(i,J,k) = PFv(i,J,k) - (dM(i,j+1) - dM(i,j)) * G%IdyCv(i,J) |
| 1963 | enddo | |
| 1964 | ||
| 1965 | !$omp end target data | |
| 1966 | endif | |
| 1967 | ||
| 1968 | 24 | if (present(pbce)) then |
| 1969 | 24 | call set_pbce_Bouss(e, tv_tmp, G, GV, US, rho0_set_pbce, CS%GFS_scale, pbce, nkblock=nkblock) |
| 1970 | endif | |
| 1971 | ||
| 1972 | !$omp target exit data if (use_EOS) & | |
| 1973 | !$omp map(delete:tv_tmp, tv_tmp%T, tv_tmp%S, tv, tv%eqn_of_state, EOSdom2d) | |
| 1974 | ||
| 1975 | !$omp target exit data map(delete: Z_0p) if (use_EOS) | |
| 1976 | ||
| 1977 | !$omp target exit data & | |
| 1978 | !$omp map(delete: pa, dpa) & | |
| 1979 | !$omp map(delete: intx_pa, inty_pa, intx_dpa, inty_dpa, intz_dpa) | |
| 1980 | ||
| 1981 | 24 | if (present(eta)) then |
| 1982 | ! eta is the sea surface height relative to a time-invariant geoid, for comparison with | |
| 1983 | ! what is used for eta in btstep. See how e was calculated about 200 lines above. | |
| 1984 | 2952 | do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1) |
| 1985 | 184488 | eta(i,j) = e(i,j,1)*GV%Z_to_H |
| 1986 | enddo | |
| 1987 | ||
| 1988 | 24 | if (CS%tides .and. (.not.CS%bq_sal_tides)) then |
| 1989 | 0 | if (CS%tides_answer_date>20230630) then |
| 1990 | 0 | do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1) |
| 1991 | 0 | eta(i,j) = eta(i,j) + (e_tidal_eq(i,j)+e_tidal_sal(i,j))*GV%Z_to_H |
| 1992 | enddo | |
| 1993 | else | |
| 1994 | 0 | do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1) |
| 1995 | 0 | eta(i,j) = eta(i,j) + e_sal_and_tide(i,j)*GV%Z_to_H |
| 1996 | enddo | |
| 1997 | endif | |
| 1998 | endif | |
| 1999 | ||
| 2000 | 24 | if (CS%calculate_SAL .and. (CS%tides_answer_date>20230630) .and. (.not.CS%bq_sal_tides)) then |
| 2001 | 0 | do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1) |
| 2002 | 0 | eta(i,j) = eta(i,j) + e_sal(i,j)*GV%Z_to_H |
| 2003 | enddo | |
| 2004 | endif | |
| 2005 | endif | |
| 2006 | ||
| 2007 | !$omp target exit data map(delete: e, T_t, T_b, S_t, S_b) | |
| 2008 | !$omp target exit data map(delete: e_tidal_eq, e_tidal_sal, e_sal_and_tide) if (CS%tides) | |
| 2009 | !$omp target exit data map(delete: e_sal) if (CS%calculate_SAL) | |
| 2010 | ||
| 2011 | 24 | if (CS%use_stanley_pgf) then |
| 2012 | ! Calculated diagnostics related to the Stanley parameterization | |
| 2013 | 0 | zeros(:) = 0.0 |
| 2014 | 0 | EOSdom_h(:) = EOS_domain(G%HI) |
| 2015 | 0 | if ((CS%id_p_stanley>0) .or. (CS%id_rho_pgf>0) .or. (CS%id_rho_stanley_pgf>0)) then |
| 2016 | ! Find the pressure at the mid-point of each layer. | |
| 2017 | 0 | H_to_RL2_T2 = GV%g_Earth*GV%H_to_RZ |
| 2018 | 0 | if (use_p_atm) then |
| 2019 | 0 | do j=js,je ; do i=is,ie |
| 2020 | 0 | p_stanley(i,j,1) = 0.5*h(i,j,1) * H_to_RL2_T2 + p_atm(i,j) |
| 2021 | enddo ; enddo | |
| 2022 | else | |
| 2023 | 0 | do j=js,je ; do i=is,ie |
| 2024 | 0 | p_stanley(i,j,1) = 0.5*h(i,j,1) * H_to_RL2_T2 |
| 2025 | enddo ; enddo | |
| 2026 | endif | |
| 2027 | 0 | do k=2,nz ; do j=js,je ; do i=is,ie |
| 2028 | 0 | p_stanley(i,j,k) = p_stanley(i,j,k-1) + 0.5*(h(i,j,k-1) + h(i,j,k)) * H_to_RL2_T2 |
| 2029 | enddo ; enddo ; enddo | |
| 2030 | endif | |
| 2031 | 0 | if (CS%id_p_stanley>0) call post_data(CS%id_p_stanley, p_stanley, CS%diag) |
| 2032 | 0 | if (CS%id_rho_pgf>0) then |
| 2033 | 0 | do k=1,nz ; do j=js,je |
| 2034 | call calculate_density(tv%T(:,j,k), tv%S(:,j,k), p_stanley(:,j,k), zeros, & | |
| 2035 | 0 | zeros, zeros, rho_pgf(:,j,k), tv%eqn_of_state, EOSdom_h) |
| 2036 | enddo ; enddo | |
| 2037 | 0 | call post_data(CS%id_rho_pgf, rho_pgf, CS%diag) |
| 2038 | endif | |
| 2039 | 0 | if (CS%id_rho_stanley_pgf>0) then |
| 2040 | 0 | do k=1,nz ; do j=js,je |
| 2041 | call calculate_density(tv%T(:,j,k), tv%S(:,j,k), p_stanley(:,j,k), tv%varT(:,j,k), & | |
| 2042 | 0 | zeros, zeros, rho_stanley_pgf(:,j,k), tv%eqn_of_state, EOSdom_h) |
| 2043 | enddo ; enddo | |
| 2044 | 0 | call post_data(CS%id_rho_stanley_pgf, rho_stanley_pgf, CS%diag) |
| 2045 | endif | |
| 2046 | endif | |
| 2047 | ||
| 2048 | 24 | if (CS%id_MassWt_u>0) call post_data(CS%id_MassWt_u, MassWt_u, CS%diag) |
| 2049 | 24 | if (CS%id_MassWt_v>0) call post_data(CS%id_MassWt_v, MassWt_v, CS%diag) |
| 2050 | ||
| 2051 | 24 | if (CS%id_rho_pgf>0) call post_data(CS%id_rho_pgf, rho_pgf, CS%diag) |
| 2052 | 24 | if (CS%id_rho_stanley_pgf>0) call post_data(CS%id_rho_stanley_pgf, rho_stanley_pgf, CS%diag) |
| 2053 | 24 | if (CS%id_p_stanley>0) call post_data(CS%id_p_stanley, p_stanley, CS%diag) |
| 2054 | ||
| 2055 | ! Diagnostics for tidal forcing and SAL height anomaly | |
| 2056 | 24 | if (CS%id_e_tide>0) then |
| 2057 | ! To be consistent with old runs, tidal forcing diagnostic also includes total SAL. | |
| 2058 | ! New diagnostics are given for each individual field. | |
| 2059 | 0 | if (CS%tides_answer_date>20230630) then ; do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 2060 | 0 | e_sal_and_tide(i,j) = e_sal(i,j) + e_tidal_eq(i,j) + e_tidal_sal(i,j) |
| 2061 | enddo ; enddo ; endif | |
| 2062 | 0 | call post_data(CS%id_e_tide, e_sal_and_tide, CS%diag) |
| 2063 | endif | |
| 2064 | 24 | if (CS%id_e_sal>0) call post_data(CS%id_e_sal, e_sal, CS%diag) |
| 2065 | 24 | if (CS%id_e_tidal_eq>0) call post_data(CS%id_e_tidal_eq, e_tidal_eq, CS%diag) |
| 2066 | 24 | if (CS%id_e_tidal_sal>0) call post_data(CS%id_e_tidal_sal, e_tidal_sal, CS%diag) |
| 2067 | ||
| 2068 | ! Diagnostics for tidal forcing and SAL horizontal gradients | |
| 2069 | 24 | if (CS%calculate_SAL .and. ((associated(ADp%sal_u) .or. associated(ADp%sal_v)))) then |
| 2070 | 0 | if (CS%tides) then ; do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 |
| 2071 | 0 | e_sal(i,j) = e_sal(i,j) + e_tidal_sal(i,j) |
| 2072 | enddo ; enddo ; endif | |
| 2073 | 0 | if (CS%bq_sal_tides) then |
| 2074 | ! sal_u = ( e(i+1) - e(i) ) * g / dx | |
| 2075 | 0 | if (associated(ADp%sal_u)) then ; do k=1,nz ; do j=js,je ; do I=Isq,Ieq |
| 2076 | 0 | ADp%sal_u(I,j,k) = (e_sal(i+1,j) - e_sal(i,j)) * GV%g_Earth * G%IdxCu(I,j) |
| 2077 | enddo ; enddo ; enddo ; endif | |
| 2078 | 0 | if (associated(ADp%sal_v)) then ; do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie |
| 2079 | 0 | ADp%sal_v(i,J,k) = (e_sal(i,j+1) - e_sal(i,j)) * GV%g_Earth * G%IdyCv(i,J) |
| 2080 | enddo ; enddo ; enddo ; endif | |
| 2081 | else | |
| 2082 | ! sal_u = ( e(i+1) - e(i) ) * g / dx * (rho(k) / rho0) | |
| 2083 | 0 | if (associated(ADp%sal_u)) then ; do k=1,nz ; do j=js,je ; do I=Isq,Ieq |
| 2084 | ADp%sal_u(I,j,k) = (e_sal(i+1,j) - e_sal(i,j)) * G%IdxCu(I,j) * I_Rho0 * & | |
| 2085 | 0 | (2.0 * intx_dpa(I,j,k) * GV%Z_to_H / ((h(i,j,k) + h(i+1,j,k)) + h_neglect) + GxRho_ref) |
| 2086 | enddo ; enddo ; enddo ; endif | |
| 2087 | 0 | if (associated(ADp%sal_v)) then ; do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie |
| 2088 | ADp%sal_v(i,J,k) = (e_sal(i,j+1) - e_sal(i,j)) * G%IdyCv(i,J) * I_Rho0 * & | |
| 2089 | 0 | (2.0 * inty_dpa(i,J,k) * GV%Z_to_H / ((h(i,j,k) + h(i,j+1,k)) + h_neglect) + GxRho_ref) |
| 2090 | enddo ; enddo ; enddo ; endif | |
| 2091 | endif | |
| 2092 | 0 | if (CS%id_sal_u>0) call post_data(CS%id_sal_u, ADp%sal_u, CS%diag) |
| 2093 | 0 | if (CS%id_sal_v>0) call post_data(CS%id_sal_v, ADp%sal_v, CS%diag) |
| 2094 | endif | |
| 2095 | ||
| 2096 | 24 | if (CS%tides .and. ((associated(ADp%tides_u) .or. associated(ADp%tides_v)))) then |
| 2097 | 0 | if (CS%bq_sal_tides) then |
| 2098 | ! tides_u = ( e(i+1) - e(i) ) * g / dx | |
| 2099 | 0 | if (associated(ADp%tides_u)) then ; do k=1,nz ; do j=js,je ; do I=Isq,Ieq |
| 2100 | 0 | ADp%tides_u(I,j,k) = (e_tidal_eq(i+1,j) - e_tidal_eq(i,j)) * GV%g_Earth * G%IdxCu(I,j) |
| 2101 | enddo ; enddo ; enddo ; endif | |
| 2102 | 0 | if (associated(ADp%tides_v)) then ; do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie |
| 2103 | 0 | ADp%tides_v(i,J,k) = (e_tidal_eq(i,j+1) - e_tidal_eq(i,j)) * GV%g_Earth * G%IdyCv(i,J) |
| 2104 | enddo ; enddo ; enddo ; endif | |
| 2105 | else | |
| 2106 | ! tides_u = ( e(i+1) - e(i) ) * g / dx * (rho(k) / rho0) | |
| 2107 | 0 | if (associated(ADp%tides_u)) then ; do k=1,nz ; do j=js,je ; do I=Isq,Ieq |
| 2108 | ADp%tides_u(I,j,k) = (e_tidal_eq(i+1,j) - e_tidal_eq(i,j)) * G%IdxCu(I,j) * I_Rho0 * & | |
| 2109 | 0 | (2.0 * intx_dpa(I,j,k) * GV%Z_to_H / ((h(i,j,k) + h(i+1,j,k)) + h_neglect) + GxRho_ref) |
| 2110 | enddo ; enddo ; enddo ; endif | |
| 2111 | 0 | if (associated(ADp%tides_v)) then ; do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie |
| 2112 | ADp%tides_v(i,J,k) = (e_tidal_eq(i,j+1) - e_tidal_eq(i,j)) * G%IdyCv(i,J) * I_Rho0 * & | |
| 2113 | 0 | (2.0 * inty_dpa(i,J,k) * GV%Z_to_H / ((h(i,j,k) + h(i,j+1,k)) + h_neglect) + GxRho_ref) |
| 2114 | enddo ; enddo ; enddo ; endif | |
| 2115 | endif | |
| 2116 | 0 | if (CS%id_tides_u>0) call post_data(CS%id_tides_u, ADp%tides_u, CS%diag) |
| 2117 | 0 | if (CS%id_tides_v>0) call post_data(CS%id_tides_v, ADp%tides_v, CS%diag) |
| 2118 | endif | |
| 2119 | 72 | end subroutine PressureForce_FV_Bouss |
| 2120 | ||
| 2121 | !> Initializes the finite volume pressure gradient control structure | |
| 2122 | 1 | subroutine PressureForce_FV_init(Time, G, GV, US, param_file, diag, CS, ADp, SAL_CSp, tides_CSp) |
| 2123 | type(time_type), target, intent(in) :: Time !< Current model time | |
| 2124 | type(ocean_grid_type), intent(in) :: G !< Ocean grid structure | |
| 2125 | type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure | |
| 2126 | type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type | |
| 2127 | type(param_file_type), intent(in) :: param_file !< Parameter file handles | |
| 2128 | type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure | |
| 2129 | type(PressureForce_FV_CS), intent(inout) :: CS !< Finite volume PGF control structure | |
| 2130 | type(accel_diag_ptrs), pointer :: ADp !< Acceleration diagnostic pointers | |
| 2131 | type(SAL_CS), intent(in), target, optional :: SAL_CSp !< SAL control structure | |
| 2132 | type(tidal_forcing_CS), intent(in), target, optional :: tides_CSp !< Tides control structure | |
| 2133 | ||
| 2134 | ! Local variables | |
| 2135 | real :: Stanley_coeff ! Coefficient relating the temperature gradient and sub-gridscale | |
| 2136 | ! temperature variance [nondim] | |
| 2137 | integer :: default_answer_date ! Global answer date | |
| 2138 | logical :: use_temperature ! If true, temperature and salinity are used as state variables. | |
| 2139 | logical :: use_EOS ! If true, density calculated from T & S using an equation of state. | |
| 2140 | logical :: useMassWghtInterp ! If true, use near-bottom mass weighting for T and S | |
| 2141 | logical :: MassWghtInterpTop ! If true, use near-surface mass weighting for T and S under ice shelves | |
| 2142 | logical :: MassWghtInterp_NonBous_bug ! If true, use a buggy mass weighting when non-Boussinesq | |
| 2143 | logical :: enable_bugs ! If true, the defaults for recently added bug-fix flags are set to | |
| 2144 | ! recreate the bugs, or if false bugs are only used if actively selected. | |
| 2145 | ! This include declares and sets the variable "version". | |
| 2146 | # include "version_variable.h" | |
| 2147 | character(len=40) :: mdl ! This module's name. | |
| 2148 | logical :: use_ALE ! If true, use the Vertical Lagrangian Remap algorithm | |
| 2149 | integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, nz | |
| 2150 | ||
| 2151 | 1 | isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed ; nz = GV%ke |
| 2152 | 1 | IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB |
| 2153 | ||
| 2154 | 1 | CS%initialized = .true. |
| 2155 | 1 | CS%diag => diag ; CS%Time => Time |
| 2156 | 1 | if (present(tides_CSp)) & |
| 2157 | 1 | CS%tides_CSp => tides_CSp |
| 2158 | 1 | if (present(SAL_CSp)) & |
| 2159 | 1 | CS%SAL_CSp => SAL_CSp |
| 2160 | ||
| 2161 | 1 | mdl = "MOM_PressureForce_FV" |
| 2162 | 1 | call log_version(param_file, mdl, version, "") |
| 2163 | call get_param(param_file, mdl, "DEBUG", CS%debug, & | |
| 2164 | "If true, write out verbose debugging data.", & | |
| 2165 | 1 | default=.false., debuggingParam=.true., do_not_log=.true.) |
| 2166 | call get_param(param_file, mdl, "RHO_PGF_REF", CS%rho_ref, & | |
| 2167 | "The reference density that is subtracted off when calculating pressure "//& | |
| 2168 | "gradient forces. Its inverse is subtracted off of specific volumes when "//& | |
| 2169 | "in non-Boussinesq mode. The default is RHO_0.", & | |
| 2170 | 1 | units="kg m-3", default=GV%Rho0*US%R_to_kg_m3, scale=US%kg_m3_to_R) |
| 2171 | call get_param(param_file, mdl, "ENABLE_BUGS_BY_DEFAULT", enable_bugs, & | |
| 2172 | 1 | default=.true., do_not_log=.true.) ! This is logged from MOM.F90. |
| 2173 | call get_param(param_file, mdl, "RHO_PGF_REF_BUG", CS%rho_ref_bug, & | |
| 2174 | "If true, recover a bug that RHO_0 (the mean seawater density in Boussinesq mode) "//& | |
| 2175 | "and RHO_PGF_REF (the subtracted reference density in finite volume pressure "//& | |
| 2176 | "gradient forces) are incorrectly interchanged in several instances in Boussinesq mode.", & | |
| 2177 | 1 | default=enable_bugs) |
| 2178 | call get_param(param_file, mdl, "TIDES", CS%tides, & | |
| 2179 | 1 | "If true, apply tidal momentum forcing.", default=.false.) |
| 2180 | 1 | if (CS%tides) then |
| 2181 | call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & | |
| 2182 | "This sets the default value for the various _ANSWER_DATE parameters.", & | |
| 2183 | 0 | default=99991231) |
| 2184 | call get_param(param_file, mdl, "TIDES_ANSWER_DATE", CS%tides_answer_date, "The vintage of "//& | |
| 2185 | "self-attraction and loading (SAL) and tidal forcing calculations. Setting "//& | |
| 2186 | "dates before 20230701 recovers old answers (Boussinesq and non-Boussinesq "//& | |
| 2187 | "modes) when SAL is part of the tidal forcing calculation. The answer "//& | |
| 2188 | "difference is only at bit level and due to a reordered summation. Setting "//& | |
| 2189 | "dates before 20250201 recovers answers (Boussinesq mode) that interface "//& | |
| 2190 | "heights are modified before pressure force integrals are calculated.", & | |
| 2191 | 0 | default=default_answer_date, do_not_log=(.not.CS%tides)) |
| 2192 | endif | |
| 2193 | call get_param(param_file, mdl, "CALCULATE_SAL", CS%calculate_SAL, & | |
| 2194 | 1 | "If true, calculate self-attraction and loading.", default=CS%tides) |
| 2195 | 1 | if (CS%calculate_SAL) & |
| 2196 | call get_param(param_file, '', "SAL_USE_BPA", CS%sal_use_bpa, default=.false., & | |
| 2197 | 0 | do_not_log=.true.) |
| 2198 | 1 | if ((CS%tides .or. CS%calculate_SAL) .and. GV%Boussinesq) & |
| 2199 | call get_param(param_file, mdl, "BOUSSINESQ_SAL_TIDES", CS%bq_sal_tides, "If true, "//& | |
| 2200 | "in Boussinesq mode, use an alternative method to include self-attraction "//& | |
| 2201 | "and loading (SAL) and tidal forcings in pressure gradient, in which their "//& | |
| 2202 | "gradients are calculated separately, instead of adding geopotential "//& | |
| 2203 | "anomalies as corrections to the interface height. This alternative method "//& | |
| 2204 | "elimates a baroclinic component of the SAL and tidal forcings.", & | |
| 2205 | 0 | default=.false.) |
| 2206 | call get_param(param_file, "MOM", "ENABLE_THERMODYNAMICS", use_temperature, & | |
| 2207 | "If true, Temperature and salinity are used as state variables.", & | |
| 2208 | 1 | default=.true., do_not_log=.true.) |
| 2209 | call get_param(param_file, "MOM", "USE_EOS", use_EOS, & | |
| 2210 | "If true, density is calculated from temperature and "//& | |
| 2211 | "salinity with an equation of state. If USE_EOS is "//& | |
| 2212 | "true, ENABLE_THERMODYNAMICS must be true as well.", & | |
| 2213 | 1 | default=use_temperature, do_not_log=.true.) |
| 2214 | ||
| 2215 | call get_param(param_file, mdl, "SSH_IN_EOS_PRESSURE_FOR_PGF", CS%use_SSH_in_Z0p, & | |
| 2216 | "If true, include contributions from the sea surface height in the height-based "//& | |
| 2217 | "pressure used in the equation of state calculations for the Boussinesq pressure "//& | |
| 2218 | "gradient forces, including adjustments for atmospheric or sea-ice pressure.", & | |
| 2219 | 1 | default=.false., do_not_log=.not.GV%Boussinesq) |
| 2220 | 1 | if (CS%tides .and. CS%tides_answer_date<=20250131 .and. CS%use_SSH_in_Z0p) & |
| 2221 | call MOM_error(FATAL, trim(mdl) // ", PressureForce_FV_init: SSH_IN_EOS_PRESSURE_FOR_PGF "//& | |
| 2222 | 0 | "needs to be FALSE to recover tide answers before 20250131.") |
| 2223 | ||
| 2224 | call get_param(param_file, "MOM", "USE_REGRIDDING", use_ALE, & | |
| 2225 | "If True, use the ALE algorithm (regridding/remapping). "//& | |
| 2226 | 1 | "If False, use the layered isopycnal algorithm.", default=.false. ) |
| 2227 | call get_param(param_file, mdl, "MASS_WEIGHT_IN_PRESSURE_GRADIENT", useMassWghtInterp, & | |
| 2228 | "If true, use mass weighting when interpolating T/S for integrals "//& | |
| 2229 | "near the bathymetry in FV pressure gradient calculations.", & | |
| 2230 | 1 | default=.false.) |
| 2231 | call get_param(param_file, mdl, "MASS_WEIGHT_IN_PRESSURE_GRADIENT_TOP", MassWghtInterpTop, & | |
| 2232 | "If true and MASS_WEIGHT_IN_PRESSURE_GRADIENT is true, use mass weighting when "//& | |
| 2233 | "interpolating T/S for integrals near the top of the water column in FV "//& | |
| 2234 | "pressure gradient calculations. ", & | |
| 2235 | 1 | default=useMassWghtInterp) |
| 2236 | call get_param(param_file, mdl, "MASS_WEIGHT_IN_PGF_NONBOUS_BUG", MassWghtInterp_NonBous_bug, & | |
| 2237 | "If true, use a masking bug in non-Boussinesq calculations with mass weighting "//& | |
| 2238 | "when interpolating T/S for integrals near the bathymetry in FV pressure "//& | |
| 2239 | "gradient calculations.", & | |
| 2240 | 1 | default=.false., do_not_log=(GV%Boussinesq .or. (.not.useMassWghtInterp))) |
| 2241 | call get_param(param_file, mdl, "MASS_WEIGHT_IN_PGF_VANISHED_ONLY", CS%MassWghtInterpVanOnly, & | |
| 2242 | "If true, use mass weighting when interpolating T/S for integrals "//& | |
| 2243 | "only if one side is vanished according to RESET_INTXPA_H_NONVANISHED. ", & | |
| 2244 | 1 | default=.false.) |
| 2245 | ||
| 2246 | 1 | CS%MassWghtInterp = 0 |
| 2247 | 1 | if (useMassWghtInterp) & |
| 2248 | 1 | CS%MassWghtInterp = ibset(CS%MassWghtInterp, 0) ! Same as CS%MassWghtInterp + 1 |
| 2249 | 1 | if (MassWghtInterpTop) & |
| 2250 | 1 | CS%MassWghtInterp = ibset(CS%MassWghtInterp, 1) ! Same as CS%MassWghtInterp + 2 |
| 2251 | 1 | if ((.not.GV%Boussinesq) .and. MassWghtInterp_NonBous_bug) & |
| 2252 | 0 | CS%MassWghtInterp = ibset(CS%MassWghtInterp, 3) ! Same as CS%MassWghtInterp + 8 |
| 2253 | ||
| 2254 | call get_param(param_file, mdl, "CORRECTION_INTXPA", CS%correction_intxpa, & | |
| 2255 | "If true, use a correction for surface pressure curvature in intx_pa.", & | |
| 2256 | 1 | default=.false., do_not_log=.not.use_EOS) |
| 2257 | call get_param(param_file, mdl, "RESET_INTXPA_INTEGRAL", CS%reset_intxpa_integral, & | |
| 2258 | "If true, reset INTXPA to match pressures at first nonvanished cell. "//& | |
| 2259 | 1 | "Includes pressure correction.", default=.false., do_not_log=.not.use_EOS) |
| 2260 | call get_param(param_file, mdl, "RESET_INTXPA_INTEGRAL_FLATTEST", CS%reset_intxpa_flattest, & | |
| 2261 | "If true, use flattest interface as reference interface where there is no "//& | |
| 2262 | "better choice for RESET_INTXPA_INTEGRAL. Otherwise, use surface interface.", & | |
| 2263 | 1 | default=.false., do_not_log=.not.use_EOS) |
| 2264 | 1 | if (.not.use_EOS) then ! These options do nothing without an equation of state. |
| 2265 | 0 | CS%correction_intxpa = .false. |
| 2266 | 0 | CS%reset_intxpa_integral = .false. |
| 2267 | 0 | CS%reset_intxpa_flattest = .false. |
| 2268 | endif | |
| 2269 | call get_param(param_file, mdl, "RESET_INTXPA_H_NONVANISHED", CS%h_nonvanished, & | |
| 2270 | "A minimal layer thickness that indicates that a layer is thick enough to usefully "//& | |
| 2271 | "reestimate the pressure integral across the interface below.", & | |
| 2272 | 1 | default=1.0e-6, units="m", scale=GV%m_to_H, do_not_log=.not.CS%reset_intxpa_integral) |
| 2273 | call get_param(param_file, mdl, "USE_INACCURATE_PGF_RHO_ANOM", CS%use_inaccurate_pgf_rho_anom, & | |
| 2274 | "If true, use a form of the PGF that uses the reference density "//& | |
| 2275 | 1 | "in an inaccurate way. This is not recommended.", default=.false.) |
| 2276 | call get_param(param_file, mdl, "RECONSTRUCT_FOR_PRESSURE", CS%reconstruct, & | |
| 2277 | "If True, use vertical reconstruction of T & S within "//& | |
| 2278 | "the integrals of the FV pressure gradient calculation. "//& | |
| 2279 | "If False, use the constant-by-layer algorithm. "//& | |
| 2280 | "The default is set by USE_REGRIDDING.", & | |
| 2281 | 1 | default=use_ALE ) |
| 2282 | call get_param(param_file, mdl, "PRESSURE_RECONSTRUCTION_SCHEME", CS%Recon_Scheme, & | |
| 2283 | "Order of vertical reconstruction of T/S to use in the "//& | |
| 2284 | "integrals within the FV pressure gradient calculation.\n"//& | |
| 2285 | " 0: PCM or no reconstruction.\n"//& | |
| 2286 | " 1: PLM reconstruction.\n"//& | |
| 2287 | " 2: PPM reconstruction.\n"//& | |
| 2288 | 1 | " 3: PLM with least squares slope.", default=1) |
| 2289 | call get_param(param_file, mdl, "BOUNDARY_EXTRAPOLATION_PRESSURE", CS%boundary_extrap, & | |
| 2290 | "If true, the reconstruction of T & S for pressure in "//& | |
| 2291 | "boundary cells is extrapolated, rather than using PCM "//& | |
| 2292 | "in these cells. If true, the same order polynomial is "//& | |
| 2293 | 1 | "used as is used for the interior cells.", default=.true.) |
| 2294 | call get_param(param_file, mdl, "USE_STANLEY_PGF", CS%use_stanley_pgf, & | |
| 2295 | "If true, turn on Stanley SGS T variance parameterization "// & | |
| 2296 | 1 | "in PGF code.", default=.false.) |
| 2297 | call get_param(param_file, mdl, "PGF_PLM_NKBLOCK", CS%nkblock, & | |
| 2298 | "Vertical block size for the PLM pressure gradient density integral. "//& | |
| 2299 | "0 processes all layers in a single block.", & | |
| 2300 | 1 | default=default_nkblock) |
| 2301 | call get_param(param_file, mdl, "PGF_PLM_NIBLOCK", CS%niblock_plm, & | |
| 2302 | "i-tile size for the PLM pressure gradient density integral. "//& | |
| 2303 | "0 uses the full i compute domain.", & | |
| 2304 | 1 | default=default_niblock_plm) |
| 2305 | call get_param(param_file, mdl, "PGF_PLM_NJBLOCK", CS%njblock_plm, & | |
| 2306 | "j-tile size for the PLM pressure gradient density integral. "//& | |
| 2307 | "0 uses the full j compute domain.", & | |
| 2308 | 1 | default=default_njblock_plm) |
| 2309 | 1 | if (CS%use_stanley_pgf) then |
| 2310 | call get_param(param_file, mdl, "STANLEY_COEFF", Stanley_coeff, & | |
| 2311 | "Coefficient correlating the temperature gradient and SGS T variance.", & | |
| 2312 | 0 | units="nondim", default=-1.0, do_not_log=.true.) |
| 2313 | 0 | if (Stanley_coeff < 0.0) call MOM_error(FATAL, & |
| 2314 | 0 | "STANLEY_COEFF must be set >= 0 if USE_STANLEY_PGF is true.") |
| 2315 | ||
| 2316 | CS%id_rho_pgf = register_diag_field('ocean_model', 'rho_pgf', diag%axesTL, & | |
| 2317 | 0 | Time, 'rho in PGF', 'kg m-3', conversion=US%R_to_kg_m3) |
| 2318 | CS%id_rho_stanley_pgf = register_diag_field('ocean_model', 'rho_stanley_pgf', diag%axesTL, & | |
| 2319 | 0 | Time, 'rho in PGF with Stanley correction', 'kg m-3', conversion=US%R_to_kg_m3) |
| 2320 | CS%id_p_stanley = register_diag_field('ocean_model', 'p_stanley', diag%axesTL, & | |
| 2321 | 0 | Time, 'p in PGF with Stanley correction', 'Pa', conversion=US%RL2_T2_to_Pa) |
| 2322 | endif | |
| 2323 | 1 | if (CS%calculate_SAL) then |
| 2324 | CS%id_e_sal = register_diag_field('ocean_model', 'e_sal', diag%axesT1, Time, & | |
| 2325 | 0 | 'Self-attraction and loading height anomaly', 'meter', conversion=US%Z_to_m) |
| 2326 | CS%id_sal_u = register_diag_field('ocean_model', 'SAL_u', diag%axesCuL, Time, & | |
| 2327 | 0 | 'Zonal Acceleration due to self-attraction and loading', 'm s-2', conversion=US%L_T2_to_m_s2) |
| 2328 | CS%id_sal_v = register_diag_field('ocean_model', 'SAL_v', diag%axesCvL, Time, & | |
| 2329 | 0 | 'Meridional Acceleration due to self-attraction and loading', 'm s-2', conversion=US%L_T2_to_m_s2) |
| 2330 | 0 | if (CS%id_sal_u > 0) & |
| 2331 | 0 | call safe_alloc_ptr(ADp%sal_u, IsdB, IedB, jsd, jed, nz) |
| 2332 | 0 | if (CS%id_sal_v > 0) & |
| 2333 | 0 | call safe_alloc_ptr(ADp%sal_v, isd, ied, JsdB, JedB, nz) |
| 2334 | endif | |
| 2335 | 1 | if (CS%tides) then |
| 2336 | CS%id_e_tide = register_diag_field('ocean_model', 'e_tidal', diag%axesT1, Time, & | |
| 2337 | 0 | 'Tidal Forcing Astronomical and SAL Height Anomaly', 'meter', conversion=US%Z_to_m) |
| 2338 | CS%id_e_tidal_eq = register_diag_field('ocean_model', 'e_tide_eq', diag%axesT1, Time, & | |
| 2339 | 0 | 'Equilibrium tides height anomaly', 'meter', conversion=US%Z_to_m) |
| 2340 | CS%id_e_tidal_sal = register_diag_field('ocean_model', 'e_tide_sal', diag%axesT1, Time, & | |
| 2341 | 0 | 'Read-in tidal self-attraction and loading height anomaly', 'meter', conversion=US%Z_to_m) |
| 2342 | CS%id_tides_u = register_diag_field('ocean_model', 'tides_u', diag%axesCuL, Time, & | |
| 2343 | 0 | 'Zonal Acceleration due to tidal forcing', 'm s-2', conversion=US%L_T2_to_m_s2) |
| 2344 | CS%id_tides_v = register_diag_field('ocean_model', 'tides_v', diag%axesCvL, Time, & | |
| 2345 | 0 | 'Meridional Acceleration due to tidal forcing', 'm s-2', conversion=US%L_T2_to_m_s2) |
| 2346 | 0 | if (CS%id_tides_u > 0) & |
| 2347 | 0 | call safe_alloc_ptr(ADp%tides_u, IsdB, IedB, jsd, jed, nz) |
| 2348 | 0 | if (CS%id_tides_v > 0) & |
| 2349 | 0 | call safe_alloc_ptr(ADp%tides_v, isd, ied, JsdB, JedB, nz) |
| 2350 | endif | |
| 2351 | ||
| 2352 | CS%id_MassWt_u = register_diag_field('ocean_model', 'MassWt_u', diag%axesCuL, Time, & | |
| 2353 | 1 | 'The fractional mass weighting at u-point PGF calculations', 'nondim') |
| 2354 | CS%id_MassWt_v = register_diag_field('ocean_model', 'MassWt_v', diag%axesCvL, Time, & | |
| 2355 | 1 | 'The fractional mass weighting at v-point PGF calculations', 'nondim') |
| 2356 | ||
| 2357 | 1 | CS%GFS_scale = 1.0 |
| 2358 | 1 | if (GV%g_prime(1) /= GV%g_Earth) CS%GFS_scale = GV%g_prime(1) / GV%g_Earth |
| 2359 | ||
| 2360 | !$omp target update to (CS) | |
| 2361 | ||
| 2362 | 1 | call log_param(param_file, mdl, "GFS / G_EARTH", CS%GFS_scale, units="nondim") |
| 2363 | ||
| 2364 | 1 | end subroutine PressureForce_FV_init |
| 2365 | ||
| 2366 | !> \namespace mom_pressureforce_fv | |
| 2367 | !! | |
| 2368 | !! Provides the Boussinesq and non-Boussinesq forms of horizontal accelerations | |
| 2369 | !! due to pressure gradients using a vertically integrated finite volume form, | |
| 2370 | !! as described by Adcroft et al., 2008. Integration in the vertical is made | |
| 2371 | !! either by quadrature or analytically. | |
| 2372 | !! | |
| 2373 | !! This form eliminates the thermobaric instabilities that had been a problem with | |
| 2374 | !! previous forms of the pressure gradient force calculation, as described by | |
| 2375 | !! Hallberg, 2005. | |
| 2376 | !! | |
| 2377 | !! Adcroft, A., R. Hallberg, and M. Harrison, 2008: A finite volume discretization | |
| 2378 | !! of the pressure gradient force using analytic integration. Ocean Modelling, 22, | |
| 2379 | !! 106-113. http://doi.org/10.1016/j.ocemod.2008.02.001 | |
| 2380 | !! | |
| 2381 | !! Hallberg, 2005: A thermobaric instability of Lagrangian vertical coordinate | |
| 2382 | !! ocean models. Ocean Modelling, 8, 279-300. | |
| 2383 | !! http://dx.doi.org/10.1016/j.ocemod.2004.01.001 | |
| 2384 | ||
| 2385 | 0 | end module MOM_PressureForce_FV |