Skip to content

Commit 4d43602

Browse files
authored
Merge pull request #8 from SebastianM-C/smc/radiation
Add Liénard-Wiechert potential evaluation
2 parents 0fc7973 + 20c7ba4 commit 4d43602

14 files changed

Lines changed: 513 additions & 25 deletions

.github/workflows/CI.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
test:
1616
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
1717
runs-on: ${{ matrix.os }}
18-
timeout-minutes: 60
18+
timeout-minutes: 120
1919
permissions: # needed to allow julia-actions/cache to proactively delete old caches that it has created
2020
actions: write
2121
contents: read
@@ -37,6 +37,9 @@ jobs:
3737
arch: ${{ matrix.arch }}
3838
- uses: julia-actions/cache@v3
3939
- uses: julia-actions/julia-buildpkg@v1
40+
- name: Monitor memory
41+
if: always()
42+
run: free -h
4043
- uses: julia-actions/julia-runtest@v1
4144
- uses: julia-actions/julia-processcoverage@v1
4245
- uses: codecov/codecov-action@v5

Project.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ version = "0.1.0-DEV"
44
authors = ["Sebastian Micluța-Câmpeanu <sebastian.mc95@proton.me> and contributors"]
55

66
[deps]
7+
DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0"
78
HypergeometricFunctions = "34004b35-14d8-5ef3-9330-4cdb6864b03a"
89
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
910
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
@@ -19,6 +20,8 @@ UnitfulAtomic = "a7773ee8-282e-5fa2-be4e-bd808c38a91a"
1920
[compat]
2021
Aqua = "0.8"
2122
CSV = "0.10"
23+
DataInterpolations = "8.9.0"
24+
FFTW = "1"
2225
HypergeometricFunctions = "0.3.28"
2326
JET = "0.9, 0.10, 0.11"
2427
LaserTypes = "0.2"
@@ -27,6 +30,7 @@ ModelingToolkit = "11"
2730
ModelingToolkitBase = "1.20.0"
2831
OrdinaryDiffEqNonlinearSolve = "1.10.0"
2932
OrdinaryDiffEqRosenbrock = "1.11.0"
33+
OrdinaryDiffEqTsit5 = "1.9"
3034
OrdinaryDiffEqVerner = "1.2.0"
3135
PhysicalConstants = "0.2.3"
3236
Plots = "1"
@@ -44,11 +48,13 @@ julia = "1.11"
4448
[extras]
4549
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
4650
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
51+
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
4752
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
4853
LaserTypes = "e07c0bfa-524c-4f35-a151-c3dd916fa2f0"
4954
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
5055
OrdinaryDiffEqNonlinearSolve = "127b3ac7-2247-4354-8eb6-78cf4e7c58e8"
5156
OrdinaryDiffEqRosenbrock = "43230ef6-c299-4910-a778-202eb28ce4ce"
57+
OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a"
5258
OrdinaryDiffEqVerner = "79d7bb75-1356-48c1-b8c0-6832512096c2"
5359
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
5460
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
@@ -59,4 +65,4 @@ SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5"
5965
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
6066

6167
[targets]
62-
test = ["Aqua", "CSV", "JET", "LaserTypes", "LinearAlgebra", "Test", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqVerner", "Plots", "Random", "SciMLBase", "StaticArrays", "Statistics", "SymbolicIndexingInterface"]
68+
test = ["Aqua", "CSV", "FFTW", "JET", "LaserTypes", "LinearAlgebra", "Test", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "Plots", "Random", "SciMLBase", "StaticArrays", "Statistics", "SymbolicIndexingInterface"]

scripts/Project.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
[deps]
2+
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
3+
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
4+
DiffEqGPU = "071ae1c0-96b5-11e9-1965-c90190d839ea"
25
ElectronDynamicsModels = "acecdaf2-97b2-47e1-90eb-3efa7bb274e5"
6+
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
37
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
48
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
59
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
610
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
11+
OrdinaryDiffEqNonlinearSolve = "127b3ac7-2247-4354-8eb6-78cf4e7c58e8"
12+
OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a"
713
OrdinaryDiffEqVerner = "79d7bb75-1356-48c1-b8c0-6832512096c2"
14+
ProfileCanvas = "efd6af41-a80b-495e-886c-e51b0c7d77a3"
15+
ProfileView = "c46f51b8-102a-5cf2-8d2c-8597cb0e0da7"
816
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
917
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1018
SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5"

scripts/thomson_scattering.jl

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
using ElectronDynamicsModels
2+
using ModelingToolkit
3+
using OrdinaryDiffEqVerner, OrdinaryDiffEqTsit5
4+
using OrdinaryDiffEqNonlinearSolve
5+
using SciMLBase
6+
using StaticArrays
7+
using SymbolicIndexingInterface
8+
using LinearAlgebra
9+
using FFTW
10+
using CairoMakie
11+
12+
# Atomic units
13+
const c = 137.03599908330932
14+
15+
# Laser parameters
16+
ω = 0.057
17+
τ = 150 / ω
18+
λ = 2π * c / ω
19+
w₀ = 75λ
20+
Rmax = 3.25w₀
21+
22+
a₀ = 10.0
23+
24+
@named ref_frame = ProperFrame(:atomic)
25+
26+
@named laser = LaguerreGaussLaser(;
27+
wavelength = λ,
28+
a0 = a₀,
29+
beam_waist = w₀,
30+
radial_index = 2,
31+
azimuthal_index = -2,
32+
ref_frame,
33+
temporal_profile = :gaussian,
34+
temporal_width = τ,
35+
focus_position = 0.0,
36+
polarization = :circular
37+
)
38+
@named elec = ClassicalElectron(; laser)
39+
sys = mtkcompile(elec)
40+
41+
# Time span
42+
τi = -8τ
43+
τf = 8τ
44+
tspan = (τi, τf)
45+
46+
# Single electron solve (for parameter access)
47+
x⁰ = [τi * c, 0.0, 0.0, 0.0]
48+
u⁰ = [c, 0.0, 0.0, 0.0]
49+
50+
u0 = [
51+
sys.x => x⁰,
52+
sys.u => u⁰,
53+
]
54+
55+
prob = ODEProblem{false, SciMLBase.FullSpecialize}(
56+
sys, u0, tspan, u0_constructor = SVector{8}, fully_determined = true
57+
)
58+
sol0 = solve(prob, Vern9(), reltol = 1.0e-15, abstol = 1.0e-12)
59+
60+
# Sunflower distribution for electron positions
61+
const ϕ = (1 + 5) / 2
62+
63+
function radius(k, n, b)
64+
if k > n - b
65+
return 1.0
66+
else
67+
return sqrt(k - 0.5) / sqrt(n - (b + 1) / 2)
68+
end
69+
end
70+
71+
function sunflower(n, α)
72+
points = Vector{Vector{Float64}}()
73+
angle_stride = 2π / ϕ^2
74+
b = round(Int, α * sqrt(n))
75+
for k in 1:n
76+
r = radius(k, n, b)
77+
θ = k * angle_stride
78+
push!(points, [r * cos(θ), r * sin(θ)])
79+
end
80+
return points
81+
end
82+
83+
# Ensemble solve
84+
N = 300
85+
R₀ = Rmax * sunflower(N, 2)
86+
= [[τi * c, r..., 0.0] for r in R₀]
87+
88+
set_x = setsym_oop(prob, [Initial(sys.x); Initial(sys.u)])
89+
90+
function prob_func(prob, i, repeat)
91+
x_new = SVector{4}(xμ[i]...)
92+
u_new = SVector{4}(c, 0.0, 0.0, 0.0)
93+
u0, p = set_x(prob, SVector{8}(x_new..., u_new...))
94+
return remake(prob; u0, p)
95+
end
96+
97+
function abserr(a₀)
98+
amp = log10(a₀)
99+
expo = -amp^2 / 27 + 32amp / 27 - 220 / 27
100+
return 10^expo
101+
end
102+
103+
ensemble = EnsembleProblem(prob; prob_func, safetycopy = false)
104+
solution = solve(
105+
ensemble, Vern9(), EnsembleThreads();
106+
reltol = 1.0e-12, abstol = abserr(a₀), trajectories = N
107+
)
108+
109+
# Radiation computation
110+
111+
trajs = trajectory_interpolants(solution)
112+
113+
# Screen parameters
114+
const Z = 2.0e5λ
115+
const δt = 2π / ω / 4
116+
const N_samples = floor(Int, (τf - τi) / δt)
117+
const x⁰_start = c * τi + hypot(Z, 25w₀ + Rmax)
118+
119+
Nx = 50
120+
Ny = 50
121+
122+
x⁰_samples = range(start = x⁰_start, step = c * δt, length = N_samples)
123+
124+
screen = ObserverScreen(
125+
LinRange(-25w₀, 25w₀, Nx),
126+
LinRange(-25w₀, 25w₀, Ny),
127+
Z,
128+
x⁰_samples
129+
)
130+
131+
A_s = accumulate_potential(trajs, screen, Tsit5())
132+
133+
# GPU
134+
# accumulate_potential(trajs, screen, GPUTsit5(), EnsembleGPUKernel(CUDA.CUDABackend()))
135+
A_ω = rfft(A_s, 1)
136+
137+
# Find fundamental frequency bin
138+
freqs = rfftfreq(N_samples, 1 / δt)
139+
idx_f1 = findmin(x -> abs(x - ω / 2π), freqs)[2]
140+
141+
# Plot the y-component of the potential at the fundamental frequency
142+
fig = Figure()
143+
ax = Axis(fig[1, 1], aspect = 1, xlabel = "x", ylabel = "y", title = "Thomson scattering (ω₁)")
144+
field = real.(A_ω[idx_f1, 3, :, :])
145+
heatmap!(
146+
ax, collect(screen.x_grid), collect(screen.y_grid), field,
147+
colorrange = maximum(abs, field) .* (-1, 1), colormap = :seismic
148+
)
149+
fig

src/ElectronDynamicsModels.jl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,32 @@ module ElectronDynamicsModels
22

33
using ModelingToolkit
44
using ModelingToolkitBase: AbstractSystem, SymbolicT, build_explicit_observed_function, get_systems
5-
using SymbolicIndexingInterface: getname, setsym_oop
5+
using SymbolicIndexingInterface: getname, setsym_oop, variable_index
66
using PhysicalConstants, Unitful, UnitfulAtomic
77
using PhysicalConstants.CODATA2018: c_0, e, m_e, ε_0
88
using LinearAlgebra
99
using Symbolics
1010
using HypergeometricFunctions: HypergeometricFunctions, _₁F₁, pochhammer
1111
using StaticArrays
12+
using SciMLBase
13+
using DataInterpolations
1214

1315
m_dot(x, y) = x[1] * y[1] - x[2] * y[2] - x[3] * y[3] - x[4] * y[4]
1416

1517
export GaussLaser, LaguerreGaussLaser
1618
export ReferenceFrame, ProperFrame, LabFrame,
17-
UniformField,
18-
PlaneWave,
19+
UniformField, PlaneWave,
1920
ParticleDynamics,
20-
LandauLifshitzRadiation,
21-
AbrahamLorentzRadiation,
21+
LandauLifshitzRadiation, AbrahamLorentzRadiation,
2222
ChargedParticle,
23-
ClassicalElectron,
24-
RadiatingElectron,
25-
LandauLifshitzElectron,
26-
FieldEvaluator
23+
ClassicalElectron, RadiatingElectron, LandauLifshitzElectron,
24+
FieldEvaluator,
25+
ObserverScreen, trajectory_interpolants, TrajectoryInterpolant, accumulate_potential
2726

2827
include("base.jl")
2928
include("dynamics.jl")
3029
include("fields.jl")
30+
include("radiation.jl")
3131
include("radiation_reaction.jl")
3232
include("external_fields.jl")
3333
include("systems.jl")

src/external_fields.jl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ Parameters:
1313
- t₀: pulse center time (= n_cycles × 2π/ω)
1414
- z₀: focus position along z-axis
1515
"""
16-
@component function GaussLaser(; name, wavelength = nothing, frequency = nothing,
16+
@component function GaussLaser(;
17+
name, wavelength = nothing, frequency = nothing,
1718
a0 = 10.0, beam_waist = nothing, polarization = :linear,
18-
n_cycles = 5, focus_position = nothing, ref_frame)
19+
n_cycles = 5, focus_position = nothing, ref_frame
20+
)
1921
if wavelength === nothing && frequency === nothing
2022
wavelength = 1.0
2123
end
@@ -82,8 +84,7 @@ Parameters:
8284
E[1] ~ real(ξx * E_g)
8385
E[2] ~ real(ξy * E_g)
8486
E[3] ~ real(
85-
2im / (k * wz^2) *
86-
(1 + im * (z / z_R)) *
87+
2im / (k * wz^2) * (1 + im * (z / z_R)) *
8788
(x[2] * (ξx * E_g) + x[3] * (ξy * E_g)),
8889
)
8990

@@ -228,7 +229,7 @@ Reference: Sarachik & Schappert, Phys. Rev. D 1, 2738 (1970)
228229

229230
vars = nameof(iv) == ? [x, t] : [x]
230231

231-
sys = System(eqs, iv, vars, [A, ω, k_dir, pol, λ]; name, systems = [ref_frame], initialization_eqs, bindings, initial_conditions)
232+
sys = System(eqs, iv; name, systems = [ref_frame], initialization_eqs, bindings, initial_conditions)
232233

233234
extend(sys, field_dynamics)
234235
end

0 commit comments

Comments
 (0)