Skip to content

Commit 662fad4

Browse files
cailmdaleyclaude
andcommitted
fix(make_cat): read the HSM g-type PSF columns the producers write
_save_psf_data still read HSM_E1_PSF/HSM_E2_PSF from the multi-epoch PSF sqlite, but every producer (psfex_interp._interpolate_me, mccd) writes HSM_G1_PSF/HSM_G2_PSF since the return to g-type ellipticities (#761) — a guaranteed KeyError on the first unflagged PSF epoch whenever SAVE_PSF is enabled. Rename the consumer (and its output columns) to the g-type grammar. The frozen-grammar property test encoded the same stale E-type convention for HSM, rubber-stamping the mismatch: fix the regex and examples (HSM_E1_PSF is now an explicit invalid example), and align the stale e-type docstrings in test_psfex_interp with the g-type behavior the assertions actually pin. Found by the final integrated review of #741. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TVYoKzcNwYjuGiFi9CxsYP
1 parent c2d03c3 commit 662fad4

3 files changed

Lines changed: 22 additions & 21 deletions

File tree

src/shapepipe/modules/make_cat_package/make_cat.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -692,13 +692,13 @@ def _save_psf_data(self, galaxy_psf_path):
692692
max_epoch = np.max(self._final_cat_file.get_data()["N_EPOCH"]) + 1
693693

694694
self._output_dict = {
695-
f"HSM_E1_PSF_{idx + 1}": np.ones(len(self._obj_id)) * -10.0
695+
f"HSM_G1_PSF_{idx + 1}": np.ones(len(self._obj_id)) * -10.0
696696
for idx in range(max_epoch)
697697
}
698698
self._output_dict = {
699699
**self._output_dict,
700700
**{
701-
f"HSM_E2_PSF_{idx + 1}": np.ones(len(self._obj_id)) * -10.0
701+
f"HSM_G2_PSF_{idx + 1}": np.ones(len(self._obj_id)) * -10.0
702702
for idx in range(max_epoch)
703703
},
704704
}
@@ -732,12 +732,12 @@ def _save_psf_data(self, galaxy_psf_path):
732732
continue
733733

734734
self._add2dict(
735-
f"HSM_E1_PSF_{epoch + 1}",
736-
gpc_data["SHAPES"]["HSM_E1_PSF"], idx
735+
f"HSM_G1_PSF_{epoch + 1}",
736+
gpc_data["SHAPES"]["HSM_G1_PSF"], idx
737737
)
738738
self._add2dict(
739-
f"HSM_E2_PSF_{epoch + 1}",
740-
gpc_data["SHAPES"]["HSM_E2_PSF"], idx
739+
f"HSM_G2_PSF_{epoch + 1}",
740+
gpc_data["SHAPES"]["HSM_G2_PSF"], idx
741741
)
742742

743743
# HSM_T_PSF already holds T (sigma_to_T applied at the

tests/module/test_psf_grammar_properties.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
(``example/cfis/final_cat.param``) names is a column the writer can
2222
produce — writer/param-file consistency;
2323
(d) the FULL frozen grammar (shapepipe#761) — ``ESTIMATOR_COMPONENT[_ERR]_
24-
OBJECT[_metacaltype]`` — holds across all three estimator families: ngmix
25-
and galsim split ellipticity into ``G1``/``G2`` (g-type), HSM into
26-
``E1``/``E2`` (e-type); every family stores exactly one size, ``T``; HSM
24+
OBJECT[_metacaltype]`` — holds across all three estimator families: all three
25+
families split ellipticity into ``G1``/``G2`` (g-type, HSM included since
26+
the return to reduced shear); every family stores exactly one size, ``T``; HSM
2727
keeps the singular ``FLAG`` token, ngmix/galsim keep plural ``FLAGS``.
2828
This part is a static example-based check (not writer-driven), so it
2929
covers galsim/HSM without depending on those producer modules — see
@@ -418,9 +418,9 @@ def test_param_file_ngmix_tokens_are_producible(param_path, obj_ids):
418418
# data-driven FITS extension name, so left as a generic uppercase token.
419419
r"|GALSIM_(?:G1|G2|T)(?:_ERR|_UNCORR)?(?:_PSF)?_[A-Z0-9_]+"
420420
r"|GALSIM_(?:FLUX|FLUX_ERR|MAG|MAG_ERR|FLAGS|RES)_[A-Z0-9_]+"
421-
# HSM: e-type, explicit PSF/STAR object, singular FLAG; the multi-epoch
421+
# HSM: g-type, explicit PSF/STAR object, singular FLAG; the multi-epoch
422422
# sink in make_cat._save_psf_data appends a bare epoch index.
423-
r"|HSM_(?:E1|E2|T)_(?:PSF|STAR)(?:_\d+)?"
423+
r"|HSM_(?:G1|G2|T)_(?:PSF|STAR)(?:_\d+)?"
424424
r"|HSM_FLAG_(?:PSF|STAR)(?:_\d+)?"
425425
r")$"
426426
)
@@ -438,10 +438,10 @@ def test_param_file_ngmix_tokens_are_producible(param_path, obj_ids):
438438
"GALSIM_G2_UNCORR_NOSHEAR",
439439
"GALSIM_T_PSF_ORIGINAL_PSF",
440440
"GALSIM_FLAGS_NOSHEAR",
441-
"HSM_E1_PSF",
441+
"HSM_G1_PSF",
442442
"HSM_T_STAR",
443443
"HSM_FLAG_PSF",
444-
"HSM_E1_PSF_3", # make_cat multi-epoch sink
444+
"HSM_G1_PSF_3", # make_cat multi-epoch sink
445445
"HSM_T_PSF_2",
446446
"HSM_FLAG_STAR_1",
447447
]
@@ -452,6 +452,7 @@ def test_param_file_ngmix_tokens_are_producible(param_path, obj_ids):
452452
"NGMIX_G1_GAL_NOSHEAR", # GAL token regression
453453
"NGMIX_ELL_NOSHEAR", # packed ellipticity
454454
"E1_PSF_HSM", # pre-rename HSM naming (not HSM_-prefixed)
455+
"HSM_E1_PSF", # e-type leftover: HSM stores g-type G1/G2 since #761
455456
"SIGMA_PSF_HSM", # raw sigma, not T
456457
"HSM_SIGMA_PSF", # stored sigma instead of T
457458
"HSM_FLAGS_PSF", # plural — HSM is singular FLAG

tests/module/test_psfex_interp.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,15 @@ def test_get_galaxy_positions_raises_for_missing_column(tmp_path):
121121

122122
@settings(deadline=None, max_examples=25)
123123
@given(e=_e_complex)
124-
def test_get_psfshapes_stores_e_type_distortion(psfex_interpolator, e):
125-
"""The stored HSM PSF shapes are e-type distortion, not g-type shear.
124+
def test_get_psfshapes_stores_g_type_shear(psfex_interpolator, e):
125+
"""The stored HSM PSF shapes are g-type reduced shear, not e-type distortion.
126126
127-
``_get_psfshapes`` reads ``moms.observed_shape.e1/.e2``; this pins it to the
128-
galsim *distortion* accessor and away from the reduced-shear ``.g1/.g2``.
127+
``_get_psfshapes`` reads ``moms.observed_shape.g1/.g2``; this pins it to the
128+
galsim *reduced-shear* accessor and away from the distortion ``.e1/.e2``.
129129
The check is exact against the same image's ``observed_shape`` (independent
130130
of how well HSM recovers the input), and the magnitude inequality is the
131-
tooth: for any non-round shape ``|e| > |g|`` strictly, so storing ``.g``
132-
instead (the pre-fix bug) would fail it.
131+
tooth: for any non-round shape ``|e| > |g|`` strictly, so storing ``.e``
132+
instead would fail it.
133133
"""
134134
psfex_interpolator.interp_PSFs = _elliptical_psf_stamps([(e.real, e.imag)])
135135
psfex_interpolator._get_psfshapes()
@@ -144,11 +144,11 @@ def test_get_psfshapes_stores_e_type_distortion(psfex_interpolator, e):
144144
mag_g = np.hypot(g1, g2)
145145
assume(mag_e - mag_g > 1e-3) # ensure a non-degenerate e-vs-g distinction
146146

147-
# Stored values are exactly the g-type distortion components ...
147+
# Stored values are exactly the g-type reduced-shear components ...
148148
npt.assert_array_equal(psfex_interpolator.psf_shapes[0, 0], g1)
149149
npt.assert_array_equal(psfex_interpolator.psf_shapes[0, 1], g2)
150150

151-
# ... whose magnitude strictly subceeds the e-type shear.
151+
# ... whose magnitude strictly subceeds the e-type distortion.
152152
mag_stored = np.hypot(
153153
psfex_interpolator.psf_shapes[0, 0], psfex_interpolator.psf_shapes[0, 1]
154154
)

0 commit comments

Comments
 (0)