mirror of
https://github.com/Richard-Sti/csiborgtools_public.git
synced 2025-06-28 18:51:12 +00:00
Overlap fixing and more (#107)
* Update README * Update density field reader * Update name of SDSSxALFAFA * Fix quick bug * Add little fixes * Update README * Put back fit_init * Add paths to initial snapshots * Add export * Remove some choices * Edit README * Add Jens' comments * Organize imports * Rename snapshot * Add additional print statement * Add paths to initial snapshots * Add masses to the initial files * Add normalization * Edit README * Update README * Fix bug in CSiBORG1 so that does not read fof_00001 * Edit README * Edit README * Overwrite comments * Add paths to init lag * Fix Quijote path * Add lagpatch * Edit submits * Update README * Fix numpy int problem * Update README * Add a flag to keep the snapshots open when fitting * Add a flag to keep snapshots open * Comment out some path issue * Keep snapshots open * Access directly snasphot * Add lagpatch for CSiBORG2 * Add treatment of x-z coordinates flipping * Add radial velocity field loader * Update README * Add lagpatch to Quijote * Fix typo * Add setter * Fix typo * Update README * Add output halo cat as ASCII * Add import * Add halo plot * Update README * Add evaluating field at radial distanfe * Add field shell evaluation * Add enclosed mass computation * Add BORG2 import * Add BORG boxsize * Add BORG paths * Edit run * Add BORG2 overdensity field * Add bulk flow clauclation * Update README * Add new plots * Add nbs * Edit paper * Update plotting * Fix overlap paths to contain simname * Add normalization of positions * Add default paths to CSiBORG1 * Add overlap path simname * Fix little things * Add CSiBORG2 catalogue * Update README * Add import * Add TNG density field constructor * Add TNG density * Add draft of calculating BORG ACL * Fix bug * Add ACL of enclosed density * Add nmean acl * Add galaxy bias calculation * Add BORG acl notebook * Add enclosed mass calculation * Add TNG300-1 dir * Add TNG300 and BORG1 dir * Update nb
This commit is contained in:
parent
0984191dc8
commit
9e4b34f579
30 changed files with 10037 additions and 248 deletions
|
@ -17,7 +17,8 @@ from csiborgtools import clustering, field, halo, match, read, summary
|
|||
from .utils import (center_of_mass, delta2ncells, number_counts, # noqa
|
||||
periodic_distance, periodic_distance_two_points, # noqa
|
||||
binned_statistic, cosine_similarity, fprint, # noqa
|
||||
hms_to_degrees, dms_to_degrees, great_circle_distance) # noqa
|
||||
hms_to_degrees, dms_to_degrees, great_circle_distance, # noqa
|
||||
radec_to_cartesian) # noqa
|
||||
from .params import paths_glamdring, simname2boxsize # noqa
|
||||
|
||||
|
||||
|
@ -52,7 +53,9 @@ class SDSSxALFALFA:
|
|||
if fpath is None:
|
||||
fpath = "/mnt/extraspace/rstiskalek/catalogs/5asfullmatch.fits"
|
||||
sel_steps = self.steps if apply_selection else None
|
||||
return read.SDSS(fpath, h=1, sel_steps=sel_steps)
|
||||
survey = read.SDSS(fpath, h=1, sel_steps=sel_steps)
|
||||
survey.name = "SDSSxALFALFA"
|
||||
return survey
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
|
|
@ -17,5 +17,6 @@ from .density import (DensityField, PotentialField, TidalTensorField,
|
|||
overdensity_field) # noqa
|
||||
from .interp import (evaluate_cartesian, evaluate_sky, field2rsp, # noqa
|
||||
fill_outside, make_sky, observer_peculiar_velocity, # noqa
|
||||
nside2radec, smoothen_field) # noqa
|
||||
smoothen_field, field_at_distance) # noqa
|
||||
from .corr import bayesian_bootstrap_correlation # noqa
|
||||
from .utils import nside2radec # noqa
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
"""
|
||||
Tools for interpolating 3D fields at arbitrary positions.
|
||||
"""
|
||||
import healpy
|
||||
import MAS_library as MASL
|
||||
import numpy
|
||||
import smoothing_library as SL
|
||||
|
@ -23,7 +22,7 @@ from numba import jit
|
|||
from tqdm import tqdm, trange
|
||||
|
||||
from ..utils import periodic_wrap_grid, radec_to_cartesian
|
||||
from .utils import divide_nonzero, force_single_precision
|
||||
from .utils import divide_nonzero, force_single_precision, nside2radec
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
@ -219,18 +218,47 @@ def make_sky(field, angpos, dist, boxsize, verbose=True):
|
|||
return out
|
||||
|
||||
|
||||
def nside2radec(nside):
|
||||
"""
|
||||
Generate RA [0, 360] deg. and declination [-90, 90] deg. for HEALPix pixel
|
||||
centres at a given nside.
|
||||
"""
|
||||
pixs = numpy.arange(healpy.nside2npix(nside))
|
||||
theta, phi = healpy.pix2ang(nside, pixs)
|
||||
###############################################################################
|
||||
# Average field at a radial distance #
|
||||
###############################################################################
|
||||
|
||||
ra = 180 / numpy.pi * phi
|
||||
dec = 90 - 180 / numpy.pi * theta
|
||||
|
||||
return numpy.vstack([ra, dec]).T
|
||||
def field_at_distance(field, distance, boxsize, smooth_scales=None, nside=128,
|
||||
verbose=True):
|
||||
"""
|
||||
Evaluate a scalar field at uniformly spaced angular coordinates at a
|
||||
given distance from the observer
|
||||
|
||||
Parameters
|
||||
----------
|
||||
field : 3-dimensional array of shape `(grid, grid, grid)`
|
||||
Field to be interpolated.
|
||||
distance : float
|
||||
Distance from the observer in `Mpc / h`.
|
||||
boxsize : float
|
||||
Box size in `Mpc / h`.
|
||||
smooth_scales : (list of) float, optional
|
||||
Smoothing scales in `Mpc / h`. If `None`, no smoothing is performed.
|
||||
nside : int, optional
|
||||
HEALPix nside. Used to generate the uniformly spaced angular
|
||||
coordinates. Recommended to be >> 1.
|
||||
verbose : bool, optional
|
||||
Smoothing verbosity flag.
|
||||
|
||||
Returns
|
||||
-------
|
||||
vals : n-dimensional array of shape `(npix, len(smooth_scales))`
|
||||
"""
|
||||
# Get positions of HEALPix pixels on the sky and then convert those to
|
||||
# box Cartesian coordinates. We take HEALPix pixels because they are
|
||||
# uniformly distributed on the sky.
|
||||
angpos = nside2radec(nside)
|
||||
X = numpy.hstack([numpy.ones(len(angpos)).reshape(-1, 1) * distance,
|
||||
angpos])
|
||||
X = radec_to_cartesian(X) / boxsize + 0.5
|
||||
|
||||
return evaluate_cartesian(field, pos=X, smooth_scales=smooth_scales,
|
||||
verbose=verbose)
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
|
|
@ -18,6 +18,7 @@ imports.
|
|||
"""
|
||||
from numba import jit
|
||||
import numpy
|
||||
import healpy
|
||||
|
||||
|
||||
def force_single_precision(x):
|
||||
|
@ -42,3 +43,26 @@ def divide_nonzero(field0, field1):
|
|||
for k in range(kmax):
|
||||
if field1[i, j, k] != 0:
|
||||
field0[i, j, k] /= field1[i, j, k]
|
||||
|
||||
|
||||
def nside2radec(nside):
|
||||
"""
|
||||
Generate RA [0, 360] deg. and declination [-90, 90] deg for HEALPix pixel
|
||||
centres at a given nside.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nside : int
|
||||
HEALPix nside.
|
||||
|
||||
Returns
|
||||
-------
|
||||
angpos : 2-dimensional array of shape (npix, 2)
|
||||
"""
|
||||
pixs = numpy.arange(healpy.nside2npix(nside))
|
||||
theta, phi = healpy.pix2ang(nside, pixs)
|
||||
|
||||
ra = 180 / numpy.pi * phi
|
||||
dec = 90 - 180 / numpy.pi * theta
|
||||
|
||||
return numpy.vstack([ra, dec]).T
|
||||
|
|
|
@ -202,10 +202,9 @@ class RealisationsMatcher(BaseMatcher):
|
|||
# in the reference simulation from the cross simulation in the initial
|
||||
# snapshot.
|
||||
match_indxs = radius_neighbours(
|
||||
catx.knn(in_initial=True, subtract_observer=False, periodic=True),
|
||||
cat0["lagpatch_coordinates"], radiusX=cat0["lagpatch_radius"],
|
||||
radiusKNN=catx["lagpatch_radius"], nmult=self.nmult,
|
||||
enforce_int32=True, verbose=verbose)
|
||||
catx.knn(in_initial=True), cat0["lagpatch_coordinates"],
|
||||
radiusX=cat0["lagpatch_radius"], radiusKNN=catx["lagpatch_radius"],
|
||||
nmult=self.nmult, enforce_int32=True, verbose=verbose)
|
||||
|
||||
# We next remove neighbours whose mass is too large/small.
|
||||
if self.dlogmass is not None:
|
||||
|
@ -367,6 +366,7 @@ class ParticleOverlap(BaseMatcher):
|
|||
cellmin = self.box_size // 2 - self.bckg_halfsize
|
||||
cellmax = self.box_size // 2 + self.bckg_halfsize
|
||||
ncells = cellmax - cellmin
|
||||
boxsize_mpc = cat.boxsize
|
||||
# We then pre-allocate the density field/check it is of the right shape
|
||||
if delta is None:
|
||||
delta = numpy.zeros((ncells,) * 3, dtype=numpy.float32)
|
||||
|
@ -382,6 +382,7 @@ class ParticleOverlap(BaseMatcher):
|
|||
for hid in iterator:
|
||||
try:
|
||||
pos = cat.snapshot.halo_coordinates(hid, is_group=True)
|
||||
pos /= boxsize_mpc
|
||||
except ValueError as e:
|
||||
# If not particles found for this halo, just skip it.
|
||||
if str(e).startswith("Halo "):
|
||||
|
@ -852,6 +853,8 @@ def load_processed_halo(hid, cat, ncells, nshift):
|
|||
pos = cat.snapshot.halo_coordinates(hid, is_group=True)
|
||||
mass = cat.snapshot.halo_masses(hid, is_group=True)
|
||||
|
||||
pos /= cat.boxsize
|
||||
|
||||
pos = pos2cell(pos, ncells)
|
||||
mins, maxs = get_halo_cell_limits(pos, ncells=ncells, nshift=nshift)
|
||||
return pos, mass, numpy.sum(mass), mins, maxs
|
||||
|
|
|
@ -34,6 +34,8 @@ def simname2boxsize(simname):
|
|||
"csiborg2_main": 676.6,
|
||||
"csiborg2_varysmall": 676.6,
|
||||
"csiborg2_random": 676.6,
|
||||
"borg1": 677.7,
|
||||
"borg2": 676.6,
|
||||
"quijote": 1000.
|
||||
}
|
||||
|
||||
|
@ -52,6 +54,8 @@ paths_glamdring = {
|
|||
"csiborg2_random_srcdir": "/mnt/extraspace/rstiskalek/csiborg2_random", # noqa
|
||||
"postdir": "/mnt/extraspace/rstiskalek/csiborg_postprocessing/",
|
||||
"quijote_dir": "/mnt/extraspace/rstiskalek/quijote",
|
||||
"borg2_dir": "/mnt/extraspace/rstiskalek/BORG_STOPYRA_2023",
|
||||
"tng300_1_dir": "/mnt/extraspace/rstiskalek/TNG300-1/",
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
from .catalogue import (CSiBORG1Catalogue, CSiBORG2Catalogue, # noqa
|
||||
CSiBORG2MergerTreeReader, QuijoteCatalogue) # noqa
|
||||
from .snapshot import (CSIBORG1Snapshot, CSIBORG2Snapshot, QuijoteSnapshot, # noqa
|
||||
CSiBORG1Field, CSiBORG2Field, QuijoteField) # noqa
|
||||
from .snapshot import (CSiBORG1Snapshot, CSiBORG2Snapshot, QuijoteSnapshot, # noqa
|
||||
CSiBORG1Field, CSiBORG2Field, QuijoteField, BORG2Field, # noqa
|
||||
BORG1Field) # noqa
|
||||
from .obs import (SDSS, MCXCClusters, PlanckClusters, TwoMPPGalaxies, # noqa
|
||||
TwoMPPGroups, ObservedCluster, match_array_to_no_masking) # noqa
|
||||
TwoMPPGroups, ObservedCluster, match_array_to_no_masking, # noqa
|
||||
cols_to_structured) # noqa
|
||||
from .paths import Paths # noqa
|
||||
|
|
|
@ -69,6 +69,7 @@ class BaseCatalogue(ABC):
|
|||
|
||||
self._observer_location = None
|
||||
self._observer_velocity = None
|
||||
self._flip_xz = False
|
||||
self._boxsize = None
|
||||
|
||||
self._cache = OrderedDict()
|
||||
|
@ -81,7 +82,7 @@ class BaseCatalogue(ABC):
|
|||
|
||||
def init_with_snapshot(self, simname, nsim, nsnap, paths, snapshot,
|
||||
bounds, boxsize, observer_location,
|
||||
observer_velocity, cache_maxsize=64):
|
||||
observer_velocity, flip_xz, cache_maxsize=64):
|
||||
self.simname = simname
|
||||
self.nsim = nsim
|
||||
self.nsnap = nsnap
|
||||
|
@ -89,6 +90,7 @@ class BaseCatalogue(ABC):
|
|||
self.boxsize = boxsize
|
||||
self.observer_location = observer_location
|
||||
self.observer_velocity = observer_velocity
|
||||
self.flip_xz = flip_xz
|
||||
|
||||
self.cache_maxsize = cache_maxsize
|
||||
|
||||
|
@ -211,6 +213,24 @@ class BaseCatalogue(ABC):
|
|||
raise TypeError("`boxsize` must be an integer or float.")
|
||||
self._boxsize = float(boxsize)
|
||||
|
||||
@property
|
||||
def flip_xz(self):
|
||||
"""
|
||||
Whether to flip the x- and z-coordinates to undo the MUSIC bug to match
|
||||
observations.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
"""
|
||||
return self._flip_xz
|
||||
|
||||
@flip_xz.setter
|
||||
def flip_xz(self, flip_xz):
|
||||
if not isinstance(flip_xz, bool):
|
||||
raise TypeError("`flip_xz` must be a boolean.")
|
||||
self._flip_xz = flip_xz
|
||||
|
||||
@property
|
||||
def cache_maxsize(self):
|
||||
"""
|
||||
|
@ -592,6 +612,10 @@ class BaseCatalogue(ABC):
|
|||
elif key == "redshift_dist":
|
||||
out = self["__cartesian_redshift_pos"]
|
||||
out = numpy.linalg.norm(out - self.observer_location, axis=1)
|
||||
elif key == "lagpatch_radius":
|
||||
out = self.lagpatch_radius
|
||||
elif key == "lagpatch_coordinates":
|
||||
out = self.lagpatch_coordinates
|
||||
elif key == "npart":
|
||||
out = self.npart
|
||||
elif key == "totmass":
|
||||
|
@ -650,16 +674,23 @@ class CSiBORG1Catalogue(BaseCatalogue):
|
|||
a boolean.
|
||||
observer_velocity : 1-dimensional array, optional
|
||||
Observer's velocity in :math:`\mathrm{km} / \mathrm{s}`.
|
||||
flip_xz : bool, optional
|
||||
Whether to flip the x- and z-coordinates to undo the MUSIC bug to match
|
||||
observations.
|
||||
cache_maxsize : int, optional
|
||||
Maximum number of cached arrays.
|
||||
"""
|
||||
def __init__(self, nsim, paths=None, snapshot=None, bounds=None,
|
||||
observer_velocity=None, cache_maxsize=64):
|
||||
observer_velocity=None, flip_xz=True, cache_maxsize=64):
|
||||
super().__init__()
|
||||
|
||||
if paths is None:
|
||||
paths = Paths(**paths_glamdring)
|
||||
|
||||
super().init_with_snapshot(
|
||||
"csiborg1", nsim, max(paths.get_snapshots(nsim, "csiborg1")),
|
||||
paths, snapshot, bounds, 677.7, [338.85, 338.85, 338.85],
|
||||
observer_velocity, cache_maxsize)
|
||||
observer_velocity, flip_xz, cache_maxsize)
|
||||
|
||||
self._custom_keys = []
|
||||
|
||||
|
@ -675,9 +706,12 @@ class CSiBORG1Catalogue(BaseCatalogue):
|
|||
|
||||
@property
|
||||
def coordinates(self):
|
||||
# NOTE: We flip x and z to undo MUSIC bug.
|
||||
z, y, x = [self._read_fof_catalogue(key) for key in ["x", "y", "z"]]
|
||||
return numpy.vstack([x, y, z]).T
|
||||
x, y, z = [self._read_fof_catalogue(key) for key in ["x", "y", "z"]]
|
||||
|
||||
if self.flip_xz:
|
||||
return numpy.vstack([z, y, x]).T
|
||||
else:
|
||||
return numpy.vstack([x, y, z]).T
|
||||
|
||||
@property
|
||||
def velocities(self):
|
||||
|
@ -698,11 +732,18 @@ class CSiBORG1Catalogue(BaseCatalogue):
|
|||
|
||||
@property
|
||||
def lagpatch_coordinates(self):
|
||||
raise RuntimeError("Lagrangian patch coordinates are not available.")
|
||||
fpath = self.paths.initial_lagpatch(self.nsim, self.simname)
|
||||
data = numpy.load(fpath)
|
||||
|
||||
if self.flip_xz:
|
||||
return numpy.vstack([data["z"], data["y"], data["x"]]).T
|
||||
else:
|
||||
return numpy.vstack([data["x"], data["y"], data["z"]]).T
|
||||
|
||||
@property
|
||||
def lagpatch_radius(self):
|
||||
raise RuntimeError("Lagrangian patch radius is not available.")
|
||||
fpath = self.paths.initial_lagpatch(self.nsim, self.simname)
|
||||
return numpy.load(fpath)["lagpatch_size"]
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
@ -730,15 +771,20 @@ class CSiBORG2Catalogue(BaseCatalogue):
|
|||
a boolean.
|
||||
observer_velocity : 1-dimensional array, optional
|
||||
Observer's velocity in :math:`\mathrm{km} / \mathrm{s}`.
|
||||
flip_xz : bool, optional
|
||||
Whether to flip the x- and z-coordinates to undo the MUSIC bug to match
|
||||
observations.
|
||||
cache_maxsize : int, optional
|
||||
Maximum number of cached arrays.
|
||||
"""
|
||||
def __init__(self, nsim, nsnap, kind, paths=None, snapshot=None,
|
||||
bounds=None, observer_velocity=None, cache_maxsize=64):
|
||||
bounds=None, observer_velocity=None, flip_xz=True,
|
||||
cache_maxsize=64):
|
||||
super().__init__()
|
||||
super().init_with_snapshot(
|
||||
f"csiborg2_{kind}", nsim, nsnap, paths, snapshot, bounds,
|
||||
676.6, [338.3, 338.3, 338.3], observer_velocity, cache_maxsize)
|
||||
676.6, [338.3, 338.3, 338.3], observer_velocity, flip_xz,
|
||||
cache_maxsize)
|
||||
|
||||
self._custom_keys = ["GroupFirstSub", "GroupContamination",
|
||||
"GroupNsubs", "Group_M_Crit200"]
|
||||
|
@ -767,16 +813,16 @@ class CSiBORG2Catalogue(BaseCatalogue):
|
|||
|
||||
@property
|
||||
def coordinates(self):
|
||||
# Loading directly the Gadget4 output, flip x and z to undo MUSIC bug.
|
||||
out = self._read_fof_catalogue("GroupPos")
|
||||
out[:, [0, 2]] = out[:, [2, 0]]
|
||||
if self.flip_xz:
|
||||
out[:, [0, 2]] = out[:, [2, 0]]
|
||||
return out
|
||||
|
||||
@property
|
||||
def velocities(self):
|
||||
# Loading directly the Gadget4 output, flip x and z to undo MUSIC bug.
|
||||
out = self._read_fof_catalogue("GroupVel")
|
||||
out[:, [0, 2]] = out[:, [2, 0]]
|
||||
if self.flip_xz:
|
||||
out[:, [0, 2]] = out[:, [2, 0]]
|
||||
return out
|
||||
|
||||
@property
|
||||
|
@ -795,11 +841,28 @@ class CSiBORG2Catalogue(BaseCatalogue):
|
|||
|
||||
@property
|
||||
def lagpatch_coordinates(self):
|
||||
raise RuntimeError("Lagrangian patch coordinates are not available.")
|
||||
if self.nsnap != 99:
|
||||
raise RuntimeError("Lagrangian patch information is only "
|
||||
"available for haloes defined at the final "
|
||||
f"snapshot (indexed 99). Chosen {self.nsnap}.")
|
||||
|
||||
fpath = self.paths.initial_lagpatch(self.nsim, self.simname)
|
||||
data = numpy.load(fpath)
|
||||
|
||||
if self.flip_xz:
|
||||
return numpy.vstack([data["z"], data["y"], data["x"]]).T
|
||||
else:
|
||||
return numpy.vstack([data["x"], data["y"], data["z"]]).T
|
||||
|
||||
@property
|
||||
def lagpatch_radius(self):
|
||||
raise RuntimeError("Lagrangian patch radius is not available.")
|
||||
if self.nsnap != 99:
|
||||
raise RuntimeError("Lagrangian patch information is only "
|
||||
"available for haloes defined at the final "
|
||||
f"snapshot (indexed 99). Chosen {self.nsnap}.")
|
||||
|
||||
fpath = self.paths.initial_lagpatch(self.nsim, self.simname)
|
||||
return numpy.load(fpath)["lagpatch_size"]
|
||||
|
||||
@property
|
||||
def GroupFirstSub(self):
|
||||
|
@ -1086,12 +1149,11 @@ class QuijoteCatalogue(BaseCatalogue):
|
|||
Maximum number of cached arrays.
|
||||
"""
|
||||
def __init__(self, nsim, paths=None, snapshot=None, bounds=None,
|
||||
observer_velocity=None,
|
||||
cache_maxsize=64):
|
||||
observer_velocity=None, cache_maxsize=64):
|
||||
super().__init__()
|
||||
super().init_with_snapshot(
|
||||
"quijote", nsim, 4, paths, snapshot, bounds, 1000,
|
||||
[500., 500., 500.,], observer_velocity, cache_maxsize)
|
||||
[500., 500., 500.,], observer_velocity, False, cache_maxsize)
|
||||
|
||||
self._custom_keys = []
|
||||
self._bounds = bounds
|
||||
|
@ -1131,11 +1193,14 @@ class QuijoteCatalogue(BaseCatalogue):
|
|||
|
||||
@property
|
||||
def lagpatch_coordinates(self):
|
||||
raise RuntimeError("Lagrangian patch coordinates are not available.")
|
||||
fpath = self.paths.initial_lagpatch(self.nsim, self.simname)
|
||||
data = numpy.load(fpath)
|
||||
return numpy.vstack([data["x"], data["y"], data["z"]]).T
|
||||
|
||||
@property
|
||||
def lagpatch_radius(self):
|
||||
raise RuntimeError("Lagrangian patch radius is not available.")
|
||||
fpath = self.paths.initial_lagpatch(self.nsim, self.simname)
|
||||
return numpy.load(fpath)["lagpatch_size"]
|
||||
|
||||
def pick_fiducial_observer(self, n, rmax):
|
||||
r"""
|
||||
|
|
|
@ -53,6 +53,12 @@ class Paths:
|
|||
Path to the CSiBORG post-processing directory.
|
||||
quijote_dir : str
|
||||
Path to the Quijote simulation directory.
|
||||
borg1_dir : str
|
||||
Path to the BORG1 simulation directory.
|
||||
borg2_dir : str
|
||||
Path to the BORG2 simulation directory.
|
||||
tng300_1_dir : str
|
||||
Path to the TNG300-1 simulation directory.
|
||||
"""
|
||||
def __init__(self,
|
||||
csiborg1_srcdir,
|
||||
|
@ -61,13 +67,18 @@ class Paths:
|
|||
csiborg2_varysmall_srcdir,
|
||||
postdir,
|
||||
quijote_dir,
|
||||
borg1_dir,
|
||||
borg2_dir,
|
||||
tng300_1_dir
|
||||
):
|
||||
self.csiborg1_srcdir = csiborg1_srcdir
|
||||
self.csiborg2_main_srcdir = csiborg2_main_srcdir
|
||||
self.csiborg2_random_srcdir = csiborg2_random_srcdir
|
||||
self.csiborg2_varysmall_srcdir = csiborg2_varysmall_srcdir
|
||||
self.quijote_dir = quijote_dir
|
||||
|
||||
self.borg1_dir = borg1_dir
|
||||
self.borg2_dir = borg2_dir
|
||||
self.tng300_1_dir = tng300_1_dir
|
||||
self.postdir = postdir
|
||||
|
||||
def get_ics(self, simname):
|
||||
|
@ -83,10 +94,10 @@ class Paths:
|
|||
-------
|
||||
ids : 1-dimensional array
|
||||
"""
|
||||
if simname == "csiborg1":
|
||||
if simname == "csiborg1" or simname == "borg1":
|
||||
files = glob(join(self.csiborg1_srcdir, "chain_*"))
|
||||
files = [int(search(r'chain_(\d+)', f).group(1)) for f in files]
|
||||
elif simname == "csiborg2_main":
|
||||
elif simname == "csiborg2_main" or simname == "borg2":
|
||||
files = glob(join(self.csiborg2_main_srcdir, "chain_*"))
|
||||
files = [int(search(r'chain_(\d+)', f).group(1)) for f in files]
|
||||
elif simname == "csiborg2_random":
|
||||
|
@ -175,25 +186,27 @@ class Paths:
|
|||
str
|
||||
"""
|
||||
if simname == "csiborg1":
|
||||
return join(self.csiborg1_srcdir, f"chain_{nsim}",
|
||||
f"snapshot_{str(nsnap).zfill(5)}.hdf5")
|
||||
fpath = join(self.csiborg1_srcdir, f"chain_{nsim}",
|
||||
f"snapshot_{str(nsnap).zfill(5)}.hdf5")
|
||||
elif simname == "csiborg2_main":
|
||||
return join(self.csiborg2_main_srcdir, f"chain_{nsim}", "output",
|
||||
f"snapshot_{str(nsnap).zfill(3)}.hdf5")
|
||||
fpath = join(self.csiborg2_main_srcdir, f"chain_{nsim}", "output",
|
||||
f"snapshot_{str(nsnap).zfill(3)}.hdf5")
|
||||
elif simname == "csiborg2_random":
|
||||
return join(self.csiborg2_random_srcdir, f"chain_{nsim}", "output",
|
||||
f"snapshot_{str(nsnap).zfill(3)}.hdf5")
|
||||
fpath = join(self.csiborg2_random_srcdir, f"chain_{nsim}",
|
||||
"output", f"snapshot_{str(nsnap).zfill(3)}.hdf5")
|
||||
elif simname == "csiborg2_varysmall":
|
||||
return join(self.csiborg2_varysmall_srcdir,
|
||||
f"chain_16417_{str(nsim).zfill(3)}", "output",
|
||||
f"snapshot_{str(nsnap).zfill(3)}.hdf5")
|
||||
fpath = join(self.csiborg2_varysmall_srcdir,
|
||||
f"chain_16417_{str(nsim).zfill(3)}", "output",
|
||||
f"snapshot_{str(nsnap).zfill(3)}.hdf5")
|
||||
elif simname == "quijote":
|
||||
return join(self.quijote_dir, "fiducial_processed",
|
||||
f"chain_{nsim}",
|
||||
f"snapshot_{str(nsnap).zfill(3)}.hdf5")
|
||||
fpath = join(self.quijote_dir, "fiducial_processed",
|
||||
f"chain_{nsim}",
|
||||
f"snapshot_{str(nsnap).zfill(3)}.hdf5")
|
||||
else:
|
||||
raise ValueError(f"Unknown simulation name `{simname}`.")
|
||||
|
||||
return fpath
|
||||
|
||||
def snapshot_catalogue(self, nsnap, nsim, simname):
|
||||
"""
|
||||
Path to the halo catalogue of a simulation snapshot.
|
||||
|
@ -218,7 +231,7 @@ class Paths:
|
|||
return join(self.csiborg2_main_srcdir, f"chain_{nsim}", "output",
|
||||
f"fof_subhalo_tab_{str(nsnap).zfill(3)}.hdf5")
|
||||
elif simname == "csiborg2_random":
|
||||
return join(self.csiborg2_ranodm_srcdir, f"chain_{nsim}", "output",
|
||||
return join(self.csiborg2_random_srcdir, f"chain_{nsim}", "output",
|
||||
f"fof_subhalo_tab_{str(nsnap).zfill(3)}.hdf5")
|
||||
elif simname == "csiborg2_varysmall":
|
||||
return join(self.csiborg2_varysmall_srcdir,
|
||||
|
@ -231,6 +244,40 @@ class Paths:
|
|||
else:
|
||||
raise ValueError(f"Unknown simulation name `{simname}`.")
|
||||
|
||||
def initial_lagpatch(self, nsim, simname):
|
||||
"""
|
||||
Path to the Lagrangain patch information of a simulation for halos
|
||||
defined at z = 0.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nsim : int
|
||||
IC realisation index.
|
||||
simname : str
|
||||
Simulation name.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
"""
|
||||
if simname == "csiborg1":
|
||||
return join(self.csiborg1_srcdir, f"chain_{nsim}",
|
||||
"initial_lagpatch.npy")
|
||||
elif simname == "csiborg2_main":
|
||||
return join(self.csiborg2_main_srcdir, "catalogues",
|
||||
f"initial_lagpatch_{nsim}.npy")
|
||||
elif simname == "csiborg2_random":
|
||||
return join(self.csiborg2_random_srcdir, "catalogues",
|
||||
f"initial_lagpatch_{nsim}.npy")
|
||||
elif simname == "csiborg2_varysmall":
|
||||
return join(self.csiborg2_varysmall_srcdir, "catalogues",
|
||||
f"initial_lagpatch_{nsim}.npy")
|
||||
elif simname == "quijote":
|
||||
return join(self.quijote_dir, "fiducial_processed",
|
||||
f"chain_{nsim}", "initial_lagpatch.npy")
|
||||
else:
|
||||
raise ValueError(f"Unknown simulation name `{simname}`.")
|
||||
|
||||
def trees(self, nsim, simname):
|
||||
"""
|
||||
Path to the halo trees of a simulation snapshot.
|
||||
|
@ -284,7 +331,7 @@ class Paths:
|
|||
-------
|
||||
str
|
||||
"""
|
||||
if simname == "csiborg":
|
||||
if "csiborg" in simname:
|
||||
fdir = join(self.postdir, "overlap")
|
||||
elif simname == "quijote":
|
||||
fdir = join(self.quijote_dir, "overlap")
|
||||
|
@ -297,7 +344,7 @@ class Paths:
|
|||
nsimx = str(nsimx).zfill(5)
|
||||
min_logmass = float('%.4g' % min_logmass)
|
||||
|
||||
fname = f"overlap_{nsim0}_{nsimx}_{min_logmass}.npz"
|
||||
fname = f"overlap_{simname}_{nsim0}_{nsimx}_{min_logmass}.npz"
|
||||
if smoothed:
|
||||
fname = fname.replace("overlap", "overlap_smoothed")
|
||||
return join(fdir, fname)
|
||||
|
@ -367,6 +414,13 @@ class Paths:
|
|||
-------
|
||||
str
|
||||
"""
|
||||
if simname == "borg2":
|
||||
return join(self.borg2_dir, f"mcmc_{nsim}.h5")
|
||||
|
||||
if simname == "borg1":
|
||||
#
|
||||
return f"/mnt/zfsusers/hdesmond/BORG_final/mcmc_{nsim}.h5"
|
||||
|
||||
if MAS == "SPH" and kind in ["density", "velocity"]:
|
||||
if simname == "csiborg1":
|
||||
raise ValueError("SPH field not available for CSiBORG1.")
|
||||
|
@ -581,3 +635,13 @@ class Paths:
|
|||
files = glob(join(fdir, f"{simname}_tpcf*"))
|
||||
run = "__" + run
|
||||
return [f for f in files if run in f]
|
||||
|
||||
def tng300_1(self):
|
||||
"""
|
||||
Path to the TNG300-1 simulation directory.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
"""
|
||||
return self.tng300_1_dir
|
||||
|
|
|
@ -18,6 +18,7 @@ should be implemented things such as flipping x- and z-axes, to make sure that
|
|||
observed RA-dec can be mapped into the simulation box.
|
||||
"""
|
||||
from abc import ABC, abstractmethod, abstractproperty
|
||||
from os.path import join
|
||||
|
||||
import numpy
|
||||
from h5py import File
|
||||
|
@ -35,14 +36,26 @@ class BaseSnapshot(ABC):
|
|||
"""
|
||||
Base class for reading snapshots.
|
||||
"""
|
||||
def __init__(self, nsim, nsnap, paths):
|
||||
if not isinstance(nsim, int):
|
||||
raise TypeError("`nsim` must be an integer")
|
||||
self._nsim = nsim
|
||||
def __init__(self, nsim, nsnap, paths, keep_snapshot_open=False,
|
||||
flip_xz=False):
|
||||
self._keep_snapshot_open = None
|
||||
|
||||
if not isinstance(nsnap, int):
|
||||
if not isinstance(nsim, (int, numpy.integer)):
|
||||
raise TypeError("`nsim` must be an integer")
|
||||
self._nsim = int(nsim)
|
||||
|
||||
if not isinstance(nsnap, (int, numpy.integer)):
|
||||
raise TypeError("`nsnap` must be an integer")
|
||||
self._nsnap = nsnap
|
||||
self._nsnap = int(nsnap)
|
||||
|
||||
if not isinstance(keep_snapshot_open, bool):
|
||||
raise TypeError("`keep_snapshot_open` must be a boolean.")
|
||||
self._keep_snapshot_open = keep_snapshot_open
|
||||
self._snapshot_file = None
|
||||
|
||||
if not isinstance(flip_xz, bool):
|
||||
raise TypeError("`flip_xz` must be a boolean.")
|
||||
self._flip_xz = flip_xz
|
||||
|
||||
self._paths = paths
|
||||
self._hid2offset = None
|
||||
|
@ -106,6 +119,30 @@ class BaseSnapshot(ABC):
|
|||
self._paths = Paths(**paths_glamdring)
|
||||
return self._paths
|
||||
|
||||
@property
|
||||
def keep_snapshot_open(self):
|
||||
"""
|
||||
Whether to keep the snapshot file open when reading halo particles.
|
||||
This is useful for repeated access to the snapshot.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
"""
|
||||
return self._keep_snapshot_open
|
||||
|
||||
@property
|
||||
def flip_xz(self):
|
||||
"""
|
||||
Whether to flip the x- and z-axes to undo the MUSIC bug so that the
|
||||
coordinates are consistent with observations.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
"""
|
||||
return self._flip_xz
|
||||
|
||||
@abstractproperty
|
||||
def coordinates(self):
|
||||
"""
|
||||
|
@ -221,6 +258,43 @@ class BaseSnapshot(ABC):
|
|||
"""
|
||||
pass
|
||||
|
||||
def open_snapshot(self):
|
||||
"""
|
||||
Open the snapshot file, particularly used in the context of loading in
|
||||
particles of individual haloes.
|
||||
|
||||
Returns
|
||||
-------
|
||||
h5py.File
|
||||
"""
|
||||
if not self.keep_snapshot_open:
|
||||
# Check if the snapshot path is set
|
||||
if not hasattr(self, "_snapshot_path"):
|
||||
raise RuntimeError("Snapshot path not set.")
|
||||
|
||||
return File(self._snapshot_path, "r")
|
||||
|
||||
# Here if we want to keep the snapshot open
|
||||
if self._snapshot_file is None:
|
||||
self._snapshot_file = File(self._snapshot_path, "r")
|
||||
|
||||
return self._snapshot_file
|
||||
|
||||
def close_snapshot(self):
|
||||
"""
|
||||
Close the snapshot file opened with `open_snapshot`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
if not self.keep_snapshot_open:
|
||||
return
|
||||
|
||||
if self._snapshot_file is not None:
|
||||
self._snapshot_file.close()
|
||||
self._snapshot_file = None
|
||||
|
||||
def select_box(self, center, boxwidth):
|
||||
"""
|
||||
Find particle coordinates of particles within a box of size `boxwidth`
|
||||
|
@ -248,10 +322,11 @@ class BaseSnapshot(ABC):
|
|||
###############################################################################
|
||||
|
||||
|
||||
class CSIBORG1Snapshot(BaseSnapshot):
|
||||
class CSiBORG1Snapshot(BaseSnapshot):
|
||||
"""
|
||||
CSiBORG1 snapshot class with the FoF halo finder particle assignment.
|
||||
CSiBORG1 was run with RAMSES.
|
||||
CSiBORG1 was run with RAMSES. Note that the haloes are defined at z = 0 and
|
||||
index from 1.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
@ -261,9 +336,16 @@ class CSIBORG1Snapshot(BaseSnapshot):
|
|||
Snapshot index.
|
||||
paths : Paths, optional
|
||||
Paths object.
|
||||
keep_snapshot_open : bool, optional
|
||||
Whether to keep the snapshot file open when reading halo particles.
|
||||
This is useful for repeated access to the snapshot.
|
||||
flip_xz : bool, optional
|
||||
Whether to flip the x- and z-axes to undo the MUSIC bug so that the
|
||||
coordinates are consistent with observations.
|
||||
"""
|
||||
def __init__(self, nsim, nsnap, paths=None):
|
||||
super().__init__(nsim, nsnap, paths)
|
||||
def __init__(self, nsim, nsnap, paths=None, keep_snapshot_open=False,
|
||||
flip_xz=False):
|
||||
super().__init__(nsim, nsnap, paths, keep_snapshot_open, flip_xz)
|
||||
self._snapshot_path = self.paths.snapshot(
|
||||
self.nsnap, self.nsim, "csiborg1")
|
||||
self._simname = "csiborg1"
|
||||
|
@ -272,6 +354,9 @@ class CSIBORG1Snapshot(BaseSnapshot):
|
|||
with File(self._snapshot_path, "r") as f:
|
||||
x = f[kind][...]
|
||||
|
||||
if self.flip_xz and kind in ["Coordinates", "Velocities"]:
|
||||
x[:, [0, 2]] = x[:, [2, 0]]
|
||||
|
||||
return x
|
||||
|
||||
def coordinates(self):
|
||||
|
@ -293,13 +378,18 @@ class CSIBORG1Snapshot(BaseSnapshot):
|
|||
if not is_group:
|
||||
raise ValueError("There is no subhalo catalogue for CSiBORG1.")
|
||||
|
||||
with File(self._snapshot_path, "r") as f:
|
||||
i, j = self.hid2offset.get(halo_id, (None, None))
|
||||
f = self.open_snapshot()
|
||||
i, j = self.hid2offset.get(halo_id, (None, None))
|
||||
if i is None:
|
||||
raise ValueError(f"Halo `{halo_id}` not found.")
|
||||
|
||||
if i is None:
|
||||
raise ValueError(f"Halo `{halo_id}` not found.")
|
||||
x = f[kind][i:j + 1]
|
||||
|
||||
x = f[kind][i:j + 1]
|
||||
if not self.keep_snapshot_open:
|
||||
self.close_snapshot()
|
||||
|
||||
if self.flip_xz and kind in ["Coordinates", "Velocities"]:
|
||||
x[:, [0, 2]] = x[:, [2, 0]]
|
||||
|
||||
return x
|
||||
|
||||
|
@ -313,8 +403,9 @@ class CSIBORG1Snapshot(BaseSnapshot):
|
|||
return self._get_halo_particles(halo_id, "Masses", is_group)
|
||||
|
||||
def _make_hid2offset(self):
|
||||
nsnap = max(self.paths.get_snapshots(self.nsim, "csiborg1"))
|
||||
catalogue_path = self.paths.snapshot_catalogue(
|
||||
self.nsnap, self.nsim, "csiborg1")
|
||||
nsnap, self.nsim, "csiborg1")
|
||||
|
||||
with File(catalogue_path, "r") as f:
|
||||
offset = f["GroupOffset"][:]
|
||||
|
@ -326,7 +417,7 @@ class CSIBORG1Snapshot(BaseSnapshot):
|
|||
# CSiBORG2 snapshot class #
|
||||
###############################################################################
|
||||
|
||||
class CSIBORG2Snapshot(BaseSnapshot):
|
||||
class CSiBORG2Snapshot(BaseSnapshot):
|
||||
"""
|
||||
CSiBORG2 snapshot class with the FoF halo finder particle assignment and
|
||||
SUBFIND subhalo finder. The simulations were run with Gadget4.
|
||||
|
@ -341,9 +432,16 @@ class CSIBORG2Snapshot(BaseSnapshot):
|
|||
CSiBORG2 run kind. One of `main`, `random`, or `varysmall`.
|
||||
paths : Paths, optional
|
||||
Paths object.
|
||||
keep_snapshot_open : bool, optional
|
||||
Whether to keep the snapshot file open when reading halo particles.
|
||||
This is useful for repeated access to the snapshot.
|
||||
flip_xz : bool, optional
|
||||
Whether to flip the x- and z-axes to undo the MUSIC bug so that the
|
||||
coordinates are consistent with observations.
|
||||
"""
|
||||
def __init__(self, nsim, nsnap, kind, paths=None):
|
||||
super().__init__(nsim, nsnap, paths)
|
||||
def __init__(self, nsim, nsnap, kind, paths=None,
|
||||
keep_snapshot_open=False, flip_xz=False):
|
||||
super().__init__(nsim, nsnap, paths, keep_snapshot_open, flip_xz)
|
||||
self.kind = kind
|
||||
|
||||
fpath = self.paths.snapshot(self.nsnap, self.nsim,
|
||||
|
@ -390,6 +488,9 @@ class CSIBORG2Snapshot(BaseSnapshot):
|
|||
else:
|
||||
x = numpy.vstack([x, f[f"PartType5/{kind}"][...]])
|
||||
|
||||
if self.flip_xz and kind in ["Coordinates", "Velocities"]:
|
||||
x[:, [0, 2]] = x[:, [2, 0]]
|
||||
|
||||
return x
|
||||
|
||||
def coordinates(self):
|
||||
|
@ -408,26 +509,39 @@ class CSIBORG2Snapshot(BaseSnapshot):
|
|||
if not is_group:
|
||||
raise RuntimeError("While the CSiBORG2 subhalo catalogue exists, it is not currently implemented.") # noqa
|
||||
|
||||
with File(self._snapshot_path, "r") as f:
|
||||
i1, j1 = self.hid2offset["type1"].get(halo_id, (None, None))
|
||||
i5, j5 = self.hid2offset["type5"].get(halo_id, (None, None))
|
||||
f = self.open_snapshot()
|
||||
i1, j1 = self.hid2offset["type1"].get(halo_id, (None, None))
|
||||
i5, j5 = self.hid2offset["type5"].get(halo_id, (None, None))
|
||||
|
||||
# Check if this is a valid halo
|
||||
if i1 is None and i5 is None:
|
||||
raise ValueError(f"Halo `{halo_id}` not found.")
|
||||
if j1 - i1 == 0 and j5 - i5 == 0:
|
||||
raise ValueError(f"Halo `{halo_id}` has no particles.")
|
||||
# Check if this is a valid halo
|
||||
if i1 is None and i5 is None:
|
||||
raise ValueError(f"Halo `{halo_id}` not found.")
|
||||
if j1 - i1 == 0 and j5 - i5 == 0:
|
||||
raise ValueError(f"Halo `{halo_id}` has no particles.")
|
||||
|
||||
if i1 is not None and j1 - i1 > 0:
|
||||
if kind == "Masses":
|
||||
x1 = numpy.ones(j1 - i1, dtype=numpy.float32)
|
||||
x1 *= f["Header"].attrs["MassTable"][1]
|
||||
else:
|
||||
x1 = f[f"PartType1/{kind}"][i1:j1]
|
||||
if i1 is not None and j1 - i1 > 0:
|
||||
if kind == "Masses":
|
||||
x1 = numpy.ones(j1 - i1, dtype=numpy.float32)
|
||||
x1 *= f["Header"].attrs["MassTable"][1]
|
||||
else:
|
||||
x1 = f[f"PartType1/{kind}"][i1:j1]
|
||||
|
||||
if i5 is not None and j5 - i5 > 0:
|
||||
x5 = f[f"PartType5/{kind}"][i5:j5]
|
||||
# Flipping of x- and z-axes
|
||||
if self.flip_xz:
|
||||
x1[:, [0, 2]] = x1[:, [2, 0]]
|
||||
|
||||
if i5 is not None and j5 - i5 > 0:
|
||||
x5 = f[f"PartType5/{kind}"][i5:j5]
|
||||
|
||||
# Flipping of x- and z-axes
|
||||
if self.flip_xz and kind in ["Coordinates", "Velocities"]:
|
||||
x5[:, [0, 2]] = x5[:, [2, 0]]
|
||||
|
||||
# Close the snapshot file if we don't want to keep it open
|
||||
if not self.keep_snapshot_open:
|
||||
self.close_snapshot()
|
||||
|
||||
# Are we stacking high-resolution and low-resolution particles?
|
||||
if i5 is None or j5 - i5 == 0:
|
||||
return x1
|
||||
|
||||
|
@ -475,7 +589,7 @@ class CSIBORG2Snapshot(BaseSnapshot):
|
|||
###############################################################################
|
||||
|
||||
|
||||
class QuijoteSnapshot(CSIBORG1Snapshot):
|
||||
class QuijoteSnapshot(CSiBORG1Snapshot):
|
||||
"""
|
||||
Quijote snapshot class with the FoF halo finder particle assignment.
|
||||
Because of similarities with how the snapshot is processed with CSiBORG1,
|
||||
|
@ -489,9 +603,12 @@ class QuijoteSnapshot(CSIBORG1Snapshot):
|
|||
Snapshot index.
|
||||
paths : Paths, optional
|
||||
Paths object.
|
||||
keep_snapshot_open : bool, optional
|
||||
Whether to keep the snapshot file open when reading halo particles.
|
||||
This is useful for repeated access to the snapshot.
|
||||
"""
|
||||
def __init__(self, nsim, nsnap, paths=None):
|
||||
super().__init__(nsim, nsnap, paths)
|
||||
def __init__(self, nsim, nsnap, paths=None, keep_snapshot_open=False):
|
||||
super().__init__(nsim, nsnap, paths, keep_snapshot_open, flip_xz=False)
|
||||
self._snapshot_path = self.paths.snapshot(self.nsnap, self.nsim,
|
||||
"quijote")
|
||||
self._simname = "quijote"
|
||||
|
@ -515,13 +632,17 @@ class BaseField(ABC):
|
|||
"""
|
||||
Base class for reading fields such as density or velocity fields.
|
||||
"""
|
||||
def __init__(self, nsim, paths):
|
||||
def __init__(self, nsim, paths, flip_xz=False):
|
||||
if isinstance(nsim, numpy.integer):
|
||||
nsim = int(nsim)
|
||||
if not isinstance(nsim, int):
|
||||
raise TypeError(f"`nsim` must be an integer. Received `{type(nsim)}`.") # noqa
|
||||
|
||||
self._nsim = nsim
|
||||
|
||||
if not isinstance(flip_xz, bool):
|
||||
raise TypeError("`flip_xz` must be a boolean.")
|
||||
self._flip_xz = flip_xz
|
||||
|
||||
self._paths = paths
|
||||
|
||||
@property
|
||||
|
@ -548,6 +669,18 @@ class BaseField(ABC):
|
|||
self._paths = Paths(**paths_glamdring)
|
||||
return self._paths
|
||||
|
||||
@property
|
||||
def flip_xz(self):
|
||||
"""
|
||||
Whether to flip the x- and z-axes to undo the MUSIC bug so that the
|
||||
coordinates are consistent with observations.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
"""
|
||||
return self._flip_xz
|
||||
|
||||
@abstractmethod
|
||||
def density_field(self, MAS, grid):
|
||||
"""
|
||||
|
@ -584,6 +717,24 @@ class BaseField(ABC):
|
|||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def radial_velocity_field(self, MAS, grid):
|
||||
"""
|
||||
Return the pre-computed radial velocity field.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
MAS : str
|
||||
Mass assignment scheme.
|
||||
grid : int
|
||||
Grid size.
|
||||
|
||||
Returns
|
||||
-------
|
||||
field : 3-dimensional array
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
###############################################################################
|
||||
# CSiBORG1 field class #
|
||||
|
@ -600,9 +751,12 @@ class CSiBORG1Field(BaseField):
|
|||
Simulation index.
|
||||
paths : Paths, optional
|
||||
Paths object. By default, the paths are set to the `glamdring` paths.
|
||||
flip_xz : bool, optional
|
||||
Whether to flip the x- and z-axes to undo the MUSIC bug so that the
|
||||
coordinates are consistent with observations.
|
||||
"""
|
||||
def __init__(self, nsim, paths=None):
|
||||
super().__init__(nsim, paths)
|
||||
def __init__(self, nsim, paths=None, flip_xz=True):
|
||||
super().__init__(nsim, paths, flip_xz)
|
||||
self._simname = "csiborg1"
|
||||
|
||||
def density_field(self, MAS, grid):
|
||||
|
@ -615,8 +769,7 @@ class CSiBORG1Field(BaseField):
|
|||
else:
|
||||
field = numpy.load(fpath)
|
||||
|
||||
# Flip x- and z-axes
|
||||
if self._simname == "csiborg1":
|
||||
if self.flip_xz:
|
||||
field = field.T
|
||||
|
||||
return field
|
||||
|
@ -634,8 +787,7 @@ class CSiBORG1Field(BaseField):
|
|||
else:
|
||||
field = numpy.load(fpath)
|
||||
|
||||
# Flip x- and z-axes
|
||||
if self._simname == "csiborg1":
|
||||
if self.flip_xz:
|
||||
field[0, ...] = field[0, ...].T
|
||||
field[1, ...] = field[1, ...].T
|
||||
field[2, ...] = field[2, ...].T
|
||||
|
@ -643,6 +795,14 @@ class CSiBORG1Field(BaseField):
|
|||
|
||||
return field
|
||||
|
||||
def radial_velocity_field(self, MAS, grid):
|
||||
if not self.flip_xz and self._simname == "csiborg1":
|
||||
raise ValueError("The radial velocity field is only implemented "
|
||||
"for the flipped x- and z-axes.")
|
||||
|
||||
fpath = self.paths.field("radvel", MAS, grid, self.nsim, "csiborg1")
|
||||
return numpy.load(fpath)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# CSiBORG2 field class #
|
||||
|
@ -661,10 +821,12 @@ class CSiBORG2Field(BaseField):
|
|||
CSiBORG2 run kind. One of `main`, `random`, or `varysmall`.
|
||||
paths : Paths, optional
|
||||
Paths object. By default, the paths are set to the `glamdring` paths.
|
||||
flip_xz : bool, optional
|
||||
Whether to flip the x- and z-axes to undo the MUSIC bug so that the
|
||||
coordinates are consistent with observations.
|
||||
"""
|
||||
|
||||
def __init__(self, nsim, kind, paths=None):
|
||||
super().__init__(nsim, paths)
|
||||
def __init__(self, nsim, kind, paths=None, flip_xz=True):
|
||||
super().__init__(nsim, paths, flip_xz)
|
||||
self.kind = kind
|
||||
|
||||
@property
|
||||
|
@ -696,7 +858,9 @@ class CSiBORG2Field(BaseField):
|
|||
else:
|
||||
field = numpy.load(fpath)
|
||||
|
||||
field = field.T # Flip x- and z-axes
|
||||
if self.flip_xz:
|
||||
field = field.T
|
||||
|
||||
return field
|
||||
|
||||
def velocity_field(self, MAS, grid):
|
||||
|
@ -713,14 +877,142 @@ class CSiBORG2Field(BaseField):
|
|||
else:
|
||||
field = numpy.load(fpath)
|
||||
|
||||
# Flip x- and z-axes
|
||||
field[0, ...] = field[0, ...].T
|
||||
field[1, ...] = field[1, ...].T
|
||||
field[2, ...] = field[2, ...].T
|
||||
field[[0, 2], ...] = field[[2, 0], ...]
|
||||
if self.flip_xz:
|
||||
field[0, ...] = field[0, ...].T
|
||||
field[1, ...] = field[1, ...].T
|
||||
field[2, ...] = field[2, ...].T
|
||||
field[[0, 2], ...] = field[[2, 0], ...]
|
||||
|
||||
return field
|
||||
|
||||
def radial_velocity_field(self, MAS, grid):
|
||||
if not self.flip_xz:
|
||||
raise ValueError("The radial velocity field is only implemented "
|
||||
"for the flipped x- and z-axes.")
|
||||
|
||||
fpath = self.paths.field("radvel", MAS, grid, self.nsim,
|
||||
f"csiborg2_{self.kind}")
|
||||
return numpy.load(fpath)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# BORG1 field class #
|
||||
###############################################################################
|
||||
|
||||
|
||||
class BORG1Field(BaseField):
|
||||
"""
|
||||
BORG2 `z = 0` field class.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nsim : int
|
||||
Simulation index.
|
||||
paths : Paths, optional
|
||||
Paths object. By default, the paths are set to the `glamdring` paths.
|
||||
"""
|
||||
def __init__(self, nsim, paths=None):
|
||||
super().__init__(nsim, paths, False)
|
||||
|
||||
def overdensity_field(self):
|
||||
fpath = self.paths.field(None, None, None, self.nsim, "borg1")
|
||||
with File(fpath, "r") as f:
|
||||
field = f["scalars/BORG_final_density"][:].astype(numpy.float32)
|
||||
|
||||
return field
|
||||
|
||||
def density_field(self):
|
||||
field = self.overdensity_field()
|
||||
omega0 = 0.307
|
||||
rho_mean = omega0 * 277.53662724583074 # Msun / kpc^3
|
||||
field += 1
|
||||
field *= rho_mean
|
||||
return field
|
||||
|
||||
def velocity_field(self, MAS, grid):
|
||||
raise RuntimeError("The velocity field is not available.")
|
||||
|
||||
def radial_velocity_field(self, MAS, grid):
|
||||
raise RuntimeError("The radial velocity field is not available.")
|
||||
|
||||
|
||||
###############################################################################
|
||||
# BORG2 field class #
|
||||
###############################################################################
|
||||
|
||||
|
||||
class BORG2Field(BaseField):
|
||||
"""
|
||||
BORG2 `z = 0` field class.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nsim : int
|
||||
Simulation index.
|
||||
paths : Paths, optional
|
||||
Paths object. By default, the paths are set to the `glamdring` paths.
|
||||
"""
|
||||
def __init__(self, nsim, paths=None):
|
||||
super().__init__(nsim, paths, False)
|
||||
|
||||
def overdensity_field(self):
|
||||
fpath = self.paths.field(None, None, None, self.nsim, "borg2")
|
||||
with File(fpath, "r") as f:
|
||||
field = f["scalars/BORG_final_density"][:].astype(numpy.float32)
|
||||
|
||||
return field
|
||||
|
||||
def density_field(self):
|
||||
field = self.overdensity_field()
|
||||
omega0 = 0.3111
|
||||
rho_mean = omega0 * 277.53662724583074 # h^2 Msun / kpc^3
|
||||
field += 1
|
||||
field *= rho_mean
|
||||
# return field
|
||||
|
||||
def velocity_field(self, MAS, grid):
|
||||
raise RuntimeError("The velocity field is not available.")
|
||||
|
||||
def radial_velocity_field(self, MAS, grid):
|
||||
raise RuntimeError("The radial velocity field is not available.")
|
||||
|
||||
|
||||
###############################################################################
|
||||
# TNG300-1 field #
|
||||
###############################################################################
|
||||
|
||||
class TNG300_1Field(BaseField):
|
||||
"""
|
||||
TNG300-1 dark matter-only `z = 0` field class.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
paths : Paths, optional
|
||||
Paths object. By default, the paths are set to the `glamdring` paths.
|
||||
"""
|
||||
def __init__(self, paths=None):
|
||||
super().__init__(0, paths, False)
|
||||
|
||||
def overdensity_field(self, MAS, grid):
|
||||
density = self.density_field(MAS, grid)
|
||||
omega_dm = 0.3089 - 0.0486
|
||||
rho_mean = omega_dm * 277.53662724583074 # h^2 Msun / kpc^3
|
||||
|
||||
density /= rho_mean
|
||||
density -= 1
|
||||
|
||||
return density
|
||||
|
||||
def density_field(self, MAS, grid):
|
||||
fpath = join(self.paths.tng300_1, "postprocessing", "density_field",
|
||||
f"rho_dm_099_{grid}_{MAS}.npy")
|
||||
return numpy.load(fpath)
|
||||
|
||||
def velocity_field(self, MAS, grid):
|
||||
raise RuntimeError("The velocity field is not available.")
|
||||
|
||||
def radial_velocity_field(self, MAS, grid):
|
||||
raise RuntimeError("The radial velocity field is not available.")
|
||||
|
||||
###############################################################################
|
||||
# Quijote field class #
|
||||
|
@ -739,7 +1031,7 @@ class QuijoteField(CSiBORG1Field):
|
|||
Paths object.
|
||||
"""
|
||||
def __init__(self, nsim, paths):
|
||||
super().__init__(nsim, paths)
|
||||
super().__init__(nsim, paths, flip_xz=False)
|
||||
self._simname = "quijote"
|
||||
|
||||
|
||||
|
|
|
@ -21,61 +21,53 @@ from tqdm import tqdm
|
|||
###############################################################################
|
||||
|
||||
|
||||
def read_interpolated_field(survey_name, kind, galaxy_index, paths, MAS, grid,
|
||||
in_rsp, rand_data=False, verbose=True):
|
||||
def read_interpolated_field(survey, simname, kind, MAS, grid, paths,
|
||||
verbose=True):
|
||||
"""
|
||||
Read in the interpolated field at the galaxy positions, and reorder the
|
||||
data to match the galaxy index.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
survey_name : str
|
||||
Survey name.
|
||||
survey : Survey
|
||||
Survey object.
|
||||
simname : str
|
||||
Simulation name.
|
||||
kind : str
|
||||
Field kind.
|
||||
galaxy_index : 1-dimensional array
|
||||
Galaxy indices to read in.
|
||||
paths : py:class:`csiborgtools.read.Paths`
|
||||
Paths manager.
|
||||
MAS : str
|
||||
Mass assignment scheme.
|
||||
grid : int
|
||||
Grid size.
|
||||
in_rsp : bool
|
||||
Whether to read in the field in redshift space.
|
||||
rand_data : bool, optional
|
||||
Whether to read in the random field data instead of the galaxy field.
|
||||
paths : py:class:`csiborgtools.read.Paths`
|
||||
Paths manager.
|
||||
verbose : bool, optional
|
||||
Verbosity flag.
|
||||
|
||||
Returns
|
||||
-------
|
||||
3-dimensional array of shape (nsims, len(galaxy_index), nsmooth)
|
||||
val : 3-dimensional array of shape (nsims, num_gal, nsmooth)
|
||||
Scalar field values at the galaxy positions.
|
||||
smooth_scales : 1-dimensional array
|
||||
Smoothing scales.
|
||||
"""
|
||||
nsims = paths.get_ics("csiborg")
|
||||
nsims = paths.get_ics(simname)
|
||||
|
||||
for i, nsim in enumerate(tqdm(nsims,
|
||||
desc="Reading fields",
|
||||
disable=not verbose)):
|
||||
fpath = paths.field_interpolated(
|
||||
survey_name, kind, MAS, grid, nsim, in_rsp=in_rsp)
|
||||
fpath = paths.field_interpolated(survey.name, simname, nsim, kind, MAS,
|
||||
grid)
|
||||
data = numpy.load(fpath)
|
||||
out_ = data["val"] if not rand_data else data["rand_val"]
|
||||
out_ = data["val"]
|
||||
|
||||
if i == 0:
|
||||
out = numpy.empty((len(nsims), *out_.shape), dtype=out_.dtype)
|
||||
indxs = data["indxs"]
|
||||
smooth_scales = data["smooth_scales"]
|
||||
|
||||
out[i] = out_
|
||||
|
||||
# Reorder the data to match the survey index.
|
||||
ind2pos = {v: k for k, v in enumerate(indxs)}
|
||||
ks = numpy.empty(len(galaxy_index), dtype=numpy.int64)
|
||||
if survey.selection_mask is not None:
|
||||
out = out[:, survey.selection_mask, :]
|
||||
|
||||
for i, k in enumerate(galaxy_index):
|
||||
j = ind2pos.get(k, None)
|
||||
if j is None:
|
||||
raise ValueError(f"There is no galaxy with index {k} in the "
|
||||
"interpolated field.")
|
||||
ks[i] = j
|
||||
|
||||
return out[:, ks, :]
|
||||
return out, smooth_scales
|
||||
|
|
|
@ -32,7 +32,8 @@ def find_peak(x, weights, shrink=0.95, min_obs=5):
|
|||
"""
|
||||
Find the peak of a 1D distribution using a shrinking window.
|
||||
"""
|
||||
assert shrink <= 1.
|
||||
if not shrink < 1:
|
||||
raise ValueError("`shrink` must be less than 1.")
|
||||
|
||||
xmin, xmax = numpy.min(x), numpy.max(x)
|
||||
xpos = (xmax + xmin) / 2
|
||||
|
@ -58,9 +59,9 @@ class PairOverlap:
|
|||
|
||||
Parameters
|
||||
----------
|
||||
cat0 : :py:class:`csiborgtools.read.CSiBORGHaloCatalogue`
|
||||
cat0 : instance of :py:class:`csiborgtools.read.BaseCatalogue`
|
||||
Halo catalogue corresponding to the reference simulation.
|
||||
catx : :py:class:`csiborgtools.read.CSiBORGHaloCatalogue`
|
||||
catx : instance of :py:class:`csiborgtools.read.BaseCatalogue`
|
||||
Halo catalogue corresponding to the cross simulation.
|
||||
min_logmass : float
|
||||
Minimum halo mass in :math:`\log_{10} M_\odot / h` to consider.
|
||||
|
@ -305,17 +306,21 @@ class PairOverlap:
|
|||
"""
|
||||
assert (norm_kind is None or norm_kind in ("r200c", "ref_patch", "sum_patch")) # noqa
|
||||
# Get positions either in the initial or final snapshot
|
||||
pos0 = self.cat0().position(in_initial=in_initial)
|
||||
posx = self.catx().position(in_initial=in_initial)
|
||||
if in_initial:
|
||||
pos0 = self.cat0("lagpatch_coordinates")
|
||||
posx = self.catx("lagpatch_coordinates")
|
||||
else:
|
||||
pos0 = self.cat0("cartesian_pos")
|
||||
posx = self.catx("cartesian_pos")
|
||||
|
||||
# Get the normalisation array if applicable
|
||||
if norm_kind == "r200c":
|
||||
norm = self.cat0("r200c")
|
||||
if norm_kind == "ref_patch":
|
||||
norm = self.cat0("lagpatch_size")
|
||||
norm = self.cat0("lagpatch_radius")
|
||||
if norm_kind == "sum_patch":
|
||||
patch0 = self.cat0("lagpatch_size")
|
||||
patchx = self.catx("lagpatch_size")
|
||||
patch0 = self.cat0("lagpatch_radius")
|
||||
patchx = self.catx("lagpatch_radius")
|
||||
norm = [None] * len(self)
|
||||
for i, ind in enumerate(self["match_indxs"]):
|
||||
norm[i] = patch0[i] + patchx[ind]
|
||||
|
@ -330,7 +335,7 @@ class PairOverlap:
|
|||
dist[i] /= norm[i]
|
||||
return numpy.array(dist, dtype=object)
|
||||
|
||||
def mass_ratio(self, mass_kind="totpartmass", in_log=True, in_abs=True):
|
||||
def mass_ratio(self, in_log=True, in_abs=True):
|
||||
"""
|
||||
Pair mass ratio of matched halos between the reference and cross
|
||||
simulations.
|
||||
|
@ -350,7 +355,7 @@ class PairOverlap:
|
|||
-------
|
||||
ratio : array of 1-dimensional arrays of shape `(nhalos, )`
|
||||
"""
|
||||
mass0, massx = self.cat0(mass_kind), self.catx(mass_kind)
|
||||
mass0, massx = self.cat0("totmass"), self.catx("totmass")
|
||||
|
||||
ratio = [None] * len(self)
|
||||
for i, ind in enumerate(self["match_indxs"]):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue