mirror of
https://github.com/Richard-Sti/csiborgtools.git
synced 2024-12-22 17:38:02 +00:00
acb8d9571c
* Fix Quijote units * Updates to units * Fix how things are loaded * Updating definitions & conventions * Clear up how fiducial observers in quijote work * Refactorize array manip * Move function definition * More code refactoring * Remove unused argument * Remove `convert_from_box` * Make a note * Converting particle units * Add notes about units * Remove box constants * Add rho_crit0 * Fix spherical overdensity mass units * Refactor more code * Edit catalogue kwargs * Edit the docstring * Edit bounds * Add new checks for empty array * Remove unused import * Remove old code * Remove old function * Update real 2 redshift * Clear up the RSP conv * Add comments * Add some units
198 lines
6.5 KiB
Python
198 lines
6.5 KiB
Python
# 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.
|
|
r"""
|
|
Script to load in the simulation particles, sort them by their FoF halo ID and
|
|
dump into a HDF5 file. Stores the first and last index of each halo in the
|
|
particle array. This can be used for fast slicing of the array to acces
|
|
particles of a single clump.
|
|
|
|
Ensures the following units:
|
|
- Positions in box units.
|
|
- Velocities in :math:`\mathrm{km} / \mathrm{s}`.
|
|
- Masses in :math:`M_\odot / h`.
|
|
"""
|
|
from argparse import ArgumentParser
|
|
from datetime import datetime
|
|
from gc import collect
|
|
|
|
import h5py
|
|
import numba
|
|
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
|
|
|
|
|
|
@numba.jit(nopython=True)
|
|
def minmax_halo(hid, halo_ids, start_loop=0):
|
|
"""
|
|
Find the start and end index of a halo in a sorted array of halo IDs.
|
|
This is much faster than using `numpy.where` and then `numpy.min` and
|
|
`numpy.max`.
|
|
"""
|
|
start = None
|
|
end = None
|
|
|
|
for i in range(start_loop, halo_ids.size):
|
|
n = halo_ids[i]
|
|
if n == hid:
|
|
if start is None:
|
|
start = i
|
|
end = i
|
|
elif n > hid:
|
|
break
|
|
return start, end
|
|
|
|
|
|
###############################################################################
|
|
# Sorting and dumping #
|
|
###############################################################################
|
|
|
|
|
|
def main(nsim, simname, verbose):
|
|
"""
|
|
Read in the snapshot particles, sort them by their FoF halo ID and dump
|
|
into a HDF5 file. Stores the first and last index of each halo in the
|
|
particle array for fast slicing of the array to acces particles of a single
|
|
halo.
|
|
|
|
Parameters
|
|
----------
|
|
nsim : int
|
|
IC realisation index.
|
|
simname : str
|
|
Simulation name.
|
|
verbose : bool
|
|
Verbosity flag.
|
|
|
|
Returns
|
|
-------
|
|
None
|
|
"""
|
|
paths = csiborgtools.read.Paths(**csiborgtools.paths_glamdring)
|
|
if simname == "csiborg":
|
|
partreader = csiborgtools.read.CSiBORGReader(paths)
|
|
else:
|
|
partreader = csiborgtools.read.QuijoteReader(paths)
|
|
|
|
nsnap = max(paths.get_snapshots(nsim, simname))
|
|
fname = paths.particles(nsim, simname)
|
|
# We first read in the halo IDs of the particles and infer the sorting.
|
|
# Right away we dump the halo IDs to a HDF5 file and clear up memory.
|
|
if verbose:
|
|
print(f"{datetime.now()}: loading PIDs of IC {nsim}.", flush=True)
|
|
part_hids = partreader.read_fof_hids(
|
|
nsnap=nsnap, nsim=nsim, verbose=verbose)
|
|
if verbose:
|
|
print(f"{datetime.now()}: sorting PIDs of IC {nsim}.", flush=True)
|
|
sort_indxs = numpy.argsort(part_hids).astype(numpy.int32)
|
|
part_hids = part_hids[sort_indxs]
|
|
with h5py.File(fname, "w") as f:
|
|
f.create_dataset("halo_ids", data=part_hids)
|
|
f.close()
|
|
del part_hids
|
|
collect()
|
|
|
|
# Next we read in the particles and sort them by their halo ID.
|
|
# We cannot directly read this as an unstructured array because the float32
|
|
# precision is insufficient to capture the halo IDs.
|
|
if simname == "csiborg":
|
|
pars_extract = ['x', 'y', 'z', 'vx', 'vy', 'vz', 'M', "ID"]
|
|
else:
|
|
pars_extract = None
|
|
parts, pids = partreader.read_particle(
|
|
nsnap, nsim, pars_extract, return_structured=False, verbose=verbose)
|
|
|
|
# In case of CSiBORG, we need to convert the mass and velocities from
|
|
# box units.
|
|
if simname == "csiborg":
|
|
box = csiborgtools.read.CSiBORGBox(nsnap, nsim, paths)
|
|
parts[:, [3, 4, 5]] = box.box2vel(parts[:, [3, 4, 5]])
|
|
parts[:, 6] = box.box2solarmass(parts[:, 6])
|
|
|
|
# Now we in two steps save the particles and particle IDs.
|
|
if verbose:
|
|
print(f"{datetime.now()}: dumping particles from {nsim}.", flush=True)
|
|
parts = parts[sort_indxs]
|
|
pids = pids[sort_indxs]
|
|
del sort_indxs
|
|
collect()
|
|
|
|
with h5py.File(fname, "r+") as f:
|
|
f.create_dataset("particle_ids", data=pids)
|
|
f.close()
|
|
del pids
|
|
collect()
|
|
|
|
with h5py.File(fname, "r+") as f:
|
|
f.create_dataset("particles", data=parts)
|
|
f.close()
|
|
del parts
|
|
collect()
|
|
|
|
if verbose:
|
|
print(f"{datetime.now()}: creating a halo map for {nsim}.", flush=True)
|
|
# Load clump IDs back to memory
|
|
with h5py.File(fname, "r") as f:
|
|
part_hids = f["halo_ids"][:]
|
|
# We loop over the unique halo IDs.
|
|
unique_halo_ids = numpy.unique(part_hids)
|
|
halo_map = numpy.full((unique_halo_ids.size, 3), numpy.nan,
|
|
dtype=numpy.int32)
|
|
start_loop = 0
|
|
niters = unique_halo_ids.size
|
|
for i in trange(niters) if verbose else range(niters):
|
|
hid = unique_halo_ids[i]
|
|
k0, kf = minmax_halo(hid, part_hids, start_loop=start_loop)
|
|
halo_map[i, 0] = hid
|
|
halo_map[i, 1] = k0
|
|
halo_map[i, 2] = kf
|
|
start_loop = kf
|
|
|
|
# We save the mapping to a HDF5 file
|
|
with h5py.File(fname, "r+") as f:
|
|
f.create_dataset("halomap", data=halo_map)
|
|
f.close()
|
|
|
|
del part_hids
|
|
collect()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# And next parse all the arguments and set up CSiBORG objects
|
|
parser = ArgumentParser()
|
|
parser.add_argument("--simname", type=str, default="csiborg",
|
|
choices=["csiborg", "quijote"],
|
|
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, verbose=MPI.COMM_WORLD.Get_size() == 1)
|
|
|
|
work_delegation(_main, nsims, MPI.COMM_WORLD)
|