4.5. Raytracer

4.5.1. Raytracer

Overview

The Raytracer class provides the functionality for tracing, geometry checking, rendering spectra and images, and focusing.

Since the Raytracer is a subclass of a Group, elements can be changed or added in the same way.

../_images/example_legrand1.png

Fig. 4.19 Example of a raytracer geometry in the TraceGUI in side view

Outline

All objects and rays can only exist in a three-dimensional box, the outline. When initializing the Raytracer it is passed as outline parameter.

RT = ot.Raytracer(outline=[-2, 2, -3, 3, -5, 60])

Geometry

Since optrace implements sequential raytracing, the surfaces and objects must be in a well-defined and unique sequence. This applies to all elements with interactions of light: Lens, IdealLens, Filter, Aperture, RaySource. The elements Detector, LineMarker, PointMarker, BoxVolume, SphereVolume, CylinderVolume are excluded from this. All ray source elements must lie before all lenses, filters and apertures. And all subsequent lenses, filters, apertures must not collide with each other and be inside the outline.

Rays that hit the outline box will be absorbed.

Surrounding Media

In Lens we learned that when creating a lens, the n2 parameter defines the subsequent medium. In the case of multiple lenses, the n2 of the previous lens is the medium prior to the next lens. In the case of the raytracer, we can define an n0 which defines the refractive index for all undefined n2=None, as well as for the region to the first lens.

The following figure shows a setup with lenses L0, L2 having a n2 defined and a custom n0 parameter in the raytracer class. The medium before the first lens as well as the medium behind L1 are therefore also n0.

../_images/rt_setup_different_ambient_media.svg

Fig. 4.20 Schematic figure of a setup with a ray source, three different lenses and three different ambient media

Absorbing Rays

optrace ensures that rays not intersecting both lens surfaces get absorbed.

Generally, these rays are error cases. A ray only hitting one surfaces must enter/leave through the lens side cylinder, that is not handled in our sequential simulation. Rays not hitting the lens at all are typically undesired. In real optical systems they would be (hopefully) absorbed by the housing of the system.

Parameter no_pol

The raytracer provides the functionality to trace polarization directions. Thus, not only the polarization vector for the ray and ray segment can be calculated, but also the exact transmission at each surface transition. Unfortunately, the calculation is comparatively computationally intensive.

With the parameter no_pol=True no polarizations are calculated and a unpolarized/uniformly polarized light is assumed everywhere. Typically this speeds up the tracing by 10-30%. Whether you can neglect the influence of polarization depends on the exact setup of the geometry.

4.5.2. Tracing

Tracing the system

Tracing is with the Raytracer.trace() method of the Raytracer class. It takes the number of rays N as parameter. The method uses the current tracing geometry and stores the ray properties internally in a RayStorage object.

Example

Below you can find an example. A eye preset is loaded and flipped around the x-axis. A point source is added at the retina and the geometry is traced.

import optrace as ot

# init raytracer
RT = ot.Raytracer(outline=[-10, 10, -10, 10, -10, 60])

# load eye preset
eye = ot.presets.geometry.arizona_eye(pupil=3)

# flip, move and add it to the tracer
eye.flip()
eye.move_to([0, 0, 0])
RT.add(eye)

# create and add divergent point source
point = ot.Point()
RS = ot.RaySource(point, spectrum=ot.presets.light_spectrum.d50,
                  divergence="Isotropic", div_angle=5,
                  pos=[0, 0, 0])
RT.add(RS)

# trace
RT.trace(100000)

# access ray parameters, render images etc.
...

Accessing the Ray Properties

Described in Section 4.15.2.

Rendering Images

Described in Section 4.8.3.

Tracing with many rays

The number of rays is limited by the RAM usage. The RAM usage is by default limited by the Raytracer.MAX_RAY_STORAGE_RAM parameter, the actual number of rays results from the complexity of the geometry. Its default value is 8GB, but it can be set for each Raytracer object separately.

To generate images with even more rays, the method Raytracer.iterative_render can be applied, which traces the geometry iteratively without holding all rays in memory. More details are available in Section 4.8.4.

4.5.3. Modelling Diffraction

Image Blurring

Image blurring can be applied to rendered images to account for resolution limits. This process utilizes an Airy disk filter, as detailed in Section 4.8.7, to approximate the blurring effect. It is important to note that this method provides a very generalized approximation.

Ray Bending

optrace incorporates experimental support for Heisenberg Uncertainty Ray Bending (HURB). Further technical details regarding its implementation are available in Section 5.3. An example for experimentation with HURB is available in Section 1.9.

The current implementation of HURB has the following limitations:

  • HURB simulates the blurring associated with edge diffraction. It does not account for interference effects.

  • Deviations persist between theoretical and simulated beam profiles. For a detailed comparison, refer to Section 5.3.6.

  • Ray bending is currently limited to the inner aperture edges of RingSurface and SlitSurface types.

  • All apertures are modeled as diffracting elements.

  • The aperture stop must be explicitly defined as a surface within the optical setup.

Another issue is that bending leads to large angle rays. The use of image rendering with automatic extent is discouraged, as these rays lead to drastically increased automatically set sizes. Provide the image size manually, see Section 4.8.3.

Given these restrictions and the experimental status of the feature, HURB requires explicit activation. To enable HURB, set use_hurb=True during the raytracer initialization:

RT = ot.Raytracer(outline=[-2, 2, -3, 3, -5, 60], use_hurb=True)

A custom uncertainty scaling factor can be configured using the HURB_FACTOR attribute:

RT.HURB_FACTOR = 2.3

Additional information concerning this factor is provided in Section 5.3.5.