ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Lumerical FDTD中Y分支器件的粒子群优化实现与避坑记录

Lumerical FDTD中Y分支器件的粒子群优化实现与避坑记录 做了这么多期Lumerical官方案例第十八期正好卡在Y分支粒子群算法优化这个点上。FDTD时域有限差分法在微纳光子器件仿真里的分量不用多说Y分支又是集成光路里几乎绕不开的分束结构粒子群算法则是目前做这类多参数优化最顺手的一类启发式方法。三件事叠在一起就是从建模、单次仿真到自动迭代找最优几何的完整链路。这篇文章是复现这期案例的完整记录也把仿真过程中踩过的坑一并写出来。适合已经在Lumerical FDTD里跑通简单波导、但还没怎么碰过自动优化的同学。1. Y分支器件与优化前的准备工作1.1 Y分支在集成光路里的地位Y分支在光路上干的事很简单一路光进来两路光出去理想情况就是各分50%。但越简单的结构在实际设计里越藏细节。输入直波导到分叉点模式场要从单模逐渐扩开再平缓过渡到两支弯曲臂整个过程一旦几何变化太剧烈就会激发出高阶模和辐射模等效成额外损耗。另一部分损耗来自弯曲臂本身的弯曲半径不够大以及两支臂靠太近的时候互相耦合串扰。器件尺寸做大了容易控制损耗但违背集成光路追求小尺寸的方向尺寸压小之后怎么在几微米到几十微米的范围内把损耗压住就是优化要解决的问题。官方案例里的Y分支模型基本形态是输入直波导接一个过渡区域再分出两条对称的S形输出臂。设计波长一般取1550纳米这是光通信C波段最常见的窗口。衬底用硅或者氮化硅这类高折射率差材料时波导对尺寸特别敏感0.1微米的偏差都会明显影响性能手动调参很难调出最优解这也是这套案例选粒子群算法的原因。1.2 官方案例里Y分支模型的初始状态案例给的初始结构通常已经能工作插损可能不算太差但离“好”还有距离。关键是把结构参数化也就是把过渡区长度、输出臂横向偏移量、S弯形状控制参数这些几何量提出来暴露成变量。优化器要做的就是改这些变量不断试出损耗更低、分束更均匀的组合。在Lumerical FDTD里参数化一般通过脚本里的set命令完成。比如模型树里有一个组对象叫y_branch里面某个子对象的名字叫L或者d脚本里写select(y_branch); set(L, 20e-6); set(d, 2.5e-6);结构就跟着变了。优化循环里每一轮把新的参数写进去运行一次FDTD仿真从监视器里读出输出端的透过率然后根据这个数值判断当前几何是好是坏。整个过程看起来直接但实际跑起来有几个坑后面会专门说。1.3 优化这类器件要关注哪些指标单个透过率指标容易误导人。Y分支的输出端有两个口只看一路可能另一路漏光了只看总透过率可能两支不平衡一边60%一边40%总效率还行但作为3dB分束器根本不合格。所以至少要同时看三个量输出端1的透过率T1输出端2的透过率T2两支的不平衡度|T1 - T2|插入损耗可以用-10*lg(T1T2)来算单位dB分光比则是T1/(T1T2)理想值是0.5。做目标函数的时候这几个量要组合起来用不能单吊一个否则优化结果会偏。2. 粒子群算法原理与关键参数2.1 粒子群算法的核心思想粒子群优化算法PSO是Kennedy和Eberhart在1995年提出的灵感来自鸟群觅食。它的基本设定很简单有一群鸟在空间里飞每只鸟知道自己当前去过的最好位置也知道整个群体目前发现的最好位置然后每只鸟根据这两个信息调整自己的飞行方向和速度。放到优化问题里“位置”就是一组设计参数“最好”就是目标函数值最大或最小“鸟”就是一个候选解。每个粒子在每一代迭代中按这个规则更新速度更新公式v_i(t1) wv_i(t) c1r1*(pbest_i - x_i(t)) c2r2(gbest - x_i(t))位置更新公式x_i(t1) x_i(t) v_i(t1)这里的w是惯性权重控制粒子保留上一代运动趋势的程度c1和c2是学习因子分别控制粒子飞向自身历史最优和全局最优的力度r1和r2是0到1之间的随机数给过程引入随机性防止粒子群过早抱团收敛到局部最优。这个公式看起来简单但三个项各有作用。惯性项让粒子有继续探索的倾向不会一下子就冲过去认知项让粒子记住自己的好经验社会项让好结果能快速在群体中传播。调参数的时候本质上就是调这三股力量的比例。2.2 参数设置与经验值粒子群算法参数不多但这几个参数直接影响收敛速度和结果质量。我复现这期案例时用的参数范围如下参数推荐范围说明粒子数N10到30设计变量少取15左右变量多可以加到30迭代次数K20到50受单次FDTD仿真耗时限制学完流程后自行权衡惯性权重w0.9线性降到0.4前期探索性强后期收敛细调学习因子c1、c21.5到2.0一般取1.7两个值可以一致速度上限设计空间的10%到20%防止粒子飞出边界导致仿真几何异常边界处理越界截断或反弹我习惯越界直接拉回到边界值粒子数不是越多越好。每次迭代每个粒子都要跑一次FDTD仿真单次仿真几分钟的话15个粒子一代就要几十分钟30代就是十几个小时。所以实际工程里往往会先压缩粒子数和代数或者用2D等效折射率模型替代3D仿真来预优化。2.3 为什么光学优化用PSO而不是直接扫参很多人一开始会想两个参数而已直接网格扫参不就行了。但光学器件仿真跟普通函数不一样每跑一次都是完整的电磁场求解一次几十秒到几分钟不等。参数一多网格扫参的计算量是指数增长的两个参数各取10个点就是100次仿真五个参数就是十万次完全不现实。梯度下降类的算法在这种场景下也不好使。FDTD仿真结果本身就带有数值噪声目标函数曲面不光滑解析梯度根本拿不到数值差分求梯度又贵又不准。粒子群这类无梯度方法只需要评估目标函数值本身不需要求导实现也简单几十行代码就能跑起来特别适合中等维度、单次评估耗时长的优化问题。3. 实操在Lumerical FDTD里串起Y分支与PSO3.1 打好Y分支基础模型先说建模。新建一个FDTD项目之后第一步是定义材料。硅波导可以直接用Lumerical材料库里的Si衬底用SiO2也可以用等效折射率法把三维结构压成二维来做预优化。二维仿真速度比三维快一两个数量级学习阶段先用二维验证整个链路三维再精调是效率最高的做法。然后是画结构。输入波导是一条宽度0.5微米的直波导过渡区之后两条输出臂分别向上下弯出去弯道轮廓用S形曲线定义。为了后面能调参数尽量把结构建成一个组对象并把过渡区长度L、输出臂横向偏移量d设成模型变量。这一步很关键如果后面每改一个参数都要手动重画自动优化就无从谈起。FDTD区域要完全包住整个器件边界条件用PML吸收边界。光源用模式光源放在输入波导截面位置中心波长1550纳米模式数选基模。输出端的两个监视器分别放在两条输出臂的末端用来读透过率。先手动跑一次baseline记录初始的T1和T2。我这次复现时baseline总透过率大概在75%左右分束比约为48:52看起来能用但优化空间确实很大。3.2 确定设计变量与目标函数我这次保留了过渡区长度L和输出臂横向偏移量d两个变量。L的范围设在5微米到30微米d的范围设在1微米到5微米。变量越少粒子群收敛越快先跑通流程再加更多变量也不迟。目标函数写法很关键。直接用T1T2虽然能提高总效率但可能出现分束严重不均的结果。用T1*T2就好很多两个输出都是0.5时乘积为0.25这是理论最大值任何一边变差乘积都会掉下来。这个函数同时惩罚总损耗和不平衡不需要额外调权重是Y分支这类分束器优化的一个很实用的选择。我实际用的评估逻辑是这样的run; T1 transmission(monitor_out1); T2 transmission(monitor_out2); fitness T1 * T2;如果还想额外约束分光比可以改成fitness T1T2 - 0.5abs(T1-T2)惩罚力度通过系数调。但第一个版本建议用最简单的T1*T2先把流程走通。3.3 脚本调用方式与PSO主循环这里正好回答一个很多人问的问题Lumerical到底怎么调用脚本。方式有几种第一种是直接在Lumerical的脚本编辑器里写.lsf脚本文件从脚本窗口点运行适合在软件内部做完整流程。第二种是在Lumerical命令行窗口里逐行输入命令调试。第三种是用外部Python或者MATLAB调用Lumerical的API接口也就是lumapi模块适合把优化算法写在更熟悉的环境里。我这次用的是Python加Lumerical API的方式粒子群主循环写在Python里每次评估时调用FDTD仿真。核心代码结构如下import numpy as np import lumapi fdtd lumapi.FDTD() fdtd.load(y_branch.fsp) def evaluate(x): fdtd.setnamed(y_branch, L, x[0]) fdtd.setnamed(y_branch, d, x[1]) fdtd.run() T1 fdtd.transmission(monitor_out1) T2 fdtd.transmission(monitor_out2) return T1 * T2 # 参数设置 n_particles 15 n_iterations 30 w 0.8 c1, c2 1.7, 1.7 lb np.array([5e-6, 1e-6]) ub np.array([30e-6, 5e-6]) # 初始化粒子群 X np.random.uniform(lb, ub, size(n_particles, 2)) V np.zeros_like(X) pbest X.copy() pbest_fit np.array([evaluate(x) for x in X]) gbest pbest[np.argmax(pbest_fit)] gbest_fit pbest_fit.max() for it in range(n_iterations): r1 np.random.rand(n_particles, 2) r2 np.random.rand(n_particles, 2) V w * V c1 * r1 * (pbest - X) c2 * r2 * (gbest - X) X X V X np.clip(X, lb, ub) for i in range(n_particles): fit evaluate(X[i]) if fit pbest_fit[i]: pbest_fit[i] fit pbest[i] X[i].copy() if fit gbest_fit: gbest_fit fit gbest X[i].copy() print(迭代次数:, it 1, 当前最优:, gbest, 适应度:, gbest_fit)注意单位问题。Lumerical默认长度单位是米所以代码里下边界写的是5e-6而不是5写错会导致仿真里出现几十微米甚至几米的奇怪结构。我第一版代码就吃过这个亏跑出来的粒子位置全是边界值结果完全不对。fdtd.setnamed里的对象名要和模型树里的名字完全一致。如果名字对不上脚本不会报错只是参数没写进去仿真结果不变粒子群空转很久还找不到方向。排查方法是先手动点开Object Tree确认组对象的准确名称或者在脚本里打印一下设置后的参数值看看有没有变化。3.4 结果后处理与最优结构验证粒子群跑完一轮后把gbest里的参数取出来用这组参数重新建一次模型跑一次完整的三维仿真来验证。不要直接相信优化过程中的二维结果二维等效折射率模型做了近似趋势对绝对值不一定准。验证时除了看T1和T2还要看频谱。把光源改成宽谱光源跑一次后查看输出端透过率随波长的变化可以确认优化结果在目标波长附近是否平滑有没有在其他波长引入意外的谐振。再看电场分布图如果分叉点有明显的散射场说明模式过渡仍然不理想可能需要增加变量维度来进一步优化。我这次优化后的最终参数是L约18微米、d约2.3微米总透过率从75%提升到87%左右分束比接近50:50。和baseline对比插损降低了大约0.6dB效果还是明显的。4. 常见问题与排查技巧实录4.1 窗口界面比例不协调怎么处理Lumerical打开之后各个窗口界面比例不协调的问题很多新手一上来就遇到。布局错乱通常是分辨率变化、外接显示器切换或者软件窗口缩放导致的。解决办法其实不复杂所有子窗口的分隔条都可以手动拖拽拖乱之后双击分隔条可以快速恢复均分如果不小心把某个窗口拖得找不到了可以在布局设置里选择重置布局把界面恢复到默认状态。调好之后建议把当前布局保存下来下次打开直接用保存的方案就不会再乱了。4.2 脚本运行报错的排查思路最常见的报错是对象名对不上。用select(xxx)的时候名称必须和Object Tree里的完全一致大小写、空格都要注意。有时候模型里有两个相似对象select选错了对象后面参数写错地方结果也很奇怪。transmission函数返回空值或报错时先检查监视器是否放对位置、是否开启了透过率计算选项。监视器名字写错不会立刻报错而是返回0导致目标函数看起来是0粒子群完全无法更新。这种问题排查起来特别阴我的经验是第一次跑优化前先手动运行一次完整仿真在命令行里分别查看两个监视器的透过率确认数值合理再挂循环。FDTD单次仿真不收敛的情况也要注意。如果结构里出现了极小尺寸的几何体网格不够密时仿真实质上是不准的。粒子群搜参过程中会随机产生各种几何偶尔出现极端参数导致网格畸形仿真时间暴增。所以速度和位置边界一定要限制好别让算法跑出合理物理范围。4.3 PSO优化不收敛或震荡粒子群迭代几十代后最优值还在跳来跳去先别急着改算法。可能是目标函数本身有数值噪声相邻两代之间差异本来就小于仿真误差这种情况继续加迭代意义不大。更常见的是w太大导致粒子一直在最优解附近震荡可以在后期把w手动降到0.3以下。还有一种情况是粒子群很快就收敛到了一个固定值但这个值明显不是好结果大概率是早熟收敛到了局部最优。可以增大粒子数或者把c1调大一些、增强认知项让粒子更愿意自己去探索。也可以连续多代跑完后随机重置一部分粒子的位置到全局解之外保持多样性。4.4 仿真时间与精度之间的平衡FDTD单次仿真耗时是粒子群优化的最大瓶颈。我第一轮用3D全矢量仿真跑单次大概3分钟15个粒子一代就是45分钟整个30代跑完要二十多个小时。后来改成先用2D等效折射率模型做预优化一轮下来快得多再用最优参数在3D里精验证几次总时间控制在几个小时内。网格精度设置也要匹配优化阶段。预优化阶段网格精度4级足够最后验证时再开高。优化过程中监视器数量和频点越多单次仿真越慢所以目标函数里尽量算窄带结果不要每次都扫整个宽谱。5. 实操后的几个体会5.1 自动优化真正的难点在于目标函数设计跑完这个案例最大的感觉是粒子群算法本身很好实现真正决定优化结果的是目标函数怎么设计。目标函数没有把分束比约束进去跑出来的结果很可能总效率高但两支严重不均约束太强算法就把精力花在均匀性上总损耗反而下不来。T1*T2这个形式恰好平衡了这两个目标这比调一堆算法参数都管用。5.2 如果再优化一次我会改哪里如果继续往下做我第一件想改的是把变量从两个扩展到四个把输出臂末端波导宽度、过渡区形状系数都加进来。变量多了之后粒子群可能不够用可以对比一下贝叶斯优化或者CMA-ES这类算法它们在高维问题上经常比PSO更稳。另外可以每跑几代就把当前的最优模型自动保存一份快照防止中间进程崩溃导致整个优化白跑这个操作成本很低但能省掉很多重复时间。
返回列表