Automated recurring documentation for optical designs with ZOS-API and LaTeX

This article will explain how to automate the documentation of optical designs with ZOS-API and LaTeX. An example for a comparison of 2 systems with 8 different evaluation graphics is given.

Authored By Helke Karen Hesse


Article Attachments


Every optical designer has regularly to face the challenge of providing extensive documentations either after having finished an optical design or for comparing two different design approaches in an intermediate state of development.
Sometimes, especially when working with multi-zoom configurations, the necessary figures can easily exceed the number of one hundred or in some cases even several hundred, and those can no longer be exported manually, nor can they be imported into a document image by image.
In the following, I would like to point out in a simple example of comparing two different designs:

  • How to automatically export the images of both designs by use of ZOS-API.
  • How to automatically generate a documentation by use of open-source software LaTeX.

In the article, we will:

  1. demonstrate how to export and name the evaluation figures of two similar optical designs which will be provided in the attachment (both are taken from one of my optical design courses).
  2. setup and compile a LaTeX sample file which integrates these images such that a side-by-side performance comparison is possible.

The design files are named as “Cooke_52mm” and “Tessar_52mm” and should be placed in the same folder for easier handling.  Our goal is to compare them

  • in terms of MTF vs. lps (line pairs in lp/mm). We therefore call the FFT MTF analysis and plot the contrast for all fields for a maximum frequency of 30 lp/mm.
  • in terms of MTF-vs-Field at 5, 10, 15, 20, 25 and 30 lp/mm. We therefore call the FFT vs Field analysis and plot the contrast for different frequency versus the field.
  • in terms of transverse ray aberrations. We therefore call the Ray Fan Analysis.
  • in terms of axial color. We therefore call the Longitudinal Aberration Analysis.

We will use a ZOS-API standalone application in Python to generate the necessary evaluation data and to export these into custom PDF images that can later be compared side-by-side.

API and LaTeX

While ZOS-API is commonly known within the community of Zemax users, the open-source software LaTeX is probably not. However, it is common standard among mathematicians and other scientists because it allows easily coding of mathematical functions and provides an extensive framework for formatting and printing scientific publications. LaTeX can be considered as sort of a programming language, which allows to easily describe complex formulas and formatting. The final documents are obtained by compiling the code.
I will not go into detail neither for ZOS-API, nor for LaTeX, but would like to refer to the available documentations for complete insight. The given simple examples demonstrate the power of combining these two options for generating extensive documentations.

For more information about ZOS-API, see the free tutorial.
For more information about LaTeX, see

Python API: Export and name the evaluation figures of an optical design

The general setup of a ZOS-API project file is well known and will not be discussed in further detail.
Generate a new standalone application folder and project file:


Then open the main file which is located in the newly generated folder. It is usually denoted as “”, where “X” is a number.
Since we want to open different files, we will first need to load further packages and to insert the definition for a function, which opens the files. We add the following at the beginning of the file:

import tkinter as tk
from tkinter import filedialog
from tkinter.filedialog import askdirectory

The tkinter package is the standard Python interface to the Tcl/Tk GUI (Graphical User Interface) toolkit. 
Then, we add the following definition after the definition-section. This will allow a file dialog to pop-up to select the optical design file.

# used for getting the directory
def search_for_file(self):
        currdir = os.getcwd()
        root = tk.Tk()
        tempdir = filedialog.askopenfilename()
        return tempdir

Now we can start with the alterations of the main program which basically begins after the #Insert Code here line.
We start with determining the MTF frequencies under consideration and adding a code to open the files and ask for the prefix for the exported figures:

# Insert Code Here

# frequencies of MTF - adapt if other values are wanted
# (or insert an input to ask for the frequencies)
# total number of frequencies for MTF vs Field is 6 in Zemax!
f = [5, 10, 15,  20, 25, 30]

# data for opening the systems and naming the images
file_path = [""] * 10
prefix = [""] * 10

# Choose Files to Open
k = 1
i = 0
while (k == 1):
    file_path[i] = zos.search_for_file()
    # now, ask for Prefix
   if (file_path[i] !=""):
       prefix[i] = input("Enter Prefix for File")
        i = i + 1
        k = 0

num_systems = i

If you make a test run, you will find, that a file dialog opens, and you can now choose as many files as you wish one after another until you abort the dialog.

For each file, a prefix will be requested to be entered in the command line of the program. The prefix will be used to name the figures. So, for example, if we select the “Cooke_52mm.zmx” in the file dialog, enter “Cooke” as the prefix. Now, a main part of the program is designed, which loops over the selected files and calls for different functions that will make the evaluations and export the images.

# loop over all systems
for k in range(0, num_systems):
    #load the system
    TheSystem.LoadFile(file_path[k], False)
   path = file_path[k].rsplit('/', 1)[0] + "/"

    # Load_File: MTF_vs_LPS
    import MTF_vs_LPS
    #Function for evaluation and image generation
    MTF_vs_LPS.Evaluate_MTF_vs_LPS(path, prefix[k], TheSystem, ZOSAPI, zos)

We still have to generate the file, that defines the MTF calculation. This follows the usual procedure of generating a new file in the same subfolder of the ZOS-API Application and then filling this with the appropriate code:

import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np

def Evaluate_MTF_vs_LPS(path, prefix, System, ZOSAPI, zos):

    #get number of field points

    #store mtf vs field values
    #store field values
    field_values = [0] * n_fields

    #save field values in variable
    for f in range(0, n_fields, 1):
        field_values[f] = System.SystemData.Fields.GetField(f + 1).Y

    # Open mtf fan analysis
    mtf = System.Analyses.New_Analysis(ZOSAPI.Analysis.AnalysisIDM.FftMtf)
    #Open Settings and adjust as needed (could also be done interactively)
    mtf_settings = mtf.GetSettings()
    mtf_settings.MaximumFrequency = 30
    mtf_settings.SampleSize = ZOSAPI.Analysis.SampleSizes.S_256x256

    #grab the results from the analysis windows
    mtf_results = mtf.GetResults()

    #lets save thy system with window open!

    # Define output figure size
    plt.figure(figsize=(10, 7))

    #define plot colors
   colors = ('navy','blue','darkgreen','limegreen', 'orange', 'red', 'darkred',\
'chocolate', 'blueviolet')

    # loop over all Fields and get data
    for FieldNum in range(0, n_fields, 1):
        #get data for field
        data = mtf_results.GetDataSeries(FieldNum)
        #extract data for x and y values (x => array / y=> Matrix)
        xRaw = data.xData.Data
        yRaw = data.yData.Data

        #transform into usable format for plotting
        x = list(xRaw)
        y = zos.reshape(yRaw, yRaw.GetLength(0), yRaw.GetLength(1), True)

       #Plot Tangential and Sagittal
       plt.plot(x, y[0], color=colors[FieldNum])
       plt.plot(x, y[1], linestyle='dashed', color=colors[FieldNum])

    #Generate Legend
   lege = [''] * n_fields * 2
    while FieldNum < n_fields:
       lege[leg_index]=str(round(System.SystemData.Fields.GetField(FieldNum+1).Y,2))+" tan"
       lege[leg_index+1]=str(round(System.SystemData.Fields.GetField(FieldNum+1).Y,2))+" sag"
        FieldNum = FieldNum+1
        leg_index = leg_index + 2
    #Name figure
   plt.title('MTF vs Line Pairs in lp/mm')
    #Format plot
    plt.xlim([0, 30])
   plt.ylim([0, 1])
    plt.xticks(np.arange(0, 30, 5))
    plt.yticks(np.arange(0, 1,0.1))
   plt.figlegend(lege, loc='lower center', ncol=n_fields)
   plt.savefig(path+prefix + "_MTF_vs_LP" + ".pdf")


Now, without detailed explanation, the other output files are generated and included in the main file, which should finally look like stated below. The files are attached to this article.

# loop over all systems
for k in range(0, num_systems):
    #load the system
    TheSystem.LoadFile(file_path[k], False)
   path = file_path[k].rsplit('/', 1)[0] + "/"

    # Load_File: MTF_vs_LPS
    import MTF_vs_LPS
    #Function for evaluation and image generation
    MTF_vs_LPS.Evaluate_MTF_vs_LPS(path, prefix[k], TheSystem, ZOSAPI, zos)

    import MTF_vs_Field
    MTF_vs_Field.Evaluate_MTF_vs_Field(path, prefix[k], f, TheSystem, ZOSAPI, zos)

    import Transverse_Ray
    Transverse_Ray.Evaluate_Aberrations(path, prefix[k], TheSystem, ZOSAPI, zos)

    import Axial_Color
    Axial_Color.Evaluate_Axial_Color(path, prefix[k], TheSystem, ZOSAPI, zos)

# This will clean up the connection to OpticStudio.
# Note that it closes down the server instance of OpticStudio,
# so you for maximum performance do not do
# this until you need to.
del zos
zos = None

Python API: Running the code

Starting the main Python program will pop up the file dialog.


We choose the “Cooke_52mm.zmx” first, enter “Cooke” in the command line for the prefix. Then the file dialog will pop up again. Now we select the “Tessar_52mm.zmx” file, enter “Tessar” for the prefix in the command line. When the file dialog pops up again, we click “Cancel”.

The program will now start and finish after a while. When we open the folder in which the Zemax Files are stored there should now be an additional collection of PDFs:


For example, the MTF vs. Field plot will look like the following:


In the next step, we will see how to generate a basic framework in LaTeX, which will allow us to import the images such that they can be compared side-by-side.

LaTeX: Generating a basic framework

For this documentation, the LaTeX Version “MiKTeX” has been used, along with the TeXnicCenter-Editor. However, you can use any other suitable LaTeX compatible editor for this purpose.

The necessary framework consists of a main file and subfiles for different chapters, which can be imported in the main file if necessary. It has to be built once for each and every type of documentation, and since it works with placeholders for the different filenames, editing will only be necessary in the main file later on.
We generate a new folder within our current folder, name it e.g. as “Documentation”, and generate empty latex files as shown below. The file “Documentation” is the main file, the other file will contain the code to insert the corresponding images into the document.
Also generate a new folder called “images” into which you copy the pdf-images.


The main document “Documentation.tex” consists of different parts, first of which is the preliminary part which introduces the different packages needed for compiling this specific document. This comprises the document class, the formatting, the graphics packages, the language packages, the compiling packages, etc… It also may contain some formatting information.

\documentclass[pdftex,a4paper, 12pt]{scrreprt}
\usepackageraphich]{babel} % if you write in English
\usepackageraphicx, grffile}
\usepackage[pdftex]{hyperref} %use pdf tex
\usepackage{lscape} % landscape format
\usepackage[section]{placeins}%leave images in section

The next part will contain the placeholders, which refer to the designs that we chose for comparison. The placeholders are defined to be the same prefixes, that we used when exporting the images.

%insert your placeholders for the different systems
%these are the prefixes that were defined earlier when exporting the images

The last part is the code for the document’s content. Here, the main page is described and filled with content, and the subfile and its code are imported for compiling.

%here begins the code
%%% First page %%%
% Title
{\huge \bfseries Using LaTex with Zemax–- Demonstration \par}
\rule{\linewidth}{0.5mm} \\[0.4cm]
{ \large The First Document \\[0.4cm] }
\rule{\linewidth}{0.5mm} \\[1.5cm]

In the subfile “01_Comparison_of_Images.tex”, the code for the image import is given. This file makes use of the placeholders and does not contain any Zemax-File-specific information. Once this file is written and adapted to the specific needs, it will never have to be modified again.

Since the basic code for importing the images is always similar, this file – no matter how many different images shall be imported – is very easy to generate by copying, pasting, and replacing, and will finally allow to import hundreds of images within a few seconds of compiling.

LaTeX: Test of the code for the images of the two designs

In our simple example, there are 8 images, that need to be imported. Some are in landscape orientation, others are in portrait. The code comprises the import and positioning of the images, such that two comparative images are placed on one page. Here is an example for the first two images:

\section[MTF vs. Line Pairs]{MTF vs. LP}


\caption{MTF vs. Line Pairs - \prefixone}
\caption{MTF vs. Line Pairs - \prefixtwo}

Compiling the main file with export to PDF file, will now show the final output. Of course, additional formatting – e.g. headlines, foot lines will be needed and inserted by specific LaTeX command in a real documentation. However, since this is based on custom preferences, no further details is given in this overview, but again I would like to refer to the LaTeX documentation for deeper insight.



This article has shown how to automate documentation using LaTeX and Python ZOS-API. In my own project documentations, it is very useful as I have – depending on the project – more than 500 different images to compare.
In a system with 6 configurations (which is for example a usual setup when considering different zoom positions), and 15 different evaluations, the tool described here will run the analyses and import the 180 images quickly  in a complete direct graphical comparison of the two files.

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



Article is closed for comments.