mjhoptics / ray-optics

geometric ray tracing for optical systems
BSD 3-Clause "New" or "Revised" License
263 stars 54 forks source link

How to find / modify the image distance of an optical model loaded from a zemax model? #46

Closed diplodocuslongus closed 3 years ago

diplodocuslongus commented 3 years ago

Hello,

As an exercise to get familiar with both ray-tracing and ray-optics, I am comparing a spherical and an aspherical lens, both loaded from Edmund Optics provided zemax models.

I load the zemax model and assign variables for the sequential and paraxial models with:

opm, info_asph = open_model("zmax_83583.zmx", info=True)
sm = opm.seq_model
pm = opm.parax_model

I have no problem with the layout plot of the spherical lens (and consequently the ray aberrations and spot diagrams), but with the aspherical lens the image distance as listed in the model is incorrect and result in the following layout:

aspheric_EO_rayoptics

Note there is an additional element (maybe a cover glass) that I'd also like to be able to remove without having to create the lens manually using a sequential model (using the lens specs listed by sm.list_model()), but for the moment the present issue is about the image distance.

So I'd like to find the correct image distance or at least be able to modify the default image distance as read from the model (also without having to create the lens from scratch).

The first order data listed with pm_asph.first_order_data() shows that indeed the image distance is incorrect (img_dist = 0.3 while the bfl = 1.266):

efl               3.172
ffl              -3.172
pp1                  -0
bfl               1.266
ppk               1.906
f/#               1.586
m                0.3045
red          -3.153e+09
obj_dist          1e+10
obj_ang               1
enp_dist             -0
enp_radius            1
na obj            1e-10
n obj                 1
img_dist            0.3
img_ht          0.05537
exp_dist         -1.906
exp_radius            1
na img          -0.3007
n img                 1
optical invariant      0.01746

I dived into the code of ray-optics, and thought I had found out that I could modify the image distance img_dist with: opm_asph.optical_spec.parax_data.fod.img_dist = opm_asph.optical_spec.parax_data.fod.bfl (here setting it to the BFL of the lens). If I re-list the first order data, the img_dist parameter is indeed changed:

print(opm_asph.optical_spec.parax_data.fod.img_dist)
pm_asph.first_order_data()

shows:

1.2658773776757877
efl               3.172
ffl              -3.172
pp1                  -0
bfl               1.266
[...]
img_dist          1.266
[...]

But the layout diagram remains the same, even after updating the model with opm.update_model(), so obviously this isn't as simple as this.

There is a compute_first_order function in the parax.firstorder module, could it be used here? How could I obtain the correct image distance, or modify this optical model so that the layout diagram be correct?

Ludo

mjhoptics commented 3 years ago

Hello Ludo,

Thank you for your detailed inquiry. I can clarify a few things.

The output from compute_first_order(), which is what is saved as osp.parax_data, is best thought of as calculated parameters. The backfocus as calculated can be used to change the final gap thickness in the sequential model, aka sm. So I would do:

opm_asph.seq_model.gaps[-1].thi = opm_asph.optical_spec.parax_data.fod.bfl
opm_asph.update_model()

The sequential model is the primary data for the ray trace. The usage specifications (aperture, field and wavelength) are contained in the optical_spec and are used to facilitate the ray tracing of imaging systems. These are the 2 models that should be changed to affect the ray trace and first order results.

To remove the cover glass from the model, you could try:

# remove the 2 surfaces, remembering to count backwards
opm_asph.seq_model.remove(4)
opm_asph.seq_model.remove(3)
# force regeneration of element model
opm_asph.ele_model.elements = []
opm_asph.update_model()
# update_model recalculates the first order data. use it to set the backfocus
opm_asph.seq_model.gaps[-1].thi = opm_asph.optical_spec.parax_data.fod.bfl
opm_asph.update_model()
# draw a new lens layout here...

This should work but as with all code written without being run, there's a good chance it won't run the first time.

Hope this helps.

Mike

diplodocuslongus commented 3 years ago

Mike,

Excellent, thank you! Your suggestions solved both of my issues, they did run first time! I'll post the code here soon as a reference example on how to modify a zemax lens model. In the meantime I consider this can be closed. Thank you again.

Ludo

diplodocuslongus commented 3 years ago

A related notebook can be found here.

mjhoptics commented 3 years ago

Thank you, Ludo, for providing a link to your notebook. I noticed that when the Zemax file is imported, one of the glasses, D-ZLAF52LA_M, wasn't found in the existing catalogs. I googled it and it appears that it is a moldable form of the CDGM glass D-ZLAF52LA, which is in the CDGM catalog ray-optics uses.

When ray-optics can't find a material in an imported lens, it replaces it with a glass named "not D-ZLAF52LA_M" that has an index of 1.5. D-ZLAF52LA_M has a nominal index of 1.81 so that may very well be the reason the focus is so far off.

The way to fix this is to modify a file that ray-optics writes out during the import process. In your case, it should be named EO_zmax_83583_tmpl.smx and will contain a dict of missing glass keys with substitution values. We can see BK7 is already identified as replaceable by N-BK7.

{'D-ZLAF52LA_M': 1, 'BK7': "create_glass('N-BK7','Schott')"}

For your case, we can replace D-ZLAF52LA_M with D-ZLAF52LA. Edit the .smx file so that it contains:

{'D-ZLAF52LA_M': "create_glass('D-ZLAF52LA','CDGM')", 'BK7': "create_glass('N-BK7','Schott')"}

and rename the file to EO_zmax_83583.smx, i.e. the original filename with a .smx extender.

Now reimport the original file. The import info should list

'glass substituted': 2

and echo the .smx file contents.

Glass manufacturers are very crafty in how they add prefixes and suffixes to their nominal glass names. This pattern ('_M') snuck by apparently.

The Thorlabs catalog lens example outlines this process in detail. There is also a video on YouTube that goes through this example.

Thanks again,

Mike

diplodocuslongus commented 3 years ago

Mike, I had noticed

'glass not found': 1,
'glass substituted': 1

when listing the lens info (and I told myself "ray-optics can even substitute a glass when one is not found!"), as well as the not D-ZLAF52LA_M when listing the sequential model specs (with sm_asph.list_model()) but it didn't occur to me to relate the substituted glass and the glass being not found to the wrong image distance. Lesson learnt.

Thank you again for your valuable inputs and for having taken the time to write.

I will experiment with the smx file and update the gist notebook early this coming week.

Ludo

diplodocuslongus commented 3 years ago

Mike, For the .smx file to be correctly interpreted, I had to use double quotes for the glass name, and the glass name in the CDGM catalog has to be D-ZLaF52LA, not D-ZLAF52LA. So my .smx file contains: {"D-ZLAF52LA_M": "create_glass('D-ZLaF52LA','CDGM')", "BK7": "create_glass('N-BK7','Schott')"}

With single quotes I get: JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1) as the end part of a long error message, and for the glass name error I've opened this issue.

And then, as you thought, the wrong index of the glass coming from the substitute is what caused the image distance to be wrong first place. Note that the cover glass should remain in place or the image distance will be off slightly (now going to see how to recalculate the proper image distance if I do remove the cover glass).

aspheric_EO_rayoptics_ok Raytrace with the correct glass

aspheric_EO_rayoptics_nocoverglass Raytrace with the correct glass and cover glass removed (image distance is slightly off)

diplodocuslongus commented 3 years ago

Updated notebook per Mike's inputs and comments here.

mjhoptics commented 3 years ago

The most recent version of ray-optics (0.7.1) does a better job of finding substitute glasses; hand editing the .smx file shouldn't be necessary. HTH Mike