diff --git a/python/lsst/meas/algorithms/normalizedCalibrationFlux.py b/python/lsst/meas/algorithms/normalizedCalibrationFlux.py index 9b6c97c58..6302acefe 100644 --- a/python/lsst/meas/algorithms/normalizedCalibrationFlux.py +++ b/python/lsst/meas/algorithms/normalizedCalibrationFlux.py @@ -19,7 +19,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -__all__ = ["NormalizedCalibrationFluxConfig", "NormalizedCalibrationFluxTask"] +__all__ = ["NormalizedCalibrationFluxConfig", "NormalizedCalibrationFluxTask", + "NormalizedCalibrationFluxError"] import numpy as np @@ -31,6 +32,35 @@ from .sourceSelector import sourceSelectorRegistry +class NormalizedCalibrationFluxError(lsst.pipe.base.AlgorithmError): + """Raised if Aperture Correction fails in a non-recoverable way. + + Parameters + ---------- + n_initial_sources : `int` + Number of sources selected by the fallback source selector. + n_calib_flux_flag : `int` + Number of selected sources with raw calibration flux flag unset. + n_ref_flux_flag : `int` + Number of selected sources with reference flux flag unset. + """ + def __init__(self, *, n_initial_sources, n_calib_flux_flag, n_ref_flux_flag): + msg = "There are no valid stars to compute normalized calibration fluxes." + msg += (f" Of {n_initial_sources} initially selected sources, {n_calib_flux_flag} have good raw" + f" calibration fluxes and {n_ref_flux_flag} have good reference fluxes.") + super().__init__(msg) + self.n_initial_sources = n_initial_sources + self.n_calib_flux_flag = n_calib_flux_flag + self.n_ref_flux_flag = n_ref_flux_flag + + @property + def metadata(self): + metadata = {"n_init_sources": self.n_initial_sources, + "n_calib_flux_flag": self.n_calib_flux_flag, + "n_ref_flux_flag": self.n_ref_flux_flag} + return metadata + + class NormalizedCalibrationFluxConfig(lsst.pex.config.Config): """Configuration parameters for NormalizedCalibrationFluxTask. """ @@ -99,6 +129,11 @@ class NormalizedCalibrationFluxTask(lsst.pipe.base.Task): Schema for the input table; will be modified in place. **kwargs : `dict` Additional kwargs to pass to lsst.pipe.base.Task.__init__() + + Raises + ------ + NormalizedCalibrationFluxError + Raised if there are not enough sources to calculate normalization. """ ConfigClass = NormalizedCalibrationFluxConfig _DefaultName = "normalizedCalibrationFlux" @@ -265,18 +300,22 @@ def _measure_aperture_correction(self, exposure, catalog): ).apCorrMap ap_corr_field = ap_corr_map.get(raw_name + "_instFlux") - except MeasureApCorrError: - self.log.warning("Failed to measure full aperture correction for %s", raw_name) + except MeasureApCorrError as e: + self.log.warning("Failed to measure full aperture correction for %s with the following error %s", + raw_name, e) - sel = self.fallback_source_selector.run(catalog, exposure=exposure).selected - sel &= (~catalog[self.config.raw_calibflux_name + "_flag"] - & ~catalog[self.config.measure_ap_corr.refFluxName + "_flag"]) + initSel = self.fallback_source_selector.run(catalog, exposure=exposure).selected + sel = (initSel & ~catalog[self.config.raw_calibflux_name + "_flag"] + & ~catalog[self.config.measure_ap_corr.refFluxName + "_flag"]) - n_sel = sel.sum() - - if n_sel == 0: + if (n_sel := sel.sum()) == 0: # This is a fatal error. - raise RuntimeError("There are no valid stars to compute normalized calibration fluxes.") + raise NormalizedCalibrationFluxError( + n_initial_sources=initSel.sum(), + n_calib_flux_flag=(initSel & ~catalog[self.config.raw_calibflux_name + "_flag"]).sum(), + n_ref_flux_flag=(initSel + & ~catalog[self.config.measure_ap_corr.refFluxName + "_flag"]).sum() + ) self.log.info("Measuring normalized flux correction with %d stars from fallback selector.", n_sel) diff --git a/tests/test_normalizedCalibrationFlux.py b/tests/test_normalizedCalibrationFlux.py index 78c2e5819..5133f69b2 100644 --- a/tests/test_normalizedCalibrationFlux.py +++ b/tests/test_normalizedCalibrationFlux.py @@ -27,7 +27,7 @@ import lsst.afw.image import lsst.afw.table import lsst.utils.tests -from lsst.meas.algorithms import NormalizedCalibrationFluxTask +from lsst.meas.algorithms import NormalizedCalibrationFluxTask, NormalizedCalibrationFluxError class NormalizedCalibrationFluxTestCase(lsst.utils.tests.TestCase): @@ -260,6 +260,20 @@ def testNormalizedCalibrationFluxApplyOnlyFail(self): warnings = '\n'.join(cm.output) self.assertIn("aperture correction map is missing base_CompensatedTophatFlux_12_instFlux", warnings) + def testNormalizedCalibrationFluxError(self): + + np.random.seed(12345) + norm_task = self._make_task() + catalog = self._make_catalog(norm_task.schema) + catalog[norm_task.config.raw_calibflux_name + "_flag"] = True + nStars = len(catalog) + + error_string = (f"There are no valid stars to compute normalized calibration fluxes. Of {nStars} " + "initially selected sources, 0 have good raw calibration fluxes and {nStars} have " + "good reference fluxes.") + with self.assertRaises(NormalizedCalibrationFluxError, msg=error_string): + norm_task.run(catalog=catalog, exposure=self.exposure) + class TestMemory(lsst.utils.tests.MemoryTestCase): pass