Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
183d192
freespace inputs with both polarisation components
chrisbrahms Jan 5, 2023
282ec02
starting free-space polarisation
chrisbrahms Jan 25, 2023
7ffa177
add quartz, calcite, MgF2
chrisbrahms Nov 16, 2024
e563766
add ADP, KDP
chrisbrahms Nov 16, 2024
d56b165
remove ADP and KDP from other sellmeier
chrisbrahms Nov 16, 2024
70019b2
Merge branch 'LupoLab:master' into freepol
chrisbrahms Dec 4, 2024
9aafcda
add Chi2 response type
chrisbrahms Dec 9, 2024
cf6ea8a
deps
chrisbrahms Dec 9, 2024
7f4a87b
linearops for radial symmetric with polarisation
chrisbrahms Dec 9, 2024
174dd04
setup for radial symmetric with polarisation
chrisbrahms Dec 9, 2024
dea4aff
faster Chi2 response
chrisbrahms Dec 9, 2024
7d03078
fix norm with birefringence
chrisbrahms Dec 9, 2024
22aa1e8
more systematic treatment of crystals in PhysData
chrisbrahms Dec 9, 2024
dc67537
add 2D free-space propagation
chrisbrahms Dec 10, 2024
b56a54f
proper birefringence in linop and norm
chrisbrahms Dec 10, 2024
e41830b
multi-dim integral function for analysis
chrisbrahms Dec 10, 2024
d2a2abe
actually proper (?) birefringence
chrisbrahms Dec 10, 2024
2b01c59
include internal crystal angle in normfun
chrisbrahms Dec 10, 2024
cd96bcf
add more crystals
chrisbrahms Dec 11, 2024
cf86528
add LBO to crystals
chrisbrahms Jan 7, 2025
f3de30f
minor fixes
chrisbrahms Feb 21, 2025
2929cd0
beam walk-off simulation (Not finished)
chrisbrahms Feb 21, 2025
b39e5ed
chi2 benchmark
chrisbrahms Jun 3, 2025
24eb152
Merge branch 'LupoLab:master' into freepol
chrisbrahms Jun 3, 2025
aa96703
Merge branch 'master' into more-crystals
chrisbrahms Aug 4, 2025
2ee4708
Merge branch 'master' into freepol
chrisbrahms Aug 20, 2025
baef317
Merge branch 'master' into freepol
chrisbrahms Aug 20, 2025
ac54cec
Merge branch 'more-crystals' into freepol
chrisbrahms Aug 20, 2025
1d36ef8
clean up crystals
chrisbrahms Aug 20, 2025
017cdcb
harmonise norm functions
chrisbrahms Aug 20, 2025
0125e19
make linop for radial propagation handle (nx, ny)
chrisbrahms Aug 20, 2025
24140b1
comments
chrisbrahms Aug 20, 2025
16efa48
use imported c
chrisbrahms Aug 20, 2025
7c890e2
fix ref_index() for mixtures
chrisbrahms Aug 20, 2025
c53fa3a
generalise field creation for free-space
chrisbrahms Aug 20, 2025
2a8028f
make linops work for single polarisation
chrisbrahms Aug 20, 2025
ddf2fef
update BBO example
chrisbrahms Aug 21, 2025
0b1709b
working full 3D bbo example
chrisbrahms Aug 22, 2025
1bd0b94
Merge branch 'LupoLab:master' into freepol
chrisbrahms Sep 3, 2025
8ee4690
remove extra z kwargs
chrisbrahms Sep 3, 2025
5ffcfb9
fix polarisation ellipse
chrisbrahms Oct 2, 2025
b709c0e
Merge branch 'master' into freepol
chrisbrahms Nov 12, 2025
e5a9e05
Merge branch 'master' into freepol
chrisbrahms Dec 12, 2025
8ef23c4
fix field creation when only one polarisation is used
chrisbrahms Dec 12, 2025
9056967
fix simple radial examples
chrisbrahms Dec 12, 2025
2d43738
fix linop for full 3D with one polarisation
chrisbrahms Dec 12, 2025
26b438b
fix full 3D setup and examples
chrisbrahms Dec 12, 2025
c5e1a45
simplify linear ops
chrisbrahms Dec 18, 2025
7d8233a
further simplification of LinearOps
chrisbrahms Dec 19, 2025
58d42e1
fix norm for 2D/3D propagation with crystal optics
chrisbrahms Dec 19, 2025
08d39bd
dramatically speed up envelope radial symmetry free-space propagation
chrisbrahms Jan 7, 2026
272d03e
comments to future self
chrisbrahms Jan 7, 2026
697263c
whitespace
chrisbrahms Jan 8, 2026
c748cc7
simplify speedup
chrisbrahms Jan 8, 2026
6e2d24a
free 2D examples
chrisbrahms Jan 14, 2026
a545f63
Merge branch 'LupoLab:master' into freepol
chrisbrahms Jan 22, 2026
d48e516
Merge branch 'master' into freepol
chrisbrahms Feb 1, 2026
9c56a2b
change y-x ordering to x-y ordering in full 3D simulations
chrisbrahms Mar 3, 2026
59cc2f4
Merge branch 'master' into freepol
chrisbrahms Mar 4, 2026
3449548
fluence shape
chrisbrahms Mar 4, 2026
29921cc
remove duplicated make_const_linop
chrisbrahms Mar 4, 2026
c2513ea
unify make_linop for free-space grids
chrisbrahms Mar 4, 2026
a03df58
group functions better
chrisbrahms Mar 4, 2026
e68f9c5
fix some tests
chrisbrahms Mar 4, 2026
cde7fe5
fix modes tests
chrisbrahms Mar 4, 2026
bc29120
fix linearops for non-crystal birefringence
chrisbrahms Mar 4, 2026
67741b2
Fields.prop! for 2D grids
chrisbrahms Mar 4, 2026
9881fdd
fix test_linops
chrisbrahms Mar 4, 2026
09978ac
unify free-space tests
chrisbrahms Mar 4, 2026
59847ab
comment
chrisbrahms Mar 4, 2026
784a665
test chi2
chrisbrahms Mar 4, 2026
164cfb1
docstrings
chrisbrahms Mar 4, 2026
9cb204c
remove tests scripts from runtests
chrisbrahms Mar 5, 2026
cbcabae
missing docstring
chrisbrahms Mar 5, 2026
00e1218
improve memory handling with function barrier
chrisbrahms Mar 5, 2026
ce29430
copilot comments
chrisbrahms Mar 6, 2026
ac1c1cc
fix error msg
chrisbrahms Mar 6, 2026
e214fd2
simplify and shorten freespace tests
chrisbrahms Mar 6, 2026
7d4673e
change const test
chrisbrahms Mar 6, 2026
206652e
increase pupil radius, make tests even smaller
chrisbrahms Mar 6, 2026
ce95b52
rtol
chrisbrahms Mar 6, 2026
1bd6999
Merge branch 'master' into freepol
chrisbrahms Mar 9, 2026
15af937
remove function corpse
chrisbrahms Mar 9, 2026
b784210
missed an inv(FT)
chrisbrahms Mar 9, 2026
4e7e608
fix merge
chrisbrahms Mar 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc"
Scratch = "6c6a2e73-6563-6170-7368-637461726353"
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
Expand Down Expand Up @@ -96,6 +97,7 @@ QuadGK = "2.4"
Random = "1.9"
Reexport = "1.2"
Roots = "2"
Rotations = "1.7.1"
Scratch = "1"
SpecialFunctions = "0.10.3, 1, 2"
StaticArrays = "1"
Expand Down
37 changes: 37 additions & 0 deletions examples/low_level_interface/freespace/chi2_benchmarking.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Luna
using BenchmarkTools
import Rotations: RotZY, RotYZ, RotMatrix, RotMatrix3
import LinearAlgebra: mul!
using Metal

θ = deg2rad(23.3717)
ϕ = deg2rad(30)
material = :BBO

Et = rand(2^12, 2)
out = zero(Et)

c = Nonlinear.Chi2Field(θ, ϕ, PhysData.χ2(material))
# r = Nonlinear.Kerr_field(PhysData.χ3(material))

function toprofile(n)
for i in 1:n
c(out, Et, 1)
end
end
##
@profview toprofile(1)
@profview toprofile(100)
@btime c(out, Et, 1)

##
Enl2 = rand(2^12, 6)'
Et2 = rand(2^12, 3)'
##
Et2_m = MtlArray(convert(Matrix{Float32}, Et2))
χ2_m = MtlArray(convert(Matrix{Float32}, c.χ2_toLab))
Enl2_m = MtlArray(convert(Matrix{Float32}, Enl2))
@btime mul!(Et2_m, χ2_m, Enl2_m)

##
@btime mul!(Et2, c.χ2_toLab, Enl2)
85 changes: 85 additions & 0 deletions examples/low_level_interface/freespace/free2D.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using Luna
Luna.set_fftw_mode(:estimate)
import FFTW
import PyPlot:pygui, plt
pygui(true)

gas = :Ar
pres = 4

τ = 30e-15
λ0 = 800e-9

w0 = 2e-3
energy = 1.5e-3
L = 2

R = 6e-3
N = 128

grid = Grid.RealGrid(L, 800e-9, (400e-9, 2000e-9), 0.2e-12)
xgrid = Grid.Free2DGrid(R, N)

x = xgrid.x
energyfun, energyfunω = Fields.energyfuncs(grid, xgrid)

densityfun = let dens0=PhysData.density(gas, pres)
z -> dens0
end

responses = (Nonlinear.Kerr_env(PhysData.γ3_gas(gas)),)

linop = LinearOps.make_const_linop(grid, xgrid, PhysData.ref_index_fun(gas, pres))
normfun = NonlinearRHS.const_norm_free2D(grid, xgrid, PhysData.ref_index_fun(gas, pres))

inputs = Fields.GaussGaussField(λ0=λ0, τfwhm=τ, energy=energy, w0=w0)

Eω, transform, FT = Luna.setup(grid, xgrid, densityfun, normfun, responses, inputs)

# statsfun = Stats.collect_stats(grid, Eω, Stats.ω0(grid))
output = Output.MemoryOutput(0, grid.zmax, 21)

Luna.run(Eω, grid, linop, transform, FT, output, max_dz=Inf, init_dz=1e-1)

ω = grid.ω
t = grid.t

zout = output.data["z"]
Eout = output.data["Eω"]

println("Transforming...")
Eωx = FFTW.ifft(Eout, 3)
Etx = FFTW.irfft(Eout, length(grid.t), (1, 3))
println("...done")


Ilog = log10.(Maths.normbymax(abs2.(Eωx)))

Iωx = abs2.(Eωx);

Ix = zeros(Float64, (length(x), length(zout)));
energy = zeros(length(zout));
for ii in axes(Etx, 4)
energy[ii] = energyfun(Etx[:, 1, :, ii]);
Ix[:, ii] = (grid.ω[2]-grid.ω[1]) .* sum(Iωx[:, 1, :, ii], dims=1);
end

ω0idx = argmin(abs.(grid.ω .- 2π*PhysData.c/λ0))

E0ωyx = FFTW.ifft(Eω[ω0idx, 1, :, :], (1, 2))

Iωxlog = log10.(Maths.normbymax(Iωx))

plt.figure()
plt.pcolormesh(zout, ω.*1e-15/2π, Iωxlog[:, 1, N÷2+1, :])
plt.xlabel("Z (m)")
plt.ylabel("f (PHz)")
plt.title("I(ω, x=0, z)")
plt.clim(-6, 0)
plt.colorbar()


plt.figure()
plt.plot(zout.*1e2, energy.*1e6)
plt.xlabel("Distance [cm]")
plt.ylabel("Energy [μJ]")
192 changes: 192 additions & 0 deletions examples/low_level_interface/freespace/free2D_bbo.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#=
Here we simulate type I SHG in a BBO crystal driven at 800 nm. The focus is small and the crystal
is reasonably thick, so we observe strong temporal and spatial walk-off effects.
=#
using Luna
import FFTW
import Luna: Hankel
import PyPlot: plt
import NumericalIntegration: integrate

λ0 = 800e-9 # driving wavelength
τfwhm = 30e-15 # pulse duration
w0 = 20e-6 # 1/e² beam radius
energy = 10e-9 # pulse energy

material = :BBO
thickness = 200e-6 # BBO thickness

R = 4*w0 # radius of the spatial window
N = 2^7 # number of spatial points

grid = Grid.RealGrid(thickness, λ0, (250e-9, 2e-6), 120e-15)
xgrid = Grid.Free2DGrid(R, N)

θ = deg2rad(29.2) # type I phase-matching angle
ϕ = deg2rad(30) # ϕ for type I phase-matching

##
responses = (Nonlinear.Chi2Field(θ, ϕ, PhysData.χ2(material)),
Nonlinear.Kerr_field(PhysData.χ3(material)))

#=
Ref index functions. Note that here nfunx takes (λ, δθ=0) as arguments,
which allows make_const_linop to calculate the actual internal angle depending
on frequency and transverse k-vector component.
=#
nfunx, nfuny = PhysData.ref_index_fun_xy(material, θ)
linop = LinearOps.make_const_linop(grid, xgrid, (nfunx, nfuny))

normfun = NonlinearRHS.const_norm_free2D(grid, xgrid, (nfunx, nfuny))
densityfun = z -> 1 # density is unity because we're considering a solid.
##
# scaling factor in front of the energy corresponds to the integral over y
inputs = Fields.GaussGaussField(;λ0, τfwhm, energy=energy/(sqrt(π/2)*w0), w0)
Eω, transform, FT = Luna.setup(grid, xgrid, densityfun, normfun, responses, inputs)

##
output = Output.MemoryOutput(0, grid.zmax, 101)
Luna.run(Eω, grid, linop, transform, FT, output; init_dz=1e-6)

##
z = output["z"]
Eωk = output["Eω"] # (ω, pol, k, z)
x = xgrid.x

# normalisation prefactor for spectral intensity
ωprefac = PhysData.c*PhysData.ε_0/2 * 2π/(grid.ω[end]^2) * sqrt(π/2)*w0

Eωr = FFTW.ifft(Eωk, 3) # (ω, pol, x, z)
Etr = FFTW.irfft(Eωr, 2*(length(grid.ω)-1), 1) # (t, pol, x, z)
EtrH = Maths.hilbert(Etr)
Iωr = abs2.(Eωr) # (ω, pol, x, z)
Itr = 0.5*PhysData.c*PhysData.ε_0*abs2.(EtrH) # (t, pol, x, z)

Irxy = dropdims(sum(Iωr; dims=1); dims=1) # (pol, x, z)
Iωxy = dropdims(Maths.integrateNd(x, Iωr; dim=3); dims=3)*ωprefac # (ω, pol, z)
Ir = dropdims(sum(Iωr; dims=(1, 2)); dims=(1, 2)) # (x, z)

Itxy = dropdims(Maths.integrateNd(x, Itr; dim=3); dims=3)*sqrt(π/2)*w0 # (t, pol, z)

Eω0 = Eωr[:, :, length(x)÷2+1, :]
Et0 = FFTW.irfft(Eω0, 2*(length(grid.ω)-1), 1) # (t, pol, z)
Et0 = Maths.hilbert(Et0)
It0 = 0.5*PhysData.c*PhysData.ε_0*abs2.(Et0)

et, eω = Fields.energyfuncs(grid, xgrid)

energy_out = dropdims(mapslices(eω, Eωk; dims=(1, 3)); dims=(1, 3))*sqrt(π/2)*w0

ω = grid.ω


##
plt.figure()
plt.pcolormesh(z*1e3, x*1e6, Ir)
plt.xlabel("Distance (mm)")
plt.ylabel("X (μm)")

##
plt.figure()
plt.plot(z*1e6, 1e9energy_out[1, :]; label="X polarisation")
plt.plot(z*1e6, 1e9energy_out[2, :]; label="Y polarisation")
plt.xlabel("Distance (μm)")
plt.ylabel("Energy (nJ)")
plt.legend()

##
fig = plt.figure()
fig.set_size_inches(12, 6)
plt.subplot(1, 2, 1)
plt.pcolormesh(z*1e3, x*1e6, Irxy[1, :, :])
plt.xlabel("Distance (mm)")
plt.ylabel("radius (μm)")
plt.title("X Polarisation")
plt.subplot(1, 2, 2)
plt.pcolormesh(z*1e3, x*1e6, Irxy[2, :, :])
plt.xlabel("Distance (mm)")
plt.ylabel("radius (μm)")
plt.title("Y Polarisation")
fig.tight_layout()


##
plt.figure()
plt.subplot(1, 2, 1)
plt.pcolormesh(z*1e3, grid.t*1e15, Itxy[:, 1, :])
plt.title("X Pol")
plt.xlabel("Distance (mm)")
plt.ylabel("Time (fs)")
plt.subplot(1, 2, 2)
plt.pcolormesh(z*1e3, grid.t*1e15, Itxy[:, 2, :])
plt.title("Y Pol")
plt.xlabel("Distance (mm)")
plt.suptitle("Time domain")

##
plt.figure()
plt.subplot(2, 1, 1)
plt.plot(grid.t*1e15, Itxy[:, 1, 1]; label="input X")
plt.plot(grid.t*1e15, Itxy[:, 2, 1]; label="input Y")
plt.legend()
plt.subplot(2, 1, 2)
plt.plot(grid.t*1e15, Itxy[:, 1, end]; label="output X")
plt.plot(grid.t*1e15, Itxy[:, 2, end]; label="output Y")
plt.xlabel("Time (fs)")
plt.legend()

##
mm = 2π*maximum(Iωxy)*1e12
plt.figure()
plt.subplot(2, 1, 1)
plt.semilogy(ω*1e-12/2π, 2π*1e12*Iωxy[:, 1, 1]; label="input X")
plt.semilogy(ω*1e-12/2π, 2π*1e12*Iωxy[:, 2, 1]; label="input Y")
plt.ylim(1e-5mm, 5mm)
plt.xlim(200, 900)
plt.legend()
plt.subplot(2, 1, 2)
plt.semilogy(ω*1e-12/2π, 2π*1e12*Iωxy[:, 1, end]; label="output X")
plt.semilogy(ω*1e-12/2π, 2π*1e12*Iωxy[:, 2, end]; label="output Y")
# plt.semilogy(ω*1e-12/2π, 2π*1e12*Iωxy[:, 2, 1]; c="C1", linestyle="--", label="input Y")
plt.ylim(1e-5mm, 5mm)
plt.xlim(200, 900)
plt.xlabel("Frequency (THz)")
plt.ylabel("SED (J/Hz)")
plt.legend()

##
fig = plt.figure()
fig.set_size_inches(12, 7)
plt.subplot(1, 2, 1)
plt.pcolormesh(grid.t*1e15, xgrid.x*1e6, (Etr[:, 1, :, end])'; cmap="seismic")
plt.clim([-1, 1].*maximum(abs.(Etr[:, 1, :, end])))
plt.xlim(-50, 50)
plt.ylim(-100, 100)
plt.xlabel("Time (fs)")
plt.ylabel("X (μm)")
plt.title("X polarisation")

plt.subplot(1, 2, 2)
plt.pcolormesh(grid.t*1e15, xgrid.x*1e6, (Etr[:, 2, :, end])'; cmap="seismic")
plt.clim([-1, 1].*maximum(abs.(Etr[:, 2, :, end])))
plt.xlim(-50, 50)
plt.ylim(-100, 100)
plt.xlabel("Time (fs)")
plt.title("Y polarisation")

##
fig = plt.figure()
fig.set_size_inches(12, 6)
plt.subplot(1, 2, 1)
plt.plot(grid.t*1e15, 1e-9*(Etr[:, 1, length(xgrid.x)÷2+1, end]);
label="Max: $(maximum(1e-9*(Etr[:, 1, length(xgrid.x)÷2+1, end])))")
plt.ylabel("E (GV/m)")
plt.legend()
plt.xlabel("Time (fs)")
plt.title("X Polarisation")
plt.subplot(1, 2, 2)
plt.plot(grid.t*1e15, 1e-9*(Etr[:, 2, length(xgrid.x)÷2+1, end]);
label="Max: $(maximum(1e-9*(Etr[:, 2, length(xgrid.x)÷2+1, end])))")
plt.legend()
plt.xlabel("Time (fs)")
plt.title("Y Polarisation")
Loading
Loading