← back to index

src/core/MOM_PressureForce_FV.F90

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)
6module MOM_PressureForce_FV
7
8use MOM_debugging, only : hchksum, uvchksum
9use MOM_diag_mediator, only : post_data, register_diag_field
10use MOM_diag_mediator, only : safe_alloc_ptr, diag_ctrl, time_type
11use MOM_error_handler, only : MOM_error, FATAL, WARNING, is_root_pe
12use MOM_file_parser, only : get_param, log_param, log_version, param_file_type
13use MOM_grid, only : ocean_grid_type
14use MOM_PressureForce_Mont, only : set_pbce_Bouss, set_pbce_nonBouss
15use MOM_self_attr_load, only : calc_SAL, SAL_CS
16use MOM_tidal_forcing, only : calc_tidal_forcing, tidal_forcing_CS
17use MOM_tidal_forcing, only : calc_tidal_forcing_legacy
18use MOM_unit_scaling, only : unit_scale_type
19use MOM_variables, only : thermo_var_ptrs, accel_diag_ptrs
20use MOM_verticalGrid, only : verticalGrid_type
21use MOM_EOS, only : calculate_density, calculate_spec_vol, EOS_domain
22use MOM_density_integrals, only : int_density_dz, int_specific_vol_dp
23use MOM_density_integrals, only : int_density_dz_generic_plm, int_density_dz_generic_ppm
24use MOM_density_integrals, only : int_spec_vol_dp_generic_plm
25use MOM_density_integrals, only : int_density_dz_generic_pcm, int_spec_vol_dp_generic_pcm
26use MOM_density_integrals, only : diagnose_mass_weight_Z, diagnose_mass_weight_p
27use MOM_ALE, only : TS_PLM_edge_values, TS_PPM_edge_values, TS_PLM_WLS_edge_values, ALE_CS
28
29implicit none ; private
30
31#include <MOM_memory.h>
32
33public PressureForce_FV_init
34public 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!> Finite volume pressure gradient control structure
42type, public :: PressureForce_FV_CS ; private
43 logical :: initialized = .false. !< True if this control structure has been initialized.
44 logical :: calculate_SAL = .false. !< If true, calculate self-attraction and loading.
45 logical :: sal_use_bpa = .false. !< If true, use bottom pressure anomaly instead of SSH
46 !! to calculate SAL.
47 logical :: tides = .false. !< If true, apply tidal momentum forcing.
48 real :: rho_ref !< The reference density that is subtracted off when calculating pressure
49 !! gradient forces [R ~> kg m-3].
50 logical :: rho_ref_bug !< If true, recover a bug that mixes GV%Rho0 and CS%rho_ref in Boussinesq mode.
51 real :: GFS_scale !< A scaling of the surface pressure gradients to
52 !! allow the use of a reduced gravity model [nondim].
53 type(time_type), pointer :: Time !< A pointer to the ocean model's clock.
54 type(diag_ctrl), pointer :: diag !< A structure that is used to regulate the
55 !! timing of diagnostic output.
56 integer :: MassWghtInterp !< A flag indicating whether and how to use mass weighting in T/S interpolation
57 logical :: correction_intxpa !< If true, apply a correction to the value of intxpa at a selected
58 !! interface under ice, using matching at the end values along with a
59 !! 5-point quadrature integral of the hydrostatic pressure or height
60 !! changes along that interface. The selected interface is either at the
61 !! ocean's surface or in the interior, depending on reset_intxpa_integral.
62 logical :: reset_intxpa_integral !< If true and the surface displacement between adjacent cells
63 !! exceeds the vertical grid spacing, reset intxpa at the interface below
64 !! a trusted interior cell. (This often applies in ice shelf cavities.)
65 logical :: MassWghtInterpVanOnly !< If true, don't do mass weighting of T/S interpolation unless vanished
66 logical :: reset_intxpa_flattest !< If true, use flattest interface rather than top for reset integral
67 !! in cases where no best nonvanished interface
68 real :: h_nonvanished !< A minimal layer thickness that indicates that a layer is thick enough
69 !! to usefully reestimate the pressure integral across the interface
70 !! below it [H ~> m or kg m-2]
71 logical :: use_inaccurate_pgf_rho_anom !< If true, uses the older and less accurate
72 !! method to calculate density anomalies, as used prior to
73 !! March 2018.
74 logical :: boundary_extrap !< Indicate whether high-order boundary
75 !! extrapolation should be used within boundary cells
76
77 logical :: reconstruct !< If true, polynomial profiles of T & S will be
78 !! reconstructed and used in the integrals for the
79 !! finite volume pressure gradient calculation.
80 !! The default depends on whether regridding is being used.
81
82 integer :: Recon_Scheme !< Order of the polynomial of the reconstruction of T & S
83 !! for the finite volume pressure gradient calculation.
84 !! By the default (1) is for a piecewise linear method
85
86 logical :: debug !< If true, write verbose checksums for debugging purposes.
87 logical :: use_SSH_in_Z0p !< If true, adjust the height at which the pressure used in the
88 !! equation of state is 0 to account for the displacement of the sea
89 !! surface including adjustments for atmospheric or sea-ice pressure.
90 logical :: use_stanley_pgf !< If true, turn on Stanley parameterization in the PGF
91 logical :: bq_sal_tides = .false. !< If true, use an alternative method for SAL and tides
92 !! in Boussinesq mode
93 integer :: tides_answer_date = 99991231 !< Recover old answers with tides
94 integer :: id_e_tide = -1 !< Diagnostic identifier
95 integer :: id_e_tidal_eq = -1 !< Diagnostic identifier
96 integer :: id_e_tidal_sal = -1 !< Diagnostic identifier
97 integer :: id_e_sal = -1 !< Diagnostic identifier
98 integer :: id_rho_pgf = -1 !< Diagnostic identifier
99 integer :: id_rho_stanley_pgf = -1 !< Diagnostic identifier
100 integer :: id_p_stanley = -1 !< Diagnostic identifier
101 integer :: id_MassWt_u = -1 !< Diagnostic identifier
102 integer :: id_MassWt_v = -1 !< Diagnostic identifier
103 integer :: id_sal_u = -1 !< Diagnostic identifier
104 integer :: id_sal_v = -1 !< Diagnostic identifier
105 integer :: id_tides_u = -1 !< Diagnostic identifier
106 integer :: id_tides_v = -1 !< Diagnostic identifier
107 type(SAL_CS), pointer :: SAL_CSp => NULL() !< SAL control structure
108 type(tidal_forcing_CS), pointer :: tides_CSp => NULL() !< Tides control structure
109end type PressureForce_FV_CS
110
111contains
112
113!> \brief Non-Boussinesq analytically-integrated finite volume form of pressure gradient
114!!
115!! Determines the acceleration due to hydrostatic pressure forces, using
116!! the analytic finite volume form of the Pressure gradient, and does not
117!! make the Boussinesq approximation.
118!!
119!! To work, the following fields must be set outside of the usual (is:ie,js:je)
120!! range before this subroutine is called:
121!! h(isB:ie+1,jsB:je+1), T(isB:ie+1,jsB:je+1), and S(isB:ie+1,jsB:je+1).
1220subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, ADp, p_atm, pbce, eta)
123 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
124 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
125 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
126 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> kg m-2]
127 type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamic variables
128 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(out) :: PFu !< Zonal acceleration [L T-2 ~> m s-2]
129 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(out) :: PFv !< Meridional acceleration [L T-2 ~> m s-2]
130 type(PressureForce_FV_CS), intent(in) :: CS !< Finite volume PGF control structure
131 type(ALE_CS), pointer :: ALE_CSp !< ALE control structure
132 type(accel_diag_ptrs), pointer :: ADp !< Acceleration diagnostic pointers
133 real, dimension(:,:), pointer :: p_atm !< The pressure at the ice-ocean
134 !! or atmosphere-ocean interface [R L2 T-2 ~> Pa].
135 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), optional, intent(out) :: pbce !< The baroclinic pressure
136 !! anomaly in each layer due to eta anomalies
137 !! [L2 T-2 H-1 ~> m4 s-2 kg-1].
138 real, dimension(SZI_(G),SZJ_(G)), optional, intent(out) :: eta !< The total column mass used to
139 !! calculate PFu and PFv [H ~> kg m-2].
140 ! Local variables
1410 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: p ! Interface pressure [R L2 T-2 ~> Pa].
142 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), target :: &
1430 T_tmp, & ! Temporary array of temperatures where layers that are lighter
144 ! than the mixed layer have the mixed layer's properties [C ~> degC].
1450 S_tmp ! Temporary array of salinities where layers that are lighter
146 ! than the mixed layer have the mixed layer's properties [S ~> ppt].
147 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: &
1480 S_t, S_b, & ! Top and bottom edge values for linear reconstructions
149 ! of salinity within each layer [S ~> ppt].
1500 T_t, T_b ! Top and bottom edge values for linear reconstructions
151 ! of temperature within each layer [C ~> degC].
152 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: &
1530 dza, & ! The change in geopotential anomaly between the top and bottom
154 ! of a layer [L2 T-2 ~> m2 s-2].
1550 intp_dza ! The vertical integral in depth of the pressure anomaly less
156 ! the pressure anomaly at the top of the layer [R L4 T-4 ~> Pa m2 s-2].
157 real, dimension(SZI_(G),SZJ_(G)) :: &
1580 dp, & ! The (positive) change in pressure across a layer [R L2 T-2 ~> Pa].
1590 SSH, & ! Sea surfae height anomaly for self-attraction and loading. Used if
160 ! CALCULATE_SAL is True and SAL_USE_BPA is False [Z ~> m].
1610 pbot, & ! Total bottom pressure for self-attraction and loading. Used if
162 ! CALCULATE_SAL is True and SAL_USE_BPA is True [R L2 T-2 ~> Pa].
1630 e_sal, & ! The bottom geopotential anomaly due to self-attraction and loading [Z ~> m].
1640 e_tidal_eq, & ! The bottom geopotential anomaly due to tidal forces from astronomical sources [Z ~> m].
1650 e_tidal_sal, & ! The bottom geopotential anomaly due to harmonic self-attraction and loading
166 ! specific to tides [Z ~> m].
1670 e_sal_and_tide, & ! The summation of self-attraction and loading and tidal forcing, used for recovering
168 ! old answers only [Z ~> m].
1690 dM ! The barotropic adjustment to the Montgomery potential to
170 ! account for a reduced gravity model [L2 T-2 ~> m2 s-2].
171 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: &
1720 za ! The geopotential anomaly (i.e. g*e + alpha_0*pressure) at the
173 ! interfaces [L2 T-2 ~> m2 s-2].
174
1750 real, dimension(SZI_(G)) :: Rho_cv_BL ! The coordinate potential density in the deepest variable
176 ! density near-surface layer [R ~> kg m-3].
177 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1) :: &
1780 intx_za ! The zonal integral of the geopotential anomaly along the
179 ! interfaces, divided by the grid spacing [L2 T-2 ~> m2 s-2].
180 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: &
1810 intx_dza ! The change in intx_za through a layer [L2 T-2 ~> m2 s-2].
182 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1) :: &
1830 inty_za ! The meridional integral of the geopotential anomaly along the
184 ! interfaces, divided by the grid spacing [L2 T-2 ~> m2 s-2].
185 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: &
1860 inty_dza ! The change in inty_za through a layer [L2 T-2 ~> m2 s-2].
187 real, dimension(SZI_(G),SZJ_(G)) :: &
1880 T_top, & ! Temperature of top layer used with correction_intxpa [C ~> degC]
1890 S_top ! Salinity of top layer used with correction_intxpa [S ~> ppt]
190 real, dimension(SZIB_(G),SZJ_(G)) :: &
1910 intx_za_cor ! Correction for curvature in intx_za [L2 T-2 ~> m2 s-2]
192 real, dimension(SZI_(G),SZJB_(G)) :: &
1930 inty_za_cor ! Correction for curvature in inty_za [L2 T-2 ~> m2 s-2]
194
195 ! These variables are used with reset_intxpa_integral. The values are taken from different
196 ! interfaces as a function of position.
197 real, dimension(SZIB_(G),SZJ_(G)) :: &
1980 T_int_W, T_int_E, & ! Temperatures on the reference interface to the east and west of a u-point [C ~> degC]
1990 S_int_W, S_int_E, & ! Salinities on the reference interface to the east and west of a u-point [S ~> ppt]
2000 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]
2010 intx_za_nonlin, & ! Deviations in the previous version of intx_pa for the reference interface
202 ! from the value that would be obtained from assuming that pressure varies
203 ! linearly with depth along that interface [R L2 T-2 ~> Pa].
2040 dp_int_x, & ! The change in x in pressure along the reference interface [R L2 T-2 ~> Pa]
2050 intx_za_cor_ri ! The correction to intx_za based on the reference interface calculations [L2 T-2 ~> m2 s-2]
206 real, dimension(SZI_(G),SZJB_(G)) :: &
2070 T_int_S, T_int_N, & ! Temperatures on the reference interface to the north and south of a v-point [C ~> degC]
2080 S_int_S, S_int_N, & ! Salinities on the reference interface to the north and south of a v-point [S ~> ppt]
2090 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]
2100 inty_za_nonlin, & ! Deviations in the previous version of intx_pa for the reference interface
211 ! from the value that would be obtained from assuming that pressure varies
212 ! linearly with depth along that interface [L2 T-2 ~> m2 s-2].
2130 dp_int_y, & ! The change in y in geopotenial height along the reference interface [R L2 T-2 ~> Pa]
2140 inty_za_cor_ri ! The correction to inty_za based on the reference interface calculations [L2 T-2 ~> m2 s-2]
215 logical, dimension(SZIB_(G),SZJ_(G)) :: &
2160 seek_x_cor ! If true, try to find a u-point interface that would provide a better estimate
217 ! of the curvature terms in the intx_pa.
218 logical, dimension(SZI_(G),SZJB_(G)) :: &
2190 seek_y_cor ! If true, try to find a v-point interface that would provide a better estimate
220 ! of the curvature terms in the inty_pa.
221 real, dimension(SZIB_(G),SZJ_(G)) :: &
2220 delta_p_x ! If using flattest interface for reset integral, store x interface
223 ! differences [R L2 T-2 ~> Pa]
224 real, dimension(SZI_(G),SZJB_(G)) :: &
2250 delta_p_y ! If using flattest interface for reset integral, store y interface
226 ! differences [R L2 T-2 ~> Pa]
227 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: &
2280 MassWt_u ! The fractional mass weighting at a u-point [nondim].
229 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: &
2300 MassWt_v ! The fractional mass weighting at a v-point [nondim].
2310 real :: p_ref(SZI_(G)) ! The pressure used to calculate the coordinate
232 ! density, [R L2 T-2 ~> Pa] (usually 2e7 Pa = 2000 dbar).
233 real :: dp_sfc ! The change in surface pressure between adjacent cells [R L2 T-2 ~> Pa]
234
235 real :: dp_neglect ! A thickness that is so small it is usually lost
236 ! in roundoff and can be neglected [R L2 T-2 ~> Pa].
237 real :: p_nonvanished ! nonvanshed pressure [R L2 T-2 ~> Pa]
238 real :: I_gEarth ! The inverse of GV%g_Earth [T2 Z L-2 ~> s2 m-1]
239 real :: alpha_anom ! The in-situ specific volume, averaged over a
240 ! layer, less alpha_ref [R-1 ~> m3 kg-1].
241 logical :: use_p_atm ! If true, use the atmospheric pressure.
242 logical :: use_ALE ! If true, use an ALE pressure reconstruction.
243 logical :: use_EOS ! If true, density is calculated from T & S using an equation of state.
244 logical :: do_more_k ! If true, there are still points where a flatter interface remains to be found.
2450 type(thermo_var_ptrs) :: tv_tmp! A structure of temporary T & S.
246
247 real :: alpha_ref ! A reference specific volume [R-1 ~> m3 kg-1] that is used
248 ! to reduce the impact of truncation errors.
2490 real :: rho_in_situ(SZI_(G)) ! The in situ density [R ~> kg m-3].
250 real :: Pa_to_H ! A factor to convert from Pa to the thickness units (H)
251 ! [H T2 R-1 L-2 ~> m Pa-1 or kg m-2 Pa-1].
252 real :: H_to_RL2_T2 ! A factor to convert from thickness units (H) to pressure
253 ! units [R L2 T-2 H-1 ~> Pa m-1 or Pa m2 kg-1].
254 real :: T5(5) ! Temperatures and salinities at five quadrature points [C ~> degC]
255 real :: S5(5) ! Salinities at five quadrature points [S ~> ppt]
256 real :: p5(5) ! Pressures at five quadrature points for use with the equation of state [R L2 T-2 ~> Pa]
257 real :: SpV5(5) ! Specific volume anomalies at five quadrature points [R-1 ~> m3 kg-1]
258 real :: wt_R ! A weighting factor [nondim]
259
260 ! real :: oneatm ! 1 standard atmosphere of pressure in [R L2 T-2 ~> Pa]
261 real, parameter :: C1_6 = 1.0/6.0 ! [nondim]
262 real, parameter :: C1_90 = 1.0/90.0 ! A rational constant [nondim]
263 integer :: is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, nkmb
264 integer, dimension(2) :: EOSdom ! The i-computational domain for the equation of state
265 integer, dimension(2) :: EOSdom_u ! The i-computational domain for the equation of state at u-velocity points
266 integer, dimension(2) :: EOSdom_v ! The i-computational domain for the equation of state at v-velocity points
267 integer :: i, j, k, m
268
2690 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
2700 nkmb=GV%nk_rho_varies
2710 Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB
2720 EOSdom(1) = Isq - (G%isd-1) ; EOSdom(2) = G%iec+1 - (G%isd-1)
2730 EOSdom_u(1) = Isq - (G%IsdB-1) ; EOSdom_u(2) = Ieq - (G%IsdB-1)
2740 EOSdom_v(1) = is - (G%isd-1) ; EOSdom_v(2) = ie - (G%isd-1)
275
2760 if (.not.CS%initialized) call MOM_error(FATAL, &
2770 "MOM_PressureForce_FV_nonBouss: Module must be initialized before it is used.")
278
2790 if (CS%use_stanley_pgf) call MOM_error(FATAL, &
280 "MOM_PressureForce_FV_nonBouss: The Stanley parameterization is not yet "//&
2810 "implemented in non-Boussinesq mode.")
282
2830 use_p_atm = associated(p_atm)
2840 use_EOS = associated(tv%eqn_of_state)
2850 use_ALE = .false.
2860 if (associated(ALE_CSp)) use_ALE = CS%reconstruct .and. use_EOS
287
2880 H_to_RL2_T2 = GV%g_Earth*GV%H_to_RZ
2890 dp_neglect = GV%g_Earth*GV%H_to_RZ * GV%H_subroundoff
2900 alpha_ref = 1.0 / CS%rho_ref
2910 I_gEarth = 1.0 / GV%g_Earth
2920 p_nonvanished = GV%g_Earth*GV%H_to_RZ*CS%h_nonvanished
293
2940 if ((CS%id_MassWt_u > 0) .or. (CS%id_MassWt_v > 0)) then
2950 MassWt_u(:,:,:) = 0.0 ; MassWt_v(:,:,:) = 0.0
296 endif
297
2980 if (use_p_atm) then
299 !$OMP parallel do default(shared)
3000 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
3010 p(i,j,1) = p_atm(i,j)
302 enddo ; enddo
303 else
304 ! oneatm = 101325.0 * US%Pa_to_RL2_T2 ! 1 atm scaled to [R L2 T-2 ~> Pa]
305 !$OMP parallel do default(shared)
3060 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
3070 p(i,j,1) = 0.0 ! or oneatm
308 enddo ; enddo
309 endif
310 !$OMP parallel do default(shared)
3110 do j=Jsq,Jeq+1 ; do k=2,nz+1 ; do i=Isq,Ieq+1
3120 p(i,j,K) = p(i,j,K-1) + H_to_RL2_T2 * h(i,j,k-1)
313 enddo ; enddo ; enddo
314
3150 if (use_EOS) then
316 ! With a bulk mixed layer, replace the T & S of any layers that are
317 ! lighter than the buffer layer with the properties of the buffer
318 ! layer. These layers will be massless anyway, and it avoids any
319 ! formal calculations with hydrostatically unstable profiles.
3200 if (nkmb>0) then
3210 tv_tmp%T => T_tmp ; tv_tmp%S => S_tmp
3220 tv_tmp%eqn_of_state => tv%eqn_of_state
3230 do i=Isq,Ieq+1 ; p_ref(i) = tv%P_Ref ; enddo
324 !$OMP parallel do default(shared) private(Rho_cv_BL)
3250 do j=Jsq,Jeq+1
3260 do k=1,nkmb ; do i=Isq,Ieq+1
3270 tv_tmp%T(i,j,k) = tv%T(i,j,k) ; tv_tmp%S(i,j,k) = tv%S(i,j,k)
328 enddo ; enddo
329 call calculate_density(tv%T(:,j,nkmb), tv%S(:,j,nkmb), p_ref, Rho_cv_BL(:), &
3300 tv%eqn_of_state, EOSdom)
3310 do k=nkmb+1,nz ; do i=Isq,Ieq+1
3320 if (GV%Rlay(k) < Rho_cv_BL(i)) then
3330 tv_tmp%T(i,j,k) = tv%T(i,j,nkmb) ; tv_tmp%S(i,j,k) = tv%S(i,j,nkmb)
334 else
3350 tv_tmp%T(i,j,k) = tv%T(i,j,k) ; tv_tmp%S(i,j,k) = tv%S(i,j,k)
336 endif
337 enddo ; enddo
338 enddo
339 else
3400 tv_tmp%T => tv%T ; tv_tmp%S => tv%S
3410 tv_tmp%eqn_of_state => tv%eqn_of_state
342 endif
343 endif
344
345 ! If regridding is activated, do a linear reconstruction of salinity
346 ! and temperature across each layer. The subscripts 't' and 'b' refer
347 ! to top and bottom values within each layer (these are the only degrees
348 ! of freedom needed to know the linear profile).
3490 if ( use_ALE .and. (CS%Recon_Scheme == 1) ) then
3500 call TS_PLM_edge_values(ALE_CSp, S_t, S_b, T_t, T_b, G, GV, tv, h, CS%boundary_extrap)
3510 elseif ( use_ALE .and. (CS%Recon_Scheme == 2) ) then
3520 call TS_PPM_edge_values(ALE_CSp, S_t, S_b, T_t, T_b, G, GV, tv, h, CS%boundary_extrap)
3530 elseif ( use_ALE .and. (CS%Recon_Scheme == 3) ) then
3540 call TS_PLM_WLS_edge_values(ALE_CSp, S_t, S_b, T_t, T_b, G, GV, tv, h)
3550 elseif (CS%reset_intxpa_integral) then
3560 do k=1,nz ; do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
3570 T_b(i,j,k) = tv%T(i,j,k) ; S_b(i,j,k) = tv%S(i,j,k)
358 enddo ; enddo ; enddo
359 endif
360
361 !$OMP parallel do default(shared) private(alpha_anom,dp)
3620 do k=1,nz
363 ! Calculate 4 integrals through the layer that are required in the
364 ! subsequent calculation.
3650 if (use_EOS) then
3660 if ( use_ALE .and. CS%Recon_Scheme > 0 ) then
3670 if ( CS%Recon_Scheme == 1 .or. CS%Recon_Scheme == 3 ) then
368 call int_spec_vol_dp_generic_plm( T_t(:,:,k), T_b(:,:,k), S_t(:,:,k), S_b(:,:,k), &
369 p(:,:,K), p(:,:,K+1), alpha_ref, dp_neglect, p(:,:,nz+1), G%HI, &
370 tv%eqn_of_state, US, dza(:,:,k), intp_dza(:,:,k), intx_dza(:,:,k), inty_dza(:,:,k), &
371 P_surf=p(:,:,1), MassWghtInterp=CS%MassWghtInterp, &
3720 MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, p_nv=p_nonvanished)
3730 elseif ( CS%Recon_Scheme == 2 ) then
374 call MOM_error(FATAL, "PressureForce_FV_nonBouss: "//&
3750 "int_spec_vol_dp_generic_ppm does not exist yet.")
376 ! call int_spec_vol_dp_generic_ppm ( tv%T(:,:,k), T_t(:,:,k), T_b(:,:,k), &
377 ! tv%S(:,:,k), S_t(:,:,k), S_b(:,:,k), p(:,:,K), p(:,:,K+1), &
378 ! alpha_ref, G%HI, tv%eqn_of_state, dza(:,:,k), intp_dza(:,:,k), &
379 ! intx_dza(:,:,k), inty_dza(:,:,k), P_surf=p(:,:,1), MassWghtInterp=CS%MassWghtInterp)
380 endif
381 else
382 call int_specific_vol_dp(tv_tmp%T(:,:,k), tv_tmp%S(:,:,k), p(:,:,K), &
383 p(:,:,K+1), alpha_ref, G%HI, tv%eqn_of_state, &
384 US, dza(:,:,k), intp_dza(:,:,k), intx_dza(:,:,k), &
385 inty_dza(:,:,k), bathyP=p(:,:,nz+1), P_surf=p(:,:,1), dP_tiny=dp_neglect, &
386 MassWghtInterp=CS%MassWghtInterp, &
3870 MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, p_nv=p_nonvanished)
388 endif
3890 if ((CS%id_MassWt_u > 0) .or. (CS%id_MassWt_v > 0)) &
390 call diagnose_mass_weight_p(p(:,:,K), p(:,:,K+1), p(:,:,nz+1), p(:,:,1), dp_neglect, CS%MassWghtInterp, &
391 G%HI, MassWt_u(:,:,k), MassWt_v(:,:,k), &
3920 MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, p_nv=p_nonvanished)
393 else
3940 alpha_anom = 1.0 / GV%Rlay(k) - alpha_ref
3950 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
3960 dp(i,j) = H_to_RL2_T2 * h(i,j,k)
3970 dza(i,j,k) = alpha_anom * dp(i,j)
3980 intp_dza(i,j,k) = 0.5 * alpha_anom * dp(i,j)**2
399 enddo ; enddo
4000 do j=js,je ; do I=Isq,Ieq
4010 intx_dza(i,j,k) = 0.5 * alpha_anom * (dp(i,j)+dp(i+1,j))
402 enddo ; enddo
4030 do J=Jsq,Jeq ; do i=is,ie
4040 inty_dza(i,j,k) = 0.5 * alpha_anom * (dp(i,j)+dp(i,j+1))
405 enddo ; enddo
406 endif
407 enddo
408
409 ! The bottom geopotential anomaly is calculated first so that the increments
410 ! to the geopotential anomalies can be reused. Alternately, the surface
411 ! geopotential could be calculated directly with separate calls to
412 ! int_specific_vol_dp with alpha_ref=0, and the anomalies used going
413 ! downward, which would relieve the need for dza, intp_dza, intx_dza, and
414 ! inty_dza to be 3-D arrays.
415
416 ! Sum vertically to determine the surface geopotential anomaly.
417 !$OMP parallel do default(shared)
4180 do j=Jsq,Jeq+1
4190 do i=Isq,Ieq+1
4200 za(i,j,nz+1) = alpha_ref*p(i,j,nz+1) - GV%g_Earth*G%bathyT(i,j)
421 enddo
4220 do k=nz,1,-1 ; do i=Isq,Ieq+1
4230 za(i,j,K) = za(i,j,K+1) + dza(i,j,k)
424 enddo ; enddo
425 enddo
426
427 ! Calculate and add self-attraction and loading (SAL) geopotential height anomaly to interface height.
4280 if (CS%calculate_SAL) then
4290 if (CS%sal_use_bpa) then
430 !$OMP parallel do default(shared)
4310 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
4320 pbot(i,j) = p(i,j,nz+1)
433 enddo ; enddo
4340 call calc_SAL(pbot, e_sal, G, CS%SAL_CSp, tmp_scale=US%Z_to_m)
435 else
436 !$OMP parallel do default(shared)
4370 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
4380 SSH(i,j) = (za(i,j,1) - alpha_ref*p(i,j,1)) * I_gEarth - G%Z_ref
439 ! Remove above sea level topography at floodable cells
4400 SSH(i,j) = SSH(i,j) - max(-G%bathyT(i,j)-G%meanSL(i,j), 0.0)
441 enddo ; enddo
4420 call calc_SAL(SSH, e_sal, G, CS%SAL_CSp, tmp_scale=US%Z_to_m)
443 endif
444
445 ! This gives new answers after the change of separating SAL from tidal forcing module.
4460 if ((CS%tides_answer_date>20230630) .or. (.not.GV%semi_Boussinesq) .or. (.not.CS%tides)) then
447 !$OMP parallel do default(shared)
4480 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
4490 za(i,j,1) = za(i,j,1) - GV%g_Earth * e_sal(i,j)
450 enddo ; enddo
451 endif
452 endif
453
454 ! Calculate and add tidal geopotential height anomaly to interface height.
4550 if (CS%tides) then
4560 if ((CS%tides_answer_date>20230630) .or. (.not.GV%semi_Boussinesq)) then
4570 call calc_tidal_forcing(CS%Time, e_tidal_eq, e_tidal_sal, G, US, CS%tides_CSp)
458 !$OMP parallel do default(shared)
4590 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
4600 za(i,j,1) = za(i,j,1) - GV%g_Earth * (e_tidal_eq(i,j) + e_tidal_sal(i,j))
461 enddo ; enddo
462 else ! This block recreates older answers with tides.
4630 if (.not.CS%calculate_SAL) e_sal(:,:) = 0.0
464 call calc_tidal_forcing_legacy(CS%Time, e_sal, e_sal_and_tide, e_tidal_eq, e_tidal_sal, &
4650 G, US, CS%tides_CSp)
466 !$OMP parallel do default(shared)
4670 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
4680 za(i,j,1) = za(i,j,1) - GV%g_Earth * e_sal_and_tide(i,j)
469 enddo ; enddo
470 endif
471 endif
472
473 ! Find the height anomalies at the interfaces. If there are no tides and no SAL,
474 ! there is no need to correct za, but omitting this changes answers at roundoff.
4750 do k=1,nz
476 !$OMP parallel do default(shared)
4770 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
4780 za(i,j,K+1) = za(i,j,K) - dza(i,j,k)
479 enddo ; enddo
480 enddo
481
4820 if (CS%debug) then
4830 call hchksum(za, "Pre-correction za", G%HI, haloshift=1, unscale=US%L_T_to_m_s**2)
4840 call hchksum(p, "Pre-correction p", G%HI, haloshift=1, unscale=US%RL2_T2_to_Pa)
485 endif
486
487 ! With an ice-shelf or icebergs, this linearity condition might need to be applied
488 ! to a sub-surface interface.
4890 if (CS%correction_intxpa .or. CS%reset_intxpa_integral) then
490 ! Determine surface temperature and salinity for use in the pressure gradient corrections
4910 if (use_ALE .and. (CS%Recon_Scheme > 0)) then
4920 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
4930 T_top(i,j) = T_t(i,j,1) ; S_top(i,j) = S_t(i,j,1)
494 enddo ; enddo
495 else
4960 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
4970 T_top(i,j) = tv%T(i,j,1) ; S_top(i,j) = tv%S(i,j,1)
498 enddo ; enddo
499 endif
500 endif
501
5020 if (CS%correction_intxpa) then
503 ! This version makes a 5 point quadrature correction for hydrostatic variations in surface
504 ! pressure under ice.
505 !$OMP parallel do default(shared) private(dp_sfc,T5,S5,p5,wt_R,SpV5)
5060 do j=js,je ; do I=Isq,Ieq
5070 intx_za_cor(I,j) = 0.0
5080 dp_sfc = (p(i+1,j,1) - p(i,j,1))
509 ! If the changes in pressure and height anomaly were explicable by just a hydrostatic balance,
510 ! the implied specific volume would be SpV_implied = alpha_ref - (dza_x / dp_x)
5110 if (dp_sfc * (alpha_ref*dp_sfc - (za(i+1,j,1)-za(i,j,1))) > 0.0) then
5120 T5(1) = T_top(i,j) ; T5(5) = T_top(i+1,j)
5130 S5(1) = S_top(i,j) ; S5(5) = S_top(i+1,j)
5140 p5(1) = p(i,j,1) ; p5(5) = p(i+1,j,1)
5150 do m=2,4
5160 wt_R = 0.25*real(m-1)
5170 T5(m) = T5(1) + (T5(5)-T5(1))*wt_R
5180 S5(m) = S5(1) + (S5(5)-S5(1))*wt_R
5190 p5(m) = p5(1) + (p5(5)-p5(1))*wt_R
520 enddo !m
5210 call calculate_spec_vol(T5, S5, p5, SpV5, tv%eqn_of_state, spv_ref=alpha_ref)
522 ! See the Boussinesq calculation of inty_pa_cor for the derivation of the following expression.
5230 intx_za_cor(I,j) = C1_90 * (4.75*(SpV5(5)-SpV5(1)) + 5.5*(SpV5(4)-SpV5(2))) * dp_sfc
524 ! Note the consistency with the linear form below because (4.75 + 5.5/2) / 90 = 1/12
525 endif
5260 intx_za(I,j,1) = 0.5*(za(i,j,1) + za(i+1,j,1)) + intx_za_cor(I,j)
527 enddo ; enddo
528 !$OMP parallel do default(shared) private(dp_sfc,T5,S5,p5,wt_R,SpV5)
5290 do J=Jsq,Jeq ; do i=is,ie
5300 inty_za_cor(i,J) = 0.0
5310 dp_sfc = (p(i,j+1,1) - p(i,j,1))
5320 if (dp_sfc * (alpha_ref*dp_sfc - (za(i,j+1,1)-za(i,j,1))) > 0.0) then
533 ! The pressure/depth relationship has a positive implied specific volume.
5340 T5(1) = T_top(i,j) ; T5(5) = T_top(i,j+1)
5350 S5(1) = S_top(i,j) ; S5(5) = S_top(i,j+1)
5360 p5(1) = p(i,j,1) ; p5(5) = p(i,j+1,1)
5370 do m=2,4
5380 wt_R = 0.25*real(m-1)
5390 T5(m) = T5(1) + (T5(5)-T5(1))*wt_R
5400 S5(m) = S5(1) + (S5(5)-S5(1))*wt_R
5410 p5(m) = p5(1) + (p5(5)-p5(1))*wt_R
542 enddo !m
5430 call calculate_spec_vol(T5, S5, p5, SpV5, tv%eqn_of_state, spv_ref=alpha_ref)
544 ! See the Boussinesq calculation of inty_pa_cor for the derivation of the following expression.
5450 inty_za_cor(i,J) = C1_90 * (4.75*(SpV5(5)-SpV5(1)) + 5.5*(SpV5(4)-SpV5(2))) * dp_sfc
546 endif
5470 inty_za(i,J,1) = 0.5*(za(i,j,1) + za(i,j+1,1)) + inty_za_cor(i,J)
548 enddo ; enddo
549 else
550 ! This order of integrating upward and then downward again is necessary with
551 ! a nonlinear equation of state, so that the surface geopotentials will go
552 ! linearly between the values at thickness points, but the bottom geopotentials
553 ! will not now be linear at the sub-grid-scale. Doing this ensures no motion
554 ! with flat isopycnals, even with a nonlinear equation of state.
555 !$OMP parallel do default(shared)
5560 do j=js,je ; do I=Isq,Ieq
5570 intx_za(I,j,1) = 0.5*(za(i,j,1) + za(i+1,j,1))
558 enddo ; enddo
559 !$OMP parallel do default(shared)
5600 do J=Jsq,Jeq ; do i=is,ie
5610 inty_za(i,J,1) = 0.5*(za(i,j,1) + za(i,j+1,1))
562 enddo ; enddo
563 endif
564
5650 do k=1,nz
566 !$OMP parallel do default(shared)
5670 do j=js,je ; do I=Isq,Ieq
5680 intx_za(I,j,K+1) = intx_za(I,j,K) - intx_dza(I,j,k)
569 enddo ; enddo
570 enddo
5710 do k=1,nz
572 !$OMP parallel do default(shared)
5730 do J=Jsq,Jeq ; do i=is,ie
5740 inty_za(i,J,K+1) = inty_za(i,J,K) - inty_dza(i,J,k)
575 enddo ; enddo
576 enddo
577
5780 if (CS%debug) then
579 call uvchksum("Prelim int[xy]_za", intx_za, inty_za, G%HI, haloshift=0, &
5800 symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%L_T_to_m_s**2)
581 call uvchksum("Prelim int[xy]_dza", intx_dza, inty_dza, G%HI, haloshift=0, &
5820 symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%L_T_to_m_s**2)
583 endif
584
5850 if (CS%reset_intxpa_integral) then
586 ! Having stored the pressure gradient info, we can work out where the first nonvanished layers is
587 ! reset intx_za there, then adjust intx_za throughout the water column.
588
589 ! Zero out the 2-d arrays that will be set from various reference interfaces.
5900 T_int_W(:,:) = 0.0 ; S_int_W(:,:) = 0.0 ; p_int_W(:,:) = 0.0
5910 T_int_E(:,:) = 0.0 ; S_int_E(:,:) = 0.0 ; p_int_E(:,:) = 0.0
5920 intx_za_nonlin(:,:) = 0.0 ; intx_za_cor_ri(:,:) = 0.0 ; dp_int_x(:,:) = 0.0
5930 do j=js,je ; do I=Isq,Ieq
5940 seek_x_cor(I,j) = (G%mask2dCu(I,j) > 0.)
5950 delta_p_x(I,j) = 0.0
596 enddo ; enddo
597
5980 do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then
5990 if ((p(i+1,j,2) >= p(i,j,1)) .and. (p(i,j,2) >= p(i+1,j,1))) then
600 ! This is the typical case in the open ocean, so use the topmost interface.
6010 T_int_W(I,j) = T_top(i,j) ; T_int_E(I,j) = T_top(i+1,j)
6020 S_int_W(I,j) = S_top(i,j) ; S_int_E(I,j) = S_top(i+1,j)
6030 p_int_W(I,j) = p(i,j,1) ; p_int_E(I,j) = p(i+1,j,1)
6040 intx_za_nonlin(I,j) = intx_za(I,j,1) - 0.5*(za(i,j,1) + za(i+1,j,1))
6050 dp_int_x(I,j) = p(i+1,j,1)-p(i,j,1)
6060 seek_x_cor(I,j) = .false.
607 endif
608 endif ; enddo ; enddo
609
6100 do k=1,nz
6110 do_more_k = .false.
6120 do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then
613 ! Find the topmost layer for which both sides are nonvanished and mass-weighting is not
614 ! activated in the subgrid interpolation.
6150 if (((h(i,j,k) > CS%h_nonvanished) .and. (h(i+1,j,k) > CS%h_nonvanished)) .and. &
616 (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
617 ! Store properties at the bottom of this cell to get a "good estimate" for intxpa at
618 ! the interface below this cell (it might have quadratic pressure dependence if sloped)
6190 T_int_W(I,j) = T_b(i,j,k) ; T_int_E(I,j) = T_b(i+1,j,k)
6200 S_int_W(I,j) = S_b(i,j,k) ; S_int_E(I,j) = S_b(i+1,j,k)
6210 p_int_W(I,j) = p(i,j,K+1) ; p_int_E(I,j) = p(i+1,j,K+1)
622
6230 intx_za_nonlin(I,j) = intx_za(I,j,K+1) - 0.5*(za(i,j,K+1) + za(i+1,j,K+1))
6240 dp_int_x(I,j) = p(i+1,j,K+1)-p(i,j,K+1)
6250 seek_x_cor(I,j) = .false.
626 else
6270 do_more_k = .true.
628 endif
629 endif ; enddo ; enddo
6300 if (.not.do_more_k) exit ! All reference interfaces have been found, so stop working downward.
631 enddo
632
6330 if (do_more_k) then
6340 if (CS%reset_intxpa_flattest) then
635 ! There are still points where a correction is needed, so use flattest interface
6360 do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then
637 ! choose top layer first
6380 T_int_W(I,j) = T_top(i,j) ; T_int_E(I,j) = T_top(i+1,j)
6390 S_int_W(I,j) = S_top(i,j) ; S_int_E(I,j) = S_top(i+1,j)
6400 p_int_W(I,j) = p(i,j,1) ; p_int_E(I,j) = p(i+1,j,1)
6410 intx_za_nonlin(I,j) = intx_za(I,j,1) - 0.5*(za(i,j,1) + za(i+1,j,1))
6420 dp_int_x(I,j) = p(i+1,j,1)-p(i,j,1)
6430 delta_p_x(I,j) = abs(p(i+1,j,1)-p(i,j,1))
6440 do k=1,nz
6450 if (abs(p(i+1,j,k+1)-p(i,j,k+1)) < delta_p_x(I,j)) then
646 ! bottom of layer is less sloped than top. Use this layer
6470 delta_p_x(I,j) = abs(p(i+1,j,k+1)-p(i,j,k+1))
6480 T_int_W(I,j) = T_b(i,j,k) ; T_int_E(I,j) = T_b(i+1,j,k)
6490 S_int_W(I,j) = S_b(i,j,k) ; S_int_E(I,j) = S_b(i+1,j,k)
6500 p_int_W(I,j) = p(i,j,K+1) ; p_int_E(I,j) = p(i+1,j,K+1)
6510 intx_za_nonlin(I,j) = intx_za(I,j,K+1) - 0.5*(za(i,j,K+1) + za(i+1,j,K+1))
6520 dp_int_x(I,j) = p(i+1,j,K+1)-p(i,j,K+1)
653 endif
654 enddo
6550 seek_x_cor(I,j) = .false.
656 endif ; enddo ; enddo
657 else
658 ! There are still points where a correction is needed, so use the top interface.
6590 do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then
6600 T_int_W(I,j) = T_top(i,j) ; T_int_E(I,j) = T_top(i+1,j)
6610 S_int_W(I,j) = S_top(i,j) ; S_int_E(I,j) = S_top(i+1,j)
6620 p_int_W(I,j) = p(i,j,1) ; p_int_E(I,j) = p(i+1,j,1)
6630 intx_za_nonlin(I,j) = intx_za(I,j,1) - 0.5*(za(i,j,1) + za(i+1,j,1))
6640 dp_int_x(I,j) = p(i+1,j,1)-p(i,j,1)
6650 seek_x_cor(I,j) = .false.
666 endif ; enddo ; enddo
667 endif
668 endif
669
6700 do j=js,je
6710 do I=Isq,Ieq
672 ! This expression assumes that temperature and salinity vary linearly with pressure
673 ! between the corners of the reference interfaces found above to get a correction to
674 ! intx_pa that takes nonlinearities in the equation of state into account.
675 ! It is derived from a 5 point quadrature estimate of the integral with a large-scale
676 ! linear correction so that the pressures and heights match at the end-points. It turns
677 ! out that this linear correction cancels out the mid-point specific volume.
678 ! This can be used without masking because dp_int_x and intx_za_nonlin are 0 over land.
6790 T5(1) = T_Int_W(I,j) ; S5(1) = S_Int_W(I,j) ; p5(1) = p_Int_W(I,j)
6800 T5(5) = T_Int_E(I,j) ; S5(5) = S_Int_E(I,j) ; p5(5) = p_Int_E(I,j)
6810 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))
6820 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))
6830 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))
6840 call calculate_spec_vol(T5, S5, p5, SpV5, tv%eqn_of_state, spv_ref=alpha_ref)
685
686 ! Note the consistency with the linear form below because (4.75 + 5.5/2) / 90 = 1/12
687 intx_za_cor_ri(I,j) = C1_90 * (4.75*(SpV5(5)-SpV5(1)) + 5.5*(SpV5(4)-SpV5(2))) * &
6880 dp_int_x(I,j) - intx_za_nonlin(I,j)
689 enddo
690 enddo
691
692 ! Repeat the calculations above for v-velocity points.
6930 T_int_S(:,:) = 0.0 ; S_int_S(:,:) = 0.0 ; p_int_S(:,:) = 0.0
6940 T_int_N(:,:) = 0.0 ; S_int_N(:,:) = 0.0 ; p_int_N(:,:) = 0.0
6950 inty_za_nonlin(:,:) = 0.0 ; inty_za_cor_ri(:,:) = 0.0 ; dp_int_y(:,:) = 0.0
6960 do J=Jsq,Jeq ; do i=is,ie
6970 seek_y_cor(i,J) = (G%mask2dCv(i,J) > 0.)
6980 delta_p_y(i,J) = 0.0
699 enddo ; enddo
700
7010 do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then
7020 if ((p(i,j+1,2) >= p(i,j,1)) .and. (p(i,j,2) >= p(i,j+1,1))) then
703 ! This is the typical case in the open ocean, so use the topmost interface.
7040 T_int_S(i,J) = T_top(i,j) ; T_int_N(i,J) = T_top(i,j+1)
7050 S_int_S(i,J) = S_top(i,j) ; S_int_N(i,J) = S_top(i,j+1)
7060 p_int_S(i,J) = p(i,j,1) ; p_int_N(i,J) = p(i,j+1,1)
7070 inty_za_nonlin(i,J) = inty_za(i,J,1) - 0.5*(za(i,j,1) + za(i,j+1,1))
7080 dp_int_y(i,J) = p(i,j+1,1) - p(i,j,1)
7090 seek_y_cor(i,J) = .false.
710 endif
711 endif ; enddo ; enddo
712
7130 do k=1,nz
7140 do_more_k = .false.
7150 do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then
716 ! Find the topmost layer for which both sides are nonvanished and mass-weighting is not
717 ! activated in the subgrid interpolation.
7180 if (((h(i,j,k) > CS%h_nonvanished) .and. (h(i,j+1,k) > CS%h_nonvanished)) .and. &
719 (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
720 ! Store properties at the bottom of this cell to get a "good estimate" for intypa at
721 ! the interface below this cell (it might have quadratic pressure dependence if sloped)
7220 T_int_S(i,J) = T_b(i,j,k) ; T_int_N(i,J) = T_b(i,j+1,k)
7230 S_int_S(i,J) = S_b(i,j,k) ; S_int_N(i,J) = S_b(i,j+1,k)
7240 p_int_S(i,J) = p(i,j,K+1) ; p_int_N(i,J) = p(i,j+1,K+1)
7250 inty_za_nonlin(i,J) = inty_za(i,J,K+1) - 0.5*(za(i,j,K+1) + za(i,j+1,K+1))
7260 dp_int_y(i,J) = p(i,j+1,K+1) - p(i,j,K+1)
7270 seek_y_cor(i,J) = .false.
728 else
7290 do_more_k = .true.
730 endif
731 endif ; enddo ; enddo
7320 if (.not.do_more_k) exit ! All reference interfaces have been found, so stop working downward.
733 enddo
734
7350 if (do_more_k) then
7360 if (CS%reset_intxpa_flattest) then
737 ! There are still points where a correction is needed, so use flattest interface.
7380 do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then
739 ! choose top interface first
7400 T_int_S(i,J) = T_top(i,j) ; T_int_N(i,J) = T_top(i,j+1)
7410 S_int_S(i,J) = S_top(i,j) ; S_int_N(i,J) = S_top(i,j+1)
7420 p_int_S(i,J) = p(i,j,1) ; p_int_N(i,J) = p(i,j+1,1)
7430 inty_za_nonlin(i,J) = inty_za(i,J,1) - 0.5*(za(i,j,1) + za(i,j+1,1))
7440 dp_int_y(i,J) = p(i,j+1,1) - p(i,j,1)
7450 delta_p_y(i,J) = abs(p(i,j+1,1)-p(i,j,1))
7460 do k=1,nz
7470 if (abs(p(i,j+1,k+1)-p(i,j,k+1)) < delta_p_y(i,J)) then
748 ! bottom of layer is less sloped than top. Use this layer
7490 delta_p_y(i,J) = abs(p(i,j+1,k+1)-p(i,j,k+1))
7500 T_int_S(i,J) = T_b(i,j,k) ; T_int_N(i,J) = T_b(i,j+1,k)
7510 S_int_S(i,J) = S_b(i,j,k) ; S_int_N(i,J) = S_b(i,j+1,k)
7520 p_int_S(i,J) = p(i,j,K+1) ; p_int_N(i,J) = p(i,j+1,K+1)
7530 inty_za_nonlin(i,J) = inty_za(i,J,K+1) - 0.5*(za(i,j,K+1) + za(i,j+1,K+1))
7540 dp_int_y(i,J) = p(i,j+1,K+1) - p(i,j,K+1)
755 endif
756 enddo
7570 seek_y_cor(i,J) = .false.
758 endif ; enddo ; enddo
759 else
760 ! There are still points where a correction is needed, so use the top interface.
7610 do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then
7620 T_int_S(i,J) = T_top(i,j) ; T_int_N(i,J) = T_top(i,j+1)
7630 S_int_S(i,J) = S_top(i,j) ; S_int_N(i,J) = S_top(i,j+1)
7640 p_int_S(i,J) = p(i,j,1) ; p_int_N(i,J) = p(i,j+1,1)
7650 inty_za_nonlin(i,J) = inty_za(i,J,1) - 0.5*(za(i,j,1) + za(i,j+1,1))
7660 dp_int_y(i,J) = p(i,j+1,1) - p(i,j,1)
7670 seek_y_cor(i,J) = .false.
768 endif ; enddo ; enddo
769 endif
770 endif
771
7720 do J=Jsq,Jeq
7730 do i=is,ie
774 ! This expression assumes that temperature and salinity vary linearly with pressure
775 ! between the corners of the reference interfaces found above to get a correction to
776 ! intx_pa that takes nonlinearities in the equation of state into account.
777 ! It is derived from a 5 point quadrature estimate of the integral with a large-scale
778 ! linear correction so that the pressures and heights match at the end-points. It turns
779 ! out that this linear correction cancels out the mid-point specific volume.
780 ! This can be used without masking because dp_int_x and intx_za_nonlin are 0 over land.
7810 T5(1) = T_Int_S(i,J) ; S5(1) = S_Int_S(i,J) ; p5(1) = p_Int_S(i,J)
7820 T5(5) = T_Int_N(i,J) ; S5(5) = S_Int_N(i,J) ; p5(5) = p_Int_N(i,J)
7830 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))
7840 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))
7850 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))
7860 call calculate_spec_vol(T5, S5, p5, SpV5, tv%eqn_of_state, spv_ref=alpha_ref)
787
788 ! Note the consistency with the linear form below because (4.75 + 5.5/2) / 90 = 1/12
789 inty_za_cor_ri(i,J) = C1_90 * (4.75*(SpV5(5)-SpV5(1)) + 5.5*(SpV5(4)-SpV5(2))) * &
7900 dp_int_y(i,J) - inty_za_nonlin(i,J)
791 enddo
792 enddo
793
7940 if (CS%debug) then
795 call uvchksum("Pre-reset int[xy]_za", intx_za, inty_za, G%HI, haloshift=0, &
7960 symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%L_T_to_m_s**2)
797 call uvchksum("int[xy]_za_cor", intx_za_cor_ri, inty_za_cor_ri, G%HI, haloshift=0, &
7980 symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%L_T_to_m_s**2)
799 call uvchksum("int[xy]_za_nonlin", intx_za_nonlin, inty_za_nonlin, G%HI, haloshift=0, &
8000 symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%L_T_to_m_s**2)
801 call uvchksum("dp_int_[xy]", dp_int_x, dp_int_y, G%HI, haloshift=0, &
8020 symmetric=G%Domain%symmetric, unscale=US%RL2_T2_to_Pa)
803 endif
804
805 ! Correct intx_pa and inty_pa at each interface using vertically constant corrections.
8060 do K=1,nz+1 ; do j=js,je ; do I=Isq,Ieq
8070 intx_za(I,j,K) = intx_za(I,j,K) + intx_za_cor_ri(I,j)
808 enddo ; enddo ; enddo
809
8100 do K=1,nz+1 ; do J=Jsq,Jeq ; do i=is,ie
8110 inty_za(i,J,K) = inty_za(i,J,K) + inty_za_cor_ri(i,J)
812 enddo ; enddo ; enddo
813
8140 if (CS%debug) then
815 call uvchksum("Post-reset int[xy]_za", intx_za, inty_za, G%HI, haloshift=0, &
8160 symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%L_T_to_m_s**2)
817 endif
818
819 endif ! intx_za and inty_za have now been reset to reflect the properties of an unimpeded interface.
820
821 !$OMP parallel do default(shared) private(dp)
8220 do k=1,nz
8230 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
8240 dp(i,j) = H_to_RL2_T2 * h(i,j,k)
825 enddo ; enddo
826
827 ! Find the horizontal pressure gradient accelerations.
828 ! These expressions for the accelerations have been carefully checked in
829 ! a set of idealized cases, and should be bug-free.
8300 do j=js,je ; do I=Isq,Ieq
831 PFu(I,j,k) = ( ((za(i,j,K+1)*dp(i,j) + intp_dza(i,j,k)) - &
832 (za(i+1,j,K+1)*dp(i+1,j) + intp_dza(i+1,j,k))) + &
833 ((dp(i+1,j) - dp(i,j)) * intx_za(I,j,K+1) - &
834 (p(i+1,j,K) - p(i,j,K)) * intx_dza(I,j,k)) ) * &
8350 (2.0*G%IdxCu(I,j) / ((dp(i,j) + dp(i+1,j)) + dp_neglect))
836 enddo ; enddo
837
8380 do J=Jsq,Jeq ; do i=is,ie
839 PFv(i,J,k) = (((za(i,j,K+1)*dp(i,j) + intp_dza(i,j,k)) - &
840 (za(i,j+1,K+1)*dp(i,j+1) + intp_dza(i,j+1,k))) + &
841 ((dp(i,j+1) - dp(i,j)) * inty_za(i,J,K+1) - &
842 (p(i,j+1,K) - p(i,j,K)) * inty_dza(i,J,k))) * &
8430 (2.0*G%IdyCv(i,J) / ((dp(i,j) + dp(i,j+1)) + dp_neglect))
844 enddo ; enddo
845 enddo
846
8470 if (CS%GFS_scale < 1.0) then
848 ! Adjust the Montgomery potential to make this a reduced gravity model.
8490 if (use_EOS) then
850 !$OMP parallel do default(shared) private(rho_in_situ)
8510 do j=Jsq,Jeq+1
852 call calculate_density(tv_tmp%T(:,j,1), tv_tmp%S(:,j,1), p(:,j,1), rho_in_situ, &
8530 tv%eqn_of_state, EOSdom)
854
8550 do i=Isq,Ieq+1
8560 dM(i,j) = (CS%GFS_scale - 1.0) * (p(i,j,1)*(1.0/rho_in_situ(i) - alpha_ref) + za(i,j,1))
857 enddo
858 enddo
859 else
860 !$OMP parallel do default(shared)
8610 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
8620 dM(i,j) = (CS%GFS_scale - 1.0) * (p(i,j,1)*(1.0/GV%Rlay(1) - alpha_ref) + za(i,j,1))
863 enddo ; enddo
864 endif
865
866 !$OMP parallel do default(shared)
8670 do k=1,nz
8680 do j=js,je ; do I=Isq,Ieq
8690 PFu(I,j,k) = PFu(I,j,k) - (dM(i+1,j) - dM(i,j)) * G%IdxCu(I,j)
870 enddo ; enddo
8710 do J=Jsq,Jeq ; do i=is,ie
8720 PFv(i,J,k) = PFv(i,J,k) - (dM(i,j+1) - dM(i,j)) * G%IdyCv(i,J)
873 enddo ; enddo
874 enddo
875 endif
876
8770 if (present(pbce)) then
8780 call set_pbce_nonBouss(p, tv_tmp, G, GV, US, CS%GFS_scale, pbce)
879 endif
880
8810 if (present(eta)) then
8820 Pa_to_H = 1.0 / (GV%g_Earth * GV%H_to_RZ)
8830 if (use_p_atm) then
884 !$OMP parallel do default(shared)
8850 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
8860 eta(i,j) = (p(i,j,nz+1) - p_atm(i,j))*Pa_to_H ! eta has the same units as h.
887 enddo ; enddo
888 else
889 !$OMP parallel do default(shared)
8900 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
8910 eta(i,j) = p(i,j,nz+1)*Pa_to_H ! eta has the same units as h.
892 enddo ; enddo
893 endif
894 endif
895
8960 if (CS%id_MassWt_u>0) call post_data(CS%id_MassWt_u, MassWt_u, CS%diag)
8970 if (CS%id_MassWt_v>0) call post_data(CS%id_MassWt_v, MassWt_v, CS%diag)
898
899 ! Diagnostics for tidal forcing and SAL height anomaly
9000 if (CS%id_e_tide>0) then
901 ! To be consistent with old runs, tidal forcing diagnostic also includes total SAL.
902 ! New diagnostics are given for each individual field.
9030 if (CS%tides_answer_date>20230630) then ; do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
9040 e_sal_and_tide(i,j) = e_sal(i,j) + e_tidal_eq(i,j) + e_tidal_sal(i,j)
905 enddo ; enddo ; endif
9060 call post_data(CS%id_e_tide, e_sal_and_tide, CS%diag)
907 endif
9080 if (CS%id_e_sal>0) call post_data(CS%id_e_sal, e_sal, CS%diag)
9090 if (CS%id_e_tidal_eq>0) call post_data(CS%id_e_tidal_eq, e_tidal_eq, CS%diag)
9100 if (CS%id_e_tidal_sal>0) call post_data(CS%id_e_tidal_sal, e_tidal_sal, CS%diag)
911
912 ! Diagnostics for tidal forcing and SAL horizontal gradients
9130 if (CS%calculate_SAL .and. (associated(ADp%sal_u) .or. associated(ADp%sal_v))) then
9140 if (CS%tides) then ; do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
9150 e_sal(i,j) = e_sal(i,j) + e_tidal_sal(i,j)
916 enddo ; enddo ; endif
9170 if (associated(ADp%sal_u)) then ; do k=1,nz ; do j=js,je ; do I=Isq,Ieq
9180 ADp%sal_u(I,j,k) = (e_sal(i+1,j) - e_sal(i,j)) * GV%g_Earth * G%IdxCu(I,j)
919 enddo ; enddo ; enddo ; endif
9200 if (associated(ADp%sal_v)) then ; do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie
9210 ADp%sal_v(i,J,k) = (e_sal(i,j+1) - e_sal(i,j)) * GV%g_Earth * G%IdyCv(i,J)
922 enddo ; enddo ; enddo ; endif
9230 if (CS%id_sal_u>0) call post_data(CS%id_sal_u, ADp%sal_u, CS%diag)
9240 if (CS%id_sal_v>0) call post_data(CS%id_sal_v, ADp%sal_v, CS%diag)
925 endif
926
9270 if (CS%tides .and. (associated(ADp%tides_u) .or. associated(ADp%tides_v))) then
9280 if (associated(ADp%tides_u)) then ; do k=1,nz ; do j=js,je ; do I=Isq,Ieq
9290 ADp%tides_u(I,j,k) = (e_tidal_eq(i+1,j) - e_tidal_eq(i,j)) * GV%g_Earth * G%IdxCu(I,j)
930 enddo ; enddo ; enddo ; endif
9310 if (associated(ADp%tides_v)) then ; do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie
9320 ADp%tides_v(i,J,k) = (e_tidal_eq(i,j+1) - e_tidal_eq(i,j)) * GV%g_Earth * G%IdyCv(i,J)
933 enddo ; enddo ; enddo ; endif
9340 if (CS%id_tides_u>0) call post_data(CS%id_tides_u, ADp%tides_u, CS%diag)
9350 if (CS%id_tides_v>0) call post_data(CS%id_tides_v, ADp%tides_v, CS%diag)
936 endif
9370end subroutine PressureForce_FV_nonBouss
938
939!> \brief Boussinesq analytically-integrated finite volume form of pressure gradient
940!!
941!! Determines the acceleration due to hydrostatic pressure forces, using
942!! the finite volume form of the terms and analytic integrals in depth.
943!!
944!! To work, the following fields must be set outside of the usual (is:ie,js:je)
945!! range before this subroutine is called:
946!! h(isB:ie+1,jsB:je+1), T(isB:ie+1,jsB:je+1), and S(isB:ie+1,jsB:je+1).
94748subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, ADp, p_atm, pbce, eta)
948 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
949 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
950 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
951 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> m]
952 type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamic variables
953 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(out) :: PFu !< Zonal acceleration [L T-2 ~> m s-2]
954 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(out) :: PFv !< Meridional acceleration [L T-2 ~> m s-2]
955 type(PressureForce_FV_CS), intent(in) :: CS !< Finite volume PGF control structure
956 type(ALE_CS), pointer :: ALE_CSp !< ALE control structure
957 type(accel_diag_ptrs), pointer :: ADp !< Acceleration diagnostic pointers
958 real, dimension(:,:), pointer :: p_atm !< The pressure at the ice-ocean
959 !! or atmosphere-ocean interface [R L2 T-2 ~> Pa].
960 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), optional, intent(out) :: pbce !< The baroclinic pressure
961 !! anomaly in each layer due to eta anomalies
962 !! [L2 T-2 H-1 ~> m s-2].
963 real, dimension(SZI_(G),SZJ_(G)), optional, intent(out) :: eta !< The sea-surface height used to
964 !! calculate PFu and PFv [H ~> m], with any
965 !! tidal contributions.
966 ! Local variables
96748 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: e ! Interface height in depth units [Z ~> m].
968 real, dimension(SZI_(G),SZJ_(G)) :: &
96948 e_sal_and_tide, & ! The summation of self-attraction and loading and tidal forcing [Z ~> m].
97048 e_sal, & ! The bottom geopotential anomaly due to self-attraction and loading [Z ~> m].
97148 e_tidal_eq, & ! The bottom geopotential anomaly due to tidal forces from astronomical sources
972 ! [Z ~> m].
97348 e_tidal_sal, & ! The bottom geopotential anomaly due to harmonic self-attraction and loading
974 ! specific to tides [Z ~> m].
97548 Z_0p, & ! The height at which the pressure used in the equation of state is 0 [Z ~> m]
97648 SSH, & ! Sea surfae height anomaly for self-attraction and loading. Used if
977 ! CALCULATE_SAL is True and SAL_USE_BPA is False [Z ~> m].
97848 pbot, & ! Total bottom pressure for self-attraction and loading. Used if
979 ! CALCULATE_SAL is True and SAL_USE_BPA is True [R L2 T-2 ~> Pa].
98048 dM ! The barotropic adjustment to the Montgomery potential to
981 ! account for a reduced gravity model [L2 T-2 ~> m2 s-2].
982 real, dimension(SZI_(G)) :: &
98348 Rho_cv_BL ! The coordinate potential density in the deepest variable
984 ! density near-surface layer [R ~> kg m-3].
985 real, dimension(SZI_(G),SZJ_(G)) :: &
98648 dz_geo ! The change in geopotential thickness through a layer [L2 T-2 ~> m2 s-2].
987 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: &
98848 pa ! The pressure anomaly (i.e. pressure + g*RHO_0*e) at the
989 ! the interface atop a layer [R L2 T-2 ~> Pa].
990 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: &
99148 dpa, & ! The change in pressure anomaly between the top and bottom
992 ! of a layer [R L2 T-2 ~> Pa].
99348 intz_dpa ! The vertical integral in depth of the pressure anomaly less the
994 ! pressure anomaly at the top of the layer [H R L2 T-2 ~> m Pa].
995 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1) :: &
99648 intx_pa ! The zonal integral of the pressure anomaly along the interface
997 ! atop a layer, divided by the grid spacing [R L2 T-2 ~> Pa].
998 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: &
99948 intx_dpa ! The change in intx_pa through a layer [R L2 T-2 ~> Pa].
1000 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1) :: &
100148 inty_pa ! The meridional integral of the pressure anomaly along the
1002 ! interface atop a layer, divided by the grid spacing [R L2 T-2 ~> Pa].
1003 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: &
100448 inty_dpa ! The change in inty_pa through a layer [R L2 T-2 ~> Pa].
1005 real, dimension(SZIB_(G),SZJ_(G)) :: &
100648 intx_pa_cor ! Correction for curvature in intx_pa [R L2 T-2 ~> Pa]
1007 real, dimension(SZI_(G),SZJB_(G)) :: &
100848 inty_pa_cor ! Correction for curvature in inty_pa [R L2 T-2 ~> Pa]
1009
1010 ! These variables are used with reset_intxpa_integral. The values are taken from different
1011 ! interfaces as a function of position.
1012 real, dimension(SZIB_(G),SZJ_(G)) :: &
101348 T_int_W, T_int_E, & ! Temperatures on the reference interface to the east and west of a u-point [C ~> degC]
101448 S_int_W, S_int_E, & ! Salinities on the reference interface to the east and west of a u-point [S ~> ppt]
101548 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]
101648 intx_pa_nonlin, & ! Deviations in the previous version of intx_pa for the reference interface
1017 ! from the value that would be obtained from assuming that pressure varies
1018 ! linearly with depth along that interface [R L2 T-2 ~> Pa].
101948 dgeo_x, & ! The change in x in geopotenial height along the reference interface [L2 T-2 ~> m2 s-2]
102048 intx_pa_cor_ri ! The correction to intx_pa based on the reference interface calculations [R L2 T-2 ~> Pa]
1021 real, dimension(SZI_(G),SZJB_(G)) :: &
102248 T_int_S, T_int_N, & ! Temperatures on the reference interface to the north and south of a v-point [C ~> degC]
102348 S_int_S, S_int_N, & ! Salinities on the reference interface to the north and south of a v-point [S ~> ppt]
102448 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]
102548 inty_pa_nonlin, & ! Deviations in the previous version of intx_pa for the reference interface
1026 ! from the value that would be obtained from assuming that pressure varies
1027 ! linearly with depth along that interface [R L2 T-2 ~> Pa].
102848 dgeo_y, & ! The change in y in geopotenial height along the reference interface [L2 T-2 ~> m2 s-2]
102948 inty_pa_cor_ri ! The correction to inty_pa based on the reference interface calculations [R L2 T-2 ~> Pa]
1030 logical, dimension(SZIB_(G),SZJ_(G)) :: &
103148 seek_x_cor ! If true, try to find a u-point interface that would provide a better estimate
1032 ! of the curvature terms in the intx_pa.
1033 logical, dimension(SZI_(G),SZJB_(G)) :: &
103448 seek_y_cor ! If true, try to find a v-point interface that would provide a better estimate
1035 ! of the curvature terms in the inty_pa.
1036 real, dimension(SZIB_(G),SZJ_(G)) :: &
103748 delta_z_x ! If using flattest interface for reset integral, store x interface differences [Z ~> m]
1038 real, dimension(SZI_(G),SZJB_(G)) :: &
103948 delta_z_y ! If using flattest interface for reset integral, store y interface differences [Z ~> m]
1040
1041 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), target :: &
104248 T_tmp, & ! Temporary array of temperatures where layers that are lighter
1043 ! than the mixed layer have the mixed layer's properties [C ~> degC].
104448 S_tmp ! Temporary array of salinities where layers that are lighter
1045 ! than the mixed layer have the mixed layer's properties [S ~> ppt].
1046 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: &
104748 S_t, S_b, & ! Top and bottom edge values for linear reconstructions
1048 ! of salinity within each layer [S ~> ppt].
104948 T_t, T_b ! Top and bottom edge values for linear reconstructions
1050 ! of temperature within each layer [C ~> degC].
1051 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: &
105248 MassWt_u ! The fractional mass weighting at a u-point [nondim].
1053 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: &
105448 MassWt_v ! The fractional mass weighting at a v-point [nondim].
1055 real, dimension(SZI_(G),SZJ_(G)) :: &
105648 T_top, & ! Temperature of top layer used with correction_intxpa [C ~> degC]
105748 S_top, & ! Salinity of top layer used with correction_intxpa [S ~> ppt]
105848 rho_top ! Density anomaly of top layer used in calculating intx_pa_cor and inty_pa_cor
1059 real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: &
106048 rho_pgf, rho_stanley_pgf ! Density [R ~> kg m-3] from EOS with and without SGS T variance
1061 ! in Stanley parameterization.
1062 real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: &
106348 p_stanley ! Pressure [R L2 T-2 ~> Pa] estimated with Rho_0
106448 real :: zeros(SZI_(G)) ! An array of zero values that can be used as an argument [various]
106548 real :: rho_in_situ(SZI_(G),SZJ_(G)) ! The in situ density [R ~> kg m-3].
106648 real :: p_ref(SZI_(G)) ! The pressure used to calculate the coordinate
1067 ! density, [R L2 T-2 ~> Pa] (usually 2e7 Pa = 2000 dbar).
106848 real :: p_surf_EOS(SZI_(G)) ! The pressure at the ocean surface determined from the surface height,
1069 ! consistent with what is used in the density integral routines [R L2 T-2 ~> Pa]
107048 real :: p0(SZI_(G), SZJ_(G)) ! An array of zeros to use for pressure [R L2 T-2 ~> Pa].
1071 real :: dz_geo_sfc ! The change in surface geopotential height between adjacent cells [L2 T-2 ~> m2 s-2]
1072 real :: GxRho0 ! The gravitational acceleration times mean ocean density [R L2 Z-1 T-2 ~> Pa m-1]
1073 real :: GxRho_ref ! The gravitational acceleration times reference density [R L2 Z-1 T-2 ~> Pa m-1]
1074 real :: rho0_int_density ! Rho0 used in int_density_dz_* subroutines [R ~> kg m-3]
1075 real :: rho0_set_pbce ! Rho0 used in set_pbce_Bouss subroutine [R ~> kg m-3]
1076 real :: h_neglect ! A thickness that is so small it is usually lost
1077 ! in roundoff and can be neglected [H ~> m].
1078 real :: I_Rho0 ! The inverse of the Boussinesq reference density [R-1 ~> m3 kg-1].
1079 real :: G_Rho0 ! G_Earth / Rho_0 in [L2 Z-1 T-2 R-1 ~> m4 s-2 kg-1].
1080 real :: I_g_rho ! The inverse of the density times the gravitational acceleration [Z T2 L-2 R-1 ~> m Pa-1]
1081 real :: rho_ref ! The reference density [R ~> kg m-3].
1082 real :: dz_neglect ! A minimal thickness [Z ~> m], like e.
1083 real :: dz_nonvanished ! A small thickness considered to be vanished for mass weighting [Z ~> m]
1084 real :: H_to_RL2_T2 ! A factor to convert from thickness units (H) to pressure
1085 ! units [R L2 T-2 H-1 ~> Pa m-1 or Pa m2 kg-1].
1086 real :: T5(5) ! Temperatures and salinities at five quadrature points [C ~> degC]
1087 real :: S5(5) ! Salinities at five quadrature points [S ~> ppt]
1088 real :: p5(5) ! Full pressures at five quadrature points for use with the equation of state [R L2 T-2 ~> Pa]
1089 real :: pa5(5) ! The pressure anomaly (i.e. pressure + g*RHO_0*e) at five quadrature points [R L2 T-2 ~> Pa].
1090 real :: r5(5) ! Densities at five quadrature points [R ~> kg m-3]
1091 real :: wt_R ! A weighting factor [nondim]
1092 real, parameter :: C1_6 = 1.0/6.0 ! A rational constant [nondim]
1093 real, parameter :: C1_90 = 1.0/90.0 ! A rational constant [nondim]
1094 logical :: use_p_atm ! If true, use the atmospheric pressure.
1095 logical :: use_ALE ! If true, use an ALE pressure reconstruction.
1096 logical :: use_EOS ! If true, density is calculated from T & S using an equation of state.
1097 logical :: do_more_k ! If true, there are still points where a flatter interface remains to be found.
109824 type(thermo_var_ptrs) :: tv_tmp! A structure of temporary T & S.
1099 integer, dimension(2) :: EOSdom ! The i-computational domain for the equation of state
1100 integer, dimension(2) :: EOSdom_h ! The i-computational domain for the equation of state at tracer points
1101 integer, dimension(2) :: EOSdom_u ! The i-computational domain for the equation of state at u-velocity points
1102 integer, dimension(2) :: EOSdom_v ! The i-computational domain for the equation of state at v-velocity points
1103 integer :: EOSdom2d(2,2) ! The 2D compute domain for the equation of state
1104 integer :: is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, nkmb
1105 integer :: i, j, k, m
1106
110724 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
110824 nkmb=GV%nk_rho_varies
110924 Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB
111024 EOSdom(1) = Isq - (G%isd-1) ; EOSdom(2) = G%iec+1 - (G%isd-1)
111124 EOSdom_u(1) = Isq - (G%IsdB-1) ; EOSdom_u(2) = Ieq - (G%IsdB-1)
111224 EOSdom_v(1) = is - (G%isd-1) ; EOSdom_v(2) = ie - (G%isd-1)
1113
111472 EOSdom2d(1,:) = EOSdom(:)
111572 EOSdom2d(2,:) = [Jsq - (G%isd - 1), (je + 1) - (G%jsd - 1)]
1116
1117 ! TODO: This would be done outside of the function!
1118
111924 if (.not.CS%initialized) call MOM_error(FATAL, &
11200 "MOM_PressureForce_FV_Bouss: Module must be initialized before it is used.")
1121
112224 use_p_atm = associated(p_atm)
112324 use_EOS = associated(tv%eqn_of_state)
112424 use_ALE = .false.
112524 if (associated(ALE_CSp)) use_ALE = CS%reconstruct .and. use_EOS
1126
112724 h_neglect = GV%H_subroundoff
112824 dz_neglect = GV%dZ_subroundoff
112924 dz_nonvanished = GV%H_to_Z*CS%h_nonvanished
113024 I_Rho0 = 1.0 / GV%Rho0
113124 G_Rho0 = GV%g_Earth / GV%Rho0
113224 GxRho0 = GV%g_Earth * GV%Rho0
113324 rho_ref = CS%rho_ref
1134
113524 if (CS%rho_ref_bug) then
113624 rho0_int_density = rho_ref
113724 rho0_set_pbce = rho_ref
113824 GxRho_ref = GxRho0
113924 I_g_rho = 1.0 / (rho_ref * GV%g_Earth)
1140 else
11410 rho0_int_density = GV%Rho0
11420 rho0_set_pbce = GV%Rho0
11430 GxRho_ref = GV%g_Earth * rho_ref
11440 I_g_rho = 1.0 / (GV%rho0 * GV%g_Earth)
1145 endif
1146
114724 if ((CS%id_MassWt_u > 0) .or. (CS%id_MassWt_v > 0)) then
11480 MassWt_u(:,:,:) = 0.0 ; MassWt_v(:,:,:) = 0.0
1149 endif
1150
1151 !$omp target enter data map(alloc: e)
1152
11532952 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
1154184488 e(i,j,nz+1) = -G%bathyT(i,j)
1155 enddo
1156
1157 ! The following two if-blocks are used to recover old answers for self-attraction and loading
1158 ! (SAL) and tides only. The old algorithm moves interface heights before density calculations,
1159 ! and therefore is incorrect without SSH_IN_EOS_PRESSURE_FOR_PGF=True (added in August 2024).
1160 ! See the code right after Pa calculation loop for the new algorithm.
1161
1162 ! Calculate and add SAL geopotential anomaly to interface height (old answers)
116324 if (CS%calculate_SAL .and. CS%tides_answer_date<=20250131) then
1164 !$omp target update from(e(:,:,nz+1))
1165
1166 !$OMP parallel do default(shared)
11670 do j=Jsq,Jeq+1
11680 do i=Isq,Ieq+1
11690 SSH(i,j) = min(-G%bathyT(i,j) - G%meanSL(i,j), 0.0)
1170 enddo
11710 do k=1,nz ; do i=Isq,Ieq+1
11720 SSH(i,j) = SSH(i,j) + h(i,j,k)*GV%H_to_Z
1173 enddo ; enddo
1174 enddo
11750 call calc_SAL(SSH, e_sal, G, CS%SAL_CSp, tmp_scale=US%Z_to_m)
1176
11770 if (CS%tides_answer_date>20230630) then ! answers_date between [20230701, 20250131]
1178 !$OMP parallel do default(shared)
11790 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
11800 e(i,j,nz+1) = e(i,j,nz+1) - e_sal(i,j)
1181 enddo ; enddo
1182 endif
1183
1184 !$omp target update to(e(:,:,nz+1))
1185 endif
1186
1187 ! Calculate and add tidal geopotential anomaly to interface height (old answers)
118824 if (CS%tides .and. CS%tides_answer_date<=20250131) then
1189 !$omp target update from(e(:,:,nz+1))
1190
11910 if (CS%tides_answer_date>20230630) then ! answers_date between [20230701, 20250131]
11920 call calc_tidal_forcing(CS%Time, e_tidal_eq, e_tidal_sal, G, US, CS%tides_CSp)
1193 !$OMP parallel do default(shared)
11940 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
11950 e(i,j,nz+1) = e(i,j,nz+1) - (e_tidal_eq(i,j) + e_tidal_sal(i,j))
1196 enddo ; enddo
1197 else ! answers_date before 20230701
11980 if (.not.CS%calculate_SAL) e_sal(:,:) = 0.0
1199 call calc_tidal_forcing_legacy(CS%Time, e_sal, e_sal_and_tide, e_tidal_eq, e_tidal_sal, &
12000 G, US, CS%tides_CSp)
1201 !$OMP parallel do default(shared)
12020 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
12030 e(i,j,nz+1) = e(i,j,nz+1) - e_sal_and_tide(i,j)
1204 enddo ; enddo
1205 endif
1206
1207 !$omp target update to(e(:,:,nz+1))
1208 endif
1209
12101824 do k=nz,1,-1
121124 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
121213836600 e(i,j,K) = e(i,j,K+1) + h(i,j,k)*GV%H_to_Z
1213 enddo
1214 enddo
1215
121624 if (use_EOS) then
121724 if (nkmb>0) then
1218 ! With a bulk mixed layer, replace the T & S of any layers that are lighter than the buffer
1219 ! layer with the properties of the buffer layer. These layers will be massless anyway, and
1220 ! it avoids any formal calculations with hydrostatically unstable profiles.
12210 tv_tmp%T => T_tmp ; tv_tmp%S => S_tmp
12220 tv_tmp%eqn_of_state => tv%eqn_of_state
1223
12240 do i=Isq,Ieq+1 ; p_ref(i) = tv%P_Ref ; enddo
1225 !$OMP parallel do default(shared) private(Rho_cv_BL)
12260 do j=Jsq,Jeq+1
12270 do k=1,nkmb ; do i=Isq,Ieq+1
12280 tv_tmp%T(i,j,k) = tv%T(i,j,k) ; tv_tmp%S(i,j,k) = tv%S(i,j,k)
1229 enddo ; enddo
1230 call calculate_density(tv%T(:,j,nkmb), tv%S(:,j,nkmb), p_ref, Rho_cv_BL(:), &
12310 tv%eqn_of_state, EOSdom)
1232
12330 do k=nkmb+1,nz ; do i=Isq,Ieq+1
12340 if (GV%Rlay(k) < Rho_cv_BL(i)) then
12350 tv_tmp%T(i,j,k) = tv%T(i,j,nkmb) ; tv_tmp%S(i,j,k) = tv%S(i,j,nkmb)
1236 else
12370 tv_tmp%T(i,j,k) = tv%T(i,j,k) ; tv_tmp%S(i,j,k) = tv%S(i,j,k)
1238 endif
1239 enddo ; enddo
1240 enddo
1241 else
124224 tv_tmp%T => tv%T ; tv_tmp%S => tv%S
124324 tv_tmp%eqn_of_state => tv%eqn_of_state
1244 endif
1245 endif
1246
1247 ! If regridding is activated, do a linear reconstruction of salinity
1248 ! and temperature across each layer. The subscripts 't' and 'b' refer
1249 ! to top and bottom values within each layer (these are the only degrees
1250 ! of freedom needed to know the linear profile).
125124 if ( use_ALE .and. (CS%Recon_Scheme == 1) ) then
125224 call TS_PLM_edge_values(ALE_CSp, S_t, S_b, T_t, T_b, G, GV, tv, h, CS%boundary_extrap)
12530 elseif ( use_ALE .and. (CS%Recon_Scheme == 2) ) then
12540 call TS_PPM_edge_values(ALE_CSp, S_t, S_b, T_t, T_b, G, GV, tv, h, CS%boundary_extrap)
12550 elseif ( use_ALE .and. (CS%Recon_Scheme == 3) ) then
12560 call TS_PLM_WLS_edge_values(ALE_CSp, S_t, S_b, T_t, T_b, G, GV, tv, h)
12570 elseif (CS%reset_intxpa_integral) then
12580 do k=1,nz ; do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
12590 T_b(i,j,k) = tv%T(i,j,k) ; S_b(i,j,k) = tv%S(i,j,k)
1260 enddo ; enddo ; enddo
1261 endif
1262
1263 !$omp target enter data map(alloc: pa)
1264
1265 ! Set the surface boundary conditions on pressure anomaly and its horizontal
1266 ! integrals, assuming that the surface pressure anomaly varies linearly
1267 ! in x and y.
126824 if (use_p_atm) then
12692952 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
1270184488 pa(i,j,1) = GxRho_ref * (e(i,j,1) - G%Z_ref) + p_atm(i,j)
1271 enddo
1272 else
12730 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
12740 pa(i,j,1) = GxRho_ref * (e(i,j,1) - G%Z_ref)
1275 enddo
1276 endif
1277
127824 if (use_EOS) then
1279 !$omp target enter data map(alloc: Z_0p)
128024 if (CS%use_SSH_in_Z0p .and. use_p_atm) then
12810 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
12820 Z_0p(i,j) = e(i,j,1) + p_atm(i,j) * I_g_rho
1283 enddo
128424 elseif (CS%use_SSH_in_Z0p) then
12850 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
12860 Z_0p(i,j) = e(i,j,1)
1287 enddo
1288 else
12892952 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
1290184488 Z_0p(i,j) = G%meanSL(i,j)
1291 enddo
1292 endif
1293 !$omp target update from(Z_0p)
1294 endif
1295
1296 ! Calculate 4 integrals through the layer that are required in the
1297 ! subsequent calculation.
1298 !$omp target enter data map(alloc: dpa, intx_dpa, inty_dpa, intz_dpa)
1299
130024 if (use_EOS) then
1301 !$omp target update from(e) if( (use_ALE .and. CS%Recon_Scheme > 0) .or. &
1302 !$omp (CS%id_MassWt_u > 0) .or. (CS%id_MassWt_v > 0))
1303 ! transfer tv_tmp%* only if int_density_dz is called
1304 !$omp target enter data map(to: tv_tmp, tv_tmp%T, tv_tmp%S) &
1305 !$omp if(.not.(use_ALE .and. CS%Recon_Scheme > 0))
1306
1307 ! The following routine computes the integrals that are needed to
1308 ! calculate the pressure gradient force. Linear profiles for T and S are
1309 ! assumed when regridding is activated. Otherwise, the previous version
1310 ! is used, whereby densities within each layer are constant no matter
1311 ! where the layers are located.
13121824 do k=1,nz
13131800 if ( use_ALE .and. CS%Recon_Scheme > 0 ) then
13141800 if ( CS%Recon_Scheme == 1 .or. CS%Recon_Scheme == 3 ) then
1315 call int_density_dz_generic_plm(k, tv, T_t, T_b, S_t, S_b, e, &
1316 rho_ref, rho0_int_density, GV%g_Earth, dz_neglect, G%bathyT, &
1317 G%HI, GV, tv%eqn_of_state, US, CS%use_stanley_pgf, dpa(:,:,k), intz_dpa(:,:,k), &
1318 intx_dpa(:,:,k), inty_dpa(:,:,k), &
1319 MassWghtInterp=CS%MassWghtInterp, &
1320 use_inaccurate_form=CS%use_inaccurate_pgf_rho_anom, Z_0p=Z_0p, &
13211800 MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, h_nv=dz_nonvanished)
13220 elseif ( CS%Recon_Scheme == 2 ) then
1323 call int_density_dz_generic_ppm(k, tv, T_t, T_b, S_t, S_b, e, &
1324 rho_ref, rho0_int_density, GV%g_Earth, dz_neglect, G%bathyT, &
1325 G%HI, GV, tv%eqn_of_state, US, CS%use_stanley_pgf, dpa(:,:,k), intz_dpa(:,:,k), &
1326 intx_dpa(:,:,k), inty_dpa(:,:,k), &
1327 MassWghtInterp=CS%MassWghtInterp, Z_0p=Z_0p, &
13280 MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, h_nv=dz_nonvanished)
1329 endif
1330 ! defensive update - not sure if it works
1331 !$omp target update to(dpa, intx_dpa, inty_dpa, intz_dpa)
1332 else
1333 call int_density_dz(tv_tmp%T(:,:,k), tv_tmp%S(:,:,k), e(:,:,K), e(:,:,K+1), &
1334 rho_ref, rho0_int_density, GV%g_Earth, G%HI, tv%eqn_of_state, US, dpa(:,:,k), &
1335 intz_dpa(:,:,k), intx_dpa(:,:,k), inty_dpa(:,:,k), G%bathyT, e(:,:,1), dz_neglect, &
1336 CS%MassWghtInterp, Z_0p=Z_0p, &
13370 MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, h_nv=dz_nonvanished)
1338 endif
13391800 if (GV%Z_to_H /= 1.0) then
13400 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
13410 intz_dpa(i,j,k) = intz_dpa(i,j,k)*GV%Z_to_H
1342 enddo
1343 endif
13441800 if ((CS%id_MassWt_u > 0) .or. (CS%id_MassWt_v > 0)) &
1345 call diagnose_mass_weight_Z(e(:,:,K), e(:,:,K+1), G%bathyT, e(:,:,1), dz_neglect, CS%MassWghtInterp, &
1346 G%HI, MassWt_u(:,:,k), MassWt_v(:,:,k), &
134724 MassWghtInterpVanOnly=CS%MassWghtInterpVanOnly, h_nv=CS%h_nonvanished)
1348 enddo
1349 !$omp target exit data map(release: tv_tmp, tv_tmp%T, tv_tmp%S) &
1350 !$omp if(.not.(use_ALE .and. CS%Recon_Scheme > 0))
1351 else
1352 !$omp target data map(alloc: dz_geo)
13530 do k=1,nz
13540 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
13550 dz_geo(i,j) = GV%g_Earth * GV%H_to_Z*h(i,j,k)
13560 dpa(i,j,k) = (GV%Rlay(k) - rho_ref) * dz_geo(i,j)
13570 intz_dpa(i,j,k) = 0.5*(GV%Rlay(k) - rho_ref) * dz_geo(i,j)*h(i,j,k)
1358 enddo
13590 do concurrent (j=js:je, I=Isq:Ieq)
13600 intx_dpa(I,j,k) = 0.5*(GV%Rlay(k) - rho_ref) * (dz_geo(i,j) + dz_geo(i+1,j))
1361 enddo
13620 do concurrent (J=Jsq:Jeq, i=is:ie)
13630 inty_dpa(i,J,k) = 0.5*(GV%Rlay(k) - rho_ref) * (dz_geo(i,j) + dz_geo(i,j+1))
1364 enddo
1365 enddo
1366 !$omp end target data
1367 endif
1368
1369 ! Set the pressure anomalies at the interfaces.
137024 do concurrent (j=Jsq:Jeq+1)
1371113112 do k=1,nz
13721488 do concurrent (i=Isq:Ieq+1)
137313726800 pa(i,j,K+1) = pa(i,j,K) + dpa(i,j,k)
1374 enddo
1375 enddo
1376 enddo
1377
1378 ! Calculate and add SAL geopotential anomaly to interface height (new answers)
137924 if (CS%calculate_SAL .and. CS%tides_answer_date>20250131) then
13800 if (CS%sal_use_bpa) then
1381 !$OMP parallel do default(shared)
13820 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
13830 pbot(i,j) = pa(i,j,nz+1) - GxRho_ref * (e(i,j,nz+1) - G%Z_ref)
1384 enddo ; enddo
13850 call calc_SAL(pbot, e_sal, G, CS%SAL_CSp, tmp_scale=US%Z_to_m)
1386 else
1387 !$OMP parallel do default(shared)
13880 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
13890 SSH(i,j) = e(i,j,1) - G%Z_ref
1390 ! Remove above sea level topography at floodable cells
13910 SSH(i,j) = SSH(i,j) - max(-G%bathyT(i,j)-G%meanSL(i,j), 0.0)
1392 enddo ; enddo
13930 call calc_SAL(SSH, e_sal, G, CS%SAL_CSp, tmp_scale=US%Z_to_m)
1394 endif
13950 if (.not.CS%bq_sal_tides) then ; do K=1,nz+1
1396 !$OMP parallel do default(shared)
13970 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
13980 e(i,j,K) = e(i,j,K) - e_sal(i,j)
13990 pa(i,j,K) = pa(i,j,K) - GxRho_ref * e_sal(i,j)
1400 enddo ; enddo
1401 enddo ; endif
1402 endif
1403
1404 ! Calculate and add tidal geopotential anomaly to interface height (new answers)
140524 if (CS%tides .and. CS%tides_answer_date>20250131) then
14060 call calc_tidal_forcing(CS%Time, e_tidal_eq, e_tidal_sal, G, US, CS%tides_CSp)
14070 if (.not.CS%bq_sal_tides) then ; do K=1,nz+1
1408 !$OMP parallel do default(shared)
14090 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
14100 e(i,j,K) = e(i,j,K) - (e_tidal_eq(i,j) + e_tidal_sal(i,j))
14110 pa(i,j,K) = pa(i,j,K) - GxRho_ref * (e_tidal_eq(i,j) + e_tidal_sal(i,j))
1412 enddo ; enddo
1413 enddo ; endif
1414 endif
1415
141624 if (CS%correction_intxpa .or. CS%reset_intxpa_integral) then
1417 ! Determine surface temperature and salinity for use in the pressure gradient corrections
14180 if (use_ALE .and. (CS%Recon_Scheme > 0)) then
14190 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
14200 T_top(i,j) = T_t(i,j,1) ; S_top(i,j) = S_t(i,j,1)
1421 enddo ; enddo
1422 else
14230 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
14240 T_top(i,j) = tv%T(i,j,1) ; S_top(i,j) = tv%S(i,j,1)
1425 enddo ; enddo
1426 endif
1427 endif
1428
1429 !$omp target enter data map(alloc: intx_pa, inty_pa)
1430
143124 if (CS%correction_intxpa) then
1432 ! TODO needs to be moved to GPU
1433
1434 ! Determine surface density for use in the pressure gradient corrections
1435 !$OMP parallel do default(shared) private(p_surf_EOS)
14360 do j=Jsq,Jeq+1
1437 ! P_surf_EOS here is consistent with the pressure that is used in the int_density_dz routines.
14380 do i=Isq,Ieq+1 ; p_surf_EOS(i) = -GxRho0*(e(i,j,1) - Z_0p(i,j)) ; enddo
1439 call calculate_density(T_top(:,j), S_top(:,j), p_surf_EOS, rho_top(:,j), &
14400 tv%eqn_of_state, EOSdom, rho_ref=rho_ref)
1441 enddo
1442
14430 if (CS%debug) then
14440 call hchksum(rho_top, "intx_pa rho_top", G%HI, haloshift=1, unscale=US%R_to_kg_m3)
14450 call hchksum(e(:,:,1), "intx_pa e(1)", G%HI, haloshift=1, unscale=US%Z_to_m)
14460 call hchksum(pa(:,:,1), "intx_pa pa(1)", G%HI, haloshift=1, unscale=US%RL2_T2_to_Pa)
1447 endif
1448
1449 ! This version attempts to correct for hydrostatic variations in surface pressure under ice.
1450 !$OMP parallel do default(shared) private(dz_geo_sfc)
14510 do j=js,je ; do I=Isq,Ieq
14520 intx_pa_cor(I,j) = 0.0
14530 dz_geo_sfc = GV%g_Earth * (e(i+1,j,1)-e(i,j,1))
14540 if ((dz_geo_sfc * rho_ref - (pa(i+1,j,1)-pa(i,j,1)))*dz_geo_sfc > 0.0) then
1455 ! The pressure/depth relationship has a positive implied density given by
1456 ! rho_implied = rho_ref - (pa(i+1,j,1)-pa(i,j,1)) / dz_geo_sfc
14570 if (-dz_geo_sfc * (pa(i+1,j,1)-pa(i,j,1)) > &
1458 0.25*((rho_top(i+1,j)+rho_top(i,j))-2.0*rho_ref) * dz_geo_sfc**2) then
1459 ! The pressure difference is at least half the size of the difference expected by hydrostatic
1460 ! balance. This test gets rid of pressure differences that are small, e.g. open ocean.
1461 ! Use 5 point quadrature to calculate intxpa
14620 T5(1) = T_top(i,j) ; T5(5) = T_top(i+1,j)
14630 S5(1) = S_top(i,j) ; S5(5) = S_top(i+1,j)
14640 pa5(1) = pa(i,j,1) ; pa5(5) = pa(i+1,j,1)
1465 ! Pressure input to density EOS is consistent with the pressure used in the int_density_dz routines.
14660 p5(1) = -GxRho0*(e(i,j,1) - Z_0p(i,j))
14670 p5(5) = -GxRho0*(e(i+1,j,1) - Z_0p(i,j))
14680 do m=2,4
14690 wt_R = 0.25*real(m-1)
14700 T5(m) = T5(1) + (T5(5)-T5(1))*wt_R
14710 S5(m) = S5(1) + (S5(5)-S5(1))*wt_R
14720 p5(m) = p5(1) + (p5(5)-p5(1))*wt_R
1473 enddo !m
14740 call calculate_density(T5, S5, p5, r5, tv%eqn_of_state, rho_ref=rho_ref)
1475
1476 ! Use a trapezoidal rule integral of the hydrostatic equation to determine the pressure
1477 ! anomalies at 5 equally spaced points along the interface, and then use Boole's rule
1478 ! quadrature to find the integrated correction to the integral of pressure along the interface.
1479 ! The derivation for this expression is shown below in the y-direction version.
14800 intx_pa_cor(I,j) = C1_90 * (4.75*(r5(5)-r5(1)) + 5.5*(r5(4)-r5(2))) * dz_geo_sfc
1481 ! Note that (4.75 + 5.5/2) / 90 = 1/12, so this is consistent with the linear result below.
1482 endif
1483 endif
14840 intx_pa(I,j,1) = 0.5*(pa(i,j,1) + pa(i+1,j,1)) + intx_pa_cor(I,j)
1485 enddo ; enddo
1486 !$OMP parallel do default(shared) private(dz_geo_sfc)
14870 do J=Jsq,Jeq ; do i=is,ie
14880 inty_pa_cor(i,J) = 0.0
14890 dz_geo_sfc = GV%g_Earth * (e(i,j+1,1)-e(i,j,1))
14900 if ((dz_geo_sfc * rho_ref - (pa(i,j+1,1)-pa(i,j,1)))*dz_geo_sfc > 0.0) then
1491 ! The pressure/depth relationship has a positive implied density
14920 if (-dz_geo_sfc * (pa(i,j+1,1)-pa(i,j,1)) > &
1493 0.25*((rho_top(i,j+1)+rho_top(i,j))-2.0*rho_ref) * dz_geo_sfc**2) then
1494 ! The pressure difference is at least half the size of the difference expected by hydrostatic
1495 ! balance. This test gets rid of pressure differences that are small, e.g. open ocean.
1496 ! Use 5 point quadrature to calculate intypa
14970 T5(1) = T_top(i,j) ; T5(5) = T_top(i,j+1)
14980 S5(1) = S_top(i,j) ; S5(5) = S_top(i,j+1)
14990 pa5(1) = pa(i,j,1) ; pa5(5) = pa(i,j+1,1)
1500 ! Pressure input to density EOS is consistent with the pressure used in the int_density_dz routines.
15010 p5(1) = -GxRho0*(e(i,j,1) - Z_0p(i,j))
15020 p5(5) = -GxRho0*(e(i,j+1,1) - Z_0p(i,j))
1503
15040 do m=2,4
15050 wt_R = 0.25*real(m-1)
15060 T5(m) = T5(1) + (T5(5)-T5(1))*wt_R
15070 S5(m) = S5(1) + (S5(5)-S5(1))*wt_R
15080 p5(m) = p5(1) + (p5(5)-p5(1))*wt_R
1509 enddo !m
15100 call calculate_density(T5, S5, p5, r5, tv%eqn_of_state, rho_ref=rho_ref)
1511
1512 ! Use a trapezoidal rule integral of the hydrostatic equation to determine the pressure
1513 ! anomalies at 5 equally spaced points along the interface, and then use Boole's rule
1514 ! quadrature to find the integrated correction to the integral of pressure along the interface.
15150 inty_pa_cor(i,J) = C1_90 * (4.75*(r5(5)-r5(1)) + 5.5*(r5(4)-r5(2))) * dz_geo_sfc
1516
1517 ! The derivation of this correction follows:
1518
1519 ! Make pressure curvature a difference from the linear fit of pressure between the two points
1520 ! (which is equivalent to taking 4 trapezoidal rule integrals of the hydrostatic equation on
1521 ! sub-segments), with a constant slope that is chosen so that the pressure anomalies at the
1522 ! two ends of the segment agree with their known values.
1523 ! d_geo_8 = 0.125*dz_geo_sfc
1524 ! dpa_subseg = 0.25*(pa5(5)-pa5(1)) + &
1525 ! 0.25*d_geo_8 * ((r5(5)+r5(1)) + 2.0*((r5(4)+r5(2)) + r5(3)))
1526 ! do m=2,4
1527 ! pa5(m) = pa5(m-1) + dpa_subseg - d_geo_8*(r5(m)+r5(m-1)))
1528 ! enddo
1529
1530 ! Explicitly finding expressions for the incremental pressures from the recursion relation above:
1531 ! 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)) )
1532 ! ! pa5(3) = 0.5*(pa5(1) + pa5(5)) + 0.25*d_geo_8 * &
1533 ! ! ( (r5(5)+r5(1)) + 2.0*((r5(4)+r5(2)) + r5(3)) + &
1534 ! ! (r5(5)-3.*r5(1)) + 2.0*((r5(4)-r5(2)) + r5(3)) - 4.*(r5(3)+r5(2)) )
1535 ! pa5(3) = 0.5*(pa5(1) + pa5(5)) + d_geo_8 * (0.5*(r5(5)-r5(1)) + (r5(4)-r5(2)) )
1536 ! ! pa5(4) = 0.25*(pa5(1) + 3.0*pa5(5)) + 0.25*d_geo_8 * &
1537 ! ! (2.0*(r5(5)-r5(1)) + 4.0*(r5(4)-r5(2)) + (r5(5)+r5(1)) + &
1538 ! ! 2.0*(r5(4)+r5(2)) + 2.0*r5(3) - 4.*(r5(4)+r5(3)))
1539 ! 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)) )
1540 ! ! pa5(5) = pa5(5) + 0.25*d_geo_8 * &
1541 ! ! ( (3.*r5(5)-r5(1)) + 2.0*((r5(4)-r5(2)) - r5(3)) + &
1542 ! ! ((r5(5)+r5(1)) + 2.0*((r5(4)+r5(2)) + r5(3))) - 4.*(r5(5)+r5(4)) )
1543 ! pa5(5) = pa5(5) ! As it should.
1544
1545 ! From these:
1546 ! pa5(2) + pa5(4) = (pa5(1) + pa5(5)) + 0.25*d_geo_8 * &
1547 ! ( (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))
1548 ! pa5(2) + pa5(4) = (pa5(1) + pa5(5)) + d_geo_8 * ( (r5(5)-r5(1)) + (r5(4)-r5(2)) )
1549
1550 ! Get the correction from the difference between the 5-point quadrature integral of pa5 and
1551 ! its trapezoidal rule integral as:
1552 ! 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)))
1553 ! inty_pa_cor(i,J) = C1_90*((32.0*(pa5(2)+pa5(4)) + 12.0*pa5(3)) - 38.0*(pa5(1)+pa5(5)))
1554 ! inty_pa_cor(i,J) = C1_90*d_geo_8 * ((32.0*( (r5(5)-r5(1)) + (r5(4)-r5(2)) ) + &
1555 ! (6.*(r5(5)-r5(1)) + 12.0*(r5(4)-r5(2)) ))
1556 ! inty_pa_cor(i,J) = C1_90*d_geo_8 * ( 38.0*(r5(5)-r5(1)) + 44.0*(r5(4)-r5(2)) )
1557 endif
1558 endif
15590 inty_pa(i,J,1) = 0.5*(pa(i,j,1) + pa(i,j+1,1)) + inty_pa_cor(i,J)
1560 enddo ; enddo
1561
15620 if (CS%debug) then
1563 call uvchksum("int[xy]_pa_cor", intx_pa_cor, inty_pa_cor, G%HI, haloshift=0, &
15640 symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%RL2_T2_to_Pa)
1565 call uvchksum("int[xy]_pa(1)", intx_pa(:,:,1), inty_pa(:,:,1), G%HI, haloshift=0, &
15660 symmetric=G%Domain%symmetric, scalar_pair=.true., unscale=US%RL2_T2_to_Pa)
1567 endif
1568
1569 ! TODO until moved to GPU
1570 !$omp target update to(intx_pa, inty_pa)
1571 else
1572 ! Set the surface boundary conditions on the horizontally integrated pressure anomaly,
1573 ! assuming that the surface pressure anomaly varies linearly in x and y.
1574 ! If there is an ice-shelf or icebergs, this linear variation would need to be applied
1575 ! to an interior interface.
157624 do concurrent (j=js:je, I=Isq:Ieq)
1577177168 intx_pa(I,j,1) = 0.5*(pa(i,j,1) + pa(i+1,j,1))
1578 enddo
157924 do concurrent (J=Jsq:Jeq, i=is:ie)
1580178584 inty_pa(i,J,1) = 0.5*(pa(i,j,1) + pa(i,j+1,1))
1581 enddo
1582 endif
1583
15841824 do k=1,nz
158524 do concurrent (j=js:je, I=Isq:Ieq)
158613287600 intx_pa(I,j,K+1) = intx_pa(I,j,K) + intx_dpa(I,j,k)
1587 enddo
1588 enddo
15891824 do k=1,nz
159024 do concurrent (J=Jsq:Jeq, i=is:ie)
159113393800 inty_pa(i,J,K+1) = inty_pa(i,J,K) + inty_dpa(i,J,k)
1592 enddo
1593 enddo
1594
159524 if (CS%reset_intxpa_integral) then
1596 ! Having stored the pressure gradient info, we can work out where the first nonvanished layers is
1597 ! reset intxpa there, then adjust intxpa throughout the water column.
1598
1599 ! TODO temporarily move back to CPU
1600 !$omp target update from(intx_pa, inty_pa)
1601
1602 ! Zero out the 2-d arrays that will be set from various reference interfaces.
16030 T_int_W(:,:) = 0.0 ; S_int_W(:,:) = 0.0 ; p_int_W(:,:) = 0.0
16040 T_int_E(:,:) = 0.0 ; S_int_E(:,:) = 0.0 ; p_int_E(:,:) = 0.0
16050 intx_pa_nonlin(:,:) = 0.0 ; dgeo_x(:,:) = 0.0 ; intx_pa_cor_ri(:,:) = 0.0
16060 do j=js,je ; do I=Isq,Ieq
16070 seek_x_cor(I,j) = (G%mask2dCu(I,j) > 0.)
16080 delta_z_x(I,j) = 0.0
1609 enddo ; enddo
1610
16110 do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then
16120 if ((e(i+1,j,2) <= e(i,j,1)) .and. (e(i,j,2) <= e(i+1,j,1))) then
1613 ! This is a typical case in the open ocean, so use the topmost interface.
16140 T_int_W(I,j) = T_top(i,j) ; T_int_E(I,j) = T_top(i+1,j)
16150 S_int_W(I,j) = S_top(i,j) ; S_int_E(I,j) = S_top(i+1,j)
16160 p_int_W(I,j) = -GxRho0*(e(i,j,1) - Z_0p(i,j))
16170 p_int_E(I,j) = -GxRho0*(e(i+1,j,1) - Z_0p(i,j))
16180 intx_pa_nonlin(I,j) = intx_pa(I,j,1) - 0.5*(pa(i,j,1) + pa(i+1,j,1))
16190 dgeo_x(I,j) = GV%g_Earth * (e(i+1,j,1)-e(i,j,1))
16200 seek_x_cor(I,j) = .false.
1621 endif
1622 endif ; enddo ; enddo
1623
16240 do k=1,nz
16250 do_more_k = .false.
16260 do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then
1627 ! Find the topmost layer for which both sides are nonvanished and mass-weighting is not
1628 ! activated in the subgrid interpolation.
16290 if (((h(i,j,k) > CS%h_nonvanished) .and. (h(i+1,j,k) > CS%h_nonvanished)) .and. &
1630 (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
1631 ! Store properties at the bottom of this cell to get a "good estimate" for intxpa at
1632 ! the interface below this cell (it might have quadratic pressure dependence if sloped)
16330 T_int_W(I,j) = T_b(i,j,k) ; T_int_E(I,j) = T_b(i+1,j,k)
16340 S_int_W(I,j) = S_b(i,j,k) ; S_int_E(I,j) = S_b(i+1,j,k)
1635 ! These pressures are only used for the equation of state, and are only a function of
1636 ! height, consistent with the expressions in the int_density_dz routines.
16370 p_int_W(I,j) = -GxRho0*(e(i,j,K+1) - Z_0p(i,j))
16380 p_int_E(I,j) = -GxRho0*(e(i+1,j,K+1) - Z_0p(i,j))
1639
16400 intx_pa_nonlin(I,j) = intx_pa(I,j,K+1) - 0.5*(pa(i,j,K+1) + pa(i+1,j,K+1))
16410 dgeo_x(I,j) = GV%g_Earth * (e(i+1,j,K+1)-e(i,j,K+1))
16420 seek_x_cor(I,j) = .false.
1643 else
16440 do_more_k = .true.
1645 endif
1646 endif ; enddo ; enddo
16470 if (.not.do_more_k) exit ! All reference interfaces have been found, so stop working downward.
1648 enddo
1649
16500 if (do_more_k) then
16510 if (CS%reset_intxpa_flattest) then
1652 ! There are still points where a correction is needed, so use flattest interface
16530 do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then
1654 ! choose top layer first
16550 T_int_W(I,j) = T_top(i,j) ; T_int_E(I,j) = T_top(i+1,j)
16560 S_int_W(I,j) = S_top(i,j) ; S_int_E(I,j) = S_top(i+1,j)
16570 p_int_W(I,j) = -GxRho0*(e(i,j,1) - Z_0p(i,j))
16580 p_int_E(I,j) = -GxRho0*(e(i+1,j,1) - Z_0p(i,j))
16590 intx_pa_nonlin(I,j) = intx_pa(I,j,1) - 0.5*(pa(i,j,1) + pa(i+1,j,1))
16600 dgeo_x(I,j) = GV%g_Earth * (e(i+1,j,1)-e(i,j,1))
16610 delta_z_x(I,j) = abs(e(i+1,j,1)-e(i,j,1))
16620 do k=1,nz
16630 if (abs(e(i+1,j,k+1)-e(i,j,k+1)) < delta_z_x(I,j)) then
1664 ! bottom of layer is less sloped than top. Use this layer
16650 delta_z_x(I,j) = abs(e(i+1,j,k+1)-e(i,j,k+1))
16660 T_int_W(I,j) = T_b(i,j,k) ; T_int_E(I,j) = T_b(i+1,j,k)
16670 S_int_W(I,j) = S_b(i,j,k) ; S_int_E(I,j) = S_b(i+1,j,k)
16680 p_int_W(I,j) = -GxRho0*(e(i,j,K+1) - Z_0p(i,j))
16690 p_int_E(I,j) = -GxRho0*(e(i+1,j,K+1) - Z_0p(i,j))
16700 intx_pa_nonlin(I,j) = intx_pa(I,j,K+1) - 0.5*(pa(i,j,K+1) + pa(i+1,j,K+1))
16710 dgeo_x(I,j) = GV%g_Earth * (e(i+1,j,K+1)-e(i,j,K+1))
1672 endif
1673 enddo
16740 seek_x_cor(I,j) = .false.
1675 endif ; enddo ; enddo
1676 else
1677 ! There are still points where a correction is needed, so use the top interface for lack of a better idea?
16780 do j=js,je ; do I=Isq,Ieq ; if (seek_x_cor(I,j)) then
16790 T_int_W(I,j) = T_top(i,j) ; T_int_E(I,j) = T_top(i+1,j)
16800 S_int_W(I,j) = S_top(i,j) ; S_int_E(I,j) = S_top(i+1,j)
16810 p_int_W(I,j) = -GxRho0*(e(i,j,1) - Z_0p(i,j))
16820 p_int_E(I,j) = -GxRho0*(e(i+1,j,1) - Z_0p(i,j))
16830 intx_pa_nonlin(I,j) = intx_pa(I,j,1) - 0.5*(pa(i,j,1) + pa(i+1,j,1))
16840 dgeo_x(I,j) = GV%g_Earth * (e(i+1,j,1)-e(i,j,1))
16850 seek_x_cor(I,j) = .false.
1686 endif ; enddo ; enddo
1687 endif
1688 endif
1689
16900 do j=js,je
16910 do I=Isq,Ieq
1692 ! This expression assumes that temperature and salinity vary linearly with hieght
1693 ! between the corners of the reference interfaces found above to get a correction to
1694 ! intx_pa that takes nonlinearities in the equation of state into account.
1695 ! It is derived from a 5 point quadrature estimate of the integral with a large-scale
1696 ! linear correction so that the pressures and heights match at the end-points. It turns
1697 ! out that this linear correction cancels out the mid-point density anomaly.
1698 ! This can be used without masking because dgeo_x and intx_pa_nonlin are 0 over land.
16990 T5(1) = T_Int_W(I,j) ; S5(1) = S_Int_W(I,j) ; p5(1) = p_Int_W(I,j)
17000 T5(5) = T_Int_E(I,j) ; S5(5) = S_Int_E(I,j) ; p5(5) = p_Int_E(I,j)
17010 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))
17020 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))
17030 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))
17040 call calculate_density(T5, S5, p5, r5, tv%eqn_of_state, rho_ref=rho_ref)
1705
1706 ! Note the consistency with the linear form below because (4.75 + 5.5/2) / 90 = 1/12
1707 intx_pa_cor_ri(I,j) = C1_90 * (4.75*(r5(5)-r5(1)) + 5.5*(r5(4)-r5(2))) * dgeo_x(I,j) - &
17080 intx_pa_nonlin(I,j)
1709 enddo
1710 enddo
1711
1712 ! Repeat the calculations above for v-velocity points.
17130 T_int_S(:,:) = 0.0 ; S_int_S(:,:) = 0.0 ; p_int_S(:,:) = 0.0
17140 T_int_N(:,:) = 0.0 ; S_int_N(:,:) = 0.0 ; p_int_N(:,:) = 0.0
17150 inty_pa_nonlin(:,:) = 0.0 ; dgeo_y(:,:) = 0.0 ; inty_pa_cor_ri(:,:) = 0.0
17160 do J=Jsq,Jeq ; do i=is,ie
17170 seek_y_cor(i,J) = (G%mask2dCv(i,J) > 0.)
17180 delta_z_y(i,J) = 0.0
1719 enddo ; enddo
1720
17210 do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then
17220 if ((e(i,j+1,2) <= e(i,j,1)) .and. (e(i,j,2) <= e(i,j+1,1))) then
1723 ! This is a typical case in the open ocean, so use the topmost interface.
17240 T_int_S(i,J) = T_top(i,j) ; T_int_N(i,J) = T_top(i,j+1)
17250 S_int_S(i,J) = S_top(i,j) ; S_int_N(i,J) = S_top(i,j+1)
17260 p_int_S(i,J) = -GxRho0*(e(i,j,1) - Z_0p(i,j))
17270 p_int_N(i,J) = -GxRho0*(e(i,j+1,1) - Z_0p(i,j))
17280 inty_pa_nonlin(i,J) = inty_pa(i,J,1) - 0.5*(pa(i,j,1) + pa(i,j+1,1))
17290 dgeo_y(i,J) = GV%g_Earth * (e(i,j+1,1)-e(i,j,1))
17300 seek_y_cor(i,J) = .false.
1731 endif
1732 endif ; enddo ; enddo
1733
17340 do k=1,nz
17350 do_more_k = .false.
17360 do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then
1737 ! Find the topmost layer for which both sides are nonvanished and mass-weighting is not
1738 ! activated in the subgrid interpolation.
17390 if (((h(i,j,k) > CS%h_nonvanished) .and. (h(i,j+1,k) > CS%h_nonvanished)) .and. &
1740 (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
1741 ! Store properties at the bottom of this cell to get a "good estimate" for intypa at
1742 ! the interface below this cell (it might have quadratic pressure dependence if sloped)
17430 T_int_S(i,J) = T_b(i,j,k) ; T_int_N(i,J) = T_b(i,j+1,k)
17440 S_int_S(i,J) = S_b(i,j,k) ; S_int_N(i,J) = S_b(i,j+1,k)
1745 ! These pressures are only used for the equation of state, and are only a function of
1746 ! height, consistent with the expressions in the int_density_dz routines.
17470 p_int_S(i,J) = -GxRho0*(e(i,j,K+1) - Z_0p(i,j))
17480 p_int_N(i,J) = -GxRho0*(e(i,j+1,K+1) - Z_0p(i,j))
17490 inty_pa_nonlin(i,J) = inty_pa(i,J,K+1) - 0.5*(pa(i,j,K+1) + pa(i,j+1,K+1))
17500 dgeo_y(i,J) = GV%g_Earth * (e(i,j+1,K+1)-e(i,j,K+1))
17510 seek_y_cor(i,J) = .false.
1752 else
17530 do_more_k = .true.
1754 endif
1755 endif ; enddo ; enddo
17560 if (.not.do_more_k) exit ! All reference interfaces have been found, so stop working downward.
1757 enddo
1758
17590 if (do_more_k) then
17600 if (CS%reset_intxpa_flattest) then
1761 ! There are still points where a correction is needed, so use flattest interface.
17620 do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then
1763 ! choose top interface first
17640 T_int_S(i,J) = T_top(i,j) ; T_int_N(i,J) = T_top(i,j+1)
17650 S_int_S(i,J) = S_top(i,j) ; S_int_N(i,J) = S_top(i,j+1)
17660 p_int_S(i,J) = -GxRho0*(e(i,j,1) - Z_0p(i,j))
17670 p_int_N(i,J) = -GxRho0*(e(i,j+1,1) - Z_0p(i,j))
17680 inty_pa_nonlin(i,J) = inty_pa(i,J,1) - 0.5*(pa(i,j,1) + pa(i,j+1,1))
17690 dgeo_y(i,J) = GV%g_Earth * (e(i,j+1,1)-e(i,j,1))
17700 delta_z_y(i,J) = abs(e(i,j+1,1)-e(i,j,1))
17710 do k=1,nz
17720 if (abs(e(i,j+1,k+1)-e(i,j,k+1)) < delta_z_y(i,J)) then
1773 ! bottom of layer is less sloped than top. Use this layer
17740 delta_z_y(i,J) = abs(e(i,j+1,k+1)-e(i,j,k+1))
17750 T_int_S(i,J) = T_b(i,j,k) ; T_int_N(i,J) = T_b(i,j+1,k)
17760 S_int_S(i,J) = S_b(i,j,k) ; S_int_N(i,J) = S_b(i,j+1,k)
17770 p_int_S(i,J) = -GxRho0*(e(i,j,k+1) - Z_0p(i,j))
17780 p_int_N(i,J) = -GxRho0*(e(i,j+1,k+1) - Z_0p(i,j))
17790 inty_pa_nonlin(i,J) = inty_pa(i,J,k+1) - 0.5*(pa(i,j,k+1) + pa(i,j+1,k+1))
17800 dgeo_y(i,J) = GV%g_Earth * (e(i,j+1,k+1)-e(i,j,k+1))
1781 endif
1782 enddo
17830 seek_y_cor(i,J) = .false.
1784 endif ; enddo ; enddo
1785 else
1786 ! There are still points where a correction is needed, so use the top interface for lack of a better idea?
17870 do J=Jsq,Jeq ; do i=is,ie ; if (seek_y_cor(i,J)) then
17880 T_int_S(i,J) = T_top(i,j) ; T_int_N(i,J) = T_top(i,j+1)
17890 S_int_S(i,J) = S_top(i,j) ; S_int_N(i,J) = S_top(i,j+1)
17900 p_int_S(i,J) = -GxRho0*(e(i,j,1) - Z_0p(i,j))
17910 p_int_N(i,J) = -GxRho0*(e(i,j+1,1) - Z_0p(i,j))
17920 inty_pa_nonlin(i,J) = inty_pa(i,J,1) - 0.5*(pa(i,j,1) + pa(i,j+1,1))
17930 dgeo_y(i,J) = GV%g_Earth * (e(i,j+1,1)-e(i,j,1))
17940 seek_y_cor(i,J) = .false.
1795 endif ; enddo ; enddo
1796 endif
1797 endif
1798
17990 do J=Jsq,Jeq
18000 do i=is,ie
1801 ! This expression assumes that temperature and salinity vary linearly with hieght
1802 ! between the corners of the reference interfaces found above to get a correction to
1803 ! intx_pa that takes nonlinearities in the equation of state into account.
1804 ! It is derived from a 5 point quadrature estimate of the integral with a large-scale
1805 ! linear correction so that the pressures and heights match at the end-points. It turns
1806 ! out that this linear correction cancels out the mid-point density anomaly.
1807 ! This can be used without masking because dgeo_y and inty_pa_nonlin are 0 over land.
18080 T5(1) = T_Int_S(i,J) ; S5(1) = S_Int_S(i,J) ; p5(1) = p_Int_S(i,J)
18090 T5(5) = T_Int_N(i,J) ; S5(5) = S_Int_N(i,J) ; p5(5) = p_Int_N(i,J)
18100 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))
18110 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))
18120 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))
18130 call calculate_density(T5, S5, p5, r5, tv%eqn_of_state, rho_ref=rho_ref)
1814
1815 ! Note the consistency with the linear form below because (4.75 + 5.5/2) / 90 = 1/12
1816 inty_pa_cor_ri(i,J) = C1_90 * (4.75*(r5(5)-r5(1)) + 5.5*(r5(4)-r5(2))) * dgeo_y(i,J) - &
18170 inty_pa_nonlin(i,J)
1818 enddo
1819 enddo
1820
1821 ! Correct intx_pa and inty_pa at each interface using vertically constant corrections.
18220 do K=1,nz+1 ; do j=js,je ; do I=Isq,Ieq
18230 intx_pa(I,j,K) = intx_pa(I,j,K) + intx_pa_cor_ri(I,j)
1824 enddo ; enddo ; enddo
1825
18260 do K=1,nz+1 ; do J=Jsq,Jeq ; do i=is,ie
18270 inty_pa(i,J,K) = inty_pa(i,J,K) + inty_pa_cor_ri(i,J)
1828 enddo ; enddo ; enddo
1829
1830 ! TODO temporarily move back to CPU
1831 !$omp target update to(intx_pa, inty_pa)
1832 endif ! intx_pa and inty_pa have now been reset to reflect the properties of an unimpeded interface.
1833
1834 ! NOTE: None of these `enter data` statements should be here. They are only
1835 ! temporarily being used to set up the data regions below.
1836 ! Eventually, they should be set up *outside* of the function.
1837
1838 !$omp target enter data if(use_EOS) &
1839 !$omp map(to: tv_tmp, tv_tmp%T, tv_tmp%S, tv, EOSdom2d)
1840
1841 ! NOTE: e_sal condition could be sharpened, but this is close enough.
1842 !$omp target enter data map(to: e_tidal_eq, e_tidal_sal, e_sal_and_tide) if (CS%tides)
1843 !$omp target enter data map(to: e_sal) if (CS%calculate_SAL)
1844
1845 !$omp target data map(to: h)
1846
184724 do concurrent (k=1:nz, j=js:je, I=Isq:Ieq)
1848 PFu(I,j,k) = (((pa(i,j,K)*h(i,j,k) + intz_dpa(i,j,k)) - &
1849 (pa(i+1,j,K)*h(i+1,j,k) + intz_dpa(i+1,j,k))) + &
1850 ((h(i+1,j,k) - h(i,j,k)) * intx_pa(I,j,K) - &
1851 (e(i+1,j,K+1) - e(i,j,K+1)) * intx_dpa(I,j,k) * GV%Z_to_H)) * &
1852 ((2.0*I_Rho0*G%IdxCu(I,j)) / &
185313245168 ((h(i,j,k) + h(i+1,j,k)) + h_neglect))
1854 enddo
1855
1856 ! Compute pressure gradient in y direction
185724 do concurrent (k=1:nz, J=Jsq:Jeq, i=is:ie)
1858 PFv(i,J,k) = (((pa(i,j,K)*h(i,j,k) + intz_dpa(i,j,k)) - &
1859 (pa(i,j+1,K)*h(i,j+1,k) + intz_dpa(i,j+1,k))) + &
1860 ((h(i,j+1,k) - h(i,j,k)) * inty_pa(i,J,K) - &
1861 (e(i,j+1,K+1) - e(i,j,K+1)) * inty_dpa(i,J,k) * GV%Z_to_H)) * &
1862 ((2.0*I_Rho0*G%IdyCv(i,J)) / &
186313354584 ((h(i,j,k) + h(i,j+1,k)) + h_neglect))
1864 enddo
1865
1866 !$omp end target data
1867
186824 if (CS%tides_answer_date>20230630 .and. CS%bq_sal_tides) then
1869 ! Calculate SAL geopotential anomaly and add its gradient to pressure
1870 ! gradient force
18710 if (CS%calculate_SAL) then
18720 do concurrent (k=1:nz, j=js:je, I=Isq:Ieq)
18730 PFu(I,j,k) = PFu(I,j,k) + (e_sal(i+1,j) - e_sal(i,j)) * GV%g_Earth * G%IdxCu(I,j)
1874 enddo
18750 do concurrent (k=1:nz, J=Jsq:Jeq, i=is:ie)
18760 PFv(i,J,k) = PFv(i,J,k) + (e_sal(i,j+1) - e_sal(i,j)) * GV%g_Earth * G%IdyCv(i,J)
1877 enddo
1878 endif
1879
1880 ! Calculate tidal geopotential anomaly and add its gradient to pressure
1881 ! gradient force
18820 if (CS%tides) then
18830 do concurrent (k=1:nz, j=js:je, I=Isq:Ieq)
1884 PFu(I,j,k) = PFu(I,j,k) + ((e_tidal_eq(i+1,j) + e_tidal_sal(i+1,j)) &
18850 - (e_tidal_eq(i,j) + e_tidal_sal(i,j))) * GV%g_Earth * G%IdxCu(I,j)
1886 enddo
18870 do concurrent (k=1:nz, J=Jsq:Jeq, i=is:ie)
1888 PFv(i,J,k) = PFv(i,J,k) + ((e_tidal_eq(i,j+1) + e_tidal_sal(i,j+1)) &
18890 - (e_tidal_eq(i,j) + e_tidal_sal(i,j))) * GV%g_Earth * G%IdyCv(i,J)
1890 enddo
1891 endif
1892 endif
1893
189424 if (CS%GFS_scale < 1.0) then
1895 ! Adjust the Montgomery potential to make this a reduced gravity model.
1896 !$omp target data map(alloc: dM)
1897
18980 if (use_EOS) then
1899 !$omp target data map(alloc: rho_in_situ)
1900
19010 if (use_p_atm) then
1902 call calculate_density(tv_tmp%T(:,:,1), tv_tmp%S(:,:,1), p_atm, rho_in_situ, &
19030 tv%eqn_of_state, EOSdom2d)
1904 else
1905 !$omp target data map(alloc: p0)
1906
1907 !$omp target
1908 !$omp parallel loop collapse(2)
19090 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
19100 p0(i,j) = 0.
1911 enddo ; enddo
1912 !$omp end target
1913
1914 call calculate_density(tv_tmp%T(:,:,1), tv_tmp%S(:,:,1), p0, rho_in_situ, &
19150 tv%eqn_of_state, EOSdom2d)
1916 !$omp end target data
1917 endif
1918
1919 !$omp target
1920 !$omp parallel loop collapse(2)
19210 do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
19220 dM(i,j) = (CS%GFS_scale - 1.0) * (G_Rho0 * rho_in_situ(i,j)) * (e(i,j,1) - G%Z_ref)
1923 enddo ; enddo
1924 !$omp end target
1925
1926 !$omp end target data
1927 else
19280 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
19290 dM(i,j) = (CS%GFS_scale - 1.0) * (G_Rho0 * GV%Rlay(1)) * (e(i,j,1) - G%Z_ref)
1930 enddo
1931 endif
1932
19330 do concurrent (k=1:nz, j=js:je, I=Isq:Ieq)
19340 PFu(I,j,k) = PFu(I,j,k) - (dM(i+1,j) - dM(i,j)) * G%IdxCu(I,j)
1935 enddo
19360 do concurrent (k=1:nz, J=Jsq:Jeq, i=is:ie)
19370 PFv(i,J,k) = PFv(i,J,k) - (dM(i,j+1) - dM(i,j)) * G%IdyCv(i,J)
1938 enddo
1939
1940 !$omp end target data
1941 endif
1942
194324 if (present(pbce)) then
194424 call set_pbce_Bouss(e, tv_tmp, G, GV, US, rho0_set_pbce, CS%GFS_scale, pbce)
1945 endif
1946
1947 !$omp target exit data if (use_EOS) &
1948 !$omp map(delete:tv_tmp, tv_tmp%T, tv_tmp%S, tv, tv%eqn_of_state, EOSdom2d)
1949
1950 !$omp target exit data map(delete: Z_0p) if (use_EOS)
1951
1952 !$omp target exit data &
1953 !$omp map(delete: pa, dpa) &
1954 !$omp map(delete: intx_pa, inty_pa, intx_dpa, inty_dpa, intz_dpa)
1955
195624 if (present(eta)) then
1957 ! eta is the sea surface height relative to a time-invariant geoid, for comparison with
1958 ! what is used for eta in btstep. See how e was calculated about 200 lines above.
19592952 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
1960184488 eta(i,j) = e(i,j,1)*GV%Z_to_H
1961 enddo
1962
196324 if (CS%tides .and. (.not.CS%bq_sal_tides)) then
19640 if (CS%tides_answer_date>20230630) then
19650 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
19660 eta(i,j) = eta(i,j) + (e_tidal_eq(i,j)+e_tidal_sal(i,j))*GV%Z_to_H
1967 enddo
1968 else
19690 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
19700 eta(i,j) = eta(i,j) + e_sal_and_tide(i,j)*GV%Z_to_H
1971 enddo
1972 endif
1973 endif
1974
197524 if (CS%calculate_SAL .and. (CS%tides_answer_date>20230630) .and. (.not.CS%bq_sal_tides)) then
19760 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
19770 eta(i,j) = eta(i,j) + e_sal(i,j)*GV%Z_to_H
1978 enddo
1979 endif
1980 endif
1981
1982 !$omp target exit data map(delete: e)
1983 !$omp target exit data map(delete: e_tidal_eq, e_tidal_sal, e_sal_and_tide) if (CS%tides)
1984 !$omp target exit data map(delete: e_sal) if (CS%calculate_SAL)
1985
198624 if (CS%use_stanley_pgf) then
1987 ! Calculated diagnostics related to the Stanley parameterization
19880 zeros(:) = 0.0
19890 EOSdom_h(:) = EOS_domain(G%HI)
19900 if ((CS%id_p_stanley>0) .or. (CS%id_rho_pgf>0) .or. (CS%id_rho_stanley_pgf>0)) then
1991 ! Find the pressure at the mid-point of each layer.
19920 H_to_RL2_T2 = GV%g_Earth*GV%H_to_RZ
19930 if (use_p_atm) then
19940 do j=js,je ; do i=is,ie
19950 p_stanley(i,j,1) = 0.5*h(i,j,1) * H_to_RL2_T2 + p_atm(i,j)
1996 enddo ; enddo
1997 else
19980 do j=js,je ; do i=is,ie
19990 p_stanley(i,j,1) = 0.5*h(i,j,1) * H_to_RL2_T2
2000 enddo ; enddo
2001 endif
20020 do k=2,nz ; do j=js,je ; do i=is,ie
20030 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
2004 enddo ; enddo ; enddo
2005 endif
20060 if (CS%id_p_stanley>0) call post_data(CS%id_p_stanley, p_stanley, CS%diag)
20070 if (CS%id_rho_pgf>0) then
20080 do k=1,nz ; do j=js,je
2009 call calculate_density(tv%T(:,j,k), tv%S(:,j,k), p_stanley(:,j,k), zeros, &
20100 zeros, zeros, rho_pgf(:,j,k), tv%eqn_of_state, EOSdom_h)
2011 enddo ; enddo
20120 call post_data(CS%id_rho_pgf, rho_pgf, CS%diag)
2013 endif
20140 if (CS%id_rho_stanley_pgf>0) then
20150 do k=1,nz ; do j=js,je
2016 call calculate_density(tv%T(:,j,k), tv%S(:,j,k), p_stanley(:,j,k), tv%varT(:,j,k), &
20170 zeros, zeros, rho_stanley_pgf(:,j,k), tv%eqn_of_state, EOSdom_h)
2018 enddo ; enddo
20190 call post_data(CS%id_rho_stanley_pgf, rho_stanley_pgf, CS%diag)
2020 endif
2021 endif
2022
202324 if (CS%id_MassWt_u>0) call post_data(CS%id_MassWt_u, MassWt_u, CS%diag)
202424 if (CS%id_MassWt_v>0) call post_data(CS%id_MassWt_v, MassWt_v, CS%diag)
2025
202624 if (CS%id_rho_pgf>0) call post_data(CS%id_rho_pgf, rho_pgf, CS%diag)
202724 if (CS%id_rho_stanley_pgf>0) call post_data(CS%id_rho_stanley_pgf, rho_stanley_pgf, CS%diag)
202824 if (CS%id_p_stanley>0) call post_data(CS%id_p_stanley, p_stanley, CS%diag)
2029
2030 ! Diagnostics for tidal forcing and SAL height anomaly
203124 if (CS%id_e_tide>0) then
2032 ! To be consistent with old runs, tidal forcing diagnostic also includes total SAL.
2033 ! New diagnostics are given for each individual field.
20340 if (CS%tides_answer_date>20230630) then ; do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
20350 e_sal_and_tide(i,j) = e_sal(i,j) + e_tidal_eq(i,j) + e_tidal_sal(i,j)
2036 enddo ; enddo ; endif
20370 call post_data(CS%id_e_tide, e_sal_and_tide, CS%diag)
2038 endif
203924 if (CS%id_e_sal>0) call post_data(CS%id_e_sal, e_sal, CS%diag)
204024 if (CS%id_e_tidal_eq>0) call post_data(CS%id_e_tidal_eq, e_tidal_eq, CS%diag)
204124 if (CS%id_e_tidal_sal>0) call post_data(CS%id_e_tidal_sal, e_tidal_sal, CS%diag)
2042
2043 ! Diagnostics for tidal forcing and SAL horizontal gradients
204424 if (CS%calculate_SAL .and. ((associated(ADp%sal_u) .or. associated(ADp%sal_v)))) then
20450 if (CS%tides) then ; do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1
20460 e_sal(i,j) = e_sal(i,j) + e_tidal_sal(i,j)
2047 enddo ; enddo ; endif
20480 if (CS%bq_sal_tides) then
2049 ! sal_u = ( e(i+1) - e(i) ) * g / dx
20500 if (associated(ADp%sal_u)) then ; do k=1,nz ; do j=js,je ; do I=Isq,Ieq
20510 ADp%sal_u(I,j,k) = (e_sal(i+1,j) - e_sal(i,j)) * GV%g_Earth * G%IdxCu(I,j)
2052 enddo ; enddo ; enddo ; endif
20530 if (associated(ADp%sal_v)) then ; do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie
20540 ADp%sal_v(i,J,k) = (e_sal(i,j+1) - e_sal(i,j)) * GV%g_Earth * G%IdyCv(i,J)
2055 enddo ; enddo ; enddo ; endif
2056 else
2057 ! sal_u = ( e(i+1) - e(i) ) * g / dx * (rho(k) / rho0)
20580 if (associated(ADp%sal_u)) then ; do k=1,nz ; do j=js,je ; do I=Isq,Ieq
2059 ADp%sal_u(I,j,k) = (e_sal(i+1,j) - e_sal(i,j)) * G%IdxCu(I,j) * I_Rho0 * &
20600 (2.0 * intx_dpa(I,j,k) * GV%Z_to_H / ((h(i,j,k) + h(i+1,j,k)) + h_neglect) + GxRho_ref)
2061 enddo ; enddo ; enddo ; endif
20620 if (associated(ADp%sal_v)) then ; do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie
2063 ADp%sal_v(i,J,k) = (e_sal(i,j+1) - e_sal(i,j)) * G%IdyCv(i,J) * I_Rho0 * &
20640 (2.0 * inty_dpa(i,J,k) * GV%Z_to_H / ((h(i,j,k) + h(i,j+1,k)) + h_neglect) + GxRho_ref)
2065 enddo ; enddo ; enddo ; endif
2066 endif
20670 if (CS%id_sal_u>0) call post_data(CS%id_sal_u, ADp%sal_u, CS%diag)
20680 if (CS%id_sal_v>0) call post_data(CS%id_sal_v, ADp%sal_v, CS%diag)
2069 endif
2070
207124 if (CS%tides .and. ((associated(ADp%tides_u) .or. associated(ADp%tides_v)))) then
20720 if (CS%bq_sal_tides) then
2073 ! tides_u = ( e(i+1) - e(i) ) * g / dx
20740 if (associated(ADp%tides_u)) then ; do k=1,nz ; do j=js,je ; do I=Isq,Ieq
20750 ADp%tides_u(I,j,k) = (e_tidal_eq(i+1,j) - e_tidal_eq(i,j)) * GV%g_Earth * G%IdxCu(I,j)
2076 enddo ; enddo ; enddo ; endif
20770 if (associated(ADp%tides_v)) then ; do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie
20780 ADp%tides_v(i,J,k) = (e_tidal_eq(i,j+1) - e_tidal_eq(i,j)) * GV%g_Earth * G%IdyCv(i,J)
2079 enddo ; enddo ; enddo ; endif
2080 else
2081 ! tides_u = ( e(i+1) - e(i) ) * g / dx * (rho(k) / rho0)
20820 if (associated(ADp%tides_u)) then ; do k=1,nz ; do j=js,je ; do I=Isq,Ieq
2083 ADp%tides_u(I,j,k) = (e_tidal_eq(i+1,j) - e_tidal_eq(i,j)) * G%IdxCu(I,j) * I_Rho0 * &
20840 (2.0 * intx_dpa(I,j,k) * GV%Z_to_H / ((h(i,j,k) + h(i+1,j,k)) + h_neglect) + GxRho_ref)
2085 enddo ; enddo ; enddo ; endif
20860 if (associated(ADp%tides_v)) then ; do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie
2087 ADp%tides_v(i,J,k) = (e_tidal_eq(i,j+1) - e_tidal_eq(i,j)) * G%IdyCv(i,J) * I_Rho0 * &
20880 (2.0 * inty_dpa(i,J,k) * GV%Z_to_H / ((h(i,j,k) + h(i,j+1,k)) + h_neglect) + GxRho_ref)
2089 enddo ; enddo ; enddo ; endif
2090 endif
20910 if (CS%id_tides_u>0) call post_data(CS%id_tides_u, ADp%tides_u, CS%diag)
20920 if (CS%id_tides_v>0) call post_data(CS%id_tides_v, ADp%tides_v, CS%diag)
2093 endif
209472end subroutine PressureForce_FV_Bouss
2095
2096!> Initializes the finite volume pressure gradient control structure
20971subroutine PressureForce_FV_init(Time, G, GV, US, param_file, diag, CS, ADp, SAL_CSp, tides_CSp)
2098 type(time_type), target, intent(in) :: Time !< Current model time
2099 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
2100 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
2101 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
2102 type(param_file_type), intent(in) :: param_file !< Parameter file handles
2103 type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure
2104 type(PressureForce_FV_CS), intent(inout) :: CS !< Finite volume PGF control structure
2105 type(accel_diag_ptrs), pointer :: ADp !< Acceleration diagnostic pointers
2106 type(SAL_CS), intent(in), target, optional :: SAL_CSp !< SAL control structure
2107 type(tidal_forcing_CS), intent(in), target, optional :: tides_CSp !< Tides control structure
2108
2109 ! Local variables
2110 real :: Stanley_coeff ! Coefficient relating the temperature gradient and sub-gridscale
2111 ! temperature variance [nondim]
2112 integer :: default_answer_date ! Global answer date
2113 logical :: use_temperature ! If true, temperature and salinity are used as state variables.
2114 logical :: use_EOS ! If true, density calculated from T & S using an equation of state.
2115 logical :: useMassWghtInterp ! If true, use near-bottom mass weighting for T and S
2116 logical :: MassWghtInterpTop ! If true, use near-surface mass weighting for T and S under ice shelves
2117 logical :: MassWghtInterp_NonBous_bug ! If true, use a buggy mass weighting when non-Boussinesq
2118 logical :: enable_bugs ! If true, the defaults for recently added bug-fix flags are set to
2119 ! recreate the bugs, or if false bugs are only used if actively selected.
2120 ! This include declares and sets the variable "version".
2121# include "version_variable.h"
2122 character(len=40) :: mdl ! This module's name.
2123 logical :: use_ALE ! If true, use the Vertical Lagrangian Remap algorithm
2124 integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, nz
2125
21261 isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed ; nz = GV%ke
21271 IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB
2128
21291 CS%initialized = .true.
21301 CS%diag => diag ; CS%Time => Time
21311 if (present(tides_CSp)) &
21321 CS%tides_CSp => tides_CSp
21331 if (present(SAL_CSp)) &
21341 CS%SAL_CSp => SAL_CSp
2135
21361 mdl = "MOM_PressureForce_FV"
21371 call log_version(param_file, mdl, version, "")
2138 call get_param(param_file, mdl, "DEBUG", CS%debug, &
2139 "If true, write out verbose debugging data.", &
21401 default=.false., debuggingParam=.true., do_not_log=.true.)
2141 call get_param(param_file, mdl, "RHO_PGF_REF", CS%rho_ref, &
2142 "The reference density that is subtracted off when calculating pressure "//&
2143 "gradient forces. Its inverse is subtracted off of specific volumes when "//&
2144 "in non-Boussinesq mode. The default is RHO_0.", &
21451 units="kg m-3", default=GV%Rho0*US%R_to_kg_m3, scale=US%kg_m3_to_R)
2146 call get_param(param_file, mdl, "ENABLE_BUGS_BY_DEFAULT", enable_bugs, &
21471 default=.true., do_not_log=.true.) ! This is logged from MOM.F90.
2148 call get_param(param_file, mdl, "RHO_PGF_REF_BUG", CS%rho_ref_bug, &
2149 "If true, recover a bug that RHO_0 (the mean seawater density in Boussinesq mode) "//&
2150 "and RHO_PGF_REF (the subtracted reference density in finite volume pressure "//&
2151 "gradient forces) are incorrectly interchanged in several instances in Boussinesq mode.", &
21521 default=enable_bugs)
2153 call get_param(param_file, mdl, "TIDES", CS%tides, &
21541 "If true, apply tidal momentum forcing.", default=.false.)
21551 if (CS%tides) then
2156 call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, &
2157 "This sets the default value for the various _ANSWER_DATE parameters.", &
21580 default=99991231)
2159 call get_param(param_file, mdl, "TIDES_ANSWER_DATE", CS%tides_answer_date, "The vintage of "//&
2160 "self-attraction and loading (SAL) and tidal forcing calculations. Setting "//&
2161 "dates before 20230701 recovers old answers (Boussinesq and non-Boussinesq "//&
2162 "modes) when SAL is part of the tidal forcing calculation. The answer "//&
2163 "difference is only at bit level and due to a reordered summation. Setting "//&
2164 "dates before 20250201 recovers answers (Boussinesq mode) that interface "//&
2165 "heights are modified before pressure force integrals are calculated.", &
21660 default=default_answer_date, do_not_log=(.not.CS%tides))
2167 endif
2168 call get_param(param_file, mdl, "CALCULATE_SAL", CS%calculate_SAL, &
21691 "If true, calculate self-attraction and loading.", default=CS%tides)
21701 if (CS%calculate_SAL) &
2171 call get_param(param_file, '', "SAL_USE_BPA", CS%sal_use_bpa, default=.false., &
21720 do_not_log=.true.)
21731 if ((CS%tides .or. CS%calculate_SAL) .and. GV%Boussinesq) &
2174 call get_param(param_file, mdl, "BOUSSINESQ_SAL_TIDES", CS%bq_sal_tides, "If true, "//&
2175 "in Boussinesq mode, use an alternative method to include self-attraction "//&
2176 "and loading (SAL) and tidal forcings in pressure gradient, in which their "//&
2177 "gradients are calculated separately, instead of adding geopotential "//&
2178 "anomalies as corrections to the interface height. This alternative method "//&
2179 "elimates a baroclinic component of the SAL and tidal forcings.", &
21800 default=.false.)
2181 call get_param(param_file, "MOM", "ENABLE_THERMODYNAMICS", use_temperature, &
2182 "If true, Temperature and salinity are used as state variables.", &
21831 default=.true., do_not_log=.true.)
2184 call get_param(param_file, "MOM", "USE_EOS", use_EOS, &
2185 "If true, density is calculated from temperature and "//&
2186 "salinity with an equation of state. If USE_EOS is "//&
2187 "true, ENABLE_THERMODYNAMICS must be true as well.", &
21881 default=use_temperature, do_not_log=.true.)
2189
2190 call get_param(param_file, mdl, "SSH_IN_EOS_PRESSURE_FOR_PGF", CS%use_SSH_in_Z0p, &
2191 "If true, include contributions from the sea surface height in the height-based "//&
2192 "pressure used in the equation of state calculations for the Boussinesq pressure "//&
2193 "gradient forces, including adjustments for atmospheric or sea-ice pressure.", &
21941 default=.false., do_not_log=.not.GV%Boussinesq)
21951 if (CS%tides .and. CS%tides_answer_date<=20250131 .and. CS%use_SSH_in_Z0p) &
2196 call MOM_error(FATAL, trim(mdl) // ", PressureForce_FV_init: SSH_IN_EOS_PRESSURE_FOR_PGF "//&
21970 "needs to be FALSE to recover tide answers before 20250131.")
2198
2199 call get_param(param_file, "MOM", "USE_REGRIDDING", use_ALE, &
2200 "If True, use the ALE algorithm (regridding/remapping). "//&
22011 "If False, use the layered isopycnal algorithm.", default=.false. )
2202 call get_param(param_file, mdl, "MASS_WEIGHT_IN_PRESSURE_GRADIENT", useMassWghtInterp, &
2203 "If true, use mass weighting when interpolating T/S for integrals "//&
2204 "near the bathymetry in FV pressure gradient calculations.", &
22051 default=.false.)
2206 call get_param(param_file, mdl, "MASS_WEIGHT_IN_PRESSURE_GRADIENT_TOP", MassWghtInterpTop, &
2207 "If true and MASS_WEIGHT_IN_PRESSURE_GRADIENT is true, use mass weighting when "//&
2208 "interpolating T/S for integrals near the top of the water column in FV "//&
2209 "pressure gradient calculations. ", &
22101 default=useMassWghtInterp)
2211 call get_param(param_file, mdl, "MASS_WEIGHT_IN_PGF_NONBOUS_BUG", MassWghtInterp_NonBous_bug, &
2212 "If true, use a masking bug in non-Boussinesq calculations with mass weighting "//&
2213 "when interpolating T/S for integrals near the bathymetry in FV pressure "//&
2214 "gradient calculations.", &
22151 default=.false., do_not_log=(GV%Boussinesq .or. (.not.useMassWghtInterp)))
2216 call get_param(param_file, mdl, "MASS_WEIGHT_IN_PGF_VANISHED_ONLY", CS%MassWghtInterpVanOnly, &
2217 "If true, use mass weighting when interpolating T/S for integrals "//&
2218 "only if one side is vanished according to RESET_INTXPA_H_NONVANISHED. ", &
22191 default=.false.)
2220
22211 CS%MassWghtInterp = 0
22221 if (useMassWghtInterp) &
22231 CS%MassWghtInterp = ibset(CS%MassWghtInterp, 0) ! Same as CS%MassWghtInterp + 1
22241 if (MassWghtInterpTop) &
22251 CS%MassWghtInterp = ibset(CS%MassWghtInterp, 1) ! Same as CS%MassWghtInterp + 2
22261 if ((.not.GV%Boussinesq) .and. MassWghtInterp_NonBous_bug) &
22270 CS%MassWghtInterp = ibset(CS%MassWghtInterp, 3) ! Same as CS%MassWghtInterp + 8
2228
2229 call get_param(param_file, mdl, "CORRECTION_INTXPA", CS%correction_intxpa, &
2230 "If true, use a correction for surface pressure curvature in intx_pa.", &
22311 default=.false., do_not_log=.not.use_EOS)
2232 call get_param(param_file, mdl, "RESET_INTXPA_INTEGRAL", CS%reset_intxpa_integral, &
2233 "If true, reset INTXPA to match pressures at first nonvanished cell. "//&
22341 "Includes pressure correction.", default=.false., do_not_log=.not.use_EOS)
2235 call get_param(param_file, mdl, "RESET_INTXPA_INTEGRAL_FLATTEST", CS%reset_intxpa_flattest, &
2236 "If true, use flattest interface as reference interface where there is no "//&
2237 "better choice for RESET_INTXPA_INTEGRAL. Otherwise, use surface interface.", &
22381 default=.false., do_not_log=.not.use_EOS)
22391 if (.not.use_EOS) then ! These options do nothing without an equation of state.
22400 CS%correction_intxpa = .false.
22410 CS%reset_intxpa_integral = .false.
22420 CS%reset_intxpa_flattest = .false.
2243 endif
2244 call get_param(param_file, mdl, "RESET_INTXPA_H_NONVANISHED", CS%h_nonvanished, &
2245 "A minimal layer thickness that indicates that a layer is thick enough to usefully "//&
2246 "reestimate the pressure integral across the interface below.", &
22471 default=1.0e-6, units="m", scale=GV%m_to_H, do_not_log=.not.CS%reset_intxpa_integral)
2248 call get_param(param_file, mdl, "USE_INACCURATE_PGF_RHO_ANOM", CS%use_inaccurate_pgf_rho_anom, &
2249 "If true, use a form of the PGF that uses the reference density "//&
22501 "in an inaccurate way. This is not recommended.", default=.false.)
2251 call get_param(param_file, mdl, "RECONSTRUCT_FOR_PRESSURE", CS%reconstruct, &
2252 "If True, use vertical reconstruction of T & S within "//&
2253 "the integrals of the FV pressure gradient calculation. "//&
2254 "If False, use the constant-by-layer algorithm. "//&
2255 "The default is set by USE_REGRIDDING.", &
22561 default=use_ALE )
2257 call get_param(param_file, mdl, "PRESSURE_RECONSTRUCTION_SCHEME", CS%Recon_Scheme, &
2258 "Order of vertical reconstruction of T/S to use in the "//&
2259 "integrals within the FV pressure gradient calculation.\n"//&
2260 " 0: PCM or no reconstruction.\n"//&
2261 " 1: PLM reconstruction.\n"//&
2262 " 2: PPM reconstruction.\n"//&
22631 " 3: PLM with least squares slope.", default=1)
2264 call get_param(param_file, mdl, "BOUNDARY_EXTRAPOLATION_PRESSURE", CS%boundary_extrap, &
2265 "If true, the reconstruction of T & S for pressure in "//&
2266 "boundary cells is extrapolated, rather than using PCM "//&
2267 "in these cells. If true, the same order polynomial is "//&
22681 "used as is used for the interior cells.", default=.true.)
2269 call get_param(param_file, mdl, "USE_STANLEY_PGF", CS%use_stanley_pgf, &
2270 "If true, turn on Stanley SGS T variance parameterization "// &
22711 "in PGF code.", default=.false.)
22721 if (CS%use_stanley_pgf) then
2273 call get_param(param_file, mdl, "STANLEY_COEFF", Stanley_coeff, &
2274 "Coefficient correlating the temperature gradient and SGS T variance.", &
22750 units="nondim", default=-1.0, do_not_log=.true.)
22760 if (Stanley_coeff < 0.0) call MOM_error(FATAL, &
22770 "STANLEY_COEFF must be set >= 0 if USE_STANLEY_PGF is true.")
2278
2279 CS%id_rho_pgf = register_diag_field('ocean_model', 'rho_pgf', diag%axesTL, &
22800 Time, 'rho in PGF', 'kg m-3', conversion=US%R_to_kg_m3)
2281 CS%id_rho_stanley_pgf = register_diag_field('ocean_model', 'rho_stanley_pgf', diag%axesTL, &
22820 Time, 'rho in PGF with Stanley correction', 'kg m-3', conversion=US%R_to_kg_m3)
2283 CS%id_p_stanley = register_diag_field('ocean_model', 'p_stanley', diag%axesTL, &
22840 Time, 'p in PGF with Stanley correction', 'Pa', conversion=US%RL2_T2_to_Pa)
2285 endif
22861 if (CS%calculate_SAL) then
2287 CS%id_e_sal = register_diag_field('ocean_model', 'e_sal', diag%axesT1, Time, &
22880 'Self-attraction and loading height anomaly', 'meter', conversion=US%Z_to_m)
2289 CS%id_sal_u = register_diag_field('ocean_model', 'SAL_u', diag%axesCuL, Time, &
22900 'Zonal Acceleration due to self-attraction and loading', 'm s-2', conversion=US%L_T2_to_m_s2)
2291 CS%id_sal_v = register_diag_field('ocean_model', 'SAL_v', diag%axesCvL, Time, &
22920 'Meridional Acceleration due to self-attraction and loading', 'm s-2', conversion=US%L_T2_to_m_s2)
22930 if (CS%id_sal_u > 0) &
22940 call safe_alloc_ptr(ADp%sal_u, IsdB, IedB, jsd, jed, nz)
22950 if (CS%id_sal_v > 0) &
22960 call safe_alloc_ptr(ADp%sal_v, isd, ied, JsdB, JedB, nz)
2297 endif
22981 if (CS%tides) then
2299 CS%id_e_tide = register_diag_field('ocean_model', 'e_tidal', diag%axesT1, Time, &
23000 'Tidal Forcing Astronomical and SAL Height Anomaly', 'meter', conversion=US%Z_to_m)
2301 CS%id_e_tidal_eq = register_diag_field('ocean_model', 'e_tide_eq', diag%axesT1, Time, &
23020 'Equilibrium tides height anomaly', 'meter', conversion=US%Z_to_m)
2303 CS%id_e_tidal_sal = register_diag_field('ocean_model', 'e_tide_sal', diag%axesT1, Time, &
23040 'Read-in tidal self-attraction and loading height anomaly', 'meter', conversion=US%Z_to_m)
2305 CS%id_tides_u = register_diag_field('ocean_model', 'tides_u', diag%axesCuL, Time, &
23060 'Zonal Acceleration due to tidal forcing', 'm s-2', conversion=US%L_T2_to_m_s2)
2307 CS%id_tides_v = register_diag_field('ocean_model', 'tides_v', diag%axesCvL, Time, &
23080 'Meridional Acceleration due to tidal forcing', 'm s-2', conversion=US%L_T2_to_m_s2)
23090 if (CS%id_tides_u > 0) &
23100 call safe_alloc_ptr(ADp%tides_u, IsdB, IedB, jsd, jed, nz)
23110 if (CS%id_tides_v > 0) &
23120 call safe_alloc_ptr(ADp%tides_v, isd, ied, JsdB, JedB, nz)
2313 endif
2314
2315 CS%id_MassWt_u = register_diag_field('ocean_model', 'MassWt_u', diag%axesCuL, Time, &
23161 'The fractional mass weighting at u-point PGF calculations', 'nondim')
2317 CS%id_MassWt_v = register_diag_field('ocean_model', 'MassWt_v', diag%axesCvL, Time, &
23181 'The fractional mass weighting at v-point PGF calculations', 'nondim')
2319
23201 CS%GFS_scale = 1.0
23211 if (GV%g_prime(1) /= GV%g_Earth) CS%GFS_scale = GV%g_prime(1) / GV%g_Earth
2322
2323 !$omp target update to (CS)
2324
23251 call log_param(param_file, mdl, "GFS / G_EARTH", CS%GFS_scale, units="nondim")
2326
23271end subroutine PressureForce_FV_init
2328
2329!> \namespace mom_pressureforce_fv
2330!!
2331!! Provides the Boussinesq and non-Boussinesq forms of horizontal accelerations
2332!! due to pressure gradients using a vertically integrated finite volume form,
2333!! as described by Adcroft et al., 2008. Integration in the vertical is made
2334!! either by quadrature or analytically.
2335!!
2336!! This form eliminates the thermobaric instabilities that had been a problem with
2337!! previous forms of the pressure gradient force calculation, as described by
2338!! Hallberg, 2005.
2339!!
2340!! Adcroft, A., R. Hallberg, and M. Harrison, 2008: A finite volume discretization
2341!! of the pressure gradient force using analytic integration. Ocean Modelling, 22,
2342!! 106-113. http://doi.org/10.1016/j.ocemod.2008.02.001
2343!!
2344!! Hallberg, 2005: A thermobaric instability of Lagrangian vertical coordinate
2345!! ocean models. Ocean Modelling, 8, 279-300.
2346!! http://dx.doi.org/10.1016/j.ocemod.2004.01.001
2347
23480end module MOM_PressureForce_FV