← back to index

src/tracer/MOM_tracer_diabatic.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!> This module contains routines that implement physical fluxes of tracers (e.g. due
6!! to surface fluxes or mixing). These are intended to be called from call_tracer_column_fns
7!! in the MOM_tracer_flow_control module.
8module MOM_tracer_diabatic
9
10use MOM_grid, only : ocean_grid_type
11use MOM_verticalGrid, only : verticalGrid_type
12use MOM_forcing_type, only : forcing
13use MOM_error_handler, only : MOM_error, FATAL, WARNING
14
15implicit none ; private
16
17#include <MOM_memory.h>
18public tracer_vertdiff, tracer_vertdiff_Eulerian
19public applyTracerBoundaryFluxesInOut
20
21contains
22
23!> This subroutine solves a tridiagonal equation for the final tracer concentrations after the
24!! dual-entrainments, and possibly sinking or surface and bottom sources, are applied. The sinking
25!! is implemented with an fully implicit upwind advection scheme. Alternate time units can be
26!! used for the timestep, surface and bottom fluxes and sink_rate provided they are all consistent.
2724subroutine tracer_vertdiff(h_old, ea, eb, dt, tr, G, GV, &
280 sfc_flux, btm_flux, btm_reservoir, sink_rate, convert_flux_in)
29 type(ocean_grid_type), intent(in) :: G !< ocean grid structure
30 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
31 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h_old !< layer thickness before entrainment
32 !! [H ~> m or kg m-2]
33 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: ea !< amount of fluid entrained from the layer
34 !! above [H ~> m or kg m-2]
35 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: eb !< amount of fluid entrained from the layer
36 !! below [H ~> m or kg m-2]
37 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: tr !< tracer concentration in concentration units [CU]
38 real, intent(in) :: dt !< amount of time covered by this call [T ~> s]
39 real, dimension(SZI_(G),SZJ_(G)), optional,intent(in) :: sfc_flux !< surface flux of the tracer in units of
40 !! [CU R Z T-1 ~> CU kg m-2 s-1] or
41 !! [CU H ~> CU m or CU kg m-2] if
42 !! convert_flux_in is .false.
43 real, dimension(SZI_(G),SZJ_(G)), optional,intent(in) :: btm_flux !< The (negative upward) bottom flux of the
44 !! tracer in [CU R Z T-1 ~> CU kg m-2 s-1] or
45 !! [CU H ~> CU m or CU kg m-2] if
46 !! convert_flux_in is .false.
47 real, dimension(SZI_(G),SZJ_(G)), optional,intent(inout) :: btm_reservoir !< amount of tracer in a bottom reservoir
48 !! [CU R Z ~> CU kg m-2]
49 real, optional,intent(in) :: sink_rate !< rate at which the tracer sinks
50 !! [Z T-1 ~> m s-1]
51 logical, optional,intent(in) :: convert_flux_in !< True if the specified sfc_flux needs
52 !! to be integrated in time
53
54 ! local variables
55 real :: sink_dist !< The distance the tracer sinks in a time step [H ~> m or kg m-2].
56 real, dimension(SZI_(G),SZJ_(G)) :: &
5724 sfc_src, & !< The time-integrated surface source of the tracer [CU H ~> CU m or CU kg m-2].
5824 btm_src !< The time-integrated bottom source of the tracer [CU H ~> CU m or CU kg m-2].
59 real, dimension(SZI_(G)) :: &
6012 b1, & !< b1 is used by the tridiagonal solver [H-1 ~> m-1 or m2 kg-1].
6124 d1 !! d1=1-c1 is used by the tridiagonal solver [nondim].
6224 real :: c1(SZI_(G),SZK_(GV)) !< c1 is used by the tridiagonal solver [nondim].
6324 real :: h_minus_dsink(SZI_(G),SZK_(GV)) !< The layer thickness minus the
64 !! difference in sinking rates across the layer [H ~> m or kg m-2].
65 !! By construction, 0 <= h_minus_dsink < h_work.
6612 real :: sink(SZI_(G),SZK_(GV)+1) !< The tracer's sinking distances at the
67 !! interfaces, limited to prevent characteristics from
68 !! crossing within a single timestep [H ~> m or kg m-2].
69 real :: b_denom_1 !< The first term in the denominator of b1 [H ~> m or kg m-2].
70 real :: h_tr !< h_tr is h at tracer points with a h_neglect added to
71 !! ensure positive definiteness [H ~> m or kg m-2].
72 real :: h_neglect !< A thickness that is so small it is usually lost
73 !! in roundoff and can be neglected [H ~> m or kg m-2].
74 logical :: convert_flux = .true.
75
76 integer :: i, j, k, is, ie, js, je, nz
7712 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
78
7912 if (nz == 1) then
80 call MOM_error(WARNING, "MOM_tracer_diabatic.F90, tracer_vertdiff called "//&
810 "with only one vertical level")
820 return
83 endif
84
8512 if (present(convert_flux_in)) convert_flux = convert_flux_in
8612 h_neglect = GV%H_subroundoff
8712 sink_dist = 0.0
8812 if (present(sink_rate)) sink_dist = (dt*sink_rate) * GV%Z_to_H
89 !$OMP parallel default(shared) private(sink,h_minus_dsink,b_denom_1,b1,d1,h_tr,c1)
90 !$OMP do
9187132 do j=js,je ; do i=is,ie ; sfc_src(i,j) = 0.0 ; btm_src(i,j) = 0.0 ; enddo ; enddo
9212 if (present(sfc_flux)) then
930 if (convert_flux) then
94 !$OMP do
950 do j=js,je ; do i=is,ie
960 sfc_src(i,j) = (sfc_flux(i,j)*dt) * GV%RZ_to_H
97 enddo ; enddo
98 else
99 !$OMP do
1000 do j=js,je ; do i=is,ie
1010 sfc_src(i,j) = sfc_flux(i,j)
102 enddo ; enddo
103 endif
104 endif
10512 if (present(btm_flux)) then
1060 if (convert_flux) then
107 !$OMP do
1080 do j=js,je ; do i=is,ie
1090 btm_src(i,j) = (btm_flux(i,j)*dt) * GV%RZ_to_H
110 enddo ; enddo
111 else
112 !$OMP do
1130 do j=js,je ; do i=is,ie
1140 btm_src(i,j) = btm_flux(i,j)
115 enddo ; enddo
116 endif
117 endif
118
11912 if (present(sink_rate)) then
120 !$OMP do
1210 do j=js,je
122 ! Find the sinking rates at all interfaces, limiting them if necesary
123 ! so that the characteristics do not cross within a timestep.
124 ! If a non-constant sinking rate were used, that would be incorprated
125 ! here.
1260 if (present(btm_reservoir)) then
1270 do i=is,ie ; sink(i,nz+1) = sink_dist ; enddo
1280 do k=2,nz ; do i=is,ie
1290 sink(i,K) = sink_dist ; h_minus_dsink(i,k) = h_old(i,j,k)
130 enddo ; enddo
131 else
1320 do i=is,ie ; sink(i,nz+1) = 0.0 ; enddo
133 ! Find the limited sinking distance at the interfaces.
1340 do k=nz,2,-1 ; do i=is,ie
1350 if (sink(i,K+1) >= sink_dist) then
1360 sink(i,K) = sink_dist
1370 h_minus_dsink(i,k) = h_old(i,j,k) + (sink(i,K+1) - sink(i,K))
1380 elseif (sink(i,K+1) + h_old(i,j,k) < sink_dist) then
1390 sink(i,K) = sink(i,K+1) + h_old(i,j,k)
1400 h_minus_dsink(i,k) = 0.0
141 else
1420 sink(i,K) = sink_dist
1430 h_minus_dsink(i,k) = (h_old(i,j,k) + sink(i,K+1)) - sink(i,K)
144 endif
145 enddo ; enddo
146 endif
1470 do i=is,ie
1480 sink(i,1) = 0.0 ; h_minus_dsink(i,1) = (h_old(i,j,1) + sink(i,2))
149 enddo
150
151 ! Now solve the tridiagonal equation for the tracer concentrations.
1520 do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
1530 b_denom_1 = h_minus_dsink(i,1) + ea(i,j,1) + h_neglect
1540 b1(i) = 1.0 / (b_denom_1 + eb(i,j,1))
1550 d1(i) = b_denom_1 * b1(i)
1560 h_tr = h_old(i,j,1) + h_neglect
1570 tr(i,j,1) = (b1(i)*h_tr)*tr(i,j,1) + b1(i)*sfc_src(i,j)
158 endif ; enddo
1590 do k=2,nz-1 ; do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
1600 c1(i,k) = eb(i,j,k-1) * b1(i)
161 b_denom_1 = h_minus_dsink(i,k) + d1(i) * (ea(i,j,k) + sink(i,K)) + &
1620 h_neglect
1630 b1(i) = 1.0 / (b_denom_1 + eb(i,j,k))
1640 d1(i) = b_denom_1 * b1(i)
1650 h_tr = h_old(i,j,k) + h_neglect
166 tr(i,j,k) = b1(i) * (h_tr * tr(i,j,k) + &
1670 (ea(i,j,k) + sink(i,K)) * tr(i,j,k-1))
168 endif ; enddo ; enddo
1690 do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
1700 c1(i,nz) = eb(i,j,nz-1) * b1(i)
171 b_denom_1 = h_minus_dsink(i,nz) + d1(i) * (ea(i,j,nz) + sink(i,nz)) + &
1720 h_neglect
1730 b1(i) = 1.0 / (b_denom_1 + eb(i,j,nz))
1740 h_tr = h_old(i,j,nz) + h_neglect
175 tr(i,j,nz) = b1(i) * ((h_tr * tr(i,j,nz) + btm_src(i,j)) + &
1760 (ea(i,j,nz) + sink(i,nz)) * tr(i,j,nz-1))
177 endif ; enddo
1780 if (present(btm_reservoir)) then ; do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
1790 btm_reservoir(i,j) = btm_reservoir(i,j) + (sink(i,nz+1)*tr(i,j,nz)) * GV%H_to_RZ
180 endif ; enddo ; endif
181
1820 do k=nz-1,1,-1 ; do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
1830 tr(i,j,k) = tr(i,j,k) + c1(i,k+1)*tr(i,j,k+1)
184 endif ; enddo ; enddo
185 enddo
186 else
187 !$OMP do
188732 do j=js,je
18987120 do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
19060168 h_tr = h_old(i,j,1) + h_neglect
19160168 b_denom_1 = h_tr + ea(i,j,1)
19260168 b1(i) = 1.0 / (b_denom_1 + eb(i,j,1))
19360168 d1(i) = h_tr * b1(i)
19460168 tr(i,j,1) = (b1(i)*h_tr)*tr(i,j,1) + b1(i)*sfc_src(i,j)
195 endif ; enddo
1966360480 do k=2,nz-1 ; do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
1974392264 c1(i,k) = eb(i,j,k-1) * b1(i)
1984392264 h_tr = h_old(i,j,k) + h_neglect
1994392264 b_denom_1 = h_tr + d1(i) * ea(i,j,k)
2004392264 b1(i) = 1.0 / (b_denom_1 + eb(i,j,k))
2014392264 d1(i) = b_denom_1 * b1(i)
2024392264 tr(i,j,k) = b1(i) * (h_tr * tr(i,j,k) + ea(i,j,k) * tr(i,j,k-1))
203 endif ; enddo ; enddo
20487120 do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
20560168 c1(i,nz) = eb(i,j,nz-1) * b1(i)
20660168 h_tr = h_old(i,j,nz) + h_neglect
20760168 b_denom_1 = h_tr + d1(i)*ea(i,j,nz)
20860168 b1(i) = 1.0 / ( b_denom_1 + eb(i,j,nz))
209 tr(i,j,nz) = b1(i) * (( h_tr * tr(i,j,nz) + btm_src(i,j)) + &
21060168 ea(i,j,nz) * tr(i,j,nz-1))
211 endif ; enddo
2126447612 do k=nz-1,1,-1 ; do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
2134452432 tr(i,j,k) = tr(i,j,k) + c1(i,k+1)*tr(i,j,k+1)
214 endif ; enddo ; enddo
215 enddo
216 endif
217 !$OMP end parallel
218
21912end subroutine tracer_vertdiff
220
221
222!> This subroutine solves a tridiagonal equation for the final tracer concentrations after
223!! Eulerian mixing, and possibly sinking or surface and bottom sources, are applied. The sinking
224!! is implemented with an fully implicit upwind advection scheme. Alternate time units can be
225!! used for the timestep, surface and bottom fluxes and sink_rate provided they are all consistent.
2260subroutine tracer_vertdiff_Eulerian(h_old, ent, dt, tr, G, GV, &
2270 sfc_flux, btm_flux, btm_reservoir, sink_rate, convert_flux_in)
228 type(ocean_grid_type), intent(in) :: G !< ocean grid structure
229 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
230 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h_old !< layer thickness before entrainment
231 !! [H ~> m or kg m-2]
232 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: ent !< Amount of fluid mixed across interfaces
233 !! [H ~> m or kg m-2]
234 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: tr !< tracer concentration in concentration units [CU]
235 real, intent(in) :: dt !< amount of time covered by this call [T ~> s]
236 real, dimension(SZI_(G),SZJ_(G)), optional,intent(in) :: sfc_flux !< surface flux of the tracer in units of
237 !! [CU R Z T-1 ~> CU kg m-2 s-1] or
238 !! [CU H ~> CU m or CU kg m-2] if
239 !! convert_flux_in is .false.
240 real, dimension(SZI_(G),SZJ_(G)), optional,intent(in) :: btm_flux !< The (negative upward) bottom flux of the
241 !! tracer in [CU kg m-2 T-1 ~> CU kg m-2 s-1] or
242 !! [CU H ~> CU m or CU kg m-2] if
243 !! convert_flux_in is .false.
244 real, dimension(SZI_(G),SZJ_(G)), optional,intent(inout) :: btm_reservoir !< amount of tracer in a bottom reservoir
245 !! [CU R Z ~> CU kg m-2]
246 real, optional,intent(in) :: sink_rate !< rate at which the tracer sinks
247 !! [Z T-1 ~> m s-1]
248 logical, optional,intent(in) :: convert_flux_in !< True if the specified sfc_flux needs
249 !! to be integrated in time
250
251 ! local variables
252 real :: sink_dist !< The distance the tracer sinks in a time step [H ~> m or kg m-2].
253 real, dimension(SZI_(G),SZJ_(G)) :: &
2540 sfc_src, & !< The time-integrated surface source of the tracer [CU H ~> CU m or CU kg m-2].
2550 btm_src !< The time-integrated bottom source of the tracer [CU H ~> CU m or CU kg m-2].
256 real, dimension(SZI_(G)) :: &
2570 b1, & !< b1 is used by the tridiagonal solver [H-1 ~> m-1 or m2 kg-1].
2580 d1 !! d1=1-c1 is used by the tridiagonal solver [nondim].
2590 real :: c1(SZI_(G),SZK_(GV)) !< c1 is used by the tridiagonal solver [nondim].
2600 real :: h_minus_dsink(SZI_(G),SZK_(GV)) !< The layer thickness minus the
261 !! difference in sinking rates across the layer [H ~> m or kg m-2].
262 !! By construction, 0 <= h_minus_dsink < h_work.
2630 real :: sink(SZI_(G),SZK_(GV)+1) !< The tracer's sinking distances at the
264 !! interfaces, limited to prevent characteristics from
265 !! crossing within a single timestep [H ~> m or kg m-2].
266 real :: b_denom_1 !< The first term in the denominator of b1 [H ~> m or kg m-2].
267 real :: h_tr !< h_tr is h at tracer points with a h_neglect added to
268 !! ensure positive definiteness [H ~> m or kg m-2].
269 real :: h_neglect !< A thickness that is so small it is usually lost
270 !! in roundoff and can be neglected [H ~> m or kg m-2].
271 logical :: convert_flux
272
273 integer :: i, j, k, is, ie, js, je, nz
2740 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
275
2760 if (nz == 1) then
277 call MOM_error(WARNING, "MOM_tracer_diabatic.F90, tracer_vertdiff called "//&
2780 "with only one vertical level")
2790 return
280 endif
281
2820 convert_flux = .true.
2830 if (present(convert_flux_in)) convert_flux = convert_flux_in
2840 h_neglect = GV%H_subroundoff
2850 sink_dist = 0.0
2860 if (present(sink_rate)) sink_dist = (dt*sink_rate) * GV%Z_to_H
287 !$OMP parallel default(shared) private(sink,h_minus_dsink,b_denom_1,b1,d1,h_tr,c1)
288 !$OMP do
2890 do j=js,je ; do i=is,ie ; sfc_src(i,j) = 0.0 ; btm_src(i,j) = 0.0 ; enddo ; enddo
2900 if (present(sfc_flux)) then
2910 if (convert_flux) then
292 !$OMP do
2930 do j=js,je ; do i=is,ie
2940 sfc_src(i,j) = (sfc_flux(i,j)*dt) * GV%RZ_to_H
295 enddo ; enddo
296 else
297 !$OMP do
2980 do j=js,je ; do i=is,ie
2990 sfc_src(i,j) = sfc_flux(i,j)
300 enddo ; enddo
301 endif
302 endif
3030 if (present(btm_flux)) then
3040 if (convert_flux) then
305 !$OMP do
3060 do j=js,je ; do i=is,ie
3070 btm_src(i,j) = (btm_flux(i,j)*dt) * GV%kg_m2_to_H
308 enddo ; enddo
309 else
310 !$OMP do
3110 do j=js,je ; do i=is,ie
3120 btm_src(i,j) = btm_flux(i,j)
313 enddo ; enddo
314 endif
315 endif
316
3170 if (present(sink_rate)) then
318 !$OMP do
3190 do j=js,je
320 ! Find the sinking rates at all interfaces, limiting them if necesary
321 ! so that the characteristics do not cross within a timestep.
322 ! If a non-constant sinking rate were used, that would be incorprated
323 ! here.
3240 if (present(btm_reservoir)) then
3250 do i=is,ie ; sink(i,nz+1) = sink_dist ; enddo
3260 do k=2,nz ; do i=is,ie
3270 sink(i,K) = sink_dist ; h_minus_dsink(i,k) = h_old(i,j,k)
328 enddo ; enddo
329 else
3300 do i=is,ie ; sink(i,nz+1) = 0.0 ; enddo
331 ! Find the limited sinking distance at the interfaces.
3320 do k=nz,2,-1 ; do i=is,ie
3330 if (sink(i,K+1) >= sink_dist) then
3340 sink(i,K) = sink_dist
3350 h_minus_dsink(i,k) = h_old(i,j,k) + (sink(i,K+1) - sink(i,K))
3360 elseif (sink(i,K+1) + h_old(i,j,k) < sink_dist) then
3370 sink(i,K) = sink(i,K+1) + h_old(i,j,k)
3380 h_minus_dsink(i,k) = 0.0
339 else
3400 sink(i,K) = sink_dist
3410 h_minus_dsink(i,k) = (h_old(i,j,k) + sink(i,K+1)) - sink(i,K)
342 endif
343 enddo ; enddo
344 endif
3450 do i=is,ie
3460 sink(i,1) = 0.0 ; h_minus_dsink(i,1) = (h_old(i,j,1) + sink(i,2))
347 enddo
348
349 ! Now solve the tridiagonal equation for the tracer concentrations.
3500 do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
3510 b_denom_1 = h_minus_dsink(i,1) + ent(i,j,1) + h_neglect
3520 b1(i) = 1.0 / (b_denom_1 + ent(i,j,2))
3530 d1(i) = b_denom_1 * b1(i)
3540 h_tr = h_old(i,j,1) + h_neglect
3550 tr(i,j,1) = (b1(i)*h_tr)*tr(i,j,1) + b1(i)*sfc_src(i,j)
356 endif ; enddo
3570 do k=2,nz-1 ; do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
3580 c1(i,k) = ent(i,j,K) * b1(i)
359 b_denom_1 = h_minus_dsink(i,k) + d1(i) * (ent(i,j,K) + sink(i,K)) + &
3600 h_neglect
3610 b1(i) = 1.0 / (b_denom_1 + ent(i,j,K+1))
3620 d1(i) = b_denom_1 * b1(i)
3630 h_tr = h_old(i,j,k) + h_neglect
364 tr(i,j,k) = b1(i) * (h_tr * tr(i,j,k) + &
3650 (ent(i,j,K) + sink(i,K)) * tr(i,j,k-1))
366 endif ; enddo ; enddo
3670 do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
3680 c1(i,nz) = ent(i,j,nz) * b1(i)
369 b_denom_1 = h_minus_dsink(i,nz) + d1(i) * (ent(i,j,nz) + sink(i,nz)) + &
3700 h_neglect
3710 b1(i) = 1.0 / (b_denom_1 + ent(i,j,nz+1))
3720 h_tr = h_old(i,j,nz) + h_neglect
373 tr(i,j,nz) = b1(i) * ((h_tr * tr(i,j,nz) + btm_src(i,j)) + &
3740 (ent(i,j,nz) + sink(i,nz)) * tr(i,j,nz-1))
375 endif ; enddo
3760 if (present(btm_reservoir)) then ; do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
3770 btm_reservoir(i,j) = btm_reservoir(i,j) + (sink(i,nz+1)*tr(i,j,nz)) * GV%H_to_RZ
378 endif ; enddo ; endif
379
3800 do k=nz-1,1,-1 ; do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
3810 tr(i,j,k) = tr(i,j,k) + c1(i,k+1)*tr(i,j,k+1)
382 endif ; enddo ; enddo
383 enddo
384 else
385 !$OMP do
3860 do j=js,je
3870 do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
3880 h_tr = h_old(i,j,1) + h_neglect
3890 b_denom_1 = h_tr + ent(i,j,1)
3900 b1(i) = 1.0 / (b_denom_1 + ent(i,j,2))
3910 d1(i) = h_tr * b1(i)
3920 tr(i,j,1) = (b1(i)*h_tr)*tr(i,j,1) + b1(i)*sfc_src(i,j)
393 endif ; enddo
3940 do k=2,nz-1 ; do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
3950 c1(i,k) = ent(i,j,K) * b1(i)
3960 h_tr = h_old(i,j,k) + h_neglect
3970 b_denom_1 = h_tr + d1(i) * ent(i,j,K)
3980 b1(i) = 1.0 / (b_denom_1 + ent(i,j,K+1))
3990 d1(i) = b_denom_1 * b1(i)
4000 tr(i,j,k) = b1(i) * (h_tr * tr(i,j,k) + ent(i,j,K) * tr(i,j,k-1))
401 endif ; enddo ; enddo
4020 do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
4030 c1(i,nz) = ent(i,j,nz) * b1(i)
4040 h_tr = h_old(i,j,nz) + h_neglect
4050 b_denom_1 = h_tr + d1(i)*ent(i,j,nz)
4060 b1(i) = 1.0 / ( b_denom_1 + ent(i,j,nz+1))
407 tr(i,j,nz) = b1(i) * (( h_tr * tr(i,j,nz) + btm_src(i,j)) + &
4080 ent(i,j,nz) * tr(i,j,nz-1))
409 endif ; enddo
4100 do k=nz-1,1,-1 ; do i=is,ie ; if (G%mask2dT(i,j) > 0.0) then
4110 tr(i,j,k) = tr(i,j,k) + c1(i,k+1)*tr(i,j,k+1)
412 endif ; enddo ; enddo
413 enddo
414 endif
415 !$OMP end parallel
416
4170end subroutine tracer_vertdiff_Eulerian
418
419
420!> This routine is modeled after applyBoundaryFluxesInOut in MOM_diabatic_aux.F90
421!! NOTE: Please note that in this routine sfc_flux gets set to zero to ensure that the surface
422!! flux of the tracer does not get applied again during a subsequent call to tracer_vertdif
42324subroutine applyTracerBoundaryFluxesInOut(G, GV, Tr, dt, fluxes, h, evap_CFL_limit, minimum_forcing_depth, &
4240 in_flux_optional, out_flux_optional, update_h_opt)
425
426 type(ocean_grid_type), intent(in ) :: G !< Grid structure
427 type(verticalGrid_type), intent(in ) :: GV !< ocean vertical grid structure
428 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: Tr !< Tracer concentration on T-cell [conc]
429 real, intent(in ) :: dt !< Time-step over which forcing is applied [T ~> s]
430 type(forcing), intent(in ) :: fluxes !< Surface fluxes container
431 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: h !< Layer thickness [H ~> m or kg m-2]
432 real, intent(in ) :: evap_CFL_limit !< Limit on the fraction of the
433 !! water that can be fluxed out of the top
434 !! layer in a timestep [nondim]
435 real, intent(in ) :: minimum_forcing_depth !< The smallest depth over
436 !! which fluxes can be applied [H ~> m or kg m-2]
437 real, dimension(SZI_(G),SZJ_(G)), optional, intent(in ) :: in_flux_optional !< The total time-integrated
438 !! amount of tracer that enters with freshwater
439 !! [conc H ~> conc m or conc kg m-2]
440 real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: out_flux_optional !< The total time-integrated
441 !! amount of tracer that leaves with freshwater
442 !! [conc H ~> conc m or conc kg m-2]
443 logical, optional, intent(in) :: update_h_opt !< Optional flag to determine whether
444 !! h should be updated
445
446 integer, parameter :: maxGroundings = 5
447 integer :: numberOfGroundings, iGround(maxGroundings), jGround(maxGroundings)
448 real :: IforcingDepthScale ! The inverse of the scale over which to apply forcing [H-1 ~> m-1 or m2 kg-1]
449 real :: dThickness ! The change in a layer's thickness [H ~> m or kg m-2]
450 real :: dTracer ! The change in the integrated tracer content of a layer [conc H ~> conc m or conc kg m-2]
451 real :: fractionOfForcing ! The fraction of the forcing to apply to a layer [nondim]
452 real :: hOld ! The layer thickness before surface forcing is applied [H ~> m or kg m-2]
453 real :: Ithickness ! The inverse of the new layer thickness [H-1 ~> m-1 or m2 kg-1]
454
45512 real :: h2d(SZI_(G),SZK_(GV)) ! A 2-d work copy of layer thicknesses [H ~> m or kg m-2]
45624 real :: Tr2d(SZI_(G),SZK_(GV)) ! A 2-d work copy of tracer concentrations [conc]
45724 real :: in_flux(SZI_(G),SZJ_(G)) ! The total time-integrated amount of tracer that
458 ! enters with freshwater [conc H ~> conc m or conc kg m-2]
45924 real :: out_flux(SZI_(G),SZJ_(G)) ! The total time-integrated amount of tracer that
460 ! leaves with freshwater [conc H ~> conc m or conc kg m-2]
46124 real :: netMassIn(SZI_(G)) ! The remaining mass entering ocean surface [H ~> m or kg m-2]
46224 real :: netMassOut(SZI_(G)) ! The remaining mass leaving ocean surface [H ~> m or kg m-2]
46324 real :: in_flux_1d(SZI_(G)) ! The remaining amount of tracer that enters with
464 ! the freshwater [conc H ~> conc m or conc kg m-2]
46512 real :: out_flux_1d(SZI_(G)) ! The remaining amount of tracer that leaves with
466 ! the freshwater [conc H ~> conc m or conc kg m-2]
467 real :: hGrounding(maxGroundings) ! The remaining fresh water flux that was not able to be
468 ! supplied from a column that grounded out [H ~> m or kg m-2]
469 logical :: update_h
470 integer :: i, j, is, ie, js, je, k, nz
471 character(len=45) :: mesg
472
47312 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
474
475 ! If no freshwater fluxes, nothing needs to be done in this routine
47612 if ( (.not. associated(fluxes%netMassIn)) .or. (.not. associated(fluxes%netMassOut)) ) return
477
478210540 in_flux(:,:) = 0.0 ; out_flux(:,:) = 0.0
47912 if (present(in_flux_optional)) then
4800 do j=js,je ; do i=is,ie
4810 in_flux(i,j) = in_flux_optional(i,j)
482 enddo ; enddo
483 endif
48412 if (present(out_flux_optional)) then
4850 do j=js,je ; do i=is,ie
4860 out_flux(i,j) = out_flux_optional(i,j)
487 enddo ; enddo
488 endif
489
49012 if (present(update_h_opt)) then
4910 update_h = update_h_opt
492 else
49312 update_h = .true.
494 endif
495
49612 numberOfGroundings = 0
497
498!$OMP parallel do default(none) shared(is,ie,js,je,nz,h,Tr,G,GV,fluxes,dt, &
499!$OMP IforcingDepthScale,minimum_forcing_depth, &
500!$OMP numberOfGroundings,iGround,jGround,update_h, &
501!$OMP in_flux,out_flux,hGrounding,evap_CFL_limit) &
502!$OMP private(h2d,Tr2d,netMassIn,netMassOut, &
503!$OMP in_flux_1d,out_flux_1d,fractionOfForcing, &
504!$OMP dThickness,dTracer,hOld,Ithickness)
505
506 ! Work in vertical slices for efficiency
507732 do j=js,je
508
509 ! Copy state into 2D-slice arrays
5106534720 do k=1,nz ; do i=is,ie
5116480000 h2d(i,k) = h(i,j,k)
5126534000 Tr2d(i,k) = Tr(i,j,k)
513 enddo ; enddo
514
51587120 do i = is,ie
51686400 in_flux_1d(i) = in_flux(i,j)
51787120 out_flux_1d(i) = out_flux(i,j)
518 enddo
519 ! The surface forcing is contained in the fluxes type.
520 ! We aggregate the thermodynamic forcing for a time step into the following:
521 ! These should have been set and stored during a call to applyBoundaryFluxesInOut
522 ! netMassIn = net mass entering at ocean surface over a timestep
523 ! netMassOut = net mass leaving ocean surface [H ~> m or kg m-2] over a time step.
524 ! netMassOut < 0 means mass leaves ocean.
525
526 ! Note here that the aggregateFW flag has already been taken care of in the call to
527 ! applyBoundaryFluxesInOut
52887120 do i=is,ie
52986400 netMassOut(i) = fluxes%netMassOut(i,j)
53087120 netMassIn(i) = fluxes%netMassIn(i,j)
531 enddo
532
533 ! Apply the surface boundary fluxes in three steps:
534 ! A/ update concentration from mass entering the ocean
535 ! B/ update concentration from mass leaving ocean.
53687120 do i=is,ie
53786400 if (G%mask2dT(i,j)>0.) then
538
539 ! A/ Update tracer due to incoming mass flux.
540120336 do k=1,1
541
542 ! Change in state due to forcing
54360168 dThickness = netMassIn(i) ! Since we are adding mass, we can use all of it
54460168 dTracer = 0.
545
546 ! Update the forcing by the part to be consumed within the present k-layer.
547 ! If fractionOfForcing = 1, then updated netMassIn, netHeat, and netSalt vanish.
54860168 netMassIn(i) = netMassIn(i) - dThickness
54960168 dTracer = dTracer + in_flux_1d(i)
55060168 in_flux_1d(i) = 0.0
551
552 ! Update state
55360168 hOld = h2d(i,k) ! Keep original thickness in hand
55460168 h2d(i,k) = h2d(i,k) + dThickness ! New thickness
555120336 if (h2d(i,k) > 0.0) then
55660168 Ithickness = 1.0/h2d(i,k) ! Inverse new thickness
557 ! The "if"s below avoid changing T/S by roundoff unnecessarily
55860168 if (dThickness /= 0. .or. dTracer /= 0.) tr2d(i,k) = (hOld*tr2d(i,k)+ dTracer)*Ithickness
559 endif
560
561 enddo ! k=1,1
562
563 ! B/ Update tracer from mass leaving ocean
5644572768 do k=1,nz
565
566 ! Place forcing into this layer if this layer has nontrivial thickness.
567 ! For layers thin relative to 1/IforcingDepthScale, then distribute
568 ! forcing into deeper layers.
5694512600 IforcingDepthScale = 1. / max(GV%H_subroundoff, minimum_forcing_depth - netMassOut(i) )
570 ! fractionOfForcing = 1.0, unless h2d is less than IforcingDepthScale.
5714512600 fractionOfForcing = min(1.0, h2d(i,k)*IforcingDepthScale)
572
573 ! In the case with (-1)*netMassOut*fractionOfForcing greater than cfl*h, we
574 ! limit the forcing applied to this cell, leaving the remaining forcing to
575 ! be distributed downwards.
5764512600 if (-fractionOfForcing*netMassOut(i) > evap_CFL_limit*h2d(i,k)) then
5770 fractionOfForcing = -evap_CFL_limit*h2d(i,k)/netMassOut(i)
578 endif
579
580 ! Change in state due to forcing
5814512600 dThickness = max( fractionOfForcing*netMassOut(i), -h2d(i,k) )
582 ! Note this is slightly different to how salt is currently treated
5834512600 dTracer = fractionOfForcing*out_flux_1d(i)
584
585 ! Update the forcing by the part to be consumed within the present k-layer.
586 ! If fractionOfForcing = 1, then new netMassOut vanishes.
5874512600 netMassOut(i) = netMassOut(i) - dThickness
5884512600 out_flux_1d(i) = out_flux_1d(i) - dTracer
589
590 ! Update state by the appropriate increment.
5914512600 hOld = h2d(i,k) ! Keep original thickness in hand
5924512600 h2d(i,k) = h2d(i,k) + dThickness ! New thickness
5934572768 if (h2d(i,k) > 0.) then
5944512600 Ithickness = 1.0/h2d(i,k) ! Inverse of new thickness
5954512600 Tr2d(i,k) = (hOld*Tr2d(i,k) + dTracer)*Ithickness
596 endif
597
598 enddo ! k
599
600 endif
601
602 ! If anything remains after the k-loop, then we have grounded out, which is a problem.
60387120 if (abs(in_flux_1d(i))+abs(out_flux_1d(i)) /= 0.0) then
604!$OMP critical
6050 numberOfGroundings = numberOfGroundings +1
6060 if (numberOfGroundings<=maxGroundings) then
6070 iGround(numberOfGroundings) = i ! Record i,j location of event for
6080 jGround(numberOfGroundings) = j ! warning message
6090 hGrounding(numberOfGroundings) = abs(in_flux_1d(i))+abs(out_flux_1d(i))
610 endif
611!$OMP end critical
612 endif
613
614 enddo ! i
615
616 ! Step C/ copy updated tracer concentration from the 2d slice now back into model state.
6176534720 do k=1,nz ; do i=is,ie
6186534000 Tr(i,j,k) = Tr2d(i,k)
619 enddo ; enddo
620
621732 if (update_h) then
6226534720 do k=1,nz ; do i=is,ie
6236534000 h(i,j,k) = h2d(i,k)
624 enddo ; enddo
625 endif
626
627 enddo ! j-loop finish
628
62912 if (numberOfGroundings>0) then
6300 do i = 1, min(numberOfGroundings, maxGroundings)
6310 write(mesg(1:45),'(3es15.3)') G%geoLonT( iGround(i), jGround(i) ), &
6320 G%geoLatT( iGround(i), jGround(i)) , hGrounding(i)
633 call MOM_error(WARNING, "MOM_tracer_diabatic.F90, applyTracerBoundaryFluxesInOut(): "//&
6340 "Tracer created. x,y,dh= "//trim(mesg), all_print=.true.)
635 enddo
636
6370 if (numberOfGroundings - maxGroundings > 0) then
6380 write(mesg, '(I0)') numberOfGroundings - maxGroundings
639 call MOM_error(WARNING, "MOM_tracer_vertical.F90, applyTracerBoundaryFluxesInOut(): "//&
6400 trim(mesg) // " groundings remaining", all_print=.true.)
641 endif
642 endif
643
64412end subroutine applyTracerBoundaryFluxesInOut
645end module MOM_tracer_diabatic