Multidip  1.0
Multi-photon matrix elements
multidip_routines.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  ! GBTOlib
32  use blas_lapack_gbl, only: blasint
33  use phys_const_gbl, only: pi, imu, to_ev
34  use precisn_gbl, only: wp
35 
36  implicit none
37 
48  type intermediatestate
49 
50  integer :: order
51  integer :: mgvn
52  integer :: dcomp
53 
58  real(wp), allocatable :: ck(:, :, :)
59 
64  real(wp), allocatable :: ap(:, :, :)
65 
72  real(wp), allocatable :: dip(:, :, :)
73 
75  type(intermediatestate), pointer :: parent => null()
76 
78  type(intermediatestate), pointer :: prev => null()
79  type(intermediatestate), pointer :: next => null()
80 
81  end type intermediatestate
82 
83 contains
84 
93  subroutine multidip_main
94 
95  use multidip_io, only: moleculardata, kmatrix, scatakcoeffs, read_wfncoeffs, read_kmatrices, read_molecular_data, &
96  myproc, nprocs
97  use multidip_params, only: nmaxphotons
98  use multidip_tests, only: run_tests
99 
100  type(MolecularData) :: moldat
101  type(KMatrix), allocatable :: km(:)
102  type(ScatAkCoeffs), allocatable :: ak(:)
103 
104  character(len=64) :: arg
105  integer :: order, nirr, lusct(8), lukmt(8), nkset(8), iarg, narg, input, i
106  logical :: verbose
107  real(wp) :: omega(nMaxPhotons)
108  complex(wp) :: polar(3, nMaxPhotons)
109 
110  namelist /mdip/ order, lusct, lukmt, nkset, polar, omega, verbose
111 
112  order = 1
113  lusct = 0
114  lukmt = 0
115  nkset = 1
116  polar = 0
117  omega = 0
118  verbose = .false.
119 
120  call print_ukrmol_header(output_unit)
121 
122  print '(/,A)', 'Program MULTIDIP: Calculation of multi-photon ionization'
123 
124  iarg = 1
125  narg = command_argument_count()
126  input = input_unit
127 
128  do while (iarg <= narg)
129  call get_command_argument(iarg, arg)
130  if (arg == '--test') then
131  call run_tests
132  return
133  else if (arg == '--input') then
134  iarg = iarg + 1
135  call get_command_argument(iarg, arg)
136  open (input, file = trim(arg), action = 'read', form = 'formatted')
137  else if (arg == '--help') then
138  print '(/,A,/)', 'Available options:'
139  print '(A)', ' --help display this help and exit'
140  print '(A)', ' --input FILE use FILE as the input file (instead of standard input)'
141  print '(A)', ' --test run internal sanity checks and exit'
142  print *
143  return
144  else
145  print '(3A)', 'Unknown command line argument "', arg, '"'
146  stop 1
147  end if
148  iarg = iarg + 1
149  end do
150 
151  read (input, nml = mdip)
152 
153  if (order < 1) then
154  print '(A)', 'Order must be positive'
155  stop 1
156  end if
157 
158  if (order > nmaxphotons) then
159  print '(A,I0)', 'Order must be <= ', nmaxphotons
160  end if
161 
162  polar(:, order + 1:nmaxphotons) = 0
163  omega(order + 1:nmaxphotons) = 0
164 
165  if (input /= input_unit) close (input)
166 
167 #ifdef WITH_COARRAYS
168  myproc = this_image()
169  nprocs = num_images()
170  print '(/,A,I0,A,I0)', 'Parallel mode; image ', myproc, ' of ', nprocs
171 #endif
172 
173  print '(/,A,/)', 'Absorbed photons'
174  do i = 1, order
175  print '(2x,A,I0,3x,A,SP,2F6.3,A,2F6.3,A,2F6.3,A,F0.3,A)', &
176  '#', i, 'eX: ', polar(1, i), 'i, eY: ', polar(2, i), 'i, eZ: ', polar(3, i), 'i, energy: ', omega(i) * to_ev, ' eV'
177  end do
178 
179  nirr = count(lukmt > 0)
180 
181  allocate (km(nirr))
182 
183  if (any(lusct > 0)) then
184  allocate (ak(nirr))
185  call read_wfncoeffs(ak, lusct)
186  end if
187 
188  call read_kmatrices(km, lukmt, nkset)
189  call read_molecular_data(moldat)
190 
191  call multidip_driver(order, moldat, km, ak, omega, polar, verbose)
192 
193  end subroutine multidip_main
194 
195 
222  subroutine multidip_driver (order, moldat, km, ak, omega, polar, verbose)
224  use multidip_io, only: moleculardata, kmatrix, scatakcoeffs, nprocs, get_diptrans
225 
226  integer, intent(in) :: order
227  logical, intent(in) :: verbose
228  type(MolecularData), intent(in) :: moldat
229  type(KMatrix), allocatable, intent(in) :: km(:)
230  type(ScatAkCoeffs), allocatable, intent(in) :: ak(:)
231  real(wp), intent(in) :: omega(:)
232  complex(wp), intent(in) :: polar(:, :)
233 
234  type(IntermediateState), pointer :: states, state
235 
236  integer, allocatable :: iidip(:), ifdip(:)
237 
238  integer :: j, s, mgvni, mgvnn, mgvn1, mgvn2, nesc, irri, iki, icomp, nstati, nchani
239 
240  iki = 1 ! which K-matrix file (and Ak file) to use for the initial state
241  mgvni = km(iki) % mgvn ! IRR of the initial state
242  nesc = km(iki) % nescat ! number of scattering energies
243  irri = minloc(abs(moldat % mgvns - mgvni), 1) ! internal index of initial IRR in molecular_data
244  nstati = moldat % mnp1(irri) ! number of inner region states in initial IRR
245  nchani = moldat % nchan(irri) ! number of outer region channels in initial IRR
246 
247  allocate (states)
248  allocate (states % ck(nstati, 2, (nesc + nprocs - 1) / nprocs))
249  allocate (states % ap(nchani, 2, (nesc + nprocs - 1) / nprocs))
250 
251  ! construct representation of the initial state
252  states % order = 0
253  states % mgvn = mgvni
254  states % dcomp = 0
255  states % ck(:, :, :) = 0
256  states % ck(1, 1, :) = 1 ! only the first inner state is populated (at all scattering energies)
257  states % ap(:, :, :) = 0
258 
259  ! for all absorbed photons
260  do j = 1, order
261 
262  print '(/,2(A,I0))', 'Photon ', j, ' of ', order
263 
264  ! for all components of the dipole operator
265  do icomp = 1, 3
266 
267  call get_diptrans(moldat, icomp, iidip, ifdip)
268 
269  ! for all transitions mediated by this component that are stored in molecular_data
270  do s = 1, size(iidip)
271 
272  mgvn1 = iidip(s) - 1
273  mgvn2 = ifdip(s) - 1
274 
275  ! apply this transition to all relevant previous intermediate states
276  state => states
277  do while (associated(state))
278 
279  ! process states of previous order
280  if (state % order + 1 == j) then
281 
282  ! IRR of the previous intermediate state (i.e. last element in MGVN history chain)
283  mgvnn = state % mgvn
284 
285  ! skip this transition if it is not applicable to the current IRR or there are no K-matrices for it
286  if ((mgvnn == mgvn1 .or. mgvnn == mgvn2) .and. &
287  any(km(:) % mgvn == mgvn1) .and. &
288  any(km(:) % mgvn == mgvn2)) then
289 
290  ! either calculate the next intermediate state or evaluate the dipole elements
291  if (j < order) then
292  call solve_intermediate_state(moldat, order, omega, irri, icomp, s, &
293  mgvnn, mgvn1, mgvn2, km, state, verbose)
294  else
295  call extract_dipole_elements(moldat, order, omega, irri, icomp, s, &
296  mgvnn, mgvn1, mgvn2, km, ak, state, verbose)
297  end if
298 
299  end if
300 
301  end if
302 
303  ! move on to the next state
304  state => state % next
305 
306  end do
307 
308  end do
309 
310  end do
311 
312  end do
313 
314  ! calculate partial wave dipoles and cross sections
315  call calculate_observables(moldat, order, states, km(iki) % escat, moldat % eig(1, irri), omega, polar)
316 
317  ! clean up the linked list
318  state => states
319  do while (associated(state % next))
320  state => state % next
321  deallocate (state % prev)
322  end do
323  deallocate (state)
324 
325  end subroutine multidip_driver
326 
327 
351  subroutine solve_intermediate_state (moldat, order, Ephoton, irri, icomp, s, mgvnn, mgvn1, mgvn2, km, state, verbose)
353  use multidip_io, only: moleculardata, kmatrix, scatakcoeffs, myproc, nprocs, apply_boundary_amplitudes, &
357 
358  type(MolecularData), intent(in) :: moldat
359  type(KMatrix), allocatable, intent(in) :: km(:)
360 
361  type(IntermediateState), pointer, intent(inout) :: state
362  type(IntermediateState), pointer :: last
363 
364  integer, intent(in) :: order, irri, icomp, s, mgvnn, mgvn1, mgvn2
365  logical, intent(in) :: verbose
366  real(wp), intent(in) :: Ephoton(:)
367 
368  integer :: nstatn, nstatf, nchann, nchanf, nopen, mgvnf, irrn, irrf, ikn, ikf, ipw, nesc, ie, mye
369  real(wp) :: Ei, IP, Etotf, dE = 0.1
370  character(len=1) :: transp
371 
372  complex(wp), allocatable :: beta(:), app(:), H(:, :), lambda(:), hc(:), hcp(:)
373 
374  integer, allocatable :: lf(:)
375  real(wp), allocatable :: Ef(:), fc(:), fcp(:), gc(:), gcp(:), P(:), rhs(:, :), omega(:)
376  real(wp), allocatable :: Pw(:, :), wPw(:, :), wPD(:, :), IwPD(:, :), wckp(:, :), Dpsi(:, :, :), corr(:, :), ckp(:, :)
377 
378  integer :: i
379 
380  if (mgvnn == mgvn1) then
381  mgvnf = mgvn2
382  transp = 'T'
383  else
384  mgvnf = mgvn1
385  transp = 'N'
386  end if
387 
388  irrn = minloc(abs(moldat % mgvns - mgvnn), 1) ! = findloc(moldat % mgvns, mgvnn, 1)
389  irrf = minloc(abs(moldat % mgvns - mgvnf), 1) ! = findloc(moldat % mgvns, mgvnf, 1)
390 
391  ikn = minloc(abs(km(:) % mgvn - mgvnn), 1) ! = findloc(km(:) % mgvn, mgvnn, 1)
392  ikf = minloc(abs(km(:) % mgvn - mgvnf), 1) ! = findloc(km(:) % mgvn, mgvnf, 1)
393 
394  nstatn = moldat % mnp1(irrn)
395  nstatf = moldat % mnp1(irrf)
396 
397  nchann = moldat % nchan(irrn)
398  nchanf = moldat % nchan(irrf)
399 
400  ei = moldat % eig(1, irri)
401  nesc = km(ikf) % nescat
402  mye = 0
403 
404  allocate (pw(nstatf, nchanf), wpd(nchanf, 2), iwpd(nchanf, 2), wpw(nchanf, nchanf), wckp(nchanf, 2), omega(order), &
405  dpsi(nstatf, 2, (nesc + nprocs - 1) / nprocs), fc(nchanf), gc(nchanf), fcp(nchanf), gcp(nchanf), &
406  hc(nchanf), hcp(nchanf), corr(nchanf, 2), &
407  ef(nchanf), lf(nchanf), ckp(nstatf, 2), h(nchanf, nchanf), lambda(nchanf), beta(nchanf), &
408  app(nchanf))
409 
410  print '(2x,A,I0,A,I0,5A)', 'Transition ', mgvnn, ' -> ', mgvnf, ' (', compt(icomp), ', ', transp, ')'
411 
412  ! set up a new intermediate state at the end of the storage chain
413  last => state
414  do while (associated(last % next))
415  last => last % next
416  end do
417  allocate (last % next)
418  last % next % prev => last
419  last => last % next
420  last % order = state % order + 1
421  last % mgvn = mgvnf
422  last % dcomp = icomp
423  last % parent => state
424  allocate (last % ck(nstatf, 2, (nesc + nprocs - 1) / nprocs))
425  allocate (last % ap(nchanf, 2, (nesc + nprocs - 1) / nprocs))
426 
427  ! apply the inner dipoles matrix on the previous inner region solution expansions
428  call apply_dipole_matrix(moldat, icomp, s, transp, nstatf, nstatn, state % ck, dpsi)
429 
430  ! for all scattering energies
431  do ie = myproc, nesc, nprocs
432 
433  ! quantum numbers of the final state
434  mye = mye + 1
435  ip = km(ikf) % escat(ie) + moldat % etarg(1) - ei
436  call calculate_photon_energies(ip, ephoton, omega)
437  etotf = ei + sum(omega(1 : last % order))
438  ef = etotf - moldat % etarg(moldat % ichl(1:nchanf, irrf))
439  lf = moldat % l2p(1:nchanf, irrf)
440  nopen = count(ef > 0)
441 
442  if (verbose) print '(4x,*(A,I0))', 'energy ', ie, ' of ', nesc
443  if (verbose) print '(4x,A,I0)', ' - open channels: ', nopen
444 
445  ! redefine the number of open channels to include also weakly closed ones
446  if (closed_interm) then
447  nopen = count(ef + de > 0)
448  if (verbose) print '(4x,A,I0)', ' - open + weakly closed channels: ', nopen
449  end if
450 
451  ! evaluate Coulomb functions at this energy for all partial waves in the final IRR
452  fc = 0; fcp = 0; gc = 0; gcp = 0; hc = 0; hcp = 0; lambda = 0
453  do ipw = 1, nopen
454  call coul(lf(ipw), ef(ipw), moldat % rmatr, fc(ipw), fcp(ipw), gc(ipw), gcp(ipw))
455  hc(ipw) = cmplx(gc(ipw), fc(ipw), wp)
456  hcp(ipw) = cmplx(gcp(ipw), fcp(ipw), wp)
457  lambda(ipw) = hcp(ipw) / hc(ipw)
458  end do
459 
460  ! inner region part of the right-hand side of the master equation
461  rhs = dpsi(:, :, mye)
462 
463  ! outer region correction to the right-hand side
464  call multiint(moldat, ei, omega, mye, last, +1, beta)
465  beta = -2 * beta / sqrt(2*abs(ef))
466  where (ef < 0) beta = beta / imu
467  corr(:, 1) = real(beta * (fcp - lambda * fc), wp)
468  corr(:, 2) = aimag(beta * (fcp - lambda * fc))
469  call apply_boundary_amplitudes(moldat, irrf, 'T', corr, dpsi(:, :, mye))
470  rhs = rhs - 0.5 * dpsi(:, :, mye)
471 
472  ! solve the master equation using the Sherman-Morrison-Woodbury formula
473  p = 1 / (etotf - moldat % eig(1:nstatf, irrf))
474  ckp(:, 1) = p * rhs(:, 1)
475  ckp(:, 2) = p * rhs(:, 2)
476 
477  if (nopen > 0) then
478 
479  ! compose the reduced inverse Hamiltonian
480  h = 0
481  call scale_boundary_amplitudes(moldat, irrf, p, pw)
482  call apply_boundary_amplitudes(moldat, irrf, 'N', pw, wpw)
483  h(1:nopen, 1:nopen) = wpw(1:nopen, 1:nopen)
484  forall (i = 1:nopen) h(i, i) = h(i, i) + 2/lambda(i)
485  forall (i = nopen + 1:nchanf) h(i, i) = 1
486  call apply_boundary_amplitudes(moldat, irrf, 'N', ckp, wpd)
487 
488  ! solve the reduced system
489  iwpd = 0
490  call solve_complex_system(nopen, h, wpd, iwpd)
491 
492  ! expand the reduced solution to the full solution
493  call apply_boundary_amplitudes(moldat, irrf, 'T', iwpd, ckp)
494  ckp(:, 1) = p * (rhs(:, 1) - ckp(:, 1))
495  ckp(:, 2) = p * (rhs(:, 2) - ckp(:, 2))
496 
497  end if
498 
499  ! extract the outer region channel amplitudes
500  call apply_boundary_amplitudes(moldat, irrf, 'N', ckp, wckp)
501  app = cmplx(wckp(:, 1), wckp(:, 2), wp)
502  where (hc /= 0)
503  app = (app - beta*fc) / hc
504  elsewhere
505  app = 0
506  end where
507 
508  if (verbose) print '(4x,A,*(E25.15))', ' - beta: ', beta(1:nopen)
509  if (verbose) print '(4x,A,*(E25.15))', ' - ap: ', app(1:nopen)
510 
511  last % ck(:, 1, mye) = ckp(:, 1)
512  last % ck(:, 2, mye) = ckp(:, 2)
513 
514  last % ap(:, 1, mye) = real(app, wp)
515  last % ap(:, 2, mye) = aimag(app)
516 
517  end do
518 
519  end subroutine solve_intermediate_state
520 
521 
543  subroutine extract_dipole_elements (moldat, order, Ephoton, irri, icomp, s, mgvnn, mgvn1, mgvn2, km, ak, state, verbose)
545  use multidip_io, only: moleculardata, kmatrix, scatakcoeffs, myproc, nprocs, apply_dipole_matrix
546  use multidip_params, only: rone, rzero, cone, czero, compt
547  use multidip_special, only: scatak,calculate_s_matrix, blas_dgemv => dgemv, blas_zgemv => zgemv
548 
549  type(MolecularData), intent(in) :: moldat
550  type(KMatrix), allocatable, intent(in) :: km(:)
551  type(ScatAkCoeffs), allocatable, intent(in) :: ak(:)
552 
553  type(IntermediateState), pointer, intent(inout) :: state
554  type(IntermediateState), pointer :: last
555 
556  integer, intent(in) :: irri, order, icomp, s, mgvnn, mgvn1, mgvn2
557  logical, intent(in) :: verbose
558  real(wp), intent(in) :: Ephoton(:)
559 
560  character(len=1) :: transp
561 
562  complex(wp), allocatable :: Af(:, :), dm(:), dp(:), Sm(:, :)
563  real(wp), allocatable :: Dpsi(:, :, :), Ef(:), echlf(:), d_inner(:, :), d_outer(:, :), Re_Af(:, :), Im_Af(:, :)
564  real(wp), allocatable :: omega(:), d_total(:, :)
565  integer, allocatable :: lf(:)
566 
567  real(wp) :: Etotf, Ei, IP
568  integer :: mgvnf, irrn, irrf, ikn, ikf, nstatn, nstatf, nchann, nchanf, nesc, ie, nopen, mye
569 
570  integer(blasint) :: m, n, inc = 1
571 
572  if (mgvnn == mgvn1) then
573  mgvnf = mgvn2
574  transp = 'T'
575  else
576  mgvnf = mgvn1
577  transp = 'N'
578  end if
579 
580  irrn = minloc(abs(moldat % mgvns - mgvnn), 1) ! = findloc(moldat % mgvns, mgvnn, 1)
581  irrf = minloc(abs(moldat % mgvns - mgvnf), 1) ! = findloc(moldat % mgvns, mgvnf, 1)
582 
583  ikn = minloc(abs(km(:) % mgvn - mgvnn), 1) ! = findloc(km(:) % mgvn, mgvnn, 1)
584  ikf = minloc(abs(km(:) % mgvn - mgvnf), 1) ! = findloc(km(:) % mgvn, mgvnf, 1)
585 
586  nstatn = moldat % mnp1(irrn)
587  nstatf = moldat % mnp1(irrf)
588 
589  nchann = km(ikn) % nchan
590  nchanf = km(ikf) % nchan
591 
592  ei = moldat % eig(1, irri)
593  nesc = km(ikf) % nescat
594  mye = 0
595 
596  allocate (dpsi(nstatf, 2, (nesc + nprocs - 1) / nprocs), d_inner(nchanf, 2), d_outer(nchanf, 2), dm(nchanf), dp(nchanf), &
597  sm(nchanf, nchanf), omega(order), d_total(nchanf, 2), ef(nchanf), lf(nchanf), echlf(nchanf))
598 
599  print '(2x,A,I0,A,I0,5A)', 'Transition ', mgvnn, ' -> ', mgvnf, ' (', compt(icomp), ', ', transp, ')'
600 
601  ! set up a final state at the end of the storage chain
602  last => state
603  do while (associated(last % next))
604  last => last % next
605  end do
606  allocate (last % next)
607  last % next % prev => last
608  last => last % next
609  last % order = order
610  last % mgvn = mgvnf
611  last % dcomp = icomp
612  last % parent => state
613  allocate (last % dip(nchanf, 2, nesc))
614 
615  ! apply the inner dipoles matrix on the previous inner region solution expansions
616  call apply_dipole_matrix(moldat, icomp, s, transp, nstatf, nstatn, state % ck, dpsi)
617 
618  ! for all scattering energies
619  do ie = myproc, nesc, nprocs
620 
621  ! quantum numbers of the final state
622  mye = mye + 1
623  etotf = km(ikf) % escat(ie) + moldat % etarg(1)
624  ip = etotf - ei
625  call calculate_photon_energies(ip, ephoton, omega)
626  echlf = moldat % etarg(moldat % ichl(1:nchanf, irrf)) - moldat % etarg(1)
627  ef = km(ikf) % escat(ie) - echlf
628  nopen = count(ef > 0)
629  lf = moldat % l2p(1:nchanf, irrf)
630 
631  if (verbose) print '(4x,*(A,I0))', 'energy ', ie, ' of ', nesc
632 
633  ! obtain wave function coefficients in the final IRR from the Ak file data structure, or calculate them on the fly
634  if (allocated(ak)) then
635  re_af = ak(ikf) % ReA(:, :, mye)
636  im_af = ak(ikf) % ImA(:, :, mye)
637  else
638  if (.not. allocated(af)) allocate (af(nstatf, nchanf))
639 
640  call scatak(moldat % rmatr, &
641  moldat % eig(1:nstatf, irrf), &
642  etotf, &
643  moldat % wamp(1:nchanf, 1:nstatf, irrf), &
644  ef, &
645  lf, &
646  km(ikf) % kmat(:, :, mye), &
647  af)
648 
649  re_af = real(Af, wp)
650  im_af = aimag(af)
651  end if
652 
653  ! contract D*psi with complex-conjugated wave function coefficients Ak
654  m = nstatf
655  n = nchanf
656  call blas_dgemv('T', m, n, rone, re_af, m, dpsi(:, 1, mye), inc, rzero, d_inner(:, 1), inc)
657  call blas_dgemv('T', m, n, +rone, im_af, m, dpsi(:, 2, mye), inc, +rone, d_inner(:, 1), inc)
658  call blas_dgemv('T', m, n, rone, re_af, m, dpsi(:, 2, mye), inc, rzero, d_inner(:, 2), inc)
659  call blas_dgemv('T', m, n, -rone, im_af, m, dpsi(:, 1, mye), inc, +rone, d_inner(:, 2), inc)
660 
661  ! calculate S-matrix for this energy
662  call calculate_s_matrix(km(ikf) % kmat(:, :, mye), sm, nopen)
663 
664  ! outer region correction
665  call multiint(moldat, ei, omega, mye, last, -1, dm)
666  call multiint(moldat, ei, omega, mye, last, +1, dp)
667  where (ef > 0)
668  dm = dm * imu / sqrt(2*pi*sqrt(2*ef))
669  dp = dp * imu / sqrt(2*pi*sqrt(2*ef))
670  elsewhere
671  dm = 0
672  dp = 0
673  end where
674  call blas_zgemv('T', n, n, -cone, sm, n, dp, inc, cone, dm, inc) ! dm = dm - S^T dp
675  d_outer(:, 1) = real(dm, wp)
676  d_outer(:, 2) = aimag(dm)
677 
678  ! combine and store the partial wave dipoles
679  d_total = d_inner + d_outer
680  last % dip(:, :, ie) = d_total
681 
682  if (verbose) print '(5x,A,1x,*(1x,E25.15))', 'd_inner = ', (d_inner(n, 1), d_inner(n, 2), n = 1, nopen)
683  if (verbose) print '(5x,A,1x,*(1x,E25.15))', 'd_outer = ', (d_outer(n, 1), d_outer(n, 2), n = 1, nopen)
684  if (verbose) print '(5x,A,1x,*(1x,E25.15))', 'd_total = ', (d_total(n, 1), d_total(n, 2), n = 1, nopen)
685 
686  end do
687 
688  end subroutine extract_dipole_elements
689 
690 
703  subroutine calculate_photon_energies (IP, Ephoton, omega)
705  real(wp), intent(in) :: IP, Ephoton(:)
706  real(wp), allocatable, intent(inout) :: omega(:)
707 
708  where (ephoton /= 0)
709  omega = ephoton
710  elsewhere
711  omega = (ip - sum(ephoton)) / (size(omega) - count(ephoton /= 0))
712  end where
713 
714  end subroutine calculate_photon_energies
715 
716 
734  subroutine multiint (moldat, Ei, omega, ie, state, sb, dip)
736  use multidip_io, only: moleculardata
737  use multidip_params, only: closed_interm
738 
739  type(MolecularData), intent(in) :: moldat
740  type(IntermediateState), pointer, intent(in) :: state
741 
742  complex(wp), allocatable, intent(inout) :: dip(:)
743  real(wp), allocatable, intent(inout) :: omega(:)
744  real(wp), intent(in) :: Ei
745  integer, intent(in) :: ie, sb
746 
747  complex(wp), allocatable :: k(:)
748  integer, allocatable :: l(:), m(:)
749 
750  real(wp) :: c, a, Ekf, Etot
751  integer :: nphot, mgvn, nchan, ichan, irr
752 
753  dip = 0
754 
755  if (.not. associated(state % parent)) stop 1 ! state with no parent should not be a final state in the first place
756 
757  a = moldat % rmatr ! R-matrix radius
758  c = 0 ! dipole damping factor
759  nphot = state % order ! number of photons absorbed by the final state
760  etot = ei + sum(omega(1:nphot)) ! total energy of the final state
761  mgvn = state % mgvn ! MGVN of the final state
762  irr = minloc(abs(moldat % mgvns - mgvn), 1) ! = findloc(moldat % mgvns, mgvn, 1)
763  nchan = moldat % nchan(irr) ! number of channels in the final irreducible representation
764 
765  allocate (k(nphot), l(nphot), m(nphot - 1))
766 
767  do ichan = 1, nchan
768 
769  ! set up quantum numbers for this channel
770  ekf = etot - moldat % etarg(moldat % ichl(ichan, irr))
771  if (ekf < 0 .and. .not. closed_interm) cycle ! ignore closed channels
772  k(1) = sqrt(2*abs(ekf)); if (ekf < 0) k(1) = k(1) * imu
773  l(1) = moldat % l2p(ichan, irr)
774 
775  ! evaluate the correction dipole integral for this final channel
776  dip(ichan) = multiint_chain(moldat, ei, omega, ie, c, 1, state, ichan, sb, k, l, m)
777 
778  end do
779 
780  end subroutine multiint
781 
782 
805  recursive complex(wp) function multiint_chain (moldat, Ei, omega, ie, c, N, state, ichanf, sb, k, l, m) result (integ)
808  use multidip_io, only: moleculardata
809  use multidip_params, only: closed_interm
810 
811  type(moleculardata), intent(in) :: moldat
812  type(intermediatestate), pointer, intent(in) :: state
813  type(intermediatestate), pointer :: parent
814 
815  complex(wp), allocatable, intent(inout) :: k(:)
816  real(wp), allocatable, intent(inout) :: omega(:)
817  integer, allocatable, intent(inout) :: l(:), m(:)
818  real(wp), intent(in) :: ei
819  integer, intent(in) :: ie, sb, n, ichanf
820 
821  complex(wp) :: ap, kr(n + 1)
822  real(wp) :: c, a, ek, qion, qpws, etot
823  integer :: i, mgvni, mgvnf, nchani, irri, ichani, nphot, dcomp, lr(n + 1), mr(n)
824 
825  integ = 0
826 
827  if (state % parent % order == 0) return ! if the parent is the initial state, there are not tails to integrate
828 
829  a = moldat % rmatr ! R-matrix radius
830  dcomp = state % dcomp ! last polarisation component absorbed by the final state
831  parent => state % parent ! previous intermediate state
832  nphot = parent % order ! number of photons absorbed by the previous intermediate state
833  mgvnf = state % mgvn ! irreducible representation of the current state
834  mgvni = parent % mgvn ! irreducible representation of the previos state
835  irri = minloc(abs(moldat % mgvns - mgvni), 1) ! = findloc(moldat % mgvns, mgvni, 1)
836  nchani = moldat % nchan(irri) ! number of channels in the previous state irreducible representation
837  etot = ei + sum(omega(1:nphot)) ! total energy of the previous intermediate state
838 
839  ! loop over all channels of the intermediate state that are dipole-coupled to ichanf
840  do ichani = 1, nchani
841 
842  ! set up quantum numbers for this channel
843  ek = etot - moldat % etarg(moldat % ichl(ichani, irri))
844  if (ek < 0 .and. .not. closed_interm) cycle ! ignore closed channels
845  k(n + 1) = sqrt(2*abs(ek)); if (ek < 0) k(n + 1) = k(n + 1) * imu
846  l(n + 1) = moldat % l2p(ichani, irri)
847  forall (i = 1:n+1) kr(i) = k(n + 2 - i)
848  forall (i = 1:n+1) lr(i) = l(n + 2 - i)
849 
850  ! dipole angular integrals
851  qion = channel_coupling_ion(moldat, dcomp, mgvnf, mgvni, ichanf, ichani)
852  qpws = channel_coupling_pws(moldat, dcomp, mgvnf, mgvni, ichanf, ichani)
853 
854  ! outer region expansion coefficient for this partial wave and energy
855  ap = cmplx(parent % ap(ichani, 1, ie), parent % ap(ichani, 2, ie), wp)
856 
857  ! evaluate Coulomb-Green's integrals for dipole-coupled ions
858  if (qion /= 0) then
859  m(n) = 0
860  forall (i = 1:n) mr(i) = m(n + 1 - i)
861  integ = integ + qion * ap * nested_cgreen_integ(a, c, n, +1, sb, mr, lr, kr)
862  integ = integ + qion * multiint_chain(moldat, ei, omega, ie, c, n + 1, parent, ichani, sb, k, l, m)
863  end if
864 
865  ! evaluate Coulomb-Green's integrals for dipole-coupled partial waves
866  if (qpws /= 0) then
867  m(n) = 1
868  forall (i = 1:n) mr(i) = m(n + 1 - i)
869  integ = integ + qpws * ap * nested_cgreen_integ(a, c, n, +1, sb, mr, lr, kr)
870  integ = integ + qpws * multiint_chain(moldat, ei, omega, ie, c, n + 1, parent, ichani, sb, k, l, m)
871  end if
872 
873  end do
874 
875  end function multiint_chain
876 
877 
893  real(wp) function channel_coupling_ion (moldat, dcomp, mgvnf, mgvni, ichanf, ichani) result (Qion)
895  use multidip_io, only: moleculardata
896 
897  type(moleculardata), intent(in) :: moldat
898  integer, intent(in) :: dcomp, mgvnf, mgvni, ichanf, ichani
899 
900  integer :: tr(3) = [ 3, 1, 2 ] ! how crlv is stored: x is last, y first, z in between
901  integer :: irri, irrf, itargi, itargf, li, lf, mi, mf
902 
903  irrf = minloc(abs(moldat % mgvns - mgvnf), 1) ! = findloc(moldat % mgvns, mgvnf, 1)
904  irri = minloc(abs(moldat % mgvns - mgvni), 1) ! = findloc(moldat % mgvns, mgvni, 1)
905 
906  lf = moldat % l2p(ichanf, irrf); mf = moldat % m2p(ichanf, irrf); itargf = moldat % ichl(ichanf, irrf)
907  li = moldat % l2p(ichani, irri); mi = moldat % m2p(ichani, irri); itargi = moldat % ichl(ichani, irri)
908 
909  if (lf /= li .or. mf /= mi) then
910  qion = 0
911  else
912  qion = moldat % crlv(itargf, itargi, tr(dcomp))
913  end if
914 
915  end function channel_coupling_ion
916 
917 
932  real(wp) function channel_coupling_pws (moldat, dcomp, mgvnf, mgvni, ichanf, ichani) result (Qpws)
934  use multidip_io, only: moleculardata
935 
936  type(moleculardata), intent(in) :: moldat
937  integer, intent(in) :: dcomp, mgvnf, mgvni, ichanf, ichani
938 
939  integer :: m(3) = [ +1, -1, 0 ] ! real spherical harmonics Y1m associated with directions x,y,z
940  integer :: irri, irrf, itargi, itargf, li, lf, mi, mf, ipwi, ipwf
941 
942  irrf = minloc(abs(moldat % mgvns - mgvnf), 1) ! = findloc(moldat % mgvns, mgvnf, 1)
943  irri = minloc(abs(moldat % mgvns - mgvni), 1) ! = findloc(moldat % mgvns, mgvni, 1)
944 
945  itargf = moldat % ichl(ichanf, irrf)
946  itargi = moldat % ichl(ichani, irri)
947 
948  if (itargf /= itargi) then
949  qpws = 0
950  else
951  lf = moldat % l2p(ichanf, irrf); mf = moldat % m2p(ichanf, irrf); ipwf = lf*(lf + 1) + mf + 1
952  li = moldat % l2p(ichani, irri); mi = moldat % m2p(ichani, irri); ipwi = li*(li + 1) + mi + 1
953  qpws = sqrt(4*pi/3) * moldat % gaunt(ipwf, ipwi, m(dcomp))
954  end if
955 
956  end function channel_coupling_pws
957 
958 
989  subroutine calculate_observables (moldat, order, state, escat, Ei, Ephoton, polar)
992  use multidip_params, only: alpha
993  use multidip_special, only: cphase
994 
995  type(MolecularData), intent(in) :: moldat
996  type(IntermediateState), pointer, intent(in) :: state
997  type(IntermediateState), pointer :: ptr, parent
998 
999  integer, intent(in) :: order
1000  real(wp), intent(in) :: escat(:)
1001  complex(wp), intent(in) :: polar(:, :)
1002  real(wp), intent(in) :: Ei, Ephoton(:)
1003 
1004  integer :: i, nesc, mxchan, lf, ichan, ie, irr, mgvn
1005 #ifdef WITH_COARRAYS
1006  real(wp), allocatable :: cs(:, :)[:]
1007  complex(wp), allocatable :: M(:, :, :)[:]
1008 #else
1009  real(wp), allocatable :: cs(:, :)
1010  complex(wp), allocatable :: M(:, :, :)
1011 #endif
1012  real(wp), allocatable :: omega(:)
1013  real(wp) :: Ef, kf, sigma, navg, IP, Q
1014  complex(wp) :: ej, d
1015 
1016  nesc = size(escat)
1017  mxchan = maxval(moldat % nchan)
1018 
1019  ! set up final storage
1020 #ifdef WITH_COARRAYS
1021  allocate (cs(2, nesc)[*], m(mxchan, nesc, 0:7)[*])
1022 #else
1023  allocate (cs(2, nesc), m(mxchan, nesc, 0:7))
1024 #endif
1025  allocate (omega(order))
1026 
1027  cs = 0
1028  m = 0
1029 
1030  ! the dipole elements stored in molecular_data do not contain the charge, so for each transition we need a factor of -1
1031  q = (-1)**order
1032 
1033  ! how many photons have unspecified (i.e. zero) polarization?
1034  navg = 0
1035  do i = 1, order
1036  if (all(polar(:, i) == 0)) then
1037  navg = navg + 1
1038  end if
1039  end do
1040 
1041  ! process contributions from all final states
1042  ptr => state
1043  do while (associated(ptr))
1044  if (ptr % order == order) then
1045 
1046  ! get polarisation factor for this absorption chain
1047  ej = polar(ptr % dcomp, ptr % order)
1048  parent => ptr
1049  do while (associated(parent % parent))
1050  ej = ej * polar(parent % dcomp, parent % order)
1051  parent => parent % parent
1052  end do
1053 
1054  ! add transition element contribution from this absorption chain
1055  do ie = myproc, nesc, nprocs
1056  do ichan = 1, size(ptr % dip, 1)
1057  d = cmplx(ptr % dip(ichan, 1, ie), ptr % dip(ichan, 2, ie), wp)
1058  m(ichan, ie, ptr % mgvn) = m(ichan, ie, ptr % mgvn) + q * ej * d
1059  cs(2, ie) = cs(2, ie) + real(d*conjg(d), wp)
1060  end do
1061  end do
1062 
1063  end if
1064  ptr => ptr % next
1065  end do
1066 
1067  ! write partial wave transition moments without Coulomb phase factor
1068  call write_partial_wave_moments(moldat, m, '')
1069 
1070  ! add prefactors
1071  do ie = myproc, nesc, nprocs
1072  ip = escat(ie) + moldat % etarg(1) - ei
1073  call calculate_photon_energies(ip, ephoton, omega)
1074  cs(1, ie) = omega(1) * to_ev
1075  do irr = 1, size(moldat % mgvns)
1076  mgvn = moldat % mgvns(irr)
1077  do ichan = 1, moldat % nchan(irr)
1078  lf = moldat % l2p(ichan, irr)
1079  ef = escat(ie) - moldat % etarg(moldat % ichl(ichan, irr)) + moldat % etarg(1)
1080  if (ef > 0) then
1081  kf = sqrt(2*ef)
1082  sigma = cphase(lf, kf)
1083  m(ichan, ie, mgvn) = m(ichan, ie, mgvn) * imu**(-lf) * (cos(sigma) + imu*sin(sigma))
1084  end if
1085  end do
1086  end do
1087  cs(2, ie) = cs(2, ie) * 2*pi * (2*pi*alpha)**order * product(omega) / 3**navg
1088  end do
1089 
1090  ! write partial wave transition moments with Coulomb phase factor
1091  call write_partial_wave_moments(moldat, m, '+cphase')
1092 
1093  ! write orientation-averaged cross section
1094  call write_cross_section(cs)
1095 
1096  end subroutine calculate_observables
1097 
1098 end module multidip_routines
complex(wp), parameter czero
subroutine write_cross_section(cs)
Write cross section to a file.
real(wp), parameter alpha
Fine structure constant.
subroutine apply_boundary_amplitudes(moldat, irr, transp, X, Y)
Multiply by boundary amplitudes.
integer, parameter nmaxphotons
The limit on number of photons is nested_cgreen_integ
subroutine scale_boundary_amplitudes(moldat, irr, v, vw)
Scale boundary amplitudes matrix by a diagonal matrix.
recursive complex(wp) function multiint_chain(moldat, Ei, omega, ie, c, N, state, ichanf, sb, k, l, m)
Calculate dipole correction integrals at given absorption depth.
subroutine run_tests
Numerical unit tests.
real(wp) function channel_coupling_ion(moldat, dcomp, mgvnf, mgvni, ichanf, ichani)
Ion channel dipole coupling.
MULTIDIP unit tests.
I/O routines used by MULTIDIP.
Definition: multidip_io.F90:29
subroutine read_molecular_data(moldat)
Read RMT molecular_data file.
subroutine get_diptrans(moldat, I, iidip, ifdip)
Return dipole transition descriptors.
subroutine solve_complex_system(n, A, X, Y)
Solve system of equations with complex matrix.
character(len=1), dimension(3), parameter compt
subroutine multidip_main
MULTIDIP main subroutine.
real(wp), parameter rzero
subroutine apply_dipole_matrix(moldat, I, s, transp, nf, nn, X, Y)
Multiply by dipole matrix.
real(wp) function cphase(l, k)
Coulomb phase.
real(wp), parameter rone
subroutine calculate_photon_energies(IP, Ephoton, omega)
Calculate energy of each photon.
logical, parameter closed_interm
Consider weakly closed channel in intermediate states (unfinished!)
subroutine solve_intermediate_state(moldat, order, Ephoton, irri, icomp, s, mgvnn, mgvn1, mgvn2, km, state, verbose)
Calculate intermediate photoionisation state.
subroutine multidip_driver(order, moldat, km, ak, omega, polar, verbose)
Central computation routine.
integer myproc
Definition: multidip_io.F90:44
Hard-coded parameters of MULTIDIP.
subroutine read_kmatrices(km, lukmt, nkset)
Read K-matrix files.
Special integrals needed by MULTIDIP.
Main MULTIDIP routines.
subroutine read_wfncoeffs(ak, lusct)
Read wave function coefficients from file.
subroutine extract_dipole_elements(moldat, order, Ephoton, irri, icomp, s, mgvnn, mgvn1, mgvn2, km, ak, state, verbose)
Calculate dipole elements from intermediate and final states.
real(wp) function channel_coupling_pws(moldat, dcomp, mgvnf, mgvni, ichanf, ichani)
Partial wave channel dipole coupling.
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.
subroutine calculate_observables(moldat, order, state, escat, Ei, Ephoton, polar)
Calculate partial wave dipoles, oriented dipoles and cross sections.
integer nprocs
Definition: multidip_io.F90:45
subroutine coul(l, Ek, r, F, Fp, G, Gp)
Coulomb functions.
subroutine write_partial_wave_moments(moldat, M, suffix)
Write partial wave moments.
subroutine multiint(moldat, Ei, omega, ie, state, sb, dip)
Evaluate the correction dipole integral for all orders.
complex(wp), parameter cone