MOM_MEKE.F90

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!> Implements the Mesoscale Eddy Kinetic Energy framework
6!! with topographic beta effect included in computing beta in Rhines scale
7
8module mom_meke
9
10use iso_fortran_env, only : real32
11
12use mom_coms, only : pe_here
13use mom_database_comms, only : dbclient_type, dbcomms_cs_type
14use mom_debugging, only : hchksum, uvchksum
15use mom_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, clock_routine
16use mom_diag_mediator, only : post_data, register_diag_field, safe_alloc_ptr
17use mom_diag_mediator, only : diag_ctrl, time_type
18use mom_domains, only : create_group_pass, do_group_pass, group_pass_type
19use mom_domains, only : pass_vector, pass_var
20use mom_error_handler, only : mom_error, fatal, warning, note, mom_mesg, is_root_pe
21use mom_file_parser, only : read_param, get_param, log_version, param_file_type
22use mom_grid, only : ocean_grid_type
24use mom_interface_heights, only : find_eta
25use mom_interpolate, only : init_external_field, time_interp_external
26use mom_interpolate, only : time_interp_external_init
27use mom_interpolate, only : external_field
28use mom_io, only : vardesc, var_desc, slasher
30use mom_restart, only : mom_restart_cs, register_restart_field, query_initialized
32use mom_time_manager, only : time_type_to_real
36use mom_meke_types, only : meke_type
37
38
39implicit none ; private
40
41#include <MOM_memory.h>
42
44
45! Constants for this module
46integer, parameter :: num_features = 4 !< How many features used to predict EKE
47integer, parameter :: mke_idx = 1 !< Index of mean kinetic energy in the feature array
48integer, parameter :: slope_z_idx = 2 !< Index of vertically averaged isopycnal slope in the feature array
49integer, parameter :: rv_idx = 3 !< Index of surface relative vorticity in the feature array
50integer, parameter :: rd_dx_z_idx = 4 !< Index of the radius of deformation over the grid size in the feature array
51
52integer, parameter :: eke_prog = 1 !< Use prognostic equation to calculate EKE
53integer, parameter :: eke_file = 2 !< Read in EKE from a file
54integer, parameter :: eke_dbclient = 3 !< Infer EKE using a neural network
55
56!> Control structure that contains MEKE parameters and diagnostics handles
57type, public :: meke_cs ; private
58 logical :: initialized = .false. !< True if this control structure has been initialized.
59 ! Parameters
60 real :: meke_frcoeff !< Efficiency of conversion of ME into MEKE [nondim]
61 real :: meke_bhfrcoeff!< Efficiency of conversion of ME into MEKE by the biharmonic dissipation [nondim]
62 real :: meke_gmcoeff !< Efficiency of conversion of PE into MEKE [nondim]
63 real :: meke_gmecoeff !< Efficiency of conversion of MEKE into ME by GME [nondim]
64 real :: meke_damping !< Local depth-independent MEKE dissipation rate [T-1 ~> s-1].
65 real :: meke_cd_scale !< The ratio of the bottom eddy velocity to the column mean
66 !! eddy velocity, i.e. sqrt(2*MEKE), [nondim]. This should be less than 1
67 !! to account for the surface intensification of MEKE.
68 real :: meke_cb !< Coefficient in the \f$\gamma_{bot}\f$ expression [nondim]
69 real :: meke_min_gamma!< Minimum value of gamma_b^2 allowed [nondim]
70 real :: meke_ct !< Coefficient in the \f$\gamma_{bt}\f$ expression [nondim]
71 logical :: visc_drag !< If true use the vertvisc_type to calculate bottom drag.
72 logical :: meke_geometric !< If true, uses the GM coefficient formulation from the GEOMETRIC
73 !! framework (Marshall et al., 2012)
74 real :: meke_geometric_alpha !< The nondimensional coefficient governing the efficiency of the
75 !! GEOMETRIC thickness diffusion [nondim].
76 logical :: meke_equilibrium_alt !< If true, use an alternative calculation for the
77 !! equilibrium value of MEKE.
78 logical :: meke_equilibrium_restoring !< If true, restore MEKE back to its equilibrium value,
79 !! which is calculated at each time step.
80 logical :: gm_src_alt !< If true, use the GM energy conversion form S^2*N^2*kappa rather
81 !! than the streamfunction for the MEKE GM source term.
82 real :: meke_min_depth_tot !< The minimum total thickness over which to distribute MEKE energy
83 !! sources from GM energy conversion [H ~> m or kg m-2]. When the total
84 !! thickness is less than this, the sources are scaled away.
85 logical :: rd_as_max_scale !< If true the length scale can not exceed the
86 !! first baroclinic deformation radius.
87 logical :: use_old_lscale !< Use the old formula for mixing length scale.
88 logical :: use_min_lscale !< Use simple minimum for mixing length scale.
89 logical :: meke_positive !< If true, it guarantees that MEKE will always be >= 0.
90 real :: lscale_maxval !< The ceiling on the MEKE mixing length scale when use_min_lscale is true [L ~> m].
91 real :: cdrag !< The bottom drag coefficient for MEKE, times rescaling factors [H L-1 ~> nondim or kg m-3]
92 real :: meke_bgsrc !< Background energy source for MEKE [L2 T-3 ~> W kg-1] (= m2 s-3).
93 real :: meke_dtscale !< Scale factor to accelerate time-stepping [nondim]
94 real :: meke_khcoeff !< Scaling factor to convert MEKE into Kh [nondim]
95 real :: meke_uscale !< MEKE velocity scale for bottom drag [L T-1 ~> m s-1]
96 real :: meke_kh !< Background lateral diffusion of MEKE [L2 T-1 ~> m2 s-1]
97 real :: meke_k4 !< Background bi-harmonic diffusivity (of MEKE) [L4 T-1 ~> m4 s-1]
98 real :: khmeke_fac !< A factor relating MEKE%Kh to the diffusivity used for
99 !! MEKE itself [nondim].
100 real :: viscosity_coeff_ku !< The scaling coefficient in the expression for
101 !! viscosity used to parameterize lateral harmonic momentum mixing
102 !! by unresolved eddies represented by MEKE [nondim].
103 real :: viscosity_coeff_au !< The scaling coefficient in the expression for
104 !! viscosity used to parameterize lateral biharmonic momentum mixing
105 !! by unresolved eddies represented by MEKE [nondim].
106 real :: lfixed !< Fixed mixing length scale [L ~> m].
107 real :: adeform !< Weighting towards deformation scale of mixing length [nondim]
108 real :: arhines !< Weighting towards Rhines scale of mixing length [nondim]
109 real :: africt !< Weighting towards frictional arrest scale of mixing length [nondim]
110 real :: aeady !< Weighting towards Eady scale of mixing length [nondim]
111 real :: agrid !< Weighting towards grid scale of mixing length [nondim]
112 real :: meke_advection_factor !< A scaling in front of the advection of MEKE [nondim]
113 real :: meke_topographic_beta !< Weight for how much topographic beta is considered
114 !! when computing beta in Rhines scale [nondim]
115 real :: meke_restoring_rate !< Inverse of the timescale used to nudge MEKE toward its
116 !! equilibrium value [T-1 ~> s-1].
117 logical :: meke_advection_bug !< If true, recover a bug in the calculation of the barotropic
118 !! transport for the advection of MEKE, wherein only the transports in the
119 !! deepest layer are used.
120 logical :: fixed_total_depth !< If true, use the nominal bathymetric depth as the estimate of
121 !! the time-varying ocean depth. Otherwise base the depth on the total
122 !! ocean mass per unit area.
123 real :: rho_fixed_total_depth !< A density used to translate the nominal bathymetric depth into an
124 !! estimate of the total ocean mass per unit area when MEKE_FIXED_TOTAL_DEPTH
125 !! is true [R ~> kg m-3]
126 logical :: kh_flux_enabled !< If true, lateral diffusive MEKE flux is enabled.
127 logical :: initialize !< If True, invokes a steady state solver to calculate MEKE.
128 logical :: debug !< If true, write out checksums of data for debugging
129 integer :: eke_src !< Enum specifying whether EKE is stepped forward prognostically (default),
130 !! read in from a file, or inferred via a neural network
131 logical :: sqg_use_meke !< If True, use MEKE%Le for the SQG vertical structure.
132 type(diag_ctrl), pointer :: diag => null() !< A type that regulates diagnostics output
133 !>@{ Diagnostic handles
134 integer :: id_meke = -1, id_ue = -1, id_kh = -1, id_src = -1
135 integer :: id_src_adv = -1, id_src_mom_k4 = -1, id_src_btm_drag = -1
136 integer :: id_src_gm = -1, id_src_mom_lp = -1, id_src_mom_bh = -1
137 integer :: id_ub = -1, id_ut = -1
138 integer :: id_gm_src = -1, id_mom_src = -1, id_mom_src_bh = -1, id_gme_snk = -1, id_decay = -1
139 integer :: id_khmeke_u = -1, id_khmeke_v = -1, id_ku = -1, id_au = -1
140 integer :: id_le = -1, id_gamma_b = -1, id_gamma_t = -1
141 integer :: id_lrhines = -1, id_leady = -1
142 integer :: id_meke_equilibrium = -1
143 !>@}
144 type(external_field) :: eke_handle !< Handle for reading in EKE from a file
145 ! Infrastructure
146 integer :: id_clock_pass !< Clock for group pass calls
147 type(group_pass_type) :: pass_meke !< Group halo pass handle for MEKE%MEKE and maybe MEKE%Kh_diff
148 type(group_pass_type) :: pass_kh !< Group halo pass handle for MEKE%Kh, MEKE%Ku, and/or MEKE%Au
149
150 ! MEKE via Machine Learning
151 type(dbclient_type), pointer :: client => null() !< Pointer to the database client
152
153 logical :: online_analysis !< If true, post the EKE used in MOM6 at every timestep
154 character(len=5) :: model_key = 'mleke' !< Key where the ML-model is stored
155 character(len=7) :: key_suffix !< Suffix appended to every key sent to Redis
156 real :: eke_max !< The maximum value of EKE considered physically reasonable [L2 T-2 ~> m2 s-2]
157
158 ! Clock ids
159 integer :: id_client_init !< Clock id to time initialization of the client
160 integer :: id_put_tensor !< Clock id to time put_tensor routine
161 integer :: id_run_model !< Clock id to time running of the ML model
162 integer :: id_unpack_tensor !< Clock id to time retrieval of EKE prediction
163
164 ! Diagnostic ids
165 integer :: id_mke = -1 !< Diagnostic id for surface mean kinetic energy
166 integer :: id_slope_z = -1 !< Diagnostic id for vertically averaged horizontal slope magnitude
167 integer :: id_slope_x = -1 !< Diagnostic id for isopycnal slope in the x-direction
168 integer :: id_slope_y = -1 !< Diagnostic id for isopycnal slope in the y-direction
169 integer :: id_rv = -1 !< Diagnostic id for surface relative vorticity
170
171end type meke_cs
172
173contains
174
175!> Integrates forward-in-time the MEKE eddy energy equation.
176!! See \ref section_MEKE_equations.
177subroutine step_forward_meke(MEKE, h, SN_u, SN_v, visc, dt, G, GV, US, CS, hu, hv, u, v, tv, Time)
178 type(meke_type), intent(inout) :: meke !< MEKE data.
179 type(ocean_grid_type), intent(inout) :: g !< Ocean grid.
180 type(verticalgrid_type), intent(in) :: gv !< Ocean vertical grid structure.
181 type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
182 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> m or kg m-2].
183 real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: sn_u !< Eady growth rate at u-points [T-1 ~> s-1].
184 real, dimension(SZI_(G),SZJB_(G)), intent(in) :: sn_v !< Eady growth rate at v-points [T-1 ~> s-1].
185 type(vertvisc_type), intent(in) :: visc !< The vertical viscosity type.
186 real, intent(in) :: dt !< Model(baroclinic) time-step [T ~> s].
187 type(meke_cs), intent(inout) :: cs !< MEKE control structure.
188 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(in) :: hu !< Accumulated zonal mass flux [H L2 ~> m3 or kg].
189 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(in) :: hv !< Accumulated meridional mass flux [H L2 ~> m3 or kg]
190 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(inout) :: u !< Zonal velocity [L T-1 ~> m s-1]
191 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(inout) :: v !< Meridional velocity [L T-1 ~> m s-1]
192 type(thermo_var_ptrs), intent(in) :: tv !< Type containing thermodynamic variables
193 type(time_type), intent(in) :: time !< The time used for interpolating EKE
194
195 ! Local variables
196 real, dimension(SZI_(G),SZJ_(G)) :: &
197 data_eke, & ! EKE from file [L2 T-2 ~> m2 s-2]
198 mass, & ! The total mass of the water column [R Z ~> kg m-2].
199 i_mass, & ! The inverse of mass [R-1 Z-1 ~> m2 kg-1].
200 depth_tot, & ! The depth of the water column [H ~> m or kg m-2].
201 src, & ! The sum of all MEKE sources [L2 T-3 ~> W kg-1] (= m2 s-3).
202 meke_decay, & ! A diagnostic of the MEKE decay timescale [T-1 ~> s-1].
203 src_adv, & ! The MEKE source/tendency from the horizontal advection of MEKE [L2 T-3 ~> W kg-1] (= m2 s-3).
204 src_mom_k4, & ! The MEKE source/tendency from the bihamornic of MEKE [L2 T-3 ~> W kg-1] (= m2 s-3).
205 src_btm_drag, & ! The MEKE source/tendency from the bottom drag acting on MEKE [L2 T-3 ~> W kg-1] (= m2 s-3).
206 src_gm, & ! The MEKE source/tendency from the thickness mixing (GM) [L2 T-3 ~> W kg-1] (= m2 s-3).
207 src_mom_lp, & ! The MEKE source/tendency from the Laplacian of the resolved flow [L2 T-3 ~> W kg-1] (= m2 s-3).
208 src_mom_bh, & ! The MEKE source/tendency from the biharmonic of the resolved flow [L2 T-3 ~> W kg-1] (= m2 s-3).
209 damp_rate_s1, & ! The MEKE damping rate computed at the 1st Strang splitting stage [T-1 ~> s-1].
210 meke_current, & ! A copy of MEKE for use in computing the MEKE damping [L2 T-2 ~> m2 s-2].
211 drag_rate_visc, & ! Near-bottom velocity contribution to bottom drag [H T-1 ~> m s-1 or kg m-2 s-1]
212 drag_rate, & ! The MEKE spindown timescale due to bottom drag [T-1 ~> s-1].
213 del2meke, & ! Laplacian of MEKE, used for bi-harmonic diffusion [T-2 ~> s-2].
214 del4meke, & ! Time-integrated MEKE tendency arising from the biharmonic of MEKE [L2 T-2 ~> m2 s-2].
215 lmixscale, & ! Eddy mixing length [L ~> m].
216 barotrfac2, & ! Ratio of EKE_barotropic / EKE [nondim]
217 bottomfac2, & ! Ratio of EKE_bottom / EKE [nondim]
218 tmp, & ! Temporary variable for computation of diagnostic velocities [L T-1 ~> m s-1]
219 equilibrium_value, & ! The equilibrium value of MEKE to be calculated at
220 ! each time step [L2 T-2 ~> m2 s-2]
221 damp_rate, & ! The MEKE damping rate [T-1 ~> s-1]
222 damping ! The net damping of a field after sdt_damp [nondim]
223
224 real, dimension(SZIB_(G),SZJ_(G)) :: &
225 meke_uflux, & ! The zonal advective and diffusive flux of MEKE with units of [R Z L4 T-3 ~> kg m2 s-3].
226 ! In one place, MEKE_uflux is used as temporary work space with units of [L2 T-2 ~> m2 s-2].
227 kh_u, & ! The zonal diffusivity that is actually used [L2 T-1 ~> m2 s-1].
228 barohu, & ! Depth integrated accumulated zonal mass flux [R Z L2 ~> kg].
229 drag_vel_u ! A piston velocity associated with bottom drag at u-points [H T-1 ~> m s-1 or kg m-2 s-1]
230 real, dimension(SZI_(G),SZJB_(G)) :: &
231 meke_vflux, & ! The meridional advective and diffusive flux of MEKE with units of [R Z L4 T-3 ~> kg m2 s-3].
232 ! In one place, MEKE_vflux is used as temporary work space with units of [L2 T-2 ~> m2 s-2].
233 kh_v, & ! The meridional diffusivity that is actually used [L2 T-1 ~> m2 s-1].
234 barohv, & ! Depth integrated accumulated meridional mass flux [R Z L2 ~> kg].
235 drag_vel_v ! A piston velocity associated with bottom drag at v-points [H T-1 ~> m s-1 or kg m-2 s-1]
236 real :: bh_coeff ! Biharmonic part of efficiency conversion in total MEKE [nondim]
237 real :: kh_here ! The local horizontal viscosity [L2 T-1 ~> m2 s-1]
238 real :: inv_kh_max ! The inverse of the local horizontal viscosity [T L-2 ~> s m-2]
239 real :: k4_here ! The local horizontal biharmonic viscosity [L4 T-1 ~> m4 s-1]
240 real :: inv_k4_max ! The inverse of the local horizontal biharmonic viscosity [T L-4 ~> s m-4]
241 real :: cdrag2 ! The square of the drag coefficient times unit conversion factors [H2 L-2 ~> nondim or kg2 m-6]
242 real :: advfac ! The product of the advection scaling factor and 1/dt [T-1 ~> s-1]
243 real :: mass_neglect ! A negligible mass [R Z ~> kg m-2].
244 real :: sdt ! dt to use locally [T ~> s] (could be scaled to accelerate)
245 real :: sdt_damp ! dt for damping [T ~> s] (sdt could be split).
246 real :: damp_step ! Size of damping timestep relative to sdt [nondim]
247 logical :: use_drag_rate ! Flag to indicate drag_rate is finite
248 logical :: any_damping_diags_s1 ! True if any damped diagnostics are enabled in first stage
249 logical :: any_damping_diags ! True if any damped diagnostics are enabled
250 integer :: i, j, k, is, ie, js, je, isq, ieq, jsq, jeq, nz
251 real(kind=real32), dimension(size(MEKE%MEKE),NUM_FEATURES) :: features_array ! The array of features
252 ! needed for the machine learning inference, with different
253 ! units for the various subarrays [various]
254
255 is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = gv%ke
256 isq = g%IscB ; ieq = g%IecB ; jsq = g%JscB ; jeq = g%JecB
257
258 if (.not.cs%initialized) call mom_error(fatal, &
259 "MOM_MEKE: Module must be initialized before it is used.")
260
261 if ((cs%MEKE_Cd_scale > 0.0) .or. (cs%MEKE_Cb>0.) .or. cs%visc_drag) then
262 use_drag_rate = .true.
263 else
264 use_drag_rate = .false.
265 endif
266
267 ! Only integrate the MEKE equations if MEKE is required.
268 if (.not. allocated(meke%MEKE)) then
269! call MOM_error(FATAL, "MOM_MEKE: MEKE%MEKE is not associated!")
270 return
271 endif
272
273 select case(cs%eke_src)
274 case(eke_prog)
275 if (cs%debug) then
276 if (allocated(meke%mom_src)) &
277 call hchksum(meke%mom_src, 'MEKE mom_src', g%HI, unscale=us%RZ3_T3_to_W_m2*us%L_to_Z**2)
278 if (allocated(meke%mom_src_bh)) &
279 call hchksum(meke%mom_src_bh, 'MEKE mom_src_bh', g%HI, unscale=us%RZ3_T3_to_W_m2*us%L_to_Z**2)
280 if (allocated(meke%GME_snk)) &
281 call hchksum(meke%GME_snk, 'MEKE GME_snk', g%HI, unscale=us%RZ3_T3_to_W_m2*us%L_to_Z**2)
282 if (allocated(meke%GM_src)) &
283 call hchksum(meke%GM_src, 'MEKE GM_src', g%HI, unscale=us%RZ3_T3_to_W_m2*us%L_to_Z**2)
284 if (allocated(meke%MEKE)) &
285 call hchksum(meke%MEKE, 'MEKE MEKE', g%HI, unscale=us%L_T_to_m_s**2)
286 call uvchksum("MEKE SN_[uv]", sn_u, sn_v, g%HI, unscale=us%s_to_T, &
287 scalar_pair=.true.)
288 call uvchksum("MEKE h[uv]", hu, hv, g%HI, haloshift=0, symmetric=.true., &
289 unscale=gv%H_to_m*us%L_to_m**2)
290 endif
291
292 sdt = dt*cs%MEKE_dtScale ! Scaled dt to use for time-stepping
293 mass_neglect = gv%H_to_RZ * gv%H_subroundoff
294 cdrag2 = cs%cdrag**2
295
296 ! With a depth-dependent (and possibly strong) damping, it seems
297 ! advisable to use Strang splitting between the damping and diffusion.
298 damp_step = 1.
299 if (cs%MEKE_KH >= 0. .or. cs%MEKE_K4 >= 0.) damp_step = 0.5
300 sdt_damp = sdt * damp_step
301
302 ! Calculate depth integrated mass exchange if doing advection [R Z L2 ~> kg]
303 if (cs%MEKE_advection_factor>0.) then
304 do j=js,je ; do i=is-1,ie
305 barohu(i,j) = 0.
306 enddo ; enddo
307 do k=1,nz
308 do j=js,je ; do i=is-1,ie
309 barohu(i,j) = barohu(i,j) + hu(i,j,k) * gv%H_to_RZ
310 enddo ; enddo
311 enddo
312 do j=js-1,je ; do i=is,ie
313 barohv(i,j) = 0.
314 enddo ; enddo
315 do k=1,nz
316 do j=js-1,je ; do i=is,ie
317 barohv(i,j) = barohv(i,j) + hv(i,j,k) * gv%H_to_RZ
318 enddo ; enddo
319 enddo
320 if (cs%MEKE_advection_bug) then
321 ! This obviously incorrect code reproduces a bug in the original implementation of
322 ! the MEKE advection.
323 do j=js,je ; do i=is-1,ie
324 barohu(i,j) = hu(i,j,nz) * gv%H_to_RZ
325 enddo ; enddo
326 do j=js-1,je ; do i=is,ie
327 barohv(i,j) = hv(i,j,nz) * gv%H_to_RZ
328 enddo ; enddo
329 endif
330 endif
331
332 ! Calculate drag_rate_visc(i,j) which accounts for the model bottom mean flow
333 if (cs%visc_drag .and. allocated(visc%Kv_bbl_u) .and. allocated(visc%Kv_bbl_v)) then
334 !$OMP parallel do default(shared)
335 do j=js,je ; do i=is-1,ie
336 drag_vel_u(i,j) = 0.0
337 if ((g%mask2dCu(i,j) > 0.0) .and. (visc%bbl_thick_u(i,j) > 0.0)) &
338 drag_vel_u(i,j) = visc%Kv_bbl_u(i,j) / visc%bbl_thick_u(i,j)
339 enddo ; enddo
340 !$OMP parallel do default(shared)
341 do j=js-1,je ; do i=is,ie
342 drag_vel_v(i,j) = 0.0
343 if ((g%mask2dCv(i,j) > 0.0) .and. (visc%bbl_thick_v(i,j) > 0.0)) &
344 drag_vel_v(i,j) = visc%Kv_bbl_v(i,j) / visc%bbl_thick_v(i,j)
345 enddo ; enddo
346
347 !$OMP parallel do default(shared)
348 do j=js,je ; do i=is,ie
349 drag_rate_visc(i,j) = (0.25*g%IareaT(i,j) * &
350 (((g%areaCu(i-1,j)*drag_vel_u(i-1,j)) + &
351 (g%areaCu(i,j)*drag_vel_u(i,j))) + &
352 ((g%areaCv(i,j-1)*drag_vel_v(i,j-1)) + &
353 (g%areaCv(i,j)*drag_vel_v(i,j))) ) )
354 enddo ; enddo
355 else
356 !$OMP parallel do default(shared)
357 do j=js,je ; do i=is,ie
358 drag_rate_visc(i,j) = 0.
359 enddo ; enddo
360 endif
361
362 !$OMP parallel do default(shared)
363 do j=js-1,je+1
364 do i=is-1,ie+1 ; mass(i,j) = 0.0 ; enddo
365 do k=1,nz ; do i=is-1,ie+1
366 mass(i,j) = mass(i,j) + g%mask2dT(i,j) * (gv%H_to_RZ * h(i,j,k)) ! [R Z ~> kg m-2]
367 enddo ; enddo
368 do i=is-1,ie+1
369 i_mass(i,j) = 0.0
370 if (mass(i,j) > 0.0) i_mass(i,j) = 1.0 / mass(i,j) ! [R-1 Z-1 ~> m2 kg-1]
371 enddo
372 enddo
373
374 if (cs%fixed_total_depth) then
375 if (gv%Boussinesq) then
376 !$OMP parallel do default(shared)
377 do j=js-1,je+1 ; do i=is-1,ie+1
378 depth_tot(i,j) = max(g%meanSL(i,j) + g%bathyT(i,j), 0.0) * gv%Z_to_H
379 enddo ; enddo
380 else
381 !$OMP parallel do default(shared)
382 do j=js-1,je+1 ; do i=is-1,ie+1
383 depth_tot(i,j) = max(g%meanSL(i,j) + g%bathyT(i,j), 0.0) * cs%rho_fixed_total_depth * gv%RZ_to_H
384 enddo ; enddo
385 endif
386 else
387 !$OMP parallel do default(shared)
388 do j=js-1,je+1 ; do i=is-1,ie+1
389 depth_tot(i,j) = mass(i,j) * gv%RZ_to_H
390 enddo ; enddo
391 endif
392
393 if (cs%initialize) then
394 call meke_equilibrium(cs, meke, g, gv, us, sn_u, sn_v, drag_rate_visc, i_mass, depth_tot)
395 cs%initialize = .false.
396 endif
397
398 ! Calculates bottomFac2, barotrFac2 and LmixScale
399 call meke_lengthscales(cs, meke, g, gv, us, sn_u, sn_v, meke%MEKE, depth_tot, bottomfac2, barotrfac2, lmixscale)
400 if (cs%debug) then
401 if (cs%visc_drag) &
402 call uvchksum("MEKE drag_vel_[uv]", drag_vel_u, drag_vel_v, g%HI, &
403 unscale=gv%H_to_mks*us%s_to_T, scalar_pair=.true.)
404 call hchksum(mass, 'MEKE mass',g%HI,haloshift=1, unscale=us%RZ_to_kg_m2)
405 call hchksum(drag_rate_visc, 'MEKE drag_rate_visc', g%HI, unscale=gv%H_to_mks*us%s_to_T)
406 call hchksum(bottomfac2, 'MEKE bottomFac2', g%HI)
407 call hchksum(barotrfac2, 'MEKE barotrFac2', g%HI)
408 call hchksum(lmixscale, 'MEKE LmixScale', g%HI, unscale=us%L_to_m)
409 endif
410
411 if (allocated(meke%Le)) then
412 !$OMP parallel do default(shared)
413 do j=js,je ; do i=is,ie
414 meke%Le(i,j) = lmixscale(i,j)
415 enddo ; enddo
416 endif
417
418 ! Aggregate sources of MEKE (background, frictional and GM)
419 !$OMP parallel do default(shared)
420 do j=js,je ; do i=is,ie
421 src(i,j) = cs%MEKE_BGsrc
422 enddo ; enddo
423
424 ! Initialize diagnostics
425 if (cs%id_src_adv > 0) src_adv(is:ie, js:je) = 0.
426 if (cs%id_src_GM > 0) src_gm(is:ie, js:je) = 0.
427 if (cs%id_src_mom_lp > 0) src_mom_lp(is:ie, js:je) = 0.
428 if (cs%id_src_mom_bh > 0) src_mom_bh(is:ie, js:je) = 0.
429 if (cs%id_src_mom_K4 > 0) src_mom_k4(is:ie, js:je) = 0.
430 if (cs%id_src_btm_drag > 0) src_btm_drag(is:ie, js:je) = 0.
431
432 ! Identify any damped diagnostics in first stage of Strang splitting
433 any_damping_diags_s1 = any([ &
434 cs%id_src_GM > 0, &
435 cs%id_src_mom_lp > 0, &
436 cs%id_src_mom_bh > 0, &
437 cs%id_src_btm_drag > 0 &
438 ])
439
440 ! Identify any damped diagnostics
441 any_damping_diags = any([ &
442 any_damping_diags_s1, &
443 cs%id_src_adv > 0, &
444 cs%id_src_mom_K4 > 0 &
445 ])
446
447 if (cs%MEKE_FrCoeff > 0.) then
448 !$OMP parallel do default(shared)
449 do j=js,je ; do i=is,ie
450 src(i,j) = src(i,j) - cs%MEKE_FrCoeff * i_mass(i,j) * meke%mom_src(i,j)
451 enddo ; enddo
452 endif
453
454 if (allocated(meke%mom_src_bh)) then
455 if (cs%MEKE_bhFrCoeff > 0. .and. cs%MEKE_FrCoeff > 0.) then
456 bh_coeff = cs%MEKE_bhFrCoeff - cs%MEKE_FrCoeff
457 else
458 bh_coeff = cs%MEKE_bhFrCoeff
459 endif
460
461 !$OMP parallel do default(shared)
462 do j=js,je ; do i=is,ie
463 src(i,j) = src(i,j) - bh_coeff * i_mass(i,j) * meke%mom_src_bh(i,j)
464 enddo ; enddo
465
466 if (cs%id_src_mom_lp > 0) then
467 !$OMP parallel do default(shared)
468 do j=js,je ; do i=is,ie
469 src_mom_lp(i,j) = -cs%MEKE_FrCoeff * i_mass(i,j) &
470 * (meke%mom_src(i,j) - meke%mom_src_bh(i,j))
471 enddo ; enddo
472 endif
473
474 if (cs%id_src_mom_bh > 0) then
475 !$OMP parallel do default(shared)
476 do j=js,je ; do i=is,ie
477 src_mom_bh(i,j) = -cs%MEKE_bhFrCoeff * i_mass(i,j) * meke%mom_src_bh(i,j)
478 enddo ; enddo
479 endif
480 endif
481
482 if (allocated(meke%GME_snk)) then
483 !$OMP parallel do default(shared)
484 do j=js,je ; do i=is,ie
485 src(i,j) = src(i,j) - cs%MEKE_GMECoeff*i_mass(i,j)*meke%GME_snk(i,j)
486 enddo ; enddo
487 endif
488
489 if (allocated(meke%GM_src)) then
490 if (cs%GM_src_alt) then
491 !$OMP parallel do default(shared)
492 do j=js,je ; do i=is,ie
493 src(i,j) = src(i,j) - cs%MEKE_GMcoeff*meke%GM_src(i,j) / &
494 (gv%H_to_RZ * max(cs%MEKE_min_depth_tot, depth_tot(i,j)))
495 enddo ; enddo
496 else
497 !$OMP parallel do default(shared)
498 do j=js,je ; do i=is,ie
499 src(i,j) = src(i,j) - cs%MEKE_GMcoeff*i_mass(i,j)*meke%GM_src(i,j)
500 enddo ; enddo
501
502 do j=js,je ; do i=is,ie
503 src_gm(i,j) = -cs%MEKE_GMcoeff*i_mass(i,j)*meke%GM_src(i,j)
504 enddo ; enddo
505 endif
506 endif
507
508 if (cs%MEKE_equilibrium_restoring) then
509 call meke_equilibrium_restoring(cs, g, gv, us, sn_u, sn_v, depth_tot, &
510 equilibrium_value)
511 do j=js,je ; do i=is,ie
512 src(i,j) = src(i,j) - cs%MEKE_restoring_rate*(meke%MEKE(i,j) - equilibrium_value(i,j))
513 enddo ; enddo
514 endif
515
516 if (cs%debug) then
517 call hchksum(src, "MEKE src", g%HI, haloshift=0, unscale=us%L_to_m**2*us%s_to_T**3)
518 endif
519
520 ! Increase EKE by a full time-steps worth of source
521 !$OMP parallel do default(shared)
522 do j=js,je ; do i=is,ie
523 meke_current(i,j) = meke%MEKE(i,j)
524 meke%MEKE(i,j) = (meke%MEKE(i,j) + sdt*src(i,j))*g%mask2dT(i,j)
525 enddo ; enddo
526
527 if (use_drag_rate) then
528 ! Calculate a viscous drag rate (includes BBL contributions from mean flow and eddies)
529 !$OMP parallel do default(shared)
530 do j=js,je ; do i=is,ie
531 drag_rate(i,j) = (gv%H_to_RZ * i_mass(i,j)) * sqrt( drag_rate_visc(i,j)**2 + &
532 cdrag2 * ( max(0.0, 2.0*bottomfac2(i,j)*meke%MEKE(i,j)) + cs%MEKE_Uscale**2 ) )
533 enddo ; enddo
534 else
535 !$OMP parallel do default(shared)
536 do j=js,je ; do i=is,ie
537 drag_rate(i,j) = 0.
538 enddo ; enddo
539 endif
540
541 ! First stage of Strang splitting
542
543 !$OMP parallel do default(shared)
544 do j=js,je ; do i=is,ie
545 damp_rate(i,j) = cs%MEKE_damping + drag_rate(i,j) * bottomfac2(i,j)
546
547 if (meke%MEKE(i,j) < 0.) damp_rate(i,j) = 0.
548 ! notice that the above line ensures a damping only if MEKE is positive,
549 ! while leaving MEKE unchanged if it is negative
550 enddo ; enddo
551
552 ! NOTE: MEKE%MEKE cannot use `damping` since we must preserve the existing
553 ! bit-reproducible solution.
554 !$OMP parallel do default(shared)
555 do j=js,je ; do i=is,ie
556 meke%MEKE(i,j) = meke%MEKE(i,j) / (1. + sdt_damp * damp_rate(i,j))
557 enddo ; enddo
558
559 if (any_damping_diags_s1) then
560 !$OMP parallel do default(shared)
561 do j=js,je ; do i=is,ie
562 damping(i,j) = 1. / (1. + sdt_damp * damp_rate(i,j))
563 enddo ; enddo
564
565 if (cs%id_decay > 0) then
566 !$OMP parallel do default(shared)
567 do j=js,je ; do i=is,ie
568 meke_decay(i,j) = damp_rate(i,j) * g%mask2dT(i,j)
569 enddo ; enddo
570 endif
571
572 if (cs%id_src_GM > 0) then
573 !$OMP parallel do default(shared)
574 do j=js,je ; do i=is,ie
575 src_gm(i,j) = src_gm(i,j) * damping(i,j)
576 enddo ; enddo
577 endif
578
579 if (cs%id_src_mom_lp > 0) then
580 !$OMP parallel do default(shared)
581 do j=js,je ; do i=is,ie
582 src_mom_lp(i,j) = src_mom_lp(i,j) * damping(i,j)
583 enddo ; enddo
584 endif
585
586 if (cs%id_src_mom_bh > 0) then
587 !$OMP parallel do default(shared)
588 do j=js,je ; do i=is,ie
589 src_mom_bh(i,j) = src_mom_bh(i,j) * damping(i,j)
590 enddo ; enddo
591 endif
592
593 if (cs%id_src_btm_drag > 0) then
594 !$OMP parallel do default(shared)
595 do j=js,je ; do i=is,ie
596 src_btm_drag(i,j) = -meke_current(i,j) * ( &
597 damp_step * (damp_rate(i,j) * damping(i,j)) &
598 )
599 enddo ; enddo
600
601 ! Store the effective damping rate if sdt is split
602 if (cs%MEKE_KH >= 0. .or. cs%MEKE_K4 >= 0.) then
603 !$OMP parallel do default(shared)
604 do j=js,je ; do i=is,ie
605 damp_rate_s1(i,j) = damp_rate(i,j) * damping(i,j)
606 enddo ; enddo
607 endif
608 endif
609 endif
610
611 if (cs%kh_flux_enabled .or. cs%MEKE_K4 >= 0.0) then
612 ! Update MEKE in the halos for lateral or bi-harmonic diffusion
613 call cpu_clock_begin(cs%id_clock_pass)
614 call do_group_pass(cs%pass_MEKE, g%Domain)
615 call cpu_clock_end(cs%id_clock_pass)
616 endif
617
618 if (cs%MEKE_K4 >= 0.0) then
619 ! Calculate Laplacian of MEKE using MEKE_uflux and MEKE_vflux as temporary work space.
620 !$OMP parallel do default(shared)
621 do j=js-1,je+1 ; do i=is-2,ie+1
622 ! MEKE_uflux is used here as workspace with units of [L2 T-2 ~> m2 s-2].
623 meke_uflux(i,j) = (g%dy_Cu(i,j)*g%IdxCu_OBCmask(i,j)) * &
624 (meke%MEKE(i+1,j) - meke%MEKE(i,j))
625 ! This would have units of [R Z L2 T-2 ~> kg s-2]
626 ! MEKE_uflux(I,j) = ((G%dy_Cu(I,j)*G%IdxCu(I,j)) * &
627 ! ((2.0*mass(i,j)*mass(i+1,j)) / ((mass(i,j)+mass(i+1,j)) + mass_neglect)) ) * &
628 ! (MEKE%MEKE(i+1,j) - MEKE%MEKE(i,j))
629 enddo ; enddo
630 !$OMP parallel do default(shared)
631 do j=js-2,je+1 ; do i=is-1,ie+1
632 ! MEKE_vflux is used here as workspace with units of [L2 T-2 ~> m2 s-2].
633 meke_vflux(i,j) = (g%dx_Cv(i,j)*g%IdyCv_OBCmask(i,j)) * &
634 (meke%MEKE(i,j+1) - meke%MEKE(i,j))
635 ! This would have units of [R Z L2 T-2 ~> kg s-2]
636 ! MEKE_vflux(i,J) = ((G%dx_Cv(i,J)*G%IdyCv(i,J)) * &
637 ! ((2.0*mass(i,j)*mass(i,j+1)) / ((mass(i,j)+mass(i,j+1)) + mass_neglect)) ) * &
638 ! (MEKE%MEKE(i,j+1) - MEKE%MEKE(i,j))
639 enddo ; enddo
640
641 !$OMP parallel do default(shared)
642 do j=js-1,je+1 ; do i=is-1,ie+1 ! del2MEKE has units [T-2 ~> s-2].
643 del2meke(i,j) = g%IareaT(i,j) * &
644 ((meke_uflux(i,j) - meke_uflux(i-1,j)) + (meke_vflux(i,j) - meke_vflux(i,j-1)))
645 enddo ; enddo
646
647 ! Bi-harmonic diffusion of MEKE
648 !$OMP parallel do default(shared) private(K4_here,Inv_K4_max)
649 do j=js,je ; do i=is-1,ie
650 k4_here = cs%MEKE_K4 ! [L4 T-1 ~> m4 s-1]
651 ! Limit Kh to avoid CFL violations.
652 inv_k4_max = 64.0 * sdt * ((g%dy_Cu(i,j)*g%IdxCu(i,j)) * &
653 max(g%IareaT(i,j), g%IareaT(i+1,j)))**2
654 if (k4_here*inv_k4_max > 0.3) k4_here = 0.3 / inv_k4_max
655
656 ! Here the units of MEKE_uflux are [R Z L4 T-3 ~> kg m2 s-3].
657 meke_uflux(i,j) = ((k4_here * (g%dy_Cu(i,j)*g%IdxCu(i,j))) * &
658 ((2.0*mass(i,j)*mass(i+1,j)) / ((mass(i,j)+mass(i+1,j)) + mass_neglect)) ) * &
659 (del2meke(i+1,j) - del2meke(i,j))
660 enddo ; enddo
661 !$OMP parallel do default(shared) private(K4_here,Inv_K4_max)
662 do j=js-1,je ; do i=is,ie
663 k4_here = cs%MEKE_K4 ! [L4 T-1 ~> m4 s-1]
664 inv_k4_max = 64.0 * sdt * ((g%dx_Cv(i,j)*g%IdyCv(i,j)) * max(g%IareaT(i,j), g%IareaT(i,j+1)))**2
665 if (k4_here*inv_k4_max > 0.3) k4_here = 0.3 / inv_k4_max
666
667 ! Here the units of MEKE_vflux are [R Z L4 T-3 ~> kg m2 s-3].
668 meke_vflux(i,j) = ((k4_here * (g%dx_Cv(i,j)*g%IdyCv(i,j))) * &
669 ((2.0*mass(i,j)*mass(i,j+1)) / ((mass(i,j)+mass(i,j+1)) + mass_neglect)) ) * &
670 (del2meke(i,j+1) - del2meke(i,j))
671 enddo ; enddo
672 ! Store change in MEKE arising from the bi-harmonic in del4MEKE [L2 T-2 ~> m2 s-2].
673 !$OMP parallel do default(shared)
674 do j=js,je ; do i=is,ie
675 del4meke(i,j) = (sdt*(g%IareaT(i,j)*i_mass(i,j))) * &
676 ((meke_uflux(i-1,j) - meke_uflux(i,j)) + &
677 (meke_vflux(i,j-1) - meke_vflux(i,j)))
678 src_mom_k4(i,j) = (g%IareaT(i,j)*i_mass(i,j)) * &
679 ((meke_uflux(i-1,j) - meke_uflux(i,j)) + &
680 (meke_vflux(i,j-1) - meke_vflux(i,j)))
681 enddo ; enddo
682 endif !
683
684 if (cs%kh_flux_enabled) then
685 ! Lateral diffusion of MEKE
686 kh_here = max(0., cs%MEKE_Kh)
687 !$OMP parallel do default(shared) firstprivate(Kh_here) private(Inv_Kh_max)
688 do j=js,je ; do i=is-1,ie
689 ! Limit Kh to avoid CFL violations.
690 if (allocated(meke%Kh)) &
691 kh_here = max(0., cs%MEKE_Kh) + &
692 cs%KhMEKE_Fac*0.5*(meke%Kh(i,j)+meke%Kh(i+1,j))
693 if (allocated(meke%Kh_diff)) &
694 kh_here = max(0.,cs%MEKE_Kh) + &
695 cs%KhMEKE_Fac*0.5*(meke%Kh_diff(i,j)+meke%Kh_diff(i+1,j))
696 inv_kh_max = 2.0*sdt * ((g%dy_Cu(i,j)*g%IdxCu(i,j)) * &
697 max(g%IareaT(i,j),g%IareaT(i+1,j)))
698 if (kh_here*inv_kh_max > 0.25) kh_here = 0.25 / inv_kh_max
699 kh_u(i,j) = kh_here
700
701 ! Here the units of MEKE_uflux and MEKE_vflux are [R Z L4 T-3 ~> kg m2 s-3].
702 meke_uflux(i,j) = ((kh_here * (g%dy_Cu(i,j)*g%IdxCu(i,j))) * &
703 ((2.0*mass(i,j)*mass(i+1,j)) / ((mass(i,j)+mass(i+1,j)) + mass_neglect)) ) * &
704 (meke%MEKE(i,j) - meke%MEKE(i+1,j))
705 enddo ; enddo
706 !$OMP parallel do default(shared) firstprivate(Kh_here) private(Inv_Kh_max)
707 do j=js-1,je ; do i=is,ie
708 if (allocated(meke%Kh)) &
709 kh_here = max(0.,cs%MEKE_Kh) + cs%KhMEKE_Fac * 0.5*(meke%Kh(i,j)+meke%Kh(i,j+1))
710 if (allocated(meke%Kh_diff)) &
711 kh_here = max(0.,cs%MEKE_Kh) + cs%KhMEKE_Fac * 0.5*(meke%Kh_diff(i,j)+meke%Kh_diff(i,j+1))
712 inv_kh_max = 2.0*sdt * ((g%dx_Cv(i,j)*g%IdyCv(i,j)) * max(g%IareaT(i,j),g%IareaT(i,j+1)))
713 if (kh_here*inv_kh_max > 0.25) kh_here = 0.25 / inv_kh_max
714 kh_v(i,j) = kh_here
715
716 ! Here the units of MEKE_uflux and MEKE_vflux are [R Z L4 T-3 ~> kg m2 s-3].
717 meke_vflux(i,j) = ((kh_here * (g%dx_Cv(i,j)*g%IdyCv(i,j))) * &
718 ((2.0*mass(i,j)*mass(i,j+1)) / ((mass(i,j)+mass(i,j+1)) + mass_neglect)) ) * &
719 (meke%MEKE(i,j) - meke%MEKE(i,j+1))
720 enddo ; enddo
721 if (cs%MEKE_advection_factor>0.) then
722 advfac = cs%MEKE_advection_factor / sdt ! [T-1 ~> s-1]
723 !$OMP parallel do default(shared)
724 do j=js,je ; do i=is-1,ie
725 ! Here the units of the quantities added to MEKE_uflux are [R Z L4 T-3 ~> kg m2 s-3].
726 if (barohu(i,j)>0.) then
727 meke_uflux(i,j) = meke_uflux(i,j) + barohu(i,j)*meke%MEKE(i,j)*advfac
728 elseif (barohu(i,j)<0.) then
729 meke_uflux(i,j) = meke_uflux(i,j) + barohu(i,j)*meke%MEKE(i+1,j)*advfac
730 endif
731 enddo ; enddo
732 !$OMP parallel do default(shared)
733 do j=js-1,je ; do i=is,ie
734 ! Here the units of the quantities added to MEKE_vflux are [R Z L4 T-3 ~> kg m2 s-3].
735 if (barohv(i,j)>0.) then
736 meke_vflux(i,j) = meke_vflux(i,j) + barohv(i,j)*meke%MEKE(i,j)*advfac
737 elseif (barohv(i,j)<0.) then
738 meke_vflux(i,j) = meke_vflux(i,j) + barohv(i,j)*meke%MEKE(i,j+1)*advfac
739 endif
740 enddo ; enddo
741 endif
742
743 !$OMP parallel do default(shared)
744 do j=js,je ; do i=is,ie
745 meke%MEKE(i,j) = meke%MEKE(i,j) + (sdt*(g%IareaT(i,j)*i_mass(i,j))) * &
746 ((meke_uflux(i-1,j) - meke_uflux(i,j)) + &
747 (meke_vflux(i,j-1) - meke_vflux(i,j)))
748 enddo ; enddo
749
750 if (cs%id_src_adv > 0) then
751 !$OMP parallel do default(shared)
752 do j=js,je ; do i=is,ie
753 src_adv(i,j) = (g%IareaT(i,j)*i_mass(i,j)) * &
754 ((meke_uflux(i-1,j) - meke_uflux(i,j)) + &
755 (meke_vflux(i,j-1) - meke_vflux(i,j)))
756 enddo ; enddo
757 endif
758 endif ! MEKE_KH>0
759
760 ! Add on bi-harmonic tendency
761 if (cs%MEKE_K4 >= 0.0) then
762 !$OMP parallel do default(shared)
763 do j=js,je ; do i=is,ie
764 meke%MEKE(i,j) = meke%MEKE(i,j) + del4meke(i,j)
765 enddo ; enddo
766 endif
767
768 ! Second stage of Strang splitting
769 if (cs%MEKE_KH >= 0.0 .or. cs%MEKE_K4 >= 0.0) then
770 ! Recalculate the drag rate, since MEKE has changed.
771 if (use_drag_rate) then
772 !$OMP parallel do default(shared)
773 do j=js,je ; do i=is,ie
774 drag_rate(i,j) = (gv%H_to_RZ * i_mass(i,j)) * sqrt( drag_rate_visc(i,j)**2 + &
775 cdrag2 * ( max(0.0, 2.0*bottomfac2(i,j)*meke%MEKE(i,j)) + cs%MEKE_Uscale**2 ) )
776 enddo ; enddo
777 endif
778
779 !$OMP parallel do default(shared)
780 do j=js,je ; do i=is,ie
781 damp_rate(i,j) = cs%MEKE_damping + drag_rate(i,j) * bottomfac2(i,j)
782
783 if (meke%MEKE(i,j) < 0.) damp_rate(i,j) = 0.
784 ! notice that the above line ensures a damping only if MEKE is positive,
785 ! while leaving MEKE unchanged if it is negative
786 enddo ; enddo
787
788 ! NOTE: MEKE%MEKE cannot use `damping` since we must preserve the
789 ! existing bit-reproducible solution.
790 !$OMP parallel do default(shared)
791 do j=js,je ; do i=is,ie
792 meke%MEKE(i,j) = meke%MEKE(i,j) / (1. + sdt_damp * damp_rate(i,j))
793 enddo ; enddo
794
795 if (any_damping_diags) then
796 !$OMP parallel do default(shared)
797 do j=js,je ; do i=is,ie
798 damping(i,j) = 1. / (1. + sdt_damp * damp_rate(i,j))
799 enddo ; enddo
800
801 if (cs%id_decay > 0) then
802 !$OMP parallel do default(shared)
803 do j=js,je ; do i=is,ie
804 meke_decay(i,j) = damp_rate(i,j) * g%mask2dT(i,j)
805 enddo ; enddo
806 endif
807
808 if (cs%id_src_GM > 0) then
809 !$OMP parallel do default(shared)
810 do j=js,je ; do i=is,ie
811 src_gm(i,j) = src_gm(i,j) * damping(i,j)
812 enddo ; enddo
813 endif
814
815 if (cs%id_src_mom_lp > 0) then
816 !$OMP parallel do default(shared)
817 do j=js,je ; do i=is,ie
818 src_mom_lp(i,j) = src_mom_lp(i,j) * damping(i,j)
819 enddo ; enddo
820 endif
821
822 if (cs%id_src_mom_bh > 0) then
823 !$OMP parallel do default(shared)
824 do j=js,je ; do i=is,ie
825 src_mom_bh(i,j) = src_mom_bh(i,j) * damping(i,j)
826 enddo ; enddo
827 endif
828
829 if (cs%id_src_adv > 0) then
830 !$OMP parallel do default(shared)
831 do j=js,je ; do i=is,ie
832 src_adv(i,j) = src_adv(i,j) * damping(i,j)
833 enddo ; enddo
834 endif
835
836 if (cs%id_src_mom_K4 > 0) then
837 !$OMP parallel do default(shared)
838 do j=js,je ; do i=is,ie
839 src_mom_k4(i,j) = src_mom_k4(i,j) * damping(i,j)
840 enddo ; enddo
841 endif
842
843 if (cs%id_src_btm_drag > 0) then
844 !$OMP parallel do default(shared)
845 do j=js,je ; do i=is,ie
846 src_btm_drag(i,j) = -meke_current(i,j) * (damp_step &
847 * ((damp_rate(i,j) + damp_rate_s1(i,j)) * damping(i,j)) &
848 )
849 enddo ; enddo
850 endif
851 endif
852 endif ! MEKE_KH>=0
853
854 if (cs%debug) then
855 call hchksum(meke%MEKE, "MEKE post-update MEKE", g%HI, haloshift=0, unscale=us%L_T_to_m_s**2)
856 endif
857
858 case(eke_file)
859 call time_interp_external(cs%eke_handle, time, data_eke, scale=us%m_s_to_L_T**2)
860 do j=js,je ; do i=is,ie
861 meke%MEKE(i,j) = data_eke(i,j) * g%mask2dT(i,j)
862 enddo ; enddo
863 call meke_lengthscales(cs, meke, g, gv, us, sn_u, sn_v, meke%MEKE, depth_tot, bottomfac2, barotrfac2, lmixscale)
864 case(eke_dbclient)
865 call pass_vector(u, v, g%Domain)
866 call meke_lengthscales(cs, meke, g, gv, us, sn_u, sn_v, meke%MEKE, depth_tot, bottomfac2, barotrfac2, lmixscale)
867 call ml_meke_calculate_features(g, gv, us, cs, meke%Rd_dx_h, u, v, tv, h, dt, features_array)
868 call predict_meke(g, us, cs, SIZE(h), time, features_array, meke%MEKE)
869 case default
870 call mom_error(fatal,"Invalid method specified for calculating EKE")
871 end select
872
873 if (cs%MEKE_positive) then
874 !$OMP parallel do default(shared)
875 do j=js,je ; do i=is,ie
876 meke%MEKE(i,j) = max(0., meke%MEKE(i,j))
877 enddo ; enddo
878 endif
879
880 call cpu_clock_begin(cs%id_clock_pass)
881 call do_group_pass(cs%pass_MEKE, g%Domain)
882 call cpu_clock_end(cs%id_clock_pass)
883
884 ! Calculate diffusivity for main model to use
885 if (cs%MEKE_KhCoeff>0.) then
886 if (.not.cs%MEKE_GEOMETRIC) then
887 if (cs%use_old_lscale) then
888 if (cs%Rd_as_max_scale) then
889 !$OMP parallel do default(shared)
890 do j=js,je ; do i=is,ie
891 meke%Kh(i,j) = (cs%MEKE_KhCoeff * &
892 sqrt(2.*max(0.,barotrfac2(i,j)*meke%MEKE(i,j))*g%areaT(i,j)) ) * &
893 min(meke%Rd_dx_h(i,j), 1.0)
894 enddo ; enddo
895 else
896 !$OMP parallel do default(shared)
897 do j=js,je ; do i=is,ie
898 meke%Kh(i,j) = cs%MEKE_KhCoeff * &
899 sqrt(2.*max(0., barotrfac2(i,j)*meke%MEKE(i,j))*g%areaT(i,j))
900 enddo ; enddo
901 endif
902 else
903 !$OMP parallel do default(shared)
904 do j=js,je ; do i=is,ie
905 meke%Kh(i,j) = cs%MEKE_KhCoeff * &
906 sqrt(2.*max(0., barotrfac2(i,j)*meke%MEKE(i,j))) * lmixscale(i,j)
907 enddo ; enddo
908 endif
909 endif
910 endif
911
912 ! Calculate viscosity for the main model to use
913 if (cs%viscosity_coeff_Ku /=0.) then
914 do j=js,je ; do i=is,ie
915 meke%Ku(i,j) = cs%viscosity_coeff_Ku * sqrt(2.*max(0.,meke%MEKE(i,j))) * lmixscale(i,j)
916 enddo ; enddo
917 endif
918
919 if (cs%viscosity_coeff_Au /=0.) then
920 do j=js,je ; do i=is,ie
921 meke%Au(i,j) = cs%viscosity_coeff_Au * sqrt(2.*max(0.,meke%MEKE(i,j))) * lmixscale(i,j)**3
922 enddo ; enddo
923 endif
924
925 if (allocated(meke%Kh) .or. allocated(meke%Ku) .or. allocated(meke%Au) &
926 .or. allocated(meke%Le)) then
927 call cpu_clock_begin(cs%id_clock_pass)
928 call do_group_pass(cs%pass_Kh, g%Domain)
929 call cpu_clock_end(cs%id_clock_pass)
930 endif
931
932 ! Offer fields for averaging.
933 if (any([cs%id_Ue, cs%id_Ub, cs%id_Ut] > 0)) &
934 tmp(:,:) = 0.
935 if (cs%id_MEKE>0) call post_data(cs%id_MEKE, meke%MEKE, cs%diag)
936 if (cs%id_Ue>0) then
937 do j=js,je ; do i=is,ie
938 tmp(i,j) = sqrt(max(0., 2. * meke%MEKE(i,j)))
939 enddo ; enddo
940 call post_data(cs%id_Ue, tmp, cs%diag)
941 endif
942 if (cs%id_Ub>0) then
943 do j=js,je ; do i=is,ie
944 tmp(i,j) = sqrt(max(0., 2. * meke%MEKE(i,j) * bottomfac2(i,j)))
945 enddo ; enddo
946 call post_data(cs%id_Ub, tmp, cs%diag)
947 endif
948 if (cs%id_Ut>0) then
949 do j=js,je ; do i=is,ie
950 tmp(i,j) = sqrt(max(0., 2. * meke%MEKE(i,j) * barotrfac2(i,j)))
951 enddo ; enddo
952 call post_data(cs%id_Ut, tmp, cs%diag)
953 endif
954 if (cs%id_Kh>0) call post_data(cs%id_Kh, meke%Kh, cs%diag)
955 if (cs%id_Ku>0) call post_data(cs%id_Ku, meke%Ku, cs%diag)
956 if (cs%id_Au>0) call post_data(cs%id_Au, meke%Au, cs%diag)
957 if (cs%id_KhMEKE_u>0) call post_data(cs%id_KhMEKE_u, kh_u, cs%diag)
958 if (cs%id_KhMEKE_v>0) call post_data(cs%id_KhMEKE_v, kh_v, cs%diag)
959 if (cs%id_src>0) call post_data(cs%id_src, src, cs%diag)
960 if (cs%id_src_adv>0) call post_data(cs%id_src_adv, src_adv, cs%diag)
961 if (cs%id_src_mom_K4>0) call post_data(cs%id_src_mom_K4, src_mom_k4, cs%diag)
962 if (cs%id_src_btm_drag>0) call post_data(cs%id_src_btm_drag, src_btm_drag, cs%diag)
963 if (cs%id_src_GM>0) call post_data(cs%id_src_GM, src_gm, cs%diag)
964 if (cs%id_src_mom_lp>0) call post_data(cs%id_src_mom_lp, src_mom_lp, cs%diag)
965 if (cs%id_src_mom_bh>0) call post_data(cs%id_src_mom_bh, src_mom_bh, cs%diag)
966 if (cs%id_decay>0) call post_data(cs%id_decay, meke_decay, cs%diag)
967 if (cs%id_GM_src>0) call post_data(cs%id_GM_src, meke%GM_src, cs%diag)
968 if (cs%id_mom_src>0) call post_data(cs%id_mom_src, meke%mom_src, cs%diag)
969 if (cs%id_mom_src_bh>0) call post_data(cs%id_mom_src_bh, meke%mom_src_bh, cs%diag)
970 if (cs%id_GME_snk>0) call post_data(cs%id_GME_snk, meke%GME_snk, cs%diag)
971 if (cs%id_Le>0) call post_data(cs%id_Le, lmixscale, cs%diag)
972 if (cs%id_gamma_b>0) then
973 do j=js,je ; do i=is,ie
974 bottomfac2(i,j) = sqrt(bottomfac2(i,j))
975 enddo ; enddo
976 call post_data(cs%id_gamma_b, bottomfac2, cs%diag)
977 endif
978 if (cs%id_gamma_t>0) then
979 do j=js,je ; do i=is,ie
980 barotrfac2(i,j) = sqrt(barotrfac2(i,j))
981 enddo ; enddo
982 call post_data(cs%id_gamma_t, barotrfac2, cs%diag)
983 endif
984
985end subroutine step_forward_meke
986
987!> Calculates the equilibrium solution where the source depends only on MEKE diffusivity
988!! and there is no lateral diffusion of MEKE.
989!! Results is in MEKE%MEKE.
990subroutine meke_equilibrium(CS, MEKE, G, GV, US, SN_u, SN_v, drag_rate_visc, I_mass, depth_tot)
991 type(ocean_grid_type), intent(inout) :: g !< Ocean grid.
992 type(verticalgrid_type), intent(in) :: GV !< Ocean vertical grid structure.
993 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
994 type(meke_cs), intent(in) :: CS !< MEKE control structure.
995 type(meke_type), intent(inout) :: MEKE !< MEKE fields
996 real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: SN_u !< Eady growth rate at u-points [T-1 ~> s-1].
997 real, dimension(SZI_(G),SZJB_(G)), intent(in) :: SN_v !< Eady growth rate at v-points [T-1 ~> s-1].
998 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: drag_rate_visc !< Mean flow velocity contribution
999 !! to the MEKE drag rate [H T-1 ~> m s-1 or kg m-2 s-1]
1000 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: I_mass !< Inverse of column mass [R-1 Z-1 ~> m2 kg-1].
1001 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: depth_tot !< The thickness of the water column [H ~> m or kg m-2].
1002
1003 ! Local variables
1004 real :: beta ! Combined topographic and planetary vorticity gradient [T-1 L-1 ~> s-1 m-1]
1005 real :: SN ! The local Eady growth rate [T-1 ~> s-1]
1006 real :: bottomFac2, barotrFac2 ! Vertical structure factors [nondim]
1007 real :: LmixScale, LRhines, LEady ! Various mixing length scales [L ~> m]
1008 real :: KhCoeff ! A copy of MEKE_KhCoeff from the control structure [nondim]
1009 real :: Kh ! A lateral diffusivity [L2 T-1 ~> m2 s-1]
1010 real :: Ubg2 ! Background (tidal?) velocity squared [L2 T-2 ~> m2 s-2]
1011 real :: cd2 ! The square of the drag coefficient times unit conversion factors [H2 L-2 ~> nondim or kg2 m-6]
1012 real :: drag_rate ! The MEKE spindown timescale due to bottom drag [T-1 ~> s-1].
1013 real :: src ! The sum of MEKE sources [L2 T-3 ~> W kg-1]
1014 real :: ldamping ! The MEKE damping rate [T-1 ~> s-1].
1015 real :: EKE, EKEmin, EKEmax, EKEerr ! [L2 T-2 ~> m2 s-2]
1016 real :: resid, ResMin, ResMax ! Residuals [L2 T-3 ~> W kg-1]
1017 real :: FatH ! Coriolis parameter at h points, used to compute topographic beta [T-1 ~> s-1]
1018 real :: beta_topo_x, beta_topo_y ! Topographic PV gradients in x and y [T-1 L-1 ~> s-1 m-1]
1019 real :: h_neglect ! A negligible thickness [H ~> m or kg m-2]
1020 integer :: i, j, is, ie, js, je, n1, n2
1021 real :: tolerance ! Width of EKE bracket [L2 T-2 ~> m2 s-2].
1022 logical :: useSecant, debugIteration
1023
1024 is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
1025
1026 debugiteration = .false.
1027 khcoeff = cs%MEKE_KhCoeff
1028 ubg2 = cs%MEKE_Uscale**2
1029 cd2 = cs%cdrag**2
1030 tolerance = 1.0e-12*us%m_s_to_L_T**2
1031 h_neglect = gv%H_subroundoff
1032
1033!$OMP do
1034 do j=js,je ; do i=is,ie
1035 ! SN = 0.25*max( (SN_u(I,j) + SN_u(I-1,j)) + (SN_v(i,J) + SN_v(i,J-1)), 0.)
1036 ! This avoids extremes values in equilibrium solution due to bad values in SN_u, SN_v
1037 sn = min(sn_u(i,j), sn_u(i-1,j), sn_v(i,j), sn_v(i,j-1))
1038
1039 if (cs%MEKE_equilibrium_alt) then
1040 meke%MEKE(i,j) = (cs%MEKE_GEOMETRIC_alpha * sn * depth_tot(i,j))**2 / cd2
1041 else
1042 fath = 0.25*((g%CoriolisBu(i,j) + g%CoriolisBu(i-1,j-1)) + &
1043 (g%CoriolisBu(i-1,j) + g%CoriolisBu(i,j-1))) ! Coriolis parameter at h points
1044
1045 ! Since zero-bathymetry cells are masked, this avoids calculations on land
1046 if (cs%MEKE_topographic_beta == 0. .or. (depth_tot(i,j) == 0.0)) then
1047 beta_topo_x = 0. ; beta_topo_y = 0.
1048 else
1049 !### Consider different combinations of these estimates of topographic beta.
1050 beta_topo_x = -cs%MEKE_topographic_beta * fath * 0.5 * ( &
1051 (depth_tot(i+1,j)-depth_tot(i,j)) * g%IdxCu(i,j) &
1052 / max(depth_tot(i+1,j), depth_tot(i,j), h_neglect) &
1053 + (depth_tot(i,j)-depth_tot(i-1,j)) * g%IdxCu(i-1,j) &
1054 / max(depth_tot(i,j), depth_tot(i-1,j), h_neglect) )
1055 beta_topo_y = -cs%MEKE_topographic_beta * fath * 0.5 * ( &
1056 (depth_tot(i,j+1)-depth_tot(i,j)) * g%IdyCv(i,j) &
1057 / max(depth_tot(i,j+1), depth_tot(i,j), h_neglect) + &
1058 (depth_tot(i,j)-depth_tot(i,j-1)) * g%IdyCv(i,j-1) &
1059 / max(depth_tot(i,j), depth_tot(i,j-1), h_neglect) )
1060 endif
1061 beta = sqrt(((g%dF_dx(i,j) + beta_topo_x)**2) + &
1062 ((g%dF_dy(i,j) + beta_topo_y)**2) )
1063
1064 if (khcoeff*sn*i_mass(i,j)>0.) then
1065 ! Solve resid(E) = 0, where resid = Kh(E) * (SN)^2 - damp_rate(E) E
1066 ekemin = 0. ! Use the trivial root as the left bracket
1067 resmin = 0. ! Need to detect direction of left residual
1068 ekemax = 0.01*us%m_s_to_L_T**2 ! First guess at right bracket
1069 usesecant = .false. ! Start using a bisection method
1070
1071 ! First find right bracket for which resid<0
1072 resid = 1.0*us%m_to_L**2*us%T_to_s**3 ; n1 = 0
1073 do while (resid>0.)
1074 n1 = n1 + 1
1075 eke = ekemax
1076 call meke_lengthscales_0d(cs, us, g%areaT(i,j), beta, depth_tot(i,j), &
1077 meke%Rd_dx_h(i,j), sn, eke, &
1078 bottomfac2, barotrfac2, lmixscale, lrhines, leady)
1079 ! TODO: Should include resolution function in Kh
1080 kh = (khcoeff * sqrt(2.*barotrfac2*eke) * lmixscale)
1081 src = kh * (sn * sn)
1082 drag_rate = (gv%H_to_RZ * i_mass(i,j)) * sqrt(drag_rate_visc(i,j)**2 + cd2 * ( 2.0*bottomfac2*eke + ubg2 ) )
1083 ldamping = cs%MEKE_damping + drag_rate * bottomfac2
1084 resid = src - ldamping * eke
1085 ! if (debugIteration) then
1086 ! write(0,*) n1, 'EKE=',EKE,'resid=',resid
1087 ! write(0,*) 'EKEmin=',EKEmin,'ResMin=',ResMin
1088 ! write(0,*) 'src=',src,'ldamping=',ldamping
1089 ! write(0,*) 'gamma-b=',bottomFac2,'gamma-t=',barotrFac2
1090 ! write(0,*) 'drag_visc=',drag_rate_visc(i,j),'Ubg2=',Ubg2
1091 ! endif
1092 if (resid>0.) then ! EKE is to the left of the root
1093 ekemin = eke ! so we move the left bracket here
1094 ekemax = 10. * eke ! and guess again for the right bracket
1095 if (resid<resmin) usesecant = .true.
1096 resmin = resid
1097 if (ekemax > 2.e17*us%m_s_to_L_T**2) then
1098 if (debugiteration) stop 'Something has gone very wrong'
1099 debugiteration = .true.
1100 resid = 1. ; n1 = 0
1101 ekemin = 0. ; resmin = 0.
1102 ekemax = 0.01*us%m_s_to_L_T**2
1103 usesecant = .false.
1104 endif
1105 endif
1106 enddo ! while(resid>0.) searching for right bracket
1107 resmax = resid
1108
1109 ! Bisect the bracket
1110 n2 = 0 ; ekeerr = ekemax - ekemin
1111 do while (ekeerr > tolerance)
1112 n2 = n2 + 1
1113 if (usesecant) then
1114 eke = ekemin + (ekemax - ekemin) * (resmin / (resmin - resmax))
1115 else
1116 eke = 0.5 * (ekemin + ekemax)
1117 endif
1118 ekeerr = min( eke-ekemin, ekemax-eke )
1119 ! TODO: Should include resolution function in Kh
1120 kh = (khcoeff * sqrt(2.*barotrfac2*eke) * lmixscale)
1121 src = kh * (sn * sn)
1122 drag_rate = (gv%H_to_RZ * i_mass(i,j)) * sqrt( drag_rate_visc(i,j)**2 + cd2 * ( 2.0*bottomfac2*eke + ubg2 ) )
1123 ldamping = cs%MEKE_damping + drag_rate * bottomfac2
1124 resid = src - ldamping * eke
1125 if (usesecant .and. resid>resmin) usesecant = .false.
1126 if (resid>0.) then ! EKE is to the left of the root
1127 ekemin = eke ! so we move the left bracket here
1128 if (resid<resmin) usesecant = .true.
1129 resmin = resid ! Save this for the secant method
1130 elseif (resid<0.) then ! EKE is to the right of the root
1131 ekemax = eke ! so we move the right bracket here
1132 resmax = resid ! Save this for the secant method
1133 else
1134 exit ! resid=0 => EKE is exactly at the root
1135 endif
1136 if (n2>200) stop 'Failing to converge?'
1137 enddo ! while(EKEmax-EKEmin>tolerance)
1138
1139 else
1140 eke = 0.
1141 endif
1142 meke%MEKE(i,j) = eke
1143 endif
1144 enddo ; enddo
1145
1146end subroutine meke_equilibrium
1147
1148
1149!< This subroutine calculates a new equilibrium value for MEKE at each time step. This is not copied into
1150!! MEKE%MEKE; rather, it is used as a restoring term to nudge MEKE%MEKE back to an equilibrium value
1151subroutine meke_equilibrium_restoring(CS, G, GV, US, SN_u, SN_v, depth_tot, &
1152 equilibrium_value)
1153 type(ocean_grid_type), intent(inout) :: G !< Ocean grid.
1154 type(verticalgrid_type), intent(in) :: GV !< Ocean vertical grid structure.
1155 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type.
1156 type(meke_cs), intent(in) :: CS !< MEKE control structure.
1157 real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: SN_u !< Eady growth rate at u-points [T-1 ~> s-1].
1158 real, dimension(SZI_(G),SZJB_(G)), intent(in) :: SN_v !< Eady growth rate at v-points [T-1 ~> s-1].
1159 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: depth_tot !< The thickness of the water column [H ~> m or kg m-2].
1160 real, dimension(SZI_(G),SZJ_(G)), intent(out) :: equilibrium_value
1161 !< Equilbrium value of MEKE to be calculated at each time step [L2 T-2 ~> m2 s-2]
1162
1163 ! Local variables
1164 real :: SN ! The local Eady growth rate [T-1 ~> s-1]
1165 integer :: i, j, is, ie, js, je ! local indices
1166 real :: cd2 ! The square of the drag coefficient [nondim]
1167
1168 is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
1169 cd2 = cs%cdrag**2
1170 equilibrium_value(:,:) = 0.0
1171
1172!$OMP do
1173 do j=js,je ; do i=is,ie
1174 ! SN = 0.25*max( (SN_u(I,j) + SN_u(I-1,j)) + (SN_v(i,J) + SN_v(i,J-1)), 0.)
1175 ! This avoids extremes values in equilibrium solution due to bad values in SN_u, SN_v
1176 sn = min(sn_u(i,j), sn_u(i-1,j), sn_v(i,j), sn_v(i,j-1))
1177 equilibrium_value(i,j) = (cs%MEKE_GEOMETRIC_alpha * sn * depth_tot(i,j))**2 / cd2
1178 enddo ; enddo
1179
1180 if (cs%id_MEKE_equilibrium>0) call post_data(cs%id_MEKE_equilibrium, equilibrium_value, cs%diag)
1181end subroutine meke_equilibrium_restoring
1182
1183!> Calculates the eddy mixing length scale and \f$\gamma_b\f$ and \f$\gamma_t\f$
1184!! functions that are ratios of either bottom or barotropic eddy energy to the
1185!! column eddy energy, respectively. See \ref section_MEKE_equations.
1186subroutine meke_lengthscales(CS, MEKE, G, GV, US, SN_u, SN_v, EKE, depth_tot, &
1187 bottomFac2, barotrFac2, LmixScale)
1188 type(meke_cs), intent(in) :: CS !< MEKE control structure.
1189 type(meke_type), intent(in) :: MEKE !< MEKE field
1190 type(ocean_grid_type), intent(inout) :: G !< Ocean grid.
1191 type(verticalgrid_type), intent(in) :: GV !< Ocean vertical grid structure.
1192 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1193 real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: SN_u !< Eady growth rate at u-points [T-1 ~> s-1].
1194 real, dimension(SZI_(G),SZJB_(G)), intent(in) :: SN_v !< Eady growth rate at v-points [T-1 ~> s-1].
1195 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: EKE !< Eddy kinetic energy [L2 T-2 ~> m2 s-2].
1196 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: depth_tot !< The thickness of the water column [H ~> m or kg m-2].
1197 real, dimension(SZI_(G),SZJ_(G)), intent(out) :: bottomFac2 !< gamma_b^2 [nondim]
1198 real, dimension(SZI_(G),SZJ_(G)), intent(out) :: barotrFac2 !< gamma_t^2 [nondim]
1199 real, dimension(SZI_(G),SZJ_(G)), intent(out) :: LmixScale !< Eddy mixing length [L ~> m].
1200 ! Local variables
1201 real, dimension(SZI_(G),SZJ_(G)) :: LRhines, LEady ! Possible mixing length scales [L ~> m]
1202 real :: beta ! Combined topographic and planetary vorticity gradient [T-1 L-1 ~> s-1 m-1]
1203 real :: SN ! The local Eady growth rate [T-1 ~> s-1]
1204 real :: FatH ! Coriolis parameter at h points [T-1 ~> s-1]
1205 real :: beta_topo_x, beta_topo_y ! Topographic PV gradients in x and y [T-1 L-1 ~> s-1 m-1]
1206 real :: h_neglect ! A negligible thickness [H ~> m or kg m-2]
1207 integer :: i, j, is, ie, js, je
1208
1209 is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
1210 h_neglect = gv%H_subroundoff
1211
1212!$OMP do
1213 do j=js,je ; do i=is,ie
1214 if (.not.cs%use_old_lscale) then
1215 if (cs%aEady > 0.) then
1216 sn = 0.25 * ( (sn_u(i,j) + sn_u(i-1,j)) + (sn_v(i,j) + sn_v(i,j-1)) )
1217 else
1218 sn = 0.
1219 endif
1220 fath = 0.25* ( ( g%CoriolisBu(i,j) + g%CoriolisBu(i-1,j-1) ) + &
1221 ( g%CoriolisBu(i-1,j) + g%CoriolisBu(i,j-1) ) ) ! Coriolis parameter at h points
1222
1223 ! If depth_tot is zero, then a division by zero FPE will be raised. In this
1224 ! case, we apply Adcroft's rule of reciprocals and set the term to zero.
1225 ! Since zero-bathymetry cells are masked, this should not affect values.
1226 if (cs%MEKE_topographic_beta == 0. .or. (depth_tot(i,j) == 0.0)) then
1227 beta_topo_x = 0. ; beta_topo_y = 0.
1228 else
1229 !### Consider different combinations of these estimates of topographic beta.
1230 beta_topo_x = -cs%MEKE_topographic_beta * fath * 0.5 * ( &
1231 (depth_tot(i+1,j)-depth_tot(i,j)) * g%IdxCu(i,j) &
1232 / max(depth_tot(i+1,j), depth_tot(i,j), h_neglect) &
1233 + (depth_tot(i,j)-depth_tot(i-1,j)) * g%IdxCu(i-1,j) &
1234 / max(depth_tot(i,j), depth_tot(i-1,j), h_neglect) )
1235 beta_topo_y = -cs%MEKE_topographic_beta * fath * 0.5 * ( &
1236 (depth_tot(i,j+1)-depth_tot(i,j)) * g%IdyCv(i,j) &
1237 / max(depth_tot(i,j+1), depth_tot(i,j), h_neglect) + &
1238 (depth_tot(i,j)-depth_tot(i,j-1)) * g%IdyCv(i,j-1) &
1239 / max(depth_tot(i,j), depth_tot(i,j-1), h_neglect) )
1240 endif
1241 beta = sqrt(((g%dF_dx(i,j) + beta_topo_x)**2) + &
1242 ((g%dF_dy(i,j) + beta_topo_y)**2) )
1243
1244 else
1245 beta = 0.
1246 endif
1247 ! Returns bottomFac2, barotrFac2 and LmixScale
1248 call meke_lengthscales_0d(cs, us, g%areaT(i,j), beta, depth_tot(i,j), &
1249 meke%Rd_dx_h(i,j), sn, meke%MEKE(i,j), &
1250 bottomfac2(i,j), barotrfac2(i,j), lmixscale(i,j), &
1251 lrhines(i,j), leady(i,j))
1252 enddo ; enddo
1253 if (cs%id_Lrhines>0) call post_data(cs%id_LRhines, lrhines, cs%diag)
1254 if (cs%id_Leady>0) call post_data(cs%id_LEady, leady, cs%diag)
1255
1256end subroutine meke_lengthscales
1257
1258!> Calculates the eddy mixing length scale and \f$\gamma_b\f$ and \f$\gamma_t\f$
1259!! functions that are ratios of either bottom or barotropic eddy energy to the
1260!! column eddy energy, respectively. See \ref section_MEKE_equations.
1261subroutine meke_lengthscales_0d(CS, US, area, beta, depth_tot, Rd_dx, SN, EKE, &
1262 bottomFac2, barotrFac2, LmixScale, Lrhines, Leady)
1263 type(meke_cs), intent(in) :: CS !< MEKE control structure.
1264 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1265 real, intent(in) :: area !< Grid cell area [L2 ~> m2]
1266 real, intent(in) :: beta !< Planetary beta = \f$ \nabla f\f$ [T-1 L-1 ~> s-1 m-1]
1267 real, intent(in) :: depth_tot !< The total thickness of the water column [H ~> m or kg m-2]
1268 real, intent(in) :: Rd_dx !< Resolution Ld/dx [nondim].
1269 real, intent(in) :: SN !< Eady growth rate [T-1 ~> s-1].
1270 real, intent(in) :: EKE !< Eddy kinetic energy [L2 T-2 ~> m2 s-2].
1271 real, intent(out) :: bottomFac2 !< gamma_b^2 [nondim]
1272 real, intent(out) :: barotrFac2 !< gamma_t^2 [nondim]
1273 real, intent(out) :: LmixScale !< Eddy mixing length [L ~> m].
1274 real, intent(out) :: Lrhines !< Rhines length scale [L ~> m].
1275 real, intent(out) :: Leady !< Eady length scale [L ~> m].
1276 ! Local variables
1277 real :: Lgrid, Ldeform, Lfrict ! Length scales [L ~> m]
1278 real :: Ue ! An eddy velocity [L T-1 ~> m s-1]
1279
1280 ! Length scale for MEKE derived diffusivity
1281 lgrid = sqrt(area) ! Grid scale
1282 ldeform = lgrid * rd_dx ! Deformation scale
1283 lfrict = depth_tot / cs%cdrag ! Frictional arrest scale
1284 ! gamma_b^2 is the ratio of bottom eddy energy to mean column eddy energy
1285 ! used in calculating bottom drag
1286 bottomfac2 = cs%MEKE_CD_SCALE**2
1287 if (lfrict*cs%MEKE_Cb>0.) bottomfac2 = bottomfac2 + 1./( 1. + cs%MEKE_Cb*(ldeform/lfrict) )**0.8
1288 bottomfac2 = max(bottomfac2, cs%MEKE_min_gamma)
1289 ! gamma_t^2 is the ratio of barotropic eddy energy to mean column eddy energy
1290 ! used in the velocity scale for diffusivity
1291 barotrfac2 = 1.
1292 if (lfrict*cs%MEKE_Ct>0.) barotrfac2 = 1. / ( 1. + cs%MEKE_Ct*(ldeform/lfrict) )**0.25
1293 barotrfac2 = max(barotrfac2, cs%MEKE_min_gamma)
1294 if (cs%use_old_lscale) then
1295 if (cs%Rd_as_max_scale) then
1296 lmixscale = min(ldeform, lgrid) ! The smaller of Ld or dx
1297 else
1298 lmixscale = lgrid
1299 endif
1300 else
1301 ue = sqrt( 2.0 * max( 0., barotrfac2*eke ) ) ! Barotropic eddy flow scale
1302 lrhines = sqrt( ue / max( beta, 1.e-30*us%T_to_s*us%L_to_m ) ) ! Rhines scale
1303 if (cs%aEady > 0.) then
1304 leady = ue / max( sn, 1.e-15*us%T_to_s ) ! Bound Eady time-scale < 1e15 seconds
1305 else
1306 leady = 0.
1307 endif
1308 if (cs%use_min_lscale) then
1309 lmixscale = cs%lscale_maxval
1310 if (cs%aDeform*ldeform > 0.) lmixscale = min(lmixscale,cs%aDeform*ldeform)
1311 if (cs%aFrict *lfrict > 0.) lmixscale = min(lmixscale,cs%aFrict *lfrict)
1312 if (cs%aRhines*lrhines > 0.) lmixscale = min(lmixscale,cs%aRhines*lrhines)
1313 if (cs%aEady *leady > 0.) lmixscale = min(lmixscale,cs%aEady *leady)
1314 if (cs%aGrid *lgrid > 0.) lmixscale = min(lmixscale,cs%aGrid *lgrid)
1315 if (cs%Lfixed > 0.) lmixscale = min(lmixscale,cs%Lfixed)
1316 else
1317 lmixscale = 0.
1318 if (cs%aDeform*ldeform > 0.) lmixscale = lmixscale + 1./(cs%aDeform*ldeform)
1319 if (cs%aFrict *lfrict > 0.) lmixscale = lmixscale + 1./(cs%aFrict *lfrict)
1320 if (cs%aRhines*lrhines > 0.) lmixscale = lmixscale + 1./(cs%aRhines*lrhines)
1321 if (cs%aEady *leady > 0.) lmixscale = lmixscale + 1./(cs%aEady *leady)
1322 if (cs%aGrid *lgrid > 0.) lmixscale = lmixscale + 1./(cs%aGrid *lgrid)
1323 if (cs%Lfixed > 0.) lmixscale = lmixscale + 1./cs%Lfixed
1324 if (lmixscale > 0.) lmixscale = 1. / lmixscale
1325 endif
1326 endif
1327
1328end subroutine meke_lengthscales_0d
1329
1330!> Initializes the MOM_MEKE module and reads parameters.
1331!! Returns True if module is to be used, otherwise returns False.
1332logical function meke_init(Time, G, GV, US, param_file, diag, dbcomms_CS, CS, MEKE, restart_CS, meke_in_dynamics)
1333 type(time_type), intent(in) :: time !< The current model time.
1334 type(ocean_grid_type), intent(inout) :: g !< The ocean's grid structure.
1335 type(verticalgrid_type), intent(in) :: gv !< Ocean vertical grid structure.
1336 type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
1337 type(param_file_type), intent(in) :: param_file !< Parameter file parser structure.
1338 type(dbcomms_cs_type), intent(in) :: dbcomms_cs !< Database communications control structure
1339 type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics structure.
1340 type(meke_cs), intent(inout) :: cs !< MEKE control structure.
1341 type(meke_type), intent(inout) :: meke !< MEKE fields
1342 type(mom_restart_cs), intent(in) :: restart_cs !< MOM restart control structure
1343 logical, intent( out) :: meke_in_dynamics !< If true, MEKE is stepped forward in dynamics
1344 !! otherwise in tracer dynamics
1345
1346 ! Local variables
1347 real :: meke_restoring_timescale ! The timescale used to nudge MEKE toward its equilibrium value [T ~> s]
1348 real :: cdrag ! The default bottom drag coefficient [nondim].
1349 character(len=200) :: eke_filename, eke_varname, inputdir
1350 character(len=16) :: eke_source_str
1351 integer :: i, j, is, ie, js, je, isd, ied, jsd, jed
1352 logical :: laplacian, biharmonic, coldstart
1353 ! This include declares and sets the variable "version".
1354# include "version_variable.h"
1355 character(len=40) :: mdl = "MOM_MEKE" ! This module's name.
1356
1357 is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
1358 isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
1359
1360 ! Determine whether this module will be used
1361 call get_param(param_file, mdl, "USE_MEKE", meke_init, default=.false., do_not_log=.true.)
1362 call log_version(param_file, mdl, version, "", all_default=.not.meke_init)
1363 call get_param(param_file, mdl, "USE_MEKE", meke_init, &
1364 "If true, turns on the MEKE scheme which calculates "// &
1365 "a sub-grid mesoscale eddy kinetic energy budget.", &
1366 default=.false.)
1367 if (.not. meke_init) return
1368 cs%initialized = .true.
1369 call get_param(param_file, mdl, "MEKE_IN_DYNAMICS", meke_in_dynamics, &
1370 "If true, step MEKE forward with the dynamics "// &
1371 "otherwise with the tracer timestep.", &
1372 default=.true.)
1373
1374 call get_param(param_file, mdl, "EKE_SOURCE", eke_source_str, &
1375 "Determine the where EKE comes from:\n" // &
1376 " 'prog': Calculated solving EKE equation\n"// &
1377 " 'file': Read in from a file\n" // &
1378 " 'dbclient': Retrieved from ML-database", default='prog')
1379
1380 call mom_mesg("MEKE_init: reading parameters ", 5)
1381
1382 select case (lowercase(eke_source_str))
1383 case("file")
1384 cs%eke_src = eke_file
1385 call time_interp_external_init
1386 call get_param(param_file, mdl, "EKE_FILE", eke_filename, &
1387 "A file in which to find the eddy kineteic energy variable.", &
1388 default="eke_file.nc")
1389 call get_param(param_file, mdl, "EKE_VARIABLE", eke_varname, &
1390 "The name of the eddy kinetic energy variable to read from "//&
1391 "EKE_FILE to use in MEKE.", &
1392 default="eke")
1393 call get_param(param_file, mdl, "INPUTDIR", inputdir, &
1394 "The directory in which all input files are found.", &
1395 default=".", do_not_log=.true.)
1396 inputdir = slasher(inputdir)
1397
1398 eke_filename = trim(inputdir) // trim(eke_filename)
1399 cs%eke_handle = init_external_field(eke_filename, eke_varname, domain=g%Domain%mpp_domain)
1400 case("prog")
1401 cs%eke_src = eke_prog
1402 ! Read all relevant parameters and write them to the model log.
1403 call get_param(param_file, mdl, "MEKE_DAMPING", cs%MEKE_damping, &
1404 "The local depth-independent MEKE dissipation rate.", &
1405 units="s-1", default=0.0, scale=us%T_to_s)
1406 call get_param(param_file, mdl, "MEKE_CD_SCALE", cs%MEKE_Cd_scale, &
1407 "The ratio of the bottom eddy velocity to the column mean "//&
1408 "eddy velocity, i.e. sqrt(2*MEKE). This should be less than 1 "//&
1409 "to account for the surface intensification of MEKE.", &
1410 units="nondim", default=0.)
1411 call get_param(param_file, mdl, "MEKE_CB", cs%MEKE_Cb, &
1412 "A coefficient in the expression for the ratio of bottom projected "//&
1413 "eddy energy and mean column energy (see Jansen et al. 2015).",&
1414 units="nondim", default=25.)
1415 call get_param(param_file, mdl, "MEKE_MIN_GAMMA2", cs%MEKE_min_gamma, &
1416 "The minimum allowed value of gamma_b^2.",&
1417 units="nondim", default=0.0001)
1418 call get_param(param_file, mdl, "MEKE_CT", cs%MEKE_Ct, &
1419 "A coefficient in the expression for the ratio of barotropic "//&
1420 "eddy energy and mean column energy (see Jansen et al. 2015).",&
1421 units="nondim", default=50.)
1422 call get_param(param_file, mdl, "MEKE_GMCOEFF", cs%MEKE_GMcoeff, &
1423 "The efficiency of the conversion of potential energy "//&
1424 "into MEKE by the thickness mixing parameterization. "//&
1425 "If MEKE_GMCOEFF is negative, this conversion is not "//&
1426 "used or calculated.", units="nondim", default=-1.0)
1427 call get_param(param_file, mdl, "MEKE_GEOMETRIC", cs%MEKE_GEOMETRIC, &
1428 "If MEKE_GEOMETRIC is true, uses the GM coefficient formulation "//&
1429 "from the GEOMETRIC framework (Marshall et al., 2012).", default=.false.)
1430 call get_param(param_file, mdl, "MEKE_GEOMETRIC_ALPHA", cs%MEKE_GEOMETRIC_alpha, &
1431 "The nondimensional coefficient governing the efficiency of the GEOMETRIC \n"//&
1432 "thickness diffusion.", units="nondim", default=0.05)
1433 call get_param(param_file, mdl, "MEKE_EQUILIBRIUM_ALT", cs%MEKE_equilibrium_alt, &
1434 "If true, use an alternative formula for computing the (equilibrium) "//&
1435 "initial value of MEKE.", default=.false.)
1436 call get_param(param_file, mdl, "MEKE_EQUILIBRIUM_RESTORING", cs%MEKE_equilibrium_restoring, &
1437 "If true, restore MEKE back to its equilibrium value, which is calculated at "//&
1438 "each time step.", default=.false.)
1439 if (cs%MEKE_equilibrium_restoring) then
1440 call get_param(param_file, mdl, "MEKE_RESTORING_TIMESCALE", meke_restoring_timescale, &
1441 "The timescale used to nudge MEKE toward its equilibrium value.", &
1442 units="s", default=1e6, scale=us%s_to_T)
1443 cs%MEKE_restoring_rate = 1.0 / meke_restoring_timescale
1444 endif
1445
1446 call get_param(param_file, mdl, "MEKE_FRCOEFF", cs%MEKE_FrCoeff, &
1447 "The efficiency of the conversion of mean energy into "//&
1448 "MEKE. If MEKE_FRCOEFF is negative, this conversion "//&
1449 "is not used or calculated.", units="nondim", default=-1.0)
1450 call get_param(param_file, mdl, "MEKE_BHFRCOEFF", cs%MEKE_bhFrCoeff, &
1451 "The efficiency of the conversion of mean energy into "//&
1452 "MEKE by the biharmonic dissipation. If MEKE_bhFRCOEFF is negative, this conversion "//&
1453 "is not used or calculated.", units="nondim", default=-1.0)
1454 call get_param(param_file, mdl, "MEKE_GMECOEFF", cs%MEKE_GMECoeff, &
1455 "The efficiency of the conversion of MEKE into mean energy "//&
1456 "by GME. If MEKE_GMECOEFF is negative, this conversion "//&
1457 "is not used or calculated.", units="nondim", default=-1.0)
1458 call get_param(param_file, mdl, "MEKE_BGSRC", cs%MEKE_BGsrc, &
1459 "A background energy source for MEKE.", &
1460 units="W kg-1", default=0.0, scale=us%m_to_L**2*us%T_to_s**3)
1461 call get_param(param_file, mdl, "MEKE_KH", cs%MEKE_Kh, &
1462 "A background lateral diffusivity of MEKE. "//&
1463 "Use a negative value to not apply lateral diffusion to MEKE.", &
1464 units="m2 s-1", default=-1.0, scale=us%m_to_L**2*us%T_to_s)
1465 call get_param(param_file, mdl, "MEKE_K4", cs%MEKE_K4, &
1466 "A lateral bi-harmonic diffusivity of MEKE. "//&
1467 "Use a negative value to not apply bi-harmonic diffusion to MEKE.", &
1468 units="m4 s-1", default=-1.0, scale=us%m_to_L**4*us%T_to_s)
1469 call get_param(param_file, mdl, "MEKE_DTSCALE", cs%MEKE_dtScale, &
1470 "A scaling factor to accelerate the time evolution of MEKE.", &
1471 units="nondim", default=1.0)
1472 call get_param(param_file, mdl, "MEKE_POSITIVE", cs%MEKE_positive, &
1473 "If true, it guarantees that MEKE will always be >= 0.", &
1474 default=.false.)
1475 case("dbclient")
1476 cs%eke_src = eke_dbclient
1477 call ml_meke_init(diag, g, us, time, param_file, dbcomms_cs, cs)
1478 case default
1479 call mom_error(fatal, "Invalid method selected for calculating EKE")
1480 end select
1481 ! GMM, make sure all parameters used to calculated MEKE are within the above if
1482
1483 call get_param(param_file, mdl, "MEKE_KHCOEFF", cs%MEKE_KhCoeff, &
1484 "A scaling factor in the expression for eddy diffusivity "//&
1485 "which is otherwise proportional to the MEKE velocity- "//&
1486 "scale times an eddy mixing-length. This factor "//&
1487 "must be >0 for MEKE to contribute to the thickness/ "//&
1488 "and tracer diffusivity in the rest of the model.", &
1489 units="nondim", default=1.0)
1490 call get_param(param_file, mdl, "MEKE_USCALE", cs%MEKE_Uscale, &
1491 "The background velocity that is combined with MEKE to "//&
1492 "calculate the bottom drag.", units="m s-1", default=0.0, scale=us%m_s_to_L_T)
1493 call get_param(param_file, mdl, "MEKE_GM_SRC_ALT", cs%GM_src_alt, &
1494 "If true, use the GM energy conversion form S^2*N^2*kappa rather "//&
1495 "than the streamfunction for the MEKE GM source term.", default=.false.)
1496 call get_param(param_file, mdl, "MEKE_MIN_DEPTH_TOT", cs%MEKE_min_depth_tot, &
1497 "The minimum total depth over which to distribute MEKE energy sources. "//&
1498 "When the total depth is less than this, the sources are scaled away.", &
1499 units="m", default=1.0, scale=gv%m_to_H, do_not_log=.not.cs%GM_src_alt)
1500 call get_param(param_file, mdl, "MEKE_VISC_DRAG", cs%visc_drag, &
1501 "If true, use the vertvisc_type to calculate the bottom "//&
1502 "drag acting on MEKE.", default=.true.)
1503 call get_param(param_file, mdl, "MEKE_KHTH_FAC", meke%KhTh_fac, &
1504 "A factor that maps MEKE%Kh to KhTh.", units="nondim", default=0.0)
1505 call get_param(param_file, mdl, "MEKE_KHTR_FAC", meke%KhTr_fac, &
1506 "A factor that maps MEKE%Kh to KhTr.", units="nondim", default=0.0)
1507 call get_param(param_file, mdl, "MEKE_KHMEKE_FAC", cs%KhMEKE_Fac, &
1508 "A factor that maps MEKE%Kh to Kh for MEKE itself.", &
1509 units="nondim", default=0.0)
1510 call get_param(param_file, mdl, "MEKE_OLD_LSCALE", cs%use_old_lscale, &
1511 "If true, use the old formula for length scale which is "//&
1512 "a function of grid spacing and deformation radius.", &
1513 default=.false.)
1514 call get_param(param_file, mdl, "MEKE_MIN_LSCALE", cs%use_min_lscale, &
1515 "If true, use a strict minimum of provided length scales "//&
1516 "rather than harmonic mean.", &
1517 default=.false.)
1518 call get_param(param_file, mdl, "MEKE_LSCALE_MAX_VAL", cs%lscale_maxval, &
1519 "The ceiling on the value of the MEKE length scale when MEKE_MIN_LSCALE=True. "//&
1520 "The default is the distance from the equator to the pole on Earth, as "//&
1521 "estimated by enlightenment era scientists, but should probably scale with RAD_EARTH.", &
1522 units="m", default=1.0e7, scale=us%m_to_L, do_not_log=.not.cs%use_min_lscale)
1523 call get_param(param_file, mdl, "MEKE_RD_MAX_SCALE", cs%Rd_as_max_scale, &
1524 "If true, the length scale used by MEKE is the minimum of "//&
1525 "the deformation radius or grid-spacing. Only used if "//&
1526 "MEKE_OLD_LSCALE=True", default=.false.)
1527 call get_param(param_file, mdl, "MEKE_VISCOSITY_COEFF_KU", cs%viscosity_coeff_Ku, &
1528 "If non-zero, is the scaling coefficient in the expression for "//&
1529 "viscosity used to parameterize harmonic lateral momentum mixing by "//&
1530 "unresolved eddies represented by MEKE. Can be negative to "//&
1531 "represent backscatter from the unresolved eddies.", &
1532 units="nondim", default=0.0)
1533 call get_param(param_file, mdl, "MEKE_VISCOSITY_COEFF_AU", cs%viscosity_coeff_Au, &
1534 "If non-zero, is the scaling coefficient in the expression for "//&
1535 "viscosity used to parameterize biharmonic lateral momentum mixing by "//&
1536 "unresolved eddies represented by MEKE. Can be negative to "//&
1537 "represent backscatter from the unresolved eddies.", &
1538 units="nondim", default=0.0)
1539 call get_param(param_file, mdl, "MEKE_FIXED_MIXING_LENGTH", cs%Lfixed, &
1540 "If positive, is a fixed length contribution to the expression "//&
1541 "for mixing length used in MEKE-derived diffusivity.", &
1542 units="m", default=0.0, scale=us%m_to_L)
1543 call get_param(param_file, mdl, "MEKE_FIXED_TOTAL_DEPTH", cs%fixed_total_depth, &
1544 "If true, use the nominal bathymetric depth as the estimate of the "//&
1545 "time-varying ocean depth. Otherwise base the depth on the total ocean mass "//&
1546 "per unit area.", default=.true.)
1547 call get_param(param_file, mdl, "MEKE_TOTAL_DEPTH_RHO", cs%rho_fixed_total_depth, &
1548 "A density used to translate the nominal bathymetric depth into an estimate "//&
1549 "of the total ocean mass per unit area when MEKE_FIXED_TOTAL_DEPTH is true.", &
1550 units="kg m-3", default=gv%Rho0*us%R_to_kg_m3, scale=us%kg_m3_to_R, &
1551 do_not_log=(gv%Boussinesq.or.(.not.cs%fixed_total_depth)))
1552
1553 call get_param(param_file, mdl, "MEKE_ALPHA_DEFORM", cs%aDeform, &
1554 "If positive, is a coefficient weighting the deformation scale "//&
1555 "in the expression for mixing length used in MEKE-derived diffusivity.", &
1556 units="nondim", default=0.0)
1557 call get_param(param_file, mdl, "MEKE_ALPHA_RHINES", cs%aRhines, &
1558 "If positive, is a coefficient weighting the Rhines scale "//&
1559 "in the expression for mixing length used in MEKE-derived diffusivity.", &
1560 units="nondim", default=0.0)
1561 call get_param(param_file, mdl, "MEKE_ALPHA_EADY", cs%aEady, &
1562 "If positive, is a coefficient weighting the Eady length scale "//&
1563 "in the expression for mixing length used in MEKE-derived diffusivity.", &
1564 units="nondim", default=0.0)
1565 call get_param(param_file, mdl, "MEKE_ALPHA_FRICT", cs%aFrict, &
1566 "If positive, is a coefficient weighting the frictional arrest scale "//&
1567 "in the expression for mixing length used in MEKE-derived diffusivity.", &
1568 units="nondim", default=0.0)
1569 call get_param(param_file, mdl, "MEKE_ALPHA_GRID", cs%aGrid, &
1570 "If positive, is a coefficient weighting the grid-spacing as a scale "//&
1571 "in the expression for mixing length used in MEKE-derived diffusivity.", &
1572 units="nondim", default=0.0)
1573 call get_param(param_file, mdl, "MEKE_COLD_START", coldstart, &
1574 "If true, initialize EKE to zero. Otherwise a local equilibrium solution "//&
1575 "is used as an initial condition for EKE.", default=.false.)
1576 call get_param(param_file, mdl, "MEKE_BACKSCAT_RO_C", meke%backscatter_Ro_c, &
1577 "The coefficient in the Rossby number function for scaling the biharmonic "//&
1578 "frictional energy source. Setting to non-zero enables the Rossby number function.", &
1579 units="nondim", default=0.0)
1580 call get_param(param_file, mdl, "MEKE_BACKSCAT_RO_POW", meke%backscatter_Ro_pow, &
1581 "The power in the Rossby number function for scaling the biharmonic "//&
1582 "frictional energy source.", units="nondim", default=0.0)
1583 call get_param(param_file, mdl, "MEKE_ADVECTION_FACTOR", cs%MEKE_advection_factor, &
1584 "A scale factor in front of advection of eddy energy. Zero turns advection off. "//&
1585 "Using unity would be normal but other values could accommodate a mismatch "//&
1586 "between the advecting barotropic flow and the vertical structure of MEKE.", &
1587 units="nondim", default=0.0)
1588 call get_param(param_file, mdl, "MEKE_ADVECTION_BUG", cs%MEKE_advection_bug, &
1589 "If true, recover a bug in the calculation of the barotropic transport for "//&
1590 "the advection of MEKE. With the bug, only the transports in the deepest "//&
1591 "layer are used.", default=.false., do_not_log=(cs%MEKE_advection_factor<=0.))
1592 call get_param(param_file, mdl, "MEKE_TOPOGRAPHIC_BETA", cs%MEKE_topographic_beta, &
1593 "A scale factor to determine how much topographic beta is weighed in " //&
1594 "computing beta in the expression of Rhines scale. Use 1 if full "//&
1595 "topographic beta effect is considered; use 0 if it's completely ignored.", &
1596 units="nondim", default=0.0)
1597 call get_param(param_file, mdl, "SQG_USE_MEKE", cs%sqg_use_MEKE, &
1598 "If true, the eddy scale of MEKE is used for the SQG vertical structure ",&
1599 default=.false.)
1600
1601 ! Nonlocal module parameters
1602 call get_param(param_file, mdl, "CDRAG", cdrag, &
1603 "CDRAG is the drag coefficient relating the magnitude of the velocity "//&
1604 "field to the bottom stress.", units="nondim", default=0.003)
1605 call get_param(param_file, mdl, "MEKE_CDRAG", cs%cdrag, &
1606 "Drag coefficient relating the magnitude of the velocity "//&
1607 "field to the bottom stress in MEKE.", units="nondim", default=cdrag, scale=us%L_to_m*gv%m_to_H)
1608 call get_param(param_file, mdl, "LAPLACIAN", laplacian, default=.false., do_not_log=.true.)
1609 call get_param(param_file, mdl, "BIHARMONIC", biharmonic, default=.false., do_not_log=.true.)
1610
1611 if (cs%viscosity_coeff_Ku/=0. .and. .not. laplacian) call mom_error(fatal, &
1612 "LAPLACIAN must be true if MEKE_VISCOSITY_COEFF_KU is true.")
1613
1614 if (cs%viscosity_coeff_Au/=0. .and. .not. biharmonic) call mom_error(fatal, &
1615 "BIHARMONIC must be true if MEKE_VISCOSITY_COEFF_AU is true.")
1616
1617 call get_param(param_file, mdl, "DEBUG", cs%debug, default=.false., do_not_log=.true.)
1618
1619 ! Identify if any lateral diffusive processes are active
1620 cs%kh_flux_enabled = .false.
1621 if ((cs%MEKE_KH >= 0.0) .or. (cs%KhMEKE_FAC > 0.0) .or. (cs%MEKE_advection_factor > 0.0)) &
1622 cs%kh_flux_enabled = .true.
1623
1624! Register fields for output from this module.
1625 cs%diag => diag
1626 cs%id_MEKE = register_diag_field('ocean_model', 'MEKE', diag%axesT1, time, &
1627 'Mesoscale Eddy Kinetic Energy', 'm2 s-2', conversion=us%L_T_to_m_s**2)
1628 if (.not. allocated(meke%MEKE)) cs%id_MEKE = -1
1629 cs%id_Kh = register_diag_field('ocean_model', 'MEKE_KH', diag%axesT1, time, &
1630 'MEKE derived diffusivity', 'm2 s-1', conversion=us%L_to_m**2*us%s_to_T)
1631 if (.not. allocated(meke%Kh)) cs%id_Kh = -1
1632 cs%id_Ku = register_diag_field('ocean_model', 'MEKE_KU', diag%axesT1, time, &
1633 'MEKE derived lateral viscosity', 'm2 s-1', conversion=us%L_to_m**2*us%s_to_T)
1634 if (.not. allocated(meke%Ku)) cs%id_Ku = -1
1635 cs%id_Au = register_diag_field('ocean_model', 'MEKE_AU', diag%axesT1, time, &
1636 'MEKE derived lateral biharmonic viscosity', 'm4 s-1', conversion=us%L_to_m**4*us%s_to_T)
1637 if (.not. allocated(meke%Au)) cs%id_Au = -1
1638 cs%id_Ue = register_diag_field('ocean_model', 'MEKE_Ue', diag%axesT1, time, &
1639 'MEKE derived eddy-velocity scale', 'm s-1', conversion=us%L_T_to_m_s)
1640 if (.not. allocated(meke%MEKE)) cs%id_Ue = -1
1641 cs%id_Ub = register_diag_field('ocean_model', 'MEKE_Ub', diag%axesT1, time, &
1642 'MEKE derived bottom eddy-velocity scale', 'm s-1', conversion=us%L_T_to_m_s)
1643 if (.not. allocated(meke%MEKE)) cs%id_Ub = -1
1644 cs%id_Ut = register_diag_field('ocean_model', 'MEKE_Ut', diag%axesT1, time, &
1645 'MEKE derived barotropic eddy-velocity scale', 'm s-1', conversion=us%L_T_to_m_s)
1646 if (.not. allocated(meke%MEKE)) cs%id_Ut = -1
1647 cs%id_src = register_diag_field('ocean_model', 'MEKE_src', diag%axesT1, time, &
1648 'MEKE energy source', 'm2 s-3', conversion=(us%L_T_to_m_s**2)*us%s_to_T)
1649
1650 cs%id_src_adv = register_diag_field('ocean_model', 'MEKE_src_adv', diag%axesT1, time, &
1651 'MEKE energy source from the horizontal advection of MEKE', 'm2 s-3', conversion=(us%L_T_to_m_s**2)*us%s_to_T)
1652
1653 cs%id_src_btm_drag = register_diag_field('ocean_model', 'MEKE_src_btm_drag', diag%axesT1, time, &
1654 'MEKE energy source from the bottom drag acting on MEKE', 'm2 s-3', conversion=(us%L_T_to_m_s**2)*us%s_to_T)
1655
1656 if (cs%MEKE_K4 >= 0.) &
1657 cs%id_src_mom_K4 = register_diag_field('ocean_model', 'MEKE_src_mom_K4', &
1658 diag%axesT1, time, 'MEKE energy source from the biharmonic of MEKE', &
1659 'm2 s-3', conversion=(us%L_T_to_m_s**2)*us%s_to_T)
1660
1661 if (cs%MEKE_GMcoeff >= 0.) &
1662 cs%id_src_GM = register_diag_field('ocean_model', 'MEKE_src_GM', &
1663 diag%axesT1, time, 'MEKE energy source from the thickness mixing (GM scheme)', &
1664 'm2 s-3', conversion=(us%L_T_to_m_s**2)*us%s_to_T)
1665
1666 if (cs%MEKE_FrCoeff >= 0.) &
1667 cs%id_src_mom_lp = register_diag_field('ocean_model', 'MEKE_src_mom_lp', &
1668 diag%axesT1, time, 'MEKE energy source from the Laplacian of resolved flows', &
1669 'm2 s-3', conversion=(us%L_T_to_m_s**2)*us%s_to_T)
1670
1671 if (cs%MEKE_bhFrCoeff >= 0.) &
1672 cs%id_src_mom_bh = register_diag_field('ocean_model', 'MEKE_src_mom_bh', &
1673 diag%axesT1, time, 'MEKE energy source from the biharmonic of resolved flows', &
1674 'm2 s-3', conversion=(us%L_T_to_m_s**2)*us%s_to_T)
1675
1676 cs%id_decay = register_diag_field('ocean_model', 'MEKE_decay', diag%axesT1, time, &
1677 'MEKE decay rate', 's-1', conversion=us%s_to_T)
1678 cs%id_GM_src = register_diag_field('ocean_model', 'MEKE_GM_src', diag%axesT1, time, &
1679 'MEKE energy available from thickness mixing', &
1680 'W m-2', conversion=us%RZ3_T3_to_W_m2*us%L_to_Z**2)
1681 if (.not. allocated(meke%GM_src)) cs%id_GM_src = -1
1682 cs%id_mom_src = register_diag_field('ocean_model', 'MEKE_mom_src',diag%axesT1, time, &
1683 'MEKE energy available from momentum', &
1684 'W m-2', conversion=us%RZ3_T3_to_W_m2*us%L_to_Z**2)
1685 if (.not. allocated(meke%mom_src)) cs%id_mom_src = -1
1686 cs%id_mom_src_bh = register_diag_field('ocean_model', 'MEKE_mom_src_bh',diag%axesT1, time, &
1687 'MEKE energy available from the biharmonic dissipation of momentum', &
1688 'W m-2', conversion=us%RZ3_T3_to_W_m2*us%L_to_Z**2)
1689 if (.not. allocated(meke%mom_src_bh)) cs%id_mom_src_bh = -1
1690 cs%id_GME_snk = register_diag_field('ocean_model', 'MEKE_GME_snk',diag%axesT1, time, &
1691 'MEKE energy lost to GME backscatter', &
1692 'W m-2', conversion=us%RZ3_T3_to_W_m2*us%L_to_Z**2)
1693 if (.not. allocated(meke%GME_snk)) cs%id_GME_snk = -1
1694 cs%id_Le = register_diag_field('ocean_model', 'MEKE_Le', diag%axesT1, time, &
1695 'Eddy mixing length used in the MEKE derived eddy diffusivity', 'm', conversion=us%L_to_m)
1696 cs%id_Lrhines = register_diag_field('ocean_model', 'MEKE_Lrhines', diag%axesT1, time, &
1697 'Rhines length scale used in the MEKE derived eddy diffusivity', 'm', conversion=us%L_to_m)
1698 cs%id_Leady = register_diag_field('ocean_model', 'MEKE_Leady', diag%axesT1, time, &
1699 'Eady length scale used in the MEKE derived eddy diffusivity', 'm', conversion=us%L_to_m)
1700 cs%id_gamma_b = register_diag_field('ocean_model', 'MEKE_gamma_b', diag%axesT1, time, &
1701 'Ratio of bottom-projected eddy velocity to column-mean eddy velocity', 'nondim')
1702 cs%id_gamma_t = register_diag_field('ocean_model', 'MEKE_gamma_t', diag%axesT1, time, &
1703 'Ratio of barotropic eddy velocity to column-mean eddy velocity', 'nondim')
1704
1705 if (cs%kh_flux_enabled) then
1706 cs%id_KhMEKE_u = register_diag_field('ocean_model', 'KHMEKE_u', diag%axesCu1, time, &
1707 'Zonal diffusivity of MEKE', 'm2 s-1', conversion=us%L_to_m**2*us%s_to_T)
1708 cs%id_KhMEKE_v = register_diag_field('ocean_model', 'KHMEKE_v', diag%axesCv1, time, &
1709 'Meridional diffusivity of MEKE', 'm2 s-1', conversion=us%L_to_m**2*us%s_to_T)
1710 endif
1711
1712 if (cs%MEKE_equilibrium_restoring) then
1713 cs%id_MEKE_equilibrium = register_diag_field('ocean_model', 'MEKE_equilibrium', diag%axesT1, time, &
1714 'Equilibrated Mesoscale Eddy Kinetic Energy', 'm2 s-2', conversion=us%L_T_to_m_s**2)
1715 endif
1716
1717 cs%id_clock_pass = cpu_clock_id('(Ocean continuity halo updates)', grain=clock_routine)
1718
1719
1720 ! Detect whether this instance of MEKE_init() is at the beginning of a run
1721 ! or after a restart. If at the beginning, we will initialize MEKE to a local
1722 ! equilibrium.
1723 cs%initialize = .not.query_initialized(meke%MEKE, "MEKE", restart_cs)
1724 if (coldstart) cs%initialize = .false.
1725 if (cs%initialize) call mom_error(warning, &
1726 "MEKE_init: Initializing MEKE with a local equilibrium balance.")
1727 if (allocated(meke%Le)) then
1728 if (.not.query_initialized(meke%Le, "MEKE_Le", restart_cs)) then
1729 !$OMP parallel do default(shared)
1730 do j=js,je ; do i=is,ie
1731 meke%Le(i,j) = sqrt(g%areaT(i,j))
1732 enddo ; enddo
1733 endif
1734 endif
1735
1736 ! Set up group passes. In the case of a restart, these fields need a halo update now.
1737 if (allocated(meke%MEKE)) then
1738 call create_group_pass(cs%pass_MEKE, meke%MEKE, g%Domain)
1739 if (allocated(meke%Kh_diff)) call create_group_pass(cs%pass_MEKE, meke%Kh_diff, g%Domain)
1740 if (.not.cs%initialize) call do_group_pass(cs%pass_MEKE, g%Domain)
1741 endif
1742 if (allocated(meke%Kh)) call create_group_pass(cs%pass_Kh, meke%Kh, g%Domain)
1743 if (allocated(meke%Ku)) call create_group_pass(cs%pass_Kh, meke%Ku, g%Domain)
1744 if (allocated(meke%Au)) call create_group_pass(cs%pass_Kh, meke%Au, g%Domain)
1745 if (allocated(meke%Le)) call create_group_pass(cs%pass_Kh, meke%Le, g%Domain)
1746
1747 if (allocated(meke%Kh) .or. allocated(meke%Ku) .or. allocated(meke%Au) &
1748 .or. allocated(meke%Le)) &
1749 call do_group_pass(cs%pass_Kh, g%Domain)
1750
1751end function meke_init
1752
1753!> Initializer for the variant of MEKE that uses ML to predict eddy kinetic energy
1754subroutine ml_meke_init(diag, G, US, Time, param_file, dbcomms_CS, CS)
1755 type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics structure.
1756 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
1757 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1758 type(time_type), intent(in) :: Time !< The current model time.
1759 type(param_file_type), intent(in) :: param_file !< Parameter file parser structure.
1760 type(dbcomms_cs_type), intent(in) :: dbcomms_CS !< Control structure for database communication
1761 type(meke_cs), intent(inout) :: CS !< Control structure for this module
1762
1763 character(len=200) :: inputdir, backend, model_filename
1764 integer :: db_return_code, batch_size
1765 character(len=40) :: mdl = "MOM_ML_MEKE"
1766
1767 ! Store pointers in control structure
1768 write(cs%key_suffix, '(A,I6.6)') '_', pe_here()
1769 ! Put some basic information into the database
1770 db_return_code = 0
1771 db_return_code = cs%client%put_tensor("meta"//cs%key_suffix, &
1772 REAL([G%isd_global, G%idg_offset, G%jsd_global, G%jdg_offset]),[4]) + db_return_code
1773 db_return_code = cs%client%put_tensor("geolat"//cs%key_suffix, g%geoLatT, shape(g%geoLatT)) + db_return_code
1774 db_return_code = cs%client%put_tensor("geolon"//cs%key_suffix, g%geoLonT, shape(g%geoLonT)) + db_return_code
1775 db_return_code = cs%client%put_tensor("EKE_shape"//cs%key_suffix, shape(g%geolonT), [2]) + db_return_code
1776
1777 if (cs%client%SR_error_parser(db_return_code)) call mom_error(fatal, "Putting metadata into the database failed")
1778
1779 call read_param(param_file, "INPUTDIR", inputdir)
1780 inputdir = slasher(inputdir)
1781
1782 call get_param(param_file, mdl, "BATCH_SIZE", batch_size, "Batch size to use for inference", default=1)
1783 call get_param(param_file, mdl, "EKE_BACKEND", backend, &
1784 "The computational backend to use for EKE inference (CPU or GPU)", default="GPU")
1785 call get_param(param_file, mdl, "EKE_MODEL", model_filename, &
1786 "Filename of the a saved pyTorch model to use", fail_if_missing = .true.)
1787 call get_param(param_file, mdl, "EKE_MAX", cs%eke_max, &
1788 "Maximum value of EKE allowed when inferring EKE", &
1789 units="m2 s-2", default=2., scale=us%m_s_to_L_T**2)
1790
1791 ! Set the machine learning model
1792 if (dbcomms_cs%colocated) then
1793 if (modulo(pe_here(),dbcomms_cs%colocated_stride) == 0) then
1794 db_return_code = cs%client%set_model_from_file(cs%model_key, trim(inputdir)//trim(model_filename), &
1795 "TORCH", backend, batch_size=batch_size)
1796 endif
1797 else
1798 if (is_root_pe()) then
1799 db_return_code = cs%client%set_model_from_file(cs%model_key, trim(inputdir)//trim(model_filename), &
1800 "TORCH", backend, batch_size=batch_size)
1801 endif
1802 endif
1803 if (cs%client%SR_error_parser(db_return_code)) then
1804 call mom_error(fatal, "MEKE: set_model failed")
1805 endif
1806
1807 call get_param(param_file, mdl, "ONLINE_ANALYSIS", cs%online_analysis, &
1808 "If true, post EKE used in MOM6 to the database for analysis", default=.true.)
1809
1810 ! Set various clock ids
1811 cs%id_client_init = cpu_clock_id('(ML_MEKE client init)', grain=clock_routine)
1812 cs%id_put_tensor = cpu_clock_id('(ML_MEKE put tensor)', grain=clock_routine)
1813 cs%id_run_model = cpu_clock_id('(ML_MEKE run model)', grain=clock_routine)
1814 cs%id_unpack_tensor = cpu_clock_id('(ML_MEKE unpack tensor )', grain=clock_routine)
1815
1816 ! Diagnostics for ML_MEKE
1817 cs%id_mke = register_diag_field('ocean_model', 'MEKE_MKE', diag%axesT1, time, &
1818 'Surface mean (resolved) kinetic energy used in MEKE', 'm2 s-2', conversion=us%L_T_to_m_s**2)
1819 cs%id_slope_z= register_diag_field('ocean_model', 'MEKE_slope_z', diag%axesT1, time, &
1820 'Vertically averaged isopyncal slope magnitude used in MEKE', 'nondim', conversion=us%Z_to_L)
1821 cs%id_slope_x= register_diag_field('ocean_model', 'MEKE_slope_x', diag%axesCui, time, &
1822 'Isopycnal slope in the x-direction used in MEKE', 'nondim', conversion=us%Z_to_L)
1823 cs%id_slope_y= register_diag_field('ocean_model', 'MEKE_slope_y', diag%axesCvi, time, &
1824 'Isopycnal slope in the y-direction used in MEKE', 'nondim', conversion=us%Z_to_L)
1825 cs%id_rv = register_diag_field('ocean_model', 'MEKE_RV', diag%axesT1, time, &
1826 'Surface relative vorticity used in MEKE', 's-1', conversion=us%s_to_T)
1827
1828end subroutine ml_meke_init
1829
1830!> Calculate the various features used for the machine learning prediction
1831subroutine ml_meke_calculate_features(G, GV, US, CS, Rd_dx_h, u, v, tv, h, dt, features_array)
1832 type(ocean_grid_type), intent(inout) :: G !< Ocean grid
1833 type(verticalgrid_type), intent(in) :: GV !< Ocean vertical grid structure
1834 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1835 type(meke_cs), intent(in) :: CS !< Control structure for MEKE
1836 real, dimension(SZI_(G),SZJ_(G)), intent(in ) :: Rd_dx_h !< Rossby radius of deformation over
1837 !! the grid length scale [nondim]
1838 real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: u !< Zonal velocity [L T-1 ~> m s-1]
1839 real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: v !< Meridional velocity [L T-1 ~> m s-1]
1840 type(thermo_var_ptrs), intent(in) :: tv !< Type containing thermodynamic variables
1841 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> m or kg m-2].
1842 real, intent(in) :: dt !< Model(baroclinic) time-step [T ~> s].
1843 real(kind=real32), dimension(SIZE(h),num_features), intent( out) :: features_array
1844 !< The array of features needed for machine
1845 !! learning inference, with different units
1846 !! for the various subarrays [various]
1847
1848 real, dimension(SZI_(G),SZJ_(G)) :: mke ! Surface kinetic energy per unit mass [L2 T-2 ~> m2 s-2]
1849 real, dimension(SZI_(G),SZJ_(G)) :: slope_z ! Vertically averaged isoneutral slopes [Z L-1 ~> nondim]
1850 real, dimension(SZIB_(G),SZJB_(G)) :: rv_z ! Surface relative vorticity [T-1 ~> s-1]
1851 real, dimension(SZIB_(G),SZJB_(G)) :: rv_z_t ! Surface relative vorticity interpolated to tracer points [T-1 ~> s-1]
1852
1853 real, dimension(SZIB_(G),SZJ_(G), SZK_(G)) :: h_u ! Thickness at u point [H ~> m or kg m-2]
1854 real, dimension(SZI_(G),SZJB_(G), SZK_(G)) :: h_v ! Thickness at v point [H ~> m or kg m-2]
1855 real, dimension(SZIB_(G),SZJ_(G),SZK_(G)+1) :: slope_x ! Isoneutral slope at U point [Z L-1 ~> nondim]
1856 real, dimension(SZI_(G),SZJB_(G),SZK_(G)+1) :: slope_y ! Isoneutral slope at V point [Z L-1 ~> nondim]
1857 real, dimension(SZIB_(G),SZJ_(G)) :: slope_x_vert_avg ! Isoneutral slope at U point [Z L-1 ~> nondim]
1858 real, dimension(SZI_(G),SZJB_(G)) :: slope_y_vert_avg ! Isoneutral slope at V point [Z L-1 ~> nondim]
1859 real, dimension(SZI_(G), SZJ_(G), SZK_(G)+1) :: e ! The interface heights relative to mean sea level [Z ~> m].
1860 real :: slope_t ! Slope interpolated to thickness points [Z L-1 ~> nondim]
1861 real :: u_t, v_t ! u and v interpolated to thickness points [L T-1 ~> m s-1]
1862 real :: dvdx, dudy ! Components of relative vorticity [T-1 ~> s-1]
1863 real :: a_e, a_w, a_n, a_s ! Fractional areas of neighboring cells for interpolating velocities [nondim]
1864 real :: Idenom ! A normalizing factor in calculating weighted averages of areas [L-2 ~> m-2]
1865 real :: sum_area ! A sum of adjacent cell areas [L2 ~> m2]
1866
1867 integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz
1868
1869 is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = gv%ke
1870 isq = g%IscB ; ieq = g%IecB ; jsq = g%JscB ; jeq = g%JecB
1871
1872 ! Calculate various features for used to infer eddy kinetic energy
1873 ! Linear interpolation to estimate thickness at a velocity points
1874 do k=1,nz ; do j=js-1,je+1 ; do i=is-1,ie+1
1875 h_u(i,j,k) = 0.5*(h(i,j,k)*g%mask2dT(i,j) + h(i+1,j,k)*g%mask2dT(i+1,j)) + gv%Angstrom_H
1876 h_v(i,j,k) = 0.5*(h(i,j,k)*g%mask2dT(i,j) + h(i,j+1,k)*g%mask2dT(i,j+1)) + gv%Angstrom_H
1877 enddo ; enddo ; enddo
1878 call find_eta(h, tv, g, gv, us, e, halo_size=2)
1879 ! Note the hard-coded dimenisional constant in the following line.
1880 call calc_isoneutral_slopes(g, gv, us, h, e, tv, dt*1.e-7*gv%m2_s_to_HZ_T, .false., slope_x, slope_y)
1881 call pass_vector(slope_x, slope_y, g%Domain)
1882 do j=js-1,je+1 ; do i=is-1,ie+1
1883 slope_x_vert_avg(i,j) = vertical_average_interface(slope_x(i,j,:), h_u(i,j,:), gv%H_subroundoff)
1884 slope_y_vert_avg(i,j) = vertical_average_interface(slope_y(i,j,:), h_v(i,j,:), gv%H_subroundoff)
1885 enddo ; enddo
1886 slope_z(:,:) = 0.
1887
1888 call pass_vector(slope_x_vert_avg, slope_y_vert_avg, g%Domain)
1889 do j=js,je ; do i=is,ie
1890 ! Calculate weights for interpolation from velocity points to h points
1891 sum_area = g%areaCu(i-1,j) + g%areaCu(i,j)
1892 if (sum_area>0.0) then
1893 idenom = sqrt(0.5*g%IareaT(i,j) / sum_area)
1894 a_w = g%areaCu(i-1,j) * idenom
1895 a_e = g%areaCu(i,j) * idenom
1896 else
1897 a_w = 0.0 ; a_e = 0.0
1898 endif
1899
1900 sum_area = g%areaCv(i,j-1) + g%areaCv(i,j)
1901 if (sum_area>0.0) then
1902 idenom = sqrt(0.5*g%IareaT(i,j) / sum_area)
1903 a_s = g%areaCv(i,j-1) * idenom
1904 a_n = g%areaCv(i,j) * idenom
1905 else
1906 a_s = 0.0 ; a_n = 0.0
1907 endif
1908
1909 ! Calculate mean kinetic energy
1910 u_t = (a_e*u(i,j,1)) + (a_w*u(i-1,j,1))
1911 v_t = (a_n*v(i,j,1)) + (a_s*v(i,j-1,1))
1912 mke(i,j) = 0.5*( (u_t*u_t) + (v_t*v_t) )
1913
1914 ! Calculate the magnitude of the slope
1915 slope_t = slope_x_vert_avg(i,j)*a_e+slope_x_vert_avg(i-1,j)*a_w
1916 slope_z(i,j) = sqrt(slope_t*slope_t)
1917 slope_t = slope_y_vert_avg(i,j)*a_n+slope_y_vert_avg(i,j-1)*a_s
1918 slope_z(i,j) = 0.5*(slope_z(i,j) + sqrt(slope_t*slope_t))*g%mask2dT(i,j)
1919 enddo ; enddo
1920 call pass_var(slope_z, g%Domain)
1921
1922 ! Calculate relative vorticity
1923 do j=jsq-1,jeq+1 ; do i=isq-1,ieq+1
1924 dvdx = ((v(i+1,j,1)*g%dyCv(i+1,j)) - (v(i,j,1)*g%dyCv(i,j)))
1925 dudy = ((u(i,j+1,1)*g%dxCu(i,j+1)) - (u(i,j,1)*g%dxCu(i,j)))
1926 ! Assumed no slip
1927 rv_z(i,j) = (2.0-g%mask2dBu(i,j)) * (dvdx - dudy) * g%IareaBu(i,j)
1928 enddo ; enddo
1929 ! Interpolate RV to t-point, revisit this calculation to include metrics
1930 do j=js,je ; do i=is,ie
1931 rv_z_t(i,j) = 0.25*(rv_z(i-1,j) + rv_z(i,j) + rv_z(i-1,j-1) + rv_z(i,j-1))
1932 enddo ; enddo
1933
1934
1935 ! Construct the feature array
1936 features_array(:,mke_idx) = pack(mke,.true.)
1937 features_array(:,slope_z_idx) = pack(slope_z,.true.)
1938 features_array(:,rd_dx_z_idx) = pack(rd_dx_h,.true.)
1939 features_array(:,rv_idx) = pack(rv_z_t,.true.)
1940
1941 if (cs%id_rv>0) call post_data(cs%id_rv, rv_z, cs%diag)
1942 if (cs%id_mke>0) call post_data(cs%id_mke, mke, cs%diag)
1943 if (cs%id_slope_z>0) call post_data(cs%id_slope_z, slope_z, cs%diag)
1944 if (cs%id_slope_x>0) call post_data(cs%id_slope_x, slope_x, cs%diag)
1945 if (cs%id_slope_y>0) call post_data(cs%id_slope_y, slope_y, cs%diag)
1946end subroutine ml_meke_calculate_features
1947
1948!> Use the machine learning interface to predict EKE
1949subroutine predict_meke(G, US, CS, npts, Time, features_array, MEKE)
1950 type(ocean_grid_type), intent(inout) :: G !< Ocean grid
1951 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1952 type(meke_cs), intent(in ) :: CS !< Control structure for MEKE
1953 integer, intent(in ) :: npts !< Number of T-grid cells on the local
1954 !! domain
1955 type(time_type), intent(in ) :: Time !< The current model time
1956 real(kind=real32), dimension(npts,num_features), intent(in ) :: features_array
1957 !< The array of features needed for machine
1958 !! learning inference, with different units
1959 !! for the various subarrays [various]
1960 real, dimension(SZI_(G),SZJ_(G)), intent( out) :: MEKE !< Eddy kinetic energy [L2 T-2 ~> m2 s-2]
1961
1962 ! Local variables
1963 integer :: db_return_code
1964 character(len=255), dimension(1) :: model_out, model_in
1965 character(len=255) :: time_suffix
1966 real(kind=real32), dimension(SIZE(MEKE)) :: meke_vec ! A one-dimensional array of the natural log of eddy kinetic
1967 ! energy in mks units [m2 s-2]
1968 real, dimension(size(MEKE,1),size(MEKE,2)) :: ln_MEKE ! the natural log of eddy kinetic energy
1969 ! in mks units [m2 s-2]
1970 real, dimension(size(MEKE,1),size(MEKE,2)) :: MEKE_mks ! The eddy kinetic energy in mks units [m2 s-2]
1971 integer :: i, j, is, ie, js, je
1972
1973 is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
1974!> Use the database client to call a machine learning model to predict eddy kinetic energy
1975 call cpu_clock_begin(cs%id_put_tensor)
1976 db_return_code = cs%client%put_tensor("features"//cs%key_suffix, features_array, shape(features_array))
1977 call cpu_clock_end(cs%id_put_tensor)
1978
1979 ! Run the ML model to predict EKE and return the result
1980 model_out(1) = "EKE"//cs%key_suffix
1981 model_in(1) = "features"//cs%key_suffix
1982 call cpu_clock_begin(cs%id_run_model)
1983 db_return_code = cs%client%run_model(cs%model_key, model_in, model_out)
1984 call cpu_clock_end(cs%id_run_model)
1985 if (cs%client%SR_error_parser(db_return_code)) then
1986 call mom_error(fatal, "MEKE: run_model failed")
1987 endif
1988 call cpu_clock_begin(cs%id_unpack_tensor)
1989 db_return_code = cs%client%unpack_tensor( model_out(1), meke_vec, shape(meke_vec) )
1990 call cpu_clock_end(cs%id_unpack_tensor)
1991
1992 ln_meke = reshape(meke_vec, shape(meke))
1993 ! Zero out the halos. These will usually be reset by the pass_var in a few lines.
1994 meke_mks(:,:) = 0.0
1995 do j=js,je ; do i=is,ie
1996 meke_mks(i,j) = min(exp(ln_meke(i,j)), us%L_T_to_m_s**2*cs%eke_max)
1997 enddo ; enddo
1998 call pass_var(meke_mks, g%Domain, halo=1)
1999
2000 if (cs%online_analysis) then
2001 write(time_suffix,"(F16.0)") time_type_to_real(time)
2002 db_return_code = cs%client%put_tensor(trim("EKE_")//trim(adjustl(time_suffix))//cs%key_suffix, &
2003 meke_mks, shape(meke))
2004 endif
2005
2006 ! Copy MEKE_mks into the argument in rescaled units.
2007 ! MEKE(:,:) = 0.0 ! This would fill in the wider halos of this intent(out) array.
2008 do j=js-1,je+1 ; do i=is-1,ie+1
2009 meke(i,j) = us%m_s_to_L_T**2 * meke_mks(i,j)
2010 enddo ; enddo
2011
2012end subroutine predict_meke
2013
2014!> Compute average of interface quantities weighted by the thickness of the surrounding
2015!! layers [arbitrary]
2016real function vertical_average_interface(h, w, h_min)
2017
2018 real, dimension(:), intent(in) :: h !< Layer Thicknesses [H ~> m or kg m-2]
2019 real, dimension(:), intent(in) :: w !< Quantity to average [arbitrary]
2020 real, intent(in) :: h_min !< The vanishingly small layer thickness [H ~> m or kg m-2]
2021
2022 real :: htot ! Twice the sum of the layer thicknesses interpolated to interior interfaces [H ~> m or kg m-2]
2023 real :: inv_htot ! The inverse of htot [H-1 ~> m-1 or m2 kg-1]
2024 integer :: k, nk
2025
2026 nk = size(h)
2027 htot = h_min
2028 do k=2,nk
2029 htot = htot + (h(k-1)+h(k))
2030 enddo
2031 inv_htot = 1./htot
2032
2034 do k=2,nk
2035 vertical_average_interface = vertical_average_interface + (w(k)*(h(k-1)+h(k)))*inv_htot
2036 enddo
2037end function vertical_average_interface
2038
2039!> Allocates memory and register restart fields for the MOM_MEKE module.
2040subroutine meke_alloc_register_restart(HI, US, param_file, MEKE, restart_CS)
2041! Arguments
2042 type(hor_index_type), intent(in) :: hi !< Horizontal index structure
2043 type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
2044 type(param_file_type), intent(in) :: param_file !< Parameter file parser structure.
2045 type(meke_type), intent(inout) :: meke !< MEKE fields
2046 type(mom_restart_cs), intent(inout) :: restart_cs !< MOM restart control struct
2047
2048 ! Local variables
2049 real :: meke_gmcoeff, meke_frcoeff, meke_bhfrcoeff, meke_gmecoeff ! Coefficients for various terms [nondim]
2050 real :: meke_khcoeff, meke_visccoeff_ku, meke_visccoeff_au ! Coefficients for various terms [nondim]
2051 logical :: use_kh_in_meke
2052 logical :: usemeke
2053 logical :: sqg_use_meke
2054 integer :: isd, ied, jsd, jed
2055
2056! Determine whether this module will be used
2057 usemeke = .false. ; call read_param(param_file,"USE_MEKE",usemeke)
2058
2059! Read these parameters to determine what should be in the restarts
2060 meke_gmcoeff = -1. ; call read_param(param_file,"MEKE_GMCOEFF",meke_gmcoeff)
2061 meke_frcoeff = -1. ; call read_param(param_file,"MEKE_FRCOEFF",meke_frcoeff)
2062 meke_bhfrcoeff = -1. ; call read_param(param_file,"MEKE_bhFRCOEFF",meke_bhfrcoeff)
2063 meke_gmecoeff = -1. ; call read_param(param_file,"MEKE_GMECOEFF",meke_gmecoeff)
2064 meke_khcoeff = 1. ; call read_param(param_file,"MEKE_KHCOEFF",meke_khcoeff)
2065 meke_visccoeff_ku = 0. ; call read_param(param_file,"MEKE_VISCOSITY_COEFF_KU",meke_visccoeff_ku)
2066 meke_visccoeff_au = 0. ; call read_param(param_file,"MEKE_VISCOSITY_COEFF_AU",meke_visccoeff_au)
2067 use_kh_in_meke = .false. ; call read_param(param_file,"USE_KH_IN_MEKE", use_kh_in_meke)
2068 sqg_use_meke = .false. ; call read_param(param_file,"SQG_USE_MEKE", sqg_use_meke)
2069
2070 if (.not. usemeke) return
2071
2072! Allocate memory
2073 call mom_mesg("MEKE_alloc_register_restart: allocating and registering", 5)
2074 isd = hi%isd ; ied = hi%ied ; jsd = hi%jsd ; jed = hi%jed
2075 allocate(meke%MEKE(isd:ied,jsd:jed), source=0.0)
2076 call register_restart_field(meke%MEKE, "MEKE", .false., restart_cs, &
2077 longname="Mesoscale Eddy Kinetic Energy", units="m2 s-2", conversion=us%L_T_to_m_s**2)
2078
2079 if (meke_gmcoeff>=0.) allocate(meke%GM_src(isd:ied,jsd:jed), source=0.0)
2080 if (meke_frcoeff>=0. .or. meke_bhfrcoeff>=0. .or. meke_gmecoeff>=0.) &
2081 allocate(meke%mom_src(isd:ied,jsd:jed), source=0.0)
2082 if (meke_bhfrcoeff >= 0.) &
2083 allocate(meke%mom_src_bh(isd:ied,jsd:jed), source=0.0)
2084 if (meke_frcoeff<0.) meke_frcoeff = 0.
2085 if (meke_bhfrcoeff<0.) meke_bhfrcoeff = 0.
2086 if (meke_gmecoeff>=0.) allocate(meke%GME_snk(isd:ied,jsd:jed), source=0.0)
2087 if (meke_khcoeff>=0.) then
2088 allocate(meke%Kh(isd:ied,jsd:jed), source=0.0)
2089 call register_restart_field(meke%Kh, "MEKE_Kh", .false., restart_cs, &
2090 longname="Lateral diffusivity from Mesoscale Eddy Kinetic Energy", &
2091 units="m2 s-1", conversion=us%L_to_m**2*us%s_to_T)
2092 endif
2093 allocate(meke%Rd_dx_h(isd:ied,jsd:jed), source=0.0)
2094 if (meke_visccoeff_ku/=0.) then
2095 allocate(meke%Ku(isd:ied,jsd:jed), source=0.0)
2096 call register_restart_field(meke%Ku, "MEKE_Ku", .false., restart_cs, &
2097 longname="Lateral viscosity from Mesoscale Eddy Kinetic Energy", &
2098 units="m2 s-1", conversion=us%L_to_m**2*us%s_to_T)
2099 endif
2100 if (sqg_use_meke) then
2101 allocate(meke%Le(isd:ied,jsd:jed), source=0.0)
2102 call register_restart_field(meke%Le, "MEKE_Le", .false., restart_cs, &
2103 longname="Eddy length scale from Mesoscale Eddy Kinetic Energy", &
2104 units="m", conversion=us%L_to_m)
2105 endif
2106 if (use_kh_in_meke) then
2107 allocate(meke%Kh_diff(isd:ied,jsd:jed), source=0.0)
2108 call register_restart_field(meke%Kh_diff, "MEKE_Kh_diff", .false., restart_cs, &
2109 longname="Copy of thickness diffusivity for diffusing MEKE", &
2110 units="m2 s-1", conversion=us%L_to_m**2*us%s_to_T)
2111 endif
2112
2113 if (meke_visccoeff_au/=0.) then
2114 allocate(meke%Au(isd:ied,jsd:jed), source=0.0)
2115 call register_restart_field(meke%Au, "MEKE_Au", .false., restart_cs, &
2116 longname="Lateral biharmonic viscosity from Mesoscale Eddy Kinetic Energy", &
2117 units="m4 s-1", conversion=us%L_to_m**4*us%s_to_T)
2118 endif
2119
2120end subroutine meke_alloc_register_restart
2121
2122!> Deallocates any variables allocated in MEKE_alloc_register_restart.
2123subroutine meke_end(MEKE)
2124 type(meke_type), intent(inout) :: meke !< A structure with MEKE-related fields.
2125
2126 ! NOTE: MEKE will always be allocated by MEKE_init, even if MEKE is disabled.
2127 ! So these must all be conditional, even though MEKE%MEKE and MEKE%Rd_dx_h
2128 ! are always allocated (when MEKE is enabled)
2129
2130 if (allocated(meke%Au)) deallocate(meke%Au)
2131 if (allocated(meke%Kh_diff)) deallocate(meke%Kh_diff)
2132 if (allocated(meke%Ku)) deallocate(meke%Ku)
2133 if (allocated(meke%Rd_dx_h)) deallocate(meke%Rd_dx_h)
2134 if (allocated(meke%Kh)) deallocate(meke%Kh)
2135 if (allocated(meke%GME_snk)) deallocate(meke%GME_snk)
2136 if (allocated(meke%mom_src)) deallocate(meke%mom_src)
2137 if (allocated(meke%mom_src_bh)) deallocate(meke%mom_src_bh)
2138 if (allocated(meke%GM_src)) deallocate(meke%GM_src)
2139 if (allocated(meke%MEKE)) deallocate(meke%MEKE)
2140 if (allocated(meke%Le)) deallocate(meke%Le)
2141end subroutine meke_end
2142
2143!> \namespace mom_meke
2144!!
2145!! \section section_MEKE The Mesoscale Eddy Kinetic Energy (MEKE) framework
2146!!
2147!! The MEKE framework accounts for the mean potential energy removed by
2148!! the first order closures used to parameterize mesoscale eddies.
2149!! It requires closure at the second order, namely dissipation and transport
2150!! of eddy energy.
2151!!
2152!! Monitoring the sub-grid scale eddy energy budget provides a means to predict
2153!! a sub-grid eddy-velocity scale which can be used in the lower order closures.
2154!!
2155!! \subsection section_MEKE_equations MEKE equations
2156!!
2157!! The eddy kinetic energy equation is:
2158!! \f[ \partial_{\tilde{t}} E =
2159!! \overbrace{ \dot{E}_b + \gamma_\eta \dot{E}_\eta + \gamma_v \dot{E}_v
2160!! }^\text{sources}
2161!! - \overbrace{ ( \lambda + C_d | U_d | \gamma_b^2 ) E
2162!! }^\text{local dissipation}
2163!! + \overbrace{ \nabla \cdot ( ( \kappa_E + \gamma_M \kappa_M ) \nabla E
2164!! - \kappa_4 \nabla^3 E )
2165!! }^\text{smoothing}
2166!! \f]
2167!! where \f$ E \f$ is the eddy kinetic energy (variable <code>MEKE</code>) with units of
2168!! m<sup>2</sup>s<sup>-2</sup>,
2169!! and \f$\tilde{t} = a t\f$ is a scaled time. The non-dimensional factor
2170!! \f$ a\geq 1 \f$ is used to accelerate towards equilibrium.
2171!!
2172!! The MEKE equation is two-dimensional and obtained by depth averaging the
2173!! the three-dimensional eddy energy equation. In the following expressions
2174!! \f$ \left< \phi \right> = \frac{1}{H} \int^\eta_{-D} \phi \, dz \f$ maps
2175!! three dimensional terms into the two-dimensional quantities needed.
2176!!
2177!! \subsubsection section_MEKE_source_terms MEKE source terms
2178!!
2179!! The source term \f$ \dot{E}_b \f$ is a constant background source
2180!! of energy intended to avoid the limit \f$E\rightarrow 0\f$.
2181!!
2182!! The "GM" source term
2183!! \f[ \dot{E}_\eta = - \left< \overline{w^\prime b^\prime} \right>
2184!! = \left< \kappa_h N^2S^2 \right>
2185!! \approx \left< \kappa_h g\prime |\nabla_\sigma \eta|^2 \right>\f]
2186!! equals the mean potential energy removed by the Gent-McWilliams closure,
2187!! and is excluded/included in the MEKE budget by the efficiency parameter
2188!! \f$ \gamma_\eta \in [0,1] \f$.
2189!!
2190!! The "frictional" source term
2191!! \f[ \dot{E}_{v} = \left< \partial_i u_j \tau_{ij} \right> \f]
2192!! equals the mean kinetic energy removed by lateral viscous fluxes, and
2193!! is excluded/included in the MEKE budget by the efficiency parameter
2194!! \f$ \gamma_v \in [0,1] \f$.
2195!!
2196!! \subsubsection section_MEKE_dissipation_terms MEKE dissipation terms
2197!!
2198!! The local dissipation of \f$ E \f$ is parameterized through a linear
2199!! damping, \f$\lambda\f$, and bottom drag, \f$ C_d | U_d | \gamma_b^2 \f$.
2200!! The \f$ \gamma_b \f$ accounts for the weak projection of the column-mean
2201!! eddy velocity to the bottom. In other words, the bottom velocity is
2202!! estimated as \f$ \gamma_b U_e \f$.
2203!! The bottom drag coefficient, \f$ C_d \f$ is the same as that used in the bottom
2204!! friction in the mean model equations.
2205!!
2206!! The bottom drag velocity scale, \f$ U_d \f$, has contributions from the
2207!! resolved state and \f$ E \f$:
2208!! \f[ U_d = \sqrt{ U_b^2 + |u|^2_{z=-D} + |\gamma_b U_e|^2 } .\f]
2209!! where the eddy velocity scale, \f$ U_e \f$, is given by:
2210!! \f[ U_e = \sqrt{ 2 E } .\f]
2211!! \f$ U_b \f$ is a constant background bottom velocity scale and is
2212!! typically not used (i.e. set to zero).
2213!!
2214!! Following \cite jansen2015, the projection of eddy energy on to the bottom
2215!! is given by the ratio of bottom energy to column mean energy:
2216!! \f[
2217!! \gamma_b^2 = \frac{E_b}{E} = \gamma_{d0}
2218!! + \left( 1 + c_{b} \frac{L_d}{L_f} \right)^{-\frac{4}{5}}
2219!! ,
2220!! \f]
2221!! \f[
2222!! \gamma_b^2 \leftarrow \max{\left( \gamma_b^2, \gamma_{min}^2 \right)}
2223!! .
2224!! \f]
2225!!
2226!! \subsection section_MEKE_smoothing MEKE smoothing terms
2227!!
2228!! \f$ E \f$ is laterally diffused by a diffusivity \f$ \kappa_E + \gamma_M
2229!! \kappa_M \f$ where \f$ \kappa_E \f$ is a constant diffusivity and the term
2230!! \f$ \gamma_M \kappa_M \f$ is a "self diffusion" using the diffusivity
2231!! calculated in the section \ref section_MEKE_diffusivity.
2232!! \f$ \kappa_4 \f$ is a constant bi-harmonic diffusivity.
2233!!
2234!! \subsection section_MEKE_diffusivity Diffusivity derived from MEKE
2235!!
2236!! The predicted eddy velocity scale, \f$ U_e \f$, can be combined with a
2237!! mixing length scale to form a diffusivity.
2238!! The primary use of a MEKE derived diffusivity is for use in thickness
2239!! diffusion (module mom_thickness_diffuse) and optionally in along
2240!! isopycnal mixing of tracers (module mom_tracer_hor_diff).
2241!! The original form used (enabled with MEKE_OLD_LSCALE=True):
2242!!
2243!! \f[ \kappa_M = \gamma_\kappa \sqrt{ \gamma_t^2 U_e^2 A_\Delta } \f]
2244!!
2245!! where \f$ A_\Delta \f$ is the area of the grid cell.
2246!! Following \cite jansen2015, we now use
2247!!
2248!! \f[ \kappa_M = \gamma_\kappa l_M \sqrt{ \gamma_t^2 U_e^2 } \f]
2249!!
2250!! where \f$ \gamma_\kappa \in [0,1] \f$ is a non-dimensional factor and,
2251!! following \cite jansen2015, \f$\gamma_t^2\f$ is the ratio of barotropic
2252!! eddy energy to column mean eddy energy given by
2253!! \f[
2254!! \gamma_t^2 = \frac{E_t}{E} = \left( 1 + c_{t} \frac{L_d}{L_f} \right)^{-\frac{1}{4}}
2255!! ,
2256!! \f]
2257!! \f[
2258!! \gamma_t^2 \leftarrow \max{\left( \gamma_t^2, \gamma_{min}^2 \right)}
2259!! .
2260!! \f]
2261!!
2262!! The length-scale is a configurable combination of multiple length scales:
2263!!
2264!! \f[
2265!! l_M = \left(
2266!! \frac{\alpha_d}{L_d}
2267!! + \frac{\alpha_f}{L_f}
2268!! + \frac{\alpha_R}{L_R}
2269!! + \frac{\alpha_e}{L_e}
2270!! + \frac{\alpha_\Delta}{L_\Delta}
2271!! + \frac{\delta[L_c]}{L_c}
2272!! \right)^{-1}
2273!! \f]
2274!!
2275!! where
2276!!
2277!! \f{eqnarray*}{
2278!! L_d & = & \sqrt{\frac{c_g^2}{f^2+2\beta c_g}} \sim \frac{ c_g }{f} \\\\
2279!! L_R & = & \sqrt{\frac{U_e}{\beta^*}} \\\\
2280!! L_e & = & \frac{U_e}{|S| N} \\\\
2281!! L_f & = & \frac{H}{c_d} \\\\
2282!! L_\Delta & = & \sqrt{A_\Delta} .
2283!! \f}
2284!!
2285!! \f$L_c\f$ is a constant and \f$\delta[L_c]\f$ is the impulse function so that the term
2286!! \f$\frac{\delta[L_c]}{L_c}\f$ evaluates to \f$\frac{1}{L_c}\f$ when \f$L_c\f$ is non-zero
2287!! but is dropped if \f$L_c=0\f$.
2288!!
2289!! \f$\beta^*\f$ is the effective \f$\beta\f$ that combines both the planetary vorticity
2290!! gradient (i.e. \f$\beta=\nabla f\f$) and the topographic \f$\beta\f$ effect,
2291!! with the latter weighed by a weighting constant, \f$c_\beta\f$, that varies
2292!! from 0 to 1, so that \f$c_\beta=0\f$ means the topographic \f$\beta\f$ effect is ignored,
2293!! while \f$c_\beta=1\f$ means it is fully considered. The new \f$\beta^*\f$ therefore
2294!! takes the form of
2295!!
2296!! \f[
2297!! \beta^* = \sqrt{( \partial_xf - c_\beta\frac{f}{D}\partial_xD )^2 +
2298!! ( \partial_yf - c_\beta\frac{f}{D}\partial_yD )^2}
2299!! \f]
2300!! where \f$D\f$ is water column depth at T points.
2301!!
2302!! \subsection section_MEKE_viscosity Viscosity derived from MEKE
2303!!
2304!! As for \f$ \kappa_M \f$, the predicted eddy velocity scale can be
2305!! used to form a harmonic eddy viscosity,
2306!!
2307!! \f[ \kappa_u = \gamma_u \sqrt{ U_e^2 A_\Delta } \f]
2308!!
2309!! as well as a biharmonic eddy viscosity,
2310!!
2311!! \f[ \kappa_4 = \gamma_4 \sqrt{ U_e^2 A_\Delta^3 } \f]
2312!!
2313!! \subsection section_MEKE_limit_case Limit cases for local source-dissipative balance
2314!!
2315!! Note that in steady-state (or when \f$ a>>1 \f$) and there is no
2316!! diffusion of \f$ E \f$ then
2317!! \f[ \overline{E} \approx \frac{ \dot{E}_b + \gamma_\eta \dot{E}_\eta +
2318!! \gamma_v \dot{E}_v }{ \lambda + C_d|U_d|\gamma_b^2 } . \f]
2319!!
2320!! In the linear drag limit, where
2321!! \f$ U_e << \min(U_b, |u|_{z=-D}, C_d^{-1}\lambda) \f$, the equilibrium becomes
2322!! \f$ \overline{E} \approx \frac{ \dot{E}_b + \gamma_\eta \dot{E}_\eta +
2323!! \gamma_v \dot{E}_v }{ \lambda + C_d \sqrt{ U_b^2 + |u|^2_{z=-D} } } \f$.
2324!!
2325!! In the nonlinear drag limit, where \f$ U_e >> \max(U_b, |u|_{z=-D}, C_d^{-1}\lambda) \f$,
2326!! the equilibrium becomes
2327!! \f$ \overline{E} \approx \left( \frac{ \dot{E}_b + \gamma_\eta \dot{E}_\eta +
2328!! \gamma_v \dot{E}_v }{ \sqrt{2} C_d \gamma_b^3 } \right)^\frac{2}{3} \f$.
2329!!
2330!! \subsubsection section_MEKE_module_parameters MEKE module parameters
2331!!
2332!! | Symbol | Module parameter |
2333!! | ------ | --------------- |
2334!! | - | <code>USE_MEKE</code> |
2335!! | \f$ a \f$ | <code>MEKE_DTSCALE</code> |
2336!! | \f$ \dot{E}_b \f$ | <code>MEKE_BGSRC</code> |
2337!! | \f$ \gamma_\eta \f$ | <code>MEKE_GMCOEFF</code> |
2338!! | \f$ \gamma_v \f$ | <code>MEKE_FrCOEFF</code> |
2339!! | \f$ \lambda \f$ | <code>MEKE_DAMPING</code> |
2340!! | \f$ U_b \f$ | <code>MEKE_USCALE</code> |
2341!! | \f$ \gamma_{d0} \f$ | <code>MEKE_CD_SCALE</code> |
2342!! | \f$ c_{b} \f$ | <code>MEKE_CB</code> |
2343!! | \f$ c_{t} \f$ | <code>MEKE_CT</code> |
2344!! | \f$ \kappa_E \f$ | <code>MEKE_KH</code> |
2345!! | \f$ \kappa_4 \f$ | <code>MEKE_K4</code> |
2346!! | \f$ \gamma_\kappa \f$ | <code>MEKE_KHCOEFF</code> |
2347!! | \f$ \gamma_M \f$ | <code>MEKE_KHMEKE_FAC</code> |
2348!! | \f$ \gamma_u \f$ | <code>MEKE_VISCOSITY_COEFF_KU</code> |
2349!! | \f$ \gamma_4 \f$ | <code>MEKE_VISCOSITY_COEFF_AU</code> |
2350!! | \f$ \gamma_{min}^2 \f$| <code>MEKE_MIN_GAMMA2</code> |
2351!! | \f$ \alpha_d \f$ | <code>MEKE_ALPHA_DEFORM</code> |
2352!! | \f$ \alpha_f \f$ | <code>MEKE_ALPHA_FRICT</code> |
2353!! | \f$ \alpha_R \f$ | <code>MEKE_ALPHA_RHINES</code> |
2354!! | \f$ \alpha_e \f$ | <code>MEKE_ALPHA_EADY</code> |
2355!! | \f$ \alpha_\Delta \f$ | <code>MEKE_ALPHA_GRID</code> |
2356!! | \f$ L_c \f$ | <code>MEKE_FIXED_MIXING_LENGTH</code> |
2357!! | \f$ c_\beta \f$ | <code>MEKE_TOPOGRAPHIC_BETA</code> |
2358!! | - | <code>MEKE_KHTH_FAC</code> |
2359!! | - | <code>MEKE_KHTR_FAC</code> |
2360!!
2361!! | Symbol | Model parameter |
2362!! | ------ | --------------- |
2363!! | \f$ C_d \f$ | <code>CDRAG</code> |
2364!!
2365!! \subsection section_MEKE_references References
2366!!
2367!! Jansen, M. F., A. J. Adcroft, R. Hallberg, and I. M. Held, 2015: Parameterization of eddy fluxes based on a
2368!! mesoscale energy budget. Ocean Modelling, 92, 28--41, http://doi.org/10.1016/j.ocemod.2015.05.007 .
2369!!
2370!! Marshall, D. P., and A. J. Adcroft, 2010: Parameterization of ocean eddies: Potential vorticity mixing, energetics
2371!! and Arnold first stability theorem. Ocean Modelling, 32, 188--204, http://doi.org/10.1016/j.ocemod.2010.02.001 .
2372
2373end module mom_meke
2374