简介:面向微纳光学器件与超表面仿真学习者,这份FDTD Solutions中文教程以Lumerical时域有限差分法为核心,帮助科研人员和工程技术人员建立从建模、材料设置到结果分析的完整仿真思路,适合具备一定光学基础、希望系统掌握软件操作的初中级用户。资源包为1个PDF文件,约11.61MB,内容按新功能、求解物理问题的方法、单位与规范化、计算机辅助设计模拟编辑、模拟实体对象、材料数据库、模拟计算与分析、脚本语言等模块组织,兼具操作指南与参考手册属性。目前已有10743人学习。读者可重点查阅8.6版用户自定义材料模型、非对角各向异性介质、模式扩展监视器、可旋转模式光源、场分析工具及材料拟合改进等要点,也可借助脚本语言章节理解系统控制、变量操作、绘图命令、近远场投影与光栅投影等进阶用法,便于在实际项目中查漏补缺并提升仿真建模与结果解读能力。
1. 超表面单元扫完一遍参数,透射率曲线抖成锯齿,先别急着怀疑算法
超表面单元跑完 20 组结构参数,透射率曲线在共振点附近抖成锯齿,多数人的第一反应是求解器不收敛,血泪经验是:八成网格和材料拟合没对齐。这份 8.6 版参考指南加脚本语言手册,讲的正是这条链路——FDTD Solutions 背后的时域有限差分法怎么把麦克斯韦方程离散到矩形网格上、单位和归一化怎么定、实体对象与材料数据库怎么建、8.1 到 8.16 那套脚本命令怎么把图形界面的点击固化成可复现的代码。它面向微纳光学器件与超表面的研究生和工程人员,也适合刚从频域求解器转过来的人。新手可以顺着章节把一条完整仿真跑通,熟手更适合直接翻材料建模、模式扩展监视器和脚本函数那几节。
2. 网格、边界与归一化:一条仿真链路上最容易糊弄的三件事
同一套结构参数,两个人跑出来的透射率差 5%,争议往往停在“谁的网络更密”上。真实原因通常分散在三处:网格把曲面切成了台阶、PML 把反射又送回了监视器、归一化用的源谱本身不平坦。这三件事在参考指南里分属不同章节,实际却是一条链上的耦合项,任何一环没配对,后面的优化和扫描都是白跑。下面按建模顺序拆开讲,每一步给出可执行的设置和判断依据。
2.1 网格精度:mesh accuracy 只是起点,共形网格才是分水岭
FDTD Solutions 用的是矩形、笛卡尔网格,材料属性、电场和磁场都在网格单元上计算。网格越细,器件形状和场分布描述得越准,代价是计算时间和内存近似按网格点数线性增长——三维下网格点数与步长的三次方成反比,把 dx 减半,内存直接涨八倍,这是很多人第一次跑三维超表面就翻车的地方。
软件默认自动生成网格,规则是:高折射率材料保证单位波长内网格点数固定,高吸收材料按穿透深度加密。这条规则对平板和体材料够用,对超表面就不够了——单元里的间隙、尖角、几十纳米厚的薄层,自动网格经常只落一两个网格点,场在该处剧烈变化却采样不足。共形网格算法就是为了解决这个:它不再把曲面切成台阶,而是在边界单元内修正材料填充比例,薄层、曲面、表面等离激元结构用相对粗糙的网格也能拿到可信结果。
# 建立 FDTD 求解区域,先定维度、精度和边界 addfdtd; set("dimension", "3D"); set("x", 0); set("x span", 2e-6); set("y", 0); set("y span", 2e-6); set("z", 0); set("z span", 1.2e-6); set("mesh accuracy", 3); # 1~8,数字越大越密,起步用 2~3 试跑 set("mesh refinement", "conformal variant 1"); # 曲面/薄层选共形网格 set("simulation time", 1000e-15); # 时域积分长度,太短会截断自由感应衰减 set("x min bc", "PML"); set("x max bc", "PML"); set("y min bc", "PML"); set("y max bc", "PML"); set("z min bc", "PML"); set("z max bc", "PML");mesh accuracy是全局粗调,mesh refinement决定边界处理方式,共形网格有两个变体,variant 1 更保守、variant 2 更激进,前者适合先确认趋势,后者在网格确实吃紧时用,但必须做收敛验证。simulation time不要凭感觉填,判断标准是场衰减到自动关闭阈值以下,否则频域结果会出现纹波,看起来像多出来的共振峰。
局部加密靠网格覆盖区完成,金属间隙、超表面单元边缘、波导侧壁这些地方强制小步长。常见做法是在结构最薄处放一个覆盖区,步长取最小特征尺寸的三分之一到五分之一。
# 在单元中心区域强制 4 nm 网格,只加密需要的地方 addmesh; set("x", 0); set("x span", 600e-9); set("y", 0); set("y span", 600e-9); set("z", 0); set("z span", 400e-9); set("override x mesh", 1); set("dx", 4e-9); set("override y mesh", 1); set("dy", 4e-9); set("override z mesh", 1); set("dz", 4e-9);覆盖区不要图省事覆盖整个仿真区,那等于把全局网格调密一遍。判断收敛的土办法很有效:把mesh accuracy从 2 提到 3 再提到 4,关键指标(共振波长、峰值透射率、衍射效率)变化小于 1%~2% 就可以停,继续加密只是在烧机时。反过来,如果精度从 2 到 4 结果一路漂移,别急着上 5,先检查是不是共形网格没开、材料拟合有残差。
2.2 PML 边界:直接指定反射率,比堆层数更可控
PML 的作用是吸收所有入射光、反射为零。实际总有残余反射,早期做法是增加 PML 层数压低反射,代价是仿真变慢、内存占用上升。7.0 之后可以直接指定需要的 PML 反射率,让软件自己决定层数和参数,这比手工调层数靠谱得多——目标反射率给到 1e-5 量级,对绝大多数微纳光学场景已经够用,再低就是在和数值噪声较劲。
一个容易被忽略的约束:PML 边界在完全覆盖结构周边时表现最好。很多设置错误来自结构只画到仿真区边缘就停了,材料属性没有延伸到 PML 内,边界处出现阻抗突变,反射直接混进监视器。7.0 起默认行为是让 PML 边界自动扩展材料属性,即使绘图时没画到那儿,这条默认值救了不少人,但如果工程是从老版本迁移过来的,务必确认这个选项处于打开状态。
可旋转模式光源那条设置与 PML 有一个直接冲突,属于典型的新功能踩坑点:设定旋转角度后,模式光源沿一定角度的平面导入,此时必须确保波导或光纤沿着 PML 边界方向延伸出去,同时要保证高级选项里的“extend structure through PML”没有被勾选。勾上之后结构会被强行拉伸穿过 PML,模式剖面与光源注入面对不上,注入效率掉得莫名其妙,场图看上去还挺正常,这种问题最难查。
| 边界类型 | 适用场景 | 关键设置 | 常见误用 |
|---|---|---|---|
| PML | 开放边界,辐射泄漏、散射 | 目标反射率 1e-5 量级 | 结构未延伸到 PML,反射混入监视器 |
| 周期 | 周期阵列单元、超表面单胞 | 周期长度与单元一致 | 斜入射时直接用周期边界导致场不匹配 |
| Bloch | 斜入射周期结构 | 给出布洛赫波矢 | 波矢方向与入射面定义不一致 |
| 金属 | 近似封闭腔、波导端面 | 无 | 用金属边界模拟开放辐射,结果全错 |
2.3 单位与归一化:cwnorm 决定了你看到的透射率是不是真值
软件内部全部按国际单位计算:长度是米,频率是赫兹,时间是秒。脚本里写600想表达 600 纳米,实际给的是 600 米,仿真区直接跨到天文尺度,求解器不报错但结果毫无意义。这类错误在新手脚本里出现频率极高,尤其是从图形界面抄参数到脚本的时候。稳妥习惯是变量名带单位后缀,period_nm = 600,赋值时统一乘1e-9。
归一化比单位更容易糊弄。频域监视器默认按源功率谱做连续波归一化(cwnorm),含义是:监视器上得到的场和功率,已经除掉了源本身的光谱形状,你看到的是相对源的响应。这个默认值对透射率计算是友好的,因为源谱通常不平坦,不归一化的话,源谱的起伏会伪装成结构的共振。
# 手动归一化:监视器功率逐频点除以源功率 P_mon = getresult("T_mon", "T"); # 频域功率监视器结果 P_src = sourcepower("source"); # 光源注入功率谱 T = P_mon.T / P_src; # 逐频点相除,得到透射率 f = P_mon.f; # 对应频率轴sourcepower取的是源在频域上的注入功率,两个数组的频率轴必须对齐,采样点不一致时要先插值再相除。更省事的做法是用内置的transmission函数,它内部已经按源功率归一化,直接给总透射率。
归一化还有一个隐性依赖:参考仿真。严格做法是先跑一次没有结构、只有源和监视器的仿真,把源谱存下来,再跑结构仿真,两者相除。不做参考仿真而直接相信 cwnorm,在源谱有深谷的波段会放大噪声,曲线看上去毛刺很重,多数人误以为是网格问题,其实是分母接近零。
2.4 2D 与 3D 混跑:什么时候可以偷懒,什么时候必须上三维
8.6 版允许在同一个工程文件里把不同模拟区间定义为 2D 和 3D,二维绘图模式可以把空间结构映射成平面视图。这个功能的价值在于分级验证:先用 2D 把物理趋势摸清,再上 3D 确认定量结果,比一上来就三维硬跑省掉大量机时。
2D 适用的前提是结构沿一个方向平移不变,或者可以合理近似为无限长:平板波导的有效折射率提取、长周期光栅的定性趋势、波导耦合长度的量级估计。超表面单胞如果是各向同性对称的圆柱或方柱阵列,斜入射下用 2D 近似也能给出趋势,但绝对效率不能直接引用,模式面积和耦合效率在 2D 与 3D 之间差异显著。
必须上 3D 的情形包括:有限尺寸单元、非对称结构、需要真实远场分布、偏振转换类器件。判断标准不是“结构看起来复杂”,而是“沿传播方向是否存在不可忽略的结构变化”。斜入射时如果入射面与结构对称轴不重合,2D 近似会直接丢掉一个维度的相位信息,远场偏折角算出来是错的。
2D 到 3D 的转换在参考指南里有专门一节,操作上要注意监视器和光源的维度也要跟着改,只改求解区维度而保留 2D 监视器,会出现监视器不在求解区内的报错。混跑时建议把 2D 与 3D 分别放在不同 group 里,用addgroup和groupscope管理对象树,避免属性互相覆盖。
3. 材料建模:从采样折射率到 9 元素介电常数张量
材料是微纳光学仿真里最不显眼却最容易出错的一环。同一份硅的折射率数据,用不同拟合方式喂进时域求解器,共振峰能偏出十几纳米。原因在于时域方法不能直接吃频域的 n、k 表,必须把它拟合成能在时间步上迭代的解析形式,拟合残差就直接变成结果误差。这一章按材料复杂度递增讲三条路线,最后落到非线性与各向异性这两种 8.6 版重点增强的能力上。
3.1 三条建模路线:采样数据、解析模型与用户自定义
第一条是采样数据材料:给一张折射率随波长变化的表,软件用多系数模型拟合成时域可迭代的形式。这条路最通用,金属、介质、常见半导体都能覆盖,代价是拟合质量取决于数据范围和模型阶数。8.6 版对拟合程序做了优化,低损耗色散材料的拟合效果改善明显,但拟合失败依然常见,典型症状是长波端折射率虚部变成负值,物理上对应增益,实际材料并没有增益。
第二条是解析模型:德鲁特模型描述金属的自由电子响应,洛伦兹和德拜模型描述束缚电子与极性分子的弛豫,塞米尔方程描述透明介质的色散。这些模型参数少、物理含义清晰,适合从文献里直接拿参数复现,缺点是覆盖的波段有限,一个德鲁特模型很难同时拟合可见和红外。
第三条是用户自定义材料:8.6 版允许创建插件材料并直接修改更新方程,理论上任何形式的电极化响应都能写进去。这条路灵活,但对使用者的要求是真正理解方程离散后的稳定性条件。
| 建模方式 | 典型材料 | 关键参数 | 主要风险 |
|---|---|---|---|
| 采样数据 + 多系数拟合 | 金、银、硅、常见介质 | 拟合波长区间、系数个数 | 拟合残差大、虚部出现负值 |
| 德鲁特 | 金属 | 等离子体频率、碰撞频率 | 波段外行为失真 |
| 洛伦兹 / 德拜 | 极性介质、聚合物 | 共振频率、振子强度、弛豫时间 | 多振子叠加后参数不唯一 |
| 塞米尔 | 透明介质 | 色散系数 | 吸收波段无法描述 |
| 用户自定义 | 非线性、负折射率 | 更新方程、耦合系数 | 时间步不匹配导致发散 |
不管走哪条路,拟合完必须检查,这一步不能省。软件里的材料浏览器可以把拟合曲线和原始数据叠在一起看残差,重点看三处:你关心的波长区间内残差是否足够小、虚部是否始终非负、拟合区间外推是否出现异常。很多人栽在只关心 1550 nm 却用了一份覆盖 200~1000 nm 的数据,外推出来的折射率完全是编的,仿真照样跑完,结论全错。
3.2 各向异性介质:张量怎么给,旋转靠什么对象
8.6 版支持完整的 9 元素介电常数张量,右侧意味着对整个 j 求和,这让磁光效应、液晶分子轴向、斜切晶体的仿真成为可能。设置顺序是有讲究的:先指定张量的对角元素,再挂一个格子特性实体对象(Grid Attribute)给出正确的旋转,让张量跟着结构一起转。
# 先建一个对角张量材料,再通过 Grid Attribute 做旋转 mat = addmaterial("Aniso_Medium"); setmaterial(mat, "Anisotropy", 1); eps_diag = [2.25, 0, 0; 0, 2.25, 0; 0, 0, 2.56]; # 对角元素:寻常光/异常光 setmaterial(mat, "epsilon", eps_diag); # 旋转交给 Grid Attribute 对象,别直接改张量的非对角项 addgridattribute("matrix transform"); set("rotation 1", 30); # 绕第一个轴旋转 30 度直接手改非对角项容易翻车的点在于:张量的旋转必须和网格方向一致,如果网格本身在斜向上不是各向同性的,旋转后的结果会出现阶梯化误差,表现为不同网格精度下结果漂移。稳妥流程是先固定网格、只转张量,观察结果是否随旋转角连续变化,再回头调网格。
各向异性材料的另一个坑是监视器设置。普通功率监视器只给总功率,看不出偏振分量的变化,磁光效应这类场景需要分别记录两个正交偏振分量,或者用模式扩展监视器把场投影到指定模式上,否则你只能看到总透射率几乎不变,误以为效应不存在。
3.3 非线性、增益与磁性材料:先把时间尺度对齐
8.6 版在材料数据库里新增了几类模型:可指定极化率的非线性材料,可以和不限于基材的色散基底组合;顺磁性材料,同时给介电常数和磁导率来拟合磁性响应;拉曼-可尔模型,用来模拟硅的拉曼效应,也能覆盖孤子传播和四波混频;四级二电子激光模型,用来模拟增益和激光动力学。
这几类模型的共同点是引入了额外的時間尺度,而时域求解器的步长必须能分辨最快的那一个。拉曼响应时间在飞秒到皮秒量级,增益恢复时间可能到纳秒,如果仿真时间只覆盖几百飞秒,看到的只是瞬态,不是稳态增益。反过来,为了覆盖纳秒过程而把时间步压到阿秒级,网格点数不变的情况下机时会爆炸。
判断方法很直接:先查材料模型的弛豫时间常数,仿真时间至少取它的五到十倍,时间步取最快时间尺度的十分之一以下。非线性系数不要一上来就给文献里的体材料值,薄膜和超表面的有效非线性系数与体材料差一个量级很正常,先用小系数确认仿真稳定、场形合理,再逐步加到目标值。
顺磁性材料容易出的问题是只改了介电常数忘了磁导率,或者反过来。这类材料的磁响应和电响应在同一波段同时存在,只设一个的话,反射相位算出来和实验对不上,而且曲线形状看起来还挺合理,属于典型的黑匣子式错误。
4. 脚本语言:把 GUI 里的点击固化成可复现的 .lsf
图形界面适合探索,不适合复现。同一组参数扫三遍,手工点三遍必然有细微差异,尤其是监视器位置和网格覆盖区这类靠鼠标拖出来的对象。脚本的价值不只是省力,而是让每次仿真都有明确的输入记录。8.6 版的脚本章节从系统控制一路排到自定义命令,8.1 到 8.16 的划分其实就是从语言基础到结果后处理的完整链路。
4.1 变量、struct 与 cell:8.6 之后脚本终于有了像样的数据结构
8.6 版在脚本语言里加入了 struct 和 cell 两种数据结构。struct 是带命名字段的集合,适合管理一组设计参数;cell 是异构数组,可以装不同类型、不同长度的对象,适合存一批监视器结果或者一组不同结构的参数集。在这之前,扫参脚本只能靠一堆带下标的变量名硬撑,改一个参数要改十几处。
# 用 struct 打包一组超表面单元参数,扫参时只改字段 struct unit; diameter = 320e-9; height = 220e-9; period = 600e-9; material = "Si (Silicon) - Palik"; }; for (i = 1:5) { unit.diameter = 280e-9 + (i-1)*20e-9; # 280 ~ 360 nm,步长 20 nm ? "diameter = " + num2str(unit.diameter*1e9) + " nm"; }num2str把数值转成字符串方便打印,?是输出命令。循环上界写成1:5是闭区间,包含两端,这一点和很多语言不同,写1:5就是五个点,不是四个。条件语句的花括号不能省,即使只有一行。
struct 的字段名在同一个作用域内全局可见,多个 struct 用同名字段不会冲突,因为访问时要带结构名。这个特性用来管理多组对比设计很顺手,design_A.diameter和design_B.diameter互不干扰。cell 的用法更接近容器,配合length取元素个数,配合下标访问,适合把一批扫描结果按顺序存下来再统一后处理。
4.2 建结构、挂监视器、跑仿真:最小闭环
一条完整的脚本至少包含四步:清理工程、建求解区、建结构与监视器、运行并取结果。8.6 版新增的addparameter和addattribute让对象可以带自定义参数,addgroup用来把相关对象收进一个组里统一管理,这在结构复杂时非常必要——一个超表面单胞里的衬底、单元、包层、监视器加起来十几个对象,不分组的话属性面板会乱成一团。
newproject; # 清空当前工程,避免旧对象残留 addfdtd; set("dimension", "3D"); set("x span", 2e-6); set("y span", 2e-6); set("z span", 1.2e-6); set("mesh accuracy", 3); set("simulation time", 1000e-15); # 衬底 addrect; set("name", "substrate"); set("z", -300e-9); set("z span", 600e-9); set("material", "SiO2 (Glass) - Palik"); # 单元结构:圆柱,直径与高度来自 struct addcircle; set("name", "pillar"); set("x", 0); set("y", 0); set("z", unit.height/2); set("z span", unit.height); set("radius", unit.diameter/2); set("material", unit.material); # 监视器:透射、反射各一个,都覆盖整个单元 addpower; set("name", "T_mon"); set("monitor type", "2D Z-normal"); set("z", 400e-9); set("x span", unit.period); set("y span", unit.period); run; # 提交求解addrect建的是矩形块,addcircle建圆柱,两者的z span都是总厚度而不是半厚。监视器的monitor type要和维度匹配,三维仿真里用2D Z-normal表示法向沿 z 的平面监视器。周期结构的监视器尺寸取一个单元周期即可,再用周期边界条件,比把整个阵列画出来省几个数量级的机时。
跑完之后取结果,getresult返回的是一个数据集对象,里面除了数据本身还带频率轴和属性信息。直接对数据集做算术会报错,要先取到具体字段,这一点和用惯了数组的人预期不同。
4.3 结果提取与光栅投影:超表面衍射效率怎么算
普通透射率监视器给的是总功率,超表面真正关心的是能量分到了哪几个衍射级。近场到远场的投影功能就是干这个的:监视器上记录近场分布,通过投影算法算出远场角谱,再对周期结构做光栅投影,直接得到各级次的衍射效率。
# 近场监视器放在结构上方,记录切面场分布 addpower; set("name", "near_field"); set("monitor type", "2D Z-normal"); set("z", 500e-9); set("x span", 3*unit.period); set("y span", 3*unit.period); # 光栅投影:指定周期、级次范围与投影方向 grating_period = unit.period; n_orders = 5; # 取 -5 到 +5 级 # 取远场投影结果,按级次统计衍射效率 far = farfieldexact3d("near_field", 1, 1, 1);farfieldexact3d的参数分别是监视器名、频率点数、角度采样数等,实际调用时以软件内函数签名和帮助为准,不同版本参数顺序有调整。光栅投影的核心输入是光栅周期和级次范围:周期必须与结构的实际周期严格一致,差一个纳米,高级次的衍射角就会偏;级次范围按你的应用定,超表面的偏折效率通常只关心 ±1 级或某一特定级次,但建议把 ±5 级都算出来看能量是否收敛。
判断投影结果是否可信,看两条:各衍射级效率之和加吸收是否接近 1,能量守恒是硬约束;把监视器往远离结构的方向移一段再重算,结果变化应该很小,变化大说明近场还没衰减到可以投影的程度。
频率域的结果还有一层归一化要注意。投影得到的效率是相对入射功率的,如果入射光源做过角度旋转或者偏振设置,归一化基准要跟着改,否则效率加起来不等于 1,却找不到原因。
4.4 参数扫描、并行与优化:runsweep 与粒子群
7.5 版引入的并行任务管理让参数扫描从串行变成可分发:runsweep建扫描任务,addjob把任务加到队列,runjobs启动,clearjobs清空。局域网里有几台机器就能并行几个任务,前提是每台机器都有引擎许可,并行计算需要额外授权,这一点在规划机时之前要先确认。
# 建一个参数扫描:直径从 280 nm 到 400 nm,7 个点 sweepname = "diam_sweep"; runsweep(sweepname, "diameter", 280e-9, 400e-9, 7); addjob(sweepname); # 加入队列 runjobs; # 按可用许可数自动分发 out = getsweepresult(sweepname, "T"); # 一次性取回全部扫描结果getsweepresult是 8.6 版新增的命令,返回的是整个扫描结果的结构化数据集,比早期用getsweepdata逐点取再拼接要省事得多。扫描点数不要一上来就铺很密,先用 5 到 7 个点看曲线趋势,找到共振区间再局部加密,这是省机时最直接的办法。
优化的入口在图形界面里,7.0 版起提供基于粒子群的优化算法,也可以自己写算法替换。粒子群适合参数维度在几个到十几个、目标函数不连续的场景,典型用途是超表面单元的几何参数寻优。用之前先想清楚目标函数怎么写:单一波长的透射率最大化和宽带平均效率最大化,写出来的评价函数完全不同,后者要在意每个频点的权重,否则优化器会挑一个窄峰给你。
8.6 版新增的良率分析工具值得单独提一下:它在优化和扫描窗口里,做的是扩展的蒙特卡罗分析,通过多参数扫描评估工艺偏差对整体性能的统计影响。做器件的人都知道,仿真最优和工艺可实现之间隔着一整个分布,良率分析就是把这个分布显式算出来——给定刻蚀偏差和膜厚波动的范围,看透射率落在容差内的比例。
5. 排查清单:五类翻车现场与定位顺序
下面五条来自反复出现的同类问题,排列顺序基本就是排查顺序:先看数值合不合理,再看结果稳不稳定,然后看资源够不够,最后才怀疑物理设置和材料。跳过前面几条直接查物理,通常会把时间浪费在本来没问题的地方。
5.1 透射率大于 1 或者出现负值
现象:功率监视器算出的透射率超过 1,某些频点甚至是负数,曲线看上去像噪声。
原因:三种可能。监视器放在 PML 内部或者跨过 PML 边界,吸收层的场被算进了透射;归一化用的源功率与监视器不在同一位置,源和监视器之间还有反射元件;监视器尺寸小于模式场分布,能量从边缘漏出去,负值则多半是数值噪声在接近零的功率上取对数。
解决:把监视器移进求解区内部,距离 PML 至少几个网格;确认源功率取样点和监视器之间没有其他反射面;监视器横向尺寸取结构周期的整数倍并向外扩一些。做完这三步,透射率加反射率加吸收应该接近 1,对不上就说明还有能量从侧边界漏掉。
5.2 结果随网格精度乱跳
现象:mesh accuracy从 2 提到 3,共振波长移动十几纳米;再提到 4,又往回移一点,没有收敛趋势。
原因:共形网格没有开启,曲面被切成台阶,网格一变台阶形态就变;或者材料拟合残差本身就大,网格加密只是把拟合误差暴露得更清楚;还有一种可能是网格覆盖区的步长与全局步长不成整数比,插值引入额外误差。
解决:先开共形网格重跑;再打开材料浏览器看拟合残差,残差大的话换拟合区间或增加系数;最后检查覆盖区步长,尽量取全局步长的整数分之一。收敛验证每次只改一个变量,同时改网格和材料,永远不知道是谁的锅。
5.3 内存爆掉、仿真跑一整夜没有结果
现象:提交后进度条长时间不动,任务管理器里内存一路涨到机器上限,最后报内存不足。
原因:三维仿真区的体积乘以网格密度得到的总网格点数远超内存,尤其是加了覆盖区之后局部步长降到几纳米,而覆盖区又画得很大;仿真时间设得过长,时域迭代步数与内存虽然不是线性关系,但结果文件会持续膨胀;PML 层数手工设得过多。
解决:先估网格点数,仿真区体积除以步长三次方,再乘场分量数,量级对了再提交;覆盖区只包住真正需要的区域;PML 改用目标反射率指定而非手工层数;能上 2D 先上 2D 摸趋势。如果机器有多个计算节点,用并行任务分发而不是单机硬扛。
5.4 斜入射与可旋转模式光源报错或场形畸变
现象:设置旋转角之后,模式光源注入的场明显不对称,或者波导里的模式剖面被压扁,透射率比预期低一个量级。
原因:高级选项里的“extend structure through PML”被勾选,结构被强行拉伸穿过 PML,模式注入面与拉伸后的结构对不上;或者波导没有延伸到 PML 边界,注入面处存在截断反射。
解决:取消该选项,手动把波导或光纤沿注入方向延伸出 PML 边界;确认旋转角与结构的实际晶向一致;先用垂直入射验证模式光源本身没问题,再逐步加角度。TFSF 光源在 8.5 版之后支持任意角度导入,斜入射场景优先用它而不是硬转模式光源。
5.5 材料拟合报错与人工负虚折射率
现象:拟合完成后材料浏览器里虚部出现负值,或者仿真跑出增益,能量不守恒。
原因:采样数据的波长范围没有覆盖仿真波段,外推导致非物理结果;多系数模型的系数个数不足以描述强色散;用有效折射率法把三维压成二维时,生成的等效材料继承了不真实的色散特性,8.6 版文档里明确提到这种情况可能出现人工负虚折射率。
解决:把仿真波段限制在采样数据范围内,不够就补数据;增加拟合系数并检查残差;对有效折射率法生成的等效材料,按原始材料物理特性的最小值和最大值限定生成折射率的范围,软件里有对应设置项。限定范围会牺牲一点精度,但能避免非物理增益这种更麻烦的问题。
6. 进阶:模式扩展监视器把 FDTD 接到线路级仿真
单器件仿真做完,下一步通常是把它放进系统里看整体响应。传统做法是手工提取 S 参数再导入线路仿真器,中间隔着格式转换和插值误差。8.6 版的模式扩展监视器把这一步做进了求解器:它能把监视器上的任意场分布扩展到一个指定模式或者一组模式上,返回的结果直接就是 S 参数形式,可以导入线路级仿真环境继续用。
操作上分三步。第一步在波导或器件的输入输出端各放一个模式扩展监视器,监视器类型要和波导截面匹配,比如沿 x 法向的波导用2D X-normal。第二步指定模式选择方式,可以选择基模、指定阶数,或者导入一个预先算好的模式文件。第三步运行后取扩展结果,得到的是各模式的复振幅系数,也就是 S 参数里的 a 和 b。
# 输入端口的模式扩展监视器 addmodeexpansion; set("name", "exp_in"); set("monitor type", "2D X-normal"); set("x", -800e-9); set("mode selection", "fundamental TE"); # 先只扩基模,确认后再加高阶 # 输出端口 addmodeexpansion; set("name", "exp_out"); set("monitor type", "2D X-normal"); set("x", 800e-9); set("mode selection", "fundamental TE"); run; # 取扩展系数,进一步组合成 S 参数 S_out = getresult("exp_out", "expansion for fundamental TE");mode selection的选择直接决定结果的含义:只扩基模时,高阶模式的能量会被算进辐射损耗,看起来插损偏大;把高阶模一起扩进来,能看清能量到底转到了哪个模式。器件里有模式转换或者多模干涉时,务必把相关模式都加上,否则 S 参数矩阵不完整,接进线路仿真后能量对不上。
模式扩展监视器返回的 S 参数可以直接导入线路级仿真环境,这是 8.6 版强调互操作性的地方。实际流程是:FDTD 里扫一遍波长,得到 broadband 的 S 参数数据集,导出后在线路仿真器里作为元件模型使用。这里有个容易忽略的细节——FDTD 的 S 参数是相对于模式功率归一化的,导入前要确认线路仿真器端的端口阻抗定义一致,不一致的话会出现莫名其妙的整体损耗。
验证这套链路是否可信,我一般会走三道检查。第一道是能量守恒:所有端口的 S 参数模平方和加辐射损耗应该接近 1,差太多说明模式集合不全或者监视器位置不对。第二道是收敛性:把网格精度提一档,S 参数的相位变化应该远小于 1 度,幅度变化小于 1%。第三道是对称性:互易器件在交换端口后 S 参数应该对称,不对称就回头查监视器法向和模式方向定义是否反了。
还有一条经验是从模式扩展监视器上得到的启发:不要等到最后才做系统级验证,器件级仿真阶段就把端口 S 参数算出来存好,后面无论换线路仿真器还是做实验对比,都有现成的数据可用。从那以后我每次建仿真工程,第一件事就是把输入输出端口的模式扩展监视器加上,哪怕当前只需要看透射率——多花的那点机时,远比事后回头补算便宜。希望帮到你。
本文还有配套的精品资源,点击获取