← back to index

src/parameterizations/lateral/MOM_thickness_diffuse.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!> Isopycnal height diffusion (or Gent McWilliams diffusion)
6module MOM_thickness_diffuse
7
8use MOM_debugging, only : hchksum, uvchksum
9use MOM_diag_mediator, only : post_data, query_averaging_enabled, diag_ctrl
10use MOM_diag_mediator, only : register_diag_field, safe_alloc_ptr, time_type
11use MOM_diag_mediator, only : diag_update_remap_grids
12use MOM_domains, only : pass_var, CORNER, pass_vector
13use MOM_error_handler, only : MOM_error, FATAL, WARNING, is_root_pe
14use MOM_EOS, only : calculate_density, calculate_density_derivs, EOS_domain
15use MOM_EOS, only : calculate_density_second_derivs
16use MOM_file_parser, only : get_param, log_version, param_file_type
17use MOM_grid, only : ocean_grid_type
18use MOM_io, only : MOM_read_data, slasher
19use MOM_interface_heights, only : find_eta, thickness_to_dz
20use MOM_isopycnal_slopes, only : vert_fill_TS
21use MOM_lateral_mixing_coeffs, only : VarMix_CS
22use MOM_MEKE_types, only : MEKE_type
23use MOM_stochastics, only : stochastic_CS
24use MOM_unit_scaling, only : unit_scale_type
25use MOM_variables, only : thermo_var_ptrs, cont_diag_ptrs
26use MOM_verticalGrid, only : verticalGrid_type
27implicit none ; private
28
29#include <MOM_memory.h>
30
31public thickness_diffuse, thickness_diffuse_init, thickness_diffuse_end
32public thickness_diffuse_get_KH
33
34! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional
35! consistency testing. These are noted in comments with units like Z, H, L, and T, along with
36! their mks counterparts with notation like "a velocity [Z T-1 ~> m s-1]". If the units
37! vary with the Boussinesq approximation, the Boussinesq variant is given first.
38
39!> Control structure for thickness_diffuse
40type, public :: thickness_diffuse_CS ; private
41 logical :: initialized = .false. !< True if this control structure has been initialized.
42 real :: Khth !< Background isopycnal depth diffusivity [L2 T-1 ~> m2 s-1]
43 real :: Khth_Slope_Cff !< Slope dependence coefficient of Khth [nondim]
44 real :: max_Khth_CFL !< Maximum value of the diffusive CFL for isopycnal height diffusion [nondim]
45 real :: Khth_Min !< Minimum value of Khth [L2 T-1 ~> m2 s-1]
46 real :: Khth_Max !< Maximum value of Khth [L2 T-1 ~> m2 s-1], or 0 for no max
47 real :: Kh_eta_bg !< Background isopycnal height diffusivity [L2 T-1 ~> m2 s-1]
48 real :: Kh_eta_vel !< Velocity scale that is multiplied by the grid spacing to give
49 !! the isopycnal height diffusivity [L T-1 ~> m s-1]
50 real :: slope_max !< Slopes steeper than slope_max are limited in some way [Z L-1 ~> nondim]
51 real :: kappa_smooth !< Vertical diffusivity used to interpolate more sensible values
52 !! of T & S into thin layers [H Z T-1 ~> m2 s-1 or kg m-1 s-1]
53 logical :: thickness_diffuse !< If true, interfaces heights are diffused.
54 logical :: full_depth_khth_min !< If true, KHTH_MIN is enforced throughout the whole water column.
55 !! Otherwise, KHTH_MIN is only enforced at the surface. This parameter
56 !! is only available when KHTH_USE_EBT_STRUCT=True and KHTH_MIN>0.
57 logical :: use_FGNV_streamfn !< If true, use the streamfunction formulation of
58 !! Ferrari et al., 2010, which effectively emphasizes
59 !! graver vertical modes by smoothing in the vertical.
60 real :: FGNV_scale !< A coefficient scaling the vertical smoothing term in the
61 !! Ferrari et al., 2010, streamfunction formulation [nondim].
62 real :: FGNV_c_min !< A minimum wave speed used in the Ferrari et al., 2010,
63 !! streamfunction formulation [L T-1 ~> m s-1].
64 real :: N2_floor !< A floor for squared buoyancy frequency in the Ferrari et al., 2010,
65 !! streamfunction formulation divided by aspect ratio rescaling factors
66 !! [L2 Z-2 T-2 ~> s-2].
67 logical :: detangle_interfaces !< If true, add 3-d structured interface height
68 !! diffusivities to horizontally smooth jagged layers.
69 real :: detangle_time !< If detangle_interfaces is true, this is the
70 !! timescale over which maximally jagged grid-scale
71 !! thickness variations are suppressed [T ~> s]. This must be
72 !! longer than DT, or 0 (the default) to use DT.
73 integer :: nkml !< number of layers within mixed layer
74 logical :: debug !< write verbose checksums for debugging purposes
75 logical :: use_GME_thickness_diffuse !< If true, passes GM coefficients to MOM_hor_visc for use
76 !! with GME closure.
77 logical :: MEKE_GEOMETRIC !< If true, uses the GM coefficient formulation from the GEOMETRIC
78 !! framework (Marshall et al., 2012)
79 real :: MEKE_GEOMETRIC_alpha!< The nondimensional coefficient governing the efficiency of
80 !! the GEOMETRIC isopycnal height diffusion [nondim]
81 real :: MEKE_GEOMETRIC_epsilon !< Minimum Eady growth rate for the GEOMETRIC thickness
82 !! diffusivity [T-1 ~> s-1].
83 integer :: MEKE_GEOM_answer_date !< The vintage of the expressions in the MEKE_GEOMETRIC
84 !! calculation. Values below 20190101 recover the answers from the
85 !! original implementation, while higher values use expressions that
86 !! satisfy rotational symmetry.
87 logical :: Use_KH_in_MEKE !< If true, uses the isopycnal height diffusivity calculated here to diffuse MEKE.
88 real :: MEKE_min_depth_diff !< The minimum total depth over which to average the diffusivity
89 !! used for MEKE [H ~> m or kg m-2]. When the total depth is less
90 !! than this, the diffusivity is scaled away.
91 logical :: GM_src_alt !< If true, use the GM energy conversion form S^2*N^2*kappa rather
92 !! than the streamfunction for the GM source term for MEKE.
93 integer :: MEKE_src_answer_date !< The vintage of the expressions in the GM energy conversion
94 !! calculation when MEKE_GM_SRC_ALT is true. Values below 20240601
95 !! recover the answers from the original implementation, while higher
96 !! values use expressions that satisfy rotational symmetry.
97 logical :: MEKE_src_slope_bug !< If true, use a bug that limits the positive values, but not the
98 !! negative values, of the slopes used when MEKE_GM_SRC_ALT is true.
99 !! When this is true, it breaks rotational symmetry.
100 logical :: use_GM_work_bug !< If true, use the incorrect sign for the
101 !! top-level work tendency on the top layer.
102 real :: Stanley_det_coeff !< The coefficient correlating SGS temperature variance with the mean
103 !! temperature gradient in the deterministic part of the Stanley parameterization.
104 !! Negative values disable the scheme. [nondim]
105 logical :: read_khth !< If true, read a file containing the spatially varying horizontal
106 !! isopycnal height diffusivity
107 logical :: use_stanley_gm !< If true, also use the Stanley parameterization in MOM_thickness_diffuse
108
109 type(diag_ctrl), pointer :: diag => NULL() !< structure used to regulate timing of diagnostics
110 real, allocatable :: GMwork(:,:) !< Work by isopycnal height diffusion [R Z L2 T-3 ~> W m-2]
111 real, allocatable :: diagSlopeX(:,:,:) !< Diagnostic: zonal neutral slope [Z L-1 ~> nondim]
112 real, allocatable :: diagSlopeY(:,:,:) !< Diagnostic: zonal neutral slope [Z L-1 ~> nondim]
113
114 real, allocatable :: Kh_eta_u(:,:) !< Isopycnal height diffusivities at u points [L2 T-1 ~> m2 s-1]
115 real, allocatable :: Kh_eta_v(:,:) !< Isopycnal height diffusivities in v points [L2 T-1 ~> m2 s-1]
116
117 real, allocatable :: KH_u_GME(:,:,:) !< Isopycnal height diffusivities in u-columns [L2 T-1 ~> m2 s-1]
118 real, allocatable :: KH_v_GME(:,:,:) !< Isopycnal height diffusivities in v-columns [L2 T-1 ~> m2 s-1]
119 real, allocatable :: khth2d(:,:) !< 2D isopycnal height diffusivity at h-points [L2 T-1 ~> m2 s-1]
120
121 !>@{
122 !! Diagnostic identifier
123 integer :: id_uhGM = -1, id_vhGM = -1, id_GMwork = -1
124 integer :: id_KH_u = -1, id_KH_v = -1, id_KH_t = -1
125 integer :: id_KH_u1 = -1, id_KH_v1 = -1, id_KH_t1 = -1
126 integer :: id_slope_x = -1, id_slope_y = -1
127 integer :: id_sfn_unlim_x = -1, id_sfn_unlim_y = -1, id_sfn_x = -1, id_sfn_y = -1
128 !>@}
129end type thickness_diffuse_CS
130
131contains
132
133!> Calculates isopycnal height diffusion coefficients and applies isopycnal height diffusion
134!! by modifying to the layer thicknesses, h. Diffusivities are limited to ensure stability.
135!! Also returns along-layer mass fluxes used in the continuity equation.
13612subroutine thickness_diffuse(h, uhtr, vhtr, tv, dt, G, GV, US, MEKE, VarMix, CDp, CS, STOCH)
137 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
138 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
139 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
140 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: h !< Layer thickness [H ~> m or kg m-2]
141 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(inout) :: uhtr !< Accumulated zonal mass flux
142 !! [L2 H ~> m3 or kg]
143 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(inout) :: vhtr !< Accumulated meridional mass flux
144 !! [L2 H ~> m3 or kg]
145 type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamics structure
146 real, intent(in) :: dt !< Time increment [T ~> s]
147 type(MEKE_type), intent(inout) :: MEKE !< MEKE fields
148 type(VarMix_CS), target, intent(in) :: VarMix !< Variable mixing coefficients
149 type(cont_diag_ptrs), intent(inout) :: CDp !< Diagnostics for the continuity equation
150 type(thickness_diffuse_CS), intent(inout) :: CS !< Control structure for thickness_diffuse
151 type(stochastic_CS), intent(inout) :: STOCH !< Stochastic control structure
152 ! Local variables
15312 real :: e(SZI_(G),SZJ_(G),SZK_(GV)+1) ! heights of interfaces, relative to mean
154 ! sea level [Z ~> m], positive up.
15524 real :: uhD(SZIB_(G),SZJ_(G),SZK_(GV)) ! Diffusive u*h fluxes [L2 H T-1 ~> m3 s-1 or kg s-1]
15624 real :: vhD(SZI_(G),SZJB_(G),SZK_(GV)) ! Diffusive v*h fluxes [L2 H T-1 ~> m3 s-1 or kg s-1]
157
158 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1) :: &
15924 KH_u, & ! Isopycnal height diffusivities in u-columns [L2 T-1 ~> m2 s-1]
16024 int_slope_u ! A nondimensional ratio from 0 to 1 that gives the relative
161 ! weighting of the interface slopes to that calculated also
162 ! using density gradients at u points. The physically correct
163 ! slopes occur at 0, while 1 is used for numerical closures [nondim].
164 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1) :: &
16524 KH_v, & ! Isopycnal height diffusivities in v-columns [L2 T-1 ~> m2 s-1]
16624 int_slope_v ! A nondimensional ratio from 0 to 1 that gives the relative
167 ! weighting of the interface slopes to that calculated also
168 ! using density gradients at v points. The physically correct
169 ! slopes occur at 0, while 1 is used for numerical closures [nondim].
170 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: &
17124 KH_t ! diagnosed diffusivity at tracer points [L2 T-1 ~> m2 s-1]
172
173 real, dimension(SZIB_(G),SZJ_(G)) :: &
17424 KH_u_CFL ! The maximum stable isopycnal height diffusivity at u grid points [L2 T-1 ~> m2 s-1]
175 real, dimension(SZI_(G),SZJB_(G)) :: &
17624 KH_v_CFL ! The maximum stable isopycnal height diffusivity at v grid points [L2 T-1 ~> m2 s-1]
177 real, dimension(SZI_(G),SZJ_(G)) :: &
17824 htot ! The sum of the total layer thicknesses [H ~> m or kg m-2]
17924 real :: Khth_Loc_u(SZIB_(G),SZJ_(G)) ! The isopycnal height diffusivity at u points [L2 T-1 ~> m2 s-1]
18024 real :: Khth_Loc_v(SZI_(G),SZJB_(G)) ! The isopycnal height diffusivity at v points [L2 T-1 ~> m2 s-1]
181 real :: h_neglect ! A thickness that is so small it is usually lost
182 ! in roundoff and can be neglected [H ~> m or kg m-2].
183 real, dimension(:,:), pointer :: cg1 => null() !< Wave speed [L T-1 ~> m s-1]
18424 real :: hu(SZI_(G),SZJ_(G)) ! A thickness-based mask at u points, used for diagnostics [nondim]
18524 real :: hv(SZI_(G),SZJ_(G)) ! A thickness-based mask at v points, used for diagnostics [nondim]
18624 real :: KH_u_lay(SZI_(G),SZJ_(G)) ! Diagnostic of isopycnal height diffusivities at u-points averaged
187 ! to layer centers [L2 T-1 ~> m2 s-1]
18812 real :: KH_v_lay(SZI_(G),SZJ_(G)) ! Diagnostic of isopycnal height diffusivities at v-points averaged
189 ! to layer centers [L2 T-1 ~> m2 s-1]
190 logical :: use_VarMix, Resoln_scaled, Depth_scaled, use_stored_slopes, khth_use_vert_struct, use_Visbeck
191 logical :: use_QG_Leith
192 integer :: i, j, k, is, ie, js, je, nz
193
19412 if (.not. CS%initialized) call MOM_error(FATAL, "MOM_thickness_diffuse: "//&
1950 "Module must be initialized before it is used.")
196
197 if ((.not.CS%thickness_diffuse) &
19812 .or. .not. (CS%Khth > 0.0 .or. CS%read_khth &
1990 .or. VarMix%use_variable_mixing)) return
200
20112 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
20212 h_neglect = GV%H_subroundoff
203
20412 if (allocated(MEKE%GM_src)) then
20587132 do j=js,je ; do i=is,ie ; MEKE%GM_src(i,j) = 0. ; enddo ; enddo
206 endif
207
20812 use_VarMix = .false. ; Resoln_scaled = .false. ; use_stored_slopes = .false.
20912 khth_use_vert_struct = .false. ; use_Visbeck = .false. ; use_QG_Leith = .false.
21012 Depth_scaled = .false.
211
21212 if (VarMix%use_variable_mixing) then
21312 use_VarMix = VarMix%use_variable_mixing .and. (CS%KHTH_Slope_Cff > 0.)
21412 Resoln_scaled = VarMix%Resoln_scaled_KhTh
21512 Depth_scaled = VarMix%Depth_scaled_KhTh
21612 use_stored_slopes = VarMix%use_stored_slopes
21712 khth_use_vert_struct = allocated(VarMix%khth_struct)
21812 use_Visbeck = VarMix%use_Visbeck
21912 use_QG_Leith = VarMix%use_QG_Leith_GM
22012 if (allocated(VarMix%cg1)) cg1 => VarMix%cg1
221 else
2220 cg1 => null()
223 endif
224
225
226 !$OMP parallel do default(shared)
22787852 do j=js,je ; do I=is-1,ie
228 KH_u_CFL(I,j) = (0.25*CS%max_Khth_CFL) / &
22987840 (dt * ((G%IdxCu(I,j)*G%IdxCu(I,j)) + (G%IdyCu(I,j)*G%IdyCu(I,j))))
230 enddo ; enddo
231 !$OMP parallel do default(shared)
23288584 do J=js-1,je ; do i=is,ie
233 KH_v_CFL(i,J) = (0.25*CS%max_Khth_CFL) / &
23488572 (dt * ((G%IdxCv(i,J)*G%IdxCv(i,J)) + (G%IdyCv(i,J)*G%IdyCv(i,J))))
235 enddo ; enddo
236
237 ! Calculates interface heights, e, in [Z ~> m].
238 !$omp target update to(h)
239 !$omp target enter data map(alloc: e)
24012 call find_eta(h, tv, G, GV, US, e, halo_size=1)
241 !$omp target exit data map(from: e)
242
243 ! Set the diffusivities.
244 !$OMP parallel default(shared)
24512 if (.not. CS%read_khth) then
246 !$OMP do
24787852 do j=js,je ; do I=is-1,ie
24887840 Khth_loc_u(I,j) = CS%Khth
249 enddo ; enddo
250 else ! use 2d KHTH that was read in from file
251 !$OMP do
2520 do j=js,je ; do I=is-1,ie
2530 Khth_loc_u(I,j) = 0.5 * (CS%khth2d(i,j) + CS%khth2d(i+1,j))
254 enddo ; enddo
255 endif
256
25712 if (use_VarMix) then
2580 if (use_Visbeck) then
259 !$OMP do
2600 do j=js,je ; do I=is-1,ie
261 Khth_loc_u(I,j) = Khth_loc_u(I,j) + &
2620 CS%KHTH_Slope_Cff*VarMix%L2u(I,j) * VarMix%SN_u(I,j)
263 enddo ; enddo
264 endif
265 endif
266
26712 if (allocated(MEKE%Kh)) then
26812 if (CS%MEKE_GEOMETRIC) then
269 !$OMP do
2700 do j=js,je ; do I=is-1,ie
271 Khth_loc_u(I,j) = Khth_loc_u(I,j) + G%OBCmaskCu(I,j) * CS%MEKE_GEOMETRIC_alpha * &
272 0.5*(MEKE%MEKE(i,j)+MEKE%MEKE(i+1,j)) / &
2730 (VarMix%SN_u(I,j) + CS%MEKE_GEOMETRIC_epsilon)
274 enddo ; enddo
275 else
27687852 do j=js,je ; do I=is-1,ie
27787840 Khth_loc_u(I,j) = Khth_loc_u(I,j) + MEKE%KhTh_fac*sqrt(MEKE%Kh(i,j)*MEKE%Kh(i+1,j))
278 enddo ; enddo
279 endif
280 endif
281
28212 if (Resoln_scaled) then
283 !$OMP do
28487852 do j=js,je ; do I=is-1,ie
28587840 Khth_loc_u(I,j) = Khth_loc_u(I,j) * VarMix%Res_fn_u(I,j)
286 enddo ; enddo
287 endif
288
28912 if (Depth_scaled) then
290 !$OMP do
2910 do j=js,je ; do I=is-1,ie
2920 Khth_loc_u(I,j) = Khth_loc_u(I,j) * VarMix%Depth_fn_u(I,j)
293 enddo ; enddo
294 endif
295
29612 if (CS%Khth_Max > 0) then
297 !$OMP do
2980 do j=js,je ; do I=is-1,ie
2990 Khth_loc_u(I,j) = max(CS%Khth_Min, min(Khth_loc_u(I,j), CS%Khth_Max))
300 enddo ; enddo
301 else
302 !$OMP do
30387852 do j=js,je ; do I=is-1,ie
30487840 Khth_loc_u(I,j) = max(CS%Khth_Min, Khth_loc_u(I,j))
305 enddo ; enddo
306 endif
307 !$OMP do
30887852 do j=js,je ; do I=is-1,ie
30987840 KH_u(I,j,1) = min(KH_u_CFL(I,j), Khth_loc_u(I,j))
310 enddo ; enddo
311
31212 if (khth_use_vert_struct) then
3130 if (CS%full_depth_khth_min) then
314 !$OMP do
3150 do K=2,nz+1 ; do j=js,je ; do I=is-1,ie
3160 KH_u(I,j,K) = KH_u(I,j,1) * 0.5 * ( VarMix%khth_struct(i,j,k-1) + VarMix%khth_struct(i+1,j,k-1) )
3170 KH_u(I,j,K) = max(KH_u(I,j,K), CS%Khth_Min)
318 enddo ; enddo ; enddo
319 else
320 !$OMP do
3210 do K=2,nz+1 ; do j=js,je ; do I=is-1,ie
3220 KH_u(I,j,K) = KH_u(I,j,1) * 0.5 * ( VarMix%khth_struct(i,j,k-1) + VarMix%khth_struct(i+1,j,k-1) )
323 enddo ; enddo ; enddo
324 endif
325 else
326 !$OMP do
3276588912 do K=2,nz+1 ; do j=js,je ; do I=is-1,ie
3286588000 KH_u(I,j,K) = KH_u(I,j,1)
329 enddo ; enddo ; enddo
330 endif
331
33212 if (use_VarMix) then
3330 if (use_QG_Leith) then
334 !$OMP do
3350 do k=1,nz ; do j=js,je ; do I=is-1,ie
3360 KH_u(I,j,k) = VarMix%KH_u_QG(I,j,k)
337 enddo ; enddo ; enddo
338 endif
339 endif
340
34112 if (CS%use_GME_thickness_diffuse) then
342 !$OMP do
3430 do k=1,nz+1 ; do j=js,je ; do I=is-1,ie
3440 CS%KH_u_GME(I,j,k) = KH_u(I,j,k)
345 enddo ; enddo ; enddo
346 endif
347
34812 if (.not. CS%read_khth) then
349 !$OMP do
35088584 do J=js-1,je ; do i=is,ie
35188572 Khth_loc_v(i,J) = CS%Khth
352 enddo ; enddo
353 else ! read KHTH from file
354 !$OMP do
3550 do J=js-1,je ; do i=is,ie
3560 Khth_loc_v(i,J) = 0.5 * (CS%khth2d(i,j) + CS%khth2d(i,j+1))
357 enddo ; enddo
358 endif
359
36012 if (use_VarMix) then
3610 if (use_Visbeck) then
362 !$OMP do
3630 do J=js-1,je ; do i=is,ie
3640 Khth_loc_v(i,J) = Khth_loc_v(i,J) + CS%KHTH_Slope_Cff*VarMix%L2v(i,J)*VarMix%SN_v(i,J)
365 enddo ; enddo
366 endif
367 endif
36812 if (allocated(MEKE%Kh)) then
36912 if (CS%MEKE_GEOMETRIC) then
370 !$OMP do
3710 do J=js-1,je ; do i=is,ie
372 Khth_loc_v(i,J) = Khth_loc_v(i,J) + G%OBCmaskCv(i,J) * CS%MEKE_GEOMETRIC_alpha * &
373 0.5*(MEKE%MEKE(i,j)+MEKE%MEKE(i,j+1)) / &
3740 (VarMix%SN_v(i,J) + CS%MEKE_GEOMETRIC_epsilon)
375 enddo ; enddo
376 else
37788584 do J=js-1,je ; do i=is,ie
37888572 Khth_loc_v(i,J) = Khth_loc_v(i,J) + MEKE%KhTh_fac*sqrt(MEKE%Kh(i,j)*MEKE%Kh(i,j+1))
379 enddo ; enddo
380 endif
381 endif
382
38312 if (Resoln_scaled) then
384 !$OMP do
38588584 do J=js-1,je ; do i=is,ie
38688572 Khth_loc_v(i,J) = Khth_loc_v(i,J) * VarMix%Res_fn_v(i,J)
387 enddo ; enddo
388 endif
389
39012 if (Depth_scaled) then
391 !$OMP do
3920 do J=js-1,je ; do i=is,ie
3930 Khth_loc_v(i,J) = Khth_loc_v(i,J) * VarMix%Depth_fn_v(i,J)
394 enddo ; enddo
395 endif
396
39712 if (CS%Khth_Max > 0) then
398 !$OMP do
3990 do J=js-1,je ; do i=is,ie
4000 Khth_loc_v(i,J) = max(CS%Khth_Min, min(Khth_loc_v(i,J), CS%Khth_Max))
401 enddo ; enddo
402 else
403 !$OMP do
40488584 do J=js-1,je ; do i=is,ie
40588572 Khth_loc_v(i,J) = max(CS%Khth_Min, Khth_loc_v(i,J))
406 enddo ; enddo
407 endif
408
40912 if (CS%max_Khth_CFL > 0.0) then
410 !$OMP do
41188584 do J=js-1,je ; do i=is,ie
41288572 KH_v(i,J,1) = min(KH_v_CFL(i,J), Khth_loc_v(i,J))
413 enddo ; enddo
414 endif
415
41612 if (khth_use_vert_struct) then
4170 if (CS%full_depth_khth_min) then
418 !$OMP do
4190 do K=2,nz+1 ; do J=js-1,je ; do i=is,ie
4200 KH_v(i,J,K) = KH_v(i,J,1) * 0.5 * ( VarMix%khth_struct(i,j,k-1) + VarMix%khth_struct(i,j+1,k-1) )
4210 KH_v(i,J,K) = max(KH_v(i,J,K), CS%Khth_Min)
422 enddo ; enddo ; enddo
423 else
424 !$OMP do
4250 do K=2,nz+1 ; do J=js-1,je ; do i=is,ie
4260 KH_v(i,J,K) = KH_v(i,J,1) * 0.5 * ( VarMix%khth_struct(i,j,k-1) + VarMix%khth_struct(i,j+1,k-1) )
427 enddo ; enddo ; enddo
428 endif
429 else
430 !$OMP do
4316643812 do K=2,nz+1 ; do J=js-1,je ; do i=is,ie
4326642900 KH_v(i,J,K) = KH_v(i,J,1)
433 enddo ; enddo ; enddo
434 endif
435
43612 if (use_VarMix) then
4370 if (use_QG_Leith) then
438 !$OMP do
4390 do k=1,nz ; do J=js-1,je ; do i=is,ie
4400 KH_v(i,J,k) = VarMix%KH_v_QG(i,J,k)
441 enddo ; enddo ; enddo
442 endif
443 endif
444
44512 if (CS%use_GME_thickness_diffuse) then
446 !$OMP do
4470 do k=1,nz+1 ; do J=js-1,je ; do i=is,ie
4480 CS%KH_v_GME(i,J,k) = KH_v(i,J,k)
449 enddo ; enddo ; enddo
450 endif
451
45212 if (allocated(MEKE%Kh)) then
45312 if (CS%MEKE_GEOMETRIC) then
4540 if (CS%MEKE_GEOM_answer_date < 20190101) then
455 !$OMP do
4560 do j=js,je ; do i=is,ie
457 ! This does not give bitwise rotational symmetry.
458 MEKE%Kh(i,j) = CS%MEKE_GEOMETRIC_alpha * MEKE%MEKE(i,j) / &
459 (0.25*(VarMix%SN_u(I,j)+VarMix%SN_u(I-1,j) + &
460 VarMix%SN_v(i,J)+VarMix%SN_v(i,J-1)) + &
4610 CS%MEKE_GEOMETRIC_epsilon)
462 enddo ; enddo
463 else
464 !$OMP do
4650 do j=js,je ; do i=is,ie
466 ! With the additional parentheses this gives bitwise rotational symmetry.
467 MEKE%Kh(i,j) = CS%MEKE_GEOMETRIC_alpha * MEKE%MEKE(i,j) / &
468 (0.25*((VarMix%SN_u(I,j)+VarMix%SN_u(I-1,j)) + &
469 (VarMix%SN_v(i,J)+VarMix%SN_v(i,J-1))) + &
4700 CS%MEKE_GEOMETRIC_epsilon)
471 enddo ; enddo
472 endif
473 endif
474 endif
475
476 !$OMP do
4776676764 do K=1,nz+1 ; do j=js,je ; do I=is-1,ie ; int_slope_u(I,j,K) = 0.0 ; enddo ; enddo ; enddo
478 !$OMP do
4796732396 do K=1,nz+1 ; do J=js-1,je ; do i=is,ie ; int_slope_v(i,J,K) = 0.0 ; enddo ; enddo ; enddo
480 !$OMP end parallel
481
48212 if (CS%detangle_interfaces) then
483 call add_detangling_Kh(h, e, Kh_u, Kh_v, KH_u_CFL, KH_v_CFL, tv, dt, G, GV, US, &
4840 CS, int_slope_u, int_slope_v)
485 endif
486
48712 if ((CS%Kh_eta_bg > 0.0) .or. (CS%Kh_eta_vel > 0.0)) then
4880 call add_interface_Kh(G, GV, US, CS, Kh_u, Kh_v, KH_u_CFL, KH_v_CFL, int_slope_u, int_slope_v)
489 endif
490
49112 if (CS%debug) then
492 call uvchksum("Kh_[uv]", Kh_u, Kh_v, G%HI, haloshift=0, &
4930 unscale=(US%L_to_m**2)*US%s_to_T, scalar_pair=.true.)
494 call uvchksum("Kh_[uv]_CFL", Kh_u_CFL, Kh_v_CFL, G%HI, haloshift=0, &
4950 unscale=(US%L_to_m**2)*US%s_to_T, scalar_pair=.true.)
4960 if (Resoln_scaled) then
497 call uvchksum("Res_fn_[uv]", VarMix%Res_fn_u, VarMix%Res_fn_v, G%HI, haloshift=0, &
4980 unscale=1.0, scalar_pair=.true.)
499 endif
5000 call uvchksum("int_slope_[uv]", int_slope_u, int_slope_v, G%HI, haloshift=0)
5010 call hchksum(h, "thickness_diffuse_1 h", G%HI, haloshift=1, unscale=GV%H_to_m)
5020 call hchksum(e, "thickness_diffuse_1 e", G%HI, haloshift=1, unscale=US%Z_to_m)
5030 if (use_stored_slopes) then
504 call uvchksum("VarMix%slope_[xy]", VarMix%slope_x, VarMix%slope_y, &
5050 G%HI, haloshift=0, unscale=US%Z_to_L)
506 endif
5070 if (associated(tv%eqn_of_state)) then
5080 call hchksum(tv%T, "thickness_diffuse T", G%HI, haloshift=1, unscale=US%C_to_degC)
5090 call hchksum(tv%S, "thickness_diffuse S", G%HI, haloshift=1, unscale=US%S_to_ppt)
510 endif
511 endif
512
513 ! Calculate uhD, vhD from h, e, KH_u, KH_v, tv%T/S
51412 if (STOCH%skeb_use_gm) then
5150 if (use_stored_slopes) then
516 call thickness_diffuse_full(h, e, Kh_u, Kh_v, tv, uhD, vhD, cg1, dt, G, GV, US, MEKE, CS, &
517 int_slope_u, int_slope_v, VarMix%slope_x, VarMix%slope_y, &
5180 STOCH=STOCH, VarMix=VarMix)
519 else
520 call thickness_diffuse_full(h, e, Kh_u, Kh_v, tv, uhD, vhD, cg1, dt, G, GV, US, MEKE, CS, &
5210 int_slope_u, int_slope_v, STOCH=STOCH, VarMix=VarMix)
522 endif
523 else
52412 if (use_stored_slopes) then
525 call thickness_diffuse_full(h, e, Kh_u, Kh_v, tv, uhD, vhD, cg1, dt, G, GV, US, MEKE, CS, &
52612 int_slope_u, int_slope_v, VarMix%slope_x, VarMix%slope_y)
527 else
528 call thickness_diffuse_full(h, e, Kh_u, Kh_v, tv, uhD, vhD, cg1, dt, G, GV, US, MEKE, CS, &
5290 int_slope_u, int_slope_v)
530 endif
531 endif
532
53312 if (VarMix%use_variable_mixing) then
53412 if (allocated(MEKE%Rd_dx_h) .and. allocated(VarMix%Rd_dx_h)) then
535 !$OMP parallel do default(shared)
53687132 do j=js,je ; do i=is,ie
53787120 MEKE%Rd_dx_h(i,j) = VarMix%Rd_dx_h(i,j)
538 enddo ; enddo
539 endif
540 endif
541
542 ! offer diagnostic fields for averaging
54312 if (query_averaging_enabled(CS%diag)) then
54412 if (CS%id_uhGM > 0) call post_data(CS%id_uhGM, uhD, CS%diag)
54512 if (CS%id_vhGM > 0) call post_data(CS%id_vhGM, vhD, CS%diag)
54612 if (CS%id_GMwork > 0) call post_data(CS%id_GMwork, CS%GMwork, CS%diag)
54712 if (CS%id_KH_u > 0) call post_data(CS%id_KH_u, KH_u, CS%diag)
54812 if (CS%id_KH_v > 0) call post_data(CS%id_KH_v, KH_v, CS%diag)
54912 if (CS%id_KH_u1 > 0) call post_data(CS%id_KH_u1, KH_u(:,:,1), CS%diag)
55012 if (CS%id_KH_v1 > 0) call post_data(CS%id_KH_v1, KH_v(:,:,1), CS%diag)
551
552 ! Diagnose diffusivity at T-cell point. Do a simple average, rather than a
553 ! thickness-weighted average, so that KH_t is depth-independent when KH_u and KH_v
554 ! are depth independent. If a thickness-weighted average were used, the variations
555 ! of thickness could give a spurious depth dependence to the diagnosed KH_t.
55612 if (CS%id_KH_t > 0 .or. CS%id_KH_t1 > 0 .or. CS%Use_KH_in_MEKE) then
5570 do k=1,nz
558 ! thicknesses across u and v faces, converted to 0/1 mask
559 ! layer average of the interface diffusivities KH_u and KH_v
5600 do j=js,je ; do I=is-1,ie
561 ! This expression uses harmonic mean thicknesses:
562 ! hu(I,j) = 2.0*h(i,j,k)*h(i+1,j,k) / (h(i,j,k)+h(i+1,j,k)+h_neglect)
563 ! This expression is a 0/1 mask based on depths where there are thick layers:
5640 hu(I,j) = 0.0 ; if (h(i,j,k)*h(i+1,j,k) /= 0.0) hu(I,j) = 1.0
5650 KH_u_lay(I,j) = 0.5*(KH_u(I,j,k)+KH_u(I,j,k+1))
566 enddo ; enddo
5670 do J=js-1,je ; do i=is,ie
568 ! This expression uses harmonic mean thicknesses:
569 ! hv(i,J) = 2.0*h(i,j,k)*h(i,j+1,k)/(h(i,j,k)+h(i,j+1,k)+h_neglect)
570 ! This expression is a 0/1 mask based on depths where there are thick layers:
5710 hv(i,J) = 0.0 ; if (h(i,j,k)*h(i,j+1,k) /= 0.0) hv(i,J) = 1.0
5720 KH_v_lay(i,J) = 0.5*(KH_v(i,J,k)+KH_v(i,J,k+1))
573 enddo ; enddo
574 ! diagnose diffusivity at T-points
5750 do j=js,je ; do i=is,ie
576 Kh_t(i,j,k) = (((hu(I-1,j)*KH_u_lay(i-1,j)) + (hu(I,j)*KH_u_lay(I,j))) + &
577 ((hv(i,J-1)*KH_v_lay(i,J-1)) + (hv(i,J)*KH_v_lay(i,J)))) / &
5780 ((hu(I-1,j)+hu(I,j)) + (hv(i,J-1)+hv(i,J)) + 1.0e-20)
579 ! Use this denominator instead if hu and hv are actual thicknesses rather than a 0/1 mask:
580 ! ((hu(I-1,j)+hu(I,j)) + (hv(i,J-1)+hv(i,J)) + h_neglect)
581 enddo ; enddo
582 enddo
583
5840 if (CS%Use_KH_in_MEKE) then
5850 MEKE%Kh_diff(:,:) = 0.0
5860 htot(:,:) = 0.0
5870 do k=1,nz
5880 do j=js,je ; do i=is,ie
5890 MEKE%Kh_diff(i,j) = MEKE%Kh_diff(i,j) + Kh_t(i,j,k) * h(i,j,k)
5900 htot(i,j) = htot(i,j) + h(i,j,k)
591 enddo ; enddo
592 enddo
593
5940 do j=js,je ; do i=is,ie
5950 MEKE%Kh_diff(i,j) = MEKE%Kh_diff(i,j) / MAX(CS%MEKE_min_depth_diff, htot(i,j))
596 enddo ; enddo
597 endif
598
5990 if (CS%id_KH_t > 0) call post_data(CS%id_KH_t, KH_t, CS%diag)
6000 if (CS%id_KH_t1 > 0) call post_data(CS%id_KH_t1, KH_t(:,:,1), CS%diag)
601 endif
602
603 endif
604
605 !$OMP parallel do default(shared)
606912 do k=1,nz
6076588900 do j=js,je ; do I=is-1,ie
6086534000 uhtr(I,j,k) = uhtr(I,j,k) + uhD(I,j,k) * dt
6096588000 if (associated(CDp%uhGM)) CDp%uhGM(I,j,k) = uhD(I,j,k)
610 enddo ; enddo
6116643800 do J=js-1,je ; do i=is,ie
6126588000 vhtr(i,J,k) = vhtr(i,J,k) + vhD(i,J,k) * dt
6136642900 if (associated(CDp%vhGM)) CDp%vhGM(i,J,k) = vhD(i,J,k)
614 enddo ; enddo
6156534912 do j=js,je ; do i=is,ie
616 h(i,j,k) = h(i,j,k) - dt * G%IareaT(i,j) * &
6176480000 ((uhD(I,j,k) - uhD(I-1,j,k)) + (vhD(i,J,k) - vhD(i,J-1,k)))
6186534000 if (h(i,j,k) < GV%Angstrom_H) h(i,j,k) = GV%Angstrom_H
619 enddo ; enddo
620 enddo
621
622 ! Whenever thickness changes let the diag manager know, target grids
623 ! for vertical remapping may need to be regenerated.
624 ! This needs to happen after the H update and before the next post_data.
62512 call diag_update_remap_grids(CS%diag)
626
62712 if (CS%debug) then
628 call uvchksum("thickness_diffuse [uv]hD", uhD, vhD, &
6290 G%HI, haloshift=0, unscale=GV%H_to_m*US%L_to_m**2*US%s_to_T)
630 call uvchksum("thickness_diffuse [uv]htr", uhtr, vhtr, &
6310 G%HI, haloshift=0, unscale=US%L_to_m**2*GV%H_to_m)
6320 call hchksum(h, "thickness_diffuse h", G%HI, haloshift=0, unscale=GV%H_to_m)
633 endif
634
635end subroutine thickness_diffuse
636
637!> Calculates parameterized layer transports for use in the continuity equation.
638!! Fluxes are limited to give positive definite thicknesses.
639!! Called by thickness_diffuse().
64024subroutine thickness_diffuse_full(h, e, Kh_u, Kh_v, tv, uhD, vhD, cg1, dt, G, GV, US, MEKE, &
64124 CS, int_slope_u, int_slope_v, slope_x, slope_y, STOCH, VarMix)
642 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
643 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
644 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
645 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> m or kg m-2]
646 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: e !< Interface positions [Z ~> m]
647 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: Kh_u !< Isopycnal height diffusivity
648 !! at u points [L2 T-1 ~> m2 s-1]
649 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1), intent(in) :: Kh_v !< Isopycnal height diffusivity
650 !! at v points [L2 T-1 ~> m2 s-1]
651 type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamics structure
652 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(out) :: uhD !< Zonal mass fluxes
653 !! [H L2 T-1 ~> m3 s-1 or kg s-1]
654 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(out) :: vhD !< Meridional mass fluxes
655 !! [H L2 T-1 ~> m3 s-1 or kg s-1]
656 real, dimension(:,:), pointer :: cg1 !< Wave speed [L T-1 ~> m s-1]
657 real, intent(in) :: dt !< Time increment [T ~> s]
658 type(MEKE_type), intent(inout) :: MEKE !< MEKE fields
659 type(thickness_diffuse_CS), intent(inout) :: CS !< Control structure for thickness_diffuse
660 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: int_slope_u !< Ratio that determine how much of
661 !! the isopycnal slopes are taken directly from
662 !! the interface slopes without consideration of
663 !! density gradients [nondim].
664 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1), intent(in) :: int_slope_v !< Ratio that determine how much of
665 !! the isopycnal slopes are taken directly from
666 !! the interface slopes without consideration of
667 !! density gradients [nondim].
668 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), optional, intent(in) :: slope_x !< Isopyc. slope at u [Z L-1 ~> nondim]
669 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1), optional, intent(in) :: slope_y !< Isopyc. slope at v [Z L-1 ~> nondim]
670 type(stochastic_CS), optional, intent(inout) :: STOCH !< Stochastic control structure
671 type(VarMix_CS), target, optional, intent(in) :: VarMix !< Variable mixing coefficents
672
673 ! Local variables
674 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: &
67524 T, & ! The temperature [C ~> degC], with the values in
676 ! in massless layers filled vertically by diffusion.
67724 S, & ! The filled salinity [S ~> ppt], with the values in
678 ! in massless layers filled vertically by diffusion.
67924 h_avail, & ! The mass available for diffusion out of each face, divided
680 ! by dt [H L2 T-1 ~> m3 s-1 or kg s-1].
68124 h_frac ! The fraction of the mass in the column above the bottom
682 ! interface of a layer that is within a layer [nondim]. 0<h_frac<=1
68324 real :: dz(SZI_(G),SZJ_(G),SZK_(GV)) ! Height change across layers [Z ~> m]
684 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1) :: &
68524 Slope_y_PE, & ! 3D array of neutral slopes at v-points, set equal to Slope (below) [Z L-1 ~> nondim]
68624 hN2_y_PE ! Harmonic mean of thicknesses around the interfaces times the buoyancy frequency
687 ! at v-points with unit conversion factors [H L2 Z-2 T-2 ~> m s-2 or kg m-2 s-2],
688 ! used for calculating the potential energy release
689 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1) :: &
69024 Slope_x_PE, & ! 3D array of neutral slopes at u-points, set equal to Slope (below) [Z L-1 ~> nondim]
69124 hN2_x_PE ! Harmonic mean of thicknesses around the interfaces times the buoyancy frequency
692 ! at u-points with unit conversion factors [H L2 Z-2 T-2 ~> m s-2 or kg m-2 s-2],
693 ! used for calculating the potential energy release
694 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: &
69524 pres, & ! The pressure at an interface [R L2 T-2 ~> Pa].
69624 h_avail_rsum ! The running sum of h_avail above an interface [H L2 T-1 ~> m3 s-1 or kg s-1].
697 real, dimension(SZIB_(G)) :: &
69824 drho_dT_u, & ! The derivative of density with temperature at u points [R C-1 ~> kg m-3 degC-1]
69924 drho_dS_u ! The derivative of density with salinity at u points [R S-1 ~> kg m-3 ppt-1].
70024 real, dimension(SZIB_(G)) :: scrap ! An array to pass to calculate_density_second_derivs()
701 ! with various units that will be ignored [various]
702 real, dimension(SZI_(G)) :: &
70324 drho_dT_v, & ! The derivative of density with temperature at v points [R C-1 ~> kg m-3 degC-1]
70424 drho_dS_v, & ! The derivative of density with salinity at v points [R S-1 ~> kg m-3 ppt-1].
70524 drho_dT_dT_h, & ! The second derivative of density with temperature at h points [R C-2 ~> kg m-3 degC-2]
70624 drho_dT_dT_hr ! The second derivative of density with temperature at h (+1) points [R C-2 ~> kg m-3 degC-2]
70724 real :: uhtot(SZIB_(G),SZJ_(G)) ! The vertical sum of uhD [H L2 T-1 ~> m3 s-1 or kg s-1].
70824 real :: vhtot(SZI_(G),SZJB_(G)) ! The vertical sum of vhD [H L2 T-1 ~> m3 s-1 or kg s-1].
709 real, dimension(SZIB_(G)) :: &
71024 T_u, & ! Temperature on the interface at the u-point [C ~> degC].
71124 S_u, & ! Salinity on the interface at the u-point [S ~> ppt].
71224 pres_u ! Pressure on the interface at the u-point [R L2 T-2 ~> Pa].
713 real, dimension(SZI_(G)) :: &
71424 T_v, & ! Temperature on the interface at the v-point [C ~> degC].
71524 S_v, & ! Salinity on the interface at the v-point [S ~> ppt].
71624 pres_v, & ! Pressure on the interface at the v-point [R L2 T-2 ~> Pa].
71724 T_h, & ! Temperature on the interface at the h-point [C ~> degC].
71824 S_h, & ! Salinity on the interface at the h-point [S ~> ppt].
71924 pres_h, & ! Pressure on the interface at the h-point [R L2 T-2 ~> Pa].
72024 T_hr, & ! Temperature on the interface at the h (+1) point [C ~> degC].
72124 S_hr, & ! Salinity on the interface at the h (+1) point [S ~> ppt].
72224 pres_hr ! Pressure on the interface at the h (+1) point [R L2 T-2 ~> Pa].
72324 real :: Work_u(SZIB_(G),SZJ_(G)) ! The work done by the isopycnal height diffusion
724 ! integrated over u-point water columns [R Z L4 T-3 ~> W]
72524 real :: Work_v(SZI_(G),SZJB_(G)) ! The work done by the isopycnal height diffusion
726 ! integrated over v-point water columns [R Z L4 T-3 ~> W]
727 real :: Work_h ! The work averaged over an h-cell [R Z L2 T-3 ~> W m-2].
728 real :: PE_release_h ! The amount of potential energy released by GM averaged over an h-cell
729 ! [R Z L2 T-3 ~> W m-2]. The calculation equals rho0 * h * S^2 * N^2 * kappa_GM.
730 real :: I4dt ! 1 / 4 dt [T-1 ~> s-1].
731 real :: drdiA, drdiB ! Along layer zonal potential density gradients in the layers above (A)
732 ! and below (B) the interface times the grid spacing [R ~> kg m-3].
733 real :: drdjA, drdjB ! Along layer meridional potential density gradients in the layers above (A)
734 ! and below (B) the interface times the grid spacing [R ~> kg m-3].
735 real :: drdkL, drdkR ! Vertical density differences across an interface [R ~> kg m-3].
73624 real :: drdi_u(SZIB_(G),SZK_(GV)) ! Copy of drdi at u-points [R ~> kg m-3].
73724 real :: drdj_v(SZI_(G),SZK_(GV)) ! Copy of drdj at v-points [R ~> kg m-3].
73824 real :: drdkDe_u(SZIB_(G),SZK_(GV)+1) ! Lateral difference of product of drdk and e at u-points
739 ! [Z R ~> kg m-2].
74024 real :: drdkDe_v(SZI_(G),SZK_(GV)+1) ! Lateral difference of product of drdk and e at v-points
741 ! [Z R ~> kg m-2].
742 real :: hg2A, hg2B, hg2L, hg2R ! Squares of geometric mean thicknesses [H2 ~> m2 or kg2 m-4].
743 real :: haA, haB, haL, haR ! Arithmetic mean thicknesses [H ~> m or kg m-2].
744 real :: dzg2A, dzg2B ! Squares of geometric mean vertical layer extents [Z2 ~> m2].
745 real :: dzaA, dzaB ! Arithmetic mean vertical layer extents [Z ~> m].
746 real :: dzaL, dzaR ! Temporary vertical layer extents [Z ~> m]
747 real :: wtA, wtB ! Unnormalized weights of the slopes above and below [H3 ~> m3 or kg3 m-6]
748 real :: wtL, wtR ! Unnormalized weights of the slopes to the left and right [H3 Z ~> m4 or kg3 m-5]
749 real :: drdx, drdy ! Zonal and meridional density gradients [R L-1 ~> kg m-4].
750 real :: drdz ! Vertical density gradient [R Z-1 ~> kg m-4].
751 real :: dz_harm ! Harmonic mean layer vertical extent [Z ~> m].
75224 real :: c2_dz_u(SZIB_(G),SZK_(GV)+1) ! Wave speed squared divided by dz at u-points [L2 Z-1 T-2 ~> m s-2]
75324 real :: c2_dz_v(SZI_(G),SZK_(GV)+1) ! Wave speed squared divided by dz at v-points [L2 Z-1 T-2 ~> m s-2]
75424 real :: dzN2_u(SZIB_(G),SZK_(GV)+1) ! Vertical extent times N2 at interfaces above u-points times
755 ! rescaling factors from vertical to horizontal distances [L2 Z-1 T-2 ~> m s-2]
75624 real :: dzN2_v(SZI_(G),SZK_(GV)+1) ! Vertical extent times N2 at interfaces above v-points times
757 ! rescaling factors from vertical to horizontal distances [L2 Z-1 T-2 ~> m s-2]
758 real :: Sfn_est ! A preliminary estimate (before limiting) of the overturning
759 ! streamfunction [H L2 T-1 ~> m3 s-1 or kg s-1].
76024 real :: Sfn_unlim_u(SZIB_(G),SZK_(GV)+1) ! Volume streamfunction for u-points [Z L2 T-1 ~> m3 s-1]
76124 real :: Sfn_unlim_v(SZI_(G),SZK_(GV)+1) ! Volume streamfunction for v-points [Z L2 T-1 ~> m3 s-1]
76224 real :: slope2_Ratio_u(SZIB_(G),SZK_(GV)+1) ! The ratio of the slope squared to slope_max squared [nondim]
76324 real :: slope2_Ratio_v(SZI_(G),SZK_(GV)+1) ! The ratio of the slope squared to slope_max squared [nondim]
764 real :: Sfn_in_h ! The overturning streamfunction [H L2 T-1 ~> m3 s-1 or kg s-1] (note that
765 ! the units are different from other Sfn vars).
766 real :: Sfn_safe ! The streamfunction that goes linearly back to 0 at the surface
767 ! [H L2 T-1 ~> m3 s-1 or kg s-1]. This is a good value to use when the
768 ! slope is so large as to be meaningless, usually due to weak stratification.
769 real :: Slope ! The slope of density surfaces, calculated in a way that is always
770 ! between -1 and 1 after undoing dimensional scaling, [Z L-1 ~> nondim]
771 real :: mag_grad2 ! The squared magnitude of the 3-d density gradient [R2 L-2 ~> kg2 m-8].
772 real :: I_slope_max2 ! The inverse of slope_max squared [L2 Z-2 ~> nondim].
773 real :: h_neglect ! A thickness that is so small it is usually lost
774 ! in roundoff and can be neglected [H ~> m or kg m-2].
775 real :: hn_2 ! Half of h_neglect [H ~> m or kg m-2].
776 real :: h_neglect2 ! h_neglect^2 [H2 ~> m2 or kg2 m-4].
777 real :: dz_neglect ! A thickness [Z ~> m], that is so small it is usually lost
778 ! in roundoff and can be neglected [Z ~> m].
779 real :: dz_neglect2 ! dz_neglect^2 [Z2 ~> m2]
780 real :: G_scale ! The gravitational acceleration times a unit conversion
781 ! factor [L2 H-1 T-2 ~> m s-2 or m4 kg-1 s-2].
782 logical :: use_EOS ! If true, density is calculated from T & S using an equation of state.
783 logical :: find_work ! If true, find the change in energy due to the fluxes.
784 integer :: nk_linear ! The number of layers over which the streamfunction goes to 0.
785 real :: G_rho0 ! g/Rho0 [L2 R-1 Z-1 T-2 ~> m4 kg-1 s-2].
786 real :: Rho_avg ! The in situ density averaged to an interface [R ~> kg m-3]
787 real :: N2_floor ! A floor for N2 to avoid degeneracy in the elliptic solver
788 ! times unit conversion factors [L2 Z-2 T-2 ~> s-2]
789 real :: N2_unlim ! An unlimited estimate of the buoyancy frequency
790 ! times unit conversion factors [L2 Z-2 T-2 ~> s-2]
791 real :: Z_to_H ! A conversion factor from heights to thicknesses, perhaps based on
792 ! a spatially variable local density [H Z-1 ~> nondim or kg m-3]
79324 real :: diag_sfn_x(SZIB_(G),SZJ_(G),SZK_(GV)+1) ! Diagnostic of the x-face streamfunction
794 ! [H L2 T-1 ~> m3 s-1 or kg s-1]
79524 real :: diag_sfn_unlim_x(SZIB_(G),SZJ_(G),SZK_(GV)+1) ! Diagnostic of the x-face streamfunction before
796 ! applying limiters [Z L2 T-1 ~> m3 s-1]
79724 real :: diag_sfn_y(SZI_(G),SZJB_(G),SZK_(GV)+1) ! Diagnostic of the y-face streamfunction
798 ! [H L2 T-1 ~> m3 s-1 or kg s-1]
79924 real :: diag_sfn_unlim_y(SZI_(G),SZJB_(G),SZK_(GV)+1) ! Diagnostic of the y-face streamfunction before
800 ! applying limiters [Z L2 T-1 ~> m3 s-1]
801 ! applying limiters [H L2 T-1 ~> m3 s-1 or kg s-1]
80212 real, allocatable :: skeb_gm_work(:,:) ! Temp array to hold GM work for SKEB
80312 real, allocatable :: skeb_ebt_norm2(:,:) ! Used to normalize EBT for SKEB
804
805 logical :: present_slope_x, present_slope_y, calc_derivatives
806 integer, dimension(2) :: EOSdom_u ! The shifted I-computational domain to use for equation of
807 ! state calculations at u-points.
808 integer, dimension(2) :: EOSdom_v ! The shifted i-computational domain to use for equation of
809 ! state calculations at v-points.
810 integer, dimension(2) :: EOSdom_h1 ! The shifted i-computational domain to use for equation of
811 ! state calculations at h points with 1 extra halo point
812 logical :: use_stanley, skeb_use_gm
813 integer :: is, ie, js, je, nz, IsdB, halo
814 integer :: i, j, k
81512 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke ; IsdB = G%IsdB
816
81712 I4dt = 0.25 / dt
81812 I_slope_max2 = 1.0 / (CS%slope_max**2)
819
82012 h_neglect = GV%H_subroundoff ; h_neglect2 = h_neglect**2 ; hn_2 = 0.5*h_neglect
82112 dz_neglect = GV%dZ_subroundoff ; dz_neglect2 = dz_neglect**2
82212 if (GV%Boussinesq) G_rho0 = GV%g_Earth / GV%Rho0
82312 N2_floor = CS%N2_floor
824
82512 use_EOS = associated(tv%eqn_of_state)
82612 present_slope_x = PRESENT(slope_x)
82712 present_slope_y = PRESENT(slope_y)
828
82912 use_stanley = CS%use_stanley_gm
830
83112 skeb_use_gm = .false.
83212 if (present(STOCH)) skeb_use_gm = STOCH%skeb_use_gm
83312 if (skeb_use_gm) then
8340 allocate(skeb_gm_work(is:ie,js:je), source=0.)
8350 allocate(skeb_ebt_norm2(is:ie,js:je), source=0.)
836 endif
837
83812 nk_linear = max(GV%nkml, 1)
839
8408063004 Slope_x_PE(:,:,:) = 0.0
8418118636 Slope_y_PE(:,:,:) = 0.0
8428063004 hN2_x_PE(:,:,:) = 0.0
8438118636 hN2_y_PE(:,:,:) = 0.0
844
84512 find_work = allocated(MEKE%GM_src)
84612 find_work = (allocated(CS%GMwork) .or. find_work)
84712 find_work = (skeb_use_gm .or. find_work)
848
84912 if (use_EOS) then
85012 halo = 1 ! Default halo to fill is 1
851 !$omp target enter data map(to: h, tv%T, tv%S)
852 !$omp target enter data map(alloc: T, S)
85312 call vert_fill_TS(h, tv%T, tv%S, CS%kappa_smooth*dt, T, S, G, GV, US, halo, larger_h_denom=.true.)
854 !$omp target exit data map(from: T, S)
855 !$omp target exit data map(release: h, tv%T, tv%S)
856 endif
857
858 ! Rescale the thicknesses, perhaps using the specific volume.
85912 call thickness_to_dz(h, tv, dz, G, GV, US, halo_size=1)
860
86112 if (CS%use_FGNV_streamfn .and. .not. associated(cg1)) call MOM_error(FATAL, &
8620 "cg1 must be associated when using FGNV streamfunction.")
863
864 !$OMP parallel default(shared)
865 ! Find the maximum and minimum permitted streamfunction.
866 !$OMP do
86791524 do j=js-1,je+1 ; do i=is-1,ie+1
86890768 h_avail_rsum(i,j,1) = 0.0
86990768 pres(i,j,1) = 0.0
87090768 if (associated(tv%p_surf)) then ; pres(i,j,1) = tv%p_surf(i,j) ; endif
871
87290768 h_avail(i,j,1) = max(I4dt*G%areaT(i,j)*(h(i,j,1)-GV%Angstrom_H),0.0)
87390768 h_avail_rsum(i,j,2) = h_avail(i,j,1)
87490768 h_frac(i,j,1) = 1.0
87591512 pres(i,j,2) = pres(i,j,1) + (GV%g_Earth*GV%H_to_RZ) * h(i,j,1)
876 enddo ; enddo
877756 do j=js-1,je+1
8786772644 do k=2,nz ; do i=is-1,ie+1
8796716832 h_avail(i,j,k) = max(I4dt*G%areaT(i,j)*(h(i,j,k)-GV%Angstrom_H),0.0)
8806716832 h_avail_rsum(i,j,k+1) = h_avail_rsum(i,j,k) + h_avail(i,j,k)
8816716832 h_frac(i,j,k) = 0.0 ; if (h_avail(i,j,k) > 0.0) &
8826716832 h_frac(i,j,k) = h_avail(i,j,k) / h_avail_rsum(i,j,k+1)
8836771888 pres(i,j,K+1) = pres(i,j,K) + (GV%g_Earth*GV%H_to_RZ) * h(i,j,k)
884 enddo ; enddo
885 enddo
886 !$OMP do
88787852 do j=js,je ; do I=is-1,ie
88887840 uhtot(I,j) = 0.0 ; Work_u(I,j) = 0.0
889 enddo ; enddo
890 !$OMP do
89188584 do J=js-1,je ; do i=is,ie
89288572 vhtot(i,J) = 0.0 ; Work_v(i,J) = 0.0
893 enddo ; enddo
894 !$OMP end parallel
895
89612 if (CS%id_sfn_x > 0) then ; diag_sfn_x(:,:,1) = 0.0 ; diag_sfn_x(:,:,nz+1) = 0.0 ; endif
89712 if (CS%id_sfn_y > 0) then ; diag_sfn_y(:,:,1) = 0.0 ; diag_sfn_y(:,:,nz+1) = 0.0 ; endif
89812 if (CS%id_sfn_unlim_x > 0) then ; diag_sfn_unlim_x(:,:,1) = 0.0 ; diag_sfn_unlim_x(:,:,nz+1) = 0.0 ; endif
89912 if (CS%id_sfn_unlim_y > 0) then ; diag_sfn_unlim_y(:,:,1) = 0.0 ; diag_sfn_unlim_y(:,:,nz+1) = 0.0 ; endif
900
90112 EOSdom_u(1) = (is-1) - (G%IsdB-1) ; EOSdom_u(2) = ie - (G%IsdB-1)
90212 EOSdom_v(:) = EOS_domain(G%HI)
90312 EOSdom_h1(:) = EOS_domain(G%HI, halo=1)
904
905 !$OMP parallel do default(none) shared(nz,is,ie,js,je,find_work,use_EOS,G,GV,US,pres,T,S, &
906 !$OMP nk_linear,IsdB,tv,h,h_neglect,e,dz,dz_neglect,dz_neglect2, &
907 !$OMP h_neglect2,hn_2,I_slope_max2,int_slope_u,KH_u,uhtot, &
908 !$OMP h_frac,h_avail_rsum,uhD,h_avail,Work_u,CS,slope_x,cg1, &
909 !$OMP diag_sfn_x,diag_sfn_unlim_x,N2_floor,EOSdom_u,EOSdom_h1, &
910 !$OMP use_stanley,present_slope_x,G_rho0,Slope_x_PE,hN2_x_PE) &
911 !$OMP private(drdiA,drdiB,drdkL,drdkR,pres_u,T_u,S_u,G_scale, &
912 !$OMP drho_dT_u,drho_dS_u,hg2A,hg2B,hg2L,hg2R,haA, &
913 !$OMP drho_dT_dT_h,scrap,pres_h,T_h,S_h,N2_unlim, &
914 !$OMP haB,haL,haR,dzaL,dzaR,wtA,wtB,wtL,wtR,drdz, &
915 !$OMP dzg2A,dzg2B,dzaA,dzaB,dz_harm,Z_to_H, &
916 !$OMP drdx,mag_grad2,Slope,slope2_Ratio_u,dzN2_u, &
917 !$OMP Sfn_unlim_u,Rho_avg,drdi_u,drdkDe_u,c2_dz_u, &
918 !$OMP Sfn_safe,Sfn_est,Sfn_in_h,calc_derivatives)
919732 do j=js,je
92087840 do I=is-1,ie ; dzN2_u(I,1) = 0. ; dzN2_u(I,nz+1) = 0. ; enddo
92154000 do K=nz,2,-1
92253280 if (find_work .and. .not.(use_EOS)) then
9230 drdiA = 0.0 ; drdiB = 0.0
9240 drdkL = GV%Rlay(k) - GV%Rlay(k-1) ; drdkR = drdkL
925 endif
926
927 calc_derivatives = use_EOS .and. (k >= nk_linear) .and. &
92853280 (find_work .or. .not. present_slope_x .or. CS%use_FGNV_streamfn .or. use_stanley)
929
930 ! Calculate the zonal fluxes and gradients.
93153280 if (calc_derivatives) then
9326500160 do I=is-1,ie
9336446880 pres_u(I) = 0.5*(pres(i,j,K) + pres(i+1,j,K))
9346446880 T_u(I) = 0.25*((T(i,j,k) + T(i+1,j,k)) + (T(i,j,k-1) + T(i+1,j,k-1)))
9356500160 S_u(I) = 0.25*((S(i,j,k) + S(i+1,j,k)) + (S(i,j,k-1) + S(i+1,j,k-1)))
936 enddo
937 call calculate_density_derivs(T_u, S_u, pres_u, drho_dT_u, drho_dS_u, &
93853280 tv%eqn_of_state, EOSdom_u)
939 endif
94053280 if (use_stanley) then
9410 do i=is-1,ie+1
9420 pres_h(i) = pres(i,j,K)
9430 T_h(i) = 0.5*(T(i,j,k) + T(i,j,k-1))
9440 S_h(i) = 0.5*(S(i,j,k) + S(i,j,k-1))
945 enddo
946
947 ! The second line below would correspond to arguments
948 ! drho_dS_dS, drho_dS_dT, drho_dT_dT, drho_dS_dP, drho_dT_dP, &
949 call calculate_density_second_derivs(T_h, S_h, pres_h, &
950 scrap, scrap, drho_dT_dT_h, scrap, scrap, &
9510 tv%eqn_of_state, EOSdom_h1)
952 endif
953
9546500880 do I=is-1,ie
9556446880 if (calc_derivatives) then
956 ! Estimate the horizontal density gradients along layers.
957 drdiA = drho_dT_u(I) * (T(i+1,j,k-1)-T(i,j,k-1)) + &
9586446880 drho_dS_u(I) * (S(i+1,j,k-1)-S(i,j,k-1))
959 drdiB = drho_dT_u(I) * (T(i+1,j,k)-T(i,j,k)) + &
9606446880 drho_dS_u(I) * (S(i+1,j,k)-S(i,j,k))
961
962 ! Estimate the vertical density gradients times the grid spacing.
963 drdkL = (drho_dT_u(I) * (T(i,j,k)-T(i,j,k-1)) + &
9646446880 drho_dS_u(I) * (S(i,j,k)-S(i,j,k-1)))
965 drdkR = (drho_dT_u(I) * (T(i+1,j,k)-T(i+1,j,k-1)) + &
9666446880 drho_dS_u(I) * (S(i+1,j,k)-S(i+1,j,k-1)))
9676446880 drdkDe_u(I,K) = (drdkR * e(i+1,j,K)) - (drdkL * e(i,j,K))
9680 elseif (find_work) then ! This is used in pure stacked SW mode
9690 drdkDe_u(I,K) = (drdkR * e(i+1,j,K)) - (drdkL * e(i,j,K))
970 endif
9716446880 if (use_stanley) then
972 ! Correction to the horizontal density gradient due to nonlinearity in
973 ! the EOS rectifying SGS temperature anomalies
974 drdiA = drdiA + 0.5 * ((drho_dT_dT_h(i+1) * tv%varT(i+1,j,k-1)) - &
9750 (drho_dT_dT_h(i) * tv%varT(i,j,k-1)) )
976 drdiB = drdiB + 0.5 * ((drho_dT_dT_h(i+1) * tv%varT(i+1,j,k)) - &
9770 (drho_dT_dT_h(i) * tv%varT(i,j,k)) )
978 endif
9796446880 if (find_work) drdi_u(I,k) = drdiB
980
9816446880 if (k > nk_linear) then
9826446880 if (use_EOS) then
9836446880 if (CS%use_FGNV_streamfn .or. find_work .or. .not.present_slope_x) then
9846446880 hg2L = h(i,j,k-1)*h(i,j,k) + h_neglect2
9856446880 hg2R = h(i+1,j,k-1)*h(i+1,j,k) + h_neglect2
9866446880 haL = 0.5*(h(i,j,k-1) + h(i,j,k)) + h_neglect
9876446880 haR = 0.5*(h(i+1,j,k-1) + h(i+1,j,k)) + h_neglect
9886446880 if (GV%Boussinesq) then
9896446880 dzaL = haL * GV%H_to_Z ; dzaR = haR * GV%H_to_Z
9900 elseif (GV%semi_Boussinesq) then
9910 dzaL = 0.5*(e(i,j,K-1) - e(i,j,K+1)) + dz_neglect
9920 dzaR = 0.5*(e(i+1,j,K-1) - e(i+1,j,K+1)) + dz_neglect
993 else
9940 dzaL = 0.5*(dz(i,j,k-1) + dz(i,j,k)) + dz_neglect
9950 dzaR = 0.5*(dz(i+1,j,k-1) + dz(i+1,j,k)) + dz_neglect
996 endif
997 ! Use the harmonic mean thicknesses to weight the horizontal gradients.
998 ! These unnormalized weights have been rearranged to minimize divisions.
9996446880 wtL = hg2L*(haR*dzaR) ; wtR = hg2R*(haL*dzaL)
1000
10016446880 drdz = ((wtL * drdkL) + (wtR * drdkR)) / ((dzaL*wtL) + (dzaR*wtR))
1002 ! The expression for drdz above is mathematically equivalent to:
1003 ! drdz = ((hg2L/haL) * drdkL/dzaL + (hg2R/haR) * drdkR/dzaR) / &
1004 ! ((hg2L/haL) + (hg2R/haR))
10056446880 hg2A = h(i,j,k-1)*h(i+1,j,k-1) + h_neglect2
10066446880 hg2B = h(i,j,k)*h(i+1,j,k) + h_neglect2
10076446880 haA = 0.5*(h(i,j,k-1) + h(i+1,j,k-1)) + h_neglect
10086446880 haB = 0.5*(h(i,j,k) + h(i+1,j,k)) + h_neglect
1009
10106446880 if (GV%Boussinesq) then
10116446880 N2_unlim = drdz*G_rho0
1012 else
1013 N2_unlim = (GV%g_Earth*GV%RZ_to_H) * &
10140 (((wtL * drdkL) + (wtR * drdkR)) / ((haL*wtL) + (haR*wtR)))
1015 endif
1016
10176446880 dzg2A = dz(i,j,k-1)*dz(i+1,j,k-1) + dz_neglect2
10186446880 dzg2B = dz(i,j,k)*dz(i+1,j,k) + dz_neglect2
10196446880 dzaA = 0.5*(dz(i,j,k-1) + dz(i+1,j,k-1)) + dz_neglect
10206446880 dzaB = 0.5*(dz(i,j,k) + dz(i+1,j,k)) + dz_neglect
1021 ! dzN2_u is used with the FGNV streamfunction formulation
10226446880 dzN2_u(I,K) = (0.5 * ( dzg2A / dzaA + dzg2B / dzaB )) * max(N2_unlim, N2_floor)
10236446880 if (find_work .and. CS%GM_src_alt) &
10240 hN2_x_PE(I,j,k) = (0.5 * ( hg2A / haA + hg2B / haB )) * max(N2_unlim, N2_floor)
1025 endif
1026
10276446880 if (present_slope_x) then
10286446880 Slope = slope_x(I,j,k)
10296446880 slope2_Ratio_u(I,K) = Slope**2 * I_slope_max2
1030 else
1031 ! Use the harmonic mean thicknesses to weight the horizontal gradients.
1032 ! These unnormalized weights have been rearranged to minimize divisions.
10330 wtA = hg2A*haB ; wtB = hg2B*haA
1034 ! This is the gradient of density along geopotentials.
1035 drdx = ((wtA * drdiA + wtB * drdiB) / (wtA + wtB) - &
10360 drdz * (e(i,j,K)-e(i+1,j,K))) * G%IdxCu(I,j)
1037
1038 ! This estimate of slope is accurate for small slopes, but bounded
1039 ! to be between -1 and 1.
10400 mag_grad2 = (US%Z_to_L*drdx)**2 + drdz**2
10410 if (mag_grad2 > 0.0) then
10420 Slope = drdx / sqrt(mag_grad2)
10430 slope2_Ratio_u(I,K) = Slope**2 * I_slope_max2
1044 else ! Just in case mag_grad2 = 0 ever.
10450 Slope = 0.0
10460 slope2_Ratio_u(I,K) = 1.0e20 ! Force the use of the safe streamfunction.
1047 endif
1048 endif
1049
1050 ! Adjust real slope by weights that bias towards slope of interfaces
1051 ! that ignore density gradients along layers.
1052 Slope = (1.0 - int_slope_u(I,j,K)) * Slope + &
10536446880 int_slope_u(I,j,K) * ((e(i+1,j,K)-e(i,j,K)) * G%IdxCu(I,j))
10546446880 slope2_Ratio_u(I,K) = (1.0 - int_slope_u(I,j,K)) * slope2_Ratio_u(I,K)
1055
10566446880 if (CS%MEKE_src_slope_bug) then
10570 Slope_x_PE(I,j,k) = MIN(Slope, CS%slope_max)
1058 else
10596446880 Slope_x_PE(I,j,k) = Slope
10606446880 if (Slope > CS%slope_max) Slope_x_PE(I,j,k) = CS%slope_max
10616446880 if (Slope < -CS%slope_max) Slope_x_PE(I,j,k) = -CS%slope_max
1062 endif
10636446880 if (CS%id_slope_x > 0) CS%diagSlopeX(I,j,k) = Slope
1064
1065 ! Estimate the streamfunction at each interface [H L2 T-1 ~> m3 s-1 or kg s-1].
10666446880 Sfn_unlim_u(I,K) = -(KH_u(I,j,K)*G%dy_Cu(I,j))*Slope
1067
1068 ! Avoid moving dense water upslope from below the level of
1069 ! the bottom on the receiving side.
10706446880 if (Sfn_unlim_u(I,K) > 0.0) then ! The flow below this interface is positive.
10710 if (e(i,j,K) < e(i+1,j,nz+1)) then
10720 Sfn_unlim_u(I,K) = 0.0 ! This is not uhtot, because it may compensate for
1073 ! deeper flow in very unusual cases.
10740 elseif (e(i+1,j,nz+1) > e(i,j,K+1)) then
1075 ! Scale the transport with the fraction of the donor layer above
1076 ! the bottom on the receiving side.
1077 Sfn_unlim_u(I,K) = Sfn_unlim_u(I,K) * ((e(i,j,K) - e(i+1,j,nz+1)) / &
10780 ((e(i,j,K) - e(i,j,K+1)) + dz_neglect))
1079 endif
1080 else
10816446880 if (e(i+1,j,K) < e(i,j,nz+1)) then ; Sfn_unlim_u(I,K) = 0.0
10825679480 elseif (e(i,j,nz+1) > e(i+1,j,K+1)) then
1083 Sfn_unlim_u(I,K) = Sfn_unlim_u(I,K) * ((e(i+1,j,K) - e(i,j,nz+1)) / &
108429316 ((e(i+1,j,K) - e(i+1,j,K+1)) + dz_neglect))
1085 endif
1086 endif
1087
1088 else ! .not. use_EOS
10890 if (present_slope_x) then
10900 Slope = slope_x(I,j,k)
1091 else
10920 Slope = (e(i+1,j,K)-e(i,j,K)) * G%IdxCu_OBCmask(I,j)
1093 endif
10940 if (CS%id_slope_x > 0) CS%diagSlopeX(I,j,k) = Slope
10950 Sfn_unlim_u(I,K) = -(KH_u(I,j,K)*G%dy_Cu(I,j))*Slope
10960 dzN2_u(I,K) = GV%g_prime(K)
1097 endif ! if (use_EOS)
1098 else ! if (k > nk_linear)
10990 dzN2_u(I,K) = N2_floor * dz_neglect
11000 Sfn_unlim_u(I,K) = 0.
1101 endif ! if (k > nk_linear)
11026500160 if (CS%id_sfn_unlim_x>0) diag_sfn_unlim_x(I,j,K) = Sfn_unlim_u(I,K)
1103 enddo ! i-loop
1104 enddo ! k-loop
1105
1106720 if (CS%use_FGNV_streamfn) then
11070 do k=1,nz ; do I=is-1,ie ; if (G%OBCmaskCu(I,j)>0.) then
1108 dz_harm = max( dz_neglect, &
11090 2. * dz(i,j,k) * dz(i+1,j,k) / ( ( dz(i,j,k) + dz(i+1,j,k) ) + dz_neglect ) )
11100 c2_dz_u(I,k) = CS%FGNV_scale * ( 0.5*( cg1(i,j) + cg1(i+1,j) ) )**2 / dz_harm
1111 endif ; enddo ; enddo
1112
1113 ! Solve an elliptic equation for the streamfunction following Ferrari et al., 2010.
11140 do I=is-1,ie
11150 if (G%OBCmaskCu(I,j)>0.) then
11160 do K=2,nz
11170 Sfn_unlim_u(I,K) = (1. + CS%FGNV_scale) * Sfn_unlim_u(I,K)
1118 enddo
11190 call streamfn_solver(nz, c2_dz_u(I,:), dzN2_u(I,:), Sfn_unlim_u(I,:))
1120 else
11210 do K=2,nz
11220 Sfn_unlim_u(I,K) = 0.
1123 enddo
1124 endif
1125 enddo
1126 endif
1127
112854012 do K=nz,2,-1
11296500880 do I=is-1,ie
1130
11316446880 if (allocated(tv%SpV_avg) .and. (find_work .or. (k > nk_linear)) ) then
1132 Rho_avg = ( ((h(i,j,k) + h(i,j,k-1)) + (h(i+1,j,k) + h(i+1,j,k-1))) + 4.0*hn_2 ) / &
1133 ( (((h(i,j,k)+hn_2) * tv%SpV_avg(i,j,k)) + ((h(i,j,k-1)+hn_2) * tv%SpV_avg(i,j,k-1))) + &
11340 (((h(i+1,j,k)+hn_2)*tv%SpV_avg(i+1,j,k)) + ((h(i+1,j,k-1)+hn_2)*tv%SpV_avg(i+1,j,k-1))) )
1135 ! Use an average density to convert the volume streamfunction estimate into a mass streamfunction.
11360 Z_to_H = GV%RZ_to_H*Rho_avg
1137 else
11386446880 Z_to_H = GV%Z_to_H
1139 endif
1140
11416446880 if (k > nk_linear) then
11426446880 if (use_EOS) then
1143
11446446880 if (uhtot(I,j) <= 0.0) then
1145 ! The transport that must balance the transport below is positive.
11466446880 Sfn_safe = uhtot(I,j) * (1.0 - h_frac(i,j,k))
1147 else ! (uhtot(I,j) > 0.0)
11480 Sfn_safe = uhtot(I,j) * (1.0 - h_frac(i+1,j,k))
1149 endif
1150
1151 ! Determine the actual streamfunction at each interface.
11526446880 Sfn_est = (Z_to_H*Sfn_unlim_u(I,K) + slope2_Ratio_u(I,K)*Sfn_safe) / (1.0 + slope2_Ratio_u(I,K))
1153 else ! When use_EOS is false, the layers are constant density.
11540 Sfn_est = Z_to_H*Sfn_unlim_u(I,K)
1155 endif
1156
1157 ! Make sure that there is enough mass above to allow the streamfunction
1158 ! to satisfy the boundary condition of 0 at the surface.
11596446880 Sfn_in_H = min(max(Sfn_est, -h_avail_rsum(i,j,K)), h_avail_rsum(i+1,j,K))
1160
1161 ! The actual transport is limited by the mass available in the two
1162 ! neighboring grid cells.
1163 uhD(I,j,k) = max(min((Sfn_in_H - uhtot(I,j)), h_avail(i,j,k)), &
11646446880 -h_avail(i+1,j,k))
1165
11666446880 if (CS%id_sfn_x>0) diag_sfn_x(I,j,K) = diag_sfn_x(I,j,K+1) + uhD(I,j,k)
1167! sfn_x(I,j,K) = max(min(Sfn_in_h, uhtot(I,j)+h_avail(i,j,k)), &
1168! uhtot(I,j)-h_avail(i+1,j,K))
1169! sfn_slope_x(I,j,K) = max(uhtot(I,j)-h_avail(i+1,j,k), &
1170! min(uhtot(I,j)+h_avail(i,j,k), &
1171! min(h_avail_rsum(i+1,j,K), max(-h_avail_rsum(i,j,K), &
1172! (KH_u(I,j,K)*G%dy_Cu(I,j)) * ((e(i,j,K)-e(i+1,j,K))*G%IdxCu(I,j)) )) ))
1173 else ! k <= nk_linear
1174 ! Balance the deeper flow with a return flow uniformly distributed
1175 ! though the remaining near-surface layers. This is the same as
1176 ! using Sfn_safe above. There is no need to apply the limiters in
1177 ! this case.
11780 if (uhtot(I,j) <= 0.0) then
11790 uhD(I,j,k) = -uhtot(I,j) * h_frac(i,j,k)
1180 else ! (uhtot(I,j) > 0.0)
11810 uhD(I,j,k) = -uhtot(I,j) * h_frac(i+1,j,k)
1182 endif
1183
11840 if (CS%id_sfn_x>0) diag_sfn_x(I,j,K) = diag_sfn_x(I,j,K+1) + uhD(I,j,k)
1185! if (sfn_slope_x(I,j,K+1) <= 0.0) then
1186! sfn_slope_x(I,j,K) = sfn_slope_x(I,j,K+1) * (1.0 - h_frac(i,j,k))
1187! else
1188! sfn_slope_x(I,j,K) = sfn_slope_x(I,j,K+1) * (1.0 - h_frac(i+1,j,k))
1189! endif
1190
1191 endif
1192
11936446880 uhtot(I,j) = uhtot(I,j) + uhD(I,j,k)
1194
11956500160 if (find_work) then
1196 ! This is the energy tendency based on the original profiles, and does
1197 ! not include any nonlinear terms due to a finite time step (which would
1198 ! involve interactions between the fluxes through the different faces.
1199 ! A second order centered estimate is used for the density transferred
1200 ! between water columns.
1201
12026446880 if (allocated(tv%SpV_avg)) then
12030 G_scale = GV%H_to_RZ * GV%g_Earth / Rho_avg
1204 else
12056446880 G_scale = GV%g_Earth * GV%H_to_Z
1206 endif
1207
1208 Work_u(I,j) = Work_u(I,j) + G_scale * &
1209 ( uhtot(I,j) * drdkDe_u(I,K) - &
1210 (uhD(I,j,k) * drdi_u(I,k)) * 0.25 * &
12116446880 ((e(i,j,K) + e(i,j,K+1)) + (e(i+1,j,K) + e(i+1,j,K+1))) )
1212 endif
1213
1214 enddo
1215 enddo ! end of k-loop
1216 enddo ! end of j-loop
1217
1218 ! Calculate the meridional fluxes and gradients.
1219
1220 !$OMP parallel do default(none) shared(nz,is,ie,js,je,find_work,use_EOS,G,GV,US,pres,T,S,dz, &
1221 !$OMP nk_linear,IsdB,tv,h,h_neglect,e,dz_neglect,dz_neglect2, &
1222 !$OMP h_neglect2,int_slope_v,KH_v,vhtot,h_frac,h_avail_rsum, &
1223 !$OMP I_slope_max2,vhD,h_avail,Work_v,CS,slope_y,cg1,hn_2,&
1224 !$OMP diag_sfn_y,diag_sfn_unlim_y,N2_floor,EOSdom_v,use_stanley,&
1225 !$OMP present_slope_y,G_rho0,Slope_y_PE,hN2_y_PE) &
1226 !$OMP private(drdjA,drdjB,drdkL,drdkR,pres_v,T_v,S_v,S_h,S_hr, &
1227 !$OMP drho_dT_v,drho_dS_v,hg2A,hg2B,hg2L,hg2R,haA,G_scale, &
1228 !$OMP drho_dT_dT_h,drho_dT_dT_hr,scrap,pres_h,T_h,T_hr, &
1229 !$OMP haB,haL,haR,dzaL,dzaR,wtA,wtB,wtL,wtR,drdz,pres_hr, &
1230 !$OMP dzg2A,dzg2B,dzaA,dzaB,dz_harm,Z_to_H, &
1231 !$OMP drdy,mag_grad2,Slope,slope2_Ratio_v,dzN2_v,N2_unlim, &
1232 !$OMP Sfn_unlim_v,Rho_avg,drdj_v,drdkDe_v,c2_dz_v, &
1233 !$OMP Sfn_safe,Sfn_est,Sfn_in_h,calc_derivatives)
1234744 do J=js-1,je
123554900 do K=nz,2,-1
123654168 if (find_work .and. .not.(use_EOS)) then
12370 drdjA = 0.0 ; drdjB = 0.0
12380 drdkL = GV%Rlay(k) - GV%Rlay(k-1) ; drdkR = drdkL
1239 endif
1240
1241 calc_derivatives = use_EOS .and. (k >= nk_linear) .and. &
124254168 (find_work .or. .not. present_slope_y .or. CS%use_FGNV_streamfn .or. use_stanley)
1243
124454168 if (calc_derivatives) then
12456554328 do i=is,ie
12466500160 pres_v(i) = 0.5*(pres(i,j,K) + pres(i,j+1,K))
12476500160 T_v(i) = 0.25*((T(i,j,k) + T(i,j+1,k)) + (T(i,j,k-1) + T(i,j+1,k-1)))
12486554328 S_v(i) = 0.25*((S(i,j,k) + S(i,j+1,k)) + (S(i,j,k-1) + S(i,j+1,k-1)))
1249 enddo
1250 call calculate_density_derivs(T_v, S_v, pres_v, drho_dT_v, drho_dS_v, &
125154168 tv%eqn_of_state, EOSdom_v)
1252 endif
125354168 if (use_stanley) then
12540 do i=is,ie
12550 pres_h(i) = pres(i,j,K)
12560 T_h(i) = 0.5*(T(i,j,k) + T(i,j,k-1))
12570 S_h(i) = 0.5*(S(i,j,k) + S(i,j,k-1))
1258
12590 pres_hr(i) = pres(i,j+1,K)
12600 T_hr(i) = 0.5*(T(i,j+1,k) + T(i,j+1,k-1))
12610 S_hr(i) = 0.5*(S(i,j+1,k) + S(i,j+1,k-1))
1262 enddo
1263
1264 ! The second line below would correspond to arguments
1265 ! drho_dS_dS, drho_dS_dT, drho_dT_dT, drho_dS_dP, drho_dT_dP, &
1266 call calculate_density_second_derivs(T_h, S_h, pres_h, &
1267 scrap, scrap, drho_dT_dT_h, scrap, scrap, &
12680 tv%eqn_of_state, EOSdom_v)
1269 call calculate_density_second_derivs(T_hr, S_hr, pres_hr, &
1270 scrap, scrap, drho_dT_dT_hr, scrap, scrap, &
12710 tv%eqn_of_state, EOSdom_v)
1272 endif
12736555060 do i=is,ie
12746500160 if (calc_derivatives) then
1275 ! Estimate the horizontal density gradients along layers.
1276 drdjA = drho_dT_v(i) * (T(i,j+1,k-1)-T(i,j,k-1)) + &
12776500160 drho_dS_v(i) * (S(i,j+1,k-1)-S(i,j,k-1))
1278 drdjB = drho_dT_v(i) * (T(i,j+1,k)-T(i,j,k)) + &
12796500160 drho_dS_v(i) * (S(i,j+1,k)-S(i,j,k))
1280
1281 ! Estimate the vertical density gradients times the grid spacing.
1282 drdkL = (drho_dT_v(i) * (T(i,j,k)-T(i,j,k-1)) + &
12836500160 drho_dS_v(i) * (S(i,j,k)-S(i,j,k-1)))
1284 drdkR = (drho_dT_v(i) * (T(i,j+1,k)-T(i,j+1,k-1)) + &
12856500160 drho_dS_v(i) * (S(i,j+1,k)-S(i,j+1,k-1)))
12866500160 drdkDe_v(i,K) = (drdkR * e(i,j+1,K)) - (drdkL * e(i,j,K))
12870 elseif (find_work) then ! This is used in pure stacked SW mode
12880 drdkDe_v(i,K) = (drdkR * e(i,j+1,K)) - (drdkL * e(i,j,K))
1289 endif
12906500160 if (use_stanley) then
1291 ! Correction to the horizontal density gradient due to nonlinearity in
1292 ! the EOS rectifying SGS temperature anomalies
1293 drdjA = drdjA + 0.5 * ((drho_dT_dT_hr(i) * tv%varT(i,j+1,k-1)) - &
12940 (drho_dT_dT_h(i) * tv%varT(i,j,k-1)) )
1295 drdjB = drdjB + 0.5 * ((drho_dT_dT_hr(i) * tv%varT(i,j+1,k)) - &
12960 (drho_dT_dT_h(i) * tv%varT(i,j,k)) )
1297 endif
1298
12996500160 if (find_work) drdj_v(i,k) = drdjB
1300
13016500160 if (k > nk_linear) then
13026500160 if (use_EOS) then
13036500160 if (CS%use_FGNV_streamfn .or. find_work .or. .not. present_slope_y) then
13046500160 hg2L = h(i,j,k-1)*h(i,j,k) + h_neglect2
13056500160 hg2R = h(i,j+1,k-1)*h(i,j+1,k) + h_neglect2
13066500160 haL = 0.5*(h(i,j,k-1) + h(i,j,k)) + h_neglect
13076500160 haR = 0.5*(h(i,j+1,k-1) + h(i,j+1,k)) + h_neglect
1308
13096500160 if (GV%Boussinesq) then
13106500160 dzaL = haL * GV%H_to_Z ; dzaR = haR * GV%H_to_Z
13110 elseif (GV%semi_Boussinesq) then
13120 dzaL = 0.5*(e(i,j,K-1) - e(i,j,K+1)) + dz_neglect
13130 dzaR = 0.5*(e(i,j+1,K-1) - e(i,j+1,K+1)) + dz_neglect
1314 else
13150 dzaL = 0.5*(dz(i,j,k-1) + dz(i,j,k)) + dz_neglect
13160 dzaR = 0.5*(dz(i,j+1,k-1) + dz(i,j+1,k)) + dz_neglect
1317 endif
1318 ! Use the harmonic mean thicknesses to weight the horizontal gradients.
1319 ! These unnormalized weights have been rearranged to minimize divisions.
13206500160 wtL = hg2L*(haR*dzaR) ; wtR = hg2R*(haL*dzaL)
1321
13226500160 drdz = ((wtL * drdkL) + (wtR * drdkR)) / ((dzaL*wtL) + (dzaR*wtR))
1323 ! The expression for drdz above is mathematically equivalent to:
1324 ! drdz = ((hg2L/haL) * drdkL/dzaL + (hg2R/haR) * drdkR/dzaR) / &
1325 ! ((hg2L/haL) + (hg2R/haR))
13266500160 hg2A = h(i,j,k-1)*h(i,j+1,k-1) + h_neglect2
13276500160 hg2B = h(i,j,k)*h(i,j+1,k) + h_neglect2
13286500160 haA = 0.5*(h(i,j,k-1) + h(i,j+1,k-1)) + h_neglect
13296500160 haB = 0.5*(h(i,j,k) + h(i,j+1,k)) + h_neglect
1330
13316500160 if (GV%Boussinesq) then
13326500160 N2_unlim = drdz*G_rho0
1333 else
1334 N2_unlim = (GV%g_Earth*GV%RZ_to_H) * &
13350 (((wtL * drdkL) + (wtR * drdkR)) / ((haL*wtL) + (haR*wtR)))
1336 endif
1337
13386500160 dzg2A = dz(i,j,k-1)*dz(i,j+1,k-1) + dz_neglect2
13396500160 dzg2B = dz(i,j,k)*dz(i,j+1,k) + dz_neglect2
13406500160 dzaA = 0.5*(dz(i,j,k-1) + dz(i,j+1,k-1)) + dz_neglect
13416500160 dzaB = 0.5*(dz(i,j,k) + dz(i,j+1,k)) + dz_neglect
1342
1343 ! dzN2_v is used with the FGNV streamfunction formulation
13446500160 dzN2_v(i,K) = (0.5*( dzg2A / dzaA + dzg2B / dzaB )) * max(N2_unlim, N2_floor)
13456500160 if (find_work .and. CS%GM_src_alt) &
13460 hN2_y_PE(i,J,k) = (0.5*( hg2A / haA + hg2B / haB )) * max(N2_unlim, N2_floor)
1347 endif
13486500160 if (present_slope_y) then
13496500160 Slope = slope_y(i,J,k)
13506500160 slope2_Ratio_v(i,K) = Slope**2 * I_slope_max2
1351 else
1352 ! Use the harmonic mean thicknesses to weight the horizontal gradients.
1353 ! These unnormalized weights have been rearranged to minimize divisions.
13540 wtA = hg2A*haB ; wtB = hg2B*haA
1355 ! This is the gradient of density along geopotentials.
1356 drdy = ((wtA * drdjA + wtB * drdjB) / (wtA + wtB) - &
13570 drdz * (e(i,j,K)-e(i,j+1,K))) * G%IdyCv(i,J)
1358
1359 ! This estimate of slope is accurate for small slopes, but bounded
1360 ! to be between -1 and 1.
13610 mag_grad2 = (US%Z_to_L*drdy)**2 + drdz**2
13620 if (mag_grad2 > 0.0) then
13630 Slope = drdy / sqrt(mag_grad2)
13640 slope2_Ratio_v(i,K) = Slope**2 * I_slope_max2
1365 else ! Just in case mag_grad2 = 0 ever.
13660 Slope = 0.0
13670 slope2_Ratio_v(i,K) = 1.0e20 ! Force the use of the safe streamfunction.
1368 endif
1369 endif
1370
1371 ! Adjust real slope by weights that bias towards slope of interfaces
1372 ! that ignore density gradients along layers.
1373 Slope = (1.0 - int_slope_v(i,J,K)) * Slope + &
13746500160 int_slope_v(i,J,K) * ((e(i,j+1,K)-e(i,j,K)) * G%IdyCv(i,J))
13756500160 slope2_Ratio_v(i,K) = (1.0 - int_slope_v(i,J,K)) * slope2_Ratio_v(i,K)
1376
13776500160 if (CS%MEKE_src_slope_bug) then
13780 Slope_y_PE(i,J,k) = MIN(Slope, CS%slope_max)
1379 else
13806500160 Slope_y_PE(i,J,k) = Slope
13816500160 if (Slope > CS%slope_max) Slope_y_PE(i,J,k) = CS%slope_max
13826500160 if (Slope < -CS%slope_max) Slope_y_PE(i,J,k) = -CS%slope_max
1383 endif
13846500160 if (CS%id_slope_y > 0) CS%diagSlopeY(I,j,k) = Slope
1385
13866500160 Sfn_unlim_v(i,K) = -((KH_v(i,J,K)*G%dx_Cv(i,J))*Slope)
1387
1388 ! Avoid moving dense water upslope from below the level of
1389 ! the bottom on the receiving side.
13906500160 if (Sfn_unlim_v(i,K) > 0.0) then ! The flow below this interface is positive.
13910 if (e(i,j,K) < e(i,j+1,nz+1)) then
13920 Sfn_unlim_v(i,K) = 0.0 ! This is not vhtot, because it may compensate for
1393 ! deeper flow in very unusual cases.
13940 elseif (e(i,j+1,nz+1) > e(i,j,K+1)) then
1395 ! Scale the transport with the fraction of the donor layer above
1396 ! the bottom on the receiving side.
1397 Sfn_unlim_v(i,K) = Sfn_unlim_v(i,K) * ((e(i,j,K) - e(i,j+1,nz+1)) / &
13980 ((e(i,j,K) - e(i,j,K+1)) + dz_neglect))
1399 endif
1400 else
14016500160 if (e(i,j+1,K) < e(i,j,nz+1)) then ; Sfn_unlim_v(i,K) = 0.0
14025904816 elseif (e(i,j,nz+1) > e(i,j+1,K+1)) then
1403 Sfn_unlim_v(i,K) = Sfn_unlim_v(i,K) * ((e(i,j+1,K) - e(i,j,nz+1)) / &
140421144 ((e(i,j+1,K) - e(i,j+1,K+1)) + dz_neglect))
1405 endif
1406 endif
1407
1408 else ! .not. use_EOS
14090 if (present_slope_y) then
14100 Slope = slope_y(i,J,k)
1411 else
14120 Slope = (e(i,j+1,K)-e(i,j,K)) * G%IdyCv_OBCmask(i,J)
1413 endif
14140 if (CS%id_slope_y > 0) CS%diagSlopeY(I,j,k) = Slope
14150 Sfn_unlim_v(i,K) = -((KH_v(i,J,K)*G%dx_Cv(i,J))*Slope)
14160 dzN2_v(i,K) = GV%g_prime(K)
1417 endif ! if (use_EOS)
1418 else ! if (k > nk_linear)
14190 dzN2_v(i,K) = N2_floor * dz_neglect
14200 Sfn_unlim_v(i,K) = 0.
1421 endif ! if (k > nk_linear)
14226554328 if (CS%id_sfn_unlim_y>0) diag_sfn_unlim_y(i,J,K) = Sfn_unlim_v(i,K)
1423 enddo ! i-loop
1424 enddo ! k-loop
1425
1426732 if (CS%use_FGNV_streamfn) then
14270 do k=1,nz ; do i=is,ie ; if (G%OBCmaskCv(i,J)>0.) then
1428 dz_harm = max( dz_neglect, &
14290 2. * dz(i,j,k) * dz(i,j+1,k) / ( ( dz(i,j,k) + dz(i,j+1,k) ) + dz_neglect ) )
14300 c2_dz_v(i,k) = CS%FGNV_scale * ( 0.5*( cg1(i,j) + cg1(i,j+1) ) )**2 / dz_harm
1431 endif ; enddo ; enddo
1432
1433 ! Solve an elliptic equation for the streamfunction following Ferrari et al., 2010.
14340 do i=is,ie
14350 if (G%OBCmaskCv(i,J)>0.) then
14360 do K=2,nz
14370 Sfn_unlim_v(i,K) = (1. + CS%FGNV_scale) * Sfn_unlim_v(i,K)
1438 enddo
14390 call streamfn_solver(nz, c2_dz_v(i,:), dzN2_v(i,:), Sfn_unlim_v(i,:))
1440 else
14410 do K=2,nz
14420 Sfn_unlim_v(i,K) = 0.
1443 enddo
1444 endif
1445 enddo
1446 endif
1447
144854912 do K=nz,2,-1
14496555060 do i=is,ie
14506500160 if (allocated(tv%SpV_avg) .and. (find_work .or. (k > nk_linear)) ) then
1451 Rho_avg = ( ((h(i,j,k) + h(i,j,k-1)) + (h(i,j+1,k) + h(i,j+1,k-1))) + 4.0*hn_2 ) / &
1452 ( (((h(i,j,k)+hn_2) * tv%SpV_avg(i,j,k)) + ((h(i,j,k-1)+hn_2) * tv%SpV_avg(i,j,k-1))) + &
14530 (((h(i,j+1,k)+hn_2)*tv%SpV_avg(i,j+1,k)) + ((h(i,j+1,k-1)+hn_2)*tv%SpV_avg(i,j+1,k-1))) )
1454 ! Use an average density to convert the volume streamfunction estimate into a mass streamfunction.
14550 Z_to_H = GV%RZ_to_H*Rho_avg
1456 else
14576500160 Z_to_H = GV%Z_to_H
1458 endif
1459
14606500160 if (k > nk_linear) then
14616500160 if (use_EOS) then
1462
14636500160 if (vhtot(i,J) <= 0.0) then
1464 ! The transport that must balance the transport below is positive.
14656500160 Sfn_safe = vhtot(i,J) * (1.0 - h_frac(i,j,k))
1466 else ! (vhtot(I,j) > 0.0)
14670 Sfn_safe = vhtot(i,J) * (1.0 - h_frac(i,j+1,k))
1468 endif
1469
1470 ! Find the actual streamfunction at each interface.
14716500160 Sfn_est = (Z_to_H*Sfn_unlim_v(i,K) + slope2_Ratio_v(i,K)*Sfn_safe) / (1.0 + slope2_Ratio_v(i,K))
1472 else ! When use_EOS is false, the layers are constant density.
14730 Sfn_est = Z_to_H*Sfn_unlim_v(i,K)
1474 endif
1475
1476 ! Make sure that there is enough mass above to allow the streamfunction
1477 ! to satisfy the boundary condition of 0 at the surface.
14786500160 Sfn_in_H = min(max(Sfn_est, -h_avail_rsum(i,j,K)), h_avail_rsum(i,j+1,K))
1479
1480 ! The actual transport is limited by the mass available in the two
1481 ! neighboring grid cells.
14826500160 vhD(i,J,k) = max(min((Sfn_in_H - vhtot(i,J)), h_avail(i,j,k)), -h_avail(i,j+1,k))
1483
14846500160 if (CS%id_sfn_y>0) diag_sfn_y(i,J,K) = diag_sfn_y(i,J,K+1) + vhD(i,J,k)
1485! sfn_y(i,J,K) = max(min(Sfn_in_h, vhtot(i,J)+h_avail(i,j,k)), &
1486! vhtot(i,J)-h_avail(i,j+1,k))
1487! sfn_slope_y(i,J,K) = max(vhtot(i,J)-h_avail(i,j+1,k), &
1488! min(vhtot(i,J)+h_avail(i,j,k), &
1489! min(h_avail_rsum(i,j+1,K), max(-h_avail_rsum(i,j,K), &
1490! (KH_v(i,J,K)*G%dx_Cv(i,J)) * ((e(i,j,K)-e(i,j+1,K))*G%IdyCv(i,J)) )) ))
1491 else ! k <= nk_linear
1492 ! Balance the deeper flow with a return flow uniformly distributed
1493 ! though the remaining near-surface layers. This is the same as
1494 ! using Sfn_safe above. There is no need to apply the limiters in
1495 ! this case.
14960 if (vhtot(i,J) <= 0.0) then
14970 vhD(i,J,k) = -vhtot(i,J) * h_frac(i,j,k)
1498 else ! (vhtot(i,J) > 0.0)
14990 vhD(i,J,k) = -vhtot(i,J) * h_frac(i,j+1,k)
1500 endif
1501
15020 if (CS%id_sfn_y>0) diag_sfn_y(i,J,K) = diag_sfn_y(i,J,K+1) + vhD(i,J,k)
1503! if (sfn_slope_y(i,J,K+1) <= 0.0) then
1504! sfn_slope_y(i,J,K) = sfn_slope_y(i,J,K+1) * (1.0 - h_frac(i,j,k))
1505! else
1506! sfn_slope_y(i,J,K) = sfn_slope_y(i,J,K+1) * (1.0 - h_frac(i,j+1,k))
1507! endif
1508 endif
1509
15106500160 vhtot(i,J) = vhtot(i,J) + vhD(i,J,k)
1511
15126554328 if (find_work) then
1513 ! This is the energy tendency based on the original profiles, and does
1514 ! not include any nonlinear terms due to a finite time step (which would
1515 ! involve interactions between the fluxes through the different faces.
1516 ! A second order centered estimate is used for the density transferred
1517 ! between water columns.
1518
15196500160 if (allocated(tv%SpV_avg)) then
15200 G_scale = GV%H_to_RZ * GV%g_Earth / Rho_avg
1521 else
15226500160 G_scale = GV%g_Earth * GV%H_to_Z
1523 endif
1524
1525 Work_v(i,J) = Work_v(i,J) + G_scale * &
1526 ( vhtot(i,J) * drdkDe_v(i,K) - &
1527 (vhD(i,J,k) * drdj_v(i,k)) * 0.25 * &
15286500160 ((e(i,j,K) + e(i,j,K+1)) + (e(i,j+1,K) + e(i,j+1,K+1))) )
1529 endif
1530
1531 enddo
1532 enddo ! end of k-loop
1533 enddo ! end of j-loop
1534
1535 ! In layer 1, enforce the boundary conditions that Sfn(z=0) = 0.0
153612 if (.not.find_work .or. .not.(use_EOS)) then
15370 do j=js,je ; do I=is-1,ie ; uhD(I,j,1) = -uhtot(I,j) ; enddo ; enddo
15380 do J=js-1,je ; do i=is,ie ; vhD(i,J,1) = -vhtot(i,J) ; enddo ; enddo
1539 else
154012 EOSdom_u(1) = (is-1) - (G%IsdB-1) ; EOSdom_u(2) = ie - (G%IsdB-1)
1541 !$OMP parallel do default(shared) private(pres_u,T_u,S_u,drho_dT_u,drho_dS_u,drdiB,G_scale)
1542732 do j=js,je
1543720 if (use_EOS) then
154487840 do I=is-1,ie
154587120 pres_u(I) = 0.5*(pres(i,j,1) + pres(i+1,j,1))
154687120 T_u(I) = 0.5*(T(i,j,1) + T(i+1,j,1))
154787840 S_u(I) = 0.5*(S(i,j,1) + S(i+1,j,1))
1548 enddo
1549 call calculate_density_derivs(T_u, S_u, pres_u, drho_dT_u, drho_dS_u, &
1550720 tv%eqn_of_state, EOSdom_u )
1551 endif
155287852 do I=is-1,ie
155387120 uhD(I,j,1) = -uhtot(I,j)
1554
155587120 G_scale = GV%g_Earth * GV%H_to_Z
155687120 if (use_EOS) then
1557 drdiB = drho_dT_u(I) * (T(i+1,j,1)-T(i,j,1)) + &
155887120 drho_dS_u(I) * (S(i+1,j,1)-S(i,j,1))
155987120 if (allocated(tv%SpV_avg)) then
1560 G_scale = GV%H_to_RZ * GV%g_Earth * &
1561 ( ( ((h(i,j,1)+hn_2) * tv%SpV_avg(i,j,1)) + ((h(i+1,j,1)+hn_2) * tv%SpV_avg(i+1,j,1)) ) / &
15620 ( (h(i,j,1) + h(i+1,j,1)) + 2.0*hn_2 ) )
1563 endif
1564 endif
156587840 if (CS%use_GM_work_bug) then
1566 Work_u(I,j) = Work_u(I,j) + G_scale * &
1567 ( (uhD(I,j,1) * drdiB) * 0.25 * &
15680 ((e(i,j,1) + e(i,j,2)) + (e(i+1,j,1) + e(i+1,j,2))) )
1569 else
1570 Work_u(I,j) = Work_u(I,j) - G_scale * &
1571 ( (uhD(I,j,1) * drdiB) * 0.25 * &
157287120 ((e(i,j,1) + e(i,j,2)) + (e(i+1,j,1) + e(i+1,j,2))) )
1573 endif
1574 enddo
1575 enddo
1576
157712 EOSdom_v(:) = EOS_domain(G%HI)
1578 !$OMP parallel do default(shared) private(pres_v,T_v,S_v,drho_dT_v,drho_dS_v,drdjB,G_scale)
1579744 do J=js-1,je
1580732 if (use_EOS) then
158188572 do i=is,ie
158287840 pres_v(i) = 0.5*(pres(i,j,1) + pres(i,j+1,1))
158387840 T_v(i) = 0.5*(T(i,j,1) + T(i,j+1,1))
158488572 S_v(i) = 0.5*(S(i,j,1) + S(i,j+1,1))
1585 enddo
1586 call calculate_density_derivs(T_v, S_v, pres_v, drho_dT_v, drho_dS_v, &
1587732 tv%eqn_of_state, EOSdom_v)
1588 endif
158988584 do i=is,ie
159087840 vhD(i,J,1) = -vhtot(i,J)
1591
159287840 G_scale = GV%g_Earth * GV%H_to_Z
159387840 if (use_EOS) then
1594 drdjB = drho_dT_v(i) * (T(i,j+1,1)-T(i,j,1)) + &
159587840 drho_dS_v(i) * (S(i,j+1,1)-S(i,j,1))
159687840 if (allocated(tv%SpV_avg)) then
1597 G_scale = GV%H_to_RZ * GV%g_Earth * &
1598 ( ( ((h(i,j,1)+hn_2) * tv%SpV_avg(i,j,1)) + ((h(i,j+1,1)+hn_2) * tv%SpV_avg(i,j+1,1)) ) / &
15990 ( (h(i,j,1) + h(i,j+1,1)) + 2.0*hn_2 ) )
1600 endif
1601 endif
1602 Work_v(i,J) = Work_v(i,J) - G_scale * &
1603 ( (vhD(i,J,1) * drdjB) * 0.25 * &
160488572 ((e(i,j,1) + e(i,j,2)) + (e(i,j+1,1) + e(i,j+1,2))) )
1605 enddo
1606 enddo
1607 endif
1608
160987132 if (find_work) then ; do j=js,je ; do i=is,ie
1610 ! Note that the units of Work_v and Work_u are [R Z L4 T-3 ~> W], while Work_h is in [R Z L2 T-3 ~> W m-2].
1611 Work_h = 0.5 * G%IareaT(i,j) * &
161286400 ((Work_u(I-1,j) + Work_u(I,j)) + (Work_v(i,J-1) + Work_v(i,J)))
161386400 if (allocated(CS%GMwork)) CS%GMwork(i,j) = Work_h
161486400 if (.not. CS%GM_src_alt) then ; if (allocated(MEKE%GM_src)) then
161586400 MEKE%GM_src(i,j) = MEKE%GM_src(i,j) + Work_h
1616 endif ; endif
161787120 if (skeb_use_gm) then
16180 skeb_gm_work(i,j) = STOCH%skeb_gm_coef * Work_h
16190 skeb_ebt_norm2(i,j) = 0.0
16200 do k=1,nz
16210 skeb_ebt_norm2(i,j) = skeb_ebt_norm2(i,j) + h(i,j,k) * VarMix%ebt_struct(i,j,k)**2
1622 enddo
16230 skeb_ebt_norm2(i,j) = GV%H_to_RZ * (skeb_ebt_norm2(i,j) + h_neglect)
1624 endif
1625 enddo ; enddo ; endif
1626
162712 if (skeb_use_gm) then
1628 ! This block spreads the GM work down through the column using the ebt vertical structure, squared.
1629 ! Note the sign convention.
16300 do k=1,nz ; do j=js,je ; do i=is,ie
1631 STOCH%skeb_diss(i,j,k) = STOCH%skeb_diss(i,j,k) - skeb_gm_work(i,j) * &
16320 VarMix%ebt_struct(i,j,k)**2 / skeb_ebt_norm2(i,j)
1633 enddo ; enddo ; enddo
1634 endif
1635
163612 if (find_work .and. CS%GM_src_alt) then ; if (allocated(MEKE%GM_src)) then
16370 if (CS%MEKE_src_answer_date >= 20240601) then
16380 do j=js,je ; do i=is,ie ; do k=nz,1,-1
1639 PE_release_h = -0.25 * GV%H_to_RZ * &
1640 ( ((KH_u(I,j,k)*(Slope_x_PE(I,j,k)**2) * hN2_x_PE(I,j,k)) + &
1641 (Kh_u(I-1,j,k)*(Slope_x_PE(I-1,j,k)**2) * hN2_x_PE(I-1,j,k))) + &
1642 ((Kh_v(i,J,k)*(Slope_y_PE(i,J,k)**2) * hN2_y_PE(i,J,k)) + &
16430 (Kh_v(i,J-1,k)*(Slope_y_PE(i,J-1,k)**2) * hN2_y_PE(i,J-1,k))) )
16440 MEKE%GM_src(i,j) = MEKE%GM_src(i,j) + PE_release_h
1645 enddo ; enddo ; enddo
1646 else
16470 do j=js,je ; do i=is,ie ; do k=nz,1,-1
1648 PE_release_h = -0.25 * GV%H_to_RZ * &
1649 ((KH_u(I,j,k)*(Slope_x_PE(I,j,k)**2) * hN2_x_PE(I,j,k)) + &
1650 (Kh_u(I-1,j,k)*(Slope_x_PE(I-1,j,k)**2) * hN2_x_PE(I-1,j,k)) + &
1651 (Kh_v(i,J,k)*(Slope_y_PE(i,J,k)**2) * hN2_y_PE(i,J,k)) + &
16520 (Kh_v(i,J-1,k)*(Slope_y_PE(i,J-1,k)**2) * hN2_y_PE(i,J-1,k)))
16530 MEKE%GM_src(i,j) = MEKE%GM_src(i,j) + PE_release_h
1654 enddo ; enddo ; enddo
1655 endif
1656
16570 if (CS%debug) then
16580 call hchksum(MEKE%GM_src, 'MEKE%GM_src', G%HI, unscale=US%RZ3_T3_to_W_m2*US%L_to_Z**2)
1659 call uvchksum("KH_[uv]", Kh_u, Kh_v, G%HI, unscale=US%L_to_m**2*US%s_to_T, &
16600 scalar_pair=.true.)
16610 call uvchksum("Slope_[xy]_PE", Slope_x_PE, Slope_y_PE, G%HI, unscale=US%Z_to_L)
1662 call uvchksum("hN2_[xy]_PE", hN2_x_PE, hN2_y_PE, G%HI, unscale=GV%H_to_mks*US%L_to_Z**2*US%s_to_T**2, &
16630 scalar_pair=.true.)
1664 endif
1665 endif ; endif
1666
166712 if (CS%id_slope_x > 0) call post_data(CS%id_slope_x, CS%diagSlopeX, CS%diag)
166812 if (CS%id_slope_y > 0) call post_data(CS%id_slope_y, CS%diagSlopeY, CS%diag)
166912 if (CS%id_sfn_x > 0) call post_data(CS%id_sfn_x, diag_sfn_x, CS%diag)
167012 if (CS%id_sfn_y > 0) call post_data(CS%id_sfn_y, diag_sfn_y, CS%diag)
167112 if (CS%id_sfn_unlim_x > 0) call post_data(CS%id_sfn_unlim_x, diag_sfn_unlim_x, CS%diag)
167212 if (CS%id_sfn_unlim_y > 0) call post_data(CS%id_sfn_unlim_y, diag_sfn_unlim_y, CS%diag)
1673
167436end subroutine thickness_diffuse_full
1675
1676!> Tridiagonal solver for streamfunction at interfaces
16770subroutine streamfn_solver(nk, c2_h, hN2, sfn)
1678 integer, intent(in) :: nk !< Number of layers
1679 real, dimension(nk), intent(in) :: c2_h !< Wave speed squared over thickness in layers, rescaled to
1680 !! [H L2 Z-2 T-2 ~> m s-2 or kg m-2 s-2]
1681 real, dimension(nk+1), intent(in) :: hN2 !< Thickness times N2 at interfaces times rescaling factors
1682 !! [H L2 Z-2 T-2 ~> m s-2 or kg m-2 s-2]
1683 real, dimension(nk+1), intent(inout) :: sfn !< Streamfunction [H L2 T-1 ~> m3 s-1 or kg s-1] or arbitrary units
1684 !! On entry, equals diffusivity times slope.
1685 !! On exit, equals the streamfunction.
1686 ! Local variables
16870 real :: c1(nk) ! The dependence of the final streamfunction on the values below [nondim]
1688 real :: d1 ! The complement of c1(k) (i.e., 1 - c1(k)) [nondim]
1689 real :: b_denom ! A term in the denominator of beta [H L2 Z-2 T-2 ~> m s-2 or kg m-2 s-2]
1690 real :: beta ! The normalization for the pivot [Z2 T2 H-1 L-2 ~> s2 m-1 or m2 s2 kg-1]
1691 integer :: k
1692
16930 sfn(1) = 0.
16940 b_denom = hN2(2) + c2_h(1)
16950 beta = 1.0 / ( b_denom + c2_h(2) )
16960 d1 = beta * b_denom
16970 sfn(2) = ( beta * hN2(2) )*sfn(2)
16980 do K=3,nk
16990 c1(k-1) = beta * c2_h(k-1)
17000 b_denom = hN2(K) + d1*c2_h(k-1)
17010 beta = 1.0 / (b_denom + c2_h(k))
17020 d1 = beta * b_denom
17030 sfn(K) = beta * (hN2(K)*sfn(K) + c2_h(k-1)*sfn(K-1))
1704 enddo
17050 c1(nk) = beta * c2_h(nk)
17060 sfn(nk+1) = 0.
17070 do K=nk,2,-1
17080 sfn(K) = sfn(K) + c1(k)*sfn(K+1)
1709 enddo
1710
17110end subroutine streamfn_solver
1712
1713!> Add a diffusivity that acts on the isopycnal heights, regardless of the densities
17140subroutine add_interface_Kh(G, GV, US, CS, Kh_u, Kh_v, Kh_u_CFL, Kh_v_CFL, int_slope_u, int_slope_v)
1715 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
1716 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
1717 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1718 type(thickness_diffuse_CS), intent(in) :: CS !< Control structure for thickness_diffuse
1719 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: Kh_u !< Isopycnal height diffusivity
1720 !! at u points [L2 T-1 ~> m2 s-1]
1721 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1), intent(inout) :: Kh_v !< Isopycnal height diffusivity
1722 !! at v points [L2 T-1 ~> m2 s-1]
1723 real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: Kh_u_CFL !< Maximum stable isopycnal height
1724 !! diffusivity at u points [L2 T-1 ~> m2 s-1]
1725 real, dimension(SZI_(G),SZJB_(G)), intent(in) :: Kh_v_CFL !< Maximum stable isopycnal height
1726 !! diffusivity at v points [L2 T-1 ~> m2 s-1]
1727 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: int_slope_u !< Ratio that determine how much of
1728 !! the isopycnal slopes are taken directly from
1729 !! the interface slopes without consideration
1730 !! of density gradients [nondim].
1731 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1), intent(inout) :: int_slope_v !< Ratio that determine how much of
1732 !! the isopycnal slopes are taken directly from
1733 !! the interface slopes without consideration
1734 !! of density gradients [nondim].
1735
1736 ! Local variables
1737 integer :: i, j, k, is, ie, js, je, nz
1738
17390 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
1740
17410 do k=1,nz+1 ; do j=js,je ; do I=is-1,ie ; if (CS%Kh_eta_u(I,j) > 0.0) then
1742 int_slope_u(I,j,K) = (int_slope_u(I,j,K)*Kh_u(I,j,K) + CS%Kh_eta_u(I,j)) / &
17430 (Kh_u(I,j,K) + CS%Kh_eta_u(I,j))
17440 Kh_u(I,j,K) = min(Kh_u(I,j,K) + CS%Kh_eta_u(I,j), Kh_u_CFL(I,j))
1745 endif ; enddo ; enddo ; enddo
1746
17470 do k=1,nz+1 ; do J=js-1,je ; do i=is,ie ; if (CS%Kh_eta_v(i,J) > 0.0) then
1748 int_slope_v(i,J,K) = (int_slope_v(i,J,K)*Kh_v(i,J,K) + CS%Kh_eta_v(i,J)) / &
17490 (Kh_v(i,J,K) + CS%Kh_eta_v(i,J))
17500 Kh_v(i,J,K) = min(Kh_v(i,J,K) + CS%Kh_eta_v(i,J), Kh_v_CFL(i,J))
1751 endif ; enddo ; enddo ; enddo
1752
17530end subroutine add_interface_Kh
1754
1755!> Modifies isopycnal height diffusivities to untangle layer structures
17560subroutine add_detangling_Kh(h, e, Kh_u, Kh_v, KH_u_CFL, KH_v_CFL, tv, dt, G, GV, US, CS, &
17570 int_slope_u, int_slope_v)
1758 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
1759 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
1760 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1761 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> m or kg m-2]
1762 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: e !< Interface positions [Z ~> m]
1763 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: Kh_u !< Isopycnal height diffusivity
1764 !! at u points [L2 T-1 ~> m2 s-1]
1765 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1), intent(inout) :: Kh_v !< Isopycnal height diffusivity
1766 !! at v points [L2 T-1 ~> m2 s-1]
1767 real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: Kh_u_CFL !< Maximum stable isopycnal height
1768 !! diffusivity at u points [L2 T-1 ~> m2 s-1]
1769 real, dimension(SZI_(G),SZJB_(G)), intent(in) :: Kh_v_CFL !< Maximum stable isopycnal height
1770 !! diffusivity at v points [L2 T-1 ~> m2 s-1]
1771 type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamics structure
1772 real, intent(in) :: dt !< Time increment [T ~> s]
1773 type(thickness_diffuse_CS), intent(in) :: CS !< Control structure for thickness_diffuse
1774 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: int_slope_u !< Ratio that determine how much of
1775 !! the isopycnal slopes are taken directly from
1776 !! the interface slopes without consideration
1777 !! of density gradients [nondim].
1778 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1), intent(inout) :: int_slope_v !< Ratio that determine how much of
1779 !! the isopycnal slopes are taken directly from
1780 !! the interface slopes without consideration
1781 !! of density gradients [nondim].
1782 ! Local variables
1783 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: &
17840 de_top ! The distances between the top of a layer and the top of the
1785 ! region where the detangling is applied [H ~> m or kg m-2].
1786 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: &
17870 Kh_lay_u ! The tentative isopycnal height diffusivity for each layer at
1788 ! u points [L2 T-1 ~> m2 s-1].
1789 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: &
17900 Kh_lay_v ! The tentative isopycnal height diffusivity for each layer at
1791 ! v points [L2 T-1 ~> m2 s-1].
1792 real, dimension(SZI_(G),SZJ_(G)) :: &
17930 de_bot ! The distances from the bottom of the region where the
1794 ! detangling is applied [H ~> m or kg m-2].
1795 real :: h1, h2 ! The thinner and thicker surrounding thicknesses [H ~> m or kg m-2],
1796 ! with the thinner modified near the boundaries to mask out
1797 ! thickness variations due to topography, etc.
1798 real :: jag_Rat ! The nondimensional jaggedness ratio for a layer, going
1799 ! from 0 (smooth) to 1 (jagged) [nondim]. This is the difference
1800 ! between the arithmetic and harmonic mean thicknesses
1801 ! normalized by the arithmetic mean thickness.
1802 real :: Kh_scale ! A ratio by which Kh_u_CFL is scaled for maximally jagged
1803 ! layers [nondim].
1804 real :: h_neglect ! A thickness that is so small it is usually lost
1805 ! in roundoff and can be neglected [H ~> m or kg m-2].
1806
1807 real :: I_sl ! The absolute value of the larger in magnitude of the slopes
1808 ! above and below [L Z-1 ~> nondim].
1809 real :: Rsl ! The ratio of the smaller magnitude slope to the larger
1810 ! magnitude one [nondim]. 0 <= Rsl <1.
1811 real :: IRsl ! The (limited) inverse of Rsl [nondim]. 1 < IRsl <= 1e9.
1812 real :: dH ! The thickness gradient divided by the damping timescale
1813 ! and the ratio of the face length to the adjacent cell
1814 ! areas for comparability with the diffusivities [L Z T-1 ~> m2 s-1].
1815 real :: adH ! The absolute value of dH [L Z T-1 ~> m2 s-1].
1816 real :: sign ! 1 or -1, with the same sign as the layer thickness gradient [nondim].
1817 real :: sl_K ! The sign-corrected slope of the interface above [Z L-1 ~> nondim].
1818 real :: sl_Kp1 ! The sign-corrected slope of the interface below [Z L-1 ~> nondim].
1819 real :: I_sl_K ! The (limited) inverse of sl_K [L Z-1 ~> nondim].
1820 real :: I_sl_Kp1 ! The (limited) inverse of sl_Kp1 [L Z-1 ~> nondim].
1821 real :: I_4t ! A quarter of a flux scaling factor divided by
1822 ! the damping timescale [T-1 ~> s-1].
1823 real :: Fn_R ! A function of Rsl, such that Rsl < Fn_R < 1 [nondim]
1824 real :: Idx_eff ! The effective inverse x-grid spacing at a u-point [L-1 ~> m-1]
1825 real :: Idy_eff ! The effective inverse y-grid spacing at a v-point [L-1 ~> m-1]
1826 real :: slope_sq ! The sum of the squared slopes above and below a layer [Z2 L-2 ~> nondim]
1827 real :: Kh_max ! A local ceiling on the diffusivity [L2 T-1 ~> m2 s-1].
1828 real :: wt1, wt2 ! Nondimensional weights [nondim].
1829 ! Variables used only in testing code.
1830 ! real, dimension(SZK_(GV)) :: uh_here ! The transport in a layer [Z L2 T-1 ~> m3 s-1]
1831 ! real, dimension(SZK_(GV)+1) :: Sfn ! The streamfunction at an interface [Z L T-1 ~> m2 s-1]
1832 real :: dKh ! An increment in the diffusivity [L2 T-1 ~> m2 s-1].
1833
1834 real, dimension(SZIB_(G),SZK_(GV)+1) :: &
18350 Kh_bg, & ! The background (floor) value of Kh [L2 T-1 ~> m2 s-1].
18360 Kh, & ! The tentative value of Kh [L2 T-1 ~> m2 s-1].
18370 Kh_detangle, & ! The detangling diffusivity that could be used [L2 T-1 ~> m2 s-1].
18380 Kh_min_max_p, & ! The smallest ceiling that can be placed on Kh(I,K)
1839 ! based on the value of Kh(I,K+1) [L2 T-1 ~> m2 s-1].
18400 Kh_min_max_m, & ! The smallest ceiling that can be placed on Kh(I,K)
1841 ! based on the value of Kh(I,K-1) [L2 T-1 ~> m2 s-1].
1842 ! The following are variables that define the relationships between
1843 ! successive values of Kh.
1844 ! Search for Kh that satisfy...
1845 ! Kh(I,K) >= Kh_min_m(I,K)*Kh(I,K-1) + Kh0_min_m(I,K)
1846 ! Kh(I,K) >= Kh_min_p(I,K)*Kh(I,K+1) + Kh0_min_p(I,K)
1847 ! Kh(I,K) <= Kh_max_m(I,K)*Kh(I,K-1) + Kh0_max_m(I,K)
1848 ! Kh(I,K) <= Kh_max_p(I,K)*Kh(I,K+1) + Kh0_max_p(I,K)
18490 Kh_min_m , & ! See above [nondim].
18500 Kh0_min_m , & ! See above [L2 T-1 ~> m2 s-1].
18510 Kh_max_m , & ! See above [nondim].
18520 Kh0_max_m, & ! See above [L2 T-1 ~> m2 s-1].
18530 Kh_min_p , & ! See above [nondim].
18540 Kh0_min_p , & ! See above [L2 T-1 ~> m2 s-1].
18550 Kh_max_p , & ! See above [nondim].
18560 Kh0_max_p ! See above [L2 T-1 ~> m2 s-1].
1857 real, dimension(SZIB_(G)) :: &
18580 Kh_max_max ! The maximum diffusivity permitted in a column [L2 T-1 ~> m2 s-1]
1859 logical, dimension(SZIB_(G)) :: &
18600 do_i ! If true, work on a column.
1861 integer :: i, j, k, n, ish, jsh, is, ie, js, je, nz, k_top
18620 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
1863
18640 k_top = GV%nk_rho_varies + 1
18650 h_neglect = GV%H_subroundoff
1866 ! The 0.5 is because we are not using uniform weightings, but are
1867 ! distributing the diffusivities more effectively (with wt1 & wt2), but this
1868 ! means that the additions to a single interface can be up to twice as large.
18690 Kh_scale = 0.5
18700 if (CS%detangle_time > dt) Kh_scale = 0.5 * dt / CS%detangle_time
1871
18720 do j=js-1,je+1 ; do i=is-1,ie+1
18730 de_top(i,j,k_top) = 0.0 ; de_bot(i,j) = 0.0
1874 enddo ; enddo
18750 do k=k_top+1,nz ; do j=js-1,je+1 ; do i=is-1,ie+1
18760 de_top(i,j,k) = de_top(i,j,k-1) + h(i,j,k-1)
1877 enddo ; enddo ; enddo
1878
18790 do j=js,je ; do I=is-1,ie
18800 Kh_lay_u(I,j,nz) = 0.0 ; Kh_lay_u(I,j,k_top) = 0.0
1881 enddo ; enddo
18820 do J=js-1,je ; do i=is,ie
18830 Kh_lay_v(i,J,nz) = 0.0 ; Kh_lay_v(i,J,k_top) = 0.0
1884 enddo ; enddo
1885
18860 do k=nz-1,k_top+1,-1
1887 ! Find the diffusivities associated with each layer.
18880 do j=js-1,je+1 ; do i=is-1,ie+1
18890 de_bot(i,j) = de_bot(i,j) + h(i,j,k+1)
1890 enddo ; enddo
1891
18920 do j=js,je ; do I=is-1,ie ; if (G%OBCmaskCu(I,j) > 0.0) then
18930 if (h(i,j,k) > h(i+1,j,k)) then
18940 h2 = h(i,j,k)
18950 h1 = max( h(i+1,j,k), h2 - min(de_bot(i+1,j), de_top(i+1,j,k)) )
1896 else
18970 h2 = h(i+1,j,k)
18980 h1 = max( h(i,j,k), h2 - min(de_bot(i,j), de_top(i,j,k)) )
1899 endif
19000 jag_Rat = (h2 - h1)**2 / (h2 + h1 + h_neglect)**2
19010 KH_lay_u(I,j,k) = (Kh_scale * KH_u_CFL(I,j)) * jag_Rat**2
1902 endif ; enddo ; enddo
1903
19040 do J=js-1,je ; do i=is,ie ; if (G%OBCmaskCv(i,J) > 0.0) then
19050 if (h(i,j,k) > h(i,j+1,k)) then
19060 h2 = h(i,j,k)
19070 h1 = max( h(i,j+1,k), h2 - min(de_bot(i,j+1), de_top(i,j+1,k)) )
1908 else
19090 h2 = h(i,j+1,k)
19100 h1 = max( h(i,j,k), h2 - min(de_bot(i,j), de_top(i,j,k)) )
1911 endif
19120 jag_Rat = (h2 - h1)**2 / (h2 + h1 + h_neglect)**2
19130 KH_lay_v(i,J,k) = (Kh_scale * KH_v_CFL(i,J)) * jag_Rat**2
1914 endif ; enddo ; enddo
1915 enddo
1916
1917 ! Limit the diffusivities
1918
19190 I_4t = Kh_scale / (4.0 * dt)
1920
19210 do n=1,2
19220 if (n==1) then ; jsh = js ; ish = is-1
19230 else ; jsh = js-1 ; ish = is ; endif
1924
19250 do j=jsh,je
1926
1927 ! First, populate the diffusivities
19280 if (n==1) then ! This is a u-column.
19290 do i=ish,ie
19300 do_i(I) = (G%OBCmaskCu(I,j) > 0.0)
19310 Kh_Max_max(I) = KH_u_CFL(I,j)
1932 enddo
19330 do K=1,nz+1 ; do i=ish,ie
19340 Kh_bg(I,K) = KH_u(I,j,K) ; Kh(I,K) = Kh_bg(I,K)
19350 Kh_min_max_p(I,K) = Kh_bg(I,K) ; Kh_min_max_m(I,K) = Kh_bg(I,K)
19360 Kh_detangle(I,K) = 0.0
1937 enddo ; enddo
1938 else ! This is a v-column.
19390 do i=ish,ie
19400 do_i(i) = (G%OBCmaskCv(i,J) > 0.0) ; Kh_Max_max(I) = KH_v_CFL(i,J)
1941 enddo
19420 do K=1,nz+1 ; do i=ish,ie
19430 Kh_bg(I,K) = KH_v(I,j,K) ; Kh(I,K) = Kh_bg(I,K)
19440 Kh_min_max_p(I,K) = Kh_bg(I,K) ; Kh_min_max_m(I,K) = Kh_bg(I,K)
19450 Kh_detangle(I,K) = 0.0
1946 enddo ; enddo
1947 endif
1948
1949 ! Determine the limits on the diffusivities.
19500 do k=k_top,nz ; do i=ish,ie ; if (do_i(i)) then
19510 if (n==1) then ! This is a u-column.
19520 dH = 0.0
19530 Idx_eff = ((G%IareaT(i+1,j) + G%IareaT(i,j)) * G%dy_Cu(I,j))
1954 ! This expression uses differences in e in place of h for better
1955 ! consistency with the slopes.
19560 if (Idx_eff > 0.0) &
1957 dH = I_4t * ((e(i+1,j,K) - e(i+1,j,K+1)) - &
19580 (e(i,j,K) - e(i,j,K+1))) / Idx_eff
1959 ! dH = I_4t * (h(i+1,j,k) - h(i,j,k)) / Idx_eff
1960
19610 adH = abs(dH)
19620 sign = 1.0 ; if (dH < 0) sign = -1.0
19630 sl_K = sign * (e(i+1,j,K)-e(i,j,K)) * G%IdxCu(I,j)
19640 sl_Kp1 = sign * (e(i+1,j,K+1)-e(i,j,K+1)) * G%IdxCu(I,j)
1965
1966 ! Add the incremental diffusivities to the surrounding interfaces.
1967 ! Adding more to the more steeply sloping layers (as below) makes
1968 ! the diffusivities more than twice as effective.
19690 slope_sq = (sl_K**2 + sl_Kp1**2)
19700 wt1 = 0.5 ; wt2 = 0.5
19710 if (slope_sq > 0.0) then
19720 wt1 = sl_K**2 / slope_sq ; wt2 = sl_Kp1**2 / slope_sq
1973 endif
19740 Kh_detangle(I,K) = Kh_detangle(I,K) + wt1*KH_lay_u(I,j,k)
19750 Kh_detangle(I,K+1) = Kh_detangle(I,K+1) + wt2*KH_lay_u(I,j,k)
1976 else ! This is a v-column.
19770 dH = 0.0
19780 Idy_eff = ((G%IareaT(i,j+1) + G%IareaT(i,j)) * G%dx_Cv(I,j))
19790 if (Idy_eff > 0.0) &
1980 dH = I_4t * ((e(i,j+1,K) - e(i,j+1,K+1)) - &
19810 (e(i,j,K) - e(i,j,K+1))) / Idy_eff
1982 ! dH = I_4t * (h(i,j+1,k) - h(i,j,k)) / Idy_eff
1983
19840 adH = abs(dH)
19850 sign = 1.0 ; if (dH < 0) sign = -1.0
19860 sl_K = sign * (e(i,j+1,K)-e(i,j,K)) * G%IdyCv(i,J)
19870 sl_Kp1 = sign * (e(i,j+1,K+1)-e(i,j,K+1)) * G%IdyCv(i,J)
1988
1989 ! Add the incremental diffusivities to the surrounding interfaces.
1990 ! Adding more to the more steeply sloping layers (as below) makes
1991 ! the diffusivities more than twice as effective.
19920 slope_sq = (sl_K**2 + sl_Kp1**2)
19930 wt1 = 0.5 ; wt2 = 0.5
19940 if (slope_sq > 0.0) then
19950 wt1 = sl_K**2 / slope_sq ; wt2 = sl_Kp1**2 / slope_sq
1996 endif
19970 Kh_detangle(I,K) = Kh_detangle(I,K) + wt1*KH_lay_v(i,J,k)
19980 Kh_detangle(I,K+1) = Kh_detangle(I,K+1) + wt2*KH_lay_v(i,J,k)
1999 endif
2000
20010 if (adH == 0.0) then
20020 Kh_min_m(I,K+1) = 1.0 ; Kh0_min_m(I,K+1) = 0.0
20030 Kh_max_m(I,K+1) = 1.0 ; Kh0_max_m(I,K+1) = 0.0
20040 Kh_min_p(I,K) = 1.0 ; Kh0_min_p(I,K) = 0.0
20050 Kh_max_p(I,K) = 1.0 ; Kh0_max_p(I,K) = 0.0
20060 elseif (adH > 0.0) then
20070 if (sl_K <= sl_Kp1) then
2008 ! This case should only arise from nonlinearities in the equation of state.
2009 ! Treat it as though dedx(K) = dedx(K+1) & dH = 0.
20100 Kh_min_m(I,K+1) = 1.0 ; Kh0_min_m(I,K+1) = 0.0
20110 Kh_max_m(I,K+1) = 1.0 ; Kh0_max_m(I,K+1) = 0.0
20120 Kh_min_p(I,K) = 1.0 ; Kh0_min_p(I,K) = 0.0
20130 Kh_max_p(I,K) = 1.0 ; Kh0_max_p(I,K) = 0.0
20140 elseif (sl_K <= 0.0) then ! Both slopes are opposite to dH
20150 I_sl = -1.0 / sl_Kp1
20160 Rsl = -sl_K * I_sl ! 0 <= Rsl < 1
20170 IRsl = 1e9 ; if (Rsl > 1e-9) IRsl = 1.0/Rsl ! 1 < IRsl <= 1e9
2018
20190 Fn_R = Rsl
20200 if (Kh_max_max(I) > 0) &
20210 Fn_R = min(sqrt(Rsl), Rsl + (adH * I_sl) / (Kh_Max_max(I)))
2022
20230 Kh_min_m(I,K+1) = Fn_R ; Kh0_min_m(I,K+1) = 0.0
20240 Kh_max_m(I,K+1) = Rsl ; Kh0_max_m(I,K+1) = adH * I_sl
20250 Kh_min_p(I,K) = IRsl ; Kh0_min_p(I,K) = -adH * (I_sl*IRsl)
20260 Kh_max_p(I,K) = 1.0/(Fn_R + 1.0e-30) ; Kh0_max_p(I,K) = 0.0
20270 elseif (sl_Kp1 < 0.0) then ! Opposite (nonzero) signs of slopes.
20280 I_sl_K = 1e18*US%Z_to_L ; if (sl_K > 1e-18*US%L_to_Z) I_sl_K = 1.0 / sl_K
20290 I_sl_Kp1 = 1e18*US%Z_to_L ; if (-sl_Kp1 > 1e-18*US%L_to_Z) I_sl_Kp1 = -1.0 / sl_Kp1
2030
20310 Kh_min_m(I,K+1) = 0.0 ; Kh0_min_m(I,K+1) = 0.0
20320 Kh_max_m(I,K+1) = - sl_K*I_sl_Kp1 ; Kh0_max_m(I,K+1) = adH*I_sl_Kp1
20330 Kh_min_p(I,K) = 0.0 ; Kh0_min_p(I,K) = 0.0
20340 Kh_max_p(I,K) = sl_Kp1*I_sl_K ; Kh0_max_p(I,K) = adH*I_sl_K
2035
2036 ! This limit does not use the slope weighting so that potentially
2037 ! sharp gradients in diffusivities are not forced to occur.
20380 Kh_Max = adH / (sl_K - sl_Kp1)
20390 Kh_min_max_p(I,K) = max(Kh_min_max_p(I,K), Kh_Max)
20400 Kh_min_max_m(I,K+1) = max(Kh_min_max_m(I,K+1), Kh_Max)
2041 else ! Both slopes are of the same sign as dH.
20420 I_sl = 1.0 / sl_K
20430 Rsl = sl_Kp1 * I_sl ! 0 <= Rsl < 1
20440 IRsl = 1e9 ; if (Rsl > 1e-9) IRsl = 1.0/Rsl ! 1 < IRsl <= 1e9
2045
2046 ! Rsl <= Fn_R <= 1
20470 Fn_R = Rsl
20480 if (Kh_max_max(I) > 0) &
20490 Fn_R = min(sqrt(Rsl), Rsl + (adH * I_sl) / Kh_Max_max(I))
2050
20510 Kh_min_m(I,K+1) = IRsl ; Kh0_min_m(I,K+1) = -adH * (I_sl*IRsl)
20520 Kh_max_m(I,K+1) = 1.0/(Fn_R + 1.0e-30) ; Kh0_max_m(I,K+1) = 0.0
20530 Kh_min_p(I,K) = Fn_R ; Kh0_min_p(I,K) = 0.0
20540 Kh_max_p(I,K) = Rsl ; Kh0_max_p(I,K) = adH * I_sl
2055 endif
2056 endif
2057 endif ; enddo ; enddo ! I-loop & k-loop
2058
20590 do k=k_top,nz+1,nz+1-k_top ; do i=ish,ie ; if (do_i(i)) then
2060 ! The diffusivities at k_top and nz+1 are both fixed.
20610 Kh_min_m(I,k) = 0.0 ; Kh0_min_m(I,k) = 0.0
20620 Kh_max_m(I,k) = 0.0 ; Kh0_max_m(I,k) = 0.0
20630 Kh_min_p(I,k) = 0.0 ; Kh0_min_p(I,k) = 0.0
20640 Kh_max_p(I,k) = 0.0 ; Kh0_max_p(I,k) = 0.0
20650 Kh_min_max_p(I,K) = Kh_bg(I,K)
20660 Kh_min_max_m(I,K) = Kh_bg(I,K)
2067 endif ; enddo ; enddo ! I-loop and k_top/nz+1 loop
2068
2069 ! Search for Kh that satisfy...
2070 ! Kh(I,K) >= Kh_min_m(I,K)*Kh(I,K-1) + Kh0_min_m(I,K)
2071 ! Kh(I,K) >= Kh_min_p(I,K)*Kh(I,K+1) + Kh0_min_p(I,K)
2072 ! Kh(I,K) <= Kh_max_m(I,K)*Kh(I,K-1) + Kh0_max_m(I,K)
2073 ! Kh(I,K) <= Kh_max_p(I,K)*Kh(I,K+1) + Kh0_max_p(I,K)
2074
2075 ! Increase the diffusivities to satisfy the min constraints.
2076 ! All non-zero min constraints on one diffusivity are max constraints on another.
20770 do K=k_top+1,nz ; do i=ish,ie ; if (do_i(i)) then
2078 Kh(I,K) = max(Kh_bg(I,K), Kh_detangle(I,K), &
20790 min(Kh_min_m(I,K)*Kh(I,K-1) + Kh0_min_m(I,K), Kh(I,K-1)))
2080
20810 if (Kh0_max_m(I,K) > Kh_bg(I,K)) Kh(I,K) = min(Kh(I,K), Kh0_max_m(I,K))
20820 if (Kh0_max_p(I,K) > Kh_bg(I,K)) Kh(I,K) = min(Kh(I,K), Kh0_max_p(I,K))
2083 endif ; enddo ; enddo ! I-loop & k-loop
2084 ! This is still true... do i=ish,ie ; Kh(I,nz+1) = Kh_bg(I,nz+1) ; enddo
20850 do K=nz,k_top+1,-1 ; do i=ish,ie ; if (do_i(i)) then
20860 Kh(I,k) = max(Kh(I,K), min(Kh_min_p(I,K)*Kh(I,K+1) + Kh0_min_p(I,K), Kh(I,K+1)))
2087
20880 Kh_Max = max(Kh_min_max_p(I,K), Kh_max_p(I,K)*Kh(I,K+1) + Kh0_max_p(I,K))
20890 Kh(I,k) = min(Kh(I,k), Kh_Max)
2090 endif ; enddo ; enddo ! I-loop & k-loop
2091 ! All non-zero min constraints on one diffusivity are max constraints on
2092 ! another layer, so the min constraints can now be discounted.
2093
2094 ! Decrease the diffusivities to satisfy the max constraints.
20950 do K=k_top+1,nz ; do i=ish,ie ; if (do_i(i)) then
20960 Kh_Max = max(Kh_min_max_m(I,K), Kh_max_m(I,K)*Kh(I,K-1) + Kh0_max_m(I,K))
20970 if (Kh(I,k) > Kh_Max) Kh(I,k) = Kh_Max
2098 endif ; enddo ; enddo ! i- and K-loops
2099
2100 ! This code tests the solutions...
2101! do i=ish,ie
2102! Sfn(:) = 0.0 ; uh_here(:) = 0.0
2103! do K=k_top,nz
2104! if ((Kh(i,K) > Kh_bg(i,K)) .or. (Kh(i,K+1) > Kh_bg(i,K+1))) then
2105! if (n==1) then ! u-point.
2106! if ((h(i+1,j,k) - h(i,j,k)) * &
2107! ((e(i+1,j,K)-e(i+1,j,K+1)) - (e(i,j,K)-e(i,j,K+1))) > 0.0) then
2108! Sfn(K) = -Kh(i,K) * (e(i+1,j,K)-e(i,j,K)) * G%IdxCu(I,j)
2109! Sfn(K+1) = -Kh(i,K+1) * (e(i+1,j,K+1)-e(i,j,K+1)) * G%IdxCu(I,j)
2110! uh_here(k) = (Sfn(K) - Sfn(K+1))*G%dy_Cu(I,j)
2111! if (abs(uh_here(k)) * min(G%IareaT(i,j), G%IareaT(i+1,j)) > &
2112! (1e-10*GV%m_to_H)) then
2113! if (uh_here(k) * (h(i+1,j,k) - h(i,j,k)) > 0.0) then
2114! call MOM_error(WARNING, "Corrective u-transport is up the thickness gradient.", .true.)
2115! endif
2116! if (((h(i,j,k) - 4.0*dt*G%IareaT(i,j)*uh_here(k)) - &
2117! (h(i+1,j,k) + 4.0*dt*G%IareaT(i+1,j)*uh_here(k))) * &
2118! (h(i,j,k) - h(i+1,j,k)) < 0.0) then
2119! call MOM_error(WARNING, "Corrective u-transport is too large.", .true.)
2120! endif
2121! endif
2122! endif
2123! else ! v-point
2124! if ((h(i,j+1,k) - h(i,j,k)) * &
2125! ((e(i,j+1,K)-e(i,j+1,K+1)) - (e(i,j,K)-e(i,j,K+1))) > 0.0) then
2126! Sfn(K) = -Kh(i,K) * (e(i,j+1,K)-e(i,j,K)) * G%IdyCv(i,J)
2127! Sfn(K+1) = -Kh(i,K+1) * (e(i,j+1,K+1)-e(i,j,K+1)) * G%IdyCv(i,J)
2128! uh_here(k) = (Sfn(K) - Sfn(K+1))*G%dx_Cv(i,J)
2129! if (abs(uh_here(K)) * min(G%IareaT(i,j), G%IareaT(i,j+1)) > &
2130! (1e-10*GV%m_to_H)) then
2131! if (uh_here(K) * (h(i,j+1,k) - h(i,j,k)) > 0.0) then
2132! call MOM_error(WARNING, &
2133! "Corrective v-transport is up the thickness gradient.", .true.)
2134! endif
2135! if (((h(i,j,k) - 4.0*dt*G%IareaT(i,j)*uh_here(K)) - &
2136! (h(i,j+1,k) + 4.0*dt*G%IareaT(i,j+1)*uh_here(K))) * &
2137! (h(i,j,k) - h(i,j+1,k)) < 0.0) then
2138! call MOM_error(WARNING, &
2139! "Corrective v-transport is too large.", .true.)
2140! endif
2141! endif
2142! endif
2143! endif ! u- or v- selection.
2144! ! de_dx(I,K) = (e(i+1,j,K)-e(i,j,K)) * G%IdxCu(I,j)
2145! endif
2146! enddo
2147! enddo
2148
21490 if (n==1) then ! This is a u-column.
21500 do K=k_top+1,nz ; do i=ish,ie
21510 if (Kh(I,K) > KH_u(I,j,K)) then
21520 dKh = (Kh(I,K) - KH_u(I,j,K))
21530 int_slope_u(I,j,K) = dKh / Kh(I,K)
21540 KH_u(I,j,K) = Kh(I,K)
2155 endif
2156 enddo ; enddo
2157 else ! This is a v-column.
21580 do K=k_top+1,nz ; do i=ish,ie
21590 if (Kh(i,K) > KH_v(i,J,K)) then
21600 dKh = Kh(i,K) - KH_v(i,J,K)
21610 int_slope_v(i,J,K) = dKh / Kh(i,K)
21620 KH_v(i,J,K) = Kh(i,K)
2163 endif
2164 enddo ; enddo
2165 endif
2166
2167 enddo ! j-loop
2168 enddo ! n-loop over u- and v- directions.
2169
21700end subroutine add_detangling_Kh
2171
2172!> Initialize the isopycnal height diffusion module and its control structure
21731subroutine thickness_diffuse_init(Time, G, GV, US, param_file, diag, CDp, CS)
2174 type(time_type), intent(in) :: Time !< Current model time
2175 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
2176 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
2177 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
2178 type(param_file_type), intent(in) :: param_file !< Parameter file handles
2179 type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure
2180 type(cont_diag_ptrs), intent(inout) :: CDp !< Continuity equation diagnostics
2181 type(thickness_diffuse_CS), intent(inout) :: CS !< Control structure for thickness_diffuse
2182
2183 ! Local variables
2184 character(len=40) :: mdl = "MOM_thickness_diffuse" ! This module's name.
2185 character(len=200) :: khth_file, inputdir, khth_varname
2186 ! This include declares and sets the variable "version".
2187# include "version_variable.h"
2188 real :: grid_sp ! The local grid spacing [L ~> m]
2189 real :: omega ! The Earth's rotation rate [T-1 ~> s-1]
2190 real :: strat_floor ! A floor for buoyancy frequency in the Ferrari et al. 2010,
2191 ! streamfunction formulation, expressed as a fraction of planetary
2192 ! rotation divided by an aspect ratio rescaling factor [L Z-1 ~> nondim]
2193 real :: Stanley_coeff ! Coefficient relating the temperature gradient and sub-gridscale
2194 ! temperature variance [nondim]
2195 logical :: khth_use_ebt_struct ! If true, uses the equivalent barotropic structure
2196 ! as the vertical structure of thickness diffusivity.
2197 ! Used to determine if FULL_DEPTH_KHTH_MIN should be
2198 ! available.
2199 logical :: use_meke = .false. ! If true, use the MEKE formulation for the thickness diffusivity.
2200 integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags.
2201 integer :: i, j
2202
22031 CS%initialized = .true.
22041 CS%diag => diag
2205
2206 ! Read all relevant parameters and write them to the model log.
22071 call log_version(param_file, mdl, version, "")
2208 call get_param(param_file, mdl, "THICKNESSDIFFUSE", CS%thickness_diffuse, &
2209 "If true, interface heights are diffused with a "//&
22101 "coefficient of KHTH.", default=.false.)
2211 call get_param(param_file, mdl, "KHTH", CS%Khth, &
2212 "The background horizontal thickness diffusivity.", &
22131 default=0.0, units="m2 s-1", scale=US%m_to_L**2*US%T_to_s)
2214 call get_param(param_file, mdl, "READ_KHTH", CS%read_khth, &
2215 "If true, read a file (given by KHTH_FILE) containing the "//&
2216 "spatially varying horizontal isopycnal height diffusivity.", &
22171 default=.false.)
22181 if (CS%read_khth) then
22190 if (CS%Khth > 0) then
2220 call MOM_error(FATAL, "thickness_diffuse_init: KHTH > 0 is not "// &
22210 "compatible with READ_KHTH = TRUE. ")
2222 endif
2223 call get_param(param_file, mdl, "INPUTDIR", inputdir, &
2224 "The directory in which all input files are found.", &
22250 default=".", do_not_log=.true.)
22260 inputdir = slasher(inputdir)
2227 call get_param(param_file, mdl, "KHTH_FILE", khth_file, &
2228 "The file containing the spatially varying horizontal "//&
22290 "isopycnal height diffusivity.", default="khth.nc")
2230 call get_param(param_file, mdl, "KHTH_VARIABLE", khth_varname, &
2231 "The name of the isopycnal height diffusivity variable to read "//&
2232 "from KHTH_FILE.", &
22330 default="khth")
22340 khth_file = trim(inputdir) // trim(khth_file)
2235
22360 allocate(CS%khth2d(G%isd:G%ied, G%jsd:G%jed), source=0.0)
22370 call MOM_read_data(khth_file, khth_varname, CS%khth2d(:,:), G%domain, scale=US%m_to_L**2*US%T_to_s)
22380 call pass_var(CS%khth2d, G%domain)
2239 endif
2240 call get_param(param_file, mdl, "KHTH_SLOPE_CFF", CS%KHTH_Slope_Cff, &
2241 "The nondimensional coefficient in the Visbeck formula for "//&
22421 "the interface depth diffusivity", units="nondim", default=0.0)
2243 call get_param(param_file, mdl, "KHTH_MIN", CS%KHTH_Min, &
2244 "The minimum horizontal thickness diffusivity.", &
22451 default=0.0, units="m2 s-1", scale=US%m_to_L**2*US%T_to_s)
2246 call get_param(param_file, mdl, "KHTH_USE_EBT_STRUCT", khth_use_ebt_struct, &
2247 "If true, uses the equivalent barotropic structure "//&
2248 "as the vertical structure of thickness diffusivity.",&
22491 default=.false., do_not_log=.true.)
22501 if (khth_use_ebt_struct .and. CS%KHTH_Min>0.0) then
2251 call get_param(param_file, mdl, "FULL_DEPTH_KHTH_MIN", CS%full_depth_khth_min, &
2252 "If true, KHTH_MIN is enforced throughout the whole water column. "//&
2253 "Otherwise, KHTH_MIN is only enforced at the surface. This parameter "//&
2254 "is only available when KHTH_USE_EBT_STRUCT=True and KHTH_MIN>0.", &
22550 default=.false.)
2256 endif
2257 call get_param(param_file, mdl, "KHTH_MAX", CS%KHTH_Max, &
2258 "The maximum horizontal thickness diffusivity.", &
22591 default=0.0, units="m2 s-1", scale=US%m_to_L**2*US%T_to_s)
2260 call get_param(param_file, mdl, "KHTH_MAX_CFL", CS%max_Khth_CFL, &
2261 "The maximum value of the local diffusive CFL ratio that "//&
2262 "is permitted for the thickness diffusivity. 1.0 is the "//&
2263 "marginally unstable value in a pure layered model, but "//&
2264 "much smaller numbers (e.g. 0.1) seem to work better for "//&
22651 "ALE-based models.", units="nondimensional", default=0.8)
2266
2267 call get_param(param_file, mdl, "KH_ETA_CONST", CS%Kh_eta_bg, &
2268 "The background horizontal diffusivity of the interface heights (without "//&
2269 "considering the layer density structure). If diffusive CFL limits are "//&
2270 "encountered, the diffusivities of the isopycnals and the interfaces heights "//&
2271 "are scaled back proportionately.", &
22721 default=0.0, units="m2 s-1", scale=US%m_to_L**2*US%T_to_s)
2273 call get_param(param_file, mdl, "KH_ETA_VEL_SCALE", CS%Kh_eta_vel, &
2274 "A velocity scale that is multiplied by the grid spacing to give a contribution "//&
2275 "to the horizontal diffusivity of the interface heights (without considering "//&
2276 "the layer density structure).", &
22771 default=0.0, units="m s-1", scale=US%m_to_L*US%T_to_s)
2278
22791 if ((CS%Kh_eta_bg > 0.0) .or. (CS%Kh_eta_vel > 0.0)) then
22800 allocate(CS%Kh_eta_u(G%IsdB:G%IedB, G%jsd:G%jed), source=0.)
22810 allocate(CS%Kh_eta_v(G%isd:G%ied, G%JsdB:G%JedB), source=0.)
22820 do j=G%jsc,G%jec ; do I=G%isc-1,G%iec
22830 grid_sp = sqrt((2.0*G%dxCu(I,j)**2 * G%dyCu(I,j)**2) / ((G%dxCu(I,j)**2) + (G%dyCu(I,j)**2)))
22840 CS%Kh_eta_u(I,j) = G%OBCmaskCu(I,j) * MAX(0.0, CS%Kh_eta_bg + CS%Kh_eta_vel * grid_sp)
2285 enddo ; enddo
22860 do J=G%jsc-1,G%jec ; do i=G%isc,G%iec
22870 grid_sp = sqrt((2.0*G%dxCv(i,J)**2 * G%dyCv(i,J)**2) / ((G%dxCv(i,J)**2) + (G%dyCv(i,J)**2)))
22880 CS%Kh_eta_v(i,J) = G%OBCmaskCv(i,J) * MAX(0.0, CS%Kh_eta_bg + CS%Kh_eta_vel * grid_sp)
2289 enddo ; enddo
2290 endif
2291
22921 if (CS%max_Khth_CFL < 0.0) CS%max_Khth_CFL = 0.0
2293 call get_param(param_file, mdl, "DETANGLE_INTERFACES", CS%detangle_interfaces, &
2294 "If defined add 3-d structured enhanced interface height "//&
2295 "diffusivities to horizontally smooth jagged layers.", &
22961 default=.false.)
22971 CS%detangle_time = 0.0
22981 if (CS%detangle_interfaces) &
2299 call get_param(param_file, mdl, "DETANGLE_TIMESCALE", CS%detangle_time, &
2300 "A timescale over which maximally jagged grid-scale "//&
2301 "thickness variations are suppressed. This must be "//&
23020 "longer than DT, or 0 to use DT.", units="s", default=0.0, scale=US%s_to_T)
2303 call get_param(param_file, mdl, "KHTH_SLOPE_MAX", CS%slope_max, &
2304 "A slope beyond which the calculated isopycnal slope is "//&
23051 "not reliable and is scaled away.", units="nondim", default=0.01, scale=US%L_to_Z)
2306 call get_param(param_file, mdl, "KD_SMOOTH", CS%kappa_smooth, &
2307 "A diapycnal diffusivity that is used to interpolate "//&
2308 "more sensible values of T & S into thin layers.", &
23091 units="m2 s-1", default=1.0e-6, scale=GV%m2_s_to_HZ_T)
2310 call get_param(param_file, mdl, "KHTH_USE_FGNV_STREAMFUNCTION", CS%use_FGNV_streamfn, &
2311 "If true, use the streamfunction formulation of "//&
2312 "Ferrari et al., 2010, which effectively emphasizes "//&
2313 "graver vertical modes by smoothing in the vertical.", &
23141 default=.false.)
2315 call get_param(param_file, mdl, "FGNV_FILTER_SCALE", CS%FGNV_scale, &
2316 "A coefficient scaling the vertical smoothing term in the "//&
2317 "Ferrari et al., 2010, streamfunction formulation.", &
23181 units="nondim", default=1., do_not_log=.not.CS%use_FGNV_streamfn)
2319 call get_param(param_file, mdl, "FGNV_C_MIN", CS%FGNV_c_min, &
2320 "A minium wave speed used in the Ferrari et al., 2010, "//&
2321 "streamfunction formulation.", &
23221 default=0., units="m s-1", scale=US%m_s_to_L_T, do_not_log=.not.CS%use_FGNV_streamfn)
2323 call get_param(param_file, mdl, "FGNV_STRAT_FLOOR", strat_floor, &
2324 "A floor for Brunt-Vasaila frequency in the Ferrari et al., 2010, "//&
2325 "streamfunction formulation, expressed as a fraction of planetary "//&
2326 "rotation, OMEGA. This should be tiny but non-zero to avoid degeneracy.", &
23271 default=1.e-15, units="nondim", scale=US%Z_to_L, do_not_log=.not.CS%use_FGNV_streamfn)
2328 call get_param(param_file, mdl, "USE_STANLEY_GM", CS%use_stanley_gm, &
2329 "If true, turn on Stanley SGS T variance parameterization "// &
23301 "in GM code.", default=.false.)
23311 if (CS%use_stanley_gm) then
2332 call get_param(param_file, mdl, "STANLEY_COEFF", Stanley_coeff, &
2333 "Coefficient correlating the temperature gradient and SGS T variance.", &
23340 units="nondim", default=-1.0, do_not_log=.true.)
23350 if (Stanley_coeff < 0.0) call MOM_error(FATAL, &
23360 "STANLEY_COEFF must be set >= 0 if USE_STANLEY_GM is true.")
2337 endif
2338 call get_param(param_file, mdl, "OMEGA", omega, &
2339 "The rotation rate of the earth.", &
23401 default=7.2921e-5, units="s-1", scale=US%T_to_s, do_not_log=.not.CS%use_FGNV_streamfn)
23411 CS%N2_floor = 0.
23421 if (CS%use_FGNV_streamfn) CS%N2_floor = (strat_floor*omega)**2
2343 call get_param(param_file, mdl, "DEBUG", CS%debug, &
2344 "If true, write out verbose debugging data.", &
23451 default=.false., debuggingParam=.true.)
2346
2347 call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, &
2348 "This sets the default value for the various _ANSWER_DATE parameters.", &
23491 default=99991231, do_not_log=.true.)
2350
2351 call get_param(param_file, mdl, "MEKE_GM_SRC_ALT", CS%GM_src_alt, &
2352 "If true, use the GM energy conversion form S^2*N^2*kappa rather "//&
23531 "than the streamfunction for the GM source term.", default=.false.)
2354 call get_param(param_file, mdl, "MEKE_GM_SRC_ANSWER_DATE", CS%MEKE_src_answer_date, &
2355 "The vintage of the expressions in the GM energy conversion calculation when "//&
2356 "MEKE_GM_SRC_ALT is true. Values below 20240601 recover the answers from the "//&
2357 "original implementation, while higher values use expressions that satisfy "//&
2358 "rotational symmetry.", &
23591 default=default_answer_date, do_not_log=.not.CS%GM_src_alt)
2360 call get_param(param_file, mdl, "MEKE_GM_SRC_ALT_SLOPE_BUG", CS%MEKE_src_slope_bug, &
2361 "If true, use a bug that limits the positive values, but not the negative values, "//&
2362 "of the slopes used when MEKE_GM_SRC_ALT is true. When this is true, it breaks "//&
2363 "all of the symmetry rules that MOM6 is supposed to obey.", &
23641 default=.false., do_not_log=.not.CS%GM_src_alt)
2365
2366 call get_param(param_file, mdl, "MEKE_GEOMETRIC", CS%MEKE_GEOMETRIC, &
2367 "If true, uses the GM coefficient formulation from the GEOMETRIC "//&
23681 "framework (Marshall et al., 2012).", default=.false.)
23691 if (CS%MEKE_GEOMETRIC) then
2370 call get_param(param_file, mdl, "MEKE_GEOMETRIC_EPSILON", CS%MEKE_GEOMETRIC_epsilon, &
2371 "Minimum Eady growth rate used in the calculation of GEOMETRIC "//&
23720 "thickness diffusivity.", units="s-1", default=1.0e-7, scale=US%T_to_s)
2373 call get_param(param_file, mdl, "MEKE_GEOMETRIC_ALPHA", CS%MEKE_GEOMETRIC_alpha, &
2374 "The nondimensional coefficient governing the efficiency of the GEOMETRIC "//&
23750 "thickness diffusion.", units="nondim", default=0.05)
2376
2377 call get_param(param_file, mdl, "MEKE_GEOMETRIC_ANSWER_DATE", CS%MEKE_GEOM_answer_date, &
2378 "The vintage of the expressions in the MEKE_GEOMETRIC calculation. "//&
2379 "Values below 20190101 recover the answers from the original implementation, "//&
2380 "while higher values use expressions that satisfy rotational symmetry.", &
23810 default=default_answer_date, do_not_log=.not.GV%Boussinesq)
23820 if (.not.GV%Boussinesq) CS%MEKE_GEOM_answer_date = max(CS%MEKE_GEOM_answer_date, 20230701)
2383 endif
2384
23851 call get_param(param_file, mdl, "USE_MEKE", use_meke, default=.false., do_not_log=.true.)
23861 if (use_meke) then
2387 call get_param(param_file, mdl, "USE_KH_IN_MEKE", CS%Use_KH_in_MEKE, &
2388 "If true, uses the thickness diffusivity calculated here to diffuse MEKE.", &
23891 default=.false.)
2390 call get_param(param_file, mdl, "MEKE_MIN_DEPTH_DIFF", CS%MEKE_min_depth_diff, &
2391 "The minimum total depth over which to average the diffusivity used for MEKE. "//&
2392 "When the total depth is less than this, the diffusivity is scaled away.", &
23931 units="m", default=1.0, scale=GV%m_to_H, do_not_log=.not.CS%Use_KH_in_MEKE)
2394 endif
2395
2396 call get_param(param_file, mdl, "USE_GME", CS%use_GME_thickness_diffuse, &
2397 "If true, use the GM+E backscatter scheme in association "//&
23981 "with the Gent and McWilliams parameterization.", default=.false.)
2399
2400 call get_param(param_file, mdl, "USE_GM_WORK_BUG", CS%use_GM_work_bug, &
2401 "If true, compute the top-layer work tendency on the u-grid "//&
2402 "with the incorrect sign, for legacy reproducibility.", &
24031 default=.false.)
2404
24051 if (CS%use_GME_thickness_diffuse) then
24060 allocate(CS%KH_u_GME(G%IsdB:G%IedB, G%jsd:G%jed, GV%ke+1), source=0.)
24070 allocate(CS%KH_v_GME(G%isd:G%ied, G%JsdB:G%JedB, GV%ke+1), source=0.)
2408 endif
2409
2410 CS%id_uhGM = register_diag_field('ocean_model', 'uhGM', diag%axesCuL, Time, &
2411 'Time Mean Diffusive Zonal Thickness Flux', &
2412 'kg s-1', conversion=GV%H_to_kg_m2*US%L_to_m**2*US%s_to_T, &
24131 y_cell_method='sum', v_extensive=.true.)
24141 if (CS%id_uhGM > 0) call safe_alloc_ptr(CDp%uhGM,G%IsdB,G%IedB,G%jsd,G%jed,GV%ke)
2415 CS%id_vhGM = register_diag_field('ocean_model', 'vhGM', diag%axesCvL, Time, &
2416 'Time Mean Diffusive Meridional Thickness Flux', &
2417 'kg s-1', conversion=GV%H_to_kg_m2*US%L_to_m**2*US%s_to_T, &
24181 x_cell_method='sum', v_extensive=.true.)
24191 if (CS%id_vhGM > 0) call safe_alloc_ptr(CDp%vhGM,G%isd,G%ied,G%JsdB,G%JedB,GV%ke)
2420
2421 CS%id_GMwork = register_diag_field('ocean_model', 'GMwork', diag%axesT1, Time, &
2422 'Integrated Tendency of Ocean Mesoscale Eddy KE from Parameterized Eddy Advection', &
2423 'W m-2', conversion=US%RZ3_T3_to_W_m2*US%L_to_Z**2, cmor_field_name='tnkebto', &
2424 cmor_long_name='Integrated Tendency of Ocean Mesoscale Eddy KE from Parameterized Eddy Advection', &
24251 cmor_standard_name='tendency_of_ocean_eddy_kinetic_energy_content_due_to_parameterized_eddy_advection')
24261 if (CS%id_GMwork > 0) &
24270 allocate(CS%GMwork(G%isd:G%ied,G%jsd:G%jed), source=0.)
2428
2429 CS%id_KH_u = register_diag_field('ocean_model', 'KHTH_u', diag%axesCui, Time, &
2430 'Parameterized mesoscale eddy advection diffusivity at U-point', &
24311 'm2 s-1', conversion=US%L_to_m**2*US%s_to_T)
2432 CS%id_KH_v = register_diag_field('ocean_model', 'KHTH_v', diag%axesCvi, Time, &
2433 'Parameterized mesoscale eddy advection diffusivity at V-point', &
24341 'm2 s-1', conversion=US%L_to_m**2*US%s_to_T)
2435 CS%id_KH_t = register_diag_field('ocean_model', 'KHTH_t', diag%axesTL, Time, &
2436 'Ocean Tracer Diffusivity due to Parameterized Mesoscale Advection', &
2437 'm2 s-1', conversion=US%L_to_m**2*US%s_to_T, &
2438 cmor_field_name='diftrblo', &
2439 cmor_long_name='Ocean Tracer Diffusivity due to Parameterized Mesoscale Advection', &
24401 cmor_standard_name='ocean_tracer_diffusivity_due_to_parameterized_mesoscale_advection')
2441
2442 CS%id_KH_u1 = register_diag_field('ocean_model', 'KHTH_u1', diag%axesCu1, Time, &
2443 'Parameterized mesoscale eddy advection diffusivity at U-points (2-D)', &
24441 'm2 s-1', conversion=US%L_to_m**2*US%s_to_T)
2445 CS%id_KH_v1 = register_diag_field('ocean_model', 'KHTH_v1', diag%axesCv1, Time, &
2446 'Parameterized mesoscale eddy advection diffusivity at V-points (2-D)', &
24471 'm2 s-1', conversion=US%L_to_m**2*US%s_to_T)
2448 CS%id_KH_t1 = register_diag_field('ocean_model', 'KHTH_t1', diag%axesT1, Time, &
2449 'Parameterized mesoscale eddy advection diffusivity at T-points (2-D)', &
24501 'm2 s-1', conversion=US%L_to_m**2*US%s_to_T)
2451
2452 CS%id_slope_x = register_diag_field('ocean_model', 'neutral_slope_x', diag%axesCui, Time, &
24531 'Zonal slope of neutral surface', 'nondim', conversion=US%Z_to_L)
24541 if (CS%id_slope_x > 0) &
24550 allocate(CS%diagSlopeX(G%IsdB:G%IedB,G%jsd:G%jed,GV%ke+1), source=0.)
2456
2457 CS%id_slope_y = register_diag_field('ocean_model', 'neutral_slope_y', diag%axesCvi, Time, &
24581 'Meridional slope of neutral surface', 'nondim', conversion=US%Z_to_L)
24591 if (CS%id_slope_y > 0) &
24600 allocate(CS%diagSlopeY(G%isd:G%ied,G%JsdB:G%JedB,GV%ke+1), source=0.)
2461
2462 CS%id_sfn_x = register_diag_field('ocean_model', 'GM_sfn_x', diag%axesCui, Time, &
2463 'Parameterized Zonal Overturning Streamfunction', &
24641 'm3 s-1', conversion=GV%H_to_m*US%L_to_m**2*US%s_to_T)
2465 CS%id_sfn_y = register_diag_field('ocean_model', 'GM_sfn_y', diag%axesCvi, Time, &
2466 'Parameterized Meridional Overturning Streamfunction', &
24671 'm3 s-1', conversion=GV%H_to_m*US%L_to_m**2*US%s_to_T)
2468 CS%id_sfn_unlim_x = register_diag_field('ocean_model', 'GM_sfn_unlim_x', diag%axesCui, Time, &
2469 'Parameterized Zonal Overturning Streamfunction before limiting/smoothing', &
24701 'm3 s-1', conversion=US%Z_to_m*US%L_to_m**2*US%s_to_T)
2471 CS%id_sfn_unlim_y = register_diag_field('ocean_model', 'GM_sfn_unlim_y', diag%axesCvi, Time, &
2472 'Parameterized Meridional Overturning Streamfunction before limiting/smoothing', &
24731 'm3 s-1', conversion=US%Z_to_m*US%L_to_m**2*US%s_to_T)
2474
24751end subroutine thickness_diffuse_init
2476
2477!> Copies KH_u_GME and KH_v_GME from private type into arrays provided as arguments
24780subroutine thickness_diffuse_get_KH(CS, KH_u_GME, KH_v_GME, G, GV)
2479 type(thickness_diffuse_CS), intent(in) :: CS !< Control structure for this module
2480 type(ocean_grid_type), intent(in) :: G !< Grid structure
2481 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
2482 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: KH_u_GME !< Isopycnal height
2483 !! diffusivities at u-faces [L2 T-1 ~> m2 s-1]
2484 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1), intent(inout) :: KH_v_GME !< Isopycnal height
2485 !! diffusivities at v-faces [L2 T-1 ~> m2 s-1]
2486 ! Local variables
2487 integer :: i,j,k
2488
24890 do k=1,GV%ke+1 ; do j = G%jsc, G%jec ; do I = G%isc-1, G%iec
24900 KH_u_GME(I,j,k) = CS%KH_u_GME(I,j,k)
2491 enddo ; enddo ; enddo
2492
24930 do k=1,GV%ke+1 ; do J = G%jsc-1, G%jec ; do i = G%isc, G%iec
24940 KH_v_GME(i,J,k) = CS%KH_v_GME(i,J,k)
2495 enddo ; enddo ; enddo
2496
24970end subroutine thickness_diffuse_get_KH
2498
2499!> Deallocate the thickness_diffus3 control structure
25001subroutine thickness_diffuse_end(CS, CDp)
2501 type(thickness_diffuse_CS), intent(inout) :: CS !< Control structure for thickness_diffuse
2502 type(cont_diag_ptrs), intent(inout) :: CDp !< Continuity diagnostic control structure
2503
25041 if (CS%id_slope_x > 0) deallocate(CS%diagSlopeX)
25051 if (CS%id_slope_y > 0) deallocate(CS%diagSlopeY)
2506
25071 if (CS%id_GMwork > 0) deallocate(CS%GMwork)
2508
2509 ! NOTE: [uv]hGM may be allocated either here or the diagnostic module
25101 if (associated(CDp%uhGM)) deallocate(CDp%uhGM)
25111 if (associated(CDp%vhGM)) deallocate(CDp%vhGM)
2512
25131 if (CS%use_GME_thickness_diffuse) then
25140 deallocate(CS%KH_u_GME)
25150 deallocate(CS%KH_v_GME)
2516 endif
2517
25181 if (allocated(CS%khth2d)) deallocate(CS%khth2d)
25191end subroutine thickness_diffuse_end
2520
2521!> \namespace mom_thickness_diffuse
2522!!
2523!! \section section_gm Isopycnal height diffusion (aka Gent-McWilliams)
2524!!
2525!! Isopycnal height diffusion is implemented via along-layer mass fluxes
2526!! \f[
2527!! h^\dagger \leftarrow h^n - \Delta t \nabla \cdot ( \vec{uh}^* )
2528!! \f]
2529!! where the mass fluxes are cast as the difference in vector streamfunction
2530!!
2531!! \f[
2532!! \vec{uh}^* = \delta_k \vec{\psi} .
2533!! \f]
2534!!
2535!! The GM implementation of isopycnal height diffusion made the streamfunction proportional
2536!! to the potential density slope
2537!! \f[
2538!! \vec{\psi} = - \kappa_h \frac{\nabla_z \rho}{\partial_z \rho}
2539!! = \frac{g\kappa_h}{\rho_o} \frac{\nabla \rho}{N^2} = -\kappa_h \frac{M^2}{N^2}
2540!! \f]
2541!! but for robustness the scheme is implemented as
2542!! \f[
2543!! \vec{\psi} = -\kappa_h \frac{M^2}{\sqrt{N^4 + M^4}}
2544!! \f]
2545!! since the quantity \f$\frac{M^2}{\sqrt{N^4 + M^4}}\f$ is bounded between $-1$ and $1$ and does not change sign
2546!! if \f$N^2<0\f$.
2547!!
2548!! Optionally, the method of \cite ferrari2010, can be used to obtain the streamfunction which solves the
2549!! vertically elliptic equation:
2550!! \f[
2551!! \gamma_F \partial_z c^2 \partial_z \psi - N_*^2 \psi = -( 1 + \gamma_F ) \kappa_h N_*^2 \frac{M^2}{\sqrt{N^4+M^4}}
2552!! \f]
2553!! which recovers the previous streamfunction relation in the limit that \f$ c \rightarrow 0 \f$.
2554!! Here, \f$c=\max(c_{min},c_g)\f$ is the maximum of either \f$c_{min}\f$ and either the first baroclinic mode
2555!! wave-speed or the equivalent barotropic mode wave-speed.
2556!! \f$N_*^2 = \max(N^2,0)\f$ is a non-negative form of the square of the buoyancy frequency.
2557!! The parameter \f$\gamma_F\f$ is used to reduce the vertical smoothing length scale.
2558!! \f[
2559!! \kappa_h = \left( \kappa_o + \alpha_{s} L_{s}^2 < S N > + \alpha_{M} \kappa_{M} \right) r(\Delta x,L_d)
2560!! \f]
2561!! where \f$ S \f$ is the isoneutral slope magnitude, \f$ N \f$ is the buoyancy frequency,
2562!! \f$\kappa_{M}\f$ is the diffusivity calculated by the MEKE parameterization (mom_meke module) and
2563!! \f$ r(\Delta x,L_d) \f$ is a function of the local resolution (ratio of grid-spacing, \f$\Delta x\f$,
2564!! to deformation radius, \f$L_d\f$). The length \f$L_s\f$ is provided by the mom_lateral_mixing_coeffs module
2565!! (enabled with <code>USE_VARIABLE_MIXING=True</code> and the term \f$<SN>\f$ is the vertical average slope
2566!! times the buoyancy frequency prescribed by \cite visbeck1996.
2567!!
2568!! The result of the above expression is subsequently bounded by minimum and maximum values, including an upper
2569!! diffusivity consistent with numerical stability (\f$ \kappa_{cfl} \f$ is calculated internally).
2570!! \f[
2571!! \kappa_h \leftarrow \min{\left( \kappa_{max}, \kappa_{cfl}, \max{\left( \kappa_{min}, \kappa_h \right)} \right)}
2572!! f(c_g,z)
2573!! \f]
2574!!
2575!! where \f$f(c_g,z)\f$ is a vertical structure function.
2576!! \f$f(c_g,z)\f$ is calculated in module mom_lateral_mixing_coeffs.
2577!! If <code>KHTH_USE_EBT_STRUCT=True</code> then \f$f(c_g,z)\f$ is set to look like the equivalent barotropic
2578!! modal velocity structure. Otherwise \f$f(c_g,z)=1\f$ and the diffusivity is independent of depth.
2579!!
2580!! In order to calculate meaningful slopes in vanished layers, temporary copies of the thermodynamic variables
2581!! are passed through a vertical smoother, function vert_fill_ts():
2582!! \f{eqnarray*}{
2583!! \left[ 1 + \Delta t \kappa_{smth} \frac{\partial^2}{\partial_z^2} \right] \theta & \leftarrow & \theta \\
2584!! \left[ 1 + \Delta t \kappa_{smth} \frac{\partial^2}{\partial_z^2} \right] s & \leftarrow & s
2585!! \f}
2586!!
2587!! \subsection section_khth_module_parameters Module mom_thickness_diffuse parameters
2588!!
2589!! | Symbol | Module parameter |
2590!! | ------ | --------------- |
2591!! | - | <code>THICKNESSDIFFUSE</code> |
2592!! | \f$ \kappa_o \f$ | <code>KHTH</code> |
2593!! | \f$ \alpha_{s} \f$ | <code>KHTH_SLOPE_CFF</code> |
2594!! | \f$ \kappa_{min} \f$ | <code>KHTH_MIN</code> |
2595!! | \f$ \kappa_{max} \f$ | <code>KHTH_MAX</code> |
2596!! | - | <code>KHTH_MAX_CFL</code> |
2597!! | \f$ \kappa_{smth} \f$ | <code>KD_SMOOTH</code> |
2598!! | \f$ \alpha_{M} \f$ | <code>MEKE_KHTH_FAC</code> (from mom_meke module) |
2599!! | - | <code>KHTH_USE_EBT_STRUCT</code> (from mom_lateral_mixing_coeffs module) |
2600!! | - | <code>KHTH_USE_FGNV_STREAMFUNCTION</code> |
2601!! | \f$ \gamma_F \f$ | <code>FGNV_FILTER_SCALE</code> |
2602!! | \f$ c_{min} \f$ | <code>FGNV_C_MIN</code> |
2603!!
2604!! \subsection section_khth_module_reference References
2605!!
2606!! Ferrari, R., S.M. Griffies, A.J.G. Nurser and G.K. Vallis, 2010:
2607!! A boundary-value problem for the parameterized mesoscale eddy transport.
2608!! Ocean Modelling, 32, 143-156. http://doi.org/10.1016/j.ocemod.2010.01.004
2609!!
2610!! Visbeck, M., J.C. Marshall, H. Jones, 1996:
2611!! Dynamics of isolated convective regions in the ocean. J. Phys. Oceangr., 26, 1721-1734.
2612!! http://dx.doi.org/10.1175/1520-0485(1996)026%3C1721:DOICRI%3E2.0.CO;2
2613
26140end module MOM_thickness_diffuse