In this article, a simulation method with an example of optimization for an exit pupil expander (EPE) system using 1D gratings is demonstrated.
In this workflow, we use Lumerical to construct the grating model and simulate its response with the RCWA solver. The full EPE system is built in OpticStudio and dynamically linked to Lumerical for integrating accurate grating models. Finally, optiSLang is used to overall control the system level optimization by modifying the grating model and achieve the required optical performance in the whole EPE system.
Overview
Understand the simulation workflow and key results
The design of an AR system with an EPE that allows to increase the size of the eye box is a challenge for the optimization at the system level because it presents a lot of parameters. In this article, the waveguide is composed of three 1D slant gratings designed in Lumerical. These gratings are respectively used for the in-coupler (IC), the fold grating, and the out-coupler (OC) in the OpticStudio optical system via a Dynamic Link (See Dynamic workflow between Lumerical RCWA and Zemax OpticStudio ). The fold grating and the OC are both divided into several zones where the grating parameters are tuned separately during the optimization process.
The optimization is then handled by optiSLang via a Python node. The use of optiSLang provides great advantages such as the capability to perform pre-processing and post-processing during each optimization cycle (e.g. convolution of the result with a pupil function). In addition, instead of using directly all the individual grating characteristics in the different zones, the parameters can be controlled by defining a function in the python code, reducing the total number of variables, and therefore the optimization time.The process is initiated by a Sensitivity block so that the system can identify the most impactful parameters when running the optimization.
This article is divided in 4 main steps as follows;
Step 1: Setting up the Optical system with Zemax and Lumerical
In this section we present the optical system that is to be optimized. It is the same type of system that we can be found in the article Augmented Reality Optical System . Note that in the original design, the energy is not spread throughout the full Eye box as most of the light is outcoupled after a few interactions with the fold grating and the out-coupler.
Step 2: Setting up the Optimization in optiSLang
The purpose of this article is to demonstrate how to use optiSlang to control the optimization of the optical system built with Zemax. This section introduces the parameters that are going to be used for the optimization, as well as the metrics used as optimization target.
Step 3: Check the optimization results
The optimization was already set, and results are available. In this section, we provide information on the structure of the optiSlang file, and explain how to visualize the results that came from the multiple optimization runs.
Step 4: Update the system with the desired design
This final section is a step by step guide to select a specific design from the different runs and to push the corresponding parameters to the optical system.
Run and Results
Instructions for running the model and discussion of key results
Step 1: Setting up the Optical system with Zemax and Lumerical
- Open the file EPE_1D_out-coupler.zar in Zemax OpticStudio to check the system settings.
The three 1D gratings are built in Lumerical as slant gratings, though the fold and OC gratings are initialized as simple binary grating with a slant angle of 0°. The optimization will use the slant angle, depth, and duty cycle as variables in each tiles of the fold and OC gratings.
In the waveguide system built in OpticStudio, a collimated beam is incident on the IC grating, propagates through the waveguide to the fold grating that is then redirecting the rays to the OC.
The detector is set to capture the rays directly at the output of the OC grating. The goal of the optimization is to enhance the uniformity of the irradiance map output toward the eye, while maximizing the total power.
To prepare for the optimization, the fold and OC gratings are separated into multiple tiles. The number and shape of these tiles can be easily customized as desired by the user. In this example, the grating zone shapes on the waveguide are circle (in-couple grating), rectangle (out-couple grating), and polygon (fold grating). To define a polygon grating zone, users can download the corresponded User Defined object DLL from How to simulate exit pupil expander (EPE) with diffractive optics for augmented reality (AR) system in OpticStudio: part 4
Step 2: Setting up the Optimization in optiSLang
- Open the file EPE_1D_optimization.opf in optiSLang to check the optimization settings
- Double click on the Python icon “EPE_1D_for_OptiSlang.py” to visualize the code.
In the attached file, a python file EPE_1D_for_OptiSlang.py is prepared for linking optiSLang to OpticStudio. For more details on how to set up such a file, see the appendix .
In this article, we want to control the grating height, duty cycle, and slant angle of both the fold grating and the OC. Instead of setting all these parameters individually in each tile,the grating parameters across the multiples zones of the given grating are controlled with a parametric function defined as follows:
$$f(v_{min},v_{max},v_{adj})=v_{min} + (v_{max}-v_{min})*(\frac{i-1}{N})^{v_{adj}}$$
Where i is the zone number, and N is the total number of zones in the grating.
These parameters are varied by optiSlang following a predefined optimization algorithm (e.g. Evolutionary Algorithm). The varied parameter values are set into the python code and flow it down to each grating tile in OpticStudio. In this process, the Python code plays a role to do the work converting these variables into the exact parameters in OpticStudio. This pre-data-processing with a function defined in Python is only possible when we optimize the system with optiSLang instead of the built-in optimizer in OpticStudio. In this way, optiSLang can optimize the system based on some virtual or high-level variables that are not directly exposed in the OpticStudio UI.
Once the parameters are set up, the remaining of the code consists of calling OpticStudio to trace rays and collect results from the detector. The other benefit of using optiSLang to optimize the system is post-data-processing. In this optimization process, we don’t directly optimize the irradiance distribution on the eye box. Instead, we first do a convolution on the irradiance distribution with a pupil function, and then set the optimization target based on this convolved result. For this example, the optimization target are the contrast, total power, and uniformity as defined below:
$$ Constrast = \frac{Max-Min}{Max+Min} $$
$$ TotalPower = \frac{Power\_Received\_By\_Eye\_Box}{Power\_Incident\_On\_IC - Coupling\_Grating} $$
$$ Uniformity = \frac{Standard\_Deviation}{Total\_Power} $$
The final part of the Python code is to draw the result of the irradiance at the eye box, as well as its convolved result, and then export pictures. This is useful for users to check how the irradiance looks like for each of the optimized system directly within optiSLang postprocessing.
Step 3: Check the optimization results
- Double click on the ‘Postprocessing (1)’ icon to visualize the results of the Optimization.
When the optimization starts, a sensitivity analysis is first performed to identify the most important parameters. With this input, the Evolutionary algorithm runs and produces a series of results. These results can be visualized directly in optiSLang in a Pareto plot.
The red marked designs in the following plot are called Pareto front. A Pareto front illustrates the trade off between multiple objectives, where no design dominates the other one regarding performance. That means all of these designs show up different balances of multiple criteria (e.g. Uniformity vs Total Power). We have picked up 2 results and show them below. For each design point corresponds a pair of plots showing respectively the contrast and the uniformity within the Eye Box.
Step 4: Update the system with the desired design
- Copy/Paste the Sensitivity block into the page and double click on the block title “Sensitivity (1)” to edit it
- In the “Dynamic Sampling” tab, uncheck the “Dynamic Sampling” option
- In the “Other” tab, Check the “Solve start designs again” option in the “Hide Evaluation settings” menu
- In the “Start Designs” tab, choose the option “import start values from system” and select the desired design identified on the Pareto Plot (e.g. #449).
- In OpticsStudio, click on the ‘Interactive extension’ button in the Programming tab, and then run the OptiSlang Simulation.
The optimization has already been done in the attached file and the optimized results can be visualized when opening the file. Sometime we might want to pick up an optimized design and investigate in OpticStudio. However, optiSLang only saves the input parameters in a table. We don’t keep the OpticStudio system. In order to select a specific design in optiSLang and push the parameters into the optical system in OpticStudio, we can duplicate the Sensitivity block and define its starting point to be the desired design. By disabling the dynamic sampling, running this block will simply read the parameter values from the selected design and push the corresponding data into OpticStudio. The user can then manually save the new system under another name. Another option to get the OpticStudio system with any picked design in optiSLang is to add a command in the python code to save the system as a .zmx file directly when running the optimization in optiSLang (e.g. TheSystem.SaveAs(‘design_optimized.zmx’). Note the second method only works when we make the change in the Python before running the optimization in optiSLang.
Important model settings
Description of important objects and settings used in this model
- Since the system relies on a Python code, it is assumed that Python is installed as well as all the additional modules used called in the script.
- It is important to ensure the “Interactive Extension” is active in OpticStudio (Programming tab) before running anything in optiSLang.
Updating the Model with Your Parameters
Instructions for updating the model based on your device parameters
- The optical system in this example is presenting an EPE with two 1D gratings divided into several zones. It is up to users to customize the shape, nature, and position of the gratings. The shape and number of the tiles may also be customized directly in the optical system.
- The variables and optimization targets are defined when setting up optiSLang and can be customized. See the appendix for more details on how to set up the optimization model in optiSLang.
- In this article, the variables are defined indirectly with a function. In the Python code of the example, it is under the name ‘linsp’. Any custom function may also be used by the user, the definition can simply be updated in the Python code.
Taking the Model Further
There are some considerations that are not covered in this demonstration but users could pay more attention when they try to follow this process for their systems.
- In this demonstration, we only consider the central field, which is a collimated beam normally incident on the waveguide. For a more comprehensive optimization, more fields can be added to cover the uniformity in full field of view.
- Similarly, the system is only designed for a single wavelength. Depending on the system design, the optimization can include multiple wavelengths.
- Some irradiance distributions look more uniform but result in higher contrast. The criteria could be improved by modifying the Python code.
Additional Resources
Related Publications
- Han-Hsiang (Michael) Cheng, Yuan Chen, Alexandra Christophe, Sabrina Niemeyer, Chih-Hao Chen, Jens Niegemann, Milad Mahpeykar, Yihua Hsiao, Yi-Hao Chen, Dylan McGuire, and Adam Reid "Optimization and tolerance for an exit pupil expander with 2D grating as out-coupler", Proc. SPIE 12449, Optical Architectures for Displays and Sensing in Augmented, Virtual, and Mixed Reality (AR, VR, MR) IV, 124490X (16 March 2023); https://doi.org/10.1117/12.2645565
See Also
Related Ansys Course
Appendix
Additional background information and theory
The information in the appendix describes how to set up the optiSLang environment from a blank file instead of using the example provided in the attachment folder.
Prepare the Python code
The basic structure of the Python code can be initiated by going to OpticStudio > Programming > Python > Interactive Extension. A few more modules are imported into the boilerplate, such as numpy, scipy, and matplotlib.
By trying to read the variable OSL_WORKING_DIR, we can know whether this Python code is called by optiSLang or performed manually. During the sensitivity analysis or optimization process, optiSLang will automatically vary the value of the variables defined. On the other hand, if we directly run this Python code without using optiSLang, then the value of these variables will be constant.
The code can be completed to link the variables values to the parameters of the optical system, trace rays, gather data from the detector, and process the results. For instance, in this example we convolve the detector output with a pupil function before computing key parameters such as uniformity, and contrast.
Setting up the Variables and Optimization targets in optiSLang code
After the Python code is prepared, we can start to plan for the optimization in optiSLang. The first step is to open an empty file, drag the Solver wizard in the scenery, and choose Python integration to select the code that was prepared beforehand.
In the solver wizard window, the variables can be selected by going through the python code. Highlight the variable name, right click on it, and select “Use as parameters”.
After the parameters have been set up, we should test whether the Python code can be successfully run. To do this, we open OpticStudio and turn on the Interactive Extension mode as below. Then in the Solver Wizard, we can click the down arrow and select “Test run with inputs” as shown below. If it works well, you should see in OpticStudio the dialog of Interactive Extension indicating “Connected”.
After the calculation is done, the response parameters can be selected in the same way the variables were set. In this example, we right click on the variables “Uniformity”, “Contrast”, and “TotalPower” in the Python code and select “Use as response”. Then the 3 variables will be shown in the right side column.
The next page in the wizard will ask users to define the reference value and the range of each parameter. The reference value will just follow the settings when we set the parameters in previous step. The range is up to the designer’s decision and there is no standard answer. Note that this range is absolute. During optimization, the parameters will not break the boundary. This is different to what we usually expect in the Zemax OpticStudio optimizer.
In the next page of the wizard, we need to set up the optimization criteria based on the given response. As shown below, we can just drag the response to the bottom side to set a constrain or objective. In this case, we have set up 3 objectives: to minimize contrast, uniformity, and maximize total power. We can also set up 2 additional constraints for contrast and total power to avoid some extreme cases where the result is uniform while the total power is extreme low, or the opposite case.
(Optional) Set up for parallel computing
It is not absolutely necessary, but it is possible to set up parallel computing for OptiSLang to speed up the optimization. Users can consider to do this if they have more than one Lumerical FDTD solver license. To set up this, the first step is to right click on the parametric system block, select “Edit”, and then set the Limit maximum in parallel at the bottom of the window. Set the number to any number that is not larger than 8, or the number of your total Lumerical FDTD solver license.
The same operation should be performed directly on the Python node, as illustrated below. Right click on the Python node and select “Edit”. To set up the details, we need to first click on the upper right corner’s hamburger mark, check “Properties & Placeholders”, and then click the OK button. Then we can set up the “MaxParallel” to the desired number as shown below (for illustration we set it to 6). It is also required to set up the option “Maximum in parallel” to the desired number at the bottom of the window. If you set up this parameter first, the MaxParallel in above will also be automatically changed, but it’s safer to double check it’s set as expected.
Finally, it’s suggested to check “Retry execution”, to set “Number of retries” to 20, and set “Delay between attempts” to 1000 ms. This settings avoid the race condition where OptiSLang would attempt to access the same OpticStudio instance with more than 1 thread.
Note that if the parallel setting is set to a number higher than one, the same number of instances of OpticStudio also needs to be open so that OptiSLang can create a thread for each of them.
Sensitivity and Optimization Settings
Sensitivity system can be set up by dragging the wizard to the parametric system block. The parameters and criteria will be just copied and we don’t need to set them again. By default it will suggest the AMOP model and we can keep this setting. The AMOP is an iterative sampling method, which samples designs into the design space until a target criterion has been reached – either maximum of designs or model quality.
Similarly, the optimization can be set by dragging an instance of the optimization wizard to the AMOP block. Note that when it asks the optimization method, we should select Real Run because this system will never have high-quality Metamodel of Optimal Prognosis (MOP). MOP is proposed in (Most and Will 2008) which is based on the search for the optimal input variable set and the most appropriate approximation model (polynomial or MLS with linear or quadratic basis). For the optimization algorithm, it’s suggested to use Evolutionary Algorithm, which is suitable for very non-uniform and discontinuous solution space.
Once Everything is set, go to OpticStudio to activate the ‘Interactive Extension’ in the programming tab, and then click on the Run button in OptiSlang.