← back to index

src/parameterizations/vertical/MOM_CVMix_KPP.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!> Provides the K-Profile Parameterization (KPP) of Large et al., 1994, via CVMix.
6module MOM_CVMix_KPP
7
8use MOM_coms, only : max_across_PEs
9use MOM_debugging, only : hchksum, is_NaN
10use MOM_diag_mediator, only : time_type, diag_ctrl, safe_alloc_ptr, post_data
11use MOM_diag_mediator, only : query_averaging_enabled, register_diag_field
12use MOM_error_handler, only : MOM_error, MOM_mesg, FATAL, WARNING, is_root_PE
13use MOM_EOS, only : EOS_type, calculate_density
14use MOM_file_parser, only : get_param, log_param, log_version, param_file_type
15use MOM_file_parser, only : openParameterBlock, closeParameterBlock
16use MOM_grid, only : ocean_grid_type, isPointInCell
17use MOM_interface_heights, only : thickness_to_dz
18use MOM_restart, only : MOM_restart_CS, register_restart_field
19use MOM_unit_scaling, only : unit_scale_type
20use MOM_variables, only : thermo_var_ptrs
21use MOM_verticalGrid, only : verticalGrid_type
22use MOM_wave_interface, only : wave_parameters_CS, Get_Langmuir_Number, get_wave_method
23use MOM_domains, only : pass_var
24use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end
25use MOM_cpu_clock, only : CLOCK_MODULE, CLOCK_ROUTINE
26use MOM_tracer_types, only : tracer_type
27
28use CVMix_kpp, only : CVMix_init_kpp, CVMix_put_kpp, CVMix_get_kpp_real
29use CVMix_kpp, only : CVMix_coeffs_kpp
30use CVMix_kpp, only : CVMix_kpp_compute_OBL_depth
31use CVMix_kpp, only : CVMix_kpp_compute_turbulent_scales
32use CVMix_kpp, only : CVMix_kpp_compute_bulk_Richardson
33use CVMix_kpp, only : CVMix_kpp_compute_unresolved_shear
34use CVMix_kpp, only : CVMix_kpp_params_type
35use CVMix_kpp, only : CVMix_kpp_compute_kOBL_depth
36use CVMix_kpp, only : CVMix_kpp_compute_StokesXi
37
38implicit none ; private
39
40#include "MOM_memory.h"
41
42public :: register_KPP_restarts
43public :: KPP_init
44public :: KPP_compute_BLD
45public :: KPP_calculate
46public :: KPP_end
47public :: KPP_NonLocalTransport_temp
48public :: KPP_NonLocalTransport_saln
49public :: KPP_NonLocalTransport
50public :: KPP_get_BLD
51
52! Enumerated constants
53integer, private, parameter :: NLT_SHAPE_CVMix = 0 !< Use the CVMix profile
54integer, private, parameter :: NLT_SHAPE_LINEAR = 1 !< Linear, \f$ G(\sigma) = 1-\sigma \f$
55integer, private, parameter :: NLT_SHAPE_PARABOLIC = 2 !< Parabolic, \f$ G(\sigma) = (1-\sigma)^2 \f$
56integer, private, parameter :: NLT_SHAPE_CUBIC = 3 !< Cubic, \f$ G(\sigma) = 1 + (2\sigma-3) \sigma^2\f$
57integer, private, parameter :: NLT_SHAPE_CUBIC_LMD = 4 !< Original shape,
58 !! \f$ G(\sigma) = \frac{27}{4} \sigma (1-\sigma)^2 \f$
59
60integer, private, parameter :: SW_METHOD_ALL_SW = 0 !< Use all shortwave radiation
61integer, private, parameter :: SW_METHOD_MXL_SW = 1 !< Use shortwave radiation absorbed in mixing layer
62integer, private, parameter :: SW_METHOD_LV1_SW = 2 !< Use shortwave radiation absorbed in layer 1
63integer, private, parameter :: LT_K_CONSTANT = 1, & !< Constant enhance K through column
64 LT_K_SCALED = 2, & !< Enhance K scales with G(sigma)
65 LT_K_MODE_CONSTANT = 1, & !< Prescribed enhancement for K
66 LT_K_MODE_VR12 = 2, & !< Enhancement for K based on
67 !! Van Roekel et al., 2012
68 LT_K_MODE_RW16 = 3, & !< Enhancement for K based on
69 !! Reichl et al., 2016
70 LT_VT2_MODE_CONSTANT = 1, & !< Prescribed enhancement for Vt2
71 LT_VT2_MODE_VR12 = 2, & !< Enhancement for Vt2 based on
72 !! Van Roekel et al., 2012
73 LT_VT2_MODE_RW16 = 3, & !< Enhancement for Vt2 based on
74 !! Reichl et al., 2016
75 LT_VT2_MODE_LF17 = 4 !< Enhancement for Vt2 based on
76 !! Li and Fox-Kemper, 2017
77
78!> Control structure for containing KPP parameters/data
79type, public :: KPP_CS ; private
80
81 ! Parameters
82 real :: Ri_crit !< Critical bulk Richardson number (defines OBL depth) [nondim]
83 real :: vonKarman !< von Karman constant (dimensionless) [nondim]
84 real :: cs !< Parameter for computing velocity scale function (dimensionless) [nondim]
85 real :: cs2 !< Parameter for multiplying by non-local term [nondim]
86 ! This is active for NLT_SHAPE_CUBIC_LMD only
87 logical :: enhance_diffusion !< If True, add enhanced diffusivity at base of boundary layer.
88 character(len=32) :: interpType !< Type of interpolation to compute bulk Richardson number
89 character(len=32) :: interpType2 !< Type of interpolation to compute diff and visc at OBL_depth
90 logical :: StokesMOST !< If True, use Stokes similarity package
91 logical :: computeEkman !< If True, compute Ekman depth limit for OBLdepth
92 logical :: computeMoninObukhov !< If True, compute Monin-Obukhov limit for OBLdepth
93 logical :: passiveMode !< If True, makes KPP passive meaning it does NOT alter the diffusivity
94 real :: deepOBLoffset !< If non-zero, is a distance from the bottom that the OBL can not
95 !! penetrate through [Z ~> m]
96 real :: minOBLdepth !< If non-zero, is a minimum depth for the OBL [Z ~> m]
97 real :: surf_layer_ext !< Fraction of OBL depth considered in the surface layer [nondim]
98 real :: minVtsqr !< Min for the squared unresolved velocity used in Rib CVMix
99 !! calculation [L2 T-2 ~> m2 s-2]
100 logical :: fixedOBLdepth !< If True, will fix the OBL depth at fixedOBLdepth_value
101 real :: fixedOBLdepth_value !< value for the fixed OBL depth when fixedOBLdepth==True [Z ~> m]
102 logical :: debug !< If True, calculate checksums and write debugging information
103 character(len=30) :: MatchTechnique !< Method used in CVMix for setting diffusivity and NLT profile functions
104 integer :: NLT_shape !< MOM6 over-ride of CVMix NLT shape function
105 logical :: applyNonLocalTrans !< If True, apply non-local transport to all tracers
106 integer :: n_smooth !< Number of times smoothing operator is applied on OBLdepth.
107 logical :: deepen_only !< If true, apply OBLdepth smoothing at a cell only if the OBLdepth gets deeper.
108 logical :: KPPzeroDiffusivity !< If True, will set diffusivity and viscosity from KPP to zero
109 !! for testing purposes.
110 logical :: KPPisAdditive !< If True, will add KPP diffusivity to initial diffusivity.
111 !! If False, will replace initial diffusivity wherever KPP diffusivity
112 !! is non-zero.
113 real :: min_thickness !< A minimum thickness used to avoid division by small numbers
114 !! in the vicinity of vanished layers [Z ~> m]
115 integer :: SW_METHOD !< Sets method for using shortwave radiation in surface buoyancy flux
116 logical :: LT_K_Enhancement !< Flags if enhancing mixing coefficients due to LT
117 integer :: LT_K_Shape !< Integer for constant or shape function enhancement
118 integer :: LT_K_Method !< Integer for mixing coefficients LT method
119 real :: KPP_CVt2 !< Parameter for Stokes MOST convection entrainment [nondim]
120 real :: KPP_K_ENH_FAC !< Factor to multiply by K if Method is CONSTANT [nondim]
121 logical :: LT_Vt2_Enhancement !< Flags if enhancing Vt2 due to LT
122 integer :: LT_VT2_METHOD !< Integer for Vt2 LT method
123 real :: KPP_VT2_ENH_FAC !< Factor to multiply by VT2 if Method is CONSTANT [nondim]
124 real :: MLD_guess_min !< The minimum estimate of the mixed layer depth used to
125 !! calculate the Langmuir number for Langmuir turbulence
126 !! enhancement with KPP [Z ~> m]
127 logical :: STOKES_MIXING !< Flag if model is mixing down Stokes gradient
128 !! This is relevant for which current to use in RiB
129 integer :: answer_date !< The vintage of the order of arithmetic in the CVMix KPP
130 !! calculations. Values below 20240501 recover the answers
131 !! from early in 2024, while higher values use expressions
132 !! that have been refactored for rotational symmetry.
133
134 !> CVMix parameters
135 type(CVMix_kpp_params_type), pointer :: KPP_params => NULL()
136
137 type(diag_ctrl), pointer :: diag => NULL() !< Pointer to diagnostics control structure
138 !>@{ Diagnostic handles
139 integer :: id_OBLdepth = -1, id_BulkRi = -1
140 integer :: id_N = -1, id_N2 = -1
141 integer :: id_Ws = -1, id_Vt2 = -1
142 integer :: id_BulkUz2 = -1, id_BulkDrho = -1
143 integer :: id_uStar = -1, id_buoyFlux = -1
144 integer :: id_sigma = -1, id_Kv_KPP = -1
145 integer :: id_Kt_KPP = -1, id_Ks_KPP = -1
146 integer :: id_Tsurf = -1, id_Ssurf = -1
147 integer :: id_Usurf = -1, id_Vsurf = -1
148 integer :: id_Kd_in = -1
149 integer :: id_NLTt = -1
150 integer :: id_NLTs = -1
151 integer :: id_EnhK = -1, id_EnhVt2 = -1
152 integer :: id_EnhW = -1
153 integer :: id_La_SL = -1
154 integer :: id_OBLdepth_original = -1
155 integer :: id_StokesXI = -1
156 integer :: id_Lam2 = -1
157 !>@}
158
159 ! Diagnostics arrays
160 real, pointer, dimension(:,:) :: OBLdepth !< Depth (positive) of ocean boundary layer (OBL) [Z ~> m]
161 real, allocatable, dimension(:,:) :: OBLdepth_original !< Depth (positive) of OBL without smoothing [Z ~> m]
162 real, allocatable, dimension(:,:) :: StokesParXI !< Stokes similarity parameter [nondim]
163 real, allocatable, dimension(:,:) :: Lam2 !< La^(-2) = Ustk0/u* [nondim]
164 real, allocatable, dimension(:,:) :: kOBL !< Level (+fraction) of OBL extent [nondim]
165 real, allocatable, dimension(:,:) :: OBLdepthprev !< previous Depth (positive) of OBL [Z ~> m]
166 real, allocatable, dimension(:,:) :: La_SL !< Langmuir number used in KPP [nondim]
167 real, allocatable, dimension(:,:,:) :: dRho !< Bulk difference in density [R ~> kg m-3]
168 real, allocatable, dimension(:,:,:) :: Uz2 !< Square of bulk difference in resolved velocity [L2 T-2 ~> m2 s-2]
169 real, allocatable, dimension(:,:,:) :: BulkRi !< Bulk Richardson number for each layer [nondim]
170 real, allocatable, dimension(:,:,:) :: sigma !< Sigma coordinate (dimensionless) [nondim]
171 real, allocatable, dimension(:,:,:) :: Ws !< Turbulent velocity scale for scalars [Z T-1 ~> m s-1]
172 real, allocatable, dimension(:,:,:) :: N !< Brunt-Vaisala frequency [T-1 ~> s-1]
173 real, allocatable, dimension(:,:,:) :: N2 !< Squared Brunt-Vaisala frequency [T-2 ~> s-2]
174 real, allocatable, dimension(:,:,:) :: Vt2 !< Unresolved squared turbulence velocity for
175 !! bulk Ri [Z2 T-2 ~> m2 s-2]
176 real, allocatable, dimension(:,:,:) :: Kt_KPP !< Temp diffusivity from KPP [Z2 T-1 ~> m2 s-1]
177 real, allocatable, dimension(:,:,:) :: Ks_KPP !< Scalar diffusivity from KPP [Z2 T-1 ~> m2 s-1]
178 real, allocatable, dimension(:,:,:) :: Kv_KPP !< Viscosity due to KPP [Z2 T-1 ~> m2 s-1]
179 real, allocatable, dimension(:,:) :: Tsurf !< Temperature of surface layer [C ~> degC]
180 real, allocatable, dimension(:,:) :: Ssurf !< Salinity of surface layer [S ~> ppt]
181 real, allocatable, dimension(:,:) :: Usurf !< i-velocity of surface layer [L T-1 ~> m s-1]
182 real, allocatable, dimension(:,:) :: Vsurf !< j-velocity of surface layer [L T-1 ~> m s-1]
183 real, allocatable, dimension(:,:,:) :: EnhK !< Enhancement for mixing coefficient [nondim]
184 real, allocatable, dimension(:,:,:) :: EnhVt2 !< Enhancement for Vt2 [nondim]
185
186end type KPP_CS
187
188!>@{ CPU time clocks
189integer :: id_clock_KPP_calc, id_clock_KPP_compute_BLD, id_clock_KPP_smoothing
190!>@}
191
192#define __DO_SAFETY_CHECKS__
193
194contains
195
196!> Routine to register restarts, pass-through to children modules
1971subroutine register_KPP_restarts(G, param_file, restart_CSp, CS)
198 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
199 type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time parameters
200 type(MOM_restart_CS), pointer :: restart_CSp !< MOM restart control structure
201 type(KPP_CS), pointer :: CS !< module control structure
202
203 character(len=40) :: mdl = 'MOM_CVMix_KPP' !< name of this module
204 logical :: use_kpp, fpmix
205
2061 if (associated(CS)) call MOM_error(FATAL, 'MOM_CVMix_KPP, register_KPP_restarts: '// &
2070 'Control structure has already been initialized')
2081 call get_param(param_file, mdl, "USE_KPP", use_kpp, default=.false., do_not_log=.true.)
209 ! Forego remainder of initialization if not using this scheme
2101 if (.not. use_kpp) return
2110 allocate(CS)
212
2130 allocate(CS%OBLdepth(SZI_(G),SZJ_(G)), source=0.0)
214
215 ! FPMIX is needed to decide if boundary layer depth should be added to restart file
216 call get_param(param_file, '', "FPMIX", fpmix, &
217 "If true, add non-local momentum flux increments and diffuse down the Eulerian gradient.", &
2180 default=.false., do_not_log=.true.)
2190 if (fpmix) call register_restart_field(CS%OBLdepth, 'KPP_OBLdepth', .false., restart_CSp)
220
221end subroutine register_KPP_restarts
222
223!> Initialize the CVMix KPP module and set up diagnostics
224!! Returns True if KPP is to be used, False otherwise.
2251logical function KPP_init(paramFile, G, GV, US, diag, Time, CS, passive)
226
227 ! Arguments
228 type(param_file_type), intent(in) :: paramFile !< File parser
229 type(ocean_grid_type), intent(in) :: G !< Ocean grid
230 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
231 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
232 type(diag_ctrl), target, intent(in) :: diag !< Diagnostics
233 type(time_type), intent(in) :: Time !< Model time
234 type(KPP_CS), pointer :: CS !< Control structure
235 logical, optional, intent(out) :: passive !< Copy of %passiveMode
236
237 ! Local variables
238# include "version_variable.h"
239 character(len=40) :: mdl = 'MOM_CVMix_KPP' !< name of this module
240 character(len=20) :: string !< local temporary string
241 character(len=20) :: langmuir_mixing_opt = 'NONE' !< Langmuir mixing option to be passed to CVMix, e.g., LWF16
242 character(len=20) :: langmuir_entrainment_opt = 'NONE' !< Langmuir entrainment option to be
243 !! passed to CVMix, e.g., LWF16
244 integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags.
245 logical :: CS_IS_ONE=.false. !< Logical for setting Cs based on Non-local
246 logical :: lnoDGat1=.false. !< True => G'(1) = 0 (shape function)
247 !! False => compute G'(1) as in LMD94
248 ! Read parameters
2491 call get_param(paramFile, mdl, "USE_KPP", KPP_init, default=.false., do_not_log=.true.)
250 call log_version(paramFile, mdl, version, 'This is the MOM wrapper to CVMix:KPP\n' // &
2511 'See http://cvmix.github.io/', all_default=.not.KPP_init)
252 call get_param(paramFile, mdl, "USE_KPP", KPP_init, &
253 "If true, turns on the [CVMix] KPP scheme of Large et al., 1994, "// &
254 "to calculate diffusivities and non-local transport in the OBL.", &
2551 default=.false.)
256 ! Forego remainder of initialization if not using this scheme
2571 if (.not. KPP_init) return
258
259 call get_param(paramFile, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, &
260 "This sets the default value for the various _ANSWER_DATE parameters.", &
2610 default=99991231, do_not_log=.true.)
262
2630 call openParameterBlock(paramFile,'KPP')
264 call get_param(paramFile, mdl, 'PASSIVE', CS%passiveMode, &
265 'If True, puts KPP into a passive-diagnostic mode.', &
2660 default=.False.)
267 !BGR: Note using PASSIVE for KPP creates warning for PASSIVE from Convection
268 ! should we create a separate flag?
2690 if (present(passive)) passive=CS%passiveMode ! This is passed back to the caller so
270 ! the caller knows to not use KPP output
271 call get_param(paramFile, mdl, 'APPLY_NONLOCAL_TRANSPORT', CS%applyNonLocalTrans, &
272 'If True, applies the non-local transport to all tracers. '// &
273 'If False, calculates the non-local transport and tendencies but '//&
274 'purely for diagnostic purposes.', &
2750 default=.not. CS%passiveMode)
276 call get_param(paramFile, mdl, 'N_SMOOTH', CS%n_smooth, &
277 'The number of times the 1-1-4-1-1 Laplacian filter is applied on OBL depth.', &
2780 default=0)
2790 if (CS%n_smooth > G%domain%nihalo) then
2800 call MOM_error(FATAL,'KPP smoothing number (N_SMOOTH) cannot be greater than NIHALO.')
2810 elseif (CS%n_smooth > G%domain%njhalo) then
2820 call MOM_error(FATAL,'KPP smoothing number (N_SMOOTH) cannot be greater than NJHALO.')
283 endif
2840 if (CS%n_smooth > 0) then
285 call get_param(paramFile, mdl, 'DEEPEN_ONLY_VIA_SMOOTHING', CS%deepen_only, &
286 'If true, apply OBLdepth smoothing at a cell only if the OBLdepth '// &
287 'gets deeper via smoothing.', &
2880 default=.false.)
2890 id_clock_KPP_smoothing = cpu_clock_id('(Ocean KPP BLD smoothing)', grain=CLOCK_ROUTINE)
290 endif
291 call get_param(paramFile, mdl, 'RI_CRIT', CS%Ri_crit, &
292 'Critical bulk Richardson number used to define depth of the '// &
293 'surface Ocean Boundary Layer (OBL).', &
2940 units='nondim', default=0.3)
295 call get_param(paramFile, mdl, 'VON_KARMAN', CS%vonKarman, &
296 'von Karman constant.', &
2970 units='nondim', default=0.40)
298 call get_param(paramFile, mdl, 'ENHANCE_DIFFUSION', CS%enhance_diffusion, &
299 'If True, adds enhanced diffusion at the based of the boundary layer.', &
3000 default=.true.)
301 call get_param(paramFile, mdl, 'INTERP_TYPE', CS%interpType, &
302 'Type of interpolation to determine the OBL depth.\n'// &
303 'Allowed types are: linear, quadratic, cubic.', &
3040 default='quadratic')
305 call get_param(paramFile, mdl, 'INTERP_TYPE2', CS%interpType2, &
306 'Type of interpolation to compute diff and visc at OBL_depth.\n'// &
307 'Allowed types are: linear, quadratic, cubic or LMD94.', &
3080 default='LMD94')
309 call get_param(paramFile, mdl, 'STOKES_MOST', CS%StokesMOST, &
310 'If True, use Stokes Similarity package.', &
3110 default=.False.)
312 call get_param(paramFile, mdl, 'COMPUTE_EKMAN', CS%computeEkman, &
313 'If True, limit OBL depth to be no deeper than Ekman depth.', &
3140 default=.False.)
315 call get_param(paramFile, mdl, 'COMPUTE_MONIN_OBUKHOV', CS%computeMoninObukhov, &
316 'If True, limit the OBL depth to be no deeper than '// &
317 'Monin-Obukhov depth.', &
3180 default=.False.)
319 call get_param(paramFile, mdl, 'CS', CS%cs, &
320 'Parameter for computing velocity scale function.', &
3210 units='nondim', default=98.96)
322 call get_param(paramFile, mdl, 'CS2', CS%cs2, &
323 'Parameter for computing non-local term.', &
3240 units='nondim', default=6.32739901508)
325 call get_param(paramFile, mdl, 'DEEP_OBL_OFFSET', CS%deepOBLoffset, &
326 'If non-zero, the distance above the bottom to which the OBL is clipped '// &
327 'if it would otherwise reach the bottom. The smaller of this and 0.1D is used.', &
3280 units='m', default=0., scale=US%m_to_Z)
329 call get_param(paramFile, mdl, 'FIXED_OBLDEPTH', CS%fixedOBLdepth, &
330 'If True, fix the OBL depth to FIXED_OBLDEPTH_VALUE '// &
331 'rather than using the OBL depth from CVMix. '// &
332 'This option is just for testing purposes.', &
3330 default=.False.)
334 call get_param(paramFile, mdl, 'FIXED_OBLDEPTH_VALUE', CS%fixedOBLdepth_value, &
335 'Value for the fixed OBL depth when fixedOBLdepth==True. '// &
336 'This parameter is for just for testing purposes. '// &
337 'It will over-ride the OBLdepth computed from CVMix.', &
3380 units='m', default=30.0, scale=US%m_to_Z)
339 call get_param(paramFile, mdl, 'SURF_LAYER_EXTENT', CS%surf_layer_ext, &
340 'Fraction of OBL depth considered in the surface layer.', &
3410 units='nondim', default=0.10)
342 call get_param(paramFile, mdl, 'MINIMUM_OBL_DEPTH', CS%minOBLdepth, &
343 'If non-zero, a minimum depth to use for KPP OBL depth. Independent of '// &
344 'this parameter, the OBL depth is always at least as deep as the first layer.', &
3450 units='m', default=0., scale=US%m_to_Z)
346 call get_param(paramFile, mdl, 'MINIMUM_VT2', CS%minVtsqr, &
347 'Min of the unresolved velocity Vt2 used in Rib CVMix calculation.\n'// &
348 'Scaling: MINIMUM_VT2 = const1*d*N*ws, with d=1m, N=1e-5/s, ws=1e-6 m/s.', &
3490 units='m2/s2', default=1e-10, scale=US%m_s_to_L_T**2)
350
351 call get_param(paramFile, mdl, 'NLT_SHAPE', string, &
352 'MOM6 method to set nonlocal transport profile. '// &
353 'Over-rides the result from CVMix. Allowed values are: \n'// &
354 '\t CVMix - Uses the profiles from CVMix specified by MATCH_TECHNIQUE\n'//&
355 '\t LINEAR - A linear profile, 1-sigma\n'// &
356 '\t PARABOLIC - A parablic profile, (1-sigma)^2\n'// &
357 '\t CUBIC - A cubic profile, (1-sigma)^2(1+2*sigma)\n'// &
358 '\t CUBIC_LMD - The original KPP profile', &
3590 default='CVMix')
3600 select case ( trim(string) )
3610 case ("CVMix") ; CS%NLT_shape = NLT_SHAPE_CVMix
3620 case ("LINEAR") ; CS%NLT_shape = NLT_SHAPE_LINEAR
3630 case ("PARABOLIC") ; CS%NLT_shape = NLT_SHAPE_PARABOLIC
3640 case ("CUBIC") ; CS%NLT_shape = NLT_SHAPE_CUBIC
3650 case ("CUBIC_LMD") ; CS%NLT_shape = NLT_SHAPE_CUBIC_LMD
366 case default ; call MOM_error(FATAL,"KPP_init: "// &
3670 "Unrecognized NLT_SHAPE option"//trim(string))
368 end select
369 call get_param(paramFile, mdl, 'MATCH_TECHNIQUE', CS%MatchTechnique, &
370 'CVMix method to set profile function for diffusivity and NLT, '// &
371 'as well as matching across OBL base. Allowed values are: \n'// &
372 '\t SimpleShapes = sigma*(1-sigma)^2 for both diffusivity and NLT\n'// &
373 '\t MatchGradient = sigma*(1-sigma)^2 for NLT; diffusivity profile from matching\n'//&
374 '\t MatchBoth = match gradient for both diffusivity and NLT\n'// &
375 '\t ParabolicNonLocal = sigma*(1-sigma)^2 for diffusivity; (1-sigma)^2 for NLT', &
3760 default='SimpleShapes')
3770 if (CS%MatchTechnique == 'ParabolicNonLocal') then
378 ! This forces Cs2 (Cs in non-local computation) to equal 1 for parabolic non-local option.
379 ! May be used during CVMix initialization.
3800 Cs_is_one=.true.
381 endif
3820 if (CS%MatchTechnique == 'ParabolicNonLocal' .or. CS%MatchTechnique == 'SimpleShapes') then
383 ! if gradient won't be matched, lnoDGat1=.true.
3840 lnoDGat1=.true.
385 endif
386
387 ! safety check to avoid negative diff/visc
3880 if (CS%MatchTechnique == 'MatchBoth' .and. (CS%interpType2 == 'cubic' .or. &
389 CS%interpType2 == 'quadratic')) then
390 call MOM_error(FATAL,"If MATCH_TECHNIQUE=MatchBoth, INTERP_TYPE2 must be set to \n"//&
391 "linear or LMD94 (recommended) to avoid negative viscosity and diffusivity.\n"//&
3920 "Please select one of these valid options." )
393 endif
394
395 call get_param(paramFile, mdl, 'KPP_ZERO_DIFFUSIVITY', CS%KPPzeroDiffusivity, &
396 'If True, zeroes the KPP diffusivity and viscosity; for testing purpose.',&
3970 default=.False.)
398 call get_param(paramFile, mdl, 'KPP_IS_ADDITIVE', CS%KPPisAdditive, &
399 'If true, adds KPP diffusivity to diffusivity from other schemes.\n'//&
400 'If false, KPP is the only diffusivity wherever KPP is non-zero.', &
4010 default=.True.)
402 call get_param(paramFile, mdl, 'KPP_SHORTWAVE_METHOD',string, &
403 'Determines contribution of shortwave radiation to KPP surface '// &
404 'buoyancy flux. Options include:\n'// &
405 ' ALL_SW: use total shortwave radiation\n'// &
406 ' MXL_SW: use shortwave radiation absorbed by mixing layer\n'// &
407 ' LV1_SW: use shortwave radiation absorbed by top model layer', &
4080 default='MXL_SW')
4090 select case ( trim(string) )
4100 case ("ALL_SW") ; CS%SW_METHOD = SW_METHOD_ALL_SW
4110 case ("MXL_SW") ; CS%SW_METHOD = SW_METHOD_MXL_SW
4120 case ("LV1_SW") ; CS%SW_METHOD = SW_METHOD_LV1_SW
413 case default ; call MOM_error(FATAL,"KPP_init: "// &
4140 "Unrecognized KPP_SHORTWAVE_METHOD option"//trim(string))
415 end select
416 call get_param(paramFile, mdl, 'CVMix_ZERO_H_WORK_AROUND', CS%min_thickness, &
417 'A minimum thickness used to avoid division by small numbers in the vicinity '// &
418 'of vanished layers. This is independent of MIN_THICKNESS used in other parts of MOM.', &
4190 units='m', default=0., scale=US%m_to_Z)
420
421!/BGR: New options for including Langmuir effects
422!/ 1. Options related to enhancing the mixing coefficient
423 call get_param(paramFile, mdl, "USE_KPP_LT_K", CS%LT_K_Enhancement, &
424 'Flag for Langmuir turbulence enhancement of turbulent '//&
4250 'mixing coefficient.', Default=.false.)
426 call get_param(paramFile, mdl, "STOKES_MIXING", CS%Stokes_Mixing, &
427 'Flag for Langmuir turbulence enhancement of turbulent '//&
4280 'mixing coefficient.', Default=.false.)
4290 if (CS%LT_K_Enhancement) then
430 call get_param(paramFile, mdl, 'KPP_LT_K_SHAPE', string, &
431 'Vertical dependence of LT enhancement of mixing. '// &
432 'Valid options are: \n'// &
433 '\t CONSTANT = Constant value for full OBL\n'// &
434 '\t SCALED = Varies based on normalized shape function.', &
4350 default='CONSTANT')
4360 select case ( trim(string))
4370 case ("CONSTANT") ; CS%LT_K_SHAPE = LT_K_CONSTANT
4380 case ("SCALED") ; CS%LT_K_SHAPE = LT_K_SCALED
439 case default ; call MOM_error(FATAL,"KPP_init: "//&
4400 "Unrecognized KPP_LT_K_SHAPE option: "//trim(string))
441 end select
442 call get_param(paramFile, mdl, "KPP_LT_K_METHOD", string , &
443 'Method to enhance mixing coefficient in KPP. '// &
444 'Valid options are: \n'// &
445 '\t CONSTANT = Constant value (KPP_K_ENH_FAC) \n'// &
446 '\t VR12 = Function of Langmuir number based on VR12\n'// &
447 '\t (Van Roekel et al. 2012)\n'// &
448 '\t (Li et al. 2016, OM) \n'// &
449 '\t RW16 = Function of Langmuir number based on RW16\n'// &
450 '\t (Reichl et al., 2016, JPO)', &
4510 default='CONSTANT')
4520 select case ( trim(string))
453 case ("CONSTANT")
4540 CS%LT_K_METHOD = LT_K_MODE_CONSTANT
4550 langmuir_mixing_opt = 'LWF16'
456 case ("VR12")
4570 CS%LT_K_METHOD = LT_K_MODE_VR12
4580 langmuir_mixing_opt = 'LWF16'
459 case ("RW16")
4600 CS%LT_K_METHOD = LT_K_MODE_RW16
4610 langmuir_mixing_opt = 'RWHGK16'
462 case default
463 call MOM_error(FATAL,"KPP_init: "//&
4640 "Unrecognized KPP_LT_K_METHOD option: "//trim(string))
465 end select
4660 if (CS%LT_K_METHOD==LT_K_MODE_CONSTANT) then
467 call get_param(paramFile, mdl, "KPP_K_ENH_FAC", CS%KPP_K_ENH_FAC, &
468 'Constant value to enhance mixing coefficient in KPP.', &
4690 units="nondim", default=1.0)
470 endif
471 endif
472!/ 2. Options related to enhancing the unresolved Vt2/entrainment in Rib
473 call get_param(paramFile, mdl, "USE_KPP_LT_VT2", CS%LT_Vt2_Enhancement, &
474 'Flag for Langmuir turbulence enhancement of Vt2 '//&
4750 'in Bulk Richardson Number.', Default=.false.)
4760 if (CS%LT_Vt2_Enhancement) then
477 call get_param(paramFile, mdl, "KPP_LT_VT2_METHOD",string , &
478 'Method to enhance Vt2 in KPP. '// &
479 'Valid options are: \n'// &
480 '\t CONSTANT = Constant value (KPP_VT2_ENH_FAC) \n'// &
481 '\t VR12 = Function of Langmuir number based on VR12\n'// &
482 '\t (Van Roekel et al., 2012) \n'// &
483 '\t (Li et al. 2016, OM) \n'// &
484 '\t RW16 = Function of Langmuir number based on RW16\n'// &
485 '\t (Reichl et al., 2016, JPO) \n'// &
486 '\t LF17 = Function of Langmuir number based on LF17\n'// &
487 '\t (Li and Fox-Kemper, 2017, JPO)', &
4880 default='CONSTANT')
4890 select case ( trim(string))
490 case ("CONSTANT")
4910 CS%LT_VT2_METHOD = LT_VT2_MODE_CONSTANT
4920 langmuir_entrainment_opt = 'LWF16'
493 case ("VR12")
4940 CS%LT_VT2_METHOD = LT_VT2_MODE_VR12
4950 langmuir_entrainment_opt = 'LWF16'
496 case ("RW16")
4970 CS%LT_VT2_METHOD = LT_VT2_MODE_RW16
4980 langmuir_entrainment_opt = 'RWHGK16'
499 case ("LF17")
5000 CS%LT_VT2_METHOD = LT_VT2_MODE_LF17
5010 langmuir_entrainment_opt = 'LF17'
502 case default
503 call MOM_error(FATAL,"KPP_init: "//&
5040 "Unrecognized KPP_LT_VT2_METHOD option: "//trim(string))
505 end select
5060 if (CS%LT_VT2_METHOD==LT_VT2_MODE_CONSTANT) then
507 call get_param(paramFile, mdl, "KPP_VT2_ENH_FAC", CS%KPP_VT2_ENH_FAC, &
508 'Constant value to enhance VT2 in KPP.', &
5090 units="nondim", default=1.0)
510 endif
511 endif
512
5130 if (CS%LT_K_ENHANCEMENT .or. CS%LT_VT2_ENHANCEMENT) then
514 call get_param(paramFile, mdl, "KPP_LT_MLD_GUESS_MIN", CS%MLD_guess_min, &
515 "The minimum estimate of the mixed layer depth used to calculate "//&
516 "the Langmuir number for Langmuir turbulence enhancement with KPP.", &
5170 units="m", default=1.0, scale=US%m_to_Z)
518 endif
519
520 call get_param(paramFile, mdl, "KPP_CVt2", CS%KPP_CVt2, &
521 'Parameter for Stokes MOST convection entrainment', &
5220 units="nondim", default=1.6)
523
524 call get_param(paramFile, mdl, "ANSWER_DATE", CS%answer_date, &
525 "The vintage of the order of arithmetic in the CVMix KPP calculations. Values "//&
526 "below 20240501 recover the answers from early in 2024, while higher values "//&
527 "use expressions that have been refactored for rotational symmetry.", &
5280 default=default_answer_date)
529
5300 call closeParameterBlock(paramFile)
531
5320 call get_param(paramFile, mdl, 'DEBUG', CS%debug, default=.False., do_not_log=.True.)
533
534 call CVMix_init_kpp( Ri_crit=CS%Ri_crit, &
535 minOBLdepth=US%Z_to_m*CS%minOBLdepth, &
536 minVtsqr=US%L_T_to_m_s**2*CS%minVtsqr, &
537 vonKarman=CS%vonKarman, &
538 surf_layer_ext=CS%surf_layer_ext, &
539 CVt2=CS%KPP_CVt2, &
540 interp_type=CS%interpType, &
541 interp_type2=CS%interpType2, &
542 lEkman=CS%computeEkman, &
543 lStokesMOST=CS%StokesMOST, &
544 lMonOb=CS%computeMoninObukhov, &
545 MatchTechnique=CS%MatchTechnique, &
546 lenhanced_diff=CS%enhance_diffusion,&
547 lnonzero_surf_nonlocal=Cs_is_one ,&
548 lnoDGat1=lnoDGat1 ,&
549 langmuir_mixing_str=langmuir_mixing_opt,&
550 langmuir_entrainment_str=langmuir_entrainment_opt,&
5510 CVMix_kpp_params_user=CS%KPP_params )
552
553 ! Register diagnostics
5540 CS%diag => diag
555 CS%id_OBLdepth = register_diag_field('ocean_model', 'KPP_OBLdepth', diag%axesT1, Time, &
556 'Thickness of the surface Ocean Boundary Layer calculated by [CVMix] KPP', &
557 'meter', conversion=US%Z_to_m, &
558 cmor_field_name='oml', cmor_long_name='ocean_mixed_layer_thickness_defined_by_mixing_scheme', &
5590 cmor_units='m', cmor_standard_name='Ocean Mixed Layer Thickness Defined by Mixing Scheme')
560 ! CMOR names are placeholders; must be modified by time period
561 ! for CMOR compliance. Diag manager will be used for omlmax and
562 ! omldamax.
5630 if (CS%n_smooth > 0) then
564 CS%id_OBLdepth_original = register_diag_field('ocean_model', 'KPP_OBLdepth_original', diag%axesT1, Time, &
565 'Thickness of the surface Ocean Boundary Layer without smoothing calculated by [CVMix] KPP', &
566 'meter', conversion=US%Z_to_m, &
567 cmor_field_name='oml', cmor_long_name='ocean_mixed_layer_thickness_defined_by_mixing_scheme', &
5680 cmor_units='m', cmor_standard_name='Ocean Mixed Layer Thickness Defined by Mixing Scheme')
569 endif
5700 if ( CS%StokesMOST ) then
571 CS%id_StokesXI = register_diag_field('ocean_model', 'StokesXI', diag%axesT1, Time, &
5720 'Stokes Similarity Parameter', 'nondim')
573 CS%id_Lam2 = register_diag_field('ocean_model', 'Lam2', diag%axesT1, Time, &
5740 'Ustk0_ustar', 'nondim')
575 endif
576 CS%id_BulkDrho = register_diag_field('ocean_model', 'KPP_BulkDrho', diag%axesTL, Time, &
577 'Bulk difference in density used in Bulk Richardson number, as used by [CVMix] KPP', &
5780 'kg/m3', conversion=US%R_to_kg_m3)
579 CS%id_BulkUz2 = register_diag_field('ocean_model', 'KPP_BulkUz2', diag%axesTL, Time, &
580 'Square of bulk difference in resolved velocity used in Bulk Richardson number via [CVMix] KPP', &
5810 'm2/s2', conversion=US%L_T_to_m_s**2)
582 CS%id_BulkRi = register_diag_field('ocean_model', 'KPP_BulkRi', diag%axesTL, Time, &
5830 'Bulk Richardson number used to find the OBL depth used by [CVMix] KPP', 'nondim')
584 CS%id_Sigma = register_diag_field('ocean_model', 'KPP_sigma', diag%axesTi, Time, &
5850 'Sigma coordinate used by [CVMix] KPP', 'nondim')
586 CS%id_Ws = register_diag_field('ocean_model', 'KPP_Ws', diag%axesTL, Time, &
587 'Turbulent vertical velocity scale for scalars used by [CVMix] KPP', &
5880 'm/s', conversion=US%Z_to_m*US%s_to_T)
589 CS%id_N = register_diag_field('ocean_model', 'KPP_N', diag%axesTi, Time, &
5900 '(Adjusted) Brunt-Vaisala frequency used by [CVMix] KPP', '1/s', conversion=US%s_to_T)
591 CS%id_N2 = register_diag_field('ocean_model', 'KPP_N2', diag%axesTi, Time, &
5920 'Square of Brunt-Vaisala frequency used by [CVMix] KPP', '1/s2', conversion=US%s_to_T**2)
593 CS%id_Vt2 = register_diag_field('ocean_model', 'KPP_Vt2', diag%axesTL, Time, &
5940 'Unresolved shear turbulence used by [CVMix] KPP', 'm2/s2', conversion=US%Z_to_m**2*US%s_to_T**2)
595 CS%id_uStar = register_diag_field('ocean_model', 'KPP_uStar', diag%axesT1, Time, &
5960 'Friction velocity, u*, as used by [CVMix] KPP', 'm/s', conversion=US%Z_to_m*US%s_to_T)
597 CS%id_buoyFlux = register_diag_field('ocean_model', 'KPP_buoyFlux', diag%axesTi, Time, &
598 'Surface (and penetrating) buoyancy flux, as used by [CVMix] KPP', &
5990 'm2/s3', conversion=US%L_to_m**2*US%s_to_T**3)
600 CS%id_Kt_KPP = register_diag_field('ocean_model', 'KPP_Kheat', diag%axesTi, Time, &
601 'Heat diffusivity due to KPP, as calculated by [CVMix] KPP', &
6020 'm2/s', conversion=US%Z2_T_to_m2_s)
603 CS%id_Kd_in = register_diag_field('ocean_model', 'KPP_Kd_in', diag%axesTi, Time, &
6040 'Diffusivity passed to KPP', 'm2/s', conversion=GV%HZ_T_to_m2_s)
605 CS%id_Ks_KPP = register_diag_field('ocean_model', 'KPP_Ksalt', diag%axesTi, Time, &
606 'Salt diffusivity due to KPP, as calculated by [CVMix] KPP', &
6070 'm2/s', conversion=US%Z2_T_to_m2_s)
608 CS%id_Kv_KPP = register_diag_field('ocean_model', 'KPP_Kv', diag%axesTi, Time, &
609 'Vertical viscosity due to KPP, as calculated by [CVMix] KPP', &
6100 'm2/s', conversion=US%Z2_T_to_m2_s)
611 CS%id_NLTt = register_diag_field('ocean_model', 'KPP_NLtransport_heat', diag%axesTi, Time, &
6120 'Non-local transport (Cs*G(sigma)) for heat, as calculated by [CVMix] KPP', 'nondim')
613 CS%id_NLTs = register_diag_field('ocean_model', 'KPP_NLtransport_salt', diag%axesTi, Time, &
6140 'Non-local tranpsort (Cs*G(sigma)) for scalars, as calculated by [CVMix] KPP', 'nondim')
615 CS%id_Tsurf = register_diag_field('ocean_model', 'KPP_Tsurf', diag%axesT1, Time, &
616 'Temperature of surface layer (10% of OBL depth) as passed to [CVMix] KPP', &
6170 'C', conversion=US%C_to_degC)
618 CS%id_Ssurf = register_diag_field('ocean_model', 'KPP_Ssurf', diag%axesT1, Time, &
619 'Salinity of surface layer (10% of OBL depth) as passed to [CVMix] KPP', &
6200 'ppt', conversion=US%S_to_ppt)
621 CS%id_Usurf = register_diag_field('ocean_model', 'KPP_Usurf', diag%axesCu1, Time, &
622 'i-component flow of surface layer (10% of OBL depth) as passed to [CVMix] KPP', &
6230 'm/s', conversion=US%L_T_to_m_s)
624 CS%id_Vsurf = register_diag_field('ocean_model', 'KPP_Vsurf', diag%axesCv1, Time, &
625 'j-component flow of surface layer (10% of OBL depth) as passed to [CVMix] KPP', &
6260 'm/s', conversion=US%L_T_to_m_s)
627 CS%id_EnhK = register_diag_field('ocean_model', 'EnhK', diag%axesTI, Time, &
6280 'Langmuir number enhancement to K as used by [CVMix] KPP','nondim')
629 CS%id_EnhVt2 = register_diag_field('ocean_model', 'EnhVt2', diag%axesTL, Time, &
6300 'Langmuir number enhancement to Vt2 as used by [CVMix] KPP','nondim')
631 CS%id_La_SL = register_diag_field('ocean_model', 'KPP_La_SL', diag%axesT1, Time, &
6320 'Surface-layer Langmuir number computed in [CVMix] KPP','nondim')
633
6340 allocate( CS%N( SZI_(G), SZJ_(G), SZK_(GV)+1 ), source=0. )
6350 allocate( CS%StokesParXI( SZI_(G), SZJ_(G) ), source=0. )
6360 allocate( CS%Lam2 ( SZI_(G), SZJ_(G) ), source=0. )
6370 allocate( CS%kOBL( SZI_(G), SZJ_(G) ), source=0. )
6380 allocate( CS%La_SL( SZI_(G), SZJ_(G) ), source=0. )
6390 allocate( CS%Vt2( SZI_(G), SZJ_(G), SZK_(GV) ), source=0. )
6400 if (CS%id_OBLdepth_original > 0) allocate( CS%OBLdepth_original( SZI_(G), SZJ_(G) ) )
641
6420 allocate( CS%OBLdepthprev( SZI_(G), SZJ_(G) ), source=0.0 )
6430 if (CS%id_BulkDrho > 0) allocate( CS%dRho( SZI_(G), SZJ_(G), SZK_(GV) ), source=0. )
6440 if (CS%id_BulkUz2 > 0) allocate( CS%Uz2( SZI_(G), SZJ_(G), SZK_(GV) ), source=0. )
6450 if (CS%id_BulkRi > 0) allocate( CS%BulkRi( SZI_(G), SZJ_(G), SZK_(GV) ), source=0. )
6460 if (CS%id_Sigma > 0) allocate( CS%sigma( SZI_(G), SZJ_(G), SZK_(GV)+1 ), source=0. )
6470 if (CS%id_Ws > 0) allocate( CS%Ws( SZI_(G), SZJ_(G), SZK_(GV) ), source=0. )
6480 if (CS%id_N2 > 0) allocate( CS%N2( SZI_(G), SZJ_(G), SZK_(GV)+1 ), source=0. )
6490 if (CS%id_Kt_KPP > 0) allocate( CS%Kt_KPP( SZI_(G), SZJ_(G), SZK_(GV)+1 ), source=0. )
6500 if (CS%id_Ks_KPP > 0) allocate( CS%Ks_KPP( SZI_(G), SZJ_(G), SZK_(GV)+1 ), source=0. )
6510 if (CS%id_Kv_KPP > 0) allocate( CS%Kv_KPP( SZI_(G), SZJ_(G), SZK_(GV)+1 ), source=0. )
6520 if (CS%id_Tsurf > 0) allocate( CS%Tsurf( SZI_(G), SZJ_(G) ), source=0. )
6530 if (CS%id_Ssurf > 0) allocate( CS%Ssurf( SZI_(G), SZJ_(G) ), source=0. )
6540 if (CS%id_Usurf > 0) allocate( CS%Usurf( SZIB_(G), SZJ_(G) ), source=0. )
6550 if (CS%id_Vsurf > 0) allocate( CS%Vsurf( SZI_(G), SZJB_(G) ), source=0. )
6560 if (CS%id_EnhVt2 > 0) allocate( CS%EnhVt2( SZI_(G), SZJ_(G), SZK_(GV) ), source=0. )
6570 if (CS%id_EnhK > 0) allocate( CS%EnhK( SZI_(G), SZJ_(G), SZK_(GV)+1 ), source=0. )
658
6590 id_clock_KPP_calc = cpu_clock_id('Ocean KPP calculate)', grain=CLOCK_MODULE)
6600 id_clock_KPP_compute_BLD = cpu_clock_id('(Ocean KPP comp BLD)', grain=CLOCK_ROUTINE)
661
6620end function KPP_init
663
664!> KPP vertical diffusivity/viscosity and non-local tracer transport
6650subroutine KPP_calculate(CS, G, GV, US, h, tv, uStar, buoyFlux, Kt, Ks, Kv, &
6660 nonLocalTransHeat, nonLocalTransScalar, Waves, lamult)
667
668 ! Arguments
669 type(KPP_CS), pointer :: CS !< Control structure
670 type(ocean_grid_type), intent(in) :: G !< Ocean grid
671 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid
672 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
673 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
674 type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamics structure.
675 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: uStar !< Surface friction velocity [Z T-1 ~> m s-1]
676 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: buoyFlux !< Surface buoyancy flux [L2 T-3 ~> m2 s-3]
677 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: Kt !< (in) Vertical diffusivity of heat w/o KPP
678 !! (out) Vertical diffusivity including KPP
679 !! [H Z T-1 ~> m2 s-1 or kg m-1 s-1]
680 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: Ks !< (in) Vertical diffusivity of salt w/o KPP
681 !! (out) Vertical diffusivity including KPP
682 !! [H Z T-1 ~> m2 s-1 or kg m-1 s-1]
683 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: Kv !< (in) Vertical viscosity w/o KPP
684 !! (out) Vertical viscosity including KPP
685 !! [H Z T-1 ~> m2 s-1 or Pa s]
686 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: nonLocalTransHeat !< Temp non-local transport [nondim]
687 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: nonLocalTransScalar !< scalar non-local trans. [nondim]
688 type(wave_parameters_CS), pointer :: Waves !< Wave CS for Langmuir turbulence
689 real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: lamult !< Langmuir enhancement multiplier [nondim]
690
691 ! Local variables
692 integer :: i, j, k ! Loop indices
6930 real, dimension(SZI_(G),SZK_(GV)) :: dz ! Height change across layers [Z ~> m]
6940 real, dimension( GV%ke ) :: cellHeight ! Cell center heights referenced to surface [Z ~> m] (negative in ocean)
6950 real, dimension( GV%ke+1 ) :: iFaceHeight ! Interface heights referenced to surface [Z ~> m] (negative in ocean)
6960 real, dimension( GV%ke ) :: z_cell ! Cell center heights referenced to surface [m] (negative in ocean)
6970 real, dimension( GV%ke+1 ) :: z_inter ! Cell interface heights referenced to surface [m] (negative in ocean)
6980 real, dimension( GV%ke+1, 2) :: Kdiffusivity ! Vertical diffusivity at interfaces in MKS units [m2 s-1]
6990 real, dimension( GV%ke+1 ) :: Kviscosity ! Vertical viscosity at interfaces in MKS units [m2 s-1]
7000 real, dimension( GV%ke+1, 2) :: nonLocalTrans ! Non-local transport for heat/salt at interfaces [nondim]
701
702 real :: surfFricVel ! Surface friction velocity in MKS units [m s-1]
703 real :: surfBuoyFlux ! Surface buoyancy flux in MKS units [m2 s-3]
704 real :: sigma ! Fractional vertical position within the boundary layer [nondim]
705 real :: sigmaRatio ! A cubic function of sigma [nondim]
706 real :: buoy_scale ! A unit conversion factor for buoyancy fluxes [m2 T3 L-2 s-3 ~> 1]
707 real :: dh ! The local thickness used for calculating interface positions [Z ~> m]
708 real :: hcorr ! A cumulative correction arising from inflation of vanished layers [Z ~> m]
709
710 ! For Langmuir Calculations
711 real :: LangEnhK ! Langmuir enhancement for mixing coefficient [nondim]
712
7130 if (CS%Stokes_Mixing .and. .not.associated(Waves)) call MOM_error(FATAL, &
7140 "KPP_calculate: The Waves control structure must be associated if STOKES_MIXING is True.")
715
7160 if (CS%debug) then
7170 call hchksum(h, "KPP in: h", G%HI, haloshift=0, unscale=GV%H_to_m)
7180 call hchksum(uStar, "KPP in: uStar", G%HI, haloshift=0, unscale=US%Z_to_m*US%s_to_T)
7190 call hchksum(buoyFlux, "KPP in: buoyFlux", G%HI, haloshift=0, unscale=US%L_to_m**2*US%s_to_T**3)
7200 call hchksum(Kt, "KPP in: Kt", G%HI, haloshift=0, unscale=GV%HZ_T_to_m2_s)
7210 call hchksum(Ks, "KPP in: Ks", G%HI, haloshift=0, unscale=GV%HZ_T_to_m2_s)
722 endif
723
7240 nonLocalTrans(:,:) = 0.0
725
7260 if (CS%id_Kd_in > 0) call post_data(CS%id_Kd_in, Kt, CS%diag)
727
7280 call cpu_clock_begin(id_clock_KPP_calc)
7290 buoy_scale = US%L_to_m**2*US%s_to_T**3
730
731 !$OMP parallel do default(none) firstprivate(nonLocalTrans) &
732 !$OMP private(surfFricVel, iFaceHeight, hcorr, dh, dz, cellHeight, &
733 !$OMP surfBuoyFlux, Kdiffusivity, Kviscosity, LangEnhK, sigma, &
734 !$OMP sigmaRatio, z_inter, z_cell) &
735 !$OMP shared(G, GV, CS, US, tv, uStar, h, buoy_scale, buoyFlux, Kt, &
736 !$OMP Ks, Kv, nonLocalTransHeat, nonLocalTransScalar, Waves, lamult)
737 ! loop over horizontal points on processor
7380 do j = G%jsc, G%jec
739
740 ! Find the vertical distances across layers.
7410 call thickness_to_dz(h, tv, dz, j, G, GV)
742
7430 do i = G%isc, G%iec ; if (G%mask2dT(i,j) > 0.0) then
744
745 ! things independent of position within the column
7460 surfFricVel = US%Z_to_m*US%s_to_T * uStar(i,j)
747
7480 iFaceHeight(1) = 0.0 ! BBL is all relative to the surface
7490 hcorr = 0.
7500 do k=1,GV%ke
751
752 ! cell center and cell bottom in meters (negative values in the ocean)
7530 dh = dz(i,k) ! Nominal thickness to use for increment
7540 dh = dh + hcorr ! Take away the accumulated error (could temporarily make dh<0)
7550 hcorr = min( dh - CS%min_thickness, 0. ) ! If inflating then hcorr<0
7560 dh = max( dh, CS%min_thickness ) ! Limit increment dh>=min_thickness
7570 cellHeight(k) = iFaceHeight(k) - 0.5 * dh
7580 iFaceHeight(k+1) = iFaceHeight(k) - dh
759
760 enddo ! k-loop finishes
761
7620 surfBuoyFlux = buoy_scale*buoyFlux(i,j,1) ! This is only used in kpp_compute_OBL_depth to limit
763 ! h to Monin-Obukhov (default is false, ie. not used)
764
765 ! Call CVMix/KPP to obtain OBL diffusivities, viscosities and non-local transports
766
767 ! Unlike LMD94, we do not match to interior diffusivities. If using the original
768 ! LMD94 shape function, not matching is equivalent to matching to a zero diffusivity.
769
770 !BGR/ Add option for use of surface buoyancy flux with total sw flux.
7710 if (CS%SW_METHOD == SW_METHOD_ALL_SW) then
7720 surfBuoyFlux = buoy_scale * buoyFlux(i,j,1)
7730 elseif (CS%SW_METHOD == SW_METHOD_MXL_SW) then
774 ! We know the actual buoyancy flux into the OBL
7750 surfBuoyFlux = buoy_scale * (buoyFlux(i,j,1) - buoyFlux(i,j,int(CS%kOBL(i,j))+1))
7760 elseif (CS%SW_METHOD == SW_METHOD_LV1_SW) then
7770 surfBuoyFlux = buoy_scale * (buoyFlux(i,j,1) - buoyFlux(i,j,2))
778 endif
779
780 ! If option "MatchBoth" is selected in CVMix, MOM should be capable of matching.
7810 if (.not. (CS%MatchTechnique == 'MatchBoth')) then
7820 Kdiffusivity(:,:) = 0. ! Diffusivities for heat and salt [m2 s-1]
7830 Kviscosity(:) = 0. ! Viscosity [m2 s-1]
784 else
7850 Kdiffusivity(:,1) = GV%HZ_T_to_m2_s * Kt(i,j,:)
7860 Kdiffusivity(:,2) = GV%HZ_T_to_m2_s * Ks(i,j,:)
7870 Kviscosity(:) = GV%HZ_T_to_m2_s * Kv(i,j,:)
788 endif
789
7900 IF (CS%LT_K_ENHANCEMENT) then
7910 if (CS%LT_K_METHOD==LT_K_MODE_CONSTANT) then
7920 LangEnhK = CS%KPP_K_ENH_FAC
7930 elseif (CS%LT_K_METHOD==LT_K_MODE_VR12) then
7940 if (present(lamult)) then
7950 LangEnhK = lamult(i,j)
796 else
797 LangEnhK = sqrt(1.+(1.5*CS%La_SL(i,j))**(-2) + &
7980 (5.4*CS%La_SL(i,j))**(-4))
799 endif
8000 elseif (CS%LT_K_METHOD==LT_K_MODE_RW16) then
801 !This maximum value is proposed in Reichl et al., 2016 JPO formula
8020 LangEnhK = min(2.25, 1. + 1./CS%La_SL(i,j))
803 else
804 !This shouldn't be reached.
805 !call MOM_error(WARNING,"Unexpected behavior in MOM_CVMix_KPP, see error in LT_K_ENHANCEMENT")
8060 LangEnhK = 1.0
807 endif
808
809 ! diffusivities don't need to be enhanced below anymore since LangEnhK is applied within CVMix.
810 ! todo: need to double check if the distinction between the two different options of LT_K_SHAPE may need to be
811 ! treated specially.
8120 do k=1,GV%ke
8130 if (CS%LT_K_SHAPE== LT_K_CONSTANT) then
8140 if (CS%id_EnhK > 0) CS%EnhK(i,j,:) = LangEnhK
815 !Kdiffusivity(k,1) = Kdiffusivity(k,1) * LangEnhK
816 !Kdiffusivity(k,2) = Kdiffusivity(k,2) * LangEnhK
817 !Kviscosity(k) = Kviscosity(k) * LangEnhK
8180 elseif (CS%LT_K_SHAPE == LT_K_SCALED) then
8190 sigma = min(1.0,-iFaceHeight(k)/CS%OBLdepth(i,j))
8200 SigmaRatio = sigma * (1. - sigma)**2 / 0.148148037
8210 if (CS%id_EnhK > 0) CS%EnhK(i,j,k) = (1.0 + (LangEnhK - 1.)*sigmaRatio)
822 !Kdiffusivity(k,1) = Kdiffusivity(k,1) * ( 1. + &
823 ! ( LangEnhK - 1.)*sigmaRatio)
824 !Kdiffusivity(k,2) = Kdiffusivity(k,2) * ( 1. + &
825 ! ( LangEnhK - 1.)*sigmaRatio)
826 !Kviscosity(k) = Kviscosity(k) * ( 1. + &
827 ! ( LangEnhK - 1.)*sigmaRatio)
828 endif
829 enddo
830 endif
831
832 ! Convert columns to MKS units for passing to CVMix
8330 do k = 1, GV%ke
8340 z_cell(k) = US%Z_to_m*cellHeight(k)
835 enddo
8360 do K = 1, GV%ke+1
8370 z_inter(K) = US%Z_to_m*iFaceHeight(K)
838 enddo
839
840 call CVMix_coeffs_kpp(Kviscosity(:), & ! (inout) Total viscosity [m2 s-1]
841 Kdiffusivity(:,1), & ! (inout) Total heat diffusivity [m2 s-1]
842 Kdiffusivity(:,2), & ! (inout) Total salt diffusivity [m2 s-1]
843 z_inter(:), & ! (in) Height of interfaces [m]
844 z_cell(:), & ! (in) Height of level centers [m]
845 Kviscosity(:), & ! (in) Original viscosity [m2 s-1]
846 Kdiffusivity(:,1), & ! (in) Original heat diffusivity [m2 s-1]
847 Kdiffusivity(:,2), & ! (in) Original salt diffusivity [m2 s-1]
848 US%Z_to_m*CS%OBLdepth(i,j), & ! (in) OBL depth [m]
849 CS%kOBL(i,j), & ! (in) level (+fraction) of OBL extent
850 nonLocalTrans(:,1),& ! (out) Non-local heat transport [nondim]
851 nonLocalTrans(:,2),& ! (out) Non-local salt transport [nondim]
852 surfFricVel, & ! (in) Turbulent friction velocity at surface [m s-1]
853 surfBuoyFlux, & ! (in) Buoyancy flux at surface [m2 s-3]
854 GV%ke, & ! (in) Number of levels to compute coeffs for
855 GV%ke, & ! (in) Number of levels in array shape
856 Langmuir_EFactor=LangEnhK,& ! Langmuir enhancement multiplier
857 StokesXi = CS%StokesParXI(i,j), & ! Stokes forcing parameter
8580 CVMix_kpp_params_user=CS%KPP_params )
859
860 ! safety check, Kviscosity and Kdiffusivity must be >= 0
8610 do k=1, GV%ke+1
8620 if (Kviscosity(k) < 0. .or. Kdiffusivity(k,1) < 0.) then
8630 write(*,'(a,3i3)') 'interface, i, j, k = ',j, j, k
8640 write(*,'(a,2f12.5)') 'lon,lat=', G%geoLonT(i,j), G%geoLatT(i,j)
8650 write(*,'(a,es12.4)') 'depth, z_inter(k) =',z_inter(k)
8660 write(*,'(a,es12.4)') 'Kviscosity(k) =',Kviscosity(k)
8670 write(*,'(a,es12.4)') 'Kdiffusivity(k,1) =',Kdiffusivity(k,1)
8680 write(*,'(a,es12.4)') 'Kdiffusivity(k,2) =',Kdiffusivity(k,2)
8690 write(*,'(a,es12.4)') 'OBLdepth =',US%Z_to_m*CS%OBLdepth(i,j)
8700 write(*,'(a,f8.4)') 'kOBL =',CS%kOBL(i,j)
8710 write(*,'(a,es12.4)') 'u* =',surfFricVel
8720 write(*,'(a,es12.4)') 'bottom, z_inter(GV%ke+1) =',z_inter(GV%ke+1)
8730 write(*,'(a,es12.4)') 'CS%La_SL(i,j) =',CS%La_SL(i,j)
8740 write(*,'(a,es12.4)') 'LangEnhK =',LangEnhK
8750 if (present(lamult)) write(*,'(a,es12.4)') 'lamult(i,j) =',lamult(i,j)
8760 write(*,*) 'Kviscosity(:) =',Kviscosity(:)
8770 write(*,*) 'Kdiffusivity(:,1) =',Kdiffusivity(:,1)
878
879 call MOM_error(FATAL,"KPP_calculate, after CVMix_coeffs_kpp: "// &
880 "Negative vertical viscosity or diffusivity has been detected. " // &
881 "This is likely related to the choice of MATCH_TECHNIQUE and INTERP_TYPE2. " //&
8820 "You might consider using the default options for these parameters." )
883 endif
884 enddo
885
886 ! Over-write CVMix NLT shape function with one of the following choices.
887 ! The CVMix code has yet to update for thse options, so we compute in MOM6.
888 ! Note that nonLocalTrans = Cs * G(sigma) (LMD94 notation), with
889 ! Cs = 6.32739901508.
890 ! Start do-loop at k=2, since k=1 is ocean surface (sigma=0)
891 ! and we do not wish to double-count the surface forcing.
892 ! Only compute nonlocal transport for 0 <= sigma <= 1.
893 ! MOM6 recommended shape is the parabolic; it gives deeper boundary layer
894 ! and no spurious extrema.
8950 if (surfBuoyFlux < 0.0) then
8960 if (CS%NLT_shape == NLT_SHAPE_CUBIC) then
8970 do k = 2, GV%ke
8980 sigma = min(1.0,-iFaceHeight(k)/CS%OBLdepth(i,j))
8990 nonLocalTrans(k,1) = (1.0 - sigma)**2 * (1.0 + 2.0*sigma) !*
9000 nonLocalTrans(k,2) = nonLocalTrans(k,1)
901 enddo
9020 elseif (CS%NLT_shape == NLT_SHAPE_PARABOLIC) then
9030 do k = 2, GV%ke
9040 sigma = min(1.0,-iFaceHeight(k)/CS%OBLdepth(i,j))
9050 nonLocalTrans(k,1) = (1.0 - sigma)**2 !*CS%CS2
9060 nonLocalTrans(k,2) = nonLocalTrans(k,1)
907 enddo
9080 elseif (CS%NLT_shape == NLT_SHAPE_LINEAR) then
9090 do k = 2, GV%ke
9100 sigma = min(1.0,-iFaceHeight(k)/CS%OBLdepth(i,j))
9110 nonLocalTrans(k,1) = (1.0 - sigma)!*CS%CS2
9120 nonLocalTrans(k,2) = nonLocalTrans(k,1)
913 enddo
9140 elseif (CS%NLT_shape == NLT_SHAPE_CUBIC_LMD) then
915 ! Sanity check (should agree with CVMix result using simple matching)
9160 do k = 2, GV%ke
9170 sigma = min(1.0,-iFaceHeight(k)/CS%OBLdepth(i,j))
9180 nonLocalTrans(k,1) = CS%CS2 * sigma*(1.0 -sigma)**2
9190 nonLocalTrans(k,2) = nonLocalTrans(k,1)
920 enddo
921 endif
922 endif
923
924 ! we apply nonLocalTrans in subroutines
925 ! KPP_NonLocalTransport_temp and KPP_NonLocalTransport_saln
9260 nonLocalTransHeat(i,j,:) = nonLocalTrans(:,1) ! temperature
9270 nonLocalTransScalar(i,j,:) = nonLocalTrans(:,2) ! salinity
928
929 ! set the KPP diffusivity and viscosity to zero for testing purposes
9300 if (CS%KPPzeroDiffusivity) then
9310 Kdiffusivity(:,1) = 0.0
9320 Kdiffusivity(:,2) = 0.0
9330 Kviscosity(:) = 0.0
934 endif
935
936 ! Copy 1d data into 3d diagnostic arrays
937 !/ grabbing obldepth_0d for next time step.
9380 CS%OBLdepthprev(i,j) = CS%OBLdepth(i,j)
9390 if (CS%id_sigma > 0) then
9400 CS%sigma(i,j,:) = 0.
9410 if (CS%OBLdepth(i,j)>0.) CS%sigma(i,j,:) = -iFaceHeight(:)/CS%OBLdepth(i,j)
942 endif
9430 if (CS%id_Kt_KPP > 0) CS%Kt_KPP(i,j,:) = US%m2_s_to_Z2_T * Kdiffusivity(:,1)
9440 if (CS%id_Ks_KPP > 0) CS%Ks_KPP(i,j,:) = US%m2_s_to_Z2_T * Kdiffusivity(:,2)
9450 if (CS%id_Kv_KPP > 0) CS%Kv_KPP(i,j,:) = US%m2_s_to_Z2_T * Kviscosity(:)
946
947 ! Update output of routine
9480 if (.not. CS%passiveMode) then
9490 if (CS%KPPisAdditive) then
9500 do k=1, GV%ke+1
9510 Kt(i,j,k) = Kt(i,j,k) + GV%m2_s_to_HZ_T * Kdiffusivity(k,1)
9520 Ks(i,j,k) = Ks(i,j,k) + GV%m2_s_to_HZ_T * Kdiffusivity(k,2)
9530 Kv(i,j,k) = Kv(i,j,k) + GV%m2_s_to_HZ_T * Kviscosity(k)
9540 if (CS%Stokes_Mixing) Waves%KvS(i,j,k) = Kv(i,j,k)
955 enddo
956 else ! KPP replaces prior diffusivity when former is non-zero
9570 do k=1, GV%ke+1
9580 if (Kdiffusivity(k,1) /= 0.) Kt(i,j,k) = GV%m2_s_to_HZ_T * Kdiffusivity(k,1)
9590 if (Kdiffusivity(k,2) /= 0.) Ks(i,j,k) = GV%m2_s_to_HZ_T * Kdiffusivity(k,2)
9600 if (Kviscosity(k) /= 0.) Kv(i,j,k) = GV%m2_s_to_HZ_T * Kviscosity(k)
9610 if (CS%Stokes_Mixing) Waves%KvS(i,j,k) = Kv(i,j,k)
962 enddo
963 endif
964 endif
965
966
967 ! end of the horizontal do-loops over the vertical columns
968 endif ; enddo ! i
969 enddo ! j
970
9710 call cpu_clock_end(id_clock_KPP_calc)
972
9730 if (CS%debug) then
9740 call hchksum(Kt, "KPP out: Kt", G%HI, haloshift=0, unscale=GV%HZ_T_to_m2_s)
9750 call hchksum(Ks, "KPP out: Ks", G%HI, haloshift=0, unscale=GV%HZ_T_to_m2_s)
976 endif
977
978 ! send diagnostics to post_data
9790 if (CS%id_OBLdepth > 0) call post_data(CS%id_OBLdepth, CS%OBLdepth, CS%diag)
9800 if (CS%id_OBLdepth_original > 0) call post_data(CS%id_OBLdepth_original,CS%OBLdepth_original,CS%diag)
9810 if (CS%id_sigma > 0) call post_data(CS%id_sigma, CS%sigma, CS%diag)
9820 if (CS%id_Ws > 0) call post_data(CS%id_Ws, CS%Ws, CS%diag)
9830 if (CS%id_uStar > 0) call post_data(CS%id_uStar, uStar, CS%diag)
9840 if (CS%id_buoyFlux > 0) call post_data(CS%id_buoyFlux, buoyFlux, CS%diag)
9850 if (CS%id_Kt_KPP > 0) call post_data(CS%id_Kt_KPP, CS%Kt_KPP, CS%diag)
9860 if (CS%id_Ks_KPP > 0) call post_data(CS%id_Ks_KPP, CS%Ks_KPP, CS%diag)
9870 if (CS%id_Kv_KPP > 0) call post_data(CS%id_Kv_KPP, CS%Kv_KPP, CS%diag)
9880 if (CS%id_NLTt > 0) call post_data(CS%id_NLTt, nonLocalTransHeat, CS%diag)
9890 if (CS%id_NLTs > 0) call post_data(CS%id_NLTs, nonLocalTransScalar,CS%diag)
990
991
9920end subroutine KPP_calculate
993
994
995!> Compute OBL depth
9960subroutine KPP_compute_BLD(CS, G, GV, US, h, Temp, Salt, u, v, tv, uStar, buoyFlux, Waves, lamult)
997
998 ! Arguments
999 type(KPP_CS), pointer :: CS !< Control structure
1000 type(ocean_grid_type), intent(inout) :: G !< Ocean grid
1001 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid
1002 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1003 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
1004 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: Temp !< potential/cons temp [C ~> degC]
1005 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: Salt !< Salinity [S ~> ppt]
1006 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(in) :: u !< Velocity i-component [L T-1 ~> m s-1]
1007 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(in) :: v !< Velocity j-component [L T-1 ~> m s-1]
1008 type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamics structure.
1009 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: uStar !< Surface friction velocity [Z T-1 ~> m s-1]
1010 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: buoyFlux !< Surface buoyancy flux [L2 T-3 ~> m2 s-3]
1011 type(wave_parameters_CS), pointer :: Waves !< Wave CS for Langmuir turbulence
1012 real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: lamult !< Langmuir enhancement factor [nondim]
1013
1014 ! Local variables
10150 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: dz ! Height change across layers [Z ~> m]
1016 ! Variables for passing to CVMix routines, often in MKS units
10170 real, dimension( GV%ke ) :: Ws_1d ! Profile of vertical velocity scale for scalars in MKS units [m s-1]
10180 real, dimension( GV%ke ) :: deltaRho ! delta Rho in numerator of Bulk Ri number [R ~> kg m-3]
10190 real, dimension( GV%ke ) :: deltaBuoy ! Change in Buoyancy based on deltaRho [m s-2]
10200 real, dimension( GV%ke ) :: deltaU2 ! square of delta U (shear) in denominator of Bulk Ri [m2 s-2]
10210 real, dimension( GV%ke ) :: surfBuoyFlux2 ! Surface buoyancy flux in MKS units [m2 s-3]
10220 real, dimension( GV%ke ) :: BulkRi_1d ! Bulk Richardson number for each layer [nondim]
10230 real, dimension( GV%ke ) :: Vt2_1d ! Unresolved squared turbulence velocity for bulk Ri [m2 s-2]
10240 real, dimension( GV%ke ) :: z_cell ! Cell center heights referenced to surface [m] (negative in ocean)
10250 real, dimension( GV%ke ) :: OBL_depth ! Cell center depths referenced to surface [m] (positive in ocean)
10260 real, dimension( GV%ke+1 ) :: z_inter ! Cell interface heights referenced to surface [m] (negative in ocean)
10270 real, dimension( GV%ke+1 ) :: N_col ! A column of buoyancy frequencies at interfaces in MKS units [s-1]
1028 real :: surfFricVel ! Surface friction velocity in MKS units [m s-1]
1029 real :: surfBuoyFlux ! Surface buoyancy flux in MKS units [m2 s-3]
1030 real :: Coriolis ! Coriolis parameter at tracer points in MKS units [s-1]
1031 real :: KPP_OBL_depth ! Boundary layer depth calculated by CVMix_kpp_compute_OBL_depth in MKS units [m]
1032
1033 ! Variables for EOS calculations
10340 real, dimension( 3*GV%ke ) :: rho_1D ! A column of densities [R ~> kg m-3]
10350 real, dimension( 3*GV%ke ) :: pres_1D ! A column of pressures [R L2 T-2 ~> Pa]
10360 real, dimension( 3*GV%ke ) :: Temp_1D ! A column of temperatures [C ~> degC]
10370 real, dimension( 3*GV%ke ) :: Salt_1D ! A column of salinities [S ~> ppt]
1038
10390 real, dimension( GV%ke ) :: cellHeight ! Cell center heights referenced to surface [Z ~> m] (negative in ocean)
10400 real, dimension( GV%ke+1 ) :: iFaceHeight ! Interface heights referenced to surface [Z ~> m] (negative in ocean)
10410 real, dimension( GV%ke+1 ) :: N2_1d ! Brunt-Vaisala frequency squared, at interfaces [T-2 ~> s-2]
1042 real :: zBottomMinusOffset ! Height of bottom plus a little bit [Z ~> m]
1043 real :: GoRho ! Gravitational acceleration in MKS units divided by density [m s-2 R-1 ~> m4 kg-1 s-2]
1044 real :: GoRho_Z_L2 ! Gravitational acceleration, perhaps divided by density, times aspect ratio
1045 ! rescaling [H T-2 R-1 ~> m4 kg-1 s-2 or m s-2]
1046 real :: pRef ! The interface pressure [R L2 T-2 ~> Pa]
1047 real :: Uk, Vk ! Layer velocities relative to their averages in the surface layer [L T-1 ~> m s-1]
1048 real :: SLdepth_0d ! Surface layer depth = surf_layer_ext*OBLdepth [Z ~> m]
1049 real :: hTot ! Running sum of thickness used in the surface layer average [Z ~> m]
1050 real :: I_hTot ! The inverse of hTot [Z-1 ~> m-1]
1051 real :: buoy_scale ! A unit conversion factor for buoyancy fluxes [m2 T3 L-2 s-3 ~> 1]
1052 real :: delH ! Thickness of a layer [Z ~> m]
1053 real :: surfTemp ! Average of temperature over the surface layer [C ~> degC]
1054 real :: surfHtemp ! Integral of temperature over the surface layer [Z C ~> m degC]
1055 real :: surfSalt ! Average of salinity over the surface layer [S ~> ppt]
1056 real :: surfHsalt ! Integral of salinity over the surface layer [Z S ~> m ppt]
1057 real :: surfHu, surfHv ! Integral of u and v over the surface layer [Z L T-1 ~> m2 s-1]
1058 real :: surfU, surfV ! Average of u and v over the surface layer [Z T-1 ~> m s-1]
1059 real :: dh ! The local thickness used for calculating interface positions [Z ~> m]
1060 real :: hcorr ! A cumulative correction arising from inflation of vanished layers [Z ~> m]
1061
1062 ! For Langmuir Calculations
1063 real :: Vt_layer ! non-dimensional extent contribution to unresolved shear
1064 real :: LangEnhVt2 ! Langmuir enhancement for unresolved shear [nondim]
10650 real, dimension(GV%ke) :: U_H, V_H ! Velocities at tracer points [L T-1 ~> m s-1]
1066 real :: MLD_guess ! A guess at the mixed layer depth for calculating the Langmuir number [Z ~> m]
1067 real :: LA ! The local Langmuir number [nondim]
1068 real :: surfHuS, surfHvS ! Stokes drift velocities integrated over the boundary layer [Z L T-1 ~> m2 s-1]
1069 real :: surfUs, surfVs ! Stokes drift velocities averaged over the boundary layer [Z T-1 ~> m s-1]
1070
1071 integer :: i, j, k, km1, kk, ksfc, ktmp ! Loop indices
1072
10730 real, dimension(GV%ke) :: uE_H, vE_H ! Eulerian velocities h-points, centers [L T-1 ~> m s-1]
10740 real, dimension(GV%ke) :: uS_H, vS_H ! Stokes drift components h-points, centers [L T-1 ~> m s-1]
10750 real, dimension(GV%ke) :: uSbar_H, vSbar_H ! Cell Average Stokes drift h-points [L T-1 ~> m s-1]
10760 real, dimension(GV%ke+1) :: uS_Hi, vS_Hi ! Stokes Drift components at interfaces [L T-1 ~> m s-1]
1077 real :: uS_SLD , vS_SLD, uS_SLC , vS_SLC, uSbar_SLD, vSbar_SLD ! Stokes at/to to Surface Layer Extent
1078 ! [L T-1 ~> m s-1]
1079 real :: StokesXI ! Stokes similarity parameter [nondim]
10800 real, dimension( GV%ke ) :: StokesXI_1d , StokesVt_1d ! Parameters of TKE production ratio [nondim]
1081 integer :: kbl ! index of cell containing boundary layer depth
1082
10830 if (CS%Stokes_Mixing .and. .not.associated(Waves)) call MOM_error(FATAL, &
10840 "KPP_compute_BLD: The Waves control structure must be associated if STOKES_MIXING is True.")
1085
10860 if (CS%debug) then
10870 call hchksum(Salt, "KPP in: S", G%HI, haloshift=0, unscale=US%S_to_ppt)
10880 call hchksum(Temp, "KPP in: T", G%HI, haloshift=0, unscale=US%C_to_degC)
10890 call hchksum(u, "KPP in: u", G%HI, haloshift=0, unscale=US%L_T_to_m_s)
10900 call hchksum(v, "KPP in: v", G%HI, haloshift=0, unscale=US%L_T_to_m_s)
1091 endif
1092
10930 call cpu_clock_begin(id_clock_KPP_compute_BLD)
1094
1095 ! some constants
10960 GoRho = US%Z_to_m*US%s_to_T**2 * (GV%g_Earth_Z_T2 / GV%Rho0)
10970 if (GV%Boussinesq) then
10980 GoRho_Z_L2 = GV%Z_to_H * GV%g_Earth_Z_T2 / GV%Rho0
1099 else
11000 GoRho_Z_L2 = GV%g_Earth_Z_T2 * GV%RZ_to_H
1101 endif
11020 buoy_scale = US%L_to_m**2*US%s_to_T**3
1103
1104 ! Find the vertical distances across layers.
11050 call thickness_to_dz(h, tv, dz, G, GV, US)
1106
1107 ! loop over horizontal points on processor
1108 !$OMP parallel do default(none) private(surfFricVel, iFaceHeight, hcorr, dh, cellHeight, &
1109 !$OMP surfBuoyFlux, U_H, V_H, Coriolis, pRef, SLdepth_0d, vt2_1d, &
1110 !$OMP ksfc, surfHtemp, surfHsalt, surfHu, surfHv, surfHuS, &
1111 !$OMP surfHvS, hTot, I_hTot, delH, surftemp, surfsalt, surfu, surfv, &
1112 !$OMP surfUs, surfVs, Uk, Vk, deltaU2, km1, kk, pres_1D, N_col, &
1113 !$OMP Temp_1D, salt_1D, surfBuoyFlux2, MLD_guess, LA, rho_1D, &
1114 !$OMP deltarho, deltaBuoy, N2_1d, ws_1d, LangEnhVT2,KPP_OBL_depth, z_cell, &
1115 !$OMP z_inter, OBL_depth, BulkRi_1d, zBottomMinusOffset, uE_H, vE_H, &
1116 !$OMP uS_H, vS_H, uSbar_H, vSbar_H , uS_Hi, vS_Hi, &
1117 !$OMP uS_SLD, vS_SLD, uS_SLC, vS_SLC, uSbar_SLD, vSbar_SLD, &
1118 !$OMP StokesXI, StokesXI_1d, StokesVt_1d, kbl) &
1119 !$OMP shared(G, GV, CS, US, uStar, h, dz, buoy_scale, buoyFlux, &
1120 !$OMP Temp, Salt, waves, tv, GoRho, GoRho_Z_L2, u, v, lamult, Vt_layer)
11210 do j = G%jsc, G%jec
11220 do i = G%isc, G%iec ; if (G%mask2dT(i,j) > 0.0) then
1123
11240 do k=1,GV%ke
11250 U_H(k) = 0.5 * (u(I,j,k)+u(I-1,j,k))
11260 V_H(k) = 0.5 * (v(i,J,k)+v(i,J-1,k))
1127 enddo
11280 if (CS%StokesMOST) then
11290 do k=1,GV%ke
11300 uE_H(k) = 0.5 * (u(I,j,k)+u(I-1,j,k)-Waves%US_x(I,j,k)-Waves%US_x(I-1,j,k))
11310 vE_H(k) = 0.5 * (v(i,J,k)+v(i,J-1,k)-Waves%US_y(i,J,k)-Waves%US_y(i,J-1,k))
1132 enddo
1133 endif
1134 ! things independent of position within the column
1135 Coriolis = 0.25*US%s_to_T*( (G%CoriolisBu(i,j) + G%CoriolisBu(i-1,j-1)) + &
11360 (G%CoriolisBu(i-1,j) + G%CoriolisBu(i,j-1)) )
11370 surfFricVel = US%Z_to_m*US%s_to_T * uStar(i,j)
1138
1139 ! Bulk Richardson number computed for each cell in a column,
1140 ! assuming OBLdepth = grid cell depth. After Rib(k) is
1141 ! known for the column, then CVMix interpolates to find
1142 ! the actual OBLdepth. This approach avoids need to iterate
1143 ! on the OBLdepth calculation. It follows that used in MOM5
1144 ! and POP.
11450 iFaceHeight(1) = 0.0 ! BBL is all relative to the surface
11460 pRef = 0. ; if (associated(tv%p_surf)) pRef = tv%p_surf(i,j)
11470 hcorr = 0.
1148
11490 if (CS%StokesMOST) call Compute_StokesDrift( i, j, h(i,j,1) , iFaceHeight(1), &
11500 uS_Hi(1), vS_Hi(1), uS_H(1), vS_H(1), uSbar_H(1), vSbar_H(1), Waves)
1151
11520 do k=1,GV%ke
1153 ! cell center and cell bottom in meters (negative values in the ocean)
11540 dh = dz(i,j,k) ! Nominal thickness to use for increment
11550 dh = dh + hcorr ! Take away the accumulated error (could temporarily make dh<0)
11560 hcorr = min( dh - CS%min_thickness, 0. ) ! If inflating then hcorr<0
11570 dh = max( dh, CS%min_thickness ) ! Limit increment dh>=min_thickness
11580 cellHeight(k) = iFaceHeight(k) - 0.5 * dh
11590 iFaceHeight(k+1) = iFaceHeight(k) - dh
1160
1161 ! find ksfc for cell where "surface layer" sits
11620 SLdepth_0d = CS%surf_layer_ext*max( max(-cellHeight(k),-iFaceHeight(2) ), CS%minOBLdepth )
11630 ksfc = k
11640 do ktmp = 1,k
11650 if (-1.0*iFaceHeight(ktmp+1) >= SLdepth_0d) then
11660 ksfc = ktmp
11670 exit
1168 endif
1169 enddo
1170
11710 if (CS%StokesMOST) then
1172 ! if k=1, want buoyFlux(i,j,1) - buoyFlux(i,j,2), otherwise
1173 ! subtract average of buoyFlux(i,j,k) and buoyFlux(i,j,k+1)
1174 surfBuoyFlux = buoy_scale * &
11750 (buoyFlux(i,j,1) - 0.5*(buoyFlux(i,j,max(2,k))+buoyFlux(i,j,k+1)) )
11760 surfBuoyFlux2(k) = surfBuoyFlux
1177 call Compute_StokesDrift(i,j, iFaceHeight(k),iFaceHeight(k+1), &
11780 uS_Hi(k+1), vS_Hi(k+1), uS_H(k), vS_H(k), uSbar_H(k), vSbar_H(k), Waves)
1179 call Compute_StokesDrift(i,j, iFaceHeight(ksfc) , -SLdepth_0d, &
11800 uS_SLD , vS_SLD, uS_SLC , vS_SLC, uSbar_SLD, vSbar_SLD, Waves)
1181 call cvmix_kpp_compute_StokesXi( iFaceHeight,CellHeight,ksfc ,SLdepth_0d,surfBuoyFlux, &
1182 surfFricVel,waves%omega_w2x(i,j), uE_H, vE_H, uS_Hi, vS_Hi, uSbar_H, vSbar_H, uS_SLD,&
11830 vS_SLD, uSbar_SLD, vSbar_SLD, StokesXI, CVMix_kpp_params_user=CS%KPP_params )
11840 StokesXI_1d(k) = StokesXI
11850 StokesVt_1d(k) = 0.0 ! StokesXI
1186
1187 ! average temperature, salinity, u and v over surface layer starting at ksfc
11880 delH = SLdepth_0d + iFaceHeight(ksfc)
11890 surfHtemp = Temp(i,j,ksfc) * delH
11900 surfHsalt = Salt(i,j,ksfc) * delH
11910 surfHu = (uE_H(ksfc) + uSbar_SLD) * delH
11920 surfHv = (vE_H(ksfc) + vSbar_SLD) * delH
11930 hTot = delH
11940 do ktmp = 1,ksfc-1 ! if ksfc >=2
11950 delH = h(i,j,ktmp)*GV%H_to_Z
11960 hTot = hTot + delH
11970 surfHtemp = surfHtemp + Temp(i,j,ktmp) * delH
11980 surfHsalt = surfHsalt + Salt(i,j,ktmp) * delH
11990 surfHu = surfHu + (uE_H(ktmp) + uSbar_H(ktmp)) * delH
12000 surfHv = surfHv + (vE_H(ktmp) + vSbar_H(ktmp)) * delH
1201 enddo
12020 I_hTot = 1./hTot
12030 surfTemp = surfHtemp * I_hTot
12040 surfSalt = surfHsalt * I_hTot
12050 surfU = surfHu * I_hTot
12060 surfV = surfHv * I_hTot
1207
12080 Uk = uE_H(k) + uS_H(k) - surfU
12090 Vk = vE_H(k) + vS_H(k) - surfV
1210
1211 else !not StokesMOST
12120 StokesXI_1d(k) = 0.0
1213 ! average temperature, salinity, u and v over surface layer
1214 ! use C-grid average to get u and v on T-points.
12150 surfHtemp = 0.0
12160 surfHsalt = 0.0
12170 surfHu = 0.0
12180 surfHv = 0.0
12190 surfHuS = 0.0
12200 surfHvS = 0.0
12210 hTot = 0.0
12220 do ktmp = 1,ksfc
1223
1224 ! SLdepth_0d can be between cell interfaces
12250 delH = min( max(0.0, SLdepth_0d - hTot), dz(i,j,ktmp) )
1226
1227 ! surface layer thickness
12280 hTot = hTot + delH
1229
1230 ! surface averaged fields
12310 surfHtemp = surfHtemp + Temp(i,j,ktmp) * delH
12320 surfHsalt = surfHsalt + Salt(i,j,ktmp) * delH
12330 surfHu = surfHu + 0.5*(u(i,j,ktmp)+u(i-1,j,ktmp)) * delH
12340 surfHv = surfHv + 0.5*(v(i,j,ktmp)+v(i,j-1,ktmp)) * delH
12350 if (CS%Stokes_Mixing) then
12360 surfHus = surfHus + 0.5*(Waves%US_x(i,j,ktmp)+Waves%US_x(i-1,j,ktmp)) * delH
12370 surfHvs = surfHvs + 0.5*(Waves%US_y(i,j,ktmp)+Waves%US_y(i,j-1,ktmp)) * delH
1238 endif
1239
1240 enddo
1241 !I_hTot = 1./hTot
1242 !surfTemp = surfHtemp * I_hTot
1243 !surfSalt = surfHsalt * I_hTot
1244 !surfU = surfHu * I_hTot
1245 !surfV = surfHv * I_hTot
1246 !surfUs = surfHus * I_hTot
1247 !surfVs = surfHvs * I_hTot
1248
12490 surfTemp = surfHtemp / hTot
12500 surfSalt = surfHsalt / hTot
12510 surfU = surfHu / hTot
12520 surfV = surfHv / hTot
12530 surfUs = surfHus / hTot
12540 surfVs = surfHvs / hTot
1255 ! vertical shear between present layer and surface layer averaged surfU and surfV.
1256 ! C-grid average to get Uk and Vk on T-points.
12570 Uk = 0.5*(u(i,j,k)+u(i-1,j,k)) - surfU
12580 Vk = 0.5*(v(i,j,k)+v(i,j-1,k)) - surfV
1259
12600 if (CS%Stokes_Mixing) then
1261 ! If momentum is mixed down the Stokes drift gradient, then
1262 ! the Stokes drift must be included in the bulk Richardson number
1263 ! calculation.
12640 Uk = Uk + (0.5*(Waves%Us_x(i,j,k)+Waves%US_x(i-1,j,k)) - surfUs )
12650 Vk = Vk + (0.5*(Waves%Us_y(i,j,k)+Waves%Us_y(i,j-1,k)) - surfVs )
1266 endif
1267
1268 ! this difference accounts for penetrating SW
12690 surfBuoyFlux = buoy_scale * (buoyFlux(i,j,1) - buoyFlux(i,j,k+1))
12700 surfBuoyFlux2(k) = surfBuoyFlux
1271
1272 endif ! StokesMOST
1273
12740 deltaU2(k) = US%L_T_to_m_s**2 * ((Uk**2) + (Vk**2))
1275
1276 ! pressure, temperature, and salinity for calling the equation of state
1277 ! kk+1 = surface fields
1278 ! kk+2 = k fields
1279 ! kk+3 = km1 fields
12800 km1 = max(1, k-1)
12810 kk = 3*(k-1)
12820 pres_1D(kk+1) = pRef
12830 pres_1D(kk+2) = pRef
12840 pres_1D(kk+3) = pRef
12850 Temp_1D(kk+1) = surfTemp
12860 Temp_1D(kk+2) = Temp(i,j,k)
12870 Temp_1D(kk+3) = Temp(i,j,km1)
12880 Salt_1D(kk+1) = surfSalt
12890 Salt_1D(kk+2) = Salt(i,j,k)
12900 Salt_1D(kk+3) = Salt(i,j,km1)
1291
1292 ! pRef is pressure at interface between k and km1 [R L2 T-2 ~> Pa].
1293 ! iterate pRef for next pass through k-loop.
12940 pRef = pRef + (GV%g_Earth * GV%H_to_RZ) * h(i,j,k)
1295
1296 enddo ! k-loop finishes
1297
12980 if ( (CS%LT_K_ENHANCEMENT .or. CS%LT_VT2_ENHANCEMENT) .and. .not. present(lamult)) then
12990 MLD_guess = max( CS%MLD_guess_min, abs(CS%OBLdepthprev(i,j) ) )
1300 call get_Langmuir_Number(LA, G, GV, US, MLD_guess, uStar(i,j), i, j, &
13010 dz=dz(i,j,:), U_H=U_H, V_H=V_H, WAVES=WAVES)
13020 CS%La_SL(i,j) = LA
1303 endif
1304
1305
1306 ! compute in-situ density
13070 call calculate_density(Temp_1D, Salt_1D, pres_1D, rho_1D, tv%eqn_of_state)
1308
1309 ! N2 (can be negative) and N (non-negative) on interfaces.
1310 ! deltaRho is non-local rho difference used for bulk Richardson number.
1311 ! CS%N is local N (with floor) used for unresolved shear calculation.
13120 do k = 1, GV%ke
13130 km1 = max(1, k-1)
13140 kk = 3*(k-1)
13150 deltaRho(k) = rho_1D(kk+2) - rho_1D(kk+1)
13160 if (GV%Boussinesq .or. GV%semi_Boussinesq) then
13170 deltaBuoy(k) = GoRho*(rho_1D(kk+2) - rho_1D(kk+1))
1318 else
1319 deltaBuoy(k) = (US%Z_to_m*US%s_to_T**2) * GV%g_Earth_Z_T2 * &
13200 ( (rho_1D(kk+2) - rho_1D(kk+1)) / (0.5 * (rho_1D(kk+2) + rho_1D(kk+1))) )
1321 endif
1322 N2_1d(k) = (GoRho_Z_L2 * (rho_1D(kk+2) - rho_1D(kk+3)) ) / &
13230 ((0.5*(h(i,j,km1) + h(i,j,k))+GV%H_subroundoff))
13240 CS%N(i,j,k) = sqrt( max( N2_1d(k), 0.) )
1325 enddo
13260 N2_1d(GV%ke+1 ) = 0.0
13270 CS%N(i,j,GV%ke+1 ) = 0.0
1328
1329 ! Convert columns to MKS units for passing to CVMix
13300 do k = 1, GV%ke
13310 OBL_depth(k) = -US%Z_to_m * cellHeight(k)
13320 z_cell(k) = US%Z_to_m*cellHeight(k)
1333 enddo
13340 do K = 1, GV%ke+1
13350 N_col(K) = US%s_to_T*CS%N(i,j,K)
13360 z_inter(K) = US%Z_to_m*iFaceHeight(K)
1337 enddo
1338
1339 ! CVMix_kpp_compute_turbulent_scales_1d_OBL computes w_s velocity scale at cell centers for
1340 ! CVmix_kpp_compute_bulk_Richardson call to CVmix_kpp_compute_unresolved_shear
1341 ! at sigma=Vt_layer (CS%surf_layer_ext or 1.0) for this calculation.
1342 ! StokesVt_1d controls Stokes enhancement (= 0 for none)
13430 Vt_layer = 1.0 ! CS%surf_layer_ext
1344 call CVMix_kpp_compute_turbulent_scales( & ! 1d_OBL
1345 Vt_layer, & ! (in) Boundary layer extent contributing to unresolved shear
1346 OBL_depth, & ! (in) OBL depth [m]
1347 surfBuoyFlux2, & ! (in) Buoyancy flux at surface [m2 s-3]
1348 surfFricVel, & ! (in) Turbulent friction velocity at surface [m s-1]
1349 xi=StokesVt_1d, & ! (in) Stokes similarity parameter-->1/CHI(xi) enhance of Vt
1350 w_s=Ws_1d, & ! (out) Turbulent velocity scale profile [m s-1]
13510 CVMix_kpp_params_user=CS%KPP_params )
1352
1353 ! Determine the enhancement factor for unresolved shear
13540 IF (CS%LT_VT2_ENHANCEMENT) then
13550 IF (CS%LT_VT2_METHOD==LT_VT2_MODE_CONSTANT) then
13560 LangEnhVT2 = CS%KPP_VT2_ENH_FAC
13570 elseif (CS%LT_VT2_METHOD==LT_VT2_MODE_VR12) then
1358 !Introduced minimum value for La_SL, so maximum value for enhvt2 is removed.
13590 if (present(lamult)) then
13600 LangEnhVT2 = lamult(i,j)
1361 else
1362 LangEnhVT2 = sqrt(1.+(1.5*CS%La_SL(i,j))**(-2) + &
13630 (5.4*CS%La_SL(i,j))**(-4))
1364 endif
1365 else
1366 ! for other methods (e.g., LT_VT2_MODE_RW16, LT_VT2_MODE_LF17), the enhancement factor is
1367 ! computed internally within CVMix using LaSL, bfsfc, and ustar to be passed to CVMix.
13680 LangEnhVT2 = 1.0
1369 endif
1370 else
13710 LangEnhVT2 = 1.0
1372 endif
1373
13740 surfBuoyFlux = buoy_scale * buoyFlux(i,j,1)
1375
1376 ! Calculate Bulk Richardson number from eq (21) of LMD94
1377 BulkRi_1d = CVmix_kpp_compute_bulk_Richardson( &
1378 zt_cntr=z_cell, & ! Depth of cell center [m]
1379 delta_buoy_cntr=deltaBuoy, & ! Bulk buoyancy difference, Br-B(z) [m s-2]
1380 delta_Vsqr_cntr=deltaU2, & ! Square of resolved velocity difference [m2 s-2]
1381 ws_cntr=Ws_1d, & ! Turbulent velocity scale profile [m s-1]
1382 N_iface=N_col, & ! Buoyancy frequency [s-1]
1383 EFactor=LangEnhVT2, & ! Langmuir enhancement factor [nondim]
1384 LaSL=CS%La_SL(i,j), & ! surface layer averaged Langmuir number [nondim]
1385 bfsfc=surfBuoyFlux2, & ! surface buoyancy flux [m2 s-3]
1386 uStar=surfFricVel, & ! surface friction velocity [m s-1]
13870 CVMix_kpp_params_user=CS%KPP_params ) ! KPP parameters
1388
1389! ! A hack to avoid KPP reaching the bottom. It was needed during development
1390! ! because KPP was unable to handle vanishingly small layers near the bottom.
1391! if (CS%deepOBLoffset>0.) then
1392! zBottomMinusOffset = iFaceHeight(GV%ke+1) + min(CS%deepOBLoffset, -0.1*iFaceHeight(GV%ke+1))
1393! CS%OBLdepth(i,j) = min( CS%OBLdepth(i,j), -zBottomMinusOffset )
1394! endif
13950 zBottomMinusOffset = iFaceHeight(GV%ke+1) + min(CS%deepOBLoffset,-0.1*iFaceHeight(GV%ke+1))
1396
1397 call CVMix_kpp_compute_OBL_depth( &
1398 BulkRi_1d, & ! (in) Bulk Richardson number
1399 z_inter, & ! (in) Height of interfaces [m]
1400 KPP_OBL_depth, & ! (out) OBL depth [m]
1401 CS%kOBL(i,j), & ! (out) level (+fraction) of OBL extent
1402 zt_cntr=z_cell, & ! (in) Height of cell centers [m]
1403 surf_fric=surfFricVel, & ! (in) Turbulent friction velocity at surface [m s-1]
1404 surf_buoy=surfBuoyFlux2, & ! (in) Buoyancy flux at surface [m2 s-3]
1405 Coriolis=Coriolis, & ! (in) Coriolis parameter [s-1]
1406 Xi = StokesXI_1d, & ! (in) Stokes similarity parameter Lmob limit (1-Xi)
1407 zBottom = zBottomMinusOffset, & ! (in) Numerical limit on OBLdepth
14080 CVMix_kpp_params_user=CS%KPP_params ) ! KPP parameters
14090 CS%OBLdepth(i,j) = US%m_to_Z * KPP_OBL_depth
1410
14110 if (CS%StokesMOST) then
14120 kbl = int(CS%kOBL(i,j))
14130 SLdepth_0d = CS%surf_layer_ext*CS%OBLdepth(i,j)
14140 surfBuoyFlux = surfBuoyFlux2(kbl)
1415 ! find ksfc for cell where "surface layer" sits
14160 ksfc = kbl
14170 do ktmp = 1, kbl
14180 if (-1.0*iFaceHeight(ktmp+1) >= SLdepth_0d) then
14190 ksfc = ktmp
14200 exit
1421 endif
1422 enddo
1423
1424 call Compute_StokesDrift(i,j, iFaceHeight(ksfc) , -SLdepth_0d, &
14250 uS_SLD , vS_SLD, uS_SLC , vS_SLC, uSbar_SLD, vSbar_SLD, Waves)
1426 call cvmix_kpp_compute_StokesXi( iFaceHeight,CellHeight,ksfc ,SLdepth_0d, &
1427 surfBuoyFlux, surfFricVel,waves%omega_w2x(i,j), uE_H, vE_H, uS_Hi, &
1428 vS_Hi, uSbar_H, vSbar_H, uS_SLD, vS_SLD, uSbar_SLD, vSbar_SLD, &
14290 StokesXI, CVMix_kpp_params_user=CS%KPP_params )
14300 CS%StokesParXI(i,j) = StokesXI
14310 CS%Lam2(i,j) = sqrt(US_Hi(1)**2+VS_Hi(1)**2) / MAX(surfFricVel,0.0002)
1432
1433 else !.not Stokes_MOST
14340 CS%StokesParXI(i,j) = 10.0
14350 CS%Lam2(i,j) = sqrt(US_Hi(1)**2+VS_Hi(1)**2) / MAX(surfFricVel,0.0002)
1436
1437 ! A hack to avoid KPP reaching the bottom. It was needed during development
1438 ! because KPP was unable to handle vanishingly small layers near the bottom.
14390 if (CS%deepOBLoffset>0.) then
14400 zBottomMinusOffset = iFaceHeight(GV%ke+1) + min(CS%deepOBLoffset, -0.1*iFaceHeight(GV%ke+1))
14410 CS%OBLdepth(i,j) = min( CS%OBLdepth(i,j), -zBottomMinusOffset )
1442 endif
1443
1444 ! apply some constraints on OBLdepth
14450 if (CS%fixedOBLdepth) CS%OBLdepth(i,j) = CS%fixedOBLdepth_value
14460 CS%OBLdepth(i,j) = max( CS%OBLdepth(i,j), -iFaceHeight(2) ) ! no shallower than top layer
14470 CS%OBLdepth(i,j) = min( CS%OBLdepth(i,j), -iFaceHeight(GV%ke+1) ) ! no deeper than bottom
14480 CS%kOBL(i,j) = CVMix_kpp_compute_kOBL_depth( iFaceHeight, cellHeight, CS%OBLdepth(i,j) )
1449
1450 endif !Stokes_MOST
1451
1452 ! compute unresolved squared velocity for diagnostics
14530 if (CS%id_Vt2 > 0) then
1454 Vt2_1d(:) = CVmix_kpp_compute_unresolved_shear( &
1455 z_cell, & ! Depth of cell center [m]
1456 ws_cntr=Ws_1d, & ! Turbulent velocity scale profile, at centers [m s-1]
1457 N_iface=N_col, & ! Buoyancy frequency at interface [s-1]
1458 EFactor=LangEnhVT2, & ! Langmuir enhancement factor [nondim]
1459 LaSL=CS%La_SL(i,j), & ! surface layer averaged Langmuir number [nondim]
1460 bfsfc=surfBuoyFlux2, & ! surface buoyancy flux [m2 s-3]
1461 uStar=surfFricVel, & ! surface friction velocity [m s-1]
14620 CVmix_kpp_params_user=CS%KPP_params ) ! KPP parameters
14630 CS%Vt2(i,j,:) = US%m_to_Z**2*US%T_to_s**2 * Vt2_1d(:)
1464 endif
1465
1466 ! recompute wscale for diagnostics, now that we in fact know boundary layer depth
1467 !BGR consider if LTEnhancement is wanted for diagnostics
14680 if (CS%id_Ws > 0) then
1469 call CVMix_kpp_compute_turbulent_scales( &
1470 -cellHeight(:)/CS%OBLdepth(i,j), & ! (in) Normalized boundary layer coordinate [nondim]
1471 US%Z_to_m*CS%OBLdepth(i,j), & ! (in) OBL depth [m]
1472 surfBuoyFlux, & ! (in) Buoyancy flux at surface [m2 s-3]
1473 surfFricVel, & ! (in) Turbulent friction velocity at surface [m s-1]
1474 xi=StokesXI, & ! (in) Stokes similarity parameter-->1/CHI(xi) enhance
1475 w_s=Ws_1d, & ! (out) Turbulent velocity scale profile [m s-1]
14760 CVMix_kpp_params_user=CS%KPP_params) ! KPP parameters
14770 CS%Ws(i,j,:) = US%m_to_Z*US%T_to_s*Ws_1d(:)
1478 endif
1479
1480 ! Diagnostics
14810 if (CS%id_N2 > 0) CS%N2(i,j,:) = N2_1d(:)
14820 if (CS%id_BulkDrho > 0) CS%dRho(i,j,:) = deltaRho(:)
14830 if (CS%id_BulkRi > 0) CS%BulkRi(i,j,:) = BulkRi_1d(:)
14840 if (CS%id_BulkUz2 > 0) CS%Uz2(i,j,:) = US%m_s_to_L_T**2 * deltaU2(:)
14850 if (CS%id_Tsurf > 0) CS%Tsurf(i,j) = surfTemp
14860 if (CS%id_Ssurf > 0) CS%Ssurf(i,j) = surfSalt
14870 if (CS%id_Usurf > 0) CS%Usurf(i,j) = surfU
14880 if (CS%id_Vsurf > 0) CS%Vsurf(i,j) = surfV
1489
1490 endif ; enddo
1491 enddo
1492
14930 call cpu_clock_end(id_clock_KPP_compute_BLD)
1494
1495 ! send diagnostics to post_data
14960 if (CS%id_BulkRi > 0) call post_data(CS%id_BulkRi, CS%BulkRi, CS%diag)
14970 if (CS%id_N > 0) call post_data(CS%id_N, CS%N, CS%diag)
14980 if (CS%id_N2 > 0) call post_data(CS%id_N2, CS%N2, CS%diag)
14990 if (CS%id_Tsurf > 0) call post_data(CS%id_Tsurf, CS%Tsurf, CS%diag)
15000 if (CS%id_Ssurf > 0) call post_data(CS%id_Ssurf, CS%Ssurf, CS%diag)
15010 if (CS%id_Usurf > 0) call post_data(CS%id_Usurf, CS%Usurf, CS%diag)
15020 if (CS%id_Vsurf > 0) call post_data(CS%id_Vsurf, CS%Vsurf, CS%diag)
15030 if (CS%id_BulkDrho > 0) call post_data(CS%id_BulkDrho, CS%dRho, CS%diag)
15040 if (CS%id_BulkUz2 > 0) call post_data(CS%id_BulkUz2, CS%Uz2, CS%diag)
15050 if (CS%id_EnhK > 0) call post_data(CS%id_EnhK, CS%EnhK, CS%diag)
15060 if (CS%id_EnhVt2 > 0) call post_data(CS%id_EnhVt2, CS%EnhVt2, CS%diag)
15070 if (CS%id_La_SL > 0) call post_data(CS%id_La_SL, CS%La_SL, CS%diag)
15080 if (CS%id_Vt2 > 0) call post_data(CS%id_Vt2, CS%Vt2, CS%diag)
1509
15100 if (CS%StokesMOST) then
15110 if (CS%id_StokesXI > 0) call post_data(CS%id_StokesXI, CS%StokesParXI, CS%diag)
15120 if (CS%id_Lam2 > 0) call post_data(CS%id_Lam2 , CS%Lam2 , CS%diag)
1513 endif
1514
1515 ! BLD smoothing:
15160 if (CS%n_smooth > 0) call KPP_smooth_BLD(CS, G, GV, US, dz)
1517
15180end subroutine KPP_compute_BLD
1519
1520
1521!> Apply a 1-1-4-1-1 Laplacian filter one time on BLD to reduce any horizontal two-grid-point noise
15220subroutine KPP_smooth_BLD(CS, G, GV, US, dz)
1523 ! Arguments
1524 type(KPP_CS), pointer :: CS !< Control structure
1525 type(ocean_grid_type), intent(inout) :: G !< Ocean grid
1526 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid
1527 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1528 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: dz !< Layer thicknesses [Z ~> m]
1529
1530 ! local variables
15310 real, dimension(SZI_(G),SZJ_(G)) :: OBLdepth_prev ! OBLdepth before s.th smoothing iteration [Z ~> m]
15320 real, dimension(SZI_(G),SZJ_(G)) :: total_depth ! The total depth of the water column, adjusted
1533 ! for the minimum layer thickness [Z ~> m]
15340 real, dimension( GV%ke ) :: cellHeight ! Cell center heights referenced to surface [Z ~> m]
1535 ! (negative in the ocean)
15360 real, dimension( GV%ke+1 ) :: iFaceHeight ! Interface heights referenced to surface [Z ~> m]
1537 ! (negative in the ocean)
1538 real :: wc, ww, we, wn, ws ! averaging weights for smoothing [nondim]
1539 real :: dh ! The local thickness used for calculating interface positions [Z ~> m]
15400 real :: h_cor(SZI_(G)) ! A cumulative correction arising from inflation of vanished layers [Z ~> m]
1541 real :: hcorr ! A cumulative correction arising from inflation of vanished layers [Z ~> m]
1542 integer :: i, j, k, s, halo
1543
15440 call cpu_clock_begin(id_clock_KPP_smoothing)
1545
1546 ! Find the total water column thickness first, as it is reused for each smoothing pass.
15470 total_depth(:,:) = 0.0
1548
1549 !$OMP parallel do default(shared) private(dh, h_cor)
15500 do j = G%jsc, G%jec
15510 h_cor(:) = 0.
15520 do k=1,GV%ke
15530 do i=G%isc,G%iec ; if (G%mask2dT(i,j) > 0.0) then
1554 ! This code replicates the interface height calculations below. It could be simpler, as shown below.
15550 dh = dz(i,j,k) ! Nominal thickness to use for increment
15560 dh = dh + h_cor(i) ! Take away the accumulated error (could temporarily make dh<0)
15570 h_cor(i) = min( dh - CS%min_thickness, 0. ) ! If inflating then hcorr<0
15580 dh = max( dh, CS%min_thickness ) ! Limit increment dh>=min_thickness
15590 total_depth(i,j) = total_depth(i,j) + dh
1560 endif ; enddo
1561 enddo
1562 enddo
1563 ! A much simpler (but answer changing) version of the total_depth calculation would be
1564 ! do k=1,GV%ke ; do j=G%jsc,G%jec ; do i=G%isc,G%iec
1565 ! total_depth(i,j) = total_depth(i,j) + dz(i,j,k)
1566 ! enddo ; enddo ; enddo
1567
1568 ! Update halos once, then march inward for each iteration
15690 if (CS%n_smooth > 1) call pass_var(total_depth, G%Domain, halo=CS%n_smooth, complete=.false.)
15700 call pass_var(CS%OBLdepth, G%Domain, halo=CS%n_smooth)
1571
15720 if (CS%id_OBLdepth_original > 0) CS%OBLdepth_original(:,:) = CS%OBLdepth(:,:)
1573
15740 do s=1,CS%n_smooth
1575
15760 OBLdepth_prev(:,:) = CS%OBLdepth(:,:)
15770 halo = CS%n_smooth - s
1578
1579 ! apply smoothing on OBL depth
1580 !$OMP parallel do default(none) shared(G, GV, CS, OBLdepth_prev, total_depth, halo) &
1581 !$OMP private(wc, ww, we, wn, ws)
15820 do j = G%jsc-halo, G%jec+halo
15830 do i = G%isc-halo, G%iec+halo ; if (G%mask2dT(i,j) > 0.0) then
1584 ! compute weights
15850 ww = 0.125 * G%mask2dT(i-1,j)
15860 we = 0.125 * G%mask2dT(i+1,j)
15870 ws = 0.125 * G%mask2dT(i,j-1)
15880 wn = 0.125 * G%mask2dT(i,j+1)
15890 wc = 1.0 - (ww+we+wn+ws)
1590
15910 if (CS%answer_date < 20240501) then
1592 CS%OBLdepth(i,j) = wc * OBLdepth_prev(i,j) &
1593 + ww * OBLdepth_prev(i-1,j) &
1594 + we * OBLdepth_prev(i+1,j) &
1595 + ws * OBLdepth_prev(i,j-1) &
15960 + wn * OBLdepth_prev(i,j+1)
1597 else
1598 CS%OBLdepth(i,j) = wc * OBLdepth_prev(i,j) &
1599 + ((ww * OBLdepth_prev(i-1,j) + we * OBLdepth_prev(i+1,j)) &
16000 + (ws * OBLdepth_prev(i,j-1) + wn * OBLdepth_prev(i,j+1)))
1601 endif
1602
1603 ! Apply OBLdepth smoothing at a cell only if the OBLdepth gets deeper via smoothing.
16040 if (CS%deepen_only) CS%OBLdepth(i,j) = max(CS%OBLdepth(i,j), OBLdepth_prev(i,j))
1605
1606 ! prevent OBL depths deeper than the bathymetric depth
16070 CS%OBLdepth(i,j) = min( CS%OBLdepth(i,j), total_depth(i,j) ) ! no deeper than bottom
1608 endif ; enddo
1609 enddo
1610
1611 enddo ! s-loop
1612
1613 ! Determine the fractional index of the bottom of the boundary layer.
1614 !$OMP parallel do default(none) shared(G, GV, CS, dz) &
1615 !$OMP private(dh, hcorr, cellHeight, iFaceHeight)
16160 do j=G%jsc,G%jec ; do i=G%isc,G%iec ; if (G%mask2dT(i,j) > 0.0) then
1617
16180 iFaceHeight(1) = 0.0 ! BBL is all relative to the surface
16190 hcorr = 0.
16200 do k=1,GV%ke
1621 ! cell center and cell bottom in meters (negative values in the ocean)
16220 dh = dz(i,j,k) ! Nominal thickness to use for increment
16230 dh = dh + hcorr ! Take away the accumulated error (could temporarily make dh<0)
16240 hcorr = min( dh - CS%min_thickness, 0. ) ! If inflating then hcorr<0
16250 dh = max( dh, CS%min_thickness ) ! Limit increment dh>=min_thickness
16260 cellHeight(k) = iFaceHeight(k) - 0.5 * dh
16270 iFaceHeight(k+1) = iFaceHeight(k) - dh
1628 enddo
1629
16300 CS%kOBL(i,j) = CVMix_kpp_compute_kOBL_depth( iFaceHeight, cellHeight, CS%OBLdepth(i,j) )
1631 endif ; enddo ; enddo
1632
16330 call cpu_clock_end(id_clock_KPP_smoothing)
1634
16350end subroutine KPP_smooth_BLD
1636
1637
1638!> Copies KPP surface boundary layer depth into BLD, in units of [Z ~> m] unless other units are specified.
16390subroutine KPP_get_BLD(CS, BLD, G, US, m_to_BLD_units)
1640 type(KPP_CS), pointer :: CS !< Control structure for
1641 !! this module
1642 type(ocean_grid_type), intent(in) :: G !< Grid structure
1643 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1644 real, dimension(SZI_(G),SZJ_(G)), intent(inout) :: BLD !< Boundary layer depth [Z ~> m] or other units
1645 real, optional, intent(in) :: m_to_BLD_units !< A conversion factor from meters
1646 !! to the desired units for BLD [various]
1647 ! Local variables
1648 real :: scale ! A dimensional rescaling factor in [nondim] or other units.
1649 integer :: i,j
1650
16510 scale = 1.0 ; if (present(m_to_BLD_units)) scale = US%Z_to_m*m_to_BLD_units
1652
1653 !$OMP parallel do default(none) shared(BLD, CS, G, scale)
16540 do j = G%jsc, G%jec ; do i = G%isc, G%iec
16550 BLD(i,j) = scale * CS%OBLdepth(i,j)
1656 enddo ; enddo
1657
16580end subroutine KPP_get_BLD
1659
1660!> Apply KPP non-local transport of surface fluxes for a given tracer
16610subroutine KPP_NonLocalTransport(CS, G, GV, h, nonLocalTrans, surfFlux, &
16620 dt, diag, tr_ptr, scalar, flux_scale)
1663 type(KPP_CS), intent(in) :: CS !< Control structure
1664 type(ocean_grid_type), intent(in) :: G !< Ocean grid
1665 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid
1666 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer/level thickness [H ~> m or kg m-2]
1667 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: nonLocalTrans !< Non-local transport [nondim]
1668 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: surfFlux !< Surface flux of scalar
1669 !! [conc H T-1 ~> conc m s-1 or conc kg m-2 s-1]
1670 real, intent(in) :: dt !< Time-step [T ~> s]
1671 type(diag_ctrl), target, intent(in) :: diag !< Diagnostics
1672 type(tracer_type), pointer, intent(in) :: tr_ptr !< tracer_type has diagnostic ids on it
1673 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: scalar !< Scalar (scalar units [conc])
1674 real, optional, intent(in) :: flux_scale !< Scale factor to get surfFlux
1675 !! into proper units [various]
1676
1677 integer :: i, j, k
16780 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: dtracer ! Rate of tracer change [conc T-1 ~> conc s-1]
16790 real, dimension(SZI_(G),SZJ_(G)) :: surfFlux_loc ! An optionally rescaled surface flux of the scalar
1680 ! in [conc H T-1 ~> conc m s-1 or conc kg m-2 s-1] or other units
1681
1682 ! term used to scale
16830 if (present(flux_scale)) then
16840 do j = G%jsc, G%jec ; do i = G%isc, G%iec
16850 surfFlux_loc(i,j) = surfFlux(i,j) * flux_scale
1686 enddo ; enddo
1687 else
16880 surfFlux_loc(:,:) = surfFlux(:,:)
1689 endif
1690
1691 ! Post surface flux diagnostic
16920 if (tr_ptr%id_net_surfflux > 0) call post_data(tr_ptr%id_net_surfflux, surfFlux_loc(:,:), diag)
1693
1694 ! Only continue if we are applying the nonlocal tendency
1695 ! or the nonlocal tendency diagnostic has been requested
16960 if ((tr_ptr%id_NLT_tendency > 0) .or. (CS%applyNonLocalTrans)) then
1697
1698 !$OMP parallel do default(none) shared(dtracer, nonLocalTrans, h, G, GV, surfFlux_loc)
16990 do k = 1, GV%ke ; do j = G%jsc, G%jec ; do i = G%isc, G%iec
1700 dtracer(i,j,k) = ( nonLocalTrans(i,j,k) - nonLocalTrans(i,j,k+1) ) / &
17010 ( h(i,j,k) + GV%H_subroundoff ) * surfFlux_loc(i,j)
1702 enddo ; enddo ; enddo
1703
1704 ! Update tracer due to non-local redistribution of surface flux
17050 if (CS%applyNonLocalTrans) then
1706 !$OMP parallel do default(none) shared(G, GV, dt, scalar, dtracer)
17070 do k = 1, GV%ke ; do j = G%jsc, G%jec ; do i = G%isc, G%iec
17080 scalar(i,j,k) = scalar(i,j,k) + dt * dtracer(i,j,k)
1709 enddo ; enddo ; enddo
1710 endif
17110 if (tr_ptr%id_NLT_tendency > 0) call post_data(tr_ptr%id_NLT_tendency, dtracer, diag)
1712
1713 endif
1714
1715
17160 if (tr_ptr%id_NLT_budget > 0) then
1717 !$OMP parallel do default(none) shared(G, GV, dtracer, nonLocalTrans, surfFlux_loc)
17180 do k = 1, GV%ke ; do j = G%jsc, G%jec ; do i = G%isc, G%iec
1719 ! Here dtracer has units of [Q R Z T-1 ~> W m-2].
17200 dtracer(i,j,k) = (nonLocalTrans(i,j,k) - nonLocalTrans(i,j,k+1)) * surfFlux_loc(i,j)
1721 enddo ; enddo ; enddo
17220 call post_data(tr_ptr%id_NLT_budget, dtracer(:,:,:), diag)
1723 endif
1724
17250end subroutine KPP_NonLocalTransport
1726
1727
1728!> Apply KPP non-local transport of surface fluxes for temperature.
17290subroutine KPP_NonLocalTransport_temp(CS, G, GV, h, nonLocalTrans, surfFlux, dt, tr_ptr, scalar, C_p)
1730 type(KPP_CS), intent(in) :: CS !< Control structure
1731 type(ocean_grid_type), intent(in) :: G !< Ocean grid
1732 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid
1733 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer/level thickness [H ~> m or kg m-2]
1734 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: nonLocalTrans !< Non-local transport [nondim]
1735 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: surfFlux !< Surface flux of temperature
1736 !! [C H T-1 ~> degC m s-1 or degC kg m-2 s-1]
1737 real, intent(in) :: dt !< Time-step [T ~> s]
1738 type(tracer_type), pointer, intent(in) :: tr_ptr !< tracer_type has diagnostic ids on it
1739 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: scalar !< temperature [C ~> degC]
1740 real, intent(in) :: C_p !< Seawater specific heat capacity
1741 !! [Q C-1 ~> J kg-1 degC-1]
1742
1743 call KPP_NonLocalTransport(CS, G, GV, h, nonLocalTrans, surfFlux, dt, CS%diag, &
17440 tr_ptr, scalar)
1745
17460end subroutine KPP_NonLocalTransport_temp
1747
1748
1749!> Apply KPP non-local transport of surface fluxes for salinity.
17500subroutine KPP_NonLocalTransport_saln(CS, G, GV, h, nonLocalTrans, surfFlux, dt, tr_ptr, scalar)
1751 type(KPP_CS), intent(in) :: CS !< Control structure
1752 type(ocean_grid_type), intent(in) :: G !< Ocean grid
1753 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid
1754 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer/level thickness [H ~> m or kg m-2]
1755 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: nonLocalTrans !< Non-local transport [nondim]
1756 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: surfFlux !< Surface flux of salt
1757 !! [S H T-1 ~> ppt m s-1 or ppt kg m-2 s-1]
1758 real, intent(in) :: dt !< Time-step [T ~> s]
1759 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: scalar !< Salinity [S ~> ppt]
1760 type(tracer_type), pointer, intent(in) :: tr_ptr !< tracer_type has diagnostic ids on it
1761
1762 call KPP_NonLocalTransport(CS, G, GV, h, nonLocalTrans, surfFlux, dt, CS%diag, &
17630 tr_ptr, scalar)
1764
17650end subroutine KPP_NonLocalTransport_saln
1766
1767!> Compute Stokes Drift components at zbot < ztop <= 0 and at k=0.5*(ztop+zbot) and
1768!! average components from ztop to zbot <= 0
17690subroutine Compute_StokesDrift(i ,j, ztop, zbot, uS_i, vS_i, uS_k, vS_k, uSbar, vSbar, waves)
1770
1771 type(wave_parameters_CS), pointer :: waves !< Wave CS for Langmuir turbulence
1772 real, intent(in) :: ztop !< cell top
1773 real, intent(in) :: zbot !< cell bottom
1774 real, intent(inout) :: uS_i !< Stokes u velocity at zbot interface
1775 real, intent(inout) :: vS_i !< Stokes v velocity at zbot interface
1776 real, intent(inout) :: uS_k !< Stokes u velocity at zk center
1777 real, intent(inout) :: vS_k !< Stokes v at zk =0.5(ztop+zbot)
1778 real, intent(inout) :: uSbar !< mean Stokes u (ztop to zbot)
1779 real, intent(inout) :: vSbar !< mean Stokes v (ztop to zbot)
1780 integer, intent(in) :: i !< Meridional index of H-point
1781 integer, intent(in) :: j !< Zonal index of H-point
1782
1783 ! local variables
1784 integer :: b !< wavenumber band index
1785 real :: fexp !< an exponential function
1786 real :: WaveNum !< Wavenumber
1787
17880 uS_i = 0.0
17890 vS_i = 0.0
17900 uS_k = 0.0
17910 vS_k = 0.0
17920 uSbar = 0.0
17930 vSbar = 0.0
17940 do b = 1, waves%NumBands
17950 WaveNum = waves%WaveNum_Cen(b)
17960 fexp = exp(2. * WaveNum * zbot)
17970 uS_i = uS_i + waves%Ustk_Hb(i,j,b) * fexp
17980 vS_i = vS_i + waves%Vstk_Hb(i,j,b) * fexp
17990 fexp = exp( WaveNum * (ztop + zbot) )
18000 uS_k = uS_k+ waves%Ustk_Hb(i,j,b) * fexp
18010 vS_k = vS_k+ waves%Vstk_Hb(i,j,b) * fexp
18020 fexp = exp(2. * WaveNum * ztop) - exp(2. * WaveNum * zbot)
18030 uSbar = uSbar + 0.5 * waves%Ustk_Hb(i,j,b) * fexp / WaveNum
18040 vSbar = vSbar + 0.5 * waves%Vstk_Hb(i,j,b) * fexp / WaveNum
1805 enddo
18060 uSbar = uSbar / (ztop-zbot)
18070 vSbar = vSbar / (ztop-zbot)
1808
18090end subroutine Compute_StokesDrift
1810
1811!> Clear pointers, deallocate memory
18120subroutine KPP_end(CS)
1813 type(KPP_CS), pointer :: CS !< Control structure
1814
18150 if (.not.associated(CS)) return
1816
18170 deallocate(CS)
1818
1819end subroutine KPP_end
1820
18210end module MOM_CVMix_KPP