An Overview of LensLab

by Joe Gwinn

Introduction

Nomenclature

Objects

System

Components

Rays

Coordinates

Raytrace Sequence

Resonate

Next Intersection

Notes

Introduction

The purpose of this overview is to give the reader the big-picture view of how LensLab works–a picture sufficient to allow readers to get started with confidence; a picture that can be built upon later without requiring wholesale revision despite the simplifications necessary in an introductory overview; a picture that allows you to read the LensLab User's Guide with greater comprehension. LensLab is, in places, very different from all other optical design software packages, and having the salient differences and core assumptions pointed out can save much frustration. To a large degree, this overview contains what I would liked to have known when I first started to use the original Optica package in 1995.

Nomenclature

There are a number of words that are common technical terms as well as the names of Mathematica objects defined and used in LensLab. In this Overview, the names of such objects will be capitalized, appearing exactly as they appear in LensLab, and will always be followed by the word "object", "function", "attribute", or "rule" as appropriate, or by use of the relevant Mathematica syntax, particularly the square brackets "[ ]" after a function name or the right arrow "→" denoting a rule. Mathematica and LensLab objects will also be bolded. The first word of a sentence will be the common technical term unless specifically designated as a Mathematica or LensLab object.

Objects

The major objects of LensLab are the optical system, the optical components within that system, the rays going from component to component, and the rays piercing surfaces within those components. These are described in the following paragraphs.

System

The system contains a list of components in ray propagation direction order, the first component to be encountered being first in the list. This list may have no name, being the list of ray sources, components and Move functions seen within, for instance, a DrawSystem[] function.

Components

The specific components in a system are instantiations of one of a number of generic component templates. To get from generic to specific, the user provides the numerical values, while the generic template provides the general structure. Examples of generic component templates are lenses, mirrors, prisms, screens, and the like. Rays originate from a special kind of generic component called a Source. Some built-in ray source functions are PointOfRays, CircleOfRays, and ConeOfRays.

There is a hierarchy of components and component-grouping functions. The higher-level components are built up using lower-level components and grouping functions. Starting with the highest-level list, you have, for example, {Prism, PlanoConvexLens, Mirror}, {SphericalLens, LensSurface}, {ComponentFoundation, ComponentRendering, Refraction, Reflection, Resonate, Confine, Unconfine}, and, the most basic, {BuildComponent}. Items within a "{}" list are of equal level. Using for instance the Resonate function, one may combine an arbitrary group of components into a virtual component, sometimes called an aggregate. In this overview we will follow the convention of assuming, for simplicity, that the term component includes all such aggregates of components. Resonate in effect creates a component within which nonsequential raytracting is done.

Each component contains its own ordered list of surfaces belonging to that component. These lists are called "ConfinedPaths", as they enumerate all possible paths through the component. Within the components, where rays are confined, raytracing may be either sequential (the most common default), or nonsequential (sometimes by default, or as requested by the Resonate function). For simple components, the first surface listed is often the component's entrance, and the last surface is often the exit surface. In sequential raytracing within a component, the surfaces are intersected in strict surface list order. Nonsequential raytracing is described later.

Rays

Rays consist of one or more straight line segments joined at the points of intersection on surfaces, at which intersection points the direction of travel is abruptly changed (deflected) by refraction, reflection, and/or surface diffraction. Rays have attributes (expressed as Mathematica rules), such as wavelength, intensity, polarization, and cumulative optical pathlength, that travel with them as they go from component to component, surface to surface. Propagated attributes such as the Resonate attribute (used by the Resonate function) may also be carried along with a ray (in a Ray object), and are used to dynamically control various aspects of raytracing. Ray attributes such as RayStart (startpoint), RayTilt (direction), and RayEnd (endpoint) are given in world coordinates, defined later.

Rays between components are called "unconfined", while rays within components (or aggregates) are called "confined". The boundaries between unconfined and confined volumes are the "unconfined surfaces", which are the entry ports of the components. Because rays exit a component only when they fail to hit another internal surface, there are no exit ports per se. Between components, all raytracing is strictly sequential. Thus, unconfined rays are traced sequentially in strict component list order.

The defining characteristic of sequential raytracing is that you can determine the order of component and surface intersections in advance without firing a single ray through the system, simply by inspection of the component list and the LensLab functions used.

By contrast, in non-sequential raytracing, which is only possible within a component, the distances from the startpoint of the current ray segment to all surfaces within the component are computed. The closest surface in the direction of ray propagation is chosen, rather than depending on the explicit ordered list of surfaces provided in advance by the author of the component template.

Coordinates

In LensLab, the X-axis (not the Z-axis) is the default optical axis for optical systems, components within systems, and surfaces within components. This convention, while convenient in Mathematica, differs from that used in most other optical design programs.

Within an optical system, Components and Rays are located using the system's "world" coordinate system. Each surface within a component has its own local coordinate system and carries a three-dimensional rotation matrix plus a three-dimensional translation vector to transform a point or ray from the world coordinate system into the surface's local coordinate system, and back. This world-to-surface transform (matrix plus vector) is derived by combining the world-to-component and component-to-surface transforms during component initialization.

For full generality, surfaces are represented within LensLab parametrically, where the local coordinates x, y, and z are given as three pure functions of the traditional parametric arguments s and t. The only restrictions on these surface functions are that they must be continuous (except for a finite number of point or line discontinuities), and that all first partial derivatives (that is, the Jacobian) must be continuous for that part of the surface through which rays will pass.

Raytrace Sequence

A ray travelling between components encounters a component, whereupon the ray is transformed from world coordinates into the local coordinates of the first (entry) surface. The point of intersection between incident ray and surface (the new startpoint) and the surface normal at that point of intersection are then computed, all in the first surface's local coordinate system. The new startpoint and normal are then transformed back into the world coordinate system. The new ray direction is computed directly in world coordinates using this new startpoint, the surface normal at that point, and the incident ray's direction, all being in world coordinates; plus the relevant physical law of reflection, refraction, or surface diffraction. The next surface is found by the same process by which the first surface was found, and the process continues until the last surface of the last component is pierced. Rays that fail to hit any surface or other component are simply reduced to zero length, appearing as a dot on the last surface intersected, unless a Boundary function is defined, in which case the rays instead terminate on the boundary box.

Deflections, which are implemented as Mathematica rules carried within each Component object specify which ray to use as input and where the output ray or rays will go. Deflection also denotes which physics law (reflection, refraction, surface diffraction) to use with the specified surface shape to determine the direction of the new (output) ray or rays, the new startpoint having been determined from the surface function. The actual surface shape is specified in a Surfaces rule, which is also carried within each Component object. This approach is used because the geometry of surfaces is independent of the physics of deflection.

If FresnelReflections→True, refracting surfaces will have two rays (one reflected and one refracted) emerging for each incoming ray. Otherwise, only the refracted ray emerges; this is used to model simple lenses. For total internal reflection or reflecting surfaces, only the reflected ray emerges. Certain kinds of components, such as diffraction gratings and diffusors, can have any number of rays emerging per incident ray.

Despite appearances, the resulting "tree of rays" isn't formally a tree. Each ray is an independent line segment with startpoint, endpoint, and the line between, but these line segments do not logically depend on each other. When plotted, they appear to form trees because the endpoint of one physically coincides with the startpoint of another, but these line segments do not depend on their parents or children. LensLab remembers that they were once parent and child, and also the identity of the founding parent ray (which has no parent), but LensLab no longer makes direct use of these facts, except for a few specialized functions like FindFocus and for wave-front analysis applications.

The total size of ray trees is controlled by dropping rays that either fall below a specified threshold intensity, or that have been deflected too many times.

Resonate

It is passage of a ray through an unconfined surface that switches the ray's Resonate attribute to True (on component entry). It later switches back to False on component exit. The net effect of this is that the ray (which starts out with Resonate→False) is traced sequentially until it pierces the entrance surface of the first component, at which point the Resonate attribute of the ray may or may not be switched on. If not, surfaces within the component are done in surface list order until the ray escapes the component, and the process continues from component to component until done. If Resonate→True at entry, the ray rattles around non-sequentially from surface to surface within the component until the ray fails to hit any of the component's surfaces, at which point sequential raytracing resumes with the next component in the system's component list, continuing until the ray's intensity falls below a specified threshold, the ray exits the system, or the specified maximum number of deflections is exceeded.

In short, raytracing is either strictly sequential, or alternates between sequential and non-sequential, starting and ending with sequential.

Next Intersection

In nonsequential raytracing, with QuickSurfaceSort→True, the raytrace engine will search the component's entire surface list and choose the surface that has the closest aperture centerpoint. With QuickSurfaceSort→False (the new default), the raytrace engine will search the surface list and choose the surface with the closest valid ray-surface intersection point. The advantage is that testing centerpoints is much faster than checking intersection points because one avoids computation of the actual intersection point, which is expensive. The risk is that the aperture centerpoint is not often the intersection point, so the raytrace engine may choose the wrong surface if the geometry is sufficiently unfavorable.

The effect of the raytrace engine choosing a wrong surface is that at least one other surface is wrongly ignored. The hallmarks of a missed surface are that rays that should have been deflected instead just pass through the surface undeviated, and that the normal intersection-point dots are missing.

Traditionally, ray-surface intersections were found by numerical iteration of an open-form system of implicit equations, using the Mathematica FindRoot function. This method is robust, but relatively slow. For increased raytracing speed, LensLab uses, where possible, direct solutions of "conic" surface equations where exact closed-form formulas to compute the ray-surface intersection point exist. These conics reside in ModelSurfaceShape functions. The corresponding closed-form formulas are called symbolic in LensLab to distinguish them from the open-form equations. For surfaces that are not directly expressible as such a conic, LensLab approximates the actual surface with either a plane or a triangular net of planar segments for which a closed form intersection formula always exists, and uses the approximating planar pieces for initial intersection detection calculations. Then, when a surface is chosen, the intersection with the closest approximating plane or planar segment is used as the starting point for an iterative numerical search to find the exact point of intersection with the surface.

Some examples of the directly-solvable conics used by LensLab are parabolas, ellipsoids, spheres, planes, and cylinders. There exist other conic and non-conic surfaces for which closed-form intersection formulas exist, but the listed surfaces are those currently used by LensLab. Closed-form intersection formulas are not available for toroidal surfaces, so LensLab always solves them iteratively. A simple interface allows you to add more symbolic surface shapes.

Notes:

N1. The box drawn by DrawSystem/AnalyzeSystem with Boxed→True has no meaning in LensLab and should not be confused with the LensLab Boundary function, which allows any set of components to be enclosed within an axis-aligned bounding box; any rays that intersect this Boundary box are absorbed. ClearBoundary boxes allow rays to pass through unabsorbed. Without a Boundary box, rays failing to hit any component of the system are reduced to zero length at the last surface they intersected.

N2. Given the basic design of LensLab, which depends on geometric optics in homogeneous media, components where the rays within are not piecewise linear straight lines must be handled as black boxes, which is to say that such components are treated as if they had no internal structure. Rays go in and rays come out, but no surface-by-surface raytracing is done within a black-box component. For example, volume holograms are handled as black-box components because geometric optics cannot adequately handle the physics of volume diffraction.

N3. The optical distance is the physical (geometric) distance multiplied by the refractive index of the medium through which the light is passing. Although usually expressed in length units (typically millimeters), the optical distance is proportional to the total number of optical wavelengths in the path. More wavelengths will fit in one centimeter of glass than in one centimeter of air, and the ratio of wavelengths in glass to wavelengths in air is precisely the refractive index of the glass relative to air, regardless of the specific wavelength of light used.

LensLab assumes homogeneous media, so finding the closest surface can be done with geometric distances, rather than the somewhat more expensive to compute optical distances, because a ray segment is always entirely within a single media and is going in a specific constant direction.

Created by Mathematica (November 3, 2005)