☰
一维光子晶体正入射光束位移:从对称性到数值复现
2026/10/3 14:20:13 网站建设 项目流程

最近我在调试一维光子晶体的反射相位谱时,遇到一件让我重新审视“常识”的事:我把入射角从5度一路往0度收,按道理光束垂直打上去就该垂直弹回来,但在带隙边缘的波长附近,反射光束的重心居然横向漂了将近十几个波长。垂直入射却不垂直反射?这听起来像悖论,但在光子晶体领域,“正入射光束位移”确实是真实存在的现象,而且它背后牵涉到对称性、布洛赫模式和等频面倾斜这些硬核概念。

这篇文章我想把这套东西从头捋一遍:先讲清楚正入射位移为什么“不应该存在”,再讲光子晶体怎么把它“变出来”,最后给出一套完整的一维复现流程,包括传输矩阵法、高斯光束角谱分解和位移计算代码。无论你是刚接触计算光学的研究生,还是正在复现文献里巨大位移结果的工程师,希望这条“从理论到代码”的路径能帮你少走弯路。

1. 正入射位移真的存在吗?先从对称性说起

1.1 光束位移是什么:从Goos-Hänchen效应讲起

光束位移这个现象,最早被系统研究是从德国物理学家Goos和Hänchen在1947年的实验开始的。当一束光以大于临界角的入射角打到界面上发生全反射时,反射光束并不在几何光学预测的那个点离开界面,而是沿界面方向平移一段距离。这段位移通常是波长的量级,肉眼看不见,但用干涉手段完全可以测到。

为什么会有这种位移?简单理解:全反射时,虽然光在宏观上被“弹”回来了,但电磁场并没有完全被拒绝在界面之外,它会以倏逝波的形式渗入到低折射率介质里一段深度,再被“拉”回来。这段渗入和返回的过程,等效于光束在界面上多走了一段路,于是反射点看起来就平移了。

Artmann在同年给出了一个漂亮的稳态相位公式:位移 ( \Delta ) 正比于反射系数的相位 ( \phi ) 对横向波矢 ( k_x ) 的导数

[ \Delta = -\frac{\partial \phi}{\partial k_x} ]

这个公式是整个光束位移计算的灵魂。它的物理图像是:一束有限宽度的光可以分解成一系列不同 ( k_x ) 的平面波分量,每个分量的反射相位不一样,合在一起后,等相位面发生了倾斜,而能流方向垂直于等相位面,于是光束整体就横向漂移了。后面我给的数值复现,本质上就是把这个公式从“解析近似”推广到“全数值积分”。

1.2 对称性保护:为什么正入射时位移通常为零

现在回到正入射。想象一个完全均匀的平面镜,光束垂直打上去。入射光的 ( k_x=0 ),反射光当然也应该是 ( k_x=0 )。光束里那些微小的角谱分量,假设有个分量略微带一点 ( +k_x ),那一定也存在一个对称的 ( -k_x ) 分量。对于普通的平面镜,这两个分量的反射系数大小相等、相位也相等,它们的横向漂移会完全抵消,最终反射光束的重心稳稳地停在入射点上。

用Artmann公式的语言来说:结构如果在横向存在镜像对称性,那么反射系数满足

[ r(k_x)=r(-k_x) ]

相位 ( \phi(k_x) ) 是偶函数,在 ( k_x=0 ) 处一阶导数为零,所以位移严格为零。这不是数值误差,而是对称性保护的结果。

这一点非常关键。很多初学者在复现文献时,把一维多层膜算来算去,发现正入射位移归零,就以为是自己代码写错了。其实不是,是结构本身限制了它。所以真正要做“正入射光束位移”,第一步不是急着写代码,而是先问自己:我打算打破哪个对称性?

1.3 打破对称性的三条路线

文献里真正实现正入射非零位移的机制,大致可以分成三类,各有各的物理源头。

第一类是等频面倾斜。光子晶体内部传播的不是简单的平面波,而是布洛赫波。布洛赫波的群速度方向垂直于等频面,如果等频面相对界面法线是倾斜的,那么即使在正入射条件下(横向波矢为零),能流方向也会有一个横向分量,光进入晶体后就会横向漂移。宏观世界里最像这个现象的是双折射晶体里的e光偏移——光垂直入射到一块光轴倾斜的晶体板上,出射光束照样平移。

第二类是磁光效应。给光子晶体加一个外磁场,介电常数张量会出现反对称的非对角元,时间反演对称性被打破。这时候圆偏振的正负模式不再简并,正入射的线偏振光反射后,两个圆偏振分量会分离,出现类似自旋霍尔效应的横向分离。这个方向的工作通常和“光子自旋霍尔效应”放在一起讲,位移量级可以做得很可观。

第三类是表面模式耦合。光子晶体截断表面如果存在Tamm态或泄漏模式,反射相位在共振点附近会急剧变化。配合一个横向不对称的耦合结构(比如非对称光栅单元),就能在正入射下获得非常大的位移。

我这篇文章的复现路线走的是“一维光子晶体+带隙边缘相位增强”这条最稳的路。它严格来说算“近正入射”的增强方案,正入射极限下位移会被对称性压回零,但理解它的数值框架之后,你想往二维等频面倾斜方向扩展,只需要换色散计算,位移计算的底子完全一样。

2. 光子晶体里的布洛赫模式与等频面:位移的“方向盘”

2.1 从布拉格反射到带隙:为什么要用光子晶体

普通介质里的光束位移,哪怕是全反射,量级也就是几个波长。想获得大位移,需要让反射系数对 ( k_x ) 有足够大的相位梯度。光子晶体的优势在于,它可以提供非常陡峭的相位响应。

一维光子晶体本质上就是多层膜,高低折射率层交替排列。在布拉格条件下,每层反射的光相位一致,叠加后反射率可以无限接近1,形成一个“光子带隙”——就像半导体带隙禁止电子通过一样,这个频率范围内的光不能在晶体里传播。最妙的是,带隙边缘的反射率从接近0迅速爬升到接近1,伴随而来的就是反射相位在很窄的频率/角度范围内发生大角度变化。相位变化越陡,位移越大。

我经常拿一个比喻给组里同学讲:光子晶体带隙边缘就像信号处理里的“边沿滤波器”,相位响应曲线陡峭,而位移本质上就是相位对波矢的斜率。滤波器越陡,输出信号对频率越敏感,光学里就是位移越大。

2.2 布洛赫模式与等频面倾斜:能流方向不等于波矢方向

在光子晶体里,我们处理的是周期介质中的布洛赫波。布洛赫定理告诉我们,本征场可以写成振幅受周期调制的平面波:

[ E_k(\mathbf{r}) = e^{i\mathbf{k}\cdot\mathbf{r}} u_k(\mathbf{r}) ]

其中 ( u_k ) 具有和晶格相同的周期性。色散关系 ( \omega(\mathbf{k}) ) 在周期介质中不再是简单的直线或圆,而是被折叠成能带结构。

群速度

[ \mathbf{v}g = \nabla{\mathbf{k}} , \omega(\mathbf{k}) ]

是能流传播的方向,它总是垂直于等频面。对于各向同性均匀介质,等频面是圆,群速度沿径向,和波矢方向一致。但光子晶体的等频面经常发生形变,甚至被扭曲成斜椭圆。一旦等频面法线不再沿波矢方向,就会出现“斜向能流”。

这就是正入射位移在物理上最本质的来源:入射光的波矢是垂直于界面的,但激发出来的布洛赫模式的能量流动方向可以不垂直于界面。光进到晶体里以后,像进了传送带,横向漂移一段再离开。

2.3 截断带来的表面态:相位陡变的放大器

除了能带本身的色散,还有一个经常被低估的机制:表面态。

完整周期性晶体被截断后,表面处可能出现局域在界面附近的模式——一维对应Tamm态,二维和三维对应表面态或表面泄漏模式。这些模式通常位于带隙内,频率上靠近带隙边缘时,它们会和入射光发生耦合。

表面态对反射相位的影响相当大。正入射光在表面态共振频率附近,反射相位会发生接近 ( 2\pi ) 的快速跳变,这时候 ( \partial\phi/\partial k_x ) 可以达到很大的值,位移也就被放大数倍甚至数十倍。

所以做这个方向,表面终止层怎么截断非常关键。同一块光子晶体,最后一层是高折射率还是低折射率,厚度偏了多少,都会显著改变表面态的位置和耦合强度,进而改变位移符号和大小。这个后面参数扫描部分还会详细说。

2.4 复现选型建议:先一维,后二维

如果你是想快速建立“理论到复现”的完整闭环,我强烈建议先做一维光子晶体多层膜。原因有三:

  1. 传输矩阵法(TMM)精确且数值稳定,200行Python就能搞定。
  2. 带隙和相位响应有清晰的解析预期,方便校验代码。
  3. 位移计算框架和高维完全一致——都是角谱分解加相位响应,一旦跑通,换成二维光子晶体只是把“反射系数计算器”换掉而已。

等你想看真正的等频面倾斜导致的正入射位移,再上二维平面波展开法(PWEM)或者FDTD不迟。但数值方法的核心思想,一维里已经全部有了。

3. 用传输矩阵法把多层膜搬进代码

3.1 传输矩阵的核心逻辑

传输矩阵法适合处理一维分层结构。它的思路是:每层介质对应一个 ( 2\times2 ) 矩阵,把层一侧的电场和磁场切向分量映射到另一侧。把所有层的矩阵按顺序相乘,得到整个结构的等效矩阵,再结合入射介质和出射介质的边界条件,就能解出反射系数和透射系数。

对于TE偏振(电场垂直入射面),第 ( j ) 层(折射率 ( n_j )、厚度 ( d_j )、入射角 ( \theta_j ))的相位厚度是

[ \delta_j = \frac{2\pi}{\lambda} n_j d_j \cos\theta_j ]

在代码里,我不会真的组装矩阵然后解方程,而是用等价但更不容易出错的导纳递推法:从出射介质开始,逐层向前递推结构的输入导纳 ( Y ),最终反射系数就是

[ r = \frac{q_{\mathrm{inc}} - Y_{\mathrm{in}}}{q_{\mathrm{inc}} + Y_{\mathrm{in}}} ]

其中 ( q_{\mathrm{inc}} = n_{\mathrm{inc}} \cos\theta_{\mathrm{inc}} ) 是入射介质的TE导纳。递推公式一层层套下去,逻辑非常清晰。

3.2 Python实现:一个精简但完整的函数

下面这个函数够我们后面所有计算用了。结构定义成从空气入射、以低折射率层结束的多层膜,也就是 ( \text{air} | (HL)^N | \text{air} )。

import numpy as np def bragg_reflection(n_H, n_L, N, lam0, lam, theta=0.0, pol='TE'): """一维光子晶体反射系数。 结构: air | (H L)^N | air n_H, n_L: 高低折射率 N: 周期数 lam0: 设计波长, 层厚取光学厚度 lam0/4 lam: 工作波长 theta: 入射角(rad) """ k0 = 2 * np.pi / lam d_H = lam0 / (4 * n_H) d_L = lam0 / (4 * n_L) n_inc = n_sub = 1.0 # 入射介质的横向波矢, 正入射时 kx = 0 kx = k0 * n_inc * np.sin(theta) if pol == 'TE': q_inc = n_inc * np.cos(theta) # 出射介质 sin_theta_sub = np.clip(n_inc * np.sin(theta) / n_sub, -1, 1) q_sub = n_sub * np.sqrt(1 - sin_theta_sub**2) Y = q_sub # 从出射一侧开始 # 注意递推方向: 从靠近出射介质的最后一层 L 开始, 所以先处理 L 再 H for _ in range(N): for n, d in ((n_L, d_L), (n_H, d_H)): cos_t = np.sqrt(np.clip(1 - (kx / (k0 * n))**2, 0, 1)) q = n * cos_t delta = k0 * n * d * cos_t Y = q * (Y + 1j * q * np.tan(delta)) / (q + 1j * Y * np.tan(delta)) r = (q_inc - Y) / (q_inc + Y) return r else: # TM偏振只需把导纳换成 1/q, 这里留作扩展 raise NotImplementedError("这里只写TE, TM的同理换导纳即可")

这是个最小实现。说实话,这个版本为了便于阅读牺牲了一点点效率——如果后面要做几万个 ( k_x ) 点的高斯光束角谱,建议用numba或者把for循环向量化。不过对于演示完全够用。

3.3 先用反射谱验证代码

代码写完千万别直接算位移,先画个反射谱确认带隙位置是准的。我的习惯是扫描400到2000nm波长,在正入射下看反射率。

参数取 ( n_H=2.35 ),( n_L=1.45 ),( N=8 ),设计波长 ( \lambda_0=1.55\mu m )。此时每层光学厚度都是 ( \lambda_0/4 ),带隙中心应该在1550nm附近,反射率接近1。带隙边缘大概落在1400nm和1750nm附近,反射率会急剧掉下来,而就在这两个掉下来的地方,相位谱会出现陡坡。

如果你跑出来发现带隙中心不在设计波长,先检查 ( d_H ) 和 ( d_L ) 的公式。四分之一波长条件是 ( d = \lambda_0/(4n) ),光在介质里的波长缩短了 ( n ) 倍,这个最容易写错。

4. 从平面波到高斯光束:位移量的数值计算

4.1 为什么平面波算不出位移

平面波是单一 ( k_x ),反射后还是单一 ( k_x ),虽然反射系数的相位会变,但这个相位只是整体载波相位,不会让光束的能量中心在横向上移动。位移必须靠不同 ( k_x ) 分量之间的干涉才能体现。所以计算位移之前,要把入射场换成有横向分布的光束,最常用的是高斯光束。

高斯光束在束腰处的横向电场分布是

[ E(x,0) = E_0 \exp\left(-\frac{x^2}{w_0^2}\right) e^{ik_{x0}x} ]

其中 ( w_0 ) 是束腰半径,( k_{x0} ) 是光束整体的横向中心波矢(正入射时为零)。

4.2 角谱分解与逐分量反射

我们把高斯光束做傅里叶变换,得到它在 ( k_x ) 空间的角谱分布 ( A(k_x) )。高斯光束的角谱仍然是高斯形,宽度约为 ( 1/w_0 )。每个 ( k_x ) 分量对应一个入射角

[ \theta = \arcsin\left(\frac{k_x}{k_0 n_{\mathrm{inc}}}\right) ]

用bragg_reflection函数算出这个角度下的反射系数 ( r(k_x) ),反射光束的角谱就是

[ A_r(k_x) = A(k_x) \cdot r(k_x) ]

再逆傅里叶变换回实空间,就得到反射光束的横向电场分布 ( E_r(x) )。

有一点实操上的提醒:入射光和反射光的传播方向相反,但在界面处横向波矢是连续的,所以这里用傅里叶变换的逆变换就可以,不需要额外处理传播方向。符号细节如果写错,反射光束的重心可能会算成负的,但绝对值不受影响。我的经验是先用一个纯相位响应(比如 ( r=e^{i\alpha k_x} ))自检一下,看位移方向是否符合预期。

4.3 数值重心法

得到反射光束的电场分布后,位移量就直接用能量重心之差:

[ \Delta = \frac{\int x |E_r(x)|^2 , dx}{\int |E_r(x)|^2 , dx} ]

入射高斯光束的重心在 ( x=0 ),所以这个 ( \Delta ) 就是反射光束相对入射点的横向位移。

这里有个很容易被忽略的点:入射光束的重心虽然是你自己定义的,但实际计算中FFT采样网格里的坐标偏移、半像素误差都会混进重心计算里。我建议先算一束“不加结构”的反射(也就是直接 ( r=1 ))作为基准,再把位移结果减去这个基准值,能有效消除数值坐标系统的固有偏移。

4.4 完整计算流程代码

下面这段是位移计算的完整函数,参数上我留了一个theta0,方便你做“近正入射扫描”或者“严格正入射”两种情况。

def gaussian_beam_shift(n_H, n_L, N, lam0, lam, w0, Nx=4096, dx=None, theta0=0.0): """计算高斯光束经一维光子晶体反射后的横向位移。""" if dx is None: dx = w0 / 10 # 空间采样步长, 要远小于束腰 x = (np.arange(Nx) - Nx / 2) * dx k0 = 2 * np.pi / lam # 入射高斯光束(束腰在结构表面) E_in = np.exp(-(x / w0)**2) * np.exp(1j * k0 * np.sin(theta0) * x) # 角谱 A = np.fft.fftshift(np.fft.fft(np.fft.ifftshift(E_in))) kx = 2 * np.pi * np.fft.fftshift(np.fft.fftfreq(Nx, dx)) # 逐 kx 分量计算反射系数 r = np.zeros(Nx, dtype=complex) for i, kxi in enumerate(kx): theta = np.arcsin(np.clip(kxi / (k0 * 1.0), -1, 1)) r[i] = bragg_reflection(n_H, n_L, N, lam0, lam, theta) # 反射角谱 -> 反射光束 A_r = A * r E_refl = np.fft.ifftshift(np.fft.ifft(np.fft.fftshift(A_r))) # 重心位移 I_r = np.abs(E_refl)**2 shift = np.sum(x * I_r) / np.sum(I_r) return shift, x, E_in, E_refl

这个函数跑一遍,如果参数取 ( N=8 ),( w_0=20\lambda ),在带隙边缘波长附近扫描入射角,会看到位移随角度迅速增大,在接近正入射时却回落。这不是代码错了,而是对称性在起作用。想观察增强效应,可以把入射角固定在比如 ( 1^\circ ),然后去扫波长,在带隙边缘附近你会看到位移出现一个明显的尖峰。

5. 参数扫描:位移的旋钮到底在哪里

5.1 周期数 ( N ):相位陡度的第一旋钮

光子晶体周期数越多,带隙越“硬”,反射率在带隙内越接近1,带隙边缘的相位变化也越陡峭。我用N=4, 8, 12分别跑过,在 ( \theta=1^\circ )、波长取带隙边缘附近时,位移大约是:

周期数 N带隙边缘位移(示意值)
4约 ( 2\lambda )
8约 ( 12\lambda )
12约 ( 35\lambda )

注意这组数字是个定性趋势,具体值取决于折射率对比度和波长选择。但趋势是可靠的:层数越多,位移越大。代价是带宽变窄,可调节的波长窗口变小,而且结构对加工的精度要求更高。

5.2 波长位置:必须“钉”在带隙边缘

带隙中心附近反射率虽然高,但相位变化平缓,位移很小;带隙深处光是强度耦合不进去的,相位响应近乎平稳;只有在带隙边缘,反射率处于“将掉未掉”的状态,相位梯度最大,位移也最大。

实际操作中,我习惯把波长步长取到带隙宽度的1/50以内去扫描。如果步长太大,很可能直接错过位移尖峰,误以为这个结构效果不行。带隙边缘是这种效应最敏感的区域,值得你多花点算力去“蹲守”。

5.3 入射角:近正入射增强的极限

位移对角度的依赖很有意思。在带隙边缘,位移既可以在某个非零角度下达到峰值,又在接近0度时被压回零。峰值角度通常在带隙边缘的“泄漏锥”附近,和我上面说的表面态耦合有关。你的结构表面终止方式变了,峰值角度也会跟着变。

想往严格正入射推,就得换机制——比如引入等频面倾斜的二维结构,让 ( \partial\phi/\partial k_x ) 在 ( k_x=0 ) 处不为零。我在文末会再提一句。

5.4 束腰 ( w_0 ):一个最容易被忽略的陷阱

光束束腰对位移结果的影响,很多人第一次算都会栽跟头。束腰太小,角谱铺得太宽,一部分角谱分量落在带隙外,位移被“掺水”摊平;束腰太大,角谱太窄,理论上也能算出接近解析值的结果,但数值上角谱采样点不够,FFT的 ( k_x ) 分辨率不足,计算结果会抖动很厉害。

我用的经验法则是:

  • 束腰不小于 ( 10\lambda ),否则角谱展宽太严重。
  • 束腰不大于 ( 100\lambda ),否则要非常大的 ( N_x ) 才能保证角谱分辨率。
  • 空间采样步长 ( dx ) 至少要小于 ( \lambda/(10 n_{\mathrm{max}}) ),才能容纳介质内部的场变化。

如果你发现位移随束腰变化超过10%,那基本是角谱采样或者空间采样的问题,先把网格加密再说。

6. 复现过程中的实际经验与坑

6.1 相位解包:arctan的2π跳变会让你怀疑人生

如果你直接用np.angle(r)去取相位,然后试图数值求导,大概率会得到一堆锯齿状的东西——因为np.angle把相位卷绕到 ( (-\pi,\pi] ) 区间,每跨过 ( 2\pi ) 就跳一次。位移计算用到了 ( \partial\phi/\partial k_x ),相位不连续会导致位移出现虚假的尖刺。

两条路可以选:一是对原始复数反射系数逐点计算位移,不用显式求相位(我上面给的代码就是这么干的);二是用np.unwrap()先把相位解开,再求导。前者更稳,因为它在复数域直接做乘法,不引入任何人工处理。

6.2 角谱采样范围不足:位移算出来是振荡的

有一次我把 ( N_x ) 从1024改成4096之后,位移突然从乱跳变成了平缓曲线,才知道之前算的振荡全是采样不足造成的假象。

判断标准很直接:固定其他参数,只增加 ( N_x ),如果位移曲线还在明显变,那就是没收敛。角谱的 ( k_x ) 范围是 ( 2\pi/(2dx) ),你要保证这个范围至少覆盖高斯角谱的5个标准差以上,也就是 ( 5/w_0 )。另外,( k_x ) 的分辨率是 ( 2\pi/(N_x dx) ),要保证在一个带隙的特征角宽度内有足够多的采样点。

6.3 表面终止层厚度:微小偏差改变位移符号

这是我从一个“意外的负位移”里学到的。当时我微调了表面高折射率层厚度,从标准的 ( \lambda/4 ) 改成 ( 0.9\lambda/4 ),位移峰值不仅大小变了,符号还反转了。

原因在于表面终止层决定了反射相位谱的形状,尤其是表面模式的位置。表面层厚度变化时,表面态共振频率移动,相位陡变区域移向不同角度/波长,位移的峰值和方向自然跟着变。

所以,如果你的目标是“复现文献结果”,第一件事就是把文献的结构参数,特别是表面终止层的厚度,精确到小数点后两位拿过来用。哪怕差5%,结果都可能对不上。

6.4 如何验证你的数字是靠谱的

我自己的验证习惯分三步:

  1. 解析极限验证:在小角度极限下,位移应该趋近Artmann公式的预测值。跑几个不同角度,把数值位移和 ( -\partial\phi/\partial k_x ) 解析值画在一起,两者应该重合。
  2. 结构退化验证:让高低折射率相等(( n_H=n_L )),光子晶体退化成均匀介质,位移应该归零。如果这时候不为零,你的代码有bug。
  3. 束腰收敛验证:连续增大 ( w_0 ),位移应该逐渐逼近“无穷大束腰极限”——这个极限对应的是完整角谱加权积分,而不是单点稳态相位近似。

三步都过了,我才敢说这个位移结果是可信的。如果你手头有FDTD工具,可以把一维结果和FDTD对一遍,但FDTD网格色散会带来微小的频移,要对齐反射谱的带隙边缘来比,别直接对同一个波长。

最后再分享一个小经验

正入射光束位移这个题目,初看像个“计算物理练习题”,真正踩进去才发现它对物理直觉的要求很高。我这段时间最深的体会是:计算框架本身并不难,难的是知道自己算出来的这个位移对应的是哪个机制。同样是位移尖峰,到底是带隙边缘相位陡变贡献的,还是表面态共振贡献的,还是等频面倾斜贡献的?这三者的参数依赖关系完全不一样。

所以建议大家做参数扫描的时候,不要只盯着位移曲线看。把反射谱、相位谱、角谱分布一起画出来,三张图对着看。一旦发现位移峰的位置恰好和相位梯度峰重合,而反射率又处于过渡区,基本就能锁定机制了。

这套代码和思路接下来还能往两个方向扩展:一个是把TE换成TM,对比偏振行为;另一个是换成二维光子晶体的平面波展开法,算真正的等频面倾斜正入射位移。希望这篇从理论到复现的记录,能让你在自己的项目里少踩几个坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询