跳轉到

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)提供三類功能:

  1. 符號計算(einsteinpy.symbolic):度規、Christoffel、Ricci、Einstein 張量、Weyl 張量。
  2. 測地線積分(einsteinpy.geodesics):數值解 Schwarzschild/Kerr 時空中的粒子與光子軌跡。
  3. 視覺化(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 也是真空解)。

小結與練習#

本節重點

  1. EinsteinPy 用 MetricTensor 打包度規,之後 Christoffel、Riemann、Ricci、Einstein 張量全部一個類方法自動派生。
  2. einsteinpy.symbolic 已停止積極開發,但底層是 SymPy,輸出可無縫混用與交叉驗證。
  3. Schwarzschild 與 Kerr 都是真空解:EinsteinPy 應給出 Ricci 與 Einstein 張量全零。
  4. FRW 實驗顯示 \(G_{tt}\) 自動生成 Friedmann 方程的幾何側。
  5. 手建 pipeline 學原理、EinsteinPy 量產、兩者互驗,是本書推薦的工作流。

練習

  1. 用 ChristoffelSymbols.from_metric(mstr) 列出 Schwarzschild 全部非零 Christoffel 符號,與第 10.3 節手工結果比對。
  2. 對 FRW 度規計算 RicciScalar,驗證 \(R = 6\left(\frac{\ddot a}{a} + \frac{\dot a^2}{a^2}\right)\) 。
  3. 把實驗 A 的 \(G_{tt}\) 配上 \(T_{tt} = \rho a^2 \delta^t_t\) ,寫下完整的 Friedmann 方程。
  4. 挑戰:用 EinsteinPy 的 change_config 把 \(R_{\mu\nu}\) 升指標成 \(R^{\mu}{}_{\nu}\) ,檢查跡是否等於 RicciScalar。