Per-Halo Physics Loop
This page is the hub for the SAGE26 physics walkthrough. It describes
what happens inside evolve_galaxies() – the function that advances every
galaxy in a halo from one snapshot to the next – and links out to the four
detailed module pages.
Source: src/core_build_model.c
In one paragraph
evolve_galaxies() takes a halo and the galaxy list inherited from its
progenitors, advances every galaxy through the snapshot interval, and writes
the result back. Internally the interval is split into a number of substeps
and the same physics block runs on every substep: gas infall, reincorporation,
satellite stripping, cooling, star formation and feedback, then mergers and
disruption. Two regimes (CGM vs hot halo) are classified once at the top of
the function and reused throughout.
Sub-stepping
Each snapshot interval is integrated in STEPS = 10 substeps by default
(defined in src/macros.h).
At high redshift the snapshot spacing can exceed the halo dynamical time
t_dyn = R_vir / V_vir, so evolve_galaxies() raises the substep count
adaptively:
effective_steps = clamp(ceil(STEPS * deltaT_total / t_dyn), STEPS, MAX_STEPS)
with MAX_STEPS = 30. The SFR history arrays (SFHMassDisk[STEPS],
SFHMassBulge[STEPS], etc.) remain sized to the fixed STEPS. Adaptive
substeps are mapped back into those bins via
step_bin = (step * STEPS) / effective_steps, so the output schema is
stable regardless of the integration cadence.
Order of operations
evolve_galaxies() runs a few setup steps once per snapshot, then enters
a substep loop that contains two inner per-galaxy loops.
Once per snapshot (before the substep loop):
# |
Step |
Where |
|---|---|---|
1 |
Halo concentration (if |
|
2 |
CGM regime classification (if |
|
3 |
FFB regime classification (if |
|
4 |
Compute the total infalling gas for the snapshot interval |
|
5 |
Choose |
inline in |
Inner per-galaxy loop (each substep, every galaxy):
# |
Step |
Where |
|---|---|---|
6 |
Central only: inject the per-substep share of infall into |
|
7 |
Central only: return ejected gas to the hot reservoir (if |
|
8 |
Type 1 satellite with |
|
9 |
Cool gas from the hot/CGM reservoir into |
|
10 |
Form stars and apply SN + AGN feedback (or FFB SF if flagged) |
|
Inner satellite/merger loop (each substep, after the per-galaxy loop):
# |
Step |
Where |
|---|---|---|
11 |
Decrement |
|
The per-snapshot infall total is divided by effective_steps and injected
as infallingGas / effective_steps per substep in step 6.
Detailed treatment of each physics step lives in its own page:
Steps 4-8: Infall, reincorporation, stripping
Step 9: Cooling and AGN heating
Step 10: Star formation and feedback
Step 11: Mergers and disruption
Post-step bookkeeping
After the substep loop completes, evolve_galaxies() does several
end-of-snapshot tasks:
Normalise
Cooling,Heating, andOutflowRateto per-unit-time by dividing the accumulated values bydeltaT.Sum
TotalSatelliteBaryonson the central from each remaining satellite.Re-attach the galaxy list to
haloaux[]for downstream output.Shift
mergeIntoIDto account for merged galaxies that will not be written out (output indices are a contiguous range, so every preceding merged galaxy bumps the index down by one).
The two-regime split as a code path map
The CGM model (CGMrecipeOn = 1) classifies every galaxy as Regime 0
(CGM / precipitation, below the Dekel & Birnboim 2006 M_shock) or
Regime 1 (hot halo, classical Croton+06). The classification controls
several decisions in the substep loop:
Step |
Regime 0 (CGM) |
Regime 1 (hot halo) |
|---|---|---|
Infall destination |
|
|
Cooling recipe |
|
|
Density profile |
|
isothermal |
AGN suppression mechanism |
|
|
Precipitation criterion |
|
none – isothermal |
When CGMrecipeOn = 0, determine_and_store_regime() is not called at
all (the Regime field is left unset) and the substep loop dispatches
cooling to cooling_recipe() -> cooling_recipe_hot() for every galaxy
regardless of mass, recovering the original SAGE behaviour.
Galaxy types
The substep loop branches on galaxies[p].Type:
Type |
Meaning |
Special handling |
|---|---|---|
0 |
Central of its FoF halo |
Receives infall, reincorporation, cooling, SF |
1 |
Satellite with its own dark matter subhalo |
Hot gas is stripped to the central; otherwise evolves like a central |
2 |
Orphan satellite (subhalo lost) |
No hot reservoir; only SF from existing cold gas; merger timer counts down |
Galaxies with mergeType > 0 are skipped in the loop – they have already
been absorbed by a merger event earlier in the snapshot.
Reading the source
The function is intentionally a flat, top-to-bottom dispatcher with no
hidden state. To trace any single galaxy through one snapshot, follow these
landmarks in core_build_model.c:
Pre-step classifications – lines around
evolve_galaxiesentry.The
for(int step = 0; step < effective_steps; step++)loop body.Inside it, the
for(int p = 0; p < ngal; p++)per-galaxy loop for cooling and SF, followed by the satellite/merger loop.After the substep loop, the per-galaxy normalisation block and the
haloaux[]linkage block.