2022-10-30 20:16:56 +00:00
|
|
|
# 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.
|
|
|
|
"""
|
2023-07-25 14:12:58 +00:00
|
|
|
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
|
2022-10-30 20:16:56 +00:00
|
|
|
"""
|
2023-04-19 14:39:35 +00:00
|
|
|
from argparse import ArgumentParser
|
2023-04-18 09:02:36 +00:00
|
|
|
|
2023-04-04 14:22:00 +00:00
|
|
|
import numpy
|
2022-10-30 20:16:56 +00:00
|
|
|
from mpi4py import MPI
|
2023-07-25 14:12:58 +00:00
|
|
|
from taskmaster import work_delegation
|
2023-07-24 12:10:21 +00:00
|
|
|
from tqdm import trange
|
|
|
|
|
|
|
|
from utils import get_nsims
|
2023-04-18 09:02:36 +00:00
|
|
|
|
2022-10-30 20:16:56 +00:00
|
|
|
try:
|
|
|
|
import csiborgtools
|
|
|
|
except ModuleNotFoundError:
|
|
|
|
import sys
|
2023-04-19 14:39:35 +00:00
|
|
|
|
2022-10-30 20:16:56 +00:00
|
|
|
sys.path.append("../")
|
|
|
|
import csiborgtools
|
|
|
|
|
2023-07-25 14:12:58 +00:00
|
|
|
|
|
|
|
def fit_halo(particles, box):
|
|
|
|
"""
|
2023-07-31 14:13:21 +00:00
|
|
|
Fit a single halo from the particle array. Only halos with more than 100
|
|
|
|
particles are fitted.
|
2023-07-25 14:12:58 +00:00
|
|
|
|
|
|
|
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)
|
2023-04-19 14:39:35 +00:00
|
|
|
|
|
|
|
out = {}
|
2023-07-25 14:12:58 +00:00
|
|
|
out["npart"] = len(halo)
|
|
|
|
out["totpartmass"] = numpy.sum(halo["M"])
|
2023-04-19 14:39:35 +00:00
|
|
|
for i, v in enumerate(["vx", "vy", "vz"]):
|
2023-07-25 14:12:58 +00:00
|
|
|
out[v] = numpy.average(halo.vel[:, i], weights=halo["M"])
|
|
|
|
|
2023-07-31 14:13:21 +00:00
|
|
|
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)
|
|
|
|
|
2023-07-25 14:12:58 +00:00
|
|
|
out["m200c"] = m200c
|
|
|
|
out["r200c"] = r200c
|
2023-07-31 14:13:21 +00:00
|
|
|
out["lambda200c"] = halo.lambda_bullock(angmom, m200c, r200c)
|
|
|
|
out["conc"] = halo.nfw_concentration(dist, r200c)
|
2023-04-19 14:39:35 +00:00
|
|
|
return out
|
|
|
|
|
|
|
|
|
2023-07-25 14:12:58 +00:00
|
|
|
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),]
|
|
|
|
|
2023-07-27 16:41:00 +00:00
|
|
|
nsnap = max(paths.get_snapshots(nsim, simname))
|
|
|
|
if simname == "csiborg":
|
|
|
|
box = csiborgtools.read.CSiBORGBox(nsnap, nsim, paths)
|
|
|
|
cat = csiborgtools.read.CSiBORGHaloCatalogue(
|
2023-07-28 19:07:28 +00:00
|
|
|
nsim, paths, bounds=None, load_fitted=False, load_initial=False)
|
2023-07-27 16:41:00 +00:00
|
|
|
else:
|
|
|
|
box = csiborgtools.read.QuijoteBox(nsnap, nsim, paths)
|
|
|
|
cat = csiborgtools.read.QuijoteHaloCatalogue(
|
2023-07-28 19:07:28 +00:00
|
|
|
nsim, paths, nsnap, bounds=None, load_fitted=False,
|
|
|
|
load_initial=False)
|
2023-04-18 09:02:36 +00:00
|
|
|
|
2023-05-02 12:57:13 +00:00
|
|
|
# Particle archive
|
2023-07-27 16:41:00 +00:00
|
|
|
f = csiborgtools.read.read_h5(paths.particles(nsim, simname))
|
2023-05-06 15:52:48 +00:00
|
|
|
particles = f["particles"]
|
2023-07-24 12:10:21 +00:00
|
|
|
halo_map = f["halomap"]
|
2023-07-25 14:12:58 +00:00
|
|
|
hid2map = {hid: i for i, hid in enumerate(halo_map[:, 0])}
|
|
|
|
|
|
|
|
out = csiborgtools.read.cols_to_structured(len(cat), cols)
|
2023-07-24 12:10:21 +00:00
|
|
|
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)
|
2023-07-25 14:12:58 +00:00
|
|
|
# Skip if no particles.
|
2023-05-06 15:52:48 +00:00
|
|
|
if part is None:
|
|
|
|
continue
|
2023-04-19 14:39:35 +00:00
|
|
|
|
2023-07-25 14:12:58 +00:00
|
|
|
_out = fit_halo(part, box)
|
2023-05-06 15:52:48 +00:00
|
|
|
for key in _out.keys():
|
2023-07-31 14:13:21 +00:00
|
|
|
out[key][i] = _out.get(key, numpy.nan)
|
2023-04-19 14:39:35 +00:00
|
|
|
|
2023-07-27 16:41:00 +00:00
|
|
|
fout = paths.structfit(nsnap, nsim, simname)
|
2023-07-25 14:12:58 +00:00
|
|
|
if verbose:
|
|
|
|
print(f"Saving to `{fout}`.", flush=True)
|
2023-05-06 15:52:48 +00:00
|
|
|
numpy.save(fout, out)
|
2023-07-25 14:12:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
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)
|