|
| 1 | +# * Diagrams |
| 2 | + |
| 3 | +""" |
| 4 | + Diagram(path, couplings) |
| 5 | +
|
| 6 | +Goldstone diagram describing a strong-field process, i.e. after an |
| 7 | +initial photoionization event, the ion and photoelectron are |
| 8 | +propagated separately (at the chosen level of approximation, typically |
| 9 | +Volkov waves are used for the photoelectrons). By interacting with the |
| 10 | +external field, the ion state may change, and through electron |
| 11 | +(re)scattering, both photoelectron and ion state may change. |
| 12 | +
|
| 13 | +The diagram is specified using a `path` of pairs (in antichronological |
| 14 | +order), where each pair designates `(resultant ion channel, |
| 15 | +interaction)`, where `interaction` is an index to one of the |
| 16 | +`couplings`. The special value `interaction == 0` corresponds to the |
| 17 | +initial ionization event, and should appear once at the end of `path` |
| 18 | +(i.e. the first event); if it also appears as the first element of |
| 19 | +`path` (i.e. the last event), it corresponds to recombination to the |
| 20 | +initial state. |
| 21 | +
|
| 22 | +# Examples |
| 23 | +
|
| 24 | +```jldoctest |
| 25 | +julia> couplings = (StrongFieldApproximation.DipoleCoupling, |
| 26 | + StrongFieldApproximation.CoulombCoupling) |
| 27 | +(StrongFieldApproximation.DipoleCoupling, StrongFieldApproximation.CoulombCoupling) |
| 28 | +
|
| 29 | +julia> Diagram([(1,0)], couplings) |
| 30 | +Goldstone Diagram: |
| 31 | + |0⟩ |
| 32 | + ╱ ╲⇜ |
| 33 | + 1┃ │𝐤 |
| 34 | +
|
| 35 | +
|
| 36 | +julia> Diagram([(1,0),(1,0)], couplings) |
| 37 | +Goldstone Diagram: |
| 38 | + |0⟩ |
| 39 | + ╱ ╲⇜ |
| 40 | + 1┃ │𝐩 |
| 41 | + ╲ ╱⇝ |
| 42 | + |0⟩ |
| 43 | +
|
| 44 | +julia> Diagram([(2,1),(1,0)], couplings) |
| 45 | +Goldstone Diagram: |
| 46 | + |0⟩ |
| 47 | + ╱ ╲⇜ |
| 48 | + 1┃ │𝐩 |
| 49 | + ⇝┃ │ |
| 50 | + 2┃ │𝐤 |
| 51 | +
|
| 52 | +
|
| 53 | +julia> Diagram([(2,2),(1,0)], couplings) |
| 54 | +Goldstone Diagram: |
| 55 | + |0⟩ |
| 56 | + ╱ ╲⇜ |
| 57 | + 1┃ │𝐩 |
| 58 | + ┠┈┈┈┤ |
| 59 | + 2┃ │𝐤 |
| 60 | +
|
| 61 | +
|
| 62 | +julia> Diagram([(1,2),(2,2),(1,0)], couplings) |
| 63 | +Goldstone Diagram: |
| 64 | + |0⟩ |
| 65 | + ╱ ╲⇜ |
| 66 | + 1┃ │𝐩 |
| 67 | + ┠┈┈┈┤ |
| 68 | + 2┃ │𝐪 |
| 69 | + ┠┈┈┈┤ |
| 70 | + 1┃ │𝐤 |
| 71 | +
|
| 72 | +
|
| 73 | +julia> Diagram([(3,1),(1,1),(2,2),(1,0)], couplings) |
| 74 | +Goldstone Diagram: |
| 75 | + |0⟩ |
| 76 | + ╱ ╲⇜ |
| 77 | + 1┃ │𝐩 |
| 78 | + ┠┈┈┈┤ |
| 79 | + 2┃ │𝐪 |
| 80 | + ⇝┃ │ |
| 81 | + 1┃ │ |
| 82 | + ⇝┃ │ |
| 83 | + 3┃ │𝐤 |
| 84 | +``` |
| 85 | +""" |
| 86 | +struct Diagram{Couplings} |
| 87 | + path::Vector{Tuple{Int,Int}} |
| 88 | + couplings::Couplings |
| 89 | + function Diagram(path, couplings::Couplings) where Couplings |
| 90 | + if !isempty(path) |
| 91 | + α,which = last(path) |
| 92 | + which == 0 || |
| 93 | + throw(ArgumentError("Non-empty diagrams must have photoionization as the first interaction")) |
| 94 | + end |
| 95 | + ncoup = length(couplings) |
| 96 | + vcoup = 0:ncoup |
| 97 | + invalid_couplings = findall(∉(vcoup), last.(path)) |
| 98 | + if !isempty(invalid_couplings) |
| 99 | + length(invalid_couplings) == 1 && |
| 100 | + throw(ArgumentError("Coupling at vertex $(first(invalid_couplings)) not in valid range $(vcoup)")) |
| 101 | + throw(ArgumentError("Couplings at vertices $(invalid_couplings) not in valid range $(vcoup)")) |
| 102 | + end |
| 103 | + for i = 2:length(path)-1 |
| 104 | + path[i][2] == 0 && |
| 105 | + throw(ArgumentError("Only the first and last interaction in a diagram may have which = 0 (corresponding to ionization/recombination)")) |
| 106 | + end |
| 107 | + if length(path) > 1 && path[1][2] == 0 |
| 108 | + path[1][1] == path[2][1] || |
| 109 | + throw(ArgumentError("Must recombine from the last channel")) |
| 110 | + end |
| 111 | + new{Couplings}(path, couplings) |
| 112 | + end |
| 113 | +end |
| 114 | + |
| 115 | +Diagram(path::Tuple{Int,Int}, couplings) = |
| 116 | + Diagram([path], couplings) |
| 117 | + |
| 118 | +""" |
| 119 | + Diagram(path::AbstractVector{Tuple{Int,Int}}, system) |
| 120 | +
|
| 121 | +Convenience constructor that sets up the [`Diagram`](@ref) given a |
| 122 | +`path` and the couplings present in `system`. If `path` is empty, the |
| 123 | +diagram corresponding to photoionization into the first ionization of |
| 124 | +channel of `system` will automatically be generated. |
| 125 | +""" |
| 126 | +function Diagram(path::AbstractVector, system::System) |
| 127 | + nc = length(system.ionization_channels) |
| 128 | + if isempty(path) |
| 129 | + nc > 1 && |
| 130 | + @warn "More than one ionization channel present in system, choosing the first" |
| 131 | + path = [(1,0)] |
| 132 | + end |
| 133 | + vc = 1:nc |
| 134 | + invalid_channels = findall(∉(vc), first.(path)) |
| 135 | + if !isempty(invalid_channels) |
| 136 | + length(invalid_channels) == 1 && |
| 137 | + throw(ArgumentError("Invalid channel at vertex $(first(invalid_channels))")) |
| 138 | + throw(ArgumentError("Invalid channels at vertices $(invalid_channels)")) |
| 139 | + end |
| 140 | + Diagram(path, [typeof(first(c)) for c in system.couplings]) |
| 141 | +end |
| 142 | + |
| 143 | +Diagram(system::System) = Diagram([], system) |
| 144 | + |
| 145 | +for f in [:length, :first, :firstindex, :lastindex, :isempty] |
| 146 | + @eval Base.$f(d::Diagram) = $f(d.path) |
| 147 | +end |
| 148 | +Base.getindex(d::Diagram, i) = Diagram(d.path[i], d.couplings) |
| 149 | + |
| 150 | +# ** Pretty-printing |
| 151 | + |
| 152 | +function draw_ionization(io, nd) |
| 153 | + println(io, lpad("|0⟩", nd+4)) |
| 154 | + print(io, lpad("╱ ╲", nd+4)) |
| 155 | + printstyled(io, "⇜", color=:light_red) |
| 156 | + println(io) |
| 157 | +end |
| 158 | + |
| 159 | +function draw_recombination(io, nd) |
| 160 | + print(io, lpad("╲ ╱", nd+4)) |
| 161 | + printstyled(io, "⇝", color=:light_red) |
| 162 | + println(io) |
| 163 | + println(io, lpad("|0⟩", nd+4)) |
| 164 | +end |
| 165 | + |
| 166 | +draw_exciton(io, ion, nd, electron="") = |
| 167 | + println(io, lpad(ion, nd)*"┃ │$(electron)") |
| 168 | + |
| 169 | +draw_coulomb_interaction(io, nd) = |
| 170 | + println(io, repeat(" ",nd)*"┠┈┈┈┤") |
| 171 | + |
| 172 | +function draw_dipole_interaction(io, nd) |
| 173 | + printstyled(io, lpad("⇝",nd), color=:light_red) |
| 174 | + println(io, "┃ │") |
| 175 | +end |
| 176 | + |
| 177 | +function Base.show(io::IO, ::MIME"text/plain", d::Diagram) |
| 178 | + println(io, "Goldstone Diagram:") |
| 179 | + if isempty(d) |
| 180 | + println(io, "|0⟩") |
| 181 | + return |
| 182 | + end |
| 183 | + nd = maximum(iw -> length(digits(iw[1])), d.path)+1 |
| 184 | + electrons = ["𝐩","𝐪"] |
| 185 | + cur_electron = 1 |
| 186 | + for (i,(ion,which)) in Iterators.reverse(enumerate(d.path)) |
| 187 | + if which == 0 |
| 188 | + if i == length(d.path) |
| 189 | + draw_ionization(io, nd) |
| 190 | + elseif i == 1 |
| 191 | + draw_recombination(io, nd) |
| 192 | + end |
| 193 | + else |
| 194 | + c = d.couplings[which] |
| 195 | + if c <: CoulombCoupling |
| 196 | + draw_coulomb_interaction(io, nd) |
| 197 | + elseif c <: DipoleCoupling |
| 198 | + draw_dipole_interaction(io, nd) |
| 199 | + end |
| 200 | + end |
| 201 | + electron = if i == 1 |
| 202 | + "𝐤" |
| 203 | + elseif !iszero(which) && d.couplings[which] <: DipoleCoupling |
| 204 | + "" |
| 205 | + else |
| 206 | + ei = mod1(cur_electron,length(electrons)) |
| 207 | + e = electrons[ei]*repeat("′", fld1(cur_electron,length(electrons))-1) |
| 208 | + cur_electron += 1 |
| 209 | + e |
| 210 | + end |
| 211 | + |
| 212 | + which == 0 && i == 1 && length(d) > 1 || draw_exciton(io, ion, nd, electron) |
| 213 | + end |
| 214 | +end |
| 215 | + |
| 216 | +# ** Diagram properties |
| 217 | + |
| 218 | +function analyze_diagram(system, diagram) |
| 219 | + α,which = first(diagram) |
| 220 | + ions = Int[] |
| 221 | + # For photoelectron spectra, time 1 is the reference time, |
| 222 | + # typically at which the laser pulse has ended; for direct |
| 223 | + # photoelectron diagrams, time 2 is the time of ionization. For |
| 224 | + # dipoles, time 1 is the time of recombination. |
| 225 | + unique_momenta = Tuple{Int,Int}[(1,2)] |
| 226 | + momenta = [1] |
| 227 | + indeterminate_momenta = Int[] |
| 228 | + ld = length(diagram) |
| 229 | + order = ld |
| 230 | + |
| 231 | + if ld > 1 |
| 232 | + if which == 0 |
| 233 | + push!(indeterminate_momenta, 1) |
| 234 | + # Recombination does not increase the order of the diagram, since |
| 235 | + # it only amounts to projecting the wavefunction on ⟨0|𝐫. |
| 236 | + order -= 1 |
| 237 | + end |
| 238 | + else |
| 239 | + push!(ions, α) |
| 240 | + end |
| 241 | + |
| 242 | + i = 2 |
| 243 | + for (α,which) in diagram.path[(iszero(which) ? 2 : 1):end] |
| 244 | + push!(ions, α) |
| 245 | + a,b = unique_momenta[end] |
| 246 | + unique_momenta[end] = (a,i) |
| 247 | + if !(iszero(which) || canonical_momentum_conservation(system, which) == CanonicalMomentumConservation()) |
| 248 | + push!(unique_momenta, (i,i+1)) |
| 249 | + push!(indeterminate_momenta, length(unique_momenta)) |
| 250 | + end |
| 251 | + if !iszero(which) |
| 252 | + push!(momenta, length(unique_momenta)) |
| 253 | + end |
| 254 | + |
| 255 | + i += 1 |
| 256 | + end |
| 257 | + |
| 258 | + return ions, unique_momenta, momenta, indeterminate_momenta, order |
| 259 | +end |
| 260 | + |
| 261 | +# 𝐤 is nothing, we want a dipole amplitude |
| 262 | +function diagram_amplitude(::Type{Amp}, system::System, diagram::Diagram, iref, i, ::Nothing) where Amp |
| 263 | + amplitude = complex(zero(Amp)) |
| 264 | + |
| 265 | + amplitude |
| 266 | +end |
| 267 | + |
| 268 | +# 𝐤 is determinate, we want a photoelectron spectrum |
| 269 | +function diagram_amplitude(::Type{Amp}, system::System, diagram::Diagram, iref, i, 𝐤) where Amp |
| 270 | + amplitude = complex(zero(Amp)) |
| 271 | + |
| 272 | + amplitude |
| 273 | +end |
| 274 | + |
| 275 | +# * Exports |
| 276 | + |
| 277 | +export Diagram |
0 commit comments