Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multi-source flux conservation test #177

Merged
merged 2 commits into from
Mar 18, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions drizzle/tests/data/nrcb5_output_wcs_psr_0p55.hdr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
NAXIS = 2 / number of array dimensions
NAXIS1 = 3774
NAXIS2 = 3760
WCSAXES = 2 / Number of coordinate axes
WCSNAX1 = 3774
WCSNAX2 = 3760
WCSNAME = 'NRCB5-resampled-psr=0.55'
CRPIX1 = 1881.5 / Pixel coordinate of reference point
CRPIX2 = 1888.5 / Pixel coordinate of reference point
CD1_1 = -3.2887784263502E-06 / Coordinate transformation matrix element
CD1_2 = 9.0452124171947E-06 / Coordinate transformation matrix element
CD2_1 = 9.0452124170788E-06 / Coordinate transformation matrix element
CD2_2 = 3.2887784263487E-06 / Coordinate transformation matrix element
CUNIT1 = 'deg' / Units of coordinate increment and value
CUNIT2 = 'deg' / Units of coordinate increment and value
CTYPE1 = 'RA---TAN' / Right ascension, gnomonic projection
CTYPE2 = 'DEC--TAN' / Declination, gnomonic projection
CRVAL1 = 34.462181668953 / [deg] Coordinate value at reference point
CRVAL2 = -5.2370769627861 / [deg] Coordinate value at reference point
LONPOLE = 180.0 / [deg] Native longitude of celestial pole
LATPOLE = -5.2370769627861 / [deg] Native latitude of celestial pole
MJDREF = 0.0 / [d] MJD of fiducial time
RADESYS = 'ICRS' / Equatorial coordinate system
COMMENT pixel scale ratio: 0.55
END
25 changes: 25 additions & 0 deletions drizzle/tests/data/nrcb5_output_wcs_psr_1p0.hdr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
NAXIS = 2 / number of array dimensions
NAXIS1 = 2076
NAXIS2 = 2068
WCSAXES = 2 / Number of coordinate axes
WCSNAX1 = 2076
WCSNAX2 = 2068
WCSNAME = 'NRCB5-resampled-psr=1.0'
CRPIX1 = 1035.5 / Pixel coordinate of reference point
CRPIX2 = 1039.5 / Pixel coordinate of reference point
CD1_1 = -5.979597265673E-06 / Coordinate transformation matrix element
CD1_2 = 1.6445840712429E-05 / Coordinate transformation matrix element
CD2_1 = 1.644584071221E-05 / Coordinate transformation matrix element
CD2_2 = 5.9795972656895E-06 / Coordinate transformation matrix element
CUNIT1 = 'deg' / Units of coordinate increment and value
CUNIT2 = 'deg' / Units of coordinate increment and value
CTYPE1 = 'RA---TAN' / Right ascension, gnomonic projection
CTYPE2 = 'DEC--TAN' / Declination, gnomonic projection
CRVAL1 = 34.462186510947 / [deg] Coordinate value at reference point
CRVAL2 = -5.237071020123 / [deg] Coordinate value at reference point
LONPOLE = 180.0 / [deg] Native longitude of celestial pole
LATPOLE = -5.237071020123 / [deg] Native latitude of celestial pole
MJDREF = 0.0 / [d] MJD of fiducial time
RADESYS = 'ICRS' / Equatorial coordinate system
COMMENT pixel scale ratio: 1.0
END
25 changes: 25 additions & 0 deletions drizzle/tests/data/nrcb5_output_wcs_psr_1p2.hdr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
NAXIS = 2 / number of array dimensions
NAXIS1 = 1730
NAXIS2 = 1723
WCSAXES = 2 / Number of coordinate axes
WCSNAX1 = 1730
WCSNAX2 = 1723
WCSNAME = 'NRCB5-resampled-psr=1.2'
CRPIX1 = 862.5 / Pixel coordinate of reference point
CRPIX2 = 866.5 / Pixel coordinate of reference point
CD1_1 = -7.1755169028951E-06 / Coordinate transformation matrix element
CD1_2 = 1.9735008788012E-05 / Coordinate transformation matrix element
CD2_1 = 1.9735008787734E-05 / Coordinate transformation matrix element
CD2_2 = 7.1755169029068E-06 / Coordinate transformation matrix element
CUNIT1 = 'deg' / Units of coordinate increment and value
CUNIT2 = 'deg' / Units of coordinate increment and value
CTYPE1 = 'RA---TAN' / Right ascension, gnomonic projection
CTYPE2 = 'DEC--TAN' / Declination, gnomonic projection
CRVAL1 = 34.46219236569 / [deg] Coordinate value at reference point
CRVAL2 = -5.2370819342517 / [deg] Coordinate value at reference point
LONPOLE = 180.0 / [deg] Native longitude of celestial pole
LATPOLE = -5.2370819342517 / [deg] Native latitude of celestial pole
MJDREF = 0.0 / [d] MJD of fiducial time
RADESYS = 'ICRS' / Equatorial coordinate system
COMMENT pixel scale ratio: 1.2
END
63 changes: 63 additions & 0 deletions drizzle/tests/data/nrcb5_sip_wcs.hdr
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
NAXIS = 2 / number of array dimensions
NAXIS1 = 2048
NAXIS2 = 2048
WCSAXES = 2 / Number of coordinate axes
WCSNAX1 = 2048
WCSNAX2 = 2048
WCSNAME = 'NCRB5-distorted'
CRPIX1 = 1024.5 / Pixel coordinate of reference point
CRPIX2 = 1024.5 / Pixel coordinate of reference point
CD1_1 = -5.9503404343539E-06 / Coordinate transformation matrix element
CD1_2 = 1.648275705369E-05 / Coordinate transformation matrix element
CD2_1 = 1.6413012543338E-05 / Coordinate transformation matrix element
CD2_2 = 6.0004968333255E-06 / Coordinate transformation matrix element
CUNIT1 = 'deg' / Units of coordinate increment and value
CUNIT2 = 'deg' / Units of coordinate increment and value
CTYPE1 = 'RA---TAN-SIP' / Right ascension, gnomonic projection
CTYPE2 = 'DEC--TAN-SIP' / Declination, gnomonic projection
CRVAL1 = 34.462141245715 / [deg] Coordinate value at reference point
CRVAL2 = -5.2370131013006 / [deg] Coordinate value at reference point
LONPOLE = 180.0 / [deg] Native longitude of celestial pole
LATPOLE = -5.2370131013006 / [deg] Native latitude of celestial pole
MJDREF = 0.0 / [d] MJD of fiducial time
RADESYS = 'ICRS' / Equatorial coordinate system
A_ORDER = 5
B_ORDER = 5
A_0_2 = 1.61277115051773E-06
A_0_3 = 1.73577114462509E-11
A_0_4 = 5.89952689299414E-15
A_0_5 = -9.2706016645490E-18
A_1_1 = -1.1751937758814E-05
A_1_2 = 1.73036803751169E-09
A_1_3 = -3.1289934897622E-14
A_1_4 = 3.06218671532168E-17
A_2_0 = -1.8926777088332E-06
A_2_1 = 1.03291495279138E-10
A_2_2 = 2.03368375378982E-14
A_2_3 = -4.4162900401209E-17
A_3_0 = 1.67914343216288E-09
A_3_1 = -5.4435048958724E-14
A_3_2 = 9.46275771964339E-17
A_4_0 = 7.41603692584866E-15
A_4_1 = -9.7176613274953E-18
A_5_0 = -2.6162006188010E-17
B_0_2 = -6.9663104269845E-06
B_0_3 = 1.69597092451221E-09
B_0_4 = -2.6284874597164E-14
B_0_5 = 3.79795310696409E-17
B_1_1 = -3.6577740533158E-06
B_1_2 = 8.02672477273099E-11
B_1_3 = -1.7478642466998E-14
B_1_4 = -2.7372755032689E-18
B_2_0 = 4.92018167812442E-06
B_2_1 = 1.69745602106894E-09
B_2_2 = 6.81819605498880E-15
B_2_3 = 7.20352111485166E-17
B_3_0 = 3.98424806886705E-12
B_3_1 = -5.3941905107867E-15
B_3_2 = -3.2098483024471E-17
B_4_0 = 2.16072939656730E-14
B_4_1 = -2.7836646857682E-17
B_5_0 = -1.8084499231992E-17
COMMENT Converted to FITS from JWST NRCB5 gwcs
END
91 changes: 61 additions & 30 deletions drizzle/tests/helpers.py
Original file line number Diff line number Diff line change
@@ -56,40 +56,71 @@ def wcs_from_file(filename, ext=None, return_data=False, crpix_shift=None,
"""
full_file_name = os.path.join(DATA_DIR, filename)
path = os.path.join(DATA_DIR, full_file_name)
with fits.open(path) as hdu:
if ext is None:
for k, u in enumerate(hdu):

def get_shape(hdr):
naxis1 = hdr.get("WCSNAX1", hdr.get("NAXIS1"))
naxis2 = hdr.get("WCSNAX2", hdr.get("NAXIS2"))
if naxis1 is None or naxis2 is None:
return None
return (naxis2, naxis1)

def data_from_hdr(hdr, data=None, shape=None):
if data is not None:
return data
bitpix = hdr.get("BITPIX", -32)
dtype = fits.hdu.BITPIX2DTYPE[bitpix]
shape = get_shape(hdr) or shape
if shape is None:
return None
return np.zeros(shape, dtype=dtype)

if os.path.splitext(filename)[1] in [".hdr", ".txt"]:
hdul = None
hdr = fits.Header.fromfile(
path,
sep='\n',
endcard=False,
padding=False
)

else:
with fits.open(path) as fits_hdul:
hdul = fits.HDUList([hdu.copy() for hdu in fits_hdul])

if ext is None and hdul is not None:
for k, u in enumerate(hdul):
if "CTYPE1" in u.header:
ext = k
break

hdr = hdu[ext].header
naxis1 = hdr.get("WCSNAX1", hdr.get("NAXIS1"))
naxis2 = hdr.get("WCSNAX2", hdr.get("NAXIS2"))
if naxis1 is not None and naxis2 is not None:
shape = (naxis2, naxis1)
if hdu[ext].data is None:
hdu[ext].data = np.zeros(shape, dtype=np.float32)
else:
shape = None

if crpix_shift is not None and "CRPIX1" in hdr:
hdr["CRPIX1"] += crpix_shift[0]
hdr["CRPIX2"] += crpix_shift[1]

result = fits_wcs.WCS(hdr, hdu)
result.array_shape = shape

if wcs_type == "gwcs":
result = _gwcs_from_hst_fits_wcs(result)

if return_data:
result = (result, )
if not isinstance(return_data, (list, tuple)):
return_data = [ext]
for ext in return_data:
data = (hdu[ext].data, )
result = result + data
hdr = hdul[ext].header

if crpix_shift is not None and "CRPIX1" in hdr:
hdr["CRPIX1"] += crpix_shift[0]
hdr["CRPIX2"] += crpix_shift[1]

result = fits_wcs.WCS(hdr, hdul)
shape = get_shape(hdr)
result.array_shape = shape

if wcs_type == "gwcs":
result = _gwcs_from_hst_fits_wcs(result)

if return_data:
if hdul is None:
data = data_from_hdr(hdr, data=None, shape=shape)
return (result, data)

result = (result, )
if not isinstance(return_data, (list, tuple)):
return_data = [ext]
for ext in return_data:
data = data_from_hdr(
hdul[ext].header,
data=hdul[ext].data,
shape=shape
)
result = result + (data, )

return result

97 changes: 97 additions & 0 deletions drizzle/tests/test_resample.py
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
import pytest

from astropy import wcs
from astropy.convolution import Gaussian2DKernel
from drizzle import cdrizzle, resample, utils

from .helpers import wcs_from_file
@@ -191,6 +192,56 @@ def make_grid_image(shape, spacing, value):
return output_image


@pytest.fixture(scope="module")
def nrcb5_stars():
full_file_name = os.path.join(DATA_DIR, "nrcb5_sip_wcs.hdr")
path = os.path.join(DATA_DIR, full_file_name)

wcs, data = wcs_from_file(path, return_data=True)
dq = np.zeros(data.shape, dtype=np.int32)

np.random.seed(0)

patch_size = 21
p2 = patch_size // 2
# add border so that resampled partial pixels can be isolated
# in the segmentation:
border = 4
pwb = patch_size + border

fwhm2sigma = 2.0 * math.sqrt(2.0 * math.log(2.0))

ny, nx = data.shape

stars = []

for yc in range(border + p2, ny - pwb, pwb):
for xc in range(border + p2, nx - pwb, pwb):
sl = np.s_[yc - p2:yc + p2 + 1, xc - p2:xc + p2 + 1]
flux = 1.0 + 99.0 * np.random.random()
if np.random.random() > 0.7:
# uniform image
psf = np.full((patch_size, patch_size), flux)
else:
# "star":
fwhm = 1.5 + 1.5 * np.random.random()
sigma = fwhm / fwhm2sigma

psf = flux * Gaussian2DKernel(
sigma,
x_size=patch_size,
y_size=patch_size
).array

flux = psf.sum()

data[sl] = psf
dq[sl] = 0
stars.append((xc, yc, flux, sl))

return data, dq, stars, wcs


def test_drizzle_defaults():
n = 200
in_shape = (n, n)
@@ -821,6 +872,52 @@ def test_flux_conservation_distorted(kernel, fc):
)


@pytest.mark.parametrize("kernel", ["square", "turbo", "point"])
@pytest.mark.parametrize("pscale_ratio", [0.55, 1.0, 1.2])
def test_flux_conservation_distorted_distributed_sources(nrcb5_stars, kernel, pscale_ratio):
""" test aperture photometry """
insci, dq, stars, wcs = nrcb5_stars

suffix = f"{pscale_ratio}".replace(".", "p")
output_wcs = wcs_from_file(f"nrcb5_output_wcs_psr_{suffix}.hdr")

pixmap = utils.calc_pixmap(
wcs,
output_wcs,
wcs.array_shape,
)

inwht = np.ones_like(insci) * (1 - dq)

driz = resample.Drizzle(
kernel=kernel,
out_shape=output_wcs.array_shape,
fillval=0.0,
)
driz.add_image(
insci,
exptime=1.0,
pixmap=pixmap,
weight_map=inwht,
scale=1,
)

# for efficiency, instead of doing this patch-by-patch,
# multiply resampled data by resampled image weight
out_data = driz.out_img * driz.out_wht

dim3 = (slice(None, None, None), )
for _, _, fin, sl in stars:
xyout = pixmap[sl + dim3]
xmin = int(np.floor(xyout[:, :, 0].min() - 0.5))
xmax = int(np.ceil(xyout[:, :, 0].max() + 1.5))
ymin = int(np.floor(xyout[:, :, 1].min() - 0.5))
ymax = int(np.ceil(xyout[:, :, 1].max() + 1.5))
fout = np.nansum(out_data[ymin:ymax, xmin:xmax])

assert np.allclose(fin, fout, rtol=1.0e-6, atol=0.0)


def test_drizzle_exptime():
n = 200
in_shape = (n, n)
Loading