4.3. Elements (Lens, Ray Source, …)¶
4.3.1. Overview¶
4.3.1.1. Types¶
In optrace the class Element
denotes an object which has 0-2 surfaces and belongs to the tracing geometry.
Tracing Elements
Tracing elements are Elements with direct ray interaction:
An element with a light emitting surface |
|
An element with two surfaces on which light is refracted. |
|
A Lens, except that it has a planar surface and refracts light without aberrations |
|
Element with a surface on which wavelength-dependent filtering takes place. |
|
Similar to a Filter, except that incident light is completely absorbed. |
Rendering Elements
Elements with no ray interaction for tracing, but the ability to render images of intersecting rays.
Element with a single surface on which images or spectra can be rendered |
Markers
Markers are Elements for annotations in 3D space.
Element consisting of a point and a label |
|
Element consisting of a line and a label |
Volumes
Objects for plotting volumes in the TraceGUI, for instance an enclosing cylinder or a medium outline.
Volume of a box or cube |
|
Cylinder volume with the symmetry axis in direction of the optical axis |
|
A spherical volume |
4.3.2. RaySource¶
4.3.2.1. Overview¶
A RaySource
defines the properties for rays it creates, including
Emitting surface/point/line
Light distribution on this area
Emitted spectrum and power
Ray polarization
Ray orientation
Ray divergence
Source position
4.3.2.2. Surface/Point/Line Parameter¶
A RaySource supports the following base shapes Point, Line, CircularSurface, RectangularSurface, RingSurface
,
which are provided as first parameter to the RaySource()
constructor.
circ = ot.CircularSurface(r=3)
RS = ot.RaySource(circ)
4.3.2.3. Position Parameter¶
The position in three-dimensional space is provided by the pos
-parameter.
RS = ot.RaySource(circ, pos=[0, 1.2, -3.5])
4.3.2.4. Power Parameter¶
Providing the power
the cumulative power of all rays is defined. This proves especially
useful when working with multiple sources with different power ratios.
RS = ot.RaySource(circ, power=0.5)
4.3.2.5. Orientation Parameter¶
The base orientation type of the rays is defined by the orientation
-parameter.
For orientation="Constant"
the orientation is independent of the position on the emitting area.
The orientation vector is then defined by the s
-parameter in cartesian coordinates.
RS = ot.RaySource(circ, orientation="Constant", s=[0.7, 0, 0.7])
Or with s_sph
for spherical coordinates, where the first one is the angle between
the orientation and the optical axis and the second the angle inside the lateral plane.
Values are provided in degrees.
RS = ot.RaySource(circ, orientation="Constant", s_sph=[20, -30])
If all rays from the source should be converging to a position conv_pos
,
mode orientation="Converging"
should be employed:
RS = ot.RaySource(circ, orientation="Converging", conv_pos=[10, 2, -1])
It is also possible to define orientations as a function of the position of the rays.
For this we need to set orientation="Function"
and provide the or_func
parameter.
This parameter takes two numpy arrays containing the x and y-position
and returns a two dimensional array with cartesian vector components in rows.
def or_func(x, y, g=5):
s = np.column_stack((-x, -y, np.ones_like(x)*g))
ab = (s[:, 0]**2 + s[:, 1]**2 + s[:, 2]**2) ** 0.5
return s / ab[:, np.newaxis]
RS = ot.RaySource(circ, orientation="Function", or_func=or_func)
As with other functions, we can also provide a keyword argument dictionary for the function:
...
RS = ot.RaySource(circ, orientation="Function", or_func=or_func, or_args=dict(g=10))
4.3.2.6. Spectrum Parameter¶
A LightSpectrum
object
is provided with the spectrum
parameter.
For instance, this can be a predefined spectrum:
RS = ot.RaySource(circ, spectrum=ot.presets.light_spectrum.d75)
Or a user defined one:
spec = ot.LightSpectrum("Monochromatic", wl=529)
RS = ot.RaySource(circ, spectrum=spec)
4.3.2.7. Divergence Parameter¶
Divergence defines how rays are distributed relative to their base orientation (orientation
parameter).
With divergence="None"
all rays follow their orientation:
RS = ot.RaySource(circ, divergence="None", s=[0.7, 0, 0.7])
Paired with orientation="Constant"
all rays are emitted in parallel.
We can also define Lambertian divergence, which follows the cosine law.
div_angle
defines the half opening angle of the cone volume in which the divergence is generated.
RS = ot.RaySource(circ, divergence="Lambertian", div_angle=10)
divergence="Isotropic"
defines divergence with equal probability in all directions,
but again only inside the cone defined by div_angle
.
RS = ot.RaySource(circ, divergence="Isotropic", div_angle=10)
User functions can be defined by divergence="Function"
and providing the div_func
parameter.
This function must take angular values in radians up to div_angle
and return a normalized or unnormalized probability.
RS = ot.RaySource(circ, divergence="Function", div_func=lambda e: np.cos(e)**2, div_angle=10)
For all the combinations above we can also generate a direction distribution inside an circular arc instead of a cone.
The correct way to do this is by setting div_2d=True
. With div_axis_angle
we can additionally define the orientation of this arc distribution.
RS = ot.RaySource(circ, divergence="Function", div_func=lambda e: np.cos(e)**2, div_2d=True, div_axis_angle=20, div_angle=10)
4.3.2.8. Image Parameter¶
Alternatively to a uniformly emitting area, there is a way to provide light distributions (modelled by images).
This emitting surface needs to be a ScalarImage
or RGBImage
object.
A RectangularSurface
emitting this image will be created automatically.
Its size will be equal to the side lengths of the image.
image = ot.presets.image.landscape([2, 3])
RS = ot.RaySource(image)
image = ot.RGBImage(np.random.sample((300, 300, 3)), [2, 3])
RS = ot.RaySource(image)
image = ot.RGBImage("some_image_path", [2, 3])
RS = ot.RaySource(image)
Every image color generates a specific physical spectrum matching its color. This spectrum is a linear combination of the sRGB primaries in Section 5.8.5.
With image
specified the spectrum
parameter won’t be used.
4.3.2.9. Polarization Parameter¶
The polarization parameter describes the distribution of linear light polarizations.
In the default case the directions are random, parametrized by polarization="Uniform"
.
RS = ot.RaySource(circ, polarization="Uniform")
polarization="x"
defines polarizations parallel to the x-axis.
RS = ot.RaySource(circ, polarization="x")
polarization="y"
defines polarizations parallel to the y-axis.
RS = ot.RaySource(circ, polarization="y")
polarization="xy"
defines random polarizations of x or y-direction.
RS = ot.RaySource(circ, polarization="xy")
The user can also set a user-defined value with polarization="Constant"
and the pol_angle
parameter.
The polarization direction is defined by an angle inside the plane perpendicular to the ray direction.
RS = ot.RaySource(circ, polarization="Constant", pol_angle=12)
Or alternatively a list with polarization="List"
,
the angular values in pol_angles
and their probabilities in pol_probs
.
RS = ot.RaySource(circ, polarization="List", pol_angles=[0, 45, 90], pol_probs=[0.5, 0.25, 0.25])
Lastly, a user defined function is set with polarization="Function"
and the pol_func
parameter.
This parameter takes angles in range \([0, ~2 \pi]\) and returns a normalized or unnormalized probability.
Above we talked how for instance for polarization="x"
the rays are parallel to the x-axis.
However, depending on their actual ray orientation this isn’t always the case.
Read about what the angles mean for rays not parallel to the optical axis in Section 5.1.8.
RS = ot.RaySource(circ, polarization="Function", pol_func=lambda ang: np.exp(-(ang - 30)**2/10))
4.3.3. Lens¶
4.3.3.1. Overview¶
A Lens
consists of two surfaces
and a medium with a RefractionIndex
inbetween.
Additionally, the position and a thickness parameter is required.
4.3.3.2. Example¶
sph1 = ot.SphericalSurface(r=3, R=10.2)
sph2 = ot.SphericalSurface(r=3, R=-20)
n = ot.RefractionIndex("Sellmeier2", coeff=[1.045, 0.266, 0.206, 0, 0])
L = ot.Lens(sph1, sph2, n=n, pos=[0, 2, 10], de=0.5)
To define a different ambient medium behind the lens (other than the ambient
medium defined by the raytracer geometry), we can provide the n2
parameter.
n2 = ot.RefractionIndex("Constant", n=1.2)
L = ot.Lens(sph1, sph2, n=n, pos=[0, 2, 10], de=0.5, n2=n2)
4.3.3.3. Lens Thickness¶
To allow for simple definitions of lens thickness and positions, there are multiple ways to define the thickness:
d
: thickness at the optical axisde
: thickness extension. Distance between largest z-position on front and lowest z-position on backd1
: distance between front surface center z-position and z-position ofpos
of Lensd2
: distance between z-position ofpos
of Lens and z-position of the back surface center
Fig. 4.15 \(d\) and \(d_\text{e}\) for a convex lens, a concave lens and a meniscus lens¶
While for a convex lens using the de
is most comfortable,
for concave or meniscus lenses the thickness at the optical axis d
proves to be more useful.
For instance, a concave lens can be defined with:
L = ot.Lens(sph2, sph1, n=n, pos=[0, 2, 10], d=0.5)
When the lens is defined by d
or de
the position pos[2]
is at the center of the d
or de
distance.
With the d1
and d2
parameters we can control the position of both surfaces relative
to the lens position manually.
For instance, with d1=0, d2=...
the lens front starts exactly at the pos
of the Lens.
On the other hand setting d1=..., d2=0
leads to the back surface center ending at pos
.
Fig. 4.16 Defining a convex lens by de=...
, by d1=0, d2=...
and by d1=..., d2=0
.¶
All cases in-between are also viable, for instance:
L = ot.Lens(sph1, sph2, n=n, pos=[0, 2, 10], d1=0.1, d2=0.6)
But only as long as the surfaces don’t collide. With a Lens object you can also access the thickness parameters:
>>> L.d
0.7
>>> L.de
0.022566018...
>>> L.d1
0.1
>>> L.d2
0.6
Or the parameters of its surfaces:
>>> L.front.ds
0.45115391...
4.3.3.4. Paraxial Properties¶
Paraxial analysis by using transfer matrix is described in Section 4.10.
As for a setup of many lenses, we can also do paraxial analysis on a simple lens.
To create a ray transfer matrix analysis object (TMA
object)
we call the member function tma()
.
>>> tma = L.tma()
>>> tma.efl
12.749973...
As the behavior can differ with the light wavelength, we can also provide a non-default wavelength in nanometers.
As the lens object itself has no knowledge of the geometry surrounding it, the prior medium it is undefined.
By default, a constant refractive index of 1 is assumed, but can be overwritten with the parameter n0
.
>>> tma = L.tma(589.2, n0=ot.RefractionIndex("Constant", n=1.1))
>>> tma.efl
17.300045...
4.3.4. Ideal Lens¶
An IdealLens
focusses and images light perfectly
and without aberrations according to the imaging equation.
The geometry is an infinitesimal thin circular area with radius r
.
Additionally, the optical power D
and a position pos
need to be provided.
IL = ot.IdealLens(r=5, D=12.5, pos=[0, 0, 9.5])
As for a normal Lens, it is possible to define a n2
.
Note that this does not change the optical power or focal length, as they are controlled by the D
parameter.
n2 = ot.RefractionIndex("Constant", n=1.25)
IL = ot.IdealLens(r=4, D=-8.2, pos=[0, 0, 9.5], n2=n2)
4.3.5. Filter¶
When light hits a Filter
,
the ray power is transmitted according to the filter’s transmittance function.
A Filter is defined by a Surface, a position and the
TransmissionSpectrum
.
spec = ot.TransmissionSpectrum("Rectangle", wl0=400, wl1=500, val=0.5)
circ = ot.CircularSurface(r=5)
F = ot.Filter(circ, pos=[0, 0, 23.93], spectrum=spec)
With a filter the approximate sRGB color is calculated with F.color()
.
The fourth return value is the opacity for visualization.
Note that the opacity is more of a visual extra than a physically correct simulation.
Calling the filter with a wavelength array returns the transmittance at these wavelengths.
>>> wl = np.array([380, 400, 550])
>>> F(wl)
array([0. , 0.5, 0. ])
When tracing, the raytracer sets all transmission values below a specific threshold T_TH
to zero.
This is done to avoid ghost rays, that are rays that merely contribute to the light distribution
or image but are nonetheless calculated and reduce performance.
An example are rays far outside of a normal distribution.
By default the relative threshold value is
>>> ot.Raytracer.T_TH
1e-05
4.3.6. Aperture¶
An Aperture
is just a
Filter
that absorbs light completely for rays that hit it.
In the most common use cases a RingSurface
is employed as Aperture surface. As for all other elements, we also need to specify the position pos
.
ring = ot.RingSurface(ri=0.05, r=5)
AP = ot.Aperture(ring, pos=[0, 2, 10.1])
4.3.7. Detector¶
A Detector
enables the rendering of images
and spectra on its geometry. But by itself, it has no effect on raytracing.
It takes a surface parameter and the position parameter as arguments.
rect = ot.RectangularSurface(dim=[1.5, 2.3])
Det = ot.Detector(rect, pos=[0, 0, 15.2])
4.3.8. Markers¶
4.3.8.1. PointMarker¶
A PointMarker
annotates positions or elements inside the tracing geometry.
As all markers, is has no influence on the tracing process.
In the simplest case a PointMarker
is defined with a text string and a position
for the Point
.
M = ot.PointMarker("Text132", pos=[0.5, 9.1, 0.5])
One can scale the text and marker with text_factor
or marker_factor
.
The actual size change is handled by the plotting GUI.
M = ot.PointMarker("Text132", pos=[0.5, 9.1, 0.5], text_factor=2.3, marker_factor=0.5)
We can also hide the marker point and only display the text with the parameter label_only=True
.
M = ot.PointMarker("Text132", pos=[0.5, 9.1, 0.5], label_only=True)
Conversly, text can be hidden by leaving the text empty:
M = ot.PointMarker("", pos=[0.5, 9.1, 0.5])
4.3.8.2. LineMarker¶
Similarly, a LineMarker
is a Line
in the xy-plane with a text annotation.
In the simplest case a LineMarker
is defined by a text string, radius, angle and a position.
M = ot.LineMarker(r=3, desc="Text132", angle=45, pos=[0.5, 9.1, 0.5])
One can scale the text and marker with text_factor
or line_factor
.
The actual size change is handled by the plotting GUI.
M = ot.LineMarker(r=3, desc="Text132", pos=[0.5, 9.1, 0.5], text_factor=2.3, line_factor=0.5)
We can hide the text and only plot the marker line by leaving the text empty:
M = ot.LineMarker(r=3, desc="", pos=[0.5, 9.1, 0.5])
4.3.9. Volumes¶
4.3.9.1. BoxVolume¶
As for a RectangularSurface
,
the parameter dim
defines the x- and y-side lengths in the lateral plane.
Parameter pos
describes the center of this rectangle.
For a BoxVolume
this surface gets extended
by length length
in positive z-direction, forming a three-dimensional volume.
ot.BoxVolume(dim=[10, 20], length=15, pos=[0, 2, 3])
Additionally, a plotting opacity and color can be specified:
ot.BoxVolume(dim=[10, 20], length=15, pos=[0, 2, 3], opacity=0.8, color=(0, 1, 0))
4.3.9.2. SphereVolume¶
A SphereVolume
is defined by its center position pos
and the sphere radius R
:
ot.SphereVolume(R=10, pos=[0, 2, 3])
As for the other volumes, the plotting opacity and color can be specified:
ot.SphereVolume(R=10, pos=[0, 2, 3], opacity=0.8, color=(0, 0, 1))
4.3.9.3. CylinderVolume¶
A CylinderVolume
is defined by its front surface center position pos
and the cylinder radius r
:
ot.CylinderVolume(r=5, length=15, pos=[0, 2, 3])
As for the other volumes, the plotting opacity and color can be specified:
ot.CylinderVolume(r=5, length=15, pos=[0, 2, 3], opacity=0.8, color=(0.5, 0.1, 0.0))
4.3.9.4. Custom Volumes¶
A custom Volume
makes it possible to define user-defined volumes.
It requires a front and back surface as parameter, as well as a position and the thickness distances d1, d2
.
These have the same meaning as for a Lens
in Section 4.3.3.3.
You can find an example below:
front = ot.ConicSurface(r=4, k=2, R=50)
back = ot.RectangularSurface(dim=[3, 3])
vol = ot.Volume(front, back, pos=[0, 1, 2], d1=front.ds, d2=back.ds+1)
Here a conic front surface and a rectangular surface are defined. front.ds, back.ds
denote the total thickness of both surfaces at their center.
The overall length for this volumes is then front.ds + back.ds + 1
.