Structure Finding API Reference

Docstrings for finding structures in loaded data — clumps, and expanding bubbles. The narrative guides are Clump Finding and Clump Finding — Synthetic Example, which scores the finder against known ground truth.

This is distinct from getclumps, which reads a clump catalogue RAMSES already wrote. The functions here find structure in data you have loaded.

Clumps

Mera.clumpfindFunction
clumpfind(obj::HydroPartType, finder::AbstractFinder; pos_unit=:kpc, mass_unit=:Msol,
          min_members=1, mask=[false], boundedness=false, bound_only=false,
          egrav=:approx, direct_max=2000, softening=0.0, iterative_unbinding=false,
          deblend=false, peak_min_distance=…, substructure=false, sub_min_members=min_members,
          tidal=false, gravity=nothing, tidal_sample=3.0, hierarchy=false,
          max_threads=Threads.nthreads()) -> ClumpCatalog

3D structure finder driven by any AbstractFinder finder value (one of the seven: ThresholdFoF, DensityWatershed, Dendrogram, GraphSegFinder, HDBSCANFinder, PhaseSpaceFoF, PersistenceFinder; it carries the field/threshold/linking-length and selects the algorithm). Per clump it returns member count, mass, centre of mass com, peak field value and peak_pos, and radius (max member distance from the COM) — positions in pos_unit, mass in mass_unit.

  • boundedness=true adds per-clump energetics (cgs): e_kin (COM-frame kinetic), e_therm (thermal, gas), e_grav (binding energy), alpha_vir = 2·e_kin/|e_grav|, and a bound flag (e_kin + e_therm < |e_grav|). bound_only=true keeps only self-bound clumps. The potential is set by egrav: :approx3/5·GM²/R (biased, fast); :direct ⇒ exact pairwise sum up to direct_max members; :tree ⇒ Barnes–Hut octree, O(N log N), accurate at any N (Barnes & Hut 1986). softening (in pos_unit) softens the kernel 1/√(r²+ε²).
  • iterative_unbinding=true runs SUBFIND-style unbinding (Springel+2001): members with positive total energy in the bulk-velocity frame are stripped iteratively until convergence, so each clump's reported membership/mass is its self-bound subset. Implies the boundedness analysis.
  • deblend=true/:peak splits merged clumps at their density peaks (members assigned to the nearest peak); deblend=:watershed instead assigns by density-descending basins. Peaks are separated by peak_min_distance (in pos_unit). (Equivalent to using a DensityWatershed finder.)
  • substructure=true builds a bound-substructure tree: each top-level clump is split into density basins (watershed) and the gravitationally self-bound ones (≥ sub_min_members) are attached as nested subclumps (with n_subclumps). Implies the boundedness analysis.
  • tidal (needs substructure=true) truncates each sub-clump at its tidal/Hill radius in the host: tidal=true uses the analytic Jacobi radius, tidal=:tensor the least-squares tidal-tensor radius from a gravity object (getgravity); tidal_sample scales the host sampling region.
  • hierarchy=true (for a Dendrogram finder) also returns the multi-scale merger tree in cat.tree (a StructureTree).
  • max_threads caps the threads used for the per-clump analysis (deterministic regardless of count).
  • validators — a composable chain of value-typed acceptance criteria (MinMembers, Bound, VirialBelow, MassAbove, Custom) that a clump must all satisfy (an AND). It is a clearer alternative to the boundedness kwargs: a Bound in the chain configures the boundedness pass (potential, iterative unbinding) and keeps only self-bound clumps; the predicate validators filter the catalog. A non-empty validators overrides boundedness/bound_only/min_members/egrav/iterative_unbinding. Membership-mutating validators run during the analysis, predicates after — independent of the order listed.
gas = gethydro(getinfo(output, path))
cat = clumpfind(gas, ThresholdFoF(:rho; threshold=1e2, threshold_unit=:nH, linking_length=0.2))
# contrast-controlled watershed + tree-gravity boundedness with iterative unbinding:
cores = clumpfind(gas, DensityWatershed(:rho; threshold=1e2, threshold_unit=:nH,
                                        linking_length=0.4, persistence=0.3);
                  boundedness=true, egrav=:tree, iterative_unbinding=true)
# the same, written as a validator chain, plus virial + size cuts:
cores = clumpfind(gas, DensityWatershed(:rho; threshold=1e2, threshold_unit=:nH, linking_length=0.4);
                  validators=[MinMembers(20), Bound(:tree; iterative=true), VirialBelow(2.0)])
clumpfind(obj::HydroPartType, field=:rho; threshold, linking_length,
          threshold_unit=:standard, pos_unit=:kpc, mass_unit=:Msol,
          min_members=1, mask=[false], boundedness=false, bound_only=false,
          egrav=:approx, direct_max=2000, deblend=false, peak_min_distance=2·linking_length,
          substructure=false, sub_min_members=min_members) -> ClumpCatalog

Convenience form of the AbstractFinder method: builds a ThresholdFoF from field/threshold/linking_length and forwards every other keyword. Existing scripts keep working unchanged; see the finder method above for the full keyword reference.

gas = gethydro(getinfo(output, path))
cat = clumpfind(gas, :rho; threshold=1e2, threshold_unit=:nH, linking_length=0.2)   # 0.2 kpc
bound = clumpfind(gas, :rho; threshold=1e2, threshold_unit=:nH, linking_length=0.2,
                  boundedness=true, bound_only=true, deblend=true)
clumpfind(components::AbstractVector; linking_length, pos_unit=:kpc, mass_unit=:Msol,
          min_members=1, boundedness=false, bound_only=false, egrav=:approx,
          direct_max=2000, softening=0.0, iterative_unbinding=false) -> ClumpCatalog

Multi-field structure finder: pre-select points from several components and link them with a single friends-of-friends pass, so over-densities in gas + stars + dark matter are found together. Each component is a NamedTuple (obj, field, threshold, name [, threshold_unit, mask]); its points with field ≥ threshold (and optional mask(obj)) join the common cloud tagged by name. Per clump the catalog reports total mass, com, radius, member count, and a components breakdown (name=(mass=…, n=…), …) per source.

boundedness=true adds the combined-cloud energetics (e_kin, e_therm, e_mag, e_grav, alpha_vir, bound) computed over all species together (each contributing its own mass and velocity; gas also its thermal/magnetic support), so the bound test uses the full self-gravity of gas + stars + DM while the components breakdown remains the per-species mass budget. egrav, direct_max, softening, iterative_unbinding and bound_only behave as in the single-object form.

cat = clumpfind([
    (obj=gas,   field=:rho,  threshold=1e2, threshold_unit=:nH, name=:gas),
    (obj=parts, field=:mass, threshold=0.0, name=:stars, mask = o->getvar(o,:birth).>0),
    (obj=parts, field=:mass, threshold=0.0, name=:dm,    mask = o->getvar(o,:birth).<=0),
]; linking_length=0.5, boundedness=true)
cat[1].components.gas.mass        # gas mass in the most massive structure
cat[1].bound                      # self-bound across all three species?
clumpfind(map::DataMapsType, field; threshold, connectivity=8, min_pixels=1) -> ClumpCatalog

2D connected-component finder on a projection map. Pixels with map[field] ≥ threshold are grouped by connectivity (4 or 8). Per region it returns pixel count n_members, mass (area-integral Σ value · pixel_area, exact for a surface-density map), com (value-weighted centroid), peak & peak_pos, and radius — positions in the map's extent units.

Mera.ClumpCatalogType
ClumpCatalog

Result of clumpfind. clumps is a vector of per-clump NamedTuples (sorted most-massive first); meta records the search parameters; tree is the StructureTree hierarchy (only when built via hierarchy=true, else nothing). Index/iterate it like a vector (cat[1], length(cat), for c in cat).

Mera.massfunctionplotFunction
massfunctionplot(cat::ClumpCatalog; cumulative=false, nbins=20, kwargs...) -> Makie.Figure

Plot the clump mass function from a ClumpCatalog (via clump_massfunction): the differential dN per mass bin, or the cumulative N(≥M) when cumulative=true, on log axes. Needs a Makie backend loaded (using CairoMakie).

using CairoMakie
fig = massfunctionplot(cat; cumulative=true)

massfunctionplot needs a Makie backend loaded (Pkg.add("CairoMakie")).

Validators

A candidate clump is kept only if it passes every validator — they combine as an AND, and membership tests are applied during the analysis while predicates are applied after, regardless of the order you list them in.

Mera.AbstractValidatorType

Abstract supertype of the criteria a candidate clump must satisfy to be kept.

Validators passed to clumpfind combine as an AND: a clump survives only if it passes every one. Concrete subtypes include MinMembers, and membership tests such as unbinding are applied during the analysis while predicates are applied after — that order is enforced regardless of the order you list them in.

Mera.MinMembersType
MinMembers(n)

Keep only clumps with at least n members.

Neighbour indices

clumpfind picks one of these automatically from the point count and density; you rarely name one directly.

Mera.AbstractNeighborIndexType

Abstract supertype of the spatial indices used to find neighbours during clump finding.

Concrete subtypes are HashGrid and CellLinkedList; clumpfind picks one automatically from the particle count and density. Both index an arbitrary ids subset of the coordinate arrays, so the same code serves the FoF pass and the per-group watershed.

Mera.HashGridType

Neighbour index storing occupied cells in a Dict, so memory scales with the number of occupied cells rather than the bounding box.

The choice for sparse or widely spread point sets. See CellLinkedList for the dense counterpart and AbstractNeighborIndex for how one is selected.

HashGrid <: AbstractNeighborIndex

Mera.CellLinkedListType

Neighbour index storing cell occupancy in flat arrays covering the whole bounding box, which avoids hashing at the cost of memory proportional to that box.

The choice for dense, compact point sets. See HashGrid for the sparse counterpart and AbstractNeighborIndex for how one is selected.

CellLinkedList <: AbstractNeighborIndex

Testing without data

synthetic_clumps builds real Mera objects in memory with known clump positions, so the finder can be scored rather than eyeballed — and it needs no simulation output.

Mera.synthetic_clumpsFunction
synthetic_clumps(; seed=1, lmax=7, background=:floor, noise=0.0,
                   disk_amp=14.0, disk_hr=0.22, disk_hz=0.10) -> NamedTuple

Build a reproducible, data-free 3-D Mera test bench with a known clump population — no simulation files required. Returns (; gas, particles, truth, info, true_label):

  • gas — a HydroDataType: eight Gaussian density clumps spread through the 2^lmax³ volume (box = 1 kpc), with per-cell velocities and pressure.
  • particles — a PartDataType: one particle bag per clump plus a two-component kinematic stream (for PhaseSpaceFoF).
  • truth — the ground-truth catalog (id, name, kind, pos, mass, width, vsig, bound).
  • true_labeltrue_label(x,y,z)::Int, the dominant clump id at a position (0 = background).

The data and all finders are fully three-dimensional; the clumps sit at different z, including a touching pair and a kinematically-hot unbound clump. The same eight clumps can be embedded in different environments to test how well a finder separates them from the floor:

  • background=:floor (default) — a flat low background; clumps are isolated islands.
  • background=:galaxy — clumps embedded in a smooth exponential ISM disk (disk_amp, radial/vertical scales disk_hr/disk_hz) whose inner region is itself elevated, so a fixed low threshold captures the diffuse disk as a spurious structure.
  • noise>0 — multiplicative log-normal per-cell fluctuations (turbulent ISM); noise is the dispersion of ln ρ (e.g. 0.2).

Either non-default option fills the whole grid (use lmax=6 to keep it fast). The clump ground truth is unchanged — the background is labelled 0, so a finder that absorbs the floor into its clumps is penalised by clump_recovery.

F = synthetic_clumps()
cat = clumpfind(F.gas, ThresholdFoF(:rho; threshold=5.0, linking_length=2.0/2^7))

See also save_synthetic_clumps, load_synthetic_clumps, and the "Clump Finding — Synthetic Example" documentation page.


Every docstring in the package is also on the Complete API Reference.