AGB dust is about zero because of **two bugs in how the simulation loads the AGB condensation efficiencies** from `GrainData_extrap.hdf5`. Both are in `arepo/src/dust_live/dust_init.c`, and both trace back to code from 2017 (commit a229c8dfd5, Ryan McKinnon). Because of them, AGB stars in these runs return gas and metals normally but almost none of it becomes dust. ## How it's supposed to work - **The table:** `GrainData_extrap.hdf5` stores a separate AGB condensation efficiency δ for each **metallicity row** (`/CondEffAGB/Z0` … `Z3`, i.e. Z = 10⁻⁴, 0.004, 0.008, 0.02), each **element** (C, O, Mg, Si, Fe) and each **stellar mass** (`/CondEffAGB/Masses` = 1, 1.5, …, 8 M☉). - **The loading:** the code reads each one, interpolates it onto its 200-point log-mass grid, and multiplies the AGB ejecta by it. - **The intent:** for example, a 3 M☉ star at Z = 0.004 should turn about **86% of its ejected carbon** into dust. It's a carbon star. ## Bug 1: the wrong dataset is read (`dust_init.c:485`) ```c for(int i = 0; i < GSD.AGB_CondEff_NZ; i++) // i = metallicity row for(int j = 0; j < GFM_N_CHEM_ELEMENTS; j++) // j = element { sprintf(setname, "/CondEffAGB/Z%d/%s/CondEff", i, ElementNames[i]); // <-- should be ElementNames[j] ... my_H5Dread(..., GSD.AGB_CondEff[i][j], ...); } ``` The element name in the path comes from the **row** index `i` rather than the element index `j`. The code's element order is H, He, C, N, …, so every element in a row gets the same table: | Row | Metallicity | Every element actually gets | Values | | --- | ----------- | --------------------------- | -------- | | Z0 | 10⁻⁴ | Hydrogen's table | all 0 | | Z1 | 0.004 | Helium's table | all 0 | | Z2 | 0.008 | Carbon's table | non-zero | | Z3 | 0.02 | Nitrogen's table | all 0 | Later the code turns zeros into log δ = −10, so "0" becomes **δ = 10⁻¹⁰**. ## Bug 2: masses treated as logs (`spline_interpolate_condeff`, `dust_init.c:355–392`) ```c mass[k] = GSD.AGB_CondEff_Mass[k]; /* already in log units */ ``` The comment assumes `/CondEffAGB/Masses` holds log₁₀ masses, but the file stores **linear** masses (1, 1.5, … 8). The code then compares them with log₁₀ m of its grid, which is at most 0.78 for every AGB star (m < 6 M☉). Every AGB star therefore lies "below" the first table entry and gets that **first entry** (the 1 M☉ value). ## The combined effect, by star metallicity | Star Z | AGB δ actually used | Example | | ---------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | < 0.004 | 10⁻¹⁰ (H and He tables) | star 0 (Z = 0.0015): 3.7×10⁻¹² M☉ of dust per 3 M☉ star, against 2.5×10⁻² intended | | 0.004–0.02 | Carbon's 1 M☉ entry, **0.00376, applied to C, O, Mg, Si and Fe alike**, blended by dz with a zero row | star 3188 (Z = 0.0044): 2.2×10⁻⁵ M☉, against 3.4×10⁻² intended | | > 0.02 | 10⁻¹⁰ (N table) | — | So AGB dust is 10³–10¹⁰ times lower than intended. In gal22 at z = 8.24 it comes to 0.67 M☉, against 1.06×10⁷ M☉ from SNII. Run as intended, AGB stars would add about 8×10⁻⁴ M☉ of dust per M☉ formed for old populations, comparable to a quarter of the SNII contribution. ## How I know this is what the simulation did - **The run's own log matches the buggy version.** Recomputing dust production for all 86,214 stars in gal0's snapshot 026 with the bugs reproduces `output/dust.txt` to **0.02%**. With the tables as intended it would be 0.2% higher at that young age, and the gap grows as populations age. - **The file contents line up exactly.** `GrainData_extrap.hdf5` really does have all-zero Hydrogen, Helium and Nitrogen tables, and its `Masses` dataset is linear: `[1, 1.5, … 8]`. ## What's *not* affected - **AGB gas and metal return** use the yield tables directly, so they're correct. That's why AGB stars show up in the returned-mass panel (3.7×10⁷ M☉ in gal22). - **SNII and SN Ia efficiencies** are read from `/CondEffSNII` correctly. ## The fix (two lines; for desika, since this code tree is hers) ```c sprintf(setname, "/CondEffAGB/Z%d/%s/CondEff", i, ElementNames[j]); // dust_init.c:485 — j, not i ... mass[k] = log10(GSD.AGB_CondEff_Mass[k]); // spline_interpolate_condeff — table is linear ``` **Implications:** - Every run built from this code tree has effectively no AGB dust. That matters little at z ≳ 8, where AGB stars haven't had time to contribute much. It matters more by z ≈ 4, where AGB dust should be a noticeable share of the stellar dust budget. - The paper's Eq. 9 calculation presumably used the tables as written, so its "production-only" ceiling includes AGB dust the simulations never made. This is worth reporting to desika before anyone relies on the AGB dust budget. In my scripts, `--agb-mode as_run` (the default) reproduces the simulation, and `--agb-mode intended` shows what the tables imply.