RaySim6 handles an enormous amount of data, and that is perhaps the biggest challenge of writing a general purpose trace simulator. Many of the ways that RaySim works were built around the requirement to keep track of the simulation and results data and still complete trace simulations quickly. This section gives some of the details about how RaySim works.
The Document View follows this hierarchical data structure, so once you get used to that architecture, you will find it is relatively easy to navigate to any part of the Document-Batch-Layer-Plane-Edge data to edit or update the data stored there.
Ray Data. A typical simulation might process 10 million trace operations. Some detailed simulations reach 100 million trace operations. RaySim does not store all intermediate rays because this would be slow and would require a large amount of RAM memory in some cases. Instead, RaySim uses a last-in-first-out (LIFO) stack data structure to store rays.Before a trace simulation is started, RaySim builds up the input bundle of rays using the Simulation Parameters set by the user. Each ray in the input bundle is pushed onto the LIFO stack as it is generated. When the bundle is complete, RaySim takes the last ray back off the stack and proceeds to trace that ray. If the ray should be subdivided, the subrays are also pushed onto the LIFO stack. When a ray reaches the Minimum Intensity condition or is lost, it is deleted from memory, and the next last ray is popped from the LIFO stack for tracing. You can see the number of rays on the stack in the status bar at the bottom of the RaySim window.
The LIFO stack is a pretty good way to keep the number of rays in memory to a minimum and is relatively fast in terms of memory access.
Results Data. A Model with 4 Layers, each requiring 4 Planes requires 4 + 4*4 = 20 separate accumulators to track light absorbed within the model. If the input bundle of rays scans 20 points in the x direction, 20 points in the y direction and 40 wavelengths, the total number of accumulators is 20 * 20 * 20 * 40 = 320,000. This is not normally a large number in terms of RAM memory. The tricky bit is mapping the data storage to both the input bundle of rays (so that individual input rays can be tracked through the model) AND the Model's Layers and Planes.To do this, RaySim builds a tree data structure - in the shape of the Model (with all the Layers and Planes) - for every ray in the input bundle. Each node of the tree accumulates data for one input ray for one part of the Model. This makes storage and retrieval of data fast and straightforward.
The allocation of 320,000 or more memory objects in RAM memory is typically slow. The tree structure helps significantly. To speed things up further, RaySim also performs dynamic memory allocation only when needed to store information. This is because the body of simulation results is very sparce in that most planes or layers do not absorb any of the light. For example, a Planar Reflector Plane with a reflectance of 1.0 will not absorb any of the light. In this case, no memory allocation is required. When data is presented in the Results View, any NULL data (where no memory has been allocated) is recognised and replaced with a zero entry
InLayer versus {x, y, z}. One important concept in RaySim is the InLayer attribute of rays. Every ray in the simulation keeps track of the layer it is presently inside (with the InLayer attribute) INDEPENDENTLY of that ray's {x, y, z} location in the Model. Every time a ray is traced through part of the Model, RaySim checks the InLayer attribute to determine which Layer that ray should be inside. NB, RaySim DOES NOT use the {x, y, z} location of a ray to determine which Layer the ray is inside. Therefore, it is possible, though not usually desirable, for a ray to be physically inside a particular Layer and have its InLayer attribute indicate that it's in a different layer. RaySim does things this way because it's tricky to work out which layer a ray is in based on its physical location, especially for Models that have one layers completely embedded inside another layer, in which case it would appear that the ray was inside two or more layers.
During tracing, RaySim uses the InLayer attribute to trace a ray by checking every Plane in the InLayer Layer for an intersection with the ray. For example, if a ray is inside the third Layer (which is InLayer = 2), RaySim will check all of the Planes that belong to that Layer for an intersection with the ray. RaySim will ignore all other planes belonging to other Layers (and other Models, for that matter). This keeps the simulation fast by minimising the number of intersection tests required to trace a ray.
When a ray intersects a Plane, RaySim checks to see if the InLayer attribute needs to be changed. In some cases, such as for Planar Reflectors, the InLayer attribute is not changed because, conceptually, the ray stays inside the same Layer. On the other hand, if the ray is transmitted through a Planar Bare surface, RaySim will change the InLayer attribute to imitate the situation where the ray has been transmitted into the new layer.
Users have to specify the adjacent Layer (called "Transmit into Layer?") for the following Planes so that RaySim knows how to modify the InLayer attribute for transmitted rays: Planar Bare, Planar Single Layer AR and Planar Ideal.
InLayer for the Input Bundle of Rays. The InLayer attribute is always initialised to the first Layer in your Model (InLayer = 0). Be sure that your rays start out physically inside the first Layer of your Model (or conversely, make sure your first Layer encloses the starting point for your bundle of input rays!)
The Normal vector. Planes are one-sided. Rays do not intersect the back side of a Plane. The Normal vector determines the "side" of a Plane. See more information here.
Trace Sequence of Events. The general sequence of a trace event is
1. Remove a ray from the LIFO stack. This ray becomes the trace ray.
2. Check all Planes belonging to the InLayer Layer for an intersection with the trace ray. Choose the closest Plane that's a positive distance away (ie. not in the backwards direction).
3. Subdivide the direct component of the ray due to bulk scattering if necessary. Push subrays onto LIFO stack and discard the trace ray.
4. Subdivide the diffuse component of the ray due to bulk scattering if necessary. Push subrays onto LIFO stack and discard the trace ray.
5. Trace the ray up to the Plane of intersection, accounting for absorption in the Layer.
6. Check the Plane Type. Reflect, transmit, refract as necessary. Subdivide the ray due to diffuse surface if necessary. Subdivide the ray with random translation if necessary. Push all subrays onto LIFO stack and discard the trace ray.
7. Lather, rinse and repeat a few million times, as necessary.
Rules for Subdividing Rays. Each ray has several attributes that keep track of whether or not that particular ray has already been subdivided in any of several different ways: Direct component in a scattering medium, diffuse component in a scattering medium or duffuse surface, at an interface between two layers and by a Randomise Tail Transmit Subdivide Plane. At the start of a trace simulation, these attributes are set to false, indicating that the ray has not already been subdivided in any way.
During simulation, a ray may encounter a Layer or Plane that requires subdivision, for example, a Lambertian Reflector Plane. When a ray intersects this Plane, the IsSubdivideInThetaXi attribute of the ray is examined. If this attribute is false, indicating that the ray has not already interesected a Lambertian Reflector Plane, that ray will be subdivided into some number of subrays with an angular distribution equivalent to a Lambertian diffuse surface. The number of subrays depends on the settings in the Simulation Controls. Each of the subrays are pushed onto the LIFO stack for later tracing, however the IsSubdivideInThetaXi attribute is set to true for the subrays, thereby preventing further angular subdivision of those subrays by this process. These rays will not be subdivided further by this process, however, their angular direction will be randomly determined (in Monte Carlo fashion) each time that ray interesects a Lambertian Reflector Plane. Subrays, may however, be further subdivided by other processes. The table below shows the which different ray attribute controls the subdivision of rays by different processes.
Ray Attribute | Controls |
nPlanarSubdivide | The number of times a ray can be subdivided at a partially transmitting interface, eg. Planar Bare Surface |
IsDividedInThetaXi | Whether or not a ray has been angularly subdivided at a Lambertian diffuse surface or in scattering bulk layer. |
IsSubdividedInScatter | Whether or not a ray has had its direct beam component subdivded in a scattering bulk layer. |
IsSubdivdedInPosition | Whether or not a ray has been subdivided by a Randomise Tail Transmit Subdivide Plane |
The number of ray subdivisions is determined by the user settings in the Simulation Controls area of the Batch Builder dialog box.
Simulation Time and Accuracy. In general, greater accuracy requires more rays, and more rays take longer to trace to completion. Thus, there is a trade off between accuracy and time that is inherent to ray tracing. The trick is minimising the number of rays while still maintaining the desired accuracy, and as such, selection of the right values for the Simulation Controls is essential. Too many rays in the input bundle or too many ray subdivisions will increase the simulation time, but may not necessarly increase the accuracy.
Users will likely develop strategy for fast and accurate simulation. Here are a few tips to get started.
1. Start with a small number of rays in the input bundle and small values for the various ray subdivision rules. The simulation will be grainy, but fast. Increase the values until you reach the desired accuracy.
2. Scan the input bundle over one parameter, for example wavelength, with small steps such that the optical properties are roughly constant. Examine the results and check to see if the results from one ray to the next are varying smoothly or have significant deviation. If there is significant deviation, the accuracy is poor.
3. The Simulation Controls on RaySim are designed to improve accuracy and speed by (1) distributing the rays where they are needed most using the rules for subdivision and (2) once the rays are distributed, to use Monte Carlo simulation to finish the simulation. For this reason, the subdivision rules are important. Try to get the rays distributed within the model early in the simulation.
4. The Number of Interface Divides controls how many times a ray will be subdivided at an interface between two materials. If you select zero for this parameter, the simulator will use Monte-Carlo techniques starting with the first interface. In this case, 100% of the ray will either be transmitted or reflected, with a probability given by the transmission/reflection coefficient of that interface. This leads to a grainy and inaccurate simulation because in some cases, the ray will not even enter the next layer of your Model. I find that 4-6 is a good value for the Number of Interface Divides. Note that the number of subrays increases geometrically with the Number of Interface Divides, so choosing 10 results in a total of 211-1 subrays.
5. In cases where you are not interested in the topographical response but the first ray incidence is upon a textured feature (or any feature that varies with position), consider using a Randomise Tail Transmit Subdivide Plane to convert the input ray into a randomly distributed (in position) "beam" of rays. This spreads out rays and enhances the accuracy in cases such as textured front features.
6. Generally, the first subdivision is the most important. In the Silicon Pyramid Batch (see the Starter document), the first subdivision is upon a Randomise Tail Transmit Subdivide Plane (it is included because of the textured front surface). The second subdivision occurs at the Diffuse Back Plane of the Back EVA Layer (because it is a Lambertian Reflector type Plane). In this Model, the first subdivision has a greater influence on the accuracy, and so tend to use larger numbers for the Subdivide in Position value and smaller numbers for the Subdivide in Theta and Subdivide in Xi values.
7. Set the Minimum Intensity to represent the desired precision. This value does not influence accuracy much, and so setting it to a vanishingly small number will likey increase simulation without improving accuracy. Essentially, the simulator will spend a good deal of time tracing insignificant rays if this number is too small. Generally, I use 0.001 to give a precision of about 2.5 significant figures
Lost and Remainder Rays. If a trace ray does not intersect any Planes belonging to the Layer that it is inside, it becomes a lost ray. More about that here. That ray is discarded and its intensity is added to a special accumulator that sums the intensities of all of the lost rays. RaySim keeps track of the total intensity of lost rays for each ray in the input bundle of rays.
Also, any rays that reach the Minimum Intensity condition are discarded. The intensity of the discarded ray is added to another special accumulator, called the Remainder, that keeps track of the total intensities of all of the rays that reach the Minimum Intensity condition. RaySim keeps track of the total intensity of these discarded rays for each ray in the input bundle of rays.
Ending a Simulation. RaySim will continue to trace until one of several conditions has been met: (1) all of the rays in the LIFO stack have been discarded (lost or reached minimum intensity), (2) the user stops or pauses the simulation from the Menu bar, (3) the user presses the escape key. With the exception of pausing the simulation, restarting the simulation will cause RaySim to begin from the very start of a simulation.