WAVEWATCH III  beta 0.0.1
w3gkemd.F90
Go to the documentation of this file.
1 #include "w3macros.h"
2 !/ ------------------------------------------------------------------- /
3 module w3gkemd
4  !/ ------------------------------------------------------------------- /
5  !/
6  !/ +-----------------------------------+
7  !/ | WAVEWATCH III NOAA/NCEP |
8  !/ | Odin Gramstad |
9  !/ | Qingxiang Liu |
10  !/ | |
11  !/ | FORTRAN 90 |
12  !/ | Last update : 03-Jun-2021 |
13  !/ +-----------------------------------+
14  !/
15  !/ 26-May-2014 : Origination. ( version 3.14 )
16  !/ Intially Dr. O. Gramstad implemented his GKE method
17  !/ in WW3 v3.14 which worked well for the single-grid
18  !/ point duration-limited test.
19  !/
20  !/ 09-Nov-2018 : Fully implemented in WW3 ( version 7.13 )
21  !/ ( Q. Liu )
22  !/ 16-Apr-2019 : Add save attribute explicitly ( version 7.13 )
23  !/ ( Q. Liu )
24  !/ 18-Apr-2019 : Add the bilinear interp. option ( version 7.13 )
25  !/ ( Q. Liu )
26  !/ 08-Jul-2019 : Use kind=8 for qi_nnz ( Q. Liu )
27  !/ 01-Apr-2020 : Boundary conditions ( Q. Liu )
28  !/ 03-Jun-2021 : Merge into the WW3 Github ( version 7.12 )
29  !/ ( Q. Liu )
30  !/
31  ! 1. Purpose:
32  ! Calculate the (resonant & quasi/near-resonant) four-wave nonlinear
33  ! interaction term S_{nl} according to the generalized kinetic
34  ! equation (GKE) developed in Gramstad and Stiassnie (2013).
35  !
36  ! References:
37  ! Gramstad and Stiassnie (2013), JFM, 818, 280-303 (hereafter GS13)
38  ! Gramstad and Babanin (2016), OD, 66, 509-526 (hereafter GB16)
39  ! Liu et al. (2021), JFM, 910, A50 (hereafter LGB21)
40  ! &
41  ! Annenkov and Shrira (2006), JFM, 561, 181-207 (*)
42  ! Annenkov and Shrira (2015), JPO, 45, 807-812
43  ! Annenkov and Shrira (2018), JFM, 844, 766-795 (*)
44  ! &
45  ! Shrira and Annenkov (2013), Book Ch., 239-281
46  ! Annenkov and Shrira (2016), Book Ch., 159-178
47  !
48  ! (*) Note that equations therein contain typos.
49  !
50  ! 2. Subroutines and functions :
51  !
52  ! [Part 1]: Kernel Function
53  !
54  ! Calculate the kernel function T_{0, 1, 2, 3} for the Zakharov
55  ! Equation.
56  !
57  ! References:
58  ! Krasitskii (1994), JFM, 272, 1 - 20 (hereafter K94)
59  ! Janssen (2009), JFM, 637, 1 - 44 (hereafter J09)
60  ! Mei et al. (2005), ch. 14, 882 - 884
61  !
62  ! Based on my own observation, Odin has closely followed the
63  ! equations presented in the appendix (A.1/2) of J09.
64  !
65  ! ----------------------------------------------------------------
66  ! Name Type Scope Description
67  ! ----------------------------------------------------------------
68  ! QFunc Func. Private q = ω^2 / g
69  ! VpFunc Func. Private V^{(+)}_{1, 2, 3}
70  ! VmFunc Func. Private V^{(-)}_{1, 2, 3}
71  ! UFunc Func. Private U_{1, 2, 3, 4}
72  ! TFunc Func. Public T_{1, 2, 3, 4}
73  ! ----------------------------------------------------------------
74  !
75  ! [Part 2]: Find Quartets (total number & configurations)
76  !
77  ! References:
78  ! Annenkov and Shrira (2015), JPO, 45, 807-812
79  ! Hasselmann and Hasselmann (1981), Exact-NL/DIA report
80  ! Hasselmann and Hasselmann (1985), JPO, 15, 1369-1377
81  !
82  ! ----------------------------------------------------------------
83  ! Name Type Scope Description
84  ! ----------------------------------------------------------------
85  ! FindQuartetNumber Subr. Private Total No. of Quartets
86  ! FindQuartetConfig Subr. Private Config. of Quartets
87  !
88  ! [Part 3]: Sparse matrix (storage, operation)
89  !
90  ! References:
91  ! Saad (1994) SPARSKIT: a basic tool kit for sparse matrix
92  ! compuation (version 2)
93  !
94  ! ----------------------------------------------------------------
95  ! Name Type Scope Description
96  ! ----------------------------------------------------------------
97  ! CooCsrInd Subr. Private COO to CSR format
98  ! ASymSmatTimVec Subr. Private (A±A^T)∙X, where (A±A^T) is an
99  ! (anti)symmetric sparse matrix
100  !
101  ! [Part 4]: GKE Integral (main subrs.)
102  !
103  ! References:
104  ! Gramstad and Stiassnie (2013), JFM, 818, 280-303 (hereafter GS13)
105  ! Gramstad and Babanin (2016), OD, 66, 509-526 (hereafter GB16)
106  ! Liu et al. (2021), JFM, 910, A50 (hereafter LGB21)
107  ! Janssen (2003), JPO, 33, 863-884 (hereafter J03)
108  ! Janssen (2009), JFM, 637, 1- 44 (hereafter J09)
109  ! Annenkov and Shrira (2013), JFM, 726, 517-546
110  !
111  ! Hasselmann and Hasselmann (1981), Exact-NL/DIA report
112  ! Hasselmann and Hasselmann (1985), JPO, 15, 1369-1377
113  ! van Vledder (2006), CE, 53, 223-242
114  ! Tolman (2013), OM, 70, 11- 24
115  !
116  ! ----------------------------------------------------------------
117  ! Name Type Scope Description
118  ! ----------------------------------------------------------------
119  ! PrepKGrid Subr. Private (σ, θ) to (kx, ky)
120  ! PrepKernelIO Subr. Private Read/Write Quartet Cfg file
121  ! BiInterpWT Subr. Private Calc. interp. weights
122  ! CalcQRSNL Subr. Public GKE Transfer Integral
123  !
124  ! 3. Future work (TODO)
125  ! * The current version only works for a constant-depth application (
126  ! either deep or finite deep). Extension of this module to be
127  ! applicable to varying-depth cases may be pursued in the future.
128  !
129  ! * Dnl -- diagonal term
130  ! * βnpqr -- nonlinear stokes correction
131  !/
132  !/ ------------------------------------------------------------------- /
133  !
134  ! Public parameters
135  !
136  ! * `qi_` denotes the variable is integer number
137  ! * `qr_` ... real ...
138  ! * `qs_` ... string
139  ! * `ql_` ... logical
140  ! * `qc_` ... complex
141  !/
142  implicit none
143  !/ ------------------------------------------------------------------- /
144  public :: prepkernelio, calcqrsnl
145  !
147 
148  private :: qfunc, vpfunc, vmfunc, ufunc, tfunc, &
149  findquartetnumber, findquartetconfig, &
150  coocsrind, asymsmattimvec, &
151  prepkgrid, biinterpwt
152  !
153  private :: qs_ver, qi_lrb, qr_eps, qr_grav, &
154  qr_pi, qr_tpi, qr_dmax, qc_iu, qs_cfg, &
155  qr_kx, qr_ky, qr_dk, qr_om, qi_nrsm, &
156  qi_nn, qi_pp, qi_qq, qi_rr, &
157  qr_k4x, qr_k4y, qr_om4, qr_dom, &
158  qr_tkern, qr_tkurt, &
159  qi_iccos, qi_ircsr, qr_sumqr, qr_sumnp, &
160  qi_bind, qr_bwgh, qr_wn1
161  !
162  private :: qi_bound, qr_fpow, qr_bdry
163  !
164  !/ ------------------------------------------------------------------- /
165  real :: qr_depth ! Real water depth d (m)
166 
167  real :: qr_oml ! λ cut off factor
168  ! λ ≤ 0 → quartets far
169  ! from resonance will not
170  ! be excluded.
171 
172  integer :: qi_disc ! Discretization of GKE
173  ! 0: continuous (like Exact-NL, WRT)
174  ! 1: discrete (see GS13)
175 
176  integer :: qi_kev ! Version of KE
177  ! 0: GKE
178  ! 1: KE from J03
179 
180  integer :: qi_interp ! Interp. option
181  ! 0: Nearest bin
182  ! 1: Bilinear Interp
183 
184  integer, parameter :: qi_bound= 1 ! Boundary conditions
185  ! 0: no bound
186  ! 1: tail extension
187 
188  real, parameter :: qr_fpow= -5. ! E(f) tail power law
189  !
190  character(len=50), parameter &
191  :: qs_ver = 'gkev0' ! version number/str
192  integer, parameter :: qi_lrb = 4 ! 4 bytes
193  real, parameter :: qr_eps = epsilon(100.0) ! Smallest positive
194  ! value supported by the
195  ! compiler (e.g., gfortran
196  ! → 1.19E-7)
197 
198  real, parameter :: qr_grav = 9.806 ! Gravational acc (m/s^2)
199  real, parameter :: qr_pi = 3.141592653589793 ! π
200  real, parameter :: qr_tpi = 2 * qr_pi ! π * 2
201  real, parameter :: qr_dmax = 3000.0 ! Maximum allowed water
202  complex, parameter :: qc_iu = (0.0, 1.0) ! complex unit `i`
203  !
204  character(len=100) :: qs_cfg ! File name for quartet/kernel
205  !
206  real, allocatable, save :: qr_kx(:), qr_ky(:), & ! kx, ky (2D grid → 1D vector)
207  qr_dk(:), qr_om(:) ! Δ\vec{k}, ω,
208  !
209  integer(kind=8) :: qi_nnz ! # of quartets
210  integer :: qi_nrsm ! # of rows of SMat
211  integer, allocatable, save :: qi_nn(:), qi_pp(:), & ! Index for Quartets
212  qi_qq(:), qi_rr(:)
213  real, allocatable, save :: qr_k4x(:), qr_k4y(:), & ! kx, ky, ω for 4th wave
214  qr_om4(:)
215  real, allocatable, save :: qr_dom(:), & ! Δω
216  qr_tkern(:), & ! Kernel `T`
217  qr_tkurt(:) ! Kurtosis `T`
218  integer, allocatable, save :: qi_iccos(:), & ! col index of CooCsr
219  qi_ircsr(:) ! row begining index of
220  ! Csr sparse matrix
221  real, allocatable, save :: qr_sumqr(:), & ! Σ over Q, R
222  qr_sumnp(:, :) ! Σ over P
223  !
224  integer, allocatable, save :: qi_bind(:, :) ! Bilinear interp. (index and
225  real, allocatable, save :: qr_bwgh(:, :) ! weight)
226  real, allocatable, save :: qr_bdry(:) ! Boundary weight
227  real, allocatable, save :: qr_wn1(:) ! wavenumber k(nk)
228  !/
229  !/ ------------------------------------------------------------------- /
230 contains
231  !/ ------------------------------------------------------------------- /
232  !/ [Part 1]
233  !/
234  function qfunc(kx, ky)
235  !/
236  !/ 19-Dec-2011 : Origination. ( O. Gramstad )
237  !/
238  !/ 09-Nov-2018 : Prepare WW3 distribution ( Q. Liu )
239  !/
240  ! 1. Purpose: Define q = ω^2 / g (i.e., q in K94 & J09)
241  !
242  ! 2. Method:
243  ! For wind-generated ocean surface waves, the dispersion relation
244  ! reads
245  ! ω^2 = g k tanh(kd),
246  ! where g is the gravtional acceleration, ω is the radian frequency,
247  ! d is the water depth. Hence,
248  !
249  ! / k = √(k_x**2. + k_y**2.) for deep-water
250  ! q = |
251  ! \ k tanh(kd) for finite-deep water (e.g.,
252  ! d < 2500.)
253  !
254  !/
255  implicit none
256  !
257  real, intent(in) :: kx, ky ! x, y components of wavenumber
258  ! vector (kx, ky)
259  real :: qfunc ! Returned function
260  !/
261  qfunc = sqrt(kx*kx + ky*ky) ! deep-water case (q = k)
262  !
263  ! Odin used qr_dmax = 2500.
264  !
265  if (qr_depth > qr_eps .and. qr_depth < qr_dmax) then
266  qfunc = qfunc * tanh(qfunc * qr_depth) ! finite-deep
267  end if
268  !
269  return
270  !/
271  end function qfunc
272  !/
273  !/ ------------------------------------------------------------------- /
274  !/
275  function vpfunc(k0x, k0y, k1x, k1y, k2x, k2y)
276  !/
277  !/ 19-Dec-2011: Origination. ( O. Gramstad )
278  !/
279  !/ 09-Nov-2018 : Prepare WW3 distribution ( Q. Liu )
280  !/
281  !/
282  ! 1. Purpose:
283  ! Calculate the second-order coefficient V^{(+)}_{1, 2, 3} of J09,
284  ! which corresponds to U^{(3)}_{0, 1, 2} of K94.
285  !
286  ! ◆ V^{(+)}_{1, 2, 3} differs from U^{(3)}_{0, 1, 2} by a factor
287  ! of 1/2π --- this is because the wave spectrum F(k) used in K94
288  ! and J09 differ by a fator of (1/2π)^2.
289  !
290  !/
291  implicit none
292  !
293  real, intent(in) :: k0x, k0y, k1x, k1y, k2x, k2y ! 3 waves
294  real :: vpfunc ! V^{(+)}_{1, 2, 3}
295  !
296  real :: q0, q1, q2 ! q for 3 waves
297  !/
298  ! Call Q function here
299  q0 = qfunc(k0x, k0y)
300  q1 = qfunc(k1x, k1y)
301  q2 = qfunc(k2x, k2y)
302  !
303  ! Odin has ignored √g here because it will be absorbed/vanish when we
304  ! calculate the kernel function T. I, however, included √g here for
305  ! clarity.
306  ! V^{(+)}_{1, 2, 3}
307  !
308  vpfunc = sqrt(1.0/32.0) * ( &
309  (k0x*k1x + k0y*k1y + q0*q1) * sqrt(sqrt(qr_grav*q2 / (q0*q1)))&
310  + (k0x*k2x + k0y*k2y + q0*q2) * sqrt(sqrt(qr_grav*q1 / (q0*q2)))&
311  + (k1x*k2x + k1y*k2y + q1*q2) * sqrt(sqrt(qr_grav*q0 / (q1*q2))))
312  !
313  return
314  !/
315  end function vpfunc
316  !/
317  !/ ------------------------------------------------------------------- /
318  !/
319  function vmfunc(k0x, k0y, k1x, k1y, k2x, k2y)
320  !/
321  !/ 19-Dec-2011 : Origination. ( O. Gramstad )
322  !/
323  !/ 09-Nov-2018 : Prepare WW3 distribution ( Q. Liu )
324  !/
325  ! 1. Purpose:
326  ! Calculate the second-order coefficient V^{(-)}_{1, 2, 3} of J09,
327  ! which corresponds to U^{(1)}_{0, 1, 2} of K94.
328  !
329  ! ◆ V^{(-)}_{1, 2, 3} differs from U^{(1)}_{0, 1, 2} by a factor
330  ! of 1/2π
331  !
332  !/
333  implicit none
334  !
335  real, intent(in) :: k0x, k0y, k1x, k1y, k2x, k2y ! 3 waves
336  real :: vmfunc ! V^{(-)}_{1, 2, 3}
337  !
338  real :: q0, q1, q2 ! q for 3 waves
339  !/
340  ! Call Q function here
341  q0 = qfunc(k0x, k0y)
342  q1 = qfunc(k1x, k1y)
343  q2 = qfunc(k2x, k2y)
344  !
345  ! V^{(-)}_{1, 2, 3}
346  !
347  vmfunc = sqrt(1.0/32.0) * ( &
348  (k0x*k1x + k0y*k1y - q0*q1) * sqrt(sqrt(qr_grav*q2 / (q0*q1)))&
349  + (k0x*k2x + k0y*k2y - q0*q2) * sqrt(sqrt(qr_grav*q1 / (q0*q2)))&
350  + (k1x*k2x + k1y*k2y + q1*q2) * sqrt(sqrt(qr_grav*q0 / (q1*q2))))
351  !
352  return
353  !/
354  end function vmfunc
355  !/
356  !/ ------------------------------------------------------------------- /
357  !/
358  function ufunc(k0x, k0y, k1x, k1y, k2x, k2y, k3x, k3y)
359  !/
360  !/ 19-Dec-2011 : Origination. ( O. Gramstad )
361  !/
362  !/ 09-Nov-2018 : Prepare WW3 distribution ( Q. Liu )
363  !/
364  ! 1. Purpose:
365  ! Calculate the intermediate quantity (i.e., U_{1, 2, 3, 4} in J09,
366  ! V_{0, 1, 2, 3} in K94) for the third-order coefficient (i.e.,
367  ! W^{(2)}_{1, 2, 3, 4} in J09, V^{(2)}_{0, 1, 2, 3} in K94).
368  !
369  ! ◆ U_{1, 2, 3, 4} differs from V_{0, 1, 2, 3} by a factor of
370  ! (1/2π)^2.
371  !
372  !/
373  implicit none
374  !
375  real, intent(in) :: k0x, k0y, k1x, k1y, &
376  k2x, k2y, k3x, k3y ! 4 waves
377  real :: ufunc ! U_{1, 2, 3, 4}
378  !
379  real :: q0, q1, q2, q3 ! q for 4 waves
380  !/
381  ! Call Q function here
382  q0 = qfunc(k0x, k0y)
383  q1 = qfunc(k1x, k1y)
384  q2 = qfunc(k2x, k2y)
385  q3 = qfunc(k3x, k3y)
386  !
387  ! U_{1, 2, 3, 4}
388  !
389  ufunc = (1.0/16.0) * sqrt(sqrt(q2*q3 / (q0*q1))) * &
390  (2.0*((k0x*k0x + k0y*k0y) * q1 + (k1x*k1x + k1y*k1y) * q0)-&
391  q0*q1*( qfunc(k0x+k2x, k0y+k2y) + qfunc(k1x+k2x, k1y+k2y)+&
392  qfunc(k0x+k3x, k0y+k3y) + qfunc(k1x+k3x, k1y+k3y) ))
393  !
394  return
395  !/
396  end function ufunc
397  !/
398  !/ ------------------------------------------------------------------- /
399  !/
400  function tfunc(k0x, k0y, k1x, k1y, k2x, k2y, k3x, k3y)
401  !/
402  !/ 19-Dec-2011 : Origination. ( O. Gramstad )
403  !/
404  !/ 09-Nov-2018 : Prepare WW3 distribution ( Q. Liu )
405  !/
406  ! 1. Purpose:
407  ! Calculate the Kernel function for the four-wave interaction, i.e.,
408  ! (T_{1, 2, 3, 4}, \widetilde{V}^{(2)}_{0, 1, 2, 3} in K94).
409  ! ◆ T from J09 and K94 differ by a factor of (1/2π)^2.
410  !
411  ! Odin's comment:
412  ! Kernel function for all combination that are not Stokes correction.
413  ! I.e. n0 != n2 and n0 != n3
414  !/
415  implicit none
416  !
417  real, intent(in) :: k0x, k0y, k1x, k1y, &
418  k2x, k2y, k3x, k3y ! 4 waves
419  real :: tfunc ! T_{1, 2, 3, 4}
420  !
421  ! Virtual-state interaction: two free waves generate a virtual state
422  ! consisting of bound waves, which then decays into a different set of
423  ! free waves (see J09)
424  !
425  real :: om0, om1, om2, om3, & ! ω for 4 waves
426  om02, & ! ω_{0-2}
427  om13, & ! ω_{1-3}
428  om12, & ! ω_{1-2}
429  om03, & ! ω_{0-3}
430  om0p1, & ! ω_{0+1}
431  om2p3 ! ω_{2+3}
432  !
433  real :: l14, l23, l56, &
434  w ! W^{(2)}_{1, 2, 3, 4} in J09
435  ! or
436  ! V^{(2)}_{0, 1, 2, 3} in K94
437  !/
438  ! Initilization
439  om0p1 = 0.
440  om2p3 = 0.
441  w = 0.
442  l14 = 0.
443  l23 = 0.
444  l56 = 0.
445  !
446  ! Get ω from q: q = ω^2 / g → ω = √(qg)
447  ! Odin has ignored √g here because it will be absorbed/vanish when we
448  ! calculate the kernel function T (V / ω). I, however, included √g here for
449  ! clarity.
450  !
451  ! ω for four free waves
452  om0 = sqrt(qr_grav * qfunc(k0x, k0y))
453  om1 = sqrt(qr_grav * qfunc(k1x, k1y))
454  om2 = sqrt(qr_grav * qfunc(k2x, k2y))
455  om3 = sqrt(qr_grav * qfunc(k3x, k3y))
456  !
457  ! ω for other combined waves
458  !
459  om02 = sqrt(qr_grav * qfunc(k0x-k2x, k0y-k2y))
460  om13 = sqrt(qr_grav * qfunc(k1x-k3x, k1y-k3y))
461  om12 = sqrt(qr_grav * qfunc(k1x-k2x, k1y-k2y))
462  om03 = sqrt(qr_grav * qfunc(k0x-k3x, k0y-k3y))
463  !
464  if (abs(k0x+k1x) > qr_eps .or. abs(k0y+k1y) > qr_eps) then
465  ! k₀ + k₁ = k₂ + k₃ = 0., ω_{0+1} = 0.,
466  ! V^{(-)}_{0+1, 0, 1} ~ 1/ω_{0+1} = NaN, L56 = NaN
467  om0p1 = sqrt(qr_grav * qfunc(k0x+k1x, k0y+k1y))
468  om2p3 = sqrt(qr_grav * qfunc(k2x+k3x, k2y+k3y))
469  end if
470  !
471  ! W^{(2)}_{1, 2, 3, 4} [Call U function here] for direct interaction
472  !
473  w = ufunc(-k0x, -k0y, -k1x, -k1y, k2x, k2y, k3x, k3y) + &
474  ufunc( k2x, k2y, k3x, k3y, -k0x, -k0y, -k1x, -k1y) - &
475  ufunc( k2x, k2y, -k1x, -k1y, -k0x, -k0y, k3x, k3y) - &
476  ufunc(-k0x, -k0y, k2x, k2y, -k1x, -k1y, k3x, k3y) - &
477  ufunc(-k0x, -k0y, k3x, k3y, k2x, k2y, -k1x, -k1y) - &
478  ufunc( k3x, k3y, -k1x, -k1y, k2x, k2y, -k0x, -k0y)
479  !
480  ! First & Fourth lines for virtual-state interaction in J09
481  !
482  l14 = vmfunc(k0x, k0y, k2x, k2y, k0x-k2x, k0y-k2y) * &
483  vmfunc(k3x, k3y, k1x, k1y, k3x-k1x, k3y-k1y) * &
484  (1.0/(om2 + om02 - om0) + 1.0/(om1 + om13 - om3)) + &
485  vmfunc(k1x, k1y, k3x, k3y, k1x-k3x, k1y-k3y) * &
486  vmfunc(k2x, k2y, k0x, k0y, k2x-k0x, k2y-k0y) * &
487  (1.0/(om3 + om13 - om1) + 1.0/(om0 + om02 - om2))
488  !
489  ! Second & Third lines for virtual-state interaction in J09
490  !
491  l23 = vmfunc(k1x, k1y, k2x, k2y, k1x-k2x, k1y-k2y) * &
492  vmfunc(k3x, k3y, k0x, k0y, k3x-k0x, k3y-k0y) * &
493  (1.0/(om2 + om12 - om1) + 1.0/(om0 + om03 - om3)) + &
494  vmfunc(k0x, k0y, k3x, k3y, k0x-k3x, k0y-k3y) * &
495  vmfunc(k2x, k2y, k1x, k1y, k2x-k1x, k2y-k1y) * &
496  (1.0/(om3 + om03 - om0) + 1.0/(om1 + om12 - om2))
497  !
498  ! Fifth & Sixth lines for virtual-state interaction in J09
499  !
500  if (abs(k0x+k1x) > qr_eps .or. abs(k0y+k1y) > qr_eps) then
501  ! k₁ + k₂ = k₃ + k₄ = 0., ω_{1+2} = 0.,
502  ! V^{(-)}_{1+2, 1, 2} ~ 1/ω_{1+2} = NaN, L56 = NaN
503  l56 = vmfunc(k0x+k1x, k0y+k1y, k0x, k0y, k1x, k1y) * &
504  vmfunc(k2x+k3x, k2y+k3y, k2x, k2y, k3x, k3y) * &
505  (1.0/(om0p1 - om0 - om1) + 1.0/(om2p3 - om2 - om3)) + &
506  vpfunc(-k0x-k1x, -k0y-k1y, k0x, k0y, k1x, k1y) * &
507  vpfunc(-k2x-k3x, -k2y-k3y, k2x, k2y, k3x, k3y) * &
508  (1.0/(om0p1 + om0 + om1) + 1.0/(om2p3 + om2 + om3))
509  end if
510  !
511  ! T_{1, 2, 3, 4}
512  !
513  tfunc = w - l14 - l23 - l56
514  !
515  return
516  !/
517  end function tfunc
518  !/
519  !/ ------------------------------------------------------------------- /
520  !/ [Part 2]
521  !/
522  subroutine findquartetnumber(ns, kx, ky, om, oml, nnz)
523  !/
524  !/ 19-Dec-2011 : Origination. ( O. Gramstad )
525  !/
526  !/ 09-Nov-2018 : Prepare WW3 distribution ( Q. Liu )
527  !/ 02-Apr-2020 : Boundary conditions (< kmin, > kmax)( Q. Liu )
528  !/
529  ! 1. Purpose:
530  ! Find the total number of quartets (resonant and quasi/near-resonant
531  ! four waves) satisfying the criteria below:
532  !
533  ! 1) \vec{k₁} + \vec{k₂} = \vec{k₃} + \vec{k₄}
534  !
535  ! 2) Δω = |ω₁ + ω₂ - ω₃ - ω₄| <= λc \min(ω₁, ω₂, ω₃, ω₄)
536  ! - that is, quartets far from the resonance is excluded for
537  ! saving the computational cost.
538  !
539  ! 3) For a given 2D frequency-direction grid (k_i, θ_j, i = 1, ...,
540  ! NK, j = 1, ..., NTH) consisting of NS points (NS = NK * NTH),
541  ! we will first reshape the 2D spectral grid (k_i, θ_j)
542  ! into a 1D wavenumber vector (k_{xl}, k_{yl}, l = 1, ..., NS).
543  ! Afterwards, we should have
544  ! 3.a) l₂ >= l₁
545  ! 3.b) l₃ ≠ l₁, l₃ ≠ l₂ (otherwise the third and fourth wave
546  ! components will be the same as the
547  ! first and second)
548  ! 3.c) l₄ >= l₃
549  ! 3.d) l₄ ≠ l₁, l₄ ≠ l₂
550  ! 3.e) k₄ >= k_{min}, k₄ <= k_{max}
551  !
552  ! Note that `l` here only denotes the index of a specific wave
553  ! component inside the 1D wavenumber vector array. For k₄, its
554  ! index l₄ is not exact, and is just approximated by the index
555  ! of its closest wave component.
556  !
557  ! 4) If we store the located quartets in a 2D large sparse matrix,
558  ! which can be organized as
559  ! |K K K |
560  ! |∩ ∩ ∩ |
561  ! |3 q l₃| 1 2 . . . N 1 2 . . . N . . . . . . 1 2 . . . N
562  ! |4 r l₄| 1 1 1 1 1 1 2 2 2 2 2 2 . . . . . . N N N N N N
563  ! |∪ ∪ ∪ |
564  ! -------
565  ! K {1 2}
566  ! K {n p}
567  ! K {l₁l₂}
568  ! -------
569  ! 1 1
570  ! 2 1 (2,1,N,3) ✗⁴ ✗²(2,1,3,N)
571  ! . 1 col > row → ▲
572  ! . 1
573  ! . 1
574  ! N 1
575  ! 1 2 (1,2,N,3) ✗³ [★ (1,2,3,N)]
576  ! 2 2
577  ! . 2
578  ! . 2 ⊚ ← row = l₁ + (l₂ - 1) * NS
579  ! . 2 ↑
580  ! N 2 col = l₃ + (l₄ - 1) * NS
581  ! . .
582  ! . .
583  ! . .
584  ! . .
585  ! . .
586  ! . . (N,3,2,1) ✓⁴ ✓³(N,3,1,2)
587  ! 1 N ▼ ← col < row
588  ! 2 N
589  ! . N (3,N,2,1) ✓² ☆ (3,N,1,2)
590  ! . N
591  ! . N
592  ! N N
593  !
594  ! where `N` shown above denotes `NS`, not `NK`, therefore the shape
595  ! of this large sparse matrix is (NS*NS, NS*NS).
596  !
597  ! Only quartets with col > row (highlighted by ▲ , i.e.,
598  ! ---------
599  ! elements in the upper trianglar matrix) are selected because,
600  ! for example, ★ (1, 2, 3, NS) & ☆ (3, NS, 1, 2) essentially
601  ! refer to the same quartet.
602  !
603  ! To sum up, criteria 1) and 2) are kinetic, whereas 3) and 4) are
604  ! enforced to avoid duplicating quartets since
605  ! ★ (k₁, k₂, k₃, k₄)
606  !
607  ! & [symmetric]
608  ! ✗² (k₂, k₁, k₃, k₄) ← filterd by 3.a)
609  ! ✗³ (k₁, k₂, k₄, k₃) ← filterd by 3.c)
610  ! ✗⁴ (k₂, k₁, k₄, k₃) ← filterd by 3.a) and 3.c)
611  !
612  ! & [antisymmetric]
613  ! ☆ (k₃, k₄, k₁, k₂) ← filterd by 4)
614  ! ✓² (k₃, k₄, k₂, k₁)
615  ! ✓³ (k₄, k₃, k₁, k₂)
616  ! ✓⁴ (k₄, k₃, k₂, k₁)
617  !
618  ! are essentially the same.
619  !
620  ! ◆ criteria 3.b) and 3.d) exclude two quartets:
621  ! / k₁ = k₃, k₂ = k₄ (k₁, k₂, k₁, k₂)
622  ! \ k₁ = k₄, k₂ = k₃ (k₁, k₂, k₂, k₁)
623  ! → singular points for the nonlinear transfer integral as
624  ! T_{1, 2, 1, 2} or T_{1, 2, 2, 1} ~ 1 / 0 = NaN
625  !
626  ! van Vledder (2006, p. 231) argued that the first quadruplet had
627  ! negligible contribution to the total transfer rate. Similarly,
628  ! for the symmetric reason, the contribution from the second
629  ! quadruplet is also very limited.
630  !
631  ! ◆ We should keep in mind that the Snl term for wave component
632  ! 3 in ★ (i.e., k₃) and in ☆ (i.e., k₁) are the same.
633  !
634  ! ◆ Although the other 7 quartets are not counted here, their
635  ! contributions to the nonlinear transfer rates should not be
636  ! ignored as the interval of the 6D integration starts from
637  ! -∞ and ends at +∞ !
638  !
639  ! More details can be found in Appendix of LGB21.
640  !
641  ! See also references:
642  ! Hasselmann and Hasselmann (1981) Exact-NL/DIA report
643  ! Hasselmann and Hasselmann (1985), JPO, 15, 1369 - 1377.
644  ! Annenkov and Shrira (2015), JPO, 45, 807-812
645  !
646  !/
647  implicit none
648  !
649  integer, intent(in) :: ns ! length of 1D wavenumber
650  ! vector, ns = nk * nth
651  real, intent(in) :: kx(ns), &
652  ky(ns), & ! (kx, ky) components
653  om(ns) ! ω or σ
654  real, intent(in) :: oml ! cut-off value λc for the
655  ! quasi-resonant criterion 2)
656  !
657  integer(kind=8), intent(out) &
658  :: nnz ! total number of quartets
659  ! i.e., nonzero values
660  ! in the large-sparse matrix
661  ! illustrated above
662  !
663  ! Local parameters
664  real :: k(ns) ! scalar/mag k
665  integer :: i1, i2, i3, i4, row, col
666  real :: k4x, k4y, k4, om4, kmin, kmax, dom
667  !/
668  ! Scalar wavenumber (i.e., magnitude)
669  k = sqrt(kx*kx + ky*ky)
670  kmin = minval(k)
671  kmax = maxval(k)
672  !
673  ! Boundary conditions: include k4 beyond kmin & kmax
674  if (qi_interp .eq. 1 .and. qi_bound .eq. 1) then
675  kmin = kmin / 9. ! 1/3 fmax
676  kmax = kmax * 9. ! 3 fmax
677  end if
678  !
679  ! Start to find the quartets: \vec{k_j}, j = 1, 2, 3 are chosen at the
680  ! grid points, and \vec_{k_4} is found by
681  ! \vec{k_4} = \vec{k_1} + \vec{k_2} - \vec{k_3}
682  !
683  nnz = 0
684  !
685  do i1 = 1, ns
686  ! criterion 3.a) ← starting from i1
687  do i2 = i1, ns
688  do i3 = 1, ns
689  ! criterion 3.b)
690  if (i3 .ne. i1 .and. i3 .ne. i2) then
691  ! criterion 1)
692  k4x = kx(i1) + kx(i2) - kx(i3)
693  k4y = ky(i1) + ky(i2) - ky(i3)
694  k4 = sqrt(k4x*k4x + k4y*k4y)
695  !
696  ! wavenumber k4 falls outside the grid (criterion 3.e)
697  if (k4 >= kmin .and. k4 <= kmax) then
698  ! ω = √(qg) & Δω
699  om4 = sqrt(qr_grav * qfunc(k4x, k4y))
700  dom = abs(om(i1) + om(i2) - om(i3) - om4) / &
701  min(om(i1), om(i2), om(i3), om4)
702  ! criterion 2)
703  if (oml <= qr_eps .or. dom <= oml) then
704  i4 = minloc((kx - k4x)*(kx - k4x) + &
705  (ky - k4y)*(ky - k4y), 1)
706  ! criterion 3.d)
707  if (i4 .ne. i1 .and. i4 .ne. i2) then
708  ! criterion 3.c)
709  if (i4 >= i3) then
710  row = i1 + ns * (i2-1)
711  col = i3 + ns * (i4-1)
712  ! criterion 4)
713  if (col > row) then
714  nnz = nnz + 1
715  end if
716  end if
717  end if
718  end if
719  end if
720  end if
721  end do
722  end do
723 #ifdef W3_TS
724  write(*, *) '→ nnz = ', nnz
725 #endif
726  end do
727  !/
728  end subroutine findquartetnumber
729  !/
730  !/ ------------------------------------------------------------------- /
731  !/
732  subroutine findquartetconfig(ns, kx, ky, om, oml, nnz, &
733  NN, PP, QQ, RR, &
734  k4x, k4y, om4)
735  !/
736  !/ 19-Dec-2011 : Origination. ( O. Gramstad )
737  !/
738  !/ 09-Nov-2018 : Prepare WW3 distribution ( Q. Liu )
739  !/ 02-Apr-2020 : Boundary conditions (< kmin, > kmax)( Q. Liu )
740  !/
741  ! 1. Purpose:
742  ! Find all the quartets that we are interested in. Initially I thought
743  ! we may merge this subroutine and the subroutine above (i.e.,
744  ! FindQuartetNumber) in such a way that we first initialize a large
745  ! array like Quartet(HNum), where HNum is a huge integer (something
746  ! like 0.5*NS**4). But it quickly turned out this was a very naive
747  ! idea because for the wavenumber grid (k, θ) used by 3G spectral
748  ! wave models, in general NS~O(10^2-3), then NS^4~O(10^8-12). Thus,
749  ! HNum becomes really very very very huge, and then we may have
750  ! the integer/memory overflow problem.
751  !
752  ! Based on the above-mentioned, we must split the whole process:
753  ! 1) find the total number of quartets with FindQuartetNumber, `nnz`
754  ! 2) allocate arrays with the known `nnz`, and store the wavenumber
755  ! and ω for k₄
756  !
757  ! For more details, see the header of the subr. FindQuartetNumber.
758  !
759  !/
760  implicit none
761  !
762  integer, intent(in) :: ns ! length of 1D wavenumber
763  ! vector, ns = nk * nth
764  real, intent(in) :: kx(ns), &
765  ky(ns), & ! (kx, ky) components
766  om(ns) ! ω or σ
767  real, intent(in) :: oml ! cut-off value λc for the
768  ! quasi-resonant criterion 2)
769  integer(kind=8), intent(in) &
770  :: nnz ! total number of quartets
771  ! returned from the subr.
772  ! FindQuartetNumber
773  !
774  integer, intent(out) :: nn(nnz), & ! index of k₁
775  pp(nnz), & ! k₂
776  qq(nnz), & ! k₃
777  rr(nnz) ! k₄ in the 1D
778  ! wavenumber vector [1 - NS]
779  real, intent(out) :: k4x(nnz), &
780  k4y(nnz), & ! x, y comp. of k₄
781  om4(nnz) ! ω₄
782  !
783  ! Local parameters
784  real :: k(ns) ! scalar/mag k
785  integer :: i1, i2, i3, i4, row, col, s
786  real :: k4xt, k4yt, k4t, om4t, kmin, kmax, dom
787  !/
788  ! Scalar wavenumber (i.e., magnitude)
789  k = sqrt(kx*kx + ky*ky)
790  kmin = minval(k)
791  kmax = maxval(k)
792  !
793  ! Boundary conditions: include k4 beyond kmin & kmax
794  if (qi_interp .eq. 1 .and. qi_bound .eq. 1) then
795  kmin = kmin / 9. ! 1/3 fmax
796  kmax = kmax * 9. ! 3 fmax
797  end if
798  !
799  ! Start to find the quartets: \vec{k_j}, j = 1, 2, 3 are chosen at the
800  ! grid points, and \vec_{k_4} is found by
801  ! \vec{k_4} = \vec{k_1} + \vec{k_2} - \vec{k_3}
802  !
803  ! s: count of quartets. This time the total number of quartets `nnz` is
804  ! already known from `FindQuartetNumber`.
805  ! nnz = 0
806  s = 0
807  !
808  do i1 = 1, ns
809  ! criterion 3.a) ← starting from i1
810  do i2 = i1, ns
811  do i3 = 1, ns
812  ! criterion 3.b)
813  if (i3 .ne. i1 .and. i3 .ne. i2) then
814  ! criterion 1)
815  k4xt = kx(i1) + kx(i2) - kx(i3)
816  k4yt = ky(i1) + ky(i2) - ky(i3)
817  k4t = sqrt(k4xt*k4xt + k4yt*k4yt)
818  !
819  ! wavenumber k4 falls outside the grid (criterion 3.e)
820  if (k4t >= kmin .and. k4t <= kmax) then
821  ! ω = √qg & Δω
822  om4t = sqrt(qr_grav * qfunc(k4xt, k4yt))
823  dom = abs(om(i1) + om(i2) - om(i3) - om4t)/&
824  min(om(i1), om(i2), om(i3), om4t)
825  ! criterion 2)
826  if (oml <= qr_eps .or. dom <= oml) then
827  i4 = minloc((kx - k4xt)*(kx - k4xt) + &
828  (ky - k4yt)*(ky - k4yt), 1)
829  ! criterion 3.d)
830  if (i4 .ne. i1 .and. i4 .ne. i2) then
831  ! criterion 3.c)
832  if (i4 >= i3) then
833  row = i1 + ns * (i2-1)
834  col = i3 + ns * (i4-1)
835  ! criterion 4)
836  if (col > row) then
837  ! nnz = nnz + 1
838  s = s + 1 ! Find 1 quartet
839  !
840  nn(s) = i1 ! Store index
841  pp(s) = i2
842  qq(s) = i3
843  rr(s) = i4
844  !
845  k4x(s) = k4xt ! k₄, ω₄
846  k4y(s) = k4yt
847  om4(s) = om4t
848  !
849  end if
850  end if
851  end if
852  end if
853  end if
854  end if
855  end do
856  end do
857  end do
858  !
859  ! Check consistency of s and nnz
860  if (s .ne. nnz) then
861  write(*, 1001) 'FindQuartetConfig'
862  call exit(1)
863  end if
864  !
865  ! Formats
866 1001 FORMAT(/' *** GKE ERROR IN gkeModule : '/ &
867  ' Subr. ', a, ': The number of Quartet Configs. does not match NNZ!'/)
868  !/
869  end subroutine findquartetconfig
870  !/
871  !/ ------------------------------------------------------------------- /
872  !/ [Part 3]
873  !/
874  subroutine coocsrind (nrow, nnz, ir, jc, ind_translate, iao)
875  !/
876  !/ 12-Sep-2012 : Origination. ( version 3.14 )
877  !/ Based on coocsr of SPARKIT ( O. Gramstad )
878  !/
879  !/ 16-Nov-2018 : Prepare WW3 distribution ( Q. Liu )
880  !/
881  ! 1. Purpose:
882  ! It becomes clear from subr. FindQuartetNumber & FindQuartetConfig
883  ! that we are faced with a problem of large sparse matrice when we
884  ! manipulate the huge set of quartets. By sparse matrix we mean
885  ! only a `relatively small number` of its matrix elements are nonzero.
886  !
887  ! For saving time or memory space, a sparse matrix is usually stored
888  ! in some compressed formats in the computer memory. Two among those
889  ! formats, COO & CSR are relevant here in our application:
890  ! 1) The coordinate format (COO) --- the simplest storage scheme
891  ! For a given sparse matrix `A` (N, N) with NNZ nonzero elements,
892  ! the COO format consists of 3 arrays:
893  ! * a (nnz): real nonzero values of A in `any order`
894  ! * ir(nnz): row indices of these nonzero values
895  ! * jc(nnz): column indices
896  !
897  ! 2) The Compressed Sparse Row format (CSR)
898  ! The CSR format is the basic format used in SPARSKIT, consisting
899  ! of three arrays as well
900  ! * a (nnz): real nonzero values of A stored row by row from row
901  ! 1 to row N
902  ! * jc(nnz): column indices in `any order`
903  ! * ia(N+1): the index of the first nonzero element at this
904  ! corresponding row in the array a and jc, that is
905  ! ia(i) provides the position in a & jc where the i-th
906  ! row starts.
907  !
908  ! This subroutine converts the sparse matrix (nrow, nrow) in the COO
909  ! format, as represented by (ir, jc) to the CSR format, as represented
910  ! by (ind_translate, iao).
911  !
912  ! N.B.:
913  ! This subr. neither needs the real value array in the COO format,
914  ! nor returns the real value array in the CSR format. Alternatively,
915  ! it returns the tranformed index (ind_translate) from COO to CSR.
916  ! With such indices, we have
917  ! *) a_csr = a_coo(ind_translate)
918  ! *) jc_csr = jc_coo(ind_translate)
919  !
920  ! References:
921  ! Youcef Saad, 1994, SPARSKIT: a basic tool kit for sparse matrix
922  ! compuation (version 2, `coocsr` therein)
923  ! See also Numerical Recipe in Fortran (ch. 2.7, p. 71)
924  !/
925  implicit none
926  !
927  integer, intent(in) :: nrow ! # of rows of sparse matrix
928  integer(kind=8), intent(in) &
929  :: nnz ! # of nonzero elements
930  integer, intent(in) :: ir(nnz) ! COO row
931  integer, intent(in) :: jc(nnz) ! COO col
932  integer, intent(out) :: ind_translate(nnz) ! indices from COO to CSR
933  integer, intent(out) :: iao(nrow+1) ! CSR iao
934  !
935  ! Local parameters
936  integer :: i, j, k, k0, iad
937  !/
938  ! Determine the number of non-zeros in each row (iao(i), i = 1, ..., nrow,
939  ! will be the # of nonzero elements at the i-th row), whereas
940  ! iao(nrow+1) = 0
941  iao(1:nrow+1) = 0
942  do k = 1, nnz
943  iao(ir(k)) = iao(ir(k)) + 1 ! row by row
944  end do
945  !
946  ! Find the positions that correspond to the first value in each row.
947  ! Now iao(i) is the position where the i-th row starts, and
948  ! iao(nrow+1) = 1 + nnz
949  k = 1
950  do j = 1, nrow+1
951  k0 = iao(j) ! num_i, # of nonzero in this row
952  iao(j) = k ! starting pos
953  k = k + k0 ! k = Σnum_i, where i <= j
954  end do
955  !
956  ! Go through the structure once more. Fill in ind_translate
957  do k = 1, nnz
958  i = ir(k) ! coo row
959  j = jc(k) ! coo col
960  !
961  ! When i-th row is encountered by the first time, iad = iao(i) denotes
962  ! the starting position for this row. Afterwards, iao(i) is added by 1
963  ! when i-th row arrives every time. In the end, iao(i) records the
964  ! starting position for the (i+1)-th row. However, the last element of
965  ! iao remains unchanged, i.e., iao(nrow+1) = iao(nrow) = 1 + nnz
966  iad = iao(i)
967  ind_translate(iad) = k
968  iao(i) = iad + 1
969  end do
970  !
971  ! Shift back IAO.
972  do j = nrow, 1, -1
973  iao(j+1) = iao(j)
974  end do
975  iao(1) = 1
976  !
977  return
978  !/
979  end subroutine coocsrind
980  !/
981  !/ ------------------------------------------------------------------- /
982  !/
983  subroutine asymsmattimvec (n, a, ja, ia, x, y, Symb)
984  !/
985  !/ 07-Sep-2012 : Origination. ( version 3.14 )
986  !/ Based on amux & atmux of SPARKIT ( O. Gramstad )
987  !/
988  !/ 16-Nov-2018 : Prepare WW3 distribution ( Q. Liu )
989  !/ 19-Feb-2018 : Add `Symb` keyword ( Q. Liu )
990  !/
991  ! 1. Purpose:
992  ! --------> Symb = -1 (antisymmetric)
993  ! Calculate the dot product of an antisymmetric CSR sparse matrix
994  ! and a vector X.
995  !
996  ! An antisymmetric (skew-symmetric) matrix is a square matrix `B`
997  ! whose transpose equals to its negative, i.e.,
998  ! B^T = -B
999  !
1000  ! ◆ Do not be confused by the name of this subr. The coming-in CSR
1001  ! sparse matrix `a` is not symmetric or antisymmetric. In our case,
1002  ! `a` is a upper triangular sparse matrix, and we are acturally
1003  ! calculating the dot product of `a - a^T` and `x`, where
1004  ! 'a - a^T' is an antisymmetric matrix due to the symmetry of
1005  ! four-wave nonlinear interactions (dN₁/dt = -dN₃/dt).
1006  !
1007  ! This operation is in essence the dot product of two common dense
1008  ! matrix/vector, such as
1009  ! M(n, 1) = A(n, n) * X(n, 1)
1010  ! or
1011  ! M_{i, 1} = Σa(i, j) * x(j, 1)
1012  !
1013  ! For the transposed array A^T,
1014  ! N_{i, 1} = Σat(i, j) * x(j, 1)
1015  ! = Σ a(j, i) * x(j, 1)
1016  ! Alternatively, we can exchange the index of i, j for easy
1017  ! understanding:
1018  ! N_{j, 1} = Σat(j, i) * x(i, 1)
1019  ! = Σ a(i, j) * x(i, 1)
1020  !
1021  ! Finally, Y = M - N = A * X - A^T * X
1022  !
1023  ! --------> Symb = 1 (Symmetric)
1024  ! Same as above but for Y = M + N = A * X + A^T * X
1025  !/
1026  implicit none
1027  !
1028  integer, intent(in) :: n ! # of rows/cols
1029  !
1030  real, intent(in) :: a(:) ! CSR a (nnz)
1031  integer, intent(in) :: ja(:) ! CSR ja(nnz)
1032  integer, intent(in) :: ia(n+1) ! CSR ia(n+1)
1033  !
1034  real, intent(in) :: x(n) ! vector of the same length
1035  real, intent(out) :: y(n) ! return product y = B * x
1036  real, intent(in) :: symb ! -1 for minus, 1 for plus
1037  !
1038  ! Local parameters
1039  integer :: i, k
1040  real :: t
1041  !/
1042  ! Initilization
1043  y(1:n) = 0.0
1044  !
1045  do i = 1, n
1046  t = 0.0
1047  do k = ia(i), ia(i+1)-1
1048  !
1049  ! M_{i, 1} = Σa(i, j) * x(j, 1)
1050  t = t + a(k) * x(ja(k))
1051  !
1052  !±N_{j, 1} = ±Σa(i, j) * x(i, 1)
1053  y(ja(k)) = y(ja(k)) + symb * a(k)*x(i)
1054  end do
1055  y(i) = y(i) + t
1056  end do
1057  ! The final Y = M ± N = A * x ± A^T * x
1058  !
1059  return
1060  !/
1061  end subroutine asymsmattimvec
1062  !/
1063  !/ ------------------------------------------------------------------- /
1064  !/ Part 4
1065  !/
1066  subroutine prepkgrid(nk, nth, dpt, sig, th)
1067  !/
1068  !/ 04-Dec-2018 : Origination. ( Q. Liu )
1069  !/ 04-Dec-2018 : Based on `z_cmpcg` & `z_wnumb` of serv_xnl4v5.f90
1070  !/ ( Q. Liu )
1071  !/ 01-Apr-2019 : Add the option using WAVNU1 ( Q. Liu )
1072  !/
1073  ! 1. Purpose:
1074  ! Compute wave number k for a given discrete frequency grid and water
1075  ! depth based on the dispersion relation for the linear wave theory
1076  ! ω^2 = gk tanh(kd)
1077  !
1078  ! ◆ In WW3, the radian frequency grid ω is invariant.
1079  !
1080  ! ◆ It is desired that the GKE module should be independent from WW3
1081  ! as much as possible. So I decided not to directly obtain
1082  ! `NK, NTH, SIG, WN, CG, DSII` from WW3
1083  !
1084  ! 2. Method
1085  ! ✓ dispopt = 0
1086  ! Finite depth linear dispersion relation, using a Pade approximation
1087  ! (Hunt, 1988) [see WRT serv_xnl4v5.f90]
1088  !
1089  ! ✓ dispopt = 1 for WAVNU1
1090  !/
1091  USE w3dispmd, ONLY: wavnu1
1092  !
1093  implicit none
1094  !
1095  integer, intent(in) :: nk ! # of frequencies
1096  integer, intent(in) :: nth ! # of directions
1097  real, intent(in) :: dpt ! water depth (m)
1098  real, intent(in) :: sig(nk) ! radian frequency σ
1099  real, intent(in) :: th(nth) ! θ (rad) [equally spaced,
1100  ! but may start from non-zero
1101  ! value]
1102  !
1103  integer, parameter :: dispopt = 1 ! dispersion relation
1104  !
1105  integer :: ik, ith, jkth, ns
1106  real :: x, xx, y, omega
1107  real :: k, cg, dsii, angr, dth
1108  real :: esin(nth), ecos(nth)
1109  !/
1110  ! Initialization
1111  ns = nk * nth
1112  ! Allocation of qr_kx/ky/dk/om/wn1 was done in PrepKernelIO)
1113  qr_kx = 0. ! ns
1114  qr_ky = 0.
1115  qr_dk = 0.
1116  qr_om = 0.
1117  qr_wn1 = 0. ! nk
1118  !
1119  ! Calc Δθ, cosθ, sinθ [θ is equally spaced]
1120  dth = qr_tpi / real(nth)
1121  !
1122  do ith = 1, nth
1123  angr = th(ith)
1124  esin(ith) = sin(angr)
1125  ecos(ith) = cos(angr)
1126  !
1127  if (abs(esin(ith)) .lt. 1.e-5) then
1128  esin(ith) = 0.
1129  if (ecos(ith) .gt. 0.5) then
1130  ecos(ith) = 1. ! θ = 0.
1131  else
1132  ecos(ith) = -1. ! θ = π
1133  end if
1134  end if
1135  !
1136  if (abs(ecos(ith)) .lt. 1.e-5) then
1137  ecos(ith) = 0.
1138  if (esin(ith) .gt. 0.5) then
1139  esin(ith) = 1. ! θ = π/2
1140  else
1141  esin(ith) = -1. ! θ = π * 3/2
1142  end if
1143  end if
1144  end do
1145  !
1146  do ik = 1, nk
1147  if (dispopt .eq. 0) then
1148  ! Calc k & Cg (`z_cmpcg` & `z_wnumb` of serv_xnl4v5.f90)
1149  omega = sig(ik)**2.0/qr_grav
1150  y = omega*dpt
1151  xx = y*(y+1.0/(1.0+y*(0.66667+y*(0.35550+y*(0.16084+y*(0.06320+y* &
1152  (0.02174+y*(0.00654+y*(0.00171+y*(0.00039+y*0.00011))))))))))
1153  x = sqrt(xx)
1154  k = x/dpt
1155  !
1156  if(dpt*k > 30.0) then
1157  cg = qr_grav/(2.0*sig(ik))
1158  else
1159  cg = sig(ik)/k*(0.5+dpt*k/sinh(2.0*dpt*k))
1160  end if
1161  !
1162  else if (dispopt .eq. 1) then
1163  ! Calc k & cg (WAVNU1 from WW3)
1164  call wavnu1(sig(ik), dpt, k, cg)
1165  end if
1166  qr_wn1(ik) = k ! Store k in qr_wn1 ('ll used for interp.)
1167 #ifdef W3_TS
1168  write(*, *) 'σ, k, cg: ', sig(ik), k, cg
1169 #endif
1170  ! Calc Δσ
1171  if (ik .eq. 1) then
1172  dsii = 0.5 * (sig(2) - sig(1)) ! first bin
1173  else if (ik .eq. nk) then
1174  dsii = 0.5 * (sig(nk) - sig(nk-1)) ! last bin
1175  else
1176  dsii = 0.5 * (sig(ik+1) - sig(ik-1)) ! interm. bin
1177  end if
1178  ! Calc Kx, Ky
1179  do ith = 1, nth
1180  jkth = ith + (ik - 1) * nth
1181  qr_kx(jkth) = k * ecos(ith)
1182  qr_ky(jkth) = k * esin(ith)
1183  ! Calc Δ\vec{k} = k Δk Δθ = k Δσ/cg Δθ
1184  qr_dk(jkth) = k * dsii / cg * dth
1185  qr_om(jkth) = sig(ik)
1186  end do
1187  end do
1188 #ifdef W3_TS
1189  write(*, *) 'qr_kx: ', qr_kx
1190  write(*, *) 'qr_ky: ', qr_ky
1191  write(*, *) 'qr_dk: ', qr_dk
1192  write(*, *) 'qr_om: ', qr_om
1193 #endif
1194  !
1195  return
1196  !/
1197  end subroutine prepkgrid
1198  !/
1199  !/ ------------------------------------------------------------------- /
1200  !/
1201  subroutine prepkernelio(nk, nth, sig, th, act)
1202  !/
1203  !/ 04-Dec-2018 : Origination ( Q. Liu )
1204  !/ 04-Dec-2018 : Extracted from Odin's subr. `calcQRSNL`
1205  !/ ( Q. Liu )
1206  !/
1207  ! 1. Purpose:
1208  ! Read & Write the pre-computed kernel coefficients `T` for a given
1209  ! discrete wavenumber grid and water depth.
1210  !
1211  ! For a typical 2D finite-depth wave model application, the wavenumber
1212  ! grid varies according to water depth. Consequently, the quartet
1213  ! configuration and interactive kernel coefficients will change as
1214  ! well.
1215  !
1216  ! Therefore, it seems extremely difficult to handle a 2D varied-depth
1217  ! application as the total number of quartets (qi_nnz) and thus the
1218  ! array size of `Inpqr0` vary [see CalcQRSNL]. Initializing a 2D
1219  ! array `Inpqr0` with a fixed size of (qi_nnz, nsea) becomes impossible.
1220  !
1221  ! So currently we are limiting ourself to deep-water or constant
1222  ! finite-deep cases.
1223  !
1224  !/
1225  USE constants, ONLY: file_endian
1226 
1227  implicit none
1228  !
1229  integer, intent(in) :: nk ! # of frequencies
1230  integer, intent(in) :: nth ! # of directions
1231  real, intent(in) :: sig(nk) ! radian frequency (rad)
1232  real, intent(in) :: th(nth) ! θ (rad) [equally spaced,
1233  ! but may start from non-zero
1234  ! value]
1235  character(len=*), intent(in) :: act ! 'read' or 'write'
1236  !
1237  ! Local parameters
1238  integer :: ns, iq, i1, i3, icol
1239  integer(kind=8) :: rpos ! reading position
1240  integer, allocatable :: irow_coo(:), & ! row of coo mat
1241  icootcsr(:) ! index for coo → csr
1242  !/
1243  ! Initilization
1244  ns = nk * nth
1245  qi_nrsm = ns * ns
1246  ! → Be very careful that the size of `qi_irCsr` is not qi_nnz !
1247  if (allocated(qi_ircsr)) deallocate(qi_ircsr); allocate(qi_ircsr(qi_nrsm+1))
1248  if (allocated(qr_sumqr)) deallocate(qr_sumqr); allocate(qr_sumqr(qi_nrsm))
1249  if (allocated(qr_sumnp)) deallocate(qr_sumnp); allocate(qr_sumnp(ns, ns))
1250  ! qr_dk/om
1251  if (allocated(qr_dk)) deallocate(qr_dk); allocate(qr_dk(ns))
1252  if (allocated(qr_om)) deallocate(qr_om); allocate(qr_om(ns))
1253  !
1254  ! Determine water depth for the whole module, which will be used by
1255  ! `T` & `Q` func.
1256  qr_depth = max(0., min(qr_depth, qr_dmax))
1257  qi_disc = max(0, min(qi_disc, 1))
1258  qi_kev = max(0, min(qi_kev, 1))
1259  qi_interp = max(0, min(qi_interp, 1))
1260  if (qi_disc .eq. 1) qi_interp = 0
1261  !
1262  ! Determine the name for the binary file which stores the quartet
1263  ! configuration and the corresponding kernel coefficient ['gkev?_d????.cfg]
1264  ! constant-depth or deep water
1265  write(qs_cfg, "(A, '_d', I4.4, '.cfg')") trim(qs_ver), int(qr_depth)
1266  !
1267  if (trim(act) == 'WRITE') then
1268  ! Calc KGrid → [qr_kx/ky/dk/om/wn]
1269  if (allocated(qr_kx)) deallocate(qr_kx); allocate(qr_kx(ns))
1270  if (allocated(qr_ky)) deallocate(qr_ky); allocate(qr_ky(ns))
1271  if (allocated(qr_wn1)) deallocate(qr_wn1); allocate(qr_wn1(nk))
1272  call prepkgrid(nk, nth, qr_depth, sig, th)
1273  ! Find total # of quartets → [qi_nnz]
1274  call findquartetnumber(ns, qr_kx, qr_ky, qr_om, qr_oml, qi_nnz)
1275  ! Find Quartet Config. → [qi_NN/PP/QQ/RR & qr_k4x/k4y/om4]
1276  if (allocated(qi_nn)) deallocate(qi_nn); allocate(qi_nn(qi_nnz))
1277  if (allocated(qi_pp)) deallocate(qi_pp); allocate(qi_pp(qi_nnz))
1278  if (allocated(qi_qq)) deallocate(qi_qq); allocate(qi_qq(qi_nnz))
1279  if (allocated(qi_rr)) deallocate(qi_rr); allocate(qi_rr(qi_nnz))
1280  !
1281  if (allocated(qr_k4x)) deallocate(qr_k4x); allocate(qr_k4x(qi_nnz))
1282  if (allocated(qr_k4y)) deallocate(qr_k4y); allocate(qr_k4y(qi_nnz))
1283  if (allocated(qr_om4)) deallocate(qr_om4); allocate(qr_om4(qi_nnz))
1284  !
1285  call findquartetconfig(ns, qr_kx, qr_ky, qr_om, qr_oml, qi_nnz, &
1286  qi_nn, qi_pp, qi_qq, qi_rr, &
1287  qr_k4x, qr_k4y, qr_om4)
1288  !
1289  ! Calc Kernel `T`
1290  if (allocated(qr_tkern)) deallocate(qr_tkern); allocate(qr_tkern(qi_nnz))
1291  if (allocated(qr_tkurt)) deallocate(qr_tkurt); allocate(qr_tkurt(qi_nnz))
1292  if (allocated(qr_dom)) deallocate(qr_dom); allocate(qr_dom(qi_nnz))
1293  !
1294  do iq = 1, qi_nnz
1295  qr_tkern(iq) = tfunc(qr_kx(qi_nn(iq)), qr_ky(qi_nn(iq)),&
1296  qr_kx(qi_pp(iq)), qr_ky(qi_pp(iq)),&
1297  qr_kx(qi_qq(iq)), qr_ky(qi_qq(iq)),&
1298  qr_k4x(iq) , qr_k4y(iq) )
1299  end do
1300  ! Calc Kernel coeff. for Kurtosis
1301  qr_tkurt = qr_tkern * sqrt(qr_om(qi_nn) * qr_om(qi_pp) * qr_om(qi_qq) * qr_om4)
1302  ! Calc Δω (Remove very small Δω; Δω=0 → resonant quartets)
1303  qr_dom = qr_om(qi_nn) + qr_om(qi_pp) - qr_om(qi_qq) - qr_om4
1304  ! TODO: should we use double precision for qr_dom
1305  ! Note for GNU compiler, qr_eps~1.2E-7 (single prec.) & ~2.2E-16 (double).
1306  ! The values above are also true for the intel compiler.
1307  ! sin(Δωt) / Δω is very different for Δω = 0 and Δw~1E-7 when t is large.
1308  where(abs(qr_dom) < qr_eps) qr_dom = 0.0
1309  !
1310  ! Calc interp. weight if necessary
1311  if (qi_interp .eq. 1) then
1312  if (allocated(qi_bind)) deallocate(qi_bind); allocate(qi_bind(4, qi_nnz))
1313  if (allocated(qr_bwgh)) deallocate(qr_bwgh); allocate(qr_bwgh(4, qi_nnz))
1314  if (qi_bound .eq. 1 ) then
1315  if (allocated(qr_bdry)) deallocate(qr_bdry); allocate(qr_bdry(qi_nnz))
1316  end if
1317  call biinterpwt(nk, nth, qr_wn1, th)
1318  end if
1319  !
1320  deallocate(qr_kx, qr_ky)
1321  deallocate(qr_k4x, qr_k4y, qr_om4)
1322  if (qi_interp .eq. 1) deallocate(qr_wn1)
1323  !
1324  ! Sparse matrix index conversion [icCos shared by two formats: COO & CSR]
1325  if (allocated(qi_iccos)) deallocate(qi_iccos); allocate(qi_iccos(qi_nnz))
1326  if (allocated(irow_coo)) deallocate(irow_coo); allocate(irow_coo(qi_nnz))
1327  if (allocated(icootcsr)) deallocate(icootcsr); allocate(icootcsr(qi_nnz))
1328  !
1329  irow_coo = qi_nn + (qi_pp - 1) * ns
1330  qi_iccos = qi_qq + (qi_rr - 1) * ns
1331  !
1332  ! FindQuartetConfig stores the quartet row by row in a discontinuous order,
1333  ! so we need keep icooTcsr & qi_irCsr
1334  call coocsrind(qi_nrsm, qi_nnz, irow_coo, qi_iccos, icootcsr, qi_ircsr)
1335  !
1336  ! Reorder index & arrays [coo → crs]
1337  qi_nn = qi_nn(icootcsr) ! used for calc. action prod.
1338  qi_pp = qi_pp(icootcsr)
1339  qi_qq = qi_qq(icootcsr)
1340  qi_rr = qi_rr(icootcsr)
1341  qr_tkern = qr_tkern(icootcsr)
1342  qr_tkurt = qr_tkurt(icootcsr)
1343  qr_dom = qr_dom(icootcsr) ! Δω
1344  !
1345  if (qi_interp .eq. 1) then ! bilinear interp. weight
1346  qi_bind = qi_bind(:, icootcsr)
1347  qr_bwgh = qr_bwgh(:, icootcsr)
1348  if (qi_bound .eq. 1) qr_bdry = qr_bdry(icootcsr)
1349  end if
1350  !
1351  qi_iccos = qi_iccos(icootcsr)
1352  deallocate(irow_coo, icootcsr)
1353  !
1354  ! Construct the sum vectors [used for 6D integration]
1355  ! Σ over Q, R [qr_sumQR]
1356  qr_sumqr = 2.0
1357  do i3 = 1, ns
1358  ! i3 == i4
1359  icol = i3 + (i3 - 1) * ns
1360  qr_sumqr(icol) = 1.0
1361  end do
1362  ! Σ over P [qr_sumNP]
1363  qr_sumnp = 1.0
1364  do i1 = 1, ns
1365  ! i1 == i2
1366  qr_sumnp(i1, i1) = 0.5
1367  end do
1368  !
1369  ! WRITE KGrid & Kernel into qs_cfg
1370  write(*, *) '[W] Writing |', trim(qs_cfg), '| ...'
1371  open(51, file=trim(qs_cfg), form='unformatted', convert=file_endian, &
1372  access='stream', status='replace')
1373  !
1374  ! It is not necessary to store `ns` since `ns = nk * nth`
1375  write(51) nk, nth, sig, th ! (f, θ) grid
1376  write(51) qr_depth, qr_oml, qi_disc, &
1377  qi_kev, qi_interp ! parameters
1378  write(51) qr_om, qr_dk
1379  write(51) qi_nnz
1380  write(51) qi_nn, qi_pp, qi_qq, qi_rr
1381  write(51) qr_tkern, qr_tkurt, qr_dom
1382  write(51) qi_iccos, qi_ircsr
1383  write(51) qr_sumqr, qr_sumnp
1384  !
1385  if (qi_interp .eq. 1) write(51) qi_bind, qr_bwgh
1386  if ( (qi_interp .eq. 1) .and. (qi_bound .eq. 1) ) &
1387  write(51) qr_bdry
1388  close(51)
1389  ! Screen Test
1390 #ifdef W3_TS
1391  write(*, *) "[W] qr_depth: ", qr_depth
1392  write(*, *) "[W] qr_oml : ", qr_oml
1393  write(*, *) "[W] qi_disc : ", qi_disc
1394  write(*, *) "[W] qi_kev : ", qi_kev
1395  write(*, *) "[W] qr_om : ", qr_om
1396  write(*, *) "[W] qr_dk : ", qr_dk
1397  write(*, *) "[W] The total number of quartets is ", qi_nnz
1398  write(*, *) '[W] qi_NN : ', qi_nn
1399  write(*, *) '[W] qi_PP : ', qi_pp
1400  write(*, *) '[W] qi_QQ : ', qi_qq
1401  write(*, *) '[W] qi_RR : ', qi_rr
1402  write(*, *) '[W] qr_TKern: ', qr_tkern
1403  write(*, *) '[W] qr_TKurt: ', qr_tkurt
1404  write(*, *) '[W] qr_dom : ', qr_dom
1405  write(*, *) '[W] qi_icCos: ', qi_iccos
1406  write(*, *) '[W] qi_irCsr: ', qi_ircsr
1407  write(*, *) '[W] Σ_QR : ', qr_sumqr(1: qi_nrsm: ns+1)
1408  write(*, *) '[W] Σ_P : ', (qr_sumnp(iq, iq), iq = 1, ns)
1409 #endif
1410  !
1411  else if (trim(act) == 'READ') then
1412  write(*, *) '⊚ → [R] Reading |', trim(qs_cfg), '| ...'
1413  open(51, file=trim(qs_cfg), form='unformatted', convert=file_endian, &
1414  access='stream', status='old')
1415  ! nk, nth, sig, th can be skipped by using pos
1416  rpos = 1_8 + qi_lrb * (2_8 + nk + nth)
1417  read(51, pos=rpos) qr_depth, qr_oml, qi_disc, &
1418  qi_kev, qi_interp
1419  !
1420  ! read ω & Δ\vec{k}
1421  read(51) qr_om, qr_dk
1422  ! read total # of quartets
1423  read(51) qi_nnz
1424  write(*, *) "⊚ → [R] The total number of quartets is ", qi_nnz
1425  write(*, *)
1426  ! allocate arrays
1427  if (allocated(qi_nn)) deallocate(qi_nn); allocate(qi_nn(qi_nnz))
1428  if (allocated(qi_pp)) deallocate(qi_pp); allocate(qi_pp(qi_nnz))
1429  if (allocated(qi_qq)) deallocate(qi_qq); allocate(qi_qq(qi_nnz))
1430  if (allocated(qi_rr)) deallocate(qi_rr); allocate(qi_rr(qi_nnz))
1431  !
1432  if (allocated(qr_tkern)) deallocate(qr_tkern); allocate(qr_tkern(qi_nnz))
1433  if (allocated(qr_tkurt)) deallocate(qr_tkurt); allocate(qr_tkurt(qi_nnz))
1434  if (allocated(qr_dom)) deallocate(qr_dom); allocate(qr_dom(qi_nnz))
1435  !
1436  if (allocated(qi_iccos)) deallocate(qi_iccos); allocate(qi_iccos(qi_nnz))
1437  !
1438  read(51) qi_nn, qi_pp, qi_qq, qi_rr
1439  read(51) qr_tkern, qr_tkurt, qr_dom
1440  read(51) qi_iccos, qi_ircsr
1441  read(51) qr_sumqr, qr_sumnp
1442  !
1443  if (qi_interp .eq. 1) then
1444  if (allocated(qi_bind)) deallocate(qi_bind); allocate(qi_bind(4, qi_nnz))
1445  if (allocated(qr_bwgh)) deallocate(qr_bwgh); allocate(qr_bwgh(4, qi_nnz))
1446  read(51) qi_bind, qr_bwgh
1447  !
1448  if (qi_bound .eq. 1) then
1449  if (allocated(qr_bdry)) deallocate(qr_bdry); allocate(qr_bdry(qi_nnz))
1450  read(51) qr_bdry
1451  end if
1452  !
1453  end if
1454  !
1455  close(51)
1456  ! Screen Test
1457 #ifdef W3_TS
1458  write(*, *) "[R] qr_depth: ", qr_depth
1459  write(*, *) "[R] qr_oml : ", qr_oml
1460  write(*, *) "[R] qi_disc : ", qi_disc
1461  write(*, *) "[R] qi_kev : ", qi_kev
1462  write(*, *) "[R] qr_om : ", qr_om
1463  write(*, *) "[R] qr_dk : ", qr_dk
1464  write(*, *) "[R] The total number of quartets is ", qi_nnz
1465  write(*, *) '[R] qi_NN : ', qi_nn
1466  write(*, *) '[R] qi_PP : ', qi_pp
1467  write(*, *) '[R] qi_QQ : ', qi_qq
1468  write(*, *) '[R] qi_RR : ', qi_rr
1469  write(*, *) '[R] qr_TKern: ', qr_tkern
1470  write(*, *) '[R] qr_TKurt: ', qr_tkurt
1471  write(*, *) '[R] qr_dom : ', qr_dom
1472  write(*, *) '[R] qi_icCos: ', qi_iccos
1473  write(*, *) '[R] qi_irCsr: ', qi_ircsr
1474  write(*, *) '[R] Σ_QR : ', qr_sumqr(1: qi_nrsm: ns+1)
1475  write(*, *) '[R] Σ_P : ', (qr_sumnp(iq, iq), iq = 1, ns)
1476 #endif
1477  end if
1478  !/
1479  end subroutine prepkernelio
1480  !/
1481  !/ ------------------------------------------------------------------- /
1482  !/
1483  subroutine biinterpwt(nk, nth, wn, th)
1484  !/
1485  !/ 19-Apr-2019 : Origination ( Q. Liu )
1486  !/ 19-Apr-2019 : Extracted from a few subrs. of mod_xnl4v5.f90
1487  !/ ( Q. Liu )
1488  !/ 01-Apr-2020 : Boundary conditions ( Q. Liu )
1489  !/
1490  ! 1. Purpose:
1491  ! Calculate weights for the bilinear interpolation.
1492  !
1493  ! 2. Method:
1494  ! See also Fig. 9 of van Vledder (2006, CE) and mod_xnl4v5.f90 (WRT).
1495  ! [q_t13v4, q_weight, q_makegrid
1496  !/
1497  implicit none
1498  !
1499  integer, intent(in) :: nk
1500  integer, intent(in) :: nth
1501  real, intent(in) :: wn(nk) ! k
1502  real, intent(in) :: th(nth) ! θ
1503  !
1504  integer :: iq, jku, jk4, jk4p, jth4t, jth4, jth4p
1505  real :: dth, aref, k4t, angr, &
1506  r_jk, r_jth, delk, w_k4, w_th4
1507  real :: kmin, kmax, k4r
1508  real :: qr_kpow
1509  !
1510  ! Initialization
1511  qi_bind = 0
1512  qr_bwgh = 0.
1513  if (qi_bound .eq. 1) qr_bdry = 1.
1514  !
1515  ! Get power law for F(k) from qi_fpow for E(f)
1516  ! E(f) df = F(k) dk → F(k) ~ f^n * cg = k^{(n-1)/2}
1517  ! N(k) = F(k) / ω ~ k^{n/2-1}
1518  ! C(k) = N(k) / k ~ k^{n/2-1 -1}
1519  qr_kpow = qr_fpow / 2. - 2.
1520 
1521  ! Kmin & Kmax
1522  kmin = minval(wn)
1523  kmax = maxval(wn)
1524  !
1525  ! In general, th(nth) in [0, 2π). Note however, it is not the case when
1526  ! the first directional bin defined in ww3_grid.inp (RTH0) is not zero.
1527  dth = qr_tpi / real(nth)
1528  aref = th(1)
1529  !
1530  ! qr_k4x(nnz), qr_k4y(nnz), wn(nk) are already available for use
1531  do iq = 1, qi_nnz
1532  k4r = sqrt(qr_k4x(iq)**2. + qr_k4y(iq)**2.) ! k₄
1533  angr= atan2(qr_k4y(iq), qr_k4x(iq)) ! θ₄ [-π, π]
1534  ! Boundary
1535  if (qi_bound .eq. 1) then
1536  k4t = max(kmin, min(k4r, kmax))
1537  else
1538  k4t = k4r ! already bounded in [kmin, kmax]
1539  end if
1540  !
1541  ! Layout of surrouding four (f, θ) grid points
1542  !
1543  ! (θ)↑
1544  ! ↑ ₄ ₃
1545  ! jth4+1 ▪ ----------- ▪
1546  ! | |
1547  ! | r_jth) |
1548  ! w- |---✗ (r_jk, |
1549  ! t| | | |
1550  ! h| |₁ | |₂
1551  ! 4- jth4 ▪ ----------- ▪ → → (k)
1552  ! jk4 jk4+1
1553  ! |---|
1554  ! wk4
1555  !
1556  ! i) θ index (counted counterclockwisely)
1557  r_jth = (angr - aref) / dth + 1.
1558  jth4t = floor(r_jth) ! 'll be revised later
1559  w_th4 = r_jth - real(jth4t) ! dirc. weight
1560  !
1561  jth4 = mod(jth4t-1+nth, nth) + 1 ! wrap around 2π
1562  jth4p = mod(jth4t+nth, nth) + 1
1563  !
1564  ! ii) k index (counted in an ascending order). Note, as required in
1565  ! FindQuartetConfig, k4T >= kmin & k4T <= kmax are already satisfied.
1566  ! Thus, the resulted jkU will be in [1, nk].
1567  ! Two special cases:
1568  ! / 1, k4T = kmin
1569  ! jkU = |
1570  ! \ NK, k4T = kmax or k4T in (wn(nk-1), kmax)
1571  !
1572  jku = 1
1573  do while (k4t > wn(jku))
1574  jku = jku + 1
1575  if (jku > nk) exit ! impossible in our case
1576  end do
1577  !
1578  if (jku .eq. 1) then ! k4T = kmin
1579  r_jk = 1.
1580  else ! k4T in (kmin, kmax]
1581  delk = wn(jku) - wn(jku-1) ! Δk
1582  r_jk = real(jku - 1.) + (k4t - wn(jku-1)) / delk
1583  end if
1584  ! Parse r_jk
1585  jk4 = floor(r_jk) ! in [1, nk]
1586  w_k4 = r_jk - real(jk4)
1587  jk4p = min(jk4+1, nk) ! k4T = kmax ← min func.
1588  !
1589  ! Store indices (in 1D vector; jkth = ith + (ik-1) * nth)
1590  qi_bind(1, iq) = jth4 + (jk4 - 1) * nth
1591  qi_bind(2, iq) = jth4 + (jk4p - 1) * nth
1592  qi_bind(3, iq) = jth4p + (jk4p - 1) * nth
1593  qi_bind(4, iq) = jth4p + (jk4 - 1) * nth
1594  !
1595  ! Store weights
1596  qr_bwgh(1, iq) = (1. - w_k4) * (1. - w_th4)
1597  qr_bwgh(2, iq) = w_k4 * (1. - w_th4)
1598  qr_bwgh(3, iq) = w_k4 * w_th4
1599  qr_bwgh(4, iq) = (1. - w_k4) * w_th4
1600  !
1601  ! Note that the qi_bind & qr_bwgh do not make full sense when
1602  ! k4 < kmin (k indices are not correct at all) or k4 > kmax (k index = NK)
1603  ! because we have capped k4T in between kmin and kmax.
1604  ! But no need to worry about this because
1605  ! 1) C(k) = 0. when k < kmin
1606  ! 2) C(k) = C(NK) * power decay when k > kmax
1607  !
1608  if (qi_bound .eq. 1) then
1609  if (k4r < kmin) then
1610  qr_bdry(iq) = 0.
1611  else if (k4r > kmax) then
1612  qr_bdry(iq) = (k4r/kmax)**qr_kpow
1613  end if
1614  end if
1615  !
1616  end do
1617  !/
1618  end subroutine biinterpwt
1619  !/
1620  !/ ------------------------------------------------------------------- /
1621  !/
1622  subroutine calcqrsnl(nk, nth, sig, th, &
1623  t0, t1, Cvk0, Cvk1, &
1624  Inpqr0, Snl, Dnl, Kurt)
1625  !/
1626  !/ 09-Dec-2018 : Origination ( Q. Liu )
1627  !/ 09-Dec-2018 : Extracted from Odin's subr. `calcQRSNL`
1628  !/ ( Q. Liu )
1629  !/ 10-Jun-2019 : Include Janssen's KE properly ( Q. Liu )
1630  !/ 07-Jun-2021 : Switch off the cal. of kurtosis (!|KT|)
1631  !/ ( Q. Liu )
1632  !/
1633  ! 1. Purpose:
1634  ! Calculate the nonlinear transfer rates for a given frequency
1635  ! grid and given action density spectrum C(\vec{k}).
1636  !
1637  ! According to J09 and GS13, C(\vec{k}) is given by
1638  ! /
1639  ! m0 = | F(\vec{k}) d \vec{k}
1640  ! /
1641  !
1642  ! F(\vec{k}) = ω C(\vec{k}) / g,
1643  !
1644  ! whereas the wave action density spectrum used in WW3 is given by
1645  ! F(\vec{k}) d \vec{k} = F(k, θ) dk dθ
1646  ! = N(k, θ) ω dk dθ
1647  !
1648  ! Thus, we have
1649  ! C(\vec{k}) = N * g / k.
1650  !
1651  ! 2. Method
1652  ! See GS13 & GB16 for all the details.
1653  !
1654  ! ◆ t0, t1 here are time `relative` to the begining time of the
1655  ! simulaiton, rather than the `absolute` time
1656  !
1657  ! ◆ Cvk0, Cvk1, Snl, Dnl shoud be organized/stored in the same way as
1658  ! qr_kx, qr_ky, qr_dk, qr_om
1659  !/
1660  implicit none
1661  !
1662  integer, intent(in) :: nk ! # of frequencies
1663  integer, intent(in) :: nth ! # of directions
1664  real, intent(in) :: sig(nk) ! radian frequency (rad)
1665  real, intent(in) :: th(nth) ! θ (rad) [equally spaced,
1666  ! but may start from non-zero
1667  !
1668  real, intent(inout) :: t0 ! previous time step
1669  real, intent(inout) :: cvk0(nk*nth) ! Action density @ t0
1670  complex, intent(inout) :: inpqr0(:) ! I(t) @ t0
1671  !
1672  real, intent(in) :: t1 ! current time step
1673  real, intent(in) :: cvk1(nk*nth) ! Action density @ t1
1674  ! ... C(\vec{k})
1675  !
1676  real, intent(out) :: snl(nk*nth) ! Snl = dC/dt
1677  real, intent(out) :: dnl(nk*nth) ! Dnl
1678  real, intent(out) :: kurt ! Kurtosis
1679  !
1680  ! Local parameters
1681  !
1682  real :: delt ! Δt
1683  logical, save :: flread = .true.
1684  integer :: num_i, ns
1685  real :: dvk0(nk*nth),& ! Odin's discrete Cvk @ t0
1686  dvk1(nk*nth) ! ... @ t1
1687  real, allocatable, save :: cvk0_r(:), & ! C₄ @ t0
1688  cvk1_r(:) ! @ t1
1689  real, allocatable, save :: fnpqr0(:), & ! C prod. @ t0
1690  fnpqr1(:), & ! C prod. @ t1
1691  mnpqr (:) ! δC_1/δt * δk_1
1692  complex, allocatable, save :: inpqr1(:), & ! I(t) @ t1
1693  etau(:), & ! exp(iΔωt)
1694  edelt(:) ! exp(iΔωΔt)
1695  !
1696  real, allocatable, save :: mnp1d(:), mnp2d(:, :)
1697  real :: secm2 ! Second-order moment²
1698  !/
1699  !
1700  ! Initilization
1701  ns = nk * nth
1702  qi_nrsm = ns * ns
1703  !
1704  ! Only constant depth is allowed now. Accordingly, we only need a single
1705  ! binary config file which provides the wavenumber grid and kernel
1706  ! coefficients.
1707  !
1708  ! Read quartets & kernel coefficients in
1709  if (flread) then
1710  ! Only read data once
1711  call prepkernelio(nk, nth, sig, th, 'READ')
1712  flread = .false.
1713  ! write(*, *) "⊚ → [R] FLag for Reading Kernels becomes |", FlRead, "|"
1714  ! Allocate arrays
1715  ! ✓ A variable with the SAVE attribute retains its value and definition,
1716  ! association, and `allocation` status on exit from a procedure
1717  !
1718  if (allocated(fnpqr0)) deallocate(fnpqr0); allocate(fnpqr0(qi_nnz))
1719  if (allocated(fnpqr1)) deallocate(fnpqr1); allocate(fnpqr1(qi_nnz))
1720  if (allocated(etau )) deallocate(etau ); allocate(etau(qi_nnz))
1721  if (allocated(edelt )) deallocate(edelt ); allocate(edelt(qi_nnz))
1722  if (allocated(mnpqr )) deallocate(mnpqr ); allocate(mnpqr(qi_nnz))
1723  if (allocated(inpqr1)) deallocate(inpqr1); allocate(inpqr1(qi_nnz))
1724  !
1725  if (allocated(mnp1d)) deallocate(mnp1d); allocate(mnp1d(qi_nrsm))
1726  if (allocated(mnp2d)) deallocate(mnp2d); allocate(mnp2d(ns, ns))
1727  !
1728  if (qi_disc .eq. 0) then
1729  if (allocated(cvk0_r)) deallocate(cvk0_r); allocate(cvk0_r(qi_nnz))
1730  if (allocated(cvk1_r)) deallocate(cvk1_r); allocate(cvk1_r(qi_nnz))
1731  end if
1732  !
1733  end if
1734  !
1735  ! Screen output (check whether the kernel data are stored in memory)
1736 #ifdef W3_TS
1737  write(*, *) "◆ qr_depth :", qr_depth
1738  write(*, *) "◆ qr_oml :", qr_oml
1739  write(*, *) "◆ qi_disc :", qi_disc
1740  write(*, *) "◆ qi_kev :", qi_kev
1741  write(*, *) "◆ qi_nnz :", qi_nnz
1742  write(*, *) "◆ qi_NN(:10) :", qi_nn(:10)
1743  write(*, *) "◆ qr_TKern(:10) :", qr_tkern(:10)
1744  write(*, *) "◆ qr_TKurt(:10) :", qr_tkurt(:10)
1745  write(*, *) "◆ qr_sumQR(:10) :", qr_sumqr(:10)
1746 #endif
1747  !
1748  num_i = size(inpqr0)
1749  if (num_i .ne. qi_nnz) then
1750  write(*, 1001) 'CalcQRSNL'
1751  call exit(1)
1752  end if
1753  !
1754  ! Start to calc. Snl term
1755  if (qi_disc == 0) then
1756  ! Define ΔC = dC/dt * Δk Δt, we have ΔC₁ = ΔC₂ = -ΔC₃ = -ΔC₄ (Δt can be
1757  ! removed by taking the unit time)
1758  !
1759  ! Cvk0/1_R (bilinear interp. or nearest bin)
1760  if (qi_interp .eq. 0) then
1761  cvk0_r = cvk0(qi_rr)
1762  cvk1_r = cvk1(qi_rr)
1763  !
1764  else if (qi_interp .eq. 1) then
1765  cvk0_r = qr_bwgh(1, :) * cvk0(qi_bind(1, :)) + &
1766  qr_bwgh(2, :) * cvk0(qi_bind(2, :)) + &
1767  qr_bwgh(3, :) * cvk0(qi_bind(3, :)) + &
1768  qr_bwgh(4, :) * cvk0(qi_bind(4, :))
1769  !
1770  cvk1_r = qr_bwgh(1, :) * cvk1(qi_bind(1, :)) + &
1771  qr_bwgh(2, :) * cvk1(qi_bind(2, :)) + &
1772  qr_bwgh(3, :) * cvk1(qi_bind(3, :)) + &
1773  qr_bwgh(4, :) * cvk1(qi_bind(4, :))
1774  !
1775  if (qi_bound .eq. 1) then
1776  cvk0_r = cvk0_r * qr_bdry
1777  cvk1_r = cvk1_r * qr_bdry
1778  end if
1779  !
1780  end if
1781  !
1782  ! F = [C₃ C₄ (C₁ + C₂) - C₁ C₂ (C₃ + C₄)] dk₂ dk₃ dk₄ ∙ dk₁
1783  ! dk₄ vanishes with the δ function
1784  fnpqr0 = (cvk0(qi_qq) * cvk0_r * ( &
1785  cvk0(qi_nn) + cvk0(qi_pp) ) - &
1786  cvk0(qi_nn) * cvk0(qi_pp) * ( &
1787  cvk0(qi_qq) + cvk0_r )) * &
1788  qr_dk(qi_nn) * qr_dk(qi_pp) * qr_dk(qi_qq)
1789  !
1790  fnpqr1 = (cvk1(qi_qq) * cvk1_r * ( &
1791  cvk1(qi_nn) + cvk1(qi_pp) ) - &
1792  cvk1(qi_nn) * cvk1(qi_pp) * ( &
1793  cvk1(qi_qq) + cvk1_r )) * &
1794  qr_dk(qi_nn) * qr_dk(qi_pp) * qr_dk(qi_qq)
1795  !
1796  else if (qi_disc == 1) then
1797  ! Used in GS13 & GB16
1798  ! F = [C₃dk₃ C₄dk₄ (C₁dk₁ + C₂dk₂) - C₁dk₁ C₂dk₂ (C₃dk₃ + C₄dk₄)]
1799  ! It seems the bilinear interpolation for this discretization approach
1800  ! is not very meaningful.
1801  dvk0 = cvk0 * qr_dk
1802  fnpqr0 = dvk0(qi_qq) * dvk0(qi_rr) * ( &
1803  dvk0(qi_nn) + dvk0(qi_pp) ) - &
1804  dvk0(qi_nn) * dvk0(qi_pp) * ( &
1805  dvk0(qi_qq) + dvk0(qi_rr) )
1806  !
1807  dvk1 = cvk1 * qr_dk
1808  fnpqr1 = dvk1(qi_qq) * dvk1(qi_rr) * ( &
1809  dvk1(qi_nn) + dvk1(qi_pp) ) - &
1810  dvk1(qi_nn) * dvk1(qi_pp) * ( &
1811  dvk1(qi_qq) + dvk1(qi_rr) )
1812  !
1813  end if
1814  !|KT|! Calc m2 for Kurtosis estimation ((2.6) of Annekov & Shrira (2013))
1815  !|KT| SecM2 = sum(Cvk1 * qr_om * qr_dk) ** 2.
1816  !
1817  ! write(*, *) '.... Input args: t0, t1 :', t0, t1
1818  if (abs(t1) < qr_eps) then
1819  ! t1 = 0.0 [essentially I₁ = 0 → I₀ = 0]
1820  t0 = 0.0
1821  cvk0 = cvk1
1822  inpqr0 = (0.0, 0.0) ! \int_{0}^{0} dt = 0
1823  snl = 0.0
1824  dnl = 0.0
1825  kurt = 0.0
1826  else
1827  ! t1 ≠ 0.0
1828  delt = t1 - t0
1829  if (delt < 0.0) then
1830  write(*, 1002) 'CalcQRSNL'
1831  call exit(2)
1832  end if
1833  etau = exp(qc_iu * cmplx(qr_dom * t1)) ! exp(iΔωt)
1834  edelt = exp(qc_iu * cmplx(qr_dom * delt)) ! exp(iΔωΔt)
1835  !
1836  ! ◆ Calc. I₁: note here I₁ = I(t₁) dk₁ dk₂ dk₃ for both qi_disc = 0/1
1837  if (qi_kev .eq. 0) then
1838  ! GKE from GS13, GB16
1839  inpqr1 = inpqr0 + cmplx(0.5 * delt) * &
1840  conjg(etau) * & ! exp(-iΔωt)
1841  (cmplx(fnpqr0) * edelt + cmplx(fnpqr1))
1842  else if (qi_kev .eq. 1) then
1843  ! KE from J03 (Fnpqr1 is taken outside the time integral; Fnpqr0 is not
1844  ! used in this case; and the real part of Inpqr1 is sin(Δωt)/Δω, and
1845  ! the imaginary part is [1 - cos(Δωt)] / Δω
1846  ! Approximation used before
1847  ! Inpqr1 = Inpqr0 + cmplx(0.5 * DelT) * &
1848  ! conjg(ETau) * (EDelT + 1)
1849  !
1850  where (abs(qr_dom) < qr_eps)
1851  ! Δω = 0., sin(Δωt)/Δω ~ t, [1 - cos(Δωt)] / Δω ~ 0
1852  inpqr1 = cmplx(t1, 0.)
1853  elsewhere
1854  ! Δω ≠ 0., cacl. sin(Δωt)/Δω & [1 - cos(Δωt)] / Δω directly
1855  ! TODO: the sign of cos is not clear yet.
1856  inpqr1 = cmplx(sin(qr_dom * t1) / qr_dom, &
1857  (1 - cos(qr_dom * t1)) / qr_dom)
1858  end where
1859  end if
1860  ! ◆ Snl [Tranfer Integal]
1861  if (qi_kev .eq. 0) then
1862  ! GKE from GS13, GB16
1863  mnpqr = 4.0 * (qr_tkern ** 2.) * real(etau * inpqr1)
1864  else if (qi_kev .eq. 1) then
1865  ! KE from J03
1866  ! Mnpqr = 4.0 * (qr_TKern ** 2.) * Fnpqr1 * real(ETau * Inpqr1)
1867  mnpqr = 4.0 * (qr_tkern ** 2.) * fnpqr1 * real(inpqr1)
1868  end if
1869  ! Calc. Σ over Q, R [Mnpqr is a upper triangular sparse matrix]
1870  ! dN₁/dt = - dN₃/dt → anti-symmetric array operation
1871  ! Mnp1D = (Mnpqr - Mnpqr^{T}) × S_{qr}
1872  call asymsmattimvec(qi_nrsm, mnpqr, qi_iccos, qi_ircsr, qr_sumqr, mnp1d, -1.0)
1873  ! Calc. Σ over P [Mnp2D is a upper triangular matrix]
1874  ! dN₁/dt = dN₂/dt → symmetric array operation
1875  ! Snl = {Σ (Mnp + Mnp^{T}) ⊙ S_{p}} / d\vec{k₁}
1876  mnp2d = reshape(mnp1d, (/ns, ns/))
1877  snl = sum((mnp2d + transpose(mnp2d)) * qr_sumnp, 2) / qr_dk
1878  ! ◆ Conservation Check
1879 #ifdef W3_TS
1880  write(*, '(A, E15.3)') ' ← {WW3 GKE } ΣSnl(k) * dk: ', sum(snl * qr_dk)
1881 #endif
1882  !
1883  ! ◆ Dnl [Diagonal term] <TODO>
1884  ! i) it is easy to calculate Dnl for Janssen's KE (but we may
1885  ! have to abandon the sparse array approach)
1886  ! ii) it is challenging to get Dnl for GKE.
1887  dnl = 0.0
1888  kurt = 0.0
1889  !
1890  !|KT|! ◆ Kurtosis
1891  !|KT| if (qi_kev .eq. 0) then
1892  !|KT|! GKE from GS13, GB16
1893  !|KT| Mnpqr = -3.0 / SecM2 * qr_TKurt * aimag(ETau * Inpqr1)
1894  !|KT| else if (qi_kev .eq. 1) then
1895  !|KT|! KE from J03 (here the imaginary part becomes [1 - cos(Δωt)] / Δω
1896  !|KT|! Mnpqr = -3.0 / SecM2 * qr_TKurt * Fnpqr1 * aimag(ETau * Inpqr1)
1897  !|KT| Mnpqr = -3.0 / SecM2 * qr_TKurt * Fnpqr1 * aimag(Inpqr1)
1898  !|KT| end if
1899  !|KT|! Calc. Σ over Q, R [Mnpqr is a upper triangular sparse matrix]
1900  !|KT|! symmetric array operation Mnp1D = (Mnpqr - Mnpqr^{T}) × S_{qr}
1901  !|KT| call ASymSmatTimVec(qi_nrsm, Mnpqr, qi_icCos, qi_irCsr, qr_sumQR, Mnp1D, 1.0)
1902  !|KT| Mnp2D = reshape(Mnp1D, (/ns, ns/))
1903  !|KT| Kurt = sum((Mnp2D + transpose(Mnp2D)) * qr_sumNP)
1904  !
1905  ! I₁ → I₀ for next computation (time step)
1906  t0 = t1
1907  cvk0 = cvk1
1908  inpqr0 = inpqr1
1909  end if
1910  !
1911  ! write(*, *) '.... Output args: t0, t1 :', t0, t1
1912  !
1913  ! Formats
1914 1001 FORMAT(/' *** GKE ERROR IN gkeModule : '/ &
1915  ' Subr. ', a, ': the stored total number of quartets &
1916  & and the size of Inpqr0 do not match !'/)
1917 1002 FORMAT(/' *** GKE ERROR IN gkeModule : '/ &
1918  ' Subr. ', a, ': t0 ≤ t1 is not satisfied !'/)
1919  !/
1920  end subroutine calcqrsnl
1921  !/
1922  !/ ------------------------------------------------------------------- /
1923 end module w3gkemd
1924 !/ ------------------------------------------------------------------- /
w3gkemd::qr_depth
real, public qr_depth
Definition: w3gkemd.F90:165
w3gkemd::qi_kev
integer, public qi_kev
Definition: w3gkemd.F90:176
w3gkemd::qr_oml
real, public qr_oml
Definition: w3gkemd.F90:167
w3gkemd::prepkernelio
subroutine, public prepkernelio(nk, nth, sig, th, act)
Definition: w3gkemd.F90:1202
w3gkemd::qi_interp
integer, public qi_interp
Definition: w3gkemd.F90:180
file
file(STRINGS ${CMAKE_BINARY_DIR}/switch switch_strings) separate_arguments(switches UNIX_COMMAND $
Definition: CMakeLists.txt:3
w3gkemd::qi_disc
integer, public qi_disc
Definition: w3gkemd.F90:172
constants
Define some much-used constants for global use (all defined as PARAMETER).
Definition: constants.F90:20
w3dispmd::wavnu1
subroutine wavnu1(SI, H, K, CG)
Definition: w3dispmd.F90:85
constants::file_endian
character(*), parameter file_endian
FILE_ENDIAN Filled by preprocessor with 'big_endian', 'little_endian', or 'native'.
Definition: constants.F90:86
w3gkemd
Definition: w3gkemd.F90:3
w3gkemd::calcqrsnl
subroutine, public calcqrsnl(nk, nth, sig, th, t0, t1, Cvk0, Cvk1, Inpqr0, Snl, Dnl, Kurt)
Definition: w3gkemd.F90:1625
w3gkemd::qi_nnz
integer(kind=8), public qi_nnz
Definition: w3gkemd.F90:209
w3dispmd
Definition: w3dispmd.F90:3