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.
import pyzdde.zdde as pyz link = pyz.createLink() print("Hello Zemax version: ", link.zGetVersion()) link.close()
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
).
- Start the ZEMAX application.
- 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. - 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 functionlink.zLoadFile()
. You can of course build an optical system from scratch entirely through PyZDDE. - Once all calculations (depending on the application) have completed, use
link.close()
to terminate the DDE communication. - A separate module called arraytrace has been added for performing array ray tracing.

- 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. - 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 aszOptimize2
,zSetWaveTuple
, etc. More functions are expected to be added over time. - 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. - 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.
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
- 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.
- 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 asimport pyzdde.zdde as pyz
). By default, ANSI text encoding is set in PyZDDE. You can check the current text encoding by callingpyz.getTextEncoding()
function. Please note that you need to do this only when you change the text setting in Zemax and not for every session. - 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.
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 ln.zLoadFile(filename) 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.set_aspect('equal') 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) ax.set_xlabel('x') ax.set_ylabel('y') fig.suptitle('Spiral Spot') ax.grid(color='lightgray', linestyle='-', linewidth=1) ax.ticklabel_format(scilimits=(-2,2)) # close the communication channel pyz.closeLink() plt.show()
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 http://www.zemax.com/support/knowledgebase/talking-to-zemax-from-python-using-pyzdde [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.
.
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.
Hello Dave,
I hope you will find pyZDDE useful. It will be awesome to see the functionality, which you are talking about, in Python. Surely it seems interesting.
Great work! Thank you! Please help me “Tracing Large Numbers of Rays”, need an example. Thanks in advance, Eli
Thanks, Eli. I haven’t implemented a way to trace large number of rays as documented in the Zemax manual yet. If not someone else, then I will certainly to do it in the near future.
I’ll wait!
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\zdde.py:213: UserWarning: Not implemented. Default timeout = 1 min)
Second: I can not understand how to open the NSC file by script. I’m doing:
link0.zLoadFile(filename)
link0.zNSCTrace(1,0)
link0.zPushLens(1)
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.
Thanks.
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.
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 / dde_backup.py.
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.
Regards,
Indranil.
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 dde_backup.py) 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 :-)).
Regards,
Indranil.
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.
Got it. My system too is similar to yours. Also, would you have some code (python/matlab/c) that I can use to test that the function actually works before I send it to you? Since I personally didn’t have this requirement for what I do, I don’t have any real code to test. If you don’t have something currently, then its alright.
Good news! I will send the “children’s code” that I use.
I sent by email. Many thanks and respect, Eli
Just sent you a mail with a couple of files.
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
Great article!!
Thanks, Xiao.