Solved

Surface Aperture: User Defined Aperture in ZOS-API?

  • 3 February 2021
  • 8 replies
  • 604 views

Userlevel 3
Badge

I have a need to apply a User Defined Aperture to a surface in Sequential mode.  This aperture might need to be calculated automatically outside of OpticStudio, or be more complicated than what the .UDA text file format accommodates.


For example, I might be able to make a curved-slit aperture out of arcs, but the line-segment approximation that 'OpticStudio internally represents the arc as' might be too crude for my purposes.  Or I might need to calculate an elliptical or other arbitrarily-curved arc segment that is not supported by UDA.


Ideally, I could calculate an aperture shape outside of OpticStudio, e.g. in Matlab or Python, and then use ZOS-API to apply this aperture to a surface. Then run the OpticStudio model using this aperture, either in 'Standalone' (Python routine calls OpticStudio) or in 'Inherent' (OpticStudio calls Python routine) mode.


I can also see it being useful if somebody wanted to do an optimization in which the shape/size of this externally-calculated aperture was variable using parameters that could be passed back and forth to OpticStudio.


Is anything like this supported, or do you have a suggestion on the best way to do it?


-- Greg

icon

Best answer by Sandrine Auriol 4 February 2021, 10:43

View original

8 replies

Userlevel 6
Badge +2

Hi Greg


In sequential mode, the only option for user-defined aperture is the UDA format.


Currently we don't have any other way to define a UDA like by using a CAD or a standard 2D drawing. One way to work can be to model the part of the system in non sequential mode where you could easily make a boolean with a CAD part, or even use a CAD part directly. The drawback is that some features like OPD may not work correctly in mixed-mode.


A simpler solution would be to approximate the shape of the aperture so that it can be described as a UDA.


I think your idea of automatically changing a UDA could work but I would keep it to a relatively simple form so that you can only have to check a few rays to update the shape of the UDA. For example there can be some problems of precision when using ARC so you can keep something more simple by using LIN. I don't know your case so feel free to let me know your thoughts and do not hesitate if you have any further questions.


Have a nice day!


Sandrine

Userlevel 3
Badge

Thank you, Sandrine.


That answers my question.


The actual aperture shapes I want to use will be more like ARCs, but might include e.g. elliptical or some other conic sections, instead of purely circular arcs.


From your reply on precision problems with ARC and suggestion on using LIN, I gather that even if I wanted a portion of my UDA to consist of a perfectly circular arc, I could 'manually' (e.g. by external programming) generate a more finely-segmented 'arc' shape by building it out of many smaller LIN segments instead of using the ARC entity, which is limited to a maximum of 64 segments.


I'll also consider using NSC mode to model part of the sequential system; we've done that before.


For now, please consider adding this as a feature in the future: a UDA that uses a ZOS-API call to externally calculate the aperture to be applied to a surface.


I notice you didn't mention User Defined Surface Apodization Using DLLs (custom 'Filter Function' DLLs).  Perhaps you could address this method of applying a transmission function to a surface.  I don't think I would use it, because I would have to somehow write DLLs that could have parameters passed to them that are calculated by e.g. a Python program.  I'm not that smart, and there are lots of opportunities for problems...


I do think that there is a fundamental difference between using a UDA and a UDS apodization, which would lead me to prefer a UDA.  Please confirm whether this is correct:


A UDA can alter ray launching so that analysis rays are generated and 'aimed' to go through the aperture, leading in better sampling (more rays) from the object to the image surface for analyses.


A UDS filter function simply blocks or attenuates rays, resulting in fewer rays available in analyses, and thus lower sampling or requiring longer calculation times due to having to increase rays or sampling in analyses to get sufficient density of calculations.


I would of course prefer to use the more numerically efficient method (as well as avoiding trying to write and debug DLLs).


-- Greg

Userlevel 6
Badge +2

Hi Greg


We already have a feature request regarding User Defined Aperture so I think I will complete it with your request. I will bring it to the attention of the product.


No I haven't mentioned a user defined apodization. That could also be an option and I hadn't thought about it. I think that the comparison you are making between the two solutions: a UDA and a UDS apodization is correct. Also the transmission of rays modified by a UDS apodization is not taken into account by all the analyses. So that limits its usability.


Sandrine

Userlevel 6
Badge +2

Hi Greg


Regarding the feature request, I was looking at the ZOS-API code and here is what we currently have:


>> TheApplication=ans;

>> TheSystem=TheApplication.PrimarySystem;

>> Surf2=TheSystem.LDE.GetSurfaceAt(2);

>> Surf2_Aperture=Surf2.ApertureData;

>> Surf2_UserAperture = Surf2_Aperture.CreateApertureTypeSettings(ZOSAPI.Editors.LDE.SurfaceApertureTypes.UserAperture);

>> Surf2_UserAperture.ApertureFile='DetectorArray1.UDA';

>> Surf2_Aperture.ChangeApertureTypeSettings(Surf2_UserAperture);

 

So you can modify the aperture file called within the API. But then the aperture file is a text file that would need to be modified separately.


Just to be clear, what part of this would you like to improve? 


Sandrine

Userlevel 3
Badge

Hi, Sandrine.


Thanks again for your quick and helpful response to this thread.


I'm glad to hear (from the ZOS-API code that you just posted) that ZOS-API can change the aperture on a surface to use a designated .UDA text file. That gets me part of the way, but isn't as versatile as what I had in mind.


There are two reasons (cases) that I would like to use ZOS-API:


(1) To automate trying different apertures, as well as generation/calculation of the aperture shape under computer control:


I could have e.g. a Python program define and write a .UDA file automatically, and then use ZOS-API to apply that aperture to a surface. Then ZOS-API (using a Standalone application) could change the aperture and call OpticStudio repetitively, i.e., iterate running OpticStudio optimizations for the different apertures ...in effect, 'optimizing' among the optimized results to find the best (optimum) aperture shape.


This case (1) doesn't appear to require a new feature, so we'll try this for now.


I can see how I might use Python to generate a UDA built out of LINs and/or ARCs.


*** Was I right that by using more than 64 LINs, a circular (or elliptical) arc or hole with higher resolution could be 'drawn' than by using the ARC, CIR, and ELI entities?


Of course, any shape can be approximated by LINs.  The manual (p.431) says that the number of line segments is 'limited only by available memory' and that by using more segments (a more complex UDA data file) will slow ray tracing down.  This might suggest a need for Case (2)...


(2) To generate a weird, calculated aperture shape such as a functional form (e.g. conic other than ellipse) to *arbitrary* precision.


This is the feature request I had in mind.


The LIN, ARC, and ELI (ellipse) entities in a UDA file might get me close to what I need for now, but I am concerned about the limited precision/number of segments, and how OpticStudio treats the rays that hit the aperture near a 'kink' in the edge shape.


I don't know how the UDA is actually implemented during ray-tracing.  If, instead of referring to a UDA file and having OpticStudio calculate the x,y ray intercepts, might it be possible to have an aperture mode that calls an externally-generated routine (Inherent mode to call a ZOS-API external extension) and uses the x,y values returned from it as the aperture edges?  And that gets the ray intercepts to better precision than from using line segments, but directly from a calculation?


I am also wondering about how I will account for the 'Aperture projection on non-plane surfaces' (21.1 User Manual, p. 427) -- like if I am applying an aperture to an off-axis parabola (which I'm thinking of doing).  I might be able to have the external code (e.g. Python program) that generates the aperture take tilt and decenter into account, while calculating the functional form of the aperture shape to apply it to the curved surface.


I think that to implement this would require a new aperture mode feature to call something that would behave like an 'aperture DLL.'  This would be somewhat analogous to the User Defined Surface feature.  But as we discussed above, using surface transmittance in a UDS is not equivalent to a User Defined Aperture.  And it would be great if the ZOS-API interface would allow calling an 'Aperture Extension' WITHOUT having to write and compile a DLL.  This last behavior is what I REALLY had in mind when I wrote the first post...so that Inherent mode works.  (OpticStudio is the 'master,' and the Python code to calculate the aperture is the 'slave.')


====

For now, we'll try writing a program to generate UDA files as in Case (1) above, and see what we can do with it.


-- Greg

Userlevel 6
Badge +2

Hi Greg,


Thank you for your reply.


Yes I believe that case 1 should work. It would be great if you could keep us informed on how your 1st solution works. And if you need any help to write, please let us know.

About the ARC / CIR / ELI commands, the syntax is:


ARC cx cy angle n

CIR cx cy radius n

ELI cx cy rx ry angle n

Those shapes are represented by a series of n line segments and the maximum value for n is 64. So if you use more 64 line segments to represent these shapes, the resolution will be better than the OpticStudio's one.


For case 2, yes I understand what you think. Just a question: are you optimizing and then finding the aperture? Or is it your plan to do it altogether? Because optimizing and then finding the aperture might just be more effective. By default OpticStudio makes the semi-diameter big enough so that all rays pass through the surfaces.


Sandrine

Userlevel 3
Badge

Hi, Sandrine.


Excellent, I think we are close to understanding each other fully.  I have discussed this with colleagues, and we are proceeding with the simple form of Case 1: using Python to calculate a UDA file.


In our current work flow, we are finding an aperture shape FIRST, and then optimizing image quality using a Sequential merit function with that aperture applied to one surface in the system, so that the proper rays are traced during optimization.


The good news is that we have only ONE surface that needs a strange-shaped aperture in our current system.


As you and I discussed above, 'arbitrary' precision in aperture shape is possible using an 'unlimited' number of LIN segments calculated by Python and written into a big UDA file, but more segments will slow down optimization while OpticStudio has to read the long UDA file. (for each ray??) I was going to ask whether you have any idea how much slower it might be with more points, but I guess that will depend a great deal on our system/model and computers!  So I suppose we can try generating UDAs having different numbers of points to see what effect they have on optimization speed for our exact model.




Re: your question on 'doing it altogether,'

To begin with, we are finding the aperture shape using a separate criterion from image quality optimization, and thus we are NOT trying to update the aperture based on the image quality optimization.  So we don't think we have a current need to iterate OpticStudio optimizations and aperture shape changes.


In the above thread, at first I had in mind a'function call' by OpticStudio of the aperture shape to an external routine, which could be faster and higher-precision (returning exact x,y, points instead of line segments) than reading a UDA text file.  But I also brought up this possibility of using the Merit Function image quality calculations to perform aperture shape optimization as ANOTHER reason to pursue Case 2 as a general capability (i.e. another reason for the feature request to have OpticStudio call an external aperture shape calculation... beyond my primary motivation to exceed the capabilities of the UDA file, which is to allow 'arbitrary precision' of the aperture curve shape).


This would be a feature request for 'Inherent mode' calls where OpticStudio runs an external Python routine to perform the aperture calculation.


'Standalone' mode, in which Python calls OpticStudio to perform a calculation, is already supported as what we have been calling 'Case 1.'  In this mode, Python is the 'master' and would calculate an aperture shape and writes a UDA text file, then call OpticStudio to calculate the merit function for the system, and the Python would use the merit function numerical value to change the aperture in some way, and iterate as required.  (I.e., Python performs the high-level 'outer loop' optimization and OpticStudio performs inner-loop optimization calculations.)


In order to optimize the aperture shape, my colleagues and I realized today that if OpticStudio could call Python as well, then one could nest these optimizations inside each other as many layers deep as we want(!) since Python can call OpticStudio AND OpticStudio can call Python!  This would be 'Inherent inside of StandAlone'... I don't know if that would cause programming problems.


But I didn't quite understand your comment:


'Because optimizing and then finding the aperture might just be more effective. By default OpticStudio makes the semi-diameter big enough so that all rays pass through the surfaces.'


Can you elaborate just a little bit before we 'close' this thread, now that I have added the above detail? By 'more effective,' are you referring to simplicity of the work-flow/calculation sequence, calculation speed, the accuracy of the optimized solution that is found, or something else?


-- Greg

Userlevel 3
Badge

I'm thinking it should not be too difficult for OpticStudio to call an external routine that calculates the aperture; like a User Defined Surface DLL, OpticStudio could pass ray coordinates to the ZOS-API extension, which could then return e.g. a '1' if the ray is in a transparent area of the surface, and a '0' if the ray is blocked by an opaque area.


This would obviate any need for reading a text file and interpolation to determine what side of an aperture or obscuration edge (inside or outside) the ray intercepts.  And of course, retain full precision for the x,y coordinates.


-- Greg

Reply