Multiple lens import example

This notebook shows the steps to follow to open multiple vendor component files using ray-optics.

#%matplotlib widget
%matplotlib inline
# initialization
from rayoptics.environment import *
root_pth = Path(rayoptics.__file__).resolve().parent

Create a new, empty, Optical Model

opm = OpticalModel()
sm = opm['seq_model']
osp = opm['optical_spec']
pm = opm['parax_model']
em = opm['ele_model']
pt = opm['part_tree']

By default, the sequential model will automatically recalculate lens apertures when the model changes. The imported lenses will have apertures defined, so turn the do_apertures setting off.

sm.do_apertures = False

Set the object distance of the empty Optical Model to infinity (1e10 is close enough).

sm.gaps[0].thi = 1e10

Listing the sequential model shows object and image interfaces and the air-filled object distance gap.

sm.list_model()
            c            t        medium     mode   zdr      sd
Obj:     0.000000  1.00000e+10       air             1      1.0000
Img:     0.000000      0.00000                       1      1.0000

Enter the Optical Specifications

The usage specifications for the optical system are defined via different properties of the OpticalSpecs class.

osp['pupil'].key, osp['pupil'].value
(('aperture', 'object', 'pupil'), 1.0)
osp.pupil.value=22
opm.update_model()

Add first component

The parameter t is the spacing following the inserted component.

opm.add_from_file(root_pth/"codev/tests/CODV_32327.seq", t=10.)

The listing shows the imported part is a cemented lens doublet.

sm.list_model()
              c            t        medium     mode   zdr      sd
  Obj:     0.000000  1.00000e+10       air             1      1.0000
32327:     0.016268      6.00000     N-BK7             1      12.000
    2:    -0.022401      2.50000     N-SF5             1      12.289
    3:    -0.007696      10.0000       air             1      12.000
  Img:     0.000000      0.00000                       1      1.0000

The part tree can be displayed using list_model on the part_tree.

pt.list_model()
root
├── Object
├── CE1
└── Image

Generate a lens picture

This is done using the interactivelayout module. All graphics in rayoptics are based on matplotlib.

layout_plt = plt.figure(FigureClass=InteractiveLayout, opt_model=opm,
                        do_draw_rays=True, do_paraxial_layout=False).plot()
../../_images/output_22_0.png

Add the second component

opm.add_from_file(root_pth/"codev/tests/CODV_49664.seq", t=17.8)

The imported element, CE2, is a triplet with a thin, aspheric, cap.

sm.list_model()
              c            t        medium     mode   zdr      sd
  Obj:     0.000000  1.00000e+10       air             1      1.0000
32327:     0.016268      6.00000     N-BK7             1      12.000
    2:    -0.022401      2.50000     N-SF5             1      12.289
    3:    -0.007696      10.0000       air             1      12.000
49663:     0.042553      9.00000   S-LAL 8             1      12.629
    5:    -0.027248      2.50000   S-TIH53             1      11.842
    6:     0.000000    0.0800000   517.520             1      11.004
    7:    -0.003096      17.8000       air             1      10.990
  Img:     0.000000      0.00000                       1      1.0000

Use the listobj() function to get formatted output of an object’s contents.

listobj(sm.ifcs[7].profile)
profile: EvenPolynomial
c=-0.003095913950326,   r=-323.006393602994   conic cnst=0.0
coefficients: [0.0, 1.38925111836e-05, -2.08175206307e-08, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
pt.list_model()
root
├── Object
├── CE1
├── CE2
└── Image
em.list_model()
0: Object (DummyInterface): Surface(lbl='Obj', profile=Spherical(c=0.0), interact_mode='dummy')
1: CE1 (CementedElement): CementedElement: [1, 2, 3]
2: CE2 (CementedElement): CementedElement: [4, 5, 6, 7]
3: Image (DummyInterface): Surface(lbl='Img', profile=Spherical(c=0.0), interact_mode='dummy')
layout_plt0 = plt.figure(FigureClass=InteractiveLayout, opt_model=opm,
                        do_draw_rays=True, do_paraxial_layout=False).plot()
../../_images/output_31_0.png

Flipping a lens element

It is common that importing components in this fashion the the catalog ordering of the surfaces is not what a particular optical model needs. The flip() can be used to flip elements and/or sequences of interfaces.

First, retrieve the element definitions from the part tree.

ce1 = pt.obj_by_name('CE1')
ce2 = pt.obj_by_name('CE2')

Flip the second cemented element.

opm.flip(ce2)
layout_plt1 = plt.figure(FigureClass=InteractiveLayout, opt_model=opm,
                        do_draw_rays=True, do_paraxial_layout=False).plot()
../../_images/output_36_0.png

Restore the element to its original orientation.

opm.flip(ce2)
layout_plt2 = plt.figure(FigureClass=InteractiveLayout, opt_model=opm,
                        do_draw_rays=True, do_paraxial_layout=False).plot()
../../_images/output_39_0.png

Flip operation for a range of Interfaces

Sometimes it is more convenient to specify a range of interface indices.

sm.list_model()
              c            t        medium     mode   zdr      sd
  Obj:     0.000000  1.00000e+10       air             1      1.0000
32327:     0.016268      6.00000     N-BK7             1      12.000
    2:    -0.022401      2.50000     N-SF5             1      12.289
    3:    -0.007696      10.0000       air             1      12.000
49663:     0.042553      9.00000   S-LAL 8             1      12.629
    5:    -0.027248      2.50000   S-TIH53             1      11.842
    6:     0.000000    0.0800000   517.520             1      11.004
    7:    -0.003096      17.8000       air             1      10.990
  Img:     0.000000      0.00000                       1      1.0000

If we want to flip the lens assembly end for end, we would want to flip the range of interfaces from 1 to 7.

opm.flip(1,7)
sm.list_model()
              c            t        medium     mode   zdr      sd
  Obj:     0.000000  1.00000e+10       air             1      1.0000
    1:     0.003096    0.0800000   517.520             1      10.990
    2:    -0.000000      2.50000   S-TIH53             1      11.004
    3:     0.027248      9.00000   S-LAL 8             1      11.842
49663:    -0.042553      10.0000       air             1      12.629
    5:     0.007696      2.50000     N-SF5             1      12.000
    6:     0.022401      6.00000     N-BK7             1      12.289
32327:    -0.016268      17.8000       air             1      12.000
  Img:     0.000000      0.00000                       1      1.0000
layout_plt3 = plt.figure(FigureClass=InteractiveLayout, opt_model=opm,
                        do_draw_rays=True, do_paraxial_layout=False).plot()
../../_images/output_45_0.png

All of the information is transformed by the flip() operation; this can be seen be using listobj() on the 1st interface of the sequential model.

listobj(sm.ifcs[1])
transmit
profile: EvenPolynomial
c=0.003095913950326,   r=323.006393602994   conic cnst=0.0
coefficients: [-0.0, -1.38925111836e-05, 2.08175206307e-08, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0]
surface_od=10.990191081085245