Multidip  1.0
Multi-photon matrix elements
multidip_integ.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 !
30 
31  use phys_const_gbl, only: pi, imu
32  use precisn_gbl, only: wp
33 
34  implicit none
35 
36 contains
37 
65  complex(wp) function nested_exp_integ (a, c, N, m, s, k) result (val)
66 
67  use multidip_params, only: ntermsppt
68 
69  integer, intent(in) :: n, m(0:n-1), s(0:2*n-1)
70  real(wp), intent(in) :: a, c
71  complex(wp), intent(in) :: k(0:2*n-1)
72 
73  integer, parameter :: npp = ntermsppt
74 
75  complex(wp) :: i(0:n*(npp+1)), f(0:n*(npp+1)-1), fg(0:n*(npp+1)-1), g(0:(n+1)*(npp+1)-1), j(0:n*(npp+1)-1)
76  complex(wp) :: t(0:npp, 0:n*(npp + 1)-1)
77 
78  real(wp) :: b(0:n*(npp+1)-1, 0:n*(npp+1))
79 
80  complex(wp) :: u, v
81  integer :: o, p, q, lvl
82 
83  if (n < 1) then
84  val = 0
85  return
86  end if
87 
88  ! For a given expansion depth of the integral (Npp) we need that many
89  ! derivatives of the base function (here of the coordinate power r^m).
90  ! With N levels of nesting, this combines to N * Npp derivatives in total
91  ! for the first base function. Further base functions are not needed
92  ! to such high orders.
93 
94  i = 0
95  f = 0
96  fg = 0
97  g = 0
98  j = 0
99  t = 0
100 
101  ! Construct Pascal triangle of binomial coefficients.
102 
103  b = 0
104  do p = lbound(b, 1), ubound(b, 1)
105  b(p, 0) = 1
106  do q = 1, p - 1
107  b(p, q) = b(p - 1, q - 1) + b(p - 1, q)
108  end do
109  b(p, p) = 1
110  end do
111 
112  ! Constants of the phase function. This accumulates all phases along the nested dimensions.
113 
114  u = 0.
115  v = 0.
116 
117  ! Initialize the "deepest-nested" integral, so that f*I = f.
118 
119  i(0) = 1.
120 
121  ! Recursively evaluate levels.
122 
123  do lvl = 0, n - 1
124 
125  ! 1. Evaluate the base function for this level, including all its derivatives.
126  ! The base function is simply a coordinate power, r^m[lvl], so its
127  ! derivatives can be calculated directly.
128 
129  f(0) = a**m(lvl)
130 
131  do p = 1, (n - lvl)*(npp + 1) - 1
132  f(p) = (m(lvl) + 1 - p) * f(p - 1) / a
133  end do
134 
135  ! 2. Combine the base function with the integral from the previous level,
136  ! resulting in the effective base function fg for this level.
137 
138  do p = 0, (n - lvl)*(npp + 1) - 1
139  fg(p) = 0
140  do q = 0, p
141  fg(p) = fg(p) + b(p,q) * f(p - q) * i(q)
142  end do
143  end do
144 
145  ! 3. Evaluate the phase function at this level. This is required for construction
146  ! of the individual terms of the asymptotic expansion, as well as for finalization
147  ! of the integral after the terms are summer. The highest derivative degree
148  ! needed is Npp plus the number of derivatives of the resulting integral that
149  ! we are calculating at this level.
150 
151  u = u + s(2*lvl+1)*k(2*lvl+1) + s(2*lvl+0)*k(2*lvl+0) + imu*c
152  v = v + s(2*lvl+1)/abs(k(2*lvl+1)) + s(2*lvl+0)/abs(k(2*lvl+0))
153 
154  g(0) = imu*a / (u*a + v)
155  g(1) = imu*v / ((u*a + v)*(u*a + v))
156 
157  do p = 2, (n - lvl + 1)*(npp + 1) - 1
158  g(p) = g(p - 1) * (-p) * u / (u*a + v)
159  end do
160 
161  ! 4. Evaluate all terms of the asymptotic expansion of the integral, as well as their
162  ! derivatives up to the order required for the integral itself, that is (N - lvl)*(Npp + 1).
163 
164  do p = 0, (n - lvl)*(npp + 1) - 1
165  t(0, p) = fg(p)
166  end do
167 
168  do o = 1, npp
169  do p = 0, (n - lvl)*(npp + 1) - o - 1
170  t(o, p) = 0
171  do q = 0, p + 1
172  t(o, p) = t(o, p) + b(p + 1, q) * g(p + 1 - q) * t(o - 1, q)
173  end do
174  end do
175  end do
176 
177  ! 5. Sum the terms of the asymptotic expansion to get the reduced integral J (and its derivatives).
178 
179  do p = 0, (n - lvl - 1)*(npp + 1)
180  j(p) = 0
181  do o = 0, npp
182  j(p) = j(p) + t(o, p)
183  end do
184  end do
185 
186  ! 5. Evaluate the resulting integral (and derivatives) at this level.
187 
188  do p = 0, (n - lvl - 1)*(npp + 1)
189  i(p) = 0
190  do q = 0, p
191  i(p) = i(p) + b(p, q) * g(p - q) * j(q)
192  end do
193  end do
194 
195  end do
196 
197  val = i(0)
198 
199  end function nested_exp_integ
200 
201 
223  complex(wp) function nested_coul_integ (a, c, N, m, s, l, k) result (val)
225  use multidip_params, only: ntermsasy
226  use multidip_special, only: cphase, kahan_add
227 
228  integer, intent(in) :: n, m(1:n), s(1:2*n), l(1:2*n)
229  real(wp), intent(in) :: a, c
230  complex(wp), intent(in) :: k(1:2*n)
231 
232  integer :: nn(2*n), ms(n), p
233  complex(wp) :: an(2*n), bn(2*n), factor(2*n)
234 
235  logical :: at_end
236  real(wp) :: total_phase, total_exponent, kr, ki
237  complex(wp) :: integ, err
238 
239  at_end = .false.
240  val = 0
241  err = 0
242  factor = 1.
243  nn = 0
244 
245  ! initial values of the Pochhammer symbols in the asymptotic series for the Coulomb-Hankel (or real Whittaker) functions
246  do p = 1, 2*n
247  an(p) = 1 + l(p) - s(p)*imu/k(p)
248  bn(p) = -l(p) - s(p)*imu/k(p)
249  end do
250 
251  ! sum all terms of the Cartesian product of all Coulomb-Hankel (or real Whittaker) functions expansions
252  do while (.not. at_end)
253 
254  ! total power in r^m
255  do p = 1, n
256  ms(p) = m(p) - nn(2*p-1) - nn(2*p-0)
257  end do
258 
259  ! radial integral
260  integ = nested_exp_integ(a, c, n, ms, s, k)
261 
262  ! combine all coefficients from the expansions of all Coulomb-Hankel (or real Whittaker) functions
263  do p = 1, 2*n
264  integ = integ * factor(p)
265  end do
266 
267  ! add the integral value using the floating-point-error compensating routine
268  call kahan_add(val, integ, err)
269 
270  ! pick up the next single term from the product of expansions of all Coulomb-Hankel (or real Whittaker) functions
271  do p = 1, 2*n
272  nn(p) = nn(p) + 1
273  if (nn(p) == ntermsasy) then
274  nn(p) = 0
275  factor(p) = 1.
276  at_end = .true.
277  else
278  factor(p) = factor(p) * (an(p) + nn(p) - 1) * (bn(p) + nn(p) - 1) / (2 * imu * s(p) * nn(p) * k(p))
279  at_end = .false.
280  exit
281  end if
282  end do
283 
284  end do
285 
286  ! calculate the overall exponential and phase factor
287  total_phase = 0
288  total_exponent = 0
289  do p = 1, 2*n
290  kr = real(k(p), wp)
291  ki = aimag(k(p))
292  if (ki == 0) then
293  ! positive energy solution -> Coulomb-Hankel function
294  total_phase = total_phase + s(p)*(kr*a + log(2*kr*a)/kr - pi*l(p)/2 + cphase(l(p), kr))
295  else
296  ! negative energy solution -> Whittaker function
297  total_exponent = total_exponent + s(p)*(-ki*a + log(2*ki*a)/ki)
298  end if
299  end do
300 
301  ! multiply the sum of expansion terms with the combined exponential factor
302  val = val * cmplx(cos(total_phase), sin(total_phase), wp) * exp(total_exponent - n*c*a)
303 
304  end function nested_coul_integ
305 
306 
330  complex(wp) function nested_cgreen_integ (a, c, N, sa, sb, m, l, k) result (val)
333 
334  integer, intent(in) :: n, sa, sb, m(:), l(:)
335  real(wp), intent(in) :: a, c
336  complex(wp), intent(in) :: k(:)
337 
338  logical :: acc(n)
339  integer :: order(n), mp(n), lp(2*n), sp(2*n), p, signs
340  complex(wp) :: kp(2*n), err, integ
341 
342  val = 0
343  err = 0
344 
345  ! initial ordering of coordinates, rN < ... < r2 < r1 (order(1) = index of the largest coordinate)
346  order = -1
347 
348  ! for all orderings of the coordinates { rN, ..., r2, r1 }
349  do while (next_permutation(order))
350 
351  ! reorder quantum numbers according to coordinate order
352  do p = 1, n
353  mp(p) = m(order(p))
354  end do
355 
356  ! for all regular Coulomb function splittings, F = (H⁺ - H⁻)/2i
357  do signs = 0, 2**(n - 1) - 1
358 
359  ! Each bit of 'signs' (starting from the least significant one) corresponds to the sign of the Coulomb-Hankel
360  ! function H arising from the F function in the corresponding Coulomb-Green's function. If 'j' is the position
361  ! of the bit (starting from 0 for the least significant one), then the sign belongs to the Coulomb-Green's
362  ! function g(r(j+1), r(j)). There are N-1 Coulomb-Green's functions in total in the integrand, so we are iterating
363  ! only over all combinations of the first N-1 bits.
364 
365  ! these (original) coordinate indices have been processed already
366  acc = .false.
367 
368  ! assemble quantum numbers for coordinates sorted from largest to smallest
369  do p = 1, n
370 
371  ! For each coordinate, there are two Coulomb-Hankel functions. For r0, one of the Coulomb-Hankel functions
372  ! is H[sa]. For rN one of them is H[sb]. Others come from separation of g(r(j+1), r(j)). Here we are processing
373  ! coordinate r(j), where j = order[n]. This coordinate is connected by the Coulomb-Green's functions to the
374  ! previous coordinate r(j-1) and to the next coordinate r(j+1). The former connection is realized by (j-1)-th
375  ! Coulomb-Green's function, while the latter by the j-th one. We compare the current coordinate r(j) to its
376  ! neighbours r(j-1) and r(j+1) simply by checking whether we have already processed them, because we are
377  ! processing the coordinates in order of the descending order. Based on this comparison, either the appropriate
378  ! F sign (if this coordinate is smaller than neighbour) or the +1 sign for H+ (if the this coordinate is greater
379  ! then the neighbour) is used.
380 
381  sp(2*p - 1) = sa
382  sp(2*p - 0) = sb
383 
384  if (order(p) > 1) sp(2*p - 1) = merge(merge(-1, +1, btest(signs, order(p) - 2)), +1, acc(order(p) - 1))
385  if (order(p) < n) sp(2*p - 0) = merge(merge(-1, +1, btest(signs, order(p) - 1)), +1, acc(order(p) + 1))
386 
387  acc(order(p)) = .true.
388 
389  kp(2*p - 1) = k(order(p) - 0)
390  kp(2*p - 0) = k(order(p) + 1)
391 
392  lp(2*p - 1) = l(order(p) - 0)
393  lp(2*p - 0) = l(order(p) + 1)
394 
395  end do
396 
397  ! evaluate integral for this hyper-triangle
398  integ = (-1)**popcnt(signs) * nested_coul_integ(a, c, n, mp, sp, lp, kp)
399 
400  ! add the integral value using the floating-point-error compensating routine
401  call kahan_add(val, integ, err)
402 
403  end do
404 
405  end do
406 
407  ! add the Green's function factors
408  do p = 2, n
409  val = val * imu / k(p)
410  end do
411 
412  end function nested_cgreen_integ
413 
414 end module multidip_integ
complex(wp) function nested_exp_integ(a, c, N, m, s, k)
Multi-dimensional triangular integral of exponentials and powers.
complex(wp) function nested_coul_integ(a, c, N, m, s, l, k)
Multi-dimensional triangular integral of Coulomb-Hankel functions and powers.
integer, parameter ntermsppt
Number of terms in expansion of the exponential integral.
subroutine kahan_add(X, dX, err)
Compensated summation.
logical function next_permutation(p)
Construct or advance permutation.
real(wp) function cphase(l, k)
Coulomb phase.
Hard-coded parameters of MULTIDIP.
Special integrals needed by MULTIDIP.
Special functions and objects used by MULTIDIP.
complex(wp) function nested_cgreen_integ(a, c, N, sa, sb, m, l, k)
Multi-dimensional integral of Coulomb-Hankel and Coulomb-Green functions and powers.
integer, parameter ntermsasy
Number of terms in expansion of the Coulomb-Hankel functions.