profiles.h#
#include <sif/measure/profiles.h>
-
SIF_MEASURE_PROFILES_H#
Stacked radial profiles of voids: density and radial velocity.
The estimator fills one row per void, binned in radius scaled by that void’s own radius, so profiles of different-sized voids are directly stackable.
The containers are only obtainable from the estimator: they mean nothing until it has filled them, so there is no public allocator, only the frees.
-
struct sif_density_profiles_t#
Stacked radial density profiles, one row per void.
-
bool differential#
True when a bin holds the contrast of that shell alone, false when it holds the contrast enclosed within the bin’s outer edge. Set by #SIF_PROFILES_DIFFERENTIAL, and recorded because the two are not distinguishable from the values.
-
bool differential#
-
static inline sif_real sif_density_profiles_bin_radius(const sif_density_profiles_t *profs, uint32_t bin)#
Radius a density bin’s value belongs at, in units of the void radius.
Not the same edge for the two binnings, and plotting one at the other’s radius shifts the whole curve by half a bin: a cumulative bin holds everything enclosed within its outer edge, while a differential bin is a shell mean and belongs at its centre. The difference is easy to miss because both are smooth and only the position of a feature moves – a void found at a fixed enclosed contrast puts that contrast exactly at r = R_v, and half a bin is enough to hide it.
- Parameters:
profs – The profile set.
bin – Bin to place, in [0, n_bins).
- Returns:
The radius, in units of each void’s own radius; multiply by a void’s radius for physical units.
-
struct sif_velocity_profiles_t#
Stacked radial velocity profiles, one row per void.
-
void sif_density_profiles_free(sif_density_profiles_t *profs)#
Release a density profile set.
- Parameters:
profs – Set to free. NULL is accepted and ignored.
-
void sif_velocity_profiles_free(sif_velocity_profiles_t *profs)#
Release a velocity profile set.
- Parameters:
profs – Set to free. NULL is accepted and ignored.
-
static inline const sif_real *sif_density_profiles_get(const sif_density_profiles_t *profs, uint64_t void_idx)#
One void’s density profile.
- Parameters:
profs – The profile set.
void_idx – Void to read, indexing the catalogue the set was built from.
- Returns:
Borrowed pointer to that void’s n_bins values.
-
static inline const sif_real *sif_velocity_profiles_get(const sif_velocity_profiles_t *profs, uint64_t void_idx)#
One void’s radial velocity profile.
- Parameters:
profs – The profile set.
void_idx – Void to read, indexing the catalogue the set was built from.
- Returns:
Borrowed pointer to that void’s n_bins values.
-
SIF_PROFILES_DEFAULT_EXT#
How far a profile reaches when the caller does not say, in units of each void’s own radius.
-
int sif_profiles(const sif_catalog_t *cat, const sif_chain_mesh_t *mesh, sif_real ext, uint32_t n_bins, sif_option opt, sif_density_profiles_t **out_dens, sif_velocity_profiles_t **out_vel)#
Stack radial profiles by binning the mesh’s tracers around each void.
Densities come out normalized to the box mean, so a profile approaches 1 far from the void centre.
Either output may be omitted, and only what is asked for is computed – velocities in particular are only available from a mesh that carries them.
Densities are cumulative by default – each bin is the contrast enclosed within its outer edge, which is what the spherical-evolution mapping expects – and #SIF_PROFILES_DIFFERENTIAL makes each bin the contrast of its own shell instead. Velocities are the mean radial velocity of a shell either way: over the tracers by default, or over their weights with #SIF_PROFILES_VELOCITY_WEIGHTED.
Which edge a bin’s value belongs at follows from that, and differs between the two: see sif_density_profiles_bin_radius(), which is what anything plotting or fitting these should ask.
A void with a non-positive radius is skipped and leaves a row of zeros; there is no profile to measure around it.
- Parameters:
cat – Voids to profile.
mesh – Tracers to bin, and the box they live in: the mesh is where the box length, the tracer count and the mean density all come from, so it has to hold every tracer of the sample rather than a subset. A mesh of a tessellation’s samples is equally valid and is what makes the result volume-weighted rather than tracer-weighted – see sif_chain_mesh_alloc_tessellation(), which needs no change here because the samples carry the tracer weight between them. Built with sif_profiles_suggest_mesh_cells() unless the caller has a mesh already – one built for a finder does just as well, and reusing it is the point of taking a mesh here rather than a field. #SIF_MESH_DROP_INDICES is fine: this estimator walks cells and never names a tracer in field order.
ext – Outer edge of the profile, in units of each void’s radius. Anything not positive selects #SIF_PROFILES_DEFAULT_EXT.
n_bins – Radial bins per profile. Must be non-zero.
opt – Honours SIF_PBC_PERIODIC / SIF_PBC_OPEN, SIF_PROFILES_CUMULATIVE / SIF_PROFILES_DIFFERENTIAL and SIF_PROFILES_VELOCITY_NUMBER / SIF_PROFILES_VELOCITY_WEIGHTED. Densities are weighted whenever the mesh carries weights; there is no flag for that, since a density that ignored them would not be the field’s.
out_dens – Address of a density set pointer, or NULL to skip. If it points at NULL a set is allocated; otherwise the existing one is filled.
out_vel – Address of a velocity set pointer, or NULL to skip. Same convention.
- Returns:
SIF_OK, SIF_ERR_INVALID for a bad argument or a request for velocities from a mesh that has none, or SIF_ERR_ALLOC.
Note
On any failure both outputs are left NULL, including a set this call allocated before a later step failed. A caller may therefore check either the status or the pointers.
-
SIF_PROFILES_MESH_PARTICLES_PER_CELL#
Tracers per mesh cell that sif_profiles() runs fastest at.
The walk pays per cell it visits and per tracer it reads, and the two pull opposite ways: coarser cells overshoot the sphere and read tracers that were never going to be inside it, finer ones spend the saving on cell overhead – which is mostly the cache miss on the cell table, not arithmetic. The balance was measured across a factor of eight in tracer count and six in void radius, and it sits here in every one of them; notably it does not move with the void size, which is why this rule does not ask about it.
Close enough to #SIF_FINDER_MESH_PARTICLES_PER_CELL that one mesh serves both calls: either constant lands the other within a percent of its own minimum.
-
SIF_PROFILES_MESH_MAX_CELLS#
Cap on the suggested resolution. The mesh’s cell_offsets array alone is 8 * n_cells^3 bytes, which is already ~130 MiB here.
-
static inline uint32_t sif_profiles_suggest_mesh_cells(uint64_t n_particles)#
Suggested chain-mesh resolution for sif_profiles().
Resolution changes only speed and memory – the profiles are identical at any of them – so it is safe to tune, and a mesh built for something else is always a valid input. It is worth tuning: the rule this replaces, one cell per search radius, measured 2.5x slower than the minimum on an ordinary catalogue and 5x on one of large voids.
Unlike sif_finder_suggest_mesh_cells() there is no floor for the search sphere having to fit inside the mesh. A profile sphere wider than the box is capped at one full row of cells inside sif_profiles() instead, so a mesh too coarse to hold it is slow, never wrong.
The minimum is broad – a factor of two either way costs on the order of 10% – so being approximate here is the point rather than a shortcoming.
- Parameters:
n_particles – Tracers the mesh will hold.
- Returns:
n_cells to hand to sif_chain_mesh_alloc(), never zero.