We are urgently looking for an alternative to ZPL-GETZERNIKE using ZOS-API.
The point is that we cannot tell “TheSystem.MFE.GetOperandValue(MeritOperandType_ZERN, “ the maximum term of Zernike.
“TheSystem.MFE.GetOperandValue(MeritOperandType_ZERN, “ always develops only up to the given coefficient :-(
MAny Thanks
Hans-Jürgen
Page 1 / 1
Hi Hans-Jürgen,
I’m not sure I understand the question correctly, but as a Merit Function operand, ZERN can only ever return a single value. The way this operand is inteded to be used in the Merit Function is by having a first ZERN operand with the Term parameter equal to the maximum term for the fitting (note that the minimum is 11 from the Help File). Then, subsequent ZERN operands (they need to strictly follow that first operand, you cannot have another operand in between!) that have the same parameters but a different Term value will return the Term fitted by the number of terms in the first operand, that way, OpticStudio only does the fit once (I hope that makes sense).
Here is a dummy example that illustrates this principle:
As you can see, the first ZERN has a Term of 25, this means the fit is made with 25 terms. Then, the following operands return a different term but since they dirrectly follow the ZERN at 25 term, they are all fitted to 25 terms.
This means that you cannot simply use the GetOperandValue from the ZOS-API MFE.
At this point, I can see two options. First, you could reproduce this Merit Function in the ZOS-API, evaluate it, and retrieve each operand value. Second, you could use the Analyze..Wavefront..Zernike Standard Coefficients (or Fringe or Annular) analysis inside the ZOS-API. This analysis is not fully implemented, so you’ll have to save the results to a text-file and then parse it. I think the first solution might be more efficient, so I’ll make an example of it below.
Here is the code I used (hopefully the comments are self-explanatory) with Python 3.8.13 and Pythonnet 2.5.2:
# The Merit Function Editor TheMFE = TheSystem.MFE
# Delete all rows TheMFE.DeleteAllRows()
# Maximum term MaxTerm = 25
# ZERN Operand type ZERNType = ZOSAPI.Editors.MFE.MeritOperandType.ZERN
# Get first operand FirstOperand = TheMFE.GetOperandAt(1)
# Change its type to ZERN FirstOperand.ChangeType(ZERNType)
# Common settings Wave = 1 Samp = 2 Field = 1 Type = 1 # Standard coefficients Epsilon = 0.0 Vertex = 0
# Adjust settings of first ZERN operand FirstOperand.GetOperandCell(2).IntegerValue = MaxTerm # Term FirstOperand.GetOperandCell(3).IntegerValue = Wave # Wave FirstOperand.GetOperandCell(4).IntegerValue = Samp # Samp FirstOperand.GetOperandCell(5).IntegerValue = Field # Field FirstOperand.GetOperandCell(6).IntegerValue = Type # Type FirstOperand.GetOperandCell(7).DoubleValue = Epsilon # Epsilon FirstOperand.GetOperandCell(8).IntegerValue = Vertex # Vertex?
# Insert all other operands with a loop changing only the term number # Note that we do not need the last term as it is already in the first operand for Term in range(1, MaxTerm): # Put new operand after the first ZERN NewOperand = TheMFE.InsertNewOperandAt(2)
# Change new operand type to ZERN NewOperand.ChangeType(ZERNType)
# Adjust its settings NewOperand.GetOperandCell(2).IntegerValue = Term # Term NewOperand.GetOperandCell(3).IntegerValue = Wave # Wave NewOperand.GetOperandCell(4).IntegerValue = Samp # Samp NewOperand.GetOperandCell(5).IntegerValue = Field # Field NewOperand.GetOperandCell(6).IntegerValue = Type # Type NewOperand.GetOperandCell(7).DoubleValue = Epsilon # Epsilon NewOperand.GetOperandCell(8).IntegerValue = Vertex # Vertex?
# Evaluate the Merit Function TheMFE.CalculateMeritFunction()
# Read and print the results (starting from the bottom) for Index in range(MaxTerm, 0, -1): # Current operand CurrentOperand = TheMFE.GetOperandAt(Index)
# Corresponding term Term = CurrentOperand.GetOperandCell(2).IntegerValue
# Corresponding value TermValue = CurrentOperand.Value
Z 1 -0.97593483 Z 2 0.00000000 Z 3 0.00000000 Z 4 -0.28473181 Z 5 0.00000000 Z 6 0.00000000 Z 7 0.00000000 Z 8 -0.00000000 Z 9 -0.00000000 Z 10 0.00000000 Z 11 0.21694194 Z 12 0.00000000 Z 13 -0.00000000 Z 14 -0.00000008 Z 15 -0.00000000 Z 16 0.00000000 Z 17 0.00000000 Z 18 -0.00000000 Z 19 -0.00000000 Z 20 -0.00000000 Z 21 0.00000000 Z 22 0.00088701 Z 23 0.00000000 Z 24 0.00000000 Z 25 -0.00000000
Note that the time consuming part of the code is creating the Merti Function. However, as it is fairly independant of the file, you could create it once and save it to a *.MF file, and then just load it with the ZOS-API.
Let me know if this helps.
Take care,
David
Hi David,
thanks for the quick response.
We need the Zernike coefficients for quick analysis without changing anything on the MFE, without opening an analysis window.
So we found: TheSystem.MFE.GetOperandValue(MeritOperandType_ZERN,
But this functionality only makes the Zernike expansion up to the specified term.
How can we force the GetOperandValue to use a defined maximum term?
To the use in MFE:
It is sufficient that all the desired coefficients are present in one "group". This can also be with an ascending index. Then the same maximum term is used for all coefficients in this “group”.
Viele Grüße
Hans-Jürgen
Hi Hans-Jürgen,
To the best of my knowledge, you can’t force GetOperandValue to use a defined maximum term.
Another solution could be to create a user-defined operand (UDOC) that you could call with GetOperandValue, but it could be as slow as using an analysis.
How fast do you need the calculation to be?
Thanks for the comment on the ascending order, I didn’t know about it.
Take care,
David
Hi David,
UDOC’s are to slow. And by the way a “big” workaround.
We cannot understand that ZPL can do something that modern programming cannot. ZEMAX has to improve here.
We may have to open an analysis window and read out the data. But I've never done that.
Great that I could help with the ascending term numbers. It's much "nicer"!
Hans-Jürgen
Hi Hans-Jürgen,
Unfortunately, I know they are slow :(
You can always send a feedback email to Zemax support and they might create a feature request for you. Its probably not going to happen in the short term though.
Opening and parsing an analysis window isn’t that bad. Its much easier and faster if the analysis is fully implemented, but the Zernike ones aren’t.
Here is an example for you:
# Open a Zernike Standard analysis ZernikeStd = TheSystem.Analyses.New_ZernikeStandardCoefficients()
# Get analysis settings Settings = ZernikeStd.GetSettings()
# Apply settings and run analysis ZernikeStd.ApplyAndWaitForCompletion()
# Save results to text file Path = 'E:\ZernikeStd.txt' ZernikeStd.GetResults().GetTextFile(Path)
# Close the analysis ZernikeStd.Close()
# Read the text file with open(Path) as F: Lines = F.readlines()
# The relevant content seem to start at line 39 in the text file # (hopefully this is conserved while modifying the settings)
# Read the relevant lines for Index in range(38, len(Lines)): # Split the line to isolate the numbers SplitLine = Lines=Index].split()
# Term number Term = int(SplitLinel1]) Value = float(SplitLinel2])
# Print the results print('Z {0} \t {1: .8f}'.format(Term, Value))
And the results for the same dummy file I did earlier:
Z 1 -0.97593483 Z 2 0.00000000 Z 3 0.00000000 Z 4 -0.28473181 Z 5 0.00000000 Z 6 0.00000000 Z 7 0.00000000 Z 8 0.00000000 Z 9 0.00000000 Z 10 0.00000000 Z 11 0.21694194 Z 12 0.00000000 Z 13 0.00000000 Z 14 -0.00000008 Z 15 0.00000000 Z 16 0.00000000 Z 17 0.00000000 Z 18 0.00000000 Z 19 0.00000000 Z 20 0.00000000 Z 21 0.00000000 Z 22 0.00088701 Z 23 0.00000000 Z 24 0.00000000 Z 25 0.00000000
Hope that helps.
Take care,
David
Hi David,
Thanks! We now do so - with the text file. A support request is out. And we hope it will come true.
All the best!
Hans-Jürgen
Thanks for posting this question Hans! I was having this exact issue so I’m going to implement David’s suggestion. It would be great to have GetOperandValue for a ZERN operand. It also seems like it should work according to their API documentation, but it’s not clear. Hope they add that feature as it would be great to add any operand to the MFE without actually creating a .mf file to load each time.
Hi Hans-Jürgen,
Unfortunately, I know they are slow :(
You can always send a feedback email to Zemax support and they might create a feature request for you. Its probably not going to happen in the short term though.
Opening and parsing an analysis window isn’t that bad. Its much easier and faster if the analysis is fully implemented, but the Zernike ones aren’t.
Here is an example for you:
# Open a Zernike Standard analysis ZernikeStd = TheSystem.Analyses.New_ZernikeStandardCoefficients()
# Get analysis settings Settings = ZernikeStd.GetSettings()
# Apply settings and run analysis ZernikeStd.ApplyAndWaitForCompletion()
# Save results to text file Path = 'E:\ZernikeStd.txt' ZernikeStd.GetResults().GetTextFile(Path)
# Close the analysis ZernikeStd.Close()
# Read the text file with open(Path) as F: Lines = F.readlines()
# The relevant content seem to start at line 39 in the text file # (hopefully this is conserved while modifying the settings)
# Read the relevant lines for Index in range(38, len(Lines)): # Split the line to isolate the numbers SplitLine = Lines=Index].split()
# Term number Term = int(SplitLinel1]) Value = float(SplitLinel2])
# Print the results print('Z {0} \t {1: .8f}'.format(Term, Value))
And the results for the same dummy file I did earlier:
Z 1 -0.97593483 Z 2 0.00000000 Z 3 0.00000000 Z 4 -0.28473181 Z 5 0.00000000 Z 6 0.00000000 Z 7 0.00000000 Z 8 0.00000000 Z 9 0.00000000 Z 10 0.00000000 Z 11 0.21694194 Z 12 0.00000000 Z 13 0.00000000 Z 14 -0.00000008 Z 15 0.00000000 Z 16 0.00000000 Z 17 0.00000000 Z 18 0.00000000 Z 19 0.00000000 Z 20 0.00000000 Z 21 0.00000000 Z 22 0.00088701 Z 23 0.00000000 Z 24 0.00000000 Z 25 0.00000000
Hope that helps.
Take care,
David
FYI, I’ve notice that I had to specify that the encoding was utf-16 when opening the file. So that may be necessary for other files.
FYI, I’ve notice that I had to specify that the encoding was utf-16 when opening the file. So that may be necessary for other files.
@Alex.TripsasThat's right, and you can configure the encoding of text output files under OpticStudio Preferences > General > TXT File Encoding. On your system, this is probably set to Unicode, and then you need to set the encoding to utf-16-le when reading the files.
I was gonna mention ZOSPy as well, but I had not realized how you implemented this. Does that mean that if I have text files that I generated with ANSI encoding, and then later on I changed this setting to Unicode in OpticStudio. Then ZOSPy won’t parse them correctly? Isn’t it safer to check the encoding for every text file? I haven’t tried this myself, but there seem to be a possibility to do that with chardet in Python:
In principle, that's true: ZOSPy will always open text files with the encoding configured in OpticStudio. However, the problem you mention does not apply to ZOSPy, because its analysis functions do not offer the ability to parse existing text output files. The text files that are opened by ZOSPy are therefore always generated using the encoding configured in the current OpticStudio instance.
For anyone trying chardet, this is what I obtained:
ANSI encoding:
ascii
Unicode encoding:
UTF-16
Take care,
David
@David.Nguyen Thanks for trying out! A small addition regarding the ANSI encoding: ANSI is a bit weird, and usually means the “preferred encoding”, which depends on the system locale. On my system, this is cp1252, and OpticStudio indeed saves its text output files with this encoding when set to “ANSI”.