How to create a User Analysis using ZOS-API

Theapplication programming interface (ZOS-API) available for OpticStudio enables connections to, and customization of, the application using the latest software technology. This article shows how to create a bespoke User Analysis using ZOS-API.

Authored By Thomas Pickering


Article Attachments


The ZOS-API is a COM-based interface rooted in .NET libraries that provides users with the ability to program in a number of languages including C++, Python, and Matlab.

For the purpose of this article we will focus on the Limited Access-type User Analysis Mode. This mode is linked to a single analysis window and is used to populate data for a custom analysis. The data is displayed OpticStudio graphics windows. Like the User Operand Mode, this mode only allows changes to a copy of the current system. 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

To create a User Analysis in C#, we need to select Programming...C#...User Analysis.

new boilerplate

Windows Explorer opens with the solution folder {Zemax}\ZOS-API Projects\CSharpUserAnalysisApplication1. Your default C# development program will also open. The solution contains a boilerplate code that can be used as the basis for any User Analysis.

Reading in the 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 +/- 1 mm in steps of 10 um and study the behavior of the Modulation Transfer Function (MTF) at 30, 40 and 50 cycles/mm.

reading in lens data editor

First, add 3 lines at the top of the code. We need to set namespace and then can use the interfaces inside without specifying the 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, 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 be able 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;

merit function editor

Using a loop to compute analysis data points

To fill the arrays with data, 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;


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, 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 ({Zemax}\ZOS-API Projects\...), locate the project folder and then move the finished application to {Zemax}\ZOS-API\User Analysis.

Run the User Analysis

To check our new User Analysis, we can now start OpticStudio and open the lens file {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.

user analysis

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

MTF vs Thickness

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


Was this article helpful?
0 out of 0 found this helpful



Article is closed for comments.