mjhoptics / ray-optics

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

Mixing add_from_file() and add_surface() #135

Open i2pi opened 6 months ago

i2pi commented 6 months ago

I'm trying to design a lens from components where I have the zmax files for some elements but not for others. When mixing add_from_file() and add_surface() I notice that if I look at the generated sequential model, all the surfaces are correct, but if I look at the element model, the elements created by the add_surface()'s disappear.

In the below block of code I add a zemax file and then call add_surface() to add a lens:

from rayoptics.environment import *

opm = OpticalModel()
opm.radius_mode = True

sm = opm['seq_model']   
em = opm['ele_model']    

sm.gaps[0].thi = 1e10

opm.add_from_file('zmax_32921.zmx', t=5) # 40x120 ACH                                     

sm.add_surface([250.0, 2.5, 1.67, 42, 30])
sm.add_surface([-50.0, 7])

opm.update_model()

em.list_elements()
sm.list_model()

In the output of list_elements() the manually added lens doesn't appear, but it does appear in the output of list_model():

0: Object (DummyInterface): Surface(lbl='Obj', profile=Spherical(c=0.0), interact_mode='dummy')
1: Object space (AirGap): Gap(t=10000000000.0, medium=<opticalglass.opticalmedium.Air object at 0x7fabe913e340>)
2: CE2 (CementedElement): CementedElement: [1, 2, 3]
3: Image space (AirGap): Gap(t=5, medium=<opticalglass.opticalmedium.Air object at 0x7fabe913e340>)
4: Image (DummyInterface): Surface(lbl='Img', profile=Spherical(c=0.0), interact_mode='dummy')
              r            t        medium     mode   zdr      sd
  Obj:     0.000000  1.00000e+10       air             1      0.0000
    1:    65.220000      9.60000    N-SSK8             1      19.500
    2:   -62.030000      4.20000    N-SF10             1      19.500
    3: -1240.670000      5.00000       air             1      19.500
    4:   250.000000      2.50000   670.420             1     0.44161
    5:   -50.000000      7.00000       air             1     0.43360
  Img:     0.000000      0.00000                       1     0.35545

As a sanity check, removing the add_from_file() code and then calling list_elements() shows the element correctly:

opm = OpticalModel()
opm.radius_mode = True

sm = opm['seq_model']   
em = opm['ele_model']    

sm.gaps[0].thi = 1e10

# opm.add_from_file('zmax_32921.zmx', t=5) # 40x120 ACH                                     

sm.add_surface([250.0, 2.5, 1.67, 42, 30])
sm.add_surface([-50.0, 7])

opm.update_model()

em.list_elements()
sm.list_model()

Producing:

0: Object (DummyInterface): Surface(lbl='Obj', profile=Spherical(c=0.0), interact_mode='dummy')
1: Object space (AirGap): Gap(t=10000000000.0, medium=<opticalglass.opticalmedium.Air object at 0x7fabe913e340>)
2: E3 (Element): Element: Spherical(c=0.004), Spherical(c=-0.02), t=2.5000, sd=0.5000, glass: 670.420
3: Image (DummyInterface): Surface(lbl='Img', profile=Spherical(c=0.0), interact_mode='dummy')
4: Image space (AirGap): Gap(t=7, medium=<opticalglass.opticalmedium.Air object at 0x7fabfb9a1970>)
              r            t        medium     mode   zdr      sd
  Obj:     0.000000  1.00000e+10       air             1      0.0000
    1:   250.000000      2.50000   670.420             1     0.50000
    2:   -50.000000      7.00000       air             1     0.49800
  Img:     0.000000      0.00000                       1     0.44188
mjhoptics commented 6 months ago

Hello @i2pi , Thank you for the detailed report and yes, unfortunately, there's a problem with how ray-optics is moving changes from the sequential model to the element model. The optical model has a method to fix these problems called rebuild_from_seq.

opm.rebuild_from_seq()

em.list_elements()
sm.list_model()

which produces:

0: Object (DummyInterface): Surface(lbl='Obj', profile=Spherical(c=0.0), interact_mode='dummy')
1: Object space (AirGap): Gap(t=10000000000.0, medium=<opticalglass.opticalmedium.Air object at 0x156f8e610>)
2: CE2 (CementedElement): CementedElement: [1, 2, 3]
3: AG2 (AirGap): Gap(t=5, medium=<opticalglass.opticalmedium.Air object at 0x156f8e610>)
4: E1 (Element): Element: Spherical(c=0.004), Spherical(c=-0.02), t=2.5000, sd=0.3544, glass: 670.420
5: Image space (AirGap): Gap(t=7, medium=<opticalglass.opticalmedium.Air object at 0x158981040>)
6: Image (DummyInterface): Surface(lbl='Img', profile=Spherical(c=0.0), interact_mode='dummy')
              r            t        medium     mode   zdr      sd
  Obj:     0.000000  1.00000e+10       air             1      0.0000
    1:    27.970000      9.50000   S-BAH11             1      12.000
    2:   -18.850000      2.50000    N-SF10             1      12.000
    3:  -152.940000      5.00000       air             1      12.000
    4:   250.000000      2.50000   670.420             1     0.35442
    5:   -50.000000      7.00000       air             1     0.33430
  Img:     0.000000      0.00000                       1     0.20880

I've had several reports of problems of this kind and have been working on a more robust solution to how model changes of various kinds are handled. The solution I've come up with seems very solid, but not all (existing) test models pass yet. Sorry for the trouble. Feel free to ask further questions. Regards Mike