3. Ray Tracing in Detail

3.1 Introduction

3.2 Sequential Tracing and Light Path

3.2.1 Light Source Ordering Example
3.2.2 Component Ording Example

3.3 Nonsequential Tracing and Resonate

3.3.1 Three-mirror Resonator
3.3.2 A Subtle Substitute for Resonate

3.4 Rays and Surfaces

3.4.1 How Rayica Generates Rays

3.4.2 Component Surfaces

Faces and Edges
Rolling Your Own Edge
Thick Components and Internal Reflections

3.5 Special Considerations with PropagateSystem

3.5.1 QuickSurfaceSort
3.5.2 Confine
3.5.3 Unconfine
3.5.4 Modular Systems

3.6 TurboTrace

3.6.1 Basics of TurboTrace

How to Enter Symbolic Parameters in Rayica

3.6.2 Lens Optimization
3.6.3 ConstructMeritFunction

Merit based on summation of optical path differences
Calculation of the beam centroid coordinates
Intensity-weighted beam-coordinate calculation
Rayica's built-in temporary variables

3.6.4 Wavefront Analysis

Two-Dimensional Analysis with Interpolation
Three-Dimensional Analysis with Zernike Polynomials

3.1 Introduction

Certain details of Rayica's ray tracing methods commend themselves to study.  By knowing which methods to employ when, you can optimize both your time and Rayica's results.  By knowing how Rayica performs ray-tracing, you can avoid various pitfalls associated with specific types of optical systems.

A large number of optical systems do not demand such fine-tuning.  Rayica handles them well using default methods.  Other optical systems, however, require more careful thought and some knowledge of Rayica's internal workings.

Most optical systems are sequential in nature.  An entrance aperture accepts incoming light, which then propagates from one element to the next, finally emerging from an exit aperture or terminating at an image plane.  A distinguishing feature of such systems is that a given ray intercepts a given surface only once.  All that need concern you about these systems is the order in which components are listed.  This order should match the intended light path of the system.

Other systems do not behave in this straightforward manner.  A ray can intercept the same surface more than once.  For example, a laser cavity involves two opposing mirrors.  The same ray may bounce between the mirrors many times before emerging from the laser.  In an optical fiber, the same ray may bounce off the inner surface many times before finally emerging from the end.

These systems require special handling.  Rayica makes a fundamental distinction between sequential and nonsequential systems.  This chapter discusses how and when Rayica invokes nonsequential ray-tracing.  It also covers the speed optimizations possible in Rayica, including TurboTrace.  These optimizations depend heavily upon the nature of the ray trace.  Speed is critically important for lens optimization problems.

Another category of systems that merit study of Rayica's ray-tracing behavior are modular optical systems.  Such systems facilitate design reuse.  You can precompute the fixed parts of a system in order to accelerate design iterations on the variable part.

Sometimes a component presents many faces to the external environment, such that light can enter from any of several bounding surfaces; only one of these surface illuminations has any bearing on the analysis.  Light entering through other surfaces is irrelevant to the design.  This situation also benefits from a knowledge of Rayica's ray-tracing details.  You can restrict Rayica's analysis to the entrance surface of interest.

None of these scenarios presents any difficulty to Rayica, but all of them require a measure of forethought tied to an understanding of the Rayica features that handle them.

Go to list of topics

3.2 Sequential Tracing and Light Path

Sequential ray-tracing is the default mode in Rayica.  The defining characteristic of this mode is that Rayica determines the order of surface intersections in advance, by inspection of input, without respect to physical geometry.  The system listing given by the user input fixes the order of intersection tests.  A given element may only transmit rays to components following it in the list.  Rayica checks the possible intersections in order, and does not backtrack.  Sequential ray-tracing is the most efficient mode of operation for this very reason.  Speed is the primary reason for using it.

3.2.1 Light Source Ordering Example

Sequential tracing demands that your system enumeration matches the light path through the system.  You must order the elements of your system in correct sequence.  Otherwise you will not obtain a correct trace.

Here is a correct system with two sources and two lenses.  The system elements appear in proper order for sequential ray tracing.  The sources precede the lenses, and the lenses appear in light-path order. Each beam refracts through each lens.

In[2]:=

greenSource = WedgeOfRays[15, NumberOfRays7, BirthPoint {-50, 10, 0}, RayLineR ... 371;Boundary[100, GraphicDesignOff] }, PlotTypeTopView] ;

[Graphics:HTMLFiles/index_2.gif]

Sequential tracing works correctly when the system list is properly ordered.

A seemingly minor alteration to the previous input will cause the sequential trace to fail.  If redSource follows L1 in the system enumeration, then redSource rays will ignore L1.  Only components listed after redSource can affect its rays.  You are not changing physical geometry here, merely reordering input.  The system list contains exactly the same elements, all in exactly the same geometric positions, the order of the Mathematica input has changed.

In[5]:=

greenSource = WedgeOfRays[15, NumberOfRays7, BirthPoint {-50, 10, 0}, RayLineR ... 371;Boundary[100, GraphicDesignOff] }, PlotTypeTopView] ;

[Graphics:HTMLFiles/index_4.gif]

L1 now fails to refract the lower beam from redSource because the system list does not follow light-path order.

The lower beam from redSource acts as though L1 does not exist.  The beam refracts only through lens L2.  The lenses have not moved, and the beam's point of origin has not changed, but its computed light path has changed.  This trace is therefore incorrect.

Go to list of topics

3.2.2 Component Ordering Example

Components too must appear in proper order for sequential ray tracing.  The system below has one source and four components, including an invisible Boundary.

In[8]:=

Off[General :: spell, General :: spell1] ; source = WedgeOfRays[15, NumberOfRays7, Bir ... 2371;prism, mirror, boundary}, PlotTypeTopView] ;

[Graphics:HTMLFiles/index_6.gif]

The prism functions correctly when the system list order mimics the light path.

Pay particular attention to the prism.  The only system elements that can transmit rays to the prism are those preceding it in the system list.  Observe what happens when you list mirror before prism.  The same elements still precede prism, but with the addition of mirror to the set.

In[15]:=

AnalyzeSystem[ {source, lens, mirror, (* moved ! *)prism, boundary}, PlotTypeTopView] ;

[Graphics:HTMLFiles/index_8.gif]

Putting mirror ahead of prism causes the sequential trace to fail.  The prism does not refract the beam.

The prism no longer affects the rays.  The trace would be identical if the prism was removed altogether.  The same elements still precede prism in the system enumeration, but the trace has failed.  Why?

The trouble arises from sequential processing.  In this kind of tracing, Rayica considers the system enumeration as a checklist of candidates for intersection or ray generation.  There is no backtracking.  Once Rayica reaches a certain point in the checklist, it does not return to previous items.

In this case the source is the first element, so Rayica generates its rays.  Rayica is then finished with source and checks it off the list.  The candidates are now, in order, {lens, mirror, prism, boundary}.  Rayica checks for intersections with lens, finds them, and checks lens off the list.  Now the remaining candidates are, in order, {mirror, prism, boundary}.  Rayica checks for intersections with mirror, finds them, and checks mirror off the list.  Rayica fails here to consider prism as a candidate.  This failure is an artifact of sequential processing.  From a processing viewpoint, the mirror is first in line, and therefore has priority.

Now the rays have physically bypassed prism and bounced off mirror.  Rayica has only {prism, boundary} remaining as candidates to the list.  The rays from mirror do not intersect prism, so Rayica checks prism off the list.  This action leaves only {boundary} on the list.  The rays from mirror do indeed intersect boundary.  Rayica finds their intersection and checks boundary off the list.  Now the checklist is empty, so Rayica terminates the trace.  (You generally always want a Boundary object as the last element in your system list.)

There are two ways to repair this failed trace.  One is to correct the system order to follow the physical light path.  Yet in many systems, the light path is more complicated than this simple example suggests.  A more general solution is Rayica's Resonate function.  Resonate instructs Rayica to perform nonsequential ray tracing for the set of components that are supplied as arguments.  In this mode, all surfaces are candidates for intersection at all times.

We wrap Resonate around the system components without changing their order.  Resonate accepts a list of components as its argument.  The argument here is the list {lens, mirror, prism}.

In[16]:=

AnalyzeSystem[ {source, Resonate[{lens, mirror, prism}], boundary}, PlotTypeTopView] ;

[Graphics:HTMLFiles/index_10.gif]

The Rayica function Resonate enforces nonsequential ray tracing.  The trace is now correct.

Components inside the list argument to Resonate may appear in any order.  The Resonate function effectively becomes just another Component object in your system list.  The trace is sequential before and after the Resonate component.  Inside the Resonate component, it is nonsequential.

Go to list of topics

3.3 Nonsequential Tracing and Resonate

Sequential ray-tracing cannot handle all possible systems.  An important category of systems not amenable to sequential processing is that of resonating cavities.  A general resonating cavity is any component or system that confines light rays temporarily or indefinitely, not transmitting them directly.  This definition encompasses laser cavities, optical fibers, integrating spheres, and so on.  Such components and systems cause light to wander within their boundaries prior to escape.

More specifically, from the viewpoint of Rayica, a resonating cavity is any component or system for which it is possible that the same ray may hit the same surface (a) more than once or (b) from more than one other surface.  Proper analysis of a resonating cavity requires nonsequential ray tracing.  You instruct Rayica to apply nonsequential methods by means of the Resonate function.  This function creates a new Component object out of the constituents of the cavity system.

3.3.1 Three-mirror Resonator

The following mirror cavity demonstrates the need for the Resonate function.  First we trace the system without calling Resonate.  The first attempt will be a sequential ray trace.  The source is a SingleRay.  Note that the mirrors have no thickness.  In Rayica terms, each of them is a single surface, not a double surface.

In[17]:=

mirror1 = MoveReflected[SphericalMirror[-300, 100, "M1"], {300, 0}, {150, 300}, {0,  ... r2, splitter, boundary}] ; ShowSystem[ringSys, PlotTypeTopView] ;

[Graphics:HTMLFiles/index_12.gif]

[Graphics:HTMLFiles/index_13.gif]

Three mirrors in a ring arrangement constitute a resonating cavity.  The trace fails in this case because Resonate was not used.

Instead of circulating between the mirrors, the ray terminated at the Boundary.  Not instructed otherwise, Rayica assumed that the trace was sequential.  The splitter was the first element encountered.  One transmissive and one reflective ray were created at the splitter.  Rayica then searched the system list for candidate components which might intersect these secondary rays.  The only component appearing in order after splitter was Boundary.  Rayica therefore intersected the two secondary rays with Boundary and terminated the trace.  Physically, the reflected secondary ray should have struck M1.  However owing to its position in the system list, M1 was no longer a candidate for intersection.

A natural temptation would be to reorder the system list to place M1 in sequence after splitter.

In[24]:=

ringSys = AnalyzeSystem[{source, splitter, mirror1, mirror2, boundary}] ; ShowSystem[ringSys, PlotTypeTopView] ;

[Graphics:HTMLFiles/index_15.gif]

[Graphics:HTMLFiles/index_16.gif]

The sequential ray trace does a little better with a revised system order.  However this trace is still incorrect because there should be a second encounter with the beam splitter.

The reordering has helped somewhat.  The ray correctly bounces off all three components.  The problem is that the ray should encounter the beam splitter a second time after its reflection from M2.  However it does not because this is still a sequential trace, and Rayica considers the trace complete once the system list is exhausted.  After M2, all that is left in the system list is boundary.  Rayica therefore intercepts the ray from M2 at the boundary and terminates the trace.

What you need is a way to ask Rayica to trace nonsequentially.  That is, Rayica should not ignore a component just because it was already hit once or appears in a certain input order.  The Resonate function causes Rayica to treat its arguments as a single Component with the property of a resonating cavity.

In[26]:=

ringSys = AnalyzeSystem[{source, Resonate[{splitter, mirror1,  ... 71;mirror2}], boundary}] ; ShowSystem[ringSys, PlotTypeTopView] ;

[Graphics:HTMLFiles/index_18.gif]

[Graphics:HTMLFiles/index_19.gif]

Now we have a true resonating cavity by means of Resonate.  This trace is correct.

Resonate properly handles this cavity.  Rays circulate within the cavity and occasionally make their way out through the beam splitter.

Any time you ask Rayica to trace a resonating system, there is a question of when to terminate the trace.  In principle it could go on forever.  Rayica creates a fresh Ray object at each new intersection.  Some of the Ray objects do leave the cavity through the splitter.  However, many more are simultaneously created as reflected rays.  These could potentially circulate endlessly.

There are two options used by Rayica to regulate such traces: GenerationLimit and ThresholdIntensity.

One way that Rayica terminates such a trace is by restricting the number of Ray creation events.  The maximum number is given by GenerationLimit, an option to AnalyzeSystem.  The default value of the limit is moderately large, but it can be set to any value.

In[28]:=

ringSys = AnalyzeSystem[{source, Resonate[{splitter, mirror1,  ... 1;boundary}, GenerationLimit  7] ; ShowSystem[ringSys, PlotTypeTopView] ;

[Graphics:HTMLFiles/index_21.gif]

[Graphics:HTMLFiles/index_22.gif]

The effect of lowering the ray GenerationLimit for the resonating cavity.

A second way that Rayica terminates such a trace is by monitoring the Intensity values of the circulating rays.  The minimum Intensity value permitted for a Ray is given by ThresholdIntensity, also an option to AnalyzeSystem.  For lossy systems, such as this cavity example, this provides a more natural ending to the trace.

Go to list of topics

3.3.2 A Subtle Substitute for Resonate

Sometimes you need Resonate even for mundane geometries.  If the light path backtracks on itself, you generally need Resonate.  However a subtle trick can allow you to trace some of these systems sequentially.  This trick accomplishes the same results as Resonate but without the speed penalty.  It requires careful analysis of the light path.  The idea is to include the same component multiple times in your system list.

Here is a system of one source and three components traced sequentially (without Resonate).  Sequential tracing cannot correctly analyze this system as given.

source = Move[LineOfRays[45, NumberOfRays6], 5] ; lens = Move[BiConvexLens[100, 50, 10 ... urce, lens, mirror, boundary}, PlotTypeTopView] ;

[Graphics:HTMLFiles/index_24.gif]

Even a simple geometry may demand nonsequential ray tracing.  The reflected rays in this sequential trace do not refract in a backward direction.

The trace has failed.  The reflected rays should have refracted through the lens a second time after reversing direction at the mirror.  Instead, the rays ignored the lens after the reflection.  We now call Resonate to advise Rayica that the system includes a nonsequential section.  This will repair the trace, but at the cost of slower execution associated with nonsequential tracing.

In[8]:=

AnalyzeSystem[ {source, Resonate[{lens, mirror}], boundary}, PlotTypeTopView] ;

[Graphics:HTMLFiles/index_26.gif]

Nonsequential tracing with Resonate handles this case properly.  Nonsequential tracing, however, incurs a speed penalty.

Resonate is generally safe under all circumstances; it has correctly traced this system.  However, it has also increased the execution time.  With a little care, we can attain the same results using sequential ray-tracing.  The light path crosses the lens two times.  Therefore, sequential tracing will work if we enter the lens two times in the system list.  One lens entry comes before mirror, and the other comes after mirror.

AnalyzeSystem[ {source, lens, mirror, lens, boundary}, PlotTypeTopView] ;

[Graphics:HTMLFiles/index_28.gif]

For speed optimization, you can trace resonating systems sequentially if you pay careful attention to the light path and system list.

This trick is rather subtle and not recommended until you are well acquainted with Rayica.  It requires a careful, accurate assessment of the light path for your system, and fastidious attention to the order of your system list.  By contrast, Resonate accepts components in any order.

Go to list of topics

3.4 Rays and Surfaces

Rayica records information about ray traces by means of Ray objects.  Rayica creates these objects dynamically to monitor the progress of the trace.  When the trace is complete, Ray objects serve as data repositories for ReadRays, the main function for system examination.  Ray generation also has ramifications for modular systems, discussed in Section 3.6.

Surfaces constitute a major topic in Rayica.  Certain issues surrounding component surfaces may influence your ray-tracing results.  Moreover, you can customize Rayica's behavior with various surface-related function calls.

3.4.1 How Rayica Generates Rays

Any ray trace spawns a certain number of Ray objects.  Each Source generates its particular set of Ray objects.  These propagate to the surfaces of various Component objects in the system.  At every intersection of a Ray with a Component, Rayica determines what action the Component has on the light.  Common interactions include reflection, refraction, beam splitting, and absorption.

Rayica embeds intercept data within the Ray object.  This data includes angle of incidence and point of intercept.  However, recording the entire optical action of a Component necessitates more software activity than a mere alteration of one Ray.  A solitary Ray cannot embody all of the relevant information.  Among other issues, Ray can only record a single straight path, not a complete, piecewise-linear path.  Rayica therefore creates multiple Ray objects to capture the total optical effect.

When Ray intersects a PlanoConvexLens, for example, Rayica registers the intercept location inside Ray, then creates another Ray to follow a refracted (but straight) path through the lens.  This second Ray encounters the back surface of the lens, where it refracts in turn.  Rayica registers its intercept location and creates a third Ray that leaves the lens.  So three distinct Ray objects record the overall light path.  This case is illustrated below.

In[30]:=

AnalyzeSystem[ {Move[SingleRay[], {-50, 0}, 15], PlanoConvexLens[80, 5 ... pView, ShowTextIntersectionNumber, DefaultFont {"Times", 14} ] ;

[Graphics:HTMLFiles/index_30.gif]

Rayica creates Ray objects 2 and 3 to record the optical effect of lens L on Ray number 1.

Two Rays were created where one existed before.

When Ray intercepts a Mirror, the result is similar.  Rayica registers the intercept and creates a reflected Ray to follow the new light path.  There are two Ray objects in this case.  A half-silvered mirror generates both a reflected and a transmitted Ray.

Go to list of topics

3.4.2 Component Surfaces

A primary task of Component objects is to maintain information about surfaces.  In general, you may view Component as a set of surfaces.  Component can have anywhere from one to dozens of surfaces.  The surfaces may be physically internal or external to the component.  Each surface has a parametric surface description, a surface normal function, and various other properties.

Surfaces within Component are numbered.  The number of a surface is called its SurfaceNumber.  There is no special meaning to surface numbers beyond their mutual uniqueness within Component.  Most Rayica components have a canonical surface ordering which assumes some default placement of the component.  This numbering system is merely convention; it has no effect on ray tracing.  Rays may enter through any external surface, whatever its number.

The reason to be concerned about surface numbers is for extraction of post-trace data.  When Ray intersects a surface, Rayica records certain identifiers into Ray that pertain to the intersection.  These include ComponentNumber, for the particular component within the system, and SurfaceNumber, for the particular surface within the component.  You often use these numbers in conjunction with ReadRays to isolate specific intercepts for examination.

Go to list of topics

Faces and Edges

Visualizing Component objects as organized collections of surfaces, instead of contiguous solids, more directly captures Rayica's behavior.  Think of multiple surfaces floating in space and you have the idea.  Rayica never defines the volume occupied by a component, but only selected faces.  The selected faces are those important for optical design.

This convention can break down; it fails only under peculiar conditions that seldom occur in practice.  Component edges typically matter very little.  Lenses are not designed to pass light through their edges, but through their faces.  Rayical properties of lens edges are mostly irrelevant to optical design work.  For this reason, most built-in Rayica components lack edge definitions.  Thus a PlanoConvexLens has only two surfaces, one planar and one convex; Rayica omits the lens circumference.  As far as Rayica is concerned, a PlanoConvexLens is a pair of infinitely thin surfaces with open space between them.

You may construct a highly contrived optical system to demonstrate how this convention works, and where it breaks down.  Following is a PlanoConvexLens turned by 90 degrees.  This rotation orients the faces away from the optic axis.  Two light sources illuminate the lens edge-on, down the optic axis: one exactly, the other through the curved face.  The light shining exactly edge-on passes straight through, unaffected, between the two faces of the lens.

In[91]:=

redlight = Move[LineOfRays[14, NumberOfRays10, RayLineRGB  Red], {.1, -3}, Twi ... rue, ,,  , AxesLabel  {"x", "y", "z"}}], , ]}], ;}]

[Graphics:HTMLFiles/index_32.gif]

The standard PlanoConvexLens has no optical edge.  The edge-on (green) light travels between the faces without refraction.  The slightly off-sides light does refract, because it interacts with one of the faces.

Rayica's graphical display indicates an edge for aesthetic reasons only.  From a ray-tracing standpoint, this PlanoConvexLens is a pair of refractive surfaces floating in space; it has no edge at all.  This fact is proven by the trace above.  Only those rays striking a lens face experience refraction.  The remaining rays pass between the two  faces without disturbance.

Whenever edges concern your system, you should carefully explore the implications of Rayica's default treatment.

Go to list of topics

Rolling Your Own Edge

In some rare cases, you may need to create modified components with proper optical edges.  Rayica allows such definitions.  They require some facility with surface data  and a bit of creativity.  This section demonstrates how to accomplish the edge-completion task for PlanoConvexLens above.  This topic is somewhat advanced and may be skipped without penalty.

We can mate the two faces of PlanoConvexLens by means of CylindricalLens, which is shaped and placed in a highly unusual configuration.  The CylindricalLens will constitute the edge of the PlanoConvexLens.  In other words, the two main faces of CylindricalLens will form a single contiguous edge for PlanoConvexLens.  The result will be a completely closed Component that properly represents the full physical behavior of a plano-convex lens.

To produce this modified component, you must obtain the parameters of CylindricalLens.  The cylindrical lens will be rotated 90 degrees relative to the plano-convex lens.  Each face of CylindricalLens will cover a 180-degree arc.  The two radii of curvature will be identical but of opposite polarities, indicating opposite directions of curvature.  The curvature radius is one-half the aperture diameter of PlanoConvexLens, or 50/2.

If you view PlanoConvexLens edge-on, the outline of its edge is rectangular.  So the face-on aperture of CylindricalLens must also be rectangular.

In[131]:=

sidelens = PlanoConvexLens[75, 50, 15, GraphicDesignWire] ; <br /> RowBox[{RowBox[{Ana ...  Frame  True, ,, , PlotRange  {{-5, 35}, {-30, 30}}}], , ]}], ;}]

[Graphics:HTMLFiles/index_34.gif]

This side view of PlanoConvexLens reveals the rectangular aperture shape required for CylindricalLens, which will form its edge.

A list of two numbers specifies a rectangular aperture.  The height of the rectangle is the diameter of the PlanoConvexLens (50 units).  The width of the aperture is more difficult to ascertain.  It is the distance between the rim of surface 1 and the flat of surface 2.  Finding this distance requires some digging into the data of PlanoConvexLens.  The thickness of PlanoConvexLens is measured from the peak of its convex face to its planar face.  The width of the CylindricalLens aperture must equal this total thickness, less the thickness of the convex surface only.  Use SurfaceFunction of the convex face (surface 1) to compute the aperture width.

In[72]:=

cylApertureHeight = 50 ; surfaceFunc = Part[SurfaceFunction /. (Surfaces/.lens), 1] cylApertureWidth = 15 - surfaceFunc[0, 50/2][[1]]

Out[73]=

RowBox[{RowBox[{{, RowBox[{RowBox[{RowBox[{-, RowBox[{RowBox[{(, RowBox[{1513.523154204342, -, #1^2, -, #2^2}], )}], ^, (1/2)}]}], +, 38.90402491008279}], ,, #1, ,, #2}], }}], &}]

Out[74]=

5.9040806017464

Resonate will now consolidate edgelens with the previous lens definition to create newlens as a single component.  edgelens is nudged into position by nested Move calls.

In[140]:=

edgelens = CylindricalLens[25, -25, {cylApertureHeight, cylApertureWidth}, 50, GraphicDesign&# ... ;Move[Move[edgelens, -25],  {12, 0, 0}, {0, 0, 1} ] } ] ;

A test of the new lens proves it out.  This optical system is identical to the previous case but has lens replaced by newlens.

In[142]:=

RowBox[{RowBox[{AnalyzeSystem, [, , RowBox[{RowBox[{{, , RowBox[{Boundary[100, ... True, ,,  , AxesLabel  {"x", "y", "z"}}], , ]}], ;}]

[Graphics:HTMLFiles/index_40.gif]

This customized PlanoConvexLens has an edge.  The edge-on (green) light now refracts.


You must ensure that both the edge and the original component are made of the same material.  Since this material was unspecified in the present case, it defaulted to BK7 glass for both the plano-convex and the cylindrical lens.

Go to list of topics

Thick Components and Internal Reflections

All surfaces in Rayica are infinitely thin.  They have no inherent thickness.  Components, in turn, are composed of surfaces.  A thickcomponent can differ from its thin equivalent only by the number of surfaces used in the model.  For instance, Mirror can be modeled as thin, with a single reflective surface, or as thick, with both a reflective and a refractive surface.


Other components maintain no such distinction between thick and thin alternatives.  Where available, thickness is usually an extra parameter.  The following table shows how this extra parameter works in several common cases.  The left column shows the thin versions, and the right column shows the thick equivalents.

Out[9]//TableForm=

Mirror[50] Mirror[50, 1]
Baffle[50] Baffle[50, 5]
ApertureStop[10, 50] ApertureStop[10, 50, 5]
BeamSplitter[{50, 50}, 50] BeamSplitter[{50, 50}, 50, 5]

The thickness parameter: single-surface components (left) and double-surface components (right).

Many components automatically incorporate multiple surfaces and are always considered thick.  This class includes standard lenses and prisms.  Two exceptions are Screen and ThinLens; these components have only one surface.

Realize that thick models can experience internal reflections.  These reflections will depend on the incidence angles of your light sources.  Moreover, there is a Boolean option for lenses called FresnelReflections that instructs Rayica to track energy losses and first-surface reflections.

Here is a simple trace showing the energy intensity of Ray propagating through a lens.  FresnelReflections is an option for the lens.

In[4]:=

AnalyzeSystem[ {Boundary[100], Move[SingleRay[], {0, -30}, 30], ᡝ ... #62754;TopView, ThresholdIntensity.1, ShowTextIntensity] ;

[Graphics:HTMLFiles/index_42.gif]

With FresnelReflections→False, the ray propagates through the lens without losing energy.

The light energy is constant before, within, and after the lens.  Under this default behavior, the light loses no energy and experiences no reflection.  We modify the default behavior by setting FresnelReflections→True.  Rayica will now track first-surface reflections and energy losses.

In[5]:=

AnalyzeSystem[ {Boundary[100], Move[SingleRay[], {0, -30}, 30], Move[P ... 62754;TopView, ThresholdIntensity.01, ShowTextIntensity] ;

[Graphics:HTMLFiles/index_44.gif]

With FresnelReflections→True, Rayica monitors the energy loss and surface reflections of the propagating ray.

In this case, about 88% of the light energy still follows the original light path; the rest is scattered.  Note the internal reflections.

Sequential ray-tracing works as you might expect when there are internal reflections.  The reflected rays propagate through the system list in reverse order, starting at their originating component.  That is why the example above includes Boundary at both ends of the system list.  The first Boundary captures backward-propagating rays.  The last Boundary captures the forward-propagating rays.  It is generally good practice and always safe to put Boundary at both ends of your system list.

Go to list of topics

3.5 Special Considerations with PropagateSystem

This section covers a number of special considerations with the use PropagateSystem and AnalyzeSystem. These considerations were especially important with previous versions of Rayica before the advent of TurboTrace and its associated functions. Today, however, their understanding has become less critical since TurboTrace does not utilize them. As such, this section can be skipped without penalty.

Go to list of topics

3.5.1 QuickSurfaceSort

When propagating rays within a component, Rayica must decide in which order to check surfaces for intersection, and which of all possible intersections is correct.  This problem is similar to that of sorting components at the system level.  At the lower level of an isolated component, the internal surfaces must be sorted in some kind of priority order for intersection processing.

Rayica uses geometry to make intelligent guesses about which surfaces should be checked first.  The actual details of Rayica's workings in this regard are complex.  However, Rayica presents an option called QuickSurfaceSort that controls the procedure in a general sense.  This option applies to AnalyzeSystem and PropagateSystem.  Note, however, that the QuickSurfaceSort option does not apply to TurboTrace and TurboPlot applications, since they do not directly employ QuickSurfaceSort at all (other for gaining scout trace information from PropagateSystem). In general, TurboTrace and TurboPlot always uses the QuickSurfaceSort->False setting.

In previous versions of Rayica, PropagateSystem used QuickSurfaceSort→True as its default setting in order to maximize its ray-trace speed. Since the advent of TurboTrace, however, speed considerations for PropagateSystem have been supplanted by the concern for roboust, trouble-free use and QuickSurfaceSort→False is now the default setting. When you set QuickSurfaceSort→True, Rayica employs an efficient, but occasionally fallible, method for sorting surfaces within components.  The method compares center-points of surfaces to sort them from nearest to farthest.  Sometimes you must set QuickSurfaceSort→False to obtain a correct trace.  This setting causes Rayica to perform actual surface intersections to determine the sort order.  Naturally, this extra calculation incurs a speed penalty, but it is more robust and Rayica has available closed-form intersection formulas for certain built-in surfaces like spheres, planes, and conics.

The next example demonstrates how QuickSurfaceSort affects the outcome of a trace.  It is borrowed from the section on resonating cavities.  The difference here is that we give the mirrors thickness.  This change means that Rayica must sort the surfaces within each mirror component for purposes of intersection processing.  If a ray can intersect two surfaces, Rayica must decide which of the two it will strike.

In[48]:=

source = Move[SingleRay[], {150, 20}] ; mirror1 = MoveReflected[SphericalMirror[-300, 100, 10, ... 2371;system, QuickSurfaceSort  True, PlotType  TopView] ;

[Graphics:HTMLFiles/index_46.gif]

With QuickSurfaceSort→True, the trace fails in this particular geometry.

The trace is wrong.  Evidently, Rayica checked for an intersection with the back surface of M2 before the front surface.  This outcome shows that Rayica's default surface processing failed for this particular geometry.

Rayica produced the wrong outcome in spite of Resonate.  The Resonate directive ensures that all surfaces will be checked; it does not affect the order in which they are checked.  To change this order, you must use QuickSurfaceSort.  When this option is False, Rayica uses a more exact, but slower, method of sorting surfaces inside components.

AnalyzeSystem[system, QuickSurfaceSort  False, (* exact processing *)PlotType  TopView] ;

[Graphics:HTMLFiles/index_48.gif]

With QuickSurfaceSort→False, Rayica uses a more exact method of sorting surfaces within components.  This trace is correct.

It is always safe to set QuickSurfaceSort→False, but this setting, just like Resonate, incurs a speed penalty.

Go to list of topics

3.5.2 Confine

Components can receive light from any exposed surface.  Sometimes concern focuses exclusively upon light that enters through a particular surface.  This surface might be termed an entry port for the component.  This situation immediately suggests a ray-tracing optimization; Rayica should follow only those rays that enter through this surface, ignoring the rest.  The Confine function instructs Rayica to consider only specific surfaces as candidates for light entry into the component.  Please note, however, that the following discussions are not used in TurboTrace and TurboPlot, which do not directly employ confined port information at all.  As such, this section only applies to the use of AnalyzeSystem and PropagateSystem.

Here is a custom function that creates a multi-sided prism component.  The output of this function is a set of adjoining LensSurface components consolidated by Resonate into a single, unified component.  As created, such a prism has no preferred entry port.

Clear[PolygonPrism] ; PolygonPrism[faces_, radius_, height_, options___] := Module[{halfangle, ...  360)/faces],  {n, 1, faces}], top, bottom}], options]] ;

This is the appearance of a 9-sided PolygonPrism.

AnalyzeSystem[PolygonPrism[9, 50, 50, SurfaceRendering {Fill, Trace}, CrossRenderingEmpty]] ;

[Graphics:HTMLFiles/index_51.gif]

A custom prism.

The following trace shows how the prism refracts light with no entry port defined.  All exposed surfaces that happen to be facing the light will refract it.

light = Move[LineOfRays[100, NumberOfRays9], 5] ; prism = Move[PolygonPrism[9, 50, 50, ... ight, prism, boundary}, PlotTypeTopView, DefaultFont {"Courier", 18}] ;

[Graphics:HTMLFiles/index_53.gif]

The prism refracts indiscriminately with no privileged entry ports defined.

Suppose that surface 3 is an entry port; light impinging on other surfaces is considered irrelevant to the design.  Then you must notify Rayica to simplify the trace.  The prism is identical, but you Confine the light to enter through surface 3.

confinedprism = Confine[prism, 3] ; AnalyzeSystem[{light, confinedprism, boundary}, PlotTypeTopView, DefaultFont {"Courier", 18}] ;

[Graphics:HTMLFiles/index_55.gif]

Confine causes the prism to receive light only through designated surfaces.

Light that would normally refract through other surfaces skips past the prism altogether, terminating at the boundary.  Light allowed into the prism through the entry port (surface 3) reflects internally and eventually exits.  Once inside the prism, light can exit through any surface.

You may define more than one surface as an entry port.  Instead of a single number, use list notation, Confine[prism,{3,4,5}], for example.

Go to list of topics

3.5.3 Unconfine

Sometimes you must eliminate entry port restrictions.  The component will then receive light from all exposed surfaces.  The Rayica function Unconfine accomplishes this task; it effectively cancels the action of Confine.  For instance, you might Unconfine the confinedPrism that was defined above:

unconfinedprism = Unconfine[confinedprism] ; AnalyzeSystem[{light, unconfinedprism, boundary}, PlotTypeTopView, DefaultFont {"Courier", 18}] ;

[Graphics:HTMLFiles/index_57.gif]

Unconfine reverses the action of Confine.  The prism no longer has preferred entry ports.

The unconfinedPrism behaves as the original prism.  Light enters through all exposed surfaces.

Go to list of topics

3.5.4 Modular Systems

Rayical design, like other forms of engineering, often benefits from a block diagram approach.  A block diagram consolidates various details into conceptual blocks that perform high-level functions.  The blocks have defined interfaces, but their internal workings need not concern the designer.  Modular blocks ease system design tasks.

In terms of ray-tracing, the most important aspect of such a block is that  it can be traced ahead of time.  Rayica features an option called ModularSystem for this purpose.  This boolean option causes Rayica to create extra rays at the system output, poised for tracing into the next module.  This mechanism essentially turns a modular optical system into a light source for downstream modules.  Rayica need not recompute the first module when you trace the overall system.  This computational savings translates into better performance.

Under normal conditions ModularSystem→False, and Rayica terminates ray creation at the final surfaces.  The last intersection surfaces kill the rays.  However, when ModularSystem→True, Rayica traces through the final surfaces and leaves the final rays ready for tracing into downstream modules.  These rays encode intensity, direction, and intersection points at the output surfaces of the modular system.

A very simple modular system comprises SingleRay and Screen.  ReadRays will extract geometry data for each ray created by this system.

In[27]:=

ReadRays[sys = AnalyzeSystem[{SingleRay[], Translate[Screen[5] ... 2754; Automatic, ModularSystem  False],  {RayStart, RayEnd}] // MatrixForm

[Graphics:HTMLFiles/index_59.gif]

Out[27]//MatrixForm=

( ( 0.` )    ( 10.` ) )                      0.`                        0.`                      0.`                        0.`

The system has only one ray. You know this fact because two data per ray were requested, start and end, and two data were obtained.  Now set ModularSystem→True.

In[26]:=

ReadRays[sys = AnalyzeSystem[{SingleRay[], Translate[Screen[5] ... 62754; Automatic, ModularSystem  True],  {RayStart, RayEnd}] // MatrixForm

[Graphics:HTMLFiles/index_62.gif]

Out[25]//MatrixForm=

( ( 0.` )    ( 10.` ) )                      0 ...                 0.`                        0.`                      0.`                        0.`

The system now has two rays.  This fact is not evident from the graphics.  However, ReadRays reports two rays: one from the origin to the screen, and another essentially sitting on the screen, having equal start and end points.  This extra ray is the output of this modular system.  Rayica has primed an exit ray with data concerning the final intersection of the modular system.

You may assign any type of component as the output of a modular system.  There can be any number of output components.  Some make more sense than others.  A typical alteration made when converting systems to modular format is the replacement of a Boundary with a ClearBoundary.  The difference between these components is that a ClearBoundary does not absorb the light but merely intersects it.  For purposes of modularity, Rayica can record the intersection data there without altering the light itself (as would a Boundary).

The modular system concept is best demonstrated by example.  Here is a practical modular system, a beam-expanding spatial filter.  Note the use of a PinHole and the final Screen.

In[33]:=

RowBox[{RowBox[{spatialFilter,  , =,  , , RowBox[{AnalyzeSystem, [, RowBox[{RowBox[{{, ... 54;TopView, ,, , FrameTrue, ,, , FrameTicksAutomatic}], ]}]}], ;}]

[Graphics:HTMLFiles/index_65.gif]

A beam-expanding spatial filter becomes a modular system building-block.

Now create a collimator by feeding the output of spatialFilter into a collimating lens.  In doing so, there is no need for light sources.  The light source was defined inside the spatialFilter (a different approach will be shown below).  Furthermore, this new system will itself be modular.  This method follows the block-design approach.  Each system has a specific purpose.

In[34]:=

collimator = AnalyzeSystem[{spatialFilter, Move[PlanoConvexLens[100, 5 ... PlotTypeTopView, FrameTrue, FrameTicksAutomatic] ;

[Graphics:HTMLFiles/index_67.gif]

The spatialFilter module placed within a collimator system, itself also modular.

You may wish to study internal reflections within the collimator.  Create a system comprising the collimator and another off-axis spatialFilter.  A Boundary delimits both ends of the system list in order to catch both forward- and backward-propagating rays.

In[36]:=

boundary = Boundary[{0, -100, -100}, {150, 120, 100}] ; finalSystem = AnalyzeSystem[{& ... #62371;PlotTypeTopView, FrameTrue, FrameTicksAutomatic] ;

[Graphics:HTMLFiles/index_69.gif]

The collimator and spatialFilter modules functioning within a system under study.

The Mathematica variables collimator and spatialFilter constitute system modules that can serve in any capacity.  They both contain pre-traced light rays.  In the example above, the only rays Rayica had to trace were those emerging from the output of the off-axis spatialFilter.

When speed is of less concern than flexibility, you can define modular systems lacking such pre-traced rays.  The basic idea is to compose a Mathematica list of components (and possibly sources as well) that serves as a modular system.  The difference here is that the list is just a list, not an OpticalSystem.  The collimator defined above is an OpticalSystem.

In[39]:=

Head[collimator]

Out[39]=

OpticalSystem

Yet, you may freely redefine Mathematica variables, and we do so to demonstrate another method of modular design.  This method does not depend on the ModularSystem option in any way.  The ModularSystem option applies only to OpticalSystem objects.  The following represents a completely different modular design approach.

In[40]:=

RowBox[{RowBox[{spatialFilter,  , =,  , , RowBox[{{, , RowBox[{LineOfRays[1, N ... #62371;PlotTypeTopView, FrameTrue, FrameTicksAutomatic] ;

[Graphics:HTMLFiles/index_73.gif]

The same system built using a different modular method.

The trace is identical but consumes more time, for Rayica must trace the light through each and every component.  There is no pre-trace data.  Some computational savings was achieved by removing the output components from the modules.  The spatialFilter previously had Screen, and the collimator had ClearBoundary.  These components were not required since ModularSystem was not in use.

The question of whether to use the ModularSystem approach or the type shown revolves around the subject of light inputs.  The ModularSystem option effectively turns upstream modules into light sources for downstream modules.  It only makes sense to define an effective light source of this nature if its own light source is fixed.  Therefore, you should use the ModularSystem method if the input light to the module is fixed.  When, instead, the input light sources will be moved about, no pre-trace is really possible.

Go to list of topics

3.6 TurboTrace

TurboTrace speeds Rayica's ray-tracing performance up to 100 times beyond standard routines (and >1000 times in special cases).  The suite of TurboTrace functions is, perhaps, the most important branch of Rayica.  As a whole, TurboTrace and its allied functions constitute an alternative ray-tracing system within Rayica.  Yet this system merely builds upon concepts already presented in this chapter.

3.6.1 Basics of TurboTrace

TurboTrace incorporates compiled functions plus certain structural and numerical optimizations to maximize tracing speed.  Compiled functions utilize a special bytecode system that bypasses the standard Mathematica interpreter.  These bytecodes embody arithmetic and branching operations, much like assembly-language opcodes.  (See The Mathematica Book for more information.)

The implementation details of TurboTrace are transparent and largely unimportant for proper usage.  The major concern with this function is to understand its basic principles of operation.  The interfaces to the turbo system are practically identical to those of the standard system.  In general, you should use TurboTrace whenever possible.  The following equivalency table shows the primary interfaces to the turbo system. Note: this table should be taken conceptually and not literally.

PropagateSystem[...] ⇔ TurboTrace[...]
AnalyzeSystem[...,PlotType→Surface] ⇔ TurboPlot[...]
ReadRays[...] ⇔ ReadTurboRays[...]
RaySelect[...] ⇔ TurboRaySelect[...]
{Ray[...],Ray[...],...} ⇔ TurboRays[...]
Surfaces/. {Component[...],...} ⇔ TurboSurfaces[...]

TurboTrace works with the same kind of system descriptions as the standard functions.  However, TurboTrace differs from them in one important respect, its assumption about surface encounters. In particular, TurboTrace offers two different ways of determining optical surface encounters during a ray trace. These two methods are selected with the TurboTrace option settings: SequentialTrace and ScoutTrace.  As its default setting, TurboTrace uses SequentialTrace -> False and ScoutTrace -> Automatic. With this setting, TurboTrace traces the rays through optical surfaces in similar fashion as PropagateSystem, described previously. SequentialTrace -> False provides the most robust tracing environment.  However, in order to achieve maximum the ray-trace speed, SequentialTrace -> True is required. With SequentialTrace -> True, TurboTrace sorts the optical surfaces in your system by means of a scout trace.  This preliminary trace involves just a handful of scout rays but fixes the order of surface encounters for all other rays.  Internally, TurboTrace calls PropagateSystem to compute the scout rays.  These yield an ordered list of surface encounters.  Armed with this list, TurboTrace constructs a compiled function for ray tracing called, RayTraceFunction. Once invoked, RayTraceFunction sends the mass of remaining rays through ordered list of surface encounters, under the assumption that all rays encounter the same surfaces, in the same order, as the scout rays.  We refer to these main rays as turbo rays.

With SequentialTrace -> True, the fact that TurboTrace uses PropagateSystem to determine surface encounters means that there is no mystery about the path.  Everything that applies to PropagateSystem, as described in previous sections, applies to the scout trace.  Fundamentally, a scout trace can fail.  If the scout trace is wrong, so is the entire TurboTrace. Sometimes TurboTrace fails even after a successful scout trace.  This situation arises when the turbo rays have a wide crosswise dispersion relative to the optics.  A paraxial scout ray within a wide beam may follow the system quite well, while some stray edge ray may not.  Nonetheless, TurboTrace wrongly assumes that it does.  Therefore you must exercise caution with TurboTrace and SequentialTrace -> True.  You can often remedy these kinds of problems by manual specification of the scout rays.  For example, an automatic scout ray traced into a Cassegrainian telescope would be paraxial and thus strike the back of the secondary mirror, yielding incorrect scout data.  In this case you would define scout rays that bypass the central obstruction of the secondary mirror.  Alternatively, you can simply use the SequentialTrace -> False setting.

In addition to the SequentialTrace option, the auxiliary ScoutTrace option determines whether a scout trace is used in TurboTrace. With SequentialTrace -> True, a scout trace always occurs. However, with SequentialTrace -> False, a scout trace does not occur with the default ScoutTrace -> Automatic setting, but does occur with ScoutTrace -> True. In this later setting, only those surfaces that intersect the scout ray get included into the constructed RayTraceFunction.

Before we can examine TurboTrace in greater detail, we first need to explore how symbolic parameters are specified in Rayica.

Go to list of topics

How to Enter Symbolic Parameters in Rayica

The basic Rayica system permits symbolic entry of most system parameters. These parameters may then be used to construct symbolic variables within the system that later become the input parameters to the compiled RayTraceFunction. In many cases, the symbolic parameter "piggy-backs" together with a numerical value in the form of an ordered list. In general, Rayica requires that all symbolic parameters are also accompanied by a numerical value. Here is an example of symbolic parameter entry within PlanoConvexLens.

In[27]:=

symbolicLens = PlanoConvexLens[{f,100},{a,50},{t,10}]

Out[27]=

PlanoConvexLens[{f, 100}, {a, 50}, {t, 10}]

In other cases, the symbolic parameter is given in the form of an option. This occurs in particular for numerical option parameters. However, in this case, the symbolic value does not piggy-back with the numeric option, but instead gets its own option name that is prefixed with the word "Symbolic" in front of the corresponding numerical option name.  Two such options are SymbolicWaveLength and SymbolicIntensity. An exception is NumberOfRays, which must be a numeric at all times.

In[28]:=

LineOfRays[10, SymbolicWaveLength->λ, SymbolicIntensity->i, NumberOfRays->4]

Out[28]=

LineOfRays[10, NumberOfRays4, SymbolicIntensityi, SymbolicWaveLengthλ]

Most types of light source functions cannot accept symbolic values for its built-in parameters. The LineOfRays source given above is one such example. Here, the linewidth parameter must be a numeric value. In error message is generated otherwise.

In[30]:=

LineOfRays[{s,10}]

Source :: symbolic : LineOfRays cannot take symbolic parameters {{s, 10}} .

Out[30]=

$Aborted

An exception is the GaussianBeam source, which can take direct symbolic parameters.

In[29]:=

symbolicSource = GaussianBeam[{w,10},{div,.001}, SymbolicWaveLength->λ, NumberOfRays->4]

Out[29]=

TagBox[RowBox[{GaussianBeam, [, RowBox[{{w, 10}, ,, RowBox[{{, RowBox[{div, ,, 0.001}], }}], ,, NumberOfRays4, ,, SymbolicWaveLengthλ}], ]}], HoldForm]

The Move function accepts symbolic parameters as long as a numeric value is also given with each one. Angles may also be specified in a similar fashion. Remember, that any angular information will use units of Degrees for Rayica's Move functions. This is in contrast with Mathematica's built-in trigonometric functions, which takes radians by default.

In[32]:=

symbolicLensSystem =
    {symbolicSource,
    Move[symbolicLens, 50],
    Move[Screen[50],{{x,200},{y,0}},{α,10}]}

Out[32]=

RowBox[{{, RowBox[{TagBox[RowBox[{GaussianBeam, [, RowBox[{{w, 10}, ,, RowBox[{{, RowBox[{div, ... 00.}], }}], ,, {y, 0}}], }}], ,, RowBox[{{, RowBox[{α, ,, 10.}], }}]}], ]}], HoldForm]}], }}]

Once a system has been defined with symbolic parameters, you can use TurboTrace to make compiled ray-trace code that uses these symbolic values as input parameters.

In[33]:=

turbotrace = TurboTrace[symbolicLensSystem]

Out[33]=

RowBox[{{, RowBox[{RayTraceFunctionRayTraceFunction[{λ, a, f, t, x, y, α}, - ... ysTrue, ,, EmbedThresholdIntensityTrue, ,, EmbedGenerationLimitTrue}], }}]

Here TurboTrace has generated a fully-compiled, RayTraceFunction that is returned along with a list of rules that describe the RayTraceFunction construction. RayTraceFunction contains all necessary information to carry out a complete ray trace operation. It uses all of the symbolic variables defined previously in symbolicLensSystem as input parameters. The SymbolicValues rule holds default numeric settings for each symbolic parameter. This was extracted from the numeric values originally given for each parameter. The EmbedRays, EmbedThresholdIntensity, and EmbedGenerationLimit options tell us whether each of these parameter types have been embedded within the compiled ray-trace code or whether the parameter has been left as an input variable to RayTraceFunction.

Go to list of topics

3.6.2 Lens Optimization

Iterative design procedures involving repeated traces will benefit substantially from TurboTrace.  Lens optimization falls into this category.  This section presents an example of lens optimization.  The objective is to find optimal surface curvatures for a lens focusing planar waves onto a screen at a fixed distance.

The optimization parameters are the radii of curvature for the front and back lens surfaces. Begin with each set to 100 mm, opposite polarities indicating opposite directions of curvature.  A Baffle fixes the desired focal plane. Here we assign symbolic parameters, r1 and r2 to the radius-of-curvature parameters of the front and back lens surfaces.

In[95]:=

sys = {LineOfRays[45, NumberOfRays6], Move[SphericalLens[{r1, 200}, {r2, -200} ...  10], 100], Move[Baffle[50], 200]} ; turbosys = TurboPlot[sys, PlotTypeTopView] ;

[Graphics:HTMLFiles/index_82.gif]

A system in need of optimization.  The light does not focus optimally at surface B owing to unoptimized radii of curv