MOM6
MOM_wave_speed.F90
1 !> Routines for calculating baroclinic wave speeds
3 
4 ! This file is part of MOM6. See LICENSE.md for the license.
5 
6 use mom_diag_mediator, only : post_data, query_averaging_enabled, diag_ctrl
7 use mom_error_handler, only : mom_error, fatal, warning
9 use mom_grid, only : ocean_grid_type
10 use mom_remapping, only : remapping_cs, initialize_remapping, remapping_core_h
15 
16 implicit none ; private
17 
18 #include <MOM_memory.h>
19 
20 public wave_speed, wave_speeds, wave_speed_init, wave_speed_set_param
21 
22 ! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional
23 ! consistency testing. These are noted in comments with units like Z, H, L, and T, along with
24 ! their mks counterparts with notation like "a velocity [Z T-1 ~> m s-1]". If the units
25 ! vary with the Boussinesq approximation, the Boussinesq variant is given first.
26 
27 !> Control structure for MOM_wave_speed
28 type, public :: wave_speed_cs ; private
29  logical :: use_ebt_mode = .false. !< If true, calculate the equivalent barotropic wave speed instead
30  !! of the first baroclinic wave speed.
31  !! This parameter controls the default behavior of wave_speed() which
32  !! can be overridden by optional arguments.
33  real :: mono_n2_column_fraction = 0. !< The lower fraction of water column over which N2 is limited as
34  !! monotonic for the purposes of calculating the equivalent barotropic
35  !! wave speed. This parameter controls the default behavior of
36  !! wave_speed() which can be overridden by optional arguments.
37  real :: mono_n2_depth = -1. !< The depth below which N2 is limited as monotonic for the purposes of
38  !! calculating the equivalent barotropic wave speed [Z ~> m].
39  !! This parameter controls the default behavior of wave_speed() which
40  !! can be overridden by optional arguments.
41  type(remapping_cs) :: remapping_cs !< Used for vertical remapping when calculating equivalent barotropic
42  !! mode structure.
43  type(diag_ctrl), pointer :: diag !< Diagnostics control structure
44 end type wave_speed_cs
45 
46 contains
47 
48 !> Calculates the wave speed of the first baroclinic mode.
49 subroutine wave_speed(h, tv, G, GV, US, cg1, CS, full_halos, use_ebt_mode, &
50  mono_N2_column_fraction, mono_N2_depth, modal_structure)
51  type(ocean_grid_type), intent(in) :: g !< Ocean grid structure
52  type(verticalgrid_type), intent(in) :: gv !< Vertical grid structure
53  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
54  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), &
55  intent(in) :: h !< Layer thickness [H ~> m or kg m-2]
56  type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamic variables
57  real, dimension(SZI_(G),SZJ_(G)), intent(out) :: cg1 !< First mode internal wave speed [m s-1]
58  type(wave_speed_cs), pointer :: cs !< Control structure for MOM_wave_speed
59  logical, optional, intent(in) :: full_halos !< If true, do the calculation
60  !! over the entire computational domain.
61  logical, optional, intent(in) :: use_ebt_mode !< If true, use the equivalent
62  !! barotropic mode instead of the first baroclinic mode.
63  real, optional, intent(in) :: mono_n2_column_fraction !< The lower fraction
64  !! of water column over which N2 is limited as monotonic
65  !! for the purposes of calculating vertical modal structure.
66  real, optional, intent(in) :: mono_n2_depth !< A depth below which N2 is limited as
67  !! monotonic for the purposes of calculating vertical
68  !! modal structure [m].
69  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), &
70  optional, intent(out) :: modal_structure !< Normalized model structure [nondim]
71 
72  ! Local variables
73  real, dimension(SZK_(G)+1) :: &
74  drho_dt, drho_ds, &
75  pres, t_int, s_int, &
76  gprime ! The reduced gravity across each interface [m2 Z-1 s-2 ~> m s-2].
77  real, dimension(SZK_(G)) :: &
78  igl, igu ! The inverse of the reduced gravity across an interface times
79  ! the thickness of the layer below (Igl) or above (Igu) it [s2 m-2].
80  real, dimension(SZK_(G),SZI_(G)) :: &
81  hf, tf, sf, rf
82  real, dimension(SZK_(G)) :: &
83  hc, tc, sc, rc
84  real det, ddet, detkm1, detkm2, ddetkm1, ddetkm2
85  real :: lam, dlam, lam0
86  real :: min_h_frac
87  real :: z_to_pa ! A conversion factor from thicknesses (in Z) to pressure (in Pa)
88  real, dimension(SZI_(G)) :: &
89  htot, hmin, & ! Thicknesses [Z ~> m].
90  h_here, hxt_here, hxs_here, hxr_here
91  real :: speed2_tot
92  real :: i_hnew, drxh_sum
93  real :: l2_to_z2 ! A scaling factor squared from units of lateral distances to depths [Z2 m-2 ~> 1].
94  real, parameter :: tol1 = 0.0001, tol2 = 0.001
95  real, pointer, dimension(:,:,:) :: t => null(), s => null()
96  real :: g_rho0 ! G_Earth/Rho0 [m5 Z-1 s-2 kg-1 ~> m4 s-2 kg-1].
97  real :: rescale, i_rescale
98  integer :: kf(szi_(g))
99  integer, parameter :: max_itt = 10
100  real :: lam_it(max_itt), det_it(max_itt), ddet_it(max_itt)
101  logical :: use_eos ! If true, density is calculated from T & S using an
102  ! equation of state.
103  integer :: kc
104  integer :: i, j, k, k2, itt, is, ie, js, je, nz
105  real :: hw, gp, sum_hc, n2min
106  logical :: l_use_ebt_mode, calc_modal_structure
107  real :: l_mono_n2_column_fraction, l_mono_n2_depth
108  real :: mode_struct(szk_(g)), ms_min, ms_max, ms_sq
109 
110  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
111 
112  if (.not. associated(cs)) call mom_error(fatal, "MOM_wave_speed: "// &
113  "Module must be initialized before it is used.")
114  if (present(full_halos)) then ; if (full_halos) then
115  is = g%isd ; ie = g%ied ; js = g%jsd ; je = g%jed
116  endif ; endif
117 
118  l2_to_z2 = us%m_to_Z**2
119 
120  l_use_ebt_mode = cs%use_ebt_mode
121  if (present(use_ebt_mode)) l_use_ebt_mode = use_ebt_mode
122  l_mono_n2_column_fraction = cs%mono_N2_column_fraction
123  if (present(mono_n2_column_fraction)) l_mono_n2_column_fraction = mono_n2_column_fraction
124  l_mono_n2_depth = us%m_to_Z*cs%mono_N2_depth
125  if (present(mono_n2_depth)) l_mono_n2_depth = us%m_to_Z*mono_n2_depth
126  calc_modal_structure = l_use_ebt_mode
127  if (present(modal_structure)) calc_modal_structure = .true.
128  if (calc_modal_structure) then
129  do k=1,nz; do j=js,je; do i=is,ie
130  modal_structure(i,j,k) = 0.0
131  enddo ; enddo ; enddo
132  endif
133 
134  s => tv%S ; t => tv%T
135  g_rho0 = us%L_T_to_m_s**2 * gv%g_Earth / gv%Rho0
136  z_to_pa = gv%Z_to_H * gv%H_to_Pa
137  use_eos = associated(tv%eqn_of_state)
138 
139  rescale = 1024.0**4 ; i_rescale = 1.0/rescale
140 
141  min_h_frac = tol1 / real(nz)
142 !$OMP parallel do default(none) shared(is,ie,js,je,nz,h,G,GV,US,min_h_frac,use_EOS,T,S,tv,&
143 !$OMP calc_modal_structure,l_use_ebt_mode,modal_structure, &
144 !$OMP l_mono_N2_column_fraction,l_mono_N2_depth,CS, &
145 !$OMP Z_to_Pa,cg1,g_Rho0,rescale,I_rescale,L2_to_Z2) &
146 !$OMP private(htot,hmin,kf,H_here,HxT_here,HxS_here,HxR_here, &
147 !$OMP Hf,Tf,Sf,Rf,pres,T_int,S_int,drho_dT, &
148 !$OMP drho_dS,drxh_sum,kc,Hc,Tc,Sc,I_Hnew,gprime, &
149 !$OMP Rc,speed2_tot,Igl,Igu,lam0,lam,lam_it,dlam, &
150 !$OMP mode_struct,sum_hc,N2min,gp,hw, &
151 !$OMP ms_min,ms_max,ms_sq, &
152 !$OMP det,ddet,detKm1,ddetKm1,detKm2,ddetKm2,det_it,ddet_it)
153  do j=js,je
154  ! First merge very thin layers with the one above (or below if they are
155  ! at the top). This also transposes the row order so that columns can
156  ! be worked upon one at a time.
157  do i=is,ie ; htot(i) = 0.0 ; enddo
158  do k=1,nz ; do i=is,ie ; htot(i) = htot(i) + h(i,j,k)*gv%H_to_Z ; enddo ; enddo
159 
160  do i=is,ie
161  hmin(i) = htot(i)*min_h_frac ; kf(i) = 1 ; h_here(i) = 0.0
162  hxt_here(i) = 0.0 ; hxs_here(i) = 0.0 ; hxr_here(i) = 0.0
163  enddo
164  if (use_eos) then
165  do k=1,nz ; do i=is,ie
166  if ((h_here(i) > hmin(i)) .and. (h(i,j,k)*gv%H_to_Z > hmin(i))) then
167  hf(kf(i),i) = h_here(i)
168  tf(kf(i),i) = hxt_here(i) / h_here(i)
169  sf(kf(i),i) = hxs_here(i) / h_here(i)
170  kf(i) = kf(i) + 1
171 
172  ! Start a new layer
173  h_here(i) = h(i,j,k)*gv%H_to_Z
174  hxt_here(i) = (h(i,j,k)*gv%H_to_Z)*t(i,j,k)
175  hxs_here(i) = (h(i,j,k)*gv%H_to_Z)*s(i,j,k)
176  else
177  h_here(i) = h_here(i) + h(i,j,k)*gv%H_to_Z
178  hxt_here(i) = hxt_here(i) + (h(i,j,k)*gv%H_to_Z)*t(i,j,k)
179  hxs_here(i) = hxs_here(i) + (h(i,j,k)*gv%H_to_Z)*s(i,j,k)
180  endif
181  enddo ; enddo
182  do i=is,ie ; if (h_here(i) > 0.0) then
183  hf(kf(i),i) = h_here(i)
184  tf(kf(i),i) = hxt_here(i) / h_here(i)
185  sf(kf(i),i) = hxs_here(i) / h_here(i)
186  endif ; enddo
187  else
188  do k=1,nz ; do i=is,ie
189  if ((h_here(i) > hmin(i)) .and. (h(i,j,k)*gv%H_to_Z > hmin(i))) then
190  hf(kf(i),i) = h_here(i) ; rf(kf(i),i) = hxr_here(i) / h_here(i)
191  kf(i) = kf(i) + 1
192 
193  ! Start a new layer
194  h_here(i) = h(i,j,k)*gv%H_to_Z
195  hxr_here(i) = (h(i,j,k)*gv%H_to_Z)*gv%Rlay(k)
196  else
197  h_here(i) = h_here(i) + h(i,j,k)*gv%H_to_Z
198  hxr_here(i) = hxr_here(i) + (h(i,j,k)*gv%H_to_Z)*gv%Rlay(k)
199  endif
200  enddo ; enddo
201  do i=is,ie ; if (h_here(i) > 0.0) then
202  hf(kf(i),i) = h_here(i) ; rf(kf(i),i) = hxr_here(i) / h_here(i)
203  endif ; enddo
204  endif
205 
206  ! From this point, we can work on individual columns without causing memory
207  ! to have page faults.
208  do i=is,ie ; if (g%mask2dT(i,j) > 0.5) then
209  if (use_eos) then
210  pres(1) = 0.0
211  do k=2,kf(i)
212  pres(k) = pres(k-1) + z_to_pa*hf(k-1,i)
213  t_int(k) = 0.5*(tf(k,i)+tf(k-1,i))
214  s_int(k) = 0.5*(sf(k,i)+sf(k-1,i))
215  enddo
216  call calculate_density_derivs(t_int, s_int, pres, drho_dt, drho_ds, 2, &
217  kf(i)-1, tv%eqn_of_state)
218 
219  ! Sum the reduced gravities to find out how small a density difference
220  ! is negligibly small.
221  drxh_sum = 0.0
222  do k=2,kf(i)
223  drxh_sum = drxh_sum + 0.5*(hf(k-1,i)+hf(k,i)) * &
224  max(0.0,drho_dt(k)*(tf(k,i)-tf(k-1,i)) + &
225  drho_ds(k)*(sf(k,i)-sf(k-1,i)))
226  enddo
227  else
228  drxh_sum = 0.0
229  do k=2,kf(i)
230  drxh_sum = drxh_sum + 0.5*(hf(k-1,i)+hf(k,i)) * &
231  max(0.0,rf(k,i)-rf(k-1,i))
232  enddo
233  endif
234 
235  if (calc_modal_structure) then
236  mode_struct(:) = 0.
237  endif
238 
239  ! Find gprime across each internal interface, taking care of convective
240  ! instabilities by merging layers.
241  if (drxh_sum <= 0.0) then
242  cg1(i,j) = 0.0
243  else
244  ! Merge layers to eliminate convective instabilities or exceedingly
245  ! small reduced gravities.
246  if (use_eos) then
247  kc = 1
248  hc(1) = hf(1,i) ; tc(1) = tf(1,i) ; sc(1) = sf(1,i)
249  do k=2,kf(i)
250  if ((drho_dt(k)*(tf(k,i)-tc(kc)) + drho_ds(k)*(sf(k,i)-sc(kc))) * &
251  (hc(kc) + hf(k,i)) < 2.0 * tol2*drxh_sum) then
252  ! Merge this layer with the one above and backtrack.
253  i_hnew = 1.0 / (hc(kc) + hf(k,i))
254  tc(kc) = (hc(kc)*tc(kc) + hf(k,i)*tf(k,i)) * i_hnew
255  sc(kc) = (hc(kc)*sc(kc) + hf(k,i)*sf(k,i)) * i_hnew
256  hc(kc) = (hc(kc) + hf(k,i))
257  ! Backtrack to remove any convective instabilities above... Note
258  ! that the tolerance is a factor of two larger, to avoid limit how
259  ! far back we go.
260  do k2=kc,2,-1
261  if ((drho_dt(k2)*(tc(k2)-tc(k2-1)) + drho_ds(k2)*(sc(k2)-sc(k2-1))) * &
262  (hc(k2) + hc(k2-1)) < tol2*drxh_sum) then
263  ! Merge the two bottommost layers. At this point kc = k2.
264  i_hnew = 1.0 / (hc(kc) + hc(kc-1))
265  tc(kc-1) = (hc(kc)*tc(kc) + hc(kc-1)*tc(kc-1)) * i_hnew
266  sc(kc-1) = (hc(kc)*sc(kc) + hc(kc-1)*sc(kc-1)) * i_hnew
267  hc(kc-1) = (hc(kc) + hc(kc-1))
268  kc = kc - 1
269  else ; exit ; endif
270  enddo
271  else
272  ! Add a new layer to the column.
273  kc = kc + 1
274  drho_ds(kc) = drho_ds(k) ; drho_dt(kc) = drho_dt(k)
275  tc(kc) = tf(k,i) ; sc(kc) = sf(k,i) ; hc(kc) = hf(k,i)
276  endif
277  enddo
278  ! At this point there are kc layers and the gprimes should be positive.
279  do k=2,kc ! Revisit this if non-Boussinesq.
280  gprime(k) = g_rho0 * (drho_dt(k)*(tc(k)-tc(k-1)) + &
281  drho_ds(k)*(sc(k)-sc(k-1)))
282  enddo
283  else ! .not.use_EOS
284  ! Do the same with density directly...
285  kc = 1
286  hc(1) = hf(1,i) ; rc(1) = rf(1,i)
287  do k=2,kf(i)
288  if ((rf(k,i) - rc(kc)) * (hc(kc) + hf(k,i)) < 2.0*tol2*drxh_sum) then
289  ! Merge this layer with the one above and backtrack.
290  rc(kc) = (hc(kc)*rc(kc) + hf(k,i)*rf(k,i)) / (hc(kc) + hf(k,i))
291  hc(kc) = (hc(kc) + hf(k,i))
292  ! Backtrack to remove any convective instabilities above... Note
293  ! that the tolerance is a factor of two larger, to avoid limit how
294  ! far back we go.
295  do k2=kc,2,-1
296  if ((rc(k2)-rc(k2-1)) * (hc(k2)+hc(k2-1)) < tol2*drxh_sum) then
297  ! Merge the two bottommost layers. At this point kc = k2.
298  rc(kc-1) = (hc(kc)*rc(kc) + hc(kc-1)*rc(kc-1)) / (hc(kc) + hc(kc-1))
299  hc(kc-1) = (hc(kc) + hc(kc-1))
300  kc = kc - 1
301  else ; exit ; endif
302  enddo
303  else
304  ! Add a new layer to the column.
305  kc = kc + 1
306  rc(kc) = rf(k,i) ; hc(kc) = hf(k,i)
307  endif
308  enddo
309  ! At this point there are kc layers and the gprimes should be positive.
310  do k=2,kc ! Revisit this if non-Boussinesq.
311  gprime(k) = g_rho0 * (rc(k)-rc(k-1))
312  enddo
313  endif ! use_EOS
314 
315  ! Sum the contributions from all of the interfaces to give an over-estimate
316  ! of the first-mode wave speed. Also populate Igl and Igu which are the
317  ! non-leading diagonals of the tridiagonal matrix.
318  if (kc >= 2) then
319  speed2_tot = 0.0
320  if (l_use_ebt_mode) then
321  igu(1) = 0. ! Neumann condition for pressure modes
322  sum_hc = hc(1)
323  n2min = l2_to_z2*gprime(2)/hc(1)
324  do k=2,kc
325  hw = 0.5*(hc(k-1)+hc(k))
326  gp = gprime(k)
327  if (l_mono_n2_column_fraction>0. .or. l_mono_n2_depth>=0.) then
328  if ( ((g%bathyT(i,j)-sum_hc < l_mono_n2_column_fraction*g%bathyT(i,j)) .or. &
329  ((l_mono_n2_depth >= 0.) .and. (sum_hc > l_mono_n2_depth))) .and. &
330  (l2_to_z2*gp > n2min*hw) ) then
331  ! Filters out regions where N2 increases with depth but only in a lower fraction
332  ! of the water column or below a certain depth.
333  gp = us%Z_to_m**2 * (n2min*hw)
334  else
335  n2min = l2_to_z2 * gp/hw
336  endif
337  endif
338  igu(k) = 1.0/(gp*hc(k))
339  igl(k-1) = 1.0/(gp*hc(k-1))
340  speed2_tot = speed2_tot + gprime(k)*(hc(k-1)+hc(k))*0.707
341  sum_hc = sum_hc + hc(k)
342  enddo
343  !Igl(kc) = 0. ! Neumann condition for pressure modes
344  igl(kc) = 2.*igu(kc) ! Dirichlet condition for pressure modes
345  else ! .not. l_use_ebt_mode
346  do k=2,kc
347  igl(k) = 1.0/(gprime(k)*hc(k)) ; igu(k) = 1.0/(gprime(k)*hc(k-1))
348  speed2_tot = speed2_tot + gprime(k)*(hc(k-1)+hc(k))
349  enddo
350  endif
351 
352  if (calc_modal_structure) then
353  mode_struct(1:kc) = 1. ! Uniform flow, first guess
354  endif
355 
356  ! Overestimate the speed to start with.
357  if (calc_modal_structure) then
358  lam0 = 0.5 / speed2_tot ; lam = lam0
359  else
360  lam0 = 1.0 / speed2_tot ; lam = lam0
361  endif
362  ! Find the determinant and its derivative with lam.
363  do itt=1,max_itt
364  lam_it(itt) = lam
365  if (l_use_ebt_mode) then
366  ! This initialization of det,ddet imply Neumann boundary conditions so that first 3 rows
367  ! of the matrix are
368  ! / b(1)-lam igl(1) 0 0 0 ... \
369  ! | igu(2) b(2)-lam igl(2) 0 0 ... |
370  ! | 0 igu(3) b(3)-lam igl(3) 0 ... |
371  ! which is consistent if the eigenvalue problem is for horizontal velocity or pressure modes.
372  !detKm1 = ( Igl(1)-lam) ; ddetKm1 = -1.0
373  !det = (Igu(2)+Igl(2)-lam)*detKm1 - (Igu(2)*Igl(1)) ; ddet = (Igu(2)+Igl(2)-lam)*ddetKm1 - detKm1
374  detkm1 = 1.0 ; ddetkm1 = 0.0
375  det = ( igl(1)-lam) ; ddet = -1.0
376  if (kc>1) then
377  detkm2 = detkm1; ddetkm2 = ddetkm1
378  detkm1 = det; ddetkm1 = ddet
379  det = (igu(2)+igl(2)-lam)*detkm1 - (igu(2)*igl(1))*detkm2
380  ddet = (igu(2)+igl(2)-lam)*ddetkm1 - (igu(2)*igl(1))*ddetkm2 - detkm1
381  endif
382  ! The last two rows of the pressure equation matrix are
383  ! | ... 0 igu(kc-1) b(kc-1)-lam igl(kc-1) |
384  ! \ ... 0 0 igu(kc) b(kc)-lam /
385  else
386  ! This initialization of det,ddet imply Dirichlet boundary conditions so that first 3 rows
387  ! of the matrix are
388  ! / b(2)-lam igl(2) 0 0 0 ... |
389  ! | igu(3) b(3)-lam igl(3) 0 0 ... |
390  ! | 0 igu43) b(4)-lam igl(4) 0 ... |
391  ! which is consistent if the eigenvalue problem is for vertical velocity modes.
392  detkm1 = 1.0 ; ddetkm1 = 0.0
393  det = (igu(2)+igl(2)-lam) ; ddet = -1.0
394  ! The last three rows of the w equation matrix are
395  ! | ... 0 igu(kc-1) b(kc-1)-lam igl(kc-1) 0 |
396  ! | ... 0 0 igu(kc-1) b(kc-1)-lam igl(kc-1) |
397  ! \ ... 0 0 0 igu(kc) b(kc)-lam /
398  endif
399  do k=3,kc
400  detkm2 = detkm1; ddetkm2 = ddetkm1
401  detkm1 = det; ddetkm1 = ddet
402 
403  det = (igu(k)+igl(k)-lam)*detkm1 - (igu(k)*igl(k-1))*detkm2
404  ddet = (igu(k)+igl(k)-lam)*ddetkm1 - (igu(k)*igl(k-1))*ddetkm2 - detkm1
405 
406  ! Rescale det & ddet if det is getting too large.
407  if (abs(det) > rescale) then
408  det = i_rescale*det ; detkm1 = i_rescale*detkm1
409  ddet = i_rescale*ddet ; ddetkm1 = i_rescale*ddetkm1
410  endif
411  enddo
412  ! Use Newton's method iteration to find a new estimate of lam.
413  det_it(itt) = det ; ddet_it(itt) = ddet
414 
415  if ((ddet >= 0.0) .or. (-det > -0.5*lam*ddet)) then
416  ! lam was not an under-estimate, as intended, so Newton's method
417  ! may not be reliable; lam must be reduced, but not by more
418  ! than half.
419  lam = 0.5 * lam
420  dlam = -lam
421  else ! Newton's method is OK.
422  dlam = - det / ddet
423  lam = lam + dlam
424  endif
425 
426  if (calc_modal_structure) then
427  call tdma6(kc, -igu, igu+igl, -igl, lam, mode_struct)
428  ms_min = mode_struct(1)
429  ms_max = mode_struct(1)
430  ms_sq = mode_struct(1)**2
431  do k = 2,kc
432  ms_min = min(ms_min, mode_struct(k))
433  ms_max = max(ms_max, mode_struct(k))
434  ms_sq = ms_sq + mode_struct(k)**2
435  enddo
436  if (ms_min<0. .and. ms_max>0.) then ! Any zero crossings => lam is too high
437  lam = 0.5 * ( lam - dlam )
438  dlam = -lam
439  mode_struct(1:kc) = abs(mode_struct(1:kc)) / sqrt( ms_sq )
440  else
441  mode_struct(1:kc) = mode_struct(1:kc) / sqrt( ms_sq )
442  endif
443  endif
444 
445  if (abs(dlam) < tol2*lam) exit
446  enddo
447 
448  cg1(i,j) = 0.0
449  if (lam > 0.0) cg1(i,j) = 1.0 / sqrt(lam)
450 
451  if (present(modal_structure)) then
452  if (mode_struct(1)/=0.) then ! Normalize
453  mode_struct(1:kc) = mode_struct(1:kc) / mode_struct(1)
454  else
455  mode_struct(1:kc)=0.
456  endif
457  ! Note that remapping_core_h requires that the same units be used
458  ! for both the source and target grid thicknesses, here [H ~> m or kg m-2].
459  call remapping_core_h(cs%remapping_CS, kc, gv%Z_to_H*hc(:), mode_struct, &
460  nz, h(i,j,:), modal_structure(i,j,:), 1.0e-30*gv%m_to_H, 1.0e-10*gv%m_to_H)
461  endif
462  else
463  cg1(i,j) = 0.0
464  if (present(modal_structure)) modal_structure(i,j,:) = 0.
465  endif
466  endif ! cg1 /= 0.0
467  else
468  cg1(i,j) = 0.0 ! This is a land point.
469  if (present(modal_structure)) modal_structure(i,j,:) = 0.
470  endif ; enddo ! i-loop
471  enddo ! j-loop
472 
473 end subroutine wave_speed
474 
475 !> Solve a non-symmetric tridiagonal problem with a scalar contribution to the leading diagonal.
476 !! This uses the Thomas algorithm rather than the Hallberg algorithm since the matrix is not symmetric.
477 subroutine tdma6(n, a, b, c, lam, y)
478  integer, intent(in) :: n !< Number of rows of matrix
479  real, dimension(n), intent(in) :: a !< Lower diagonal
480  real, dimension(n), intent(in) :: b !< Leading diagonal
481  real, dimension(n), intent(in) :: c !< Upper diagonal
482  real, intent(in) :: lam !< Scalar subtracted from leading diagonal
483  real, dimension(n), intent(inout) :: y !< RHS on entry, result on exit
484  ! Local variables
485  integer :: k, l
486  real :: beta(n), yy(n), lambda
487  lambda = lam
488  beta(1) = b(1) - lambda
489  if (beta(1)==0.) then ! lam was chosen too perfectly
490  ! Change lambda and redo this first row
491  lambda = (1. + 1.e-5) * lambda
492  beta(1) = b(1) - lambda
493  endif
494  beta(1) = 1. / beta(1)
495  yy(1) = y(1)
496  do k = 2, n
497  beta(k) = ( b(k) - lambda ) - a(k) * c(k-1) * beta(k-1)
498  if (beta(k)==0.) then ! lam was chosen too perfectly
499  ! Change lambda and redo everything up to row k
500  lambda = (1. + 1.e-5) * lambda
501  beta(1) = 1. / ( b(1) - lambda )
502  do l = 2, k
503  beta(l) = 1. / ( ( b(l) - lambda ) - a(l) * c(l-1) * beta(l-1) )
504  yy(l) = y(l) - a(l) * yy(l-1) * beta(l-1)
505  enddo
506  else
507  beta(k) = 1. / beta(k)
508  endif
509  yy(k) = y(k) - a(k) * yy(k-1) * beta(k-1)
510  enddo
511  y(n) = yy(n) * beta(n)
512  do k = n-1, 1, -1
513  y(k) = ( yy(k) - c(k) * y(k+1) ) * beta(k)
514  enddo
515 end subroutine tdma6
516 
517 !> Calculates the wave speeds for the first few barolinic modes.
518 subroutine wave_speeds(h, tv, G, GV, US, nmodes, cn, CS, full_halos)
519  type(ocean_grid_type), intent(in) :: g !< Ocean grid structure
520  type(verticalgrid_type), intent(in) :: gv !< Vertical grid structure
521  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
522  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thickness [H ~> m or kg m-2]
523  type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamic variables
524  integer, intent(in) :: nmodes !< Number of modes
525  real, dimension(G%isd:G%ied,G%jsd:G%jed,nmodes), intent(out) :: cn !< Waves speeds [m s-1]
526  type(wave_speed_cs), optional, pointer :: cs !< Control structure for MOM_wave_speed
527  logical, optional, intent(in) :: full_halos !< If true, do the calculation
528  !! over the entire computational domain.
529  ! Local variables
530  real, dimension(SZK_(G)+1) :: &
531  drho_dt, drho_ds, &
532  pres, t_int, s_int, &
533  gprime ! The reduced gravity across each interface [m s-2]
534  real, dimension(SZK_(G)) :: &
535  igl, igu ! The inverse of the reduced gravity across an interface times
536  ! the thickness of the layer below (Igl) or above (Igu) it [s2 m-2].
537  real, dimension(SZK_(G)-1) :: &
538  a_diag, b_diag, c_diag
539  ! diagonals of tridiagonal matrix; one value for each
540  ! interface (excluding surface and bottom)
541  real, dimension(SZK_(G),SZI_(G)) :: &
542  hf, tf, sf, rf
543  real, dimension(SZK_(G)) :: &
544  hc, tc, sc, rc
545  real, parameter :: c1_thresh = 0.01
546  ! if c1 is below this value, don't bother calculating
547  ! cn values for higher modes
548  real :: det, ddet ! determinant & its derivative of eigen system
549  real :: lam_1 ! approximate mode-1 eigen value
550  real :: lam_n ! approximate mode-n eigen value
551  real :: dlam ! increment in lam for Newton's method
552  real :: lammin ! minimum lam value for root searching range
553  real :: lammax ! maximum lam value for root searching range
554  real :: laminc ! width of moving window for root searching
555  real :: det_l,det_r ! determinant value at left and right of window
556  real :: ddet_l,ddet_r ! derivative of determinant at left and right of window
557  real :: det_sub,ddet_sub! derivative of determinant at subinterval endpoint
558  real :: xl,xr ! lam guesses at left and right of window
559  real :: xl_sub ! lam guess at left of subinterval window
560  real,dimension(nmodes) :: &
561  xbl,xbr ! lam guesses bracketing a zero-crossing (root)
562  integer :: numint ! number of widows (intervals) in root searching range
563  integer :: nrootsfound ! number of extra roots found (not including 1st root)
564  real :: min_h_frac
565  real :: z_to_pa ! A conversion factor from thicknesses (in Z) to pressure (in Pa)
566  real, dimension(SZI_(G)) :: &
567  htot, hmin, & ! Thicknesses [Z ~> m].
568  h_here, hxt_here, hxs_here, hxr_here
569  real :: speed2_tot ! overestimate of the mode-1 speed squared [m2 s-2]
570  real :: speed2_min ! minimum mode speed (squared) to consider in root searching
571  real, parameter :: reduct_factor = 0.5
572  ! factor used in setting speed2_min
573  real :: i_hnew, drxh_sum
574  real, parameter :: tol1 = 0.0001, tol2 = 0.001
575  real, pointer, dimension(:,:,:) :: t => null(), s => null()
576  real :: g_rho0 ! G_Earth/Rho0 [m5 Z-1 s-2 kg-1 ~> m4 s-2 kg-1].
577  integer :: kf(szi_(g))
578  integer, parameter :: max_itt = 10
579  logical :: use_eos ! If true, density is calculated from T & S using the equation of state.
580  real, dimension(SZK_(G)+1) :: z_int, n2
581  integer :: nsub ! number of subintervals used for root finding
582  integer, parameter :: sub_it_max = 4
583  ! maximum number of times to subdivide interval
584  ! for root finding (# intervals = 2**sub_it_max)
585  logical :: sub_rootfound ! if true, subdivision has located root
586  integer :: kc, nrows
587  integer :: sub, sub_it
588  integer :: i, j, k, k2, itt, is, ie, js, je, nz, row, iint, m, ig, jg
589 
590  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
591 
592  if (present(cs)) then
593  if (.not. associated(cs)) call mom_error(fatal, "MOM_wave_speed: "// &
594  "Module must be initialized before it is used.")
595  endif
596 
597  if (present(full_halos)) then ; if (full_halos) then
598  is = g%isd ; ie = g%ied ; js = g%jsd ; je = g%jed
599  endif ; endif
600 
601  s => tv%S ; t => tv%T
602  g_rho0 = us%L_T_to_m_s**2 * gv%g_Earth / gv%Rho0
603  use_eos = associated(tv%eqn_of_state)
604  z_to_pa = gv%Z_to_H * gv%H_to_Pa
605 
606  min_h_frac = tol1 / real(nz)
607  !$OMP parallel do default(private) shared(is,ie,js,je,nz,h,G,GV,US,min_h_frac,use_EOS,T,S, &
608  !$OMP Z_to_Pa,tv,cn,g_Rho0,nmodes)
609  do j=js,je
610  ! First merge very thin layers with the one above (or below if they are
611  ! at the top). This also transposes the row order so that columns can
612  ! be worked upon one at a time.
613  do i=is,ie ; htot(i) = 0.0 ; enddo
614  do k=1,nz ; do i=is,ie ; htot(i) = htot(i) + h(i,j,k)*gv%H_to_Z ; enddo ; enddo
615 
616  do i=is,ie
617  hmin(i) = htot(i)*min_h_frac ; kf(i) = 1 ; h_here(i) = 0.0
618  hxt_here(i) = 0.0 ; hxs_here(i) = 0.0 ; hxr_here(i) = 0.0
619  enddo
620  if (use_eos) then
621  do k=1,nz ; do i=is,ie
622  if ((h_here(i) > hmin(i)) .and. (h(i,j,k)*gv%H_to_Z > hmin(i))) then
623  hf(kf(i),i) = h_here(i)
624  tf(kf(i),i) = hxt_here(i) / h_here(i)
625  sf(kf(i),i) = hxs_here(i) / h_here(i)
626  kf(i) = kf(i) + 1
627 
628  ! Start a new layer
629  h_here(i) = h(i,j,k)*gv%H_to_Z
630  hxt_here(i) = (h(i,j,k)*gv%H_to_Z)*t(i,j,k)
631  hxs_here(i) = (h(i,j,k)*gv%H_to_Z)*s(i,j,k)
632  else
633  h_here(i) = h_here(i) + h(i,j,k)*gv%H_to_Z
634  hxt_here(i) = hxt_here(i) + (h(i,j,k)*gv%H_to_Z)*t(i,j,k)
635  hxs_here(i) = hxs_here(i) + (h(i,j,k)*gv%H_to_Z)*s(i,j,k)
636  endif
637  enddo ; enddo
638  do i=is,ie ; if (h_here(i) > 0.0) then
639  hf(kf(i),i) = h_here(i)
640  tf(kf(i),i) = hxt_here(i) / h_here(i)
641  sf(kf(i),i) = hxs_here(i) / h_here(i)
642  endif ; enddo
643  else
644  do k=1,nz ; do i=is,ie
645  if ((h_here(i) > hmin(i)) .and. (h(i,j,k)*gv%H_to_Z > hmin(i))) then
646  hf(kf(i),i) = h_here(i) ; rf(kf(i),i) = hxr_here(i) / h_here(i)
647  kf(i) = kf(i) + 1
648 
649  ! Start a new layer
650  h_here(i) = h(i,j,k)*gv%H_to_Z
651  hxr_here(i) = (h(i,j,k)*gv%H_to_Z)*gv%Rlay(k)
652  else
653  h_here(i) = h_here(i) + h(i,j,k)*gv%H_to_Z
654  hxr_here(i) = hxr_here(i) + (h(i,j,k)*gv%H_to_Z)*gv%Rlay(k)
655  endif
656  enddo ; enddo
657  do i=is,ie ; if (h_here(i) > 0.0) then
658  hf(kf(i),i) = h_here(i) ; rf(kf(i),i) = hxr_here(i) / h_here(i)
659  endif ; enddo
660  endif
661 
662  ! From this point, we can work on individual columns without causing memory
663  ! to have page faults.
664  do i=is,ie
665  if (g%mask2dT(i,j) > 0.5) then
666  if (use_eos) then
667  pres(1) = 0.0
668  do k=2,kf(i)
669  pres(k) = pres(k-1) + z_to_pa*hf(k-1,i)
670  t_int(k) = 0.5*(tf(k,i)+tf(k-1,i))
671  s_int(k) = 0.5*(sf(k,i)+sf(k-1,i))
672  enddo
673  call calculate_density_derivs(t_int, s_int, pres, drho_dt, drho_ds, 2, &
674  kf(i)-1, tv%eqn_of_state)
675 
676  ! Sum the reduced gravities to find out how small a density difference
677  ! is negligibly small.
678  drxh_sum = 0.0
679  do k=2,kf(i)
680  drxh_sum = drxh_sum + 0.5*(hf(k-1,i)+hf(k,i)) * &
681  max(0.0,drho_dt(k)*(tf(k,i)-tf(k-1,i)) + &
682  drho_ds(k)*(sf(k,i)-sf(k-1,i)))
683  enddo
684  else
685  drxh_sum = 0.0
686  do k=2,kf(i)
687  drxh_sum = drxh_sum + 0.5*(hf(k-1,i)+hf(k,i)) * &
688  max(0.0,rf(k,i)-rf(k-1,i))
689  enddo
690  endif
691  ! Find gprime across each internal interface, taking care of convective
692  ! instabilities by merging layers.
693  if (drxh_sum <= 0.0) then
694  cn(i,j,:) = 0.0
695  else
696  ! Merge layers to eliminate convective instabilities or exceedingly
697  ! small reduced gravities.
698  if (use_eos) then
699  kc = 1
700  hc(1) = hf(1,i) ; tc(1) = tf(1,i) ; sc(1) = sf(1,i)
701  do k=2,kf(i)
702  if ((drho_dt(k)*(tf(k,i)-tc(kc)) + drho_ds(k)*(sf(k,i)-sc(kc))) * &
703  (hc(kc) + hf(k,i)) < 2.0 * tol2*drxh_sum) then
704  ! Merge this layer with the one above and backtrack.
705  i_hnew = 1.0 / (hc(kc) + hf(k,i))
706  tc(kc) = (hc(kc)*tc(kc) + hf(k,i)*tf(k,i)) * i_hnew
707  sc(kc) = (hc(kc)*sc(kc) + hf(k,i)*sf(k,i)) * i_hnew
708  hc(kc) = (hc(kc) + hf(k,i))
709  ! Backtrack to remove any convective instabilities above... Note
710  ! that the tolerance is a factor of two larger, to avoid limit how
711  ! far back we go.
712  do k2=kc,2,-1
713  if ((drho_dt(k2)*(tc(k2)-tc(k2-1)) + drho_ds(k2)*(sc(k2)-sc(k2-1))) * &
714  (hc(k2) + hc(k2-1)) < tol2*drxh_sum) then
715  ! Merge the two bottommost layers. At this point kc = k2.
716  i_hnew = 1.0 / (hc(kc) + hc(kc-1))
717  tc(kc-1) = (hc(kc)*tc(kc) + hc(kc-1)*tc(kc-1)) * i_hnew
718  sc(kc-1) = (hc(kc)*sc(kc) + hc(kc-1)*sc(kc-1)) * i_hnew
719  hc(kc-1) = (hc(kc) + hc(kc-1))
720  kc = kc - 1
721  else ; exit ; endif
722  enddo
723  else
724  ! Add a new layer to the column.
725  kc = kc + 1
726  drho_ds(kc) = drho_ds(k) ; drho_dt(kc) = drho_dt(k)
727  tc(kc) = tf(k,i) ; sc(kc) = sf(k,i) ; hc(kc) = hf(k,i)
728  endif
729  enddo
730  ! At this point there are kc layers and the gprimes should be positive.
731  do k=2,kc ! Revisit this if non-Boussinesq.
732  gprime(k) = g_rho0 * (drho_dt(k)*(tc(k)-tc(k-1)) + &
733  drho_ds(k)*(sc(k)-sc(k-1)))
734  enddo
735  else ! .not.use_EOS
736  ! Do the same with density directly...
737  kc = 1
738  hc(1) = hf(1,i) ; rc(1) = rf(1,i)
739  do k=2,kf(i)
740  if ((rf(k,i) - rc(kc)) * (hc(kc) + hf(k,i)) < 2.0*tol2*drxh_sum) then
741  ! Merge this layer with the one above and backtrack.
742  rc(kc) = (hc(kc)*rc(kc) + hf(k,i)*rf(k,i)) / (hc(kc) + hf(k,i))
743  hc(kc) = (hc(kc) + hf(k,i))
744  ! Backtrack to remove any convective instabilities above... Note
745  ! that the tolerance is a factor of two larger, to avoid limit how
746  ! far back we go.
747  do k2=kc,2,-1
748  if ((rc(k2)-rc(k2-1)) * (hc(k2)+hc(k2-1)) < tol2*drxh_sum) then
749  ! Merge the two bottommost layers. At this point kc = k2.
750  rc(kc-1) = (hc(kc)*rc(kc) + hc(kc-1)*rc(kc-1)) / (hc(kc) + hc(kc-1))
751  hc(kc-1) = (hc(kc) + hc(kc-1))
752  kc = kc - 1
753  else ; exit ; endif
754  enddo
755  else
756  ! Add a new layer to the column.
757  kc = kc + 1
758  rc(kc) = rf(k,i) ; hc(kc) = hf(k,i)
759  endif
760  enddo
761  ! At this point there are kc layers and the gprimes should be positive.
762  do k=2,kc ! Revisit this if non-Boussinesq.
763  gprime(k) = g_rho0 * (rc(k)-rc(k-1))
764  enddo
765  endif ! use_EOS
766 
767  !-----------------NOW FIND WAVE SPEEDS---------------------------------------
768  ig = i + g%idg_offset ; jg = j + g%jdg_offset
769  ! Sum the contributions from all of the interfaces to give an over-estimate
770  ! of the first-mode wave speed.
771  if (kc >= 2) then
772  ! Set depth at surface
773  z_int(1) = 0.0
774  ! initialize speed2_tot
775  speed2_tot = 0.0
776  ! Calculate Igu, Igl, depth, and N2 at each interior interface
777  ! [excludes surface (K=1) and bottom (K=kc+1)]
778  do k=2,kc
779  igl(k) = 1.0/(gprime(k)*hc(k)) ; igu(k) = 1.0/(gprime(k)*hc(k-1))
780  z_int(k) = z_int(k-1) + hc(k-1)
781  n2(k) = us%m_to_Z**2*gprime(k)/(0.5*(hc(k)+hc(k-1)))
782  speed2_tot = speed2_tot + gprime(k)*(hc(k-1)+hc(k))
783  enddo
784  ! Set stratification for surface and bottom (setting equal to nearest interface for now)
785  n2(1) = n2(2) ; n2(kc+1) = n2(kc)
786  ! Calcualte depth at bottom
787  z_int(kc+1) = z_int(kc)+hc(kc)
788  ! check that thicknesses sum to total depth
789  if (abs(z_int(kc+1)-htot(i)) > 1.e-12*htot(i)) then
790  call mom_error(fatal, "wave_structure: mismatch in total depths")
791  endif
792 
793  ! Define the diagonals of the tridiagonal matrix
794  ! First, populate interior rows
795  do k=3,kc-1
796  row = k-1 ! indexing for TD matrix rows
797  a_diag(row) = (-igu(k))
798  b_diag(row) = (igu(k)+igl(k))
799  c_diag(row) = (-igl(k))
800  enddo
801  ! Populate top row of tridiagonal matrix
802  k=2 ; row = k-1
803  a_diag(row) = 0.0
804  b_diag(row) = (igu(k)+igl(k))
805  c_diag(row) = (-igl(k))
806  ! Populate bottom row of tridiagonal matrix
807  k=kc ; row = k-1
808  a_diag(row) = (-igu(k))
809  b_diag(row) = (igu(k)+igl(k))
810  c_diag(row) = 0.0
811  ! Total number of rows in the matrix = number of interior interfaces
812  nrows = kc-1
813 
814  ! Under estimate the first eigen value to start with.
815  lam_1 = 1.0 / speed2_tot
816 
817  ! Find the first eigen value
818  do itt=1,max_itt
819  ! calculate the determinant of (A-lam_1*I)
820  call tridiag_det(a_diag(1:nrows),b_diag(1:nrows),c_diag(1:nrows), &
821  nrows,lam_1,det,ddet)
822  ! Use Newton's method iteration to find a new estimate of lam_1
823  !det = det_it(itt) ; ddet = ddet_it(itt)
824  if ((ddet >= 0.0) .or. (-det > -0.5*lam_1*ddet)) then
825  ! lam_1 was not an under-estimate, as intended, so Newton's method
826  ! may not be reliable; lam_1 must be reduced, but not by more
827  ! than half.
828  lam_1 = 0.5 * lam_1
829  else ! Newton's method is OK.
830  dlam = - det / ddet
831  lam_1 = lam_1 + dlam
832  if (abs(dlam) < tol2*lam_1) then
833  ! calculate 1st mode speed
834  if (lam_1 > 0.0) cn(i,j,1) = 1.0 / sqrt(lam_1)
835  exit
836  endif
837  endif
838  enddo
839 
840  ! Find other eigen values if c1 is of significant magnitude, > cn_thresh
841  nrootsfound = 0 ! number of extra roots found (not including 1st root)
842  if (nmodes>1 .and. kc>=nmodes+1 .and. cn(i,j,1)>c1_thresh) then
843  ! Set the the range to look for the other desired eigen values
844  ! set min value just greater than the 1st root (found above)
845  lammin = lam_1*(1.0 + tol2)
846  ! set max value based on a low guess at wavespeed for highest mode
847  speed2_min = (reduct_factor*cn(i,j,1)/real(nmodes))**2
848  lammax = 1.0 / speed2_min
849  ! set width of interval (not sure about this - BDM)
850  laminc = 0.5*lam_1
851  ! set number of intervals within search range
852  numint = nint((lammax - lammin)/laminc)
853 
854  ! Find intervals containing zero-crossings (roots) of the determinant
855  ! that are beyond the first root
856 
857  ! find det_l of first interval (det at left endpoint)
858  call tridiag_det(a_diag(1:nrows),b_diag(1:nrows),c_diag(1:nrows), &
859  nrows,lammin,det_l,ddet_l)
860  ! move interval window looking for zero-crossings************************
861  do iint=1,numint
862  xr = lammin + laminc * iint
863  xl = xr - laminc
864  call tridiag_det(a_diag(1:nrows),b_diag(1:nrows),c_diag(1:nrows), &
865  nrows,xr,det_r,ddet_r)
866  if (det_l*det_r < 0.0) then ! if function changes sign
867  if (det_l*ddet_l < 0.0) then ! if function at left is headed to zero
868  nrootsfound = nrootsfound + 1
869  xbl(nrootsfound) = xl
870  xbr(nrootsfound) = xr
871  else
872  ! function changes sign but has a local max/min in interval,
873  ! try subdividing interval as many times as necessary (or sub_it_max).
874  ! loop that increases number of subintervals:
875  !call MOM_error(WARNING, "determinant changes sign"// &
876  ! "but has a local max/min in interval;"//&
877  ! " reduce increment in lam.")
878  ! begin subdivision loop -------------------------------------------
879  sub_rootfound = .false. ! initialize
880  do sub_it=1,sub_it_max
881  nsub = 2**sub_it ! number of subintervals; nsub=2,4,8,...
882  ! loop over each subinterval:
883  do sub=1,nsub-1,2 ! only check odds; sub = 1; 1,3; 1,3,5,7;...
884  xl_sub = xl + laminc/(nsub)*sub
885  call tridiag_det(a_diag(1:nrows),b_diag(1:nrows),c_diag(1:nrows), &
886  nrows,xl_sub,det_sub,ddet_sub)
887  if (det_sub*det_r < 0.0) then ! if function changes sign
888  if (det_sub*ddet_sub < 0.0) then ! if function at left is headed to zero
889  sub_rootfound = .true.
890  nrootsfound = nrootsfound + 1
891  xbl(nrootsfound) = xl_sub
892  xbr(nrootsfound) = xr
893  exit ! exit sub loop
894  endif ! headed toward zero
895  endif ! sign change
896  enddo ! sub-loop
897  if (sub_rootfound) exit ! root has been found, exit sub_it loop
898  ! Otherwise, function changes sign but has a local max/min in one of the
899  ! sub intervals, try subdividing again unless sub_it_max has been reached.
900  if (sub_it == sub_it_max) then
901  call mom_error(warning, "wave_speed: root not found "// &
902  " after sub_it_max subdivisions of original"// &
903  " interval.")
904  endif ! sub_it == sub_it_max
905  enddo ! sub_it-loop-------------------------------------------------
906  endif ! det_l*ddet_l < 0.0
907  endif ! det_l*det_r < 0.0
908  ! exit iint-loop if all desired roots have been found
909  if (nrootsfound >= nmodes-1) then
910  ! exit if all additional roots found
911  exit
912  elseif (iint == numint) then
913  ! oops, lamMax not large enough - could add code to increase (BDM)
914  ! set unfound modes to zero for now (BDM)
915  cn(i,j,nrootsfound+2:nmodes) = 0.0
916  else
917  ! else shift interval and keep looking until nmodes or numint is reached
918  det_l = det_r
919  ddet_l = ddet_r
920  endif
921  enddo ! iint-loop
922 
923  ! Use Newton's method to find the roots within the identified windows
924  do m=1,nrootsfound ! loop over the root-containing widows (excluding 1st mode)
925  lam_n = xbl(m) ! first guess is left edge of window
926  do itt=1,max_itt
927  ! calculate the determinant of (A-lam_n*I)
928  call tridiag_det(a_diag(1:nrows),b_diag(1:nrows),c_diag(1:nrows), &
929  nrows,lam_n,det,ddet)
930  ! Use Newton's method to find a new estimate of lam_n
931  dlam = - det / ddet
932  lam_n = lam_n + dlam
933  if (abs(dlam) < tol2*lam_1) then
934  ! calculate nth mode speed
935  if (lam_n > 0.0) cn(i,j,m+1) = 1.0 / sqrt(lam_n)
936  exit
937  endif ! within tol
938  enddo ! itt-loop
939  enddo ! n-loop
940  else
941  cn(i,j,2:nmodes) = 0.0 ! else too small to worry about
942  endif ! if nmodes>1 .and. kc>nmodes .and. c1>c1_thresh
943  else
944  cn(i,j,:) = 0.0
945  endif ! if more than 2 layers
946  endif ! if drxh_sum < 0
947  else
948  cn(i,j,:) = 0.0 ! This is a land point.
949  endif ! if not land
950  enddo ! i-loop
951  enddo ! j-loop
952 
953 end subroutine wave_speeds
954 
955 !> Calculate the determinant of a tridiagonal matrix with diagonals a,b-lam,c where lam is constant across rows.
956 subroutine tridiag_det(a,b,c,nrows,lam,det_out,ddet_out)
957  real, dimension(:), intent(in) :: a !< Lower diagonal of matrix (first entry = 0)
958  real, dimension(:), intent(in) :: b !< Leading diagonal of matrix (excluding lam)
959  real, dimension(:), intent(in) :: c !< Upper diagonal of matrix (last entry = 0)
960  integer, intent(in) :: nrows !< Size of matrix
961  real, intent(in) :: lam !< Value subtracted from b
962  real, intent(out):: det_out !< Determinant
963  real, intent(out):: ddet_out !< Derivative of determinant w.r.t. lam
964  ! Local variables
965  real, dimension(nrows) :: det ! value of recursion function
966  real, dimension(nrows) :: ddet ! value of recursion function for derivative
967  real, parameter:: rescale = 1024.0**4 ! max value of determinant allowed before rescaling
968  real :: I_rescale ! inverse of rescale
969  integer :: n ! row (layer interface) index
970 
971  if (size(b) /= nrows) call mom_error(warning, "Diagonal b must be same length as nrows.")
972  if (size(a) /= nrows) call mom_error(warning, "Diagonal a must be same length as nrows.")
973  if (size(c) /= nrows) call mom_error(warning, "Diagonal c must be same length as nrows.")
974 
975  i_rescale = 1.0/rescale
976 
977  det(1) = 1.0 ; ddet(1) = 0.0
978  det(2) = b(2)-lam ; ddet(2) = -1.0
979  do n=3,nrows
980  det(n) = (b(n)-lam)*det(n-1) - (a(n)*c(n-1))*det(n-2)
981  ddet(n) = (b(n)-lam)*ddet(n-1) - (a(n)*c(n-1))*ddet(n-2) - det(n-1)
982  ! Rescale det & ddet if det is getting too large.
983  if (abs(det(n)) > rescale) then
984  det(n) = i_rescale*det(n) ; det(n-1) = i_rescale*det(n-1)
985  ddet(n) = i_rescale*ddet(n) ; ddet(n-1) = i_rescale*ddet(n-1)
986  endif
987  enddo
988  det_out = det(nrows)
989  ddet_out = ddet(nrows)
990 
991 end subroutine tridiag_det
992 
993 !> Initialize control structure for MOM_wave_speed
994 subroutine wave_speed_init(CS, use_ebt_mode, mono_N2_column_fraction, mono_N2_depth)
995  type(wave_speed_cs), pointer :: cs !< Control structure for MOM_wave_speed
996  logical, optional, intent(in) :: use_ebt_mode !< If true, use the equivalent
997  !! barotropic mode instead of the first baroclinic mode.
998  real, optional, intent(in) :: mono_n2_column_fraction !< The lower fraction of water column over
999  !! which N2 is limited as monotonic for the purposes of
1000  !! calculating the vertical modal structure.
1001  real, optional, intent(in) :: mono_n2_depth !< The depth below which N2 is limited
1002  !! as monotonic for the purposes of calculating the
1003  !! vertical modal structure.
1004 ! This include declares and sets the variable "version".
1005 #include "version_variable.h"
1006  character(len=40) :: mdl = "MOM_wave_speed" ! This module's name.
1007 
1008  if (associated(cs)) then
1009  call mom_error(warning, "wave_speed_init called with an "// &
1010  "associated control structure.")
1011  return
1012  else ; allocate(cs) ; endif
1013 
1014  ! Write all relevant parameters to the model log.
1015  call log_version(mdl, version)
1016 
1017  call wave_speed_set_param(cs, use_ebt_mode=use_ebt_mode, mono_n2_column_fraction=mono_n2_column_fraction)
1018 
1019  call initialize_remapping(cs%remapping_CS, 'PLM', boundary_extrapolation=.false.)
1020 
1021 end subroutine wave_speed_init
1022 
1023 !> Sets internal parameters for MOM_wave_speed
1024 subroutine wave_speed_set_param(CS, use_ebt_mode, mono_N2_column_fraction, mono_N2_depth)
1025  type(wave_speed_cs), pointer :: cs !< Control structure for MOM_wave_speed
1026  logical, optional, intent(in) :: use_ebt_mode !< If true, use the equivalent
1027  !! barotropic mode instead of the first baroclinic mode.
1028  real, optional, intent(in) :: mono_n2_column_fraction !< The lower fraction of water column over
1029  !! which N2 is limited as monotonic for the purposes of
1030  !! calculating the vertical modal structure.
1031  real, optional, intent(in) :: mono_n2_depth !< The depth below which N2 is limited
1032  !! as monotonic for the purposes of calculating the
1033  !! vertical modal structure.
1034 
1035  if (.not.associated(cs)) call mom_error(fatal, &
1036  "wave_speed_set_param called with an associated control structure.")
1037 
1038  if (present(use_ebt_mode)) cs%use_ebt_mode = use_ebt_mode
1039  if (present(mono_n2_column_fraction)) cs%mono_N2_column_fraction = mono_n2_column_fraction
1040  if (present(mono_n2_depth)) cs%mono_N2_depth = mono_n2_depth
1041 
1042 end subroutine wave_speed_set_param
1043 
1044 !> \namespace mom_wave_speed
1045 !!
1046 !! Subroutine wave_speed() solves for the first baroclinic mode wave speed. (It could
1047 !! solve for all the wave speeds, but the iterative approach taken here means
1048 !! that this is not particularly efficient.)
1049 !!
1050 !! If `e(k)` is the perturbation interface height, this means solving for the
1051 !! smallest eigenvalue (`lam` = 1/c^2) of the system
1052 !!
1053 !! \verbatim
1054 !! -Igu(k)*e(k-1) + (Igu(k)+Igl(k)-lam)*e(k) - Igl(k)*e(k+1) = 0.0
1055 !! \endverbatim
1056 !!
1057 !! with rigid lid boundary conditions e(1) = e(nz+1) = 0.0 giving
1058 !!
1059 !! \verbatim
1060 !! (Igu(2)+Igl(2)-lam)*e(2) - Igl(2)*e(3) = 0.0
1061 !! -Igu(nz)*e(nz-1) + (Igu(nz)+Igl(nz)-lam)*e(nz) = 0.0
1062 !! \endverbatim
1063 !!
1064 !! Here
1065 !! \verbatim
1066 !! Igl(k) = 1.0/(gprime(k)*h(k)) ; Igu(k) = 1.0/(gprime(k)*h(k-1))
1067 !! \endverbatim
1068 !!
1069 !! Alternately, these same eigenvalues can be found from the second smallest
1070 !! eigenvalue of the Montgomery potential (M(k)) calculation:
1071 !!
1072 !! \verbatim
1073 !! -Igl(k)*M(k-1) + (Igl(k)+Igu(k+1)-lam)*M(k) - Igu(k+1)*M(k+1) = 0.0
1074 !! \endverbatim
1075 !!
1076 !! with rigid lid and flat bottom boundary conditions
1077 !!
1078 !! \verbatim
1079 !! (Igu(2)-lam)*M(1) - Igu(2)*M(2) = 0.0
1080 !! -Igl(nz)*M(nz-1) + (Igl(nz)-lam)*M(nz) = 0.0
1081 !! \endverbatim
1082 !!
1083 !! Note that the barotropic mode has been eliminated from the rigid lid
1084 !! interface height equations, hence the matrix is one row smaller. Without
1085 !! the rigid lid, the top boundary condition is simpler to implement with
1086 !! the M equations.
1087 
1088 end module mom_wave_speed
mom_verticalgrid
Provides a transparent vertical ocean grid type and supporting routines.
Definition: MOM_verticalGrid.F90:2
mom_file_parser::log_version
An overloaded interface to log version information about modules.
Definition: MOM_file_parser.F90:109
mom_diag_mediator
The subroutines here provide convenient wrappers to the fms diag_manager interfaces with additional d...
Definition: MOM_diag_mediator.F90:3
mom_variables::thermo_var_ptrs
Pointers to an assortment of thermodynamic fields that may be available, including potential temperat...
Definition: MOM_variables.F90:82
mom_eos
Provides subroutines for quantities specific to the equation of state.
Definition: MOM_EOS.F90:2
mom_remapping::remapping_cs
Container for remapping parameters.
Definition: MOM_remapping.F90:22
mom_unit_scaling::unit_scale_type
Describes various unit conversion factors.
Definition: MOM_unit_scaling.F90:14
mom_remapping
Provides column-wise vertical remapping functions.
Definition: MOM_remapping.F90:2
mom_diag_mediator::post_data
Make a diagnostic available for averaging or output.
Definition: MOM_diag_mediator.F90:70
mom_verticalgrid::verticalgrid_type
Describes the vertical ocean grid, including unit conversion factors.
Definition: MOM_verticalGrid.F90:24
mom_variables
Provides transparent structures with groups of MOM6 variables and supporting routines.
Definition: MOM_variables.F90:2
mom_wave_speed::wave_speed_cs
Control structure for MOM_wave_speed.
Definition: MOM_wave_speed.F90:28
mom_file_parser
The MOM6 facility to parse input files for runtime parameters.
Definition: MOM_file_parser.F90:2
mom_eos::calculate_density_derivs
Calculate the derivatives of density with temperature and salinity from T, S, and P.
Definition: MOM_EOS.F90:70
mom_grid
Provides the ocean grid type.
Definition: MOM_grid.F90:2
mom_unit_scaling
Provides a transparent unit rescaling type to facilitate dimensional consistency testing.
Definition: MOM_unit_scaling.F90:2
mom_wave_speed
Routines for calculating baroclinic wave speeds.
Definition: MOM_wave_speed.F90:2
mom_error_handler
Routines for error handling and I/O management.
Definition: MOM_error_handler.F90:2
mom_grid::ocean_grid_type
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:25
mom_diag_mediator::diag_ctrl
The following data type a list of diagnostic fields an their variants, as well as variables that cont...
Definition: MOM_diag_mediator.F90:239
mom_eos::calculate_density
Calculates density of sea water from T, S and P.
Definition: MOM_EOS.F90:60