예제 #1
0
    def process_single_halo(
            self,
            zoom_obj: Zoom = None,
            path_to_snap: str = None,
            path_to_catalogue: str = None
    ):

        sw_data, vr_data = self.get_handles_from_zoom(
            zoom_obj,
            path_to_snap,
            path_to_catalogue,
            mask_radius_r500=self.max_radius_r500
        )
        fb = Cosmology().fb0
        critical_density = unyt_quantity(
            sw_data.metadata.cosmology.critical_density(sw_data.metadata.z).value, 'g/cm**3'
        ).to('Msun/Mpc**3')

        sw_data.gas.radial_distances.convert_to_physical()
        sw_data.gas.masses.convert_to_physical()
        sw_data.gas.densities.convert_to_physical()
        sw_data.gas.entropies.convert_to_physical()

        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(f'[{self.__class__.__name__}] {err}')

            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
            )
            true_hse.interpolate_hse(density_contrast=500.)
            r500 = true_hse.r500hse
            m500 = true_hse.m500hse

        try:
            _ = sw_data.gas.temperatures
        except AttributeError as err:
            print(f'[{self.__class__.__name__}] {err}')
            if xlargs.debug:
                print(f"[{self.__class__.__name__}] Computing gas temperature from internal energies.")
            sw_data.gas.temperatures = sw_data.gas.internal_energies * (gamma - 1) * mean_molecular_weight * mh / kb

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

        index = np.where(
            (sw_data.gas.radial_distances < self.max_radius_r500 * r500) &
            (sw_data.gas.fofgroup_ids == 1) &
            (sw_data.gas.temperatures > Tcut_halogas)
        )[0]
        radial_distance = sw_data.gas.radial_distances[index] / r500

        # Define radial bins and shell volumes
        lbins = np.logspace(-2, np.log10(self.max_radius_r500), 51) * radial_distance.units
        radial_bin_centres = 10 ** (0.5 * np.log10(lbins[1:] * lbins[:-1])) * radial_distance.units

        if self.weighting == 'xray':
            # Compute hydrogen number density and the log10
            # of the temperature to provide to the xray interpolator.
            data_nH = np.log10(
                sw_data.gas.element_mass_fractions.hydrogen * sw_data.gas.densities.to('g*cm**-3') / mp)
            data_T = np.log10(sw_data.gas.temperatures.value)

            # Interpolate the Cloudy table to get emissivities
            emissivities = unyt_array(
                10 ** cloudy.interpolate_X_Ray(
                    data_nH,
                    data_T,
                    sw_data.gas.element_mass_fractions,
                    fill_value=-50.
                ), 'erg/s/cm**3'
            )
            xray_luminosities = emissivities * sw_data.gas.masses / sw_data.gas.densities
            weighting = xray_luminosities[index]
            del data_nH, data_T, emissivities, xray_luminosities

        elif self.weighting == 'mass':
            weighting = sw_data.gas.masses[index]

        elif self.weighting == 'volume':
            volume_proxy = sw_data.gas.masses[index] / sw_data.gas.densities[index]
            weighting = volume_proxy
            del volume_proxy

        gas_mass = sw_data.gas.masses[index]

        gas_mass_profile = histogram_unyt(
            radial_distance,
            bins=lbins,
            weights=gas_mass,
        )
        gas_mass_profile = np.nancumsum(gas_mass_profile.value) * gas_mass_profile.units
        gas_mass_profile.convert_to_units(Solar_Mass)

        return radial_bin_centres, gas_mass_profile, m500
예제 #2
0
    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()
예제 #3
0
    def process_single_halo(
            self,
            zoom_obj: Zoom = None,
            path_to_snap: str = None,
            path_to_catalogue: str = None
    ):

        sw_data, vr_data = self.get_handles_from_zoom(
            zoom_obj,
            path_to_snap,
            path_to_catalogue,
            mask_radius_r500=self.max_radius_r500
        )
        fb = Cosmology().fb0
        setattr(self, 'fb', fb)
        setattr(self, 'z', sw_data.metadata.z)

        try:
            r500 = vr_data.spherical_overdensities.r_500_rhocrit[0].to('Mpc')
            m500 = vr_data.spherical_overdensities.mass_500_rhocrit[0].to('Msun')
        except AttributeError as err:
            print(f'[{self.__class__.__name__}] {err}')

            spherical_overdensity = SODelta500(
                path_to_snap=path_to_snap,
                path_to_catalogue=path_to_catalogue,
            )
            r500 = spherical_overdensity.get_r500()
            m500 = spherical_overdensity.get_m500()

        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
            )
            true_hse.interpolate_hse(density_contrast=500.)
            r500 = true_hse.r500hse
            m500 = true_hse.m500hse

        # Define radial bins
        lbins = np.logspace(-2, np.log10(self.max_radius_r500), 51) * dimensionless
        radial_bin_centres = 10 ** (0.5 * np.log10(lbins[1:] * lbins[:-1])) * dimensionless

        # Compute gas mass profile
        sw_data.gas.radial_distances.convert_to_physical()
        sw_data.gas.masses.convert_to_physical()
        masses = sw_data.gas.masses

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

        try:
            fof_ids = sw_data.gas.fofgroup_ids

            # Select all particles within sphere
            mask = np.where(
                (sw_data.gas.radial_distances <= self.max_radius_r500 * r500) &
                (fof_ids == 1) &
                (temperatures > Tcut_halogas)
            )[0]
            del fof_ids
        except AttributeError as err:
            print(err)
            print(f"[{self.__class__.__name__}] Select particles only by radial distance.")
            mask = np.where(
                (sw_data.gas.radial_distances <= self.max_radius_r500 * r500) &
                (temperatures > Tcut_halogas)
            )[0]

        radial_distances = sw_data.gas.radial_distances[mask] / r500
        assert (radial_distances >= 0).all()
        masses = unyt_array(masses, sw_data.units.mass)[mask]
        assert (masses >= 0).all()
        del mask

        mass_weights = histogram_unyt(radial_distances, bins=lbins, weights=masses)
        cumulative_gas_mass_profile = np.nancumsum(mass_weights.value) * masses.units

        # Compute total mass profile
        sw_data.gas.radial_distances.convert_to_physical()
        sw_data.dark_matter.radial_distances.convert_to_physical()

        radial_distances_collect = [
            sw_data.gas.radial_distances,
            sw_data.dark_matter.radial_distances,
        ]
        if sw_data.metadata.n_stars > 0:
            sw_data.stars.radial_distances.convert_to_physical()
            radial_distances_collect.append(sw_data.stars.radial_distances)
        elif xlargs.debug:
            print(f"[{self.__class__.__name__}] stars not detected.")

        if sw_data.metadata.n_black_holes > 0:
            sw_data.black_holes.radial_distances.convert_to_physical()
            radial_distances_collect.append(sw_data.black_holes.radial_distances)
        elif xlargs.debug:
            print(f"[{self.__class__.__name__}] black_holes not detected.")

        radial_distances = np.concatenate(radial_distances_collect) * sw_data.units.length / r500

        sw_data.gas.masses.convert_to_physical()
        sw_data.dark_matter.masses.convert_to_physical()
        masses_collect = [
            sw_data.gas.masses,
            sw_data.dark_matter.masses,
        ]
        if sw_data.metadata.n_stars > 0:
            sw_data.stars.masses.convert_to_physical()
            masses_collect.append(sw_data.stars.masses)
        elif xlargs.debug:
            print(f"[{self.__class__.__name__}] stars not detected.")

        if sw_data.metadata.n_black_holes > 0:
            sw_data.black_holes.subgrid_masses.convert_to_physical()
            masses_collect.append(sw_data.black_holes.subgrid_masses)
        elif xlargs.debug:
            print(f"[{self.__class__.__name__}] black_holes not detected.")

        masses = np.concatenate(masses_collect)

        try:
            fof_ids_collect = [
                sw_data.gas.fofgroup_ids,
                sw_data.dark_matter.fofgroup_ids,
            ]
            if sw_data.metadata.n_stars > 0:
                fof_ids_collect.append(sw_data.stars.fofgroup_ids)
            elif xlargs.debug:
                print(f"[{self.__class__.__name__}] stars not detected.")

            if sw_data.metadata.n_black_holes > 0:
                fof_ids_collect.append(sw_data.black_holes.fofgroup_ids)
            elif xlargs.debug:
                print(f"[{self.__class__.__name__}] black_holes not detected.")

            fof_ids = np.concatenate(fof_ids_collect)

            # Select all particles within sphere
            mask = np.where(
                (radial_distances <= self.max_radius_r500) &
                (fof_ids == 1)
            )[0]

            del fof_ids

        except AttributeError as err:
            print(err)
            print(f"[{self.__class__.__name__}] Select particles only by radial distance.")
            mask = np.where(radial_distances <= self.max_radius_r500)[0]

        mass_weights = histogram_unyt(radial_distances[mask], bins=lbins, weights=masses[mask])
        cumulative_mass_profile = np.nancumsum(mass_weights.value) * sw_data.units.mass

        return radial_bin_centres, cumulative_gas_mass_profile, cumulative_mass_profile, m500 * fb
예제 #4
0
    def process_single_halo(
            self,
            zoom_obj: Zoom = None,
            path_to_snap: str = None,
            path_to_catalogue: str = None,
            **kwargs
    ):
        sw_data, vr_data = self.get_handles_from_zoom(zoom_obj, path_to_snap, path_to_catalogue, **kwargs)

        if xlargs.mass_estimator == 'true':

            kwarg_parser = dict(zoom_obj=zoom_obj, path_to_snap=path_to_snap, path_to_catalogue=path_to_catalogue)

            try:
                r200 = vr_data.radii.r_200crit[0].to('Mpc')
            except AttributeError as err:
                print(err)
                if xlargs.debug:
                    print(f'[{self.__class__.__name__}] Launching spherical overdensity calculation...')
                spherical_overdensity = SODelta200(
                    path_to_snap=path_to_snap,
                    path_to_catalogue=path_to_catalogue,
                )
                r200 = spherical_overdensity.get_r200()

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

            try:
                r1000 = vr_data.spherical_overdensities.r_1000_rhocrit[0].to('Mpc')
            except AttributeError as err:
                print(err)
                if xlargs.debug:
                    print(f'[{self.__class__.__name__}] Launching spherical overdensity calculation...')
                r1000 = SphericalOverdensities(density_contrast=1000).process_single_halo(**kwarg_parser)[0]

            try:
                r1500 = vr_data.spherical_overdensities.r_1500_rhocrit[0].to('Mpc')
            except AttributeError as err:
                print(err)
                if xlargs.debug:
                    print(f'[{self.__class__.__name__}] Launching spherical overdensity calculation...')
                r1500 = SphericalOverdensities(density_contrast=1500).process_single_halo(**kwarg_parser)[0]

            try:
                r2500 = vr_data.spherical_overdensities.r_2500_rhocrit[0].to('Mpc')
            except AttributeError as err:
                print(err)
                if xlargs.debug:
                    print(f'[{self.__class__.__name__}] Launching spherical overdensity calculation...')
                spherical_overdensity = SODelta2500(
                    path_to_snap=path_to_snap,
                    path_to_catalogue=path_to_catalogue,
                )
                r2500 = spherical_overdensity.get_r2500()

        elif 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
            )

            for density_contrast in [200, 500, 1000, 1500, 2500]:
                true_hse.interpolate_hse(density_contrast=density_contrast)

            r200 = true_hse.r200hse
            r500 = true_hse.r500hse
            r1000 = true_hse.r1000hse
            r1500 = true_hse.r1500hse
            r2500 = true_hse.r2500hse

        sw_data.gas.radial_distances.convert_to_physical()

        # Select hot gas within sphere
        mask = np.where(
            (sw_data.gas.radial_distances <= 2 * r500) &
            (sw_data.gas.temperatures > Tcut_halogas) &
            (sw_data.gas.fofgroup_ids == 1)
        )[0]

        sw_data.gas.radial_distances = sw_data.gas.radial_distances[mask]
        sw_data.gas.masses = sw_data.gas.masses[mask]
        sw_data.gas.temperatures = sw_data.gas.temperatures[mask]

        radial_distances_scaled = sw_data.gas.radial_distances / r500

        # Define radial bins and shell volumes
        lbins = np.linspace(
            radial_distances_scaled.min().value,
            radial_distances_scaled.max().value,
            100
        ) * radial_distances_scaled.units
        radial_bin_centres = 10.0 ** (0.5 * np.log10(lbins[1:] * lbins[:-1])) * radial_distances_scaled.units
        volume_shell = (4. * np.pi / 3.) * (r500 ** 3) * ((lbins[1:]) ** 3 - (lbins[:-1]) ** 3)

        mass_weights, _ = histogram_unyt(radial_distances_scaled, 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 = (sw_data.gas.temperatures * kb).to('keV') * sw_data.gas.masses
        temperature_weights, _ = histogram_unyt(radial_distances_scaled, 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)

        entropy_interpolate = interp1d(radial_bin_centres * r500, entropy_profile, kind='linear')

        k30kpc = entropy_interpolate(0.03 * Mpc) * entropy_profile.units
        k0p15r500 = entropy_interpolate(0.15 * r500) * entropy_profile.units
        k2500 = entropy_interpolate(r2500) * entropy_profile.units
        k1500 = entropy_interpolate(r1500) * entropy_profile.units
        k1000 = entropy_interpolate(r1000) * entropy_profile.units
        k500 = entropy_interpolate(r500) * entropy_profile.units
        k200 = entropy_interpolate(r200) * entropy_profile.units

        return k30kpc, k0p15r500, k2500, k1500, k1000, k500, k200