4.15. Advanced Topics

4.15.1. Raytracing Errors

The raytracer emits relevant warnings and errors while simulating the geometry. These are internally characterized by flags, which are listed in the following table:

Table 4.24 List of raytracing flags and messages

Flag

Description

Raytracer.INFOS.ABSORB_MISSING

Rays are absorbed at the current lens surface because they missed it.

Raytracer.INFOS.TIR

Total inner reflection. As reflections are not simulated, the ray is treated as being absorbed at the refracting surface.

Raytracer.INFOS.ILL_COND

Ill-conditioned rays for intersection calculation at a numerical, custom surface. In almost all cases the resulting intersection would 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 have left the defined geometry outline are absorbed at the affected outline plane.

The raytracer also provides a Raytracer.geometry_error flag that is set when tracing was aborted due to a critical geometry issue. Geometry checks are only performed while tracing, so Raytracer.trace must be called first.

In the case of surface collisions, a subset of collision points will be displayed in the 3D view inside the GUI.

4.15.2. Accessing Ray Properties

4.15.2.1. Overview

Definitions:

Ray Section

part of a ray with a constant direction vector. Sections 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 identical for all rays. If a ray is absorbed early on, all consecutive sections consist of zero length vectors starting at the last position and having their power set to zero. Direction and polarization are undefined.

The following table shows an overview of available ray properties. Some of those are attributes that are stored while tracing, while others are functions, which must be calculated from other attributes first.

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)

mm

geometrical length of each ray section

Optical section lengths

optical_lengths()

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

mm

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)

-

section-wise refractive index of the material at the ray’s wavelength

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 RayStorage is accessible as rays attribute of the Raytracer. Value are accessed by typical numpy array indexing or slicing. See the table above for the variable names and dimensions. The number of rays and sections per ray is accessible through Raytrace.rays.N and Raytrace.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 of all rays write:

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

To access the wavelength of the tenth ray only:

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 of each ray:

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

4.15.2.3. Masking

A masking method of the RayStorage class can be applied for more control over accessing ray properties. This method provides an easy interface for most relevant ray properties, while also improving performance, as only the desired components are calculated. A call of rays_by_mask without parameters returns a tuple of position, direction, polarization, weights, wavelengths, source number, refractive index of all rays and ray sections.

pos, dirs, pols, powers, wls, snum, n = RT.rays.rays_by_mask()

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 masks 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 required, 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 returned properties by setting the ret parameter. All undesired parameters are not calculated, improving the overall performance. The function still returns a tuple of 7 elements, but undesired elements are set to None. The parameter is a seven element bool list, where all needed properties are marked with True:

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

See the code reference of rays_by_mask for more details.

4.15.3. Object Descriptions

Child classes of BaseClass supply parameters desc, long_desc. The former is used as short descriptive string, while the latter is a more verbose one. These descriptions are used for printing geometry information and labelling elements in the 2D or 3D plots.

4.15.4. Modifying Initialized Objects

Most objects are locked after initialization to avoid issues and hard-to-debug problems. After locking, object properties can not be changed without specially exposed functions for exactly this purpose. For instance, changing a lens surface leads to a change of the whole lens geometry, 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 in a correct and defined way.

Locked objects/properties include:

  • all surface types as well as lines and points

  • positions of geometrical objects (lens, detector, …) (specific functions available)

  • surface assignment of objects (specific functions available)

  • properties of simulated rays

  • a calculated ray transfer analysis object (TMA)

4.15.5. Color Conversions

Color conversion are supported via the namespace optrace.tracer.color. optrace provides conversions for the colorspaces XYZ, sRGB, linear SRGB, CIELUV and xyY as well as specific color properties such as saturation and hue in CIELUV.

Check the Color Management section for a technical descriptions on the color processing. API reference section optrace.tracer.color provides information on the usage of the implemented functions.

The sRGB Perceptual Rendering Intent function allows for extra parameters compared to the other rendering intents. For instance, a fixed chroma scaling factor is set by the chroma_scale parameter of the optrace.tracer.color.xyz_to_srgb_linear function. Such a fixed factor is useful for image comparisons. A suitable scaling factor can be calculated using optrace.tracer.color.get_chroma_scale. A relative lightness threshold can be set using the L_th parameter, which excludes darker, mostly invisible colors in the chroma scale calculation. After chroma scaling, colors remaining outside the gamut are projected onto the gamut edge, same as for the absolute rendering intent. See the docstring of both functions for further information.