Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download

Github repo cloud-examples: https://github.com/sagemath/cloud-examples

Views: 8060
License: MIT
%auto typeset_mode(True, display=False) ︠9251b803-1c83-476f-9414-be051e54c19di︠ %html <div id="cell_text_154" class="text_cell"> <h1 style="text-align: center;">Schwarzschild spacetime</h1> <p>This worksheet demonstrates a few capabilities of <a href="http://sagemanifolds.obspm.fr/">SageManifolds</a> (version 0.8) in computations regarding Schwarzschild spacetime.</p> <p>It is released under the GNU General Public License version 3.</p> <p>(c) Eric Gourgoulhon, Michal Bejger (2015)</p> <p><em><span style="color: #008080;">The corresponding worksheet file can be downloaded from</span><span style="color: #ff6600;"><span style="color: #0000ff;"> <a href="http://sagemanifolds.obspm.fr/examples/sws/SM_Schwarzschild.sws"><span style="color: #0000ff;">here</span></a></span></span></em></p> <p><em><span style="color: #ff6600;"><span style="color: #0000ff;"><span style="color: #0000ff;"><br /></span></span></span></em></p> </div> <h2>Spacetime manifold</h2> <p>We declare the Schwarzschild spacetime as a 4-dimensional differentiable manifold:</p>

Schwarzschild spacetime

This worksheet demonstrates a few capabilities of SageManifolds (version 0.8) in computations regarding Schwarzschild spacetime.

It is released under the GNU General Public License version 3.

(c) Eric Gourgoulhon, Michal Bejger (2015)

The corresponding worksheet file can be downloaded from here


Spacetime manifold

We declare the Schwarzschild spacetime as a 4-dimensional differentiable manifold:

M = Manifold(4, 'M', r'\mathcal{M}') ; M
print M

The spacetime manifold M\mathcal{M} can be split into 4 regions, corresponding to the 4 quadrants in the Kruskal diagram.Let us denote by RI\mathcal{R}_{\mathrm{I}} to RIV\mathcal{R}_{\mathrm{IV}} the interiors of these 4 regions. RI\mathcal{R}_{\mathrm{I}} and  RIII\mathcal{R}_{\mathrm{III}}  are asymtotically flat regions outside the event horizon;  RII\mathcal{R}_{\mathrm{II}} is inside the future event horizon and RIV\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 mm of the Schwarzschild spacetime is declared as a symbolic variable:

m = var('m') ; assume(m>=0)

Boyer-Lindquist coordinates

The standard Boyer-Lindquist coordinates (also called Schwarzschild coordinates) are defined on RIRII\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
X

We naturally introduce two subcharts as the restrictions of the chart XX to regions RI\mathcal{R}_{\mathrm{I}} and RII\mathcal{R}_{\mathrm{II}} respectively. Since, in terms of the Boyer-Lindquist coordinates, RI\mathcal{R}_{\mathrm{I}} (resp. RII\mathcal{R}_{\mathrm{II}}) is defined by r>2mr>2m (resp. r<2mr<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()
M.default_frame().domain()

Metric tensor

The metric tensor is defined as follows:

g = M.lorentz_metric('g')

The metric tensor is set by 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 RI\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 RIM\mathcal{R}_{\mathrm{I}}\subset \mathcal{M}, it is possible to apply gg to vv:

s = g(v,v) ; print s
s.display() # v is indeed a null vector

Levi-Civita Connection

 

The Levi-Civita connection \nabla associated with gg:

nab = g.connection() ; print nab

Let us verify that the covariant derivative of gg with respect to \nabla vanishes identically:

nab(g) == 0
nab(g).display()

The nonzero Christoffel symbols of gg with respect to Schwarzschild coordinates, skipping those that can be deduced by symmetry:

g.christoffel_symbols_display()

Curvature

The Riemann curvature tensor associated with gg:

R = g.riemann() ; print R

The Weyl conformal tensor associated with gg:

C = g.weyl() ; print C
C.display()

The Ricci tensor associated with gg:

Ric = g.ricci() ; print Ric

Einstein equation

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

Bianchi identity

Let us check the Bianchi identity pR jkli+kR jlpi+lR jpki=0\nabla_p R^i_{\ \, j kl} + \nabla_k R^i_{\ \, jlp} + \nabla_l R^i_{\ \, jpk} = 0:

DR = nab(R) ; print DR
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] ,

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] ,

Kretschmann scalar

Let us first introduce tensor RR^\flat, of components Rijkl=gipR jklpR_{ijkl} = g_{ip} R^p_{\ \, jkl}:

dR = R.down(g) ; print dR

and tensor RR^\sharp, of components Rijkl=gjpgkqglrR pqriR^{ijkl} = g^{jp} g^{kq} g^{lr} R^i_{\ \, pqr}:

uR = R.up(g) ; print uR

The Kretschmann scalar is K:=RijklRijklK := 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 contract() method, 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()

Eddington-Finkelstein coordinates

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

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
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
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
ch_EF_BL_I.display()
ch_EF_BL_II = ch_BL_EF_II.inverse() ; print ch_EF_BL_II
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.

Plot of the Boyer-Lindquist coordinates in terms of the Eddington-Finkelstein ones

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})

Tetrad of the static observer

Let us introduce the orthonormal tetrad (eα)(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

At this stage, 7 vector frames have been defined on the manifold M\mathcal{M}:

M.frames()

The first vector of the tetrad is the static observer 4-velocity:

print e[0]
e[0].display()

As any 4-velocity, it is a unit timelike vector:

g(e[0],e[0]).expr()

Let us check that the tetrad (eα)(e_\alpha) is orthonormal:

for i in M.irange(): for j in M.irange(): print g(e[i],e[j]).expr() , print " "

Another view of the above result:

g[e,:]

or, equivalently,

g.display(e)

The expression of the 4-velocity e0e_0 and the vector e1e_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 ee do not commute: their structure coefficients are not identically zero:

e.structure_coef()[:]

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 Ω 10\Omega^0_{\ \, 1} associated with the tetrad (eα)(e_\alpha):

g.connection().curvature_form(0,1,e).display(X_I.frame())

Kruskal-Szekeres coordinates

Let us now introduce the Kruskal-Szekeres coordinates (U,V,θ,φ)(U,V,\theta,\varphi) on the spacetime manifold, via the standard transformation expressing them in terms of the Boyer-Lindquist coordinates (t,r,θ,φ)(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()
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()

Plot of the Boyer-Lindquist coordinates in terms of the Kruskal ones

We draw the Boyer-Lindquist chart in Region I (red) and Region II (green), with lines of constant rr being dashed:

graphI = X_I.plot(X_KS, ranges={t:(-12,12), r:(2.001,5)}, nb_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)}, nb_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=0r=0 as a Boyer-Lindquist chart plot with (r,θ,ϕ)(r,\theta,\phi) fixed at (0,π/2,0)(0, \pi/2, 0).  Similarly, we add the event horizon r=2mr=2m as a Boyer-Lindquist chart plot with (r,θ,ϕ)(r,\theta,\phi) fixed at (2.00001m,π/2,0)(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='black', 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='yellow', thickness=2, parameters={m:1}) show(graphI+graphII+graph_r0+graph_r2, xmin=-3, xmax=3, ymin=-3, ymax=3, axes_labels=['$U$', '$V$'])

Plot of the Eddington-Finkelstein coordinates in terms of the Kruskal ones

We first get the change of coordinates (v,r,θ,ϕ)(U,V,θ,ϕ)(v,r,\theta,\phi) \mapsto (U,V,\theta,\phi) by composing the change (v,r,θ,ϕ)(t,r,θ,ϕ)(v,r,\theta,\phi) \mapsto (t,r,\theta,\phi) with (t,r,θ,ϕ)(U,V,θ,ϕ)(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)}, nb_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)}, nb_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()

There are 8 explicit coordinate changes (the coordinate change KS \rightarrow BL is not known in explicit form):

M.coord_changes()

There are 10 vector frames (among which 9 coordinate frames):

M.frames()

There are 16 fields of tangent space automorphisms expressing the changes of coordinate bases and tetrad:

len(M.frame_changes())

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 ee 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 Ω 10\Omega^0_{\ \, 1} associated to the Kruskal-Szekeres coordinate frame:

om = g.connection().curvature_form(0,1, X_KS_I.frame()) ; print om
om.display(X_KS_I.frame())

Isotropic coordinates

Let us now introduce isotropic coordinates (t,rˉ,θ,φ)(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
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()
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)
ch_iso_BL_I.inverse().display()

At this stage, 11 charts have been defined on the manifold M\mathcal{M}:

M.atlas()

12 vector frames have been defined on M\mathcal{M}: 11 coordinate bases and the tetrad (eα)(e_\alpha):

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 g00g_{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 factorize 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)