Skip to content

Astigmatic gausslets#54

Open
TacHawkes wants to merge 65 commits into
masterfrom
AstigmaticGausslets
Open

Astigmatic gausslets#54
TacHawkes wants to merge 65 commits into
masterfrom
AstigmaticGausslets

Conversation

@TacHawkes
Copy link
Copy Markdown
Collaborator

@TacHawkes TacHawkes commented Apr 29, 2026

PR Description - Astigmatic Gausslets (#54)

Summary
This PR introduces the AstigmaticGaussianBeamlet (AGB) as a first-class beam type in BeamletOptics. AGBs extend the standard 3-ray GaussianBeamlet to a 9-ray parabasal model, enabling analytical E-field computation for beams with elliptical cross-sections and independent phase curvature in two orthogonal transverse axes.

This is required for physically correct simulation of:

  • Beams through cylindrical or tilted spherical optics.
  • Astigmatic aberrations (tangential/sagittal focal splitting).
  • Interferometry with astigmatic beams.
  • Multi-beamlet superposition and decomposition.

Key Features & Improvements

  • 9-Ray Parabasal Model: Chief ray + 4 waist rays + 4 divergence rays, fully parameterized.
  • Robust Field Calculation: Complex E-field evaluation with correct propagation phase, Gouy phase, and transverse quadratic phase.
  • Caching: Introduced caching of propagation data for GB/AGBs to speed up electric field calculations significantly.
  • Beam Groups & Decomposition: Added support for AGB beam groups and simple decomposition.
  • Physical Validation: Extensive tests for energy conservation, peak intensity scaling, astigmatic Gouy phase, and polarization.

Changes Since Initial Opening

  • Flipped Gouy phase sign to match standard conventions.
  • Corrected beam waist size calculations using covariance matrix eigenvectors (fixing the overestimation at large z).
  • Fixed polarization bugs and improved field calculation/retracing.
  • Added Fraunhofer diffraction test case and optical invariant checks.
  • Documented waist parameters math and ensured all exported functions have docstrings.

…nd detector integration

- Added AstigmaticGaussianBeamlet with 9-ray structure (1 chief, 8 auxiliary).
- Implemented synchronised tracing in System.jl to ensure all rays hit the same shapes.
- Integrated AGB into the new Detector architecture (AstigmaticGaussianBeamletHit, field evaluation).
- Added support for AGB in Beamsplitters (Thin, Plate, Cube) and PolarizationFilter.
- Added Makie extension for AGB surface rendering.
- Added comprehensive tests and modularized testing structure.
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

Codecov Report

❌ Patch coverage is 71.33700% with 313 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.63%. Comparing base (5078077) to head (0aea026).

Files with missing lines Patch % Lines
src/AstigmaticGaussian.jl 78.05% 70 Missing ⚠️
ext/RenderAstigmaticGaussian.jl 0.00% 62 Missing ⚠️
src/OpticalComponents/Detectors/DetectorUtils.jl 34.48% 38 Missing ⚠️
src/BeamGroups.jl 84.91% 35 Missing ⚠️
src/System.jl 80.00% 27 Missing ⚠️
src/Gaussian.jl 24.24% 25 Missing ⚠️
...pticalComponents/Beamsplitters/CubeBeamsplitter.jl 0.00% 16 Missing ⚠️
src/OpticalComponents/Detectors/Detector.jl 81.03% 11 Missing ⚠️
...OpticalComponents/Polarizers/PolarizationFilter.jl 0.00% 8 Missing ⚠️
src/Config.jl 62.50% 6 Missing ⚠️
... and 5 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #54      +/-   ##
==========================================
- Coverage   71.97%   71.63%   -0.35%     
==========================================
  Files          57       61       +4     
  Lines        2901     3885     +984     
==========================================
+ Hits         2088     2783     +695     
- Misses        813     1102     +289     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f154c5c9b8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# For auxiliary rays, we just need the new rays starting at npos
aux_ints = map(b -> begin
r = rays(b)[id]
BeamInteraction{T, Ray{T}}(nothing, Ray(npos, direction(r), wavelength(r)))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve auxiliary ray hit positions through polarizer

This rebuilds every auxiliary ray with the same origin npos from the chief ray, so after a polarization-filter hit all parabasal rays are forced to start at the chief intersection point instead of their own intersections. For astigmatic beamlets this collapses the transverse geometry and corrupts downstream waist/phase evolution whenever the auxiliary rays hit different points on the filter plane. Each auxiliary ray should be re-emitted from its own hit point (position(r) + length(r) * direction(r)).

Useful? React with 👍 / 👎.

Comment thread src/PolarizedRays.jl Outdated
return PolarizedRay{F}(
Point3{F}(pos),
normalize(Point3{F}(dir)),
Point3{F}(dir),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Normalize direction in PolarizedRay constructor

The constructor now stores dir directly instead of normalizing it, which regresses prior behavior and makes PolarizedRay inconsistent with Ray. Many propagation/intersection calculations assume unit-length direction vectors; passing a non-unit dir now silently distorts geometric distances and optical-path-related phase calculations for polarized beams.

Useful? React with 👍 / 👎.

Comment thread src/OpticalComponents/Detectors/DetectorUtils.jl Outdated
@TacHawkes
Copy link
Copy Markdown
Collaborator Author

After some refactoring and a lot of headaches, the following script reproduces a Fraunhofer diffraction pattern like in this paper:

# Reproduce fig. 12 of Harvey et. al. Optical Engineering 54(3), 035105 (March 2015)
using GLMakie, BeamletOptics

const BMO = BeamletOptics

const mm = 1e-3
const nm = 1e-9

## Figure 12: Fraunhofer diffraction of a square aperture
# D = 2 mm, λ = 632.8 nm, evaluated at 100 m
D = 2mm
λ = 632.8nm
L = 100.0 # meters

# N x N Gaussian beamlets
n_beams = 100
Δ = D / n_beams
w0s = Δ

xs = LinRange(-D / 2 + Δ / 2, D / 2 - Δ / 2, n_beams)
zs = LinRange(-D / 2 + Δ / 2, D / 2 - Δ / 2, n_beams)

beams = AstigmaticGaussianBeamlet[]

for x in xs
    for z in zs
        b = AstigmaticGaussianBeamlet([x, 0, z], [0, 1, 0], λ, w0s; E0 = [0, 0, 1.0])
        push!(beams, b)
    end
end

pd = Detector(3000mm)
translate_to3d!(pd, [0, L, 0])

system = System([pd])

# Trace all beams to the detector
solve_system!.(Ref(system), beams)

##
x, z, I = BMO.intensity(
    pd, n = 300, x_min = -200mm, x_max = 200mm, z_min = -200mm, z_max = 200mm)

# Compare with analytical solution: I(x,z) ∝ sinc²(D*x/(λ*L)) * sinc²(D*z/(λ*L))
x_norm = x .* D ./* L)
z_norm = z .* D ./* L)
sinc_sq(u) = (u == 0) ? 1.0 : (sin* u) /* u))^2
analy_I = [sinc_sq(u_x) * sinc_sq(u_z) for u_x in x_norm, u_z in z_norm]

# Normalize numerical intensity to match analytical peak (1.0)
I_norm = I ./ maximum(I)

## 2D heatmap comparison
fig_2d = Figure(size = (800, 400))
ax_num = Axis(fig_2d[1, 1], title = "Numerical (AGB)", xlabel = "x (m)", ylabel = "z (m)")
heatmap!(ax_num, x, z, log10.(I_norm .+ 1e-15), colormap = :inferno, colorrange = (-4, 0))

ax_ana = Axis(fig_2d[1, 2], title = "Analytical", xlabel = "x (m)", ylabel = "z (m)")
heatmap!(ax_ana, x, z, log10.(analy_I .+ 1e-15), colormap = :inferno, colorrange = (-4, 0))
display(fig_2d)

##
# 1D comparison
# Take the slice at z = 0 (middle index)
mid_idx = argmin(abs.(z))

fig_1d = Figure()
ax_1d = Axis(fig_1d[1, 1], title = "Fraunhofer Diffraction Profile (z=0)",
    xlabel = "x (m)", ylabel = "Normalized Irradiance", yscale = log10)

# The paper plots it on a log scale down to 1e-6
lines!(ax_1d, x, analy_I[:, mid_idx], label = "Analytical", color = :black, linewidth = 2)
lines!(ax_1d, x, I_norm[:, mid_idx], label = "Numerical (AGB)",
    color = :red, linestyle = :dash, linewidth = 2)

ylims!(ax_1d, 1e-6, 1.5)
axislegend(ax_1d)
display(fig_1d)
image image

@StackEnjoyer
Copy link
Copy Markdown
Collaborator

StackEnjoyer commented May 4, 2026

Todos / Fixmes

  • parabasal_ray_parameters: only one wxp/dxp and wyp/dyp should be used to calculate h1/u1 etc.
    • averaging over multiple basis effectively obfuscates the invariant abort threshold
    • however, all pairs should be used to check the invariant, with all having to pass individually
  • AGB constructor: consider optical power, similar to current GaussianBeamlet constructor
  • random basis vector sampling for e.g. GaussianBeamletDecomposition
  • test WavefrontBeamletDecomposition energy conservation
  • fix AGB render! bug on ellipse basis sign flip
  • fix AGB render! flen bug on intersected object
  • unify waist_parameters and gauss_parameters for GB and AGB where applicable

@TacHawkes
Copy link
Copy Markdown
Collaborator Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6e4db6eb41

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

p_proj = pt + dist * dir

v = p_proj - p0
push!(pts_2D, Point2{T}(dot(v, ex), dot(v, ez)))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Convert astigmatic spot coordinates to real before Point2

calc_local_pos for AstigmaticGaussianBeamletHit builds points from h1/h2, which are stored as Point3{Complex{T}}; this makes pt, p_proj, and the local dot results complex. Constructing Point2{T} from those values at this line will raise an InexactError whenever the imaginary part is nonzero (the normal astigmatic case), so spot_diagram/local-limit workflows can fail for astigmatic detector hits.

Useful? React with 👍 / 👎.

# 3D waist point
pt = origin + waist_radius * (cos_t[k] * nd1 + sin_t[k] * nd2)
# Project onto detector plane along ray direction
dist = dot(p0 - pt, ey) / dot(dir, ey)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add parallel-ray guard in detector-plane projection

This projection now divides by dot(dir, ey) directly; for grazing/near-parallel incidence (dot(dir, ey) ≈ 0) it produces Inf/NaN projected points instead of a controlled failure path, which can corrupt spot bounds and field grids downstream. The previous implementation used line_plane_distance3d, which included a threshold check for this geometry edge case.

Useful? React with 👍 / 👎.

@TacHawkes
Copy link
Copy Markdown
Collaborator Author

From my point of view this PR is good to go. The last remaining ToDo is:

  • Finish AGB documentation

@TacHawkes TacHawkes force-pushed the AstigmaticGausslets branch from f8fdae6 to 0aea026 Compare May 10, 2026 09:47
@TacHawkes
Copy link
Copy Markdown
Collaborator Author

We might have to pre-render the double slit example. It seems to break doc CI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants