← back to index

src/parameterizations/vertical/MOM_set_viscosity.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!> Calculates various values related to the bottom boundary layer, such as the viscosity and
8!! thickness of the BBL (set_viscous_BBL).
9module MOM_set_visc
10
11use MOM_ALE, only : ALE_CS, ALE_remap_velocities, ALE_remap_interface_vals, ALE_remap_vertex_vals
12use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, CLOCK_ROUTINE
13use MOM_cvmix_conv, only : cvmix_conv_is_used
14use MOM_CVMix_ddiff, only : CVMix_ddiff_is_used
15use MOM_cvmix_shear, only : cvmix_shear_is_used
16use MOM_debugging, only : uvchksum, hchksum
17use MOM_diag_mediator, only : post_data, register_diag_field, safe_alloc_ptr
18use MOM_diag_mediator, only : diag_ctrl, time_type
19use MOM_domains, only : pass_var, CORNER
20use MOM_EOS, only : calculate_density, calculate_density_derivs, calculate_specific_vol_derivs
21use MOM_error_handler, only : MOM_error, FATAL, WARNING
22use MOM_file_parser, only : get_param, log_param, log_version, param_file_type
23use MOM_file_parser, only : openParameterBlock, closeParameterBlock
24use MOM_forcing_type, only : forcing, mech_forcing, find_ustar
25use MOM_grid, only : ocean_grid_type
26use MOM_hor_index, only : hor_index_type
27use MOM_interface_heights, only : thickness_to_dz
28use MOM_intrinsic_functions, only : cuberoot
29use MOM_io, only : slasher, MOM_read_data, vardesc, var_desc
30use MOM_kappa_shear, only : kappa_shear_is_used, kappa_shear_at_vertex
31use MOM_open_boundary, only : ocean_OBC_type, OBC_segment_type, OBC_NONE, OBC_DIRECTION_E
32use MOM_open_boundary, only : OBC_DIRECTION_W, OBC_DIRECTION_N, OBC_DIRECTION_S
33use MOM_restart, only : register_restart_field, query_initialized, MOM_restart_CS
34use MOM_restart, only : register_restart_field_as_obsolete, register_restart_pair
35use MOM_safe_alloc, only : safe_alloc_ptr, safe_alloc_alloc
36use MOM_unit_scaling, only : unit_scale_type
37use MOM_variables, only : thermo_var_ptrs, vertvisc_type, porous_barrier_type
38use MOM_verticalGrid, only : verticalGrid_type, get_thickness_units
39
40implicit none ; private
41
42#include <MOM_memory.h>
43
44public set_viscous_BBL, set_viscous_ML, set_visc_init, set_visc_end
45public set_visc_register_restarts, set_u_at_v, set_v_at_u
46public remap_vertvisc_aux_vars
47
48! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional
49! consistency testing. These are noted in comments with units like Z, H, L, and T, along with
50! their mks counterparts with notation like "a velocity [Z T-1 ~> m s-1]". If the units
51! vary with the Boussinesq approximation, the Boussinesq variant is given first.
52
53!> Control structure for MOM_set_visc
54type, public :: set_visc_CS ; private
55 logical :: initialized = .false. !< True if this control structure has been initialized.
56 real :: Hbbl !< The static bottom boundary layer thickness [H ~> m or kg m-2].
57 !! Runtime parameter `HBBL`.
58 real :: dz_bbl !< The static bottom boundary layer thickness in height units [Z ~> m].
59 !! Runtime parameter `HBBL`.
60 real :: cdrag !< The quadratic drag coefficient [nondim].
61 !! Runtime parameter `CDRAG`.
62 real :: c_Smag !< The Laplacian Smagorinsky coefficient for
63 !! calculating the drag in channels [nondim].
64 real :: drag_bg_vel !< An assumed unresolved background velocity for
65 !! calculating the bottom drag [L T-1 ~> m s-1].
66 !! Runtime parameter `DRAG_BG_VEL`.
67 !! Should not be used if BBL_USE_TIDAL_BG is True.
68 real :: BBL_thick_min !< The minimum bottom boundary layer thickness [Z ~> m].
69 !! This might be Kv / (cdrag * drag_bg_vel) to give
70 !! Kv as the minimum near-bottom viscosity.
71 real :: Htbl_shelf !< A nominal thickness of the surface boundary layer for use
72 !! in calculating the near-surface velocity [H ~> m or kg m-2].
73 real :: Htbl_shelf_min !< The minimum surface boundary layer thickness [Z ~> m].
74 real :: KV_BBL_min !< The minimum viscosity in the bottom boundary layer [H Z T-1 ~> m2 s-1 or Pa s]
75 real :: KV_TBL_min !< The minimum viscosity in the top boundary layer [H Z T-1 ~> m2 s-1 or Pa s]
76 logical :: bottomdraglaw !< If true, the bottom stress is calculated with a
77 !! drag law c_drag*|u|*u. The velocity magnitude
78 !! may be an assumed value or it may be based on the
79 !! actual velocity in the bottommost `HBBL`, depending
80 !! on whether linear_drag is true.
81 !! Runtime parameter `BOTTOMDRAGLAW`.
82 logical :: bottomdragmap !< If true, apply the spatially varying drag coefficient (cdrag_2d)
83 !! instead of the spatially uniform drag coefficient (cdrag).
84 logical :: body_force_drag !< If true, the bottom stress is imposed as an explicit body force
85 !! applied over a fixed distance from the bottom, rather than as an
86 !! implicit calculation based on an enhanced near-bottom viscosity.
87 logical :: BBL_use_EOS !< If true, use the equation of state in determining
88 !! the properties of the bottom boundary layer.
89 logical :: linear_drag !< If true, the drag law is cdrag*`DRAG_BG_VEL`*u.
90 !! Runtime parameter `LINEAR_DRAG`.
91 logical :: Channel_drag !< If true, the drag is exerted directly on each layer
92 !! according to what fraction of the bottom they overlie.
93 real :: Chan_drag_max_vol !< The maximum bottom boundary layer volume within which the
94 !! channel drag is applied, normalized by the full cell area,
95 !! or a negative value to apply no maximum [Z ~> m].
96 real :: channel_break_depth !< When CHANNEL_DRAG is true, the bathymetric depth interpolated
97 !! to the vorticity point is a combination of the harmonic mean of the
98 !! adjacent velocity point depths below this depth [Z ~> m] and the
99 !! arithmetic mean of the adjacent depths above it, to roughly mimic a
100 !! continental shelf break profile. The internal version of this depth
101 !! uses the same offset (G%Z_ref) as the bathymetry.
102 logical :: correct_BBL_bounds !< If true, uses the correct bounds on the BBL thickness and
103 !! viscosity so that the bottom layer feels the intended drag.
104 logical :: RiNo_mix !< If true, use Richardson number dependent mixing.
105 logical :: dynamic_viscous_ML !< If true, use a bulk Richardson number criterion to
106 !! determine the mixed layer thickness for viscosity.
107 real :: bulk_Ri_ML !< The bulk mixed layer used to determine the
108 !! thickness of the viscous mixed layer [nondim]
109 real :: omega !< The Earth's rotation rate [T-1 ~> s-1].
110 real :: ustar_min !< A minimum value of ustar to avoid numerical
111 !! problems [H T-1 ~> m s-1 or kg m-2 s-1]. If the value is
112 !! small enough, this should not affect the solution.
113 real :: TKE_decay !< The ratio of the natural Ekman depth to the TKE
114 !! decay scale [nondim]
115 real :: omega_frac !< When setting the decay scale for turbulence, use this
116 !! fraction of the absolute rotation rate blended with the local
117 !! value of f, as sqrt((1-of)*f^2 + of*4*omega^2) [nondim]
118 real :: tideampfac2 !< A factor to multiply by tideamp to convert to a mean ustar,
119 !! accounts for conversion of amplitude to mean magnitude over
120 !! a time average much longer than the tidal periods and for
121 !! non-commuting conversion of mean tideamp to mean ustar**3 [nondim]
122 logical :: concave_trigonometric_L !< If true, use trigonometric expressions to determine the
123 !! fractional open interface lengths for concave topography.
124 integer :: answer_date !< The vintage of the order of arithmetic and expressions in the set
125 !! viscosity calculations. Values below 20190101 recover the answers
126 !! from the end of 2018, while higher values use updated and more robust
127 !! forms of the same expressions.
128 logical :: debug !< If true, write verbose checksums for debugging purposes.
129 logical :: BBL_use_tidal_bg !< If true, use a tidal background amplitude for the bottom velocity
130 !! when computing the bottom stress.
131 character(len=200) :: inputdir !< The directory for input files.
132 type(ocean_OBC_type), pointer :: OBC => NULL() !< Open boundaries control structure
133 type(diag_ctrl), pointer :: diag => NULL() !< A structure that is used to
134 !! regulate the timing of diagnostic output.
135 ! Allocatable data arrays
136 real, allocatable, dimension(:,:) :: cdrag_u !< The spatially varying quadratic drag coefficient [nondim]
137 real, allocatable, dimension(:,:) :: cdrag_v !< The spatially varying quadratic drag coefficient [nondim]
138 real, allocatable, dimension(:,:) :: tideamp !< RMS tidal amplitude at h points [Z T-1 ~> m s-1]
139 ! Diagnostic arrays
140 real, allocatable, dimension(:,:) :: bbl_u !< BBL mean U current [L T-1 ~> m s-1]
141 real, allocatable, dimension(:,:) :: bbl_v !< BBL mean V current [L T-1 ~> m s-1]
142 !>@{ Diagnostics handles
143 integer :: id_bbl_thick_u = -1, id_kv_bbl_u = -1, id_bbl_u = -1
144 integer :: id_bbl_thick_v = -1, id_kv_bbl_v = -1, id_bbl_v = -1
145 integer :: id_Ray_u = -1, id_Ray_v = -1
146 integer :: id_nkml_visc_u = -1, id_nkml_visc_v = -1
147 !>@}
148end type set_visc_CS
149
150contains
151
152!> Calculates the thickness of the bottom boundary layer and the viscosity within that layer.
15312subroutine set_viscous_BBL(u, v, h, tv, visc, G, GV, US, CS, pbv)
154 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
155 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
156 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
157 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), &
158 intent(in) :: u !< The zonal velocity [L T-1 ~> m s-1].
159 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), &
160 intent(in) :: v !< The meridional velocity [L T-1 ~> m s-1].
161 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
162 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2].
163 type(thermo_var_ptrs), intent(in) :: tv !< A structure containing pointers to any
164 !! available thermodynamic fields. Absent fields
165 !! have NULL pointers.
166 type(vertvisc_type), intent(inout) :: visc !< A structure containing vertical viscosities and
167 !! related fields.
168 type(set_visc_CS), intent(inout) :: CS !< The control structure returned by a previous
169 !! call to set_visc_init.
170 type(porous_barrier_type),intent(in) :: pbv !< porous barrier fractional cell metrics
171
172 ! Local variables
173 real, dimension(SZIB_(G),SZJB_(G)) :: &
17424 ustar, & ! The bottom friction velocity [H T-1 ~> m s-1 or kg m-2 s-1].
17524 T_EOS, & ! The temperature used to calculate the partial derivatives
176 ! of density with T and S [C ~> degC].
17724 S_EOS, & ! The salinity used to calculate the partial derivatives
178 ! of density with T and S [S ~> ppt].
17924 dR_dT, & ! Partial derivative of the density in the bottom boundary
180 ! layer with temperature [R C-1 ~> kg m-3 degC-1].
18124 dR_dS, & ! Partial derivative of the density in the bottom boundary
182 ! layer with salinity [R S-1 ~> kg m-3 ppt-1].
18324 press, & ! The pressure at which dR_dT and dR_dS are evaluated [R L2 T-2 ~> Pa].
18424 umag_avg, & ! The average magnitude of velocities in the bottom boundary layer [L T-1 ~> m s-1].
18524 h_bbl_drag, & ! The thickness over which to apply drag as a body force [H ~> m or kg m-2].
18624 dz_bbl_drag ! The vertical height over which to apply drag as a body force [Z ~> m].
187 real :: htot ! Sum of the layer thicknesses up to some point [H ~> m or kg m-2].
188 real :: dztot ! Distance from the bottom up to some point [Z ~> m].
189 real :: htot_vel ! Sum of the layer thicknesses up to some point [H ~> m or kg m-2].
190 real :: dztot_vel ! Distance from the bottom up to some point [Z ~> m].
191
192 real :: Rhtot ! Running sum of thicknesses times the layer potential
193 ! densities [H R ~> kg m-2 or kg2 m-5].
194 real, dimension(SZIB_(G),SZJ_(G)) :: &
19512 D_u, & ! Bottom depth linearly interpolated to u points [Z ~> m].
19624 mask_u ! A mask that disables any contributions from u points that
197 ! are land or past open boundary conditions [nondim], 0 or 1.
198 real, dimension(SZI_(G),SZJB_(G)) :: &
19924 D_v, & ! Bottom depth linearly interpolated to v points [Z ~> m].
20024 mask_v ! A mask that disables any contributions from v points that
201 ! are land or past open boundary conditions [nondim], 0 or 1.
202 real, dimension(SZIB_(G),SZJB_(G),SZK_(GV)) :: &
20324 h_at_vel, & ! Layer thickness at a velocity point, using an upwind-biased
204 ! second order accurate estimate based on the previous velocity
205 ! direction [H ~> m or kg m-2].
20624 h_vel, & ! Arithmetic mean of the layer thicknesses adjacent to a
207 ! velocity point [H ~> m or kg m-2].
20824 dz_at_vel, & ! Vertical extent of a layer, using an upwind-biased
209 ! second order accurate estimate based on the previous velocity
210 ! direction [Z ~> m].
21124 dz_vel, & ! Arithmetic mean of the difference in across the layers adjacent
212 ! to a velocity point [Z ~> m].
21324 T_vel, & ! Arithmetic mean of the layer temperatures adjacent to a
214 ! velocity point [C ~> degC].
21524 S_vel, & ! Arithmetic mean of the layer salinities adjacent to a
216 ! velocity point [S ~> ppt].
21724 SpV_vel, & ! Arithmetic mean of the layer averaged specific volumes adjacent to a
218 ! velocity point [R-1 ~> m3 kg-1].
21924 Rml_vel ! Arithmetic mean of the layer coordinate densities adjacent
220 ! to a velocity point [R ~> kg m-3].
22124 real :: dz(SZI_(G),SZJ_(G),SZK_(GV)) ! Height change across layers [Z ~> m]
222
223 real :: h_vel_pos ! The arithmetic mean thickness at a velocity point
224 ! plus H_neglect to avoid 0 values [H ~> m or kg m-2].
225 real :: ustarsq ! 400 times the square of ustar, times
226 ! Rho0 divided by G_Earth and the conversion
227 ! from m to thickness units [H R ~> kg m-2 or kg2 m-5].
228 real :: cdrag ! The drag coefficient [nondim].
229 real :: cdrag_sqrt ! Square root of the drag coefficient [nondim].
230 real :: cdrag_sqrt_H ! Square root of the drag coefficient, times a unit conversion factor
231 ! from lateral lengths to layer thicknesses [H L-1 ~> nondim or kg m-3].
232 real :: cdrag_sqrt_H_RL ! Square root of the drag coefficient, times a unit conversion factor from
233 ! density times lateral lengths to layer thicknesses [H L-1 R-1 ~> m3 kg-1 or nondim]
234 real :: cdrag_L_to_H ! The drag coefficient times conversion factors from lateral
235 ! distance to thickness units [H L-1 ~> nondim or kg m-3]
236 real :: cdrag_RL_to_H ! The drag coefficient times conversion factors from density times lateral
237 ! distance to thickness units [H L-1 R-1 ~> m3 kg-1 or nondim]
238 real :: cdrag_conv ! The drag coefficient times a combination of static conversion factors and in
239 ! situ density or Boussinesq reference density [H L-1 ~> nondim or kg m-3]
240 real :: oldfn ! The integrated energy required to
241 ! entrain up to the bottom of the layer,
242 ! divided by G_Earth [H R ~> kg m-2 or kg2 m-5].
243 real :: Dfn ! The increment in oldfn for entraining
244 ! the layer [H R ~> kg m-2 or kg2 m-5].
245 real :: frac_used ! The fraction of the present layer that contributes to Dh and Ddz [nondim]
246 real :: Dh ! The increment in layer thickness from
247 ! the present layer [H ~> m or kg m-2].
248 real :: Ddz ! The increment in height change from the present layer [Z ~> m].
249 real :: bbl_thick ! The thickness of the bottom boundary layer [Z ~> m].
250 real :: BBL_thick_max ! A huge upper bound on the boundary layer thickness [Z ~> m].
251 real :: kv_bbl ! The bottom boundary layer viscosity [H Z T-1 ~> m2 s-1 or Pa s]
252 real :: C2f ! C2f = 2*f at velocity points [T-1 ~> s-1].
25324 real :: u2_bg(SZIB_(G),SZJB_(G)) ! The square of an assumed background velocity, for calculating the mean
254 ! magnitude near the bottom for use in the quadratic bottom drag [L2 T-2 ~> m2 s-2].
255 real :: hwtot ! Sum of the thicknesses used to calculate
256 ! the near-bottom velocity magnitude [H ~> m or kg m-2].
257 real :: I_hwtot ! The Adcroft reciprocal of hwtot [H-1 ~> m-1 or m2 kg-1].
258 real :: dzwtot ! The vertical extent of the region used to calculate
259 ! the near-bottom velocity magnitude [Z ~> m].
260 real :: hutot ! Running sum of thicknesses times the velocity
261 ! magnitudes [H L T-1 ~> m2 s-1 or kg m-1 s-1].
262 real :: Thtot ! Running sum of thickness times temperature [C H ~> degC m or degC kg m-2].
263 real :: Shtot ! Running sum of thickness times salinity [S H ~> ppt m or ppt kg m-2].
264 real :: SpV_htot ! Running sum of thickness times specific volume [H R-1 ~> m4 kg-1 or m]
265 real :: hweight ! The thickness of a layer that is within Hbbl
266 ! of the bottom [H ~> m or kg m-2].
267 real :: dzweight ! The counterpart of hweight in height units [Z ~> m].
268 real :: v_at_u, u_at_v ! v at a u point or vice versa [L T-1 ~> m s-1].
269 real :: Rho0x400_G ! 400*Rho0/G_Earth, times unit conversion factors
270 ! [R T2 H-1 ~> kg s2 m-4 or s2 m-1].
271 ! The 400 is a constant proposed by Killworth and Edwards, 1999.
272 real, dimension(SZI_(G),SZJ_(G),max(GV%nk_rho_varies,1)) :: &
27324 Rml ! The mixed layer coordinate density [R ~> kg m-3].
27424 real :: p_ref(SZI_(G),SZJ_(G)) ! The pressure used to calculate the coordinate
275 ! density [R L2 T-2 ~> Pa] (usually set to 2e7 Pa = 2000 dbar).
276
277 real :: D_vel ! The bottom depth relative to the shelfbreak depth at a velocity point [Z ~> m].
278 real :: Dp, Dm ! The bottom depths at the edges of a velocity cell relative to the
279 ! shelfbreak depth [Z ~> m].
280 real :: D_vel_p, D_vel_m ! The bottom depths in adjacent velocity points relative to the
281 ! shelfbreak depth [Z ~> m].
282 real :: crv ! crv is the curvature of the bottom depth across a
283 ! cell, times the cell width squared [Z ~> m].
284 real :: slope ! The absolute value of the bottom depth slope across
285 ! a cell times the cell width [Z ~> m].
286 real :: Vol_bbl_chan ! The volume of the bottom boundary layer as used in the channel
287 ! drag parameterization, normalized by the full horizontal area
288 ! of the velocity cell [Z ~> m].
28924 real :: vol_below(SZK_(GV)+1) ! The volume below each interface, normalized by the full
290 ! horizontal area of a velocity cell [Z ~> m].
29124 real :: L(SZK_(GV)+1) ! The fraction of the full cell width that is open at
292 ! the depth of each interface [nondim].
293 ! The next 9 variables are only used for debugging.
29424 real :: L_trig(SZK_(GV)+1) ! The fraction of the full cell width that is open at
295 ! the depth of each interface from trigonometric expressions [nondim].
29624 real :: vol_err_trig(SZK_(GV)+1) ! The error in the volume below based on L_trig [Z ~> m]
29724 real :: vol_err_iter(SZK_(GV)+1) ! The error in the volume below based on L_iter [Z ~> m]
29824 real :: norm_err_trig(SZK_(GV)+1) ! vol_err_trig normalized by vol_below [nondim]
29924 real :: norm_err_iter(SZK_(GV)+1) ! vol_err_iter normalized by vol_below [nondim]
30024 real :: dL_trig_itt(SZK_(GV)+1) ! The difference between estimates of the fraction of the full cell
301 ! width that is open at the depth of each interface [nondim].
302 real :: max_dL_trig_itt ! The largest difference between L and L_trig, for debugging [nondim]
303 real :: max_norm_err_trig ! The largest magnitude value of norm_err_trig in a column [nondim]
304 real :: max_norm_err_iter ! The largest magnitude value of norm_err_iter in a column [nondim]
305
306 real :: h_neglect ! A thickness that is so small it is usually lost
307 ! in roundoff and can be neglected [H ~> m or kg m-2].
308 real :: dz_neglect ! A vertical distance that is so small it is usually lost
309 ! in roundoff and can be neglected [Z ~> m].
310 real :: ustH ! ustar converted to units of H T-1 [H T-1 ~> m s-1 or kg m-2 s-1].
311 real :: root ! A temporary variable [H T-1 ~> m s-1 or kg m-2 s-1].
312
313 real :: Cell_width ! The transverse width of the velocity cell [L ~> m].
314 real :: Rayleigh ! A factor that is multiplied by the layer's velocity magnitude
315 ! to give the Rayleigh drag velocity, times a lateral distance to
316 ! thickness conversion factor [H L-1 ~> nondim or kg m-3].
317 real :: gam ! The ratio of the change in the open interface width
318 ! to the open interface width atop a cell [nondim].
319 real :: BBL_frac ! The fraction of a layer's drag that goes into the
320 ! viscous bottom boundary layer [nondim].
321 real :: BBL_visc_frac ! The fraction of all the drag that is expressed as
322 ! a viscous bottom boundary layer [nondim].
323 real :: h_bbl_fr ! The fraction of the bottom boundary layer in a layer [nondim].
324 real :: h_sum ! The sum of the thicknesses of the layers below the one being
325 ! worked on [H ~> m or kg m-2].
326 real :: tideampfac2_x_0p5 ! tideampfac2 multiplied by the c-grid averaging factor of 0.5
327 real, parameter :: C1_3 = 1.0/3.0, C1_6 = 1.0/6.0, C1_12 = 1.0/12.0 ! Rational constants [nondim]
328 real :: tmp ! A temporary variable, sometimes in [Z ~> m]
32912 logical :: use_BBL_EOS, do_i(SZIB_(G),SZJB_(G))
330 integer, dimension(2,2) :: EOSdom ! The computational domain for the equation of state
331 integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, m, n, K2, nkmb, nkml, jstart
332 integer :: is_OBC, ie_OBC, js_OBC, je_OBC
333 type(ocean_OBC_type), pointer :: OBC => NULL()
334
33512 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
33612 Isq = G%isc-1 ; Ieq = G%IecB ; Jsq = G%jsc-1 ; Jeq = G%JecB
33712 nkmb = GV%nk_rho_varies ; nkml = GV%nkml
33812 h_neglect = GV%H_subroundoff
33912 dz_neglect = GV%dZ_subroundoff
340
34112 Rho0x400_G = 400.0*(GV%H_to_RZ / GV%g_Earth_Z_T2)
34212 tideampfac2_x_0p5 = CS%tideampfac2*0.5
343
34412 if (.not.CS%initialized) call MOM_error(FATAL,"MOM_set_viscosity(BBL): "//&
3450 "Module must be initialized before it is used.")
346
34712 if (.not.CS%bottomdraglaw) return
348
34912 if (CS%debug) then
3500 call uvchksum("Start set_viscous_BBL [uv]", u, v, G%HI, haloshift=1, unscale=US%L_T_to_m_s)
3510 call hchksum(h,"Start set_viscous_BBL h", G%HI, haloshift=1, unscale=GV%H_to_m)
3520 if (associated(tv%T)) call hchksum(tv%T, "Start set_viscous_BBL T", G%HI, haloshift=1, unscale=US%C_to_degC)
3530 if (associated(tv%S)) call hchksum(tv%S, "Start set_viscous_BBL S", G%HI, haloshift=1, unscale=US%S_to_ppt)
3540 if (allocated(tv%SpV_avg)) &
3550 call hchksum(tv%SpV_avg, "Start set_viscous_BBL SpV_avg", G%HI, haloshift=1, unscale=US%kg_m3_to_R)
3560 if (allocated(tv%SpV_avg)) call hchksum(tv%SpV_avg, "Cornerless SpV_avg", G%HI, &
3570 haloshift=1, omit_corners=.true., unscale=US%kg_m3_to_R)
3580 if (associated(tv%T)) call hchksum(tv%T, "Cornerless T", G%HI, haloshift=1, &
3590 omit_corners=.true., unscale=US%C_to_degC)
3600 if (associated(tv%S)) call hchksum(tv%S, "Cornerless S", G%HI, haloshift=1, &
3610 omit_corners=.true., unscale=US%S_to_ppt)
362 endif
363
36412 use_BBL_EOS = associated(tv%eqn_of_state) .and. CS%BBL_use_EOS
36512 OBC => CS%OBC
366
36712 if (.not.CS%bottomdragmap) then
36812 cdrag_sqrt = sqrt(CS%cdrag)
36912 cdrag_sqrt_H = cdrag_sqrt * US%L_to_m * GV%m_to_H
37012 cdrag_sqrt_H_RL = cdrag_sqrt * US%L_to_Z * GV%RZ_to_H
37112 cdrag_L_to_H = CS%cdrag * US%L_to_m * GV%m_to_H
37212 cdrag_RL_to_H = CS%cdrag * US%L_to_Z * GV%RZ_to_H
373 endif
37412 BBL_thick_max = G%Rad_Earth_L * US%L_to_Z
37512 K2 = max(nkmb+1, 2)
376
377 !$omp target enter data map(alloc: dz)
378
379 ! Find the vertical distances across layers.
38012 call thickness_to_dz(h, tv, dz, G, GV, US, halo_size=1, do_offload=.true.)
381
382! With a linear drag law, the friction velocity is already known.
383! if (CS%linear_drag) ustar(:) = cdrag_sqrt_H*CS%drag_bg_vel
384
385 !$omp target enter data map(to: tv, tv%T, tv%S, tv%p_surf, CS) map(alloc: Rml, p_ref, ustar, &
386 !$omp umag_avg, u2_bg, mask_u, mask_v, h_bbl_drag, dz_bbl_drag, do_i, dR_dS, dR_dT, D_u, D_v, &
387 !$omp press, S_EOS, T_EOS, Rml_vel)
388
38912 if ((nkml>0) .and. .not.use_BBL_EOS) then
3900 EOSdom(1,1) = Isq - (G%isd-1) ; EOSdom(1,2) = G%iec+1 - (G%isd-1)
3910 EOSdom(2,1) = Jsq - (G%jsd-1) ; EOSdom(2,2) = G%jec+1 - (G%jsd-1)
3920 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
3930 p_ref(i,j) = tv%P_Ref
394 enddo
3950 do k=1,nkmb
3960 call calculate_density(tv%T(:,:,k), tv%S(:,:,k), p_ref, Rml(:,:,k), tv%eqn_of_state, EOSdom)
397 enddo
398 endif
399
40012 do concurrent (J=js-1:je, i=is-1:ie+1)
40189304 D_v(i,J) = 0.5*(G%bathyT(i,j) + G%bathyT(i,j+1))
40290780 mask_v(i,J) = G%mask2dCv(i,J)
403 enddo
4041464 do concurrent (j=js-1:je+1, I=is-1:ie)
40590024 D_u(I,j) = 0.5*(G%bathyT(i,j) + G%bathyT(i+1,j))
40691488 mask_u(I,j) = G%mask2dCu(I,j)
407 enddo
408
40912 if (associated(OBC) .and. CS%Channel_drag) then
410 !$omp target update from(mask_u, mask_v, D_u, D_v)
411 ! Use a one-sided projection of bottom depths at OBC points.
4120 if (OBC%v_N_OBCs_on_PE) then
4130 Js_OBC = max(js-1, OBC%Js_v_N_obc) ; Je_OBC = min(je, OBC%Je_v_N_obc)
4140 is_OBC = max(is-1, OBC%is_v_N_obc) ; ie_OBC = min(ie+1, OBC%ie_v_N_obc)
415 !$OMP parallel do default(shared)
4160 do J=Js_OBC,Je_OBC ; do i=is_OBC,ie_OBC
4170 if (OBC%segnum_v(i,J) > 0) D_v(i,J) = G%bathyT(i,j) ! OBC_DIRECTION_N
418 enddo ; enddo
419 endif
4200 if (OBC%v_S_OBCs_on_PE) then
421 !$omp target update from(D_v)
4220 Js_OBC = max(js-1, OBC%Js_v_S_obc) ; Je_OBC = min(je, OBC%Je_v_S_obc)
4230 is_OBC = max(is-1, OBC%is_v_S_obc) ; ie_OBC = min(ie+1, OBC%ie_v_S_obc)
424 !$OMP parallel do default(shared)
4250 do J=Js_OBC,Je_OBC ; do i=is_OBC,ie_OBC
4260 if (OBC%segnum_v(i,J) < 0) D_v(i,J) = G%bathyT(i,j+1) ! OBC_DIRECTION_S
427 enddo ; enddo
428 !$omp target update to(D_v)
429 endif
4300 if (OBC%u_E_OBCs_on_PE) then
431 !$omp target update from(D_u)
4320 js_OBC = max(js-1, OBC%js_u_E_obc) ; je_OBC = min(je+1, OBC%je_u_E_obc)
4330 Is_OBC = max(is-1, OBC%Is_u_E_obc) ; Ie_OBC = min(ie, OBC%Ie_u_E_obc)
434 !$OMP parallel do default(shared)
4350 do j=js_OBC,je_OBC ; do I=Is_OBC,Ie_OBC
4360 if (OBC%segnum_u(I,j) > 0) D_u(I,j) = G%bathyT(i,j) ! OBC_DIRECTION_E
437 enddo ; enddo
438 !$omp target update to(D_u)
439 endif
4400 if (OBC%u_W_OBCs_on_PE) then
4410 js_OBC = max(js-1, OBC%js_u_W_obc) ; je_OBC = min(je+1, OBC%je_u_W_obc)
4420 Is_OBC = max(is-1, OBC%Is_u_W_obc) ; Ie_OBC = min(ie, OBC%Ie_u_W_obc)
443 !$OMP parallel do default(shared)
4440 do j=js_OBC,je_OBC ; do I=Is_OBC,Ie_OBC
4450 if (OBC%segnum_u(I,j) < 0) D_u(I,j) = G%bathyT(i+1,j) ! OBC_DIRECTION_W
446 enddo ; enddo
447 endif
448
4490 do n=1,OBC%number_of_segments
450 ! Now project bottom depths across cell-corner points in the OBCs. The two
451 ! projections have to occur in sequence and can not be combined easily.
4520 if (.not. OBC%segment(n)%on_pe) cycle
453 ! Use a one-sided projection of bottom depths at OBC points.
4540 I = OBC%segment(n)%HI%IsdB ; J = OBC%segment(n)%HI%JsdB
4550 if (OBC%segment(n)%is_N_or_S .and. (J >= js-1) .and. (J <= je)) then
4560 do I = max(is-1,OBC%segment(n)%HI%IsdB), min(ie,OBC%segment(n)%HI%IedB)
4570 if (OBC%segment(n)%direction == OBC_DIRECTION_N) then
4580 D_u(I,j+1) = D_u(I,j) ; mask_u(I,j+1) = 0.0
4590 elseif (OBC%segment(n)%direction == OBC_DIRECTION_S) then
4600 D_u(I,j) = D_u(I,j+1) ; mask_u(I,j) = 0.0
461 endif
462 enddo
4630 elseif (OBC%segment(n)%is_E_or_W .and. (I >= is-1) .and. (I <= ie)) then
4640 do J = max(js-1,OBC%segment(n)%HI%JsdB), min(je,OBC%segment(n)%HI%JedB)
4650 if (OBC%segment(n)%direction == OBC_DIRECTION_E) then
4660 D_v(i+1,J) = D_v(i,J) ; mask_v(i+1,J) = 0.0
4670 elseif (OBC%segment(n)%direction == OBC_DIRECTION_W) then
4680 D_v(i,J) = D_v(i+1,J) ; mask_v(i,J) = 0.0
469 endif
470 enddo
471 endif
472 enddo
473 !$omp target update to(mask_u, mask_v, D_u, D_v)
474 endif
475
47612 if (.not.use_BBL_EOS) then
4770 do concurrent (k=1:nz, j=G%jsdB:G%Jedb, i=G%isdB:G%iedB)
4780 Rml_vel(i,j,k) = 0.0
479 enddo
480 endif
481
482 ! Resetting Ray_[uv] is required by body force drag.
48312 if (allocated(visc%Ray_u)) then
4841560 do concurrent (k=1:nz, j=G%jsd:G%jed, i=G%isdB:G%iedB)
4858001624 visc%Ray_u(i,j,k) = 0.0
486 enddo
487 endif
48812 if (allocated(visc%Ray_v)) then
4891548 do concurrent (k=1:nz, j=G%jsdB:G%jedB, i=G%isd:G%ied)
4908056332 visc%Ray_v(i,j,k) = 0.0
491 enddo
492 endif
493
494 !$omp target enter data map(alloc: S_vel, T_vel, SpV_vel, h_vel, h_at_vel, dz_vel, &
495 !$omp dz_at_vel)
496
49736 do m=1,2
49824 if (m==1) then
499 ! m=1 refers to u-points
50012 is = Isq ; ie = Ieq
50112 jstart = G%Jsc
502 else
503 ! m=2 refers to v-points
50412 is = G%isc ; ie = G%iec
50512 jstart = Jsq
506 endif
507
50824 do concurrent (j=jstart:Jeq)
5091452 if (m==1) then
510720 do concurrent (i=is:ie)
51187840 do_i(i,j) = (G%mask2dCu(I,j) > 0.0)
512 enddo
513 else
514732 do concurrent (i=is:ie)
51588572 do_i(i,j) = (G%mask2dCv(i,J) > 0.0)
516 enddo
517 endif
518
519 ! Calculate thickness at velocity points (u or v depending on value of m).
520 ! Also interpolate the ML density or T/S properties.
5211452 if (m==1) then ! u-points
522720 do concurrent (k=1:nz, I=is:ie)
5236534000 if (do_i(I,j)) then
5244453200 if (u(I,j,k) * (h(i+1,j,k) - h(i,j,k)) >= 0) then
525 ! If the flow is from thin to thick then bias towards the thinner thickness
526 h_at_vel(I,j,k) = 2.0*h(i,j,k)*h(i+1,j,k) / &
5272551229 (h(i,j,k) + h(i+1,j,k) + h_neglect)
528 dz_at_vel(I,j,k) = 2.0*dz(i,j,k)*dz(i+1,j,k) / &
5292551229 (dz(i,j,k) + dz(i+1,j,k) + dz_neglect)
530 else
531 ! If the flow is from thick to thin then use the simple average thickness
5321901971 h_at_vel(I,j,k) = 0.5 * (h(i,j,k) + h(i+1,j,k))
5331901971 dz_at_vel(I,j,k) = 0.5 * (dz(i,j,k) + dz(i+1,j,k))
534 endif
535 endif
5366534000 h_vel(I,j,k) = 0.5 * (h(i,j,k) + h(i+1,j,k))
5376621840 dz_vel(I,j,k) = 0.5 * (dz(i,j,k) + dz(i+1,j,k))
538 enddo
539720 if (use_BBL_EOS) then ; do concurrent (k=1:nz, I=is:ie)
540 ! Perhaps these should be thickness weighted.
5416534000 T_vel(I,j,k) = 0.5 * (tv%T(i,j,k) + tv%T(i+1,j,k))
5426621840 S_vel(I,j,k) = 0.5 * (tv%S(i,j,k) + tv%S(i+1,j,k))
5430 enddo ; else ; do concurrent (k=1:nkmb, I=is:ie)
5440 Rml_vel(I,j,k) = 0.5 * (Rml(i,j,k) + Rml(i+1,j,k))
545 enddo ; endif
546720 if (allocated(tv%SpV_avg)) then ; do concurrent (k=1:nz, I=is:ie)
5470 SpV_vel(I,j,k) = 0.5 * (tv%SpV_avg(i,j,k) + tv%SpV_avg(i+1,j,k))
548 enddo ; endif
549 else ! v-points
550732 do concurrent (k=1:nz, i=is:ie)
5516588000 if (do_i(i,j)) then
5524404600 if (v(i,J,k) * (h(i,j+1,k) - h(i,j,k)) >= 0) then
553 ! If the flow is from thin to thick then bias towards the thinner thickness
554 h_at_vel(i,j,k) = 2.0*h(i,j,k)*h(i,j+1,k) / &
5552337718 (h(i,j,k) + h(i,j+1,k) + h_neglect)
556 dz_at_vel(i,j,k) = 2.0*dz(i,j,k)*dz(i,j+1,k) / &
5572337718 (dz(i,j,k) + dz(i,j+1,k) + dz_neglect)
558 else
559 ! If the flow is from thick to thin then use the simple average thickness
5602066882 h_at_vel(i,j,k) = 0.5 * (h(i,j,k) + h(i,j+1,k))
5612066882 dz_at_vel(i,j,k) = 0.5 * (dz(i,j,k) + dz(i,j+1,k))
562 endif
563 endif
5646588000 h_vel(i,j,k) = 0.5 * (h(i,j,k) + h(i,j+1,k))
5656676572 dz_vel(i,j,k) = 0.5 * (dz(i,j,k) + dz(i,j+1,k))
566 enddo
567732 if (use_BBL_EOS) then ; do concurrent (k=1:nz, i=is:ie)
568 ! Perhaps these should be thickness weighted.
5696588000 T_vel(i,j,k) = 0.5 * (tv%T(i,j,k) + tv%T(i,j+1,k))
5706676572 S_vel(i,j,k) = 0.5 * (tv%S(i,j,k) + tv%S(i,j+1,k))
5710 enddo ; else ; do concurrent (k=1:nkmb, i=is:ie)
5720 Rml_vel(i,j,k) = 0.5 * (Rml(i,j,k) + Rml(i,j+1,k))
573 enddo ; endif
574732 if (allocated(tv%SpV_avg)) then ; do concurrent (k=1:nz, i=is:ie)
5750 SpV_vel(i,j,k) = 0.5 * (tv%SpV_avg(i,j,k) + tv%SpV_avg(i,j+1,k))
576 enddo ; endif
577 endif
578
5791452 if (associated(OBC)) then ; if (OBC%number_of_segments > 0) then
580 ! Apply a zero gradient projection of thickness across OBC points.
5810 if (m==1) then
582 do concurrent (I=is:ie, do_i(I,j) .and. (OBC%segnum_u(I,j) /= 0)) &
5830 DO_LOCALITY(local(k))
5840 if (OBC%segnum_u(I,j) > 0) then ! OBC_DIRECTION_E
5850 do k=1,nz
5860 h_at_vel(I,j,k) = h(i,j,k) ; h_vel(I,j,k) = h(i,j,k)
5870 dz_at_vel(I,j,k) = dz(i,j,k) ; dz_vel(I,j,k) = dz(i,j,k)
588 enddo
5890 if (use_BBL_EOS) then
5900 do k=1,nz
5910 T_vel(I,j,k) = tv%T(i,j,k) ; S_vel(I,j,k) = tv%S(i,j,k)
592 enddo
593 else
5940 do k=1,nkmb
5950 Rml_vel(I,j,k) = Rml(i,j,k)
596 enddo
597 endif
5980 if (allocated(tv%SpV_avg)) then ; do k=1,nz
5990 SpV_vel(I,j,k) = tv%SpV_avg(i,j,k)
600 enddo ; endif
6010 elseif (OBC%segnum_u(I,j) < 0) then ! OBC_DIRECTION_W
6020 do k=1,nz
6030 h_at_vel(I,j,k) = h(i+1,j,k) ; h_vel(I,j,k) = h(i+1,j,k)
6040 dz_at_vel(I,j,k) = dz(i+1,j,k) ; dz_vel(I,j,k) = dz(i+1,j,k)
605 enddo
6060 if (use_BBL_EOS) then
6070 do k=1,nz
6080 T_vel(I,j,k) = tv%T(i+1,j,k) ; S_vel(I,j,k) = tv%S(i+1,j,k)
609 enddo
610 else
6110 do k=1,nkmb
6120 Rml_vel(I,j,k) = Rml(i+1,j,k)
613 enddo
614 endif
6150 if (allocated(tv%SpV_avg)) then ; do k=1,nz
6160 SpV_vel(I,j,k) = tv%SpV_avg(i+1,j,k)
617 enddo ; endif
618 endif
619 enddo
620 else
621 do concurrent (i=is:ie, do_i(i,j) .and. (OBC%segnum_v(i,J) /= 0)) &
6220 DO_LOCALITY(local(k))
6230 if (OBC%segnum_v(i,J) > 0) then ! OBC_DIRECTION_N
6240 do k=1,nz
6250 h_at_vel(i,j,k) = h(i,j,k) ; h_vel(i,j,k) = h(i,j,k)
6260 dz_at_vel(i,j,k) = dz(i,j,k) ; dz_vel(i,j,k) = dz(i,j,k)
627 enddo
6280 if (use_BBL_EOS) then
6290 do k=1,nz
6300 T_vel(i,j,k) = tv%T(i,j,k) ; S_vel(i,j,k) = tv%S(i,j,k)
631 enddo
632 else
6330 do k=1,nkmb
6340 Rml_vel(i,j,k) = Rml(i,j,k)
635 enddo
636 endif
6370 if (allocated(tv%SpV_avg)) then ; do k=1,nz
6380 SpV_vel(i,j,k) = tv%SpV_avg(i,j,k)
639 enddo ; endif
6400 elseif (OBC%segnum_v(i,J) < 0) then ! OBC_DIRECTION_S
6410 do k=1,nz
6420 h_at_vel(i,j,k) = h(i,j+1,k) ; h_vel(i,j,k) = h(i,j+1,k)
6430 dz_at_vel(i,j,k) = dz(i,j+1,k) ; dz_vel(i,j,k) = dz(i,j+1,k)
644 enddo
6450 if (use_BBL_EOS) then
6460 do k=1,nz
6470 T_vel(i,j,k) = tv%T(i,j+1,k) ; S_vel(i,j,k) = tv%S(i,j+1,k)
648 enddo
649 else
6500 do k=1,nkmb
6510 Rml_vel(i,j,k) = Rml(i,j+1,k)
652 enddo
653 endif
6540 if (allocated(tv%SpV_avg)) then ; do k=1,nz
6550 SpV_vel(i,j,k) = tv%SpV_avg(i,j+1,k)
656 enddo ; endif
657 endif
658 enddo
659 endif
660 endif ; endif
661
662 ! Set the "back ground" friction velocity scale to either the tidal amplitude or place-holder constant
6631452 if (CS%BBL_use_tidal_bg) then
6640 do concurrent (i=is:ie, do_i(i,j)) ; if (m==1) then
665 u2_bg(I,j) = tideampfac2_x_0p5 * ( G%mask2dT(i,j)*(CS%tideamp(i,j)*CS%tideamp(i,j))+ &
6660 G%mask2dT(i+1,j)*(CS%tideamp(i+1,j)*CS%tideamp(i+1,j)) )
667 else
668 u2_bg(i,j) = tideampfac2_x_0p5 * ( G%mask2dT(i,j)*(CS%tideamp(i,j)*CS%tideamp(i,j))+ &
6690 G%mask2dT(i,j+1)*(CS%tideamp(i,j+1)*CS%tideamp(i,j+1)) )
670 endif ; enddo
671 else
672176412 do concurrent (i=is:ie, do_i(i,j))
673176412 u2_bg(i,j) = CS%drag_bg_vel * CS%drag_bg_vel
674 enddo
675 endif
676
6771452 if (use_BBL_EOS .or. CS%body_force_drag .or. .not.CS%linear_drag) then
678 ! Calculate the mean velocity magnitude over the bottommost CS%Hbbl of
679 ! the water column for determining the quadratic bottom drag.
680 ! Used in ustar(i,j)
681 do concurrent (i=is:ie, do_i(i,j)) DO_LOCALITY(local(k, cdrag_sqrt)) &
682176412 DO_LOCALITY(local_init(cdrag_sqrt_H, cdrag_sqrt_H_RL))
683118104 htot_vel = 0.0 ; hwtot = 0.0 ; hutot = 0.0
684118104 dztot_vel = 0.0 ; dzwtot = 0.0
685118104 Thtot = 0.0 ; Shtot = 0.0 ; SpV_htot = 0.0
686
687118104 if (CS%bottomdragmap) then
6880 if (m==1) then
6890 cdrag_sqrt = sqrt(CS%cdrag_u(i,j))
690 else
6910 cdrag_sqrt = sqrt(CS%cdrag_v(i,j))
692 endif
6930 cdrag_sqrt_H = cdrag_sqrt * US%L_to_m * GV%m_to_H
6940 cdrag_sqrt_H_RL = cdrag_sqrt * US%L_to_Z * GV%RZ_to_H
695 endif
696
6972962762 do k=nz,1,-1
6982962744 if (htot_vel>=CS%Hbbl) exit ! terminate the k loop
699
7002844658 hweight = MIN(CS%Hbbl - htot_vel, h_at_vel(i,j,k))
7012844658 if (hweight < 1.5*GV%Angstrom_H + h_neglect) cycle
7022844658 dzweight = MIN(CS%dz_bbl - dztot_vel, dz_at_vel(i,j,k))
703
7042844658 htot_vel = htot_vel + h_at_vel(i,j,k)
7052844658 hwtot = hwtot + hweight
7062844658 dztot_vel = dztot_vel + dz_at_vel(i,j,k)
7072844658 dzwtot = dzwtot + dzweight
708
7092844658 if ((.not.CS%linear_drag) .and. (hweight >= 0.0)) then ; if (m==1) then
7101439521 v_at_u = set_v_at_u(v, h, G, GV, i, j, k, mask_v, OBC)
7111439521 hutot = hutot + hweight * sqrt(u(I,j,k)*u(I,j,k) + v_at_u*v_at_u + u2_bg(I,j))
712 else
7131405137 u_at_v = set_u_at_v(u, h, G, GV, i, j, k, mask_u, OBC)
7141405137 hutot = hutot + hweight * sqrt(v(i,J,k)*v(i,J,k) + u_at_v*u_at_v + u2_bg(i,j))
715 endif ; endif
716
7172844658 if (use_BBL_EOS .and. (hweight >= 0.0)) then
7182844658 Thtot = Thtot + hweight * T_vel(i,j,k)
7192844658 Shtot = Shtot + hweight * S_vel(i,j,k)
720 endif
7212844676 if (allocated(tv%SpV_avg) .and. (hweight >= 0.0)) then
7220 SpV_htot = SpV_htot + hweight * SpV_vel(i,j,k)
723 endif
724 enddo ! end of k loop
725
726 ! Find the Adcroft reciprocal of the total thickness weights
727118104 I_hwtot = 0.0 ; if (hwtot > 0.0) I_hwtot = 1.0 / hwtot
728
729 ! Set u* based on u*^2 = Cdrag u_bbl^2
730118104 if ((hwtot <= 0.0) .or. (CS%linear_drag .and. .not.allocated(tv%SpV_avg))) then
7310 ustar(i,j) = cdrag_sqrt_H * CS%drag_bg_vel
732118104 elseif (CS%linear_drag .and. allocated(tv%SpV_avg)) then
7330 ustar(i,j) = cdrag_sqrt_H_RL * CS%drag_bg_vel * (hwtot / SpV_htot)
734118104 elseif (allocated(tv%SpV_avg)) then ! (.not.CS%linear_drag)
7350 ustar(i,j) = cdrag_sqrt_H_RL * hutot / SpV_htot
736 else ! (.not.CS%linear_drag .and. .not.allocated(tv%SpV_avg))
737118104 ustar(i,j) = cdrag_sqrt_H * hutot / hwtot
738 endif
739
740118104 umag_avg(i,j) = hutot * I_hwtot
741118104 h_bbl_drag(i,j) = hwtot
742118104 dz_bbl_drag(i,j) = dzwtot
743
744118104 if (use_BBL_EOS) then ; if (hwtot > 0.0) then
745118104 T_EOS(i,j) = Thtot/hwtot ; S_EOS(i,j) = Shtot/hwtot
746 else
7470 T_EOS(i,j) = 0.0 ; S_EOS(i,j) = 0.0
748 endif ; endif
749
750 ! Diagnostic BBL flow speed at u- and v-points.
751294516 if (CS%id_bbl_u>0 .and. m==1) then
7520 if (hwtot > 0.0) CS%bbl_u(I,j) = hutot/hwtot
753118104 elseif (CS%id_bbl_v>0 .and. m==2) then
7540 if (hwtot > 0.0) CS%bbl_v(i,J) = hutot/hwtot
755 endif
756 enddo
757 else
758 do concurrent (i=is:ie) DO_LOCALITY(local(cdrag_sqrt)) &
7590 DO_LOCALITY(local_init(cdrag_sqrt_H))
7600 if (CS%bottomdragmap) then
7610 if (m==1) then
7620 cdrag_sqrt = sqrt(CS%cdrag_u(i,j))
763 else
7640 cdrag_sqrt = sqrt(CS%cdrag_v(i,j))
765 endif
7660 cdrag_sqrt_H = cdrag_sqrt * US%L_to_m * GV%m_to_H
767 endif
7680 ustar(i,j) = cdrag_sqrt_H * CS%drag_bg_vel
769 enddo
770 endif ! Not linear_drag
771
7722928 if (use_BBL_EOS) then
7731452 if (associated(tv%p_surf)) then
77488572 if (m==1) then ; do concurrent (i=is:ie) ; press(I,j) = 0.5*(tv%p_surf(i,j) + tv%p_surf(i+1,j)) ; enddo
77588572 else ; do concurrent (i=is:ie) ; press(i,j) = 0.5*(tv%p_surf(i,j) + tv%p_surf(i,j+1)) ; enddo ; endif
776 else
7770 do concurrent (i=is:ie) ; press(i,j) = 0.0 ; enddo
778 endif
779
780351372 do concurrent (i=is:ie, .not.do_i(i,j)) ; T_EOS(i,j) = 0.0 ; S_EOS(i,j) = 0.0 ; enddo
781
7821452 do concurrent (i=is:ie)
78313298412 do k=1,nz
78413296960 press(i,j) = press(i,j) + (GV%H_to_RZ*GV%g_Earth) * h_vel(i,j,k)
785 enddo
786 enddo
787 endif
788 enddo ! end of j loop
789
79024 if (use_BBL_EOS) then
79124 EOSdom(1,1) = is-G%IsdB+1 ; EOSdom(1,2) = ie-G%IsdB+1
79224 EOSdom(2,1) = jstart-G%JsdB+1 ; EOSdom(2,2) = Jeq-G%JsdB+1
79324 call calculate_density_derivs(T_EOS, S_EOS, press, dR_dT, dR_dS, tv%eqn_of_state, EOSdom)
794 endif
795
796 ! Find a BBL thickness given by equation 2.20 of Killworth and Edwards, 1999:
797 ! ( f h / Cn u* )^2 + ( N h / Ci u* ) = 1
798 ! where Cn=0.5 and Ci=20 (constants suggested by Zilitinkevich and Mironov, 1996).
799 ! Eq. 2.20 can be expressed in terms of boundary layer thicknesses limited by
800 ! rotation (h_f) and stratification (h_N):
801 ! ( h / h_f )^2 + ( h / h_N ) = 1
802 ! When stratification dominates h_N<<h_f, and vice versa.
803 !$omp target teams loop collapse(2) thread_limit(128) private( &
804 !$omp k, ustarsq, htot, dztot, Thtot, Shtot, oldfn, Dfn, Dh, Ddz, &
805 !$omp frac_used, Rhtot, C2f, ustH, bbl_thick, Vol_bbl_chan, vol_below, &
806 !$omp D_vel, D_vel_p, D_vel_m, Dp, tmp, Dm, crv, slope, &
807 !$omp max_dL_trig_itt, max_norm_err_trig, max_norm_err_iter, &
808 !$omp norm_err_trig, norm_err_iter, dL_trig_itt, vol_err_trig, L_trig, &
809 !$omp L, vol_err_iter, BBL_visc_frac, BBL_frac, cdrag_conv, h_vel_pos, &
810 !$omp Cell_width, gam, Rayleigh, v_at_u, u_at_v, kv_bbl, h_sum, &
811 !$omp I_hwtot, h_bbl_fr, root, cdrag &
812 !$omp ) firstprivate(cdrag_L_to_H, cdrag_RL_to_H)
813176448 do j=jstart,jeq ; do i=is,ie ; if (do_i(i,j)) then
814 ! The 400.0 in this expression is the square of a Ci introduced in KW99, eq. 2.22.
815118104 ustarsq = Rho0x400_G * ustar(i,j)**2 ! Note not in units of u*^2 but [H R ~> kg m-2 or kg2 m-5]
816118104 htot = 0.0
817118104 dztot = 0.0
818
819118104 if (CS%bottomdragmap) then
8200 if (m==1) then
8210 cdrag = CS%cdrag_u(i,j)
822 else
8230 cdrag = CS%cdrag_v(i,j)
824 endif
8250 cdrag_L_to_H = cdrag * US%L_to_m * GV%m_to_H
8260 cdrag_RL_to_H = cdrag * US%L_to_Z * GV%RZ_to_H
827 endif
828
829 ! Calculate the thickness of a stratification limited BBL ignoring rotation:
830 ! h_N = Ci u* / N (limit of KW99 eq. 2.20 for |f|->0)
831 ! For layer mode, N^2 = g'/h. Since (Ci u*)^2 = (h_N N)^2 = h_N g' then
832 ! h_N = (Ci u*)^2 / g' (KW99, eq, 2.22)
833 ! Starting from the bottom, integrate the stratification upward until h_N N balances Ci u*
834 ! or in layer mode
835 ! h_N Delta rho ~ (Ci u*)^2 rho0 / g
836 ! where the rhs is stored in variable ustarsq.
837 ! The method was described in Stephens and Hallberg 2000 (unpublished and lost manuscript).
838118104 if (use_BBL_EOS) then
839118104 Thtot = 0.0 ; Shtot = 0.0 ; oldfn = 0.0
8403048932 do k=nz,2,-1
8413045350 if (h_at_vel(i,j,k) <= 0.0) cycle
842
843 ! Delta rho * h_bbl assuming everything below is homogenized
844 oldfn = dR_dT(i,j)*(Thtot - T_vel(i,j,k)*htot) + &
8453045350 dR_dS(i,j)*(Shtot - S_vel(i,j,k)*htot)
8463045350 if (oldfn >= ustarsq) exit
847
848 ! Local Delta rho * h_bbl at interface
849 Dfn = (dR_dT(i,j)*(T_vel(i,j,k) - T_vel(i,j,k-1)) + &
850 dR_dS(i,j)*(S_vel(i,j,k) - S_vel(i,j,k-1))) * &
8512930828 (h_at_vel(i,j,k) + htot)
852
8532930828 if ((oldfn + Dfn) <= ustarsq) then
854 ! Use whole layer
8552816266 Dh = h_at_vel(i,j,k)
8562816266 Ddz = dz_at_vel(i,j,k)
857 else
858 ! Use only part of the layer
859114562 frac_used = sqrt((ustarsq-oldfn) / (Dfn))
860114562 Dh = h_at_vel(i,j,k) * frac_used
861114562 Ddz = dz_at_vel(i,j,k) * frac_used
862 endif
863
864 ! Increment total BBL thickness and cumulative T and S
8652930828 htot = htot + Dh
8662930828 dztot = dztot + Ddz
8672934410 Thtot = Thtot + T_vel(i,j,k)*Dh ; Shtot = Shtot + S_vel(i,j,k)*Dh
868 enddo
869118104 if ((oldfn < ustarsq) .and. h_at_vel(i,j,1) > 0.0) then
870 ! Layer 1 might be part of the BBL.
8713582 if (dR_dT(i,j) * (Thtot - T_vel(i,j,1)*htot) + &
872 dR_dS(i,j) * (Shtot - S_vel(i,j,1)*htot) < ustarsq) then
8733542 htot = htot + h_at_vel(i,j,1)
8743542 dztot = dztot + dz_at_vel(i,j,1)
875 endif
876 endif ! Examination of layer 1.
877 else ! Use Rlay and/or the coordinate density as density variables.
8780 Rhtot = 0.0
8790 do k=nz,K2,-1
8800 oldfn = Rhtot - GV%Rlay(k)*htot
8810 Dfn = (GV%Rlay(k) - GV%Rlay(k-1))*(h_at_vel(i,j,k)+htot)
882
8830 if (oldfn >= ustarsq) then
8840 cycle
8850 elseif ((oldfn + Dfn) <= ustarsq) then
8860 Dh = h_at_vel(i,j,k)
8870 Ddz = dz_at_vel(i,j,k)
888 else
8890 frac_used = sqrt((ustarsq-oldfn) / (Dfn))
8900 Dh = h_at_vel(i,j,k) * frac_used
8910 Ddz = dz_at_vel(i,j,k) * frac_used
892 endif
893
8940 htot = htot + Dh
8950 dztot = dztot + Ddz
8960 Rhtot = Rhtot + GV%Rlay(k)*Dh
897 enddo
8980 if (nkml>0) then
8990 do k=nkmb,2,-1
9000 oldfn = Rhtot - Rml_vel(i,j,k)*htot
9010 Dfn = (Rml_vel(i,j,k) - Rml_vel(i,j,k-1)) * (h_at_vel(i,j,k)+htot)
902
9030 if (oldfn >= ustarsq) then
9040 cycle
9050 elseif ((oldfn + Dfn) <= ustarsq) then
9060 Dh = h_at_vel(i,j,k)
9070 Ddz = dz_at_vel(i,j,k)
908 else
9090 frac_used = sqrt((ustarsq-oldfn) / (Dfn))
9100 Dh = h_at_vel(i,j,k) * frac_used
9110 Ddz = dz_at_vel(i,j,k) * frac_used
912 endif
913
9140 htot = htot + Dh
9150 dztot = dztot + Ddz
9160 Rhtot = Rhtot + Rml_vel(i,j,k)*Dh
917 enddo
9180 if (Rhtot - Rml_vel(i,j,1)*htot < ustarsq) then
9190 htot = htot + h_at_vel(i,j,1)
9200 dztot = dztot + dz_at_vel(i,j,1)
921 endif
922 else
9230 if (Rhtot - GV%Rlay(1)*htot < ustarsq) then
9240 htot = htot + h_at_vel(i,j,1)
9250 dztot = dztot + dz_at_vel(i,j,1)
926 endif
927 endif
928 endif ! use_BBL_EOS
929
930 ! Value of 2*f at u- or v-points.
931118104 if (m==1) then ; C2f = G%CoriolisBu(I,J-1) + G%CoriolisBu(I,J)
93258728 else ; C2f = G%CoriolisBu(I-1,J) + G%CoriolisBu(I,J) ; endif
933
934 ! The thickness of a rotation limited BBL ignoring stratification is
935 ! h_f ~ Cn u* / f (limit of KW99 eq. 2.20 for N->0).
936 ! The buoyancy limit of BBL thickness (h_N) is already in the variable htot from above.
937 ! Substituting x = h_N/h into KW99 eq. 2.20 yields the quadratic
938 ! x^2 - x = (h_N / h_f)^2
939 ! for which the positive root is
940 ! xp = 1/2 + sqrt( 1/4 + (h_N/h_f)^2 )
941 ! and thus h_bbl = h_N / xp . Since h_f = Cn u*/f and Cn=0.5
942 ! xp = 1/2 + sqrt( 1/4 + (2 f h_N/u*)^2 )
943 ! To avoid dividing by zero if u*=0 then
944 ! xp u* = 1/2 u* + sqrt( 1/4 u*^2 + (2 f h_N)^2 )
945118104 if (CS%cdrag * u2_bg(i,j) <= 0.0) then
946 ! This avoids NaNs and overflows, and could be used in all cases,
947 ! but is not bitwise identical to the current code.
9480 ustH = ustar(i,j) ; root = sqrt(0.25*ustH**2 + (htot*C2f)**2)
9490 if (dztot*ustH <= (CS%BBL_thick_min+dz_neglect) * (0.5*ustH + root)) then
9500 bbl_thick = CS%BBL_thick_min
951 else
952 ! The following expression reads
953 ! h_bbl = h_N u* / ( 1/2 u* + sqrt( 1/4 u*^2 + ( 2 f h_N )^2 ) )
954 ! which is h_bbl = h_N u*/(xp u*) as described above.
9550 bbl_thick = (dztot * ustH) / (0.5*ustH + root)
956 endif
957 else
958 ! The following expression reads
959 ! h_bbl = h_N / ( 1/2 + sqrt( 1/4 + ( 2 f h_N / u* )^2 ) )
960 ! which is h_bbl = h_N/xp as described above.
961118104 bbl_thick = dztot / (0.5 + sqrt(0.25 + htot*htot*C2f*C2f / (ustar(i,j)*ustar(i,j)) ) )
962
963118104 if (bbl_thick < CS%BBL_thick_min) bbl_thick = CS%BBL_thick_min
964 endif
965
966 ! Store the normalized bottom boundary layer volume.
967118104 if (CS%Channel_drag) Vol_bbl_chan = bbl_thick
968
969 ! If there is Richardson number dependent mixing, that determines
970 ! the vertical extent of the bottom boundary layer, and there is no
971 ! need to set that scale here. In fact, viscously reducing the
972 ! shears over an excessively large region reduces the efficacy of
973 ! the Richardson number dependent mixing.
974 ! In other words, if using RiNo_mix then CS%dz_bbl acts as an upper bound on
975 ! bbl_thick.
976118104 if ((bbl_thick > 0.5*CS%dz_bbl) .and. (CS%RiNo_mix)) bbl_thick = 0.5*CS%dz_bbl
977
978 ! If drag is a body force, bbl_thick is HBBL
979118104 if (CS%body_force_drag) bbl_thick = dz_bbl_drag(i,j)
980
981118104 if (CS%Channel_drag) then
982
983118104 vol_below(nz+1) = 0.0
9848975904 do K=nz,1,-1
9858975904 vol_below(K) = vol_below(K+1) + dz_vel(i,j,k)
986 enddo
987
988 ! Find the bathymetry at adjacent points relative to the shelf break. For now this
989 ! shelf break depth is set with a global constant, but it could vary in space.
990118104 if (m==1) then
99159376 D_vel = D_u(I,j) - CS%channel_break_depth
99259376 D_vel_p = G%mask2dCu(I,j+1) * (D_u(I,j+1) - CS%channel_break_depth)
99359376 D_vel_m = G%mask2dCu(I,j-1) * (D_u(I,j-1) - CS%channel_break_depth)
994 else
99558728 D_vel = D_v(i,J) - CS%channel_break_depth
99658728 D_vel_p = G%mask2dCv(i+1,J) * (D_v(i+1,J) - CS%channel_break_depth)
99758728 D_vel_m = G%mask2dCv(i-1,J) * (D_v(i-1,J) - CS%channel_break_depth)
998 endif
999 ! This profile uses a harmonic mean bottom depth below some reference value to
1000 ! roughly mimic the topographic shape at and beneath a continental shelf break.
1001 ! Above this a simple arithmetic mean is used.
1002118104 if ((D_vel > 0.0) .and. (D_vel_p > 0.0)) then
1003115644 Dp = 2.0 * D_vel * D_vel_p / (D_vel + D_vel_p)
1004 else ! This is above the shelf-break, noting that D is positive downward.
10052460 Dp = 0.5 * (min(D_vel, 0.0) + min(D_vel_p, 0.0))
1006 endif
1007118104 if ((D_vel > 0.0) .and. (D_vel_m > 0.0)) then
1008115644 Dm = 2.0 * D_vel * D_vel_m / (D_vel + D_vel_m)
1009 else ! This is above the shelf-break, noting that D is positive downward.
10102460 Dm = 0.5 * (min(D_vel, 0.0) + min(D_vel_m, 0.0))
1011 endif
1012118104 if (Dm > Dp) then ; tmp = Dp ; Dp = Dm ; Dm = tmp ; endif
1013118104 crv = 3.0*(Dp + Dm - 2.0*D_vel)
1014118104 slope = Dp - Dm
1015
1016 ! If the curvature is small enough, there is no reason not to assume
1017 ! a uniformly sloping or flat bottom.
1018118104 if (abs(crv) < 1e-2*(slope + CS%BBL_thick_min)) crv = 0.0
1019
1020 ! Determine the normalized open length (L) at each interface.
1021118104 if (crv == 0.0) then
1022768 call find_L_open_uniform_slope(vol_below, Dp, Dm, L, GV)
1023117336 elseif (crv > 0.0) then
102411664 if (CS%concave_trigonometric_L) then
10250 call find_L_open_concave_trigonometric(vol_below, D_vel, Dp, Dm, L, GV)
1026 else
102711664 call find_L_open_concave_iterative(vol_below, D_vel, Dp, Dm, L, GV)
102811664 if (CS%debug) then
1029 ! The tests in this block reveal that the iterative and trigonometric solutions are
1030 ! mathematically equivalent, but in some cases the iterative solution is consistent
1031 ! at roundoff, but that the trigonmetric solutions have errors that can be several
1032 ! orders of magnitude larger in some cases.
10330 call find_L_open_concave_trigonometric(vol_below, D_vel, Dp, Dm, L_trig, GV)
10340 call test_L_open_concave(vol_below, D_vel, Dp, Dm, L_trig, vol_err_trig, GV)
10350 call test_L_open_concave(vol_below, D_vel, Dp, Dm, L, vol_err_iter, GV)
10360 max_dL_trig_itt = 0.0 ; max_norm_err_trig = 0.0 ; max_norm_err_iter = 0.0
10370 norm_err_trig(:) = 0.0 ; norm_err_iter(:) = 0.0
10380 do K=1,nz+1
10390 dL_trig_itt(K) = L_trig(K) - L(K)
10400 if (abs(dL_trig_itt(K)) > abs(max_dL_trig_itt)) max_dL_trig_itt = dL_trig_itt(K)
10410 norm_err_trig(K) = vol_err_trig(K) / (vol_below(K) + dz_neglect)
10420 norm_err_iter(K) = vol_err_iter(K) / (vol_below(K) + dz_neglect)
10430 if (abs(norm_err_trig(K)) > abs(max_norm_err_trig)) max_norm_err_trig = norm_err_trig(K)
10440 if (abs(norm_err_iter(K)) > abs(max_norm_err_iter)) max_norm_err_iter = norm_err_iter(K)
1045 enddo
10460 if (abs(max_dL_trig_itt) > 1.0e-13) &
10470 K = nz+1 ! This is here only to use as a break point for a debugger.
10480 if (abs(max_norm_err_trig) > 1.0e-13) &
10490 K = nz+1 ! This is here only to use as a break point for a debugger.
10500 if (abs(max_norm_err_iter) > 1.0e-13) &
10510 K = nz+1 ! This is here only to use as a break point for a debugger.
1052 endif
1053 endif
1054 else ! crv < 0.0
1055105672 call find_L_open_convex(vol_below, D_vel, Dp, Dm, L, GV, US, CS)
1056 endif ! end of crv<0 cases.
1057
1058 ! Determine the Rayleigh drag contributions.
1059
1060 ! The drag within the bottommost Vol_bbl_chan is applied as a part of an enhanced bottom
1061 ! viscosity, while above this the drag is applied directly to the layers in question as a
1062 ! Rayleigh drag term.
1063
1064 ! Restrict the volume over which the channel drag is applied from the previously determined value.
1065118104 if (CS%Chan_drag_max_vol >= 0.0) Vol_bbl_chan = min(Vol_bbl_chan, CS%Chan_drag_max_vol)
1066
1067118104 BBL_visc_frac = 0.0
10688975904 do K=nz,1,-1
1069 !modify L(K) for porous barrier parameterization
10708857800 if (m==1) then ; L(K) = L(K)*pbv%por_layer_widthU(I,j,K)
10714404600 else ; L(K) = L(K)*pbv%por_layer_widthV(i,J,K) ; endif
1072
1073 ! Determine the drag contributing to the bottom boundary layer
1074 ! and the Rayleigh drag that acts on each layer.
10758857800 if (L(K) > L(K+1)) then
10762944908 if (vol_below(K+1) < Vol_bbl_chan) then
10772662188 BBL_frac = (1.0-vol_below(K+1)/Vol_bbl_chan)**2
10782662188 BBL_visc_frac = BBL_visc_frac + BBL_frac*(L(K) - L(K+1))
1079 else
1080282720 BBL_frac = 0.0
1081 endif
1082
10832944908 if (allocated(tv%SpV_avg)) then
10840 cdrag_conv = cdrag_RL_to_H / SpV_vel(i,j,k)
1085 else
10862944908 cdrag_conv = cdrag_L_to_H
1087 endif
1088
10892944908 h_vel_pos = h_vel(i,j,k) + h_neglect
10902944908 if (m==1) then ; Cell_width = G%dy_Cu(I,j)*pbv%por_face_areaU(I,j,k)
10911456992 else ; Cell_width = G%dx_Cv(i,J)*pbv%por_face_areaV(i,J,k) ; endif
10922944908 gam = 1.0 - L(K+1)/L(K)
1093 Rayleigh = cdrag_conv * (L(K)-L(K+1)) * (1.0-BBL_frac) * &
1094 (12.0*CS%c_Smag*h_vel_pos) / (12.0*CS%c_Smag*h_vel_pos + &
10952944908 cdrag_conv * gam*(1.0-gam)*(1.0-1.5*gam) * L(K)**2 * Cell_width)
1096 else ! This layer feels no drag.
10975912892 Rayleigh = 0.0
1098 endif
1099
11008975904 if (m==1) then
11014453200 if (Rayleigh > 0.0) then
11021428468 v_at_u = set_v_at_u(v, h, G, GV, i, j, k, mask_v, OBC)
11031428468 visc%Ray_u(I,j,k) = Rayleigh * sqrt(u(I,j,k)*u(I,j,k) + v_at_u*v_at_u + u2_bg(I,j))
11043024732 else ; visc%Ray_u(I,j,k) = 0.0 ; endif
1105 else
11064404600 if (Rayleigh > 0.0) then
11071398216 u_at_v = set_u_at_v(u, h, G, GV, i, j, k, mask_u, OBC)
11081398216 visc%Ray_v(i,J,k) = Rayleigh * sqrt(v(i,J,k)*v(i,J,k) + u_at_v*u_at_v + u2_bg(i,j))
11093006384 else ; visc%Ray_v(i,J,k) = 0.0 ; endif
1110 endif
1111
1112 enddo ! k loop to determine visc%Ray_[uv].
1113
1114 ! Set the near-bottom viscosity to a value which will give
1115 ! the correct stress when the shear occurs over bbl_thick.
1116 ! See next block for explanation.
1117118104 if (CS%correct_BBL_bounds .and. &
1118 cdrag_sqrt*ustar(i,j)*bbl_thick*BBL_visc_frac <= CS%Kv_BBL_min) then
1119 ! If the bottom stress implies less viscosity than Kv_BBL_min then
1120 ! set kv_bbl to the bound and recompute bbl_thick to be consistent
1121 ! but with a ridiculously large upper bound on thickness (for Cd u*=0)
11220 kv_bbl = CS%Kv_BBL_min
11230 if ((cdrag_sqrt*ustar(i,j))*BBL_visc_frac*BBL_thick_max > kv_bbl) then
11240 bbl_thick = kv_bbl / ( (cdrag_sqrt*ustar(i,j)) * BBL_visc_frac )
1125 else
11260 bbl_thick = BBL_thick_max
1127 endif
1128 else
1129118104 kv_bbl = (cdrag_sqrt*ustar(i,j)) * bbl_thick*BBL_visc_frac
1130 endif
1131
1132 else ! Not Channel_drag.
1133 ! Set the near-bottom viscosity to a value which will give
1134 ! the correct stress when the shear occurs over bbl_thick.
1135 ! - The bottom stress is tau_b = Cdrag * u_bbl^2
1136 ! - u_bbl was calculated by averaging flow over CS%Hbbl
1137 ! (and includes unresolved tidal components)
1138 ! - u_bbl is embedded in u* since u*^2 = Cdrag u_bbl^2
1139 ! - The average shear in the BBL is du/dz = 2 * u_bbl / h_bbl
1140 ! (which assumes a linear profile, hence the "2")
1141 ! - bbl_thick was bounded to <= 0.5 * CS%dz_bbl
1142 ! - The viscous stress kv_bbl du/dz should balance tau_b
1143 ! Cdrag u_bbl^2 = kv_bbl du/dz
1144 ! = 2 kv_bbl u_bbl
1145 ! so
1146 ! kv_bbl = 0.5 h_bbl Cdrag u_bbl
1147 ! = 0.5 h_bbl sqrt(Cdrag) u*
11480 if (CS%correct_BBL_bounds .and. &
1149 cdrag_sqrt*ustar(i,j)*bbl_thick <= CS%Kv_BBL_min) then
1150 ! If the bottom stress implies less viscosity than Kv_BBL_min then
1151 ! set kv_bbl to the bound and recompute bbl_thick to be consistent
1152 ! but with a ridiculously large upper bound on thickness (for Cd u*=0)
11530 kv_bbl = CS%Kv_BBL_min
11540 if ((cdrag_sqrt*ustar(i,j))*BBL_thick_max > kv_bbl) then
11550 bbl_thick = kv_bbl / ( cdrag_sqrt*ustar(i,j) )
1156 else
11570 bbl_thick = BBL_thick_max
1158 endif
1159 else
11600 kv_bbl = (cdrag_sqrt*ustar(i,j)) * bbl_thick
1161 endif
1162 endif
1163
1164118104 if (CS%body_force_drag) then ; if (h_bbl_drag(i,j) > 0.0) then
1165 ! Increment the Rayleigh drag as a way introduce the bottom drag as a body force.
11660 h_sum = 0.0
11670 I_hwtot = 1.0 / h_bbl_drag(i,j)
11680 do k=nz,1,-1
11690 h_bbl_fr = min(h_bbl_drag(i,j) - h_sum, h_at_vel(i,j,k)) * I_hwtot
11700 if (allocated(tv%SpV_avg)) then
11710 cdrag_conv = cdrag_RL_to_H / SpV_vel(i,j,k)
1172 else
11730 cdrag_conv = cdrag_L_to_H
1174 endif
11750 if (m==1) then
11760 visc%Ray_u(I,j,k) = visc%Ray_u(I,j,k) + (cdrag_conv * umag_avg(I,j)) * h_bbl_fr
1177 else
11780 visc%Ray_v(i,J,k) = visc%Ray_v(i,J,k) + (cdrag_conv * umag_avg(i,j)) * h_bbl_fr
1179 endif
11800 h_sum = h_sum + h_at_vel(i,j,k)
11810 if (h_sum >= h_bbl_drag(i,j)) exit ! The top of this layer is above the drag zone.
1182 enddo
1183 ! Do not enhance the near-bottom viscosity in this case.
11840 Kv_bbl = CS%Kv_BBL_min
1185 endif ; endif
1186
1187118104 kv_bbl = max(CS%Kv_BBL_min, kv_bbl)
1188118104 if (m==1) then
118959376 visc%bbl_thick_u(I,j) = bbl_thick
119059376 if (allocated(visc%Kv_bbl_u)) visc%Kv_bbl_u(I,j) = kv_bbl
1191 else
119258728 visc%bbl_thick_v(i,J) = bbl_thick
119358728 if (allocated(visc%Kv_bbl_v)) visc%Kv_bbl_v(i,J) = kv_bbl
1194 endif
1195 endif ; enddo ; enddo ! end of i & j loops
1196 enddo ! end of m loop
1197
1198 !$omp target exit data map(release: dz, tv, tv%T, tv%S, S_vel, T_vel, SpV_vel, h_vel, h_at_vel, &
1199 !$omp dz_vel, dz_at_vel, Rml, Rml_vel, p_ref, ustar, umag_avg, u2_bg, mask_u, mask_v, &
1200 !$omp h_bbl_drag, dz_bbl_drag, do_i, dR_dS, dR_dT, D_u, D_v, press, S_EOS, T_EOS, tv%p_surf, CS)
1201
1202! Offer diagnostics for averaging
120312 if (CS%id_bbl_thick_u > 0) &
12040 call post_data(CS%id_bbl_thick_u, visc%bbl_thick_u, CS%diag)
120512 if (CS%id_kv_bbl_u > 0) &
12060 call post_data(CS%id_kv_bbl_u, visc%kv_bbl_u, CS%diag)
120712 if (CS%id_bbl_u > 0) &
12080 call post_data(CS%id_bbl_u, CS%bbl_u, CS%diag)
120912 if (CS%id_bbl_thick_v > 0) &
12100 call post_data(CS%id_bbl_thick_v, visc%bbl_thick_v, CS%diag)
121112 if (CS%id_kv_bbl_v > 0) &
12120 call post_data(CS%id_kv_bbl_v, visc%kv_bbl_v, CS%diag)
121312 if (CS%id_bbl_v > 0) &
12140 call post_data(CS%id_bbl_v, CS%bbl_v, CS%diag)
121512 if (CS%id_Ray_u > 0) &
12160 call post_data(CS%id_Ray_u, visc%Ray_u, CS%diag)
121712 if (CS%id_Ray_v > 0) &
12180 call post_data(CS%id_Ray_v, visc%Ray_v, CS%diag)
1219
122012 if (CS%debug) then
12210 if (allocated(visc%Ray_u) .and. allocated(visc%Ray_v)) then
1222 !$omp target update from(visc%Ray_u, visc%Ray_v)
1223 call uvchksum("Ray [uv]", visc%Ray_u, visc%Ray_v, G%HI, haloshift=0, &
12240 unscale=GV%H_to_m*US%s_to_T, scalar_pair=.true.)
1225 endif
12260 if (allocated(visc%kv_bbl_u) .and. allocated(visc%kv_bbl_v)) then
1227 !$omp target update from(visc%Kv_bbl_u, visc%Kv_bbl_v)
1228 call uvchksum("kv_bbl_[uv]", visc%kv_bbl_u, visc%kv_bbl_v, G%HI, &
12290 haloshift=0, unscale=GV%HZ_T_to_m2_s, scalar_pair=.true.)
1230 endif
12310 if (allocated(visc%bbl_thick_u) .and. allocated(visc%bbl_thick_v)) then
1232 !$omp target update from(visc%bbl_thick_u, visc%bbl_thick_v)
1233 call uvchksum("bbl_thick_[uv]", visc%bbl_thick_u, visc%bbl_thick_v, &
12340 G%HI, haloshift=0, unscale=US%Z_to_m, scalar_pair=.true.)
1235 endif
1236 endif
1237end subroutine set_viscous_BBL
1238
1239!> Determine the normalized open length of each interface, given the edge depths and normalized
1240!! volumes below each interface.
1241768pure subroutine find_L_open_uniform_slope(vol_below, Dp, Dm, L, GV)
1242 !$omp declare target
1243 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
1244 real, dimension(SZK_(GV)+1), intent(in) :: vol_below !< The volume below each interface, normalized by
1245 !! the full horizontal area of a velocity cell [Z ~> m]
1246 real, intent(in) :: Dp !< The larger of the two depths at the edge
1247 !! of a velocity cell [Z ~> m]
1248 real, intent(in) :: Dm !< The smaller of the two depths at the edge
1249 !! of a velocity cell [Z ~> m]
1250 real, dimension(SZK_(GV)+1), intent(out) :: L !< The fraction of the full cell width that is open at
1251 !! the depth of each interface [nondim]
1252
1253 ! Local variables
1254 real :: slope ! The absolute value of the bottom depth slope across a cell times the cell width [Z ~> m].
1255 real :: I_slope ! The inverse of the normalized slope [Z-1 ~> m-1]
1256 real :: Vol_open ! The cell volume above which it is open [Z ~> m].
1257 integer :: K, nz
1258
1259768 nz = GV%ke
1260
1261768 slope = abs(Dp - Dm)
1262768 if (slope == 0.0) then
12630 L(1:nz) = 1.0 ; L(nz+1) = 0.0
1264 else
1265768 Vol_open = 0.5*slope
1266768 I_slope = 1.0 / slope
1267
1268768 L(nz+1) = 0.0
126958368 do K=nz,1,-1
127058368 if (vol_below(K) >= Vol_open) then ; L(K) = 1.0
1271 else
1272 ! With a uniformly sloping bottom, the calculation of L(K) is the solution of a simple quadratic equation.
127315312 L(K) = sqrt(2.0*vol_below(K)*I_slope)
1274 endif
1275 enddo
1276 endif
1277
1278768end subroutine find_L_open_uniform_slope
1279
1280!> Determine the normalized open length of each interface for concave bathymetry (from the ocean perspective)
1281!! using trigonometric expressions. In this case there can be two separate open regions.
12820pure subroutine find_L_open_concave_trigonometric(vol_below, D_vel, Dp, Dm, L, GV)
1283 !$omp declare target
1284 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
1285 real, dimension(SZK_(GV)+1), intent(in) :: vol_below !< The volume below each interface, normalized by
1286 !! the full horizontal area of a velocity cell [Z ~> m]
1287 real, intent(in) :: D_vel !< The average bottom depth at a velocity point [Z ~> m]
1288 real, intent(in) :: Dp !< The larger of the two depths at the edge
1289 !! of a velocity cell [Z ~> m]
1290 real, intent(in) :: Dm !< The smaller of the two depths at the edge
1291 !! of a velocity cell [Z ~> m]
1292 real, dimension(SZK_(GV)+1), intent(out) :: L !< The fraction of the full cell width that is open at
1293 !! the depth of each interface [nondim]
1294
1295 ! Local variables
1296 real :: crv ! crv is the curvature of the bottom depth across a
1297 ! cell, times the cell width squared [Z ~> m].
1298 real :: crv_3 ! crv/3 [Z ~> m].
1299 real :: slope ! The absolute value of the bottom depth slope across
1300 ! a cell times the cell width [Z ~> m].
1301 ! The following "volumes" have units of vertical heights because they are normalized
1302 ! by the full horizontal area of a velocity cell.
1303 real :: Vol_open ! The cell volume above which the face is fully is open [Z ~> m].
1304 real :: Vol_2_reg ! The cell volume above which there are two separate
1305 ! open areas that must be integrated [Z ~> m].
1306 real :: C24_crv ! 24/crv [Z-1 ~> m-1].
1307 real :: apb_4a, ax2_3apb ! Various nondimensional ratios of crv and slope [nondim].
1308 real :: a2x48_apb3, Iapb ! Combinations of crv (a) and slope (b) [Z-1 ~> m-1]
1309 real :: L0 ! A linear estimate of L appropriate for tiny volumes [nondim].
1310 real :: slope_crv ! The slope divided by the curvature [nondim]
1311 real :: tmp_val_m1_to_p1 ! A temporary variable [nondim]
1312 real, parameter :: C1_3 = 1.0/3.0, C1_12 = 1.0/12.0 ! Rational constants [nondim]
1313 real, parameter :: C2pi_3 = 8.0*atan(1.0)/3.0 ! An irrational constant, 2/3 pi. [nondim]
1314 integer :: K, nz
1315
13160 nz = GV%ke
1317
1318 ! Each cell extends from x=-1/2 to 1/2, and has a topography
1319 ! given by D(x) = crv*x^2 + slope*x + D_vel - crv/12.
1320 !crv_3 = (Dp + Dm - 2.0*D_vel) ; crv = 3.0*crv_3
13210 crv_3 = (Dp + Dm - (2.0*D_vel)) ; crv = 3.0*crv_3
13220 slope = Dp - Dm
1323
1324 ! Calculate the volume above which the entire cell is open and the volume at which the
1325 ! equation that is solved for L changes because there are two separate open regions.
13260 if (slope >= crv) then
13270 Vol_open = D_vel - Dm ; Vol_2_reg = Vol_open
1328 else
13290 slope_crv = slope / crv
13300 Vol_open = 0.25*slope*slope_crv + C1_12*crv
13310 Vol_2_reg = 0.5*slope_crv**2 * (crv - C1_3*slope)
1332 endif
1333 ! Define some combinations of crv & slope for later use.
13340 C24_crv = 24.0/crv ; Iapb = 1.0/(crv+slope)
13350 apb_4a = (slope+crv)/(4.0*crv) ; a2x48_apb3 = (48.0*(crv*crv))*(Iapb**3)
13360 ax2_3apb = 2.0*C1_3*crv*Iapb
1337
13380 L(nz+1) = 0.0
1339 ! Determine the normalized open length (L) at each interface.
13400 do K=nz,1,-1
13410 if (vol_below(K) >= Vol_open) then ! The whole cell is open.
13420 L(K) = 1.0
13430 elseif (vol_below(K) < Vol_2_reg) then
1344 ! In this case, there is a contiguous open region and
1345 ! vol_below(K) = 0.5*L^2*(slope + crv/3*(3-4L)).
13460 if (a2x48_apb3*vol_below(K) < 1e-8) then ! Could be 1e-7?
1347 ! There is a very good approximation here for massless layers.
1348 !L0 = sqrt(2.0*vol_below(K)*Iapb) ; L(K) = L0*(1.0 + ax2_3apb*L0)
13490 L0 = sqrt(2.0*vol_below(K)*Iapb) ; L(K) = L0*(1.0 + (ax2_3apb*L0))
1350 else
1351 !L(K) = apb_4a * (1.0 - &
1352 ! 2.0 * cos(C1_3*acos(a2x48_apb3*vol_below(K) - 1.0) - C2pi_3))
1353 L(K) = apb_4a * (1.0 - &
13540 2.0 * cos(C1_3*acos((a2x48_apb3*vol_below(K)) - 1.0) - C2pi_3))
1355 endif
1356 ! To check the answers.
1357 ! Vol_err = 0.5*(L(K)*L(K))*(slope + crv_3*(3.0-4.0*L(K))) - vol_below(K)
1358 else ! There are two separate open regions.
1359 ! vol_below(K) = slope^2/4crv + crv/12 - (crv/12)*(1-L)^2*(1+2L)
1360 ! At the deepest volume, L = slope/crv, at the top L = 1.
1361 ! L(K) = 0.5 - cos(C1_3*acos(1.0 - C24_crv*(Vol_open - vol_below(K))) - C2pi_3)
13620 tmp_val_m1_to_p1 = 1.0 - C24_crv*(Vol_open - vol_below(K))
13630 tmp_val_m1_to_p1 = max(-1., min(1., tmp_val_m1_to_p1))
13640 L(K) = 0.5 - cos(C1_3*acos(tmp_val_m1_to_p1) - C2pi_3)
1365 ! To check the answers.
1366 ! Vol_err = Vol_open - 0.25*crv_3*(1.0+2.0*L(K)) * (1.0-L(K))**2 - vol_below(K)
1367 endif
1368 enddo ! k loop to determine L(K) in the concave case
1369
13700end subroutine find_L_open_concave_trigonometric
1371
1372
1373
1374!> Determine the normalized open length of each interface for concave bathymetry (from the ocean perspective) using
1375!! iterative methods to solve the relevant cubic equations. In this case there can be two separate open regions.
137611664pure subroutine find_L_open_concave_iterative(vol_below, D_vel, Dp, Dm, L, GV)
1377 !$omp declare target
1378 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
1379 real, dimension(SZK_(GV)+1), intent(in) :: vol_below !< The volume below each interface, normalized by
1380 !! the full horizontal area of a velocity cell [Z ~> m]
1381 real, intent(in) :: D_vel !< The average bottom depth at a velocity point [Z ~> m]
1382 real, intent(in) :: Dp !< The larger of the two depths at the edge
1383 !! of a velocity cell [Z ~> m]
1384 real, intent(in) :: Dm !< The smaller of the two depths at the edge
1385 !! of a velocity cell [Z ~> m]
1386 real, dimension(SZK_(GV)+1), intent(out) :: L !< The fraction of the full cell width that is open at
1387 !! the depth of each interface [nondim]
1388
1389 ! Local variables
1390 real :: crv ! crv is the curvature of the bottom depth across a
1391 ! cell, times the cell width squared [Z ~> m].
1392 real :: crv_3 ! crv/3 [Z ~> m].
1393 real :: slope ! The absolute value of the bottom depth slope across
1394 ! a cell times the cell width [Z ~> m].
1395
1396 ! The following "volumes" have units of vertical heights because they are normalized
1397 ! by the full horizontal area of a velocity cell.
1398 real :: Vol_open ! The cell volume above which the face is fully is open [Z ~> m].
1399 real :: Vol_2_reg ! The cell volume above which there are two separate
1400 ! open areas that must be integrated [Z ~> m].
1401 real :: L_2_reg ! The value of L when vol_below is Vol_2_reg [nondim]
1402 real :: vol_inflect_1 ! The volume at which there is an inflection point in the expression
1403 ! relating L to vol_err when there is a single open region [Z ~> m]
1404 real :: vol_inflect_2 ! The volume at which there is an inflection point in the expression
1405 ! relating L to vol_err when there are two open regions [Z ~> m]
1406
1407 real :: L_inflect_1 ! The value of L that sits at an inflection point in the expression
1408 ! relating L to vol_err when there is a single open region [nondim]
1409 real :: L_inflect_2 ! The value of L that sits at an inflection point in the expression
1410 ! relating L to vol_err when there is are two open regions [nondim]
1411 real :: L_max, L_min ! Maximum and minimum bounds on the solution for L for an interface [nondim]
1412 real :: vol_err ! The difference between the volume below an interface for a given value
1413 ! of L and the target value [Z ~> m]
1414 real :: dVol_dL ! The partial derivative of the volume below with L [Z ~> m]
1415 real :: vol_err_max ! The value of vol_err when L is L_max [Z ~> m]
1416
1417 ! The following combinations of slope and crv are reused across layers, and hence are pre-calculated
1418 ! for efficiency. All are non-negative.
1419 real :: Icrvpslope ! The inverse of the sum of crv and slope [Z-1 ~> m-1]
1420 real :: slope_crv ! The slope divided by the curvature [nondim]
1421 ! These are only used if the slope exceeds or matches the curvature.
1422 real :: smc ! The slope minus the curvature [Z ~> m]
1423 real :: C3c_m_s ! 3 times the curvature minus the slope [Z ~> m]
1424 real :: I_3c_m_s ! The inverse of 3 times the curvature minus the slope [Z-1 ~> m-1]
1425 ! These are only used if the curvature exceeds the slope.
1426 real :: C4_crv ! The inverse of a quarter of the curvature [Z-1 ~> m-1]
1427 real :: sxcms_c ! The slope times the difference between the curvature and slope
1428 ! divided by the curvature [Z ~> m]
1429 real :: slope2_4crv ! A quarter of the slope squared divided by the curvature [Z ~> m]
1430 real :: I_3s_m_c ! The inverse of 3 times the slope minus the curvature [Z-1 ~> m-1]
1431 real :: C3s_m_c ! 3 times the slope minus the curvature [Z ~> m]
1432
1433 real, parameter :: C1_3 = 1.0 / 3.0, C1_12 = 1.0 / 12.0 ! Rational constants [nondim]
1434 integer :: K, nz, itt
1435 integer, parameter :: max_itt = 10
1436
143711664 nz = GV%ke
1438
1439 ! Each cell extends from x=-1/2 to 1/2, and has a topography
1440 ! given by D(x) = crv*x^2 + slope*x + D_vel - crv/12.
1441
144211664 crv_3 = (Dp + Dm - 2.0*D_vel) ; crv = 3.0*crv_3
144311664 slope = Dp - Dm
1444
1445 ! Calculate the volume above which the entire cell is open and the volume at which the
1446 ! equation that is solved for L changes because there are two separate open regions.
144711664 if (slope >= crv) then
14489024 Vol_open = D_vel - Dm ; Vol_2_reg = Vol_open
14499024 L_2_reg = 1.0
14509024 if (crv + slope >= 4.0*crv) then
14515544 L_inflect_1 = 1.0 ; Vol_inflect_1 = Vol_open
1452 else
14533480 slope_crv = slope / crv
14543480 L_inflect_1 = 0.25 + 0.25*slope_crv
14553480 vol_inflect_1 = 0.25*C1_12 * ((slope_crv + 1.0)**2 * (slope + crv))
1456 endif
1457 ! Precalculate some combinations of crv & slope for later use.
14589024 smc = slope - crv
14599024 C3c_m_s = 3.0*crv - slope
14609024 if (C3c_m_s > 2.0*smc) I_3c_m_s = 1.0 / C3c_m_s
1461 else
14622640 slope_crv = slope / crv
14632640 Vol_open = 0.25*slope*slope_crv + C1_12*crv
14642640 Vol_2_reg = 0.5*slope_crv**2 * (crv - C1_3*slope)
14652640 L_2_reg = slope_crv
1466
1467 ! The inflection point is useful to know because below the inflection point
1468 ! Newton's method converges monotonically from above and conversely above it.
1469 ! These are the inflection point values of L and vol_below with a single open segment.
14702640 vol_inflect_1 = 0.25*C1_12 * ((slope_crv + 1.0)**2 * (slope + crv))
14712640 L_inflect_1 = 0.25 + 0.25*slope_crv
1472 ! These are the inflection point values of L and vol_below when there are two open segments.
1473 ! Vol_inflect_2 = Vol_open - 0.125 * crv_3, which is equivalent to:
14742640 vol_inflect_2 = 0.25*slope*slope_crv + 0.125*crv_3
14752640 L_inflect_2 = 0.5
1476 ! Precalculate some combinations of crv & slope for later use.
14772640 C4_crv = 4.0 / crv
14782640 slope2_4crv = 0.25 * slope * slope_crv
14792640 sxcms_c = slope_crv*(crv - slope)
14802640 C3s_m_c = 3.0*slope - crv
14812640 if (C3s_m_c > 2.0*sxcms_c) I_3s_m_c = 1.0 / C3s_m_c
1482 endif
1483 ! Define some combinations of crv & slope for later use.
148411664 Icrvpslope = 1.0 / (crv+slope)
1485
148611664 L(nz+1) = 0.0
1487 ! Determine the normalized open length (L) at each interface.
1488886464 do K=nz,1,-1
1489886464 if (vol_below(K) >= Vol_open) then ! The whole cell is open.
1490553728 L(K) = 1.0
1491321072 elseif (vol_below(K) < Vol_2_reg) then
1492 ! In this case, there is a single contiguous open region from x=1/2-L to 1/2.
1493 ! Changing the horizontal variable in the expression from D(x) to D(L) gives:
1494 ! x(L) = 1/2 - L
1495 ! D(L) = crv*(0.5 - L)^2 + slope*(0.5 - L) + D_vel - crv/12
1496 ! D(L) = crv*L^2 - crv*L + crv/4 + slope*(1/2 - L) + D_vel - crv/12
1497 ! D(L) = crv*L^2 - (slope+crv)*L + slope/2 + D_vel + crv/6
1498 ! D(0) = slope/2 + D_vel + crv/6 = (Dp - Dm)/2 + D_vel + (Dp + Dm - 2*D_vel)/2 = Dp
1499 ! D(1) = crv - slope - crv + slope/2 + Dvel + crv/6 = D_vel - slope/2 + crv/6 = Dm
1500 !
1501 ! vol_below = integral(y = 0 to L) D(y) dy - L * D(L)
1502 ! = crv/3*L^3 - (slope+crv)/2*L^2 + (slope/2 + D_vel + crv/6)*L -
1503 ! (crv*L^2 - (slope+crv)*L + slope/2 + D_vel + crv/6) * L
1504 ! = -2/3 * crv * L^3 + 1/2 * (slope+crv) * L^2
1505 ! vol_below(K) = 0.5*L(K)**2*(slope + crv_3*(3-4*L(K)))
1506 ! L(K) is between L(K+1) and slope_crv.
1507319080 L_max = min(L_2_reg, 1.0)
1508319080 if (vol_below(K) <= vol_inflect_1) L_max = min(L_max, L_inflect_1)
1509
1510319080 L_min = L(K+1)
1511319080 if (vol_below(K) >= vol_inflect_1) L_min = max(L_min, L_inflect_1)
1512
1513 ! Ignoring the cubic term gives an under-estimate but is very accurate for near bottom
1514 ! layers, so use this as a potential floor.
1515319080 if (2.0*vol_below(K)*Icrvpslope > L_min**2) L_min = sqrt(2.0*vol_below(K)*Icrvpslope)
1516
1517 ! Start with L_min in most cases.
1518319080 L(k) = L_min
1519
1520319080 if (vol_below(K) <= vol_inflect_1) then
1521 ! Starting with L_min below L_inflect_1, only the first overshooting iteration of Newton's
1522 ! method needs bounding.
1523318384 L(k) = L_min
1524318384 vol_err = 0.5*L(K)**2 * (slope + crv*(1.0 - 4.0*C1_3*L(K))) - vol_below(K)
1525 ! If vol_err is 0 or positive (perhaps due to roundoff in L(K+1)), L_min is already the best solution.
1526318384 if (vol_err < 0.0) then
1527318384 dVol_dL = L(K) * (slope + crv*(1.0 - 2.0*L(k)))
1528318384 if (L(K)*dVol_dL > vol_err + L_max*dVol_dL) then
152924 L(K) = L_max
1530 else
1531318360 L(K) = L(K) - (vol_err / dVol_dL)
1532 endif
1533
1534 ! Subsequent iterations of Newton's method do not need bounds.
1535937427 do itt=1,max_itt
1536937427 vol_err = 0.5*L(K)**2 * (slope + crv*(1.0 - 4.0*C1_3*L(K))) - vol_below(K)
1537937427 dVol_dL = L(K) * (slope + crv*(1.0 - 2.0*L(k)))
1538937427 if (abs(vol_err) < max(1.0e-15*L(K), 1.0e-25)*dVol_dL) exit
1539619043 L(K) = L(K) - (vol_err / dVol_dL)
1540 enddo
1541 endif
1542 else ! (vol_below(K) > vol_inflect_1)
1543 ! Iteration from below converges monotonically, but we need to deal with the case where we are
1544 ! close to the peak of the topography and Newton's method mimics the convergence of bisection.
1545
1546 ! Evaluate the error when L(K) = L_min as a possible first guess.
1547696 L(k) = L_min
1548696 vol_err = 0.5*L(K)**2 * (slope + crv*(1.0 - 4.0*C1_3*L(K))) - vol_below(K)
1549 ! If vol_err is 0 or positive (perhaps due to roundoff in L(K+1)), L_min is already the best solution.
1550696 if (vol_err < 0.0) then
1551
1552 ! These two upper estimates deal with the possibility that this point may be near
1553 ! the upper extrema, where the error term might be approximately parabolic and
1554 ! Newton's method would converge slowly like simple bisection.
1555696 if (slope < crv) then
1556 ! if ((L_2_reg - L_min)*(3.0*slope - crv) > 2.0*slope_crv*(crv-slope)) then
1557168 if ((L_2_reg - L_min)*C3s_m_c > 2.0*sxcms_c) then
1558 ! There is a decent upper estimate of L from the approximate quadratic equation found
1559 ! by examining the error expressions at L ~= L_2_reg and ignoring the cubic term.
1560 L_max = (slope_crv*(2.0*slope) - sqrt(sxcms_c**2 + &
156148 2.0*C3s_m_c*(Vol_2_reg - vol_below(K))) ) * I_3s_m_c
1562 ! The line above is equivalent to:
1563 ! L_max = (slope_crv*(2.0*slope) - sqrt(slope_crv**2*(crv-slope)**2 + &
1564 ! 2.0*(3.0*slope - crv)*(Vol_2_reg - vol_below(K))) ) / &
1565 ! (3.0*slope - crv)
1566 else
1567120 L_max = slope_crv
1568 endif
1569 else ! (slope >= crv)
1570528 if ((1.0 - L_min)*C3c_m_s > 2.0*smc) then
1571 ! There is a decent upper estimate of L from the approximate quadratic equation found
1572 ! by examining the error expressions at L ~= 1 and ignoring the cubic term.
1573168 L_max = ( 2.0*crv - sqrt(smc**2 + 2.0*C3c_m_s * (Vol_open - vol_below(K))) ) * I_3c_m_s
1574 ! The line above is equivalent to:
1575 ! L_max = ( 2.0*crv - sqrt((slope - crv)**2 + 2.0*(3.0*crv - slope) * (Vol_open - vol_below(K))) ) / &
1576 ! (3.0*crv - slope)
1577 else
1578360 L_max = 1.0
1579 endif
1580 endif
1581696 Vol_err_max = 0.5*L_max**2 * (slope + crv*(1.0 - 4.0*C1_3*L_max)) - vol_below(K)
1582 ! if (Vol_err_max < 0.0) call MOM_error(FATAL, &
1583 ! "Vol_err_max should never be negative in find_L_open_concave_iterative.")
1584696 if ((Vol_err_max < abs(Vol_err)) .and. (L_max < 1.0)) then
1585 ! Start with 1 bounded Newton's method step from L_max
1586264 dVol_dL = L_max * (slope + crv*(1.0 - 2.0*L_max))
1587264 L(K) = max(L_min, L_max - (vol_err_max / dVol_dL) )
1588 ! else ! Could use the fact that Vol_err is known to take an iteration?
1589 endif
1590
1591 ! Subsequent iterations of Newton's method do not need bounds.
15922976 do itt=1,max_itt
15932976 vol_err = 0.5*L(K)**2 * (slope + crv*(1.0 - 4.0*C1_3*L(K))) - vol_below(K)
15942976 dVol_dL = L(K) * (slope + crv*(1.0 - 2.0*L(k)))
15952976 if (abs(vol_err) < max(1.0e-15*L(K), 1.0e-25)*dVol_dL) exit
15962280 L(K) = L(K) - (vol_err / dVol_dL)
1597 enddo
1598 endif
1599
1600 endif
1601
1602 ! To check the answers.
1603 ! Vol_err = 0.5*(L(K)*L(K))*(slope + crv_3*(3.0-4.0*L(K))) - vol_below(K)
1604 else ! There are two separate open regions.
1605 ! vol_below(K) = slope^2/(4*crv) + crv/12 - (crv/12)*(1-L)^2*(1+2L)
1606 ! At the deepest volume, L = slope/crv, at the top L = 1.
1607
1608 ! To check the answers.
1609 ! Vol_err = Vol_open - 0.25*crv_3*(1.0+2.0*L(K)) * (1.0-L(K))**2 - vol_below(K)
1610 ! or equivalently:
1611 ! Vol_err = Vol_open - 0.25*crv_3*(3.0-2.0*(1.0-L(K))) * (1.0-L(K))**2 - vol_below(K)
1612 ! ! Note that: Vol_open = 0.25*slope*slope_crv + C1_12*crv
1613 ! Vol_err = 0.25*slope*slope_crv + 0.25*crv_3*( 1.0 - (1.0 + 2.0*L(K)) * (1.0-L(K))**2 ) - vol_below(K)
1614 ! Vol_err = 0.25*crv_3*L(K)**2*( 3.0 - 2.0*L(K) ) + 0.25*slope*slope_crv - vol_below(K)
1615
1616 ! Derivation of the L_max limit below:
1617 ! Vol_open - vol_below(K) = 0.25*crv_3*(3.0-2.0*(1.0-L(K))) * (1.0-L(K))**2
1618 ! (3.0-2.0*(1.0-L(K))) * (1.0-L(K))**2 = (Vol_open - vol_below(K)) / (0.25*crv_3)
1619 ! When 1-L(K) << 1:
1620 ! 3.0 * (1.0-L_max)**2 = (Vol_open - vol_below(K)) / (0.25*crv_3)
1621 ! (1.0-L_max)**2 = (Vol_open - vol_below(K)) / (0.25*crv)
1622
1623 ! Derivation of the L_min limit below:
1624 ! Vol_err = 0.25*crv_3*L(K)**2*( 3.0 - 2.0*L(K) ) + 0.25*slope*slope_crv - vol_below(K)
1625 ! crv*L(K)**2*( 1.0 - 2.0*C1_3*L(K) ) = 4.0*vol_below(K) - slope*slope_crv
1626 ! When L(K) << 1:
1627 ! crv*L_min**2 = 4.0*vol_below(K) - slope*slope_crv
1628 ! L_min = sqrt((4.0*vol_below(K) - slope*slope_crv)/crv)
1629 ! Noting that L(K) >= slope_crv, when L(K)-slope_crv << 1:
1630 ! (crv + 2.0*C1_3*slope)*L_min**2 = 4.0*vol_below(K) - slope*slope_crv
1631 ! L_min = sqrt((4.0*vol_below(K) - slope*slope_crv)/(crv + 2.0*C1_3*slope))
1632
16331992 if (vol_below(K) <= Vol_inflect_2) then
1634 ! Newton's Method would converge monotonically from above, but overshoot from below.
16351776 L_min = max(L(K+1), L_2_reg) ! L_2_reg = slope_crv
1636 ! This under-estimate of L(K) is accurate for L ~= slope_crv:
16371776 if ((4.0*vol_below(K) - slope*slope_crv) > (crv + 2.0*C1_3*slope)*L_min**2) &
1638960 L_min = max(L_min, sqrt((4.0*vol_below(K) - slope*slope_crv) / (crv + 2.0*C1_3*slope)))
16391776 L_max = 0.5 ! = L_inflect_2
1640
1641 ! Starting with L_min below L_inflect_2, only the first overshooting iteration of Newton's
1642 ! method needs bounding.
16431776 L(k) = L_min
16441776 Vol_err = crv_3*L(K)**2*( 0.75 - 0.5*L(K) ) + (slope2_4crv - vol_below(K))
1645
1646 ! If vol_err is 0 or positive (perhaps due to roundoff in L(K+1)), L_min is already the best solution.
16471776 if (vol_err < 0.0) then
16481776 dVol_dL = 0.5*crv * (L(K) * (1.0 - L(K)))
16491776 if (L(K)*dVol_dL >= vol_err + L_max*dVol_dL) then
16500 L(K) = L_max
1651 else
16521776 L(K) = L(K) - (vol_err / dVol_dL)
1653 endif
1654 ! Subsequent iterations of Newton's method do not need bounds.
16555619 do itt=1,max_itt
16565619 Vol_err = crv_3 * (L(K)**2 * (0.75 - 0.5*L(K))) + (slope2_4crv - vol_below(K))
16575619 dVol_dL = 0.5*crv * (L(K)*(1.0 - L(K)))
16585619 if (abs(vol_err) < max(1.0e-15*L(K), 1.0e-25)*dVol_dL) exit
16593843 L(K) = L(K) - (vol_err / dVol_dL)
1660 enddo
1661 endif
1662 else ! (vol_below(K) > Vol_inflect_2)
1663 ! Newton's Method would converge monotonically from below, but overshoots from above, and
1664 ! we may need to deal with the case where we are close to the peak of the topography.
1665216 L_min = max(L(K+1), 0.5)
1666216 L(k) = L_min
1667
1668216 Vol_err = crv_3 * (L(K)**2 * ( 0.75 - 0.5*L(K))) + (slope2_4crv - vol_below(K))
1669 ! If vol_err is 0 or positive (perhaps due to roundoff in L(K+1)), L(k) is already the best solution.
1670216 if (Vol_err < 0.0) then
1671 ! This over-estimate of L(K) is accurate for L ~= 1:
1672216 L_max = 1.0 - sqrt( (Vol_open - vol_below(K)) * C4_crv )
1673216 Vol_err_max = crv_3 * (L_max**2 * ( 0.75 - 0.5*L_max)) + (slope2_4crv - vol_below(K))
1674 ! if (Vol_err_max < 0.0) call MOM_error(FATAL, &
1675 ! "Vol_err_max should never be negative in find_L_open_concave_iterative.")
1676216 if ((Vol_err_max < abs(Vol_err)) .and. (L_max < 1.0)) then
1677 ! Start with 1 bounded Newton's method step from L_max
1678216 dVol_dL = 0.5*crv * (L_max * (1.0 - L_max))
1679216 L(K) = max(L_min, L_max - (vol_err_max / dVol_dL) )
1680 ! else ! Could use the fact that Vol_err is known to take an iteration?
1681 endif
1682
1683 ! Subsequent iterations of Newton's method do not need bounds.
1684816 do itt=1,max_itt
1685816 Vol_err = crv_3 * (L(K)**2 * ( 0.75 - 0.5*L(K))) + (slope2_4crv - vol_below(K))
1686816 dVol_dL = 0.5*crv * (L(K) * (1.0 - L(K)))
1687816 if (abs(vol_err) < max(1.0e-15*L(K), 1.0e-25)*dVol_dL) exit
1688600 L(K) = L(K) - (vol_err / dVol_dL)
1689 enddo
1690 endif
1691 endif
1692
1693 endif
1694 enddo ! k loop to determine L(K) in the concave case
1695
169611664end subroutine find_L_open_concave_iterative
1697
1698
1699
1700!> Test the validity the normalized open lengths of each interface for concave bathymetry (from the ocean perspective)
1701!! by evaluating and returing the relevant cubic equations.
17020pure subroutine test_L_open_concave(vol_below, D_vel, Dp, Dm, L, vol_err, GV)
1703 !$omp declare target
1704 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
1705 real, dimension(SZK_(GV)+1), intent(in) :: vol_below !< The volume below each interface, normalized by
1706 !! the full horizontal area of a velocity cell [Z ~> m]
1707 real, intent(in) :: D_vel !< The average bottom depth at a velocity point [Z ~> m]
1708 real, intent(in) :: Dp !< The larger of the two depths at the edge
1709 !! of a velocity cell [Z ~> m]
1710 real, intent(in) :: Dm !< The smaller of the two depths at the edge
1711 !! of a velocity cell [Z ~> m]
1712 real, dimension(SZK_(GV)+1), intent(in) :: L !< The fraction of the full cell width that is open at
1713 !! the depth of each interface [nondim]
1714 real, dimension(SZK_(GV)+1), intent(out) :: vol_err !< The difference between vol_below and the
1715 !! value obtained from using L in the cubic equation [Z ~> m]
1716
1717 ! Local variables
1718 real :: crv ! crv is the curvature of the bottom depth across a
1719 ! cell, times the cell width squared [Z ~> m].
1720 real :: crv_3 ! crv/3 [Z ~> m].
1721 real :: slope ! The absolute value of the bottom depth slope across
1722 ! a cell times the cell width [Z ~> m].
1723
1724 ! The following "volumes" have units of vertical heights because they are normalized
1725 ! by the full horizontal area of a velocity cell.
1726 real :: Vol_open ! The cell volume above which the face is fully is open [Z ~> m].
1727 real :: Vol_2_reg ! The cell volume above which there are two separate
1728 ! open areas that must be integrated [Z ~> m].
1729 real :: L_2_reg ! The value of L when vol_below is Vol_2_reg [nondim]
1730
1731 ! The following combinations of slope and crv are reused across layers, and hence are pre-calculated
1732 ! for efficiency. All are non-negative.
1733 real :: slope_crv ! The slope divided by the curvature [nondim]
1734 ! These are only used if the curvature exceeds the slope.
1735 real :: slope2_4crv ! A quarter of the slope squared divided by the curvature [Z ~> m]
1736
1737 real, parameter :: C1_3 = 1.0 / 3.0, C1_12 = 1.0 / 12.0 ! Rational constants [nondim]
1738 integer :: K, nz
1739
17400 nz = GV%ke
1741
1742 ! Each cell extends from x=-1/2 to 1/2, and has a topography
1743 ! given by D(x) = crv*x^2 + slope*x + D_vel - crv/12.
1744
17450 crv_3 = (Dp + Dm - 2.0*D_vel) ; crv = 3.0*crv_3
17460 slope = Dp - Dm
1747
1748 ! Calculate the volume above which the entire cell is open and the volume at which the
1749 ! equation that is solved for L changes because there are two separate open regions.
17500 if (slope >= crv) then
17510 Vol_open = D_vel - Dm ; Vol_2_reg = Vol_open
17520 L_2_reg = 1.0
17530 if (crv + slope >= 4.0*crv) then
17540 slope_crv = 1.0
1755 else
17560 slope_crv = slope / crv
1757 endif
1758 else
17590 slope_crv = slope / crv
17600 Vol_open = 0.25*slope*slope_crv + C1_12*crv
17610 Vol_2_reg = 0.5*slope_crv**2 * (crv - C1_3*slope)
17620 L_2_reg = slope_crv
1763 endif
17640 slope2_4crv = 0.25 * slope * slope_crv
1765
1766 ! Determine the volume error based on the normalized open length (L) at each interface.
17670 Vol_err(nz+1) = 0.0
17680 do K=nz,1,-1
17690 if (L(K) >= 1.0) then
17700 Vol_err(K) = max(Vol_open - vol_below(K), 0.0)
17710 elseif (L(K) <= L_2_reg) then
17720 vol_err(K) = 0.5*L(K)**2 * (slope + crv*(1.0 - 4.0*C1_3*L(K))) - vol_below(K)
1773 else ! There are two separate open regions.
17740 Vol_err(K) = crv_3 * (L(K)**2 * ( 0.75 - 0.5*L(K))) + (slope2_4crv - vol_below(K))
1775 endif
1776 enddo ! k loop to determine L(K) in the concave case
1777
17780end subroutine test_L_open_concave
1779
1780
1781!> Determine the normalized open length of each interface for convex bathymetry (from the ocean
1782!! perspective) using Newton's method iterations. In this case there is a single open region
1783!! with the minimum depth at one edge of the cell.
1784105672pure subroutine find_L_open_convex(vol_below, D_vel, Dp, Dm, L, GV, US, CS)
1785 !$omp declare target
1786 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
1787 real, dimension(SZK_(GV)+1), intent(in) :: vol_below !< The volume below each interface, normalized by
1788 !! the full horizontal area of a velocity cell [Z ~> m]
1789 real, intent(in) :: D_vel !< The average bottom depth at a velocity point [Z ~> m]
1790 real, intent(in) :: Dp !< The larger of the two depths at the edge
1791 !! of a velocity cell [Z ~> m]
1792 real, intent(in) :: Dm !< The smaller of the two depths at the edge
1793 !! of a velocity cell [Z ~> m]
1794 real, dimension(SZK_(GV)+1), intent(out) :: L !< The fraction of the full cell width that is open at
1795 !! the depth of each interface [nondim]
1796 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1797 type(set_visc_CS), intent(in) :: CS !< The control structure returned by a previous
1798 !! call to set_visc_init.
1799
1800 ! Local variables
1801 real :: crv ! crv is the curvature of the bottom depth across a
1802 ! cell, times the cell width squared [Z ~> m].
1803 real :: crv_3 ! crv/3 [Z ~> m].
1804 real :: slope ! The absolute value of the bottom depth slope across
1805 ! a cell times the cell width [Z ~> m].
1806 ! All of the following "volumes" have units of vertical heights because they are normalized
1807 ! by the full horizontal area of a velocity cell.
1808 real :: Vol_err ! The error in the volume with the latest estimate of
1809 ! L, or the error for the interface below [Z ~> m].
1810 real :: Vol_quit ! The volume error below which to quit iterating [Z ~> m].
1811 real :: Vol_tol ! A volume error tolerance [Z ~> m].
1812 real :: Vol_open ! The cell volume above which the face is fully open [Z ~> m].
1813 real :: Vol_direct ! With less than Vol_direct [Z ~> m], there is a direct
1814 ! solution of a cubic equation for L.
1815 real :: Vol_err_max ! The volume error for the upper bound on the correct value for L [Z ~> m]
1816 real :: Vol_err_min ! The volume error for the lower bound on the correct value for L [Z ~> m]
1817 real :: Vol_0 ! A deeper volume with known width L0 [Z ~> m].
1818 real :: dVol ! vol - Vol_0 [Z ~> m].
1819 real :: dV_dL2 ! The partial derivative of volume with L squared
1820 ! evaluated at L=L0 [Z ~> m].
1821 real :: L_direct ! The value of L above volume Vol_direct [nondim].
1822 real :: L_max, L_min ! Upper and lower bounds on the correct value for L [nondim].
1823 real :: L0 ! The value of L above volume Vol_0 [nondim].
1824 real :: Iapb, Ibma_2 ! Combinations of crv (a) and slope (b) [Z-1 ~> m-1]
1825 real :: C24_crv ! 24/crv [Z-1 ~> m-1].
1826 real :: curv_tol ! Numerator of curvature cubed, used to estimate
1827 ! accuracy of a single L(:) Newton iteration [Z5 ~> m5]
1828 real, parameter :: C1_3 = 1.0/3.0, C1_6 = 1.0/6.0 ! Rational constants [nondim]
1829 logical :: use_L0, do_one_L_iter ! Control flags for L(:) Newton iteration
1830 integer :: K, nz, itt
1831 integer, parameter:: maxitt = 20
1832
1833105672 nz = GV%ke
1834
1835 ! Each cell extends from x=-1/2 to 1/2, and has a topography
1836 ! given by D(x) = crv*x^2 + slope*x + D_vel - crv/12.
1837105672 crv_3 = (Dp + Dm - 2.0*D_vel) ; crv = 3.0*crv_3
1838105672 slope = Dp - Dm
1839
1840 ! Calculate the volume above which the entire cell is open and the volume at which the
1841 ! equation that is solved for L changes because there is a direct solution.
1842105672 Vol_open = D_vel - Dm
1843105672 if (slope >= -crv) then
184489760 Iapb = 1.0e30*US%Z_to_m ; if (slope+crv /= 0.0) Iapb = 1.0/(crv+slope)
184589760 Vol_direct = 0.0 ; L_direct = 0.0 ; C24_crv = 0.0
1846 else
184715912 C24_crv = 24.0/crv ; Iapb = 1.0/(crv+slope)
184815912 L_direct = 1.0 + slope/crv ! L_direct < 1 because crv < 0
184915912 Vol_direct = -C1_6*crv*L_direct**3
1850 endif
1851105672 Ibma_2 = 2.0 / (slope - crv)
1852
1853105672 if (CS%answer_date < 20190101) Vol_quit = (0.9*GV%Angstrom_Z + GV%dZ_subroundoff)
1854
1855105672 L(nz+1) = 0.0 ; Vol_err = 0.0
1856 ! Determine the normalized open length (L) at each interface.
18578031072 do K=nz,1,-1
18588031072 if (vol_below(K) >= Vol_open) then
18595431235 L(K) = 1.0
18602494165 elseif (vol_below(K) <= Vol_direct) then
1861351360 if (CS%answer_date < 20260501) then
18620 L(K) = (-0.25*C24_crv*vol_below(K))**C1_3
1863 else
1864351360 L(K) = cuberoot(-0.25*C24_crv*vol_below(K))
1865 endif
1866 else
1867 ! x_R is at 1/2 but x_L is in the interior & L is found by iteratively solving
1868 ! vol_below(K) = 0.5*L^2*(slope + crv/3*(3-4L))
1869
1870 ! Vol_err = 0.5*(L(K+1)*L(K+1))*(slope + crv_3*(3.0-4.0*L(K+1))) - vol_below(K+1)
1871 ! Change to ...
1872 ! if (min(vol_below(K+1) + Vol_err, vol_below(K)) <= Vol_direct) then ?
18732142805 if (vol_below(K+1) + Vol_err <= Vol_direct) then
187498556 L0 = L_direct ; Vol_0 = Vol_direct
1875 else
18762044249 L0 = L(K+1) ; Vol_0 = vol_below(K+1) + Vol_err
1877 ! Change to Vol_0 = min(vol_below(K+1) + Vol_err, vol_below(K)) ?
1878 endif
1879
1880 ! Try a relatively simple solution that usually works well
1881 ! for massless layers.
18822142805 dV_dL2 = 0.5*(slope+crv) - crv*L0 ; dVol = (vol_below(K)-Vol_0)
1883 ! dV_dL2 = 0.5*(slope+crv) - crv*L0 ; dVol = max(vol_below(K)-Vol_0, 0.0)
1884
18852142805 use_L0 = .false.
18862142805 do_one_L_iter = .false.
18872142805 if (CS%answer_date < 20190101) then
1888 curv_tol = GV%Angstrom_Z*dV_dL2**2 &
18890 * (0.25 * dV_dL2 * GV%Angstrom_Z - crv * L0 * dVol)
18900 do_one_L_iter = (crv * crv * dVol**3) < curv_tol
1891 else
1892 ! The following code is more robust when GV%Angstrom_H=0, but
1893 ! it changes answers.
18942142805 use_L0 = (dVol <= 0.)
1895
18962142805 Vol_tol = max(0.5 * GV%Angstrom_Z + GV%dZ_subroundoff, 1e-14 * vol_below(K))
18972142805 Vol_quit = max(0.9 * GV%Angstrom_Z + GV%dZ_subroundoff, 1e-14 * vol_below(K))
1898
1899 curv_tol = Vol_tol * dV_dL2**2 &
19002142805 * (dV_dL2 * Vol_tol - 2.0 * crv * L0 * dVol)
19012142805 do_one_L_iter = (crv * crv * dVol**3) < curv_tol
1902 endif
1903
19042142805 if (use_L0) then
19050 L(K) = L0
19060 Vol_err = 0.5*(L(K)*L(K))*(slope + crv_3*(3.0-4.0*L(K))) - vol_below(K)
19072142805 elseif (do_one_L_iter) then
1908 ! One iteration of Newton's method should give an estimate
1909 ! that is accurate to within Vol_tol.
19100 L(K) = sqrt(L0*L0 + dVol / dV_dL2)
19110 Vol_err = 0.5*(L(K)*L(K))*(slope + crv_3*(3.0-4.0*L(K))) - vol_below(K)
1912 else
19132142805 if (dV_dL2*(1.0-L0*L0) < dVol + &
1914 dV_dL2 * (Vol_open - vol_below(K))*Ibma_2) then
1915101353 L_max = sqrt(1.0 - (Vol_open - vol_below(K))*Ibma_2)
1916 else
19172041452 L_max = sqrt(L0*L0 + dVol / dV_dL2)
1918 endif
19192142805 L_min = sqrt(L0*L0 + dVol / (0.5*(slope+crv) - crv*L_max))
1920
19212142805 Vol_err_min = 0.5*(L_min**2)*(slope + crv_3*(3.0-4.0*L_min)) - vol_below(K)
19222142805 Vol_err_max = 0.5*(L_max**2)*(slope + crv_3*(3.0-4.0*L_max)) - vol_below(K)
1923 ! if ((abs(Vol_err_min) <= Vol_quit) .or. (Vol_err_min >= Vol_err_max)) then
19242142805 if (abs(Vol_err_min) <= Vol_quit) then
19250 L(K) = L_min ; Vol_err = Vol_err_min
1926 else
1927 L(K) = sqrt((L_min**2*Vol_err_max - L_max**2*Vol_err_min) / &
19282142805 (Vol_err_max - Vol_err_min))
19292807612 do itt=1,maxitt
19302807612 Vol_err = 0.5*(L(K)*L(K))*(slope + crv_3*(3.0-4.0*L(K))) - vol_below(K)
19312807612 if (abs(Vol_err) <= Vol_quit) exit
1932 ! Take a Newton's method iteration. This equation has proven
1933 ! robust enough not to need bracketing.
1934664807 L(K) = L(K) - Vol_err / (L(K)* (slope + crv - 2.0*crv*L(K)))
1935 ! This would be a Newton's method iteration for L^2:
1936 ! L(K) = sqrt(L(K)*L(K) - Vol_err / (0.5*(slope+crv) - crv*L(K)))
1937 enddo
1938 endif ! end of iterative solver
1939 endif ! end of 1-boundary alternatives.
1940 endif ! end of 0, 1- and 2- boundary cases.
1941 enddo ! k loop to determine L(K) in the convex case
1942
1943105672end subroutine find_L_open_convex
1944
1945!> This subroutine finds a thickness-weighted value of v at the u-points.
19462867989pure function set_v_at_u(v, h, G, GV, i, j, k, mask2dCv, OBC)
1947 !$omp declare target
1948 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
1949 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
1950 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), &
1951 intent(in) :: v !< The meridional velocity [L T-1 ~> m s-1]
1952 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
1953 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
1954 integer, intent(in) :: i !< The i-index of the u-location to work on.
1955 integer, intent(in) :: j !< The j-index of the u-location to work on.
1956 integer, intent(in) :: k !< The k-index of the u-location to work on.
1957 real, dimension(SZI_(G),SZJB_(G)),&
1958 intent(in) :: mask2dCv !< A multiplicative mask of the v-points [nondim]
1959 type(ocean_OBC_type), pointer :: OBC !< A pointer to an open boundary condition structure
1960 real :: set_v_at_u !< The return value of v at u points points in the
1961 !! same units as u, i.e. [L T-1 ~> m s-1] or other units.
1962
1963 ! This subroutine finds a thickness-weighted value of v at the u-points.
1964 real :: hwt(0:1,-1:0) ! Masked weights used to average u onto v [H ~> m or kg m-2].
1965 real :: hwt_tot ! The sum of the masked thicknesses [H ~> m or kg m-2].
1966 integer :: i0, j0, i1, j1
1967
196820075923 do j0 = -1,0 ; do i0 = 0,1 ; i1 = i+i0 ; J1 = J+j0
196917207934 hwt(i0,j0) = (h(i1,j1,k) + h(i1,j1+1,k)) * mask2dCv(i1,J1)
1970 enddo ; enddo
1971
19722867989 if (associated(OBC)) then ; if (OBC%number_of_segments > 0) then
19730 do j0 = -1,0 ; do i0 = 0,1 ; if (OBC%segnum_v(i+i0,J+j0) /= 0) then
19740 i1 = i+i0 ; J1 = J+j0
19750 if (OBC%segnum_v(i1,j1) > 0) then ! OBC_DIRECTION_N
19760 hwt(i0,j0) = 2.0 * h(i1,j1,k) * mask2dCv(i1,J1)
19770 elseif (OBC%segnum_v(i1,J1) < 0) then ! OBC_DIRECTION_S
19780 hwt(i0,j0) = 2.0 * h(i1,J1+1,k) * mask2dCv(i1,J1)
1979 endif
1980 endif ; enddo ; enddo
1981 endif ; endif
1982
19832867989 hwt_tot = (hwt(0,-1) + hwt(1,0)) + (hwt(1,-1) + hwt(0,0))
19842867989 set_v_at_u = 0.0
19852867989 if (hwt_tot > 0.0) set_v_at_u = &
1986 (((hwt(0,0) * v(i,J,k)) + (hwt(1,-1) * v(i+1,J-1,k))) + &
19872867989 ((hwt(1,0) * v(i+1,J,k)) + (hwt(0,-1) * v(i,J-1,k)))) / hwt_tot
1988
19892867989end function set_v_at_u
1990
1991!> This subroutine finds a thickness-weighted value of u at the v-points.
19922803353pure function set_u_at_v(u, h, G, GV, i, j, k, mask2dCu, OBC)
1993 !$omp declare target
1994 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
1995 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
1996 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), &
1997 intent(in) :: u !< The zonal velocity [L T-1 ~> m s-1] or other units.
1998 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
1999 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
2000 integer, intent(in) :: i !< The i-index of the u-location to work on.
2001 integer, intent(in) :: j !< The j-index of the u-location to work on.
2002 integer, intent(in) :: k !< The k-index of the u-location to work on.
2003 real, dimension(SZIB_(G),SZJ_(G)), &
2004 intent(in) :: mask2dCu !< A multiplicative mask of the u-points [nondim]
2005 type(ocean_OBC_type), pointer :: OBC !< A pointer to an open boundary condition structure
2006 real :: set_u_at_v !< The return value of u at v points in the
2007 !! same units as u, i.e. [L T-1 ~> m s-1] or other units.
2008
2009 ! This subroutine finds a thickness-weighted value of u at the v-points.
2010 real :: hwt(-1:0,0:1) ! Masked weights used to average u onto v [H ~> m or kg m-2].
2011 real :: hwt_tot ! The sum of the masked thicknesses [H ~> m or kg m-2].
2012 integer :: i0, j0, i1, j1
2013
201419623471 do j0 = 0,1 ; do i0 = -1,0 ; I1 = I+i0 ; j1 = j+j0
201516820118 hwt(i0,j0) = (h(i1,j1,k) + h(i1+1,j1,k)) * mask2dCu(I1,j1)
2016 enddo ; enddo
2017
20182803353 if (associated(OBC)) then ; if (OBC%number_of_segments > 0) then
20190 do j0 = 0,1 ; do i0 = -1,0 ; if ((OBC%segnum_u(I+i0,j+j0) /= 0)) then
20200 I1 = I+i0 ; j1 = j+j0
20210 if (OBC%segnum_u(I1,j1) > 0) then ! OBC_DIRECTION_E
20220 hwt(i0,j0) = 2.0 * h(I1,j1,k) * mask2dCu(I1,j1)
20230 elseif (OBC%segnum_u(I1,j1) < 0) then ! OBC_DIRECTION_W
20240 hwt(i0,j0) = 2.0 * h(I1+1,j1,k) * mask2dCu(I1,j1)
2025 endif
2026 endif ; enddo ; enddo
2027 endif ; endif
2028
20292803353 hwt_tot = (hwt(-1,0) + hwt(0,1)) + (hwt(0,0) + hwt(-1,1))
20302803353 set_u_at_v = 0.0
20312803353 if (hwt_tot > 0.0) set_u_at_v = &
2032 (((hwt(0,0) * u(I,j,k)) + (hwt(-1,1) * u(I-1,j+1,k))) + &
20332803353 ((hwt(-1,0) * u(I-1,j,k)) + (hwt(0,1) * u(I,j+1,k)))) / hwt_tot
2034
20352803353end function set_u_at_v
2036
2037!> Calculates the thickness of the surface boundary layer for applying an elevated viscosity.
2038!!
2039!! A bulk Richardson criterion or the thickness of the topmost NKML layers (with a bulk mixed layer)
2040!! are currently used. The thicknesses are given in terms of fractional layers, so that this
2041!! thickness will move as the thickness of the topmost layers change.
204224subroutine set_viscous_ML(u, v, h, tv, forces, visc, dt, G, GV, US, CS)
2043 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
2044 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
2045 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
2046 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), &
2047 intent(in) :: u !< The zonal velocity [L T-1 ~> m s-1].
2048 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), &
2049 intent(in) :: v !< The meridional velocity [L T-1 ~> m s-1].
2050 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
2051 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2].
2052 type(thermo_var_ptrs), intent(in) :: tv !< A structure containing pointers to any available
2053 !! thermodynamic fields. Absent fields have
2054 !! NULL pointers.
2055 type(mech_forcing), intent(in) :: forces !< A structure with the driving mechanical forces
2056 type(vertvisc_type), intent(inout) :: visc !< A structure containing vertical viscosities and
2057 !! related fields.
2058 real, intent(in) :: dt !< Time increment [T ~> s].
2059 type(set_visc_CS), intent(inout) :: CS !< The control structure returned by a previous
2060 !! call to set_visc_init.
2061
2062 ! Local variables
2063 real, dimension(SZIB_(G)) :: &
206448 htot, & ! The total thickness of the layers that are within the
2065 ! surface mixed layer [H ~> m or kg m-2].
206648 dztot, & ! The distance from the surface to the bottom of the layers that are
2067 ! within the surface mixed layer [Z ~> m]
206848 Thtot, & ! The integrated temperature of layers that are within the
2069 ! surface mixed layer [H C ~> m degC or kg degC m-2].
207048 Shtot, & ! The integrated salt of layers that are within the
2071 ! surface mixed layer [H S ~> m ppt or kg ppt m-2].
207248 SpV_htot, & ! Running sum of thickness times specific volume [H R-1 ~> m4 kg-1 or m]
207348 Rhtot, & ! The integrated density of layers that are within the surface mixed layer
2074 ! [H R ~> kg m-2 or kg2 m-5]. Rhtot is only used if no
2075 ! equation of state is used.
207648 uhtot, & ! The depth integrated zonal velocity within the surface
2077 ! mixed layer [H L T-1 ~> m2 s-1 or kg m-1 s-1].
207848 vhtot, & ! The depth integrated meridional velocity within the surface
2079 ! mixed layer [H L T-1 ~> m2 s-1 or kg m-1 s-1].
208048 Idecay_len_TKE, & ! The inverse of a turbulence decay length scale [H-1 ~> m-1 or m2 kg-1].
208148 dR_dT, & ! Partial derivative of the density at the base of layer nkml
2082 ! (roughly the base of the mixed layer) with temperature [R C-1 ~> kg m-3 degC-1].
208348 dR_dS, & ! Partial derivative of the density at the base of layer nkml
2084 ! (roughly the base of the mixed layer) with salinity [R S-1 ~> kg m-3 ppt-1].
208548 dSpV_dT, & ! Partial derivative of the specific volume at the base of layer nkml
2086 ! (roughly the base of the mixed layer) with temperature [R-1 C-1 ~> m3 kg-1 degC-1].
208748 dSpV_dS, & ! Partial derivative of the specific volume at the base of layer nkml
2088 ! (roughly the base of the mixed layer) with salinity [R-1 S-1 ~> m3 kg-1 ppt-1].
208948 ustar, & ! The surface friction velocity under ice shelves [H T-1 ~> m s-1 or kg m-2 s-1].
209048 press, & ! The pressure at which dR_dT and dR_dS are evaluated [R L2 T-2 ~> Pa].
209148 T_EOS, & ! The potential temperature at which dR_dT and dR_dS are evaluated [C ~> degC]
209248 S_EOS ! The salinity at which dR_dT and dR_dS are evaluated [S ~> ppt].
209348 real :: dz(SZI_(G),SZJ_(G),SZK_(GV)) ! Height change across layers [Z ~> m]
2094 real, dimension(SZIB_(G),SZJ_(G)) :: &
209548 mask_u ! A mask that disables any contributions from u points that
2096 ! are land or past open boundary conditions [nondim], 0 or 1.
2097 real, dimension(SZI_(G),SZJB_(G)) :: &
209848 mask_v ! A mask that disables any contributions from v points that
2099 ! are land or past open boundary conditions [nondim], 0 or 1.
210048 real :: U_star_2d(SZI_(G),SZJ_(G)) ! The wind friction velocity in thickness-based units,
2101 ! calculated using the Boussinesq reference density or the time-evolving
2102 ! surface density in non-Boussinesq mode [H T-1 ~> m s-1 or kg m-2 s-1]
210348 real :: h_at_vel(SZIB_(G),SZK_(GV))! Layer thickness at velocity points,
2104 ! using an upwind-biased second order accurate estimate based
2105 ! on the previous velocity direction [H ~> m or kg m-2].
210648 real :: dz_at_vel(SZIB_(G),SZK_(GV)) ! Vertical extent of a layer at velocity points,
2107 ! using an upwind-biased second order accurate estimate based
2108 ! on the previous velocity direction [Z ~> m].
210948 integer :: k_massive(SZIB_(G)) ! The k-index of the deepest layer yet found
2110 ! that has more than h_tiny thickness and will be in the
2111 ! viscous mixed layer.
2112 real :: Uh2 ! The squared magnitude of the difference between the velocity
2113 ! integrated through the mixed layer and the velocity of the
2114 ! interior layer layer times the depth of the mixed layer
2115 ! [H2 L2 T-2 ~> m4 s-2 or kg2 m-2 s-2].
2116 real :: htot_vel ! Sum of the layer thicknesses up to some point [H ~> m or kg m-2].
2117 real :: hwtot ! Sum of the thicknesses used to calculate
2118 ! the near-bottom velocity magnitude [H ~> m or kg m-2].
2119 real :: hutot ! Running sum of thicknesses times the velocity
2120 ! magnitudes [H L T-1 ~> m2 s-1 or kg m-1 s-1].
2121 real :: hweight ! The thickness of a layer that is within Hbbl
2122 ! of the bottom [H ~> m or kg m-2].
2123 real :: tbl_thick ! The thickness of the top boundary layer [Z ~> m].
2124
2125 real :: hlay ! The layer thickness at velocity points [H ~> m or kg m-2].
2126 real :: I_2hlay ! 1 / 2*hlay [H-1 ~> m-1 or m2 kg-1].
2127 real :: T_lay ! The layer temperature at velocity points [C ~> degC].
2128 real :: S_lay ! The layer salinity at velocity points [S ~> ppt].
2129 real :: Rlay ! The layer potential density at velocity points [R ~> kg m-3].
2130 real :: Rlb ! The potential density of the layer below [R ~> kg m-3].
2131 real :: v_at_u ! The meridional velocity at a zonal velocity point [L T-1 ~> m s-1].
2132 real :: u_at_v ! The zonal velocity at a meridional velocity point [L T-1 ~> m s-1].
2133 real :: gHprime ! The mixed-layer internal gravity wave speed squared, based
2134 ! on the mixed layer thickness and density difference across
2135 ! the base of the mixed layer [L2 T-2 ~> m2 s-2].
2136 real :: RiBulk ! The bulk Richardson number below which water is in the
2137 ! viscous mixed layer, including reduction for turbulent decay [nondim]
2138 real :: dt_Rho0 ! The time step divided by the conversion from the layer
2139 ! thickness to layer mass [T H Z-1 R-1 ~> s m3 kg-1 or s].
2140 real :: g_H_Rho0 ! The gravitational acceleration times the conversion from H to m divided
2141 ! by the mean density [L2 T-2 H-1 R-1 ~> m4 s-2 kg-1 or m7 s-2 kg-2].
2142 real :: ustarsq ! 400 times the square of ustar, times
2143 ! Rho0 divided by G_Earth and the conversion
2144 ! from m to thickness units [H R ~> kg m-2 or kg2 m-5].
2145 real :: cdrag_sqrt ! Square root of the drag coefficient [nondim].
2146 real :: cdrag_sqrt_H ! Square root of the drag coefficient, times a unit conversion
2147 ! factor from lateral lengths to layer thicknesses [H L-1 ~> nondim or kg m-3].
2148 real :: cdrag_sqrt_H_RL ! Square root of the drag coefficient, times a unit conversion factor from
2149 ! density times lateral lengths to layer thicknesses [H L-1 R-1 ~> m3 kg-1 or nondim]
2150 real :: oldfn ! The integrated energy required to
2151 ! entrain up to the bottom of the layer,
2152 ! divided by G_Earth [H R ~> kg m-2 or kg2 m-5].
2153 real :: Dfn ! The increment in oldfn for entraining
2154 ! the layer [H R ~> kg m-2 or kg2 m-5].
2155 real :: frac_used ! The fraction of the present layer that contributes to Dh and Ddz [nondim]
2156 real :: Dh ! The increment in layer thickness from the present layer [H ~> m or kg m-2].
2157 real :: Ddz ! The increment in height change from the present layer [Z ~> m].
215848 real :: u2_bg(SZIB_(G)) ! The square of an assumed background velocity, for
2159 ! calculating the mean magnitude near the top for use in
2160 ! the quadratic surface drag [L2 T-2 ~> m2 s-2].
2161 real :: h_tiny ! A very small thickness [H ~> m or kg m-2]. Layers that are less than
2162 ! h_tiny can not be the deepest in the viscous mixed layer.
2163 real :: absf ! The absolute value of f averaged to velocity points [T-1 ~> s-1].
2164 real :: U_star ! The friction velocity at velocity points [H T-1 ~> m s-1 or kg m-2 s-1].
2165 real :: h_neglect ! A thickness that is so small it is usually lost
2166 ! in roundoff and can be neglected [H ~> m or kg m-2].
2167 real :: dz_neglect ! A vertical distance that is so small it is usually lost
2168 ! in roundoff and can be neglected [Z ~> m].
2169 real :: Rho0x400_G ! 400*Rho0/G_Earth, times unit conversion factors
2170 ! [R T2 H-1 ~> kg s2 m-4 or s2 m-1].
2171 ! The 400 is a constant proposed by Killworth and Edwards, 1999.
2172 real :: ustar1 ! ustar [H T-1 ~> m s-1 or kg m-2 s-1]
2173 real :: h2f2 ! (h*2*f)^2 [H2 T-2 ~> m2 s-2 or kg2 m-4 s-2]
217424 logical :: use_EOS, do_any, do_any_shelf, do_i(SZIB_(G))
2175 logical :: nonBous_ML ! If true, use the non-Boussinesq form of some energy and
2176 ! stratification calculations.
2177 integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, K2, nkmb, nkml, n
2178 type(ocean_OBC_type), pointer :: OBC => NULL()
2179
218024 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
218124 Isq = G%isc-1 ; Ieq = G%IecB ; Jsq = G%jsc-1 ; Jeq = G%JecB
218224 nkmb = GV%nk_rho_varies ; nkml = GV%nkml
2183
218424 if (.not.CS%initialized) call MOM_error(FATAL,"MOM_set_viscosity(visc_ML): "//&
21850 "Module must be initialized before it is used.")
2186
2187 ! TODO: Remove this check and move it outside of the function call.
218824 if (.not.(CS%dynamic_viscous_ML .or. associated(forces%frac_shelf_u) .or. &
218924 associated(forces%frac_shelf_v)) ) return
2190
2191 ! NOTE: Requried since this is called by the GPU-enabled dycore, but it could
2192 ! also be implicitly fixing other functions.
2193 !$omp target update from(u, v)
2194
21950 Rho0x400_G = 400.0*(GV%H_to_RZ / GV%g_Earth_Z_T2)
21960 cdrag_sqrt = sqrt(CS%cdrag)
21970 cdrag_sqrt_H = cdrag_sqrt * US%L_to_m * GV%m_to_H
21980 cdrag_sqrt_H_RL = cdrag_sqrt * US%L_to_Z * GV%RZ_to_H
2199
22000 OBC => CS%OBC
22010 use_EOS = associated(tv%eqn_of_state)
22020 nonBous_ML = allocated(tv%SpV_avg)
22030 dt_Rho0 = dt / GV%H_to_RZ
22040 h_neglect = GV%H_subroundoff
22050 h_tiny = 2.0*GV%Angstrom_H + h_neglect
22060 dz_neglect = GV%dZ_subroundoff
22070 g_H_Rho0 = (GV%g_Earth*GV%H_to_Z) / (GV%Rho0)
2208
22090 if (associated(forces%frac_shelf_u) .neqv. associated(forces%frac_shelf_v)) &
2210 call MOM_error(FATAL, "set_viscous_ML: one of forces%frac_shelf_u and "//&
22110 "forces%frac_shelf_v is associated, but the other is not.")
2212
2213 ! Extract the friction velocity from the forcing type.
22140 call find_ustar(forces, tv, U_star_2d, G, GV, US, halo=1, H_T_units=.true.)
2215
22160 if (associated(forces%frac_shelf_u)) then
2217 ! This configuration has ice shelves, and the appropriate variables need to be
2218 ! allocated. If the arrays have already been allocated, these calls do nothing.
22190 if (.not.allocated(visc%taux_shelf)) &
22200 allocate(visc%taux_shelf(G%IsdB:G%IedB, G%jsd:G%jed), source=0.0)
22210 if (.not.allocated(visc%tauy_shelf)) &
22220 allocate(visc%tauy_shelf(G%isd:G%ied, G%JsdB:G%JedB), source=0.0)
22230 if (.not.allocated(visc%tbl_thick_shelf_u)) &
22240 allocate(visc%tbl_thick_shelf_u(G%IsdB:G%IedB, G%jsd:G%jed), source=0.0)
22250 if (.not.allocated(visc%tbl_thick_shelf_v)) &
22260 allocate(visc%tbl_thick_shelf_v(G%isd:G%ied, G%JsdB:G%JedB), source=0.0)
22270 if (.not.allocated(visc%kv_tbl_shelf_u)) &
22280 allocate(visc%kv_tbl_shelf_u(G%IsdB:G%IedB, G%jsd:G%jed), source=0.0)
22290 if (.not.allocated(visc%kv_tbl_shelf_v)) &
22300 allocate(visc%kv_tbl_shelf_v(G%isd:G%ied, G%JsdB:G%JedB), source=0.0)
2231
2232 ! With a linear drag law under shelves, the friction velocity is already known.
2233! if (CS%linear_drag) ustar(:) = cdrag_sqrt_H*CS%drag_bg_vel
2234
2235 ! Find the vertical distances across layers.
22360 call thickness_to_dz(h, tv, dz, G, GV, US, halo_size=1)
2237 endif
2238
2239 !$OMP parallel do default(shared)
22400 do J=js-1,je ; do i=is-1,ie+1
22410 mask_v(i,J) = G%mask2dCv(i,J)
2242 enddo ; enddo
2243 !$OMP parallel do default(shared)
22440 do j=js-1,je+1 ; do I=is-1,ie
22450 mask_u(I,j) = G%mask2dCu(I,j)
2246 enddo ; enddo
2247
22480 if (associated(OBC)) then ; do n=1,OBC%number_of_segments
2249 ! Project bottom depths across cell-corner points in the OBCs.
22500 if (.not. OBC%segment(n)%on_pe) cycle
2251 ! Use a one-sided projection of bottom depths at OBC points.
22520 I = OBC%segment(n)%HI%IsdB ; J = OBC%segment(n)%HI%JsdB
22530 if (OBC%segment(n)%is_N_or_S .and. (J >= js-1) .and. (J <= je)) then
22540 do I = max(is-1,OBC%segment(n)%HI%IsdB), min(ie,OBC%segment(n)%HI%IedB)
22550 if (OBC%segment(n)%direction == OBC_DIRECTION_N) mask_u(I,j+1) = 0.0
22560 if (OBC%segment(n)%direction == OBC_DIRECTION_S) mask_u(I,j) = 0.0
2257 enddo
22580 elseif (OBC%segment(n)%is_E_or_W .and. (I >= is-1) .and. (I <= ie)) then
22590 do J = max(js-1,OBC%segment(n)%HI%JsdB), min(je,OBC%segment(n)%HI%JedB)
22600 if (OBC%segment(n)%direction == OBC_DIRECTION_E) mask_v(i+1,J) = 0.0
22610 if (OBC%segment(n)%direction == OBC_DIRECTION_W) mask_v(i,J) = 0.0
2262 enddo
2263 endif
2264 enddo ; endif
2265
2266 !$OMP parallel do default(private) shared(u,v,h,dz,tv,forces,visc,dt,G,GV,US,CS,use_EOS,dt_Rho0, &
2267 !$OMP nonBous_ML,h_neglect,dz_neglect,h_tiny,g_H_Rho0, &
2268 !$OMP js,je,OBC,Isq,Ieq,nz,nkml,U_star_2d,mask_v, &
2269 !$OMP cdrag_sqrt,cdrag_sqrt_H,cdrag_sqrt_H_RL,Rho0x400_G)
22700 do j=js,je ! u-point loop
22710 if (CS%dynamic_viscous_ML) then
22720 do_any = .false.
22730 do I=Isq,Ieq
22740 htot(I) = 0.0
22750 if (G%mask2dCu(I,j) < 0.5) then
22760 do_i(I) = .false. ; visc%nkml_visc_u(I,j) = nkml
2277 else
22780 do_i(I) = .true. ; do_any = .true.
22790 k_massive(I) = nkml
22800 Thtot(I) = 0.0 ; Shtot(I) = 0.0 ; Rhtot(i) = 0.0
22810 uhtot(I) = dt_Rho0 * forces%taux(I,j)
2282 vhtot(I) = 0.25 * dt_Rho0 * ((forces%tauy(i,J) + forces%tauy(i+1,J-1)) + &
22830 (forces%tauy(i,J-1) + forces%tauy(i+1,J)))
2284
22850 if (CS%omega_frac >= 1.0) then ; absf = 2.0*CS%omega ; else
22860 absf = 0.5*(abs(G%CoriolisBu(I,J)) + abs(G%CoriolisBu(I,J-1)))
22870 if (CS%omega_frac > 0.0) &
22880 absf = sqrt(CS%omega_frac*4.0*CS%omega**2 + (1.0-CS%omega_frac)*absf**2)
2289 endif
22900 U_star = max(CS%ustar_min, 0.5*(U_star_2d(i,j) + U_star_2d(i+1,j)))
22910 Idecay_len_TKE(I) = (absf / U_star) * CS%TKE_decay
2292 endif
2293 enddo
2294
22950 if (do_any) then ; do k=1,nz
22960 if (k > nkml) then
22970 do_any = .false.
22980 if (use_EOS .and. (k==nkml+1)) then
2299 ! Find dRho/dT and dRho_dS.
23000 do I=Isq,Ieq
23010 press(I) = (GV%H_to_RZ*GV%g_Earth) * htot(I)
23020 if (associated(tv%p_surf)) press(I) = press(I) + 0.5*(tv%p_surf(i,j)+tv%p_surf(i+1,j))
23030 k2 = max(1,nkml)
23040 I_2hlay = 1.0 / (h(i,j,k2) + h(i+1,j,k2) + h_neglect)
23050 T_EOS(I) = ((h(i,j,k2)*tv%T(i,j,k2)) + (h(i+1,j,k2)*tv%T(i+1,j,k2))) * I_2hlay
23060 S_EOS(I) = ((h(i,j,k2)*tv%S(i,j,k2)) + (h(i+1,j,k2)*tv%S(i+1,j,k2))) * I_2hlay
2307 enddo
2308 call calculate_density_derivs(T_EOS, S_EOS, press, dR_dT, dR_dS, tv%eqn_of_state, &
23090 (/Isq-G%IsdB+1,Ieq-G%IsdB+1/) )
23100 if (nonBous_ML) then
2311 call calculate_specific_vol_derivs(T_EOS, S_EOS, press, dSpV_dT, dSpV_dS, tv%eqn_of_state, &
23120 (/Isq-G%IsdB+1,Ieq-G%IsdB+1/) )
2313 endif
2314 endif
2315
23160 do I=Isq,Ieq ; if (do_i(I)) then
2317
23180 hlay = 0.5*(h(i,j,k) + h(i+1,j,k))
23190 if (hlay > h_tiny) then ! Only consider non-vanished layers.
23200 I_2hlay = 1.0 / (h(i,j,k) + h(i+1,j,k))
2321 v_at_u = 0.5 * ((h(i,j,k) * (v(i,J,k) + v(i,J-1,k))) + &
23220 (h(i+1,j,k) * (v(i+1,J,k) + v(i+1,J-1,k)))) * I_2hlay
23230 Uh2 = (uhtot(I) - htot(I)*u(I,j,k))**2 + (vhtot(I) - htot(I)*v_at_u)**2
2324
23250 if (use_EOS) then
23260 T_lay = ((h(i,j,k)*tv%T(i,j,k)) + (h(i+1,j,k)*tv%T(i+1,j,k))) * I_2hlay
23270 S_lay = ((h(i,j,k)*tv%S(i,j,k)) + (h(i+1,j,k)*tv%S(i+1,j,k))) * I_2hlay
23280 if (nonBous_ML) then
2329 gHprime = (GV%g_Earth * GV%H_to_RZ) * (dSpV_dT(I) * (Thtot(I) - T_lay*htot(I)) + &
23300 dSpV_dS(I) * (Shtot(I) - S_lay*htot(I)))
2331 else
2332 gHprime = g_H_Rho0 * (dR_dT(I) * (T_lay*htot(I) - Thtot(I)) + &
23330 dR_dS(I) * (S_lay*htot(I) - Shtot(I)))
2334 endif
2335 else
23360 gHprime = g_H_Rho0 * (GV%Rlay(k)*htot(I) - Rhtot(I))
2337 endif
2338
23390 if (gHprime > 0.0) then
23400 RiBulk = CS%bulk_Ri_ML * exp(-htot(I) * Idecay_len_TKE(I))
23410 if (RiBulk * Uh2 <= (htot(I)**2) * gHprime) then
23420 visc%nkml_visc_u(I,j) = real(k_massive(I))
23430 do_i(I) = .false.
23440 elseif (RiBulk * Uh2 <= (htot(I) + hlay)**2 * gHprime) then
2345 visc%nkml_visc_u(I,j) = real(k-1) + &
23460 ( sqrt(RiBulk * Uh2 / gHprime) - htot(I) ) / hlay
23470 do_i(I) = .false.
2348 endif
2349 endif
23500 k_massive(I) = k
2351 endif ! hlay > h_tiny
2352
23530 if (do_i(I)) do_any = .true.
2354 endif ; enddo
2355
23560 if (.not.do_any) exit ! All columns are done.
2357 endif
2358
23590 do I=Isq,Ieq ; if (do_i(I)) then
23600 htot(I) = htot(I) + 0.5 * (h(i,j,k) + h(i+1,j,k))
23610 uhtot(I) = uhtot(I) + 0.5 * (h(i,j,k) + h(i+1,j,k)) * u(I,j,k)
2362 vhtot(I) = vhtot(I) + 0.25 * ((h(i,j,k) * (v(i,J,k) + v(i,J-1,k))) + &
23630 (h(i+1,j,k) * (v(i+1,J,k) + v(i+1,J-1,k))))
23640 if (use_EOS) then
23650 Thtot(I) = Thtot(I) + 0.5 * ((h(i,j,k)*tv%T(i,j,k)) + (h(i+1,j,k)*tv%T(i+1,j,k)))
23660 Shtot(I) = Shtot(I) + 0.5 * ((h(i,j,k)*tv%S(i,j,k)) + (h(i+1,j,k)*tv%S(i+1,j,k)))
2367 else
23680 Rhtot(i) = Rhtot(i) + 0.5 * (h(i,j,k) + h(i+1,j,k)) * GV%Rlay(k)
2369 endif
2370 endif ; enddo
2371 enddo ; endif
2372
23730 if (do_any) then ; do I=Isq,Ieq ; if (do_i(I)) then
23740 visc%nkml_visc_u(I,j) = k_massive(I)
2375 endif ; enddo ; endif
2376 endif ! dynamic_viscous_ML
2377
23780 do_any_shelf = .false.
23790 if (associated(forces%frac_shelf_u)) then
23800 do I=Isq,Ieq
23810 if (forces%frac_shelf_u(I,j)*G%mask2dCu(I,j) == 0.0) then
23820 do_i(I) = .false.
23830 visc%tbl_thick_shelf_u(I,j) = 0.0 ; visc%kv_tbl_shelf_u(I,j) = 0.0
2384 else
23850 do_i(I) = .true. ; do_any_shelf = .true.
2386 endif
2387 enddo
2388 endif
2389
23900 if (do_any_shelf) then
23910 do k=1,nz ; do I=Isq,Ieq ; if (do_i(I)) then
23920 if (u(I,j,k) * (h(i+1,j,k) - h(i,j,k)) >= 0) then
2393 h_at_vel(i,k) = 2.0*h(i,j,k)*h(i+1,j,k) / &
23940 (h(i,j,k) + h(i+1,j,k) + h_neglect)
2395 dz_at_vel(i,k) = 2.0*dz(i,j,k)*dz(i+1,j,k) / &
23960 (dz(i,j,k) + dz(i+1,j,k) + dz_neglect)
2397 else
23980 h_at_vel(i,k) = 0.5 * (h(i,j,k) + h(i+1,j,k))
23990 dz_at_vel(i,k) = 0.5 * (dz(i,j,k) + dz(i+1,j,k))
2400 endif
2401 else
24020 h_at_vel(I,k) = 0.0
24030 dz_at_vel(I,k) = 0.0
24040 ustar(I) = 0.0
2405 endif ; enddo ; enddo
2406
24070 do I=Isq,Ieq ; if (do_i(I)) then
24080 htot_vel = 0.0 ; hwtot = 0.0 ; hutot = 0.0
24090 Thtot(I) = 0.0 ; Shtot(I) = 0.0 ; SpV_htot(I) = 0.0
24100 if (use_EOS .or. .not.CS%linear_drag) then ; do k=1,nz
24110 if (htot_vel>=CS%Htbl_shelf) exit ! terminate the k loop
24120 hweight = MIN(CS%Htbl_shelf - htot_vel, h_at_vel(i,k))
24130 if (hweight <= 1.5*GV%Angstrom_H + h_neglect) cycle
2414
24150 htot_vel = htot_vel + h_at_vel(i,k)
24160 hwtot = hwtot + hweight
2417
24180 if (.not.CS%linear_drag) then
24190 v_at_u = set_v_at_u(v, h, G, GV, i, j, k, mask_v, OBC)
2420 ! Set the "back ground" friction velocity scale to either the tidal amplitude or place-holder constant
24210 if (CS%BBL_use_tidal_bg) then
2422 u2_bg(I) = 0.5*( G%mask2dT(i,j)*(CS%tideamp(i,j)*CS%tideamp(i,j))+ &
24230 G%mask2dT(i+1,j)*(CS%tideamp(i+1,j)*CS%tideamp(i+1,j)) )
2424 else
24250 u2_bg(I) = CS%drag_bg_vel * CS%drag_bg_vel
2426 endif
24270 hutot = hutot + hweight * sqrt(u(I,j,k)**2 + v_at_u**2 + u2_bg(I))
2428 endif
24290 if (use_EOS) then
24300 Thtot(I) = Thtot(I) + hweight * 0.5 * (tv%T(i,j,k) + tv%T(i+1,j,k))
24310 Shtot(I) = Shtot(I) + hweight * 0.5 * (tv%S(i,j,k) + tv%S(i+1,j,k))
2432 endif
24330 if (allocated(tv%SpV_avg)) then
24340 SpV_htot(I) = SpV_htot(I) + hweight * 0.5 * (tv%SpV_avg(i,j,k) + tv%SpV_avg(i+1,j,k))
2435 endif
2436 enddo ; endif
2437
24380 if ((hwtot <= 0.0) .or. (CS%linear_drag .and. .not.allocated(tv%SpV_avg))) then
24390 ustar(I) = cdrag_sqrt_H * CS%drag_bg_vel
24400 elseif (CS%linear_drag .and. allocated(tv%SpV_avg)) then
24410 ustar(I) = cdrag_sqrt_H_RL * CS%drag_bg_vel * (hwtot / SpV_htot(I))
24420 elseif (allocated(tv%SpV_avg)) then ! (.not.CS%linear_drag)
24430 ustar(I) = cdrag_sqrt_H_RL * hutot / SpV_htot(I)
2444 else ! (.not.CS%linear_drag .and. .not.allocated(tv%SpV_avg))
24450 ustar(I) = cdrag_sqrt_H * hutot / hwtot
2446 endif
2447
24480 if (use_EOS) then ; if (hwtot > 0.0) then
24490 T_EOS(I) = Thtot(I)/hwtot ; S_EOS(I) = Shtot(I)/hwtot
2450 else
24510 T_EOS(I) = 0.0 ; S_EOS(I) = 0.0
2452 endif ; endif
2453 ! if (allocated(tv%SpV_avg)) SpV_av(I) = SpVhtot(I) / hwtot
2454 endif ; enddo ! I-loop
2455
24560 if (use_EOS) then
2457 call calculate_density_derivs(T_EOS, S_EOS, forces%p_surf(:,j), dR_dT, dR_dS, &
24580 tv%eqn_of_state, (/Isq-G%IsdB+1,Ieq-G%IsdB+1/) )
2459 endif
2460
24610 do I=Isq,Ieq ; if (do_i(I)) then
2462 ! The 400.0 in this expression is the square of a constant proposed
2463 ! by Killworth and Edwards, 1999, in equation (2.20).
24640 ustarsq = Rho0x400_G * ustar(i)**2
24650 htot(i) = 0.0 ; dztot(i) = 0.0
24660 if (use_EOS) then
24670 Thtot(i) = 0.0 ; Shtot(i) = 0.0 ; oldfn = 0.0
24680 do k=1,nz-1
24690 if (h_at_vel(i,k) <= 0.0) cycle
24700 T_Lay = 0.5 * (tv%T(i,j,k) + tv%T(i+1,j,k))
24710 S_Lay = 0.5 * (tv%S(i,j,k) + tv%S(i+1,j,k))
24720 oldfn = dR_dT(i)*(T_Lay*htot(i) - Thtot(i)) + dR_dS(i)*(S_Lay*htot(i) - Shtot(i))
24730 if (oldfn >= ustarsq) exit
2474
2475 Dfn = (dR_dT(i)*(0.5*(tv%T(i,j,k+1)+tv%T(i+1,j,k+1)) - T_Lay) + &
2476 dR_dS(i)*(0.5*(tv%S(i,j,k+1)+tv%S(i+1,j,k+1)) - S_Lay)) * &
24770 (h_at_vel(i,k)+htot(i))
24780 if ((oldfn + Dfn) <= ustarsq) then
24790 Dh = h_at_vel(i,k)
24800 Ddz = dz_at_vel(i,k)
2481 else
24820 frac_used = sqrt((ustarsq-oldfn) / (Dfn))
24830 Dh = h_at_vel(i,k) * frac_used
24840 Ddz = dz_at_vel(i,k) * frac_used
2485 endif
2486
24870 htot(i) = htot(i) + Dh
24880 dztot(i) = dztot(i) + Ddz
24890 Thtot(i) = Thtot(i) + T_Lay*Dh ; Shtot(i) = Shtot(i) + S_Lay*Dh
2490 enddo
24910 if ((oldfn < ustarsq) .and. (h_at_vel(i,nz) > 0.0)) then
24920 T_Lay = 0.5*(tv%T(i,j,nz) + tv%T(i+1,j,nz))
24930 S_Lay = 0.5*(tv%S(i,j,nz) + tv%S(i+1,j,nz))
24940 if (dR_dT(i)*(T_Lay*htot(i) - Thtot(i)) + &
2495 dR_dS(i)*(S_Lay*htot(i) - Shtot(i)) < ustarsq) then
24960 htot(i) = htot(i) + h_at_vel(i,nz)
24970 dztot(i) = dztot(i) + dz_at_vel(i,nz)
2498 endif
2499 endif ! Examination of layer nz.
2500 else ! Use Rlay as the density variable.
25010 Rhtot = 0.0
25020 do k=1,nz-1
25030 Rlay = GV%Rlay(k) ; Rlb = GV%Rlay(k+1)
2504
25050 oldfn = Rlay*htot(i) - Rhtot(i)
25060 if (oldfn >= ustarsq) exit
2507
25080 Dfn = (Rlb - Rlay)*(h_at_vel(i,k)+htot(i))
25090 if ((oldfn + Dfn) <= ustarsq) then
25100 Dh = h_at_vel(i,k)
25110 Ddz = dz_at_vel(i,k)
2512 else
25130 frac_used = sqrt((ustarsq-oldfn) / (Dfn))
25140 Dh = h_at_vel(i,k) * frac_used
25150 Ddz = dz_at_vel(i,k) * frac_used
2516 endif
2517
25180 htot(i) = htot(i) + Dh
25190 dztot(i) = dztot(i) + Ddz
25200 Rhtot(i) = Rhtot(i) + Rlay*Dh
2521 enddo
25220 if (GV%Rlay(nz)*htot(i) - Rhtot(i) < ustarsq) then
25230 htot(i) = htot(i) + h_at_vel(i,nz)
25240 dztot(i) = dztot(i) + dz_at_vel(i,nz)
2525 endif
2526 endif ! use_EOS
2527
2528 ! visc%tbl_thick_shelf_u(I,j) = max(CS%Htbl_shelf_min, &
2529 ! dztot(I) / (0.5 + sqrt(0.25 + &
2530 ! ((htot(i)*(G%CoriolisBu(I,J-1)+G%CoriolisBu(I,J)))**2) / &
2531 ! (ustar(i)**2) )) )
25320 ustar1 = ustar(i)
25330 h2f2 = (htot(i)*(G%CoriolisBu(I,J-1)+G%CoriolisBu(I,J)) + h_neglect*CS%omega)**2
2534 tbl_thick = max(CS%Htbl_shelf_min, &
25350 ( dztot(I)*ustar(i) ) / ( 0.5*ustar1 + sqrt((0.5*ustar1)**2 + h2f2 ) ) )
25360 visc%tbl_thick_shelf_u(I,j) = tbl_thick
25370 visc%Kv_tbl_shelf_u(I,j) = max(CS%Kv_TBL_min, cdrag_sqrt*ustar1*tbl_thick)
2538 endif ; enddo ! I-loop
2539 endif ! do_any_shelf
2540
2541 enddo ! j-loop at u-points
2542
2543 !$OMP parallel do default(private) shared(u,v,h,dz,tv,forces,visc,dt,G,GV,US,CS,use_EOS,dt_Rho0, &
2544 !$OMP nonBous_ML,h_neglect,dz_neglect,h_tiny,g_H_Rho0, &
2545 !$OMP is,ie,OBC,Jsq,Jeq,nz,nkml,U_star_2d,mask_u, &
2546 !$OMP cdrag_sqrt,cdrag_sqrt_H,cdrag_sqrt_H_RL,Rho0x400_G)
25470 do J=Jsq,Jeq ! v-point loop
25480 if (CS%dynamic_viscous_ML) then
25490 do_any = .false.
25500 do i=is,ie
25510 htot(i) = 0.0
25520 if (G%mask2dCv(i,J) < 0.5) then
25530 do_i(i) = .false. ; visc%nkml_visc_v(i,J) = nkml
2554 else
25550 do_i(i) = .true. ; do_any = .true.
25560 k_massive(i) = nkml
25570 Thtot(i) = 0.0 ; Shtot(i) = 0.0 ; Rhtot(i) = 0.0
25580 vhtot(i) = dt_Rho0 * forces%tauy(i,J)
2559 uhtot(i) = 0.25 * dt_Rho0 * ((forces%taux(I,j) + forces%taux(I-1,j+1)) + &
25600 (forces%taux(I-1,j) + forces%taux(I,j+1)))
2561
25620 if (CS%omega_frac >= 1.0) then ; absf = 2.0*CS%omega ; else
25630 absf = 0.5*(abs(G%CoriolisBu(I-1,J)) + abs(G%CoriolisBu(I,J)))
25640 if (CS%omega_frac > 0.0) &
25650 absf = sqrt(CS%omega_frac*4.0*CS%omega**2 + (1.0-CS%omega_frac)*absf**2)
2566 endif
2567
25680 U_star = max(CS%ustar_min, 0.5*(U_star_2d(i,j) + U_star_2d(i,j+1)))
25690 Idecay_len_TKE(i) = (absf / U_star) * CS%TKE_decay
2570
2571 endif
2572 enddo
2573
25740 if (do_any) then ; do k=1,nz
25750 if (k > nkml) then
25760 do_any = .false.
25770 if (use_EOS .and. (k==nkml+1)) then
2578 ! Find dRho/dT and dRho_dS.
25790 do i=is,ie
25800 press(i) = (GV%H_to_RZ * GV%g_Earth) * htot(i)
25810 if (associated(tv%p_surf)) press(i) = press(i) + 0.5*(tv%p_surf(i,j)+tv%p_surf(i,j+1))
25820 k2 = max(1,nkml)
25830 I_2hlay = 1.0 / (h(i,j,k2) + h(i,j+1,k2) + h_neglect)
25840 T_EOS(i) = ((h(i,j,k2)*tv%T(i,j,k2)) + (h(i,j+1,k2)*tv%T(i,j+1,k2))) * I_2hlay
25850 S_EOS(i) = ((h(i,j,k2)*tv%S(i,j,k2)) + (h(i,j+1,k2)*tv%S(i,j+1,k2))) * I_2hlay
2586 enddo
2587 call calculate_density_derivs(T_EOS, S_EOS, press, dR_dT, dR_dS, &
25880 tv%eqn_of_state, (/is-G%IsdB+1,ie-G%IsdB+1/) )
25890 if (nonBous_ML) then
2590 call calculate_specific_vol_derivs(T_EOS, S_EOS, press, dSpV_dT, dSpV_dS, tv%eqn_of_state, &
25910 (/is-G%IsdB+1,ie-G%IsdB+1/) )
2592 endif
2593 endif
2594
25950 do i=is,ie ; if (do_i(i)) then
2596
25970 hlay = 0.5*(h(i,j,k) + h(i,j+1,k))
25980 if (hlay > h_tiny) then ! Only consider non-vanished layers.
25990 I_2hlay = 1.0 / (h(i,j,k) + h(i,j+1,k))
2600 u_at_v = 0.5 * ((h(i,j,k) * (u(I-1,j,k) + u(I,j,k))) + &
26010 (h(i,j+1,k) * (u(I-1,j+1,k) + u(I,j+1,k)))) * I_2hlay
26020 Uh2 = (vhtot(i) - htot(i)*v(i,J,k))**2 + (uhtot(i) - htot(i)*u_at_v)**2
2603
26040 if (use_EOS) then
26050 T_lay = ((h(i,j,k)*tv%T(i,j,k)) + (h(i,j+1,k)*tv%T(i,j+1,k))) * I_2hlay
26060 S_lay = ((h(i,j,k)*tv%S(i,j,k)) + (h(i,j+1,k)*tv%S(i,j+1,k))) * I_2hlay
26070 if (nonBous_ML) then
2608 gHprime = (GV%g_Earth * GV%H_to_RZ) * (dSpV_dT(i) * (Thtot(i) - T_lay*htot(i)) + &
26090 dSpV_dS(i) * (Shtot(i) - S_lay*htot(i)))
2610 else
2611 gHprime = g_H_Rho0 * (dR_dT(i) * (T_lay*htot(i) - Thtot(i)) + &
26120 dR_dS(i) * (S_lay*htot(i) - Shtot(i)))
2613 endif
2614 else
26150 gHprime = g_H_Rho0 * (GV%Rlay(k)*htot(i) - Rhtot(i))
2616 endif
2617
26180 if (gHprime > 0.0) then
26190 RiBulk = CS%bulk_Ri_ML * exp(-htot(i) * Idecay_len_TKE(i))
26200 if (RiBulk * Uh2 <= htot(i)**2 * gHprime) then
26210 visc%nkml_visc_v(i,J) = real(k_massive(i))
26220 do_i(i) = .false.
26230 elseif (RiBulk * Uh2 <= (htot(i) + hlay)**2 * gHprime) then
2624 visc%nkml_visc_v(i,J) = real(k-1) + &
26250 ( sqrt(RiBulk * Uh2 / gHprime) - htot(i) ) / hlay
26260 do_i(i) = .false.
2627 endif
2628 endif
26290 k_massive(i) = k
2630 endif ! hlay > h_tiny
2631
26320 if (do_i(i)) do_any = .true.
2633 endif ; enddo
2634
26350 if (.not.do_any) exit ! All columns are done.
2636 endif
2637
26380 do i=is,ie ; if (do_i(i)) then
26390 htot(i) = htot(i) + 0.5 * (h(i,J,k) + h(i,j+1,k))
26400 vhtot(i) = vhtot(i) + 0.5 * (h(i,j,k) + h(i,j+1,k)) * v(i,J,k)
2641 uhtot(i) = uhtot(i) + 0.25 * ((h(i,j,k) * (u(I-1,j,k) + u(I,j,k))) + &
26420 (h(i,j+1,k) * (u(I-1,j+1,k) + u(I,j+1,k))))
26430 if (use_EOS) then
26440 Thtot(i) = Thtot(i) + 0.5 * ((h(i,j,k)*tv%T(i,j,k)) + (h(i,j+1,k)*tv%T(i,j+1,k)))
26450 Shtot(i) = Shtot(i) + 0.5 * ((h(i,j,k)*tv%S(i,j,k)) + (h(i,j+1,k)*tv%S(i,j+1,k)))
2646 else
26470 Rhtot(i) = Rhtot(i) + 0.5 * (h(i,j,k) + h(i,j+1,k)) * GV%Rlay(k)
2648 endif
2649 endif ; enddo
2650 enddo ; endif
2651
26520 if (do_any) then ; do i=is,ie ; if (do_i(i)) then
26530 visc%nkml_visc_v(i,J) = k_massive(i)
2654 endif ; enddo ; endif
2655
2656 endif ! dynamic_viscous_ML
2657
26580 do_any_shelf = .false.
26590 if (associated(forces%frac_shelf_v)) then
26600 do i=is,ie
26610 if (forces%frac_shelf_v(i,J)*G%mask2dCv(i,J) == 0.0) then
26620 do_i(i) = .false.
26630 visc%tbl_thick_shelf_v(i,J) = 0.0 ; visc%kv_tbl_shelf_v(i,J) = 0.0
2664 else
26650 do_i(i) = .true. ; do_any_shelf = .true.
2666 endif
2667 enddo
2668 endif
2669
26700 if (do_any_shelf) then
26710 do k=1,nz ; do i=is,ie ; if (do_i(i)) then
26720 if (v(i,J,k) * (h(i,j+1,k) - h(i,j,k)) >= 0) then
2673 h_at_vel(i,k) = 2.0*h(i,j,k)*h(i,j+1,k) / &
26740 (h(i,j,k) + h(i,j+1,k) + h_neglect)
2675 dz_at_vel(i,k) = 2.0*dz(i,j,k)*dz(i,j+1,k) / &
26760 (dz(i,j,k) + dz(i,j+1,k) + dz_neglect)
2677 else
26780 h_at_vel(i,k) = 0.5 * (h(i,j,k) + h(i,j+1,k))
26790 dz_at_vel(i,k) = 0.5 * (dz(i,j,k) + dz(i,j+1,k))
2680 endif
2681 else
26820 h_at_vel(I,k) = 0.0
26830 dz_at_vel(I,k) = 0.0
26840 ustar(i) = 0.0
2685 endif ; enddo ; enddo
2686
26870 do i=is,ie ; if (do_i(i)) then
26880 htot_vel = 0.0 ; hwtot = 0.0 ; hutot = 0.0
26890 Thtot(i) = 0.0 ; Shtot(i) = 0.0 ; SpV_htot(i) = 0.0
26900 if (use_EOS .or. .not.CS%linear_drag) then ; do k=1,nz
26910 if (htot_vel>=CS%Htbl_shelf) exit ! terminate the k loop
26920 hweight = MIN(CS%Htbl_shelf - htot_vel, h_at_vel(i,k))
26930 if (hweight <= 1.5*GV%Angstrom_H + h_neglect) cycle
2694
26950 htot_vel = htot_vel + h_at_vel(i,k)
26960 hwtot = hwtot + hweight
2697
26980 if (.not.CS%linear_drag) then
26990 u_at_v = set_u_at_v(u, h, G, GV, i, J, k, mask_u, OBC)
2700 ! Set the "back ground" friction velocity scale to either the tidal amplitude or place-holder constant
27010 if (CS%BBL_use_tidal_bg) then
2702 u2_bg(i) = 0.5*( G%mask2dT(i,j)*(CS%tideamp(i,j)*CS%tideamp(i,j))+ &
27030 G%mask2dT(i,j+1)*(CS%tideamp(i,j+1)*CS%tideamp(i,j+1)) )
2704 else
27050 u2_bg(i) = CS%drag_bg_vel * CS%drag_bg_vel
2706 endif
27070 hutot = hutot + hweight * sqrt(v(i,J,k)**2 + u_at_v**2 + u2_bg(i))
2708 endif
27090 if (use_EOS) then
27100 Thtot(i) = Thtot(i) + hweight * 0.5 * (tv%T(i,j,k) + tv%T(i,j+1,k))
27110 Shtot(i) = Shtot(i) + hweight * 0.5 * (tv%S(i,j,k) + tv%S(i,j+1,k))
2712 endif
27130 if (allocated(tv%SpV_avg)) then
27140 SpV_htot(i) = SpV_htot(i) + hweight * 0.5 * (tv%SpV_avg(i,j,k) + tv%SpV_avg(i,j+1,k))
2715 endif
2716 enddo ; endif
2717
27180 if ((hwtot <= 0.0) .or. (CS%linear_drag .and. .not.allocated(tv%SpV_avg))) then
27190 ustar(i) = cdrag_sqrt_H * CS%drag_bg_vel
27200 elseif (CS%linear_drag .and. allocated(tv%SpV_avg)) then
27210 ustar(i) = cdrag_sqrt_H_RL * CS%drag_bg_vel * (hwtot / SpV_htot(i))
27220 elseif (allocated(tv%SpV_avg)) then ! (.not.CS%linear_drag)
27230 ustar(i) = cdrag_sqrt_H_RL * hutot / SpV_htot(i)
2724 else ! (.not.CS%linear_drag .and. .not.allocated(tv%SpV_avg))
27250 ustar(i) = cdrag_sqrt_H * hutot / hwtot
2726 endif
2727
27280 if (use_EOS) then ; if (hwtot > 0.0) then
27290 T_EOS(i) = Thtot(i)/hwtot ; S_EOS(i) = Shtot(i)/hwtot
2730 else
27310 T_EOS(i) = 0.0 ; S_EOS(i) = 0.0
2732 endif ; endif
2733 endif ; enddo ! I-loop
2734
27350 if (use_EOS) then
2736 call calculate_density_derivs(T_EOS, S_EOS, forces%p_surf(:,j), dR_dT, dR_dS, &
27370 tv%eqn_of_state, (/is-G%IsdB+1,ie-G%IsdB+1/) )
2738 endif
2739
27400 do i=is,ie ; if (do_i(i)) then
2741 ! The 400.0 in this expression is the square of a constant proposed
2742 ! by Killworth and Edwards, 1999, in equation (2.20).
27430 ustarsq = Rho0x400_G * ustar(i)**2
27440 htot(i) = 0.0
27450 dztot(i) = 0.0
27460 if (use_EOS) then
27470 Thtot(i) = 0.0 ; Shtot(i) = 0.0 ; oldfn = 0.0
27480 do k=1,nz-1
27490 if (h_at_vel(i,k) <= 0.0) cycle
27500 T_Lay = 0.5 * (tv%T(i,j,k) + tv%T(i,j+1,k))
27510 S_Lay = 0.5 * (tv%S(i,j,k) + tv%S(i,j+1,k))
27520 oldfn = dR_dT(i)*(T_Lay*htot(i) - Thtot(i)) + dR_dS(i)*(S_Lay*htot(i) - Shtot(i))
27530 if (oldfn >= ustarsq) exit
2754
2755 Dfn = (dR_dT(i)*(0.5*(tv%T(i,j,k+1)+tv%T(i,j+1,k+1)) - T_Lay) + &
2756 dR_dS(i)*(0.5*(tv%S(i,j,k+1)+tv%S(i,j+1,k+1)) - S_Lay)) * &
27570 (h_at_vel(i,k)+htot(i))
27580 if ((oldfn + Dfn) <= ustarsq) then
27590 Dh = h_at_vel(i,k)
27600 Ddz = dz_at_vel(i,k)
2761 else
27620 frac_used = sqrt((ustarsq-oldfn) / (Dfn))
27630 Dh = h_at_vel(i,k) * frac_used
27640 Ddz = dz_at_vel(i,k) * frac_used
2765 endif
2766
27670 htot(i) = htot(i) + Dh
27680 dztot(i) = dztot(i) + Ddz
27690 Thtot(i) = Thtot(i) + T_Lay*Dh ; Shtot(i) = Shtot(i) + S_Lay*Dh
2770 enddo
27710 if ((oldfn < ustarsq) .and. (h_at_vel(i,nz) > 0.0)) then
27720 T_Lay = 0.5*(tv%T(i,j,nz) + tv%T(i,j+1,nz))
27730 S_Lay = 0.5*(tv%S(i,j,nz) + tv%S(i,j+1,nz))
27740 if (dR_dT(i)*(T_Lay*htot(i) - Thtot(i)) + &
2775 dR_dS(i)*(S_Lay*htot(i) - Shtot(i)) < ustarsq) then
27760 htot(i) = htot(i) + h_at_vel(i,nz)
27770 dztot(i) = dztot(i) + dz_at_vel(i,nz)
2778 endif
2779 endif ! Examination of layer nz.
2780 else ! Use Rlay as the density variable.
27810 Rhtot = 0.0
27820 do k=1,nz-1
27830 Rlay = GV%Rlay(k) ; Rlb = GV%Rlay(k+1)
2784
27850 oldfn = Rlay*htot(i) - Rhtot(i)
27860 if (oldfn >= ustarsq) exit
2787
27880 Dfn = (Rlb - Rlay)*(h_at_vel(i,k)+htot(i))
27890 if ((oldfn + Dfn) <= ustarsq) then
27900 Dh = h_at_vel(i,k)
27910 Ddz = dz_at_vel(i,k)
2792 else
27930 frac_used = sqrt((ustarsq-oldfn) / (Dfn))
27940 Dh = h_at_vel(i,k) * frac_used
27950 Ddz = dz_at_vel(i,k) * frac_used
2796 endif
2797
27980 htot(i) = htot(i) + Dh
27990 dztot(i) = dztot(i) + Ddz
28000 Rhtot = Rhtot + Rlay*Dh
2801 enddo
28020 if (GV%Rlay(nz)*htot(i) - Rhtot(i) < ustarsq) then
28030 htot(i) = htot(i) + h_at_vel(i,nz)
28040 dztot(i) = dztot(i) + dz_at_vel(i,nz)
2805 endif
2806 endif ! use_EOS
2807
2808 ! visc%tbl_thick_shelf_v(i,J) = max(CS%Htbl_shelf_min, &
2809 ! dztot(i) / (0.5 + sqrt(0.25 + &
2810 ! (htot(i)*(G%CoriolisBu(I-1,J)+G%CoriolisBu(I,J)))**2 / &
2811 ! (ustar(i))**2 )) )
28120 ustar1 = ustar(i)
28130 h2f2 = (htot(i)*(G%CoriolisBu(I-1,J)+G%CoriolisBu(I,J)) + h_neglect*CS%omega)**2
2814 tbl_thick = max(CS%Htbl_shelf_min, &
28150 ( dztot(i)*ustar(i) ) / ( 0.5*ustar1 + sqrt((0.5*ustar1)**2 + h2f2 ) ) )
28160 visc%tbl_thick_shelf_v(i,J) = tbl_thick
28170 visc%Kv_tbl_shelf_v(i,J) = max(CS%Kv_TBL_min, cdrag_sqrt*ustar1*tbl_thick)
2818
2819 endif ; enddo ! i-loop
2820 endif ! do_any_shelf
2821
2822 enddo ! J-loop at v-points
2823
2824 !$omp target update to(visc%nkml_visc_u, visc%nkml_visc_v) if (CS%dynamic_viscous_ML)
2825
28260 if (CS%debug) then
28270 if (allocated(visc%nkml_visc_u) .and. allocated(visc%nkml_visc_v)) &
2828 call uvchksum("nkml_visc_[uv]", visc%nkml_visc_u, visc%nkml_visc_v, &
28290 G%HI, haloshift=0, scalar_pair=.true.)
2830 endif
28310 if (CS%id_nkml_visc_u > 0) call post_data(CS%id_nkml_visc_u, visc%nkml_visc_u, CS%diag)
28320 if (CS%id_nkml_visc_v > 0) call post_data(CS%id_nkml_visc_v, visc%nkml_visc_v, CS%diag)
2833
2834end subroutine set_viscous_ML
2835
2836!> Register any fields associated with the vertvisc_type.
28371subroutine set_visc_register_restarts(HI, G, GV, US, param_file, visc, restart_CS, use_ice_shelf)
2838 type(hor_index_type), intent(in) :: HI !< A horizontal index type structure.
2839 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
2840 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
2841 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
2842 type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time
2843 !! parameters.
2844 type(vertvisc_type), intent(inout) :: visc !< A structure containing vertical
2845 !! viscosities and related fields.
2846 !! Allocated here.
2847 type(MOM_restart_CS), intent(inout) :: restart_CS !< MOM restart control structure
2848 logical, intent(in) :: use_ice_shelf !< if true, register tau_shelf restarts
2849 ! Local variables
2850 logical :: use_kappa_shear, KS_at_vertex
2851 logical :: adiabatic, useKPP, useEPBL, use_ideal_age
2852 logical :: do_brine_plume, use_hor_bnd_diff, use_neutral_diffusion, use_fpmix
2853 logical :: use_CVMix_shear, MLE_use_PBL_MLD, MLE_use_Bodner, use_CVMix_conv
2854 integer :: isd, ied, jsd, jed, nz
2855 real :: hfreeze !< If hfreeze > 0 [Z ~> m], melt potential will be computed.
2856 character(len=16) :: Kv_units, Kd_units
2857 character(len=40) :: mdl = "MOM_set_visc" ! This module's name.
285811 type(vardesc) :: u_desc, v_desc
28591 isd = HI%isd ; ied = HI%ied ; jsd = HI%jsd ; jed = HI%jed ; nz = GV%ke
2860
2861 call get_param(param_file, mdl, "ADIABATIC", adiabatic, default=.false., &
28621 do_not_log=.true.)
2863
28641 use_kappa_shear = .false. ; KS_at_vertex = .false. ; use_CVMix_shear = .false.
28651 useKPP = .false. ; useEPBL = .false. ; use_CVMix_conv = .false.
2866
28671 if (.not.adiabatic) then
28681 use_kappa_shear = kappa_shear_is_used(param_file)
28691 KS_at_vertex = kappa_shear_at_vertex(param_file)
28701 use_CVMix_shear = CVMix_shear_is_used(param_file)
28711 use_CVMix_conv = CVMix_conv_is_used(param_file)
2872 call get_param(param_file, mdl, "USE_KPP", useKPP, &
2873 "If true, turns on the [CVMix] KPP scheme of Large et al., 1994, "//&
2874 "to calculate diffusivities and non-local transport in the OBL.", &
28751 default=.false., do_not_log=.true.)
2876 call get_param(param_file, mdl, "ENERGETICS_SFC_PBL", useEPBL, &
2877 "If true, use an implied energetics planetary boundary "//&
2878 "layer scheme to determine the diffusivity and viscosity "//&
28791 "in the surface boundary layer.", default=.false., do_not_log=.true.)
2880 endif
2881
28821 if (GV%Boussinesq) then
28831 Kv_units = "m2 s-1" ; Kd_units = "m2 s-1"
2884 else
28850 Kv_units = "Pa s" ; Kd_units = "kg m-1 s-1"
2886 endif
2887
28881 if (use_kappa_shear .or. useKPP .or. useEPBL .or. use_CVMix_shear .or. use_CVMix_conv) then
28891 call safe_alloc_ptr(visc%Kd_shear, isd, ied, jsd, jed, nz+1)
2890 call register_restart_field(visc%Kd_shear, "Kd_shear", .false., restart_CS, &
2891 "Shear-driven turbulent diffusivity at interfaces", &
28921 units=Kd_units, conversion=GV%HZ_T_to_MKS, z_grid='i')
2893 endif
28941 if (useKPP .or. useEPBL .or. use_CVMix_shear .or. use_CVMix_conv .or. &
2895 (use_kappa_shear .and. .not.KS_at_vertex )) then
28961 call safe_alloc_ptr(visc%Kv_shear, isd, ied, jsd, jed, nz+1)
2897 call register_restart_field(visc%Kv_shear, "Kv_shear", .false., restart_CS, &
2898 "Shear-driven turbulent viscosity at interfaces", &
28991 units=Kv_units, conversion=GV%HZ_T_to_MKS, z_grid='i')
2900 endif
29011 if (use_kappa_shear .and. KS_at_vertex) then
29021 call safe_alloc_ptr(visc%TKE_turb, HI%IsdB, HI%IedB, HI%JsdB, HI%JedB, nz+1)
29031 call safe_alloc_ptr(visc%Kv_shear_Bu, HI%IsdB, HI%IedB, HI%JsdB, HI%JedB, nz+1)
2904 call register_restart_field(visc%Kv_shear_Bu, "Kv_shear_Bu", .false., restart_CS, &
2905 "Shear-driven turbulent viscosity at vertex interfaces", &
29061 units=Kv_units, conversion=GV%HZ_T_to_MKS, hor_grid="Bu", z_grid='i')
29070 elseif (use_kappa_shear) then
29080 call safe_alloc_ptr(visc%TKE_turb, isd, ied, jsd, jed, nz+1)
2909 endif
2910
29111 if (useKPP) then
2912 ! MOM_bkgnd_mixing uses Kv_slow when KPP is defined.
29130 call safe_alloc_ptr(visc%Kv_slow, isd, ied, jsd, jed, nz+1)
2914 endif
2915
2916 ! visc%MLD and visc%h_ML are used to communicate the state of the (e)PBL or KPP to the rest of the model
2917 call get_param(param_file, mdl, "MLE_USE_PBL_MLD", MLE_use_PBL_MLD, &
29181 default=.false., do_not_log=.true.)
2919 ! visc%h_ML needs to be allocated when melt potential is computed (HFREEZE>0) or one of
2920 ! several other parameterizations are in use.
2921 call get_param(param_file, mdl, "HFREEZE", hfreeze, &
29221 units="m", default=-1.0, scale=US%m_to_Z, do_not_log=.true.)
2923 call get_param(param_file, mdl, "DO_BRINE_PLUME", do_brine_plume, &
2924 "If true, use a brine plume parameterization from Nguyen et al., 2009.", &
29251 default=.false., do_not_log=.true.)
2926 call get_param(param_file, mdl, "USE_HORIZONTAL_BOUNDARY_DIFFUSION", use_hor_bnd_diff, &
29271 default=.false., do_not_log=.true.)
2928 call get_param(param_file, mdl, "USE_NEUTRAL_DIFFUSION", use_neutral_diffusion, &
29291 default=.false., do_not_log=.true.)
29301 if (use_neutral_diffusion) &
2931 call get_param(param_file, mdl, "NDIFF_INTERIOR_ONLY", use_neutral_diffusion, &
29320 default=.false., do_not_log=.true.)
2933 call get_param(param_file, mdl, "FPMIX", use_fpmix, &
29341 default=.false., do_not_log=.true.)
2935 call get_param(param_file, mdl, "USE_IDEAL_AGE_TRACER", use_ideal_age, &
29361 default=.false., do_not_log=.true.)
29371 call openParameterBlock(param_file, 'MLE', do_not_log=.true.)
2938 call get_param(param_file, mdl, "USE_BODNER23", MLE_use_Bodner, &
29391 default=.false., do_not_log=.true.)
29401 call closeParameterBlock(param_file)
2941
29421 if (MLE_use_PBL_MLD .or. MLE_use_Bodner) then
29431 call safe_alloc_ptr(visc%MLD, isd, ied, jsd, jed)
2944 endif
2945 if ((hfreeze >= 0.0) .or. MLE_use_PBL_MLD .or. do_brine_plume .or. use_fpmix .or. &
29461 use_neutral_diffusion .or. use_hor_bnd_diff .or. use_ideal_age) then
29471 call safe_alloc_ptr(visc%h_ML, isd, ied, jsd, jed)
2948 endif
2949
29501 if (MLE_use_PBL_MLD) then
2951 call register_restart_field(visc%MLD, "MLD", .false., restart_CS, &
29521 "Instantaneous active mixing layer depth", units="m", conversion=US%Z_to_m)
2953 endif
2954 if (MLE_use_PBL_MLD .or. do_brine_plume .or. use_fpmix .or. &
29551 use_neutral_diffusion .or. use_hor_bnd_diff) then
2956 call register_restart_field(visc%h_ML, "h_ML", .false., restart_CS, &
2957 "Instantaneous active mixing layer thickness", &
29581 units=get_thickness_units(GV), conversion=GV%H_to_mks)
2959 endif
2960
2961 ! visc%sfc_buoy_flx is used to communicate the state of the (e)PBL or KPP to the rest of the model
29621 if (MLE_use_PBL_MLD .or. MLE_use_Bodner) then
29631 call safe_alloc_ptr(visc%sfc_buoy_flx, isd, ied, jsd, jed)
2964 call register_restart_field(visc%sfc_buoy_flx, "SFC_BFLX", .false., restart_CS, &
2965 "Instantaneous surface buoyancy flux", "m2 s-3", &
29661 conversion=US%Z_to_m**2*US%s_to_T**3)
2967 endif
2968
29691 if (use_ice_shelf) then
29700 if (.not.allocated(visc%taux_shelf)) &
29710 allocate(visc%taux_shelf(G%IsdB:G%IedB, G%jsd:G%jed), source=0.0)
29720 if (.not.allocated(visc%tauy_shelf)) &
29730 allocate(visc%tauy_shelf(G%isd:G%ied, G%JsdB:G%JedB), source=0.0)
2974 u_desc = var_desc("u_taux_shelf", "Pa", "the zonal stress on the ocean under ice shelves", &
29750 hor_grid='Cu',z_grid='1')
2976 v_desc = var_desc("v_tauy_shelf", "Pa", "the meridional stress on the ocean under ice shelves", &
29770 hor_grid='Cv',z_grid='1')
2978 call register_restart_pair(visc%taux_shelf, visc%tauy_shelf, u_desc, v_desc, &
29790 .false., restart_CS, conversion=US%RZ_T_to_kg_m2s*US%L_T_to_m_s)
2980 endif
2981
298211end subroutine set_visc_register_restarts
2983
2984!> This subroutine does remapping for the auxiliary restart variables in a vertvisc_type
2985!! that are used across timesteps
29860subroutine remap_vertvisc_aux_vars(G, GV, visc, h_old, h_new, ALE_CSp, OBC)
2987 type(ocean_grid_type), intent(inout) :: G !< ocean grid structure
2988 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
2989 type(vertvisc_type), intent(inout) :: visc !< A structure containing vertical
2990 !! viscosities and related fields.
2991 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
2992 intent(in) :: h_old !< Thickness of source grid [H ~> m or kg m-2]
2993 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
2994 intent(in) :: h_new !< Thickness of destination grid [H ~> m or kg m-2]
2995 type(ALE_CS), pointer :: ALE_CSp !< ALE control structure to use when remapping
2996 type(ocean_OBC_type), pointer :: OBC !< Open boundary structure
2997
29980 if (associated(visc%Kd_shear)) then
29990 call ALE_remap_interface_vals(ALE_CSp, G, GV, h_old, h_new, visc%Kd_shear)
3000 endif
3001
30020 if (associated(visc%Kv_shear)) then
30030 call ALE_remap_interface_vals(ALE_CSp, G, GV, h_old, h_new, visc%Kv_shear)
3004 endif
3005
30060 if (associated(visc%Kv_shear_Bu)) then
30070 call ALE_remap_vertex_vals(ALE_CSp, G, GV, h_old, h_new, visc%Kv_shear_Bu)
3008 endif
3009
30100end subroutine remap_vertvisc_aux_vars
3011
3012!> Initializes the MOM_set_visc control structure
30131subroutine set_visc_init(Time, G, GV, US, param_file, diag, visc, CS, restart_CS, OBC)
3014 type(time_type), target, intent(in) :: Time !< The current model time.
3015 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
3016 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
3017 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
3018 type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time
3019 !! parameters.
3020 type(diag_ctrl), target, intent(inout) :: diag !< A structure that is used to regulate diagnostic
3021 !! output.
3022 type(vertvisc_type), intent(inout) :: visc !< A structure containing vertical viscosities and
3023 !! related fields.
3024 type(set_visc_CS), intent(inout) :: CS !< Vertical viscosity control structure
3025 type(MOM_restart_CS), intent(inout) :: restart_CS !< MOM restart control structure
3026 type(ocean_OBC_type), pointer :: OBC !< A pointer to an open boundary condition structure
3027
3028 ! Local variables
3029 real :: Csmag_chan_dflt ! The default value for SMAG_CONST_CHANNEL [nondim]
3030 real :: smag_const1 ! The default value for the Smagorinsky Laplacian coefficient [nondim]
3031 real :: TKE_decay_dflt ! The default value of a coefficient scaling the vertical decay
3032 ! rate of TKE [nondim]
3033 real :: bulk_Ri_ML_dflt ! The default bulk Richardson number for a bulk mixed layer [nondim]
3034 real :: Kv_background ! The background kinematic viscosity in the interior [Z2 T-1 ~> m2 s-1]
3035 real :: omega_frac_dflt ! The default value for the fraction of the absolute rotation rate that
3036 ! is used in place of the absolute value of the local Coriolis
3037 ! parameter in the denominator of some expressions [nondim]
3038 real :: Chan_max_thick_dflt ! The default value for CHANNEL_DRAG_MAX_THICK [Z ~> m]
3039 real :: tideamp_factor ! A factor to multiply by tideamp when converting to mean tidal magnitude [nondim]
3040 real :: shelfbreak_depth ! When CHANNEL_DRAG is true, the bathymetric depth interpolated
3041 ! to the vorticity point is a combination of the harmonic mean of the
3042 ! adjacent velocity point depths below this depth [Z ~> m] and the
3043 ! arithmetic mean of the adjacent depths above it, to roughly mimic a
3044 ! continental shelf break profile.
30451 real, allocatable, dimension(:,:) :: cdrag_h !< The spatially varying quadratic drag coefficient [nondim]
3046
3047 integer :: i, j, is, ie, js, je
3048 integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, nz
3049 integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags.
3050 logical :: adiabatic, use_omega, MLE_use_PBL_MLD
3051 logical :: use_KPP
3052 logical :: use_regridding ! If true, use the ALE algorithm rather than layered
3053 ! isopycnal or stacked shallow water mode.
3054 logical :: use_temperature ! If true, temperature and salinity are used as state variables.
3055 logical :: use_EOS ! If true, density calculated from T & S using an equation of state.
3056 character(len=200) :: filename, cdrag_file, tideamp_file ! Input file names or paths
3057 character(len=80) :: cdrag_var, tideamp_var ! Input file variable names
3058 ! This include declares and sets the variable "version".
3059# include "version_variable.h"
3060 character(len=40) :: mdl = "MOM_set_visc" ! This module's name.
3061
30621 CS%initialized = .true.
30631 CS%OBC => OBC
3064
30651 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
30661 isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed ; nz = GV%ke
30671 IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB
3068
30691 CS%diag => diag
3070
3071 ! Set default, read and log parameters
30721 call log_version(param_file, mdl, version, "")
30731 CS%RiNo_mix = .false.
30741 call get_param(param_file, mdl, "INPUTDIR", CS%inputdir, default=".")
30751 CS%inputdir = slasher(CS%inputdir)
3076 call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, &
3077 "This sets the default value for the various _ANSWER_DATE parameters.", &
30781 default=99991231)
3079 call get_param(param_file, mdl, "SET_VISC_ANSWER_DATE", CS%answer_date, &
3080 "The vintage of the order of arithmetic and expressions in the set viscosity "//&
3081 "calculations. Values below 20190101 recover the answers from the end of 2018, "//&
3082 "while higher values use updated and more robust forms of the same expressions.", &
30831 default=default_answer_date, do_not_log=.not.GV%Boussinesq)
30841 if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701)
3085 call get_param(param_file, mdl, "BOTTOMDRAGLAW", CS%bottomdraglaw, &
3086 "If true, the bottom stress is calculated with a drag "//&
3087 "law of the form c_drag*|u|*u. The velocity magnitude "//&
3088 "may be an assumed value or it may be based on the "//&
3089 "actual velocity in the bottommost HBBL, depending on "//&
30901 "LINEAR_DRAG.", default=.true.)
3091 call get_param(param_file, mdl, "DRAG_AS_BODY_FORCE", CS%body_force_drag, &
3092 "If true, the bottom stress is imposed as an explicit body force "//&
3093 "applied over a fixed distance from the bottom, rather than as an "//&
3094 "implicit calculation based on an enhanced near-bottom viscosity. "//&
3095 "The thickness of the bottom boundary layer is HBBL.", &
30961 default=.false., do_not_log=.not.CS%bottomdraglaw)
3097 call get_param(param_file, mdl, "CHANNEL_DRAG", CS%Channel_drag, &
3098 "If true, the bottom drag is exerted directly on each "//&
3099 "layer proportional to the fraction of the bottom it overlies.", &
31001 default=.false.)
3101 call get_param(param_file, mdl, "CHANNEL_DRAG_SHELFBREAK_DEPTH", shelfbreak_depth, &
3102 "When CHANNEL_DRAG is true, the bathymetric depth interpolated to the "//&
3103 "vorticity point is a combination of the harmonic mean of the adjacent "//&
3104 "velocity point depths below this depth and the arithmetic mean of the "//&
3105 "depths above it, to roughly mimic a continental shelf break profile. "//&
3106 "Setting this to exceed MAXIMUM_DEPTH leads to linear interpolation of "//&
3107 "the topography between velocity points.", &
31081 default=0.0, units="m", scale=US%m_to_Z, do_not_log=.not.CS%Channel_drag)
31091 CS%channel_break_depth = shelfbreak_depth - G%Z_ref
3110
3111 call get_param(param_file, mdl, "LINEAR_DRAG", CS%linear_drag, &
3112 "If LINEAR_DRAG and BOTTOMDRAGLAW are defined the drag "//&
31131 "law is cdrag*DRAG_BG_VEL*u.", default=.false.)
3114 call get_param(param_file, mdl, "ADIABATIC", adiabatic, default=.false., &
31151 do_not_log=.true.)
31161 if (adiabatic) then
3117 call log_param(param_file, mdl, "ADIABATIC",adiabatic, &
3118 "There are no diapycnal mass fluxes if ADIABATIC is true. "//&
3119 "This assumes that KD = 0.0 and that there is no buoyancy forcing, "//&
31200 "but makes the model faster by eliminating subroutine calls.", default=.false.)
3121 endif
3122
31231 if (.not.adiabatic) then
31241 CS%RiNo_mix = kappa_shear_is_used(param_file)
3125 endif
3126
31271 call get_param(param_file, mdl, "DEBUG", CS%debug, default=.false.)
3128
3129 call get_param(param_file, mdl, "DYNAMIC_VISCOUS_ML", CS%dynamic_viscous_ML, &
3130 "If true, use a bulk Richardson number criterion to "//&
3131 "determine the mixed layer thickness for viscosity.", &
31321 default=.false.)
31331 if (CS%dynamic_viscous_ML) then
31340 call get_param(param_file, mdl, "BULK_RI_ML", bulk_Ri_ML_dflt, units="nondim", default=0.0)
3135 call get_param(param_file, mdl, "BULK_RI_ML_VISC", CS%bulk_Ri_ML, &
3136 "The efficiency with which mean kinetic energy released by mechanically "//&
3137 "forced entrainment of the mixed layer is converted to turbulent "//&
3138 "kinetic energy. By default, BULK_RI_ML_VISC = BULK_RI_ML or 0.", &
31390 units="nondim", default=bulk_Ri_ML_dflt)
31400 call get_param(param_file, mdl, "TKE_DECAY", TKE_decay_dflt, units="nondim", default=0.0)
3141 call get_param(param_file, mdl, "TKE_DECAY_VISC", CS%TKE_decay, &
3142 "TKE_DECAY_VISC relates the vertical rate of decay of "//&
3143 "the TKE available for mechanical entrainment to the "//&
3144 "natural Ekman depth for use in calculating the dynamic "//&
3145 "mixed layer viscosity. By default, TKE_DECAY_VISC = TKE_DECAY or 0.", &
31460 units="nondim", default=TKE_decay_dflt)
3147 call get_param(param_file, mdl, "ML_USE_OMEGA", use_omega, &
3148 "If true, use the absolute rotation rate instead of the "//&
3149 "vertical component of rotation when setting the decay "//&
31500 "scale for turbulence.", default=.false., do_not_log=.true.)
31510 omega_frac_dflt = 0.0
31520 if (use_omega) then
31530 call MOM_error(WARNING, "ML_USE_OMEGA is deprecated; use ML_OMEGA_FRAC=1.0 instead.")
31540 omega_frac_dflt = 1.0
3155 endif
3156 call get_param(param_file, mdl, "ML_OMEGA_FRAC", CS%omega_frac, &
3157 "When setting the decay scale for turbulence, use this "//&
3158 "fraction of the absolute rotation rate blended with the "//&
3159 "local value of f, as sqrt((1-of)*f^2 + of*4*omega^2).", &
31600 units="nondim", default=omega_frac_dflt)
3161 call get_param(param_file, mdl, "OMEGA", CS%omega, &
3162 "The rotation rate of the earth.", &
31630 units="s-1", default=7.2921e-5, scale=US%T_to_s)
3164 ! This give a minimum decay scale that is typically much less than Angstrom.
31650 CS%ustar_min = 2e-4*CS%omega*(GV%Angstrom_H + GV%H_subroundoff)
3166 else
3167 call get_param(param_file, mdl, "OMEGA", CS%omega, &
3168 "The rotation rate of the earth.", &
31691 units="s-1", default=7.2921e-5, scale=US%T_to_s)
3170 endif
3171
3172 call get_param(param_file, mdl, "HBBL", CS%dz_bbl, &
3173 "The thickness of a bottom boundary layer with a viscosity increased by "//&
3174 "KV_EXTRA_BBL if BOTTOMDRAGLAW is not defined, or the thickness over which "//&
3175 "near-bottom velocities are averaged for the drag law if BOTTOMDRAGLAW is "//&
3176 "defined but LINEAR_DRAG is not.", &
31771 units="m", scale=US%m_to_Z, fail_if_missing=.true.) ! Rescaled later
31781 if (CS%bottomdraglaw) then
3179 call get_param(param_file, mdl, "CDRAG", CS%cdrag, &
3180 "CDRAG is the drag coefficient relating the magnitude of "//&
3181 "the velocity field to the bottom stress. CDRAG is only "//&
31821 "used if BOTTOMDRAGLAW is defined.", units="nondim", default=0.003)
3183 call get_param(param_file, mdl, "CDRAG_MAP", CS%bottomdragmap, &
3184 "If true, apply a spatially varying scaling factor to CDRAG, "//&
31851 "specified by CDRAG_VAR in CDRAG_FILE.", default=.false.)
3186 call get_param(param_file, mdl, "CDRAG_FILE", cdrag_file, &
3187 "The name of the file with the spatially varying bottom drag "//&
31881 "scaling factor.", default="", do_not_log=.not.CS%bottomdragmap)
3189 call get_param(param_file, mdl, "CDRAG_VAR", cdrag_var, &
3190 "The name of the variable in CDRAG_FILE with the spatially "//&
3191 "varying bottom drag scaling factor at h points.", &
31921 default="", do_not_log=.not.CS%bottomdragmap)
3193 call get_param(param_file, mdl, "BBL_USE_TIDAL_BG", CS%BBL_use_tidal_bg, &
3194 "Flag to use the tidal RMS amplitude in place of constant "//&
3195 "background velocity for computing u* in the BBL. "//&
3196 "This flag is only used when BOTTOMDRAGLAW is true and "//&
31971 "LINEAR_DRAG is false.", default=.false.)
31981 if (CS%BBL_use_tidal_bg) then
3199 call get_param(param_file, mdl, "TIDEAMP_FILE", tideamp_file, &
3200 "The path to the file containing the spatially varying "//&
32010 "tidal amplitudes with INT_TIDE_DISSIPATION.", default="tideamp.nc")
3202 call get_param(param_file, mdl, "TIDEAMP_VARNAME", tideamp_var, &
3203 "The name of the tidal amplitude variable in the input file.", &
32040 default="tideamp")
3205 ! This value is here only to detect whether it is inadvertently used. CS%drag_bg_vel should
3206 ! not be used if CS%BBL_use_tidal_bg is True. For this reason, we do not apply dimensions,
3207 ! nor dimensional testing in this mode. If we ever detect a dimensional sensitivity to
3208 ! this parameter, in this mode, then it means it is being used inappropriately.
32090 CS%drag_bg_vel = 1.e30
3210 call get_param(param_file, mdl, "TIDEAMP_FACTOR", tideamp_factor, &
3211 "A parameter to multiply by tideamp when converting to ustar. "//&
3212 "It accounts for converting the amplitude to a mean magintude (approx 1/sqrt(2)) "//&
3213 "and possibly also for non-commuting averaging operators when converting to ustar**3. "//&
3214 "It is ignored if negative and uncapped so it can be greater than 1 if desired.",&
32150 units="nondim", default=-1.0)
32160 if (tideamp_factor < 0.0) then
32170 CS%tideampfac2 = 1.0
3218 else
32190 CS%tideampfac2 = tideamp_factor*tideamp_factor
3220 endif
3221 else
3222 call get_param(param_file, mdl, "DRAG_BG_VEL", CS%drag_bg_vel, &
3223 "DRAG_BG_VEL is either the assumed bottom velocity (with "//&
3224 "LINEAR_DRAG) or an unresolved velocity that is "//&
3225 "combined with the resolved velocity to estimate the "//&
3226 "velocity magnitude. DRAG_BG_VEL is only used when "//&
32271 "BOTTOMDRAGLAW is defined.", units="m s-1", default=0.0, scale=US%m_s_to_L_T)
3228 endif
3229 call get_param(param_file, mdl, "USE_REGRIDDING", use_regridding, &
32301 do_not_log=.true., default=.false. )
3231 call get_param(param_file, mdl, "ENABLE_THERMODYNAMICS", use_temperature, &
32321 default=.true., do_not_log=.true.)
3233 call get_param(param_file, mdl, "USE_EOS", use_EOS, &
32341 default=use_temperature, do_not_log=.true.)
3235 call get_param(param_file, mdl, "BBL_USE_EOS", CS%BBL_use_EOS, &
3236 "If true, use the equation of state in determining the properties of the "//&
3237 "bottom boundary layer. Otherwise use the layer target potential densities. "//&
3238 "The default of this parameter is the value of USE_EOS.", &
32391 default=use_EOS, do_not_log=.not.use_temperature)
32401 if (use_regridding .and. (.not. CS%BBL_use_EOS)) &
32410 call MOM_error(FATAL,"When using MOM6 in ALE mode it is required to set BBL_USE_EOS to True.")
3242 endif
3243 call get_param(param_file, mdl, "BBL_THICK_MIN", CS%BBL_thick_min, &
3244 "The minimum bottom boundary layer thickness that can be "//&
3245 "used with BOTTOMDRAGLAW. This might be "//&
3246 "Kv/(cdrag*drag_bg_vel) to give Kv as the minimum "//&
32471 "near-bottom viscosity.", units="m", default=0.0, scale=US%m_to_Z)
3248 call get_param(param_file, mdl, "HTBL_SHELF_MIN", CS%Htbl_shelf_min, &
3249 "The minimum top boundary layer thickness that can be "//&
3250 "used with BOTTOMDRAGLAW. This might be "//&
3251 "Kv/(cdrag*drag_bg_vel) to give Kv as the minimum "//&
32521 "near-top viscosity.", units="m", default=US%Z_to_m*CS%BBL_thick_min, scale=US%m_to_Z)
3253 call get_param(param_file, mdl, "HTBL_SHELF", CS%Htbl_shelf, &
3254 "The thickness over which near-surface velocities are "//&
3255 "averaged for the drag law under an ice shelf. By "//&
3256 "default this is the same as HBBL", &
32571 units="m", default=US%Z_to_m*CS%dz_bbl, scale=GV%m_to_H)
3258
3259 call get_param(param_file, mdl, "KV", Kv_background, &
3260 "The background kinematic viscosity in the interior. "//&
3261 "The molecular value, ~1e-6 m2 s-1, may be used.", &
32621 units="m2 s-1", scale=US%m2_s_to_Z2_T, fail_if_missing=.true.)
3263
3264 call get_param(param_file, mdl, "USE_KPP", use_KPP, &
3265 "If true, turns on the [CVMix] KPP scheme of Large et al., 1994, "//&
3266 "to calculate diffusivities and non-local transport in the OBL.", &
32671 do_not_log=.true., default=.false.)
3268
3269 call get_param(param_file, mdl, "KV_BBL_MIN", CS%KV_BBL_min, &
3270 "The minimum viscosities in the bottom boundary layer.", &
32711 units="m2 s-1", default=US%Z2_T_to_m2_s*Kv_background, scale=GV%m2_s_to_HZ_T)
3272 call get_param(param_file, mdl, "KV_TBL_MIN", CS%KV_TBL_min, &
3273 "The minimum viscosities in the top boundary layer.", &
32741 units="m2 s-1", default=US%Z2_T_to_m2_s*Kv_background, scale=GV%m2_s_to_HZ_T)
3275 call get_param(param_file, mdl, "CORRECT_BBL_BOUNDS", CS%correct_BBL_bounds, &
3276 "If true, uses the correct bounds on the BBL thickness and "//&
3277 "viscosity so that the bottom layer feels the intended drag.", &
32781 default=.false.)
3279
32801 if (CS%Channel_drag) then
32811 call get_param(param_file, mdl, "SMAG_LAP_CONST", smag_const1, units="nondim", default=-1.0)
3282
32831 cSmag_chan_dflt = 0.15
32841 if (smag_const1 >= 0.0) cSmag_chan_dflt = smag_const1
3285
3286 call get_param(param_file, mdl, "SMAG_CONST_CHANNEL", CS%c_Smag, &
3287 "The nondimensional Laplacian Smagorinsky constant used "//&
3288 "in calculating the channel drag if it is enabled. The "//&
3289 "default is to use the same value as SMAG_LAP_CONST if "//&
3290 "it is defined, or 0.15 if it is not. The value used is "//&
3291 "also 0.15 if the specified value is negative.", &
32921 units="nondim", default=cSmag_chan_dflt, do_not_log=.not.CS%Channel_drag)
32931 if (CS%c_Smag < 0.0) CS%c_Smag = 0.15
3294
3295 call get_param(param_file, mdl, "TRIG_CHANNEL_DRAG_WIDTHS", CS%concave_trigonometric_L, &
3296 "If true, use trigonometric expressions to determine the fractional open "//&
3297 "interface lengths for concave topography.", &
32981 default=.true., do_not_log=.not.CS%Channel_drag)
3299 endif
3300
33011 Chan_max_thick_dflt = -1.0*US%m_to_Z
33021 if (CS%RiNo_mix) Chan_max_thick_dflt = 0.5*CS%dz_bbl
33031 if (CS%body_force_drag) Chan_max_thick_dflt = CS%dz_bbl
3304 call get_param(param_file, mdl, "CHANNEL_DRAG_MAX_BBL_THICK", CS%Chan_drag_max_vol, &
3305 "The maximum bottom boundary layer thickness over which the channel drag is "//&
3306 "exerted, or a negative value for no fixed limit, instead basing the BBL "//&
3307 "thickness on the bottom stress, rotation and stratification. The default is "//&
3308 "proportional to HBBL if USE_JACKSON_PARAM or DRAG_AS_BODY_FORCE is true.", &
3309 units="m", default=US%Z_to_m*Chan_max_thick_dflt, scale=US%m_to_Z, &
33101 do_not_log=.not.CS%Channel_drag)
3311
3312 call get_param(param_file, mdl, "MLE_USE_PBL_MLD", MLE_use_PBL_MLD, &
33131 default=.false., do_not_log=.true.)
3314
33151 CS%Hbbl = CS%dz_bbl * (US%Z_to_m * GV%m_to_H) ! Rescaled for use in expressions in thickness units.
3316
3317 ! Update scalar data to device
3318 !$omp target update to(visc)
3319 !$omp target update to(CS)
3320
3321 ! Now update arrays that were defined in set_visc_register_restarts()
3322 !$omp target enter data map(to: visc%Kv_shear) &
3323 !$omp if (associated(visc%Kv_shear))
3324 !$omp target enter data map(to: visc%Kv_shear_Bu) &
3325 !$omp if (associated(visc%Kv_shear_Bu))
3326
33271 if (CS%RiNo_mix .and. kappa_shear_at_vertex(param_file)) then
3328 ! This is necessary for reproducibility across restarts in non-symmetric mode.
33291 call pass_var(visc%Kv_shear_Bu, G%Domain, position=CORNER, complete=.true.)
3330 endif
3331
33321 if (CS%bottomdraglaw) then
33338841 allocate(visc%bbl_thick_u(IsdB:IedB,jsd:jed), source=0.0)
33348902 allocate(visc%bbl_thick_v(isd:ied,JsdB:JedB), source=0.0)
3335 !$omp target enter data map(to: visc%bbl_thick_u, visc%bbl_thick_v)
33368841 allocate(visc%kv_bbl_u(IsdB:IedB,jsd:jed), source=0.0)
33378902 allocate(visc%kv_bbl_v(isd:ied,JsdB:JedB), source=0.0)
3338 !$omp target enter data map(to: visc%kv_bbl_u, visc%kv_bbl_v)
33398773 allocate(visc%ustar_bbl(isd:ied,jsd:jed), source=0.0)
33408773 allocate(visc%BBL_meanKE_loss(isd:ied,jsd:jed), source=0.0)
33418773 allocate(visc%BBL_meanKE_loss_sqrtCd(isd:ied,jsd:jed), source=0.0)
3342
3343 CS%id_bbl_thick_u = register_diag_field('ocean_model', 'bbl_thick_u', &
33441 diag%axesCu1, Time, 'BBL thickness at u points', 'm', conversion=US%Z_to_m)
3345 CS%id_kv_bbl_u = register_diag_field('ocean_model', 'kv_bbl_u', diag%axesCu1, &
33461 Time, 'BBL viscosity at u points', 'm2 s-1', conversion=GV%HZ_T_to_m2_s)
3347 CS%id_bbl_u = register_diag_field('ocean_model', 'bbl_u', diag%axesCu1, &
33481 Time, 'BBL mean u current', 'm s-1', conversion=US%L_T_to_m_s)
33491 if (CS%id_bbl_u>0) then
33500 allocate(CS%bbl_u(IsdB:IedB,jsd:jed), source=0.0)
3351 endif
3352 CS%id_bbl_thick_v = register_diag_field('ocean_model', 'bbl_thick_v', &
33531 diag%axesCv1, Time, 'BBL thickness at v points', 'm', conversion=US%Z_to_m)
3354 CS%id_kv_bbl_v = register_diag_field('ocean_model', 'kv_bbl_v', diag%axesCv1, &
33551 Time, 'BBL viscosity at v points', 'm2 s-1', conversion=GV%HZ_T_to_m2_s)
3356 CS%id_bbl_v = register_diag_field('ocean_model', 'bbl_v', diag%axesCv1, &
33571 Time, 'BBL mean v current', 'm s-1', conversion=US%L_T_to_m_s)
33581 if (CS%id_bbl_v>0) then
33590 allocate(CS%bbl_v(isd:ied,JsdB:JedB), source=0.0)
3360 endif
33611 if (CS%bottomdragmap) then
33620 if (len_trim(cdrag_file)==0 .or. len_trim(cdrag_var)==0) then
33630 call MOM_error(FATAL,"CDRAG_FILE and CDRAG_VAR are required when using CDRAG_MAP.")
3364 endif
33650 allocate(cdrag_h(isd:ied,jsd:jed), source=0.0)
33660 allocate(CS%cdrag_u(IsdB:IedB,jsd:jed), source=0.0)
33670 allocate(CS%cdrag_v(isd:ied,JsdB:JedB), source=0.0)
33680 filename = trim(CS%inputdir) // trim(cdrag_file)
33690 call log_param(param_file, mdl, "INPUTDIR/CDRAG_FILE", filename)
33700 call MOM_read_data(filename, cdrag_var, cdrag_h, G%domain, scale=CS%cdrag)
33710 call pass_var(cdrag_h, G%domain)
33720 do j=js,je ; do I=is-1,ie ; if (G%mask2dCu(I,j) > 0) then
3373 CS%cdrag_u(I,j) = (G%mask2dT(i,j) * cdrag_h(i,j) + G%mask2dT(i+1,j) * cdrag_h(i+1,j)) / &
33740 (G%mask2dT(i,j) + G%mask2dT(i+1,j))
3375 endif ; enddo ; enddo
33760 do J=js-1,je ; do i=is,ie ; if (G%mask2dCv(i,J) > 0) then
3377 CS%cdrag_v(i,J) = (G%mask2dT(i,j) * cdrag_h(i,j) + G%mask2dT(i,j+1) * cdrag_h(i,j+1)) / &
33780 (G%mask2dT(i,j) + G%mask2dT(i,j+1))
3379 endif ; enddo ; enddo
33800 deallocate(cdrag_h)
3381 endif
33821 if (CS%BBL_use_tidal_bg) then
33830 allocate(CS%tideamp(isd:ied,jsd:jed), source=0.0)
33840 filename = trim(CS%inputdir) // trim(tideamp_file)
33850 call log_param(param_file, mdl, "INPUTDIR/TIDEAMP_FILE", filename)
33860 call MOM_read_data(filename, tideamp_var, CS%tideamp, G%domain, scale=US%m_to_Z*US%T_to_s)
33870 call pass_var(CS%tideamp,G%domain)
3388 endif
3389 endif
33901 if (CS%Channel_drag .or. CS%body_force_drag) then
3391663076 allocate(visc%Ray_u(IsdB:IedB,jsd:jed,nz), source=0.0)
3392667651 allocate(visc%Ray_v(isd:ied,JsdB:JedB,nz), source=0.0)
3393 !$omp target enter data map(to: visc%Ray_u, visc%Ray_v)
3394 CS%id_Ray_u = register_diag_field('ocean_model', 'Rayleigh_u', diag%axesCuL, &
33951 Time, 'Rayleigh drag velocity at u points', 'm s-1', conversion=GV%H_to_m*US%s_to_T)
3396 CS%id_Ray_v = register_diag_field('ocean_model', 'Rayleigh_v', diag%axesCvL, &
33971 Time, 'Rayleigh drag velocity at v points', 'm s-1', conversion=GV%H_to_m*US%s_to_T)
3398 endif
3399
3400
34011 if (CS%dynamic_viscous_ML) then
34020 allocate(visc%nkml_visc_u(IsdB:IedB,jsd:jed), source=0.0)
34030 allocate(visc%nkml_visc_v(isd:ied,JsdB:JedB), source=0.0)
3404 !$omp target enter data map(to: visc%nkml_visc_u, visc%nkml_visc_v)
3405
3406 CS%id_nkml_visc_u = register_diag_field('ocean_model', 'nkml_visc_u', &
34070 diag%axesCu1, Time, 'Number of layers in viscous mixed layer at u points', 'nondim')
3408 CS%id_nkml_visc_v = register_diag_field('ocean_model', 'nkml_visc_v', &
34090 diag%axesCv1, Time, 'Number of layers in viscous mixed layer at v points', 'nondim')
3410 endif
3411
34121 call register_restart_field_as_obsolete('Kd_turb','Kd_shear', restart_CS)
34131 call register_restart_field_as_obsolete('Kv_turb','Kv_shear', restart_CS)
3414
34151end subroutine set_visc_init
3416
3417!> This subroutine dellocates any memory in the set_visc control structure.
34181subroutine set_visc_end(visc, CS)
3419 type(vertvisc_type), intent(inout) :: visc !< A structure containing vertical viscosities and
3420 !! related fields. Elements are deallocated here.
3421 type(set_visc_CS), intent(inout) :: CS !< The control structure returned by a previous
3422 !! call to set_visc_init.
3423
34241 if (allocated(visc%bbl_thick_u)) then
3425 !$omp target exit data map(delete: visc%bbl_thick_u)
34261 deallocate(visc%bbl_thick_u)
3427 endif
34281 if (allocated(visc%bbl_thick_v)) then
3429 !$omp target exit data map(delete: visc%bbl_thick_v)
34301 deallocate(visc%bbl_thick_v)
3431 endif
34321 if (allocated(visc%kv_bbl_u)) then
3433 !$omp target exit data map(delete: visc%kv_bbl_u)
34341 deallocate(visc%kv_bbl_u)
3435 endif
34361 if (allocated(visc%kv_bbl_v)) then
3437 !$omp target exit data map(delete: visc%kv_bbl_v)
34381 deallocate(visc%kv_bbl_v)
3439 endif
34401 if (allocated(CS%bbl_u)) deallocate(CS%bbl_u)
34411 if (allocated(CS%bbl_v)) deallocate(CS%bbl_v)
34421 if (allocated(visc%Ray_u)) then
3443 !$omp target exit data map(delete: visc%Ray_u)
34441 deallocate(visc%Ray_u)
3445 endif
34461 if (allocated(visc%Ray_v)) then
3447 !$omp target exit data map(delete: visc%Ray_v)
34481 deallocate(visc%Ray_v)
3449 endif
34501 if (allocated(visc%nkml_visc_u)) then
3451 !$omp target exit data map(delete: visc%nkml_visc_u)
34520 deallocate(visc%nkml_visc_u)
3453 endif
34541 if (allocated(visc%nkml_visc_v)) then
3455 !$omp target exit data map(delete: visc%nkml_visc_v)
34560 deallocate(visc%nkml_visc_v)
3457 endif
34581 if (associated(visc%Kd_shear)) deallocate(visc%Kd_shear)
34591 if (associated(visc%Kv_slow)) deallocate(visc%Kv_slow)
34601 if (associated(visc%TKE_turb)) deallocate(visc%TKE_turb)
34611 if (associated(visc%Kv_shear)) deallocate(visc%Kv_shear)
34621 if (associated(visc%Kv_shear_Bu)) deallocate(visc%Kv_shear_Bu)
34631 if (allocated(visc%ustar_bbl)) deallocate(visc%ustar_bbl)
34641 if (allocated(visc%BBL_meanKE_loss)) deallocate(visc%BBL_meanKE_loss)
34651 if (allocated(visc%BBL_meanKE_loss_sqrtCd)) deallocate(visc%BBL_meanKE_loss_sqrtCd)
34661 if (allocated(visc%taux_shelf)) deallocate(visc%taux_shelf)
34671 if (allocated(visc%tauy_shelf)) deallocate(visc%tauy_shelf)
34681 if (allocated(visc%tbl_thick_shelf_u)) deallocate(visc%tbl_thick_shelf_u)
34691 if (allocated(visc%tbl_thick_shelf_v)) deallocate(visc%tbl_thick_shelf_v)
34701 if (allocated(visc%kv_tbl_shelf_u)) deallocate(visc%kv_tbl_shelf_u)
34711 if (allocated(visc%kv_tbl_shelf_v)) deallocate(visc%kv_tbl_shelf_v)
34721end subroutine set_visc_end
3473
3474!> \namespace mom_set_visc
3475!!
3476!! This would also be the module in which other viscous quantities that are flow-independent might be set.
3477!! This information is transmitted to other modules via a vertvisc type structure.
3478!!
3479!! The same code is used for the two velocity components, by indirectly referencing the velocities and
3480!! defining a handful of direction-specific defined variables.
3481
34820end module MOM_set_visc