← back to index

src/parameterizations/vertical/MOM_CVMix_shear.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!> Interface to CVMix interior shear schemes
6module MOM_CVMix_shear
7
8!> \author Brandon Reichl
9
10use MOM_diag_mediator, only : post_data, register_diag_field, safe_alloc_ptr
11use MOM_diag_mediator, only : diag_ctrl, time_type
12use MOM_error_handler, only : MOM_error, is_root_pe, FATAL, WARNING, NOTE
13use MOM_file_parser, only : get_param, log_version, param_file_type
14use MOM_grid, only : ocean_grid_type
15use MOM_interface_heights, only : thickness_to_dz
16use MOM_unit_scaling, only : unit_scale_type
17use MOM_variables, only : thermo_var_ptrs
18use MOM_verticalGrid, only : verticalGrid_type
19use MOM_EOS, only : calculate_density
20use CVMix_shear, only : CVMix_init_shear, CVMix_coeffs_shear
21use MOM_kappa_shear, only : kappa_shear_is_used
22implicit none ; private
23
24#include <MOM_memory.h>
25
26public calculate_CVMix_shear, CVMix_shear_init, CVMix_shear_is_used, CVMix_shear_end
27
28! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional
29! consistency testing. These are noted in comments with units like Z, H, L, and T, along with
30! their mks counterparts with notation like "a velocity [Z T-1 ~> m s-1]". If the units
31! vary with the Boussinesq approximation, the Boussinesq variant is given first.
32
33!> Control structure including parameters for CVMix interior shear schemes.
34type, public :: CVMix_shear_cs ; private
35 logical :: use_LMD94 !< Flags to use the LMD94 scheme
36 logical :: use_PP81 !< Flags to use Pacanowski and Philander (JPO 1981)
37 integer :: n_smooth_ri !< Number of times to smooth Ri using a 1-2-1 filter
38 real :: Ri_zero !< LMD94 critical Richardson number [nondim]
39 real :: Nu_zero !< LMD94 maximum interior diffusivity [Z2 T-1 ~> m2 s-1]
40 real :: KPP_exp !< Exponent of unitless factor of diffusivities
41 !! for KPP internal shear mixing scheme [nondim]
42 real, allocatable, dimension(:,:,:) :: N2 !< Squared Brunt-Vaisala frequency [T-2 ~> s-2]
43 real, allocatable, dimension(:,:,:) :: S2 !< Squared shear frequency [T-2 ~> s-2]
44 real, allocatable, dimension(:,:,:) :: ri_grad !< Gradient Richardson number [nondim]
45 real, allocatable, dimension(:,:,:) :: ri_grad_orig !< Gradient Richardson number
46 !! after smoothing [nondim]
47 character(10) :: Mix_Scheme !< Mixing scheme name (string)
48
49 type(diag_ctrl), pointer :: diag => NULL() !< Pointer to the diagnostics control structure
50 !>@{ Diagnostic handles
51 integer :: id_N2 = -1, id_S2 = -1, id_ri_grad = -1, id_kv = -1, id_kd = -1
52 integer :: id_ri_grad_orig = -1
53 !>@}
54
55end type CVMix_shear_cs
56
57character(len=40) :: mdl = "MOM_CVMix_shear" !< This module's name.
58
59contains
60
61!> Subroutine for calculating (internal) vertical diffusivities/viscosities
620subroutine calculate_CVMix_shear(u_H, v_H, h, tv, kd, kv, G, GV, US, CS )
63 type(ocean_grid_type), intent(in) :: G !< Grid structure.
64 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure.
65 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
66 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: u_H !< Initial zonal velocity on T points [L T-1 ~> m s-1]
67 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: v_H !< Initial meridional velocity on T
68 !! points [L T-1 ~> m s-1]
69 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> m or kg m-2].
70 type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamics structure.
71 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(out) :: kd !< The vertical diffusivity at each interface
72 !! (not layer!) [H Z T-1 ~> m2 s-1 or kg m-1 s-1]
73 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(out) :: kv !< The vertical viscosity at each interface
74 !! (not layer!) [H Z T-1 ~> m2 s-1 or Pa s]
75 type(CVMix_shear_cs), pointer :: CS !< The control structure returned by a previous
76 !! call to CVMix_shear_init.
77 ! Local variables
78 integer :: i, j, k, kk, km1, s
79 real :: GoRho ! Gravitational acceleration divided by density [Z T-2 R-1 ~> m4 s-2 kg-1]
80 real :: pref ! Interface pressures [R L2 T-2 ~> Pa]
81 real :: DU, DV ! Velocity differences [L T-1 ~> m s-1]
82 real :: dz_int ! Grid spacing around an interface [Z ~> m]
83 real :: N2 ! Buoyancy frequency at an interface [T-2 ~> s-2]
84 real :: S2 ! Shear squared at an interface [T-2 ~> s-2]
85 real :: dummy ! A dummy variable [nondim]
86 real :: dRho ! Buoyancy differences [Z T-2 ~> m s-2]
870 real, dimension(SZI_(G),SZK_(GV)) :: dz ! Height change across layers [Z ~> m]
880 real, dimension(2*(GV%ke)) :: pres_1d ! A column of interface pressures [R L2 T-2 ~> Pa]
890 real, dimension(2*(GV%ke)) :: temp_1d ! A column of temperatures [C ~> degC]
900 real, dimension(2*(GV%ke)) :: salt_1d ! A column of salinities [S ~> ppt]
910 real, dimension(2*(GV%ke)) :: rho_1d ! A column of densities at interface pressures [R ~> kg m-3]
920 real, dimension(GV%ke+1) :: Ri_Grad !< Gradient Richardson number [nondim]
930 real, dimension(GV%ke+1) :: Ri_Grad_prev !< Gradient Richardson number before s.th smoothing iteration [nondim]
940 real, dimension(GV%ke+1) :: Kvisc !< Vertical viscosity at interfaces [m2 s-1]
950 real, dimension(GV%ke+1) :: Kdiff !< Diapycnal diffusivity at interfaces [m2 s-1]
96 real :: epsln !< Threshold to identify vanished layers [H ~> m or kg m-2]
97
98 ! some constants
990 GoRho = GV%g_Earth_Z_T2 / GV%Rho0
1000 epsln = 1.e-10 * GV%m_to_H
101
1020 do j = G%jsc, G%jec
103
104 ! Find the vertical distances across layers.
1050 call thickness_to_dz(h, tv, dz, j, G, GV)
106
1070 do i = G%isc, G%iec
108
109 ! skip calling for land points
1100 if (G%mask2dT(i,j)==0.) cycle
111
112 ! Richardson number computed for each cell in a column.
1130 pRef = 0. ; if (associated(tv%p_surf)) pRef = tv%p_surf(i,j)
1140 Ri_Grad(:)=1.e8 !Initialize w/ large Richardson value
1150 do k=1,GV%ke
116 ! pressure, temp, and saln for EOS
117 ! kk+1 = k fields
118 ! kk+2 = km1 fields
1190 km1 = max(1, k-1)
1200 kk = 2*(k-1)
1210 pres_1D(kk+1) = pRef
1220 pres_1D(kk+2) = pRef
1230 Temp_1D(kk+1) = tv%T(i,j,k)
1240 Temp_1D(kk+2) = tv%T(i,j,km1)
1250 Salt_1D(kk+1) = tv%S(i,j,k)
1260 Salt_1D(kk+2) = tv%S(i,j,km1)
127
128 ! pRef is pressure at interface between k and km1.
129 ! iterate pRef for next pass through k-loop.
1300 pRef = pRef + (GV%g_Earth * GV%H_to_RZ) * h(i,j,k)
131
132 enddo ! k-loop finishes
133
134 ! compute in-situ density [R ~> kg m-3]
1350 call calculate_density(Temp_1D, Salt_1D, pres_1D, rho_1D, tv%eqn_of_state)
136
137 ! N2 (can be negative) on interface
1380 do k = 1, GV%ke
1390 km1 = max(1, k-1)
1400 kk = 2*(k-1)
1410 DU = u_h(i,j,k) - u_h(i,j,km1)
1420 DV = v_h(i,j,k) - v_h(i,j,km1)
1430 if (GV%Boussinesq .or. GV%semi_Boussinesq) then
1440 dRho = GoRho * (rho_1D(kk+1) - rho_1D(kk+2))
145 else
1460 dRho = GV%g_Earth_Z_T2 * (rho_1D(kk+1) - rho_1D(kk+2)) / (0.5*(rho_1D(kk+1) + rho_1D(kk+2)))
147 endif
1480 dz_int = 0.5*(dz(i,km1) + dz(i,k)) + GV%dZ_subroundoff
1490 N2 = DRHO / dz_int
1500 S2 = US%L_to_Z**2*((DU*DU) + (DV*DV)) / (dz_int*dz_int)
1510 Ri_Grad(k) = max(0., N2) / max(S2, 1.e-10*US%T_to_s**2)
152
153 ! fill 3d arrays, if user asks for diagnostics
1540 if (CS%id_N2 > 0) CS%N2(i,j,k) = N2
1550 if (CS%id_S2 > 0) CS%S2(i,j,k) = S2
156
157 enddo
158
1590 Ri_grad(GV%ke+1) = Ri_grad(GV%ke)
160
1610 if (CS%n_smooth_ri > 0) then
162
1630 if (CS%id_ri_grad_orig > 0) CS%ri_grad_orig(i,j,:) = Ri_Grad(:)
164
165 ! 1) fill Ri_grad in vanished layers with adjacent value
1660 do k = 2, GV%ke
1670 if (h(i,j,k) <= epsln) Ri_grad(k) = Ri_grad(k-1)
168 enddo
169
1700 Ri_grad(GV%ke+1) = Ri_grad(GV%ke)
171
1720 do s=1,CS%n_smooth_ri
173
1740 Ri_Grad_prev(:) = Ri_Grad(:)
175
176 ! 2) vertically smooth Ri with 1-2-1 filter
1770 dummy = 0.25 * Ri_grad_prev(2)
1780 do k = 3, GV%ke
1790 Ri_Grad(k) = dummy + 0.5 * Ri_Grad_prev(k) + 0.25 * Ri_grad_prev(k+1)
1800 dummy = 0.25 * Ri_grad(k)
181 enddo
182 enddo
183
1840 Ri_grad(GV%ke+1) = Ri_grad(GV%ke)
185
186 endif
187
1880 if (CS%id_ri_grad > 0) CS%ri_grad(i,j,:) = Ri_Grad(:)
189
1900 do K=1,GV%ke+1
1910 Kvisc(K) = GV%HZ_T_to_m2_s * kv(i,j,K)
1920 Kdiff(K) = GV%HZ_T_to_m2_s * kd(i,j,K)
193 enddo
194
195 ! Call to CVMix wrapper for computing interior mixing coefficients.
196 call CVMix_coeffs_shear(Mdiff_out=Kvisc(:), &
197 Tdiff_out=Kdiff(:), &
198 RICH=Ri_Grad(:), &
199 nlev=GV%ke, &
2000 max_nlev=GV%ke)
2010 do K=1,GV%ke+1
2020 kv(i,j,K) = GV%m2_s_to_HZ_T * Kvisc(K)
2030 kd(i,j,K) = GV%m2_s_to_HZ_T * Kdiff(K)
204 enddo
205 enddo
206 enddo
207
208 ! write diagnostics
2090 if (CS%id_kd > 0) call post_data(CS%id_kd, kd, CS%diag)
2100 if (CS%id_kv > 0) call post_data(CS%id_kv, kv, CS%diag)
2110 if (CS%id_N2 > 0) call post_data(CS%id_N2, CS%N2, CS%diag)
2120 if (CS%id_S2 > 0) call post_data(CS%id_S2, CS%S2, CS%diag)
2130 if (CS%id_ri_grad > 0) call post_data(CS%id_ri_grad, CS%ri_grad, CS%diag)
2140 if (CS%id_ri_grad_orig > 0) call post_data(CS%id_ri_grad_orig ,CS%ri_grad_orig, CS%diag)
215
2160end subroutine calculate_CVMix_shear
217
218
219!> Initialized the CVMix internal shear mixing routine.
220!! \todo Does this note require emphasis?
221!! \note *This is where we test to make sure multiple internal shear
222!! mixing routines (including JHL) are not enabled at the same time.*
223!! (returns) CVMix_shear_init - True if module is to be used, False otherwise
2241logical function CVMix_shear_init(Time, G, GV, US, param_file, diag, CS)
225 type(time_type), intent(in) :: Time !< The current time.
226 type(ocean_grid_type), intent(in) :: G !< Grid structure.
227 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure.
228 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
229 type(param_file_type), intent(in) :: param_file !< Run-time parameter file handle
230 type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure.
231 type(CVMix_shear_cs), pointer :: CS !< This module's control structure.
232 ! Local variables
233 integer :: NumberTrue=0
234 logical :: use_JHL
235 logical :: use_LMD94
236 logical :: use_PP81
237
238! This include declares and sets the variable "version".
239#include "version_variable.h"
240
2411 if (associated(CS)) then
242 call MOM_error(WARNING, "CVMix_shear_init called with an associated "// &
2430 "control structure.")
2440 return
245 endif
246
247! Set default, read and log parameters
2481 call get_param(param_file, mdl, "USE_LMD94", use_LMD94, default=.false., do_not_log=.true.)
2491 call get_param(param_file, mdl, "USE_PP81", use_PP81, default=.false., do_not_log=.true.)
250 call log_version(param_file, mdl, version, &
251 "Parameterization of shear-driven turbulence via CVMix (various options)", &
2521 all_default=.not.(use_PP81.or.use_LMD94))
253 call get_param(param_file, mdl, "USE_LMD94", use_LMD94, &
254 "If true, use the Large-McWilliams-Doney (JGR 1994) "//&
2551 "shear mixing parameterization.", default=.false.)
2561 if (use_LMD94) &
2570 NumberTrue=NumberTrue + 1
258 call get_param(param_file, mdl, "USE_PP81", use_PP81, &
259 "If true, use the Pacanowski and Philander (JPO 1981) "//&
2601 "shear mixing parameterization.", default=.false.)
2611 if (use_PP81) &
2620 NumberTrue = NumberTrue + 1
2631 use_JHL=kappa_shear_is_used(param_file)
2641 if (use_JHL) NumberTrue = NumberTrue + 1
265 ! After testing for interior schemes, make sure only 0 or 1 are enabled.
266 ! Otherwise, warn user and kill job.
2671 if ((NumberTrue) > 1) then
268 call MOM_error(FATAL, 'MOM_CVMix_shear_init: '// &
269 'Multiple shear driven internal mixing schemes selected, '//&
2700 'please disable all but one scheme to proceed.')
271 endif
272
2731 CVMix_shear_init = use_PP81 .or. use_LMD94
274
275 ! Forego remainder of initialization if not using this scheme
2761 if (.not. CVMix_shear_init) return
277
2780 allocate(CS)
2790 CS%use_LMD94 = use_LMD94
2800 CS%use_PP81 = use_PP81
2810 if (use_LMD94) &
2820 CS%Mix_Scheme = 'KPP'
2830 if (use_PP81) &
2840 CS%Mix_Scheme = 'PP'
285
286 call get_param(param_file, mdl, "NU_ZERO", CS%Nu_Zero, &
287 "Leading coefficient in KPP shear mixing.", &
2880 units="m2 s-1", default=5.e-3, scale=US%m2_s_to_Z2_T)
289 call get_param(param_file, mdl, "RI_ZERO", CS%Ri_Zero, &
290 "Critical Richardson for KPP shear mixing, "// &
291 "NOTE this the internal mixing and this is "// &
292 "not for setting the boundary layer depth.", &
2930 units="nondim", default=0.8)
294 call get_param(param_file, mdl, "KPP_EXP", CS%KPP_exp, &
295 "Exponent of unitless factor of diffusivities, "// &
296 "for KPP internal shear mixing scheme.", &
2970 units="nondim", default=3.0)
298 call get_param(param_file, mdl, "N_SMOOTH_RI", CS%n_smooth_ri, &
299 "If > 0, vertically smooth the Richardson "// &
300 "number by applying a 1-2-1 filter N_SMOOTH_RI times.", &
3010 default=0)
302 call cvmix_init_shear(mix_scheme=CS%Mix_Scheme, &
303 KPP_nu_zero=US%Z2_T_to_m2_s*CS%Nu_Zero, &
304 KPP_Ri_zero=CS%Ri_zero, &
3050 KPP_exp=CS%KPP_exp)
306
307 ! Register diagnostics; allocation and initialization
3080 CS%diag => diag
309
310 CS%id_N2 = register_diag_field('ocean_model', 'N2_shear', diag%axesTi, Time, &
3110 'Square of Brunt-Vaisala frequency used by MOM_CVMix_shear module', '1/s2', conversion=US%s_to_T**2)
3120 if (CS%id_N2 > 0) then
3130 allocate( CS%N2( SZI_(G), SZJ_(G), SZK_(GV)+1 ), source=0. )
314 endif
315
316 CS%id_S2 = register_diag_field('ocean_model', 'S2_shear', diag%axesTi, Time, &
3170 'Square of vertical shear used by MOM_CVMix_shear module','1/s2', conversion=US%s_to_T**2)
3180 if (CS%id_S2 > 0) then
3190 allocate( CS%S2( SZI_(G), SZJ_(G), SZK_(GV)+1 ), source=0. )
320 endif
321
322 CS%id_ri_grad = register_diag_field('ocean_model', 'ri_grad_shear', diag%axesTi, Time, &
3230 'Gradient Richarson number used by MOM_CVMix_shear module','nondim')
3240 if (CS%id_ri_grad > 0) then !Initialize w/ large Richardson value
3250 allocate( CS%ri_grad( SZI_(G), SZJ_(G), SZK_(GV)+1 ), source=1.e8 )
326 endif
327
3280 if (CS%n_smooth_ri > 0) then
329 CS%id_ri_grad_orig = register_diag_field('ocean_model', 'ri_grad_shear_orig', &
330 diag%axesTi, Time, &
331 'Original gradient Richarson number, before smoothing was applied. This is '//&
3320 'part of the MOM_CVMix_shear module and only available when N_SMOOTH_RI > 0','nondim')
333 endif
3340 if (CS%id_ri_grad_orig > 0 .or. CS%n_smooth_ri > 0) then !Initialize w/ large Richardson value
3350 allocate( CS%ri_grad_orig( SZI_(G), SZJ_(G), SZK_(GV)+1 ), source=1.e8 )
336 endif
337
338 CS%id_kd = register_diag_field('ocean_model', 'kd_shear_CVMix', diag%axesTi, Time, &
3390 'Vertical diffusivity added by MOM_CVMix_shear module', 'm2/s', conversion=GV%HZ_T_to_m2_s)
340 CS%id_kv = register_diag_field('ocean_model', 'kv_shear_CVMix', diag%axesTi, Time, &
3410 'Vertical viscosity added by MOM_CVMix_shear module', 'm2/s', conversion=GV%HZ_T_to_m2_s)
342
3430end function CVMix_shear_init
344
345!> Reads the parameters "USE_LMD94" and "USE_PP81" and returns true if either is true.
346!! This function allows other modules to know whether this parameterization will
347!! be used without needing to duplicate the log entry.
3482logical function CVMix_shear_is_used(param_file)
349 type(param_file_type), intent(in) :: param_file !< Run-time parameter files handle.
350 ! Local variables
351 logical :: LMD94, PP81
352 call get_param(param_file, mdl, "USE_LMD94", LMD94, &
3532 default=.false., do_not_log=.true.)
354 call get_param(param_file, mdl, "USE_PP81", PP81, &
3552 default=.false., do_not_log=.true.)
3562 CVMix_shear_is_used = (LMD94 .or. PP81)
3572end function CVMix_shear_is_used
358
359!> Clear pointers and deallocate memory
3600subroutine CVMix_shear_end(CS)
361 type(CVMix_shear_cs), intent(inout) :: CS !< Control structure for this module that
362 !! will be deallocated in this subroutine
3630 if (CS%id_N2 > 0) deallocate(CS%N2)
3640 if (CS%id_S2 > 0) deallocate(CS%S2)
3650 if (CS%id_ri_grad > 0) deallocate(CS%ri_grad)
3660end subroutine CVMix_shear_end
367
3680end module MOM_CVMix_shear