以下文章来源于微信公众号:硅光口袋 ,作者李子正
Lumerical软件具有较强的扩展性,可以仿真波长量级光电器件中的各种现象。软件官网提供了各方向的基础案例,利用自带的脚本语言和数据传递流,将一部分功能打包以供调用。我们在进行自己领域内的仿真时,可以借鉴案例思路,使用脚本控制整个仿真过程。这方面的教程基本没有,导致我自学软件的时候浪费了大量时间,这期借助一个简单的例子:弯曲波导插入损耗的扫描,总结FDTD仿真中数据传递的方式,以及脚本控制该过程的方法。
首先,用如下一张动图来表达软件中指令和数据的关系。这里我用编程中常见的语言来类比(如云朵中所示),黑色箭头代表指令的传递方向,红色箭头代表结果数据的返回方向。“Script File Editor”是个编译器,其语法类似C和matlab的混合体,也支持python,用它可以控制仿真的整体运行,这就像主函数调用各个自定义函数并对变量进行操作的过程,结果各函数会将结果数据返回。“Optimizations and Sweeps'是用来实现参数化扫描和优化的模块,相当于一个自定义函数,可以被主函数调用。剩下的部分都在项目树中,“model”代表整个仿真模型,是极大的父类,包含了仿真区、结构组、分析组、光源等子类,而波导、微环、监视器等则是子类的子类,子子类。这些子类中包含了各种可调参数,包括位置、尺寸、材料等,以及用于记录有限时域差分结果的数组变量,包括电场、磁场、透射谱等。
该流程图以FDTD Solutions为基准,可以推广至其他几个孪生软件。接下来,用一个很简单的测弯曲损耗的例子说明该过程,模型就是一个槽波导,如下图。我们要实现的是一键扫描在不同弯曲半径条件下,波导的插入损耗,并将相应的结果图画出来。
接下来按照前面的流程图,描述具体设置。在父类model中,在设置页面设置变量bending_radius记录波导的弯曲半径,该参数可以被传递到任何子类内,只需在设置页面用脚本设置即可,具体如图中所示。与使用其他仿真软件类似,所有的设置尽量参数化(也就是尽量用自变量弯曲半径来表达其他变量),这样后续想要自变量时,其他内容都会随之改变,可以省去很多麻烦。
槽波导结构是用图中的90 degree bend结构组定义的,其下嵌套三个子结构组,分别代表中间弯曲波导、左侧直波导和右侧直波导三部分。变量bending_radius由父类定义,记录波导的弯曲半径。在设置页面脚本内定义波导的宽度为0.6um,槽宽度为0.3um,通过设置mesh order实现覆盖开槽。
类似地,在存放监视器的分析组IL_cal中,父类中定义过的变量bending radius可以直接调用,在设置页面定义监视器的参数。同时,在分析页面调用监视器FDTD计算得到的结果,给出计算插入损耗的定义,设置分析组返回插入损耗和波长两个结果。这两个返回值可以选择返回到父类model中,统一管理方便调用,这个例子很简单只有一个分析组,所以不返回。虽然在流程图里没有体现,但分析页面的作用也相当于自定义函数,对于监视器有限时域差分得到的结果,包括电场、磁场、时域频域谱等,可以进行取相位,计算时延色散,以及很多对于计算结果的初步处理。
这样项目树部分就设置好了,其他不发生改变的部分例如衬底、包覆层、光源波长范围、监视器监视内容等,可以将其看成常量,直接设置即可。这里为了简便我没有特别设置网格,可以将网格的参数定义在父类model里随其他参数变化,也可以直接手动定义,是具体情况而定。软件里有三种组,布局组只有定义位置坐标的作用,结构组可以实现绕xyz三个轴的任意角度旋转。结构组和分析组涉及到参数传递,可以进行父类到子类的继承,可以选择将这两类的参数定义在父类model中进行统筹管理。只有分析组具有自定义函数的功能,通过在上面提到的在分析页面中写脚本,可以实现各种功能。
相信有些朋友也有这样的感受:在草稿纸上计算大量点坐标,构建了一个非常复杂的模型,满怀期待地点击运行之后,经过长达十几小时的等待,发现结果不尽人意,这时看着自己的模型,发现有些结构上的坐标已经忘记当时是怎么算的,想起来之后开始修改,发现牵一发而动全身,又得把所有点的坐标重新算一遍。所以,在整个设置过程中,能参数化的地方尽量参数化,包括comsol等类似的软件,这会使整个工程自动化,后续不管是优化扫描还是导出到python、matlab处理,都方便很多,修改参数的时候也非常省心。
完成了项目树部分的设置,接下来进行器件设计中较常见的操作——参数扫描。软件中将扫描、优化、蒙特卡洛仿真和S参数提取放在了一个集合内,较常用的还是扫描。扫描可以多级嵌套,也可以作为子过程嵌套在优化中。现在有比较成熟的python的接口,做优化或者基于优化进行逆设计都可以实现。这里只进行对自变量弯曲半径的扫描,直接用脚本编辑器完成,如下图所示。
当然手动设置也是可行的,扫描和优化存在嵌套时可能手动设置还更省心一点。如果先不运行扫描,只执行runsweep()函数之前的代码,就在Optim-ization and Sweeps页面得到扫描sweep_IL。可以看到,参数和结果均符合脚本编辑器中的设置。
执行整个脚本,指令下达到Optimization and Sweeps,该函数循环十次,将父类中的变量bending_radius从小到大设置为从1um到20um的长度值,每次循环执行一次FDTD仿真,计算当前弯曲半径下的结果,并将每次的两个结果返回值记录在数组IL和lamda中。整个流程遵循本文在前面的动图,实现对于不同弯曲半径下波导插入损耗的计算,结尾处结果图会直接弹出。这里再来看一下点击运行脚本后,仿真区的变化过程。
除了FDTD以外,对于MODE,DEVICE,INTERCONNECT,本文提到的规律也有效,几个软件之间模型结构是可以直接ctrl+c,ctrl+v的,处理数据的方式,写脚本的语法,也基本一致。在仿真更加复杂的目标时,变量和监视器繁多,搞清楚数据的传递方式会显得更加重要。