Multidip  1.0
Multi-photon matrix elements
multidip_rabitt_delays.F90
Go to the documentation of this file.
1 ! Copyright 2021
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 !
22 
58 
59  use iso_fortran_env, only: real64
60 #ifdef HAVE_NO_FINDLOC
61  use algorithms, only: findloc
62 #endif
63 
64  implicit none
65 
66  type string
67  character(:), allocatable :: str
68  end type string
69 
70  call rabitt_main
71 
72 contains
73 
78  subroutine print_help
79 
80  print '(A)', 'Calculate discrete RABITT time delays using the two-photon raw dipole elements'
81  print '( )'
82  print '(A)', 'Usage:'
83  print '( )'
84  print '(A)', ' multidip_rabitt_delays [OPTIONS] --xuv-plus-ir [FILES1] --xuv-minus-ir [FILES2]'
85  print '( )'
86  print '(A)', 'Options:'
87  print '(A)', ' --help Display this help'
88  print '(A)', ' --one-photon Calculate one-photon delays (equivalent to --orders 1 1)'
89  print '(A)', ' --two-photon Calculate two-photon delays (equivalent to --orders 2 2)'
90  print '(A)', ' --orders M N Calculate mixed-photon delays'
91  print '(A)', ' --spherical Assume matrix elements in the spherical basis'
92  print '(A)', ' --cartesian Assume matrix elements in the Cartesian basis'
93  print '(A)', ' --xuv-plus-ir FILE File with paths to files containing raw matrix elements for the XUV+IR pathway'
94  print '(A)', ' --xuv-minus-ir FILE File with paths to files containing raw matrix elements for the XUV-IR pathway'
95  print '( )'
96  print '(A)', ' --max-lab MAXL Maximal angular momentum for asymmetry parameer (default: 0)'
97  print '(A)', ' --polar THETA PHI Polarization direction of XUV in molecular frame (degrees, default: 0 0, i.e. z)'
98  print '(A)', ' --polar-ir THETA PHI As above but for IR, by default oriented in the same direction as XUV'
99  print '(A)', ' --theta ANGLE(s) Polar emission angles (degrees, default: 0)'
100  print '(A)', ' --phi ANGLE(s) Azimuthal emission angles (degrees, default: 0)'
101  print '(A)', 'The files containing the list of paths to raw dipole data are assumed to contain one path on each line.&
102  & A convenient way how to pass all files of a given mask to this program in some Unix-like shells is using&
103  & the so-called process substitution like this:'
104  print '( )'
105  print '(A)', ' multidip_rabitt_delays --xuv-plus-ir <(ls XUV+IR/rawdip-*) --xuv-minus-ir <(ls XUV-IR/rawdip-*)'
106  print '( )'
107  print '(A)', 'Note that the file names provided on the command line need to satisfy the original MULTIDIP naming scheme,&
108  & so that it is possible to extract the partial wave quantum numbers.'
109  print '( )'
110  print '(A)', 'The resulting RABITT cross section interference term will be written to file "rabitt-signal-L0-xyz.txt" or&
111  & "rabitt-signal-L0-sph.txt" depending on the selected mode. To obtain the resulting&
112  & molecular orientation averaged photoionization delays, one needs to take the complex argument and divide&
113  & it by 2*omega_IR.'
114 
115  end subroutine print_help
116 
117 
124  subroutine rabitt_main
125 
126  use iso_fortran_env, only: output_unit
127  use mpi_gbl, only: mpi_mod_start, mpi_mod_finalize
129 
130  type(string), allocatable :: XUV_plus_IR_filenames(:), XUV_minus_IR_filenames(:)
131 
132  complex(real64), allocatable :: MM(:, :), MM0(:,:)
133  complex(real64), allocatable :: M_XUV_plus_IR_sph(:, :, :, :), M_XUV_minus_IR_sph(:, :, :, :)
134  complex(real64), allocatable :: M_XUV_plus_IR_xyz(:, :, :, :), M_XUV_minus_IR_xyz(:, :, :, :)
135 
136  real(real64), parameter :: rone = 1, pi = 4*atan(rone)
137  real(real64), allocatable :: theta(:), phi(:)
138  real(real64) :: polar(2), polarIR(2), axis(3), axisIR(3)
139 
140  integer, allocatable :: target_states(:), chains1(:, :), chains2(:, :)
141 
142  integer :: i, L, order1, order2, maxl, ntarg, nesc, maxlab
143  logical :: sph
144  character(len=200) :: str
145 
146  if (command_argument_count() == 0) then
147  call print_help
148  return
149  end if
150 
151  call print_ukrmol_header(output_unit)
152  call mpi_mod_start
153 
154  print '(/,A,/)', 'Program RABITT: Calculation of two-photon interference amplitudes'
155 
156  if (.not. process_command_line(order1, order2, sph, maxlab, polar, polarir, theta, phi, &
157  xuv_plus_ir_filenames, xuv_minus_ir_filenames)) then
158  return
159  end if
160 
161  ! read raw multipole elements from disk
162  call read_raw_multipoles(order1, order2, sph, ntarg, maxl, nesc, target_states, chains1, chains2, &
163  xuv_plus_ir_filenames, xuv_minus_ir_filenames, &
164  m_xuv_plus_ir_xyz, m_xuv_minus_ir_xyz)
165  if (nesc == 0) return
166 
167  ! convert Cartesian matrix elements to spherical basis
168  if (sph) then
169  m_xuv_plus_ir_sph = m_xuv_plus_ir_xyz
170  m_xuv_minus_ir_sph = m_xuv_minus_ir_xyz
171  else
172  allocate (m_xuv_plus_ir_sph, mold = m_xuv_plus_ir_xyz)
173  allocate (m_xuv_minus_ir_sph, mold = m_xuv_minus_ir_xyz)
174  call convert_xyz_to_sph(m_xuv_plus_ir_xyz, m_xuv_plus_ir_sph, maxl, chains1)
175  call convert_xyz_to_sph(m_xuv_minus_ir_xyz, m_xuv_minus_ir_sph, maxl, chains2)
176  end if
177 
178  ! allocate final storage for the quadratic dipoles
179  allocate (mm(2 + ntarg, nesc), mm0(2 + ntarg, nesc))
180 
181  do l = 0, min(maxlab, order1 + order2)
182 
183  str = 'rabitt-signal-L'
184 
185  ! for checking purposes, evaluate the isotropic parameter in the Cartesian basis
186  if (l == 0 .and. .not. sph) then
187  call calculate_quadratic_dipole_xyz(mm, l, maxl, chains1, chains2, ntarg, nesc, &
188  m_xuv_plus_ir_xyz, m_xuv_minus_ir_xyz)
189  call write_quadratic_dipoles(l, mm, str, 'xyz')
190  end if
191 
192  ! evaluate the asymmetry parameter in the spherical basis
193  call calculate_quadratic_dipole_sph(mm, l, maxl, chains1, chains2, ntarg, nesc, &
194  m_xuv_plus_ir_sph, m_xuv_minus_ir_sph)
195  call write_quadratic_dipoles(l, mm, str, 'sph')
196 
197  if (l == 0) then
198  mm0 = mm
199  else
200  do i = 3, 2 + ntarg
201  mm(i,:) = mm(i,:) / mm0(i,:)
202  enddo
203 
204  str = 'rabitt-beta-L'
205  call write_quadratic_dipoles(l, mm, str, '')
206  endif
207 
208  end do
209 
210  if (.not. sph) then
211  polar = polar * pi / 180
212  polarir = polarir * pi / 180
213  axis = [ sin(polar(1))*cos(polar(2)), sin(polar(1))*sin(polar(2)), cos(polar(1)) ]
214  axisir = [ sin(polarir(1))*cos(polarir(2)), sin(polarir(1))*sin(polarir(2)), cos(polarir(1)) ]
215  call calculate_oriented_dipoles(maxl, chains1, chains2, ntarg, nesc, &
216  m_xuv_plus_ir_xyz, m_xuv_minus_ir_xyz, axis, axisir, theta, phi)
217  end if
218 
219  print '(/,A)', 'Done.'
220 
221  call mpi_mod_finalize
222 
223  end subroutine rabitt_main
224 
225 
243  logical function process_command_line (ord1, ord2, sph, maxlab, polar, polarIR, theta, phi, &
244  XUV_plus_IR_filenames, XUV_minus_IR_filenames)
245 
246  integer, intent(out) :: ord1, ord2, maxlab
247  real(real64), intent(out) :: polar(2), polarir(2)
248  logical, intent(out) :: sph
249 
250  real(real64), allocatable, intent(inout) :: theta(:), phi(:)
251  real(real64), allocatable :: real_array(:)
252 
253  type(string), allocatable, intent(inout) :: xuv_plus_ir_filenames(:), xuv_minus_ir_filenames(:)
254  type(string), allocatable :: string_array(:)
255 
256  character(len=8192) :: arg, filenames(2)
257 
258  integer :: iarg, narg, basis, n, ifile, stat, u
259 
260  narg = command_argument_count()
261  iarg = 1
262  polar = 0
263  polarir = 0
264  ord1 = 2
265  ord2 = 2
266  basis = -1
267  maxlab = 0
268  process_command_line = .true.
269 
270  main_loop: do while (iarg <= narg)
271 
272  call get_command_argument(iarg, arg)
273 
274  select case (trim(arg))
275 
276  case ('--help')
277  call print_help
278  process_command_line = .false.
279  return
280 
281  case ('--one-photon')
282  ord1 = 1
283  ord2 = 1
284 
285  case ('--two-photon')
286  ord1 = 2
287  ord2 = 2
288 
289  case ('--orders')
290  iarg = iarg + 1
291  call get_command_argument(iarg, arg)
292  read (arg, *) ord1
293  iarg = iarg + 1
294  call get_command_argument(iarg, arg)
295  read (arg, *) ord2
296 
297  case ('--cartesian')
298  basis = 1
299 
300  case ('--spherical')
301  basis = 2
302 
303  case ('--max-lab')
304  iarg = iarg + 1
305  call get_command_argument(iarg, arg)
306  read (arg, *) maxlab
307 
308  case ('--polar')
309  iarg = iarg + 1
310  call get_command_argument(iarg, arg)
311  read (arg, *) polar(1)
312  iarg = iarg + 1
313  call get_command_argument(iarg, arg)
314  read (arg, *) polar(2)
315  polarir = polar
316 
317  case ('--polar-ir')
318  iarg = iarg + 1
319  call get_command_argument(iarg, arg)
320  read (arg, *) polarir(1)
321  iarg = iarg + 1
322  call get_command_argument(iarg, arg)
323  read (arg, *) polarir(2)
324 
325  case ('--theta')
326  iarg = iarg + 1
327  do while (iarg <= narg)
328  call get_command_argument(iarg, arg)
329  if (arg(1:2) == '--') cycle main_loop
330  n = 0
331  if (allocated(theta)) n = size(theta)
332  allocate (real_array(n + 1))
333  if (allocated(theta)) real_array(1:n) = theta(1:n)
334  call move_alloc(real_array, theta)
335  read (arg, *) theta(n + 1)
336  iarg = iarg + 1
337  end do
338 
339  case ('--phi')
340  iarg = iarg + 1
341  do while (iarg <= narg)
342  call get_command_argument(iarg, arg)
343  if (arg(1:2) == '--') cycle main_loop
344  n = 0
345  if (allocated(phi)) n = size(phi)
346  allocate (real_array(n + 1))
347  if (allocated(theta)) real_array(1:n) = phi(1:n)
348  call move_alloc(real_array, phi)
349  read (arg, *) phi(n + 1)
350  iarg = iarg + 1
351  end do
352 
353  case ('--xuv-plus-ir')
354  iarg = iarg + 1
355  call get_command_argument(iarg, filenames(1))
356 
357  case ('--xuv-minus-ir')
358  iarg = iarg + 1
359  call get_command_argument(iarg, filenames(2))
360 
361  case default
362  print '(2a)', 'Unknown command line argument ', trim(arg)
363  stop 1
364 
365  end select
366 
367  iarg = iarg + 1
368 
369  end do main_loop
370 
371  if (basis == -1) then
372  print '(a)', 'Missing one of --cartesian/--spehrical'
373  stop 1
374  end if
375 
376  sph = basis == 2
377 
378  ! read filenames from the XUV+IR and XUV-IR files
379  do ifile = 1, 2
380  open (newunit = u, file = trim(filenames(ifile)), action = 'read', iostat = stat)
381  if (stat /= 0) then
382  print '(a)', 'Failed to open file "', trim(filenames(ifile)), '"'
383  stop 1
384  end if
385  if (ifile == 0) allocate (xuv_plus_ir_filenames(0))
386  if (ifile == 1) allocate (xuv_minus_ir_filenames(0))
387  n = 0
388  do
389  read (u, '(a)', iostat = stat) arg
390  if (is_iostat_end(stat)) exit
391  if (stat /= 0) then
392  print '(a,i0,3a)', 'Error ', stat, ' while reading from file "', filenames(ifile), '"'
393  stop 1
394  end if
395  allocate (string_array(n + 1))
396  if (ifile == 1 .and. n >= 1) string_array(1:n) = xuv_plus_ir_filenames
397  if (ifile == 2 .and. n >= 1) string_array(1:n) = xuv_minus_ir_filenames
398  n = n + 1
399  string_array(n) % str = trim(arg)
400  if (ifile == 1) call move_alloc(string_array, xuv_plus_ir_filenames)
401  if (ifile == 2) call move_alloc(string_array, xuv_minus_ir_filenames)
402  end do
403  close (u)
404  end do
405 
406  print '(a,2x,i0,2x,i0)', 'Photon orders:', ord1, ord2
407  print '(a,1x,a)', 'Angular basis:', merge('spherical', 'cartesian', sph)
408  print '(a,1x,2(1x,f0.3))', 'XUV polarization:', polar
409  print '(a,1x,2(1x,f0.3))', 'IR polarization:', polarir
410  print '(a,5x,i0)', 'Max lab L:', maxlab
411  if (allocated(theta)) print '(a,1x,*(1x,f0.1))', 'Polar angles:', theta
412  if (allocated(phi)) print '(a,1x,*(1x,f0.1))', 'Azimut angles:', phi
413  print '()'
414 
415  end function process_command_line
416 
417 
424  subroutine read_raw_multipoles (order1, order2, sph, ntarg, maxl, nesc, target_states, chains1, chains2, &
425  XUV_plus_IR_filenames, XUV_minus_IR_filenames, M_XUV_plus_IR, M_XUV_minus_IR)
426 
427  type(string), allocatable, intent(in) :: XUV_plus_IR_filenames(:), XUV_minus_IR_filenames(:)
428 
429  complex(real64), allocatable, intent(inout) :: M_XUV_plus_IR(:, :, :, :), M_XUV_minus_IR(:, :, :, :)
430  integer, allocatable, intent(inout) :: target_states(:), chains1(:, :), chains2(:, :)
431 
432  logical, intent(in) :: sph
433  integer, intent(in) :: order1, order2
434  integer, intent(out) :: ntarg, maxl, nesc
435 
436  character(len=:),allocatable :: filename
437  complex(real64), allocatable :: buffer(:)
438  integer, allocatable :: new_target_states(:)
439  integer :: i, idx, ichain, nchain1, nchain2, l, m, nene, ifile, nplus, nminus
440 
441  ntarg = 0
442  maxl = 0
443  nesc = 0
444 
445  allocate (target_states(ntarg))
446 
447  nplus = size(xuv_plus_ir_filenames)
448  nminus = size(xuv_minus_ir_filenames)
449 
450  call setup_chains(order1, nchain1, chains1)
451  call setup_chains(order2, nchain2, chains2)
452 
453  if (nplus /= nminus) then
454  print '(a,i0,a,i0,a,/)', 'Warning: Number of XUV-IR and XUV+IR files is different (', nminus, ' vs ', nplus, ').'
455  end if
456 
457  ! first pass over all files to get limiting values
458  do ifile = 1, nplus + nminus
459  if (ifile <= nplus) filename = xuv_plus_ir_filenames(ifile) % str
460  if (ifile > nplus) filename = xuv_minus_ir_filenames(ifile - nplus) % str
461  print '(3A)', 'Reading file "', filename, '"...'
462  call read_file_data(filename, nene)
463  if (ifile <= nplus) call parse_file_name(filename, order1, sph, ichain, i, l, m)
464  if (ifile > nplus) call parse_file_name(filename, order2, sph, ichain, i, l, m)
465  print '(2(A,I0),A,SP,I0)', ' - target state: ', i, ', partial wave: ', l, ', ', m
466  print '(A,I0)', ' - number of energies: ', nene
467  if (ifile <= nplus) print '(A,*(1x,I0))', ' - absorption chain:', chains1(:, ichain)
468  if (ifile > nplus) print '(A,*(1x,I0))', ' - absorption chain:', chains2(:, ichain)
469  maxl = max(maxl, l)
470  nesc = max(nesc, nene)
471  if (findloc(target_states, i, 1) == 0) then
472  allocate (new_target_states(ntarg + 1))
473  new_target_states(1 : ntarg) = target_states(1:ntarg)
474  new_target_states(1 + ntarg) = i
475  call move_alloc(new_target_states, target_states)
476  ntarg = ntarg + 1
477  end if
478  deallocate (filename)
479  end do
480 
481  ! resize the multipole storage
482  allocate (m_xuv_plus_ir(nesc, (maxl + 1)**2, nchain1, ntarg), m_xuv_minus_ir(nesc, (maxl + 1)**2, nchain2, ntarg))
483 
484  m_xuv_plus_ir = 0
485  m_xuv_minus_ir = 0
486 
487  ! actually read the dipoles now
488  do ifile = 1, nplus + nminus
489  if (ifile <= nplus) then
490  filename = xuv_plus_ir_filenames(ifile) % str
491  call parse_file_name(filename, order1, sph, ichain, i, l, m)
492  else
493  filename = xuv_minus_ir_filenames(ifile - nplus) % str
494  call parse_file_name(filename, order2, sph, ichain, i, l, m)
495  end if
496  call read_file_data(filename, nene, buffer)
497  idx = findloc(target_states, i, 1)
498  if (ifile <= nplus) m_xuv_plus_ir(1:nene, l*l + l + m + 1, ichain, idx) = buffer(1:nene)
499  if (ifile > nplus) m_xuv_minus_ir(1:nene, l*l + l + m + 1, ichain, idx) = buffer(1:nene)
500  deallocate (filename)
501  end do
502 
503  end subroutine read_raw_multipoles
504 
505 
527  subroutine setup_chains (order, nchain, chains)
528 
529  integer, intent(in) :: order
530  integer, intent(out) :: nchain
531  integer, allocatable, intent(inout) :: chains(:, :)
532 
533  integer :: i, idx
534 
535  nchain = 3**order
536 
537  allocate (chains(order, nchain))
538 
539  ! the first pathway uses the lowest labels for all photons
540  chains(:, 1) = -1
541 
542  ! build other pathways one after another
543  do idx = 2, 3**order
544 
545  ! use the previous pathway as starting point
546  chains(:, idx) = chains(:, idx - 1)
547 
548  ! atempt to increment the sequence (least significant digit last)
549  increment_chain: do i = order, 1, -1
550  if (chains(i, idx) < 1) then
551  ! if possible, increment the right-most value and exit
552  chains(i, idx) = chains(i, idx) + 1
553  exit increment_chain
554  else
555  ! otherwise wrap around to the lowest index and try next
556  chains(i, idx) = -1
557  end if
558  end do increment_chain
559 
560  end do
561 
562  end subroutine setup_chains
563 
564 
572  subroutine parse_file_name (filename, order, sph, ichain, i, l, m)
573 
574  character(len=*), intent(in) :: filename
575  logical, intent(in) :: sph
576  integer, intent(in) :: order
577  integer, intent(out) :: ichain, i, l, m
578 
579  character(len=1) :: c(order)
580  integer :: j, k, q(order), u, v
581 
582  k = scan(filename, '(', back = .true.)
583 
584  v = scan(filename(1:k-1), '-', back = .true.)
585  u = scan(filename(1:v-1), '-', back = .true.)
586 
587  if (v - u /= order + 1) then
588  print '(3A,I0)', 'Name of the file "', filename, '" is not consistent with the given order ', order
589  stop 1
590  end if
591 
592  do j = 1, order
593  c(j) = filename(k - 2 - order + j : k - 2 - order + j)
594  end do
595 
596  do j = 1, order
597  if (sph) then
598  select case (c(j))
599  case ('m'); q(j) = -1
600  case ('0'); q(j) = 0
601  case ('p'); q(j) = +1
602  case default
603  print '(3A)', 'Dipole component "', c(j), '" is not valid in spherical basis.'
604  stop 1
605  end select
606  else
607  select case (c(j))
608  case ('y'); q(j) = -1
609  case ('z'); q(j) = 0
610  case ('x'); q(j) = +1
611  case default
612  print '(3A)', 'Dipole component "', c(j), '" is not valid in Cartesian basis.'
613  stop 1
614  end select
615  end if
616  end do
617 
618  ichain = 1
619  do j = 1, order
620  ichain = 3*(ichain - 1) + q(j) + 2
621  end do
622 
623  j = k + 1
624  k = j + scan(filename(j:), ',') - 1
625  read (filename(j : k - 1), *) i
626 
627  j = k + 1
628  k = j + scan(filename(j:), ',') - 1
629  read (filename(j : k - 1), *) l
630 
631  j = k + 1
632  k = j + scan(filename(j:), ')') - 1
633  read (filename(j : k - 1), *) m
634 
635  end subroutine parse_file_name
636 
637 
646  subroutine read_file_data (filename, n, buffer)
647 
648  character(len=*), intent(in) :: filename
649  integer, intent(out) :: n
650  complex(real64), allocatable, intent(inout), optional :: buffer(:)
651 
652  integer :: ierr, u, i
653  real(real64) :: re_d, im_d
654 
655  open (newunit = u, file = filename, action = 'read', form = 'formatted', iostat = ierr)
656 
657  if (ierr /= 0) then
658  print '(3A)', 'Failed to open file "', filename, '"'
659  stop 1
660  end if
661 
662  ! first pass: count valid lines only
663  ierr = 0
664  n = 0
665  do while (ierr == 0)
666  read (u, *, iostat = ierr) re_d, im_d
667  if (ierr == 0) n = n + 1
668  end do
669 
670  ! if no buffer was given, do not read the data
671  if (.not. present(buffer)) then
672  close (u)
673  return
674  end if
675 
676  ! resize the buffer
677  if (allocated(buffer)) then
678  if (size(buffer) < n) then
679  deallocate (buffer)
680  end if
681  end if
682  if (.not. allocated(buffer)) then
683  allocate (buffer(n))
684  end if
685 
686  rewind(u)
687 
688  ! actually read the data now
689  do i = 1, n
690  read (u, *) re_d, im_d
691  buffer(i) = cmplx(re_d, im_d, real64)
692  end do
693 
694  close (u)
695 
696  end subroutine read_file_data
697 
698 
703  subroutine write_quadratic_dipoles (L, MM, prefix, stem)
704 
705  complex(real64), allocatable, intent(in) :: MM(:, :)
706  character(len=*), intent(in) :: prefix
707  character(len=*), intent(in) :: stem
708  integer, intent(in) :: L
709 
710  character(len=200) :: filename
711  integer :: u, ie
712 
713  write (filename, '(A,I0,3A)') trim(prefix), l, '-', trim(stem), '.txt'
714 
715  print '(/,3A)', 'Writing M[XUV+IR]* M[XUV-IR] to file "', trim(filename), '"'
716 
717  open (newunit = u, file = trim(filename), action = 'write', form = 'formatted')
718 
719  do ie = 1, size(mm, 2)
720  write (u, '(2E25.15)') sum(mm(:, ie))
721  end do
722 
723  close (u)
724 
725  end subroutine write_quadratic_dipoles
726 
727 
762  subroutine calculate_oriented_dipoles (maxl, chains1, chains2, ntarg, nesc, Mp, Mm, axis, axisIR, theta, phi)
763 
764  use dipelm_defs, only: pi
765  use dipelm_special_functions, only: a_re_sp_harm, a_sp_harm
767 
768  integer, intent(in) :: maxl, ntarg, nesc
769  integer, allocatable, intent(in) :: chains1(:, :), chains2(:, :)
770  real(real64), intent(in) :: axis(3), axisIR(3)
771  real(real64), allocatable, intent(in) :: theta(:), phi(:)
772  complex(real64), allocatable, intent(in) :: Mp(:, :, :, :), Mm(:, :, :, :)
773 
774  real(real64), allocatable :: Xlm(:, :), Yl0(:, :), sins(:), coss(:)
775  complex(real64), allocatable :: Qmol(:, :), Qint(:, :), Qaxs(:, :), Qrec(:, :), QM(:), Xvalues(:)
776 
777  integer :: l1, l2, m1, m2, pw1, pw2, pw1a, pw2a, c1, c2, i1, i2, icomp, iang, nang, ntheta, nphi, itg, ncompt(3)
778  real(real64) :: cav, cai, cax, rtheta, rphi, zero = 0
779 
780  ntheta = 0
781  nphi = 0
782 
783  if (allocated(theta)) ntheta = size(theta)
784  if (allocated(phi)) nphi = size(phi)
785 
786  nang = max(ntheta, nphi)
787 
788  if (nang == 0) return
789 
790  allocate (xlm((maxl + 1)**2, nang), yl0((maxl + 1)**2, nang), qmol(nesc, nang), qaxs(nesc, nang), qrec(nesc, nang), &
791  qint(nesc, 1), sins(nang), coss(nang), qm(nesc))
792 
793  qmol = 0
794  qint = 0
795  qaxs = 0
796  qrec = 0
797 
798  ! precalculate angular functions
799  do iang = 1, nang
800  rtheta = 0
801  rphi = 0
802 
803  if (ntheta > 0) rtheta = theta(1 + mod(iang - 1, ntheta)) * pi / 180
804  if (nphi > 0) rphi = phi(1 + mod(iang - 1, nphi)) * pi / 180
805 
806  call a_re_sp_harm(maxl, rtheta, rphi, xvalues)
807  xlm(:, iang) = real(xvalues, real64) ! X_{lm}(theta, phi)
808  deallocate (xvalues)
809 
810  call a_sp_harm(maxl, rtheta, zero, xvalues)
811  yl0(:, iang) = real(xvalues, real64) ! Y_{lm}(theta, 0)
812  deallocate (xvalues)
813 
814  sins(iang) = sin(rtheta)
815  coss(iang) = cos(rtheta)
816  end do
817 
818  ! evaluate the interference terms
819  do itg = 1, ntarg
820  do l1 = 0, maxl
821  do m1 = -l1, l1
822  pw1 = l1*l1 + l1 + m1 + 1
823  pw1a = l1*l1 + l1 + abs(m1) + 1
824  do l2 = 0, maxl
825  do m2 = -l2, l2
826  pw2 = l2*l2 + l2 + m2 + 1
827  pw2a = l2*l2 + l2 + abs(m2) + 1
828  do c1 = 1, size(chains1, 2)
829  do c2 = 1, size(chains2, 2)
830  cax = 0
831  cai = 0
832  ncompt = 0
833 
834  if (sum(axis**2) > 0 .and. sum(axisir**2) > 0) then
835  ! product of oriented polarization unit vectors
836  cax = 1
837  do icomp = 1, size(chains1, 1)
838  i1 = 1 + mod(chains1(icomp, c1) + 2, 3) ! m projection -> Cartesian index
839  ncompt(i1) = ncompt(i1) + 1
840  cax = cax * merge(axis(i1), axisir(i1), icomp == 1)
841  end do
842  do icomp = 1, size(chains2, 1)
843  i2 = 1 + mod(chains2(icomp, c2) + 2, 3) ! m projection -> Cartesian index
844  ncompt(i2) = ncompt(i2) + 1
845  cax = cax * merge(axis(i2), axisir(i2), icomp == 1)
846  end do
847 
848  ! azimuthal integral over the product of polarization vectors (assume axis == axisIR)
849  if (ncompt(1) == 0 .and. ncompt(2) == 0) cai = 2*pi ! zz, zzzz
850  if (ncompt(1) == 2 .neqv. ncompt(2) == 2) cai = pi ! xx, yy, xxzz, yyzz
851  if (ncompt(1) == 2 .and. ncompt(2) == 2) cai = pi/4 ! xxyy
852  if (ncompt(1) == 4 .or. ncompt(2) == 4) cai = 3*pi/4 ! xxxx, yyyy
853  if (any(mod(ncompt, 2) /= 0)) cai = 0
854  cai = cai * axis(3)**ncompt(3) * hypot(axis(1), axis(2))**(ncompt(1) + ncompt(2))
855  end if
856 
857  ! angular average of product of polarization unit vectors
858  cav = cartesian_vector_component_product_average([chains1(:, c1), chains2(:, c2)])
859 
860  ! update the value of the RABITT interference term
861  if (cax /= 0 .or. cai /= 0 .or. cav /= 0) then
862  qm = conjg(mp(:, pw1, c1, itg)) * mm(:, pw2, c2, itg)
863  if (all(qm == 0)) cycle
864  !$omp parallel do
865  do iang = 1, nang
866  if (cax /= 0) then
867  qmol(:, iang) = qmol(:, iang) + qm * cax * xlm(pw1, iang) * xlm(pw2, iang)
868  end if
869  if (cax /= 0 .and. l1 == l2 .and. m1 == m2 .and. iang == 1) then
870  qint(:, 1) = qint(:, 1) + qm * cax
871  end if
872  if (m1 == m2 .and. cai /= 0) then
873  qaxs(:, iang) = qaxs(:, iang) + qm * cai * yl0(pw1a, iang) * yl0(pw2a, iang)
874  end if
875  if (m1 == m2 .and. cav /= 0) then
876  qrec(:, iang) = qrec(:, iang) + qm * cav * yl0(pw1a, iang) * yl0(pw2a, iang)
877  end if
878  end do
879  end if
880  end do ! c2
881  end do ! c1
882  end do ! m2
883  end do ! l2
884  end do ! m1
885  end do ! l1
886  end do ! itg
887 
888  ! write fixed-polarization results to files (one per emission direction)
889  call write_to_file('rabitt-signal-mol-', qmol)
890  call write_to_file('rabitt-signal-int-', qint)
891  call write_to_file('rabitt-signal-axs-', qaxs)
892 
893  ! write fully polarization-averaged results to files (one per emission direction)
894  call write_to_file('rabitt-signal-rec-', qrec)
895 
896  end subroutine calculate_oriented_dipoles
897 
898 
909  subroutine write_to_file (stem, Q)
910 
911  character(len=*), intent(in) :: stem
912  complex(real64), allocatable, intent(in) :: Q(:, :)
913 
914  character(len=256) :: filename
915  integer :: iang, iene, u
916 
917  do iang = 1, size(q, 2)
918  write (filename, '(A,I0,A)') stem, iang, '.txt'
919  open (newunit = u, file = trim(filename), form = 'formatted')
920  do iene = 1, size(q, 1)
921  write (u, '(2E25.15)') q(iene, iang)
922  end do
923  close (u)
924  end do
925 
926  end subroutine write_to_file
927 
928 end program multidip_rabitt_delays
subroutine rabitt_main
Main program.
subroutine write_quadratic_dipoles(L, MM, prefix, stem)
Write results to file.
subroutine calculate_oriented_dipoles(maxl, chains1, chains2, ntarg, nesc, Mp, Mm, axis, axisIR, theta, phi)
Molecular- and recoil-frame RABITT delays.
subroutine read_raw_multipoles(order1, order2, sph, ntarg, maxl, nesc, target_states, chains1, chains2, XUV_plus_IR_filenames, XUV_minus_IR_filenames, M_XUV_plus_IR, M_XUV_minus_IR)
Read multipoles from disk.
subroutine write_to_file(stem, Q)
Write the interference term to file.
subroutine parse_file_name(filename, order, sph, ichain, i, l, m)
Extract quantum numbers from raw multipole file name.
logical function process_command_line(ord1, ord2, sph, maxlab, polar, polarIR, theta, phi, XUV_plus_IR_filenames, XUV_minus_IR_filenames)
Read options from the command line.
program multidip_rabitt_delays
Calculate RABITT delays from raw multipole elements.
subroutine setup_chains(order, nchain, chains)
Assemble absorption chains.
subroutine print_help
Print program usage information.
subroutine read_file_data(filename, n, buffer)
Read raw multipole from file.
Main MULTIDIP routines.
subroutine convert_xyz_to_sph(M_xyz, M_sph, maxl, chains)
Change coordiantes.
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_quadratic_dipole_xyz(beta, L, maxl, chains1, chains2, ntarg, nesc, M1, M2)
Evaluate asymmetry parameter for given total L in the Cartesian basis.
Special functions and objects used by MULTIDIP.
real(wp) recursive function cartesian_vector_component_product_average(q)
Return Cartesian invariant.