Skip to content

Commit 31c3eda

Browse files
committed
File separation
1 parent b83aca4 commit 31c3eda

File tree

5 files changed

+416
-407
lines changed

5 files changed

+416
-407
lines changed

src/StrongFieldApproximation.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ include("field.jl")
2222
include("volkov.jl")
2323

2424
include("channels.jl")
25+
include("system.jl")
26+
include("diagrams.jl")
27+
include("momenta.jl")
28+
2529
include("dyson_expansions.jl")
2630

2731
include("ionization_rates.jl")

src/diagrams.jl

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
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

Comments
 (0)