#!/usr/bin/env python3 """Compare a PBHVlasov (GRTeclyn) LTB e = 0 particle run in geodesic slicing with the analytic LTB solution. Geodesic slicing + zero shift keeps the coordinate ϱ attached to the LTB label r through the t_0 map, so rho_LTB(r(ϱ), t), K_LTB, chi_LTB = [gamma_ϱϱ (R/ϱ)^4]^(-1/3) are exact references at any later time. usage: vlasov_ltb_compare.py OUTDIR (extraction_data files + pbh_vlasov_out.dat + setup.json)""" import glob, json, os, re, sys import numpy as np from scipy.integrate import cumulative_trapezoid from scipy.interpolate import CubicSpline sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..")) from pbhgr.ltb import LTBGaussian out = sys.argv[1] S = json.load(open(os.path.join(out, "setup.json"))) mu, t0, tC0, L, N = S["mu"], S["t0"], S["tC0"], S["L"], S["N"] ltb = LTBGaussian(mu); rm = ltb.r_m; a0 = (t0 / ltb.t_i) ** (2.0 / 3.0) # t_0 isotropic map ϱ(r) exactly as in the generator r = np.linspace(1e-6, 8 * rm, 20000) R0 = np.array([ltb.R(t0, x) for x in r]); E = ltb.E(r) Rp0 = CubicSpline(r, R0)(r, 1) f = Rp0 / (R0 * np.sqrt(1 + 2 * E)) - 1.0 / r tail = cumulative_trapezoid(f[::-1], r[::-1], initial=0.0)[::-1] varrho = a0 * r * np.exp(tail) r_of_rho = CubicSpline(varrho, r) drho_dr = CubicSpline(r, varrho)(r, 1) def ltb_profiles(t, rho_pts): rr = r_of_rho(rho_pts) R = np.array([ltb.R(t, x) for x in rr]); Rd = np.array([ltb.Rdot(t, x) for x in rr]) # derivatives w.r.t. r from splines on a local fine grid rf = np.linspace(rr.min() * 0.9 + 1e-7, rr.max() * 1.1, 4000) Rf = np.array([ltb.R(t, x) for x in rf]); Rdf = np.array([ltb.Rdot(t, x) for x in rf]) Rp = CubicSpline(rf, Rf)(rr, 1); Rdp = CubicSpline(rf, Rdf)(rr, 1) Ef = ltb.E(rr); mp = ltb.mp(rr) rho = mp / (4 * np.pi * R**2 * Rp) K = -(Rdp / Rp + 2 * Rd / R) drhodr = np.interp(rr, r, drho_dr) g_rr = Rp**2 / (1 + 2 * Ef) / drhodr**2 chi = (g_rr * (R / rho_pts) ** 4) ** (-1.0 / 3.0) return rho, K, chi def read_line(fn): rows = [] for l in open(fn): if l.startswith("#") or not l.strip(): continue # fixed-width coordinates (12 chars each, no separator) followed by the value rows.append([float(l[0:12]), float(l[12:24]), float(l[24:36]), float(l[36:].split()[0])]) return np.array(rows) d = np.loadtxt(os.path.join(out, "pbh_vlasov_out.dat")) files = sorted(glob.glob(os.path.join(out, "rho_line_*.dat"))) print(f"mu = {mu}, t_0 = {t0/tC0:.2f} t_C(0), N = {N}, dx = {L/N:.1f}; {len(files)} line files, {len(d)} steps") picks = [0, len(files) // 4, len(files) // 2, 3 * len(files) // 4, len(files) - 1] cells = [2, 3, 4, 6, 8, 12, 16, 24] for k in picks: t = d[k, 0] + t0 rho3 = read_line(files[k]); x = rho3[:, 0] - L / 2; rho3 = rho3[:, 3] chi3 = read_line(files[k].replace("rho_line", "chi_line"))[:, 3] K3 = read_line(files[k].replace("rho_line", "K_line"))[:, 3] dx = L / N idx = [int(np.argmin(np.abs(x - c * dx))) for c in cells] pts = x[idx] rl, Kl, cl = ltb_profiles(t, pts) print(f"t = {t/tC0:.3f} t_C(0): cells " + " ".join(f"{c:>6d}" for c in cells)) print(" rho3D/rhoLTB " + " ".join(f"{rho3[i]/rl[j]:6.3f}" for j, i in enumerate(idx))) print(" K3D/KLTB " + " ".join(f"{K3[i]/Kl[j]:6.3f}" for j, i in enumerate(idx))) print(" chi3D/chiLTB " + " ".join(f"{chi3[i]/cl[j]:6.3f}" for j, i in enumerate(idx))) print(f" rho(0)/rho_far: 3D {rho3[0]/rho3[-5]:.2f}, LTB(2 cells) {rl[0]/ltb_profiles(t, np.array([x[-5]]))[0][0]:.2f}")