macros.h#
#include <sif/core/macros.h>
-
SIF_CORE_MACROS_H#
Library-wide types, option flags, status codes and compiler hints.
Every translation unit in sif includes this header, directly or otherwise. It is organised in sections:
option flags the bit fields passed as sif_option
scalar type sif_real and its math wrappers
compiler hints attribute wrappers with neutral fallbacks
status codes SIF_OK and the SIF_ERR_* family
assertions debug-only invariant checks
atomics relaxed read-modify-write on a uint64_t
log levels
-
type sif_option#
Bit field of behaviour flags accepted by most entry points.
Flags from different families occupy different bit ranges and are combined with
|. Passing SIF_DEFAULT selects every family’s default, which is always the zero-valued member.
-
SIF_DEFAULT#
Every option at its default.
-
SIF_PBC_PERIODIC#
Whether the box wraps at its faces. @{
@}
Bits 12-15. Bits 8-11 are free. @{
-
SIF_FINDER_CONSUME_GRID#
Leave the grid holding the field smoothed at the last radius, instead of restoring the input.
A finder smooths the grid in place at every radius, so the input is gone by the time it finishes. By default it is transformed back at the end; this flag skips that, saving one inverse FFT for a caller that is going to discard the grid anyway.
-
SIF_FINDER_SEARCH_1_50#
How far past a rung the rescaling looks for the crossing, as a multiple of the rung: r_search = factor * radius.
This is the finder’s dominant cost and almost none of it is usually needed. The rescaling histograms every tracer in the shell [radius, r_search] – everything inside the rung is counted whole, cell by cell, and costs nothing – so the work goes as factor^3 - 1. At 2.0 that is 7 r^3 to place a crossing that, on a 1.5% ladder, is measured at a mean of 1.03 r.
What the reach actually has to be is set by the ladder, not by the void: a rung returns crossings in [radius, r_search], so consecutive rungs must overlap or sizes between them are reachable at no rung at all. Reaching to the previous, larger rung is exactly enough, and sif_finder_exodus() raises r_search to it when the factor here falls short – so a sparse ladder stays correct whatever is chosen, and a fine one stops paying for reach it has already covered. A void is found at the largest rung at or below its crossing radius either way; a lower factor only stops the rungs below that one from re-deriving an answer their elder already returned.
1.5 is the default because it is comfortably above any ladder anyone runs (a 50% step) while costing 2.4 r^3 against 7. Raise it for a coarse ladder, or to 2.0 to reproduce older catalogs bit for bit. @{
-
SIF_FINDER_SEARCH_1_25#
< Default.
-
static inline float sif__finder_search_factor(uint32_t opt)#
The multiple of the rung a #SIF__FINDER_SEARCH_MASK selection asks for. Order follows the encoding, not the value: the default has to be the zero-valued member.
@}
-
SIF_FINDER_KEEP_CIC_WINDOW#
Skip deconvolution of the CIC assignment window.
sif_grid_assign_cic() convolves the field with its own window before the finder ever sees it, so a top-hat applied on top of that smooths twice: the effective window is wider than the radius asked for, and it has soft edges about a cell thick. Both push the measured contrast in a void upward, which for the exodus finder shows up directly as rescaled radii biased large. Deconvolution is therefore the default; this flag disables it for grids that were not built by CIC – one filled by hand, or from a tessellation, or read from a file whose assignment is unknown.
Note
The correction grows towards the Nyquist corner, so it is only safe while the top-hat that follows suppresses those modes. The finders check the smallest radius against the cell size and warn when it does not.
@}
Bits 16-17, deliberately outside the 8-14 range the per-entry-point families reuse: a mesh is built from inside several of those calls, and a flag that collided with one of them would be read as the other’s. @{
-
SIF_MESH_DROP_INDICES#
Release sif_chain_mesh_t::original_indices once the mesh is in canonical order, instead of keeping it.
The map back to field order costs 8 bytes per particle – 25 GiB at 3.4e9 tracers – and the finders never read it. It is still built and still sorted on, so the mesh is byte-for-byte the one a default build produces; only the key is dropped afterwards. The cost is that sif_chain_mesh_find_nearest_open() and sif_chain_mesh_find_nearest_pbc() can no longer name their answer and refuse.
-
SIF_MESH_NO_CANONICAL#
Skip the canonical ordering pass, leaving each cell in whatever order the parallel scatter produced.
The pass exists so that two identical runs give byte-identical meshes: the scatter claims slots with an atomic, so which chunk reaches a cell first is a race, and everything that walks a cell inherits it. Sorting each cell on the field index replaces that with an order fixed by the input alone.
What that buys is reproducibility of the last bits, and nothing else – the set of particles in a cell is identical either way, so any measurement that does not depend on summation order is unaffected. A finder that only counts tracers inside a sphere is in that class; stacked profiles, anything that sums weights (the exodus finder included, on a weighted mesh) and nearest-neighbour queries with exact distance ties are not.
Worth setting only where the ordering genuinely does not matter, because the pass is no longer the bottleneck it once was: it sorts a compact key array and applies the permutation once, rather than swapping whole payload rows per inversion.
@}
-
SIF_PROFILES_CUMULATIVE#
What each bin of a density profile holds.
CUMULATIVE is the contrast enclosed within a bin’s outer edge, which is what the spherical-evolution mapping takes. DIFFERENTIAL is the contrast of that shell alone, which is the one worth measuring from a tessellation: a shell is all boundary, so it is where resolving a cell’s overlap with the sphere rather than counting whole tracers makes the most difference.
Velocity profiles are differential either way; a mean infall over everything inside a radius is not a quantity anyone wants. @{
@}
-
SIF_PROFILES_VELOCITY_NUMBER#
What a shell’s mean radial velocity is averaged over.
NUMBER is the plain mean over the tracers in the shell. WEIGHTED is the mean over their weights, sum(w v) / sum(w), which for a mass-weighted field is the momentum of the shell over its mass. On a mesh without weights the two are the same thing.
NUMBER is the default because of what it means on a tessellation’s samples: every sample stands for the same volume, so counting them gives the volume-weighted velocity, which is what the samples are for. WEIGHTED on the same mesh gives the tracer-weighted one instead. @{
@}
-
SIF_SPHERICAL_B94#
How the linear and non-linear density contrasts of a void are mapped onto each other.
B94 is the Bernardeau (1994) fit, closed form and accurate to 0.2%; EXACT root-finds the Einstein-de Sitter expansion solution.
Note
Bit 9 is free. Bits 8-9 used to select between a histogram, a KDE and an auto-binned histogram; the VSF is now always a plain histogram, and bit 8 has since been taken by this mapping. @{
@}
-
SIF_VSF_BIN_LN#
Spacing of the radius bins. @{
@}
-
SIF_VSF_MERGE_MEAN#
How several size functions are merged into one. @{
@}
-
SIF_DELTA_SHUFFLE_NONE#
How the surrogate field for a PDF comparison is generated.
PHASES keeps every
|delta_k|and randomizes only the phase, so the realized P(k) is bit-for-bit the input’s and any change in the PDF is attributable to phase information alone. GAUSSIAN additionally resamples the amplitudes from the Rayleigh distribution implied by|delta_k|, which is the correct surrogate when the comparison is against a Gaussian random field ensemble rather than against this one realization. @{
@}
-
SIF_DELTA_KEEP_CIC_WINDOW#
Skip deconvolution of the CIC assignment window.
The CIC assignment in sif_grid_assign_cic convolves the field with its own window, which has to come back out before the top-hat is applied or the field is smoothed twice. Deconvolution is therefore the default; this flag disables it for grids that were not built by CIC.
-
SIF_DELTA_FILTER_TOP_HAT#
Window applied to both the PDF and the spectral moments.
They have to share one window: gamma and R_star describe the field whose PDF is being measured, and mixing a top-hat histogram with Gaussian-window moments would describe two different fields.
The top-hat is the natural window for counts in spheres, but its W^2 decays only as k^-4, so the k^4-weighted sigma_2 sum does not converge and is cut off by the grid instead of by the field. Use the Gaussian whenever sigma_2 has to carry meaning. @{
@}
-
SIF_BBKS_G_FITTED#
Which G(gamma, w) to evaluate.
FITTED is the BBKS analytic approximation, fast and closed-form but calibrated for a limited gamma band. EXACT quadratures the defining integral, which costs a few hundred evaluations of the curvature weight per point and is correct everywhere. @{
@}
The library’s floating-point type: double if SIF_USE_DOUBLE is defined at configure time, float otherwise.
Use sif_real for every physical quantity, and the SIF_REAL_* wrappers below rather than the libm functions directly: calling cos() on a float build promotes to double and back on every evaluation, and calling cosf() on a double build silently discards precision.
printf conversion that round-trips a sif_real exactly: 17 significant digits are enough to recover any double, 9 any float.
-
type sif_real#
scanf conversion matching sif_real. Getting this wrong is silent: scanf writes through a pointer whose type it cannot check.
-
SIF_PI#
Pi at sif_real precision.
Defined here rather than taken from M_PI, which is not in C99 and whose fallbacks are easy to get wrong: a float literal used in a double build silently costs eight digits.
-
SIF_CACHE_LINE#
Assumed cache-line size, in bytes. Override at configure time.
Must be a power of two and at least
sizeof(void*).
-
SIF_PRAGMA_HELPER(x)#
Emits a _Pragma from a macro argument.
-
SIF_NODISCARD#
Warn if the return value is discarded.
-
SIF_PURE_FUNCTION#
No side effects; result depends only on the arguments and memory.
-
SIF_HOT_LOOP#
Hint that a function sits on a hot path.
-
SIF_ALWAYS_INLINE#
- Inline even where the compiler would rather not: for a body that
takes a compile-time flag and has to be specialized on it at every call.
-
SIF_ALIGN_T#
Align an object on a cache line.
-
SIF_POPCOUNT_U64(x)#
Population count and trailing-zero count, with portable fallbacks.
Wrapped here rather than called directly, for the same reason as everything else in this section: a raw
__builtin_in library code compiles nowhere but GCC and Clang, and the failure is a build error in a file that has nothing to do with portability. The fallbacks are the textbook loops – slower, but only reached on a compiler that has no intrinsic to offer. @{
Set bits in a 64-bit word. Undefined for no compiler at all.
Trailing zeros in a 32-bit word. Undefined for x == 0, matching __builtin_ctz.
@}
-
SIF_OK#
Returned by functions that can fail for a distinguishable reason.
Such functions return an int: SIF_OK on success, a negative SIF_ERR_* otherwise. Allocators keep returning a pointer, NULL on failure. @{
-
SIF_ERR_IO#
A file could not be opened, read, written, or failed validation.
-
SIF_ERR_UNSUPPORTED#
This build cannot do it: the feature was left out at configure time (an HDF5 read in a build without HDF5, for one). The log says which option to rebuild with.
@}
-
SIF_ASSERT(cond)#
Debug-only invariant check. Compiles to nothing by default.
Enabled by configuring with -DSIF_DEBUG_CHECKS. Deliberately NOT keyed off NDEBUG: the release preset never defines it, and these guards sit in the hottest loops in the library.
Warning
The condition is not evaluated in a normal build. It must never carry a side effect.
-
SIF_ATOMIC_LOAD_U64(p)#
Read-modify-write on a uint64_t word, relaxed ordering.
Relaxed is sufficient wherever these are used: the words are bitmask lanes whose bits are set independently, so only atomicity of the individual update matters, not its order relative to any other memory operation.
SIF_HAS_ATOMIC_BUILTINS is 0 where the compiler provides no such builtins, and callers must serialize the update themselves. @{
@}
-
SIF_LOG_LEVEL_TRACE#
Verbosity threshold, set through sif_config_t::log_level. @{
@}