MATLAB有限元法手搓脊形波导仿真:从Helmholtz方程到模场求解

发布时间:2026/10/3 3:07:43
MATLAB有限元法手搓脊形波导仿真:从Helmholtz方程到模场求解 简介这份资源面向光学与光子集成领域的学习者和研究人员提供一套基于MATLAB有限元方法FEM模拟脊形波导中光波传播的仿真程序适合具备一定电磁场与数值计算基础、希望快速上手波导模式分析的读者。压缩包内共1个文件为单个m脚本整体约3KB核心代码围绕脊形波导的几何建模、折射率分配、有限元网格划分、边界条件设置以及标量亥姆霍兹方程求解展开可输出电场强度与相位分布并进一步分析传播损耗、模场分布和有效折射率等关键指标。目前已有342人学习下载说明该方向具备一定关注度。读者可借助这份脚本理解FEM在非均匀介质与复杂几何波导中的实现思路将其作为光通信器件与集成光路设计的入门仿真工具或在此基础上修改参数以优化脊形波导结构、降低传输损耗。1. 脊形波导仿真为什么值得用 MATLAB FEM 手搓一遍做集成光子的人迟早会碰到一个问题商用模式求解器跑出来的基模分布和实测耦合效率对不上。你怀疑是脊形波导的几何参数没吃准但软件是个黑匣子网格怎么划、边界怎么截断、特征值怎么迭代全看不见。这时候手里有一套能改、能读、能断点调试的 MATLAB 有限元代码价值就出来了。这份 FEM_opticalwaveguide 资源就是干这个的用标量有限元法求解脊形波导的模场核心是 FEM.m 一个脚本配套 FEM.zip 打包。它不追求替代 COMSOL 或 Lumerical而是让你把 Helmholtz 方程的弱形式、网格离散、边界条件、广义特征值求解这条链路完整走一遍。适合两类人一是刚接触光波导仿真、想搞懂 FEM 到底在算什么的研究生二是需要快速验证脊形波导单模条件、又不想每次开商用软件等 license 的工程师。MATLAB 在这里的角色不是“画图工具”而是把稀疏矩阵组装和 eigs 求解暴露给你参数改一行模场立刻重算。2. 标量 Helmholtz 到广义特征值FEM.m 里到底在解什么2.1 从波动方程到弱形式的推导链路脊形波导的模场分析本质是求下面这个标量 Helmholtz 方程的本征解∇²E k₀²n²(x,y)E 0其中 k₀ 2π/λ 是真空波数n(x,y) 是横截面折射率分布E 是标量电场。脊形波导的折射率分布是分区的脊区折射率 n_core两侧包层 n_clad基底 n_sub。要求解的是满足边界条件的离散本征值 β²进而得到有效折射率 n_eff β/k₀。直接对强形式做有限元会碰到二阶导数难处理的问题标准做法是转弱形式。两边乘测试函数 v 并积分用格林公式把 ∇² 降阶∫∫ (∇v·∇E) dΩ k₀²∫∫ n²vE dΩ - β²∫∫ vE dΩ整理后得到广义特征值问题[K]{E} β²[M]{E}K 是刚度矩阵对应 ∇v·∇E 项M 是质量矩阵对应 n²vE 项。这一步是整个代码的骨架FEM.m 里组装稀疏矩阵的部分就是围绕这两个矩阵展开的。常见做法是用一阶三角形单元每个单元三个节点局部刚度矩阵按面积和形函数梯度算再按全局节点编号累加。2.2 网格离散与折射率分配的实现细节脊形波导的横截面是矩形叠矩形网格划分要保证脊区边界附近足够密。FEM.m 里一般用规则三角网格或者调用 MATLAB 的 triangulation 生成。关键参数有三个脊宽 W、脊高 H、以及计算域的总尺寸。计算域不能取太小否则模式会被边界“压”变形也不能太大否则网格量爆炸。折射率分配的逻辑是按单元质心坐标判断落在哪个区域% 按单元质心分配折射率 for e 1:numElems cx mean(nodes(elemNodes(e,:),1)); cy mean(nodes(elemNodes(e,:),2)); if cx -W/2 cx W/2 cy 0 cy H n_elem(e) n_core; % 脊区 elseif cy 0 n_elem(e) n_sub; % 基底 else n_elem(e) n_clad; % 包层 end end这段逻辑说明用质心判断比用节点判断更稳因为一个单元可能跨在折射率界面上质心法相当于给单元一个等效折射率。参数上n_core 对硅波导取 3.48n_clad 取 1.44SiO₂n_sub 同样取 1.44。如果你的结构是 SiN 或聚合物改这三个值即可但要注意折射率对比度越低模式越容易泄漏到包层计算域要相应放大。2.3 边界条件PML 还是 Dirichlet边界处理是这套代码最容易翻车的地方。最简单的做法是 Dirichlet 边界把计算域外边界强制置零。这对强限制波导比如 SOI 脊形波导勉强能用但模式会被人为反射算出的 n_eff 偏高。更合理的做法是加一层 PML完美匹配层在计算域外围把坐标做复拉伸让出射波被吸收掉。FEM.m 如果只实现了 Dirichlet你会在结果里看到模场在边界处被硬生生截断。判断方法很简单把计算域放大 20%如果 n_eff 变化超过 1e-3说明边界反射严重需要换 PML 或者继续放大域。我一般会先用 Dirichlet 快速跑一遍看模式形状确认基模存在后再决定是否上 PML。3. 从 FEM.m 到模场图一次完整的脊形波导仿真流程3.1 参数初始化与几何建模拿到 FEM.m 后第一步不是直接 run而是把顶部的参数区读一遍。典型参数块长这样% 物理参数 lambda 1550e-9; % 工作波长单位米 k0 2*pi/lambda; % 真空波数 n_core 3.48; % 脊区折射率Si n_clad 1.44; % 包层折射率SiO2 n_sub 1.44; % 基底折射率 % 几何参数 W 500e-9; % 脊宽 H 220e-9; % 脊高 H_slab 100e-9; % 平板层厚度如有 Lx 3e-6; % 计算域宽 Ly 3e-6; % 计算域高 % 网格参数 hmax 50e-9; % 最大单元尺寸参数说明lambda 决定 k0直接影响特征值量级W 和 H 是脊形波导的核心几何单模条件通常要求 W 在 400–600 nm、H 在 200–300 nm 之间对 SOI 平台hmax 控制网格密度50 nm 是个折中值降到 20 nm 精度提升但内存翻倍。Lx 和 Ly 取 3 μm 对强限制波导够用弱限制要加到 6–8 μm。3.2 组装刚度矩阵与质量矩阵网格生成后核心工作是遍历所有单元计算局部矩阵并累加到全局稀疏矩阵。一阶三角形单元的局部刚度矩阵公式是K_local(i,j) (1/(4A)) * (b_ib_j c_ic_j)其中 A 是单元面积b、c 是形函数系数。质量矩阵更简单M_local(i,j) (A/12) * (1 δ_ij)代码实现时用稀疏三元组累加效率最高% 预分配三元组 nTriplets numElems * 9; I zeros(nTriplets,1); J zeros(nTriplets,1); VK zeros(nTriplets,1); VM zeros(nTriplets,1); idx 0; for e 1:numElems idx_local elemNodes(e,:); [Ke, Me] localMatrices(nodes(idx_local,:), n_elem(e), k0); for a 1:3 for b 1:3 idx idx 1; I(idx) idx_local(a); J(idx) idx_local(b); VK(idx) Ke(a,b); VM(idx) Me(a,b); end end end K sparse(I, J, VK, numNodes, numNodes); M sparse(I, J, VM, numNodes, numNodes);逻辑说明localMatrices 函数返回 3×3 的局部刚度矩阵和质量矩阵质量矩阵里已经乘了 n²。用三元组累加再 sparse 一次成型比循环里直接赋值快一个数量级。参数上n_elem(e) 就是 2.2 节分配的单元折射率k0 来自波长。3.3 求解广义特征值与提取有效折射率矩阵组装完后调用 eigs 求前几个最小特征值% 求解广义特征值问题 K*E beta^2*M*E numModes 4; sigma 0; % shift-invert求靠近0的特征值 [Evec, D] eigs(K, M, numModes, sigma); beta2 diag(D); beta sqrt(beta2); n_eff beta / k0; % 排序并输出 [n_eff_sorted, order] sort(real(n_eff), descend); fprintf(前 %d 个模式的有效折射率\n, numModes); for m 1:numModes fprintf( 模式 %d: n_eff %.6f\n, m, n_eff_sorted(m)); end参数说明sigma0 表示用 shift-invert 模式求最靠近零的特征值这对波导问题最有效因为基模的 β² 通常接近 k₀²n_core² 但略小。numModes 取 4 是为了同时看到基模和高阶模判断单模条件。n_eff 的实部就是模式有效折射率虚部如果非零说明有泄漏或损耗。常见做法是检查基模 n_eff 是否在 n_clad 和 n_core 之间且第二高阶模的 n_eff 是否低于 n_clad——低于就说明截止了。3.4 模场可视化与单模条件判断拿到特征向量后把它映射回节点上画出来% 绘制基模模场 E1 Evec(:, order(1)); trisurf(elemNodes, nodes(:,1)*1e9, nodes(:,2)*1e9, abs(E1), EdgeColor, none); view(2); axis equal tight; xlabel(x (nm)); ylabel(y (nm)); title(sprintf(基模 |E|, n_{eff} %.4f, n_eff_sorted(1))); colorbar;这段代码用 trisurf 直接画三角网格上的场分布view(2) 压成俯视图。判断单模的标准是基模能量集中在脊区高阶模要么截止要么被束缚得很弱。如果看到基模明显往基底泄漏说明 H 太小或者 n_clad 太高。我一般会把 W 从 400 nm 扫到 800 nm每次记录前两个模式的 n_eff画一条曲线看高阶模什么时候降到 n_clad 以下那个 W 就是单模截止宽度。4. 避坑与排查脊形波导 FEM 仿真里最容易翻车的五件事4.1 现象n_eff 算出来大于 n_core原因网格太粗或者 Dirichlet 边界把模式“挤”在了计算域内导致特征值虚高。另一个可能是折射率分配时把包层单元误判成了脊区。解决先把 hmax 减半重跑如果 n_eff 下降并收敛说明是网格问题。再检查折射率分配逻辑打印 n_elem 的直方图确认脊区单元数量与几何面积估算一致。正常情况下 n_eff 必须小于 n_core大于 n_clad。4.2 现象eigs 报错“矩阵维度不匹配”或返回空特征值原因K 和 M 的稀疏结构不一致通常是三元组累加时 I、J 索引越界或者 numNodes 和实际节点数对不上。解决在 sparse 之前加一句 assert(max(I) numNodes max(J) numNodes)。另外检查 elemNodes 的编号是否从 1 开始MATLAB 不支持 0 索引。如果 eigs 返回空把 sigma 改成 smallestabs 试试不同 MATLAB 版本对 shift-invert 的支持有差异。4.3 现象模场图看起来对称但 n_eff 对不上理论值原因标量 FEM 忽略了偏振相关的矢量效应。脊形波导的准 TE 和准 TM 模有效折射率差在 1e-3 量级标量法只能给出一个平均结果。解决如果要做偏振分析这套标量代码不够用需要上矢量 FEM 或者半矢量法。但作为快速估算和趋势判断标量结果够用。我一般会拿它和商用软件的标量求解器对比偏差在 0.5% 以内就认为代码没问题。4.4 现象计算时间随计算域增大急剧上升原因稀疏矩阵的维度是节点数计算域放大一倍节点数翻四倍eigs 的迭代成本更高。解决优先用 PML 而不是无限放大计算域。如果 FEM.m 没有 PML可以手动在计算域外围加一层吸收区把该区域的折射率设为复数虚部控制吸收强度。另一个技巧是先用粗网格定位模式再用细网格局部加密。4.5 现象高阶模和基模的 n_eff 非常接近分不清哪个是基模原因波导接近截止模式间的分离度变小。或者 eigs 求解的模式顺序没有按 n_eff 排序。解决永远对 n_eff 做降序排序后再取模式。判断基模的另一个依据是模场重叠积分基模在脊区的能量占比应该最高。如果两个模式的 n_eff 差小于 1e-4说明波导尺寸接近单模-多模边界实际器件里会有模式串扰风险需要调整 W 或 H。5. 进阶技巧用参数扫描把 FEM.m 变成设计工具单次跑一个结构只能看一个点真正有用的是把 FEM.m 包成参数扫描循环。我一般会写一个外层脚本扫脊宽 W 从 300 nm 到 800 nm步长 50 nm每次调用核心求解函数记录基模和二阶模的 n_eff。核心改动是把 FEM.m 里硬编码的参数改成函数入参function [n_eff, Evec, nodes, elemNodes] solveRidgeWaveguide(W, H, lambda, hmax) % 封装后的脊形波导求解函数 % 输入W 脊宽H 脊高lambda 波长hmax 最大网格尺寸 % 输出n_eff 有效折射率列表Evec 特征向量网格数据 k0 2*pi/lambda; n_core 3.48; n_clad 1.44; n_sub 1.44; % ... 几何建模、网格生成、矩阵组装、eigs 求解 ... % 返回排序后的 n_eff 和对应模场 end封装后扫描脚本就三行W_list 300e-9:50e-9:800e-9; for i 1:length(W_list) [n_eff, ~, ~, ~] solveRidgeWaveguide(W_list(i), 220e-9, 1550e-9, 50e-9); fprintf(W %d nm, n_eff(基模) %.4f, n_eff(二阶) %.4f\n, ... round(W_list(i)*1e9), n_eff(1), n_eff(2)); end跑完把结果画成曲线横轴 W纵轴 n_eff两条线分别对应基模和二阶模。二阶模曲线跌破 n_clad 的那个 W 就是单模截止宽度。这个扫描在普通笔记本上大概几分钟比开商用软件等 license 快得多。验证方法上我习惯拿两个点做交叉校验一是把扫描得到的单模截止宽度和文献里同平台的数据对比偏差超过 10% 就回去查网格和边界二是固定一个 W把 hmax 从 100 nm 降到 20 nm看 n_eff 是否收敛到同一值。收敛了才敢信扫描结果。有个血泪经验早期我扫参数时忘了每次清空上一次的稀疏矩阵变量MATLAB 工作区里残留的 K、M 被下一次循环覆盖了一半结果 n_eff 曲线出现莫名其妙的跳变。从那以后我每次调用求解函数前都强制 clear K M Evec或者干脆把求解逻辑全封在函数里靠函数工作区隔离。这个习惯帮我省了至少两次误判单模条件的后悔药。希望帮到你。本文还有配套的精品资源点击获取