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 #ifdef HAVE_NO_FINDLOC
32  ! supply missing intrinsic functions
33  use algorithms, only: findloc
34 #endif
35 
36  ! GBTOlib
37  use blas_lapack_gbl, only: blasint
38  use phys_const_gbl, only: pi, imu, to_ev
39  use precisn_gbl, only: wp
40 
41  implicit none
42 
54 
55  integer :: order
56  integer :: mgvn
57  integer :: dcomp
58 
63  real(wp), allocatable :: ck(:, :, :)
64 
69  real(wp), allocatable :: ap(:, :, :)
70 
77  real(wp), allocatable :: dip(:, :, :)
78 
80  type(intermediatestate), pointer :: parent => null()
81 
83  type(intermediatestate), pointer :: prev => null()
84  type(intermediatestate), pointer :: next => null()
85 
86  end type intermediatestate
87 
88  ! for debugging purposes
89  logical, parameter :: test_expansion = .false.
90 
91 contains
92 
101  subroutine multidip_main
102 
103  use, intrinsic :: iso_c_binding, only: c_ptr
104 
105  use mpi_gbl, only: comm_rank => myrank, comm_size => nprocs, mpi_mod_finalize, mpi_mod_start, mpi_xermsg
107  myproc, nprocs
109 #ifdef WITH_GSL
110  use multidip_special, only: gsl_set_error_handler_off
111 #endif
112  use multidip_tests, only: run_tests
113 
114  type(moleculardata) :: moldat
115  type(kmatrix), allocatable :: km(:)
116  type(scatakcoeffs), allocatable :: ak(:)
117 
118  character(len=256) :: arg, rmt_data, raw, msg
119  type(c_ptr) :: dummy
120 
121  integer :: order, nirr, lusct(8), lukmt(8), lubnd, nkset(8), iarg, narg, input, i, erange(2)
122  logical :: verbose, mpiio
123  real(wp) :: omega(nMaxPhotons), first_IP, rasym
124  complex(wp) :: polar(3, nMaxPhotons)
125 
126  call print_ukrmol_header(output_unit)
127 
128  print '(/,A)', 'Program MULTIDIP: Calculation of multi-photon ionization'
129 
130  iarg = 1
131  narg = command_argument_count()
132  input = input_unit
133 
134 #ifdef WITH_GSL
135  dummy = gsl_set_error_handler_off()
136 #endif
137 
138  do while (iarg <= narg)
139  call get_command_argument(iarg, arg)
140  if (arg == '--test') then
141  call run_tests
142  return
143  else if (arg == '--input') then
144  iarg = iarg + 1
145  call get_command_argument(iarg, arg)
146  open (input, file = trim(arg), action = 'read', form = 'formatted')
147  else if (arg == '--help') then
148  print '(/,A,/)', 'Available options:'
149  print '(A)', ' --help display this help and exit'
150  print '(A)', ' --input FILE use FILE as the input file (instead of standard input)'
151  print '(A)', ' --test run internal sanity checks and exit'
152  print *
153  return
154  else
155  write (msg, '(3a)') 'Unknown command line argument "', trim(arg), '"'
156  call mpi_xermsg('multidip_routines', 'multidip_main', trim(msg), 1, 1)
157  end if
158  iarg = iarg + 1
159  end do
160 
161  call read_input_namelist(input, order, lusct, lukmt, lubnd, nkset, polar, omega, verbose, rmt_data, first_ip, rasym, raw, &
162  erange, mpiio)
163 
164  if (input /= input_unit) close (input)
165 
166  call mpi_mod_start
167 
168  myproc = comm_rank + 1
169  nprocs = comm_size
170  print '(/,A,I0,A,I0)', 'Parallel mode; image ', myproc, ' of ', nprocs
171 
172  print '(/,A,/)', 'Absorbed photons'
173  do i = 1, order
174  print '(2x,A,I0,3x,A,SP,2F6.3,A,2F6.3,A,2F6.3,A,F0.3,A)', &
175  '#', i, 'eX: ', polar(1, i), 'i, eY: ', polar(2, i), 'i, eZ: ', polar(3, i), 'i, energy: ', omega(i) * to_ev, ' eV'
176  end do
177 
178  if (first_ip > 0) then
179  print '(/,A,F0.5,A)', 'Override first IP to ', first_ip * to_ev, ' eV'
180  else
181  print '(/,A)', 'Use calculated IP'
182  end if
183 
184  nirr = count(lukmt > 0)
185 
186  allocate (km(nirr))
187 
188  if (any(lusct > 0)) then
189  allocate (ak(nirr))
190  call read_wfncoeffs(ak, lusct)
191  end if
192 
193  call read_kmatrices(km, lukmt, nkset)
194  call read_molecular_data(moldat, trim(rmt_data), mpiio, test_expansion)
195 
196  if (any(erange /= 0)) then
197  if (erange(1) < 1 .or. erange(1) > erange(2) .or. erange(2) > minval(km % nescat)) then
198  print '(/,A,I0)', 'Error: Energy range must satisfy 1 <= erange(1) <= erange(2) <= ', minval(km % nescat)
199  end if
200  else
201  erange(1) = 1
202  erange(2) = minval(km % nescat)
203  end if
204 
205  if (rasym > moldat % rmatr) then
206  if (order <= 2) then
207  write (*, '(/,A,F0.1,A,F0.1,A)', advance = 'no') 'Interval ', moldat % rmatr, ' to ', rasym, &
208  ' will be integrated numerically using '
209  select case (num_integ_algo)
210  case (1); print '(a)', 'Romberg quadrature'
211  case (2); print '(a)', 'Levin quadrature'
212  end select
213  else
214  call mpi_xermsg('multidip_routines', 'multidip_main', &
215  'Error: Numerical integration (rasym) is currently only implemented for the second order.', 1, 1)
216  end if
217  end if
218 
219  call multidip_driver(order, moldat, km, ak, lubnd, omega(1:order), polar(:, 1:order), verbose, first_ip, rasym, raw, erange)
220 
221  call mpi_mod_finalize
222 
223  end subroutine multidip_main
224 
225 
257  subroutine multidip_driver (order, moldat, km, ak, lubnd, omega, polar, verbose, first_IP, r0, raw, erange)
258 
262 
263  integer, intent(in) :: order, erange(2), lubnd
264  logical, intent(in) :: verbose
265  type(moleculardata), intent(in) :: moldat
266  type(kmatrix), allocatable, intent(in) :: km(:)
267  type(scatakcoeffs), allocatable, intent(in) :: ak(:)
268  real(wp), intent(in) :: omega(:), first_IP, r0
269  complex(wp), intent(in) :: polar(:, :)
270  character(len=*), intent(in) :: raw
271 
272  type(intermediatestate), pointer :: states, state
273 
274  integer, allocatable :: iidip(:), ifdip(:)
275  real(wp), allocatable :: escat(:)
276 
277  integer :: j, s, mgvni, mgvnn, mgvn1, mgvn2, nesc, irri, iki, icomp, nstati, nchani, max_dim
278  real(wp) :: Ei
279 
280  iki = 1 ! which K-matrix file (and Ak file) to use for the initial state
281  mgvni = km(iki) % mgvn ! IRR of the initial state
282  nesc = erange(2) - erange(1) + 1 ! number of scattering energies
283  irri = findloc(moldat % mgvns, mgvni, 1) ! internal index of initial IRR in molecular_data
284  nstati = moldat % mnp1(irri) ! number of inner region states in initial IRR
285  nchani = moldat % nchan(irri) ! number of outer region channels in initial IRR
286 
287  allocate (states)
288  allocate (states % ck(nstati, 2, (nesc + nprocs - 1) / nprocs))
289  allocate (states % ap(nchani, 2, (nesc + nprocs - 1) / nprocs))
290 
291  ! initialize Coulomb-Green integral caching
292  if (cache_integrals) then
293  max_dim = merge(order, order - 1, extend_istate)
294  call nested_cgreen_integ_cache % init(max_dim, (nesc + nprocs - 1) / nprocs, moldat % rmatr, r0, 0._wp)
295  end if
296 
297  ! prepate the initial state
298  call setup_initial_state(states, moldat, irri, lubnd, ei)
299 
300  ! for all absorbed photons
301  do j = 1, order
302 
303  print '(/,2(A,I0))', 'Photon ', j, ' of ', order
304 
305  ! for all components of the dipole operator
306  do icomp = 1, 3
307 
308  call get_diptrans(moldat, icomp, iidip, ifdip)
309 
310  ! for all transitions mediated by this component that are stored in molecular_data
311  do s = 1, size(iidip)
312 
313  mgvn1 = iidip(s) - 1
314  mgvn2 = ifdip(s) - 1
315 
316  ! apply this transition to all relevant previous intermediate states
317  state => states
318  do while (associated(state))
319 
320  ! process states of previous order
321  if (state % order + 1 == j) then
322 
323  ! IRR of the previous intermediate state (i.e. last element in MGVN history chain)
324  mgvnn = state % mgvn
325 
326  ! skip this transition if it is not applicable to the current IRR or there are no K-matrices for it
327  if ((mgvnn == mgvn1 .or. mgvnn == mgvn2) .and. &
328  any(km(:) % mgvn == mgvn1) .and. &
329  any(km(:) % mgvn == mgvn2)) then
330 
331  ! either calculate the next intermediate state or evaluate the dipole elements
332  if (j < order) then
333  call solve_intermediate_state(moldat, order, omega, icomp, s, mgvnn, &
334  mgvn1, mgvn2, km, state, verbose, ei, first_ip, r0, erange)
335  else
336  call extract_dipole_elements(moldat, order, omega, icomp, s, mgvnn, &
337  mgvn1, mgvn2, km, ak, state, verbose, ei, first_ip, r0, erange)
338  end if
339 
340  end if
341 
342  end if
343 
344  ! move on to the next state
345  state => state % next
346 
347  end do
348 
349  end do
350 
351  end do
352 
353  end do
354 
355  ! calculate observables
356  escat = km(iki) % escat(erange(1):erange(2))
357  call write_energy_grid(escat)
358  call calculate_pw_transition_elements(moldat, order, states, escat, ei, first_ip, omega, polar, erange)
359  call calculate_asymmetry_parameters(moldat, order, states, escat, ei, first_ip, omega, raw, erange)
360 
361  ! clean up the linked list
362  state => states
363  do while (associated(state % next))
364  state => state % next
365  deallocate (state % prev)
366  end do
367  deallocate (state)
368 
369  end subroutine multidip_driver
370 
371 
385  subroutine setup_initial_state (states, moldat, irr, lubnd, Ei)
386 
389  use multidip_params, only: extend_istate
390 
391  type(intermediatestate), pointer, intent(inout) :: states
392  type(moleculardata), intent(in) :: moldat
393  integer, intent(in) :: irr, lubnd
394  real(wp), intent(out) :: Ei
395 
396  complex(wp), allocatable :: ap(:)
397  real(wp), allocatable :: Ek(:), bnd(:, :), wbnd(:, :), fc(:), gc(:), fcp(:), gcp(:)
398  real(wp) :: r
399  integer :: mye, nmye, nchan, nstat, ichan, stot, mgvn, nbnd
400 
401  nmye = size(states % ck, 3)
402  mgvn = moldat % mgvns(irr)
403  stot = moldat % stot(irr)
404  nchan = moldat % nchan(irr)
405  nstat = moldat % mnp1(irr)
406  nbnd = 1
407  r = moldat % rmatr
408  ei = moldat % eig(1, irr)
409 
410  ! construct representation of the initial state in the inner region
411  states % order = 0
412  states % mgvn = mgvn
413  states % dcomp = 0
414 
415  allocate (bnd(nstat, nbnd))
416 
417  if (lubnd <= 0) then
418  bnd(:, 1) = 0 ! all inner region eigenstates are unpopulated...
419  bnd(1, 1) = 1 ! ... except for the first one, which has the coefficient 1.0
420  else
421  call read_bndcoeffs(bnd(:, 1), ei, lubnd, mgvn, stot) ! get calculated coefs from BOUND
422  end if
423 
424  do mye = 1, nmye
425  states % ck(:, 1, mye) = bnd(:, 1)
426  states % ck(:, 2, mye) = 0
427  end do
428 
429  print '(/,a,/)', 'Bound state information'
430  print '(4x,a,e25.15)', 'First R-matrix pole = ', moldat % eig(1, irr)
431  print '(4x,a,e25.15)', 'Calculated initial energy = ', ei
432 
433  ! construct representation of the initial state in the outer region
434  if (extend_istate) then
435 
436  allocate (wbnd(nchan, nbnd), fc(nchan), gc(nchan), fcp(nchan), gcp(nchan), ap(nchan))
437 
438  call apply_boundary_amplitudes(moldat, irr, 'N', bnd, wbnd)
439 
440  ! "photoelectron" "kinetic" energies (actually negative closed channel thresholds)
441  ek = ei - moldat % etarg(moldat % ichl(: , irr))
442 
443  call evaluate_fundamental_solutions(moldat, r, irr, nchan, ek, fc, gc, fcp, gcp, sqrtknorm = .false.)
444 
445  ! obtain outer coeffs by projecting the inner state on channels at boundary and dividing by decaying Whittaker function
446  ap = wbnd(:, 1) / gc
447 
448  do mye = 1, nmye
449  states % ap(:, 1, mye) = real(ap)
450  states % ap(:, 2, mye) = aimag(ap)
451  end do
452 
453  print '(4x,a,e25.15)', 'Total inner norm = ', norm2(bnd(:, 1))
454  print '(/,a,/)', 'Bound state outer region channel coefficients and amplitudes'
455  print '(4x,a,4x,a,23x,a,20x,a,20x,a)', 'chan', 'Ek', 'Re ap', 'Im ap', 'f_p'
456  do ichan = 1, nchan
457  print '(i6,a,4e25.15)', ichan, ': ', ek(ichan), ap(ichan), wbnd(ichan, 1)
458  end do
459 
460  else
461 
462  states % ap(:, :, :) = 0 ! outer region is empty
463 
464  end if
465 
466  if (test_expansion) then
467  call test_outer_expansion('initial-state.txt', moldat, irr, states % ck(:, :, 1), states % ap(:, :, 1), ei)
468  end if
469 
470  end subroutine setup_initial_state
471 
472 
499  subroutine solve_intermediate_state (moldat, order, Ephoton, icomp, s, mgvnn, mgvn1, mgvn2, &
500  km, state, verbose, calc_Ei, first_IP, r0, erange)
501 
507 
508  type(moleculardata), intent(in) :: moldat
509  type(kmatrix), intent(in) :: km(:)
510 
511  type(intermediatestate), pointer, intent(inout) :: state
512  type(intermediatestate), pointer :: last
513 
514  integer, intent(in) :: order, icomp, s, mgvnn, mgvn1, mgvn2, erange(2)
515  logical, intent(in) :: verbose
516  real(wp), intent(in) :: Ephoton(:), calc_Ei, first_IP, r0
517 
518  integer :: nstatn, nstatf, nchann, nchanf, nopen, mgvnf, irrn, irrf, ikn, ikf, ipw, nesc, ie, mye, t, dt, i
519  real(wp) :: Ei, Etotf
520  character(len=1) :: transp
521  character(len=1024) :: filename
522 
523  complex(wp), allocatable :: beta(:), app(:), H(:, :), lambda(:), hc(:), hcp(:)
524  real(wp), allocatable :: fc(:), fcp(:), gc(:), gcp(:)
525 
526  integer, allocatable :: lf(:)
527  real(wp), allocatable :: Ef(:), P(:), rhs(:, :), omega(:), echl(:)
528  real(wp), allocatable :: Pw(:, :), wPw(:, :), wPD(:, :), IwPD(:, :), wckp(:, :), Dpsi(:, :, :), corr(:, :), ckp(:, :)
529 
530  if (mgvnn == mgvn1) then
531  mgvnf = mgvn2
532  transp = 'T'
533  else
534  mgvnf = mgvn1
535  transp = 'N'
536  end if
537 
538  irrn = findloc(moldat % mgvns, mgvnn, 1)
539  irrf = findloc(moldat % mgvns, mgvnf, 1)
540 
541  ikn = findloc(km(:) % mgvn, mgvnn, 1)
542  ikf = findloc(km(:) % mgvn, mgvnf, 1)
543 
544  nstatn = moldat % mnp1(irrn)
545  nstatf = moldat % mnp1(irrf)
546 
547  nchann = moldat % nchan(irrn)
548  nchanf = moldat % nchan(irrf)
549 
550  nesc = erange(2) - erange(1) + 1
551  mye = 0
552  t = 0
553 
554  allocate (pw(nstatf, nchanf), wpd(nchanf, 2), iwpd(nchanf, 2), wpw(nchanf, nchanf), wckp(nchanf, 2), omega(order), &
555  dpsi(nstatf, 2, (nesc + nprocs - 1) / nprocs), fc(nchanf), gc(nchanf), fcp(nchanf), gcp(nchanf), &
556  hc(nchanf), hcp(nchanf), corr(nchanf, 2), echl(nchanf), &
557  ef(nchanf), lf(nchanf), ckp(nstatf, 2), h(nchanf, nchanf), lambda(nchanf), beta(nchanf), &
558  app(nchanf))
559 
560  echl(1:nchanf) = moldat % etarg(moldat % ichl(1:nchanf, irrf)) - moldat % etarg(1)
561 
562  ! set up a new intermediate state at the end of the storage chain
563  last => state
564  do while (associated(last % next))
565  last => last % next
566  end do
567  allocate (last % next)
568  last % next % prev => last
569  last => last % next
570  last % order = state % order + 1
571  last % mgvn = mgvnf
572  last % dcomp = icomp
573  last % parent => state
574  allocate (last % ck(nstatf, 2, (nesc + nprocs - 1) / nprocs))
575  allocate (last % ap(nchanf, 2, (nesc + nprocs - 1) / nprocs))
576 
577  call print_transition_header(last)
578 
579  ! apply the inner dipoles matrix on the previous inner region solution expansions
580  call reset_timer(t, dt)
581  call apply_dipole_matrix(moldat, icomp, s, transp, nstatf, nstatn, state % ck, dpsi)
582  call reset_timer(t, dt)
583  print '(4x,A,I0,A)', 'inner dipoles applied in ', dt, ' s'
584 
585  ! for all scattering energies
586  do ie = erange(1) + myproc - 1, erange(2), nprocs
587 
588  mye = mye + 1
589 
590  ! calculate photon energies and the initial state energy (take into account user-specified first ionization potential)
591  ei = calc_ei
592  call calculate_photon_energies(first_ip, km(ikf) % escat(ie), moldat % etarg(1), ei, ephoton, omega)
593 
594  ! quantum numbers of the final state
595  etotf = ei + sum(omega(1 : last % order))
596  ef = km(ikf) % escat(ie) - echl - sum(omega(last % order + 1 :))
597  lf = moldat % l2p(1:nchanf, irrf)
598  nopen = merge(count(ef + closed_range > 0), count(ef > 0), closed_interm)
599 
600  print '(4x,A,*(I0,A))', 'energy ', ie, ' of ', km(ikf) % nescat, &
601  ' (', count(ef > 0), ' open / ', nopen, ' open + weakly closed channels of ', nchanf, ')'
602 
603  ! evaluate Coulomb functions at this energy for all partial waves in the final IRR
604  fc = 0; fcp = 0; gc = 0; gcp = 0; hc = 0; hcp = 0; lambda = 0
605  call evaluate_fundamental_solutions(moldat, moldat % rmatr, irrf, nopen, ef, fc, gc, fcp, gcp, sqrtknorm = .false.)
606  do ipw = 1, nopen
607  hc(ipw) = gc(ipw) + imu*fc(ipw)
608  hcp(ipw) = gcp(ipw) + imu*fcp(ipw)
609  lambda(ipw) = hcp(ipw) / hc(ipw)
610  end do
611 
612  ! inner region part of the right-hand side of the master equation
613  rhs = dpsi(:, :, mye)
614 
615  ! outer region correction to the right-hand side
616  call multiint(moldat, r0, ei, km(ikf) % escat(ie) - sum(omega(last % order + 1 :)), omega, mye, last, +1, beta)
617  beta = -2 * beta / sqrt(2*abs(ef))
618  where (ef < 0) beta = beta / imu
619  corr(:, 1) = real(beta * (fcp - lambda * fc), wp)
620  corr(:, 2) = aimag(beta * (fcp - lambda * fc))
621  call apply_boundary_amplitudes(moldat, irrf, 'T', corr, dpsi(:, :, mye))
622  rhs = rhs - 0.5 * dpsi(:, :, mye)
623 
624  ! solve the master equation using the Sherman-Morrison-Woodbury formula
625  p = 1 / (etotf - moldat % eig(1:nstatf, irrf))
626  ckp(:, 1) = p * rhs(:, 1)
627  ckp(:, 2) = p * rhs(:, 2)
628 
629  if (nopen > 0) then
630 
631  ! compose the reduced inverse Hamiltonian
632  h = 0
633  call scale_boundary_amplitudes(moldat, irrf, p, pw)
634  call apply_boundary_amplitudes(moldat, irrf, 'N', pw, wpw)
635  h(1:nopen, 1:nopen) = wpw(1:nopen, 1:nopen)
636  do i = 1, nopen
637  h(i, i) = h(i, i) + 2/lambda(i)
638  end do
639  do i = nopen + 1, nchanf
640  h(i, i) = 1
641  end do
642  call apply_boundary_amplitudes(moldat, irrf, 'N', ckp, wpd)
643 
644  ! solve the reduced system
645  iwpd = 0
646  call solve_complex_system(nopen, h, wpd, iwpd)
647 
648  ! expand the reduced solution to the full solution
649  call apply_boundary_amplitudes(moldat, irrf, 'T', iwpd, ckp)
650  ckp(:, 1) = p * (rhs(:, 1) - ckp(:, 1))
651  ckp(:, 2) = p * (rhs(:, 2) - ckp(:, 2))
652 
653  end if
654 
655  ! extract the outer region channel amplitudes
656  call apply_boundary_amplitudes(moldat, irrf, 'N', ckp, wckp)
657  app = cmplx(wckp(:, 1), wckp(:, 2), wp)
658  where (hc /= 0)
659  app = (app - beta*fc) / hc
660  elsewhere
661  app = 0
662  end where
663 
664  ! for debugging purposes
665  if (test_expansion) then
666  wckp(:, 1) = real(app)
667  wckp(:, 2) = aimag(app)
668  write (filename, '(a,i0,a,i0,a)') 'intermediate-state-', irrf, '-', ie, '.txt'
669  call test_outer_expansion(trim(filename), moldat, irrf, ckp, wckp, etotf)
670  end if
671 
672  if (verbose) print '(4x,A,*(E25.15))', ' - beta: ', beta(1:nopen)
673  if (verbose) print '(4x,A,*(E25.15))', ' - ap: ', app(1:nopen)
674 
675  last % ck(:, 1, mye) = ckp(:, 1)
676  last % ck(:, 2, mye) = ckp(:, 2)
677 
678  last % ap(:, 1, mye) = real(app, wp)
679  last % ap(:, 2, mye) = aimag(app)
680 
681  end do
682 
683  call reset_timer(t, dt)
684  print '(4x,A,I0,A)', 'intermediate states solved in ', dt, ' s'
685 
686  end subroutine solve_intermediate_state
687 
688 
713  subroutine extract_dipole_elements (moldat, order, Ephoton, icomp, s, mgvnn, mgvn1, mgvn2, &
714  km, ak, state, verbose, calc_Ei, first_IP, r0, erange)
715 
720 
721  type(scatakcoeffs), allocatable, intent(in) :: ak(:)
722 
723  type(moleculardata), intent(in) :: moldat
724  type(kmatrix), intent(in) :: km(:)
725 
726  type(intermediatestate), pointer, intent(inout) :: state
727  type(intermediatestate), pointer :: last
728 
729  integer, intent(in) :: order, icomp, s, mgvnn, mgvn1, mgvn2, erange(2)
730  logical, intent(in) :: verbose
731  real(wp), intent(in) :: Ephoton(:), calc_Ei, first_IP, r0
732 
733  character(len=1) :: transp
734  character(len=1024) :: filename
735 
736  complex(wp), allocatable :: Af(:, :), dm(:), dp(:), Sm(:, :), tmat(:, :)
737  real(wp), allocatable :: Dpsi(:, :, :), Ef(:), echlf(:), d_inner(:, :), d_outer(:, :), Re_Af(:, :), Im_Af(:, :)
738  real(wp), allocatable :: omega(:), d_total(:, :), kmat(:, :), Sf(:), Cf(:), Sfp(:), Cfp(:)
739  integer, allocatable :: lf(:)
740 
741  real(wp) :: Etotf, Ei, Ek
742  integer :: mgvnf, irrn, irrf, ikn, ikf, nstatn, nstatf, nchann, nchanf, nesc, ie, nopen, nochf, mye, nene, maxchan, i
743  integer :: t, dt
744 
745  integer(blasint) :: m, n, lds, inc = 1
746 
747  if (mgvnn == mgvn1) then
748  mgvnf = mgvn2
749  transp = 'T'
750  else
751  mgvnf = mgvn1
752  transp = 'N'
753  end if
754 
755  irrn = findloc(moldat % mgvns, mgvnn, 1)
756  irrf = findloc(moldat % mgvns, mgvnf, 1)
757 
758  ikn = findloc(km(:) % mgvn, mgvnn, 1)
759  ikf = findloc(km(:) % mgvn, mgvnf, 1)
760 
761  nstatn = moldat % mnp1(irrn)
762  nstatf = moldat % mnp1(irrf)
763 
764  nchann = km(ikn) % nchan
765  nchanf = km(ikf) % nchan
766 
767  nesc = erange(2) - erange(1) + 1
768  nene = (nesc + nprocs - 1) / nprocs
769  mye = 0
770  t = 0
771 
772  maxchan = nchanf
773  if (maxtarg > 0) then
774  maxchan = count(moldat % ichl(1:nchanf, irrf) <= maxtarg)
775  end if
776 
777  allocate (dpsi(nstatf, 2, nene), omega(order), ef(nchanf), lf(nchanf), echlf(nchanf), &
778  re_af(nstatf, maxchan), im_af(nstatf, maxchan), af(nstatf, maxchan), sm(nchanf, nchanf), &
779  d_inner(maxchan, 2), d_outer(maxchan, 2), d_total(maxchan, 2), dm(maxchan), dp(nchanf), &
780  sf(nchanf), cf(nchanf), sfp(nchanf), cfp(nchanf), kmat(nchanf, nchanf), tmat(nchanf, nchanf))
781 
782  echlf(1:nchanf) = moldat % etarg(moldat % ichl(1:nchanf, irrf)) - moldat % etarg(1)
783 
784  ! set up a final state at the end of the storage chain
785  last => state
786  do while (associated(last % next))
787  last => last % next
788  end do
789  allocate (last % next)
790  last % next % prev => last
791  last => last % next
792  last % order = order
793  last % mgvn = mgvnf
794  last % dcomp = icomp
795  last % parent => state
796  allocate (last % dip(maxchan, 2, nene))
797 
798  call print_transition_header(last)
799 
800  ! apply the inner dipoles matrix on the previous inner region solution expansions
801  call reset_timer(t, dt)
802  call apply_dipole_matrix(moldat, icomp, s, transp, nstatf, nstatn, state % ck, dpsi)
803  call reset_timer(t, dt)
804  print '(4x,A,I0,A)', 'inner dipoles applied in ', dt, ' s'
805 
806  ! position K-matrix file pointer at the beginning of (this process') K-matrix data
807  call km(ikf) % reset_kmatrix_position(skip = myproc - 1)
808  if (allocated(ak)) call ak(ikf) % reset_wfncoeffs_position(skip = myproc - 1)
809 
810  ! for all scattering energies
811  do ie = myproc, erange(2), nprocs
812 
813  ! read the K-matrix and Ak coeffs from the file and then skip the next 'nprocs - 1' records used by other images
814  call km(ikf) % get_kmatrix(kmat, skip = nprocs - 1)
815  if (allocated(ak)) call ak(ikf) % get_wfncoeffs(ek, re_af, im_af, skip = nprocs - 1)
816  if (ie < erange(1)) cycle
817 
818  mye = mye + 1
819  last % dip(:, :, mye) = 0
820 
821  ! calculate photon energies and the initial state energy (take into account user-specified first ionization potential)
822  ei = calc_ei
823  call calculate_photon_energies(first_ip, km(ikf) % escat(ie), moldat % etarg(1), ei, ephoton, omega)
824 
825  ! quantum numbers of the final state
826  etotf = ei + sum(omega)
827  ef = km(ikf) % escat(ie) - echlf ! photoelectron energies in all (open and closed) channels
828  lf = moldat % l2p(1:nchanf, irrf) ! photoelectron angular momentum in all (open and closed) channels
829  nopen = count(ef > 0) ! number of all open channels
830  nochf = min(nopen, maxchan) ! number of open channels for which we want to obtain dipoles
831 
832  print '(4x,A,*(I0,A))', 'energy ', ie, ' of ', km(ikf) % nescat, ' (', nopen, ' open channels of ', nchanf, ')'
833 
834  ! get values and derivatives of the fundamental solutions of uncoupled (or dipole-coupled) outer region equations
835  call evaluate_fundamental_solutions(moldat, moldat % rmatr, irrf, nchanf, ef, sf, cf, sfp, cfp, sqrtknorm = .true.)
836 
837  ! if desired, recalculate the K-matrix here (possibly to include long-range dipole)
838  if (custom_kmatrices) then
839  call calculate_k_matrix(moldat, nopen, irrf, etotf, sf, cf, sfp, cfp, kmat)
840  end if
841  call calculate_t_matrix(kmat, tmat, nopen)
842 
843  ! contract D*psi with complex-conjugated wave function coefficients Ak to get inner region contribution
844  if (.not. allocated(ak)) then
845  call apply_ak_coefficients_multidip(dpsi(:, :, mye), d_inner, moldat, nopen, irrf, etotf, &
846  sfp, cfp, kmat, tmat, .true.)
847  else
848  call apply_ak_coefficiens_compak(dpsi(:, :, mye), d_inner, re_af(:, 1:nochf), im_af(:, 1:nochf))
849  end if
850 
851  ! outer region correction
852  dm = 0
853  dp = 0
854  call multiint(moldat, r0, ei, km(ikf) % escat(ie), omega, mye, last, -1, dm(1:nochf))
855  call multiint(moldat, r0, ei, km(ikf) % escat(ie), omega, mye, last, +1, dp(1:nopen))
856  dm(1:nochf) = dm(1:nochf) * imu / sqrt(2*pi*sqrt(2*ef(1:nochf)))
857  dp(1:nopen) = dp(1:nopen) * imu / sqrt(2*pi*sqrt(2*ef(1:nopen)))
858  if (verbose) print '(5x,A,1x,*(1x,E25.15))', 'dm = ', dm(1:nochf)
859  if (verbose) print '(5x,A,1x,*(1x,E25.15))', 'dp = ', dp(1:nopen)
860  if (any(dp /= 0) .or. test_expansion) then
861  m = int(nopen, blasint)
862  n = int(nochf, blasint)
863  lds = int(nchanf, blasint)
864  call calculate_s_matrix(tmat, sm, nopen)
865  call blas_zgemv('T', m, n, -cone, sm, lds, dp, inc, cone, dm, inc) ! dm = dm - S^T dp
866  end if
867  d_outer(:, 1) = real(dm)
868  d_outer(:, 2) = aimag(dm)
869 
870  ! for debugging purposes
871  if (test_expansion) then
872  write (filename, '(a,i0,a,i0,a)') 'final-state-', irrf, '-', ie, '.txt'
873  call test_final_expansion(trim(filename), moldat, irrf, nopen, etotf, ef, sfp, cfp, kmat, tmat)
874  end if
875 
876  ! combine and store the partial wave dipoles
877  d_total = d_inner + d_outer
878  last % dip(:, :, mye) = d_total
879 
880  if (verbose) print '(5x,A,1x,*(1x,E25.15))', 'd_inner = ', (d_inner(i, 1), d_inner(i, 2), i = 1, nochf)
881  if (verbose) print '(5x,A,1x,*(1x,E25.15))', 'd_outer = ', (d_outer(i, 1), d_outer(i, 2), i = 1, nochf)
882  if (verbose) print '(5x,A,1x,*(1x,E25.15))', 'd_total = ', (d_total(i, 1), d_total(i, 2), i = 1, nochf)
883 
884  end do
885 
886  call reset_timer(t, dt)
887  print '(4x,A,I0,A)', 'matrix elements calculated in ', dt, ' s'
888 
889  end subroutine extract_dipole_elements
890 
891 
901  subroutine print_transition_header (state)
902 
903  use multidip_params, only: compt
904 
905  type(intermediatestate), pointer, intent(in) :: state
906  type(intermediatestate), pointer :: ptr
907 
908  integer, allocatable :: irreds(:), compts(:)
909  integer :: order, i
910 
911  order = state % order
912 
913  allocate (irreds(order + 1), compts(order))
914 
915  ptr => state
916  irreds(order + 1) = ptr % mgvn
917  do while (associated(ptr % parent))
918  compts(order) = ptr % dcomp
919  ptr => ptr % parent
920  irreds(order) = ptr % mgvn
921  order = order - 1
922  end do
923 
924  write (*, '(2x,a)', advance = 'no') 'Transition'
925  do i = 1, state % order
926  write (*, '(1x,i0,1x,3a)', advance = 'no') irreds(i), '-[', compt(compts(i)), ']->'
927  end do
928  write (*, '(1x,i0)') irreds(state % order + 1)
929 
930  end subroutine print_transition_header
931 
932 
944  subroutine apply_ak_coefficiens_compak (psi, Apsi, ReAk, ImAk)
945 
946  use multidip_params, only: rone, rzero
947  use multidip_special, only: blas_dgemv => dgemv
948 
949  real(wp), intent(in) :: psi(:, :), ReAk(:, :), ImAk(:, :)
950  real(wp), intent(inout) :: Apsi(:, :)
951 
952  integer(blasint) :: m, n, inc = 1
953 
954  m = int(size(reak, 1), blasint)
955  n = int(size(reak, 2), blasint)
956 
957  call blas_dgemv('T', m, n, rone, reak, m, psi(:, 1), inc, rzero, apsi(:, 1), inc)
958  call blas_dgemv('T', m, n, +rone, imak, m, psi(:, 2), inc, +rone, apsi(:, 1), inc)
959  call blas_dgemv('T', m, n, rone, reak, m, psi(:, 2), inc, rzero, apsi(:, 2), inc)
960  call blas_dgemv('T', m, n, -rone, imak, m, psi(:, 1), inc, +rone, apsi(:, 2), inc)
961 
962  end subroutine apply_ak_coefficiens_compak
963 
964 
1003  subroutine apply_ak_coefficients_multidip (psi, Apsi, moldat, nopen, irr, Etot, Sp, Cp, kmat, tmat, conj)
1004 
1006  use multidip_params, only: czero, rzero
1007 
1008  type(moleculardata), intent(in) :: moldat
1009  real(wp), intent(in) :: psi(:, :), Etot
1010  real(wp), intent(inout) :: Apsi(:, :)
1011  integer, intent(in) :: nopen, irr
1012  real(wp), intent(in) :: Sp(:), Cp(:), kmat(:, :)
1013  complex(wp), intent(in) :: tmat(:, :)
1014  logical, intent(in) :: conj
1015 
1016  real(wp), allocatable :: tmps(:, :), tmpc(:, :)
1017  complex(wp), allocatable :: tmpo(:)
1018 
1019  integer(blasint) :: n, ldk, one = 1
1020 
1021  integer :: nchan, nstat, i, j
1022  real(wp) :: Fij
1023  complex(wp) :: z, T
1024 
1025  nstat = moldat % mnp1(irr)
1026  nchan = moldat % nchan(irr)
1027  n = nopen
1028  ldk = size(kmat, 1)
1029 
1030  allocate (tmps(nstat, 2), tmpc(nchan, 2), tmpo(nopen))
1031 
1032  ! multiply solution by R-matrix poles
1033  tmps(:, 1) = psi(:, 1) / (moldat % eig(1:nstat, irr) - etot)
1034  tmps(:, 2) = psi(:, 2) / (moldat % eig(1:nstat, irr) - etot)
1035 
1036  ! contract with the boundary amplitudes
1037  call apply_boundary_amplitudes(moldat, irr, 'N', tmps, tmpc)
1038 
1039  ! multiply by complex-conjugated matrix F' = (TS' + TC'K) / √2π
1040  do j = 1, nopen
1041  tmpo(j) = 0
1042  do i = 1, nchan
1043  fij = (merge(sp(i), rzero, i == j) + cp(i)*kmat(i, j)) / sqrt(2*pi)
1044  tmpo(j) = tmpo(j) + cmplx(tmpc(i, 1), tmpc(i, 2), wp)*fij
1045  end do
1046  end do
1047 
1048  ! multiply by complex-conjugated T (nopen-by-nopen)
1049  do j = 1, min(nopen, size(apsi, 1))
1050  apsi(j, 1) = 0
1051  apsi(j, 2) = 0
1052  do i = 1, nopen
1053  t = merge(1, 0, i == j) + conjg(tmat(i, j))/2 ! = (1 + iK)⁻¹
1054  if (conj) t = conjg(t)
1055  z = tmpo(i) * t
1056  apsi(j, 1) = apsi(j, 1) + real(z)
1057  apsi(j, 2) = apsi(j, 2) + aimag(z)
1058  end do
1059  end do
1060 
1061  ! erase the closed-channel amplitudes
1062  do j = nopen + 1, min(nchan, size(apsi, 1))
1063  apsi(j, 1) = 0
1064  apsi(j, 2) = 0
1065  end do
1066 
1067  end subroutine apply_ak_coefficients_multidip
1068 
1069 
1078  subroutine test_final_expansion (filename, moldat, irr, nopen, Etot, Ek, Sp, Cp, kmat, tmat)
1079 
1080  use multidip_io, only: moleculardata
1082 
1083  character(len=*), intent(in) :: filename
1084  type(moleculardata), intent(in) :: moldat
1085  integer, intent(in) :: irr, nopen
1086  real(wp), intent(in) :: Etot, Ek(:), Sp(:), Cp(:), kmat(:, :)
1087  complex(wp), intent(in) :: tmat(:, :)
1088 
1089  real(wp), allocatable :: psi(:, :), Apsi(:, :), S(:), C(:), dSdr(:), dCdr(:)
1090  complex(wp), allocatable :: Fqp(:, :), Hp(:), Hm(:)
1091 
1092  real(wp) :: dr, r
1093  integer :: i, nfdm, u, nchan, p, q, nstat
1094 
1095  nfdm = size(moldat % r_points) - 1
1096  nchan = moldat % nchan(irr)
1097  nstat = moldat % mnp1(irr)
1098  dr = 0.1
1099 
1100  allocate (fqp(nchan, nopen), psi(nstat, 2), apsi(nchan, 2), s(nchan), c(nchan), hp(nchan), hm(nchan), dsdr(nchan), &
1101  dcdr(nchan))
1102 
1103  open (newunit = u, file = filename, action = 'write', form = 'formatted')
1104 
1105  ! inner region
1106  do i = 1, nfdm
1107  write (u, '(e25.15)', advance = 'no') moldat % r_points(i)
1108  do q = 1, nchan
1109  if (.not. associated(moldat % wmat2(i, irr) % mat)) then
1110  print '(a)', 'Error: wmat2 has not been read from molecular_data'
1111  stop 1
1112  else if (moldat % wmat2(i, irr) % distributed) then
1113  print '(a)', 'Error: test_outer_expansion not implemented in MPI-IO mode'
1114  stop 1
1115  end if
1116  ! the "wave-function" is a row of a boundary amplitudes matrix to reduce the Ak coefficient with
1117  psi(:, 1) = moldat % wmat2(i, irr) % mat(q, :)
1118  psi(:, 2) = 0
1119  ! perform the reduction w*A
1120  call apply_ak_coefficients_multidip(psi, apsi, moldat, nopen, irr, etot, sp, cp, kmat, tmat, .false.)
1121  ! combine components to a single complex number
1122  fqp(q, 1:nopen) = cmplx(apsi(1:nopen, 1), apsi(1:nopen, 2), wp)
1123  end do
1124  write (u, '(*(e25.15))') fqp(1:nchan, 1:nopen)
1125  end do
1126 
1127  ! outer region
1128  do i = 0, nint(moldat % rmatr/dr)
1129  r = moldat % rmatr + i*dr
1130  write (u, '(e25.15)', advance = 'no') r
1131  call evaluate_fundamental_solutions(moldat, r, irr, nchan, ek, s, c, dsdr, dcdr)
1132  hp = (c + imu*s) * (-imu) / sqrt(2*pi)
1133  hm = (c - imu*s) * (-imu) / sqrt(2*pi)
1134  do p = 1, nopen
1135  do q = 1, nchan
1136  if (q == p) fqp(q, p) = hp(q)
1137  if (q /= p) fqp(q, p) = 0
1138  fqp(q, p) = fqp(q, p) - hm(q) * conjg(1 + tmat(q, p))
1139  write (u, '(*(e25.15))', advance = 'no') fqp(q, p)
1140  end do
1141  end do
1142  write (u, '()')
1143  end do
1144 
1145  close (u)
1146 
1147  end subroutine test_final_expansion
1148 
1149 
1160  subroutine reset_timer (t, dt)
1161 
1162  use mpi_gbl, only: mpi_mod_barrier
1163 
1164  integer, intent(inout) :: t
1165  integer, intent(out) :: dt
1166  integer :: clk_now, clk_rate, clk_max, ierr
1167 
1168  call mpi_mod_barrier(ierr)
1169 
1170  call system_clock(clk_now, clk_rate, clk_max)
1171 
1172  if (t < clk_now) then
1173  dt = (clk_now - t) / clk_rate
1174  else
1175  ! Cater for possible system clock wrap-around. Also pay attention to integer overflow; clk_max
1176  ! is typically the upper limit of the given integer type.
1177  dt = ((clk_max - t) + clk_now) / clk_rate
1178  end if
1179 
1180  t = clk_now
1181 
1182  end subroutine reset_timer
1183 
1184 
1202  subroutine calculate_photon_energies (first_IP, escat, etarg, Ei, Ephoton, omega)
1203 
1204  real(wp), intent(in) :: first_IP, escat, etarg, Ephoton(:)
1205  real(wp), intent(inout) :: Ei
1206  real(wp), allocatable, intent(inout) :: omega(:)
1207  real(wp) :: IP, Ef
1208 
1209  ! calculate total final energy
1210  ef = etarg + escat
1211 
1212  ! obtain the calculated ionization potential (including non-zero photoelectron energy)
1213  ip = ef - ei
1214 
1215  ! set the requested ionization potential by shifting the initial state energy
1216  if (first_ip > 0) then
1217  ip = first_ip + escat
1218  ei = ef - ip
1219  end if
1220 
1221  ! calculate photon energies
1222  where (ephoton /= 0)
1223  omega = ephoton
1224  elsewhere
1225  omega = (ip - sum(ephoton)) / (size(omega) - count(ephoton /= 0))
1226  end where
1227 
1228  end subroutine calculate_photon_energies
1229 
1230 
1250  subroutine multiint (moldat, r0, Ei, esc, omega, ie, state, sb, dip)
1251 
1252  use mpi_gbl, only: mpi_xermsg
1254  use multidip_io, only: moleculardata
1256 
1257  type(moleculardata), intent(in) :: moldat
1258  type(intermediatestate), pointer, intent(in) :: state
1259 
1260  complex(wp), intent(inout) :: dip(:)
1261  real(wp), allocatable, intent(inout) :: omega(:)
1262  real(wp), intent(in) :: Ei, esc, r0
1263  integer, intent(in) :: ie, sb
1264 
1265  complex(wp), allocatable :: k(:)
1266  integer, allocatable :: l(:), m(:)
1267 
1268  real(wp) :: c, a, Ekf, ebase, echl, etarg
1269  integer :: nphot, mgvn, ichan, irr, ichl
1270 
1271  dip = 0
1272 
1273  if (cache_integrals) nested_cgreen_integ_cache % ie = ie ! use integral cache for this energy
1274 
1275  if (.not. associated(state % parent)) then ! state with no parent should not be a final state in the first place
1276  call mpi_xermsg('multidip_routines', 'multiint', 'Runtime error: unexpected parent state', 1, 1)
1277  end if
1278 
1279  a = moldat % rmatr ! R-matrix radius
1280  c = 0 ! dipole damping factor
1281  nphot = state % order ! number of photons absorbed by the final state
1282  mgvn = state % mgvn ! MGVN of the final state
1283  irr = findloc(moldat % mgvns, mgvn, 1) ! index of this spin-symmetry in molecular_data
1284  ebase = moldat % etarg(1) ! energy of the first ion state
1285 
1286  !$omp parallel if(.not. cache_integrals) default(none) shared(moldat, state, omega, dip, closed_range, closed_interm) &
1287  !$omp& private(k, l, m, ichan, ichl, etarg, echl, Ekf) &
1288  !$omp& firstprivate(r0, c, esc, sb, ie, Ei, ebase, irr, nphot)
1289 
1290  allocate (k(nphot + 1), l(nphot + 1), m(nphot))
1291 
1292  !$omp do schedule(dynamic)
1293 
1294  do ichan = 1, size(dip)
1295 
1296  ! set up quantum numbers for this channel
1297  ichl = moldat % ichl(ichan, irr) ! index of ion state this partial wave is coupled to
1298  etarg = moldat % etarg(ichl) ! energy of that ion state
1299  echl = etarg - ebase ! excitation threshold of this ion state w.r.t. ion ground
1300  ekf = esc - echl ! always positive for final states (but not for intermediate)
1301  if (ekf + closed_range <= 0) cycle ! ignore strongly closed channels
1302  if (ekf < 0 .and. .not. closed_interm) cycle ! optionally ignore also weakly closed channels
1303  k(1) = sqrt(2*abs(ekf)); if (ekf < 0) k(1) = k(1) * imu
1304  l(1) = moldat % l2p(ichan, irr)
1305 
1306  ! evaluate the correction dipole integral for this final channel
1307  dip(ichan) = multiint_chain(moldat, r0, ei, esc - omega(nphot), omega, ie, c, 1, state, ichan, sb, k, l, m)
1308 
1309  end do
1310 
1311  !$omp end do
1312  !$omp end parallel
1313 
1314  end subroutine multiint
1315 
1316 
1345  recursive complex(wp) function multiint_chain (moldat, r0, Ei, esc, omega, ie, c, N, state, ichanf, sb, k, l, m) result (integ)
1346 
1348  use multidip_io, only: moleculardata
1350 
1351  type(moleculardata), intent(in) :: moldat
1352  type(intermediatestate), pointer, intent(in) :: state
1353  type(intermediatestate), pointer :: parent
1354 
1355  complex(wp), allocatable, intent(inout) :: k(:)
1356  real(wp), allocatable, intent(inout) :: omega(:)
1357  integer, allocatable, intent(inout) :: l(:), m(:)
1358  real(wp), intent(in) :: r0, ei, esc
1359  integer, intent(in) :: ie, sb, n, ichanf
1360 
1361  complex(wp) :: ap, kr(n + 1)
1362  real(wp) :: z, c, a, ek, qion, qpws, ebase, etarg, echl
1363  integer :: mgvni, mgvnf, nchani, irri, irrf, ichani, ichl, nphot, dcomp, lr(n + 1), mr(n)
1364 
1365  integ = 0
1366 
1367  ! no need to recurse further if the current state is the initial state
1368  if (.not. associated(state % parent)) return
1369 
1370  parent => state % parent ! previous intermediate state
1371  nphot = parent % order ! number of photons absorbed by the previous intermediate state
1372  ebase = moldat % etarg(1) ! energy of the ground ion state
1373  a = moldat % rmatr ! R-matrix radius
1374  z = moldat % nz - moldat % nelc ! Residual ion charge
1375  dcomp = state % dcomp ! last polarisation component absorbed by the final state
1376  mgvnf = state % mgvn ! irreducible representation of the current state
1377  mgvni = parent % mgvn ! irreducible representation of the previos state
1378  irrf = findloc(moldat % mgvns, mgvnf, 1) ! index of the current spin-symmetry in molecular_data
1379  irri = findloc(moldat % mgvns, mgvni, 1) ! index of the previous spin-symmetry in molecular_data
1380  nchani = moldat % nchan(irri) ! number of channels in the previous state irreducible representation
1381 
1382  ! loop over all channels of the intermediate state that are dipole-coupled to ichanf
1383  do ichani = 1, nchani
1384 
1385  ! set up quantum numbers for this channel
1386  ichl = moldat % ichl(ichani, irri) ! index of ion state this pw is coupled to
1387  etarg = moldat % etarg(ichl) ! energy of that state
1388  echl = etarg - ebase ! excitation threshold of this ion state w.r.t. ground ion
1389  ek = esc - echl ! can become negative starting from some channel
1390  if (ek + closed_range <= 0) exit ! ignore strongly closed channels
1391  if (ek < 0 .and. .not. closed_interm) exit ! optionally ignore also weakly closed channels
1392  k(n + 1) = sqrt(2*abs(ek)); if (ek < 0) k(n + 1) = k(n + 1) * imu
1393  l(n + 1) = moldat % l2p(ichani, irri)
1394  kr(1 : n + 1) = k(n + 1 : 1 : -1)
1395  lr(1 : n + 1) = l(n + 1 : 1 : -1)
1396 
1397  ! dipole angular integrals
1398  qion = channel_coupling_ion(moldat, dcomp, irrf, irri, ichanf, ichani)
1399  qpws = channel_coupling_pws(moldat, dcomp, irrf, irri, ichanf, ichani)
1400 
1401  ! outer region expansion coefficient for this partial wave and energy
1402  ap = cmplx(parent % ap(ichani, 1, ie), parent % ap(ichani, 2, ie), wp)
1403 
1404  ! evaluate Coulomb-Green's integrals for dipole-coupled ions
1405  if (qion /= 0) then
1406  m(n) = 0
1407  mr(1:n) = m(n:1:-1)
1408  if (ap /= 0) integ = integ + qion * ap * nested_cgreen_integ(z, a, r0, c, n, +1, sb, mr, lr, kr)
1409  if (nphot > 0) integ = integ + qion * multiint_chain(moldat, r0, ei, esc - omega(nphot), &
1410  omega, ie, c, n + 1, parent, ichani, sb, k, l, m)
1411  end if
1412 
1413  ! evaluate Coulomb-Green's integrals for dipole-coupled partial waves
1414  if (qpws /= 0) then
1415  m(n) = 1
1416  mr(1:n) = m(n:1:-1)
1417  if (ap /= 0) integ = integ + qpws * ap * nested_cgreen_integ(z, a, r0, c, n, +1, sb, mr, lr, kr)
1418  if (nphot > 0) integ = integ + qpws * multiint_chain(moldat, r0, ei, esc - omega(nphot), &
1419  omega, ie, c, n + 1, parent, ichani, sb, k, l, m)
1420  end if
1421 
1422  end do
1423 
1424  end function multiint_chain
1425 
1426 
1442  real(wp) function channel_coupling_ion (moldat, dcomp, irrf, irri, ichanf, ichani) result (qion)
1443 
1444  use multidip_io, only: moleculardata
1445  use multidip_params, only: carti
1446 
1447  type(moleculardata), intent(in) :: moldat
1448  integer, intent(in) :: dcomp, irrf, irri, ichanf, ichani
1449 
1450  integer :: itargi, itargf, li, lf, mi, mf
1451 
1452  lf = moldat % l2p(ichanf, irrf); mf = moldat % m2p(ichanf, irrf); itargf = moldat % ichl(ichanf, irrf)
1453  li = moldat % l2p(ichani, irri); mi = moldat % m2p(ichani, irri); itargi = moldat % ichl(ichani, irri)
1454 
1455  if (lf /= li .or. mf /= mi) then
1456  qion = 0
1457  else
1458  qion = moldat % crlv(itargf, itargi, carti(dcomp))
1459  end if
1460 
1461  end function channel_coupling_ion
1462 
1463 
1478  real(wp) function channel_coupling_pws (moldat, dcomp, irrf, irri, ichanf, ichani) result (qpws)
1479 
1480  use multidip_io, only: moleculardata
1481  use multidip_params, only: cartm
1482 
1483  type(moleculardata), intent(in) :: moldat
1484  integer, intent(in) :: dcomp, irrf, irri, ichanf, ichani
1485 
1486  integer :: itargi, itargf, li, lf, mi, mf, ipwi, ipwf
1487 
1488  itargf = moldat % ichl(ichanf, irrf)
1489  itargi = moldat % ichl(ichani, irri)
1490 
1491  if (itargf /= itargi) then
1492  qpws = 0
1493  else
1494  lf = moldat % l2p(ichanf, irrf); mf = moldat % m2p(ichanf, irrf); ipwf = lf*(lf + 1) + mf + 1
1495  li = moldat % l2p(ichani, irri); mi = moldat % m2p(ichani, irri); ipwi = li*(li + 1) + mi + 1
1496  qpws = sqrt(4*pi/3) * moldat % gaunt(ipwf, ipwi, cartm(dcomp))
1497  end if
1498 
1499  end function channel_coupling_pws
1500 
1501 
1532  subroutine calculate_pw_transition_elements (moldat, order, state, escat, calc_Ei, first_IP, Ephoton, polar, erange)
1533 
1535  use multidip_params, only: alpha, maxtarg
1536  use multidip_special, only: cphase
1537 
1538  type(moleculardata), intent(in) :: moldat
1539  type(intermediatestate), pointer, intent(in) :: state
1540  type(intermediatestate), pointer :: ptr, parent
1541 
1542  integer, intent(in) :: order, erange(2)
1543  real(wp), intent(in) :: escat(:), calc_Ei, first_IP, Ephoton(:)
1544  complex(wp), intent(in) :: polar(:, :)
1545  integer :: nesc, ntarg, nchan, mxchan, lf, ichan, itarg, ie, irr, mgvn, mye, nirr, nene
1546  real(wp) :: Z, Ef, Ei, kf, sigma, Q
1547  complex(wp) :: d, ej
1548  complex(wp), allocatable :: M(:, :, :)
1549  real(wp), allocatable :: cs(:, :), omega(:)
1550 
1551  z = moldat % nz - moldat % nelc ! residual charge
1552  nirr = size(moldat % mgvns) ! number of irreducible representations in molecular_data
1553  nesc = size(escat) ! total number of scattering energies
1554  mxchan = maxval(moldat % nchan) ! maximal number of channels per irreducible representation
1555  ntarg = size(moldat % etarg) ! number of targes (residual ions)
1556  nene = (nesc + nprocs - 1) / nprocs ! number of scattering energies managed by this image
1557 
1558  ! if not all final targets are required, reduce some of the above limits
1559  if (maxtarg > 0) then
1560  mxchan = 0
1561  do irr = 1, nirr
1562  nchan = moldat % nchan(irr)
1563  mxchan = max(mxchan, count(moldat % ichl(1:nchan, irr) <= maxtarg))
1564  end do
1565  ntarg = min(ntarg, maxtarg)
1566  end if
1567 
1568  ! set up final storage
1569  allocate (omega(order), m(mxchan, nene, 0:7), cs(2 + ntarg, nene))
1570  m = 0
1571  cs = 0
1572 
1573  ! the dipole elements stored in molecular_data do not contain the charge, so for each transition we need a factor of -1
1574  q = (-1)**order
1575 
1576  ! calculate fixed-in-space transition matrix elements
1577  ptr => state
1578  do while (associated(ptr))
1579  if (ptr % order == order) then
1580 
1581  ! get polarisation factor for this absorption chain
1582  ej = polar(ptr % dcomp, ptr % order)
1583  parent => ptr % parent
1584  do while (associated(parent % parent))
1585  ej = ej * polar(parent % dcomp, parent % order)
1586  parent => parent % parent
1587  end do
1588 
1589  ! add transition element contribution from this absorption chain
1590  mye = 0
1591  do ie = myproc, nesc, nprocs
1592  mye = mye + 1
1593  do ichan = 1, size(ptr % dip, 1)
1594  d = ej * cmplx(ptr % dip(ichan, 1, mye), ptr % dip(ichan, 2, mye), wp)
1595  m(ichan, mye, ptr % mgvn) = m(ichan, mye, ptr % mgvn) + q * d
1596  end do
1597  end do
1598 
1599  end if
1600  ptr => ptr % next
1601  end do
1602 
1603  ! write partial wave transition moments without Coulomb phase factor
1604  call write_partial_wave_moments(moldat, m, nesc, '')
1605 
1606  ! process transition matrix elements
1607  mye = 0
1608  do ie = myproc, nesc, nprocs
1609 
1610  mye = mye + 1
1611  ei = calc_ei
1612  call calculate_photon_energies(first_ip, escat(ie), moldat % etarg(1), ei, ephoton, omega)
1613 
1614  ! add phase factors to transition matrix elements
1615  do irr = 1, nirr
1616  mgvn = moldat % mgvns(irr)
1617  nchan = min(int(moldat % nchan(irr)), mxchan)
1618  do ichan = 1, nchan
1619  lf = moldat % l2p(ichan, irr)
1620  ef = escat(ie) - moldat % etarg(moldat % ichl(ichan, irr)) + moldat % etarg(1)
1621  if (ef > 0) then
1622  kf = sqrt(2*ef)
1623  sigma = cphase(z, lf, kf)
1624  m(ichan, mye, mgvn) = m(ichan, mye, mgvn) * imu**(-lf) * (cos(sigma) + imu*sin(sigma))
1625  end if
1626  end do
1627  end do
1628 
1629  ! calculate oriented cross section
1630  cs(1, mye) = omega(1) * to_ev
1631  do itarg = 1, ntarg
1632  cs(2 + itarg, mye) = 0
1633  do irr = 1, nirr
1634  mgvn = moldat % mgvns(irr)
1635  nchan = min(int(moldat % nchan(irr)), mxchan)
1636  do ichan = 1, nchan
1637  if (itarg == moldat % ichl(ichan, irr)) then
1638  d = m(ichan, mye, mgvn)
1639  cs(2 + itarg, mye) = cs(2 + itarg, mye) + real(d * conjg(d))
1640  end if
1641  end do
1642  end do
1643  end do
1644  cs(2, mye) = sum(cs(3:, mye))
1645  cs(2:, mye) = cs(2:, mye) * 2*pi * (2*pi*alpha)**order * product(omega)
1646 
1647  end do
1648 
1649  ! write partial wave transition moments with Coulomb phase factor
1650  call write_partial_wave_moments(moldat, m, nesc, '+cphase')
1651 
1652  ! write fixed-in-space cross section
1653  call write_cross_section(cs, nesc, erange, 'gen_photo_xsec.txt')
1654 
1655  end subroutine calculate_pw_transition_elements
1656 
1657 
1679  subroutine calculate_asymmetry_parameters (moldat, order, state, escat, calc_Ei, first_IP, Ephoton, raw, erange)
1680 
1682  use multidip_params, only: alpha, maxtarg
1683  use multidip_special, only: cphase
1684 
1685  character(len=*), intent(in) :: raw
1686  type(moleculardata), intent(in) :: moldat
1687  type(intermediatestate), pointer, intent(in) :: state
1688  type(intermediatestate), pointer :: ptr, parent
1689 
1690  integer, intent(in) :: order, erange(2)
1691  real(wp), intent(in) :: escat(:), calc_Ei, first_IP, Ephoton(:)
1692 
1693  integer :: nesc, ii, li, mi, nchain, ichain
1694  integer :: ntarg, ichan, ie, mye, irr, L, nene, maxl, pw
1695  integer, allocatable :: chains(:, :)
1696  real(wp), allocatable :: omega(:), cs(:, :)
1697  complex(wp), allocatable :: M_xyz(:, :, :, :), M_sph(:, :, :, :), beta(:, :)
1698  complex(wp) :: d
1699  real(wp) :: Z, Ek, k, sigma, Ei, Q
1700  character(len=50) :: filename
1701 
1702  z = moldat % nz - moldat % nelc
1703  nesc = size(escat) ! total number of scattering energies
1704  nene = (nesc + nprocs - 1) / nprocs ! number of scattering energies managed by this image
1705  maxl = maxval(moldat % l2p) ! highest partial wave angular momentum
1706  ntarg = size(moldat % etarg) ! number of targes (residual ions)
1707 
1708  ! if not all final targets are required, reduce some of the above limits
1709  if (maxtarg > 0) then
1710  ntarg = min(ntarg, maxtarg)
1711  maxl = maxval(moldat % l2p, moldat % ichl <= maxtarg)
1712  end if
1713 
1714  ! find out how many absorption chains we have
1715  nchain = 0
1716  ptr => state
1717  do while (associated(ptr))
1718  if (ptr % order == order) then
1719  nchain = nchain + 1
1720  end if
1721  ptr => ptr % next
1722  end do
1723 
1724  allocate (omega(order), chains(order, nchain), cs(ntarg, nene), beta(2 + ntarg, nesc), &
1725  m_xyz(nene, (maxl + 1)**2, nchain, ntarg), &
1726  m_sph(nene, (maxl + 1)**2, nchain, ntarg))
1727 
1728 
1729  m_xyz = 0 ! multiphoton transition matrix elements in Cartesian basis
1730  m_sph = 0 ! multiphoton transition matrix elements in spherical basis
1731 
1732  ! the dipole elements stored in molecular_data do not contain the charge, so for each transition we need a factor of -1
1733  q = (-1)**order
1734 
1735  ! assemble multi-photon molecular transition elements in Cartesian basis
1736  ichain = 0
1737  ptr => state
1738  do while (associated(ptr))
1739  if (ptr % order == order) then
1740 
1741  ! find this irreducible representation in molecular_data
1742  irr = findloc(moldat % mgvns, ptr % mgvn, 1)
1743 
1744  ! assemble polarisation components for this absorption chain
1745  ichain = ichain + 1
1746  parent => ptr
1747  do while (associated(parent % parent))
1748  chains(parent % order, ichain) = mod(1 + parent % dcomp, 3) - 1 ! y ~ -1, z ~ 0, x ~ +1
1749  parent => parent % parent
1750  end do
1751 
1752  ! copy all Cartesian matrix elements for this irreducible representation and absorption history to M_xyz
1753  do ichan = 1, size(ptr % dip, 1)
1754  ii = moldat % ichl(ichan, irr)
1755  li = moldat % l2p(ichan, irr)
1756  mi = moldat % m2p(ichan, irr)
1757  pw = li*li + li + mi + 1
1758  mye = 0
1759  do ie = myproc, nesc, nprocs
1760  mye = mye + 1
1761  ek = escat(ie) - moldat % etarg(ii) + moldat % etarg(1)
1762  if (ek > 0) then
1763  k = sqrt(2*ek)
1764  sigma = cphase(z, li, k)
1765  d = cmplx(ptr % dip(ichan, 1, mye), ptr % dip(ichan, 2, mye), wp)
1766  if (.not. d == d) d = 0 ! clear NaNs
1767  m_xyz(mye, pw, ichain, ii) = imu**(-li) * (cos(sigma) + imu*sin(sigma)) * q * d
1768  end if
1769  end do
1770  end do
1771 
1772  end if
1773  ptr => ptr % next
1774  end do
1775 
1776  if (raw == 'xyz' .or. raw == 'both') then
1777  call write_raw_dipoles(m_xyz, chains, nesc, 'xyz')
1778  end if
1779 
1780  ! transform the multiphoton transition matrix elements from Cartesian basis (M_xyz) to spherical basis (M_sph)
1781  call convert_xyz_to_sph (m_xyz, m_sph, maxl, chains)
1782 
1783  if (raw == 'sph' .or. raw == 'both') then
1784  call write_raw_dipoles(m_sph, chains, nesc, 'sph')
1785  end if
1786 
1787  ! evaluate and write the asymmetry parameters for all possible orders
1788  do l = 0, 2*order
1789 
1790  print '(/,A,I0)', 'Evaluating asymmetry parameter for L = ', l
1791 
1792  ! calculate the absolute asymmetry parameter
1793  call calculate_quadratic_dipole_sph(beta, l, maxl, chains, chains, ntarg, nesc, m_sph, m_sph)
1794 
1795  ! sum partial cross section, add prefactors
1796  mye = 0
1797  do ie = myproc, nesc, nprocs
1798  mye = mye + 1
1799  ei = calc_ei
1800  call calculate_photon_energies(first_ip, escat(ie), moldat % etarg(1), ei, ephoton, omega)
1801  beta(1, mye) = omega(1) * to_ev
1802  beta(2:, mye) = beta(2:, mye) * 2*pi * (2*pi*alpha)**order * product(abs(omega))
1803  if (l == 0) then
1804  cs(:, mye) = real(beta(3:, mye)) ! partial cross sections per target
1805  beta(2, mye) = sum(cs(:, mye)) ! total cross section
1806  else
1807  where (cs(:, mye) > 0)
1808  beta(3:, mye) = beta(3:, mye) / cs(:, mye) ! scale partial asymmetry parameter by the partial cross section
1809  end where
1810  beta(2, :) = 0 ! there is no "total beta" for L > 0
1811  end if
1812  end do
1813 
1814  ! write the asymmetry parameter
1815  write (filename, '(A,I0,A,I0,A)') 'gen_', order, 'photo_beta_', l, '.txt'
1816  call write_cross_section(real(beta, wp), nesc, erange, filename)
1817 
1818  end do
1819 
1820  end subroutine calculate_asymmetry_parameters
1821 
1822 
1830  subroutine convert_xyz_to_sph (M_xyz, M_sph, maxl, chains)
1831 
1832  use dipelm_special_functions, only: sph_basis_transform_elm
1833 
1834  complex(wp), allocatable, intent(in) :: M_xyz(:, :, :, :)
1835  complex(wp), allocatable, intent(inout) :: M_sph(:, :, :, :)
1836  integer, intent(in) :: maxl, chains(:, :)
1837 
1838  integer :: order, ntarg, nchain, i, ii, l, mi, mj, pwi, pwj, ichain, jchain
1839  complex(wp) :: cpl
1840 
1841  order = size(chains, 1)
1842  nchain = size(chains, 2)
1843  ntarg = size(m_xyz, 4)
1844 
1845  m_sph = 0
1846 
1847  do ii = 1, ntarg
1848  do ichain = 1, nchain
1849  do jchain = 1, nchain
1850  do l = 0, maxl
1851  do mi = -l, l
1852  do mj = -l, l
1853  cpl = conjg(sph_basis_transform_elm(l, mj, mi, 'Slm'))
1854  do i = 1, order
1855  cpl = cpl * sph_basis_transform_elm(1, chains(i, jchain), chains(i, ichain), 'Slm')
1856  end do
1857  pwi = l*l + l + mi + 1
1858  pwj = l*l + l + mj + 1
1859  m_sph(:, pwj, jchain, ii) = m_sph(:, pwj, jchain, ii) + cpl * m_xyz(:, pwi, ichain, ii)
1860  end do
1861  end do
1862  end do
1863  end do
1864  end do
1865  end do
1866 
1867  end subroutine convert_xyz_to_sph
1868 
1869 
1878  subroutine calculate_quadratic_dipole_sph (beta, L, maxl, chains1, chains2, ntarg, nesc, M1, M2)
1879 
1880  use mpi_gbl, only: mpi_reduceall_inplace_sum_wp
1881  use multidip_io, only: myproc, nprocs
1883 
1884  complex(wp), allocatable, intent(inout) :: beta(:, :)
1885  complex(wp), allocatable, intent(in) :: M1(:, :, :, :), M2(:, :, :, :)
1886  integer, intent(in) :: L, maxl, chains1(:, :), chains2(:, :), ntarg, nesc
1887 
1888  complex(wp) :: MM
1889  real(wp), allocatable :: T(:, :, :, :), buffer(:)
1890  integer :: ie, mye, itarg, idx, li, mi, lj, mj, pwi, pwj, ichain, jchain, nchain1, nchain2, order1, order2
1891  integer :: qi(size(chains1, 1)), qj(size(chains2, 1)), p(size(chains1, 1))
1892 
1893  p = 0 ! hard-code laboratory-frame polarisations to zero
1894  beta = 0
1895  order1 = size(chains1, 1); nchain1 = size(chains1, 2)
1896  order2 = size(chains2, 1); nchain2 = size(chains2, 2)
1897 
1898  if (order1 /= order2) then
1899  print '(A)', 'WARNING: calculate_quadratic_dipole_sph is implemented only equal absorption orders'
1900  return
1901  end if
1902 
1903  allocate (t((maxl + 1)**2, (maxl + 1)**2, nchain1, nchain2))
1904 
1905  ! erase the angular integrals storage
1906  !$omp parallel do default(none) private(pwi, pwj, ichain, jchain) shared(maxl, nchain1, nchain2, T) collapse(4)
1907  do jchain = 1, nchain2
1908  do ichain = 1, nchain1
1909  do pwj = 1, (maxl + 1)**2
1910  do pwi = 1, (maxl + 1)**2
1911  t(pwi, pwj, ichain, jchain) = 0
1912  end do
1913  end do
1914  end do
1915  end do
1916 
1917  ! calculate angular integrals (distribute over images and threads)
1918  !$omp parallel do default(none) schedule(dynamic) private(idx, pwi, pwj, li, lj, mi, mj, ichain, jchain, qi, qj) &
1919  !$omp& shared(nchain1, nchain2, maxl, chains1, chains2, nprocs, myproc, L, p, T, order1)
1920  do idx = myproc, (maxl + 1)**4 * nchain1 * nchain2, nprocs
1921 
1922  ! unpack idx = ((pwi*(maxl + 1)**2 + pwj)*nchain1 + ichain - 1)*nchain2 + jchain
1923  pwi = 1 + (idx - 1) / (nchain2 * nchain1 * (maxl + 1)**2) ! = 1, ..., (maxl + 1)^2
1924  pwj = 1 + mod((idx - 1) / (nchain2 * nchain1), (maxl + 1)**2) ! = 1, ..., (maxl + 1)^2
1925  ichain = 1 + mod((idx - 1) / nchain2, nchain1) ! = 1, ..., nchain1
1926  jchain = 1 + mod( idx - 1, nchain2) ! = 1, ..., nchain2
1927 
1928  ! unpack pwi = li*li + li + mi + 1
1929  li = floor(sqrt(pwi - 1._wp))
1930  mi = pwi - li*li - li - 1
1931  qi = chains1(:, ichain)
1932 
1933  ! unpack pwj = lj*lj + lj + mj + 1
1934  lj = floor(sqrt(pwj - 1._wp))
1935  mj = pwj - lj*lj - lj - 1
1936  qj = chains2(:, jchain)
1937 
1938  t(pwi, pwj, ichain, jchain) = beta_contraction_tensor(l, order1, p, li, mi, qi, lj, mj, qj)
1939 
1940  end do
1941 
1942  ! allreduce the contraction tensor
1943  buffer = reshape(t, [size(t)])
1944  call mpi_reduceall_inplace_sum_wp(buffer, size(buffer))
1945  t = reshape(buffer, shape(t))
1946 
1947  ! calculate the asymmetry parameter for this image's energies (distribute over threads)
1948  !$omp parallel do default(none) private(idx, itarg, pwi, pwj, ichain, jchain, li, mi, lj, mj, mye, MM) reduction(+:beta) &
1949  !$omp& shared(ntarg, maxl, nchain1, nchain2, myproc, nesc, nprocs, M1, M2, T)
1950  do idx = 1, ntarg * (maxl + 1)**4 * nchain1 * nchain2
1951 
1952  ! unpack idx = (((itarg - 1)*(maxl + 1)**2 + pwi)*(maxl + 1)**2 + pwj)*nchain1 + ichain - 1)*nchain2 + jchain
1953  itarg = 1 + (idx - 1) / (nchain2 * nchain1 * (maxl + 1)**4) ! = 1, ..., ntarg
1954  pwi = 1 + mod((idx - 1) / (nchain2 * nchain1 * (maxl + 1)**2), (maxl + 1)**2) ! = 1, ..., (maxl + 1)^2
1955  pwj = 1 + mod((idx - 1) / (nchain2 * nchain1), (maxl + 1)**2) ! = 1, ..., (maxl + 1)^2
1956  ichain = 1 + mod((idx - 1) / nchain2, nchain1) ! = 1, ..., nchain1
1957  jchain = 1 + mod( idx - 1, nchain2) ! = 1, ..., nchain2
1958 
1959  ! unpack pwi = li*li + li + mi
1960  li = floor(sqrt(pwi - 1._wp))
1961  mi = pwi - li*li - li - 1
1962 
1963  ! unpack pwj = lj*lj + lj + mj
1964  lj = floor(sqrt(pwj - 1._wp))
1965  mj = pwj - lj*lj - lj - 1
1966 
1967  mye = 0
1968  do ie = myproc, nesc, nprocs
1969  mye = mye + 1
1970  mm = conjg(m1(mye, pwi, ichain, itarg)) * m2(mye, pwj, jchain, itarg)
1971  beta(2 + itarg, mye) = beta(2 + itarg, mye) + t(pwi, pwj, ichain, jchain) * mm
1972  end do
1973 
1974  end do
1975 
1976  end subroutine calculate_quadratic_dipole_sph
1977 
1978 
1989  subroutine calculate_quadratic_dipole_xyz (beta, L, maxl, chains1, chains2, ntarg, nesc, M1, M2)
1990 
1991  use multidip_io, only: myproc, nprocs
1993 
1994  complex(wp), allocatable, intent(inout) :: beta(:, :)
1995  complex(wp), allocatable, intent(in) :: M1(:, :, :, :), M2(:, :, :, :)
1996  integer, intent(in) :: L, maxl, chains1(:, :), chains2(:, :), ntarg, nesc
1997 
1998  complex(wp) :: MM
1999  real(wp) :: T
2000  integer :: ie, mye, itarg, li, mi, lj, mj, pwi, pwj, qi(size(chains1, 1)), qj(size(chains2, 1)), ichain, jchain
2001 
2002  beta = 0
2003 
2004  if (l /= 0) then
2005  print '(A)', 'WARNING: calculate_quadratic_dipole_xyz is implemented only for L = 0'
2006  return
2007  end if
2008 
2009  ! calculate the asymmetry parameter
2010  !$omp parallel do collapse(2) default(none) private(ichain, jchain, qi, qj, T, pwi, mye, ie, MM) reduction(+:beta) &
2011  !$omp& shared(chains1, chains2, maxl, myproc, nesc, nprocs, ntarg, M1, M2)
2012  do ichain = 1, size(chains1, 2)
2013  do jchain = 1, size(chains2, 2)
2014  qi = chains1(:, ichain)
2015  qj = chains2(:, jchain)
2017  do pwi = 1, (maxl + 1)**2
2018  mye = 0
2019  do ie = myproc, nesc, nprocs
2020  mye = mye + 1
2021  do itarg = 1, ntarg
2022  mm = conjg(m1(mye, pwi, ichain, itarg)) * m2(mye, pwi, jchain, itarg)
2023  beta(2 + itarg, mye) = beta(2 + itarg, mye) + t * mm
2024  end do
2025  end do
2026  end do
2027  end do
2028  end do
2029 
2030  end subroutine calculate_quadratic_dipole_xyz
2031 
2032 end module multidip_routines
Special integrals needed by MULTIDIP.
complex(wp) function nested_cgreen_integ(Z, a, r0, c, N, sa, sb, m, l, k)
Multi-dimensional integral of Coulomb-Hankel and Coulomb-Green functions and powers.
type(nested_cgreen_integ_cache_t) nested_cgreen_integ_cache
I/O routines used by MULTIDIP.
Definition: multidip_io.F90:29
subroutine read_bndcoeffs(bnd, Ei, lubnd, mgvn0, stot0)
Read inner bound wave function coefficients.
subroutine write_raw_dipoles(M, chains, nesc, stem)
Write raw transition dipole moments.
subroutine read_wfncoeffs(ak, lusct)
Read wave function coefficients from files.
subroutine write_partial_wave_moments(moldat, M, nesc, suffix)
Write partial wave moments.
subroutine scale_boundary_amplitudes(moldat, irr, v, vw)
Scale boundary amplitudes matrix by a diagonal matrix.
subroutine get_diptrans(moldat, I, iidip, ifdip)
Return dipole transition descriptors.
subroutine write_cross_section(cs, nesc, erange, filename)
Write cross sections to a file.
subroutine apply_dipole_matrix(moldat, component, irrpair, transp, nf, nn, X, Y)
Multiply by dipole matrix.
subroutine apply_boundary_amplitudes(moldat, irr, transp, X, Y)
Multiply by boundary amplitudes.
integer nprocs
Definition: multidip_io.F90:49
integer myproc
Definition: multidip_io.F90:48
subroutine read_molecular_data(moldat, filename, mpiio, read_wmat2)
Read RMT molecular_data file.
subroutine read_kmatrices(km, lukmt, nkset)
Read K-matrix files.
subroutine write_energy_grid(escat)
Write photoelectron energies to file.
Routines related to outer region asymptotic channels.
subroutine test_outer_expansion(filename, moldat, irr, ck, ap, Etot)
Evaluate wfn in channels for inside and outside of the R-matrix sphere.
subroutine evaluate_fundamental_solutions(moldat, r, irr, nopen, Ek, S, C, Sp, Cp, sqrtknorm)
Evaluate asymptotic solutions at boundary.
subroutine calculate_k_matrix(moldat, nopen, irr, Etot, S, C, Sp, Cp, Kmat)
Calculate (generalized) K-matrix.
Hard-coded parameters of MULTIDIP.
subroutine read_input_namelist(input, order, lusct, lukmt, lubnd, nkset, polar, omega, verbose, rmt_data, first_IP, rasym, raw, erange, mpiio)
logical extend_istate
Continue initial state from the known inner region expansion outwards.
logical closed_interm
Consider weakly closed channel in intermediate states (unfinished!)
logical custom_kmatrices
Ignore RSOLVE K-matrices and calculate them from scratch.
integer num_integ_algo
Numerical integration mode (1 = Romberg, 2 = Levin)
integer, dimension(3), parameter carti
Position of a Cartesian coordinate in the real spherical basis (y, z, x).
character(len=1), dimension(3), parameter compt
logical cache_integrals
Cache Coulomb-Green integrals in memory (but disables some threading)
real(wp), parameter rone
complex(wp), parameter cone
real(wp) closed_range
Energy interval (a.u.) before threshold for inclusion of closed channels.
real(wp), parameter rzero
integer maxtarg
Maximal number of target states to calculate dipoles for (0 = all)
real(wp), parameter alpha
Fine structure constant.
integer, dimension(3), parameter cartm
Real spherical harmonic m-value corresponding to given a Cartesian coord.
complex(wp), parameter czero
integer, parameter nmaxphotons
The limit on number of photons is nested_cgreen_integ
Main MULTIDIP routines.
real(wp) function channel_coupling_ion(moldat, dcomp, irrf, irri, ichanf, ichani)
Ion channel dipole coupling.
subroutine convert_xyz_to_sph(M_xyz, M_sph, maxl, chains)
Change coordiantes.
subroutine setup_initial_state(states, moldat, irr, lubnd, Ei)
Construct initial state.
subroutine solve_intermediate_state(moldat, order, Ephoton, icomp, s, mgvnn, mgvn1, mgvn2, km, state, verbose, calc_Ei, first_IP, r0, erange)
Calculate intermediate photoionisation state.
recursive complex(wp) function multiint_chain(moldat, r0, Ei, esc, omega, ie, c, N, state, ichanf, sb, k, l, m)
Calculate dipole correction integrals at given absorption depth.
subroutine reset_timer(t, dt)
Get current time stamp.
subroutine calculate_quadratic_dipole_sph(beta, L, maxl, chains1, chains2, ntarg, nesc, M1, M2)
Evaluate asymmetry parameter for given total L in the spherical basis.
subroutine calculate_pw_transition_elements(moldat, order, state, escat, calc_Ei, first_IP, Ephoton, polar, erange)
Calculate partial wave dipoles, oriented dipoles and cross sections.
logical, parameter test_expansion
subroutine calculate_photon_energies(first_IP, escat, etarg, Ei, Ephoton, omega)
Adjust ionization potential and calculate energy of each photon.
subroutine apply_ak_coefficients_multidip(psi, Apsi, moldat, nopen, irr, Etot, Sp, Cp, kmat, tmat, conj)
Multiply vector by the (complex-conjugated) wave function coefficients.
subroutine extract_dipole_elements(moldat, order, Ephoton, icomp, s, mgvnn, mgvn1, mgvn2, km, ak, state, verbose, calc_Ei, first_IP, r0, erange)
Calculate dipole elements from intermediate and final states.
real(wp) function channel_coupling_pws(moldat, dcomp, irrf, irri, ichanf, ichani)
Partial wave channel dipole coupling.
subroutine test_final_expansion(filename, moldat, irr, nopen, Etot, Ek, Sp, Cp, kmat, tmat)
Write radially sampled final wave-function to file.
subroutine print_transition_header(state)
Prints a one-line summary of the transition.
subroutine calculate_asymmetry_parameters(moldat, order, state, escat, calc_Ei, first_IP, Ephoton, raw, erange)
Calculate cross sections and asymmetry parameters.
subroutine calculate_quadratic_dipole_xyz(beta, L, maxl, chains1, chains2, ntarg, nesc, M1, M2)
Evaluate asymmetry parameter for given total L in the Cartesian basis.
subroutine apply_ak_coefficiens_compak(psi, Apsi, ReAk, ImAk)
Multiply vector by the (complex-conjugated) wave function coefficients.
subroutine multidip_driver(order, moldat, km, ak, lubnd, omega, polar, verbose, first_IP, r0, raw, erange)
Central computation routine.
subroutine multiint(moldat, r0, Ei, esc, omega, ie, state, sb, dip)
Evaluate the correction dipole integral for all orders.
subroutine multidip_main
MULTIDIP main subroutine.
Special functions and objects used by MULTIDIP.
subroutine solve_complex_system(n, A, X, Y)
Solve system of equations with complex matrix.
subroutine coul(Z, l, Ek, r, F, Fp, G, Gp)
Coulomb functions.
subroutine calculate_t_matrix(K, T, nopen)
Calculate T-matrix from K-matrix.
subroutine calculate_s_matrix(T, S, nopen)
Calculate S-matrix from T-matrix.
real(wp) recursive function cartesian_vector_component_product_average(q)
Return Cartesian invariant.
real(wp) function beta_contraction_tensor(J, n, p, li, mi, qi, lj, mj, qj)
Angular part of the beta parameters.
real(wp) function cphase(Z, l, k)
Coulomb phase.
MULTIDIP unit tests.
subroutine run_tests
Numerical unit tests.
K-matrix file.
Definition: multidip_io.F90:58
RMT molecular data file.
Photoionization wave function coefficients.
Definition: multidip_io.F90:75