#!/usr/bin/env python # coding: utf-8 # # Conformal completion of Minkowski spacetime # # This Jupyter/SageMath notebook is relative to the lectures # [Geometry and physics of black holes](https://relativite.obspm.fr/blackholes/). # # It makes use of SageMath differential geometry tools developed through the # [SageManifolds](http://sagemanifolds.obspm.fr) project. # *NB:* a version of SageMath at least equal to 9.4 is required to run this notebook: # In[1]: version() # First we set up the notebook to display mathematical objects using LaTeX formatting: # In[2]: get_ipython().run_line_magic('display', 'latex') # ## Spherical coordinates on Minkowski spacetime # # We declare the spacetime manifold $M$: # In[3]: M = Manifold(4, 'M', structure='Lorentzian') print(M) # and the spherical coordinates $(t,r,\theta,\phi)$ as a chart on $M$: # In[4]: XS. = M.chart(r't r:(0,+oo) th:(0,pi):\theta ph:(0,2*pi):\phi') XS # In[5]: XS.coord_range() # In term of these coordinates, we set up Minkowski metric as # In[6]: 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() # ## Null coordinates # # Let us introduce the null coordinates $u=t-r$ (retarded time) and $v=t+r$ (advanced time): # In[7]: XN. = 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 # In[8]: XN.coord_range() # In[9]: XS_to_XN = XS.transition_map(XN, [t-r, t+r, th, ph]) XS_to_XN.display() # In[10]: XS_to_XN.inverse().display() # In terms of the null coordinates $(u,v,\theta,\phi)$, the Minkowski metric writes # In[11]: g.display(XN) # For a better display, let us factor the metric components: # In[12]: 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)$: # In[13]: 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 # In[14]: show(graph, xmin=0, xmax=4, ymin=0, ymax=4, aspect_ratio=1) # In[15]: graph.save("glo_null_coord.pdf", xmin=0, xmax=4, ymin=0, ymax=4, aspect_ratio=1) # ## Compactified null coordinates # # 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)$: # In[16]: 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) # In[17]: graph.save('glo_atan.pdf', aspect_ratio=1) # In[18]: XNC. = 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 # In[19]: XNC.coord_range() # In[20]: XN_to_XNC = XN.transition_map(XNC, [atan(u), atan(v), th, ph]) XN_to_XNC.display() # In[21]: XN_to_XNC.inverse().display() # Expressed in terms of the coordinates $(U,V,\theta,\phi)$, the metric tensor is # In[22]: g.display(XNC) # Again, for a better display, we may factor the metric components: # In[23]: g.apply_map(factor, frame=XNC.frame(), chart=XNC, keep_other_components=True) g.display(XNC) # Let us call $\Omega^{-2}$ the common factor: # In[24]: Omega = M.scalar_field({XNC: 2*cos(U)*cos(V)}, name='Omega', latex_name=r'\Omega') Omega.display() # In[25]: Omega.display(XS) # ## Conformal metric # # We introduce the metric $\tilde g = \Omega^2 g$: # In[26]: 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()`: # In[27]: g22 = gt[XNC.frame(), 2, 2, XNC].expr() g22 # In[28]: g22_simpl = g22.factor().reduce_trig() g22_simpl # In[29]: g33st = gt[XNC.frame(), 3, 3, XNC].expr() / sin(th)^2 g33st # In[30]: g33st.expand_trig() # In[31]: g33_simpl = g33st.factor().reduce_trig() * sin(th)^2 g33_simpl # In[32]: 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: # In[33]: gt.display(XNC) # In terms of the non-compactified null coordinates $(u,v,\theta,\phi)$: # In[34]: gt.display(XN) # In[35]: 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)$: # In[36]: gt.display() # In[37]: gt.apply_map(factor, keep_other_components=True) gt.display() # ## Einstein cylinder coordinates # # 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$: # In[38]: XC. = 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: [tauch-pi]) XC # In[39]: XC.coord_range() # In[40]: XC_to_XNC = XC.transition_map(XNC, [(tau-ch)/2, (tau+ch)/2, th, ph]) XC_to_XNC.display() # In[41]: 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$: # In[42]: gt.display(XC) # The square of the conformal factor expressed in all the coordinates introduced so far: # In[43]: (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: # In[44]: 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: # In[45]: 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`: # In[46]: 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) # In[47]: XC_to_XS = XS_to_XC.inverse() XC_to_XS.display() # ## Conformal Penrose diagram # # Let us draw the coordinate grid $(t,r)$ in terms of the coordinates $(\tau,\chi)$: # In[48]: 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) # In[49]: graph.save('glo_conf_diag_Mink.pdf', figsize=8) # Some blow-up near $i^0$: # In[50]: 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: # In[51]: XL. = M.chart(r't rh:\rho th:(0,pi):\theta ph:(0,2*pi):\phi') XL # In[52]: XS_to_XL = XS.transition_map(XL, [t, ln(r), th, ph]) XS_to_XL.display() # In[53]: XS_to_XL.inverse().display() # In[54]: 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) # In[55]: 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) # ### Null radial geodesics in the conformal diagram # # 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 # - the null geodesics defined by $(u,\theta,\phi) = (u_0, \pi/2,\pi)$ for 17 values of $u_0$ evenly spaced in $[-8,8]$ (dashed lines) # - the null geodesics defined by $(v,\theta,\phi) = (v_0, \pi/2,\pi)$ for 17 values of $v_0$ evenly spaced in $[-8,8]$ (solid lines) # In[56]: 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) # In[57]: graph.save('glo_conf_Mink_null.pdf', figsize=8) # ## Conformal factor # # The conformal factor expressed in various coordinate systems: # In[58]: Omega.display() # The expression in terms of $(\tau,\chi,\theta,\phi)$ can be simplified: # In[59]: Omega_XC = Omega.expr(XC) Omega_XC # In[60]: Omega_XC.trig_reduce() # Hence we set # In[61]: Omega.add_expr(Omega_XC.trig_reduce(), XC) Omega.display() # A plot of $\Omega$ in terms of the coordinates $(\tau,\chi)$: # In[62]: 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']) # In[63]: show(graph, aspect_ratio=1, viewer='tachyon') # ### Differential of the conformal factor # # The 1-form $\mathrm{d}\Omega$ is: # In[64]: dOmega = Omega.differential() print(dOmega) # In[65]: dOmega.display() # In[66]: dOmega.display(XNC) # In[67]: M.set_default_chart(XNC) M.set_default_frame(XNC.frame()) # In[68]: dOmega.display() # In[69]: dOmega1 = M.one_form() dOmega1[0] = -2*cos(V)*sin(U) dOmega1[1] = -2*cos(U)*sin(V) dOmega1.display() # In[70]: dOmega1.display(XC.frame(), XC) # ## Einstein static universe # In[71]: E = Manifold(4, 'E') print(E) # In[72]: XE. = E.chart(r'tau:\tau ch:(0,pi):\chi th:(0,pi):\theta ph:(0,2*pi):\phi') XE # In[73]: XE.coord_range() # In[74]: XC.coord_range() # ## Embedding of $M$ in $E$ # In[75]: Phi = M.diff_map(E, {(XC, XE): [tau, ch, th, ph]}, name='Phi', latex_name=r'\Phi') print(Phi) Phi.display() # In[76]: XS.plot(XE, mapping=Phi, ambient_coords=(ch, tau), fixed_coords={th: pi/2, ph: pi}, plot_points=200, color={t: 'red', r: 'grey'}) # ## Embedding of $E$ in $\mathbb{R}^5$ # In[77]: R5 = Manifold(5, 'R^5', latex_name=r'\mathbb{R}^5') print(R5) # In[78]: X5. = R5.chart(r'tau:\tau W X Y Z') X5 # In[79]: 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() # The Einstein cylinder: # In[80]: 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']) # ## Embedding of $M$ in $\mathbb{R}^5$ # # 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$: # In[81]: Theta = Psi * Phi print(Theta) Theta.display() # In[82]: 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']) # In[83]: graph = (graphE+graphM).rotate((0,0,1), 0.2) show(graph, aspect_ratio=(2,2,1), viewer='tachyon', frame=False, figsize=20) # In[84]: graph = (graphE+graphM).rotate((0,0,1), pi) show(graph, aspect_ratio=(2,2,1), viewer='tachyon', frame=False, figsize=20) # In[85]: 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 # In[86]: show(graphE+graphMN, aspect_ratio=1, frame=False) # In[87]: show(graphE+graphMN, aspect_ratio=(2,2,1), viewer='tachyon', frame=False, figsize=20) # In[ ]: