How to create a User Operand using ZOS-API

In this article, we will create a User Operand using the ZOS-API to calculate the sum of absolute distance between surfaces. This operand replicates the TTHI operand. The User operand can be called by using the UDOC Merit Function operand, to control parameters and perform optimizations.

Authored By Haosheng Hu

Article Attachments

Introduction

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 Operand mode. This mode is nearly identical to the User Analysis mode, except it is used to create a user defined operand for a custom data calculation. User operands are called by using UDOC operands in the Merit Function Editor. Like the User Analysis 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 Operands 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 Operand in C#, so we need to select C#>User Operand

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

We will use this User Operand in the Double Gauss example file, to calculate the total thickness of a defined range of surfaces. The sample file is located in {Zemax}\Samples\Sequential\Objectives\Double Gauss 28 degree field.zmx’.
We will use a User Operand to read the center thickness values of surfaces which are between the Object surface and the Stop surface and then sum those values to calculate the total length of the front group of the Double Gauss system. This can also be done using the TTHI built-in operand.

There are two lines at the top of the code. These two lines list the namespaces that the application will be using frequently. The interfaces inside the namespace can then be used without specifying the full path.

using System; using ZOSAPI;

In the boilerplate template, four arguments are available.

double Hx = TheApplication.OperandArgument1; double Hy = TheApplication.OperandArgument2; double Px = TheApplication.OperandArgument3; double Py = TheApplication.OperandArgument4;

The name of the arguments cannot be customized. We will use two of them to define the surface range in our calculation.

In the boilerplate template, find the "// Add your custom code here..." and start coding.

We will define surf1 and surf2 to define the surface range and connect to the first two arguments above. Note, we are not using argument 3 and 4 in this example.

int surf1 = (int)TheApplication.OperandArgument1; int surf2 = (int)TheApplication.OperandArgument2;

We will define an object “surf” which is an instance of the interface ZOSAPI.Editors.LDE.ILDERow to read parameter values inside of the Lens Data Editor Please remember, that C# is strongly typed, which means you have to specify the datatype of your variable when you declare it.

ZOSAPI.Editors.LDE.ILDERow surf; double thic = 0;

Sum absolute values of thicknesses

We will use a “if” loop to sum all surface center thicknesses within the defined surface range. If the surface range input is valid, then the user operand will return the sum of the thicknesses. Otherwise it will return an infinite number, which means that the input is invalid.

if (surf1 <= surf2) {          for (int i = surf1; i <= surf2; i++)          {                    surf = TheSystem.LDE.GetSurfaceAt(i);                    thic = thic + Math.Abs(surf.Thickness);                             }          operandResults[0] = thic; } else {          operandResults[0] = 1e9; }

Now, the coding for our example User Operand is finished. Please note, in this example, we used operandResult[0] to place the summed thickness value at data field 0. If there are more than one computed value that needs to be placed, you can place these values in different data fields and use the Data argument in the UDOC operand to control the exact returned value.

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.

Unlike the User Analysis that is compiled as a Console application, the User Operand should be compiled as a Windows application.

If not, a console window will keep popping up when optimizing with user defined operands, since the compiled .exe file is called multiple times during optimization.

In Visual Studio, open the Project Properties, and change the Output Type of your user operand application to Windows Application. Note, the compiled User Operand application is called by the UDOC Merit Function operand as a client program, defined by its program number in the Prog# argument of UDOC. For this reason, the Assembly Name of the application should be defined as UDOCXX. XX represents a number from 00 to 99.

Then, build the solution.

Exe applications with a defined Assembly Name will be stored in the Output Path selected in the Project Properties of Visual Studio:

We need to locate the finished application and copy it to {Zemax}\ZOS-API\Operands’

Optimize System with User Operand

To check our new User Operand, we can now start OpticStudio, and open the lens file  {Zemax}\Samples\Sequential\Objectives\Double Gauss 28 degree field.zmx’

Open the Merit Function Editor and insert the UDOC operand in the Merit Function Editor:

• Prog# is the the integer value of your application name.
• Data = 0 will execute the client program and return the value in the Value column.
• Timeout sets the maximum amount of time in milliseconds OpticStudio should wait for a UDOC to compute the requested data and return control to OpticStudio. It is set as 50000 in this case.

Then the UDOC operand works like any built-in Merit Function operands.

• Hx=1 and Hy=5 are the arguments. They define the surface range.
• The value will report the total length of the front group (thickness from surface 1 to surface 6)
• The target will control that value.

Click the update button of the Merit Function Editor when all arguments are set. The calculated value will be displayed in the Value column, as expected:

In addition to performing computations, the UDOC operand can also be used to perform optimizations, just like any other Merit Function operand.

For example, let’s constrain the total length of the first group to 40 mm and keep each surface center thickness within a reasonable range. The target is set to 40 mm in the UDOC operand. We add MNCT and MXCT operands to limit the center thicknesses. Other optimization criteria could be added to the Merit Function Editor if needed, such as the RMS Spot Size or Wavefront Error.

Run the optimization with only thickness controls.

After the optimization, the total length of first group is exactly 40 mm and the center thickness of each surface meets our requirements.

KA-01830