黑洞模拟从来不是"写个脚本跑一下"的事。广义相对论方程在极端曲率下的数值求解,对精度、稳定性和计算规模的要求极其苛刻。亚利桑那大学的天体物理学家 Chi-kwan Chan 正在用 OpenAI 的 Codex 编码代理来加速这类模拟的搭建——不是让 AI 替他做物理,而是让 AI 替他处理那些耗时的工程细节。
黑洞模拟到底在算什么
Chan 的工作核心是对黑洞周围时空进行数值模拟。具体来说:
- 求解爱因斯坦场方程:在强引力场区域,时空曲率本身会成为方程的变量,形成高度非线性的耦合系统。
- 追踪光线与粒子轨迹:模拟光子在弯曲时空中的路径,生成可观测的"黑洞阴影"图像,与 EHT(事件视界望远镜)的实际观测做对比。
- 测试广义相对论边界:在 Kerr 时空、磁化等离子体等条件下,检验 GR 预测是否与模拟结果一致。
这些模拟通常涉及大规模并行计算、自适应网格细化(AMR)、以及 CUDA 或 OpenCL 加速。工程量巨大——而 Codex 的介入点,恰恰在这些"物理之外、代码之内"的环节。
Codex 在模拟流程中的实际角色
Chan 并不是把场方程丢给 Codex 让它推导。他的用法更务实:
- 脚手架生成:让 Codex 快速搭建项目骨架——Makefile、CMake 配置、Python 绑定、测试框架,这些重复性工作过去要花数天。
- 数值内核的模板代码:比如 Riemann solver 的骨架、边界条件的处理框架。物理逻辑由 Chan 填入,但 Codex 能给出正确的循环结构、内存布局和索引约定。
- 调试与性能分析辅助:Codex 可以解读 nvprof 或 nsight 的输出,定位 kernel 中的 bank conflict 或 uncoalesced access,并建议修复方案。
- 文档与注释同步:模拟代码的注释往往滞后。Codex 能根据代码变更批量更新 docstring 和 README。
关键洞察:Codex 的价值不是替代物理直觉,而是消除"知道该算什么但写代码太慢"的摩擦。
实践:从 Schwarzschild 几何开始
下面用一个可运行的 Python 示例演示黑洞模拟的入门计算——Schwarzschild 时空中的光线轨迹追踪。这不是 Chan 的完整生产代码,但展示了模拟的核心思路,你可以在此基础上扩展。
"""
Schwarzschild 黑洞光线轨迹模拟
计算光子在 Schwarzschild 时空中的轨迹,可视化黑洞"阴影"边界
依赖:numpy, matplotlib
运行:python schwarzschild_raytrace.py
"""
import numpy as np
import matplotlib.pyplot as plt
# ---- 物理参数 ----
M = 1.0 # 黑洞质量(几何单位 G=c=1)
r_s = 2 * M # Schwarzschild 半径(事件视界)
def effective_potential(r, b):
"""光线在 Schwarzschild 时空中的有效势
b: impact parameter(碰撞参数)
"""
return (1 - r_s / r) * (1 + b**2 / r**2)
def trace_ray(b, dr=0.01, r_start=50.0):
"""追踪一条碰撞参数为 b 的光线轨迹
返回 (r, phi) 数组
"""
r = r_start
phi = 0.0
trajectory = []
# 光子临界碰撞参数:b_crit = 3*sqrt(3)*M ≈ 5.196
b_crit = 3 * np.sqrt(3) * M
while r > r_s * 1.01 and r < r_start * 1.5:
# dphi/dr = b / (r^2 * sqrt(1 - b^2/r^2 * (1 - r_s/r)))
denom_sq = 1 - (b**2 / r**2) * (1 - r_s / r)
if denom_sq <= 0:
# 光子被捕获,轨迹终止
break
dphi_dr = b / (r**2 * np.sqrt(denom_sq))
phi += dphi_dr * dr
r -= dr # 光子向黑洞方向传播
trajectory.append((r, phi))
return np.array(trajectory) if trajectory else np.array([]).reshape(0, 2)
# ---- 批量追踪不同碰撞参数的光线 ----
b_values = np.linspace(3.0, 10.0, 40)
trajectories = {}
for b in b_values:
traj = trace_ray(b)
if len(traj) > 0:
trajectories[b] = traj
# ---- 可视化 ----
fig, ax = plt.subplots(figsize=(8, 8))
# 绘制事件视界
theta_circle = np.linspace(0, 2 * np.pi, 100)
ax.plot(r_s * np.cos(theta_circle), r_s * np.sin(theta_circle),
'k-', linewidth=2, label='事件视界')
# 绘制光线轨迹
for b, traj in trajectories.items():
r_vals, phi_vals = traj[:, 0], traj[:, 1]
x = r_vals * np.cos(phi_vals)
y = r_vals * np.sin(phi_vals)
color = 'red' if b < 3 * np.sqrt(3) * M else 'blue'
ax.plot(x, y, color=color, alpha=0.6, linewidth=0.8)
# 光子球标注
r_photon = 3 * M
ax.plot(r_photon * np.cos(theta_circle), r_photon * np.sin(theta_circle),
'g--', linewidth=1, label='光子球 r=3M')
ax.set_xlim(-15, 15)
ax.set_ylim(-15, 15)
ax.set_aspect('equal')
ax.set_xlabel('x (M)')
ax.set_ylabel('y (M)')
ax.set_title('Schwarzschild 黑洞光线轨迹')
ax.legend()
plt.tight_layout()
plt.savefig('blackhole_shadow.png', dpi=150)
plt.show()
print(f"临界碰撞参数 b_crit = {3*np.sqrt(3)*M:.3f} M")
print("红色轨迹:被黑洞捕获 | 蓝色轨迹:逃逸")
运行后会生成一张黑洞阴影图——红色光线被捕获,蓝色光线逃逸,分界线对应临界碰撞参数 $b_{crit} = 3\sqrt{3}M$。这正是 EHT 观测 M87* 黑洞阴影时用来比对的理论基线之一。
用 Codex 加速下一步
当你想把这个简单模拟升级为生产级代码时,Codex 的典型用法:
# 用 Codex CLI 生成 CUDA 加速版本的项目骨架
codex "将 schwarzschild_raytrace.py 改写为 CUDA 版本:
- 核心轨迹计算移到 GPU kernel
- 保持 Python 接口,用 PyCUDA 或 cupy 包装
- 添加批量光线并行追踪(100万条光线)
- 输出改为 numpy 数组而非逐条 list"
# 让 Codex 补充测试框架
codex "为 schwarzschild_raytrace.py 添加 pytest 测试:
- 验证 b_crit 附近光线的捕获/逃逸判定
- 验证远距离光线近似直线
- 验证有效势在光子球处极值"
Codex 返回的代码不会自动保证物理正确性——你需要逐行审查数值细节。但它能省下你写 CUDA kernel 骨架、测试框架、构建脚本的那两三天时间。
采用建议与边界
| 场景 | Codex 适合度 | 注意事项 |
|---|---|---|
| 项目脚手架、构建系统 | ★★★★ | 直接可用,审查即可 |
| 数值内核模板 | ★★★ | 物理逻辑必须自己填入,Codex 的索引/循环可能有错 |
| 性能优化建议 | ★★★ | 需要实际 profiling 验证,AI 常给出"看起来合理但无效"的优化 |
| 物理推导与方程离散 | ★★ | 高风险,GR 数值离散的稳定性条件 AI 不懂 |
| 论文级结果生成 | ★ | 完全不适合,必须人工验证每一步 |
Chan 的经验指向一个清晰的原则:让 Codex 处理确定性高的工程任务,物理判断永远留在人类这边。 黑洞模拟的可靠性最终取决于你对场方程离散化、边界条件、收敛性的理解——Codex 能让你更快到达可以施展这些判断的代码状态,但不能替你做判断本身。
如果你正在做类似的科学计算项目,可以从一个最小可运行的物理核心(如上面的 Schwarzschild 示例)开始,先用 Codex 搭好构建和测试基础设施,再逐步把计算内核推向 GPU 和分布式。这样每一步都有可验证的基线,AI 生成的工程代码也不会把物理逻辑埋进难以审查的深处。