Exemple #1
0
    def plot_observations(self, axes: plt.Axes):
        sun_observations = Sun2009()
        r_r500, S_S500_50, S_S500_10, S_S500_90 = sun_observations.get_shortcut(
        )

        rexcess = Pratt2010(n_radial_bins=21)
        bin_median, bin_perc16, bin_perc84 = rexcess.combine_entropy_profiles(
            m500_limits=(1e14 * Solar_Mass, 5e14 * Solar_Mass),
            k500_rescale=True)

        r = np.array([*axes.get_xlim()])
        k = 1.40 * r**1.1
        axes.plot(r, k, c='grey', ls='--', label='VKB (2005)')

        asymmetric_error = np.array(
            list(zip(bin_median - bin_perc16, bin_perc84 - bin_median))).T
        axes.errorbar(rexcess.radial_bins,
                      bin_median,
                      yerr=asymmetric_error,
                      fmt='o',
                      markersize=2,
                      color='grey',
                      ecolor='lightgray',
                      elinewidth=0.7,
                      capsize=0,
                      label=rexcess.citation)
        asymmetric_error = np.array(
            list(zip(S_S500_50 - S_S500_10, S_S500_90 - S_S500_50))).T
        axes.errorbar(r_r500,
                      S_S500_50,
                      yerr=asymmetric_error,
                      fmt='^',
                      markersize=2,
                      color='grey',
                      ecolor='lightgray',
                      elinewidth=0.7,
                      capsize=0,
                      label=sun_observations.citation)
        entropy_profile * gas_fraction_enclosed**(2 / 3) *
        cosmology.ez_function(gas_profile_obj.z)**(2 / 3),
        linestyle='-',
        color='r',
        linewidth=1,
        alpha=1,
    )
    axes[1, 1].set_xscale('log')
    axes[1, 1].set_yscale('log')
    axes[1, 1].set_ylabel(
        r'$E(z) ^ {2/3}~(K/K_{500})~\times~f_{\rm gas}(<r) ^ {2/3}$')
    axes[1, 1].set_xlabel(r'$r/r_{500}$')
    axes[1, 1].set_ylim([5e-5, 3])
    axes[1, 1].set_xlim([0.01, 1])

    sun_observations = Sun2009()
    r_r500, S_S500_50, S_S500_10, S_S500_90 = sun_observations.get_shortcut()

    axes[0, 1].errorbar(r_r500,
                        S_S500_50,
                        yerr=(S_S500_50 - S_S500_10, S_S500_90 - S_S500_50),
                        fmt='o',
                        color='grey',
                        ecolor='lightgray',
                        elinewidth=0.5,
                        capsize=0,
                        markersize=2,
                        label=sun_observations.citation,
                        zorder=0)

    rexcess = Pratt2010(n_radial_bins=30)
import velociraptor as vr
from typing import Tuple
import matplotlib.patheffects as path_effects

try:
    plt.style.use("../mnras.mplstyle")
except:
    pass

import sys

# Make the register backend visible to the script
sys.path.append("..")
from literature import Sun2009, Cosmology

Sun2009 = Sun2009()
fb = Cosmology().fb

# Constants
mean_molecular_weight = 0.59
mean_atomic_weight_per_free_electron = 1.14


def update_prop(handle, orig):
    handle.update_from(orig)
    handle.set_marker("o")


def histogram_unyt(
    data: unyt.unyt_array,
    bins: unyt.unyt_array = None,
import sys
import numpy as np
from unyt import kpc
from matplotlib import pyplot as plt

sys.path.append("..")

from literature import Sun2009, Pratt2010
plt.style.use('../register/mnras.mplstyle')
lit = Sun2009(reduced_table=True, disable_cosmo_conversion=False)
from matplotlib import pyplot as plt

x = [
    np.median(30 * kpc / lit.R_500),
    np.median(0.15),
    np.median(lit.R_2500 / lit.R_500),
    np.median(lit.R_1500 / lit.R_500),
    np.median(lit.R_1000 / lit.R_500),
    np.median(1),
]
y = [
    np.median(lit.K_500 / lit.K_500_adi),
    np.median(lit.K_1000 / lit.K_500_adi),
    np.median(lit.K_1500 / lit.K_500_adi),
    np.median(lit.K_2500 / lit.K_500_adi),
    np.median(lit.K_0p15r500 / lit.K_500_adi),
    np.median(lit.K_30kpc / lit.K_500_adi),
][::-1]

plt.loglog()
plt.scatter(x, y, label='Edo')
    def process_single_halo(
        self,
        zoom_obj: Zoom = None,
        path_to_snap: str = None,
        path_to_catalogue: str = None,
        agn_time: str = None,
        z_agn_start: float = 18,
        z_agn_end: float = 0.,
    ):
        aperture_fraction = xlargs.aperture_percent / 100

        sw_data, vr_data = self.get_handles_from_zoom(zoom_obj,
                                                      path_to_snap,
                                                      path_to_catalogue,
                                                      mask_radius_r500=5)

        try:
            m500 = vr_data.spherical_overdensities.mass_500_rhocrit[0].to(
                'Msun')
            r500 = vr_data.spherical_overdensities.r_500_rhocrit[0].to('Mpc')
        except AttributeError as err:
            print(err)
            print(
                f'[{self.__class__.__name__}] Launching spherical overdensity calculation...'
            )
            spherical_overdensity = SODelta500(
                path_to_snap=path_to_snap,
                path_to_catalogue=path_to_catalogue,
            )
            m500 = spherical_overdensity.get_m500()
            r500 = spherical_overdensity.get_r500()

        if xlargs.mass_estimator == 'hse':
            true_hse = HydrostaticEstimator(
                path_to_catalogue=path_to_catalogue,
                path_to_snap=path_to_snap,
                profile_type='true',
                diagnostics_on=False).interpolate_hse()
            r500 = true_hse.r500hse
            m500 = true_hse.M500hse

        aperture_fraction = aperture_fraction * r500

        # Convert datasets to physical quantities
        # r500c is already in physical units
        sw_data.gas.radial_distances.convert_to_physical()
        sw_data.gas.coordinates.convert_to_physical()
        sw_data.gas.masses.convert_to_physical()
        sw_data.gas.densities.convert_to_physical()

        activate_cooling_times = True
        try:
            cooling_times = calculate_mean_cooling_times(sw_data)
        except AttributeError as err:
            print(err)
            if xlargs.debug:
                print(
                    f'[{self.__class__.__name__}] Setting activate_cooling_times = False'
                )
            activate_cooling_times = False

        if xlargs.debug:
            print(f"[{self.__class__.__name__}] m500 = ", m500)
            print(f"[{self.__class__.__name__}] r500 = ", r500)
            print(f"[{self.__class__.__name__}] aperture_fraction = ",
                  aperture_fraction)
            print(
                f"[{self.__class__.__name__}] Number of particles being imported",
                len(sw_data.gas.densities))

        gamma = 5 / 3

        try:
            a_heat = sw_data.gas.last_agnfeedback_scale_factors
        except AttributeError as err:
            print(err)
            if xlargs.debug:
                print('Setting `last_agnfeedback_scale_factors` with 0.1.')
            a_heat = np.ones_like(sw_data.gas.masses) * 0.1

        try:
            fof_ids = sw_data.gas.fofgroup_ids
        except AttributeError as err:
            print(err)
            if xlargs.debug:
                print(
                    f"[{self.__class__.__name__}] Select particles only by radial distance."
                )
            fof_ids = np.ones_like(sw_data.gas.densities)

        try:
            temperature = sw_data.gas.temperatures
        except AttributeError as err:
            print(err)
            if xlargs.debug:
                print(
                    f"[{self.__class__.__name__}] Computing gas temperature from internal energies."
                )
            A = sw_data.gas.entropies * sw_data.units.mass
            temperature = mean_molecular_weight * (gamma - 1) * (
                A * sw_data.gas.densities**(5 / 3 - 1)) / (gamma - 1) * mh / kb

        try:
            hydrogen_fractions = sw_data.gas.element_mass_fractions.hydrogen
        except AttributeError as err:
            print(err)
            if xlargs.debug:
                print(
                    f"[{self.__class__.__name__}] Setting H fractions to primordial values."
                )
            hydrogen_fractions = np.ones_like(
                sw_data.gas.densities) * primordial_hydrogen_mass_fraction

        try:
            agn_flag = sw_data.gas.heated_by_agnfeedback
        except AttributeError as err:
            print(err)
            if xlargs.debug:
                print(
                    f"[{self.__class__.__name__}] Setting all agn_flag to zero."
                )
            agn_flag = np.zeros_like(sw_data.gas.densities)

        try:
            snii_flag = sw_data.gas.heated_by_sniifeedback
        except AttributeError as err:
            print(err)
            if xlargs.debug:
                print(
                    f"[{self.__class__.__name__}] Setting all snii_flag to zero."
                )
            snii_flag = np.zeros_like(sw_data.gas.densities)

        if agn_time is None:
            index = np.where((sw_data.gas.radial_distances < aperture_fraction)
                             & (fof_ids == 1) & (temperature > 1e5))[0]

            number_density = (sw_data.gas.densities / mh).to(
                'cm**-3').value[index] * hydrogen_fractions[index]
            temperature = temperature.to('K').value[index]

        elif agn_time == 'before':

            index = np.where(
                (sw_data.gas.radial_distances < aperture_fraction)
                & (fof_ids == 1) & (a_heat > (1 / (z_agn_start + 1)))
                & (a_heat < (1 / (z_agn_end + 1)))
                & (sw_data.gas.densities_before_last_agnevent > 0))[0]

            density = sw_data.gas.densities_before_last_agnevent[index]
            number_density = (
                density / mh).to('cm**-3').value * hydrogen_fractions[index]
            A = sw_data.gas.entropies_before_last_agnevent[
                index] * sw_data.units.mass
            temperature = mean_molecular_weight * (gamma - 1) * (
                A * density**(5 / 3 - 1)) / (gamma - 1) * mh / kb
            temperature = temperature.to('K').value

        elif agn_time == 'after':

            index = np.where((sw_data.gas.radial_distances < aperture_fraction)
                             & (fof_ids == 1) & (a_heat > (1 /
                                                           (z_agn_start + 1)))
                             & (a_heat < (1 / (z_agn_end + 1)))
                             & (sw_data.gas.densities_at_last_agnevent > 0))[0]

            density = sw_data.gas.densities_at_last_agnevent[index]
            number_density = (
                density / mh).to('cm**-3').value * hydrogen_fractions[index]
            A = sw_data.gas.entropies_at_last_agnevent[
                index] * sw_data.units.mass
            temperature = mean_molecular_weight * (gamma - 1) * (
                A * density**(5 / 3 - 1)) / (gamma - 1) * mh / kb
            temperature = temperature.to('K').value

        agn_flag = agn_flag[index]
        snii_flag = snii_flag[index]
        agn_flag = agn_flag > 0
        snii_flag = snii_flag > 0

        # Calculate the critical density for the cross-hair marker
        rho_crit = unyt_quantity(
            sw_data.metadata.cosmology.critical_density(
                sw_data.metadata.z).value, 'g/cm**3').to('Msun/Mpc**3')
        nH_500 = (primordial_hydrogen_mass_fraction * Cosmology().fb0 *
                  rho_crit * 500 / mh).to('cm**-3')

        # Entropy
        electron_number_density = (sw_data.gas.densities[index] /
                                   mh).to('cm**-3') / mean_molecular_weight
        entropy = kb * temperature * K / electron_number_density**(2 / 3)
        entropy = entropy.to('keV*cm**2')

        x = number_density
        y = temperature

        if activate_cooling_times:
            w = cooling_times[index]

        if xlargs.debug:
            print("Number of particles being plotted", len(x))

        # Set the limits of the figure.
        assert (x > 0).all(), f"Found negative value(s) in x: {x[x <= 0]}"
        assert (y > 0).all(), f"Found negative value(s) in y: {y[y <= 0]}"

        # density_bounds = [1e-6, 1e4]  # in nh/cm^3
        # temperature_bounds = [1e3, 1e10]  # in K
        density_bounds = [1e-5, 1]  # in nh/cm^3
        temperature_bounds = [1e6, 1e9]  # in K
        pdf_ybounds = [1, 10**6]
        bins = 256

        # Make the norm object to define the image stretch
        density_bins = np.logspace(np.log10(density_bounds[0]),
                                   np.log10(density_bounds[1]), bins)
        temperature_bins = np.logspace(np.log10(temperature_bounds[0]),
                                       np.log10(temperature_bounds[1]), bins)

        T500 = (G * mean_molecular_weight * m500 * mp / r500 / 2 /
                kb).to('K').value
        K500 = (T500 * K * kb /
                (3 * m500 * Cosmology().fb0 /
                 (4 * np.pi * r500**3 * mp))**(2 / 3)).to('keV*cm**2')

        # Make the norm object to define the image stretch
        contour_density_bins = np.logspace(
            np.log10(density_bounds[0]) - 0.5,
            np.log10(density_bounds[1]) + 0.5, bins * 4)
        contour_temperature_bins = np.logspace(
            np.log10(temperature_bounds[0]) - 0.5,
            np.log10(temperature_bounds[1]) + 0.5, bins * 4)

        fig = plt.figure(figsize=(8, 6), constrained_layout=True)
        gs = fig.add_gridspec(3, 4, hspace=0.35, wspace=0.7)
        axes = gs.subplots()

        for ax in [axes[0, 0], axes[0, 1], axes[0, 2], axes[1, 0], axes[1, 1]]:
            ax.loglog()
            # Draw cross-hair marker
            ax.hlines(y=T500,
                      xmin=nH_500 / 3,
                      xmax=nH_500 * 3,
                      colors='k',
                      linestyles='-',
                      lw=0.5)
            ax.vlines(x=nH_500,
                      ymin=T500 / 5,
                      ymax=T500 * 5,
                      colors='k',
                      linestyles='-',
                      lw=0.5)

            # Draw contours
            draw_k500(ax, contour_density_bins, contour_temperature_bins, K500)
            draw_adiabats(ax, contour_density_bins, contour_temperature_bins)
            draw_cooling_contours(ax,
                                  contour_density_bins,
                                  contour_temperature_bins,
                                  levels=[1, 1e2, 1e3, 1e4, 1e5],
                                  color='green')
            draw_cooling_contours(
                ax,
                contour_density_bins,
                contour_temperature_bins,
                levels=[Cosmology().age(sw_data.metadata.z).to('Myr').value],
                prefix='$t_H(z)=$',
                color='red',
                use_labels=False)

        # PLOT ALL PARTICLES ===============================================
        H, density_edges, temperature_edges = np.histogram2d(
            x, y, bins=[density_bins, temperature_bins])
        draw_2d_hist(axes[0, 0], density_edges, temperature_edges, H,
                     'Greys_r', "All particles")

        # PLOT SN HEATED PARTICLES ===============================================
        H, density_edges, temperature_edges = np.histogram2d(
            x[(snii_flag & ~agn_flag)],
            y[(snii_flag & ~agn_flag)],
            bins=[density_bins, temperature_bins])
        draw_2d_hist(axes[0, 1], density_edges, temperature_edges, H,
                     'Greens_r', "SNe heated only")
        axes[0, 1].axhline(10**7.5, color='k', linestyle='--', lw=1, zorder=0)

        # PLOT NOT HEATED PARTICLES ===============================================
        H, density_edges, temperature_edges = np.histogram2d(
            x[(~snii_flag & ~agn_flag)],
            y[(~snii_flag & ~agn_flag)],
            bins=[density_bins, temperature_bins])
        draw_2d_hist(axes[0, 2], density_edges, temperature_edges, H,
                     'Greens_r', "Not heated")

        # PLOT AGN HEATED PARTICLES ===============================================
        H, density_edges, temperature_edges = np.histogram2d(
            x[(agn_flag & ~snii_flag)],
            y[(agn_flag & ~snii_flag)],
            bins=[density_bins, temperature_bins])
        draw_2d_hist(axes[1, 1], density_edges, temperature_edges, H, 'Reds_r',
                     "AGN heated only")
        axes[1, 1].axhline(10**8.5, color='k', linestyle='--', lw=1, zorder=0)

        # PLOT AGN+SN HEATED PARTICLES ===============================================
        H, density_edges, temperature_edges = np.histogram2d(
            x[(agn_flag & snii_flag)],
            y[(agn_flag & snii_flag)],
            bins=[density_bins, temperature_bins])
        draw_2d_hist(axes[1, 0], density_edges, temperature_edges, H,
                     'Purples_r', "AGN and SNe heated")
        axes[1, 0].axhline(10**8.5, color='k', linestyle='--', lw=1, zorder=0)
        axes[1, 0].axhline(10**7.5, color='k', linestyle='--', lw=1, zorder=0)

        bins = np.linspace(0., 5.5, 51)

        axes[2, 0].clear()
        axes[2, 0].set_xscale('linear')
        axes[2, 0].set_yscale('log')
        if activate_cooling_times:
            axes[2, 0].hist(w, bins=bins, histtype='step', label='All')
            axes[2, 0].hist(w[(agn_flag & snii_flag)],
                            bins=bins,
                            histtype='step',
                            label='AGN & SN')
            axes[2, 0].hist(w[(agn_flag & ~snii_flag)],
                            bins=bins,
                            histtype='step',
                            label='AGN')
            axes[2, 0].hist(w[(~agn_flag & snii_flag)],
                            bins=bins,
                            histtype='step',
                            label='SN')
            axes[2, 0].hist(w[(~agn_flag & ~snii_flag)],
                            bins=bins,
                            histtype='step',
                            label='Not heated')
        axes[2, 0].axvline(np.log10(Cosmology().age(
            sw_data.metadata.z).to('Myr').value),
                           color='k',
                           linestyle='--',
                           lw=0.5,
                           zorder=0)
        axes[2, 0].set_xlabel(f"$\log_{{10}}$(Cooling time [Myr])")
        axes[2, 0].set_ylabel('Number of particles')
        axes[2, 0].set_ylim(pdf_ybounds)
        axes[2, 0].legend(loc="upper left")

        if activate_cooling_times:
            hydrogen_fraction = sw_data.gas.element_mass_fractions.hydrogen[
                index]
        bins = np.linspace(0, 1, 51)

        axes[2, 1].clear()
        axes[2, 1].set_xscale('linear')
        axes[2, 1].set_yscale('log')
        if activate_cooling_times:
            axes[2, 1].hist(hydrogen_fraction,
                            bins=bins,
                            histtype='step',
                            label='All')
            axes[2, 1].hist(hydrogen_fraction[(agn_flag & snii_flag)],
                            bins=bins,
                            histtype='step',
                            label='AGN & SN')
            axes[2, 1].hist(hydrogen_fraction[(agn_flag & ~snii_flag)],
                            bins=bins,
                            histtype='step',
                            label='AGN')
            axes[2, 1].hist(hydrogen_fraction[(~agn_flag & snii_flag)],
                            bins=bins,
                            histtype='step',
                            label='SN')
            axes[2, 1].hist(hydrogen_fraction[(~agn_flag & ~snii_flag)],
                            bins=bins,
                            histtype='step',
                            label='Not heated')
        axes[2, 1].set_xlabel("Hydrogen fraction")
        axes[2, 1].set_ylabel('Number of particles')
        axes[2, 1].set_ylim(pdf_ybounds)

        if activate_cooling_times:
            log_gas_Z = np.log10(
                sw_data.gas.metal_mass_fractions.value[index] / 0.0133714)
        bins = np.linspace(-4, 1, 51)

        axes[2, 2].clear()
        axes[2, 2].set_xscale('linear')
        axes[2, 2].set_yscale('log')
        if activate_cooling_times:
            axes[2, 2].hist(log_gas_Z, bins=bins, histtype='step', label='All')
            axes[2, 2].hist(log_gas_Z[(agn_flag & snii_flag)],
                            bins=bins,
                            histtype='step',
                            label='AGN & SN')
            axes[2, 2].hist(log_gas_Z[(agn_flag & ~snii_flag)],
                            bins=bins,
                            histtype='step',
                            label='AGN')
            axes[2, 2].hist(log_gas_Z[(~agn_flag & snii_flag)],
                            bins=bins,
                            histtype='step',
                            label='SN')
            axes[2, 2].hist(log_gas_Z[(~agn_flag & ~snii_flag)],
                            bins=bins,
                            histtype='step',
                            label='Not heated')
        axes[2, 2].axvline(0.5, color='k', linestyle='--', lw=0.5, zorder=0)
        axes[2, 2].set_xlabel(f"$\log_{{10}}$(Metallicity [Z$_\odot$])")
        axes[2, 2].set_ylabel('Number of particles')
        axes[2, 2].set_ylim(pdf_ybounds)

        bins = np.logspace(np.log10(density_edges.min()),
                           np.log10(density_edges.max()), 51)

        axes[0, 3].clear()
        axes[0, 3].set_xscale('log')
        axes[0, 3].set_yscale('log')
        axes[0, 3].hist(x, bins=bins, histtype='step', label='All')
        axes[0, 3].hist(x[(agn_flag & snii_flag)],
                        bins=bins,
                        histtype='step',
                        label='AGN & SN')
        axes[0, 3].hist(x[(agn_flag & ~snii_flag)],
                        bins=bins,
                        histtype='step',
                        label='AGN')
        axes[0, 3].hist(x[(~agn_flag & snii_flag)],
                        bins=bins,
                        histtype='step',
                        label='SN')
        axes[0, 3].hist(x[(~agn_flag & ~snii_flag)],
                        bins=bins,
                        histtype='step',
                        label='Not heated')
        axes[0, 3].set_xlabel(f"Density [$n_H$ cm$^{{-3}}$]")
        axes[0, 3].set_ylabel('Number of particles')
        axes[0, 3].set_ylim(pdf_ybounds)
        axes[0, 3].legend()

        bins = np.logspace(np.log10(temperature_edges.min()),
                           np.log10(temperature_edges.max()), 51)

        axes[1, 3].clear()
        axes[1, 3].set_xscale('log')
        axes[1, 3].set_yscale('log')
        axes[1, 3].hist(y, bins=bins, histtype='step', label='All')
        axes[1, 3].hist(y[(agn_flag & snii_flag)],
                        bins=bins,
                        histtype='step',
                        label='AGN & SN')
        axes[1, 3].hist(y[(agn_flag & ~snii_flag)],
                        bins=bins,
                        histtype='step',
                        label='AGN')
        axes[1, 3].hist(y[(~agn_flag & snii_flag)],
                        bins=bins,
                        histtype='step',
                        label='SN')
        axes[1, 3].hist(y[(~agn_flag & ~snii_flag)],
                        bins=bins,
                        histtype='step',
                        label='Not heated')
        axes[1, 3].set_xlabel("Temperature [K]")
        axes[1, 3].set_ylabel('Number of particles')
        axes[1, 3].set_ylim(pdf_ybounds)

        bins = np.logspace(0, 4, 51)

        axes[2, 3].clear()
        axes[2, 3].set_xscale('log')
        axes[2, 3].set_yscale('log')
        axes[2, 3].hist(entropy, bins=bins, histtype='step', label='All')
        axes[2, 3].hist(entropy[(agn_flag & snii_flag)],
                        bins=bins,
                        histtype='step',
                        label='AGN & SN')
        axes[2, 3].hist(entropy[(agn_flag & ~snii_flag)],
                        bins=bins,
                        histtype='step',
                        label='AGN')
        axes[2, 3].hist(entropy[(~agn_flag & snii_flag)],
                        bins=bins,
                        histtype='step',
                        label='SN')
        axes[2, 3].hist(entropy[(~agn_flag & ~snii_flag)],
                        bins=bins,
                        histtype='step',
                        label='Not heated')
        axes[2, 3].set_xlabel("Entropy [keV cm$^2$]")
        axes[2, 3].set_ylabel('Number of particles')
        axes[2, 3].set_ylim(pdf_ybounds)

        # Entropy profile
        max_radius_r500 = 4

        try:
            temperature = sw_data.gas.temperatures
        except AttributeError as err:
            print(err)
            if xlargs.debug:
                print(
                    f"[{self.__class__.__name__}] Computing gas temperature from internal energies."
                )
            A = sw_data.gas.entropies * sw_data.units.mass
            temperature = mean_molecular_weight * (gamma - 1) * (
                A * sw_data.gas.densities**(5 / 3 - 1)) / (gamma - 1) * mh / kb

        index = np.where((sw_data.gas.radial_distances < max_radius_r500 *
                          r500) & (fof_ids == 1) & (temperature > 1e5))[0]
        radial_distance = sw_data.gas.radial_distances[index] / r500
        sw_data.gas.masses = sw_data.gas.masses[index]
        temperature = temperature[index]

        # Define radial bins and shell volumes
        lbins = np.logspace(-2, np.log10(max_radius_r500),
                            51) * radial_distance.units
        radial_bin_centres = 10.0**(
            0.5 * np.log10(lbins[1:] * lbins[:-1])) * radial_distance.units
        volume_shell = (4. * np.pi / 3.) * (r500**3) * ((lbins[1:])**3 -
                                                        (lbins[:-1])**3)

        mass_weights, _ = histogram_unyt(radial_distance,
                                         bins=lbins,
                                         weights=sw_data.gas.masses)
        mass_weights[mass_weights == 0] = np.nan  # Replace zeros with Nans
        density_profile = mass_weights / volume_shell
        number_density_profile = (density_profile.to('g/cm**3') /
                                  (mp * mean_molecular_weight)).to('cm**-3')

        mass_weighted_temperatures = (temperature *
                                      kb).to('keV') * sw_data.gas.masses
        temperature_weights, _ = histogram_unyt(
            radial_distance, bins=lbins, weights=mass_weighted_temperatures)
        temperature_weights[temperature_weights ==
                            0] = np.nan  # Replace zeros with Nans
        temperature_profile = temperature_weights / mass_weights  # kBT in units of [keV]

        entropy_profile = temperature_profile / number_density_profile**(2 / 3)

        rho_crit = unyt_quantity(
            sw_data.metadata.cosmology.critical_density(
                sw_data.metadata.z).value, 'g/cm**3').to('Msun/Mpc**3')
        density_profile /= rho_crit

        kBT500 = (G * mean_molecular_weight * m500 * mp / r500 / 2).to('keV')
        K500 = (kBT500 / (3 * m500 * Cosmology().fb0 /
                          (4 * np.pi * r500**3 * mp))**(2 / 3)).to('keV*cm**2')

        axes[1, 2].plot(
            radial_bin_centres,
            entropy_profile / K500,
            linestyle='-',
            color='r',
            linewidth=1,
            alpha=1,
        )
        axes[1, 2].set_xscale('log')
        axes[1, 2].set_yscale('log')

        axes[1, 2].axvline(0.15, color='k', linestyle='--', lw=0.5, zorder=0)
        axes[1, 2].set_ylabel(r'Entropy [keV cm$^2$]')
        axes[1, 2].set_xlabel(r'$r/r_{500}$')
        # axes[1, 2].set_ylim([1, 1e4])
        axes[1, 2].set_ylim([1e-2, 5])
        axes[1, 2].set_xlim([0.01, max_radius_r500])

        # axes[1, 2].axhline(y=K500, color='k', linestyle=':', linewidth=0.5)
        # axes[1, 2].text(
        #     axes[1, 2].get_xlim()[0], K500, r'$K_{500}$',
        #     horizontalalignment='left',
        #     verticalalignment='bottom',
        #     color='k',
        #     bbox=dict(
        #         boxstyle='square,pad=10',
        #         fc='none',
        #         ec='none'
        #     )
        # )
        sun_observations = Sun2009()
        sun_observations.filter_by('M_500', 8e13, 3e14)
        sun_observations.overlay_entropy_profiles(axes=axes[1, 2],
                                                  k_units='K500adi',
                                                  markersize=1,
                                                  linewidth=0.5)
        rexcess = Pratt2010()
        bin_median, bin_perc16, bin_perc84 = rexcess.combine_entropy_profiles(
            m500_limits=(1e14 * Solar_Mass, 5e14 * Solar_Mass),
            k500_rescale=True)
        axes[1, 2].fill_between(rexcess.radial_bins,
                                bin_perc16,
                                bin_perc84,
                                color='aqua',
                                alpha=0.85,
                                linewidth=0)
        axes[1, 2].plot(rexcess.radial_bins, bin_median, c='k')

        z_agn_recent_text = (
            f"Selecting gas heated between {z_agn_start:.1f} > z > {z_agn_end:.1f} (relevant to AGN plot only)\n"
            f"({1 / (z_agn_start + 1):.2f} < a < {1 / (z_agn_end + 1):.2f})\n")
        if agn_time is not None:
            z_agn_recent_text = (
                f"Selecting gas {agn_time:s} heated between {z_agn_start:.1f} > z > {z_agn_end:.1f}\n"
                f"({1 / (z_agn_start + 1):.2f} < a < {1 / (z_agn_end + 1):.2f})\n"
            )

        fig.suptitle((
            f"{os.path.basename(path_to_snap)}\n"
            f"Aperture = {xlargs.aperture_percent / 100:.2f} $R_{{500}}$\t\t"
            f"$z = {sw_data.metadata.z:.2f}$\t\t"
            f"Age = {Cosmology().age(sw_data.metadata.z).value:.2f} Gyr\t\t"
            f"\t$M_{{500}}={latex_float(m500.value)}\\ {m500.units.latex_repr}$\n"
            f"{z_agn_recent_text:s}"
            f"Central FoF group only\t\tEstimator: {xlargs.mass_estimator}"),
                     fontsize=7)

        if not xlargs.quiet:
            plt.show()

        fig.savefig(os.path.join(
            default_output_directory,
            f"cooling_times_{os.path.basename(path_to_snap)[:-5].replace('.', 'p')}.png"
        ),
                    dpi=300)

        plt.close()