Giter Club home page Giter Club logo

ray-optics's Introduction

ReadTheDocs PyPI-Server Conda-Forge

ray-optics

Installation

To install rayoptics using pip, use

> pip install rayoptics

Alternatively, rayoptics can be installed from the conda-forge channel using conda

> conda install rayoptics --channel conda-forge

Documentation

The documentation for ray-optics is hosted at Read the Docs

Tools for image forming optical design and analysis

RayOptics is a Python geometrical optics and image forming optics library. It provides a geometric ray tracing foundation for the analysis of image forming and coherent optical systems. A number of standard geometrical analysis options such as transverse ray and wavefront aberration analysis are provided on this base. Paraxial layout of optical systems is also supported by y-ybar diagrams and graphical editing of paraxial rays in lens layout views. Import of Zemax .zmx and CODEV .seq files is supported. RayOptics can be used in Python scripts, the Python and IPython shells, the Jupyter notebook, and a Qt-based graphical user interface application.

Getting Help

Users of ray-optics are encouraged to post questions to the Discussions page of the Github repository. Please post any bug reports to the Issues page in the repo. The author welcomes questions and comments posted on Github and will try to be responsive. The author will not respond to emails with questions about ray-optics.

Note

This project has been set up using PyScaffold 4.3. For details and usage information on PyScaffold see https://pyscaffold.org/.

ray-optics's People

Contributors

ajeddeloh avatar dependabot[bot] avatar dominikonysz avatar mjhoptics avatar mpetroff avatar quentgit avatar wuffi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ray-optics's Issues

parse .seq files and write lists

Hello,
I opened a .seq file and was able to plot it following your example in the docu.

How can I write its radii, thicknesses, materials and semi-diameters to python lists?
Is there a method in ray-optics?

Kind regards,
Jonas

list SequentialModel with named glass

Dear Mr. Hayford,

just minor issue.

>>> from rayoptics.environment import *
>>> opm = OpticalModel()
>>> sm  = opm['seq_model']
>>> opm.radius_mode = True
>>> sm.gaps[0].thi=1e10
>>> sm.add_surface([23.713, 4.831, 'N-LAK9', 'Schott'])
>>> sm.add_surface([7331.288, 5.86])
>>> sm.list_elements()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/dist-packages/rayoptics/seq/sequential.py", line 655, in list_elements
    if gp.medium.label.lower() != 'air':
AttributeError: 'SchottGlass' object has no attribute 'label'

create_surface_and_gap with string arguments returns an instance of Glass from opticalglass.glass otherwise an instance of Medium from rayoptics.seq.medium (which has a subtype Glass).

Cheers!

Modeling a Varioptic lens at 0 power

I am looking to model a varioptic lens at 0 power; meaning that the lens acts as a series of flat interfaces. How might I include that in a sequential model using your library?

For reference, this is what I did in a ray transfer matrice equation to model it:

def create_flat_interface():
""" flat interface"""

    # varioptic/flat interface matrix
    flat_oil = sym.Matrix([[1, 0],[0, substrate_dict['WATER']/substrate_dict['OIL']]])
    flat_water = sym.Matrix([[1, 0],[0, substrate_dict['AIR']/substrate_dict['WATER']]])
    flat_air = sym.Matrix([[1, 0],[0, substrate_dict['OIL']/substrate_dict['AIR']]])

    interface_dist = sym.Matrix([[1,d_interface],[0,1]])
    varioptic_interface = flat_air * interface_dist * flat_oil * interface_dist *flat_water #

    return varioptic_interface

Unable to import rayoptics

Hi,

I am trying out your cool project. I installed in on my Linux machine (Redhat 7.7).
Installation was successful but if I try:

from rayoptics.environment import *

I get following error:

>>> from rayoptics.environment import *
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/dylan/pydev/lib64/python3.6/site-packages/rayoptics/environment.py", line 26, in <module>
    from rayoptics.gui.appcmds import create_new_model
  File "/home/dylan/pydev/lib64/python3.6/site-packages/rayoptics/gui/appcmds.py", line 14, in <module>
    from rayoptics.mpl.axisarrayfigure import Fit
  File "/home/dylan/pydev/lib64/python3.6/site-packages/rayoptics/mpl/axisarrayfigure.py", line 14, in <module>
    from scipy.interpolate import spline
ImportError: cannot import name 'spline'

The installed version of scipy is 1.3.1.
There doesn't seem to be a 'spline' attribute in scipy.interpolate.

I am not sure what the issue is - I suspect your project is not compatible with scipy 1.3.1?
Please let me know if there is a workaround.

Thanks and Regards
Dibyendu

Numpy compatibility problem

I did a fresh installation of the module from conda in a separate virtual environment, but couldn't import it:

>>> from rayoptics.environment import *
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/byatharth/opt/miniconda3/envs/rayoptics/lib/python3.12/site-packages/rayoptics/environment.py", line 28, in <module>
    import rayoptics.gui.appcmds as cmds
  File "/Users/byatharth/opt/miniconda3/envs/rayoptics/lib/python3.12/site-packages/rayoptics/gui/appcmds.py", line 19, in <module>
    from rayoptics.codev import cmdproc
  File "/Users/byatharth/opt/miniconda3/envs/rayoptics/lib/python3.12/site-packages/rayoptics/codev/cmdproc.py", line 16, in <module>
    from rayoptics.elem.surface import (DecenterData, Circular, Rectangular,
  File "/Users/byatharth/opt/miniconda3/envs/rayoptics/lib/python3.12/site-packages/rayoptics/elem/surface.py", line 35, in <module>
    import transforms3d as t3d
  File "/Users/byatharth/opt/miniconda3/envs/rayoptics/lib/python3.12/site-packages/transforms3d/__init__.py", line 10, in <module>
    from . import quaternions
  File "/Users/byatharth/opt/miniconda3/envs/rayoptics/lib/python3.12/site-packages/transforms3d/quaternions.py", line 26, in <module>
    _MAX_FLOAT = np.maximum_sctype(np.float64)
                 ^^^^^^^^^^^^^^^^^
  File "/Users/byatharth/opt/miniconda3/envs/rayoptics/lib/python3.12/site-packages/numpy/__init__.py", line 397, in __getattr__
    raise AttributeError(
AttributeError: `np.maximum_sctype` was removed in the NumPy 2.0 release. Use a specific dtype instead. You should avoid relying on any implicit mechanism and select the largest dtype of a kind explicitly in the code.

Unfortunately, I have no experience with these things. Thanks for the help.

plt.figure does not open graphics window

First, MANY KUDOS for writing a FOSS optical design solution. It's been a long time coming and it's especially nice to have a solution for Linux. I've long been tempted to do write my own (having especially strong ideas of what a GUI might be like), but one has only so many hours in the day. I see you used to work on SYNOPSYS. It's funny, I just found this ray-optics project after the free trial they sent me wouldn't run on a virtual machine (it detected it was in a virtual machine and quit), and I got it to run in WINE exactly once. Anyway, left a bad taste in my mouth before even trying it. Have experience with Zemax and OSLO, and I'm ready for something different. Anyway, thank you! THANK YOU.

I've tried this on two different PCs, both running Debian 10. Going through the Triplet Example tutorial, when I run layout_plt = plt.figure(FigureClass=InteractiveLayout, opt_model=opm, do_draw_rays=True, do_paraxial_layout=False, is_dark=isdark).plot(), nothing happens, though I expect that this is what should open the graphics window showing the raytrace. I wondered if there's a step missing in the docs, i.e. something like plt.show(layout_plt)?

If I run just plt.figure(FigureClass=InteractiveLayout, opt_model=opm, do_draw_rays=True, do_paraxial_layout=False, is_dark=isdark).plot(), it spits out <InteractiveLayout size 640x480 with 1 Axes>, so it seems like things are otherwise functioning.

On a possibly related note, I can't find the mysterious desktop application. The command "rayoptics" isn't in my path, and I haven't figured out where the binary might be.

~Justine

Wedge or mirror ?

Hi.
Can I use wedged window, angled mirror or off axis beam?
Thanks.
Petro.

addsurface

I wat to konw how to use addsurface(class) to creat ray chase。if there is a phase data,how can I get optical chase?

Prefer using local loggers

Hey Michael,

would it be possible to replace the global logging (e.g. logging.info) with local loggers?

logger = logging.getLogger(__name__)
logger.info(...)

I'm having the issue that when using an INFO log level some logs from rayoptics are also printed. Since they are using the root logger I cannot just hide the logs coming from rayoptics. Particularly the one at medium.py:109 is printed very often in my case.

I was thinking of providing you with a PR but realized that there are some cases where you are logging into files. It didn't seem so straightforward to refactor them in a simple way while adhering to your style. Neither it would have been a good approach to just refactor the ones bothering me.

It's not a very urgent thing for me but maybe you could keep that in mind whenever you do something to the code next time.

Refractive index

Very nice package!

I am working in the IR range down to long wavelengths and I was wondering about the best way to add arbitrary refractive index data. I typically get them from refractiveindex.info and extrapolate if necessary, so I could produce a 2D list or a numpy array.

Wrong root is picked in intersect function for Spherical surface profile.

Hi,

I found a bug in the following function:

def intersect(self, p, d, eps, z_dir):

It seems to be picking the wrong quadratic equation root and as a result, breaking the whole raytracing process. However, the commented-out function:
# return super().intersect(p, d, eps, z_dir)

can handle the problem.

This issue arises when a simple relay lens, consisting of two positive lenses, is used and their separation causes the effective focal length to change from positive to negative. When this occurs, along with an aperture stop located in the middle of the two lenses, mathematical errors can occur within the intersect function. The following code provides an example:

from rayoptics.environment import *

isdark = False

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']

osp['pupil'] = PupilSpec(osp, key=['object', 'NA'], value=0.01)
osp['fov'] = FieldSpec(osp, key=['object', 'height'], value=5, flds=[0., 0.707, 1.], is_relative=True)
osp['wvls'] = WvlSpec([(780.2, 1.0)], ref_wl=0)

opm.radius_mode = True


sm.gaps[0].thi=100


sm.add_surface([100, 3, 'N-BK7', "Schott"]) 
sm.add_surface([-100, 98]) # when thi changed to 97, works
sm.add_surface([0,98]) # when thi changed to 97, works
sm.set_stop()
sm.add_surface([100, 3.1, 'N-BK7', "Schott"])
sm.add_surface([-100, 2])


opm.update_model()
pm.first_order_data()

layout_plt = plt.figure(FigureClass=InteractiveLayout, opt_model=opm, is_dark=isdark).plot()
plt.show()

While I can understand that is a peculiar situation a similar bug occurs also when only half of the system is considered:

from rayoptics.environment import *

isdark = False

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']

osp['pupil'] = PupilSpec(osp, key=['object', 'NA'], value=0.01)
osp['fov'] = FieldSpec(osp, key=['object', 'height'], value=5, flds=[0., 0.707, 1.], is_relative=True)
osp['wvls'] = WvlSpec([(780.2, 1.0)], ref_wl=0)

opm.radius_mode = True


sm.gaps[0].thi=100


sm.add_surface([100, 3, 'N-BK7', "Schott"]) 
sm.add_surface([-100, 98]) # when thi changed to 97, works
sm.add_surface([0,0])
sm.set_stop()


opm.update_model()
pm.first_order_data()

layout_plt = plt.figure(FigureClass=InteractiveLayout, opt_model=opm, is_dark=isdark).plot()
plt.show()

From what I understand, the problem is related to the inputs of the function. I noticed, that the d parameter is getting the opposite sign when the bug occurs, while z_dir stays the same. As the other super().intersect does not rely on z_dir, the problem does not appear. The issue may be related to the position of the aperture stop since the bug does not appear when it is placed on another surface. Because of that, I would guess it may originate already here:

def compute_first_order(opt_model, stop, wvl):

when already for the first surface enp_dist is miscalculated, or not handled as intended. However, this is only a suspicion and not a definitive statement.

Added surfaces not shown in plot

Hello Michael,

I'm currently having a problem with plotting an optical model after adding surfaces later on using seq_model.add_surface(...). Here is an example using the Triplet example:

from rayoptics.environment import *

from matplotlib import pyplot as plt

opm = OpticalModel()
sm = opm.seq_model

osp = opm.optical_spec
osp['pupil'] = PupilSpec(osp, key=['object', 'pupil'], value=12.5)
osp['wvls'] = WvlSpec([('F', 0.5), (587.5618, 1.0), ('C', 0.5)], ref_wl=1)
max_field = 20
FLD = [1e-10, 0.5 * max_field, 0.75 * max_field, max_field]
osp["fov"] = FieldSpec(osp, key=["object", "angle"], value=max(FLD), flds=FLD)

opm.radius_mode = True
sm.gaps[0].thi=1e10

sm.add_surface([23, 4.831, 'N-LAK9', 'Schott'])
sm.add_surface([7331.288, 5.86])
sm.add_surface([-24.456, .975, 'N-SF5,Schott'])
sm.set_stop()
sm.add_surface([21.896, 4.822])
sm.add_surface([86.759, 3.127, 'N-LAK9', 'Schott'])
sm.add_surface([-20.4942, 41.2365])

opm.update_model()

# Here everything is fine
plt.figure(
    100,
    FigureClass=InteractiveLayout,
    opt_model=opm,
    is_dark=True,
).plot()
plt.show()

opm.radius_mode = True
sm.set_cur_surface(3)
sm.add_surface([100, 5, 'N-LAK9', 'Schott'])
sm.add_surface([1000, 1])

opm.update_model()

# Here the new lens is missing but the rays are interacting with the hidden lens
plt.figure(
    100,
    FigureClass=InteractiveLayout,
    opt_model=opm,
    is_dark=True,
).plot()
plt.show()

So plotting the example optical model obviously works as expected:

but in the second plot the new lens (between the middle and last lens) is not shown although the rays are being traced correctly:

As far as I could see the new elements are not being added to the part_tree although the ids of the other elements are updated.
I have also tried running opm.update_model(build="rebuild") but that didn't help unfortunately.

Am I missing some step additional to the opm.update_model() or is there some other way to force the reload of the part_tree to correctly display the plot?

Cannot import spline

Screenshot from 2021-01-28 10-25-11

I believe the update to optical glass 0.7 needs scipy > 1.3, however the function spline is only provided in scipy < 1.3.

Otherwise, I am getting this error. Can this be updated accordingly, please?

python 3.6
scipy 1.5.4

Permission error for log file

Thanks a lot for writing and sharing this very nice and helpful software!

A potential caveat for new users is a permission problem for the log file when running rayoptics in a directory for which the user does not have write permissions:

(rayoptics) C:\Users>rayoptics
Traceback (most recent call last):
  File "C:\Users\user\Anaconda3\envs\rayoptics\Scripts\rayoptics-script.py", line 9, in <module>
    sys.exit(main())
  File "C:\Users\users\Anaconda3\envs\rayoptics\lib\site-packages\rayoptics\qtgui\rayopticsapp.py", line 563, in main
    logging.basicConfig(filename='rayoptics.log',
  File "C:\Users\users\Anaconda3\envs\rayoptics\lib\logging\__init__.py", line 2003, in basicConfig
    h = FileHandler(filename, mode,
  File "C:\Users\users\Anaconda3\envs\rayoptics\lib\logging\__init__.py", line 1146, in __init__
    StreamHandler.__init__(self, self._open())
  File "C:\Users\users\Anaconda3\envs\rayoptics\lib\logging\__init__.py", line 1175, in _open
    return open(self.baseFilename, self.mode, encoding=self.encoding,
PermissionError: [Errno 13] Permission denied: 'C:\\Users\\rayoptics.log'

Handling this as an exception in line 563 of rayopticsapp.py might be a possible fix:

def main():
    try:
        logging.basicConfig(filename='rayoptics.log',
                            filemode='w',
                            level=logging.INFO)
    except:
        logging.basicConfig(filename=Path.home().joinpath('rayoptics.log'),
                            filemode='w',
                            level=logging.INFO)

Since different operating systems use different directories for log files, I find it difficult to find a good cross-platform solution.

Software crash on linux after initialization with rayoptics

The problem is in the name of OpticalGlass/Data/SUMITA.xlsx file. The software wants to read it as 'Sumita.xlsx' after initialization. Although, It works after you change the name as above but it's better to update the code to read it in it's standard form.

How to building locally

I assume that if I want to build and install locally all I need to do is this?

python setup.py install

Thanks and Regards

Zemax import failed

Hi,

I try to open zmx file extracted from zar file (unzar via zmxtools https://zmxtools.readthedocs.io/en/latest/),
it was failed to open via rayoptics GUI .

Traceback (most recent call last):
File "/home/dino/.local/lib/python3.10/site-packages/rayoptics/qtgui/rayopticsapp.py", line 240, in do_file_action
self.file_action(q.text())
File "/home/dino/.local/lib/python3.10/site-packages/rayoptics/qtgui/rayopticsapp.py", line 268, in file_action
self.open_file(filename)
File "/home/dino/.local/lib/python3.10/site-packages/rayoptics/qtgui/rayopticsapp.py", line 310, in open_file
opt_model = cmds.open_model(file_name, **kwargs)
File "/home/dino/.local/lib/python3.10/site-packages/rayoptics/gui/appcmds.py", line 91, in open_model
opm, import_info = zmxread.read_lens_file(file_url_pth, **kwargs)
File "/home/dino/.local/lib/python3.10/site-packages/rayoptics/zemax/zmxread.py", line 65, in read_lens_file
opt_model, info = read_lens(filename, inpt, **kwargs)
File "/home/dino/.local/lib/python3.10/site-packages/rayoptics/zemax/zmxread.py", line 104, in read_lens
process_line(opt_model, line, i+1)
File "/home/dino/.local/lib/python3.10/site-packages/rayoptics/zemax/zmxread.py", line 182, in process_line
elif handle_aperture_data(opt_model, cur, cmd, inputs):
File "/home/dino/.local/lib/python3.10/site-packages/rayoptics/zemax/zmxread.py", line 379, in handle_aperture_data
ca_list = ifc.clear_apertures
AttributeError: 'ThinLens' object has no attribute 'clear_apertures'. Did you mean: 'max_aperture'?
qt.qpa.xcb: QXcbConnection: XCB error: 3 (BadWindow), sequence: 1714, resource id: 22272805, major code: 40 (TranslateCoords), minor code: 0

The zar file got from Open HSI models (https://github.com/openhsi/openhsi-models).
It seems to have a problem on zmxread.py,

File "/home/dino/.local/lib/python3.10/site-packages/rayoptics/zemax/zmxread.py", line 379, in handle_aperture_data
ca_list = ifc.clear_apertures
AttributeError: 'ThinLens' object has no attribute 'clear_apertures'. Did you mean: 'max_aperture'?

I modified

ca_list = []

It looks partially working (I could see the lens view), something for help.

ss

open-hsi-opt1.zmx.txt

Help needed with first steps: example eye model

Hi,

I stumbled upon this nice package and thought I give it a spin. I first struggled to get it up and running, but soon realized my python 3.6 was not up for it. Now on python 3.9 it runs like a charm, but unfortunately on this run I bump into issues.

I made a Jupiter notebook trying to model the lenses of the eye. Since opticalglass obviously does not provide the relevant data, I use opticalglas.glass.calc_glass_constant(nd,nF,nC)[0] to calculate V-number needed for sm.add_surface(surf_data) with surf_data being the list [curvature, thickness, refractive_index, v-number] . I think I understood how that works and I think its fine.

However, when I put all together it won't plot. I think it has to do with drawing the element box. If I use the same refractive index and V number everywhere it plots just fine. Any idea what is going wrong?

Eye Notebook

Plots are not printing

I'm running the example:

%matplotlib widget

initialization

from rayoptics.environment import *
import matplotlib
matplotlib.use('Qt5Agg')

root_pth = Path(rayoptics.file).resolve().parent

app = AppManager(None)
opm = app.model = OpticalModel()
sm = opm['seq_model']
osp = opm['optical_spec']
pm = opm['parax_model']
em = opm['ele_model']
pt = opm['part_tree']

copying the code until:
layout_plt0 = plt.figure(FigureClass=InteractiveLayout, opt_model=opm,do_draw_rays=True, do_paraxial_layout=False).plot()

The code runs but it will not display a plot I've tried this in both VScode and Juypter notebook.

Zemax parsing errors if TYPE is not set on surfaces

Edmund optics omits the TYPE line on their zmx files which causes the parser to fail. Parsing succeeds if I add a line:

TYPE STANDARD

to every surface definition.

Lens on edmund I was trying to load:
https://www.edmundoptics.com/p/11-with-100mm-and-100mm-efl-achromats-30mm-achromatic-pair/12126/

Traceback:

[andrew@lime ~]$ rayoptics
close event received: <PyQt5.QtWidgets.QMdiSubWindow object at 0x7f05a9fb7160>
Traceback (most recent call last):
  File "/home/andrew/.local/lib/python3.8/site-packages/rayoptics/qtgui/rayopticsapp.py", line 235, in file_action
    self.open_file(filename)
  File "/home/andrew/.local/lib/python3.8/site-packages/rayoptics/qtgui/rayopticsapp.py", line 262, in open_file
    opt_model = cmds.open_model(file_name, **kwargs)
  File "/home/andrew/.local/lib/python3.8/site-packages/rayoptics/gui/appcmds.py", line 77, in open_model
    opm, import_info = zmxread.read_lens_file(file_name, **kwargs)
  File "/home/andrew/.local/lib/python3.8/site-packages/rayoptics/zemax/zmxread.py", line 62, in read_lens_file
    opt_model, info = read_lens(filename, inpt, **kwargs)
  File "/home/andrew/.local/lib/python3.8/site-packages/rayoptics/zemax/zmxread.py", line 100, in read_lens
    process_line(opt_model, line, i+1)
  File "/home/andrew/.local/lib/python3.8/site-packages/rayoptics/zemax/zmxread.py", line 138, in process_line
    if s.z_type != 'PARAXIAL':
AttributeError: 'Surface' object has no attribute 'z_type'
Aborted (core dumped)

The offending code is here:

https://github.com/mjhoptics/ray-optics/blob/master/src/rayoptics/zemax/zmxread.py#L138

Perhaps interfaces should have a z_type of STANDARD by default? Let me know if you can't reproduce or need any more information.

P.S. This is a great project. I'm trying to teach myseld geometric optics and it's invaluable to be able to try things out. Thanks for all the work that's made this possible.

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

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

Finite source field point

Hello, I've been really impressed with this package. I'm wondering if there is a way to have a field source point that located a finite distance from the first lens. I sense that it must be related to the 'NA' key of the PupilSpec class, but I keep getting errors from that:
osp.pupil = PupilSpec(osp, key=['object', 'NA'], value=0.2)

Any thoughts would be greatly appreciated.

IndexError in appcmds.open_model if no multi-part path is provided.

rayoptics/gui/appcmds.py l88 checks for parts and fails, as the check tries to access the second element in a tuple that only has one element.
`

la4966, info = open_model("./la4966.zmx", info=True)
<<< /tmp/ray-optics/./lib64/python3.11/site-packages/rayoptics/gui/appcmds.py:88, in open_model(file_url, info, post_process_imports, **kwargs)
<<< 86 else:
<<< 87 # if we're importing another program's file, collect import info
<<<---> 88 if len(file_url_pth.parts) > 0 and file_url_pth.parts[1] == 'www.photonstophotos.net':
<<< 89 opm, import_info = obench.read_obench_url(file_url, **kwargs)
<<<
<<< IndexError: tuple index out of range`

Passing a full path (multiple parts) works:
`

la4966, info = open_model("/tmp/ray-optics/la4966.zmx", info=True)

`

I recommend using
try: ... except: ...
instead of if: ... else: ....

Cheers,
-ix

Mixing add_from_file() and add_surface()

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

module 'rayoptics.elem.elements' has no attribute 'DummyInterface'

Hi,
i just wanted to try out some stuff but as soon as a new optical model is to be created it throws the error message used for the title.

I reduced the script to the following:

from rayoptics.environment import *
opm = OpticalModel()

For some reason this here works flawless:

from rayoptics.elem.elements import DummyInterface

Here is the complete error message:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[17], line 1
----> 1 opm = OpticalModel()

File ~/pythen_env/lib/python3.11/site-packages/rayoptics/optical/opticalmodel.py:135, in OpticalModel.__init__(self, radius_mode, specsheet, **kwargs)
    132 if kwargs.get('do_init', True):
    133     # need to do this after OpticalSpec is initialized
    134     self.seq_model.update_model()
--> 135     elements_from_sequence(self.ele_model,
    136                            self.seq_model,
    137                            self.part_tree)

File ~/pythen_env/lib/python3.11/site-packages/rayoptics/elem/parttree.py:362, in elements_from_sequence(ele_model, seq_model, part_tree)
    360 num_eles = len(eles)
    361 if num_eles == 0:
--> 362     process_airgap(
    363         ele_model, seq_model, part_tree,
    364         i, g, z_dir, ifc, g_tfrm, add_ele=True)
    365 else:
    366     if buried_reflector is True:

File ~/pythen_env/lib/python3.11/site-packages/rayoptics/elem/parttree.py:484, in process_airgap(ele_model, seq_model, part_tree, i, g, z_dir, s, g_tfrm, add_ele)
    482 if add_dummy:
    483     sd = s.surface_od()
--> 484     di = elements.DummyInterface(s, sd=sd, tfrm=g_tfrm, idx=i,
    485                                  label=dummy_label)
    486     part_tree.add_element_to_tree(di, tag=dummy_tag)
    487     ele_model.add_element(di)

AttributeError: module 'rayoptics.elem.elements' has no attribute 'DummyInterface'

Btw.: I installed ryoptics (version 0.8.5) via pip

Kind regards

Does this package do exact ray-tracing, or uses the paraxial approximation?

Hello,

Thank you for putting together this package!

I'm looking for a ray-tracing package that does exact ray-tracing, not just paraxial ray-tracing, but I could not determine if this package is suitable.

Can you confirm to me that this package implements exact ray-tracing? If so, you might want to also mention it in the documentation.

Cheers!
Laurent

Possible issue

Hi
I am getting a bit strange result with this one:

%matplotlib inline
isdark = False
from rayoptics.environment import *
from rayoptics.elem.elements import Element
from rayoptics.raytr.trace import apply_paraxial_vignetting

# JP2019-152887 Example 3 (Nikon AF-S Nikkor 400mm f/2.8 E FL ED VR)
# Obtained via https://www.photonstophotos.net/GeneralTopics/Lenses/OpticalBench/OpticalBenchHub.htm

opm = OpticalModel()
sm  = opm.seq_model
osp = opm.optical_spec
pm = opm.parax_model
osp.pupil = PupilSpec(osp, key=['image', 'f/#'], value=2.89)
osp.field_of_view = FieldSpec(osp, key=['object', 'angle'], flds=[0., 3.14])
osp.spectral_region = WvlSpec([(486.1327, 0.5), (587.5618, 1.0), (656.2725, 0.5)], ref_wl=1)
opm.system_spec.title = 'JP2019-152887 Example 3 (Nikon AF-S Nikkor 400mm f/2.8 E FL ED VR)'
opm.system_spec.dimensions = 'MM'
opm.radius_mode = True
sm.gaps[0].thi=1e10
sm.add_surface([1200.37,5,1.5168,63.88])
sm.ifcs[sm.cur_surface].max_aperture = 69.61
sm.add_surface([1199.79,1])
sm.ifcs[sm.cur_surface].max_aperture = 69.61
sm.add_surface([207.079,17.5,1.43384,95.26])
sm.ifcs[sm.cur_surface].max_aperture = 68.61
sm.add_surface([-1127.53,44.9])
sm.ifcs[sm.cur_surface].max_aperture = 68.61
sm.add_surface([175.97,18,1.43384,95.26])
sm.ifcs[sm.cur_surface].max_aperture = 57.61
sm.add_surface([-397.271,3.07])
sm.ifcs[sm.cur_surface].max_aperture = 57.61
sm.add_surface([-360.24,6,1.61266,44.46])
sm.ifcs[sm.cur_surface].max_aperture = 55.36
sm.add_surface([353.184,90])
sm.ifcs[sm.cur_surface].max_aperture = 55.36
sm.add_surface([66.4844,4,1.795,45.32])
sm.ifcs[sm.cur_surface].max_aperture = 34.61
sm.add_surface([45.9182,15,1.49782,82.54])
sm.ifcs[sm.cur_surface].max_aperture = 31.61
sm.add_surface([1114.11,18.503])
sm.ifcs[sm.cur_surface].max_aperture = 31.61
sm.add_surface([2992.55,2.5,1.755,52.34])
sm.ifcs[sm.cur_surface].max_aperture = 24.11
sm.add_surface([118.04,3.35])
sm.ifcs[sm.cur_surface].max_aperture = 23.36
sm.add_surface([-241.694,3.5,1.84668,23.83])
sm.ifcs[sm.cur_surface].max_aperture = 23.36
sm.add_surface([-86.4136,2.4,1.53996,59.52])
sm.ifcs[sm.cur_surface].max_aperture = 23.36
sm.add_surface([64.2643,38.179])
sm.ifcs[sm.cur_surface].max_aperture = 23.36
sm.add_surface([0,1.5])
sm.set_stop()
sm.ifcs[sm.cur_surface].max_aperture = 18.979
sm.add_surface([90.0336,7.6,1.48749,70.43])
sm.ifcs[sm.cur_surface].max_aperture = 19.36
sm.add_surface([-63.8039,1.2])
sm.ifcs[sm.cur_surface].max_aperture = 19.36
sm.add_surface([-65.9768,1.9,1.84668,23.83])
sm.ifcs[sm.cur_surface].max_aperture = 18.36
sm.add_surface([-114.876,5])
sm.ifcs[sm.cur_surface].max_aperture = 18.36
sm.add_surface([300.359,3.5,1.84668,23.83])
sm.ifcs[sm.cur_surface].max_aperture = 18.36
sm.add_surface([-128.056,1.9,1.59319,67.94])
sm.ifcs[sm.cur_surface].max_aperture = 18.36
sm.add_surface([53.9004,3.1])
sm.ifcs[sm.cur_surface].max_aperture = 18.36
sm.add_surface([-347.542,1.9,1.755,52.33])
sm.ifcs[sm.cur_surface].max_aperture = 17.36
sm.add_surface([94.5337,4.19])
sm.ifcs[sm.cur_surface].max_aperture = 17.36
sm.add_surface([118.353,3.5,1.7725,49.68])
sm.ifcs[sm.cur_surface].max_aperture = 16.86
sm.add_surface([-384.382,0.1])
sm.ifcs[sm.cur_surface].max_aperture = 16.86
sm.add_surface([67.4622,4.5,1.64,60.14])
sm.ifcs[sm.cur_surface].max_aperture = 17.11
sm.add_surface([-340.421,1.9,1.84668,23.83])
sm.ifcs[sm.cur_surface].max_aperture = 17.11
sm.add_surface([246.642,6.5])
sm.ifcs[sm.cur_surface].max_aperture = 17.11
sm.add_surface([0,1.5,1.5168,63.88])
sm.ifcs[sm.cur_surface].max_aperture = 17.86
sm.add_surface([0,74.22])
sm.ifcs[sm.cur_surface].max_aperture = 17.86
sm.list_surfaces()
sm.list_gaps()
sm.do_apertures = False
opm.update_model()
apply_paraxial_vignetting(opm)
layout_plt = plt.figure(FigureClass=InteractiveLayout, opt_model=opm, do_draw_rays=True, do_paraxial_layout=False,
                        is_dark=isdark).plot()
sm.list_model()
# List the optical specifications
pm.first_order_data()
# List the paraxial model
pm.list_lens()
# Plot the transverse ray aberrations
abr_plt = plt.figure(FigureClass=RayFanFigure, opt_model=opm,
          data_type='Ray', scale_type=Fit.All_Same, is_dark=isdark).plot()
# Plot the wavefront aberration
wav_plt = plt.figure(FigureClass=RayFanFigure, opt_model=opm,
          data_type='OPD', scale_type=Fit.All_Same, is_dark=isdark).plot()
# Plot spot diagrams
spot_plt = plt.figure(FigureClass=SpotDiagramFigure, opt_model=opm,
                      scale_type=Fit.User_Scale, user_scale_value=0.1, is_dark=isdark).plot()

Failed to execute example

Hello,

I tried to do your example:

root_pth = Path(rayoptics.file).resolve().parent
opm, info = open_model(root_pth/"zemax/tests/354710-C-Zemax(ZMX).zmx", info=True)

osp = opm.optical_spec

sm = opm['seq_model']
#osp = opm['optical_spec']
#pm = opm['parax_model']
#em = opm['ele_model']
#pt = opm['part_tree']
#ar = opm['analysis_results']

And get this error:

TypeError Traceback (most recent call last)
Input In [36], in <cell line: 3>()
1 osp = opm.optical_spec
----> 3 sm = opm['seq_model']

TypeError: 'OpticalModel' object is not subscriptable

Best regards,
Jonas

software crashes after initialization

The problem is in the name of OpticalGlass/Data/SUMITA.xlsx file. The software wants to read it as 'Sumita.xlsx' after initialization.
When I changed the name of the file it starts working. But Its better to change the code to read it in its stansard form.

Unable to initiate qtgui

Hi,

I tried to start rayoptics from the command line but the program froze and shut down by itself (without displaying any UI elements). In the "rayoptics.log" I got the following warning: .virtualenvs\rayoptics\lib\site-packages\scipy\optimize_zeros_py.py:348: RuntimeWarning: Tolerance of -2.0827775648601943e-07 reached. warnings.warn(msg, RuntimeWarning). Is this the cause of the problem?

I'm using rayoptics under Win10, Python 3.9.

Thank you in advance for any advices on how to fix the issue.

Best regards,
Zhiyao

Filenames in the zemax module need to be pathlib paths (and this is not documented)

Hi there,
I noticed that when trying to use functions from the zemax module (such as read_lens_file), supplying a path as a string will not work, as the first input argument filename is expected to have the methods found in a pathlib Path object (such as "open")
This doesn't seem to be documented.

The fix seems to be to use such a syntax:

filepath = pathlib.Path("subdir/myfile.zmx')
rayoptics.zemax.zmxread.read_lens_file(filepath)

Would it be possible to update the docs to reflect this, or alternatively to enable paths supplied as string objects?

qt dpi ratio error?

After
$python3 -m venv venv
$source venv/bin/activate
$pip install pip --upgrade
$pip install rayoptics
And
$rayoptics

I get the following error:
...
/rayoptics/qtgui/plotview.py", line 288, in get_icon
pm.setDevicePixelRatio(fig.canvas._dpi_ratio)
AttributeError: 'PlotCanvas' object has no attribute '_dpi_ratio'

Maybe this is a related fix
rgerum/pylustrator@23ecf67

Usage of EvenPolynomial Class

Dear Michael,

thank you very much for your efforts writing this library.
So far I am tried to use it for simple spherical lens systems in sequential mode and was impressed by the library capabilities.
I am still finding my way around the lib though. I would like to add some aspheric coefficents to some of the surfaces. May I ask how to define them? add_surface method does not seem to have this options, I noticed the EvenPolynomial Class but unfortunately did not find much information how to use it.
Is there a way to import the sag in discrete form numerically, does rayoptics provide methods to perform EvenPolynomial fits?

Regards,
Jaka

Hang opening model

open_model hangs when opening this model: (from edmund optics part no. 67-270)

I came across this when trying running through their entire catalog, importing them all; I have no immediate use for this.

VERS 171115
DBDT 0 67270 1.000E+10 0.000E+00
DBDT 1 0 1 0 0 1 0
MODE SEQ
NAME 67270 Asphere UV 25mm x 20mm
NOTE 0 Asphere UV 25mm x 20mm UV CTD TS
PFIL 0 0 0
LANG 0
UNIT MM X W X CM MR CPMM
ENPD 25
ENVD 20 1 0
GFAC 0 0
GCAT SCHOTT OHARA CORNING 
SDMA 0 1 0
OMMA 1 1
FTYP 0 0 1 1 0 0 0 1
ROPD 2
HYPR 0
PICB 1
XFLN 0
YFLN 0
FWGN 1
WAVM 1 0.58756180000000002 1
PWAV 1
GLRS 1 0
SURF 0
  FIMP 

  CURV 0.0
  DISZ INFINITY
  MEMA 0 0 0 0 1 ""
SURF 1
  COMM 67270
  STOP
  TYPE EVENASPH
  FIMP 

  CURV 1.090512540894220256E-01
  COAT EO_MGF2(350NM)
  PARM 1 0
  PARM 2 8.7918690842930003e-05
  PARM 3 3.0516522809890002e-07
  PARM 4 -7.9505969444229997e-10
  PARM 5 8.0420427876200001e-12
  PARM 6 0
  PARM 7 0
  PARM 8 0
  DISZ 14
  GLAS C79-80
  CONI -1.1026260927610001
  DIAM 11.25 1 0 0 1 ""
  OEMA 1.25 0 0 0 1 ""
  MEMA 12.5 0 0 0 1 ""
  FLAP 0 11.25 0
SURF 2
  FIMP 

  CURV 0.0
  COAT EO_UVAR
  DISZ 10.439323912615771
  MAZH 0 1
  DIAM 11.25 1 0 0 1 ""
  OEMA 1.25 0 0 0 1 ""
  MEMA 12.5 0 0 0 1 ""
  FLAP 0 11.25 0
SURF 3
  FIMP 

  CURV 0.0
  DISZ 0
  MEMA 0 0 0 0 1 ""

I let it run for several minutes before killing it. Here's the traceback when I killed it:

KeyboardInterrupt:

In [7]: open_model('edmund_optics/67270.zmx')
^C---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-7-82ab977afbf0> in <module>
----> 1 open_model('edmund_optics/67270.zmx')

~/ray-optics/src/rayoptics/gui/appcmds.py in open_model(file_name, info, **kwargs)
     75         opm = open_roa(file_name, **kwargs)
     76     elif file_extension == '.zmx':
---> 77         opm, import_info = zmxread.read_lens_file(file_name, **kwargs)
     78         if info:
     79             return opm, import_info

~/ray-optics/src/rayoptics/zemax/zmxread.py in read_lens_file(filename, **kwargs)
     63             break
     64
---> 65     opt_model, info = read_lens(filename, inpt, **kwargs)
     66     _track_contents['encoding'] = decode
     67

~/ray-optics/src/rayoptics/zemax/zmxread.py in read_lens(filename, inpt, **kwargs)
    106     _glass_handler.save_replacements()
    107
--> 108     opt_model.update_model()
    109
    110     info = _track_contents, _glass_handler.glasses_not_found

~/ray-optics/src/rayoptics/optical/opticalmodel.py in update_model(self)
    152
    153     def update_model(self):
--> 154         self.seq_model.update_model()
    155         self.optical_spec.update_model()
    156         self.parax_model.update_model()

~/ray-optics/src/rayoptics/seq/sequential.py in update_model(self)
    342             osp.update_model()
    343
--> 344             self.set_clear_apertures()
    345
    346     def apply_scale_factor(self, scale_factor):

~/ray-optics/src/rayoptics/seq/sequential.py in set_clear_apertures(self)
    660
    661     def set_clear_apertures(self):
--> 662         rayset = trace.trace_boundary_rays(self.opt_model,
    663                                            use_named_tuples=True)
    664

~/ray-optics/src/rayoptics/raytr/trace.py in trace_boundary_rays(opt_model, **kwargs)
    284     fov = opt_model.optical_spec.field_of_view
    285     for fi, fld in enumerate(fov.fields):
--> 286         rim_rays = trace_boundary_rays_at_field(opt_model, fld, wvl, **kwargs)
    287         fld.pupil_rays = boundary_ray_dict(opt_model, rim_rays)
    288         rayset.append(rim_rays)

~/ray-optics/src/rayoptics/raytr/trace.py in trace_boundary_rays_at_field(opt_model, fld, wvl, use_named_tuples)
    261     for p in osp.pupil.pupil_rays:
    262         try:
--> 263             ray, op, wvl = trace_base(opt_model, p, fld, wvl)
    264         except TraceError as ray_error:
    265             ray = ray_error.ray

~/ray-optics/src/rayoptics/raytr/trace.py in trace_base(opt_model, pupil, fld, wvl, **kwargs)
    150     length = norm(dir0)
    151     dir0 = dir0/length
--> 152     return rt.trace(opt_model.seq_model, pt0, dir0, wvl, **kwargs)
    153
    154

~/ray-optics/src/rayoptics/raytr/raytrace.py in trace(seq_model, pt0, dir0, wvl, **kwargs)
     82     kwargs['last_surf'] = kwargs.get('last_surf',
     83                                      seq_model.get_num_surfaces()-2)
---> 84     return trace_raw(path, pt0, dir0, wvl, **kwargs)
     85
     86

~/ray-optics/src/rayoptics/raytr/raytrace.py in trace_raw(path, pt0, dir0, wvl, eps, **kwargs)
    161
    162             # intersect ray with profile
--> 163             pp_dst_intrsct, inc_pt = ifc.intersect(pp_pt_before, b4_dir,
    164                                                    eps=eps, z_dir=z_dir_before)
    165             dst_b4 = pp_dst + pp_dst_intrsct

~/ray-optics/src/rayoptics/elem/surface.py in intersect(self, p0, d, eps, z_dir)
    196
    197     def intersect(self, p0, d, eps=1.0e-12, z_dir=1.0):
--> 198         return self.profile.intersect(p0, d, eps, z_dir)
    199
    200     def normal(self, p):

~/ray-optics/src/rayoptics/elem/profiles.py in intersect(self, p0, d, eps, z_dir)
     86         while delta > eps:
     87             p = p1 + s1*d
---> 88             s2 = s1 - self.f(p)/np.dot(d, self.normal(p))
     89             delta = abs(s2 - s1)
     90 #            print("intersect", s1, s2, delta)

~/ray-optics/src/rayoptics/elem/profiles.py in f(self, p)
    591
    592     def f(self, p):
--> 593         return p[2] - self.sag(p[0], p[1])
    594
    595     def profile(self, sd, dir=1, steps=21):

~/ray-optics/src/rayoptics/elem/profiles.py in sag(self, x, y)
    576         try:
    577             # sphere + conic contribution
--> 578             z = self.cv*r2/(1. + sqrt(1. - (self.cc+1.0)*self.cv*self.cv*r2))
    579         except ValueError:
    580             raise TraceMissedSurfaceError

KeyboardInterrupt:

Qbfs polynomial aspheric surface

Hello Mike,

Thanks for the great package!
The aspheric lens, ASL5040-UV from ThorLabs, is currently being treated as a spherical surface as opposed to a Qbfs polynomial surface. Perhaps, if possible, a future enhancement could include support for this type of surface?

Best,
Lauren

Refocus feature in GUI does not work properly

Description

Hi,
After clicking the "Refocus" function the calculated distance is getting accumulated each time when the function is invoked. As result, the focal plane is getting shifted from the correct position. I believe that the bug is in this line:

opt_model['optical_spec']['focus'].focus_shift += focus_shift

Solution

Replacing += with = seems to solve the issue.

Best,
pwarciszewski

Support of 'Return to surface' surface type

Hi,
Thank you for you amazing library. In order to improve the interfacing between ray-optics and CodeV, could it be possible to implement the 'Return to surface' surface type ? In a CodeV .seq, the line is 'RET Sk'.

Thank you,
DrPaprikaa

Wavefront became strange when lens is at a distance of FFL to the STOP

Dear Michael Hayford,

First, thank you for your nice module.

I get an issue with wavefront : when the space before the system is near of it front focal lenght, the wavefront values became strange for a field with an angle of 0. The wavefront is normal when the distance before the system is not his ffl, or when the angle is not 0.
Also, it seems to depend on the system, as I get the error for a Thorlabs lens but I cannot reproduce it with the triplet example of ray-optics documentation.

I put some code below that reproduce the effect :

from rayoptics.environment import *
import numpy as np
from rayoptics.gui import dashboards
import ipywidgets as widgets
from rayoptics import raytr

pupil=22.5
wvl_and_weight=[(510,1)]
fld=[0]

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.radius_mode = True
sm.do_apertures = True
sm.gaps[0].thi = 1e10

osp['wvls'] = WvlSpec(wvl_and_weight)
osp.pupil.value=pupil
osp['fov'] = FieldSpec(osp, key=['object', 'angle'], flds=fld, is_relative=False)

sm.add_surface([0,0],sd=pupil/2)
sm.set_stop()
sm.add_surface([0,0])
sm.add_surface([106.237415,10.6000, 'N-BK7,Schott'],sd=25.4)
sm.add_surface([-92.104793,6.00000, 'N-SF2,Schott'],sd=25.4)
sm.add_surface([-409.394327,0],sd=25.4)
sm.add_surface([0,190.59622227040197])

opm.update_model()

When there is no ffl before the system, we get this :

sm.list_model()

fld, wvl, foc = osp.lookup_fld_wvl_focus(0)
wavefront = analyses.eval_wavefront(opm,num_rays=128,fld=fld, wvl=wvl,foc=0)
c_2=wavefront[:,:,2].copy()
c_2Mask = np.ma.masked_where(np.logical_not(np.isnan(c_2)),c_2)
plt.figure()
plt.title("Wavefront")
plt.imshow(c_2Mask.data,cmap='jet')#,cmap="BrBG_r")
plt.colorbar()
plt.show()
              r            t        medium     mode   zdr      sd
  Obj:     0.000000  1.00000e+10       air             1      0.0000
 Stop:     0.000000      0.00000       air             1      11.250
    2:     0.000000      0.00000       air             1      11.250
    3:   106.237415      10.6000     N-BK7             1      11.250
    4:   -92.104793      6.00000     N-SF2             1      10.909
    5:  -409.394327      0.00000       air             1      10.756
    6:     0.000000      190.596       air             1      10.748
  Img:     0.000000      0.00000                       1   0.0020472

errorWVF1

But if I put ffl before the sytem, I get :

sm.gaps[2].thi=198.59357423827296
opm.update_model()
sm.list_model()

fld, wvl, foc = osp.lookup_fld_wvl_focus(0)
wavefront = analyses.eval_wavefront(opm,num_rays=128,fld=fld, wvl=wvl,foc=0)
c_2=wavefront[:,:,2].copy()
c_2Mask = np.ma.masked_where(np.logical_not(np.isnan(c_2)),c_2)
plt.figure()
plt.title("Wavefront")
plt.imshow(c_2Mask.data,cmap='jet')#,cmap="BrBG_r")
plt.colorbar()
plt.show()
              r            t        medium     mode   zdr      sd
  Obj:     0.000000  1.00000e+10       air             1      0.0000
 Stop:     0.000000      0.00000       air             1      11.250
    2:     0.000000      198.594       air             1      11.250
    3:   106.237415      10.6000     N-BK7             1      11.250
    4:   -92.104793      6.00000     N-SF2             1      10.909
    5:  -409.394327      0.00000       air             1      10.756
    6:     0.000000      190.596       air             1      10.748
  Img:     0.000000      0.00000                       1   0.0020472

errorWVF2

I can manage to avoid it by changing the distance before the system, but if you know what goes wrong, it would be helpful.

Thanks and Best Regards,
quentGit

SequentialModel reset

Dear Mr. Hayford,
I came accross a minor error.

In file seq/sequential.py in function reset() at line 117:

self.__init__()

Throws an error because constructor gets called without the required argument.
I guess it should be

self.__init__(self.opt_model)

I'm a mere hobbyist and have just started trying out your library.
Thanks a lot for sharing it!

Cheers!

Problem importing zmx lens

Hello,

Before all, thanks for this great package!
I'm trying to import zmx file from Edmund web site

https://www.edmundoptics.com/p/25mm-diameter-x-40mm-efl-aspherized-achromatic-lens/10169/


              r            t        medium     mode   zdr      sd
  Obj:     0.000000  1.00000e+10       air             1  4.2787e+08
 Stop:    24.800000      9.00000   S-BSM14             1      12.500
    2:   -37.600000      2.50000   S-TIH53             1      12.500
    3:  -135.000000    0.0800000   517.520             1      12.500
    4:  -102.337111      33.4660       air             1      12.500
  Img:     0.000000      0.00000                       1      1.7157

The material are not well imported:
Materials are:
ELEMENT A: N-SK14
ELEMENT B: N-SF57

I have some errors in log file

INFO:root:Line 4: Command MODE not supported
INFO:root:Line 6: Command PFIL not supported
INFO:root:Line 7: Command LANG not supported
INFO:root:Line 10: Command ENVD not supported
INFO:root:Line 11: Command GFAC not supported
INFO:root:Line 13: Command SDMA not supported
INFO:root:Line 16: Command ROPD not supported
INFO:root:Line 18: Command PICB not supported
INFO:root:Line 25: Command PWAV not supported
INFO:root:Line 26: Command GLRS not supported
INFO:root:Line 27: Command COFN not supported
INFO:root:Line 29: Command FIMP not supported
INFO:root:Line 35: Command COMM not supported
INFO:root:Line 37: Command FIMP not supported
INFO:root:Line 40: Command COAT not supported
INFO:root:Line 47: Command FIMP not supported
INFO:root:Line 56: Command FIMP not supported
INFO:root:Line 66: Command FIMP not supported
INFO:root:Line 82: Command FIMP not supported

Thanks for your support

Spot-diagram data

Hi,

Is there a way to access the spot-diagram data at specified field/wavelength/zoom positions ?
It would be ideal for image quality evaluation purposes : plotting, data analysis...

Thank you,
DrPaprikaa

GUI crashes on bad input

Entering invalid data in text boxes crashes the gui since the text is parsed using eval().

I'm happy to fix this but not sure what direction to take:

  1. Handle all exceptions from eval. I can't think of any that shouldn't be ignored when calling eval() in this context?
  2. Switch to parsing doubles normally

Was the intent being using eval to allow references to python variables?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.