MOM6
MOM_continuity_PPM.F90
Go to the documentation of this file.
1 !> Solve the layer continuity equation using the PPM method for layer fluxes.
3 
4 ! This file is part of MOM6. See LICENSE.md for the license.
5 
6 use mom_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, clock_routine
7 use mom_diag_mediator, only : time_type, diag_ctrl
8 use mom_error_handler, only : mom_error, fatal, warning, is_root_pe
10 use mom_grid, only : ocean_grid_type
13 use mom_variables, only : bt_cont_type
15 
16 implicit none ; private
17 
18 #include <MOM_memory.h>
19 
21 
23 
24 !> Control structure for mom_continuity_ppm
25 type, public :: continuity_ppm_cs ; private
26  type(diag_ctrl), pointer :: diag !< Diagnostics control structure.
27  logical :: upwind_1st !< If true, use a first-order upwind scheme.
28  logical :: monotonic !< If true, use the Colella & Woodward monotonic
29  !! limiter; otherwise use a simple positive
30  !! definite limiter.
31  logical :: simple_2nd !< If true, use a simple second order (arithmetic
32  !! mean) interpolation of the edge values instead
33  !! of the higher order interpolation.
34  real :: tol_eta !< The tolerance for free-surface height
35  !! discrepancies between the barotropic solution and
36  !! the sum of the layer thicknesses, in m.
37  real :: tol_vel !< The tolerance for barotropic velocity
38  !! discrepancies between the barotropic solution and
39  !! the sum of the layer thicknesses, in m s-1.
40  real :: tol_eta_aux !< The tolerance for free-surface height
41  !! discrepancies between the barotropic solution and
42  !! the sum of the layer thicknesses when calculating
43  !! the auxiliary corrected velocities, in m.
44  real :: cfl_limit_adjust !< The maximum CFL of the adjusted velocities, ND.
45  logical :: aggress_adjust !< If true, allow the adjusted velocities to have a
46  !! relative CFL change up to 0.5. False by default.
47  logical :: vol_cfl !< If true, use the ratio of the open face lengths
48  !! to the tracer cell areas when estimating CFL
49  !! numbers. Without aggress_adjust, the default is
50  !! false; it is always true with.
51  logical :: better_iter !< If true, stop corrective iterations using a
52  !! velocity-based criterion and only stop if the
53  !! iteration is better than all predecessors.
54  logical :: use_visc_rem_max !< If true, use more appropriate limiting bounds
55  !! for corrections in strongly viscous columns.
56  logical :: marginal_faces !< If true, use the marginal face areas from the
57  !! continuity solver for use as the weights in the
58  !! barotropic solver. Otherwise use the transport
59  !! averaged areas.
60 end type continuity_ppm_cs
61 
62 !> A container for loop bounds
63 type :: loop_bounds_type ; private
64  !>@{
65  !! Loop bounds
66  integer :: ish, ieh, jsh, jeh
67  !>@}
68 end type loop_bounds_type
69 
70 contains
71 
72 !> Time steps the layer thicknesses, using a monotonically limit, directionally split PPM scheme,
73 !! based on Lin (1994).
74 subroutine continuity_ppm(u, v, hin, h, uh, vh, dt, G, GV, CS, uhbt, vhbt, OBC, &
75  visc_rem_u, visc_rem_v, u_cor, v_cor, &
76  uhbt_aux, vhbt_aux, u_cor_aux, v_cor_aux, BT_cont)
77  ! In the following documentation, H is used for the units of thickness (usually m or kg m-2.)
78  type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
79  type(continuity_ppm_cs), pointer :: CS !< Module's control structure.
80  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: u !< Zonal velocity, in m s-1.
81  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: v !< Meridional velocity, in m s-1.
82  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: hin !< Initial layer thickness, in H.
83  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: h !< Final layer thickness, in H.
84  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(out) :: uh !< Zonal volume flux,
85  !! u*h*dy, H m2 s-1.
86  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(out) :: vh !< Meridional volume flux,
87  !! v*h*dx, H m2 s-1.
88  real, intent(in) :: dt !< Time increment in s.
89  type(verticalgrid_type), intent(in) :: GV !< Vertical grid structure.
90  real, dimension(SZIB_(G),SZJ_(G)), intent(in), optional :: uhbt
91  !< The summed volume flux through zonal faces, H m2 s-1.
92  real, dimension(SZI_(G),SZJB_(G)), intent(in), optional :: vhbt
93  !< The summed volume flux through meridional faces, H m2 s-1.
94  type(ocean_obc_type), pointer, optional :: OBC !< Open boundaries control structure.
95  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in), optional :: visc_rem_u
96  !< The fraction of zonal momentum originally in a layer that remains after a time-step
97  !! of viscosity, and the fraction of a time-step's worth of a barotropic acceleration that
98  !! a layer experiences after viscosity is applied.
99  !! Non-dimensional between 0 (at the bottom) and 1 (far above the bottom).
100  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in), optional :: visc_rem_v
101  !< The fraction of meridional momentum originally in a layer that remains after a time-step
102  !! of viscosity, and the fraction of a time-step's worth of a barotropic acceleration that
103  !! a layer experiences after viscosity is applied.
104  !! Non-dimensional between 0 (at the bottom) and 1 (far above the bottom).
105  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(out), optional :: u_cor
106  !< The zonal velocities that give uhbt as the depth-integrated transport, in m s-1.
107  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(out), optional :: v_cor
108  !< The meridional velocities that give vhbt as the depth-integrated transport, in m s-1.
109  real, dimension(SZIB_(G),SZJ_(G)), intent(in), optional :: uhbt_aux
110  !< A second set of summed volume fluxes through zonal faces, in H m2 s-1.
111  real, dimension(SZI_(G),SZJB_(G)), intent(in), optional :: vhbt_aux
112  !< A second set of summed volume fluxes through meridional faces, in H m2 s-1.
113  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(out), optional :: u_cor_aux
114  !< The zonal velocities that give uhbt_aux as the depth-integrated transports, in m s-1.
115  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(out), optional :: v_cor_aux
116  !< The meridional velocities that give vhbt_aux as the depth-integrated transports, in m s-1.
117  type(bt_cont_type), pointer, optional :: BT_cont !< A structure with
118  !! elements that describe the effective open face areas as a function of barotropic flow.
119  ! Local variables
120  real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: h_input ! Left and right face thicknesses, in H.
121  real :: h_min
122  type(loop_bounds_type) :: LB
123  integer :: is, ie, js, je, nz, stencil
124  integer :: i, j, k
125 
126  logical :: x_first
127  logical :: local_Flather_EW, local_Flather_NS
128  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
129 
130  h_min = gv%Angstrom
131 
132  if (.not.associated(cs)) call mom_error(fatal, &
133  "MOM_continuity_PPM: Module must be initialized before it is used.")
134  x_first = (mod(g%first_direction,2) == 0)
135 
136  local_flather_ew = .false. ; local_flather_ns = .false.
137  if (present(obc)) then ; if (associated(obc)) then ; if (obc%OBC_pe) then
138  local_flather_ew = obc%Flather_u_BCs_exist_globally
139  local_flather_ns = obc%Flather_v_BCs_exist_globally
140  ! If an OBC is being applied, copy the input thicknesses so that the
141  ! OBC code works even if hin == h.
142  if (local_flather_ew .or. local_flather_ns) &
143  h_input(:,:,:) = hin(:,:,:)
144  endif ; endif ; endif
145 
146  if (present(visc_rem_u) .neqv. present(visc_rem_v)) call mom_error(fatal, &
147  "MOM_continuity_PPM: Either both visc_rem_u and visc_rem_v or neither"// &
148  " one must be present in call to continuity_PPM.")
149 
150  stencil = 3 ; if (cs%simple_2nd) stencil = 2 ; if (cs%upwind_1st) stencil = 1
151 
152  if (x_first) then
153  ! First, advect zonally.
154  lb%ish = g%isc ; lb%ieh = g%iec
155  lb%jsh = g%jsc-stencil ; lb%jeh = g%jec+stencil
156  call zonal_mass_flux(u, hin, uh, dt, g, gv, cs, lb, uhbt, obc, visc_rem_u, &
157  u_cor, uhbt_aux, u_cor_aux, bt_cont)
158 
159  call cpu_clock_begin(id_clock_update)
160 !$OMP parallel do default(none) shared(LB,nz,G,uh,hin,dt,h)
161  do k=1,nz ; do j=lb%jsh,lb%jeh ; do i=lb%ish,lb%ieh
162  h(i,j,k) = hin(i,j,k) - dt* g%IareaT(i,j) * (uh(i,j,k) - uh(i-1,j,k))
163  ! Uncomment this line to prevent underflow.
164  ! if (h(i,j,k) < h_min) h(i,j,k) = h_min
165  enddo ; enddo ; enddo
166  call cpu_clock_end(id_clock_update)
167 
168  if (local_flather_ew) then
169  do k=1,nz
170  do j=lb%jsh,lb%jeh ; do i=lb%ish,lb%ieh+1
171  if (obc%segnum_u(i-1,j) /= obc_none) then
172  if (obc%segment(obc%segnum_u(i-1,j))%direction == obc_direction_e) &
173  h(i,j,k) = h_input(i-1,j,k)
174  endif
175  enddo
176  do i=lb%ish-1,lb%ieh
177  if (obc%segnum_u(i,j) /= obc_none) then
178  if (obc%segment(obc%segnum_u(i,j))%direction == obc_direction_w) &
179  h(i,j,k) = h_input(i+1,j,k)
180  endif
181  enddo ; enddo
182  enddo
183  endif
184  lb%ish = g%isc ; lb%ieh = g%iec ; lb%jsh = g%jsc ; lb%jeh = g%jec
185 
186  ! Now advect meridionally, using the updated thicknesses to determine
187  ! the fluxes.
188  call meridional_mass_flux(v, h, vh, dt, g, gv, cs, lb, vhbt, obc, visc_rem_v, &
189  v_cor, vhbt_aux, v_cor_aux, bt_cont)
190 
191  call cpu_clock_begin(id_clock_update)
192 !$OMP parallel do default(none) shared(h_min,nz,LB,h,dt,G,vh)
193  do k=1,nz ; do j=lb%jsh,lb%jeh ; do i=lb%ish,lb%ieh
194  h(i,j,k) = h(i,j,k) - dt*g%IareaT(i,j) * (vh(i,j,k) - vh(i,j-1,k))
195  ! This line prevents underflow.
196  if (h(i,j,k) < h_min) h(i,j,k) = h_min
197  enddo ; enddo ; enddo
198  call cpu_clock_end(id_clock_update)
199 
200  if (local_flather_ns) then
201  do k=1,nz
202  do j=lb%jsh,lb%jeh+1 ; do i=lb%ish-1,lb%ieh+1
203  if (obc%segnum_v(i,j-1) /= obc_none) then
204  if (obc%segment(obc%segnum_v(i,j-1))%direction == obc_direction_n) &
205  h(i,j,k) = h_input(i,j-1,k)
206  endif
207  enddo ; enddo
208  do j=lb%jsh-1,lb%jeh ; do i=lb%ish-1,lb%ieh+1
209  if (obc%segnum_v(i,j) /= obc_none) then
210  if (obc%segment(obc%segnum_v(i,j))%direction == obc_direction_s) &
211  h(i,j,k) = h_input(i,j+1,k)
212  endif
213  enddo ; enddo
214  enddo
215  endif
216  else ! .not. x_first
217  ! First, advect meridionally, so set the loop bounds accordingly.
218  lb%ish = g%isc-stencil ; lb%ieh = g%iec+stencil
219  lb%jsh = g%jsc ; lb%jeh = g%jec
220 
221  call meridional_mass_flux(v, hin, vh, dt, g, gv, cs, lb, vhbt, obc, visc_rem_v, &
222  v_cor, vhbt_aux, v_cor_aux, bt_cont)
223 
224  call cpu_clock_begin(id_clock_update)
225 !$OMP parallel do default(none) shared(nz,LB,h,hin,dt,G,vh)
226  do k=1,nz ; do j=lb%jsh,lb%jeh ; do i=lb%ish,lb%ieh
227  h(i,j,k) = hin(i,j,k) - dt*g%IareaT(i,j) * (vh(i,j,k) - vh(i,j-1,k))
228  enddo ; enddo ; enddo
229  call cpu_clock_end(id_clock_update)
230 
231  if (local_flather_ns) then
232  do k=1,nz
233  do j=lb%jsh,lb%jeh+1 ; do i=lb%ish-1,lb%ieh+1
234  if (obc%segnum_v(i,j-1) /= obc_none) then
235  if (obc%segment(obc%segnum_v(i,j-1))%direction == obc_direction_n) &
236  h(i,j,k) = h_input(i,j-1,k)
237  endif
238  enddo ; enddo
239  do j=lb%jsh-1,lb%jeh ; do i=lb%ish-1,lb%ieh+1
240  if (obc%segnum_v(i,j) /= obc_none) then
241  if (obc%segment(obc%segnum_v(i,j))%direction == obc_direction_s) &
242  h(i,j,k) = h_input(i,j+1,k)
243  endif
244  enddo ; enddo
245  enddo
246  endif
247 
248  ! Now advect zonally, using the updated thicknesses to determine
249  ! the fluxes.
250  lb%ish = g%isc ; lb%ieh = g%iec ; lb%jsh = g%jsc ; lb%jeh = g%jec
251  call zonal_mass_flux(u, h, uh, dt, g, gv, cs, lb, uhbt, obc, visc_rem_u, &
252  u_cor, uhbt_aux, u_cor_aux, bt_cont)
253 
254  call cpu_clock_begin(id_clock_update)
255 !$OMP parallel do default(none) shared(h_min,nz,LB,h,dt,G,uh)
256  do k=1,nz ; do j=lb%jsh,lb%jeh ; do i=lb%ish,lb%ieh
257  h(i,j,k) = h(i,j,k) - dt* g%IareaT(i,j) * (uh(i,j,k) - uh(i-1,j,k))
258  ! This line prevents underflow.
259  if (h(i,j,k) < h_min) h(i,j,k) = h_min
260  enddo ; enddo ; enddo
261  call cpu_clock_end(id_clock_update)
262 
263  if (local_flather_ew) then
264  do k=1,nz
265  do j=lb%jsh,lb%jeh ; do i=lb%ish,lb%ieh+1
266  if (obc%segnum_u(i-1,j) /= obc_none) then
267  if (obc%segment(obc%segnum_u(i-1,j))%direction == obc_direction_e) &
268  h(i,j,k) = h_input(i-1,j,k)
269  endif
270  enddo
271  do i=lb%ish-1,lb%ieh
272  if (obc%segnum_u(i,j) /= obc_none) then
273  if (obc%segment(obc%segnum_u(i,j))%direction == obc_direction_w) &
274  h(i,j,k) = h_input(i+1,j,k)
275  endif
276  enddo ; enddo
277  enddo
278  endif
279  endif
280 
281 end subroutine continuity_ppm
282 
283 !> Calculates the mass or volume fluxes through the zonal faces, and other related quantities.
284 subroutine zonal_mass_flux(u, h_in, uh, dt, G, GV, CS, LB, uhbt, OBC, &
285  visc_rem_u, u_cor, uhbt_aux, u_cor_aux, BT_cont)
286  type(ocean_grid_type), intent(inout) :: G !< Ocean's grid structure.
287  type(verticalgrid_type), intent(in) :: GV !< Ocean's vertical grid structure.
288  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: u !< Zonal velocity, in m s-1.
289  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: h_in !< Layer thickness used to
290  !! calculate fluxes, in H.
291  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(out) :: uh !< Volume flux through zonal
292  !! faces = u*h*dy, H m2 s-1.
293  real, intent(in) :: dt !< Time increment in s.
294  type(continuity_ppm_cs), pointer :: CS !< This module's control structure.
295  type(loop_bounds_type), intent(in) :: LB !< Loop bounds structure.
296  type(ocean_obc_type), pointer, optional :: OBC !< Open boundaries control structure.
297  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in), optional :: visc_rem_u !<
298  !< The fraction of zonal momentum originally in a layer that remains after a time-step
299  !! of viscosity, and the fraction of a time-step's worth of a barotropic acceleration that
300  !! a layer experiences after viscosity is applied.
301  !! Non-dimensional between 0 (at the bottom) and 1 (far above the bottom).
302  real, dimension(SZIB_(G),SZJ_(G)), intent(in), optional :: uhbt
303  !< The summed volume flux through zonal faces, H m2 s-1.
304  real, dimension(SZIB_(G),SZJ_(G)), intent(in), optional :: uhbt_aux
305  !< A second set of summed volume fluxes through zonal faces, in H m2 s-1.
306  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(out), optional :: u_cor
307  !< The zonal velocitiess (u with a barotropic correction)
308  !! that give uhbt as the depth-integrated transport, m s-1.
309  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(out), optional :: u_cor_aux
310  !< The zonal velocities (u with a barotropic correction)
311  !! that give uhbt_aux as the depth-integrated transports, in m s-1.
312  type(bt_cont_type), pointer, optional :: BT_cont !<
313  !< A structure with elements that describe the effective
314  !! open face areas as a function of barotropic flow.
315  ! Local variables
316  real, dimension(SZIB_(G),SZK_(G)) :: duhdu ! Partial derivative of uh with u, in H m.
317  real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: h_L, h_R ! Left and right face thicknesses, in H.
318  real, dimension(SZIB_(G)) :: &
319  du, & ! Corrective barotropic change in the velocity, in m s-1.
320  du_min_CFL, & ! Min/max limits on du correction
321  du_max_CFL, & ! to avoid CFL violations
322  duhdu_tot_0, & ! Summed partial derivative of uh with u, in H m.
323  uh_tot_0, & ! Summed transport with no barotropic correction in H m2 s-1.
324  visc_rem_max ! The column maximum of visc_rem.
325  logical, dimension(SZIB_(G)) :: do_I
326  real, dimension(SZIB_(G),SZK_(G)) :: &
327  visc_rem ! A 2-D copy of visc_rem_u or an array of 1's.
328  real :: I_vrm ! 1.0 / visc_rem_max, nondim.
329  real :: CFL_dt ! The maximum CFL ratio of the adjusted velocities divided by
330  ! the time step, in s-1.
331  real :: I_dt ! 1.0 / dt, in s-1.
332  real :: du_lim ! The velocity change that give a relative CFL of 1, in m s-1.
333  real :: dx_E, dx_W ! Effective x-grid spacings to the east and west, in m.
334  integer :: i, j, k, ish, ieh, jsh, jeh, n, nz
335  logical :: do_aux, local_specified_BC, use_visc_rem, set_BT_cont, any_simple_OBC
336  logical :: local_Flather_OBC, local_open_BC, is_simple
337  type(obc_segment_type), pointer :: segment
338 
339  do_aux = (present(uhbt_aux) .and. present(u_cor_aux))
340  use_visc_rem = present(visc_rem_u)
341  local_specified_bc = .false. ; set_bt_cont = .false. ; local_flather_obc = .false.
342  local_open_bc = .false.
343  if (present(bt_cont)) set_bt_cont = (associated(bt_cont))
344  if (present(obc)) then ; if (associated(obc)) then
345  local_specified_bc = obc%specified_u_BCs_exist_globally
346  local_flather_obc = obc%Flather_u_BCs_exist_globally .or. &
347  obc%Flather_v_BCs_exist_globally
348  local_open_bc = obc%open_u_BCs_exist_globally
349  endif ; endif
350  ish = lb%ish ; ieh = lb%ieh ; jsh = lb%jsh ; jeh = lb%jeh ; nz = g%ke
351 
352  cfl_dt = cs%CFL_limit_adjust / dt
353  i_dt = 1.0 / dt
354  if (cs%aggress_adjust) cfl_dt = i_dt
355 
356  call cpu_clock_begin(id_clock_update)
357 !$OMP parallel do default(none) shared(ish,ieh,jsh,jeh,nz,CS,h_L,h_in,h_R,G,GV,LB,visc_rem)
358  do k=1,nz
359  ! This sets h_L and h_R.
360  if (cs%upwind_1st) then
361  do j=jsh,jeh ; do i=ish-1,ieh+1
362  h_l(i,j,k) = h_in(i,j,k) ; h_r(i,j,k) = h_in(i,j,k)
363  enddo ; enddo
364  else
365  call ppm_reconstruction_x(h_in(:,:,k), h_l(:,:,k), h_r(:,:,k), g, lb, &
366  2.0*gv%Angstrom, cs%monotonic, simple_2nd=cs%simple_2nd)
367  endif
368  do i=ish-1,ieh ; visc_rem(i,k) = 1.0 ; enddo
369  enddo
370  if (local_open_bc) then
371  do n=1, obc%number_of_segments
372  segment => obc%segment(n)
373  if (.not. segment%on_pe .or. segment%specified) cycle
374  if (segment%direction == obc_direction_e) then
375  i=segment%HI%IsdB
376  do k=1,nz ; do j=segment%HI%jsd,segment%HI%jed
377  h_in(i+1,j,k) = h_in(i,j,k)
378  h_l(i+1,j,k) = h_in(i,j,k)
379  h_r(i+1,j,k) = h_in(i,j,k)
380  h_r(i,j,k) = h_in(i,j,k)
381  enddo ; enddo
382  elseif (segment%direction == obc_direction_w) then
383  i=segment%HI%IsdB
384  do k=1,nz ; do j=segment%HI%jsd,segment%HI%jed
385  h_in(i,j,k) = h_in(i+1,j,k)
386  h_l(i,j,k) = h_in(i+1,j,k)
387  h_r(i,j,k) = h_in(i+1,j,k)
388  h_l(i+1,j,k) = h_in(i+1,j,k)
389  enddo ; enddo
390  endif
391  enddo
392  endif
393  call cpu_clock_end(id_clock_update)
394 
395  call cpu_clock_begin(id_clock_correct)
396 !$OMP parallel do default(none) shared(ish,ieh,jsh,jeh,nz,u,h_in,h_L,h_R,use_visc_rem,visc_rem_u, &
397 !$OMP uh,dt,G,GV,CS,local_specified_BC,OBC,uhbt,do_aux,set_BT_cont, &
398 !$OMP CFL_dt,I_dt,u_cor,uhbt_aux,u_cor_aux,BT_cont, local_Flather_OBC) &
399 !$OMP private(do_I,duhdu,du,du_max_CFL,du_min_CFL,uh_tot_0,duhdu_tot_0, &
400 !$OMP is_simple, &
401 !$OMP visc_rem_max, I_vrm, du_lim, dx_E, dx_W, any_simple_OBC ) &
402 !$OMP firstprivate(visc_rem)
403  do j=jsh,jeh
404  do i=ish-1,ieh ; do_i(i) = .true. ; visc_rem_max(i) = 0.0 ; enddo
405  ! Set uh and duhdu.
406  do k=1,nz
407  if (use_visc_rem) then ; do i=ish-1,ieh
408  visc_rem(i,k) = visc_rem_u(i,j,k)
409  visc_rem_max(i) = max(visc_rem_max(i), visc_rem(i,k))
410  enddo ; endif
411  call zonal_flux_layer(u(:,j,k), h_in(:,j,k), h_l(:,j,k), h_r(:,j,k), &
412  uh(:,j,k), duhdu(:,k), visc_rem(:,k), &
413  dt, g, j, ish, ieh, do_i, cs%vol_CFL)
414  if (local_specified_bc) then
415  do i=ish-1,ieh
416  if (obc%segment(obc%segnum_u(i,j))%specified) &
417  uh(i,j,k) = obc%segment(obc%segnum_u(i,j))%normal_trans(i,j,k)
418  enddo
419  endif
420  enddo
421 
422  if ((.not.use_visc_rem).or.(.not.cs%use_visc_rem_max)) then ; do i=ish-1,ieh
423  visc_rem_max(i) = 1.0
424  enddo ; endif
425 
426  if (present(uhbt) .or. do_aux .or. set_bt_cont) then
427  ! Set limits on du that will keep the CFL number between -1 and 1.
428  ! This should be adequate to keep the root bracketed in all cases.
429  do i=ish-1,ieh
430  i_vrm = 0.0
431  if (visc_rem_max(i) > 0.0) i_vrm = 1.0 / visc_rem_max(i)
432  if (cs%vol_CFL) then
433  dx_w = ratio_max(g%areaT(i,j), g%dy_Cu(i,j), 1000.0*g%dxT(i,j))
434  dx_e = ratio_max(g%areaT(i+1,j), g%dy_Cu(i,j), 1000.0*g%dxT(i+1,j))
435  else ; dx_w = g%dxT(i,j) ; dx_e = g%dxT(i+1,j) ; endif
436  du_max_cfl(i) = 2.0* (cfl_dt * dx_w) * i_vrm
437  du_min_cfl(i) = -2.0 * (cfl_dt * dx_e) * i_vrm
438  uh_tot_0(i) = 0.0 ; duhdu_tot_0(i) = 0.0
439  enddo
440  do k=1,nz ; do i=ish-1,ieh
441  duhdu_tot_0(i) = duhdu_tot_0(i) + duhdu(i,k)
442  uh_tot_0(i) = uh_tot_0(i) + uh(i,j,k)
443  enddo ; enddo
444  if (use_visc_rem) then
445  if (cs%aggress_adjust) then
446  do k=1,nz ; do i=ish-1,ieh
447  if (cs%vol_CFL) then
448  dx_w = ratio_max(g%areaT(i,j), g%dy_Cu(i,j), 1000.0*g%dxT(i,j))
449  dx_e = ratio_max(g%areaT(i+1,j), g%dy_Cu(i,j), 1000.0*g%dxT(i+1,j))
450  else ; dx_w = g%dxT(i,j) ; dx_e = g%dxT(i+1,j) ; endif
451 
452  du_lim = 0.499*((dx_w*i_dt - u(i,j,k)) + min(0.0,u(i-1,j,k)))
453  if (du_max_cfl(i) * visc_rem(i,k) > du_lim) &
454  du_max_cfl(i) = du_lim / visc_rem(i,k)
455 
456  du_lim = 0.499*((-dx_e*i_dt - u(i,j,k)) + max(0.0,u(i+1,j,k)))
457  if (du_min_cfl(i) * visc_rem(i,k) < du_lim) &
458  du_min_cfl(i) = du_lim / visc_rem(i,k)
459  enddo ; enddo
460  else
461  do k=1,nz ; do i=ish-1,ieh
462  if (cs%vol_CFL) then
463  dx_w = ratio_max(g%areaT(i,j), g%dy_Cu(i,j), 1000.0*g%dxT(i,j))
464  dx_e = ratio_max(g%areaT(i+1,j), g%dy_Cu(i,j), 1000.0*g%dxT(i+1,j))
465  else ; dx_w = g%dxT(i,j) ; dx_e = g%dxT(i+1,j) ; endif
466 
467  if (du_max_cfl(i) * visc_rem(i,k) > dx_w*cfl_dt - u(i,j,k)) &
468  du_max_cfl(i) = (dx_w*cfl_dt - u(i,j,k)) / visc_rem(i,k)
469  if (du_min_cfl(i) * visc_rem(i,k) < -dx_e*cfl_dt - u(i,j,k)) &
470  du_min_cfl(i) = -(dx_e*cfl_dt + u(i,j,k)) / visc_rem(i,k)
471  enddo ; enddo
472  endif
473  else
474  if (cs%aggress_adjust) then
475  do k=1,nz ; do i=ish-1,ieh
476  if (cs%vol_CFL) then
477  dx_w = ratio_max(g%areaT(i,j), g%dy_Cu(i,j), 1000.0*g%dxT(i,j))
478  dx_e = ratio_max(g%areaT(i+1,j), g%dy_Cu(i,j), 1000.0*g%dxT(i+1,j))
479  else ; dx_w = g%dxT(i,j) ; dx_e = g%dxT(i+1,j) ; endif
480 
481  du_max_cfl(i) = min(du_max_cfl(i), 0.499 * &
482  ((dx_w*i_dt - u(i,j,k)) + min(0.0,u(i-1,j,k))) )
483  du_min_cfl(i) = max(du_min_cfl(i), 0.499 * &
484  ((-dx_e*i_dt - u(i,j,k)) + max(0.0,u(i+1,j,k))) )
485  enddo ; enddo
486  else
487  do k=1,nz ; do i=ish-1,ieh
488  if (cs%vol_CFL) then
489  dx_w = ratio_max(g%areaT(i,j), g%dy_Cu(i,j), 1000.0*g%dxT(i,j))
490  dx_e = ratio_max(g%areaT(i+1,j), g%dy_Cu(i,j), 1000.0*g%dxT(i+1,j))
491  else ; dx_w = g%dxT(i,j) ; dx_e = g%dxT(i+1,j) ; endif
492 
493  du_max_cfl(i) = min(du_max_cfl(i), dx_w*cfl_dt - u(i,j,k))
494  du_min_cfl(i) = max(du_min_cfl(i), -(dx_e*cfl_dt + u(i,j,k)))
495  enddo ; enddo
496  endif
497  endif
498  do i=ish-1,ieh
499  du_max_cfl(i) = max(du_max_cfl(i),0.0)
500  du_min_cfl(i) = min(du_min_cfl(i),0.0)
501  enddo
502 
503  ! Up to this point, everything is shared between uhbt and uhbt_aux.
504 
505  any_simple_obc = .false.
506  if (present(uhbt) .or. do_aux .or. set_bt_cont) then
507  if (local_specified_bc .or. local_flather_obc) then ; do i=ish-1,ieh
508  ! Avoid reconciling barotropic/baroclinic transports if transport is specified
509  is_simple = obc%segment(obc%segnum_u(i,j))%specified
510  do_i(i) = .not.(obc%segnum_u(i,j) /= obc_none .and. is_simple)
511  any_simple_obc = any_simple_obc .or. is_simple
512  enddo ; else ; do i=ish-1,ieh
513  do_i(i) = .true.
514  enddo ; endif
515  endif
516 
517  if (present(uhbt)) then
518  call zonal_flux_adjust(u, h_in, h_l, h_r, uhbt(:,j), uh_tot_0, &
519  duhdu_tot_0, du, du_max_cfl, du_min_cfl, dt, g, &
520  cs, visc_rem, j, ish, ieh, do_i, .true., uh)
521 
522  if (present(u_cor)) then ; do k=1,nz
523  do i=ish-1,ieh ; u_cor(i,j,k) = u(i,j,k) + du(i) * visc_rem(i,k) ; enddo
524  if (local_specified_bc) then ; do i=ish-1,ieh
525  if (obc%segment(obc%segnum_u(i,j))%specified) &
526  u_cor(i,j,k) = obc%segment(obc%segnum_u(i,j))%normal_vel(i,j,k)
527  enddo ; endif
528  enddo ; endif ! u-corrected
529 
530  endif
531 
532  if (do_aux) then
533  call zonal_flux_adjust(u, h_in, h_l, h_r, uhbt_aux(:,j), uh_tot_0, &
534  duhdu_tot_0, du, du_max_cfl, du_min_cfl, dt, g, &
535  cs, visc_rem, j, ish, ieh, do_i, .false.)
536 
537  do k=1,nz
538  do i=ish-1,ieh ; u_cor_aux(i,j,k) = u(i,j,k) + du(i) * visc_rem(i,k) ; enddo
539  if (local_specified_bc) then ; do i=ish-1,ieh
540  if (obc%segment(obc%segnum_u(i,j))%specified) &
541  u_cor_aux(i,j,k) = obc%segment(obc%segnum_u(i,j))%normal_vel(i,j,k)
542  enddo ; endif
543  enddo
544  endif ! do_aux
545 
546  if (set_bt_cont) then
547  call set_zonal_bt_cont(u, h_in, h_l, h_r, bt_cont, uh_tot_0, duhdu_tot_0,&
548  du_max_cfl, du_min_cfl, dt, g, cs, visc_rem, &
549  visc_rem_max, j, ish, ieh, do_i)
550  if (any_simple_obc) then
551  do i=ish-1,ieh
552  do_i(i) = obc%segment(obc%segnum_u(i,j))%specified
553  if (do_i(i)) bt_cont%Fa_u_W0(i,j) = gv%H_subroundoff*g%dy_Cu(i,j)
554  enddo
555  do k=1,nz ; do i=ish-1,ieh ; if (do_i(i)) then
556  if (abs(obc%segment(obc%segnum_u(i,j))%normal_vel(i,j,k)) > 0.0) &
557  bt_cont%Fa_u_W0(i,j) = bt_cont%Fa_u_W0(i,j) + &
558  obc%segment(obc%segnum_u(i,j))%normal_trans(i,j,k) / &
559  obc%segment(obc%segnum_u(i,j))%normal_vel(i,j,k)
560  endif ; enddo ; enddo
561  do i=ish-1,ieh ; if (do_i(i)) then
562  bt_cont%Fa_u_E0(i,j) = bt_cont%Fa_u_W0(i,j)
563  bt_cont%Fa_u_WW(i,j) = bt_cont%Fa_u_W0(i,j)
564  bt_cont%Fa_u_EE(i,j) = bt_cont%Fa_u_W0(i,j)
565  endif ; enddo
566  endif
567  endif ! set_BT_cont
568 
569  endif ! present(uhbt) or do_aux or set_BT_cont
570  enddo ! j-loop
571  call cpu_clock_end(id_clock_correct)
572 
573  if (set_bt_cont) then ; if (associated(bt_cont%h_u)) then
574  if (present(u_cor)) then
575  call zonal_face_thickness(u_cor, h_in, h_l, h_r, bt_cont%h_u, dt, g, lb, &
576  cs%vol_CFL, cs%marginal_faces, visc_rem_u)
577  else
578  call zonal_face_thickness(u, h_in, h_l, h_r, bt_cont%h_u, dt, g, lb, &
579  cs%vol_CFL, cs%marginal_faces, visc_rem_u)
580  endif
581  endif ; endif
582 
583 end subroutine zonal_mass_flux
584 
585 !> Evaluates the zonal mass or volume fluxes in a layer.
586 subroutine zonal_flux_layer(u, h, h_L, h_R, uh, duhdu, visc_rem, dt, G, j, &
587  ish, ieh, do_I, vol_CFL)
588  type(ocean_grid_type), intent(inout) :: G !< Ocean's grid structure.
589  real, dimension(SZIB_(G)), intent(in) :: u !< Zonal velocity, in m s-1.
590  real, dimension(SZIB_(G)), intent(in) :: visc_rem !< Both the fraction of the
591  !! momentum originally in a layer that remains after a time-step
592  !! of viscosity, and the fraction of a time-step's worth of a barotropic
593  !! acceleration that a layer experiences after viscosity is applied.
594  !! Non-dimensional between 0 (at the bottom) and 1 (far above the bottom).
595  real, dimension(SZI_(G)), intent(in) :: h !< Layer thickness, in H.
596  real, dimension(SZI_(G)), intent(in) :: h_L !< Left thickness, in H.
597  real, dimension(SZI_(G)), intent(in) :: h_R !< Right thickness, in H.
598  real, dimension(SZIB_(G)), intent(inout) :: uh !< Zonal mass or volume
599  !! transport, in H m2 s-1.
600  real, dimension(SZIB_(G)), intent(inout) :: duhdu !< Partial derivative of uh
601  !! with u, in H m.
602  real, intent(in) :: dt !< Time increment in s.
603  integer, intent(in) :: j !< Spatial index.
604  integer, intent(in) :: ish !< Start of index range.
605  integer, intent(in) :: ieh !< End of index range.
606  logical, dimension(SZIB_(G)), intent(in) :: do_I !< Which i values to work on.
607  logical, intent(in) :: vol_CFL !< If true, rescale the
608  !! ratio of face areas to the cell areas when estimating the CFL number.
609  ! Local variables
610  real :: CFL ! The CFL number based on the local velocity and grid spacing, ND.
611  real :: curv_3 ! A measure of the thickness curvature over a grid length,
612  ! with the same units as h_in.
613  real :: h_marg ! The marginal thickness of a flux, in H.
614  integer :: i
615 
616  do i=ish-1,ieh ; if (do_i(i)) then
617  ! Set new values of uh and duhdu.
618  if (u(i) > 0.0) then
619  if (vol_cfl) then ; cfl = (u(i) * dt) * (g%dy_Cu(i,j) * g%IareaT(i,j))
620  else ; cfl = u(i) * dt * g%IdxT(i,j) ; endif
621  curv_3 = h_l(i) + h_r(i) - 2.0*h(i)
622  uh(i) = g%dy_Cu(i,j) * u(i) * &
623  (h_r(i) + cfl * (0.5*(h_l(i) - h_r(i)) + curv_3*(cfl - 1.5)))
624  h_marg = h_r(i) + cfl * ((h_l(i) - h_r(i)) + 3.0*curv_3*(cfl - 1.0))
625  elseif (u(i) < 0.0) then
626  if (vol_cfl) then ; cfl = (-u(i) * dt) * (g%dy_Cu(i,j) * g%IareaT(i+1,j))
627  else ; cfl = -u(i) * dt * g%IdxT(i+1,j) ; endif
628  curv_3 = h_l(i+1) + h_r(i+1) - 2.0*h(i+1)
629  uh(i) = g%dy_Cu(i,j) * u(i) * &
630  (h_l(i+1) + cfl * (0.5*(h_r(i+1)-h_l(i+1)) + curv_3*(cfl - 1.5)))
631  h_marg = h_l(i+1) + cfl * ((h_r(i+1)-h_l(i+1)) + 3.0*curv_3*(cfl - 1.0))
632  else
633  uh(i) = 0.0
634  h_marg = 0.5 * (h_l(i+1) + h_r(i))
635  endif
636  duhdu(i) = g%dy_Cu(i,j) * h_marg * visc_rem(i)
637  endif ; enddo
638 
639 end subroutine zonal_flux_layer
640 
641 !> Sets the effective interface thickness at each zonal velocity point.
642 subroutine zonal_face_thickness(u, h, h_L, h_R, h_u, dt, G, LB, vol_CFL, &
643  marginal, visc_rem_u)
644  type(ocean_grid_type), intent(inout) :: G !< Ocean's grid structure.
645  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: u !< Zonal velocity, in m s-1.
646  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thickness used to
647  !! calculate fluxes, in H.
648  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_L !< Left thickness in the
649  !! reconstruction, in H.
650  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_R !< Right thickness in the
651  !! reconstruction, in H.
652  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout) :: h_u !< Thickness at zonal faces,
653  !! in H.
654  real, intent(in) :: dt !< Time increment in s.
655  type(loop_bounds_type), intent(in) :: LB !< Loop bounds structure.
656  logical, intent(in) :: vol_CFL !<
657  !! If true, rescale the ratio of face areas to the cell
658  !! areas when estimating the CFL number.
659  logical, intent(in) :: marginal !<
660  !! If true, report the marginal face thicknesses; otherwise
661  !! report transport-averaged thicknesses.
662  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in), optional :: visc_rem_u !<
663  !! Both the fraction of the momentum originally in a
664  !! layer that remains after a time-step of viscosity,
665  !! and the fraction of a time-step's worth of a
666  !! barotropic acceleration that a layer experiences
667  !! after viscosity is applied. Non-dimensional between
668  !! 0 (at the bottom) and 1 (far above the bottom).
669  ! Local variables
670  real :: CFL ! The CFL number based on the local velocity and grid spacing, ND.
671  real :: curv_3 ! A measure of the thickness curvature over a grid length,
672  ! with the same units as h_in.
673  real :: h_avg ! The average thickness of a flux, in H.
674  real :: h_marg ! The marginal thickness of a flux, in H.
675  integer :: i, j, k, ish, ieh, jsh, jeh, nz
676  ish = lb%ish ; ieh = lb%ieh ; jsh = lb%jsh ; jeh = lb%jeh ; nz = g%ke
677 
678 !$OMP parallel default(none) shared(ish,ieh,jsh,jeh,nz,u,vol_CFL,dt,G, &
679 !$OMP h_L,h_R,h,h_u,visc_rem_u,marginal) &
680 !$OMP private(CFL,curv_3,h_marg,h_avg)
681 !$OMP do
682  do k=1,nz ; do j=jsh,jeh ; do i=ish-1,ieh
683  if (u(i,j,k) > 0.0) then
684  if (vol_cfl) then ; cfl = (u(i,j,k) * dt) * (g%dy_Cu(i,j) * g%IareaT(i,j))
685  else ; cfl = u(i,j,k) * dt * g%IdxT(i,j) ; endif
686  curv_3 = h_l(i,j,k) + h_r(i,j,k) - 2.0*h(i,j,k)
687  h_avg = h_r(i,j,k) + cfl * (0.5*(h_l(i,j,k) - h_r(i,j,k)) + curv_3*(cfl - 1.5))
688  h_marg = h_r(i,j,k) + cfl * ((h_l(i,j,k) - h_r(i,j,k)) + 3.0*curv_3*(cfl - 1.0))
689  elseif (u(i,j,k) < 0.0) then
690  if (vol_cfl) then ; cfl = (-u(i,j,k)*dt) * (g%dy_Cu(i,j) * g%IareaT(i+1,j))
691  else ; cfl = -u(i,j,k) * dt * g%IdxT(i+1,j) ; endif
692  curv_3 = h_l(i+1,j,k) + h_r(i+1,j,k) - 2.0*h(i+1,j,k)
693  h_avg = h_l(i+1,j,k) + cfl * (0.5*(h_r(i+1,j,k)-h_l(i+1,j,k)) + curv_3*(cfl - 1.5))
694  h_marg = h_l(i+1,j,k) + cfl * ((h_r(i+1,j,k)-h_l(i+1,j,k)) + &
695  3.0*curv_3*(cfl - 1.0))
696  else
697  h_avg = 0.5 * (h_l(i+1,j,k) + h_r(i,j,k))
698  ! The choice to use the arithmetic mean here is somewhat arbitrariy, but
699  ! it should be noted that h_L(i+1,j,k) and h_R(i,j,k) are usually the same.
700  h_marg = 0.5 * (h_l(i+1,j,k) + h_r(i,j,k))
701  ! h_marg = (2.0 * h_L(i+1,j,k) * h_R(i,j,k)) / &
702  ! (h_L(i+1,j,k) + h_R(i,j,k) + GV%H_subroundoff)
703  endif
704 
705  if (marginal) then ; h_u(i,j,k) = h_marg
706  else ; h_u(i,j,k) = h_avg ; endif
707  enddo; enddo ; enddo
708  if (present(visc_rem_u)) then
709 !$OMP do
710  do k=1,nz ; do j=jsh,jeh ; do i=ish-1,ieh
711  h_u(i,j,k) = h_u(i,j,k) * visc_rem_u(i,j,k)
712  enddo ; enddo ; enddo
713  endif
714 !$OMP end parallel
715 
716 end subroutine zonal_face_thickness
717 
718 !> Returns the barotropic velocity adjustment that gives the
719 !! desired barotropic (layer-summed) transport.
720 subroutine zonal_flux_adjust(u, h_in, h_L, h_R, uhbt, uh_tot_0, duhdu_tot_0, &
721  du, du_max_CFL, du_min_CFL, dt, G, CS, visc_rem, &
722  j, ish, ieh, do_I_in, full_precision, uh_3d)
723  type(ocean_grid_type), intent(inout) :: G !< Ocean's grid structure.
724  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: u !< Zonal velocity, in m s-1.
725  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_in !< Layer thickness used to
726  !! calculate fluxes, in H.
727  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_L !< Left thickness in the
728  !! reconstruction, in H.
729  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_R !< Right thickness in the
730  !! reconstruction, in H.
731  real, dimension(SZIB_(G),SZK_(G)), intent(in) :: visc_rem !<
732  !! Both the fraction of the momentum originally in a
733  !! layer that remains after a time-step of viscosity,
734  !! and the fraction of a time-step's worth of a
735  !! barotropic acceleration that a layer experiences
736  !! after viscosity is applied. Non-dimensional between
737  !! 0 (at the bottom) and 1 (far above the bottom).
738  real, dimension(SZIB_(G)), intent(in), optional :: uhbt !<
739  !! The summed volume flux through zonal faces, H m2 s-1.
740  real, dimension(SZIB_(G)), intent(in) :: du_max_CFL !< Maximum acceptable
741  !! value of du, in m s-1.
742  real, dimension(SZIB_(G)), intent(in) :: du_min_CFL !< Minimum acceptable
743  !! value of du, in m s-1.
744  real, dimension(SZIB_(G)), intent(in) :: uh_tot_0 !<
745  !! The summed transport with 0 adjustment, in H m2 s-1.
746  real, dimension(SZIB_(G)), intent(in) :: duhdu_tot_0 !<
747  !! The partial derivative of du_err with du at 0 adjustment, in H m.
748  real, dimension(SZIB_(G)), intent(out) :: du !<
749  !! The barotropic velocity adjustment, in m s-1.
750  real, intent(in) :: dt !< Time increment in s.
751  type(continuity_ppm_cs), pointer :: CS !< This module's control structure.
752  integer, intent(in) :: j !< Spatial index.
753  integer, intent(in) :: ish !< Start of index range.
754  integer, intent(in) :: ieh !< End of index range.
755  logical, dimension(SZIB_(G)), intent(in) :: do_I_in !<
756  !! A logical flag indicating which I values to work on.
757  logical, intent(in), optional :: full_precision !<
758  !! A flag indicating how carefully to iterate. The
759  !! default is .true. (more accurate).
760  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(inout), optional :: uh_3d !<
761  !! Volume flux through zonal faces = u*h*dy, H m2 s-1.
762  ! Local variables
763  real, dimension(SZIB_(G),SZK_(G)) :: &
764  uh_aux, & ! An auxiliary zonal volume flux, in H m s-1.
765  duhdu ! Partial derivative of uh with u, in H m.
766  real, dimension(SZIB_(G)) :: &
767  uh_err, & ! Difference between uhbt and the summed uh, in H m2 s-1.
768  uh_err_best, & ! The smallest value of uh_err found so far, in H m2 s-1.
769  u_new, & ! The velocity with the correction added, in m s-1.
770  duhdu_tot,&! Summed partial derivative of uh with u, in H m.
771  du_min, & ! Min/max limits on du correction based on CFL limits
772  du_max ! and previous iterations, in m s-1.
773  real :: du_prev ! The previous value of du, in m s-1.
774  real :: ddu ! The change in du from the previous iteration, in m s-1.
775  real :: tol_eta ! The tolerance for the current iteration, in m.
776  real :: tol_vel ! The tolerance for velocity in the current iteration, m s-1.
777  integer :: i, k, nz, itt, max_itts = 20
778  logical :: full_prec, domore, do_I(szib_(g))
779 
780  nz = g%ke
781  full_prec = .true. ; if (present(full_precision)) full_prec = full_precision
782 
783  uh_aux(:,:) = 0.0 ; duhdu(:,:) = 0.0
784 
785  if (present(uh_3d)) then ; do k=1,nz ; do i=ish-1,ieh
786  uh_aux(i,k) = uh_3d(i,j,k)
787  enddo ; enddo ; endif
788 
789  do i=ish-1,ieh
790  du(i) = 0.0 ; do_i(i) = do_i_in(i)
791  du_max(i) = du_max_cfl(i) ; du_min(i) = du_min_cfl(i)
792  uh_err(i) = uh_tot_0(i) - uhbt(i) ; duhdu_tot(i) = duhdu_tot_0(i)
793  uh_err_best(i) = abs(uh_err(i))
794  enddo
795 
796  do itt=1,max_itts
797  if (full_prec) then
798  select case (itt)
799  case (:1) ; tol_eta = 1e-6 * cs%tol_eta
800  case (2) ; tol_eta = 1e-4 * cs%tol_eta
801  case (3) ; tol_eta = 1e-2 * cs%tol_eta
802  case default ; tol_eta = cs%tol_eta
803  end select
804  else
805  tol_eta = cs%tol_eta_aux ; if (itt<=1) tol_eta = 1e-6 * cs%tol_eta_aux
806  endif
807  tol_vel = cs%tol_vel
808 
809  do i=ish-1,ieh
810  if (uh_err(i) > 0.0) then ; du_max(i) = du(i)
811  elseif (uh_err(i) < 0.0) then ; du_min(i) = du(i)
812  else ; do_i(i) = .false. ; endif
813  enddo
814  domore = .false.
815  do i=ish-1,ieh ; if (do_i(i)) then
816  if ((dt*min(g%IareaT(i,j),g%IareaT(i+1,j))*abs(uh_err(i)) > tol_eta) .or.&
817  (cs%better_iter .and. ((abs(uh_err(i)) > tol_vel * duhdu_tot(i)) .or.&
818  (abs(uh_err(i)) > uh_err_best(i))) )) then
819  ! Use Newton's method, provided it stays bounded. Otherwise bisect
820  ! the value with the appropriate bound.
821  if (full_prec) then
822  ddu = -uh_err(i) / duhdu_tot(i)
823  du_prev = du(i)
824  du(i) = du(i) + ddu
825  if (abs(ddu) < 1.0e-15*abs(du(i))) then
826  do_i(i) = .false. ! ddu is small enough to quit.
827  elseif (ddu > 0.0) then
828  if (du(i) >= du_max(i)) then
829  du(i) = 0.5*(du_prev + du_max(i))
830  if (du_max(i) - du_prev < 1.0e-15*abs(du(i))) do_i(i) = .false.
831  endif
832  else ! ddu < 0.0
833  if (du(i) <= du_min(i)) then
834  du(i) = 0.5*(du_prev + du_min(i))
835  if (du_prev - du_min(i) < 1.0e-15*abs(du(i))) do_i(i) = .false.
836  endif
837  endif
838  else
839  ! Use Newton's method, provided it stays bounded, just like above.
840  du(i) = du(i) - uh_err(i) / duhdu_tot(i)
841  if ((du(i) >= du_max(i)) .or. (du(i) <= du_min(i))) &
842  du(i) = 0.5*(du_max(i) + du_min(i))
843  endif
844  if (do_i(i)) domore = .true.
845  else
846  do_i(i) = .false.
847  endif
848  endif ; enddo
849  if (.not.domore) exit
850 
851  if ((itt < max_itts) .or. present(uh_3d)) then ; do k=1,nz
852  do i=ish-1,ieh ; u_new(i) = u(i,j,k) + du(i) * visc_rem(i,k) ; enddo
853  call zonal_flux_layer(u_new, h_in(:,j,k), h_l(:,j,k), h_r(:,j,k), &
854  uh_aux(:,k), duhdu(:,k), visc_rem(:,k), &
855  dt, g, j, ish, ieh, do_i, cs%vol_CFL)
856  enddo ; endif
857 
858  if (itt < max_itts) then
859  do i=ish-1,ieh
860  uh_err(i) = -uhbt(i) ; duhdu_tot(i) = 0.0
861  enddo
862  do k=1,nz ; do i=ish-1,ieh
863  uh_err(i) = uh_err(i) + uh_aux(i,k)
864  duhdu_tot(i) = duhdu_tot(i) + duhdu(i,k)
865  enddo ; enddo
866  do i=ish-1,ieh
867  uh_err_best(i) = min(uh_err_best(i), abs(uh_err(i)))
868  enddo
869  endif
870  enddo ! itt-loop
871  ! If there are any faces which have not converged to within the tolerance,
872  ! so-be-it, or else use a final upwind correction?
873  ! This never seems to happen with 20 iterations as max_itt.
874 
875  if (present(uh_3d)) then ; do k=1,nz ; do i=ish-1,ieh
876  uh_3d(i,j,k) = uh_aux(i,k)
877  enddo ; enddo ; endif
878 
879 end subroutine zonal_flux_adjust
880 
881 !> Sets a structure that describes the zonal barotropic volume or mass fluxes as a
882 !! function of barotropic flow to agree closely with the sum of the layer's transports.
883 subroutine set_zonal_bt_cont(u, h_in, h_L, h_R, BT_cont, uh_tot_0, duhdu_tot_0, &
884  du_max_CFL, du_min_CFL, dt, G, CS, visc_rem, &
885  visc_rem_max, j, ish, ieh, do_I)
886  type(ocean_grid_type), intent(inout) :: G !< Ocean's grid structure.
887  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: u !< Zonal velocity, in m s-1.
888  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_in !< Layer thickness used to
889  !! calculate fluxes, in H.
890  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_L !< Left thickness in the
891  !! reconstruction, in H.
892  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_R !< Right thickness in the
893  !! reconstruction, in H.
894  type(bt_cont_type), intent(inout) :: BT_cont !<
895  !! A structure with elements that describe the effective
896  !! open face areas as a function of barotropic flow.
897  real, dimension(SZIB_(G)), intent(in) :: uh_tot_0 !<
898  !! The summed transport with 0 adjustment, in H m2 s-1.
899  real, dimension(SZIB_(G)), intent(in) :: duhdu_tot_0 !<
900  !! The partial derivative of du_err with du at 0 adjustment, in H m.
901  real, dimension(SZIB_(G)), intent(in) :: du_max_CFL !< Maximum acceptable
902  !! value of du, in m s-1.
903  real, dimension(SZIB_(G)), intent(in) :: du_min_CFL !< Minimum acceptable
904  !! value of du, in m s-1.
905  real, intent(in) :: dt !< Time increment in s.
906  type(continuity_ppm_cs), pointer :: CS !< This module's control structure.
907  real, dimension(SZIB_(G),SZK_(G)), intent(in) :: visc_rem !<
908  !! Both the fraction of the momentum originally in a
909  !! layer that remains after a time-step of viscosity,
910  !! and the fraction of a time-step's worth of a
911  !! barotropic acceleration that a layer experiences
912  !! after viscosity is applied. Non-dimensional between
913  !! 0 (at the bottom) and 1 (far above the bottom).
914  real, dimension(SZIB_(G)), intent(in) :: visc_rem_max !< Maximum allowable visc_rem.
915  integer, intent(in) :: j !< Spatial index.
916  integer, intent(in) :: ish !< Start of index range.
917  integer, intent(in) :: ieh !< End of index range.
918  logical, dimension(SZIB_(G)), intent(in) :: do_I !<
919  !! A logical flag indicating which I values to work on.
920  ! Local variables
921  real, dimension(SZIB_(G)) :: &
922  du0, & ! The barotropic velocity increment that gives 0 transport, m s-1.
923  duL, duR, & ! The barotropic velocity increments that give the westerly
924  ! (duL) and easterly (duR) test velocities.
925  zeros, & ! An array of full of 0's.
926  du_cfl, & ! The velocity increment that corresponds to CFL_min, in m s-1.
927  u_l, u_r, & ! The westerly (u_L), easterly (u_R), and zero-barotropic
928  u_0, & ! transport (u_0) layer test velocities, in m s-1.
929  fa_marg_l, & ! The effective layer marginal face areas with the westerly
930  fa_marg_r, & ! (_L), easterly (_R), and zero-barotropic (_0) test
931  fa_marg_0, & ! velocities, in H m.
932  uh_l, uh_r, & ! The layer transports with the westerly (_L), easterly (_R),
933  uh_0, & ! and zero-barotropic (_0) test velocities, in H m2 s-1.
934  famt_l, famt_r, & ! The summed effective marginal face areas for the 3
935  famt_0, & ! test velocities, in H m.
936  uhtot_l, & ! The summed transport with the westerly (uhtot_L) and
937  uhtot_r ! and easterly (uhtot_R) test velocities, in H m2 s-1.
938  real :: FA_0 ! The effective face area with 0 barotropic transport, in m H.
939  real :: FA_avg ! The average effective face area, in m H, nominally given by
940  ! the realized transport divided by the barotropic velocity.
941  real :: visc_rem_lim ! The larger of visc_rem and min_visc_rem, ND. This
942  ! limiting is necessary to keep the inverse of visc_rem
943  ! from leading to large CFL numbers.
944  real :: min_visc_rem ! The smallest permitted value for visc_rem that is used
945  ! in finding the barotropic velocity that changes the
946  ! flow direction. This is necessary to keep the inverse
947  ! of visc_rem from leading to large CFL numbers.
948  real :: CFL_min ! A minimal increment in the CFL to try to ensure that the
949  ! flow is truly upwind, ND.
950  real :: Idt ! The inverse of the time step, in s-1.
951  logical :: domore
952  integer :: i, k, nz
953 
954  nz = g%ke ; idt = 1.0/dt
955  min_visc_rem = 0.1 ; cfl_min = 1e-6
956 
957  ! Diagnose the zero-transport correction, du0.
958  do i=ish-1,ieh ; zeros(i) = 0.0 ; enddo
959  call zonal_flux_adjust(u, h_in, h_l, h_r, zeros, uh_tot_0, &
960  duhdu_tot_0, du0, du_max_cfl, du_min_cfl, dt, g, &
961  cs, visc_rem, j, ish, ieh, do_i, .true.)
962 
963  ! Determine the westerly- and easterly- fluxes. Choose a sufficiently
964  ! negative velocity correction for the easterly-flux, and a sufficiently
965  ! positive correction for the westerly-flux.
966  domore = .false.
967  do i=ish-1,ieh
968  if (do_i(i)) domore = .true.
969  du_cfl(i) = (cfl_min * idt) * g%dxCu(i,j)
970  dur(i) = min(0.0,du0(i) - du_cfl(i))
971  dul(i) = max(0.0,du0(i) + du_cfl(i))
972  famt_l(i) = 0.0 ; famt_r(i) = 0.0 ; famt_0(i) = 0.0
973  uhtot_l(i) = 0.0 ; uhtot_r(i) = 0.0
974  enddo
975 
976  if (.not.domore) then
977  do k=1,nz ; do i=ish-1,ieh
978  bt_cont%FA_u_W0(i,j) = 0.0 ; bt_cont%FA_u_WW(i,j) = 0.0
979  bt_cont%uBT_WW(i,j) = 0.0
980  bt_cont%FA_u_E0(i,j) = 0.0 ; bt_cont%FA_u_EE(i,j) = 0.0
981  bt_cont%uBT_EE(i,j) = 0.0
982  enddo ; enddo
983  return
984  endif
985 
986  do k=1,nz ; do i=ish-1,ieh ; if (do_i(i)) then
987  visc_rem_lim = max(visc_rem(i,k), min_visc_rem*visc_rem_max(i))
988  if (visc_rem_lim > 0.0) then ! This is almost always true for ocean points.
989  if (u(i,j,k) + dur(i)*visc_rem_lim > -du_cfl(i)*visc_rem(i,k)) &
990  dur(i) = -(u(i,j,k) + du_cfl(i)*visc_rem(i,k)) / visc_rem_lim
991  if (u(i,j,k) + dul(i)*visc_rem_lim < du_cfl(i)*visc_rem(i,k)) &
992  dul(i) = -(u(i,j,k) - du_cfl(i)*visc_rem(i,k)) / visc_rem_lim
993  endif
994  endif ; enddo ; enddo
995 
996  do k=1,nz
997  do i=ish-1,ieh ; if (do_i(i)) then
998  u_l(i) = u(i,j,k) + dul(i) * visc_rem(i,k)
999  u_r(i) = u(i,j,k) + dur(i) * visc_rem(i,k)
1000  u_0(i) = u(i,j,k) + du0(i) * visc_rem(i,k)
1001  endif ; enddo
1002  call zonal_flux_layer(u_0, h_in(:,j,k), h_l(:,j,k), h_r(:,j,k), uh_0, &
1003  fa_marg_0, visc_rem(:,k), dt, g, j, ish, ieh, do_i, &
1004  cs%vol_CFL)
1005  call zonal_flux_layer(u_l, h_in(:,j,k), h_l(:,j,k), h_r(:,j,k), uh_l, &
1006  fa_marg_l, visc_rem(:,k), dt, g, j, ish, ieh, do_i, &
1007  cs%vol_CFL)
1008  call zonal_flux_layer(u_r, h_in(:,j,k), h_l(:,j,k), h_r(:,j,k), uh_r, &
1009  fa_marg_r, visc_rem(:,k), dt, g, j, ish, ieh, do_i, &
1010  cs%vol_CFL)
1011  do i=ish-1,ieh ; if (do_i(i)) then
1012  famt_0(i) = famt_0(i) + fa_marg_0(i)
1013  famt_l(i) = famt_l(i) + fa_marg_l(i)
1014  famt_r(i) = famt_r(i) + fa_marg_r(i)
1015  uhtot_l(i) = uhtot_l(i) + uh_l(i)
1016  uhtot_r(i) = uhtot_r(i) + uh_r(i)
1017  endif ; enddo
1018  enddo
1019  do i=ish-1,ieh ; if (do_i(i)) then
1020  fa_0 = famt_0(i) ; fa_avg = famt_0(i)
1021  if ((dul(i) - du0(i)) /= 0.0) &
1022  fa_avg = uhtot_l(i) / (dul(i) - du0(i))
1023  if (fa_avg > max(fa_0, famt_l(i))) then ; fa_avg = max(fa_0, famt_l(i))
1024  elseif (fa_avg < min(fa_0, famt_l(i))) then ; fa_0 = fa_avg ; endif
1025 
1026  bt_cont%FA_u_W0(i,j) = fa_0 ; bt_cont%FA_u_WW(i,j) = famt_l(i)
1027  if (abs(fa_0-famt_l(i)) <= 1e-12*fa_0) then ; bt_cont%uBT_WW(i,j) = 0.0 ; else
1028  bt_cont%uBT_WW(i,j) = (1.5 * (dul(i) - du0(i))) * &
1029  ((famt_l(i) - fa_avg) / (famt_l(i) - fa_0))
1030  endif
1031 
1032  fa_0 = famt_0(i) ; fa_avg = famt_0(i)
1033  if ((dur(i) - du0(i)) /= 0.0) &
1034  fa_avg = uhtot_r(i) / (dur(i) - du0(i))
1035  if (fa_avg > max(fa_0, famt_r(i))) then ; fa_avg = max(fa_0, famt_r(i))
1036  elseif (fa_avg < min(fa_0, famt_r(i))) then ; fa_0 = fa_avg ; endif
1037 
1038  bt_cont%FA_u_E0(i,j) = fa_0 ; bt_cont%FA_u_EE(i,j) = famt_r(i)
1039  if (abs(famt_r(i) - fa_0) <= 1e-12*fa_0) then ; bt_cont%uBT_EE(i,j) = 0.0 ; else
1040  bt_cont%uBT_EE(i,j) = (1.5 * (dur(i) - du0(i))) * &
1041  ((famt_r(i) - fa_avg) / (famt_r(i) - fa_0))
1042  endif
1043  else
1044  bt_cont%FA_u_W0(i,j) = 0.0 ; bt_cont%FA_u_WW(i,j) = 0.0
1045  bt_cont%uBT_WW(i,j) = 0.0
1046  bt_cont%FA_u_E0(i,j) = 0.0 ; bt_cont%FA_u_EE(i,j) = 0.0
1047  bt_cont%uBT_EE(i,j) = 0.0
1048  endif ; enddo
1049 
1050 end subroutine set_zonal_bt_cont
1051 
1052 !> Calculates the mass or volume fluxes through the meridional faces, and other related quantities.
1053 subroutine meridional_mass_flux(v, h_in, vh, dt, G, GV, CS, LB, vhbt, OBC, &
1054  visc_rem_v, v_cor, vhbt_aux, v_cor_aux, BT_cont)
1055  type(ocean_grid_type), intent(inout) :: G !< Ocean's grid structure.
1056  type(verticalgrid_type), intent(in) :: GV !< Ocean's vertical grid structure.
1057  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: v !< Meridional velocity, in m s-1.
1058  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: h_in !< Layer thickness used to
1059  !! calculate fluxes, in H.
1060  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(out) :: vh !< Volume flux through meridional
1061  !! faces = v*h*dx, H m2 s-1.
1062  real, intent(in) :: dt !< Time increment in s.
1063  type(continuity_ppm_cs), pointer :: CS !< This module's control structure.
1064  type(loop_bounds_type), intent(in) :: LB !< Loop bounds structure.
1065  type(ocean_obc_type), pointer, optional :: OBC !<
1066  !! This open boundary condition type specifies whether, where,
1067  !! and what open boundary conditions are used.
1068  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in), optional :: visc_rem_v !<
1069  !! Both the fraction of the momentum originally in a
1070  !! layer that remains after a time-step of viscosity,
1071  !! and the fraction of a time-step's worth of a
1072  !! barotropic acceleration that a layer experiences
1073  !! after viscosity is applied. Nondimensional between
1074  !! 0 (at the bottom) and 1 (far above the bottom).
1075  real, dimension(SZI_(G),SZJB_(G)), intent(in), optional :: vhbt !<
1076  !! The summed volume flux through meridional faces, H m2 s-1.
1077  real, dimension(SZI_(G),SZJB_(G)), intent(in), optional :: vhbt_aux !<
1078  !! A second set of summed volume fluxes through meridional
1079  !! faces, in H m2 s-1.
1080  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(out), optional :: v_cor !<
1081  !! The meridional velocitiess (v with a barotropic correction)
1082  !! that give vhbt as the depth-integrated transport, m s-1.
1083  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(out), optional :: v_cor_aux !<
1084  !! The meridional velocities (v with a barotropic correction)
1085  !! that give vhbt_aux as the depth-integrated transports, in m s-1.
1086  type(bt_cont_type), pointer, optional :: BT_cont !<
1087  !! A structure with elements that describe the effective
1088  ! Local variables
1089  real, dimension(SZI_(G),SZK_(G)) :: &
1090  dvhdv ! Partial derivative of vh with v, in m2.
1091  real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: &
1092  h_L, h_R ! Left and right face thicknesses, in m.
1093  real, dimension(SZI_(G)) :: &
1094  dv, & ! Corrective barotropic change in the velocity, in m s-1.
1095  dv_min_CFL, & ! Min/max limits on dv correction
1096  dv_max_CFL, & ! to avoid CFL violations
1097  dvhdv_tot_0, & ! Summed partial derivative of vh with v, in H m.
1098  vh_tot_0, & ! Summed transport with no barotropic correction in H m2 s-1.
1099  visc_rem_max ! The column maximum of visc_rem.
1100  logical, dimension(SZI_(G)) :: do_I
1101  real, dimension(SZI_(G),SZK_(G)) :: &
1102  visc_rem ! A 2-D copy of visc_rem_v or an array of 1's.
1103  real :: I_vrm ! 1.0 / visc_rem_max, nondim.
1104  real :: CFL_dt ! The maximum CFL ratio of the adjusted velocities divided by
1105  ! the time step, in s-1.
1106  real :: I_dt ! 1.0 / dt, in s-1.
1107  real :: dv_lim ! The velocity change that give a relative CFL of 1, in m s-1.
1108  real :: dy_N, dy_S ! Effective y-grid spacings to the north and south, in m.
1109  integer :: i, j, k, ish, ieh, jsh, jeh, n, nz
1110  logical :: do_aux, local_specified_BC, use_visc_rem, set_BT_cont, any_simple_OBC
1111  logical :: local_Flather_OBC, is_simple, local_open_BC
1112  type(obc_segment_type), pointer :: segment
1113 
1114  do_aux = (present(vhbt_aux) .and. present(v_cor_aux))
1115  use_visc_rem = present(visc_rem_v)
1116  local_specified_bc = .false. ; set_bt_cont = .false. ; local_flather_obc = .false.
1117  local_open_bc = .false.
1118  if (present(bt_cont)) set_bt_cont = (associated(bt_cont))
1119  if (present(obc)) then ; if (associated(obc)) then ; if (obc%OBC_pe) then
1120  local_specified_bc = obc%specified_v_BCs_exist_globally
1121  local_flather_obc = obc%Flather_u_BCs_exist_globally .or. &
1122  obc%Flather_v_BCs_exist_globally
1123  local_open_bc = obc%open_v_BCs_exist_globally
1124  endif ; endif ; endif
1125  ish = lb%ish ; ieh = lb%ieh ; jsh = lb%jsh ; jeh = lb%jeh ; nz = g%ke
1126 
1127  cfl_dt = cs%CFL_limit_adjust / dt
1128  i_dt = 1.0 / dt
1129  if (cs%aggress_adjust) cfl_dt = i_dt
1130 
1131  call cpu_clock_begin(id_clock_update)
1132 !$OMP parallel do default(none) shared(nz,ish,ieh,jsh,jeh,h_in,h_L,h_R,G,GV,LB,CS,visc_rem)
1133  do k=1,nz
1134  ! This sets h_L and h_R.
1135  if (cs%upwind_1st) then
1136  do j=jsh-1,jeh+1 ; do i=ish,ieh
1137  h_l(i,j,k) = h_in(i,j,k) ; h_r(i,j,k) = h_in(i,j,k)
1138  enddo ; enddo
1139  else
1140  call ppm_reconstruction_y(h_in(:,:,k), h_l(:,:,k), h_r(:,:,k), g, lb, &
1141  2.0*gv%Angstrom, cs%monotonic, simple_2nd=cs%simple_2nd)
1142  endif
1143  do i=ish,ieh ; visc_rem(i,k) = 1.0 ; enddo
1144  enddo
1145  if (local_open_bc) then
1146  do n=1, obc%number_of_segments
1147  segment => obc%segment(n)
1148  if (.not. segment%on_pe .or. segment%specified) cycle
1149  if (segment%direction == obc_direction_n) then
1150  j=segment%HI%JsdB
1151  do k=1,nz ; do i=segment%HI%isd,segment%HI%ied
1152  h_in(i,j+1,k) = h_in(i,j,k)
1153  h_l(i,j+1,k) = h_in(i,j,k)
1154  h_r(i,j+1,k) = h_in(i,j,k)
1155  h_r(i,j,k) = h_in(i,j,k)
1156  enddo ; enddo
1157  elseif (segment%direction == obc_direction_s) then
1158  j=segment%HI%JsdB
1159  do k=1,nz ; do i=segment%HI%isd,segment%HI%ied
1160  h_in(i,j,k) = h_in(i,j+1,k)
1161  h_l(i,j,k) = h_in(i,j+1,k)
1162  h_r(i,j,k) = h_in(i,j+1,k)
1163  h_l(i,j+1,k) = h_in(i,j+1,k)
1164  enddo ; enddo
1165  endif
1166  enddo
1167  endif
1168  call cpu_clock_end(id_clock_update)
1169 
1170  call cpu_clock_begin(id_clock_correct)
1171 !$OMP parallel do default(none) shared(ish,ieh,jsh,jeh,nz,v,h_in,h_L,h_R,vh,use_visc_rem, &
1172 !$OMP visc_rem_v,dt,G,GV,CS,local_specified_BC,OBC,vhbt,do_aux, &
1173 !$OMP set_BT_cont,CFL_dt,I_dt,v_cor,vhbt_aux, &
1174 !$OMP v_cor_aux,BT_cont, local_Flather_OBC ) &
1175 !$OMP private(do_I,dvhdv,dv,dv_max_CFL,dv_min_CFL,vh_tot_0, &
1176 !$OMP dvhdv_tot_0,visc_rem_max,I_vrm,dv_lim,dy_N, &
1177 !$OMP is_simple, &
1178 !$OMP dy_S,any_simple_OBC ) &
1179 !$OMP firstprivate(visc_rem)
1180  do j=jsh-1,jeh
1181  do i=ish,ieh ; do_i(i) = .true. ; visc_rem_max(i) = 0.0 ; enddo
1182  ! This sets vh and dvhdv.
1183  do k=1,nz
1184  if (use_visc_rem) then ; do i=ish,ieh
1185  visc_rem(i,k) = visc_rem_v(i,j,k)
1186  visc_rem_max(i) = max(visc_rem_max(i), visc_rem(i,k))
1187  enddo ; endif
1188  call merid_flux_layer(v(:,j,k), h_in(:,:,k), h_l(:,:,k), h_r(:,:,k), &
1189  vh(:,j,k), dvhdv(:,k), visc_rem(:,k), &
1190  dt, g, j, ish, ieh, do_i, cs%vol_CFL)
1191  if (local_specified_bc) then
1192  do i=ish,ieh
1193  if (obc%segment(obc%segnum_v(i,j))%specified) &
1194  vh(i,j,k) = obc%segment(obc%segnum_v(i,j))%normal_trans(i,j,k)
1195  enddo
1196  endif
1197  enddo ! k-loop
1198  if ((.not.use_visc_rem) .or. (.not.cs%use_visc_rem_max)) then ; do i=ish,ieh
1199  visc_rem_max(i) = 1.0
1200  enddo ; endif
1201 
1202  if (present(vhbt) .or. do_aux .or. set_bt_cont) then
1203  ! Set limits on dv that will keep the CFL number between -1 and 1.
1204  ! This should be adequate to keep the root bracketed in all cases.
1205  do i=ish,ieh
1206  i_vrm = 0.0
1207  if (visc_rem_max(i) > 0.0) i_vrm = 1.0 / visc_rem_max(i)
1208  if (cs%vol_CFL) then
1209  dy_s = ratio_max(g%areaT(i,j), g%dx_Cv(i,j), 1000.0*g%dyT(i,j))
1210  dy_n = ratio_max(g%areaT(i,j+1), g%dx_Cv(i,j), 1000.0*g%dyT(i,j+1))
1211  else ; dy_s = g%dyT(i,j) ; dy_n = g%dyT(i,j+1) ; endif
1212  dv_max_cfl(i) = 2.0 * (cfl_dt * dy_s) * i_vrm
1213  dv_min_cfl(i) = -2.0 * (cfl_dt * dy_n) * i_vrm
1214  vh_tot_0(i) = 0.0 ; dvhdv_tot_0(i) = 0.0
1215  enddo
1216  do k=1,nz ; do i=ish,ieh
1217  dvhdv_tot_0(i) = dvhdv_tot_0(i) + dvhdv(i,k)
1218  vh_tot_0(i) = vh_tot_0(i) + vh(i,j,k)
1219  enddo ; enddo
1220 
1221  if (use_visc_rem) then
1222  if (cs%aggress_adjust) then
1223  do k=1,nz ; do i=ish,ieh
1224  if (cs%vol_CFL) then
1225  dy_s = ratio_max(g%areaT(i,j), g%dx_Cv(i,j), 1000.0*g%dyT(i,j))
1226  dy_n = ratio_max(g%areaT(i,j+1), g%dx_Cv(i,j), 1000.0*g%dyT(i,j+1))
1227  else ; dy_s = g%dyT(i,j) ; dy_n = g%dyT(i,j+1) ; endif
1228  dv_lim = 0.499*((dy_s*i_dt - v(i,j,k)) + min(0.0,v(i,j-1,k)))
1229  if (dv_max_cfl(i) * visc_rem(i,k) > dv_lim) &
1230  dv_max_cfl(i) = dv_lim / visc_rem(i,k)
1231 
1232  dv_lim = 0.499*((-dy_n*cfl_dt - v(i,j,k)) + max(0.0,v(i,j+1,k)))
1233  if (dv_min_cfl(i) * visc_rem(i,k) < dv_lim) &
1234  dv_min_cfl(i) = dv_lim / visc_rem(i,k)
1235  enddo ; enddo
1236  else
1237  do k=1,nz ; do i=ish,ieh
1238  if (cs%vol_CFL) then
1239  dy_s = ratio_max(g%areaT(i,j), g%dx_Cv(i,j), 1000.0*g%dyT(i,j))
1240  dy_n = ratio_max(g%areaT(i,j+1), g%dx_Cv(i,j), 1000.0*g%dyT(i,j+1))
1241  else ; dy_s = g%dyT(i,j) ; dy_n = g%dyT(i,j+1) ; endif
1242  if (dv_max_cfl(i) * visc_rem(i,k) > dy_s*cfl_dt - v(i,j,k)) &
1243  dv_max_cfl(i) = (dy_s*cfl_dt - v(i,j,k)) / visc_rem(i,k)
1244  if (dv_min_cfl(i) * visc_rem(i,k) < -dy_n*cfl_dt - v(i,j,k)) &
1245  dv_min_cfl(i) = -(dy_n*cfl_dt + v(i,j,k)) / visc_rem(i,k)
1246  enddo ; enddo
1247  endif
1248  else
1249  if (cs%aggress_adjust) then
1250  do k=1,nz ; do i=ish,ieh
1251  if (cs%vol_CFL) then
1252  dy_s = ratio_max(g%areaT(i,j), g%dx_Cv(i,j), 1000.0*g%dyT(i,j))
1253  dy_n = ratio_max(g%areaT(i,j+1), g%dx_Cv(i,j), 1000.0*g%dyT(i,j+1))
1254  else ; dy_s = g%dyT(i,j) ; dy_n = g%dyT(i,j+1) ; endif
1255  dv_max_cfl(i) = min(dv_max_cfl(i), 0.499 * &
1256  ((dy_s*i_dt - v(i,j,k)) + min(0.0,v(i,j-1,k))) )
1257  dv_min_cfl(i) = max(dv_min_cfl(i), 0.499 * &
1258  ((-dy_n*i_dt - v(i,j,k)) + max(0.0,v(i,j+1,k))) )
1259  enddo ; enddo
1260  else
1261  do k=1,nz ; do i=ish,ieh
1262  if (cs%vol_CFL) then
1263  dy_s = ratio_max(g%areaT(i,j), g%dx_Cv(i,j), 1000.0*g%dyT(i,j))
1264  dy_n = ratio_max(g%areaT(i,j+1), g%dx_Cv(i,j), 1000.0*g%dyT(i,j+1))
1265  else ; dy_s = g%dyT(i,j) ; dy_n = g%dyT(i,j+1) ; endif
1266  dv_max_cfl(i) = min(dv_max_cfl(i), dy_s*cfl_dt - v(i,j,k))
1267  dv_min_cfl(i) = max(dv_min_cfl(i), -(dy_n*cfl_dt + v(i,j,k)))
1268  enddo ; enddo
1269  endif
1270  endif
1271  do i=ish,ieh
1272  dv_max_cfl(i) = max(dv_max_cfl(i),0.0)
1273  dv_min_cfl(i) = min(dv_min_cfl(i),0.0)
1274  enddo
1275 
1276  ! Up to this point, everything is shared between vhbt and vhbt_aux.
1277 
1278  any_simple_obc = .false.
1279  if (present(vhbt) .or. do_aux .or. set_bt_cont) then
1280  if (local_specified_bc .or. local_flather_obc) then ; do i=ish,ieh
1281  ! Avoid reconciling barotropic/baroclinic transports if transport is specified
1282  is_simple = obc%segment(obc%segnum_v(i,j))%specified
1283  do_i(i) = .not.(obc%segnum_v(i,j) /= obc_none .and. is_simple)
1284  any_simple_obc = any_simple_obc .or. is_simple
1285  enddo ; else ; do i=ish,ieh
1286  do_i(i) = .true.
1287  enddo ; endif
1288  endif
1289 
1290  if (present(vhbt)) then
1291  call meridional_flux_adjust(v, h_in, h_l, h_r, vhbt(:,j), vh_tot_0, &
1292  dvhdv_tot_0, dv, dv_max_cfl, dv_min_cfl, dt, g, &
1293  cs, visc_rem, j, ish, ieh, do_i, .true., vh)
1294 
1295  if (present(v_cor)) then ; do k=1,nz
1296  do i=ish,ieh ; v_cor(i,j,k) = v(i,j,k) + dv(i) * visc_rem(i,k) ; enddo
1297  if (local_specified_bc) then ; do i=ish,ieh
1298  if (obc%segment(obc%segnum_v(i,j))%specified) &
1299  v_cor(i,j,k) = obc%segment(obc%segnum_v(i,j))%normal_vel(i,j,k)
1300  enddo ; endif
1301  enddo ; endif ! v-corrected
1302  endif
1303 
1304  if (do_aux) then
1305  call meridional_flux_adjust(v, h_in, h_l, h_r, vhbt_aux(:,j), vh_tot_0, &
1306  dvhdv_tot_0, dv, dv_max_cfl, dv_min_cfl, dt, g, &
1307  cs, visc_rem, j, ish, ieh, do_i, .false.)
1308 
1309  do k=1,nz
1310  do i=ish,ieh ; v_cor_aux(i,j,k) = v(i,j,k) + dv(i) * visc_rem(i,k) ; enddo
1311  if (local_specified_bc) then ; do i=ish,ieh
1312  if (obc%segment(obc%segnum_v(i,j))%specified) &
1313  v_cor_aux(i,j,k) = obc%segment(obc%segnum_v(i,j))%normal_vel(i,j,k)
1314  enddo ; endif
1315  enddo
1316  endif ! do_aux
1317 
1318  if (set_bt_cont) then
1319  call set_merid_bt_cont(v, h_in, h_l, h_r, bt_cont, vh_tot_0, dvhdv_tot_0,&
1320  dv_max_cfl, dv_min_cfl, dt, g, cs, visc_rem, &
1321  visc_rem_max, j, ish, ieh, do_i)
1322  if (any_simple_obc) then
1323  do i=ish,ieh
1324  do_i(i) = (obc%segment(obc%segnum_v(i,j))%specified)
1325  if (do_i(i)) bt_cont%Fa_v_S0(i,j) = gv%H_subroundoff*g%dx_Cv(i,j)
1326  enddo
1327  do k=1,nz ; do i=ish,ieh ; if (do_i(i)) then
1328  if (abs(obc%segment(obc%segnum_v(i,j))%normal_vel(i,j,k)) > 0.0) &
1329  bt_cont%Fa_v_S0(i,j) = bt_cont%Fa_v_S0(i,j) + &
1330  obc%segment(obc%segnum_v(i,j))%normal_trans(i,j,k) / &
1331  obc%segment(obc%segnum_v(i,j))%normal_vel(i,j,k)
1332  endif ; enddo ; enddo
1333  do i=ish,ieh ; if (do_i(i)) then
1334  bt_cont%Fa_v_N0(i,j) = bt_cont%Fa_v_S0(i,j)
1335  bt_cont%Fa_v_SS(i,j) = bt_cont%Fa_v_S0(i,j)
1336  bt_cont%Fa_v_NN(i,j) = bt_cont%Fa_v_S0(i,j)
1337  endif ; enddo
1338  endif
1339  endif ! set_BT_cont
1340 
1341  endif ! present(vhbt) or do_aux or set_BT_cont
1342  enddo ! j-loop
1343  call cpu_clock_end(id_clock_correct)
1344 
1345  if (set_bt_cont) then ; if (associated(bt_cont%h_v)) then
1346  if (present(v_cor)) then
1347  call merid_face_thickness(v_cor, h_in, h_l, h_r, bt_cont%h_v, dt, g, lb, &
1348  cs%vol_CFL, cs%marginal_faces, visc_rem_v)
1349  else
1350  call merid_face_thickness(v, h_in, h_l, h_r, bt_cont%h_v, dt, g, lb, &
1351  cs%vol_CFL, cs%marginal_faces, visc_rem_v)
1352  endif
1353  endif ; endif
1354 
1355 end subroutine meridional_mass_flux
1356 
1357 !> Evaluates the meridional mass or volume fluxes in a layer.
1358 subroutine merid_flux_layer(v, h, h_L, h_R, vh, dvhdv, visc_rem, dt, G, J, &
1359  ish, ieh, do_I, vol_CFL)
1360  type(ocean_grid_type), intent(inout) :: G !< Ocean's grid structure.
1361  real, dimension(SZI_(G)), intent(in) :: v !< Meridional velocity, in m s-1.
1362  real, dimension(SZI_(G)), intent(in) :: visc_rem !< Both the fraction of the
1363  !! momentum originally in a layer that remains after a time-step
1364  !! of viscosity, and the fraction of a time-step's worth of a barotropic
1365  !! acceleration that a layer experiences after viscosity is applied.
1366  !! Non-dimensional between 0 (at the bottom) and 1 (far above the bottom).
1367  real, dimension(SZI_(G),SZJ_(G)), intent(in) :: h !< Layer thickness used to
1368  !! calculate fluxes, in H.
1369  real, dimension(SZI_(G),SZJ_(G)), intent(in) :: h_L !< Left thickness in the
1370  !! reconstruction, in H.
1371  real, dimension(SZI_(G),SZJ_(G)), intent(in) :: h_R !< Right thickness in the
1372  !! reconstruction, in H.
1373  real, dimension(SZI_(G)), intent(inout) :: vh !< Meridional mass or volume
1374  !! transport, in H m2 s-1.
1375  real, dimension(SZI_(G)), intent(inout) :: dvhdv !< Partial derivative of vh
1376  !! with v, in H m.
1377  real, intent(in) :: dt !< Time increment in s.
1378  integer, intent(in) :: j !< Spatial index.
1379  integer, intent(in) :: ish !< Start of index range.
1380  integer, intent(in) :: ieh !< End of index range.
1381  logical, dimension(SZI_(G)), intent(in) :: do_I !< Which i values to work on.
1382  logical, intent(in) :: vol_CFL !< If true, rescale the
1383  !! ratio of face areas to the cell areas when estimating the CFL number.
1384  ! Local variables
1385  real :: CFL ! The CFL number based on the local velocity and grid spacing, ND.
1386  real :: curv_3 ! A measure of the thickness curvature over a grid length,
1387  ! with the same units as h_in.
1388  real :: h_marg ! The marginal thickness of a flux, in m.
1389  integer :: i
1390 
1391  do i=ish,ieh ; if (do_i(i)) then
1392  if (v(i) > 0.0) then
1393  if (vol_cfl) then ; cfl = (v(i) * dt) * (g%dx_Cv(i,j) * g%IareaT(i,j))
1394  else ; cfl = v(i) * dt * g%IdyT(i,j) ; endif
1395  curv_3 = h_l(i,j) + h_r(i,j) - 2.0*h(i,j)
1396  vh(i) = g%dx_Cv(i,j) * v(i) * ( h_r(i,j) + cfl * &
1397  (0.5*(h_l(i,j) - h_r(i,j)) + curv_3*(cfl - 1.5)) )
1398  h_marg = h_r(i,j) + cfl * ((h_l(i,j) - h_r(i,j)) + &
1399  3.0*curv_3*(cfl - 1.0))
1400  elseif (v(i) < 0.0) then
1401  if (vol_cfl) then ; cfl = (-v(i) * dt) * (g%dx_Cv(i,j) * g%IareaT(i,j+1))
1402  else ; cfl = -v(i) * dt * g%IdyT(i,j+1) ; endif
1403  curv_3 = h_l(i,j+1) + h_r(i,j+1) - 2.0*h(i,j+1)
1404  vh(i) = g%dx_Cv(i,j) * v(i) * ( h_l(i,j+1) + cfl * &
1405  (0.5*(h_r(i,j+1)-h_l(i,j+1)) + curv_3*(cfl - 1.5)) )
1406  h_marg = h_l(i,j+1) + cfl * ((h_r(i,j+1)-h_l(i,j+1)) + &
1407  3.0*curv_3*(cfl - 1.0))
1408  else
1409  vh(i) = 0.0
1410  h_marg = 0.5 * (h_l(i,j+1) + h_r(i,j))
1411  endif
1412  dvhdv(i) = g%dx_Cv(i,j) * h_marg * visc_rem(i)
1413  endif ; enddo
1414 
1415 end subroutine merid_flux_layer
1416 
1417 !> Sets the effective interface thickness at each meridional velocity point.
1418 subroutine merid_face_thickness(v, h, h_L, h_R, h_v, dt, G, LB, vol_CFL, &
1419  marginal, visc_rem_v)
1420  type(ocean_grid_type), intent(inout) :: G !< Ocean's grid structure.
1421  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: v !< Meridional velocity, in m s-1.
1422  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thickness used to
1423  !! calculate fluxes, in H.
1424  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_L !< Left thickness in the
1425  !! reconstruction, in H.
1426  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_R !< Right thickness in the
1427  !! reconstruction, in H.
1428  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout) :: h_v !< Thickness at meridional faces,
1429  !! in H.
1430  real, intent(in) :: dt !< Time increment in s.
1431  type(loop_bounds_type), intent(in) :: LB !< Loop bounds structure.
1432  logical, intent(in) :: vol_CFL !<
1433  !! If true, rescale the ratio of face areas to the cell
1434  !! areas when estimating the CFL number.
1435  logical, intent(in) :: marginal !<
1436  !! If true, report the marginal face thicknesses; otherwise
1437  !! report transport-averaged thicknesses.
1438  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in), optional :: visc_rem_v !<
1439  !! Both the fraction of the momentum originally in a
1440  !! layer that remains after a time-step of viscosity,
1441  !! and the fraction of a time-step's worth of a
1442  !! barotropic acceleration that a layer experiences
1443  !! after viscosity is applied. Non-dimensional between
1444  !! 0 (at the bottom) and 1 (far above the bottom).
1445  ! Local variables
1446  real :: CFL ! The CFL number based on the local velocity and grid spacing, ND.
1447  real :: curv_3 ! A measure of the thickness curvature over a grid length,
1448  ! with the same units as h_in.
1449  real :: h_avg ! The average thickness of a flux, in H.
1450  real :: h_marg ! The marginal thickness of a flux, in H.
1451  integer :: i, j, k, ish, ieh, jsh, jeh, nz
1452  ish = lb%ish ; ieh = lb%ieh ; jsh = lb%jsh ; jeh = lb%jeh ; nz = g%ke
1453 
1454 !$OMP parallel default(none) shared(ish,ieh,jsh,jeh,nz,v,vol_CFL,dt,G, &
1455 !$OMP h_L,h_R,h,h_v,visc_rem_v,marginal) &
1456 !$OMP private(CFL,curv_3,h_marg,h_avg)
1457 !$OMP do
1458  do k=1,nz ; do j=jsh-1,jeh ; do i=ish,ieh
1459  if (v(i,j,k) > 0.0) then
1460  if (vol_cfl) then ; cfl = (v(i,j,k) * dt) * (g%dx_Cv(i,j) * g%IareaT(i,j))
1461  else ; cfl = v(i,j,k) * dt * g%IdyT(i,j) ; endif
1462  curv_3 = h_l(i,j,k) + h_r(i,j,k) - 2.0*h(i,j,k)
1463  h_avg = h_r(i,j,k) + cfl * (0.5*(h_l(i,j,k) - h_r(i,j,k)) + curv_3*(cfl - 1.5))
1464  h_marg = h_r(i,j,k) + cfl * ((h_l(i,j,k) - h_r(i,j,k)) + &
1465  3.0*curv_3*(cfl - 1.0))
1466  elseif (v(i,j,k) < 0.0) then
1467  if (vol_cfl) then ; cfl = (-v(i,j,k)*dt) * (g%dx_Cv(i,j) * g%IareaT(i,j+1))
1468  else ; cfl = -v(i,j,k) * dt * g%IdyT(i,j+1) ; endif
1469  curv_3 = h_l(i,j+1,k) + h_r(i,j+1,k) - 2.0*h(i,j+1,k)
1470  h_avg = h_l(i,j+1,k) + cfl * (0.5*(h_r(i,j+1,k)-h_l(i,j+1,k)) + curv_3*(cfl - 1.5))
1471  h_marg = h_l(i,j+1,k) + cfl * ((h_r(i,j+1,k)-h_l(i,j+1,k)) + &
1472  3.0*curv_3*(cfl - 1.0))
1473  else
1474  h_avg = 0.5 * (h_l(i,j+1,k) + h_r(i,j,k))
1475  ! The choice to use the arithmetic mean here is somewhat arbitrariy, but
1476  ! it should be noted that h_L(i+1,j,k) and h_R(i,j,k) are usually the same.
1477  h_marg = 0.5 * (h_l(i,j+1,k) + h_r(i,j,k))
1478  ! h_marg = (2.0 * h_L(i,j+1,k) * h_R(i,j,k)) / &
1479  ! (h_L(i,j+1,k) + h_R(i,j,k) + GV%H_subroundoff)
1480  endif
1481 
1482  if (marginal) then ; h_v(i,j,k) = h_marg
1483  else ; h_v(i,j,k) = h_avg ; endif
1484  enddo ; enddo ; enddo
1485 
1486  if (present(visc_rem_v)) then
1487 !$OMP do
1488  do k=1,nz ; do j=jsh-1,jeh ; do i=ish,ieh
1489  h_v(i,j,k) = h_v(i,j,k) * visc_rem_v(i,j,k)
1490  enddo ; enddo ; enddo
1491  endif
1492 !$OMP end parallel
1493 
1494 end subroutine merid_face_thickness
1495 
1496 !> Returns the barotropic velocity adjustment that gives the desired barotropic (layer-summed) transport.
1497 subroutine meridional_flux_adjust(v, h_in, h_L, h_R, vhbt, vh_tot_0, dvhdv_tot_0, &
1498  dv, dv_max_CFL, dv_min_CFL, dt, G, CS, visc_rem, &
1499  j, ish, ieh, do_I_in, full_precision, vh_3d)
1500  type(ocean_grid_type), intent(inout) :: G !< Ocean's grid structure.
1501  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: v !< Meridional velocity, in m s-1.
1502  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_in !< Layer thickness used to
1503  !! calculate fluxes, in H.
1504  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_L !< Left thickness in the
1505  !! reconstruction, in H.
1506  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_R !< Right thickness in the
1507  !! reconstruction, in H.
1508  real, dimension(SZI_(G),SZK_(G)), intent(in) :: visc_rem !<
1509  !! Both the fraction of the momentum originally in a
1510  !! layer that remains after a time-step of viscosity,
1511  !! and the fraction of a time-step's worth of a
1512  !! barotropic acceleration that a layer experiences
1513  !! after viscosity is applied. Non-dimensional between
1514  !! 0 (at the bottom) and 1 (far above the bottom).
1515  real, dimension(SZI_(G)), intent(in), optional :: vhbt !<
1516  !! The summed volume flux through meridional faces, H m2 s-1.
1517  real, dimension(SZI_(G)), intent(in) :: dv_max_CFL !< Maximum acceptable value
1518  !! of dv, in m s-1.
1519  real, dimension(SZI_(G)), intent(in) :: dv_min_CFL !< Minimum acceptable value
1520  !! of dv, in m s-1.
1521  real, dimension(SZI_(G)), intent(in) :: vh_tot_0 !<
1522  !! The summed transport with 0 adjustment, in H m2 s-1.
1523  real, dimension(SZI_(G)), intent(in) :: dvhdv_tot_0 !<
1524  !! The partial derivative of dv_err with dv at 0 adjustment, in H m.
1525  real, dimension(SZI_(G)), intent(out) :: dv !<
1526  !! The barotropic velocity adjustment, in m s-1.
1527  real, intent(in) :: dt !< Time increment in s.
1528  type(continuity_ppm_cs), pointer :: CS !< This module's control structure.
1529  integer, intent(in) :: j !< Spatial index.
1530  integer, intent(in) :: ish !< Start of index range.
1531  integer, intent(in) :: ieh !< End of index range.
1532  logical, dimension(SZI_(G)), intent(in) :: do_I_in !<
1533  !! A logical flag indicating which I values to work on.
1534  logical, intent(in), optional :: full_precision !<
1535  !! full_precision - A flag indicating how carefully to iterate. The
1536  !! default is .true. (more accurate).
1537  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(inout), optional :: vh_3d !<
1538  !! Volume flux through meridional faces = v*h*dx, H m2 s-1.
1539  ! Local variables
1540  real, dimension(SZI_(G),SZK_(G)) :: &
1541  vh_aux, & ! An auxiliary meridional volume flux, in H m s-1.
1542  dvhdv ! Partial derivative of vh with v, in H m.
1543  real, dimension(SZI_(G)) :: &
1544  vh_err, & ! Difference between vhbt and the summed vh, in H m2 s-1.
1545  vh_err_best, & ! The smallest value of vh_err found so far, in H m2 s-1.
1546  v_new, & ! The velocity with the correction added, in m s-1.
1547  dvhdv_tot,&! Summed partial derivative of vh with u, in H m.
1548  dv_min, & ! Min/max limits on dv correction based on CFL limits
1549  dv_max ! and previous iterations, in m s-1.
1550  real :: dv_prev ! The previous value of dv, in m s-1.
1551  real :: ddv ! The change in dv from the previous iteration, in m s-1.
1552  real :: tol_eta ! The tolerance for the current iteration, in m.
1553  real :: tol_vel ! The tolerance for velocity in the current iteration, m s-1.
1554  integer :: i, k, nz, itt, max_itts = 20
1555  logical :: full_prec, domore, do_I(szi_(g))
1556 
1557  nz = g%ke
1558  full_prec = .true. ; if (present(full_precision)) full_prec = full_precision
1559 
1560  vh_aux(:,:) = 0.0 ; dvhdv(:,:) = 0.0
1561 
1562  if (present(vh_3d)) then ; do k=1,nz ; do i=ish,ieh
1563  vh_aux(i,k) = vh_3d(i,j,k)
1564  enddo ; enddo ; endif
1565 
1566  do i=ish,ieh
1567  dv(i) = 0.0 ; do_i(i) = do_i_in(i)
1568  dv_max(i) = dv_max_cfl(i) ; dv_min(i) = dv_min_cfl(i)
1569  vh_err(i) = vh_tot_0(i) - vhbt(i) ; dvhdv_tot(i) = dvhdv_tot_0(i)
1570  vh_err_best(i) = abs(vh_err(i))
1571  enddo
1572 
1573  do itt=1,max_itts
1574  if (full_prec) then
1575  select case (itt)
1576  case (:1) ; tol_eta = 1e-6 * cs%tol_eta
1577  case (2) ; tol_eta = 1e-4 * cs%tol_eta
1578  case (3) ; tol_eta = 1e-2 * cs%tol_eta
1579  case default ; tol_eta = cs%tol_eta
1580  end select
1581  else
1582  tol_eta = cs%tol_eta_aux ; if (itt<=1) tol_eta = 1e-6 * cs%tol_eta_aux
1583  endif
1584  tol_vel = cs%tol_vel
1585 
1586  do i=ish,ieh
1587  if (vh_err(i) > 0.0) then ; dv_max(i) = dv(i)
1588  elseif (vh_err(i) < 0.0) then ; dv_min(i) = dv(i)
1589  else ; do_i(i) = .false. ; endif
1590  enddo
1591  domore = .false.
1592  do i=ish,ieh ; if (do_i(i)) then
1593  if ((dt*min(g%IareaT(i,j),g%IareaT(i,j+1))*abs(vh_err(i)) > tol_eta) .or.&
1594  (cs%better_iter .and. ((abs(vh_err(i)) > tol_vel * dvhdv_tot(i)) .or.&
1595  (abs(vh_err(i)) > vh_err_best(i))) )) then
1596  ! Use Newton's method, provided it stays bounded. Otherwise bisect
1597  ! the value with the appropriate bound.
1598  if (full_prec) then
1599  ddv = -vh_err(i) / dvhdv_tot(i)
1600  dv_prev = dv(i)
1601  dv(i) = dv(i) + ddv
1602  if (abs(ddv) < 1.0e-15*abs(dv(i))) then
1603  do_i(i) = .false. ! ddv is small enough to quit.
1604  elseif (ddv > 0.0) then
1605  if (dv(i) >= dv_max(i)) then
1606  dv(i) = 0.5*(dv_prev + dv_max(i))
1607  if (dv_max(i) - dv_prev < 1.0e-15*abs(dv(i))) do_i(i) = .false.
1608  endif
1609  else ! dvv(i) < 0.0
1610  if (dv(i) <= dv_min(i)) then
1611  dv(i) = 0.5*(dv_prev + dv_min(i))
1612  if (dv_prev - dv_min(i) < 1.0e-15*abs(dv(i))) do_i(i) = .false.
1613  endif
1614  endif
1615  else
1616  ! Use Newton's method, provided it stays bounded, just like above.
1617  dv(i) = dv(i) - vh_err(i) / dvhdv_tot(i)
1618  if ((dv(i) >= dv_max(i)) .or. (dv(i) <= dv_min(i))) &
1619  dv(i) = 0.5*(dv_max(i) + dv_min(i))
1620  endif
1621  if (do_i(i)) domore = .true.
1622  else
1623  do_i(i) = .false.
1624  endif
1625  endif ; enddo
1626  if (.not.domore) exit
1627 
1628  if ((itt < max_itts) .or. present(vh_3d)) then ; do k=1,nz
1629  do i=ish,ieh ; v_new(i) = v(i,j,k) + dv(i) * visc_rem(i,k) ; enddo
1630  call merid_flux_layer(v_new, h_in(:,:,k), h_l(:,:,k), h_r(:,:,k), &
1631  vh_aux(:,k), dvhdv(:,k), visc_rem(:,k), &
1632  dt, g, j, ish, ieh, do_i, cs%vol_CFL)
1633  enddo ; endif
1634 
1635  if (itt < max_itts) then
1636  do i=ish,ieh
1637  vh_err(i) = -vhbt(i) ; dvhdv_tot(i) = 0.0
1638  enddo
1639  do k=1,nz ; do i=ish,ieh
1640  vh_err(i) = vh_err(i) + vh_aux(i,k)
1641  dvhdv_tot(i) = dvhdv_tot(i) + dvhdv(i,k)
1642  enddo ; enddo
1643  do i=ish,ieh
1644  vh_err_best(i) = min(vh_err_best(i), abs(vh_err(i)))
1645  enddo
1646  endif
1647  enddo ! itt-loop
1648  ! If there are any faces which have not converged to within the tolerance,
1649  ! so-be-it, or else use a final upwind correction?
1650  ! This never seems to happen with 20 iterations as max_itt.
1651 
1652  if (present(vh_3d)) then ; do k=1,nz ; do i=ish,ieh
1653  vh_3d(i,j,k) = vh_aux(i,k)
1654  enddo ; enddo ; endif
1655 
1656 end subroutine meridional_flux_adjust
1657 
1658 !> Sets of a structure that describes the meridional barotropic volume or mass fluxes as a
1659 !! function of barotropic flow to agree closely with the sum of the layer's transports.
1660 subroutine set_merid_bt_cont(v, h_in, h_L, h_R, BT_cont, vh_tot_0, dvhdv_tot_0, &
1661  dv_max_CFL, dv_min_CFL, dt, G, CS, visc_rem, &
1662  visc_rem_max, j, ish, ieh, do_I)
1663  type(ocean_grid_type), intent(inout) :: G !< Ocean's grid structure.
1664  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: v !< Meridional velocity, in m s-1.
1665  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_in !< Layer thickness used to
1666  !! calculate fluxes, in H.
1667  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_L !< Left thickness in the
1668  !! reconstruction, in H.
1669  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h_R !< Right thickness in the
1670  !! reconstruction, in H.
1671  type(bt_cont_type), intent(inout) :: BT_cont !<
1672  !! A structure with elements that describe the effective
1673  !! open face areas as a function of barotropic flow.
1674  real, dimension(SZI_(G)), intent(in) :: vh_tot_0 !<
1675  !! The summed transport with 0 adjustment, in H m2 s-1.
1676  real, dimension(SZI_(G)), intent(in) :: dvhdv_tot_0 !<
1677  !! The partial derivative of du_err with dv at 0 adjustment, in H m.
1678  real, dimension(SZI_(G)), intent(in) :: dv_max_CFL !< Maximum acceptable value
1679  !! of dv, in m s-1.
1680  real, dimension(SZI_(G)), intent(in) :: dv_min_CFL !< Minimum acceptable value
1681  !! of dv, in m s-1.
1682  real, intent(in) :: dt !< Time increment in s.
1683  type(continuity_ppm_cs), pointer :: CS !< This module's control structure.
1684  real, dimension(SZI_(G),SZK_(G)), intent(in) :: visc_rem !<
1685  !! Both the fraction of the momentum originally in a
1686  !! layer that remains after a time-step of viscosity,
1687  !! and the fraction of a time-step's worth of a
1688  !! barotropic acceleration that a layer experiences
1689  !! after viscosity is applied. Non-dimensional between
1690  !! 0 (at the bottom) and 1 (far above the bottom).
1691  real, dimension(SZI_(G)), intent(in) :: visc_rem_max !< Maximum allowable visc_rem.
1692  integer, intent(in) :: j !< Spatial index.
1693  integer, intent(in) :: ish !< Start of index range.
1694  integer, intent(in) :: ieh !< End of index range.
1695  logical, dimension(SZI_(G)), intent(in) :: do_I !<
1696  !! A logical flag indicating which I values to work on.
1697  ! Local variables
1698  real, dimension(SZI_(G)) :: &
1699  dv0, & ! The barotropic velocity increment that gives 0 transport, m s-1.
1700  dvL, dvR, & ! The barotropic velocity increments that give the southerly
1701  ! (dvL) and northerly (dvR) test velocities.
1702  zeros, & ! An array of full of 0's.
1703  dv_cfl, & ! The velocity increment that corresponds to CFL_min, in m s-1.
1704  v_l, v_r, & ! The southerly (v_L), northerly (v_R), and zero-barotropic
1705  v_0, & ! transport (v_0) layer test velocities, in m s-1.
1706  fa_marg_l, & ! The effective layer marginal face areas with the southerly
1707  fa_marg_r, & ! (_L), northerly (_R), and zero-barotropic (_0) test
1708  fa_marg_0, & ! velocities, in H m.
1709  vh_l, vh_r, & ! The layer transports with the southerly (_L), northerly (_R)
1710  vh_0, & ! and zero-barotropic (_0) test velocities, in H m2 s-1.
1711  famt_l, famt_r, & ! The summed effective marginal face areas for the 3
1712  famt_0, & ! test velocities, in H m.
1713  vhtot_l, & ! The summed transport with the southerly (vhtot_L) and
1714  vhtot_r ! and northerly (vhtot_R) test velocities, in H m2 s-1.
1715  real :: FA_0 ! The effective face area with 0 barotropic transport, in m H.
1716  real :: FA_avg ! The average effective face area, in m H, nominally given by
1717  ! the realized transport divided by the barotropic velocity.
1718  real :: visc_rem_lim ! The larger of visc_rem and min_visc_rem, ND. This
1719  ! limiting is necessary to keep the inverse of visc_rem
1720  ! from leading to large CFL numbers.
1721  real :: min_visc_rem ! The smallest permitted value for visc_rem that is used
1722  ! in finding the barotropic velocity that changes the
1723  ! flow direction. This is necessary to keep the inverse
1724  ! of visc_rem from leading to large CFL numbers.
1725  real :: CFL_min ! A minimal increment in the CFL to try to ensure that the
1726  ! flow is truly upwind, ND.
1727  real :: Idt ! The inverse of the time step, in s-1.
1728  logical :: domore
1729  integer :: i, k, nz
1730 
1731  nz = g%ke ; idt = 1.0/dt
1732  min_visc_rem = 0.1 ; cfl_min = 1e-6
1733 
1734  ! Diagnose the zero-transport correction, dv0.
1735  do i=ish,ieh ; zeros(i) = 0.0 ; enddo
1736  call meridional_flux_adjust(v, h_in, h_l, h_r, zeros, vh_tot_0, &
1737  dvhdv_tot_0, dv0, dv_max_cfl, dv_min_cfl, dt, g, &
1738  cs, visc_rem, j, ish, ieh, do_i, .true.)
1739 
1740  ! Determine the southerly- and northerly- fluxes. Choose a sufficiently
1741  ! negative velocity correction for the northerly-flux, and a sufficiently
1742  ! positive correction for the southerly-flux.
1743  domore = .false.
1744  do i=ish,ieh ; if (do_i(i)) then
1745  domore = .true.
1746  dv_cfl(i) = (cfl_min * idt) * g%dyCv(i,j)
1747  dvr(i) = min(0.0,dv0(i) - dv_cfl(i))
1748  dvl(i) = max(0.0,dv0(i) + dv_cfl(i))
1749  famt_l(i) = 0.0 ; famt_r(i) = 0.0 ; famt_0(i) = 0.0
1750  vhtot_l(i) = 0.0 ; vhtot_r(i) = 0.0
1751  endif ; enddo
1752 
1753  if (.not.domore) then
1754  do k=1,nz ; do i=ish,ieh
1755  bt_cont%FA_v_S0(i,j) = 0.0 ; bt_cont%FA_v_SS(i,j) = 0.0
1756  bt_cont%vBT_SS(i,j) = 0.0
1757  bt_cont%FA_v_N0(i,j) = 0.0 ; bt_cont%FA_v_NN(i,j) = 0.0
1758  bt_cont%vBT_NN(i,j) = 0.0
1759  enddo ; enddo
1760  return
1761  endif
1762 
1763  do k=1,nz ; do i=ish,ieh ; if (do_i(i)) then
1764  visc_rem_lim = max(visc_rem(i,k), min_visc_rem*visc_rem_max(i))
1765  if (visc_rem_lim > 0.0) then ! This is almost always true for ocean points.
1766  if (v(i,j,k) + dvr(i)*visc_rem_lim > -dv_cfl(i)*visc_rem(i,k)) &
1767  dvr(i) = -(v(i,j,k) + dv_cfl(i)*visc_rem(i,k)) / visc_rem_lim
1768  if (v(i,j,k) + dvl(i)*visc_rem_lim < dv_cfl(i)*visc_rem(i,k)) &
1769  dvl(i) = -(v(i,j,k) - dv_cfl(i)*visc_rem(i,k)) / visc_rem_lim
1770  endif
1771  endif ; enddo ; enddo
1772  do k=1,nz
1773  do i=ish,ieh ; if (do_i(i)) then
1774  v_l(i) = v(i,j,k) + dvl(i) * visc_rem(i,k)
1775  v_r(i) = v(i,j,k) + dvr(i) * visc_rem(i,k)
1776  v_0(i) = v(i,j,k) + dv0(i) * visc_rem(i,k)
1777  endif ; enddo
1778  call merid_flux_layer(v_0, h_in(:,:,k), h_l(:,:,k), h_r(:,:,k), vh_0, &
1779  fa_marg_0, visc_rem(:,k), dt, g, j, ish, ieh, do_i, &
1780  cs%vol_CFL)
1781  call merid_flux_layer(v_l, h_in(:,:,k), h_l(:,:,k), h_r(:,:,k), vh_l, &
1782  fa_marg_l, visc_rem(:,k), dt, g, j, ish, ieh, do_i, &
1783  cs%vol_CFL)
1784  call merid_flux_layer(v_r, h_in(:,:,k), h_l(:,:,k), h_r(:,:,k), vh_r, &
1785  fa_marg_r, visc_rem(:,k), dt, g, j, ish, ieh, do_i, &
1786  cs%vol_CFL)
1787  do i=ish,ieh ; if (do_i(i)) then
1788  famt_0(i) = famt_0(i) + fa_marg_0(i)
1789  famt_l(i) = famt_l(i) + fa_marg_l(i)
1790  famt_r(i) = famt_r(i) + fa_marg_r(i)
1791  vhtot_l(i) = vhtot_l(i) + vh_l(i)
1792  vhtot_r(i) = vhtot_r(i) + vh_r(i)
1793  endif ; enddo
1794  enddo
1795  do i=ish,ieh ; if (do_i(i)) then
1796  fa_0 = famt_0(i) ; fa_avg = famt_0(i)
1797  if ((dvl(i) - dv0(i)) /= 0.0) &
1798  fa_avg = vhtot_l(i) / (dvl(i) - dv0(i))
1799  if (fa_avg > max(fa_0, famt_l(i))) then ; fa_avg = max(fa_0, famt_l(i))
1800  elseif (fa_avg < min(fa_0, famt_l(i))) then ; fa_0 = fa_avg ; endif
1801  bt_cont%FA_v_S0(i,j) = fa_0 ; bt_cont%FA_v_SS(i,j) = famt_l(i)
1802  if (abs(fa_0-famt_l(i)) <= 1e-12*fa_0) then ; bt_cont%vBT_SS(i,j) = 0.0 ; else
1803  bt_cont%vBT_SS(i,j) = (1.5 * (dvl(i) - dv0(i))) * &
1804  ((famt_l(i) - fa_avg) / (famt_l(i) - fa_0))
1805  endif
1806 
1807  fa_0 = famt_0(i) ; fa_avg = famt_0(i)
1808  if ((dvr(i) - dv0(i)) /= 0.0) &
1809  fa_avg = vhtot_r(i) / (dvr(i) - dv0(i))
1810  if (fa_avg > max(fa_0, famt_r(i))) then ; fa_avg = max(fa_0, famt_r(i))
1811  elseif (fa_avg < min(fa_0, famt_r(i))) then ; fa_0 = fa_avg ; endif
1812  bt_cont%FA_v_N0(i,j) = fa_0 ; bt_cont%FA_v_NN(i,j) = famt_r(i)
1813  if (abs(famt_r(i) - fa_0) <= 1e-12*fa_0) then ; bt_cont%vBT_NN(i,j) = 0.0 ; else
1814  bt_cont%vBT_NN(i,j) = (1.5 * (dvr(i) - dv0(i))) * &
1815  ((famt_r(i) - fa_avg) / (famt_r(i) - fa_0))
1816  endif
1817  else
1818  bt_cont%FA_v_S0(i,j) = 0.0 ; bt_cont%FA_v_SS(i,j) = 0.0
1819  bt_cont%vBT_SS(i,j) = 0.0
1820  bt_cont%FA_v_N0(i,j) = 0.0 ; bt_cont%FA_v_NN(i,j) = 0.0
1821  bt_cont%vBT_NN(i,j) = 0.0
1822  endif ; enddo
1823 
1824 end subroutine set_merid_bt_cont
1825 
1826 !> Calculates left/right edge values for PPM reconstruction.
1827 subroutine ppm_reconstruction_x(h_in, h_L, h_R, G, LB, h_min, monotonic, simple_2nd)
1828  type(ocean_grid_type), intent(in) :: G !< Ocean's grid structure.
1829  real, dimension(SZI_(G),SZJ_(G)), intent(in) :: h_in !< Layer thickness, in H.
1830  real, dimension(SZI_(G),SZJ_(G)), intent(out) :: h_L !< Left thickness in the
1831  !! reconstruction, in H.
1832  real, dimension(SZI_(G),SZJ_(G)), intent(out) :: h_R !< Right thickness in the
1833  !! reconstruction, in H.
1834  type(loop_bounds_type), intent(in) :: LB !< Active loop bounds structure.
1835  real, intent(in) :: h_min !< The minimum thickness
1836  !! that can be obtained by a concave parabolic fit.
1837  logical, optional, intent(in) :: monotonic !< If true, use the
1838  !! Colella & Woodward monotonic limiter.
1839  !! Otherwise use a simple positive-definite limiter.
1840  logical, optional, intent(in) :: simple_2nd !< If true, use the
1841  !! arithmetic mean thicknesses as the default edge values
1842  !! for a simple 2nd order scheme.
1843 
1844  ! Local variables with useful mnemonic names.
1845  real, dimension(SZI_(G),SZJ_(G)) :: slp ! The slopes.
1846  real, parameter :: oneSixth = 1./6.
1847  real :: h_ip1, h_im1
1848  real :: dMx, dMn
1849  logical :: use_CW84, use_2nd
1850  character(len=256) :: mesg
1851  integer :: i, j, isl, iel, jsl, jel, stencil
1852 
1853  use_cw84 = .false. ; if (present(monotonic)) use_cw84 = monotonic
1854  use_2nd = .false. ; if (present(simple_2nd)) use_2nd = simple_2nd
1855  isl = lb%ish-1 ; iel = lb%ieh+1 ; jsl = lb%jsh ; jel = lb%jeh
1856 
1857  ! This is the stencil of the reconstruction, not the scheme overall.
1858  stencil = 2 ; if (use_2nd) stencil = 1
1859 
1860  if ((isl-stencil < g%isd) .or. (iel+stencil > g%ied)) then
1861  write(mesg,'("In MOM_continuity_PPM, PPM_reconstruction_x called with a ", &
1862  & "x-halo that needs to be increased by ",i2,".")') &
1863  stencil + max(g%isd-isl,iel-g%ied)
1864  call mom_error(fatal,mesg)
1865  endif
1866  if ((jsl < g%jsd) .or. (jel > g%jed)) then
1867  write(mesg,'("In MOM_continuity_PPM, PPM_reconstruction_x called with a ", &
1868  & "y-halo that needs to be increased by ",i2,".")') &
1869  max(g%jsd-jsl,jel-g%jed)
1870  call mom_error(fatal,mesg)
1871  endif
1872 
1873  if (use_2nd) then
1874  do j=jsl,jel ; do i=isl,iel
1875  h_im1 = g%mask2dT(i-1,j) * h_in(i-1,j) + (1.0-g%mask2dT(i-1,j)) * h_in(i,j)
1876  h_ip1 = g%mask2dT(i+1,j) * h_in(i+1,j) + (1.0-g%mask2dT(i+1,j)) * h_in(i,j)
1877  h_l(i,j) = 0.5*( h_im1 + h_in(i,j) )
1878  h_r(i,j) = 0.5*( h_ip1 + h_in(i,j) )
1879  enddo ; enddo
1880  else
1881  do j=jsl,jel ; do i=isl-1,iel+1
1882  if ((g%mask2dT(i-1,j) * g%mask2dT(i,j) * g%mask2dT(i+1,j)) == 0.0) then
1883  slp(i,j) = 0.0
1884  else
1885  ! This uses a simple 2nd order slope.
1886  slp(i,j) = 0.5 * (h_in(i+1,j) - h_in(i-1,j))
1887  ! Monotonic constraint, see Eq. B2 in Lin 1994, MWR (132)
1888  dmx = max(h_in(i+1,j), h_in(i-1,j), h_in(i,j)) - h_in(i,j)
1889  dmn = h_in(i,j) - min(h_in(i+1,j), h_in(i-1,j), h_in(i,j))
1890  slp(i,j) = sign(1.,slp(i,j)) * min(abs(slp(i,j)), 2. * min(dmx, dmn))
1891  ! * (G%mask2dT(i-1,j) * G%mask2dT(i,j) * G%mask2dT(i+1,j))
1892  endif
1893  enddo; enddo
1894 
1895  do j=jsl,jel ; do i=isl,iel
1896  ! Neighboring values should take into account any boundaries. The 3
1897  ! following sets of expressions are equivalent.
1898  ! h_im1 = h_in(i-1,j,k) ; if (G%mask2dT(i-1,j) < 0.5) h_im1 = h_in(i,j)
1899  ! h_ip1 = h_in(i+1,j,k) ; if (G%mask2dT(i+1,j) < 0.5) h_ip1 = h_in(i,j)
1900  h_im1 = g%mask2dT(i-1,j) * h_in(i-1,j) + (1.0-g%mask2dT(i-1,j)) * h_in(i,j)
1901  h_ip1 = g%mask2dT(i+1,j) * h_in(i+1,j) + (1.0-g%mask2dT(i+1,j)) * h_in(i,j)
1902  ! Left/right values following Eq. B2 in Lin 1994, MWR (132)
1903  h_l(i,j) = 0.5*( h_im1 + h_in(i,j) ) + onesixth*( slp(i-1,j) - slp(i,j) )
1904  h_r(i,j) = 0.5*( h_ip1 + h_in(i,j) ) + onesixth*( slp(i,j) - slp(i+1,j) )
1905  enddo; enddo
1906  endif
1907 
1908  if (use_cw84) then
1909  call ppm_limit_cw84(h_in, h_l, h_r, g, isl, iel, jsl, jel)
1910  else
1911  call ppm_limit_pos(h_in, h_l, h_r, h_min, g, isl, iel, jsl, jel)
1912  endif
1913 
1914  return
1915 end subroutine ppm_reconstruction_x
1916 
1917 !> Calculates left/right edge values for PPM reconstruction.
1918 subroutine ppm_reconstruction_y(h_in, h_L, h_R, G, LB, h_min, monotonic, simple_2nd)
1919  type(ocean_grid_type), intent(in) :: G !< Ocean's grid structure.
1920  real, dimension(SZI_(G),SZJ_(G)), intent(in) :: h_in !< Layer thickness, in H.
1921  real, dimension(SZI_(G),SZJ_(G)), intent(out) :: h_L !< Left thickness in the
1922  !! reconstruction, in H.
1923  real, dimension(SZI_(G),SZJ_(G)), intent(out) :: h_R !< Right thickness in the
1924  !! reconstruction, in H.
1925  type(loop_bounds_type), intent(in) :: LB !< Active loop bounds structure.
1926  real, intent(in) :: h_min !< The minimum thickness
1927  !! that can be obtained by a concave parabolic fit.
1928  logical, optional, intent(in) :: monotonic !< If true, use the
1929  !! Colella & Woodward monotonic limiter.
1930  !! Otherwise use a simple positive-definite limiter.
1931  logical, optional, intent(in) :: simple_2nd !< If true, use the
1932  !! arithmetic mean thicknesses as the default edge values
1933  !! for a simple 2nd order scheme.
1934 
1935  ! Local variables with useful mnemonic names.
1936  real, dimension(SZI_(G),SZJ_(G)) :: slp ! The slopes.
1937  real, parameter :: oneSixth = 1./6.
1938  real :: h_jp1, h_jm1
1939  real :: dMx, dMn
1940  logical :: use_CW84, use_2nd
1941  character(len=256) :: mesg
1942  integer :: i, j, isl, iel, jsl, jel, stencil
1943 
1944  use_cw84 = .false. ; if (present(monotonic)) use_cw84 = monotonic
1945  use_2nd = .false. ; if (present(simple_2nd)) use_2nd = simple_2nd
1946  isl = lb%ish ; iel = lb%ieh ; jsl = lb%jsh-1 ; jel = lb%jeh+1
1947 
1948  ! This is the stencil of the reconstruction, not the scheme overall.
1949  stencil = 2 ; if (use_2nd) stencil = 1
1950 
1951  if ((isl < g%isd) .or. (iel > g%ied)) then
1952  write(mesg,'("In MOM_continuity_PPM, PPM_reconstruction_y called with a ", &
1953  & "x-halo that needs to be increased by ",i2,".")') &
1954  max(g%isd-isl,iel-g%ied)
1955  call mom_error(fatal,mesg)
1956  endif
1957  if ((jsl-stencil < g%jsd) .or. (jel+stencil > g%jed)) then
1958  write(mesg,'("In MOM_continuity_PPM, PPM_reconstruction_y called with a ", &
1959  & "y-halo that needs to be increased by ",i2,".")') &
1960  stencil + max(g%jsd-jsl,jel-g%jed)
1961  call mom_error(fatal,mesg)
1962  endif
1963 
1964  if (use_2nd) then
1965  do j=jsl,jel ; do i=isl,iel
1966  h_jm1 = g%mask2dT(i,j-1) * h_in(i,j-1) + (1.0-g%mask2dT(i,j-1)) * h_in(i,j)
1967  h_jp1 = g%mask2dT(i,j+1) * h_in(i,j+1) + (1.0-g%mask2dT(i,j+1)) * h_in(i,j)
1968  h_l(i,j) = 0.5*( h_jm1 + h_in(i,j) )
1969  h_r(i,j) = 0.5*( h_jp1 + h_in(i,j) )
1970  enddo ; enddo
1971  else
1972  do j=jsl-1,jel+1 ; do i=isl,iel
1973  if ((g%mask2dT(i,j-1) * g%mask2dT(i,j) * g%mask2dT(i,j+1)) == 0.0) then
1974  slp(i,j) = 0.0
1975  else
1976  ! This uses a simple 2nd order slope.
1977  slp(i,j) = 0.5 * (h_in(i,j+1) - h_in(i,j-1))
1978  ! Monotonic constraint, see Eq. B2 in Lin 1994, MWR (132)
1979  dmx = max(h_in(i,j+1), h_in(i,j-1), h_in(i,j)) - h_in(i,j)
1980  dmn = h_in(i,j) - min(h_in(i,j+1), h_in(i,j-1), h_in(i,j))
1981  slp(i,j) = sign(1.,slp(i,j)) * min(abs(slp(i,j)), 2. * min(dmx, dmn))
1982  ! * (G%mask2dT(i,j-1) * G%mask2dT(i,j) * G%mask2dT(i,j+1))
1983  endif
1984  enddo ; enddo
1985 
1986  do j=jsl,jel ; do i=isl,iel
1987  ! Neighboring values should take into account any boundaries. The 3
1988  ! following sets of expressions are equivalent.
1989  h_jm1 = g%mask2dT(i,j-1) * h_in(i,j-1) + (1.0-g%mask2dT(i,j-1)) * h_in(i,j)
1990  h_jp1 = g%mask2dT(i,j+1) * h_in(i,j+1) + (1.0-g%mask2dT(i,j+1)) * h_in(i,j)
1991  ! Left/right values following Eq. B2 in Lin 1994, MWR (132)
1992  h_l(i,j) = 0.5*( h_jm1 + h_in(i,j) ) + onesixth*( slp(i,j-1) - slp(i,j) )
1993  h_r(i,j) = 0.5*( h_jp1 + h_in(i,j) ) + onesixth*( slp(i,j) - slp(i,j+1) )
1994  enddo ; enddo
1995  endif
1996 
1997  if (use_cw84) then
1998  call ppm_limit_cw84(h_in, h_l, h_r, g, isl, iel, jsl, jel)
1999  else
2000  call ppm_limit_pos(h_in, h_l, h_r, h_min, g, isl, iel, jsl, jel)
2001  endif
2002 
2003  return
2004 end subroutine ppm_reconstruction_y
2005 
2006 !> This subroutine limits the left/right edge values of the PPM reconstruction
2007 !! to give a reconstruction that is positive-definite. Here this is
2008 !! reinterpreted as giving a constant thickness if the mean thickness is less
2009 !! than h_min, with a minimum of h_min otherwise.
2010 subroutine ppm_limit_pos(h_in, h_L, h_R, h_min, G, iis, iie, jis, jie)
2011  type(ocean_grid_type), intent(in) :: G !< Ocean's grid structure.
2012  real, dimension(SZI_(G),SZJ_(G)), intent(in) :: h_in !< Layer thickness, in H.
2013  real, dimension(SZI_(G),SZJ_(G)), intent(inout) :: h_L !< Left thickness in the
2014  !! reconstruction, in H.
2015  real, dimension(SZI_(G),SZJ_(G)), intent(inout) :: h_R !< Right thickness in the
2016  !! reconstruction, in H.
2017  real, intent(in) :: h_min !< The minimum thickness
2018  !! that can be obtained by a concave parabolic fit.
2019  integer, intent(in) :: iis !< Start of i index range.
2020  integer, intent(in) :: iie !< End of i index range.
2021  integer, intent(in) :: jis !< Start of j index range.
2022  integer, intent(in) :: jie !< End of j index range.
2023 
2024 ! Local variables
2025  real :: curv, dh, scale
2026  character(len=256) :: mesg
2027  integer :: i,j
2028 
2029  do j=jis,jie ; do i=iis,iie
2030  ! This limiter prevents undershooting minima within the domain with
2031  ! values less than h_min.
2032  curv = 3.0*(h_l(i,j) + h_r(i,j) - 2.0*h_in(i,j))
2033  if (curv > 0.0) then ! Only minima are limited.
2034  dh = h_r(i,j) - h_l(i,j)
2035  if (abs(dh) < curv) then ! The parabola's minimum is within the cell.
2036  if (h_in(i,j) <= h_min) then
2037  h_l(i,j) = h_in(i,j) ; h_r(i,j) = h_in(i,j)
2038  elseif (12.0*curv*(h_in(i,j) - h_min) < (curv**2 + 3.0*dh**2)) then
2039  ! The minimum value is h_in - (curv^2 + 3*dh^2)/(12*curv), and must
2040  ! be limited in this case. 0 < scale < 1.
2041  scale = 12.0*curv*(h_in(i,j) - h_min) / (curv**2 + 3.0*dh**2)
2042  h_l(i,j) = h_in(i,j) + scale*(h_l(i,j) - h_in(i,j))
2043  h_r(i,j) = h_in(i,j) + scale*(h_r(i,j) - h_in(i,j))
2044  endif
2045  endif
2046  endif
2047  enddo ; enddo
2048 
2049 end subroutine ppm_limit_pos
2050 
2051 !> This subroutine limits the left/right edge values of the PPM reconstruction
2052 !! according to the monotonic prescription of Colella and Woodward, 1984.
2053 subroutine ppm_limit_cw84(h_in, h_L, h_R, G, iis, iie, jis, jie)
2054  type(ocean_grid_type), intent(in) :: G !< Ocean's grid structure.
2055  real, dimension(SZI_(G),SZJ_(G)), intent(in) :: h_in !< Layer thickness, in H.
2056  real, dimension(SZI_(G),SZJ_(G)), intent(inout) :: h_L !< Left thickness in the
2057  !! reconstruction, in H.
2058  real, dimension(SZI_(G),SZJ_(G)), intent(inout) :: h_R !< Right thickness in the
2059  !! reconstruction, in H.
2060  integer, intent(in) :: iis !< Start of i index range.
2061  integer, intent(in) :: iie !< End of i index range.
2062  integer, intent(in) :: jis !< Start of j index range.
2063  integer, intent(in) :: jie !< End of j index range.
2064 
2065  ! Local variables
2066  real :: h_i, RLdiff, RLdiff2, RLmean, FunFac
2067  character(len=256) :: mesg
2068  integer :: i,j
2069 
2070  do j=jis,jie ; do i=iis,iie
2071  ! This limiter monotonizes the parabola following
2072  ! Colella and Woodward, 1984, Eq. 1.10
2073  h_i = h_in(i,j)
2074  if ( ( h_r(i,j) - h_i ) * ( h_i - h_l(i,j) ) <= 0. ) then
2075  h_l(i,j) = h_i ; h_r(i,j) = h_i
2076  else
2077  rldiff = h_r(i,j) - h_l(i,j) ! Difference of edge values
2078  rlmean = 0.5 * ( h_r(i,j) + h_l(i,j) ) ! Mean of edge values
2079  funfac = 6. * rldiff * ( h_i - rlmean ) ! Some funny factor
2080  rldiff2 = rldiff * rldiff ! Square of difference
2081  if ( funfac > rldiff2 ) h_l(i,j) = 3. * h_i - 2. * h_r(i,j)
2082  if ( funfac < -rldiff2 ) h_r(i,j) = 3. * h_i - 2. * h_l(i,j)
2083  endif
2084  enddo ; enddo
2085 
2086  return
2087 end subroutine ppm_limit_cw84
2088 
2089 !> Return the maximum ratio of a/b or maxrat.
2090 function ratio_max(a, b, maxrat) result(ratio)
2091  real, intent(in) :: a !< Numerator
2092  real, intent(in) :: b !< Denominator
2093  real, intent(in) :: maxrat !< Maximum value of ratio.
2094  real :: ratio !< Return value.
2095 
2096  if (abs(a) > abs(maxrat*b)) then
2097  ratio = maxrat
2098  else
2099  ratio = a / b
2100  endif
2101 end function ratio_max
2102 
2103 !> Initializes continuity_ppm_cs
2104 subroutine continuity_ppm_init(Time, G, GV, param_file, diag, CS)
2105  type(time_type), target, intent(in) :: Time !< Time increment in s.
2106  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
2107  type(verticalgrid_type), intent(in) :: GV !< Vertical grid structure.
2108  type(param_file_type), intent(in) :: param_file !< A structure indicating
2109  !! the open file to parse for model parameter values.
2110  type(diag_ctrl), target, intent(inout) :: diag !< A structure that is used to
2111  !! regulate diagnostic output.
2112  type(continuity_ppm_cs), pointer :: CS !< Module's control structure.
2113 !> This include declares and sets the variable "version".
2114 #include "version_variable.h"
2115  character(len=40) :: mdl = "MOM_continuity_PPM" ! This module's name.
2116 
2117  if (associated(cs)) then
2118  call mom_error(warning, "continuity_PPM_init called with associated control structure.")
2119  return
2120  endif
2121  allocate(cs)
2122 
2123 ! Read all relevant parameters and write them to the model log.
2124  call log_version(param_file, mdl, version, "")
2125  call get_param(param_file, mdl, "MONOTONIC_CONTINUITY", cs%monotonic, &
2126  "If true, CONTINUITY_PPM uses the Colella and Woodward \n"//&
2127  "monotonic limiter. The default (false) is to use a \n"//&
2128  "simple positive definite limiter.", default=.false.)
2129  call get_param(param_file, mdl, "SIMPLE_2ND_PPM_CONTINUITY", cs%simple_2nd, &
2130  "If true, CONTINUITY_PPM uses a simple 2nd order \n"//&
2131  "(arithmetic mean) interpolation of the edge values. \n"//&
2132  "This may give better PV conservation propterties. While \n"//&
2133  "it formally reduces the accuracy of the continuity \n"//&
2134  "solver itself in the strongly advective limit, it does \n"//&
2135  "not reduce the overall order of accuracy of the dynamic \n"//&
2136  "core.", default=.false.)
2137  call get_param(param_file, mdl, "UPWIND_1ST_CONTINUITY", cs%upwind_1st, &
2138  "If true, CONTINUITY_PPM becomes a 1st-order upwind \n"//&
2139  "continuity solver. This scheme is highly diffusive \n"//&
2140  "but may be useful for debugging or in single-column \n"//&
2141  "mode where its minimal stencil is useful.", default=.false.)
2142  call get_param(param_file, mdl, "ETA_TOLERANCE", cs%tol_eta, &
2143  "The tolerance for the differences between the \n"//&
2144  "barotropic and baroclinic estimates of the sea surface \n"//&
2145  "height due to the fluxes through each face. The total \n"//&
2146  "tolerance for SSH is 4 times this value. The default \n"//&
2147  "is 0.5*NK*ANGSTROM, and this should not be set less x\n"//&
2148  "than about 10^-15*MAXIMUM_DEPTH.", units="m", &
2149  default=0.5*g%ke*gv%Angstrom_z)
2150 
2151  call get_param(param_file, mdl, "ETA_TOLERANCE_AUX", cs%tol_eta_aux, &
2152  "The tolerance for free-surface height discrepancies \n"//&
2153  "between the barotropic solution and the sum of the \n"//&
2154  "layer thicknesses when calculating the auxiliary \n"//&
2155  "corrected velocities. By default, this is the same as \n"//&
2156  "ETA_TOLERANCE, but can be made larger for efficiency.", &
2157  units="m", default=cs%tol_eta)
2158  call get_param(param_file, mdl, "VELOCITY_TOLERANCE", cs%tol_vel, &
2159  "The tolerance for barotropic velocity discrepancies \n"//&
2160  "between the barotropic solution and the sum of the \n"//&
2161  "layer thicknesses.", units="m s-1", default=3.0e8) ! The speed of light is the default.
2162 
2163  call get_param(param_file, mdl, "CONT_PPM_AGGRESS_ADJUST", cs%aggress_adjust,&
2164  "If true, allow the adjusted velocities to have a \n"//&
2165  "relative CFL change up to 0.5.", default=.false.)
2166  cs%vol_CFL = cs%aggress_adjust
2167  call get_param(param_file, mdl, "CONT_PPM_VOLUME_BASED_CFL", cs%vol_CFL, &
2168  "If true, use the ratio of the open face lengths to the \n"//&
2169  "tracer cell areas when estimating CFL numbers. The \n"//&
2170  "default is set by CONT_PPM_AGGRESS_ADJUST.", &
2171  default=cs%aggress_adjust, do_not_read=cs%aggress_adjust)
2172  call get_param(param_file, mdl, "CONTINUITY_CFL_LIMIT", cs%CFL_limit_adjust, &
2173  "The maximum CFL of the adjusted velocities.", units="nondim", &
2174  default=0.5)
2175  call get_param(param_file, mdl, "CONT_PPM_BETTER_ITER", cs%better_iter, &
2176  "If true, stop corrective iterations using a velocity \n"//&
2177  "based criterion and only stop if the iteration is \n"//&
2178  "better than all predecessors.", default=.true.)
2179  call get_param(param_file, mdl, "CONT_PPM_USE_VISC_REM_MAX", &
2180  cs%use_visc_rem_max, &
2181  "If true, use more appropriate limiting bounds for \n"//&
2182  "corrections in strongly viscous columns.", default=.true.)
2183  call get_param(param_file, mdl, "CONT_PPM_MARGINAL_FACE_AREAS", cs%marginal_faces, &
2184  "If true, use the marginal face areas from the continuity \n"//&
2185  "solver for use as the weights in the barotropic solver. \n"//&
2186  "Otherwise use the transport averaged areas.", default=.true.)
2187 
2188  cs%diag => diag
2189 
2190  id_clock_update = cpu_clock_id('(Ocean continuity update)', grain=clock_routine)
2191  id_clock_correct = cpu_clock_id('(Ocean continuity correction)', grain=clock_routine)
2192 
2193  cs%tol_eta = cs%tol_eta * gv%m_to_H
2194  cs%tol_eta_aux = cs%tol_eta_aux * gv%m_to_H
2195 
2196 end subroutine continuity_ppm_init
2197 
2198 !> continuity_PPM_stencil returns the continuity solver stencil size
2199 function continuity_ppm_stencil(CS) result(stencil)
2200  type(continuity_ppm_cs), pointer :: CS !< Module's control structure.
2201  integer :: stencil !< The continuity solver stencil size with the current settings.
2202 
2203  stencil = 3 ; if (cs%simple_2nd) stencil = 2 ; if (cs%upwind_1st) stencil = 1
2204 
2205 end function continuity_ppm_stencil
2206 
2207 !> Destructor for continuity_ppm_cs
2208 subroutine continuity_ppm_end(CS)
2209  type(continuity_ppm_cs), pointer :: CS !< Module's control structure.
2210  deallocate(cs)
2211 end subroutine continuity_ppm_end
2212 
2213 !> \namespace mom_continuity_ppm
2214 !!
2215 !! This module contains the subroutines that advect layer
2216 !! thickness. The scheme here uses a Piecewise-Parabolic method with
2217 !! a positive definite limiter.
2218 
2219 end module mom_continuity_ppm
integer, parameter, public obc_direction_s
Indicates the boundary is an effective southern boundary.
integer, parameter, public obc_direction_w
Indicates the boundary is an effective western boundary.
integer, parameter, public obc_direction_n
Indicates the boundary is an effective northern boundary.
subroutine merid_flux_layer(v, h, h_L, h_R, vh, dvhdv, visc_rem, dt, G, J, ish, ieh, do_I, vol_CFL)
Evaluates the meridional mass or volume fluxes in a layer.
subroutine set_merid_bt_cont(v, h_in, h_L, h_R, BT_cont, vh_tot_0, dvhdv_tot_0, dv_max_CFL, dv_min_CFL, dt, G, CS, visc_rem, visc_rem_max, j, ish, ieh, do_I)
Sets of a structure that describes the meridional barotropic volume or mass fluxes as a function of b...
integer function, public continuity_ppm_stencil(CS)
continuity_PPM_stencil returns the continuity solver stencil size
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19
The following data type a list of diagnostic fields an their variants, as well as variables that cont...
subroutine ppm_reconstruction_y(h_in, h_L, h_R, G, LB, h_min, monotonic, simple_2nd)
Calculates left/right edge values for PPM reconstruction.
subroutine set_zonal_bt_cont(u, h_in, h_L, h_R, BT_cont, uh_tot_0, duhdu_tot_0, du_max_CFL, du_min_CFL, dt, G, CS, visc_rem, visc_rem_max, j, ish, ieh, do_I)
Sets a structure that describes the zonal barotropic volume or mass fluxes as a function of barotropi...
Provides the ocean grid type.
Definition: MOM_grid.F90:2
Open boundary segment data structure.
subroutine, public continuity_ppm(u, v, hin, h, uh, vh, dt, G, GV, CS, uhbt, vhbt, OBC, visc_rem_u, visc_rem_v, u_cor, v_cor, uhbt_aux, vhbt_aux, u_cor_aux, v_cor_aux, BT_cont)
Time steps the layer thicknesses, using a monotonically limit, directionally split PPM scheme...
subroutine ppm_limit_pos(h_in, h_L, h_R, h_min, G, iis, iie, jis, jie)
This subroutine limits the left/right edge values of the PPM reconstruction to give a reconstruction ...
subroutine zonal_flux_adjust(u, h_in, h_L, h_R, uhbt, uh_tot_0, duhdu_tot_0, du, du_max_CFL, du_min_CFL, dt, G, CS, visc_rem, j, ish, ieh, do_I_in, full_precision, uh_3d)
Returns the barotropic velocity adjustment that gives the desired barotropic (layer-summed) transport...
The BT_cont_type structure contains information about the summed layer transports and how they will v...
Control structure for mom_continuity_ppm.
logical function, public is_root_pe()
A container for loop bounds.
subroutine, public continuity_ppm_end(CS)
Destructor for continuity_ppm_cs.
subroutine meridional_mass_flux(v, h_in, vh, dt, G, GV, CS, LB, vhbt, OBC, visc_rem_v, v_cor, vhbt_aux, v_cor_aux, BT_cont)
Calculates the mass or volume fluxes through the meridional faces, and other related quantities...
integer, parameter, public obc_direction_e
Indicates the boundary is an effective eastern boundary.
subroutine zonal_flux_layer(u, h, h_L, h_R, uh, duhdu, visc_rem, dt, G, j, ish, ieh, do_I, vol_CFL)
Evaluates the zonal mass or volume fluxes in a layer.
Solve the layer continuity equation using the PPM method for layer fluxes.
integer, parameter, public obc_none
Controls where open boundary conditions are applied.
real function ratio_max(a, b, maxrat)
Return the maximum ratio of a/b or maxrat.
subroutine ppm_limit_cw84(h_in, h_L, h_R, G, iis, iie, jis, jie)
This subroutine limits the left/right edge values of the PPM reconstruction according to the monotoni...
subroutine zonal_face_thickness(u, h, h_L, h_R, h_u, dt, G, LB, vol_CFL, marginal, visc_rem_u)
Sets the effective interface thickness at each zonal velocity point.
subroutine, public mom_error(level, message, all_print)
subroutine ppm_reconstruction_x(h_in, h_L, h_R, G, LB, h_min, monotonic, simple_2nd)
Calculates left/right edge values for PPM reconstruction.
subroutine meridional_flux_adjust(v, h_in, h_L, h_R, vhbt, vh_tot_0, dvhdv_tot_0, dv, dv_max_CFL, dv_min_CFL, dt, G, CS, visc_rem, j, ish, ieh, do_I_in, full_precision, vh_3d)
Returns the barotropic velocity adjustment that gives the desired barotropic (layer-summed) transport...
subroutine zonal_mass_flux(u, h_in, uh, dt, G, GV, CS, LB, uhbt, OBC, visc_rem_u, u_cor, uhbt_aux, u_cor_aux, BT_cont)
Calculates the mass or volume fluxes through the zonal faces, and other related quantities.
subroutine, public continuity_ppm_init(Time, G, GV, param_file, diag, CS)
Initializes continuity_ppm_cs.
subroutine merid_face_thickness(v, h, h_L, h_R, h_v, dt, G, LB, vol_CFL, marginal, visc_rem_v)
Sets the effective interface thickness at each meridional velocity point.