光子晶体连续域束缚态远场偏振与空间能带值计算指南

发布时间:2026/10/25 7:29:26
光子晶体连续域束缚态远场偏振与空间能带值计算指南

简介:围绕光子晶体连续域束缚态(BIC)的远场偏振计算,这份资料包面向从事COMSOL光学仿真、拓扑光子学及微纳光子器件研究的高年级本科生、研究生和科研人员,内容涵盖BIC理论基础、k空间能带与Q值分析、远场偏振特性计算等,整合了仿真模型思路与Matlab脚本,可直接作为课题入门和复现计算的参考。包内共15个文件,包括6篇docx研究文档(理论梳理与计算说明)、4个html过程记录(仿真分析步骤与结果讨论)、4张jpg结果图像(能带/偏振图等)及1份txt说明文件,压缩包整体大小2.83MB,结构清晰,便于按需查阅。目前已有42人学习浏览,借助其中的文档可掌握BIC参数扫描、Q值提取与远场偏振投影方法,Matlab脚本提供能带绘制和偏振分析的具体代码,图像则直观展示计算结果,适合正在搭建BIC仿真框架、需要对比验证远场偏振计算路径的读者。

1. 光子晶体连续域束缚态:这个“不辐射”的模式为什么值得算远场偏振

“光子晶体连续域束缚态远场偏振计算含空间能带值”这串关键词,拆开看就是三件事:设计一个周期性结构,让某个模式泡在连续辐射谱里却不向外泄漏;在离这个模式很近的动量点上,算出它的远场偏振方向和旋转规律;再把结果放回能带图上,确认它落在空间能带值的哪个位置。做光芯片激光选模、超表面窄带滤波和二次谐波增强的人会被这串问题卡住:BIC 的远场辐射严格为零,直接在奇点上算偏振只会得到一堆数值噪声,能带图也会失真。这里直接给出一条可复现的路线:先用平面波展开法定位候选能带,再用 Stokes 参数做远场偏振提取,最后绕奇点走一圈验证拓扑荷。

2. 能带计算路线:从结构建模到 BIC 候选模式的完整流程

2.1 为什么选 PWE 找能带、选 FDTD 算远场:两条路线各管一段

光子晶体能带计算最常见的两套工具是平面波展开法(PWE,MPB 就是代表实现)和时域有限差分法(FDTD)。PWE 在周期性结构里求解本征频率非常快,一次性把整个布里渊区路径上的能带算完,特别适合先摸清模式分布;但它默认结构无损、周期无限,拿不到模式泄漏率,自然也算不出 Q 值。FDTD 能直接激励结构再测衰减,Q 值给得很实,但扫一个 k 点就要一个完整仿真,把整条能带扫出来成本太高。所以我的常规做法是两段式:用 PWE 圈出疑似 BIC 的能带区间,再用 FDTD 在那个 k 点附近做衰减测量,验证 Q 值是否随分辨率增加而发散。

物理模型也要先定住。这里采用二维方形晶格介质柱结构,晶格常数设为 a,圆柱半径 r,折射率 n。取二维模型而不是三维板,是为了先把 xy 平面内的辐射通道算清楚,避开 z 方向 slab 模式带来的额外泄漏项。对这个模型,BIC 的判据很直接:某个布洛赫模式的频率落在背景模式的连续谱区间内,但它的对称性与所有可辐射通道不匹配,于是能量只被局域在结构里,无法耦合到远场。

2.2 最小建模脚本:方形晶格介质柱算到 BIC 候选点

用一个可跑的 MPB Python 脚本把结构建起来。这里以 meep.mpb 的 Python 接口为例,不同小版本的参数名略有差异,但主体流程一致。

import meep as mp from meep import mpb resolution = 32 # 网格分辨率,单位是每个晶格常数内的采样点数 r = 0.2 # 介质柱半径,单位是晶格常数 a n_si = 3.5 # 柱体折射率,硅在近红外波段约 3.5 geometry_lattice = mp.Lattice( basis1=mp.Vector3(1.0, 0.0), basis2=mp.Vector3(0.0, 1.0) ) geometry = [ mp.Cylinder( radius=r, height=mp.inf, # 二维模型认为柱体沿 z 向无限长 material=mp.Medium(index=n_si), axis=mp.Vector3(0, 0, 1) ) ] ms = mpb.ModeSolver( geometry=geometry, geometry_lattice=geometry_lattice, resolution=resolution, num_bands=6 ) ms.run_tm() # 计算 TM 极化(电场沿 z 方向)的能带

这里有两个参数直接影响后续找 BIC 的难度。第一个是resolution,建议从 16 开始,一旦看到某个能带交叉点疑似出现“嵌入连续谱”的特征,再提到 64 复核 Q 值趋势;网格不够时,BIC 会被数值泄漏伪装成普通泄漏模。第二个是r,半径 0.2 只是一个起点,不同半径会移动能带位置和对称性匹配条件,后面做参数扫描时它是最重要的变量。run_tm()只算 TM 模,是因为介质柱结构里 BIC 通常更容易出现在 TM 能带的交叉点附近;TE 模也可以算,但建议分开跑,避免简并模式混在一起难判断。

2.3 从能带图看 Q 值:连续域里“不泄漏”的模式长什么样

MPB 输出的是实频率,不能直接读出 Q 值。判断一个点是 BIC 候选,先看它的频率是否落在连续谱区间内,再看它能否辐射。能带图上最直观的信号是:某条能带的频率穿过其他模式形成的连续谱带,但它所在的分支没有表现出明显的带隙或反交叉,颜色/对称性标记与相邻分支不同。这时候才值得对它做 FDTD 复核。

复核 Q 值的做法是在结构中心放一个窄带偶极子源,频率对准候选模式的频点,然后在结构内放一个探针记录电场衰减曲线。衰减满足 |E(t)| ∝ exp(-ωt/2Q),对包络做对数线性拟合就能得到 Q。真正的 BIC 在数值上表现为 Q 随网格分辨率持续上升,没有收敛上限的趋势;如果 Q 稳定在某个有限值,说明它只是一个高 Q 准 BIC,而不是严格 BIC。以下表格是我用来区分两者的快速对照。

特征准 BIC严格 BIC
Q 随分辨率变化先上升后收敛持续上升,无上限
远场偏振幅值有限值但很小严格趋近于零
绕奇点拓扑荷整数但可能混入杂散相位清晰的整数
对称性要求弱对称破缺即可需要严格匹配

这一步容易忽略的是对称性条件。BIC 之所以不辐射,本质上是模式的远场辐射通道因对称性被禁止,网格一旦把结构的旋转对称性破坏,这条禁令就失效了。所以建模时要把介质柱的中心放在网格元胞的对称中心上,还要保证圆柱的圆形轮廓不被矩形网格过度“切”出棱角,后面第五章会专门展开这个坑。

3. 远场偏振计算:用 Stokes 参数从复电场里提取偏振态

3.1 远场偏振为什么不能直接看近场:横波条件先满足

近场里的电场分量和远场传播方向并不垂直,这是计算偏振时最容易犯的错误。结构表面的近场电场里有很强的 Ez 分量,而远场是横波,电场必须垂直于传播方向。直接拿近场复矢量去算偏振,得到的结果和远场实际辐射偏振完全对不上。更麻烦的是 BIC 附近的近场振幅非常大,因为它把能量紧紧锁在结构里;而同一模式的远场振幅趋近于零。用幅度大的近场去推幅度小的远场,数值噪声会彻底淹没真实偏振信息。

正确做法是先选定远场观察方向,单位波矢记为 k̂。把 k̂ 与全局坐标的 ẑ 做叉积,得到投影平面的一组正交基 e1、e2,再把复杂电场矢量投影到这两个基上。这一步完成后,剩下的就是标准偏振分析。实际项目里,BIC 的远场偏振通常是在泄漏辐射显微镜里测的,测量方向正好对应布里渊区里的某个动量方向;数值计算时,我们就在该动量方向构造 k̂。

3.2 用 Stokes 参数做偏振提取:一个可以直接抄的函数

Stokes 参数的优点是它完整描述偏振态,包括强度、线偏振方位角和椭圆率,而且不需要假设电场是线偏振的。这里给出一个已经封装好的函数,输入某个 k 方向的复电场矢量,返回 Stokes 分量和偏振方位角。

import numpy as np def far_field_stokes(E_cplx, kvec): # E_cplx: 长度为 3 的复电场矢量 [Ex, Ey, Ez] # kvec: 长度为 3 的实数波矢,无需归一化 E = np.asarray(E_cplx, dtype=complex) k = np.asarray(kvec, dtype=float) k_hat = k / np.linalg.norm(k) # 构造与传播方向正交的两个基矢量 e1, e2 z_hat = np.array([0.0, 0.0, 1.0]) e1 = np.cross(k_hat, z_hat) if np.linalg.norm(e1) < 1e-12: # 当 k 与 z 轴平行时,退化为固定取 x 方向 e1 = np.array([1.0, 0.0, 0.0]) e1 = e1 / np.linalg.norm(e1) e2 = np.cross(k_hat, e1) # 投影到横向平面 E1 = np.dot(E, e1) E2 = np.dot(E, e2) # Stokes 参数 S0 = np.abs(E1)**2 + np.abs(E2)**2 S1 = np.abs(E1)**2 - np.abs(E2)**2 S2 = 2.0 * np.real(E1 * np.conj(E2)) S3 = -2.0 * np.imag(E1 * np.conj(E2)) # 偏振方位角,范围在 0 到 pi 之间 psi = 0.5 * np.arctan2(S2, S1) return S0, S1, S2, S3, psi

调用这个函数之前,要确认 E_cplx 来自同一个时间切片的复场,相位关系不能乱。S0 表示总强度,S1 和 S2 决定线偏振方向,S3 表示圆偏振分量。BIC 点附近 S0 趋近于零,此时 psi 会因为数值噪声到处乱跳,这不是程序 bug,而是物理上该点没有远场辐射、偏振本来就没有定义。所以偏振计算要刻意避开严格 BIC 点,取它周围一圈动量点来计算。

3.3 从近场到远场:傅里叶变换与相位归一化的两个参数

远场复电场可以从 FDTD 的近场监测面做空间傅里叶变换得到。近场平面上每个点的复电场 E(x, y) 做一个二维 FFT,得到的频域分量就对应不同远场方向的幅值和相位。这一步有两个参数直接决定结果质量:近场监测面的宽度必须是整数个晶格周期,否则 FFT 会引入 sinc 振铃,远场角度谱上出现一堆伪峰;时间窗口长度要覆盖模式衰减到足够小,否则频率分辨率不够,BIC 附近极窄的偏振变化会被抹掉。

def near_to_far_spectrum(E_near): # E_near: 二维复数数组,形状为 (ny, nx) # 先做空间 FFT,再平移到低频居中 E_far = np.fft.fftshift(np.fft.fft2(E_near)) # 返回的每个元素对应一个 (kx, ky) 采样点 return E_far

FFT 输出的索引坐标与动量 (kx, ky) 的对应关系取决于采样网格间距。实空间采样点间距 dx 决定动量空间总范围,采样点数量决定动量分辨率。用这个结果作为far_field_stokes的输入时,要注意 E_near 必须包含完整的相位信息,不能用强度值替代。取相位时,所有监测点要从同一个时间参考点导出,通常先让结构响应进入稳态再记录一个时间快照。

提示:离 BIC 太近时,S0 会被数值噪声主导。实际计算中我一般取布里渊区半径的 1%~5% 作为偏移量,并在圆上取 8~12 个点,这样既能看到完整的偏振旋转,又不会踩进零辐射区。

4. 空间能带值提取:把能带曲面、BIC 位置与偏振分布对应起来

4.1 把能带铺成空间曲面:空间能带值指的是哪一组数据

“空间能带值”这个说法在光子晶体语境里容易产生歧义。它不是指实空间坐标上每个点的能带频率,那是声子晶体局域态密度的处理方式;在光子晶体 BIC 计算里,它通常指动量空间里的能带取值:把第一布里渊区当作坐标平面,每个点 (kx, ky) 对应某个模式的一个本征频率,把这一组频率铺成曲面,就是空间能带值。BIC 就藏在这张曲面上,位置往往在能带交叉形成的锥形点附近。

构建这张曲面时,不要只沿 Γ-X-M 路径算一维能带,那样会漏掉 BIC 所在的方向。正确做法是在二维动量网格上做扫描,kx 和 ky 都取第一布里渊区一半的范围,网格数从 21×21 起步。BIC 本身是一个零维奇点,网格太粗会直接把它跳过去,所以第一轮粗扫只是定位可疑区域,第二轮要在可疑区域附近做局部加密扫描。

4.2 从这里判断 BIC 位置:能带转折点与 Q 值不发散的模式

把能带曲面画出来后,BIC 候选点有几个可辨识的特征:频率等高线围绕某个点出现涡旋状分布;能带曲面在该点附近呈现尖锐的漏斗形状;沿任意方向穿过该点,频率连续但模式的远场辐射突然消失。下面这段代码把从求解器导出的频率表填充成能带曲面,再做可视化。

import numpy as np import matplotlib.pyplot as plt def build_band_surface(kx_grid, ky_grid, band_index, freq_table): # kx_grid, ky_grid: 由 np.meshgrid 生成的动量坐标网格 # freq_table: 求解器导出的数组,形状为 (n_k, n_band) # band_index: 要绘制的能带序号,从 0 开始计数 surface = np.zeros_like(kx_grid) nky, nkx = kx_grid.shape for i in range(nky): for j in range(nkx): k_idx = i * nkx + j surface[i, j] = freq_table[k_idx, band_index] return surface # 示例:在 21x21 网格上计算第三条能带 kx = np.linspace(-0.5, 0.5, 21) ky = np.linspace(-0.5, 0.5, 21) KX, KY = np.meshgrid(kx, ky) # freq_table 由能带求解器输出,每个 k 点取若干条能带频率 # surface = build_band_surface(KX, KY, band_index=2, freq_table=freq_table) plt.contourf(KX, KY, surface, levels=40, cmap="viridis") plt.colorbar(label="normalized frequency") plt.xlabel(r"$k_x$ (units of $2\pi/a$)") plt.ylabel(r"$k_y$ (units of $2\pi/a$)")

注意band_index的取值要和第二、三章的能带顺序保持一致。TM 模式下第三条能带通常是最容易出 BIC 的位置,因为前两条能带往往落在空气光锥之下,辐射通道本身就不存在。判断曲面上的漏斗点是不是真 BIC,还要回到 2.3 节的 Q 值复核,单靠频率曲面只能确定“能带在这里发生了交叉”,不能确定“交叉点不辐射”。

4.3 热图辅助:把偏振方位角叠到能带曲面上

空间能带值除了频率,还可以把远场偏振方位角作为第二层信息叠加上去。做法是在能带曲面的等高线图上,把每个 k 点的偏振方位角画成短线;短线方向代表该动量的线偏振方向,长度代表 S0 的相对强度。BIC 附近会看到清晰的旋涡状短线分布:绕奇点走一圈,短线方向旋转 2π 的整数倍。这个旋涡结构直接对应 BIC 的拓扑荷,比单纯看能带交叉更可靠。

叠加图还能帮你判断 BIC 的对称性类型。如果旋涡旋转方向相反,说明拓扑荷符号不同,对应的偏振构型也不同。这类信息在做超表面偏振调控时非常关键:目标偏振转换器件的功能,本质上就是让远场偏振沿特定路径绕 BIC 演化。绘图时要注意角度显示的范围,方位角是 0 到 π 的周期量,直接用原始值画会看到剧烈跳变,建议先对角度做 unwrap 处理,再映射到颜色或短线方向。

5. 常见问题排查:BIC 偏振与能带计算最容易翻车的四个坑

5.1 BIC 点上算偏振,结果全是噪声

现象:把 k 点正好设在 BIC 位置,调用far_field_stokes后 S1、S2 在每次运行之间完全不一致,方位角随机跳动。

原因:BIC 的远场辐射严格为零,求解器算出来的只是浮点噪声。Stokes 参数的 S1、S2 都由远场复电场乘积构成,真实信号趋近零时,噪声就成了主导项。

解决:永远不要在严格奇点上算偏振。绕 BIC 取一个半径为 Δk 的小圆,Δk 取布里渊区半径的 1% 到 5%,圆上相邻点之间夹角均匀。设定几个不同半径各算一圈,如果方位角旋转角一致,说明结果收敛;如果旋转角随半径变化,就要加密动量网格,可能是附近还有其他奇点干扰。

5.2 能带曲面出现多余的折叠分支

现象:空间能带曲面图上有大量重复的等高线条纹,看起来像是多了一整条能带。

原因:扫描动量网格时没有使用不可约布里渊区,而是把整个方形布里渊区扫了一遍。晶格对称性导致某些能带在多个区域内重复出现,叠加在一起就成了伪造的多分支结构。

解决:先确认结构属于哪个点群,方形晶格通常取不可约布里渊区的三角区域扫描即可。如果做全布里渊区扫描,后处理时按点群对称性把等效点合并,或者只在等高线图里保留频率最低的那条分支。这个坑在第四章代码里特别明显,我当时第一次画出“八爪鱼”状的曲面,排查了半天才发现是扫描范围选错了。

5.3 Q 值上不去,总是封顶在某个有限值

现象:FDTD 算出的 Q 值随分辨率从 16 升到 64,涨到几千后就不动了,再怎么加密网格也上不去。

原因:结构本身是准 BIC,不是严格 BIC;或者介质柱的圆形边界被直角网格离散后引入了非物理的对称性破缺。网格一粗,圆柱轮廓变成锯齿状,旋转对称性被破坏,本应禁止的辐射通道被重新打开。

解决:先检查结构是否满足严格对称条件。圆柱中心要落在网格元胞中心,半径在网格上的离散误差用亚像素平滑处理,MEEP 里可以开启 subpixel 默认平均。如果已经做了这些,Q 值还封顶,说明这个几何参数点本身就不是 BIC 条件,沿能带方向扫几个相邻参数,让 r 或折射率微调,再重新看 Q 值趋势。

5.4 近场到远场频谱出现非物理的旁瓣

现象:near_to_far_spectrum得到的远场角度谱里,目标方向附近出现一串规则的伪峰,间距相等。

原因:近场监测面宽度不是晶格周期的整数倍。监测面被边界截断后,FFT 把边界的跳变卷进频谱,产生 sinc 振铃。伪峰间距恰好对应截断窗口的尺寸。

解决:把监测面宽度精确设置为整数个周期。如果结构本身有缺陷或需要局部激励,先在监测面加一个窗函数做平滑,再用 FFT。加窗会牺牲一点角分辨率,但能换来干净的频谱。防止这类问题的习惯做法是在设计仿真时就记录监测面的起点坐标,保证它正好落在周期边界上。

提示:以上四个坑都会在“先算能带、再算远场”的流程里暴露。每改一个参数,先记录网格分辨率和 k 点偏移量,不然翻车后根本不知道是几何参数变了还是数值设置变了。

6. 两个验证技巧:对称性自检与绕 BIC 追踪偏振旋转

6.1 用对称性做第一次自检

拿到能带曲面和远场偏振结果后,先别急着往下做器件设计。跑一个对称性检查:把整个结构旋转 90 度,重新算一遍能带和 Stoke,验证 KX、KY 坐标相应旋转后频率没有变化。如果旋转后的偏振方位角没有跟着旋转,说明投影坐标系定义有问题,要回 3.2 节检查 e1、e2 的构造——这类问题不是物理错了,是坐标系没对齐。这个检查只需要多跑一次能带扫描,成本很低,却能把一半的坐标系 bug 挡在门外。

6.2 绕 BIC 走一圈追踪偏振方向,验证拓扑荷

绕 BIC 绕一圈看偏振方位角的总旋转量,这是 BIC 所有数值结果里最有说服力的一致性验证。拓扑荷由总旋转角除以 2π 得到,理论上必须是整数。如果算出 0.5 或 1.7 这种值,说明网格、动量偏移或相位归一化至少有一项出了问题。下面这个函数直接用小圆采样计算绕数。

def winding_number(k_center, dk, n_points=12, get_field): # k_center: BIC 所在 (kx, ky) # dk: 绕圈半径 # get_field: 输入 (kx, ky),返回该方向远场复电场 psi_list = [] for idx in range(n_points): angle = 2.0 * np.pi * idx / n_points kx = k_center[0] + dk * np.cos(angle) ky = k_center[1] + dk * np.sin(angle) E = get_field(kx, ky) S0, S1, S2, S3, psi = far_field_stokes(E, [kx, ky, 0.0]) psi_list.append(psi) # unwrap 防止方位角在 ±pi/2 处跳变 unwrapped = np.unwrap(2.0 * np.array(psi_list)) total = (unwrapped[-1] - unwrapped[0]) / 2.0 return int(round(total / (2.0 * np.pi)))

这个小函数现在是我所有 BIC 计算的标配。每次换结构参数、换网格密度,我都会先用它验证一遍绕数,再去做投影器件设计。如果绕数不是整数,我会回到第一章的建模流程重新检查,不会继续往下算。代价只是多花十分钟,但能省下一个晚上重算的时间——这是我在这类计算里最实在的血泪经验,希望帮到你。

本文还有配套的精品资源,点击获取