def test_align_wcs_1im_no_ref(mock_fits_wcs): xy = np.array([[1, 0], [2, 3], [3, 1], [4, 5]]) imcat = Table(xy, names=('x', 'y')) tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat}) with pytest.raises(ValueError) as e: align_wcs(tpwcs, refcat=None, fitgeom='shift', match=None) assert e.value.args[0] == "Too few input images (or groups of images)."
def test_align_wcs_tpwcs_refcat_must_have_catalog(mock_fits_wcs): xy = np.array([[1, 0], [2, 3], [3, 1], [4, 5]]) imcat = Table(xy, names=('x', 'y')) tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat}) reftpwcs = FITSWCS(mock_fits_wcs) with pytest.raises(ValueError) as e: align_wcs(tpwcs, refcat=reftpwcs) assert (e.value.args[0] == "Reference 'TPWCS' must contain a catalog.")
def test_align_wcs_unknown_fitgeom(mock_fits_wcs): xy = np.array([[1, 0], [2, 3], [3, 1], [4, 5]]) imcat = Table(xy, names=('x', 'y')) tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat}) with pytest.raises(ValueError) as e: align_wcs(tpwcs, fitgeom='unknown') assert (e.value.args[0] == "Unsupported 'fitgeom'. Valid values are: " "'shift', 'rshift', 'rscale', or 'general'")
def test_align_wcs_wrong_refcat_type(mock_fits_wcs): xy = np.array([[1, 0], [2, 3], [3, 1], [4, 5]]) imcat = Table(xy, names=('x', 'y')) tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat}) with pytest.raises(TypeError) as e: align_wcs(tpwcs, refcat=xy, fitgeom='shift', match=None) assert (e.value.args[0] == "Unsupported 'refcat' type. Supported 'refcat' " "types are 'tweakwcs.tpwcs.TPWCS' and 'astropy.table.Table'")
def test_align_wcs_no_radec_in_refcat(mock_fits_wcs): xy = np.array([[1, 0], [2, 3], [3, 1], [4, 5]]) imcat = Table(xy, names=('x', 'y')) refcat = Table(xy, names=('x', 'y')) tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat}) with pytest.raises(KeyError) as e: align_wcs(tpwcs, refcat, fitgeom='shift', match=None) assert (e.value.args[0] == "Reference catalogs *must* contain *both* 'RA' " "and 'DEC' columns.")
def test_fit_wcs_empty_cat(empty_refcat, empty_imcat, mock_fits_wcs): tpwcs = FITSWCS( mock_fits_wcs, meta={'catalog': Table([[], []], names=('x', 'y')), 'group_id': 1} ) with pytest.raises(ValueError) as e: align_wcs([tpwcs, tpwcs, tpwcs]) assert e.value.args[0] == ("Too few input images (or groups of images) " "with non-empty catalogs.")
def test_fit_drop_empty(mock_fits_wcs): t0 = Table([[], []], names=('x', 'y')) t1 = Table([[1], [3]], names=('x', 'y')) wcscats = [ FITSWCS( copy.deepcopy(mock_fits_wcs), meta={'catalog': t0.copy(), 'group_id': 1} ), FITSWCS( copy.deepcopy(mock_fits_wcs), meta={'catalog': t1.copy(), 'group_id': 2} ), FITSWCS( copy.deepcopy(mock_fits_wcs), meta={'catalog': t0.copy(), 'group_id': 2} ), FITSWCS( copy.deepcopy(mock_fits_wcs), meta={'catalog': t0.copy(), 'group_id': 3} ), FITSWCS( copy.deepcopy(mock_fits_wcs), meta={'catalog': t0.copy(), 'group_id': 3} ), FITSWCS( copy.deepcopy(mock_fits_wcs), meta={'catalog': t1.copy(), 'group_id': 4} ), FITSWCS( copy.deepcopy(mock_fits_wcs), meta={'catalog': t1.copy(), 'group_id': 4} ) ] align_wcs(wcscats, fitgeom='shift') status = [w.meta.get('fit_info')['status'] for w in wcscats] assert status[0] == 'FAILED: empty source catalog' assert status[3] == 'FAILED: empty source catalog' assert status[4] == 'FAILED: empty source catalog' if status[1] == 'SUCCESS': assert status[2] == 'SUCCESS' assert status[5] == 'REFERENCE' assert status[6] == 'REFERENCE' elif status[1] == 'REFERENCE': assert status[2] == 'REFERENCE' assert status[5] == 'SUCCESS' assert status[6] == 'SUCCESS' else: assert False
def test_align_wcs_refcat_from_imcat(mock_fits_wcs, enforce): shift = (12, -34) rot = (15, 17) scale = (1.0123, 0.9876) crpix = mock_fits_wcs.wcs.crpix - 1 xy = 1024 * np.random.random((100, 2)) m = build_fit_matrix(rot, scale) xyr = np.dot(xy - crpix, m) + crpix + shift imcat = Table(xy, names=('x', 'y')) refcat = Table(xyr, names=('x', 'y')) tpwcs1 = FITSWCS(mock_fits_wcs, meta={'catalog': imcat}) tpwcs2 = copy.deepcopy(tpwcs1) reftpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': refcat}) input_catalogs = [reftpwcs, tpwcs1, tpwcs2] status = align_wcs( input_catalogs, refcat=None, fitgeom='general', match=None, enforce_user_order=enforce, expand_refcat=True ) assert status for cat in input_catalogs: if cat.meta['fit_info']['status'] == 'REFERENCE': if enforce: assert cat is reftpwcs continue assert cat.meta['fit_info']['status'] == 'SUCCESS'
def test_align_wcs_simple_ref_image_general(shift, rot, scale, fitgeom, weighted, mock_fits_wcs): xy = 1024 * np.random.random((100, 2)) if weighted: w = np.ones((100, 1)) xy = np.hstack((xy, w)) names = ('x', 'y', 'weight') else: names = ('x', 'y') m = build_fit_matrix(rot, scale) xyr = np.dot(xy[:, :2], m.T) + shift imcat = Table(xy, names=names) radec = mock_fits_wcs.wcs_pix2world(xyr, 0) if weighted: radec = np.hstack((radec, w)) names = ('RA', 'DEC', 'weight') else: names = ('RA', 'DEC') refcat = Table(radec, names=names) tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat}) status = align_wcs(tpwcs, refcat, fitgeom=fitgeom, match=None) assert status assert tpwcs.meta['fit_info']['status'] == 'SUCCESS' assert tpwcs.meta['fit_info']['fitgeom'] == fitgeom assert np.allclose(tpwcs.meta['fit_info']['shift'], shift) assert np.allclose(tpwcs.meta['fit_info']['matrix'], m) assert np.allclose(tpwcs.meta['fit_info']['rot'], rot) assert tpwcs.meta['fit_info']['proper'] assert np.allclose(tpwcs.meta['fit_info']['scale'], scale) assert tpwcs.meta['fit_info']['rmse'] < 1.0e-8
def test_align_wcs_simple_twpwcs_ref(mock_fits_wcs): shift = (12, -34) rot = (15, 17) scale = (1.0123, 0.9876) xy = 1024 * np.random.random((100, 2)) m = build_fit_matrix(rot, scale) xyr = np.dot(xy, m.T) + shift imcat = Table(xy, names=('x', 'y')) refcat = Table(xyr, names=('x', 'y')) tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat}) reftpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': refcat}) status = align_wcs(tpwcs, reftpwcs, ref_tpwcs=tpwcs, fitgeom='general', match=None) assert status assert tpwcs.meta['fit_info']['status'] == 'SUCCESS' assert tpwcs.meta['fit_info']['fitgeom'] == 'general' assert np.allclose(tpwcs.meta['fit_info']['shift'], shift) assert np.allclose(tpwcs.meta['fit_info']['matrix'], m) assert np.allclose(tpwcs.meta['fit_info']['rot'], rot) assert tpwcs.meta['fit_info']['proper'] assert np.allclose(tpwcs.meta['fit_info']['scale'], scale) assert tpwcs.meta['fit_info']['rmse'] < 1.0e-8
def test_align_wcs_tpwcs_type(mock_fits_wcs): imcat = Table([[1, 2, 3, 4], [0, 3, 1, 5]], names=('x', 'y')) class WrongTPWCS: def __init__(self, wcs): self.wcs = wcs self.meta = {'catalog': imcat} err = ("Input 'wcscat' must be either a single TPWCS-derived " " object or a list of TPWCS-derived objects.") tpwcs = WrongTPWCS(mock_fits_wcs) with pytest.raises(TypeError) as e: align_wcs(tpwcs) assert (e.value.args[0] == err) with pytest.raises(TypeError) as e: align_wcs([tpwcs, tpwcs]) assert (e.value.args[0] == err)
def test_align_wcs_minobj(mock_fits_wcs): shift = (12, -34) rot = (15, 17) scale = (1.0123, 0.9876) crpix = mock_fits_wcs.wcs.crpix - 1 xy = 1024 * np.random.random((100, 2)) m = build_fit_matrix(rot, scale) xyr = np.dot(xy - crpix, m) + crpix + shift imcat = Table(xy, names=('x', 'y')) refcat = Table(xyr, names=('x', 'y')) tpwcs1 = FITSWCS(mock_fits_wcs, meta={'catalog': imcat}) reftpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': refcat}) status = align_wcs([reftpwcs, tpwcs1], refcat=None, fitgeom='general', minobj=200, match=None, enforce_user_order=True, expand_refcat=False) assert status assert reftpwcs.meta['fit_info']['status'] == 'REFERENCE' assert tpwcs1.meta['fit_info']['status'] == 'FAILED: not enough matches'
def process(self, input): try: images = datamodels.ModelContainer(input) except TypeError as e: e.args = ("Input to tweakreg must be a list of DataModels, an " "association, or an already open ModelContainer " "containing one or more DataModels.", ) + e.args[1:] raise e # Build the catalogs for input images for image_model in images: catalog = make_tweakreg_catalog(image_model, self.kernel_fwhm, self.snr_threshold, brightest=self.brightest, peakmax=self.peakmax) # filter out sources outside the image array if WCS validity # region is provided: wcs_bounds = image_model.meta.wcs.pixel_bounds if wcs_bounds is not None: ((xmin, xmax), (ymin, ymax)) = wcs_bounds xname = 'xcentroid' if 'xcentroid' in catalog.colnames else 'x' yname = 'ycentroid' if 'ycentroid' in catalog.colnames else 'y' x = catalog[xname] y = catalog[yname] mask = (x > xmin) & (x < xmax) & (y > ymin) & (y < ymax) catalog = catalog[mask] filename = image_model.meta.filename nsources = len(catalog) if nsources == 0: self.log.warning('No sources found in {}.'.format(filename)) else: self.log.info('Detected {} sources in {}.'.format( len(catalog), filename)) if self.save_catalogs: catalog_filename = filename.replace( '.fits', '_cat.{}'.format(self.catalog_format)) if self.catalog_format == 'ecsv': fmt = 'ascii.ecsv' elif self.catalog_format == 'fits': # NOTE: The catalog must not contain any 'None' values. # FITS will also not clobber existing files. fmt = 'fits' else: raise ValueError( '\'catalog_format\' must be "ecsv" or "fits".') catalog.write(catalog_filename, format=fmt, overwrite=True) self.log.info( 'Wrote source catalog: {}'.format(catalog_filename)) image_model.meta.tweakreg_catalog = catalog_filename image_model.catalog = catalog # Now use the catalogs for tweakreg if len(images) == 0: raise ValueError("Input must contain at least one image model.") # group images by their "group id": grp_img = images.models_grouped self.log.info('') self.log.info("Number of image groups to be aligned: {:d}.".format( len(grp_img))) self.log.info("Image groups:") if len(grp_img) == 1: self.log.info("* Images in GROUP 1:") for im in grp_img[0]: self.log.info(" {}".format(im.meta.filename)) self.log.info('') # we need at least two exposures to perform image alignment self.log.warning("At least two exposures are required for image " "alignment.") self.log.warning("Nothing to do. Skipping 'TweakRegStep'...") self.skip = True for model in images: model.meta.cal_step.tweakreg = "SKIPPED" return input # create a list of WCS-Catalog-Images Info and/or their Groups: imcats = [] for g in grp_img: if len(g) == 0: raise AssertionError("Logical error in the pipeline code.") else: group_name = _common_name(g) wcsimlist = list(map(self._imodel2wcsim, g)) self.log.info("* Images in GROUP '{}':".format(group_name)) for im in wcsimlist: im.meta['group_id'] = group_name self.log.info(" {}".format(im.meta['name'])) imcats.extend(wcsimlist) self.log.info('') # align images: tpmatch = TPMatch(searchrad=self.searchrad, separation=self.separation, use2dhist=self.use2dhist, tolerance=self.tolerance, xoffset=self.xoffset, yoffset=self.yoffset) try: align_wcs(imcats, refcat=None, enforce_user_order=self.enforce_user_order, expand_refcat=self.expand_refcat, minobj=self.minobj, match=tpmatch, fitgeom=self.fitgeometry, nclip=self.nclip, sigma=(self.sigma, 'rmse')) except ValueError as e: msg = e.args[0] if (msg == "Too few input images (or groups of images) with " "non-empty catalogs."): # we need at least two exposures to perform image alignment self.log.warning(msg) self.log.warning("At least two exposures are required for " "image alignment.") self.log.warning("Nothing to do. Skipping 'TweakRegStep'...") self.skip = True for model in images: model.meta.cal_step.tweakreg = "SKIPPED" return images else: raise e for imcat in imcats: imcat.meta['image_model'].meta.cal_step.tweakreg = 'COMPLETE' # retrieve fit status and update wcs if fit is successful: fit_info = imcat.meta.get('fit_info') if fit_info['status'] in 'SUCCESS': imcat.meta['image_model'].meta.wcs = imcat.wcs return images
def process(self, input): try: images = datamodels.ModelContainer(input) except TypeError as e: e.args = ("Input to tweakreg must be a list of DataModels, an " "association, or an already open ModelContainer " "containing one or more DataModels.", ) + e.args[1:] raise e if self.align_to_gaia: # Set expand_refcat to True to eliminate possibility of duplicate # entries when aligning to GAIA self.expand_refcat = True if len(images) == 0: raise ValueError("Input must contain at least one image model.") # Build the catalogs for input images for image_model in images: catalog = make_tweakreg_catalog(image_model, self.kernel_fwhm, self.snr_threshold, brightest=self.brightest, peakmax=self.peakmax) # filter out sources outside the WCS bounding box bb = image_model.meta.wcs.bounding_box if bb is not None: ((xmin, xmax), (ymin, ymax)) = bb xname = 'xcentroid' if 'xcentroid' in catalog.colnames else 'x' yname = 'ycentroid' if 'ycentroid' in catalog.colnames else 'y' x = catalog[xname] y = catalog[yname] mask = (x > xmin) & (x < xmax) & (y > ymin) & (y < ymax) catalog = catalog[mask] filename = image_model.meta.filename nsources = len(catalog) if nsources == 0: self.log.warning('No sources found in {}.'.format(filename)) else: self.log.info('Detected {} sources in {}.'.format( len(catalog), filename)) if self.save_catalogs: catalog_filename = filename.replace( '.fits', '_cat.{}'.format(self.catalog_format)) if self.catalog_format == 'ecsv': fmt = 'ascii.ecsv' elif self.catalog_format == 'fits': # NOTE: The catalog must not contain any 'None' values. # FITS will also not clobber existing files. fmt = 'fits' else: raise ValueError( '\'catalog_format\' must be "ecsv" or "fits".') catalog.write(catalog_filename, format=fmt, overwrite=True) self.log.info( 'Wrote source catalog: {}'.format(catalog_filename)) image_model.meta.tweakreg_catalog = catalog_filename # Temporarily attach catalog to the image model so that it follows # the grouping by exposure, to be removed after use below image_model.catalog = catalog # group images by their "group id": grp_img = list(images.models_grouped) self.log.info('') self.log.info("Number of image groups to be aligned: {:d}.".format( len(grp_img))) self.log.info("Image groups:") if len(grp_img) == 1: self.log.info("* Images in GROUP 1:") for im in grp_img[0]: self.log.info(" {}".format(im.meta.filename)) self.log.info('') # we need at least two exposures to perform image alignment self.log.warning("At least two exposures are required for image " "alignment.") self.log.warning("Nothing to do. Skipping 'TweakRegStep'...") self.skip = True for model in images: model.meta.cal_step.tweakreg = "SKIPPED" # Remove the attached catalogs del model.catalog return input # create a list of WCS-Catalog-Images Info and/or their Groups: imcats = [] for g in grp_img: if len(g) == 0: raise AssertionError("Logical error in the pipeline code.") else: group_name = _common_name(g) wcsimlist = list(map(self._imodel2wcsim, g)) # Remove the attached catalogs for model in g: del model.catalog self.log.info("* Images in GROUP '{}':".format(group_name)) for im in wcsimlist: im.meta['group_id'] = group_name self.log.info(" {}".format(im.meta['name'])) imcats.extend(wcsimlist) self.log.info('') # align images: tpmatch = TPMatch(searchrad=self.searchrad, separation=self.separation, use2dhist=self.use2dhist, tolerance=self.tolerance, xoffset=self.xoffset, yoffset=self.yoffset) try: align_wcs(imcats, refcat=None, enforce_user_order=self.enforce_user_order, expand_refcat=self.expand_refcat, minobj=self.minobj, match=tpmatch, fitgeom=self.fitgeometry, nclip=self.nclip, sigma=(self.sigma, 'rmse')) except ValueError as e: msg = e.args[0] if (msg == "Too few input images (or groups of images) with " "non-empty catalogs."): # we need at least two exposures to perform image alignment self.log.warning(msg) self.log.warning("At least two exposures are required for " "image alignment.") self.log.warning("Nothing to do. Skipping 'TweakRegStep'...") self.skip = True for model in images: model.meta.cal_step.tweakreg = "SKIPPED" return images else: raise e if self.align_to_gaia: # Get catalog of GAIA sources for the field # # NOTE: If desired, the pipeline can write out the reference # catalog as a separate product with a name based on # whatever convention is determined by the JWST Cal Working # Group. if self.save_gaia_catalog: output_name = 'fit_{}_ref.ecsv'.format( self.gaia_catalog.lower()) else: output_name = None ref_cat = amutils.create_astrometric_catalog(images, self.gaia_catalog, output=output_name) # Check that there are enough GAIA sources for a reliable/valid fit num_ref = len(ref_cat) if num_ref < self.min_gaia: msg = "Not enough GAIA sources for a fit: {}\n".format(num_ref) msg += "Skipping alignment to {} astrometric catalog!\n".format( self.gaia_catalog) # Raise Exception here to avoid rest of code in this try block self.log.warning(msg) else: # align images: # Update to separation needed to prevent confusion of sources # from overlapping images where centering is not consistent or # for the possibility that errors still exist in relative overlap. tpmatch_gaia = TPMatch(searchrad=self.searchrad * 3.0, separation=self.separation / 10.0, use2dhist=self.use2dhist, tolerance=self.tolerance, xoffset=0.0, yoffset=0.0) # Set group_id to same value so all get fit as one observation # The assigned value, 987654, has been hard-coded to make it # easy to recognize when alignment to GAIA was being performed # as opposed to the group_id values used for relative alignment # earlier in this step. for imcat in imcats: imcat.meta['group_id'] = 987654 if 'REFERENCE' in imcat.meta['fit_info']['status']: del imcat.meta['fit_info'] # Perform fit align_wcs(imcats, refcat=ref_cat, enforce_user_order=True, expand_refcat=False, minobj=self.minobj, match=tpmatch_gaia, fitgeom=self.fitgeometry, nclip=self.nclip, sigma=(self.sigma, 'rmse')) for imcat in imcats: imcat.meta['image_model'].meta.cal_step.tweakreg = 'COMPLETE' # retrieve fit status and update wcs if fit is successful: if 'SUCCESS' in imcat.meta.get('fit_info')['status']: # Update/create the WCS .name attribute with information # on this astrometric fit as the only record that it was # successful: if self.align_to_gaia: # NOTE: This .name attrib agreed upon by the JWST Cal # Working Group. # Current value is merely a place-holder based # on HST conventions. This value should also be # translated to the FITS WCSNAME keyword # IF that is what gets recorded in the archive # for end-user searches. imcat.wcs.name = "FIT-LVL3-{}".format(self.gaia_catalog) imcat.meta['image_model'].meta.wcs = imcat.wcs """ # Also update FITS representation in input exposures for # subsequent reprocessing by the end-user. # Not currently enabled, but may be requested later... gwcs_header = imcat.wcs.to_fits_sip(max_pix_error=0.1, max_inv_pix_error=0.1, degree=3, npoints=128) imcat.meta['image_model'].wcs = wcs.WCS(header=gwcs_header) """ return images
def test_multi_image_set(mock_fits_wcs): np.random.seed(1) v1 = 1e10 * np.finfo(np.double).eps v2 = 1 - v1 corners = np.array([[v1, v1], [v1, v2], [v2, v2], [v2, v1]]) n = 1 def get_points(): nonlocal n, v1, v2 pts = [] for _ in range(4): v1 *= n v2 = 1 - v1 n += 1 corners = np.array([[v1, v1], [v1, v2], [v2, v2], [v2, v1]]) pts += [ v1 + (v2 - v1) * np.random.random((250 - len(corners), 2)), corners ] return np.vstack(pts) # reference catalog sources: wcsref = copy.deepcopy(mock_fits_wcs) xyref = 512 * get_points() xyref[250:500, 0] += 512 xyref[500:750, 1] += 512 xyref[750:, :] += 512 radec = wcsref.wcs_pix2world(xyref, 0) refcat = Table(radec, names=('RA', 'DEC')) wcsref = copy.deepcopy(mock_fits_wcs) refcat = Table(xyref, names=('x', 'y')) ref_img_tpwcs = FITSWCS(wcsref, meta={'catalog': refcat, 'name': 'ref_img_tpwcs'}) # single overlap catalog sources: wcsim1 = copy.deepcopy(mock_fits_wcs) wcsim1.wcs.crval += 1e-5 xyim1 = 512 + 512 * np.vstack((np.random.random((1000, 2)), corners)) xyim1[:250, :] = xyref[750:, :] # overlap xyim1[250:500, 0] += 512 xyim1[500:750, 1] += 512 xyim1[750:, :] += 512 imcat = Table(xyim1, names=('x', 'y')) im1_tpwcs = FITSWCS(wcsim1, meta={'catalog': imcat, 'name': 'im1_tpwcs'}) # non-overlaping image: wcsim2 = copy.deepcopy(mock_fits_wcs) xyim2 = xyim1.copy() xyim2[:, 0] += 2000.0 imcat = Table(xyim2, names=('x', 'y')) im2_tpwcs = FITSWCS(wcsim2, meta={'catalog': imcat, 'name': 'im2_tpwcs'}) # grouped images overlap reference catalog sources: wcsim3 = copy.deepcopy(mock_fits_wcs) xyim3 = 512 * np.vstack((np.random.random((1000, 2)), corners)) xyim3[:250, :] = xyref[250:500, :] # overlap xyim3[250:750, 0] += 1024 xyim3[750:, 0] += 512 xyim3[500:, 1] -= 512 imcat = Table(xyim3, names=('x', 'y')) im3_tpwcs = FITSWCS(wcsim3, meta={ 'catalog': imcat, 'group_id': 'group1', 'name': 'im3_tpwcs' }) wcsim4 = copy.deepcopy(mock_fits_wcs) xyim4 = (512, -512) + 1024 * np.vstack((np.random.random((1000, 2)), corners)) imcat = Table(xyim4, names=('x', 'y')) im4_tpwcs = FITSWCS(wcsim4, meta={ # noqa: F841 'catalog': imcat, 'group_id': 'group1', 'name': 'im4_tpwcs' }) wcsim5 = copy.deepcopy(mock_fits_wcs) xyim5 = (512, -512 - 1024) + 1024 * np.vstack((np.random.random((1000, 2)), corners)) imcat = Table(xyim5, names=('x', 'y')) im5_tpwcs = FITSWCS(wcsim5, meta={ 'catalog': imcat, 'group_id': 'group1', 'name': 'im5_tpwcs' }) # Temporarily remove im4_tpwcs from imglist due to crashes in # spherical_geometry. imglist = [ ref_img_tpwcs, im1_tpwcs, im2_tpwcs, im5_tpwcs, im3_tpwcs, # im4_tpwcs ] status = align_wcs(imglist, None, fitgeom='general', enforce_user_order=False, expand_refcat=True) assert status assert im1_tpwcs.meta['fit_info']['status'] == 'SUCCESS' assert im1_tpwcs.meta['fit_info']['fitgeom'] == 'general' assert im1_tpwcs.meta['fit_info']['rmse'] < 1e8 * _ATOL assert np.allclose(im1_tpwcs.wcs.wcs.crval, ref_img_tpwcs.wcs.wcs.crval, rtol=0, atol=1.0e-10)
def test_align_wcs_tpwcs_missing_cat(mock_fits_wcs): tpwcs = FITSWCS(mock_fits_wcs) with pytest.raises(ValueError) as e: align_wcs(tpwcs) assert (e.value.args[0] == "Each object in 'wcscat' must have a valid " "catalog.")
def test_multichip_jwst_alignment(monkeypatch): # this test is fundamentally equivalent to test_multichip_alignment_step() # with the following differences: # 1. test_multichip_alignment_step() test includes parts of the JWST # pipeline step itself; # 2. test_multichip_alignment_step() does not have access to 'fit_info' # in the meta data and so test_multichip_jwst_alignment() can test # the fit more extensively. monkeypatch.setattr(tweakreg_step, 'align_wcs', _align_wcs) monkeypatch.setattr(tweakreg_step, 'make_tweakreg_catalog', _make_tweakreg_catalog) w1 = _make_gwcs_wcs('data/wfc3_uvis1.hdr') imcat1 = tweakwcs.JWSTgWCS(w1, {'v2_ref': 0, 'v3_ref': 0, 'roll_ref': 0}) imcat1.meta['catalog'] = table.Table.read( get_pkg_data_filename('data/wfc3_uvis1.cat'), format='ascii.csv', delimiter=' ', names=['x', 'y']) imcat1.meta['catalog']['x'] += 1 imcat1.meta['catalog']['y'] += 1 imcat1.meta['group_id'] = 1 imcat1.meta['name'] = 'ext1' w2 = _make_gwcs_wcs('data/wfc3_uvis2.hdr') imcat2 = tweakwcs.JWSTgWCS(w2, {'v2_ref': 0, 'v3_ref': 0, 'roll_ref': 0}) imcat2.meta['catalog'] = table.Table.read( get_pkg_data_filename('data/wfc3_uvis2.cat'), format='ascii.csv', delimiter=' ', names=['x', 'y']) imcat2.meta['catalog']['x'] += 1 imcat2.meta['catalog']['y'] += 1 imcat2.meta['group_id'] = 1 imcat2.meta['name'] = 'ext4' refcat = table.Table.read(get_pkg_data_filename('data/ref.cat'), format='ascii.csv', delimiter=' ', names=['RA', 'DEC']) align_wcs([imcat1, imcat2], refcat, match=_match, nclip=None, sigma=3, fitgeom='general') fi1 = imcat1.meta['fit_info'] fi2 = imcat2.meta['fit_info'] w1m = imcat1.wcs w2m = imcat2.wcs assert np.allclose(w1m(*w1.crpix), (83.206917667519, -67.73275818507248), rtol=0) assert np.allclose(w2m(*w2.crpix), (83.15167050722597, -67.74220306069903), rtol=0) assert np.allclose(fi1['<scale>'], 1.0025, rtol=0, atol=2e-8) assert np.allclose(fi2['<scale>'], 1.0025, rtol=0, atol=2e-8) assert fi1['rmse'] < 5e-5 assert fi2['rmse'] < 5e-5 ra1, dec1 = imcat1.wcs(imcat1.meta['catalog']['x'], imcat1.meta['catalog']['y']) ra2, dec2 = imcat2.wcs(imcat2.meta['catalog']['x'], imcat2.meta['catalog']['y']) ra = np.concatenate([ra1, ra2]) dec = np.concatenate([dec1, dec2]) rra = refcat['RA'] rdec = refcat['DEC'] rmse_ra = np.sqrt(np.mean((ra - rra)**2)) rmse_dec = np.sqrt(np.mean((dec - rdec)**2)) assert rmse_ra < _REF_RMSE_RA assert rmse_dec < _REF_RMSE_DEC
def _align_wcs(imcats, **kwargs): new_kwargs = {k: v for k, v in kwargs.items() if k != 'match'} new_kwargs['match'] = _match return align_wcs(imcats, **new_kwargs)