用于验证光学系统设计(透镜位置和尺寸、焦距)的简单光线跟踪库。

raytracing的Python项目详细描述


光线跟踪

作者[丹尼尔•C_té](邮件地址:dccote@cervo.ulaval.ca?subject=raytracing python模块)

此代码旨在提供一个简单的光线跟踪模块,用于计算光路的各种特性(对象、图像、光圈停止、场停止)。它使用abcd矩阵,不考虑像差(球面或彩色)。因为它使用abcd形式(或光线矩阵,或高斯矩阵),所以它可以跟踪光线,也可以跟踪高斯激光束。

它不是一个要执行"使用光线跟踪进行三维渲染"的包。

该代码首先是为教学目的而开发的,并在我的"optique"学习笔记(仅限法语),也可在我的研究中实际使用。我没有尝试过编写高性能代码。可读性使用的简单性是这里的关键。它是一个只有几个文件的模块,并且只将matplotlib作为依赖模块。

该模块将raymatrixmatrixgroupimagingpath定义为跟踪光线的主要元素。矩阵矩阵组是一个矩阵或多个矩阵序列,其中射线将传播到其中。imagingpath也是一个元素序列,在前端有一个对象。矩阵的特定子类存在:空间镜头厚度光圈。最后,光线扇是光线的集合,从具有一定角度范围的给定点发出。

如果要使用相干激光束进行计算,则使用gaussianbeamlaserpath。除了形式主义不允许高斯光束被"阻塞"外,所有东西本质上都是一样的,因此在laserpath

安装和升级

您需要matplotlib,这是一个相当标准的python模块。如果您没有,安装蟒蛇是您的最佳选择。您应该选择Python3.7或更高版本。有几种安装模块的方法:

  1. 最简单的:pip install raytracingpip install--升级raytracing
  2. 如果下载模块的源代码,则可以键入:python setup.py install
  3. 从github中,您可以获得最新版本(包括bug),然后键入python setup.py install
  4. 如果完全丢失,则可以将源文件中的文件夹光线跟踪(包含\uu init_uuuuuu.py)复制到与您自己的脚本相同的目录中。

开始

安装软件包后,在您自己的脚本中导入该软件包的最简单方法:

fromraytracingimport*

这将导入raygaussianbeam,以及几个matrix元素,例如spacelensthicklensaperturemedielectricinterface,而且还导入matrix group(将元素组合在一起)。imagingpath(用物体在前边缘进行光线跟踪)、laserpath(用高斯激光束从前边缘跟踪)和一些预定义的其他,如objective(创建一个非常厚的镜头来模拟一个物体)。

创建imagingpathlaserpath,然后填充光学元件,例如spacelensaperture或供应商镜头。然后可以调整路径属性(imagingpath中的对象高度或实例或inputbeam,用于laserpath)并显示在matplotlib中。您可以使用matrixgroup创建一组元素,例如望远镜、后聚焦或任何一组您希望视为"组"的光学元素。例如,Thorlabs和Edmund光学镜头被定义为矩阵组

这将向您展示一些您可以做的事情的示例:

python -m raytracing

或请求帮助:

python -m raytracing -h

在您的代码中(例如源代码中包含的test.pydemo.py文件),您可以这样做:

fromraytracingimport*path=ImagingPath()path.append(Space(d=100))path.append(Lens(f=50,diameter=25))path.append(Space(d=120))path.append(Lens(f=70))path.append(Space(d=100))path.display()

还可以对元素调用display()来查看基点、主平面、bfl和ffl。您可以使用任何单个矩阵元素,也可以使用矩阵组

from raytracing import *

thorlabs.AC254_050_A().display()
eo.PN_33_921().display()

文档

光学元件(带参数和默认值)的类层次结构为:

image-2019051123041520

文档最多是稀疏的。您可以通过以下方式获得帮助:

  1. 从代码中读取自动生成的文档(不是很好看,但至少是一些文档):
      LI>核心:
      1. 射线:用于几何光学的射线,高度和角度分别为$y$和$\theta$。
      2. gaussianbeam:具有复杂曲率半径$q$的高斯激光束。
      3. 矩阵:任何2x2矩阵。
      4. matrixgroup:将一组矩阵视为一个单元(也将其绘制为一个单元)
      5. imagingpath:amatrixgroup前面有一个用于几何光学的对象
      6. laserpath:amatrixgroup在前端或谐振器处输入激光束。
    1. 光学元件:光圈空间透镜电介质接口电介质板厚透镜
    2. 专用镜头:定义普通消色差和物镜
    3. Thorlabs镜头:来自Thorlabs的消色差双镜头。
    4. 埃德蒙光学镜头:埃德蒙光学的消色差双镜头
    5. 奥林巴斯的目标:奥林巴斯的一些目标。
    6. 眼镜:Thorlabs用来制作消色差双色眼镜的一些眼镜。它们都有一个函数n(波长),返回该波长的索引。从http://reflectiveindex.info>获取的所有数据。
  2. 输入(交互):帮助(矩阵)帮助(矩阵组)帮助(射线)帮助(图像路径)获取API,
  3. 查看使用python-m raytracing的示例
  4. 只需查看代码即可。
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 == True

示例

示例目录中,可以运行demo.py查看各种系统,illuminator.py查看科勒照明器,以及invariant.py查看镜头直径的作用示例以确定视野。但是,您也可以使用python-m raytracing直接运行模块,它将运行以下代码(\u main\uuu.py),让您了解可能的情况:

from.imagingpathimport*from.laserpathimport*from.specialtylensesimport*from.axiconimport*importraytracing.thorlabsasthorlabsimportraytracing.eoaseoimportraytracing.olympusasolympusimportargparseap=argparse.ArgumentParser(prog='python -m raytracing')ap.add_argument("-e","--examples",required=False,default='all',help="Specific example numbers, separated by a comma")args=vars(ap.parse_args())examples=args['examples']ifexamples=='all':examples=range(1,30)else:examples=[int(y)foryinexamples.split(',')]if1inexamples:path=ImagingPath()path.label="Demo #1: lens f = 5cm, infinite diameter"path.append(Space(d=10))path.append(Lens(f=5))path.append(Space(d=10))path.display(comments="""Demo #1: lens with f=5 cm, infinite diameter    An object at z=0 (front edge) is used. It is shown in blue. The image (or any intermediate images) are shown in red.\n\    This will use the default objectHeight and fanAngle but they can be changed with:    path.objectHeight = 1.0    path.fanAngle = 0.5    path.fanNumber = 5    path.rayNumber = 3    Code:    path = ImagingPath()    path.label = "Demo #1: lens f = 5cm, infinite diameter"    path.append(Space(d=10))    path.append(Lens(f=5))    path.append(Space(d=10))    path.display()    """)if2inexamples:path=ImagingPath()path.label="Demo #2: Two lenses, infinite diameters"path.append(Space(d=10))path.append(Lens(f=5))path.append(Space(d=20))path.append(Lens(f=5))path.append(Space(d=10))path.display(comments="""Demo #2: Two lenses, infinite diameters    An object at z=0 (front edge) is used with default properties (see Demo #1).    Code:    path = ImagingPath()    path.label = "Demo #2: Two lenses, infinite diameters"    path.append(Space(d=10))    path.append(Lens(f=5))    path.append(Space(d=20))    path.append(Lens(f=5))    path.append(Space(d=10))    path.display()    """)# or#path.save("Figure 2.pdf")if3inexamples:path=ImagingPath()path.label="Demo #3: Finite lens"path.append(Space(d=10))path.append(Lens(f=5,diameter=2.5))path.append(Space(d=3))path.append(Space(d=17))path.display(comments="""Demo #3: A finite lens    An object at z=0 (front edge) is used with default properties (see Demo #1). Notice the aperture stop (AS)    identified at the lens which blocks the cone of light. There is no field stop to restrict the field of view,    which is why we must use the default object and cannot restrict the field of view. Notice how the default    rays are blocked.    path = ImagingPath()    path.objectHeight = 1.0    # object height (full).    path.objectPosition = 0.0  # always at z=0 for now.    path.fanAngle = 0.5        # full fan angle for rays    path.fanNumber = 9         # number of rays in fan    path.rayNumber = 3         # number of points on object    path.label = "Demo #3: Finite lens"    path.append(Space(d=10))    path.append(Lens(f=5, diameter=2.5))    path.append(Space(d=3))    path.append(Space(d=17))    path.display()    """)if4inexamples:path=ImagingPath()path.label="Demo #4: Aperture behind lens"path.append(Space(d=10))path.append(Lens(f=5,diameter=3))path.append(Space(d=3))path.append(Aperture(diameter=3))path.append(Space(d=17))path.display(comments="""Demo #4: Aperture behind lens    Notice the aperture stop (AS) identified after the lens, not at the lens. Again, since there is no field stop,    we cannot restrict the object to the field of view because it is infinite.    Code:    path = ImagingPath()    path.label = "Demo #4: Aperture behind lens"    path.append(Space(d=10))    path.append(Lens(f=5, diameter=3))    path.append(Space(d=3))    path.append(Aperture(diameter=3))    path.append(Space(d=17))    path.display()    """)if5inexamples:path=ImagingPath()path.label="Demo #5: Simple microscope system"path.fanAngle=0.1# full fan angle for rayspath.fanNumber=5# number of rays in fanpath.rayNumber=5# number of points on objectpath.append(Space(d=4))path.append(Lens(f=4,diameter=0.8,label='Obj'))path.append(Space(d=4+18))path.append(Lens(f=18,diameter=5.0,label='Tube Lens'))path.append(Space(d=18))path.display(limitObjectToFieldOfView=True,comments="""# Demo #5: Simple microscope system    The aperture stop (AS) is at the entrance of the objective lens, and the tube lens, in this particular microscope, is    the field stop (FS) and limits the field of view. Because the field stop exists, we can use limitObjectToFieldOfView=True    when displaying, which will set the objectHeight to the field of view, but will still trace all the rays using our parameters.    path = ImagingPath()    path.label = "Demo #5: Simple microscope system"    path.fanAngle = 0.1        # full fan angle for rays    path.fanNumber = 5         # number of rays in fan    path.rayNumber = 5         # number of points on object    path.append(Space(d=4))    path.append(Lens(f=4, diameter=0.8, label='Obj'))    path.append(Space(d=4 + 18))    path.append(Lens(f=18, diameter=5.0, label='Tube Lens'))    path.append(Space(d=18))    path.display()    """)if6inexamples:path=ImagingPath()path.label="Demo #6: Simple microscope system, only principal rays"path.append(Space(d=4))path.append(Lens(f=4,diameter=0.8,label='Obj'))path.append(Space(d=4+18))path.append(Lens(f=18,diameter=5.0,label='Tube Lens'))path.append(Space(d=18))path.display(limitObjectToFieldOfView=True,onlyChiefAndMarginalRays=True,comments="""# Demo #6: Simple microscope system, only principal rays    The aperture stop (AS) is at the entrance of the objective lens, and the tube lens, in this particular microscope, is    the field stop (FS) and limits the field of view. Because the field stop exists, we can use limitObjectToFieldOfView=True    when displaying, which will set the objectHeight to the field of view. We can also require that only the principal rays are drawn: chief ray    marginal ray (or axial ray).    path = ImagingPath()    path.label = "Demo #6: Simple microscope system, only principal rays"    path.append(Space(d=4))    path.append(Lens(f=4, diameter=0.8, label='Obj'))    path.append(Space(d=4 + 18))    path.append(Lens(f=18, diameter=5.0, label='Tube Lens'))    path.append(Space(d=18))    path.display()    """)if7inexamples:path=ImagingPath()path.label="Demo #7: Focussing through a dielectric slab"path.append(Space(d=10))path.append(Lens(f=5))path.append(Space(d=3))path.append(DielectricSlab(n=1.5,thickness=4))path.append(Space(d=10))path.display(comments=path.label+"""\n    path = ImagingPath()    path.label = "Demo #7: Focussing through a dielectric slab"    path.append(Space(d=10))    path.append(Lens(f=5))    path.append(Space(d=3))    path.append(DielectricSlab(n=1.5, thickness=4))    path.append(Space(d=10))""")if8inexamples:# Demo #8: Virtual imagepath=ImagingPath()path.label="Demo #8: Virtual image at -2f with object at f/2"path.append(Space(d=2.5))path.append(Lens(f=5))path.append(Space(d=10))path.display(comments=path.label+"""\n    path = ImagingPath()    path.label = "Demo #8: Virtual image at -2f with object at f/2"    path.append(Space(d=2.5))    path.append(Lens(f=5))    path.append(Space(d=10))    path.display()""")if9inexamples:# Demo #9: Infinite telecentric 4f telescopepath=ImagingPath()path.label="Demo #9: Infinite telecentric 4f telescope"path.append(Space(d=5))path.append(Lens(f=5))path.append(Space(d=10))path.append(Lens(f=5))path.append(Space(d=5))path.display(comments=path.label+"""\n    path = ImagingPath()    path.label = "Demo #9: Infinite telecentric 4f telescope"    path.append(Space(d=5))    path.append(Lens(f=5))    path.append(Space(d=10))    path.append(Lens(f=5))    path.append(Space(d=5))    """)if10inexamples:path=ImagingPath()path.fanAngle=0.05path.append(Space(d=20))path.append(Lens(f=-10,label='Div'))path.append(Space(d=7))path.append(Lens(f=10,label='Foc'))path.append(Space(d=40))(focal,focal)=path.effectiveFocalLengths()bfl=path.backFocalLength()path.label="Demo #10: Retrofocus $f_e$={0:.1f} cm, and BFL={1:.1f}".format(focal,bfl)path.display(comments=path.label+"""\n    A retrofocus has a back focal length longer than the effective focal length. It comes from a diverging lens followed by a converging    lens. We can always obtain the effective focal lengths and the back focal length of a system.    path = ImagingPath()    path.fanAngle = 0.05    path.append(Space(d=20))    path.append(Lens(f=-10, label='Div'))    path.append(Space(d=7))    path.append(Lens(f=10, label='Foc'))    path.append(Space(d=40))    (focal,focal) = path.effectiveFocalLengths()    bfl = path.backFocalLength()    path.label = "Demo #10: Retrofocus $f_e$={0:.1f} cm, and BFL={1:.1f}".format(focal, bfl)    path.display()    """)if11inexamples:# Demo #11: Thick diverging lenspath=ImagingPath()path.label="Demo #11: Thick diverging lens"path.objectHeight=20path.append(Space(d=50))path.append(ThickLens(R1=-20,R2=20,n=1.55,thickness=10,diameter=25,label='Lens'))path.append(Space(d=50))path.display(onlyChiefAndMarginalRays=True,comments=path.label+"""\n    path = ImagingPath()    path.label = "Demo #11: Thick diverging lens"    path.objectHeight = 20    path.append(Space(d=50))    path.append(ThickLens(R1=-20, R2=20, n=1.55, thickness=10, diameter=25, label='Lens'))    path.append(Space(d=50))    path.display()""")if12inexamples:# Demo #12: Thick diverging lens built from individual elementspath=ImagingPath()path.label="Demo #12: Thick diverging lens built from individual elements"path.objectHeight=20path.append(Space(d=50))path.append(DielectricInterface(R=-20,n1=1.0,n2=1.55,diameter=25,label='Front'))path.append(Space(d=10,diameter=25,label='Lens'))path.append(DielectricInterface(R=20,n1=1.55,n2=1.0,diameter=25,label='Back'))path.append(Space(d=50))path.display(onlyChiefAndMarginalRays=True,comments=path.label+"""\n    path = ImagingPath()    path.label = "Demo #12: Thick diverging lens built from individual elements"    path.objectHeight = 20    path.append(Space(d=50))    path.append(DielectricInterface(R=-20, n1=1.0, n2=1.55, diameter=25, label='Front'))    path.append(Space(d=10, diameter=25, label='Lens'))    path.append(DielectricInterface(R=20, n1=1.55, n2=1.0, diameter=25, label='Back'))    path.append(Space(d=50))    path.display()""")if13inexamples:# Demo #13, forward and backward conjugates# We can obtain the position of the image for any matrix# by using forwardConjugate(): it calculates the distance# after the element where the image is, assuming an object# at the front surface.M1=Space(d=10)M2=Lens(f=5)M3=M2*M1print(M3.forwardConjugate())print(M3.backwardConjugate())if14inexamples:# Demo #14: Generic objectivesobj=Objective(f=10,NA=0.8,focusToFocusLength=60,backAperture=18,workingDistance=2,label="Objective")print("Focal distances: ",obj.focalDistances())print("Position of PP1 and PP2: ",obj.principalPlanePositions(z=0))print("Focal spots positions: ",obj.focusPositions(z=0))print("Distance between entrance and exit planes: ",obj.L)path=ImagingPath()path.fanAngle=0.0path.fanNumber=1path.rayNumber=15path.objectHeight=10.0path.label="Demo #14 Path with generic objective"path.append(Space(180))path.append(obj)path.append(Space(10))path.display(comments=path.label+"""    path = ImagingPath()    path.fanAngle = 0.0    path.fanNumber = 1    path.rayNumber = 15    path.objectHeight = 10.0    path.label = "Path with generic objective"    path.append(Space(180))    path.append(obj)    path.append(Space(10))    path.display()""")if15inexamples:# Demo #15: Olympus objective LUMPlanFL40Xpath=ImagingPath()path.fanAngle=0.0path.fanNumber=1path.rayNumber=15path.objectHeight=10.0path.label="Demo #15 Path with LUMPlanFL40X"path.append(Space(180))path.append(olympus.LUMPlanFL40X())path.display(comments=path.label+"""    path = ImagingPath()    path.fanAngle = 0.0    path.fanNumber = 1    path.rayNumber = 15    path.objectHeight = 10.0    path.label = "Path with LUMPlanFL40X"    path.append(Space(180))    path.append(olympus.LUMPlanFL40X())    path.append(Space(10))    path.display()""")if16inexamples:# Demo #16: Vendor lensesthorlabs.AC254_050_A().display()eo.PN_33_921().display()if17inexamples:# Demo #17: Vendor lensespath=ImagingPath()path.label="Demo #17: Vendor Lenses"path.append(Space(d=50))path.append(thorlabs.AC254_050_A())path.append(Space(d=50))path.append(thorlabs.AC254_050_A())path.append(Space(d=150))path.append(eo.PN_33_921())path.append(Space(d=50))path.append(eo.PN_88_593())path.append(Space(180))path.append(olympus.LUMPlanFL40X())path.append(Space(10))path.display(comments=path.label+"""\n    path = ImagingPath()    path.label = "Demo #17: Vendor Lenses"    path.append(Space(d=50))    path.append(thorlabs.AC254_050_A())    path.append(Space(d=50))    path.append(thorlabs.AC254_050_A())    path.append(Space(d=150))    path.append(eo.PN_33_921())    path.append(Space(d=50))    path.append(eo.PN_88_593())    path.append(Space(180))    path.append(olympus.LUMPlanFL40X())    path.append(Space(10))    path.display()""")if18inexamples:# Demo #18: Laser beam and vendor lensespath=LaserPath()path.label="Demo #18: Laser beam and vendor lenses"path.append(Space(d=50))path.append(thorlabs.AC254_050_A())path.append(Space(d=50))path.append(thorlabs.AC254_050_A())path.append(Space(d=150))path.append(eo.PN_33_921())path.append(Space(d=50))path.append(eo.PN_88_593())path.append(Space(d=180))path.append(olympus.LUMPlanFL40X())path.append(Space(d=10))path.display(inputBeam=GaussianBeam(w=0.001),comments="""    path = LaserPath()    path.label = "Demo #18: Laser beam and vendor lenses"    path.append(Space(d=50))    path.append(thorlabs.AC254_050_A())    path.append(Space(d=50))    path.append(thorlabs.AC254_050_A())    path.append(Space(d=150))    path.append(eo.PN_33_921())    path.append(Space(d=50))    path.append(eo.PN_88_593())    path.append(Space(d=180))    path.append(olympus.LUMPlanFL40X())    path.append(Space(d=10))    path.display()""")if19inexamples:cavity=LaserPath(label="Laser cavity: round trip\nCalculated laser modes")cavity.isResonator=Truecavity.append(Space(d=160))cavity.append(DielectricSlab(thickness=100,n=1.8))cavity.append(Space(d=160))cavity.append(CurvedMirror(R=400))cavity.append(Space(d=160))cavity.append(DielectricSlab(thickness=100,n=1.8))cavity.append(Space(d=160))# Calculate all self-replicating modes (i.e. eigenmodes)(q1,q2)=cavity.eigenModes()print(q1,q2)# Obtain all physical (i.e. finite) self-replicating modesqs=cavity.laserModes()forqinqs:print(q)# Showcavity.display()

Figure1microscopeillumination

已知限制

实际计算中没有已知的错误,但显示中有错误或限制:

  1. 在一个图上放置几个没有重叠的标签是不容易的。我还在努力呢。
  2. 也不容易弄清楚箭头的"正确大小"、字体、标签的位置、光圈上"刻度"的大小。
  3. 用适当的二级标签标记焦点应该是可能的,也许是上标?
  4. 当元素具有无限大的直径时,y比例并不总是适当设置的:光线将超出在图形上绘制的元素。

许可证

此代码在麻省理工学院许可证下提供。

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java消除多个构造函数中的冗余   java如何准备和提供测试数据   java如何处理Selenium Chromedriver选择证书弹出确认?   java服务器在命名时中断。重新绑定   java如何从包含许多元素的对象中提取对象的单个元素   主方法上的java执行   用于输出棋盘的按钮的java GridLayout   java如何结合Web/移动用户身份验证   要打开以运行(windows+R)和执行命令的Java代码   java我在忘记密码的电子邮件发送过程中遇到以下错误err=javax。邮政AuthenticationFailedException这是我的代码   列出在使用泛型集合的用户定义类中实现的JAVA 8。排序()不起作用   java Apache POI写入时间大于24小时   java Hibernate通过另一个实体映射   使用Java在现有json文件中追加json对象   Spring批处理上的java停止处理   java为特定的mybatisspring映射器设置不同的执行器类型   java我想阅读html的内容,我需要用所需的文本对其进行更改