class PythonStandaloneApplication(object): def __init__(self): EnsureModule('{EA433010-2BAC-43C4-857C-7AEAC4A8CCE0}', 0, 1, 0) EnsureModule('{F66684D7-AAFE-4A62-9156-FF7A7853F764}', 0, 1, 0) self.TheConnection = EnsureDispatch("ZOSAPI.ZOSAPI_Connection") print(self.TheConnection) self.TheApplication = self.TheConnection.CreateNewApplication() print(self.TheApplication) self.TheSystem = self.TheApplication.PrimarySystem print(self.TheSystem) def __del__(self): if self.TheApplication is not None: self.TheApplication.CloseApplication() self.TheApplication = None self.TheConnection = None
class PythonStandaloneApplication(object): class LicenseException(Exception): pass class ConnectionException(Exception): pass class InitializationException(Exception): pass class SystemNotPresentException(Exception): pass def __init__(self): # make sure the Python wrappers are available for the COM client and # interfaces EnsureModule('ZOSAPI_Interfaces', 0, 1, 0) # Note - the above can also be accomplished using 'makepy.py' in the # following directory: # {PythonEnv}\Lib\site-packages\wind32com\client\ # Also note that the generate wrappers do not get refreshed when the # COM library changes. # To refresh the wrappers, you can manually delete everything in the # cache directory: # {PythonEnv}\Lib\site-packages\win32com\gen_py\*.* self.TheConnection = EnsureDispatch("ZOSAPI.ZOSAPI_Connection") if self.TheConnection is None: raise PythonStandaloneApplication.ConnectionException( "Unable to intialize COM connection to ZOSAPI") self.TheApplication = self.TheConnection.CreateNewApplication() if self.TheApplication is None: raise PythonStandaloneApplication.InitializationException( "Unable to acquire ZOSAPI application") if self.TheApplication.IsValidLicenseForAPI == False: raise PythonStandaloneApplication.LicenseException( "License is not valid for ZOSAPI use") self.TheSystem = self.TheApplication.PrimarySystem if self.TheSystem is None: raise PythonStandaloneApplication.SystemNotPresentException( "Unable to acquire Primary system") def __del__(self): if self.TheApplication is not None: self.TheApplication.CloseApplication() self.TheApplication = None self.TheConnection = None def OpenFile(self, filepath, saveIfNeeded): if self.TheSystem is None: raise PythonStandaloneApplication.SystemNotPresentException( "Unable to acquire Primary system") self.TheSystem.LoadFile(filepath, saveIfNeeded) def CloseFile(self, save): if self.TheSystem is None: raise PythonStandaloneApplication.SystemNotPresentException( "Unable to acquire Primary system") self.TheSystem.Close(save) def SamplesDir(self): if self.TheApplication is None: raise PythonStandaloneApplication.InitializationException( "Unable to acquire ZOSAPI application") return self.TheApplication.SamplesDir def ExampleConstants(self): if self.TheApplication.LicenseStatus is constants.LicenseStatusType_PremiumEdition: return "Premium" elif self.TheApplication.LicenseStatus is constants.LicenseStatusType_ProfessionalEdition: return "Professional" elif self.TheApplication.LicenseStatus is constants.LicenseStatusType_StandardEdition: return "Standard" else: return "Invalid"
class MisAlignmentGenerator(object): class LicenseException(Exception): pass class ConnectionException(Exception): pass class InitializationException(Exception): pass class SystemNotPresentException(Exception): pass def __init__(self): # make sure the Python wrappers are available for the COM client and # interfaces EnsureModule('ZOSAPI_Interfaces', 0, 1, 0) # Note - the above can also be accomplished using 'makepy.py' in the # following directory: # {PythonEnv}\Lib\site-packages\wind32com\client\ # Also note that the generate wrappers do not get refreshed when the # COM library changes. # To refresh the wrappers, you can manually delete everything in the # cache directory: # {PythonEnv}\Lib\site-packages\win32com\gen_py\*.* self.TheConnection = EnsureDispatch("ZOSAPI.ZOSAPI_Connection") if self.TheConnection is None: raise MisAlignmentGenerator.ConnectionException("Unable to intialize COM connection to ZOSAPI") self.TheApplication = self.TheConnection.CreateNewApplication() if self.TheApplication is None: raise MisAlignmentGenerator.InitializationException("Unable to acquire ZOSAPI application") if self.TheApplication.IsValidLicenseForAPI == False: raise MisAlignmentGenerator.LicenseException("License is not valid for ZOSAPI use") self.TheSystem = self.TheApplication.PrimarySystem if self.TheSystem is None: raise MisAlignmentGenerator.SystemNotPresentException("Unable to acquire Primary system") def __del__(self): """Boiler plate""" if self.TheApplication is not None: self.TheApplication.CloseApplication() self.TheApplication = None self.TheConnection = None def OpenFile(self, filepath, saveIfNeeded): """Boiler plate""" if self.TheSystem is None: raise MisAlignmentGenerator.SystemNotPresentException("Unable to acquire Primary system") self.TheSystem.LoadFile(filepath, saveIfNeeded) def CloseFile(self, save): """Boiler plate""" if self.TheSystem is None: raise MisAlignmentGenerator.SystemNotPresentException("Unable to acquire Primary system") self.TheSystem.Close(save) def SamplesDir(self): """Boiler plate""" if self.TheApplication is None: raise MisAlignmentGenerator.InitializationException("Unable to acquire ZOSAPI application") return self.TheApplication.SamplesDir def ExampleConstants(self): """Boiler plate""" if self.TheApplication.LicenseStatus is constants.LicenseStatusType_PremiumEdition: return "Premium" elif self.TheApplication.LicenseStatus is constants.LicenseStatusType_ProfessionalEdition: return "Professional" elif self.TheApplication.LicenseStatus is constants.LicenseStatusType_StandardEdition: return "Standard" else: return "Invalid" def SpecialGauss(self,mean, sigma): """ A gaussian distribution whith the tails clipped at 2 sigma.""" rand = 10.0 * sigma while abs(rand) > 2.0 * sigma: rand = random.gauss(0,sigma) return(rand + mean) def RemoveAllMtfRows(self): """Remove all the oparands in the merit function editor""" mfe = self.TheSystem.MFE nRows = mfe.NumberOfOperands dmfsrow = -1 CastTo(mfe,'IEditor').DeleteRowsAt(0, nRows) def AddREAOp(self, surf, missCenter, missPupilX, missPupilY, REAXp): """ Add operand of type REAX or REAY to MFE. This means how much the (almost)chief ray is going to miss the vertex of the surface. surf is the surface that we are aiming for missCenter is the amount we miss the center of the surface by. missPupilX and missPupilY is how much we miss the center of the aperture by. REAXp is true if we are aiming in x, false if we are aiming in y """ mfe = self.TheSystem.MFE op = mfe.AddOperand() if REAXp: op.ChangeType(constants.MeritOperandType_REAX) else: op.ChangeType(constants.MeritOperandType_REAY) p1 = op.GetOperandCell(constants.MeritColumn_Param1) p1.IntegerValue = surf p3 = op.GetOperandCell(constants.MeritColumn_Param3) p3.DoubleValue = 0.0 p4 = op.GetOperandCell(constants.MeritColumn_Param4) p4.DoubleValue = 0.0 p5 = op.GetOperandCell(constants.MeritColumn_Param5) p5.DoubleValue = missPupilX p6 = op.GetOperandCell(constants.MeritColumn_Param6) p6.DoubleValue = missPupilY op.Target = missCenter wt = op.GetOperandCell(constants.MeritColumn_Weight) wt.DoubleValue = 1.0 def AddREAOperands(self, surface, missx, missy, pupilx, pupily): """ Add MF operands """ self.AddREAOp(surface, missx, pupilx, pupily, True) self.AddREAOp(surface, missy, pupilx, pupily, False) def SurfaceDisplacement(self, surface, missx, missy): """ Displace the mirror vertex randomly """ lde = self.TheSystem.LDE row = CastTo(lde,'IEditor').GetRowAt(surface - 1) colx = CastTo(row,'ILDERow').GetSurfaceCell(constants.SurfaceColumn_Par1); colx.DoubleValue = missx; coly = CastTo(row,'ILDERow').GetSurfaceCell(constants.SurfaceColumn_Par2); coly.DoubleValue = missy; def ThicknessRandomizer(self, sigma): """ All planes with a thickness different from 0 gets moved around a little""" lde = self.TheSystem.LDE nSurf = lde.NumberOfSurfaces for n in range(0,nSurf): surf = lde.GetSurfaceAt(n) thickness = surf.Thickness if thickness != 0: surf.Thickness = self.SpecialGauss(thickness, sigma) def LocalOptimize(self, target): """ Start local optimization, keep tunning until MF is below target, local optimization converges, or 500 minutes has passed. """ lopt = self.TheSystem.Tools.OpenLocalOptimization() lopt.Algorithm = constants.OptimizationAlgorithm_DampedLeastSquares lopt.Cycles = constants.OptimizationCycles_Infinite lopt.NumberOfCores = 8 print("Starting local optimization") CastTo(lopt, "ISystemTool").Run() mf = lopt.InitialMeritFunction counter = 0 print("Starting loop, mf = " + str(mf)) while mf > target: time.sleep(6) mf = lopt.CurrentMeritFunction print("mf = " + str(mf)) counter = counter + 1 if( counter > 10): break CastTo(lopt, "ISystemTool").Cancel() CastTo(lopt, "ISystemTool").Close() return(mf) def ListMirrorPlanes(self): """ Get a list containing the indexes of mirror surfaces """ lde = self.TheSystem.LDE nSurf = lde.NumberOfSurfaces surfList = [] for n in range(0,nSurf): surf = lde.GetSurfaceAt(n) if surf.Material == 'MIRROR': surfList.append(n) return(surfList) def createPickupsAndSetOrder(self, indexFrom, indexTo): """ Create picups with scale factor -1 for decenters and tilts. Set order to 1""" lde = self.TheSystem.LDE surf2 = lde.GetSurfaceAt(indexTo) for cellIndex in [12,13,14,15]: cell2 = CastTo(surf2, "IEditorRow").GetCellAt(cellIndex) pickup = cell2.CreateSolveType(constants.SolveType_SurfacePickup)._S_SurfacePickup pickup.ScaleFactor = -1.0 pickup.Surface = indexFrom cell2.SetSolveData(pickup) ocol = CastTo(surf2,'ILDERow').GetSurfaceCell(constants.SurfaceColumn_Par6) ocol.IntegerValue = 1 def CBify(self, index, variablep): """ Make surface a CG, make tilts variable """ surf = self.TheSystem.LDE.GetSurfaceAt(index) setting = surf.GetSurfaceTypeSettings(constants.SurfaceType_CoordinateBreak) surf.ChangeType(setting) if(variablep): CastTo(surf,'IEditorRow').GetCellAt(14).MakeSolveVariable() CastTo(surf,'IEditorRow').GetCellAt(15).MakeSolveVariable() def AddCoordinateBreaks(self): """ Add coordinate break surfaces to the LDE, set variables and pickups """ mList = self.ListMirrorPlanes() lde = self.TheSystem.LDE for index in mList[::-1]: lde.InsertNewSurfaceAt(index+1) self.CBify(index+1, False) lde.InsertNewSurfaceAt(index) self.CBify(index, True) self.createPickupsAndSetOrder(index,index+2) def RemoveAllVariables(self): """ Remove all the variables """ rv = self.TheSystem.Tools.RemoveAllVariables() def MisalignSystem(self, t1, t2, t3): """ Misalign the system T1 is the s.t.d. of decentering of the mirror T2 is the s.t.d. of how much the laser misses the center of the mirror T3 is the s.t.d. of the thickness The vertex is displaced by t1. The chief ray should miss by t2. Final plane is missed by t1 + t2, since there is no vertex displacement, only decenter and """ self.ThicknessRandomizer(t3) stopSurf = self.TheSystem.LDE.StopSurface stopRad = self.TheSystem.LDE.GetSurfaceAt(stopSurf).SemiDiameter lastSurf = self.TheSystem.LDE.NumberOfSurfaces - 1 px = self.SpecialGauss(0,t2)/stopRad py = self.SpecialGauss(0,t2)/stopRad mList = self.ListMirrorPlanes() mList.append(lastSurf) for surf in mList: x1 = self.SpecialGauss(0,t1) y1 = self.SpecialGauss(0,t1) x2 = self.SpecialGauss(0,t2) y2 = self.SpecialGauss(0,t2) if not surf == lastSurf: self.SurfaceDisplacement(surf, x1, y1) if surf == lastSurf: self.AddREAOperands(surf, x1 + x2, y1 + y2, px, py) elif not surf == stopSurf: self.AddREAOperands(surf, x2, y2, px, py) self.LocalOptimize(0.00000001)
class PlotCentralFieldMTF(object): class LicenseException(Exception): pass class ConnectionException(Exception): pass class InitializationException(Exception): pass class SystemNotPresentException(Exception): pass def __init__(self): # make sure the Python wrappers are available for the COM client and # interfaces EnsureModule('ZOSAPI_Interfaces', 0, 1, 0) # Note - the above can also be accomplished using 'makepy.py' in the # following directory: # {PythonEnv}\Lib\site-packages\wind32com\client\ # Also note that the generate wrappers do not get refreshed when the # COM library changes. # To refresh the wrappers, you can manually delete everything in the # cache directory: # {PythonEnv}\Lib\site-packages\win32com\gen_py\*.* self.TheConnection = EnsureDispatch("ZOSAPI.ZOSAPI_Connection") if self.TheConnection is None: raise PlotCentralFieldMTF.ConnectionException( "Unable to intialize COM connection to ZOSAPI") self.TheApplication = self.TheConnection.CreateNewApplication() if self.TheApplication is None: raise PlotCentralFieldMTF.InitializationException( "Unable to acquire ZOSAPI application") if self.TheApplication.IsValidLicenseForAPI == False: raise PlotCentralFieldMTF.LicenseException( "License is not valid for ZOSAPI use") self.TheSystem = self.TheApplication.PrimarySystem if self.TheSystem is None: raise PlotCentralFieldMTF.SystemNotPresentException( "Unable to acquire Primary system") def __del__(self): if self.TheApplication is not None: self.TheApplication.CloseApplication() self.TheApplication = None self.TheConnection = None def OpenFile(self, filepath, saveIfNeeded): if self.TheSystem is None: raise PlotCentralFieldMTF.SystemNotPresentException( "Unable to acquire Primary system") self.TheSystem.LoadFile(filepath, saveIfNeeded) def CloseFile(self, save): if self.TheSystem is None: raise PlotCentralFieldMTF.SystemNotPresentException( "Unable to acquire Primary system") self.TheSystem.Close(save) def SamplesDir(self): if self.TheApplication is None: raise PlotCentralFieldMTF.InitializationException( "Unable to acquire ZOSAPI application") return self.TheApplication.SamplesDir def ExampleConstants(self): if self.TheApplication.LicenseStatus is constants.LicenseStatusType_PremiumEdition: return "Premium" elif self.TheApplication.LicenseStatus is constants.LicenseStatusType_ProfessionalEdition: return "Professional" elif self.TheApplication.LicenseStatus is constants.LicenseStatusType_StandardEdition: return "Standard" else: return "Invalid" def RemoveExtremeFields(self): """Remove field points 6,7,8,9 and 1. """ field = self.TheSystem.SystemData.Fields for x in range(9, 5, -1): field.RemoveField(x) field.RemoveField(1) def CheckLimits(self, xdata, ydata, index, histos): resolution = 20.0 for i in range(xdata.Length): if ydata.Data[i][index] < 0.25: lim = xdata.Data[i] resolution = xdata.Data[i] break histos.resolutions.append(20) return (resolution) def CornerCounter(self, res, index, h5, h75, h10): if res > 5.0: h5[index] = 1 + h5[index] if res > 7.5: h75[index] = 1 + h75[index] if res > 10.0: h10[index] = 1 + h10[index] def PlotMtfAllConfigs(self, bname, histos): """Loop over all configs in MCE, and plot the MTF for all active fields""" mce = self.TheSystem.MCE mcs = mce.NumberOfConfigurations #Loop over all configs for mc in range(mcs): mce.SetCurrentConfiguration(mc + 1) #Plot MTF gmtf = self.TheSystem.Analyses.New_GeometricMtf() settings = CastTo(gmtf.GetSettings(), 'IAS_GeometricMtf') #settings.ShowDiffractionLimit() settings.MaximumFrequency = 20.0 gmtf.ApplyAndWaitForCompletion() #gmtf.ToFile('m:\\gmtf.txt') results = gmtf.GetResults() fig, ax = plt.subplots(1, 1, figsize=(8, 6)) res5 = [0, 0, 0] res75 = [0, 0, 0] res10 = [0, 0, 0] #Loop over results. for i in range(results.NumberOfDataSeries): ds = results.GetDataSeries(i) plt.plot(ds.XData.Data, ds.YData.Data) resT = self.CheckLimits(ds.XData, ds.YData, 0, histos) #Tangential (or opposite) resS = self.CheckLimits(ds.XData, ds.YData, 1, histos) #Sagittal(or opposite) self.CornerCounter(resT, 0, res5, res75, res10) self.CornerCounter(resS, 1, res5, res75, res10) self.CornerCounter((resT + resS) / 2.0, 2, res5, res75, res10) histos.FillCounterHisto(histos.histos5, res5) histos.FillCounterHisto(histos.histos75, res75) histos.FillCounterHisto(histos.histos10, res10) plt.grid() fig.savefig('c:\\Users\\haavagj\\plots\\' + bname + str(mc) + '.png') plt.close(fig)
class MtfMFGenerator(object): class LicenseException(Exception): pass class ConnectionException(Exception): pass class InitializationException(Exception): pass class SystemNotPresentException(Exception): pass def __init__(self): # make sure the Python wrappers are available for the COM client and # interfaces EnsureModule('ZOSAPI_Interfaces', 0, 1, 0) # Note - the above can also be accomplished using 'makepy.py' in the # following directory: # {PythonEnv}\Lib\site-packages\wind32com\client\ # Also note that the generate wrappers do not get refreshed when the # COM library changes. # To refresh the wrappers, you can manually delete everything in the # cache directory: # {PythonEnv}\Lib\site-packages\win32com\gen_py\*.* self.TheConnection = EnsureDispatch("ZOSAPI.ZOSAPI_Connection") if self.TheConnection is None: raise MtfMFGenerator.ConnectionException("Unable to intialize COM connection to ZOSAPI") self.TheApplication = self.TheConnection.CreateNewApplication() if self.TheApplication is None: raise MtfMFGenerator.InitializationException("Unable to acquire ZOSAPI application") if self.TheApplication.IsValidLicenseForAPI == False: raise MtfMFGenerator.LicenseException("License is not valid for ZOSAPI use") self.TheSystem = self.TheApplication.PrimarySystem if self.TheSystem is None: raise MtfMFGenerator.SystemNotPresentException("Unable to acquire Primary system") def __del__(self): """Boiler plate""" if self.TheApplication is not None: self.TheApplication.CloseApplication() self.TheApplication = None self.TheConnection = None def OpenFile(self, filepath, saveIfNeeded): """Boiler plate""" if self.TheSystem is None: raise MtfMFGenerator.SystemNotPresentException("Unable to acquire Primary system") self.TheSystem.LoadFile(filepath, saveIfNeeded) def CloseFile(self, save): """Boiler plate""" if self.TheSystem is None: raise MtfMFGenerator.SystemNotPresentException("Unable to acquire Primary system") self.TheSystem.Close(save) def SamplesDir(self): """Boiler plate""" if self.TheApplication is None: raise MtfMFGenerator.InitializationException("Unable to acquire ZOSAPI application") return self.TheApplication.SamplesDir def ExampleConstants(self): """Boiler plate""" if self.TheApplication.LicenseStatus is constants.LicenseStatusType_PremiumEdition: return "Premium" elif self.TheApplication.LicenseStatus is constants.LicenseStatusType_ProfessionalEdition: return "Professional" elif self.TheApplication.LicenseStatus is constants.LicenseStatusType_StandardEdition: return "Standard" else: return "Invalid" def RemoveAllAfterDMFS(self): """Remove all the oparands after the first DMFS in the merit function editor""" mfe = self.TheSystem.MFE nRows = mfe.NumberOfOperands dmfsrow = -1 for r in range(nRows): row = mfe.GetOperandAt(r) if(row.Type == constants.MeritOperandType_DMFS): dmfsrow = r break if(dmfsrow > 0): CastTo(mfe,'IEditor').DeleteRowsAt(dmfsrow, nRows - dmfsrow) def AddMTFOPGT(self, field, freq, target,type): """ Add operand of type type (GMTS or GMTT) for field and freq. Then add operant OPGT requiring the previous operand to be larger than target """ mfe = self.TheSystem.MFE mtf = mfe.AddOperand() mtf.ChangeType(type) p1 = mtf.GetOperandCell(constants.MeritColumn_Param1) p1.IntegerValue = 2 p3 = mtf.GetOperandCell(constants.MeritColumn_Param3) p3.IntegerValue = field + 1 p4 = mtf.GetOperandCell(constants.MeritColumn_Param4) p4.DoubleValue = freq p6 = mtf.GetOperandCell(constants.MeritColumn_Param6) p6.IntegerValue = 1 opgt = mfe.AddOperand() opgt.ChangeType(constants.MeritOperandType_OPGT) opgt.Target = target wc = opgt.GetOperandCell(constants.MeritColumn_Weight) wc.DoubleValue = 1.0 p1 = opgt.GetOperandCell(constants.MeritColumn_Param1) p1.IntegerValue = CastTo(mtf,'IEditorRow').RowIndex + 1 def OptimizeMTFGreaterThan(self, nFields, freq, target): """ Create MF to optimize on MTF for the nFields first field points, trying to make it greater than target at freq. Operands will be added to the end of the merit function. """ mce = self.TheSystem.MCE mcs = mce.NumberOfConfigurations for mc in range(mcs): mfe = self.TheSystem.MFE cnf = mfe.AddOperand() cnf.ChangeType(constants.MeritOperandType_CONF) cnf.GetOperandCell(constants.MeritColumn_Param1).IntegerValue = mc + 1 for f in range(nFields): self.AddMTFOPGT(f, freq, target, constants.MeritOperandType_GMTS) self.AddMTFOPGT(f, freq, target, constants.MeritOperandType_GMTT) def LocalOptimizeMTF(self, target): """ Start local optimization, keep tunning until MF is below target, local optimization converges, or 500 minutes has passed. """ lopt = self.TheSystem.Tools.OpenLocalOptimization() lopt.Algorithm = constants.OptimizationAlgorithm_DampedLeastSquares lopt.Cycles = constants.OptimizationCycles_Infinite lopt.NumberOfCores = 8 print("Starting local optimization") CastTo(lopt, "ISystemTool").Run() mf = lopt.InitialMeritFunction counter = 0 dcount = 0 print("Starting loop, mf = " + str(mf)) while mf > target: time.sleep(60) if (lopt.CurrentMeritFunction < mf): dcount = 0 if dcount > 0: print("dcount is " + str(dcount)) mf = lopt.CurrentMeritFunction print("mf = " + str(mf)) counter = counter + 1 dcount = dcount + 1 if( counter > 500): break if( dcount > 5): break CastTo(lopt, "ISystemTool").Cancel() CastTo(lopt, "ISystemTool").Close() return(mf) def HammerOptimize(self, target): """ Start hammer optimization. Keep running until MF is below target, printing current MF every 10 minutes. """ hopt = self.TheSystem.Tools.OpenHammerOptimization() hopt.Algorithm = constants.OptimizationAlgorithm_DampedLeastSquares hopt.NumberOfCores = 8 print("Starting hammer optimization") CastTo(hopt, "ISystemTool").Run() mf = hopt.InitialMeritFunction print("Starting loop, mf = " + str(mf)) iter = 0 while mf > target: time.sleep(600) mf = hopt.CurrentMeritFunction print("Time " + str(iter)) print("mf = " + str(mf)) iter = iter + 1 CastTo(hopt, "ISystemTool").Cancel() CastTo(hopt, "ISystemTool").Close() return(mf)