PythonStandalone_07_TiltDecenterAndMFOperand
Last update: 17.07.2025 1import clr, os, winreg
2from itertools import islice
3
4import numpy as np
5
6class PythonStandaloneApplication(object):
7 class LicenseException(Exception):
8 pass
9 class ConnectionException(Exception):
10 pass
11 class InitializationException(Exception):
12 pass
13 class SystemNotPresentException(Exception):
14 pass
15
16 def __init__(self, path=None):
17 # determine location of ZOSAPI_NetHelper.dll & add as reference
18 aKey = winreg.OpenKey(winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER), r"Software\Zemax", 0, winreg.KEY_READ)
19 zemaxData = winreg.QueryValueEx(aKey, 'ZemaxRoot')
20 NetHelper = os.path.join(os.sep, zemaxData[0], r'ZOS-API\Libraries\ZOSAPI_NetHelper.dll')
21 winreg.CloseKey(aKey)
22 clr.AddReference(NetHelper)
23 import ZOSAPI_NetHelper
24
25 # Find the installed version of OpticStudio
26 if path is None:
27 isInitialized = ZOSAPI_NetHelper.ZOSAPI_Initializer.Initialize()
28 else:
29 # Note -- uncomment the following line to use a custom initialization path
30 isInitialized = ZOSAPI_NetHelper.ZOSAPI_Initializer.Initialize(path)
31
32 # determine the ZOS root directory
33 if isInitialized:
34 dir = ZOSAPI_NetHelper.ZOSAPI_Initializer.GetZemaxDirectory()
35 else:
36 raise PythonStandaloneApplication.InitializationException("Unable to locate Zemax OpticStudio. Try using a hard-coded path.")
37
38 # add ZOS-API referencecs
39 clr.AddReference(os.path.join(os.sep, dir, "ZOSAPI.dll"))
40 clr.AddReference(os.path.join(os.sep, dir, "ZOSAPI_Interfaces.dll"))
41 import ZOSAPI
42
43 # create a reference to the API namespace
44 self.ZOSAPI = ZOSAPI
45
46 # create a reference to the API namespace
47 self.ZOSAPI = ZOSAPI
48
49 # Create the initial connection class
50 self.TheConnection = ZOSAPI.ZOSAPI_Connection()
51
52 if self.TheConnection is None:
53 raise PythonStandaloneApplication.ConnectionException("Unable to initialize .NET connection to ZOSAPI")
54
55 self.TheApplication = self.TheConnection.CreateNewApplication()
56 if self.TheApplication is None:
57 raise PythonStandaloneApplication.InitializationException("Unable to acquire ZOSAPI application")
58
59 if self.TheApplication.IsValidLicenseForAPI == False:
60 raise PythonStandaloneApplication.LicenseException("License is not valid for ZOSAPI use")
61
62 self.TheSystem = self.TheApplication.PrimarySystem
63 if self.TheSystem is None:
64 raise PythonStandaloneApplication.SystemNotPresentException("Unable to acquire Primary system")
65
66 def __del__(self):
67 if self.TheApplication is not None:
68 self.TheApplication.CloseApplication()
69 self.TheApplication = None
70
71 self.TheConnection = None
72
73 def OpenFile(self, filepath, saveIfNeeded):
74 if self.TheSystem is None:
75 raise PythonStandaloneApplication.SystemNotPresentException("Unable to acquire Primary system")
76 self.TheSystem.LoadFile(filepath, saveIfNeeded)
77
78 def CloseFile(self, save):
79 if self.TheSystem is None:
80 raise PythonStandaloneApplication.SystemNotPresentException("Unable to acquire Primary system")
81 self.TheSystem.Close(save)
82
83 def SamplesDir(self):
84 if self.TheApplication is None:
85 raise PythonStandaloneApplication.InitializationException("Unable to acquire ZOSAPI application")
86
87 return self.TheApplication.SamplesDir
88
89 def ExampleConstants(self):
90 if self.TheApplication.LicenseStatus == self.ZOSAPI.LicenseStatusType.PremiumEdition:
91 return "Premium"
92 elif self.TheApplication.LicenseStatus == self.ZOSAPI.LicenseStatusTypeProfessionalEdition:
93 return "Professional"
94 elif self.TheApplication.LicenseStatus == self.ZOSAPI.LicenseStatusTypeStandardEdition:
95 return "Standard"
96 else:
97 return "Invalid"
98
99 def reshape(self, data, x, y, transpose = False):
100 """Converts a System.Double[,] to a 2D list for plotting or post processing
101
102 Parameters
103 ----------
104 data : System.Double[,] data directly from ZOS-API
105 x : x width of new 2D list [use var.GetLength(0) for dimension]
106 y : y width of new 2D list [use var.GetLength(1) for dimension]
107 transpose : transposes data; needed for some multi-dimensional line series data
108
109 Returns
110 -------
111 res : 2D list; can be directly used with Matplotlib or converted to
112 a numpy array using numpy.asarray(res)
113 """
114 if type(data) is not list:
115 data = list(data)
116 var_lst = [y] * x;
117 it = iter(data)
118 res = [list(islice(it, i)) for i in var_lst]
119 if transpose:
120 return self.transpose(res);
121 return res
122
123 def transpose(self, data):
124 """Transposes a 2D list (Python3.x or greater).
125
126 Useful for converting mutli-dimensional line series (i.e. FFT PSF)
127
128 Parameters
129 ----------
130 data : Python native list (if using System.Data[,] object reshape first)
131
132 Returns
133 -------
134 res : transposed 2D list
135 """
136 if type(data) is not list:
137 data = list(data)
138 return list(map(list, zip(*data)))
139
140
141if __name__ == '__main__':
142 zos = PythonStandaloneApplication()
143
144 # load local variables
145 ZOSAPI = zos.ZOSAPI
146 TheApplication = zos.TheApplication
147 TheSystem = zos.TheSystem
148 FilePath = TheApplication.ZemaxDataDir + r"\Samples\Sequential\Objectives\Cooke 40 degree field.zos"
149 TheSystem.LoadFile(FilePath, False)
150
151 # ! [e07s01_py]
152 # run the design lockdown tool to fix diameters, remove solves
153 LockdownTool = TheSystem.Tools.OpenDesignLockdown()
154 LockdownTool.UsePrecisionRounding = True
155 LockdownTool.DecimalPrecision = 2
156 LockdownTool.RunAndWaitForCompletion()
157 LockdownTool.Close()
158 # ! [e07s01_py]
159
160 # recreate the functionality of the tilt/decenter elements tool
161 # apply coordinate breaks around the 2nd lens element (surf 3/4)
162 surf3 = TheSystem.LDE.InsertNewSurfaceAt(3)
163 surf6 = TheSystem.LDE.InsertNewSurfaceAt(6)
164 CB = surf3.GetSurfaceTypeSettings(ZOSAPI.Editors.LDE.SurfaceType.CoordinateBreak)
165 surf3.ChangeType(CB)
166 surf6.ChangeType(CB)
167 # insert a dummy surface after 2nd CB
168 surf7 = TheSystem.LDE.InsertNewSurfaceAt(7)
169 surf7.Thickness = TheSystem.LDE.GetSurfaceAt(5).Thickness # the dummy carries the original thickness
170
171 # ! [e07s02_py]
172 # we're going to play with the STOP surface position, so let's put STOP on surf 1
173 TheSystem.LDE.GetSurfaceAt(1).IsStop = True
174 # ! [e07s02_p]
175
176 # ! [e07s03_py]
177 # create position solve
178 PositionSolve = TheSystem.LDE.GetSurfaceAt(5).ThicknessCell.CreateSolveType(ZOSAPI.Editors.SolveType.Position)
179 # set the properties for the position solve
180 # solve-specific properties are in ISolvePosition interface, accessed via _S_Position
181 PositionSolve._S_Position.FromSurface = 3
182 PositionSolve._S_Position.Length = 0
183 # apply position solve
184 TheSystem.LDE.GetSurfaceAt(5).ThicknessCell.SetSolveData(PositionSolve)
185 # ! [e07s03_py]
186
187 # ! [e07s04_py]
188 # create pickup solve
189 PickupSolve = TheSystem.LDE.GetSurfaceAt(6).ThicknessCell.CreateSolveType(ZOSAPI.Editors.SolveType.SurfacePickup)
190 # solve-specific properties are in ISolveSurfacePickup interface, accessed via _S_SurfacePickup
191 PickupSolve._S_SurfacePickup.Surface = 5
192 PickupSolve._S_SurfacePickup.ScaleFactor = -1
193 PickupSolve._S_SurfacePickup.offset = 0
194 PickupSolve._S_SurfacePickup.Column = ZOSAPI.Editors.LDE.SurfaceColumn.Thickness
195 # apply the pickup solve
196 TheSystem.LDE.GetSurfaceAt(6).ThicknessCell.SetSolveData(PickupSolve)
197 # ! [e07s04_py]
198
199 # ! [e07s05_py]
200 # set pickup solves for coordinate break tilt/decenter parameter cells
201 # these parameters are columns 12-16 in the Lens Data Editor (parameters 1-5)
202 surf3 = TheSystem.LDE.GetSurfaceAt(3)
203 surf6 = TheSystem.LDE.GetSurfaceAt(6)
204 ParameterPickup = surf6.GetCellAt(12).CreateSolveType(ZOSAPI.Editors.SolveType.SurfacePickup)
205 ParameterPickup._S_SurfacePickup.Surface = 3
206 ParameterPickup._S_SurfacePickup.ScaleFactor = -1
207 ParameterPickup._S_SurfacePickup.MakePickupFromCurrentColumn()
208 for i in range(12, 17):
209 surf6.GetCellAt(i).SetSolveData(ParameterPickup)
210 surf3.GetCellAt(i).DoubleValue = np.random.uniform(-0.1, .01) # assign random tilt/decenter values
211 # also, set the 'order' flag for CB#2
212 surf6.GetCellAt(17).IntegerValue = 1
213 # ! [e07s05_py]
214
215 # ! [e07s06_py]
216 # check the global rotation matrix of surface 5 (rear of tilted/decentered lens)
217 GLCR = ZOSAPI.Editors.MFE.MeritOperandType.GLCR
218 # GLCR operand only uses two input parameters: the surface number, and the rotation matrix entry number.
219 # But, GetOperandValue() expects the operand type, plus 8 more inputs because some operands use all 8.
220 # So, we will have to put 0's for the additional unused inputs in the function call.
221 RotationMatrix = np.zeros([3, 3])
222 i = 1
223 for x in range(0, 3):
224 for y in range(0, 3):
225 RotationMatrix[x][y] = TheSystem.MFE.GetOperandValue(GLCR, 5, i, 0, 0, 0, 0, 0, 0)
226 i = i + 1
227 # ! [e07s06_py]
228
229 OutFilePath = TheApplication.ZemaxDataDir + r"\Samples\API\Python\Python_07_TiltDecenterAndMFOperand.zos"
230 TheSystem.SaveAs(OutFilePath)
231
232 print('Created new file: %s' % OutFilePath)
233
234 # This will clean up the connection to OpticStudio.
235 # Note that it closes down the server instance of OpticStudio, so you for maximum performance do not do
236 # this until you need to.
237 del zos
238 zos = None
Definition: ZemaxService.cs:198