Package: raytracing
RayTracingby the DCC/M Lab grouphttp://www.dccmlab.ca, guided by Prof.Daniel Côté.[Update September 2023]: What are we up to? You will notice the master branch has not changed in some time. However, other branches are actively being worked on, including a GPU-accelerated branch that promises to open up possibilities for Monte Carlo calculations. More extensive tutorials, with practical lab-related calculations, are also being worked on.  Stay tuned.This code aims to provide a simple ray tracing module for calculating various properties of optical paths (object, image, aperture stops, field stops).  It makes use of ABCD matrices and does not consider spherical aberrations but can compute chromatic aberrations for simple cases when the materials are known. Since it uses the ABCD formalism (or Ray matrices, or Gauss matrices) it can perform tracing of rays and gaussian laser beams.It is not a package to do "Rendering in 3D with raytracing".The code has been developed first for teaching purposes and is used in my "Optique" Study Notes (french only), but also for actual use in my research. As of January 21st, 2021, there is an extensive, freely accessible, peer-reviewed tutorial in Journal of Neurophotonics:"Tools and tutorial on practical ray tracing for microscopy"by V. Pineau Noël*, S. Masoumi*, E. Parham*, G. Genest, L. Bégin, M.-A. Vigneault, D. C. Côté,
Neurophotonics, 8(1), 010801 (2021).
*Equal contributions.
Permalink:https://doi.org/10.1117/1.NPh.8.1.010801The published tutorial assumes version 1.3.x.  There are videotutorials(in english or french, with english subtitles when in french) on YouTube. We have made no attempts at making high performance code.Readabilityandsimplicity of usageare the key here. It is a module with a few files, and onlymatplotlibandnumpyas dependent modules.Where do I get started?If you want to use the module, keep reading.If you have a suggestion or a bug report, go toIssues.If you want to read and contribute to the code, go to theWikifor general considerations. We plan to have a roadmap in the near future.Getting startedThe module definesRay,Matrix,MatrixGroupandImagingPathas the main elements for tracing rays.MatrixandMatrixGroupare either one or a sequence of many matrices into whichRaywill propagate.ImagingPathis also a sequence of elements, with an object at the front edge.  Specific subclasses ofMatrixexists:Space,Lens,ThicklLens, andAperture. Finally, a ray fan is a collection of rays, originating from a given point with a range of angles.We have tried to separate the calculation code (i.e. the matrices and subclasses) from the drawing code (figures and graphics). One can use the calculation code without any graphics calls.If you want to perform calculations with coherent laser beams, then you useGaussianBeamandLaserPath. Everything is essentially the same, except that the formalism does not allow for the gaussian beam to be "blocked", hence any calculation of stops with aperture are not available inLaserPath. That part of the code is less developed, but it is nevertheless available.What's new?To get information about what is new, currently the best place is therelease page on GitHub.There is aFrequently Asked Questionspage.The article above is fully compatible with all 1.3.x versions.  As long as the API does not change, versions will be 1.3.x.Installing and upgradingYou needmatplotlib, which is a fairly standard Python module. If you do not have it,  installingAnacondais your best option. Python 3.6 or later is required. There are several ways to install the module:Simplest:pip install raytracingorpip install --upgrade raytracingIf you need to installpip, downloadgetpip.pyand run it withpython getpip.pyIf you download thesourceof the module, then you can type:python setup.py installFrom GitHub, you can get the latest version (including bugs, which are 153% free!) and then typepython setup.py installIf you are completely lost, copying the folderraytracing(the one that includes__init__.py) from the source file into the same directory as your own script will work.Watch the tutorial with subtitleshere.Getting startedThe simplest way to import the package in your own scripts after installing it:fromraytracingimport*This will importRay,GaussianBeam,  and severalMatrixelements such asSpace,Lens,ThickLens,Aperture,DielectricInterface, but alsoMatrixGroup(to group elements together),ImagingPath(to ray trace with an object at the front edge),LaserPath(to trace a gaussian laser beam from the front edge) and a few predefined other such asObjective(to create a very thick lens that mimicks an objective).You create anImagingPathor aLaserPath, which you then populate with optical elements such asSpace,LensorApertureor vendor lenses. You can then adjust the path properties (object height inImagingPathfor instance or inputBeam forLaserPath) and display in matplotlib. You can create a group of elements withMatrixGroupfor instance a telescope, a retrofocus or any group of optical elements you would like to treat as a "group".  The Thorlabs and Edmund optics lenses, for instance, are defined asMatrixGroups.This will show you a list of examples of things you can do (more on that in the Examples section):python-mraytracing-l# List examplespython-mraytracing-eall# Run all of thempython-mraytracing-e1,2,4,6# Only run 1,2,4 and 6python-mraytracing-t# Run all the tests.  Some performance tests can take up to a minute, but they should all pass.or request help with:python -m raytracing -hIn your code, you would do this:fromraytracingimport*path=ImagingPath()path.append(Space(d=50))path.append(Lens(f=50,diameter=25))path.append(Space(d=120))path.append(Lens(f=70))path.append(Space(d=100))path.display()You can also calldisplay()on an element to see the cardinal points, principal planes, BFL (back focal length, or the distance between the last interface and the focal point after the lens) and FFL (front focal length, or the distance between the focal point before the lens and the first interface). You can do it with any singleMatrixelement but also withMatrixGroup.fromraytracingimport*thorlabs.AC254_050_A().display()eo.PN_33_921().display()Finally, an addition as of 1.2.0 is the ability to obtain the intensity profile of a given source from the object plane at the exit plane of anOpticalPath. This is in fact really simple: by tracing a large number of rays, with the number of rays at y and θ being proportionnal to the intensity, one can obtain the intensity profile by plotting the histogram of rays reaching a given height at the image plane.Raysare small classes that return aRaythat satisfies the condition of the class.  Currently, there isUniformRays,RandomUniformRaysLambertianRaysandRandomLambertianRays(a Lambertian distribution follows a cosθ distribution, it is a common diffuse surface source).  They appear like iterators and can easily be used like this example script:fromraytracingimport*fromnumpyimport*importmatplotlib.pyplotasplt# Kohler illumination with these variablesfobj=5dObj=5f2=200d2=50f3=100d3=50# We build the path (i.e. not an Imaging path)path=OpticalPath()path.append(Space(d=f3))path.append(Lens(f=f3,diameter=d3))path.append(Space(d=f3))path.append(Space(d=f2))path.append(Lens(f=f2,diameter=d2))path.append(Space(d=f2))path.append(Space(d=fobj))path.append(Lens(f=fobj,diameter=dObj))path.append(Space(d=fobj))# Obtaining the intensity profilenRays=1000000# Increase for better resolutioninputRays=RandomLambertianRays(yMax=2.5,maxCount=nRays)inputRays.display("Input profile")outputRays=path.traceManyThrough(inputRays,progress=True)# On macOS and Linux, you can do parallel computations# outputRays = path.traceManyThroughInParallel(inputRays, progress=True, processes=8)outputRays.display("Output profile")and you will get the following ray histograms:Finally, it is possible to obtain the chromatic aberrations for compound lenses (achromatic doublets from Thorlabs and Edmund optics, and singlet lens because the materials are known). The following command will give you the focal shift as a function of wavelength (as a graph or values):fromraytracingimport*thorlabs.AC254_100_A().showChromaticAberrations()wavelengths,shifts=thorlabs.AC254_100_A().focalShifts()DocumentationAll the documentation isavailable online.The class hierarchy for optical elements (with parameters and defaults) is:You may obtain help by:Reading the documentation for the code (API reference).Core:Ray: a ray for geometrical optics with a height and angle $y$ and $\theta$.Rays: ray distributions to ray trace an object through the optical system.UniformRays,RandomUniformRays,LambertianRaysandRandomLambertianRaysare currently available.  See example above.GaussianBeam: a gaussian laser beam with complex radius of curvature $q$.Matrix: any 2x2 matrix.MatrixGroup: treats a group of matrix as a unit (draws it as a unit too)ImagingPath: AMatrixGroupwith an object at the front for geometrical opticsLaserPath: AMatrixGroupwith a laser beam input at the front or a Resonator.Optical elements:Aperture,Space,Lens,DielectricInterface,DielectricSlab,ThickLensSpecialty lenses: Defines a general achromat and objective lensThorlabs lenses: Achromat doublet lenses from Thorlabs.Edmund Optics lenses: Achromat doublet lenses from Edmund OpticsOlympus objectives: A few objectives from Olympus.Glasses: A few glasses used by Thorlabs to make achromatic doublets. They all have a single functionn(wavelength)that returns the index at that wavelength.  All data obtained fromhttp://refractiveindex.info.Zemax ZMX file reader: to read text-based Zemax files of lenses.typing (interactively):help(Matrix),help(MatrixGroup)help(Ray),help(ImagingPath)to get the API,look at the examples withpython -m raytracingsimply look at the code.python>>>help(Matrix)HelponclassMatrixinmoduleraytracing.abcd:classMatrix(builtins.object)|Matrix(A,B,C,D,physicalLength=0,apertureDiameter=inf,label='')||Amatrixandanopticalelementthatcantransformarayoranother|matrix.||Thegeneralproperties(A,B,C,D)aredefinedhere.Theoperator"*"is|overloadedtoallowsimplestatementssuchas:||ray2=M1*ray|or|M3=M2*M1||Thephysicallengthisincludedinthematrixtoallowsimplemanagementof|theraytracing.IFtwomatricesaremultiplied,theresultingmatrice|willhaveaphysicallengththatisthesumofbothmatrices.||Inadditionfiniteaperturesareconsidered:iftheapertureDiameter|isnotinfinite(default),thentheobjectisassumedtolimitthe|rayheighttoplusorminusapertureDiameter/2fromthefrontedgetotheback|edgeoftheelement.||Methodsdefinedhere:||__init__(self,A,B,C,D,physicalLength=0,apertureDiameter=inf,label='')|Initializeself.Seehelp(type(self))foraccuratesignature.||__mul__(self,rightSide)|Operatoroverloadingallowingeasytoreadmatrixmultiplication||Forinstance,withM1=Matrix()andM2=Matrix(),onecanwrite|M3=M1*M2.Withr=Ray(),onecanapplytheM1transformtoaray|withr=M1*r||__str__(self)|Stringdescriptionthatallowstheuseofprint(Matrix())||backwardConjugate(self)|Withanimageatthebackedgeoftheelement,|whereistheobject?Distancebeforetheelementby|whicharaymusttraveltoreachtheconjugateplaneat|thebackoftheelement.Apositivedistancemeansthe|objectis"distance"infrontoftheelement(ortothe|left,orbefore).||M2=M1*Space(distance)|# M2.isImaging == TrueExamplesYou can list several examplespython -m raytracing -l:Allexamplecodeonyourmachineisfoundat:/somedirectory/on/your/machine1.ex01.pyAsinglelensf=50mm,infinitediameter2.ex02.pyTwolenses,infinitediameters3.ex03.pyFinite-diameterlens4.ex04.pyAperturebehindlensactingasFieldStop5.ex05.pySimplemicroscopesystem6.ex06.pyKohlerillumination7.ex07.pyFocussingthroughadielectricslab8.ex08.pyVirtualimageat-fwithobjectatf/29.ex09.pyInfinitetelecentric4ftelescope10.ex10.pyRetrofocus$f_e$={0:.1f}cm,andBFL={1:.1f}11.ex11.pyThickdiverginglenscomputedfromtheLensmakerequation12.ex12.pyThickdiverginglensbuiltfromindividualelements13.ex13.pyObtaintheforwardandbackwardconjugates14.ex14.pyGenericobjectives15.ex15.pyModelOlympusobjectiveLUMPlanFL40X16.ex16.pyCommercialdoubletsfromThorlabsandEdmund17.ex17.pyAnopticalsystemwithvendorlenses18.ex18.pyLaserbeamandvendorlenses19.ex19.pyCavityroundtripandcalculatedlasermodes
....andmorecompleteexamplesat/somedirectory/on/your/machineYou can run them all withpython -m raytracing -e all(see them all below) to get a flavour of what is possible (note: in the US, it will give you a flavor of what is possible instead). Notice the command will tell you where the directory with all the tests is on your machine.You will find more complete examplesin thatexamplesdirectory, distributed with the module.  For instance,illuminator.pyto see a Kohler illuminator, andinvariant.pyto see an example of the role of lens diameters to determine the field of view.Known limitationsThere are no known bugs in the actual calculations, but there are bugs or limitations in the display:It is not easy to put several labels on a graph without any overlap. We are still working on it.It is also not easy to figure out what "the right size" should be for an arrow head, the font, the position of a label, the size of the "ticks" on the aperture.Labelling focal points with appropriate secondary labels should be possible, maybe a superscript?The y-scale is not always set appropriately when the elements have infinite diameters: the rays will go beyond the element drawn on the figure.LicenceThis code is provided under theMIT License.