Skip to content

Commit 16cecf8

Browse files
authored
Detector update (#37)
* add Detector.jl * flesh out hit types * add spot_diagram for testing * change wave_number to wavenumber * more "ideas" * use getter fcts * gaussian beamlet intensity test fct * switch gb eval loop order * dispatch calc_local_pos and calc_local_lims based on detector hit type * fix issue #36 * rename some hit getters * prepare calc local lims and pos for gb integration * add ellipse helper fct * add calc_local_pos and "spot diagram" for GaussianBeamlets * calc_local_lims for GaussianBeamlet * add some docs * add ellipse tests * add Detector type docs * up vn to 0.11 due to breaking detector changes * replace Photodetector with new API * start with splitting up runtests.jl * fix spot diagram x-axis flip * more runtests splitting * even more runtests splitting * replace Spotdetector and PSFDetector with new API * remove double Interference tests * finalize runtests splitting * update countlines * add hit docstrings * rm old detector files * rm stale refs in abstract pd docstring * fix psf orientation bug * update docs p1 * Make normal3d deterministic * return multithreading to gb intensity calc. * add reentrant lock to detector push! * add spot diagram docstring and error msg * update MI tutorial * update detectors.md * final changes * Update detector docs * fix type bug * use Union{all implemented hit types} instead of <:AbstractDetectorHit for type stability * Try to improve inference * add center calc. algorithm types for type stability
1 parent 7a0ab61 commit 16cecf8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+4207
-3516
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "BeamletOptics"
22
uuid = "c387492b-ffec-4e51-9a46-bd230226031c"
33
authors = ["Hugo Uittenbosch <[email protected]>, Oliver Kliebisch <[email protected]> and Thomas Dekorsy <[email protected]>"]
4-
version = "0.10.2"
4+
version = "0.11.0"
55

66
[deps]
77
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"

docs/src/api/conventions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Conventions
22

3-
In order to ensure implicit and explicit compliance with large parts of the API, the following conventions need to be used. Below you can find some essential conventions that need to be followed. For specific components or parts of the code base, refer to the documentation if certain guidelines need to be followed.
3+
In order to ensure implicit and explicit compliance with large parts of the API, the following conventions need to be used unless specified otherwise. Below you can find some essential conventions. For specific components or parts of the code base, refer to the respective documentation if certain guidelines need to be followed.
44

55
!!! warning
66
Failure to comply with the following conventions can lead to spurious effects and silent bugs when using the API of this package!

docs/src/api/core.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ BeamletOptics.retrace_system!
9797

9898
## CPU and GPU support
9999

100-
Parallizing the execution of a [`solve_system!`](@ref) call on the CPU is straight-forward for systems that do not feature objects which can be mutated during runtime, e.g. detectors like the [`Photodetector`](@ref). For each beam or ray the solution is independent and the solver can run on multiple threads. Special consideration needs to be taken when implementing mutable elements as mentioned above, since multiple threads might be able to access the underlying memory, leading to race conditions. Specifically, this means ensuring atomic write and read access.
100+
Parallizing the execution of a [`solve_system!`](@ref) call on the CPU is straight-forward for systems that do not feature objects which can be mutated during runtime, e.g. detectors like the [`Detector`](@ref). For each beam or ray the solution is independent and the solver can run on multiple threads. Special consideration needs to be taken when implementing mutable elements as mentioned above, since multiple threads might be able to access the underlying memory, leading to race conditions. Specifically, this means ensuring e.g. atomic write and read access.
101101

102102
With respect to GPU acceleration, this is not the case. Currently, all available implementations of [`solve_system!`](@ref) are highly branching algorithms which can not be implemented in a parallized way easily. This will most likley require a specific new subtype of the [`BeamletOptics.AbstractSystem`](@ref) with determinable sequential properties. This is not a development goal as of the writing of this section.
103103

docs/src/assets/detector_assets/photodetector_showcase.jl

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ using GLMakie, BeamletOptics
33
GLMakie.activate!(; ssao=true)
44

55
##
6-
pd = Photodetector(1e-3, 1000)
6+
pd = Detector(1e-3)
77
pd_body = MeshDummy(joinpath(@__DIR__, "FDS010.stl"))
88
zrotate3d!(pd_body, π)
99
translate3d!(pd, [0,-3e-3,0])
@@ -18,13 +18,18 @@ g2 = GaussianBeamlet([0,-20e-3,0], [0,1,0], 532e-9, 5e-4)
1818
solve_system!(system, g1)
1919
solve_system!(system, g2)
2020

21+
x, y, I = intensity(pd, n=1000)
22+
spots = spot_diagram(pd)
23+
2124
## render fringes
2225
fringes_fig = Figure()
2326
heat = Axis(fringes_fig[1, 1], xlabel="x [mm]", ylabel="y [mm]", aspect=1)
24-
hm = heatmap!(heat, pd.x*1e3, pd.y*1e3, intensity(pd), colormap=:viridis)
27+
hm = heatmap!(heat, x*1e3, y*1e3, I, colormap=:viridis)
2528
cb = Colorbar(fringes_fig[1, 2], hm, label="Intensity [W/m²]")
2629

27-
save("fringes_showcase.png", fringes_fig; px_per_unit=4)
30+
scatter!(heat, spots*1e3; color=:red, markersize=4)
31+
32+
save("fringes_showcase.png", fringes_fig; px_per_unit=8)
2833

2934
## render system
3035
detector_fig = Figure(size=(600, 280))

docs/src/assets/detector_assets/psfdetector_showcase.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ n = 1.5
1515
cs = UniformDiscSource([0, -10mm, 0], [0, 1, 0], 15e-3, λ)
1616
lens = SphericalLens(R1, R2, l, d, x -> n)
1717

18-
psfd = PSFDetector(10e-3)
18+
psfd = Detector(10e-3)
1919
translate3d!(psfd, [0, 200mm + 0.13mm, 0])
2020

2121
sys = System([lens, psfd])
@@ -46,7 +46,7 @@ AL75150 = Lens(
4646

4747
xrotate3d!(AL75150, deg2rad(-0.5))
4848

49-
pd = PSFDetector(15e-3)
49+
pd = Detector(15e-3)
5050

5151
translate3d!(pd, [0, 158.1779e-3, 0.0])
5252
system = System([AL75150, pd])

docs/src/assets/detector_assets/spotdetector_showcase.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const mm = 1e-3
77
##
88
lens = ThinLens(50mm, 50mm, BeamletOptics.inch, 1.5)
99

10-
sd = Spotdetector(10e-3)
10+
sd = Detector(10e-3)
1111
translate3d!(sd, [0, 50mm, 0])
1212

1313
system = System([lens, sd])
@@ -33,14 +33,16 @@ cs = CollimatedSource([0,-50mm,0], [0,1,0], aperture, 1e-6; num_rings, num_rays)
3333

3434
t1 = @timed solve_system!(system, cs)
3535

36+
spots = spot_diagram(sd)
37+
3638
render!(system_ax, cs, color=:blue, show_pos=false, render_every=50)
3739

3840
save("spot_diagram_system.png", system_fig, px_per_unit=4)
3941

4042
## render diagram
4143
spot_fig = Figure(size=(600,400))
4244
spot_ax = Axis(spot_fig[1,1], aspect=1, xlabel="x [mm]", ylabel="y [mm]")
43-
sc = scatter!(spot_ax, sd.data, markersize=3, color=:blue)
45+
sc = scatter!(spot_ax, spots, markersize=3, color=:blue)
4446

4547
extime = trunc(t1.time*1e3, digits=2)
4648

docs/src/assets/mi_assets/michelson_showcase.jl

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ arm_2 = ObjectGroup([m2, arm_holder_2])
6262

6363
# PD
6464
pd_holder = MeshDummy(joinpath(asset_dir, "PD Assembly.stl"))
65-
pd = Photodetector(5e-3, 200)
65+
pd_size = 5mm
66+
pd = Detector(pd_size)
6667
translate3d!(pd, [0, -12cm, 0])
6768

6869
pd_assembly = ObjectGroup([pd, pd_holder])
@@ -235,20 +236,42 @@ set_view(ax, pd_view)
235236
save("mi_pd.png", fig; px_per_unit=8, update = false)
236237

237238
## fringe plot
239+
x, y, I = intensity(
240+
pd,
241+
n=200,
242+
x_min=-pd_size/2,
243+
x_max= pd_size/2,
244+
z_min=-pd_size/2,
245+
z_max= pd_size/2,
246+
)
247+
spots = spot_diagram(pd)
248+
238249
fringes_fig = Figure(size=(600, 270))
239250
heat1 = Axis(fringes_fig[1, 1], xlabel="x [mm]", ylabel="y [mm]", title="Before rotation", aspect=1)
240251
heat2 = Axis(fringes_fig[1, 2], xlabel="x [mm]", ylabel="y [mm]", title="After rotation", aspect=1, yaxisposition=:right)
241252

242253
hidedecorations!(heat1)
243254
hidedecorations!(heat2)
244255

245-
hm = heatmap!(heat1, pd.x*1e3, pd.y*1e3, intensity(pd), colormap=:viridis)
256+
hm = heatmap!(heat1, x*1e3, y*1e3, I, colormap=:viridis)
257+
scatter!(heat1, spots*1e3; color=:red, markersize=2)
246258

247259
zrotate3d!(m1, 1e-3)
248260
empty!(pd)
249261
solve_system!(system, beam)
250262

251-
hm = heatmap!(heat2, pd.x*1e3, pd.y*1e3, intensity(pd), colormap=:viridis)
263+
x, y, I = intensity(
264+
pd,
265+
n=200,
266+
x_min=-pd_size/2,
267+
x_max= pd_size/2,
268+
z_min=-pd_size/2,
269+
z_max= pd_size/2,
270+
)
271+
spots = spot_diagram(pd)
272+
273+
hm = heatmap!(heat2, x*1e3, y*1e3, I, colormap=:viridis)
274+
scatter!(heat2, spots*1e3; color=:red, markersize=2)
252275

253276
save("mi_fringes.png", fringes_fig, px_per_unit=4)
254277

@@ -265,7 +288,7 @@ P = zeros(n+1)
265288
for i in eachindex(P)
266289
empty!(pd)
267290
solve_system!(system, beam)
268-
P[i] = BeamletOptics.optical_power(pd)
291+
P[i] = optical_power(pd)
269292
# translate by Δy
270293
translate3d!(m2, [0, Δy, 0])
271294
end

docs/src/basics/beams.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ CollimatedSource(::AbstractArray{<:Real}, ::AbstractArray{<:Real}, ::Real, ::Rea
5050

5151
A special constructor called [`UniformDiscSource`](@ref) is available, which offers an equal-area
5252
sampling (Fibonnaci-pattern) sampling and is thus favorable in situations where the weighting of the
53-
individual beams becomes important, e.g. for calculating a point spread function using [`PSFDetector`](@ref).
53+
individual beams becomes important, e.g. for calculating a point spread function using the [`intensity`](@ref) function.
5454

5555
```@docs; canonical=false
5656
UniformDiscSource

docs/src/basics/components/detectors.md

Lines changed: 55 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,75 @@ include(joinpath(@__DIR__, "..", "..", "assets", "cond_save.jl"))
33
44
detector_showcase_dir = joinpath(@__DIR__, "..", "..", "assets", "detector_assets")
55
6-
conditional_include(joinpath(detector_showcase_dir, "photodetector_showcase.jl"))
76
conditional_include(joinpath(detector_showcase_dir, "spotdetector_showcase.jl"))
8-
conditional_include(joinpath(detector_showcase_dir, "psfdetector_showcase.jl"))
7+
conditional_include(joinpath(detector_showcase_dir, "photodetector_showcase.jl"), use_placeholder=false)
8+
conditional_include(joinpath(detector_showcase_dir, "psfdetector_showcase.jl"), use_placeholder=false)
99
```
1010

1111
# Detectors
1212

13-
Detectors provide a way to evaluate beam data during optical simulations. They are designed to accumulate e.g. field data, enabling analysis of intensity distributions, interference patterns, and other beam properties.
13+
In practice, photodetectors allow the conversion of electromagnetic radiation into electric signals. BMO provides the [`Detector`](@ref) as an element to capture and evaluate optical data during and after running a simulation, respectively. The `Detector` is designed to accumulate e.g. field or ray data, enabling analysis of intensity distributions, interference patterns, and other beam properties. Currently, post-processing capabilities are limited to the functionality as described in the [Spot diagrams](@ref) and [Field distributions](@ref) sections.
1414

15-
Detectors are supposed to fall under the [`BeamletOptics.AbstractDetector`](@ref) type, which defines a interface for detector implementations.
15+
In general, detector-like elements are supposed to fall under the [`BeamletOptics.AbstractDetector`](@ref) type, which defines a interface for detector implementations.
1616

1717
!!! warning "Resetting detectors"
18-
In general, the data stored in a detector is not automatically reset between calls of [`solve_system!`](@ref). This task is placed within the responsibility of the user. A detector reset can be performed with the [`empty!`](@ref) function.
18+
In general, the data stored in a `Detector` is not automatically reset between calls of [`solve_system!`](@ref). This task is placed within the responsibility of the user. A detector reset can be performed with the [`empty!`](@ref) function.
1919

20-
## Photodetector type
20+
## Detector type
2121

22-
A concrete implementation to "measure" intensity distributions generated by a [`GaussianBeamlet`](@ref) is provided in the form of the [`Photodetector`](@ref):
22+
A `Detector` can be easily spawned by initializing e.g. `pd = Detector(5mm)` which will create a 5x5 mm² detection screen.
2323

2424
```@docs; canonical=false
25-
Photodetector(::Real, ::Int)
25+
Detector(::Real, ::Bool)
26+
Detector
2627
```
2728

28-
The `interact3d` model of the [`Photodetector`](@ref) can store complex electric field (E-field) values from intersecting [`GaussianBeamlet`](@ref)s, enabling the reconstruction of spatial intensity distribution across its active surface. This data can be used to calculate e.g. beam interference patterns via the [`intensity`](@ref) function. The [`BeamletOptics.optical_power`](@ref) method can be used in order to obtain the total optical power at the detector. Below a rendered example of a detector model ([FDS010](https://www.thorlabs.com/thorproduct.cfm?partnumber=FDS010)) can be seen. The detector active area is marked in blue (1x1 mm²).
29+
After solving a system containing a `Detector`, the methods listed below can be used in order to analyze the stored data. If no data is obtained during the tracing procedure, an error message will be stored.
2930

30-
![Photodetector showcase](pd_showcase.png)
31+
## Spot diagrams
3132

32-
One of the use cases of the [`Photodetector`](@ref) is to analyse interference patterns. The figure below demonstrates an example intensity distribution captured by the detector pictured above, showing radial fringes due to a mismatch of the radii of curvature of the interfering [`GaussianBeamlet`](@ref)s.
33+
The `spot_diagram` method provides a straight forward way to generate spot diagrams, which are commonly used to perform initial assessments of the optical performance of an imaging setup.
3334

34-
!!! tip "Interferometer tutorial"
35-
Refer to the [Michelson interferometer](@ref) section for a detailed tutorial on how to use the [`Photodetector`](@ref).
35+
```@docs; canonical=false
36+
spot_diagram
37+
```
3638

37-
![Interference fringes showcase](fringes_showcase.png)
39+
Below an optical system consisting of a collection of collimated [`Beam`](@ref)s passing through a [`ThinLens`](@ref) is shown. A [`Detector`](@ref) is positioned at the approximate focal plane to capture the resulting spot diagram.
40+
41+
![Thin lens setup](spot_diagram_system.png)
42+
43+
The beam bundle used to generate the spot diagram was created via the [`CollimatedSource`](@ref) constructor. The resulting spot diagram of the lens shown above is visualized below.
44+
45+
![Spot diagram showcase](spot_diagram_showcase.png)
3846

39-
## Spotdetector type
47+
## Field distributions
4048

41-
A straight forward detector that stores the [`BeamletOptics.Intersection`](@ref) position of an incoming [`Beam`](@ref). The [`Spotdetector`](@ref) can be used to generate spot diagrams, which are commonly used to perform initial assessments of the optical performance of an imaging setup.
49+
Alternatively, the detector data can also be used to reconstruct electric field distributions of incoming beams on its surface using coherent addition. Depending on the beam type, either plane wave or Gaussian beam models are used. As a user, this data can be accessed using the [`electric_field`](@ref) interface.
4250

4351
```@docs; canonical=false
44-
Spotdetector(::AbstractFloat)
52+
electric_field(::Detector)
4553
```
4654

47-
Below an optical system consisting of a collection of collimated [`Beam`](@ref)s passing through a [`ThinLens`](@ref) is shown. A [`Spotdetector`](@ref) is positioned at the approximate focal plane to capture the resulting spot diagram.
55+
For convienience, the [`intensity`](@ref) function returns flux values directly. The [`optical_power`](@ref) method can be used in order to obtain the total optical power on the detector surface.
4856

49-
![Thin lens setup](spot_diagram_system.png)
57+
```@docs; canonical=false
58+
intensity(::Detector)
59+
```
5060

51-
The beam bundle used to generate the spot diagram was created via the [`CollimatedSource`](@ref) constructor. The resulting spot diagram of the lens shown above is visualized below.
61+
### Gaussian beamlet interference
5262

53-
![Spot diagram showcase](spot_diagram_showcase.png)
63+
One of the use cases of the [`Detector`](@ref) is to analyse interference patterns. Below a rendered example of a detector model ([FDS010](https://www.thorlabs.com/thorproduct.cfm?partnumber=FDS010)) can be seen. The detector active area is marked in blue (1x1 mm²).
5464

55-
## Point-spread-function detector type
65+
![Photodetector showcase](pd_showcase.png)
66+
67+
The figure below demonstrates an example intensity distribution captured by the detector pictured above, showing radial fringes due to a mismatch of the radii of curvature of the interfering [`GaussianBeamlet`](@ref)s. In addition, the beam waists have been visualized.
68+
69+
!!! tip "Interferometer tutorial"
70+
Refer to the [Michelson interferometer](@ref) section for a detailed tutorial on how to use the [`Detector`](@ref).
71+
72+
![Interference fringes showcase](fringes_showcase.png)
73+
74+
### Point spread function calculation
5675

5776
!!! warning "Experimental feature"
5877
The point spread function estimation is a highly experimental feature. It does not use
@@ -63,24 +82,15 @@ The beam bundle used to generate the spot diagram was created via the [`Collimat
6382
The package offers a simple method to estimate the point spread function of a system. It is
6483
currently limited and requires careful assessment by the user, if the results are to be trusted.
6584

66-
To analyze the PSF of a imaging system a [`PSFDetector`](@ref) is added to the system at the plane
67-
and orientation where the PSF is requested. This is the same approach as for the other detector types.
68-
69-
```@docs; canonical=false
70-
PSFDetector(::Real)
71-
```
72-
73-
The intensity map together with the coordinate system of the detector can be retrieved after solving the system by calling the [`intensity`](@ref) function.
74-
75-
```@docs; canonical=false
76-
intensity(::PSFDetector)
77-
```
85+
To analyze the PSF of a imaging system a [`Detector`](@ref) is added to the system at the plane
86+
and orientation where the PSF is requested. This is the same approach as for the other detector types.
87+
The intensity map together with the coordinate system of the detector can be retrieved after solving
88+
the system by calling the [`intensity`](@ref) function.
7889

7990
!!! warning
8091
When dealing with a collimated source as the input to your optical system, where you want to calculate the PSF, **DO NOT** use the [`CollimatedSource`](@ref) beam group directly but instead use the [`UniformDiscSource`](@ref) constructor. This function returns a `CollimatedSource` with an equal-area sampling, which correctly weights the outer beams in relation to the inner beams. Otherwise the results might be wrong.
8192

82-
83-
### Airy-Disc Example
93+
#### Airy-Disc Example
8494

8595
This is a classic example where a collimated circular beam is imaged onto a point by a singlet lens.
8696
Due to the finite size of the aperture stop (in this case given by the 15 mm size of the beam), the diffraction
@@ -101,28 +111,28 @@ d = 25.4e-3
101111
n = 1.5
102112
λ = 1e-6
103113

104-
# generate uniform source, lens and PSF detector
114+
# generate uniform source, lens and detector
105115
cs = UniformDiscSource([0, -10mm, 0], [0, 1, 0], 15e-3, λ)
106116
lens = SphericalLens(R1, R2, l, d, x -> n)
107-
psfd = PSFDetector(10e-3)
117+
detector = Detector(10e-3)
108118

109119
# shift detector into focus
110-
translate3d!(psfd, [0, 200mm + 0.13mm, 0])
120+
translate3d!(detector, [0, 200mm + 0.13mm, 0])
111121

112122
# build system
113-
sys = System([lens, psfd])
123+
sys = System([lens, detector])
114124

115125
solve_system!(sys, cs)
116126

117127
# retrieve intensity
118-
x, z, I_num = intensity(psfd; n=500, crop_factor=5)
128+
x, z, I_num = intensity(detector; n=500, crop_factor=10)
119129
```
120130

121131
Visualizing the result yields the expected Airy-disk pattern.
122132

123133
![Airy disc PSF](psf_airy_showcase.png)
124134

125-
### Coma and Astigmatism Example
135+
#### Coma and Astigmatism Example
126136

127137
In this example, an aspheric lens images the collimated source onto a point but is tilted around the x-axis by 0.5 degrees.
128138
This results in aberrations distorting the stigmatic imaging and leading to coma and astigmatism.
@@ -141,10 +151,10 @@ AL75150 = Lens(
141151

142152
xrotate3d!(AL75150, deg2rad(-0.5))
143153

144-
pd = PSFDetector(15e-3)
154+
detector = Detector(15e-3)
145155

146-
translate3d!(pd, [0, 158.1779e-3, 0.0])
147-
system = System([AL75150, pd])
156+
translate3d!(detector, [0, 158.1779e-3, 0.0])
157+
system = System([AL75150, detector])
148158

149159
ps = UniformDiscSource([0, -0.1, 0], [0,1,0], 0.8*d, 1550e-9)
150160

0 commit comments

Comments
 (0)