Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NA difference with Oslo #154

Open
yohann95 opened this issue Aug 29, 2024 · 2 comments
Open

NA difference with Oslo #154

yohann95 opened this issue Aug 29, 2024 · 2 comments

Comments

@yohann95
Copy link

Hello Mike,
First thank you for your return by email. I ask my next question here.

I have noticed a big difference between the NA given by ray-optics and given by Oslo for high NA aspheric lenses. I the example here, Oslo gives 0.8 and ray-optics gives 0.67. What is the reason for that ?

from rayoptics.environment import OpticalModel, PupilSpec, FieldSpec, WvlSpec, EvenPolynomial

opm = OpticalModel()
opm.system_spec.dimensions = 'mm'
opm.radius_mode = True

sm = opm['seq_model']
osp = opm['optical_spec']
pm = opm['parax_model']
ar = opm['analysis_results']

osp['pupil'] = PupilSpec(osp, key=['object', 'epd'], value=2*12.)
osp['fov'] = FieldSpec(osp, key=['object', 'angle'], value=0.)
osp['wvls'] = WvlSpec([(800., 1.)], ref_wl=0)

sm.gaps[0].thi=1e10
sm.add_surface([1e10, 0.])
sm.set_stop()
sm.add_surface([11.325, 9.45, 1.755])
sm.ifcs[sm.cur_surface].profile = EvenPolynomial(
r=11.325,cc=-0.63,coefs=[0., 5.0056e-06, -2.968e-08, -2.3959e-10, -6.0923e-13])
sm.add_surface([1e10, 9.6154])

opm.update_model()

print('NA = %.3f'%abs(ar['parax_data'].fod.img_na))

@mjhoptics
Copy link
Owner

Hello @yohann95,
You're correct that ray-optics doesn't match OSLO. I looked into this some and I think OSLO has the correct answer. The underlying data and calculations are correct; the issue was calculating the NA from the paraxial data.

from rayoptics.environment import *

Create an empty model and fill in by scripting

opm = OpticalModel()
sm  = opm['seq_model']
osp = opm['optical_spec']
pm = opm['parax_model']
em = opm['ele_model']
pt = opm['part_tree']
ar = opm['analysis_results']
opm.system_spec.dimensions = 'mm'
opm.radius_mode = True

osp['pupil'] = PupilSpec(osp, key=['object', 'epd'], value=2*12.)
osp['fov'] = FieldSpec(osp, key=['object', 'angle'], value=0.)
osp['wvls'] = WvlSpec([(800., 1.)], ref_wl=0)

sm.gaps[0].thi=1e10
sm.add_surface([1e10, 0.])
sm.set_stop()
sm.add_surface([11.325, 9.45, 1.755])
sm.ifcs[sm.cur_surface].profile = EvenPolynomial(
r=11.325,cc=-0.63,coefs=[0., 5.0056e-06, -2.968e-08, -2.3959e-10, -6.0923e-13])
sm.add_surface([1e10, 9.6154])

opm.update_model()
layout_plt = plt.figure(FigureClass=InteractiveLayout, opt_model=opm, is_dark=False).plot()

layout_plt

listobj(osp)
aperture: object epd; value=24.0
field: object angle; value=1.0
x,y=0.0 vlx= 0.000 vux= 0.000 vly= 0.000 vuy= 0.000
y=1.0 vly= 0.000 vuy= 0.000 vlx= 0.000 vux= 0.000
central wavelength=  800.0000 nm
wavelength (weight) =  800.0000 (1.000)*
focus shift=0.0
pm.first_order_data()
efl                  15
f                    15
f'                   15
ffl                 -15
pp1          -6.098e-09
bfl               9.615
ppk              -5.385
pp sep            4.065
f/#               0.625
m              -1.5e-09
red          -6.667e+08
obj_dist          1e+10
obj_ang               1
enp_dist             -0
enp_radius           12
na obj          1.2e-09
n obj                 1
img_dist          9.615
img_ht           0.2618
exp_dist         -5.385
exp_radius           12
na img          -0.6247
n img                 1
optical invariant       0.2095
pm.list_model()
           ax_ht        pr_ht       ax_slp       pr_slp         power          tau        index    type
 0:            0 -1.74551e+08      1.2e-09    0.0174551             0        1e+10      1.00000    dummy
 1:           12            0      1.2e-09    0.0174551             0            0      1.00000    transmit
 2:           12            0         -0.8    0.0174551    0.06666667      5.38462      1.75500    transmit
 3:      7.69231    0.0939888         -0.8    0.0174551     -7.55e-11       9.6154      1.00000    transmit
 4: -1.22841e-05     0.261826         -0.8    0.0174551             0            0      1.00000    dummy

ray-optics NA calculation

The paraxial slope in image space is 0.8, in agreement with Oslo. The NA is defined as

NA = n sin(theta)

ray-optics calculates the (paraxial) NA by converting the slope to an angle and taking the sine of that. For high NA systems, such as yours, the sine and tangent differ substantially.

slope = pm.ax[-1][mc.slp]
angle = math.atan(slope)
NA = math.sin(angle)

print(f"{slope=:8.4f}  {angle=:8.4f}  {NA=:8.4f}")
slope= -0.8000  angle= -0.6747  NA= -0.6247

While this may have some logic to it, it may not be right. I did a quick bookshelf survey and Shannon clearly states

paraxial NA = n * slp

I'll change ray-optics to match OSLO's and Shannon's definition of paraxial NA.

Real ray NA

To calculate the real ray NA, trace an axial marginal ray.

f0 = osp['fov'].fields[0]
wvl = osp['wvls'].central_wvl

marg_ray, err = trace_ray(opm, [0, 1], f0, wvl, 
                          output_filter=None, rayerr_filter=None)
list_ray(marg_ray)
            X            Y            Z           L            M            N               Len
  0:      0.00000     12.00000            0     0.000000     0.000000     1.000000        1e+10
  1:      0.00000     12.00000      7.2e-09     0.000000     0.000000     1.000000       7.0803
  2:      0.00000     12.00000       7.0803     0.000000    -0.426858     0.904319       2.6205
  3:      0.00000     10.88143   5.9203e-09     0.000000    -0.749136     0.662417       14.516
  4:      0.00000      0.00725            0     0.000000    -0.749136     0.662417            0

The "real" NA is the sine of the image space marginal ray angle. For the axial marginal ray, this is equal to the M direction consine.

print(f"real ray NA={marg_ray.ray[-1].d[1]:8.4f}")
real ray NA= -0.7491

The transverse ray aberration curves show high order coma, so the system isn't aplanatic.

ta_plt = plt.figure(FigureClass=RayFanFigure, opt_model=opm, data_type='Ray', 
                     scale_type=Fit.All_Same, dpi=130).plot()

ta_plt

Hope this helps. Feel free to ask further questions.

Mike Hayford

@yohann95
Copy link
Author

yohann95 commented Sep 2, 2024

Hello Mike,
Thank you very much for your return. That completely answers my question. There is still a little mismatch between the Oslo NA (=0.80) and the ray-optics NA (=0.75), but this time I think Oslo is wrong. The value of NA=0.80 is not consistent with the Oslo graphical representation...
I have a second question but not related to this one so I open another issue.
Yohann

mjhoptics added a commit that referenced this issue Sep 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants