ZOS-API using Python

This article outlines differences between using Python and other ZOS-API languages and review how Python (2.x) handles enumerated variables. Working sample code snippets are provided.

Authored By Michael Humphreys


Article Attachments


For an introduction to the ZOS-API, please visit the resources listed in the references section prior to attempting use of these code snippets.

COM based languages: pywin32

ZOS-API is written as a .NET library and uses win32com to communicate with pure COM based languages, such as Python. There is a Python extension called pywin32 (https://sourceforge.net/projects/pywin32/) that implements win32com. There are 2 versions of the pywin32 module, a 32-bit and a 64-bit. There is a random issue with how the 32-bit pywin32 handles specific interfaces, so we recommend only using 64-bit Python along with 64-bit pywin32 module. The ZOS-API can run with either Python 2.x or Python 3.x as long as the pywin32 is 64-bit and references the correct Python build.

Enumerated variables

In order to efficiently implement enumerated variables in Python, ZOSAPI stores all the values in a single dictionary called constants. Although Python 3.4 natively supports enumerated variables, the ZOSAPI specifically stores all enumerated variables in a single dictionary entry to ensure compatibility with Python 2.x. Enumerated variables, or enums, are settings that have discrete values and are typically set in the OpticStudio UI from a dropdown list. Although enums can appear anywhere ZOSAPI requires a discrete value from a built in setting, they are most often encountered as arguments to methods; anytime you see a method that has an argument starting with ZOSAPI.xxx then you will most likely need to use the constants dictionary to map the enum.

To use enums in Python, you need to reference the correct property of the constants dictionary. Although these should remain relatively stable from release to release, there could be minor modifications (such as capitalization) or additions between ZOS-API versions, so please make sure you are referencing the correct property for the version of ZOS-API that you are using. An example of properly using an enum in ZOS-API is:

TheAnalyses = TheSystem.Analyses
newWin = TheAnalyses.New_Analysis(constants.AnalysisIDM_RayTrace)

There is a complete list for OpticStudio in the attachments.

PsfRotation Enumeration

Cast interfaces

Although Python supports interface inheritance, derived interfaces do not inherit base interface methods/properties. Therefore, many Analysis (such as .GetSettings() method) and Tools (such as .RunAndWaitForCompletion()method) interfaces require Python to explicitly cast the member to the actual interface that contains the desired properties or methods. In order to access the methods/properties of a derived interface you must first obtain a reference to the correct interface type, which is what CastTo() does. Looking at the API documentation, one can see that the IAS_RayTrace Interface inherits it’s members from the IAS_ interface. Anytime this appears in the documentation and you want to change something from that particular interface, you will need to use thesettings = CastTo(newWin_Settings, "IAS_RayTrace") function.

IAS_RayTrace Interface

Sample code

Running a Single Ray Trace

This code will run a Single Ray Trace for a full field marginal ray and then save the data to a text file. Note that currently, you will need to use the .GetTextFile() method to read the results of many analysis windows, including the Single Ray Trace.

# initiates a new analyses instance
TheAnalyses = TheSystem.Analyses
newWin = TheAnalyses.New_Analysis(constants.AnalysisIDM_RayTrace)

# creates a reference to the analysis settings interface
newWin_Settings = newWin.GetSettings()

# casts the settings interface to the IAS_RayTrace
# Python does not support derived or inherited interfaces so every time you need to
#    modify a settings of a derived/inherited interface you will need to cast to
#    that proper interface
settings = CastTo(newWin_Settings, "IAS_RayTrace")

# modifies the settings of the single ray trace
settings.hx = 0
settings.hy = 1
settings.px = 0
settings.py = 0
settings.Type = constants.RayTraceType_DirectionCosines

# runs the single ray trace

# gets the results from the single ray trace and saves to an output file
newWin_Results = newWin.GetResults()

Creating a circular aperture

This code will open an already created file called sc.zmx, will apply a Circular Aperture to the first surface, and then save a copy of the lens to a new file.

# Insert Code Here

# create Application reference and load file
TheApplication = zosapi.TheApplication
sample_dir = TheApplication.SamplesDir
TheSystem = TheApplication.LoadNewSystem('C:\Temp\sc.zmx')

# initialize the LDE and get reference for the first surface
TheLDE = TheSystem.LDE
Surface_1 = TheLDE.GetSurfaceAt(1)

# creates a circular aperture on the first surface
sA = Surface_1.ApertureData.CreateApertureTypeSettings(constants.SurfaceApertureTypes_CircularAperture)

# changes the aperture and applies the changes to the surface
sA._S_CircularAperture.MaximumRadius = 8
# sA._S_CircularAperture.ApertureXDecenter
# sA._S_CircularAperture.ApertureYDecenter
# sA._S_CircularAperture.MinimumRadius

# saves the file

Thickness solve for paraxial focus

This code will create a singlet from scratch, set the System Explorer...Aperture...Aperture Type...Float By Stop Size, and then apply a Marginal Ray Height Thickness Solve on the last surface using Height = 0 and a Pupil Zone = 0 to find the paraxial focus. (Note that if you want to find the marginal focus, you should change the PupilZone from 0 to 1). For the code to save the lens, you will need to have a C:\Temp that exists.

# Insert Code Here
#Creates a new Sequential Mode file
TheApplication = zosapi.TheApplication;
TheSystem = TheApplication.CreateNewSystem(constants.SystemType_Sequential);

#Sets the system aperture value
TheSystemData = TheSystem.SystemData;
TSD_Aperture = TheSystemData.Aperture;
#Sets the system aperture to FloatByStopSize (see Interface Doc for list of values)
#Use Surface.SemiDiameter to set aperture value now
TSD_Aperture.ApertureType = 3;

#Creates an instance of the Lens Data Editor
TheLDE = TheSystem.LDE;

#Sets Radius, Thickness, Material and Semi-Diameter for Surface 1
Surface_1 = TheLDE.GetSurfaceAt(1);
Surface_1.Radius = 100;
Surface_1.Thickness = 5;
Surface_1.Material = 'N-BK7';
Surface_1.SemiDiameter = 10;

#Inserts surface & sets Radius and Semi-Diameter
Surface_2 = TheLDE.InsertNewSurfaceAt(2);
Surface_2.Radius = -100;
Surface_2.SemiDiameter = 10;

#Sets a thickness on the last surface; finds the paraxial focus with Hy=1 and Py=0
Solver2 = Surface_2.ThicknessCell.CreateSolveType(constants.SolveType_MarginalRayHeight);
Solver2_MRH = Solver2._S_MarginalRayHeight;
Solver2_MRH.Height = 1;
Solver2_MRH.PupilZone = 0;

#Saves the system to the C:\Temp folder

Physical Optics Propagation (POP)

This code provides a set of examples for using POP with the ZOSAPI. It is broken down into sections including: 1) POP in the LDE, 2) Inserting a Slide Surface, 3) Modifying POP.CFG file, 4) Running POP & Saving Results, and 5) Changing Aperture Type. This example assumes that a valid lens is already loaded in TheSystem and that the "C:\Temp" folder already exists. When using the .MODIFYSETTINGS() method (i.e., the HasAnalysisSpecificSettings property is False), you will need to manually save a CFG file before trying to run this code.
You can save the CFG file by opening up POP Settings and clicking Save. For the MODIFYSETTINGS keyword, there is no way of changing the Display...Data option, so if you want to get both the Irradiance and Phase data, you should set the Display...Data to Irradiance, click Save, navigate to  '...Documents\Zemax\Configs' and rename POP.CFG to POP_IRRADIANCE.CFG. Next, change Display...Data to Phase, click Save, navigate to '...Documents\Zemax\Configs' and rename POP.CFG to POP_PHASE.CFG

# 1. physical optics in LDE
s1 = TheLDE.GetSurfaceAt(1)
s1_pop = s1.PhysicalOpticsData
s1_pop.ResampleAfterRefraction = True
s1_pop.AutoResample = True

# 2. Change to Slide Surface & update slide image
s2 = TheLDE.GetSurfaceAt(2)
s2_type = s2.GetSurfaceTypeSettings(constants.SurfaceType_SlideSurface)
s2_type.fileName = 'barchart.BMP'

# 2.5 Modify Settings Method
# Modify settings method
pop = TheSystem.Analyses.New_Analysis_SettingsFirst(constants.AnalysisIDM_PhysicalOpticsPropagation)
pop_setting = pop.GetSettings()
pop_settings = CastTo(pop_setting, 'IAS_')

if pop.HasAnalysisSpecificSettings == False:
    cfg = TheApplication.ZemaxDataDir + '\\Configs\\POP_IRRADIANCE.CFG'
    pop_settings.ModifySettings(cfg, 'POP_END', (TheLDE.NumberOfSurfaces - 1))

    # changes beam to Guassian, 128x128, Auto, 10x15um waist
    pop_settings.ModifySettings(cfg, 'POP_BEAMTYPE', 0)
    pop_settings.ModifySettings(cfg, 'POP_SAMPX',3)
    pop_settings.ModifySettings(cfg, 'POP_SAMPY',3)
    pop_settings.ModifySettings(cfg, 'POP_PARAM1', 0.010)    # Waist X
    pop_settings.ModifySettings(cfg, 'POP_PARAM2', 0.015)    # Waist Y
    pop_settings.ModifySettings(cfg, 'POP_AUTO', 1)          # needs to go after POP_PARAM


# 3. Run POP and save output
pop_results = pop.GetResults()

# --------------------------------------------------------------------------------------------

# this is a repeat of 2.5 and 3; it was easier to simply copy & paste and change POP_PHASE.CFG file
pop2 = TheSystem.Analyses.New_Analysis_SettingsFirst(constants.AnalysisIDM_PhysicalOpticsPropagation)
pop_setting = pop2.GetSettings()
pop_settings = CastTo(pop_setting, 'IAS_')

cfg = TheApplication.ZemaxDataDir + '\\Configs\\POP_PHASE.CFG'
pop_settings.ModifySettings(cfg, 'POP_END', (TheLDE.NumberOfSurfaces - 1))
pop_settings.ModifySettings(cfg, 'POP_BEAMTYPE', 0)
pop_settings.ModifySettings(cfg, 'POP_SAMPX', 3)
pop_settings.ModifySettings(cfg, 'POP_SAMPY', 3)
pop_settings.ModifySettings(cfg, 'POP_PARAM1', 0.010)  # Waist X
pop_settings.ModifySettings(cfg, 'POP_PARAM2', 0.015)  # Waist Y
pop_settings.ModifySettings(cfg, 'POP_AUTO', 1)  # needs to go after POP_PARAM
pop_results = pop2.GetResults()

# --------------------------------------------------------------------------------------------

# 4. System Aperture Float By Stop Size
TheSystem.SystemData.Aperture.ApertureType = 3

NSC Raytrace with smoothing

The following snippet will setup a NSC Raytrace using the ISystemTool interface. A smoothing factor of 3 will either be applied directly to the Detector Object in the NSCE or to the Detector Viewer based on a user-defined variable of smoothType. The code will also either clear the detectors using the NSDD MFE/ZPL protocol or will directly call the ClearDetectors method. It will then print out the number of rays that strike the Detector Rectangle located at Object 2.

TheNCE = TheSystem.NCE
smoothType = 1
if smoothType == 0:
    # applies the smoothing directly to a Detector Viewer Window
    det = TheSystem.Analyses.New_Analysis(constants.AnalysisIDM_DetectorViewer)
    det_setting = det.GetSettings()
    det_settings = CastTo(det_setting,'IAS_DetectorViewer')
    det_settings.Smoothing = 3

    # applies the smoothing to the NSCE detector object
    o2 = TheNCE.GetObjectAt(2)
    cVal = o2.GetObjectCell(constants.ObjectColumn_Par7).Col
    row = CastTo(o2,'IEditorRow')
    row.GetCellAt(cVal).Value = 3

# opens instance of NSCRayTrace; note only one instance can be opened across all ZOS
NSCRayTrace = TheSystem.Tools.OpenNSCRayTrace()

# clears detectors
clearDet = 0
if clearDet == 0:
    TheSystem.NCE.GetDetectorData(0, 0, 0) # same as NSDD(1,0,0,0)
    NSCRayTrace.ClearDetectors(0)  # same as clicking Clear on Ray Trace

# setup ray trace
NSCRayTrace.SplitNSCRays = True
NSCRayTrace.ScatterNSCRays = True
NSCRayTrace.UsePolarization = True
NSCRayTrace.IgnoreErrors = True
NSCRayTrace.SaveRays = False

# cast NSCRayTrace to parent ISystemTool to get common methods such as RunAndWaitForCompletion
baseTool = CastTo(NSCRayTrace, 'ISystemTool')

# gets number of rays that hit the detector
ret, pixel_val = TheNCE.GetDetectorData(2,-3,0)
print('number of rays that hit the detector: ',pixel_val)

NSC Coat/Scatter

This code snippet will modify the coating & scattering properties of object 2 and change object 3 to a Grid Sag. It will set the properties of face 0 to 50% Lambertian scattering, Face 1 to an I.50 coating and Face 2 to Reflective. For Object 3, the GRD file 'use_this_one.grd' needs to be saved to the '...\Documents\Zemax\Objects\Grid Files'.

# Coating and scattering on Object 2
Object2 = TheNCE.GetObjectAt(2)
# Changes face 0 scattering to Lambertian
# ZOSAPI.Editors.NCE.ObjectScatteringTypes.Lambertian
Object2_Scatter = Object2.CoatScatterData.GetFaceData(0).CreateScatterModelSettings(constants.ObjectScatteringTypes_Lambertian)
Object2_Scatter._S_Lambertian.ScatterFraction = 0.5
Object2.CoatScatterData.GetFaceData(0).NumberOfRays = 2  # this has to go after the ChangeScatterModelSettings
# Changes face 1 to I.50 coating
Object2.CoatScatterData.GetFaceData(1).FaceIs = constants.FaceIsType_ObjectDefault
# ZOSAPI.Editors.NCE.FaceIsType.ObjectDefault
Object2.CoatScatterData.GetFaceData(1).Coating = 'I.50'
# Changes face 2 to reflective
# ZOSAPI.Editors.NCE.FaceIsType.Reflective
Object2.CoatScatterData.GetFaceData(2).FaceIs = constants.FaceIsType_Reflective

# Change object 3 to grid lens and set file
Object3 = TheNCE.GetObjectAt(3)
# ZOSAPI.Editors.LDE.SurfaceType.GridSagLens
Object3_Type = Object3.GetObjectTypeSettings(constants.ObjectType_GridSagLens)
#NOTE: file must already exist in 'Objects/Grid Files' and be properly formatted
Object3_Type.FileName1 = 'use_this_one.grd'


Article: ZOS-API.NET: An Overview
Article: How to build and optimize a singlet using ZOS-API with Python
Article: How to create a User Analysis using ZOS-API
Webinar: Matlab & ZOS-API.NET


Was this article helpful?
0 out of 4 found this helpful



Article is closed for comments.