mirror of
https://github.com/Richard-Sti/csiborgtools.git
synced 2024-12-22 17:08:03 +00:00
Matching of observations (#127)
* Rename file * Add indents * Update imports * Add counting * Docs * Add nb * Rename nb * Update nb * Add PV processing * Update nb * Add Pantheon+groups * Update submission scripts * Add Pantheon+zSN * Update nb * Edit param * Matchin SFI * Update nb * Fix path bug * Add list of clusters * Update imports * Update imports * Add cartesian & mass of clusters * Add observation to halo matching * Add nb * Add inverse CDF * Add import * Update nb * Add comments
This commit is contained in:
parent
3876985f26
commit
c4557cf35b
16 changed files with 1727 additions and 714 deletions
|
@ -14,6 +14,7 @@
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
from csiborgtools import clustering, field, flow, halo, match, read, summary # noqa
|
from csiborgtools import clustering, field, flow, halo, match, read, summary # noqa
|
||||||
|
|
||||||
|
from .clusters import clusters # noqa
|
||||||
from .utils import (center_of_mass, delta2ncells, number_counts, # noqa
|
from .utils import (center_of_mass, delta2ncells, number_counts, # noqa
|
||||||
periodic_distance, periodic_distance_two_points, # noqa
|
periodic_distance, periodic_distance_two_points, # noqa
|
||||||
binned_statistic, cosine_similarity, fprint, # noqa
|
binned_statistic, cosine_similarity, fprint, # noqa
|
||||||
|
@ -58,18 +59,3 @@ class SDSSxALFALFA:
|
||||||
survey = read.SDSS(fpath, h=1, sel_steps=sel_steps)
|
survey = read.SDSS(fpath, h=1, sel_steps=sel_steps)
|
||||||
survey.name = "SDSSxALFALFA"
|
survey.name = "SDSSxALFALFA"
|
||||||
return survey
|
return survey
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Clusters #
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
clusters = {"Virgo": read.ObservedCluster(RA=hms_to_degrees(12, 27),
|
|
||||||
dec=dms_to_degrees(12, 43),
|
|
||||||
dist=16.5 * 0.7,
|
|
||||||
name="Virgo"),
|
|
||||||
"Fornax": read.ObservedCluster(RA=hms_to_degrees(3, 38),
|
|
||||||
dec=dms_to_degrees(-35, 27),
|
|
||||||
dist=19 * 0.7,
|
|
||||||
name="Fornax"),
|
|
||||||
}
|
|
||||||
|
|
105
csiborgtools/clusters.py
Normal file
105
csiborgtools/clusters.py
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
# Copyright (C) 2024 Richard Stiskalek
|
||||||
|
# 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, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
"""
|
||||||
|
Database of a few nearby observed clusters. Can be augmented with the list
|
||||||
|
compiled in https://arxiv.org/abs/2402.01834 or some eROSITA clusters?
|
||||||
|
"""
|
||||||
|
from csiborgtools.read import ObservedCluster
|
||||||
|
from .utils import hms_to_degrees, dms_to_degrees
|
||||||
|
|
||||||
|
|
||||||
|
# https://arxiv.org/abs/astro-ph/0702510
|
||||||
|
# https://arxiv.org/abs/2002.12820
|
||||||
|
# https://en.wikipedia.org/wiki/Virgo_Cluster
|
||||||
|
VIRGO = ObservedCluster(
|
||||||
|
RA=hms_to_degrees(12, 27), dec=dms_to_degrees(12, 43), dist=16.5 * 0.73,
|
||||||
|
mass=6.3e14 * 0.73, name="Virgo")
|
||||||
|
|
||||||
|
# https://arxiv.org/abs/astro-ph/0702320
|
||||||
|
# https://en.wikipedia.org/wiki/Fornax_Cluster
|
||||||
|
FORNAX = ObservedCluster(
|
||||||
|
RA=hms_to_degrees(3, 35), dec=-35.7, dist=19.3 * 0.7,
|
||||||
|
mass=7e13 * 0.73, name="Fornax")
|
||||||
|
|
||||||
|
# https://en.wikipedia.org/wiki/Coma_Cluster
|
||||||
|
# https://arxiv.org/abs/2311.08603
|
||||||
|
COMA = ObservedCluster(
|
||||||
|
RA=hms_to_degrees(12, 59, 48.7), dec=dms_to_degrees(27, 58, 50),
|
||||||
|
dist=102.975 * 0.705, mass=1.2e15 * 0.73, name="Coma")
|
||||||
|
|
||||||
|
# https://en.wikipedia.org/wiki/Perseus_Cluster
|
||||||
|
# https://ui.adsabs.harvard.edu/abs/2020MNRAS.494.1681A/abstract
|
||||||
|
PERSEUS = ObservedCluster(
|
||||||
|
RA=hms_to_degrees(3, 18), dec=dms_to_degrees(41, 30),
|
||||||
|
dist=73.6 * 0.705, mass=1.2e15 * 0.7, name="Perseus")
|
||||||
|
|
||||||
|
# https://en.wikipedia.org/wiki/Centaurus_Cluster
|
||||||
|
# Not sure about the mass, couldn't find a good estimate. Some paper claimed
|
||||||
|
# 3e13 Msun, but that seems a little low?
|
||||||
|
CENTAURUS = ObservedCluster(
|
||||||
|
RA=hms_to_degrees(12, 48, 51.8), dec=dms_to_degrees(-41, 18, 21),
|
||||||
|
dist=52.4 * 0.705, mass=2e14 * 0.7, name="Centaurus")
|
||||||
|
|
||||||
|
# https://en.wikipedia.org/wiki/Shapley_Supercluster
|
||||||
|
# https://arxiv.org/abs/0805.0596
|
||||||
|
SHAPLEY = ObservedCluster(
|
||||||
|
RA=hms_to_degrees(13, 25), dec=dms_to_degrees(-30),
|
||||||
|
dist=136, mass=1e16 * 0.7, name="Shapley")
|
||||||
|
|
||||||
|
# https://en.wikipedia.org/wiki/Norma_Cluster
|
||||||
|
# https://arxiv.org/abs/0706.2227
|
||||||
|
NORMA = ObservedCluster(
|
||||||
|
RA=hms_to_degrees(16, 15, 32.8), dec=dms_to_degrees(-60, 53, 30),
|
||||||
|
dist=67.8 * 0.705, mass=1e15 * 0.7, name="Norma")
|
||||||
|
|
||||||
|
# Wikipedia seems to give the wrong distance.
|
||||||
|
# https://en.wikipedia.org/wiki/Leo_Cluster
|
||||||
|
# https://arxiv.org/abs/astro-ph/0406367
|
||||||
|
LEO = ObservedCluster(
|
||||||
|
RA=hms_to_degrees(11, 44, 36.5), dec=dms_to_degrees(19, 43, 32),
|
||||||
|
dist=91.3 * 0.705, mass=7e14 * 0.7, name="Leo")
|
||||||
|
|
||||||
|
# https://en.wikipedia.org/wiki/Hydra_Cluster
|
||||||
|
HYDRA = ObservedCluster(
|
||||||
|
RA=hms_to_degrees(9, 18), dec=dms_to_degrees(-12, 5),
|
||||||
|
dist=58.3 * 0.705, mass=4e15 * 0.7, name="Hydra")
|
||||||
|
|
||||||
|
# I think this is Pisces? Not very sure about its mass.
|
||||||
|
# https://en.wikipedia.org/wiki/Abell_262
|
||||||
|
# https://arxiv.org/abs/0911.1774
|
||||||
|
PISCES = ObservedCluster(
|
||||||
|
RA=hms_to_degrees(1, 52, 50.4), dec=dms_to_degrees(36, 8, 46),
|
||||||
|
dist=68.8 * 0.705, mass=2e14 * 0.7, name="Pisces")
|
||||||
|
|
||||||
|
# This one is in the ZOA
|
||||||
|
# https://en.wikipedia.org/wiki/Ophiuchus_Supercluster
|
||||||
|
# https://arxiv.org/abs/1509.00986
|
||||||
|
OPICHIUS = ObservedCluster(
|
||||||
|
RA=hms_to_degrees(17, 10, 0), dec=dms_to_degrees(-22),
|
||||||
|
dist=83.4, mass=1e15 * 0.7, name="Ophiuchus")
|
||||||
|
|
||||||
|
|
||||||
|
clusters = {"Virgo": VIRGO,
|
||||||
|
"Fornax": FORNAX,
|
||||||
|
"Coma": COMA,
|
||||||
|
"Perseus": PERSEUS,
|
||||||
|
"Centaurus": CENTAURUS,
|
||||||
|
"Shapley": SHAPLEY,
|
||||||
|
"Norma": NORMA,
|
||||||
|
"Leo": LEO,
|
||||||
|
"Hydra": HYDRA,
|
||||||
|
"Pisces": PISCES,
|
||||||
|
"Opichius": OPICHIUS,
|
||||||
|
}
|
|
@ -212,7 +212,12 @@ class DataLoader:
|
||||||
raise ValueError("Invalid simulation index.")
|
raise ValueError("Invalid simulation index.")
|
||||||
nsim = nsims[ksim]
|
nsim = nsims[ksim]
|
||||||
|
|
||||||
with File(paths.field_los(simname, catalogue), 'r') as f:
|
if "Pantheon+" in catalogue:
|
||||||
|
fpath = paths.field_los(simname, "Pantheon+")
|
||||||
|
else:
|
||||||
|
fpath = paths.field_los(simname, catalogue)
|
||||||
|
|
||||||
|
with File(fpath, 'r') as f:
|
||||||
has_smoothed = True if f[f"density_{nsim}"].ndim > 2 else False
|
has_smoothed = True if f[f"density_{nsim}"].ndim > 2 else False
|
||||||
if has_smoothed and (ksmooth is None or not isinstance(ksmooth, int)): # noqa
|
if has_smoothed and (ksmooth is None or not isinstance(ksmooth, int)): # noqa
|
||||||
raise ValueError("The output contains smoothed field but "
|
raise ValueError("The output contains smoothed field but "
|
||||||
|
@ -236,8 +241,13 @@ class DataLoader:
|
||||||
for key in f.keys():
|
for key in f.keys():
|
||||||
arr[key] = f[key][:]
|
arr[key] = f[key][:]
|
||||||
elif catalogue in ["LOSS", "Foundation", "SFI_gals", "2MTF",
|
elif catalogue in ["LOSS", "Foundation", "SFI_gals", "2MTF",
|
||||||
"Pantheon+", "SFI_gals_masked", "SFI_groups"]:
|
"Pantheon+", "SFI_gals_masked", "SFI_groups",
|
||||||
|
"Pantheon+_groups", "Pantheon+_groups_zSN",
|
||||||
|
"Pantheon+_zSN"]:
|
||||||
with File(catalogue_fpath, 'r') as f:
|
with File(catalogue_fpath, 'r') as f:
|
||||||
|
if "Pantheon+" in catalogue:
|
||||||
|
grp = f["Pantheon+"]
|
||||||
|
else:
|
||||||
grp = f[catalogue]
|
grp = f[catalogue]
|
||||||
|
|
||||||
dtype = [(key, np.float32) for key in grp.keys()]
|
dtype = [(key, np.float32) for key in grp.keys()]
|
||||||
|
@ -1381,15 +1391,26 @@ def get_model(loader, zcmb_max=None, verbose=True):
|
||||||
los_overdensity[mask], los_velocity[mask], RA[mask], dec[mask],
|
los_overdensity[mask], los_velocity[mask], RA[mask], dec[mask],
|
||||||
zCMB[mask], mB[mask], x1[mask], c[mask], e_mB[mask], e_x1[mask],
|
zCMB[mask], mB[mask], x1[mask], c[mask], e_mB[mask], e_x1[mask],
|
||||||
e_c[mask], loader.rdist, loader._Omega_m)
|
e_c[mask], loader.rdist, loader._Omega_m)
|
||||||
elif kind == "Pantheon+":
|
elif "Pantheon+" in kind:
|
||||||
keys = ["RA", "DEC", "zCMB", "mB", "x1", "c", "biasCor_m_b", "mBERR",
|
keys = ["RA", "DEC", "zCMB", "mB", "x1", "c", "biasCor_m_b", "mBERR",
|
||||||
"x1ERR", "cERR", "biasCorErr_m_b"]
|
"x1ERR", "cERR", "biasCorErr_m_b", "zCMB_SN", "zCMB_Group"]
|
||||||
|
|
||||||
RA, dec, zCMB, mB, x1, c, bias_corr_mB, e_mB, e_x1, e_c, e_bias_corr_mB = (loader.cat[k] for k in keys) # noqa
|
RA, dec, zCMB, mB, x1, c, bias_corr_mB, e_mB, e_x1, e_c, e_bias_corr_mB, zCMB_SN, zCMB_Group = (loader.cat[k] for k in keys) # noqa
|
||||||
mB -= bias_corr_mB
|
mB -= bias_corr_mB
|
||||||
e_mB = np.sqrt(e_mB**2 + e_bias_corr_mB**2)
|
e_mB = np.sqrt(e_mB**2 + e_bias_corr_mB**2)
|
||||||
|
|
||||||
mask = (zCMB < zcmb_max)
|
mask = (zCMB < zcmb_max)
|
||||||
|
|
||||||
|
if kind == "Pantheon+_groups":
|
||||||
|
mask &= np.isfinite(zCMB_Group)
|
||||||
|
|
||||||
|
if kind == "Pantheon+_groups_zSN":
|
||||||
|
mask &= np.isfinite(zCMB_Group)
|
||||||
|
zCMB = zCMB_SN
|
||||||
|
|
||||||
|
if kind == "Pantheon+_zSN":
|
||||||
|
zCMB = zCMB_SN
|
||||||
|
|
||||||
model = SN_PV_validation_model(
|
model = SN_PV_validation_model(
|
||||||
los_overdensity[mask], los_velocity[mask], RA[mask], dec[mask],
|
los_overdensity[mask], los_velocity[mask], RA[mask], dec[mask],
|
||||||
zCMB[mask], mB[mask], x1[mask], c[mask], e_mB[mask], e_x1[mask],
|
zCMB[mask], mB[mask], x1[mask], c[mask], e_mB[mask], e_x1[mask],
|
||||||
|
|
|
@ -12,5 +12,6 @@
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
from .match import (ParticleOverlap, RealisationsMatcher, calculate_overlap, # noqa
|
from .overlap import (ParticleOverlap, RealisationsMatcher, calculate_overlap, # noqa
|
||||||
find_neighbour, matching_max) # noqa
|
find_neighbour, matching_max) # noqa
|
||||||
|
from .obs_to_box import (MatchingProbability, MatchCatalogues) # noqa
|
||||||
|
|
396
csiborgtools/match/obs_to_box.py
Normal file
396
csiborgtools/match/obs_to_box.py
Normal file
|
@ -0,0 +1,396 @@
|
||||||
|
# Copyright (C) 2024 Richard Stiskalek
|
||||||
|
# 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, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
"""
|
||||||
|
Code to match observations to a constrained simulation.
|
||||||
|
"""
|
||||||
|
from abc import ABC
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from colossus.cosmology import cosmology
|
||||||
|
from colossus.lss import mass_function
|
||||||
|
from scipy.interpolate import interp1d
|
||||||
|
from sklearn.neighbors import NearestNeighbors
|
||||||
|
from tqdm import trange
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Matching probability class #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
class BaseMatchingProbability(ABC):
|
||||||
|
"""Base class for `MatchingProbability`."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def halo_pos(self):
|
||||||
|
"""
|
||||||
|
Halo positions in the constrained simulation.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
2-dimensional array of shape `(n, 3)`
|
||||||
|
"""
|
||||||
|
return self._halo_pos
|
||||||
|
|
||||||
|
@halo_pos.setter
|
||||||
|
def halo_pos(self, x):
|
||||||
|
if not isinstance(x, np.ndarray) and x.ndim == 2 and x.shape[1] == 3:
|
||||||
|
raise ValueError("Invalid halo positions.")
|
||||||
|
self._halo_pos = x
|
||||||
|
|
||||||
|
@property
|
||||||
|
def halo_log_mass(self):
|
||||||
|
"""
|
||||||
|
Halo log mass in the constrained simulation.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
1-dimensional array of shape `(n,)`
|
||||||
|
"""
|
||||||
|
return self._halo_log_mass
|
||||||
|
|
||||||
|
@halo_log_mass.setter
|
||||||
|
def halo_log_mass(self, x):
|
||||||
|
if not isinstance(x, np.ndarray) and x.ndim == 1 and len(x) != len(self.halo_pos): # noqa
|
||||||
|
raise ValueError("Invalid halo log mass.")
|
||||||
|
self._halo_log_mass = x
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nhalo(self):
|
||||||
|
""""
|
||||||
|
Number of haloes in the constrained simulation that are used for
|
||||||
|
matching.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
int
|
||||||
|
"""
|
||||||
|
return self.halo_log_mass.size
|
||||||
|
|
||||||
|
def HMF(self, log_mass):
|
||||||
|
"""
|
||||||
|
Evaluate the halo mass function at a given mass.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
log_mass : float
|
||||||
|
Logarithmic mass of the halo in `Msun / h`.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
HMF : float
|
||||||
|
The HMF in `h^3 Mpc^-3 dex^-1`.
|
||||||
|
"""
|
||||||
|
return self._hmf(log_mass)
|
||||||
|
|
||||||
|
|
||||||
|
class MatchingProbability(BaseMatchingProbability):
|
||||||
|
""""
|
||||||
|
Matching probability by calculating the CDF of finding a halo of a certain
|
||||||
|
mass at a given distance from a reference point. Calibrated against a HMF,
|
||||||
|
by assuming that the haloes are uniformly distributed. This is only
|
||||||
|
approximate treatment, as the haloes are not uniformly distributed, however
|
||||||
|
it is sufficient for the present purposes.
|
||||||
|
|
||||||
|
NOTE: The method currently does not account for uncertainty in distance.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
halo_pos : 2-dimensional array of shape `(n, 3)`
|
||||||
|
Halo positions in the constrained simulation in `Mpc / h`.
|
||||||
|
halo_log_mass : 1-dimensional array of shape `(n,)`
|
||||||
|
Halo log mass in the constrained simulation in `Msun / h`.
|
||||||
|
mdef : str, optional
|
||||||
|
Definition of the halo mass. Default is 'fof'.
|
||||||
|
cosmo_params : dict, optional
|
||||||
|
Cosmological parameters of the constrained simulation.
|
||||||
|
"""
|
||||||
|
def __init__(self, halo_pos, halo_log_mass, mdef="fof",
|
||||||
|
cosmo_params={'flat': True, 'H0': 67.66, 'Om0': 0.3111,
|
||||||
|
'Ob0': 0.0489, 'sigma8': 0.8101, 'ns': 0.9665}):
|
||||||
|
self.halo_pos = halo_pos
|
||||||
|
self.halo_log_mass = halo_log_mass
|
||||||
|
|
||||||
|
# Define the kNN object and fit it to the halo positions, so that we
|
||||||
|
# can quickly query distances to an arbitrary point.
|
||||||
|
self._knn = NearestNeighbors()
|
||||||
|
self._knn.fit(halo_pos)
|
||||||
|
|
||||||
|
# Next, get the HMF from colossus and create its interpolant.
|
||||||
|
cosmology.addCosmology("myCosmo", **cosmo_params)
|
||||||
|
cosmology.setCosmology("myCosmo")
|
||||||
|
|
||||||
|
x = np.logspace(10, 16, 10000)
|
||||||
|
y = mass_function.massFunction(
|
||||||
|
x, 0.0, mdef=mdef, model="angulo12", q_out="dndlnM") * np.log(10)
|
||||||
|
self._hmf = interp1d(np.log10(x), y, kind="cubic")
|
||||||
|
|
||||||
|
def pdf(self, r, log_mass):
|
||||||
|
"""
|
||||||
|
Calculate the PDF of finding a halo of a given mass at a given distance
|
||||||
|
from a random point.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
r : float
|
||||||
|
Distance from the random point in `Mpc / h`.
|
||||||
|
log_mass : float
|
||||||
|
Logarithmic mass of the halo in `Msun / h`.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
float
|
||||||
|
"""
|
||||||
|
nd = self.HMF(log_mass)
|
||||||
|
return 4 * np.pi * r**2 * nd * np.exp(-4 / 3 * np.pi * r**3 * nd)
|
||||||
|
|
||||||
|
def cdf(self, r, log_mass):
|
||||||
|
"""
|
||||||
|
Calculate the CDF of finding a halo of a given mass at a given distance
|
||||||
|
from a random point.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
r : float
|
||||||
|
Distance from the random point in `Mpc / h`.
|
||||||
|
log_mass : float
|
||||||
|
Logarithmic mass of the halo in `Msun / h`.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
float
|
||||||
|
"""
|
||||||
|
nd = self.HMF(log_mass)
|
||||||
|
return 1 - np.exp(-4 / 3 * np.pi * r**3 * nd)
|
||||||
|
|
||||||
|
def inverse_cdf(self, cdf, log_mass):
|
||||||
|
"""
|
||||||
|
Calculate the inverse CDF of finding a halo of a given mass at a given
|
||||||
|
distance from a random point.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
cdf : float
|
||||||
|
CDF of finding a halo of a given mass at a given distance.
|
||||||
|
log_mass : float
|
||||||
|
Logarithmic mass of the halo in `Msun / h`.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
float
|
||||||
|
"""
|
||||||
|
nd = self.HMF(log_mass)
|
||||||
|
return (np.log(1 - cdf) / (-4 / 3 * np.pi * nd))**(1 / 3)
|
||||||
|
|
||||||
|
def cdf_per_halo(self, refpos, ref_log_mass=None, rmax=50,
|
||||||
|
return_full=True):
|
||||||
|
"""
|
||||||
|
Calculate the CDF per each halo in the constrained simulation.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
refpos : 1-dimensional array of shape `(3,)`
|
||||||
|
Reference position in `Mpc / h`.
|
||||||
|
ref_log_mass : float, optional
|
||||||
|
Reference log mass, used to calculate the difference in log mass
|
||||||
|
between the reference and each halo.
|
||||||
|
rmax : float, optional
|
||||||
|
Maximum distance from the reference point to consider. Below this,
|
||||||
|
the CDF is simply set to 1.
|
||||||
|
return_full : bool, optional
|
||||||
|
If `True`, return the CDF, dlogmass and indxs for all haloes,
|
||||||
|
otherwise return only the haloes within `rmax`.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
cdf : 1-dimensional array of shape `(nhalo,)`
|
||||||
|
CDF per halo.
|
||||||
|
dlogmass : 1-dimensional array of shape `(nhalo,)`
|
||||||
|
Difference in log mass between the reference and each halo.
|
||||||
|
indxs : 1-dimensional array of shape `(nhalo,)`
|
||||||
|
Indices of the haloes.
|
||||||
|
"""
|
||||||
|
if not (isinstance(refpos, np.ndarray) and refpos.ndim == 1):
|
||||||
|
raise ValueError("Invalid reference position.")
|
||||||
|
if ref_log_mass is not None and not isinstance(ref_log_mass, (float, int, np.float32, np.float64)): # noqa
|
||||||
|
raise ValueError("Invalid reference log mass.")
|
||||||
|
|
||||||
|
# Use the kNN to pick out the haloes within `rmax` of the reference
|
||||||
|
# point.
|
||||||
|
dist, indxs = self._knn.radius_neighbors(
|
||||||
|
refpos.reshape(-1, 3), rmax, return_distance=True)
|
||||||
|
dist, indxs = dist[0], indxs[0]
|
||||||
|
|
||||||
|
cdf_ = self.cdf(dist, self.halo_log_mass[indxs])
|
||||||
|
if ref_log_mass is not None:
|
||||||
|
dlogmass_ = self.halo_log_mass[indxs] - ref_log_mass
|
||||||
|
else:
|
||||||
|
dlogmass_ = None
|
||||||
|
|
||||||
|
if return_full:
|
||||||
|
cdf = np.ones(self.nhalo)
|
||||||
|
cdf[indxs] = cdf_
|
||||||
|
if ref_log_mass is not None:
|
||||||
|
dlogmass = np.full(self.nhalo, np.infty)
|
||||||
|
dlogmass[indxs] = dlogmass_
|
||||||
|
else:
|
||||||
|
dlogmass = dlogmass_
|
||||||
|
|
||||||
|
indxs = np.arange(self.nhalo)
|
||||||
|
else:
|
||||||
|
cdf, dlogmass = cdf_, dlogmass_
|
||||||
|
|
||||||
|
return cdf, dlogmass, indxs
|
||||||
|
|
||||||
|
def match_halo(self, refpos, ref_log_mass, pvalue_threshold=0.005,
|
||||||
|
max_absdlogmass=1., rmax=50, verbose=True,
|
||||||
|
catalogue_index=0):
|
||||||
|
"""
|
||||||
|
Match a halo in the constrained simulation to a reference halo.
|
||||||
|
Considers match the highest significance halo within `rmax` and
|
||||||
|
within `max_absdlogmass` of the reference halo mass. In case of no
|
||||||
|
match, returns `None`.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
refpos : 1-dimensional array of shape `(3,)`
|
||||||
|
Reference position.
|
||||||
|
ref_log_mass : float
|
||||||
|
Reference log mass.
|
||||||
|
pvalue_threshold : float, optional
|
||||||
|
Threshold for the CDF to be considered a match.
|
||||||
|
max_absdlogmass : float, optional
|
||||||
|
Maximum difference in log mass between the reference and the
|
||||||
|
matched halo.
|
||||||
|
rmax : float, optional
|
||||||
|
Maximum distance from the reference point to consider.
|
||||||
|
verbose : bool, optional
|
||||||
|
If `True`, print information about the match.
|
||||||
|
catalogue_index : int, optional
|
||||||
|
Optional catalogue index for more informative printing.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
cdf : float, or None
|
||||||
|
CDF of the matched halo (significance), if any.
|
||||||
|
index : int, or None
|
||||||
|
Index of the matched halo, if any.
|
||||||
|
"""
|
||||||
|
cdf, dlogmass, indxs = self.cdf_per_halo(
|
||||||
|
refpos, ref_log_mass, rmax, return_full=False)
|
||||||
|
|
||||||
|
dlogmass = np.abs(dlogmass)
|
||||||
|
ks = np.argsort(cdf)
|
||||||
|
cdf, dlogmass, indxs = cdf[ks], dlogmass[ks], indxs[ks]
|
||||||
|
|
||||||
|
matches = np.where(
|
||||||
|
(cdf < pvalue_threshold) & (dlogmass < max_absdlogmass))[0]
|
||||||
|
|
||||||
|
if len(matches) == 0:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
if verbose and len(matches) > 1:
|
||||||
|
print(f"Found {len(matches)} plausible matches in catalogue {catalogue_index}.") # noqa
|
||||||
|
for i, k in enumerate(matches):
|
||||||
|
j = indxs[k]
|
||||||
|
logM = self.halo_log_mass[j]
|
||||||
|
dx = np.linalg.norm(self.halo_pos[j] - refpos)
|
||||||
|
print(f" {i + 1}: CDF = {cdf[k]:.3e}, index = {j}, logM = {logM:.3f} Msun / h, dx = {dx:.3f} Mpc / h.") # noqa
|
||||||
|
|
||||||
|
print(flush=True)
|
||||||
|
|
||||||
|
k = matches[0]
|
||||||
|
return cdf[k], indxs[k]
|
||||||
|
|
||||||
|
|
||||||
|
class MatchCatalogues:
|
||||||
|
"""
|
||||||
|
A wrapper for `MatchingProbability` that allows to match observed clusters
|
||||||
|
to haloes in multiple catalogues.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
catalogues : list
|
||||||
|
List of halo catalogues of constrained simulations.
|
||||||
|
cosmo_params : dict, optional
|
||||||
|
Cosmological parameters of the constrained simulation to calculate
|
||||||
|
the corresponding FOF mass function.
|
||||||
|
"""
|
||||||
|
def __init__(self, catalogues,
|
||||||
|
cosmo_params={'flat': True, 'H0': 67.66, 'Om0': 0.3111,
|
||||||
|
'Ob0': 0.0489, 'sigma8': 0.8101, 'ns': 0.9665}):
|
||||||
|
mdef = "fof"
|
||||||
|
self._catalogues = catalogues
|
||||||
|
self._prob_models = [None] * len(catalogues)
|
||||||
|
|
||||||
|
for i in trange(len(catalogues)):
|
||||||
|
pos = catalogues[i]["cartesian_pos"]
|
||||||
|
log_mass = np.log10(catalogues[i]["totmass"])
|
||||||
|
|
||||||
|
self._prob_models[i] = MatchingProbability(
|
||||||
|
pos, log_mass, mdef, cosmo_params)
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
return self._prob_models[index]
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._catalogues)
|
||||||
|
|
||||||
|
def __call__(self, refpos, ref_log_mass, pvalue_threshold=0.05,
|
||||||
|
max_absdlogmass=1., rmax=50, verbose=True):
|
||||||
|
"""
|
||||||
|
Calculate the CDFs of finding a halo of a certain mass at a given
|
||||||
|
distance from a reference point for all catalogues.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
refpos : 1-dimensional array of shape `(3,)`
|
||||||
|
Reference position.
|
||||||
|
ref_log_mass : float
|
||||||
|
Reference log mass.
|
||||||
|
pvalue_threshold : float, optional
|
||||||
|
Threshold for the CDF to be considered a match.
|
||||||
|
max_absdlogmass : float, optional
|
||||||
|
Maximum difference in log mass between the reference and the
|
||||||
|
matched halo.
|
||||||
|
rmax : float, optional
|
||||||
|
Maximum distance from the reference point to consider.
|
||||||
|
verbose : bool, optional
|
||||||
|
Verbosity flag.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
cdfs : dict
|
||||||
|
Dictionary of CDFs per halo, with keys being the simulation
|
||||||
|
indices.
|
||||||
|
indxs : dict
|
||||||
|
Dictionary of indices of the matched haloes, with keys being the
|
||||||
|
simulation indices.
|
||||||
|
"""
|
||||||
|
cdfs, indxs = {}, {}
|
||||||
|
for i in trange(len(self), desc="Matching catalogues",
|
||||||
|
disable=not verbose):
|
||||||
|
cdf, indx = self._prob_models[i].match_halo(
|
||||||
|
refpos, ref_log_mass, pvalue_threshold, max_absdlogmass, rmax,
|
||||||
|
verbose, i)
|
||||||
|
|
||||||
|
if cdf is not None:
|
||||||
|
cdfs[i] = cdf
|
||||||
|
indxs[i] = indx
|
||||||
|
|
||||||
|
n = len(self) - len(cdfs)
|
||||||
|
if n > 0 and verbose:
|
||||||
|
print(f"Failed to assign haloes in {n} catalogues.")
|
||||||
|
|
||||||
|
return cdfs, indxs
|
|
@ -26,6 +26,8 @@ from astropy.io import fits
|
||||||
from astropy.cosmology import FlatLambdaCDM
|
from astropy.cosmology import FlatLambdaCDM
|
||||||
from scipy import constants
|
from scipy import constants
|
||||||
|
|
||||||
|
from ..utils import radec_to_cartesian
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Text survey base class #
|
# Text survey base class #
|
||||||
|
@ -756,6 +758,42 @@ class BaseSingleObservation(ABC):
|
||||||
|
|
||||||
self._spherical_pos = pos
|
self._spherical_pos = pos
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mass(self):
|
||||||
|
"""
|
||||||
|
Total mass estimate in Msun / h.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
float
|
||||||
|
"""
|
||||||
|
if self._mass is None:
|
||||||
|
raise ValueError("`mass` is not set!")
|
||||||
|
return self._mass
|
||||||
|
|
||||||
|
@mass.setter
|
||||||
|
def mass(self, mass):
|
||||||
|
if not isinstance(mass, (int, float)):
|
||||||
|
raise ValueError("`mass` must be a float.")
|
||||||
|
self._mass = mass
|
||||||
|
|
||||||
|
def cartesian_pos(self, boxsize):
|
||||||
|
"""
|
||||||
|
Cartesian position of the observation in Mpc / h, assuming the observer
|
||||||
|
is in the centre of the box.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
boxsize : float
|
||||||
|
Box size in Mpc / h.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
1-dimensional array of shape (3,)
|
||||||
|
"""
|
||||||
|
return radec_to_cartesian(
|
||||||
|
self.spherical_pos.reshape(1, 3)).reshape(-1,) + boxsize / 2
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""
|
"""
|
||||||
|
@ -788,13 +826,16 @@ class ObservedCluster(BaseSingleObservation):
|
||||||
Declination in degrees.
|
Declination in degrees.
|
||||||
dist : float
|
dist : float
|
||||||
Distance in Mpc / h.
|
Distance in Mpc / h.
|
||||||
|
mass : float
|
||||||
|
Total mass estimate in Msun / h.
|
||||||
name : str
|
name : str
|
||||||
Cluster name.
|
Cluster name.
|
||||||
"""
|
"""
|
||||||
def __init__(self, RA, dec, dist, name):
|
def __init__(self, RA, dec, dist, mass, name):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.name = name
|
self.name = name
|
||||||
self.spherical_pos = [dist, RA, dec]
|
self.spherical_pos = [dist, RA, dec]
|
||||||
|
self.mass = mass
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -38,7 +38,7 @@ def read_samples(catalogue, simname, ksmooth, include_calibration=False,
|
||||||
Vx, Vy, Vz, beta, sigma_v, alpha = [], [], [], [], [], []
|
Vx, Vy, Vz, beta, sigma_v, alpha = [], [], [], [], [], []
|
||||||
BIC, AIC, logZ, chi2 = [], [], [], []
|
BIC, AIC, logZ, chi2 = [], [], [], []
|
||||||
|
|
||||||
if catalogue in ["LOSS", "Foundation", "Pantheon+"]:
|
if catalogue in ["LOSS", "Foundation"] or "Pantheon+" in catalogue:
|
||||||
alpha_cal, beta_cal, mag_cal, e_mu_intrinsic = [], [], [], []
|
alpha_cal, beta_cal, mag_cal, e_mu_intrinsic = [], [], [], []
|
||||||
elif catalogue in ["2MTF", "SFI_gals", "SFI_gals_masked"]:
|
elif catalogue in ["2MTF", "SFI_gals", "SFI_gals_masked"]:
|
||||||
a, b, e_mu_intrinsic = [], [], []
|
a, b, e_mu_intrinsic = [], [], []
|
||||||
|
@ -86,7 +86,7 @@ def read_samples(catalogue, simname, ksmooth, include_calibration=False,
|
||||||
except KeyError:
|
except KeyError:
|
||||||
chi2.append([0.])
|
chi2.append([0.])
|
||||||
|
|
||||||
if catalogue in ["LOSS", "Foundation", "Pantheon+"]:
|
if catalogue in ["LOSS", "Foundation"] or "Pantheon+" in catalogue: # noqa
|
||||||
alpha_cal.append(f[f"sim_{nsim}/alpha_cal"][:])
|
alpha_cal.append(f[f"sim_{nsim}/alpha_cal"][:])
|
||||||
beta_cal.append(f[f"sim_{nsim}/beta_cal"][:])
|
beta_cal.append(f[f"sim_{nsim}/beta_cal"][:])
|
||||||
mag_cal.append(f[f"sim_{nsim}/mag_cal"][:])
|
mag_cal.append(f[f"sim_{nsim}/mag_cal"][:])
|
||||||
|
@ -106,7 +106,7 @@ def read_samples(catalogue, simname, ksmooth, include_calibration=False,
|
||||||
|
|
||||||
gof = np.hstack(BIC), np.hstack(AIC), np.hstack(logZ), np.hstack(chi2)
|
gof = np.hstack(BIC), np.hstack(AIC), np.hstack(logZ), np.hstack(chi2)
|
||||||
|
|
||||||
if catalogue in ["LOSS", "Foundation", "Pantheon+"]:
|
if catalogue in ["LOSS", "Foundation"] or "Pantheon+" in catalogue:
|
||||||
alpha_cal, beta_cal, mag_cal, e_mu_intrinsic = np.hstack(alpha_cal), np.hstack(beta_cal), np.hstack(mag_cal), np.hstack(e_mu_intrinsic) # noqa
|
alpha_cal, beta_cal, mag_cal, e_mu_intrinsic = np.hstack(alpha_cal), np.hstack(beta_cal), np.hstack(mag_cal), np.hstack(e_mu_intrinsic) # noqa
|
||||||
elif catalogue in ["2MTF", "SFI_gals", "SFI_gals_masked"]:
|
elif catalogue in ["2MTF", "SFI_gals", "SFI_gals_masked"]:
|
||||||
a, b, e_mu_intrinsic = np.hstack(a), np.hstack(b), np.hstack(e_mu_intrinsic) # noqa
|
a, b, e_mu_intrinsic = np.hstack(a), np.hstack(b), np.hstack(e_mu_intrinsic) # noqa
|
||||||
|
@ -118,6 +118,7 @@ def read_samples(catalogue, simname, ksmooth, include_calibration=False,
|
||||||
raise ValueError(f"Catalogue {catalogue} not recognized.")
|
raise ValueError(f"Catalogue {catalogue} not recognized.")
|
||||||
|
|
||||||
# Calculate magnitude of V_ext
|
# Calculate magnitude of V_ext
|
||||||
|
|
||||||
Vmag = np.sqrt(Vx**2 + Vy**2 + Vz**2)
|
Vmag = np.sqrt(Vx**2 + Vy**2 + Vz**2)
|
||||||
# Calculate direction in galactic coordinates of V_ext
|
# Calculate direction in galactic coordinates of V_ext
|
||||||
V = np.vstack([Vx, Vy, Vz]).T
|
V = np.vstack([Vx, Vy, Vz]).T
|
||||||
|
@ -128,7 +129,7 @@ def read_samples(catalogue, simname, ksmooth, include_calibration=False,
|
||||||
names = ["alpha", "beta", "Vmag", "l", "b", "sigma_v"]
|
names = ["alpha", "beta", "Vmag", "l", "b", "sigma_v"]
|
||||||
|
|
||||||
if include_calibration:
|
if include_calibration:
|
||||||
if catalogue in ["LOSS", "Foundation", "Pantheon+"]:
|
if catalogue in ["LOSS", "Foundation"] or "Pantheon+" in catalogue:
|
||||||
data += [alpha_cal, beta_cal, mag_cal, e_mu_intrinsic]
|
data += [alpha_cal, beta_cal, mag_cal, e_mu_intrinsic]
|
||||||
names += ["alpha_cal", "beta_cal", "mag_cal", "e_mu_intrinsic"]
|
names += ["alpha_cal", "beta_cal", "mag_cal", "e_mu_intrinsic"]
|
||||||
elif catalogue in ["2MTF", "SFI_gals", "SFI_gals_masked"]:
|
elif catalogue in ["2MTF", "SFI_gals", "SFI_gals_masked"]:
|
||||||
|
|
436
notebooks/flow/process_PV.ipynb
Normal file
436
notebooks/flow/process_PV.ipynb
Normal file
File diff suppressed because one or more lines are too long
368
notebooks/match_observation/match_clusters.ipynb
Normal file
368
notebooks/match_observation/match_clusters.ipynb
Normal file
File diff suppressed because one or more lines are too long
25
notebooks/match_observation/match_clusters.py
Normal file
25
notebooks/match_observation/match_clusters.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import csiborgtools
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
|
||||||
|
def open_cat(nsim, simname, bounds):
|
||||||
|
if "csiborg1" in simname:
|
||||||
|
cat = csiborgtools.read.CSiBORG1Catalogue(nsim, bounds=bounds)
|
||||||
|
elif "csiborg2" in simname:
|
||||||
|
cat = csiborgtools.read.CSiBORG2Catalogue(
|
||||||
|
nsim, 99, simname.split("_")[-1], bounds=bounds)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown simulation name: {simname}.")
|
||||||
|
|
||||||
|
return cat
|
||||||
|
|
||||||
|
|
||||||
|
def open_cats(simname, bounds):
|
||||||
|
paths = csiborgtools.read.Paths(**csiborgtools.paths_glamdring)
|
||||||
|
nsims = paths.get_ics(simname)
|
||||||
|
catalogues = [None] * len(nsims)
|
||||||
|
|
||||||
|
for i, nsim in enumerate(tqdm(nsims, desc="Opening catalogues")):
|
||||||
|
catalogues[i] = open_cat(nsim, simname, bounds)
|
||||||
|
|
||||||
|
return catalogues
|
|
@ -61,7 +61,7 @@ def get_los(catalogue_name, simname, comm):
|
||||||
if catalogue_name in ["LOSS", "Foundation", "SFI_gals",
|
if catalogue_name in ["LOSS", "Foundation", "SFI_gals",
|
||||||
"SFI_gals_masked", "SFI_groups", "2MTF",
|
"SFI_gals_masked", "SFI_groups", "2MTF",
|
||||||
"Pantheon+"]:
|
"Pantheon+"]:
|
||||||
fpath = join(folder, "PV_compilation_Supranta2019.hdf5")
|
fpath = join(folder, "PV_compilation.hdf5")
|
||||||
with File(fpath, 'r') as f:
|
with File(fpath, 'r') as f:
|
||||||
grp = f[catalogue_name]
|
grp = f[catalogue_name]
|
||||||
RA = grp["RA"][:]
|
RA = grp["RA"][:]
|
||||||
|
@ -286,7 +286,7 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
rmax = 200
|
rmax = 200
|
||||||
dr = 0.5
|
dr = 0.5
|
||||||
smooth_scales = [0, 2]
|
smooth_scales = [0, 2, 4]
|
||||||
|
|
||||||
comm = MPI.COMM_WORLD
|
comm = MPI.COMM_WORLD
|
||||||
paths = csiborgtools.read.Paths(**csiborgtools.paths_glamdring)
|
paths = csiborgtools.read.Paths(**csiborgtools.paths_glamdring)
|
||||||
|
|
|
@ -52,8 +52,10 @@ def get_model(args, nsim_iterator, get_model_kwargs):
|
||||||
if args.catalogue == "A2":
|
if args.catalogue == "A2":
|
||||||
fpath = join(folder, "A2.h5")
|
fpath = join(folder, "A2.h5")
|
||||||
elif args.catalogue in ["LOSS", "Foundation", "Pantheon+", "SFI_gals",
|
elif args.catalogue in ["LOSS", "Foundation", "Pantheon+", "SFI_gals",
|
||||||
"2MTF", "SFI_groups", "SFI_gals_masked"]:
|
"2MTF", "SFI_groups", "SFI_gals_masked",
|
||||||
fpath = join(folder, "PV_compilation_Supranta2019.hdf5")
|
"Pantheon+_groups", "Pantheon+_groups_zSN",
|
||||||
|
"Pantheon+_zSN"]:
|
||||||
|
fpath = join(folder, "PV_compilation.hdf5")
|
||||||
elif "CB2_" in args.catalogue:
|
elif "CB2_" in args.catalogue:
|
||||||
kind = args.catalogue.split("_")[-1]
|
kind = args.catalogue.split("_")[-1]
|
||||||
fpath = join(folder, f"PV_mock_CB2_17417_{kind}.hdf5")
|
fpath = join(folder, f"PV_mock_CB2_17417_{kind}.hdf5")
|
||||||
|
|
|
@ -7,8 +7,8 @@ queue="berg"
|
||||||
env="/mnt/users/rstiskalek/csiborgtools/venv_csiborg/bin/python"
|
env="/mnt/users/rstiskalek/csiborgtools/venv_csiborg/bin/python"
|
||||||
file="flow_validation.py"
|
file="flow_validation.py"
|
||||||
|
|
||||||
catalogue="CB2_small"
|
catalogue="Pantheon+_groups"
|
||||||
simname="csiborg2_main"
|
simname="csiborg2_varysmall"
|
||||||
|
|
||||||
|
|
||||||
pythoncm="$env $file --catalogue $catalogue --simname $simname --ksmooth $ksmooth"
|
pythoncm="$env $file --catalogue $catalogue --simname $simname --ksmooth $ksmooth"
|
||||||
|
|
Loading…
Reference in a new issue