MOM6
MOM_lateral_boundary_diffusion.F90
Go to the documentation of this file.
1 !> Calculates and applies diffusive fluxes as a parameterization of lateral mixing (non-neutral) by
2 !! mesoscale eddies near the top and bottom (to be implemented) boundary layers of the ocean.
3 
5 
6 ! This file is part of MOM6. See LICENSE.md for the license.
7 
8 use mom_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end
9 use mom_cpu_clock, only : clock_module, clock_routine
10 use mom_domains, only : pass_var
11 use mom_diag_mediator, only : diag_ctrl, time_type
13 use mom_error_handler, only : mom_error, fatal, warning, mom_mesg, is_root_pe
16 use mom_grid, only : ocean_grid_type
23 use mom_cvmix_kpp, only : kpp_get_bld, kpp_cs
26 
27 implicit none ; private
28 
30 public boundary_k_range
31 
32 ! Private parameters to avoid doing string comparisons for bottom or top boundary layer
33 integer, public, parameter :: surface = -1 !< Set a value that corresponds to the surface bopundary
34 integer, public, parameter :: bottom = 1 !< Set a value that corresponds to the bottom boundary
35 #include <MOM_memory.h>
36 
37 !> Sets parameters for lateral boundary mixing module.
38 type, public :: lateral_boundary_diffusion_cs ; private
39  integer :: method !< Determine which of the three methods calculate
40  !! and apply near boundary layer fluxes
41  !! 1. Bulk-layer approach
42  !! 2. Along layer
43  integer :: deg !< Degree of polynomial reconstruction
44  integer :: surface_boundary_scheme !< Which boundary layer scheme to use
45  !! 1. ePBL; 2. KPP
46  type(remapping_cs) :: remap_cs !< Control structure to hold remapping configuration
47  type(kpp_cs), pointer :: kpp_csp => null() !< KPP control structure needed to get BLD
48  type(energetic_pbl_cs), pointer :: energetic_pbl_csp => null() !< ePBL control structure needed to get BLD
49  type(diag_ctrl), pointer :: diag => null() !< A structure that is used to
50  !! regulate the timing of diagnostic output.
52 
53 ! This include declares and sets the variable "version".
54 #include "version_variable.h"
55 character(len=40) :: mdl = "MOM_lateral_boundary_diffusion" !< Name of this module
56 
57 contains
58 
59 !> Initialization routine that reads runtime parameters and sets up pointers to other control structures that might be
60 !! needed for lateral boundary diffusion.
61 logical function lateral_boundary_diffusion_init(Time, G, param_file, diag, diabatic_CSp, CS)
62  type(time_type), target, intent(in) :: time !< Time structure
63  type(ocean_grid_type), intent(in) :: g !< Grid structure
64  type(param_file_type), intent(in) :: param_file !< Parameter file structure
65  type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure
66  type(diabatic_cs), pointer :: diabatic_csp !< KPP control structure needed to get BLD
67  type(lateral_boundary_diffusion_cs), pointer :: cs !< Lateral boundary mixing control structure
68 
69  ! local variables
70  character(len=80) :: string ! Temporary strings
71  logical :: boundary_extrap
72 
73  if (ASSOCIATED(cs)) then
74  call mom_error(fatal, "lateral_boundary_diffusion_init called with associated control structure.")
75  return
76  endif
77 
78  ! Log this module and master switch for turning it on/off
79  call log_version(param_file, mdl, version, &
80  "This module implements lateral diffusion of tracers near boundaries")
81  call get_param(param_file, mdl, "USE_LATERAL_BOUNDARY_DIFFUSION", lateral_boundary_diffusion_init, &
82  "If true, enables the lateral boundary tracer's diffusion module.", &
83  default=.false.)
84 
85  if (.not. lateral_boundary_diffusion_init) then
86  return
87  endif
88 
89  allocate(cs)
90  cs%diag => diag
91  call extract_diabatic_member(diabatic_csp, kpp_csp=cs%KPP_CSp)
92  call extract_diabatic_member(diabatic_csp, energetic_pbl_csp=cs%energetic_PBL_CSp)
93 
94  cs%surface_boundary_scheme = -1
95  if ( .not. ASSOCIATED(cs%energetic_PBL_CSp) .and. .not. ASSOCIATED(cs%KPP_CSp) ) then
96  call mom_error(fatal,"Lateral boundary diffusion is true, but no valid boundary layer scheme was found")
97  endif
98 
99  ! Read all relevant parameters and write them to the model log.
100  call get_param(param_file, mdl, "LATERAL_BOUNDARY_METHOD", cs%method, &
101  "Determine how to apply boundary lateral diffusion of tracers: \n"//&
102  "1. Bulk layer approach \n"//&
103  "2. Along layer approach", default=1)
104  call get_param(param_file, mdl, "LBD_BOUNDARY_EXTRAP", boundary_extrap, &
105  "Use boundary extrapolation in LBD code", &
106  default=.false.)
107  call get_param(param_file, mdl, "LBD_REMAPPING_SCHEME", string, &
108  "This sets the reconstruction scheme used "//&
109  "for vertical remapping for all variables. "//&
110  "It can be one of the following schemes: "//&
112  call initialize_remapping( cs%remap_CS, string, boundary_extrapolation = boundary_extrap )
113  call extract_member_remapping_cs(cs%remap_CS, degree=cs%deg)
114 
116 
117 !> Driver routine for calculating lateral diffusive fluxes near the top and bottom boundaries.
118 !! Two different methods are available:
119 !! Method 1: lower order representation, calculate fluxes from bulk layer integrated quantities.
120 !! Method 2: more straight forward, diffusion is applied layer by layer using only information
121 !! from neighboring cells.
122 subroutine lateral_boundary_diffusion(G, GV, US, h, Coef_x, Coef_y, dt, Reg, CS)
123  type(ocean_grid_type), intent(inout) :: g !< Grid type
124  type(verticalgrid_type), intent(in) :: gv !< ocean vertical grid structure
125  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
126  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), &
127  intent(in) :: h !< Layer thickness [H ~> m or kg m-2]
128  real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: coef_x !< dt * Kh * dy / dx at u-points [m2]
129  real, dimension(SZI_(G),SZJB_(G)), intent(in) :: coef_y !< dt * Kh * dx / dy at v-points [m2]
130  real, intent(in) :: dt !< Tracer time step * I_numitts
131  !! (I_numitts in tracer_hordiff)
132  type(tracer_registry_type), pointer :: reg !< Tracer registry
133  type(lateral_boundary_diffusion_cs), intent(in) :: cs !< Control structure for this module
134 
135  ! Local variables
136  real, dimension(SZI_(G),SZJ_(G)) :: hbl !< bnd. layer depth [m]
137  real, dimension(SZI_(G),SZJ_(G),SZK_(G),CS%deg+1) :: ppoly0_coefs !< Coefficients of polynomial
138  real, dimension(SZI_(G),SZJ_(G),SZK_(G),2) :: ppoly0_e !< Edge values from reconstructions
139  real, dimension(SZK_(G),CS%deg+1) :: ppoly_s !< Slopes from reconstruction (placeholder)
140  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)) :: uflx !< Zonal flux of tracer [conc m^3]
141  real, dimension(SZIB_(G),SZJ_(G)) :: uflx_bulk !< Total calculated bulk-layer u-flux for the tracer
142  real, dimension(SZI_(G),SZJB_(G),SZK_(G)) :: vflx !< Meridional flux of tracer [conc m^3]
143  real, dimension(SZI_(G),SZJB_(G)) :: vflx_bulk !< Total calculated bulk-layer v-flux for the tracer
144  real, dimension(SZIB_(G),SZJ_(G)) :: uwork_2d !< Layer summed u-flux transport
145  real, dimension(SZI_(G),SZJB_(G)) :: vwork_2d !< Layer summed v-flux transport
146  real, dimension(SZI_(G),SZJ_(G),G%ke) :: tendency !< tendency array for diagn
147  real, dimension(SZI_(G),SZJ_(G)) :: tendency_2d !< depth integrated content tendency for diagn
148  type(tracer_type), pointer :: tracer => null() !< Pointer to the current tracer
149  integer :: remap_method !< Reconstruction method
150  integer :: i,j,k,m !< indices to loop over
151  real :: idt !< inverse of the time step [s-1]
152 
153  idt = 1./dt
154  hbl(:,:) = 0.
155  if (ASSOCIATED(cs%KPP_CSp)) call kpp_get_bld(cs%KPP_CSp, hbl, g)
156  if (ASSOCIATED(cs%energetic_PBL_CSp)) call energetic_pbl_get_mld(cs%energetic_PBL_CSp, hbl, g, us)
157 
158  call pass_var(hbl,g%Domain)
159  do m = 1,reg%ntr
160  tracer => reg%tr(m)
161 
162  ! for diagnostics
163  if (tracer%id_lbdxy_conc > 0 .or. tracer%id_lbdxy_cont > 0 .or. tracer%id_lbdxy_cont_2d > 0) then
164  tendency(:,:,:) = 0.0
165  endif
166 
167  do j = g%jsc-1, g%jec+1
168  ! Interpolate state to interface
169  do i = g%isc-1, g%iec+1
170  call build_reconstructions_1d( cs%remap_CS, g%ke, h(i,j,:), tracer%t(i,j,:), ppoly0_coefs(i,j,:,:), &
171  ppoly0_e(i,j,:,:), ppoly_s, remap_method, gv%H_subroundoff, gv%H_subroundoff)
172  enddo
173  enddo
174  ! Diffusive fluxes in the i-direction
175  uflx(:,:,:) = 0.
176  vflx(:,:,:) = 0.
177  uflx_bulk(:,:) = 0.
178  vflx_bulk(:,:) = 0.
179 
180  ! Method #1
181  if ( cs%method == 1 ) then
182  do j=g%jsc,g%jec
183  do i=g%isc-1,g%iec
184  if (g%mask2dCu(i,j)>0.) then
185  call fluxes_bulk_method(surface, gv%ke, cs%deg, h(i,j,:), h(i+1,j,:), hbl(i,j), hbl(i+1,j), &
186  g%areaT(i,j), g%areaT(i+1,j), tracer%t(i,j,:), tracer%t(i+1,j,:), &
187  ppoly0_coefs(i,j,:,:), ppoly0_coefs(i+1,j,:,:), ppoly0_e(i,j,:,:), &
188  ppoly0_e(i+1,j,:,:), remap_method, coef_x(i,j), uflx_bulk(i,j), uflx(i,j,:))
189  endif
190  enddo
191  enddo
192  do j=g%jsc-1,g%jec
193  do i=g%isc,g%iec
194  if (g%mask2dCv(i,j)>0.) then
195  call fluxes_bulk_method(surface, gv%ke, cs%deg, h(i,j,:), h(i,j+1,:), hbl(i,j), hbl(i,j+1), &
196  g%areaT(i,j), g%areaT(i,j+1), tracer%t(i,j,:), tracer%t(i,j+1,:), &
197  ppoly0_coefs(i,j,:,:), ppoly0_coefs(i,j+1,:,:), ppoly0_e(i,j,:,:), &
198  ppoly0_e(i,j+1,:,:), remap_method, coef_y(i,j), vflx_bulk(i,j), vflx(i,j,:))
199  endif
200  enddo
201  enddo
202  ! Post tracer bulk diags
203  if (tracer%id_lbd_bulk_dfx>0) call post_data(tracer%id_lbd_bulk_dfx, uflx_bulk*idt, cs%diag)
204  if (tracer%id_lbd_bulk_dfy>0) call post_data(tracer%id_lbd_bulk_dfy, vflx_bulk*idt, cs%diag)
205 
206  ! Method #2
207  elseif (cs%method == 2) then
208  do j=g%jsc,g%jec
209  do i=g%isc-1,g%iec
210  if (g%mask2dCu(i,j)>0.) then
211  call fluxes_layer_method(surface, gv%ke, cs%deg, h(i,j,:), h(i+1,j,:), hbl(i,j), hbl(i+1,j), &
212  g%areaT(i,j), g%areaT(i+1,j), tracer%t(i,j,:), tracer%t(i+1,j,:), ppoly0_coefs(i,j,:,:), &
213  ppoly0_coefs(i+1,j,:,:), ppoly0_e(i,j,:,:), ppoly0_e(i+1,j,:,:), remap_method, coef_x(i,j), uflx(i,j,:))
214  endif
215  enddo
216  enddo
217  do j=g%jsc-1,g%jec
218  do i=g%isc,g%iec
219  if (g%mask2dCv(i,j)>0.) then
220  call fluxes_layer_method(surface, gv%ke, cs%deg, h(i,j,:), h(i,j+1,:), hbl(i,j), hbl(i,j+1), &
221  g%areaT(i,j), g%areaT(i,j+1), tracer%t(i,j,:), tracer%t(i,j+1,:), ppoly0_coefs(i,j,:,:), &
222  ppoly0_coefs(i,j+1,:,:), ppoly0_e(i,j,:,:), ppoly0_e(i,j+1,:,:), remap_method, coef_y(i,j), vflx(i,j,:))
223  endif
224  enddo
225  enddo
226  endif
227 
228  ! Update the tracer fluxes
229  do k=1,gv%ke ; do j=g%jsc,g%jec ; do i=g%isc,g%iec
230  if (g%mask2dT(i,j)>0.) then
231  tracer%t(i,j,k) = tracer%t(i,j,k) + (( (uflx(i-1,j,k)-uflx(i,j,k)) ) + ( (vflx(i,j-1,k)-vflx(i,j,k) ) ))* &
232  (g%IareaT(i,j)/( h(i,j,k) + gv%H_subroundoff))
233 
234  if (tracer%id_lbdxy_conc > 0 .or. tracer%id_lbdxy_cont > 0 .or. tracer%id_lbdxy_cont_2d > 0 ) then
235  tendency(i,j,k) = (( (uflx(i-1,j,k)-uflx(i,j,k)) ) + ( (vflx(i,j-1,k)-vflx(i,j,k) ) )) * g%IareaT(i,j) * idt
236  endif
237 
238  endif
239  enddo ; enddo ; enddo
240 
241  ! Post the tracer diagnostics
242  if (tracer%id_lbd_dfx>0) call post_data(tracer%id_lbd_dfx, uflx*idt, cs%diag)
243  if (tracer%id_lbd_dfy>0) call post_data(tracer%id_lbd_dfy, vflx*idt, cs%diag)
244  if (tracer%id_lbd_dfx_2d>0) then
245  uwork_2d(:,:) = 0.
246  do k=1,gv%ke; do j=g%jsc,g%jec; do i=g%isc-1,g%iec
247  uwork_2d(i,j) = uwork_2d(i,j) + (uflx(i,j,k) * idt)
248  enddo; enddo; enddo
249  call post_data(tracer%id_lbd_dfx_2d, uwork_2d, cs%diag)
250  endif
251 
252  if (tracer%id_lbd_dfy_2d>0) then
253  vwork_2d(:,:) = 0.
254  do k=1,gv%ke; do j=g%jsc-1,g%jec; do i=g%isc,g%iec
255  vwork_2d(i,j) = vwork_2d(i,j) + (vflx(i,j,k) * idt)
256  enddo; enddo; enddo
257  call post_data(tracer%id_lbd_dfy_2d, vwork_2d, cs%diag)
258  endif
259 
260  ! post tendency of tracer content
261  if (tracer%id_lbdxy_cont > 0) then
262  call post_data(tracer%id_lbdxy_cont, tendency(:,:,:), cs%diag)
263  endif
264 
265  ! post depth summed tendency for tracer content
266  if (tracer%id_lbdxy_cont_2d > 0) then
267  tendency_2d(:,:) = 0.
268  do j = g%jsc,g%jec ; do i = g%isc,g%iec
269  do k = 1, gv%ke
270  tendency_2d(i,j) = tendency_2d(i,j) + tendency(i,j,k)
271  enddo
272  enddo ; enddo
273  call post_data(tracer%id_lbdxy_cont_2d, tendency_2d(:,:), cs%diag)
274  endif
275 
276  ! post tendency of tracer concentration; this step must be
277  ! done after posting tracer content tendency, since we alter
278  ! the tendency array.
279  if (tracer%id_lbdxy_conc > 0) then
280  do k = 1, gv%ke ; do j = g%jsc,g%jec ; do i = g%isc,g%iec
281  tendency(i,j,k) = tendency(i,j,k) / ( h(i,j,k) + gv%H_subroundoff )
282  enddo ; enddo ; enddo
283  call post_data(tracer%id_lbdxy_conc, tendency, cs%diag)
284  endif
285 
286  enddo
287 
288 end subroutine lateral_boundary_diffusion
289 
290 !< Calculate bulk layer value of a scalar quantity as the thickness weighted average
291 real function bulk_average(boundary, nk, deg, h, hBLT, phi, ppoly0_E, ppoly0_coefs, method, k_top, zeta_top, k_bot, &
292  zeta_bot)
293  integer :: boundary !< SURFACE or BOTTOM [nondim]
294  integer :: nk !< Number of layers [nondim]
295  integer :: deg !< Degree of polynomial [nondim]
296  real, dimension(nk) :: h !< Layer thicknesses [m]
297  real :: hblt !< Depth of the boundary layer [m]
298  real, dimension(nk) :: phi !< Scalar quantity
299  real, dimension(nk,2) :: ppoly0_e(:,:) !< Edge value of polynomial
300  real, dimension(nk,deg+1) :: ppoly0_coefs(:,:) !< Coefficients of polynomial
301  integer :: method !< Remapping scheme to use
302 
303  integer :: k_top !< Index of the first layer within the boundary
304  real :: zeta_top !< Fraction of the layer encompassed by the bottom boundary layer
305  !! (0 if none, 1. if all). For the surface, this is always 0. because
306  !! integration starts at the surface [nondim]
307  integer :: k_bot !< Index of the last layer within the boundary
308  real :: zeta_bot !< Fraction of the layer encompassed by the surface boundary layer
309  !! (0 if none, 1. if all). For the bottom boundary layer, this is always 1.
310  !! because integration starts at the bottom [nondim]
311  ! Local variables
312  real :: htot !< Running sum of the thicknesses (top to bottom)
313  integer :: k !< k indice
314 
315 
316  htot = 0.
317  bulk_average = 0.
318  if (hblt == 0.) return
319  if (boundary == surface) then
320  htot = (h(k_bot) * zeta_bot)
321  bulk_average = average_value_ppoly( nk, phi, ppoly0_e, ppoly0_coefs, method, k_bot, 0., zeta_bot) * htot
322  do k = k_bot-1,1,-1
323  bulk_average = bulk_average + phi(k)*h(k)
324  htot = htot + h(k)
325  enddo
326  elseif (boundary == bottom) then
327  htot = (h(k_top) * zeta_top)
328  ! (note 1-zeta_top because zeta_top is the fraction of the layer)
329  bulk_average = average_value_ppoly( nk, phi, ppoly0_e, ppoly0_coefs, method, k_top, (1.-zeta_top), 1.) * htot
330  do k = k_top+1,nk
331  bulk_average = bulk_average + phi(k)*h(k)
332  htot = htot + h(k)
333  enddo
334  else
335  call mom_error(fatal, "bulk_average: a valid boundary type must be provided.")
336  endif
337 
338  bulk_average = bulk_average / hblt
339 
340 end function bulk_average
341 
342 !> Calculate the harmonic mean of two quantities
343 !! See \ref section_harmonic_mean.
344 real function harmonic_mean(h1,h2)
345  real :: h1 !< Scalar quantity
346  real :: h2 !< Scalar quantity
347  if (h1 + h2 == 0.) then
348  harmonic_mean = 0.
349  else
350  harmonic_mean = 2.*(h1*h2)/(h1+h2)
351  endif
352 end function harmonic_mean
353 
354 !> Find the k-index range corresponding to the layers that are within the boundary-layer region
355 subroutine boundary_k_range(boundary, nk, h, hbl, k_top, zeta_top, k_bot, zeta_bot)
356  integer, intent(in ) :: boundary !< SURFACE or BOTTOM [nondim]
357  integer, intent(in ) :: nk !< Number of layers [nondim]
358  real, dimension(nk), intent(in ) :: h !< Layer thicknesses of the column [m]
359  real, intent(in ) :: hbl !< Thickness of the boundary layer [m]
360  !! If surface, with respect to zbl_ref = 0.
361  !! If bottom, with respect to zbl_ref = SUM(h)
362  integer, intent( out) :: k_top !< Index of the first layer within the boundary
363  real, intent( out) :: zeta_top !< Distance from the top of a layer to the intersection of the
364  !! top extent of the boundary layer (0 at top, 1 at bottom) [nondim]
365  integer, intent( out) :: k_bot !< Index of the last layer within the boundary
366  real, intent( out) :: zeta_bot !< Distance of the lower layer to the boundary layer depth
367  !! (0 at top, 1 at bottom) [nondim]
368  ! Local variables
369  real :: htot
370  integer :: k
371  ! Surface boundary layer
372  if ( boundary == surface ) then
373  k_top = 1
374  zeta_top = 0.
375  htot = 0.
376  k_bot = 1
377  zeta_bot = 0.
378  if (hbl == 0.) return
379  if (hbl >= sum(h(:))) then
380  k_bot = nk
381  zeta_bot = 0.
382  return
383  endif
384  do k=1,nk
385  htot = htot + h(k)
386  if ( htot >= hbl) then
387  k_bot = k
388  zeta_bot = 1 - (htot - hbl)/h(k)
389  return
390  endif
391  enddo
392  ! Bottom boundary layer
393  elseif ( boundary == bottom ) then
394  k_top = nk
395  zeta_top = 1.
396  k_bot = nk
397  zeta_bot = 1.
398  htot = 0.
399  if (hbl == 0.) return
400  if (hbl >= sum(h(:))) then
401  k_top = 1
402  zeta_top = 0.
403  return
404  endif
405  do k=nk,1,-1
406  htot = htot + h(k)
407  if (htot >= hbl) then
408  k_top = k
409  zeta_top = 1 - (htot - hbl)/h(k)
410  return
411  endif
412  enddo
413  else
414  call mom_error(fatal,"Houston, we've had a problem in boundary_k_range")
415  endif
416 
417 end subroutine boundary_k_range
418 
419 
420 !> Calculate the lateral boundary diffusive fluxes using the layer by layer method.
421 !! See \ref section_method2
422 subroutine fluxes_layer_method(boundary, nk, deg, h_L, h_R, hbl_L, hbl_R, area_L, area_R, phi_L, phi_R, &
423  ppoly0_coefs_L, ppoly0_coefs_R, ppoly0_E_L, ppoly0_E_R, method, khtr_u, F_layer)
424 
425  integer, intent(in ) :: boundary !< Which boundary layer SURFACE or BOTTOM [nondim]
426  integer, intent(in ) :: nk !< Number of layers [nondim]
427  integer, intent(in ) :: deg !< order of the polynomial reconstruction [nondim]
428  real, dimension(nk), intent(in ) :: h_L !< Layer thickness (left) [m]
429  real, dimension(nk), intent(in ) :: h_R !< Layer thickness (right) [m]
430  real, intent(in ) :: hbl_L !< Thickness of the boundary boundary
431  !! layer (left) [m]
432  real, intent(in ) :: hbl_R !< Thickness of the boundary boundary
433  !! layer (right) [m]
434  real, intent(in ) :: area_L !< Area of the horizontal grid (left) [m^2]
435  real, intent(in ) :: area_R !< Area of the horizontal grid (right) [m^2]
436  real, dimension(nk), intent(in ) :: phi_L !< Tracer values (left) [conc]
437  real, dimension(nk), intent(in ) :: phi_R !< Tracer values (right) [conc]
438  real, dimension(nk,deg+1), intent(in ) :: ppoly0_coefs_L !< Tracer reconstruction (left) [conc]
439  real, dimension(nk,deg+1), intent(in ) :: ppoly0_coefs_R !< Tracer reconstruction (right) [conc]
440  real, dimension(nk,2), intent(in ) :: ppoly0_E_L !< Polynomial edge values (left) [ nondim ]
441  real, dimension(nk,2), intent(in ) :: ppoly0_E_R !< Polynomial edge values (right) [ nondim ]
442  integer, intent(in ) :: method !< Method of polynomial integration [ nondim ]
443  real, intent(in ) :: khtr_u !< Horizontal diffusivities times delta t at U-point [m^2]
444  real, dimension(nk), intent( out) :: F_layer !< Layerwise diffusive flux at U- or V-point [m^3 conc]
445 
446  ! Local variables
447  real, dimension(nk) :: h_means !< Calculate the layer-wise harmonic means [m]
448  real :: khtr_avg !< Thickness-weighted diffusivity at the u-point [m^2 s^-1]
449  !! This is just to remind developers that khtr_avg should be
450  !! computed once khtr is 3D.
451  real :: heff !< Harmonic mean of layer thicknesses [m]
452  real :: inv_heff !< Inverse of the harmonic mean of layer thicknesses [m^[-1]
453  real :: phi_L_avg, phi_R_avg !< Bulk, thickness-weighted tracer averages (left and right column)
454  !! [conc m^-3 ]
455  real :: htot !< Total column thickness [m]
456  integer :: k, k_bot_min, k_top_max !< k-indices, min and max for top and bottom, respectively
457  integer :: k_top_L, k_bot_L !< k-indices left
458  integer :: k_top_R, k_bot_R !< k-indices right
459  real :: zeta_top_L, zeta_top_R !< distance from the top of a layer to the boundary
460  !! layer depth [nondim]
461  real :: zeta_bot_L, zeta_bot_R !< distance from the bottom of a layer to the boundary
462  !!layer depth [nondim]
463  real :: h_work_L, h_work_R !< dummy variables
464  real :: hbl_min !< minimum BLD (left and right) [m]
465 
466  f_layer(:) = 0.0
467  if (hbl_l == 0. .or. hbl_r == 0.) then
468  return
469  endif
470 
471  ! Calculate vertical indices containing the boundary layer
472  call boundary_k_range(boundary, nk, h_l, hbl_l, k_top_l, zeta_top_l, k_bot_l, zeta_bot_l)
473  call boundary_k_range(boundary, nk, h_r, hbl_r, k_top_r, zeta_top_r, k_bot_r, zeta_bot_r)
474 
475  if (boundary == surface) then
476  k_bot_min = min(k_bot_l, k_bot_r)
477  ! make sure left and right k indices span same range
478  if (k_bot_min .ne. k_bot_l) then
479  k_bot_l = k_bot_min
480  zeta_bot_l = 1.0
481  endif
482  if (k_bot_min .ne. k_bot_r) then
483  k_bot_r= k_bot_min
484  zeta_bot_r = 1.0
485  endif
486 
487  h_work_l = (h_l(k_bot_l) * zeta_bot_l)
488  h_work_r = (h_r(k_bot_r) * zeta_bot_r)
489 
490  phi_l_avg = average_value_ppoly( nk, phi_l, ppoly0_e_l, ppoly0_coefs_l, method, k_bot_l, 0., zeta_bot_l)
491  phi_r_avg = average_value_ppoly( nk, phi_r, ppoly0_e_r, ppoly0_coefs_r, method, k_bot_r, 0., zeta_bot_r)
492  heff = harmonic_mean(h_work_l, h_work_r)
493  ! tracer flux where the minimum BLD intersets layer
494  ! GMM, khtr_avg should be computed once khtr is 3D
495  f_layer(k_bot_min) = -(heff * khtr_u) * (phi_r_avg - phi_l_avg)
496 
497  do k = k_bot_min-1,1,-1
498  heff = harmonic_mean(h_l(k), h_r(k))
499  f_layer(k) = -(heff * khtr_u) * (phi_r(k) - phi_l(k))
500  enddo
501  endif
502 
503  if (boundary == bottom) then
504  k_top_max = max(k_top_l, k_top_r)
505  ! make sure left and right k indices span same range
506  if (k_top_max .ne. k_top_l) then
507  k_top_l = k_top_max
508  zeta_top_l = 1.0
509  endif
510  if (k_top_max .ne. k_top_r) then
511  k_top_r= k_top_max
512  zeta_top_r = 1.0
513  endif
514 
515  h_work_l = (h_l(k_top_l) * zeta_top_l)
516  h_work_r = (h_r(k_top_r) * zeta_top_r)
517 
518  phi_l_avg = average_value_ppoly( nk, phi_l, ppoly0_e_l, ppoly0_coefs_l, method, k_top_l, 1.0-zeta_top_l, 1.0)
519  phi_r_avg = average_value_ppoly( nk, phi_r, ppoly0_e_r, ppoly0_coefs_r, method, k_top_r, 1.0-zeta_top_r, 1.0)
520  heff = harmonic_mean(h_work_l, h_work_r)
521 
522  ! tracer flux where the minimum BLD intersets layer
523  f_layer(k_top_max) = (-heff * khtr_u) * (phi_r_avg - phi_l_avg)
524 
525  do k = k_top_max+1,nk
526  heff = harmonic_mean(h_l(k), h_r(k))
527  f_layer(k) = -(heff * khtr_u) * (phi_r(k) - phi_l(k))
528  enddo
529  endif
530 
531 end subroutine fluxes_layer_method
532 
533 !> Apply the lateral boundary diffusive fluxes calculated from a 'bulk model'
534 !! See \ref section_method1
535 subroutine fluxes_bulk_method(boundary, nk, deg, h_L, h_R, hbl_L, hbl_R, area_L, area_R, phi_L, phi_R, ppoly0_coefs_L, &
536  ppoly0_coefs_R, ppoly0_E_L, ppoly0_E_R, method, khtr_u, F_bulk, F_layer, F_limit)
537 
538  integer, intent(in ) :: boundary !< Which boundary layer SURFACE or BOTTOM [nondim]
539  integer, intent(in ) :: nk !< Number of layers [nondim]
540  integer, intent(in ) :: deg !< order of the polynomial reconstruction [nondim]
541  real, dimension(nk), intent(in ) :: h_L !< Layer thickness (left) [m]
542  real, dimension(nk), intent(in ) :: h_R !< Layer thickness (right) [m]
543  real, intent(in ) :: hbl_L !< Thickness of the boundary boundary
544  !! layer (left) [m]
545  real, intent(in ) :: hbl_R !< Thickness of the boundary boundary
546  !! layer (left) [m]
547  real, intent(in ) :: area_L !< Area of the horizontal grid (left) [m^2]
548  real, intent(in ) :: area_R !< Area of the horizontal grid (right) [m^2]
549  real, dimension(nk), intent(in ) :: phi_L !< Tracer values (left) [conc]
550  real, dimension(nk), intent(in ) :: phi_R !< Tracer values (right) [conc]
551  real, dimension(nk,deg+1), intent(in ) :: ppoly0_coefs_L !< Tracer reconstruction (left) [conc]
552  real, dimension(nk,deg+1), intent(in ) :: ppoly0_coefs_R !< Tracer reconstruction (right) [conc]
553  real, dimension(nk,2), intent(in ) :: ppoly0_E_L !< Polynomial edge values (left) [nondim]
554  real, dimension(nk,2), intent(in ) :: ppoly0_E_R !< Polynomial edge values (right) [nondim]
555  integer, intent(in ) :: method !< Method of polynomial integration [nondim]
556  real, intent(in ) :: khtr_u !< Horizontal diffusivities times delta t at U-point [m^2]
557  real, intent( out) :: F_bulk !< The bulk mixed layer lateral flux [m^3 conc]
558  real, dimension(nk), intent( out) :: F_layer !< Layerwise diffusive flux at U-point [m^3 conc]
559  real, optional, dimension(nk), intent( out) :: F_limit !< The amount of flux not applied due to limiter
560  !! F_layer(k) - F_max [m^3 conc]
561  ! Local variables
562  real, dimension(nk) :: h_means !< Calculate the layer-wise harmonic means [m]
563  real :: khtr_avg !< Thickness-weighted diffusivity at the u-point [m^2 s^-1]
564  !! This is just to remind developers that khtr_avg should be
565  !! computed once khtr is 3D.
566  real :: heff !< Harmonic mean of layer thicknesses [m]
567  real :: inv_heff !< Inverse of the harmonic mean of layer thicknesses [m^[-1]
568  real :: phi_L_avg, phi_R_avg !< Bulk, thickness-weighted tracer averages (left and right column)
569  !! [conc m^-3 ]
570  real :: htot ! Total column thickness [m]
571  integer :: k, k_min, k_max !< k-indices, min and max for top and bottom, respectively
572  integer :: k_top_L, k_bot_L !< k-indices left
573  integer :: k_top_R, k_bot_R !< k-indices right
574  real :: zeta_top_L, zeta_top_R !< distance from the top of a layer to the
575  !! boundary layer [nondim]
576  real :: zeta_bot_L, zeta_bot_R !< distance from the bottom of a layer to the
577  !! boundary layer [nondim]
578  real :: h_work_L, h_work_R !< dummy variables
579  real :: F_max !< The maximum amount of flux that can leave a
580  !! cell [m^3 conc]
581  logical :: limited !< True if the flux limiter was applied
582  real :: hfrac, F_bulk_remain
583 
584  if (hbl_l == 0. .or. hbl_r == 0.) then
585  f_bulk = 0.
586  f_layer(:) = 0.
587  return
588  endif
589 
590  ! Calculate vertical indices containing the boundary layer
591  call boundary_k_range(boundary, nk, h_l, hbl_l, k_top_l, zeta_top_l, k_bot_l, zeta_bot_l)
592  call boundary_k_range(boundary, nk, h_r, hbl_r, k_top_r, zeta_top_r, k_bot_r, zeta_bot_r)
593 
594  ! Calculate bulk averages of various quantities
595  phi_l_avg = bulk_average(boundary, nk, deg, h_l, hbl_l, phi_l, ppoly0_e_l, ppoly0_coefs_l, method, k_top_l, &
596  zeta_top_l, k_bot_l, zeta_bot_l)
597  phi_r_avg = bulk_average(boundary, nk, deg, h_r, hbl_r, phi_r, ppoly0_e_r, ppoly0_coefs_r, method, k_top_r, &
598  zeta_top_r, k_bot_r, zeta_bot_r)
599 
600  ! Calculate the 'bulk' diffusive flux from the bulk averaged quantities
601  ! GMM, khtr_avg should be computed once khtr is 3D
602  heff = harmonic_mean(hbl_l, hbl_r)
603  f_bulk = -(khtr_u * heff) * (phi_r_avg - phi_l_avg)
604  f_bulk_remain = f_bulk
605  ! Calculate the layerwise sum of the vertical effective thickness. This is different than the heff calculated
606  ! above, but is used as a way to decompose the fluxes onto the individual layers
607  h_means(:) = 0.
608 
609  if (boundary == surface) then
610  k_min = min(k_bot_l, k_bot_r)
611 
612  ! left hand side
613  if (k_bot_l == k_min) then
614  h_work_l = h_l(k_min) * zeta_bot_l
615  else
616  h_work_l = h_l(k_min)
617  endif
618 
619  ! right hand side
620  if (k_bot_r == k_min) then
621  h_work_r = h_r(k_min) * zeta_bot_r
622  else
623  h_work_r = h_r(k_min)
624  endif
625 
626  h_means(k_min) = harmonic_mean(h_work_l,h_work_r)
627 
628  do k=1,k_min-1
629  h_means(k) = harmonic_mean(h_l(k),h_r(k))
630  enddo
631 
632  elseif (boundary == bottom) then
633  k_max = max(k_top_l, k_top_r)
634  ! left hand side
635  if (k_top_l == k_max) then
636  h_work_l = h_l(k_max) * zeta_top_l
637  else
638  h_work_l = h_l(k_max)
639  endif
640 
641  ! right hand side
642  if (k_top_r == k_max) then
643  h_work_r = h_r(k_max) * zeta_top_r
644  else
645  h_work_r = h_r(k_max)
646  endif
647 
648  h_means(k_max) = harmonic_mean(h_work_l,h_work_r)
649 
650  do k=nk,k_max+1,-1
651  h_means(k) = harmonic_mean(h_l(k),h_r(k))
652  enddo
653  endif
654 
655  if ( sum(h_means) == 0. ) then
656  return
657  ! Decompose the bulk flux onto the individual layers
658  else
659  ! Initialize remaining thickness
660  inv_heff = 1./sum(h_means)
661  do k=1,nk
662  if (h_means(k) > 0.) then
663  hfrac = h_means(k)*inv_heff
664  f_layer(k) = f_bulk * hfrac
665  ! limit the flux to 0.2 of the tracer *gradient*
666  ! Why 0.2?
667  ! t=0 t=inf
668  ! 0 .2
669  ! 0 1 0 .2.2.2
670  ! 0 .2
671  !
672  f_max = -0.2 * ((area_r*(phi_r(k)*h_r(k)))-(area_l*(phi_l(k)*h_r(k))))
673 
674  ! check if bulk flux (or F_layer) and F_max have same direction
675  if ( sign(1.,f_bulk) == sign(1., f_max)) then
676  ! Distribute bulk flux onto layers
677  if ( ((boundary == surface) .and. (k == k_min)) .or. ((boundary == bottom) .and. (k == nk)) ) then
678  f_layer(k) = f_bulk_remain ! GMM, are not using F_bulk_remain for now. Should we keep it?
679  endif
680  f_bulk_remain = f_bulk_remain - f_layer(k)
681 
682  ! Apply flux limiter calculated above
683  if (f_max >= 0.) then
684  limited = f_layer(k) > f_max
685  f_layer(k) = min(f_layer(k),f_max)
686  else
687  limited = f_layer(k) < f_max
688  f_layer(k) = max(f_layer(k),f_max)
689  endif
690 
691  ! GMM, again we are not using F_limit. Should we delete it?
692  if (PRESENT(f_limit)) then
693  if (limited) then
694  f_limit(k) = f_layer(k) - f_max
695  else
696  f_limit(k) = 0.
697  endif
698  endif
699  else
700  ! do not apply a flux on this layer
701  f_bulk_remain = f_bulk_remain - f_layer(k)
702  f_layer(k) = 0.
703  endif
704  else
705  f_layer(k) = 0.
706  endif
707  enddo
708  endif
709 
710 end subroutine fluxes_bulk_method
711 
712 !> Unit tests for near-boundary horizontal mixing
713 logical function near_boundary_unit_tests( verbose )
714  logical, intent(in) :: verbose !< If true, output additional information for debugging unit tests
715 
716  ! Local variables
717  integer, parameter :: nk = 2 ! Number of layers
718  integer, parameter :: deg = 1 ! Degree of reconstruction (linear here)
719  integer, parameter :: method = 1 ! Method used for integrating polynomials
720  real, dimension(nk) :: phi_l, phi_r ! Tracer values (left and right column) [ nondim m^-3 ]
721  real, dimension(nk) :: phi_l_avg, phi_r_avg ! Bulk, thickness-weighted tracer averages (left and right column)
722  real, dimension(nk,deg+1) :: phi_pp_l, phi_pp_r ! Coefficients for the linear pseudo-reconstructions
723  ! [ nondim m^-3 ]
724 
725  real, dimension(nk,2) :: ppoly0_e_l, ppoly0_e_r! Polynomial edge values (left and right) [concentration]
726  real, dimension(nk) :: h_l, h_r ! Layer thickness (left and right) [m]
727  real :: khtr_u ! Horizontal diffusivities at U-point [m^2 s^-1]
728  real :: hbl_l, hbl_r ! Depth of the boundary layer (left and right) [m]
729  real :: f_bulk ! Total diffusive flux across the U point [nondim s^-1]
730  real, dimension(nk) :: f_layer ! Diffusive flux within each layer at U-point [nondim s^-1]
731  real :: h_u, hblt_u ! Thickness at the u-point [m]
732  real :: khtr_avg ! Thickness-weighted diffusivity at the u-point [m^2 s^-1]
733  real :: heff ! Harmonic mean of layer thicknesses [m]
734  real :: inv_heff ! Inverse of the harmonic mean of layer thicknesses [m^[-1]
735  character(len=120) :: test_name ! Title of the unit test
736  integer :: k_top ! Index of cell containing top of boundary
737  real :: zeta_top ! Nondimension position
738  integer :: k_bot ! Index of cell containing bottom of boundary
739  real :: zeta_bot ! Nondimension position
740  real :: area_l,area_r ! Area of grid cell [m^2]
741  area_l = 1.; area_r = 1. ! Set to unity for all unit tests
742 
743  near_boundary_unit_tests = .false.
744 
745  ! Unit tests for boundary_k_range
746  test_name = 'Surface boundary spans the entire top cell'
747  h_l = (/5.,5./)
748  call boundary_k_range(surface, nk, h_l, 5., k_top, zeta_top, k_bot, zeta_bot)
749  near_boundary_unit_tests = test_boundary_k_range(k_top, zeta_top, k_bot, zeta_bot, 1, 0., 1, 1., test_name, verbose)
750 
751  test_name = 'Surface boundary spans the entire column'
752  h_l = (/5.,5./)
753  call boundary_k_range(surface, nk, h_l, 10., k_top, zeta_top, k_bot, zeta_bot)
754  near_boundary_unit_tests = test_boundary_k_range(k_top, zeta_top, k_bot, zeta_bot, 1, 0., 2, 0., test_name, verbose)
755 
756  test_name = 'Bottom boundary spans the entire bottom cell'
757  h_l = (/5.,5./)
758  call boundary_k_range(bottom, nk, h_l, 5., k_top, zeta_top, k_bot, zeta_bot)
759  near_boundary_unit_tests = test_boundary_k_range(k_top, zeta_top, k_bot, zeta_bot, 2, 0., 2, 1., test_name, verbose)
760 
761  test_name = 'Bottom boundary spans the entire column'
762  h_l = (/5.,5./)
763  call boundary_k_range(bottom, nk, h_l, 10., k_top, zeta_top, k_bot, zeta_bot)
764  near_boundary_unit_tests = test_boundary_k_range(k_top, zeta_top, k_bot, zeta_bot, 1, 0., 2, 1., test_name, verbose)
765 
766  test_name = 'Surface boundary intersects second layer'
767  h_l = (/10.,10./)
768  call boundary_k_range(surface, nk, h_l, 17.5, k_top, zeta_top, k_bot, zeta_bot)
769  near_boundary_unit_tests = test_boundary_k_range(k_top, zeta_top, k_bot, zeta_bot, 1, 0., 2, 0.75, test_name, verbose)
770 
771  test_name = 'Surface boundary intersects first layer'
772  h_l = (/10.,10./)
773  call boundary_k_range(surface, nk, h_l, 2.5, k_top, zeta_top, k_bot, zeta_bot)
774  near_boundary_unit_tests = test_boundary_k_range(k_top, zeta_top, k_bot, zeta_bot, 1, 0., 1, 0.25, test_name, verbose)
775 
776  test_name = 'Surface boundary is deeper than column thickness'
777  h_l = (/10.,10./)
778  call boundary_k_range(surface, nk, h_l, 21.0, k_top, zeta_top, k_bot, zeta_bot)
779  near_boundary_unit_tests = test_boundary_k_range(k_top, zeta_top, k_bot, zeta_bot, 1, 0., 2, 0., test_name, verbose)
780 
781  test_name = 'Bottom boundary intersects first layer'
782  h_l = (/10.,10./)
783  call boundary_k_range(bottom, nk, h_l, 17.5, k_top, zeta_top, k_bot, zeta_bot)
784  near_boundary_unit_tests = test_boundary_k_range(k_top, zeta_top, k_bot, zeta_bot, 1, 0.75, 2, 1., test_name, verbose)
785 
786  test_name = 'Bottom boundary intersects second layer'
787  h_l = (/10.,10./)
788  call boundary_k_range(bottom, nk, h_l, 2.5, k_top, zeta_top, k_bot, zeta_bot)
789  near_boundary_unit_tests = test_boundary_k_range(k_top, zeta_top, k_bot, zeta_bot, 2, 0.25, 2, 1., test_name, verbose)
790 
791  ! All cases in this section have hbl which are equal to the column thicknesses
792  test_name = 'Equal hbl and same layer thicknesses (gradient from right to left)'
793  hbl_l = 10; hbl_r = 10
794  h_l = (/5.,5./) ; h_r = (/5.,5./)
795  phi_l = (/0.,0./) ; phi_r = (/1.,1./)
796  phi_pp_l(1,1) = 0.; phi_pp_l(1,2) = 0.
797  phi_pp_l(2,1) = 0.; phi_pp_l(2,2) = 0.
798  phi_pp_r(1,1) = 1.; phi_pp_r(1,2) = 0.
799  phi_pp_r(2,1) = 1.; phi_pp_r(2,2) = 0.
800  ppoly0_e_l(1,1) = 0.; ppoly0_e_l(1,2) = 0.
801  ppoly0_e_l(2,1) = 0.; ppoly0_e_l(2,2) = 0.
802  ppoly0_e_r(1,1) = 1.; ppoly0_e_r(1,2) = 1.
803  ppoly0_e_r(2,1) = 1.; ppoly0_e_r(2,2) = 1.
804  khtr_u = 1.
805  call fluxes_bulk_method(surface, nk, deg, h_l, h_r, hbl_l, hbl_r, area_l, area_r, phi_l, phi_r, phi_pp_l, phi_pp_r, &
806  ppoly0_e_l, ppoly0_e_r, method, khtr_u, f_bulk, f_layer)
807  near_boundary_unit_tests = test_layer_fluxes( verbose, nk, test_name, f_layer, (/-5.0,-5.0/) )
808 
809  test_name = 'Equal hbl and same layer thicknesses (gradient from left to right)'
810  hbl_l = 10.; hbl_r = 10.
811  h_l = (/5.,5./) ; h_r = (/5.,5./)
812  phi_l = (/1.,1./) ; phi_r = (/0.,0./)
813  phi_pp_l(1,1) = 1.; phi_pp_l(1,2) = 0.
814  phi_pp_l(2,1) = 1.; phi_pp_l(2,2) = 0.
815  phi_pp_r(1,1) = 0.; phi_pp_r(1,2) = 0.
816  phi_pp_r(2,1) = 0.; phi_pp_r(2,2) = 0.
817  ppoly0_e_l(1,1) = 1.; ppoly0_e_l(1,2) = 1.
818  ppoly0_e_l(2,1) = 1.; ppoly0_e_l(2,2) = 1.
819  ppoly0_e_r(1,1) = 0.; ppoly0_e_r(1,2) = 0.
820  ppoly0_e_r(2,1) = 0.; ppoly0_e_r(2,2) = 0.
821  khtr_u = 1.
822  call fluxes_bulk_method(surface, nk, deg, h_l, h_r, hbl_l, hbl_r, area_l, area_r, phi_l, phi_r, phi_pp_l, phi_pp_r,&
823  ppoly0_e_l, ppoly0_e_r, method, khtr_u, f_bulk, f_layer)
824  near_boundary_unit_tests = test_layer_fluxes( verbose, nk, test_name, f_layer, (/5.0,5.0/) )
825 
826  test_name = 'Equal hbl and same layer thicknesses (no gradient)'
827  hbl_l = 10; hbl_r = 10
828  h_l = (/5.,5./) ; h_r = (/5.,5./)
829  phi_l = (/1.,1./) ; phi_r = (/1.,1./)
830  phi_pp_l(1,1) = 1.; phi_pp_l(1,2) = 0.
831  phi_pp_l(2,1) = 1.; phi_pp_l(2,2) = 0.
832  phi_pp_r(1,1) = 1.; phi_pp_r(1,2) = 0.
833  phi_pp_r(2,1) = 1.; phi_pp_r(2,2) = 0.
834  ppoly0_e_l(1,1) = 1.; ppoly0_e_l(1,2) = 0.
835  ppoly0_e_l(2,1) = 1.; ppoly0_e_l(2,2) = 0.
836  ppoly0_e_r(1,1) = 1.; ppoly0_e_r(1,2) = 1.
837  ppoly0_e_r(2,1) = 1.; ppoly0_e_r(2,2) = 1.
838  khtr_u = 1.
839  call fluxes_bulk_method(surface, nk, deg, h_l, h_r, hbl_l, hbl_r, area_l, area_r, phi_l, phi_r, phi_pp_l, phi_pp_r,&
840  ppoly0_e_l, ppoly0_e_r, method, khtr_u, f_bulk, f_layer)
841  near_boundary_unit_tests = test_layer_fluxes( verbose, nk, test_name, f_layer, (/0.0,0.0/) )
842 
843  test_name = 'Equal hbl and different layer thicknesses (gradient right to left)'
844  hbl_l = 16.; hbl_r = 16.
845  h_l = (/10.,6./) ; h_r = (/6.,10./)
846  phi_l = (/0.,0./) ; phi_r = (/1.,1./)
847  phi_pp_l(1,1) = 0.; phi_pp_l(1,2) = 0.
848  phi_pp_l(2,1) = 0.; phi_pp_l(2,2) = 0.
849  phi_pp_r(1,1) = 1.; phi_pp_r(1,2) = 0.
850  phi_pp_r(2,1) = 1.; phi_pp_r(2,2) = 0.
851  ppoly0_e_l(1,1) = 0.; ppoly0_e_l(1,2) = 0.
852  ppoly0_e_l(2,1) = 0.; ppoly0_e_l(2,2) = 0.
853  ppoly0_e_r(1,1) = 1.; ppoly0_e_r(1,2) = 1.
854  ppoly0_e_r(2,1) = 1.; ppoly0_e_r(2,2) = 1.
855  khtr_u = 1.
856  call fluxes_bulk_method(surface, nk, deg, h_l, h_r, hbl_l, hbl_r, area_l, area_r, phi_l, phi_r, phi_pp_l, phi_pp_r,&
857  ppoly0_e_l, ppoly0_e_r, method, khtr_u, f_bulk, f_layer)
858  near_boundary_unit_tests = test_layer_fluxes( verbose, nk, test_name, f_layer, (/-8.0,-8.0/) )
859 
860  test_name = 'Equal hbl and same layer thicknesses (diagonal tracer values)'
861  hbl_l = 10.; hbl_r = 10.
862  h_l = (/5.,5./) ; h_r = (/5.,5./)
863  phi_l = (/1.,0./) ; phi_r = (/0.,1./)
864  phi_pp_l(1,1) = 1.; phi_pp_l(1,2) = 0.
865  phi_pp_l(2,1) = 0.; phi_pp_l(2,2) = 0.
866  phi_pp_r(1,1) = 0.; phi_pp_r(1,2) = 0.
867  phi_pp_r(2,1) = 1.; phi_pp_r(2,2) = 0.
868  ppoly0_e_l(1,1) = 1.; ppoly0_e_l(1,2) = 1.
869  ppoly0_e_l(2,1) = 0.; ppoly0_e_l(2,2) = 0.
870  ppoly0_e_r(1,1) = 0.; ppoly0_e_r(1,2) = 0.
871  ppoly0_e_r(2,1) = 1.; ppoly0_e_r(2,2) = 1.
872  khtr_u = 1.
873  call fluxes_bulk_method(surface, nk, deg, h_l, h_r, hbl_l, hbl_r, area_l, area_r, phi_l, phi_r, phi_pp_l, phi_pp_r,&
874  ppoly0_e_l, ppoly0_e_r, method, khtr_u, f_bulk, f_layer)
875  near_boundary_unit_tests = test_layer_fluxes( verbose, nk, test_name, f_layer, (/0.0,0.0/) )
876 
877  test_name = 'Different hbl and different layer thicknesses (gradient from right to left)'
878  hbl_l = 12; hbl_r = 20
879  h_l = (/6.,6./) ; h_r = (/10.,10./)
880  phi_l = (/0.,0./) ; phi_r = (/1.,1./)
881  phi_pp_l(1,1) = 0.; phi_pp_l(1,2) = 0.
882  phi_pp_l(2,1) = 0.; phi_pp_l(2,2) = 0.
883  phi_pp_r(1,1) = 1.; phi_pp_r(1,2) = 0.
884  phi_pp_r(2,1) = 1.; phi_pp_r(2,2) = 0.
885  ppoly0_e_l(1,1) = 0.; ppoly0_e_l(1,2) = 0.
886  ppoly0_e_l(2,1) = 0.; ppoly0_e_l(2,2) = 0.
887  ppoly0_e_r(1,1) = 1.; ppoly0_e_r(1,2) = 1.
888  ppoly0_e_r(2,1) = 1.; ppoly0_e_r(2,2) = 1.
889  khtr_u = 1.
890  call fluxes_bulk_method(surface, nk, deg, h_l, h_r, hbl_l, hbl_r, area_l, area_r, phi_l, phi_r, phi_pp_l, phi_pp_r,&
891  ppoly0_e_l, ppoly0_e_r, method, khtr_u, f_bulk, f_layer)
892  near_boundary_unit_tests = test_layer_fluxes( verbose, nk, test_name, f_layer, (/-7.5,-7.5/) )
893 
894  ! Cases where hbl < column thickness (polynomial coefficients specified for pseudo-linear reconstruction)
895 
896  test_name = 'hbl < column thickness, hbl same, constant concentration each column'
897  hbl_l = 2; hbl_r = 2
898  h_l = (/1.,2./) ; h_r = (/1.,2./)
899  phi_l = (/0.,0./) ; phi_r = (/1.,1./)
900  phi_pp_l(1,1) = 0.; phi_pp_l(1,2) = 0.
901  phi_pp_l(2,1) = 0.; phi_pp_l(2,2) = 0.
902  phi_pp_r(1,1) = 1.; phi_pp_r(1,2) = 0.
903  phi_pp_r(2,1) = 1.; phi_pp_r(2,2) = 0.
904  khtr_u = 1.
905  call fluxes_bulk_method(surface, nk, deg, h_l, h_r, hbl_l, hbl_r, area_l, area_r, phi_l, phi_r, phi_pp_l, phi_pp_r,&
906  ppoly0_e_l, ppoly0_e_r, method, khtr_u, f_bulk, f_layer)
907  near_boundary_unit_tests = test_layer_fluxes( verbose, nk, test_name, f_layer, (/-1.,-1./) )
908 
909  test_name = 'hbl < column thickness, hbl same, linear profile right'
910  hbl_l = 2; hbl_r = 2
911  h_l = (/1.,2./) ; h_r = (/1.,2./)
912  phi_l = (/0.,0./) ; phi_r = (/0.5,2./)
913  phi_pp_l(1,1) = 0.; phi_pp_l(1,2) = 0.
914  phi_pp_l(2,1) = 0.; phi_pp_l(2,2) = 0.
915  phi_pp_r(1,1) = 0.; phi_pp_r(1,2) = 1.
916  phi_pp_r(2,1) = 1.; phi_pp_r(2,2) = 2.
917  khtr_u = 1.
918  ppoly0_e_l(1,1) = 0.; ppoly0_e_l(1,2) = 0.
919  ppoly0_e_l(2,1) = 0.; ppoly0_e_l(2,2) = 0.
920  ppoly0_e_r(1,1) = 0.; ppoly0_e_r(1,2) = 1.
921  ppoly0_e_r(2,1) = 1.; ppoly0_e_r(2,2) = 3.
922  call fluxes_bulk_method(surface, nk, deg, h_l, h_r, hbl_l, hbl_r, area_l, area_r, phi_l, phi_r, phi_pp_l, phi_pp_r,&
923  ppoly0_e_l, ppoly0_e_r, method, khtr_u, f_bulk, f_layer)
924  near_boundary_unit_tests = test_layer_fluxes( verbose, nk, test_name, f_layer, (/-1.,-1./) )
925 
926  test_name = 'hbl < column thickness, hbl same, linear profile right, khtr=2'
927  hbl_l = 2; hbl_r = 2
928  h_l = (/1.,2./) ; h_r = (/1.,2./)
929  phi_l = (/0.,0./) ; phi_r = (/0.5,2./)
930  phi_pp_l(1,1) = 0.; phi_pp_l(1,2) = 0.
931  phi_pp_l(2,1) = 0.; phi_pp_l(2,2) = 0.
932  phi_pp_r(1,1) = 0.; phi_pp_r(1,2) = 1.
933  phi_pp_r(2,1) = 1.; phi_pp_r(2,2) = 2.
934  khtr_u = 2.
935  ppoly0_e_l(1,1) = 0.; ppoly0_e_l(1,2) = 0.
936  ppoly0_e_l(2,1) = 0.; ppoly0_e_l(2,2) = 0.
937  ppoly0_e_r(1,1) = 0.; ppoly0_e_r(1,2) = 1.
938  ppoly0_e_r(2,1) = 1.; ppoly0_e_r(2,2) = 3.
939  call fluxes_layer_method(surface, nk, deg, h_l, h_r, hbl_l, hbl_r, area_l, area_r, phi_l, phi_r, phi_pp_l, &
940  phi_pp_r, ppoly0_e_l, ppoly0_e_r, method, khtr_u, f_layer)
941  near_boundary_unit_tests = test_layer_fluxes( verbose, nk, test_name, f_layer, (/-2.,-2./) )
942 
943  ! unit tests for layer by layer method
944  test_name = 'Different hbl and different column thicknesses (gradient from right to left)'
945  hbl_l = 12; hbl_r = 20
946  h_l = (/6.,6./) ; h_r = (/10.,10./)
947  phi_l = (/0.,0./) ; phi_r = (/1.,1./)
948  phi_pp_l(1,1) = 0.; phi_pp_l(1,2) = 0.
949  phi_pp_l(2,1) = 0.; phi_pp_l(2,2) = 0.
950  phi_pp_r(1,1) = 1.; phi_pp_r(1,2) = 0.
951  phi_pp_r(2,1) = 1.; phi_pp_r(2,2) = 0.
952  ppoly0_e_l(1,1) = 0.; ppoly0_e_l(1,2) = 0.
953  ppoly0_e_l(2,1) = 0.; ppoly0_e_l(2,2) = 0.
954  ppoly0_e_r(1,1) = 1.; ppoly0_e_r(1,2) = 1.
955  ppoly0_e_r(2,1) = 1.; ppoly0_e_r(2,2) = 1.
956  khtr_u = 1.
957  call fluxes_layer_method(surface, nk, deg, h_l, h_r, hbl_l, hbl_r, area_l, area_r, phi_l, phi_r, phi_pp_l, &
958  phi_pp_r, ppoly0_e_l, ppoly0_e_r, method, khtr_u, f_layer)
959  near_boundary_unit_tests = test_layer_fluxes( verbose, nk, test_name, f_layer, (/-7.5,0.0/) )
960 
961  test_name = 'Different hbl and different column thicknesses (linear profile right)'
962 
963  hbl_l = 15; hbl_r = 6
964  h_l = (/10.,10./) ; h_r = (/12.,10./)
965  phi_l = (/0.,0./) ; phi_r = (/1.,3./)
966  phi_pp_l(1,1) = 0.; phi_pp_l(1,2) = 0.
967  phi_pp_l(2,1) = 0.; phi_pp_l(2,2) = 0.
968  phi_pp_r(1,1) = 0.; phi_pp_r(1,2) = 2.
969  phi_pp_r(2,1) = 2.; phi_pp_r(2,2) = 2.
970  ppoly0_e_l(1,1) = 0.; ppoly0_e_l(1,2) = 0.
971  ppoly0_e_l(2,1) = 0.; ppoly0_e_l(2,2) = 0.
972  ppoly0_e_r(1,1) = 0.; ppoly0_e_r(1,2) = 2.
973  ppoly0_e_r(2,1) = 2.; ppoly0_e_r(2,2) = 4.
974  khtr_u = 1.
975  call fluxes_layer_method(surface, nk, deg, h_l, h_r, hbl_l, hbl_r, area_l, area_r, phi_l, phi_r, phi_pp_l, &
976  phi_pp_r, ppoly0_e_l, ppoly0_e_r, method, khtr_u, f_layer)
977  near_boundary_unit_tests = test_layer_fluxes( verbose, nk, test_name, f_layer, (/-3.75,0.0/) )
978 end function near_boundary_unit_tests
979 
980 !> Returns true if output of near-boundary unit tests does not match correct computed values
981 !! and conditionally writes results to stream
982 logical function test_layer_fluxes(verbose, nk, test_name, F_calc, F_ans)
983  logical, intent(in) :: verbose !< If true, write results to stdout
984  character(len=80), intent(in) :: test_name !< Brief description of the unit test
985  integer, intent(in) :: nk !< Number of layers
986  real, dimension(nk), intent(in) :: f_calc !< Fluxes of the unitless tracer from the algorithm [s^-1]
987  real, dimension(nk), intent(in) :: f_ans !< Fluxes of the unitless tracer calculated by hand [s^-1]
988  ! Local variables
989  integer :: k
990  integer, parameter :: stdunit = 6
991 
992  test_layer_fluxes = .false.
993  do k=1,nk
994  if ( f_calc(k) /= f_ans(k) ) then
995  test_layer_fluxes = .true.
996  write(stdunit,*) "UNIT TEST FAILED: ", test_name
997  write(stdunit,10) k, f_calc(k), f_ans(k)
998  elseif (verbose) then
999  write(stdunit,10) k, f_calc(k), f_ans(k)
1000  endif
1001  enddo
1002 
1003 10 format("Layer=",i3," F_calc=",f20.16," F_ans",f20.16)
1004 end function test_layer_fluxes
1005 
1006 !> Return true if output of unit tests for boundary_k_range does not match answers
1007 logical function test_boundary_k_range(k_top, zeta_top, k_bot, zeta_bot, k_top_ans, zeta_top_ans,&
1008  k_bot_ans, zeta_bot_ans, test_name, verbose)
1009  integer :: k_top !< Index of cell containing top of boundary
1010  real :: zeta_top !< Nondimension position
1011  integer :: k_bot !< Index of cell containing bottom of boundary
1012  real :: zeta_bot !< Nondimension position
1013  integer :: k_top_ans !< Index of cell containing top of boundary
1014  real :: zeta_top_ans !< Nondimension position
1015  integer :: k_bot_ans !< Index of cell containing bottom of boundary
1016  real :: zeta_bot_ans !< Nondimension position
1017  character(len=80) :: test_name !< Name of the unit test
1018  logical :: verbose !< If true always print output
1019 
1020  integer, parameter :: stdunit = 6
1021 
1022  test_boundary_k_range = k_top .ne. k_top_ans
1023  test_boundary_k_range = test_boundary_k_range .or. (zeta_top .ne. zeta_top_ans)
1024  test_boundary_k_range = test_boundary_k_range .or. (k_bot .ne. k_bot_ans)
1025  test_boundary_k_range = test_boundary_k_range .or. (zeta_bot .ne. zeta_bot_ans)
1026 
1027  if (test_boundary_k_range) write(stdunit,*) "UNIT TEST FAILED: ", test_name
1028  if (test_boundary_k_range .or. verbose) then
1029  write(stdunit,20) "k_top", k_top, "k_top_ans", k_top_ans
1030  write(stdunit,20) "k_bot", k_bot, "k_bot_ans", k_bot_ans
1031  write(stdunit,30) "zeta_top", zeta_top, "zeta_top_ans", zeta_top_ans
1032  write(stdunit,30) "zeta_bot", zeta_bot, "zeta_bot_ans", zeta_bot_ans
1033  endif
1034 
1035  20 format(a,"=",i3,x,a,"=",i3)
1036  30 format(a,"=",f20.16,x,a,"=",f20.16)
1037 
1038 
1039 end function test_boundary_k_range
1040 
1041 !> \namespace mom_lateral_boundary_diffusion
1042 !!
1043 !! \section section_LBD The Lateral Boundary Diffusion (LBD) framework
1044 !!
1045 !! The LBD framework accounts for the effects of diabatic mesoscale fluxes
1046 !! within surface and bottom boundary layers. Unlike the equivalent adiabatic
1047 !! fluxes, which is applied along neutral density surfaces, LBD is purely
1048 !! horizontal.
1049 !!
1050 !! The bottom boundary layer fluxes remain to be implemented, although most
1051 !! of the steps needed to do so have already been added and tested.
1052 !!
1053 !! Boundary lateral diffusion can be applied using one of the three methods:
1054 !!
1055 !! * [Method #1: Bulk layer](@ref section_method1) (default);
1056 !! * [Method #2: Along layer](@ref section_method2);
1057 !!
1058 !! A brief summary of these methods is provided below.
1059 !!
1060 !! \subsection section_method1 Bulk layer approach (Method #1)
1061 !!
1062 !! Apply the lateral boundary diffusive fluxes calculated from a 'bulk model'.This
1063 !! is a lower order representation (Kraus-Turner like approach) which assumes that
1064 !! eddies are acting along well mixed layers (i.e., eddies do not know care about
1065 !! vertical tracer gradients within the boundary layer).
1066 !!
1067 !! Step #1: compute vertical indices containing boundary layer (boundary_k_range).
1068 !! For the TOP boundary layer, these are:
1069 !!
1070 !! k_top, k_bot, zeta_top, zeta_bot
1071 !!
1072 !! Step #2: compute bulk averages (thickness weighted) tracer averages (phi_L and phi_R),
1073 !! then calculate the bulk diffusive flux (F_{bulk}):
1074 !!
1075 !! \f[ F_{bulk} = -KHTR \times h_{eff} \times (\phi_R - \phi_L), \f]
1076 !! where h_eff is the [harmonic mean](@ref section_harmonic_mean) of the boundary layer depth
1077 !! in the left and right columns (\f[ HBL_L \f] and \f[ HBL_R \f], respectively).
1078 !!
1079 !! Step #3: decompose F_bulk onto individual layers:
1080 !!
1081 !! \f[ F_{layer}(k) = F_{bulk} \times h_{frac}(k) , \f]
1082 !!
1083 !! where h_{frac} is
1084 !!
1085 !! \f[ h_{frac}(k) = h_u(k) \times \frac{1}{\sum(h_u)}. \f]
1086 !!
1087 !! h_u is the [harmonic mean](@ref section_harmonic_mean) of thicknesses at each layer.
1088 !! Special care (layer reconstruction) must be taken at k_min = min(k_botL, k_bot_R).
1089 !!
1090 !! Step #4: limit the tracer flux so that 1) only down-gradient fluxes are applied,
1091 !! and 2) the flux cannot be larger than F_max, which is defined using the tracer
1092 !! gradient:
1093 !!
1094 !! \f[ F_{max} = -0.2 \times [(V_R(k) \times \phi_R(k)) - (V_L(k) \times \phi_L(k))], \f]
1095 !! where V is the cell volume. Why 0.2?
1096 !! t=0 t=inf
1097 !! 0 .2
1098 !! 0 1 0 .2.2.2
1099 !! 0 .2
1100 !!
1101 !! \subsection section_method2 Along layer approach (Method #2)
1102 !!
1103 !! This is a more straight forward method where diffusion is applied layer by layer using
1104 !! only information from neighboring cells.
1105 !!
1106 !! Step #1: compute vertical indices containing boundary layer (boundary_k_range).
1107 !! For the TOP boundary layer, these are:
1108 !!
1109 !! k_top, k_bot, zeta_top, zeta_bot
1110 !!
1111 !! Step #2: calculate the diffusive flux at each layer:
1112 !!
1113 !! \f[ F_{k} = -KHTR \times h_{eff}(k) \times (\phi_R(k) - \phi_L(k)), \f]
1114 !! where h_eff is the [harmonic mean](@ref section_harmonic_mean) of the layer thickness
1115 !! in the left and right columns. Special care (layer reconstruction) must be taken at
1116 !! k_min = min(k_botL, k_bot_R). This method does not require a limiter since KHTR
1117 !! is already limted based on a diffusive CFL condition prior to the call of this
1118 !! module.
1119 !!
1120 !! \subsection section_harmonic_mean Harmonic Mean
1121 !!
1122 !! The harmonic mean (HM) betwen h1 and h2 is defined as:
1123 !!
1124 !! \f[ HM = \frac{2 \times h1 \times h2}{h1 + h2} \f]
1125 !!
mom_lateral_boundary_diffusion::lateral_boundary_diffusion_cs
Sets parameters for lateral boundary mixing module.
Definition: MOM_lateral_boundary_diffusion.F90:38
mom_lateral_boundary_diffusion::lateral_boundary_diffusion
subroutine, public lateral_boundary_diffusion(G, GV, US, h, Coef_x, Coef_y, dt, Reg, CS)
Driver routine for calculating lateral diffusive fluxes near the top and bottom boundaries....
Definition: MOM_lateral_boundary_diffusion.F90:123
mom_lateral_boundary_diffusion::mdl
character(len=40) mdl
Name of this module.
Definition: MOM_lateral_boundary_diffusion.F90:55
mom_verticalgrid
Provides a transparent vertical ocean grid type and supporting routines.
Definition: MOM_verticalGrid.F90:2
mom_cvmix_kpp::kpp_get_bld
subroutine, public kpp_get_bld(CS, BLD, G)
Copies KPP surface boundary layer depth into BLD.
Definition: MOM_CVMix_KPP.F90:1469
mom_tracer_registry::tracer_type
The tracer type.
Definition: MOM_tracer_registry.F90:38
mom_file_parser::log_version
An overloaded interface to log version information about modules.
Definition: MOM_file_parser.F90:109
mom_remapping::extract_member_remapping_cs
subroutine, public extract_member_remapping_cs(CS, remapping_scheme, degree, boundary_extrapolation, check_reconstruction, check_remapping, force_bounds_in_subcell)
Definition: MOM_remapping.F90:120
mom_lateral_boundary_diffusion::surface
integer, parameter, public surface
Set a value that corresponds to the surface bopundary.
Definition: MOM_lateral_boundary_diffusion.F90:33
mom_lateral_boundary_diffusion::boundary_k_range
subroutine, public boundary_k_range(boundary, nk, h, hbl, k_top, zeta_top, k_bot, zeta_bot)
Find the k-index range corresponding to the layers that are within the boundary-layer region.
Definition: MOM_lateral_boundary_diffusion.F90:356
mom_diag_mediator
The subroutines here provide convenient wrappers to the fms diag_manager interfaces with additional d...
Definition: MOM_diag_mediator.F90:3
mom_diag_mediator::register_diag_field
integer function, public register_diag_field(module_name, field_name, axes_in, init_time, long_name, units, missing_value, range, mask_variant, standard_name, verbose, do_not_log, err_msg, interp_method, tile_count, cmor_field_name, cmor_long_name, cmor_units, cmor_standard_name, cell_methods, x_cell_method, y_cell_method, v_cell_method, conversion, v_extensive)
Returns the "diag_mediator" handle for a group (native, CMOR, z-coord, ...) of diagnostics derived fr...
Definition: MOM_diag_mediator.F90:1878
mom_remapping::build_reconstructions_1d
subroutine, public build_reconstructions_1d(CS, n0, h0, u0, ppoly_r_coefs, ppoly_r_E, ppoly_r_S, iMethod, h_neglect, h_neglect_edge)
Creates polynomial reconstructions of u0 on the source grid h0.
Definition: MOM_remapping.F90:356
mom_energetic_pbl
Energetically consistent planetary boundary layer parameterization.
Definition: MOM_energetic_PBL.F90:2
mom_error_handler::mom_mesg
subroutine, public mom_mesg(message, verb, all_print)
This provides a convenient interface for writing an informative comment.
Definition: MOM_error_handler.F90:53
mom_domains::pass_var
Do a halo update on an array.
Definition: MOM_domains.F90:49
mom_remapping::remapping_cs
Container for remapping parameters.
Definition: MOM_remapping.F90:22
mom_tracer_registry
This module contains the tracer_registry_type and the subroutines that handle registration of tracers...
Definition: MOM_tracer_registry.F90:5
mom_file_parser::param_file_type
A structure that can be parsed to read and document run-time parameters.
Definition: MOM_file_parser.F90:54
mom_file_parser::get_param
An overloaded interface to read and log the values of various types of parameters.
Definition: MOM_file_parser.F90:102
mom_lateral_boundary_diffusion::fluxes_layer_method
subroutine fluxes_layer_method(boundary, nk, deg, h_L, h_R, hbl_L, hbl_R, area_L, area_R, phi_L, phi_R, ppoly0_coefs_L, ppoly0_coefs_R, ppoly0_E_L, ppoly0_E_R, method, khtr_u, F_layer)
Calculate the lateral boundary diffusive fluxes using the layer by layer method. See Along layer appr...
Definition: MOM_lateral_boundary_diffusion.F90:424
mom_unit_scaling::unit_scale_type
Describes various unit conversion factors.
Definition: MOM_unit_scaling.F90:14
mom_lateral_boundary_diffusion::bulk_average
real function bulk_average(boundary, nk, deg, h, hBLT, phi, ppoly0_E, ppoly0_coefs, method, k_top, zeta_top, k_bot, zeta_bot)
Definition: MOM_lateral_boundary_diffusion.F90:293
mom_diabatic_driver::extract_diabatic_member
subroutine, public extract_diabatic_member(CS, opacity_CSp, optics_CSp, evap_CFL_limit, minimum_forcing_depth, KPP_CSp, energetic_PBL_CSp, diabatic_aux_CSp)
Returns pointers or values of members within the diabatic_CS type. For extensibility,...
Definition: MOM_diabatic_driver.F90:2865
mom_remapping
Provides column-wise vertical remapping functions.
Definition: MOM_remapping.F90:2
mom_diag_mediator::post_data
Make a diagnostic available for averaging or output.
Definition: MOM_diag_mediator.F90:70
mom_remapping::remappingdefaultscheme
character(len=3), public remappingdefaultscheme
Default remapping method.
Definition: MOM_remapping.F90:69
mom_verticalgrid::verticalgrid_type
Describes the vertical ocean grid, including unit conversion factors.
Definition: MOM_verticalGrid.F90:24
mom_lateral_boundary_diffusion::lateral_boundary_diffusion_init
logical function, public lateral_boundary_diffusion_init(Time, G, param_file, diag, diabatic_CSp, CS)
Initialization routine that reads runtime parameters and sets up pointers to other control structures...
Definition: MOM_lateral_boundary_diffusion.F90:62
mom_remapping::remappingschemesdoc
character(len=256), public remappingschemesdoc
Documentation for external callers.
Definition: MOM_remapping.F90:62
mom_lateral_boundary_diffusion::harmonic_mean
real function harmonic_mean(h1, h2)
Calculate the harmonic mean of two quantities See Harmonic Mean.
Definition: MOM_lateral_boundary_diffusion.F90:345
mom_domains
Describes the decomposed MOM domain and has routines for communications across PEs.
Definition: MOM_domains.F90:2
mom_lateral_boundary_diffusion::test_boundary_k_range
logical function test_boundary_k_range(k_top, zeta_top, k_bot, zeta_bot, k_top_ans, zeta_top_ans, k_bot_ans, zeta_bot_ans, test_name, verbose)
Return true if output of unit tests for boundary_k_range does not match answers.
Definition: MOM_lateral_boundary_diffusion.F90:1009
mom_cpu_clock
Wraps the MPP cpu clock functions.
Definition: MOM_cpu_clock.F90:2
mom_energetic_pbl::energetic_pbl_get_mld
subroutine, public energetic_pbl_get_mld(CS, MLD, G, US, m_to_MLD_units)
Copies the ePBL active mixed layer depth into MLD.
Definition: MOM_energetic_PBL.F90:1920
mom_file_parser
The MOM6 facility to parse input files for runtime parameters.
Definition: MOM_file_parser.F90:2
mom_remapping::average_value_ppoly
real function, public average_value_ppoly(n0, u0, ppoly0_E, ppoly0_coefs, method, i0, xa, xb)
Returns the average value of a reconstruction within a single source cell, i0, between the non-dimens...
Definition: MOM_remapping.F90:926
mom_lateral_boundary_diffusion::bottom
integer, parameter, public bottom
Set a value that corresponds to the bottom boundary.
Definition: MOM_lateral_boundary_diffusion.F90:34
mom_tracer_registry::tracer_registry_type
Type to carry basic tracer information.
Definition: MOM_tracer_registry.F90:138
mom_energetic_pbl::energetic_pbl_cs
This control structure holds parameters for the MOM_energetic_PBL module.
Definition: MOM_energetic_PBL.F90:35
mom_lateral_boundary_diffusion
Calculates and applies diffusive fluxes as a parameterization of lateral mixing (non-neutral) by meso...
Definition: MOM_lateral_boundary_diffusion.F90:4
mom_diabatic_driver
This routine drives the diabatic/dianeutral physics for MOM.
Definition: MOM_diabatic_driver.F90:2
mom_grid
Provides the ocean grid type.
Definition: MOM_grid.F90:2
mom_unit_scaling
Provides a transparent unit rescaling type to facilitate dimensional consistency testing.
Definition: MOM_unit_scaling.F90:2
mom_error_handler::is_root_pe
logical function, public is_root_pe()
This returns .true. if the current PE is the root PE.
Definition: MOM_error_handler.F90:44
mom_file_parser::openparameterblock
subroutine, public openparameterblock(CS, blockName, desc)
Tags blockName onto the end of the active parameter block name.
Definition: MOM_file_parser.F90:2015
mom_lateral_boundary_diffusion::near_boundary_unit_tests
logical function, public near_boundary_unit_tests(verbose)
Unit tests for near-boundary horizontal mixing.
Definition: MOM_lateral_boundary_diffusion.F90:714
mom_error_handler::mom_error
subroutine, public mom_error(level, message, all_print)
This provides a convenient interface for writing an mpp_error message with run-time filter based on a...
Definition: MOM_error_handler.F90:72
mom_remapping::initialize_remapping
subroutine, public initialize_remapping(CS, remapping_scheme, boundary_extrapolation, check_reconstruction, check_remapping, force_bounds_in_subcell, answers_2018)
Constructor for remapping control structure.
Definition: MOM_remapping.F90:1547
mom_cvmix_kpp
Provides the K-Profile Parameterization (KPP) of Large et al., 1994, via CVMix.
Definition: MOM_CVMix_KPP.F90:2
mom_error_handler
Routines for error handling and I/O management.
Definition: MOM_error_handler.F90:2
mom_cvmix_kpp::kpp_cs
Control structure for containing KPP parameters/data.
Definition: MOM_CVMix_KPP.F90:71
mom_grid::ocean_grid_type
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:26
mom_file_parser::closeparameterblock
subroutine, public closeparameterblock(CS)
Remove the lowest level of recursion from the active block name.
Definition: MOM_file_parser.F90:2033
mom_lateral_boundary_diffusion::test_layer_fluxes
logical function test_layer_fluxes(verbose, nk, test_name, F_calc, F_ans)
Returns true if output of near-boundary unit tests does not match correct computed values and conditi...
Definition: MOM_lateral_boundary_diffusion.F90:983
mom_diabatic_driver::diabatic_cs
Control structure for this module.
Definition: MOM_diabatic_driver.F90:92
mom_diag_mediator::diag_ctrl
The following data type a list of diagnostic fields an their variants, as well as variables that cont...
Definition: MOM_diag_mediator.F90:239
mom_lateral_boundary_diffusion::fluxes_bulk_method
subroutine fluxes_bulk_method(boundary, nk, deg, h_L, h_R, hbl_L, hbl_R, area_L, area_R, phi_L, phi_R, ppoly0_coefs_L, ppoly0_coefs_R, ppoly0_E_L, ppoly0_E_R, method, khtr_u, F_bulk, F_layer, F_limit)
Apply the lateral boundary diffusive fluxes calculated from a 'bulk model' See Bulk layer approach (M...
Definition: MOM_lateral_boundary_diffusion.F90:537