from pathlib import Path from sys import argv from typing import List import numpy as np import pandas as pd from matplotlib import pyplot as plt from matplotlib.axes import Axes from matplotlib.axis import YTick from matplotlib.collections import QuadMesh from matplotlib.colors import LogNorm from matplotlib.figure import Figure from matplotlib.patches import Polygon from numpy import inf from scipy.stats import norm from halo_vis import get_comp_id from paths import base_dir from utils import figsize_from_page_fraction, rowcolumn_labels, waveforms, tex_fmt # density like in Vr: G = 43.022682 # in Mpc (km/s)^2 / (10^10 Msun) vmaxs = {"Mvir": 52, "Vmax": 93, "cNFW": 31} units = { "distance": "Mpc", "Mvir": r"10^{10} \textrm{ M}_\odot", "Vmax": r"\textrm{km } \textrm{s}^{-1}", # TODO } def concentration(row, halo_type: str) -> bool: r_200crit = row[f"{halo_type}_R_200crit"] if r_200crit <= 0: cnfw = -1 colour = "orange" return False # return cnfw, colour r_size = row[ f"{halo_type}_R_size" ] # largest difference from center of mass to any halo particle m_200crit = row[f"{halo_type}_Mass_200crit"] vmax = row[ f"{halo_type}_Vmax" ] # largest velocity coming from enclosed mass profile calculation rmax = row[f"{halo_type}_Rmax"] npart = row[f"{halo_type}_npart"] VmaxVvir2 = vmax ** 2 * r_200crit / (G * m_200crit) if VmaxVvir2 <= 1.05: if m_200crit == 0: cnfw = r_size / rmax return False # colour = 'white' else: cnfw = r_200crit / rmax return False # colour = 'white' else: if npart >= 100: # only calculate cnfw for groups with more than 100 particles cnfw = row[f"{halo_type}_cNFW"] return True # colour = 'black' else: if m_200crit == 0: cnfw = r_size / rmax return False # colour = 'white' else: cnfw = r_200crit / rmax return False # colour = 'white' # assert np.isclose(cnfw, row[f'{halo_type}_cNFW']) # # return cnfw, colour def plot_comparison_hist2d(ax: Axes, file: Path, property: str): print("WARNING: Can only plot hist2d of properties with comp_ or ref_ right now!") print(f" Selected property: {property}") x_col = f"ref_{property}" y_col = f"comp_{property}" df = pd.read_csv(file) # if mode == 'concentration_analysis': # min_x = min([min(df[x_col]), min(df[y_col])]) # max_x = max([max(df[x_col]), max(df[y_col])]) # df = df.loc[2 * df.ref_cNFW < df.comp_cNFW] # else: min_x = min([min(df[x_col]), min(df[y_col])]) max_x = max([max(df[x_col]), max(df[y_col])]) num_bins = 100 bins = np.geomspace(min_x, max_x, num_bins) if property == "cNFW": rows = [] for i, row in df.iterrows(): comp_cnfw_normal = concentration(row, halo_type="comp") ref_cnfw_normal = concentration(row, halo_type="ref") cnfw_normal = comp_cnfw_normal and ref_cnfw_normal if cnfw_normal: rows.append(row) df = pd.concat(rows, axis=1).T print(df) if property in ["Mvir", "Vmax"]: percentiles = [] for rep_row in range(num_bins): rep_x_left = bins[rep_row] rep_x_right = bins[rep_row] + 1 rep_bin = (rep_x_left < df[x_col]) & (df[x_col] < rep_x_right) rep_values = df.loc[rep_bin][y_col] / df.loc[rep_bin][x_col] if len(rep_values) < 10: percentiles.append([np.nan, np.nan, np.nan]) continue percentiles.append(np.quantile(rep_values, [norm.cdf(-1), norm.cdf(0), norm.cdf(1)])) percentiles = np.asarray(percentiles) print(percentiles.shape) args = {"color": "C1", "zorder": 10} # ax.fill_between(bins, percentiles[::, 0], percentiles[::, 2], alpha=0.1, **args) ax.plot(bins, percentiles[::, 0], alpha=0.9, **args) ax.plot(bins, percentiles[::, 1], alpha=0.9, **args) ax.plot(bins, percentiles[::, 2], alpha=0.9, **args) if property in vmaxs: vmax = vmaxs[property] else: vmax = None print("WARNING: vmax not set") image: QuadMesh _, _, _, image = ax.hist2d( df[x_col], df[y_col] / df[x_col], bins=(bins, np.linspace(0, 2, num_bins)), norm=LogNorm(vmax=vmax), cmap="gray_r", rasterized=True, ) # ax.plot([rep_x_left, rep_x_left], [mean - std, mean + std], c="C1") # ax.annotate( # text=f"std={std:.2f}", xy=(rep_x_left, mean + std), # textcoords="axes fraction", xytext=(0.1, 0.9), # arrowprops={} # ) print("vmin/vmax", image.norm.vmin, image.norm.vmax) # fig.colorbar(hist) ax.set_xscale("log") # ax.set_yscale("log") ax.set_xlim(min(df[x_col]), max(df[y_col])) ax.plot( [min(df[x_col]), max(df[y_col])], [1, 1], linewidth=1, color="C0", zorder=10, linestyle="dotted" ) return x_col, y_col # ax.set_title(file.name) # fig.savefig(Path(f"~/tmp/comparison_{file.stem}.pdf").expanduser()) # fig.suptitle def plot_comparison_hist(ax: Axes, file: Path, property: str, m_min=None, m_max=None): df = pd.read_csv(file) if m_min: df = df.loc[(m_min < df["ref_Mvir"]) & (df["ref_Mvir"] < m_max)] num_bins = 100 histtype = "bar" label = None density = False if property == "distance": bins = np.geomspace(min(df[property]), max(df[property]), 100) mean = df[property].mean() median = df[property].median() ax.axvline(mean, label="mean", color="C1") ax.axvline(median, label="median", color="C2") else: bins = num_bins if property == "match": labels = { (-inf, 30): "$M<30$", (None, None): "all $M$ $[10^{10}$ $\mathrm{M}_\odot]$", (30, 100): "$30 float: return mass / partmass def partnum2mass(partnum: float) -> float: return partnum * partmass sec_ax = ax.secondary_xaxis( "top", functions=(mass2partnum, partnum2mass) ) sec_ax.set_xlabel(r"\textrm{Halo Size }[\# \textrm{particles}]") # rowcolumn_labels(axes, comparisons, isrow=False) rowcolumn_labels(axes, waveforms, isrow=True) fig.tight_layout() fig.subplots_adjust(hspace=0) fig.savefig(Path(f"~/tmp/comparison_{property}.pdf").expanduser()) if show: plt.show() def main(): # properties = ['group_size', 'Mass_200crit', 'Mass_tot', 'Mvir', 'R_200crit', 'Rvir', 'Vmax', 'cNFW', 'q', # 's'] if len(argv) > 1: properties = argv[1:] else: properties = ["Mvir", "Vmax", "cNFW", "distance", "match"] for property in properties: compare_property(property, show=len(argv) == 2) if __name__ == "__main__": main()