fix(ngmix): release FitModel observations after each metacal fit#843
Merged
Conversation
Each resdict value returned by MetacalBootstrapper.go is an ngmix FitModel — a dict subclass that retains the full per-epoch metacal observations on the plain attribute .obs (set by FitModel._set_obs) plus the pixel arrays derived from them in ._pixels_list, ~5 MB per object. do_ngmix_metacal results accumulate in final_res until the SAVE_BATCH flush, so process RSS ramps ~4.8 MB/object (to ~6 GB at SAVE_BATCH=1000 on a 3000-object test) even though compile_results reads only scalar dict keys. Null .obs and ._pixels_list immediately after the fit (and drop the obsdict reference once average_multiepoch_psf has consumed it). With the fix, RSS stays flat at ~0.9 GB over the same run and the output catalog is numerically identical to the unpatched baseline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WuMuqutUtBCgRxCoWjoebV
martinkilbinger
approved these changes
Jul 16, 2026
martinkilbinger
left a comment
Contributor
There was a problem hiding this comment.
Another nice and simple fix for a long-standing problem (that I had forgotten about...)
cailmdaley
added a commit
that referenced
this pull request
Jul 16, 2026
…star-cat store naming Three P0-blocking fixes found by running against real data: - exposures(name): the fabricated per-unit exp_numbers list must carry the original exposure name verbatim (2605805p — get_images matches <name>.fits.fz; the bare dedup id matches nothing). The index now stores both; exp_get_images passes --exp-name from the parse-time dict. - tile_star_cat: the pre-generated store names tiles in ShapePipe's image-number convention (dots->dashes); translate when linking. - profile: PYTHONPATH pinned to this branch's src/ (identical to develop@97e16d50 — orchestration commits never touch src/); NOT shapepipe-prod (drifted to a PR branch mid-run, and the live p3-batch1 job reads it) and not the sif default (frozen pre-#843). Run scoped to the 210/211 overlap quad (19 unique exposures, 15 shared — exercises structural dedup; the zero-overlap 196 quad is kept for the append-invariant test). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NA8M1oLZLbWJxwoyTNrTAi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(ngmix): release FitModel observations after each metacal fit
Problem
The ngmix module's memory climbs steadily as it works through a tile — by roughly 5 MB for every galaxy it fits — and never comes back down until the tile is done. Each finished fit is held in full, so a tile with tens of thousands of galaxies accumulates gigabytes of image data it no longer needs. On a full production tile (~27,000 galaxies) during the Nibi run in #808, this reached ~11 GB per worker; in the controlled test below (3,000 galaxies) it reaches ~6 GB. Both are the same steady climb, just stopped at different points.
The current CFIS configs set
SAVE_BATCH = 1000to periodically write results out and clear them, which caps how high the climb gets — but the module's own default is to never clear until the tile finishes, and even with the cap the memory is pure waste. That per-worker footprint is what limits how many workers fit on a node, so it directly throttles throughput.Root cause
Each per-object result in the
resdictreturned byMetacalBootstrapper.goindo_ngmix_metacalis an ngmixFitModel— a dict subclass that retains the full per-epoch metacal observations on the plain attribute.obs(set byFitModel._set_obs), plus the pixel arrays derived from them in._pixels_list, together ~5 MB per object. ShapePipe accumulates these results andcompile_resultsreads only scalar keys from each (g,g_cov,T,flux,s2n,flags, …) — the retained image arrays are never touched again. They are dead weight held to the end of the tile.Fix
In
shapepipe/modules/ngmix_package/ngmix.py, immediately after the metacal fit: null.obsand._pixels_liston eachFitModelinresdict, and drop theobsdictreference onceaverage_multiepoch_psfhas consumed it. Dict-like access to the fit results is untouched. +14 lines.Evidence / testing
A/B on tile 196-305, objects 0-3000, SAVE_BATCH=1000, single core, RSS sampled every 30 s:
Found during the Nibi test run in #808.
— Claude (Fable) on behalf of Cail.
🤖 Generated with Claude Code