This notebook illustrates a few tensor calculus capabilities of SageMath on the example of Schwarzschild spacetime. See the SageManifolds page for more details.
%display latex
First we introduce the spacetime $M$ as a 4-dimensional Lorentzian manifold:
M = Manifold(4, 'M', structure='Lorentzian')
print(M)
4-dimensional Lorentzian manifold M
Then we define the standard Schwarzchild-Droste coordinates $(t,r,\theta,\phi)$ on it, via the method chart
called on the manifold object M
:
X.<t, r, th, ph> = M.chart(r"t r:(0,+oo) th:(0,pi):\theta ph:(0,2*pi):\phi")
Note that the argument of chart()
is a raw string (hence the prefix r
in front of it), which defines the range of each coordinate, if different from $(-\infty, +\infty)$, as well as its LaTeX symbol, if different from the Python symbol to denote the coordinate. The Python variables for each coordinate are declared within the <...>
operator on the left-hand side, X
denoting the Python variable chosen for the coordinate chart.
X
print(X)
Chart (M, (t, r, th, ph))
X.coord_range()
The Schwarzschild metric depends on one parameter: the black hole mass $m$; we declare the latter as a symbolic variable, via the command var
:
m = var('m')
assume(m>0)
We define next the metric tensor $g$ from its non-vanishing components in the manifold's default frame, namely the coordinate frame associated to Schwarzschild-Droste coordinate:
g = M.metric()
g[0, 0] = -(1 - 2*m/r)
g[1,1] = 1/(1-2*m/r)
g[2,2] = r^2
g[3,3] = r^2*sin(th)^2
g.display()
Display of the metric components as a matrix:
g[:]
g[0,1]
g[1,1]
Display of the non-vanishing components:
g.display_comp()
print(g)
Lorentzian metric g on the 4-dimensional Lorentzian manifold M
g
The nonzero (and nonredundant) Christoffel symbols of $g$ with respect to the default coordinate chart (X
):
g.christoffel_symbols_display()
All the nonzero Christoffel symbols:
g.christoffel_symbols_display(only_nonredundant=False)
The Ricci tensor:
g.ricci()
print(g.ricci())
Field of symmetric bilinear forms Ric(g) on the 4-dimensional Lorentzian manifold M
We check that the Schwarzschild metric is a solution of the vacuum Einstein equation:
g.ricci().display()
The Riemann curvature tensor:
R = g.riemann()
R.display()
R.display_comp()
The Kretschmann scalar is the "square" of the Riemann tensor defined by
$$K = R_{abcd} \, R^{abcd}$$
To compute it, we must first form the tensor fields whose components are $R_{abcd}$ and
$R^{abcd}$. They are obtained by respectively lowering and raising the indices of the components $R^a_{\ \, bcd}$ of the Riemann tensor, via the metric $g$. These two operations are performed by the methods down()
and up()
. The contraction is performed by summation on repeated indices, using LaTeX notations:
K = R.down(g)['_{abcd}'] * R.up(g)['^{abcd}']
print(K)
K.display()
Scalar field on the 4-dimensional Lorentzian manifold M
K.expr()
Let us introduce the exterior $E$ of the black hole as an open subset of $M$:
E = M.open_subset('E', coord_def = {X: r>2*m})
On $E$ static observers can exist; their 4-velocity $u$ is
u = E.vector_field(name='u')
u[0] = 1/sqrt(1-2*m/r)
u.display()
g(u, u)
print(g(u,u))
Scalar field g(u,u) on the Open subset E of the 4-dimensional Lorentzian manifold M
g(u,u).display()
u.plot(ambient_coords=(r,t), fixed_coords={th: pi/2, ph: pi},
ranges={t: (0, 6), r: (2.1, 6)}, parameters={m: 1}, scale=0.3)
nabla = g.connection()
nabla
print(nabla(u))
Tensor field nabla_g(u) of type (1,1) on the Open subset E of the 4-dimensional Lorentzian manifold M
nabla(u).display()
The 4-acceleration of static observers is defined by $$ a^a = u^b \nabla_b u^a$$ Since the derivative index is the last one (number 1), i.e. $(\nabla u)^a_{\ \, b} = \nabla_b u^a$, we write
a = u.contract(nabla(u), 1)
a.display()
An alternative computation of the 4-acceleration using LaTeX-based index notation:
a = u['^b']*nabla(u)['^a_b']
a.display()
We introduce the Eddington-Finkelstein coordinates $(T,r,\theta,\phi)$ as a second chart on the manifold:
EF.<T, r, th, ph> = M.chart(r"T r:(0,+oo) th:(0,pi):\theta ph:(0,2*pi):\phi")
EF
The transformation from Schwarzschild-Droste coordinates (chart X
) to Eddington-Finkelstein ones (chart EF
) is
X_to_EF = X.transition_map(EF, [t +2*m*ln(abs(r/(2*m)-1)), r, th, ph])
X_to_EF.display()
X_to_EF.inverse().display()
g.display()
g.display(EF.frame())
g[EF.frame(),:]
At this stage, three charts have been introduced on the manifold:
M.atlas()
One can plot the gridlines of a chart in terms of another chart, for instance the Schwarzschild-Droste chart (X
) in terms
of the Eddington-Finkelstein chart (EF
):
X.plot(EF, ranges={t:(0, 8), r:(2.1, 10)}, fixed_coords={th:pi/2, ph:0},
ambient_coords=(r,T), style={t:'--', r:'-'}, parameters={m: 1}) \
+ X.plot(EF, ranges={t:(0, 8), r:(0.1, 1.9)}, fixed_coords={th:pi/2, ph:0},
ambient_coords=(r,T), number_values={t: 9, r: 3},
style={t:'--', r:'-'}, parameters={m: 1})
This reveals the singularity of the Schwarzschild-Droste coordinates at $r=2m$ (the black hole horizon).
Visit the black hole section of SageManifolds examples.