Soon, we will launch a new and updated customer portal, which is an important step toward providing our customers with one place to learn, interact, and get help.
Learn more.

How to create a User Analysis using ZOS-API

In this article, we will show how to create a User Analysis using the new ZOS-API.NET.
  • Vary thickness of a surface in steps of 10 μm
  • Run QuickFocus to bring image plane into focus
  • For each step, calculate FFT MTF at several spatial frequencies
  • Plot data
Thomas Aumeyr
Programming Zemax


An application programming interface (ZOS-API) has been developed for OpticStudio that enables connections to, and customization of, the application using the latest software technology. While ZOS-API relies on a COM interface, it is rooted in .NET libraries, and as such programming with the API can be done in a number of languages.
The connection between your application program and OpticStudio is classified in 1 of 4 Program Modes. These modes can be generally grouped into 2 categories: 1) Full Control (Standalone and User Extensions modes), in which the user generally has full control over the lens design and user interface and 2) Limited Access (User Operands and User Analysis modes), in which the user is locked down to working with a copy of the existing lens file. For the purpose of this article we will focus on the User Analysis Mode. This mode is linked to a single analysis window. This mode is nearly identical to User Operands mode, except it is used to populate data for a custom analysis. The data is displayed using the modern graphics provided in OpticStudio for most analyses. Like the User Operand Mode, This mode would not allow changes to the current lens system or to the user interface (i.e. in this mode only changes to a copy of the system are allowed). User analyses can be written using either C++ (COM) or C# (.NET) – depending on the user’s comfort with either language.

Open new boilerplate template

Let’s create a User Analysis in C#, so we need to select C#>User Analysis

A Windows Explorer opens with the solution folder ‘..\Documents\Zemax\ZOS-API Projects\CSharpUserAnalysisApplication1’. Also, Visual Studio opens with your new solution. The solution contains a boilerplate code that can be used as the basis for any User Analysis.

Reading in Lens Data Editor

We create this User Analysis specifically for the Double Gauss example file located in
       ‘..\Zemax\Samples\Sequential\Objectives\Double Gauss 28 degree field.zmx’
We will vary the thickness of Surface 6 by +/- 1mm in steps of 10 um and study the behaviour of the Modulation Transfer Function (MTF) at 30, 40 and 50 cycles/mm.

First, we add 3 lines at toppest of the codes. We need to set using the namespace and then can use the interfaces inside without specifying full path.

using ZOSAPI.Editors.MFE;
using ZOSAPI.Editors;
using ZOSAPI.Tools.General;

Then, find the "// Add your custom code here..." and start coding.

We change the window title, and read in the Lens Data Editor (LDE) and declare arrays to hold the analysis data.
// Add your custom code here...
TheAnalysisData.WindowTitle = "MTF vs. Thickness";
ILensDataEditor TheLDE = TheSystem.LDE;
ILDERow surf6 = TheLDE.GetSurfaceAt(6);
double[] MTFs30 = new double[201];
double[] MTFs40 = new double[201];
double[] MTFs50 = new double[201];
double[] surf6Thic = new double[201];

Please remember, that C# is strongly typed, which means you have to specify the datatype of your variable when you declare it.

Setting up the Merit Function Editor

Next, we add 3 operands, change them to MTFS (Modulation transfer function, sagittal), set the sampling (Parameter 1) to 64x64 and the frequency (Parameter 4) to 30, 40 and 50 cycles/mm respectively. As these blocks are very similar, you should to make use of Copy & Paste (Ctrl+C, Ctrl+V).
IMeritFunctionEditor TheMFE = TheSystem.MFE;
IMFERow Operand_1 = TheMFE.AddOperand();
IEditorCell Op1Samp = Operand_1.GetOperandCell(MeritColumn.Param1);
Op1Samp.IntegerValue = 2;  
IEditorCell Op1Freq = Operand_1.GetOperandCell(MeritColumn.Param4);
Op1Freq.DoubleValue = 30;
IMFERow Operand_2 = TheMFE.AddOperand();
IEditorCell Op2Samp = Operand_2.GetOperandCell(MeritColumn.Param1);
Op2Samp.IntegerValue = 2;
IEditorCell Op2Freq = Operand_2.GetOperandCell(MeritColumn.Param4);
Op2Freq.DoubleValue = 40;
IMFERow Operand_3 = TheMFE.AddOperand();
IEditorCell Op3Samp = Operand_3.GetOperandCell(MeritColumn.Param1);
Op3Samp.IntegerValue = 2;
IEditorCell Op3Freq = Operand_3.GetOperandCell(MeritColumn.Param4);
Op3Freq.DoubleValue = 50;


To fill the arrays with data, we vary the thickness of surface 6 and run a QuickFocus before computing the Merit Function. Then we can write each Operand value in its respective data array.
double step = 0.01;
surf6.Thickness = surf6.Thickness - 100 * step;
for (int i = 0; i < 201; i++)
       surf6.Thickness = surf6.Thickness + step;
       surf6Thic[i] = surf6.Thickness;
       IQuickFocus quickFocus = TheSystem.Tools.OpenQuickFocus();
       quickFocus.Criterion = QuickFocusCriterion.SpotSizeRadial;
       quickFocus.UseCentroid = true;
       MTFs30[i] = Operand_1.Value;
       MTFs40[i] = Operand_2.Value;
       MTFs50[i] = Operand_3.Value;


Plot results

Finally, we use TheAnalysisData to create a specific plot type and populate the data. If you have ever created a user analysis using DDE you will find the plotting via ZOS-API much more straightforward and flexible.
IUser2DLineData linePlot = TheAnalysisData.Make2DLinePlot
       ("MTF vs. Surface 6 Thickness", 201, surf6Thic);
linePlot.AddSeries("MTF 30 cycles/mm", ZemaxColor.Color1, 201, MTFs30);
linePlot.AddSeries("MTF 40 cycles/mm", ZemaxColor.Color2, 201, MTFs40);
linePlot.AddSeries("MTF 50 cycles/mm", ZemaxColor.Color3, 201, MTFs50);
linePlot.XLabel = "Surface 6 Thickness [mm]";
linePlot.YLabel = "MTF";

There are currently four types of a data that can be calculated and displayed:
  • 2D Line Plot
  • 2D Grid Plot
  • 2D RGB Grid Plot
  • Text Data 
Note that only one format of data is allowed per User Analysis.

Save, build and move executable

We build the solution as a Release. Using Debug mode is fine when you’re debugging, but when distributing a plugin, you should always use Release mode, since the Debug libraries are not redistributable. Therefore let’s switch to release mode and rebuild.

We need to navigate to our solution folder (in ‘..Documents\Zemax\ZOS-API Projects\...’), locate the project folder and then move the finished application to
       ‘C:\Program Files\Zemax OpticStudio\ZOS-API\User Analysis’

Run User Analysis

To check our new User Analysis, we can now start OpticStudio and open the lens file
       ‘..\Documents\Zemax\Samples\Sequential\Objectives\Double Gauss 28 degree field.zmx’
Under the Programming tab, we go to User Analyses, there should now be the analysis that we just made.

When we click it, we get the results of our User Analysis!


Further Possibilities 

In this example, the surface being varied, the variation range and step size and the spatial frequencies at which the MTF is reported are hard coded, but it is straightforward to add a settings dialogue to allow these inputs to be specified by the user. This way we can create a User Analysis that can be used more universally. Settings are stored in a simple dictionary consisting of key-value pairs. The dictionary is empty when your Analysis is first launched. However, any entries you add to the Settings Dictionary will be saved between updates and can be stored with the session.
The following types can be stored in the settings:
  • Integer
  • Float (32-bit)
  • Double (64-bit)
  • Boolean
  • String
  • Byte