← back to index

src/parameterizations/vertical/MOM_vert_friction.F90

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

1! This file is part of MOM6, the Modular Ocean Model version 6.
2! See the LICENSE file for licensing information.
3! SPDX-License-Identifier: Apache-2.0
4
5#include "do_concurrent_compat.h"
6
7!> Implements vertical viscosity (vertvisc)
8module MOM_vert_friction
9
10use MOM_domains, only : pass_var, To_All, Omit_corners
11use MOM_domains, only : pass_vector, Scalar_Pair
12use MOM_diag_mediator, only : post_data, register_diag_field, safe_alloc_ptr
13use MOM_diag_mediator, only : post_product_u, post_product_sum_u
14use MOM_diag_mediator, only : post_product_v, post_product_sum_v
15use MOM_diag_mediator, only : diag_ctrl, query_averaging_enabled
16use MOM_domains, only : create_group_pass, do_group_pass, group_pass_type
17use MOM_domains, only : To_North, To_East
18use MOM_debugging, only : uvchksum, hchksum
19use MOM_error_handler, only : MOM_error, FATAL, WARNING, NOTE
20use MOM_file_parser, only : get_param, log_param, log_version, param_file_type
21use MOM_forcing_type, only : mech_forcing, find_ustar
22use MOM_get_input, only : directories
23use MOM_grid, only : ocean_grid_type
24use MOM_io, only : MOM_read_data, slasher
25use MOM_open_boundary, only : ocean_OBC_type, OBC_NONE, OBC_DIRECTION_E
26use MOM_open_boundary, only : OBC_DIRECTION_W, OBC_DIRECTION_N, OBC_DIRECTION_S
27use MOM_PointAccel, only : write_u_accel, write_v_accel, PointAccel_init
28use MOM_PointAccel, only : PointAccel_CS
29use MOM_time_manager, only : time_type, time_minus_signed
30use MOM_unit_scaling, only : unit_scale_type
31use MOM_variables, only : thermo_var_ptrs, vertvisc_type
32use MOM_variables, only : cont_diag_ptrs, accel_diag_ptrs
33use MOM_variables, only : ocean_internal_state
34use MOM_verticalGrid, only : verticalGrid_type
35use MOM_wave_interface, only : wave_parameters_CS
36use MOM_set_visc, only : set_v_at_u, set_u_at_v
37use MOM_lateral_mixing_coeffs, only : VarMix_CS
38
39use CVMix_kpp, only : cvmix_kpp_composite_Gshape
40
41implicit none ; private
42
43#include <MOM_memory.h>
44
45public vertvisc, vertvisc_remnant, vertvisc_coef
46public vertvisc_limit_vel, vertvisc_init, vertvisc_end
47public updateCFLtruncationValue
48public vertFPmix
49
50! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional
51! consistency testing. These are noted in comments with units like Z, H, L, and T, along with
52! their mks counterparts with notation like "a velocity [Z T-1 ~> m s-1]". If the units
53! vary with the Boussinesq approximation, the Boussinesq variant is given first.
54
55!> The control structure with parameters and memory for the MOM_vert_friction module
56type, public :: vertvisc_CS ; private
57 logical :: initialized = .false. !< True if this control structure has been initialized.
58 real :: Hmix !< The mixed layer thickness [Z ~> m].
59 real :: Hmix_stress !< The mixed layer thickness over which the wind
60 !! stress is applied with direct_stress [H ~> m or kg m-2].
61 real :: Kvml_invZ2 !< The extra vertical viscosity scale in [H Z T-1 ~> m2 s-1 or Pa s] in a
62 !! surface mixed layer with a characteristic thickness given by Hmix,
63 !! and scaling proportional to (Hmix/z)^2, where z is the distance
64 !! from the surface; this can get very large with thin layers.
65 real :: Kv !< The interior vertical viscosity [H Z T-1 ~> m2 s-1 or Pa s].
66 real :: Hbbl !< The static bottom boundary layer thickness [Z ~> m].
67 real :: Hbbl_gl90 !< The static bottom boundary layer thickness used for GL90 [Z ~> m].
68 real :: Kv_extra_bbl !< An extra vertical viscosity in the bottom boundary layer of thickness
69 !! Hbbl when there is not a bottom drag law in use [H Z T-1 ~> m2 s-1 or Pa s].
70 real :: vonKar !< The von Karman constant as used for mixed layer viscosity [nondim]
71
72 logical :: use_GL90_in_SSW !< If true, use the GL90 parameterization in stacked shallow water mode (SSW).
73 !! The calculation of the GL90 viscosity coefficient uses the fact that in SSW
74 !! we simply have 1/N^2 = h/g^prime, where g^prime is the reduced gravity.
75 !! This identity does not generalize to non-SSW setups.
76 logical :: use_GL90_N2 !< If true, use GL90 vertical viscosity coefficient that is depth-independent;
77 !! this corresponds to a kappa_GM that scales as N^2 with depth.
78 real :: kappa_gl90 !< The scalar diffusivity used in the GL90 vertical viscosity scheme
79 !! [L2 H Z-1 T-1 ~> m2 s-1 or Pa s]
80 logical :: read_kappa_gl90 !< If true, read a file containing the spatially varying kappa_gl90
81 real :: alpha_gl90 !< Coefficient used to compute a depth-independent GL90 vertical
82 !! viscosity via Kv_gl90 = alpha_gl90 * f^2. Note that the implied
83 !! Kv_gl90 corresponds to a kappa_gl90 that scales as N^2 with depth.
84 !! [H Z T ~> m2 s or kg s m-1]
85 real :: vel_underflow !< Velocity components smaller than vel_underflow
86 !! are set to 0 [L T-1 ~> m s-1].
87 real :: CFL_trunc !< Velocity components will be truncated when they
88 !! are large enough that the corresponding CFL number
89 !! exceeds this value [nondim].
90 real :: CFL_report !< The value of the CFL number that will cause the
91 !! accelerations to be reported [nondim]. CFL_report
92 !! will often equal CFL_trunc.
93 real :: truncRampTime !< The time-scale over which to ramp up the value of
94 !! CFL_trunc from CFL_truncS to CFL_truncE [T ~> s]
95 real :: CFL_truncS !< The start value of CFL_trunc [nondim]
96 real :: CFL_truncE !< The end/target value of CFL_trunc [nondim]
97 logical :: CFLrampingIsActivated = .false. !< True if the ramping has been initialized
98 type(time_type) :: rampStartTime !< The time at which the ramping of CFL_trunc starts
99
100 real ALLOCABLE_, dimension(NIMEMB_PTR_,NJMEM_,NK_INTERFACE_) :: &
101 a_u !< The u-drag coefficient across an interface [H T-1 ~> m s-1 or Pa s m-1]
102 real ALLOCABLE_, dimension(NIMEMB_PTR_,NJMEM_,NK_INTERFACE_) :: &
103 a_u_gl90 !< The u-drag coefficient associated with GL90 across an interface [H T-1 ~> m s-1 or Pa s m-1]
104 real ALLOCABLE_, dimension(NIMEMB_PTR_,NJMEM_,NKMEM_) :: &
105 h_u !< The effective layer thickness at u-points [H ~> m or kg m-2].
106 real ALLOCABLE_, dimension(NIMEM_,NJMEMB_PTR_,NK_INTERFACE_) :: &
107 a_v !< The v-drag coefficient across an interface [H T-1 ~> m s-1 or Pa s m-1]
108 real ALLOCABLE_, dimension(NIMEM_,NJMEMB_PTR_,NK_INTERFACE_) :: &
109 a_v_gl90 !< The v-drag coefficient associated with GL90 across an interface [H T-1 ~> m s-1 or Pa s m-1]
110 real ALLOCABLE_, dimension(NIMEM_,NJMEMB_PTR_,NKMEM_) :: &
111 h_v !< The effective layer thickness at v-points [H ~> m or kg m-2].
112 real, pointer, dimension(:,:) :: a1_shelf_u => NULL() !< The u-momentum coupling coefficient under
113 !! ice shelves [H T-1 ~> m s-1 or Pa s m-1]. Retained to determine stress under shelves.
114 real, pointer, dimension(:,:) :: a1_shelf_v => NULL() !< The v-momentum coupling coefficient under
115 !! ice shelves [H T-1 ~> m s-1 or Pa s m-1]. Retained to determine stress under shelves.
116
117 logical :: split !< If true, use the split time stepping scheme.
118 logical :: bottomdraglaw !< If true, the bottom stress is calculated with a
119 !! drag law c_drag*|u|*u. The velocity magnitude
120 !! may be an assumed value or it may be based on the
121 !! actual velocity in the bottommost HBBL, depending
122 !! on whether linear_drag is true.
123 logical :: harmonic_visc !< If true, the harmonic mean thicknesses are used
124 !! to calculate the viscous coupling between layers
125 !! except near the bottom. Otherwise the arithmetic
126 !! mean thickness is used except near the bottom.
127 real :: harm_BL_val !< A scale to determine when water is in the boundary
128 !! layers based solely on harmonic mean thicknesses
129 !! for the purpose of determining the extent to which
130 !! the thicknesses used in the viscosities are upwinded [nondim].
131 logical :: direct_stress !< If true, the wind stress is distributed over the topmost Hmix_stress
132 !! of fluid, and an added mixed layer viscosity or a physically based
133 !! boundary layer turbulence parameterization is not needed for stability.
134 logical :: dynamic_viscous_ML !< If true, use the results from a dynamic
135 !! calculation, perhaps based on a bulk Richardson
136 !! number criterion, to determine the mixed layer
137 !! thickness for viscosity.
138 logical :: fixed_LOTW_ML !< If true, use a Law-of-the-wall prescription for the mixed layer
139 !! viscosity within a boundary layer that is the lesser of Hmix and the
140 !! total depth of the ocean in a column.
141 logical :: apply_LOTW_floor !< If true, use a Law-of-the-wall prescription to set a lower bound
142 !! on the viscous coupling between layers within the surface boundary
143 !! layer, based the distance of interfaces from the surface. This only
144 !! acts when there are large changes in the thicknesses of successive
145 !! layers or when the viscosity is set externally and the wind stress
146 !! has subsequently increased.
147 integer :: answer_date !< The vintage of the order of arithmetic and expressions in the viscous
148 !! calculations. Values below 20190101 recover the answers from the end
149 !! of 2018, while higher values use expressions that do not use an
150 !! arbitrary and hard-coded maximum viscous coupling coefficient between
151 !! layers. In non-Boussinesq cases, values below 20230601 recover a
152 !! form of the viscosity within the mixed layer that breaks up the
153 !! magnitude of the wind stress with BULKMIXEDLAYER, DYNAMIC_VISCOUS_ML
154 !! or FIXED_DEPTH_LOTW_ML, but not LOTW_VISCOUS_ML_FLOOR.
155 logical :: debug !< If true, write verbose checksums for debugging purposes.
156 integer :: nkml !< The number of layers in the mixed layer.
157 integer, pointer :: ntrunc !< The number of times the velocity has been
158 !! truncated since the last call to write_energy.
159 character(len=200) :: u_trunc_file !< The complete path to a file in which a column of
160 !! u-accelerations are written if velocity truncations occur.
161 character(len=200) :: v_trunc_file !< The complete path to a file in which a column of
162 !! v-accelerations are written if velocity truncations occur.
163 logical :: StokesMixing !< If true, do Stokes drift mixing via the Lagrangian current
164 !! (Eulerian plus Stokes drift). False by default and set
165 !! via STOKES_MIXING_COMBINED.
166
167 type(diag_ctrl), pointer :: diag !< A structure that is used to regulate the
168 !! timing of diagnostic output.
169 real, allocatable, dimension(:,:) :: kappa_gl90_2d !< 2D kappa_gl90 at h-points [L2 H Z-1 T-1 ~> m2 s-1 or Pa s]
170
171 !>@{ Diagnostic identifiers
172 integer :: id_du_dt_visc = -1, id_dv_dt_visc = -1, id_du_dt_visc_gl90 = -1, id_dv_dt_visc_gl90 = -1
173 integer :: id_GLwork = -1
174 integer :: id_au_vv = -1, id_av_vv = -1, id_au_gl90_vv = -1, id_av_gl90_vv = -1
175 integer :: id_du_dt_str = -1, id_dv_dt_str = -1
176 integer :: id_h_u = -1, id_h_v = -1, id_hML_u = -1 , id_hML_v = -1
177 integer :: id_Omega_w2x = -1, id_FPtau2s = -1 , id_FPtau2w = -1
178 integer :: id_uE_h = -1, id_vE_h = -1
179 integer :: id_uStk = -1, id_vStk = -1
180 integer :: id_uStk0 = -1, id_vStk0 = -1
181 integer :: id_uInc_h= -1, id_vInc_h= -1
182 integer :: id_taux_bot = -1, id_tauy_bot = -1
183 integer :: id_Kv_slow = -1, id_Kv_u = -1, id_Kv_v = -1
184 integer :: id_Kv_gl90_u = -1, id_Kv_gl90_v = -1
185 ! integer :: id_hf_du_dt_visc = -1, id_hf_dv_dt_visc = -1
186 integer :: id_h_du_dt_visc = -1, id_h_dv_dt_visc = -1
187 integer :: id_hf_du_dt_visc_2d = -1, id_hf_dv_dt_visc_2d = -1
188 integer :: id_h_du_dt_str = -1, id_h_dv_dt_str = -1
189 integer :: id_du_dt_str_visc_rem = -1, id_dv_dt_str_visc_rem = -1
190 !>@}
191
192 type(PointAccel_CS), pointer :: PointAccel_CSp => NULL() !< A pointer to the control structure
193 !! for recording accelerations leading to velocity truncations
194
195 type(group_pass_type) :: pass_KE_uv !< A handle used for group halo passes
196end type vertvisc_CS
197
198contains
199
200!> Add nonlocal stress increments to ui^n and vi^n.
2010subroutine vertFPmix(ui, vi, uold, vold, hbl_h, h, forces, dt, lpost, Cemp_NL, G, GV, US, CS, OBC, Waves)
202 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
203 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure
204 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), &
205 intent(inout) :: ui !< Zonal velocity after vertvisc [L T-1 ~> m s-1]
206 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), &
207 intent(inout) :: vi !< Meridional velocity after vertvisc [L T-1 ~> m s-1]
208 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), &
209 intent(inout) :: uold !< Old Zonal velocity [L T-1 ~> m s-1]
210 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), &
211 intent(inout) :: vold !< Old Meridional velocity [L T-1 ~> m s-1]
212 real, dimension(SZI_(G),SZJ_(G)), intent(inout) :: hbl_h !< boundary layer depth [H ~> m]
213 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
214 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
215 type(mech_forcing), intent(in) :: forces !< A structure with the driving mechanical forces
216 real, intent(in) :: dt !< Time increment [T ~> s]
217 real, intent(in) :: Cemp_NL !< empirical coefficient of non-local momentum mixing [nondim]
218 logical, intent(in) :: lpost !< Compute and make available FPMix diagnostics
219 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
220 type(vertvisc_CS), pointer :: CS !< Vertical viscosity control structure
221 type(ocean_OBC_type), pointer :: OBC !< Open boundary condition structure
222 type(wave_parameters_CS), &
223 optional, pointer :: Waves !< Container for wave/Stokes information
224
225 ! local variables
2260 real, dimension(SZIB_(G),SZJ_(G)) :: hbl_u !< boundary layer depth (u-pts) [H ~> m]
2270 real, dimension(SZI_(G),SZJB_(G)) :: hbl_v !< boundary layer depth (v-pts) [H ~> m]
2280 real, dimension(SZIB_(G),SZJ_(G)) :: taux_u !< kinematic zonal wind stress (u-pts) [L2 T-2 ~> m2 s-2]
2290 real, dimension(SZI_(G),SZJB_(G)) :: tauy_v !< kinematic merid wind stress (v-pts) [L2 T-2 ~> m2 s-2]
2300 real, dimension(SZI_(G),SZJ_(G)) :: uS0 !< surface zonal Stokes drift h-pts [L T-1 ~> m s-1]
2310 real, dimension(SZI_(G),SZJ_(G)) :: vS0 !< surface zonal Stokes drift h-pts [L T-1 ~> m s-1]
2320 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: uE_u !< zonal Eulerian u-pts [L T-1 ~> m s-1]
2330 real, dimension(SZI_(G) ,SZJ_(G),SZK_(GV)) :: uE_h !< zonal Eulerian h-pts [L T-1 ~> m s-1]
2340 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: vE_v !< merid Eulerian v-pts [L T-1 ~> m s-1]
2350 real, dimension(SZI_(G) ,SZJ_(G),SZK_(GV)) :: vE_h !< merid Eulerian h-pts [L T-1 ~> m s-1]
2360 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: uInc_u !< zonal Eulerian u-pts [L T-1 ~> m s-1]
2370 real, dimension(SZI_(G) ,SZJ_(G),SZK_(GV)) :: uInc_h !< zonal Eulerian h-pts [L T-1 ~> m s-1]
2380 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: vInc_v !< merid Eulerian v-pts [L T-1 ~> m s-1]
2390 real, dimension(SZI_(G) ,SZJ_(G),SZK_(GV)) :: vInc_h !< merid Eulerian h-pts [L T-1 ~> m s-1]
2400 real, dimension(SZI_(G) ,SZJ_(G),SZK_(GV)) :: uStk !< zonal Stokes Drift (h-pts) [L T-1 ~> m s-1]
2410 real, dimension(SZI_(G) ,SZJ_(G),SZK_(GV)) :: vStk !< merid Stokes Drift (h-pts) [L T-1 ~> m s-1]
2420 real, dimension(SZI_(G) ,SZJ_(G),SZK_(GV)+1) :: omega_tau2s !< angle stress to shear (h-pts) [rad]
2430 real, dimension(SZI_(G) ,SZJ_(G),SZK_(GV)+1) :: omega_tau2w !< angle stress to wind (h-pts) [rad]
244 real :: omega_tmp, omega_s2x, omega_tau2x !< temporary angle wrt the x axis [rad]
245 real :: Irho0 !< Inverse of the mean density rescaled to [Z L-1 R-1 ~> m3 kg-1]
246 real :: pi !< ! The ratio of the circumference of a circle to its diameter [nondim]
247 real :: tmp_u, tmp_v !< temporary ocean mask weights on u and v points [nondim]
248 real :: fexp !< temporary exponential function [nondim]
249 real :: sigma !< temporary normalize boundary layer coordinate [nondim]
250 real :: Gat1, Gsig, dGdsig !< Shape parameters [nondim]
251 real :: du, dv !< Intermediate velocity differences [L T-1 ~> m s-1]
252 real :: depth !< Cumulative of thicknesses [H ~> m]
253 integer :: b, kp1, k, nz !< band and vertical indices
254 integer :: i, j, is, ie, js, je, Isq, Ieq, Jsq, Jeq !< horizontal indices
255
2560 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
2570 Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB ; nz = GV%ke
258
2590 pi = 4. * atan2(1.,1.)
2600 Irho0 = 1.0 / GV%Rho0
261
2620 call pass_var(hbl_h , G%Domain, halo=1)
263
264 ! u-points
2650 do j = js,je
2660 do I = Isq,Ieq
2670 taux_u(I,j) = forces%taux(I,j) * Irho0
2680 if ( (G%mask2dCu(I,j) > 0.5) ) then
269 ! h to u-pts
2700 tmp_u = MAX (1.0 ,(G%mask2dT(i,j) + G%mask2dT(i+1,j) ) )
2710 hbl_u(I,j) = ((G%mask2dT(i,j) * hbl_h(i,j)) + (G%mask2dT(i+1,j) * hbl_h(i+1,j))) / tmp_u
2720 depth = 0.
2730 Gat1 = 0.
2740 do k=1, nz
275 ! cell center
2760 depth = depth + 0.5*CS%h_u(I,j,k)
2770 uE_u(I,j,k) = ui(I,j,k) - waves%Us_x(I,j,k)
2780 if ( depth < hbl_u(I,j) ) then
2790 sigma = depth / hbl_u(i,j)
280 ! cell bottom
2810 depth = depth + 0.5*CS%h_u(I,j,k)
2820 call cvmix_kpp_composite_Gshape(sigma,Gat1,Gsig,dGdsig)
283 ! nonlocal boundary-layer increment
2840 uInc_u(I,j,k) = dt * Cemp_NL * taux_u(I,j) * dGdsig / hbl_u(I,j)
2850 ui(I,j,k) = ui(I,j,k) + uInc_u(I,j,k)
286 else
2870 uInc_u(I,j,k) = 0.0
288 endif
289 enddo
290 else
2910 do k=1, nz
2920 uInc_u(I,j,k) = 0.0
293 enddo
294 endif
295 enddo
296 enddo
297
298 ! v-points
2990 do J = Jsq,Jeq
3000 do i = is,ie
3010 tauy_v(i,J) = forces%tauy(i,J) * Irho0
3020 if ( (G%mask2dCv(i,J) > 0.5) ) then
303 ! h to v-pts
3040 tmp_v = max( 1.0 ,(G%mask2dT(i,j) + G%mask2dT(i,j+1)))
3050 hbl_v(i,J) = (G%mask2dT(i,j) * hbl_h(i,J) + G%mask2dT(i,j+1) * hbl_h(i,j+1)) / tmp_v
3060 depth = 0.
3070 Gat1 = 0.
3080 do k=1, nz
309 ! cell center
3100 depth = depth + 0.5* CS%h_v(i,J,k)
3110 vE_v(i,J,k) = vi(i,J,k) - waves%Us_y(i,J,k)
3120 if ( depth < hbl_v(i,J) ) then
3130 sigma = depth / hbl_v(i,J)
314 ! cell bottom
3150 depth = depth + 0.5* CS%h_v(i,J,k)
3160 call cvmix_kpp_composite_Gshape(sigma,Gat1,Gsig,dGdsig)
317 ! nonlocal boundary-layer increment
3180 vInc_v(i,J,k) = dt * Cemp_NL * tauy_v(i,J) * dGdsig / hbl_v(i,J)
3190 vi(i,J,k) = vi(i,J,k) + vInc_v(i,J,k)
320 else
3210 vInc_v(i,J,k) = 0.0
322 endif
323 enddo
324 else
3250 do k=1, nz
3260 vInc_v(i,J,k) = 0.0
327 enddo
328 endif
329 enddo
330 enddo
331
332 ! Compute and store diagnostics, only during the corrector step.
3330 if (lpost) then
3340 call pass_vector(uE_u , vE_v , G%Domain, To_All)
3350 call pass_vector(uInc_u, vInc_v , G%Domain, To_All)
3360 uStk = 0.0
3370 vStk = 0.0
3380 uS0 = 0.0
3390 vS0 = 0.0
340
3410 do j = js,je
3420 do i = is,ie
3430 if (G%mask2dT(i,j) > 0.5) then
344 ! u to h-pts
3450 tmp_u = max( 1.0 ,(G%mask2dCu(i,j) + G%mask2dCu(i-1,j)))
346 ! v to h-pts
3470 tmp_v = max( 1.0 ,(G%mask2dCv(i,j) + G%mask2dCv(i,j-1)))
3480 do k = 1,nz
3490 uE_h(i,j,k) = (G%mask2dCu(i,j) * uE_u(i,j,k) + G%mask2dCu(i-1,j) * uE_u(i-1,j,k)) / tmp_u
3500 uInc_h(i,j,k) = (G%mask2dCu(i,j) * uInc_u(i,j,k) + G%mask2dCu(i-1,j) * uInc_u(i-1,j,k)) / tmp_u
3510 vE_h(i,j,k) = (G%mask2dCv(i,j) * vE_v(i,j,k) + G%mask2dCv(i,j-1) * vE_v(i,j-1,k)) / tmp_v
3520 vInc_h(i,j,k) = (G%mask2dCv(i,j) * vInc_v(i,j,k) + G%mask2dCv(i,j-1) * vInc_v(i,j-1,k)) / tmp_v
353 enddo
354 ! Wind, Stress and Shear align at surface
3550 Omega_tau2w(i,j,1) = 0.0
3560 Omega_tau2s(i,j,1) = 0.0
3570 do k = 1,nz
3580 kp1 = min( nz , k+1)
3590 du = uE_h(i,j,k) - uE_h(i,j,kp1)
3600 dv = vE_h(i,j,k) - vE_h(i,j,kp1)
3610 omega_s2x = atan2( dv , du )
362
3630 du = du + uInc_h(i,j,k) - uInc_h(i,j,kp1)
3640 dv = dv + vInc_h(i,j,k) - vInc_h(i,j,kp1)
3650 omega_tau2x = atan2( dv , du )
366
3670 omega_tmp = omega_tau2x - forces%omega_w2x(i,j)
3680 if ( (omega_tmp > pi ) ) omega_tmp = omega_tmp - 2.*pi
3690 if ( (omega_tmp < (0.-pi)) ) omega_tmp = omega_tmp + 2.*pi
3700 Omega_tau2w(i,j,kp1) = omega_tmp
371
3720 omega_tmp = omega_tau2x - omega_s2x
3730 if ( (omega_tmp > pi ) ) omega_tmp = omega_tmp - 2.*pi
3740 if ( (omega_tmp < (0.-pi)) ) omega_tmp = omega_tmp + 2.*pi
3750 Omega_tau2s(i,j,kp1) = omega_tmp
376
377 enddo
378 endif
379
380 ! Stokes drift
3810 do b=1,waves%NumBands
3820 uS0(i,j) = uS0(i,j) + waves%UStk_Hb(i,j,b) ! or forces%UStkb(i,j,b)
3830 vS0(i,j) = vS0(i,j) + waves%VStk_Hb(i,j,b) ! or forces%VStkb(i,j,b)
384 enddo
3850 depth = 0.0
3860 do k = 1,nz
3870 do b = 1, waves%NumBands
388 ! cell center
3890 fexp = exp(-2. * waves%WaveNum_Cen(b) * (depth+0.5*h(i,j,k)) )
3900 uStk(i,j,k) = uStk(i,j,k) + waves%UStk_Hb(i,j,b) * fexp
3910 vStk(i,j,k) = vStk(i,j,k) + waves%VStk_Hb(i,j,b) * fexp
392 enddo
393 ! cell bottom
3940 depth = depth + h(i,j,k)
395 enddo
396 enddo
397 enddo
398
399 ! post FPmix diagnostics
4000 if (CS%id_uE_h > 0) call post_data(CS%id_uE_h , uE_h , CS%diag)
4010 if (CS%id_vE_h > 0) call post_data(CS%id_vE_h , vE_h , CS%diag)
4020 if (CS%id_uInc_h > 0) call post_data(CS%id_uInc_h , uInc_h , CS%diag)
4030 if (CS%id_vInc_h > 0) call post_data(CS%id_vInc_h , vInc_h , CS%diag)
4040 if (CS%id_FPtau2s > 0) call post_data(CS%id_FPtau2s, Omega_tau2s, CS%diag)
4050 if (CS%id_FPtau2w > 0) call post_data(CS%id_FPtau2w, Omega_tau2w, CS%diag)
4060 if (CS%id_uStk0 > 0) call post_data(CS%id_uStk0 , uS0 , CS%diag)
4070 if (CS%id_vStk0 > 0) call post_data(CS%id_vStk0 , vS0 , CS%diag)
4080 if (CS%id_uStk > 0) call post_data(CS%id_uStk , uStk , CS%diag)
4090 if (CS%id_vStk > 0) call post_data(CS%id_vStk , vStk , CS%diag)
4100 if (CS%id_Omega_w2x > 0) call post_data(CS%id_Omega_w2x, forces%omega_w2x, CS%diag)
411
412 endif
413
4140end subroutine vertFPmix
415
416
417!> Compute coupling coefficient associated with vertical viscosity parameterization as in Greatbatch and Lamb
418!! (1990), hereafter referred to as the GL90 vertical viscosity parameterization. This vertical viscosity scheme
419!! redistributes momentum in the vertical, and is the equivalent of the Gent & McWilliams (1990) parameterization,
420!! but in a TWA (thickness-weighted averaged) set of equations. The vertical viscosity coefficient nu is computed
421!! from kappa_GM via thermal wind balance, and the following relation:
422!! nu = kappa_GM * f^2 / N^2.
423!! In the following subroutine kappa_GM is assumed either (a) constant or (b) horizontally varying. In both cases,
424!! (a) and (b), one can additionally impose an EBT structure in the vertical for kappa_GM.
425!! A third possible formulation of nu is depth-independent:
426!! nu = f^2 * alpha
427!! The latter formulation would be equivalent to a kappa_GM that varies as N^2 with depth.
428!! The vertical viscosity del_z ( nu del_z u) is applied to the momentum equation with stress-free boundary
429!! conditions at the top and bottom.
430!!
431!! In SSW mode, we have 1/N^2 = h/g'. The coupling coefficient is therefore equal to
432!! a_cpl_gl90 = nu / h = kappa_GM * f^2 / g'
433!! or
434!! a_cpl_gl90 = nu / h = f^2 * alpha / h
435
4360subroutine find_coupling_coef_gl90(a_cpl_gl90, hvel, i, j, z_i, G, GV, CS, VarMix, work_on_u)
437 !$omp declare target
438 type(ocean_grid_type), intent(in) :: G !< Grid structure.
439 type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure.
440 real, dimension(SZK_(GV)), intent(in) :: hvel !< Distance between interfaces
441 !! at velocity points [Z ~> m]
442 integer, intent(in) :: i !< Column i-index
443 integer, intent(in) :: j !< Column j-index
444 real, dimension(SZK_(GV)+1), intent(in) :: z_i !< Estimate of interface heights above the
445 !! bottom, normalized by the GL90 bottom
446 !! boundary layer thickness [nondim]
447 real, dimension(SZK_(GV)+1),intent(out) :: a_cpl_gl90 !< Coupling coefficient associated
448 !! with GL90 across interfaces; is not
449 !! included in a_cpl [H T-1 ~> m s-1 or Pa s m-1].
450 type(vertvisc_cs), intent(in) :: CS !< Vertical viscosity control structure
451 type(VarMix_CS), intent(in) :: VarMix !< Variable mixing coefficients
452 logical, intent(in) :: work_on_u !< If true, u-points are being calculated,
453 !! otherwise they are v-points.
454
455 ! local variables
456 logical :: kdgl90_use_vert_struct ! use vertical structure for GL90 coefficient
457 integer :: k, nz
458 real :: f2 !< Squared Coriolis parameter at a velocity grid point [T-2 ~> s-2].
459 real :: h_neglect ! A vertical distance that is so small it is usually lost in roundoff error
460 ! and can be neglected [Z ~> m].
461 real :: botfn ! A function that is 1 at the bottom and small far from it [nondim]
462 real :: z2 ! The distance from the bottom, normalized by Hbbl_gl90 [nondim]
463
4640 nz = GV%ke
4650 h_neglect = GV%dZ_subroundoff
4660 kdgl90_use_vert_struct = .false.
467
4680 if (VarMix%use_variable_mixing) then
4690 kdgl90_use_vert_struct = allocated(VarMix%kdgl90_struct)
470 endif
471
4720 a_cpl_gl90(:) = 0.
473
4740 do K=2,nz
4750 if (work_on_u) then
476 ! compute coupling coefficient at u-points
4770 f2 = 0.25 * (G%CoriolisBu(I,J-1) + G%CoriolisBu(I,J))**2
4780 if (CS%use_GL90_N2) then
4790 a_cpl_gl90(K) = 2. * f2 * CS%alpha_gl90 / (hvel(k) + hvel(k-1) + h_neglect)
480 else
4810 if (CS%read_kappa_gl90) then
4820 a_cpl_gl90(K) = f2 * 0.5 * (CS%kappa_gl90_2d(i,j) + CS%kappa_gl90_2d(i+1,j)) / GV%g_prime(K)
483 else
4840 a_cpl_gl90(K) = f2 * CS%kappa_gl90 / GV%g_prime(K)
485 endif
4860 if (kdgl90_use_vert_struct) then
487 a_cpl_gl90(K) = a_cpl_gl90(K) * 0.5 &
4880 * (VarMix%kdgl90_struct(i,j,k-1) + VarMix%kdgl90_struct(i+1,j,k-1))
489 endif
490 endif
491 ! botfn determines when a point is within the influence of the GL90 bottom boundary layer,
492 ! going from 1 at the bottom to 0 in the interior.
4930 z2 = z_i(k)
4940 botfn = 1. / (1. + 0.09 * z2 * z2 * z2 * z2 * z2 * z2)
495
4960 a_cpl_gl90(K) = a_cpl_gl90(K) * (1. - botfn)
497 else
498 ! compute viscosities at v-points
4990 f2 = 0.25 * (G%CoriolisBu(I-1,J) + G%CoriolisBu(I,J))**2
500
5010 if (CS%use_GL90_N2) then
5020 a_cpl_gl90(K) = 2. * f2 * CS%alpha_gl90 / (hvel(k) + hvel(k-1) + h_neglect)
503 else
5040 if (CS%read_kappa_gl90) then
5050 a_cpl_gl90(K) = f2 * 0.5 * (CS%kappa_gl90_2d(i,j) + CS%kappa_gl90_2d(i,j+1)) / GV%g_prime(K)
506 else
5070 a_cpl_gl90(K) = f2 * CS%kappa_gl90 / GV%g_prime(K)
508 endif
5090 if (kdgl90_use_vert_struct) then
510 a_cpl_gl90(K) = a_cpl_gl90(K) * 0.5 &
5110 * (VarMix%kdgl90_struct(i,j,k-1) + VarMix%kdgl90_struct(i,j+1,k-1))
512 endif
513 endif
514 ! botfn determines when a point is within the influence of the GL90 bottom boundary layer,
515 ! going from 1 at the bottom to 0 in the interior.
5160 z2 = z_i(k)
5170 botfn = 1. / (1. + 0.09 * z2 * z2 * z2 * z2 * z2 * z2)
518
5190 a_cpl_gl90(K) = a_cpl_gl90(K) * (1. - botfn)
520 endif
521 enddo
5220end subroutine find_coupling_coef_gl90
523
524
525!> Perform a fully implicit vertical diffusion
526!! of momentum. Stress top and bottom boundary conditions are used.
527!!
528!! This is solving the tridiagonal system
529!! \f[ \left(h_k + a_{k + 1/2} + a_{k - 1/2} + r_k\right) u_k^{n+1}
530!! = h_k u_k^n + a_{k + 1/2} u_{k+1}^{n+1} + a_{k - 1/2} u_{k-1}^{n+1} \f]
531!! where \f$a_{k + 1/2} = \Delta t \nu_{k + 1/2} / h_{k + 1/2}\f$
532!! is the <em>interfacial coupling thickness per time step</em>,
533!! encompassing background viscosity as well as contributions from
534!! enhanced mixed and bottom layer viscosities.
535!! $r_k$ is a Rayleigh drag term due to channel drag.
536!! There is an additional stress term on the right-hand side
537!! if DIRECT_STRESS is true, applied to the surface layer.
53896subroutine vertvisc(u, v, h, forces, visc, dt, OBC, ADp, CDp, G, GV, US, CS, &
53996 taux_bot, tauy_bot, fpmix, Waves)
540 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
541 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure
542 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
543 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), &
544 intent(inout) :: u !< Zonal velocity [L T-1 ~> m s-1]
545 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), &
546 intent(inout) :: v !< Meridional velocity [L T-1 ~> m s-1]
547 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
548 intent(in) :: h !< Layer thickness [H ~> m or kg m-2]
549 type(mech_forcing), intent(in) :: forces !< A structure with the driving mechanical forces
550 type(vertvisc_type), intent(inout) :: visc !< Viscosities and bottom drag
551 real, intent(in) :: dt !< Time increment [T ~> s]
552 type(ocean_OBC_type), pointer :: OBC !< Open boundary condition structure
553 type(accel_diag_ptrs), intent(inout) :: ADp !< Accelerations in the momentum
554 !! equations for diagnostics
555 type(cont_diag_ptrs), intent(inout) :: CDp !< Continuity equation terms
556 type(vertvisc_CS) :: CS !< Vertical viscosity control structure
557 real, dimension(SZIB_(G),SZJ_(G)), &
558 optional, intent(out) :: taux_bot !< Zonal bottom stress from ocean to
559 !! rock [R L Z T-2 ~> Pa]
560 real, dimension(SZI_(G),SZJB_(G)), &
561 optional, intent(out) :: tauy_bot !< Meridional bottom stress from ocean to
562 !! rock [R L Z T-2 ~> Pa]
563 logical, optional, intent(in) :: fpmix !< fpmix along Eulerian shear
564 type(wave_parameters_CS), &
565 optional, pointer :: Waves !< Container for wave/Stokes information
566
567 ! Fields from forces used in this subroutine:
568 ! taux: Zonal wind stress [R L Z T-2 ~> Pa].
569 ! tauy: Meridional wind stress [R L Z T-2 ~> Pa].
570
571 ! Local variables
572
573 real :: b1
574 ! A variable used by the tridiagonal solver [H-1 ~> m-1 or m2 kg-1].
57596 real :: c1(SZK_(GV))
576 ! A variable used by the tridiagonal solver [nondim].
577 real :: d1
578 ! d1=1-c1 is used by the tridiagonal solver [nondim].
579 real :: Ray
580 ! Ray is the Rayleigh-drag velocity [H T-1 ~> m s-1 or Pa s m-1]
581 real :: b_denom_1
582 ! The first term in the denominator of b1 [H ~> m or kg m-2].
583
584 real :: Hmix ! The mixed layer thickness over which stress
585 ! is applied with direct_stress [H ~> m or kg m-2].
586 real :: I_Hmix ! The inverse of Hmix [H-1 ~> m-1 or m2 kg-1].
587 real :: Idt ! The inverse of the time step [T-1 ~> s-1].
588 real :: dt_Rho0 ! The time step divided by the mean density [T H Z-1 R-1 ~> s m3 kg-1 or s].
589 real :: h_neglect ! A thickness that is so small it is usually lost
590 ! in roundoff and can be neglected [H ~> m or kg m-2].
591
592 real :: stress ! The surface stress times the time step, divided
593 ! by the density [H L T-1 ~> m2 s-1 or kg m-1 s-1].
594 real :: accel_underflow ! An acceleration magnitude that is so small that values that are less
595 ! than this are diagnosed as 0 [L T-2 ~> m s-2].
596 real :: zDS, h_a ! Temporary thickness variables used with direct_stress [H ~> m or kg m-2]
597 real :: hfr ! Temporary ratio of thicknesses used with direct_stress [nondim]
59896 real :: surface_stress(SZIB_(G), SZJB_(G))
599 ! The same as stress, unless the wind stress is applied as a body force
600 ! [H L T-1 ~> m2 s-1 or kg m-1 s-1].
60148 real, allocatable, dimension(:,:,:) :: KE_term ! A term in the kinetic energy budget
602 ! [H L2 T-3 ~> m3 s-3 or W m-2]
60348 real, allocatable, dimension(:,:,:) :: KE_u ! The area integral of a KE term in a layer at u-points
604 ! [H L4 T-3 ~> m5 s-3 or kg m2 s-3]
60548 real, allocatable, dimension(:,:,:) :: KE_v ! The area integral of a KE term in a layer at v-points
606 ! [H L4 T-3 ~> m5 s-3 or kg m2 s-3]
607
608 logical :: DoStokesMixing
609 logical :: lfpmix
610
611 integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, n
61248 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
61348 Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB ; nz = GV%ke
614
61548 if (.not.CS%initialized) call MOM_error(FATAL,"MOM_vert_friction(visc): "// &
6160 "Module must be initialized before it is used.")
617
61848 if (CS%id_GLwork > 0) then
6190 allocate(KE_u(G%IsdB:G%IedB,G%jsd:G%jed,GV%ke), source=0.0)
6200 allocate(KE_v(G%isd:G%ied,G%JsdB:G%JedB,GV%ke), source=0.0)
6210 allocate(KE_term(G%isd:G%ied,G%jsd:G%jed,GV%ke), source=0.0)
6220 if (.not.G%symmetric) &
6230 call create_group_pass(CS%pass_KE_uv, KE_u, KE_v, G%Domain, To_North+To_East)
624 endif
625
62648 if (CS%direct_stress) then
6270 Hmix = CS%Hmix_stress
6280 I_Hmix = 1.0 / Hmix
629 endif
63048 dt_Rho0 = dt / GV%H_to_RZ
63148 h_neglect = GV%H_subroundoff
63248 Idt = 1.0 / dt
633
63448 accel_underflow = CS%vel_underflow * Idt
635
636 !Check if Stokes mixing allowed if requested (present and associated)
63748 DoStokesMixing=.false.
63848 if (CS%StokesMixing) then
6390 if (present(Waves)) DoStokesMixing = associated(Waves)
6400 if (.not. DoStokesMixing) &
6410 call MOM_error(FATAL, "Stokes Mixing called without associated Waves Control Structure")
642 endif
64348 lfpmix = .false.
64448 if ( present(fpmix) ) lfpmix = fpmix
645
646 ! Update the zonal velocity component using a modification of a standard
647 ! tridiagonal solver.
648
649 ! WGL: Brandon Reichl says the following is obsolete. u(I,j,k) already
650 ! includes Stokes.
651 ! When mixing down Eulerian current + Stokes drift add before calling solver
65248 if (DoStokesMixing) then
6530 do k=1,nz ; do j=G%jsc,G%jec ; do I=Isq,Ieq ; if (G%mask2dCu(I,j) > 0.) then
6540 u(I,j,k) = u(I,j,k) + Waves%Us_x(I,j,k)
655 endif ; enddo ; enddo ; enddo
656 endif
657
65848 if (lfpmix) then
6590 do k=1,nz ; do j=G%jsc,G%jec ; do I=Isq,Ieq ; if (G%mask2dCu(I,j) > 0.) then
6600 u(I,j,k) = u(I,j,k) - Waves%Us_x(I,j,k)
661 endif ; enddo ; enddo ; enddo
662 endif
663
66448 if (associated(ADp%du_dt_visc)) then
6650 do k=1,nz ; do j=G%jsc,G%jec ; do I=Isq,Ieq
6660 ADp%du_dt_visc(I,j,k) = u(I,j,k)
667 enddo ; enddo ; enddo
668 endif
669
670 !$omp target enter data map(to: ADp)
671 !$omp target enter data map(alloc: surface_stress)
672 !$omp target enter data map(alloc: ADp%dv_dt_str)
673 !$omp target enter data map(alloc: ADp%du_dt_str)
674
67548 if (associated(ADp%du_dt_visc_gl90)) then
6760 do concurrent (k=1:nz, j=G%jsc:G%jec, I=Isq:Ieq)
6770 ADp%du_dt_visc_gl90(I,j,k) = u(I,j,k)
678 enddo
679 endif
680
68148 if (associated(ADp%du_dt_str)) then
6820 do concurrent (k=1:nz, j=G%jsc:G%jec, I=Isq:Ieq)
6830 ADp%du_dt_str(I,j,k) = 0.0
684 enddo
685 endif
686
687 ! One option is to have the wind stress applied as a body force
688 ! over the topmost Hmix fluid. If DIRECT_STRESS is not defined,
689 ! the wind stress is applied as a stress boundary condition.
69048 if (CS%direct_stress) then
6910 do concurrent (j=G%jsc:G%jec, I=Isq:Ieq, G%mask2dCu(i,j) > 0.0)
6920 surface_stress(I,j) = 0.0
6930 zDS = 0.0
6940 stress = dt_Rho0 * forces%taux(I,j)
6950 do k=1,nz
6960 h_a = 0.5 * (h(i,j,k) + h(i+1,j,k)) + h_neglect
6970 hfr = 1.0 ; if ((zDS+h_a) > Hmix) hfr = (Hmix - zDS) / h_a
6980 u(I,j,k) = u(I,j,k) + I_Hmix * hfr * stress
6990 if (associated(ADp%du_dt_str)) ADp%du_dt_str(I,j,k) = (I_Hmix * hfr * stress) * Idt
7000 zDS = zDS + h_a ; if (zDS >= Hmix) exit
701 enddo
702 enddo
703 else
7045856 do concurrent (j=G%jsc:G%jec, I=Isq:Ieq)
705354336 surface_stress(I,j) = dt_Rho0 * (G%mask2dCu(I,j)*forces%taux(I,j))
706 enddo
707 endif
708
709 ! perform forward elimination on the tridiagonal system
710 !
711 ! denote the diagonal of the system as b_k, the subdiagonal as a_k
712 ! and the superdiagonal as c_k. The right-hand side terms are d_k.
713 !
714 ! ignoring the Rayleigh drag contribution,
715 ! we have a_k = -dt * a_u(k)
716 ! b_k = h_u(k) + dt * (a_u(k) + a_u(k+1))
717 ! c_k = -dt * a_u(k+1)
718 !
719 ! for forward elimination, we want to:
720 ! calculate c'_k = - c_k / (b_k + a_k c'_(k-1))
721 ! and d'_k = (d_k - a_k d'_(k-1)) / (b_k + a_k c'_(k-1))
722 ! where c'_1 = c_1/b_1 and d'_1 = d_1/b_1
723 !
724 ! This form is mathematically equivalent to Thomas' tridiagonal matrix algorithm, but it
725 ! does not suffer from the acute sensitivity to truncation errors of the Thomas algorithm
726 ! because it involves no subtraction, as discussed by Schopf & Loughe, MWR, 1995.
727 !
728 ! b1 is the denominator term 1 / (b_k + a_k c'_(k-1))
729 ! b_denom_1 is (b_k + a_k + c_k) - a_k(1 - c'_(k-1))
730 ! = (b_k + c_k + c'_(k-1))
731 ! this is done so that d1 = b1 * b_denom_1 = 1 - c'_(k-1)
732 ! c1(k) is -c'_(k - 1)
733 ! and the right-hand-side is destructively updated to be d'_k
734
735 !$omp target enter data map(alloc: c1)
736
737 !$omp target teams loop collapse(2) &
738 !$omp private(b1, c1, d1, Ray, b_denom_1)
739351408 do j=G%jsc,G%jec ; do I=Isq,Ieq ; if (G%mask2dCu(I,j) > 0.) then
740237504 Ray = 0.
741237504 if (allocated(visc%Ray_u)) Ray = visc%Ray_u(I,j,1)
742
743237504 b_denom_1 = CS%h_u(I,j,1) + dt * (Ray + CS%a_u(I,j,1))
744237504 b1 = 1. / (b_denom_1 + dt * CS%a_u(I,j,2))
745237504 d1 = b_denom_1 * b1
746237504 u(I,j,1) = b1 * (CS%h_u(I,j,1) * u(I,j,1) + surface_stress(I,j))
747
748237504 if (associated(ADp%du_dt_str)) then
7490 ADp%du_dt_str(I,j,1) = b1 * (CS%h_u(I,j,1) * ADp%du_dt_str(I,j,1) + surface_stress(I,j) * Idt)
750 endif
751
75217812800 do k=2,nz
75317575296 if (allocated(visc%Ray_u)) Ray = visc%Ray_u(I,j,k)
754
75517575296 c1(k) = dt * CS%a_u(I,j,K) * b1
75617575296 b_denom_1 = CS%h_u(I,j,k) + dt * (Ray + CS%a_u(I,j,K) * d1)
75717575296 b1 = 1. / (b_denom_1 + dt * CS%a_u(I,j,K+1))
75817575296 d1 = b_denom_1 * b1
75917575296 u(I,j,k) = (CS%h_u(I,j,k) * u(I,j,k) + dt * CS%a_u(I,j,K) * u(I,j,k-1)) * b1
760
76117575296 if (associated(ADp%du_dt_str)) then
762 ADp%du_dt_str(I,j,k) = (CS%h_u(I,j,k) * ADp%du_dt_str(I,j,k) &
7630 + dt * CS%a_u(I,j,K) * ADp%du_dt_str(I,j,k-1)) * b1
764 endif
765
766 !### Force FMA evaluation of b1 by blocking lookahead with an impossible branch.
767 ! XXX: Check GPU behavior
76817812800 if (dt < 0) exit
769 enddo
770
771237504 if (associated(ADp%du_dt_str)) then
7720 if (abs(ADp%du_dt_str(I,j,nz)) < accel_underflow) &
7730 ADp%du_dt_str(I,j,nz) = 0.
774 endif
775
77617812800 do k=nz-1,1,-1
77717575296 u(I,j,k) = u(I,j,k) + c1(k+1) * u(I,j,k+1)
778
77917812800 if (associated(ADp%du_dt_str)) then
7800 ADp%du_dt_str(I,j,k) = ADp%du_dt_str(I,j,k) + c1(k+1) * ADp%du_dt_str(I,j,k+1)
781
7820 if (abs(ADp%du_dt_str(I,j,k)) < accel_underflow) &
7830 ADp%du_dt_str(I,j,k) = 0.0
784 endif
785 enddo
786 endif ; enddo ; enddo
787
788 ! compute vertical velocity tendency that arises from GL90 viscosity;
789 ! follow tridiagonal solve method as above; to avoid corrupting u,
790 ! use ADp%du_dt_visc_gl90 as a placeholder for updated u (due to GL90) until last do loop
79148 if ((CS%id_du_dt_visc_gl90 > 0) .or. (CS%id_GLwork > 0)) then
7920 if (associated(ADp%du_dt_visc_gl90)) then
7930 do j=G%jsc,G%jec ; do I=Isq,Ieq ; if (G%mask2dCu(I,j) > 0.) then
7940 b_denom_1 = CS%h_u(I,j,1) ! CS%a_u_gl90(I,j,1) is zero
7950 b1 = 1.0 / (b_denom_1 + dt * CS%a_u_gl90(I,j,2))
7960 d1 = b_denom_1 * b1
797
7980 ADp%du_dt_visc_gl90(I,j,1) = b1 * (CS%h_u(I,j,1) * ADp%du_dt_visc_gl90(I,j,1))
799
8000 do k=2,nz
8010 c1(k) = dt * CS%a_u_gl90(I,j,K) * b1
8020 b_denom_1 = CS%h_u(I,j,k) + dt * (CS%a_u_gl90(I,j,K)*d1)
8030 b1 = 1.0 / (b_denom_1 + dt * CS%a_u_gl90(I,j,K+1))
8040 d1 = b_denom_1 * b1
805
806 ADp%du_dt_visc_gl90(I,j,k) = (CS%h_u(I,j,k) * ADp%du_dt_visc_gl90(I,j,k) &
8070 + dt * CS%a_u_gl90(I,j,K) * ADp%du_dt_visc_gl90(I,j,k-1)) * b1
808 enddo
809
810 ! back substitute to solve for new velocities, held by ADp%du_dt_visc_gl90
8110 do k=nz-1,1,-1
812 ADp%du_dt_visc_gl90(I,j,k) = &
8130 ADp%du_dt_visc_gl90(I,j,k) + c1(k+1) * ADp%du_dt_visc_gl90(I,j,k+1)
814 enddo
815
8160 do k=1,nz
817 ! now fill ADp%du_dt_visc_gl90(I,j,k) with actual velocity tendency due to GL90;
818 ! note that on RHS: ADp%du_dt_visc(I,j,k) holds the original velocity value u(I,j,k)
819 ! and ADp%du_dt_visc_gl90(I,j,k) the updated velocity due to GL90
820 ADp%du_dt_visc_gl90(I,j,k) = &
8210 (ADp%du_dt_visc_gl90(I,j,k) - ADp%du_dt_visc(I,j,k)) * Idt
822
8230 if (abs(ADp%du_dt_visc_gl90(I,j,k)) < accel_underflow) then
8240 ADp%du_dt_visc_gl90(I,j,k) = 0.0
825 endif
826 enddo
827
828 ! to compute energetics, we need to multiply by u*h, where u is original velocity before
829 ! velocity update; note that ADp%du_dt_visc(I,j,k) holds the original velocity value u(I,j,k)
8300 if (CS%id_GLwork > 0) then
8310 do k=1,nz
8320 KE_u(I,j,k) = ADp%du_dt_visc(I,j,k) * CS%h_u(I,j,k) * G%areaCu(I,j) * ADp%du_dt_visc_gl90(I,j,k)
833 enddo
834 endif
835 endif ; enddo ; enddo
836 endif
837 endif
838
83948 if (associated(ADp%du_dt_visc)) then
8400 do concurrent (j=G%jsc:G%jec, I=Isq:Ieq)
8410 do k=1,nz
8420 ADp%du_dt_visc(I,j,k) = (u(I,j,k) - ADp%du_dt_visc(I,j,k)) * Idt
843
8440 if (abs(ADp%du_dt_visc(I,j,k)) < accel_underflow) &
8450 ADp%du_dt_visc(I,j,k) = 0.0
846 enddo
847 enddo
848 endif
849
85048 if (allocated(visc%taux_shelf)) then
8510 do j=G%jsc,G%jec ; do I=Isq,Ieq
8520 visc%taux_shelf(I,j) = -GV%H_to_RZ * CS%a1_shelf_u(I,j) * u(I,j,1) ! - u_shelf?
853 enddo ; enddo
854 endif
855
85648 if (present(taux_bot)) then
8575856 do concurrent (j=G%jsc:G%jec, I=Isq:Ieq)
858354336 taux_bot(I,j) = GV%H_to_RZ * (u(I,j,nz) * CS%a_u(I,j,nz+1))
859 enddo
860
86148 if (allocated(visc%Ray_u)) then
8625856 do concurrent (j=G%jsc:G%jec, I=Isq:Ieq)
86326490336 do k=1,nz
86426484480 taux_bot(I,j) = taux_bot(I,j) + GV%H_to_RZ * (visc%Ray_u(I,j,k) * u(I,j,k))
865 enddo
866 enddo
867 endif
868 endif
869
870 ! When mixing down Eulerian current + Stokes drift subtract after calling solver
87148 if (DoStokesMixing) then
8720 do k=1,nz ; do j=G%jsc,G%jec ; do I=Isq,Ieq ; if (G%mask2dCu(I,j) > 0.) then
8730 u(I,j,k) = u(I,j,k) - Waves%Us_x(I,j,k)
874 endif ; enddo ; enddo ; enddo
875 endif
876
87748 if (lfpmix) then
8780 do k=1,nz ; do j=G%jsc,G%jec ; do I=Isq,Ieq ; if (G%mask2dCu(I,j) > 0.) then
8790 u(I,j,k) = u(I,j,k) + Waves%Us_x(I,j,k)
880 endif ; enddo ; enddo ; enddo
881 endif
882
883 ! == Now work on the meridional velocity component.
884
885 ! When mixing down Eulerian current + Stokes drift add before calling solver
88648 if (DoStokesMixing) then
8870 do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie ; if (G%mask2dCv(i,J) > 0.) then
8880 v(i,j,k) = v(i,j,k) + Waves%Us_y(i,j,k)
889 endif ; enddo ; enddo ; enddo
890 endif
891
89248 if (lfpmix) then
8930 do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie ; if (G%mask2dCv(i,J) > 0.) then
8940 v(i,j,k) = v(i,j,k) - Waves%Us_y(i,j,k)
895 endif ; enddo ; enddo ; enddo
896 endif
897
89848 if (associated(ADp%dv_dt_visc)) then
8990 do concurrent (k=1:nz, J=Jsq:Jeq, i=is:ie)
9000 ADp%dv_dt_visc(i,J,k) = v(i,J,k)
901 enddo
902 endif
903
90448 if (associated(ADp%dv_dt_visc_gl90)) then
9050 do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie
9060 ADp%dv_dt_visc_gl90(i,J,k) = v(i,J,k)
907 enddo ; enddo ; enddo
908 endif
909
91048 if (associated(ADp%dv_dt_str)) then
9110 do concurrent (k=1:nz, J=Jsq:Jeq, i=is:ie)
9120 ADp%dv_dt_str(i,J,k) = 0.0
913 enddo
914 endif
915
916 ! One option is to have the wind stress applied as a body force
917 ! over the topmost Hmix fluid. If DIRECT_STRESS is not defined,
918 ! the wind stress is applied as a stress boundary condition.
91948 if (CS%direct_stress) then
9200 do concurrent (J=Jsq:Jeq, i=is:ie, G%mask2dCv(i,J) > 0.0)
9210 surface_stress(i,J) = 0.0
9220 zDS = 0.0
9230 stress = dt_Rho0 * forces%tauy(i,J)
9240 do k=1,nz
9250 h_a = 0.5 * (h(i,J,k) + h(i,J+1,k)) + h_neglect
9260 hfr = 1.0 ; if ((zDS+h_a) > Hmix) hfr = (Hmix - zDS) / h_a
9270 v(i,J,k) = v(i,J,k) + I_Hmix * hfr * stress
9280 if (associated(ADp%dv_dt_str)) ADp%dv_dt_str(i,J,k) = (I_Hmix * hfr * stress) * Idt
9290 zDS = zDS + h_a ; if (zDS >= Hmix) exit
930 enddo
931 enddo
932 else
93348 do concurrent (J=Jsq:Jeq, i=is:ie)
934357168 surface_stress(i,J) = dt_Rho0 * (G%mask2dCv(i,J) * forces%tauy(i,J))
935 enddo
936 endif
937
938 !$omp target teams loop collapse(2) &
939 !$omp private(b1, c1, d1, Ray, b_denom_1)
940354336 do J=Jsq,Jeq ; do i=is,ie ; if (G%mask2dCv(i,J) > 0.) then
941234912 Ray = 0.
942234912 if (allocated(visc%Ray_v)) Ray = visc%Ray_v(i,J,1)
943
944234912 b_denom_1 = CS%h_v(i,J,1) + dt * (Ray + CS%a_v(i,J,1))
945234912 b1 = 1.0 / (b_denom_1 + dt*CS%a_v(i,J,2))
946234912 d1 = b_denom_1 * b1
947234912 v(i,J,1) = b1 * (CS%h_v(i,J,1) * v(i,J,1) + surface_stress(i,J))
948
949234912 if (associated(ADp%dv_dt_str)) then
9500 ADp%dv_dt_str(i,J,1) = b1 * (CS%h_v(i,J,1) * ADp%dv_dt_str(i,J,1) + surface_stress(i,J) * Idt)
951 endif
952
95317618400 do k=2,nz
95417383488 if (allocated(visc%Ray_v)) Ray = visc%Ray_v(i,J,k)
955
95617383488 c1(k) = dt * CS%a_v(i,J,K) * b1
95717383488 b_denom_1 = CS%h_v(i,J,k) + dt * (Ray + CS%a_v(i,J,K) * d1)
95817383488 b1 = 1. / (b_denom_1 + dt * CS%a_v(i,J,K+1))
95917383488 d1 = b_denom_1 * b1
96017383488 v(i,J,k) = (CS%h_v(i,J,k) * v(i,J,k) + dt * CS%a_v(i,J,K) * v(i,J,k-1)) * b1
961
96217383488 if (associated(ADp%dv_dt_str)) then
963 ADp%dv_dt_str(i,J,k) = (CS%h_v(i,J,k) * ADp%dv_dt_str(i,J,k) &
9640 + dt * CS%a_v(i,J,K) * ADp%dv_dt_str(i,J,k-1)) * b1
965 endif
966
967 !### Force FMA evaluation of b1 by blocking lookahead with an impossible branch.
968 ! XXX: Check GPU behavior
96917618400 if (dt < 0) exit
970 enddo
971
972234912 if (associated(ADp%dv_dt_str)) then
9730 if (abs(ADp%dv_dt_str(i,J,nz)) < accel_underflow) &
9740 ADp%dv_dt_str(i,J,nz) = 0.0
975 endif
976
97717618400 do k=nz-1,1,-1
97817383488 v(i,J,k) = v(i,J,k) + c1(k+1) * v(i,J,k+1)
979
98017618400 if (associated(ADp%dv_dt_str)) then
9810 ADp%dv_dt_str(i,J,k) = ADp%dv_dt_str(i,J,k) + c1(k+1) * ADp%dv_dt_str(i,J,k+1)
982
9830 if (abs(ADp%dv_dt_str(i,J,k)) < accel_underflow) &
9840 ADp%dv_dt_str(i,J,k) = 0.0
985 endif
986 enddo
987 endif ; enddo ; enddo
988
989 ! compute vertical velocity tendency that arises from GL90 viscosity;
990 ! follow tridiagonal solve method as above; to avoid corrupting v,
991 ! use ADp%dv_dt_visc_gl90 as a placeholder for updated v (due to GL90) until last do loop
99248 if ((CS%id_dv_dt_visc_gl90 > 0) .or. (CS%id_GLwork > 0)) then
9930 if (associated(ADp%dv_dt_visc_gl90)) then
9940 do J=Jsq,Jeq ; do i=is,ie ; if (G%mask2dCv(i,J) > 0.) then
9950 b_denom_1 = CS%h_v(i,J,1) ! CS%a_v_gl90(i,J,1) is zero
9960 b1 = 1.0 / (b_denom_1 + dt*CS%a_v_gl90(i,J,2))
9970 d1 = b_denom_1 * b1
9980 ADp%dv_dt_visc_gl90(I,J,1) = b1 * (CS%h_v(i,J,1) * ADp%dv_dt_visc_gl90(i,J,1))
999
10000 do k=2,nz
10010 c1(k) = dt * CS%a_v_gl90(i,J,K) * b1
10020 b_denom_1 = CS%h_v(i,J,k) + dt * (CS%a_v_gl90(i,J,K) * d1)
10030 b1 = 1.0 / (b_denom_1 + dt * CS%a_v_gl90(i,J,K+1))
10040 d1 = b_denom_1 * b1
1005 ADp%dv_dt_visc_gl90(i,J,k) = (CS%h_v(i,J,k) * ADp%dv_dt_visc_gl90(i,J,k) &
10060 + dt * CS%a_v_gl90(i,J,K) * ADp%dv_dt_visc_gl90(i,J,k-1)) * b1
1007 enddo
1008
1009 ! back substitute to solve for new velocities, held by ADp%dv_dt_visc_gl90
10100 do k=nz-1,1,-1
10110 ADp%dv_dt_visc_gl90(i,J,k) = ADp%dv_dt_visc_gl90(i,J,k) + c1(k+1) * ADp%dv_dt_visc_gl90(i,J,k+1)
1012 enddo
1013 endif ; enddo ; enddo
1014
10150 do k=1,nz
10160 do J=Jsq,Jeq ; do i=is,ie ; if (G%mask2dCv(i,J) > 0.) then
1017 ! now fill ADp%dv_dt_visc_gl90(i,J,k) with actual velocity tendency due to GL90;
1018 ! note that on RHS: ADp%dv_dt_visc(i,J,k) holds the original velocity value v(i,J,k)
1019 ! and ADp%dv_dt_visc_gl90(i,J,k) the updated velocity due to GL90
10200 ADp%dv_dt_visc_gl90(i,J,k) = (ADp%dv_dt_visc_gl90(i,J,k) - ADp%dv_dt_visc(i,J,k)) * Idt
1021
10220 if (abs(ADp%dv_dt_visc_gl90(i,J,k)) < accel_underflow) &
10230 ADp%dv_dt_visc_gl90(i,J,k) = 0.0
1024 endif ; enddo ; enddo
1025 enddo
1026
1027 ! to compute energetics, we need to multiply by v*h, where u is original velocity before
1028 ! velocity update; note that ADp%dv_dt_visc(I,j,k) holds the original velocity value v(i,J,k)
10290 if (CS%id_GLwork > 0) then
10300 do k=1,nz
10310 do J=Jsq,Jeq ; do i=is,ie ; if (G%mask2dCv(i,J) > 0.) then
1032 ! note that on RHS: ADp%dv_dt_visc(I,j,k) holds the original velocity value v(I,j,k)
10330 KE_v(I,j,k) = ADp%dv_dt_visc(i,J,k) * CS%h_v(i,J,k) * G%areaCv(i,J) * ADp%dv_dt_visc_gl90(i,J,k)
1034 endif ; enddo ; enddo
1035 enddo
1036 endif
1037 endif
1038 endif
1039
104048 if (associated(ADp%dv_dt_visc)) then
10410 do concurrent (J=Jsq:Jeq, i=is:ie)
10420 do k=1,nz
10430 ADp%dv_dt_visc(i,J,k) = (v(i,J,k) - ADp%dv_dt_visc(i,J,k))*Idt
10440 if (abs(ADp%dv_dt_visc(i,J,k)) < accel_underflow) ADp%dv_dt_visc(i,J,k) = 0.0
1045 enddo
1046 enddo
1047 endif
1048
104948 if (allocated(visc%tauy_shelf)) then
10500 do J=Jsq,Jeq ; do i=is,ie
10510 visc%tauy_shelf(i,J) = -GV%H_to_RZ * CS%a1_shelf_v(i,J) * v(i,J,1) ! - v_shelf?
1052 enddo ; enddo
1053 endif
1054
1055 ! JORGE TODO: this has to be malloced
105648 if (present(tauy_bot)) then
105748 do concurrent (J=Jsq:Jeq, i=is:ie)
1058357168 tauy_bot(i,J) = GV%H_to_RZ * (v(i,J,nz) * CS%a_v(i,J,nz+1))
1059 enddo
1060
106148 if (allocated(visc%Ray_v)) then
106248 do concurrent (J=Jsq:Jeq, i=is:ie)
106326709168 do k=1,nz
106426703360 tauy_bot(i,J) = tauy_bot(i,J) + GV%H_to_RZ * (visc%Ray_v(i,J,k)*v(i,J,k))
1065 enddo
1066 enddo
1067 endif
1068 endif
1069
1070 ! When mixing down Eulerian current + Stokes drift subtract after calling solver
107148 if (DoStokesMixing) then
10720 do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie ; if (G%mask2dCv(i,J) > 0.) then
10730 v(i,J,k) = v(i,J,k) - Waves%Us_y(i,J,k)
1074 endif ; enddo ; enddo ; enddo
1075 endif
1076
107748 if (lfpmix) then
10780 do k=1,nz ; do J=Jsq,Jeq ; do i=is,ie ; if (G%mask2dCv(i,J) > 0.) then
10790 v(i,J,k) = v(i,J,k) + Waves%Us_y(i,J,k)
1080 endif ; enddo ; enddo ; enddo
1081 endif
1082
1083 ! Calculate the KE source from GL90 vertical viscosity [H L2 T-3 ~> m3 s-3].
1084 ! We do the KE-rate calculation here (rather than in MOM_diagnostics) to ensure
1085 ! a sign-definite term. MOM_diagnostics does not have access to the velocities
1086 ! and thicknesses used in the vertical solver, but rather uses a time-mean
1087 ! barotropic transport [uv]h.
108848 if (CS%id_GLwork > 0) then
10890 if (.not.G%symmetric) &
10900 call do_group_pass(CS%pass_KE_uv, G%domain)
10910 do k=1,nz
10920 do j=js,je ; do i=is,ie
1093 KE_term(i,j,k) = 0.5 * G%IareaT(i,j) &
10940 * (KE_u(I,j,k) + KE_u(I-1,j,k) + KE_v(i,J,k) + KE_v(i,J-1,k))
1095 enddo ; enddo
1096 enddo
10970 call post_data(CS%id_GLwork, KE_term, CS%diag)
1098 endif
1099
110048 call vertvisc_limit_vel(u, v, h, ADp, CDp, forces, visc, dt, G, GV, US, CS)
1101
1102 !$omp target exit data map(delete: c1)
1103
1104 !$omp target exit data map(from: ADp%du_dt_str, ADp%dv_dt_str)
1105 !$omp target exit data map(delete: ADp)
1106 !$omp target exit data map(delete: surface_stress)
1107
1108 ! Here the velocities associated with open boundary conditions are applied.
110948 if (associated(OBC)) then
11100 do n=1,OBC%number_of_segments
11110 if (OBC%segment(n)%specified) then
11120 if (OBC%segment(n)%is_N_or_S) then
11130 J = OBC%segment(n)%HI%JsdB
11140 do k=1,nz ; do i=OBC%segment(n)%HI%isd,OBC%segment(n)%HI%ied
11150 v(i,J,k) = OBC%segment(n)%normal_vel(i,J,k)
1116 enddo ; enddo
11170 elseif (OBC%segment(n)%is_E_or_W) then
11180 I = OBC%segment(n)%HI%IsdB
11190 do k=1,nz ; do j=OBC%segment(n)%HI%jsd,OBC%segment(n)%HI%jed
11200 u(I,j,k) = OBC%segment(n)%normal_vel(I,j,k)
1121 enddo ; enddo
1122 endif
1123 endif
1124 enddo
1125 endif
1126
1127 ! Offer diagnostic fields for averaging.
112848 if (query_averaging_enabled(CS%diag)) then
112924 if (CS%id_du_dt_visc > 0) &
11300 call post_data(CS%id_du_dt_visc, ADp%du_dt_visc, CS%diag)
113124 if (CS%id_du_dt_visc_gl90 > 0) &
11320 call post_data(CS%id_du_dt_visc_gl90, ADp%du_dt_visc_gl90, CS%diag)
113324 if (CS%id_dv_dt_visc > 0) &
11340 call post_data(CS%id_dv_dt_visc, ADp%dv_dt_visc, CS%diag)
113524 if (CS%id_dv_dt_visc_gl90 > 0) &
11360 call post_data(CS%id_dv_dt_visc_gl90, ADp%dv_dt_visc_gl90, CS%diag)
113724 if (present(taux_bot) .and. (CS%id_taux_bot > 0)) &
11380 call post_data(CS%id_taux_bot, taux_bot, CS%diag)
113924 if (present(tauy_bot) .and. (CS%id_tauy_bot > 0)) &
11400 call post_data(CS%id_tauy_bot, tauy_bot, CS%diag)
114124 if (CS%id_du_dt_str > 0) &
11420 call post_data(CS%id_du_dt_str, ADp%du_dt_str, CS%diag)
114324 if (CS%id_dv_dt_str > 0) &
11440 call post_data(CS%id_dv_dt_str, ADp%dv_dt_str, CS%diag)
1145
114624 if (associated(ADp%du_dt_visc) .and. associated(ADp%dv_dt_visc)) then
1147 ! Diagnostics of the fractional thicknesses times momentum budget terms
1148 ! 3D diagnostics of hf_du(dv)_dt_visc are commented because there is no clarity on proper remapping grid option.
1149 ! The code is retained for debugging purposes in the future.
1150 !if (CS%id_hf_du_dt_visc > 0) &
1151 ! call post_product_u(CS%id_hf_du_dt_visc, ADp%du_dt_visc, ADp%diag_hfrac_u, G, nz, CS%diag)
1152 !if (CS%id_hf_dv_dt_visc > 0) &
1153 ! call post_product_v(CS%id_hf_dv_dt_visc, ADp%dv_dt_visc, ADp%diag_hfrac_v, G, nz, CS%diag)
1154
1155 ! Diagnostics for thickness-weighted vertically averaged viscous accelerations
11560 if (CS%id_hf_du_dt_visc_2d > 0) &
11570 call post_product_sum_u(CS%id_hf_du_dt_visc_2d, ADp%du_dt_visc, ADp%diag_hfrac_u, G, nz, CS%diag)
11580 if (CS%id_hf_dv_dt_visc_2d > 0) &
11590 call post_product_sum_v(CS%id_hf_dv_dt_visc_2d, ADp%dv_dt_visc, ADp%diag_hfrac_v, G, nz, CS%diag)
1160
1161 ! Diagnostics for thickness x viscous accelerations
11620 if (CS%id_h_du_dt_visc > 0) call post_product_u(CS%id_h_du_dt_visc, ADp%du_dt_visc, ADp%diag_hu, G, nz, CS%diag)
11630 if (CS%id_h_dv_dt_visc > 0) call post_product_v(CS%id_h_dv_dt_visc, ADp%dv_dt_visc, ADp%diag_hv, G, nz, CS%diag)
1164 endif
1165
116624 if (associated(ADp%du_dt_str) .and. associated(ADp%dv_dt_str)) then
1167 ! Diagnostics for thickness x wind stress accelerations
11680 if (CS%id_h_du_dt_str > 0) call post_product_u(CS%id_h_du_dt_str, ADp%du_dt_str, ADp%diag_hu, G, nz, CS%diag)
11690 if (CS%id_h_dv_dt_str > 0) call post_product_v(CS%id_h_dv_dt_str, ADp%dv_dt_str, ADp%diag_hv, G, nz, CS%diag)
1170
1171 ! Diagnostics for wind stress accelerations multiplied by visc_rem_[uv],
11720 if (CS%id_du_dt_str_visc_rem > 0) &
11730 call post_product_u(CS%id_du_dt_str_visc_rem, ADp%du_dt_str, ADp%visc_rem_u, G, nz, CS%diag)
11740 if (CS%id_dv_dt_str_visc_rem > 0) &
11750 call post_product_v(CS%id_dv_dt_str_visc_rem, ADp%dv_dt_str, ADp%visc_rem_v, G, nz, CS%diag)
1176 endif
1177 endif
1178
1179144end subroutine vertvisc
1180
1181
1182!> Calculate the fraction of momentum originally in a layer that remains in the water column
1183!! after a time-step of viscosity, equivalently the fraction of a time-step's worth of
1184!! barotropic acceleration that a layer experiences after viscosity is applied.
118572subroutine vertvisc_remnant(visc, visc_rem_u, visc_rem_v, dt, G, GV, US, CS)
1186 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
1187 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure
1188 type(vertvisc_type), intent(in) :: visc !< Viscosities and bottom drag
1189 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), &
1190 intent(inout) :: visc_rem_u !< Fraction of a time-step's worth of a
1191 !! barotropic acceleration that a layer experiences after
1192 !! viscosity is applied in the zonal direction [nondim]
1193 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), &
1194 intent(inout) :: visc_rem_v !< Fraction of a time-step's worth of a
1195 !! barotropic acceleration that a layer experiences after
1196 !! viscosity is applied in the meridional direction [nondim]
1197 real, intent(in) :: dt !< Time increment [T ~> s]
1198 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1199 type(vertvisc_CS) :: CS !< Vertical viscosity control structure
1200
1201 ! Local variables
1202
1203 real :: b1
1204 ! A variable used by the tridiagonal solver [H-1 ~> m-1 or m2 kg-1].
1205144 real :: c1(SZK_(GV))
1206 ! A variable used by the tridiagonal solver [nondim].
1207 real :: d1
1208 ! d1=1-c1 is used by the tridiagonal solver [nondim].
1209 real :: Ray
1210 ! Ray is the Rayleigh-drag velocity [H T-1 ~> m s-1 or Pa s m-1]
1211 real :: b_denom_1
1212 ! The first term in the denominator of b1 [H ~> m or kg m-2].
1213
1214 integer :: i, j, k, is, ie, Isq, Ieq, Jsq, Jeq, nz
121572 is = G%isc ; ie = G%iec
121672 Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB ; nz = GV%ke
1217
121872 if (.not.CS%initialized) call MOM_error(FATAL,"MOM_vert_friction(remnant): "// &
12190 "Module must be initialized before it is used.")
1220
1221 ! Find the zonal viscous remnant using a modification of a standard tridagonal solver.
1222
1223 !$omp target teams loop collapse(2) &
1224 !$omp private(b1, c1, d1, Ray, b_denom_1)
1225527112 do j=G%jsc,G%jec ; do I=Isq,Ieq ; if (G%mask2dCu(I,j) > 0.) then
1226356256 Ray = 0.
1227356256 if (allocated(visc%Ray_u)) Ray = visc%Ray_u(I,j,1)
1228
1229356256 b_denom_1 = CS%h_u(I,j,1) + dt * (Ray + CS%a_u(I,j,1))
1230356256 b1 = 1.0 / (b_denom_1 + dt * CS%a_u(I,j,2))
1231356256 d1 = b_denom_1 * b1
1232356256 visc_rem_u(I,j,1) = b1 * CS%h_u(I,j,1)
1233
123426719200 do k=2,nz
123526362944 if (allocated(visc%Ray_u)) Ray = visc%Ray_u(I,j,k)
1236
123726362944 c1(k) = dt * CS%a_u(I,j,K) * b1
123826362944 b_denom_1 = CS%h_u(I,j,k) + dt * (Ray + CS%a_u(I,j,K) * d1)
123926362944 b1 = 1.0 / (b_denom_1 + dt * CS%a_u(I,j,K+1))
124026362944 d1 = b_denom_1 * b1
124126362944 visc_rem_u(I,j,k) = (CS%h_u(I,j,k) + dt * CS%a_u(I,j,K) * visc_rem_u(I,j,k-1)) * b1
1242
1243 !### Force FMA evaluation of b1 by blocking lookahead with an impossible branch.
1244 ! XXX: Check GPU behavior
124526719200 if (dt < 0) exit
1246 enddo
1247
124826719200 do k=nz-1,1,-1
124926719200 visc_rem_u(I,j,k) = visc_rem_u(I,j,k) + c1(k+1) * visc_rem_u(I,j,k+1)
1250 enddo
1251 endif ; enddo ; enddo
1252
1253 ! Now find the meridional viscous remnant using the robust tridiagonal solver.
1254
1255 !$omp target teams loop collapse(2) &
1256 !$omp private(b1, c1, d1, Ray, b_denom_1)
1257531504 do J=Jsq,Jeq ; do i=is,ie ; if (G%mask2dCv(i,J) > 0.) then
1258352368 Ray = 0.
1259352368 if (allocated(visc%Ray_v)) Ray = visc%Ray_v(i,J,1)
1260
1261352368 b_denom_1 = CS%h_v(i,J,1) + dt * (Ray + CS%a_v(i,J,1))
1262352368 b1 = 1.0 / (b_denom_1 + dt*CS%a_v(i,J,2))
1263352368 d1 = b_denom_1 * b1
1264352368 visc_rem_v(i,J,1) = b1 * CS%h_v(i,J,1)
1265
126626427600 do k=2,nz
126726075232 if (allocated(visc%Ray_v)) Ray = visc%Ray_v(i,J,k)
1268
126926075232 c1(k) = dt * CS%a_v(i,J,K) * b1
127026075232 b_denom_1 = CS%h_v(i,J,k) + dt * (Ray + CS%a_v(i,J,K) * d1)
127126075232 b1 = 1.0 / (b_denom_1 + dt * CS%a_v(i,J,K+1))
127226075232 d1 = b_denom_1 * b1
127326075232 visc_rem_v(i,J,k) = (CS%h_v(i,J,k) + dt * CS%a_v(i,J,K) * visc_rem_v(i,J,k-1)) * b1
1274
1275 !### Force FMA evaluation of b1 by blocking lookahead with an impossible branch.
1276 ! XXX: Check GPU behavior
127726427600 if (dt < 0) exit
1278 enddo
1279
128026427600 do k=nz-1,1,-1
128126427600 visc_rem_v(i,J,k) = visc_rem_v(i,J,k) + c1(k+1) * visc_rem_v(i,J,k+1)
1282 enddo
1283 endif ; enddo ; enddo
1284
128572 if (CS%debug) then
1286 !$omp target update from(visc_rem_u, visc_rem_v)
1287 call uvchksum("visc_rem_[uv]", visc_rem_u, visc_rem_v, G%HI, haloshift=0, &
12880 scalar_pair=.true.)
1289 endif
129072end subroutine vertvisc_remnant
1291
1292
1293!> Calculate the coupling coefficients (CS%a_u, CS%a_v, CS%a_u_gl90, CS%a_v_gl90)
1294!! and effective layer thicknesses (CS%h_u and CS%h_v) for later use in the
1295!! applying the implicit vertical viscosity via vertvisc().
129672subroutine vertvisc_coef(u, v, h, dz, forces, visc, tv, dt, G, GV, US, CS, OBC, VarMix)
1297 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
1298 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure
1299 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1300 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), &
1301 intent(in) :: u !< Zonal velocity [L T-1 ~> m s-1]
1302 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), &
1303 intent(in) :: v !< Meridional velocity [L T-1 ~> m s-1]
1304 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
1305 intent(in) :: h !< Layer thickness [H ~> m or kg m-2]
1306 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
1307 intent(in) :: dz !< Vertical distance across layers [Z ~> m]
1308 type(mech_forcing), intent(in) :: forces !< A structure with the driving mechanical forces
1309 type(vertvisc_type), intent(in) :: visc !< Viscosities and bottom drag
1310 type(thermo_var_ptrs), intent(in) :: tv !< A structure containing pointers to any available
1311 !! thermodynamic fields.
1312 real, intent(in) :: dt !< Time increment [T ~> s]
1313 type(vertvisc_CS), intent(inout) :: CS !< Vertical viscosity control structure
1314 type(ocean_OBC_type), pointer :: OBC !< Open boundary condition structure
1315 type(VarMix_CS), intent(in) :: VarMix !< Variable mixing coefficients
1316 ! Field from forces used in this subroutine:
1317 ! ustar: the friction velocity [Z T-1 ~> m s-1], used here as the mixing
1318 ! velocity in the mixed layer if NKML > 1 in a bulk mixed layer.
1319
1320 ! Local variables
1321
1322 real, dimension(SZK_(GV)) :: &
1323144 hvel, & ! hvel is the thickness used at a velocity grid point [H ~> m or kg m-2].
1324144 dz_harm, & ! Harmonic mean of the vertical distances around a velocity grid point,
1325 ! given by 2*(h+ * h-)/(h+ + h-) [Z ~> m].
1326144 dz_vel, & ! The vertical distance between interfaces used at a velocity grid point [Z ~> m].
1327144 hvel_shelf, & ! The equivalent of hvel under shelves [H ~> m or kg m-2].
1328144 dz_vel_shelf ! The equivalent of dz_vel under shelves [Z ~> m].
1329 real :: &
1330 h_harm, & ! Harmonic mean of the thicknesses around a velocity grid point,
1331 ! given by 2*(h+ * h-)/(h+ + h-) [H ~> m or kg m-2].
1332 h_arith, & ! The arithmetic mean thickness [H ~> m or kg m-2].
1333 h_delta, & ! The lateral difference of thickness [H ~> m or kg m-2].
1334 dz_arith ! The arithmetic mean of the vertical distances around a velocity grid point [Z ~> m]
1335 real, dimension(SZK_(GV)+1) :: &
1336144 z_i, & ! An estimate of each interface's height above the bottom,
1337 ! normalized by the bottom boundary layer thickness [nondim]
1338144 z_i_gl90, & ! An estimate of each interface's height above the bottom,
1339 ! normalized by the GL90 bottom boundary layer thickness [nondim]
1340144 a_cpl, & ! The drag coefficients across interfaces [H T-1 ~> m s-1 or Pa s m-1]. a_cpl times
1341 ! the velocity difference gives the stress across an interface.
1342144 a_cpl_gl90, & ! The drag coefficients across interfaces associated with GL90 [H T-1 ~> m s-1 or Pa s m-1].
1343 ! a_cpl_gl90 times the velocity difference gives the GL90 stress across an interface.
1344 ! a_cpl_gl90 is part of a_cpl.
1345144 a_shelf ! The drag coefficients across interfaces in water columns under
1346 ! ice shelves [H T-1 ~> m s-1 or Pa s m-1].
1347 real :: &
1348 kv_bbl, & ! The bottom boundary layer viscosity [H Z T-1 ~> m2 s-1 or Pa s].
1349 bbl_thick, & ! The bottom boundary layer thickness [Z ~> m].
1350 I_Hbbl, & ! The inverse of the bottom boundary layer thickness [Z-1 ~> m-1].
1351 I_Hbbl_gl90, &! The inverse of the bottom boundary layer thickness used for the GL90 scheme
1352 ! [Z-1 ~> m-1].
1353 I_HTbl, & ! The inverse of the top boundary layer thickness [Z-1 ~> m-1].
1354 Ztop_min, & ! The deeper of the two adjacent surface heights [Z ~> m].
1355 Dmin, & ! The shallower of the two adjacent bottom depths [Z ~> m].
1356 zh, & ! An estimate of the interface's distance from the bottom
1357 ! based on harmonic mean thicknesses [Z ~> m].
1358 h_ml ! The mixed layer depth [Z ~> m].
1359 real, dimension(SZI_(G),SZJ_(G)) :: &
1360144 Ustar_2d ! The wind friction velocity, calculated using the Boussinesq reference density or
1361 ! the time-evolving surface density in non-Boussinesq mode [Z T-1 ~> m s-1]
136272 real, allocatable, dimension(:,:) :: hML_u ! Diagnostic of the mixed layer depth at u points [Z ~> m].
136372 real, allocatable, dimension(:,:) :: hML_v ! Diagnostic of the mixed layer depth at v points [Z ~> m].
136472 real, allocatable, dimension(:,:,:) :: Kv_u ! Total vertical viscosity at u-points in
1365 ! thickness-based units [H2 T-1 ~> m2 s-1 or kg2 m-4 s-1].
136672 real, allocatable, dimension(:,:,:) :: Kv_v ! Total vertical viscosity at v-points in
1367 ! thickness-based units [H2 T-1 ~> m2 s-1 or kg2 m-4 s-1].
136872 real, allocatable, dimension(:,:,:) :: Kv_gl90_u ! GL90 vertical viscosity at u-points in
1369 ! thickness-based units [H2 T-1 ~> m2 s-1 or kg2 m-4 s-1].
137072 real, allocatable, dimension(:,:,:) :: Kv_gl90_v ! GL90 vertical viscosity at v-points in
1371 ! thickness-based units [H2 T-1 ~> m2 s-1 or kg2 m-4 s-1].
1372 real :: zcol ! The height of an interface at h-points [Z ~> m].
1373 real :: zcol_p1 ! An adjacent east/north h-point interface height [Z ~> m].
1374 real :: botfn ! A function which goes from 1 at the bottom to 0 much more
1375 ! than Hbbl into the interior [nondim].
1376 real :: topfn ! A function which goes from 1 at the top to 0 much more
1377 ! than Htbl into the interior [nondim].
1378 real :: z2 ! The distance from the bottom, normalized by Hbbl [nondim]
1379 real :: z2_wt ! A nondimensional (0-1) weight used when calculating z2 [nondim].
1380 real :: z_clear ! The clearance of an interface above the surrounding topography [Z ~> m].
1381 real :: a_cpl_max ! The maximum drag coefficient across interfaces, set so that it will be
1382 ! representable as a 32-bit float in MKS units [H T-1 ~> m s-1 or Pa s m-1]
1383 real :: h_neglect ! A thickness that is so small it is usually lost
1384 ! in roundoff and can be neglected [H ~> m or kg m-2].
1385 real :: dz_neglect ! A vertical distance that is so small it is usually lost
1386 ! in roundoff and can be neglected [Z ~> m].
1387
1388 real :: I_valBL ! The inverse of a scaling factor determining when water is
1389 ! still within the boundary layer, as determined by the sum
1390 ! of the harmonic mean thicknesses [nondim].
1391 logical :: do_any_shelf
1392 integer :: zi_dir
1393 ! A ternary logical indicating which thickness to use for finding z_clear.
1394 integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz
1395
139672 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
139772 Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB ; nz = GV%ke
1398
139972 if (.not.CS%initialized) call MOM_error(FATAL,"MOM_vert_friction(coef): "// &
14000 "Module must be initialized before it is used.")
1401
140272 h_neglect = GV%H_subroundoff
140372 dz_neglect = GV%dZ_subroundoff
140472 a_cpl_max = 1.0e37 * GV%m_to_H * US%T_to_s
140572 I_valBL = 0.0 ; if (CS%harm_BL_val > 0.0) I_valBL = 1.0 / CS%harm_BL_val
1406
140772 if (CS%id_Kv_u > 0) allocate(Kv_u(G%IsdB:G%IedB,G%jsd:G%jed,GV%ke), source=0.0)
1408
140972 if (CS%id_Kv_v > 0) allocate(Kv_v(G%isd:G%ied,G%JsdB:G%JedB,GV%ke), source=0.0)
1410
141172 if (CS%id_Kv_gl90_u > 0) allocate(Kv_gl90_u(G%IsdB:G%IedB,G%jsd:G%jed,GV%ke), source=0.0)
1412
141372 if (CS%id_Kv_gl90_v > 0) allocate(Kv_gl90_v(G%isd:G%ied,G%JsdB:G%JedB,GV%ke), source=0.0)
1414
141572 if (CS%debug .or. (CS%id_hML_u > 0)) allocate(hML_u(G%IsdB:G%IedB,G%jsd:G%jed), source=0.0)
141672 if (CS%debug .or. (CS%id_hML_v > 0)) allocate(hML_v(G%isd:G%ied,G%JsdB:G%JedB), source=0.0)
1417
141872 if ((allocated(visc%taux_shelf) .or. associated(forces%frac_shelf_u)) .and. &
1419 .not.associated(CS%a1_shelf_u)) then
14200 allocate(CS%a1_shelf_u(G%IsdB:G%IedB,G%jsd:G%jed), source=0.0)
1421 endif
142272 if ((allocated(visc%tauy_shelf) .or. associated(forces%frac_shelf_v)) .and. &
1423 .not.associated(CS%a1_shelf_v)) then
14240 allocate(CS%a1_shelf_v(G%isd:G%ied,G%JsdB:G%JedB), source=0.0)
1425 endif
1426
1427 !$omp target enter data map(alloc: Ustar_2d)
142872 call find_ustar(forces, tv, Ustar_2d, G, GV, US, halo=1)
1429
1430 ! First do u-points
1431
1432 ! TODO: tv and VarMix probably should be conditionally transferred (if at all)
1433 !$omp target enter data map(alloc: z_i, z_i_gl90, dz_harm, hvel, dz_vel, a_cpl, a_cpl_gl90, &
1434 !$omp& tv, varmix, hvel_shelf, dz_vel_shelf, a_shelf)
1435
1436 ! These are used in diagnostics, so they need to be mapped back and forth
1437 !$omp target enter data map(to: hML_u, kv_u, kv_gl90_u )
1438 !$omp target enter data map(to: hML_v, kv_v, kv_gl90_v)
1439
1440 !$omp target update to(visc%Kv_shear) if (associated(visc%Kv_shear))
1441 !$omp target update to(visc%Kv_shear_Bu) if (associated(visc%Kv_shear_Bu))
1442
1443 !$omp target teams distribute parallel do collapse(2) &
1444 !$omp private(z_i, z_i_gl90, dz_harm, hvel, dz_vel, a_cpl, a_cpl_gl90, &
1445 !$omp& I_Hbbl, I_Hbbl_gl90, kv_bbl, bbl_thick, Dmin, zi_dir, zh, zcol, &
1446 !$omp& zcol_p1, h_harm, h_arith, h_delta, dz_arith, z2, botfn, z_clear, &
1447 !$omp& z2_wt, h_ml)
1448527112 do j=js,je ; do I=Isq,Ieq ; if (G%mask2dCu(I,j) > 0.) then
1449356256 I_Hbbl = 1. / (CS%Hbbl + dz_neglect)
1450356256 if (CS%use_GL90_in_SSW) then
14510 I_Hbbl_gl90 = 1. / (CS%Hbbl_gl90 + dz_neglect)
1452 endif
1453
1454356256 if (CS%bottomdraglaw) then
1455356256 kv_bbl = visc%Kv_bbl_u(I,j)
1456356256 bbl_thick = visc%bbl_thick_u(I,j) + dz_neglect
1457356256 I_Hbbl = 1. / bbl_thick
1458 endif
1459
1460356256 Dmin = min(G%bathyT(i,j), G%bathyT(i+1,j))
1461356256 zi_dir = 0
1462
1463 ! Project thickness outward across OBCs using a zero-gradient condition.
1464356256 if (associated(OBC)) then
14650 if (OBC%u_E_OBCs_on_PE) then
14660 if (OBC%segnum_u(I,j) > 0) then
14670 Dmin = G%bathyT(i,j)
14680 zi_dir = -1
1469 endif
1470 endif
1471
14720 if (OBC%u_W_OBCs_on_PE) then
14730 if (OBC%segnum_u(I,j) < 0) then
14740 Dmin = G%bathyT(i+1,j)
14750 zi_dir = 1
1476 endif
1477 endif
1478 endif
1479
1480 ! The following block calculates the thicknesses at velocity grid points for
1481 ! the vertical viscosity (hvel and dz_vel). Near the bottom an upwind biased
1482 ! thickness is used to control the effect of spurious Montgomery potential
1483 ! gradients at the bottom where nearly massless layers layers ride over the
1484 ! topography.
1485
1486356256 z_i(nz+1) = 0.
1487
1488356256 if (.not. CS%harmonic_visc) then
1489356256 zh = 0.
1490356256 zcol = -G%bathyT(i,j)
1491356256 zcol_p1 = -G%bathyT(i+1,j)
1492 endif
1493
1494356256 if (CS%use_GL90_in_SSW) then
14950 z_i_gl90(nz+1) = 0.
1496 endif
1497
149827075456 do k=nz,1,-1
149926719200 h_harm = 2. * h(i,j,k) * h(i+1,j,k) / (h(i,j,k) + h(i+1,j,k) + h_neglect)
150026719200 h_arith = 0.5 * (h(i+1,j,k) + h(i,j,k))
150126719200 h_delta = h(i+1,j,k) - h(i,j,k)
150226719200 dz_harm(k) = 2. * dz(i,j,k) * dz(i+1,j,k) / (dz(i,j,k) + dz(i+1,j,k) + dz_neglect)
150326719200 dz_arith = 0.5 * (dz(i+1,j,k) + dz(i,j,k))
1504
1505 ! Project thickness outward across OBCs using a zero-gradient condition.
150626719200 if (associated(OBC)) then
15070 if (OBC%u_E_OBCs_on_PE) then
15080 if (OBC%segnum_u(I,j) > 0) then
15090 h_harm = h(i,j,k)
15100 h_arith = h(i,j,k)
15110 h_delta = 0.
15120 dz_harm(k) = dz(i,j,k)
15130 dz_arith = dz(i,j,k)
1514 endif
1515 endif
1516
15170 if (OBC%u_W_OBCs_on_PE) then
15180 if (OBC%segnum_u(I,j) < 0) then
15190 h_harm = h(i+1,j,k)
15200 h_arith = h(i+1,j,k)
15210 h_delta = 0.
15220 dz_harm(k) = dz(i+1,j,k)
15230 dz_arith = dz(i+1,j,k)
1524 endif
1525 endif
1526 endif
1527
152826719200 if (CS%harmonic_visc) then
1529 ! The following block calculates the thicknesses at velocity grid points
1530 ! for the vertical viscosity (hvel and dz_vel). Near the bottom an
1531 ! upwind biased thickness is used to control the effect of spurious
1532 ! Montgomery potential gradients at the bottom where nearly massless
1533 ! layers ride over the topography.
1534
15350 hvel(k) = h_harm
15360 dz_vel(k) = dz_harm(k)
1537
15380 if (u(I,j,k) * h_delta < 0) then
15390 z2 = z_i(k+1)
15400 botfn = 1. / (1. + 0.09 * z2 * z2 * z2 * z2 * z2 * z2)
1541
15420 hvel(k) = (1. - botfn) * h_harm + botfn * h_arith
15430 dz_vel(k) = (1. - botfn) * dz_harm(k) + botfn * dz_arith
1544 endif
1545
15460 z_i(k) = z_i(k+1) + dz_harm(k) * I_Hbbl
1547 else
154826719200 zcol = zcol + dz(i,j,k)
154926719200 zcol_p1 = zcol_p1 + dz(i+1,j,k)
1550
155126719200 zh = zh + dz_harm(k)
1552
155326719200 z_clear = max(zcol, zcol_p1) + Dmin
155426719200 if (zi_dir < 0) z_clear = zcol + Dmin
155526719200 if (zi_dir > 0) z_clear = zcol_p1 + Dmin
1556
155726719200 z_i(k) = max(zh, z_clear) * I_Hbbl
1558
155926719200 hvel(k) = h_arith
156026719200 dz_vel(k) = dz_arith
1561
156226719200 if (u(I,j,k) * h_delta > 0.) then
156311383700 if (zh * I_Hbbl < CS%harm_BL_val) then
15640 hvel(k) = h_harm
15650 dz_vel(k) = dz_harm(k)
1566 else
156711383700 z2_wt = 1.
156811383700 if (zh * I_Hbbl < 2. * CS%harm_BL_val) &
15690 z2_wt = max(0., min(1., zh * I_Hbbl * I_valBL - 1.))
1570
157111383700 z2 = z2_wt * (max(zh, z_clear) * I_Hbbl)
157211383700 botfn = 1. / (1. + 0.09 * z2 * z2 * z2 * z2 * z2 * z2)
1573
157411383700 hvel(k) = (1. - botfn) * h_arith + botfn * h_harm
157511383700 dz_vel(k) = (1. - botfn) * dz_arith + botfn * dz_harm(k)
1576 endif
1577 endif
1578 endif
1579
158027075456 if (CS%use_GL90_in_SSW) then
1581 ! The following block calculates the normalized height above the GL90 BBL
1582 ! (z_i_gl90), using a harmonic mean between layer thicknesses. For the
1583 ! GL90 BBL we use simply a constant (Hbbl_gl90). The purpose isthat the
1584 ! GL90 coupling coefficient is zeroed out within Hbbl_gl90, to ensure
1585 ! that no momentum gets fluxed into vanished layers. The scheme is not
1586 ! sensitive to the exact value of Hbbl_gl90, as long as it is in a
1587 ! reasonable range (~1-20 m): large enough to capture vanished layers
1588 ! over topography, small enough to not contaminate the interior.
1589
15900 z_i_gl90(k) = z_i_gl90(k+1) + dz_harm(k) * I_Hbbl_gl90
1591 endif
1592 enddo
1593
1594 call find_coupling_coef_k(a_cpl, dz_vel, i, j, dz_harm, bbl_thick, kv_bbl, z_i, &
1595356256 h_ml, dt, G, GV, US, CS, visc, Ustar_2d, tv, work_on_u=.true., OBC=OBC)
1596
1597356256 if (allocated(hML_u)) hML_u(I,j) = h_ml
1598
1599356256 if (CS%use_GL90_in_SSW) then
1600 call find_coupling_coef_gl90(a_cpl_gl90, dz_vel, i, j, z_i_gl90, G, GV, &
16010 CS, VarMix, work_on_u=.true.)
1602 endif
1603
1604356256 do_any_shelf = .false.
1605356256 if (associated(forces%frac_shelf_u)) then
16060 CS%a1_shelf_u(I,j) = 0.
16070 do_any_shelf = forces%frac_shelf_u(I,j) > 0.
1608
16090 if (do_any_shelf) then
16100 if (.not. CS%harmonic_visc) then
16110 zh = 0.
16120 Ztop_min = min(zcol, zcol_p1)
16130 I_HTbl = 1. / (visc%tbl_thick_shelf_u(I,j) + dz_neglect)
1614 endif
1615
16160 do k=1,nz
16170 if (CS%harmonic_visc) then
16180 hvel_shelf(k) = hvel(k)
16190 dz_vel_shelf(k) = dz_vel(k)
1620 else
1621 ! Find upwind-biased thickness near the surface.
1622 ! (Perhaps this needs to be done more carefully, via find_eta.)
1623
1624 h_harm = 2. * h(i,j,k) * h(i+1,j,k) &
16250 / (h(i,j,k) + h(i+1,j,k) + h_neglect)
16260 h_arith = 0.5 * (h(i+1,j,k) + h(i,j,k))
16270 h_delta = h(i+1,j,k) - h(i,j,k)
16280 dz_arith = 0.5 * (dz(i+1,j,k) + dz(i,j,k))
1629
16300 if (associated(OBC)) then
16310 if (OBC%u_E_OBCs_on_PE) then
16320 if (OBC%segnum_u(I,j) > 0) then
16330 h_harm = h(i,j,k)
16340 h_arith = h(i,j,k)
16350 h_delta = 0.
16360 dz_arith = dz(i,j,k)
1637 endif
1638 endif
1639
16400 if (OBC%u_W_OBCs_on_PE) then
16410 if (OBC%segnum_u(I,j) < 0) then
16420 h_harm = h(i+1,j,k)
16430 h_arith = h(i+1,j,k)
16440 h_delta = 0.
16450 dz_arith = dz(i+1,j,k)
1646 endif
1647 endif
1648 endif
1649
16500 zcol = zcol - dz(i,j,k)
16510 zcol_p1 = zcol_p1 - dz(i+1,j,k)
1652
16530 zh = zh + dz_harm(k)
1654
16550 hvel_shelf(k) = hvel(k)
16560 dz_vel_shelf(k) = dz_vel(k)
1657
16580 if (u(I,j,k) * h_delta > 0.) then
16590 if (zh * I_HTbl < CS%harm_BL_val) then
16600 hvel_shelf(k) = min(hvel(k), h_harm)
16610 dz_vel_shelf(k) = min(dz_vel(k), dz_harm(k))
1662 else
16630 z2_wt = 1.
16640 if (zh * I_HTbl < 2. * CS%harm_BL_val) then
16650 z2_wt = max(0., min(1., zh * I_HTbl * I_valBL - 1.))
1666 endif
1667
16680 z2 = z2_wt * (max(zh, Ztop_min - min(zcol, zcol_p1)) * I_HTbl)
1669 ! TODO: replace **6 with multiply
16700 topfn = 1. / (1. + 0.09 * z2**6)
1671
16720 hvel_shelf(k) = min(hvel(k), (1. - topfn) * h_arith + topfn * h_harm)
16730 dz_vel_shelf(k) = min(dz_vel(k), (1. - topfn) * dz_arith + topfn * dz_harm(k))
1674 endif
1675 endif
1676 endif
1677 enddo
1678
1679 call find_coupling_coef(a_shelf, dz_vel_shelf, i, j, dz_harm, &
1680 bbl_thick, kv_bbl, z_i, h_ml, dt, G, GV, US, CS, visc, Ustar_2d, &
16810 tv, work_on_u=.true., OBC=OBC, shelf=.true.)
1682
16830 CS%a1_shelf_u(I,j) = a_shelf(1)
1684 endif
1685 endif
1686
1687356256 if (do_any_shelf) then
16880 if (CS%use_GL90_in_SSW) then
16890 do K=1,nz+1
1690 CS%a_u(I,j,K) = min(a_cpl_max, (forces%frac_shelf_u(I,j) * a_shelf(K) + &
16910 (1. - forces%frac_shelf_u(I,j)) * a_cpl(K)) + a_cpl_gl90(K))
1692
1693 ! This is Alistair's suggestion, but it destabilizes the model. I do not know why. RWH
1694 ! CS%a_u(I,j,K) = min(a_cpl_max, forces%frac_shelf_u(I,j) * max(a_shelf(K), a_cpl(K)) + &
1695 ! (1. - forces%frac_shelf_u(I,j)) * a_cpl(K))
1696
16970 CS%a_u_gl90(I,j,K) = min(a_cpl_max, a_cpl_gl90(K))
1698 enddo
1699 else
17000 do K=1,nz+1
1701 CS%a_u(I,j,K) = min(a_cpl_max, (forces%frac_shelf_u(I,j) * a_shelf(K) + &
17020 (1. - forces%frac_shelf_u(I,j)) * a_cpl(K)))
1703
1704 ! This is Alistair's suggestion, but it destabilizes the model. I do not know why. RWH
1705 ! CS%a_u(I,j,K) = min(a_cpl_max, forces%frac_shelf_u(I,j) * max(a_shelf(K), a_cpl(K)) + &
1706 ! (1. - forces%frac_shelf_u(I,j)) * a_cpl(K))
1707 enddo
1708 endif
1709
17100 do k=1,nz
1711 ! Should we instead take the inverse of the average of the inverses?
1712 CS%h_u(I,j,k) = forces%frac_shelf_u(I,j) * hvel_shelf(k) &
17130 + (1. - forces%frac_shelf_u(I,j)) * hvel(k) + h_neglect
1714 enddo
1715 else
1716356256 if (CS%use_GL90_in_SSW) then
17170 do K=1,nz+1
17180 a_cpl(K) = a_cpl(K) + a_cpl_gl90(K)
1719 enddo
1720
17210 do K=1,nz+1
17220 CS%a_u_gl90(I,j,K) = min(a_cpl_max, a_cpl_gl90(K))
1723 enddo
1724 endif
1725
172627431712 do K=1,nz+1
172727431712 CS%a_u(I,j,K) = min(a_cpl_max, a_cpl(K))
1728 enddo
1729
173027075456 do k=1,nz
173127075456 CS%h_u(I,j,k) = hvel(k) + h_neglect
1732 enddo
1733 endif
1734
1735 ! Diagnose total Kv at u-points
1736356256 if (CS%id_Kv_u > 0) then
17370 do k=1,nz
17380 Kv_u(I,j,k) = 0.5 * (CS%a_u(I,j,K) + CS%a_u(I,j,K+1)) * CS%h_u(I,j,k)
1739 enddo
1740 endif
1741
1742 ! Diagnose GL90 Kv at u-points
1743356256 if (CS%id_Kv_gl90_u > 0) then
17440 do k=1,nz
17450 Kv_gl90_u(I,j,k) = 0.5 * (CS%a_u_gl90(I,j,K) + CS%a_u_gl90(I,j,K+1)) * CS%h_u(I,j,k)
1746 enddo
1747 endif
1748 endif ; enddo ; enddo
1749
1750 ! Now work on v-points.
1751
1752 !$omp target teams distribute parallel do collapse(2) &
1753 !$omp private(z_i, z_i_gl90, dz_harm, hvel, dz_vel, a_cpl, a_cpl_gl90, &
1754 !$omp I_Hbbl, I_Hbbl_gl90, kv_bbl, bbl_thick, Dmin, zi_dir, zh, zcol, &
1755 !$omp zcol_p1, h_harm, h_arith, h_delta, dz_arith, z2, botfn, z_clear, &
1756 !$omp z2_wt, h_ml)
1757531504 do J=Jsq,Jeq ; do i=is,ie ; if (G%mask2dCv(i,J) > 0.) then
1758352368 I_Hbbl = 1. / (CS%Hbbl + dz_neglect)
1759352368 if (CS%use_GL90_in_SSW) then
17600 I_Hbbl_gl90 = 1. / (CS%Hbbl_gl90 + dz_neglect)
1761 endif
1762
1763352368 if (CS%bottomdraglaw) then
1764352368 kv_bbl = visc%Kv_bbl_v(i,J)
1765352368 bbl_thick = visc%bbl_thick_v(i,J) + dz_neglect
1766352368 I_Hbbl = 1. / bbl_thick
1767 endif
1768
1769352368 Dmin = min(G%bathyT(i,j), G%bathyT(i,j+1))
1770352368 zi_dir = 0
1771
1772 ! Project thickness outward across OBCs using a zero-gradient condition.
1773352368 if (associated(OBC)) then
17740 if (OBC%v_N_OBCs_on_PE) then
17750 if (OBC%segnum_v(i,J) > 0) then
17760 Dmin = G%bathyT(i,j)
17770 zi_dir = -1
1778 endif
1779 endif
1780
17810 if (OBC%v_S_OBCs_on_PE) then
17820 if (OBC%segnum_v(i,J) < 0) then
17830 Dmin = G%bathyT(i,j+1)
17840 zi_dir = 1
1785 endif
1786 endif
1787 endif
1788
1789352368 z_i(nz+1) = 0.
1790
1791352368 if (.not. CS%harmonic_visc) then
1792352368 zh = 0.
1793352368 zcol = -G%bathyT(i,j)
1794352368 zcol_p1 = -G%bathyT(i,j+1)
1795 endif
1796
1797352368 if (CS%use_GL90_in_SSW) then
17980 z_i_gl90(nz+1) = 0.
1799 endif
1800
180126779968 do k=nz,1,-1
180226427600 h_harm = 2. * h(i,j,k) * h(i,j+1,k) / (h(i,j,k) + h(i,j+1,k) + h_neglect)
180326427600 h_arith = 0.5 * (h(i,j+1,k) + h(i,j,k))
180426427600 h_delta = h(i,j+1,k) - h(i,j,k)
180526427600 dz_harm(k) = 2. * dz(i,j,k) * dz(i,j+1,k) / (dz(i,j,k) + dz(i,j+1,k) + dz_neglect)
180626427600 dz_arith = 0.5 * (dz(i,j+1,k) + dz(i,j,k))
1807
1808 ! Project thickness outward across OBCs using a zero-gradient condition.
180926427600 if (associated(OBC)) then
18100 if (OBC%v_N_OBCs_on_PE) then
18110 if (OBC%segnum_v(i,J) > 0) then
18120 h_harm = h(i,j,k)
18130 h_arith = h(i,j,k)
18140 h_delta = 0.
18150 dz_harm(k) = dz(i,j,k)
18160 dz_arith = dz(i,j,k)
1817 endif
1818 endif
1819
18200 if (OBC%v_S_OBCs_on_PE) then
18210 if (OBC%segnum_v(i,J) < 0) then
18220 h_harm = h(i,j+1,k)
18230 h_arith = h(i,j+1,k)
18240 h_delta = 0.
18250 dz_harm(k) = dz(i,j+1,k)
18260 dz_arith = dz(i,j+1,k)
1827 endif
1828 endif
1829 endif
1830
183126427600 if (CS%harmonic_visc) then
1832 ! The following block calculates the thicknesses at velocity grid points
1833 ! for the vertical viscosity (hvel and dz_vel). Near the bottom an
1834 ! upwind biased thickness is used to control the effect of spurious
1835 ! Montgomery potential gradients at the bottom where nearly massless
1836 ! layers ride over the topography.
1837
18380 hvel(k) = h_harm
18390 dz_vel(k) = dz_harm(k)
1840
18410 if (v(i,J,k) * h_delta < 0) then
18420 z2 = z_i(k+1)
18430 botfn = 1. / (1. + 0.09 * z2 * z2 * z2 * z2 * z2 * z2)
1844
18450 hvel(k) = (1. - botfn) * h_harm + botfn * h_arith
18460 dz_vel(k) = (1. - botfn) * dz_harm(k) + botfn * dz_arith
1847 endif
1848
18490 z_i(k) = z_i(k+1) + dz_harm(k) * I_Hbbl
1850 else
185126427600 zcol = zcol + dz(i,j,k)
185226427600 zcol_p1 = zcol_p1 + dz(i,j+1,k)
1853
185426427600 zh = zh + dz_harm(k)
1855
185626427600 z_clear = max(zcol, zcol_p1) + Dmin
185726427600 if (zi_dir < 0) z_clear = zcol + Dmin
185826427600 if (zi_dir > 0) z_clear = zcol_p1 + Dmin
1859
186026427600 z_i(k) = max(zh, z_clear) * I_Hbbl
1861
186226427600 hvel(k) = h_arith
186326427600 dz_vel(k) = dz_arith
1864
186526427600 if (v(i,J,k) * h_delta > 0) then
186610711176 if (zh * I_Hbbl < CS%harm_BL_val) then
18670 hvel(k) = h_harm
18680 dz_vel(k) = dz_harm(k)
1869 else
187010711176 z2_wt = 1.
187110711176 if (zh * I_Hbbl < 2. * CS%harm_BL_val) &
18720 z2_wt = max(0., min(1., zh * I_Hbbl * I_valBL - 1.))
1873
1874 ! TODO: should z_clear be used here?
187510711176 z2 = z2_wt * (max(zh, max(zcol, zcol_p1) + Dmin) * I_Hbbl)
187610711176 botfn = 1. / (1. + 0.09 * z2 * z2 * z2 * z2 * z2 * z2)
1877
187810711176 hvel(k) = (1. - botfn) * h_arith + botfn * h_harm
187910711176 dz_vel(k) = (1. - botfn) * dz_arith + botfn * dz_harm(k)
1880 endif
1881 endif
1882 endif
1883
188426779968 if (CS%use_GL90_in_SSW) then
1885 ! The following block calculates the normalized height above the GL90 BBL
1886 ! (z_i_gl90), using a harmonic mean between layer thicknesses. For the
1887 ! GL90 BBL we use simply a constant (Hbbl_gl90). The purpose is that the
1888 ! GL90 coupling coefficient is zeroed out within Hbbl_gl90, to ensure
1889 ! that no momentum gets fluxed into vanished layers. The scheme is not
1890 ! sensitive to the exact value of Hbbl_gl90, as long as it is in a
1891 ! reasonable range (~1-20 m): large enough to capture vanished layers
1892 ! over topography, small enough to not contaminate the interior.
1893
18940 z_i_gl90(k) = z_i_gl90(k+1) + dz_harm(k) * I_Hbbl_gl90
1895 endif
1896 enddo
1897
1898 call find_coupling_coef_k(a_cpl, dz_vel, i, j, dz_harm, bbl_thick, kv_bbl, z_i, &
1899352368 h_ml, dt, G, GV, US, CS, visc, Ustar_2d, tv, work_on_u=.false., OBC=OBC)
1900
1901352368 if (allocated(hML_v)) hML_v(i,J) = h_ml
1902
1903352368 if (CS%use_GL90_in_SSW) then
1904 call find_coupling_coef_gl90(a_cpl_gl90, dz_vel, i, j, z_i_gl90, G, GV, &
19050 CS, VarMix, work_on_u=.false.)
1906 endif
1907
1908352368 do_any_shelf = .false.
1909352368 if (associated(forces%frac_shelf_v)) then
19100 CS%a1_shelf_v(i,J) = 0.
19110 do_any_shelf = forces%frac_shelf_v(i,J) > 0.
1912
19130 if (do_any_shelf) then
1914 ! Initialize non-harmonic depths
19150 if (.not. CS%harmonic_visc) then
19160 zh = 0.
19170 Ztop_min = min(zcol, zcol_p1)
19180 I_HTbl = 1. / (visc%tbl_thick_shelf_v(i,J) + dz_neglect)
1919 endif
1920
19210 do k=1,nz
19220 if (CS%harmonic_visc) then
19230 hvel_shelf(k) = hvel(k)
19240 dz_vel_shelf(k) = dz_vel(k)
1925 else
1926 ! Find upwind-biased thickness near the surface.
1927 ! Perhaps this needs to be done more carefully, via find_eta.
1928 h_harm = 2. * h(i,j,k) * h(i,j+1,k) &
19290 / (h(i,j,k) + h(i,j+1,k) + h_neglect)
19300 h_arith = 0.5 * (h(i,j+1,k) + h(i,j,k))
19310 h_delta = h(i,j+1,k) - h(i,j,k)
19320 dz_arith = 0.5 * (dz(i,j+1,k) + dz(i,j,k))
1933
1934 ! Project thickness outward across OBCs using a zero-gradient condition.
19350 if (associated(OBC)) then
19360 if (OBC%v_N_OBCs_on_PE) then
19370 if (OBC%segnum_v(i,J) > 0) then
19380 h_harm = h(i,j,k)
19390 h_arith = h(i,j,k)
19400 h_delta = 0.
19410 dz_arith = dz(i,j,k)
1942 endif
1943 endif
1944
19450 if (OBC%v_S_OBCs_on_PE) then
19460 if (OBC%segnum_v(i,J) < 0) then
19470 h_harm = h(i,j+1,k)
19480 h_arith = h(i,j+1,k)
19490 h_delta = 0.
19500 dz_arith = dz(i,j+1,k)
1951 endif
1952 endif
1953 endif
1954
19550 zcol = zcol - dz(i,j,k)
19560 zcol_p1 = zcol_p1 - dz(i,j+1,k)
1957
19580 zh = zh + dz_harm(k)
1959
19600 hvel_shelf(k) = hvel(k)
19610 dz_vel_shelf(k) = dz_vel(k)
1962
19630 if (v(i,J,k) * h_delta > 0.) then
19640 if (zh * I_HTbl < CS%harm_BL_val) then
19650 hvel_shelf(k) = min(hvel(k), h_harm)
19660 dz_vel_shelf(k) = min(dz_vel(k), dz_harm(k))
1967 else
19680 z2_wt = 1.
19690 if (zh * I_HTbl < 2. * CS%harm_BL_val) &
19700 z2_wt = max(0., min(1., zh * I_HTbl * I_valBL - 1.))
1971
19720 z2 = z2_wt * (max(zh, Ztop_min - min(zcol, zcol_p1)) * I_HTbl)
1973 ! TODO: Replace **6
19740 topfn = 1. / (1. + 0.09 * z2**6)
1975
19760 hvel_shelf(k) = min(hvel(k), (1. - topfn) * h_arith + topfn * h_harm)
19770 dz_vel_shelf(k) = min(dz_vel(k), (1. - topfn) * dz_arith + topfn * dz_harm(k))
1978 endif
1979 endif
1980 endif
1981 enddo
1982
1983 call find_coupling_coef(a_shelf, dz_vel_shelf, i, j, dz_harm, &
1984 bbl_thick, kv_bbl, z_i, h_ml, dt, G, GV, US, CS, visc, Ustar_2d, &
19850 tv, work_on_u=.false., OBC=OBC, shelf=.true.)
1986
19870 CS%a1_shelf_v(i,J) = a_shelf(1)
1988 endif
1989 endif
1990
1991352368 if (do_any_shelf) then
19920 if (CS%use_GL90_in_SSW) then
19930 do K=1,nz+1
1994 CS%a_v(I,j,K) = min(a_cpl_max, (forces%frac_shelf_v(I,j) * a_shelf(K) + &
19950 (1. - forces%frac_shelf_v(I,j)) * a_cpl(K)) + a_cpl_gl90(K))
1996
1997 ! This is Alistair's suggestion, but it destabilizes the model. I do not know why. RWH
1998 ! CS%a_v(I,j,K) = min(a_cpl_max, forces%frac_shelf_v(I,j) * max(a_shelf(K), a_cpl(K)) + &
1999 ! (1. - forces%frac_shelf_v(I,j)) * a_cpl(K))
2000
20010 CS%a_v_gl90(I,j,K) = min(a_cpl_max, a_cpl_gl90(K))
2002 enddo
2003 else
20040 do K=1,nz+1
2005 CS%a_v(I,j,K) = min(a_cpl_max, (forces%frac_shelf_v(I,j) * a_shelf(K) + &
20060 (1. - forces%frac_shelf_v(I,j)) * a_cpl(K)))
2007 ! This is Alistair's suggestion, but it destabilizes the model. I do not know why. RWH
2008 ! CS%a_v(I,j,K) = min(a_cpl_max, forces%frac_shelf_v(I,j) * max(a_shelf(K), a_cpl(K)) + &
2009 ! (1. - forces%frac_shelf_v(I,j)) * a_cpl(K))
2010 enddo
2011 endif
2012
20130 do k=1,nz
2014 ! Should we instead take the inverse of the average of the inverses?
2015 CS%h_v(I,j,k) = forces%frac_shelf_v(I,j) * hvel_shelf(k) &
20160 + (1. - forces%frac_shelf_v(I,j)) * hvel(k) + h_neglect
2017 enddo
2018 else
2019352368 if (CS%use_GL90_in_SSW) then
20200 do K=1,nz+1
20210 a_cpl(K) = a_cpl(K) + a_cpl_gl90(K)
2022 enddo
2023
20240 do K=1,nz+1
20250 CS%a_v_gl90(i,J,K) = min(a_cpl_max, a_cpl_gl90(K))
2026 enddo
2027 endif
2028
202927132336 do K=1,nz+1
203027132336 CS%a_v(i,J,K) = min(a_cpl_max, a_cpl(K))
2031 enddo
2032
203326779968 do k=1,nz
203426779968 CS%h_v(i,J,k) = hvel(k) + h_neglect
2035 enddo
2036 endif
2037
2038 ! Diagnose total Kv at v-points
2039352368 if (CS%id_Kv_v > 0) then
20400 do k=1,nz
20410 Kv_v(i,J,k) = 0.5 * (CS%a_v(i,J,K) + CS%a_v(i,J,K+1)) * CS%h_v(i,J,k)
2042 enddo
2043 endif
2044
2045 ! Diagnose GL90 Kv at v-points
2046352368 if (CS%id_Kv_gl90_v > 0) then
20470 do k=1,nz
20480 Kv_gl90_v(i,J,k) = 0.5 * (CS%a_v_gl90(i,J,K) + CS%a_v_gl90(i,J,K+1)) * CS%h_v(i,J,k)
2049 enddo
2050 endif
2051 endif ; enddo ; enddo
2052
2053 !$omp target exit data map(delete: z_i, z_i_gl90, dz_harm, hvel, dz_vel, a_cpl, a_cpl_gl90, &
2054 !$omp& tv, varmix, hvel_shelf, dz_vel_shelf, a_shelf, hml_u, kv_u, kv_gl90_u)
2055
2056 ! These are used in diagnostics, so they need to be mapped back and forth
2057 !$omp target exit data map(from: hML_u, kv_u, kv_gl90_u )
2058 !$omp target exit data map(from: hML_v, kv_v, kv_gl90_v)
2059
2060 !$omp target exit data map(delete: Ustar_2d)
2061
206272 if (CS%debug) then
2063 !$omp target update from(CS%h_u, CS%h_v, CS%a_u, CS%a_v)
2064 call uvchksum("loop vertvisc_coef h_[uv]", CS%h_u, CS%h_v, G%HI, haloshift=0, &
20650 unscale=GV%H_to_m, scalar_pair=.true.)
2066 call uvchksum("vertvisc_coef a_[uv]", CS%a_u, CS%a_v, G%HI, haloshift=0, &
20670 unscale=GV%H_to_m*US%s_to_T, scalar_pair=.true.)
20680 if (allocated(hML_u) .and. allocated(hML_v)) &
2069 call uvchksum("vertvisc_coef hML_[uv]", hML_u, hML_v, G%HI, &
20700 haloshift=0, unscale=US%Z_to_m, scalar_pair=.true.)
2071 endif
2072
2073! Offer diagnostic fields for averaging.
207472 if (query_averaging_enabled(CS%diag)) then
207524 if (associated(visc%Kv_slow) .and. (CS%id_Kv_slow > 0)) &
20760 call post_data(CS%id_Kv_slow, visc%Kv_slow, CS%diag)
207724 if (CS%id_Kv_u > 0) call post_data(CS%id_Kv_u, Kv_u, CS%diag)
207824 if (CS%id_Kv_v > 0) call post_data(CS%id_Kv_v, Kv_v, CS%diag)
207924 if (CS%id_Kv_gl90_u > 0) call post_data(CS%id_Kv_gl90_u, Kv_gl90_u, CS%diag)
208024 if (CS%id_Kv_gl90_v > 0) call post_data(CS%id_Kv_gl90_v, Kv_gl90_v, CS%diag)
208124 if (CS%id_au_vv > 0) call post_data(CS%id_au_vv, CS%a_u, CS%diag)
208224 if (CS%id_av_vv > 0) call post_data(CS%id_av_vv, CS%a_v, CS%diag)
208324 if (CS%id_au_gl90_vv > 0) call post_data(CS%id_au_gl90_vv, CS%a_u_gl90, CS%diag)
208424 if (CS%id_av_gl90_vv > 0) call post_data(CS%id_av_gl90_vv, CS%a_v_gl90, CS%diag)
208524 if (CS%id_h_u > 0) call post_data(CS%id_h_u, CS%h_u, CS%diag)
208624 if (CS%id_h_v > 0) call post_data(CS%id_h_v, CS%h_v, CS%diag)
208724 if (CS%id_hML_u > 0) call post_data(CS%id_hML_u, hML_u, CS%diag)
208824 if (CS%id_hML_v > 0) call post_data(CS%id_hML_v, hML_v, CS%diag)
2089 endif
2090
209172 if (allocated(hML_u)) deallocate(hML_u)
209272 if (allocated(hML_v)) deallocate(hML_v)
2093
209472end subroutine vertvisc_coef
2095
2096!> Calculate the 'coupling coefficient' (a_cpl) at the interfaces.
2097!! If BOTTOMDRAGLAW is defined, the minimum of Hbbl and half the adjacent
2098!! layer thicknesses are used to calculate a_cpl near the bottom.
2099708624pure subroutine find_coupling_coef_k(a_cpl, hvel, i, j, h_harm, bbl_thick, kv_bbl, z_i, h_ml, &
2100708624 dt, G, GV, US, CS, visc, Ustar_2d, tv, work_on_u, OBC, shelf)
2101 !$omp declare target
2102 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
2103 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure
2104 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
2105 real, dimension(SZK_(GV)+1), &
2106 intent(out) :: a_cpl !< Coupling coefficient across interfaces [H T-1 ~> m s-1 or Pa s m-1]
2107 real, dimension(SZK_(GV)), &
2108 intent(in) :: hvel !< Distance between interfaces at velocity points [Z ~> m]
2109 integer, intent(in) :: i !< Column i-index
2110 integer, intent(in) :: j !< Column j-index
2111 real, dimension(SZK_(GV)), &
2112 intent(in) :: h_harm !< Harmonic mean of thicknesses around a velocity
2113 !! grid point [Z ~> m]
2114 real, intent(in) :: bbl_thick !< Bottom boundary layer thickness [Z ~> m]
2115 real, intent(in) :: kv_bbl !< Bottom boundary layer viscosity, exclusive of
2116 !! any depth-dependent contributions from
2117 !! visc%Kv_shear [H Z T-1 ~> m2 s-1 or Pa s]
2118 real, dimension(SZK_(GV)+1), intent(in) :: z_i !< Estimate of interface heights above the bottom,
2119 !! normalized by the bottom boundary layer thickness [nondim]
2120 real, intent(out) :: h_ml !< Mixed layer depth [Z ~> m]
2121 real, intent(in) :: dt !< Time increment [T ~> s]
2122 type(vertvisc_CS), intent(in) :: CS !< Vertical viscosity control structure
2123 type(vertvisc_type), intent(in) :: visc !< Structure containing viscosities and bottom drag
2124 real, dimension(SZI_(G),SZJ_(G)), &
2125 intent(in) :: Ustar_2d !< The wind friction velocity, calculated using
2126 !! the Boussinesq reference density or the
2127 !! time-evolving surface density in non-Boussinesq
2128 !! mode [Z T-1 ~> m s-1]
2129 type(thermo_var_ptrs), intent(in) :: tv !< A structure containing pointers to any available
2130 !! thermodynamic fields.
2131 logical, intent(in) :: work_on_u !< If true, u-points are being calculated,
2132 !! otherwise they are v-points
2133 type(ocean_OBC_type), pointer :: OBC !< Open boundary condition structure
2134 logical, optional, intent(in) :: shelf !< If present and true, use a surface boundary
2135 !! condition appropriate for an ice shelf.
2136
2137 real :: u_star ! ustar at a velocity point [Z T-1 ~> m s-1]
2138 real :: tau_mag ! The magnitude of the wind stress at a velocity point including gustiness [H Z T-2 ~> m2 s-2 or Pa]
2139 real :: absf ! The average of the neighboring absolute values of f [T-1 ~> s-1].
2140 real :: rho_av1 ! The harmonic mean surface layer density at velocity points [R ~> kg m-3]
2141 real :: z_t ! The distance from the top, sometimes normalized
2142 ! by Hmix, [Z ~> m] or [nondim].
2143 real :: kv_TBL ! The viscosity in a top boundary layer under ice [H Z T-1 ~> m2 s-1 or Pa s]
2144 real :: tbl_thick ! The thickness of the top boundary layer [Z ~> m]
2145 real :: Kv_add ! A viscosity to add [H Z T-1 ~> m2 s-1 or Pa s]
2146 real :: Kv_tot ! The total viscosity at an interface [H Z T-1 ~> m2 s-1 or Pa s]
2147 integer :: nk_in_ml ! The index of the deepest interface in the mixed layer.
2148 real :: h_shear ! The distance over which shears occur [Z ~> m].
2149 real :: dhc ! The distance between the center of adjacent layers [Z ~> m].
2150 real :: visc_ml ! The mixed layer viscosity [H Z T-1 ~> m2 s-1 or Pa s].
2151 real :: I_Hmix ! The inverse of the mixed layer thickness [Z-1 ~> m-1].
2152 real :: a_ml ! The layer coupling coefficient across an interface in
2153 ! the mixed layer [H T-1 ~> m s-1 or Pa s m-1].
2154 real :: a_floor ! A lower bound on the layer coupling coefficient across an interface in
2155 ! the mixed layer [H T-1 ~> m s-1 or Pa s m-1].
2156 real :: I_amax ! The inverse of the maximum coupling coefficient [T H-1 ~> s m-1 or s m2 kg-1].
2157 real :: temp1 ! A temporary variable [Z2 ~> m2]
2158 real :: ustar2_denom ! A temporary variable in the surface boundary layer turbulence
2159 ! calculations [H Z-1 T-1 ~> s-1 or kg m-3 s-1]
2160 real :: h_neglect ! A vertical distance that is so small it is usually lost
2161 ! in roundoff and can be neglected [Z ~> m].
2162 real :: z2 ! A copy of z_i [nondim]
2163 real :: botfn ! A function that is 1 at the bottom and small far from it [nondim]
2164 real :: topfn ! A function that is 1 at the top and small far from it [nondim]
2165 real :: kv_top ! A viscosity associated with the top boundary layer [H Z T-1 ~> m2 s-1 or Pa s]
2166 logical :: do_shelf, do_OBCs, can_exit
2167 integer :: k
2168 integer :: nz, max_nk
2169
2170708624 nz = GV%ke
2171
2172708624 h_neglect = GV%dZ_subroundoff
2173
2174708624 if (CS%answer_date < 20190101) then
2175 ! The maximum coupling coefficient was originally introduced to avoid
2176 ! truncation error problems in the tridiagonal solver. Effectively, the 1e-10
2177 ! sets the maximum coupling coefficient increment to 1e10 m per timestep.
21780 I_amax = (1.0e-10*GV%H_to_m) * dt
2179 else
2180708624 I_amax = 0.0
2181 endif
2182
2183708624 do_shelf = .false. ; if (present(shelf)) do_shelf = shelf
2184
2185708624 do_OBCs = .false.
2186 !if (associated(OBC)) then
2187 ! if (work_on_u) then
2188 ! do_OBCS = OBC%u_E_OBCs_on_PE .or. OBC%u_W_OBCs_on_PE
2189 ! else
2190 ! do_OBCS = OBC%v_N_OBCs_on_PE .or. OBC%v_S_OBCs_on_PE
2191 ! endif
2192 !endif
2193
219454564048 a_cpl(:) = 0.
2195708624 h_ml = 0.
2196
2197708624 if (CS%Kvml_invZ2 > 0. .and. .not. do_shelf) then
21980 I_Hmix = 1. / (CS%Hmix + h_neglect)
21990 z_t = h_neglect * I_Hmix
2200 endif
2201
220253146800 do K=2,nz
220352438176 Kv_tot = CS%Kv
2204
220552438176 if (CS%Kvml_invZ2 > 0. .and. .not. do_shelf) then
2206 ! This is an older (vintage ~1997) way to prevent wind stresses from driving very
2207 ! large flows in nearly massless near-surface layers when there is not a physically-
2208 ! based surface boundary layer parameterization. It does not have a plausible
2209 ! physical basis, and probably should not be used.
22100 z_t = z_t + h_harm(k-1) * I_Hmix
2211 Kv_tot = CS%Kv + CS%Kvml_invZ2 / ((z_t * z_t) * &
22120 (1. + 0.09 * z_t * z_t * z_t * z_t * z_t * z_t))
2213 endif
2214
221552438176 if (associated(visc%Kv_shear)) then
2216 ! Add in viscosities that are determined by physical processes that are handled in
2217 ! other modules, and which do not respond immediately to the changing layer thicknesses.
2218 ! These processes may include shear-driven mixing or contributions from some boundary
2219 ! layer turbulence schemes. Other viscosity contributions that respond to the evolving
2220 ! layer thicknesses or the surface wind stresses are added later.
222152438176 if (work_on_u) then
222226362944 Kv_add = 0.5 * (visc%Kv_shear(i,j,k) + visc%Kv_shear(i+1,j,k))
2223
222426362944 if (do_OBCs) then
22250 if (OBC%u_E_OBCs_on_PE) then
22260 if (OBC%segnum_u(I,j) > 0) then
22270 Kv_add = visc%Kv_shear(i,j,k)
2228 endif
2229 endif
2230
22310 if (OBC%u_W_OBCs_on_PE) then
22320 if (OBC%segnum_u(I,j) < 0) then
22330 Kv_add = visc%Kv_shear(i+1,j,k)
2234 endif
2235 endif
2236 endif
2237
223826362944 Kv_tot = Kv_tot + Kv_add
2239 else
224026075232 Kv_add = 0.5 * (visc%Kv_shear(i,j,k) + visc%Kv_shear(i,j+1,k))
2241
224226075232 if (do_OBCs) then
22430 if (OBC%v_N_OBCs_on_PE) then
22440 if (OBC%segnum_v(i,J) > 0) then
22450 Kv_add = visc%Kv_shear(i,j,k)
2246 endif
2247 endif
2248
22490 if (OBC%v_S_OBCs_on_PE) then
22500 if (OBC%segnum_v(i,J) < 0) then
22510 Kv_add = visc%Kv_shear(i,j+1,k)
2252 endif
2253 endif
2254 endif
2255
225626075232 Kv_tot = Kv_tot + Kv_add
2257 endif
2258 endif
2259
226052438176 if (associated(visc%Kv_shear_Bu)) then
2261 ! This is similar to what was done above, but for contributions coming from the corner
2262 ! (vorticity) points. Because OBCs run through the faces and corners there is no need
2263 ! to further modify these viscosities here to take OBCs into account.
226452438176 if (work_on_u) then
226526362944 Kv_tot = Kv_tot + 0.5 * (visc%Kv_shear_Bu(I,J-1,k) + visc%Kv_shear_Bu(I,J,k))
2266 else
226726075232 Kv_tot = Kv_tot + 0.5 * (visc%Kv_shear_Bu(I-1,J,k) + visc%Kv_shear_Bu(I,J,k))
2268 endif
2269 endif
2270
2271 ! Set the viscous coupling coefficients, excluding surface mixed layer contributions
2272 ! for now, but including viscous bottom drag, working up from the bottom.
227353146800 if (CS%bottomdraglaw) then
2274 ! botfn determines when a point is within the influence of the bottom
2275 ! boundary layer, going from 1 at the bottom to 0 in the interior.
227652438176 z2 = z_i(k)
227752438176 botfn = 1. / (1. + 0.09 * z2 * z2 * z2 * z2 * z2 * z2)
2278
227952438176 Kv_tot = Kv_tot + (kv_bbl - CS%Kv) * botfn
228052438176 dhc = 0.5 * (hvel(k) + hvel(k-1))
228152438176 if (dhc > bbl_thick) then
228236470640 h_shear = ((1. - botfn) * dhc + botfn * bbl_thick) + h_neglect
2283 else
228415967536 h_shear = dhc + h_neglect
2285 endif
2286
2287 ! Calculate the coupling coefficients from the viscosities.
228852438176 a_cpl(K) = Kv_tot / (h_shear + (I_amax * Kv_tot))
22890 elseif (abs(CS%Kv_extra_bbl) > 0.0) then
2290 ! There is a simple enhancement of the near-bottom viscosities, but no
2291 ! adjustment of the viscous coupling length scales to give a particular
2292 ! bottom stress.
2293
2294 ! botfn determines when a point is within the influence of the bottom
2295 ! boundary layer, going from 1 at the bottom to 0 in the interior.
22960 z2 = z_i(k)
22970 botfn = 1. / (1. + 0.09 * z2 * z2 * z2 * z2 * z2 * z2)
2298
22990 Kv_tot = Kv_tot + CS%Kv_extra_bbl * botfn
23000 h_shear = 0.5 * (hvel(k) + hvel(k-1) + h_neglect)
2301
2302 ! Calculate the coupling coefficients from the viscosities.
23030 a_cpl(K) = Kv_tot / (h_shear + I_amax * Kv_tot)
2304 else
2305 ! Any near-bottom viscous enhancements were already incorporated into
2306 ! Kv_tot, and there is no adjustment of the viscous coupling length
2307 ! scales to give a particular bottom stress.
2308
23090 h_shear = 0.5 * (hvel(k) + hvel(k-1) + h_neglect)
2310 ! Calculate the coupling coefficients from the viscosities.
23110 a_cpl(K) = Kv_tot / (h_shear + I_amax * Kv_tot)
2312 endif
2313 enddo
2314
2315 ! Assign the bottom coupling coefficients
2316708624 if (CS%bottomdraglaw) then
2317708624 dhc = hvel(nz) * 0.5
2318708624 a_cpl(nz+1) = kv_bbl / ((min(dhc, bbl_thick) + h_neglect) + I_amax * kv_bbl)
23190 elseif (abs(CS%Kv_extra_bbl) > 0.0) then
2320 a_cpl(nz+1) = (CS%Kv + CS%Kv_extra_bbl) &
23210 / ((0.5 * hvel(nz) + h_neglect) + I_amax * (CS%Kv + CS%Kv_extra_bbl))
2322 else
23230 a_cpl(nz+1) = CS%Kv / ((0.5 * hvel(nz) + h_neglect) + I_amax * CS%Kv)
2324 endif
2325
2326 ! Add surface intensified viscous coupling, either as a no-slip boundary condition under a
2327 ! rigid ice-shelf, or due to wind-stress driven surface boundary layer mixing that has not
2328 ! already been added via visc%Kv_shear.
2329708624 if (do_shelf) then
2330 ! Set the coefficients to include the no-slip surface stress.
23310 if (work_on_u) then
23320 kv_TBL = visc%Kv_tbl_shelf_u(I,j)
23330 tbl_thick = visc%tbl_thick_shelf_u(I,j) + h_neglect
2334 else
23350 kv_TBL = visc%Kv_tbl_shelf_v(i,J)
23360 tbl_thick = visc%tbl_thick_shelf_v(i,J) + h_neglect
2337 endif
23380 z_t = 0.0
2339
2340 ! If a_cpl(1) were not already 0, it would be added here.
23410 if (0.5 * hvel(1) > tbl_thick) then
23420 a_cpl(1) = kv_TBL / (tbl_thick + I_amax * kv_TBL)
2343 else
23440 a_cpl(1) = kv_TBL / ((0.5 * hvel(1) + h_neglect) + I_amax * kv_TBL)
2345 endif
2346
23470 do K=2,nz
23480 z_t = z_t + hvel(k-1) / tbl_thick
23490 topfn = 1. / (1. + 0.09 * z_t**6)
2350
23510 dhc = 0.5 * (hvel(k) + hvel(k-1))
23520 if (dhc > tbl_thick) then
23530 h_shear = ((1. - topfn) * dhc + topfn * tbl_thick) + h_neglect
2354 else
23550 h_shear = dhc + h_neglect
2356 endif
2357
23580 kv_top = topfn * kv_TBL
23590 a_cpl(K) = a_cpl(K) + kv_top / (h_shear + I_amax * kv_top)
2360 enddo
2361708624 elseif (CS%dynamic_viscous_ML .or. (GV%nkml>0) .or. CS%fixed_LOTW_ML .or. CS%apply_LOTW_floor) then
2362
2363 ! Find the friction velocity and the absolute value of the Coriolis parameter at this point.
2364 ! Zero out the friction velocity on land points.
23650 u_star = 0.
23660 tau_mag = 0.
2367
23680 if (allocated(tv%SpV_avg)) then
23690 rho_av1 = 0.
2370
23710 if (work_on_u) then
23720 u_star = 0.5 * (Ustar_2d(i,j) + Ustar_2d(i+1,j))
23730 rho_av1 = 2. / (tv%SpV_avg(i,j,1) + tv%SpV_avg(i+1,j,1))
23740 absf = 0.5 * (abs(G%CoriolisBu(I,J-1)) + abs(G%CoriolisBu(I,J)))
2375
23760 if (do_OBCs) then
23770 if (OBC%u_E_OBCs_on_PE) then
23780 if (OBC%segnum_u(I,j) > 0) then
23790 u_star = Ustar_2d(i,j)
23800 rho_av1 = 1. / tv%SpV_avg(i,j,1)
2381 endif
2382 endif
2383
23840 if (OBC%u_W_OBCs_on_PE) then
23850 if (OBC%segnum_u(I,j) < 0) then
23860 u_star = Ustar_2d(i+1,j)
23870 rho_av1 = 1. / tv%SpV_avg(i+1,j,1)
2388 endif
2389 endif
2390 endif
2391 else
23920 u_star = 0.5 * (Ustar_2d(i,j) + Ustar_2d(i,j+1))
23930 rho_av1 = 2. / (tv%SpV_avg(i,j,1) + tv%SpV_avg(i,j+1,1))
23940 absf = 0.5 * (abs(G%CoriolisBu(I-1,J)) + abs(G%CoriolisBu(I,J)))
2395
23960 if (do_OBCs) then
23970 if (OBC%v_N_OBCs_on_PE) then
23980 if (OBC%segnum_v(i,J) > 0) then
23990 u_star = Ustar_2d(i,j)
24000 rho_av1 = 1. / tv%SpV_avg(i,j,1)
2401 endif
2402 endif
2403
24040 if (OBC%v_S_OBCs_on_PE) then
24050 if (OBC%segnum_v(i,J) < 0) then
24060 u_star = Ustar_2d(i,j+1)
24070 rho_av1 = 1. / tv%SpV_avg(i,j+1,1)
2408 endif
2409 endif
2410 endif
2411 endif
2412
24130 tau_mag = GV%RZ_to_H * rho_av1 * u_star**2
2414 else ! (.not.allocated(tv%SpV_avg))
24150 if (work_on_u) then
24160 u_star = 0.5 * (Ustar_2d(i,j) + Ustar_2d(i+1,j))
24170 absf = 0.5 * (abs(G%CoriolisBu(I,J-1)) + abs(G%CoriolisBu(I,J)))
2418
24190 if (do_OBCs) then
24200 if (OBC%u_E_OBCs_on_PE) then
24210 if (OBC%segnum_u(I,j) > 0) then
24220 u_star = Ustar_2d(i,j)
2423 endif
2424 endif
2425
24260 if (OBC%u_W_OBCs_on_PE) then
24270 if (OBC%segnum_u(I,j) < 0) then
24280 u_star = Ustar_2d(i+1,j)
2429 endif
2430 endif
2431 endif
2432 else
24330 u_star = 0.5 * (Ustar_2d(i,j) + Ustar_2d(i,j+1))
24340 absf = 0.5 * (abs(G%CoriolisBu(I-1,J)) + abs(G%CoriolisBu(I,J)))
2435
24360 if (do_OBCs) then
24370 if (OBC%v_N_OBCs_on_PE) then
24380 if (OBC%segnum_v(i,J) > 0) then
24390 u_star = Ustar_2d(i,j)
2440 endif
2441 endif
2442
24430 if (OBC%v_S_OBCs_on_PE) then
24440 if (OBC%segnum_v(i,J) < 0) then
24450 u_star = Ustar_2d(i,j+1)
2446 endif
2447 endif
2448 endif
2449 endif
2450
24510 tau_mag = GV%Z_to_H * u_star**2
2452 endif
2453
2454 ! Determine the thickness of the surface ocean boundary layer and its extent in index space.
24550 nk_in_ml = 0
2456
24570 if (CS%dynamic_viscous_ML) then
2458 ! The fractional number of layers that are within the viscous boundary layer were
2459 ! previously stored in visc%nkml_visc_[uv].
24600 h_ml = h_neglect
24610 max_nk = 0
2462
24630 if (work_on_u) then
24640 nk_in_ml = ceiling(visc%nkml_visc_u(I,j))
24650 max_nk = max(max_nk, nk_in_ml)
2466
24670 do k=1,max_nk
24680 if (k <= visc%nkml_visc_u(I,j)) then ! This layer is all in the ML.
24690 h_ml = h_ml + hvel(k)
24700 elseif (k < visc%nkml_visc_u(I,j) + 1.) then ! Part of this layer is in the ML.
24710 h_ml = h_ml + ((visc%nkml_visc_u(I,j) + 1.) - k) * hvel(k)
2472 endif
2473 enddo
2474 else
24750 nk_in_ml = ceiling(visc%nkml_visc_v(i,J))
24760 max_nk = max(max_nk, nk_in_ml)
2477
24780 do k=1,max_nk
24790 if (k <= visc%nkml_visc_v(i,J)) then ! This layer is all in the ML.
24800 h_ml = h_ml + hvel(k)
24810 elseif (k < visc%nkml_visc_v(i,J) + 1.) then ! Part of this layer is in the ML.
24820 h_ml = h_ml + ((visc%nkml_visc_v(i,J) + 1.) - k) * hvel(k)
2483 endif
2484 enddo
2485 endif
24860 elseif (GV%nkml>0) then
2487 ! This is a simple application of a refined-bulk mixed layer with GV%nkml sublayers.
24880 max_nk = GV%nkml
24890 nk_in_ml = GV%nkml
2490
24910 h_ml = h_neglect
2492
24930 do k=1,GV%nkml
24940 h_ml = h_ml + hvel(k)
2495 enddo
24960 elseif (CS%fixed_LOTW_ML .or. CS%apply_LOTW_floor) then
2497 ! Determine which interfaces are within CS%Hmix of the surface, and set the viscous
2498 ! boundary layer thickness to the the smaller of CS%Hmix and the depth of the ocean.
24990 h_ml = 0.0
25000 do k=1,nz
25010 can_exit = .true.
25020 if (h_ml < CS%Hmix) then
25030 nk_in_ml = k
2504
25050 if (h_ml + hvel(k) < CS%Hmix) then
25060 h_ml = h_ml + hvel(k)
25070 can_exit = .false. ! Part of the next deeper layer is also in the mixed layer.
2508 else
25090 h_ml = CS%Hmix
2510 endif
2511 endif
2512
25130 if (can_exit) exit ! All remaining layers in this row are below the mixed layer depth.
2514 enddo
2515
2516 ! This made more sense in the slab/layer solvers...
25170 max_nk = 0
25180 max_nk = max(max_nk, nk_in_ml)
2519 endif
2520
2521 ! Avoid working on land or on columns where the viscous coupling could not be increased.
25220 if (u_star <= 0.) nk_in_ml = 0
2523
2524 ! Set the viscous coupling at the interfaces as the larger of what was previously
2525 ! set and the contributions from the surface boundary layer.
25260 z_t = 0.0
25270 if (CS%apply_LOTW_floor .and. &
2528 (CS%dynamic_viscous_ML .or. GV%nkml > 0 .or. CS%fixed_LOTW_ML)) then
25290 do K=2,max_nk
25300 if (k <= nk_in_ml) then
25310 z_t = z_t + hvel(k-1)
2532
2533 ! The viscosity in visc_ml is set to go to 0 at the mixed layer top and bottom
2534 ! (in a log-layer) and be further limited by rotation to give the natural Ekman length.
25350 temp1 = (z_t * h_ml - z_t * z_t)
25360 if (GV%Boussinesq) then
2537 ustar2_denom = (CS%vonKar * GV%Z_to_H * u_star**2) &
25380 / (absf * temp1 + (h_ml + h_neglect) * u_star)
2539 else
2540 ustar2_denom = (CS%vonKar * tau_mag) &
25410 / (absf * temp1 + (h_ml + h_neglect) * u_star)
2542 endif
2543
25440 visc_ml = temp1 * ustar2_denom
2545 ! Set the viscous coupling based on the model's vertical resolution. The omission of
2546 ! the I_amax factor here is consistent with answer dates above 20190101.
25470 a_ml = visc_ml / (0.25 * (hvel(k) + hvel(k-1) + h_neglect))
2548
2549 ! As a floor on the viscous coupling, assume that the length scale in the denominator can
2550 ! not be larger than the distance from the surface, consistent with a logarithmic velocity
2551 ! profile. This is consistent with visc_ml, but cancels out common factors of z_t.
25520 a_floor = (h_ml - z_t) * ustar2_denom
2553
2554 ! Choose the largest estimate of a_cpl.
25550 a_cpl(K) = max(a_cpl(K), a_ml, a_floor)
2556 ! An option could be added to change this to: a_cpl(i,K) = max(a_cpl(i,K) + a_ml, a_floor)
2557 endif
2558 enddo
25590 elseif (CS%apply_LOTW_floor) then
25600 do K=2,max_nk
25610 if (k <= nk_in_ml) then
25620 z_t = z_t + hvel(k-1)
2563
25640 temp1 = (z_t * h_ml - z_t * z_t)
25650 if (GV%Boussinesq) then
2566 ustar2_denom = (CS%vonKar * GV%Z_to_H * u_star**2) &
25670 / (absf * temp1 + (h_ml + h_neglect) * u_star)
2568 else
2569 ustar2_denom = (CS%vonKar * tau_mag) &
25700 / (absf * temp1 + (h_ml + h_neglect) * u_star)
2571 endif
2572
2573 ! As a floor on the viscous coupling, assume that the length scale in the denominator can not
2574 ! be larger than the distance from the surface, consistent with a logarithmic velocity profile.
25750 a_cpl(K) = max(a_cpl(K), (h_ml - z_t) * ustar2_denom)
2576 endif
2577 enddo
2578 else
25790 do K=2,max_nk
25800 if (k <= nk_in_ml) then
25810 z_t = z_t + hvel(k-1)
2582
25830 temp1 = (z_t * h_ml - z_t * z_t)
2584 ! This viscosity is set to go to 0 at the mixed layer top and bottom (in a log-layer)
2585 ! and be further limited by rotation to give the natural Ekman length.
2586 ! The following expressions are mathematically equivalent.
25870 if (GV%Boussinesq .or. (CS%answer_date < 20230601)) then
2588 visc_ml = u_star * CS%vonKar * (GV%Z_to_H * temp1 * u_star) &
25890 / (absf * temp1 + (h_ml + h_neglect) * u_star)
2590 else
2591 visc_ml = CS%vonKar * (temp1 * tau_mag) &
25920 / (absf * temp1 + (h_ml + h_neglect) * u_star)
2593 endif
25940 a_ml = visc_ml / (0.25 * (hvel(k) + hvel(k-1) + h_neglect) + 0.5 * I_amax * visc_ml)
2595
2596 ! Choose the largest estimate of a_cpl, but these could be changed to be additive.
25970 a_cpl(K) = max(a_cpl(K), a_ml)
2598 ! An option could be added to change this to: a_cpl(i,K) = a_cpl(i,K) + a_ml
2599 endif
2600 enddo
2601 endif
2602 endif
2603708624end subroutine find_coupling_coef_k
2604
2605
2606!> Calculate the 'coupling coefficient' (a_cpl) at the interfaces.
2607!! If BOTTOMDRAGLAW is defined, the minimum of Hbbl and half the adjacent
2608!! layer thicknesses are used to calculate a_cpl near the bottom.
26090subroutine find_coupling_coef(a_cpl, hvel, i, j, h_harm, bbl_thick, kv_bbl, z_i, h_ml, &
26100 dt, G, GV, US, CS, visc, Ustar_2d, tv, work_on_u, OBC, shelf)
2611 !$omp declare target
2612 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
2613 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure
2614 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
2615 real, dimension(SZK_(GV)+1), &
2616 intent(out) :: a_cpl !< Coupling coefficient across interfaces [H T-1 ~> m s-1 or Pa s m-1]
2617 real, dimension(SZK_(GV)), &
2618 intent(in) :: hvel !< Distance between interfaces at velocity points [Z ~> m]
2619 integer, intent(in) :: i !< Column i-index
2620 integer, intent(in) :: j !< Column j-index
2621 real, dimension(SZK_(GV)), &
2622 intent(in) :: h_harm !< Harmonic mean of thicknesses around a velocity
2623 !! grid point [Z ~> m]
2624 real, intent(in) :: bbl_thick !< Bottom boundary layer thickness [Z ~> m]
2625 real, intent(in) :: kv_bbl !< Bottom boundary layer viscosity, exclusive of
2626 !! any depth-dependent contributions from
2627 !! visc%Kv_shear [H Z T-1 ~> m2 s-1 or Pa s]
2628 real, dimension(SZK_(GV)+1), &
2629 intent(in) :: z_i !< Estimate of interface heights above the bottom,
2630 !! normalized by the bottom boundary layer thickness [nondim]
2631 real, intent(out) :: h_ml !< Mixed layer depth [Z ~> m]
2632 real, intent(in) :: dt !< Time increment [T ~> s]
2633 type(vertvisc_CS), intent(in) :: CS !< Vertical viscosity control structure
2634 type(vertvisc_type), intent(in) :: visc !< Structure containing viscosities and bottom drag
2635 real, dimension(SZI_(G),SZJ_(G)), &
2636 intent(in) :: Ustar_2d !< The wind friction velocity, calculated using
2637 !! the Boussinesq reference density or the
2638 !! time-evolving surface density in non-Boussinesq
2639 !! mode [Z T-1 ~> m s-1]
2640 type(thermo_var_ptrs), intent(in) :: tv !< A structure containing pointers to any available
2641 !! thermodynamic fields.
2642 logical, intent(in) :: work_on_u !< If true, u-points are being calculated,
2643 !! otherwise they are v-points
2644 type(ocean_OBC_type), pointer :: OBC !< Open boundary condition structure
2645 logical, optional, intent(in) :: shelf !< If present and true, use a surface boundary
2646 !! condition appropriate for an ice shelf.
2647
2648 ! Local variables
2649
2650 real :: &
2651 u_star, & ! ustar at a velocity point [Z T-1 ~> m s-1]
2652 tau_mag, & ! The magnitude of the wind stress at a velocity point including gustiness [H Z T-2 ~> m2 s-2 or Pa]
2653 absf, & ! The average of the neighboring absolute values of f [T-1 ~> s-1].
2654 rho_av1, & ! The harmonic mean surface layer density at velocity points [R ~> kg m-3]
2655 z_t, & ! The distance from the top, sometimes normalized
2656 ! by Hmix, [Z ~> m] or [nondim].
2657 kv_TBL, & ! The viscosity in a top boundary layer under ice [H Z T-1 ~> m2 s-1 or Pa s]
2658 tbl_thick, &! The thickness of the top boundary layer [Z ~> m]
2659 Kv_add, & ! A viscosity to add [H Z T-1 ~> m2 s-1 or Pa s]
2660 Kv_tot ! The total viscosity at an interface [H Z T-1 ~> m2 s-1 or Pa s]
2661 integer :: &
2662 nk_in_ml ! The index of the deepest interface in the mixed layer.
2663 real :: h_shear ! The distance over which shears occur [Z ~> m].
2664 real :: dhc ! The distance between the center of adjacent layers [Z ~> m].
2665 real :: visc_ml ! The mixed layer viscosity [H Z T-1 ~> m2 s-1 or Pa s].
2666 real :: I_Hmix ! The inverse of the mixed layer thickness [Z-1 ~> m-1].
2667 real :: a_ml ! The layer coupling coefficient across an interface in
2668 ! the mixed layer [H T-1 ~> m s-1 or Pa s m-1].
2669 real :: a_floor ! A lower bound on the layer coupling coefficient across an interface in
2670 ! the mixed layer [H T-1 ~> m s-1 or Pa s m-1].
2671 real :: I_amax ! The inverse of the maximum coupling coefficient [T H-1 ~> s m-1 or s m2 kg-1].
2672 real :: temp1 ! A temporary variable [Z2 ~> m2]
2673 real :: ustar2_denom ! A temporary variable in the surface boundary layer turbulence
2674 ! calculations [H Z-1 T-1 ~> s-1 or kg m-3 s-1]
2675 real :: h_neglect ! A vertical distance that is so small it is usually lost
2676 ! in roundoff and can be neglected [Z ~> m].
2677 real :: z2 ! A copy of z_i [nondim]
2678 real :: botfn ! A function that is 1 at the bottom and small far from it [nondim]
2679 real :: topfn ! A function that is 1 at the top and small far from it [nondim]
2680 real :: kv_top ! A viscosity associated with the top boundary layer [H Z T-1 ~> m2 s-1 or Pa s]
2681 logical :: do_shelf, do_OBCs, can_exit
2682 integer :: k
2683 integer :: nz, max_nk
2684
26850 nz = GV%ke
2686
26870 h_neglect = GV%dZ_subroundoff
2688
26890 if (CS%answer_date < 20190101) then
2690 ! The maximum coupling coefficient was originally introduced to avoid
2691 ! truncation error problems in the tridiagonal solver. Effectively, the 1e-10
2692 ! sets the maximum coupling coefficient increment to 1e10 m per timestep.
26930 I_amax = (1.0e-10*GV%H_to_m) * dt
2694 else
26950 I_amax = 0.0
2696 endif
2697
26980 do_shelf = .false. ; if (present(shelf)) do_shelf = shelf
2699
27000 do_OBCs = .false.
27010 if (associated(OBC)) then
27020 if (work_on_u) then
27030 do_OBCS = OBC%u_E_OBCs_on_PE .or. OBC%u_W_OBCs_on_PE
2704 else
27050 do_OBCS = OBC%v_N_OBCs_on_PE .or. OBC%v_S_OBCs_on_PE
2706 endif
2707 endif
2708
27090 a_cpl(:) = 0.
27100 h_ml = 0.
2711
27120 if (CS%Kvml_invZ2 > 0. .and. .not. do_shelf) then
27130 I_Hmix = 1. / (CS%Hmix + h_neglect)
27140 z_t = h_neglect * I_Hmix
2715 endif
2716
27170 do K=2,nz
27180 Kv_tot = CS%Kv
2719
27200 if (CS%Kvml_invZ2 > 0. .and. .not. do_shelf) then
2721 ! This is an older (vintage ~1997) way to prevent wind stresses from driving very
2722 ! large flows in nearly massless near-surface layers when there is not a physically-
2723 ! based surface boundary layer parameterization. It does not have a plausible
2724 ! physical basis, and probably should not be used.
27250 z_t = z_t + h_harm(k-1) * I_Hmix
2726 Kv_tot = CS%Kv + CS%Kvml_invZ2 / ((z_t * z_t) * &
27270 (1. + 0.09 * z_t * z_t * z_t * z_t * z_t * z_t))
2728 endif
2729
27300 if (associated(visc%Kv_shear)) then
2731 ! Add in viscosities that are determined by physical processes that are handled in
2732 ! other modules, and which do not respond immediately to the changing layer thicknesses.
2733 ! These processes may include shear-driven mixing or contributions from some boundary
2734 ! layer turbulence schemes. Other viscosity contributions that respond to the evolving
2735 ! layer thicknesses or the surface wind stresses are added later.
27360 if (work_on_u) then
27370 Kv_add = 0.5 * (visc%Kv_shear(i,j,k) + visc%Kv_shear(i+1,j,k))
2738
27390 if (do_OBCs) then
27400 if (OBC%u_E_OBCs_on_PE) then
27410 if (OBC%segnum_u(I,j) > 0) then
27420 Kv_add = visc%Kv_shear(i,j,k)
2743 endif
2744 endif
2745
27460 if (OBC%u_W_OBCs_on_PE) then
27470 if (OBC%segnum_u(I,j) < 0) then
27480 Kv_add = visc%Kv_shear(i+1,j,k)
2749 endif
2750 endif
2751 endif
2752
27530 Kv_tot = Kv_tot + Kv_add
2754 else
27550 Kv_add = 0.5 * (visc%Kv_shear(i,j,k) + visc%Kv_shear(i,j+1,k))
2756
27570 if (do_OBCs) then
27580 if (OBC%v_N_OBCs_on_PE) then
27590 if (OBC%segnum_v(i,J) > 0) then
27600 Kv_add = visc%Kv_shear(i,j,k)
2761 endif
2762 endif
2763
27640 if (OBC%v_S_OBCs_on_PE) then
27650 if (OBC%segnum_v(i,J) < 0) then
27660 Kv_add = visc%Kv_shear(i,j+1,k)
2767 endif
2768 endif
2769 endif
2770
27710 Kv_tot = Kv_tot + Kv_add
2772 endif
2773 endif
2774
27750 if (associated(visc%Kv_shear_Bu)) then
2776 ! This is similar to what was done above, but for contributions coming from the corner
2777 ! (vorticity) points. Because OBCs run through the faces and corners there is no need
2778 ! to further modify these viscosities here to take OBCs into account.
27790 if (work_on_u) then
27800 Kv_tot = Kv_tot + 0.5 * (visc%Kv_shear_Bu(I,J-1,k) + visc%Kv_shear_Bu(I,J,k))
2781 else
27820 Kv_tot = Kv_tot + 0.5 * (visc%Kv_shear_Bu(I-1,J,k) + visc%Kv_shear_Bu(I,J,k))
2783 endif
2784 endif
2785
2786 ! Set the viscous coupling coefficients, excluding surface mixed layer contributions
2787 ! for now, but including viscous bottom drag, working up from the bottom.
27880 if (CS%bottomdraglaw) then
2789 ! botfn determines when a point is within the influence of the bottom
2790 ! boundary layer, going from 1 at the bottom to 0 in the interior.
27910 z2 = z_i(k)
27920 botfn = 1. / (1. + 0.09 * z2 * z2 * z2 * z2 * z2 * z2)
2793
27940 Kv_tot = Kv_tot + (kv_bbl - CS%Kv) * botfn
27950 dhc = 0.5 * (hvel(k) + hvel(k-1))
27960 if (dhc > bbl_thick) then
27970 h_shear = ((1. - botfn) * dhc + botfn * bbl_thick) + h_neglect
2798 else
27990 h_shear = dhc + h_neglect
2800 endif
2801
2802 ! Calculate the coupling coefficients from the viscosities.
28030 a_cpl(K) = Kv_tot / (h_shear + (I_amax * Kv_tot))
28040 elseif (abs(CS%Kv_extra_bbl) > 0.0) then
2805 ! There is a simple enhancement of the near-bottom viscosities, but no
2806 ! adjustment of the viscous coupling length scales to give a particular
2807 ! bottom stress.
2808
2809 ! botfn determines when a point is within the influence of the bottom
2810 ! boundary layer, going from 1 at the bottom to 0 in the interior.
28110 z2 = z_i(k)
28120 botfn = 1. / (1. + 0.09 * z2 * z2 * z2 * z2 * z2 * z2)
2813
28140 Kv_tot = Kv_tot + CS%Kv_extra_bbl * botfn
28150 h_shear = 0.5 * (hvel(k) + hvel(k-1) + h_neglect)
2816
2817 ! Calculate the coupling coefficients from the viscosities.
28180 a_cpl(K) = Kv_tot / (h_shear + I_amax * Kv_tot)
2819 else
2820 ! Any near-bottom viscous enhancements were already incorporated into
2821 ! Kv_tot, and there is no adjustment of the viscous coupling length
2822 ! scales to give a particular bottom stress.
2823
28240 h_shear = 0.5 * (hvel(k) + hvel(k-1) + h_neglect)
2825 ! Calculate the coupling coefficients from the viscosities.
28260 a_cpl(K) = Kv_tot / (h_shear + I_amax * Kv_tot)
2827 endif
2828 enddo
2829
2830 ! Assign the bottom coupling coefficients
28310 if (CS%bottomdraglaw) then
28320 dhc = hvel(nz) * 0.5
28330 a_cpl(nz+1) = kv_bbl / ((min(dhc, bbl_thick) + h_neglect) + I_amax * kv_bbl)
28340 elseif (abs(CS%Kv_extra_bbl) > 0.0) then
2835 a_cpl(nz+1) = (CS%Kv + CS%Kv_extra_bbl) &
28360 / ((0.5 * hvel(nz) + h_neglect) + I_amax * (CS%Kv + CS%Kv_extra_bbl))
2837 else
28380 a_cpl(nz+1) = CS%Kv / ((0.5 * hvel(nz) + h_neglect) + I_amax * CS%Kv)
2839 endif
2840
2841 ! Add surface intensified viscous coupling, either as a no-slip boundary condition under a
2842 ! rigid ice-shelf, or due to wind-stress driven surface boundary layer mixing that has not
2843 ! already been added via visc%Kv_shear.
28440 if (do_shelf) then
2845 ! Set the coefficients to include the no-slip surface stress.
28460 if (work_on_u) then
28470 kv_TBL = visc%Kv_tbl_shelf_u(I,j)
28480 tbl_thick = visc%tbl_thick_shelf_u(I,j) + h_neglect
2849 else
28500 kv_TBL = visc%Kv_tbl_shelf_v(i,J)
28510 tbl_thick = visc%tbl_thick_shelf_v(i,J) + h_neglect
2852 endif
2853
28540 z_t = 0.0
2855
2856 ! If a_cpl(1) were not already 0, it would be added here.
28570 if (0.5 * hvel(1) > tbl_thick) then
28580 a_cpl(1) = kv_TBL / (tbl_thick + I_amax * kv_TBL)
2859 else
28600 a_cpl(1) = kv_TBL / ((0.5 * hvel(1) + h_neglect) + I_amax * kv_TBL)
2861 endif
2862
28630 do K=2,nz
28640 z_t = z_t + hvel(k-1) / tbl_thick
28650 topfn = 1. / (1. + 0.09 * z_t**6)
2866
28670 dhc = 0.5 * (hvel(k) + hvel(k-1))
28680 if (dhc > tbl_thick) then
28690 h_shear = ((1. - topfn) * dhc + topfn * tbl_thick) + h_neglect
2870 else
28710 h_shear = dhc + h_neglect
2872 endif
2873
28740 kv_top = topfn * kv_TBL
28750 a_cpl(K) = a_cpl(K) + kv_top / (h_shear + I_amax * kv_top)
2876 enddo
28770 elseif (CS%dynamic_viscous_ML .or. (GV%nkml>0) .or. CS%fixed_LOTW_ML .or. CS%apply_LOTW_floor) then
2878
2879 ! Find the friction velocity and the absolute value of the Coriolis parameter at this point.
28800 u_star = 0. ! Zero out the friction velocity on land points.
28810 tau_mag = 0. ! Zero out the friction velocity on land points.
2882
28830 if (allocated(tv%SpV_avg)) then
28840 rho_av1 = 0.
2885
28860 if (work_on_u) then
28870 u_star = 0.5 * (Ustar_2d(i,j) + Ustar_2d(i+1,j))
28880 rho_av1 = 2. / (tv%SpV_avg(i,j,1) + tv%SpV_avg(i+1,j,1))
28890 absf = 0.5 * (abs(G%CoriolisBu(I,J-1)) + abs(G%CoriolisBu(I,J)))
2890
28910 if (do_OBCs) then
28920 if (OBC%u_E_OBCs_on_PE) then
28930 if (OBC%segnum_u(I,j) > 0) then
28940 u_star = Ustar_2d(i,j)
28950 rho_av1 = 1. / tv%SpV_avg(i,j,1)
2896 endif
2897 endif
2898
28990 if (OBC%u_W_OBCs_on_PE) then
29000 if (OBC%segnum_u(I,j) < 0) then
29010 u_star = Ustar_2d(i+1,j)
29020 rho_av1 = 1. / tv%SpV_avg(i+1,j,1)
2903 endif
2904 endif
2905 endif
2906 else
29070 u_star = 0.5 * (Ustar_2d(i,j) + Ustar_2d(i,j+1))
29080 rho_av1 = 2. / (tv%SpV_avg(i,j,1) + tv%SpV_avg(i,j+1,1))
29090 absf = 0.5 * (abs(G%CoriolisBu(I-1,J)) + abs(G%CoriolisBu(I,J)))
2910
29110 if (do_OBCs) then
29120 if (OBC%v_N_OBCs_on_PE) then
29130 if (OBC%segnum_v(i,J) > 0) then
29140 u_star = Ustar_2d(i,j)
29150 rho_av1 = 1. / tv%SpV_avg(i,j,1)
2916 endif
2917 endif
2918
29190 if (OBC%v_S_OBCs_on_PE) then
29200 if (OBC%segnum_v(i,J) < 0) then
29210 u_star = Ustar_2d(i,j+1)
29220 rho_av1 = 1. / tv%SpV_avg(i,j+1,1)
2923 endif
2924 endif
2925 endif
2926 endif
2927
29280 tau_mag = GV%RZ_to_H * rho_av1 * u_star**2
2929 else ! (.not.allocated(tv%SpV_avg))
29300 if (work_on_u) then
29310 u_star = 0.5 * (Ustar_2d(i,j) + Ustar_2d(i+1,j))
29320 absf = 0.5 * (abs(G%CoriolisBu(I,J-1)) + abs(G%CoriolisBu(I,J)))
2933
29340 if (do_OBCs) then
29350 if (OBC%u_E_OBCs_on_PE) then
29360 if (OBC%segnum_u(I,j) > 0) then
29370 u_star = Ustar_2d(i,j)
2938 endif
2939 endif
2940
29410 if (OBC%u_W_OBCs_on_PE) then
29420 if (OBC%segnum_u(I,j) < 0) then
29430 u_star = Ustar_2d(i+1,j)
2944 endif
2945 endif
2946 endif
2947 else
29480 u_star = 0.5 * (Ustar_2d(i,j) + Ustar_2d(i,j+1))
29490 absf = 0.5 * (abs(G%CoriolisBu(I-1,J)) + abs(G%CoriolisBu(I,J)))
2950
29510 if (do_OBCs) then
29520 if (OBC%v_N_OBCs_on_PE) then
29530 if (OBC%segnum_v(i,J) > 0) then
29540 u_star = Ustar_2d(i,j)
2955 endif
2956 endif
2957
29580 if (OBC%v_S_OBCs_on_PE) then
29590 if (OBC%segnum_v(i,J) < 0) then
29600 u_star = Ustar_2d(i,j+1)
2961 endif
2962 endif
2963 endif
2964 endif
2965
29660 tau_mag = GV%Z_to_H * u_star**2
2967 endif
2968
2969 ! Determine the thickness of the surface ocean boundary layer and its extent in index space.
29700 nk_in_ml = 0
29710 if (CS%dynamic_viscous_ML) then
2972 ! The fractional number of layers that are within the viscous boundary layer were
2973 ! previously stored in visc%nkml_visc_[uv].
29740 h_ml = h_neglect
29750 max_nk = 0
2976
29770 if (work_on_u) then
29780 nk_in_ml = ceiling(visc%nkml_visc_u(I,j))
29790 max_nk = max(max_nk, nk_in_ml)
2980
29810 do k=1,max_nk
29820 if (k <= visc%nkml_visc_u(I,j)) then ! This layer is all in the ML.
29830 h_ml = h_ml + hvel(k)
29840 elseif (k < visc%nkml_visc_u(I,j) + 1.) then ! Part of this layer is in the ML.
29850 h_ml = h_ml + ((visc%nkml_visc_u(I,j) + 1.) - k) * hvel(k)
2986 endif
2987 enddo
2988 else
29890 nk_in_ml = ceiling(visc%nkml_visc_v(i,J))
29900 max_nk = max(max_nk, nk_in_ml)
2991
29920 do k=1,max_nk
29930 if (k <= visc%nkml_visc_v(i,J)) then ! This layer is all in the ML.
29940 h_ml = h_ml + hvel(k)
29950 elseif (k < visc%nkml_visc_v(i,J) + 1.) then ! Part of this layer is in the ML.
29960 h_ml = h_ml + ((visc%nkml_visc_v(i,J) + 1.) - k) * hvel(k)
2997 endif
2998 enddo
2999 endif
30000 elseif (GV%nkml>0) then
3001 ! This is a simple application of a refined-bulk mixed layer with GV%nkml sublayers.
30020 max_nk = GV%nkml
30030 nk_in_ml = GV%nkml
3004
30050 h_ml = h_neglect
3006
30070 do k=1,GV%nkml
30080 h_ml = h_ml + hvel(k)
3009 enddo
30100 elseif (CS%fixed_LOTW_ML .or. CS%apply_LOTW_floor) then
3011 ! Determine which interfaces are within CS%Hmix of the surface, and set the viscous
3012 ! boundary layer thickness to the smaller of CS%Hmix and the depth of the ocean.
30130 h_ml = 0.0
30140 do k=1,nz
30150 can_exit = .true.
30160 if (h_ml < CS%Hmix) then
30170 nk_in_ml = k
3018
30190 if (h_ml + hvel(k) < CS%Hmix) then
30200 h_ml = h_ml + hvel(k)
30210 can_exit = .false. ! Part of the next deeper layer is also in the mixed layer.
3022 else
30230 h_ml = CS%Hmix
3024 endif
3025 endif
3026
30270 if (can_exit) exit ! All remaining layers in this row are below the mixed layer depth.
3028 enddo
3029
30300 max_nk = max(0, nk_in_ml)
3031 endif
3032
3033 ! Avoid working on columns where the viscous coupling could not be increased.
30340 if (u_star <= 0.) nk_in_ml = 0
3035
3036 ! Set the viscous coupling at the interfaces as the larger of what was previously
3037 ! set and the contributions from the surface boundary layer.
30380 z_t = 0.
30390 if (CS%apply_LOTW_floor .and. &
3040 (CS%dynamic_viscous_ML .or. GV%nkml > 0 .or. CS%fixed_LOTW_ML)) then
30410 do K=2,max_nk
30420 if (k <= nk_in_ml) then
30430 z_t = z_t + hvel(k-1)
3044
3045 ! The viscosity in visc_ml is set to go to 0 at the mixed layer top and bottom
3046 ! (in a log-layer) and be further limited by rotation to give the natural Ekman length.
30470 temp1 = (z_t * h_ml - z_t * z_t)
30480 if (GV%Boussinesq) then
3049 ustar2_denom = (CS%vonKar * GV%Z_to_H * u_star**2) &
30500 / (absf * temp1 + (h_ml + h_neglect) * u_star)
3051 else
3052 ustar2_denom = (CS%vonKar * tau_mag) &
30530 / (absf * temp1 + (h_ml + h_neglect) * u_star)
3054 endif
3055
30560 visc_ml = temp1 * ustar2_denom
3057 ! Set the viscous coupling based on the model's vertical resolution. The omission of
3058 ! the I_amax factor here is consistent with answer dates above 20190101.
30590 a_ml = visc_ml / (0.25 * (hvel(k) + hvel(k-1) + h_neglect))
3060
3061 ! As a floor on the viscous coupling, assume that the length scale in the denominator can
3062 ! not be larger than the distance from the surface, consistent with a logarithmic velocity
3063 ! profile. This is consistent with visc_ml, but cancels out common factors of z_t.
30640 a_floor = (h_ml - z_t) * ustar2_denom
3065
3066 ! Choose the largest estimate of a_cpl.
30670 a_cpl(K) = max(a_cpl(K), a_ml, a_floor)
3068 ! An option could be added to change this to: a_cpl(i,K) = max(a_cpl(i,K) + a_ml, a_floor)
3069 endif
3070 enddo
30710 elseif (CS%apply_LOTW_floor) then
30720 do K=2,max_nk
30730 if (k <= nk_in_ml) then
30740 z_t = z_t + hvel(k-1)
3075
30760 temp1 = (z_t * h_ml - z_t * z_t)
30770 if (GV%Boussinesq) then
3078 ustar2_denom = (CS%vonKar * GV%Z_to_H * u_star**2) &
30790 / (absf * temp1 + (h_ml + h_neglect) * u_star)
3080 else
3081 ustar2_denom = (CS%vonKar * tau_mag) &
30820 / (absf * temp1 + (h_ml + h_neglect) * u_star)
3083 endif
3084
3085 ! As a floor on the viscous coupling, assume that the length scale in the denominator can not
3086 ! be larger than the distance from the surface, consistent with a logarithmic velocity profile.
30870 a_cpl(K) = max(a_cpl(K), (h_ml - z_t) * ustar2_denom)
3088 endif
3089 enddo
3090 else
30910 do K=2,max_nk
30920 if (k <= nk_in_ml) then
30930 z_t = z_t + hvel(k-1)
3094
30950 temp1 = (z_t * h_ml - z_t * z_t)
3096 ! This viscosity is set to go to 0 at the mixed layer top and bottom (in a log-layer)
3097 ! and be further limited by rotation to give the natural Ekman length.
3098 ! The following expressions are mathematically equivalent.
30990 if (GV%Boussinesq .or. (CS%answer_date < 20230601)) then
3100 visc_ml = u_star * CS%vonKar * (GV%Z_to_H * temp1 * u_star) &
31010 / (absf * temp1 + (h_ml + h_neglect) * u_star)
3102 else
3103 visc_ml = CS%vonKar * (temp1 * tau_mag) &
31040 / (absf * temp1 + (h_ml + h_neglect) * u_star)
3105 endif
31060 a_ml = visc_ml / (0.25 * (hvel(k) + hvel(k-1) + h_neglect) + 0.5 * I_amax * visc_ml)
3107
3108 ! Choose the largest estimate of a_cpl, but these could be changed to be additive.
31090 a_cpl(K) = max(a_cpl(K), a_ml)
3110 ! An option could be added to change this to: a_cpl(i,K) = a_cpl(i,K) + a_ml
3111 endif
3112 enddo
3113 endif
3114 endif
31150end subroutine find_coupling_coef
3116
3117
3118!> Velocity components which exceed a threshold for physically reasonable values are truncated,
3119!! and the running sum of the number of trunctionas within the non-symmetric memory computational
3120!! domain is incremented. Optionally, any column with excessive velocities may be sent
3121!! to a diagnostic reporting subroutine.
312248subroutine vertvisc_limit_vel(u, v, h, ADp, CDp, forces, visc, dt, G, GV, US, CS)
3123 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
3124 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure
3125 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
3126 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), &
3127 intent(inout) :: u !< Zonal velocity [L T-1 ~> m s-1]
3128 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), &
3129 intent(inout) :: v !< Meridional velocity [L T-1 ~> m s-1]
3130 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
3131 intent(in) :: h !< Layer thickness [H ~> m or kg m-2]
3132 type(accel_diag_ptrs), intent(in) :: ADp !< Acceleration diagnostic pointers
3133 type(cont_diag_ptrs), intent(in) :: CDp !< Continuity diagnostic pointers
3134 type(mech_forcing), intent(in) :: forces !< A structure with the driving mechanical forces
3135 type(vertvisc_type), intent(in) :: visc !< Viscosities and bottom drag
3136 real, intent(in) :: dt !< Time increment [T ~> s]
3137 type(vertvisc_CS) :: CS !< Vertical viscosity control structure
3138
3139 ! Local variables
3140 real :: CFL ! The local CFL number [nondim]
3141 real :: H_report ! A thickness below which not to report truncations [H ~> m or kg m-2]
314296 real :: vel_report(SZIB_(G),SZJB_(G)) ! The velocity to report [L T-1 ~> m s-1]
314396 real :: u_old(SZIB_(G),SZJ_(G),SZK_(GV)) ! The previous u-velocity [L T-1 ~> m s-1]
314496 real :: v_old(SZI_(G),SZJB_(G),SZK_(GV)) ! The previous v-velocity [L T-1 ~> m s-1]
3145 logical :: trunc_any_array(SZI_(G),SZJB_(G),SZK_(GV))
314696 logical :: trunc_any, dowrite(SZIB_(G),SZJB_(G))
3147 logical :: do_any_write
3148 integer :: ntrunc ! Thread-safe truncation counter
3149 integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz
315048 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
315148 Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB
3152
315348 H_report = 3.0 * GV%Angstrom_H
3154
3155 !$omp target enter data map(alloc: dowrite, vel_report)
3156 !$omp target enter data map(alloc: u_old, v_old)
3157
315848 if (len_trim(CS%u_trunc_file) > 0) then
31590 do_any_write = .false.
31600 trunc_any = .false.
3161
31620 do concurrent (j=js:je, I=Isq:Ieq)
31630 dowrite(I,j) = .false.
31640 vel_report(I,j) = 3.0e8 * US%m_s_to_L_T
3165 enddo
3166
3167 do concurrent (k=1:nz, j=js:je, I=Isq:Ieq) &
31680 DO_LOCALITY(reduce(.or.: trunc_any, do_any_write))
31690 if (abs(u(I,j,k)) < CS%vel_underflow) u(I,j,k) = 0.0
31700 if (u(I,j,k) < 0.0) then
31710 CFL = (-u(I,j,k) * dt) * (G%dy_Cu(I,j) * G%IareaT(i+1,j))
3172 else
31730 CFL = (u(I,j,k) * dt) * (G%dy_Cu(I,j) * G%IareaT(i,j))
3174 endif
31750 if (CFL > CS%CFL_trunc) trunc_any = .true.
31760 if (CFL > CS%CFL_report) then
31770 dowrite(I,j) = .true.
31780 do_any_write = .true.
31790 vel_report(I,j) = min(vel_report(I,j), abs(u(I,j,k)))
3180 endif
3181 enddo
3182
31830 do concurrent (j=js:je, I=Isq:Ieq, dowrite(I,j))
31840 u_old(I,j,:) = u(I,j,:)
3185 enddo
3186
31870 if (trunc_any) then
31880 ntrunc = 0
31890 do concurrent (k=1:nz, j=js:je, I=Isq:Ieq) DO_LOCALITY(reduce(+: ntrunc))
31900 if ((u(I,j,k) * (dt * G%dy_Cu(I,j))) * G%IareaT(i+1,j) < -CS%CFL_trunc) then
31910 u(I,j,k) = (-0.9*CS%CFL_trunc) * (G%areaT(i+1,j) / (dt * G%dy_Cu(I,j)))
31920 if (((I >= G%isc) .and. (I <= G%iec) .and. (j >= G%jsc) .and. (j <= G%jec)) .and. &
31930 (CS%h_u(I,j,k) > H_report)) ntrunc = ntrunc + 1
31940 elseif ((u(I,j,k) * (dt * G%dy_Cu(I,j))) * G%IareaT(i,j) > CS%CFL_trunc) then
31950 u(I,j,k) = (0.9*CS%CFL_trunc) * (G%areaT(i,j) / (dt * G%dy_Cu(I,j)))
31960 if (((I >= G%isc) .and. (I <= G%iec) .and. (j >= G%jsc) .and. (j <= G%jec)) .and. &
31970 (CS%h_u(I,j,k) > H_report)) ntrunc = ntrunc + 1
3198 endif
3199 enddo
32000 CS%ntrunc = CS%ntrunc + ntrunc
3201 endif
3202
32030 if (do_any_write) then
3204 !$omp target update from (u_old, vel_report)
32050 do j=js,je ; do I=Isq,Ieq ; if (dowrite(I,j)) then
3206 ! Call a diagnostic reporting subroutines are called if unphysically large values are found.
3207 call write_u_accel(I, j, u_old, h, ADp, CDp, dt, G, GV, US, CS%PointAccel_CSp, &
32080 vel_report(I,j), forces%taux(I,j), a=CS%a_u, hv=CS%h_u)
3209 endif ; enddo ; enddo
3210 endif
3211 else ! Do not report accelerations leading to large velocities.
321248 ntrunc = 0
321348 do concurrent (k=1:nz, j=js:je, I=Isq:Ieq) DO_LOCALITY(reduce(+: ntrunc))
321426490336 if (abs(u(I,j,k)) < CS%vel_underflow) then ; u(I,j,k) = 0.0
321526136000 elseif ((u(I,j,k) * (dt * G%dy_Cu(I,j))) * G%IareaT(i+1,j) < -CS%CFL_trunc) then
32160 u(I,j,k) = (-0.9*CS%CFL_trunc) * (G%areaT(i+1,j) / (dt * G%dy_Cu(I,j)))
32170 if (((I >= G%isc) .and. (I <= G%iec) .and. (j >= G%jsc) .and. (j <= G%jec)) .and. &
32180 (CS%h_u(I,j,k) > H_report)) ntrunc = ntrunc + 1
321926136000 elseif ((u(I,j,k) * (dt * G%dy_Cu(I,j))) * G%IareaT(i,j) > CS%CFL_trunc) then
32200 u(I,j,k) = (0.9*CS%CFL_trunc) * (G%areaT(i,j) / (dt * G%dy_Cu(I,j)))
32210 if (((I >= G%isc) .and. (I <= G%iec) .and. (j >= G%jsc) .and. (j <= G%jec)) .and. &
32220 (CS%h_u(I,j,k) > H_report)) ntrunc = ntrunc + 1
3223 endif
3224 enddo
322548 CS%ntrunc = CS%ntrunc + ntrunc
3226 endif
3227
322848 if (len_trim(CS%v_trunc_file) > 0) then
32290 do_any_write =.false.
32300 trunc_any = .false.
3231
32320 do concurrent (J=Jsq:Jeq, i=is:ie)
32330 dowrite(i,J) = .false.
32340 vel_report(i,J) = 3.0e8 * US%m_s_to_L_T
3235 enddo
3236
3237 do concurrent (k=1:nz, J=Jsq:Jeq, i=is:ie) &
32380 DO_LOCALITY(reduce(.or.: trunc_any, do_any_write))
32390 if (abs(v(i,J,k)) < CS%vel_underflow) v(i,J,k) = 0.0
32400 if (v(i,J,k) < 0.0) then
32410 CFL = (-v(i,J,k) * dt) * (G%dx_Cv(i,J) * G%IareaT(i,j+1))
3242 else
32430 CFL = (v(i,J,k) * dt) * (G%dx_Cv(i,J) * G%IareaT(i,j))
3244 endif
32450 if (CFL > CS%CFL_trunc) trunc_any = .true.
32460 if (CFL > CS%CFL_report) then
32470 dowrite(i,J) = .true.
32480 do_any_write = .true.
32490 vel_report(i,J) = min(vel_report(i,J), abs(v(i,J,k)))
3250 endif
3251 enddo
3252
32530 do concurrent (J=Jsq:Jeq, i=is:ie, dowrite(i,J))
32540 v_old(i,J,:) = v(i,J,:)
3255 enddo
3256
32570 if (trunc_any) then
32580 ntrunc = 0
32590 do concurrent (k=1:nz, J=Jsq:Jeq, i=is:ie) DO_LOCALITY(reduce(+: ntrunc))
32600 if ((v(i,J,k) * (dt * G%dx_Cv(i,J))) * G%IareaT(i,j+1) < -CS%CFL_trunc) then
32610 v(i,J,k) = (-0.9*CS%CFL_trunc) * (G%areaT(i,j+1) / (dt * G%dx_Cv(i,J)))
32620 if (((i >= G%isc) .and. (i <= G%iec) .and. (J >= G%jsc) .and. (J <= G%jec)) .and. &
32630 (CS%h_v(i,J,k) > H_report)) ntrunc = ntrunc + 1
32640 elseif ((v(i,J,k) * (dt * G%dx_Cv(i,J))) * G%IareaT(i,j) > CS%CFL_trunc) then
32650 v(i,J,k) = (0.9*CS%CFL_trunc) * (G%areaT(i,j) / (dt * G%dx_Cv(i,J)))
32660 if (((i >= G%isc) .and. (i <= G%iec) .and. (J >= G%jsc) .and. (J <= G%jec)) .and. &
32670 (CS%h_v(i,J,k) > H_report)) ntrunc = ntrunc + 1
3268 endif
3269 enddo
32700 CS%ntrunc = CS%ntrunc + ntrunc
3271 endif
3272
32730 if (do_any_write) then
3274 !$omp target update from(v_old, vel_report)
32750 do J=Jsq,Jeq ; do i=is,ie ; if (dowrite(i,J)) then
3276 ! Call a diagnostic reporting subroutines are called if unphysically large values are found.
3277 call write_v_accel(i, J, v_old, h, ADp, CDp, dt, G, GV, US, CS%PointAccel_CSp, &
32780 vel_report(i,J), forces%tauy(i,J), a=CS%a_v, hv=CS%h_v)
3279 endif ; enddo ; enddo
3280 endif
3281 else ! Do not report accelerations leading to large velocities.
328248 ntrunc = 0
328348 do concurrent (k=1:nz, J=Jsq:Jeq, i=is:ie) DO_LOCALITY(reduce(+: ntrunc))
328426709168 if (abs(v(i,J,k)) < CS%vel_underflow) then ; v(i,J,k) = 0.0
328526352000 elseif ((v(i,J,k) * (dt * G%dx_Cv(i,J))) * G%IareaT(i,j+1) < -CS%CFL_trunc) then
32860 v(i,J,k) = (-0.9*CS%CFL_trunc) * (G%areaT(i,j+1) / (dt * G%dx_Cv(i,J)))
32870 if (((i >= G%isc) .and. (i <= G%iec) .and. (J >= G%jsc) .and. (J <= G%jec)) .and. &
32880 (CS%h_v(i,J,k) > H_report)) ntrunc = ntrunc + 1
328926352000 elseif ((v(i,J,k) * (dt * G%dx_Cv(i,J))) * G%IareaT(i,j) > CS%CFL_trunc) then
32900 v(i,J,k) = (0.9*CS%CFL_trunc) * (G%areaT(i,j) / (dt * G%dx_Cv(i,J)))
32910 if (((i >= G%isc) .and. (i <= G%iec) .and. (J >= G%jsc) .and. (J <= G%jec)) .and. &
32920 (CS%h_v(i,J,k) > H_report)) ntrunc = ntrunc + 1
3293 endif
3294 enddo
329548 CS%ntrunc = CS%ntrunc + ntrunc
3296 endif
3297
3298 !$omp target exit data map(release: u_old, v_old, dowrite, vel_report)
329948end subroutine vertvisc_limit_vel
3300
3301
3302!> Initialize the vertical friction module
33031subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, &
3304 ntrunc, CS, fpmix)
3305 type(ocean_internal_state), &
3306 target, intent(in) :: MIS !< The "MOM Internal State", a set of pointers
3307 !! to the fields and accelerations that make
3308 !! up the ocean's physical state
3309 type(time_type), target, intent(in) :: Time !< Current model time
3310 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
3311 type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure
3312 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
3313 type(param_file_type), intent(in) :: param_file !< File to parse for parameters
3314 type(diag_ctrl), target, intent(inout) :: diag !< Diagnostic control structure
3315 type(accel_diag_ptrs), intent(inout) :: ADp !< Acceleration diagnostic pointers
3316 type(directories), intent(in) :: dirs !< Relevant directory paths
3317 integer, target, intent(inout) :: ntrunc !< Number of velocity truncations
3318 type(vertvisc_CS), pointer :: CS !< Vertical viscosity control structure
3319 logical, optional, intent(in) :: fpmix !< Nonlocal momentum mixing
3320
3321 ! Local variables
3322
3323 real :: Kv_BBL ! A viscosity in the bottom boundary layer with a simple scheme [H Z T-1 ~> m2 s-1 or Pa s]
3324 real :: Kv_back_z ! A background kinematic viscosity [Z2 T-1 ~> m2 s-1]
3325 integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags.
3326 integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, nz
3327 logical :: lfpmix
3328 character(len=200) :: kappa_gl90_file, inputdir, kdgl90_varname
3329 ! This include declares and sets the variable "version".
3330# include "version_variable.h"
3331 character(len=40) :: mdl = "MOM_vert_friction" ! This module's name.
3332 character(len=40) :: thickness_units
3333 real :: Kv_mks ! KVML in MKS [m2 s-1]
3334
33351 CS%initialized = .true.
3336
33371 if (GV%Boussinesq) then ; thickness_units = "m"
33380 else ; thickness_units = "kg m-2" ; endif
3339
33401 isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed ; nz = GV%ke
33411 IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB
3342
33431 CS%diag => diag ; CS%ntrunc => ntrunc ; ntrunc = 0
3344
33451 lfpmix = .false.
33461 if (present(fpmix)) lfpmix = fpmix
3347
3348! Default, read and log parameters
33491 call log_version(param_file, mdl, version, "", log_to_all=.true., debugging=.true.)
3350 call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, &
3351 "This sets the default value for the various _ANSWER_DATE parameters.", &
33521 default=99991231)
3353 call get_param(param_file, mdl, "VERT_FRICTION_ANSWER_DATE", CS%answer_date, &
3354 "The vintage of the order of arithmetic and expressions in the viscous "//&
3355 "calculations. Values below 20190101 recover the answers from the end of 2018, "//&
3356 "while higher values use expressions that do not use an arbitrary hard-coded "//&
3357 "maximum viscous coupling coefficient between layers. Values below 20230601 "//&
3358 "recover a form of the viscosity within the mixed layer that breaks up the "//&
3359 "magnitude of the wind stress in some non-Boussinesq cases.", &
33601 default=default_answer_date, do_not_log=.not.GV%Boussinesq)
33611 if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701)
3362
3363 call get_param(param_file, mdl, "BOTTOMDRAGLAW", CS%bottomdraglaw, &
3364 "If true, the bottom stress is calculated with a drag "//&
3365 "law of the form c_drag*|u|*u. The velocity magnitude "//&
3366 "may be an assumed value or it may be based on the "//&
3367 "actual velocity in the bottommost HBBL, depending on "//&
33681 "LINEAR_DRAG.", default=.true.)
3369 call get_param(param_file, mdl, "DIRECT_STRESS", CS%direct_stress, &
3370 "If true, the wind stress is distributed over the topmost HMIX_STRESS of fluid "//&
3371 "(like in HYCOM), and an added mixed layer viscosity or a physically based "//&
3372 "boundary layer turbulence parameterization is not needed for stability.", &
33731 default=.false.)
3374 call get_param(param_file, mdl, "DYNAMIC_VISCOUS_ML", CS%dynamic_viscous_ML, &
3375 "If true, use a bulk Richardson number criterion to "//&
3376 "determine the mixed layer thickness for viscosity.", &
33771 default=.false.)
3378 call get_param(param_file, mdl, "FIXED_DEPTH_LOTW_ML", CS%fixed_LOTW_ML, &
3379 "If true, use a Law-of-the-wall prescription for the mixed layer viscosity "//&
3380 "within a boundary layer that is the lesser of HMIX_FIXED and the total "//&
33811 "depth of the ocean in a column.", default=.false.)
3382 call get_param(param_file, mdl, "LOTW_VISCOUS_ML_FLOOR", CS%apply_LOTW_floor, &
3383 "If true, use a Law-of-the-wall prescription to set a lower bound on the "//&
3384 "viscous coupling between layers within the surface boundary layer, based "//&
3385 "the distance of interfaces from the surface. This only acts when there "//&
3386 "are large changes in the thicknesses of successive layers or when the "//&
3387 "viscosity is set externally and the wind stress has subsequently increased.", &
33881 default=.false.)
3389 call get_param(param_file, mdl, 'VON_KARMAN_CONST', CS%vonKar, &
3390 'The value the von Karman constant as used for mixed layer viscosity.', &
33911 units='nondim', default=0.41)
3392 call get_param(param_file, mdl, "U_TRUNC_FILE", CS%u_trunc_file, &
3393 "The absolute path to a file into which the accelerations "//&
3394 "leading to zonal velocity truncations are written. "//&
3395 "Undefine this for efficiency if this diagnostic is not needed.", &
33961 default=" ", debuggingParam=.true.)
3397 call get_param(param_file, mdl, "V_TRUNC_FILE", CS%v_trunc_file, &
3398 "The absolute path to a file into which the accelerations "//&
3399 "leading to meridional velocity truncations are written. "//&
3400 "Undefine this for efficiency if this diagnostic is not needed.", &
34011 default=" ", debuggingParam=.true.)
3402 call get_param(param_file, mdl, "HARMONIC_VISC", CS%harmonic_visc, &
3403 "If true, use the harmonic mean thicknesses for "//&
34041 "calculating the vertical viscosity.", default=.false.)
3405 call get_param(param_file, mdl, "HARMONIC_BL_SCALE", CS%harm_BL_val, &
3406 "A scale to determine when water is in the boundary "//&
3407 "layers based solely on harmonic mean thicknesses for "//&
3408 "the purpose of determining the extent to which the "//&
3409 "thicknesses used in the viscosities are upwinded.", &
34101 default=0.0, units="nondim")
34111 call get_param(param_file, mdl, "DEBUG", CS%debug, default=.false.)
3412
34131 if (GV%nkml < 1) then
3414 call get_param(param_file, mdl, "HMIX_FIXED", CS%Hmix, &
3415 "The prescribed depth over which the near-surface viscosity and "//&
3416 "diffusivity are elevated when the bulk mixed layer is not used.", &
34171 units="m", scale=US%m_to_Z, fail_if_missing=.true.)
3418 endif
34191 if (CS%direct_stress) then
34200 if (GV%nkml < 1) then
3421 call get_param(param_file, mdl, "HMIX_STRESS", CS%Hmix_stress, &
3422 "The depth over which the wind stress is applied if DIRECT_STRESS is true.", &
34230 units="m", default=US%Z_to_m*CS%Hmix, scale=GV%m_to_H)
3424 else
3425 call get_param(param_file, mdl, "HMIX_STRESS", CS%Hmix_stress, &
3426 "The depth over which the wind stress is applied if DIRECT_STRESS is true.", &
34270 units="m", fail_if_missing=.true., scale=GV%m_to_H)
3428 endif
34290 if (CS%Hmix_stress <= 0.0) call MOM_error(FATAL, "vertvisc_init: " // &
34300 "HMIX_STRESS must be set to a positive value if DIRECT_STRESS is true.")
3431 endif
3432 call get_param(param_file, mdl, "KV", Kv_back_z, &
3433 "The background kinematic viscosity in the interior. "//&
3434 "The molecular value, ~1e-6 m2 s-1, may be used.", &
34351 units="m2 s-1", fail_if_missing=.true., scale=US%m2_s_to_Z2_T)
3436 ! Convert input kinematic viscosity to dynamic viscosity when non-Boussinesq.
34371 CS%Kv = (US%Z2_T_to_m2_s*GV%m2_s_to_HZ_T) * Kv_back_z
3438
3439 call get_param(param_file, mdl, "USE_GL90_IN_SSW", CS%use_GL90_in_SSW, &
3440 "If true, use simpler method to calculate 1/N^2 in GL90 vertical "// &
3441 "viscosity coefficient. This method is valid in stacked shallow water mode.", &
34421 default=.false.)
3443 call get_param(param_file, mdl, "KD_GL90", CS%kappa_gl90, &
3444 "The scalar diffusivity used in GL90 vertical viscosity scheme.", &
3445 units="m2 s-1", default=0.0, scale=US%m_to_L*US%Z_to_L*GV%m_to_H*US%T_to_s, &
34461 do_not_log=.not.CS%use_GL90_in_SSW)
3447 call get_param(param_file, mdl, "READ_KD_GL90", CS%read_kappa_gl90, &
3448 "If true, read a file (given by KD_GL90_FILE) containing the "//&
3449 "spatially varying diffusivity KD_GL90 used in the GL90 scheme.", default=.false., &
34501 do_not_log=.not.CS%use_GL90_in_SSW)
34511 if (CS%read_kappa_gl90) then
34520 if (CS%kappa_gl90 > 0) then
3453 call MOM_error(FATAL, "MOM_vert_friction.F90, vertvisc_init: KD_GL90 > 0 "// &
34540 "is not compatible with READ_KD_GL90 = .TRUE. ")
3455 endif
3456 call get_param(param_file, mdl, "INPUTDIR", inputdir, &
3457 "The directory in which all input files are found.", &
34580 default=".", do_not_log=.true.)
34590 inputdir = slasher(inputdir)
3460 call get_param(param_file, mdl, "KD_GL90_FILE", kappa_gl90_file, &
3461 "The file containing the spatially varying diffusivity used in the "// &
34620 "GL90 scheme.", default="kd_gl90.nc", do_not_log=.not.CS%use_GL90_in_SSW)
3463 call get_param(param_file, mdl, "KD_GL90_VARIABLE", kdgl90_varname, &
3464 "The name of the GL90 diffusivity variable to read "//&
34650 "from KD_GL90_FILE.", default="kd_gl90", do_not_log=.not.CS%use_GL90_in_SSW)
34660 kappa_gl90_file = trim(inputdir) // trim(kappa_gl90_file)
3467
34680 allocate(CS%kappa_gl90_2d(G%isd:G%ied, G%jsd:G%jed), source=0.0)
3469 call MOM_read_data(kappa_gl90_file, kdgl90_varname, CS%kappa_gl90_2d(:,:), G%domain, &
34700 scale=US%m_to_L*US%Z_to_L*GV%m_to_H*US%T_to_s)
34710 call pass_var(CS%kappa_gl90_2d, G%domain)
3472 endif
3473 call get_param(param_file, mdl, "USE_GL90_N2", CS%use_GL90_N2, &
3474 "If true, use GL90 vertical viscosity coefficient that is depth-independent; "// &
3475 "this corresponds to a kappa_GM that scales as N^2 with depth.", &
34761 default=.false., do_not_log=.not.CS%use_GL90_in_SSW)
34771 if (CS%use_GL90_N2) then
34780 if (.not. CS%use_GL90_in_SSW) call MOM_error(FATAL, &
3479 "MOM_vert_friction.F90, vertvisc_init: "//&
34800 "When USE_GL90_N2=True, USE_GL90_in_SSW must also be True.")
34810 if (CS%kappa_gl90 > 0) then
3482 call MOM_error(FATAL, "MOM_vert_friction.F90, vertvisc_init: KD_GL90 > 0 "// &
34830 "is not compatible with USE_GL90_N2 = .TRUE. ")
3484 endif
34850 if (CS%read_kappa_gl90) call MOM_error(FATAL, &
3486 "MOM_vert_friction.F90, vertvisc_init: "//&
34870 "READ_KD_GL90 = .TRUE. is not compatible with USE_GL90_N2 = .TRUE.")
3488 call get_param(param_file, mdl, "alpha_GL90", CS%alpha_gl90, &
3489 "Coefficient used to compute a depth-independent GL90 vertical "//&
3490 "viscosity via Kv_GL90 = alpha_GL90 * f2. Is only used "// &
3491 "if USE_GL90_N2 is true. Note that the implied Kv_GL90 "// &
3492 "corresponds to a KD_GL90 that scales as N^2 with depth.", &
3493 units="m2 s", default=0.0, scale=GV%m_to_H*US%m_to_Z*US%s_to_T, &
34940 do_not_log=.not.CS%use_GL90_in_SSW)
3495 endif
3496 call get_param(param_file, mdl, "HBBL_GL90", CS%Hbbl_gl90, &
3497 "The thickness of the GL90 bottom boundary layer, "//&
3498 "which defines the range over which the GL90 coupling "//&
3499 "coefficient is zeroed out, in order to avoid fluxing "//&
3500 "momentum into vanished layers over steep topography.", &
35011 units="m", default=5.0, scale=US%m_to_Z, do_not_log=.not.CS%use_GL90_in_SSW)
3502
35031 CS%Kvml_invZ2 = 0.0
35041 if (GV%nkml < 1) then
3505 call get_param(param_file, mdl, "KVML", Kv_mks, &
3506 "The scale for an extra kinematic viscosity in the mixed layer", &
35071 units="m2 s-1", default=-1.0, do_not_log=.true.)
35081 if (Kv_mks >= 0.0) then
35090 call MOM_error(WARNING, "KVML is a deprecated parameter. Use KV_ML_INVZ2 instead.")
3510 else
35111 Kv_mks = 0.0
3512 endif
3513 call get_param(param_file, mdl, "KV_ML_INVZ2", CS%Kvml_invZ2, &
3514 "An extra kinematic viscosity in a mixed layer of thickness HMIX_FIXED, "//&
3515 "with the actual viscosity scaling as 1/(z*HMIX_FIXED)^2, where z is the "//&
3516 "distance from the surface, to allow for finite wind stresses to be "//&
3517 "transmitted through infinitesimally thin surface layers. This is an "//&
3518 "older option for numerical convenience without a strong physical basis, "//&
3519 "and its use is now discouraged.", &
35201 units="m2 s-1", default=Kv_mks, scale=GV%m2_s_to_HZ_T)
3521 endif
3522
35231 if (.not.CS%bottomdraglaw) then
3524 call get_param(param_file, mdl, "KV_EXTRA_BBL", CS%Kv_extra_bbl, &
3525 "An extra kinematic viscosity in the benthic boundary layer. "//&
3526 "KV_EXTRA_BBL is not used if BOTTOMDRAGLAW is true.", &
35270 units="m2 s-1", default=0.0, scale=GV%m2_s_to_HZ_T, do_not_log=.true.)
35280 if (CS%Kv_extra_bbl == 0.0) then
3529 call get_param(param_file, mdl, "KVBBL", Kv_BBL, &
3530 "An extra kinematic viscosity in the benthic boundary layer. "//&
3531 "KV_EXTRA_BBL is not used if BOTTOMDRAGLAW is true.", &
3532 units="m2 s-1", default=US%Z2_T_to_m2_s*Kv_back_z, scale=GV%m2_s_to_HZ_T, &
35330 do_not_log=.true.)
35340 if (abs(Kv_BBL - CS%Kv) > 1.0e-15*abs(CS%Kv)) then
35350 call MOM_error(WARNING, "KVBBL is a deprecated parameter. Use KV_EXTRA_BBL instead.")
35360 CS%Kv_extra_bbl = Kv_BBL - CS%Kv
3537 endif
3538 endif
3539 call log_param(param_file, mdl, "KV_EXTRA_BBL", CS%Kv_extra_bbl, &
3540 "An extra kinematic viscosity in the benthic boundary layer. "//&
3541 "KV_EXTRA_BBL is not used if BOTTOMDRAGLAW is true.", &
35420 units="m2 s-1", default=0.0, unscale=GV%HZ_T_to_m2_s)
3543 endif
3544 call get_param(param_file, mdl, "HBBL", CS%Hbbl, &
3545 "The thickness of a bottom boundary layer with a viscosity increased by "//&
3546 "KV_EXTRA_BBL if BOTTOMDRAGLAW is not defined, or the thickness over which "//&
3547 "near-bottom velocities are averaged for the drag law if BOTTOMDRAGLAW is "//&
3548 "defined but LINEAR_DRAG is not.", &
35491 units="m", fail_if_missing=.true., scale=US%m_to_Z)
3550 call get_param(param_file, mdl, "CFL_TRUNCATE", CS%CFL_trunc, &
3551 "The value of the CFL number that will cause velocity "//&
3552 "components to be truncated; instability can occur past 0.5.", &
35531 units="nondim", default=0.5)
3554 call get_param(param_file, mdl, "CFL_REPORT", CS%CFL_report, &
3555 "The value of the CFL number that causes accelerations "//&
3556 "to be reported; the default is CFL_TRUNCATE.", &
35571 units="nondim", default=CS%CFL_trunc)
3558 call get_param(param_file, mdl, "CFL_TRUNCATE_RAMP_TIME", CS%truncRampTime, &
3559 "The time over which the CFL truncation value is ramped "//&
3560 "up at the beginning of the run.", &
35611 units="s", default=0., scale=US%s_to_T)
35621 CS%CFL_truncE = CS%CFL_trunc
3563 call get_param(param_file, mdl, "CFL_TRUNCATE_START", CS%CFL_truncS, &
3564 "The start value of the truncation CFL number used when "//&
3565 "ramping up CFL_TRUNC.", &
35661 units="nondim", default=0.)
3567 call get_param(param_file, mdl, "STOKES_MIXING_COMBINED", CS%StokesMixing, &
3568 "Flag to use Stokes drift Mixing via the Lagrangian "//&
3569 " current (Eulerian plus Stokes drift). "//&
3570 " Still needs work and testing, so not recommended for use.",&
35711 default=.false.)
3572 !BGR 04/04/2018{
3573 ! StokesMixing is required for MOM6 for some Langmuir mixing parameterization.
3574 ! The code used here has not been developed for vanishing layers or in
3575 ! conjunction with any bottom friction. Therefore, the following line is
3576 ! added so this functionality cannot be used without user intervention in
3577 ! the code. This will prevent general use of this functionality until proper
3578 ! care is given to the previously mentioned issues. Comment out the following
3579 ! MOM_error to use, but do so at your own risk and with these points in mind.
3580 !}
35811 if (CS%StokesMixing) then
3582 call MOM_error(FATAL, "Stokes mixing requires user intervention in the code.\n"//&
3583 " Model now exiting. See MOM_vert_friction.F90 for \n"//&
35840 " details (search 'BGR 04/04/2018' to locate comment).")
3585 endif
3586 call get_param(param_file, mdl, "VEL_UNDERFLOW", CS%vel_underflow, &
3587 "A negligibly small velocity magnitude below which velocity "//&
3588 "components are set to 0. A reasonable value might be "//&
3589 "1e-30 m/s, which is less than an Angstrom divided by "//&
35901 "the age of the universe.", units="m s-1", default=0.0, scale=US%m_s_to_L_T)
3591
3592 !$omp target update to(CS)
3593
3594671917 ALLOC_(CS%a_u(IsdB:IedB,jsd:jed,nz+1)) ; CS%a_u(:,:,:) = 0.0
3595671917 ALLOC_(CS%a_u_gl90(IsdB:IedB,jsd:jed,nz+1)) ; CS%a_u_gl90(:,:,:) = 0.0
3596663076 ALLOC_(CS%h_u(IsdB:IedB,jsd:jed,nz)) ; CS%h_u(:,:,:) = 0.0
3597676553 ALLOC_(CS%a_v(isd:ied,JsdB:JedB,nz+1)) ; CS%a_v(:,:,:) = 0.0
3598676553 ALLOC_(CS%a_v_gl90(isd:ied,JsdB:JedB,nz+1)) ; CS%a_v_gl90(:,:,:) = 0.0
3599667651 ALLOC_(CS%h_v(isd:ied,JsdB:JedB,nz)) ; CS%h_v(:,:,:) = 0.0
3600
3601 !$omp target enter data map(to: CS%a_u, CS%a_v)
3602 !$omp target enter data map(to: CS%h_u, CS%h_v)
3603 ! TODO: Conditional?
3604 !$omp target enter data map(to: CS%a_u_gl90, CS%a_v_gl90)
3605
3606 CS%id_Kv_slow = register_diag_field('ocean_model', 'Kv_slow', diag%axesTi, Time, &
36071 'Slow varying vertical viscosity', 'm2 s-1', conversion=GV%HZ_T_to_m2_s)
3608
3609 CS%id_Kv_u = register_diag_field('ocean_model', 'Kv_u', diag%axesCuL, Time, &
36101 'Total vertical viscosity at u-points', 'm2 s-1', conversion=GV%H_to_m**2*US%s_to_T)
3611
3612 CS%id_Kv_v = register_diag_field('ocean_model', 'Kv_v', diag%axesCvL, Time, &
36131 'Total vertical viscosity at v-points', 'm2 s-1', conversion=GV%H_to_m**2*US%s_to_T)
3614
3615 CS%id_Kv_gl90_u = register_diag_field('ocean_model', 'Kv_gl90_u', diag%axesCuL, Time, &
36161 'GL90 vertical viscosity at u-points', 'm2 s-1', conversion=GV%H_to_m**2*US%s_to_T)
3617
3618 CS%id_Kv_gl90_v = register_diag_field('ocean_model', 'Kv_gl90_v', diag%axesCvL, Time, &
36191 'GL90 vertical viscosity at v-points', 'm2 s-1', conversion=GV%H_to_m**2*US%s_to_T)
3620
3621 CS%id_au_vv = register_diag_field('ocean_model', 'au_visc', diag%axesCui, Time, &
36221 'Zonal Viscous Vertical Coupling Coefficient', 'm s-1', conversion=GV%H_to_m*US%s_to_T)
3623
3624 CS%id_av_vv = register_diag_field('ocean_model', 'av_visc', diag%axesCvi, Time, &
36251 'Meridional Viscous Vertical Coupling Coefficient', 'm s-1', conversion=GV%H_to_m*US%s_to_T)
3626
3627 CS%id_au_gl90_vv = register_diag_field('ocean_model', 'au_gl90_visc', diag%axesCui, Time, &
36281 'Zonal Viscous Vertical GL90 Coupling Coefficient', 'm s-1', conversion=GV%H_to_m*US%s_to_T)
3629
3630 CS%id_av_gl90_vv = register_diag_field('ocean_model', 'av_gl90_visc', diag%axesCvi, Time, &
36311 'Meridional Viscous Vertical GL90 Coupling Coefficient', 'm s-1', conversion=GV%H_to_m*US%s_to_T)
3632
3633 CS%id_h_u = register_diag_field('ocean_model', 'Hu_visc', diag%axesCuL, Time, &
3634 'Thickness at Zonal Velocity Points for Viscosity', &
36351 thickness_units, conversion=GV%H_to_MKS)
3636 ! Alternately, to always give this variable in 'm' use the following line instead:
3637 ! 'm', conversion=GV%H_to_m)
3638
3639 CS%id_h_v = register_diag_field('ocean_model', 'Hv_visc', diag%axesCvL, Time, &
3640 'Thickness at Meridional Velocity Points for Viscosity', &
36411 thickness_units, conversion=GV%H_to_MKS)
3642
3643 CS%id_hML_u = register_diag_field('ocean_model', 'HMLu_visc', diag%axesCu1, Time, &
3644 'Mixed Layer Thickness at Zonal Velocity Points for Viscosity', &
36451 thickness_units, conversion=US%Z_to_m)
3646
3647 CS%id_hML_v = register_diag_field('ocean_model', 'HMLv_visc', diag%axesCv1, Time, &
3648 'Mixed Layer Thickness at Meridional Velocity Points for Viscosity', &
36491 thickness_units, conversion=US%Z_to_m)
3650
36511 if (lfpmix) then
3652 CS%id_uE_h = register_diag_field('ocean_model', 'uE_h' , CS%diag%axesTL, &
36530 Time, 'x-zonal Eulerian' , 'm s-1', conversion=US%L_T_to_m_s)
3654 CS%id_vE_h = register_diag_field('ocean_model', 'vE_h' , CS%diag%axesTL, &
36550 Time, 'y-merid Eulerian' , 'm s-1', conversion=US%L_T_to_m_s)
3656 CS%id_uInc_h = register_diag_field('ocean_model','uInc_h',CS%diag%axesTL, &
36570 Time, 'x-zonal Eulerian' , 'm s-1', conversion=US%L_T_to_m_s)
3658 CS%id_vInc_h = register_diag_field('ocean_model','vInc_h',CS%diag%axesTL, &
36590 Time, 'x-zonal Eulerian' , 'm s-1', conversion=US%L_T_to_m_s)
3660 CS%id_uStk = register_diag_field('ocean_model', 'uStk' , CS%diag%axesTL, &
36610 Time, 'x-FP du increment' , 'm s-1', conversion=US%L_T_to_m_s)
3662 CS%id_vStk = register_diag_field('ocean_model', 'vStk' , CS%diag%axesTL, &
36630 Time, 'y-FP dv increment' , 'm s-1', conversion=US%L_T_to_m_s)
3664
3665 CS%id_FPtau2s = register_diag_field('ocean_model','Omega_tau2s',CS%diag%axesTi, &
36660 Time, 'Stress direction from shear','radians')
3667 CS%id_FPtau2w = register_diag_field('ocean_model','Omega_tau2w',CS%diag%axesTi, &
36680 Time, 'Stress direction from wind','radians')
3669 CS%id_uStk0 = register_diag_field('ocean_model', 'uStk0' , diag%axesT1, &
36700 Time, 'Zonal Surface Stokes', 'm s-1', conversion=US%L_T_to_m_s)
3671 CS%id_vStk0 = register_diag_field('ocean_model', 'vStk0' , diag%axesT1, &
36720 Time, 'Merid Surface Stokes', 'm s-1', conversion=US%L_T_to_m_s)
3673 endif
3674
3675 CS%id_du_dt_visc = register_diag_field('ocean_model', 'du_dt_visc', diag%axesCuL, Time, &
36761 'Zonal Acceleration from Vertical Viscosity', 'm s-2', conversion=US%L_T2_to_m_s2)
36771 if (CS%id_du_dt_visc > 0) call safe_alloc_ptr(ADp%du_dt_visc,IsdB,IedB,jsd,jed,nz)
3678 CS%id_dv_dt_visc = register_diag_field('ocean_model', 'dv_dt_visc', diag%axesCvL, Time, &
36791 'Meridional Acceleration from Vertical Viscosity', 'm s-2', conversion=US%L_T2_to_m_s2)
36801 if (CS%id_dv_dt_visc > 0) call safe_alloc_ptr(ADp%dv_dt_visc,isd,ied,JsdB,JedB,nz)
3681 CS%id_GLwork = register_diag_field('ocean_model', 'GLwork', diag%axesTL, Time, &
3682 'Sign-definite Kinetic Energy Source from GL90 Vertical Viscosity', &
36831 'm3 s-3', conversion=GV%H_to_m*(US%L_T_to_m_s**2)*US%s_to_T)
3684 CS%id_du_dt_visc_gl90 = register_diag_field('ocean_model', 'du_dt_visc_gl90', diag%axesCuL, Time, &
36851 'Zonal Acceleration from GL90 Vertical Viscosity', 'm s-2', conversion=US%L_T2_to_m_s2)
36861 if ((CS%id_du_dt_visc_gl90 > 0) .or. (CS%id_GLwork > 0)) then
36870 call safe_alloc_ptr(ADp%du_dt_visc_gl90,IsdB,IedB,jsd,jed,nz)
36880 call safe_alloc_ptr(ADp%du_dt_visc,IsdB,IedB,jsd,jed,nz)
3689 endif
3690 CS%id_dv_dt_visc_gl90 = register_diag_field('ocean_model', 'dv_dt_visc_gl90', diag%axesCvL, Time, &
36911 'Meridional Acceleration from GL90 Vertical Viscosity', 'm s-2', conversion=US%L_T2_to_m_s2)
36921 if ((CS%id_dv_dt_visc_gl90 > 0) .or. (CS%id_GLwork > 0)) then
36930 call safe_alloc_ptr(ADp%dv_dt_visc_gl90,isd,ied,JsdB,JedB,nz)
36940 call safe_alloc_ptr(ADp%dv_dt_visc,isd,ied,JsdB,JedB,nz)
3695 endif
3696 CS%id_du_dt_str = register_diag_field('ocean_model', 'du_dt_str', diag%axesCuL, Time, &
36971 'Zonal Acceleration from Surface Wind Stresses', 'm s-2', conversion=US%L_T2_to_m_s2)
36981 if (CS%id_du_dt_str > 0) call safe_alloc_ptr(ADp%du_dt_str,IsdB,IedB,jsd,jed,nz)
3699 CS%id_dv_dt_str = register_diag_field('ocean_model', 'dv_dt_str', diag%axesCvL, Time, &
37001 'Meridional Acceleration from Surface Wind Stresses', 'm s-2', conversion=US%L_T2_to_m_s2)
37011 if (CS%id_dv_dt_str > 0) call safe_alloc_ptr(ADp%dv_dt_str,isd,ied,JsdB,JedB,nz)
3702
3703 CS%id_taux_bot = register_diag_field('ocean_model', 'taux_bot', diag%axesCu1, &
3704 Time, 'Zonal Bottom Stress from Ocean to Earth', &
37051 'Pa', conversion=US%RZ_to_kg_m2*US%L_T2_to_m_s2)
3706 CS%id_tauy_bot = register_diag_field('ocean_model', 'tauy_bot', diag%axesCv1, &
3707 Time, 'Meridional Bottom Stress from Ocean to Earth', &
37081 'Pa', conversion=US%RZ_to_kg_m2*US%L_T2_to_m_s2)
3709
3710 !CS%id_hf_du_dt_visc = register_diag_field('ocean_model', 'hf_du_dt_visc', diag%axesCuL, Time, &
3711 ! 'Fractional Thickness-weighted Zonal Acceleration from Vertical Viscosity', &
3712 ! 'm s-2', v_extensive=.true., conversion=US%L_T2_to_m_s2)
3713 !if (CS%id_hf_du_dt_visc > 0) then
3714 ! call safe_alloc_ptr(ADp%du_dt_visc,IsdB,IedB,jsd,jed,nz)
3715 ! call safe_alloc_ptr(ADp%diag_hfrac_u,IsdB,IedB,jsd,jed,nz)
3716 !endif
3717
3718 !CS%id_hf_dv_dt_visc = register_diag_field('ocean_model', 'hf_dv_dt_visc', diag%axesCvL, Time, &
3719 ! 'Fractional Thickness-weighted Meridional Acceleration from Vertical Viscosity', &
3720 ! 'm s-2', v_extensive=.true., conversion=US%L_T2_to_m_s2)
3721 !if (CS%id_hf_dv_dt_visc > 0) then
3722 ! call safe_alloc_ptr(ADp%dv_dt_visc,isd,ied,JsdB,JedB,nz)
3723 ! call safe_alloc_ptr(ADp%diag_hfrac_v,isd,ied,JsdB,JedB,nz)
3724 !endif
3725
3726 CS%id_hf_du_dt_visc_2d = register_diag_field('ocean_model', 'hf_du_dt_visc_2d', diag%axesCu1, Time, &
3727 'Depth-sum Fractional Thickness-weighted Zonal Acceleration from Vertical Viscosity', &
37281 'm s-2', conversion=US%L_T2_to_m_s2)
37291 if (CS%id_hf_du_dt_visc_2d > 0) then
37300 call safe_alloc_ptr(ADp%du_dt_visc,IsdB,IedB,jsd,jed,nz)
37310 call safe_alloc_ptr(ADp%diag_hfrac_u,IsdB,IedB,jsd,jed,nz)
3732 endif
3733
3734 CS%id_hf_dv_dt_visc_2d = register_diag_field('ocean_model', 'hf_dv_dt_visc_2d', diag%axesCv1, Time, &
3735 'Depth-sum Fractional Thickness-weighted Meridional Acceleration from Vertical Viscosity', &
37361 'm s-2', conversion=US%L_T2_to_m_s2)
37371 if (CS%id_hf_dv_dt_visc_2d > 0) then
37380 call safe_alloc_ptr(ADp%dv_dt_visc,isd,ied,JsdB,JedB,nz)
37390 call safe_alloc_ptr(ADp%diag_hfrac_v,isd,ied,JsdB,JedB,nz)
3740 endif
3741
3742 CS%id_h_du_dt_visc = register_diag_field('ocean_model', 'h_du_dt_visc', diag%axesCuL, Time, &
3743 'Thickness Multiplied Zonal Acceleration from Horizontal Viscosity', &
37441 'm2 s-2', conversion=GV%H_to_m*US%L_T2_to_m_s2)
37451 if (CS%id_h_du_dt_visc > 0) then
37460 call safe_alloc_ptr(ADp%du_dt_visc,IsdB,IedB,jsd,jed,nz)
37470 call safe_alloc_ptr(ADp%diag_hu,IsdB,IedB,jsd,jed,nz)
3748 endif
3749
3750 CS%id_h_dv_dt_visc = register_diag_field('ocean_model', 'h_dv_dt_visc', diag%axesCvL, Time, &
3751 'Thickness Multiplied Meridional Acceleration from Horizontal Viscosity', &
37521 'm2 s-2', conversion=GV%H_to_m*US%L_T2_to_m_s2)
37531 if (CS%id_h_dv_dt_visc > 0) then
37540 call safe_alloc_ptr(ADp%dv_dt_visc,isd,ied,JsdB,JedB,nz)
37550 call safe_alloc_ptr(ADp%diag_hv,isd,ied,JsdB,JedB,nz)
3756 endif
3757
3758 CS%id_h_du_dt_str = register_diag_field('ocean_model', 'h_du_dt_str', diag%axesCuL, Time, &
3759 'Thickness Multiplied Zonal Acceleration from Surface Wind Stresses', &
37601 'm2 s-2', conversion=GV%H_to_m*US%L_T2_to_m_s2)
37611 if (CS%id_h_du_dt_str > 0) then
37620 call safe_alloc_ptr(ADp%du_dt_str,IsdB,IedB,jsd,jed,nz)
37630 call safe_alloc_ptr(ADp%diag_hu,IsdB,IedB,jsd,jed,nz)
3764 endif
3765
3766 CS%id_h_dv_dt_str = register_diag_field('ocean_model', 'h_dv_dt_str', diag%axesCvL, Time, &
3767 'Thickness Multiplied Meridional Acceleration from Surface Wind Stresses', &
37681 'm2 s-2', conversion=GV%H_to_m*US%L_T2_to_m_s2)
37691 if (CS%id_h_dv_dt_str > 0) then
37700 call safe_alloc_ptr(ADp%dv_dt_str,isd,ied,JsdB,JedB,nz)
37710 call safe_alloc_ptr(ADp%diag_hv,isd,ied,JsdB,JedB,nz)
3772 endif
3773
3774 CS%id_du_dt_str_visc_rem = register_diag_field('ocean_model', 'du_dt_str_visc_rem', diag%axesCuL, Time, &
3775 'Zonal Acceleration from Surface Wind Stresses multiplied by viscous remnant', &
37761 'm s-2', conversion=US%L_T2_to_m_s2)
37771 if (CS%id_du_dt_str_visc_rem > 0) then
37780 call safe_alloc_ptr(ADp%du_dt_str,IsdB,IedB,jsd,jed,nz)
37790 call safe_alloc_ptr(ADp%visc_rem_u,IsdB,IedB,jsd,jed,nz)
3780 endif
3781
3782 CS%id_dv_dt_str_visc_rem = register_diag_field('ocean_model', 'dv_dt_str_visc_rem', diag%axesCvL, Time, &
3783 'Meridional Acceleration from Surface Wind Stresses multiplied by viscous remnant', &
37841 'm s-2', conversion=US%L_T2_to_m_s2)
37851 if (CS%id_dv_dt_str_visc_rem > 0) then
37860 call safe_alloc_ptr(ADp%dv_dt_str,isd,ied,JsdB,JedB,nz)
37870 call safe_alloc_ptr(ADp%visc_rem_v,isd,ied,JsdB,JedB,nz)
3788 endif
3789
37901 if ((len_trim(CS%u_trunc_file) > 0) .or. (len_trim(CS%v_trunc_file) > 0)) &
37910 call PointAccel_init(MIS, Time, G, param_file, diag, dirs, CS%PointAccel_CSp)
3792
37931end subroutine vertvisc_init
3794
3795!> Update the CFL truncation value as a function of time.
3796!! If called with the optional argument activate=.true., record the
3797!! value of Time as the beginning of the ramp period.
379825subroutine updateCFLtruncationValue(Time, CS, US, activate)
3799 type(time_type), target, intent(in) :: Time !< Current model time
3800 type(vertvisc_CS), pointer :: CS !< Vertical viscosity control structure
3801 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
3802 logical, optional, intent(in) :: activate !< Specify whether to record the value of
3803 !! Time as the beginning of the ramp period
3804
3805 ! Local variables
3806 real :: deltaTime ! The time since CS%rampStartTime [T ~> s], which may be negative.
3807 real :: wghtA ! The relative weight of the final value [nondim]
3808 character(len=12) :: msg
3809
381025 if (CS%truncRampTime==0.) return ! This indicates to ramping is turned off
3811
3812 ! We use the optional argument to indicate this Time should be recorded as the
3813 ! beginning of the ramp-up period.
38140 if (present(activate)) then
38150 if (activate) then
38160 CS%rampStartTime = Time ! Record the current time
38170 CS%CFLrampingIsActivated = .true.
3818 endif
3819 endif
38200 if (.not.CS%CFLrampingIsActivated) return
38210 deltaTime = max(0., time_minus_signed(Time, CS%rampStartTime, scale=US%s_to_T))
38220 if (deltaTime >= CS%truncRampTime) then
38230 CS%CFL_trunc = CS%CFL_truncE
38240 CS%truncRampTime = 0. ! This turns off ramping after this call
3825 else
38260 wghtA = min( 1., deltaTime / CS%truncRampTime ) ! Linear profile in time
3827 !wghtA = wghtA*wghtA ! Convert linear profile to parabolic profile in time
3828 !wghtA = wghtA*wghtA*(3. - 2.*wghtA) ! Convert linear profile to cosine profile
38290 wghtA = 1. - ( (1. - wghtA)**2 ) ! Convert linear profile to inverted parabolic profile
38300 CS%CFL_trunc = CS%CFL_truncS + wghtA * ( CS%CFL_truncE - CS%CFL_truncS )
3831 endif
38320 write(msg(1:12),'(es12.3)') CS%CFL_trunc
38330 call MOM_error(NOTE, "MOM_vert_friction: updateCFLtruncationValue set CFL limit to "//trim(msg))
3834end subroutine updateCFLtruncationValue
3835
3836!> Clean up and deallocate the vertical friction module
38371subroutine vertvisc_end(CS)
3838 type(vertvisc_CS), intent(inout) :: CS !< Vertical viscosity control structure that
3839 !! will be deallocated in this subroutine.
3840
38411 if ((len_trim(CS%u_trunc_file) > 0) .or. (len_trim(CS%v_trunc_file) > 0)) &
38420 deallocate(CS%PointAccel_CSp)
3843
38441 DEALLOC_(CS%a_u) ; DEALLOC_(CS%h_u)
38451 DEALLOC_(CS%a_v) ; DEALLOC_(CS%h_v)
38461 if (associated(CS%a1_shelf_u)) deallocate(CS%a1_shelf_u)
38471 if (associated(CS%a1_shelf_v)) deallocate(CS%a1_shelf_v)
38481 if (allocated(CS%kappa_gl90_2d)) deallocate(CS%kappa_gl90_2d)
38491end subroutine vertvisc_end
3850
3851!> \namespace mom_vert_friction
3852!! \author Robert Hallberg
3853!! \date April 1994 - October 2006
3854!!
3855!! The vertical diffusion of momentum is fully implicit. This is
3856!! necessary to allow for vanishingly small layers. The coupling
3857!! is based on the distance between the centers of adjacent layers,
3858!! except where a layer is close to the bottom compared with a
3859!! bottom boundary layer thickness when a bottom drag law is used.
3860!! A stress top b.c. and a no slip bottom b.c. are used. There
3861!! is no limit on the time step for vertvisc.
3862!!
3863!! Near the bottom, the horizontal thickness interpolation scheme
3864!! changes to an upwind biased estimate to control the effect of
3865!! spurious Montgomery potential gradients at the bottom where
3866!! nearly massless layers layers ride over the topography. Within a
3867!! few boundary layer depths of the bottom, the harmonic mean
3868!! thickness (i.e. (2 h+ h-) / (h+ + h-) ) is used if the velocity
3869!! is from the thinner side and the arithmetic mean thickness
3870!! (i.e. (h+ + h-)/2) is used if the velocity is from the thicker
3871!! side. Both of these thickness estimates are second order
3872!! accurate. Above this the arithmetic mean thickness is used.
3873!!
3874!! In addition, vertvisc truncates any velocity component that exceeds a
3875!! maximum CFL number to a fraction of this value. This basically keeps
3876!! instabilities spatially localized. The number of times the velocity is
3877!! truncated is reported each time the energies are saved, and if
3878!! exceeds CS%Maxtrunc the model will stop itself and change the time
3879!! to a large value. This has proven very useful in (1) diagnosing
3880!! model failures and (2) letting the model settle down to a
3881!! meaningful integration from a poorly specified initial condition.
3882!!
3883!! The same code is used for the two velocity components, by
3884!! indirectly referencing the velocities and defining a handful of
3885!! direction-specific defined variables.
3886!!
3887!! Macros written all in capital letters are defined in MOM_memory.h.
3888!!
3889!! A small fragment of the grid is shown below:
3890!! \verbatim
3891!! j+1 x ^ x ^ x At x: q
3892!! j+1 > o > o > At ^: v, frhatv, tauy
3893!! j x ^ x ^ x At >: u, frhatu, taux
3894!! j > o > o > At o: h
3895!! j-1 x ^ x ^ x
3896!! i-1 i i+1 At x & ^:
3897!! i i+1 At > & o:
3898!! \endverbatim
3899!!
3900!! The boundaries always run through q grid points (x).
39010end module MOM_vert_friction