← back to index

src/tracer/MOM_tracer_advect.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!> This module contains the subroutines that advect tracers along coordinate surfaces.
8module MOM_tracer_advect
9
10use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end
11use MOM_cpu_clock, only : CLOCK_MODULE, CLOCK_ROUTINE
12use MOM_diag_mediator, only : post_data, query_averaging_enabled, diag_ctrl
13use MOM_diag_mediator, only : register_diag_field, safe_alloc_ptr, time_type
14use MOM_domains, only : sum_across_PEs, max_across_PEs
15use MOM_domains, only : create_group_pass, do_group_pass, group_pass_type, pass_var
16use MOM_error_handler, only : MOM_error, FATAL, WARNING, MOM_mesg, is_root_pe
17use MOM_file_parser, only : get_param, log_version, param_file_type
18use MOM_grid, only : ocean_grid_type
19use MOM_open_boundary, only : ocean_OBC_type, OBC_NONE, OBC_DIRECTION_E
20use MOM_open_boundary, only : OBC_DIRECTION_W, OBC_DIRECTION_N, OBC_DIRECTION_S
21use MOM_open_boundary, only : OBC_segment_type
22use MOM_tracer_registry, only : tracer_registry_type, tracer_type
23use MOM_unit_scaling, only : unit_scale_type
24use MOM_verticalGrid, only : verticalGrid_type
25use MOM_tracer_advect_schemes, only : ADVECT_PLM, ADVECT_PPMH3, ADVECT_PPM
26use MOM_tracer_advect_schemes, only : set_tracer_advect_scheme, TracerAdvectionSchemeDoc
27implicit none ; private
28
29#include <MOM_memory.h>
30
31public advect_tracer
32public tracer_advect_init
33public tracer_advect_end
34
35!> Control structure for this module
36type, public :: tracer_advect_CS ; private
37 real :: dt !< The baroclinic dynamics time step [T ~> s].
38 type(diag_ctrl), pointer :: diag !< A structure that is used to regulate the
39 !< timing of diagnostic output.
40 logical :: debug !< If true, write verbose checksums for debugging purposes.
41 logical :: useHuynhStencilBug = .false. !< If true, use the incorrect stencil width.
42 !! This is provided for compatibility with legacy simuations.
43 type(group_pass_type) :: pass_uhr_vhr_t_hprev !< A structure used for group passes
44 integer :: default_advect_scheme = -1 !< Determines which reconstruction to use
45end type tracer_advect_CS
46
47!>@{ CPU time clocks
48integer :: id_clock_advect
49integer :: id_clock_pass
50integer :: id_clock_sync
51!>@}
52
53contains
54
55!> This routine time steps the tracer concentration using a
56!! monotonic, conservative, weakly diffusive scheme.
57720subroutine advect_tracer(h_end, uhtr, vhtr, OBC, dt, G, GV, US, CS, Reg, x_first_in, &
580 vol_prev, max_iter_in, update_vol_prev, uhr_out, vhr_out)
59 type(ocean_grid_type), intent(inout) :: G !< ocean grid structure
60 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
61 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
62 intent(in) :: h_end !< Layer thickness after advection [H ~> m or kg m-2]
63 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), &
64 intent(in) :: uhtr !< Accumulated volume or mass flux through the
65 !! zonal faces [H L2 ~> m3 or kg]
66 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), &
67 intent(in) :: vhtr !< Accumulated volume or mass flux through the
68 !! meridional faces [H L2 ~> m3 or kg]
69 type(ocean_OBC_type), pointer :: OBC !< specifies whether, where, and what OBCs are used
70 real, intent(in) :: dt !< time increment [T ~> s]
71 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
72 type(tracer_advect_CS), pointer :: CS !< control structure for module
73 type(tracer_registry_type), pointer :: Reg !< pointer to tracer registry
74 logical, optional, intent(in) :: x_first_in !< If present, indicate whether to update
75 !! first in the x- or y-direction.
76 ! The remaining optional arguments are only used in offline tracer mode.
77 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
78 optional, intent(inout) :: vol_prev !< Cell volume before advection [H L2 ~> m3 or kg].
79 !! If update_vol_prev is true, the returned value is
80 !! the cell volume after the transport that was done
81 !! by this call, and if all the transport could be
82 !! accommodated it should be close to h_end*G%areaT.
83 integer, optional, intent(in) :: max_iter_in !< The maximum number of iterations
84 logical, optional, intent(in) :: update_vol_prev !< If present and true, update vol_prev to
85 !! return its value after the tracer have been updated.
86 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), &
87 optional, intent(out) :: uhr_out !< Remaining accumulated volume or mass fluxes
88 !! through the zonal faces [H L2 ~> m3 or kg]
89 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), &
90 optional, intent(out) :: vhr_out !< Remaining accumulated volume or mass fluxes
91 !! through the meridional faces [H L2 ~> m3 or kg]
92
93 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: &
94720 hprev ! cell volume at the end of previous tracer change [H L2 ~> m3 or kg]
95 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: &
96720 uhr ! The remaining zonal thickness flux [H L2 ~> m3 or kg]
97 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: &
98720 vhr ! The remaining meridional thickness fluxes [H L2 ~> m3 or kg]
99720 real :: uh_neglect(SZIB_(G),SZJ_(G)) ! uh_neglect and vh_neglect are the
100720 real :: vh_neglect(SZI_(G),SZJB_(G)) ! magnitude of remaining transports that
101 ! can be simply discarded [H L2 ~> m3 or kg].
102
103 real :: landvolfill ! An arbitrary? nonzero cell volume [H L2 ~> m3 or kg].
104 logical :: use_PPM_stencil ! If true, use the correct PPM stencil width.
105 real :: Idt ! 1/dt [T-1 ~> s-1].
106720 logical :: domore_u(SZJ_(G),SZK_(GV)) ! domore_u and domore_v indicate whether there is more
107720 logical :: domore_v(SZJB_(G),SZK_(GV)) ! advection to be done in the corresponding row or column.
108 logical :: x_first ! If true, advect in the x-direction first.
109 integer :: max_iter ! maximum number of iterations in each layer
1100 integer :: domore_k(SZK_(GV))
111 integer :: stencil ! stencil of the advection scheme
112 integer :: nsten_halo ! number of stencils that fit in the halos
113 integer :: i, j, k, m, is, ie, js, je, isd, ied, jsd, jed, nz, itt, ntr, do_any
114 integer :: isv, iev, jsv, jev ! The valid range of the indices.
115 integer :: IsdB, IedB, JsdB, JedB
116 integer :: stencil_local ! Stencil for the local adection scheme
117360 integer :: local_advect_scheme(Reg%ntr) ! contains the list of the advection for each tracer
118 integer :: domore_k_tmp
119
120360 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
121360 isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed
122360 IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB
123360 landvolfill = 1.0e-20 ! This is arbitrary, but must be positive.
124360 stencil = 2 ! The scheme's stencil; 2 for PLM
125
126360 ntr = Reg%ntr
127360 Idt = 1.0 / dt
128
129360 if (.not. associated(CS)) call MOM_error(FATAL, "MOM_tracer_advect: "// &
1300 "tracer_advect_init must be called before advect_tracer.")
131360 if (.not. associated(Reg)) call MOM_error(FATAL, "MOM_tracer_advect: "// &
1320 "register_tracer must be called before advect_tracer.")
133360 if (Reg%ntr==0) return
134
135 !$omp target enter data map(to: OBC, Reg, Reg%Tr(:)) map(alloc: domore_u, domore_v, uhr, vhr, uh_neglect, &
136 !$omp vh_neglect, hprev, local_advect_scheme)
137
1380 do concurrent (k=1:nz, j=jsd:jed)
1390 domore_u(j,k) = .false.
140 enddo
1410 do concurrent (k=1:nz, j=jsdB:jedB)
1420 domore_v(j,k) = .false.
143 enddo
144
1450 call cpu_clock_begin(id_clock_advect)
1460 x_first = (MOD(G%first_direction,2) == 0)
147
148 ! Choose the maximum stencil from all the local advection scheme
1490 do concurrent (m = 1:ntr)
150
1510 local_advect_scheme(m) = Reg%Tr(m)%advect_scheme
1520 if (local_advect_scheme(m) < 0) local_advect_scheme(m) = CS%default_advect_scheme
153
1540 if (local_advect_scheme(m) == ADVECT_PLM) then
1550 stencil_local = 2
1560 elseif (local_advect_scheme(m) == ADVECT_PPM) then
1570 stencil_local = 3
1580 elseif (local_advect_scheme(m) == ADVECT_PPMH3) then
1590 if (CS%useHuynhStencilBug) then
1600 stencil_local = 2
161 else
1620 stencil_local = 3
163 endif
164 endif
1650 stencil = max(stencil, stencil_local)
166 enddo
167
168 !$omp target update from(local_advect_scheme)
169
1700 if (min(is-isd,ied-ie,js-jsd,jed-je) < stencil) then
171 call MOM_error(FATAL, "MOM_tracer_advect: "//&
1720 "stencil is wider than the halo.")
173 endif
174
1750 max_iter = 2*INT(CEILING(dt/CS%dt)) + 1
176
1770 if (present(max_iter_in)) max_iter = max_iter_in
1780 if (present(x_first_in)) x_first = x_first_in
1790 call cpu_clock_begin(id_clock_pass)
1800 call create_group_pass(CS%pass_uhr_vhr_t_hprev, uhr, vhr, G%Domain)
1810 call create_group_pass(CS%pass_uhr_vhr_t_hprev, hprev, G%Domain)
1820 do m=1,ntr
1830 call create_group_pass(CS%pass_uhr_vhr_t_hprev, Reg%Tr(m)%t, G%Domain)
184 enddo
1850 call cpu_clock_end(id_clock_pass)
186
187 ! This initializes the halos of uhr and vhr because pass_vector might do
188 ! calculations on them, even though they are never used.
1890 do concurrent (k=1:nz)
1900 do concurrent (j=jsd:jed, I=IsdB:IedB)
1910 uhr(I,j,k) = 0.0
192 enddo
1930 do concurrent (J=jsdB:jedB, i=Isd:Ied)
1940 vhr(i,J,k) = 0.0
195 enddo
1960 do concurrent (j=jsd:jed, i=Isd:Ied)
1970 hprev(i,j,k) = 0.0
198 enddo
1990 domore_k(k)=1
200 ! Put the remaining (total) thickness fluxes into uhr and vhr.
2010 do concurrent (j=js:je, I=is-1:ie)
2020 uhr(I,j,k) = uhtr(I,j,k)
203 enddo
2040 do concurrent (J=js-1:je, i=is:ie)
2050 vhr(i,J,k) = vhtr(i,J,k)
206 enddo
2070 if (.not. present(vol_prev)) then
208 ! This loop reconstructs the thickness field the last time that the
209 ! tracers were updated, probably just after the diabatic forcing. A useful
210 ! diagnostic could be to compare this reconstruction with that older value.
2110 do concurrent (j=js:je, i=is:ie)
212 hprev(i,j,k) = max(0.0, G%areaT(i,j)*h_end(i,j,k) + &
2130 ((uhr(I,j,k) - uhr(I-1,j,k)) + (vhr(i,J,k) - vhr(i,J-1,k))))
214 ! In the case that the layer is now dramatically thinner than it was previously,
215 ! add a bit of mass to avoid truncation errors. This will lead to
216 ! non-conservation of tracers
217 hprev(i,j,k) = hprev(i,j,k) + &
2180 max(0.0, 1.0e-13*hprev(i,j,k) - G%areaT(i,j)*h_end(i,j,k))
219 enddo
220 else
2210 do concurrent (j=js:je, i=is:ie)
2220 hprev(i,j,k) = vol_prev(i,j,k)
223 enddo
224 endif
225 enddo
226
2270 do concurrent (j=jsd:jed, I=isd:ied-1)
2280 uh_neglect(I,j) = GV%H_subroundoff * MIN(G%areaT(i,j), G%areaT(i+1,j))
229 enddo
2300 do concurrent (J=jsd:jed-1, i=isd:ied)
2310 vh_neglect(i,J) = GV%H_subroundoff * MIN(G%areaT(i,j), G%areaT(i,j+1))
232 enddo
233
234 ! update GPU copy of Tr(:)%t
235 ! only update t because other members are zeroed
236 !$ do m=1,ntr
237 !$omp target enter data map(to: Reg%Tr(m)%t) &
238 !$omp map(alloc: Reg%Tr(m)%ad_x, Reg%Tr(m)%ad_y, Reg%Tr(m)%ad2d_x, Reg%Tr(m)%ad2d_y, &
239 !$omp Reg%Tr(m)%advection_xy)
240 !$ enddo
241
242 ! initialize diagnostic fluxes and tendencies
2430 do concurrent (m=1:ntr)
2440 if (associated(Reg%Tr(m)%ad_x)) then
2450 do concurrent (k=1:nz, j=jsd:jed, I=IsdB:IedB)
2460 Reg%Tr(m)%ad_x(i,j,k) = 0.0
247 enddo
248 endif
2490 if (associated(Reg%Tr(m)%ad_y)) then
2500 do concurrent (k=1:nz, j=JsdB:jedB, i=isd:ied)
2510 Reg%Tr(m)%ad_y(i,j,k) = 0.0
252 enddo
253 endif
2540 if (associated(Reg%Tr(m)%advection_xy)) then
2550 do concurrent (k=1:nz, j=jsd:jed, i=isd:ied)
2560 Reg%Tr(m)%advection_xy(i,j,k) = 0.0
257 enddo
258 endif
2590 if (associated(Reg%Tr(m)%ad2d_x)) then
2600 do concurrent (j=jsd:jed, I=IsdB:IedB)
2610 Reg%Tr(m)%ad2d_x(i,j) = 0.0
262 enddo
263 endif
2640 if (associated(Reg%Tr(m)%ad2d_y)) then
2650 do concurrent (J=JsdB:JedB, i=isd:ied)
2660 Reg%Tr(m)%ad2d_y(i,j) = 0.0
267 enddo
268 endif
269 enddo
270
2710 isv = is ; iev = ie ; jsv = js ; jev = je
2720 nsten_halo = min(is - isd, ied - ie, js - jsd, jed - je) / stencil
273
2740 do itt=1,max_iter
275
2760 if (isv > is-stencil) then
2770 call do_group_pass(CS%pass_uhr_vhr_t_hprev, G%Domain, clock=id_clock_pass, omp_offload=.true.)
278
2790 isv = is - nsten_halo * stencil ; jsv = js - nsten_halo * stencil
2800 iev = ie + nsten_halo * stencil ; jev = je + nsten_halo * stencil
281 ! Reevaluate domore_u & domore_v unless the valid range is the same size as
282 ! before. Also, do this if there is Strang splitting.
2830 if ((nsten_halo > 1) .or. (itt==1)) then
2840 do concurrent (k=1:nz, domore_k(k) > 0)
2850 do concurrent (j=jsv:jev, .not.domore_u(j,k))
2860 do i=isv+stencil-1,iev-stencil ; if (uhr(I,j,k) /= 0.0) then
2870 domore_u(j,k) = .true. ; exit
288 endif ; enddo ! i-loop
289 enddo
2900 do concurrent (J=jsv+stencil-1:jev-stencil, .not.domore_v(J,k))
2910 do i=isv+stencil,iev-stencil ; if (vhr(i,J,k) /= 0.0) then
2920 domore_v(J,k) = .true. ; exit
293 endif ; enddo ! i-loop
294 enddo
295
296 ! At this point, domore_k is global. Change it so that it indicates
297 ! whether any work is needed on a layer on this processor.
2980 domore_k_tmp = 0
2990 do concurrent (j=jsv:jev, domore_u(j,k)) DO_LOCALITY(reduce(max:domore_k_tmp))
3000 domore_k_tmp = 1
301 enddo
3020 do concurrent (J=jsv+stencil-1:jev-stencil, domore_v(J,k)) DO_LOCALITY(reduce(max:domore_k_tmp))
3030 domore_k_tmp = 1
304 enddo
3050 domore_k(k) = domore_k_tmp
306 enddo ! k-loop
307 endif
308 endif
309
310 ! Set the range of valid points after this iteration.
3110 isv = isv + stencil ; iev = iev - stencil
3120 jsv = jsv + stencil ; jev = jev - stencil
313
314 ! To ensure positive definiteness of the thickness at each iteration, the
315 ! mass fluxes out of each layer are checked each step, and limited to keep
316 ! the thicknesses positive. This means that several iterations may be required
317 ! for all the transport to happen. The sum over domore_k keeps the processors
318 ! synchronized. This may not be very efficient, but it should be reliable.
319
3200 if (x_first) then
321
3220 do k=1,nz ; if (domore_k(k) > 0) then
323 ! First, advect zonally.
324 call advect_x(Reg%Tr, hprev, uhr, uh_neglect, OBC, domore_u, ntr, Idt, &
325 isv, iev, jsv-stencil, jev+stencil, k, G, GV, US, &
3260 local_advect_scheme)
327 endif ; enddo
328
3290 do k=1,nz ; if (domore_k(k) > 0) then
330 ! Next, advect meridionally.
331 call advect_y(Reg%Tr, hprev, vhr, vh_neglect, OBC, domore_v, ntr, Idt, &
3320 isv, iev, jsv, jev, k, G, GV, US, local_advect_scheme)
333
334 ! Update domore_k(k) for the next iteration
3350 domore_k_tmp = 0
3360 do concurrent (j=jsv-stencil:jev+stencil, domore_u(j,k)) DO_LOCALITY(reduce(max:domore_k_tmp))
3370 domore_k_tmp = 1
338 enddo
3390 do concurrent (J=jsv-1:jev, domore_v(J,k)) DO_LOCALITY(reduce(max:domore_k_tmp))
3400 domore_k_tmp = 1
341 enddo
3420 domore_k(k) = domore_k_tmp
343 endif ; enddo
344 else
3450 do k=1,nz ; if (domore_k(k) > 0) then
346 ! First, advect meridionally.
347 call advect_y(Reg%Tr, hprev, vhr, vh_neglect, OBC, domore_v, ntr, Idt, &
348 isv-stencil, iev+stencil, jsv, jev, k, G, GV, US, &
3490 local_advect_scheme)
350 endif ; enddo
351
3520 do k=1,nz ; if (domore_k(k) > 0) then
353 ! Next, advect zonally.
354 call advect_x(Reg%Tr, hprev, uhr, uh_neglect, OBC, domore_u, ntr, Idt, &
3550 isv, iev, jsv, jev, k, G, GV, US, local_advect_scheme)
356
357 ! Update domore_k(k) for the next iteration
3580 domore_k_tmp = 0
3590 do concurrent (j=jsv:jev, domore_u(j,k)) DO_LOCALITY(reduce(max:domore_k_tmp))
3600 domore_k_tmp = 1
361 enddo
3620 do concurrent (J=jsv-1:jev, domore_v(J,k)) DO_LOCALITY(reduce(max:domore_k_tmp))
3630 domore_k_tmp = 1
364 enddo
3650 domore_k(k) = domore_k_tmp
366 endif ; enddo
367
368 endif ! x_first
369
370 ! If the advection just isn't finishing after max_iter, move on.
3710 if (itt >= max_iter) then
3720 exit
373 endif
374
375 ! Exit if there are no layers that need more iterations.
3760 if (isv > is-stencil) then
3770 do_any = 0
3780 call cpu_clock_begin(id_clock_sync)
3790 call sum_across_PEs(domore_k(:), nz)
3800 call cpu_clock_end(id_clock_sync)
3810 do k=1,nz ; do_any = do_any + domore_k(k) ; enddo
3820 if (do_any == 0) then
3830 exit
384 endif
385
386 endif
387
388 enddo ! Iterations loop
389
390 !$ do m = 1, ntr
391 !$omp target exit data map(from: Reg%Tr(m)%t, Reg%Tr(m)%ad_x, Reg%Tr(m)%ad_y, Reg%Tr(m)%ad2d_x, &
392 !$omp Reg%Tr(m)%ad2d_y, Reg%Tr(m)%advection_xy)
393 !$ enddo
394
3950 if (present(uhr_out)) then
3960 do concurrent (k=1:nz, j=jsd:jed, i=isdB:iedB)
3970 uhr_out(i,j,k) = uhr(i,j,k)
398 enddo
399 endif
4000 if (present(vhr_out)) then
4010 do concurrent (k=1:nz, j=jsdB:jedB, i=isd:ied)
4020 vhr_out(i,j,k) = vhr(i,j,k)
403 enddo
404 endif
4050 if (present(vol_prev) .and. present(update_vol_prev)) then
4060 if (update_vol_prev) then
4070 do concurrent (k=1:nz, j=jsd:jed, i=isd:ied)
4080 vol_prev(i,j,k) = hprev(i,j,k)
409 enddo
410 endif
411 endif
412
413 !$omp target exit data map(release: hprev, uhr, vhr, uh_neglect, vh_neglect, domore_u, &
414 !$omp domore_v, local_advect_scheme, OBC, Reg, Reg%Tr(:))
415
4160 call cpu_clock_end(id_clock_advect)
417
418360end subroutine advect_tracer
419
420
421!> This subroutine does 1-d flux-form advection in the zonal direction using
422!! a monotonic piecewise linear scheme.
4230subroutine advect_x(Tr, hprev, uhr, uh_neglect, OBC, domore_u, ntr, Idt, &
4240 is, ie, js, je, k, G, GV, US, advect_schemes)
425 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure
426 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
427 integer, intent(in) :: ntr !< The number of tracers
428 type(tracer_type), dimension(ntr), intent(inout) :: Tr !< The array of registered tracers to work on
429 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: hprev !< cell volume at the end of previous
430 !! tracer change [H L2 ~> m3 or kg]
431 real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(inout) :: uhr !< accumulated volume/mass flux through
432 !! the zonal face [H L2 ~> m3 or kg]
433 real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: uh_neglect !< A tiny zonal mass flux that can
434 !! be neglected [H L2 ~> m3 or kg]
435 type(ocean_OBC_type), pointer :: OBC !< specifies whether, where, and what OBCs are used
436 logical, dimension(SZJ_(G),SZK_(GV)), intent(inout) :: domore_u !< If true, there is more advection to be
437 !! done in this u-row
438 real, intent(in) :: Idt !< The inverse of dt [T-1 ~> s-1]
439 integer, intent(in) :: is !< The starting tracer i-index to work on
440 integer, intent(in) :: ie !< The ending tracer i-index to work on
441 integer, intent(in) :: js !< The starting tracer j-index to work on
442 integer, intent(in) :: je !< The ending tracer j-index to work on
443 integer, intent(in) :: k !< The k-level to work on
444 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
445 integer, dimension(ntr), intent(in) :: advect_schemes !< list of advection schemes to use
446
447 real, dimension(SZI_(G),ntr) :: &
4480 slope_x ! The concentration slope per grid point [conc].
449 real, dimension(SZIB_(G),SZJ_(G),ntr) :: &
4500 flux_x ! The tracer flux across a boundary [H L2 conc ~> m3 conc or kg conc].
451 real, dimension(SZI_(G),ntr) :: &
4520 T_tmp ! The copy of the tracer concentration at constant i,k [conc].
453
454 real :: hup, hlos ! hup is the upwind volume, hlos is the
455 ! part of that volume that might be lost
456 ! due to advection out the other side of
457 ! the grid box, both in [H L2 ~> m3 or kg].
4580 real :: uhh(SZIB_(G)) ! The zonal flux that occurs during the
459 ! current iteration [H L2 ~> m3 or kg].
460 real, dimension(SZIB_(G)) :: &
4610 hlst, & ! Work variable [H L2 ~> m3 or kg].
4620 Ihnew, & ! Work variable [H-1 L-2 ~> m-3 or kg-1].
4630 CFL ! The absolute value of the advective upwind-cell CFL number [nondim].
464 real :: min_h ! The minimum thickness that can be realized during
465 ! any of the passes [H ~> m or kg m-2].
466 real :: tiny_h ! The smallest numerically invertible thickness [H ~> m or kg m-2].
467 real :: h_neglect ! A thickness that is so small it is usually lost
468 ! in roundoff and can be neglected [H ~> m or kg m-2].
469 real :: aR, aL ! Reconstructed tracer concentrations at the right and left edges [conc]
470 real :: dMx ! Difference between the maximum of the surrounding cell concentrations and
471 ! the value in the cell whose reconstruction is being found [conc]
472 real :: dMn ! Difference between the tracer concentration in the cell whose reconstruction
473 ! is being found and the minimum of the surrounding values [conc]
474 real :: Tp, Tc, Tm ! Tracer concentrations around the upstream cell [conc]
475 real :: dA ! Difference between the reconstruction tracer edge values [conc]
476 real :: mA ! Average of the reconstruction tracer edge values [conc]
477 real :: a6 ! Curvature of the reconstruction tracer values [conc]
4780 logical :: do_i(SZI_(G),SZJ_(G)) ! If true, work on given points.
479 logical :: usePLMslope, domore_u_jk
480 integer :: i, j, m, n, i_up, stencil, ntr_id
481 type(OBC_segment_type), pointer :: segment=>NULL()
4820 logical, dimension(SZJ_(G),SZK_(GV)) :: domore_u_initial
483
484 ! keep a local copy of the initial values of domore_u, which is to be used when computing ad2d_x
485 ! diagnostic at the end of this subroutine.
4860 domore_u_initial = domore_u
487
4880 usePLMslope = .false.
489 ! stencil for calculating slope values
4900 stencil = 1
4910 do m = 1,ntr
4920 if ((advect_schemes(m) == ADVECT_PLM) .or. (advect_schemes(m) == ADVECT_PPM)) &
4930 usePLMslope = .true.
4940 if (advect_schemes(m) == ADVECT_PPM) stencil = 2
495 enddo
496
4970 min_h = 0.1*GV%Angstrom_H
4980 tiny_h = tiny(min_h)
4990 h_neglect = GV%H_subroundoff
500
501 ! do I=is-1,ie ; CFL(I) = 0.0 ; enddo
502
503 !$omp target enter data &
504 !$omp map(alloc: slope_x, T_tmp, uhh, CFL, hlst, Ihnew, do_i, flux_x)
505 !$omp target teams loop private(slope_x, T_tmp, uhh, CFL, hlst, Ihnew, Tp, dMx, dMn, m, i, n, &
506 !$omp ntr_id, hup, hlos, i_up, Tc, Tm, aL, aR, dA, mA, a6, domore_u_jk)
5070 do j=js,je ; if (domore_u(j,k)) then
5080 domore_u_jk = .false.
509
510 ! Calculate the i-direction profiles (slopes) of each tracer that is being advected.
5110 if (usePLMslope) then
5120 do concurrent (m=1:ntr, i=is-stencil:ie+stencil)
513 !if (ABS(Tr(m)%t(i+1,j,k)-Tr(m)%t(i,j,k)) < &
514 ! ABS(Tr(m)%t(i,j,k)-Tr(m)%t(i-1,j,k))) then
515 ! maxslope = 4.0*(Tr(m)%t(i+1,j,k)-Tr(m)%t(i,j,k))
516 !else
517 ! maxslope = 4.0*(Tr(m)%t(i,j,k)-Tr(m)%t(i-1,j,k))
518 !endif
519 !if ((Tr(m)%t(i+1,j,k)-Tr(m)%t(i,j,k)) * (Tr(m)%t(i,j,k)-Tr(m)%t(i-1,j,k)) < 0.0) then
520 ! slope_x(i,m) = 0.0
521 !elseif (ABS(Tr(m)%t(i+1,j,k)-Tr(m)%t(i-1,j,k))<ABS(maxslope)) then
522 ! slope_x(i,m) = G%mask2dCu(I,j)*G%mask2dCu(I-1,j) * &
523 ! 0.5*(Tr(m)%t(i+1,j,k)-Tr(m)%t(i-1,j,k))
524 !else
525 ! slope_x(i,m) = G%mask2dCu(I,j)*G%mask2dCu(I-1,j) * 0.5*maxslope
526 !endif
5270 Tp = Tr(m)%t(i+1,j,k) ; Tc = Tr(m)%t(i,j,k) ; Tm = Tr(m)%t(i-1,j,k)
5280 dMx = max( Tp, Tc, Tm ) - Tc
5290 dMn= Tc - min( Tp, Tc, Tm )
530 slope_x(i,m) = G%mask2dCu(I,j)*G%mask2dCu(I-1,j) * &
5310 sign( min(0.5*abs(Tp-Tm), 2.0*dMx, 2.0*dMn), Tp-Tm )
532 enddo
533 endif ! usePLMslope
534
535 ! make a copy of the tracers in case values need to be overridden for OBCs
5360 do concurrent (m = 1:ntr, i=G%isd:G%ied)
5370 T_tmp(i,m) = Tr(m)%t(i,j,k)
538 enddo
539 ! loop through open boundaries and recalculate flux terms
5400 if (associated(OBC)) then ; if (OBC%OBC_pe) then
5410 do n=1,OBC%number_of_segments
542 ! segment=>OBC%segment(n)
5430 if (.not. associated(OBC%segment(n)%tr_Reg)) cycle
5440 if (OBC%segment(n)%is_E_or_W) then
5450 if (j>=OBC%segment(n)%HI%jsd .and. j<=OBC%segment(n)%HI%jed) then
5460 I = OBC%segment(n)%HI%IsdB
5470 do concurrent (m = 1:OBC%segment(n)%tr_Reg%ntseg) ! replace tracers with OBC values
5480 ntr_id = OBC%segment(n)%tr_reg%Tr(m)%ntr_index
5490 if (allocated(OBC%segment(n)%tr_Reg%Tr(m)%tres)) then
5500 if (OBC%segment(n)%direction == OBC_DIRECTION_W) then
5510 T_tmp(i,ntr_id) = OBC%segment(n)%tr_Reg%Tr(m)%tres(i,j,k)
552 else
5530 T_tmp(i+1,ntr_id) = OBC%segment(n)%tr_Reg%Tr(m)%tres(i,j,k)
554 endif
555 else
5560 if (OBC%segment(n)%direction == OBC_DIRECTION_W) then
5570 T_tmp(i,ntr_id) = OBC%segment(n)%tr_Reg%Tr(m)%OBC_inflow_conc
558 else
5590 T_tmp(i+1,ntr_id) = OBC%segment(n)%tr_Reg%Tr(m)%OBC_inflow_conc
560 endif
561 endif
562 enddo
563 ! Apply update tracer values for slope calculation
5640 do concurrent (m = 1:ntr, i=OBC%segment(n)%HI%IsdB-1:OBC%segment(n)%HI%IsdB+1)
5650 Tp = T_tmp(i+1,m) ; Tc = T_tmp(i,m) ; Tm = T_tmp(i-1,m)
5660 dMx = max( Tp, Tc, Tm ) - Tc
5670 dMn= Tc - min( Tp, Tc, Tm )
568 slope_x(i,m) = G%mask2dCu(I,j)*G%mask2dCu(I-1,j) * &
5690 sign( min(0.5*abs(Tp-Tm), 2.0*dMx, 2.0*dMn), Tp-Tm )
570 enddo
571
572 endif
573 endif
574 enddo
575 endif ; endif
576
577
578 ! Calculate the i-direction fluxes of each tracer, using as much
579 ! the minimum of the remaining mass flux (uhr) and the half the mass
580 ! in the cell plus whatever part of its half of the mass flux that
581 ! the flux through the other side does not require.
5820 do concurrent (I=is-1:ie) DO_LOCALITY(reduce(.or.:domore_u_jk))
583 if ((uhr(I,j,k) == 0.0) .or. &
5840 ((uhr(I,j,k) < 0.0) .and. (hprev(i+1,j,k) <= tiny_h)) .or. &
5850 ((uhr(I,j,k) > 0.0) .and. (hprev(i,j,k) <= tiny_h)) ) then
5860 uhh(I) = 0.0
5870 CFL(I) = 0.0
5880 elseif (uhr(I,j,k) < 0.0) then
5890 hup = hprev(i+1,j,k) - G%areaT(i+1,j)*min_h
5900 hlos = MAX(0.0, uhr(I+1,j,k))
5910 if ((((hup - hlos) + uhr(I,j,k)) < 0.0) .and. &
592 ((0.5*hup + uhr(I,j,k)) < 0.0)) then
5930 uhh(I) = MIN(-0.5*hup, -hup+hlos, 0.0)
5940 domore_u_jk = .true.
595 else
5960 uhh(I) = uhr(I,j,k)
597 endif
5980 CFL(I) = - uhh(I) / (hprev(i+1,j,k)) ! CFL is positive
599 else
6000 hup = hprev(i,j,k) - G%areaT(i,j)*min_h
6010 hlos = MAX(0.0, -uhr(I-1,j,k))
6020 if ((((hup - hlos) - uhr(I,j,k)) < 0.0) .and. &
603 ((0.5*hup - uhr(I,j,k)) < 0.0)) then
6040 uhh(I) = MAX(0.5*hup, hup-hlos, 0.0)
6050 domore_u_jk = .true.
606 else
6070 uhh(I) = uhr(I,j,k)
608 endif
6090 CFL(I) = uhh(I) / (hprev(i,j,k)) ! CFL is positive
610 endif
611 enddo
612
6130 domore_u(j,k) = domore_u_jk
614
6150 do concurrent (m=1:ntr)
616
6170 if ((advect_schemes(m) == ADVECT_PPM) .or. (advect_schemes(m) == ADVECT_PPMH3)) then
6180 do concurrent (I=is-1:ie)
619 ! centre cell depending on upstream direction
6200 if (uhh(I) >= 0.0) then
6210 i_up = i
622 else
6230 i_up = i+1
624 endif
625
626 ! Implementation of PPM-H3
6270 Tp = T_tmp(i_up+1,m) ; Tc = T_tmp(i_up,m) ; Tm = T_tmp(i_up-1,m)
628
6290 if (advect_schemes(m) == ADVECT_PPMH3) then
6300 aL = ( 5.*Tc + ( 2.*Tm - Tp ) )/6. ! H3 estimate
6310 aL = max( min(Tc,Tm), aL) ; aL = min( max(Tc,Tm), aL) ! Bound
6320 aR = ( 5.*Tc + ( 2.*Tp - Tm ) )/6. ! H3 estimate
6330 aR = max( min(Tc,Tp), aR) ; aR = min( max(Tc,Tp), aR) ! Bound
634 else
6350 aL = 0.5 * ((Tm + Tc) + (slope_x(i_up-1,m) - slope_x(i_up,m)) / 3.)
6360 aR = 0.5 * ((Tc + Tp) + (slope_x(i_up,m) - slope_x(i_up+1,m)) / 3.)
637 endif
638
6390 dA = aR - aL ; mA = 0.5*( aR + aL )
6400 if (G%mask2dCu(I_up,j)*G%mask2dCu(I_up-1,j)*(Tp-Tc)*(Tc-Tm) <= 0.) then
6410 aL = Tc ; aR = Tc ! PCM for local extrema and boundary cells
6420 elseif ( dA*(Tc-mA) > (dA*dA)/6. ) then
6430 aL = (3.*Tc) - 2.*aR
6440 elseif ( dA*(Tc-mA) < - (dA*dA)/6. ) then
6450 aR = (3.*Tc) - 2.*aL
646 endif
647
6480 a6 = 6.*Tc - 3. * (aR + aL) ! Curvature
649
6500 if (uhh(I) >= 0.0) then
651 flux_x(I,j,m) = uhh(I)*( aR - 0.5 * CFL(I) * ( &
6520 ( aR - aL ) - a6 * ( 1. - 2./3. * CFL(I) ) ) )
653 else
654 flux_x(I,j,m) = uhh(I)*( aL + 0.5 * CFL(I) * ( &
6550 ( aR - aL ) + a6 * ( 1. - 2./3. * CFL(I) ) ) )
656 endif
657 enddo
658 else ! PLM
6590 do concurrent (I=is-1:ie)
6600 if (uhh(I) >= 0.0) then
661 ! Indirect implementation of PLM
662 !aL = Tr(m)%t(i,j,k) - 0.5 * slope_x(i,m)
663 !aR = Tr(m)%t(i,j,k) + 0.5 * slope_x(i,m)
664 !flux_x(I,j,m) = uhh(I)*( aR - 0.5 * (aR-aL) * CFL(I) )
665 ! Alternative implementation of PLM
6660 Tc = T_tmp(i,m)
6670 flux_x(I,j,m) = uhh(I)*( Tc + 0.5 * slope_x(i,m) * ( 1. - CFL(I) ) )
668 else
669 ! Indirect implementation of PLM
670 !aL = Tr(m)%t(i+1,j,k) - 0.5 * slope_x(i+1,m)
671 !aR = Tr(m)%t(i+1,j,k) + 0.5 * slope_x(i+1,m)
672 !flux_x(I,j,m) = uhh(I)*( aL + 0.5 * (aR-aL) * CFL(I) )
673 ! Alternative implementation of PLM
6740 Tc = T_tmp(i+1,m)
6750 flux_x(I,j,m) = uhh(I)*( Tc - 0.5 * slope_x(i+1,m) * ( 1. - CFL(I) ) )
676 endif
677 enddo
678 endif ! usePPM
679 enddo
680
6810 if (associated(OBC)) then ; if (OBC%OBC_pe) then
6820 if (OBC%specified_u_BCs_exist_globally .or. OBC%open_u_BCs_exist_globally) then
6830 do n=1,OBC%number_of_segments
684 ! segment=>OBC%segment(n)
6850 if (.not. associated(OBC%segment(n)%tr_Reg)) cycle
6860 if (OBC%segment(n)%is_E_or_W) then
6870 if (j>=OBC%segment(n)%HI%jsd .and. j<=OBC%segment(n)%HI%jed) then
6880 I = OBC%segment(n)%HI%IsdB
689 ! Tracer fluxes are set to prescribed values only for inflows from masked areas.
690 ! Now changing to simply fixed inflows.
6910 if ((uhr(I,j,k) > 0.0) .and. (OBC%segment(n)%direction == OBC_DIRECTION_W) .or. &
692 (uhr(I,j,k) < 0.0) .and. (OBC%segment(n)%direction == OBC_DIRECTION_E)) then
6930 uhh(I) = uhr(I,j,k)
694 ! should the reservoir evolve for this case Kate ?? - Nope
6950 do concurrent (m=1:OBC%segment(n)%tr_Reg%ntseg)
6960 ntr_id = OBC%segment(n)%tr_reg%Tr(m)%ntr_index
6970 if (allocated(OBC%segment(n)%tr_Reg%Tr(m)%tres)) then
6980 flux_x(I,j,ntr_id) = uhh(I)*OBC%segment(n)%tr_Reg%Tr(m)%tres(I,j,k)
6990 else ; flux_x(I,j,ntr_id) = uhh(I)*OBC%segment(n)%tr_Reg%Tr(m)%OBC_inflow_conc ; endif
700 enddo
701 endif
702 endif
703 endif
704 enddo
705 endif
706
7070 if (OBC%open_u_BCs_exist_globally) then
7080 do n=1,OBC%number_of_segments
709 ! segment=>OBC%segment(n)
7100 I = OBC%segment(n)%HI%IsdB
7110 if (OBC%segment(n)%is_E_or_W .and. (j >= OBC%segment(n)%HI%jsd .and. j<= OBC%segment(n)%HI%jed)) then
7120 if (OBC%segment(n)%specified) cycle
7130 if (.not. associated(OBC%segment(n)%tr_Reg)) cycle
714
715 ! Tracer fluxes are set to prescribed values only for inflows from masked areas.
7160 if ((uhr(I,j,k) > 0.0) .and. (G%mask2dT(i,j) < 0.5) .or. &
717 (uhr(I,j,k) < 0.0) .and. (G%mask2dT(i+1,j) < 0.5)) then
7180 uhh(I) = uhr(I,j,k)
7190 do concurrent (m=1:OBC%segment(n)%tr_Reg%ntseg)
7200 ntr_id = OBC%segment(n)%tr_reg%Tr(m)%ntr_index
7210 if (allocated(OBC%segment(n)%tr_Reg%Tr(m)%tres)) then
7220 flux_x(I,j,ntr_id) = uhh(I)*OBC%segment(n)%tr_Reg%Tr(m)%tres(I,j,k)
7230 else; flux_x(I,j,ntr_id) = uhh(I)*OBC%segment(n)%tr_Reg%Tr(m)%OBC_inflow_conc; endif
724 enddo
725 endif
726 endif
727 enddo
728 endif
729 endif ; endif
730
731 ! Calculate new tracer concentration in each cell after accounting
732 ! for the i-direction fluxes.
7330 do concurrent (I=is-1:ie)
7340 uhr(I,j,k) = uhr(I,j,k) - uhh(I)
7350 if (abs(uhr(I,j,k)) < uh_neglect(I,j)) uhr(I,j,k) = 0.0
736 enddo
7370 do concurrent (i=is:ie)
7380 if ((uhh(I) /= 0.0) .or. (uhh(I-1) /= 0.0)) then
7390 do_i(i,j) = .true.
7400 hlst(i) = hprev(i,j,k)
7410 hprev(i,j,k) = hprev(i,j,k) - (uhh(I) - uhh(I-1))
7420 if (hprev(i,j,k) <= 0.0) then ; do_i(i,j) = .false.
7430 elseif (hprev(i,j,k) < h_neglect*G%areaT(i,j)) then
7440 hlst(i) = hlst(i) + (h_neglect*G%areaT(i,j) - hprev(i,j,k))
7450 Ihnew(i) = 1.0 / (h_neglect*G%areaT(i,j))
7460 else ; Ihnew(i) = 1.0 / hprev(i,j,k) ; endif
747 else
7480 do_i(i,j) = .false.
749 endif
750 enddo
751
752 ! Update do_i so that nothing changes outside of the OBC (problem for interior OBCs only)
7530 if (associated(OBC)) then
754 if (.not.OBC%exterior_OBC_bug .and. OBC%OBC_pe &
7550 .and. (OBC%specified_u_BCs_exist_globally .or. OBC%open_u_BCs_exist_globally)) then
756 ! OBC_DIRECTION_E / OBC_DIRECTION_W on the west / east edge
7570 do concurrent (i=is:ie, OBC%segnum_u(I-1,j) > 0 .or. OBC%segnum_u(I,j) < 0)
7580 do_i(i,j) = .false.
759 enddo
760 endif
761 endif
762
763 ! update tracer concentration from i-flux and save some diagnostics
7640 do concurrent (m=1:ntr)
765
766 ! update tracer
7670 do concurrent (i=is:ie)
7680 if (do_i(i,j)) then
7690 if (Ihnew(i) > 0.0) then
770 Tr(m)%t(i,j,k) = (Tr(m)%t(i,j,k) * hlst(i) - &
7710 (flux_x(I,j,m) - flux_x(I-1,j,m))) * Ihnew(i)
772 endif
773 endif
774 enddo
775
776 ! diagnostics
7770 if (associated(Tr(m)%ad_x)) then
7780 do concurrent (I=is-1:ie, do_i(i,j) .or. do_i(i+1,j))
7790 Tr(m)%ad_x(I,j,k) = Tr(m)%ad_x(I,j,k) + flux_x(I,j,m)*Idt
780 enddo
781 endif
782
783 ! diagnose convergence of flux_x (do not use the Ihnew(i) part of the logic).
784 ! division by areaT to get into W/m2 for heat and kg/(s*m2) for salt.
7850 if (associated(Tr(m)%advection_xy)) then
7860 do concurrent (i=is:ie, do_i(i,j))
787 Tr(m)%advection_xy(i,j,k) = Tr(m)%advection_xy(i,j,k) - &
788 (flux_x(I,j,m) - flux_x(I-1,j,m)) * &
7890 Idt * G%IareaT(i,j)
790 enddo
791 endif
792
793 enddo
794
795 endif ; enddo ! End of j-loop.
796
797 ! Do user controlled underflow of the tracer concentrations.
7980 do concurrent (m=1:ntr, Tr(m)%conc_underflow > 0.0)
7990 do concurrent (j=js:je, i=is:ie)
8000 if (abs(Tr(m)%t(i,j,k)) < Tr(m)%conc_underflow) Tr(m)%t(i,j,k) = 0.0
801 enddo
802 enddo
803
804 ! compute ad2d_x diagnostic outside above j-loop so as to make the summation ordered when OMP is active.
805
8060 do m=1,ntr ; if (associated(Tr(m)%ad2d_x)) then
8070 do concurrent (j=js:je, domore_u_initial(j,k))
8080 do concurrent (I=is-1:ie, do_i(i,j) .or. do_i(i+1,j))
8090 Tr(m)%ad2d_x(I,j) = Tr(m)%ad2d_x(I,j) + flux_x(I,j,m)*Idt
810 enddo
811 enddo
812 endif ; enddo ! End of m-loop.
813
814 !$omp target exit data &
815 !$omp map(release: slope_x, T_tmp, uhh, CFL, hlst, Ihnew, do_i, flux_x)
8160end subroutine advect_x
817
818!> This subroutine does 1-d flux-form advection using a monotonic piecewise
819!! linear scheme.
8200subroutine advect_y(Tr, hprev, vhr, vh_neglect, OBC, domore_v, ntr, Idt, &
8210 is, ie, js, je, k, G, GV, US, advect_schemes)
822 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure
823 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
824 integer, intent(in) :: ntr !< The number of tracers
825 type(tracer_type), dimension(ntr), intent(inout) :: Tr !< The array of registered tracers to work on
826 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: hprev !< cell volume at the end of previous
827 !! tracer change [H L2 ~> m3 or kg]
828 real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(inout) :: vhr !< accumulated volume/mass flux through
829 !! the meridional face [H L2 ~> m3 or kg]
830 real, dimension(SZI_(G),SZJB_(G)), intent(inout) :: vh_neglect !< A tiny meridional mass flux that can
831 !! be neglected [H L2 ~> m3 or kg]
832 type(ocean_OBC_type), pointer :: OBC !< specifies whether, where, and what OBCs are used
833 logical, dimension(SZJB_(G),SZK_(GV)), intent(inout) :: domore_v !< If true, there is more advection to be
834 !! done in this v-row
835 real, intent(in) :: Idt !< The inverse of dt [T-1 ~> s-1]
836 integer, intent(in) :: is !< The starting tracer i-index to work on
837 integer, intent(in) :: ie !< The ending tracer i-index to work on
838 integer, intent(in) :: js !< The starting tracer j-index to work on
839 integer, intent(in) :: je !< The ending tracer j-index to work on
840 integer, intent(in) :: k !< The k-level to work on
841 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
842 integer, dimension(ntr), intent(in) :: advect_schemes !< list of advection schemes to use
843
844 real, dimension(SZI_(G),ntr,SZJ_(G)) :: &
8450 slope_y ! The concentration slope per grid point [conc].
846 real, dimension(SZI_(G),ntr,SZJB_(G)) :: &
8470 flux_y ! The tracer flux across a boundary [H L2 conc ~> m3 conc or kg conc].
848 real, dimension(SZI_(G),ntr,SZJB_(G)) :: &
8490 T_tmp ! The copy of the tracer concentration at constant i,k [conc].
8500 real :: vhh(SZI_(G),SZJB_(G)) ! The meridional flux that occurs during the
851 ! current iteration [H L2 ~> m3 or kg].
852 real :: hup, hlos ! hup is the upwind volume, hlos is the
853 ! part of that volume that might be lost
854 ! due to advection out the other side of
855 ! the grid box, both in [H L2 ~> m3 or kg].
856 real, dimension(SZIB_(G)) :: &
8570 hlst, & ! Work variable [H L2 ~> m3 or kg].
8580 Ihnew, & ! Work variable [H-1 L-2 ~> m-3 or kg-1].
8590 CFL ! The absolute value of the advective upwind-cell CFL number [nondim].
860 real :: min_h ! The minimum thickness that can be realized during
861 ! any of the passes [H ~> m or kg m-2].
862 real :: tiny_h ! The smallest numerically invertible thickness [H ~> m or kg m-2].
863 real :: h_neglect ! A thickness that is so small it is usually lost
864 ! in roundoff and can be neglected [H ~> m or kg m-2].
865 real :: aR, aL ! Reconstructed tracer concentrations at the right and left edges [conc]
866 real :: dMx ! Difference between the maximum of the surrounding cell concentrations and
867 ! the value in the cell whose reconstruction is being found [conc]
868 real :: dMn ! Difference between the tracer average in the cell whose reconstruction
869 ! is being found and the minimum of the surrounding values [conc]
870 real :: Tp, Tc, Tm ! Tracer concentrations around the upstream cell [conc]
871 real :: dA ! Difference between the reconstruction tracer edge values [conc]
872 real :: mA ! Average of the reconstruction tracer edge values [conc]
873 real :: a6 ! Curvature of the reconstruction tracer values [conc]
8740 logical :: do_j_tr(SZJ_(G)) ! If true, calculate the tracer profiles.
8750 logical :: do_i(SZI_(G), SZJ_(G)) ! If true, work on given points.
876 logical :: usePLMslope
877 integer :: i, j, j2, m, n, j_up, stencil, ntr_id
878 type(OBC_segment_type), pointer :: segment=>NULL()
8790 logical :: domore_v_initial(SZJB_(G)), domore_v_jk ! Initial state of domore_v
880
8810 usePLMslope = .false.
882 ! stencil for calculating slope values
8830 stencil = 1
8840 do m = 1,ntr
8850 if ((advect_schemes(m) == ADVECT_PLM) .or. (advect_schemes(m) == ADVECT_PPM)) &
8860 usePLMslope = .true.
8870 if (advect_schemes(m) == ADVECT_PPM) stencil = 2
888 enddo
889
8900 min_h = 0.1*GV%Angstrom_H
8910 tiny_h = tiny(min_h)
8920 h_neglect = GV%H_subroundoff
893
894 !$omp target enter data map(alloc: vhh, T_tmp, slope_y, flux_y, domore_v_initial, do_j_tr, do_i)
895
896 ! We conditionally perform work on tracer points: calculating the PLM slope,
897 ! and updating tracer concentration within a cell
898 ! this depends on whether there is a flux which would affect this tracer point,
899 ! as indicated by domore_v. In the case of PPM reconstruction, a flux requires
900 ! slope calculations at the two tracer points on either side (as indicated by
901 ! the stencil variable), so we account for this with the do_j_tr flag array
902 !
903 ! Note: this does lead to unnecessary work in updating tracer concentrations,
904 ! since that doesn't need a wider stencil with the PPM advection scheme, but
905 ! this would require an additional loop, etc.
9060 do concurrent (j=SZJ_(G))
9070 do_j_tr(j) = .false.
908 enddo
9090 do concurrent (J=js-1:je, domore_v(J,k))
9100 do concurrent (j2=1-stencil:stencil)
9110 do_j_tr(j+j2) = .true.
912 enddo
913 enddo
9140 do concurrent (j=SZJB_(G))
9150 domore_v_initial(j) = domore_v(j,k)
916 enddo
917
918 ! Calculate the j-direction profiles (slopes) of each tracer that
919 ! is being advected.
9200 if (usePLMslope) then
9210 do concurrent (j=js-stencil:je+stencil, do_j_tr(j))
9220 do concurrent (m=1:ntr, i=is:ie)
923 !if (ABS(Tr(m)%t(i,j+1,k)-Tr(m)%t(i,j,k)) < &
924 ! ABS(Tr(m)%t(i,j,k)-Tr(m)%t(i,j-1,k))) then
925 ! maxslope = 4.0*(Tr(m)%t(i,j+1,k)-Tr(m)%t(i,j,k))
926 !else
927 ! maxslope = 4.0*(Tr(m)%t(i,j,k)-Tr(m)%t(i,j-1,k))
928 !endif
929 !if ((Tr(m)%t(i,j+1,k)-Tr(m)%t(i,j,k))*(Tr(m)%t(i,j,k)-Tr(m)%t(i,j-1,k)) < 0.0) then
930 ! slope_y(i,m,j) = 0.0
931 !elseif (ABS(Tr(m)%t(i,j+1,k)-Tr(m)%t(i,j-1,k))<ABS(maxslope)) then
932 ! slope_y(i,m,j) = G%mask2dCv(i,J) * G%mask2dCv(i,J-1) * &
933 ! 0.5*(Tr(m)%t(i,j+1,k)-Tr(m)%t(i,j-1,k))
934 !else
935 ! slope_y(i,m,j) = G%mask2dCv(i,J) * G%mask2dCv(i,J-1) * 0.5*maxslope
936 !endif
9370 Tp = Tr(m)%t(i,j+1,k) ; Tc = Tr(m)%t(i,j,k) ; Tm = Tr(m)%t(i,j-1,k)
9380 dMx = max( Tp, Tc, Tm ) - Tc
9390 dMn = Tc - min( Tp, Tc, Tm )
940 slope_y(i,m,j) = G%mask2dCv(i,J)*G%mask2dCv(i,J-1) * &
9410 sign( min(0.5*abs(Tp-Tm), 2.0*dMx, 2.0*dMn), Tp-Tm )
942 enddo
943 enddo ! End of i-, m-, & j- loops.
944 endif ! usePLMslope
945
946
947 ! make a copy of the tracers in case values need to be overridden for OBCs
948
9490 do concurrent (j=G%jsd:G%jed, m=1:ntr, i=G%isd:G%ied)
9500 T_tmp(i,m,j) = Tr(m)%t(i,j,k)
951 enddo
952
953 ! loop through open boundaries and recalculate flux terms
9540 if (associated(OBC)) then ; if (OBC%OBC_pe) then
9550 do n=1,OBC%number_of_segments
9560 segment=>OBC%segment(n)
9570 if (.not. associated(segment%tr_Reg)) cycle
9580 do i=is,ie
9590 if (segment%is_N_or_S) then
9600 if (i>=segment%HI%isd .and. i<=segment%HI%ied) then
9610 J = segment%HI%JsdB
9620 do m = 1,segment%tr_Reg%ntseg ! replace tracers with OBC values
9630 ntr_id = segment%tr_reg%Tr(m)%ntr_index
9640 if (allocated(segment%tr_Reg%Tr(m)%tres)) then
9650 if (segment%direction == OBC_DIRECTION_S) then
9660 T_tmp(i,ntr_id,j) = segment%tr_Reg%Tr(m)%tres(i,j,k)
967 else
9680 T_tmp(i,ntr_id,j+1) = segment%tr_Reg%Tr(m)%tres(i,j,k)
969 endif
970 else
9710 if (segment%direction == OBC_DIRECTION_S) then
9720 T_tmp(i,ntr_id,j) = segment%tr_Reg%Tr(m)%OBC_inflow_conc
973 else
9740 T_tmp(i,ntr_id,j+1) = segment%tr_Reg%Tr(m)%OBC_inflow_conc
975 endif
976 endif
977 enddo
9780 do m = 1,ntr ! Apply update tracer values for slope calculation
9790 do j=segment%HI%JsdB-1,segment%HI%JsdB+1
9800 Tp = T_tmp(i,m,j+1) ; Tc = T_tmp(i,m,j) ; Tm = T_tmp(i,m,j-1)
9810 dMx = max( Tp, Tc, Tm ) - Tc
9820 dMn= Tc - min( Tp, Tc, Tm )
983 slope_y(i,m,j) = G%mask2dCv(i,J)*G%mask2dCv(i,J-1) * &
9840 sign( min(0.5*abs(Tp-Tm), 2.0*dMx, 2.0*dMn), Tp-Tm )
985 enddo
986 enddo
987 endif
988 endif ! is_N_S
989 enddo ! i-loop
990 enddo ! segment loop
991 endif ; endif
992
993 ! Calculate the j-direction fluxes of each tracer, using as much
994 ! the minimum of the remaining mass flux (vhr) and the half the mass
995 ! in the cell plus whatever part of its half of the mass flux that
996 ! the flux through the other side does not require.
997 !$omp target teams loop &
998 !$omp private(CFL, Ihnew, hup, hlos, j_up, Tp, Tc, Tm, i, m, aL, aR, dA, mA, a6, n, ntr_id, &
999 !$omp domore_v_jk)
10000 do J=js-1,je ; if (domore_v(J,k)) then
10010 domore_v_jk = .false.
1002
10030 do concurrent (i=is:ie) DO_LOCALITY(reduce(.or.:domore_v_jk))
1004 if ((vhr(i,J,k) == 0.0) .or. &
10050 ((vhr(i,J,k) < 0.0) .and. (hprev(i,j+1,k) <= tiny_h)) .or. &
10060 ((vhr(i,J,k) > 0.0) .and. (hprev(i,j,k) <= tiny_h)) ) then
10070 vhh(i,J) = 0.0
10080 CFL(i) = 0.0
10090 elseif (vhr(i,J,k) < 0.0) then
10100 hup = hprev(i,j+1,k) - G%areaT(i,j+1)*min_h
10110 hlos = MAX(0.0, vhr(i,J+1,k))
10120 if ((((hup - hlos) + vhr(i,J,k)) < 0.0) .and. &
1013 ((0.5*hup + vhr(i,J,k)) < 0.0)) then
10140 vhh(i,J) = MIN(-0.5*hup, -hup+hlos, 0.0)
10150 domore_v_jk = .true.
1016 else
10170 vhh(i,J) = vhr(i,J,k)
1018 endif
10190 CFL(i) = - vhh(i,J) / hprev(i,j+1,k) ! CFL is positive
1020 else
10210 hup = hprev(i,j,k) - G%areaT(i,j)*min_h
10220 hlos = MAX(0.0, -vhr(i,J-1,k))
10230 if ((((hup - hlos) - vhr(i,J,k)) < 0.0) .and. &
1024 ((0.5*hup - vhr(i,J,k)) < 0.0)) then
10250 vhh(i,J) = MAX(0.5*hup, hup-hlos, 0.0)
10260 domore_v_jk = .true.
1027 else
10280 vhh(i,J) = vhr(i,J,k)
1029 endif
10300 CFL(i) = vhh(i,J) / hprev(i,j,k) ! CFL is positive
1031 endif
1032 enddo
1033
10340 domore_v(j,k) = domore_v_jk
1035
10360 do concurrent (m=1:ntr)
1037
10380 if ((advect_schemes(m) == ADVECT_PPM) .or. (advect_schemes(m) == ADVECT_PPMH3)) then
10390 do concurrent (i=is:ie)
1040 ! centre cell depending on upstream direction
10410 if (vhh(i,J) >= 0.0) then
10420 j_up = j
1043 else
10440 j_up = j + 1
1045 endif
1046
1047 ! Implementation of PPM-H3
10480 Tp = T_tmp(i,m,j_up+1) ; Tc = T_tmp(i,m,j_up) ; Tm = T_tmp(i,m,j_up-1)
1049
10500 if (advect_schemes(m) == ADVECT_PPMH3) then
10510 aL = ( 5.*Tc + ( 2.*Tm - Tp ) )/6. ! H3 estimate
10520 aL = max( min(Tc,Tm), aL) ; aL = min( max(Tc,Tm), aL) ! Bound
10530 aR = ( 5.*Tc + ( 2.*Tp - Tm ) )/6. ! H3 estimate
10540 aR = max( min(Tc,Tp), aR) ; aR = min( max(Tc,Tp), aR) ! Bound
1055 else
10560 aL = 0.5 * ((Tm + Tc) + (slope_y(i,m,j_up-1) - slope_y(i,m,j_up)) / 3.)
10570 aR = 0.5 * ((Tc + Tp) + (slope_y(i,m,j_up) - slope_y(i,m,j_up+1)) / 3.)
1058 endif
1059
10600 dA = aR - aL ; mA = 0.5*( aR + aL )
10610 if (G%mask2dCv(i,J_up)*G%mask2dCv(i,J_up-1)*(Tp-Tc)*(Tc-Tm) <= 0.) then
10620 aL = Tc ; aR = Tc ! PCM for local extrema and boundary cells
10630 elseif ( dA*(Tc-mA) > (dA*dA)/6. ) then
10640 aL = (3.*Tc) - 2.*aR
10650 elseif ( dA*(Tc-mA) < - (dA*dA)/6. ) then
10660 aR = (3.*Tc) - 2.*aL
1067 endif
1068
10690 a6 = 6.*Tc - 3. * (aR + aL) ! Curvature
1070
10710 if (vhh(i,J) >= 0.0) then
1072 flux_y(i,m,J) = vhh(i,J)*( aR - 0.5 * CFL(i) * ( &
10730 ( aR - aL ) - a6 * ( 1. - 2./3. * CFL(I) ) ) )
1074 else
1075 flux_y(i,m,J) = vhh(i,J)*( aL + 0.5 * CFL(i) * ( &
10760 ( aR - aL ) + a6 * ( 1. - 2./3. * CFL(I) ) ) )
1077 endif
1078 enddo
1079 else ! PLM
10800 do concurrent (i=is:ie)
10810 if (vhh(i,J) >= 0.0) then
1082 ! Indirect implementation of PLM
1083 !aL = Tr(m)%t(i,j,k) - 0.5 * slope_y(i,m,j)
1084 !aR = Tr(m)%t(i,j,k) + 0.5 * slope_y(i,m,j)
1085 !flux_y(i,m,J) = vhh(i,J)*( aR - 0.5 * (aR-aL) * CFL(i) )
1086 ! Alternative implementation of PLM
10870 Tc = T_tmp(i,m,j)
10880 flux_y(i,m,J) = vhh(i,J)*( Tc + 0.5 * slope_y(i,m,j) * ( 1. - CFL(i) ) )
1089 else
1090 ! Indirect implementation of PLM
1091 !aL = Tr(m)%t(i,j+1,k) - 0.5 * slope_y(i,m,j+1)
1092 !aR = Tr(m)%t(i,j+1,k) + 0.5 * slope_y(i,m,j+1)
1093 !flux_y(i,m,J) = vhh(i,J)*( aL + 0.5 * (aR-aL) * CFL(i) )
1094 ! Alternative implementation of PLM
10950 Tc = T_tmp(i,m,j+1)
10960 flux_y(i,m,J) = vhh(i,J)*( Tc - 0.5 * slope_y(i,m,j+1) * ( 1. - CFL(i) ) )
1097 endif
1098 enddo
1099 endif ! usePPM
1100 enddo
1101
11020 if (associated(OBC)) then ; if (OBC%OBC_pe) then
11030 if (OBC%specified_v_BCs_exist_globally .or. OBC%open_v_BCs_exist_globally) then
11040 do n=1,OBC%number_of_segments
11050 if (.not. OBC%segment(n)%specified) cycle
11060 if (.not. associated(OBC%segment(n)%tr_Reg)) cycle
11070 if (OBC%segment(n)%is_N_or_S) then
11080 if (J >= OBC%segment(n)%HI%JsdB .and. J<= OBC%segment(n)%HI%JedB) then
11090 do i=OBC%segment(n)%HI%isd,OBC%segment(n)%HI%ied
1110 ! Tracer fluxes are set to prescribed values only for inflows from masked areas.
1111 ! Now changing to simply fixed inflows.
11120 if ((vhr(i,J,k) > 0.0) .and. (OBC%segment(n)%direction == OBC_DIRECTION_S) .or. &
11130 (vhr(i,J,k) < 0.0) .and. (OBC%segment(n)%direction == OBC_DIRECTION_N)) then
11140 vhh(i,J) = vhr(i,J,k)
11150 do m=1,OBC%segment(n)%tr_Reg%ntseg
11160 ntr_id = OBC%segment(n)%tr_reg%Tr(m)%ntr_index
11170 if (allocated(OBC%segment(n)%tr_Reg%Tr(m)%tres)) then
11180 flux_y(i,ntr_id,J) = vhh(i,J)*OBC%segment(n)%tr_Reg%Tr(m)%tres(i,J,k)
1119 else
11200 flux_y(i,ntr_id,J) = vhh(i,J)*OBC%segment(n)%tr_Reg%Tr(m)%OBC_inflow_conc
1121 endif
1122 enddo
1123 endif
1124 enddo
1125 endif
1126 endif
1127 enddo
1128 endif
1129
11300 if (OBC%open_v_BCs_exist_globally) then
11310 do n=1,OBC%number_of_segments
11320 if (OBC%segment(n)%specified) cycle
11330 if (.not. associated(OBC%segment(n)%tr_Reg)) cycle
11340 if (OBC%segment(n)%is_N_or_S .and. (J >= OBC%segment(n)%HI%JsdB .and. J<= OBC%segment(n)%HI%JedB)) then
11350 do i=OBC%segment(n)%HI%isd,OBC%segment(n)%HI%ied
1136 ! Tracer fluxes are set to prescribed values only for inflows from masked areas.
11370 if ((vhr(i,J,k) > 0.0) .and. (G%mask2dT(i,j) < 0.5) .or. &
11380 (vhr(i,J,k) < 0.0) .and. (G%mask2dT(i,j+1) < 0.5)) then
11390 vhh(i,J) = vhr(i,J,k)
11400 do m=1,OBC%segment(n)%tr_Reg%ntseg
11410 ntr_id = OBC%segment(n)%tr_reg%Tr(m)%ntr_index
11420 if (allocated(OBC%segment(n)%tr_Reg%Tr(m)%tres)) then
11430 flux_y(i,ntr_id,J) = vhh(i,J)*OBC%segment(n)%tr_Reg%Tr(m)%tres(i,J,k)
11440 else ; flux_y(i,ntr_id,J) = vhh(i,J)*OBC%segment(n)%tr_Reg%Tr(m)%OBC_inflow_conc ; endif
1145 enddo
1146 endif
1147 enddo
1148 endif
1149 enddo
1150 endif
1151 endif ; endif
1152
1153 else ! not domore_v.
11540 do concurrent (i=is:ie)
11550 vhh(i,J) = 0.0
1156 enddo
11570 do concurrent (m=1:ntr, i=is:ie)
11580 flux_y(i,m,J) = 0.0
1159 enddo
1160 endif ; enddo ! End of j-loop
1161
11620 do concurrent (J=js-1:je, i=is:ie)
11630 vhr(i,J,k) = vhr(i,J,k) - vhh(i,J)
11640 if (abs(vhr(i,J,k)) < vh_neglect(i,J)) vhr(i,J,k) = 0.0
1165 enddo
1166
1167 ! Calculate new tracer concentration in each cell after accounting
1168 ! for the j-direction fluxes.
1169 !$omp target teams loop private(hlst, Ihnew, i, m)
11700 do j=js,je ; if (do_j_tr(j)) then
11710 do concurrent (i=is:ie)
11720 if ((vhh(i,J) /= 0.0) .or. (vhh(i,J-1) /= 0.0)) then
11730 do_i(i,j) = .true.
11740 hlst(i) = hprev(i,j,k)
11750 hprev(i,j,k) = max(hprev(i,j,k) - (vhh(i,J) - vhh(i,J-1)), 0.0)
11760 if (hprev(i,j,k) <= 0.0) then ; do_i(i,j) = .false.
11770 elseif (hprev(i,j,k) < h_neglect*G%areaT(i,j)) then
11780 hlst(i) = hlst(i) + (h_neglect*G%areaT(i,j) - hprev(i,j,k))
11790 Ihnew(i) = 1.0 / (h_neglect*G%areaT(i,j))
11800 else ; Ihnew(i) = 1.0 / hprev(i,j,k) ; endif
11810 else ; do_i(i,j) = .false. ; endif
1182 enddo
1183
1184 ! Update do_i so that nothing changes outside of the OBC (problem for interior OBCs only)
11850 if (associated(OBC)) then
1186 if (.not.OBC%exterior_OBC_bug .and. OBC%OBC_pe &
11870 .and. (OBC%specified_v_BCs_exist_globally .or. OBC%open_v_BCs_exist_globally)) then
1188 ! OBC_DIRECTION_N / OBC_DIRECTION_S on the south / north edge
11890 do concurrent (i=is:ie, OBC%segnum_v(i,J-1) > 0 .or. OBC%segnum_v(i,J) < 0)
11900 do_i(i,j) = .false.
1191 enddo
1192 endif
1193 endif
1194
1195 ! update tracer and save some diagnostics
11960 do concurrent (m=1:ntr)
11970 do concurrent (i=is:ie, do_i(i,j))
1198 Tr(m)%t(i,j,k) = (Tr(m)%t(i,j,k) * hlst(i) - &
11990 (flux_y(i,m,J) - flux_y(i,m,J-1))) * Ihnew(i)
1200 enddo
1201
1202 ! diagnose convergence of flux_y and add to convergence of flux_x.
1203 ! division by areaT to get into W/m2 for heat and kg/(s*m2) for salt.
12040 if (associated(Tr(m)%advection_xy)) then
12050 do concurrent (i=is:ie, do_i(i,j))
1206 Tr(m)%advection_xy(i,j,k) = Tr(m)%advection_xy(i,j,k) - &
1207 (flux_y(i,m,J) - flux_y(i,m,J-1))* Idt * &
12080 G%IareaT(i,j)
1209 enddo
1210 endif
1211
1212 enddo
1213 endif ; enddo ! End of j-loop.
1214
1215 ! Do user controlled underflow of the tracer concentrations.
12160 do concurrent (m=1:ntr, Tr(m)%conc_underflow > 0.0)
12170 do concurrent (j=js:je, i=is:ie, abs(Tr(m)%t(i,j,k)) < Tr(m)%conc_underflow)
12180 Tr(m)%t(i,j,k) = 0.0
1219 enddo
1220 enddo
1221
1222 ! compute ad_y and ad2d_y diagnostic outside above j-loop so as to make the summation ordered when OMP is active.
12230 do m=1,ntr ; if (associated(Tr(m)%ad_y)) then
12240 do concurrent (J=js-1:je, domore_v_initial(J))
12250 do concurrent (i=is:ie, do_i(i,j) .or. do_i(i,j+1))
12260 Tr(m)%ad_y(i,J,k) = Tr(m)%ad_y(i,J,k) + flux_y(i,m,J)*Idt
1227 enddo
1228 enddo
1229 endif ; enddo ! End of m-loop.
1230
12310 do m=1,ntr ; if (associated(Tr(m)%ad2d_y)) then
12320 do concurrent (J=js-1:je, domore_v_initial(J))
12330 do concurrent (i=is:ie, do_i(i,j) .or. do_i(i,j+1))
12340 Tr(m)%ad2d_y(i,J) = Tr(m)%ad2d_y(i,J) + flux_y(i,m,J)*Idt
1235 enddo
1236 enddo
1237 endif ; enddo ! End of m-loop.
1238
1239 !$omp target exit data map(release: vhh, T_tmp, slope_y, flux_y, domore_v_initial, do_j_tr, do_i)
1240
12410end subroutine advect_y
1242
1243!> Initialize lateral tracer advection module
12441subroutine tracer_advect_init(Time, G, US, param_file, diag, CS)
1245 type(time_type), target, intent(in) :: Time !< current model time
1246 type(ocean_grid_type), intent(in) :: G !< ocean grid structure
1247 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1248 type(param_file_type), intent(in) :: param_file !< open file to parse for model parameters
1249 type(diag_ctrl), target, intent(inout) :: diag !< regulates diagnostic output
1250 type(tracer_advect_CS), pointer :: CS !< module control structure
1251
1252 ! This include declares and sets the variable "version".
1253# include "version_variable.h"
1254 character(len=40) :: mdl = "MOM_tracer_advect" ! This module's name.
1255 character(len=256) :: mesg ! Message for error messages.
1256
12571 if (associated(CS)) then
12580 call MOM_error(WARNING, "tracer_advect_init called with associated control structure.")
12590 return
1260 endif
12611 allocate(CS)
1262
12631 CS%diag => diag
1264
1265 ! Read all relevant parameters and write them to the model log.
12661 call log_version(param_file, mdl, version, "")
1267 call get_param(param_file, mdl, "DT", CS%dt, fail_if_missing=.true., &
12681 desc="The (baroclinic) dynamics time step.", units="s", scale=US%s_to_T)
12691 call get_param(param_file, mdl, "DEBUG", CS%debug, default=.false.)
1270 call get_param(param_file, mdl, "TRACER_ADVECTION_SCHEME", mesg, &
1271 desc="The horizontal transport scheme for tracers:\n"//&
12721 trim(TracerAdvectionSchemeDoc), default='PLM')
1273
1274 ! Get the integer value of the tracer scheme
12751 call set_tracer_advect_scheme(CS%default_advect_scheme, mesg)
1276
12771 if (CS%default_advect_scheme == ADVECT_PPMH3) then
1278 call get_param(param_file, mdl, "USE_HUYNH_STENCIL_BUG", &
1279 CS%useHuynhStencilBug, &
1280 desc="If true, use a stencil width of 2 in PPM:H3 tracer advection. " &
1281 // "This is incorrect and will produce regressions in certain " &
1282 // "configurations, but may be required to reproduce results in " &
1283 // "legacy simulations.", &
12840 default=.false.)
1285 endif
1286
1287 !$omp target enter data map(to: CS)
1288
12891 id_clock_advect = cpu_clock_id('(Ocean advect tracer)', grain=CLOCK_MODULE)
12901 id_clock_pass = cpu_clock_id('(Ocean tracer halo updates)', grain=CLOCK_ROUTINE)
12911 id_clock_sync = cpu_clock_id('(Ocean tracer global synch)', grain=CLOCK_ROUTINE)
1292
1293end subroutine tracer_advect_init
1294
1295!> Close the tracer advection module
12961subroutine tracer_advect_end(CS)
1297 type(tracer_advect_CS), pointer :: CS !< module control structure
1298
1299 !$omp target exit data map(delete: CS)
13001 if (associated(CS)) deallocate(CS)
1301
13021end subroutine tracer_advect_end
1303
1304
1305!> \namespace mom_tracer_advect
1306!!
1307!! This program contains the subroutines that advect tracers
1308!! horizontally (i.e. along layers).
1309!!
1310!! \section section_mom_advect_intro
1311!!
1312!! * advect_tracer advects tracer concentrations using a combination
1313!! of the modified flux advection scheme from Easter (Mon. Wea. Rev.,
1314!! 1993) with tracer distributions given by the monotonic piecewise
1315!! parabolic method, as described in Carpenter et al. (MWR, 1990).
1316!! This scheme conserves the total amount of tracer while avoiding
1317!! spurious maxima and minima of the tracer concentration.
1318!!
1319!! * advect_tracer subroutine determines the volume of a layer in
1320!! a grid cell at the previous instance when the tracer concentration
1321!! was changed, so it is essential that the volume fluxes should be
1322!! correct. It is also important that the tracer advection occurs
1323!! before each calculation of the diabatic forcing.
1324!!
1325!! The advection scheme of some tracers can be set to be different
1326!! to that used by active tracers.
1327
1328
13290end module MOM_tracer_advect