Question

Tryind to get Started with ZOSPy

  • 22 September 2023
  • 10 replies
  • 303 views

Userlevel 1

Hi is have a few Beginner Questions about ZOSPy.

Sofar the few times I needed to do combine Zemax with Python I used PyZDDE.
PyZDDE doesn’t seem to work any more with the Ansys Version of Zemax, so that I now have to switch.

I just started to look into ZOS-API and try to wrap my head around how it works.

For the start I have a few simple questions about ZOSPy:

  • How do I control to which Zemax File ZOSPy connects?
  • Does is it just connect to what ever Zemax File is open?
  • What happens if several Zemax files are open?
  • Do need to put the Phython Skipt/Jupyter Notebook in the same directory as the Zemax File?
  • Do I need to switch Zemax into Interactive Extension mode before Running the Python script?

 

 


10 replies

Userlevel 2

Hi Georg!

 

Glad to see that you are using ZOSPy to interact with OpticStudio through Python! I have answered your questions 

How do I control to which Zemax File ZOSPy connects?

  1. In case you want to start OpticStudio as a standalone application, you can use
    zos = zp.ZOS()
    zos.wakeup()
    zos.create_new_application() # or zos.connect_as_extension()
    oss = zos.get_primary_system()
    oss.load("absolute_path_to_file.zmx")
    See also this example in the documentation. Make sure that it is the absolue filepath.
     
  2. In case you have two instances of OpticStudio open and want to connect to a specific one
    connect_as_extension has a keyword parameter, instancenumber, that can be used to specify to which instance ZOSPy should connect. This instance number is by default set to 0, which means that it makes the first connection it can find. When you start the interactive extension in OpticStudio, the popup gives an instance number:

    If you specify that instance number, it will connect to that specific instance. E.g.:
    zos = zp.ZOS()
    zos.wakeup()
    zos.connect_as_extension(instancenumber=1)

 

Does is it just connect to what ever Zemax File is open?

No. While it could do so in PyZDDe,ZOSPy will not. There are two ways to initiate a connection

  1. As a standalone application. This means that ZOSPy will start an instance of OpticStudio that you will not see present on your task bar. 
  2. As an extension. See case 2 of my previous answer. This requires you to first enable the interactive extension (under programming → interactive extension in OpticStudio
     

What happens if several Zemax files are open?

I think the other two answers provide saufficient information on this

 

Do need to put the Phython Skipt/Jupyter Notebook in the same directory as the Zemax File?

Not at all! See also the first answer. If you connect as extension, it can connect to the OpticStudio instance that is open. If you create a standalone application, you can use absolute paths to load (and save) OpticStudio files

 

 

Hopes this answers your questions fully. If not, feel free to ask again. Also, have a look at our example folder on GitHub or the example section in the documentation.

Userlevel 1

Thanks that will help me a lot to get started.

Userlevel 1

Hi,

I reworked my PyZDDE script into ZOSPy and it went pretty well sofar.

But I have few additional Questions:

  • do I need to disconnect in the end (and even if it is not necessary is there a command to do it)?
  • can I trigger somehow the “Update All” button, that updates all Windows?
  • Is there a way to save the Zemax File under it’s current name? If I use “oss.save()” is get a warning that I need to use “save_as()” first.
Userlevel 2

Hi Georg,

Glad all is going well so far. With regards to your questions:

 

Do I need to disconnect in the end (and even if it is not necessary is there a command to do it)?

This depends on how you are connecting. If you run a single script and the script finished, everything will be disconnected. If you however use something more dynamic such as a Jupyter notebook, the connection stays live until you disconnect. This can be achieved with the following command:

zos.Application.CloseApplication()

 

Can I trigger somehow the “Update All” button, that updates all Windows?

The OpticStudioSystem has a property ’LensUpdateMode’ that should enable you to specify the behaviour. However, due to your comment, I have noticed that there is a bug in ZOSpy which prevents you from directly using that property. I have made a pull request on GitHub to fix that, but until that is released, you can use

oss._System.UpdateMode = zospy.constants.LensUpdateMode.None_
oss._System.UpdateMode = zospy.constants.LensUpdateMode.EditorsOnly
oss._System.UpdateMode = zospy.constants.LensUpdateMode.AllWindows

Please note that None has a trailing ‘_’.

 

Is there a way to save the Zemax File under it’s current name? If I use “oss.save()” is get a warning that I need to use “save_as()” first.

Thank you for reporting this. We have not ran into this problem yet, but I estimate you open the file yourself and then connect as extension. In that case, ZOSPy indeed does not account check the filename (yet). I will look into this.

For now, there are 2 solutions:

  1. (recommended way) first connect as extension and then open the file using oss.load(absolute_filepath)
  2. change the _OpenFile attribute in your script, e.g. by oss._OpenFile = oss.SystemFile. This is the (private) attribute that ZOSPy checks to determine which file is used. However, note that we are looking into optimizing saving and loading. It is possible that we drop the _OpenFile attribute and not consider it a breaking change as it is a private attribute. Hence, option 1 is favourable. I will try to address this swiftly. 
Userlevel 2

Hi Georg,

 

We have just released a new version of ZOSPy, v1.1.1. This version fixed the bug with LensUpdateMode, So you can now call the following code to specify the update mode!

oss.LensUpdateMode = zospy.constants.LensUpdateMode.None_
oss.LensUpdateMode = zospy.constants.LensUpdateMode.EditorsOnly
oss.LensUpdateMode = zospy.constants.LensUpdateMode.AllWindows

 

Userlevel 1

Thanks

I have two other questions.

If I close  Interactive Extension Mode manually or with

zos.Application.CloseApplication()

and than start it again I need to restart the  Extension Mode and the script I get in the Scritp a warning that it can’t connect to a second extension. Is there a way to clear this? (Sofar I have restarted the Kernel.)

The second question is connected to this.

I want to try to write scrip for batch processing several Zemax files.
For opening the files would probably use:

oss.load("absolute_path_to_file.zmx")

but how do I close the files correctly so that I can open the next file without running into the problem mentioned above or into the limit of Zemax files I can open simutaniously?

 

 

Userlevel 3
Badge

Hi Georg,

Which of these 2 errors do you get?

  1. RuntimeError: Only one Zemax application can exist within runtime
  2. ValueError: Cannot have more than one active ZOS instance

After calling zos.Application.CloseApplication(), you shouldn't get the first error. Immediately after calling zos.Application.CloseApplication() (or closing the connection in OpticStudio) you should be able to connect again using zos.connect_as_extension(). I tested this on my system, and it works as expected.

However, you may get the second error, which means you tried to create a new ZOS object (using e.g. zos = zp.ZOS()). You get this error because the ZOS-API only allows for a single connection. You can get rid of this error in various ways:

  1. Delete the existing ZOS instance, e.g. by running del zos. Note that objects deleted using del still need to be removed by the garbage collector, and in my experience the garbage collector doesn't always do this immediately, especially not in Jupyter notebooks. You may therefore still get the second error if you create a new ZOS instance shortly after deleting the old instance.
  2. Restart the script or the Jupyter kernel. This will work in all cases, since all connections are closed.

The nice thing is that you don't need to do this. The ZOS object manages the connection with the API, and needs to be instantiated only once. Using the same instance, you can reconnect to OpticStudio after disconnecting.

 

So this should work:

import zospy as zp

# Initialize the ZOS-API connection
zos = zp.ZOS()

# Connect to OpticStudio
oss = zos.connect_as_extension()

# Disconnect from OpticStudio
zos.Application.CloseApplication()

# Reconnect to OpticStudio
oss = zos.connect_as_extension()

But this won’t work:

import zospy as zp

# Initialize the ZOS-API connection
zos = zp.ZOS()

# Connect to OpticStudio
oss = zos.connect_as_extension()

# Disconnect from OpticStudio
zos.Application.CloseApplication()

# Initialize the ZOS-API connection
zos = zp.ZOS() # ValueError: Cannot have more than one active ZOS instance.

# Reconnect to OpticStudio
oss = zos.connect_as_extension()

A more in-depth explanation of this behaviour is given in this issue: https://github.com/MREYE-LUMC/ZOSPy/issues/19

Regarding your second question: loading a new file with oss.load should also close the previously loaded file. You can also open multiple files by creating multiple optical systems, e.g. using

oss2 = zos.create_new_system()
oss2.load("absolute/path/to/file.zmx")

but this does not really make sense in extension mode, because you can only see a single system. In ZOSPy, it is recommended to only use multiple optical systems in standalone mode, due to weird behaviour of the ZOS-API when creating multiple optical systems in extension mode:

 

Thank you for trying ZOSPy, and feel free to get back to us if you've got more questions!

Userlevel 1

Thanks, I really ran zos = zp.ZOS()  a second time. 

That should be easy enough to avoid.

Userlevel 1

Hi,

I was running into some unintuitive behaviour with   oss.load()    and   oss.save() when calling a non existing file.


It happened because I didn’t realize that the Test files I was using were “.zos” and not “.zmx”.

 

When doing so I didn’t got an error message after calling  oss.load()  (even with just and empty string as file path) and the script just seemed to continue to work on an empty Zemax file.


When I made changes to surfaces they seem to stick, even when loading another (non existing) file, untill I restarted the Kernel.

Calling   oss.save()  after loading an non existing file also didn’t resulted in an Error massage, even if it doesn’t look like the file was actually saved (or I couldn’t figure out where it was saved).

Userlevel 3
Badge

Hi,

Your observations are correct, as for now we follow the default behaviour of the ZOS-API when working with file paths. The ZOS-API itself does not complain if you point it to a non-existing file, and will keep working on the old system. The same goes for the save function, which silently fails if you feed it a non-existing or relative path. This is indeed unintuitive and it would be nice if the ZOS-API threw an exception instead of failing silently, but this is clearly not the case.

We are planning to change this in a future release of ZOSPy, which will allow specifying relative file paths and raise an exception if you specify a path to a non-existing file. You can read about this in this discussion on GitHub: https://github.com/MREYE-LUMC/ZOSPy/discussions/34. For now, you will need to manually check if files exist and convert them to absolute paths. You can do so using pathlib's resolve method (which can do both) or using os.path.exists and os.path.abspath.

Reply