plot_ah_trajectories.pyΒΆ
plot_ah_trajectories
plots the coordinate trajectories on a plane. It can
also draw the outline of the horizons and force the center of mass to be in the
origin of the coordinate system.
#!/usr/bin/env python3
# Copyright (C) 2020-2024 Gabriele Bozzola
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, see <https://www.gnu.org/licenses/>.
import logging
import matplotlib.pyplot as plt
from kuibit import argparse_helper as kah
from kuibit.series import sample_common
from kuibit.simdir import SimDir
from kuibit.visualize_matplotlib import (
add_text_to_corner,
get_figname,
plot_horizon_on_plane_at_time,
save_from_dir_filename_ext,
setup_matplotlib,
)
if __name__ == "__main__":
desc = f"""{kah.get_program_name()} plots the trajectories of given apparent horizons on
a plane. Optionally, it also plots the outline of the horizons."""
parser = kah.init_argparse(desc)
kah.add_figure_to_parser(parser)
parser.add_argument(
"--plane",
type=str,
choices=["xy", "xz", "yz"],
default="xy",
help="Plane to plot (default: %(default)s)",
)
parser.add_argument(
"-a",
"--horizons",
type=int,
required=True,
help="Apparent horizons to consider.",
nargs="+",
)
parser.add_argument(
"--draw-ah",
help="Draw the outlines of the apparent horizons.",
action="store_true",
)
parser.add_argument(
"--force-com-at-origin",
help="Force the Newtonian center of mass to be in the origin "
"(this uses the irreducible masses). In this case, everything "
"will be resampled to times that are common across the horizons.",
action="store_true",
)
args = kah.get_args(parser)
setup_matplotlib(rc_par_file=args.mpl_rc_file)
# Parse arguments
logger = logging.getLogger(__name__)
if args.verbose:
logging.basicConfig(format="%(asctime)s - %(message)s")
logger.setLevel(logging.DEBUG)
# For the figure name, we chain all the horizon numbers
horizon_name = "_".join([str(h) for h in args.horizons])
figname = get_figname(
args, default=f"ah_{horizon_name}_trajectory_{args.plane}"
)
logger.debug(f"Figname: {figname}")
with SimDir(
args.datadir,
ignore_symlinks=args.ignore_symlinks,
pickle_file=args.pickle_file,
) as sim:
logger.debug("Prepared SimDir")
sim_hor = sim.horizons
logger.debug(
f"Apparent horizons available: {sim_hor.available_apparent_horizons}"
)
# Check that the horizons are available
for ah in args.horizons:
if ah not in sim_hor.available_apparent_horizons:
raise ValueError(f"Apparent horizons {ah} is not available")
# Now, we prepare the list with the centroids. The keys are the
# horizon numbers and the values are TimeSeries. We prepare this so that
# we can compute the center of mass. For that, we also need the masses
# (x_cm = sum m_i / M x_i). We use the irreducible mass for this.
ah_coords = {"x": [], "y": [], "z": []}
masses = []
# If force_com_at_origin, these objects will be rewritten so that we can
# assume that they house the quantities we want to plot.
for ah in args.horizons:
logger.debug(f"Reading horizon {ah}")
hor = sim_hor.get_apparent_horizon(ah)
for coord, ah_coord in ah_coords.items():
ah_coord.append(hor.ah[f"centroid_{coord}"])
if args.force_com_at_origin:
logger.debug("Reading mass")
masses.append(hor.ah.m_irreducible)
if args.force_com_at_origin:
# x_cm = sum m_i / M x_i
logger.debug("Computing center of mass")
logger.debug("Resampling to common times")
# We have to resample everything to a common time interval. This is
# because we are going to combine the various objects with
# mathematical operations.
# Loop over the three coordinates and overwrite the list of
# TimeSeries (note that ahs here are lists of TimeSeries and
# sample_common(ahs) returns a new list of TimeSeries)
ah_coords = {
coord: sample_common(ahs) for coord, ahs in ah_coords.items()
}
masses = sample_common(masses)
# First, we compute the total mass (as TimeSeries)
total_mass = sum(mass for mass in masses)
# Loop over the three coordinates
for coord, ah_coord in ah_coords.items():
# For each coordinate, compute the center of mass along that
# coordinate
com = sum(
mass * ah / total_mass
for mass, ah in zip(masses, ah_coords[coord])
)
# Now, we update ah_coords over that coordinate by subtracting
# the center of mass from each apparent horizon
ah_coord = [ah - com for ah in ah_coords[coord]]
to_plot_x, to_plot_y = args.plane
logger.debug(f"Plotting on the x axis {to_plot_x}")
logger.debug(f"Plotting on the y axis {to_plot_y}")
# Now we loop over all the horizons
for ind, ah in enumerate(args.horizons):
plt.plot(
ah_coords[to_plot_x][ind].y,
ah_coords[to_plot_y][ind].y,
label=f"Horizon {ah}",
)
# We save the time to plot the horizon outline
time = ah_coords[to_plot_x][ind].tmax
# Try to draw the shape of the horizon
if args.draw_ah:
logger.debug(f"Drawing shape at time {time} for ah {ah}")
plot_horizon_on_plane_at_time(
sim_hor.get_apparent_horizon(ah), time, args.plane
)
if args.force_com_at_origin:
xlabel = f"{to_plot_x} - {to_plot_x}_CM"
ylabel = f"{to_plot_y} - {to_plot_y}_CM"
else:
xlabel, ylabel = to_plot_x, to_plot_y
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.gca().set_aspect("equal")
plt.legend()
add_text_to_corner(rf"$t = {time:.3f}$")
logger.debug("Saving")
save_from_dir_filename_ext(
args.outdir,
figname,
args.fig_extension,
tikz_clean_figure=args.tikz_clean_figure,
)
logger.debug("DONE")