From 1fec60e0588f0be1be596e77a61fa2552c9a10f5 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Wed, 1 Jul 2026 18:33:33 +0200 Subject: [PATCH 1/3] refactor(make_cat): remove the dead galsim shape serialization path ShapePipe has no galsim shape-measurement runner -- only ngmix_runner measures shapes and writes NGMIX_* columns. The galsim family in make_cat (_save_galsim_shapes, the process() "galsim" branch) could only be populated by reading an external shape catalogue that nothing in the pipeline produces; every production config pins SHAPE_MEASUREMENT_TYPE = ngmix. #761 renamed these columns onto the v2 grammar, but they are not creatable end-to-end -- confirmed from the sp_validation side, where the mirror galsim consumer was removed as dead code (sp_validation #201). Remove the dormant writer rather than carry grammar-renamed columns no pipeline can exercise. - delete SaveCatalogue._save_galsim_shapes and its process() "galsim" dispatch branch; process modes are now "ngmix" or "psf" - drop the now-orphaned cs_util.size import (only the galsim block used it) - delete tests/module/test_galsim_grammar_properties.py, which drove the removed writer Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_0191bMtgT2gcmcT7fNYbGLS1 --- .../modules/make_cat_package/make_cat.py | 142 +--------- .../module/test_galsim_grammar_properties.py | 251 ------------------ 2 files changed, 3 insertions(+), 390 deletions(-) delete mode 100644 tests/module/test_galsim_grammar_properties.py diff --git a/src/shapepipe/modules/make_cat_package/make_cat.py b/src/shapepipe/modules/make_cat_package/make_cat.py index ae4ac648..a92d479b 100644 --- a/src/shapepipe/modules/make_cat_package/make_cat.py +++ b/src/shapepipe/modules/make_cat_package/make_cat.py @@ -13,7 +13,6 @@ from astropy import coordinates as coords from astropy import units as u from astropy.wcs import WCS -from cs_util import size as cs_size from sqlitedict import SqliteDict from shapepipe.pipeline import file_io @@ -243,7 +242,7 @@ def process( Parameters ---------- mode : str - Run mode, options are ``ngmix``, ``galsim`` or ``psf`` + Run mode, options are ``ngmix`` or ``psf`` cat_path : str Path to input catalogue moments : bool @@ -263,15 +262,13 @@ def process( err_msg = None if mode == "ngmix": err_msg = self._save_ngmix_data(cat_path, moments) - elif mode == "galsim": - self._save_galsim_shapes(cat_path) elif mode == "psf": self._save_psf_data(cat_path) else: err_msg = ( f"Invalid process mode ({mode}) for " - + '``make_cat.Savecatalogue``. Options are "ngmix", ' - + '"galsim" or "psf".' + + '``make_cat.Savecatalogue``. Options are "ngmix" ' + + 'or "psf".' ) if err_msg is None: @@ -543,139 +540,6 @@ def _save_ngmix_data(self, ngmix_cat_path, moments=False): return None - def _save_galsim_shapes(self, galsim_cat_path): - """Save GalSim Shapes. - - Save the GalSim catalogue into the final one. - - Parameters - ---------- - galsim_cat_path : str - Path to GalSim catalogue to save - - """ - galsim_cat_file = file_io.FITSCatalogue(galsim_cat_path) - galsim_cat_file.open() - - self._key_ends = galsim_cat_file.get_ext_name()[1:] - - galsim_id = galsim_cat_file.get_data()["id"] - - for key_str in ( - "GALSIM_T_", - "GALSIM_T_PSF_", - "GALSIM_FLUX_", - "GALSIM_MAG_", - ): - self._update_dict(key_str, np.zeros(len(self._obj_id))) - for key_str in ("GALSIM_FLUX_ERR_", "GALSIM_MAG_ERR_", "GALSIM_RES_"): - self._update_dict(key_str, np.ones(len(self._obj_id)) * -1) - for key_str in ( - "GALSIM_G1_", - "GALSIM_G2_", - "GALSIM_G1_ERR_", - "GALSIM_G2_ERR_", - "GALSIM_G1_UNCORR_", - "GALSIM_G2_UNCORR_", - "GALSIM_G1_PSF_", - "GALSIM_G2_PSF_", - ): - self._update_dict(key_str, np.ones(len(self._obj_id)) * -10.0) - self._update_dict( - "GALSIM_FLAGS_", - np.ones(len(self._obj_id), dtype="int16"), - ) - - for idx, id_tmp in enumerate(self._obj_id): - ind = np.where(id_tmp == galsim_id)[0] - if len(ind) > 0: - - for key in self._key_ends: - - gcf_data = galsim_cat_file.get_data(key) - - if key == "ORIGINAL_PSF": - - # PSF columns sourced from the galaxy uncorr fields - # for this special extension (asymmetry preserved). - self._add2dict( - f"GALSIM_G1_PSF_{key}", - gcf_data["gal_uncorr_g1"][ind[0]], idx - ) - self._add2dict( - f"GALSIM_G2_PSF_{key}", - gcf_data["gal_uncorr_g2"][ind[0]], idx - ) - self._add2dict( - f"GALSIM_T_PSF_{key}", - cs_size.sigma_to_T(gcf_data["gal_sigma"][ind[0]]), - idx - ) - - else: - - self._add2dict( - f"GALSIM_G1_{key}", gcf_data["gal_g1"][ind[0]], idx - ) - self._add2dict( - f"GALSIM_G2_{key}", gcf_data["gal_g2"][ind[0]], idx - ) - self._add2dict( - f"GALSIM_G1_ERR_{key}", - gcf_data["gal_g1_err"][ind[0]], idx - ) - self._add2dict( - f"GALSIM_G2_ERR_{key}", - gcf_data["gal_g2_err"][ind[0]], idx - ) - - self._add2dict( - f"GALSIM_G1_UNCORR_{key}", - gcf_data["gal_uncorr_g1"][ind[0]], idx - ) - self._add2dict( - f"GALSIM_G2_UNCORR_{key}", - gcf_data["gal_uncorr_g2"][ind[0]], idx - ) - - self._add2dict( - f"GALSIM_T_{key}", - cs_size.sigma_to_T(gcf_data["gal_sigma"][ind[0]]), - idx - ) - - self._add2dict( - f"GALSIM_G1_PSF_{key}", - gcf_data["psf_g1"][ind[0]], idx - ) - self._add2dict( - f"GALSIM_G2_PSF_{key}", - gcf_data["psf_g2"][ind[0]], idx - ) - self._add2dict( - f"GALSIM_T_PSF_{key}", - cs_size.sigma_to_T(gcf_data["psf_sigma"][ind[0]]), - idx - ) - - flux = gcf_data["gal_flux"][ind[0]] - flux_err = gcf_data["gal_flux_err"][ind[0]] - self._add2dict(f"GALSIM_FLUX_{key}", flux, idx) - self._add2dict(f"GALSIM_FLUX_ERR_{key}", flux_err, idx) - - mag = gcf_data["gal_mag"][ind[0]] - mag_err = gcf_data["gal_mag_err"][ind[0]] - self._add2dict(f"GALSIM_MAG_{key}", mag, idx) - self._add2dict(f"GALSIM_MAG_ERR_{key}", mag_err, idx) - - flags = gcf_data["gal_flag"][ind[0]] - self._add2dict(f"GALSIM_FLAGS_{key}", flags, idx) - - res = gcf_data["gal_resolution"][ind[0]] - self._add2dict(f"GALSIM_RES_{key}", res, idx) - - galsim_cat_file.close() - def _save_psf_data(self, galaxy_psf_path): """Save PSF data. diff --git a/tests/module/test_galsim_grammar_properties.py b/tests/module/test_galsim_grammar_properties.py deleted file mode 100644 index 28346eaf..00000000 --- a/tests/module/test_galsim_grammar_properties.py +++ /dev/null @@ -1,251 +0,0 @@ -"""PROPERTY-BASED TESTS FOR THE GALSIM GALAXY/PSF COLUMN GRAMMAR. - -Drives ``SaveCatalogue._save_galsim_shapes`` (the make_cat writer) to pin the -renamed galsim grammar (shapepipe#761): the packed length-2 -``GALSIM_GAL_ELL_*`` / ``GALSIM_PSF_ELL_*`` columns are split into scalar -``GALSIM_G1_*`` / ``GALSIM_G2_*`` (g-type — galsim is correctly g-type, no -e/g value fix needed here), the galaxy carries no ``GAL`` token (implicit -default object, same rule as ngmix), and the raw ``GALSIM_GAL_SIGMA_*`` / -``GALSIM_PSF_SIGMA_*`` columns are replaced by the single stored size -``GALSIM_T_*`` (``T = 2 sigma^2``, via ``cs_util.size.sigma_to_T``). - -The ``ORIGINAL_PSF`` extension is a deliberate asymmetry, preserved rather -than normalized away: its PSF columns are sourced from the *galaxy's* -uncorrected fields (``gal_uncorr_g1/g2``, ``gal_sigma``), not the -per-extension ``psf_g1/g2``/``psf_sigma`` fields every other extension uses. -""" - -import re -import tempfile -from pathlib import Path - -import numpy as np -import numpy.testing as npt -from astropy.io import fits -from cs_util import size as cs_size -from hypothesis import given, settings -from hypothesis import strategies as st - -from shapepipe.modules.make_cat_package.make_cat import SaveCatalogue - -# --------------------------------------------------------------------------- -# Harness -# --------------------------------------------------------------------------- - -# Source columns _save_galsim_shapes reads per extension. The ORIGINAL_PSF -# extension only consumes the gal_uncorr_g1/g2 + gal_sigma subset, but the -# real galsim catalogue carries the full set on every extension, so the -# fixture mirrors that rather than special-casing the row shape. -GALSIM_KEYS = [ - "id", - "gal_g1", "gal_g2", "gal_g1_err", "gal_g2_err", - "gal_uncorr_g1", "gal_uncorr_g2", "gal_sigma", - "psf_g1", "psf_g2", "psf_sigma", - "gal_flux", "gal_flux_err", "gal_mag", "gal_mag_err", - "gal_flag", "gal_resolution", -] -INT_KEYS = {"id", "gal_flag"} - -# Per-key multiplier so a single scalar ``seed`` fans out into twelve -# distinct values — a swapped source (e.g. psf_g1 <-> gal_uncorr_g1) breaks -# at least one equality below. -_MULT = { - "gal_g1": 1.0, "gal_g2": 1.3, "gal_g1_err": 1.7, "gal_g2_err": 2.1, - "gal_uncorr_g1": 2.6, "gal_uncorr_g2": 3.1, "gal_sigma": 0.4, - "psf_g1": 0.6, "psf_g2": 0.8, "psf_sigma": 0.5, - "gal_flux": 10.0, "gal_flux_err": 1.0, "gal_mag": 5.0, - "gal_mag_err": 0.2, -} - - -def _galsim_row(obj_id, seed): - """One object's per-key values for one extension, scaled by ``seed``.""" - row = {"id": obj_id, "gal_flag": 0, "gal_resolution": 0.5} - row.update({key: mult * seed for key, mult in _MULT.items()}) - return row - - -def _write_galsim_cat(path, obj_ids, seed_by_ext): - """Write a synthetic galsim FITS, one extension per ``seed_by_ext`` key. - - Extension order is preserved (dict order), matching how - ``get_ext_name()[1:]`` drives ``self._key_ends`` in production. - """ - hdus = [fits.PrimaryHDU()] - for ext, seed in seed_by_ext.items(): - rows = [_galsim_row(oid, seed) for oid in obj_ids] - cols = [ - fits.Column( - name=key, - format="K" if key in INT_KEYS else "D", - array=np.array([row[key] for row in rows]), - ) - for key in GALSIM_KEYS - ] - hdus.append(fits.BinTableHDU.from_columns(cols, name=ext)) - fits.HDUList(hdus).writeto(path, overwrite=True) - - -def _run_save_galsim(galsim_path, obj_ids): - """Drive ``_save_galsim_shapes`` and return its populated output dict.""" - inst = object.__new__(SaveCatalogue) - inst._obj_id = np.asarray(obj_ids) - inst._output_dict = {} - inst._save_galsim_shapes(str(galsim_path)) - return inst._output_dict - - -# --------------------------------------------------------------------------- -# Strategies -# --------------------------------------------------------------------------- - -obj_ids_strategy = st.lists( - st.integers(min_value=1, max_value=10_000), min_size=1, max_size=5, - unique=True, -) -# A finite seed bounded away from zero, so every per-key value it scales is -# itself non-zero and the family-vs-family / key-vs-key comparisons below are -# never vacuously satisfied by an accidental 0 == 0. -_seed = st.floats( - min_value=0.05, max_value=5.0, allow_nan=False, allow_infinity=False, -) - - -# --------------------------------------------------------------------------- -# (a) Scalar split + size routing, regular extension -# --------------------------------------------------------------------------- - -@settings(deadline=None, max_examples=25) -@given(obj_ids=obj_ids_strategy, seed=_seed) -def test_regular_extension_scalar_split_and_size(obj_ids, seed): - """A non-``ORIGINAL_PSF`` extension routes each source to its own column. - - ``GALSIM_G1/G2_`` from ``gal_g1/g2``, the ``_ERR``/``_UNCORR`` - variants from their namesake sources, ``GALSIM_T_`` from - ``sigma_to_T(gal_sigma)``, and the PSF family - (``GALSIM_G1/G2_PSF_``, ``GALSIM_T_PSF_``) from ``psf_g1/g2`` / - ``sigma_to_T(psf_sigma)`` — never from the galaxy's own fields. - """ - ext = "NOSHEAR" - path = tempfile.NamedTemporaryFile(suffix=".fits", delete=False).name - _write_galsim_cat(path, obj_ids, {ext: seed}) - out = _run_save_galsim(path, obj_ids) - Path(path).unlink() - - n = len(obj_ids) - row = _galsim_row(obj_ids[0], seed) - npt.assert_allclose(out[f"GALSIM_G1_{ext}"], [row["gal_g1"]] * n) - npt.assert_allclose(out[f"GALSIM_G2_{ext}"], [row["gal_g2"]] * n) - npt.assert_allclose(out[f"GALSIM_G1_ERR_{ext}"], [row["gal_g1_err"]] * n) - npt.assert_allclose(out[f"GALSIM_G2_ERR_{ext}"], [row["gal_g2_err"]] * n) - npt.assert_allclose( - out[f"GALSIM_G1_UNCORR_{ext}"], [row["gal_uncorr_g1"]] * n - ) - npt.assert_allclose( - out[f"GALSIM_G2_UNCORR_{ext}"], [row["gal_uncorr_g2"]] * n - ) - npt.assert_allclose( - out[f"GALSIM_T_{ext}"], [cs_size.sigma_to_T(row["gal_sigma"])] * n - ) - npt.assert_allclose(out[f"GALSIM_G1_PSF_{ext}"], [row["psf_g1"]] * n) - npt.assert_allclose(out[f"GALSIM_G2_PSF_{ext}"], [row["psf_g2"]] * n) - npt.assert_allclose( - out[f"GALSIM_T_PSF_{ext}"], [cs_size.sigma_to_T(row["psf_sigma"])] * n - ) - # The galaxy and PSF families must stay distinct (un-aliased), and the - # raw-sigma value must never leak through unconverted. - assert not np.allclose(out[f"GALSIM_T_{ext}"], row["gal_sigma"]) - assert not np.allclose(out[f"GALSIM_G1_{ext}"], out[f"GALSIM_G1_PSF_{ext}"]) - - npt.assert_allclose(out[f"GALSIM_FLUX_{ext}"], [row["gal_flux"]] * n) - npt.assert_allclose(out[f"GALSIM_FLUX_ERR_{ext}"], [row["gal_flux_err"]] * n) - npt.assert_allclose(out[f"GALSIM_MAG_{ext}"], [row["gal_mag"]] * n) - npt.assert_allclose(out[f"GALSIM_MAG_ERR_{ext}"], [row["gal_mag_err"]] * n) - npt.assert_allclose(out[f"GALSIM_RES_{ext}"], [row["gal_resolution"]] * n) - - -# --------------------------------------------------------------------------- -# (b) ORIGINAL_PSF asymmetry: sourced from the galaxy's uncorrected fields -# --------------------------------------------------------------------------- - -@settings(deadline=None, max_examples=25) -@given(obj_ids=obj_ids_strategy, seed_main=_seed, seed_origpsf=_seed) -def test_original_psf_extension_sourced_from_gal_uncorr( - obj_ids, seed_main, seed_origpsf -): - """``ORIGINAL_PSF`` columns trace to ``gal_uncorr_*``/``gal_sigma``, NOT - ``psf_*`` — the one preserved asymmetry in the galsim grammar. - - The two extensions are written with independent seeds, and the - ``ORIGINAL_PSF`` row's own ``psf_g1/g2``/``psf_sigma`` are deliberately - distinct from its ``gal_uncorr_*``/``gal_sigma`` (the seed multipliers - differ), so a regression that wired the regular per-extension ``psf_*`` - sourcing into ``ORIGINAL_PSF`` would be caught, not masked by equal - values. - """ - seed_by_ext = {"NOSHEAR": seed_main, "ORIGINAL_PSF": seed_origpsf} - path = tempfile.NamedTemporaryFile(suffix=".fits", delete=False).name - _write_galsim_cat(path, obj_ids, seed_by_ext) - out = _run_save_galsim(path, obj_ids) - Path(path).unlink() - - n = len(obj_ids) - op_row = _galsim_row(obj_ids[0], seed_origpsf) - npt.assert_allclose( - out["GALSIM_G1_PSF_ORIGINAL_PSF"], [op_row["gal_uncorr_g1"]] * n - ) - npt.assert_allclose( - out["GALSIM_G2_PSF_ORIGINAL_PSF"], [op_row["gal_uncorr_g2"]] * n - ) - npt.assert_allclose( - out["GALSIM_T_PSF_ORIGINAL_PSF"], - [cs_size.sigma_to_T(op_row["gal_sigma"])] * n, - ) - # Not sourced from this extension's own psf_g1/g2/psf_sigma (those are - # scaled by the same seed but through different multipliers, so equality - # here would mean the asymmetry was lost). - assert not np.allclose( - out["GALSIM_G1_PSF_ORIGINAL_PSF"], [op_row["psf_g1"]] * n - ) - assert not np.allclose( - out["GALSIM_T_PSF_ORIGINAL_PSF"], - [cs_size.sigma_to_T(op_row["psf_sigma"])] * n, - ) - - -# --------------------------------------------------------------------------- -# (c) Every emitted column matches the new grammar; no legacy tokens survive -# --------------------------------------------------------------------------- - -@settings(deadline=None, max_examples=25) -@given(obj_ids=obj_ids_strategy, seed=_seed) -def test_emitted_columns_match_grammar_no_legacy_tokens(obj_ids, seed): - """Every ``GALSIM_*`` column matches the scalar grammar; nothing packed. - - No ``GALSIM_GAL_ELL*``/``GALSIM_PSF_ELL*`` (packed length-2 ellipticity) - and no ``GALSIM_*_SIGMA*`` (raw size) survives — both replaced by scalar - ``G1``/``G2`` components and the single stored size ``T``. The galaxy - also carries no ``GAL`` token (implicit default object, same rule as - ngmix). - """ - ext_names = ["NOSHEAR", "ORIGINAL_PSF"] - seed_by_ext = {ext: seed * (i + 1) for i, ext in enumerate(ext_names)} - path = tempfile.NamedTemporaryFile(suffix=".fits", delete=False).name - _write_galsim_cat(path, obj_ids, seed_by_ext) - out = _run_save_galsim(path, obj_ids) - Path(path).unlink() - - assert out, "writer produced no columns" - - _component = "G1|G2|T" - _qual = "(?:_ERR|_UNCORR)?" - _ext = "|".join(re.escape(e) for e in ext_names) - grammar_re = re.compile( - rf"^GALSIM_(?:{_component}){_qual}(?:_PSF)?_(?:{_ext})$" - rf"|^GALSIM_(?:FLUX|FLUX_ERR|MAG|MAG_ERR|FLAGS|RES)_(?:{_ext})$" - ) - for col in out: - assert grammar_re.match(col), f"off-grammar column emitted: {col!r}" - assert "_ELL" not in col, col - assert "SIGMA" not in col, col - assert "_GAL_" not in col and not col.endswith("_GAL"), col From 35ffa2d098f7542e50f31985f2ef6ff7496a3b80 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Wed, 1 Jul 2026 18:33:48 +0200 Subject: [PATCH 2/3] refactor(make_cat): restrict SHAPE_MEASUREMENT_TYPE to ngmix ngmix is now the pipeline's only shape estimator, so make_cat_runner validates SHAPE_MEASUREMENT_TYPE against ngmix alone and raises on anything else. The knob is kept (rather than hardcoded) as the extension point for a future estimator family, mirroring sp_validation's retained `prefix`. The shape-write loop drops the galsim-vs-ngmix catalogue selection -- shapes are always read from the single ngmix input. Docstring and the cat_matched.param comment lose their galsim references. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_0191bMtgT2gcmcT7fNYbGLS1 --- example/unions_800/cat_matched.param | 2 +- src/shapepipe/modules/make_cat_package/__init__.py | 2 +- src/shapepipe/modules/make_cat_runner.py | 12 +++--------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/example/unions_800/cat_matched.param b/example/unions_800/cat_matched.param index ce19045d..5d5d99e8 100644 --- a/example/unions_800/cat_matched.param +++ b/example/unions_800/cat_matched.param @@ -94,7 +94,7 @@ FLAG_TILING # magnitude, mainly for plots MAG_AUTO -# SNR from SExtractor, used for cuts on GALSIM shapes +# SNR from SExtractor, used for shape cuts SNR_WIN # PSF size measured on original image diff --git a/src/shapepipe/modules/make_cat_package/__init__.py b/src/shapepipe/modules/make_cat_package/__init__.py index 1bd1cee5..80f23b9a 100644 --- a/src/shapepipe/modules/make_cat_package/__init__.py +++ b/src/shapepipe/modules/make_cat_package/__init__.py @@ -43,7 +43,7 @@ spread model and :math:`\sigma_s` is the spread model error; default value is ``0.01`` SHAPE_MEASUREMENT_TYPE : list - Shape measurement method, valid options are ``ngmix`` and/or ``galsim`` + Shape measurement method; the only supported option is ``ngmix`` SAVE_PSF_DATA : bool, optional Save PSF information if ``True``; default value is ``False`` TILE_LIST : str, optional diff --git a/src/shapepipe/modules/make_cat_runner.py b/src/shapepipe/modules/make_cat_runner.py index fa1fc43f..99fed739 100644 --- a/src/shapepipe/modules/make_cat_runner.py +++ b/src/shapepipe/modules/make_cat_runner.py @@ -77,10 +77,8 @@ def make_cat_runner( "SHAPE_MEASUREMENT_TYPE", ) for shape_type in shape_type_list: - if shape_type.lower() not in ["ngmix", "galsim"]: - raise ValueError( - "SHAPE_MEASUREMENT_TYPE must be in [ngmix, galsim]" - ) + if shape_type.lower() != "ngmix": + raise ValueError("SHAPE_MEASUREMENT_TYPE must be ngmix") # Fetch PSF data option if config.has_option(module_config_sec, "SAVE_PSF_DATA"): @@ -125,11 +123,7 @@ def make_cat_runner( w_log.info("Save shape measurement data") for shape_type in shape_type_list: w_log.info(f"Save {shape_type.lower()} data") - cat_path = ( - shape2_cat_path if shape_type == "galsim" else shape1_cat_path - ) - err_msg = sc_inst.process(shape_type.lower(), cat_path) - + err_msg = sc_inst.process(shape_type.lower(), shape1_cat_path) # If error message: delete (incomplete) output file and raise error if err_msg is not None: From 09084c50e26e0f5fb9625d261ed2a79a318c0343 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Wed, 1 Jul 2026 18:33:58 +0200 Subject: [PATCH 3/3] test: drop galsim from the frozen column-grammar examples The static grammar test covered ngmix, galsim, and HSM. With the galsim writer gone, drop its regex branch and its valid/invalid example columns; the frozen grammar now spans the two families ShapePipe actually produces (ngmix, HSM). Also drop the docstring pointer to the deleted test_galsim_grammar_properties.py. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_0191bMtgT2gcmcT7fNYbGLS1 --- tests/module/test_psf_grammar_properties.py | 46 +++++++-------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/tests/module/test_psf_grammar_properties.py b/tests/module/test_psf_grammar_properties.py index 09eaf181..593614a1 100644 --- a/tests/module/test_psf_grammar_properties.py +++ b/tests/module/test_psf_grammar_properties.py @@ -21,14 +21,13 @@ (``example/cfis/final_cat.param``) names is a column the writer can produce — writer/param-file consistency; (d) the FULL frozen grammar (shapepipe#761) — ``ESTIMATOR_COMPONENT[_ERR]_ - OBJECT[_metacaltype]`` — holds across all three estimator families: ngmix - and galsim split ellipticity into ``G1``/``G2`` (g-type), HSM into - ``E1``/``E2`` (e-type); every family stores exactly one size, ``T``; HSM - keeps the singular ``FLAG`` token, ngmix/galsim keep plural ``FLAGS``. - This part is a static example-based check (not writer-driven), so it - covers galsim/HSM without depending on those producer modules — see - ``test_galsim_grammar_properties.py`` and ``test_psfex_interp.py`` for - the writer-driven coverage of those families. + OBJECT[_metacaltype]`` — holds across both estimator families: ngmix + splits ellipticity into ``G1``/``G2`` (g-type), HSM into ``E1``/``E2`` + (e-type); every family stores exactly one size, ``T``; HSM keeps the + singular ``FLAG`` token, ngmix keeps plural ``FLAGS``. This part is a + static example-based check (not writer-driven), so it covers HSM without + depending on its producer module — see ``test_psfex_interp.py`` for the + writer-driven coverage of that family. Setup for (a)-(c) mirrors ``tests/module/test_make_cat.py``: a synthetic ngmix FITS with the five shear-type extensions carrying the exact @@ -390,23 +389,22 @@ def test_param_file_ngmix_tokens_are_producible(param_path, obj_ids): # --------------------------------------------------------------------------- -# (d) The FULL frozen grammar, across all three estimator families +# (d) The FULL frozen grammar, across both estimator families # --------------------------------------------------------------------------- # # ``ESTIMATOR_COMPONENT[_ERR]_OBJECT[_metacaltype]`` (shapepipe#761). This # section encodes the grammar itself as a static property — example column -# names, not a driven writer — so it covers ngmix, galsim, and HSM together -# without depending on the galsim/HSM producer modules (covered by their own -# writer-driving tests: ``test_galsim_grammar_properties.py``, -# ``test_psfex_interp.py``). What it pins: +# names, not a driven writer — so it covers ngmix and HSM together without +# depending on the HSM producer module (covered by its own writer-driving +# test, ``test_psfex_interp.py``). What it pins: # -# - galaxy is the implicit default object, no token, for BOTH g-type writers -# (ngmix, galsim); +# - galaxy is the implicit default object, no token, for the g-type ngmix +# writer; # - ellipticity is split into named scalar components, never a packed -# 2-vector: ``G1``/``G2`` for g-type (ngmix, galsim), ``E1``/``E2`` for -# e-type (HSM); +# 2-vector: ``G1``/``G2`` for g-type (ngmix), ``E1``/``E2`` for e-type +# (HSM); # - exactly one stored size, ``T`` (no ``SIGMA``/``FWHM``/``r50``); -# - HSM keeps the singular ``FLAG`` token; ngmix/galsim keep plural ``FLAGS``. +# - HSM keeps the singular ``FLAG`` token; ngmix keeps plural ``FLAGS``. FROZEN_GRAMMAR_RE = re.compile( r"^(?:" @@ -414,10 +412,6 @@ def test_param_file_ngmix_tokens_are_producible(param_path, obj_ids): r"NGMIXm?_(?:G1|G2|T|SNR|FLUX|MAG|FLAGS)(?:_ERR)?" r"(?:_(?:PSF_ORIG|PSF_RECONV))?_(?:NOSHEAR|1P|1M|2P|2M)" r"|NGMIXm?_(?:MCAL_FLAGS|MCAL_TYPES_FAIL|N_EPOCH)" - # galsim: galaxy (no object token) or explicit PSF object; is a - # data-driven FITS extension name, so left as a generic uppercase token. - r"|GALSIM_(?:G1|G2|T)(?:_ERR|_UNCORR)?(?:_PSF)?_[A-Z0-9_]+" - r"|GALSIM_(?:FLUX|FLUX_ERR|MAG|MAG_ERR|FLAGS|RES)_[A-Z0-9_]+" # HSM: e-type, explicit PSF/STAR object, singular FLAG; the multi-epoch # sink in make_cat._save_psf_data appends a bare epoch index. r"|HSM_(?:E1|E2|T)_(?:PSF|STAR)(?:_\d+)?" @@ -434,10 +428,6 @@ def test_param_file_ngmix_tokens_are_producible(param_path, obj_ids): "NGMIX_FLAGS_2P", "NGMIXm_G1_PSF_RECONV_NOSHEAR", "NGMIX_MCAL_FLAGS", - "GALSIM_G1_NOSHEAR", # galaxy, no object token - "GALSIM_G2_UNCORR_NOSHEAR", - "GALSIM_T_PSF_ORIGINAL_PSF", - "GALSIM_FLAGS_NOSHEAR", "HSM_E1_PSF", "HSM_T_STAR", "HSM_FLAG_PSF", @@ -455,10 +445,6 @@ def test_param_file_ngmix_tokens_are_producible(param_path, obj_ids): "SIGMA_PSF_HSM", # raw sigma, not T "HSM_SIGMA_PSF", # stored sigma instead of T "HSM_FLAGS_PSF", # plural — HSM is singular FLAG - "GALSIM_GAL_ELL_NOSHEAR", # GAL token + packed ellipticity - "GALSIM_GAL_SIGMA_NOSHEAR", # GAL token + raw sigma - "GALSIM_PSF_ELL_NOSHEAR", # packed ellipticity - "GALSIM_FWHM_NOSHEAR", # FWHM, not T "SPREAD_MODEL", # removed entirely, not renamed ]