本文我们讨论下如何以官网例程Data Analysis - MPB Documentation为基础,求解《光子晶体-控制光流》第58页结构的场分布。
58页的结构是这样的:
我们的代码是这样的:
import math
import meep as mp
from meep import mpb
import numpy as np
import matplotlib.pyplot as plt
num_bands = 9
k_points = [
mp.Vector3(0.49, 0),
mp.Vector3(0.5, 0),
]
k_points = mp.interpolate(9, k_points)
geometry = [mp.Cylinder(0.2, material=mp.Medium(epsilon=8.9))]
geometry_lattice = mp.Lattice(size=mp.Vector3(1, 1))
resolution = 64
ms = mpb.ModeSolver(
num_bands=num_bands,
k_points=k_points,
geometry=geometry,
geometry_lattice=geometry_lattice,
resolution=resolution
)
# 准备一个列表来存储磁场
hfields = []
# 定义收集磁场的回调函数
def get_hfields(ms, band):
hfield_data = ms.get_hfield(band, bloch_phase=True)
hfields.append(hfield_data)
print(f"已获取能带 {band} 的磁场数据,形状: {hfield_data.shape}") # 调试信息
# 只执行一次 run_te,同时输出Hz和收集磁场数据
ms.run_te(
mpb.output_at_kpoint(
mp.Vector3(0.49, 0),
mpb.output_hfield_z, # 直接输出Hz场
# mpb.fix_hfield_phase, #这里一定要打上注释,这行千万不能写
get_hfields # 收集磁场数据供后续处理
)
)
# 检查是否成功获取到磁场
print(f"共获取到 {len(hfields)} 个能带的磁场数据")
# 创建MPBData实例用于数据转换:cite[3]
md = mpb.MPBData(rectify=True, periods=3, resolution=32)
eps = ms.get_epsilon() # 获取原始介电常数
converted_eps = md.convert(eps) # 转换为与场数据相同的网格
# 转换磁场数据
converted = []
for i, f in enumerate(hfields):
# 提取Hz分量 (z-component)
f_z = f[..., 0, 2] # 假设张量索引格式正确
converted_f = md.convert(f_z)
converted.append(converted_f)
# 创建图形 - 根据实际获取的能带数量调整布局
n = len(converted)
rows = math.ceil(n / 3) # 动态计算行数
plt.figure(figsize=(15, 5 * rows))
for i, f in enumerate(converted):
plt.subplot(rows, 3, i + 1)
# 绘制介电常数轮廓作为背景
plt.contour(converted_eps.T, levels=[np.mean(converted_eps)], colors='black', linestyles='dashed', alpha=0.5)
# 绘制磁场实部
im = plt.imshow(np.real(f).T, interpolation='spline36', cmap='RdBu', alpha=0.9)
plt.colorbar(im, fraction=0.046, pad=0.04)
plt.title(f'Band {i+1} Hz')
plt.axis('off')
plt.tight_layout()
plt.show()
运行结果是这样的:
和文章结果非常一致。
我发现值得注意的一点是“ # mpb.fix_hfield_phase, #这里一定要打上注释,这行千万不能写”,一定不能选固定相位,否则结果和书不吻合。原因我还不太清楚
==============================、
更新,原因大概清楚了,以下是AI的回答:
| 方法 | 相位来源 | 是否天然可复现 |
|---|---|---|
| PWE(MPB、RSoft BandSOLVE) | 求解本征方程,全局相位自由 | 不同实现、不同运行之间都可能差一个全局相位 |
| FDTD(MEEP、Lumerical FDTD、RSoft FullWAVE) | 相位由激励源的物理过程决定 | 源相同 → 结果相同,天然可复现 |
MPB算的场图,如果不固定住 mpb.fix_hfield_phase的话,每次算出来的结果都不一样,只有加上 mpb.fix_hfield_phase才能保证每次仿真的结果是一样的。这是因为 PWE法算出的结果不同实现、不同运行之间都可能差一个全局相位,这是PWE法的特性决定的。
要想用PWE法比如MPB去重复COMSOL/LUMERICAL的本征模式场图结果,必须取消掉mpb.fix_hfield_phase,这样得到的场图有可能和COMSOL/LUMERICAL一致。而加上这个语句,因为MPB和RSOFT,LUMERICAL等默认约定可能不一样,导致如果加上mpb.fix_hfield_phase,虽然每次得到的结果确实是固定的,但是仍然可能和COMSOL不一致。
MPB(频域本征求解器):相位是“自由”的
MPB 求解的是频域下的本征值方程。它算出的本征函数(模式场)E(r),可以乘以任意一个全局复相位因子 e^iϕ,而仍然满足同一个本征方程。这意味着,在数学上,这个相位是不确定的。
MPB 默认不做任何规范,直接输出这个“自由”的相位。因此,每次运行或不同 band 之间,这个相位都可能随机变化。
MEEP 使用 FDTD 方法,在时间域上迭代求解 Maxwell 方程组。它模拟的是电磁场随时间的真实演化过程。
在 MEEP 中,场的相位是由物理过程和激励源(source)的相位决定的,具有明确的物理意义。
如果你用一个
ContinuousSource(连续源)激发,稳态场的相位就由源的相位决定。如果你用一个
GaussianSource(高斯脉冲源)激发,你需要通过傅里叶变换(DFT)来获得频域信息,而 DFT 计算出的复数场,其相位同样是相对于激励源而言的。
因此,MEEP 不需要,也没有fix_efield_phase这样一个专门的函数来“固定”相位。它输出的复数场本身就携带了相对于源的、确定的相位信息。你只需要保证每次仿真使用完全相同的源(包括其相位、频率、位置等),得到的结果就是可复现的。