From cf729bae3a5bbcdfe5aa1b5a410b342315b79a46 Mon Sep 17 00:00:00 2001 From: John Parejko Date: Mon, 10 Jun 2024 16:03:44 -0700 Subject: [PATCH 1/2] Add nanojanskyToInstFlux to PhotoCalib This is useful for computing the expected instrumental flux for refcat sources. --- include/lsst/afw/image/PhotoCalib.h | 17 +++++++++++++++++ python/lsst/afw/image/_photoCalib.cc | 9 +++++++++ src/image/PhotoCalib.cc | 10 ++++++++++ tests/test_photoCalib.py | 14 ++++++++++++++ 4 files changed, 50 insertions(+) diff --git a/include/lsst/afw/image/PhotoCalib.h b/include/lsst/afw/image/PhotoCalib.h index 6d5168972a..7472376eb0 100644 --- a/include/lsst/afw/image/PhotoCalib.h +++ b/include/lsst/afw/image/PhotoCalib.h @@ -389,6 +389,23 @@ class PhotoCalib final : public table::io::PersistableFacade, public /// @overload magnitudeToInstFlux(double, lsst::geom::Point const &) const; double magnitudeToInstFlux(double magnitude) const; + /** + * Convert nanojansky to instFlux (ADU). + * + * If passed point, use the exact calculation at that point, otherwise, use the mean scaling factor. + * + * Useful for computing expected instrumental flux from reference catalog sources. + * + * @param[in] nanojansky The flux in nanojanksies to convert. + * @param[in] point The position that flux is to be converted at. + * + * @returns Source instFlux in ADU. + */ + double nanojanskyToInstFlux(double nanojansky, lsst::geom::Point const &point) const; + + /// @overload nanojanskyToInstFlux(double, lsst::geom::Point const &) const; + double nanojanskyToInstFlux(double nanojansky) const; + /** * Get the mean photometric calibration. * diff --git a/python/lsst/afw/image/_photoCalib.cc b/python/lsst/afw/image/_photoCalib.cc index 89cf09d88b..beffbe7ca9 100644 --- a/python/lsst/afw/image/_photoCalib.cc +++ b/python/lsst/afw/image/_photoCalib.cc @@ -159,6 +159,15 @@ void declarePhotoCalib(lsst::utils::python::WrapperCollection &wrappers) { py::overload_cast(&PhotoCalib::magnitudeToInstFlux, py::const_), "instFlux"_a); + /* from nanojansky. */ + cls.def("nanojanskyToInstFlux", + py::overload_cast const &>( + &PhotoCalib::nanojanskyToInstFlux, py::const_), + "nanojansky"_a, "point"_a); + cls.def("nanojanskyToInstFlux", + py::overload_cast(&PhotoCalib::nanojanskyToInstFlux, py::const_), + "nanojansky"_a); + /* utilities */ cls.def("getCalibrationMean", &PhotoCalib::getCalibrationMean); cls.def("getCalibrationErr", &PhotoCalib::getCalibrationErr); diff --git a/src/image/PhotoCalib.cc b/src/image/PhotoCalib.cc index 8948729554..d9a682fb12 100644 --- a/src/image/PhotoCalib.cc +++ b/src/image/PhotoCalib.cc @@ -65,6 +65,8 @@ double toInstFluxFromMagnitude(double magnitude, double scale) { return utils::ABMagnitudeToNanojansky(magnitude) / scale; } +double toInstFluxFromNanojansky(double nanojansky, double scale) { return nanojansky / scale; } + double toNanojanskyErr(double instFlux, double instFluxErr, double scale, double scaleErr, double nanojansky) { return std::abs(nanojansky) * hypot(instFluxErr / instFlux, scaleErr / scale); @@ -219,6 +221,14 @@ double PhotoCalib::magnitudeToInstFlux(double magnitude, lsst::geom::Point const &point) const { + return toInstFluxFromNanojansky(nanojansky, evaluate(point)); +} + std::shared_ptr PhotoCalib::computeScaledCalibration() const { return *(_calibration) / _calibrationMean; } diff --git a/tests/test_photoCalib.py b/tests/test_photoCalib.py index d7fb9174f9..2108b8fff4 100644 --- a/tests/test_photoCalib.py +++ b/tests/test_photoCalib.py @@ -258,6 +258,20 @@ def _testPhotoCalibCenter(self, photoCalib, calibrationErr): photoCalib.magnitudeToInstFlux(mag2, self.pointXShift), rtol=1e-15) + # test reverse conversion: nanojansky to instFlux (no position specified) + self.assertFloatsAlmostEqual(self.instFlux1, photoCalib.nanojanskyToInstFlux(self.flux1)) + self.assertFloatsAlmostEqual(self.instFlux2, photoCalib.nanojanskyToInstFlux(self.flux2), rtol=1e-15) + + # test round-tripping instFlux->nanojansky->instFlux (position specified) + flux = photoCalib.instFluxToNanojansky(self.instFlux1, self.pointXShift) + self.assertFloatsAlmostEqual(self.instFlux1, + photoCalib.nanojanskyToInstFlux(flux, self.pointXShift), + rtol=1e-15) + flux2 = photoCalib.instFluxToNanojansky(self.instFlux2, self.pointXShift) + self.assertFloatsAlmostEqual(self.instFlux2, + photoCalib.nanojanskyToInstFlux(flux2, self.pointXShift), + rtol=1e-15) + # test round-tripping arrays (position specified) instFlux1Array = np.full(10, self.instFlux1) instFlux2Array = np.full(10, self.instFlux2) From deb3fadd64b2835c60d4902d9dc29ecec1edfe62 Mon Sep 17 00:00:00 2001 From: John Parejko Date: Mon, 10 Jun 2024 16:04:05 -0700 Subject: [PATCH 2/2] Fix typos in variable names --- python/lsst/afw/image/_photoCalib.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/lsst/afw/image/_photoCalib.cc b/python/lsst/afw/image/_photoCalib.cc index beffbe7ca9..d551463e9c 100644 --- a/python/lsst/afw/image/_photoCalib.cc +++ b/python/lsst/afw/image/_photoCalib.cc @@ -154,10 +154,10 @@ void declarePhotoCalib(lsst::utils::python::WrapperCollection &wrappers) { cls.def("magnitudeToInstFlux", py::overload_cast const &>( &PhotoCalib::magnitudeToInstFlux, py::const_), - "instFlux"_a, "point"_a); + "magnitude"_a, "point"_a); cls.def("magnitudeToInstFlux", py::overload_cast(&PhotoCalib::magnitudeToInstFlux, py::const_), - "instFlux"_a); + "magnitude"_a); /* from nanojansky. */ cls.def("nanojanskyToInstFlux",