← back to index

src/parameterizations/lateral/MOM_MEKE.F90

portedportable, not yet portedexecuted, not portableexecutable, not hit by this run

1! This file is part of MOM6, the Modular Ocean Model version 6.
2! See the LICENSE file for licensing information.
3! SPDX-License-Identifier: Apache-2.0
4
5#include "do_concurrent_compat.h"
6
7!> Implements the Mesoscale Eddy Kinetic Energy framework
8!! with topographic beta effect included in computing beta in Rhines scale
9
10module MOM_MEKE
11
12use iso_fortran_env, only : real32
13
14use MOM_coms, only : PE_here
15use MOM_database_comms, only : dbclient_type, dbcomms_CS_type
16use MOM_debugging, only : hchksum, uvchksum
17use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, CLOCK_ROUTINE
18use MOM_diag_mediator, only : post_data, register_diag_field, safe_alloc_ptr
19use MOM_diag_mediator, only : diag_ctrl, time_type
20use MOM_domains, only : create_group_pass, do_group_pass, group_pass_type
21use MOM_domains, only : pass_vector, pass_var
22use MOM_error_handler, only : MOM_error, FATAL, WARNING, NOTE, MOM_mesg, is_root_pe
23use MOM_file_parser, only : read_param, get_param, log_version, param_file_type
24use MOM_grid, only : ocean_grid_type
25use MOM_hor_index, only : hor_index_type
26use MOM_interface_heights, only : find_eta
27use MOM_interpolate, only : init_external_field, time_interp_external
28use MOM_interpolate, only : time_interp_external_init
29use MOM_interpolate, only : external_field
30use MOM_io, only : vardesc, var_desc, slasher
31use MOM_isopycnal_slopes, only : calc_isoneutral_slopes
32use MOM_restart, only : MOM_restart_CS, register_restart_field, query_initialized
33use MOM_string_functions, only : lowercase
34use MOM_time_manager, only : time_type_to_real
35use MOM_unit_scaling, only : unit_scale_type
36use MOM_variables, only : vertvisc_type, thermo_var_ptrs
37use MOM_verticalGrid, only : verticalGrid_type
38use MOM_MEKE_types, only : MEKE_type
39
40implicit none ; private
41
42#include <MOM_memory.h>
43
44public step_forward_MEKE, MEKE_init, MEKE_alloc_register_restart, MEKE_end
45
46! Constants for this module
47integer, parameter :: NUM_FEATURES = 4 !< How many features used to predict EKE
48integer, parameter :: MKE_IDX = 1 !< Index of mean kinetic energy in the feature array
49integer, parameter :: SLOPE_Z_IDX = 2 !< Index of vertically averaged isopycnal slope in the feature array
50integer, parameter :: RV_IDX = 3 !< Index of surface relative vorticity in the feature array
51integer, parameter :: RD_DX_Z_IDX = 4 !< Index of the radius of deformation over the grid size in the feature array
52
53integer, parameter :: EKE_PROG = 1 !< Use prognostic equation to calculate EKE
54integer, parameter :: EKE_FILE = 2 !< Read in EKE from a file
55integer, parameter :: EKE_DBCLIENT = 3 !< Infer EKE using a neural network
56
57!> Control structure that contains MEKE parameters and diagnostics handles
58type, public :: MEKE_CS ; private
59 logical :: initialized = .false. !< True if this control structure has been initialized.
60 ! Parameters
61 real :: MEKE_FrCoeff !< Efficiency of conversion of ME into MEKE [nondim]
62 real :: MEKE_bhFrCoeff!< Efficiency of conversion of ME into MEKE by the biharmonic dissipation [nondim]
63 real :: MEKE_GMcoeff !< Efficiency of conversion of PE into MEKE [nondim]
64 real :: MEKE_GMECoeff !< Efficiency of conversion of MEKE into ME by GME [nondim]
65 real :: MEKE_damping !< Local depth-independent MEKE dissipation rate [T-1 ~> s-1].
66 real :: MEKE_Cd_scale !< The ratio of the bottom eddy velocity to the column mean
67 !! eddy velocity, i.e. sqrt(2*MEKE), [nondim]. This should be less than 1
68 !! to account for the surface intensification of MEKE.
69 real :: MEKE_Cb !< Coefficient in the \f$\gamma_{bot}\f$ expression [nondim]
70 real :: MEKE_min_gamma!< Minimum value of gamma_b^2 allowed [nondim]
71 real :: MEKE_Ct !< Coefficient in the \f$\gamma_{bt}\f$ expression [nondim]
72 logical :: visc_drag !< If true use the vertvisc_type to calculate bottom drag.
73 logical :: MEKE_GEOMETRIC !< If true, uses the GM coefficient formulation from the GEOMETRIC
74 !! framework (Marshall et al., 2012)
75 real :: MEKE_GEOMETRIC_alpha !< The nondimensional coefficient governing the efficiency of the
76 !! GEOMETRIC thickness diffusion [nondim].
77 logical :: MEKE_equilibrium_alt !< If true, use an alternative calculation for the
78 !! equilibrium value of MEKE.
79 logical :: MEKE_equilibrium_restoring !< If true, restore MEKE back to its equilibrium value,
80 !! which is calculated at each time step.
81 logical :: GM_src_alt !< If true, use the GM energy conversion form S^2*N^2*kappa rather
82 !! than the streamfunction for the MEKE GM source term.
83 real :: MEKE_min_depth_tot !< The minimum total thickness over which to distribute MEKE energy
84 !! sources from GM energy conversion [H ~> m or kg m-2]. When the total
85 !! thickness is less than this, the sources are scaled away.
86 logical :: Rd_as_max_scale !< If true the length scale can not exceed the
87 !! first baroclinic deformation radius.
88 logical :: use_old_lscale !< Use the old formula for mixing length scale.
89 logical :: use_min_lscale !< Use simple minimum for mixing length scale.
90 logical :: MEKE_positive !< If true, it guarantees that MEKE will always be >= 0.
91 real :: lscale_maxval !< The ceiling on the MEKE mixing length scale when use_min_lscale is true [L ~> m].
92 real :: cdrag !< The bottom drag coefficient for MEKE, times rescaling factors [H L-1 ~> nondim or kg m-3]
93 real :: MEKE_BGsrc !< Background energy source for MEKE [L2 T-3 ~> W kg-1] (= m2 s-3).
94 real :: MEKE_dtScale !< Scale factor to accelerate time-stepping [nondim]
95 real :: MEKE_KhCoeff !< Scaling factor to convert MEKE into Kh [nondim]
96 real :: MEKE_Uscale !< MEKE velocity scale for bottom drag [L T-1 ~> m s-1]
97 real :: MEKE_KH !< Background lateral diffusion of MEKE [L2 T-1 ~> m2 s-1]
98 real :: MEKE_K4 !< Background bi-harmonic diffusivity (of MEKE) [L4 T-1 ~> m4 s-1]
99 real :: KhMEKE_Fac !< A factor relating MEKE%Kh to the diffusivity used for
100 !! MEKE itself [nondim].
101 real :: viscosity_coeff_Ku !< The scaling coefficient in the expression for
102 !! viscosity used to parameterize lateral harmonic momentum mixing
103 !! by unresolved eddies represented by MEKE [nondim].
104 real :: viscosity_coeff_Au !< The scaling coefficient in the expression for
105 !! viscosity used to parameterize lateral biharmonic momentum mixing
106 !! by unresolved eddies represented by MEKE [nondim].
107 real :: Lfixed !< Fixed mixing length scale [L ~> m].
108 real :: aDeform !< Weighting towards deformation scale of mixing length [nondim]
109 real :: aRhines !< Weighting towards Rhines scale of mixing length [nondim]
110 real :: aFrict !< Weighting towards frictional arrest scale of mixing length [nondim]
111 real :: aEady !< Weighting towards Eady scale of mixing length [nondim]
112 real :: aGrid !< Weighting towards grid scale of mixing length [nondim]
113 real :: MEKE_advection_factor !< A scaling in front of the advection of MEKE [nondim]
114 real :: MEKE_topographic_beta !< Weight for how much topographic beta is considered
115 !! when computing beta in Rhines scale [nondim]
116 real :: MEKE_restoring_rate !< Inverse of the timescale used to nudge MEKE toward its
117 !! equilibrium value [T-1 ~> s-1].
118 logical :: MEKE_advection_bug !< If true, recover a bug in the calculation of the barotropic
119 !! transport for the advection of MEKE, wherein only the transports in the
120 !! deepest layer are used.
121 logical :: fixed_total_depth !< If true, use the nominal bathymetric depth as the estimate of
122 !! the time-varying ocean depth. Otherwise base the depth on the total
123 !! ocean mass per unit area.
124 real :: rho_fixed_total_depth !< A density used to translate the nominal bathymetric depth into an
125 !! estimate of the total ocean mass per unit area when MEKE_FIXED_TOTAL_DEPTH
126 !! is true [R ~> kg m-3]
127 logical :: kh_flux_enabled !< If true, lateral diffusive MEKE flux is enabled.
128 logical :: initialize !< If True, invokes a steady state solver to calculate MEKE.
129 logical :: debug !< If true, write out checksums of data for debugging
130 integer :: eke_src !< Enum specifying whether EKE is stepped forward prognostically (default),
131 !! read in from a file, or inferred via a neural network
132 logical :: sqg_use_MEKE !< If True, use MEKE%Le for the SQG vertical structure.
133 type(diag_ctrl), pointer :: diag => NULL() !< A type that regulates diagnostics output
134 !>@{ Diagnostic handles
135 integer :: id_MEKE = -1, id_Ue = -1, id_Kh = -1, id_src = -1
136 integer :: id_src_adv = -1, id_src_mom_K4 = -1, id_src_btm_drag = -1
137 integer :: id_src_GM = -1, id_src_mom_lp = -1, id_src_mom_bh = -1
138 integer :: id_Ub = -1, id_Ut = -1
139 integer :: id_GM_src = -1, id_mom_src = -1, id_mom_src_bh = -1, id_GME_snk = -1, id_decay = -1
140 integer :: id_KhMEKE_u = -1, id_KhMEKE_v = -1, id_Ku = -1, id_Au = -1
141 integer :: id_Le = -1, id_gamma_b = -1, id_gamma_t = -1
142 integer :: id_Lrhines = -1, id_Leady = -1
143 integer :: id_MEKE_equilibrium = -1
144 !>@}
145 type(external_field) :: eke_handle !< Handle for reading in EKE from a file
146 ! Infrastructure
147 integer :: id_clock_pass !< Clock for group pass calls
148 type(group_pass_type) :: pass_MEKE !< Group halo pass handle for MEKE%MEKE and maybe MEKE%Kh_diff
149 type(group_pass_type) :: pass_Kh !< Group halo pass handle for MEKE%Kh, MEKE%Ku, and/or MEKE%Au
150
151 ! MEKE via Machine Learning
152 type(dbclient_type), pointer :: client => NULL() !< Pointer to the database client
153
154 logical :: online_analysis !< If true, post the EKE used in MOM6 at every timestep
155 character(len=5) :: model_key = 'mleke' !< Key where the ML-model is stored
156 character(len=7) :: key_suffix !< Suffix appended to every key sent to Redis
157 real :: eke_max !< The maximum value of EKE considered physically reasonable [L2 T-2 ~> m2 s-2]
158
159 ! Clock ids
160 integer :: id_client_init !< Clock id to time initialization of the client
161 integer :: id_put_tensor !< Clock id to time put_tensor routine
162 integer :: id_run_model !< Clock id to time running of the ML model
163 integer :: id_unpack_tensor !< Clock id to time retrieval of EKE prediction
164
165 ! Diagnostic ids
166 integer :: id_mke = -1 !< Diagnostic id for surface mean kinetic energy
167 integer :: id_slope_z = -1 !< Diagnostic id for vertically averaged horizontal slope magnitude
168 integer :: id_slope_x = -1 !< Diagnostic id for isopycnal slope in the x-direction
169 integer :: id_slope_y = -1 !< Diagnostic id for isopycnal slope in the y-direction
170 integer :: id_rv = -1 !< Diagnostic id for surface relative vorticity
171
172 ! Isoneutral blocking parameters
173 integer :: niblock !< The i block size used in calc_isoneutral_slopes [nondim].
174 integer :: njblock !< The j block size used in calc_isoneutral_slopes [nondim].
175 integer :: nkblock !< The k block size used in calc_isoneutral_slopes [nondim].
176
177end type MEKE_CS
178
179contains
180
181!> Integrates forward-in-time the MEKE eddy energy equation.
182!! See \ref section_MEKE_equations.
18324subroutine step_forward_MEKE(MEKE, h, SN_u, SN_v, visc, dt, G, GV, US, CS, hu, hv, u, v, tv, Time)
184 type(MEKE_type), intent(inout) :: MEKE !< MEKE data.
185 type(ocean_grid_type), intent(inout) :: G !< Ocean grid.
186 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure.
187 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
188 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> m or kg m-2].
189 real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: SN_u !< Eady growth rate at u-points [T-1 ~> s-1].
190 real, dimension(SZI_(G),SZJB_(G)), intent(in) :: SN_v !< Eady growth rate at v-points [T-1 ~> s-1].
191 type(vertvisc_type), intent(in) :: visc !< The vertical viscosity type.
192 real, intent(in) :: dt !< Model(baroclinic) time-step [T ~> s].
193 type(MEKE_CS), intent(inout) :: CS !< MEKE control structure.
194 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(in) :: hu !< Accumulated zonal mass flux [H L2 ~> m3 or kg].
195 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(in) :: hv !< Accumulated meridional mass flux [H L2 ~> m3 or kg]
196 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(inout) :: u !< Zonal velocity [L T-1 ~> m s-1]
197 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(inout) :: v !< Meridional velocity [L T-1 ~> m s-1]
198 type(thermo_var_ptrs), intent(in) :: tv !< Type containing thermodynamic variables
199 type(time_type), intent(in) :: Time !< The time used for interpolating EKE
200
201 ! Local variables
202 real, dimension(SZI_(G),SZJ_(G)) :: &
20348 data_eke, & ! EKE from file [L2 T-2 ~> m2 s-2]
20448 mass, & ! The total mass of the water column [R Z ~> kg m-2].
20548 I_mass, & ! The inverse of mass [R-1 Z-1 ~> m2 kg-1].
20648 depth_tot, & ! The depth of the water column [H ~> m or kg m-2].
20748 src, & ! The sum of all MEKE sources [L2 T-3 ~> W kg-1] (= m2 s-3).
20848 MEKE_decay, & ! A diagnostic of the MEKE decay timescale [T-1 ~> s-1].
20948 src_adv, & ! The MEKE source/tendency from the horizontal advection of MEKE [L2 T-3 ~> W kg-1] (= m2 s-3).
21048 src_mom_K4, & ! The MEKE source/tendency from the bihamornic of MEKE [L2 T-3 ~> W kg-1] (= m2 s-3).
21148 src_btm_drag, & ! The MEKE source/tendency from the bottom drag acting on MEKE [L2 T-3 ~> W kg-1] (= m2 s-3).
21248 src_GM, & ! The MEKE source/tendency from the thickness mixing (GM) [L2 T-3 ~> W kg-1] (= m2 s-3).
21348 src_mom_lp, & ! The MEKE source/tendency from the Laplacian of the resolved flow [L2 T-3 ~> W kg-1] (= m2 s-3).
21448 src_mom_bh, & ! The MEKE source/tendency from the biharmonic of the resolved flow [L2 T-3 ~> W kg-1] (= m2 s-3).
21548 damp_rate_s1, & ! The MEKE damping rate computed at the 1st Strang splitting stage [T-1 ~> s-1].
21648 MEKE_current, & ! A copy of MEKE for use in computing the MEKE damping [L2 T-2 ~> m2 s-2].
21748 drag_rate_visc, & ! Near-bottom velocity contribution to bottom drag [H T-1 ~> m s-1 or kg m-2 s-1]
21848 drag_rate, & ! The MEKE spindown timescale due to bottom drag [T-1 ~> s-1].
21948 del2MEKE, & ! Laplacian of MEKE, used for bi-harmonic diffusion [T-2 ~> s-2].
22048 del4MEKE, & ! Time-integrated MEKE tendency arising from the biharmonic of MEKE [L2 T-2 ~> m2 s-2].
22148 LmixScale, & ! Eddy mixing length [L ~> m].
22248 barotrFac2, & ! Ratio of EKE_barotropic / EKE [nondim]
22348 bottomFac2, & ! Ratio of EKE_bottom / EKE [nondim]
22448 tmp, & ! Temporary variable for computation of diagnostic velocities [L T-1 ~> m s-1]
22548 equilibrium_value, & ! The equilibrium value of MEKE to be calculated at
226 ! each time step [L2 T-2 ~> m2 s-2]
22748 damp_rate, & ! The MEKE damping rate [T-1 ~> s-1]
22848 damping ! The net damping of a field after sdt_damp [nondim]
229
230 real, dimension(SZIB_(G),SZJ_(G)) :: &
23148 MEKE_uflux, & ! The zonal advective and diffusive flux of MEKE with units of [R Z L4 T-3 ~> kg m2 s-3].
232 ! In one place, MEKE_uflux is used as temporary work space with units of [L2 T-2 ~> m2 s-2].
23348 Kh_u, & ! The zonal diffusivity that is actually used [L2 T-1 ~> m2 s-1].
23424 baroHu, & ! Depth integrated accumulated zonal mass flux [R Z L2 ~> kg].
23548 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]
236 real, dimension(SZI_(G),SZJB_(G)) :: &
23748 MEKE_vflux, & ! The meridional advective and diffusive flux of MEKE with units of [R Z L4 T-3 ~> kg m2 s-3].
238 ! In one place, MEKE_vflux is used as temporary work space with units of [L2 T-2 ~> m2 s-2].
23948 Kh_v, & ! The meridional diffusivity that is actually used [L2 T-1 ~> m2 s-1].
24048 baroHv, & ! Depth integrated accumulated meridional mass flux [R Z L2 ~> kg].
24148 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]
242 real :: bh_coeff ! Biharmonic part of efficiency conversion in total MEKE [nondim]
243 real :: Kh_here ! The local horizontal viscosity [L2 T-1 ~> m2 s-1]
244 real :: Inv_Kh_max ! The inverse of the local horizontal viscosity [T L-2 ~> s m-2]
245 real :: K4_here ! The local horizontal biharmonic viscosity [L4 T-1 ~> m4 s-1]
246 real :: Inv_K4_max ! The inverse of the local horizontal biharmonic viscosity [T L-4 ~> s m-4]
247 real :: cdrag2 ! The square of the drag coefficient times unit conversion factors [H2 L-2 ~> nondim or kg2 m-6]
248 real :: advFac ! The product of the advection scaling factor and 1/dt [T-1 ~> s-1]
249 real :: mass_neglect ! A negligible mass [R Z ~> kg m-2].
250 real :: sdt ! dt to use locally [T ~> s] (could be scaled to accelerate)
251 real :: sdt_damp ! dt for damping [T ~> s] (sdt could be split).
252 real :: damp_step ! Size of damping timestep relative to sdt [nondim]
253 logical :: use_drag_rate ! Flag to indicate drag_rate is finite
254 logical :: any_damping_diags_s1 ! True if any damped diagnostics are enabled in first stage
255 logical :: any_damping_diags ! True if any damped diagnostics are enabled
256 integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz
25796 real(kind=real32), dimension(size(MEKE%MEKE),NUM_FEATURES) :: features_array ! The array of features
258 ! needed for the machine learning inference, with different
259 ! units for the various subarrays [various]
260
26124 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
26224 Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB
263
26424 if (.not.CS%initialized) call MOM_error(FATAL, &
2650 "MOM_MEKE: Module must be initialized before it is used.")
266
26724 if ((CS%MEKE_Cd_scale > 0.0) .or. (CS%MEKE_Cb>0.) .or. CS%visc_drag) then
26824 use_drag_rate = .true.
269 else
2700 use_drag_rate = .false.
271 endif
272
273 ! Only integrate the MEKE equations if MEKE is required.
27424 if (.not. allocated(MEKE%MEKE)) then
275! call MOM_error(FATAL, "MOM_MEKE: MEKE%MEKE is not associated!")
2760 return
277 endif
278
279 ! Map the per-call inputs and the local scratch. The MEKE% and CS% members were mapped
280 ! and synced at init; host-written MEKE% members are refreshed below.
281 !$omp target enter data map(to: SN_u, SN_v)
282 !$omp target enter data map(alloc: mass, I_mass, depth_tot, src, drag_rate, drag_rate_visc)
283 !$omp target enter data map(alloc: drag_vel_u, drag_vel_v, bottomFac2, barotrFac2, LmixScale)
284 !$omp target enter data map(alloc: MEKE_uflux, MEKE_vflux, Kh_u, Kh_v, baroHu, baroHv)
285 !$omp target enter data map(alloc: damp_rate, damping, MEKE_current, damp_rate_s1)
286 !$omp target enter data map(alloc: del2MEKE, del4MEKE, MEKE_decay, equilibrium_value)
287 !$omp target enter data map(alloc: src_adv, src_GM, src_mom_K4, src_btm_drag, src_mom_lp, src_mom_bh)
288
289 ! These MEKE members are written on the host by other modules (thickness_diffuse,
290 ! hor_visc frictional-work accumulation, VarMix), so refresh the device copies.
291 if (allocated(MEKE%GM_src)) then
292 !$omp target update to(MEKE%GM_src)
293 endif
294 if (allocated(MEKE%mom_src)) then
295 !$omp target update to(MEKE%mom_src)
296 endif
297 if (allocated(MEKE%mom_src_bh)) then
298 !$omp target update to(MEKE%mom_src_bh)
299 endif
300 if (allocated(MEKE%GME_snk)) then
301 !$omp target update to(MEKE%GME_snk)
302 endif
303 if (allocated(MEKE%Rd_dx_h)) then
304 !$omp target update to(MEKE%Rd_dx_h)
305 endif
306 if (allocated(MEKE%Kh_diff)) then
307 !$omp target update to(MEKE%Kh_diff)
308 endif
309
31048 select case(CS%eke_src)
311 case(EKE_PROG)
31224 if (CS%debug) then
3130 if (allocated(MEKE%mom_src)) &
3140 call hchksum(MEKE%mom_src, 'MEKE mom_src', G%HI, unscale=US%RZ3_T3_to_W_m2*US%L_to_Z**2)
3150 if (allocated(MEKE%mom_src_bh)) &
3160 call hchksum(MEKE%mom_src_bh, 'MEKE mom_src_bh', G%HI, unscale=US%RZ3_T3_to_W_m2*US%L_to_Z**2)
3170 if (allocated(MEKE%GME_snk)) &
3180 call hchksum(MEKE%GME_snk, 'MEKE GME_snk', G%HI, unscale=US%RZ3_T3_to_W_m2*US%L_to_Z**2)
3190 if (allocated(MEKE%GM_src)) &
3200 call hchksum(MEKE%GM_src, 'MEKE GM_src', G%HI, unscale=US%RZ3_T3_to_W_m2*US%L_to_Z**2)
3210 if (allocated(MEKE%MEKE)) &
3220 call hchksum(MEKE%MEKE, 'MEKE MEKE', G%HI, unscale=US%L_T_to_m_s**2)
323 call uvchksum("MEKE SN_[uv]", SN_u, SN_v, G%HI, unscale=US%s_to_T, &
3240 scalar_pair=.true.)
325 !$omp target update from(hu, hv)
326 call uvchksum("MEKE h[uv]", hu, hv, G%HI, haloshift=0, symmetric=.true., &
3270 unscale=GV%H_to_m*US%L_to_m**2)
328 endif
329
33024 sdt = dt*CS%MEKE_dtScale ! Scaled dt to use for time-stepping
33124 mass_neglect = GV%H_to_RZ * GV%H_subroundoff
33224 cdrag2 = CS%cdrag**2
333
334 ! With a depth-dependent (and possibly strong) damping, it seems
335 ! advisable to use Strang splitting between the damping and diffusion.
33624 damp_step = 1.
33724 if (CS%MEKE_KH >= 0. .or. CS%MEKE_K4 >= 0.) damp_step = 0.5
33824 sdt_damp = sdt * damp_step
339
340 ! Calculate depth integrated mass exchange if doing advection [R Z L2 ~> kg]
34124 if (CS%MEKE_advection_factor>0.) then
3420 do concurrent (j=js:je, I=is-1:ie) DO_LOCALITY(local(k))
3430 baroHu(I,j) = 0.
3440 do k=1,nz
3450 baroHu(I,j) = baroHu(I,j) + hu(I,j,k) * GV%H_to_RZ
346 enddo
347 enddo
3480 do concurrent (J=js-1:je, i=is:ie) DO_LOCALITY(local(k))
3490 baroHv(i,J) = 0.
3500 do k=1,nz
3510 baroHv(i,J) = baroHv(i,J) + hv(i,J,k) * GV%H_to_RZ
352 enddo
353 enddo
3540 if (CS%MEKE_advection_bug) then
355 ! This obviously incorrect code reproduces a bug in the original implementation of
356 ! the MEKE advection.
3570 do concurrent (j=js:je, I=is-1:ie)
3580 baroHu(I,j) = hu(I,j,nz) * GV%H_to_RZ
359 enddo
3600 do concurrent (J=js-1:je, i=is:ie)
3610 baroHv(i,J) = hv(i,J,nz) * GV%H_to_RZ
362 enddo
363 endif
364 endif
365
366 ! Calculate drag_rate_visc(i,j) which accounts for the model bottom mean flow
36724 if (CS%visc_drag .and. allocated(visc%Kv_bbl_u) .and. allocated(visc%Kv_bbl_v)) then
36824 do concurrent (j=js:je, I=is-1:ie)
369174240 drag_vel_u(I,j) = 0.0
370174240 if ((G%mask2dCu(I,j) > 0.0) .and. (visc%bbl_thick_u(I,j) > 0.0)) &
371177168 drag_vel_u(I,j) = visc%Kv_bbl_u(I,j) / visc%bbl_thick_u(I,j)
372 enddo
37324 do concurrent (J=js-1:je, i=is:ie)
374175680 drag_vel_v(i,J) = 0.0
375175680 if ((G%mask2dCv(i,J) > 0.0) .and. (visc%bbl_thick_v(i,J) > 0.0)) &
376178584 drag_vel_v(i,J) = visc%Kv_bbl_v(i,J) / visc%bbl_thick_v(i,J)
377 enddo
378
37924 do concurrent (j=js:je, i=is:ie)
380 drag_rate_visc(i,j) = (0.25*G%IareaT(i,j) * &
381 (((G%areaCu(I-1,j)*drag_vel_u(I-1,j)) + &
382 (G%areaCu(I,j)*drag_vel_u(I,j))) + &
383 ((G%areaCv(i,J-1)*drag_vel_v(i,J-1)) + &
384175704 (G%areaCv(i,J)*drag_vel_v(i,J))) ) )
385 enddo
386 else
3870 do concurrent (j=js:je, i=is:ie)
3880 drag_rate_visc(i,j) = 0.
389 enddo
390 endif
391
3922952 do concurrent (j=js-1:je+1, i=is-1:ie+1) DO_LOCALITY(local(k))
393181536 mass(i,j) = 0.0
39413796736 do k=1,nz
39513796736 mass(i,j) = mass(i,j) + G%mask2dT(i,j) * (GV%H_to_RZ * h(i,j,k)) ! [R Z ~> kg m-2]
396 enddo
397181536 I_mass(i,j) = 0.0
398184488 if (mass(i,j) > 0.0) I_mass(i,j) = 1.0 / mass(i,j) ! [R-1 Z-1 ~> m2 kg-1]
399 enddo
400
40124 if (CS%fixed_total_depth) then
40224 if (GV%Boussinesq) then
4032952 do concurrent (j=js-1:je+1, i=is-1:ie+1)
404184488 depth_tot(i,j) = max(G%meanSL(i,j) + G%bathyT(i,j), 0.0) * GV%Z_to_H
405 enddo
406 else
4070 do concurrent (j=js-1:je+1, i=is-1:ie+1)
4080 depth_tot(i,j) = max(G%meanSL(i,j) + G%bathyT(i,j), 0.0) * CS%rho_fixed_total_depth * GV%RZ_to_H
409 enddo
410 endif
411 else
4120 do concurrent (j=js-1:je+1, i=is-1:ie+1)
4130 depth_tot(i,j) = mass(i,j) * GV%RZ_to_H
414 enddo
415 endif
416
41724 if (CS%initialize) then
418 ! MEKE_equilibrium runs on the host, once: sync its device-computed inputs down and
419 ! its result (MEKE%MEKE) back up.
420 !$omp target update from(drag_rate_visc, I_mass, depth_tot)
4211 call MEKE_equilibrium(CS, MEKE, G, GV, US, SN_u, SN_v, drag_rate_visc, I_mass, depth_tot)
4221 CS%initialize = .false.
423 !$omp target update to(MEKE%MEKE)
424 endif
425
426 ! TODO: THIS RUNS ON THE HOST, BE WARY! AAAAAA
427 ! MEKE_lengthScales_0d takes (1+x)**0.8 / (1+x)**0.25 and the intrinsic pow differs
428 ! CPU vs GPU in the last bit, so this stays on the host (with the depth_tot /
429 ! bottomFac2 / barotrFac2 / LmixScale syncs below) until a bit-reproducible pow
430 ! lands. Only bottomFac2 (the **0.8) actually diverged on device.
431 !$omp target update from(depth_tot)
43224 call MEKE_lengthScales(CS, MEKE, G, GV, US, SN_u, SN_v, MEKE%MEKE, depth_tot, bottomFac2, barotrFac2, LmixScale)
433 !$omp target update to(bottomFac2, barotrFac2, LmixScale)
43424 if (CS%debug) then
435 !$omp target update from(mass, drag_rate_visc, bottomFac2, barotrFac2, LmixScale)
436 if (CS%visc_drag) then
437 !$omp target update from(drag_vel_u, drag_vel_v)
438 endif
4390 if (CS%visc_drag) &
440 call uvchksum("MEKE drag_vel_[uv]", drag_vel_u, drag_vel_v, G%HI, &
4410 unscale=GV%H_to_mks*US%s_to_T, scalar_pair=.true.)
4420 call hchksum(mass, 'MEKE mass',G%HI,haloshift=1, unscale=US%RZ_to_kg_m2)
4430 call hchksum(drag_rate_visc, 'MEKE drag_rate_visc', G%HI, unscale=GV%H_to_mks*US%s_to_T)
4440 call hchksum(bottomFac2, 'MEKE bottomFac2', G%HI)
4450 call hchksum(barotrFac2, 'MEKE barotrFac2', G%HI)
4460 call hchksum(LmixScale, 'MEKE LmixScale', G%HI, unscale=US%L_to_m)
447 endif
448
44924 if (allocated(MEKE%Le)) then
4500 do concurrent (j=js:je, i=is:ie)
4510 MEKE%Le(i,j) = LmixScale(i,j)
452 enddo
453 endif
454
455 ! Aggregate sources of MEKE (background, frictional and GM)
45624 do concurrent (j=js:je, i=is:ie)
457175704 src(i,j) = CS%MEKE_BGsrc
458 enddo
459
460 ! Initialize diagnostics
46124 if (CS%id_src_adv > 0) then
4620 do concurrent (j=js:je, i=is:ie)
4630 src_adv(i,j) = 0.
464 enddo
465 endif
46624 if (CS%id_src_GM > 0) then
4670 do concurrent (j=js:je, i=is:ie)
4680 src_GM(i,j) = 0.
469 enddo
470 endif
47124 if (CS%id_src_mom_lp > 0) then
4720 do concurrent (j=js:je, i=is:ie)
4730 src_mom_lp(i,j) = 0.
474 enddo
475 endif
47624 if (CS%id_src_mom_bh > 0) then
4770 do concurrent (j=js:je, i=is:ie)
4780 src_mom_bh(i,j) = 0.
479 enddo
480 endif
48124 if (CS%id_src_mom_K4 > 0) then
4820 do concurrent (j=js:je, i=is:ie)
4830 src_mom_K4(i,j) = 0.
484 enddo
485 endif
48624 if (CS%id_src_btm_drag > 0) then
4870 do concurrent (j=js:je, i=is:ie)
4880 src_btm_drag(i,j) = 0.
489 enddo
490 endif
491
492 ! Identify any damped diagnostics in first stage of Strang splitting
493 any_damping_diags_s1 = any([ &
494 CS%id_src_GM > 0, &
495 CS%id_src_mom_lp > 0, &
496 CS%id_src_mom_bh > 0, &
497 CS%id_src_btm_drag > 0 &
498120 ])
499
500 ! Identify any damped diagnostics
501 any_damping_diags = any([ &
502 any_damping_diags_s1, &
503 CS%id_src_adv > 0, &
504 CS%id_src_mom_K4 > 0 &
50596 ])
506
50724 if (CS%MEKE_FrCoeff > 0.) then
5080 do concurrent (j=js:je, i=is:ie)
5090 src(i,j) = src(i,j) - CS%MEKE_FrCoeff * I_mass(i,j) * MEKE%mom_src(i,j)
510 enddo
511 endif
512
51324 if (allocated(MEKE%mom_src_bh)) then
5140 if (CS%MEKE_bhFrCoeff > 0. .and. CS%MEKE_FrCoeff > 0.) then
5150 bh_coeff = CS%MEKE_bhFrCoeff - CS%MEKE_FrCoeff
516 else
5170 bh_coeff = CS%MEKE_bhFrCoeff
518 endif
519
5200 do concurrent (j=js:je, i=is:ie)
5210 src(i,j) = src(i,j) - bh_coeff * I_mass(i,j) * MEKE%mom_src_bh(i,j)
522 enddo
523
5240 if (CS%id_src_mom_lp > 0) then
5250 do concurrent (j=js:je, i=is:ie)
526 src_mom_lp(i,j) = -CS%MEKE_FrCoeff * I_mass(i,j) &
5270 * (MEKE%mom_src(i,j) - MEKE%mom_src_bh(i,j))
528 enddo
529 endif
530
5310 if (CS%id_src_mom_bh > 0) then
5320 do concurrent (j=js:je, i=is:ie)
5330 src_mom_bh(i,j) = -CS%MEKE_bhFrCoeff * I_mass(i,j) * MEKE%mom_src_bh(i,j)
534 enddo
535 endif
536 endif
537
53824 if (allocated(MEKE%GME_snk)) then
5390 do concurrent (j=js:je, i=is:ie)
5400 src(i,j) = src(i,j) - CS%MEKE_GMECoeff*I_mass(i,j)*MEKE%GME_snk(i,j)
541 enddo
542 endif
543
54424 if (allocated(MEKE%GM_src)) then
54524 if (CS%GM_src_alt) then
5460 do concurrent (j=js:je, i=is:ie)
547 src(i,j) = src(i,j) - CS%MEKE_GMcoeff*MEKE%GM_src(i,j) / &
5480 (GV%H_to_RZ * MAX(CS%MEKE_min_depth_tot, depth_tot(i,j)))
549 enddo
550 else
55124 do concurrent (j=js:je, i=is:ie)
552175704 src(i,j) = src(i,j) - CS%MEKE_GMcoeff*I_mass(i,j)*MEKE%GM_src(i,j)
553 enddo
554
55524 do concurrent (j=js:je, i=is:ie)
556175704 src_GM(i,j) = -CS%MEKE_GMcoeff*I_mass(i,j)*MEKE%GM_src(i,j)
557 enddo
558 endif
559 endif
560
56124 if (CS%MEKE_equilibrium_restoring) then
562 ! MEKE_equilibrium_restoring runs on the host: sync its device-computed input down
563 ! and its result up before the device loop consumes it.
564 !$omp target update from(depth_tot)
565 call MEKE_equilibrium_restoring(CS, G, GV, US, SN_u, SN_v, depth_tot, &
5660 equilibrium_value)
567 !$omp target update to(equilibrium_value)
5680 do concurrent (j=js:je, i=is:ie)
5690 src(i,j) = src(i,j) - CS%MEKE_restoring_rate*(MEKE%MEKE(i,j) - equilibrium_value(i,j))
570 enddo
571 endif
572
57324 if (CS%debug) then
574 !$omp target update from(src)
5750 call hchksum(src, "MEKE src", G%HI, haloshift=0, unscale=US%L_to_m**2*US%s_to_T**3)
576 endif
577
578 ! Increase EKE by a full time-steps worth of source
57924 do concurrent (j=js:je, i=is:ie)
580172800 MEKE_current(i,j) = MEKE%MEKE(i,j)
581175704 MEKE%MEKE(i,j) = (MEKE%MEKE(i,j) + sdt*src(i,j))*G%mask2dT(i,j)
582 enddo
583
58424 if (use_drag_rate) then
585 ! Calculate a viscous drag rate (includes BBL contributions from mean flow and eddies)
58624 do concurrent (j=js:je, i=is:ie)
587 drag_rate(i,j) = (GV%H_to_RZ * I_mass(i,j)) * sqrt( drag_rate_visc(i,j)**2 + &
588175704 cdrag2 * ( max(0.0, 2.0*bottomFac2(i,j)*MEKE%MEKE(i,j)) + CS%MEKE_Uscale**2 ) )
589 enddo
590 else
5910 do concurrent (j=js:je, i=is:ie)
5920 drag_rate(i,j) = 0.
593 enddo
594 endif
595
596 ! First stage of Strang splitting
597
59824 do concurrent (j=js:je, i=is:ie)
599172800 damp_rate(i,j) = CS%MEKE_damping + drag_rate(i,j) * bottomFac2(i,j)
600
601175704 if (MEKE%MEKE(i,j) < 0.) damp_rate(i,j) = 0.
602 ! notice that the above line ensures a damping only if MEKE is positive,
603 ! while leaving MEKE unchanged if it is negative
604 enddo
605
606 ! NOTE: MEKE%MEKE cannot use `damping` since we must preserve the existing
607 ! bit-reproducible solution.
60824 do concurrent (j=js:je, i=is:ie)
609175704 MEKE%MEKE(i,j) = MEKE%MEKE(i,j) / (1. + sdt_damp * damp_rate(i,j))
610 enddo
611
61224 if (any_damping_diags_s1) then
6130 do concurrent (j=js:je, i=is:ie)
6140 damping(i,j) = 1. / (1. + sdt_damp * damp_rate(i,j))
615 enddo
616
6170 if (CS%id_decay > 0) then
6180 do concurrent (j=js:je, i=is:ie)
6190 MEKE_decay(i,j) = damp_rate(i,j) * G%mask2dT(i,j)
620 enddo
621 endif
622
6230 if (CS%id_src_GM > 0) then
6240 do concurrent (j=js:je, i=is:ie)
6250 src_GM(i,j) = src_GM(i,j) * damping(i,j)
626 enddo
627 endif
628
6290 if (CS%id_src_mom_lp > 0) then
6300 do concurrent (j=js:je, i=is:ie)
6310 src_mom_lp(i,j) = src_mom_lp(i,j) * damping(i,j)
632 enddo
633 endif
634
6350 if (CS%id_src_mom_bh > 0) then
6360 do concurrent (j=js:je, i=is:ie)
6370 src_mom_bh(i,j) = src_mom_bh(i,j) * damping(i,j)
638 enddo
639 endif
640
6410 if (CS%id_src_btm_drag > 0) then
6420 do concurrent (j=js:je, i=is:ie)
643 src_btm_drag(i,j) = -MEKE_current(i,j) * ( &
644 damp_step * (damp_rate(i,j) * damping(i,j)) &
6450 )
646 enddo
647
648 ! Store the effective damping rate if sdt is split
6490 if (CS%MEKE_KH >= 0. .or. CS%MEKE_K4 >= 0.) then
6500 do concurrent (j=js:je, i=is:ie)
6510 damp_rate_s1(i,j) = damp_rate(i,j) * damping(i,j)
652 enddo
653 endif
654 endif
655 endif
656
65724 if (CS%kh_flux_enabled .or. CS%MEKE_K4 >= 0.0) then
658 ! Update MEKE in the halos for lateral or bi-harmonic diffusion
65924 call cpu_clock_begin(CS%id_clock_pass)
66024 call do_group_pass(CS%pass_MEKE, G%Domain, omp_offload=.true.)
66124 call cpu_clock_end(CS%id_clock_pass)
662 endif
663
66424 if (CS%MEKE_K4 >= 0.0) then
665 ! Calculate Laplacian of MEKE using MEKE_uflux and MEKE_vflux as temporary work space.
6660 do concurrent (j=js-1:je+1, I=is-2:ie+1)
667 ! MEKE_uflux is used here as workspace with units of [L2 T-2 ~> m2 s-2].
668 MEKE_uflux(I,j) = (G%dy_Cu(I,j)*G%IdxCu_OBCmask(I,j)) * &
6690 (MEKE%MEKE(i+1,j) - MEKE%MEKE(i,j))
670 ! This would have units of [R Z L2 T-2 ~> kg s-2]
671 ! MEKE_uflux(I,j) = ((G%dy_Cu(I,j)*G%IdxCu(I,j)) * &
672 ! ((2.0*mass(i,j)*mass(i+1,j)) / ((mass(i,j)+mass(i+1,j)) + mass_neglect)) ) * &
673 ! (MEKE%MEKE(i+1,j) - MEKE%MEKE(i,j))
674 enddo
6750 do concurrent (J=js-2:je+1, i=is-1:ie+1)
676 ! MEKE_vflux is used here as workspace with units of [L2 T-2 ~> m2 s-2].
677 MEKE_vflux(i,J) = (G%dx_Cv(i,J)*G%IdyCv_OBCmask(i,J)) * &
6780 (MEKE%MEKE(i,j+1) - MEKE%MEKE(i,j))
679 ! This would have units of [R Z L2 T-2 ~> kg s-2]
680 ! MEKE_vflux(i,J) = ((G%dx_Cv(i,J)*G%IdyCv(i,J)) * &
681 ! ((2.0*mass(i,j)*mass(i,j+1)) / ((mass(i,j)+mass(i,j+1)) + mass_neglect)) ) * &
682 ! (MEKE%MEKE(i,j+1) - MEKE%MEKE(i,j))
683 enddo
684
6850 do concurrent (j=js-1:je+1, i=is-1:ie+1) ! del2MEKE has units [T-2 ~> s-2].
686 del2MEKE(i,j) = G%IareaT(i,j) * &
6870 ((MEKE_uflux(I,j) - MEKE_uflux(I-1,j)) + (MEKE_vflux(i,J) - MEKE_vflux(i,J-1)))
688 enddo
689
690 ! Bi-harmonic diffusion of MEKE
6910 do concurrent (j=js:je, I=is-1:ie) DO_LOCALITY(local(K4_here, Inv_K4_max))
6920 K4_here = CS%MEKE_K4 ! [L4 T-1 ~> m4 s-1]
693 ! Limit Kh to avoid CFL violations.
694 Inv_K4_max = 64.0 * sdt * ((G%dy_Cu(I,j)*G%IdxCu(I,j)) * &
6950 max(G%IareaT(i,j), G%IareaT(i+1,j)))**2
6960 if (K4_here*Inv_K4_max > 0.3) K4_here = 0.3 / Inv_K4_max
697
698 ! Here the units of MEKE_uflux are [R Z L4 T-3 ~> kg m2 s-3].
699 MEKE_uflux(I,j) = ((K4_here * (G%dy_Cu(I,j)*G%IdxCu(I,j))) * &
700 ((2.0*mass(i,j)*mass(i+1,j)) / ((mass(i,j)+mass(i+1,j)) + mass_neglect)) ) * &
7010 (del2MEKE(i+1,j) - del2MEKE(i,j))
702 enddo
7030 do concurrent (J=js-1:je, i=is:ie) DO_LOCALITY(local(K4_here, Inv_K4_max))
7040 K4_here = CS%MEKE_K4 ! [L4 T-1 ~> m4 s-1]
7050 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
7060 if (K4_here*Inv_K4_max > 0.3) K4_here = 0.3 / Inv_K4_max
707
708 ! Here the units of MEKE_vflux are [R Z L4 T-3 ~> kg m2 s-3].
709 MEKE_vflux(i,J) = ((K4_here * (G%dx_Cv(i,J)*G%IdyCv(i,J))) * &
710 ((2.0*mass(i,j)*mass(i,j+1)) / ((mass(i,j)+mass(i,j+1)) + mass_neglect)) ) * &
7110 (del2MEKE(i,j+1) - del2MEKE(i,j))
712 enddo
713 ! Store change in MEKE arising from the bi-harmonic in del4MEKE [L2 T-2 ~> m2 s-2].
7140 do concurrent (j=js:je, i=is:ie)
715 del4MEKE(i,j) = (sdt*(G%IareaT(i,j)*I_mass(i,j))) * &
716 ((MEKE_uflux(I-1,j) - MEKE_uflux(I,j)) + &
7170 (MEKE_vflux(i,J-1) - MEKE_vflux(i,J)))
718 src_mom_K4(i,j) = (G%IareaT(i,j)*I_mass(i,j)) * &
719 ((MEKE_uflux(I-1,j) - MEKE_uflux(I,j)) + &
7200 (MEKE_vflux(i,J-1) - MEKE_vflux(i,J)))
721 enddo
722 endif !
723
72424 if (CS%kh_flux_enabled) then
725 ! Lateral diffusion of MEKE
72624 Kh_here = max(0., CS%MEKE_Kh)
72724 do concurrent (j=js:je, I=is-1:ie) DO_LOCALITY(local(Inv_Kh_max) local_init(Kh_here))
728 ! Limit Kh to avoid CFL violations.
729174240 if (allocated(MEKE%Kh)) &
730 Kh_here = max(0., CS%MEKE_Kh) + &
731174240 CS%KhMEKE_Fac*0.5*(MEKE%Kh(i,j)+MEKE%Kh(i+1,j))
732174240 if (allocated(MEKE%Kh_diff)) &
733 Kh_here = max(0.,CS%MEKE_Kh) + &
7340 CS%KhMEKE_Fac*0.5*(MEKE%Kh_diff(i,j)+MEKE%Kh_diff(i+1,j))
735 Inv_Kh_max = 2.0*sdt * ((G%dy_Cu(I,j)*G%IdxCu(I,j)) * &
736174240 max(G%IareaT(i,j),G%IareaT(i+1,j)))
737174240 if (Kh_here*Inv_Kh_max > 0.25) Kh_here = 0.25 / Inv_Kh_max
738174240 Kh_u(I,j) = Kh_here
739
740 ! Here the units of MEKE_uflux and MEKE_vflux are [R Z L4 T-3 ~> kg m2 s-3].
741 MEKE_uflux(I,j) = ((Kh_here * (G%dy_Cu(I,j)*G%IdxCu(I,j))) * &
742 ((2.0*mass(i,j)*mass(i+1,j)) / ((mass(i,j)+mass(i+1,j)) + mass_neglect)) ) * &
743177168 (MEKE%MEKE(i,j) - MEKE%MEKE(i+1,j))
744 enddo
74524 do concurrent (J=js-1:je, i=is:ie) DO_LOCALITY(local(Inv_Kh_max) local_init(Kh_here))
746175680 if (allocated(MEKE%Kh)) &
747175680 Kh_here = max(0.,CS%MEKE_Kh) + CS%KhMEKE_Fac * 0.5*(MEKE%Kh(i,j)+MEKE%Kh(i,j+1))
748175680 if (allocated(MEKE%Kh_diff)) &
7490 Kh_here = max(0.,CS%MEKE_Kh) + CS%KhMEKE_Fac * 0.5*(MEKE%Kh_diff(i,j)+MEKE%Kh_diff(i,j+1))
750175680 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)))
751175680 if (Kh_here*Inv_Kh_max > 0.25) Kh_here = 0.25 / Inv_Kh_max
752175680 Kh_v(i,J) = Kh_here
753
754 ! Here the units of MEKE_uflux and MEKE_vflux are [R Z L4 T-3 ~> kg m2 s-3].
755 MEKE_vflux(i,J) = ((Kh_here * (G%dx_Cv(i,J)*G%IdyCv(i,J))) * &
756 ((2.0*mass(i,j)*mass(i,j+1)) / ((mass(i,j)+mass(i,j+1)) + mass_neglect)) ) * &
757178584 (MEKE%MEKE(i,j) - MEKE%MEKE(i,j+1))
758 enddo
75924 if (CS%MEKE_advection_factor>0.) then
7600 advFac = CS%MEKE_advection_factor / sdt ! [T-1 ~> s-1]
7610 do concurrent (j=js:je, I=is-1:ie)
762 ! Here the units of the quantities added to MEKE_uflux are [R Z L4 T-3 ~> kg m2 s-3].
7630 if (baroHu(I,j)>0.) then
7640 MEKE_uflux(I,j) = MEKE_uflux(I,j) + baroHu(I,j)*MEKE%MEKE(i,j)*advFac
7650 elseif (baroHu(I,j)<0.) then
7660 MEKE_uflux(I,j) = MEKE_uflux(I,j) + baroHu(I,j)*MEKE%MEKE(i+1,j)*advFac
767 endif
768 enddo
7690 do concurrent (J=js-1:je, i=is:ie)
770 ! Here the units of the quantities added to MEKE_vflux are [R Z L4 T-3 ~> kg m2 s-3].
7710 if (baroHv(i,J)>0.) then
7720 MEKE_vflux(i,J) = MEKE_vflux(i,J) + baroHv(i,J)*MEKE%MEKE(i,j)*advFac
7730 elseif (baroHv(i,J)<0.) then
7740 MEKE_vflux(i,J) = MEKE_vflux(i,J) + baroHv(i,J)*MEKE%MEKE(i,j+1)*advFac
775 endif
776 enddo
777 endif
778
77924 do concurrent (j=js:je, i=is:ie)
780 MEKE%MEKE(i,j) = MEKE%MEKE(i,j) + (sdt*(G%IareaT(i,j)*I_mass(i,j))) * &
781 ((MEKE_uflux(I-1,j) - MEKE_uflux(I,j)) + &
782175704 (MEKE_vflux(i,J-1) - MEKE_vflux(i,J)))
783 enddo
784
78524 if (CS%id_src_adv > 0) then
7860 do concurrent (j=js:je, i=is:ie)
787 src_adv(i,j) = (G%IareaT(i,j)*I_mass(i,j)) * &
788 ((MEKE_uflux(I-1,j) - MEKE_uflux(I,j)) + &
7890 (MEKE_vflux(i,J-1) - MEKE_vflux(i,J)))
790 enddo
791 endif
792 endif ! MEKE_KH>0
793
794 ! Add on bi-harmonic tendency
79524 if (CS%MEKE_K4 >= 0.0) then
7960 do concurrent (j=js:je, i=is:ie)
7970 MEKE%MEKE(i,j) = MEKE%MEKE(i,j) + del4MEKE(i,j)
798 enddo
799 endif
800
801 ! Second stage of Strang splitting
80224 if (CS%MEKE_KH >= 0.0 .or. CS%MEKE_K4 >= 0.0) then
803 ! Recalculate the drag rate, since MEKE has changed.
8040 if (use_drag_rate) then
8050 do concurrent (j=js:je, i=is:ie)
806 drag_rate(i,j) = (GV%H_to_RZ * I_mass(i,j)) * sqrt( drag_rate_visc(i,j)**2 + &
8070 cdrag2 * ( max(0.0, 2.0*bottomFac2(i,j)*MEKE%MEKE(i,j)) + CS%MEKE_Uscale**2 ) )
808 enddo
809 endif
810
8110 do concurrent (j=js:je, i=is:ie)
8120 damp_rate(i,j) = CS%MEKE_damping + drag_rate(i,j) * bottomFac2(i,j)
813
8140 if (MEKE%MEKE(i,j) < 0.) damp_rate(i,j) = 0.
815 ! notice that the above line ensures a damping only if MEKE is positive,
816 ! while leaving MEKE unchanged if it is negative
817 enddo
818
819 ! NOTE: MEKE%MEKE cannot use `damping` since we must preserve the
820 ! existing bit-reproducible solution.
8210 do concurrent (j=js:je, i=is:ie)
8220 MEKE%MEKE(i,j) = MEKE%MEKE(i,j) / (1. + sdt_damp * damp_rate(i,j))
823 enddo
824
8250 if (any_damping_diags) then
8260 do concurrent (j=js:je, i=is:ie)
8270 damping(i,j) = 1. / (1. + sdt_damp * damp_rate(i,j))
828 enddo
829
8300 if (CS%id_decay > 0) then
8310 do concurrent (j=js:je, i=is:ie)
8320 MEKE_decay(i,j) = damp_rate(i,j) * G%mask2dT(i,j)
833 enddo
834 endif
835
8360 if (CS%id_src_GM > 0) then
8370 do concurrent (j=js:je, i=is:ie)
8380 src_GM(i,j) = src_GM(i,j) * damping(i,j)
839 enddo
840 endif
841
8420 if (CS%id_src_mom_lp > 0) then
8430 do concurrent (j=js:je, i=is:ie)
8440 src_mom_lp(i,j) = src_mom_lp(i,j) * damping(i,j)
845 enddo
846 endif
847
8480 if (CS%id_src_mom_bh > 0) then
8490 do concurrent (j=js:je, i=is:ie)
8500 src_mom_bh(i,j) = src_mom_bh(i,j) * damping(i,j)
851 enddo
852 endif
853
8540 if (CS%id_src_adv > 0) then
8550 do concurrent (j=js:je, i=is:ie)
8560 src_adv(i,j) = src_adv(i,j) * damping(i,j)
857 enddo
858 endif
859
8600 if (CS%id_src_mom_K4 > 0) then
8610 do concurrent (j=js:je, i=is:ie)
8620 src_mom_K4(i,j) = src_mom_K4(i,j) * damping(i,j)
863 enddo
864 endif
865
8660 if (CS%id_src_btm_drag > 0) then
8670 do concurrent (j=js:je, i=is:ie)
868 src_btm_drag(i,j) = -MEKE_current(i,j) * (damp_step &
869 * ((damp_rate(i,j) + damp_rate_s1(i,j)) * damping(i,j)) &
8700 )
871 enddo
872 endif
873 endif
874 endif ! MEKE_KH>=0
875
87624 if (CS%debug) then
877 !$omp target update from(MEKE%MEKE)
8780 call hchksum(MEKE%MEKE, "MEKE post-update MEKE", G%HI, haloshift=0, unscale=US%L_T_to_m_s**2)
879 endif
880
881 case(EKE_FILE)
8820 call time_interp_external(CS%eke_handle, Time, data_eke, scale=US%m_s_to_L_T**2)
8830 do j=js,je ; do i=is,ie
8840 MEKE%MEKE(i,j) = data_eke(i,j) * G%mask2dT(i,j)
885 enddo ; enddo
886 !$omp target update to(MEKE%MEKE)
8870 call MEKE_lengthScales(CS, MEKE, G, GV, US, SN_u, SN_v, MEKE%MEKE, depth_tot, bottomFac2, barotrFac2, LmixScale)
888 !$omp target update to(bottomFac2, barotrFac2, LmixScale)
889 case(EKE_DBCLIENT)
8900 call pass_vector(u, v, G%Domain)
8910 call MEKE_lengthScales(CS, MEKE, G, GV, US, SN_u, SN_v, MEKE%MEKE, depth_tot, bottomFac2, barotrFac2, LmixScale)
892 !$omp target update to(bottomFac2, barotrFac2, LmixScale)
8930 call ML_MEKE_calculate_features(G, GV, US, CS, MEKE%Rd_dx_h, u, v, tv, h, dt, features_array)
8940 call predict_MEKE(G, US, CS, SIZE(h), Time, features_array, MEKE%MEKE)
895 !$omp target update to(MEKE%MEKE)
896 case default
89724 call MOM_error(FATAL,"Invalid method specified for calculating EKE")
898 end select
899
90024 if (CS%MEKE_positive) then
9010 do concurrent (j=js:je, i=is:ie)
9020 MEKE%MEKE(i,j) = MAX(0., MEKE%MEKE(i,j))
903 enddo
904 endif
905
90624 call cpu_clock_begin(CS%id_clock_pass)
90724 call do_group_pass(CS%pass_MEKE, G%Domain, omp_offload=.true.)
90824 call cpu_clock_end(CS%id_clock_pass)
909
910 ! Calculate diffusivity for main model to use
91124 if (CS%MEKE_KhCoeff>0.) then
91224 if (.not.CS%MEKE_GEOMETRIC) then
91324 if (CS%use_old_lscale) then
9140 if (CS%Rd_as_max_scale) then
9150 do concurrent (j=js:je, i=is:ie)
916 MEKE%Kh(i,j) = (CS%MEKE_KhCoeff * &
917 sqrt(2.*max(0.,barotrFac2(i,j)*MEKE%MEKE(i,j))*G%areaT(i,j)) ) * &
9180 min(MEKE%Rd_dx_h(i,j), 1.0)
919 enddo
920 else
9210 do concurrent (j=js:je, i=is:ie)
922 MEKE%Kh(i,j) = CS%MEKE_KhCoeff * &
9230 sqrt(2.*max(0., barotrFac2(i,j)*MEKE%MEKE(i,j))*G%areaT(i,j))
924 enddo
925 endif
926 else
92724 do concurrent (j=js:je, i=is:ie)
928 MEKE%Kh(i,j) = CS%MEKE_KhCoeff * &
929175704 sqrt(2.*max(0., barotrFac2(i,j)*MEKE%MEKE(i,j))) * LmixScale(i,j)
930 enddo
931 endif
932 endif
933 endif
934
935 ! Calculate viscosity for the main model to use
93624 if (CS%viscosity_coeff_Ku /=0.) then
9370 do concurrent (j=js:je, i=is:ie)
9380 MEKE%Ku(i,j) = CS%viscosity_coeff_Ku * sqrt(2.*max(0.,MEKE%MEKE(i,j))) * LmixScale(i,j)
939 enddo
940 endif
941
94224 if (CS%viscosity_coeff_Au /=0.) then
9430 do concurrent (j=js:je, i=is:ie)
9440 MEKE%Au(i,j) = CS%viscosity_coeff_Au * sqrt(2.*max(0.,MEKE%MEKE(i,j))) * LmixScale(i,j)**3
945 enddo
946 endif
947
948 if (allocated(MEKE%Kh) .or. allocated(MEKE%Ku) .or. allocated(MEKE%Au) &
94924 .or. allocated(MEKE%Le)) then
95024 call cpu_clock_begin(CS%id_clock_pass)
95124 call do_group_pass(CS%pass_Kh, G%Domain, omp_offload=.true.)
95224 call cpu_clock_end(CS%id_clock_pass)
953 endif
954
955 ! Everything below runs on the host: sync the prognostic fields and any posted
956 ! scratch down from the device. MEKE%MEKE and MEKE%Kh in particular are read on the
957 ! host by thickness_diffuse and the restart machinery.
958 !$omp target update from(MEKE%MEKE)
959 if (allocated(MEKE%Kh)) then
960 !$omp target update from(MEKE%Kh)
961 endif
962 if (allocated(MEKE%Ku)) then
963 !$omp target update from(MEKE%Ku)
964 endif
965 if (allocated(MEKE%Au)) then
966 !$omp target update from(MEKE%Au)
967 endif
968 if (allocated(MEKE%Le)) then
969 !$omp target update from(MEKE%Le)
970 endif
971 if (CS%id_Ub>0 .or. CS%id_gamma_b>0) then
972 !$omp target update from(bottomFac2)
973 endif
974 if (CS%id_Ut>0 .or. CS%id_gamma_t>0) then
975 !$omp target update from(barotrFac2)
976 endif
977 if (CS%id_Le>0) then
978 !$omp target update from(LmixScale)
979 endif
980 if (CS%id_KhMEKE_u>0) then
981 !$omp target update from(Kh_u)
982 endif
983 if (CS%id_KhMEKE_v>0) then
984 !$omp target update from(Kh_v)
985 endif
986 if (CS%id_src>0) then
987 !$omp target update from(src)
988 endif
989 if (CS%id_src_adv>0) then
990 !$omp target update from(src_adv)
991 endif
992 if (CS%id_src_mom_K4>0) then
993 !$omp target update from(src_mom_K4)
994 endif
995 if (CS%id_src_btm_drag>0) then
996 !$omp target update from(src_btm_drag)
997 endif
998 if (CS%id_src_GM>0) then
999 !$omp target update from(src_GM)
1000 endif
1001 if (CS%id_src_mom_lp>0) then
1002 !$omp target update from(src_mom_lp)
1003 endif
1004 if (CS%id_src_mom_bh>0) then
1005 !$omp target update from(src_mom_bh)
1006 endif
1007 if (CS%id_decay>0) then
1008 !$omp target update from(MEKE_decay)
1009 endif
1010
1011 ! Offer fields for averaging.
101296 if (any([CS%id_Ue, CS%id_Ub, CS%id_Ut] > 0)) &
10130 tmp(:,:) = 0.
101424 if (CS%id_MEKE>0) call post_data(CS%id_MEKE, MEKE%MEKE, CS%diag)
101524 if (CS%id_Ue>0) then
10160 do j=js,je ; do i=is,ie
10170 tmp(i,j) = sqrt(max(0., 2. * MEKE%MEKE(i,j)))
1018 enddo ; enddo
10190 call post_data(CS%id_Ue, tmp, CS%diag)
1020 endif
102124 if (CS%id_Ub>0) then
10220 do j=js,je ; do i=is,ie
10230 tmp(i,j) = sqrt(max(0., 2. * MEKE%MEKE(i,j) * bottomFac2(i,j)))
1024 enddo ; enddo
10250 call post_data(CS%id_Ub, tmp, CS%diag)
1026 endif
102724 if (CS%id_Ut>0) then
10280 do j=js,je ; do i=is,ie
10290 tmp(i,j) = sqrt(max(0., 2. * MEKE%MEKE(i,j) * barotrFac2(i,j)))
1030 enddo ; enddo
10310 call post_data(CS%id_Ut, tmp, CS%diag)
1032 endif
103324 if (CS%id_Kh>0) call post_data(CS%id_Kh, MEKE%Kh, CS%diag)
103424 if (CS%id_Ku>0) call post_data(CS%id_Ku, MEKE%Ku, CS%diag)
103524 if (CS%id_Au>0) call post_data(CS%id_Au, MEKE%Au, CS%diag)
103624 if (CS%id_KhMEKE_u>0) call post_data(CS%id_KhMEKE_u, Kh_u, CS%diag)
103724 if (CS%id_KhMEKE_v>0) call post_data(CS%id_KhMEKE_v, Kh_v, CS%diag)
103824 if (CS%id_src>0) call post_data(CS%id_src, src, CS%diag)
103924 if (CS%id_src_adv>0) call post_data(CS%id_src_adv, src_adv, CS%diag)
104024 if (CS%id_src_mom_K4>0) call post_data(CS%id_src_mom_K4, src_mom_K4, CS%diag)
104124 if (CS%id_src_btm_drag>0) call post_data(CS%id_src_btm_drag, src_btm_drag, CS%diag)
104224 if (CS%id_src_GM>0) call post_data(CS%id_src_GM, src_GM, CS%diag)
104324 if (CS%id_src_mom_lp>0) call post_data(CS%id_src_mom_lp, src_mom_lp, CS%diag)
104424 if (CS%id_src_mom_bh>0) call post_data(CS%id_src_mom_bh, src_mom_bh, CS%diag)
104524 if (CS%id_decay>0) call post_data(CS%id_decay, MEKE_decay, CS%diag)
104624 if (CS%id_GM_src>0) call post_data(CS%id_GM_src, MEKE%GM_src, CS%diag)
104724 if (CS%id_mom_src>0) call post_data(CS%id_mom_src, MEKE%mom_src, CS%diag)
104824 if (CS%id_mom_src_bh>0) call post_data(CS%id_mom_src_bh, MEKE%mom_src_bh, CS%diag)
104924 if (CS%id_GME_snk>0) call post_data(CS%id_GME_snk, MEKE%GME_snk, CS%diag)
105024 if (CS%id_Le>0) call post_data(CS%id_Le, LmixScale, CS%diag)
105124 if (CS%id_gamma_b>0) then
10520 do j=js,je ; do i=is,ie
10530 bottomFac2(i,j) = sqrt(bottomFac2(i,j))
1054 enddo ; enddo
10550 call post_data(CS%id_gamma_b, bottomFac2, CS%diag)
1056 endif
105724 if (CS%id_gamma_t>0) then
10580 do j=js,je ; do i=is,ie
10590 barotrFac2(i,j) = sqrt(barotrFac2(i,j))
1060 enddo ; enddo
10610 call post_data(CS%id_gamma_t, barotrFac2, CS%diag)
1062 endif
1063
1064 !$omp target exit data map(release: SN_u, SN_v)
1065 !$omp target exit data map(release: mass, I_mass, depth_tot, src, drag_rate, drag_rate_visc)
1066 !$omp target exit data map(release: drag_vel_u, drag_vel_v, bottomFac2, barotrFac2, LmixScale)
1067 !$omp target exit data map(release: MEKE_uflux, MEKE_vflux, Kh_u, Kh_v, baroHu, baroHv)
1068 !$omp target exit data map(release: damp_rate, damping, MEKE_current, damp_rate_s1)
1069 !$omp target exit data map(release: del2MEKE, del4MEKE, MEKE_decay, equilibrium_value)
1070 !$omp target exit data map(release: src_adv, src_GM, src_mom_K4, src_btm_drag, src_mom_lp, src_mom_bh)
1071
1072end subroutine step_forward_MEKE
1073
1074!> Calculates the equilibrium solution where the source depends only on MEKE diffusivity
1075!! and there is no lateral diffusion of MEKE.
1076!! Results is in MEKE%MEKE.
10771subroutine MEKE_equilibrium(CS, MEKE, G, GV, US, SN_u, SN_v, drag_rate_visc, I_mass, depth_tot)
1078 type(ocean_grid_type), intent(inout) :: G !< Ocean grid.
1079 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure.
1080 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1081 type(MEKE_CS), intent(in) :: CS !< MEKE control structure.
1082 type(MEKE_type), intent(inout) :: MEKE !< MEKE fields
1083 real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: SN_u !< Eady growth rate at u-points [T-1 ~> s-1].
1084 real, dimension(SZI_(G),SZJB_(G)), intent(in) :: SN_v !< Eady growth rate at v-points [T-1 ~> s-1].
1085 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: drag_rate_visc !< Mean flow velocity contribution
1086 !! to the MEKE drag rate [H T-1 ~> m s-1 or kg m-2 s-1]
1087 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: I_mass !< Inverse of column mass [R-1 Z-1 ~> m2 kg-1].
1088 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: depth_tot !< The thickness of the water column [H ~> m or kg m-2].
1089
1090 ! Local variables
1091 real :: beta ! Combined topographic and planetary vorticity gradient [T-1 L-1 ~> s-1 m-1]
1092 real :: SN ! The local Eady growth rate [T-1 ~> s-1]
1093 real :: bottomFac2, barotrFac2 ! Vertical structure factors [nondim]
1094 real :: LmixScale, LRhines, LEady ! Various mixing length scales [L ~> m]
1095 real :: KhCoeff ! A copy of MEKE_KhCoeff from the control structure [nondim]
1096 real :: Kh ! A lateral diffusivity [L2 T-1 ~> m2 s-1]
1097 real :: Ubg2 ! Background (tidal?) velocity squared [L2 T-2 ~> m2 s-2]
1098 real :: cd2 ! The square of the drag coefficient times unit conversion factors [H2 L-2 ~> nondim or kg2 m-6]
1099 real :: drag_rate ! The MEKE spindown timescale due to bottom drag [T-1 ~> s-1].
1100 real :: src ! The sum of MEKE sources [L2 T-3 ~> W kg-1]
1101 real :: ldamping ! The MEKE damping rate [T-1 ~> s-1].
1102 real :: EKE, EKEmin, EKEmax, EKEerr ! [L2 T-2 ~> m2 s-2]
1103 real :: resid, ResMin, ResMax ! Residuals [L2 T-3 ~> W kg-1]
1104 real :: FatH ! Coriolis parameter at h points, used to compute topographic beta [T-1 ~> s-1]
1105 real :: beta_topo_x, beta_topo_y ! Topographic PV gradients in x and y [T-1 L-1 ~> s-1 m-1]
1106 real :: h_neglect ! A negligible thickness [H ~> m or kg m-2]
1107 integer :: i, j, is, ie, js, je, n1, n2
1108 real :: tolerance ! Width of EKE bracket [L2 T-2 ~> m2 s-2].
1109 logical :: useSecant, debugIteration
1110
11111 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
1112
11131 debugIteration = .false.
11141 KhCoeff = CS%MEKE_KhCoeff
11151 Ubg2 = CS%MEKE_Uscale**2
11161 cd2 = CS%cdrag**2
11171 tolerance = 1.0e-12*US%m_s_to_L_T**2
11181 h_neglect = GV%H_subroundoff
1119
1120!$OMP do
11217261 do j=js,je ; do i=is,ie
1122 ! SN = 0.25*max( (SN_u(I,j) + SN_u(I-1,j)) + (SN_v(i,J) + SN_v(i,J-1)), 0.)
1123 ! This avoids extremes values in equilibrium solution due to bad values in SN_u, SN_v
11247200 SN = min(SN_u(I,j), SN_u(I-1,j), SN_v(i,J), SN_v(i,J-1))
1125
11267260 if (CS%MEKE_equilibrium_alt) then
11270 MEKE%MEKE(i,j) = (CS%MEKE_GEOMETRIC_alpha * SN * depth_tot(i,j))**2 / cd2
1128 else
1129 FatH = 0.25*((G%CoriolisBu(I,J) + G%CoriolisBu(I-1,J-1)) + &
11307200 (G%CoriolisBu(I-1,J) + G%CoriolisBu(I,J-1))) ! Coriolis parameter at h points
1131
1132 ! Since zero-bathymetry cells are masked, this avoids calculations on land
11337200 if (CS%MEKE_topographic_beta == 0. .or. (depth_tot(i,j) == 0.0)) then
11347200 beta_topo_x = 0. ; beta_topo_y = 0.
1135 else
1136 !### Consider different combinations of these estimates of topographic beta.
1137 beta_topo_x = -CS%MEKE_topographic_beta * FatH * 0.5 * ( &
1138 (depth_tot(i+1,j)-depth_tot(i,j)) * G%IdxCu(I,j) &
1139 / max(depth_tot(i+1,j), depth_tot(i,j), h_neglect) &
1140 + (depth_tot(i,j)-depth_tot(i-1,j)) * G%IdxCu(I-1,j) &
11410 / max(depth_tot(i,j), depth_tot(i-1,j), h_neglect) )
1142 beta_topo_y = -CS%MEKE_topographic_beta * FatH * 0.5 * ( &
1143 (depth_tot(i,j+1)-depth_tot(i,j)) * G%IdyCv(i,J) &
1144 / max(depth_tot(i,j+1), depth_tot(i,j), h_neglect) + &
1145 (depth_tot(i,j)-depth_tot(i,j-1)) * G%IdyCv(i,J-1) &
11460 / max(depth_tot(i,j), depth_tot(i,j-1), h_neglect) )
1147 endif
1148 beta = sqrt(((G%dF_dx(i,j) + beta_topo_x)**2) + &
11497200 ((G%dF_dy(i,j) + beta_topo_y)**2) )
1150
11517200 if (KhCoeff*SN*I_mass(i,j)>0.) then
1152 ! Solve resid(E) = 0, where resid = Kh(E) * (SN)^2 - damp_rate(E) E
11534694 EKEmin = 0. ! Use the trivial root as the left bracket
11544694 ResMin = 0. ! Need to detect direction of left residual
11554694 EKEmax = 0.01*US%m_s_to_L_T**2 ! First guess at right bracket
11564694 useSecant = .false. ! Start using a bisection method
1157
1158 ! First find right bracket for which resid<0
11594694 resid = 1.0*US%m_to_L**2*US%T_to_s**3 ; n1 = 0
11609388 do while (resid>0.)
11614694 n1 = n1 + 1
11624694 EKE = EKEmax
1163 call MEKE_lengthScales_0d(CS, US, G%areaT(i,j), beta, depth_tot(i,j), &
1164 MEKE%Rd_dx_h(i,j), SN, EKE, &
11654694 bottomFac2, barotrFac2, LmixScale, LRhines, LEady)
1166 ! TODO: Should include resolution function in Kh
11674694 Kh = (KhCoeff * sqrt(2.*barotrFac2*EKE) * LmixScale)
11684694 src = Kh * (SN * SN)
11694694 drag_rate = (GV%H_to_RZ * I_mass(i,j)) * sqrt(drag_rate_visc(i,j)**2 + cd2 * ( 2.0*bottomFac2*EKE + Ubg2 ) )
11704694 ldamping = CS%MEKE_damping + drag_rate * bottomFac2
11714694 resid = src - ldamping * EKE
1172 ! if (debugIteration) then
1173 ! write(0,*) n1, 'EKE=',EKE,'resid=',resid
1174 ! write(0,*) 'EKEmin=',EKEmin,'ResMin=',ResMin
1175 ! write(0,*) 'src=',src,'ldamping=',ldamping
1176 ! write(0,*) 'gamma-b=',bottomFac2,'gamma-t=',barotrFac2
1177 ! write(0,*) 'drag_visc=',drag_rate_visc(i,j),'Ubg2=',Ubg2
1178 ! endif
11794694 if (resid>0.) then ! EKE is to the left of the root
11800 EKEmin = EKE ! so we move the left bracket here
11810 EKEmax = 10. * EKE ! and guess again for the right bracket
11820 if (resid<ResMin) useSecant = .true.
11830 ResMin = resid
11840 if (EKEmax > 2.e17*US%m_s_to_L_T**2) then
11850 if (debugIteration) stop 'Something has gone very wrong'
11860 debugIteration = .true.
11870 resid = 1. ; n1 = 0
11880 EKEmin = 0. ; ResMin = 0.
11890 EKEmax = 0.01*US%m_s_to_L_T**2
11900 useSecant = .false.
1191 endif
1192 endif
1193 enddo ! while(resid>0.) searching for right bracket
11944694 ResMax = resid
1195
1196 ! Bisect the bracket
11974694 n2 = 0 ; EKEerr = EKEmax - EKEmin
1198137051 do while (EKEerr > tolerance)
1199132357 n2 = n2 + 1
1200132357 if (useSecant) then
120111501 EKE = EKEmin + (EKEmax - EKEmin) * (ResMin / (ResMin - ResMax))
1202 else
1203120856 EKE = 0.5 * (EKEmin + EKEmax)
1204 endif
1205132357 EKEerr = min( EKE-EKEmin, EKEmax-EKE )
1206 ! TODO: Should include resolution function in Kh
1207132357 Kh = (KhCoeff * sqrt(2.*barotrFac2*EKE) * LmixScale)
1208132357 src = Kh * (SN * SN)
1209132357 drag_rate = (GV%H_to_RZ * I_mass(i,j)) * sqrt( drag_rate_visc(i,j)**2 + cd2 * ( 2.0*bottomFac2*EKE + Ubg2 ) )
1210132357 ldamping = CS%MEKE_damping + drag_rate * bottomFac2
1211132357 resid = src - ldamping * EKE
1212132357 if (useSecant .and. resid>ResMin) useSecant = .false.
1213132357 if (resid>0.) then ! EKE is to the left of the root
121420693 EKEmin = EKE ! so we move the left bracket here
121520693 if (resid<ResMin) useSecant = .true.
121620693 ResMin = resid ! Save this for the secant method
1217111664 elseif (resid<0.) then ! EKE is to the right of the root
1218111664 EKEmax = EKE ! so we move the right bracket here
1219111664 ResMax = resid ! Save this for the secant method
1220 else
12210 exit ! resid=0 => EKE is exactly at the root
1222 endif
1223132357 if (n2>200) stop 'Failing to converge?'
1224 enddo ! while(EKEmax-EKEmin>tolerance)
1225
1226 else
12272506 EKE = 0.
1228 endif
12297200 MEKE%MEKE(i,j) = EKE
1230 endif
1231 enddo ; enddo
1232
12331end subroutine MEKE_equilibrium
1234
1235
1236!< This subroutine calculates a new equilibrium value for MEKE at each time step. This is not copied into
1237!! MEKE%MEKE; rather, it is used as a restoring term to nudge MEKE%MEKE back to an equilibrium value
12380subroutine MEKE_equilibrium_restoring(CS, G, GV, US, SN_u, SN_v, depth_tot, &
12390 equilibrium_value)
1240 type(ocean_grid_type), intent(inout) :: G !< Ocean grid.
1241 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure.
1242 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type.
1243 type(MEKE_CS), intent(in) :: CS !< MEKE control structure.
1244 real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: SN_u !< Eady growth rate at u-points [T-1 ~> s-1].
1245 real, dimension(SZI_(G),SZJB_(G)), intent(in) :: SN_v !< Eady growth rate at v-points [T-1 ~> s-1].
1246 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: depth_tot !< The thickness of the water column [H ~> m or kg m-2].
1247 real, dimension(SZI_(G),SZJ_(G)), intent(out) :: equilibrium_value
1248 !< Equilbrium value of MEKE to be calculated at each time step [L2 T-2 ~> m2 s-2]
1249
1250 ! Local variables
1251 real :: SN ! The local Eady growth rate [T-1 ~> s-1]
1252 integer :: i, j, is, ie, js, je ! local indices
1253 real :: cd2 ! The square of the drag coefficient [nondim]
1254
12550 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
12560 cd2 = CS%cdrag**2
12570 equilibrium_value(:,:) = 0.0
1258
1259!$OMP do
12600 do j=js,je ; do i=is,ie
1261 ! SN = 0.25*max( (SN_u(I,j) + SN_u(I-1,j)) + (SN_v(i,J) + SN_v(i,J-1)), 0.)
1262 ! This avoids extremes values in equilibrium solution due to bad values in SN_u, SN_v
12630 SN = min(SN_u(I,j), SN_u(I-1,j), SN_v(i,J), SN_v(i,J-1))
12640 equilibrium_value(i,j) = (CS%MEKE_GEOMETRIC_alpha * SN * depth_tot(i,j))**2 / cd2
1265 enddo ; enddo
1266
12670 if (CS%id_MEKE_equilibrium>0) call post_data(CS%id_MEKE_equilibrium, equilibrium_value, CS%diag)
12680end subroutine MEKE_equilibrium_restoring
1269
1270!> Calculates the eddy mixing length scale and \f$\gamma_b\f$ and \f$\gamma_t\f$
1271!! functions that are ratios of either bottom or barotropic eddy energy to the
1272!! column eddy energy, respectively. See \ref section_MEKE_equations.
127324subroutine MEKE_lengthScales(CS, MEKE, G, GV, US, SN_u, SN_v, EKE, depth_tot, &
127424 bottomFac2, barotrFac2, LmixScale)
1275 type(MEKE_CS), intent(in) :: CS !< MEKE control structure.
1276 type(MEKE_type), intent(in) :: MEKE !< MEKE field
1277 type(ocean_grid_type), intent(inout) :: G !< Ocean grid.
1278 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure.
1279 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1280 real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: SN_u !< Eady growth rate at u-points [T-1 ~> s-1].
1281 real, dimension(SZI_(G),SZJB_(G)), intent(in) :: SN_v !< Eady growth rate at v-points [T-1 ~> s-1].
1282 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: EKE !< Eddy kinetic energy [L2 T-2 ~> m2 s-2].
1283 real, dimension(SZI_(G),SZJ_(G)), intent(in) :: depth_tot !< The thickness of the water column [H ~> m or kg m-2].
1284 real, dimension(SZI_(G),SZJ_(G)), intent(out) :: bottomFac2 !< gamma_b^2 [nondim]
1285 real, dimension(SZI_(G),SZJ_(G)), intent(out) :: barotrFac2 !< gamma_t^2 [nondim]
1286 real, dimension(SZI_(G),SZJ_(G)), intent(out) :: LmixScale !< Eddy mixing length [L ~> m].
1287 ! Local variables
128848 real, dimension(SZI_(G),SZJ_(G)) :: LRhines, LEady ! Possible mixing length scales [L ~> m]
1289 real :: beta ! Combined topographic and planetary vorticity gradient [T-1 L-1 ~> s-1 m-1]
1290 real :: SN ! The local Eady growth rate [T-1 ~> s-1]
1291 real :: FatH ! Coriolis parameter at h points [T-1 ~> s-1]
1292 real :: beta_topo_x, beta_topo_y ! Topographic PV gradients in x and y [T-1 L-1 ~> s-1 m-1]
1293 real :: h_neglect ! A negligible thickness [H ~> m or kg m-2]
1294 integer :: i, j, is, ie, js, je
1295
129624 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
129724 h_neglect = GV%H_subroundoff
1298
1299 ! TODO: THIS RUNS ON THE HOST, BE WARY! AAAAAA (see the note at the call site in
1300 ! step_forward_MEKE: the intrinsic pow in MEKE_lengthScales_0d is not bitwise CPU==GPU)
1301!$OMP do
1302174264 do j=js,je ; do i=is,ie
1303172800 if (.not.CS%use_old_lscale) then
1304172800 if (CS%aEady > 0.) then
1305172800 SN = 0.25 * ( (SN_u(I,j) + SN_u(I-1,j)) + (SN_v(i,J) + SN_v(i,J-1)) )
1306 else
13070 SN = 0.
1308 endif
1309 FatH = 0.25* ( ( G%CoriolisBu(I,J) + G%CoriolisBu(I-1,J-1) ) + &
1310172800 ( G%CoriolisBu(I-1,J) + G%CoriolisBu(I,J-1) ) ) ! Coriolis parameter at h points
1311
1312 ! If depth_tot is zero, then a division by zero FPE will be raised. In this
1313 ! case, we apply Adcroft's rule of reciprocals and set the term to zero.
1314 ! Since zero-bathymetry cells are masked, this should not affect values.
1315172800 if (CS%MEKE_topographic_beta == 0. .or. (depth_tot(i,j) == 0.0)) then
1316172800 beta_topo_x = 0. ; beta_topo_y = 0.
1317 else
1318 !### Consider different combinations of these estimates of topographic beta.
1319 beta_topo_x = -CS%MEKE_topographic_beta * FatH * 0.5 * ( &
1320 (depth_tot(i+1,j)-depth_tot(i,j)) * G%IdxCu(I,j) &
1321 / max(depth_tot(i+1,j), depth_tot(i,j), h_neglect) &
1322 + (depth_tot(i,j)-depth_tot(i-1,j)) * G%IdxCu(I-1,j) &
13230 / max(depth_tot(i,j), depth_tot(i-1,j), h_neglect) )
1324 beta_topo_y = -CS%MEKE_topographic_beta * FatH * 0.5 * ( &
1325 (depth_tot(i,j+1)-depth_tot(i,j)) * G%IdyCv(i,J) &
1326 / max(depth_tot(i,j+1), depth_tot(i,j), h_neglect) + &
1327 (depth_tot(i,j)-depth_tot(i,j-1)) * G%IdyCv(i,J-1) &
13280 / max(depth_tot(i,j), depth_tot(i,j-1), h_neglect) )
1329 endif
1330 beta = sqrt(((G%dF_dx(i,j) + beta_topo_x)**2) + &
1331172800 ((G%dF_dy(i,j) + beta_topo_y)**2) )
1332
1333 else
13340 beta = 0.
1335 endif
1336 ! Returns bottomFac2, barotrFac2 and LmixScale
1337 call MEKE_lengthScales_0d(CS, US, G%areaT(i,j), beta, depth_tot(i,j), &
1338 MEKE%Rd_dx_h(i,j), SN, MEKE%MEKE(i,j), &
1339 bottomFac2(i,j), barotrFac2(i,j), LmixScale(i,j), &
1340174240 LRhines(i,j), LEady(i,j))
1341 enddo ; enddo
134224 if (CS%id_Lrhines>0) call post_data(CS%id_LRhines, LRhines, CS%diag)
134324 if (CS%id_Leady>0) call post_data(CS%id_LEady, LEady, CS%diag)
1344
134524end subroutine MEKE_lengthScales
1346
1347!> Calculates the eddy mixing length scale and \f$\gamma_b\f$ and \f$\gamma_t\f$
1348!! functions that are ratios of either bottom or barotropic eddy energy to the
1349!! column eddy energy, respectively. See \ref section_MEKE_equations.
1350177494pure subroutine MEKE_lengthScales_0d(CS, US, area, beta, depth_tot, Rd_dx, SN, EKE, &
1351 bottomFac2, barotrFac2, LmixScale, Lrhines, Leady)
1352 type(MEKE_CS), intent(in) :: CS !< MEKE control structure.
1353 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1354 real, intent(in) :: area !< Grid cell area [L2 ~> m2]
1355 real, intent(in) :: beta !< Planetary beta = \f$ \nabla f\f$ [T-1 L-1 ~> s-1 m-1]
1356 real, intent(in) :: depth_tot !< The total thickness of the water column [H ~> m or kg m-2]
1357 real, intent(in) :: Rd_dx !< Resolution Ld/dx [nondim].
1358 real, intent(in) :: SN !< Eady growth rate [T-1 ~> s-1].
1359 real, intent(in) :: EKE !< Eddy kinetic energy [L2 T-2 ~> m2 s-2].
1360 real, intent(out) :: bottomFac2 !< gamma_b^2 [nondim]
1361 real, intent(out) :: barotrFac2 !< gamma_t^2 [nondim]
1362 real, intent(out) :: LmixScale !< Eddy mixing length [L ~> m].
1363 real, intent(out) :: Lrhines !< Rhines length scale [L ~> m].
1364 real, intent(out) :: Leady !< Eady length scale [L ~> m].
1365 ! Local variables
1366 real :: Lgrid, Ldeform, Lfrict ! Length scales [L ~> m]
1367 real :: Ue ! An eddy velocity [L T-1 ~> m s-1]
1368
1369 ! Length scale for MEKE derived diffusivity
1370177494 Lgrid = sqrt(area) ! Grid scale
1371177494 Ldeform = Lgrid * Rd_dx ! Deformation scale
1372177494 Lfrict = depth_tot / CS%cdrag ! Frictional arrest scale
1373 ! gamma_b^2 is the ratio of bottom eddy energy to mean column eddy energy
1374 ! used in calculating bottom drag
1375177494 bottomFac2 = CS%MEKE_CD_SCALE**2
1376177494 if (Lfrict*CS%MEKE_Cb>0.) bottomFac2 = bottomFac2 + 1./( 1. + CS%MEKE_Cb*(Ldeform/Lfrict) )**0.8
1377177494 bottomFac2 = max(bottomFac2, CS%MEKE_min_gamma)
1378 ! gamma_t^2 is the ratio of barotropic eddy energy to mean column eddy energy
1379 ! used in the velocity scale for diffusivity
1380177494 barotrFac2 = 1.
1381177494 if (Lfrict*CS%MEKE_Ct>0.) barotrFac2 = 1. / ( 1. + CS%MEKE_Ct*(Ldeform/Lfrict) )**0.25
1382177494 barotrFac2 = max(barotrFac2, CS%MEKE_min_gamma)
1383177494 if (CS%use_old_lscale) then
13840 if (CS%Rd_as_max_scale) then
13850 LmixScale = min(Ldeform, Lgrid) ! The smaller of Ld or dx
1386 else
13870 LmixScale = Lgrid
1388 endif
1389 else
1390177494 Ue = sqrt( 2.0 * max( 0., barotrFac2*EKE ) ) ! Barotropic eddy flow scale
1391177494 Lrhines = sqrt( Ue / max( beta, 1.e-30*US%T_to_s*US%L_to_m ) ) ! Rhines scale
1392177494 if (CS%aEady > 0.) then
1393177494 Leady = Ue / max( SN, 1.e-15*US%T_to_s ) ! Bound Eady time-scale < 1e15 seconds
1394 else
13950 Leady = 0.
1396 endif
1397177494 if (CS%use_min_lscale) then
13980 LmixScale = CS%lscale_maxval
13990 if (CS%aDeform*Ldeform > 0.) LmixScale = min(LmixScale,CS%aDeform*Ldeform)
14000 if (CS%aFrict *Lfrict > 0.) LmixScale = min(LmixScale,CS%aFrict *Lfrict)
14010 if (CS%aRhines*Lrhines > 0.) LmixScale = min(LmixScale,CS%aRhines*Lrhines)
14020 if (CS%aEady *Leady > 0.) LmixScale = min(LmixScale,CS%aEady *Leady)
14030 if (CS%aGrid *Lgrid > 0.) LmixScale = min(LmixScale,CS%aGrid *Lgrid)
14040 if (CS%Lfixed > 0.) LmixScale = min(LmixScale,CS%Lfixed)
1405 else
1406177494 LmixScale = 0.
1407177494 if (CS%aDeform*Ldeform > 0.) LmixScale = LmixScale + 1./(CS%aDeform*Ldeform)
1408177494 if (CS%aFrict *Lfrict > 0.) LmixScale = LmixScale + 1./(CS%aFrict *Lfrict)
1409177494 if (CS%aRhines*Lrhines > 0.) LmixScale = LmixScale + 1./(CS%aRhines*Lrhines)
1410177494 if (CS%aEady *Leady > 0.) LmixScale = LmixScale + 1./(CS%aEady *Leady)
1411177494 if (CS%aGrid *Lgrid > 0.) LmixScale = LmixScale + 1./(CS%aGrid *Lgrid)
1412177494 if (CS%Lfixed > 0.) LmixScale = LmixScale + 1./CS%Lfixed
1413177494 if (LmixScale > 0.) LmixScale = 1. / LmixScale
1414 endif
1415 endif
1416
1417177494end subroutine MEKE_lengthScales_0d
1418
1419!> Initializes the MOM_MEKE module and reads parameters.
1420!! Returns True if module is to be used, otherwise returns False.
14211logical function MEKE_init(Time, G, GV, US, param_file, diag, dbcomms_CS, CS, MEKE, restart_CS, meke_in_dynamics)
1422 type(time_type), intent(in) :: Time !< The current model time.
1423 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
1424 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure.
1425 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1426 type(param_file_type), intent(in) :: param_file !< Parameter file parser structure.
1427 type(dbcomms_CS_type), intent(in) :: dbcomms_CS !< Database communications control structure
1428 type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics structure.
1429 type(MEKE_CS), intent(inout) :: CS !< MEKE control structure.
1430 type(MEKE_type), intent(inout) :: MEKE !< MEKE fields
1431 type(MOM_restart_CS), intent(in) :: restart_CS !< MOM restart control structure
1432 logical, intent( out) :: meke_in_dynamics !< If true, MEKE is stepped forward in dynamics
1433 !! otherwise in tracer dynamics
1434
1435 ! Local variables
1436 real :: MEKE_restoring_timescale ! The timescale used to nudge MEKE toward its equilibrium value [T ~> s]
1437 real :: cdrag ! The default bottom drag coefficient [nondim].
1438 character(len=200) :: eke_filename, eke_varname, inputdir
1439 character(len=16) :: eke_source_str
1440 integer :: i, j, is, ie, js, je, isd, ied, jsd, jed
1441 logical :: laplacian, biharmonic, coldStart
1442 ! This include declares and sets the variable "version".
1443# include "version_variable.h"
1444 character(len=40) :: mdl = "MOM_MEKE" ! This module's name.
1445
14461 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
14471 isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed
1448
1449 ! Determine whether this module will be used
14501 call get_param(param_file, mdl, "USE_MEKE", MEKE_init, default=.false., do_not_log=.true.)
14511 call log_version(param_file, mdl, version, "", all_default=.not.MEKE_init)
1452 call get_param(param_file, mdl, "USE_MEKE", MEKE_init, &
1453 "If true, turns on the MEKE scheme which calculates "// &
1454 "a sub-grid mesoscale eddy kinetic energy budget.", &
14551 default=.false.)
14561 if (.not. MEKE_init) return
14571 CS%initialized = .true.
1458 call get_param(param_file, mdl, "MEKE_IN_DYNAMICS", meke_in_dynamics, &
1459 "If true, step MEKE forward with the dynamics "// &
1460 "otherwise with the tracer timestep.", &
14611 default=.true.)
1462
1463 call get_param(param_file, mdl, "EKE_SOURCE", eke_source_str, &
1464 "Determine the where EKE comes from:\n" // &
1465 " 'prog': Calculated solving EKE equation\n"// &
1466 " 'file': Read in from a file\n" // &
14671 " 'dbclient': Retrieved from ML-database", default='prog')
1468
14691 call MOM_mesg("MEKE_init: reading parameters ", 5)
1470
14711 select case (lowercase(eke_source_str))
1472 case("file")
14730 CS%eke_src = EKE_FILE
14740 call time_interp_external_init
1475 call get_param(param_file, mdl, "EKE_FILE", eke_filename, &
1476 "A file in which to find the eddy kineteic energy variable.", &
14770 default="eke_file.nc")
1478 call get_param(param_file, mdl, "EKE_VARIABLE", eke_varname, &
1479 "The name of the eddy kinetic energy variable to read from "//&
1480 "EKE_FILE to use in MEKE.", &
14810 default="eke")
1482 call get_param(param_file, mdl, "INPUTDIR", inputdir, &
1483 "The directory in which all input files are found.", &
14840 default=".", do_not_log=.true.)
14850 inputdir = slasher(inputdir)
1486
14870 eke_filename = trim(inputdir) // trim(eke_filename)
14880 CS%eke_handle = init_external_field(eke_filename, eke_varname, domain=G%Domain%mpp_domain)
1489 case("prog")
14901 CS%eke_src = EKE_PROG
1491 ! Read all relevant parameters and write them to the model log.
1492 call get_param(param_file, mdl, "MEKE_DAMPING", CS%MEKE_damping, &
1493 "The local depth-independent MEKE dissipation rate.", &
14941 units="s-1", default=0.0, scale=US%T_to_s)
1495 call get_param(param_file, mdl, "MEKE_CD_SCALE", CS%MEKE_Cd_scale, &
1496 "The ratio of the bottom eddy velocity to the column mean "//&
1497 "eddy velocity, i.e. sqrt(2*MEKE). This should be less than 1 "//&
1498 "to account for the surface intensification of MEKE.", &
14991 units="nondim", default=0.)
1500 call get_param(param_file, mdl, "MEKE_CB", CS%MEKE_Cb, &
1501 "A coefficient in the expression for the ratio of bottom projected "//&
1502 "eddy energy and mean column energy (see Jansen et al. 2015).",&
15031 units="nondim", default=25.)
1504 call get_param(param_file, mdl, "MEKE_MIN_GAMMA2", CS%MEKE_min_gamma, &
1505 "The minimum allowed value of gamma_b^2.",&
15061 units="nondim", default=0.0001)
1507 call get_param(param_file, mdl, "MEKE_CT", CS%MEKE_Ct, &
1508 "A coefficient in the expression for the ratio of barotropic "//&
1509 "eddy energy and mean column energy (see Jansen et al. 2015).",&
15101 units="nondim", default=50.)
1511 call get_param(param_file, mdl, "MEKE_GMCOEFF", CS%MEKE_GMcoeff, &
1512 "The efficiency of the conversion of potential energy "//&
1513 "into MEKE by the thickness mixing parameterization. "//&
1514 "If MEKE_GMCOEFF is negative, this conversion is not "//&
15151 "used or calculated.", units="nondim", default=-1.0)
1516 call get_param(param_file, mdl, "MEKE_GEOMETRIC", CS%MEKE_GEOMETRIC, &
1517 "If MEKE_GEOMETRIC is true, uses the GM coefficient formulation "//&
15181 "from the GEOMETRIC framework (Marshall et al., 2012).", default=.false.)
1519 call get_param(param_file, mdl, "MEKE_GEOMETRIC_ALPHA", CS%MEKE_GEOMETRIC_alpha, &
1520 "The nondimensional coefficient governing the efficiency of the GEOMETRIC \n"//&
15211 "thickness diffusion.", units="nondim", default=0.05)
1522 call get_param(param_file, mdl, "MEKE_EQUILIBRIUM_ALT", CS%MEKE_equilibrium_alt, &
1523 "If true, use an alternative formula for computing the (equilibrium) "//&
15241 "initial value of MEKE.", default=.false.)
1525 call get_param(param_file, mdl, "MEKE_EQUILIBRIUM_RESTORING", CS%MEKE_equilibrium_restoring, &
1526 "If true, restore MEKE back to its equilibrium value, which is calculated at "//&
15271 "each time step.", default=.false.)
15281 if (CS%MEKE_equilibrium_restoring) then
1529 call get_param(param_file, mdl, "MEKE_RESTORING_TIMESCALE", MEKE_restoring_timescale, &
1530 "The timescale used to nudge MEKE toward its equilibrium value.", &
15310 units="s", default=1e6, scale=US%s_to_T)
15320 CS%MEKE_restoring_rate = 1.0 / MEKE_restoring_timescale
1533 endif
1534
1535 call get_param(param_file, mdl, "MEKE_FRCOEFF", CS%MEKE_FrCoeff, &
1536 "The efficiency of the conversion of mean energy into "//&
1537 "MEKE. If MEKE_FRCOEFF is negative, this conversion "//&
15381 "is not used or calculated.", units="nondim", default=-1.0)
1539 call get_param(param_file, mdl, "MEKE_BHFRCOEFF", CS%MEKE_bhFrCoeff, &
1540 "The efficiency of the conversion of mean energy into "//&
1541 "MEKE by the biharmonic dissipation. If MEKE_bhFRCOEFF is negative, this conversion "//&
15421 "is not used or calculated.", units="nondim", default=-1.0)
1543 call get_param(param_file, mdl, "MEKE_GMECOEFF", CS%MEKE_GMECoeff, &
1544 "The efficiency of the conversion of MEKE into mean energy "//&
1545 "by GME. If MEKE_GMECOEFF is negative, this conversion "//&
15461 "is not used or calculated.", units="nondim", default=-1.0)
1547 call get_param(param_file, mdl, "MEKE_BGSRC", CS%MEKE_BGsrc, &
1548 "A background energy source for MEKE.", &
15491 units="W kg-1", default=0.0, scale=US%m_to_L**2*US%T_to_s**3)
1550 call get_param(param_file, mdl, "MEKE_KH", CS%MEKE_Kh, &
1551 "A background lateral diffusivity of MEKE. "//&
1552 "Use a negative value to not apply lateral diffusion to MEKE.", &
15531 units="m2 s-1", default=-1.0, scale=US%m_to_L**2*US%T_to_s)
1554 call get_param(param_file, mdl, "MEKE_K4", CS%MEKE_K4, &
1555 "A lateral bi-harmonic diffusivity of MEKE. "//&
1556 "Use a negative value to not apply bi-harmonic diffusion to MEKE.", &
15571 units="m4 s-1", default=-1.0, scale=US%m_to_L**4*US%T_to_s)
1558 call get_param(param_file, mdl, "MEKE_DTSCALE", CS%MEKE_dtScale, &
1559 "A scaling factor to accelerate the time evolution of MEKE.", &
15601 units="nondim", default=1.0)
1561 call get_param(param_file, mdl, "MEKE_POSITIVE", CS%MEKE_positive, &
1562 "If true, it guarantees that MEKE will always be >= 0.", &
15631 default=.false.)
1564 case("dbclient")
15650 CS%eke_src = EKE_DBCLIENT
15660 call ML_MEKE_init(diag, G, US, Time, param_file, dbcomms_CS, CS)
1567 case default
15681 call MOM_error(FATAL, "Invalid method selected for calculating EKE")
1569 end select
1570 ! GMM, make sure all parameters used to calculated MEKE are within the above if
1571
1572 call get_param(param_file, mdl, "MEKE_KHCOEFF", CS%MEKE_KhCoeff, &
1573 "A scaling factor in the expression for eddy diffusivity "//&
1574 "which is otherwise proportional to the MEKE velocity- "//&
1575 "scale times an eddy mixing-length. This factor "//&
1576 "must be >0 for MEKE to contribute to the thickness/ "//&
1577 "and tracer diffusivity in the rest of the model.", &
15781 units="nondim", default=1.0)
1579 call get_param(param_file, mdl, "MEKE_USCALE", CS%MEKE_Uscale, &
1580 "The background velocity that is combined with MEKE to "//&
15811 "calculate the bottom drag.", units="m s-1", default=0.0, scale=US%m_s_to_L_T)
1582 call get_param(param_file, mdl, "MEKE_GM_SRC_ALT", CS%GM_src_alt, &
1583 "If true, use the GM energy conversion form S^2*N^2*kappa rather "//&
15841 "than the streamfunction for the MEKE GM source term.", default=.false.)
1585 call get_param(param_file, mdl, "MEKE_MIN_DEPTH_TOT", CS%MEKE_min_depth_tot, &
1586 "The minimum total depth over which to distribute MEKE energy sources. "//&
1587 "When the total depth is less than this, the sources are scaled away.", &
15881 units="m", default=1.0, scale=GV%m_to_H, do_not_log=.not.CS%GM_src_alt)
1589 call get_param(param_file, mdl, "MEKE_VISC_DRAG", CS%visc_drag, &
1590 "If true, use the vertvisc_type to calculate the bottom "//&
15911 "drag acting on MEKE.", default=.true.)
1592 call get_param(param_file, mdl, "MEKE_KHTH_FAC", MEKE%KhTh_fac, &
15931 "A factor that maps MEKE%Kh to KhTh.", units="nondim", default=0.0)
1594 call get_param(param_file, mdl, "MEKE_KHTR_FAC", MEKE%KhTr_fac, &
15951 "A factor that maps MEKE%Kh to KhTr.", units="nondim", default=0.0)
1596 call get_param(param_file, mdl, "MEKE_KHMEKE_FAC", CS%KhMEKE_Fac, &
1597 "A factor that maps MEKE%Kh to Kh for MEKE itself.", &
15981 units="nondim", default=0.0)
1599 call get_param(param_file, mdl, "MEKE_OLD_LSCALE", CS%use_old_lscale, &
1600 "If true, use the old formula for length scale which is "//&
1601 "a function of grid spacing and deformation radius.", &
16021 default=.false.)
1603 call get_param(param_file, mdl, "MEKE_MIN_LSCALE", CS%use_min_lscale, &
1604 "If true, use a strict minimum of provided length scales "//&
1605 "rather than harmonic mean.", &
16061 default=.false.)
1607 call get_param(param_file, mdl, "MEKE_LSCALE_MAX_VAL", CS%lscale_maxval, &
1608 "The ceiling on the value of the MEKE length scale when MEKE_MIN_LSCALE=True. "//&
1609 "The default is the distance from the equator to the pole on Earth, as "//&
1610 "estimated by enlightenment era scientists, but should probably scale with RAD_EARTH.", &
16111 units="m", default=1.0e7, scale=US%m_to_L, do_not_log=.not.CS%use_min_lscale)
1612 call get_param(param_file, mdl, "MEKE_RD_MAX_SCALE", CS%Rd_as_max_scale, &
1613 "If true, the length scale used by MEKE is the minimum of "//&
1614 "the deformation radius or grid-spacing. Only used if "//&
16151 "MEKE_OLD_LSCALE=True", default=.false.)
1616 call get_param(param_file, mdl, "MEKE_VISCOSITY_COEFF_KU", CS%viscosity_coeff_Ku, &
1617 "If non-zero, is the scaling coefficient in the expression for "//&
1618 "viscosity used to parameterize harmonic lateral momentum mixing by "//&
1619 "unresolved eddies represented by MEKE. Can be negative to "//&
1620 "represent backscatter from the unresolved eddies.", &
16211 units="nondim", default=0.0)
1622 call get_param(param_file, mdl, "MEKE_VISCOSITY_COEFF_AU", CS%viscosity_coeff_Au, &
1623 "If non-zero, is the scaling coefficient in the expression for "//&
1624 "viscosity used to parameterize biharmonic lateral momentum mixing by "//&
1625 "unresolved eddies represented by MEKE. Can be negative to "//&
1626 "represent backscatter from the unresolved eddies.", &
16271 units="nondim", default=0.0)
1628 call get_param(param_file, mdl, "MEKE_FIXED_MIXING_LENGTH", CS%Lfixed, &
1629 "If positive, is a fixed length contribution to the expression "//&
1630 "for mixing length used in MEKE-derived diffusivity.", &
16311 units="m", default=0.0, scale=US%m_to_L)
1632 call get_param(param_file, mdl, "MEKE_FIXED_TOTAL_DEPTH", CS%fixed_total_depth, &
1633 "If true, use the nominal bathymetric depth as the estimate of the "//&
1634 "time-varying ocean depth. Otherwise base the depth on the total ocean mass "//&
16351 "per unit area.", default=.true.)
1636 call get_param(param_file, mdl, "MEKE_TOTAL_DEPTH_RHO", CS%rho_fixed_total_depth, &
1637 "A density used to translate the nominal bathymetric depth into an estimate "//&
1638 "of the total ocean mass per unit area when MEKE_FIXED_TOTAL_DEPTH is true.", &
1639 units="kg m-3", default=GV%Rho0*US%R_to_kg_m3, scale=US%kg_m3_to_R, &
16401 do_not_log=(GV%Boussinesq.or.(.not.CS%fixed_total_depth)))
1641
1642 call get_param(param_file, mdl, "MEKE_ALPHA_DEFORM", CS%aDeform, &
1643 "If positive, is a coefficient weighting the deformation scale "//&
1644 "in the expression for mixing length used in MEKE-derived diffusivity.", &
16451 units="nondim", default=0.0)
1646 call get_param(param_file, mdl, "MEKE_ALPHA_RHINES", CS%aRhines, &
1647 "If positive, is a coefficient weighting the Rhines scale "//&
1648 "in the expression for mixing length used in MEKE-derived diffusivity.", &
16491 units="nondim", default=0.0)
1650 call get_param(param_file, mdl, "MEKE_ALPHA_EADY", CS%aEady, &
1651 "If positive, is a coefficient weighting the Eady length scale "//&
1652 "in the expression for mixing length used in MEKE-derived diffusivity.", &
16531 units="nondim", default=0.0)
1654 call get_param(param_file, mdl, "MEKE_ALPHA_FRICT", CS%aFrict, &
1655 "If positive, is a coefficient weighting the frictional arrest scale "//&
1656 "in the expression for mixing length used in MEKE-derived diffusivity.", &
16571 units="nondim", default=0.0)
1658 call get_param(param_file, mdl, "MEKE_ALPHA_GRID", CS%aGrid, &
1659 "If positive, is a coefficient weighting the grid-spacing as a scale "//&
1660 "in the expression for mixing length used in MEKE-derived diffusivity.", &
16611 units="nondim", default=0.0)
1662 call get_param(param_file, mdl, "MEKE_COLD_START", coldStart, &
1663 "If true, initialize EKE to zero. Otherwise a local equilibrium solution "//&
16641 "is used as an initial condition for EKE.", default=.false.)
1665 call get_param(param_file, mdl, "MEKE_BACKSCAT_RO_C", MEKE%backscatter_Ro_c, &
1666 "The coefficient in the Rossby number function for scaling the biharmonic "//&
1667 "frictional energy source. Setting to non-zero enables the Rossby number function.", &
16681 units="nondim", default=0.0)
1669 call get_param(param_file, mdl, "MEKE_BACKSCAT_RO_POW", MEKE%backscatter_Ro_pow, &
1670 "The power in the Rossby number function for scaling the biharmonic "//&
16711 "frictional energy source.", units="nondim", default=0.0)
1672 call get_param(param_file, mdl, "MEKE_ADVECTION_FACTOR", CS%MEKE_advection_factor, &
1673 "A scale factor in front of advection of eddy energy. Zero turns advection off. "//&
1674 "Using unity would be normal but other values could accommodate a mismatch "//&
1675 "between the advecting barotropic flow and the vertical structure of MEKE.", &
16761 units="nondim", default=0.0)
1677 call get_param(param_file, mdl, "MEKE_ADVECTION_BUG", CS%MEKE_advection_bug, &
1678 "If true, recover a bug in the calculation of the barotropic transport for "//&
1679 "the advection of MEKE. With the bug, only the transports in the deepest "//&
16801 "layer are used.", default=.false., do_not_log=(CS%MEKE_advection_factor<=0.))
1681 call get_param(param_file, mdl, "MEKE_TOPOGRAPHIC_BETA", CS%MEKE_topographic_beta, &
1682 "A scale factor to determine how much topographic beta is weighed in " //&
1683 "computing beta in the expression of Rhines scale. Use 1 if full "//&
1684 "topographic beta effect is considered; use 0 if it's completely ignored.", &
16851 units="nondim", default=0.0)
1686 call get_param(param_file, mdl, "SQG_USE_MEKE", CS%sqg_use_MEKE, &
1687 "If true, the eddy scale of MEKE is used for the SQG vertical structure ",&
16881 default=.false.)
1689
1690 ! Nonlocal module parameters
1691 call get_param(param_file, mdl, "CDRAG", cdrag, &
1692 "CDRAG is the drag coefficient relating the magnitude of the velocity "//&
16931 "field to the bottom stress.", units="nondim", default=0.003)
1694 call get_param(param_file, mdl, "MEKE_CDRAG", CS%cdrag, &
1695 "Drag coefficient relating the magnitude of the velocity "//&
16961 "field to the bottom stress in MEKE.", units="nondim", default=cdrag, scale=US%L_to_m*GV%m_to_H)
16971 call get_param(param_file, mdl, "LAPLACIAN", laplacian, default=.false., do_not_log=.true.)
16981 call get_param(param_file, mdl, "BIHARMONIC", biharmonic, default=.false., do_not_log=.true.)
1699
17001 if (CS%viscosity_coeff_Ku/=0. .and. .not. laplacian) call MOM_error(FATAL, &
17010 "LAPLACIAN must be true if MEKE_VISCOSITY_COEFF_KU is true.")
1702
17031 if (CS%viscosity_coeff_Au/=0. .and. .not. biharmonic) call MOM_error(FATAL, &
17040 "BIHARMONIC must be true if MEKE_VISCOSITY_COEFF_AU is true.")
1705
17061 call get_param(param_file, mdl, "DEBUG", CS%debug, default=.false., do_not_log=.true.)
1707
1708 ! Identify if any lateral diffusive processes are active
17091 CS%kh_flux_enabled = .false.
17101 if ((CS%MEKE_KH >= 0.0) .or. (CS%KhMEKE_FAC > 0.0) .or. (CS%MEKE_advection_factor > 0.0)) &
17111 CS%kh_flux_enabled = .true.
1712
1713! Register fields for output from this module.
17141 CS%diag => diag
1715 CS%id_MEKE = register_diag_field('ocean_model', 'MEKE', diag%axesT1, Time, &
17161 'Mesoscale Eddy Kinetic Energy', 'm2 s-2', conversion=US%L_T_to_m_s**2)
17171 if (.not. allocated(MEKE%MEKE)) CS%id_MEKE = -1
1718 CS%id_Kh = register_diag_field('ocean_model', 'MEKE_KH', diag%axesT1, Time, &
17191 'MEKE derived diffusivity', 'm2 s-1', conversion=US%L_to_m**2*US%s_to_T)
17201 if (.not. allocated(MEKE%Kh)) CS%id_Kh = -1
1721 CS%id_Ku = register_diag_field('ocean_model', 'MEKE_KU', diag%axesT1, Time, &
17221 'MEKE derived lateral viscosity', 'm2 s-1', conversion=US%L_to_m**2*US%s_to_T)
17231 if (.not. allocated(MEKE%Ku)) CS%id_Ku = -1
1724 CS%id_Au = register_diag_field('ocean_model', 'MEKE_AU', diag%axesT1, Time, &
17251 'MEKE derived lateral biharmonic viscosity', 'm4 s-1', conversion=US%L_to_m**4*US%s_to_T)
17261 if (.not. allocated(MEKE%Au)) CS%id_Au = -1
1727 CS%id_Ue = register_diag_field('ocean_model', 'MEKE_Ue', diag%axesT1, Time, &
17281 'MEKE derived eddy-velocity scale', 'm s-1', conversion=US%L_T_to_m_s)
17291 if (.not. allocated(MEKE%MEKE)) CS%id_Ue = -1
1730 CS%id_Ub = register_diag_field('ocean_model', 'MEKE_Ub', diag%axesT1, Time, &
17311 'MEKE derived bottom eddy-velocity scale', 'm s-1', conversion=US%L_T_to_m_s)
17321 if (.not. allocated(MEKE%MEKE)) CS%id_Ub = -1
1733 CS%id_Ut = register_diag_field('ocean_model', 'MEKE_Ut', diag%axesT1, Time, &
17341 'MEKE derived barotropic eddy-velocity scale', 'm s-1', conversion=US%L_T_to_m_s)
17351 if (.not. allocated(MEKE%MEKE)) CS%id_Ut = -1
1736 CS%id_src = register_diag_field('ocean_model', 'MEKE_src', diag%axesT1, Time, &
17371 'MEKE energy source', 'm2 s-3', conversion=(US%L_T_to_m_s**2)*US%s_to_T)
1738
1739 CS%id_src_adv = register_diag_field('ocean_model', 'MEKE_src_adv', diag%axesT1, Time, &
17401 'MEKE energy source from the horizontal advection of MEKE', 'm2 s-3', conversion=(US%L_T_to_m_s**2)*US%s_to_T)
1741
1742 CS%id_src_btm_drag = register_diag_field('ocean_model', 'MEKE_src_btm_drag', diag%axesT1, Time, &
17431 '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)
1744
17451 if (CS%MEKE_K4 >= 0.) &
1746 CS%id_src_mom_K4 = register_diag_field('ocean_model', 'MEKE_src_mom_K4', &
1747 diag%axesT1, Time, 'MEKE energy source from the biharmonic of MEKE', &
17480 'm2 s-3', conversion=(US%L_T_to_m_s**2)*US%s_to_T)
1749
17501 if (CS%MEKE_GMcoeff >= 0.) &
1751 CS%id_src_GM = register_diag_field('ocean_model', 'MEKE_src_GM', &
1752 diag%axesT1, Time, 'MEKE energy source from the thickness mixing (GM scheme)', &
17531 'm2 s-3', conversion=(US%L_T_to_m_s**2)*US%s_to_T)
1754
17551 if (CS%MEKE_FrCoeff >= 0.) &
1756 CS%id_src_mom_lp = register_diag_field('ocean_model', 'MEKE_src_mom_lp', &
1757 diag%axesT1, Time, 'MEKE energy source from the Laplacian of resolved flows', &
17580 'm2 s-3', conversion=(US%L_T_to_m_s**2)*US%s_to_T)
1759
17601 if (CS%MEKE_bhFrCoeff >= 0.) &
1761 CS%id_src_mom_bh = register_diag_field('ocean_model', 'MEKE_src_mom_bh', &
1762 diag%axesT1, Time, 'MEKE energy source from the biharmonic of resolved flows', &
17630 'm2 s-3', conversion=(US%L_T_to_m_s**2)*US%s_to_T)
1764
1765 CS%id_decay = register_diag_field('ocean_model', 'MEKE_decay', diag%axesT1, Time, &
17661 'MEKE decay rate', 's-1', conversion=US%s_to_T)
1767 CS%id_GM_src = register_diag_field('ocean_model', 'MEKE_GM_src', diag%axesT1, Time, &
1768 'MEKE energy available from thickness mixing', &
17691 'W m-2', conversion=US%RZ3_T3_to_W_m2*US%L_to_Z**2)
17701 if (.not. allocated(MEKE%GM_src)) CS%id_GM_src = -1
1771 CS%id_mom_src = register_diag_field('ocean_model', 'MEKE_mom_src',diag%axesT1, Time, &
1772 'MEKE energy available from momentum', &
17731 'W m-2', conversion=US%RZ3_T3_to_W_m2*US%L_to_Z**2)
17741 if (.not. allocated(MEKE%mom_src)) CS%id_mom_src = -1
1775 CS%id_mom_src_bh = register_diag_field('ocean_model', 'MEKE_mom_src_bh',diag%axesT1, Time, &
1776 'MEKE energy available from the biharmonic dissipation of momentum', &
17771 'W m-2', conversion=US%RZ3_T3_to_W_m2*US%L_to_Z**2)
17781 if (.not. allocated(MEKE%mom_src_bh)) CS%id_mom_src_bh = -1
1779 CS%id_GME_snk = register_diag_field('ocean_model', 'MEKE_GME_snk',diag%axesT1, Time, &
1780 'MEKE energy lost to GME backscatter', &
17811 'W m-2', conversion=US%RZ3_T3_to_W_m2*US%L_to_Z**2)
17821 if (.not. allocated(MEKE%GME_snk)) CS%id_GME_snk = -1
1783 CS%id_Le = register_diag_field('ocean_model', 'MEKE_Le', diag%axesT1, Time, &
17841 'Eddy mixing length used in the MEKE derived eddy diffusivity', 'm', conversion=US%L_to_m)
1785 CS%id_Lrhines = register_diag_field('ocean_model', 'MEKE_Lrhines', diag%axesT1, Time, &
17861 'Rhines length scale used in the MEKE derived eddy diffusivity', 'm', conversion=US%L_to_m)
1787 CS%id_Leady = register_diag_field('ocean_model', 'MEKE_Leady', diag%axesT1, Time, &
17881 'Eady length scale used in the MEKE derived eddy diffusivity', 'm', conversion=US%L_to_m)
1789 CS%id_gamma_b = register_diag_field('ocean_model', 'MEKE_gamma_b', diag%axesT1, Time, &
17901 'Ratio of bottom-projected eddy velocity to column-mean eddy velocity', 'nondim')
1791 CS%id_gamma_t = register_diag_field('ocean_model', 'MEKE_gamma_t', diag%axesT1, Time, &
17921 'Ratio of barotropic eddy velocity to column-mean eddy velocity', 'nondim')
1793
17941 if (CS%kh_flux_enabled) then
1795 CS%id_KhMEKE_u = register_diag_field('ocean_model', 'KHMEKE_u', diag%axesCu1, Time, &
17961 'Zonal diffusivity of MEKE', 'm2 s-1', conversion=US%L_to_m**2*US%s_to_T)
1797 CS%id_KhMEKE_v = register_diag_field('ocean_model', 'KHMEKE_v', diag%axesCv1, Time, &
17981 'Meridional diffusivity of MEKE', 'm2 s-1', conversion=US%L_to_m**2*US%s_to_T)
1799 endif
1800
18011 if (CS%MEKE_equilibrium_restoring) then
1802 CS%id_MEKE_equilibrium = register_diag_field('ocean_model', 'MEKE_equilibrium', diag%axesT1, Time, &
18030 'Equilibrated Mesoscale Eddy Kinetic Energy', 'm2 s-2', conversion=US%L_T_to_m_s**2)
1804 endif
1805
18061 CS%id_clock_pass = cpu_clock_id('(Ocean continuity halo updates)', grain=CLOCK_ROUTINE)
1807
1808
1809 ! Detect whether this instance of MEKE_init() is at the beginning of a run
1810 ! or after a restart. If at the beginning, we will initialize MEKE to a local
1811 ! equilibrium.
18121 CS%initialize = .not.query_initialized(MEKE%MEKE, "MEKE", restart_CS)
18131 if (coldStart) CS%initialize = .false.
18141 if (CS%initialize) call MOM_error(WARNING, &
18151 "MEKE_init: Initializing MEKE with a local equilibrium balance.")
18161 if (allocated(MEKE%Le)) then
18170 if (.not.query_initialized(MEKE%Le, "MEKE_Le", restart_CS)) then
1818 !$OMP parallel do default(shared)
18190 do j=js,je ; do i=is,ie
18200 MEKE%Le(i,j) = sqrt(G%areaT(i,j))
1821 enddo ; enddo
1822 endif
1823 endif
1824
1825 ! Set up group passes. In the case of a restart, these fields need a halo update now.
18261 if (allocated(MEKE%MEKE)) then
18271 call create_group_pass(CS%pass_MEKE, MEKE%MEKE, G%Domain)
18281 if (allocated(MEKE%Kh_diff)) call create_group_pass(CS%pass_MEKE, MEKE%Kh_diff, G%Domain)
18291 if (.not.CS%initialize) call do_group_pass(CS%pass_MEKE, G%Domain)
1830 endif
18311 if (allocated(MEKE%Kh)) call create_group_pass(CS%pass_Kh, MEKE%Kh, G%Domain)
18321 if (allocated(MEKE%Ku)) call create_group_pass(CS%pass_Kh, MEKE%Ku, G%Domain)
18331 if (allocated(MEKE%Au)) call create_group_pass(CS%pass_Kh, MEKE%Au, G%Domain)
18341 if (allocated(MEKE%Le)) call create_group_pass(CS%pass_Kh, MEKE%Le, G%Domain)
1835
1836 if (allocated(MEKE%Kh) .or. allocated(MEKE%Ku) .or. allocated(MEKE%Au) &
18371 .or. allocated(MEKE%Le)) &
18381 call do_group_pass(CS%pass_Kh, G%Domain)
1839
1840 ! This CS and the MEKE type are inline members of MOM_control_struct, whose whole address
1841 ! range the solo driver puts on the device with `enter data map(alloc: MOM_CSp)` -- allocated,
1842 ! never copied. Any later map(to:) of them is a present-table no-op, so device kernels would
1843 ! read zeros for CS scalars. `target update` always copies; issue it here, once the
1844 ! parameters are final, and BEFORE the component maps below, whose attach then fixes the
1845 ! MEKE arrays' descriptors in the device copy.
1846 !$omp target update to(CS)
1847 if (allocated(MEKE%MEKE)) then
1848 !$omp target enter data map(to: MEKE%MEKE)
1849 endif
1850 if (allocated(MEKE%GM_src)) then
1851 !$omp target enter data map(to: MEKE%GM_src)
1852 endif
1853 if (allocated(MEKE%mom_src)) then
1854 !$omp target enter data map(to: MEKE%mom_src)
1855 endif
1856 if (allocated(MEKE%mom_src_bh)) then
1857 !$omp target enter data map(to: MEKE%mom_src_bh)
1858 endif
1859 if (allocated(MEKE%GME_snk)) then
1860 !$omp target enter data map(to: MEKE%GME_snk)
1861 endif
1862 if (allocated(MEKE%Kh)) then
1863 !$omp target enter data map(to: MEKE%Kh)
1864 endif
1865 if (allocated(MEKE%Kh_diff)) then
1866 !$omp target enter data map(to: MEKE%Kh_diff)
1867 endif
1868 if (allocated(MEKE%Rd_dx_h)) then
1869 !$omp target enter data map(to: MEKE%Rd_dx_h)
1870 endif
1871 if (allocated(MEKE%Ku)) then
1872 !$omp target enter data map(to: MEKE%Ku)
1873 endif
1874 if (allocated(MEKE%Au)) then
1875 !$omp target enter data map(to: MEKE%Au)
1876 endif
1877 if (allocated(MEKE%Le)) then
1878 !$omp target enter data map(to: MEKE%Le)
1879 endif
1880
18811end function MEKE_init
1882
1883!> Initializer for the variant of MEKE that uses ML to predict eddy kinetic energy
18840subroutine ML_MEKE_init(diag, G, US, Time, param_file, dbcomms_CS, CS)
1885 type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics structure.
1886 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
1887 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1888 type(time_type), intent(in) :: Time !< The current model time.
1889 type(param_file_type), intent(in) :: param_file !< Parameter file parser structure.
1890 type(dbcomms_CS_type), intent(in) :: dbcomms_CS !< Control structure for database communication
1891 type(MEKE_CS), intent(inout) :: CS !< Control structure for this module
1892
1893 character(len=200) :: inputdir, backend, model_filename
1894 integer :: db_return_code, batch_size
1895 character(len=40) :: mdl = "MOM_ML_MEKE"
1896#ifdef __NVCOMPILER_OPENMP_GPU
1897 integer, parameter :: default_niblock = 0
1898 integer, parameter :: default_njblock = 0
1899 integer, parameter :: default_nkblock = 0
1900#else
1901 integer, parameter :: default_niblock = 0
1902 integer, parameter :: default_njblock = 1
1903 integer, parameter :: default_nkblock = 1
1904#endif
1905
1906 ! Store pointers in control structure
19070 write(CS%key_suffix, '(A,I6.6)') '_', PE_here()
1908 ! Put some basic information into the database
19090 db_return_code = 0
1910 db_return_code = CS%client%put_tensor("meta"//CS%key_suffix, &
19110 REAL([G%isd_global, G%idg_offset, G%jsd_global, G%jdg_offset]),[4]) + db_return_code
19120 db_return_code = CS%client%put_tensor("geolat"//CS%key_suffix, G%geoLatT, shape(G%geoLatT)) + db_return_code
19130 db_return_code = CS%client%put_tensor("geolon"//CS%key_suffix, G%geoLonT, shape(G%geoLonT)) + db_return_code
19140 db_return_code = CS%client%put_tensor("EKE_shape"//CS%key_suffix, shape(G%geolonT), [2]) + db_return_code
1915
19160 if (CS%client%SR_error_parser(db_return_code)) call MOM_error(FATAL, "Putting metadata into the database failed")
1917
19180 call read_param(param_file, "INPUTDIR", inputdir)
19190 inputdir = slasher(inputdir)
1920
19210 call get_param(param_file, mdl, "BATCH_SIZE", batch_size, "Batch size to use for inference", default=1)
1922 call get_param(param_file, mdl, "EKE_BACKEND", backend, &
19230 "The computational backend to use for EKE inference (CPU or GPU)", default="GPU")
1924 call get_param(param_file, mdl, "EKE_MODEL", model_filename, &
19250 "Filename of the a saved pyTorch model to use", fail_if_missing = .true.)
1926 call get_param(param_file, mdl, "EKE_MAX", CS%eke_max, &
1927 "Maximum value of EKE allowed when inferring EKE", &
19280 units="m2 s-2", default=2., scale=US%m_s_to_L_T**2)
1929
1930 ! Set the machine learning model
19310 if (dbcomms_CS%colocated) then
19320 if (modulo(PE_here(),dbcomms_CS%colocated_stride) == 0) then
1933 db_return_code = CS%client%set_model_from_file(CS%model_key, trim(inputdir)//trim(model_filename), &
19340 "TORCH", backend, batch_size=batch_size)
1935 endif
1936 else
19370 if (is_root_pe()) then
1938 db_return_code = CS%client%set_model_from_file(CS%model_key, trim(inputdir)//trim(model_filename), &
19390 "TORCH", backend, batch_size=batch_size)
1940 endif
1941 endif
19420 if (CS%client%SR_error_parser(db_return_code)) then
19430 call MOM_error(FATAL, "MEKE: set_model failed")
1944 endif
1945
1946 call get_param(param_file, mdl, "ONLINE_ANALYSIS", CS%online_analysis, &
19470 "If true, post EKE used in MOM6 to the database for analysis", default=.true.)
1948
1949 ! Set various clock ids
19500 CS%id_client_init = cpu_clock_id('(ML_MEKE client init)', grain=CLOCK_ROUTINE)
19510 CS%id_put_tensor = cpu_clock_id('(ML_MEKE put tensor)', grain=CLOCK_ROUTINE)
19520 CS%id_run_model = cpu_clock_id('(ML_MEKE run model)', grain=CLOCK_ROUTINE)
19530 CS%id_unpack_tensor = cpu_clock_id('(ML_MEKE unpack tensor )', grain=CLOCK_ROUTINE)
1954
1955 ! Diagnostics for ML_MEKE
1956 CS%id_mke = register_diag_field('ocean_model', 'MEKE_MKE', diag%axesT1, Time, &
19570 'Surface mean (resolved) kinetic energy used in MEKE', 'm2 s-2', conversion=US%L_T_to_m_s**2)
1958 CS%id_slope_z= register_diag_field('ocean_model', 'MEKE_slope_z', diag%axesT1, Time, &
19590 'Vertically averaged isopyncal slope magnitude used in MEKE', 'nondim', conversion=US%Z_to_L)
1960 CS%id_slope_x= register_diag_field('ocean_model', 'MEKE_slope_x', diag%axesCui, Time, &
19610 'Isopycnal slope in the x-direction used in MEKE', 'nondim', conversion=US%Z_to_L)
1962 CS%id_slope_y= register_diag_field('ocean_model', 'MEKE_slope_y', diag%axesCvi, Time, &
19630 'Isopycnal slope in the y-direction used in MEKE', 'nondim', conversion=US%Z_to_L)
1964 CS%id_rv = register_diag_field('ocean_model', 'MEKE_RV', diag%axesT1, Time, &
19650 'Surface relative vorticity used in MEKE', 's-1', conversion=US%s_to_T)
1966
1967 ! Isoneutral blocking parameters
1968 call get_param(param_file, mdl, "ISOPYCNAL_NIBLOCK", CS%niblock, &
1969 "The i-direction block size used to calculate isopycnal slopes. "//&
1970 "If 0, or when running with OpenMP offload, "//&
1971 "the full computational domain width is used. "//&
1972 "If USE_STANLEY_ISO is true, ISOPYCNAL_NIBLOCK cannot equal 1.", &
19730 default=default_niblock, layoutParam=.true.)
1974 call get_param(param_file, mdl, "ISOPYCNAL_NJBLOCK", CS%njblock, &
1975 "The j-direction block size used to calculate isopycnal slopes. "//&
1976 "If 0, defaults to 1, except when running with OpenMP offload, "//&
1977 "in which case the full computational domain height is used. " //&
1978 "If USE_STANLEY_ISO is true, ISOPYCNAL_NJBLOCK cannot equal 1.", &
19790 default=default_njblock, layoutParam=.true.)
1980 call get_param(param_file, mdl, "ISOPYCNAL_NKBLOCK", CS%nkblock, &
1981 "The k-direction block size used to calculate isopycnal slopes. "//&
1982 "If 0, defaults to 1, except when "//&
1983 "running with OpenMP offload, in which case the full computational "//&
19840 "domain depth is used.", default=default_nkblock, layoutParam=.true.)
1985
19860 if (CS%niblock < 0) &
1987 call MOM_error(FATAL, "ISOPYCNAL_NIBLOCK must be nonnegative; "//&
19880 "use 0 to select the default block size.")
19890 if (CS%njblock < 0) &
1990 call MOM_error(FATAL, "ISOPYCNAL_NJBLOCK must be nonnegative; "//&
19910 "use 0 to select the default block size.")
19920 if (CS%nkblock < 0) &
1993 call MOM_error(FATAL, "ISOPYCNAL_NKBLOCK must be nonnegative; "//&
19940 "use 0 to select the default block size.")
1995
1996
19970end subroutine ML_MEKE_init
1998
1999!> Calculate the various features used for the machine learning prediction
20000subroutine ML_MEKE_calculate_features(G, GV, US, CS, Rd_dx_h, u, v, tv, h, dt, features_array)
2001 type(ocean_grid_type), intent(inout) :: G !< Ocean grid
2002 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure
2003 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
2004 type(MEKE_CS), intent(in) :: CS !< Control structure for MEKE
2005 real, dimension(SZI_(G),SZJ_(G)), intent(in ) :: Rd_dx_h !< Rossby radius of deformation over
2006 !! the grid length scale [nondim]
2007 real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: u !< Zonal velocity [L T-1 ~> m s-1]
2008 real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: v !< Meridional velocity [L T-1 ~> m s-1]
2009 type(thermo_var_ptrs), intent(in) :: tv !< Type containing thermodynamic variables
2010 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> m or kg m-2].
2011 real, intent(in) :: dt !< Model(baroclinic) time-step [T ~> s].
2012 real(kind=real32), dimension(SIZE(h),num_features), intent( out) :: features_array
2013 !< The array of features needed for machine
2014 !! learning inference, with different units
2015 !! for the various subarrays [various]
2016
20170 real, dimension(SZI_(G),SZJ_(G)) :: mke ! Surface kinetic energy per unit mass [L2 T-2 ~> m2 s-2]
20180 real, dimension(SZI_(G),SZJ_(G)) :: slope_z ! Vertically averaged isoneutral slopes [Z L-1 ~> nondim]
20190 real, dimension(SZIB_(G),SZJB_(G)) :: rv_z ! Surface relative vorticity [T-1 ~> s-1]
20200 real, dimension(SZIB_(G),SZJB_(G)) :: rv_z_t ! Surface relative vorticity interpolated to tracer points [T-1 ~> s-1]
2021
20220 real, dimension(SZIB_(G),SZJ_(G), SZK_(G)) :: h_u ! Thickness at u point [H ~> m or kg m-2]
20230 real, dimension(SZI_(G),SZJB_(G), SZK_(G)) :: h_v ! Thickness at v point [H ~> m or kg m-2]
20240 real, dimension(SZIB_(G),SZJ_(G),SZK_(G)+1) :: slope_x ! Isoneutral slope at U point [Z L-1 ~> nondim]
20250 real, dimension(SZI_(G),SZJB_(G),SZK_(G)+1) :: slope_y ! Isoneutral slope at V point [Z L-1 ~> nondim]
20260 real, dimension(SZIB_(G),SZJ_(G)) :: slope_x_vert_avg ! Isoneutral slope at U point [Z L-1 ~> nondim]
20270 real, dimension(SZI_(G),SZJB_(G)) :: slope_y_vert_avg ! Isoneutral slope at V point [Z L-1 ~> nondim]
20280 real, dimension(SZI_(G), SZJ_(G), SZK_(G)+1) :: e ! The interface heights relative to mean sea level [Z ~> m].
2029 real :: slope_t ! Slope interpolated to thickness points [Z L-1 ~> nondim]
2030 real :: u_t, v_t ! u and v interpolated to thickness points [L T-1 ~> m s-1]
2031 real :: dvdx, dudy ! Components of relative vorticity [T-1 ~> s-1]
2032 real :: a_e, a_w, a_n, a_s ! Fractional areas of neighboring cells for interpolating velocities [nondim]
2033 real :: Idenom ! A normalizing factor in calculating weighted averages of areas [L-2 ~> m-2]
2034 real :: sum_area ! A sum of adjacent cell areas [L2 ~> m2]
2035
2036 integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz
2037 integer :: niblock, njblock, nkblock
2038
20390 niblock = CS%niblock
20400 njblock = CS%njblock
20410 nkblock = CS%nkblock
2042
20430 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
20440 Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB
2045
2046 ! Calculate various features for used to infer eddy kinetic energy
2047 ! Linear interpolation to estimate thickness at a velocity points
20480 do k=1,nz ; do j=js-1,je+1 ; do i=is-1,ie+1
20490 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
20500 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
2051 enddo ; enddo ; enddo
2052
20530 if (niblock == 0) niblock = ie - is + 1
20540 if (njblock == 0) njblock = je - js + 1
20550 if (nkblock == 0) nkblock = nz
2056
2057 !$omp target update to(h)
2058 !$omp target enter data map(alloc: e)
20590 call find_eta(h, tv, G, GV, US, e, halo_size=2)
2060 ! Note the hard-coded dimenisional constant in the following line.
2061 ! UMW: Below is untested
2062 !$omp target enter data map(to: tv, tv%T, tv%S, slope_x, slope_y)
2063 !$omp target enter data map(to: tv%SpV_avg) if (allocated(tv%SpV_avg))
2064 !$omp target enter data map(to: tv%p_surf) if (associated(tv%p_surf))
2065 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, &
20660 niblock, njblock, nkblock )
2067 !$omp target exit data map(release: tv, tv%T, tv%S)
2068 !$omp target exit data map(delete: e)
2069 !$omp target exit data map(release: tv%SpV_avg) if (allocated(tv%SpV_avg))
2070 !$omp target exit data map(release: tv%p_surf) if (associated(tv%p_surf))
2071 !$omp target exit data map(from: slope_x, slope_y)
20720 call pass_vector(slope_x, slope_y, G%Domain)
20730 do j=js-1,je+1 ; do i=is-1,ie+1
20740 slope_x_vert_avg(I,j) = vertical_average_interface(slope_x(i,j,:), h_u(i,j,:), GV%H_subroundoff)
20750 slope_y_vert_avg(i,J) = vertical_average_interface(slope_y(i,j,:), h_v(i,j,:), GV%H_subroundoff)
2076 enddo ; enddo
20770 slope_z(:,:) = 0.
2078
20790 call pass_vector(slope_x_vert_avg, slope_y_vert_avg, G%Domain)
20800 do j=js,je ; do i=is,ie
2081 ! Calculate weights for interpolation from velocity points to h points
20820 sum_area = G%areaCu(I-1,j) + G%areaCu(I,j)
20830 if (sum_area>0.0) then
20840 Idenom = sqrt(0.5*G%IareaT(i,j) / sum_area)
20850 a_w = G%areaCu(I-1,j) * Idenom
20860 a_e = G%areaCu(I,j) * Idenom
2087 else
20880 a_w = 0.0 ; a_e = 0.0
2089 endif
2090
20910 sum_area = G%areaCv(i,J-1) + G%areaCv(i,J)
20920 if (sum_area>0.0) then
20930 Idenom = sqrt(0.5*G%IareaT(i,j) / sum_area)
20940 a_s = G%areaCv(i,J-1) * Idenom
20950 a_n = G%areaCv(i,J) * Idenom
2096 else
20970 a_s = 0.0 ; a_n = 0.0
2098 endif
2099
2100 ! Calculate mean kinetic energy
21010 u_t = (a_e*u(I,j,1)) + (a_w*u(I-1,j,1))
21020 v_t = (a_n*v(i,J,1)) + (a_s*v(i,J-1,1))
21030 mke(i,j) = 0.5*( (u_t*u_t) + (v_t*v_t) )
2104
2105 ! Calculate the magnitude of the slope
21060 slope_t = slope_x_vert_avg(I,j)*a_e+slope_x_vert_avg(I-1,j)*a_w
21070 slope_z(i,j) = sqrt(slope_t*slope_t)
21080 slope_t = slope_y_vert_avg(i,J)*a_n+slope_y_vert_avg(i,J-1)*a_s
21090 slope_z(i,j) = 0.5*(slope_z(i,j) + sqrt(slope_t*slope_t))*G%mask2dT(i,j)
2110 enddo ; enddo
21110 call pass_var(slope_z, G%Domain)
2112
2113 ! Calculate relative vorticity
21140 do J=Jsq-1,Jeq+1 ; do I=Isq-1,Ieq+1
21150 dvdx = ((v(i+1,J,1)*G%dyCv(i+1,J)) - (v(i,J,1)*G%dyCv(i,J)))
21160 dudy = ((u(I,j+1,1)*G%dxCu(I,j+1)) - (u(I,j,1)*G%dxCu(I,j)))
2117 ! Assumed no slip
21180 rv_z(I,J) = (2.0-G%mask2dBu(I,J)) * (dvdx - dudy) * G%IareaBu(I,J)
2119 enddo ; enddo
2120 ! Interpolate RV to t-point, revisit this calculation to include metrics
21210 do j=js,je ; do i=is,ie
21220 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))
2123 enddo ; enddo
2124
2125
2126 ! Construct the feature array
21270 features_array(:,mke_idx) = pack(mke,.true.)
21280 features_array(:,slope_z_idx) = pack(slope_z,.true.)
21290 features_array(:,rd_dx_z_idx) = pack(Rd_dx_h,.true.)
21300 features_array(:,rv_idx) = pack(rv_z_t,.true.)
2131
21320 if (CS%id_rv>0) call post_data(CS%id_rv, rv_z, CS%diag)
21330 if (CS%id_mke>0) call post_data(CS%id_mke, mke, CS%diag)
21340 if (CS%id_slope_z>0) call post_data(CS%id_slope_z, slope_z, CS%diag)
21350 if (CS%id_slope_x>0) call post_data(CS%id_slope_x, slope_x, CS%diag)
21360 if (CS%id_slope_y>0) call post_data(CS%id_slope_y, slope_y, CS%diag)
21370end subroutine ML_MEKE_calculate_features
2138
2139!> Use the machine learning interface to predict EKE
21400subroutine predict_MEKE(G, US, CS, npts, Time, features_array, MEKE)
2141 type(ocean_grid_type), intent(inout) :: G !< Ocean grid
2142 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
2143 type(MEKE_CS), intent(in ) :: CS !< Control structure for MEKE
2144 integer, intent(in ) :: npts !< Number of T-grid cells on the local
2145 !! domain
2146 type(time_type), intent(in ) :: Time !< The current model time
2147 real(kind=real32), dimension(npts,num_features), intent(in ) :: features_array
2148 !< The array of features needed for machine
2149 !! learning inference, with different units
2150 !! for the various subarrays [various]
2151 real, dimension(SZI_(G),SZJ_(G)), intent( out) :: MEKE !< Eddy kinetic energy [L2 T-2 ~> m2 s-2]
2152
2153 ! Local variables
2154 integer :: db_return_code
2155 character(len=255), dimension(1) :: model_out, model_in
2156 character(len=255) :: time_suffix
21570 real(kind=real32), dimension(SIZE(MEKE)) :: MEKE_vec ! A one-dimensional array of the natural log of eddy kinetic
2158 ! energy in mks units [m2 s-2]
21590 real, dimension(size(MEKE,1),size(MEKE,2)) :: ln_MEKE ! the natural log of eddy kinetic energy
2160 ! in mks units [m2 s-2]
21610 real, dimension(size(MEKE,1),size(MEKE,2)) :: MEKE_mks ! The eddy kinetic energy in mks units [m2 s-2]
2162 integer :: i, j, is, ie, js, je
2163
21640 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
2165!> Use the database client to call a machine learning model to predict eddy kinetic energy
21660 call cpu_clock_begin(CS%id_put_tensor)
21670 db_return_code = CS%client%put_tensor("features"//CS%key_suffix, features_array, shape(features_array))
21680 call cpu_clock_end(CS%id_put_tensor)
2169
2170 ! Run the ML model to predict EKE and return the result
21710 model_out(1) = "EKE"//CS%key_suffix
21720 model_in(1) = "features"//CS%key_suffix
21730 call cpu_clock_begin(CS%id_run_model)
21740 db_return_code = CS%client%run_model(CS%model_key, model_in, model_out)
21750 call cpu_clock_end(CS%id_run_model)
21760 if (CS%client%SR_error_parser(db_return_code)) then
21770 call MOM_error(FATAL, "MEKE: run_model failed")
2178 endif
21790 call cpu_clock_begin(CS%id_unpack_tensor)
21800 db_return_code = CS%client%unpack_tensor( model_out(1), MEKE_vec, shape(MEKE_vec) )
21810 call cpu_clock_end(CS%id_unpack_tensor)
2182
21830 ln_MEKE = reshape(MEKE_vec, shape(MEKE))
2184 ! Zero out the halos. These will usually be reset by the pass_var in a few lines.
21850 MEKE_mks(:,:) = 0.0
21860 do j=js,je ; do i=is,ie
21870 MEKE_mks(i,j) = MIN(exp(ln_MEKE(i,j)), US%L_T_to_m_s**2*CS%eke_max)
2188 enddo ; enddo
21890 call pass_var(MEKE_mks, G%Domain, halo=1)
2190
21910 if (CS%online_analysis) then
21920 write(time_suffix,"(F16.0)") time_type_to_real(Time)
2193 db_return_code = CS%client%put_tensor(trim("EKE_")//trim(adjustl(time_suffix))//CS%key_suffix, &
21940 MEKE_mks, shape(MEKE))
2195 endif
2196
2197 ! Copy MEKE_mks into the argument in rescaled units.
2198 ! MEKE(:,:) = 0.0 ! This would fill in the wider halos of this intent(out) array.
21990 do j=js-1,je+1 ; do i=is-1,ie+1
22000 MEKE(i,j) = US%m_s_to_L_T**2 * MEKE_mks(i,j)
2201 enddo ; enddo
2202
22030end subroutine predict_MEKE
2204
2205!> Compute average of interface quantities weighted by the thickness of the surrounding
2206!! layers [arbitrary]
22070real function vertical_average_interface(h, w, h_min)
2208
2209 real, dimension(:), intent(in) :: h !< Layer Thicknesses [H ~> m or kg m-2]
2210 real, dimension(:), intent(in) :: w !< Quantity to average [arbitrary]
2211 real, intent(in) :: h_min !< The vanishingly small layer thickness [H ~> m or kg m-2]
2212
2213 real :: htot ! Twice the sum of the layer thicknesses interpolated to interior interfaces [H ~> m or kg m-2]
2214 real :: inv_htot ! The inverse of htot [H-1 ~> m-1 or m2 kg-1]
2215 integer :: k, nk
2216
22170 nk = size(h)
22180 htot = h_min
22190 do k=2,nk
22200 htot = htot + (h(k-1)+h(k))
2221 enddo
22220 inv_htot = 1./htot
2223
22240 vertical_average_interface = 0.
22250 do K=2,nk
22260 vertical_average_interface = vertical_average_interface + (w(k)*(h(k-1)+h(k)))*inv_htot
2227 enddo
22280end function vertical_average_interface
2229
2230!> Allocates memory and register restart fields for the MOM_MEKE module.
22311subroutine MEKE_alloc_register_restart(HI, US, param_file, MEKE, restart_CS)
2232! Arguments
2233 type(hor_index_type), intent(in) :: HI !< Horizontal index structure
2234 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
2235 type(param_file_type), intent(in) :: param_file !< Parameter file parser structure.
2236 type(MEKE_type), intent(inout) :: MEKE !< MEKE fields
2237 type(MOM_restart_CS), intent(inout) :: restart_CS !< MOM restart control struct
2238
2239 ! Local variables
2240 real :: MEKE_GMcoeff, MEKE_FrCoeff, MEKE_bhFrCoeff, MEKE_GMECoeff ! Coefficients for various terms [nondim]
2241 real :: MEKE_KHCoeff, MEKE_viscCoeff_Ku, MEKE_viscCoeff_Au ! Coefficients for various terms [nondim]
2242 logical :: Use_KH_in_MEKE
2243 logical :: useMEKE
2244 logical :: sqg_use_MEKE
2245 integer :: isd, ied, jsd, jed
2246
2247! Determine whether this module will be used
22481 useMEKE = .false. ; call read_param(param_file,"USE_MEKE",useMEKE)
2249
2250! Read these parameters to determine what should be in the restarts
22511 MEKE_GMcoeff = -1. ; call read_param(param_file,"MEKE_GMCOEFF",MEKE_GMcoeff)
22521 MEKE_FrCoeff = -1. ; call read_param(param_file,"MEKE_FRCOEFF",MEKE_FrCoeff)
22531 MEKE_bhFrCoeff = -1. ; call read_param(param_file,"MEKE_bhFRCOEFF",MEKE_bhFrCoeff)
22541 MEKE_GMEcoeff = -1. ; call read_param(param_file,"MEKE_GMECOEFF",MEKE_GMEcoeff)
22551 MEKE_KhCoeff = 1. ; call read_param(param_file,"MEKE_KHCOEFF",MEKE_KhCoeff)
22561 MEKE_viscCoeff_Ku = 0. ; call read_param(param_file,"MEKE_VISCOSITY_COEFF_KU",MEKE_viscCoeff_Ku)
22571 MEKE_viscCoeff_Au = 0. ; call read_param(param_file,"MEKE_VISCOSITY_COEFF_AU",MEKE_viscCoeff_Au)
22581 Use_KH_in_MEKE = .false. ; call read_param(param_file,"USE_KH_IN_MEKE", Use_KH_in_MEKE)
22591 sqg_use_MEKE = .false. ; call read_param(param_file,"SQG_USE_MEKE", sqg_use_MEKE)
2260
22611 if (.not. useMEKE) return
2262
2263! Allocate memory
22641 call MOM_mesg("MEKE_alloc_register_restart: allocating and registering", 5)
22651 isd = HI%isd ; ied = HI%ied ; jsd = HI%jsd ; jed = HI%jed
22668773 allocate(MEKE%MEKE(isd:ied,jsd:jed), source=0.0)
2267 call register_restart_field(MEKE%MEKE, "MEKE", .false., restart_CS, &
22681 longname="Mesoscale Eddy Kinetic Energy", units="m2 s-2", conversion=US%L_T_to_m_s**2)
2269
22708773 if (MEKE_GMcoeff>=0.) allocate(MEKE%GM_src(isd:ied,jsd:jed), source=0.0)
22711 if (MEKE_FrCoeff>=0. .or. MEKE_bhFrCoeff>=0. .or. MEKE_GMECoeff>=0.) &
22720 allocate(MEKE%mom_src(isd:ied,jsd:jed), source=0.0)
22731 if (MEKE_bhFrCoeff >= 0.) &
22740 allocate(MEKE%mom_src_bh(isd:ied,jsd:jed), source=0.0)
22751 if (MEKE_FrCoeff<0.) MEKE_FrCoeff = 0.
22761 if (MEKE_bhFrCoeff<0.) MEKE_bhFrCoeff = 0.
22771 if (MEKE_GMECoeff>=0.) allocate(MEKE%GME_snk(isd:ied,jsd:jed), source=0.0)
22781 if (MEKE_KhCoeff>=0.) then
22798773 allocate(MEKE%Kh(isd:ied,jsd:jed), source=0.0)
2280 call register_restart_field(MEKE%Kh, "MEKE_Kh", .false., restart_CS, &
2281 longname="Lateral diffusivity from Mesoscale Eddy Kinetic Energy", &
22821 units="m2 s-1", conversion=US%L_to_m**2*US%s_to_T)
2283 endif
22848773 allocate(MEKE%Rd_dx_h(isd:ied,jsd:jed), source=0.0)
22851 if (MEKE_viscCoeff_Ku/=0.) then
22860 allocate(MEKE%Ku(isd:ied,jsd:jed), source=0.0)
2287 call register_restart_field(MEKE%Ku, "MEKE_Ku", .false., restart_CS, &
2288 longname="Lateral viscosity from Mesoscale Eddy Kinetic Energy", &
22890 units="m2 s-1", conversion=US%L_to_m**2*US%s_to_T)
2290 endif
22911 if (sqg_use_MEKE) then
22920 allocate(MEKE%Le(isd:ied,jsd:jed), source=0.0)
2293 call register_restart_field(MEKE%Le, "MEKE_Le", .false., restart_CS, &
2294 longname="Eddy length scale from Mesoscale Eddy Kinetic Energy", &
22950 units="m", conversion=US%L_to_m)
2296 endif
22971 if (Use_Kh_in_MEKE) then
22980 allocate(MEKE%Kh_diff(isd:ied,jsd:jed), source=0.0)
2299 call register_restart_field(MEKE%Kh_diff, "MEKE_Kh_diff", .false., restart_CS, &
2300 longname="Copy of thickness diffusivity for diffusing MEKE", &
23010 units="m2 s-1", conversion=US%L_to_m**2*US%s_to_T)
2302 endif
2303
23041 if (MEKE_viscCoeff_Au/=0.) then
23050 allocate(MEKE%Au(isd:ied,jsd:jed), source=0.0)
2306 call register_restart_field(MEKE%Au, "MEKE_Au", .false., restart_CS, &
2307 longname="Lateral biharmonic viscosity from Mesoscale Eddy Kinetic Energy", &
23080 units="m4 s-1", conversion=US%L_to_m**4*US%s_to_T)
2309 endif
2310
2311end subroutine MEKE_alloc_register_restart
2312
2313!> Deallocates any variables allocated in MEKE_alloc_register_restart.
23141subroutine MEKE_end(MEKE)
2315 type(MEKE_type), intent(inout) :: MEKE !< A structure with MEKE-related fields.
2316
2317 ! NOTE: MEKE will always be allocated by MEKE_init, even if MEKE is disabled.
2318 ! So these must all be conditional, even though MEKE%MEKE and MEKE%Rd_dx_h
2319 ! are always allocated (when MEKE is enabled)
2320
2321 ! Unmap before deallocating, mirroring the maps at the end of MEKE_init.
2322 if (allocated(MEKE%MEKE)) then
2323 !$omp target exit data map(delete: MEKE%MEKE)
2324 endif
2325 if (allocated(MEKE%GM_src)) then
2326 !$omp target exit data map(delete: MEKE%GM_src)
2327 endif
2328 if (allocated(MEKE%mom_src)) then
2329 !$omp target exit data map(delete: MEKE%mom_src)
2330 endif
2331 if (allocated(MEKE%mom_src_bh)) then
2332 !$omp target exit data map(delete: MEKE%mom_src_bh)
2333 endif
2334 if (allocated(MEKE%GME_snk)) then
2335 !$omp target exit data map(delete: MEKE%GME_snk)
2336 endif
2337 if (allocated(MEKE%Kh)) then
2338 !$omp target exit data map(delete: MEKE%Kh)
2339 endif
2340 if (allocated(MEKE%Kh_diff)) then
2341 !$omp target exit data map(delete: MEKE%Kh_diff)
2342 endif
2343 if (allocated(MEKE%Rd_dx_h)) then
2344 !$omp target exit data map(delete: MEKE%Rd_dx_h)
2345 endif
2346 if (allocated(MEKE%Ku)) then
2347 !$omp target exit data map(delete: MEKE%Ku)
2348 endif
2349 if (allocated(MEKE%Au)) then
2350 !$omp target exit data map(delete: MEKE%Au)
2351 endif
2352 if (allocated(MEKE%Le)) then
2353 !$omp target exit data map(delete: MEKE%Le)
2354 endif
2355
23561 if (allocated(MEKE%Au)) deallocate(MEKE%Au)
23571 if (allocated(MEKE%Kh_diff)) deallocate(MEKE%Kh_diff)
23581 if (allocated(MEKE%Ku)) deallocate(MEKE%Ku)
23591 if (allocated(MEKE%Rd_dx_h)) deallocate(MEKE%Rd_dx_h)
23601 if (allocated(MEKE%Kh)) deallocate(MEKE%Kh)
23611 if (allocated(MEKE%GME_snk)) deallocate(MEKE%GME_snk)
23621 if (allocated(MEKE%mom_src)) deallocate(MEKE%mom_src)
23631 if (allocated(MEKE%mom_src_bh)) deallocate(MEKE%mom_src_bh)
23641 if (allocated(MEKE%GM_src)) deallocate(MEKE%GM_src)
23651 if (allocated(MEKE%MEKE)) deallocate(MEKE%MEKE)
23661 if (allocated(MEKE%Le)) deallocate(MEKE%Le)
23671end subroutine MEKE_end
2368
2369!> \namespace mom_meke
2370!!
2371!! \section section_MEKE The Mesoscale Eddy Kinetic Energy (MEKE) framework
2372!!
2373!! The MEKE framework accounts for the mean potential energy removed by
2374!! the first order closures used to parameterize mesoscale eddies.
2375!! It requires closure at the second order, namely dissipation and transport
2376!! of eddy energy.
2377!!
2378!! Monitoring the sub-grid scale eddy energy budget provides a means to predict
2379!! a sub-grid eddy-velocity scale which can be used in the lower order closures.
2380!!
2381!! \subsection section_MEKE_equations MEKE equations
2382!!
2383!! The eddy kinetic energy equation is:
2384!! \f[ \partial_{\tilde{t}} E =
2385!! \overbrace{ \dot{E}_b + \gamma_\eta \dot{E}_\eta + \gamma_v \dot{E}_v
2386!! }^\text{sources}
2387!! - \overbrace{ ( \lambda + C_d | U_d | \gamma_b^2 ) E
2388!! }^\text{local dissipation}
2389!! + \overbrace{ \nabla \cdot ( ( \kappa_E + \gamma_M \kappa_M ) \nabla E
2390!! - \kappa_4 \nabla^3 E )
2391!! }^\text{smoothing}
2392!! \f]
2393!! where \f$ E \f$ is the eddy kinetic energy (variable <code>MEKE</code>) with units of
2394!! m<sup>2</sup>s<sup>-2</sup>,
2395!! and \f$\tilde{t} = a t\f$ is a scaled time. The non-dimensional factor
2396!! \f$ a\geq 1 \f$ is used to accelerate towards equilibrium.
2397!!
2398!! The MEKE equation is two-dimensional and obtained by depth averaging the
2399!! the three-dimensional eddy energy equation. In the following expressions
2400!! \f$ \left< \phi \right> = \frac{1}{H} \int^\eta_{-D} \phi \, dz \f$ maps
2401!! three dimensional terms into the two-dimensional quantities needed.
2402!!
2403!! \subsubsection section_MEKE_source_terms MEKE source terms
2404!!
2405!! The source term \f$ \dot{E}_b \f$ is a constant background source
2406!! of energy intended to avoid the limit \f$E\rightarrow 0\f$.
2407!!
2408!! The "GM" source term
2409!! \f[ \dot{E}_\eta = - \left< \overline{w^\prime b^\prime} \right>
2410!! = \left< \kappa_h N^2S^2 \right>
2411!! \approx \left< \kappa_h g\prime |\nabla_\sigma \eta|^2 \right>\f]
2412!! equals the mean potential energy removed by the Gent-McWilliams closure,
2413!! and is excluded/included in the MEKE budget by the efficiency parameter
2414!! \f$ \gamma_\eta \in [0,1] \f$.
2415!!
2416!! The "frictional" source term
2417!! \f[ \dot{E}_{v} = \left< \partial_i u_j \tau_{ij} \right> \f]
2418!! equals the mean kinetic energy removed by lateral viscous fluxes, and
2419!! is excluded/included in the MEKE budget by the efficiency parameter
2420!! \f$ \gamma_v \in [0,1] \f$.
2421!!
2422!! \subsubsection section_MEKE_dissipation_terms MEKE dissipation terms
2423!!
2424!! The local dissipation of \f$ E \f$ is parameterized through a linear
2425!! damping, \f$\lambda\f$, and bottom drag, \f$ C_d | U_d | \gamma_b^2 \f$.
2426!! The \f$ \gamma_b \f$ accounts for the weak projection of the column-mean
2427!! eddy velocity to the bottom. In other words, the bottom velocity is
2428!! estimated as \f$ \gamma_b U_e \f$.
2429!! The bottom drag coefficient, \f$ C_d \f$ is the same as that used in the bottom
2430!! friction in the mean model equations.
2431!!
2432!! The bottom drag velocity scale, \f$ U_d \f$, has contributions from the
2433!! resolved state and \f$ E \f$:
2434!! \f[ U_d = \sqrt{ U_b^2 + |u|^2_{z=-D} + |\gamma_b U_e|^2 } .\f]
2435!! where the eddy velocity scale, \f$ U_e \f$, is given by:
2436!! \f[ U_e = \sqrt{ 2 E } .\f]
2437!! \f$ U_b \f$ is a constant background bottom velocity scale and is
2438!! typically not used (i.e. set to zero).
2439!!
2440!! Following \cite jansen2015, the projection of eddy energy on to the bottom
2441!! is given by the ratio of bottom energy to column mean energy:
2442!! \f[
2443!! \gamma_b^2 = \frac{E_b}{E} = \gamma_{d0}
2444!! + \left( 1 + c_{b} \frac{L_d}{L_f} \right)^{-\frac{4}{5}}
2445!! ,
2446!! \f]
2447!! \f[
2448!! \gamma_b^2 \leftarrow \max{\left( \gamma_b^2, \gamma_{min}^2 \right)}
2449!! .
2450!! \f]
2451!!
2452!! \subsection section_MEKE_smoothing MEKE smoothing terms
2453!!
2454!! \f$ E \f$ is laterally diffused by a diffusivity \f$ \kappa_E + \gamma_M
2455!! \kappa_M \f$ where \f$ \kappa_E \f$ is a constant diffusivity and the term
2456!! \f$ \gamma_M \kappa_M \f$ is a "self diffusion" using the diffusivity
2457!! calculated in the section \ref section_MEKE_diffusivity.
2458!! \f$ \kappa_4 \f$ is a constant bi-harmonic diffusivity.
2459!!
2460!! \subsection section_MEKE_diffusivity Diffusivity derived from MEKE
2461!!
2462!! The predicted eddy velocity scale, \f$ U_e \f$, can be combined with a
2463!! mixing length scale to form a diffusivity.
2464!! The primary use of a MEKE derived diffusivity is for use in thickness
2465!! diffusion (module mom_thickness_diffuse) and optionally in along
2466!! isopycnal mixing of tracers (module mom_tracer_hor_diff).
2467!! The original form used (enabled with MEKE_OLD_LSCALE=True):
2468!!
2469!! \f[ \kappa_M = \gamma_\kappa \sqrt{ \gamma_t^2 U_e^2 A_\Delta } \f]
2470!!
2471!! where \f$ A_\Delta \f$ is the area of the grid cell.
2472!! Following \cite jansen2015, we now use
2473!!
2474!! \f[ \kappa_M = \gamma_\kappa l_M \sqrt{ \gamma_t^2 U_e^2 } \f]
2475!!
2476!! where \f$ \gamma_\kappa \in [0,1] \f$ is a non-dimensional factor and,
2477!! following \cite jansen2015, \f$\gamma_t^2\f$ is the ratio of barotropic
2478!! eddy energy to column mean eddy energy given by
2479!! \f[
2480!! \gamma_t^2 = \frac{E_t}{E} = \left( 1 + c_{t} \frac{L_d}{L_f} \right)^{-\frac{1}{4}}
2481!! ,
2482!! \f]
2483!! \f[
2484!! \gamma_t^2 \leftarrow \max{\left( \gamma_t^2, \gamma_{min}^2 \right)}
2485!! .
2486!! \f]
2487!!
2488!! The length-scale is a configurable combination of multiple length scales:
2489!!
2490!! \f[
2491!! l_M = \left(
2492!! \frac{\alpha_d}{L_d}
2493!! + \frac{\alpha_f}{L_f}
2494!! + \frac{\alpha_R}{L_R}
2495!! + \frac{\alpha_e}{L_e}
2496!! + \frac{\alpha_\Delta}{L_\Delta}
2497!! + \frac{\delta[L_c]}{L_c}
2498!! \right)^{-1}
2499!! \f]
2500!!
2501!! where
2502!!
2503!! \f{eqnarray*}{
2504!! L_d & = & \sqrt{\frac{c_g^2}{f^2+2\beta c_g}} \sim \frac{ c_g }{f} \\\\
2505!! L_R & = & \sqrt{\frac{U_e}{\beta^*}} \\\\
2506!! L_e & = & \frac{U_e}{|S| N} \\\\
2507!! L_f & = & \frac{H}{c_d} \\\\
2508!! L_\Delta & = & \sqrt{A_\Delta} .
2509!! \f}
2510!!
2511!! \f$L_c\f$ is a constant and \f$\delta[L_c]\f$ is the impulse function so that the term
2512!! \f$\frac{\delta[L_c]}{L_c}\f$ evaluates to \f$\frac{1}{L_c}\f$ when \f$L_c\f$ is non-zero
2513!! but is dropped if \f$L_c=0\f$.
2514!!
2515!! \f$\beta^*\f$ is the effective \f$\beta\f$ that combines both the planetary vorticity
2516!! gradient (i.e. \f$\beta=\nabla f\f$) and the topographic \f$\beta\f$ effect,
2517!! with the latter weighed by a weighting constant, \f$c_\beta\f$, that varies
2518!! from 0 to 1, so that \f$c_\beta=0\f$ means the topographic \f$\beta\f$ effect is ignored,
2519!! while \f$c_\beta=1\f$ means it is fully considered. The new \f$\beta^*\f$ therefore
2520!! takes the form of
2521!!
2522!! \f[
2523!! \beta^* = \sqrt{( \partial_xf - c_\beta\frac{f}{D}\partial_xD )^2 +
2524!! ( \partial_yf - c_\beta\frac{f}{D}\partial_yD )^2}
2525!! \f]
2526!! where \f$D\f$ is water column depth at T points.
2527!!
2528!! \subsection section_MEKE_viscosity Viscosity derived from MEKE
2529!!
2530!! As for \f$ \kappa_M \f$, the predicted eddy velocity scale can be
2531!! used to form a harmonic eddy viscosity,
2532!!
2533!! \f[ \kappa_u = \gamma_u \sqrt{ U_e^2 A_\Delta } \f]
2534!!
2535!! as well as a biharmonic eddy viscosity,
2536!!
2537!! \f[ \kappa_4 = \gamma_4 \sqrt{ U_e^2 A_\Delta^3 } \f]
2538!!
2539!! \subsection section_MEKE_limit_case Limit cases for local source-dissipative balance
2540!!
2541!! Note that in steady-state (or when \f$ a>>1 \f$) and there is no
2542!! diffusion of \f$ E \f$ then
2543!! \f[ \overline{E} \approx \frac{ \dot{E}_b + \gamma_\eta \dot{E}_\eta +
2544!! \gamma_v \dot{E}_v }{ \lambda + C_d|U_d|\gamma_b^2 } . \f]
2545!!
2546!! In the linear drag limit, where
2547!! \f$ U_e << \min(U_b, |u|_{z=-D}, C_d^{-1}\lambda) \f$, the equilibrium becomes
2548!! \f$ \overline{E} \approx \frac{ \dot{E}_b + \gamma_\eta \dot{E}_\eta +
2549!! \gamma_v \dot{E}_v }{ \lambda + C_d \sqrt{ U_b^2 + |u|^2_{z=-D} } } \f$.
2550!!
2551!! In the nonlinear drag limit, where \f$ U_e >> \max(U_b, |u|_{z=-D}, C_d^{-1}\lambda) \f$,
2552!! the equilibrium becomes
2553!! \f$ \overline{E} \approx \left( \frac{ \dot{E}_b + \gamma_\eta \dot{E}_\eta +
2554!! \gamma_v \dot{E}_v }{ \sqrt{2} C_d \gamma_b^3 } \right)^\frac{2}{3} \f$.
2555!!
2556!! \subsubsection section_MEKE_module_parameters MEKE module parameters
2557!!
2558!! | Symbol | Module parameter |
2559!! | ------ | --------------- |
2560!! | - | <code>USE_MEKE</code> |
2561!! | \f$ a \f$ | <code>MEKE_DTSCALE</code> |
2562!! | \f$ \dot{E}_b \f$ | <code>MEKE_BGSRC</code> |
2563!! | \f$ \gamma_\eta \f$ | <code>MEKE_GMCOEFF</code> |
2564!! | \f$ \gamma_v \f$ | <code>MEKE_FrCOEFF</code> |
2565!! | \f$ \lambda \f$ | <code>MEKE_DAMPING</code> |
2566!! | \f$ U_b \f$ | <code>MEKE_USCALE</code> |
2567!! | \f$ \gamma_{d0} \f$ | <code>MEKE_CD_SCALE</code> |
2568!! | \f$ c_{b} \f$ | <code>MEKE_CB</code> |
2569!! | \f$ c_{t} \f$ | <code>MEKE_CT</code> |
2570!! | \f$ \kappa_E \f$ | <code>MEKE_KH</code> |
2571!! | \f$ \kappa_4 \f$ | <code>MEKE_K4</code> |
2572!! | \f$ \gamma_\kappa \f$ | <code>MEKE_KHCOEFF</code> |
2573!! | \f$ \gamma_M \f$ | <code>MEKE_KHMEKE_FAC</code> |
2574!! | \f$ \gamma_u \f$ | <code>MEKE_VISCOSITY_COEFF_KU</code> |
2575!! | \f$ \gamma_4 \f$ | <code>MEKE_VISCOSITY_COEFF_AU</code> |
2576!! | \f$ \gamma_{min}^2 \f$| <code>MEKE_MIN_GAMMA2</code> |
2577!! | \f$ \alpha_d \f$ | <code>MEKE_ALPHA_DEFORM</code> |
2578!! | \f$ \alpha_f \f$ | <code>MEKE_ALPHA_FRICT</code> |
2579!! | \f$ \alpha_R \f$ | <code>MEKE_ALPHA_RHINES</code> |
2580!! | \f$ \alpha_e \f$ | <code>MEKE_ALPHA_EADY</code> |
2581!! | \f$ \alpha_\Delta \f$ | <code>MEKE_ALPHA_GRID</code> |
2582!! | \f$ L_c \f$ | <code>MEKE_FIXED_MIXING_LENGTH</code> |
2583!! | \f$ c_\beta \f$ | <code>MEKE_TOPOGRAPHIC_BETA</code> |
2584!! | - | <code>MEKE_KHTH_FAC</code> |
2585!! | - | <code>MEKE_KHTR_FAC</code> |
2586!!
2587!! | Symbol | Model parameter |
2588!! | ------ | --------------- |
2589!! | \f$ C_d \f$ | <code>CDRAG</code> |
2590!!
2591!! \subsection section_MEKE_references References
2592!!
2593!! Jansen, M. F., A. J. Adcroft, R. Hallberg, and I. M. Held, 2015: Parameterization of eddy fluxes based on a
2594!! mesoscale energy budget. Ocean Modelling, 92, 28--41, http://doi.org/10.1016/j.ocemod.2015.05.007 .
2595!!
2596!! Marshall, D. P., and A. J. Adcroft, 2010: Parameterization of ocean eddies: Potential vorticity mixing, energetics
2597!! and Arnold first stability theorem. Ocean Modelling, 32, 188--204, http://doi.org/10.1016/j.ocemod.2010.02.001 .
2598
25990end module MOM_MEKE
2600