Remove halo fitting. (#83)

* Rename file

* Remove old content

* Remove halo fit

* Completely remove fits

* Add utils here

* Account for renaming
This commit is contained in:
Richard Stiskalek 2023-08-07 11:33:27 +02:00 committed by GitHub
parent ff395af148
commit f4a7cb0d16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 148 additions and 718 deletions

View file

@ -12,7 +12,8 @@
# 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 csiborgtools import clustering, field, fits, match, read # noqa from csiborgtools import clustering, field, match, read # noqa
from .utils import (center_of_mass, delta2ncells, periodic_distance, number_counts) # noqa
# Arguments to csiborgtools.read.Paths. # Arguments to csiborgtools.read.Paths.
paths_glamdring = {"srcdir": "/mnt/extraspace/hdesmond/", paths_glamdring = {"srcdir": "/mnt/extraspace/hdesmond/",

View file

@ -1,16 +0,0 @@
# Copyright (C) 2022 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.
from .halo import (Halo, delta2ncells, center_of_mass, # noqa
periodic_distance, shift_to_center_of_box, number_counts) # noqa

View file

@ -1,542 +0,0 @@
# Copyright (C) 2022 Richard Stiskalek, Deaglan Bartlett
# 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.
"""A halo object."""
from abc import ABC
import numpy
from numba import jit
from scipy.optimize import minimize
GRAV = 4.300917270069976e-09 # G in (Msun / h)^-1 (Mpc / h) (km / s)^2
class BaseStructure(ABC):
"""
Basic structure object for handling operations on its particles.
"""
_particles = None
_box = None
@property
def particles(self):
"""
Particle array.
Returns
-------
particles : structured array
"""
return self._particles
@particles.setter
def particles(self, particles):
assert particles.ndim == 2 and particles.shape[1] == 7
self._particles = particles
@property
def box(self):
"""
Box object handling unit conversion.
Returns
-------
box : Object derived from :py:class:`csiborgtools.units.BaseBox`
"""
return self._box
@box.setter
def box(self, box):
try:
assert box._name == "box_units"
self._box = box
except AttributeError as err:
raise TypeError from err
@property
def pos(self):
"""
Cartesian particle coordinates in the box coordinate system.
Returns
-------
pos : 2-dimensional array of shape `(n_particles, 3)`.
"""
return numpy.vstack([self[p] for p in ('x', 'y', 'z')]).T
@property
def vel(self):
"""
Cartesian particle velocity components.
Returns
-------
vel : 2-dimensional array of shape (`n_particles, 3`)
"""
return numpy.vstack([self[p] for p in ("vx", "vy", "vz")]).T
def center_of_mass(self, npart_min=30, shrink_factor=0.98):
r"""
Calculate the center of mass of a halo via the shrinking sphere
procedure. Iteratively reduces initial radius and calculates the CM of
enclosed particles while the number of enclosed particles is greater
than a set minimum.
Parameters
----------
npart_min : int, optional
Minimum number of enclosed particles above which to continue
shrinking the sphere.
shrink_factor : float, optional
Factor by which to shrink the sphere radius at each iteration.
Returns
-------
cm : 1-dimensional array of shape `(3, )`
Center of mass in box units.
dist : 1-dimensional array of shape `(n_particles, )`
Distance of each particle from the center of mass in box units.
"""
pos, mass = self.pos, self["M"]
cm = center_of_mass(pos, mass, boxsize=1)
rad = None
while True:
dist = periodic_distance(pos, cm, boxsize=1)
if rad is None:
rad = numpy.max(dist)
within_rad = dist <= rad
cm = center_of_mass(pos[within_rad], mass[within_rad], boxsize=1)
if numpy.sum(within_rad) < npart_min:
return cm, periodic_distance(pos, cm, boxsize=1)
rad *= shrink_factor
def spherical_overdensity_mass(self, dist, delta_mult, kind="crit"):
r"""
Calculate spherical overdensity mass and radius around a CM, defined as
the inner-most radius where the density falls below a given threshold.
The exact radius is found via linear interpolation between the two
particles enclosing the threshold.
Parameters
----------
dist : 1-dimensional array of shape `(n_particles, )`
Distance of each particle from the centre of mass in box units.
delta_mult : int or float
Overdensity multiple.
kind : str, optional
Either `crit` or `matter`, for critical or matter overdensity
Returns
-------
mass : float
Overdensity mass in (Msun / h).
rad : float
Overdensity radius in box units.
"""
if kind not in ["crit", "matter"]:
raise ValueError("kind must be either `crit` or `matter`.")
rho = delta_mult * self.box.rho_crit0
rho *= self.box.Om if kind == "matter" else 1.
argsort = numpy.argsort(dist)
dist = self.box.box2mpc(dist[argsort])
norm_density = numpy.cumsum(self['M'][argsort])
totmass = norm_density[-1]
with numpy.errstate(divide="ignore"):
norm_density /= (4. / 3. * numpy.pi * dist**3)
norm_density /= rho
# This ensures that the j - 1 index is also just above 1, therefore the
# expression below strictly interpolates.
j = find_first_below_threshold(norm_density, 1.)
if j is None:
return numpy.nan, numpy.nan
i = j - 1
rad = (dist[j] - dist[i])
rad *= (1. - norm_density[i]) / (norm_density[j] - norm_density[i])
rad += dist[i]
mass = radius_to_mass(rad, rho)
rad = self.box.mpc2box(rad)
if mass > totmass:
return numpy.nan, numpy.nan
return mass, rad
def angular_momentum(self, dist, cm, rad, npart_min=10):
r"""
Calculate angular momentum around a centre of mass using all particles
within a radius. Accounts for periodicity of the box and units are
(Msun / h) * (Mpc / h) * (km / s).
Parameters
----------
dist : 1-dimensional array of shape `(n_particles, )`
Distance of each particle from center of mass in box units.
cm : 1-dimensional array of shape `(3, )`
Reference point in box units.
rad : float
Radius around the reference point in box units.
npart_min : int, optional
Minimum number of enclosed particles for a radius to be
considered trustworthy.
Returns
-------
angmom : 1-dimensional array or shape `(3, )`
"""
mask = dist < rad
if numpy.sum(mask) < npart_min:
return numpy.full(3, numpy.nan, numpy.float32)
mass, pos, vel = self["M"][mask], self.pos[mask], self.vel[mask]
pos = shift_to_center_of_box(pos, cm, 1.0, set_cm_to_zero=True)
pos = self.box.box2mpc(pos)
vel -= numpy.average(vel, axis=0, weights=mass)
return numpy.sum(mass[:, numpy.newaxis] * numpy.cross(pos, vel),
axis=0)
def lambda_bullock(self, angmom, mass, rad):
"""
Calculate the Bullock spin, see Eq. 5 in [1].
Parameters
----------
angmom : 1-dimensional array of shape `(3, )`
Angular momentum in (Msun / h) * (Mpc / h) * (km / s).
ref : 1-dimensional array of shape `(3, )`
Reference point in box units.
rad : float
Radius around the reference point in box units.
Returns
-------
lambda_bullock : float
References
----------
[1] A Universal Angular Momentum Profile for Galactic Halos; 2001;
Bullock, J. S.; Dekel, A.; Kolatt, T. S.; Kravtsov, A. V.;
Klypin, A. A.; Porciani, C.; Primack, J. R.
"""
out = numpy.linalg.norm(angmom)
return out / numpy.sqrt(2 * GRAV * mass**3 * self.box.box2mpc(rad))
def nfw_concentration(self, dist, rad, conc_min=1e-3, npart_min=10):
"""
Calculate the NFW concentration parameter in a given radius around a
reference point.
Parameters
----------
dist : 1-dimensional array of shape `(n_particles, )`
Distance of each particle from center of mass in box units.
rad : float
Radius around the reference point in box units.
conc_min : float
Minimum concentration limit.
npart_min : int, optional
Minimum number of enclosed particles to calculate the
concentration.
Returns
-------
conc : float
"""
mask = dist < rad
if numpy.sum(mask) < npart_min:
return numpy.nan
dist, weight = dist[mask], self["M"][mask]
weight /= weight[0]
res = minimize(negll_nfw_concentration, x0=1.,
args=(dist / rad, weight, ), method='Nelder-Mead',
bounds=((numpy.log10(conc_min), 5),))
if not res.success:
return numpy.nan
conc = 10**res["x"][0]
if conc < conc_min or numpy.isclose(conc, conc_min):
return numpy.nan
return conc
def __getitem__(self, key):
key_to_index = {'x': 0, 'y': 1, 'z': 2,
'vx': 3, 'vy': 4, 'vz': 5, 'M': 6}
if key not in key_to_index:
raise RuntimeError(f"Invalid key `{key}`!")
return self.particles[:, key_to_index[key]]
def __len__(self):
return self.particles.shape[0]
class Halo(BaseStructure):
"""
Halo object to handle operations on its particles.
Parameters
----------
particles : structured array
Particle array. Must contain `['x', 'y', 'z', 'vx', 'vy', 'vz', 'M']`.
info : structured array
Array containing information from the halo finder.
box : :py:class:`csiborgtools.read.CSiBORGBox`
Box units object.
"""
def __init__(self, particles, box):
self.particles = particles
self.box = box
###############################################################################
# Other, supplementary functions #
###############################################################################
@jit(nopython=True, fastmath=True, boundscheck=False)
def center_of_mass(points, mass, boxsize):
"""
Calculate the center of mass of a halo while assuming periodic boundary
conditions of a cubical box. Assuming that particle positions are in
`[0, boxsize)` range. This is a JIT implementation.
Parameters
----------
points : 2-dimensional array of shape (n_particles, 3)
Particle position array.
mass : 1-dimensional array of shape `(n_particles, )`
Particle mass array.
boxsize : float
Box size in the same units as `parts` coordinates.
Returns
-------
cm : 1-dimensional array of shape `(3, )`
"""
cm = numpy.zeros(3, dtype=points.dtype)
totmass = sum(mass)
# Convert positions to unit circle coordinates in the complex plane,
# calculate the weighted average and convert it back to box coordinates.
for i in range(3):
cm_i = sum(mass * numpy.exp(2j * numpy.pi * points[:, i] / boxsize))
cm_i /= totmass
cm_i = numpy.arctan2(cm_i.imag, cm_i.real) * boxsize / (2 * numpy.pi)
if cm_i < 0:
cm_i += boxsize
cm[i] = cm_i
return cm
@jit(nopython=True)
def periodic_distance(points, reference, boxsize):
"""
Compute the 3D distance between multiple points and a reference point using
periodic boundary conditions. This is an optimized JIT implementation.
Parameters
----------
points : 2-dimensional array of shape `(n_points, 3)`
Points to calculate the distance from the reference point.
reference : 1-dimensional array of shape `(3, )`
Reference point.
boxsize : float
Box size.
Returns
-------
dist : 1-dimensional array of shape `(n_points, )`
"""
npoints = len(points)
half_box = boxsize / 2
dist = numpy.zeros(npoints, dtype=points.dtype)
for i in range(npoints):
for j in range(3):
dist_1d = abs(points[i, j] - reference[j])
if dist_1d > (half_box):
dist_1d = boxsize - dist_1d
dist[i] += dist_1d**2
dist[i] = dist[i]**0.5
return dist
def shift_to_center_of_box(points, cm, boxsize, set_cm_to_zero=False):
"""
Shift the positions such that the CM is at the center of the box, while
accounting for periodic boundary conditions.
Parameters
----------
points : 2-dimensional array of shape `(n_points, 3)`
Points to shift.
cm : 1-dimensional array of shape `(3, )`
Center of mass.
boxsize : float
Box size.
set_cm_to_zero : bool, optional
If `True`, set the CM to zero.
Returns
-------
shifted_positions : 2-dimensional array of shape `(n_points, 3)`
"""
pos = (points + (boxsize / 2 - cm)) % boxsize
if set_cm_to_zero:
pos -= boxsize / 2
return pos
@jit(nopython=True, fastmath=True, boundscheck=False)
def radius_to_mass(radius, rho):
"""
Compute the mass of a sphere with a given radius and density.
Parameters
----------
radius : float
Radius of the sphere.
rho : float
Density of the sphere.
Returns
-------
mass : float
"""
return ((4 * numpy.pi * rho) / 3) * radius**3
@jit(nopython=True, fastmath=True, boundscheck=False)
def find_first_below_threshold(x, threshold):
"""
Find index of first element in `x` that is below `threshold`. The index
must be greater than 0. If no such element is found, return `None`.
Parameters
----------
x : 1-dimensional array
Array to search in.
threshold : float
Threshold value.
Returns
-------
index : int or None
"""
for i in range(1, len(x)):
if 1 < x[i - 1] and x[i] < threshold:
return i
return None
@jit(nopython=True, fastmath=True, boundscheck=False)
def negll_nfw_concentration(log_c, xs, w):
"""
Negative log-likelihood of the NFW concentration parameter.
Parameters
----------
log_c : float
Logarithm of the concentration parameter.
xs : 1-dimensional array
Normalised radii.
w : 1-dimensional array
Weights.
Returns
------
negll : float
"""
c = 10**log_c
ll = xs / (1 + c * xs)**2 * c**2
ll *= (1 + c) / ((1 + c) * numpy.log(1 + c) - c)
ll = numpy.sum(numpy.log(w * ll))
return -ll
@jit(nopython=True, fastmath=True, boundscheck=False)
def delta2ncells(delta):
"""
Calculate the number of cells in `delta` that are non-zero.
Parameters
----------
delta : 3-dimensional array
Halo density field.
Returns
-------
ncells : int
Number of non-zero cells.
"""
tot = 0
imax, jmax, kmax = delta.shape
for i in range(imax):
for j in range(jmax):
for k in range(kmax):
if delta[i, j, k] > 0:
tot += 1
return tot
@jit(nopython=True, fastmath=True, boundscheck=False)
def number_counts(x, bin_edges):
"""
Calculate counts of samples in bins.
Parameters
----------
x : 1-dimensional array
Samples' values.
bin_edges : 1-dimensional array
Bin edges.
Returns
-------
counts : 1-dimensional array
Bin counts.
"""
out = numpy.full(bin_edges.size - 1, numpy.nan, dtype=numpy.float32)
for i in range(bin_edges.size - 1):
out[i] = numpy.sum((x >= bin_edges[i]) & (x < bin_edges[i + 1]))
return out

140
csiborgtools/utils.py Normal file
View file

@ -0,0 +1,140 @@
# Copyright (C) 2022 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.
"""Collection of stand-off utility functions used in the scripts."""
import numpy
from numba import jit
@jit(nopython=True, fastmath=True, boundscheck=False)
def center_of_mass(points, mass, boxsize):
"""
Calculate the center of mass of a halo while assuming periodic boundary
conditions of a cubical box. Assuming that particle positions are in
`[0, boxsize)` range. This is a JIT implementation.
Parameters
----------
points : 2-dimensional array of shape (n_particles, 3)
Particle position array.
mass : 1-dimensional array of shape `(n_particles, )`
Particle mass array.
boxsize : float
Box size in the same units as `parts` coordinates.
Returns
-------
cm : 1-dimensional array of shape `(3, )`
"""
cm = numpy.zeros(3, dtype=points.dtype)
totmass = sum(mass)
# Convert positions to unit circle coordinates in the complex plane,
# calculate the weighted average and convert it back to box coordinates.
for i in range(3):
cm_i = sum(mass * numpy.exp(2j * numpy.pi * points[:, i] / boxsize))
cm_i /= totmass
cm_i = numpy.arctan2(cm_i.imag, cm_i.real) * boxsize / (2 * numpy.pi)
if cm_i < 0:
cm_i += boxsize
cm[i] = cm_i
return cm
@jit(nopython=True)
def periodic_distance(points, reference, boxsize):
"""
Compute the 3D distance between multiple points and a reference point using
periodic boundary conditions. This is an optimized JIT implementation.
Parameters
----------
points : 2-dimensional array of shape `(n_points, 3)`
Points to calculate the distance from the reference point.
reference : 1-dimensional array of shape `(3, )`
Reference point.
boxsize : float
Box size.
Returns
-------
dist : 1-dimensional array of shape `(n_points, )`
"""
npoints = len(points)
half_box = boxsize / 2
dist = numpy.zeros(npoints, dtype=points.dtype)
for i in range(npoints):
for j in range(3):
dist_1d = abs(points[i, j] - reference[j])
if dist_1d > (half_box):
dist_1d = boxsize - dist_1d
dist[i] += dist_1d**2
dist[i] = dist[i]**0.5
return dist
@jit(nopython=True, fastmath=True, boundscheck=False)
def delta2ncells(delta):
"""
Calculate the number of cells in `delta` that are non-zero.
Parameters
----------
delta : 3-dimensional array
Halo density field.
Returns
-------
ncells : int
Number of non-zero cells.
"""
tot = 0
imax, jmax, kmax = delta.shape
for i in range(imax):
for j in range(jmax):
for k in range(kmax):
if delta[i, j, k] > 0:
tot += 1
return tot
@jit(nopython=True, fastmath=True, boundscheck=False)
def number_counts(x, bin_edges):
"""
Calculate counts of samples in bins.
Parameters
----------
x : 1-dimensional array
Samples' values.
bin_edges : 1-dimensional array
Bin edges.
Returns
-------
counts : 1-dimensional array
Bin counts.
"""
out = numpy.full(bin_edges.size - 1, numpy.nan, dtype=numpy.float32)
for i in range(bin_edges.size - 1):
out[i] = numpy.sum((x >= bin_edges[i]) & (x < bin_edges[i + 1]))
return out

View file

@ -1,153 +0,0 @@
# Copyright (C) 2022 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.
"""
A script to fit FoF halos (concentration, ...). The CSiBORG particle array of
each realisation must have been processed in advance by `pre_dumppart.py`.
Quijote is not supported yet
"""
from argparse import ArgumentParser
import numpy
from mpi4py import MPI
from taskmaster import work_delegation
from tqdm import trange
from utils import get_nsims
try:
import csiborgtools
except ModuleNotFoundError:
import sys
sys.path.append("../")
import csiborgtools
def fit_halo(particles, box):
"""
Fit a single halo from the particle array. Only halos with more than 100
particles are fitted.
Parameters
----------
particles : 2-dimensional array of shape `(n_particles, 3)`
Particle array. The columns must be `x`, `y`, `z`, `vx`, `vy`, `vz`,
`M`.
box : object derived from :py:class`csiborgtools.read.BaseBox`
Box object.
Returns
-------
out : dict
"""
halo = csiborgtools.fits.Halo(particles, box)
out = {}
out["npart"] = len(halo)
out["totpartmass"] = numpy.sum(halo["M"])
for i, v in enumerate(["vx", "vy", "vz"]):
out[v] = numpy.average(halo.vel[:, i], weights=halo["M"])
if out["npart"] < 100:
return out
cm, dist = halo.center_of_mass()
m200c, r200c = halo.spherical_overdensity_mass(dist, 200)
angmom = halo.angular_momentum(dist, cm, r200c)
out["m200c"] = m200c
out["r200c"] = r200c
out["lambda200c"] = halo.lambda_bullock(angmom, m200c, r200c)
out["conc"] = halo.nfw_concentration(dist, r200c)
return out
def _main(nsim, simname, verbose):
"""
Fit the FoF halos.
Parameters
----------
nsim : int
IC realisation index.
simname : str
Simulation name.
verbose : bool
Verbosity flag.
"""
cols = [("index", numpy.int32),
("npart", numpy.int32),
("totpartmass", numpy.float32),
("vx", numpy.float32),
("vy", numpy.float32),
("vz", numpy.float32),
("conc", numpy.float32),
("r200c", numpy.float32),
("m200c", numpy.float32),
("lambda200c", numpy.float32),]
nsnap = max(paths.get_snapshots(nsim, simname))
if simname == "csiborg":
box = csiborgtools.read.CSiBORGBox(nsnap, nsim, paths)
cat = csiborgtools.read.CSiBORGHaloCatalogue(
nsim, paths, bounds=None, load_fitted=False, load_initial=False)
else:
box = csiborgtools.read.QuijoteBox(nsnap, nsim, paths)
cat = csiborgtools.read.QuijoteHaloCatalogue(
nsim, paths, nsnap, bounds=None, load_fitted=False,
load_initial=False)
# Particle archive
f = csiborgtools.read.read_h5(paths.particles(nsim, simname))
particles = f["particles"]
halo_map = f["halomap"]
hid2map = {hid: i for i, hid in enumerate(halo_map[:, 0])}
out = csiborgtools.read.cols_to_structured(len(cat), cols)
for i in trange(len(cat)) if verbose else range(len(cat)):
hid = cat["index"][i]
out["index"][i] = hid
part = csiborgtools.read.load_halo_particles(hid, particles, halo_map,
hid2map)
# Skip if no particles.
if part is None:
continue
_out = fit_halo(part, box)
for key in _out.keys():
out[key][i] = _out.get(key, numpy.nan)
fout = paths.structfit(nsnap, nsim, simname)
if verbose:
print(f"Saving to `{fout}`.", flush=True)
numpy.save(fout, out)
if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument("--simname", type=str, default="csiborg",
choices=["csiborg", "quijote", "quijote_full"],
help="Simulation name")
parser.add_argument("--nsims", type=int, nargs="+", default=None,
help="IC realisations. If `-1` processes all.")
args = parser.parse_args()
paths = csiborgtools.read.Paths(**csiborgtools.paths_glamdring)
nsims = get_nsims(args, paths)
def main(nsim):
_main(nsim, args.simname, MPI.COMM_WORLD.Get_size() == 1)
work_delegation(main, nsims, MPI.COMM_WORLD)

View file

@ -61,7 +61,7 @@ def get_counts(nsim, bins, paths, parser_args):
cat = csiborgtools.read.CSiBORGHaloCatalogue( cat = csiborgtools.read.CSiBORGHaloCatalogue(
nsim, paths, bounds=bounds, load_fitted=False, load_initial=False) nsim, paths, bounds=bounds, load_fitted=False, load_initial=False)
logmass = numpy.log10(cat["fof_totpartmass"]) logmass = numpy.log10(cat["fof_totpartmass"])
counts = csiborgtools.fits.number_counts(logmass, bins) counts = csiborgtools.number_counts(logmass, bins)
elif simname == "quijote": elif simname == "quijote":
cat0 = csiborgtools.read.QuijoteHaloCatalogue( cat0 = csiborgtools.read.QuijoteHaloCatalogue(
nsim, paths, nsnap=4, load_fitted=False, load_initial=False) nsim, paths, nsnap=4, load_fitted=False, load_initial=False)
@ -72,12 +72,12 @@ def get_counts(nsim, bins, paths, parser_args):
for nobs in range(nmax): for nobs in range(nmax):
cat = cat0.pick_fiducial_observer(nobs, rmax=parser_args.Rmax) cat = cat0.pick_fiducial_observer(nobs, rmax=parser_args.Rmax)
logmass = numpy.log10(cat["group_mass"]) logmass = numpy.log10(cat["group_mass"])
counts[nobs, :] = csiborgtools.fits.number_counts(logmass, bins) counts[nobs, :] = csiborgtools.number_counts(logmass, bins)
elif simname == "quijote_full": elif simname == "quijote_full":
cat = csiborgtools.read.QuijoteHaloCatalogue( cat = csiborgtools.read.QuijoteHaloCatalogue(
nsim, paths, nsnap=4, load_fitted=False, load_initial=False) nsim, paths, nsnap=4, load_fitted=False, load_initial=False)
logmass = numpy.log10(cat["group_mass"]) logmass = numpy.log10(cat["group_mass"])
counts = csiborgtools.fits.number_counts(logmass, bins) counts = csiborgtools.number_counts(logmass, bins)
else: else:
raise ValueError(f"Unknown simulation name `{simname}`.") raise ValueError(f"Unknown simulation name `{simname}`.")

View file

@ -91,14 +91,14 @@ def _main(nsim, simname, verbose):
pos, mass = part[:, :3], part[:, 3] pos, mass = part[:, :3], part[:, 3]
# Calculate the centre of mass and the Lagrangian patch size. # Calculate the centre of mass and the Lagrangian patch size.
cm = csiborgtools.fits.center_of_mass(pos, mass, boxsize=1.0) cm = csiborgtools.center_of_mass(pos, mass, boxsize=1.0)
distances = csiborgtools.fits.periodic_distance(pos, cm, boxsize=1.0) distances = csiborgtools.periodic_distance(pos, cm, boxsize=1.0)
out["x"][i], out["y"][i], out["z"][i] = cm out["x"][i], out["y"][i], out["z"][i] = cm
out["lagpatch_size"][i] = numpy.percentile(distances, 99) out["lagpatch_size"][i] = numpy.percentile(distances, 99)
# Calculate the number of cells with > 0 density. # Calculate the number of cells with > 0 density.
delta = overlapper.make_delta(pos, mass, subbox=True) delta = overlapper.make_delta(pos, mass, subbox=True)
out["lagpatch_ncells"][i] = csiborgtools.fits.delta2ncells(delta) out["lagpatch_ncells"][i] = csiborgtools.delta2ncells(delta)
# Now save it # Now save it
fout = paths.initmatch(nsim, simname, "fit") fout = paths.initmatch(nsim, simname, "fit")