MOM6
MOM_CoriolisAdv.F90
Go to the documentation of this file.
1 !> Accelerations due to the Coriolis force and momentum advection
3 
4 ! This file is part of MOM6. See LICENSE.md for the license.
5 
6 !> \author Robert Hallberg, April 1994 - June 2002
7 
9 use mom_diag_mediator, only : register_diag_field, safe_alloc_ptr, time_type
10 use mom_error_handler, only : mom_error, mom_mesg, fatal, warning
12 use mom_grid, only : ocean_grid_type
18 
19 implicit none ; private
20 
22 
23 #include <MOM_memory.h>
24 
25 !> Control structure for mom_coriolisadv
26 type, public :: coriolisadv_cs ; private
27  integer :: coriolis_scheme !< Selects the discretization for the Coriolis terms.
28  !! Valid values are:
29  !! - SADOURNY75_ENERGY - Sadourny, 1975
30  !! - ARAKAWA_HSU90 - Arakawa & Hsu, 1990, Energy & non-div. Enstrophy
31  !! - ROBUST_ENSTRO - Pseudo-enstrophy scheme
32  !! - SADOURNY75_ENSTRO - Sadourny, JAS 1975, Enstrophy
33  !! - ARAKAWA_LAMB81 - Arakawa & Lamb, MWR 1981, Energy & Enstrophy
34  !! - ARAKAWA_LAMB_BLEND - A blend of Arakawa & Lamb with Arakawa & Hsu and Sadourny energy.
35  !! The default, SADOURNY75_ENERGY, is the safest choice then the
36  !! deformation radius is poorly resolved.
37  integer :: ke_scheme !< KE_SCHEME selects the discretization for
38  !! the kinetic energy. Valid values are:
39  !! KE_ARAKAWA, KE_SIMPLE_GUDONOV, KE_GUDONOV
40  integer :: pv_adv_scheme !< PV_ADV_SCHEME selects the discretization for PV advection
41  !! Valid values are:
42  !! - PV_ADV_CENTERED - centered (aka Sadourny, 75)
43  !! - PV_ADV_UPWIND1 - upwind, first order
44  real :: f_eff_max_blend !< The factor by which the maximum effective Coriolis
45  !! acceleration from any point can be increased when
46  !! blending different discretizations with the
47  !! ARAKAWA_LAMB_BLEND Coriolis scheme. This must be
48  !! greater than 2.0, and is 4.0 by default.
49  real :: wt_lin_blend !< A weighting value beyond which the blending between
50  !! Sadourny and Arakawa & Hsu goes linearly to 0.
51  !! This must be between 1 and 1e-15, often 1/8.
52  logical :: no_slip !< If true, no slip boundary conditions are used.
53  !! Otherwise free slip boundary conditions are assumed.
54  !! The implementation of the free slip boundary
55  !! conditions on a C-grid is much cleaner than the
56  !! no slip boundary conditions. The use of free slip
57  !! b.c.s is strongly encouraged. The no slip b.c.s
58  !! are not implemented with the biharmonic viscosity.
59  logical :: bound_coriolis !< If true, the Coriolis terms at u points are
60  !! bounded by the four estimates of (f+rv)v from the
61  !! four neighboring v points, and similarly at v
62  !! points. This option would have no effect on the
63  !! SADOURNY75_ENERGY scheme if it were possible to
64  !! use centered difference thickness fluxes.
65  logical :: coriolis_en_dis !< If CORIOLIS_EN_DIS is defined, two estimates of
66  !! the thickness fluxes are used to estimate the
67  !! Coriolis term, and the one that dissipates energy
68  !! relative to the other one is used. This is only
69  !! available at present if Coriolis scheme is
70  !! SADOURNY75_ENERGY.
71  type(time_type), pointer :: time !< A pointer to the ocean model's clock.
72  type(diag_ctrl), pointer :: diag !< A structure that is used to regulate the timing of diagnostic output.
73  integer :: id_rv = -1, id_pv = -1, id_gkeu = -1, id_gkev = -1
74  integer :: id_rvxu = -1, id_rvxv = -1
75 end type coriolisadv_cs
76 
77 ! Enumeration values for Coriolis_Scheme
78 integer, parameter :: sadourny75_energy = 1
79 integer, parameter :: arakawa_hsu90 = 2
80 integer, parameter :: robust_enstro = 3
81 integer, parameter :: sadourny75_enstro = 4
82 integer, parameter :: arakawa_lamb81 = 5
83 integer, parameter :: al_blend = 6
84 character*(20), parameter :: sadourny75_energy_string = "SADOURNY75_ENERGY"
85 character*(20), parameter :: arakawa_hsu_string = "ARAKAWA_HSU90"
86 character*(20), parameter :: robust_enstro_string = "ROBUST_ENSTRO"
87 character*(20), parameter :: sadourny75_enstro_string = "SADOURNY75_ENSTRO"
88 character*(20), parameter :: arakawa_lamb_string = "ARAKAWA_LAMB81"
89 character*(20), parameter :: al_blend_string = "ARAKAWA_LAMB_BLEND"
90 ! Enumeration values for KE_Scheme
91 integer, parameter :: ke_arakawa = 10
92 integer, parameter :: ke_simple_gudonov = 11
93 integer, parameter :: ke_gudonov = 12
94 character*(20), parameter :: ke_arakawa_string = "KE_ARAKAWA"
95 character*(20), parameter :: ke_simple_gudonov_string = "KE_SIMPLE_GUDONOV"
96 character*(20), parameter :: ke_gudonov_string = "KE_GUDONOV"
97 ! Enumeration values for PV_Adv_Scheme
98 integer, parameter :: pv_adv_centered = 21
99 integer, parameter :: pv_adv_upwind1 = 22
100 character*(20), parameter :: pv_adv_centered_string = "PV_ADV_CENTERED"
101 character*(20), parameter :: pv_adv_upwind1_string = "PV_ADV_UPWIND1"
102 
103 contains
104 
105 !> Calculates the Coriolis and momentum advection contributions to the acceleration.
106 subroutine coradcalc(u, v, h, uh, vh, CAu, CAv, OBC, AD, G, GV, CS)
107  type(ocean_grid_type), intent(in) :: G !< Ocen grid structure
108  type(verticalgrid_type), intent(in) :: GV !< Vertical grid structure
109  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: u !< Zonal velocity (m/s)
110  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: v !< Meridional velocity (m/s)
111  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thickness (m or kg/m2)
112  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: uh !< Zonal transport u*h*dy (m3/s or kg/s)
113  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: vh !< Meridional transport v*h*dx (m3/s or kg/s)
114  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(out) :: CAu !< Zonal acceleration due to Coriolis
115  !! and momentum advection, in m/s2.
116  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(out) :: CAv !< Meridional acceleration due to Coriolis
117  !! and momentum advection, in m/s2.
118  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
119  type(accel_diag_ptrs), intent(inout) :: AD !< Storage for acceleration diagnostics
120  type(coriolisadv_cs), pointer :: CS !< Control structure for MOM_CoriolisAdv
121  ! Local variables
122 
123  real, dimension(SZIB_(G),SZJB_(G)) :: &
124  q, & ! Layer potential vorticity, in m-1 s-1.
125  Ih_q, & ! The inverse of thickness interpolated to q pointes, in
126  ! units of m-1 or m2 kg-1.
127  area_q ! The sum of the ocean areas at the 4 adjacent thickness
128  ! points, in m2.
129 
130  real, dimension(SZIB_(G),SZJ_(G)) :: &
131  a, b, c, d ! a, b, c, & d are combinations of the potential vorticities
132  ! surrounding an h grid point. At small scales, a = q/4,
133  ! b = q/4, etc. All are in units of m-1 s-1 or m2 kg-1 s-1,
134  ! and use the indexing of the corresponding u point.
135 
136  real, dimension(SZI_(G),SZJ_(G)) :: &
137  Area_h, & ! The ocean area at h points, in m2. Area_h is used to find the
138  ! average thickness in the denominator of q. 0 for land points.
139  ke ! Kinetic energy per unit mass, KE = (u^2 + v^2)/2, in m2 s-2.
140  real, dimension(SZIB_(G),SZJ_(G)) :: &
141  hArea_u, & ! The cell area weighted thickness interpolated to u points
142  ! times the effective areas, in H m2.
143  kex, & ! The zonal gradient of Kinetic energy per unit mass,
144  ! KEx = d/dx KE, in m s-2.
145  uh_center ! centered u times h at u-points
146  real, dimension(SZI_(G),SZJB_(G)) :: &
147  hArea_v, & ! The cell area weighted thickness interpolated to v points
148  ! times the effective areas, in H m2.
149  key, & ! The meridonal gradient of Kinetic energy per unit mass,
150  ! KEy = d/dy KE, in m s-2.
151  vh_center ! centered v times h at v-points
152  real, dimension(SZI_(G),SZJ_(G)) :: &
153  uh_min, uh_max, & ! The smallest and largest estimates of the volume
154  vh_min, vh_max, & ! fluxes through the faces (i.e. u*h*dy & v*h*dx),
155  ! in m3 s-1 or kg s-1.
156  ep_u, ep_v ! Additional pseudo-Coriolis terms in the Arakawa and Lamb
157  ! discretization, in m-1 s-1 or m2 kg-1 s-1.
158  real, dimension(SZIB_(G),SZJB_(G)) :: &
159  dvdx,dudy, &! Contributions to the circulation around q-points (m2 s-1)
160  abs_vort, & ! Absolute vorticity at q-points, in s-1.
161  q2, & ! Relative vorticity over thickness.
162  max_fvq, & ! The maximum or minimum of the
163  min_fvq, & ! adjacent values of (-u) or v times
164  max_fuq, & ! the absolute vorticity, in m s-2.
165  min_fuq ! All are defined at q points.
166  real, dimension(SZIB_(G),SZJB_(G),SZK_(G)) :: &
167  PV, & ! A diagnostic array of the potential vorticities, in m-1 s-1.
168  RV ! A diagnostic array of the relative vorticities, in s-1.
169  real :: fv1, fv2, fu1, fu2 ! (f+rv)*v or (f+rv)*u in m s-2.
170  real :: max_fv, max_fu ! The maximum or minimum of the neighbor-
171  real :: min_fv, min_fu ! max(min)_fu(v)q, in m s-2.
172 
173  real, parameter :: C1_12=1.0/12.0 ! C1_12 = 1/12
174  real, parameter :: C1_24=1.0/24.0 ! C1_24 = 1/24
175  real :: absolute_vorticity ! Absolute vorticity, in s-1.
176  real :: relative_vorticity ! Relative vorticity, in s-1.
177  real :: Ih ! Inverse of thickness, m-1 or m2 kg-1.
178  real :: max_Ihq, min_Ihq ! The maximum and minimum of the nearby Ihq.
179  real :: hArea_q ! The sum of area times thickness of the cells
180  ! surrounding a q point, in m3 or kg.
181  real :: h_neglect ! A thickness that is so small it is usually
182  ! lost in roundoff and can be neglected, in m.
183  real :: temp1, temp2 ! Temporary variables, in m2 s-2.
184  real, parameter :: eps_vel=1.0e-10 ! A tiny, positive velocity, in m s-1.
185 
186  real :: uhc, vhc ! Centered estimates of uh and vh in m3 s-1 or kg s-1.
187  real :: uhm, vhm ! The input estimates of uh and vh in m3 s-1 or kg s-1.
188  real :: c1, c2, c3, slope ! Nondimensional parameters for the Coriolis
189  ! limiter scheme.
190 
191  real :: Fe_m2 ! Nondimensional temporary variables asssociated with
192  real :: rat_lin ! the ARAKAWA_LAMB_BLEND scheme.
193  real :: rat_m1 ! The ratio of the maximum neighboring inverse thickness
194  ! to the minimum inverse thickness minus 1. rat_m1 >= 0.
195  real :: AL_wt ! The relative weight of the Arakawa & Lamb scheme to the
196  ! Arakawa & Hsu scheme, nondimensional between 0 and 1.
197  real :: Sad_wt ! The relative weight of the Sadourny energy scheme to
198  ! the other two with the ARAKAWA_LAMB_BLEND scheme,
199  ! nondimensional between 0 and 1.
200 
201  real :: Heff1, Heff2 ! Temporary effective H at U or V points in m or kg m-2.
202  real :: Heff3, Heff4 ! Temporary effective H at U or V points in m or kg m-2.
203  real :: h_tiny ! A very small thickness, in m or kg m-2.
204  real :: UHeff, VHeff ! More temporary variables, in m3 s-1 or kg s-1.
205  real :: QUHeff,QVHeff ! More temporary variables, in m3 s-2 or kg s-2.
206  integer :: i, j, k, n, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz
207 
208 ! To work, the following fields must be set outside of the usual
209 ! is to ie range before this subroutine is called:
210 ! v[is-1,ie+1,ie+2], u[is-1,ie+1], vh[ie+1], uh[is-1], and
211 ! h[is-1,ie+1,ie+2].
212 ! In the y-direction, the following fields must be set:
213 ! v[js-1,je+1], u[js-1,je+1,je+2], vh[js-1], uh[je+1], and
214 ! h[js-1,je+1,je+2].
215 
216  if (.not.associated(cs)) call mom_error(fatal, &
217  "MOM_CoriolisAdv: Module must be initialized before it is used.")
218  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
219  isq = g%IscB ; ieq = g%IecB ; jsq = g%JscB ; jeq = g%JecB ; nz = g%ke
220  h_neglect = gv%H_subroundoff
221  h_tiny = gv%Angstrom ! Perhaps this should be set to h_neglect instead.
222 
223  !$OMP parallel do default(private) shared(Isq,Ieq,Jsq,Jeq,G,Area_h)
224  do j=jsq-1,jeq+2 ; do i=isq-1,ieq+2
225  area_h(i,j) = g%mask2dT(i,j) * g%areaT(i,j)
226  enddo ; enddo
227  !$OMP parallel do default(private) shared(Isq,Ieq,Jsq,Jeq,G,Area_h,Area_q)
228  do j=jsq-1,jeq+1 ; do i=isq-1,ieq+1
229  area_q(i,j) = (area_h(i,j) + area_h(i+1,j+1)) + &
230  (area_h(i+1,j) + area_h(i,j+1))
231  enddo ; enddo
232 
233  !$OMP parallel do default(private) shared(u,v,h,uh,vh,CAu,CAv,G,CS,AD,Area_h,Area_q,&
234  !$OMP RV,PV,is,ie,js,je,Isq,Ieq,Jsq,Jeq,nz,h_neglect,h_tiny,OBC)
235  do k=1,nz
236  ! Here the second order accurate layer potential vorticities, q,
237  ! are calculated. hq is second order accurate in space. Relative
238  ! vorticity is second order accurate everywhere with free slip b.c.s,
239  ! but only first order accurate at boundaries with no slip b.c.s.
240  ! First calculate the contributions to the circulation around the q-point.
241  do j=jsq-1,jeq+1 ; do i=isq-1,ieq+1
242  dvdx(i,j) = v(i+1,j,k)*g%dyCv(i+1,j) - v(i,j,k)*g%dyCv(i,j)
243  dudy(i,j) = u(i,j+1,k)*g%dxCu(i,j+1) - u(i,j,k)*g%dxCu(i,j)
244  enddo ; enddo
245  do j=jsq-1,jeq+1 ; do i=isq-1,ieq+2
246  harea_v(i,j) = 0.5*(area_h(i,j) * h(i,j,k) + area_h(i,j+1) * h(i,j+1,k))
247  enddo ; enddo
248  do j=jsq-1,jeq+2 ; do i=isq-1,ieq+1
249  harea_u(i,j) = 0.5*(area_h(i,j) * h(i,j,k) + area_h(i+1,j) * h(i+1,j,k))
250  enddo ; enddo
251  if (cs%Coriolis_En_Dis) then
252  do j=jsq,jeq+1 ; do i=is-1,ie
253  uh_center(i,j) = 0.5 * (g%dy_Cu(i,j) * u(i,j,k)) * (h(i,j,k) + h(i+1,j,k))
254  enddo ; enddo
255  do j=js-1,je ; do i=isq,ieq+1
256  vh_center(i,j) = 0.5 * (g%dx_Cv(i,j) * v(i,j,k)) * (h(i,j,k) + h(i,j+1,k))
257  enddo ; enddo
258  endif
259 
260  ! Adjust circulation components to relative vorticity and thickness projected onto
261  ! velocity points on open boundaries.
262  if (associated(obc)) then ; do n=1,obc%number_of_segments
263  if (.not. obc%segment(n)%on_pe) cycle
264  i = obc%segment(n)%HI%IsdB ; j = obc%segment(n)%HI%JsdB
265  if (obc%segment(n)%is_N_or_S .and. (j >= jsq-1) .and. (j <= jeq+1)) then
266  if (obc%zero_vorticity) then ; do i=obc%segment(n)%HI%IsdB,obc%segment(n)%HI%IedB
267  dvdx(i,j) = 0. ; dudy(i,j) = 0.
268  enddo ; endif
269  if (obc%freeslip_vorticity) then ; do i=obc%segment(n)%HI%IsdB,obc%segment(n)%HI%IedB
270  dudy(i,j) = 0.
271  enddo ; endif
272 
273  ! Project thicknesses across OBC points with a no-gradient condition.
274  do i = max(isq-1,obc%segment(n)%HI%isd), min(ieq+2,obc%segment(n)%HI%ied)
275  if (obc%segment(n)%direction == obc_direction_n) then
276  harea_v(i,j) = 0.5 * (area_h(i,j) + area_h(i,j+1)) * h(i,j,k)
277  else ! (OBC%segment(n)%direction == OBC_DIRECTION_S)
278  harea_v(i,j) = 0.5 * (area_h(i,j) + area_h(i,j+1)) * h(i,j+1,k)
279  endif
280  enddo
281 
282  if (cs%Coriolis_En_Dis) then
283  do i = max(isq-1,obc%segment(n)%HI%isd), min(ieq+2,obc%segment(n)%HI%ied)
284  if (obc%segment(n)%direction == obc_direction_n) then
285  vh_center(i,j) = g%dx_Cv(i,j) * v(i,j,k) * h(i,j,k)
286  else ! (OBC%segment(n)%direction == OBC_DIRECTION_S)
287  vh_center(i,j) = g%dx_Cv(i,j) * v(i,j,k) * h(i,j+1,k)
288  endif
289  enddo
290  endif
291  elseif (obc%segment(n)%is_E_or_W .and. (i >= isq-1) .and. (i <= ieq+1)) then
292  if (obc%zero_vorticity) then ; do j=obc%segment(n)%HI%JsdB,obc%segment(n)%HI%JedB
293  dvdx(i,j) = 0. ; dudy(i,j) = 0.
294  enddo ; endif
295  if (obc%freeslip_vorticity) then ; do j=obc%segment(n)%HI%JsdB,obc%segment(n)%HI%JedB
296  dvdx(i,j) = 0.
297  enddo ; endif
298 
299  ! Project thicknesses across OBC points with a no-gradient condition.
300  do j = max(jsq-1,obc%segment(n)%HI%jsd), min(jeq+2,obc%segment(n)%HI%jed)
301  if (obc%segment(n)%direction == obc_direction_e) then
302  harea_u(i,j) = 0.5*(area_h(i,j) + area_h(i+1,j)) * h(i,j,k)
303  else ! (OBC%segment(n)%direction == OBC_DIRECTION_W)
304  harea_u(i,j) = 0.5*(area_h(i,j) + area_h(i+1,j)) * h(i+1,j,k)
305  endif
306  enddo
307  if (cs%Coriolis_En_Dis) then
308  do j = max(jsq-1,obc%segment(n)%HI%jsd), min(jeq+2,obc%segment(n)%HI%jed)
309  if (obc%segment(n)%direction == obc_direction_e) then
310  uh_center(i,j) = g%dy_Cu(i,j) * u(i,j,k) * h(i,j,k)
311  else ! (OBC%segment(n)%direction == OBC_DIRECTION_W)
312  uh_center(i,j) = g%dy_Cu(i,j) * u(i,j,k) * h(i+1,j,k)
313  endif
314  enddo
315  endif
316  endif
317  enddo ; endif
318 
319  if (associated(obc)) then ; do n=1,obc%number_of_segments
320  if (.not. obc%segment(n)%on_pe) cycle
321  ! Now project thicknesses across cell-corner points in the OBCs. The two
322  ! projections have to occur in sequence and can not be combined easily.
323  i = obc%segment(n)%HI%IsdB ; j = obc%segment(n)%HI%JsdB
324  if (obc%segment(n)%is_N_or_S .and. (j >= jsq-1) .and. (j <= jeq+1)) then
325  do i = max(isq-1,obc%segment(n)%HI%IsdB), min(ieq+1,obc%segment(n)%HI%IedB)
326  if (obc%segment(n)%direction == obc_direction_n) then
327  if (area_h(i,j) + area_h(i+1,j) > 0.0) then
328  harea_u(i,j+1) = harea_u(i,j) * ((area_h(i,j+1) + area_h(i+1,j+1)) / &
329  (area_h(i,j) + area_h(i+1,j)))
330  else ; harea_u(i,j+1) = 0.0 ; endif
331  else ! (OBC%segment(n)%direction == OBC_DIRECTION_S)
332  if (area_h(i,j+1) + area_h(i+1,j+1) > 0.0) then
333  harea_u(i,j) = harea_u(i,j+1) * ((area_h(i,j) + area_h(i+1,j)) / &
334  (area_h(i,j+1) + area_h(i+1,j+1)))
335  else ; harea_u(i,j) = 0.0 ; endif
336  endif
337  enddo
338  elseif (obc%segment(n)%is_E_or_W .and. (i >= isq-1) .and. (i <= ieq+1)) then
339  do j = max(jsq-1,obc%segment(n)%HI%JsdB), min(jeq+1,obc%segment(n)%HI%JedB)
340  if (obc%segment(n)%direction == obc_direction_e) then
341  if (area_h(i,j) + area_h(i,j+1) > 0.0) then
342  harea_v(i+1,j) = harea_v(i,j) * ((area_h(i+1,j) + area_h(i+1,j+1)) / &
343  (area_h(i,j) + area_h(i,j+1)))
344  else ; harea_v(i+1,j) = 0.0 ; endif
345  else ! (OBC%segment(n)%direction == OBC_DIRECTION_W)
346  harea_v(i,j) = 0.5 * (area_h(i,j) + area_h(i,j+1)) * h(i,j+1,k)
347  if (area_h(i+1,j) + area_h(i+1,j+1) > 0.0) then
348  harea_v(i,j) = harea_v(i+1,j) * ((area_h(i,j) + area_h(i,j+1)) / &
349  (area_h(i+1,j) + area_h(i+1,j+1)))
350  else ; harea_v(i,j) = 0.0 ; endif
351  endif
352  enddo
353  endif
354  enddo ; endif
355 
356  do j=jsq-1,jeq+1 ; do i=isq-1,ieq+1
357  if (cs%no_slip ) then
358  relative_vorticity = (2.0-g%mask2dBu(i,j)) * (dvdx(i,j) - dudy(i,j)) * &
359  g%IareaBu(i,j)
360  else
361  relative_vorticity = g%mask2dBu(i,j) * (dvdx(i,j) - dudy(i,j)) * &
362  g%IareaBu(i,j)
363  endif
364  absolute_vorticity = g%CoriolisBu(i,j) + relative_vorticity
365  ih = 0.0
366  if (area_q(i,j) > 0.0) then
367  harea_q = (harea_u(i,j) + harea_u(i,j+1)) + (harea_v(i,j) + harea_v(i+1,j))
368  ih = area_q(i,j) / (harea_q + h_neglect*area_q(i,j))
369  endif
370  q(i,j) = absolute_vorticity * ih
371  abs_vort(i,j) = absolute_vorticity
372  ih_q(i,j) = ih
373 
374  if (cs%bound_Coriolis) then
375  fv1 = absolute_vorticity*v(i+1,j,k)
376  fv2 = absolute_vorticity*v(i,j,k)
377  fu1 = -absolute_vorticity*u(i,j+1,k)
378  fu2 = -absolute_vorticity*u(i,j,k)
379  if (fv1 > fv2) then
380  max_fvq(i,j) = fv1 ; min_fvq(i,j) = fv2
381  else
382  max_fvq(i,j) = fv2 ; min_fvq(i,j) = fv1
383  endif
384  if (fu1 > fu2) then
385  max_fuq(i,j) = fu1 ; min_fuq(i,j) = fu2
386  else
387  max_fuq(i,j) = fu2 ; min_fuq(i,j) = fu1
388  endif
389  endif
390 
391  if (cs%id_rv > 0) rv(i,j,k) = relative_vorticity
392  if (cs%id_PV > 0) pv(i,j,k) = q(i,j)
393  if (ASSOCIATED(ad%rv_x_v) .or. ASSOCIATED(ad%rv_x_u)) &
394  q2(i,j) = relative_vorticity * ih
395  enddo ; enddo
396 
397  ! a, b, c, and d are combinations of neighboring potential
398  ! vorticities which form the Arakawa and Hsu vorticity advection
399  ! scheme. All are defined at u grid points.
400 
401  if (cs%Coriolis_Scheme == arakawa_hsu90) then
402  do j=jsq,jeq+1
403  do i=is-1,ieq
404  a(i,j) = (q(i,j) + (q(i+1,j) + q(i,j-1))) * c1_12
405  d(i,j) = ((q(i,j) + q(i+1,j-1)) + q(i,j-1)) * c1_12
406  enddo
407  do i=isq,ieq
408  b(i,j) = (q(i,j) + (q(i-1,j) + q(i,j-1))) * c1_12
409  c(i,j) = ((q(i,j) + q(i-1,j-1)) + q(i,j-1)) * c1_12
410  enddo
411  enddo
412  elseif (cs%Coriolis_Scheme == arakawa_lamb81) then
413  do j=jsq,jeq+1 ; do i=isq,ieq+1
414  a(i-1,j) = (2.0*(q(i,j) + q(i-1,j-1)) + (q(i-1,j) + q(i,j-1))) * c1_24
415  d(i-1,j) = ((q(i,j) + q(i-1,j-1)) + 2.0*(q(i-1,j) + q(i,j-1))) * c1_24
416  b(i,j) = ((q(i,j) + q(i-1,j-1)) + 2.0*(q(i-1,j) + q(i,j-1))) * c1_24
417  c(i,j) = (2.0*(q(i,j) + q(i-1,j-1)) + (q(i-1,j) + q(i,j-1))) * c1_24
418  ep_u(i,j) = ((q(i,j) - q(i-1,j-1)) + (q(i-1,j) - q(i,j-1))) * c1_24
419  ep_v(i,j) = (-(q(i,j) - q(i-1,j-1)) + (q(i-1,j) - q(i,j-1))) * c1_24
420  enddo ; enddo
421  elseif (cs%Coriolis_Scheme == al_blend) then
422  fe_m2 = cs%F_eff_max_blend - 2.0
423  rat_lin = 1.5 * fe_m2 / max(cs%wt_lin_blend, 1.0e-16)
424 
425  ! This allows the code to always give Sadourny Energy
426  if (cs%F_eff_max_blend <= 2.0) then ; fe_m2 = -1. ; rat_lin = -1.0 ; endif
427 
428  do j=jsq,jeq+1 ; do i=isq,ieq+1
429  min_ihq = min(ih_q(i-1,j-1), ih_q(i,j-1), ih_q(i-1,j), ih_q(i,j))
430  max_ihq = max(ih_q(i-1,j-1), ih_q(i,j-1), ih_q(i-1,j), ih_q(i,j))
431  rat_m1 = 1.0e15
432  if (max_ihq < 1.0e15*min_ihq) rat_m1 = max_ihq / min_ihq - 1.0
433  ! The weights used here are designed to keep the effective Coriolis
434  ! acceleration from any one point on its neighbors within a factor
435  ! of F_eff_max. The minimum permitted value is 2 (the factor for
436  ! Sadourny's energy conserving scheme).
437 
438  ! Determine the relative weights of Arakawa & Lamb vs. Arakawa and Hsu.
439  if (rat_m1 <= fe_m2) then ; al_wt = 1.0
440  elseif (rat_m1 < 1.5*fe_m2) then ; al_wt = 3.0*fe_m2 / rat_m1 - 2.0
441  else ; al_wt = 0.0 ; endif
442 
443  ! Determine the relative weights of Sadourny Energy vs. the other two.
444  if (rat_m1 <= 1.5*fe_m2) then ; sad_wt = 0.0
445  elseif (rat_m1 <= rat_lin) then
446  sad_wt = 1.0 - (1.5*fe_m2) / rat_m1
447  elseif (rat_m1 < 2.0*rat_lin) then
448  sad_wt = 1.0 - (cs%wt_lin_blend / rat_lin) * (rat_m1 - 2.0*rat_lin)
449  else ; sad_wt = 1.0 ; endif
450 
451  a(i-1,j) = sad_wt * 0.25 * q(i-1,j) + (1.0 - sad_wt) * &
452  ( ((2.0-al_wt)* q(i-1,j) + al_wt*q(i,j-1)) + &
453  2.0 * (q(i,j) + q(i-1,j-1)) ) * c1_24
454  d(i-1,j) = sad_wt * 0.25 * q(i-1,j-1) + (1.0 - sad_wt) * &
455  ( ((2.0-al_wt)* q(i-1,j-1) + al_wt*q(i,j)) + &
456  2.0 * (q(i-1,j) + q(i,j-1)) ) * c1_24
457  b(i,j) = sad_wt * 0.25 * q(i,j) + (1.0 - sad_wt) * &
458  ( ((2.0-al_wt)* q(i,j) + al_wt*q(i-1,j-1)) + &
459  2.0 * (q(i-1,j) + q(i,j-1)) ) * c1_24
460  c(i,j) = sad_wt * 0.25 * q(i,j-1) + (1.0 - sad_wt) * &
461  ( ((2.0-al_wt)* q(i,j-1) + al_wt*q(i-1,j)) + &
462  2.0 * (q(i,j) + q(i-1,j-1)) ) * c1_24
463  ep_u(i,j) = al_wt * ((q(i,j) - q(i-1,j-1)) + (q(i-1,j) - q(i,j-1))) * c1_24
464  ep_v(i,j) = al_wt * (-(q(i,j) - q(i-1,j-1)) + (q(i-1,j) - q(i,j-1))) * c1_24
465  enddo ; enddo
466  endif
467 
468  if (cs%Coriolis_En_Dis) then
469  ! c1 = 1.0-1.5*RANGE ; c2 = 1.0-RANGE ; c3 = 2.0 ; slope = 0.5
470  c1 = 1.0-1.5*0.5 ; c2 = 1.0-0.5 ; c3 = 2.0 ; slope = 0.5
471 
472  do j=jsq,jeq+1 ; do i=is-1,ie
473  uhc = uh_center(i,j)
474  uhm = uh(i,j,k)
475  ! This sometimes matters with some types of open boundary conditions.
476  if (g%dy_Cu(i,j) == 0.0) uhc = uhm
477 
478  if (abs(uhc) < 0.1*abs(uhm)) then
479  uhm = 10.0*uhc
480  elseif (abs(uhc) > c1*abs(uhm)) then
481  if (abs(uhc) < c2*abs(uhm)) then ; uhc = (3.0*uhc+(1.0-c2*3.0)*uhm)
482  elseif (abs(uhc) <= c3*abs(uhm)) then ; uhc = uhm
483  else ; uhc = slope*uhc+(1.0-c3*slope)*uhm
484  endif
485  endif
486 
487  if (uhc > uhm) then
488  uh_min(i,j) = uhm ; uh_max(i,j) = uhc
489  else
490  uh_max(i,j) = uhm ; uh_min(i,j) = uhc
491  endif
492  enddo ; enddo
493  do j=js-1,je ; do i=isq,ieq+1
494  vhc = vh_center(i,j)
495  vhm = vh(i,j,k)
496  ! This sometimes matters with some types of open boundary conditions.
497  if (g%dx_Cv(i,j) == 0.0) vhc = vhm
498 
499  if (abs(vhc) < 0.1*abs(vhm)) then
500  vhm = 10.0*vhc
501  elseif (abs(vhc) > c1*abs(vhm)) then
502  if (abs(vhc) < c2*abs(vhm)) then ; vhc = (3.0*vhc+(1.0-c2*3.0)*vhm)
503  else if (abs(vhc) <= c3*abs(vhm)) then ; vhc = vhm
504  else ; vhc = slope*vhc+(1.0-c3*slope)*vhm
505  endif
506  endif
507 
508  if (vhc > vhm) then
509  vh_min(i,j) = vhm ; vh_max(i,j) = vhc
510  else
511  vh_max(i,j) = vhm ; vh_min(i,j) = vhc
512  endif
513  enddo ; enddo
514  endif
515 
516  ! Calculate KE and the gradient of KE
517  call gradke(u, v, h, ke, kex, key, k, obc, g, cs)
518 
519  ! Calculate the tendencies of zonal velocity due to the Coriolis
520  ! force and momentum advection. On a Cartesian grid, this is
521  ! CAu = q * vh - d(KE)/dx.
522  if (cs%Coriolis_Scheme == sadourny75_energy) then
523  if (cs%Coriolis_En_Dis) then
524  ! Energy dissipating biased scheme, Hallberg 200x
525  do j=js,je ; do i=isq,ieq
526  if (q(i,j)*u(i,j,k) == 0.0) then
527  temp1 = q(i,j) * ( (vh_max(i,j)+vh_max(i+1,j)) &
528  + (vh_min(i,j)+vh_min(i+1,j)) )*0.5
529  elseif (q(i,j)*u(i,j,k) < 0.0) then
530  temp1 = q(i,j) * (vh_max(i,j)+vh_max(i+1,j))
531  else
532  temp1 = q(i,j) * (vh_min(i,j)+vh_min(i+1,j))
533  endif
534  if (q(i,j-1)*u(i,j,k) == 0.0) then
535  temp2 = q(i,j-1) * ( (vh_max(i,j-1)+vh_max(i+1,j-1)) &
536  + (vh_min(i,j-1)+vh_min(i+1,j-1)) )*0.5
537  elseif (q(i,j-1)*u(i,j,k) < 0.0) then
538  temp2 = q(i,j-1) * (vh_max(i,j-1)+vh_max(i+1,j-1))
539  else
540  temp2 = q(i,j-1) * (vh_min(i,j-1)+vh_min(i+1,j-1))
541  endif
542  cau(i,j,k) = 0.25 * g%IdxCu(i,j) * (temp1 + temp2)
543  enddo ; enddo
544  else
545  ! Energy conserving scheme, Sadourny 1975
546  do j=js,je ; do i=isq,ieq
547  cau(i,j,k) = 0.25 * &
548  (q(i,j) * (vh(i+1,j,k) + vh(i,j,k)) + &
549  q(i,j-1) * (vh(i,j-1,k) + vh(i+1,j-1,k))) * g%IdxCu(i,j)
550  enddo ; enddo
551  endif
552  elseif (cs%Coriolis_Scheme == sadourny75_enstro) then
553  do j=js,je ; do i=isq,ieq
554  cau(i,j,k) = 0.125 * (g%IdxCu(i,j) * (q(i,j) + q(i,j-1))) * &
555  ((vh(i+1,j,k) + vh(i,j,k)) + (vh(i,j-1,k) + vh(i+1,j-1,k)))
556  enddo ; enddo
557  elseif ((cs%Coriolis_Scheme == arakawa_hsu90) .or. &
558  (cs%Coriolis_Scheme == arakawa_lamb81) .or. &
559  (cs%Coriolis_Scheme == al_blend)) then
560  ! (Global) Energy and (Local) Enstrophy conserving, Arakawa & Hsu 1990
561  do j=js,je ; do i=isq,ieq
562  cau(i,j,k) = ((a(i,j) * vh(i+1,j,k) + &
563  c(i,j) * vh(i,j-1,k)) &
564  + (b(i,j) * vh(i,j,k) + &
565  d(i,j) * vh(i+1,j-1,k))) * g%IdxCu(i,j)
566  enddo ; enddo
567  elseif (cs%Coriolis_Scheme == robust_enstro) then
568  ! An enstrophy conserving scheme robust to vanishing layers
569  ! Note: Heffs are in lieu of h_at_v that should be returned by the
570  ! continuity solver. AJA
571  do j=js,je ; do i=isq,ieq
572  heff1 = abs(vh(i,j,k)*g%IdxCv(i,j))/(eps_vel+abs(v(i,j,k)))
573  heff1 = max(heff1,min(h(i,j,k),h(i,j+1,k)))
574  heff1 = min(heff1,max(h(i,j,k),h(i,j+1,k)))
575  heff2 = abs(vh(i,j-1,k)*g%IdxCv(i,j-1))/(eps_vel+abs(v(i,j-1,k)))
576  heff2 = max(heff2,min(h(i,j-1,k),h(i,j,k)))
577  heff2 = min(heff2,max(h(i,j-1,k),h(i,j,k)))
578  heff3 = abs(vh(i+1,j,k)*g%IdxCv(i+1,j))/(eps_vel+abs(v(i+1,j,k)))
579  heff3 = max(heff3,min(h(i+1,j,k),h(i+1,j+1,k)))
580  heff3 = min(heff3,max(h(i+1,j,k),h(i+1,j+1,k)))
581  heff4 = abs(vh(i+1,j-1,k)*g%IdxCv(i+1,j-1))/(eps_vel+abs(v(i+1,j-1,k)))
582  heff4 = max(heff4,min(h(i+1,j-1,k),h(i+1,j,k)))
583  heff4 = min(heff4,max(h(i+1,j-1,k),h(i+1,j,k)))
584  if (cs%PV_Adv_Scheme == pv_adv_centered) then
585  cau(i,j,k) = 0.5*(abs_vort(i,j)+abs_vort(i,j-1)) * &
586  ((vh(i ,j ,k)+vh(i+1,j-1,k)) + &
587  (vh(i ,j-1,k)+vh(i+1,j ,k)) ) / &
588  (h_tiny +((heff1+heff4) +(heff2+heff3)) ) * g%IdxCu(i,j)
589  elseif (cs%PV_Adv_Scheme == pv_adv_upwind1) then
590  vheff = ((vh(i ,j ,k)+vh(i+1,j-1,k)) + &
591  (vh(i ,j-1,k)+vh(i+1,j ,k)) )
592  qvheff = 0.5*( (abs_vort(i,j)+abs_vort(i,j-1))*vheff &
593  -(abs_vort(i,j)-abs_vort(i,j-1))*abs(vheff) )
594  cau(i,j,k) = qvheff / &
595  (h_tiny +((heff1+heff4) +(heff2+heff3)) ) * g%IdxCu(i,j)
596  endif
597  enddo ; enddo
598  endif
599  ! Add in the additonal terms with Arakawa & Lamb.
600  if ((cs%Coriolis_Scheme == arakawa_lamb81) .or. &
601  (cs%Coriolis_Scheme == al_blend)) then ; do j=js,je ; do i=isq,ieq
602  cau(i,j,k) = cau(i,j,k) + &
603  (ep_u(i,j)*uh(i-1,j,k) - ep_u(i+1,j)*uh(i+1,j,k)) * g%IdxCu(i,j)
604  enddo ; enddo ; endif
605 
606 
607  if (cs%bound_Coriolis) then
608  do j=js,je ; do i=isq,ieq
609  max_fv = max(max_fvq(i,j),max_fvq(i,j-1))
610  min_fv = min(min_fvq(i,j),min_fvq(i,j-1))
611  ! CAu(I,j,k) = min( CAu(I,j,k), max_fv )
612  ! CAu(I,j,k) = max( CAu(I,j,k), min_fv )
613  if (cau(i,j,k) > max_fv) then
614  cau(i,j,k) = max_fv
615  else
616  if (cau(i,j,k) < min_fv) cau(i,j,k) = min_fv
617  endif
618  enddo ; enddo
619  endif
620 
621  ! Term - d(KE)/dx.
622  do j=js,je ; do i=isq,ieq
623  cau(i,j,k) = cau(i,j,k) - kex(i,j)
624  if (ASSOCIATED(ad%gradKEu)) ad%gradKEu(i,j,k) = -kex(i,j)
625  enddo ; enddo
626 
627 
628  ! Calculate the tendencies of meridional velocity due to the Coriolis
629  ! force and momentum advection. On a Cartesian grid, this is
630  ! CAv = - q * uh - d(KE)/dy.
631  if (cs%Coriolis_Scheme == sadourny75_energy) then
632  if (cs%Coriolis_En_Dis) then
633  ! Energy dissipating biased scheme, Hallberg 200x
634  do j=jsq,jeq ; do i=is,ie
635  if (q(i-1,j)*v(i,j,k) == 0.0) then
636  temp1 = q(i-1,j) * ( (uh_max(i-1,j)+uh_max(i-1,j+1)) &
637  + (uh_min(i-1,j)+uh_min(i-1,j+1)) )*0.5
638  elseif (q(i-1,j)*v(i,j,k) > 0.0) then
639  temp1 = q(i-1,j) * (uh_max(i-1,j)+uh_max(i-1,j+1))
640  else
641  temp1 = q(i-1,j) * (uh_min(i-1,j)+uh_min(i-1,j+1))
642  endif
643  if (q(i,j)*v(i,j,k) == 0.0) then
644  temp2 = q(i,j) * ( (uh_max(i,j)+uh_max(i,j+1)) &
645  + (uh_min(i,j)+uh_min(i,j+1)) )*0.5
646  elseif (q(i,j)*v(i,j,k) > 0.0) then
647  temp2 = q(i,j) * (uh_max(i,j)+uh_max(i,j+1))
648  else
649  temp2 = q(i,j) * (uh_min(i,j)+uh_min(i,j+1))
650  endif
651  cav(i,j,k) = - 0.25 * g%IdyCv(i,j) * (temp1 + temp2)
652  enddo ; enddo
653  else
654  ! Energy conserving scheme, Sadourny 1975
655  do j=jsq,jeq ; do i=is,ie
656  cav(i,j,k) = - 0.25* &
657  (q(i-1,j)*(uh(i-1,j,k) + uh(i-1,j+1,k)) + &
658  q(i,j)*(uh(i,j,k) + uh(i,j+1,k))) * g%IdyCv(i,j)
659  enddo ; enddo
660  endif
661  elseif (cs%Coriolis_Scheme == sadourny75_enstro) then
662  do j=jsq,jeq ; do i=is,ie
663  cav(i,j,k) = -0.125 * (g%IdyCv(i,j) * (q(i-1,j) + q(i,j))) * &
664  ((uh(i-1,j,k) + uh(i-1,j+1,k)) + (uh(i,j,k) + uh(i,j+1,k)))
665  enddo ; enddo
666  elseif ((cs%Coriolis_Scheme == arakawa_hsu90) .or. &
667  (cs%Coriolis_Scheme == arakawa_lamb81) .or. &
668  (cs%Coriolis_Scheme == al_blend)) then
669  ! (Global) Energy and (Local) Enstrophy conserving, Arakawa & Hsu 1990
670  do j=jsq,jeq ; do i=is,ie
671  cav(i,j,k) = - ((a(i-1,j) * uh(i-1,j,k) + &
672  c(i,j+1) * uh(i,j+1,k)) &
673  + (b(i,j) * uh(i,j,k) + &
674  d(i-1,j+1) * uh(i-1,j+1,k))) * g%IdyCv(i,j)
675  enddo ; enddo
676  elseif (cs%Coriolis_Scheme == robust_enstro) then
677  ! An enstrophy conserving scheme robust to vanishing layers
678  ! Note: Heffs are in lieu of h_at_u that should be returned by the
679  ! continuity solver. AJA
680  do j=jsq,jeq ; do i=is,ie
681  heff1 = abs(uh(i,j,k)*g%IdyCu(i,j))/(eps_vel+abs(u(i,j,k)))
682  heff1 = max(heff1,min(h(i,j,k),h(i+1,j,k)))
683  heff1 = min(heff1,max(h(i,j,k),h(i+1,j,k)))
684  heff2 = abs(uh(i-1,j,k)*g%IdyCu(i-1,j))/(eps_vel+abs(u(i-1,j,k)))
685  heff2 = max(heff2,min(h(i-1,j,k),h(i,j,k)))
686  heff2 = min(heff2,max(h(i-1,j,k),h(i,j,k)))
687  heff3 = abs(uh(i,j+1,k)*g%IdyCu(i,j+1))/(eps_vel+abs(u(i,j+1,k)))
688  heff3 = max(heff3,min(h(i,j+1,k),h(i+1,j+1,k)))
689  heff3 = min(heff3,max(h(i,j+1,k),h(i+1,j+1,k)))
690  heff4 = abs(uh(i-1,j+1,k)*g%IdyCu(i-1,j+1))/(eps_vel+abs(u(i-1,j+1,k)))
691  heff4 = max(heff4,min(h(i-1,j+1,k),h(i,j+1,k)))
692  heff4 = min(heff4,max(h(i-1,j+1,k),h(i,j+1,k)))
693  if (cs%PV_Adv_Scheme == pv_adv_centered) then
694  cav(i,j,k) = - 0.5*(abs_vort(i,j)+abs_vort(i-1,j)) * &
695  ((uh(i ,j ,k)+uh(i-1,j+1,k)) + &
696  (uh(i-1,j ,k)+uh(i ,j+1,k)) ) / &
697  (h_tiny + ((heff1+heff4) +(heff2+heff3)) ) * g%IdyCv(i,j)
698  elseif (cs%PV_Adv_Scheme == pv_adv_upwind1) then
699  uheff = ((uh(i ,j ,k)+uh(i-1,j+1,k)) + &
700  (uh(i-1,j ,k)+uh(i ,j+1,k)) )
701  quheff = 0.5*( (abs_vort(i,j)+abs_vort(i-1,j))*uheff &
702  -(abs_vort(i,j)-abs_vort(i-1,j))*abs(uheff) )
703  cav(i,j,k) = - quheff / &
704  (h_tiny + ((heff1+heff4) +(heff2+heff3)) ) * g%IdyCv(i,j)
705  endif
706  enddo ; enddo
707  endif
708  ! Add in the additonal terms with Arakawa & Lamb.
709  if ((cs%Coriolis_Scheme == arakawa_lamb81) .or. &
710  (cs%Coriolis_Scheme == al_blend)) then ; do j=jsq,jeq ; do i=is,ie
711  cav(i,j,k) = cav(i,j,k) + &
712  (ep_v(i,j)*vh(i,j-1,k) - ep_v(i,j+1)*vh(i,j+1,k)) * g%IdyCv(i,j)
713  enddo ; enddo ; endif
714 
715  if (cs%bound_Coriolis) then
716  do j=jsq,jeq ; do i=is,ie
717  max_fu = max(max_fuq(i,j),max_fuq(i-1,j))
718  min_fu = min(min_fuq(i,j),min_fuq(i-1,j))
719  if (cav(i,j,k) > max_fu) then
720  cav(i,j,k) = max_fu
721  else
722  if (cav(i,j,k) < min_fu) cav(i,j,k) = min_fu
723  endif
724  enddo ; enddo
725  endif
726 
727  ! Term - d(KE)/dy.
728  do j=jsq,jeq ; do i=is,ie
729  cav(i,j,k) = cav(i,j,k) - key(i,j)
730  if (ASSOCIATED(ad%gradKEv)) ad%gradKEv(i,j,k) = -key(i,j)
731  enddo ; enddo
732 
733  if (ASSOCIATED(ad%rv_x_u) .or. ASSOCIATED(ad%rv_x_v)) then
734  ! Calculate the Coriolis-like acceleration due to relative vorticity.
735  if (cs%Coriolis_Scheme == sadourny75_energy) then
736  if (ASSOCIATED(ad%rv_x_u)) then
737  do j=jsq,jeq ; do i=is,ie
738  ad%rv_x_u(i,j,k) = - 0.25* &
739  (q2(i-1,j)*(uh(i-1,j,k) + uh(i-1,j+1,k)) + &
740  q2(i,j)*(uh(i,j,k) + uh(i,j+1,k))) * g%IdyCv(i,j)
741  enddo ; enddo
742  endif
743 
744  if (ASSOCIATED(ad%rv_x_v)) then
745  do j=js,je ; do i=isq,ieq
746  ad%rv_x_v(i,j,k) = 0.25 * &
747  (q2(i,j) * (vh(i+1,j,k) + vh(i,j,k)) + &
748  q2(i,j-1) * (vh(i,j-1,k) + vh(i+1,j-1,k))) * g%IdxCu(i,j)
749  enddo ; enddo
750  endif
751  else
752  if (ASSOCIATED(ad%rv_x_u)) then
753  do j=jsq,jeq ; do i=is,ie
754  ad%rv_x_u(i,j,k) = -g%IdyCv(i,j) * c1_12 * &
755  ((q2(i,j) + q2(i-1,j) + q2(i-1,j-1)) * uh(i-1,j,k) + &
756  (q2(i-1,j) + q2(i,j) + q2(i,j-1)) * uh(i,j,k) + &
757  (q2(i-1,j) + q2(i,j+1) + q2(i,j)) * uh(i,j+1,k) + &
758  (q2(i,j) + q2(i-1,j+1) + q2(i-1,j)) * uh(i-1,j+1,k))
759  enddo ; enddo
760  endif
761 
762  if (ASSOCIATED(ad%rv_x_v)) then
763  do j=js,je ; do i=isq,ieq
764  ad%rv_x_v(i,j,k) = g%IdxCu(i,j) * c1_12 * &
765  ((q2(i+1,j) + q2(i,j) + q2(i,j-1)) * vh(i+1,j,k) + &
766  (q2(i-1,j) + q2(i,j) + q2(i,j-1)) * vh(i,j,k) + &
767  (q2(i-1,j-1) + q2(i,j) + q2(i,j-1)) * vh(i,j-1,k) + &
768  (q2(i+1,j-1) + q2(i,j) + q2(i,j-1)) * vh(i+1,j-1,k))
769  enddo ; enddo
770  endif
771  endif
772  endif
773 
774  enddo ! k-loop.
775 
776  ! Here the various Coriolis-related derived quantities are offered for averaging.
777  if (query_averaging_enabled(cs%diag)) then
778  if (cs%id_rv > 0) call post_data(cs%id_rv, rv, cs%diag)
779  if (cs%id_PV > 0) call post_data(cs%id_PV, pv, cs%diag)
780  if (cs%id_gKEu>0) call post_data(cs%id_gKEu, ad%gradKEu, cs%diag)
781  if (cs%id_gKEv>0) call post_data(cs%id_gKEv, ad%gradKEv, cs%diag)
782  if (cs%id_rvxu > 0) call post_data(cs%id_rvxu, ad%rv_x_u, cs%diag)
783  if (cs%id_rvxv > 0) call post_data(cs%id_rvxv, ad%rv_x_v, cs%diag)
784  endif
785 
786 end subroutine coradcalc
787 
788 
789 !> Calculates the acceleration due to the gradient of kinetic energy.
790 subroutine gradke(u, v, h, KE, KEx, KEy, k, OBC, G, CS)
791  type(ocean_grid_type), intent(in) :: G !< Ocen grid structure
792  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: u !< Zonal velocity (m/s)
793  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: v !< Meridional velocity (m/s)
794  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thickness (m or kg/m2)
795  real, dimension(SZI_(G) ,SZJ_(G) ), intent(out) :: KE !< Kinetic energy (m2/s2)
796  real, dimension(SZIB_(G),SZJ_(G) ), intent(out) :: KEx !< Zonal acceleration due to kinetic
797  !! energy gradient (m/s2)
798  real, dimension(SZI_(G) ,SZJB_(G)), intent(out) :: KEy !< Meridional acceleration due to kinetic
799  !! energy gradient (m/s2)
800  integer, intent(in) :: k !< Layer number to calculate for
801  type(ocean_obc_type), pointer :: OBC !< Open boundary control structure
802  type(coriolisadv_cs), pointer :: CS !< Control structure for MOM_CoriolisAdv
803  ! Local variables
804  real :: um, up, vm, vp ! Temporary variables with units of m s-1.
805  real :: um2, up2, vm2, vp2 ! Temporary variables with units of m2 s-2.
806  real :: um2a, up2a, vm2a, vp2a ! Temporary variables with units of m4 s-2.
807  integer :: i, j, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, n
808 
809  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
810  isq = g%IscB ; ieq = g%IecB ; jsq = g%JscB ; jeq = g%JecB
811 
812 
813  ! Calculate KE (Kinetic energy for use in the -grad(KE) acceleration term).
814  if (cs%KE_Scheme.eq.ke_arakawa) then
815  ! The following calculation of Kinetic energy includes the metric terms
816  ! identified in Arakawa & Lamb 1982 as important for KE conservation. It
817  ! also includes the possibility of partially-blocked tracer cell faces.
818  do j=jsq,jeq+1 ; do i=isq,ieq+1
819  ke(i,j) = ( ( g%areaCu( i ,j)*(u( i ,j,k)*u( i ,j,k)) &
820  +g%areaCu(i-1,j)*(u(i-1,j,k)*u(i-1,j,k)) ) &
821  +( g%areaCv(i, j )*(v(i, j ,k)*v(i, j ,k)) &
822  +g%areaCv(i,j-1)*(v(i,j-1,k)*v(i,j-1,k)) ) &
823  )*0.25*g%IareaT(i,j)
824  enddo ; enddo
825  elseif (cs%KE_Scheme.eq.ke_simple_gudonov) then
826  ! The following discretization of KE is based on the one-dimensinal Gudonov
827  ! scheme which does not take into account any geometric factors
828  do j=jsq,jeq+1 ; do i=isq,ieq+1
829  up = 0.5*( u(i-1,j,k) + abs( u(i-1,j,k) ) ) ; up2 = up*up
830  um = 0.5*( u( i ,j,k) - abs( u( i ,j,k) ) ) ; um2 = um*um
831  vp = 0.5*( v(i,j-1,k) + abs( v(i,j-1,k) ) ) ; vp2 = vp*vp
832  vm = 0.5*( v(i, j ,k) - abs( v(i, j ,k) ) ) ; vm2 = vm*vm
833  ke(i,j) = ( max(up2,um2) + max(vp2,vm2) ) *0.5
834  enddo ; enddo
835  elseif (cs%KE_Scheme.eq.ke_gudonov) then
836  ! The following discretization of KE is based on the one-dimensinal Gudonov
837  ! scheme but has been adapted to take horizontal grid factors into account
838  do j=jsq,jeq+1 ; do i=isq,ieq+1
839  up = 0.5*( u(i-1,j,k) + abs( u(i-1,j,k) ) ) ; up2a = up*up*g%areaCu(i-1,j)
840  um = 0.5*( u( i ,j,k) - abs( u( i ,j,k) ) ) ; um2a = um*um*g%areaCu( i ,j)
841  vp = 0.5*( v(i,j-1,k) + abs( v(i,j-1,k) ) ) ; vp2a = vp*vp*g%areaCv(i,j-1)
842  vm = 0.5*( v(i, j ,k) - abs( v(i, j ,k) ) ) ; vm2a = vm*vm*g%areaCv(i, j )
843  ke(i,j) = ( max(um2a,up2a) + max(vm2a,vp2a) )*0.5*g%IareaT(i,j)
844  enddo ; enddo
845  endif
846 
847  ! Term - d(KE)/dx.
848  do j=js,je ; do i=isq,ieq
849  kex(i,j) = (ke(i+1,j) - ke(i,j)) * g%IdxCu(i,j)
850  enddo ; enddo
851 
852  ! Term - d(KE)/dy.
853  do j=jsq,jeq ; do i=is,ie
854  key(i,j) = (ke(i,j+1) - ke(i,j)) * g%IdyCv(i,j)
855  enddo ; enddo
856 
857  if (associated(obc)) then
858  do n=1,obc%number_of_segments
859  if (obc%segment(n)%is_N_or_S) then
860  do i=obc%segment(n)%HI%isd,obc%segment(n)%HI%ied
861  key(i,obc%segment(n)%HI%JsdB) = 0.
862  enddo
863  elseif (obc%segment(n)%is_E_or_W) then
864  do j=obc%segment(n)%HI%jsd,obc%segment(n)%HI%jed
865  kex(obc%segment(n)%HI%IsdB,j) = 0.
866  enddo
867  endif
868  enddo
869  endif
870 
871 end subroutine gradke
872 
873 !> Initializes the control structure for coriolisadv_cs
874 subroutine coriolisadv_init(Time, G, param_file, diag, AD, CS)
875  type(time_type), target, intent(in) :: Time !< Current model time
876  type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
877  type(param_file_type), intent(in) :: param_file !< Runtime parameter handles
878  type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure
879  type(accel_diag_ptrs), target, intent(inout) :: AD !< Strorage for acceleration diagnostics
880  type(coriolisadv_cs), pointer :: CS !< Control structure fro MOM_CoriolisAdv
881  ! Local variables
882 ! This include declares and sets the variable "version".
883 #include "version_variable.h"
884  character(len=40) :: mdl = "MOM_CoriolisAdv" ! This module's name.
885  character(len=20) :: tmpstr
886  character(len=400) :: mesg
887  integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, nz
888 
889  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed ; nz = g%ke
890  isdb = g%IsdB ; iedb = g%IedB ; jsdb = g%JsdB ; jedb = g%JedB
891 
892  if (associated(cs)) then
893  call mom_error(warning, "CoriolisAdv_init called with associated control structure.")
894  return
895  endif
896  allocate(cs)
897 
898  cs%diag => diag ; cs%Time => time
899 
900  ! Read all relevant parameters and write them to the model log.
901  call log_version(param_file, mdl, version, "")
902  call get_param(param_file, mdl, "NOSLIP", cs%no_slip, &
903  "If true, no slip boundary conditions are used; otherwise \n"//&
904  "free slip boundary conditions are assumed. The \n"//&
905  "implementation of the free slip BCs on a C-grid is much \n"//&
906  "cleaner than the no slip BCs. The use of free slip BCs \n"//&
907  "is strongly encouraged, and no slip BCs are not used with \n"//&
908  "the biharmonic viscosity.", default=.false.)
909 
910  call get_param(param_file, mdl, "CORIOLIS_EN_DIS", cs%Coriolis_En_Dis, &
911  "If true, two estimates of the thickness fluxes are used \n"//&
912  "to estimate the Coriolis term, and the one that \n"//&
913  "dissipates energy relative to the other one is used.", &
914  default=.false.)
915 
916  ! Set %Coriolis_Scheme
917  ! (Select the baseline discretization for the Coriolis term)
918  call get_param(param_file, mdl, "CORIOLIS_SCHEME", tmpstr, &
919  "CORIOLIS_SCHEME selects the discretization for the \n"//&
920  "Coriolis terms. Valid values are: \n"//&
921  "\t SADOURNY75_ENERGY - Sadourny, 1975; energy cons. \n"//&
922  "\t ARAKAWA_HSU90 - Arakawa & Hsu, 1990 \n"//&
923  "\t SADOURNY75_ENSTRO - Sadourny, 1975; enstrophy cons. \n"//&
924  "\t ARAKAWA_LAMB81 - Arakawa & Lamb, 1981; En. + Enst.\n"//&
925  "\t ARAKAWA_LAMB_BLEND - A blend of Arakawa & Lamb with \n"//&
926  "\t Arakawa & Hsu and Sadourny energy", &
927  default=sadourny75_energy_string)
928  tmpstr = uppercase(tmpstr)
929  select case (tmpstr)
931  cs%Coriolis_Scheme = sadourny75_energy
932  case (arakawa_hsu_string)
933  cs%Coriolis_Scheme = arakawa_hsu90
935  cs%Coriolis_Scheme = sadourny75_enstro
936  case (arakawa_lamb_string)
937  cs%Coriolis_Scheme = arakawa_lamb81
938  case (al_blend_string)
939  cs%Coriolis_Scheme = al_blend
940  case (robust_enstro_string)
941  cs%Coriolis_Scheme = robust_enstro
942  cs%Coriolis_En_Dis = .false.
943  case default
944  call mom_mesg('CoriolisAdv_init: Coriolis_Scheme ="'//trim(tmpstr)//'"', 0)
945  call mom_error(fatal, "CoriolisAdv_init: Unrecognized setting "// &
946  "#define CORIOLIS_SCHEME "//trim(tmpstr)//" found in input file.")
947  end select
948  if (cs%Coriolis_Scheme == al_blend) then
949  call get_param(param_file, mdl, "CORIOLIS_BLEND_WT_LIN", cs%wt_lin_blend, &
950  "A weighting value for the ratio of inverse thicknesses, \n"//&
951  "beyond which the blending between Sadourny Energy and \n"//&
952  "Arakawa & Hsu goes linearly to 0 when CORIOLIS_SCHEME \n"//&
953  "is ARAWAKA_LAMB_BLEND. This must be between 1 and 1e-16.", &
954  units="nondim", default=0.125)
955  call get_param(param_file, mdl, "CORIOLIS_BLEND_F_EFF_MAX", cs%F_eff_max_blend, &
956  "The factor by which the maximum effective Coriolis \n"//&
957  "acceleration from any point can be increased when \n"//&
958  "blending different discretizations with the \n"//&
959  "ARAKAWA_LAMB_BLEND Coriolis scheme. This must be \n"//&
960  "greater than 2.0 (the max value for Sadourny energy).", &
961  units="nondim", default=4.0)
962  cs%wt_lin_blend = min(1.0, max(cs%wt_lin_blend,1e-16))
963  if (cs%F_eff_max_blend < 2.0) call mom_error(warning, "CoriolisAdv_init: "//&
964  "CORIOLIS_BLEND_F_EFF_MAX should be at least 2.")
965  endif
966 
967  mesg = "If true, the Coriolis terms at u-points are bounded by \n"//&
968  "the four estimates of (f+rv)v from the four neighboring \n"//&
969  "v-points, and similarly at v-points."
970  if (cs%Coriolis_En_Dis .and. (cs%Coriolis_Scheme == sadourny75_energy)) then
971  mesg = trim(mesg)//" This option is \n"//&
972  "always effectively false with CORIOLIS_EN_DIS defined and \n"//&
973  "CORIOLIS_SCHEME set to "//trim(sadourny75_energy_string)//"."
974  else
975  mesg = trim(mesg)//" This option would \n"//&
976  "have no effect on the SADOURNY Coriolis scheme if it \n"//&
977  "were possible to use centered difference thickness fluxes."
978  endif
979  call get_param(param_file, mdl, "BOUND_CORIOLIS", cs%bound_Coriolis, mesg, &
980  default=.false.)
981  if ((cs%Coriolis_En_Dis .and. (cs%Coriolis_Scheme == sadourny75_energy)) .or. &
982  (cs%Coriolis_Scheme == robust_enstro)) cs%bound_Coriolis = .false.
983 
984  ! Set KE_Scheme (selects discretization of KE)
985  call get_param(param_file, mdl, "KE_SCHEME", tmpstr, &
986  "KE_SCHEME selects the discretization for acceleration \n"//&
987  "due to the kinetic energy gradient. Valid values are: \n"//&
988  "\t KE_ARAKAWA, KE_SIMPLE_GUDONOV, KE_GUDONOV", &
989  default=ke_arakawa_string)
990  tmpstr = uppercase(tmpstr)
991  select case (tmpstr)
992  case (ke_arakawa_string); cs%KE_Scheme = ke_arakawa
993  case (ke_simple_gudonov_string); cs%KE_Scheme = ke_simple_gudonov
994  case (ke_gudonov_string); cs%KE_Scheme = ke_gudonov
995  case default
996  call mom_mesg('CoriolisAdv_init: KE_Scheme ="'//trim(tmpstr)//'"', 0)
997  call mom_error(fatal, "CoriolisAdv_init: "// &
998  "#define KE_SCHEME "//trim(tmpstr)//" in input file is invalid.")
999  end select
1000 
1001  ! Set PV_Adv_Scheme (selects discretization of PV advection)
1002  call get_param(param_file, mdl, "PV_ADV_SCHEME", tmpstr, &
1003  "PV_ADV_SCHEME selects the discretization for PV \n"//&
1004  "advection. Valid values are: \n"//&
1005  "\t PV_ADV_CENTERED - centered (aka Sadourny, 75) \n"//&
1006  "\t PV_ADV_UPWIND1 - upwind, first order", &
1007  default=pv_adv_centered_string)
1008  select case (uppercase(tmpstr))
1009  case (pv_adv_centered_string)
1010  cs%PV_Adv_Scheme = pv_adv_centered
1011  case (pv_adv_upwind1_string)
1012  cs%PV_Adv_Scheme = pv_adv_upwind1
1013  case default
1014  call mom_mesg('CoriolisAdv_init: PV_Adv_Scheme ="'//trim(tmpstr)//'"', 0)
1015  call mom_error(fatal, "CoriolisAdv_init: "// &
1016  "#DEFINE PV_ADV_SCHEME in input file is invalid.")
1017  end select
1018 
1019  cs%id_rv = register_diag_field('ocean_model', 'RV', diag%axesBL, time, &
1020  'Relative Vorticity', 'second-1')
1021 
1022  cs%id_PV = register_diag_field('ocean_model', 'PV', diag%axesBL, time, &
1023  'Potential Vorticity', 'meter-1 second-1')
1024 
1025  cs%id_gKEu = register_diag_field('ocean_model', 'gKEu', diag%axesCuL, time, &
1026  'Zonal Acceleration from Grad. Kinetic Energy', 'meter-1 second-2')
1027  if (cs%id_gKEu > 0) call safe_alloc_ptr(ad%gradKEu,isdb,iedb,jsd,jed,nz)
1028 
1029  cs%id_gKEv = register_diag_field('ocean_model', 'gKEv', diag%axesCvL, time, &
1030  'Meridional Acceleration from Grad. Kinetic Energy', 'meter-1 second-2')
1031  if (cs%id_gKEv > 0) call safe_alloc_ptr(ad%gradKEv,isd,ied,jsdb,jedb,nz)
1032 
1033  cs%id_rvxu = register_diag_field('ocean_model', 'rvxu', diag%axesCvL, time, &
1034  'Meridional Acceleration from Relative Vorticity', 'meter-1 second-2')
1035  if (cs%id_rvxu > 0) call safe_alloc_ptr(ad%rv_x_u,isd,ied,jsdb,jedb,nz)
1036 
1037  cs%id_rvxv = register_diag_field('ocean_model', 'rvxv', diag%axesCuL, time, &
1038  'Zonal Acceleration from Relative Vorticity', 'meter-1 second-2')
1039  if (cs%id_rvxv > 0) call safe_alloc_ptr(ad%rv_x_v,isdb,iedb,jsd,jed,nz)
1040 
1041 end subroutine coriolisadv_init
1042 
1043 !> Destructor for coriolisadv_cs
1044 subroutine coriolisadv_end(CS)
1045  type(coriolisadv_cs), pointer :: CS !< Control structure fro MOM_CoriolisAdv
1046  deallocate(cs)
1047 end subroutine coriolisadv_end
1048 
1049 !> \namespace mom_coriolisadv
1050 !!
1051 !! This file contains the subroutine that calculates the time
1052 !! derivatives of the velocities due to Coriolis acceleration and
1053 !! momentum advection. This subroutine uses either a vorticity
1054 !! advection scheme from Arakawa and Hsu, Mon. Wea. Rev. 1990, or
1055 !! Sadourny's (JAS 1975) energy conserving scheme. Both have been
1056 !! modified to use general orthogonal coordinates as described in
1057 !! Arakawa and Lamb, Mon. Wea. Rev. 1981. Both schemes are second
1058 !! order accurate, and allow for vanishingly small layer thicknesses.
1059 !! The Arakawa and Hsu scheme globally conserves both total energy
1060 !! and potential enstrophy in the limit of nondivergent flow.
1061 !! Sadourny's energy conserving scheme conserves energy if the flow
1062 !! is nondivergent or centered difference thickness fluxes are used.
1063 !!
1064 !! Two sets of boundary conditions have been coded in the
1065 !! definition of relative vorticity. These are written as:
1066 !! NOSLIP defined (in spherical coordinates):
1067 !! relvort = dv/dx (east & west), with v = 0.
1068 !! relvort = -sec(Q) * d(u cos(Q))/dy (north & south), with u = 0.
1069 !!
1070 !! NOSLIP not defined (free slip):
1071 !! relvort = 0 (all boundaries)
1072 !!
1073 !! with Q temporarily defined as latitude. The free slip boundary
1074 !! condition is much more natural on a C-grid.
1075 !!
1076 !! A small fragment of the grid is shown below:
1077 !! \verbatim
1078 !!
1079 !! j+1 x ^ x ^ x At x: q, CoriolisBu
1080 !! j+1 > o > o > At ^: v, CAv, vh
1081 !! j x ^ x ^ x At >: u, CAu, uh, a, b, c, d
1082 !! j > o > o > At o: h, KE
1083 !! j-1 x ^ x ^ x
1084 !! i-1 i i+1 At x & ^:
1085 !! i i+1 At > & o:
1086 !! \endverbatim
1087 !!
1088 !! The boundaries always run through q grid points (x).
1089 
1090 end module mom_coriolisadv
integer, parameter, public obc_direction_s
Indicates the boundary is an effective southern boundary.
subroutine, public coradcalc(u, v, h, uh, vh, CAu, CAv, OBC, AD, G, GV, CS)
Calculates the Coriolis and momentum advection contributions to the acceleration. ...
integer, parameter, public obc_direction_w
Indicates the boundary is an effective western boundary.
character *(20), parameter robust_enstro_string
integer, parameter, public obc_direction_n
Indicates the boundary is an effective northern boundary.
integer, parameter sadourny75_energy
character *(20), parameter ke_arakawa_string
integer, parameter pv_adv_upwind1
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...
character *(20), parameter arakawa_lamb_string
character *(20), parameter ke_simple_gudonov_string
Provides the ocean grid type.
Definition: MOM_grid.F90:2
character *(20), parameter sadourny75_enstro_string
Accelerations due to the Coriolis force and momentum advection.
The accel_diag_ptrs structure contains pointers to arrays with accelerations, which can later be used...
subroutine gradke(u, v, h, KE, KEx, KEy, k, OBC, G, CS)
Calculates the acceleration due to the gradient of kinetic energy.
integer, parameter sadourny75_enstro
subroutine, public coriolisadv_init(Time, G, param_file, diag, AD, CS)
Initializes the control structure for coriolisadv_cs.
integer, parameter ke_simple_gudonov
Control structure for mom_coriolisadv.
character *(20), parameter pv_adv_upwind1_string
character *(20), parameter ke_gudonov_string
logical function, public query_averaging_enabled(diag_cs, time_int, time_end)
character(len=len(input_string)) function, public uppercase(input_string)
character *(20), parameter arakawa_hsu_string
character *(20), parameter sadourny75_energy_string
integer, parameter robust_enstro
subroutine, public mom_mesg(message, verb, all_print)
integer, parameter al_blend
integer, parameter, public obc_direction_e
Indicates the boundary is an effective eastern boundary.
integer, parameter pv_adv_centered
integer, parameter arakawa_lamb81
subroutine, public coriolisadv_end(CS)
Destructor for coriolisadv_cs.
integer, parameter ke_gudonov
integer, parameter arakawa_hsu90
Controls where open boundary conditions are applied.
character *(20), parameter pv_adv_centered_string
character *(20), parameter al_blend_string
integer function, public register_diag_field(module_name, field_name, axes, init_time, long_name, units, missing_value, range, mask_variant, standard_name, verbose, do_not_log, err_msg, interp_method, tile_count, cmor_field_name, cmor_long_name, cmor_units, cmor_standard_name, cell_methods, x_cell_method, y_cell_method, v_cell_method, conversion, v_extensive)
Returns the "diag_mediator" handle for a group (native, CMOR, z-coord, ...) of diagnostics derived fr...
subroutine, public mom_error(level, message, all_print)
integer, parameter ke_arakawa