ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

COMSOL弱形式求解三维光子晶体能带:从原理到实战

COMSOL弱形式求解三维光子晶体能带:从原理到实战 做三维光子晶体能带计算很多人第一反应是拿平面波展开法或者FDTD但如果你是COMSOL用户一定试过频率域模块里“硬算”布洛赫边界那套流程建好原胞、加周期条件、扫波矢、提取本征频率最后连成一条能带曲线。这套流程跑二维还好到了三维就开始吃力——网格数量大特征值极容易混入伪模扫几十个k点要跑半天更别说想换一种材料模型或引入增益、损耗这类非标准物理时内置物理接口给你留的“可调旋钮”往往不够用。我后来把整个计算改成COMSOL弱形式来做相当于在软件里自己搭了一个针对光子晶体本征问题的求解器折腾过一遍以后再回头看三维光子晶体的能带结构很多以前模糊的细节都清楚了。这篇文章把我当时的完整思路、推导过程、软件配置和踩坑记录都整理出来给同样准备啃这块硬骨头的朋友一个参考。无论你是刚入门COMSOL还是已经在拿现成模块跑光子晶体只要想把能带计算做得更稳、更可控这篇应该能帮你省下不少试错时间。1. 方案选型为什么是COMSOL加弱形式1.1 求解光子晶体能带的常规套路与痛点光子晶体能带结构本质上就是周期性介质结构里的色散关系本质是要解周期性介质中的麦克斯韦方程组找到各个波矢对应的本征频率。做这件事的方法不少常见的有平面波展开法、时域有限差分法以及COMSOL为代表的有限元方法。平面波展开法是最经典的做法把介电常数和场都展开成傅里叶级数求解代数本征方程。它在处理简单结构时速度很快但遇到高介电常数对比、形状复杂的原胞或者材料有频散甚至有损耗时展开项数和收敛性都会让人头疼。FDTD则是打一个宽频脉冲源通过傅里叶分析提取谐振频率思路直观但做本征值扫描时计算量巨大而且在三维结构里数值色散不容易控制。COMSOL这类有限元工具的好处是几何建模灵活、网格自适应成熟、边界条件也方便设置。但直接用电磁波频域接口Electromagnetic Waves, Frequency Domain算能带时有几个非常实际的问题光子晶体本征问题本质是矢量波动方程直接求解时会出现大量无物理意义的伪模。内置接口加了较多物理假设用户想自定义方程时界面会显得比较“重”。三维模型的自由度高、内存压力大扫k点时如果不做优化整个计算流程会异常漫长。特征值排序、模式识别、频率归一化这些后处理需要自己理清逻辑否则画出来的能带曲线一条线都对不上。我在做三维光子晶体时最开始也是用内置接口但后来遇到一个高介电对比算例怎么都去不掉伪模才下决心切到弱形式。1.2 弱形式带来的自由度弱形式的原因说来也简单内置物理接口已经替你把方程“包装好了”但包装好的东西必然牺牲一定的可调性。COMSOL的“弱形式偏微分方程”Weak Form PDE接口则相当于把一个方程从零开始交给你定义你写什么它就解什么。这样做有几个明显的优势。第一能够直接控制本征方程中的每一项。内置电磁接口通常以电场或者磁场为因变量背后默认了某个固定的变分形式。弱形式里你可以自己选择用什么因变量、加不加惩罚项、保留哪个二阶项甚至可以将特征值问题的形式改写成对求解器更友好的形态。第二能够方便地处理非标准物理。比如想研究非线性介质、增益介质、各项异性材料或者引入磁光效应内置接口的修改范围有限但弱形式里加一个源项、改一个材料张量都不是难事。我做三维光子晶体时顺便做过一个增益介质版本在那个算例里弱形式的灵活性体现得淋漓尽致。第三能够对布洛赫边界和波矢扫描做统一管理。把波矢分量写进全局参数然后通过参数化扫描批量完成整个布里渊区的计算整个过程可以形成一套很清晰的工作流写论文或做工程汇报时也容易复现。当然弱形式也有代价你需要自己理解方程否则出错了连排查的方向都没有。但恰恰是这个过程能让你对“光子晶体能带到底是怎么解出来的”有真正的掌握这也是我愿意花时间写这篇文章的原因。2. 理论基础从麦克斯韦方程组到弱形式特征值问题2.1 光子晶体里的布洛赫定理与不可约布里渊区周期性结构中的电磁波满足布洛赫定理这是能带计算的根基。简单说在一个介电常数满足周期性分布的结构中电磁场的模式可以写成E(r) u(r) · exp(ik · r)其中u(r)是与晶格周期相同的函数k是布洛赫波矢。这个形式可以这样理解电磁波有一个整体上的相位趋势exp(ik · r)但幅度受到周期性函数u(r)调制。正是这个u(r)的周期性让我们只需要在一个原胞内求解问题大大压缩计算规模。三维光子晶体的原胞由晶格基矢决定。以最简单且最常用来验证算法的简单立方晶格为例可以用a表示晶格常数倒格子基矢为b1 (2π/a, 0, 0)、b2 (0, 2π/a, 0)、b3 (0, 0, 2π/a)。在倒空间中第一布里渊区有若干个高对称点常见路径是Γ-X-M-R-Γ其中Γ点为(0, 0, 0)X点为(π/a, 0, 0)M点为(π/a, π/a, 0)R点为(π/a, π/a, π/a)实际计算时只要沿这些高对称点连成的路径采样就能得到完整的能带信息。需要注意的是波矢k的取值要在倒格子空间中处理而COMSOL几何建模时用的是实空间坐标两者之间的换算关系一定要理清。2.2 时谐麦克斯韦方程推导到弱形式假定研究的是线性、各向同性、无源介质且电磁场随时间作时谐振荡即E(r,t) E(r)exp(iωt)。从麦克斯韦方程组出发可以推导出关于电场的矢量波动方程∇ × (∇ × E(r)) - k₀² · εr(r) · E(r) 0这里的εr(r)是位置相关介电常数k₀ ω/c是真空波数c是真空光速。实际写代码时经常还会用相对磁导率μr不过对大多数光子晶体材料而言μr近似等于1后面公式里忽略也问题不大。要写成有限元能处理的弱形式做法是对上述方程乘上一个测试函数F并在原胞体积V内做体积分然后利用矢量恒等式对旋度项做一次分部积分。最终可以得到∫V (∇ × F)·(∇ × E) dV - k₀² ∫V εr F·E dV 边界项 0在周期边界条件下原胞相对面上的边界积分正好抵消边界项可以消去。这里得到的表达式就是COMSOL弱形式界面里要输入的“弱表达式”的数学基础。注意一个问题这个方程关于k₀²是本征值问题而且左边第一项含有旋度算子。如果我们直接照搬进COMSOL会发现这是一个矢量本征问题三个电场分量相互耦合接下来如何处理需要认真考虑。2.3 处理矢量场时的伪模问题与惩罚项三维光子晶体本征计算最大的坑是伪模。原因是有限元里如果用普通的拉格朗日节点基函数来离散电场矢量场可能无法保证数值解的散度约束。麦克斯韦方程本身要求无源区内∇·(εE) 0但离散后的数值空间里这个条件不自动满足就会出现一类数值污染模式它们不会在物理实验中出现却会混在特征值谱里干扰判断。最简单的抑制办法是加入一个惩罚项∫V β(∇·F)(∇·E) dVβ取一个较小的正值通常0.01到0.1量级作用是“惩罚”那些不满足散度条件的模式。也可以把它理解成在方程里加了一条弱约束让求解器在寻找本征模时更偏好满足无散条件的物理模。这个技巧在早期很多光子晶体计算文章里出现过如今在商业软件里用弱形式重新实现效果依然很好。如果用H场而不是E场来做因变量也能天然压制一部分伪模但在介质突变面上磁场的边界条件处理不如电场直观。实际项目里我更推荐用E场加惩罚项的组合后面小节会给出可直接粘贴到COMSOL里的写法。3. COMSOL建模实操从几何到弱形式方程3.1 确定三维原胞几何与材料参数为了让流程具体可复现这里就用最经典的一个三维光子晶体参数做例子简单立方晶格晶格常数a设为1在COMSOL几何里直接以归一化单位建原胞内放一个球形散射体。散射体介电常数取11.56常见于硅在近红外波段约3.4折射率的平方背景介质介电常数取1空气。用COMSOL建模时在三维组件里建立一个长方体边长设为1中心在原点或者让角点落在原点习惯不同而已注意后面边界设置别搞混就行。然后在原胞正中心加一个半径R 0.3的球体。这个半径的选取对应一个经典算例在空气背景中排布高介电球带隙宽度比较可观适合验证算法。在“材料”部分维护两种材料空气的介电常数设为1球的介电常数设为11.56。模型里建议开启“自动网格”之前先在心里过一遍球表面附近场变化最剧烈网格需要做局部加密所以后面对球面网格尺寸单独设置其余区域可以稍稀疏。这里还要定义若干全局参数。我的习惯是把归一化工作放到参数里完成而不是靠后处理临时算。需要维护的参数一次性列出来参数表参数名表达式说明a1晶格常数无量纲R0.3散射体半径eps_bg1背景介电常数eps_sc11.56散射体介电常数kx0布洛赫波矢x分量倒空间ky0布洛赫波矢y分量kz0布洛赫波矢z分量pen0.05散度惩罚系数其中kx、ky、kz是后面要扫描的变量它们的单位要特别注意。COMSOL内置周期条件里如果填的是“波矢分量”默认单位是rad/m但当前几何用的是归一化长度所以给出倒空间坐标时需要乘以2π才能在物理上对得上。更省心的做法是以倒格子坐标形式定义扫描变量然后在周期条件的波矢输入里写成“2pikx”后面扫描kx时其实就是在扫描以(2π/a)为单位的倒格矢。3.2 因变量与弱形式表达式的具体写法接下来是核心步骤。在“模型开发器”里添加一个“弱形式偏微分方程”接口类型选择因变量是三个电场分量Ex、Ey、Ez。之所以需要三个分量是因为方程是矢量方程需要把所有方向耦合都写进弱表达式里。COMSOL的弱形式PDE节点里有两个关键输入框“弱表达式”和“约束”。我们最主要的功夫都花在“弱表达式”里。先写出旋度算子的分量形式。对一个矢量A(Ax, Ay, Az)旋度是curl(A) (∂Az/∂y - ∂Ay/∂z, ∂Ax/∂z - ∂Az/∂x, ∂Ay/∂x - ∂Ax/∂y)弱形式需要的是“curl(E)·curl(F)”这种内积形式其中F是测试函数。由于F的旋度分量在形式上与E的分量表达式相似只是把场变量换成对应测试函数因此可以手工展开所有项。在COMSOL表达式里∂Ax/∂y这样的一阶偏导一般写成AxY或者d(Ax, y)不同版本语法略有差异建议以对应版本帮助文档为准。下面给出我常用的基于导函数据语法“d()”书写方式curl1 d(Ez, y) - d(Ey, z)curl2 d(Ex, z) - d(Ez, x)curl3 d(Ey, x) - d(Ex, y)同理测试函数F(Ex_test, Ey_test, Ez_test)的旋度写为tcurl1 d(test(Ez), y) - d(test(Ey), z)tcurl2 d(test(Ex), z) - d(test(Ez), x)tcurl3 d(test(Ey), x) - d(test(Ex), y)于是弱表达式第一部分就可以写成curl1 * tcurl1 curl2 * tcurl2 curl3 * tcurl3后面紧接着特征值项eigvarl * epsr * (test(Ex)*Ex test(Ey)*Ey test(Ez)*Ez)其中eigvarl在COMSOL特征值研究里代表要求解的特征值这里的λ实际上对应公式里的k₀²。epsr需要定义成空间坐标相关的介电常数最方便的方式是使用COMSOL的“变量”功能加上台阶函数判断或者直接用材料定义的介电常数变量如epsilon_r_iso。为避免歧义我习惯单独定义一个变量epsr表达为epsr eps_bg (eps_sc - eps_bg) * (xdest(x) ydest(y) z*dest(z) R^2)具体写法其实更推荐用阶跃函数can build from logical expression最后加上惩罚项pen * (d(Ex, x) d(Ey, y) d(Ez, z)) * (d(test(Ex), x) d(test(Ey), y) d(test(Ez), z))把三部分叠加起来整个弱表达式就是curl1 * tcurl1 curl2 * tcurl2 curl3 * tcurl3 - eigvarl * epsr * (test(Ex)*Ex test(Ey)*Ey test(Ez)*Ez) pen * divE * div_testE在特征值研究设置中把“特征值名称”设为eigvarl。由于我们关心的是物理上能传播的实模式搜索区间放在实轴附近通常设置10到20个特征值目标值可以取一个比第一个带边频率略大的数。这里还有一个细节COMSOL的弱形式PDE接口默认需要设置因变量的单元阶次。三维矢量电磁问题建议使用“三次”还是“二次”从精度和自由度平衡考虑我一般用二次。这个选择在低阶模上精度已经不错又不至于让网格数量失控。如果你想验证网格收敛性可以在同一k点下用二次和三次各算一次比较几个特征值的相对误差——这是检查离散质量的最直接办法。3.3 布洛赫周期边界条件的设置在弱形式PDE接口下还需要给原胞的相对面配上周期条件。COMSOL的“周期条件”节点可以选择Floquet周期类型并指定三个方向上的波矢分量。以简单立方原胞为例需要设x0与x1两个面配对y、z同理。注意坐标面的选择要与几何建立时一致如果几何是从-0.5到0.5建的边界就相应设为-0.5和0.5。在周期条件节点的“Floquet波矢”设置里三个分量直接引用前面定义得参数k_Bloch_x 2pikxk_Bloch_y 2pikyk_Bloch_z 2pikz这里的kx、ky、kz是我们后续扫描时使用的倒格子坐标。这样设置的好处是扫描点直接写k点的“倒格矢坐标”比如X点就是0.5,0,0不用每次都手算rad/m。需要提醒周期条件只能用在相互对应且网格划分一致的面上。COMSOL在创建周期配对时通常会对网格做一致性处理但如果之后你手动“编辑”过边界网格有可能破坏配对关系导致求解器报“周期性约束失效”之类的错误。我建议在网格序列完成后再专门检查一遍周期配对映射确保没有例外边界。还有一个细节是三维情况下周期面的角边三条棱的交线也需要处理。简单做法是把棱也纳入周期约束COMSOL在配对三个方向的周期面时通常会自动处理好棱的约束继承。但个别版本可能出现棱上的约束冗余或冲突表现为求解时自由度不足。如果遇到这种情况可以在周期面设置里把“继承约束”选项打开或者手动增加棱上的周期约束。4. 能带计算与后处理从特征值到能带曲线4.1 布里渊区高对称点与k点路径采样理论上一套能带图只需要沿不可约布里渊区边界扫描波矢即可。对简单立方晶格不可约布里渊区的高对称点路径为Γ-X-M-R-Γ。在COMSOL里做扫描方式是定义一个“参数化扫描”把扫描变量设为kx、ky、kz。但直接同时扫三个变量全部组合会爆炸。最聪明的办法是定义一组辅助参数每行对应路径上某个点。比如定义扫描步数N 10用“参数化曲线”逻辑将kx设为路径坐标t的分段线性函数。一种常用做法是在“全局定义”中定义一个路径参数s范围0到4依次对应Γ→X、X→M、M→R、R→Γ四段然后通过if语句或者插值函数分别映射到kx、ky、kz。更干净的做法是在COMSOL里用“插值”函数定义曲线第一个列是s后面三列分别是kx、ky、kz采样点即路径上的各个位置。我个人更推荐后一种方法把路径点数据提前算好写成CSV文件然后在COMSOL里用“插值函数”读入扫描变量设为s。这样做的好处是路径规划、密度控制都在外部做改起来也方便。例如想要每段10个点则可以生成这样一张表skxkykz0.00.0000.0000.0000.10.0500.0000.000............1.00.5000.0000.0001.10.5000.0500.000............扫描时注意特征值求解通常会为每个参数步生成一组特征值但不同参数步之间的模式编号是独立的不能直接按特征值编号连线。这一点对后处理制图非常关键一会儿单独说。4.2 特征值求解器配置与频率归一化研究类型选择“特征值”。COMSOL的特征值求解器默认可能求的是从小到大排列的本征值但具体排序方式和特征值的相对位置在三维模型里往往会有交叉和简并。如果想稳定地获取前若干个低频模式需要在求解器的设置中指定“所需特征值数量”并设置好搜索区域比如“搜索特征值附近的点1e6”单位为rad²/s²这个取决于特征值的含义。由于我们弱表达式里特征值eigvarl对应的是k₀²物理上我们需要换算回频率f sqrt(eigvarl) * c / (2π)在COMSOL后处理中可以直接在结果表达式里写f_norm a * sqrt(eigvarl) / (2π)这个归一化频率a/λ fa/c是光子晶体能带图中常用横轴。如果你把晶格常数a设成了1表达式就更简单。注意能带图纵轴一般用“归一化频率fa/c”如果直接用sqrt(eigvarl)/(2π)由于k₀ ω/c 2πf/c得到的是a/λ。两者数值相等是因为a1时fa/c a/λ。这里单位比较绕建议在正式画图前先用解析解验证一个简单算例比如均匀介质里的平面波色散确保换算没有差一两个数量级。具体验证方法也不复杂把介电球设成与背景一致也就是全空间均匀介质然后算一个k点比如Γ点附近的特征值。理论值应满足f c·|k|/(2π√ε)如果计算的频率和理论值一致说明特征值到频率的换算是正确的。这一步能极大减少后处理的胡思乱想。4.3 能带曲线绘制与模式识别将参数化扫描计算完成后处理特征值数据时有几个经典坑。第一个坑是不同波矢间的模式配对。COMSOL参数化扫描得到的结果会以“参数s、特征值编号、特征值大小”的形式存放。直接按特征值编号画线你会看到曲线像一团乱麻一样来回穿越因为某些模式在波矢变化过程中会交换顺序或者被识别为复特征值。解决思路有两种。一种是对每个波矢得到的特征值按大小重新排序然后按“最接近上一个波矢特征值”的原则做最近邻匹配。这个思路实现并不复杂可以在COMSOL里写个小脚本也可以导出数据到MATLAB或Python处理。另一种是检查各特征值的场模式图手动确认哪些是目标模式、哪些是伪模。论文撰写阶段我通常两种方法结合使用。第二个坑是简并。三维光子晶体每个能带在高对称点附近往往存在简并态例如X点的某些能带会成对出现。如果只看特征值数值可能以为模式丢失了其实只是两三个特征值几乎重合。处理时要把简并度考虑进去能带曲线每个k点可以允许重频出现。第三个坑是模式筛选。加了惩罚项以后虽然伪模数量会大幅减少但不代表完全消失。一个快速筛选方法是看特征值虚部的大小。物理上无损耗介质中频率应为实数若某个模式虚部显著偏离零则优先怀疑是伪模。实际COMSOL解得的特征值通常会带有很小的数值噪声虚部但只要虚部相对于实部小几个数量级就可以接受。如果虚部过大比如超过实部的1%就要检查网格和惩罚项系数。4.4 补充从能带到器件参数虽然这篇文章主要讲能带但很多朋友做光子晶体最终是为了指导器件设计。比如做高Q谐振腔、波导或者BAW谐振器时能带结构能给出模式的截止频率、群速度和有效折射率等参数。在COMSOL里拿到特征值后群速度可以用能带曲线对k求导得到处理时要注意多能带交叉有效折射率也可以用特征值计算n_eff k₀/|k|。如果材料里引入了损耗特征值会是复数实部决定谐振频率虚部则对应损耗或增益这时你能看到类似于“有效折射率虚部”的量。这个量在光子晶体光波导和谐振器设计中非常关键损耗估算和模式泄漏分析都靠它。5. 常见问题与排查技巧实录5.1 特征值偏大或怎么扫都出现一堆零频模这种情况最多的原因是单位混乱。比如几何以纳米建、光速以m/s代入、晶格常数又没归一化而特征值eigvarl对应k₀²最后换算频率时差了10的若干次方。建议一开始就把几何尺度归一化到晶格常数a波长也以a为单位换算就能规避大多数单位问题。零频模大量出现则一般是散度伪模。如果加了惩罚项后仍然顽固存在尝试把pen值从0.01加大到0.2或者检查周期边界是否设置完整。还有一种可能是你忘记把因变量的单元阶次设为至少“二次”用线性元解三维矢量电磁问题时零能模几乎必然占据特征谱前部。5.2 高对称点的简并模算不出来或者错位高对称点处的简并最容易出问题因为求解器默认的搜索窗口可能把近简并模式漏掉或者合并。遇到这种情况可以在该k点单独增加“所需特征值数量”并缩小搜索区间的目标值。同时要注意高对称点附近的波矢边界条件相位在某些面上可能产生恒定偏移这不会影响本征值但会给解的结构带来一个任意相位因子。在后期画模式场图时如果发现场分布带“整体旋转”这是由布洛赫定理的规范自由度引起的合法现象。5.3 参数化扫描中途报错或结果不连续扫描时最常见的报错是“没有找到特征值请尝试调整搜索区间”。原因通常是在某个k点感兴趣的特征频率已经跑到当前搜索区间之外。解决办法不是调小搜索区间而是先在一个代表性的低对称点做单点计算确实了解频率范围再设置合理的搜索窗口。结果不连续还有个容易忽略的原因网格在每个参数步会重新划分。如果网格序列前没有做“固定网格”设定默认可能依据几何变化自适应调整导致特征值的微小差异被放大成可见跳变。三维原胞几何一般不会随参数变化所以扫描前务必把网格设置里的“自适应”关掉保持各k点使用同一套网格。5.4 内存溢出与计算时间过长三维光子晶体计算对内存要求很高。一个原胞如果网格剖得较细自由度轻松几十万甚至上百万。弱形式PDE的特征值求解器默认使用直接求解器比如MUMPS或PARDISO内存消耗非常大。这里有几个实用建议先用粗网格跑通整个流程确认能带趋势合理后再加密网格。连趋势都还没看到就上细网格浪费时间也容易打击信心。开启“参数化扫描”前先测试单点求解大概需要多少内存和时长评估总耗时。如果只是查看前几个低频带可以设置求解器只计算最靠近目标值的前6或前10阶特征值不必一次性算几十个。适当使用局部网格加密而不是全局加密。散射体球面附近、介质界面附近需要加密远离界面的方角区域对低频带的影响较小。5.5 一个隐藏很深的坑几何棱边上的周期约束三维原胞有12条棱、8个顶点。如果周期条件只配对了6个面而没处理棱求解器会去解一个自由度约束不完整的模型。表面上能跑通但会出现某些模式严重依赖网格甚至不收敛的结果。解决方案是在三个方向的“周期条件”节点中明确对“面”设置的同时也检查棱上的约束COMSOL通常提供“在边上”的选项将其设为“周期条件”即可。如果界面找不到这个选项可以通过在棱上再增加一个周期性点约束实现但这会比较繁琐建议升级到较新版本再处理。6. 一点心得体会三维光子晶体能带计算这件事网上教程不少但真正把方程推清楚再把弱形式写进COMSOL里的完整案例并不多。我最初也是看官方文档加上自己摸索中间因为伪模问题纠结了将近两周后来才意识到弱形式里加一个惩罚项就能解决那个瞬间确实有豁然开朗的感觉。如果你打算复现这篇文章里的流程我给一个顺序建议先从一维或二维周期结构开始把弱形式和布洛赫边界跑通确认特征值换算没有问题再来挑战三维。三维的难度不在原理而在计算规模和调试成本。网格先粗后细k点先少后多等曲线走势合理后再逐步加密加细这会让你少走很多弯路。最后留一个我实际在用的小技巧把能带计算的主流程做成一个COMSOL“App”在界面里留下“晶格常数”“介电常数”“半径”和“k点路径参数”四个输入框团队成员想快速评估新结构时直接填参数就能出图。弱形式方案一旦跑通它的迁移成本很低换结构、换材料、换晶型都只是几何重建和参数修改的问题。希望这篇文章能帮你把第一步迈得顺一些。
返回列表