This Jupyter/SageMath notebook is relative to the lectures Geometry and physics of black holes.
It makes use of SageMath differential geometry tools developed through the SageManifolds project.
NB: a version of SageMath at least equal to 9.4 is required to run this notebook:
version()
'SageMath version 10.3, Release Date: 2024-03-19'
First we set up the notebook to display mathematical objects using LaTeX formatting:
%display latex
We declare the spacetime manifold $M$:
M = Manifold(4, 'M', structure='Lorentzian')
print(M)
4-dimensional Lorentzian manifold M
and the spherical coordinates $(t,r,\theta,\phi)$ as a chart on $M$:
XS.<t,r,th,ph> = M.chart(r't r:(0,+oo) th:(0,pi):\theta ph:(0,2*pi):\phi')
XS
XS.coord_range()
In term of these coordinates, we set up Minkowski metric as
g = M.metric()
g[0,0] = -1
g[1,1] = 1
g[2,2] = r^2
g[3,3] = r^2*sin(th)^2
g.display()
Let us introduce the null coordinates $u=t-r$ (retarded time) and $v=t+r$ (advanced time):
XN.<u,v,th,ph> = M.chart(r'u v th:(0,pi):\theta ph:(0,2*pi):\phi',
coord_restrictions=lambda u,v,th,ph: v-u>0)
XN
XN.coord_range()
XS_to_XN = XS.transition_map(XN, [t-r, t+r, th, ph])
XS_to_XN.display()
XS_to_XN.inverse().display()
In terms of the null coordinates $(u,v,\theta,\phi)$, the Minkowski metric writes
g.display(XN)
For a better display, let us factor the metric components:
g.apply_map(factor, frame=XN.frame(), chart=XN,
keep_other_components=True)
g.display(XN)
Let us plot the coordinate grid $(u,v)$ in terms of the coordinates $(t,r)$:
graph = XN.plot(XS, ambient_coords=(r,t), fixed_coords={th: pi/2, ph: pi},
number_values=17, plot_points=200, color='green',
style={u: '--', v: '-'}, thickness=1.5)
graph
show(graph, xmin=0, xmax=4, ymin=0, ymax=4, aspect_ratio=1)
graph.save("glo_null_coord.pdf", xmin=0, xmax=4, ymin=0, ymax=4,
aspect_ratio=1)
Instead of $(u,v)$, which span $\mathbb{R}$, let consider the coordinates $U = \mathrm{atan}\, u$ and $V = \mathrm{atan}\, v$, which span $\left(-\frac{\pi}{2}, \frac{\pi}{2}\right)$:
graph = plot(atan(u), (u,-6, 6), thickness=2, axes_labels=[r'$u$', r'$U$']) \
+ line([(-6,-pi/2), (6,-pi/2)], linestyle='--') \
+ line([(-6,pi/2), (6,pi/2)], linestyle='--')
show(graph, aspect_ratio=1)
graph.save('glo_atan.pdf', aspect_ratio=1)
XNC.<U,V,th,ph> = M.chart(r'U:(-pi/2,pi/2) V:(-pi/2,pi/2) th:(0,pi):\theta ph:(0,2*pi):\phi',
coord_restrictions=lambda U,V,th,ph: V-U>0)
XNC
XNC.coord_range()
XN_to_XNC = XN.transition_map(XNC, [atan(u), atan(v), th, ph])
XN_to_XNC.display()
XN_to_XNC.inverse().display()
Expressed in terms of the coordinates $(U,V,\theta,\phi)$, the metric tensor is
g.display(XNC)
Again, for a better display, we may factor the metric components:
g.apply_map(factor, frame=XNC.frame(), chart=XNC,
keep_other_components=True)
g.display(XNC)
Let us call $\Omega^{-2}$ the common factor:
Omega = M.scalar_field({XNC: 2*cos(U)*cos(V)}, name='Omega',
latex_name=r'\Omega')
Omega.display()
Omega.display(XS)
We introduce the metric $\tilde g = \Omega^2 g$:
gt = M.lorentzian_metric('gt', latex_name=r'\tilde{g}')
gt.set(Omega^2*g)
gt.display(XNC)
Clearly the metric components ${\tilde g}_{\theta\theta}$ and ${\tilde g}_{\phi\phi}$ can be simplified further. Let us do it by hand, by extracting the symbolic expression via expr()
:
g22 = gt[XNC.frame(), 2, 2, XNC].expr()
g22
g22_simpl = g22.factor().reduce_trig()
g22_simpl
g33st = gt[XNC.frame(), 3, 3, XNC].expr() / sin(th)^2
g33st
g33st.expand_trig()
g33_simpl = g33st.factor().reduce_trig() * sin(th)^2
g33_simpl
gt.add_comp(XNC.frame())[2,2, XNC] = g22_simpl
gt.add_comp(XNC.frame())[3,3, XNC] = g33_simpl
Hence the final form of the conformal metric in terms of the compactified null coordinates:
gt.display(XNC)
In terms of the non-compactified null coordinates $(u,v,\theta,\phi)$:
gt.display(XN)
gt.apply_map(factor, frame=XN.frame(), chart=XN,
keep_other_components=True)
gt.display(XN)
and in terms of the default coordinates $(t,r,\theta,\phi)$:
gt.display()
gt.apply_map(factor, keep_other_components=True)
gt.display()
Let us introduce some coordinates $(\tau,\chi)$ such that the null coordinates $(U,V)$ are respectively half the retarded time $\tau -\chi$ and half the advanced time $\tau+\chi$:
XC.<tau,ch,th,ph> = M.chart(r'tau:(-pi,pi):\tau ch:(0,pi):\chi th:(0,pi):\theta ph:(0,2*pi):\phi',
coord_restrictions=lambda tau,ch,th,ph: [tau<pi-ch, tau>ch-pi])
XC
XC.coord_range()
XC_to_XNC = XC.transition_map(XNC, [(tau-ch)/2, (tau+ch)/2, th, ph])
XC_to_XNC.display()
XC_to_XNC.inverse().display()
The conformal metric takes then the form of the standard metric on the Einstein cylinder $\mathbb{R}\times\mathbb{S}^3$:
gt.display(XC)
The square of the conformal factor expressed in all the coordinates introduced so far:
(Omega^2).display()
The transition map $(t,r,\theta,\phi) \mapsto (\tau,\chi,\theta,\phi)$ is obtained by combining the various transition maps obtained so far:
XS_to_XC = M.coord_change(XNC, XC) * M.coord_change(XN, XNC) * M.coord_change(XS, XN)
XS_to_XC.display()
The inverse transitin map:
XC_to_XS = M.coord_change(XN, XS) * M.coord_change(XNC, XN) * M.coord_change(XC, XNC)
XC_to_XS.display()
The expressions for $t$ and $r$ can be simplified via reduce_trig
:
t_c = XC_to_XS(tau,ch,th,ph)[0]
r_c = XC_to_XS(tau,ch,th,ph)[1]
XS_to_XC.set_inverse(t_c.reduce_trig(), r_c.reduce_trig(), th, ph)
Check of the inverse coordinate transformation: t == t *passed* r == r *passed* th == th *passed* ph == ph *passed* tau == arctan((sin(ch) + sin(tau))/(cos(ch) + cos(tau))) + arctan(-(sin(ch) - sin(tau))/(cos(ch) + cos(tau))) **failed** ch == arctan((sin(ch) + sin(tau))/(cos(ch) + cos(tau))) - arctan(-(sin(ch) - sin(tau))/(cos(ch) + cos(tau))) **failed** th == th *passed* ph == ph *passed* NB: a failed report can reflect a mere lack of simplification.
XC_to_XS = XS_to_XC.inverse()
XC_to_XS.display()
Let us draw the coordinate grid $(t,r)$ in terms of the coordinates $(\tau,\chi)$:
graphXS = XS.plot(XC, ambient_coords=(ch, tau), fixed_coords={th: pi/2, ph: pi},
max_range=30, number_values=51, plot_points=250,
color={t: 'red', r: 'grey'})
graph_i0 = circle((pi,0), 0.05, fill=True, color='grey') + \
text(r"$i^0$", (3.3, 0.2), fontsize=18, color='grey')
graph_ip = circle((0,pi), 0.05, fill=True, color='red') + \
text(r"$i^+$", (0.25, 3.3), fontsize=18, color='red')
graph_im = circle((0,-pi), 0.05, fill=True, color='red') + \
text(r"$i^-$", (0.25, -3.3), fontsize=18, color='red')
graph_Ip = line([(0,pi), (pi,0)], color='green', thickness=2) + \
text(r"$\mathscr{I}^+$", (1.8, 1.8), fontsize=18, color='green')
graph_Im = line([(0,-pi), (pi,0)], color='green', thickness=2) + \
text(r"$\mathscr{I}^-$", (1.8, -1.8), fontsize=18, color='green')
graph = graphXS + graph_i0 + graph_ip + graph_im + graph_Ip + graph_Im
show(graph, figsize=8)
graph.save('glo_conf_diag_Mink.pdf', figsize=8)
Some blow-up near $i^0$:
graph = XS.plot(XC, ambient_coords=(ch, tau), fixed_coords={th: pi/2, ph: pi},
max_range=100, number_values=41, plot_points=200,
color={t: 'red', r: 'grey'})
graph += circle((pi,0), 0.005, fill=True, color='grey') + \
text(r"$i^0$", (pi, 0.02), fontsize=18, color='grey')
show(graph, xmin=3., xmax=3.2, ymin=-0.2, ymax=0.2, aspect_ratio=1)
To produce a more satisfactory figure, let us use some logarithmic radial coordinate:
XL.<t, rh, th, ph> = M.chart(r't rh:\rho th:(0,pi):\theta ph:(0,2*pi):\phi')
XL
XS_to_XL = XS.transition_map(XL, [t, ln(r), th, ph])
XS_to_XL.display()
XS_to_XL.inverse().display()
XL_to_XC = M.coord_change(XS, XC) * M.coord_change(XL, XS)
XC_to_XL = M.coord_change(XS, XL) * M.coord_change(XC, XS)
graph = XL.plot(XC, ambient_coords=(ch, tau), fixed_coords={th: pi/2, ph: pi},
ranges={t: (-20, 20), rh: (-2, 10)}, number_values=19,
color={t: 'red', rh: 'grey'})
graph += circle((pi,0), 0.005, fill=True, color='grey') + \
text(r"$i^0$", (pi, 0.02), fontsize=18, color='grey')
show(graph, xmin=3., xmax=3.2, ymin=-0.2, ymax=0.2, aspect_ratio=1)
To get a view of the null radial geodesics in the conformal diagram, it suffices to plot the chart $(u,v,\theta,\phi)$ in terms of the chart $(\tau,\chi,\theta,\phi)$. The following plot shows
graphXN = XN.plot(XC, ambient_coords=(ch, tau), fixed_coords={th: pi/2, ph: pi},
number_values=17, plot_points=150, color='green',
style={u: '--', v: '-'}, thickness=1.5)
graph = graphXN + graph_i0 + graph_ip + graph_im + graph_Ip + graph_Im
show(graph, figsize=8)
graph.save('glo_conf_Mink_null.pdf', figsize=8)
The conformal factor expressed in various coordinate systems:
Omega.display()
The expression in terms of $(\tau,\chi,\theta,\phi)$ can be simplified:
Omega_XC = Omega.expr(XC)
Omega_XC
Omega_XC.trig_reduce()
Hence we set
Omega.add_expr(Omega_XC.trig_reduce(), XC)
Omega.display()
A plot of $\Omega$ in terms of the coordinates $(\tau,\chi)$:
graph = plot3d(Omega.expr(XC), (tau,-pi,pi), (ch,0,pi)) \
+ plot3d(0, (tau,-pi,pi), (ch,0,pi), color='yellow', opacity=0.7)
show(graph, aspect_ratio=1, axes_labels=['tau', 'chi', 'Omega'])
show(graph, aspect_ratio=1, viewer='tachyon')
The 1-form $\mathrm{d}\Omega$ is:
dOmega = Omega.differential()
print(dOmega)
1-form dOmega on the 4-dimensional Lorentzian manifold M
dOmega.display()
dOmega.display(XNC)
M.set_default_chart(XNC)
M.set_default_frame(XNC.frame())
dOmega.display()
dOmega1 = M.one_form()
dOmega1[0] = -2*cos(V)*sin(U)
dOmega1[1] = -2*cos(U)*sin(V)
dOmega1.display()
dOmega1.display(XC.frame(), XC)
E = Manifold(4, 'E')
print(E)
4-dimensional differentiable manifold E
XE.<tau,ch,th,ph> = E.chart(r'tau:\tau ch:(0,pi):\chi th:(0,pi):\theta ph:(0,2*pi):\phi')
XE
XE.coord_range()
XC.coord_range()
Phi = M.diff_map(E, {(XC, XE): [tau, ch, th, ph]},
name='Phi', latex_name=r'\Phi')
print(Phi)
Phi.display()
Differentiable map Phi from the 4-dimensional Lorentzian manifold M to the 4-dimensional differentiable manifold E
XS.plot(XE, mapping=Phi, ambient_coords=(ch, tau), fixed_coords={th: pi/2, ph: pi},
plot_points=200, color={t: 'red', r: 'grey'})
R5 = Manifold(5, 'R^5', latex_name=r'\mathbb{R}^5')
print(R5)
5-dimensional differentiable manifold R^5
X5.<tau,W,X,Y,Z> = R5.chart(r'tau:\tau W X Y Z')
X5
Psi = E.diff_map(R5, {(XE, X5): [tau,
cos(ch),
sin(ch)*sin(th)*cos(ph),
sin(ch)*sin(th)*sin(ph),
sin(ch)*cos(th)]},
name='Psi', latex_name=r'\Psi')
print(Psi)
Psi.display()
Differentiable map Psi from the 4-dimensional differentiable manifold E to the 5-dimensional differentiable manifold R^5
The Einstein cylinder:
graphE = XE.plot(X5, ambient_coords=(W,X,tau), mapping=Psi,
fixed_coords={th:pi/2, ph:0.001}, max_range=4,
number_values=9, color='silver', thickness=0.5,
label_axes=False) # phi = 0
graphE += XE.plot(X5, ambient_coords=(W,X,tau), mapping=Psi,
fixed_coords={th:pi/2, ph:pi}, max_range=4,
number_values=9, color='silver', thickness=0.5,
label_axes=False) # phi = pi
show(graphE, aspect_ratio=1, axes_labels=['W', 'X', 'tau'])
The embedding $\Theta:\, M\rightarrow \mathbb{R}^5$ is obtained by composition of the embeddings $\Phi:\, M\rightarrow E$ and $\Psi:\, E\rightarrow \mathbb{R}^5$:
Theta = Psi * Phi
print(Theta)
Theta.display()
Differentiable map from the 4-dimensional Lorentzian manifold M to the 5-dimensional differentiable manifold R^5
graphM = XS.plot(X5, ambient_coords=(W,X,tau), mapping=Theta,
fixed_coords={th:pi/2, ph:0.001}, max_range=30,
number_values=51, plot_points=250, color={t:'red', r:'black'},
label_axes=False) # phi = 0
graphM += XS.plot(X5, ambient_coords=(W,X,tau), mapping=Theta,
fixed_coords={th:pi/2, ph:pi}, max_range=30,
number_values=51, plot_points=250, color={t:'red', r:'black'},
label_axes=False) # phi = pi
show(graphE+graphM, aspect_ratio=1, axes_labels=['W', 'X', 'tau'])
graph = (graphE+graphM).rotate((0,0,1), 0.2)
show(graph, aspect_ratio=(2,2,1), viewer='tachyon',
frame=False, figsize=20)
graph = (graphE+graphM).rotate((0,0,1), pi)
show(graph, aspect_ratio=(2,2,1), viewer='tachyon',
frame=False, figsize=20)
graphMN = XN.plot(X5, ambient_coords=(W,X,tau), mapping=Theta,
fixed_coords={th:pi/2, ph:0.001}, max_range=16,
number_values=21, plot_points=150, color='green',
style={u: '--', v: '-'}, label_axes=False) # phi = 0
graphMN += XN.plot(X5, ambient_coords=(W,X,tau), mapping=Theta,
fixed_coords={th:pi/2, ph:pi}, max_range=16,
number_values=21, plot_points=150, color='green',
style={u: '--', v: '-'}, label_axes=False) # phi = pi
show(graphE+graphMN, aspect_ratio=1, frame=False)
show(graphE+graphMN, aspect_ratio=(2,2,1), viewer='tachyon',
frame=False, figsize=20)