08.4 EinsteinPy 實戰#
本節目標#
- 認識 EinsteinPy:Python 的廣義相對論計算套件,涵蓋符號計算、測地線積分與視覺化。
- 掌握經典 pipeline:輸入 metric → 自動算 Christoffel → Ricci → Einstein 張量,全程一行式呼叫。
- 以 Schwarzschild 度規完整跑一遍,並和第 10.3 節手工 pipeline 對照。
- 誠實面對 API 現況:
einsteinpy.symbolic模組已停止積極開發,學會何時退回 SymPy 手寫。 - 動手實驗:自己 input 一個 metric(FRW 或 Kerr),讓 EinsteinPy 算出 Einstein 張量。
EinsteinPy 簡介與安裝#
EinsteinPy(https://docs.einsteinpy.org)提供三類功能:
- 符號計算(
einsteinpy.symbolic):度規、Christoffel、Ricci、Einstein 張量、Weyl 張量。 - 測地線積分(
einsteinpy.geodesics):數值解 Schwarzschild/Kerr 時空中的粒子與光子軌跡。 - 視覺化(
einsteinpy.hypersurface、plotting):嵌入圖與軌道圖。
安裝:
pip install einsteinpy
匯入慣例(社群通用縮寫):
from einsteinpy.symbolic import MetricTensor, RicciScalar, EinsteinTensor
from einsteinpy.symbolic.helpers import simplify_syntactic
import sympy as sp
經典 pipeline:metric → Christoffel → Ricci → Einstein 張量#
EinsteinPy 的符號層建構在 SymPy 之上:你用 SymPy 符號與矩陣寫出 \(g_{\mu\nu}\) ,包成 MetricTensor,之後所有派生量都是「一個屬性呼叫」:
import sympy as sp
from einsteinpy.symbolic import MetricTensor, ChristoffelSymbols, RiemannCurvatureTensor, RicciTensor, RicciScalar, EinsteinTensor
# 符號與座標
t, r, th, ph = sp.symbols('t r theta phi')
M = sp.Symbol('M', positive=True)
c, G = sp.Symbol('c'), sp.Symbol('G')
# Schwarzschild 度規(球座標、號差 (-,+,+,+))
g = sp.diag(
-(1 - 2*G*M/(c**2*r)), 1/(1 - 2*G*M/(c**2*r)),
r**2, r**2*sp.sin(th)**2
)
mstr = MetricTensor(g, (t, r, th, ph))
mstr.tensor() # 取回 SymPy Matrix
# 一路往下派生
ch = ChristoffelSymbols.from_metric(mstr) # Γ^i_{jk}
riem = RiemannCurvatureTensor.from_christoffels(ch)
ric = RicciTensor.from_riemann(riem) # R_{μν}
rsc = RicciScalar.from_riccitensor(ric) # R
Gmn = EinsteinTensor.from_metric(mstr) # G_{μν} = R_{μν} - 1/2 R g_{μν}
print(rsc.tensor()) # 應為 0(真空解)
print(ric.tensor()) # 應為零矩陣(Ricci-flat)
Gmn.tensor() # 也為零:真空時空 G_{μν} = 0
重點:from_metric / from_christoffels 這類類方法把整條「壓縮鏈」自動化,等價於我們在第 8.2 節手寫的 full_pipeline,但程式碼只剩五行。
誠實說明:EinsteinPy API 現況與 SymPy 混用#
寫技術書不能只報喜。你應該知道的現況:
| 事實 | 影響與對策 |
|---|---|
einsteinpy.symbolic 自 2021 年起已停止積極開發(原維護者畢業離開專案) | API 仍可用、可重現本節結果,但別期待 bug 快速修復 |
| 官方文件標記部分類別為 deprecated | 以 pip install einsteinpy 當時版本的 docstring 為準 |
| 底層就是 SymPy | .tensor() 取回的是 SymPy 物件,可以無縫混用:sp.simplify(ric.tensor()[0,0]) |
| 符號簡化常殘留未化簡項 | 建議 sp.simplify 或 .simplify() 再比對答案 |
測地線模組(geodesics)依賴 numba/odeint,安裝偶爾失敗 | 符號計算部分不依賴這些,可單獨使用 |
實務建議:把 EinsteinPy 當「檢查工具」,把第 8.2 節的手寫 pipeline 當「理解工具」。兩者輸出都是 SymPy 物件,交叉驗證非常方便:
# EinsteinPy 與手寫 pipeline 的交叉驗證
from sympy import simplify
assert simplify(ric.tensor()) == sp.zeros(4, 4) # Schwarzschild 真空
與 SymPy 手建 pipeline 的對照表#
以「輸入度規、輸出 Einstein 張量」為任務:
| 步驟 | SymPy 手建(第 8.2 節) | EinsteinPy |
|---|---|---|
| 度規輸入 | sp.diag(...) + 自己管理座標元組 | MetricTensor(g, coords) |
| 逆度規 | g.inv() | mstr.inverse_metric() |
| Christoffel | 三重迴圈 + sp.diff(約 15 行) | ChristoffelSymbols.from_metric(mstr) |
| Riemann | 四重迴圈(約 20 行) | RiemannCurvatureTensor.from_christoffels(ch) |
| Ricci / \(R\) | 手寫收縮 | RicciTensor / RicciScalar |
| Einstein 張量 | 手寫 \(R_{\mu\nu}-\frac12 Rg_{\mu\nu}\) | EinsteinTensor.from_metric(mstr) |
| 指標升降 | 自己寫 g^{ik}R_{...} | 內建 change_config(升/降指標) |
| 學習價值 | 高:每一步看得見 | 低:黑盒,但適合量產計算 |
結論:學習期用手建,量產期用 EinsteinPy,交付前兩者互驗。
動手實驗:自己 input 一個 metric 跑 Einstein 張量#
實驗 A:FRW 宇宙(號差 \((-,+,+,+)\) , \(a(t)\) 是尺度因子):
import sympy as sp
from einsteinpy.symbolic import MetricTensor, EinsteinTensor, RicciScalar
t, k, rho, p = sp.symbols('t k rho p', real=True)
a = sp.Function('a')(t)
coords = (t, sp.Symbol('chi'), sp.Symbol('theta'), sp.Symbol('phi'))
# FRW: ds² = -dt² + a(t)²[dχ² + S_k(χ)²(dθ² + sin²θ dφ²)],取 S_k(χ)=χ 簡化(k=0 扁平情形)
g_frw = sp.diag(-1, a**2, a**2*sp.Symbol('chi')**2,
a**2*sp.Symbol('chi')**2*sp.sin(sp.Symbol('theta'))**2)
m_frw = MetricTensor(g_frw, coords)
G_frw = EinsteinTensor.from_metric(m_frw)
# 取 G_{tt} 分量(應為 3(a'/a)² 的形狀,即 Friedmann 方程左端)
Gtt = sp.simplify(G_frw.tensor()[0, 0])
print("G_tt / g_tt =", sp.simplify(-Gtt)) # = 3(ȧ/a)²
你會看到 \(G_{tt} = -3\dot a^2\) (配合 \(a\) 的導數記號可能顯示為 Derivative(a(t), t)**2)。把它移項就是 Friedmann 方程 \(\frac{\dot a^2}{a^2} = \frac{8\pi G}{3}\rho\) 的幾何側——場方程左端由純幾何自動生成,這正是 Einstein 張量的威力。
實驗 B(挑戰):Kerr 度規。把 Boyer–Lindquist 座標下的 Kerr 度規鍵入( \(a\) 為自轉參數):
t, r, th, ph = sp.symbols('t r theta phi')
M, a = sp.symbols('M a', positive=True)
Sigma = r**2 + a**2*sp.cos(th)**2
Delta = r**2 - 2*M*r + a**2
g_kerr = sp.Matrix([
[-(1 - 2*M*r/Sigma), 0, 0, -2*M*r*a*sp.sin(th)**2/Sigma],
[0, Sigma/Delta, 0, 0],
[0, 0, Sigma, 0],
[-2*M*r*a*sp.sin(th)**2/Sigma, 0, 0,
sp.sin(th)**2*(Sigma*(r**2+a**2) + 2*M*r*a**2*sp.sin(th)**2)/Sigma]
])
m_kerr = MetricTensor(g_kerr, (t, r, th, ph))
EinsteinTensor.from_metric(m_kerr).tensor()
注意事項:Kerr 的非對角項讓簡化變慢,跑幾分鐘是正常的;驗證目標是 \(G_{\mu\nu}\equiv 0\) (Kerr 也是真空解)。
小結與練習#
本節重點
- EinsteinPy 用
MetricTensor打包度規,之後 Christoffel、Riemann、Ricci、Einstein 張量全部一個類方法自動派生。 einsteinpy.symbolic已停止積極開發,但底層是 SymPy,輸出可無縫混用與交叉驗證。- Schwarzschild 與 Kerr 都是真空解:EinsteinPy 應給出 Ricci 與 Einstein 張量全零。
- FRW 實驗顯示 \(G_{tt}\) 自動生成 Friedmann 方程的幾何側。
- 手建 pipeline 學原理、EinsteinPy 量產、兩者互驗,是本書推薦的工作流。
練習
- 用
ChristoffelSymbols.from_metric(mstr)列出 Schwarzschild 全部非零 Christoffel 符號,與第 10.3 節手工結果比對。 - 對 FRW 度規計算
RicciScalar,驗證 \(R = 6\left(\frac{\ddot a}{a} + \frac{\dot a^2}{a^2}\right)\) 。 - 把實驗 A 的 \(G_{tt}\) 配上 \(T_{tt} = \rho a^2 \delta^t_t\) ,寫下完整的 Friedmann 方程。
- 挑戰:用 EinsteinPy 的
change_config把 \(R_{\mu\nu}\) 升指標成 \(R^{\mu}{}_{\nu}\) ,檢查跡是否等於RicciScalar。