Closed dibyendumajumdar closed 3 years ago
Hello @dibyendumajumdar, Yes you can use conic, even polynomials or even+odd polynomials. The mjhoptics/ray-optics-notebooks repo has a couple of examples. Cell Phone Lens.ipynb is a lens from a patent that has several high order aspherics. The Two Mirror Telescopes.ipynb file compares several telescope forms with different conic mirrors. The classes used are in rayoptics/elem/profiles.py Hope this helps.
Thank you.
Can glass types be specified just using refractive index / abbe number as they are in patent data? I have a hobby of converting photo lenses from patent data to tools like yours - it is more difficult to do so if you require a glass catalog to be pre-defined.
@dibyendumajumdar, yes, you can. In the package rayoptics.seq.medium, there are several classes for modeling glasses. Try the class Glass; it takes index and v-number. HTH, MIke
That's great, thank you. Bill Claff maintains a lot of photo lens patent data at https://www.photonstophotos.net/GeneralTopics/Lenses/OpticalBench/OpticalBenchHub.htm, and I have a utility that converts from his data to various tools. I will try to add support for ray-optics, and share results I get.
That sounds interesting, if some variation on existing inputs would really help, let me know. There is a website, http://www.lens-designs.com, that has many patent lenses, in zemax format. I've used this site as the source of files to test the zemax importer. I have a notebook that can walk the different categories on the site and generate a list of (hundreds) of zmx files. I did things like opening every 20th file and ploting the results. Quite a variety of lens forms. I need to clean it up and check it in.
According to Bill that site has lots of errors.. Bill maintains a list that matches production lenses.
Patent descriptions aren't necessarily production prescriptions. If Bill has validated the designs on his site are production prescriptions, that's good to know.
Yes, the validation is basically matching the patent to schematic given by manufacturer - Bill tends to overlay the two.
Hi - I am doing one example manually - but I am kind of stuck. I can see when I layout the model that it doesn't look right.
This is my Jupyter Notebook: https://github.com/cameragossip/cameragossip.github.io/blob/master/lensdesigns/mjh-ray-optics-Nikkor-58mm-f0.95.ipynb
The patent I am trying to model is from https://patents.google.com/patent/WO2019229849A1/en
Bill Claff's version: https://www.photonstophotos.net/GeneralTopics/Lenses/OpticalBench/OpticalBench.htm#Data/WO2019-229849_Example01P.txt,figureOpacity=0.25,AxisO,OffAxis
I wasn't sure if I am entering the aspheric data correctly - the conic constant in the patent is described thus:
x = (h**2 / r) / [1+ {1− (1 + κ) · (h / r)**2 } 1/2 ]
+ A4h**4 + A6h**6 + A8h**8 + A10h**10 + A12h**12 + A14h**14
Here, h is the height in the direction perpendicular to the optical axis,
x is the distance along the optical axis direction from the tangent
plane of the apex of the aspheric surface at the height h to the
aspheric surface,
and κ is the conic constant.
I think I need to use EvenPolynomial for this, is my understanding correct?
It looks like the surface normal of the first aspheric is wrong. This may take a while to track down. Otherwise, your setup looks good.
Also the concave surfaces are not displaying properly - I am not sure if I am missing something.
Because Ray failure occurs at the first element, estimates for clear apertures default to 1, so most of the elements are drawn as short squat elements. I’ll check the concavity in detail.
Here are a few things to try to get a better lens layout.
1) Set paraxial apertures in the sequential model:
for i, ifc in enumerate(sm.ifcs): sd = abs(pm.ax[i][0]) + abs(pm.pr[i][0]) ifc.set_max_aperture(sd)
2) Transfer the max aperture to the corresponding lens element:
em = opm.ele_model for i, e in enumerate(em.elements): if isinstance(e, elements.Element): sd1 = sm.ifcs[e.s1_indx].max_aperture sd2 = sm.ifcs[e.s2_indx].max_aperture e.sd = max(sd1, sd2)
3) Draw the lens layout without the rays (do_draw_rays=False):
layout_plt = plt.figure(FigureClass=InteractiveLayout, opt_model=opm, do_draw_rays=False, do_paraxial_layout=False, is_dark=isdark).plot()
Still looking at the actual problem...
Thank you. I was doing thus but not sure why it didn't work? The diameters below were computed by Bill Claff - I divided by 2 as I thought ray-optics expects semi-diameter - don't know if my understanding is correct.
elmn = [e for e in opm.ele_model.elements if isinstance(e, Element)]
elmn[0].sd = 66.8/2
elmn[1].sd = 65.82/2
#elmn[1].flat2 = 57.94/2
elmn[2].sd = 58.28/2
#elmn[2].flat1 = 58.28/2
elmn[3].sd = 65.32/2
elmn[4].sd = 70.90/2
elmn[5].sd = 65.0/2
elmn[6].sd = 61.06/2
elmn[7].sd = 59.42/2
elmn[8].sd = 50.24/2
elmn[9].sd = 49.92/2
elmn[10].sd = 49.92/2
elmn[11].sd = 51.12/2
elmn[12].sd = 46.8/2
elmn[13].sd = 45.36/2
elmn[14].sd = 39.46/2
elmn[15].sd = 39.46/2
elmn[16].sd = 38.94/2
elmn[17].sd = 44.3/2
Adding this helped:
for e in elmn:
sm.ifcs[e.s1_indx].max_aperture = e.sd
sm.ifcs[e.s2_indx].max_aperture = e.sd
btw love that the type of glass is indicated by the color coding - is it just based on refractive index?
Here are a few things to try to get a better lens layout.
1) Set paraxial apertures in the sequential model:
for i, ifc in enumerate(sm.ifcs): sd = abs(pm.ax[i][0]) + abs(pm.pr[i][0]) ifc.set_max_aperture(sd)
2) Transfer the max aperture to the corresponding lens element:
em = opm.ele_model for i, e in enumerate(em.elements): if isinstance(e, elements.Element): sd1 = sm.ifcs[e.s1_indx].max_aperture sd2 = sm.ifcs[e.s2_indx].max_aperture e.sd = max(sd1, sd2)
3) Draw the lens layout without the rays (do_draw_rays=False):
layout_plt = plt.figure(FigureClass=InteractiveLayout, opt_model=opm, do_draw_rays=False, do_paraxial_layout=False, is_dark=isdark).plot()
Still looking at the actual problem...
The elements have a mounting semi-diameter and the interfaces/surfaces have a clear aperture semi-diameter. If the surface is concave, I try to draw a flat between the surface semi-diameter and the element semi-diameter. For most of the surfaces, the ray trace data wasn't available because of the ray failures so the surface semi-diameters were set to 1. So the layout was drawing enormous flats on the elements with concave surfaces. Setting the surface semi-diameters as well as the elements avoided the flats being drawn.
The color for the glass type is determined by where it is in the glass map, so both index and v-number. You can get a glass map figure as follows:
import opticalglass.glassmap as gm gmf = plt.figure(FigureClass=gm.GlassMapFigure, glass_db=gm.GlassMapDB()).plot()
Is it possible to add an optional parameter to add_surface to set the max_aperture value?
Another question: is it essential to set PupilSpec? If I only have the F# as given by the patent then how can I set this? I tried doing it the way I saw in the example you mentioned before but maybe I did something wrong as it broke the system.
osp.pupil = PupilSpec(osp, key=['object', 'f/#'], value=0.98)
Specify the f/# in image space, not object space, e.g.
osp.pupil = PupilSpec(osp, key=['image', 'f/#'], value=0.98)
Adding the max_aperture to the add_surface fct is doable. Currently it takes a list for each surface. The list can contain 4 items: [radius, thickness, index, v-number] or, if air is the material, contains 2 items: [radius, thickness] I could add sd/max_aperture to the end of these as an option: [radius, thickness, index, v-number, max_aperture] [radius, thickness, max_aperture] Or, I could add a keyword argument, sd, so that the lists are unchanged. What do you think?
Specify the f/# in image space, not object space, e.g.
osp.pupil = PupilSpec(osp, key=['image', 'f/#'], value=0.98)
Okay thank you
Adding the max_aperture to the add_surface fct is doable. Currently it takes a list for each surface. The list can contain 4 items: [radius, thickness, index, v-number] or, if air is the material, contains 2 items: [radius, thickness] I could add sd/max_aperture to the end of these as an option: [radius, thickness, index, v-number, max_aperture] [radius, thickness, max_aperture] Or, I could add a keyword argument, sd, so that the lists are unchanged. What do you think?
I guess keyword arg will be better, as it is optional I presume.
There is code that automatically tries to set apertures based on ray tracing when update_model() is called. I need to make sure that that doesn't wipe out any user entered values. Hopefully I can do that without breaking something else.
Here is another lens - Leica 50mm f2 Summmicron: Auto generated from my utility - from Bill Claff's data.
Patent US 4,123,144
%matplotlib inline
isdark = False
from rayoptics.environment import *
from rayoptics.elem.elements import Element
opm = OpticalModel()
sm = opm.seq_model
osp = opm.optical_spec
pm = opm.parax_model
osp.pupil = PupilSpec(osp, key=['image', 'f/#'], value=2)
osp.field_of_view = FieldSpec(osp, key=['object', 'angle'], flds=[0., 22.5])
osp.spectral_region = WvlSpec([(486.1327, 0.5), (587.5618, 1.0), (656.2725, 0.5)], ref_wl=1)
opm.system_spec.title = 'US 4,123,144'
opm.system_spec.dimensions = 'MM'
opm.radius_mode = True
sm.gaps[0].thi=1e10
sm.add_surface([42.71,3.99,1.7343,28.19])
sm.ifcs[-1].max_aperture = 14.47
sm.add_surface([195.38,0.2])
sm.ifcs[-1].max_aperture = 13.53
sm.add_surface([20.5,7.18,1.67133,41.64])
sm.ifcs[-1].max_aperture = 12.01
sm.add_surface([0,1.29,1.7919,25.55])
sm.ifcs[-1].max_aperture = 10.745
sm.add_surface([14.94,5.35])
sm.ifcs[-1].max_aperture = 9.195
sm.add_surface([0,7.61])
sm.set_stop()
sm.ifcs[-1].max_aperture = 9.0295
sm.add_surface([-14.94,1,1.65222,33.6])
sm.ifcs[-1].max_aperture = 8.75
sm.add_surface([0,5.22,1.79227,47.15])
sm.ifcs[-1].max_aperture = 9.635
sm.add_surface([-20.5,0.2])
sm.ifcs[-1].max_aperture = 10.19
sm.add_surface([0,3.69,1.79227,47.15])
sm.ifcs[-1].max_aperture = 11.48
sm.add_surface([-42.71,37.32])
sm.ifcs[-1].max_aperture = 11.985
sm.list_surfaces()
sm.list_gaps()
opm.update_model()
sm.list_model()
pm.first_order_data()
layout_plt = plt.figure(FigureClass=InteractiveLayout, opt_model=opm, do_draw_rays=True, do_paraxial_layout=False,
is_dark=isdark).plot()
I think the skew rays are going out of the bounds with this:
Nikkor 50mm f1.4 US 4448497
%matplotlib inline
isdark = False
from rayoptics.environment import *
from rayoptics.elem.elements import Element
opm = OpticalModel()
sm = opm.seq_model
osp = opm.optical_spec
pm = opm.parax_model
osp.pupil = PupilSpec(osp, key=['image', 'f/#'], value=1.4)
osp.field_of_view = FieldSpec(osp, key=['object', 'angle'], flds=[0., 23])
osp.spectral_region = WvlSpec([(486.1327, 0.5), (587.5618, 1.0), (656.2725, 0.5)], ref_wl=1)
opm.system_spec.title = 'US 4448497'
opm.system_spec.dimensions = 'MM'
opm.radius_mode = True
sm.gaps[0].thi=1e10
sm.add_surface([86.771,9.8837,1.80218,44.7])
sm.ifcs[-1].max_aperture = 38
sm.add_surface([592.843,0.1938])
sm.ifcs[-1].max_aperture = 38
sm.add_surface([50.839,9.6899,1.78797,47.5])
sm.ifcs[-1].max_aperture = 32
sm.add_surface([75.826,2.907])
sm.ifcs[-1].max_aperture = 31
sm.add_surface([141.066,2.7132,1.6727,32.2])
sm.ifcs[-1].max_aperture = 30
sm.add_surface([34.981,16.6512])
sm.ifcs[-1].max_aperture = 25.5
sm.add_surface([0,13])
sm.set_stop()
sm.ifcs[-1].max_aperture = 24.709
sm.add_surface([-34.459,1.938,1.74,28.3])
sm.ifcs[-1].max_aperture = 25.4
sm.add_surface([9689.92,12.9845,1.77279,49.4])
sm.ifcs[-1].max_aperture = 29
sm.add_surface([-56.876,0.3876])
sm.ifcs[-1].max_aperture = 30.5
sm.add_surface([-179.516,8.1395,1.78797,47.5])
sm.ifcs[-1].max_aperture = 33.4
sm.add_surface([-60.444,0.1938])
sm.ifcs[-1].max_aperture = 33.9
sm.add_surface([277.19,5.4264,1.78797,47.5])
sm.ifcs[-1].max_aperture = 33
sm.add_surface([-331.492,74.1])
sm.ifcs[-1].max_aperture = 33
sm.list_surfaces()
sm.list_gaps()
opm.update_model()
sm.list_model()
pm.first_order_data()
layout_plt = plt.figure(FigureClass=InteractiveLayout, opt_model=opm, do_draw_rays=True, do_paraxial_layout=False,
is_dark=isdark).plot()
You can apply vignetting to each field point with the function apply_paraxial_vignetting, e.g.
from rayoptics.raytr.trace import apply_paraxial_vignetting
apply_paraxial_vignetting(opm)
Looks like you're making good progress with the lens import.
Cool, that works. Well yes the import is easy as to do - as long as I can find a way to specify everything. Which seems to be the case so far.
Please let me know if you are interested in having these in your notebook repo. I can submit PRs.
Is there a way to generate spot diagrams - both on axis and for skew rays?
The simple way to do a spot diagram is:
spot_plt = plt.figure(FigureClass=SpotDiagramFigure, opt_model=opm, scale_type=Fit.User_Scale, user_scale_value=0.1, is_dark=isdark).plot()
I added a way to build up dashboards with more control over the constituent parts. You can see these in the "focus tilt notebook.ipynb" and "wavefront notebook.ipynb".
If you'd like to checkin your notebooks, that would be good. We could put them in a subdirectory for Bill Claff models.
The simple way to do a spot diagram is:
spot_plt = plt.figure(FigureClass=SpotDiagramFigure, opt_model=opm, scale_type=Fit.User_Scale, user_scale_value=0.1, is_dark=isdark).plot()
Cool.
I added a way to build up dashboards with more control over the constituent parts. You can see these in the "focus tilt notebook.ipynb" and "wavefront notebook.ipynb".
Very nice - its amazing!
If you'd like to checkin your notebooks, that would be good. We could put them in a subdirectory for Bill Claff models.
They are really patent data - but Bill extracts them and puts them into a text format - the bit that he adds is standardized data, and he adds the diameters. Of course he has his own website / tool to show the lens schema, but his app is limited as it only does 2-d ray tracing based on refractive index - so it is not really meant to evaluate the lens design in any way.
I am interested in photo lenses that map to real production lenses, and these are a subset of the data maintained by Bill.
Happy to submit PRs. Most newer lenses have aspherics so I guess I should wait for the aspheric issue to be resolved before submitting those.
I don't mind what we call the subdirectory - you could call it 'PhotoLensPatents' and put a README that says where it is sourced from.
Updated a fix for the aspheric ray trace problem. Just a note on the patent prescription. It uses the conic constant, rather than the eccentricity. Use the cc keyword for this, not the ec one.
I noticed the fix, so cloned the repo, and also used cc instead of ec. Looks better but still some issues:
I will submit a PR to your notebook repo - it may be easier to play around with it.
Btw I noticed when auto generating the notebook that if I set the aspherical profiles while adding surfaces, they are lost? So I need to add the profiles only after adding all the surfaces?
It should work after the add_surface call (famous last words...)
I'm seeing the cover plate when using the data you uploaded initially. I'll look into the vignetting setting - doesn't work all the time for me either.
I was seeing the cover glass before too - but not in the latest above ...
@mjhoptics thank you for fixing the 58mm f0.95 notebook.
I submitted a PR for a few more examples - generally speaking I am still facing issues with the skew rays going outside of the apertures, and also the layout issues I mentioned before.
I saw a do_apertures flag - tried it but I got an error on setting this to False.
Hi @mjhoptics
I updated my clone of the repo with above, but I do not see a difference.
For example:
Add the line
sm.do_apertures = False
before the line
opm.update_model()
Cool. thank you
Hi
Is it possible to now model lens systems that have aspheric surfaces? Is there an example available?
Thanks and Regards Dibyendu