4.15. Advanced Topics

4.15.1. Raytracing Errors

The raytracer emits warnings and errors while simulating. Internally these are characterized by flags, which can be found below:

Table 4.24 List of raytracing messages

Flag

Description

Raytracer.INFOS.ABSORB_MISSING

Rays are absorbed because they miss a lens surface.

Raytracer.INFOS.TIR

Total inner reflection. Reflections are not simulated, so the ray is treated as being absorbed at the surface intersection

Raytracer.INFOS.T_BELOW_TTH

A filter transmittivity is below the T_TH threshold of the raytracer and the ray is therefore absorbed. Avoids ‘ghost rays’ that need to be traced, but don’t contribute to an image.

Raytracer.INFOS.ILL_COND

Ill-conditioned rays for hit finding of a numerical, custom surface. In almost all cases the intersection will be wrong. This can happen if the surface is badly defined numerically or geometrically, there are surface collisions or a ray hits the surface multiple times. Please check the geometry of the raytracer and the surface definition.

Raytracer.INFOS.OUTLINE_INTERSECTION

Rays that would leave the outline are absorbed at the outline intersection

The raytracer also provides a Raytracer.geometry_error flag that gets set when tracing was aborted due to issues with the geometry. Geometry checks are only executed while tracing, so Raytracer.trace must be called first.

When using the GUI, a subset of surface collision points will also be displayed in the geometry.

4.15.2. Accessing Ray Properties

4.15.2.1. Overview

Definitions:

Ray Section: Part of the ray with a constant direction vector. Sections typically start and end at surface intersections.
Ray: Entirety of the ray going from the source to its absorption
N: number of rays
Nt: number of sections per ray, equal for all rays

The number of sections is the same for all rays. If a ray gets absorbed early, all consecutive sections consist of zero length vectors starting at the last position and having their power set to zero. Direction and polarization are then undefined.

The following table shows an overview of ray properties. Some of those are attributes that are stored while tracing, while others are functions, as these properties must be calculated from other attributes first. They are intentionally kept as functions and are not exposed as properties, so an computational overhead is communicated to the user.

Table 4.25 List of ray properties

Quantity

Method/Attribute

Type

Unit

Function

Position

p_list

numpy.ndarray of type numpy.float64 of shape (N, Nt, 3)

mm

3D starting position for all ray sections

Direction vectors

direction_vectors()

numpy.ndarray of type numpy.float64 of shape (N, Nt, 3)

-

normalized (with normalize=True) or unnormalized direction vectors for each ray section

Section lengths

ray_lengths()

numpy.ndarray of type numpy.float64 of shape (N, Nt)

-

geometrical length of each ray section

Optical section lengths

optical_lengths()

numpy.ndarray of type numpy.float64 of shape (N, Nt)

-

optical length of each ray section (geometrical length multiplied by refractive index)

Polarization

pol_list

numpy.ndarray of type numpy.float32 of shape (N, Nt, 3)

-

unity 3D polarization vector

Power

w_list

numpy.ndarray of type numpy.float32 of shape (N, Nt)

W

ray power

Refractive Index

n_list

numpy.ndarray of type numpy.float64 of shape (N, Nt)

-

refractive indices for all ray sections

Wavelength

wl_list

numpy.ndarray of type numpy.float32 of shape N

nm

wavelength of the ray

4.15.2.2. Direct Access

After raytracing, the ray storage is accessible as attribute of the Raytracer. Value are accessed by typical numpy array indexing or slicing. See the table above for the variable names and dimensions. Number of rays and sections per ray is accessible through Raytracer.rays.N and Raytracer.rays.nt.

Let’s create an example geometry:

# create raytracer
RT = ot.Raytracer(outline=[-15, 15, -15, 15, -15, 30])

# add RaySource
RSS = ot.CircularSurface(r=2)
RS = ot.RaySource(RSS, pos=[0, 0, -10])
RT.add(RS)

# load LeGrand Eye model
eye = ot.presets.geometry.legrand_eye()
RT.add(eye)

# trace
RT.trace(100000)

To access positions of the third ray section write:

RT.rays.p_list[:, 2, :]

To access the wavelength of the tenth ray:

RT.rays.wl_list[9]

Access the position z-component of all sections of the twenty-third to twenty-sixth ray by writing:

RT.rays.p_list[22:25, :, 2]

Access the ray section lengths for the fourth section:

RT.rays.ray_lengths()[:, 3]

4.15.2.3. Masking

For more control over accessing ray properties masking methods of the RayStorage class can be applied. A call of rays_by_mask without parameters is:

RT.rays.rays_by_mask()

… and returns a tuple of position, direction, polarization, weights, wavelengths, source number, refractive index.

Providing a boolean array as first parameter applies masks to all these elements:

mask = np.array([0, 1, 0, 1, ...], dtype=bool)
RT.rays.rays_by_mask(mask)

Providing an additional array of integers also selects the ray sections:

mask = np.array([0, 1, 0, 1, ...], dtype=bool)
sec = np.array([3, 0, 5, 1, 1, 2, ...])
RT.rays.rays_by_mask(mask, sec)

By default, ray direction vectors are normalized, if this isn’t needed, you can provide normalize=False:

mask = np.array([0, 1, 0, 1, ...], dtype=bool)
sec = np.array([3, 0, 5, 1, 1, 2, ...])
RT.rays.rays_by_mask(mask, sec, normalize=False)

You can restrict the properties by setting the ret parameter. All unneeded parameters are not calculated and set to None, speeding things up. This parameter is a seven element bool list that marks all needed properties:

ret = [False, True, False, True, True, True, True]
RT.rays.rays_by_mask(ret=ret)

The function still returns a tuple of 7 elements, but undesired elements have value None instead of an array. See the code reference of rays_by_mask for more detail.

4.15.3. Object Descriptions

Child classes of BaseClass include parameters desc, long_desc. The former should be a short descriptive string and the latter a more verbose one. These descriptions can be user provided and are used in for plotting and text output.

4.15.4. Modifying Initialized Objects

To avoid issues and hard-to-debug problems, some objects are locked after initialization. This means object properties can not be changed or assigned. Some objects include specific methods to change their properties after initialization. For instance, changing a lens surface leads to a change of the lens, which in turn can lead to changes in the lens group or raytracer. Such changes should only be applicable through specific functions that update everything accordingly in a defined way.

Locked objects/properties include:

  • all surface types as well as lines and points

  • positions of geometrical objects (lens, detector, …) (but these are assignable through a function)

  • surface assignment (but accessible through specific functions)

  • properties of rendered rays

  • a calculated ray transfer analysis object (TMA)

The list of traced rays is read-only, since their properties should only be assigned by the simulation itself.

4.15.5. Color Conversions

Color conversion are supported via the namespace optrace.color. optrace provides conversions for the colorspaces XYZ, sRGB, linear SRGB, CIELUV and xyY as well as some color properties like Saturation and Hue in CIELUV.

Check the Color Handling section for a technical and fundamental descriptions of color processing and calculation. Go to the code reference section optrace.tracer.color for information on the usage of implemented functions.

For the sRGB Perceptual Rendering Intent there a extra parameters available. For instance, a fixed saturation scaling can be set using the chroma_scale parameter of the optrace.tracer.color.xyz_to_srgb_linear function. A suitable scaling factor can be calculated using optrace.tracer.color.get_saturation_scale. This is useful for viable comparison between images, as the saturation scaling factor is the same. The function optrace.tracer.color.xyz_to_srgb_linear provides the chroma_scale parameter to override the best matching one. Alternatively, a relative lightness threshold can be set using the L_th parameter, which excludes colors of darker image regions to calculate/apply the factor in both functions. This is helpful when the scaling factor is largely affected by color values that are mostly invisible. If there still colors outside the gamut after the operation (for instance, because they were below L_th or the user set chroma_scale value was insufficient), they are projected onto the gamut edge as for the absolute rendering intent. See the docstring of both functions for further information.