This notebook illustrates a few capabilities of SageMath in computations regarding Schwarzschild spacetime. The corresponding tools have been developed within the SageManifolds project.
Click here to download the notebook file (ipynb format). To run it, you must start SageMath with the Jupyter interface, via the command sage -n jupyter
NB: a version of SageMath at least equal to 8.2 is required to run this notebook:
version()
'SageMath version 9.2.beta6, Release Date: 2020-07-25'
First we set up the notebook to display mathematical objects using LaTeX rendering:
%display latex
and we initialize a time counter for benchmarking:
import time
comput_time0 = time.perf_counter()
We declare the Schwarzschild spacetime as a 4-dimensional Lorentzian manifold:
M = Manifold(4, 'M', r'\mathcal{M}', structure='Lorentzian')
M
print(M)
4-dimensional Lorentzian manifold M
The spacetime manifold $\mathcal{M}$ can be split into 4 regions, corresponding to the 4 quadrants in the Kruskal diagram.Let us denote by $\mathcal{R}_{\mathrm{I}}$ to $\mathcal{R}_{\mathrm{IV}}$ the interiors of these 4 regions. $\mathcal{R}_{\mathrm{I}}$ and $\mathcal{R}_{\mathrm{III}}$ are asymtotically flat regions outside the event horizon; $\mathcal{R}_{\mathrm{II}}$ is inside the future event horizon and $\mathcal{R}_{\mathrm{IV}}$ is inside the past event horizon.
regI = M.open_subset('R_I', r'\mathcal{R}_{\mathrm{I}}')
regII = M.open_subset('R_II', r'\mathcal{R}_{\mathrm{II}}')
regIII = M.open_subset('R_III', r'\mathcal{R}_{\mathrm{III}}')
regIV = M.open_subset('R_IV', r'\mathcal{R}_{\mathrm{IV}}')
regI, regII, regIII, regIV
The parameter $m$ of the Schwarzschild spacetime is declared as a symbolic variable:
m = var('m') ; assume(m>=0)
The standard Boyer-Lindquist coordinates (also called Schwarzschild coordinates) are defined on $\mathcal{R}_{\mathrm{I}}\cup \mathcal{R}_{\mathrm{II}}$
regI_II = regI.union(regII) ; regI_II
X.<t,r,th,ph> = regI_II.chart(r't r:(0,+oo) th:(0,pi):\theta ph:(0,2*pi):\phi')
print(X)
Chart (R_I_union_R_II, (t, r, th, ph))
X
We naturally introduce two subcharts as the restrictions of the chart $X$ to regions $\mathcal{R}_{\mathrm{I}}$ and $\mathcal{R}_{\mathrm{II}}$ respectively. Since, in terms of the Boyer-Lindquist coordinates, $\mathcal{R}_{\mathrm{I}}$ (resp. $\mathcal{R}_{\mathrm{II}}$) is defined by $r>2m$ (resp. $r<2m$), we set
X_I = X.restrict(regI, r>2*m) ; X_I
X_II = X.restrict(regII, r<2*m) ; X_II
At this stage, the manifold's atlas has 3 charts:
M.atlas()
M.default_chart()
Three vector frames have been defined on the manifold: the three coordinate frames:
M.frames()
print(M.default_frame())
Coordinate frame (R_I_union_R_II, (d/dt,d/dr,d/dth,d/dph))
M.default_frame().domain()
The metric tensor is obtained as follows:
g = M.metric()
print(g)
Lorentzian metric g on the 4-dimensional Lorentzian manifold M
One has to set its components in the coordinate frame associated with Schwarzschild coordinates, which is the current manifold's default frame:
g[0,0], g[1,1] = -(1-2*m/r), 1/(1-2*m/r)
g[2,2], g[3,3] = r^2, (r*sin(th))^2
g.display()
As an example, let us consider a vector field defined only on $\mathcal{R}_{\mathrm{I}}$:
v = regI.vector_field('v')
v[0] = 1
v[1] = 1 - 2*m/r
# unset components are zero
v.display()
v.domain()
g.domain()
Since $\mathcal{R}_{\mathrm{I}}\subset \mathcal{M}$, it is possible to apply $g$ to $v$:
s = g(v,v)
print(s)
Scalar field g(v,v) on the Open subset R_I of the 4-dimensional Lorentzian manifold M
s.display() # v is indeed a null vector
The Levi-Civita connection $\nabla$ associated with $g$:
nab = g.connection()
print(nab)
Levi-Civita connection nabla_g associated with the Lorentzian metric g on the 4-dimensional Lorentzian manifold M
Let us verify that the covariant derivative of $g$ with respect to $\nabla$ vanishes identically:
nab(g) == 0
nab(g).display()
The nonzero Christoffel symbols of $g$ with respect to Schwarzschild coordinates, skipping those that can be deduced by symmetry:
g.christoffel_symbols_display()
The Riemann curvature tensor associated with $g$:
R = g.riemann()
print(R)
Tensor field Riem(g) of type (1,3) on the 4-dimensional Lorentzian manifold M
The Weyl conformal tensor associated with $g$:
C = g.weyl()
print(C)
Tensor field C(g) of type (1,3) on the 4-dimensional Lorentzian manifold M
C.display()
The Ricci tensor associated with $g$:
Ric = g.ricci()
print(Ric)
Field of symmetric bilinear forms Ric(g) on the 4-dimensional Lorentzian manifold M
Let us check that the Schwarzschild metric is a solution of the vacuum Einstein equation:
Ric == 0
Ric.display() # another view of the above
Contrary to the Ricci tensor, the Riemann tensor does not vanish:
R[:]
R.display()
The nonzero components of the Riemann tensor, skipping those that can be deduced by antisymmetry:
R.display_comp(only_nonredundant=True)
Ric[:]
Since the Ricci tensor is zero, the Weyl tensor is of course equal to the Riemann tensor:
C == R
Let us check the Bianchi identity $\nabla_p R^i_{\ \, j kl} + \nabla_k R^i_{\ \, jlp} + \nabla_l R^i_{\ \, jpk} = 0$:
DR = nab(R)
print(DR)
Tensor field nabla_g(Riem(g)) of type (1,4) on the 4-dimensional Lorentzian manifold M
from __future__ import print_function # because the print below is valid only in Python3
for i in M.irange():
for j in M.irange():
for k in M.irange():
for l in M.irange():
for p in M.irange():
print(DR[i,j,k,l,p] + DR[i,j,l,p,k] + DR[i,j,p,k,l], end=' ')
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Let us check that if we turn the first $+$ sign into a $-$ one, the Bianchi identity does no longer hold:
for i in M.irange():
for j in M.irange():
for k in M.irange():
for l in M.irange():
for p in M.irange():
print(DR[i,j,k,l,p] - DR[i,j,l,p,k] + DR[i,j,p,k,l], end=' ')
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -12*m/(2*m*r^3 - r^4) 0 0 12*m/(2*m*r^3 - r^4) 0 0 0 0 0 0 0 0 0 0 0 0 0 -6*m/r^2 0 0 0 0 0 6*m/r^2 0 0 0 0 0 0 0 0 0 0 -6*m*sin(th)^2/r^2 0 0 0 0 0 0 0 0 6*m*sin(th)^2/r^2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -6*m/r^2 0 0 0 0 0 6*m/r^2 0 0 0 0 0 0 0 0 -6*m/r^2 0 0 6*m/r^2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -6*m*sin(th)^2/r^2 0 0 0 0 0 0 0 0 6*m*sin(th)^2/r^2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -6*m*sin(th)^2/r^2 0 0 6*m*sin(th)^2/r^2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -12*(2*m^2 - m*r)/r^5 0 0 12*(2*m^2 - m*r)/r^5 0 0 0 0 0 0 0 0 0 0 0 0 0 -6*(4*m^3 - 4*m^2*r + m*r^2)/r^4 0 0 0 0 0 6*(4*m^3 - 4*m^2*r + m*r^2)/r^4 0 0 0 0 0 0 0 0 0 0 -6*(4*m^3 - 4*m^2*r + m*r^2)*sin(th)^2/r^4 0 0 0 0 0 0 0 0 6*(4*m^3 - 4*m^2*r + m*r^2)*sin(th)^2/r^4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -6*m/r^2 0 0 6*m/r^2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6*(2*m^2 - m*r)*sin(th)^2/r 0 0 -6*(2*m^2 - m*r)*sin(th)^2/r 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -6*m*sin(th)^2/r^2 0 0 0 0 0 6*m*sin(th)^2/r^2 0 0 0 0 0 0 0 0 0 0 0 0 0 -6*(2*m^2 - m*r)*sin(th)^2/r 0 0 6*(2*m^2 - m*r)*sin(th)^2/r 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6*(2*m^2 - m*r)/r^5 0 0 0 0 0 -6*(2*m^2 - m*r)/r^5 0 0 0 0 0 0 0 0 6*(2*m^2 - m*r)/r^5 0 0 -6*(2*m^2 - m*r)/r^5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -6*m/(2*m*r^3 - r^4) 0 0 6*m/(2*m*r^3 - r^4) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6*m*sin(th)^2/r^2 0 0 -6*m*sin(th)^2/r^2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12*m*sin(th)^2/r^2 0 0 -12*m*sin(th)^2/r^2 0 0 0 0 0 0 0 0 6*m*sin(th)^2/r^2 0 0 0 0 0 -6*m*sin(th)^2/r^2 0 0 0 0 0 0 0 0 -6*m*sin(th)^2/r^2 0 0 6*m*sin(th)^2/r^2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6*(2*m^2 - m*r)/r^5 0 0 0 0 0 0 0 0 -6*(2*m^2 - m*r)/r^5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6*(2*m^2 - m*r)/r^5 0 0 -6*(2*m^2 - m*r)/r^5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -6*m/(2*m*r^3 - r^4) 0 0 0 0 0 6*m/(2*m*r^3 - r^4) 0 0 0 0 0 0 0 0 0 0 0 0 0 -6*m/r^2 0 0 6*m/r^2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -12*m/r^2 0 0 12*m/r^2 0 0 0 0 0 0 0 0 -6*m/r^2 0 0 0 0 0 6*m/r^2 0 0 0 0 0 0 0 0 6*m/r^2 0 0 -6*m/r^2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Let us first introduce tensor $R^\flat$, of components $R_{ijkl} = g_{ip} R^p_{\ \, jkl}$:
dR = R.down(g)
print(dR)
Tensor field of type (0,4) on the 4-dimensional Lorentzian manifold M
and tensor $R^\sharp$, of components $R^{ijkl} = g^{jp} g^{kq} g^{lr} R^i_{\ \, pqr}$:
uR = R.up(g)
print(uR)
Tensor field of type (4,0) on the 4-dimensional Lorentzian manifold M
The Kretschmann scalar is $K := R^{ijkl} R_{ijkl}$:
Kr = 0
for i in M.irange():
for j in M.irange():
for k in M.irange():
for l in M.irange():
Kr += uR[i,j,k,l]*dR[i,j,k,l]
Kr
Instead of the above loops, the Kretschmann scalar can also be computed by means of the method contract()
, asking that the contraction takes place on all indices (positions 0, 1, 2, 3):
Kr = uR.contract(0, 1, 2, 3, dR, 0, 1, 2, 3)
Kr.expr()
The contraction can also be performed by means of index notations:
Kr = uR['^{ijkl}']*dR['_{ijkl}']
Kr.expr()
Let us introduce new coordinates on the spacetime manifold: the ingoing Eddington-Finkelstein ones:
X_EF.<v,r,th,ph> = regI_II.chart(r'v r:(0,+oo) th:(0,pi):\theta ph:(0,2*pi):\varphi')
print(X_EF); X_EF
Chart (R_I_union_R_II, (v, r, th, ph))
The change from Schwarzschild (Boyer-Lindquist) coordinates to the ingoing Eddington-Finkelstein ones:
ch_BL_EF_I = X_I.transition_map(X_EF, [t+r+2*m*ln(r/(2*m)-1), r, th, ph],
restrictions2=r>2*m)
print(ch_BL_EF_I) ; ch_BL_EF_I
Change of coordinates from Chart (R_I, (t, r, th, ph)) to Chart (R_I, (v, r, th, ph))
ch_BL_EF_I.display()
X_EF_I = X_EF.restrict(regI) ; X_EF_I
ch_BL_EF_II = X_II.transition_map(X_EF, [t+r+2*m*ln(1-r/(2*m)), r, th, ph],
restrictions2=r<2*m)
print(ch_BL_EF_II) ; ch_BL_EF_II
Change of coordinates from Chart (R_II, (t, r, th, ph)) to Chart (R_II, (v, r, th, ph))
ch_BL_EF_II.display()
X_EF_II = X_EF.restrict(regII) ; X_EF_II
The manifold's atlas has now 6 charts:
M.atlas()
The default chart is 'BL':
M.default_chart()
The change from Eddington-Finkelstein coordinates to the Schwarzschild (Boyer-Lindquist) ones, computed as the inverse of ch_BL_EF
:
ch_EF_BL_I = ch_BL_EF_I.inverse()
print(ch_EF_BL_I)
Change of coordinates from Chart (R_I, (v, r, th, ph)) to Chart (R_I, (t, r, th, ph))
ch_EF_BL_I.display()
ch_EF_BL_II = ch_BL_EF_II.inverse()
print(ch_EF_BL_II)
Change of coordinates from Chart (R_II, (v, r, th, ph)) to Chart (R_II, (t, r, th, ph))
ch_EF_BL_II.display()
At this stage, 6 vector frames have been defined on the manifold: the 6 coordinate frames associated with the various charts:
M.frames()
The default frame is:
M.default_frame()
The coframes are the duals of the defined vector frames:
M.coframes()
If not specified, tensor components are assumed to refer to the manifold's default frame. For instance, for the metric tensor:
g.display()
g[:]
The tensor components in the frame associated with Eddington-Finkelstein coordinates in Region I are obtained by providing the frame to the function display()
:
g.display(X_EF_I.frame())
They are also returned by the method comp(), with the frame as argument:
g.comp(X_EF_I.frame())[:]
or, as a schortcut,
g[X_EF_I.frame(),:]
Similarly, the metric components in the frame associated with Eddington-Finkelstein coordinates in Region II are obtained by
g.display(X_EF_II.frame())
Note that their form is identical to that in Region I.
Let us perform the plot in Region I:
X_I.plot(X_EF_I, ranges={t:(0,8), r:(2.1,10)}, fixed_coords={th:pi/2,ph:0},
ambient_coords=(r,v), style={t:'--', r:'-'}, parameters={m:1})
Let us introduce the orthonormal tetrad $(e_\alpha)$ associated with the static observers in Schwarzschild spacetime, i.e. the observers whose worldlines are parallel to the timelike Killing vector in the Region I.
The orthonormal tetrad is defined via a tangent-space automorphism that relates it to the Boyer-Lindquist coordinate frame in Region I:
ch_to_stat = regI.automorphism_field()
ch_to_stat[0,0], ch_to_stat[1,1] = 1/sqrt(1-2*m/r), sqrt(1-2*m/r)
ch_to_stat[2,2], ch_to_stat[3,3] = 1/r, 1/(r*sin(th))
ch_to_stat[:]
e = X_I.frame().new_frame(ch_to_stat, 'e')
print(e)
Vector frame (R_I, (e_0,e_1,e_2,e_3))
At this stage, 7 vector frames have been defined on the manifold $\mathcal{M}$:
M.frames()
The first vector of the tetrad is the static observer 4-velocity:
print(e[0])
Vector field e_0 on the Open subset R_I of the 4-dimensional Lorentzian manifold M
e[0].display()
As any 4-velocity, it is a unit timelike vector:
g(e[0],e[0]).expr()
Equivalently, we may use the method dot
to get the scalar product (since $g$ is the default metric on $\mathcal{M}$):
e[0].dot(e[0]).expr()
Let us check that the tetrad $(e_\alpha)$ is orthonormal:
for i in M.irange():
for j in M.irange():
print(e[i].dot(e[j]).expr(), end=' ')
print("")
-1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
Another view of the above result:
g[e,:]
or, equivalently,
g.display(e)
The expression of the 4-velocity $e_0$ and the vector $e_1$ in terms of the frame associated with Eddington-Finkelstein coordinates:
e[0].display(X_EF_I.frame())
e[1].display(X_EF_I.frame())
Contrary to vectors of a coordinate frame, the vectors of the tetrad $e$ do not commute: their structure coefficients are not identically zero:
e.structure_coeff()[:]
Equivalently, the Lie derivative of one vector along another one is not necessarily zero:
e[0].lie_der(e[1]).display(e)
The curvature 2-form $\Omega^0_{\ \, 1}$ associated with the tetrad $(e_\alpha)$:
g.connection().curvature_form(0,1,e).display(X_I.frame())
Let us now introduce the Kruskal-Szekeres coordinates $(U,V,\theta,\varphi)$ on the spacetime manifold, via the standard transformation expressing them in terms of the Boyer-Lindquist coordinates $(t,r,\theta,\varphi)$:
M0 = regI.union(regII).union(regIII).union(regIV) ; M0
X_KS.<U,V,th,ph> = M0.chart(r'U V th:(0,pi):\theta ph:(0,2*pi):\varphi')
X_KS.add_restrictions(V^2 < 1 + U^2)
X_KS
X_KS_I = X_KS.restrict(regI, [U>0, V<U, V>-U]) ; X_KS_I
ch_BL_KS_I = X_I.transition_map(X_KS_I, [sqrt(r/(2*m)-1)*exp(r/(4*m))*cosh(t/(4*m)),
sqrt(r/(2*m)-1)*exp(r/(4*m))*sinh(t/(4*m)),
th, ph])
print(ch_BL_KS_I)
ch_BL_KS_I.display()
Change of coordinates from Chart (R_I, (t, r, th, ph)) to Chart (R_I, (U, V, th, ph))
X_KS_II = X_KS.restrict(regII, [V>0, V>U, V>-U]) ; X_KS_II
ch_BL_KS_II = X_II.transition_map(X_KS_II, [sqrt(1-r/(2*m))*exp(r/(4*m))*sinh(t/(4*m)),
sqrt(1-r/(2*m))*exp(r/(4*m))*cosh(t/(4*m)),
th, ph])
print(ch_BL_KS_II)
ch_BL_KS_II.display()
Change of coordinates from Chart (R_II, (t, r, th, ph)) to Chart (R_II, (U, V, th, ph))
We draw the Boyer-Lindquist chart in Region I (red) and Region II (green), with lines of constant $r$ being dashed:
graphI = X_I.plot(X_KS, ranges={t:(-12,12), r:(2.001,5)}, number_values={t:17, r:9},
fixed_coords={th:pi/2,ph:0}, ambient_coords=(U,V),
style={t:'--', r:'-'}, parameters={m:1})
graphII = X_II.plot(X_KS, ranges={t:(-12,12), r:(0.001,1.999)}, number_values={t:17, r:9},
fixed_coords={th:pi/2,ph:0}, ambient_coords=(U,V),
style={t:'--', r:'-'}, color='green', parameters={m:1})
show(graphI+graphII, xmin=-3, xmax=3, ymin=-3, ymax=3, axes_labels=['$U$', '$V$'])
We may add to the graph the singularity $r=0$ as a Boyer-Lindquist chart plot with $(r,\theta,\phi)$ fixed at $(0, \pi/2, 0)$. Similarly, we add the event horizon $r=2m$ as a Boyer-Lindquist chart plot with $(r,\theta,\phi)$ fixed at $(2.00001m, \pi/2, 0)$:
graph_r0 = X_II.plot(X_KS, fixed_coords={r:0, th:pi/2, ph:0}, ambient_coords=(U,V),
color='yellow', thickness=3, parameters={m:1})
graph_r2 = X_I.plot(X_KS, ranges={t:(-40,40)}, fixed_coords={r:2.00001, th:pi/2, ph:0},
ambient_coords=(U,V), color='black', thickness=2, parameters={m:1})
show(graphI+graphII+graph_r0+graph_r2, xmin=-3, xmax=3, ymin=-3, ymax=3,
axes_labels=['$U$', '$V$'])
We first get the change of coordinates $(v,r,\theta,\phi) \mapsto (U,V,\theta,\phi)$ by composing the change $(v,r,\theta,\phi) \mapsto (t,r,\theta,\phi)$ with $(t,r,\theta,\phi) \mapsto (U,V,\theta,\phi)$:
ch_EF_KS_I = ch_BL_KS_I * ch_EF_BL_I
ch_EF_KS_I
ch_EF_KS_I.display()
ch_EF_KS_II = ch_BL_KS_II * ch_EF_BL_II
ch_EF_KS_II
graphI_EF = X_EF_I.plot(X_KS, ranges={v:(-12,12), r:(2.001,5)}, number_values={v:17, r:9},
fixed_coords={th:pi/2,ph:0}, ambient_coords=(U,V),
style={v:'--', r:'-'}, parameters={m:1})
graphII_EF = X_EF_II.plot(X_KS, ranges={v:(-12,12), r:(0.001,1.999)},
number_values={v:17, r:9},
fixed_coords={th:pi/2,ph:0}, ambient_coords=(U,V),
style={v:'--', r:'-'}, color='green', parameters={m:1})
show(graphI_EF+graphII_EF+graph_r0+graph_r2, xmin=-3, xmax=3, ymin=-3, ymax=3,
axes_labels=['$U$', '$V$'])
There are now 9 charts defined on the spacetime manifold:
M.atlas()
len(M.atlas())
There are 8 explicit coordinate changes (the coordinate change KS $\rightarrow$ BL is not known in explicit form):
M.coord_changes()
len(M.coord_changes())
There are 10 vector frames (among which 9 coordinate frames):
M.frames()
len(M.frames())
There are 14 fields of tangent space automorphisms expressing the changes of coordinate bases and tetrad:
len(M.changes_of_frame())
Thanks to these changes of frames, the components of the metric tensor with respect to the Kruskal-Szekeres can be computed by the method display()
and are found to be:
g.display(X_KS_I.frame())
g[X_KS_I.frame(),:]
g.display(X_KS_II.frame())
The first vector of the orthonormal tetrad $e$ expressed on the Kruskal-Szekeres frame:
e[0].display(X_KS_I.frame())
The Riemann curvature tensor in terms of the Kruskal-Szekeres coordinates:
g.riemann().display(X_KS_I.frame())
g.riemann().display_comp(X_KS_I.frame(), only_nonredundant=True)
The curvature 2-form $\Omega^0_{\ \, 1}$ associated to the Kruskal-Szekeres coordinate frame:
om = g.connection().curvature_form(0,1, X_KS_I.frame())
print(om)
2-form curvature (0,1) of connection nabla_g w.r.t. Coordinate frame (R_I, (d/dU,d/dV,d/dth,d/dph)) on the Open subset R_I of the 4-dimensional Lorentzian manifold M
om.display(X_KS_I.frame())
Let us now introduce isotropic coordinates $(t,\bar{r},\theta,\varphi)$ on the spacetime manifold:
regI_III = regI.union(regIII) ; regI_III
X_iso.<t,ri,th,ph> = regI_III.chart(r't ri:(0,+oo):\bar{r} th:(0,pi):\theta ph:(0,2*pi):\varphi')
print(X_iso) ; X_iso
Chart (R_I_union_R_III, (t, ri, th, ph))
X_iso_I = X_iso.restrict(regI, ri>m/2) ; X_iso_I
The transformation from the isotropic coordinates to the Boyer-Lindquist ones:
assume(2*ri>m) # we consider only region I
ch_iso_BL_I = X_iso_I.transition_map(X_I, [t, ri*(1+m/(2*ri))^2, th, ph])
print(ch_iso_BL_I)
ch_iso_BL_I.display()
Change of coordinates from Chart (R_I, (t, ri, th, ph)) to Chart (R_I, (t, r, th, ph))
assume(r>2*m) # we consider only region I
ch_iso_BL_I.set_inverse(t, (r-m+sqrt(r*(r-2*m)))/2, th, ph, verbose=True)
Check of the inverse coordinate transformation: t == t *passed* ri == ri *passed* th == th *passed* ph == ph *passed* t == t *passed* r == r *passed* th == th *passed* ph == ph *passed*
ch_iso_BL_I.inverse().display()
At this stage, 11 charts have been defined on the manifold $\mathcal{M}$:
M.atlas()
len(M.atlas())
12 vector frames have been defined on $\mathcal{M}$: 11 coordinate bases and the tetrad $(e_\alpha)$:
M.frames()
len(M.frames())
The components of the metric tensor in terms of the isotropic coordinates are given by
g.display(X_iso_I.frame(), X_iso_I)
The $g_{00}$ component can be factorized:
g[X_iso_I.frame(), 0,0, X_iso_I]
g[X_iso_I.frame(), 0,0, X_iso_I].factor()
Let us also factor the other components:
for i in range(1,4):
g[X_iso_I.frame(), i,i, X_iso_I].factor()
The output of the display()
command looks nicer:
g.display(X_iso_I.frame(), X_iso_I)
Expression of the tetrad associated with the static observer in terms of the isotropic coordinate basis:
e[0].display(X_iso_I.frame(), X_iso_I)
e[1].display(X_iso_I.frame(), X_iso_I)
e[2].display(X_iso_I.frame(), X_iso_I)
e[3].display(X_iso_I.frame(), X_iso_I)
print("Total elapsed time: {} s".format(time.perf_counter() - comput_time0))
Total elapsed time: 253.3007331409999 s