59 use iso_fortran_env,
only: real64
60 #ifdef HAVE_NO_FINDLOC
61 use algorithms,
only: findloc
67 character(:),
allocatable :: str
80 print
'(A)',
'Calculate discrete RABITT time delays using the two-photon raw dipole elements'
84 print
'(A)',
' multidip_rabitt_delays [OPTIONS] --xuv-plus-ir [FILES1] --xuv-minus-ir [FILES2]'
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'
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:'
105 print
'(A)',
' multidip_rabitt_delays --xuv-plus-ir <(ls XUV+IR/rawdip-*) --xuv-minus-ir <(ls XUV-IR/rawdip-*)'
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.'
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&
126 use iso_fortran_env,
only: output_unit
127 use mpi_gbl,
only: mpi_mod_start, mpi_mod_finalize
130 type(
string),
allocatable :: XUV_plus_IR_filenames(:), XUV_minus_IR_filenames(:)
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(:, :, :, :)
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)
140 integer,
allocatable :: target_states(:), chains1(:, :), chains2(:, :)
142 integer :: i, L, order1, order2, maxl, ntarg, nesc, maxlab
144 character(len=200) :: str
146 if (command_argument_count() == 0)
then
151 call print_ukrmol_header(output_unit)
154 print
'(/,A,/)',
'Program RABITT: Calculation of two-photon interference amplitudes'
157 xuv_plus_ir_filenames, xuv_minus_ir_filenames))
then
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
169 m_xuv_plus_ir_sph = m_xuv_plus_ir_xyz
170 m_xuv_minus_ir_sph = m_xuv_minus_ir_xyz
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)
179 allocate (mm(2 + ntarg, nesc), mm0(2 + ntarg, nesc))
181 do l = 0, min(maxlab, order1 + order2)
183 str =
'rabitt-signal-L'
186 if (l == 0 .and. .not. sph)
then
188 m_xuv_plus_ir_xyz, m_xuv_minus_ir_xyz)
194 m_xuv_plus_ir_sph, m_xuv_minus_ir_sph)
201 mm(i,:) = mm(i,:) / mm0(i,:)
204 str =
'rabitt-beta-L'
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)) ]
216 m_xuv_plus_ir_xyz, m_xuv_minus_ir_xyz, axis, axisir, theta, phi)
219 print
'(/,A)',
'Done.'
221 call mpi_mod_finalize
244 XUV_plus_IR_filenames, XUV_minus_IR_filenames)
246 integer,
intent(out) :: ord1, ord2, maxlab
247 real(real64),
intent(out) :: polar(2), polarir(2)
248 logical,
intent(out) :: sph
250 real(real64),
allocatable,
intent(inout) :: theta(:), phi(:)
251 real(real64),
allocatable :: real_array(:)
253 type(
string),
allocatable,
intent(inout) :: xuv_plus_ir_filenames(:), xuv_minus_ir_filenames(:)
254 type(
string),
allocatable :: string_array(:)
256 character(len=8192) :: arg, filenames(2)
258 integer :: iarg, narg, basis, n, ifile, stat, u
260 narg = command_argument_count()
270 main_loop:
do while (iarg <= narg)
272 call get_command_argument(iarg, arg)
274 select case (trim(arg))
281 case (
'--one-photon')
285 case (
'--two-photon')
291 call get_command_argument(iarg, arg)
294 call get_command_argument(iarg, arg)
305 call get_command_argument(iarg, arg)
310 call get_command_argument(iarg, arg)
311 read (arg, *) polar(1)
313 call get_command_argument(iarg, arg)
314 read (arg, *) polar(2)
319 call get_command_argument(iarg, arg)
320 read (arg, *) polarir(1)
322 call get_command_argument(iarg, arg)
323 read (arg, *) polarir(2)
327 do while (iarg <= narg)
328 call get_command_argument(iarg, arg)
329 if (arg(1:2) ==
'--') cycle main_loop
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)
341 do while (iarg <= narg)
342 call get_command_argument(iarg, arg)
343 if (arg(1:2) ==
'--') cycle main_loop
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)
353 case (
'--xuv-plus-ir')
355 call get_command_argument(iarg, filenames(1))
357 case (
'--xuv-minus-ir')
359 call get_command_argument(iarg, filenames(2))
362 print
'(2a)',
'Unknown command line argument ', trim(arg)
371 if (basis == -1)
then
372 print
'(a)',
'Missing one of --cartesian/--spehrical'
380 open (newunit = u, file = trim(filenames(ifile)), action =
'read', iostat = stat)
382 print
'(a)',
'Failed to open file "', trim(filenames(ifile)),
'"'
385 if (ifile == 0)
allocate (xuv_plus_ir_filenames(0))
386 if (ifile == 1)
allocate (xuv_minus_ir_filenames(0))
389 read (u,
'(a)', iostat = stat) arg
390 if (is_iostat_end(stat))
exit
392 print
'(a,i0,3a)',
'Error ', stat,
' while reading from file "', filenames(ifile),
'"'
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
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)
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
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)
427 type(
string),
allocatable,
intent(in) :: XUV_plus_IR_filenames(:), XUV_minus_IR_filenames(:)
429 complex(real64),
allocatable,
intent(inout) :: M_XUV_plus_IR(:, :, :, :), M_XUV_minus_IR(:, :, :, :)
430 integer,
allocatable,
intent(inout) :: target_states(:), chains1(:, :), chains2(:, :)
432 logical,
intent(in) :: sph
433 integer,
intent(in) :: order1, order2
434 integer,
intent(out) :: ntarg, maxl, nesc
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
445 allocate (target_states(ntarg))
447 nplus =
size(xuv_plus_ir_filenames)
448 nminus =
size(xuv_minus_ir_filenames)
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,
').'
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,
'"...'
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)
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)
478 deallocate (filename)
482 allocate (m_xuv_plus_ir(nesc, (maxl + 1)**2, nchain1, ntarg), m_xuv_minus_ir(nesc, (maxl + 1)**2, nchain2, ntarg))
488 do ifile = 1, nplus + nminus
489 if (ifile <= nplus)
then
490 filename = xuv_plus_ir_filenames(ifile) % str
493 filename = xuv_minus_ir_filenames(ifile - nplus) % str
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)
529 integer,
intent(in) :: order
530 integer,
intent(out) :: nchain
531 integer,
allocatable,
intent(inout) :: chains(:, :)
537 allocate (chains(order, nchain))
546 chains(:, idx) = chains(:, idx - 1)
549 increment_chain:
do i = order, 1, -1
550 if (chains(i, idx) < 1)
then
552 chains(i, idx) = chains(i, idx) + 1
558 end do increment_chain
574 character(len=*),
intent(in) :: filename
575 logical,
intent(in) :: sph
576 integer,
intent(in) :: order
577 integer,
intent(out) :: ichain, i, l, m
579 character(len=1) :: c(order)
580 integer :: j, k, q(order), u, v
582 k = scan(filename,
'(', back = .true.)
584 v = scan(filename(1:k-1),
'-', back = .true.)
585 u = scan(filename(1:v-1),
'-', back = .true.)
587 if (v - u /= order + 1)
then
588 print
'(3A,I0)',
'Name of the file "', filename,
'" is not consistent with the given order ', order
593 c(j) = filename(k - 2 - order + j : k - 2 - order + j)
599 case (
'm'); q(j) = -1
601 case (
'p'); q(j) = +1
603 print
'(3A)',
'Dipole component "', c(j),
'" is not valid in spherical basis.'
608 case (
'y'); q(j) = -1
610 case (
'x'); q(j) = +1
612 print
'(3A)',
'Dipole component "', c(j),
'" is not valid in Cartesian basis.'
620 ichain = 3*(ichain - 1) + q(j) + 2
624 k = j + scan(filename(j:),
',') - 1
625 read (filename(j : k - 1), *) i
628 k = j + scan(filename(j:),
',') - 1
629 read (filename(j : k - 1), *) l
632 k = j + scan(filename(j:),
')') - 1
633 read (filename(j : k - 1), *) m
648 character(len=*),
intent(in) :: filename
649 integer,
intent(out) :: n
650 complex(real64),
allocatable,
intent(inout),
optional :: buffer(:)
652 integer :: ierr, u, i
653 real(real64) :: re_d, im_d
655 open (newunit = u, file = filename, action =
'read', form =
'formatted', iostat = ierr)
658 print
'(3A)',
'Failed to open file "', filename,
'"'
666 read (u, *, iostat = ierr) re_d, im_d
667 if (ierr == 0) n = n + 1
671 if (.not.
present(buffer))
then
677 if (
allocated(buffer))
then
678 if (
size(buffer) < n)
then
682 if (.not.
allocated(buffer))
then
690 read (u, *) re_d, im_d
691 buffer(i) = cmplx(re_d, im_d, real64)
705 complex(real64),
allocatable,
intent(in) :: MM(:, :)
706 character(len=*),
intent(in) :: prefix
707 character(len=*),
intent(in) :: stem
708 integer,
intent(in) :: L
710 character(len=200) :: filename
713 write (filename,
'(A,I0,3A)') trim(prefix), l,
'-', trim(stem),
'.txt'
715 print
'(/,3A)',
'Writing M[XUV+IR]* M[XUV-IR] to file "', trim(filename),
'"'
717 open (newunit = u, file = trim(filename), action =
'write', form =
'formatted')
719 do ie = 1,
size(mm, 2)
720 write (u,
'(2E25.15)') sum(mm(:, ie))
762 subroutine calculate_oriented_dipoles (maxl, chains1, chains2, ntarg, nesc, Mp, Mm, axis, axisIR, theta, phi)
764 use dipelm_defs,
only: pi
765 use dipelm_special_functions,
only: a_re_sp_harm, a_sp_harm
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(:, :, :, :)
774 real(real64),
allocatable :: Xlm(:, :), Yl0(:, :), sins(:), coss(:)
775 complex(real64),
allocatable :: Qmol(:, :), Qint(:, :), Qaxs(:, :), Qrec(:, :), QM(:), Xvalues(:)
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
783 if (
allocated(theta)) ntheta =
size(theta)
784 if (
allocated(phi)) nphi =
size(phi)
786 nang = max(ntheta, nphi)
788 if (nang == 0)
return
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))
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
806 call a_re_sp_harm(maxl, rtheta, rphi, xvalues)
807 xlm(:, iang) = real(xvalues, real64)
810 call a_sp_harm(maxl, rtheta, zero, xvalues)
811 yl0(:, iang) = real(xvalues, real64)
814 sins(iang) = sin(rtheta)
815 coss(iang) = cos(rtheta)
822 pw1 = l1*l1 + l1 + m1 + 1
823 pw1a = l1*l1 + l1 + abs(m1) + 1
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)
834 if (sum(axis**2) > 0 .and. sum(axisir**2) > 0)
then
837 do icomp = 1,
size(chains1, 1)
838 i1 = 1 + mod(chains1(icomp, c1) + 2, 3)
839 ncompt(i1) = ncompt(i1) + 1
840 cax = cax * merge(axis(i1), axisir(i1), icomp == 1)
842 do icomp = 1,
size(chains2, 1)
843 i2 = 1 + mod(chains2(icomp, c2) + 2, 3)
844 ncompt(i2) = ncompt(i2) + 1
845 cax = cax * merge(axis(i2), axisir(i2), icomp == 1)
849 if (ncompt(1) == 0 .and. ncompt(2) == 0) cai = 2*pi
850 if (ncompt(1) == 2 .neqv. ncompt(2) == 2) cai = pi
851 if (ncompt(1) == 2 .and. ncompt(2) == 2) cai = pi/4
852 if (ncompt(1) == 4 .or. ncompt(2) == 4) cai = 3*pi/4
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))
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
867 qmol(:, iang) = qmol(:, iang) + qm * cax * xlm(pw1, iang) * xlm(pw2, iang)
869 if (cax /= 0 .and. l1 == l2 .and. m1 == m2 .and. iang == 1)
then
870 qint(:, 1) = qint(:, 1) + qm * cax
872 if (m1 == m2 .and. cai /= 0)
then
873 qaxs(:, iang) = qaxs(:, iang) + qm * cai * yl0(pw1a, iang) * yl0(pw2a, iang)
875 if (m1 == m2 .and. cav /= 0)
then
876 qrec(:, iang) = qrec(:, iang) + qm * cav * yl0(pw1a, iang) * yl0(pw2a, iang)
911 character(len=*),
intent(in) :: stem
912 complex(real64),
allocatable,
intent(in) :: Q(:, :)
914 character(len=256) :: filename
915 integer :: iang, iene, u
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)
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.
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.