← back to index

src/tracer/MOM_tracer_hor_diff.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#include "do_concurrent_compat.h"
5
6!> Main routine for lateral (along surface or neutral) diffusion of tracers
7module MOM_tracer_hor_diff
8
9use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end
10use MOM_cpu_clock, only : CLOCK_MODULE, CLOCK_ROUTINE
11use MOM_diag_mediator, only : post_data, diag_ctrl
12use MOM_diag_mediator, only : register_diag_field, safe_alloc_ptr, time_type
13use MOM_domains, only : sum_across_PEs, max_across_PEs
14use MOM_domains, only : create_group_pass, do_group_pass, group_pass_type
15use MOM_domains, only : pass_vector
16use MOM_debugging, only : hchksum, uvchksum
17use MOM_diabatic_driver, only : diabatic_CS
18use MOM_EOS, only : calculate_density, EOS_type, EOS_domain
19use MOM_error_handler, only : MOM_error, FATAL, WARNING, MOM_mesg, is_root_pe
20use MOM_error_handler, only : MOM_set_verbosity, callTree_showQuery
21use MOM_error_handler, only : callTree_enter, callTree_leave, callTree_waypoint
22use MOM_file_parser, only : get_param, log_version, param_file_type
23use MOM_grid, only : ocean_grid_type
24use MOM_lateral_mixing_coeffs, only : VarMix_CS
25use MOM_MEKE_types, only : MEKE_type
26use MOM_neutral_diffusion, only : neutral_diffusion_init, neutral_diffusion_end
27use MOM_neutral_diffusion, only : neutral_diffusion_CS
28use MOM_neutral_diffusion, only : neutral_diffusion_calc_coeffs, neutral_diffusion
29use MOM_hor_bnd_diffusion, only : hbd_CS, hor_bnd_diffusion_init
30use MOM_hor_bnd_diffusion, only : hor_bnd_diffusion, hor_bnd_diffusion_end
31use MOM_tracer_registry, only : tracer_registry_type, tracer_type, MOM_tracer_chksum
32use MOM_unit_scaling, only : unit_scale_type
33use MOM_variables, only : thermo_var_ptrs, vertvisc_type
34use MOM_verticalGrid, only : verticalGrid_type
35
36implicit none ; private
37
38#include <MOM_memory.h>
39
40public tracer_hordiff, tracer_hor_diff_init, tracer_hor_diff_end
41
42!> The control structure for along-layer and epineutral tracer diffusion
43type, public :: tracer_hor_diff_CS ; private
44 real :: KhTr !< The along-isopycnal tracer diffusivity [L2 T-1 ~> m2 s-1].
45 real :: KhTr_Slope_Cff !< The non-dimensional coefficient in KhTr formula [nondim]
46 real :: KhTr_min !< Minimum along-isopycnal tracer diffusivity [L2 T-1 ~> m2 s-1].
47 real :: KhTr_max !< Maximum along-isopycnal tracer diffusivity [L2 T-1 ~> m2 s-1].
48 real :: KhTr_passivity_coeff !< Passivity coefficient that scales Rd/dx (default = 0)
49 !! where passivity is the ratio between along-isopycnal
50 !! tracer mixing and thickness mixing [nondim]
51 real :: KhTr_passivity_min !< Passivity minimum (default = 1/2) [nondim]
52 real :: ML_KhTR_scale !< With Diffuse_ML_interior, the ratio of the
53 !! truly horizontal diffusivity in the mixed
54 !! layer to the epipycnal diffusivity [nondim].
55 real :: max_diff_CFL !< If positive, locally limit the along-isopycnal
56 !! tracer diffusivity to keep the diffusive CFL
57 !! locally at or below this value [nondim].
58 logical :: KhTr_use_vert_struct !< If true, uses the equivalent barotropic structure
59 !! as the vertical structure of tracer diffusivity.
60 logical :: full_depth_khtr_min !< If true, KHTR_MIN is enforced throughout the whole water column.
61 !! Otherwise, KHTR_MIN is only enforced at the surface. This parameter
62 !! is only available when KHTR_USE_EBT_STRUCT=True and KHTR_MIN>0.
63 logical :: Diffuse_ML_interior !< If true, diffuse along isopycnals between
64 !! the mixed layer and the interior.
65 logical :: check_diffusive_CFL !< If true, automatically iterate the diffusion
66 !! to ensure that the diffusive equivalent of
67 !! the CFL limit is not violated.
68 logical :: use_neutral_diffusion !< If true, use the neutral_diffusion module from within
69 !! tracer_hor_diff.
70 logical :: use_hor_bnd_diffusion !< If true, use the hor_bnd_diffusion module from within
71 !! tracer_hor_diff.
72 logical :: recalc_neutral_surf !< If true, recalculate the neutral surfaces if CFL has been
73 !! exceeded
74 logical :: limit_bug !< If true and the answer date is 20240330 or below, use a
75 !! rotational symmetry breaking bug when limiting the tracer
76 !! properties in tracer_epipycnal_ML_diff.
77 integer :: answer_date !< The vintage of the order of arithmetic to use for the tracer
78 !! diffusion. Values of 20240330 or below recover the answers
79 !! from the original form of this code, while higher values use
80 !! mathematically equivalent expressions that recover rotational symmetry
81 !! when DIFFUSE_ML_TO_INTERIOR is true.
82 type(neutral_diffusion_CS), pointer :: neutral_diffusion_CSp => NULL() !< Control structure for neutral diffusion.
83 type(hbd_CS), pointer :: hor_bnd_diffusion_CSp => NULL() !< Control structure for
84 !! horizontal boundary diffusion.
85 type(diag_ctrl), pointer :: diag => NULL() !< A structure that is used to
86 !! regulate the timing of diagnostic output.
87 logical :: debug !< If true, write verbose checksums for debugging purposes.
88 logical :: show_call_tree !< Display the call tree while running. Set by VERBOSITY level.
89 logical :: first_call = .true. !< This is true until after the first call
90 !>@{ Diagnostic IDs
91 integer :: id_KhTr_u = -1
92 integer :: id_KhTr_v = -1
93 integer :: id_KhTr_h = -1
94 integer :: id_CFL = -1
95 integer :: id_khdt_x = -1
96 integer :: id_khdt_y = -1
97 !>@}
98
99 type(group_pass_type) :: pass_t !< For group halo pass, used in both
100 !! tracer_hordiff and tracer_epipycnal_ML_diff
101end type tracer_hor_diff_CS
102
103!> A type that can be used to create arrays of pointers to 2D arrays
104type p2d
105 real, dimension(:,:), pointer :: p => NULL() !< A pointer to a 2D array of reals [various]
106end type p2d
107!> A type that can be used to create arrays of pointers to 2D integer arrays
108type p2di
109 integer, dimension(:,:), pointer :: p => NULL() !< A pointer to a 2D array of integers
110end type p2di
111
112!>@{ CPU time clocks
113integer :: id_clock_diffuse, id_clock_epimix, id_clock_pass, id_clock_sync
114!>@}
115
116contains
117
118!> Compute along-coordinate diffusion of all tracers
119!! using the diffusivity in CS%KhTr, or using space-dependent diffusivity.
120!! Multiple iterations are used (if necessary) so that there is no limit
121!! on the acceptable time increment.
1220subroutine tracer_hordiff(h, dt, MEKE, VarMix, visc, G, GV, US, CS, Reg, tv, do_online_flag, read_khdt_x, read_khdt_y)
123 type(ocean_grid_type), intent(inout) :: G !< Grid type
124 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
125 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
126 intent(in) :: h !< Layer thickness [H ~> m or kg m-2]
127 real, intent(in) :: dt !< time step [T ~> s]
128 type(MEKE_type), intent(in) :: MEKE !< MEKE fields
129 type(VarMix_CS), intent(in) :: VarMix !< Variable mixing type
130 type(vertvisc_type), intent(in) :: visc !< Structure with vertical viscosities,
131 !! boundary layer properties and related fields
132 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
133 type(tracer_hor_diff_CS), pointer :: CS !< module control structure
134 type(tracer_registry_type), pointer :: Reg !< registered tracers
135 type(thermo_var_ptrs), intent(in) :: tv !< A structure containing pointers to any available
136 !! thermodynamic fields, including potential temp and
137 !! salinity or mixed layer density. Absent fields have
138 !! NULL ptrs, and these may (probably will) point to
139 !! some of the same arrays as Tr does. tv is required
140 !! for epipycnal mixing between mixed layer and the interior.
141 ! Optional inputs for offline tracer transport
142 logical, optional, intent(in) :: do_online_flag !< If present and true, do online
143 !! tracer transport with stored velocities.
144 ! The next two arguments do not appear to be used anywhere.
145 real, dimension(SZIB_(G),SZJ_(G)), &
146 optional, intent(in) :: read_khdt_x !< If present, these are the zonal diffusivities
147 !! times a timestep from a previous run [L2 ~> m2]
148 real, dimension(SZI_(G),SZJB_(G)), &
149 optional, intent(in) :: read_khdt_y !< If present, these are the meridional diffusivities
150 !! times a timestep from a previous run [L2 ~> m2]
151
152
153 real, dimension(SZI_(G),SZJ_(G)) :: &
1542 Ihdxdy, & ! The inverse of the volume or mass of fluid in a layer in a
155 ! grid cell [H-1 L-2 ~> m-3 or kg-1].
1561 CFL, & ! A diffusive CFL number for each cell [nondim].
1572 dTr ! The change in a tracer's concentration, in units of concentration [Conc].
158
1592 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: Kh_h
160 ! The tracer diffusivity averaged to tracer points [L2 T-1 ~> m2 s-1].
161 real, dimension(SZIB_(G),SZJ_(G)) :: &
1622 khdt_x ! The value of Khtr*dt times the open face width divided by
163 ! the distance between adjacent tracer points [L2 ~> m2].
164 real, dimension(SZI_(G),SZJB_(G)) :: &
1652 khdt_y ! The value of Khtr*dt times the open face width divided by
166 ! the distance between adjacent tracer points [L2 ~> m2].
167 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1) :: &
1682 Coef_x, & ! The coefficients relating zonal tracer differences to time-integrated
169 ! fluxes, in [L2 ~> m2] for some schemes and [H L2 ~> m3 or kg] for others.
1702 Kh_u ! Tracer mixing coefficient at u-points [L2 T-1 ~> m2 s-1].
171 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1) :: &
1722 Coef_y, & ! The coefficients relating meridional tracer differences to time-integrated
173 ! fluxes, in [L2 ~> m2] for some schemes and [H L2 ~> m3 or kg] for others.
1741 Kh_v ! Tracer mixing coefficient at u-points [L2 T-1 ~> m2 s-1].
175
176 real :: khdt_max ! The local limiting value of khdt_x or khdt_y [L2 ~> m2].
177 real :: Coef_min ! The local limiting value of Coef_x or Coef_y, in [L2 ~> m2] for some
178 ! schemes and [H L2 ~> m3 or kg] for others.
179 real :: max_CFL ! The global maximum of the diffusive CFL number [nondim]
180 logical :: use_VarMix, Resoln_scaled, do_online, use_Eady
181 integer :: i, j, k, m, is, ie, js, je, nz, ntr, itt, num_itts
182 real :: I_numitts ! The inverse of the number of iterations, num_itts [nondim]
183 real :: scale ! The fraction of khdt_x or khdt_y that is applied in this
184 ! layer for this iteration [nondim].
185 real :: Idt ! The inverse of the time step [T-1 ~> s-1].
186 real :: h_neglect ! A thickness that is so small it is usually lost
187 ! in roundoff and can be neglected [H ~> m or kg m-2].
188 real :: Kh_loc ! The local value of Kh [L2 T-1 ~> m2 s-1].
189 real :: Res_Fn ! The local value of the resolution function [nondim].
190 real :: Rd_dx ! The local value of deformation radius over grid-spacing [nondim].
191 real :: normalize ! normalization used for diagnostic Kh_h [nondim]; diffusivity averaged to h-points.
192 real :: MEKE_KhTr_fac
193
1941 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
195
1961 do_online = .true.
1971 if (present(do_online_flag)) do_online = do_online_flag
198
1991 if (.not. associated(CS)) call MOM_error(FATAL, "MOM_tracer_hor_diff: "// &
2000 "register_tracer must be called before tracer_hordiff.")
2011 if (.not. associated(Reg)) call MOM_error(FATAL, "MOM_tracer_hor_diff: "// &
2020 "register_tracer must be called before tracer_hordiff.")
2031 if (Reg%ntr == 0 .or. (CS%KhTr <= 0.0 .and. .not. VarMix%use_variable_mixing)) return
204
2051 if (CS%show_call_tree) call callTree_enter("tracer_hordiff(), MOM_tracer_hor_diff.F90")
206
2071 call cpu_clock_begin(id_clock_diffuse)
208
209 !$omp target enter data map(to: Reg, Reg%Tr, CS) map(alloc: khdt_x, khdt_y, kh_u, kh_v)
210 !$ do m = 1, Reg%ntr
211 !$omp target enter data map(to: Reg%Tr(m)%t)
212 !$omp target enter data map(to: Reg%Tr(m)%df_x) if(associated(Reg%Tr(m)%df_x))
213 !$omp target enter data map(to: Reg%Tr(m)%df_y) if(associated(Reg%Tr(m)%df_y))
214 !$omp target enter data map(to: Reg%Tr(m)%df2d_x) if(associated(Reg%Tr(m)%df2d_x))
215 !$omp target enter data map(to: Reg%Tr(m)%df2d_y) if(associated(Reg%Tr(m)%df2d_y))
216 !$ enddo
217
2181 ntr = Reg%ntr
2191 Idt = 1.0 / dt
2201 h_neglect = GV%H_subroundoff
221
2221 if (CS%Diffuse_ML_interior .and. CS%first_call) then ; if (is_root_pe()) then
2234 do m=1,ntr ; if (associated(Reg%Tr(m)%df_x) .or. associated(Reg%Tr(m)%df_y)) &
224 call MOM_error(WARNING, "tracer_hordiff: Tracer "//trim(Reg%Tr(m)%name)// &
225 " has associated 3-d diffusive flux diagnostics. These are not "//&
226 "valid when DIFFUSE_ML_TO_INTERIOR is defined. Use 2-d tracer "//&
2271 "diffusion diagnostics instead to get accurate total fluxes.")
228 enddo
229 endif ; endif
2301 CS%first_call = .false.
231
2321 if (CS%debug) call MOM_tracer_chksum("Before tracer diffusion ", Reg, G)
233
2341 use_VarMix = .false. ; Resoln_scaled = .false. ; use_Eady = .false.
2351 if (VarMix%use_variable_mixing) then
2361 use_VarMix = VarMix%use_variable_mixing
2371 Resoln_scaled = VarMix%Resoln_scaled_KhTr
2381 use_Eady = CS%KhTr_Slope_Cff > 0.
2391 CS%KhTr_use_vert_struct = allocated(VarMix%khtr_struct)
240 endif
241
2421 call cpu_clock_begin(id_clock_pass)
2434 do m=1,ntr
2444 call create_group_pass(CS%pass_t, Reg%Tr(m)%t(:,:,:), G%Domain)
245 enddo
2461 call cpu_clock_end(id_clock_pass)
247
2481 if (CS%show_call_tree) call callTree_waypoint("Calculating diffusivity (tracer_hordiff)")
249
2501 if (do_online) then
2511 if (use_VarMix) then
2521 MEKE_KhTr_fac = MEKE%KhTr_fac
253 !$omp target enter data map(to: VarMix, VarMix%SN_u, VarMix%L2u, VarMix%Res_fn_h, &
254 !$omp VarMix%Rd_dx_h, MEKE, MEKE%Kh)
2551 do concurrent (j=js:je, I=is-1:ie)
25664980 Kh_loc = CS%KhTr
25764980 if (use_Eady) Kh_loc = Kh_loc + CS%KhTr_Slope_Cff*VarMix%L2u(I,j)*VarMix%SN_u(I,j)
25864980 if (allocated(MEKE%Kh)) &
2590 Kh_loc = Kh_loc + MEKE_KhTr_fac*sqrt(MEKE%Kh(i,j)*MEKE%Kh(i+1,j))
26064980 if (CS%KhTr_max > 0.) Kh_loc = min(Kh_loc, CS%KhTr_max)
26164980 if (Resoln_scaled) &
26264980 Kh_loc = Kh_loc * 0.5*(VarMix%Res_fn_h(i,j) + VarMix%Res_fn_h(i+1,j))
26364980 Kh_u(I,j,1) = max(Kh_loc, CS%KhTr_min)
26465342 if (CS%KhTr_passivity_coeff>0.) then ! Apply passivity
2650 Rd_dx=0.5*( VarMix%Rd_dx_h(i,j)+VarMix%Rd_dx_h(i+1,j) ) ! Rd/dx at u-points
2660 Kh_loc = Kh_u(I,j,1)*max( CS%KhTr_passivity_min, CS%KhTr_passivity_coeff*Rd_dx )
2670 if (CS%KhTr_max > 0.) Kh_loc = min(Kh_loc, CS%KhTr_max) ! Re-apply max
2680 Kh_u(I,j,1) = max(Kh_loc, CS%KhTr_min) ! Re-apply min
269 endif
270 enddo
2711 do concurrent (J=js-1:je, i=is:ie)
27265160 Kh_loc = CS%KhTr
27365160 if (use_Eady) Kh_loc = Kh_loc + CS%KhTr_Slope_Cff*VarMix%L2v(i,J)*VarMix%SN_v(i,J)
27465160 if (allocated(MEKE%Kh)) &
2750 Kh_loc = Kh_loc + MEKE_KhTr_fac*sqrt(MEKE%Kh(i,j)*MEKE%Kh(i,j+1))
27665160 if (CS%KhTr_max > 0.) Kh_loc = min(Kh_loc, CS%KhTr_max)
27765160 if (Resoln_scaled) &
27865160 Kh_loc = Kh_loc * 0.5*(VarMix%Res_fn_h(i,j) + VarMix%Res_fn_h(i,j+1))
27965160 Kh_v(i,J,1) = max(Kh_loc, CS%KhTr_min)
28065521 if (CS%KhTr_passivity_coeff>0.) then ! Apply passivity
2810 Rd_dx = 0.5*( VarMix%Rd_dx_h(i,j)+VarMix%Rd_dx_h(i,j+1) ) ! Rd/dx at v-points
2820 Kh_loc = Kh_v(i,J,1)*max( CS%KhTr_passivity_min, CS%KhTr_passivity_coeff*Rd_dx )
2830 if (CS%KhTr_max > 0.) Kh_loc = min(Kh_loc, CS%KhTr_max) ! Re-apply max
2840 Kh_v(i,J,1) = max(Kh_loc, CS%KhTr_min) ! Re-apply min
285 endif
286 enddo
287 !$omp target exit data map(release: VarMix, VarMix%SN_u, VarMix%L2u, VarMix%SN_v, &
288 !$omp VarMix%L2v, VarMix%Res_fn_h, VarMix%Rd_dx_h, MEKE, MEKE%Kh)
289
2901 do concurrent (j=js:je, I=is-1:ie)
29165342 khdt_x(I,j) = dt*(Kh_u(I,j,1)*(G%dy_Cu(I,j)*G%IdxCu(I,j)))
292 enddo
2931 do concurrent (J=js-1:je, i=is:ie)
29465521 khdt_y(i,J) = dt*(Kh_v(i,J,1)*(G%dx_Cv(i,J)*G%IdyCv(i,J)))
295 enddo
2960 elseif (Resoln_scaled) then
297 !$OMP parallel do default(shared) private(Res_fn)
2980 do j=js,je ; do I=is-1,ie
2990 Res_fn = 0.5 * (VarMix%Res_fn_h(i,j) + VarMix%Res_fn_h(i+1,j))
3000 Kh_u(I,j,1) = max(CS%KhTr * Res_fn, CS%KhTr_min)
3010 khdt_x(I,j) = dt*(CS%KhTr*(G%dy_Cu(I,j)*G%IdxCu(I,j))) * Res_fn
302 enddo ; enddo
303 !$OMP parallel do default(shared) private(Res_fn)
3040 do J=js-1,je ; do i=is,ie
3050 Res_fn = 0.5*(VarMix%Res_fn_h(i,j) + VarMix%Res_fn_h(i,j+1))
3060 Kh_v(i,J,1) = max(CS%KhTr * Res_fn, CS%KhTr_min)
3070 khdt_y(i,J) = dt*(CS%KhTr*(G%dx_Cv(i,J)*G%IdyCv(i,J))) * Res_fn
308 enddo ; enddo
309 !$omp target update to(khdt_x, khdt_y, Kh_u, Kh_v)
310 else ! Use a simple constant diffusivity.
3110 if (CS%id_KhTr_u > 0) then
312 !$OMP parallel do default(shared)
3130 do j=js,je ; do I=is-1,ie
3140 Kh_u(I,j,1) = CS%KhTr
3150 khdt_x(I,j) = dt*(CS%KhTr*(G%dy_Cu(I,j)*G%IdxCu(I,j)))
316 enddo ; enddo
317 else
318 !$OMP parallel do default(shared)
3190 do j=js,je ; do I=is-1,ie
3200 khdt_x(I,j) = dt*(CS%KhTr*(G%dy_Cu(I,j)*G%IdxCu(I,j)))
321 enddo ; enddo
322 endif
3230 if (CS%id_KhTr_v > 0) then
324 !$OMP parallel do default(shared)
3250 do J=js-1,je ; do i=is,ie
3260 Kh_v(i,J,1) = CS%KhTr
3270 khdt_y(i,J) = dt*(CS%KhTr*(G%dx_Cv(i,J)*G%IdyCv(i,J)))
328 enddo ; enddo
329 else
330 !$OMP parallel do default(shared)
3310 do J=js-1,je ; do i=is,ie
3320 khdt_y(i,J) = dt*(CS%KhTr*(G%dx_Cv(i,J)*G%IdyCv(i,J)))
333 enddo ; enddo
334 endif
335 !$omp target update to(khdt_x, khdt_y, Kh_u, Kh_v)
336 endif ! VarMix
337
3381 if (CS%max_diff_CFL > 0.0) then
339 !$omp target update from(khdt_x, khdt_y, Kh_u, Kh_v)
3400 if ((CS%id_KhTr_u > 0) .or. (CS%id_KhTr_h > 0)) then
341 !$OMP parallel do default(shared) private(khdt_max)
3420 do j=js,je ; do I=is-1,ie
3430 khdt_max = 0.125*CS%max_diff_CFL * min(G%areaT(i,j), G%areaT(i+1,j))
3440 if (khdt_x(I,j) > khdt_max) then
3450 khdt_x(I,j) = khdt_max
3460 if (dt*(G%dy_Cu(I,j)*G%IdxCu(I,j)) > 0.0) &
3470 Kh_u(I,j,1) = khdt_x(I,j) / (dt*(G%dy_Cu(I,j)*G%IdxCu(I,j)))
348 endif
349 enddo ; enddo
350 else
351 !$OMP parallel do default(shared) private(khdt_max)
3520 do j=js,je ; do I=is-1,ie
3530 khdt_max = 0.125*CS%max_diff_CFL * min(G%areaT(i,j), G%areaT(i+1,j))
3540 khdt_x(I,j) = min(khdt_x(I,j), khdt_max)
355 enddo ; enddo
356 endif
3570 if ((CS%id_KhTr_v > 0) .or. (CS%id_KhTr_h > 0)) then
358 !$OMP parallel do default(shared) private(khdt_max)
3590 do J=js-1,je ; do i=is,ie
3600 khdt_max = 0.125*CS%max_diff_CFL * min(G%areaT(i,j), G%areaT(i,j+1))
3610 if (khdt_y(i,J) > khdt_max) then
3620 khdt_y(i,J) = khdt_max
3630 if (dt*(G%dx_Cv(i,J)*G%IdyCv(i,J)) > 0.0) &
3640 Kh_v(i,J,1) = khdt_y(i,J) / (dt*(G%dx_Cv(i,J)*G%IdyCv(i,J)))
365 endif
366 enddo ; enddo
367 else
368 !$OMP parallel do default(shared) private(khdt_max)
3690 do J=js-1,je ; do i=is,ie
3700 khdt_max = 0.125*CS%max_diff_CFL * min(G%areaT(i,j), G%areaT(i,j+1))
3710 khdt_y(i,J) = min(khdt_y(i,J), khdt_max)
372 enddo ; enddo
373 endif
374 !$omp target update to(khdt_x, khdt_y, Kh_u, Kh_v)
375 endif
376
377 else ! .not. do_online
378 !$OMP parallel do default(shared)
3790 do j=js,je ; do I=is-1,ie
3800 khdt_x(I,j) = read_khdt_x(I,j)
381 enddo ; enddo
382 !$OMP parallel do default(shared)
3830 do J=js-1,je ; do i=is,ie
3840 khdt_y(i,J) = read_khdt_y(i,J)
385 enddo ; enddo
3860 call pass_vector(khdt_x, khdt_y, G%Domain)
387 !$omp target update to(khdt_x, khdt_y, Kh_u, Kh_v)
388 endif ! do_online
389
3901 if (CS%check_diffusive_CFL) then
3910 if (CS%show_call_tree) call callTree_waypoint("Checking diffusive CFL (tracer_hordiff)")
3920 max_CFL = 0.0
3930 do j=js,je ; do i=is,ie
394 CFL(i,j) = 2.0*((khdt_x(I-1,j) + khdt_x(I,j)) + &
3950 (khdt_y(i,J-1) + khdt_y(i,J))) * G%IareaT(i,j)
3960 if (max_CFL < CFL(i,j)) max_CFL = CFL(i,j)
397 enddo ; enddo
3980 call cpu_clock_begin(id_clock_sync)
3990 call max_across_PEs(max_CFL)
4000 call cpu_clock_end(id_clock_sync)
4010 num_itts = max(1, ceiling(max_CFL - 4.0*EPSILON(max_CFL)))
4020 I_numitts = 1.0 / (real(num_itts))
4030 if (CS%id_CFL > 0) call post_data(CS%id_CFL, CFL, CS%diag)
4041 elseif (CS%max_diff_CFL > 0.0) then
4050 num_itts = max(1, ceiling(CS%max_diff_CFL - 4.0*EPSILON(CS%max_diff_CFL)))
4060 I_numitts = 1.0 / (real(num_itts))
407 else
4081 num_itts = 1 ; I_numitts = 1.0
409 endif
410
4114 do m=1,ntr
4123 if (associated(Reg%Tr(m)%df_x)) then
4130 do k=1,nz ; do j=js,je ; do I=is-1,ie
4140 Reg%Tr(m)%df_x(I,j,k) = 0.0
415 enddo ; enddo ; enddo
416 !$omp target update to(Reg%Tr(m)%df_x)
417 endif
4183 if (associated(Reg%Tr(m)%df_y)) then
4190 do k=1,nz ; do J=js-1,je ; do i=is,ie
4200 Reg%Tr(m)%df_y(i,J,k) = 0.0
421 enddo ; enddo ; enddo
422 !$omp target update to(Reg%Tr(m)%df_y)
423 endif
4243 if (associated(Reg%Tr(m)%df2d_x)) then
4250 do j=js,je ; do I=is-1,ie ; Reg%Tr(m)%df2d_x(I,j) = 0.0 ; enddo ; enddo
426 !$omp target update to(Reg%Tr(m)%df2d_x)
427 endif
4284 if (associated(Reg%Tr(m)%df2d_y)) then
4290 do J=js-1,je ; do i=is,ie ; Reg%Tr(m)%df2d_y(i,J) = 0.0 ; enddo ; enddo
430 !$omp target update to(Reg%Tr(m)%df2d_y)
431 endif
432 enddo
433
4341 if (CS%use_hor_bnd_diffusion) then
435 !$omp target update from(h)
436
4370 if (CS%show_call_tree) call callTree_waypoint("Calling horizontal boundary diffusion (tracer_hordiff)")
438 !$omp target update from(khdt_x, khdt_y)
4390 call do_group_pass(CS%pass_t, G%Domain, clock=id_clock_pass)
440
4410 do k=1,nz+1
4420 do J=js-1,je
4430 do i=is,ie
4440 Coef_y(i,J,K) = I_numitts * khdt_y(i,J)
445 enddo
446 enddo
447 enddo
4480 do k=1,nz+1
4490 do j=js,je
4500 do I=is-1,ie
4510 Coef_x(I,j,K) = I_numitts * khdt_x(I,j)
452 enddo
453 enddo
454 enddo
4550 if (CS%KhTr_use_vert_struct) then
4560 if (CS%full_depth_khtr_min) then
4570 do K=2,nz+1
4580 do J=js-1,je
4590 do i=is,ie
4600 Coef_y(i,J,K) = Coef_y(i,J,1) * 0.5 * ( VarMix%khtr_struct(i,j,k-1) + VarMix%khtr_struct(i,j+1,k-1) )
4610 Coef_min = I_numitts * dt * (CS%KhTr_min*(G%dx_Cv(i,J)*G%IdyCv(i,J)))
4620 Coef_y(i,J,K) = max(Coef_y(i,J,K), Coef_min)
463 enddo
464 enddo
465 enddo
4660 do k=2,nz+1
4670 do j=js,je
4680 do I=is-1,ie
4690 Coef_x(I,j,K) = Coef_x(I,j,1) * 0.5 * ( VarMix%khtr_struct(i,j,k-1) + VarMix%khtr_struct(i+1,j,k-1) )
4700 Coef_min = I_numitts * dt * (CS%KhTr_min*(G%dy_Cu(I,j)*G%IdxCu(I,j)))
4710 Coef_x(I,j,K) = max(Coef_x(I,j,K), Coef_min)
472 enddo
473 enddo
474 enddo
475 else
4760 do K=2,nz+1
4770 do J=js-1,je
4780 do i=is,ie
4790 Coef_y(i,J,K) = Coef_y(i,J,1) * 0.5 * ( VarMix%ebt_struct(i,j,k-1) + VarMix%ebt_struct(i,j+1,k-1) )
480 enddo
481 enddo
482 enddo
4830 do k=2,nz+1
4840 do j=js,je
4850 do I=is-1,ie
4860 Coef_x(I,j,K) = Coef_x(I,j,1) * 0.5 * ( VarMix%ebt_struct(i,j,k-1) + VarMix%ebt_struct(i+1,j,k-1) )
487 enddo
488 enddo
489 enddo
490 endif
491 endif
492
4930 do itt=1,num_itts
4940 if (CS%show_call_tree) call callTree_waypoint("Calling horizontal boundary diffusion (tracer_hordiff)",itt)
4950 if (itt>1) then ! Update halos for subsequent iterations
4960 call do_group_pass(CS%pass_t, G%Domain, clock=id_clock_pass)
497 endif
498 call hor_bnd_diffusion(G, GV, US, h, Coef_x, Coef_y, I_numitts*dt, Reg, visc, &
4990 CS%hor_bnd_diffusion_CSp)
500 enddo ! itt
501 endif
502
5031 if (CS%use_neutral_diffusion) then
504 !$omp target update from(h)
505
5060 if (CS%show_call_tree) call callTree_waypoint("Calling neutral diffusion coeffs (tracer_hordiff)")
507
5080 call do_group_pass(CS%pass_t, G%Domain, clock=id_clock_pass)
509 ! We are assuming that neutral surfaces do not evolve (much) as a result of multiple
510 !horizontal diffusion iterations. Otherwise the call to neutral_diffusion_calc_coeffs()
511 ! would be inside the itt-loop. -AJA
512
5130 if (associated(tv%p_surf)) then
5140 call neutral_diffusion_calc_coeffs(G, GV, US, h, tv%T, tv%S, visc, CS%neutral_diffusion_CSp, p_surf=tv%p_surf)
515 else
5160 call neutral_diffusion_calc_coeffs(G, GV, US, h, tv%T, tv%S, visc, CS%neutral_diffusion_CSp)
517 endif
518
5190 do k=1,nz+1
5200 do J=js-1,je
5210 do i=is,ie
5220 Coef_y(i,J,K) = I_numitts * khdt_y(i,J)
523 enddo
524 enddo
525 enddo
5260 do k=1,nz+1
5270 do j=js,je
5280 do I=is-1,ie
5290 Coef_x(I,j,K) = I_numitts * khdt_x(I,j)
530 enddo
531 enddo
532 enddo
5330 if (CS%KhTr_use_vert_struct) then
5340 do K=2,nz+1
5350 do J=js-1,je
5360 do i=is,ie
5370 Coef_y(i,J,K) = Coef_y(i,J,1) * 0.5 * ( VarMix%khtr_struct(i,j,k-1) + VarMix%khtr_struct(i,j+1,k-1) )
538 enddo
539 enddo
540 enddo
5410 do k=2,nz+1
5420 do j=js,je
5430 do I=is-1,ie
5440 Coef_x(I,j,K) = Coef_x(I,j,1) * 0.5 * ( VarMix%khtr_struct(i,j,k-1) + VarMix%khtr_struct(i+1,j,k-1) )
545 enddo
546 enddo
547 enddo
548 endif
549
5500 do itt=1,num_itts
5510 if (CS%show_call_tree) call callTree_waypoint("Calling neutral diffusion (tracer_hordiff)",itt)
5520 if (itt>1) then ! Update halos for subsequent iterations
5530 call do_group_pass(CS%pass_t, G%Domain, clock=id_clock_pass)
5540 if (CS%recalc_neutral_surf) then
5550 if (associated(tv%p_surf)) then
556 call neutral_diffusion_calc_coeffs(G, GV, US, h, tv%T, tv%S, visc, CS%neutral_diffusion_CSp, &
5570 p_surf=tv%p_surf)
558 else
5590 call neutral_diffusion_calc_coeffs(G, GV, US, h, tv%T, tv%S, visc, CS%neutral_diffusion_CSp)
560 endif
561 endif
562 endif
5630 call neutral_diffusion(G, GV, h, Coef_x, Coef_y, I_numitts*dt, Reg, US, CS%neutral_diffusion_CSp)
564 enddo ! itt
565
566 else ! following if not using neutral diffusion, but instead along-surface diffusion
567
5681 if (CS%show_call_tree) call callTree_waypoint("Calculating horizontal diffusion (tracer_hordiff)")
569 !$omp target enter data map(alloc: dTr, Ihdxdy)
570 !$omp target enter data map(to: Coef_x, Coef_y) if(CS%use_hor_bnd_diffusion) ! copy from non-ported loops above
571 !$omp target enter data map(alloc: Coef_x, Coef_y) if(.not.CS%use_hor_bnd_diffusion)
5722 do itt=1,num_itts
5731 call do_group_pass(CS%pass_t, G%Domain, clock=id_clock_pass, omp_offload=.true.)
574 ! loop should probably be reordered
57523 do k=1,nz
57622 scale = I_numitts
57722 if (CS%Diffuse_ML_interior) then
57822 if (k<=GV%nkml) then
5792 if (CS%ML_KhTr_scale <= 0.0) cycle
5800 scale = I_numitts * CS%ML_KhTr_scale
581 endif
58220 if ((k>GV%nkml) .and. (k<=GV%nk_rho_varies)) cycle
583 endif
584
58518 do concurrent (J=js-1:je, i=is:ie)
586 Coef_y(i,J,1) = ((scale * khdt_y(i,J))*2.0*(h(i,j,k)*h(i,j+1,k))) / &
5871179378 (h(i,j,k)+h(i,j+1,k)+h_neglect)
588 enddo
589
59018 do concurrent (j=js:je)
5913240 do concurrent (I=is-1:ie)
592 Coef_x(I,j,1) = ((scale * khdt_x(I,j))*2.0*(h(i,j,k)*h(i+1,j,k))) / &
5931172880 (h(i,j,k)+h(i+1,j,k)+h_neglect)
594 enddo
595
5963258 do concurrent (i=is:ie)
5971169640 Ihdxdy(i,j) = G%IareaT(i,j) / (h(i,j,k)+h_neglect)
598 enddo
599 enddo
600
60173 do m=1,ntr
60254 do concurrent (j=js:je, i=is:ie)
603 dTr(i,j) = Ihdxdy(i,j) * &
604 ( ((Coef_x(I-1,j,1) * (Reg%Tr(m)%t(i-1,j,k) - Reg%Tr(m)%t(i,j,k))) - &
605 (Coef_x(I,j,1) * (Reg%Tr(m)%t(i,j,k) - Reg%Tr(m)%t(i+1,j,k)))) + &
606 ((Coef_y(i,J-1,1) * (Reg%Tr(m)%t(i,j-1,k) - Reg%Tr(m)%t(i,j,k))) - &
6073518694 (Coef_y(i,J,1) * (Reg%Tr(m)%t(i,j,k) - Reg%Tr(m)%t(i,j+1,k)))) )
608 enddo
60954 if (associated(Reg%Tr(m)%df_x)) then ; do j=js,je ; do I=G%IscB,G%IecB
610 Reg%Tr(m)%df_x(I,j,k) = Reg%Tr(m)%df_x(I,j,k) + Coef_x(I,j,1) &
6110 * (Reg%Tr(m)%t(i,j,k) - Reg%Tr(m)%t(i+1,j,k)) * Idt
612 enddo ; enddo ; endif
61354 if (associated(Reg%Tr(m)%df_y)) then ; do J=G%JscB,G%JecB ; do i=is,ie
614 Reg%Tr(m)%df_y(i,J,k) = Reg%Tr(m)%df_y(i,J,k) + Coef_y(i,J,1) &
6150 * (Reg%Tr(m)%t(i,j,k) - Reg%Tr(m)%t(i,j+1,k)) * Idt
616 enddo ; enddo ; endif
61754 if (associated(Reg%Tr(m)%df2d_x)) then ; do j=js,je ; do I=G%IscB,G%IecB
618 Reg%Tr(m)%df2d_x(I,j) = Reg%Tr(m)%df2d_x(I,j) + Coef_x(I,j,1) &
6190 * (Reg%Tr(m)%t(i,j,k) - Reg%Tr(m)%t(i+1,j,k)) * Idt
620 enddo ; enddo ; endif
62154 if (associated(Reg%Tr(m)%df2d_y)) then ; do J=G%JscB,G%JecB ; do i=is,ie
622 Reg%Tr(m)%df2d_y(i,J) = Reg%Tr(m)%df2d_y(i,J) + Coef_y(i,J,1) &
6230 * (Reg%Tr(m)%t(i,j,k) - Reg%Tr(m)%t(i,j+1,k)) * Idt
624 enddo ; enddo ; endif
62518 do concurrent (j=js:je, i=is:ie)
6263518694 Reg%Tr(m)%t(i,j,k) = Reg%Tr(m)%t(i,j,k) + dTr(i,j)
627 enddo
628 enddo
629
630 enddo ! End of k loop.
631
632 ! Do user controlled underflow of the tracer concentrations.
6335 do m=1,ntr ; if (Reg%Tr(m)%conc_underflow > 0.0) then
6340 do concurrent (k=1:nz, j=js:je, i=is:ie, abs(Reg%Tr(m)%t(i,j,k)) < Reg%Tr(m)%conc_underflow)
6350 Reg%Tr(m)%t(i,j,k) = 0.0
636 enddo
637 endif ; enddo
638
639 enddo ! End of "while" loop.
640 !$omp target exit data map(release: dTr, Ihdxdy, Coef_x, Coef_y)
641 endif ! endif for CS%use_neutral_diffusion
6421 call cpu_clock_end(id_clock_diffuse)
643
6441 if (CS%Diffuse_ML_interior) then
6451 if (CS%show_call_tree) call callTree_waypoint("Calling epipycnal_ML_diff (tracer_hordiff)")
6461 if (CS%debug) call MOM_tracer_chksum("Before epipycnal diff ", Reg, G)
647
6481 call cpu_clock_begin(id_clock_epimix)
649 call tracer_epipycnal_ML_diff(h, dt, Reg%Tr, ntr, khdt_x, khdt_y, G, GV, US, &
6501 CS, tv, num_itts)
6511 call cpu_clock_end(id_clock_epimix)
652 endif
653 !$ do m = 1, Reg%ntr
654 !$omp target exit data map(from: Reg%Tr(m)%t)
655 !$omp target exit data map(from: Reg%Tr(m)%df_x) if(associated(Reg%Tr(m)%df_x))
656 !$omp target exit data map(from: Reg%Tr(m)%df_y) if(associated(Reg%Tr(m)%df_y))
657 !$omp target exit data map(from: Reg%Tr(m)%df2d_x) if(associated(Reg%Tr(m)%df2d_x))
658 !$omp target exit data map(from: Reg%Tr(m)%df2d_y) if(associated(Reg%Tr(m)%df2d_y))
659 !$ enddo
660 !$omp target exit data map(release: Reg%Tr, Reg)
661
6621 if (CS%debug) call MOM_tracer_chksum("After tracer diffusion ", Reg, G)
663
664 ! post diagnostics for 2d tracer diffusivity
6651 if (CS%id_KhTr_u > 0) then
666 !$omp target exit data map(from: Kh_u)
6670 do j=js,je ; do I=is-1,ie
6680 Kh_u(I,j,:) = G%mask2dCu(I,j)*Kh_u(I,j,1)
669 enddo ; enddo
6700 if (CS%KhTr_use_vert_struct) then
6710 do K=2,nz+1
6720 do j=js,je
6730 do I=is-1,ie
6740 Kh_u(I,j,K) = Kh_u(I,j,1) * 0.5 * ( VarMix%khtr_struct(i,j,k-1) + VarMix%khtr_struct(i+1,j,k-1) )
675 enddo
676 enddo
677 enddo
678 endif
6790 call post_data(CS%id_KhTr_u, Kh_u, CS%diag)
680 endif
6811 if (CS%id_KhTr_v > 0) then
682 !$omp target exit data map(from: Kh_v)
6830 do J=js-1,je ; do i=is,ie
6840 Kh_v(i,J,:) = G%mask2dCv(i,J)*Kh_v(i,J,1)
685 enddo ; enddo
6860 if (CS%KhTr_use_vert_struct) then
6870 do K=2,nz+1
6880 do J=js-1,je
6890 do i=is,ie
6900 Kh_v(i,J,K) = Kh_v(i,J,1) * 0.5 * ( VarMix%khtr_struct(i,j,k-1) + VarMix%khtr_struct(i,j+1,k-1) )
691 enddo
692 enddo
693 enddo
694 endif
6950 call post_data(CS%id_KhTr_v, Kh_v, CS%diag)
696 endif
6971 if (CS%id_KhTr_h > 0) then
698 !$omp target exit data map(from: Kh_u, Kh_v)
6990 Kh_h(:,:,:) = 0.0
7000 do j=js,je ; do I=is-1,ie
7010 Kh_u(I,j,1) = G%mask2dCu(I,j)*Kh_u(I,j,1)
702 enddo ; enddo
7030 do J=js-1,je ; do i=is,ie
7040 Kh_v(i,J,1) = G%mask2dCv(i,J)*Kh_v(i,J,1)
705 enddo ; enddo
706
7070 do j=js,je ; do i=is,ie
708 normalize = 1.0 / ((G%mask2dCu(I-1,j)+G%mask2dCu(I,j)) + &
7090 (G%mask2dCv(i,J-1)+G%mask2dCv(i,J)) + 1.0e-37)
710 Kh_h(i,j,:) = normalize*G%mask2dT(i,j)*((Kh_u(I-1,j,1)+Kh_u(I,j,1)) + &
7110 (Kh_v(i,J-1,1)+Kh_v(i,J,1)))
7120 if (CS%KhTr_use_vert_struct) then
7130 do K=2,nz+1
714 Kh_h(i,j,K) = normalize*G%mask2dT(i,j)*VarMix%khtr_struct(i,j,k-1)*((Kh_u(I-1,j,1)+Kh_u(I,j,1)) + &
7150 (Kh_v(i,J-1,1)+Kh_v(i,J,1)))
716 enddo
717 endif
718 enddo ; enddo
7190 call post_data(CS%id_KhTr_h, Kh_h, CS%diag)
720 endif
721
722 !$omp target update from(khdt_x, khdt_y) if(CS%debug .or. CS%id_khdt_x>0 .or. CS%id_khdt_y>0)
723 !$omp target exit data map(release: khdt_x, khdt_y, Kh_u, Kh_v) map(release: CS)
724
7251 if (CS%debug) then
726 call uvchksum("After tracer diffusion khdt_[xy]", khdt_x, khdt_y, &
727 G%HI, haloshift=0, symmetric=.true., unscale=US%L_to_m**2, &
7280 scalar_pair=.true.)
729 endif
730
7311 if (CS%id_khdt_x > 0) call post_data(CS%id_khdt_x, khdt_x, CS%diag)
7321 if (CS%id_khdt_y > 0) call post_data(CS%id_khdt_y, khdt_y, CS%diag)
733
7341 if (CS%show_call_tree) call callTree_leave("tracer_hordiff()")
735
7361end subroutine tracer_hordiff
737
738!> This subroutine does epipycnal diffusion of all tracers between the mixed
739!! and buffer layers and the interior, using the diffusivity in CS%KhTr.
740!! Multiple iterations are used (if necessary) so that there is no limit on the
741!! acceptable time increment.
7421subroutine tracer_epipycnal_ML_diff(h, dt, Tr, ntr, khdt_epi_x, khdt_epi_y, G, &
743 GV, US, CS, tv, num_itts)
744 type(ocean_grid_type), intent(inout) :: G !< ocean grid structure
745 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
746 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< layer thickness [H ~> m or kg m-2]
747 real, intent(in) :: dt !< time step [T ~> s]
748 type(tracer_type), intent(inout) :: Tr(:) !< tracer array
749 integer, intent(in) :: ntr !< number of tracers
750 real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: khdt_epi_x !< Zonal epipycnal diffusivity times
751 !! a time step and the ratio of the open face width over
752 !! the distance between adjacent tracer points [L2 ~> m2]
753 real, dimension(SZI_(G),SZJB_(G)), intent(in) :: khdt_epi_y !< Meridional epipycnal diffusivity times
754 !! a time step and the ratio of the open face width over
755 !! the distance between adjacent tracer points [L2 ~> m2]
756 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
757 type(tracer_hor_diff_CS), intent(inout) :: CS !< module control structure
758 type(thermo_var_ptrs), intent(in) :: tv !< thermodynamic structure
759 integer, intent(in) :: num_itts !< number of iterations (usually=1)
760
761
762 real, dimension(SZI_(G), SZJ_(G)) :: &
7632 Rml_max ! The maximum coordinate density within the mixed layer [R ~> kg m-3].
764 real, dimension(SZI_(G), SZJ_(G), max(1,GV%nk_rho_varies)) :: &
7652 rho_coord ! The coordinate density that is used to mix along [R ~> kg m-3].
766
767 ! The naming mnemonic is a=above,b=below,L=Left,R=Right,u=u-point,v=v-point.
768 ! These are 1-D arrays of pointers to 2-d arrays to minimize memory usage.
769 real, dimension(:,:,:), allocatable :: &
7701 deep_wt_Lu, deep_wt_Ru, & ! The relative weighting of the deeper of a pair [nondim].
7711 hP_Lu, hP_Ru, & ! The total thickness on each side for each pair [H ~> m or kg m-2].
7721 deep_wt_Lv, deep_wt_Rv, & ! The relative weighting of the deeper of a pair [nondim].
7731 hP_Lv, hP_Rv ! The total thickness on each side for each pair [H ~> m or kg m-2].
774
775 integer, dimension(:,:,:), allocatable :: &
7761 k0b_Lu, k0a_Lu, & ! The original k-indices of the layers that participate
7771 k0b_Ru, k0a_Ru, & ! in each pair of mixing at u-faces.
7781 k0b_Lv, k0a_Lv, & ! The original k-indices of the layers that participate
7791 k0b_Rv, k0a_Rv ! in each pair of mixing at v-faces.
780
781 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: &
7822 tr_flux_N, & ! The tracer flux through the northern face [conc H L2 ~> conc m3 or conc kg]
7832 tr_flux_S, & ! The tracer flux through the southern face [conc H L2 ~> conc m3 or conc kg]
7842 tr_flux_E, & ! The tracer flux through the eastern face [conc H L2 ~> conc m3 or conc kg]
7852 tr_flux_W, & ! The tracer flux through the western face [conc H L2 ~> conc m3 or conc kg]
7862 tr_flux_conv ! The flux convergence of tracers [conc H L2 ~> conc m3 or conc kg]
787
788 ! The following 3-d arrays were created in 2014 in MOM6 PR#12 to facilitate openMP threading
789 ! on an i-loop, which might have been ill advised.
790 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)*2) :: &
7912 Tr_flux_3d, & ! The tracer flux through pairings at meridional faces [conc H L2 ~> conc m3 or conc kg]
7922 Tr_adj_vert_L, & ! Vertical adjustments to which layer the fluxes go into in the southern
793 ! columns at meridional face [conc H L2 ~> conc m3 or conc kg]
7942 Tr_adj_vert_R ! Vertical adjustments to which layer the fluxes go into in the northern
795 ! columns at meridional face [conc H L2 ~> conc m3 or conc kg]
796
797 real, dimension(SZI_(G),SZK_(GV), SZJ_(G)) :: &
7982 rho_srt, & ! The density of each layer of the sorted columns [R ~> kg m-3].
7992 h_srt ! The thickness of each layer of the sorted columns [H ~> m or kg m-2].
800 integer, dimension(SZI_(G),SZK_(GV), SZJ_(G)) :: &
8012 k0_srt ! The original k-index that each layer of the sorted column corresponds to.
802
803 real, dimension(SZK_(GV)) :: &
8042 h_demand_L, & ! The thickness in the left column that is demanded to match the thickness
805 ! in the counterpart [H ~> m or kg m-2].
8062 h_demand_R, & ! The thickness in the right column that is demanded to match the thickness
807 ! in the counterpart [H ~> m or kg m-2].
8082 h_used_L, & ! The summed thickness from the left column that has actually been used [H ~> m or kg m-2]
8092 h_used_R, & ! The summed thickness from the right columns that has actually been used [H ~> m or kg m-2]
8102 h_supply_frac_L, & ! The fraction of the demanded thickness that can actually be supplied
811 ! from a layer on the left [nondim].
8122 h_supply_frac_R ! The fraction of the demanded thickness that can actually be supplied
813 ! from a layer on the right [nondim].
814 integer, dimension(SZI_(G), SZJ_(G)) :: &
8152 num_srt, & ! The number of layers that are sorted in each column.
8162 k_end_srt, & ! The maximum index in each column that might need to be
817 ! sorted, based on neighboring values of max_kRho
8182 max_kRho ! The index of the layer whose target density is just denser
819 ! than the densest part of the mixed layer.
820 integer, dimension(SZJ_(G)) :: &
8212 max_srt ! The maximum value of num_srt in a k-row.
822 integer, dimension(SZIB_(G), SZJ_(G)) :: &
8232 nPu ! The number of epipycnal pairings at each u-point.
824 integer, dimension(SZI_(G), SZJB_(G)) :: &
8252 nPv ! The number of epipycnal pairings at each v-point.
826 real :: h_exclude ! A thickness that layers must attain to be considered
827 ! for inclusion in mixing [H ~> m or kg m-2].
828 real :: Idt ! The inverse of the time step [T-1 ~> s-1].
829 real :: I_maxitt ! The inverse of the maximum number of iterations [nondim]
830 real :: rho_pair, rho_a, rho_b ! Temporary densities [R ~> kg m-3].
831 real :: Tr_min_face ! The minimum tracer concentration associated with a pairing [Conc]
832 real :: Tr_max_face ! The maximum tracer concentration associated with a pairing [Conc]
833 real :: Tr_La, Tr_Lb ! The 2 left-side tracer concentrations that might be associated with a pairing [Conc]
834 real :: Tr_Ra, Tr_Rb ! The 2 right-side tracer concentrations that might be associated with a pairing [Conc]
835 real :: Tr_av_L ! The average tracer concentrations on the left side of a pairing [Conc].
836 real :: Tr_av_R ! The average tracer concentrations on the right side of a pairing [Conc].
837 real :: Tr_flux ! The tracer flux from left to right in a pair [conc H L2 ~> conc m3 or conc kg].
838 real :: Tr_adj_vert ! A downward vertical adjustment to Tr_flux between the two cells that
839 ! make up one side of the pairing [conc H L2 ~> conc m3 or conc kg].
840 real :: h_L, h_R ! Thicknesses to the left and right [H ~> m or kg m-2].
841 real :: wt_a, wt_b ! Fractional weights of layers above and below [nondim].
842 real :: vol ! A cell volume or mass [H L2 ~> m3 or kg].
843
844 ! The total number of pairings is usually much less than twice the number of layers, but
845 ! the memory in these 1-d columns of pairings can be allocated generously for safety.
846 integer, dimension(SZK_(GV)*2) :: &
8472 kbs_Lp, & ! The sorted indices of the Left and Right columns for
8482 kbs_Rp ! each pairing.
849 logical, dimension(SZK_(GV)*2) :: &
8502 left_set, & ! If true, the left or right point determines the density of
8512 right_set ! of the trio. If densities are exactly equal, both are true.
852
853 real :: tmp ! A temporary variable used in swaps [various]
8542 real :: p_ref_cv(SZI_(G),SZJ_(G)) ! The reference pressure for the coordinate density [R L2 T-2 ~> Pa]
855
856 integer, dimension(2,2) :: EOSdom ! The i-computational domain for the equation of state
857 integer :: k_max, k_min, k_test, itmp
858 integer :: i, j, k, k2, m, is, ie, js, je, nz, nkmb
859 integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, k_size
860 integer :: kL, kR, kLa, kLb, kRa, kRb, nP, itt, ns, max_itt
861 integer :: PEmax_kRho
862
8631 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
8641 isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed
8651 IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB
8661 Idt = 1.0 / dt
8671 nkmb = GV%nk_rho_varies
868
8691 if (num_itts <= 1) then
8701 max_itt = 1 ; I_maxitt = 1.0
871 else
8720 max_itt = num_itts ; I_maxitt = 1.0 / (real(max_itt))
873 endif
874
875 !$omp target enter data map(alloc: rho_coord, Rml_max, max_kRho, rho_srt, k0_srt, num_srt, &
876 !$omp h_srt, p_ref_cv, k_end_srt, max_srt)
877
8781 do concurrent (j=jsd:jed, i=isd:ied)
87969553 p_ref_cv(i,j) = tv%P_Ref
880 enddo
881
8821 call do_group_pass(CS%pass_t, G%Domain, clock=id_clock_pass, omp_offload=.true.)
883 ! Determine which layers the mixed- and buffer-layers map into...
884 !$OMP parallel do default(shared)
8855 do k=1,nkmb
886 call calculate_density(tv%T(:,:,k), tv%S(:,:,k), p_ref_cv, rho_coord(:,:,k), &
8875 tv%eqn_of_state)
888 enddo
889 !$omp target exit data map(release: p_ref_cv)
890
891365 do concurrent (j=js-2:je+2, i=is-2:ie+2)
89266976 Rml_max(i,j) = rho_coord(i,j,1)
89367341 num_srt(i,j) = 0 ; max_kRho(i,j) = 0
894 enddo
895202024 do k=2,nkmb ; do concurrent (j=js-2:je+2, i=is-2:ie+2, Rml_max(i,j) < rho_coord(i,j,k))
896202023 Rml_max(i,j) = rho_coord(i,j,k)
897 enddo ; enddo
898
899 ! Use bracketing and bisection to find the k-level that the densest of the
900 ! mixed and buffer layer corresponds to, such that:
901 ! GV%Rlay(max_kRho-1) < Rml_max <= GV%Rlay(max_kRho)
90267341 do concurrent (j=js-2:je+2, i=is-2:ie+2, G%mask2dT(i,j) > 0.0)
90367341 if ((Rml_max(i,j) > GV%Rlay(nz)) .or. (nkmb+1 > nz)) then ; max_kRho(i,j) = nz+1
90446490 elseif ((Rml_max(i,j) <= GV%Rlay(nkmb+1)) .or. (nkmb+2 > nz)) then ; max_kRho(i,j) = nkmb+1
905 else
90646490 k_min = nkmb+2 ; k_max = nz
90786910 do
908133400 k_test = (k_min + k_max) / 2
909133400 if (Rml_max(i,j) <= GV%Rlay(k_test-1)) then ; k_max = k_test-1
91083832 elseif (GV%Rlay(k_test) < Rml_max(i,j)) then ; k_min = k_test+1
91125734 else ; max_kRho(i,j) = k_test ; exit ; endif
912
913107666 if (k_min == k_max) then ; max_kRho(i,j) = k_max ; exit ; endif
914 enddo
915 endif
916 enddo
917
9181 PEmax_kRho = 0
919363 do concurrent (j=js-1:je+1, i=is-1:ie+1) DO_LOCALITY(reduce(max:PEmax_kRho))
920 k_end_srt(i,j) = max(max_kRho(i,j), max_kRho(i-1,j), max_kRho(i+1,j), &
92165884 max_kRho(i,j-1), max_kRho(i,j+1))
92266247 if (PEmax_kRho < k_end_srt(i,j)) PEmax_kRho = k_end_srt(i,j)
923 enddo
9241 if (PEmax_kRho > nz) PEmax_kRho = nz ! PEmax_kRho could have been nz+1.
925
9261 h_exclude = 10.0*(GV%Angstrom_H + GV%H_subroundoff)
927
9281 do concurrent (j=js-1:je+1) DO_LOCALITY(local(k, ns))
929264446 do k=1,nkmb ; do concurrent (i=is-1:ie+1, G%mask2dT(i,j) > 0.0) DO_LOCALITY(local(ns))
930264264 if (h(i,j,k) > h_exclude) then
93146370 num_srt(i,j) = num_srt(i,j) + 1 ; ns = num_srt(i,j)
93246370 k0_srt(i,ns,j) = k
93346370 rho_srt(i,ns,j) = rho_coord(i,j,k)
93446370 h_srt(i,ns,j) = h(i,j,k)
935 endif
936 enddo ; enddo
9371057239 do k=nkmb+1,PEmax_kRho ; do concurrent (i=is-1:ie+1, G%mask2dT(i,j) > 0.0) DO_LOCALITY(local(ns))
9381057056 if ((k<=k_end_srt(i,j)) .and. (h(i,j,k) > h_exclude)) then
93954440 num_srt(i,j) = num_srt(i,j) + 1 ; ns = num_srt(i,j)
94054440 k0_srt(i,ns,j) = k
94154440 rho_srt(i,ns,j) = GV%Rlay(k)
94254440 h_srt(i,ns,j) = h(i,j,k)
943 endif
944 enddo ; enddo
945 enddo
946 !$omp target exit data map(release: rho_coord, Rml_max, k_end_srt)
947 ! Sort each column by increasing density. This should already be close,
948 ! and the size of the arrays are small, so straight insertion is used.
949363 do concurrent (j=js-1:je+1, i=is-1:ie+1)
950120687 do k=2,num_srt(i,j) ; if (rho_srt(i,k,j) < rho_srt(i,k-1,j)) then
951 ! The last segment needs to be shuffled earlier in the list.
95214598 do k2 = k,2,-1 ; if (rho_srt(i,k2,j) < rho_srt(i,k2-1,j)) then
9537299 itmp = k0_srt(i,k2-1,j) ; k0_srt(i,k2-1,j) = k0_srt(i,k2,j) ; k0_srt(i,k2,j) = itmp
9547299 tmp = rho_srt(i,k2-1,j) ; rho_srt(i,k2-1,j) = rho_srt(i,k2,j) ; rho_srt(i,k2,j) = tmp
9557299 tmp = h_srt(i,k2-1,j) ; h_srt(i,k2-1,j) = h_srt(i,k2,j) ; h_srt(i,k2,j) = tmp
956 endif ; enddo
957 endif ; enddo
958 enddo
9591 do concurrent (j=js-1:je+1) DO_LOCALITY(local(itmp))
960 ! max_srt(j) = 0
961182 itmp = 0
962 ! nvfortran do concurrent cannot reduce array elements
963182 do concurrent (i=is-1:ie+1) DO_LOCALITY(reduce(max:itmp))
96466066 itmp = max(itmp, num_srt(i,j))
965 enddo
966183 max_srt(j) = itmp
967 enddo
9681 k_size = 1
9691 do concurrent (j=js-1:je+1) DO_LOCALITY(reduce(max:k_size))
970183 k_size = max(k_size, 2*max_srt(j))
971 enddo
9721 allocate(k0a_Lu(IsdB:iedB,k_size,jsd:jed))
9731 allocate(k0a_Ru(IsdB:iedB,k_size,jsd:jed))
9741 allocate(k0b_Lu(IsdB:iedB,k_size,jsd:jed))
9751 allocate(k0b_Ru(IsdB:iedB,k_size,jsd:jed))
9761 allocate(deep_wt_Lu(IsdB:iedB,k_size,jsd:jed))
9771 allocate(deep_wt_Ru(IsdB:iedB,k_size,jsd:jed))
9781 allocate(hP_Lu(IsdB:iedB,k_size,jsd:jed))
9791 allocate(hP_Ru(IsdB:iedB,k_size,jsd:jed))
980 !$omp target enter data map(alloc: deep_wt_Lu, deep_wt_Ru, hP_Lu, hP_Ru, k0a_Lu, k0a_Ru, k0b_Lu, &
981 !$omp k0b_Ru)
982 !$omp target enter data map(alloc: nPu)
983!$OMP target teams loop collapse(2) &
984!$OMP private(h_demand_L,h_used_L,h_demand_R,h_used_R, &
985!$OMP kR,kL,nP,rho_pair,kbs_Lp,kbs_Rp,rho_a,rho_b, &
986!$OMP wt_b,left_set,right_set,h_supply_frac_R, &
987!$OMP h_supply_frac_L, k)
98865161 do j=js,je ; do I=is-1,ie ; if (G%mask2dCu(I,j) > 0.0) then
989 ! Set up the pairings for fluxes through the zonal faces.
990
991146364 do k=1,num_srt(i,j) ; h_demand_L(k) = 0.0 ; h_used_L(k) = 0.0 ; enddo
992146331 do k=1,num_srt(i+1,j) ; h_demand_R(k) = 0.0 ; h_used_R(k) = 0.0 ; enddo
993
994 ! First merge the left and right lists into a single, sorted list.
995
996 ! Discard any layers that are lighter than the lightest in the other
997 ! column. They can only participate in mixing as the lighter part of a
998 ! pair of points.
99946067 if (rho_srt(i,1,j) < rho_srt(i+1,1,j)) then
100019160 kR = 1
100119176 do kL=2,num_srt(i,j) ; if (rho_srt(i,kL,j) >= rho_srt(i+1,1,j)) exit ; enddo
100226907 elseif (rho_srt(i+1,1,j) < rho_srt(i,1,j)) then
100319318 kL = 1
100419335 do kR=2,num_srt(i+1,j) ; if (rho_srt(i+1,kR,j) >= rho_srt(i,1,j)) exit ; enddo
1005 else
10067589 kL = 1 ; kR = 1
1007 endif
100846067 nP = 0
1009105240 do ! Loop to accumulate pairs of columns.
1010151307 if ((kL > num_srt(i,j)) .or. (kR > num_srt(i+1,j))) exit
1011
1012105240 if (rho_srt(i,kL,j) > rho_srt(i+1,kR,j)) then
1013 ! The right point is lighter and defines the density for this trio.
101425340 nP = nP+1 ; k = nP
101525340 rho_pair = rho_srt(i+1,kR,j)
1016
101725340 k0b_Lu(I,k,j) = k0_srt(i,kL,j) ; k0b_Ru(I,k,j) = k0_srt(i+1,kR,j)
101825340 k0a_Lu(I,k,j) = k0_srt(i,kL-1,j) ; k0a_Ru(I,k,j) = k0b_Ru(I,k,j)
101925340 kbs_Lp(k) = kL ; kbs_Rp(k) = kR
1020
102125340 rho_a = rho_srt(i,kL-1,j) ; rho_b = rho_srt(i,kL,j)
102225340 wt_b = 1.0 ; if (abs(rho_a - rho_b) > abs(rho_pair - rho_a)) &
102325340 wt_b = (rho_pair - rho_a) / (rho_b - rho_a)
102425340 deep_wt_Lu(I,k,j) = wt_b ; deep_wt_Ru(I,k,j) = 1.0
1025
102625340 h_demand_L(kL) = h_demand_L(kL) + 0.5*h_srt(i+1,kR,j) * wt_b
102725340 h_demand_L(kL-1) = h_demand_L(kL-1) + 0.5*h_srt(i+1,kR,j) * (1.0-wt_b)
1028
102925340 kR = kR+1 ; left_set(k) = .false. ; right_set(k) = .true.
103079900 elseif (rho_srt(i,kL,j) < rho_srt(i+1,kR,j)) then
1031 ! The left point is lighter and defines the density for this trio.
103225458 nP = nP+1 ; k = nP
103325458 rho_pair = rho_srt(i,kL,j)
103425458 k0b_Lu(I,k,j) = k0_srt(i,kL,j) ; k0b_Ru(I,k,j) = k0_srt(i+1,kR,j)
103525458 k0a_Lu(I,k,j) = k0b_Lu(I,k,j) ; k0a_Ru(I,k,j) = k0_srt(i+1,kR-1,j)
1036
103725458 kbs_Lp(k) = kL ; kbs_Rp(k) = kR
1038
103925458 rho_a = rho_srt(i+1,kR-1,j) ; rho_b = rho_srt(i+1,kR,j)
104025458 wt_b = 1.0 ; if (abs(rho_a - rho_b) > abs(rho_pair - rho_a)) &
104125458 wt_b = (rho_pair - rho_a) / (rho_b - rho_a)
104225458 deep_wt_Lu(I,k,j) = 1.0 ; deep_wt_Ru(I,k,j) = wt_b
1043
104425458 h_demand_R(kR) = h_demand_R(kR) + 0.5*h_srt(i,kL,j) * wt_b
104525458 h_demand_R(kR-1) = h_demand_R(kR-1) + 0.5*h_srt(i,kL,j) * (1.0-wt_b)
1046
104725458 kL = kL+1 ; left_set(k) = .true. ; right_set(k) = .false.
104854442 elseif ((k0_srt(i,kL,j) <= nkmb) .or. (k0_srt(i+1,kR,j) <= nkmb)) then
1049 ! The densities are exactly equal and one layer is above the interior.
1050321 nP = nP+1 ; k = nP
1051321 k0b_Lu(I,k,j) = k0_srt(i,kL,j) ; k0b_Ru(I,k,j) = k0_srt(i+1,kR,j)
1052321 k0a_Lu(I,k,j) = k0b_Lu(I,k,j) ; k0a_Ru(I,k,j) = k0b_Ru(I,k,j)
1053321 kbs_Lp(k) = kL ; kbs_Rp(k) = kR
1054321 deep_wt_Lu(I,k,j) = 1.0 ; deep_wt_Ru(I,k,j) = 1.0
1055
1056321 h_demand_L(kL) = h_demand_L(kL) + 0.5*h_srt(i+1,kR,j)
1057321 h_demand_R(kR) = h_demand_R(kR) + 0.5*h_srt(i,kL,j)
1058
1059321 kL = kL+1 ; kR = kR+1 ; left_set(k) = .true. ; right_set(k) = .true.
1060 else ! The densities are exactly equal and in the interior.
1061 ! Mixing in this case has already occurred, so accumulate the thickness
1062 ! demanded for that mixing and skip onward.
106354121 h_demand_L(kL) = h_demand_L(kL) + 0.5*h_srt(i+1,kR,j)
106454121 h_demand_R(kR) = h_demand_R(kR) + 0.5*h_srt(i,kL,j)
1065
106654121 kL = kL+1 ; kR = kR+1
1067 endif
1068 enddo ! Loop to accumulate pairs of columns.
106946067 nPu(I,j) = nP ! This is the number of active pairings.
1070
1071 ! Determine what fraction of the thickness "demand" can be supplied.
1072146331 do k=1,num_srt(i+1,j)
1073100264 h_supply_frac_R(k) = 1.0
1074100264 if (h_demand_R(k) > 0.5*h_srt(i+1,k,j)) &
107583137 h_supply_frac_R(k) = 0.5*h_srt(i+1,k,j) / h_demand_R(k)
1076 enddo
1077146364 do k=1,num_srt(i,j)
1078100297 h_supply_frac_L(k) = 1.0
1079100297 if (h_demand_L(k) > 0.5*h_srt(i,k,j)) &
108085574 h_supply_frac_L(k) = 0.5*h_srt(i,k,j) / h_demand_L(k)
1081 enddo
1082
1083 ! Distribute the "exported" thicknesses proportionately.
108497186 do k=1,nPu(I,j)
108551119 kL = kbs_Lp(k) ; kR = kbs_Rp(k)
108651119 hP_Lu(I,k,j) = 0.0 ; hP_Ru(I,k,j) = 0.0
108751119 if (left_set(k)) then ! Add the contributing thicknesses on the right.
108825779 if (deep_wt_Ru(I,k,j) < 1.0) then
108925458 hP_Ru(I,k,j) = 0.5*h_srt(i,kL,j) * min(h_supply_frac_R(kR), h_supply_frac_R(kR-1))
109025458 wt_b = deep_wt_Ru(I,k,j)
109125458 h_used_R(kR-1) = h_used_R(kR-1) + (1.0 - wt_b)*hP_Ru(I,k,j)
109225458 h_used_R(kR) = h_used_R(kR) + wt_b*hP_Ru(I,k,j)
1093 else
1094321 hP_Ru(I,k,j) = 0.5*h_srt(i,kL,j) * h_supply_frac_R(kR)
1095321 h_used_R(kR) = h_used_R(kR) + hP_Ru(I,k,j)
1096 endif
1097 endif
109897186 if (right_set(k)) then ! Add the contributing thicknesses on the left.
109925661 if (deep_wt_Lu(I,k,j) < 1.0) then
110025340 hP_Lu(I,k,j) = 0.5*h_srt(i+1,kR,j) * min(h_supply_frac_L(kL), h_supply_frac_L(kL-1))
110125340 wt_b = deep_wt_Lu(I,k,j)
110225340 h_used_L(kL-1) = h_used_L(kL-1) + (1.0 - wt_b)*hP_Lu(I,k,j)
110325340 h_used_L(kL) = h_used_L(kL) + wt_b*hP_Lu(I,k,j)
1104 else
1105321 hP_Lu(I,k,j) = 0.5*h_srt(i+1,kR,j) * h_supply_frac_L(kL)
1106321 h_used_L(kL) = h_used_L(kL) + hP_Lu(I,k,j)
1107 endif
1108 endif
1109 enddo
1110
1111 ! The left-over thickness (at least half the layer thickness) is now
1112 ! added to the thicknesses of the importing columns.
111397186 do k=1,nPu(I,j)
111451119 if (left_set(k)) hP_Lu(I,k,j) = hP_Lu(I,k,j) + &
111525779 (h_srt(i,kbs_Lp(k),j) - h_used_L(kbs_Lp(k)))
111651119 if (right_set(k)) hP_Ru(I,k,j) = hP_Ru(I,k,j) + &
111771728 (h_srt(i+1,kbs_Rp(k),j) - h_used_R(kbs_Rp(k)))
1118 enddo
1119
1120 endif ; enddo ; enddo ! i- & j- loops over zonal faces.
1121
11221 allocate(deep_wt_Lv(isd:ied,k_size,JsdB:JedB))
11231 allocate(deep_wt_Rv(isd:ied,k_size,JsdB:JedB))
11241 allocate(hP_Lv(isd:ied,k_size,JsdB:JedB))
11251 allocate(hP_Rv(isd:ied,k_size,JsdB:JedB))
11261 allocate(k0a_Lv(isd:ied,k_size,JsdB:JedB))
11271 allocate(k0a_Rv(isd:ied,k_size,JsdB:JedB))
11281 allocate(k0b_Lv(isd:ied,k_size,JsdB:JedB))
11291 allocate(k0b_Rv(isd:ied,k_size,JsdB:JedB))
1130 !$omp target enter data map(alloc: deep_wt_Lv, deep_wt_Rv, hP_Lv, hP_Rv, k0a_Lv, k0a_Rv, k0b_Lv, &
1131 !$omp k0b_Rv)
1132
1133 !$omp target enter data map(alloc: nPv)
1134
1135!$OMP target teams loop collapse(2) &
1136!$OMP private(h_demand_L,h_used_L,h_demand_R,h_used_R, &
1137!$OMP kR,kL,nP,rho_pair,kbs_Lp,kbs_Rp,rho_a,rho_b, &
1138!$OMP wt_b,left_set,right_set,h_supply_frac_R, &
1139!$OMP h_supply_frac_L,k) map(to: num_srt)
114065342 do J=js-1,je ; do i=is,ie ; if (G%mask2dCv(i,J) > 0.0) then
1141 ! Set up the pairings for fluxes through the meridional faces.
1142
1143145769 do k=1,num_srt(i,j) ; h_demand_L(k) = 0.0 ; h_used_L(k) = 0.0 ; enddo
1144145814 do k=1,num_srt(i,j+1) ; h_demand_R(k) = 0.0 ; h_used_R(k) = 0.0 ; enddo
1145
1146 ! First merge the left and right lists into a single, sorted list.
1147
1148 ! Discard any layers that are lighter than the lightest in the other
1149 ! column. They can only participate in mixing as the lighter part of a
1150 ! pair of points.
115145890 if (rho_srt(i,1,j) < rho_srt(i,1,j+1)) then
11520 kR = 1
11530 do kL=2,num_srt(i,j) ; if (rho_srt(i,kL,j) >= rho_srt(i,1,j+1)) exit ; enddo
115445890 elseif (rho_srt(i,1,j+1) < rho_srt(i,1,j)) then
115541570 kL = 1
115644754 do kR=2,num_srt(i,j+1) ; if (rho_srt(i,kR,j+1) >= rho_srt(i,1,j)) exit ; enddo
1157 else
11584320 kL = 1 ; kR = 1
1159 endif
116045890 nP = 0
116198120 do ! Loop to accumulate pairs of columns.
1162144010 if ((kL > num_srt(i,j)) .or. (kR > num_srt(i,j+1))) exit
1163
116498120 if (rho_srt(i,kL,j) > rho_srt(i,kR,j+1)) then
1165 ! The right point is lighter and defines the density for this trio.
11664486 nP = nP+1 ; k = nP
11674486 rho_pair = rho_srt(i,kR,j+1)
1168
11694486 k0b_Lv(i,k,J) = k0_srt(i,kL,j) ; k0b_Rv(i,k,J) = k0_srt(i,kR,j+1)
11704486 k0a_Lv(i,k,J) = k0_srt(i,kL-1,j) ; k0a_Rv(i,k,J) = k0b_Rv(i,k,J)
11714486 kbs_Lp(k) = kL ; kbs_Rp(k) = kR
1172
11734486 rho_a = rho_srt(i,kL-1,j) ; rho_b = rho_srt(i,kL,j)
11744486 wt_b = 1.0 ; if (abs(rho_a - rho_b) > abs(rho_pair - rho_a)) &
11754486 wt_b = (rho_pair - rho_a) / (rho_b - rho_a)
11764486 deep_wt_Lv(I,k,J) = wt_b ; deep_wt_Rv(I,k,J) = 1.0
1177
11784486 h_demand_L(kL) = h_demand_L(kL) + 0.5*h_srt(i,kR,j+1) * wt_b
11794486 h_demand_L(kL-1) = h_demand_L(kL-1) + 0.5*h_srt(i,kR,j+1) * (1.0-wt_b)
1180
11814486 kR = kR+1 ; left_set(k) = .false. ; right_set(k) = .true.
118293634 elseif (rho_srt(i,kL,j) < rho_srt(i,kR,j+1)) then
1183 ! The left point is lighter and defines the density for this trio.
118443582 nP = nP+1 ; k = nP
118543582 rho_pair = rho_srt(i,kL,j)
118643582 k0b_Lv(i,k,J) = k0_srt(i,kL,j) ; k0b_Rv(i,k,J) = k0_srt(i,kR,j+1)
118743582 k0a_Lv(i,k,J) = k0b_Lv(i,k,J) ; k0a_Rv(i,k,J) = k0_srt(i,kR-1,j+1)
1188
118943582 kbs_Lp(k) = kL ; kbs_Rp(k) = kR
1190
119143582 rho_a = rho_srt(i,kR-1,j+1) ; rho_b = rho_srt(i,kR,j+1)
119243582 wt_b = 1.0 ; if (abs(rho_a - rho_b) > abs(rho_pair - rho_a)) &
119343582 wt_b = (rho_pair - rho_a) / (rho_b - rho_a)
119443582 deep_wt_Lv(I,k,J) = 1.0 ; deep_wt_Rv(I,k,J) = wt_b
1195
119643582 h_demand_R(kR) = h_demand_R(kR) + 0.5*h_srt(i,kL,j) * wt_b
119743582 h_demand_R(kR-1) = h_demand_R(kR-1) + 0.5*h_srt(i,kL,j) * (1.0-wt_b)
1198
119943582 kL = kL+1 ; left_set(k) = .true. ; right_set(k) = .false.
120050052 elseif ((k0_srt(i,kL,j) <= nkmb) .or. (k0_srt(i,kR,j+1) <= nkmb)) then
1201 ! The densities are exactly equal and one layer is above the interior.
12020 nP = nP+1 ; k = nP
12030 k0b_Lv(i,k,J) = k0_srt(i,kL,j) ; k0b_Rv(i,k,J) = k0_srt(i,kR,j+1)
12040 k0a_Lv(i,k,J) = k0b_Lv(i,k,J) ; k0a_Rv(i,k,J) = k0b_Rv(i,k,J)
12050 kbs_Lp(k) = kL ; kbs_Rp(k) = kR
12060 deep_wt_Lv(I,k,J) = 1.0 ; deep_wt_Rv(I,k,J) = 1.0
1207
12080 h_demand_L(kL) = h_demand_L(kL) + 0.5*h_srt(i,kR,j+1)
12090 h_demand_R(kR) = h_demand_R(kR) + 0.5*h_srt(i,kL,j)
1210
12110 kL = kL+1 ; kR = kR+1 ; left_set(k) = .true. ; right_set(k) = .true.
1212 else ! The densities are exactly equal and in the interior.
1213 ! Mixing in this case has already occurred, so accumulate the thickness
1214 ! demanded for that mixing and skip onward.
121550052 h_demand_L(kL) = h_demand_L(kL) + 0.5*h_srt(i,kR,j+1)
121650052 h_demand_R(kR) = h_demand_R(kR) + 0.5*h_srt(i,kL,j)
1217
121850052 kL = kL+1 ; kR = kR+1
1219 endif
1220 enddo ! Loop to accumulate pairs of columns.
122145890 nPv(i,J) = nP ! This is the number of active pairings.
1222
1223 ! Determine what fraction of the thickness "demand" can be supplied.
1224145814 do k=1,num_srt(i,j+1)
122599924 h_supply_frac_R(k) = 1.0
122699924 if (h_demand_R(k) > 0.5*h_srt(i,k,j+1)) &
122770814 h_supply_frac_R(k) = 0.5*h_srt(i,k,j+1) / h_demand_R(k)
1228 enddo
1229145769 do k=1,num_srt(i,j)
123099879 h_supply_frac_L(k) = 1.0
123199879 if (h_demand_L(k) > 0.5*h_srt(i,k,j)) &
123295711 h_supply_frac_L(k) = 0.5*h_srt(i,k,j) / h_demand_L(k)
1233 enddo
1234
1235 ! Distribute the "exported" thicknesses proportionately.
123693958 do k=1,nPv(i,J)
123748068 kL = kbs_Lp(k) ; kR = kbs_Rp(k)
123848068 hP_Lv(I,k,J) = 0.0 ; hP_Rv(I,k,J) = 0.0
123948068 if (left_set(k)) then ! Add the contributing thicknesses on the right.
124043582 if (deep_wt_Rv(I,k,J) < 1.0) then
124143582 hP_Rv(I,k,J) = 0.5*h_srt(i,kL,j) * min(h_supply_frac_R(kR), h_supply_frac_R(kR-1))
124243582 wt_b = deep_wt_Rv(I,k,J)
124343582 h_used_R(kR-1) = h_used_R(kR-1) + (1.0 - wt_b) * hP_Rv(I,k,J)
124443582 h_used_R(kR) = h_used_R(kR) + wt_b * hP_Rv(I,k,J)
1245 else
12460 hP_Rv(I,k,J) = 0.5*h_srt(i,kL,j) * h_supply_frac_R(kR)
12470 h_used_R(kR) = h_used_R(kR) + hP_Rv(I,k,J)
1248 endif
1249 endif
125093958 if (right_set(k)) then ! Add the contributing thicknesses on the left.
12514486 if (deep_wt_Lv(I,k,J) < 1.0) then
12524486 hP_Lv(I,k,J) = 0.5*h_srt(i,kR,j+1) * min(h_supply_frac_L(kL), h_supply_frac_L(kL-1))
12534486 wt_b = deep_wt_Lv(I,k,J)
12544486 h_used_L(kL-1) = h_used_L(kL-1) + (1.0 - wt_b) * hP_Lv(I,k,J)
12554486 h_used_L(kL) = h_used_L(kL) + wt_b * hP_Lv(I,k,J)
1256 else
12570 hP_Lv(I,k,J) = 0.5*h_srt(i,kR,j+1) * h_supply_frac_L(kL)
12580 h_used_L(kL) = h_used_L(kL) + hP_Lv(I,k,J)
1259 endif
1260 endif
1261 enddo
1262
1263 ! The left-over thickness (at least half the layer thickness) is now
1264 ! added to the thicknesses of the importing columns.
126593958 do k=1,nPv(i,J)
126648068 if (left_set(k)) hP_Lv(I,k,J) = hP_Lv(I,k,J) + &
126743582 (h_srt(i,kbs_Lp(k),j) - h_used_L(kbs_Lp(k)))
126848068 if (right_set(k)) hP_Rv(I,k,J) = hP_Rv(I,k,J) + &
126950376 (h_srt(i,kbs_Rp(k),j+1) - h_used_R(kbs_Rp(k)))
1270 enddo
1271
1272
1273 endif ; enddo ; enddo ! i- & j- loops over meridional faces.
1274 !$omp target exit data map(release: h_srt, k0_srt)
1275 ! The tracer-specific calculations start here.
1276
1277 !$omp target enter data map(alloc: Tr_flux_3d, Tr_adj_vert_L, Tr_adj_vert_R, tr_flux_N, &
1278 !$omp tr_flux_S, tr_flux_E, tr_flux_W, tr_flux_conv)
1279
12802 do itt=1,max_itt
1281
12821 if (itt > 1) then ! The halos have already been filled if itt==1.
12830 call do_group_pass(CS%pass_t, G%Domain, clock=id_clock_pass, omp_offload=.true.)
1284 endif
12855 do m=1,ntr
1286 ! Zero out tracer tendencies.
12873 if (CS%answer_date <= 20240330) then
12880 do concurrent (k=1:nz, j=jsd:jed, i=isd:ied)
12890 tr_flux_conv(i,j,k) = 0.0
1290 enddo
1291 else
12923 do concurrent (k=1:nz, j=jsd:jed, i=isd:ied)
12934566144 tr_flux_N(i,j,k) = 0.0 ; tr_flux_S(i,j,k) = 0.0
12944774803 tr_flux_E(i,j,k) = 0.0 ; tr_flux_W(i,j,k) = 0.0
1295 enddo
1296 endif
12973 do concurrent (k=1:2*nz, J=JsdB:JedB, i=isd:ied)
12989180864 tr_flux_3d(i,j,k) = 0.0
12999390627 tr_adj_vert_R(i,j,k) = 0.0 ; tr_adj_vert_L(i,j,k) = 0.0
1300 enddo
1301
1302 ! collapse(2) is reproducible for CS%answer_date > 20240330 but not <= 20240330.
1303 ! Do concurrent around j-loop doesn't seem to do the right thing.
1304 !$omp target teams loop collapse(2) private(Tr_min_face,Tr_max_face,kLa,kLb,kRa,kRb, &
1305 !$omp Tr_La,Tr_Lb,Tr_Ra,Tr_Rb,Tr_av_L,wt_b,Tr_av_R, &
1306 !$omp h_L,h_R,Tr_flux,Tr_adj_vert,wt_a,vol)
1307195483 do j=js,je ; do I=is-1,ie ; if (G%mask2dCu(I,j) > 0.0) then
1308 ! Determine the fluxes through the zonal faces.
1309
1310 ! Find the acceptable range of tracer concentration around this face.
1311138201 if (nPu(I,j) >= 1) then
1312131520 Tr_min_face = min(Tr(m)%t(i,j,1), Tr(m)%t(i+1,j,1))
1313131520 Tr_max_face = max(Tr(m)%t(i,j,1), Tr(m)%t(i+1,j,1))
1314526080 do k=2,nkmb
1315394560 Tr_min_face = min(Tr_min_face, Tr(m)%t(i,j,k), Tr(m)%t(i+1,j,k))
1316526080 Tr_max_face = max(Tr_max_face, Tr(m)%t(i,j,k), Tr(m)%t(i+1,j,k))
1317 enddo
1318
1319 ! Include the next two layers denser than the densest buffer layer.
1320131520 kLa = nkmb+1 ; if (max_kRho(i,j) < nz+1) kLa = max_kRho(i,j)
1321131520 kLb = kLa ; if (max_kRho(i,j) < nz) kLb = max_kRho(i,j)+1
1322131520 kRa = nkmb+1 ; if (max_kRho(i+1,j) < nz+1) kRa = max_kRho(i+1,j)
1323131520 kRb = kRa ; if (max_kRho(i+1,j) < nz) kRb = max_kRho(i+1,j)+1
1324131520 Tr_La = Tr_min_face ; Tr_Lb = Tr_La ; Tr_Ra = Tr_La ; Tr_Rb = Tr_La
1325131520 if (h(i,j,kLa) > h_exclude) Tr_La = Tr(m)%t(i,j,kLa)
1326131520 if ((CS%answer_date <= 20240330) .and. CS%limit_bug) then
13270 if (h(i,j,kLb) > h_exclude) Tr_La = Tr(m)%t(i,j,kLb)
1328 else
1329131520 if (h(i,j,kLb) > h_exclude) Tr_Lb = Tr(m)%t(i,j,kLb)
1330 endif
1331131520 if (h(i+1,j,kRa) > h_exclude) Tr_Ra = Tr(m)%t(i+1,j,kRa)
1332131520 if (h(i+1,j,kRb) > h_exclude) Tr_Rb = Tr(m)%t(i+1,j,kRb)
1333131520 Tr_min_face = min(Tr_min_face, Tr_La, Tr_Lb, Tr_Ra, Tr_Rb)
1334131520 Tr_max_face = max(Tr_max_face, Tr_La, Tr_Lb, Tr_Ra, Tr_Rb)
1335
1336 ! Include all points in diffusive pairings at this face.
1337284877 do k=1,nPu(I,j)
1338153357 Tr_Lb = Tr(m)%t(i,j,k0b_Lu(I,k,j))
1339153357 Tr_Rb = Tr(m)%t(i+1,j,k0b_Ru(I,k,j))
1340153357 Tr_La = Tr_Lb ; Tr_Ra = Tr_Rb
1341153357 if (deep_wt_Lu(I,k,j) < 1.0) Tr_La = Tr(m)%t(i,j,k0a_Lu(I,k,j))
1342153357 if (deep_wt_Ru(I,k,j) < 1.0) Tr_Ra = Tr(m)%t(i+1,j,k0a_Ru(I,k,j))
1343153357 Tr_min_face = min(Tr_min_face, Tr_La, Tr_Lb, Tr_Ra, Tr_Rb)
1344284877 Tr_max_face = max(Tr_max_face, Tr_La, Tr_Lb, Tr_Ra, Tr_Rb)
1345 enddo
1346 endif
1347
1348291558 do k=1,nPu(I,j)
1349153357 kLb = k0b_Lu(I,k,j) ; Tr_Lb = Tr(m)%t(i,j,kLb) ; Tr_av_L = Tr_Lb
1350153357 if (deep_wt_Lu(I,k,j) < 1.0) then
135176020 kLa = k0a_Lu(I,k,j) ; Tr_La = Tr(m)%t(i,j,kLa)
135276020 wt_b = deep_wt_Lu(I,k,j)
135376020 Tr_av_L = wt_b*Tr_Lb + (1.0-wt_b)*Tr_La
1354 endif
1355
1356153357 kRb = k0b_Ru(I,k,j) ; Tr_Rb = Tr(m)%t(i+1,j,kRb) ; Tr_av_R = Tr_Rb
1357153357 if (deep_wt_Ru(I,k,j) < 1.0) then
135876374 kRa = k0a_Ru(I,k,j) ; Tr_Ra = Tr(m)%t(i+1,j,kRa)
135976374 wt_b = deep_wt_Ru(I,k,j)
136076374 Tr_av_R = wt_b*Tr_Rb + (1.0-wt_b)*Tr_Ra
1361 endif
1362
1363153357 h_L = hP_Lu(I,k,j) ; h_R = hP_Ru(I,k,j)
1364153357 if (CS%answer_date <= 20240330) then
1365 Tr_flux = I_maxitt * khdt_epi_x(I,j) * (Tr_av_L - Tr_av_R) * &
13660 ((2.0 * h_L * h_R) / (h_L + h_R))
1367 else
1368 Tr_flux = I_maxitt * ((2.0 * h_L * h_R) / (h_L + h_R)) * &
1369153357 khdt_epi_x(I,j) * (Tr_av_L - Tr_av_R)
1370 endif
1371
1372153357 if (deep_wt_Lu(I,k,j) >= 1.0) then
137377337 if (CS%answer_date <= 20240330) then
13740 tr_flux_conv(i,j,kLb) = tr_flux_conv(i,j,kLb) - Tr_flux
1375 else
137677337 tr_flux_E(i,j,kLb) = tr_flux_E(i,j,kLb) + Tr_flux
1377 endif
1378 else
137976020 Tr_adj_vert = 0.0
138076020 wt_b = deep_wt_Lu(I,k,j) ; wt_a = 1.0 - wt_b
138176020 vol = hP_Lu(I,k,j) * G%areaT(i,j)
1382
1383 ! Ensure that the tracer flux does not drive the tracer values
1384 ! outside of the range Tr_min_face <= Tr <= Tr_max_face, or if it
1385 ! does that the concentration in both contributing pieces exceed
1386 ! this range equally. With down-gradient fluxes and the initial tracer
1387 ! concentrations determining the valid range, the latter condition
1388 ! only enters for large values of the effective diffusive CFL number.
138976020 if (Tr_flux > 0.0) then
13907752 if (Tr_La < Tr_Lb) then ; if (vol*(Tr_La-Tr_min_face) < Tr_flux) &
1391 Tr_adj_vert = -wt_a * min(Tr_flux - vol * (Tr_La-Tr_min_face), &
1392299 (vol*wt_b) * (Tr_Lb - Tr_La))
13936292 else ; if (vol*(Tr_Lb-Tr_min_face) < Tr_flux) &
1394 Tr_adj_vert = wt_b * min(Tr_flux - vol * (Tr_Lb-Tr_min_face), &
13951494 (vol*wt_a) * (Tr_La - Tr_Lb))
1396 endif
139768268 elseif (Tr_flux < 0.0) then
139833058 if (Tr_La > Tr_Lb) then ; if (vol * (Tr_max_face-Tr_La) < -Tr_flux) &
1399 Tr_adj_vert = wt_a * min(-Tr_flux - vol * (Tr_max_face-Tr_La), &
140021839 (vol*wt_b) * (Tr_La - Tr_Lb))
14016306 else ; if (vol*(Tr_max_face-Tr_Lb) < -Tr_flux) &
1402 Tr_adj_vert = -wt_b * min(-Tr_flux - vol * (Tr_max_face-Tr_Lb), &
14031584 (vol*wt_a)*(Tr_Lb - Tr_La))
1404 endif
1405 endif
1406
140776020 if (CS%answer_date <= 20240330) then
14080 tr_flux_conv(i,j,kLa) = tr_flux_conv(i,j,kLa) - (wt_a*Tr_flux + Tr_adj_vert)
14090 tr_flux_conv(i,j,kLb) = tr_flux_conv(i,j,kLb) - (wt_b*Tr_flux - Tr_adj_vert)
1410 else
141176020 tr_flux_E(i,j,kLa) = tr_flux_E(i,j,kLa) + (wt_a*Tr_flux + Tr_adj_vert)
141276020 tr_flux_E(i,j,kLb) = tr_flux_E(i,j,kLb) + (wt_b*Tr_flux - Tr_adj_vert)
1413 endif
1414 endif
1415
1416153357 if (deep_wt_Ru(I,k,j) >= 1.0) then
141776983 if (CS%answer_date <= 20240330) then
14180 tr_flux_conv(i+1,j,kRb) = tr_flux_conv(i+1,j,kRb) + Tr_flux
1419 else
142076983 tr_flux_W(i+1,j,kRb) = tr_flux_W(i+1,j,kRb) + Tr_flux
1421 endif
1422 else
142376374 Tr_adj_vert = 0.0
142476374 wt_b = deep_wt_Ru(I,k,j) ; wt_a = 1.0 - wt_b
142576374 vol = hP_Ru(I,k,j) * G%areaT(i+1,j)
1426
1427 ! Ensure that the tracer flux does not drive the tracer values
1428 ! outside of the range Tr_min_face <= Tr <= Tr_max_face, or if it
1429 ! does that the concentration in both contributing pieces exceed
1430 ! this range equally. With down-gradient fluxes and the initial tracer
1431 ! concentrations determining the valid range, the latter condition
1432 ! only enters for large values of the effective diffusive CFL number.
143376374 if (Tr_flux < 0.0) then
14347808 if (Tr_Ra < Tr_Rb) then ; if (vol * (Tr_Ra-Tr_min_face) < -Tr_flux) &
1435 Tr_adj_vert = -wt_a * min(-Tr_flux - vol * (Tr_Ra-Tr_min_face), &
1436283 (vol*wt_b) * (Tr_Rb - Tr_Ra))
14376384 else ; if (vol*(Tr_Rb-Tr_min_face) < (-Tr_flux)) &
1438 Tr_adj_vert = wt_b * min(-Tr_flux - vol * (Tr_Rb-Tr_min_face), &
14391499 (vol*wt_a) * (Tr_Ra - Tr_Rb))
1440 endif
144168566 elseif (Tr_flux > 0.0) then
144233228 if (Tr_Ra > Tr_Rb) then ; if (vol * (Tr_max_face-Tr_Ra) < Tr_flux) &
1443 Tr_adj_vert = wt_a * min(Tr_flux - vol * (Tr_max_face-Tr_Ra), &
144421891 (vol*wt_b) * (Tr_Ra - Tr_Rb))
14456432 else ; if (vol*(Tr_max_face-Tr_Rb) < Tr_flux) &
1446 Tr_adj_vert = -wt_b * min(Tr_flux - vol * (Tr_max_face-Tr_Rb), &
14471602 (vol*wt_a)*(Tr_Rb - Tr_Ra))
1448 endif
1449 endif
1450
145176374 if (CS%answer_date <= 20240330) then
14520 tr_flux_conv(i+1,j,kRa) = tr_flux_conv(i+1,j,kRa) + (wt_a*Tr_flux - Tr_adj_vert)
14530 tr_flux_conv(i+1,j,kRb) = tr_flux_conv(i+1,j,kRb) + (wt_b*Tr_flux + Tr_adj_vert)
1454 else
145576374 tr_flux_W(i+1,j,kRa) = tr_flux_W(i+1,j,kRa) + (wt_a*Tr_flux - Tr_adj_vert)
145676374 tr_flux_W(i+1,j,kRb) = tr_flux_W(i+1,j,kRb) + (wt_b*Tr_flux + Tr_adj_vert)
1457 endif
1458 endif
1459153357 if (associated(Tr(m)%df2d_x)) &
1460138201 Tr(m)%df2d_x(I,j) = Tr(m)%df2d_x(I,j) + Tr_flux * Idt
1461 enddo ! Loop over pairings at faces.
1462 endif ; enddo ; enddo ! i- & j- loops over zonal faces.
1463
1464 ! this gives wrong result when using do concurrent on NVHPC 25.9
1465 !$omp target teams loop collapse(2) private(Tr_min_face,Tr_max_face,kLa,kLb,kRa,kRb, &
1466 !$omp Tr_La,Tr_Lb,Tr_Ra,Tr_Rb,Tr_av_L,wt_b,Tr_av_R, &
1467 !$omp h_L,h_R,Tr_flux,Tr_adj_vert,wt_a,vol)
1468196026 do J=js-1,je ; do i=is,ie ; if (G%mask2dCv(i,J) > 0.0) then
1469 ! Determine the fluxes through the meridional faces.
1470
1471 ! Find the acceptable range of tracer concentration around this face.
1472137670 if (nPv(i,J) >= 1) then
1473130746 Tr_min_face = min(Tr(m)%t(i,j,1), Tr(m)%t(i,j+1,1))
1474130746 Tr_max_face = max(Tr(m)%t(i,j,1), Tr(m)%t(i,j+1,1))
1475522984 do k=2,nkmb
1476392238 Tr_min_face = min(Tr_min_face, Tr(m)%t(i,j,k), Tr(m)%t(i,j+1,k))
1477522984 Tr_max_face = max(Tr_max_face, Tr(m)%t(i,j,k), Tr(m)%t(i,j+1,k))
1478 enddo
1479
1480 ! Include the next two layers denser than the densest buffer layer.
1481130746 kLa = nkmb+1 ; if (max_kRho(i,j) < nz+1) kLa = max_kRho(i,j)
1482130746 kLb = kLa ; if (max_kRho(i,j) < nz) kLb = max_kRho(i,j)+1
1483130746 kRa = nkmb+1 ; if (max_kRho(i,j+1) < nz+1) kRa = max_kRho(i,j+1)
1484130746 kRb = kRa ; if (max_kRho(i,j+1) < nz) kRb = max_kRho(i,j+1)+1
1485130746 Tr_La = Tr_min_face ; Tr_Lb = Tr_La ; Tr_Ra = Tr_La ; Tr_Rb = Tr_La
1486130746 if (h(i,j,kLa) > h_exclude) Tr_La = Tr(m)%t(i,j,kLa)
1487130746 if ((CS%answer_date <= 20240330) .and. CS%limit_bug) then
14880 if (h(i,j,kLb) > h_exclude) Tr_La = Tr(m)%t(i,j,kLb)
1489 else
1490130746 if (h(i,j,kLb) > h_exclude) Tr_Lb = Tr(m)%t(i,j,kLb)
1491 endif
1492130746 if (h(i,j+1,kRa) > h_exclude) Tr_Ra = Tr(m)%t(i,j+1,kRa)
1493130746 if (h(i,j+1,kRb) > h_exclude) Tr_Rb = Tr(m)%t(i,j+1,kRb)
1494130746 Tr_min_face = min(Tr_min_face, Tr_La, Tr_Lb, Tr_Ra, Tr_Rb)
1495130746 Tr_max_face = max(Tr_max_face, Tr_La, Tr_Lb, Tr_Ra, Tr_Rb)
1496
1497 ! Include all points in diffusive pairings at this face.
1498274950 do k=1,nPv(i,J)
1499144204 Tr_Lb = Tr(m)%t(i,j,k0b_Lv(i,k,J)) ; Tr_Rb = Tr(m)%t(i,j+1,k0b_Rv(i,k,J))
1500144204 Tr_La = Tr_Lb ; Tr_Ra = Tr_Rb
1501144204 if (deep_wt_Lv(I,k,J) < 1.0) Tr_La = Tr(m)%t(i,j,k0a_Lv(i,k,J))
1502144204 if (deep_wt_Rv(I,k,J) < 1.0) Tr_Ra = Tr(m)%t(i,j+1,k0a_Rv(i,k,J))
1503144204 Tr_min_face = min(Tr_min_face, Tr_La, Tr_Lb, Tr_Ra, Tr_Rb)
1504274950 Tr_max_face = max(Tr_max_face, Tr_La, Tr_Lb, Tr_Ra, Tr_Rb)
1505 enddo
1506 endif
1507
1508281874 do k=1,nPv(i,J)
1509144204 kLb = k0b_Lv(i,k,J) ; Tr_Lb = Tr(m)%t(i,j,kLb) ; Tr_av_L = Tr_Lb
1510144204 if (deep_wt_Lv(I,k,J) < 1.0) then
151113458 kLa = k0a_Lv(i,k,J) ; Tr_La = Tr(m)%t(i,j,kLa)
151213458 wt_b = deep_wt_Lv(I,k,J)
151313458 Tr_av_L = wt_b * Tr_Lb + (1.0-wt_b) * Tr_La
1514 endif
1515
1516144204 kRb = k0b_Rv(i,k,J) ; Tr_Rb = Tr(m)%t(i,j+1,kRb) ; Tr_av_R = Tr_Rb
1517144204 if (deep_wt_Rv(I,k,J) < 1.0) then
1518130746 kRa = k0a_Rv(i,k,J) ; Tr_Ra = Tr(m)%t(i,j+1,kRa)
1519130746 wt_b = deep_wt_Rv(I,k,J)
1520130746 Tr_av_R = wt_b * Tr_Rb + (1.0-wt_b) * Tr_Ra
1521 endif
1522
1523144204 h_L = hP_Lv(I,k,J) ; h_R = hP_Rv(I,k,J)
1524 Tr_flux = I_maxitt * ((2.0 * h_L * h_R) / (h_L + h_R)) * &
1525144204 khdt_epi_y(i,J) * (Tr_av_L - Tr_av_R)
1526144204 Tr_flux_3d(i,J,k) = Tr_flux
1527
1528144204 if (deep_wt_Lv(I,k,J) < 1.0) then
152913458 Tr_adj_vert = 0.0
153013458 wt_b = deep_wt_Lv(I,k,J) ; wt_a = 1.0 - wt_b
153113458 vol = hP_Lv(I,k,J) * G%areaT(i,j)
1532
1533 ! Ensure that the tracer flux does not drive the tracer values
1534 ! outside of the range Tr_min_face <= Tr <= Tr_max_face.
153513458 if (Tr_flux > 0.0) then
15361214 if (Tr_La < Tr_Lb) then ; if (vol * (Tr_La-Tr_min_face) < Tr_flux) &
1537 Tr_adj_vert = -wt_a * min(Tr_flux - vol * (Tr_La-Tr_min_face), &
1538120 (vol*wt_b) * (Tr_Lb - Tr_La))
1539861 else ; if (vol*(Tr_Lb-Tr_min_face) < Tr_flux) &
1540 Tr_adj_vert = wt_b * min(Tr_flux - vol * (Tr_Lb-Tr_min_face), &
1541129 (vol*wt_a) * (Tr_La - Tr_Lb))
1542 endif
154312244 elseif (Tr_flux < 0.0) then
15445705 if (Tr_La > Tr_Lb) then ; if (vol * (Tr_max_face-Tr_La) < -Tr_flux) &
1545 Tr_adj_vert = wt_a * min(-Tr_flux - vol * (Tr_max_face-Tr_La), &
15464437 (vol*wt_b) * (Tr_La - Tr_Lb))
1547868 else ; if (vol*(Tr_max_face-Tr_Lb) < -Tr_flux) &
1548 Tr_adj_vert = -wt_b * min(-Tr_flux - vol * (Tr_max_face-Tr_Lb), &
1549110 (vol*wt_a)*(Tr_Lb - Tr_La))
1550 endif
1551 endif
155213458 Tr_adj_vert_L(i,J,k) = Tr_adj_vert
1553 endif
1554
1555144204 if (deep_wt_Rv(I,k,J) < 1.0) then
1556130746 Tr_adj_vert = 0.0
1557130746 wt_b = deep_wt_Rv(I,k,J) ; wt_a = 1.0 - wt_b
1558130746 vol = hP_Rv(I,k,J) * G%areaT(i,j+1)
1559
1560 ! Ensure that the tracer flux does not drive the tracer values
1561 ! outside of the range Tr_min_face <= Tr <= Tr_max_face.
1562130746 if (Tr_flux < 0.0) then
156312828 if (Tr_Ra < Tr_Rb) then ; if (vol * (Tr_Ra-Tr_min_face) < -Tr_flux) &
1564 Tr_adj_vert = -wt_a * min(-Tr_flux - vol * (Tr_Ra-Tr_min_face), &
1565478 (vol*wt_b) * (Tr_Rb - Tr_Ra))
156610461 else ; if (vol*(Tr_Rb-Tr_min_face) < (-Tr_flux)) &
1567 Tr_adj_vert = wt_b * min(-Tr_flux - vol * (Tr_Rb-Tr_min_face), &
15682201 (vol*wt_a) * (Tr_Ra - Tr_Rb))
1569 endif
1570117918 elseif (Tr_flux > 0.0) then
157156086 if (Tr_Ra > Tr_Rb) then ; if (vol * (Tr_max_face-Tr_Ra) < Tr_flux) &
1572 Tr_adj_vert = wt_a * min(Tr_flux - vol * (Tr_max_face-Tr_Ra), &
157336185 (vol*wt_b) * (Tr_Ra - Tr_Rb))
157410215 else ; if (vol*(Tr_max_face-Tr_Rb) < Tr_flux) &
1575 Tr_adj_vert = -wt_b * min(Tr_flux - vol * (Tr_max_face-Tr_Rb), &
15762223 (vol*wt_a)*(Tr_Rb - Tr_Ra))
1577 endif
1578 endif
1579130746 Tr_adj_vert_R(i,J,k) = Tr_adj_vert
1580 endif
1581144204 if (associated(Tr(m)%df2d_y)) &
1582137670 Tr(m)%df2d_y(i,J) = Tr(m)%df2d_y(i,J) + Tr_flux * Idt
1583 enddo ! Loop over pairings at faces.
1584 endif ; enddo ; enddo ! i- & j- loops over meridional faces.
1585
1586 ! The non-stride-1 loop order here is to facilitate openMP threading. However, it might be
1587 ! suboptimal when openMP threading is not used, at which point it might be better to fuse
1588 ! this loop with those that precede it and thereby eliminate the need for three 3-d arrays.
15893 if (CS%answer_date <= 20240330) then
1590 ! KRa/b aren't guaranteed to be unique and update of tr_flux_conv(:, j/j+1, :) means this
1591 ! loop must be serial in both j and k. i can be paralellised.
15920 do concurrent (i=is:ie) DO_LOCALITY(local(j, k, kLb, kRb, kLa, wt_b, wt_a, kRa))
15930 do J=js-1,je ; if (G%mask2dCv(i,J) > 0.0) then
15940 do k=1,nPv(i,J)
15950 kLb = k0b_Lv(i,k,J) ; kRb = k0b_Rv(i,k,J)
15960 if (deep_wt_Lv(I,k,J) >= 1.0) then
15970 tr_flux_conv(i,j,kLb) = tr_flux_conv(i,j,kLb) - Tr_flux_3d(i,J,k)
1598 else
15990 kLa = k0a_Lv(i,k,J)
16000 wt_b = deep_wt_Lv(I,k,J) ; wt_a = 1.0 - wt_b
16010 tr_flux_conv(i,j,kLa) = tr_flux_conv(i,j,kLa) - (wt_a*Tr_flux_3d(i,J,k) + Tr_adj_vert_L(i,J,k))
16020 tr_flux_conv(i,j,kLb) = tr_flux_conv(i,j,kLb) - (wt_b*Tr_flux_3d(i,J,k) - Tr_adj_vert_L(i,J,k))
1603 endif
16040 if (deep_wt_Rv(I,k,J) >= 1.0) then
16050 tr_flux_conv(i,j+1,kRb) = tr_flux_conv(i,j+1,kRb) + Tr_flux_3d(i,J,k)
1606 else
16070 kRa = k0a_Rv(i,k,J)
16080 wt_b = deep_wt_Rv(I,k,J) ; wt_a = 1.0 - wt_b
1609 tr_flux_conv(i,j+1,kRa) = tr_flux_conv(i,j+1,kRa) + &
16100 (wt_a*Tr_flux_3d(i,J,k) - Tr_adj_vert_R(i,J,k))
1611 tr_flux_conv(i,j+1,kRb) = tr_flux_conv(i,j+1,kRb) + &
16120 (wt_b*Tr_flux_3d(i,J,k) + Tr_adj_vert_R(i,J,k))
1613 endif
1614 enddo
1615 endif ; enddo
1616 enddo
1617 else
1618 ! Update of tr_flux_N/S can be done independently so both i and j can be parallelised.
1619196563 do concurrent (J=js-1:je, i=is:ie, G%mask2dCv(i,J) > 0.0) DO_LOCALITY(local(k, kLb, kRb, kLa, wt_b, wt_a, kRa))
1620340767 do k=1,nPv(i,J)
1621144204 kLb = k0b_Lv(i,k,J) ; kRb = k0b_Rv(i,k,J)
1622144204 if (deep_wt_Lv(I,k,J) >= 1.0) then
1623130746 tr_flux_N(i,j,kLb) = tr_flux_N(i,j,kLb) + Tr_flux_3d(i,J,k)
1624 else
162513458 kLa = k0a_Lv(i,k,J)
162613458 wt_b = deep_wt_Lv(I,k,J) ; wt_a = 1.0 - wt_b
162713458 tr_flux_N(i,j,kLa) = tr_flux_N(i,j,kLa) + (wt_a*Tr_flux_3d(i,J,k) + Tr_adj_vert_L(i,J,k))
162813458 tr_flux_N(i,j,kLb) = tr_flux_N(i,j,kLb) + (wt_b*Tr_flux_3d(i,J,k) - Tr_adj_vert_L(i,J,k))
1629 endif
1630281874 if (deep_wt_Rv(I,k,J) >= 1.0) then
163113458 tr_flux_S(i,j+1,kRb) = tr_flux_S(i,j+1,kRb) + Tr_flux_3d(i,J,k)
1632 else
1633130746 kRa = k0a_Rv(i,k,J)
1634130746 wt_b = deep_wt_Rv(I,k,J) ; wt_a = 1.0 - wt_b
1635130746 tr_flux_S(i,j+1,kRa) = tr_flux_S(i,j+1,kRa) + (wt_a*Tr_flux_3d(i,J,k) - Tr_adj_vert_R(i,J,k))
1636130746 tr_flux_S(i,j+1,kRb) = tr_flux_S(i,j+1,kRb) + (wt_b*Tr_flux_3d(i,J,k) + Tr_adj_vert_R(i,J,k))
1637 endif
1638 enddo
1639 enddo
1640 endif
1641
16423 if (CS%answer_date >= 20240331) then
16433 do concurrent (k=1:PEmax_kRho, j=js:je, i=is:ie)
1644 tr_flux_conv(i,j,k) = ((tr_flux_W(i,j,k) - tr_flux_E(i,j,k)) + &
16454083483 (tr_flux_S(i,j,k) - tr_flux_N(i,j,k)))
1646 enddo
1647 endif
1648
16494083483 do concurrent (k=1:PEmax_kRho, j=js:je, i=is:ie, (G%mask2dT(i,j) > 0.0) .and. (h(i,j,k) > 0.0))
16504083483 Tr(m)%t(i,j,k) = Tr(m)%t(i,j,k) + tr_flux_conv(i,j,k) / (h(i,j,k)*G%areaT(i,j))
1651 enddo
1652
1653 ! Do user controlled underflow of the tracer concentrations.
16544 if (Tr(m)%conc_underflow > 0.0) then
1655 !$omp target update from(Tr(m)%t)
1656 !$OMP parallel do default(shared)
16570 do k=1,nz ; do j=js,je ; do i=is,ie
16580 if (abs(Tr(m)%t(i,j,k)) < Tr(m)%conc_underflow) Tr(m)%t(i,j,k) = 0.0
1659 enddo ; enddo ; enddo
1660 !$omp target update to(Tr(m)%t)
1661 endif
1662
1663 enddo ! Loop over tracers
1664 enddo ! Loop over iterations
1665
1666 !$omp target exit data map(release: Tr_flux_3d, Tr_adj_vert_L, Tr_adj_vert_R, tr_flux_N, &
1667 !$omp tr_flux_S, tr_flux_E, tr_flux_W, tr_flux_conv, nPv, nPu, max_kRho, rho_srt, num_srt, &
1668 !$omp max_srt)
1669
1670 !$omp target exit data map(release: deep_wt_Lu, deep_wt_Ru, hP_Lu, hP_Ru, k0a_Lu, k0a_Ru, &
1671 !$omp k0b_Lu, k0b_Ru)
1672
1673 !$omp target exit data map(release: deep_wt_Lv, deep_wt_Rv, hP_Lv, hP_Rv, k0a_Lv, k0a_Rv, &
1674 !$Omp k0b_Lv, k0b_Rv)
1675
16761end subroutine tracer_epipycnal_ML_diff
1677
1678
1679!> Initialize lateral tracer diffusion module
16801subroutine tracer_hor_diff_init(Time, G, GV, US, param_file, diag, EOS, diabatic_CSp, CS)
1681 type(time_type), target, intent(in) :: Time !< current model time
1682 type(ocean_grid_type), intent(in) :: G !< ocean grid structure
1683 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
1684 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1685 type(diag_ctrl), target, intent(inout) :: diag !< diagnostic control
1686 type(EOS_type), target, intent(in) :: EOS !< Equation of state CS
1687 type(diabatic_CS), pointer, intent(in) :: diabatic_CSp !< Equation of state CS
1688 type(param_file_type), intent(in) :: param_file !< parameter file
1689 type(tracer_hor_diff_CS), pointer :: CS !< horz diffusion control structure
1690
1691 ! This include declares and sets the variable "version".
1692# include "version_variable.h"
1693 character(len=40) :: mdl = "MOM_tracer_hor_diff" ! This module's name.
1694 integer :: default_answer_date
1695
16961 if (associated(CS)) then
16970 call MOM_error(WARNING, "tracer_hor_diff_init called with associated control structure.")
16980 return
1699 endif
17001 allocate(CS)
1701
17021 CS%diag => diag
17031 CS%show_call_tree = callTree_showQuery()
1704
1705 ! Read all relevant parameters and write them to the model log.
17061 call log_version(param_file, mdl, version, "")
1707 call get_param(param_file, mdl, "KHTR", CS%KhTr, &
1708 "The background along-isopycnal tracer diffusivity.", &
17091 units="m2 s-1", default=0.0, scale=US%m_to_L**2*US%T_to_s)
1710! call get_param(param_file, mdl, "KHTR_USE_EBT_STRUCT", CS%KhTh_use_ebt_struct, &
1711! "If true, uses the equivalent barotropic structure "//&
1712! "as the vertical structure of the tracer diffusivity.",&
1713! default=.false.)
1714 call get_param(param_file, mdl, "KHTR_SLOPE_CFF", CS%KhTr_Slope_Cff, &
1715 "The scaling coefficient for along-isopycnal tracer "//&
1716 "diffusivity using a shear-based (Visbeck-like) "//&
1717 "parameterization. A non-zero value enables this param.", &
17181 units="nondim", default=0.0)
1719 call get_param(param_file, mdl, "KHTR_MIN", CS%KhTr_Min, &
1720 "The minimum along-isopycnal tracer diffusivity.", &
17211 units="m2 s-1", default=0.0, scale=US%m_to_L**2*US%T_to_s)
17221 if (CS%KhTr_Min > 0.0) then
1723 call get_param(param_file, mdl, "FULL_DEPTH_KHTR_MIN", CS%full_depth_khtr_min, &
1724 "If true, KHTR_MIN is enforced throughout the whole water column. "//&
1725 "Otherwise, KHTR_MIN is only enforced at the surface. This parameter "//&
1726 "is only available when KHTR_USE_EBT_STRUCT=True and KHTR_MIN>0.", &
17270 default=.false.)
1728 endif
1729 call get_param(param_file, mdl, "KHTR_MAX", CS%KhTr_Max, &
1730 "The maximum along-isopycnal tracer diffusivity.", &
17311 units="m2 s-1", default=0.0, scale=US%m_to_L**2*US%T_to_s)
1732 call get_param(param_file, mdl, "KHTR_PASSIVITY_COEFF", CS%KhTr_passivity_coeff, &
1733 "The coefficient that scales deformation radius over "//&
1734 "grid-spacing in passivity, where passivity is the ratio "//&
1735 "between along isopycnal mixing of tracers to thickness mixing. "//&
1736 "A non-zero value enables this parameterization.", &
17371 units="nondim", default=0.0)
1738 call get_param(param_file, mdl, "KHTR_PASSIVITY_MIN", CS%KhTr_passivity_min, &
1739 "The minimum passivity which is the ratio between "//&
1740 "along isopycnal mixing of tracers to thickness mixing.", &
17411 units="nondim", default=0.5)
1742 call get_param(param_file, mdl, "DIFFUSE_ML_TO_INTERIOR", CS%Diffuse_ML_interior, &
1743 "If true, enable epipycnal mixing between the surface "//&
17441 "boundary layer and the interior.", default=.false.)
1745 call get_param(param_file, mdl, "CHECK_DIFFUSIVE_CFL", CS%check_diffusive_CFL, &
1746 "If true, use enough iterations the diffusion to ensure "//&
1747 "that the diffusive equivalent of the CFL limit is not "//&
1748 "violated. If false, always use the greater of 1 or "//&
17491 "MAX_TR_DIFFUSION_CFL iteration.", default=.false.)
1750 call get_param(param_file, mdl, "MAX_TR_DIFFUSION_CFL", CS%max_diff_CFL, &
1751 "If positive, locally limit the along-isopycnal tracer "//&
1752 "diffusivity to keep the diffusive CFL locally at or "//&
1753 "below this value. The number of diffusive iterations "//&
1754 "is often this value or the next greater integer.", &
17551 units="nondim", default=-1.0)
1756 call get_param(param_File, mdl, "RECALC_NEUTRAL_SURF", CS%recalc_neutral_surf, &
1757 "If true, then recalculate the neutral surfaces if the \n"//&
1758 "diffusive CFL is exceeded. If false, assume that the \n"//&
17591 "positions of the surfaces do not change \n", default=.false.)
1760 call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, &
1761 "This sets the default value for the various _ANSWER_DATE parameters.", &
17621 default=99991231, do_not_log=.true.)
1763 call get_param(param_file, mdl, "HOR_DIFF_ANSWER_DATE", CS%answer_date, &
1764 "The vintage of the order of arithmetic to use for the tracer diffusion. "//&
1765 "Values of 20240330 or below recover the answers from the original form of the "//&
1766 "along-isopycnal mixed layer to interior mixing code, while higher values use "//&
1767 "mathematically equivalent expressions that recover rotational symmetry "//&
1768 "when DIFFUSE_ML_TO_INTERIOR is true.", &
17691 default=default_answer_date, do_not_log=.not.CS%Diffuse_ML_interior)
1770 call get_param(param_file, mdl, "HOR_DIFF_LIMIT_BUG", CS%limit_bug, &
1771 "If true and the answer date is 20240330 or below, use a rotational symmetry "//&
1772 "breaking bug when limiting the tracer properties in tracer_epipycnal_ML_diff.", &
17731 default=.false., do_not_log=((.not.CS%Diffuse_ML_interior).or.(CS%answer_date>=20240331)))
17741 CS%ML_KhTR_scale = 1.0
17751 if (CS%Diffuse_ML_interior) then
1776 call get_param(param_file, mdl, "ML_KHTR_SCALE", CS%ML_KhTR_scale, &
1777 "With Diffuse_ML_interior, the ratio of the truly "//&
1778 "horizontal diffusivity in the mixed layer to the "//&
1779 "epipycnal diffusivity. The valid range is 0 to 1.", &
17801 units="nondim", default=1.0)
1781 endif
1782
1783 CS%use_neutral_diffusion = neutral_diffusion_init(Time, G, GV, US, param_file, diag, EOS, &
17841 diabatic_CSp, CS%neutral_diffusion_CSp )
17851 if (CS%use_neutral_diffusion .and. CS%Diffuse_ML_interior) call MOM_error(FATAL, "MOM_tracer_hor_diff: "// &
17860 "USE_NEUTRAL_DIFFUSION and DIFFUSE_ML_TO_INTERIOR are mutually exclusive!")
1787 CS%use_hor_bnd_diffusion = hor_bnd_diffusion_init(Time, G, GV, US, param_file, diag, diabatic_CSp, &
17881 CS%hor_bnd_diffusion_CSp)
17891 if (CS%use_hor_bnd_diffusion .and. CS%Diffuse_ML_interior) call MOM_error(FATAL, "MOM_tracer_hor_diff: "// &
17900 "USE_HORIZONTAL_BOUNDARY_DIFFUSION and DIFFUSE_ML_TO_INTERIOR are mutually exclusive!")
1791
17921 call get_param(param_file, mdl, "DEBUG", CS%debug, default=.false.)
1793
17941 id_clock_diffuse = cpu_clock_id('(Ocean diffuse tracer)', grain=CLOCK_MODULE)
17951 id_clock_epimix = cpu_clock_id('(Ocean epipycnal diffuse tracer)',grain=CLOCK_MODULE)
17961 id_clock_pass = cpu_clock_id('(Ocean tracer halo updates)', grain=CLOCK_ROUTINE)
17971 id_clock_sync = cpu_clock_id('(Ocean tracer global synch)', grain=CLOCK_ROUTINE)
1798
17991 CS%id_KhTr_u = -1
18001 CS%id_KhTr_v = -1
18011 CS%id_KhTr_h = -1
18021 CS%id_CFL = -1
1803
1804 CS%id_KhTr_u = register_diag_field('ocean_model', 'KHTR_u', diag%axesCui, Time, &
18051 'Epipycnal tracer diffusivity at zonal faces of tracer cell', 'm2 s-1', conversion=US%L_to_m**2*US%s_to_T)
1806 CS%id_KhTr_v = register_diag_field('ocean_model', 'KHTR_v', diag%axesCvi, Time, &
18071 'Epipycnal tracer diffusivity at meridional faces of tracer cell', 'm2 s-1', conversion=US%L_to_m**2*US%s_to_T)
1808 CS%id_KhTr_h = register_diag_field('ocean_model', 'KHTR_h', diag%axesTi, Time, &
1809 'Epipycnal tracer diffusivity at tracer cell center', 'm2 s-1', conversion=US%L_to_m**2*US%s_to_T, &
1810 cmor_field_name='diftrelo', &
1811 cmor_standard_name= 'ocean_tracer_epineutral_laplacian_diffusivity', &
18121 cmor_long_name = 'Ocean Tracer Epineutral Laplacian Diffusivity')
1813
1814 CS%id_khdt_x = register_diag_field('ocean_model', 'KHDT_x', diag%axesCu1, Time, &
18151 'Epipycnal tracer diffusivity operator at zonal faces of tracer cell', 'm2', conversion=US%L_to_m**2)
1816 CS%id_khdt_y = register_diag_field('ocean_model', 'KHDT_y', diag%axesCv1, Time, &
18171 'Epipycnal tracer diffusivity operator at meridional faces of tracer cell', 'm2', conversion=US%L_to_m**2)
18181 if (CS%check_diffusive_CFL) then
1819 CS%id_CFL = register_diag_field('ocean_model', 'CFL_lateral_diff', diag%axesT1, Time,&
18200 'Grid CFL number for lateral/neutral tracer diffusion', 'nondim')
1821 endif
1822
1823
1824end subroutine tracer_hor_diff_init
1825
18261subroutine tracer_hor_diff_end(CS)
1827 type(tracer_hor_diff_CS), pointer :: CS !< module control structure
1828
18291 call neutral_diffusion_end(CS%neutral_diffusion_CSp)
18301 call hor_bnd_diffusion_end(CS%hor_bnd_diffusion_CSp)
18311 if (associated(CS)) deallocate(CS)
1832
18331end subroutine tracer_hor_diff_end
1834
1835
1836!> \namespace mom_tracer_hor_diff
1837!!
1838!! \section section_intro Introduction to the module
1839!!
1840!! This module contains subroutines that handle horizontal
1841!! diffusion (i.e., isoneutral or along layer) of tracers.
1842!!
1843!! Each of the tracers are subject to Fickian along-coordinate
1844!! diffusion if Khtr is defined and positive. The tracer diffusion
1845!! can use a suitable number of iterations to guarantee stability
1846!! with an arbitrarily large time step.
1847
18480end module MOM_tracer_hor_diff