codex/past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2001/iii/paper-68-conformal-diagrams.py

past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2001/iii/paper-68-conformal-diagrams.py
"""Draw two conformal geometries; output the same-basename PNG in cwd."""

from pathlib import Path

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def main():
    plt.rcParams.update({"font.size": 12, "axes.titlesize": 14})
    fig, axes = plt.subplots(1, 2, figsize=(12.8, 6.6), dpi=100, layout="constrained")
    ds, ads = axes
    eta = np.linspace(-np.pi / 2, np.pi / 2, 600)
    halfwidth = np.pi / 2 - np.abs(eta)
    ds.fill_betweenx(eta, -halfwidth, halfwidth, color="#e4eef8", label="Central static patch")
    for sign in (-1, 1):
        ds.plot(sign * (np.pi / 2 - eta), eta, color="#d55e00", linewidth=2, label="Future event horizon" if sign == 1 else None)
        ds.plot(sign * (np.pi / 2 + eta), eta, color="#999999", linestyle=":", linewidth=1.3)
        ds.axvline(sign * np.pi, color="#777777", linestyle="--", linewidth=1)
    ds.plot(np.zeros_like(eta), eta, color="#0072b2", linewidth=2)
    ds.axhline(np.pi / 2, color="black", linewidth=2)
    ds.axhline(-np.pi / 2, color="black", linewidth=2)
    ds.text(0, np.pi / 2 + 0.15, "Spacelike future infinity", ha="center", va="bottom")
    ds.text(0, -np.pi / 2 - 0.15, "Spacelike past infinity", ha="center", va="top")
    ds.text(0, 0.26, "Observer", color="#0072b2", ha="center", bbox={"facecolor": "white", "edgecolor": "none", "pad": 2})
    ds.set(xlim=(-np.pi - 0.13, np.pi + 0.13), ylim=(-np.pi / 2 - 0.45, np.pi / 2 + 0.45), xlabel=r"Spatial angle $\chi$ (dashed edges identified)", ylabel=r"Conformal time $\eta$")
    ds.set_xticks([-np.pi, -np.pi / 2, 0, np.pi / 2, np.pi], [r"$-\pi$", r"$-\pi/2$", "0", r"$\pi/2$", r"$\pi$"])
    ds.set_yticks([-np.pi / 2, 0, np.pi / 2], [r"$-\pi/2$", "0", r"$\pi/2$"])
    ds.set_title("de Sitter: conformal cylinder")
    ds.legend(loc="lower center", bbox_to_anchor=(0.5, -0.39), fontsize=10, framealpha=1)
    ds.set_aspect("equal")

    tau = np.linspace(-np.pi, np.pi, 900)
    energy = 1.7
    amplitude = np.sqrt(energy**2 - 1)
    psi = np.arctan(amplitude * np.sin(tau))
    time = np.unwrap(np.arctan2(energy * np.sin(tau), np.cos(tau)))
    ads.plot(psi, time, color="#0072b2", linewidth=2, label="Timelike geodesic")
    ads.plot([0, np.pi / 2], [0, np.pi / 2], color="#d55e00", linewidth=2, label="Null geodesic")
    ads.plot([0, -np.pi / 2], [0, np.pi / 2], color="#d55e00", linewidth=2)
    for sign in (-1, 1):
        ads.axvline(sign * np.pi / 2, color="black", linewidth=2)
        ads.text(sign * (np.pi / 2 + 0.13), 0, "Timelike infinity", rotation=90, ha="center", va="center")
        ads.annotate("", xy=(0, sign * (np.pi + 0.32)), xytext=(0, sign * (np.pi - 0.1)), arrowprops={"arrowstyle": "->", "color": "#555555", "lw": 1.4})
    ads.text(0, np.pi / 2 + 0.13, "Finite coordinate time;\ninfinite affine parameter", ha="center", fontsize=10, bbox={"facecolor": "white", "edgecolor": "none", "pad": 2})
    ads.set(xlim=(-np.pi / 2 - 0.4, np.pi / 2 + 0.4), ylim=(-np.pi - 0.4, np.pi + 0.4), xlabel=r"Conformal radius $\psi$", ylabel=r"Global time $t$ (continues indefinitely)")
    ads.set_xticks([-np.pi / 2, 0, np.pi / 2], [r"$-\pi/2$", "0", r"$\pi/2$"])
    ads.set_yticks([-np.pi, -np.pi / 2, 0, np.pi / 2, np.pi], [r"$-\pi$", r"$-\pi/2$", "0", r"$\pi/2$", r"$\pi$"])
    ads.set_title("Anti-de Sitter: universal-cover strip")
    ads.legend(loc="lower center", bbox_to_anchor=(0.5, -0.19), fontsize=10, framealpha=1)
    ads.set_aspect("equal")
    for axis in axes:
        axis.grid(alpha=0.15)
        for spine in axis.spines.values():
            spine.set_visible(False)
    fig.savefig(Path(__file__).with_suffix(".png").name, facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()