New RSP density distinction (#72)

* Edit docs

* Fix kernel units

* Add NGP interpolation

* Add Vobs but not implemented

* Add BORG density option

* Add BORG mcmc path

* Organise imports

* Add new density field distinction
This commit is contained in:
Richard Stiskalek 2023-06-26 20:41:07 +01:00 committed by GitHub
parent cd6d448874
commit de7def61d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 141 additions and 74 deletions

View file

@ -17,6 +17,8 @@ from csiborgtools import clustering, field, fits, match, read # noqa
# Arguments to csiborgtools.read.Paths. # Arguments to csiborgtools.read.Paths.
paths_glamdring = {"srcdir": "/mnt/extraspace/hdesmond/", paths_glamdring = {"srcdir": "/mnt/extraspace/hdesmond/",
"postdir": "/mnt/extraspace/rstiskalek/CSiBORG/", "postdir": "/mnt/extraspace/rstiskalek/CSiBORG/",
"BORG_dir": "/mnt/extraspace/rstiskalek/BORG/",
"BORG_final_density": "/users/hdesmond/BORG_final/",
"quijote_dir": "/mnt/extraspace/rstiskalek/Quijote", "quijote_dir": "/mnt/extraspace/rstiskalek/Quijote",
} }

View file

@ -17,9 +17,10 @@ from warnings import warn
try: try:
import MAS_library as MASL # noqa import MAS_library as MASL # noqa
from .density import DensityField, PotentialField, VelocityField, TidalTensorField # noqa from .density import (DensityField, PotentialField, # noqa
TidalTensorField, VelocityField)
from .interp import (evaluate_cartesian, evaluate_sky, field2rsp, # noqa from .interp import (evaluate_cartesian, evaluate_sky, field2rsp, # noqa
make_sky, fill_outside) # noqa fill_outside, make_sky, observer_vobs)
from .utils import nside2radec, smoothen_field # noqa from .utils import nside2radec, smoothen_field # noqa
except ImportError: except ImportError:
warn("MAS_library not found, `DensityField` will not be available", UserWarning) # noqa warn("MAS_library not found, `DensityField` will not be available", UserWarning) # noqa

View file

@ -17,13 +17,13 @@ Density field and cross-correlation calculations.
""" """
from abc import ABC from abc import ABC
import numpy
import MAS_library as MASL import MAS_library as MASL
import numpy
from numba import jit from numba import jit
from tqdm import trange from tqdm import trange
from ..read.utils import real2redshift from ..read.utils import real2redshift
from .interp import divide_nonzero
from .utils import force_single_precision from .utils import force_single_precision
@ -249,7 +249,7 @@ class VelocityField(BaseField):
/ numpy.sqrt(px**2 + py**2 + pz**2)) / numpy.sqrt(px**2 + py**2 + pz**2))
return radvel return radvel
def __call__(self, parts, grid, mpart, flip_xz=True, nbatch=30, def __call__(self, parts, grid, flip_xz=True, nbatch=30,
verbose=True): verbose=True):
""" """
Calculate the velocity field using a Pylians routine [1, 2]. Calculate the velocity field using a Pylians routine [1, 2].
@ -263,8 +263,6 @@ class VelocityField(BaseField):
Columns are: `x`, `y`, `z`, `vx`, `vy`, `vz`, `M`. Columns are: `x`, `y`, `z`, `vx`, `vy`, `vz`, `M`.
grid : int grid : int
Grid size. Grid size.
mpart : float
Particle mass.
flip_xz : bool, optional flip_xz : bool, optional
Whether to flip the `x` and `z` coordinates. Whether to flip the `x` and `z` coordinates.
nbatch : int, optional nbatch : int, optional
@ -287,6 +285,7 @@ class VelocityField(BaseField):
rho_vely = numpy.zeros((grid, grid, grid), dtype=numpy.float32) rho_vely = numpy.zeros((grid, grid, grid), dtype=numpy.float32)
rho_velz = numpy.zeros((grid, grid, grid), dtype=numpy.float32) rho_velz = numpy.zeros((grid, grid, grid), dtype=numpy.float32)
rho_vel = [rho_velx, rho_vely, rho_velz] rho_vel = [rho_velx, rho_vely, rho_velz]
cellcounts = numpy.zeros((grid, grid, grid), dtype=numpy.float32)
nparts = parts.shape[0] nparts = parts.shape[0]
batch_size = nparts // nbatch batch_size = nparts // nbatch
@ -302,16 +301,22 @@ class VelocityField(BaseField):
if flip_xz: if flip_xz:
pos[:, [0, 2]] = pos[:, [2, 0]] pos[:, [0, 2]] = pos[:, [2, 0]]
vel[:, [0, 2]] = vel[:, [2, 0]] vel[:, [0, 2]] = vel[:, [2, 0]]
vel *= mass.reshape(-1, 1) / mpart vel *= mass.reshape(-1, 1)
for i in range(3): for i in range(3):
MASL.MA(pos, rho_vel[i], self.boxsize, self.MAS, W=vel[:, i], MASL.MA(pos, rho_vel[i], self.boxsize, self.MAS, W=vel[:, i],
verbose=False) verbose=False)
MASL.MA(pos, cellcounts, self.boxsize, self.MAS, W=mass,
verbose=False)
if end == nparts: if end == nparts:
break break
start = end start = end
return numpy.stack(rho_vel) for i in range(3):
divide_nonzero(rho_vel[i], cellcounts)
return numpy.stack([rho_velx, rho_vely, rho_velz])
############################################################################### ###############################################################################

View file

@ -24,10 +24,9 @@ from ..read.utils import radec_to_cartesian, real2redshift
from .utils import force_single_precision from .utils import force_single_precision
def evaluate_cartesian(*fields, pos): def evaluate_cartesian(*fields, pos, interp="CIC"):
""" """
Evaluate a scalar field at Cartesian coordinates using CIC Evaluate a scalar field at Cartesian coordinates.
interpolation.
Parameters Parameters
---------- ----------
@ -36,19 +35,29 @@ def evaluate_cartesian(*fields, pos):
pos : 2-dimensional array of shape `(n_samples, 3)` pos : 2-dimensional array of shape `(n_samples, 3)`
Positions to evaluate the density field. Assumed to be in box Positions to evaluate the density field. Assumed to be in box
units. units.
interp : str, optional
Interpolation method. Can be either `CIC` or `NGP`.
Returns Returns
------- -------
interp_fields : (list of) 1-dimensional array of shape `(n_samples,). interp_fields : (list of) 1-dimensional array of shape `(n_samples,).
""" """
assert interp in ["CIC", "NGP"]
boxsize = 1. boxsize = 1.
pos = force_single_precision(pos, "pos") pos = force_single_precision(pos, "pos")
nsamples = pos.shape[0] nsamples = pos.shape[0]
interp_fields = [numpy.full(nsamples, numpy.nan, dtype=numpy.float32) interp_fields = [numpy.full(nsamples, numpy.nan, dtype=numpy.float32)
for __ in range(len(fields))] for __ in range(len(fields))]
if interp == "CIC":
for i, field in enumerate(fields): for i, field in enumerate(fields):
MASL.CIC_interp(field, boxsize, pos, interp_fields[i]) MASL.CIC_interp(field, boxsize, pos, interp_fields[i])
else:
pos = numpy.floor(pos * fields[0].shape[0]).astype(numpy.int32)
for i, field in enumerate(fields):
for j in range(nsamples):
interp_fields[i][j] = field[pos[j, 0], pos[j, 1], pos[j, 2]]
if len(fields) == 1: if len(fields) == 1:
return interp_fields[0] return interp_fields[0]
@ -168,8 +177,30 @@ def divide_nonzero(field0, field1):
field0[i, j, k] /= field1[i, j, k] field0[i, j, k] /= field1[i, j, k]
def field2rsp(*fields, parts, box, nbatch=30, flip_partsxz=True, init_value=0., def observer_vobs(velocity_field):
verbose=True): """
Calculate the observer velocity from a velocity field. Assumes the observer
is in the centre of the box.
Parameters
----------
velocity_field : 4-dimensional array of shape `(3, grid, grid, grid)`
Velocity field to calculate the observer velocity from.
Returns
-------
vobs : 1-dimensional array of shape `(3,)`
Observer velocity in units of `velocity_field`.
"""
pos = numpy.asanyarray([0.5, 0.5, 0.5]).reshape(1, 3)
vobs = numpy.full(3, numpy.nan, dtype=numpy.float32)
for i in range(3):
vobs[i] = evaluate_cartesian(velocity_field[i, ...], pos=pos)[0]
return vobs
def field2rsp(*fields, parts, vobs, box, nbatch=30, flip_partsxz=True,
init_value=0., verbose=True):
""" """
Forward model real space scalar fields to redshift space. Attaches field Forward model real space scalar fields to redshift space. Attaches field
values to particles, those are then moved to redshift space and from their values to particles, those are then moved to redshift space and from their
@ -182,6 +213,8 @@ def field2rsp(*fields, parts, box, nbatch=30, flip_partsxz=True, init_value=0.,
parts : 2-dimensional array of shape `(n_parts, 6)` parts : 2-dimensional array of shape `(n_parts, 6)`
Particle positions and velocities in real space. Must be organised as Particle positions and velocities in real space. Must be organised as
`x, y, z, vx, vy, vz`. `x, y, z, vx, vy, vz`.
vobs : 1-dimensional array of shape `(3,)`
Observer velocity in units matching `parts`.
box : :py:class:`csiborgtools.read.CSiBORGBox` box : :py:class:`csiborgtools.read.CSiBORGBox`
The simulation box information and transformations. The simulation box information and transformations.
nbatch : int, optional nbatch : int, optional
@ -199,6 +232,7 @@ def field2rsp(*fields, parts, box, nbatch=30, flip_partsxz=True, init_value=0.,
------- -------
rsp_fields : (list of) 3-dimensional array of shape `(grid, grid, grid)` rsp_fields : (list of) 3-dimensional array of shape `(grid, grid, grid)`
""" """
raise NotImplementedError("Figure out what to do with Vobs.")
nfields = len(fields) nfields = len(fields)
# Check that all fields have the same shape. # Check that all fields have the same shape.
if nfields > 1: if nfields > 1:

View file

@ -31,16 +31,21 @@ class Paths:
Path to the folder where the RAMSES outputs are stored. Path to the folder where the RAMSES outputs are stored.
postdir: str, optional postdir: str, optional
Path to the folder where post-processed files are stored. Path to the folder where post-processed files are stored.
borg_dir : str, optional
Path to the folder where BORG MCMC chains are stored.
quiote_dir : str, optional quiote_dir : str, optional
Path to the folder where Quijote simulations are stored. Path to the folder where Quijote simulations are stored.
""" """
_srcdir = None _srcdir = None
_postdir = None _postdir = None
_borg_dir = None
_quijote_dir = None _quijote_dir = None
def __init__(self, srcdir=None, postdir=None, quijote_dir=None): def __init__(self, srcdir=None, postdir=None, borg_dir=None,
quijote_dir=None):
self.srcdir = srcdir self.srcdir = srcdir
self.postdir = postdir self.postdir = postdir
self.borg_dir = borg_dir
self.quijote_dir = quijote_dir self.quijote_dir = quijote_dir
@staticmethod @staticmethod
@ -68,6 +73,26 @@ class Paths:
self._check_directory(path) self._check_directory(path)
self._srcdir = path self._srcdir = path
@property
def borg_dir(self):
"""
Path to the folder where BORG MCMC chains are stored.
Returns
-------
path : str
"""
if self._borg_dir is None:
raise ValueError("`borg_dir` is not set!")
return self._borg_dir
@borg_dir.setter
def borg_dir(self, path):
if path is None:
return
self._check_directory(path)
self._borg_dir = path
@property @property
def quijote_dir(self): def quijote_dir(self):
""" """
@ -146,6 +171,21 @@ class Paths:
return nsim return nsim
return f"{str(nobs).zfill(2)}{str(nsim).zfill(3)}" return f"{str(nobs).zfill(2)}{str(nsim).zfill(3)}"
def borg_mcmc(self, nsim):
"""
Path to the BORG MCMC chain file.
Parameters
----------
nsim : int
IC realisation index.
Returns
-------
path : str
"""
return join(self.borg_dir, "mcmc", f"mcmc_{nsim}.h5")
def mmain(self, nsnap, nsim): def mmain(self, nsnap, nsim):
""" """
Path to the `mmain` CSiBORG files of summed substructure. Path to the `mmain` CSiBORG files of summed substructure.
@ -373,7 +413,7 @@ class Paths:
IC realisation index. IC realisation index.
in_rsp : bool in_rsp : bool
Whether the calculation is performed in redshift space. Whether the calculation is performed in redshift space.
smooth_scale : float smooth_scale : float, optional
Smoothing scale in :math:`\mathrm{Mpc}/h` Smoothing scale in :math:`\mathrm{Mpc}/h`
Returns Returns

View file

@ -61,54 +61,30 @@ def density_field(nsim, parser_args, to_save=True):
nsnap = max(paths.get_snapshots(nsim)) nsnap = max(paths.get_snapshots(nsim))
box = csiborgtools.read.CSiBORGBox(nsnap, nsim, paths) box = csiborgtools.read.CSiBORGBox(nsnap, nsim, paths)
parts = csiborgtools.read.read_h5(paths.particles(nsim))["particles"] parts = csiborgtools.read.read_h5(paths.particles(nsim))["particles"]
gen = csiborgtools.field.DensityField(box, parser_args.MAS) gen = csiborgtools.field.DensityField(box, parser_args.MAS)
if parser_args.kind == "density":
field = gen(parts, parser_args.grid, in_rsp=False,
verbose=parser_args.verbose)
if parser_args.in_rsp:
field = csiborgtools.field.field2rsp(*field, parts=parts, box=box,
verbose=parser_args.verbose)
else:
field = gen(parts, parser_args.grid, in_rsp=parser_args.in_rsp, field = gen(parts, parser_args.grid, in_rsp=parser_args.in_rsp,
verbose=parser_args.verbose) verbose=parser_args.verbose)
if parser_args.smooth_scale > 0:
field = csiborgtools.field.smoothen_field(
field, parser_args.smooth_scale, box.boxsize * box.h, threads=1)
if to_save: if to_save:
fout = paths.field("density", parser_args.MAS, parser_args.grid, fout = paths.field(parser_args.kind, parser_args.MAS, parser_args.grid,
nsim, parser_args.in_rsp) nsim, parser_args.in_rsp, parser_args.smooth_scale)
print(f"{datetime.now()}: saving output to `{fout}`.") print(f"{datetime.now()}: saving output to `{fout}`.")
numpy.save(fout, field) numpy.save(fout, field)
return field return field
def density_field_smoothed(nsim, parser_args, to_save=True):
"""
Calculate the smoothed density field in the CSiBORG simulation. The
unsmoothed density field must already be precomputed.
Parameters
----------
nsim : int
Simulation index.
parser_args : argparse.Namespace
Parsed arguments.
to_save : bool, optional
Whether to save the output to disk.
Returns
-------
smoothed_density : 3-dimensional array
"""
paths = csiborgtools.read.Paths(**csiborgtools.paths_glamdring)
nsnap = max(paths.get_snapshots(nsim))
box = csiborgtools.read.CSiBORGBox(nsnap, nsim, paths)
# Load the real space overdensity field
rho = numpy.load(paths.field("density", parser_args.MAS, parser_args.grid,
nsim, in_rsp=False))
rho = csiborgtools.field.smoothen_field(rho, parser_args.smooth_scale,
box.boxsize, threads=1)
if to_save:
fout = paths.field("density", parser_args.MAS, parser_args.grid,
nsim, parser_args.in_rsp, parser_args.smooth_scale)
print(f"{datetime.now()}: saving output to `{fout}`.")
numpy.save(fout, rho)
return rho
############################################################################### ###############################################################################
# Velocity field # # Velocity field #
############################################################################### ###############################################################################
@ -185,7 +161,7 @@ def potential_field(nsim, parser_args, to_save=True):
nsim, in_rsp=False)) nsim, in_rsp=False))
if parser_args.smooth_scale > 0: if parser_args.smooth_scale > 0:
rho = csiborgtools.field.smoothen_field(rho, parser_args.smooth_scale, rho = csiborgtools.field.smoothen_field(rho, parser_args.smooth_scale,
box.boxsize, threads=1) box.boxsize * box.h, threads=1)
rho = density_gen.overdensity_field(rho) rho = density_gen.overdensity_field(rho)
# Calculate the real space potentiel field # Calculate the real space potentiel field
gen = csiborgtools.field.PotentialField(box, parser_args.MAS) gen = csiborgtools.field.PotentialField(box, parser_args.MAS)
@ -281,7 +257,7 @@ def environment_field(nsim, parser_args, to_save=True):
nsim, in_rsp=False)) nsim, in_rsp=False))
if parser_args.smooth_scale > 0: if parser_args.smooth_scale > 0:
rho = csiborgtools.field.smoothen_field(rho, parser_args.smooth_scale, rho = csiborgtools.field.smoothen_field(rho, parser_args.smooth_scale,
box.boxsize, threads=1) box.boxsize * box.h, threads=1)
rho = density_gen.overdensity_field(rho) rho = density_gen.overdensity_field(rho)
# Calculate the real space tidal tensor field, delete overdensity. # Calculate the real space tidal tensor field, delete overdensity.
if parser_args.verbose: if parser_args.verbose:
@ -339,27 +315,27 @@ if __name__ == "__main__":
parser.add_argument("--nsims", type=int, nargs="+", default=None, parser.add_argument("--nsims", type=int, nargs="+", default=None,
help="IC realisations. `-1` for all simulations.") help="IC realisations. `-1` for all simulations.")
parser.add_argument("--kind", type=str, parser.add_argument("--kind", type=str,
choices=["density", "velocity", "radvel", "potential", choices=["density", "rspdensity", "velocity", "radvel",
"environment"], "potential", "environment"],
help="What derived field to calculate?") help="What derived field to calculate?")
parser.add_argument("--MAS", type=str, parser.add_argument("--MAS", type=str,
choices=["NGP", "CIC", "TSC", "PCS"]) choices=["NGP", "CIC", "TSC", "PCS"])
parser.add_argument("--grid", type=int, help="Grid resolution.") parser.add_argument("--grid", type=int, help="Grid resolution.")
parser.add_argument("--in_rsp", type=lambda x: bool(strtobool(x)), parser.add_argument("--in_rsp", type=lambda x: bool(strtobool(x)),
help="Calculate in RSP?") help="Calculate in RSP?")
parser.add_argument("--smooth_scale", type=float, default=0) parser.add_argument("--smooth_scale", type=float, default=0,
help="Smoothing scale in Mpc/h.")
parser.add_argument("--verbose", type=lambda x: bool(strtobool(x)), parser.add_argument("--verbose", type=lambda x: bool(strtobool(x)),
help="Verbosity flag for reading in particles.") help="Verbosity flag for reading in particles.")
parser.add_argument("--simname", type=str, default="csiborg",
help="Verbosity flag for reading in particles.")
parser_args = parser.parse_args() parser_args = parser.parse_args()
comm = MPI.COMM_WORLD comm = MPI.COMM_WORLD
paths = csiborgtools.read.Paths(**csiborgtools.paths_glamdring) paths = csiborgtools.read.Paths(**csiborgtools.paths_glamdring)
nsims = get_nsims(parser_args, paths) nsims = get_nsims(parser_args, paths)
def main(nsim): def main(nsim):
if parser_args.kind == "density": if parser_args.kind == "density" or parser_args.kind == "rspdensity":
if parser_args.smooth_scale > 0:
density_field_smoothed(nsim, parser_args)
else:
density_field(nsim, parser_args) density_field(nsim, parser_args)
elif parser_args.kind == "velocity": elif parser_args.kind == "velocity":
velocity_field(nsim, parser_args) velocity_field(nsim, parser_args)

View file

@ -19,6 +19,7 @@ from argparse import ArgumentParser
import matplotlib as mpl import matplotlib as mpl
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy import numpy
from h5py import File
import healpy import healpy
import scienceplots # noqa import scienceplots # noqa
@ -244,7 +245,8 @@ def load_field(kind, nsim, grid, MAS, in_rsp=False, smooth_scale=None):
def plot_projected_field(kind, nsim, grid, in_rsp, smooth_scale, MAS="PCS", def plot_projected_field(kind, nsim, grid, in_rsp, smooth_scale, MAS="PCS",
highres_only=True, slice_find=None, pdf=False): vel_component=0, highres_only=True, slice_find=None,
pdf=False):
r""" r"""
Plot the mean projected field, however can also plot a single slice. Plot the mean projected field, however can also plot a single slice.
@ -262,6 +264,8 @@ def plot_projected_field(kind, nsim, grid, in_rsp, smooth_scale, MAS="PCS",
Smoothing scale in :math:`\mathrm{Mpc} / h`. Smoothing scale in :math:`\mathrm{Mpc} / h`.
MAS : str, optional MAS : str, optional
Mass assignment scheme. Mass assignment scheme.
vel_component : int, optional
Which velocity field component to plot.
highres_only : bool, optional highres_only : bool, optional
Whether to only plot the high-resolution region. Whether to only plot the high-resolution region.
slice_find : float, optional slice_find : float, optional
@ -283,12 +287,16 @@ def plot_projected_field(kind, nsim, grid, in_rsp, smooth_scale, MAS="PCS",
smooth_scale=smooth_scale) smooth_scale=smooth_scale)
density_gen = csiborgtools.field.DensityField(box, MAS) density_gen = csiborgtools.field.DensityField(box, MAS)
field = density_gen.overdensity_field(field) + 1 field = density_gen.overdensity_field(field) + 1
elif kind == "borg_density":
field = File(paths.borg_mcmc(nsim), 'r')
field = field["scalars"]["BORG_final_density"][...]
else: else:
field = load_field(kind, nsim, grid, MAS=MAS, in_rsp=in_rsp, field = load_field(kind, nsim, grid, MAS=MAS, in_rsp=in_rsp,
smooth_scale=smooth_scale) smooth_scale=smooth_scale)
if kind == "velocity": if kind == "velocity":
field = field[0, ...] field = field[vel_component, ...]
field = box.box2vel(field)
if highres_only: if highres_only:
csiborgtools.field.fill_outside(field, numpy.nan, rmax=155.5, csiborgtools.field.fill_outside(field, numpy.nan, rmax=155.5,
@ -552,22 +560,23 @@ if __name__ == "__main__":
if False: if False:
plot_hmf(pdf=False) plot_hmf(pdf=False)
if True: if False:
kind = "overdensity" kind = "overdensity"
grid = 1024 grid = 1024
plot_sky_distribution(kind, 7444, grid, nside=64, plot_sky_distribution(kind, 7444, grid, nside=64,
plot_groups=False, dmin=45, dmax=60, plot_groups=False, dmin=45, dmax=60,
plot_halos=5e13, volume_weight=True) plot_halos=5e13, volume_weight=True)
if False: if True:
kind = "density" kind = "overdensity"
grid = 256 grid = 256
smooth_scale = 0 smooth_scale = 0
# plot_projected_field("overdensity", 7444, grid, in_rsp=True, # plot_projected_field("overdensity", 7444, grid, in_rsp=True,
# highres_only=False) # highres_only=False)
plot_projected_field(kind, 7444, grid, in_rsp=False, plot_projected_field(kind, 7444, grid, in_rsp=False,
smooth_scale=smooth_scale, slice_find=0.5, smooth_scale=smooth_scale, slice_find=0.5,
highres_only=False) MAS="PCS",
highres_only=True)
if False: if False:
paths = csiborgtools.read.Paths(**csiborgtools.paths_glamdring) paths = csiborgtools.read.Paths(**csiborgtools.paths_glamdring)