Accessing ZEMAX/ OpticStudio from Python using PyZDDE


ZEMAX (also known as OpticStudio), a leading tool for design and analysis of optical systems, provides an in-built DDE server that enables access to ZEMAX’s features externally using any Windows application. The ability to access and control ZEMAX externally allows it to be used in many interesting and powerful ways, some of which has been documented in the “ZEMAX Extensions” chapter of the ZEMAX manual. An excellent toolbox called MZDDE, developed by Derek Griffith at CSIR, already exists for users of MATLAB. When I tried to look for an equivalent toolbox in Python, I couldn’t find one, so I decided to write one myself.

PyZDDE is a Python-based toolbox for communicating with ZEMAX using the DDE messaging protocol. It is similar to MZDDE and very much inspired by it. Currently PyZDDE is not as extensive as MZDDE as it is a work in progress. However, it has some distinguishing features — it can be used with regular Python scripts and also in an interactive environment such as an IPython shell, QtConsole or IPython notebook. The ability to interact with ZEMAX from an IPython notebook using PyZDDE can be a powerful tool for both teaching and documentation. Please visit the github repository for downloading and using the code. The github repository also contains an accompanying wiki page which describes briefly on how to use the PyZDDE toolbox.

Here I would like to briefly document the communication process between ZEMAX and Python using PyZDDE. But before that I would like to show you how simple it is to communicate with Zemax using PyZDDE using a “Hello world” example (it gets and prints the running Zemax version):

import pyzdde.zdde as pyz
link = pyz.createLink()
print("Hello Zemax version: ", link.zGetVersion())

Zemax maintains a separate copy of a lens in the DDE server and all processing/modifications using any external application is done on this lens residing in the DDE server. The lens (if any) in the LDE is not modified by the DDE calls. One can of course move the lens in the DDE server to the LDE (using zPushLens) or move the lens in the LDE to the DDE server (using zGetRefresh).

The basic sequence of steps of accessing ZEMAX (the DDE server) from Python (the DDE client) are as follows:
  1. Start the ZEMAX application.
  2. Import the pyzdde module (import pyzdde.zdde as pyz) and create a DDE communication object object (link = pyz.createLink()) in a Python script, IPython QtConsole (interactive environment) or IPython notebook.
  3. Now, if you want to work with a lens in the LDE, use link.zGetRefresh() to copy the lens from the LDE to the DDE server. Else, load a ZEMAX lens file directly into the DDE server using the function link.zLoadFile(). You can of course build an optical system from scratch entirely through PyZDDE.
  4. Once all calculations (depending on the application) have completed, use link.close() to terminate the DDE communication.
  5. A separate module called arraytrace has been added for performing array ray tracing.
 The figure below illustrates the above process.
Currently, all the functions (126 in total) for accessing the ZEMAX using “data items” have been implemented. In addition, there are few utility functions. At this point in time, a distribution version is not available as the toolbox is being updated regularly. Please download the code folder to a local directory in your computer and add that directory to python search path in order to use it (please see the github wiki for details).
There are 4 types of functions in the toolbox:
  1. Functions for accessing ZEMAX using the data items as defined in the “ZEMAX EXTENSIONS” chapter of the ZEMAX manual. These functions’ names start with “z” and the rest of the function-names match the corresponding data-item defined by Zemax. For example zGetSolve, zSetSolve, etc.
  2. Helper functions to enhance the toolbox functionality beyond just the data items, such as zLensScale, zCalculateHiatus, zSpiralSpot. Also, there are other helper functions which increase the capability of the toolbox such as zOptimize2, zSetWaveTuple, etc. More functions are expected to be added over time.
  3. Few functions, which start with “ipz“, such as ipzCaptureWindow, ipzGetTextWindow can be used to embed analysis, graphic windows and text files from Zemax into an IPython Notebook or IPython QtConsole.
  4. There are several other functions which can be used independent of a running Zemax session. Examples include showZOperandList, findZOperand, findZButtonCode, etc. More functions are expected to be added over time.
All the functions prefixed with “z” or “ipz” (types 1, 2 and 3) interact with Zemax directly and hence require a Zemax session to running simultaneously.As they are instance methods of a pyzdde channel object, a pyzdde object needs to be created.

For example:

import pyzdde.zdde as pyz
ln = pyz.createLink() # create a DDE communication link

Helper functions of type 4 can be accessed the the `zdde` module directly.

For example:

pyz.zo.findZOperand("decenter")   # same as calling pyz.findZOperand("decenter")

ZEMAX settings

  1. PUSH LENS PERMISSION: All operations through the DDE affect the lens in the DDE server. In order to copy the lens the lens from the DDE server to the main application/LDE, you need to “push” the lens from the server to the LDE. TO do so, please enable the option “Allow Extensions to Push Lenses”, under File->Preferences->Editors tab.
  2. ANSI/UNICODE TEXT ENCODING: PyZDDE supports both ANSI and UNICODE text from Zemax. Please set the appropriate text encoding in PyZDDE to by calling module function pyz.setTextEncoding(text_encoding) (assuming that PyZDDE was imported as import pyzdde.zdde as pyz). By default, ANSI text encoding is set in PyZDDE. You can check the current text encoding by calling pyz.getTextEncoding() function. Please note that you need to do this only when you change the text setting in Zemax and not for every session.
  3. PURE NSC MODE: If want to work on an optical design in pure NSC mode, please start ZEMAX in pure NSC mode before initiating the communication with PyZDDE. There is no way to switch the ZEMAX mode using external interfaces.

Getting started

For getting started, please refer to the Getting started page at github wiki.

See how PyZDDE may be used interactively at Using PyZDDE interactively wiki page.
Also, there is an example IPython notebook at nbviewer.
The following is an example Python script used to produce a spiral spot diagram (see figure below).
from __future__ import print_function
import sys
import os
import matplotlib.pyplot as plt

import pyzdde.zdde as pyz

# The ZEMAX file path
zmxfp = cd + '\\ZMXFILES\\'
zmxfile = 'Cooke 40 degree field.zmx'
filename = zmxfp + zmxfile

# Create a DDE comm link
ln = pyz.createLink()

# Load a lens file into the ZEMAX DDE server
hx, hy = 0.0, 0.4
spirals, rays = 100, 6000
xb, yb, _, _, = ln.zSpiralSpot(hx, hy, 1, spirals, rays)
xg, yg, _, _, = ln.zSpiralSpot(hx, hy, 2, spirals, rays)
xr, yr, _, _, = ln.zSpiralSpot(hx, hy, 3, spirals, rays)

fig = plt.figure(facecolor='w')
ax = fig.add_subplot(111)
ax.scatter(xr, yr, s=5, c='red' , linewidth=0.35, zorder=20)
ax.scatter(xg, yg, s=5, c='lime', linewidth=0.35, zorder=21)
ax.scatter(xb, yb, s=5, c='blue', linewidth=0.35, zorder=22)
fig.suptitle('Spiral Spot')
ax.grid(color='lightgray', linestyle='-', linewidth=1)

# close the communication channel

Output of the above Python script.


UPDATE [01/26/2014]:

I have also written a Zemax knowledge-base article called “Talking to ZEMAX from Python using PyZDDE”. That article gives a more complete overview of the toolbox. The link to the knowledge-base article is [updated on 02.24.2015]

Is there anything missing?

The short answer is yes! At this time you cannot perform bulk/ array ray tracing with PyZDDE. I hope that in the near future, this issue will be resolved.

UPDATE [02/24/2015]:

As of today, there is a way to do array ray tracing. The code is still in a separate branch, as I am testing the code. It will soon be integrated into the master branch.

UPDATE [03/02/2015]:

PyZDDE is now feature complete with array ray tracing feature added to it.



20 thoughts on “Accessing ZEMAX/ OpticStudio from Python using PyZDDE

  1. This is great. I have been using the Matlab zDDE tools for some time and wanted to duplicate them in Python. Happy to find someone has already done it.

    I wrote a script using zDDE and a Matlab keybreak function (written by someone else) that allows you to visually control and align optical source beams by using the keyboard arrow keys. I will take a look at your code and see if I can do it in Python.

  2. Hello! I have next question 😉
    I wrote a script using your PyZDDE for tolerance analyzing in non-sequential mode.
    First problem: lack of function zSetTimeout () ( : C:\PyZDDE\pyzdde\ UserWarning: Not implemented. Default timeout = 1 min)
    Second: I can not understand how to open the NSC file by script. I’m doing:
    This opens the Mixed sequential /non-sequential mode ;(
    Could you help me?
    Thank you very much for all advices!
    Thanks, Eli

    • Hi Eli,

      Regarding your first question. The zSetTimeout() has not been implemented yet as I am using the DDE module from pywin32. As far as I am aware (I could be wrong), there isn’t a way to set the DDE call’s timeout using the pywin32 package. Very recently, I have also added a secondary DDE module (but currently that is only used as a backup in case the pywin32 module import fails). I think the timeout function should be able to use this module. However the implementation of timeout function is something for the future or based on demand.

      I am currently very busy with my PhD work, so getting the time to work on PyZDDE is a little hard for me right now. But PyZDDE is an opensource project, and anyone is more than welcome to contribute [hint, hint ! :-)].

      To answer your second question, if you know for sure that the file that you are trying to open is going to use ZEMAX in purely Non-Sequential mode, then you will first need to switch the ZEMAX mode to pure NSC from ZEMAX menu, and then use PyZDDE to load a purely NSC based file (basically do the same as you have done above). I have checked this, and it seems to work. There is no way to switch the ZEMAX mode from PyZDDE as ZEMAX has not exposed any dataitem to do this.

      Hope that helps.

      • Thank you for your answers!
        I am a neophyte in Python, help write the code still can not, but I’m learning…
        Zemax I know/use for a long time. I see good prospects in the joint use of Python/Zemax.

  3. Hi, Indranil,
    The problem with timeout looks very considerable, a one minute – this is extremely short for a real design.
    I have to ask, is it solvable problem? What should I do? Some cumbersome operations can be divided into cycles, but this is not enough.
    I fear that I’ll spend a lot of time, and ahead of a deadlock…
    Or do use only ZPL commands?.. But ZPL is very limited. I do not know what to do …
    What do you think?
    Thank you very much for all advices!
    Thanks, Eli

    • Hi Eli,

      I have, in the past, typically broken down operations into smaller cycles. However, I agree that it is not possible to do so always. I think the timeout problem is solvable (I haven’t spent any time on this issue till now, so I could be wrong). Will it be ok with you if I looked into it during the coming weekend? Also, if you have little time you could look into the file PyZDDE / pyzdde /
      I don’t use ZPL through PyZDDE much, unless I really have to. In fact using ZPL through the DDE (using Matlab or Python or even C) is really clunky.
      I will spend some time during the weekend to see if there is an easy fix (I don’t think I will have any time before that).
      Also, you are most welcome.

      • Hi Indranil,
        I am very grateful to you for your prompt and positive response!
        I will wait for the result. My knowledge of Python is not yet enough, of course.
        But I’m learning and experimenting.
        Thought how to make easy development package I have now 🙂

        Thanks, Eli

      • Hi Eli,
        You are most welcome, the pleasure is mine. I have taken a quick look and it seems that the “timeout” issue can be fixed relatively easily (I will be using the module. I will make some quick and dirty changes to a couple of files and send you those files. I will also request you to see if those changes worked for you. Once you are happy with it, I will clean it up and put back the changes into the main repository. Can you please send me an e-mail address to which I can send the files. Please use the contact page to send the e-mail address. I think I this can be done much before the weekend (but I am not promising it before the weekend :-)).

  4. WOW!
    I sent you the e-small. Thank you so much!
    P.S. I use Win7/64-bit and Zemax 64-bit (13 Rel.2 SP2), it may be important.

  5. Dear Indranil,
    It works! Thank you very much! I am very grateful to you!
    Except one bug (due to lack of utils.pyzddeutils, see e-mail) everything went perfectly!
    I will continue to work with PyZDDE and learn Python, and I hope someday I can help in the creation of a full-functional library.
    Good luck in your PhD! Thank you again!
    Regards, Eli

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s