← 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 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
1243 real, dimension(SZK_(GV)+1), intent(in) :: vol_below !< The volume below each interface, normalized by
1244 !! the full horizontal area of a velocity cell [Z ~> m]
1245 real, intent(in) :: Dp !< The larger of the two depths at the edge
1246 !! of a velocity cell [Z ~> m]
1247 real, intent(in) :: Dm !< The smaller of the two depths at the edge
1248 !! of a velocity cell [Z ~> m]
1249 real, dimension(SZK_(GV)+1), intent(out) :: L !< The fraction of the full cell width that is open at
1250 !! the depth of each interface [nondim]
1251 !$omp declare target
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 !$omp declare target
1259
1260768 nz = GV%ke
1261
1262768 slope = abs(Dp - Dm)
1263768 if (slope == 0.0) then
12640 L(1:nz) = 1.0 ; L(nz+1) = 0.0
1265 else
1266768 Vol_open = 0.5*slope
1267768 I_slope = 1.0 / slope
1268
1269768 L(nz+1) = 0.0
127058368 do K=nz,1,-1
127158368 if (vol_below(K) >= Vol_open) then ; L(K) = 1.0
1272 else
1273 ! With a uniformly sloping bottom, the calculation of L(K) is the solution of a simple quadratic equation.
127415312 L(K) = sqrt(2.0*vol_below(K)*I_slope)
1275 endif
1276 enddo
1277 endif
1278
1279768end subroutine find_L_open_uniform_slope
1280
1281!> Determine the normalized open length of each interface for concave bathymetry (from the ocean perspective)
1282!! using trigonometric expressions. In this case there can be two separate open regions.
12830pure subroutine find_L_open_concave_trigonometric(vol_below, D_vel, Dp, Dm, L, GV)
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 !$omp declare target
1295
1296 ! Local variables
1297 real :: crv ! crv is the curvature of the bottom depth across a
1298 ! cell, times the cell width squared [Z ~> m].
1299 real :: crv_3 ! crv/3 [Z ~> m].
1300 real :: slope ! The absolute value of the bottom depth slope across
1301 ! a cell times the cell width [Z ~> m].
1302 ! The following "volumes" have units of vertical heights because they are normalized
1303 ! by the full horizontal area of a velocity cell.
1304 real :: Vol_open ! The cell volume above which the face is fully is open [Z ~> m].
1305 real :: Vol_2_reg ! The cell volume above which there are two separate
1306 ! open areas that must be integrated [Z ~> m].
1307 real :: C24_crv ! 24/crv [Z-1 ~> m-1].
1308 real :: apb_4a, ax2_3apb ! Various nondimensional ratios of crv and slope [nondim].
1309 real :: a2x48_apb3, Iapb ! Combinations of crv (a) and slope (b) [Z-1 ~> m-1]
1310 real :: L0 ! A linear estimate of L appropriate for tiny volumes [nondim].
1311 real :: slope_crv ! The slope divided by the curvature [nondim]
1312 real :: tmp_val_m1_to_p1 ! A temporary variable [nondim]
1313 real, parameter :: C1_3 = 1.0/3.0, C1_12 = 1.0/12.0 ! Rational constants [nondim]
1314 real, parameter :: C2pi_3 = 8.0*atan(1.0)/3.0 ! An irrational constant, 2/3 pi. [nondim]
1315 integer :: K, nz
1316 !$omp declare target
1317
13180 nz = GV%ke
1319
1320 ! Each cell extends from x=-1/2 to 1/2, and has a topography
1321 ! given by D(x) = crv*x^2 + slope*x + D_vel - crv/12.
1322 !crv_3 = (Dp + Dm - 2.0*D_vel) ; crv = 3.0*crv_3
13230 crv_3 = (Dp + Dm - (2.0*D_vel)) ; crv = 3.0*crv_3
13240 slope = Dp - Dm
1325
1326 ! Calculate the volume above which the entire cell is open and the volume at which the
1327 ! equation that is solved for L changes because there are two separate open regions.
13280 if (slope >= crv) then
13290 Vol_open = D_vel - Dm ; Vol_2_reg = Vol_open
1330 else
13310 slope_crv = slope / crv
13320 Vol_open = 0.25*slope*slope_crv + C1_12*crv
13330 Vol_2_reg = 0.5*slope_crv**2 * (crv - C1_3*slope)
1334 endif
1335 ! Define some combinations of crv & slope for later use.
13360 C24_crv = 24.0/crv ; Iapb = 1.0/(crv+slope)
13370 apb_4a = (slope+crv)/(4.0*crv) ; a2x48_apb3 = (48.0*(crv*crv))*(Iapb**3)
13380 ax2_3apb = 2.0*C1_3*crv*Iapb
1339
13400 L(nz+1) = 0.0
1341 ! Determine the normalized open length (L) at each interface.
13420 do K=nz,1,-1
13430 if (vol_below(K) >= Vol_open) then ! The whole cell is open.
13440 L(K) = 1.0
13450 elseif (vol_below(K) < Vol_2_reg) then
1346 ! In this case, there is a contiguous open region and
1347 ! vol_below(K) = 0.5*L^2*(slope + crv/3*(3-4L)).
13480 if (a2x48_apb3*vol_below(K) < 1e-8) then ! Could be 1e-7?
1349 ! There is a very good approximation here for massless layers.
1350 !L0 = sqrt(2.0*vol_below(K)*Iapb) ; L(K) = L0*(1.0 + ax2_3apb*L0)
13510 L0 = sqrt(2.0*vol_below(K)*Iapb) ; L(K) = L0*(1.0 + (ax2_3apb*L0))
1352 else
1353 !L(K) = apb_4a * (1.0 - &
1354 ! 2.0 * cos(C1_3*acos(a2x48_apb3*vol_below(K) - 1.0) - C2pi_3))
1355 L(K) = apb_4a * (1.0 - &
13560 2.0 * cos(C1_3*acos((a2x48_apb3*vol_below(K)) - 1.0) - C2pi_3))
1357 endif
1358 ! To check the answers.
1359 ! Vol_err = 0.5*(L(K)*L(K))*(slope + crv_3*(3.0-4.0*L(K))) - vol_below(K)
1360 else ! There are two separate open regions.
1361 ! vol_below(K) = slope^2/4crv + crv/12 - (crv/12)*(1-L)^2*(1+2L)
1362 ! At the deepest volume, L = slope/crv, at the top L = 1.
1363 ! L(K) = 0.5 - cos(C1_3*acos(1.0 - C24_crv*(Vol_open - vol_below(K))) - C2pi_3)
13640 tmp_val_m1_to_p1 = 1.0 - C24_crv*(Vol_open - vol_below(K))
13650 tmp_val_m1_to_p1 = max(-1., min(1., tmp_val_m1_to_p1))
13660 L(K) = 0.5 - cos(C1_3*acos(tmp_val_m1_to_p1) - C2pi_3)
1367 ! To check the answers.
1368 ! Vol_err = Vol_open - 0.25*crv_3*(1.0+2.0*L(K)) * (1.0-L(K))**2 - vol_below(K)
1369 endif
1370 enddo ! k loop to determine L(K) in the concave case
1371
13720end subroutine find_L_open_concave_trigonometric
1373
1374
1375
1376!> Determine the normalized open length of each interface for concave bathymetry (from the ocean perspective) using
1377!! iterative methods to solve the relevant cubic equations. In this case there can be two separate open regions.
137811664pure subroutine find_L_open_concave_iterative(vol_below, D_vel, Dp, Dm, L, GV)
1379 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
1380 real, dimension(SZK_(GV)+1), intent(in) :: vol_below !< The volume below each interface, normalized by
1381 !! the full horizontal area of a velocity cell [Z ~> m]
1382 real, intent(in) :: D_vel !< The average bottom depth at a velocity point [Z ~> m]
1383 real, intent(in) :: Dp !< The larger of the two depths at the edge
1384 !! of a velocity cell [Z ~> m]
1385 real, intent(in) :: Dm !< The smaller of the two depths at the edge
1386 !! of a velocity cell [Z ~> m]
1387 real, dimension(SZK_(GV)+1), intent(out) :: L !< The fraction of the full cell width that is open at
1388 !! the depth of each interface [nondim]
1389 !$omp declare target
1390
1391 ! Local variables
1392 real :: crv ! crv is the curvature of the bottom depth across a
1393 ! cell, times the cell width squared [Z ~> m].
1394 real :: crv_3 ! crv/3 [Z ~> m].
1395 real :: slope ! The absolute value of the bottom depth slope across
1396 ! a cell times the cell width [Z ~> m].
1397
1398 ! The following "volumes" have units of vertical heights because they are normalized
1399 ! by the full horizontal area of a velocity cell.
1400 real :: Vol_open ! The cell volume above which the face is fully is open [Z ~> m].
1401 real :: Vol_2_reg ! The cell volume above which there are two separate
1402 ! open areas that must be integrated [Z ~> m].
1403 real :: L_2_reg ! The value of L when vol_below is Vol_2_reg [nondim]
1404 real :: vol_inflect_1 ! The volume at which there is an inflection point in the expression
1405 ! relating L to vol_err when there is a single open region [Z ~> m]
1406 real :: vol_inflect_2 ! The volume at which there is an inflection point in the expression
1407 ! relating L to vol_err when there are two open regions [Z ~> m]
1408
1409 real :: L_inflect_1 ! The value of L that sits at an inflection point in the expression
1410 ! relating L to vol_err when there is a single open region [nondim]
1411 real :: L_inflect_2 ! The value of L that sits at an inflection point in the expression
1412 ! relating L to vol_err when there is are two open regions [nondim]
1413 real :: L_max, L_min ! Maximum and minimum bounds on the solution for L for an interface [nondim]
1414 real :: vol_err ! The difference between the volume below an interface for a given value
1415 ! of L and the target value [Z ~> m]
1416 real :: dVol_dL ! The partial derivative of the volume below with L [Z ~> m]
1417 real :: vol_err_max ! The value of vol_err when L is L_max [Z ~> m]
1418
1419 ! The following combinations of slope and crv are reused across layers, and hence are pre-calculated
1420 ! for efficiency. All are non-negative.
1421 real :: Icrvpslope ! The inverse of the sum of crv and slope [Z-1 ~> m-1]
1422 real :: slope_crv ! The slope divided by the curvature [nondim]
1423 ! These are only used if the slope exceeds or matches the curvature.
1424 real :: smc ! The slope minus the curvature [Z ~> m]
1425 real :: C3c_m_s ! 3 times the curvature minus the slope [Z ~> m]
1426 real :: I_3c_m_s ! The inverse of 3 times the curvature minus the slope [Z-1 ~> m-1]
1427 ! These are only used if the curvature exceeds the slope.
1428 real :: C4_crv ! The inverse of a quarter of the curvature [Z-1 ~> m-1]
1429 real :: sxcms_c ! The slope times the difference between the curvature and slope
1430 ! divided by the curvature [Z ~> m]
1431 real :: slope2_4crv ! A quarter of the slope squared divided by the curvature [Z ~> m]
1432 real :: I_3s_m_c ! The inverse of 3 times the slope minus the curvature [Z-1 ~> m-1]
1433 real :: C3s_m_c ! 3 times the slope minus the curvature [Z ~> m]
1434
1435 real, parameter :: C1_3 = 1.0 / 3.0, C1_12 = 1.0 / 12.0 ! Rational constants [nondim]
1436 integer :: K, nz, itt
1437 integer, parameter :: max_itt = 10
1438 !$omp declare target
1439
144011664 nz = GV%ke
1441
1442 ! Each cell extends from x=-1/2 to 1/2, and has a topography
1443 ! given by D(x) = crv*x^2 + slope*x + D_vel - crv/12.
1444
144511664 crv_3 = (Dp + Dm - 2.0*D_vel) ; crv = 3.0*crv_3
144611664 slope = Dp - Dm
1447
1448 ! Calculate the volume above which the entire cell is open and the volume at which the
1449 ! equation that is solved for L changes because there are two separate open regions.
145011664 if (slope >= crv) then
14519024 Vol_open = D_vel - Dm ; Vol_2_reg = Vol_open
14529024 L_2_reg = 1.0
14539024 if (crv + slope >= 4.0*crv) then
14545544 L_inflect_1 = 1.0 ; Vol_inflect_1 = Vol_open
1455 else
14563480 slope_crv = slope / crv
14573480 L_inflect_1 = 0.25 + 0.25*slope_crv
14583480 vol_inflect_1 = 0.25*C1_12 * ((slope_crv + 1.0)**2 * (slope + crv))
1459 endif
1460 ! Precalculate some combinations of crv & slope for later use.
14619024 smc = slope - crv
14629024 C3c_m_s = 3.0*crv - slope
14639024 if (C3c_m_s > 2.0*smc) I_3c_m_s = 1.0 / C3c_m_s
1464 else
14652640 slope_crv = slope / crv
14662640 Vol_open = 0.25*slope*slope_crv + C1_12*crv
14672640 Vol_2_reg = 0.5*slope_crv**2 * (crv - C1_3*slope)
14682640 L_2_reg = slope_crv
1469
1470 ! The inflection point is useful to know because below the inflection point
1471 ! Newton's method converges monotonically from above and conversely above it.
1472 ! These are the inflection point values of L and vol_below with a single open segment.
14732640 vol_inflect_1 = 0.25*C1_12 * ((slope_crv + 1.0)**2 * (slope + crv))
14742640 L_inflect_1 = 0.25 + 0.25*slope_crv
1475 ! These are the inflection point values of L and vol_below when there are two open segments.
1476 ! Vol_inflect_2 = Vol_open - 0.125 * crv_3, which is equivalent to:
14772640 vol_inflect_2 = 0.25*slope*slope_crv + 0.125*crv_3
14782640 L_inflect_2 = 0.5
1479 ! Precalculate some combinations of crv & slope for later use.
14802640 C4_crv = 4.0 / crv
14812640 slope2_4crv = 0.25 * slope * slope_crv
14822640 sxcms_c = slope_crv*(crv - slope)
14832640 C3s_m_c = 3.0*slope - crv
14842640 if (C3s_m_c > 2.0*sxcms_c) I_3s_m_c = 1.0 / C3s_m_c
1485 endif
1486 ! Define some combinations of crv & slope for later use.
148711664 Icrvpslope = 1.0 / (crv+slope)
1488
148911664 L(nz+1) = 0.0
1490 ! Determine the normalized open length (L) at each interface.
1491886464 do K=nz,1,-1
1492886464 if (vol_below(K) >= Vol_open) then ! The whole cell is open.
1493553728 L(K) = 1.0
1494321072 elseif (vol_below(K) < Vol_2_reg) then
1495 ! In this case, there is a single contiguous open region from x=1/2-L to 1/2.
1496 ! Changing the horizontal variable in the expression from D(x) to D(L) gives:
1497 ! x(L) = 1/2 - L
1498 ! D(L) = crv*(0.5 - L)^2 + slope*(0.5 - L) + D_vel - crv/12
1499 ! D(L) = crv*L^2 - crv*L + crv/4 + slope*(1/2 - L) + D_vel - crv/12
1500 ! D(L) = crv*L^2 - (slope+crv)*L + slope/2 + D_vel + crv/6
1501 ! D(0) = slope/2 + D_vel + crv/6 = (Dp - Dm)/2 + D_vel + (Dp + Dm - 2*D_vel)/2 = Dp
1502 ! D(1) = crv - slope - crv + slope/2 + Dvel + crv/6 = D_vel - slope/2 + crv/6 = Dm
1503 !
1504 ! vol_below = integral(y = 0 to L) D(y) dy - L * D(L)
1505 ! = crv/3*L^3 - (slope+crv)/2*L^2 + (slope/2 + D_vel + crv/6)*L -
1506 ! (crv*L^2 - (slope+crv)*L + slope/2 + D_vel + crv/6) * L
1507 ! = -2/3 * crv * L^3 + 1/2 * (slope+crv) * L^2
1508 ! vol_below(K) = 0.5*L(K)**2*(slope + crv_3*(3-4*L(K)))
1509 ! L(K) is between L(K+1) and slope_crv.
1510319080 L_max = min(L_2_reg, 1.0)
1511319080 if (vol_below(K) <= vol_inflect_1) L_max = min(L_max, L_inflect_1)
1512
1513319080 L_min = L(K+1)
1514319080 if (vol_below(K) >= vol_inflect_1) L_min = max(L_min, L_inflect_1)
1515
1516 ! Ignoring the cubic term gives an under-estimate but is very accurate for near bottom
1517 ! layers, so use this as a potential floor.
1518319080 if (2.0*vol_below(K)*Icrvpslope > L_min**2) L_min = sqrt(2.0*vol_below(K)*Icrvpslope)
1519
1520 ! Start with L_min in most cases.
1521319080 L(k) = L_min
1522
1523319080 if (vol_below(K) <= vol_inflect_1) then
1524 ! Starting with L_min below L_inflect_1, only the first overshooting iteration of Newton's
1525 ! method needs bounding.
1526318384 L(k) = L_min
1527318384 vol_err = 0.5*L(K)**2 * (slope + crv*(1.0 - 4.0*C1_3*L(K))) - vol_below(K)
1528 ! If vol_err is 0 or positive (perhaps due to roundoff in L(K+1)), L_min is already the best solution.
1529318384 if (vol_err < 0.0) then
1530318384 dVol_dL = L(K) * (slope + crv*(1.0 - 2.0*L(k)))
1531318384 if (L(K)*dVol_dL > vol_err + L_max*dVol_dL) then
153224 L(K) = L_max
1533 else
1534318360 L(K) = L(K) - (vol_err / dVol_dL)
1535 endif
1536
1537 ! Subsequent iterations of Newton's method do not need bounds.
1538937427 do itt=1,max_itt
1539937427 vol_err = 0.5*L(K)**2 * (slope + crv*(1.0 - 4.0*C1_3*L(K))) - vol_below(K)
1540937427 dVol_dL = L(K) * (slope + crv*(1.0 - 2.0*L(k)))
1541937427 if (abs(vol_err) < max(1.0e-15*L(K), 1.0e-25)*dVol_dL) exit
1542619043 L(K) = L(K) - (vol_err / dVol_dL)
1543 enddo
1544 endif
1545 else ! (vol_below(K) > vol_inflect_1)
1546 ! Iteration from below converges monotonically, but we need to deal with the case where we are
1547 ! close to the peak of the topography and Newton's method mimics the convergence of bisection.
1548
1549 ! Evaluate the error when L(K) = L_min as a possible first guess.
1550696 L(k) = L_min
1551696 vol_err = 0.5*L(K)**2 * (slope + crv*(1.0 - 4.0*C1_3*L(K))) - vol_below(K)
1552 ! If vol_err is 0 or positive (perhaps due to roundoff in L(K+1)), L_min is already the best solution.
1553696 if (vol_err < 0.0) then
1554
1555 ! These two upper estimates deal with the possibility that this point may be near
1556 ! the upper extrema, where the error term might be approximately parabolic and
1557 ! Newton's method would converge slowly like simple bisection.
1558696 if (slope < crv) then
1559 ! if ((L_2_reg - L_min)*(3.0*slope - crv) > 2.0*slope_crv*(crv-slope)) then
1560168 if ((L_2_reg - L_min)*C3s_m_c > 2.0*sxcms_c) then
1561 ! There is a decent upper estimate of L from the approximate quadratic equation found
1562 ! by examining the error expressions at L ~= L_2_reg and ignoring the cubic term.
1563 L_max = (slope_crv*(2.0*slope) - sqrt(sxcms_c**2 + &
156448 2.0*C3s_m_c*(Vol_2_reg - vol_below(K))) ) * I_3s_m_c
1565 ! The line above is equivalent to:
1566 ! L_max = (slope_crv*(2.0*slope) - sqrt(slope_crv**2*(crv-slope)**2 + &
1567 ! 2.0*(3.0*slope - crv)*(Vol_2_reg - vol_below(K))) ) / &
1568 ! (3.0*slope - crv)
1569 else
1570120 L_max = slope_crv
1571 endif
1572 else ! (slope >= crv)
1573528 if ((1.0 - L_min)*C3c_m_s > 2.0*smc) then
1574 ! There is a decent upper estimate of L from the approximate quadratic equation found
1575 ! by examining the error expressions at L ~= 1 and ignoring the cubic term.
1576168 L_max = ( 2.0*crv - sqrt(smc**2 + 2.0*C3c_m_s * (Vol_open - vol_below(K))) ) * I_3c_m_s
1577 ! The line above is equivalent to:
1578 ! L_max = ( 2.0*crv - sqrt((slope - crv)**2 + 2.0*(3.0*crv - slope) * (Vol_open - vol_below(K))) ) / &
1579 ! (3.0*crv - slope)
1580 else
1581360 L_max = 1.0
1582 endif
1583 endif
1584696 Vol_err_max = 0.5*L_max**2 * (slope + crv*(1.0 - 4.0*C1_3*L_max)) - vol_below(K)
1585 ! if (Vol_err_max < 0.0) call MOM_error(FATAL, &
1586 ! "Vol_err_max should never be negative in find_L_open_concave_iterative.")
1587696 if ((Vol_err_max < abs(Vol_err)) .and. (L_max < 1.0)) then
1588 ! Start with 1 bounded Newton's method step from L_max
1589264 dVol_dL = L_max * (slope + crv*(1.0 - 2.0*L_max))
1590264 L(K) = max(L_min, L_max - (vol_err_max / dVol_dL) )
1591 ! else ! Could use the fact that Vol_err is known to take an iteration?
1592 endif
1593
1594 ! Subsequent iterations of Newton's method do not need bounds.
15952976 do itt=1,max_itt
15962976 vol_err = 0.5*L(K)**2 * (slope + crv*(1.0 - 4.0*C1_3*L(K))) - vol_below(K)
15972976 dVol_dL = L(K) * (slope + crv*(1.0 - 2.0*L(k)))
15982976 if (abs(vol_err) < max(1.0e-15*L(K), 1.0e-25)*dVol_dL) exit
15992280 L(K) = L(K) - (vol_err / dVol_dL)
1600 enddo
1601 endif
1602
1603 endif
1604
1605 ! To check the answers.
1606 ! Vol_err = 0.5*(L(K)*L(K))*(slope + crv_3*(3.0-4.0*L(K))) - vol_below(K)
1607 else ! There are two separate open regions.
1608 ! vol_below(K) = slope^2/(4*crv) + crv/12 - (crv/12)*(1-L)^2*(1+2L)
1609 ! At the deepest volume, L = slope/crv, at the top L = 1.
1610
1611 ! To check the answers.
1612 ! Vol_err = Vol_open - 0.25*crv_3*(1.0+2.0*L(K)) * (1.0-L(K))**2 - vol_below(K)
1613 ! or equivalently:
1614 ! Vol_err = Vol_open - 0.25*crv_3*(3.0-2.0*(1.0-L(K))) * (1.0-L(K))**2 - vol_below(K)
1615 ! ! Note that: Vol_open = 0.25*slope*slope_crv + C1_12*crv
1616 ! 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)
1617 ! Vol_err = 0.25*crv_3*L(K)**2*( 3.0 - 2.0*L(K) ) + 0.25*slope*slope_crv - vol_below(K)
1618
1619 ! Derivation of the L_max limit below:
1620 ! Vol_open - vol_below(K) = 0.25*crv_3*(3.0-2.0*(1.0-L(K))) * (1.0-L(K))**2
1621 ! (3.0-2.0*(1.0-L(K))) * (1.0-L(K))**2 = (Vol_open - vol_below(K)) / (0.25*crv_3)
1622 ! When 1-L(K) << 1:
1623 ! 3.0 * (1.0-L_max)**2 = (Vol_open - vol_below(K)) / (0.25*crv_3)
1624 ! (1.0-L_max)**2 = (Vol_open - vol_below(K)) / (0.25*crv)
1625
1626 ! Derivation of the L_min limit below:
1627 ! Vol_err = 0.25*crv_3*L(K)**2*( 3.0 - 2.0*L(K) ) + 0.25*slope*slope_crv - vol_below(K)
1628 ! crv*L(K)**2*( 1.0 - 2.0*C1_3*L(K) ) = 4.0*vol_below(K) - slope*slope_crv
1629 ! When L(K) << 1:
1630 ! crv*L_min**2 = 4.0*vol_below(K) - slope*slope_crv
1631 ! L_min = sqrt((4.0*vol_below(K) - slope*slope_crv)/crv)
1632 ! Noting that L(K) >= slope_crv, when L(K)-slope_crv << 1:
1633 ! (crv + 2.0*C1_3*slope)*L_min**2 = 4.0*vol_below(K) - slope*slope_crv
1634 ! L_min = sqrt((4.0*vol_below(K) - slope*slope_crv)/(crv + 2.0*C1_3*slope))
1635
16361992 if (vol_below(K) <= Vol_inflect_2) then
1637 ! Newton's Method would converge monotonically from above, but overshoot from below.
16381776 L_min = max(L(K+1), L_2_reg) ! L_2_reg = slope_crv
1639 ! This under-estimate of L(K) is accurate for L ~= slope_crv:
16401776 if ((4.0*vol_below(K) - slope*slope_crv) > (crv + 2.0*C1_3*slope)*L_min**2) &
1641960 L_min = max(L_min, sqrt((4.0*vol_below(K) - slope*slope_crv) / (crv + 2.0*C1_3*slope)))
16421776 L_max = 0.5 ! = L_inflect_2
1643
1644 ! Starting with L_min below L_inflect_2, only the first overshooting iteration of Newton's
1645 ! method needs bounding.
16461776 L(k) = L_min
16471776 Vol_err = crv_3*L(K)**2*( 0.75 - 0.5*L(K) ) + (slope2_4crv - vol_below(K))
1648
1649 ! If vol_err is 0 or positive (perhaps due to roundoff in L(K+1)), L_min is already the best solution.
16501776 if (vol_err < 0.0) then
16511776 dVol_dL = 0.5*crv * (L(K) * (1.0 - L(K)))
16521776 if (L(K)*dVol_dL >= vol_err + L_max*dVol_dL) then
16530 L(K) = L_max
1654 else
16551776 L(K) = L(K) - (vol_err / dVol_dL)
1656 endif
1657 ! Subsequent iterations of Newton's method do not need bounds.
16585619 do itt=1,max_itt
16595619 Vol_err = crv_3 * (L(K)**2 * (0.75 - 0.5*L(K))) + (slope2_4crv - vol_below(K))
16605619 dVol_dL = 0.5*crv * (L(K)*(1.0 - L(K)))
16615619 if (abs(vol_err) < max(1.0e-15*L(K), 1.0e-25)*dVol_dL) exit
16623843 L(K) = L(K) - (vol_err / dVol_dL)
1663 enddo
1664 endif
1665 else ! (vol_below(K) > Vol_inflect_2)
1666 ! Newton's Method would converge monotonically from below, but overshoots from above, and
1667 ! we may need to deal with the case where we are close to the peak of the topography.
1668216 L_min = max(L(K+1), 0.5)
1669216 L(k) = L_min
1670
1671216 Vol_err = crv_3 * (L(K)**2 * ( 0.75 - 0.5*L(K))) + (slope2_4crv - vol_below(K))
1672 ! If vol_err is 0 or positive (perhaps due to roundoff in L(K+1)), L(k) is already the best solution.
1673216 if (Vol_err < 0.0) then
1674 ! This over-estimate of L(K) is accurate for L ~= 1:
1675216 L_max = 1.0 - sqrt( (Vol_open - vol_below(K)) * C4_crv )
1676216 Vol_err_max = crv_3 * (L_max**2 * ( 0.75 - 0.5*L_max)) + (slope2_4crv - vol_below(K))
1677 ! if (Vol_err_max < 0.0) call MOM_error(FATAL, &
1678 ! "Vol_err_max should never be negative in find_L_open_concave_iterative.")
1679216 if ((Vol_err_max < abs(Vol_err)) .and. (L_max < 1.0)) then
1680 ! Start with 1 bounded Newton's method step from L_max
1681216 dVol_dL = 0.5*crv * (L_max * (1.0 - L_max))
1682216 L(K) = max(L_min, L_max - (vol_err_max / dVol_dL) )
1683 ! else ! Could use the fact that Vol_err is known to take an iteration?
1684 endif
1685
1686 ! Subsequent iterations of Newton's method do not need bounds.
1687816 do itt=1,max_itt
1688816 Vol_err = crv_3 * (L(K)**2 * ( 0.75 - 0.5*L(K))) + (slope2_4crv - vol_below(K))
1689816 dVol_dL = 0.5*crv * (L(K) * (1.0 - L(K)))
1690816 if (abs(vol_err) < max(1.0e-15*L(K), 1.0e-25)*dVol_dL) exit
1691600 L(K) = L(K) - (vol_err / dVol_dL)
1692 enddo
1693 endif
1694 endif
1695
1696 endif
1697 enddo ! k loop to determine L(K) in the concave case
1698
169911664end subroutine find_L_open_concave_iterative
1700
1701
1702
1703!> Test the validity the normalized open lengths of each interface for concave bathymetry (from the ocean perspective)
1704!! by evaluating and returing the relevant cubic equations.
17050pure subroutine test_L_open_concave(vol_below, D_vel, Dp, Dm, L, vol_err, GV)
1706 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
1707 real, dimension(SZK_(GV)+1), intent(in) :: vol_below !< The volume below each interface, normalized by
1708 !! the full horizontal area of a velocity cell [Z ~> m]
1709 real, intent(in) :: D_vel !< The average bottom depth at a velocity point [Z ~> m]
1710 real, intent(in) :: Dp !< The larger of the two depths at the edge
1711 !! of a velocity cell [Z ~> m]
1712 real, intent(in) :: Dm !< The smaller of the two depths at the edge
1713 !! of a velocity cell [Z ~> m]
1714 real, dimension(SZK_(GV)+1), intent(in) :: L !< The fraction of the full cell width that is open at
1715 !! the depth of each interface [nondim]
1716 real, dimension(SZK_(GV)+1), intent(out) :: vol_err !< The difference between vol_below and the
1717 !! value obtained from using L in the cubic equation [Z ~> m]
1718 !$omp declare target
1719
1720 ! Local variables
1721 real :: crv ! crv is the curvature of the bottom depth across a
1722 ! cell, times the cell width squared [Z ~> m].
1723 real :: crv_3 ! crv/3 [Z ~> m].
1724 real :: slope ! The absolute value of the bottom depth slope across
1725 ! a cell times the cell width [Z ~> m].
1726
1727 ! The following "volumes" have units of vertical heights because they are normalized
1728 ! by the full horizontal area of a velocity cell.
1729 real :: Vol_open ! The cell volume above which the face is fully is open [Z ~> m].
1730 real :: Vol_2_reg ! The cell volume above which there are two separate
1731 ! open areas that must be integrated [Z ~> m].
1732 real :: L_2_reg ! The value of L when vol_below is Vol_2_reg [nondim]
1733
1734 ! The following combinations of slope and crv are reused across layers, and hence are pre-calculated
1735 ! for efficiency. All are non-negative.
1736 real :: slope_crv ! The slope divided by the curvature [nondim]
1737 ! These are only used if the curvature exceeds the slope.
1738 real :: slope2_4crv ! A quarter of the slope squared divided by the curvature [Z ~> m]
1739
1740 real, parameter :: C1_3 = 1.0 / 3.0, C1_12 = 1.0 / 12.0 ! Rational constants [nondim]
1741 integer :: K, nz
1742 !$omp declare target
1743
17440 nz = GV%ke
1745
1746 ! Each cell extends from x=-1/2 to 1/2, and has a topography
1747 ! given by D(x) = crv*x^2 + slope*x + D_vel - crv/12.
1748
17490 crv_3 = (Dp + Dm - 2.0*D_vel) ; crv = 3.0*crv_3
17500 slope = Dp - Dm
1751
1752 ! Calculate the volume above which the entire cell is open and the volume at which the
1753 ! equation that is solved for L changes because there are two separate open regions.
17540 if (slope >= crv) then
17550 Vol_open = D_vel - Dm ; Vol_2_reg = Vol_open
17560 L_2_reg = 1.0
17570 if (crv + slope >= 4.0*crv) then
17580 slope_crv = 1.0
1759 else
17600 slope_crv = slope / crv
1761 endif
1762 else
17630 slope_crv = slope / crv
17640 Vol_open = 0.25*slope*slope_crv + C1_12*crv
17650 Vol_2_reg = 0.5*slope_crv**2 * (crv - C1_3*slope)
17660 L_2_reg = slope_crv
1767 endif
17680 slope2_4crv = 0.25 * slope * slope_crv
1769
1770 ! Determine the volume error based on the normalized open length (L) at each interface.
17710 Vol_err(nz+1) = 0.0
17720 do K=nz,1,-1
17730 if (L(K) >= 1.0) then
17740 Vol_err(K) = max(Vol_open - vol_below(K), 0.0)
17750 elseif (L(K) <= L_2_reg) then
17760 vol_err(K) = 0.5*L(K)**2 * (slope + crv*(1.0 - 4.0*C1_3*L(K))) - vol_below(K)
1777 else ! There are two separate open regions.
17780 Vol_err(K) = crv_3 * (L(K)**2 * ( 0.75 - 0.5*L(K))) + (slope2_4crv - vol_below(K))
1779 endif
1780 enddo ! k loop to determine L(K) in the concave case
1781
17820end subroutine test_L_open_concave
1783
1784
1785!> Determine the normalized open length of each interface for convex bathymetry (from the ocean
1786!! perspective) using Newton's method iterations. In this case there is a single open region
1787!! with the minimum depth at one edge of the cell.
1788105672pure subroutine find_L_open_convex(vol_below, D_vel, Dp, Dm, L, GV, US, CS)
1789 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
1790 real, dimension(SZK_(GV)+1), intent(in) :: vol_below !< The volume below each interface, normalized by
1791 !! the full horizontal area of a velocity cell [Z ~> m]
1792 real, intent(in) :: D_vel !< The average bottom depth at a velocity point [Z ~> m]
1793 real, intent(in) :: Dp !< The larger of the two depths at the edge
1794 !! of a velocity cell [Z ~> m]
1795 real, intent(in) :: Dm !< The smaller of the two depths at the edge
1796 !! of a velocity cell [Z ~> m]
1797 real, dimension(SZK_(GV)+1), intent(out) :: L !< The fraction of the full cell width that is open at
1798 !! the depth of each interface [nondim]
1799 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1800 type(set_visc_CS), intent(in) :: CS !< The control structure returned by a previous
1801 !! call to set_visc_init.
1802 !$omp declare target
1803
1804 ! Local variables
1805 real :: crv ! crv is the curvature of the bottom depth across a
1806 ! cell, times the cell width squared [Z ~> m].
1807 real :: crv_3 ! crv/3 [Z ~> m].
1808 real :: slope ! The absolute value of the bottom depth slope across
1809 ! a cell times the cell width [Z ~> m].
1810 ! All of the following "volumes" have units of vertical heights because they are normalized
1811 ! by the full horizontal area of a velocity cell.
1812 real :: Vol_err ! The error in the volume with the latest estimate of
1813 ! L, or the error for the interface below [Z ~> m].
1814 real :: Vol_quit ! The volume error below which to quit iterating [Z ~> m].
1815 real :: Vol_tol ! A volume error tolerance [Z ~> m].
1816 real :: Vol_open ! The cell volume above which the face is fully open [Z ~> m].
1817 real :: Vol_direct ! With less than Vol_direct [Z ~> m], there is a direct
1818 ! solution of a cubic equation for L.
1819 real :: Vol_err_max ! The volume error for the upper bound on the correct value for L [Z ~> m]
1820 real :: Vol_err_min ! The volume error for the lower bound on the correct value for L [Z ~> m]
1821 real :: Vol_0 ! A deeper volume with known width L0 [Z ~> m].
1822 real :: dVol ! vol - Vol_0 [Z ~> m].
1823 real :: dV_dL2 ! The partial derivative of volume with L squared
1824 ! evaluated at L=L0 [Z ~> m].
1825 real :: L_direct ! The value of L above volume Vol_direct [nondim].
1826 real :: L_max, L_min ! Upper and lower bounds on the correct value for L [nondim].
1827 real :: L0 ! The value of L above volume Vol_0 [nondim].
1828 real :: Iapb, Ibma_2 ! Combinations of crv (a) and slope (b) [Z-1 ~> m-1]
1829 real :: C24_crv ! 24/crv [Z-1 ~> m-1].
1830 real :: curv_tol ! Numerator of curvature cubed, used to estimate
1831 ! accuracy of a single L(:) Newton iteration [Z5 ~> m5]
1832 real, parameter :: C1_3 = 1.0/3.0, C1_6 = 1.0/6.0 ! Rational constants [nondim]
1833 logical :: use_L0, do_one_L_iter ! Control flags for L(:) Newton iteration
1834 integer :: K, nz, itt
1835 integer, parameter:: maxitt = 20
1836 !$omp declare target
1837
1838105672 nz = GV%ke
1839
1840 ! Each cell extends from x=-1/2 to 1/2, and has a topography
1841 ! given by D(x) = crv*x^2 + slope*x + D_vel - crv/12.
1842105672 crv_3 = (Dp + Dm - 2.0*D_vel) ; crv = 3.0*crv_3
1843105672 slope = Dp - Dm
1844
1845 ! Calculate the volume above which the entire cell is open and the volume at which the
1846 ! equation that is solved for L changes because there is a direct solution.
1847105672 Vol_open = D_vel - Dm
1848105672 if (slope >= -crv) then
184989760 Iapb = 1.0e30*US%Z_to_m ; if (slope+crv /= 0.0) Iapb = 1.0/(crv+slope)
185089760 Vol_direct = 0.0 ; L_direct = 0.0 ; C24_crv = 0.0
1851 else
185215912 C24_crv = 24.0/crv ; Iapb = 1.0/(crv+slope)
185315912 L_direct = 1.0 + slope/crv ! L_direct < 1 because crv < 0
185415912 Vol_direct = -C1_6*crv*L_direct**3
1855 endif
1856105672 Ibma_2 = 2.0 / (slope - crv)
1857
1858105672 if (CS%answer_date < 20190101) Vol_quit = (0.9*GV%Angstrom_Z + GV%dZ_subroundoff)
1859
1860105672 L(nz+1) = 0.0 ; Vol_err = 0.0
1861 ! Determine the normalized open length (L) at each interface.
18628031072 do K=nz,1,-1
18638031072 if (vol_below(K) >= Vol_open) then
18645431235 L(K) = 1.0
18652494165 elseif (vol_below(K) <= Vol_direct) then
1866351360 if (CS%answer_date < 20260501) then
18670 L(K) = (-0.25*C24_crv*vol_below(K))**C1_3
1868 else
1869351360 L(K) = cuberoot(-0.25*C24_crv*vol_below(K))
1870 endif
1871 else
1872 ! x_R is at 1/2 but x_L is in the interior & L is found by iteratively solving
1873 ! vol_below(K) = 0.5*L^2*(slope + crv/3*(3-4L))
1874
1875 ! Vol_err = 0.5*(L(K+1)*L(K+1))*(slope + crv_3*(3.0-4.0*L(K+1))) - vol_below(K+1)
1876 ! Change to ...
1877 ! if (min(vol_below(K+1) + Vol_err, vol_below(K)) <= Vol_direct) then ?
18782142805 if (vol_below(K+1) + Vol_err <= Vol_direct) then
187998556 L0 = L_direct ; Vol_0 = Vol_direct
1880 else
18812044249 L0 = L(K+1) ; Vol_0 = vol_below(K+1) + Vol_err
1882 ! Change to Vol_0 = min(vol_below(K+1) + Vol_err, vol_below(K)) ?
1883 endif
1884
1885 ! Try a relatively simple solution that usually works well
1886 ! for massless layers.
18872142805 dV_dL2 = 0.5*(slope+crv) - crv*L0 ; dVol = (vol_below(K)-Vol_0)
1888 ! dV_dL2 = 0.5*(slope+crv) - crv*L0 ; dVol = max(vol_below(K)-Vol_0, 0.0)
1889
18902142805 use_L0 = .false.
18912142805 do_one_L_iter = .false.
18922142805 if (CS%answer_date < 20190101) then
1893 curv_tol = GV%Angstrom_Z*dV_dL2**2 &
18940 * (0.25 * dV_dL2 * GV%Angstrom_Z - crv * L0 * dVol)
18950 do_one_L_iter = (crv * crv * dVol**3) < curv_tol
1896 else
1897 ! The following code is more robust when GV%Angstrom_H=0, but
1898 ! it changes answers.
18992142805 use_L0 = (dVol <= 0.)
1900
19012142805 Vol_tol = max(0.5 * GV%Angstrom_Z + GV%dZ_subroundoff, 1e-14 * vol_below(K))
19022142805 Vol_quit = max(0.9 * GV%Angstrom_Z + GV%dZ_subroundoff, 1e-14 * vol_below(K))
1903
1904 curv_tol = Vol_tol * dV_dL2**2 &
19052142805 * (dV_dL2 * Vol_tol - 2.0 * crv * L0 * dVol)
19062142805 do_one_L_iter = (crv * crv * dVol**3) < curv_tol
1907 endif
1908
19092142805 if (use_L0) then
19100 L(K) = L0
19110 Vol_err = 0.5*(L(K)*L(K))*(slope + crv_3*(3.0-4.0*L(K))) - vol_below(K)
19122142805 elseif (do_one_L_iter) then
1913 ! One iteration of Newton's method should give an estimate
1914 ! that is accurate to within Vol_tol.
19150 L(K) = sqrt(L0*L0 + dVol / dV_dL2)
19160 Vol_err = 0.5*(L(K)*L(K))*(slope + crv_3*(3.0-4.0*L(K))) - vol_below(K)
1917 else
19182142805 if (dV_dL2*(1.0-L0*L0) < dVol + &
1919 dV_dL2 * (Vol_open - vol_below(K))*Ibma_2) then
1920101353 L_max = sqrt(1.0 - (Vol_open - vol_below(K))*Ibma_2)
1921 else
19222041452 L_max = sqrt(L0*L0 + dVol / dV_dL2)
1923 endif
19242142805 L_min = sqrt(L0*L0 + dVol / (0.5*(slope+crv) - crv*L_max))
1925
19262142805 Vol_err_min = 0.5*(L_min**2)*(slope + crv_3*(3.0-4.0*L_min)) - vol_below(K)
19272142805 Vol_err_max = 0.5*(L_max**2)*(slope + crv_3*(3.0-4.0*L_max)) - vol_below(K)
1928 ! if ((abs(Vol_err_min) <= Vol_quit) .or. (Vol_err_min >= Vol_err_max)) then
19292142805 if (abs(Vol_err_min) <= Vol_quit) then
19300 L(K) = L_min ; Vol_err = Vol_err_min
1931 else
1932 L(K) = sqrt((L_min**2*Vol_err_max - L_max**2*Vol_err_min) / &
19332142805 (Vol_err_max - Vol_err_min))
19342807612 do itt=1,maxitt
19352807612 Vol_err = 0.5*(L(K)*L(K))*(slope + crv_3*(3.0-4.0*L(K))) - vol_below(K)
19362807612 if (abs(Vol_err) <= Vol_quit) exit
1937 ! Take a Newton's method iteration. This equation has proven
1938 ! robust enough not to need bracketing.
1939664807 L(K) = L(K) - Vol_err / (L(K)* (slope + crv - 2.0*crv*L(K)))
1940 ! This would be a Newton's method iteration for L^2:
1941 ! L(K) = sqrt(L(K)*L(K) - Vol_err / (0.5*(slope+crv) - crv*L(K)))
1942 enddo
1943 endif ! end of iterative solver
1944 endif ! end of 1-boundary alternatives.
1945 endif ! end of 0, 1- and 2- boundary cases.
1946 enddo ! k loop to determine L(K) in the convex case
1947
1948105672end subroutine find_L_open_convex
1949
1950!> This subroutine finds a thickness-weighted value of v at the u-points.
19512867989pure function set_v_at_u(v, h, G, GV, i, j, k, mask2dCv, OBC)
1952 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
1953 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
1954 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), &
1955 intent(in) :: v !< The meridional velocity [L T-1 ~> m s-1]
1956 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
1957 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
1958 integer, intent(in) :: i !< The i-index of the u-location to work on.
1959 integer, intent(in) :: j !< The j-index of the u-location to work on.
1960 integer, intent(in) :: k !< The k-index of the u-location to work on.
1961 real, dimension(SZI_(G),SZJB_(G)),&
1962 intent(in) :: mask2dCv !< A multiplicative mask of the v-points [nondim]
1963 type(ocean_OBC_type), pointer :: OBC !< A pointer to an open boundary condition structure
1964 real :: set_v_at_u !< The return value of v at u points points in the
1965 !! same units as u, i.e. [L T-1 ~> m s-1] or other units.
1966 !$omp declare target
1967
1968 ! This subroutine finds a thickness-weighted value of v at the u-points.
1969 real :: hwt(0:1,-1:0) ! Masked weights used to average u onto v [H ~> m or kg m-2].
1970 real :: hwt_tot ! The sum of the masked thicknesses [H ~> m or kg m-2].
1971 integer :: i0, j0, i1, j1
1972
197320075923 do j0 = -1,0 ; do i0 = 0,1 ; i1 = i+i0 ; J1 = J+j0
197417207934 hwt(i0,j0) = (h(i1,j1,k) + h(i1,j1+1,k)) * mask2dCv(i1,J1)
1975 enddo ; enddo
1976
19772867989 if (associated(OBC)) then ; if (OBC%number_of_segments > 0) then
19780 do j0 = -1,0 ; do i0 = 0,1 ; if (OBC%segnum_v(i+i0,J+j0) /= 0) then
19790 i1 = i+i0 ; J1 = J+j0
19800 if (OBC%segnum_v(i1,j1) > 0) then ! OBC_DIRECTION_N
19810 hwt(i0,j0) = 2.0 * h(i1,j1,k) * mask2dCv(i1,J1)
19820 elseif (OBC%segnum_v(i1,J1) < 0) then ! OBC_DIRECTION_S
19830 hwt(i0,j0) = 2.0 * h(i1,J1+1,k) * mask2dCv(i1,J1)
1984 endif
1985 endif ; enddo ; enddo
1986 endif ; endif
1987
19882867989 hwt_tot = (hwt(0,-1) + hwt(1,0)) + (hwt(1,-1) + hwt(0,0))
19892867989 set_v_at_u = 0.0
19902867989 if (hwt_tot > 0.0) set_v_at_u = &
1991 (((hwt(0,0) * v(i,J,k)) + (hwt(1,-1) * v(i+1,J-1,k))) + &
19922867989 ((hwt(1,0) * v(i+1,J,k)) + (hwt(0,-1) * v(i,J-1,k)))) / hwt_tot
1993
19942867989end function set_v_at_u
1995
1996!> This subroutine finds a thickness-weighted value of u at the v-points.
19972803353pure function set_u_at_v(u, h, G, GV, i, j, k, mask2dCu, OBC)
1998 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
1999 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure
2000 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), &
2001 intent(in) :: u !< The zonal velocity [L T-1 ~> m s-1] or other units.
2002 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
2003 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
2004 integer, intent(in) :: i !< The i-index of the u-location to work on.
2005 integer, intent(in) :: j !< The j-index of the u-location to work on.
2006 integer, intent(in) :: k !< The k-index of the u-location to work on.
2007 real, dimension(SZIB_(G),SZJ_(G)), &
2008 intent(in) :: mask2dCu !< A multiplicative mask of the u-points [nondim]
2009 type(ocean_OBC_type), pointer :: OBC !< A pointer to an open boundary condition structure
2010 real :: set_u_at_v !< The return value of u at v points in the
2011 !! same units as u, i.e. [L T-1 ~> m s-1] or other units.
2012 !$omp declare target
2013
2014 ! This subroutine finds a thickness-weighted value of u at the v-points.
2015 real :: hwt(-1:0,0:1) ! Masked weights used to average u onto v [H ~> m or kg m-2].
2016 real :: hwt_tot ! The sum of the masked thicknesses [H ~> m or kg m-2].
2017 integer :: i0, j0, i1, j1
2018
201919623471 do j0 = 0,1 ; do i0 = -1,0 ; I1 = I+i0 ; j1 = j+j0
202016820118 hwt(i0,j0) = (h(i1,j1,k) + h(i1+1,j1,k)) * mask2dCu(I1,j1)
2021 enddo ; enddo
2022
20232803353 if (associated(OBC)) then ; if (OBC%number_of_segments > 0) then
20240 do j0 = 0,1 ; do i0 = -1,0 ; if ((OBC%segnum_u(I+i0,j+j0) /= 0)) then
20250 I1 = I+i0 ; j1 = j+j0
20260 if (OBC%segnum_u(I1,j1) > 0) then ! OBC_DIRECTION_E
20270 hwt(i0,j0) = 2.0 * h(I1,j1,k) * mask2dCu(I1,j1)
20280 elseif (OBC%segnum_u(I1,j1) < 0) then ! OBC_DIRECTION_W
20290 hwt(i0,j0) = 2.0 * h(I1+1,j1,k) * mask2dCu(I1,j1)
2030 endif
2031 endif ; enddo ; enddo
2032 endif ; endif
2033
20342803353 hwt_tot = (hwt(-1,0) + hwt(0,1)) + (hwt(0,0) + hwt(-1,1))
20352803353 set_u_at_v = 0.0
20362803353 if (hwt_tot > 0.0) set_u_at_v = &
2037 (((hwt(0,0) * u(I,j,k)) + (hwt(-1,1) * u(I-1,j+1,k))) + &
20382803353 ((hwt(-1,0) * u(I-1,j,k)) + (hwt(0,1) * u(I,j+1,k)))) / hwt_tot
2039
20402803353end function set_u_at_v
2041
2042!> Calculates the thickness of the surface boundary layer for applying an elevated viscosity.
2043!!
2044!! A bulk Richardson criterion or the thickness of the topmost NKML layers (with a bulk mixed layer)
2045!! are currently used. The thicknesses are given in terms of fractional layers, so that this
2046!! thickness will move as the thickness of the topmost layers change.
204724subroutine set_viscous_ML(u, v, h, tv, forces, visc, dt, G, GV, US, CS)
2048 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
2049 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
2050 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
2051 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), &
2052 intent(in) :: u !< The zonal velocity [L T-1 ~> m s-1].
2053 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), &
2054 intent(in) :: v !< The meridional velocity [L T-1 ~> m s-1].
2055 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
2056 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2].
2057 type(thermo_var_ptrs), intent(in) :: tv !< A structure containing pointers to any available
2058 !! thermodynamic fields. Absent fields have
2059 !! NULL pointers.
2060 type(mech_forcing), intent(in) :: forces !< A structure with the driving mechanical forces
2061 type(vertvisc_type), intent(inout) :: visc !< A structure containing vertical viscosities and
2062 !! related fields.
2063 real, intent(in) :: dt !< Time increment [T ~> s].
2064 type(set_visc_CS), intent(inout) :: CS !< The control structure returned by a previous
2065 !! call to set_visc_init.
2066
2067 ! Local variables
2068 real, dimension(SZIB_(G)) :: &
206948 htot, & ! The total thickness of the layers that are within the
2070 ! surface mixed layer [H ~> m or kg m-2].
207148 dztot, & ! The distance from the surface to the bottom of the layers that are
2072 ! within the surface mixed layer [Z ~> m]
207348 Thtot, & ! The integrated temperature of layers that are within the
2074 ! surface mixed layer [H C ~> m degC or kg degC m-2].
207548 Shtot, & ! The integrated salt of layers that are within the
2076 ! surface mixed layer [H S ~> m ppt or kg ppt m-2].
207748 SpV_htot, & ! Running sum of thickness times specific volume [H R-1 ~> m4 kg-1 or m]
207848 Rhtot, & ! The integrated density of layers that are within the surface mixed layer
2079 ! [H R ~> kg m-2 or kg2 m-5]. Rhtot is only used if no
2080 ! equation of state is used.
208148 uhtot, & ! The depth integrated zonal velocity within the surface
2082 ! mixed layer [H L T-1 ~> m2 s-1 or kg m-1 s-1].
208348 vhtot, & ! The depth integrated meridional velocity within the surface
2084 ! mixed layer [H L T-1 ~> m2 s-1 or kg m-1 s-1].
208548 Idecay_len_TKE, & ! The inverse of a turbulence decay length scale [H-1 ~> m-1 or m2 kg-1].
208648 dR_dT, & ! Partial derivative of the density at the base of layer nkml
2087 ! (roughly the base of the mixed layer) with temperature [R C-1 ~> kg m-3 degC-1].
208848 dR_dS, & ! Partial derivative of the density at the base of layer nkml
2089 ! (roughly the base of the mixed layer) with salinity [R S-1 ~> kg m-3 ppt-1].
209048 dSpV_dT, & ! Partial derivative of the specific volume at the base of layer nkml
2091 ! (roughly the base of the mixed layer) with temperature [R-1 C-1 ~> m3 kg-1 degC-1].
209248 dSpV_dS, & ! Partial derivative of the specific volume at the base of layer nkml
2093 ! (roughly the base of the mixed layer) with salinity [R-1 S-1 ~> m3 kg-1 ppt-1].
209448 ustar, & ! The surface friction velocity under ice shelves [H T-1 ~> m s-1 or kg m-2 s-1].
209548 press, & ! The pressure at which dR_dT and dR_dS are evaluated [R L2 T-2 ~> Pa].
209648 T_EOS, & ! The potential temperature at which dR_dT and dR_dS are evaluated [C ~> degC]
209748 S_EOS ! The salinity at which dR_dT and dR_dS are evaluated [S ~> ppt].
209848 real :: dz(SZI_(G),SZJ_(G),SZK_(GV)) ! Height change across layers [Z ~> m]
2099 real, dimension(SZIB_(G),SZJ_(G)) :: &
210048 mask_u ! A mask that disables any contributions from u points that
2101 ! are land or past open boundary conditions [nondim], 0 or 1.
2102 real, dimension(SZI_(G),SZJB_(G)) :: &
210348 mask_v ! A mask that disables any contributions from v points that
2104 ! are land or past open boundary conditions [nondim], 0 or 1.
210548 real :: U_star_2d(SZI_(G),SZJ_(G)) ! The wind friction velocity in thickness-based units,
2106 ! calculated using the Boussinesq reference density or the time-evolving
2107 ! surface density in non-Boussinesq mode [H T-1 ~> m s-1 or kg m-2 s-1]
210848 real :: h_at_vel(SZIB_(G),SZK_(GV))! Layer thickness at velocity points,
2109 ! using an upwind-biased second order accurate estimate based
2110 ! on the previous velocity direction [H ~> m or kg m-2].
211148 real :: dz_at_vel(SZIB_(G),SZK_(GV)) ! Vertical extent of a layer at velocity points,
2112 ! using an upwind-biased second order accurate estimate based
2113 ! on the previous velocity direction [Z ~> m].
211448 integer :: k_massive(SZIB_(G)) ! The k-index of the deepest layer yet found
2115 ! that has more than h_tiny thickness and will be in the
2116 ! viscous mixed layer.
2117 real :: Uh2 ! The squared magnitude of the difference between the velocity
2118 ! integrated through the mixed layer and the velocity of the
2119 ! interior layer layer times the depth of the mixed layer
2120 ! [H2 L2 T-2 ~> m4 s-2 or kg2 m-2 s-2].
2121 real :: htot_vel ! Sum of the layer thicknesses up to some point [H ~> m or kg m-2].
2122 real :: hwtot ! Sum of the thicknesses used to calculate
2123 ! the near-bottom velocity magnitude [H ~> m or kg m-2].
2124 real :: hutot ! Running sum of thicknesses times the velocity
2125 ! magnitudes [H L T-1 ~> m2 s-1 or kg m-1 s-1].
2126 real :: hweight ! The thickness of a layer that is within Hbbl
2127 ! of the bottom [H ~> m or kg m-2].
2128 real :: tbl_thick ! The thickness of the top boundary layer [Z ~> m].
2129
2130 real :: hlay ! The layer thickness at velocity points [H ~> m or kg m-2].
2131 real :: I_2hlay ! 1 / 2*hlay [H-1 ~> m-1 or m2 kg-1].
2132 real :: T_lay ! The layer temperature at velocity points [C ~> degC].
2133 real :: S_lay ! The layer salinity at velocity points [S ~> ppt].
2134 real :: Rlay ! The layer potential density at velocity points [R ~> kg m-3].
2135 real :: Rlb ! The potential density of the layer below [R ~> kg m-3].
2136 real :: v_at_u ! The meridional velocity at a zonal velocity point [L T-1 ~> m s-1].
2137 real :: u_at_v ! The zonal velocity at a meridional velocity point [L T-1 ~> m s-1].
2138 real :: gHprime ! The mixed-layer internal gravity wave speed squared, based
2139 ! on the mixed layer thickness and density difference across
2140 ! the base of the mixed layer [L2 T-2 ~> m2 s-2].
2141 real :: RiBulk ! The bulk Richardson number below which water is in the
2142 ! viscous mixed layer, including reduction for turbulent decay [nondim]
2143 real :: dt_Rho0 ! The time step divided by the conversion from the layer
2144 ! thickness to layer mass [T H Z-1 R-1 ~> s m3 kg-1 or s].
2145 real :: g_H_Rho0 ! The gravitational acceleration times the conversion from H to m divided
2146 ! by the mean density [L2 T-2 H-1 R-1 ~> m4 s-2 kg-1 or m7 s-2 kg-2].
2147 real :: ustarsq ! 400 times the square of ustar, times
2148 ! Rho0 divided by G_Earth and the conversion
2149 ! from m to thickness units [H R ~> kg m-2 or kg2 m-5].
2150 real :: cdrag_sqrt ! Square root of the drag coefficient [nondim].
2151 real :: cdrag_sqrt_H ! Square root of the drag coefficient, times a unit conversion
2152 ! factor from lateral lengths to layer thicknesses [H L-1 ~> nondim or kg m-3].
2153 real :: cdrag_sqrt_H_RL ! Square root of the drag coefficient, times a unit conversion factor from
2154 ! density times lateral lengths to layer thicknesses [H L-1 R-1 ~> m3 kg-1 or nondim]
2155 real :: oldfn ! The integrated energy required to
2156 ! entrain up to the bottom of the layer,
2157 ! divided by G_Earth [H R ~> kg m-2 or kg2 m-5].
2158 real :: Dfn ! The increment in oldfn for entraining
2159 ! the layer [H R ~> kg m-2 or kg2 m-5].
2160 real :: frac_used ! The fraction of the present layer that contributes to Dh and Ddz [nondim]
2161 real :: Dh ! The increment in layer thickness from the present layer [H ~> m or kg m-2].
2162 real :: Ddz ! The increment in height change from the present layer [Z ~> m].
216348 real :: u2_bg(SZIB_(G)) ! The square of an assumed background velocity, for
2164 ! calculating the mean magnitude near the top for use in
2165 ! the quadratic surface drag [L2 T-2 ~> m2 s-2].
2166 real :: h_tiny ! A very small thickness [H ~> m or kg m-2]. Layers that are less than
2167 ! h_tiny can not be the deepest in the viscous mixed layer.
2168 real :: absf ! The absolute value of f averaged to velocity points [T-1 ~> s-1].
2169 real :: U_star ! The friction velocity at velocity points [H T-1 ~> m s-1 or kg m-2 s-1].
2170 real :: h_neglect ! A thickness that is so small it is usually lost
2171 ! in roundoff and can be neglected [H ~> m or kg m-2].
2172 real :: dz_neglect ! A vertical distance that is so small it is usually lost
2173 ! in roundoff and can be neglected [Z ~> m].
2174 real :: Rho0x400_G ! 400*Rho0/G_Earth, times unit conversion factors
2175 ! [R T2 H-1 ~> kg s2 m-4 or s2 m-1].
2176 ! The 400 is a constant proposed by Killworth and Edwards, 1999.
2177 real :: ustar1 ! ustar [H T-1 ~> m s-1 or kg m-2 s-1]
2178 real :: h2f2 ! (h*2*f)^2 [H2 T-2 ~> m2 s-2 or kg2 m-4 s-2]
217924 logical :: use_EOS, do_any, do_any_shelf, do_i(SZIB_(G))
2180 logical :: nonBous_ML ! If true, use the non-Boussinesq form of some energy and
2181 ! stratification calculations.
2182 integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, K2, nkmb, nkml, n
2183 type(ocean_OBC_type), pointer :: OBC => NULL()
2184
218524 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
218624 Isq = G%isc-1 ; Ieq = G%IecB ; Jsq = G%jsc-1 ; Jeq = G%JecB
218724 nkmb = GV%nk_rho_varies ; nkml = GV%nkml
2188
218924 if (.not.CS%initialized) call MOM_error(FATAL,"MOM_set_viscosity(visc_ML): "//&
21900 "Module must be initialized before it is used.")
2191
2192 ! TODO: Remove this check and move it outside of the function call.
219324 if (.not.(CS%dynamic_viscous_ML .or. associated(forces%frac_shelf_u) .or. &
219424 associated(forces%frac_shelf_v)) ) return
2195
2196 ! NOTE: Requried since this is called by the GPU-enabled dycore, but it could
2197 ! also be implicitly fixing other functions.
2198 !$omp target update from(u, v)
2199
22000 Rho0x400_G = 400.0*(GV%H_to_RZ / GV%g_Earth_Z_T2)
22010 cdrag_sqrt = sqrt(CS%cdrag)
22020 cdrag_sqrt_H = cdrag_sqrt * US%L_to_m * GV%m_to_H
22030 cdrag_sqrt_H_RL = cdrag_sqrt * US%L_to_Z * GV%RZ_to_H
2204
22050 OBC => CS%OBC
22060 use_EOS = associated(tv%eqn_of_state)
22070 nonBous_ML = allocated(tv%SpV_avg)
22080 dt_Rho0 = dt / GV%H_to_RZ
22090 h_neglect = GV%H_subroundoff
22100 h_tiny = 2.0*GV%Angstrom_H + h_neglect
22110 dz_neglect = GV%dZ_subroundoff
22120 g_H_Rho0 = (GV%g_Earth*GV%H_to_Z) / (GV%Rho0)
2213
22140 if (associated(forces%frac_shelf_u) .neqv. associated(forces%frac_shelf_v)) &
2215 call MOM_error(FATAL, "set_viscous_ML: one of forces%frac_shelf_u and "//&
22160 "forces%frac_shelf_v is associated, but the other is not.")
2217
2218 ! Extract the friction velocity from the forcing type.
22190 call find_ustar(forces, tv, U_star_2d, G, GV, US, halo=1, H_T_units=.true.)
2220
22210 if (associated(forces%frac_shelf_u)) then
2222 ! This configuration has ice shelves, and the appropriate variables need to be
2223 ! allocated. If the arrays have already been allocated, these calls do nothing.
22240 if (.not.allocated(visc%taux_shelf)) &
22250 allocate(visc%taux_shelf(G%IsdB:G%IedB, G%jsd:G%jed), source=0.0)
22260 if (.not.allocated(visc%tauy_shelf)) &
22270 allocate(visc%tauy_shelf(G%isd:G%ied, G%JsdB:G%JedB), source=0.0)
22280 if (.not.allocated(visc%tbl_thick_shelf_u)) &
22290 allocate(visc%tbl_thick_shelf_u(G%IsdB:G%IedB, G%jsd:G%jed), source=0.0)
22300 if (.not.allocated(visc%tbl_thick_shelf_v)) &
22310 allocate(visc%tbl_thick_shelf_v(G%isd:G%ied, G%JsdB:G%JedB), source=0.0)
22320 if (.not.allocated(visc%kv_tbl_shelf_u)) &
22330 allocate(visc%kv_tbl_shelf_u(G%IsdB:G%IedB, G%jsd:G%jed), source=0.0)
22340 if (.not.allocated(visc%kv_tbl_shelf_v)) &
22350 allocate(visc%kv_tbl_shelf_v(G%isd:G%ied, G%JsdB:G%JedB), source=0.0)
2236
2237 ! With a linear drag law under shelves, the friction velocity is already known.
2238! if (CS%linear_drag) ustar(:) = cdrag_sqrt_H*CS%drag_bg_vel
2239
2240 ! Find the vertical distances across layers.
22410 call thickness_to_dz(h, tv, dz, G, GV, US, halo_size=1)
2242 endif
2243
2244 !$OMP parallel do default(shared)
22450 do J=js-1,je ; do i=is-1,ie+1
22460 mask_v(i,J) = G%mask2dCv(i,J)
2247 enddo ; enddo
2248 !$OMP parallel do default(shared)
22490 do j=js-1,je+1 ; do I=is-1,ie
22500 mask_u(I,j) = G%mask2dCu(I,j)
2251 enddo ; enddo
2252
22530 if (associated(OBC)) then ; do n=1,OBC%number_of_segments
2254 ! Project bottom depths across cell-corner points in the OBCs.
22550 if (.not. OBC%segment(n)%on_pe) cycle
2256 ! Use a one-sided projection of bottom depths at OBC points.
22570 I = OBC%segment(n)%HI%IsdB ; J = OBC%segment(n)%HI%JsdB
22580 if (OBC%segment(n)%is_N_or_S .and. (J >= js-1) .and. (J <= je)) then
22590 do I = max(is-1,OBC%segment(n)%HI%IsdB), min(ie,OBC%segment(n)%HI%IedB)
22600 if (OBC%segment(n)%direction == OBC_DIRECTION_N) mask_u(I,j+1) = 0.0
22610 if (OBC%segment(n)%direction == OBC_DIRECTION_S) mask_u(I,j) = 0.0
2262 enddo
22630 elseif (OBC%segment(n)%is_E_or_W .and. (I >= is-1) .and. (I <= ie)) then
22640 do J = max(js-1,OBC%segment(n)%HI%JsdB), min(je,OBC%segment(n)%HI%JedB)
22650 if (OBC%segment(n)%direction == OBC_DIRECTION_E) mask_v(i+1,J) = 0.0
22660 if (OBC%segment(n)%direction == OBC_DIRECTION_W) mask_v(i,J) = 0.0
2267 enddo
2268 endif
2269 enddo ; endif
2270
2271 !$OMP parallel do default(private) shared(u,v,h,dz,tv,forces,visc,dt,G,GV,US,CS,use_EOS,dt_Rho0, &
2272 !$OMP nonBous_ML,h_neglect,dz_neglect,h_tiny,g_H_Rho0, &
2273 !$OMP js,je,OBC,Isq,Ieq,nz,nkml,U_star_2d,mask_v, &
2274 !$OMP cdrag_sqrt,cdrag_sqrt_H,cdrag_sqrt_H_RL,Rho0x400_G)
22750 do j=js,je ! u-point loop
22760 if (CS%dynamic_viscous_ML) then
22770 do_any = .false.
22780 do I=Isq,Ieq
22790 htot(I) = 0.0
22800 if (G%mask2dCu(I,j) < 0.5) then
22810 do_i(I) = .false. ; visc%nkml_visc_u(I,j) = nkml
2282 else
22830 do_i(I) = .true. ; do_any = .true.
22840 k_massive(I) = nkml
22850 Thtot(I) = 0.0 ; Shtot(I) = 0.0 ; Rhtot(i) = 0.0
22860 uhtot(I) = dt_Rho0 * forces%taux(I,j)
2287 vhtot(I) = 0.25 * dt_Rho0 * ((forces%tauy(i,J) + forces%tauy(i+1,J-1)) + &
22880 (forces%tauy(i,J-1) + forces%tauy(i+1,J)))
2289
22900 if (CS%omega_frac >= 1.0) then ; absf = 2.0*CS%omega ; else
22910 absf = 0.5*(abs(G%CoriolisBu(I,J)) + abs(G%CoriolisBu(I,J-1)))
22920 if (CS%omega_frac > 0.0) &
22930 absf = sqrt(CS%omega_frac*4.0*CS%omega**2 + (1.0-CS%omega_frac)*absf**2)
2294 endif
22950 U_star = max(CS%ustar_min, 0.5*(U_star_2d(i,j) + U_star_2d(i+1,j)))
22960 Idecay_len_TKE(I) = (absf / U_star) * CS%TKE_decay
2297 endif
2298 enddo
2299
23000 if (do_any) then ; do k=1,nz
23010 if (k > nkml) then
23020 do_any = .false.
23030 if (use_EOS .and. (k==nkml+1)) then
2304 ! Find dRho/dT and dRho_dS.
23050 do I=Isq,Ieq
23060 press(I) = (GV%H_to_RZ*GV%g_Earth) * htot(I)
23070 if (associated(tv%p_surf)) press(I) = press(I) + 0.5*(tv%p_surf(i,j)+tv%p_surf(i+1,j))
23080 k2 = max(1,nkml)
23090 I_2hlay = 1.0 / (h(i,j,k2) + h(i+1,j,k2) + h_neglect)
23100 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
23110 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
2312 enddo
2313 call calculate_density_derivs(T_EOS, S_EOS, press, dR_dT, dR_dS, tv%eqn_of_state, &
23140 (/Isq-G%IsdB+1,Ieq-G%IsdB+1/) )
23150 if (nonBous_ML) then
2316 call calculate_specific_vol_derivs(T_EOS, S_EOS, press, dSpV_dT, dSpV_dS, tv%eqn_of_state, &
23170 (/Isq-G%IsdB+1,Ieq-G%IsdB+1/) )
2318 endif
2319 endif
2320
23210 do I=Isq,Ieq ; if (do_i(I)) then
2322
23230 hlay = 0.5*(h(i,j,k) + h(i+1,j,k))
23240 if (hlay > h_tiny) then ! Only consider non-vanished layers.
23250 I_2hlay = 1.0 / (h(i,j,k) + h(i+1,j,k))
2326 v_at_u = 0.5 * ((h(i,j,k) * (v(i,J,k) + v(i,J-1,k))) + &
23270 (h(i+1,j,k) * (v(i+1,J,k) + v(i+1,J-1,k)))) * I_2hlay
23280 Uh2 = (uhtot(I) - htot(I)*u(I,j,k))**2 + (vhtot(I) - htot(I)*v_at_u)**2
2329
23300 if (use_EOS) then
23310 T_lay = ((h(i,j,k)*tv%T(i,j,k)) + (h(i+1,j,k)*tv%T(i+1,j,k))) * I_2hlay
23320 S_lay = ((h(i,j,k)*tv%S(i,j,k)) + (h(i+1,j,k)*tv%S(i+1,j,k))) * I_2hlay
23330 if (nonBous_ML) then
2334 gHprime = (GV%g_Earth * GV%H_to_RZ) * (dSpV_dT(I) * (Thtot(I) - T_lay*htot(I)) + &
23350 dSpV_dS(I) * (Shtot(I) - S_lay*htot(I)))
2336 else
2337 gHprime = g_H_Rho0 * (dR_dT(I) * (T_lay*htot(I) - Thtot(I)) + &
23380 dR_dS(I) * (S_lay*htot(I) - Shtot(I)))
2339 endif
2340 else
23410 gHprime = g_H_Rho0 * (GV%Rlay(k)*htot(I) - Rhtot(I))
2342 endif
2343
23440 if (gHprime > 0.0) then
23450 RiBulk = CS%bulk_Ri_ML * exp(-htot(I) * Idecay_len_TKE(I))
23460 if (RiBulk * Uh2 <= (htot(I)**2) * gHprime) then
23470 visc%nkml_visc_u(I,j) = real(k_massive(I))
23480 do_i(I) = .false.
23490 elseif (RiBulk * Uh2 <= (htot(I) + hlay)**2 * gHprime) then
2350 visc%nkml_visc_u(I,j) = real(k-1) + &
23510 ( sqrt(RiBulk * Uh2 / gHprime) - htot(I) ) / hlay
23520 do_i(I) = .false.
2353 endif
2354 endif
23550 k_massive(I) = k
2356 endif ! hlay > h_tiny
2357
23580 if (do_i(I)) do_any = .true.
2359 endif ; enddo
2360
23610 if (.not.do_any) exit ! All columns are done.
2362 endif
2363
23640 do I=Isq,Ieq ; if (do_i(I)) then
23650 htot(I) = htot(I) + 0.5 * (h(i,j,k) + h(i+1,j,k))
23660 uhtot(I) = uhtot(I) + 0.5 * (h(i,j,k) + h(i+1,j,k)) * u(I,j,k)
2367 vhtot(I) = vhtot(I) + 0.25 * ((h(i,j,k) * (v(i,J,k) + v(i,J-1,k))) + &
23680 (h(i+1,j,k) * (v(i+1,J,k) + v(i+1,J-1,k))))
23690 if (use_EOS) then
23700 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)))
23710 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)))
2372 else
23730 Rhtot(i) = Rhtot(i) + 0.5 * (h(i,j,k) + h(i+1,j,k)) * GV%Rlay(k)
2374 endif
2375 endif ; enddo
2376 enddo ; endif
2377
23780 if (do_any) then ; do I=Isq,Ieq ; if (do_i(I)) then
23790 visc%nkml_visc_u(I,j) = k_massive(I)
2380 endif ; enddo ; endif
2381 endif ! dynamic_viscous_ML
2382
23830 do_any_shelf = .false.
23840 if (associated(forces%frac_shelf_u)) then
23850 do I=Isq,Ieq
23860 if (forces%frac_shelf_u(I,j)*G%mask2dCu(I,j) == 0.0) then
23870 do_i(I) = .false.
23880 visc%tbl_thick_shelf_u(I,j) = 0.0 ; visc%kv_tbl_shelf_u(I,j) = 0.0
2389 else
23900 do_i(I) = .true. ; do_any_shelf = .true.
2391 endif
2392 enddo
2393 endif
2394
23950 if (do_any_shelf) then
23960 do k=1,nz ; do I=Isq,Ieq ; if (do_i(I)) then
23970 if (u(I,j,k) * (h(i+1,j,k) - h(i,j,k)) >= 0) then
2398 h_at_vel(i,k) = 2.0*h(i,j,k)*h(i+1,j,k) / &
23990 (h(i,j,k) + h(i+1,j,k) + h_neglect)
2400 dz_at_vel(i,k) = 2.0*dz(i,j,k)*dz(i+1,j,k) / &
24010 (dz(i,j,k) + dz(i+1,j,k) + dz_neglect)
2402 else
24030 h_at_vel(i,k) = 0.5 * (h(i,j,k) + h(i+1,j,k))
24040 dz_at_vel(i,k) = 0.5 * (dz(i,j,k) + dz(i+1,j,k))
2405 endif
2406 else
24070 h_at_vel(I,k) = 0.0
24080 dz_at_vel(I,k) = 0.0
24090 ustar(I) = 0.0
2410 endif ; enddo ; enddo
2411
24120 do I=Isq,Ieq ; if (do_i(I)) then
24130 htot_vel = 0.0 ; hwtot = 0.0 ; hutot = 0.0
24140 Thtot(I) = 0.0 ; Shtot(I) = 0.0 ; SpV_htot(I) = 0.0
24150 if (use_EOS .or. .not.CS%linear_drag) then ; do k=1,nz
24160 if (htot_vel>=CS%Htbl_shelf) exit ! terminate the k loop
24170 hweight = MIN(CS%Htbl_shelf - htot_vel, h_at_vel(i,k))
24180 if (hweight <= 1.5*GV%Angstrom_H + h_neglect) cycle
2419
24200 htot_vel = htot_vel + h_at_vel(i,k)
24210 hwtot = hwtot + hweight
2422
24230 if (.not.CS%linear_drag) then
24240 v_at_u = set_v_at_u(v, h, G, GV, i, j, k, mask_v, OBC)
2425 ! Set the "back ground" friction velocity scale to either the tidal amplitude or place-holder constant
24260 if (CS%BBL_use_tidal_bg) then
2427 u2_bg(I) = 0.5*( G%mask2dT(i,j)*(CS%tideamp(i,j)*CS%tideamp(i,j))+ &
24280 G%mask2dT(i+1,j)*(CS%tideamp(i+1,j)*CS%tideamp(i+1,j)) )
2429 else
24300 u2_bg(I) = CS%drag_bg_vel * CS%drag_bg_vel
2431 endif
24320 hutot = hutot + hweight * sqrt(u(I,j,k)**2 + v_at_u**2 + u2_bg(I))
2433 endif
24340 if (use_EOS) then
24350 Thtot(I) = Thtot(I) + hweight * 0.5 * (tv%T(i,j,k) + tv%T(i+1,j,k))
24360 Shtot(I) = Shtot(I) + hweight * 0.5 * (tv%S(i,j,k) + tv%S(i+1,j,k))
2437 endif
24380 if (allocated(tv%SpV_avg)) then
24390 SpV_htot(I) = SpV_htot(I) + hweight * 0.5 * (tv%SpV_avg(i,j,k) + tv%SpV_avg(i+1,j,k))
2440 endif
2441 enddo ; endif
2442
24430 if ((hwtot <= 0.0) .or. (CS%linear_drag .and. .not.allocated(tv%SpV_avg))) then
24440 ustar(I) = cdrag_sqrt_H * CS%drag_bg_vel
24450 elseif (CS%linear_drag .and. allocated(tv%SpV_avg)) then
24460 ustar(I) = cdrag_sqrt_H_RL * CS%drag_bg_vel * (hwtot / SpV_htot(I))
24470 elseif (allocated(tv%SpV_avg)) then ! (.not.CS%linear_drag)
24480 ustar(I) = cdrag_sqrt_H_RL * hutot / SpV_htot(I)
2449 else ! (.not.CS%linear_drag .and. .not.allocated(tv%SpV_avg))
24500 ustar(I) = cdrag_sqrt_H * hutot / hwtot
2451 endif
2452
24530 if (use_EOS) then ; if (hwtot > 0.0) then
24540 T_EOS(I) = Thtot(I)/hwtot ; S_EOS(I) = Shtot(I)/hwtot
2455 else
24560 T_EOS(I) = 0.0 ; S_EOS(I) = 0.0
2457 endif ; endif
2458 ! if (allocated(tv%SpV_avg)) SpV_av(I) = SpVhtot(I) / hwtot
2459 endif ; enddo ! I-loop
2460
24610 if (use_EOS) then
2462 call calculate_density_derivs(T_EOS, S_EOS, forces%p_surf(:,j), dR_dT, dR_dS, &
24630 tv%eqn_of_state, (/Isq-G%IsdB+1,Ieq-G%IsdB+1/) )
2464 endif
2465
24660 do I=Isq,Ieq ; if (do_i(I)) then
2467 ! The 400.0 in this expression is the square of a constant proposed
2468 ! by Killworth and Edwards, 1999, in equation (2.20).
24690 ustarsq = Rho0x400_G * ustar(i)**2
24700 htot(i) = 0.0 ; dztot(i) = 0.0
24710 if (use_EOS) then
24720 Thtot(i) = 0.0 ; Shtot(i) = 0.0 ; oldfn = 0.0
24730 do k=1,nz-1
24740 if (h_at_vel(i,k) <= 0.0) cycle
24750 T_Lay = 0.5 * (tv%T(i,j,k) + tv%T(i+1,j,k))
24760 S_Lay = 0.5 * (tv%S(i,j,k) + tv%S(i+1,j,k))
24770 oldfn = dR_dT(i)*(T_Lay*htot(i) - Thtot(i)) + dR_dS(i)*(S_Lay*htot(i) - Shtot(i))
24780 if (oldfn >= ustarsq) exit
2479
2480 Dfn = (dR_dT(i)*(0.5*(tv%T(i,j,k+1)+tv%T(i+1,j,k+1)) - T_Lay) + &
2481 dR_dS(i)*(0.5*(tv%S(i,j,k+1)+tv%S(i+1,j,k+1)) - S_Lay)) * &
24820 (h_at_vel(i,k)+htot(i))
24830 if ((oldfn + Dfn) <= ustarsq) then
24840 Dh = h_at_vel(i,k)
24850 Ddz = dz_at_vel(i,k)
2486 else
24870 frac_used = sqrt((ustarsq-oldfn) / (Dfn))
24880 Dh = h_at_vel(i,k) * frac_used
24890 Ddz = dz_at_vel(i,k) * frac_used
2490 endif
2491
24920 htot(i) = htot(i) + Dh
24930 dztot(i) = dztot(i) + Ddz
24940 Thtot(i) = Thtot(i) + T_Lay*Dh ; Shtot(i) = Shtot(i) + S_Lay*Dh
2495 enddo
24960 if ((oldfn < ustarsq) .and. (h_at_vel(i,nz) > 0.0)) then
24970 T_Lay = 0.5*(tv%T(i,j,nz) + tv%T(i+1,j,nz))
24980 S_Lay = 0.5*(tv%S(i,j,nz) + tv%S(i+1,j,nz))
24990 if (dR_dT(i)*(T_Lay*htot(i) - Thtot(i)) + &
2500 dR_dS(i)*(S_Lay*htot(i) - Shtot(i)) < ustarsq) then
25010 htot(i) = htot(i) + h_at_vel(i,nz)
25020 dztot(i) = dztot(i) + dz_at_vel(i,nz)
2503 endif
2504 endif ! Examination of layer nz.
2505 else ! Use Rlay as the density variable.
25060 Rhtot = 0.0
25070 do k=1,nz-1
25080 Rlay = GV%Rlay(k) ; Rlb = GV%Rlay(k+1)
2509
25100 oldfn = Rlay*htot(i) - Rhtot(i)
25110 if (oldfn >= ustarsq) exit
2512
25130 Dfn = (Rlb - Rlay)*(h_at_vel(i,k)+htot(i))
25140 if ((oldfn + Dfn) <= ustarsq) then
25150 Dh = h_at_vel(i,k)
25160 Ddz = dz_at_vel(i,k)
2517 else
25180 frac_used = sqrt((ustarsq-oldfn) / (Dfn))
25190 Dh = h_at_vel(i,k) * frac_used
25200 Ddz = dz_at_vel(i,k) * frac_used
2521 endif
2522
25230 htot(i) = htot(i) + Dh
25240 dztot(i) = dztot(i) + Ddz
25250 Rhtot(i) = Rhtot(i) + Rlay*Dh
2526 enddo
25270 if (GV%Rlay(nz)*htot(i) - Rhtot(i) < ustarsq) then
25280 htot(i) = htot(i) + h_at_vel(i,nz)
25290 dztot(i) = dztot(i) + dz_at_vel(i,nz)
2530 endif
2531 endif ! use_EOS
2532
2533 ! visc%tbl_thick_shelf_u(I,j) = max(CS%Htbl_shelf_min, &
2534 ! dztot(I) / (0.5 + sqrt(0.25 + &
2535 ! ((htot(i)*(G%CoriolisBu(I,J-1)+G%CoriolisBu(I,J)))**2) / &
2536 ! (ustar(i)**2) )) )
25370 ustar1 = ustar(i)
25380 h2f2 = (htot(i)*(G%CoriolisBu(I,J-1)+G%CoriolisBu(I,J)) + h_neglect*CS%omega)**2
2539 tbl_thick = max(CS%Htbl_shelf_min, &
25400 ( dztot(I)*ustar(i) ) / ( 0.5*ustar1 + sqrt((0.5*ustar1)**2 + h2f2 ) ) )
25410 visc%tbl_thick_shelf_u(I,j) = tbl_thick
25420 visc%Kv_tbl_shelf_u(I,j) = max(CS%Kv_TBL_min, cdrag_sqrt*ustar1*tbl_thick)
2543 endif ; enddo ! I-loop
2544 endif ! do_any_shelf
2545
2546 enddo ! j-loop at u-points
2547
2548 !$OMP parallel do default(private) shared(u,v,h,dz,tv,forces,visc,dt,G,GV,US,CS,use_EOS,dt_Rho0, &
2549 !$OMP nonBous_ML,h_neglect,dz_neglect,h_tiny,g_H_Rho0, &
2550 !$OMP is,ie,OBC,Jsq,Jeq,nz,nkml,U_star_2d,mask_u, &
2551 !$OMP cdrag_sqrt,cdrag_sqrt_H,cdrag_sqrt_H_RL,Rho0x400_G)
25520 do J=Jsq,Jeq ! v-point loop
25530 if (CS%dynamic_viscous_ML) then
25540 do_any = .false.
25550 do i=is,ie
25560 htot(i) = 0.0
25570 if (G%mask2dCv(i,J) < 0.5) then
25580 do_i(i) = .false. ; visc%nkml_visc_v(i,J) = nkml
2559 else
25600 do_i(i) = .true. ; do_any = .true.
25610 k_massive(i) = nkml
25620 Thtot(i) = 0.0 ; Shtot(i) = 0.0 ; Rhtot(i) = 0.0
25630 vhtot(i) = dt_Rho0 * forces%tauy(i,J)
2564 uhtot(i) = 0.25 * dt_Rho0 * ((forces%taux(I,j) + forces%taux(I-1,j+1)) + &
25650 (forces%taux(I-1,j) + forces%taux(I,j+1)))
2566
25670 if (CS%omega_frac >= 1.0) then ; absf = 2.0*CS%omega ; else
25680 absf = 0.5*(abs(G%CoriolisBu(I-1,J)) + abs(G%CoriolisBu(I,J)))
25690 if (CS%omega_frac > 0.0) &
25700 absf = sqrt(CS%omega_frac*4.0*CS%omega**2 + (1.0-CS%omega_frac)*absf**2)
2571 endif
2572
25730 U_star = max(CS%ustar_min, 0.5*(U_star_2d(i,j) + U_star_2d(i,j+1)))
25740 Idecay_len_TKE(i) = (absf / U_star) * CS%TKE_decay
2575
2576 endif
2577 enddo
2578
25790 if (do_any) then ; do k=1,nz
25800 if (k > nkml) then
25810 do_any = .false.
25820 if (use_EOS .and. (k==nkml+1)) then
2583 ! Find dRho/dT and dRho_dS.
25840 do i=is,ie
25850 press(i) = (GV%H_to_RZ * GV%g_Earth) * htot(i)
25860 if (associated(tv%p_surf)) press(i) = press(i) + 0.5*(tv%p_surf(i,j)+tv%p_surf(i,j+1))
25870 k2 = max(1,nkml)
25880 I_2hlay = 1.0 / (h(i,j,k2) + h(i,j+1,k2) + h_neglect)
25890 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
25900 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
2591 enddo
2592 call calculate_density_derivs(T_EOS, S_EOS, press, dR_dT, dR_dS, &
25930 tv%eqn_of_state, (/is-G%IsdB+1,ie-G%IsdB+1/) )
25940 if (nonBous_ML) then
2595 call calculate_specific_vol_derivs(T_EOS, S_EOS, press, dSpV_dT, dSpV_dS, tv%eqn_of_state, &
25960 (/is-G%IsdB+1,ie-G%IsdB+1/) )
2597 endif
2598 endif
2599
26000 do i=is,ie ; if (do_i(i)) then
2601
26020 hlay = 0.5*(h(i,j,k) + h(i,j+1,k))
26030 if (hlay > h_tiny) then ! Only consider non-vanished layers.
26040 I_2hlay = 1.0 / (h(i,j,k) + h(i,j+1,k))
2605 u_at_v = 0.5 * ((h(i,j,k) * (u(I-1,j,k) + u(I,j,k))) + &
26060 (h(i,j+1,k) * (u(I-1,j+1,k) + u(I,j+1,k)))) * I_2hlay
26070 Uh2 = (vhtot(i) - htot(i)*v(i,J,k))**2 + (uhtot(i) - htot(i)*u_at_v)**2
2608
26090 if (use_EOS) then
26100 T_lay = ((h(i,j,k)*tv%T(i,j,k)) + (h(i,j+1,k)*tv%T(i,j+1,k))) * I_2hlay
26110 S_lay = ((h(i,j,k)*tv%S(i,j,k)) + (h(i,j+1,k)*tv%S(i,j+1,k))) * I_2hlay
26120 if (nonBous_ML) then
2613 gHprime = (GV%g_Earth * GV%H_to_RZ) * (dSpV_dT(i) * (Thtot(i) - T_lay*htot(i)) + &
26140 dSpV_dS(i) * (Shtot(i) - S_lay*htot(i)))
2615 else
2616 gHprime = g_H_Rho0 * (dR_dT(i) * (T_lay*htot(i) - Thtot(i)) + &
26170 dR_dS(i) * (S_lay*htot(i) - Shtot(i)))
2618 endif
2619 else
26200 gHprime = g_H_Rho0 * (GV%Rlay(k)*htot(i) - Rhtot(i))
2621 endif
2622
26230 if (gHprime > 0.0) then
26240 RiBulk = CS%bulk_Ri_ML * exp(-htot(i) * Idecay_len_TKE(i))
26250 if (RiBulk * Uh2 <= htot(i)**2 * gHprime) then
26260 visc%nkml_visc_v(i,J) = real(k_massive(i))
26270 do_i(i) = .false.
26280 elseif (RiBulk * Uh2 <= (htot(i) + hlay)**2 * gHprime) then
2629 visc%nkml_visc_v(i,J) = real(k-1) + &
26300 ( sqrt(RiBulk * Uh2 / gHprime) - htot(i) ) / hlay
26310 do_i(i) = .false.
2632 endif
2633 endif
26340 k_massive(i) = k
2635 endif ! hlay > h_tiny
2636
26370 if (do_i(i)) do_any = .true.
2638 endif ; enddo
2639
26400 if (.not.do_any) exit ! All columns are done.
2641 endif
2642
26430 do i=is,ie ; if (do_i(i)) then
26440 htot(i) = htot(i) + 0.5 * (h(i,J,k) + h(i,j+1,k))
26450 vhtot(i) = vhtot(i) + 0.5 * (h(i,j,k) + h(i,j+1,k)) * v(i,J,k)
2646 uhtot(i) = uhtot(i) + 0.25 * ((h(i,j,k) * (u(I-1,j,k) + u(I,j,k))) + &
26470 (h(i,j+1,k) * (u(I-1,j+1,k) + u(I,j+1,k))))
26480 if (use_EOS) then
26490 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)))
26500 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)))
2651 else
26520 Rhtot(i) = Rhtot(i) + 0.5 * (h(i,j,k) + h(i,j+1,k)) * GV%Rlay(k)
2653 endif
2654 endif ; enddo
2655 enddo ; endif
2656
26570 if (do_any) then ; do i=is,ie ; if (do_i(i)) then
26580 visc%nkml_visc_v(i,J) = k_massive(i)
2659 endif ; enddo ; endif
2660
2661 endif ! dynamic_viscous_ML
2662
26630 do_any_shelf = .false.
26640 if (associated(forces%frac_shelf_v)) then
26650 do i=is,ie
26660 if (forces%frac_shelf_v(i,J)*G%mask2dCv(i,J) == 0.0) then
26670 do_i(i) = .false.
26680 visc%tbl_thick_shelf_v(i,J) = 0.0 ; visc%kv_tbl_shelf_v(i,J) = 0.0
2669 else
26700 do_i(i) = .true. ; do_any_shelf = .true.
2671 endif
2672 enddo
2673 endif
2674
26750 if (do_any_shelf) then
26760 do k=1,nz ; do i=is,ie ; if (do_i(i)) then
26770 if (v(i,J,k) * (h(i,j+1,k) - h(i,j,k)) >= 0) then
2678 h_at_vel(i,k) = 2.0*h(i,j,k)*h(i,j+1,k) / &
26790 (h(i,j,k) + h(i,j+1,k) + h_neglect)
2680 dz_at_vel(i,k) = 2.0*dz(i,j,k)*dz(i,j+1,k) / &
26810 (dz(i,j,k) + dz(i,j+1,k) + dz_neglect)
2682 else
26830 h_at_vel(i,k) = 0.5 * (h(i,j,k) + h(i,j+1,k))
26840 dz_at_vel(i,k) = 0.5 * (dz(i,j,k) + dz(i,j+1,k))
2685 endif
2686 else
26870 h_at_vel(I,k) = 0.0
26880 dz_at_vel(I,k) = 0.0
26890 ustar(i) = 0.0
2690 endif ; enddo ; enddo
2691
26920 do i=is,ie ; if (do_i(i)) then
26930 htot_vel = 0.0 ; hwtot = 0.0 ; hutot = 0.0
26940 Thtot(i) = 0.0 ; Shtot(i) = 0.0 ; SpV_htot(i) = 0.0
26950 if (use_EOS .or. .not.CS%linear_drag) then ; do k=1,nz
26960 if (htot_vel>=CS%Htbl_shelf) exit ! terminate the k loop
26970 hweight = MIN(CS%Htbl_shelf - htot_vel, h_at_vel(i,k))
26980 if (hweight <= 1.5*GV%Angstrom_H + h_neglect) cycle
2699
27000 htot_vel = htot_vel + h_at_vel(i,k)
27010 hwtot = hwtot + hweight
2702
27030 if (.not.CS%linear_drag) then
27040 u_at_v = set_u_at_v(u, h, G, GV, i, J, k, mask_u, OBC)
2705 ! Set the "back ground" friction velocity scale to either the tidal amplitude or place-holder constant
27060 if (CS%BBL_use_tidal_bg) then
2707 u2_bg(i) = 0.5*( G%mask2dT(i,j)*(CS%tideamp(i,j)*CS%tideamp(i,j))+ &
27080 G%mask2dT(i,j+1)*(CS%tideamp(i,j+1)*CS%tideamp(i,j+1)) )
2709 else
27100 u2_bg(i) = CS%drag_bg_vel * CS%drag_bg_vel
2711 endif
27120 hutot = hutot + hweight * sqrt(v(i,J,k)**2 + u_at_v**2 + u2_bg(i))
2713 endif
27140 if (use_EOS) then
27150 Thtot(i) = Thtot(i) + hweight * 0.5 * (tv%T(i,j,k) + tv%T(i,j+1,k))
27160 Shtot(i) = Shtot(i) + hweight * 0.5 * (tv%S(i,j,k) + tv%S(i,j+1,k))
2717 endif
27180 if (allocated(tv%SpV_avg)) then
27190 SpV_htot(i) = SpV_htot(i) + hweight * 0.5 * (tv%SpV_avg(i,j,k) + tv%SpV_avg(i,j+1,k))
2720 endif
2721 enddo ; endif
2722
27230 if ((hwtot <= 0.0) .or. (CS%linear_drag .and. .not.allocated(tv%SpV_avg))) then
27240 ustar(i) = cdrag_sqrt_H * CS%drag_bg_vel
27250 elseif (CS%linear_drag .and. allocated(tv%SpV_avg)) then
27260 ustar(i) = cdrag_sqrt_H_RL * CS%drag_bg_vel * (hwtot / SpV_htot(i))
27270 elseif (allocated(tv%SpV_avg)) then ! (.not.CS%linear_drag)
27280 ustar(i) = cdrag_sqrt_H_RL * hutot / SpV_htot(i)
2729 else ! (.not.CS%linear_drag .and. .not.allocated(tv%SpV_avg))
27300 ustar(i) = cdrag_sqrt_H * hutot / hwtot
2731 endif
2732
27330 if (use_EOS) then ; if (hwtot > 0.0) then
27340 T_EOS(i) = Thtot(i)/hwtot ; S_EOS(i) = Shtot(i)/hwtot
2735 else
27360 T_EOS(i) = 0.0 ; S_EOS(i) = 0.0
2737 endif ; endif
2738 endif ; enddo ! I-loop
2739
27400 if (use_EOS) then
2741 call calculate_density_derivs(T_EOS, S_EOS, forces%p_surf(:,j), dR_dT, dR_dS, &
27420 tv%eqn_of_state, (/is-G%IsdB+1,ie-G%IsdB+1/) )
2743 endif
2744
27450 do i=is,ie ; if (do_i(i)) then
2746 ! The 400.0 in this expression is the square of a constant proposed
2747 ! by Killworth and Edwards, 1999, in equation (2.20).
27480 ustarsq = Rho0x400_G * ustar(i)**2
27490 htot(i) = 0.0
27500 dztot(i) = 0.0
27510 if (use_EOS) then
27520 Thtot(i) = 0.0 ; Shtot(i) = 0.0 ; oldfn = 0.0
27530 do k=1,nz-1
27540 if (h_at_vel(i,k) <= 0.0) cycle
27550 T_Lay = 0.5 * (tv%T(i,j,k) + tv%T(i,j+1,k))
27560 S_Lay = 0.5 * (tv%S(i,j,k) + tv%S(i,j+1,k))
27570 oldfn = dR_dT(i)*(T_Lay*htot(i) - Thtot(i)) + dR_dS(i)*(S_Lay*htot(i) - Shtot(i))
27580 if (oldfn >= ustarsq) exit
2759
2760 Dfn = (dR_dT(i)*(0.5*(tv%T(i,j,k+1)+tv%T(i,j+1,k+1)) - T_Lay) + &
2761 dR_dS(i)*(0.5*(tv%S(i,j,k+1)+tv%S(i,j+1,k+1)) - S_Lay)) * &
27620 (h_at_vel(i,k)+htot(i))
27630 if ((oldfn + Dfn) <= ustarsq) then
27640 Dh = h_at_vel(i,k)
27650 Ddz = dz_at_vel(i,k)
2766 else
27670 frac_used = sqrt((ustarsq-oldfn) / (Dfn))
27680 Dh = h_at_vel(i,k) * frac_used
27690 Ddz = dz_at_vel(i,k) * frac_used
2770 endif
2771
27720 htot(i) = htot(i) + Dh
27730 dztot(i) = dztot(i) + Ddz
27740 Thtot(i) = Thtot(i) + T_Lay*Dh ; Shtot(i) = Shtot(i) + S_Lay*Dh
2775 enddo
27760 if ((oldfn < ustarsq) .and. (h_at_vel(i,nz) > 0.0)) then
27770 T_Lay = 0.5*(tv%T(i,j,nz) + tv%T(i,j+1,nz))
27780 S_Lay = 0.5*(tv%S(i,j,nz) + tv%S(i,j+1,nz))
27790 if (dR_dT(i)*(T_Lay*htot(i) - Thtot(i)) + &
2780 dR_dS(i)*(S_Lay*htot(i) - Shtot(i)) < ustarsq) then
27810 htot(i) = htot(i) + h_at_vel(i,nz)
27820 dztot(i) = dztot(i) + dz_at_vel(i,nz)
2783 endif
2784 endif ! Examination of layer nz.
2785 else ! Use Rlay as the density variable.
27860 Rhtot = 0.0
27870 do k=1,nz-1
27880 Rlay = GV%Rlay(k) ; Rlb = GV%Rlay(k+1)
2789
27900 oldfn = Rlay*htot(i) - Rhtot(i)
27910 if (oldfn >= ustarsq) exit
2792
27930 Dfn = (Rlb - Rlay)*(h_at_vel(i,k)+htot(i))
27940 if ((oldfn + Dfn) <= ustarsq) then
27950 Dh = h_at_vel(i,k)
27960 Ddz = dz_at_vel(i,k)
2797 else
27980 frac_used = sqrt((ustarsq-oldfn) / (Dfn))
27990 Dh = h_at_vel(i,k) * frac_used
28000 Ddz = dz_at_vel(i,k) * frac_used
2801 endif
2802
28030 htot(i) = htot(i) + Dh
28040 dztot(i) = dztot(i) + Ddz
28050 Rhtot = Rhtot + Rlay*Dh
2806 enddo
28070 if (GV%Rlay(nz)*htot(i) - Rhtot(i) < ustarsq) then
28080 htot(i) = htot(i) + h_at_vel(i,nz)
28090 dztot(i) = dztot(i) + dz_at_vel(i,nz)
2810 endif
2811 endif ! use_EOS
2812
2813 ! visc%tbl_thick_shelf_v(i,J) = max(CS%Htbl_shelf_min, &
2814 ! dztot(i) / (0.5 + sqrt(0.25 + &
2815 ! (htot(i)*(G%CoriolisBu(I-1,J)+G%CoriolisBu(I,J)))**2 / &
2816 ! (ustar(i))**2 )) )
28170 ustar1 = ustar(i)
28180 h2f2 = (htot(i)*(G%CoriolisBu(I-1,J)+G%CoriolisBu(I,J)) + h_neglect*CS%omega)**2
2819 tbl_thick = max(CS%Htbl_shelf_min, &
28200 ( dztot(i)*ustar(i) ) / ( 0.5*ustar1 + sqrt((0.5*ustar1)**2 + h2f2 ) ) )
28210 visc%tbl_thick_shelf_v(i,J) = tbl_thick
28220 visc%Kv_tbl_shelf_v(i,J) = max(CS%Kv_TBL_min, cdrag_sqrt*ustar1*tbl_thick)
2823
2824 endif ; enddo ! i-loop
2825 endif ! do_any_shelf
2826
2827 enddo ! J-loop at v-points
2828
2829 !$omp target update to(visc%nkml_visc_u, visc%nkml_visc_v) if (CS%dynamic_viscous_ML)
2830
28310 if (CS%debug) then
28320 if (allocated(visc%nkml_visc_u) .and. allocated(visc%nkml_visc_v)) &
2833 call uvchksum("nkml_visc_[uv]", visc%nkml_visc_u, visc%nkml_visc_v, &
28340 G%HI, haloshift=0, scalar_pair=.true.)
2835 endif
28360 if (CS%id_nkml_visc_u > 0) call post_data(CS%id_nkml_visc_u, visc%nkml_visc_u, CS%diag)
28370 if (CS%id_nkml_visc_v > 0) call post_data(CS%id_nkml_visc_v, visc%nkml_visc_v, CS%diag)
2838
2839end subroutine set_viscous_ML
2840
2841!> Register any fields associated with the vertvisc_type.
28421subroutine set_visc_register_restarts(HI, G, GV, US, param_file, visc, restart_CS, use_ice_shelf)
2843 type(hor_index_type), intent(in) :: HI !< A horizontal index type structure.
2844 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
2845 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
2846 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
2847 type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time
2848 !! parameters.
2849 type(vertvisc_type), intent(inout) :: visc !< A structure containing vertical
2850 !! viscosities and related fields.
2851 !! Allocated here.
2852 type(MOM_restart_CS), intent(inout) :: restart_CS !< MOM restart control structure
2853 logical, intent(in) :: use_ice_shelf !< if true, register tau_shelf restarts
2854 ! Local variables
2855 logical :: use_kappa_shear, KS_at_vertex
2856 logical :: adiabatic, useKPP, useEPBL, use_ideal_age
2857 logical :: do_brine_plume, use_hor_bnd_diff, use_neutral_diffusion, use_fpmix
2858 logical :: use_CVMix_shear, MLE_use_PBL_MLD, MLE_use_Bodner, use_CVMix_conv
2859 integer :: isd, ied, jsd, jed, nz
2860 real :: hfreeze !< If hfreeze > 0 [Z ~> m], melt potential will be computed.
2861 character(len=16) :: Kv_units, Kd_units
2862 character(len=40) :: mdl = "MOM_set_visc" ! This module's name.
286311 type(vardesc) :: u_desc, v_desc
28641 isd = HI%isd ; ied = HI%ied ; jsd = HI%jsd ; jed = HI%jed ; nz = GV%ke
2865
2866 call get_param(param_file, mdl, "ADIABATIC", adiabatic, default=.false., &
28671 do_not_log=.true.)
2868
28691 use_kappa_shear = .false. ; KS_at_vertex = .false. ; use_CVMix_shear = .false.
28701 useKPP = .false. ; useEPBL = .false. ; use_CVMix_conv = .false.
2871
28721 if (.not.adiabatic) then
28731 use_kappa_shear = kappa_shear_is_used(param_file)
28741 KS_at_vertex = kappa_shear_at_vertex(param_file)
28751 use_CVMix_shear = CVMix_shear_is_used(param_file)
28761 use_CVMix_conv = CVMix_conv_is_used(param_file)
2877 call get_param(param_file, mdl, "USE_KPP", useKPP, &
2878 "If true, turns on the [CVMix] KPP scheme of Large et al., 1994, "//&
2879 "to calculate diffusivities and non-local transport in the OBL.", &
28801 default=.false., do_not_log=.true.)
2881 call get_param(param_file, mdl, "ENERGETICS_SFC_PBL", useEPBL, &
2882 "If true, use an implied energetics planetary boundary "//&
2883 "layer scheme to determine the diffusivity and viscosity "//&
28841 "in the surface boundary layer.", default=.false., do_not_log=.true.)
2885 endif
2886
28871 if (GV%Boussinesq) then
28881 Kv_units = "m2 s-1" ; Kd_units = "m2 s-1"
2889 else
28900 Kv_units = "Pa s" ; Kd_units = "kg m-1 s-1"
2891 endif
2892
28931 if (use_kappa_shear .or. useKPP .or. useEPBL .or. use_CVMix_shear .or. use_CVMix_conv) then
28941 call safe_alloc_ptr(visc%Kd_shear, isd, ied, jsd, jed, nz+1)
2895 call register_restart_field(visc%Kd_shear, "Kd_shear", .false., restart_CS, &
2896 "Shear-driven turbulent diffusivity at interfaces", &
28971 units=Kd_units, conversion=GV%HZ_T_to_MKS, z_grid='i')
2898 endif
28991 if (useKPP .or. useEPBL .or. use_CVMix_shear .or. use_CVMix_conv .or. &
2900 (use_kappa_shear .and. .not.KS_at_vertex )) then
29011 call safe_alloc_ptr(visc%Kv_shear, isd, ied, jsd, jed, nz+1)
2902 call register_restart_field(visc%Kv_shear, "Kv_shear", .false., restart_CS, &
2903 "Shear-driven turbulent viscosity at interfaces", &
29041 units=Kv_units, conversion=GV%HZ_T_to_MKS, z_grid='i')
2905 endif
29061 if (use_kappa_shear .and. KS_at_vertex) then
29071 call safe_alloc_ptr(visc%TKE_turb, HI%IsdB, HI%IedB, HI%JsdB, HI%JedB, nz+1)
29081 call safe_alloc_ptr(visc%Kv_shear_Bu, HI%IsdB, HI%IedB, HI%JsdB, HI%JedB, nz+1)
2909 call register_restart_field(visc%Kv_shear_Bu, "Kv_shear_Bu", .false., restart_CS, &
2910 "Shear-driven turbulent viscosity at vertex interfaces", &
29111 units=Kv_units, conversion=GV%HZ_T_to_MKS, hor_grid="Bu", z_grid='i')
29120 elseif (use_kappa_shear) then
29130 call safe_alloc_ptr(visc%TKE_turb, isd, ied, jsd, jed, nz+1)
2914 endif
2915
29161 if (useKPP) then
2917 ! MOM_bkgnd_mixing uses Kv_slow when KPP is defined.
29180 call safe_alloc_ptr(visc%Kv_slow, isd, ied, jsd, jed, nz+1)
2919 endif
2920
2921 ! visc%MLD and visc%h_ML are used to communicate the state of the (e)PBL or KPP to the rest of the model
2922 call get_param(param_file, mdl, "MLE_USE_PBL_MLD", MLE_use_PBL_MLD, &
29231 default=.false., do_not_log=.true.)
2924 ! visc%h_ML needs to be allocated when melt potential is computed (HFREEZE>0) or one of
2925 ! several other parameterizations are in use.
2926 call get_param(param_file, mdl, "HFREEZE", hfreeze, &
29271 units="m", default=-1.0, scale=US%m_to_Z, do_not_log=.true.)
2928 call get_param(param_file, mdl, "DO_BRINE_PLUME", do_brine_plume, &
2929 "If true, use a brine plume parameterization from Nguyen et al., 2009.", &
29301 default=.false., do_not_log=.true.)
2931 call get_param(param_file, mdl, "USE_HORIZONTAL_BOUNDARY_DIFFUSION", use_hor_bnd_diff, &
29321 default=.false., do_not_log=.true.)
2933 call get_param(param_file, mdl, "USE_NEUTRAL_DIFFUSION", use_neutral_diffusion, &
29341 default=.false., do_not_log=.true.)
29351 if (use_neutral_diffusion) &
2936 call get_param(param_file, mdl, "NDIFF_INTERIOR_ONLY", use_neutral_diffusion, &
29370 default=.false., do_not_log=.true.)
2938 call get_param(param_file, mdl, "FPMIX", use_fpmix, &
29391 default=.false., do_not_log=.true.)
2940 call get_param(param_file, mdl, "USE_IDEAL_AGE_TRACER", use_ideal_age, &
29411 default=.false., do_not_log=.true.)
29421 call openParameterBlock(param_file, 'MLE', do_not_log=.true.)
2943 call get_param(param_file, mdl, "USE_BODNER23", MLE_use_Bodner, &
29441 default=.false., do_not_log=.true.)
29451 call closeParameterBlock(param_file)
2946
29471 if (MLE_use_PBL_MLD .or. MLE_use_Bodner) then
29481 call safe_alloc_ptr(visc%MLD, isd, ied, jsd, jed)
2949 endif
2950 if ((hfreeze >= 0.0) .or. MLE_use_PBL_MLD .or. do_brine_plume .or. use_fpmix .or. &
29511 use_neutral_diffusion .or. use_hor_bnd_diff .or. use_ideal_age) then
29521 call safe_alloc_ptr(visc%h_ML, isd, ied, jsd, jed)
2953 endif
2954
29551 if (MLE_use_PBL_MLD) then
2956 call register_restart_field(visc%MLD, "MLD", .false., restart_CS, &
29571 "Instantaneous active mixing layer depth", units="m", conversion=US%Z_to_m)
2958 endif
2959 if (MLE_use_PBL_MLD .or. do_brine_plume .or. use_fpmix .or. &
29601 use_neutral_diffusion .or. use_hor_bnd_diff) then
2961 call register_restart_field(visc%h_ML, "h_ML", .false., restart_CS, &
2962 "Instantaneous active mixing layer thickness", &
29631 units=get_thickness_units(GV), conversion=GV%H_to_mks)
2964 endif
2965
2966 ! visc%sfc_buoy_flx is used to communicate the state of the (e)PBL or KPP to the rest of the model
29671 if (MLE_use_PBL_MLD .or. MLE_use_Bodner) then
29681 call safe_alloc_ptr(visc%sfc_buoy_flx, isd, ied, jsd, jed)
2969 call register_restart_field(visc%sfc_buoy_flx, "SFC_BFLX", .false., restart_CS, &
2970 "Instantaneous surface buoyancy flux", "m2 s-3", &
29711 conversion=US%Z_to_m**2*US%s_to_T**3)
2972 endif
2973
29741 if (use_ice_shelf) then
29750 if (.not.allocated(visc%taux_shelf)) &
29760 allocate(visc%taux_shelf(G%IsdB:G%IedB, G%jsd:G%jed), source=0.0)
29770 if (.not.allocated(visc%tauy_shelf)) &
29780 allocate(visc%tauy_shelf(G%isd:G%ied, G%JsdB:G%JedB), source=0.0)
2979 u_desc = var_desc("u_taux_shelf", "Pa", "the zonal stress on the ocean under ice shelves", &
29800 hor_grid='Cu',z_grid='1')
2981 v_desc = var_desc("v_tauy_shelf", "Pa", "the meridional stress on the ocean under ice shelves", &
29820 hor_grid='Cv',z_grid='1')
2983 call register_restart_pair(visc%taux_shelf, visc%tauy_shelf, u_desc, v_desc, &
29840 .false., restart_CS, conversion=US%RZ_T_to_kg_m2s*US%L_T_to_m_s)
2985 endif
2986
298711end subroutine set_visc_register_restarts
2988
2989!> This subroutine does remapping for the auxiliary restart variables in a vertvisc_type
2990!! that are used across timesteps
29910subroutine remap_vertvisc_aux_vars(G, GV, visc, h_old, h_new, ALE_CSp, OBC)
2992 type(ocean_grid_type), intent(inout) :: G !< ocean grid structure
2993 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
2994 type(vertvisc_type), intent(inout) :: visc !< A structure containing vertical
2995 !! viscosities and related fields.
2996 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
2997 intent(in) :: h_old !< Thickness of source grid [H ~> m or kg m-2]
2998 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
2999 intent(in) :: h_new !< Thickness of destination grid [H ~> m or kg m-2]
3000 type(ALE_CS), pointer :: ALE_CSp !< ALE control structure to use when remapping
3001 type(ocean_OBC_type), pointer :: OBC !< Open boundary structure
3002
30030 if (associated(visc%Kd_shear)) then
30040 call ALE_remap_interface_vals(ALE_CSp, G, GV, h_old, h_new, visc%Kd_shear)
3005 endif
3006
30070 if (associated(visc%Kv_shear)) then
30080 call ALE_remap_interface_vals(ALE_CSp, G, GV, h_old, h_new, visc%Kv_shear)
3009 endif
3010
30110 if (associated(visc%Kv_shear_Bu)) then
30120 call ALE_remap_vertex_vals(ALE_CSp, G, GV, h_old, h_new, visc%Kv_shear_Bu)
3013 endif
3014
30150end subroutine remap_vertvisc_aux_vars
3016
3017!> Initializes the MOM_set_visc control structure
30181subroutine set_visc_init(Time, G, GV, US, param_file, diag, visc, CS, restart_CS, OBC)
3019 type(time_type), target, intent(in) :: Time !< The current model time.
3020 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
3021 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
3022 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
3023 type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time
3024 !! parameters.
3025 type(diag_ctrl), target, intent(inout) :: diag !< A structure that is used to regulate diagnostic
3026 !! output.
3027 type(vertvisc_type), intent(inout) :: visc !< A structure containing vertical viscosities and
3028 !! related fields.
3029 type(set_visc_CS), intent(inout) :: CS !< Vertical viscosity control structure
3030 type(MOM_restart_CS), intent(inout) :: restart_CS !< MOM restart control structure
3031 type(ocean_OBC_type), pointer :: OBC !< A pointer to an open boundary condition structure
3032
3033 ! Local variables
3034 real :: Csmag_chan_dflt ! The default value for SMAG_CONST_CHANNEL [nondim]
3035 real :: smag_const1 ! The default value for the Smagorinsky Laplacian coefficient [nondim]
3036 real :: TKE_decay_dflt ! The default value of a coefficient scaling the vertical decay
3037 ! rate of TKE [nondim]
3038 real :: bulk_Ri_ML_dflt ! The default bulk Richardson number for a bulk mixed layer [nondim]
3039 real :: Kv_background ! The background kinematic viscosity in the interior [Z2 T-1 ~> m2 s-1]
3040 real :: omega_frac_dflt ! The default value for the fraction of the absolute rotation rate that
3041 ! is used in place of the absolute value of the local Coriolis
3042 ! parameter in the denominator of some expressions [nondim]
3043 real :: Chan_max_thick_dflt ! The default value for CHANNEL_DRAG_MAX_THICK [Z ~> m]
3044 real :: tideamp_factor ! A factor to multiply by tideamp when converting to mean tidal magnitude [nondim]
3045 real :: shelfbreak_depth ! When CHANNEL_DRAG is true, the bathymetric depth interpolated
3046 ! to the vorticity point is a combination of the harmonic mean of the
3047 ! adjacent velocity point depths below this depth [Z ~> m] and the
3048 ! arithmetic mean of the adjacent depths above it, to roughly mimic a
3049 ! continental shelf break profile.
30501 real, allocatable, dimension(:,:) :: cdrag_h !< The spatially varying quadratic drag coefficient [nondim]
3051
3052 integer :: i, j, is, ie, js, je
3053 integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, nz
3054 integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags.
3055 logical :: adiabatic, use_omega, MLE_use_PBL_MLD
3056 logical :: use_KPP
3057 logical :: use_regridding ! If true, use the ALE algorithm rather than layered
3058 ! isopycnal or stacked shallow water mode.
3059 logical :: use_temperature ! If true, temperature and salinity are used as state variables.
3060 logical :: use_EOS ! If true, density calculated from T & S using an equation of state.
3061 character(len=200) :: filename, cdrag_file, tideamp_file ! Input file names or paths
3062 character(len=80) :: cdrag_var, tideamp_var ! Input file variable names
3063 ! This include declares and sets the variable "version".
3064# include "version_variable.h"
3065 character(len=40) :: mdl = "MOM_set_visc" ! This module's name.
3066
30671 CS%initialized = .true.
30681 CS%OBC => OBC
3069
30701 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
30711 isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed ; nz = GV%ke
30721 IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB
3073
30741 CS%diag => diag
3075
3076 ! Set default, read and log parameters
30771 call log_version(param_file, mdl, version, "")
30781 CS%RiNo_mix = .false.
30791 call get_param(param_file, mdl, "INPUTDIR", CS%inputdir, default=".")
30801 CS%inputdir = slasher(CS%inputdir)
3081 call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, &
3082 "This sets the default value for the various _ANSWER_DATE parameters.", &
30831 default=99991231)
3084 call get_param(param_file, mdl, "SET_VISC_ANSWER_DATE", CS%answer_date, &
3085 "The vintage of the order of arithmetic and expressions in the set viscosity "//&
3086 "calculations. Values below 20190101 recover the answers from the end of 2018, "//&
3087 "while higher values use updated and more robust forms of the same expressions.", &
30881 default=default_answer_date, do_not_log=.not.GV%Boussinesq)
30891 if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701)
3090 call get_param(param_file, mdl, "BOTTOMDRAGLAW", CS%bottomdraglaw, &
3091 "If true, the bottom stress is calculated with a drag "//&
3092 "law of the form c_drag*|u|*u. The velocity magnitude "//&
3093 "may be an assumed value or it may be based on the "//&
3094 "actual velocity in the bottommost HBBL, depending on "//&
30951 "LINEAR_DRAG.", default=.true.)
3096 call get_param(param_file, mdl, "DRAG_AS_BODY_FORCE", CS%body_force_drag, &
3097 "If true, the bottom stress is imposed as an explicit body force "//&
3098 "applied over a fixed distance from the bottom, rather than as an "//&
3099 "implicit calculation based on an enhanced near-bottom viscosity. "//&
3100 "The thickness of the bottom boundary layer is HBBL.", &
31011 default=.false., do_not_log=.not.CS%bottomdraglaw)
3102 call get_param(param_file, mdl, "CHANNEL_DRAG", CS%Channel_drag, &
3103 "If true, the bottom drag is exerted directly on each "//&
3104 "layer proportional to the fraction of the bottom it overlies.", &
31051 default=.false.)
3106 call get_param(param_file, mdl, "CHANNEL_DRAG_SHELFBREAK_DEPTH", shelfbreak_depth, &
3107 "When CHANNEL_DRAG is true, the bathymetric depth interpolated to the "//&
3108 "vorticity point is a combination of the harmonic mean of the adjacent "//&
3109 "velocity point depths below this depth and the arithmetic mean of the "//&
3110 "depths above it, to roughly mimic a continental shelf break profile. "//&
3111 "Setting this to exceed MAXIMUM_DEPTH leads to linear interpolation of "//&
3112 "the topography between velocity points.", &
31131 default=0.0, units="m", scale=US%m_to_Z, do_not_log=.not.CS%Channel_drag)
31141 CS%channel_break_depth = shelfbreak_depth - G%Z_ref
3115
3116 call get_param(param_file, mdl, "LINEAR_DRAG", CS%linear_drag, &
3117 "If LINEAR_DRAG and BOTTOMDRAGLAW are defined the drag "//&
31181 "law is cdrag*DRAG_BG_VEL*u.", default=.false.)
3119 call get_param(param_file, mdl, "ADIABATIC", adiabatic, default=.false., &
31201 do_not_log=.true.)
31211 if (adiabatic) then
3122 call log_param(param_file, mdl, "ADIABATIC",adiabatic, &
3123 "There are no diapycnal mass fluxes if ADIABATIC is true. "//&
3124 "This assumes that KD = 0.0 and that there is no buoyancy forcing, "//&
31250 "but makes the model faster by eliminating subroutine calls.", default=.false.)
3126 endif
3127
31281 if (.not.adiabatic) then
31291 CS%RiNo_mix = kappa_shear_is_used(param_file)
3130 endif
3131
31321 call get_param(param_file, mdl, "DEBUG", CS%debug, default=.false.)
3133
3134 call get_param(param_file, mdl, "DYNAMIC_VISCOUS_ML", CS%dynamic_viscous_ML, &
3135 "If true, use a bulk Richardson number criterion to "//&
3136 "determine the mixed layer thickness for viscosity.", &
31371 default=.false.)
31381 if (CS%dynamic_viscous_ML) then
31390 call get_param(param_file, mdl, "BULK_RI_ML", bulk_Ri_ML_dflt, units="nondim", default=0.0)
3140 call get_param(param_file, mdl, "BULK_RI_ML_VISC", CS%bulk_Ri_ML, &
3141 "The efficiency with which mean kinetic energy released by mechanically "//&
3142 "forced entrainment of the mixed layer is converted to turbulent "//&
3143 "kinetic energy. By default, BULK_RI_ML_VISC = BULK_RI_ML or 0.", &
31440 units="nondim", default=bulk_Ri_ML_dflt)
31450 call get_param(param_file, mdl, "TKE_DECAY", TKE_decay_dflt, units="nondim", default=0.0)
3146 call get_param(param_file, mdl, "TKE_DECAY_VISC", CS%TKE_decay, &
3147 "TKE_DECAY_VISC relates the vertical rate of decay of "//&
3148 "the TKE available for mechanical entrainment to the "//&
3149 "natural Ekman depth for use in calculating the dynamic "//&
3150 "mixed layer viscosity. By default, TKE_DECAY_VISC = TKE_DECAY or 0.", &
31510 units="nondim", default=TKE_decay_dflt)
3152 call get_param(param_file, mdl, "ML_USE_OMEGA", use_omega, &
3153 "If true, use the absolute rotation rate instead of the "//&
3154 "vertical component of rotation when setting the decay "//&
31550 "scale for turbulence.", default=.false., do_not_log=.true.)
31560 omega_frac_dflt = 0.0
31570 if (use_omega) then
31580 call MOM_error(WARNING, "ML_USE_OMEGA is deprecated; use ML_OMEGA_FRAC=1.0 instead.")
31590 omega_frac_dflt = 1.0
3160 endif
3161 call get_param(param_file, mdl, "ML_OMEGA_FRAC", CS%omega_frac, &
3162 "When setting the decay scale for turbulence, use this "//&
3163 "fraction of the absolute rotation rate blended with the "//&
3164 "local value of f, as sqrt((1-of)*f^2 + of*4*omega^2).", &
31650 units="nondim", default=omega_frac_dflt)
3166 call get_param(param_file, mdl, "OMEGA", CS%omega, &
3167 "The rotation rate of the earth.", &
31680 units="s-1", default=7.2921e-5, scale=US%T_to_s)
3169 ! This give a minimum decay scale that is typically much less than Angstrom.
31700 CS%ustar_min = 2e-4*CS%omega*(GV%Angstrom_H + GV%H_subroundoff)
3171 else
3172 call get_param(param_file, mdl, "OMEGA", CS%omega, &
3173 "The rotation rate of the earth.", &
31741 units="s-1", default=7.2921e-5, scale=US%T_to_s)
3175 endif
3176
3177 call get_param(param_file, mdl, "HBBL", CS%dz_bbl, &
3178 "The thickness of a bottom boundary layer with a viscosity increased by "//&
3179 "KV_EXTRA_BBL if BOTTOMDRAGLAW is not defined, or the thickness over which "//&
3180 "near-bottom velocities are averaged for the drag law if BOTTOMDRAGLAW is "//&
3181 "defined but LINEAR_DRAG is not.", &
31821 units="m", scale=US%m_to_Z, fail_if_missing=.true.) ! Rescaled later
31831 if (CS%bottomdraglaw) then
3184 call get_param(param_file, mdl, "CDRAG", CS%cdrag, &
3185 "CDRAG is the drag coefficient relating the magnitude of "//&
3186 "the velocity field to the bottom stress. CDRAG is only "//&
31871 "used if BOTTOMDRAGLAW is defined.", units="nondim", default=0.003)
3188 call get_param(param_file, mdl, "CDRAG_MAP", CS%bottomdragmap, &
3189 "If true, apply a spatially varying scaling factor to CDRAG, "//&
31901 "specified by CDRAG_VAR in CDRAG_FILE.", default=.false.)
3191 call get_param(param_file, mdl, "CDRAG_FILE", cdrag_file, &
3192 "The name of the file with the spatially varying bottom drag "//&
31931 "scaling factor.", default="", do_not_log=.not.CS%bottomdragmap)
3194 call get_param(param_file, mdl, "CDRAG_VAR", cdrag_var, &
3195 "The name of the variable in CDRAG_FILE with the spatially "//&
3196 "varying bottom drag scaling factor at h points.", &
31971 default="", do_not_log=.not.CS%bottomdragmap)
3198 call get_param(param_file, mdl, "BBL_USE_TIDAL_BG", CS%BBL_use_tidal_bg, &
3199 "Flag to use the tidal RMS amplitude in place of constant "//&
3200 "background velocity for computing u* in the BBL. "//&
3201 "This flag is only used when BOTTOMDRAGLAW is true and "//&
32021 "LINEAR_DRAG is false.", default=.false.)
32031 if (CS%BBL_use_tidal_bg) then
3204 call get_param(param_file, mdl, "TIDEAMP_FILE", tideamp_file, &
3205 "The path to the file containing the spatially varying "//&
32060 "tidal amplitudes with INT_TIDE_DISSIPATION.", default="tideamp.nc")
3207 call get_param(param_file, mdl, "TIDEAMP_VARNAME", tideamp_var, &
3208 "The name of the tidal amplitude variable in the input file.", &
32090 default="tideamp")
3210 ! This value is here only to detect whether it is inadvertently used. CS%drag_bg_vel should
3211 ! not be used if CS%BBL_use_tidal_bg is True. For this reason, we do not apply dimensions,
3212 ! nor dimensional testing in this mode. If we ever detect a dimensional sensitivity to
3213 ! this parameter, in this mode, then it means it is being used inappropriately.
32140 CS%drag_bg_vel = 1.e30
3215 call get_param(param_file, mdl, "TIDEAMP_FACTOR", tideamp_factor, &
3216 "A parameter to multiply by tideamp when converting to ustar. "//&
3217 "It accounts for converting the amplitude to a mean magintude (approx 1/sqrt(2)) "//&
3218 "and possibly also for non-commuting averaging operators when converting to ustar**3. "//&
3219 "It is ignored if negative and uncapped so it can be greater than 1 if desired.",&
32200 units="nondim", default=-1.0)
32210 if (tideamp_factor < 0.0) then
32220 CS%tideampfac2 = 1.0
3223 else
32240 CS%tideampfac2 = tideamp_factor*tideamp_factor
3225 endif
3226 else
3227 call get_param(param_file, mdl, "DRAG_BG_VEL", CS%drag_bg_vel, &
3228 "DRAG_BG_VEL is either the assumed bottom velocity (with "//&
3229 "LINEAR_DRAG) or an unresolved velocity that is "//&
3230 "combined with the resolved velocity to estimate the "//&
3231 "velocity magnitude. DRAG_BG_VEL is only used when "//&
32321 "BOTTOMDRAGLAW is defined.", units="m s-1", default=0.0, scale=US%m_s_to_L_T)
3233 endif
3234 call get_param(param_file, mdl, "USE_REGRIDDING", use_regridding, &
32351 do_not_log=.true., default=.false. )
3236 call get_param(param_file, mdl, "ENABLE_THERMODYNAMICS", use_temperature, &
32371 default=.true., do_not_log=.true.)
3238 call get_param(param_file, mdl, "USE_EOS", use_EOS, &
32391 default=use_temperature, do_not_log=.true.)
3240 call get_param(param_file, mdl, "BBL_USE_EOS", CS%BBL_use_EOS, &
3241 "If true, use the equation of state in determining the properties of the "//&
3242 "bottom boundary layer. Otherwise use the layer target potential densities. "//&
3243 "The default of this parameter is the value of USE_EOS.", &
32441 default=use_EOS, do_not_log=.not.use_temperature)
32451 if (use_regridding .and. (.not. CS%BBL_use_EOS)) &
32460 call MOM_error(FATAL,"When using MOM6 in ALE mode it is required to set BBL_USE_EOS to True.")
3247 endif
3248 call get_param(param_file, mdl, "BBL_THICK_MIN", CS%BBL_thick_min, &
3249 "The minimum bottom 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-bottom viscosity.", units="m", default=0.0, scale=US%m_to_Z)
3253 call get_param(param_file, mdl, "HTBL_SHELF_MIN", CS%Htbl_shelf_min, &
3254 "The minimum top boundary layer thickness that can be "//&
3255 "used with BOTTOMDRAGLAW. This might be "//&
3256 "Kv/(cdrag*drag_bg_vel) to give Kv as the minimum "//&
32571 "near-top viscosity.", units="m", default=US%Z_to_m*CS%BBL_thick_min, scale=US%m_to_Z)
3258 call get_param(param_file, mdl, "HTBL_SHELF", CS%Htbl_shelf, &
3259 "The thickness over which near-surface velocities are "//&
3260 "averaged for the drag law under an ice shelf. By "//&
3261 "default this is the same as HBBL", &
32621 units="m", default=US%Z_to_m*CS%dz_bbl, scale=GV%m_to_H)
3263
3264 call get_param(param_file, mdl, "KV", Kv_background, &
3265 "The background kinematic viscosity in the interior. "//&
3266 "The molecular value, ~1e-6 m2 s-1, may be used.", &
32671 units="m2 s-1", scale=US%m2_s_to_Z2_T, fail_if_missing=.true.)
3268
3269 call get_param(param_file, mdl, "USE_KPP", use_KPP, &
3270 "If true, turns on the [CVMix] KPP scheme of Large et al., 1994, "//&
3271 "to calculate diffusivities and non-local transport in the OBL.", &
32721 do_not_log=.true., default=.false.)
3273
3274 call get_param(param_file, mdl, "KV_BBL_MIN", CS%KV_BBL_min, &
3275 "The minimum viscosities in the bottom boundary layer.", &
32761 units="m2 s-1", default=US%Z2_T_to_m2_s*Kv_background, scale=GV%m2_s_to_HZ_T)
3277 call get_param(param_file, mdl, "KV_TBL_MIN", CS%KV_TBL_min, &
3278 "The minimum viscosities in the top boundary layer.", &
32791 units="m2 s-1", default=US%Z2_T_to_m2_s*Kv_background, scale=GV%m2_s_to_HZ_T)
3280 call get_param(param_file, mdl, "CORRECT_BBL_BOUNDS", CS%correct_BBL_bounds, &
3281 "If true, uses the correct bounds on the BBL thickness and "//&
3282 "viscosity so that the bottom layer feels the intended drag.", &
32831 default=.false.)
3284
32851 if (CS%Channel_drag) then
32861 call get_param(param_file, mdl, "SMAG_LAP_CONST", smag_const1, units="nondim", default=-1.0)
3287
32881 cSmag_chan_dflt = 0.15
32891 if (smag_const1 >= 0.0) cSmag_chan_dflt = smag_const1
3290
3291 call get_param(param_file, mdl, "SMAG_CONST_CHANNEL", CS%c_Smag, &
3292 "The nondimensional Laplacian Smagorinsky constant used "//&
3293 "in calculating the channel drag if it is enabled. The "//&
3294 "default is to use the same value as SMAG_LAP_CONST if "//&
3295 "it is defined, or 0.15 if it is not. The value used is "//&
3296 "also 0.15 if the specified value is negative.", &
32971 units="nondim", default=cSmag_chan_dflt, do_not_log=.not.CS%Channel_drag)
32981 if (CS%c_Smag < 0.0) CS%c_Smag = 0.15
3299
3300 call get_param(param_file, mdl, "TRIG_CHANNEL_DRAG_WIDTHS", CS%concave_trigonometric_L, &
3301 "If true, use trigonometric expressions to determine the fractional open "//&
3302 "interface lengths for concave topography.", &
33031 default=.true., do_not_log=.not.CS%Channel_drag)
3304 endif
3305
33061 Chan_max_thick_dflt = -1.0*US%m_to_Z
33071 if (CS%RiNo_mix) Chan_max_thick_dflt = 0.5*CS%dz_bbl
33081 if (CS%body_force_drag) Chan_max_thick_dflt = CS%dz_bbl
3309 call get_param(param_file, mdl, "CHANNEL_DRAG_MAX_BBL_THICK", CS%Chan_drag_max_vol, &
3310 "The maximum bottom boundary layer thickness over which the channel drag is "//&
3311 "exerted, or a negative value for no fixed limit, instead basing the BBL "//&
3312 "thickness on the bottom stress, rotation and stratification. The default is "//&
3313 "proportional to HBBL if USE_JACKSON_PARAM or DRAG_AS_BODY_FORCE is true.", &
3314 units="m", default=US%Z_to_m*Chan_max_thick_dflt, scale=US%m_to_Z, &
33151 do_not_log=.not.CS%Channel_drag)
3316
3317 call get_param(param_file, mdl, "MLE_USE_PBL_MLD", MLE_use_PBL_MLD, &
33181 default=.false., do_not_log=.true.)
3319
33201 CS%Hbbl = CS%dz_bbl * (US%Z_to_m * GV%m_to_H) ! Rescaled for use in expressions in thickness units.
3321
3322 ! Update scalar data to device
3323 !$omp target update to(visc)
3324 !$omp target update to(CS)
3325
3326 ! Now update arrays that were defined in set_visc_register_restarts()
3327 !$omp target enter data map(to: visc%Kv_shear) &
3328 !$omp if (associated(visc%Kv_shear))
3329 !$omp target enter data map(to: visc%Kv_shear_Bu) &
3330 !$omp if (associated(visc%Kv_shear_Bu))
3331
33321 if (CS%RiNo_mix .and. kappa_shear_at_vertex(param_file)) then
3333 ! This is necessary for reproducibility across restarts in non-symmetric mode.
33341 call pass_var(visc%Kv_shear_Bu, G%Domain, position=CORNER, complete=.true.)
3335 endif
3336
33371 if (CS%bottomdraglaw) then
33388841 allocate(visc%bbl_thick_u(IsdB:IedB,jsd:jed), source=0.0)
33398902 allocate(visc%bbl_thick_v(isd:ied,JsdB:JedB), source=0.0)
3340 !$omp target enter data map(to: visc%bbl_thick_u, visc%bbl_thick_v)
33418841 allocate(visc%kv_bbl_u(IsdB:IedB,jsd:jed), source=0.0)
33428902 allocate(visc%kv_bbl_v(isd:ied,JsdB:JedB), source=0.0)
3343 !$omp target enter data map(to: visc%kv_bbl_u, visc%kv_bbl_v)
33448773 allocate(visc%ustar_bbl(isd:ied,jsd:jed), source=0.0)
33458773 allocate(visc%BBL_meanKE_loss(isd:ied,jsd:jed), source=0.0)
33468773 allocate(visc%BBL_meanKE_loss_sqrtCd(isd:ied,jsd:jed), source=0.0)
3347
3348 CS%id_bbl_thick_u = register_diag_field('ocean_model', 'bbl_thick_u', &
33491 diag%axesCu1, Time, 'BBL thickness at u points', 'm', conversion=US%Z_to_m)
3350 CS%id_kv_bbl_u = register_diag_field('ocean_model', 'kv_bbl_u', diag%axesCu1, &
33511 Time, 'BBL viscosity at u points', 'm2 s-1', conversion=GV%HZ_T_to_m2_s)
3352 CS%id_bbl_u = register_diag_field('ocean_model', 'bbl_u', diag%axesCu1, &
33531 Time, 'BBL mean u current', 'm s-1', conversion=US%L_T_to_m_s)
33541 if (CS%id_bbl_u>0) then
33550 allocate(CS%bbl_u(IsdB:IedB,jsd:jed), source=0.0)
3356 endif
3357 CS%id_bbl_thick_v = register_diag_field('ocean_model', 'bbl_thick_v', &
33581 diag%axesCv1, Time, 'BBL thickness at v points', 'm', conversion=US%Z_to_m)
3359 CS%id_kv_bbl_v = register_diag_field('ocean_model', 'kv_bbl_v', diag%axesCv1, &
33601 Time, 'BBL viscosity at v points', 'm2 s-1', conversion=GV%HZ_T_to_m2_s)
3361 CS%id_bbl_v = register_diag_field('ocean_model', 'bbl_v', diag%axesCv1, &
33621 Time, 'BBL mean v current', 'm s-1', conversion=US%L_T_to_m_s)
33631 if (CS%id_bbl_v>0) then
33640 allocate(CS%bbl_v(isd:ied,JsdB:JedB), source=0.0)
3365 endif
33661 if (CS%bottomdragmap) then
33670 if (len_trim(cdrag_file)==0 .or. len_trim(cdrag_var)==0) then
33680 call MOM_error(FATAL,"CDRAG_FILE and CDRAG_VAR are required when using CDRAG_MAP.")
3369 endif
33700 allocate(cdrag_h(isd:ied,jsd:jed), source=0.0)
33710 allocate(CS%cdrag_u(IsdB:IedB,jsd:jed), source=0.0)
33720 allocate(CS%cdrag_v(isd:ied,JsdB:JedB), source=0.0)
33730 filename = trim(CS%inputdir) // trim(cdrag_file)
33740 call log_param(param_file, mdl, "INPUTDIR/CDRAG_FILE", filename)
33750 call MOM_read_data(filename, cdrag_var, cdrag_h, G%domain, scale=CS%cdrag)
33760 call pass_var(cdrag_h, G%domain)
33770 do j=js,je ; do I=is-1,ie ; if (G%mask2dCu(I,j) > 0) then
3378 CS%cdrag_u(I,j) = (G%mask2dT(i,j) * cdrag_h(i,j) + G%mask2dT(i+1,j) * cdrag_h(i+1,j)) / &
33790 (G%mask2dT(i,j) + G%mask2dT(i+1,j))
3380 endif ; enddo ; enddo
33810 do J=js-1,je ; do i=is,ie ; if (G%mask2dCv(i,J) > 0) then
3382 CS%cdrag_v(i,J) = (G%mask2dT(i,j) * cdrag_h(i,j) + G%mask2dT(i,j+1) * cdrag_h(i,j+1)) / &
33830 (G%mask2dT(i,j) + G%mask2dT(i,j+1))
3384 endif ; enddo ; enddo
33850 deallocate(cdrag_h)
3386 endif
33871 if (CS%BBL_use_tidal_bg) then
33880 allocate(CS%tideamp(isd:ied,jsd:jed), source=0.0)
33890 filename = trim(CS%inputdir) // trim(tideamp_file)
33900 call log_param(param_file, mdl, "INPUTDIR/TIDEAMP_FILE", filename)
33910 call MOM_read_data(filename, tideamp_var, CS%tideamp, G%domain, scale=US%m_to_Z*US%T_to_s)
33920 call pass_var(CS%tideamp,G%domain)
3393 endif
3394 endif
33951 if (CS%Channel_drag .or. CS%body_force_drag) then
3396663076 allocate(visc%Ray_u(IsdB:IedB,jsd:jed,nz), source=0.0)
3397667651 allocate(visc%Ray_v(isd:ied,JsdB:JedB,nz), source=0.0)
3398 !$omp target enter data map(to: visc%Ray_u, visc%Ray_v)
3399 CS%id_Ray_u = register_diag_field('ocean_model', 'Rayleigh_u', diag%axesCuL, &
34001 Time, 'Rayleigh drag velocity at u points', 'm s-1', conversion=GV%H_to_m*US%s_to_T)
3401 CS%id_Ray_v = register_diag_field('ocean_model', 'Rayleigh_v', diag%axesCvL, &
34021 Time, 'Rayleigh drag velocity at v points', 'm s-1', conversion=GV%H_to_m*US%s_to_T)
3403 endif
3404
3405
34061 if (CS%dynamic_viscous_ML) then
34070 allocate(visc%nkml_visc_u(IsdB:IedB,jsd:jed), source=0.0)
34080 allocate(visc%nkml_visc_v(isd:ied,JsdB:JedB), source=0.0)
3409 !$omp target enter data map(to: visc%nkml_visc_u, visc%nkml_visc_v)
3410
3411 CS%id_nkml_visc_u = register_diag_field('ocean_model', 'nkml_visc_u', &
34120 diag%axesCu1, Time, 'Number of layers in viscous mixed layer at u points', 'nondim')
3413 CS%id_nkml_visc_v = register_diag_field('ocean_model', 'nkml_visc_v', &
34140 diag%axesCv1, Time, 'Number of layers in viscous mixed layer at v points', 'nondim')
3415 endif
3416
34171 call register_restart_field_as_obsolete('Kd_turb','Kd_shear', restart_CS)
34181 call register_restart_field_as_obsolete('Kv_turb','Kv_shear', restart_CS)
3419
34201end subroutine set_visc_init
3421
3422!> This subroutine dellocates any memory in the set_visc control structure.
34231subroutine set_visc_end(visc, CS)
3424 type(vertvisc_type), intent(inout) :: visc !< A structure containing vertical viscosities and
3425 !! related fields. Elements are deallocated here.
3426 type(set_visc_CS), intent(inout) :: CS !< The control structure returned by a previous
3427 !! call to set_visc_init.
3428
34291 if (allocated(visc%bbl_thick_u)) then
3430 !$omp target exit data map(delete: visc%bbl_thick_u)
34311 deallocate(visc%bbl_thick_u)
3432 endif
34331 if (allocated(visc%bbl_thick_v)) then
3434 !$omp target exit data map(delete: visc%bbl_thick_v)
34351 deallocate(visc%bbl_thick_v)
3436 endif
34371 if (allocated(visc%kv_bbl_u)) then
3438 !$omp target exit data map(delete: visc%kv_bbl_u)
34391 deallocate(visc%kv_bbl_u)
3440 endif
34411 if (allocated(visc%kv_bbl_v)) then
3442 !$omp target exit data map(delete: visc%kv_bbl_v)
34431 deallocate(visc%kv_bbl_v)
3444 endif
34451 if (allocated(CS%bbl_u)) deallocate(CS%bbl_u)
34461 if (allocated(CS%bbl_v)) deallocate(CS%bbl_v)
34471 if (allocated(visc%Ray_u)) then
3448 !$omp target exit data map(delete: visc%Ray_u)
34491 deallocate(visc%Ray_u)
3450 endif
34511 if (allocated(visc%Ray_v)) then
3452 !$omp target exit data map(delete: visc%Ray_v)
34531 deallocate(visc%Ray_v)
3454 endif
34551 if (allocated(visc%nkml_visc_u)) then
3456 !$omp target exit data map(delete: visc%nkml_visc_u)
34570 deallocate(visc%nkml_visc_u)
3458 endif
34591 if (allocated(visc%nkml_visc_v)) then
3460 !$omp target exit data map(delete: visc%nkml_visc_v)
34610 deallocate(visc%nkml_visc_v)
3462 endif
34631 if (associated(visc%Kd_shear)) deallocate(visc%Kd_shear)
34641 if (associated(visc%Kv_slow)) deallocate(visc%Kv_slow)
34651 if (associated(visc%TKE_turb)) deallocate(visc%TKE_turb)
34661 if (associated(visc%Kv_shear)) deallocate(visc%Kv_shear)
34671 if (associated(visc%Kv_shear_Bu)) deallocate(visc%Kv_shear_Bu)
34681 if (allocated(visc%ustar_bbl)) deallocate(visc%ustar_bbl)
34691 if (allocated(visc%BBL_meanKE_loss)) deallocate(visc%BBL_meanKE_loss)
34701 if (allocated(visc%BBL_meanKE_loss_sqrtCd)) deallocate(visc%BBL_meanKE_loss_sqrtCd)
34711 if (allocated(visc%taux_shelf)) deallocate(visc%taux_shelf)
34721 if (allocated(visc%tauy_shelf)) deallocate(visc%tauy_shelf)
34731 if (allocated(visc%tbl_thick_shelf_u)) deallocate(visc%tbl_thick_shelf_u)
34741 if (allocated(visc%tbl_thick_shelf_v)) deallocate(visc%tbl_thick_shelf_v)
34751 if (allocated(visc%kv_tbl_shelf_u)) deallocate(visc%kv_tbl_shelf_u)
34761 if (allocated(visc%kv_tbl_shelf_v)) deallocate(visc%kv_tbl_shelf_v)
34771end subroutine set_visc_end
3478
3479!> \namespace mom_set_visc
3480!!
3481!! This would also be the module in which other viscous quantities that are flow-independent might be set.
3482!! This information is transmitted to other modules via a vertvisc type structure.
3483!!
3484!! The same code is used for the two velocity components, by indirectly referencing the velocities and
3485!! defining a handful of direction-specific defined variables.
3486
34870end module MOM_set_visc