def test_compound_input_units_equivalencies(): """ Test setting input_units_equivalencies on one of the models. """ s1 = Shift(10 * u.deg) s1.input_units_equivalencies = {'x': u.pixel_scale(0.5 * u.deg / u.pix)} s2 = Shift(10 * u.deg) sp = Shift(10 * u.pix) cs = s1 | s2 assert cs.input_units_equivalencies == {'x': u.pixel_scale(0.5 * u.deg / u.pix)} out = cs(10 * u.pix) assert_quantity_allclose(out, 25 * u.deg) cs = sp | s1 assert cs.input_units_equivalencies is None out = cs(10 * u.pix) assert_quantity_allclose(out, 20 * u.deg) cs = s1 & s2 assert cs.input_units_equivalencies == {'x0': u.pixel_scale(0.5 * u.deg / u.pix)} cs = cs.rename('TestModel') out = cs(20 * u.pix, 10 * u.deg) assert_quantity_allclose(out, 20 * u.deg) with pytest.raises(UnitsError) as exc: out = cs(20 * u.pix, 10 * u.pix) assert exc.value.args[0] == "Shift: Units of input 'x', pix (unknown), could not be converted to required input units of deg (angle)"
def test_simple_gwcs(): # https://gwcs.readthedocs.io/en/latest/#getting-started shift_by_crpix = models.Shift(-(2048 - 1) * u.pix) & models.Shift(-(1024 - 1) * u.pix) matrix = np.array([[1.290551569736E-05, 5.9525007864732E-06], [5.0226382102765E-06, -1.2644844123757E-05]]) rotation = models.AffineTransformation2D(matrix * u.deg, translation=[0, 0] * u.deg) rotation.input_units_equivalencies = {"x": u.pixel_scale(1 * (u.deg / u.pix)), "y": u.pixel_scale(1 * (u.deg / u.pix))} rotation.inverse = models.AffineTransformation2D(np.linalg.inv(matrix) * u.pix, translation=[0, 0] * u.pix) rotation.inverse.input_units_equivalencies = {"x": u.pixel_scale(1 * (u.pix / u.deg)), "y": u.pixel_scale(1 * (u.pix / u.deg))} tan = models.Pix2Sky_TAN() celestial_rotation = models.RotateNative2Celestial( 5.63056810618 * u.deg, -72.05457184279 * u.deg, 180 * u.deg) det2sky = shift_by_crpix | rotation | tan | celestial_rotation det2sky.name = "linear_transform" detector_frame = cf.Frame2D(name="detector", axes_names=("x", "y"), unit=(u.pix, u.pix)) sky_frame = cf.CelestialFrame(reference_frame=ICRS(), name='icrs', unit=(u.deg, u.deg)) pipeline = [(detector_frame, det2sky), (sky_frame, None)] w = gwcs.wcs.WCS(pipeline) result = wcs_utils.get_compass_info(w, (1024, 2048), r_fac=0.25) assert_allclose(result[:-1], (1024.0, 512.0, 1131.0265005852038, 279.446189124443, 1262.0057201165127, 606.2863901330095, 155.2870478938214, -86.89813081941797)) assert not result[-1]
def test1(tmpdir, ret=False): shift_by_crpix = models.Shift(-2048 * u.pix) & models.Shift(-1024 * u.pix) matrix = np.array([[1.290551569736E-05, 5.9525007864732E-06], [5.0226382102765E-06, -1.2644844123757E-05]]) rotation = models.AffineTransformation2D(matrix * u.deg, translation=[0, 0] * u.deg) rotation.input_units_equivalencies = { "x": u.pixel_scale(1 * u.deg / u.pix), "y": u.pixel_scale(1 * u.deg / u.pix) } rotation.inverse = models.AffineTransformation2D( np.linalg.inv(matrix) * u.pix, translation=[0, 0] * u.pix) rotation.inverse.input_units_equivalencies = { "x": u.pixel_scale(1 * u.pix / u.deg), "y": u.pixel_scale(1 * u.pix / u.deg) } tan = models.Pix2Sky_TAN() celestial_rotation = models.RotateNative2Celestial(5.63056810618 * u.deg, -72.05457184279 * u.deg, 180 * u.deg) det2sky = shift_by_crpix | rotation | tan | celestial_rotation det2sky.name = "linear_transform" detector_frame = cf.Frame2D(name="detector", axes_names=("x", "y"), unit=(u.pix, u.pix)) sky_frame = cf.CelestialFrame(reference_frame=coords.ICRS(), name='icrs', unit=(u.deg, u.deg)) pipeline = [(detector_frame, det2sky), (sky_frame, None)] wcsobj = gwcs.wcs.WCS(pipeline) wcs_set = WcsSet(default=wcsobj, extra=wcsobj) tree = {'wcs_set': wcs_set} if ret: return wcs_set helpers.assert_roundtrip_tree(tree, tmpdir)
def gwcs_simple_imaging_units(): shift_by_crpix = models.Shift(-2048 * u.pix) & models.Shift(-1024 * u.pix) matrix = np.array([[1.290551569736E-05, 5.9525007864732E-06], [5.0226382102765E-06, -1.2644844123757E-05]]) rotation = models.AffineTransformation2D(matrix * u.deg, translation=[0, 0] * u.deg) rotation.input_units_equivalencies = { "x": u.pixel_scale(1 * u.deg / u.pix), "y": u.pixel_scale(1 * u.deg / u.pix) } rotation.inverse = models.AffineTransformation2D( np.linalg.inv(matrix) * u.pix, translation=[0, 0] * u.pix) rotation.inverse.input_units_equivalencies = { "x": u.pixel_scale(1 * u.pix / u.deg), "y": u.pixel_scale(1 * u.pix / u.deg) } tan = models.Pix2Sky_TAN() celestial_rotation = models.RotateNative2Celestial(5.63056810618 * u.deg, -72.05457184279 * u.deg, 180 * u.deg) det2sky = shift_by_crpix | rotation | tan | celestial_rotation det2sky.name = "linear_transform" detector_frame = cf.Frame2D(name="detector", axes_names=("x", "y"), unit=(u.pix, u.pix)) sky_frame = cf.CelestialFrame(reference_frame=coord.ICRS(), name='icrs', unit=(u.deg, u.deg)) pipeline = [(detector_frame, det2sky), (sky_frame, None)] return wcs.WCS(pipeline)
def test_add_equivelencies(): e1 = u.pixel_scale(10*u.arcsec/u.pixel) + u.temperature_energy() assert isinstance(e1, Equivalency) assert e1.name == ["pixel_scale", "temperature_energy"] assert isinstance(e1.kwargs, list) assert e1.kwargs == [dict({'pixscale': 10*u.arcsec/u.pix}), dict()] e2 = u.pixel_scale(10*u.arcsec/u.pixel) + [1, 2,3] assert isinstance(e2, list)
def test_pixel_scale_invalid_scale_unit(): pixscale = 0.4 * u.arcsec pixscale2 = 0.4 * u.arcsec / u.pix ** 2 with pytest.raises(u.UnitsError, match="pixel dimension"): u.pixel_scale(pixscale) with pytest.raises(u.UnitsError, match="pixel dimension"): u.pixel_scale(pixscale2)
def test_add_equivelencies(): e1 = u.pixel_scale(10*u.arcsec/u.pixel) + u.temperature_energy() assert isinstance(e1, Equivalency) assert e1.name == ["pixel_scale", "temperature_energy"] assert isinstance(e1.kwargs, list) assert e1.kwargs == [dict({'pixscale': 10*u.arcsec/u.pix}), dict()] e2 = u.pixel_scale(10*u.arcsec/u.pixel) + [1, 2, 3] assert isinstance(e2, list)
def setup(self): aff = models.AffineTransformation2D(matrix=[[1, 0], [0, 1]] * u.arcsec, translation=[0, 0] * u.arcsec) aff.input_units_equivalencies = {'x': u.pixel_scale(1 * u.arcsec/u.pix), 'y': u.pixel_scale(1 * u.arcsec/u.pix)} self.model = (models.Shift(-10.5 * u.pix) & models.Shift(-13.2 * u.pix) | aff | models.Scale(.01 * u.arcsec) & models.Scale(.04 * u.deg) | models.Pix2Sky_TAN() | models.RotateNative2Celestial(5.6 * u.deg, -72.05 * u.deg, 180 * u.deg))
def load_wcs(input_model, reference_files={}): """ Create a gWCS object and store it in ``Model.meta``. Parameters ---------- input_model : `~jwst.datamodels.DataModel` The exposure. reference_files : dict A dict {reftype: reference_file_name} containing all reference files that apply to this exposure. """ if "wcsinfo" not in input_model.meta: input_model.meta.cal_step.assign_wcs = "SKIPPED" log.warning("assign_wcs: SKIPPED") return input_model else: output_model = input_model.copy() shift_by_crpix = models.Shift( -(input_model.meta.wcsinfo.crpix1 - 1) * u.pix ) & models.Shift(-(input_model.meta.wcsinfo.crpix2 - 1) * u.pix) pix2sky = getattr( models, "Pix2Sky_{}".format(input_model.meta.wcsinfo.ctype1[-3:]) )() celestial_rotation = models.RotateNative2Celestial( input_model.meta.wcsinfo.crval1 * u.deg, input_model.meta.wcsinfo.crval2 * u.deg, 180 * u.deg, ) pix2sky.input_units_equivalencies = { "x": u.pixel_scale(input_model.meta.wcsinfo.cdelt1 * u.deg / u.pix), "y": u.pixel_scale(input_model.meta.wcsinfo.cdelt2 * u.deg / u.pix), } det2sky = shift_by_crpix | pix2sky | celestial_rotation det2sky.name = "linear_transform" detector_frame = cf.Frame2D( name="detector", axes_names=("x", "y"), unit=(u.pix, u.pix) ) sky_frame = cf.CelestialFrame( reference_frame=getattr( coord, input_model.meta.coordinates.reference_frame )(), name="sky_frame", unit=(u.deg, u.deg), ) pipeline = [(detector_frame, det2sky), (sky_frame, None)] wcs = WCS(pipeline) output_model.meta.wcs = wcs output_model.meta.cal_step.assign_wcs = "COMPLETE" return output_model
def test_compound_and_equiv_call(): """ Check that equivalencies work when passed to evaluate, for a composite model with two inputs. """ s1 = Shift(10 * u.deg) s2 = Shift(10 * u.deg) cs = s1 & s2 out = cs(10 * u.pix, 10 * u.pix, equivalencies={'x0': u.pixel_scale(0.5 * u.deg / u.pix), 'x1': u.pixel_scale(0.5 * u.deg / u.pix)}) assert_quantity_allclose(out[0], 15 * u.deg) assert_quantity_allclose(out[1], 15 * u.deg)
def test_compound_and_equiv_call(): """ Check that equivalencies work when passed to evaluate, for a compsite model with two inputs. """ s1 = Shift(10 * u.deg) s2 = Shift(10 * u.deg) cs = s1 & s2 out = cs(10 * u.pix, 10 * u.pix, equivalencies={'x0': u.pixel_scale(0.5 * u.deg / u.pix), 'x1': u.pixel_scale(0.5 * u.deg / u.pix)}) assert_quantity_allclose(out[0], 15 * u.deg) assert_quantity_allclose(out[1], 15 * u.deg)
def get_src_img(pos, survey, angular=None, level=0, **kwargs): if level > 5: print("Failing") raise ValueError("Too many failed attempts. ") sv_survey = {"first": "VLA FIRST (1.4 GHz)", "wise": "WISE 3.4"} survey = sv_survey[survey] if survey in sv_survey else survey if angular is None: FITS_SIZE = 5 * u.arcmin else: FITS_SIZE = (angular).to(u.arcsecond) CELL_SIZE = 2.0 * u.arcsec / u.pix imsize = FITS_SIZE.to("pix", equivalencies=u.pixel_scale(CELL_SIZE)) try: images = SkyView.get_images( pos, survey, pixels=int(imsize.value), width=FITS_SIZE, height=FITS_SIZE, coordinates="J2000", ) except: import time time.sleep(4) get_src_img(pos, survey, angular=angular, level=level + 1) return images[0]
def kernel_size_px(self): """ Returns a 2-tuple specifying the half-size of the beam image to be initialized, in pixels. The interpolation may become unstable if the image is severely undersampled, pixel sizes of more than 8 arcsec are not recommended. Returns ------- out : 2-tuple, each element an integer. """ if self.px_size > 12. * U.arcsec: warnings.warn( "Using WSRT beam with datacube pixel size >> 8 arcsec," " beam interpolation may fail.") freq = self.vel.to(U.GHz, equivalencies=U.doppler_radio(f_HI)) bheader, bdata = self._load_beamfile() centroid = self._centroid() aspect_x, aspect_y = np.floor(bdata.shape[0] // 2 * np.sin(self.dec)), bdata.shape[1] // 2 aspect_x = aspect_x * np.abs((bheader['CDELT1'] * U.deg)).to(U.arcsec)\ * (centroid[2] / freq).to(U.dimensionless_unscaled) aspect_y = aspect_y * (bheader['CDELT2'] * U.deg).to(U.arcsec) \ * (centroid[2] / freq).to(U.dimensionless_unscaled) return tuple([ (a.to(U.pix, U.pixel_scale(self.px_size / U.pix))).value + 1 for a in (aspect_x, aspect_y) ])
def test_compound_input_units_equivalencies(): """ Test setting input_units_equivalencies on one of the models. """ s1 = Shift(10 * u.deg) s1.input_units_equivalencies = {'x': u.pixel_scale(0.5 * u.deg / u.pix)} s2 = Shift(10 * u.deg) sp = Shift(10 * u.pix) cs = s1 | s2 out = cs(10 * u.pix) assert_quantity_allclose(out, 25 * u.deg) cs = sp | s1 out = cs(10 * u.pix) assert_quantity_allclose(out, 20 * u.deg) cs = s1 & s2 cs = cs.rename('TestModel') out = cs(20 * u.pix, 10 * u.deg) assert_quantity_allclose(out, 20 * u.deg) with pytest.raises(UnitsError) as exc: out = cs(20 * u.pix, 10 * u.pix) assert exc.value.args[0] == "TestModel: Units of input 'x1', pix (unknown), could not be converted to required input units of deg (angle)"
def __init__(self, *args, **kwargs): # Must be set AFTER the super() call time = kwargs.pop("time", None) beam = kwargs.pop("beam", None) fake_sources = kwargs.pop("fake_sources", None) sources = kwargs.pop("sources", None) super(NikaMap, self).__init__(*args, **kwargs) if isinstance(self.wcs, WCS): pixsize = np.abs(self.wcs.wcs.cdelt[0]) * u.deg else: pixsize = np.abs(self.meta.get("header", {"CDELT": 1}).get("CDELT1", 1)) * u.deg self._pixel_scale = u.pixel_scale(pixsize / u.pixel) if time is not None: self.time = time else: self.time = np.zeros(self.data.shape) * u.s if beam is None: # Default gaussian beam bmaj = self.meta.get("header", {"BMAJ": 1}).get("BMAJ", 1) * u.deg self.beam = NikaBeam(bmaj, pixel_scale=self._pixel_scale) else: self.beam = beam self.fake_sources = fake_sources self.sources = sources
def _add_attr_img_width_pix_arcsec(self): """ creating attributes self.img_width_pix (int) self.img_height_pix (int) self.img_height_arcsec (angular quantity) self.img_width_arcsec (angular quantity) which are self.img_width and img_height but changed to indicated units """ survey_pixelscale = u.pixel_scale(self.pixsize / u.pixel) if hasattr(self.img_width, 'unit'): nwpix = (self.img_width.to(u.pix, survey_pixelscale) / u.pix).to( u.dimensionless_unscaled) self.img_width_pix = int(np.floor(nwpix)) self.img_width_arcsec = self.img_width.to(u.arcsec, survey_pixelscale) else: raise ValueError('self.img_width has no angular units') if hasattr(self.img_height, 'unit'): nhpix = (self.img_height.to(u.pix, survey_pixelscale) / u.pix).to( u.dimensionless_unscaled) self.img_height_pix = int(np.floor(nhpix)) self.img_height_arcsec = self.img_height.to( u.arcsec, survey_pixelscale) else: raise ValueError('self.img_height has no angular units')
def test_affine_with_quantities(): x = 1 y = 2 xdeg = (x * u.pix).to(u.deg, equivalencies=u.pixel_scale(2.5 * u.deg / u.pix)) ydeg = (y * u.pix).to(u.deg, equivalencies=u.pixel_scale(2.5 * u.deg / u.pix)) xpix = x * u.pix ypix = y * u.pix # test affine with matrix only qaff = projections.AffineTransformation2D(matrix=[[1, 2], [2, 1]] * u.deg) with pytest.raises(ValueError): qx1, qy1 = qaff(xpix, ypix, equivalencies={ 'x': u.pixel_scale(2.5 * u.deg / u.pix), 'y': u.pixel_scale(2.5 * u.deg / u.pix)}) # test affine with matrix and translation qaff = projections.AffineTransformation2D(matrix=[[1, 2], [2, 1]] * u.deg, translation=[1, 2] * u.deg) qx1, qy1 = qaff(xpix, ypix, equivalencies={ 'x': u.pixel_scale(2.5 * u.deg / u.pix), 'y': u.pixel_scale(2.5 * u.deg / u.pix)}) aff = projections.AffineTransformation2D(matrix=[[1, 2], [2, 1]], translation=[1, 2]) x1, y1 = aff(xdeg.value, ydeg.value) assert_quantity_allclose(qx1, x1 * u.deg) assert_quantity_allclose(qy1, y1 * u.deg) # test the case of WCS PC and CDELT transformations pc = np.array([[0.86585778922708, 0.50029020461607], [-0.50029020461607, 0.86585778922708]]) cdelt = np.array([[1, 3.0683055555556E-05], [3.0966944444444E-05, 1]]) matrix = cdelt * pc qaff = projections.AffineTransformation2D(matrix=matrix * u.deg, translation=[0, 0] * u.deg) inv_matrix = np.linalg.inv(matrix) inv_qaff = projections.AffineTransformation2D(matrix=inv_matrix * u.pix, translation=[0, 0] * u.pix) qaff.inverse = inv_qaff qx1, qy1 = qaff(xpix, ypix, equivalencies={ 'x': u.pixel_scale(1 * u.deg / u.pix), 'y': u.pixel_scale(1 * u.deg / u.pix)}) x1, y1 = qaff.inverse(qx1, qy1, equivalencies={ 'x': u.pixel_scale(1 * u.deg / u.pix), 'y': u.pixel_scale(1 * u.deg / u.pix)}) assert_quantity_allclose(x1, xpix) assert_quantity_allclose(y1, ypix)
def get_pixel_scale(file: Union['fits.hdu_list.hdulist.HDUList', 'str'], ext: int = 0, astropy_units: bool = False): """ Using the FITS file header, obtains the pixel scale of the file (in degrees). Declination scale is the true angular size of the pixel. Assumes that the change in spherical distortion in RA over the width of the image is negligible. :param file: :return: Tuple containing the pixel scale of the FITS file: (ra scale, dec scale) If astropy_units is True, returns it as an astropy pixel_scale equivalency. """ # TODO: Rewrite photometry functions to use this file, path = path_or_hdu(file) header = file[ext].header image = file[ext].data # To take (very roughly) into account the spherical distortion to the RA, we obtain an RA pixel scale by dividing # the difference in RA across the image by the number of pixels. It's good enough to give an average value, and the # difference SHOULD be pretty tiny across the image. w = wcs.WCS(header) end = image.shape[0] - 1 ra_pixel_scale = (w.pixel_to_world(0, 0).ra.deg - w.pixel_to_world(end, 0).ra.deg) / end # By comparison the pixel scale in declination is easy to obtain - as DEC is undistorted, it is simply the true # pixel scale of the image, which the header stores. dec_pixel_scale = w.pixel_scale_matrix[1, 1] if dec_pixel_scale == 0: dec_pixel_scale = w.pixel_scale_matrix[1, 0] if path: file.close() ra_pixel_scale = abs(ra_pixel_scale) dec_pixel_scale = abs(dec_pixel_scale) if astropy_units: ra_pixel_scale = units.pixel_scale(ra_pixel_scale * units.deg / units.pixel) dec_pixel_scale = units.pixel_scale(dec_pixel_scale * units.deg / units.pixel) return ra_pixel_scale, dec_pixel_scale
def gwcs_2d_shift_scale_quantity(): m4 = models.Shift(1 * u.pix) & models.Shift(2 * u.pix) m5 = models.Scale(5 * u.deg) m6 = models.Scale(10 * u.deg) m5.input_units_equivalencies = {'x': u.pixel_scale(1 * u.deg / u.pix)} m6.input_units_equivalencies = {'x': u.pixel_scale(1 * u.deg / u.pix)} m5.inverse = models.Scale(1. / 5 * u.pix) m6.inverse = models.Scale(1. / 10 * u.pix) m5.inverse.input_units_equivalencies = { 'x': u.pixel_scale(1 * u.pix / u.deg) } m6.inverse.input_units_equivalencies = { 'x': u.pixel_scale(1 * u.pix / u.deg) } m7 = m5 & m6 m8 = m4 | m7 pipe2 = [(detector_2d, m8), (icrs_sky_frame, None)] return wcs.WCS(pipe2)
def test_equivelency(): ps = u.pixel_scale(10*u.arcsec/u.pix) assert isinstance(ps, Equivalency) assert isinstance(ps.name, list) assert len(ps.name) == 1 assert ps.name[0] == "pixel_scale" assert isinstance(ps.kwargs, list) assert len(ps.kwargs) == 1 assert ps.kwargs[0] == dict({'pixscale': 10*u.arcsec/u.pix})
def test_compound_pipe_equiv_call(): """ Check that equivalencies work when passed to evaluate, for a chained model (which has one input). """ s1 = Shift(10 * u.deg) s2 = Shift(10 * u.deg) cs = s1 | s2 out = cs(10 * u.pix, equivalencies={'x': u.pixel_scale(0.5 * u.deg / u.pix)}) assert_quantity_allclose(out, 25 * u.deg)
def kernel_size_px(self): """ Returns a 2-tuple specifying the half-size of the beam image to be initialized, in pixels. Returns ------- out : 2-tuple, each element an integer. """ size = np.ceil((self.bmaj * self.truncate).to( U.pix, U.pixel_scale(self.px_size / U.pix))).value + 1 return size, size
def _init_sm_lengths(self, source=None, datacube=None): """ Determine kernel sizes in pixel units. Parameters ---------- source : martini.sources.SPHSource (or inheriting class) instance The source providing the kernel sizes. datacube : martini.DataCube instance The datacube providing the pixel scale. """ self.sm_lengths = np.arctan( source.hsm_g / source.sky_coordinates.distance).to( U.pix, U.pixel_scale(datacube.px_size / U.pix)) return
def _prune_source(self): """ Determines which particles cannot contribute to the DataCube and removes them to speed up calculation. Assumes the kernel is 0 at distances greater than the kernel size (which may differ from the SPH smoothing length). """ # pixels indexed from 0 (not like in FITS!) for better use with numpy origin = 0 skycoords = self.source.sky_coordinates particle_coords = np.vstack( self.datacube.wcs.sub(3).wcs_world2pix( skycoords.ra.to(self.datacube.units[0]), skycoords.dec.to(self.datacube.units[1]), skycoords.radial_velocity.to(self.datacube.units[2]), origin)) * U.pix # could use a function bound to source which returns the size of the # kernel, in case this isn't equal to the smoothing length for some # kernel sm_length = np.arctan( self.source.hsm_g / self.source.sky_coordinates.distance).to( U.pix, U.pixel_scale(self.datacube.px_size / U.pix)) sm_range = np.ceil( sm_length * self.sph_kernel.size_in_fwhm ).astype(int) spectrum_half_width = self.spectral_model.half_width(self.source) / \ self.datacube.channel_width reject_conditions = ( (particle_coords[:2] + sm_range[np.newaxis] < 0 * U.pix).any(axis=0), particle_coords[0] - sm_range > (self.datacube.n_px_x + self.datacube.padx * 2) * U.pix, particle_coords[1] - sm_range > (self.datacube.n_px_y + self.datacube.pady * 2) * U.pix, particle_coords[2] + 4 * spectrum_half_width * U.pix < 0 * U.pix, particle_coords[2] - 4 * spectrum_half_width * U.pix > self.datacube.n_channels * U.pix, ) reject_mask = np.zeros(particle_coords[0].shape) for condition in reject_conditions: reject_mask = np.logical_or(reject_mask, condition) self.source.apply_mask(np.logical_not(reject_mask)) return
def moffat_psf(fwhm, pixel_scale=0.2, shape=41, alpha=4.765): """ Moffat point-spread function. Parameters ---------- fwhm : float or `~astropy.units.Quantity` Full width at half maximum of the psf. If a float is given, the units will be assumed to be arcsec. The units can be angular or in pixels. pixel_scale : float or `~astropy.units.Quantity`, optional The pixel scale of the psf image. If a float is given, the units are assumed to be arcsec / pixel (why would you want anything different?). shape : int or list-like, optional Shape of the psf image. Must be odd. If an int is given, the x and y dimensions will be set to this value: (shape, shape). alpha : float, optional Power index of the Moffat model. Returns ------- psf : `~numpy.ndarray` The PSF image normalized such that its sum is equal to one. Notes ----- The default value `alpha = 4.765` is a fit to the prediction from atmospheric turbulence theory (`Trujillo et al. 2001 <https://ui.adsabs.harvard.edu/abs/2001MNRAS.328..977T/abstract>`_). """ fwhm = check_units(fwhm, 'arcsec') pixel_scale = check_units(pixel_scale, u.arcsec / u.pixel) y_size, x_size = _check_shape(shape) width = fwhm.to('pixel', u.pixel_scale(pixel_scale)).value gamma = width / (2 * np.sqrt(2**(1 / alpha) - 1)) model = Moffat2DKernel(gamma=gamma, alpha=alpha, x_size=x_size, y_size=y_size) model.normalize() psf = model.array return psf
def __init__(self, sp, scale_radius, xy_dim, pixel_scale, dx=0, dy=0, labels=None): self.sp = sp self.mag_limit = sp.mag_limit self.mag_limit_band = sp.mag_limit_band self.smooth_model = None if self.mag_limit is not None and sp.frac_num_sampled < 1.0: _rs = check_units(scale_radius, 'kpc').to('Mpc').value _distance = check_units(sp.distance, 'Mpc').to('Mpc').value _pixel_scale = check_units(pixel_scale, u.arcsec / u.pixel) self.r_sky = np.arctan2(_rs, _distance) * u.radian.to('arcsec') self.r_sky *= u.arcsec r_pix = self.r_sky.to('pixel', u.pixel_scale(_pixel_scale)).value xy_dim = check_xy_dim(xy_dim) x_0, y_0 = xy_dim // 2 x_0 += dx y_0 += dy self.smooth_model = Plummer2D(x_0=x_0, y_0=y_0, scale_radius=r_pix) self.xy_kw = dict(num_stars=sp.num_stars, scale_radius=scale_radius, distance=sp.distance, xy_dim=xy_dim, pixel_scale=pixel_scale, dx=dx, dy=dy, random_state=sp.rng) _xy = plummer_xy(**self.xy_kw) super(PlummerSP, self).__init__(_xy, sp.mag_table, xy_dim, pixel_scale, labels)
def test_pixel_scale(): pix = 75*u.pix asec = 30*u.arcsec pixscale = 0.4*u.arcsec/u.pix pixscale2 = 2.5*u.pix/u.arcsec assert_quantity_allclose(pix.to(u.arcsec, u.pixel_scale(pixscale)), asec) assert_quantity_allclose(pix.to(u.arcmin, u.pixel_scale(pixscale)), asec) assert_quantity_allclose(pix.to(u.arcsec, u.pixel_scale(pixscale2)), asec) assert_quantity_allclose(pix.to(u.arcmin, u.pixel_scale(pixscale2)), asec) assert_quantity_allclose(asec.to(u.pix, u.pixel_scale(pixscale)), pix) assert_quantity_allclose(asec.to(u.pix, u.pixel_scale(pixscale2)), pix)
def test_pixel_scale_acceptable_scale_unit(): pix = 75 * u.pix v = 3000 * (u.cm / u.s) pixscale = 0.4 * (u.m / u.s / u.pix) pixscale2 = 2.5 * (u.pix / (u.m / u.s)) assert_quantity_allclose(pix.to(u.m / u.s, u.pixel_scale(pixscale)), v) assert_quantity_allclose(pix.to(u.km / u.s, u.pixel_scale(pixscale)), v) assert_quantity_allclose(pix.to(u.m / u.s, u.pixel_scale(pixscale2)), v) assert_quantity_allclose(pix.to(u.km / u.s, u.pixel_scale(pixscale2)), v) assert_quantity_allclose(v.to(u.pix, u.pixel_scale(pixscale)), pix) assert_quantity_allclose(v.to(u.pix, u.pixel_scale(pixscale2)), pix)
def gaussian_psf(fwhm, pixel_scale=0.2, shape=41): """ Gaussian point-spread function. Parameters ---------- fwhm : float or `~astropy.units.Quantity` Full width at half maximum of the psf. If a float is given, the units will be assumed to be `~astropy.units.arcsec`. The units can be angular or in pixels. pixel_scale : float or `~astropy.units.Quantity`, optional The pixel scale of the psf image. If a float is given, the units are assumed to be `~astropy.units.arcsec` per `~astropy.units.pixel` (why would you want anything different?). shape : int or list-like, optional Shape of the psf image. Must be odd. If an int is given, the x and y dimensions will be set to this value: (shape, shape). Returns ------- psf : `~numpy.ndarray` The PSF image normalized such that its sum is equal to one. """ fwhm = check_units(fwhm, 'arcsec') pixel_scale = check_units(pixel_scale, u.arcsec / u.pixel) y_size, x_size = _check_shape(shape) width = fwhm.to('pixel', u.pixel_scale(pixel_scale)).value width *= gaussian_fwhm_to_sigma model = Gaussian2DKernel(x_stddev=width, y_stddev=width, x_size=x_size, y_size=y_size) model.normalize() psf = model.array return psf
def __delta_sky_to_pixels(self) -> Tuple[np.ndarray, np.ndarray]: """Transform the spherical offsets from the sky-reference frame to a pixel-reference frame. Returns: Tuple[np.ndarray, np.ndarray] -- Coordinates in a pixel-reference frame """ offsets = self.coords["offsets-angular"] if self.pixel_scale is None: return offsets pixel_scale = (self.pixel_scale if isinstance(self.pixel_scale, tuple) else (self.pixel_scale, self.pixel_scale)) pixel_scale = tuple( [u.pixel_scale(ps / u.pixel) for ps in pixel_scale]) # RA increases right-to-left. The opposite of an array offsets = ( -offsets[0].to(u.pixel, pixel_scale[0]), offsets[1].to(u.pixel, pixel_scale[1]), ) return offsets
def _add_attr_img_width_pix_arcsec(self): """ creating attributes self.img_width_pix (int) self.img_height_pix (int) self.img_height_arcsec (angular quantity) self.img_width_arcsec (angular quantity) which are self.img_width and img_height but changed to indicated units """ survey_pixelscale = u.pixel_scale(self.pixsize/u.pixel) if hasattr(self.img_width, 'unit'): nwpix = (self.img_width.to(u.pix, survey_pixelscale)/u.pix).to(u.dimensionless_unscaled) self.img_width_pix = int(np.floor(nwpix)) self.img_width_arcsec = self.img_width.to(u.arcsec, survey_pixelscale) else: raise ValueError('self.img_width has no angular units') if hasattr(self.img_height, 'unit'): nhpix = (self.img_height.to(u.pix, survey_pixelscale)/u.pix).to(u.dimensionless_unscaled) self.img_height_pix = int(np.floor(nhpix)) self.img_height_arcsec = self.img_height.to(u.arcsec, survey_pixelscale) else: raise ValueError('self.img_height has no angular units')
def fit_alignment_box(region, box_size=30, verbose=False, seeing=None, medfilt=False): pixelscale = u.pixel_scale(0.1798*u.arcsec/u.pixel) if medfilt is True: region = median_filter(region, size=(3,3)) # Estimate center of alignment box threshold_pct = 80 window = region.data > np.percentile(region.data, threshold_pct) alignment_box_position = ndimage.measurements.center_of_mass(window) offset_val = np.median(region.data[~window]) offset = models.Const2D(offset_val) # Determine fluctuations in sky sky_amplitude = np.median(region.data[window]) sky_fluctuations = np.std(region.data[window]) # Detect box edges gradx = np.gradient(region.data, axis=1) horizontal_profile = np.sum(gradx, axis=0) h_edges = fit_CSU_edges(horizontal_profile) grady = np.gradient(region.data, axis=0) vertical_profile = np.sum(grady, axis=1) v_edges = fit_CSU_edges(vertical_profile) # Estimate stellar position maxr = np.max(region.data) starloc = (np.where(region == maxr)[0][0], np.where(region == maxr)[1][0]) # Build model of sky, star, & box boxamplitude = 1 box = mosfireAlignmentBox(boxamplitude, alignment_box_position[1], alignment_box_position[0],\ abs(h_edges[0]-h_edges[1]), abs(v_edges[0]-v_edges[1])) box.amplitude.fixed = True box.x_width.min = 10 box.y_width.min = 10 sky = models.Const2D(sky_amplitude) sky.amplitude.min = 0 star_amplitude = maxr - sky_amplitude star_sigma = star_amplitude / sky_fluctuations if star_sigma < 5: if verbose: print(f'No star detected. sigma={star_sigma:.1f}') return [None]*4 else: if verbose: print(f'Detected peak pixel {star_sigma:.1f} sigma above sky.') star = models.Gaussian2D(amplitude=star_amplitude, x_mean=starloc[1], y_mean=starloc[0], x_stddev=2, y_stddev=2) # print(h_edges) # print(v_edges) # star.y_mean.min = v_edges[0] # star.y_mean.max = v_edges[1] # star.x_mean.min = h_edges[0] # star.x_mean.max = h_edges[1] star.amplitude.min = 5*sky_fluctuations star.x_stddev.min = 1 # FWHM = 2.355*stddev = 0.42 arcsec FWHM star.x_stddev.max = 4 # FWHM = 2.355*stddev = 1.47 arcsec FWHM star.y_stddev.min = 1 star.y_stddev.max = 4 if seeing is not None and seeing > 0: sigma = (seeing / 2.355 * u.arcsec).to(u.pixel, equivalencies=pixelscale) star.x_stddev.min = max(2, sigma.value-1) star.y_stddev.min = max(2, sigma.value-1) star.x_stddev.max = min(sigma.value+1, 4) star.y_stddev.max = min(sigma.value+1, 4) # print(f"Using seeing value {seeing} arcsec. sigma limits {star.x_stddev.min}, {star.x_stddev.max} pix") model = box*(sky + star) + offset # modelim = np.zeros((61,61)) # fitim = np.zeros((61,61)) # for i in range(0,60): # for j in range(0,60): # modelim[j,i] = model(i,j) # fitim[j,i] = model(i,j) # residuals = region.data-fitim # residualsum = np.sum(residuals) # import pdb ; pdb.set_trace() fitter = fitting.LevMarLSQFitter() y, x = np.mgrid[:2*box_size+1, :2*box_size+1] fit = fitter(model, x, y, region.data) FWHMx = 2*(2*np.log(2))**0.5*fit.x_stddev_2.value * u.pix FWHMy = 2*(2*np.log(2))**0.5*fit.y_stddev_2.value * u.pix FWHM = (FWHMx**2 + FWHMy**2)**0.5/2**0.5 FWHMarcsec = FWHM.to(u.arcsec, equivalencies=pixelscale) sky_amplitude = fit.amplitude_1.value star_flux = 2*np.pi*fit.amplitude_2.value*fit.x_stddev_2.value*fit.y_stddev_2.value star_amplitude = fit.amplitude_2.value boxpos_x = fit.x_0_0.value boxpos_y = fit.y_0_0.value star_x = fit.x_mean_2.value star_y = fit.y_mean_2.value if verbose: print(f" Box X Center = {boxpos_x:.0f}") if verbose: print(f" Box Y Center = {boxpos_y:.0f}") if verbose: print(f" Sky Brightness = {fit.amplitude_1.value:.0f} ADU") if verbose: print(f" Stellar FWHM = {FWHMarcsec:.2f}") if verbose: print(f" Stellar Xpos = {star_x:.0f}") if verbose: print(f" Stellar Xpos = {star_y:.0f}") if verbose: print(f" Stellar Amplitude = {star_amplitude:.0f} ADU") if verbose: print(f" Stellar Flux (fit) = {star_flux:.0f} ADU") result = {'Star X': star_x, 'Star Y': star_y, 'Star Amplitude': star_amplitude, 'Sky Amplitude': sky_amplitude, 'FWHM pix': FWHM.value, 'FWHM arcsec': FWHMarcsec, 'Box X': boxpos_x, 'Box Y': boxpos_y, # 'Residuals': residuals, } return result
y_0 = r_195[band]['y_0'] elif got_one: x_0 = previous_centroid[0] y_0 = previous_centroid[1] print('{:s}: {:s}band: {:d}/{:d}. x_0: {:.3g}, y_0: {:.3g}.'.format( fname, band, int(index), int(len(fnames)), x_0, y_0)+\ ' Got {:d} sources'.format(len(phot_table)) ) dist = np.sqrt( (phot_table['xcenter']-x_0)**2 + (phot_table['ycenter']-y_0)**2 ) phot_table['dist_from_r0_px'] = dist phot_table.sort(['dist_from_r0_px']) pisco_pixelscale = u.pixel_scale(0.069*u.arcsec/u.pixel) phot_table['dist_from_r0_as'] = phot_table['dist_from_r0_px'].to( u.arcsec, pisco_pixelscale) tr56 = phot_table[0] if tr56['dist_from_r0_as'] > 1.*u.arcsec: # NOTE: this approach (if there is a jump, just skip to the # next frame and ignore the last one) was "needed" b/c of one frame # in i-band images: tr_45?.fits (see notes for the correct number). # If you don't do this, get stuck on that frame. print('ERR: {:s} position of tr56 is {:.3g} ({:.3g}) from prev'.\ format(fname, tr56['dist_from_r0_px'], tr56['dist_from_r0_as'])) print('\tcontinue to next frame...')
def given_cameras_get_stars_on_silicon(coords, cam_directions, verbose=True, withgaps=True): ''' If the TESS spacecraft points in a particular direction, which stars fall on silicon? Args: coords (np.ndarray of astropy.coordinate.SkyCoords): array of astropy coordinates for the stars that will be either on, or not on silicon. cam_directions (list of float tuples): list with format: [ (cam1_elat, cam1_elon), (cam2_elat, cam2_elon), (cam3_elat, cam3_elon), (cam4_elat, cam4_elon) ], where all entries are tuples of floats, and "cam1_elat" means ecliptic latitude of camera 1 center. This function will require that camera 2 is 24 degrees from camera 1, camera 3 is 48 degrees from camera 1, and camera 4 is 72 degrees from camera 1. Returns: on_chip (np.ndarray): array of 1's and 0's for what falls on silicon, and what does not. Same length as coords. Nomenclature: "Sector" means one "grouping" of 4 cameras. There are 13 sectors over the first TESS year. "View" means one pointing of a camera, in a sector. There are 26*4=104 views over the first two years. This function corresponds to only a single sector. ''' n_coords = len(coords) if verbose: print('computing whether on silicon for {:d} objects'.format(n_coords)) n_cameras = 4 # this will hopefully never change n_sectors = 1 # this function is for a single sector n_views = n_sectors * n_cameras # compute camera central coordinates for the first year, given the initial # longitude. views, cam_coords = [], [] for n_camera, this_cam_dirn in zip(range(n_cameras), cam_directions): this_elat = this_cam_dirn[0] * u.deg this_elon = this_cam_dirn[1] * u.deg this_coord = SkyCoord(lon=this_elon, lat=this_elat, frame='barycentrictrueecliptic') views.append([ n_camera, this_elon, this_elat, this_coord.icrs.ra, this_coord.icrs.dec ]) cam_coords.append(this_coord) separations = [ cam_coords[0].separation(cam_coords[1]).value, cam_coords[0].separation(cam_coords[2]).value, cam_coords[0].separation(cam_coords[3]).value ] np.testing.assert_array_almost_equal( separations, [24, 48, 72], decimal=2, err_msg='camera separations should be 24 degrees. have {:s}'.format( repr(cam_coords))) views_columns = ['n_camera', 'elon', 'elat', 'ra', 'dec'] views = pd.DataFrame(views, columns=views_columns) # ccd info. see e.g., Huang et al, 2018. The gap size is a number inherited # from Josh Winn's code. fov = 24. * u.degree ccd_pix = 4096 * u.pix gap_pix = (2. / 0.015) * u.pix # about 133 pixels per gap pixel_scale = u.pixel_scale(fov / (ccd_pix + gap_pix)) ccd_center = np.ones(2) * (ccd_pix + gap_pix) / 2 delt = (np.ones(2) * u.pix).to(u.degree, pixel_scale) elon = coords.barycentrictrueecliptic.lon.value elat = coords.barycentrictrueecliptic.lat.value ra = coords.icrs.ra.value dec = coords.icrs.dec.value onchip = np.zeros_like(ra) for ix, view in views.iterrows(): # create new WCS object. See. e.g., Calabretta & Greisen 2002, paper # II, table 1. CRPIX: pixel-coordinates of reference pixel (center of # image). CRVAL: celestial long (RA) and lat (dec) of reference pixel. # CDELT: degrees per pixel at reference pixel location, i.e. the # coordinate scale. CTYPE: gnomic projection. w = wcs.WCS(naxis=2) w.wcs.crpix = ccd_center.value w.wcs.crval = [view['elon'].value, view['elat'].value] ###w.wcs.crval = [view['ra'].value, view['dec'].value] # either works w.wcs.cdelt = delt.value w.wcs.ctype = ['RA---TAN', 'DEC--TAN'] # follow fits standards: The image pixel count starts with 1. x, y = w.wcs_world2pix(elon, elat, 1) ###x, y = w.wcs_world2pix(ra, dec, 1) # either works try: # the extra "1" is because of 1-based image count. if withgaps: onchip += ( # lower left CCD ((x > 0.0) & (x < (ccd_pix.value - gap_pix.value) / 2. - 1.) & (y > 0.0) & (y < (ccd_pix.value - gap_pix.value) / 2. - 1.)) | # lower right CCD ((x > (ccd_pix.value + gap_pix.value) / 2. - 1.) & (x < (ccd_pix.value + gap_pix.value) - 1.) & (y > 0.0) & (y < (ccd_pix.value - gap_pix.value) / 2. - 1.)) | # upper right CCD ((x > (ccd_pix.value + gap_pix.value) / 2. - 1.) & (x < (ccd_pix.value + gap_pix.value) - 1.) & (y > (ccd_pix.value + gap_pix.value) / 2. - 1.) & (y < (ccd_pix.value + gap_pix.value) - 1.)) | # upper left CCD ((x > 0.0) & (x < (ccd_pix.value - gap_pix.value) / 2. - 1.) & (y > (ccd_pix.value + gap_pix.value) / 2. - 1.) & (y < (ccd_pix.value + gap_pix.value) - 1.))) elif not withgaps: onchip += ((x > 0.) & (x < ccd_pix.value + gap_pix.value - 1.) & (y > 0.) & (y < ccd_pix.value + gap_pix.value - 1.)) except Exception as e: print('failed computing onchip. error msg: {:s}'.format(e)) return 0 if verbose: print( 'computed sector numbers for {:d} stars/objects'.format(n_coords)) return onchip.astype(np.int)
def __init__(self, **kwargs): """ Imager, parent class for all obj operator for images Params ------ Operator params: /either obj (object of class obsobj): with attributes ra, dec, dir_obj /or ra (float) dec (float) /either dir_obj (string) /or dir_parent (string): attr dir_obj is set to dir_parent+'SDSSJXXXX+XXXX/' survey (str): survey of the photometric system if not provided, use self.obj.survey. Raise exception if self.obj.survey does not exist. z=-1 (float): redshift, if not provided, use self.obj.z or self.obj.sdss.z. It does not automatically query sdss to get z. If nothing is pecified then set to -1. center_mode='n/2' (str): how is image center defined in case of even n, 'n/2' or 'n/2-1'. Should be set to n/2-1 if the image is downloaded from HSC quarry. Attributes ---------- Operator Attributes: obj (instance of objObj) ra (float) dec (float) dir_obj (string) survey (str): e.g., 'hsc' survey of the photometric system z (float): redshift pixsize (astropy angle quantity): in unit of arcsec pixelscale (astropy pixscale quantity): for pixel and arcsec conversion """ super(Imager, self).__init__(**kwargs) # set survey if hasattr(self.obj, 'survey'): default_survey = self.obj.survey self.survey = kwargs.pop('survey', default_survey) else: self.survey = kwargs.pop('survey') # set up obj.survey if self.survey == 'hsc': self.obj.add_hsc() elif self.survey == 'sdss': self.obj.add_sdss() # set z if hasattr(self.obj, 'z'): self.z = kwargs.pop('z', self.obj.z) elif hasattr(self.obj, 'sdss'): self.z = kwargs.pop('z', self.obj.sdss.z) elif 'z' in kwargs: self.z = kwargs.pop('z') else: print("[imager] not redshift used, assuming -1") self.z = -1 # set center_mode self.center_mode = kwargs.pop('center_mode', 'n/2') # set pixsize self.pixsize = surveysetup.pixsize[self.survey] self.pixelscale = u.pixel_scale(self.pixsize/u.pixel)
def given_one_camera_get_stars_on_silicon(coords, cam_direction, verbose=True, withgaps=True): ''' If you have a single TESS camera, and it points in a particular direction, which sources fall on silicon? (This is relevant in a niche case: you have made an incomplete set of lightcurves for some field, and a you have a list of coordinates, and you want to know which coordinates you would _expect_ to have a lightcurve). Args: coords (np.ndarray of astropy.coordinate.SkyCoords): array of astropy coordinates for the stars that will be either on, or not on silicon. cam_direction (float tuple): tuple with format: (cam_elat, cam_elon) where "cam_elat" means ecliptic latitude of the camera's center. Returns: on_chip (np.ndarray): array of 1's and 0's for what falls on silicon, and what does not. Same length as coords. ''' n_coords = len(coords) if verbose: print('computing whether on silicon for {:d} objects'.format(n_coords)) n_cameras = 1 # this function is for a single camera's "view" n_sectors = 1 # this function is for a single sector n_views = n_sectors * n_cameras # compute camera central coordinates for the first year, given the initial # longitude. views, cam_coords = [], [] this_elat = cam_direction[0] * u.deg this_elon = cam_direction[1] * u.deg this_coord = SkyCoord(lon=this_elon, lat=this_elat, frame='barycentrictrueecliptic') views = pd.DataFrame( { 'n_camera': n_cameras, 'elon': this_elon, 'elat': this_elat, 'ra': this_coord.icrs.ra, 'dec': this_coord.icrs.dec }, index=[0]) # ccd info. see e.g., Huang et al, 2018. The gap size is a number inherited # from Josh Winn's code. fov = 24. * u.degree ccd_pix = 4096 * u.pix gap_pix = (2. / 0.015) * u.pix # about 133 pixels per gap pixel_scale = u.pixel_scale(fov / (ccd_pix + gap_pix)) ccd_center = np.ones(2) * (ccd_pix + gap_pix) / 2 delt = (np.ones(2) * u.pix).to(u.degree, pixel_scale) elon = coords.barycentrictrueecliptic.lon.value elat = coords.barycentrictrueecliptic.lat.value ra = coords.icrs.ra.value dec = coords.icrs.dec.value onchip = np.zeros_like(ra) for ix, view in views.iterrows(): # create new WCS object. See. e.g., Calabretta & Greisen 2002, paper # II, table 1. CRPIX: pixel-coordinates of reference pixel (center of # image). CRVAL: celestial long (RA) and lat (dec) of reference pixel. # CDELT: degrees per pixel at reference pixel location, i.e. the # coordinate scale. CTYPE: gnomic projection. w = wcs.WCS(naxis=2) w.wcs.crpix = ccd_center.value w.wcs.crval = [view['elon'], view['elat']] w.wcs.cdelt = delt.value w.wcs.ctype = ['RA---TAN', 'DEC--TAN'] # follow fits standards: The image pixel count starts with 1. x, y = w.wcs_world2pix(elon, elat, 1) try: # the extra "1" is because of 1-based image count. if withgaps: onchip += ( # lower left CCD ((x > 0.0) & (x < (ccd_pix.value - gap_pix.value) / 2. - 1.) & (y > 0.0) & (y < (ccd_pix.value - gap_pix.value) / 2. - 1.)) | # lower right CCD ((x > (ccd_pix.value + gap_pix.value) / 2. - 1.) & (x < (ccd_pix.value + gap_pix.value) - 1.) & (y > 0.0) & (y < (ccd_pix.value - gap_pix.value) / 2. - 1.)) | # upper right CCD ((x > (ccd_pix.value + gap_pix.value) / 2. - 1.) & (x < (ccd_pix.value + gap_pix.value) - 1.) & (y > (ccd_pix.value + gap_pix.value) / 2. - 1.) & (y < (ccd_pix.value + gap_pix.value) - 1.)) | # upper left CCD ((x > 0.0) & (x < (ccd_pix.value - gap_pix.value) / 2. - 1.) & (y > (ccd_pix.value + gap_pix.value) / 2. - 1.) & (y < (ccd_pix.value + gap_pix.value) - 1.))) elif not withgaps: onchip += ((x > 0.) & (x < ccd_pix.value + gap_pix.value - 1.) & (y > 0.) & (y < ccd_pix.value + gap_pix.value - 1.)) except Exception as e: print('failed computing onchip. error msg: {:s}'.format(e)) return 0 if verbose: print( 'computed sector numbers for {:d} stars/objects'.format(n_coords)) return onchip.astype(np.int)