MOM6
mom_wave_structure Module Reference

Detailed Description

Vertical structure functions for first baroclinic mode wave speed.

Data Types

type  wave_structure_cs
 The control structure for the MOM_wave_structure module. More...
 

Functions/Subroutines

subroutine, public wave_structure (h, tv, G, GV, US, cn, ModeNum, freq, CS, En, full_halos)
 This subroutine determines the internal wave velocity structure for any mode. More...
 
subroutine tridiag_solver (a, b, c, h, y, method, x)
 Solves a tri-diagonal system Ax=y using either the standard Thomas algorithm (TDMA_T) or its more stable variant that invokes the "Hallberg substitution" (TDMA_H). More...
 
subroutine, public wave_structure_init (Time, G, param_file, diag, CS)
 Allocate memory associated with the wave structure module and read parameters. More...
 

Function/Subroutine Documentation

◆ tridiag_solver()

subroutine mom_wave_structure::tridiag_solver ( real, dimension(:), intent(in)  a,
real, dimension(:), intent(in)  b,
real, dimension(:), intent(in)  c,
real, dimension(:), intent(in)  h,
real, dimension(:), intent(in)  y,
character(len=*), intent(in)  method,
real, dimension(:), intent(out)  x 
)
private

Solves a tri-diagonal system Ax=y using either the standard Thomas algorithm (TDMA_T) or its more stable variant that invokes the "Hallberg substitution" (TDMA_H).

Parameters
[in]alower diagonal with first entry equal to zero.
[in]bmiddle diagonal.
[in]cupper diagonal with last entry equal to zero.
[in]hvector of values that have already been added to b; used for systems of the form (e.g. average layer thickness in vertical diffusion case): [ -alpha(k-1/2) ] * e(k-1) + [ alpha(k-1/2) + alpha(k+1/2) + h(k) ] * e(k) + [ -alpha(k+1/2) ] * e(k+1) = y(k) where a(k)=[-alpha(k-1/2)], b(k)=[alpha(k-1/2)+alpha(k+1/2) + h(k)], and c(k)=[-alpha(k+1/2)]. Only used with TDMA_H method.
[in]yvector of known values on right hand side.
[in]methodA string describing the algorithm to use
[out]xvector of unknown values to solve for.

Definition at line 543 of file MOM_wave_structure.F90.

543  real, dimension(:), intent(in) :: a !< lower diagonal with first entry equal to zero.
544  real, dimension(:), intent(in) :: b !< middle diagonal.
545  real, dimension(:), intent(in) :: c !< upper diagonal with last entry equal to zero.
546  real, dimension(:), intent(in) :: h !< vector of values that have already been added to b; used
547  !! for systems of the form (e.g. average layer thickness in vertical diffusion case):
548  !! [ -alpha(k-1/2) ] * e(k-1) +
549  !! [ alpha(k-1/2) + alpha(k+1/2) + h(k) ] * e(k) +
550  !! [ -alpha(k+1/2) ] * e(k+1) = y(k)
551  !! where a(k)=[-alpha(k-1/2)], b(k)=[alpha(k-1/2)+alpha(k+1/2) + h(k)],
552  !! and c(k)=[-alpha(k+1/2)]. Only used with TDMA_H method.
553  real, dimension(:), intent(in) :: y !< vector of known values on right hand side.
554  character(len=*), intent(in) :: method !< A string describing the algorithm to use
555  real, dimension(:), intent(out) :: x !< vector of unknown values to solve for.
556  ! Local variables
557  integer :: nrow ! number of rows in A matrix
558 ! real, allocatable, dimension(:,:) :: A_check ! for solution checking
559 ! real, allocatable, dimension(:) :: y_check ! for solution checking
560  real, allocatable, dimension(:) :: c_prime, y_prime, q, alpha
561  ! intermediate values for solvers
562  real :: Q_prime, beta ! intermediate values for solver
563  integer :: k ! row (e.g. interface) index
564  integer :: i,j
565 
566  nrow = size(y)
567  allocate(c_prime(nrow))
568  allocate(y_prime(nrow))
569  allocate(q(nrow))
570  allocate(alpha(nrow))
571 ! allocate(A_check(nrow,nrow))
572 ! allocate(y_check(nrow))
573 
574  if (method == 'TDMA_T') then
575  ! Standard Thomas algoritim (4th variant).
576  ! Note: Requires A to be non-singular for accuracy/stability
577  c_prime(:) = 0.0 ; y_prime(:) = 0.0
578  c_prime(1) = c(1)/b(1) ; y_prime(1) = y(1)/b(1)
579 
580  ! Forward sweep
581  do k=2,nrow-1
582  c_prime(k) = c(k)/(b(k)-a(k)*c_prime(k-1))
583  enddo
584  !print *, 'c_prime=', c_prime(1:nrow)
585  do k=2,nrow
586  y_prime(k) = (y(k)-a(k)*y_prime(k-1))/(b(k)-a(k)*c_prime(k-1))
587  enddo
588  !print *, 'y_prime=', y_prime(1:nrow)
589  x(nrow) = y_prime(nrow)
590 
591  ! Backward sweep
592  do k=nrow-1,1,-1
593  x(k) = y_prime(k)-c_prime(k)*x(k+1)
594  enddo
595  !print *, 'x=',x(1:nrow)
596 
597  ! Check results - delete later
598  !do j=1,nrow ; do i=1,nrow
599  ! if (i==j)then ; A_check(i,j) = b(i)
600  ! elseif (i==j+1)then ; A_check(i,j) = a(i)
601  ! elseif (i==j-1)then ; A_check(i,j) = c(i)
602  ! endif
603  !enddo ; enddo
604  !print *, 'A(2,1),A(2,2),A(1,2)=', A_check(2,1), A_check(2,2), A_check(1,2)
605  !y_check = matmul(A_check,x)
606  !if (all(y_check /= y))then
607  ! print *, "tridiag_solver: Uh oh, something's not right!"
608  ! print *, "y=", y
609  ! print *, "y_check=", y_check
610  !endif
611 
612  elseif (method == 'TDMA_H') then
613  ! Thomas algoritim (4th variant) w/ Hallberg substitution.
614  ! For a layered system where k is at interfaces, alpha{k+1/2} refers to
615  ! some property (e.g. inverse thickness for mode-structure problem) of the
616  ! layer below and alpha{k-1/2} refers to the layer above.
617  ! Here, alpha(k)=alpha{k+1/2} and alpha(k-1)=alpha{k-1/2}.
618  ! Strictly speaking, this formulation requires A to be a non-singular,
619  ! symmetric, diagonally dominant matrix, with h>0.
620  ! Need to add a check for these conditions.
621  do k=1,nrow-1
622  if (abs(a(k+1)-c(k)) > 1.e-10*(abs(a(k+1))+abs(c(k)))) then
623  call mom_error(warning, "tridiag_solver: matrix not symmetric; need symmetry when invoking TDMA_H")
624  endif
625  enddo
626  alpha = -c
627  ! Alpha of the bottom-most layer is not necessarily zero. Therefore,
628  ! back out the value from the provided b(nrow and h(nrow) values
629  alpha(nrow) = b(nrow)-h(nrow)-alpha(nrow-1)
630  ! Prime other variables
631  beta = 1/b(1)
632  y_prime(:) = 0.0 ; q(:) = 0.0
633  y_prime(1) = beta*y(1) ; q(1) = beta*alpha(1)
634  q_prime = 1-q(1)
635 
636  ! Forward sweep
637  do k=2,nrow-1
638  beta = 1/(h(k)+alpha(k-1)*q_prime+alpha(k))
639  if (isnan(beta))then ; print *, "Tridiag_solver: beta is NAN" ; endif
640  q(k) = beta*alpha(k)
641  y_prime(k) = beta*(y(k)+alpha(k-1)*y_prime(k-1))
642  q_prime = beta*(h(k)+alpha(k-1)*q_prime)
643  enddo
644  if ((h(nrow)+alpha(nrow-1)*q_prime+alpha(nrow)) == 0.0)then
645  call mom_error(fatal, "Tridiag_solver: this system is not stable.") ! ; overriding beta(nrow)
646  ! This has hard-coded dimensions: beta = 1/(1e-15) ! place holder for unstable systems - delete later
647  else
648  beta = 1/(h(nrow)+alpha(nrow-1)*q_prime+alpha(nrow))
649  endif
650  y_prime(nrow) = beta*(y(nrow)+alpha(nrow-1)*y_prime(nrow-1))
651  x(nrow) = y_prime(nrow)
652  ! Backward sweep
653  do k=nrow-1,1,-1
654  x(k) = y_prime(k)+q(k)*x(k+1)
655  enddo
656  !print *, 'yprime=',y_prime(1:nrow)
657  !print *, 'x=',x(1:nrow)
658  endif
659 
660  deallocate(c_prime,y_prime,q,alpha)
661 ! deallocate(A_check,y_check)
662 

◆ wave_structure()

subroutine, public mom_wave_structure::wave_structure ( real, dimension( g %isd: g %ied, g %jsd: g %jed, g %ke), intent(in)  h,
type(thermo_var_ptrs), intent(in)  tv,
type(ocean_grid_type), intent(in)  G,
type(verticalgrid_type), intent(in)  GV,
type(unit_scale_type), intent(in)  US,
real, dimension( g %isd: g %ied, g %jsd: g %jed), intent(in)  cn,
integer, intent(in)  ModeNum,
real, intent(in)  freq,
type(wave_structure_cs), pointer  CS,
real, dimension( g %isd: g %ied, g %jsd: g %jed), intent(in), optional  En,
logical, intent(in), optional  full_halos 
)

This subroutine determines the internal wave velocity structure for any mode.

This subroutine solves for the eigen vector [vertical structure, e(k)] associated with the first baroclinic mode speed [i.e., smallest eigen value (lam = 1/c^2)] of the system d2e/dz2 = -(N2/cn2)e, or (A-lam*I)e = 0, where A = -(1/N2)(d2/dz2), lam = 1/c^2, and I is the identity matrix. 2nd order discretization in the vertical lets this system be represented as

-Igu(k)*e(k-1) + (Igu(k)+Igl(k)-lam)*e(k) - Igl(k)*e(k+1) = 0.0

with rigid lid boundary conditions e(1) = e(nz+1) = 0.0 giving

(Igu(2)+Igl(2)-lam)*e(2) - Igl(2)*e(3) = 0.0 -Igu(nz)*e(nz-1) + (Igu(nz)+Igl(nz)-lam)*e(nz) = 0.0

where, upon noting N2 = reduced gravity/layer thickness, we get Igl(k) = 1.0/(gprime(k)*H(k)) ; Igu(k) = 1.0/(gprime(k)*H(k-1))

The eigen value for this system is approximated using "wave_speed." This subroutine uses these eigen values (mode speeds) to estimate the corresponding eigen vectors (velocity structure) using the "inverse iteration with shift" method. The algorithm is

Pick a starting vector reasonably close to mode structure and with unit magnitude, b_guess For n=1,2,3,... Solve (A-lam*I)e = e_guess for e Set e_guess=e/|e| and repeat, with each iteration refining the estimate of e

Parameters
[in]gThe ocean's grid structure.
[in]gvThe ocean's vertical grid structure.
[in]usA dimensional unit scaling type
[in]hLayer thicknesses [H ~> m or kg m-2]
[in]tvA structure pointing to various thermodynamic variables.
[in]cnThe (non-rotational) mode internal gravity wave speed [m s-1].
[in]modenumMode number
[in]freqIntrinsic wave frequency [s-1].
csThe control structure returned by a previous call to wave_structure_init.
[in]enInternal wave energy density [J m-2].
[in]full_halosIf true, do the calculation over the entire computational domain.

Definition at line 92 of file MOM_wave_structure.F90.

92  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
93  type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
94  type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
95  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
96  type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various
97  !! thermodynamic variables.
98  real, dimension(SZI_(G),SZJ_(G)), intent(in) :: cn !< The (non-rotational) mode internal
99  !! gravity wave speed [m s-1].
100  integer, intent(in) :: ModeNum !< Mode number
101  real, intent(in) :: freq !< Intrinsic wave frequency [s-1].
102  type(wave_structure_CS), pointer :: CS !< The control structure returned by a
103  !! previous call to wave_structure_init.
104  real, dimension(SZI_(G),SZJ_(G)), &
105  optional, intent(in) :: En !< Internal wave energy density [J m-2].
106  logical,optional, intent(in) :: full_halos !< If true, do the calculation
107  !! over the entire computational domain.
108  ! Local variables
109  real, dimension(SZK_(G)+1) :: &
110  dRho_dT, dRho_dS, &
111  pres, T_int, S_int, &
112  gprime ! The reduced gravity across each interface [m2 Z-1 s-2 ~> m s-2].
113  real, dimension(SZK_(G)) :: &
114  Igl, Igu ! The inverse of the reduced gravity across an interface times
115  ! the thickness of the layer below (Igl) or above (Igu) it [s2 m-2].
116  real, dimension(SZK_(G),SZI_(G)) :: &
117  Hf, Tf, Sf, Rf
118  real, dimension(SZK_(G)) :: &
119  Hc, Tc, Sc, Rc, &
120  det, ddet
121  real, dimension(SZI_(G),SZJ_(G)) :: &
122  htot
123  real :: lam
124  real :: min_h_frac
125  real :: H_to_pres
126  real, dimension(SZI_(G)) :: &
127  hmin, & ! Thicknesses [Z ~> m].
128  H_here, HxT_here, HxS_here, HxR_here
129  real :: speed2_tot
130  real :: I_Hnew, drxh_sum
131  real, parameter :: tol1 = 0.0001, tol2 = 0.001
132  real, pointer, dimension(:,:,:) :: T => null(), s => null()
133  real :: g_Rho0 ! G_Earth/Rho0 in m5 Z-1 s-2 kg-1.
134  real :: rescale, I_rescale
135  integer :: kf(SZI_(G))
136  integer, parameter :: max_itt = 1 ! number of times to iterate in solving for eigenvector
137  real, parameter :: cg_subRO = 1e-100 ! a very small number
138  real, parameter :: a_int = 0.5 ! value of normalized integral: \int(w_strct^2)dz = a_int
139  real :: I_a_int ! inverse of a_int
140  real :: f2 ! squared Coriolis frequency
141  real :: Kmag2 ! magnitude of horizontal wave number squared
142  logical :: use_EOS ! If true, density is calculated from T & S using an
143  ! equation of state.
144  real, dimension(SZK_(G)+1) :: w_strct, u_strct, W_profile, Uavg_profile, z_int, N2
145  ! local representations of variables in CS; note,
146  ! not all rows will be filled if layers get merged!
147  real, dimension(SZK_(G)+1) :: w_strct2, u_strct2
148  ! squared values
149  real, dimension(SZK_(G)) :: dz ! thicknesses of merged layers (same as Hc I hope)
150  real, dimension(SZK_(G)+1) :: dWdz_profile ! profile of dW/dz
151  real :: w2avg ! average of squared vertical velocity structure funtion
152  real :: int_dwdz2, int_w2, int_N2w2, KE_term, PE_term, W0
153  ! terms in vertically averaged energy equation
154  real :: gp_unscaled ! A version of gprime rescaled to [m s-2].
155  real, dimension(SZK_(G)-1) :: lam_z ! product of eigen value and gprime(k); one value for each
156  ! interface (excluding surface and bottom)
157  real, dimension(SZK_(G)-1) :: a_diag, b_diag, c_diag
158  ! diagonals of tridiagonal matrix; one value for each
159  ! interface (excluding surface and bottom)
160  real, dimension(SZK_(G)-1) :: e_guess ! guess at eigen vector with unit amplitde (for TDMA)
161  real, dimension(SZK_(G)-1) :: e_itt ! improved guess at eigen vector (from TDMA)
162  real :: Pi
163  integer :: kc
164  integer :: i, j, k, k2, itt, is, ie, js, je, nz, nzm, row, ig, jg, ig_stop, jg_stop
165 
166  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
167  i_a_int = 1/a_int
168 
169  !if (present(CS)) then
170  if (.not. associated(cs)) call mom_error(fatal, "MOM_wave_structure: "// &
171  "Module must be initialized before it is used.")
172  !endif
173 
174  if (present(full_halos)) then ; if (full_halos) then
175  is = g%isd ; ie = g%ied ; js = g%jsd ; je = g%jed
176  endif ; endif
177 
178  pi = (4.0*atan(1.0))
179 
180  s => tv%S ; t => tv%T
181  g_rho0 = us%L_T_to_m_s**2 * gv%g_Earth /gv%Rho0
182  use_eos = associated(tv%eqn_of_state)
183 
184  h_to_pres = gv%Z_to_H*gv%H_to_Pa
185  rescale = 1024.0**4 ; i_rescale = 1.0/rescale
186 
187  min_h_frac = tol1 / real(nz)
188 
189  do j=js,je
190  ! First merge very thin layers with the one above (or below if they are
191  ! at the top). This also transposes the row order so that columns can
192  ! be worked upon one at a time.
193  do i=is,ie ; htot(i,j) = 0.0 ; enddo
194  do k=1,nz ; do i=is,ie ; htot(i,j) = htot(i,j) + h(i,j,k)*gv%H_to_Z ; enddo ; enddo
195 
196  do i=is,ie
197  hmin(i) = htot(i,j)*min_h_frac ; kf(i) = 1 ; h_here(i) = 0.0
198  hxt_here(i) = 0.0 ; hxs_here(i) = 0.0 ; hxr_here(i) = 0.0
199  enddo
200  if (use_eos) then
201  do k=1,nz ; do i=is,ie
202  if ((h_here(i) > hmin(i)) .and. (h(i,j,k)*gv%H_to_Z > hmin(i))) then
203  hf(kf(i),i) = h_here(i)
204  tf(kf(i),i) = hxt_here(i) / h_here(i)
205  sf(kf(i),i) = hxs_here(i) / h_here(i)
206  kf(i) = kf(i) + 1
207 
208  ! Start a new layer
209  h_here(i) = h(i,j,k)*gv%H_to_Z
210  hxt_here(i) = (h(i,j,k)*gv%H_to_Z)*t(i,j,k)
211  hxs_here(i) = (h(i,j,k)*gv%H_to_Z)*s(i,j,k)
212  else
213  h_here(i) = h_here(i) + h(i,j,k)*gv%H_to_Z
214  hxt_here(i) = hxt_here(i) + (h(i,j,k)*gv%H_to_Z)*t(i,j,k)
215  hxs_here(i) = hxs_here(i) + (h(i,j,k)*gv%H_to_Z)*s(i,j,k)
216  endif
217  enddo ; enddo
218  do i=is,ie ; if (h_here(i) > 0.0) then
219  hf(kf(i),i) = h_here(i)
220  tf(kf(i),i) = hxt_here(i) / h_here(i)
221  sf(kf(i),i) = hxs_here(i) / h_here(i)
222  endif ; enddo
223  else
224  do k=1,nz ; do i=is,ie
225  if ((h_here(i) > hmin(i)) .and. (h(i,j,k)*gv%H_to_Z > hmin(i))) then
226  hf(kf(i),i) = h_here(i) ; rf(kf(i),i) = hxr_here(i) / h_here(i)
227  kf(i) = kf(i) + 1
228 
229  ! Start a new layer
230  h_here(i) = h(i,j,k)*gv%H_to_Z
231  hxr_here(i) = (h(i,j,k)*gv%H_to_Z)*gv%Rlay(k)
232  else
233  h_here(i) = h_here(i) + h(i,j,k)*gv%H_to_Z
234  hxr_here(i) = hxr_here(i) + (h(i,j,k)*gv%H_to_Z)*gv%Rlay(k)
235  endif
236  enddo ; enddo
237  do i=is,ie ; if (h_here(i) > 0.0) then
238  hf(kf(i),i) = h_here(i) ; rf(kf(i),i) = hxr_here(i) / h_here(i)
239  endif ; enddo
240  endif ! use_EOS?
241 
242  ! From this point, we can work on individual columns without causing memory
243  ! to have page faults.
244  do i=is,ie ; if (cn(i,j)>0.0)then
245  !----for debugging, remove later----
246  ig = i + g%idg_offset ; jg = j + g%jdg_offset
247  !if (ig == CS%int_tide_source_x .and. jg == CS%int_tide_source_y) then
248  !-----------------------------------
249  if (g%mask2dT(i,j) > 0.5) then
250 
251  lam = 1/(cn(i,j)**2)
252 
253  ! Calculate drxh_sum
254  if (use_eos) then
255  pres(1) = 0.0
256  do k=2,kf(i)
257  pres(k) = pres(k-1) + h_to_pres*hf(k-1,i)
258  t_int(k) = 0.5*(tf(k,i)+tf(k-1,i))
259  s_int(k) = 0.5*(sf(k,i)+sf(k-1,i))
260  enddo
261  call calculate_density_derivs(t_int, s_int, pres, drho_dt, drho_ds, 2, &
262  kf(i)-1, tv%eqn_of_state)
263 
264  ! Sum the reduced gravities to find out how small a density difference
265  ! is negligibly small.
266  drxh_sum = 0.0
267  do k=2,kf(i)
268  drxh_sum = drxh_sum + 0.5*(hf(k-1,i)+hf(k,i)) * &
269  max(0.0,drho_dt(k)*(tf(k,i)-tf(k-1,i)) + &
270  drho_ds(k)*(sf(k,i)-sf(k-1,i)))
271  enddo
272  else
273  drxh_sum = 0.0
274  do k=2,kf(i)
275  drxh_sum = drxh_sum + 0.5*(hf(k-1,i)+hf(k,i)) * &
276  max(0.0,rf(k,i)-rf(k-1,i))
277  enddo
278  endif ! use_EOS?
279 
280  ! Find gprime across each internal interface, taking care of convective
281  ! instabilities by merging layers.
282  if (drxh_sum >= 0.0) then
283  ! Merge layers to eliminate convective instabilities or exceedingly
284  ! small reduced gravities.
285  if (use_eos) then
286  kc = 1
287  hc(1) = hf(1,i) ; tc(1) = tf(1,i) ; sc(1) = sf(1,i)
288  do k=2,kf(i)
289  if ((drho_dt(k)*(tf(k,i)-tc(kc)) + drho_ds(k)*(sf(k,i)-sc(kc))) * &
290  (hc(kc) + hf(k,i)) < 2.0 * tol2*drxh_sum) then
291  ! Merge this layer with the one above and backtrack.
292  i_hnew = 1.0 / (hc(kc) + hf(k,i))
293  tc(kc) = (hc(kc)*tc(kc) + hf(k,i)*tf(k,i)) * i_hnew
294  sc(kc) = (hc(kc)*sc(kc) + hf(k,i)*sf(k,i)) * i_hnew
295  hc(kc) = (hc(kc) + hf(k,i))
296  ! Backtrack to remove any convective instabilities above... Note
297  ! that the tolerance is a factor of two larger, to avoid limit how
298  ! far back we go.
299  do k2=kc,2,-1
300  if ((drho_dt(k2)*(tc(k2)-tc(k2-1)) + drho_ds(k2)*(sc(k2)-sc(k2-1))) * &
301  (hc(k2) + hc(k2-1)) < tol2*drxh_sum) then
302  ! Merge the two bottommost layers. At this point kc = k2.
303  i_hnew = 1.0 / (hc(kc) + hc(kc-1))
304  tc(kc-1) = (hc(kc)*tc(kc) + hc(kc-1)*tc(kc-1)) * i_hnew
305  sc(kc-1) = (hc(kc)*sc(kc) + hc(kc-1)*sc(kc-1)) * i_hnew
306  hc(kc-1) = (hc(kc) + hc(kc-1))
307  kc = kc - 1
308  else ; exit ; endif
309  enddo
310  else
311  ! Add a new layer to the column.
312  kc = kc + 1
313  drho_ds(kc) = drho_ds(k) ; drho_dt(kc) = drho_dt(k)
314  tc(kc) = tf(k,i) ; sc(kc) = sf(k,i) ; hc(kc) = hf(k,i)
315  endif
316  enddo
317  ! At this point there are kc layers and the gprimes should be positive.
318  do k=2,kc ! Revisit this if non-Boussinesq.
319  gprime(k) = g_rho0 * (drho_dt(k)*(tc(k)-tc(k-1)) + &
320  drho_ds(k)*(sc(k)-sc(k-1)))
321  enddo
322  else ! .not.use_EOS
323  ! Do the same with density directly...
324  kc = 1
325  hc(1) = hf(1,i) ; rc(1) = rf(1,i)
326  do k=2,kf(i)
327  if ((rf(k,i) - rc(kc)) * (hc(kc) + hf(k,i)) < 2.0*tol2*drxh_sum) then
328  ! Merge this layer with the one above and backtrack.
329  rc(kc) = (hc(kc)*rc(kc) + hf(k,i)*rf(k,i)) / (hc(kc) + hf(k,i))
330  hc(kc) = (hc(kc) + hf(k,i))
331  ! Backtrack to remove any convective instabilities above... Note
332  ! that the tolerance is a factor of two larger, to avoid limit how
333  ! far back we go.
334  do k2=kc,2,-1
335  if ((rc(k2)-rc(k2-1)) * (hc(k2)+hc(k2-1)) < tol2*drxh_sum) then
336  ! Merge the two bottommost layers. At this point kc = k2.
337  rc(kc-1) = (hc(kc)*rc(kc) + hc(kc-1)*rc(kc-1)) / (hc(kc) + hc(kc-1))
338  hc(kc-1) = (hc(kc) + hc(kc-1))
339  kc = kc - 1
340  else ; exit ; endif
341  enddo
342  else
343  ! Add a new layer to the column.
344  kc = kc + 1
345  rc(kc) = rf(k,i) ; hc(kc) = hf(k,i)
346  endif
347  enddo
348  ! At this point there are kc layers and the gprimes should be positive.
349  do k=2,kc ! Revisit this if non-Boussinesq.
350  gprime(k) = g_rho0 * (rc(k)-rc(k-1))
351  enddo
352  endif ! use_EOS?
353 
354  !-----------------NOW FIND WAVE STRUCTURE-------------------------------------
355  ! Construct and solve tridiagonal system for the interior interfaces
356  ! Note that kc = number of layers,
357  ! kc+1 = nzm = number of interfaces,
358  ! kc-1 = number of interior interfaces (excluding surface and bottom)
359  ! Also, note that "K" refers to an interface, while "k" refers to the layer below.
360  ! Need at least 3 layers (2 internal interfaces) to generate a matrix, also
361  ! need number of layers to be greater than the mode number
362  if (kc >= modenum + 1) then
363  ! Set depth at surface
364  z_int(1) = 0.0
365  ! Calculate Igu, Igl, depth, and N2 at each interior interface
366  ! [excludes surface (K=1) and bottom (K=kc+1)]
367  do k=2,kc
368  igl(k) = 1.0/(gprime(k)*hc(k)) ; igu(k) = 1.0/(gprime(k)*hc(k-1))
369  z_int(k) = z_int(k-1) + hc(k-1)
370  n2(k) = us%m_to_Z**2*gprime(k)/(0.5*(hc(k)+hc(k-1)))
371  enddo
372  ! Set stratification for surface and bottom (setting equal to nearest interface for now)
373  n2(1) = n2(2) ; n2(kc+1) = n2(kc)
374  ! Calcualte depth at bottom
375  z_int(kc+1) = z_int(kc)+hc(kc)
376  ! check that thicknesses sum to total depth
377  if (abs(z_int(kc+1)-htot(i,j)) > 1.e-14*htot(i,j)) then
378  call mom_error(fatal, "wave_structure: mismatch in total depths")
379  endif
380 
381  ! Note that many of the calcluation from here on revert to using vertical
382  ! distances in m, not Z.
383 
384  ! Populate interior rows of tridiagonal matrix; must multiply through by
385  ! gprime to get tridiagonal matrix to the symmetrical form:
386  ! [-1/H(k-1)]e(k-1) + [1/H(k-1)+1/H(k)-lam_z]e(k) + [-1/H(k)]e(k+1) = 0,
387  ! where lam_z = lam*gprime is now a function of depth.
388  ! Frist, populate interior rows
389  do k=3,kc-1
390  row = k-1 ! indexing for TD matrix rows
391  gp_unscaled = us%m_to_Z*gprime(k)
392  lam_z(row) = lam*gp_unscaled
393  a_diag(row) = gp_unscaled*(-igu(k))
394  b_diag(row) = gp_unscaled*(igu(k)+igl(k)) - lam_z(row)
395  c_diag(row) = gp_unscaled*(-igl(k))
396  if (isnan(lam_z(row)))then ; print *, "Wave_structure: lam_z(row) is NAN" ; endif
397  if (isnan(a_diag(row)))then ; print *, "Wave_structure: a(k) is NAN" ; endif
398  if (isnan(b_diag(row)))then ; print *, "Wave_structure: b(k) is NAN" ; endif
399  if (isnan(c_diag(row)))then ; print *, "Wave_structure: c(k) is NAN" ; endif
400  enddo
401  ! Populate top row of tridiagonal matrix
402  k=2 ; row = k-1 ;
403  gp_unscaled = us%m_to_Z*gprime(k)
404  lam_z(row) = lam*gp_unscaled
405  a_diag(row) = 0.0
406  b_diag(row) = gp_unscaled*(igu(k)+igl(k)) - lam_z(row)
407  c_diag(row) = gp_unscaled*(-igl(k))
408  ! Populate bottom row of tridiagonal matrix
409  k=kc ; row = k-1
410  gp_unscaled = us%m_to_Z*gprime(k)
411  lam_z(row) = lam*gp_unscaled
412  a_diag(row) = gp_unscaled*(-igu(k))
413  b_diag(row) = gp_unscaled*(igu(k)+igl(k)) - lam_z(row)
414  c_diag(row) = 0.0
415 
416  ! Guess a vector shape to start with (excludes surface and bottom)
417  e_guess(1:kc-1) = sin((z_int(2:kc)/htot(i,j)) *pi)
418  e_guess(1:kc-1) = e_guess(1:kc-1)/sqrt(sum(e_guess(1:kc-1)**2))
419 
420  ! Perform inverse iteration with tri-diag solver
421  do itt=1,max_itt
422  call tridiag_solver(a_diag(1:kc-1),b_diag(1:kc-1),c_diag(1:kc-1), &
423  -lam_z(1:kc-1),e_guess(1:kc-1),"TDMA_H",e_itt)
424  e_guess(1:kc-1) = e_itt(1:kc-1)/sqrt(sum(e_itt(1:kc-1)**2))
425  enddo ! itt-loop
426  w_strct(2:kc) = e_guess(1:kc-1)
427  w_strct(1) = 0.0 ! rigid lid at surface
428  w_strct(kc+1) = 0.0 ! zero-flux at bottom
429 
430  ! Check to see if solver worked
431  ig_stop = 0 ; jg_stop = 0
432  if (isnan(sum(w_strct(1:kc+1))))then
433  print *, "Wave_structure: w_strct has a NAN at ig=", ig, ", jg=", jg
434  if (i<g%isc .or. i>g%iec .or. j<g%jsc .or. j>g%jec)then
435  print *, "This is occuring at a halo point."
436  endif
437  ig_stop = ig ; jg_stop = jg
438  endif
439 
440  ! Normalize vertical structure function of w such that
441  ! \int(w_strct)^2dz = a_int (a_int could be any value, e.g., 0.5)
442  nzm = kc+1 ! number of layer interfaces after merging
443  !(including surface and bottom)
444  w2avg = 0.0
445  do k=1,nzm-1
446  dz(k) = us%Z_to_m*hc(k)
447  w2avg = w2avg + 0.5*(w_strct(k)**2+w_strct(k+1)**2)*dz(k)
448  enddo
449  !### Some mathematical cancellations could occur in the next two lines.
450  w2avg = w2avg / htot(i,j)
451  w_strct = w_strct / sqrt(htot(i,j)*w2avg*i_a_int)
452 
453  ! Calculate vertical structure function of u (i.e. dw/dz)
454  do k=2,nzm-1
455  u_strct(k) = 0.5*((w_strct(k-1) - w_strct(k) )/dz(k-1) + &
456  (w_strct(k) - w_strct(k+1))/dz(k))
457  enddo
458  u_strct(1) = (w_strct(1) - w_strct(2) )/dz(1)
459  u_strct(nzm) = (w_strct(nzm-1)- w_strct(nzm))/dz(nzm-1)
460 
461  ! Calculate wavenumber magnitude
462  f2 = us%s_to_T**2 * g%CoriolisBu(i,j)**2
463  !f2 = 0.25*US%s_to_T**2 *((G%CoriolisBu(I,J)**2 + G%CoriolisBu(I-1,J-1)**2) + &
464  ! (G%CoriolisBu(I,J-1)**2 + G%CoriolisBu(I-1,J)**2))
465  kmag2 = (freq**2 - f2) / (cn(i,j)**2 + cg_subro**2)
466 
467  ! Calculate terms in vertically integrated energy equation
468  int_dwdz2 = 0.0 ; int_w2 = 0.0 ; int_n2w2 = 0.0
469  u_strct2 = u_strct(1:nzm)**2
470  w_strct2 = w_strct(1:nzm)**2
471  ! vertical integration with Trapezoidal rule
472  do k=1,nzm-1
473  int_dwdz2 = int_dwdz2 + 0.5*(u_strct2(k)+u_strct2(k+1))*dz(k)
474  int_w2 = int_w2 + 0.5*(w_strct2(k)+w_strct2(k+1))*dz(k)
475  int_n2w2 = int_n2w2 + 0.5*(w_strct2(k)*n2(k)+w_strct2(k+1)*n2(k+1))*dz(k)
476  enddo
477 
478  ! Back-calculate amplitude from energy equation
479  if (kmag2 > 0.0) then
480  ke_term = 0.25*gv%Rho0*( (1+f2/freq**2)/kmag2*int_dwdz2 + int_w2 )
481  pe_term = 0.25*gv%Rho0*( int_n2w2/freq**2 )
482  if (en(i,j) >= 0.0) then
483  w0 = sqrt( en(i,j)/(ke_term + pe_term) )
484  else
485  call mom_error(warning, "wave_structure: En < 0.0; setting to W0 to 0.0")
486  print *, "En(i,j)=", en(i,j), " at ig=", ig, ", jg=", jg
487  w0 = 0.0
488  endif
489  ! Calculate actual vertical velocity profile and derivative
490  w_profile = w0*w_strct
491  dwdz_profile = w0*u_strct
492  ! Calculate average magnitude of actual horizontal velocity over a period
493  uavg_profile = abs(dwdz_profile) * sqrt((1+f2/freq**2)/(2.0*kmag2))
494  else
495  w_profile = 0.0
496  dwdz_profile = 0.0
497  uavg_profile = 0.0
498  endif
499 
500  ! Store values in control structure
501  cs%w_strct(i,j,1:nzm) = w_strct(:)
502  cs%u_strct(i,j,1:nzm) = u_strct(:)
503  cs%W_profile(i,j,1:nzm) = w_profile(:)
504  cs%Uavg_profile(i,j,1:nzm)= uavg_profile(:)
505  cs%z_depths(i,j,1:nzm) = us%Z_to_m*z_int(:)
506  cs%N2(i,j,1:nzm) = n2(:)
507  cs%num_intfaces(i,j) = nzm
508  else
509  ! If not enough layers, default to zero
510  nzm = kc+1
511  cs%w_strct(i,j,1:nzm) = 0.0
512  cs%u_strct(i,j,1:nzm) = 0.0
513  cs%W_profile(i,j,1:nzm) = 0.0
514  cs%Uavg_profile(i,j,1:nzm)= 0.0
515  cs%z_depths(i,j,1:nzm) = 0.0 ! could use actual values
516  cs%N2(i,j,1:nzm) = 0.0 ! could use with actual values
517  cs%num_intfaces(i,j) = nzm
518  endif ! kc >= 3 and kc > ModeNum + 1?
519  endif ! drxh_sum >= 0?
520  !else ! if at test point - delete later
521  ! return ! if at test point - delete later
522  !endif ! if at test point - delete later
523  endif ! mask2dT > 0.5?
524  else
525  ! if cn=0.0, default to zero
526  nzm = nz+1! could use actual values
527  cs%w_strct(i,j,1:nzm) = 0.0
528  cs%u_strct(i,j,1:nzm) = 0.0
529  cs%W_profile(i,j,1:nzm) = 0.0
530  cs%Uavg_profile(i,j,1:nzm)= 0.0
531  cs%z_depths(i,j,1:nzm) = 0.0 ! could use actual values
532  cs%N2(i,j,1:nzm) = 0.0 ! could use with actual values
533  cs%num_intfaces(i,j) = nzm
534  endif ; enddo ! if cn>0.0? ; i-loop
535  enddo ! j-loop
536 

◆ wave_structure_init()

subroutine, public mom_wave_structure::wave_structure_init ( type(time_type), intent(in)  Time,
type(ocean_grid_type), intent(in)  G,
type(param_file_type), intent(in)  param_file,
type(diag_ctrl), intent(in), target  diag,
type(wave_structure_cs), pointer  CS 
)

Allocate memory associated with the wave structure module and read parameters.

Parameters
[in]timeThe current model time.
[in]gThe ocean's grid structure.
[in]param_fileA structure to parse for run-time parameters.
[in]diagA structure that is used to regulate diagnostic output.
csA pointer that is set to point to the control structure for this module.

Definition at line 667 of file MOM_wave_structure.F90.

667  type(time_type), intent(in) :: Time !< The current model time.
668  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
669  type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time
670  !! parameters.
671  type(diag_ctrl), target, intent(in) :: diag !< A structure that is used to regulate
672  !! diagnostic output.
673  type(wave_structure_CS), pointer :: CS !< A pointer that is set to point to the
674  !! control structure for this module.
675 ! This include declares and sets the variable "version".
676 #include "version_variable.h"
677  character(len=40) :: mdl = "MOM_wave_structure" ! This module's name.
678  integer :: isd, ied, jsd, jed, nz
679 
680  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed ; nz = g%ke
681 
682  if (associated(cs)) then
683  call mom_error(warning, "wave_structure_init called with an "// &
684  "associated control structure.")
685  return
686  else ; allocate(cs) ; endif
687 
688  call get_param(param_file, mdl, "INTERNAL_TIDE_SOURCE_X", cs%int_tide_source_x, &
689  "X Location of generation site for internal tide", default=1.)
690  call get_param(param_file, mdl, "INTERNAL_TIDE_SOURCE_Y", cs%int_tide_source_y, &
691  "Y Location of generation site for internal tide", default=1.)
692 
693  cs%diag => diag
694 
695  ! Allocate memory for variable in control structure; note,
696  ! not all rows will be filled if layers get merged!
697  allocate(cs%w_strct(isd:ied,jsd:jed,nz+1))
698  allocate(cs%u_strct(isd:ied,jsd:jed,nz+1))
699  allocate(cs%W_profile(isd:ied,jsd:jed,nz+1))
700  allocate(cs%Uavg_profile(isd:ied,jsd:jed,nz+1))
701  allocate(cs%z_depths(isd:ied,jsd:jed,nz+1))
702  allocate(cs%N2(isd:ied,jsd:jed,nz+1))
703  allocate(cs%num_intfaces(isd:ied,jsd:jed))
704 
705  ! Write all relevant parameters to the model log.
706  call log_version(param_file, mdl, version, "")
707