From 4046203d64f566e12030778fda016a264a8f13b2 Mon Sep 17 00:00:00 2001 From: Mihai Cara Date: Mon, 19 Feb 2024 02:17:02 -0500 Subject: [PATCH 1/3] Change warning type for some kernels. Add unit tests for flux conservation --- CHANGES.rst | 8 ++ drizzle/tests/test_drizzle.py | 144 ++++++++++++++++++++++++++++++++++ src/cdrizzleapi.c | 2 +- 3 files changed, 153 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 47968bb..e9bccb4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,14 @@ Release Notes ============= + +1.15.1 (Unreleased) +=================== + +- Fixed the warning type for the "gaussian", "lanczos2", and "lanczos3" kernels + (``DeprecationWarning`` to ``Warning``). [#141] + + 1.15.0 (2024-02-16) =================== diff --git a/drizzle/tests/test_drizzle.py b/drizzle/tests/test_drizzle.py index 4bfefbf..b07aa65 100644 --- a/drizzle/tests/test_drizzle.py +++ b/drizzle/tests/test_drizzle.py @@ -642,3 +642,147 @@ def test_context_planes(): driz.add_image(image, inwcs) assert driz.outcon.shape == (2, 10, 10) + + +@pytest.mark.parametrize( + 'kernel,conserves', + [ + ('square', True), + ('point', True), + ('turbo', True), + pytest.param( + 'lanczos2', + False, + marks=pytest.mark.xfail(reason='Not a flux-conserving kernel') + ), + pytest.param( + 'lanczos3', + False, + marks=pytest.mark.xfail(reason='Not a flux-conserving kernel') + ), + pytest.param( + 'gaussian', + False, + marks=pytest.mark.xfail(reason='Not a flux-conserving kernel') + ), + ], +) +def test_flux_conservation_nondistorted(kernel, conserves): + + n = 200 + in_shape = (n, n) + + # input coordinate grid: + y, x = np.indices(in_shape, dtype=np.float64) + + # simulate a gaussian "star": + fwhm = 2.9 + x0 = 50.0 + y0 = 68.0 + sig = fwhm / (2.0 * np.sqrt(2.0 * np.log(2.0 * fwhm))) + sig2 = sig * sig + star = np.exp(-0.5 / sig2 * ((x.astype(np.float32) - x0)**2 + (y.astype(np.float32) - y0)**2)) + in_sci = (star / np.sum(star)).astype(np.float32) # normalize to 1 + in_wht = np.ones(in_shape, dtype=np.float32) + + # linear shift: + xp = x + 0.5 + yp = y + 0.2 + + pixmap = np.dstack([xp, yp]) + + out_shape = (int(yp.max()) + 1, int(xp.max()) + 1) + # make sure distorion is not moving flux out of the image towards negative + # coordinates (just because of the simple way of how we account for output + # image size) + assert np.min(xp) > -0.5 and np.min(yp) > -0.5 + + out_sci = np.zeros(out_shape, dtype=np.float32) + out_ctx = np.zeros(out_shape, dtype=np.int32) + out_wht = np.zeros(out_shape, dtype=np.float32) + + cdrizzle.tdriz( + in_sci, in_wht, pixmap, out_sci, out_wht, out_ctx, + pixfrac=1.0, scale=1.0, kernel=kernel, in_units="cps", + expscale=1.0, wtscale=1.0 + ) + + assert np.allclose( + np.sum(out_sci * out_wht), + np.sum(in_sci), + atol=0.0, + rtol=0.0001 + ) + +@pytest.mark.parametrize( + 'kernel,conserves', + [ + ('square', True), + ('point', True), + ('turbo', True), + pytest.param( + 'lanczos2', + False, + marks=pytest.mark.xfail(reason='Not a flux-conserving kernel') + ), + pytest.param( + 'lanczos3', + False, + marks=pytest.mark.xfail(reason='Not a flux-conserving kernel') + ), + pytest.param( + 'gaussian', + False, + marks=pytest.mark.xfail(reason='Not a flux-conserving kernel') + ), + ], +) +def test_flux_conservation_distorted(kernel, conserves): + + n = 200 + in_shape = (n, n) + + # input coordinate grid: + y, x = np.indices(in_shape, dtype=np.float64) + + # simulate a gaussian "star": + fwhm = 2.9 + x0 = 50.0 + y0 = 68.0 + sig = fwhm / (2.0 * np.sqrt(2.0 * np.log(2.0 * fwhm))) + sig2 = sig * sig + star = np.exp(-0.5 / sig2 * ((x.astype(np.float32) - x0)**2 + (y.astype(np.float32) - y0)**2)) + in_sci = (star / np.sum(star)).astype(np.float32) # normalize to 1 + in_wht = np.ones(in_shape, dtype=np.float32) + + # linear shift: + xp = x + 0.5 + yp = y + 0.2 + # add distortion: + xp += 1e-4 * x**2 + 1e-5 * x * y + yp += 1e-3 * y**2 - 2e-5 * x * y + + pixmap = np.dstack([xp, yp]) + + out_shape = (int(yp.max()) + 1, int(xp.max()) + 1) + # make sure distorion is not moving (pixels with) flux out of the image + # towards negative coordinates (just because of the simple way of how we + # account for output image size): + assert np.min(xp) > -0.5 and np.min(yp) > -0.5 + + out_sci = np.zeros(out_shape, dtype=np.float32) + out_ctx = np.zeros(out_shape, dtype=np.int32) + out_wht = np.zeros(out_shape, dtype=np.float32) + + cdrizzle.tdriz( + in_sci, in_wht, pixmap, out_sci, out_wht, out_ctx, + pixfrac=1.0, scale=1.0, kernel=kernel, in_units="cps", + expscale=1.0, wtscale=1.0 + ) + + assert np.allclose( + np.sum(out_sci * out_wht), + np.sum(in_sci), + atol=0.0, + rtol=0.0001 + ) diff --git a/src/cdrizzleapi.c b/src/cdrizzleapi.c index 930a748..95c4772 100644 --- a/src/cdrizzleapi.c +++ b/src/cdrizzleapi.c @@ -196,7 +196,7 @@ tdriz(PyObject *obj UNUSED_PARAM, PyObject *args, PyObject *keywords) kernel_str) < 1) { strcpy(warn_msg, "Selected kernel is not a flux-conserving kernel."); } - PyErr_WarnEx(PyExc_DeprecationWarning, warn_msg, 1); + PyErr_WarnEx(PyExc_Warning, warn_msg, 1); } if (pfract <= 0.001){ From 29a4974e6a2d791887effa07de4974aeb7c13a25 Mon Sep 17 00:00:00 2001 From: Mihai Cara Date: Mon, 19 Feb 2024 19:49:32 -0500 Subject: [PATCH 2/3] address ruff rules --- drizzle/tests/test_drizzle.py | 48 +++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/drizzle/tests/test_drizzle.py b/drizzle/tests/test_drizzle.py index b07aa65..5f955ef 100644 --- a/drizzle/tests/test_drizzle.py +++ b/drizzle/tests/test_drizzle.py @@ -653,22 +653,21 @@ def test_context_planes(): pytest.param( 'lanczos2', False, - marks=pytest.mark.xfail(reason='Not a flux-conserving kernel') + marks=pytest.mark.xfail(reason='Not a flux-conserving kernel'), ), pytest.param( 'lanczos3', False, - marks=pytest.mark.xfail(reason='Not a flux-conserving kernel') + marks=pytest.mark.xfail(reason='Not a flux-conserving kernel'), ), pytest.param( 'gaussian', False, - marks=pytest.mark.xfail(reason='Not a flux-conserving kernel') + marks=pytest.mark.xfail(reason='Not a flux-conserving kernel'), ), ], ) def test_flux_conservation_nondistorted(kernel, conserves): - n = 200 in_shape = (n, n) @@ -702,16 +701,25 @@ def test_flux_conservation_nondistorted(kernel, conserves): out_wht = np.zeros(out_shape, dtype=np.float32) cdrizzle.tdriz( - in_sci, in_wht, pixmap, out_sci, out_wht, out_ctx, - pixfrac=1.0, scale=1.0, kernel=kernel, in_units="cps", - expscale=1.0, wtscale=1.0 + in_sci, + in_wht, + pixmap, + out_sci, + out_wht, + out_ctx, + pixfrac=1.0, + scale=1.0, + kernel=kernel, + in_units="cps", + expscale=1.0, + wtscale=1.0, ) assert np.allclose( np.sum(out_sci * out_wht), np.sum(in_sci), atol=0.0, - rtol=0.0001 + rtol=0.0001, ) @pytest.mark.parametrize( @@ -723,22 +731,21 @@ def test_flux_conservation_nondistorted(kernel, conserves): pytest.param( 'lanczos2', False, - marks=pytest.mark.xfail(reason='Not a flux-conserving kernel') + marks=pytest.mark.xfail(reason='Not a flux-conserving kernel'), ), pytest.param( 'lanczos3', False, - marks=pytest.mark.xfail(reason='Not a flux-conserving kernel') + marks=pytest.mark.xfail(reason='Not a flux-conserving kernel'), ), pytest.param( 'gaussian', False, - marks=pytest.mark.xfail(reason='Not a flux-conserving kernel') + marks=pytest.mark.xfail(reason='Not a flux-conserving kernel'), ), ], ) def test_flux_conservation_distorted(kernel, conserves): - n = 200 in_shape = (n, n) @@ -775,14 +782,23 @@ def test_flux_conservation_distorted(kernel, conserves): out_wht = np.zeros(out_shape, dtype=np.float32) cdrizzle.tdriz( - in_sci, in_wht, pixmap, out_sci, out_wht, out_ctx, - pixfrac=1.0, scale=1.0, kernel=kernel, in_units="cps", - expscale=1.0, wtscale=1.0 + in_sci, + in_wht, + pixmap, + out_sci, + out_wht, + out_ctx, + pixfrac=1.0, + scale=1.0, + kernel=kernel, + in_units="cps", + expscale=1.0, + wtscale=1.0, ) assert np.allclose( np.sum(out_sci * out_wht), np.sum(in_sci), atol=0.0, - rtol=0.0001 + rtol=0.0001, ) From 27496e46180030c83ed5065752e58fe1f2a35b3b Mon Sep 17 00:00:00 2001 From: Mihai Cara Date: Fri, 23 Feb 2024 00:34:46 -0500 Subject: [PATCH 3/3] simplify test setup --- drizzle/tests/test_drizzle.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drizzle/tests/test_drizzle.py b/drizzle/tests/test_drizzle.py index 5f955ef..99fd1e0 100644 --- a/drizzle/tests/test_drizzle.py +++ b/drizzle/tests/test_drizzle.py @@ -645,29 +645,26 @@ def test_context_planes(): @pytest.mark.parametrize( - 'kernel,conserves', + 'kernel', [ - ('square', True), - ('point', True), - ('turbo', True), + 'square', + 'point', + 'turbo', pytest.param( 'lanczos2', - False, marks=pytest.mark.xfail(reason='Not a flux-conserving kernel'), ), pytest.param( 'lanczos3', - False, marks=pytest.mark.xfail(reason='Not a flux-conserving kernel'), ), pytest.param( 'gaussian', - False, marks=pytest.mark.xfail(reason='Not a flux-conserving kernel'), ), ], ) -def test_flux_conservation_nondistorted(kernel, conserves): +def test_flux_conservation_nondistorted(kernel): n = 200 in_shape = (n, n) @@ -723,29 +720,26 @@ def test_flux_conservation_nondistorted(kernel, conserves): ) @pytest.mark.parametrize( - 'kernel,conserves', + 'kernel', [ - ('square', True), - ('point', True), - ('turbo', True), + 'square', + 'point', + 'turbo', pytest.param( 'lanczos2', - False, marks=pytest.mark.xfail(reason='Not a flux-conserving kernel'), ), pytest.param( 'lanczos3', - False, marks=pytest.mark.xfail(reason='Not a flux-conserving kernel'), ), pytest.param( 'gaussian', - False, marks=pytest.mark.xfail(reason='Not a flux-conserving kernel'), ), ], ) -def test_flux_conservation_distorted(kernel, conserves): +def test_flux_conservation_distorted(kernel): n = 200 in_shape = (n, n)