PythonStandalone_08_NSCEDetectorData
Last update: 17.07.2025 1import clr, os, winreg
2from itertools import islice
3
4import math
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 \Samples\Non-Sequential\Scattering\Abg scattering surface.zos
145 # 1. delete object 3 (specular ray blocking)
146 # 2. insert detector polar position at same pos as object 2
147 # - retrieve object 2 rotation matrix, match orientation
148 # - size = 20
149 # remove absorb material from obj 4
150 # -. run ray trace
151 # 3/4. Save/Load detector data
152 # 5/6. get detector data for detector polar
153 # - retrieve single-value data with GetDetectorPolarData()
154 # - retrieve data grid (all pixels) with GetAllDetectorPolarDataSafe()
155 # 7/8. get detector rectangle data
156 # - retrieve single-value data with GetDetectorData()
157 # - retrieve data grid (all pixels) with GetAllDetectorDataSafe()
158 # 9/10. get coherent detector rectangle data
159 # - retrieve single-value data with GetCoherentData()
160 # - retrieve data grid (all pixels) with GetAllCoherentDataSafe()
161
162 # load local variables
163 ZOSAPI = zos.ZOSAPI
164 TheApplication = zos.TheApplication
165 TheSystem = zos.TheSystem
166
167 file = "\\Samples\\Non-sequential\\Scattering\\ABg scattering surface.zos"
168 DataDir = TheApplication.ZemaxDataDir
169 filepath = DataDir + file
170 TheSystem.LoadFile(filepath, False)
171
172 # ! [e08s01_py]
173 # Delete unnecessary object from NSCE
174 TheSystem.NCE.RemoveObjectAt(3)
175 # Add detector co-located with another general object
176 obj3 = TheSystem.NCE.InsertNewObjectAt(3)
177 DetectorPolar = obj3.GetObjectTypeSettings(ZOSAPI.Editors.NCE.ObjectType.DetectorPolar)
178 obj3.ChangeType(DetectorPolar)
179 # Set the detector polar radial size to 20
180 obj3.GetCellAt(12).DoubleValue = 20 # cell 12 corresponds to par 2 in NCE
181 # ! [e08s01_py]
182
183 # ! [e08s02_py]
184 # Co-locate object 3 with object 2 (here, could alternatively use Ref Object flag)
185 # need to use placeholders for out parameters
186 success, R11, R12, R13, R21, R22, R23, R31, R32, R33, Xo, Yo, Zo = TheSystem.NCE.GetMatrix(2, 11, 12, 13, 21, 22, 23, 31, 32, 33, 1, 2, 3)
187 obj3.XPosition = Xo
188 obj3.YPosition = Yo
189 obj3.ZPosition = Zo
190 # Conversion from rotation matrix to tilts described in KBA "Rotation Matrix and Tilt About X/Y/Z in OpticStudio"
191 obj3.TiltAboutX = math.degrees(math.atan2(-1 * R23, R33))
192 obj3.TiltAboutY = math.degrees(math.asin(R13))
193 obj3.TiltAboutZ = math.degrees(math.atan2(-1 * R12, R11))
194 # ! [e08s02_py]
195
196 # Remove ABSORB material from object 4
197 TheSystem.NCE.GetObjectAt(4).Material = ''
198 # Run the ray trace
199 RayTrace = TheSystem.Tools.OpenNSCRayTrace()
200 RayTrace.ClearDetectors(0) # clear the old detector data!
201 RayTrace.ScatterNSCRays = True
202 RayTrace.UsePolarization = False
203 RayTrace.SplitNSCRays = False
204 RayTrace.IgnoreErrors = True
205 RayTrace.RunAndWaitForCompletion()
206 RayTrace.Close()
207
208 # The next two steps are technically unnecessary in this case; since we just ran the
209 # ray trace, the results are already there. But, we demonstrate usage here anyways
210 # ! [e08s03_py]
211 # Save detector data -- allows ray trace results to be loaded later
212 # For detector polar, the file type is .DDP; for detector rectangle, it's .DDR
213 DetectorPolarFile = TheApplication.ZemaxDataDir + "\\Samples\\API\\Python\\detector3polar.DDP"
214 DetectorRectFile = TheApplication.ZemaxDataDir + "\\Samples\\API\\Python\\detector4rect.DDR"
215 TheSystem.NCE.SaveDetector(3, DetectorPolarFile)
216 TheSystem.NCE.SaveDetector(4, DetectorRectFile)
217 # ! [e08s03_py]
218
219 # ! [e08s04_py]
220 # Load detector data -- for analyzing previous ray trace results
221 # For detector polar, the file type is .DDP; for detector rectangle, it's .DDR
222 DetectorPolarFile = TheApplication.ZemaxDataDir + "\\Samples\\API\\Python\\detector3polar.DDP"
223 DetectorRectFile = TheApplication.ZemaxDataDir + "\\Samples\\API\\Python\\detector4rect.DDR"
224 TheSystem.NCE.LoadDetector(3, DetectorPolarFile, False)
225 TheSystem.NCE.LoadDetector(4, DetectorRectFile, False)
226 # ! [e08s04_py]
227
228 # ! [e08s05_py]
229 # Here we read in the detector polar data from ZOS
230 # GetPolarDetectorData() is very similar to the MF operand NSDP;
231 # can retrieve RMS (degrees), total power, chromaticity, etc. (see NSDP in OpticStudio Help)
232 # Note: GetPolarDetectorData() uses an enumeration for data type, shown here
233 DataFlag_Power = ZOSAPI.Editors.NCE.PolarDetectorDataType.Power
234 DataFlag_ChromX = ZOSAPI.Editors.NCE.PolarDetectorDataType.Cx
235 DataFlag_ChromY = ZOSAPI.Editors.NCE.PolarDetectorDataType.Cy
236 # need to use placeholders for out parameters
237 success, DetPolarData_RadialRMS = TheSystem.NCE.GetPolarDetectorData(3, -4, DataFlag_Power, 0) # obj=3, pix=-4, data=power enum
238 success, DetPolarData_ChromX = TheSystem.NCE.GetPolarDetectorData(3, 0, DataFlag_ChromX, 0) # obj=3, pix=0, data=chromaticityX enum
239 success, DetPolarData_ChromY = TheSystem.NCE.GetPolarDetectorData(3, 0, DataFlag_ChromY, 0) # obj=3, pix=0, data=chromaticityY enum
240 # ! [e08s05_py]
241
242 # ! [e08s06_py]
243 # To retrieve the entire data array (power, tristim. X/Y/Z, etc. for each pixel)
244 # can use GetAllPolarDetectorDataSafe(), or GetAllPolarDetectorData()
245 # Note: GetPolarDetectorData uses an enumeration for data type, shown here
246 DataFlag_TriX = ZOSAPI.Editors.NCE.PolarDetectorDataType.TriX
247 DataFlag_TriY = ZOSAPI.Editors.NCE.PolarDetectorDataType.TriY
248 DataFlag_TriZ = ZOSAPI.Editors.NCE.PolarDetectorDataType.TriZ
249 DetPolarData_TriX = TheSystem.NCE.GetAllPolarDetectorDataSafe(3, DataFlag_TriX) # Tristimulus X value polar pixel
250 DetPolarData_TriY = TheSystem.NCE.GetAllPolarDetectorDataSafe(3, DataFlag_TriY) # Tristimulus Y value on each pixel
251 DetPolarData_TriZ = TheSystem.NCE.GetAllPolarDetectorDataSafe(3, DataFlag_TriZ) # Tristimulus Z value on each pixel
252 # ! [e08s06_py]
253
254 # ! [e08s07_py]
255 # Here we read in the detector rectangle data
256 # GetDetectorData() is very similar to the Merit Function operand NSDD
257 # can retrieve Std Deviation, # of rays, total power, etc.; data calculated over whole detector or individual pixel
258 # need to use placeholders for out parameters
259 success, DetRectangleData_StdDev = TheSystem.NCE.GetDetectorData(4, -4, 0, 0) # obj=4, pix=-4, data=0
260 # ! [e08s07_py]
261
262 # ! [e08s08_py]
263 # To retrieve the entire data array (flux, flux/area, etc.) for all pixel data,
264 # can use GetAllDetectorDataSafe(), or GetAllDetectorData().
265 # The 'Data' inputs for these functions (parameter 2) can be found in the API syntax help,
266 # under the listing for GetDetectorData().
267 DetRectangleData_Flux = TheSystem.NCE.GetAllDetectorDataSafe(4, 0) # total flux on each pixel
268 DetRectangleData_FluxArea = TheSystem.NCE.GetAllDetectorDataSafe(4, 1) # flux/area on each pixel
269 DetRectangleData_FluxSAP = TheSystem.NCE.GetAllDetectorDataSafe(4, 2) # flux/solid angle pixel on each pixel
270 # ! [e08s08_py]
271
272 # Finally, let's read coherent data
273 # The coherent data isn't very meaningful in this example, but it serves to demonstrate API usage and functionality
274 # ! [e08s09_py]
275 # Read in the detector rectangle coherent data
276 # GetCoherentData() is very similar to the operand NSDC
277 # can retrieve real, imaginary, amplitude, power with 'data' input
278 # for pix=0, get sum on detector; pix>0 gives single pixel data
279 success, DetRectangle_CoherentAmp = TheSystem.NCE.GetCoherentData(4, 0, ZOSAPI.Editors.NCE.DetectorDataType.Amplitude, 0) # obj=4, pix=0, data=2
280 success, DetRectangle_CoherentPower = TheSystem.NCE.GetCoherentData(4, 0, ZOSAPI.Editors.NCE.DetectorDataType.Power, 0) # obj=4, pix=0, data=3
281 # ! [e08s09_py]
282
283 fileName = DataDir + r"\Samples\API\Python\Python8_NSCEDetectorData.zos"
284 TheSystem.SaveAs(fileName)
285 print('Saved file: %s' % fileName)
286 # from here, we can plot or analyze any detector we want!
287
288 # This will clean up the connection to OpticStudio.
289 # Note that it closes down the server instance of OpticStudio, so you for maximum performance do not do
290 # this until you need to.
291 del zos
292 zos = None
Definition: ZemaxService.cs:198