Multidip  1.0
Multi-photon matrix elements
multidip_special.F90
Go to the documentation of this file.
1 ! Copyright 2020
2 !
3 ! For a comprehensive list of the developers that contributed to these codes
4 ! see the UK-AMOR website.
5 !
6 ! This file is part of UKRmol-out (UKRmol+ suite).
7 !
8 ! UKRmol-out is free software: you can redistribute it and/or modify
9 ! it under the terms of the GNU General Public License as published by
10 ! the Free Software Foundation, either version 3 of the License, or
11 ! (at your option) any later version.
12 !
13 ! UKRmol-out is distributed in the hope that it will be useful,
14 ! but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ! GNU General Public License for more details.
17 !
18 ! You should have received a copy of the GNU General Public License
19 ! along with UKRmol-out (in source/COPYING). Alternatively, you can also visit
20 ! <https://www.gnu.org/licenses/>.
21 !
27 
28  use, intrinsic :: iso_c_binding, only: c_double, c_int, c_f_pointer
29  use, intrinsic :: iso_fortran_env, only: input_unit, int32, int64, real64, output_unit
30 
31  use blas_lapack_gbl, only: blasint
32  use phys_const_gbl, only: pi, imu
33  use precisn_gbl, only: wp
34 
35  implicit none
36 
37  real(wp), parameter :: rzero = 0
38  real(wp), parameter :: rone = 1
39 
40 #ifdef WITH_GSL
41 
42  type, bind(c) :: gsl_sf_result
43  real(c_double) :: val
44  real(c_double) :: err
45  end type gsl_sf_result
46 
47  interface
48  ! logarithm of the complex gamma function from GSL
49  function gsl_sf_lngamma_complex_e (zr, zi, lnr, arg) bind(C, name='gsl_sf_lngamma_complex_e')
50  use, intrinsic :: iso_c_binding, only: c_int, c_double
51  import gsl_sf_result
52  real(c_double), value :: zi, zr
53  type(gsl_sf_result) :: lnr, arg
54  integer(c_int) :: gsl_sf_lngamma_complex_e
55  end function gsl_sf_lngamma_complex_e
56  end interface
57 
58  interface
59  ! Coulomb wave function from GSL
60  function gsl_sf_coulomb_wave_fg_e (eta, rho, l, k, F, Fp, G, Gp, expF, expG) bind(C, name='gsl_sf_coulomb_wave_FG_e')
61  use, intrinsic :: iso_c_binding, only: c_int, c_double
62  import gsl_sf_result
63  real(c_double), value :: eta, rho, l
64  integer(c_int), value :: k
65  type(gsl_sf_result) :: f, fp, g, gp
66  real(c_double) :: expf, expg
67  integer(c_int) :: gsl_sf_coulomb_wave_fg_e
68  end function gsl_sf_coulomb_wave_fg_e
69  end interface
70 
71  interface
72  ! irregular confluent hypergeometric function U
73  pure function gsl_sf_hyperg_u (a, b, x) bind(C, name='gsl_sf_hyperg_U')
74  use, intrinsic :: iso_c_binding, only: c_double
75  real(c_double), value :: a, b, x
76  real(c_double) :: gsl_sf_hyperg_u
77  end function gsl_sf_hyperg_u
78  end interface
79 #endif
80 
81  interface
82  ! Coulomb phase from UKRmol-out
83  function cphaz (l, eta, iwrite)
84  import wp
85  integer :: l, iwrite
86  real(wp) :: eta, cphaz
87  end function cphaz
88  end interface
89 
90  interface
91  ! Coulomb wave function from UKRmol-out
92  subroutine coulfg (xx, eta1, xlmin, xlmax, fc, gc, fcp, gcp, mode1, kfn, ifail)
93  import wp
94  integer :: mode1, kfn, ifail
95  real(wp) :: xx, eta1, xlmin, xlmax, fc(*), gc(*), fcp(*), gcp(*)
96  end subroutine coulfg
97  end interface
98 
99  interface
100  ! Decaying Whittaker function
101  subroutine couln (l, Z, ERy, r, U, Up, acc, efx, ierr, nlast, fnorm)
102  import wp
103  integer :: l, ierr, nlast
104  real(wp) :: Z, Ery, r, U, Up, acc, efx, fnorm
105  end subroutine couln
106  end interface
107 
108  interface
109  !Exponentially decaying solution with given (negative) energy and angular momentum
110  subroutine decay (k, l, r, U, Up, iwrite)
111  import wp
112  integer :: l, iwrite
113  real(wp) :: k, r, U, Up
114  end subroutine decay
115  end interface
116 
117  interface
118  ! real matrix-matrix multiplication from BLAS
119  subroutine dgemm (transa, transb, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc)
120  import real64, blasint
121  character(len=1) :: transa, transb
122  integer(blasint) :: m, n, k, lda, ldb, ldc
123  real(real64) :: A(lda, *), B(ldb, *), C(ldc, *), alpha, beta
124  end subroutine dgemm
125  end interface
126 
127  interface
128  ! real matrix-vector multiplication from BLAS
129  subroutine dgemv (trans, m, n, alpha, A, lda, X, incx, beta, Y, incy)
130  import real64, blasint
131  character(len=1) :: trans
132  integer(blasint) :: m, n, incx, incy, lda
133  real(real64) :: A(lda, *), X(*), Y(*), alpha, beta
134  end subroutine dgemv
135  end interface
136 
137  interface
138  ! complex matrix-vector multiplication from BLAS
139  subroutine zgemv (trans, m, n, alpha, A, lda, X, incx, beta, Y, incy)
140  import real64, blasint
141  character(len=1) :: trans
142  integer(blasint) :: m, n, incx, incy, lda
143  complex(real64) :: A(lda, *), X(*), Y(*), alpha, beta
144  end subroutine zgemv
145  end interface
146 
147  interface
148  ! complex LU decomposition from LAPACK
149  subroutine zgetrf (m, n, A, lda, ipiv, info)
150  import real64, blasint
151  integer(blasint) :: m, n, lda, ipiv(*), info
152  complex(real64) :: A(lda, *)
153  end subroutine zgetrf
154  end interface
155 
156  interface
157  ! complex matrix solution from LAPACK
158  subroutine zgetrs (trans, n, nrhs, A, lda, ipiv, B, ldb, info)
159  import real64, blasint
160  character(len=1) :: trans
161  integer(blasint) :: n, nrhs, lda, ldb, info, ipiv(*)
162  complex(real64) :: A(lda, *), B(ldb, *)
163  end subroutine zgetrs
164  end interface
165 
166  interface
167  ! complex matrix inversion from LAPACK
168  subroutine zgetri (n, A, lda, ipiv, work, lwork, info)
169  import real64, blasint
170  integer(blasint) :: n, lda, ipiv(*), lwork, info
171  complex(real64) :: A(lda, *), work(*)
172  end subroutine zgetri
173  end interface
174 
175 contains
176 
186  subroutine calculate_s_matrix (Km, Sm, nopen)
188  complex(wp), allocatable, intent(inout) :: Sm(:, :)
189  real(wp), intent(in) :: Km(:, :)
190  integer, intent(in) :: nopen
191 
192  complex(wp), allocatable :: IpiK(:, :), ImiK(:, :)
193  integer :: i, nchan
194 
195  nchan = size(sm, 1)
196 
197  sm = 0
198 
199  allocate (ipik(nchan, nchan), imik(nchan, nchan))
200 
201  ipik = 0; forall (i = 1:nchan) ipik(i, i) = 1
202  imik = 0; forall (i = 1:nchan) imik(i, i) = 1
203  ipik(1:nopen, 1:nopen) = ipik(1:nopen, 1:nopen) + imu*km(1:nopen, 1:nopen)
204  imik(1:nopen, 1:nopen) = imik(1:nopen, 1:nopen) - imu*km(1:nopen, 1:nopen)
205 
206  call invert_matrix(imik)
207 
208  sm = matmul(ipik, imik)
209 
210  end subroutine calculate_s_matrix
211 
212 
220  subroutine scatak (rmatr, eig, etot, w, Ek, l, Km, Ak)
222  complex(wp), allocatable, intent(inout) :: Ak(:,:)
223  real(wp), intent(in) :: rmatr, eig(:), etot, Ek(:), w(:,:), Km(:,:)
224  integer, intent(in) :: l(:)
225 
226  complex(wp), allocatable :: T(:,:)
227  real(wp), allocatable :: V(:,:), TT(:,:,:), Sp(:,:), Cp(:,:), P(:), XX(:,:,:), wFp(:,:,:), k(:)
228 
229  integer(blasint) :: i, j, nchan, nstat, nchan2, nopen, nopen2
230  real(wp) :: F, Fp, G, Gp
231 
232  nchan = size(ek)
233  nchan2 = 2*nchan
234  nopen = count(ek > 0)
235  nopen2 = 2*nopen
236  nstat = size(eig)
237  k = sqrt(2*abs(ek))
238 
239  allocate (v(nchan, nchan), t(nopen, nopen), sp(nchan, nchan), cp(nchan, nchan), tt(nchan, nopen, 2), &
240  xx(nchan, nopen, 2), wfp(nstat, nopen, 2))
241 
242  t = 0; v = 0; sp = 0; cp = 0
243 
244  ! construct, factorize and invert T
245  forall (i = 1:nopen) t(i,i) = 1
246  t = t + imu*km(1:nopen, 1:nopen)
247  call invert_matrix(t)
248  tt(1:nopen, 1:nopen, 1) = real(T, wp)
249  tt(1:nopen, 1:nopen, 2) = aimag(t)
250 
251  ! evaluate the Coulomb functions and the normalization factor
252  do i = 1, nchan
253  call coul(l(i), ek(i), rmatr, f, fp, g, gp)
254  v(i,i) = sqrt(2/(pi*k(i)))
255  sp(i,i) = fp
256  cp(i,i) = gp
257  end do
258 
259  ! photoionization coefficient
260  call dgemm('N', 'N', nchan, nopen, nchan, rone, cp, nchan, km, nchan, rone, sp, nchan) ! S' + C' K
261  call dgemm('N', 'N', nchan, nopen2, nopen, rone, sp, nchan, tt, nchan, rzero, xx, nchan) ! (S' + C' K) T
262  call dgemm('N', 'N', nchan, nopen2, nchan, rone, v, nchan, xx, nchan, rzero, tt, nchan) ! V (S' + C' K) T
263  call dgemm('T', 'N', nstat, nopen2, nchan, rone, w, nchan, tt, nchan, rzero, wfp, nstat) ! wT V (S' + C' K) T
264  p = 0.5 / (eig - etot)
265  do i = 1, nopen
266  ak(:, i) = p * cmplx(wfp(:, i, 1), wfp(:, i, 2), wp)
267  end do
268  do i = nopen + 1, nchan
269  ak(:, i) = 0
270  end do
271 
272  end subroutine scatak
273 
274 
282  real(wp) function cphase (l, k) result (sigma)
284  integer, intent(in) :: l
285  real(wp), intent(in) :: k
286 #ifdef WITH_GSL
287  type(gsl_sf_result) :: lnr, arg
288  integer(c_int) :: err
289  real(c_double) :: zr, zi
290 
291  zr = l + 1
292  zi = -1/k
293  err = gsl_sf_lngamma_complex_e(zr, zi, lnr, arg)
294  sigma = arg % val
295 #else
296  sigma = cphaz(l, -1/k, 0)
297 #endif
298 
299  end function cphase
300 
301 
314  subroutine coul (l, Ek, r, F, Fp, G, Gp)
316  integer, intent(in) :: l
317  real(wp), intent(in) :: Ek, r
318  real(wp), intent(inout) :: F, Fp, G, Gp
319  real(wp) :: Z = 1
320 #ifdef WITH_GSL
321  call coul_gsl(l, ek, r, f, fp, g, gp)
322 #else
323  call coul_ukrmol(l, ek, r, f, fp, g, gp)
324 #endif
325  end subroutine coul
326 
327 
334  subroutine coul_gsl (l, Ek, r, F, Fp, G, Gp)
336  integer, intent(in) :: l
337  real(wp), intent(in) :: Ek, r
338  real(wp), intent(inout) :: F, Fp, G, Gp
339 #ifdef WITH_GSL
340  real(wp) :: Z = 1
341  type(gsl_sf_result) :: resF, resFp, resG, resGp
342  real(c_double) :: expF, expG, eta, rho, lc, k, a, b, x, U, Up
343  integer(c_int) :: err, zero = 0
344 
345  if (ek > 0) then
346  ! positive-energy solution
347  k = sqrt(2*ek)
348  rho = k*r
349  eta = -z/k
350  lc = l
351  err = gsl_sf_coulomb_wave_fg_e(eta, rho, lc, zero, resf, resfp, resg, resgp, expf, expg)
352  f = resf % val
353  fp = resfp % val * k
354  g = resg % val
355  gp = resgp % val * k
356  else
357  ! negative-energy solution, Whittaker function W (WARNING: charged only)
358  k = sqrt(-2*ek)
359  a = l + 1 - z/k
360  b = 2*l + 2
361  x = 2*k*r
362  u = gsl_sf_hyperg_u(a, b, x)
363  up = -2*k*a * gsl_sf_hyperg_u(a + 1, b + 1, x) ! DLMF §13.3.22
364  f = 0
365  fp = 0
366  g = exp(-x/2) * x**(l + 1) * u ! DLMF §13.14.3
367  gp = exp(-x/2) * x**(l + 1) * (-k * u + (l + 1)/r * u + up)
368  end if
369 #else
370  f = 0; fp = 0
371  g = 0; gp = 0
372 #endif
373  end subroutine coul_gsl
374 
375 
382  subroutine coul_ukrmol (l, Ek, r, F, Fp, G, Gp)
384  integer, intent(in) :: l
385  real(wp), intent(in) :: Ek, r
386  real(wp), intent(inout) :: F, Fp, G, Gp
387  real(wp) :: Z = 1
388  integer :: ifail, mode = 1, kfn = 0, nterms, iwrite = 0
389  real(wp) :: fc(l + 1), gc(l + 1), fcp(l + 1), gcp(l + 1), lc, efx, fnorm, acc = 1e-10_wp, k
390 
391  lc = l
392  if (ek > 0) then
393  ! positive-energy solution
394  k = sqrt(2*ek)
395  call coulfg(k*r, -z/k, lc, lc, fc, gc, fcp, gcp, mode, kfn, ifail)
396  f = fc(l + 1)
397  g = gc(l + 1)
398  fp = fcp(l + 1) * k
399  gp = gcp(l + 1) * k
400  else
401  ! negative energy solution (charged or neutral)
402  if (z /= 0) then
403  call couln(l, z, 2*ek, r, g, gp, acc, efx, ifail, nterms, fnorm)
404  else
405  call decay(2*ek, l, r, g, gp, iwrite)
406  end if
407  f = 0
408  fp = 0
409  end if
410 
411  end subroutine coul_ukrmol
412 
413 
420  subroutine invert_matrix (T)
422  complex(wp), allocatable, intent(inout) :: T(:, :)
423 
424  complex(wp), allocatable :: work(:)
425  complex(wp) :: nwork(1)
426 
427  integer(blasint), allocatable :: ipiv(:)
428  integer(blasint) :: m, info, lwork
429 
430  m = size(t, 1)
431 
432  ! calculate LU decomposition
433  allocate (ipiv(m))
434  call zgetrf(m, m, t, m, ipiv, info)
435 
436  ! query for workspace size and allocate memory
437  lwork = -1
438  call zgetri(m, t, m, ipiv, nwork, lwork, info)
439  lwork = int(nwork(1))
440  allocate (work(lwork))
441 
442  ! compute the inverse
443  call zgetri(m, t, m, ipiv, work, lwork, info)
444 
445  end subroutine invert_matrix
446 
447 
455  subroutine solve_complex_system (n, A, X, Y)
457  complex(wp), allocatable, intent(inout) :: A(:, :)
458  real(wp), allocatable, intent(inout) :: X(:, :), Y(:, :)
459  integer, intent(in) :: n
460 
461  integer(blasint) :: m, info, nrhs, lda
462  integer(blasint), allocatable :: ipiv(:)
463  complex(wp), allocatable :: XX(:)
464 
465  allocate (ipiv(n))
466 
467  ! calculate LU decomposition
468  m = n
469  lda = size(a, 1)
470  call zgetrf(m, m, a, lda, ipiv, info)
471 
472  ! combine real and imaginary part of the right-hand side
473  xx = cmplx(x(1:n, 1), x(1:n, 2), wp)
474 
475  ! solve the equation
476  nrhs = 1
477  call zgetrs('N', m, nrhs, a, lda, ipiv, xx, m, info)
478 
479  y(1:n, 1) = real(XX, wp)
480  y(1:n, 2) = aimag(xx)
481 
482  end subroutine solve_complex_system
483 
484 
497  logical function next_permutation (p) result (ok)
499  integer, intent(inout) :: p(:)
500  integer :: i, j, k, n
501 
502  n = size(p)
503 
504  ! no permutation in empty array
505  if (n == 0) then
506  ok = .false.
507  return
508  end if
509 
510  ! initialize a new permutation
511  if (any(p < 1)) then
512  forall (i = 1 : n) p(i) = i
513  ok = .true.
514  return
515  end if
516 
517  ! one-element permutation cannot be advanced further
518  if (n == 1) then
519  ok = .false.
520  return
521  end if
522 
523  i = n
524 
525  do
526  k = i
527  i = i - 1
528  if (p(i) < p(k)) then
529  j = n
530  do while (p(i) >= p(j))
531  j = j - 1
532  end do
533  call swap(p(i), p(j))
534  call reverse(p(k:n))
535  ok = .true.
536  return
537  else if (i == 1) then
538  ok = .false.
539  return
540  end if
541  end do
542 
543  end function next_permutation
544 
545 
552  subroutine swap (a, b)
554  integer, intent(inout) :: a, b
555  integer :: c
556 
557  c = a
558  a = b
559  b = c
560 
561  end subroutine swap
562 
563 
570  subroutine reverse (a)
572  integer, intent(inout) :: a(:)
573  integer :: i, n
574 
575  n = size(a)
576 
577  do i = 1, n/2
578  call swap(a(i), a(n + 1 - i))
579  end do
580 
581  end subroutine reverse
582 
583 
598  subroutine kahan_add (X, dX, err)
600  complex(wp), intent(inout) :: X, err
601  complex(wp), intent(in) :: dX
602 
603  complex(wp) :: Y, Z
604 
605  y = dx - err
606  z = x + y
607  err = (z - x) - y ! the parenthesis must be obeyed, otherwise we get rubbish
608  x = z
609 
610  end subroutine kahan_add
611 
612 end module multidip_special
subroutine coul_gsl(l, Ek, r, F, Fp, G, Gp)
Coulomb functions (GSL)
subroutine invert_matrix(T)
Invert a complex matrix.
subroutine kahan_add(X, dX, err)
Compensated summation.
subroutine reverse(a)
Reverse order of elements in array.
subroutine coul_ukrmol(l, Ek, r, F, Fp, G, Gp)
Coulomb functions (UKRmol)
logical function next_permutation(p)
Construct or advance permutation.
subroutine solve_complex_system(n, A, X, Y)
Solve system of equations with complex matrix.
real(wp), parameter rone
real(wp) function cphase(l, k)
Coulomb phase.
Special functions and objects used by MULTIDIP.
subroutine swap(a, b)
Exchange value of two integers.
subroutine coul(l, Ek, r, F, Fp, G, Gp)
Coulomb functions.
real(wp), parameter rzero