Exemplo n.º 1
0
    def preload(self):
        """Preload the files into memory.

        There are memory implications to this, so we don't do this by default.  However, it can be
        a big speedup if memory isn't an issue.  Especially if many (or all) of the images are
        stored in the same file as different HDUs.
        """
        from multiprocessing import Lock
        from galsim._pyfits import pyfits
        if self.logger:
            self.logger.debug('RealGalaxyCatalog: start preload')
        for file_name in np.concatenate((self.gal_file_name , self.psf_file_name)):
            # numpy sometimes add a space at the end of the string that is not present in
            # the original file.  Stupid.  But this next line removes it.
            file_name = file_name.strip()
            if file_name not in self.loaded_files:
                if self.logger:
                    self.logger.debug('RealGalaxyCatalog: preloading %s',file_name)
                # I use memmap=False, because I was getting problems with running out of
                # file handles in the great3 real_gal run, which uses a lot of rgc files.
                # I think there must be a bug in pyfits that leaves file handles open somewhere
                # when memmap = True.  Anyway, I don't know what the performance implications
                # are (since I couldn't finish the run with the default memmap=True), but I
                # don't think there is much impact either way with memory mapping in our case.
                self.loaded_files[file_name] = pyfits.open(file_name,memmap=False)
Exemplo n.º 2
0
    def read_fits(self):
        """Read in a DES_Shapelet stored using the the FITS-file version.
        """
        from galsim._pyfits import pyfits
        with pyfits.open(self.file_name) as fits:
            cat = fits[1].data
        # These fields each only contain one element, hence the [0]'s.
        self.psf_order = cat.field('psf_order')[0]
        self.psf_size = (self.psf_order + 1) * (self.psf_order + 2) // 2
        self.sigma = cat.field('sigma')[0]
        self.fit_order = cat.field('fit_order')[0]
        self.fit_size = (self.fit_order + 1) * (self.fit_order + 2) // 2
        self.npca = cat.field('npca')[0]

        self.bounds = galsim.BoundsD(float(cat.field('xmin')[0]),
                                     float(cat.field('xmax')[0]),
                                     float(cat.field('ymin')[0]),
                                     float(cat.field('ymax')[0]))

        self.ave_psf = cat.field('ave_psf')[0]
        assert self.ave_psf.shape == (self.psf_size, )

        # Note: older pyfits versions don't get the shape right.
        # For newer pyfits versions the reshape command should be a no op.
        self.rot_matrix = cat.field('rot_matrix')[0].reshape(
            (self.psf_size, self.npca)).T
        assert self.rot_matrix.shape == (self.npca, self.psf_size)

        self.interp_matrix = cat.field('interp_matrix')[0].reshape(
            (self.npca, self.fit_size)).T
        assert self.interp_matrix.shape == (self.fit_size, self.npca)
Exemplo n.º 3
0
 def _getFile(self, file_name):
     from multiprocessing import Lock
     from galsim._pyfits import pyfits
     if file_name in self.loaded_files:
         if self.logger:
             self.logger.debug('RealGalaxyCatalog: File %s is already open',
                               file_name)
         f = self.loaded_files[file_name]
     else:
         self.loaded_lock.acquire()
         # Check again in case two processes both hit the else at the same time.
         if file_name in self.loaded_files:
             if self.logger:
                 self.logger.debug(
                     'RealGalaxyCatalog: File %s is already open',
                     file_name)
             f = self.loaded_files[file_name]
         else:
             if self.logger:
                 self.logger.debug('RealGalaxyCatalog: open file %s',
                                   file_name)
             f = pyfits.open(file_name, memmap=False)
             self.loaded_files[file_name] = f
         self.loaded_lock.release()
     return f
Exemplo n.º 4
0
    def read_fits(self):
        """Read in a DES_Shapelet stored using the the FITS-file version.
        """
        from galsim._pyfits import pyfits
        with pyfits.open(self.file_name) as fits:
            cat = fits[1].data
        # These fields each only contain one element, hence the [0]'s.
        self.psf_order = cat.field('psf_order')[0]
        self.psf_size = (self.psf_order+1) * (self.psf_order+2) // 2
        self.sigma = cat.field('sigma')[0]
        self.fit_order = cat.field('fit_order')[0]
        self.fit_size = (self.fit_order+1) * (self.fit_order+2) // 2
        self.npca = cat.field('npca')[0]

        self.bounds = galsim.BoundsD(
            float(cat.field('xmin')[0]), float(cat.field('xmax')[0]),
            float(cat.field('ymin')[0]), float(cat.field('ymax')[0]))

        self.ave_psf = cat.field('ave_psf')[0]
        assert self.ave_psf.shape == (self.psf_size,)

        # Note: older pyfits versions don't get the shape right.
        # For newer pyfits versions the reshape command should be a no op.
        self.rot_matrix = cat.field('rot_matrix')[0].reshape((self.psf_size,self.npca)).T
        assert self.rot_matrix.shape == (self.npca, self.psf_size)

        self.interp_matrix = cat.field('interp_matrix')[0].reshape((self.npca,self.fit_size)).T
        assert self.interp_matrix.shape == (self.fit_size, self.npca)
Exemplo n.º 5
0
def PhotonArray_read(cls, file_name):
    """Create a PhotonArray, reading the photon data from a FITS file.

    The file being read in is not arbitrary.  It is expected to be a file that was written
    out with the PhotonArray `write` method.

        >>> photons.write('photons.fits')
        >>> photons2 = galsim.PhotonArray.read('photons.fits')

    @param file_name    The file name of the input FITS file.
    """
    from galsim._pyfits import pyfits, pyfits_version
    with pyfits.open(file_name) as fits:
        data = fits[1].data
    N = len(data)
    if pyfits_version > '3.0':
        names = data.columns.names
    else:  # pragma: no cover
        names = data.dtype.names

    ret = cls.__new__(cls)
    _PhotonArray_empty_init(ret, N)
    ret.x = data['x']
    ret.y = data['y']
    ret.flux = data['flux']
    if 'dxdz' in names:
        ret.dxdz = data['dxdz']
        ret.dydz = data['dydz']
    if 'wavelength' in names:
        ret.wavelength = data['wavelength']
    return ret
Exemplo n.º 6
0
    def preload(self):
        """Preload the files into memory.

        There are memory implications to this, so we don't do this by default.  However, it can be
        a big speedup if memory isn't an issue.  Especially if many (or all) of the images are
        stored in the same file as different HDUs.
        """
        from multiprocessing import Lock
        from galsim._pyfits import pyfits
        if self.logger:
            self.logger.debug('RealGalaxyCatalog: start preload')
        for file_name in np.concatenate((self.gal_file_name , self.psf_file_name)):
            # numpy sometimes add a space at the end of the string that is not present in
            # the original file.  Stupid.  But this next line removes it.
            file_name = file_name.strip()
            if file_name not in self.loaded_files:
                if self.logger:
                    self.logger.debug('RealGalaxyCatalog: preloading %s',file_name)
                # I use memmap=False, because I was getting problems with running out of
                # file handles in the great3 real_gal run, which uses a lot of rgc files.
                # I think there must be a bug in pyfits that leaves file handles open somewhere
                # when memmap = True.  Anyway, I don't know what the performance implications
                # are (since I couldn't finish the run with the default memmap=True), but I
                # don't think there is much impact either way with memory mapping in our case.
                self.loaded_files[file_name] = pyfits.open(file_name,memmap=False)
Exemplo n.º 7
0
	def get_bundled_psfex_psf(self, iobj, iexp, options, return_profile=False):

		wcs_path = self.get_source_info(iobj,iexp)[3].strip()

		wcs_path = check_wcs(wcs_path)

		#Find the exposure name
		source_path = self.get_source_path(iobj, iexp)
		source = os.path.split(source_path)[1].split('.')[0]

		#PSF blacklist
		if source in self.blacklist:
			if options.verbosity>2:
				print "%s blacklisted" % source
			return None

		#get the HDU name corresponding to that name.
		#Might need to tweak that naming
		hdu_name = "psf_"+options.psfex_rerun_version+"_"+source+"_psfcat"

		#Maybe we have it in the cache already
		if hdu_name in PSFEX_CACHE:
			psfex_i = PSFEX_CACHE[hdu_name]
			if psfex_i is None:
				return None

		else:
    		#Turn the HDU into a PSFEx object
    		#PSFEx expects a pyfits HDU not fitsio.
    		#This is insane.  I know this.
			import galsim.des
			try:
				pyfits = galsim.pyfits
			except AttributeError:
				from galsim._pyfits import pyfits

			try:
				hdu = pyfits.open(self._filename)[hdu_name]
			except KeyError:
				PSFEX_CACHE[hdu_name] = None
				if options.verbosity>3:
					print "Could not find HDU %s in %s" % (hdu_name, self._filename)

				return None

			try:
				psfex_i = galsim.des.DES_PSFEx(hdu, wcs_path)
			except IOError:
				print "PSF bad but not blacklisted: %s in %s"%(hdu_name, self._filename)
				psfex_i = None

			PSFEX_CACHE[hdu_name] = psfex_i

		if psfex_i == None:
			return None

		#Get the image array
		return self.extract_psfex_psf(psfex_i, iobj, iexp, options, return_profile=return_profile)
Exemplo n.º 8
0
    def getNoiseProperties(self, i):
        """Returns the components needed to make the noise correlation function at index `i`.
           Specifically, the noise image (or None), the pixel_scale, and the noise variance,
           as a tuple (im, scale, var).
        """

        if self.logger:
            self.logger.debug('RealGalaxyCatalog %d: Start getNoise', i)
        if self.noise_file_name is None:
            im = None
        else:
            if i >= len(self.noise_file_name):
                raise IndexError(
                    'index %d given to getNoise is out of range (0..%d)' %
                    (i, len(self.noise_file_name) - 1))
            if self.noise_file_name[i] in self.saved_noise_im:
                im = self.saved_noise_im[self.noise_file_name[i]]
                if self.logger:
                    self.logger.debug(
                        'RealGalaxyCatalog %d: Got saved noise im', i)
            else:
                self.noise_lock.acquire()
                # Again, a second check in case two processes get here at the same time.
                if self.noise_file_name[i] in self.saved_noise_im:
                    im = self.saved_noise_im[self.noise_file_name[i]]
                    if self.logger:
                        self.logger.debug(
                            'RealGalaxyCatalog %d: Got saved noise im', i)
                else:
                    from galsim._pyfits import pyfits
                    with pyfits.open(self.noise_file_name[i]) as fits:
                        array = fits[0].data
                    im = galsim.Image(np.ascontiguousarray(
                        array.astype(np.float64)),
                                      scale=self.pixel_scale[i])
                    self.saved_noise_im[self.noise_file_name[i]] = im
                    if self.logger:
                        self.logger.debug(
                            'RealGalaxyCatalog %d: Built noise im', i)
                self.noise_lock.release()

        return im, self.pixel_scale[i], self.variance[i]
Exemplo n.º 9
0
 def readFits(self, hdu, _nobjects_only=False):
     """Read in an input catalog from a FITS file.
     """
     from galsim._pyfits import pyfits, pyfits_version
     with pyfits.open(self.file_name) as fits:
         raw_data = fits[hdu].data
     if pyfits_version > '3.0':
         self.names = raw_data.columns.names
     else:
         self.names = raw_data.dtype.names
     self.nobjects = len(raw_data.field(self.names[0]))
     if (_nobjects_only): return
     # The pyfits raw_data is a FITS_rec object, which isn't picklable, so we need to
     # copy the fields into a new structure to make sure our Catalog is picklable.
     # The simplest is probably a dict keyed by the field names, which we save as self.data.
     self.data = {}
     for name in self.names:
         self.data[name] = raw_data.field(name)
     self.ncols = len(self.names)
     self.isfits = True
Exemplo n.º 10
0
 def readFits(self, hdu, _nobjects_only=False):
     """Read in an input catalog from a FITS file.
     """
     from galsim._pyfits import pyfits, pyfits_version
     with pyfits.open(self.file_name) as fits:
         raw_data = fits[hdu].data
     if pyfits_version > '3.0':
         self.names = raw_data.columns.names
     else:
         self.names = raw_data.dtype.names
     self.nobjects = len(raw_data.field(self.names[0]))
     if (_nobjects_only): return
     # The pyfits raw_data is a FITS_rec object, which isn't picklable, so we need to
     # copy the fields into a new structure to make sure our Catalog is picklable.
     # The simplest is probably a dict keyed by the field names, which we save as self.data.
     self.data = {}
     for name in self.names:
         self.data[name] = raw_data.field(name)
     self.ncols = len(self.names)
     self.isfits = True
Exemplo n.º 11
0
 def _getFile(self, file_name):
     from multiprocessing import Lock
     from galsim._pyfits import pyfits
     if file_name in self.loaded_files:
         if self.logger:
             self.logger.debug('RealGalaxyCatalog: File %s is already open',file_name)
         f = self.loaded_files[file_name]
     else:
         self.loaded_lock.acquire()
         # Check again in case two processes both hit the else at the same time.
         if file_name in self.loaded_files:
             if self.logger:
                 self.logger.debug('RealGalaxyCatalog: File %s is already open',file_name)
             f = self.loaded_files[file_name]
         else:
             if self.logger:
                 self.logger.debug('RealGalaxyCatalog: open file %s',file_name)
             f = pyfits.open(file_name,memmap=False)
             self.loaded_files[file_name] = f
         self.loaded_lock.release()
     return f
Exemplo n.º 12
0
    def read(self):
        """Read in relevant catalog information"""

        from galsim._pyfits import pyfits

        if isinstance(self.file_name, basestring):
            # If a filename is passed:
            hdu_list = pyfits.open(self.file_name)
            model_fits = hdu_list[1]
        else:
            # If a fits HDU is directly passed:
            hdu_list = None
            model_fits = self.file_name

        self.catalog = model_fits.data

        # NB: As discussed in `scene.py`, there is a bug in the pyfits FITS_Rec class that leads to memory leaks.
        # The simplest workaround seems to be to convert it to a regular numpy recarray.
        self.catalog = np.array(self.catalog, copy=True)

        # The input logger needs to know the original catalog size
        self.ntotal = len(self.catalog)

        # Close file!
        if hdu_list: hdu_list.close()

        # Galaxy indices in original ngmix catalog
        self.orig_index = np.arange(self.ntotal)

        # Get flags and create mask
        self.getFlags()
        self.makeMask()

        # Do mask cut
        self.maskCut()

        # pudb.set_trace()

        return
Exemplo n.º 13
0
    def getNoiseProperties(self, i):
        """Returns the components needed to make the noise correlation function at index `i`.
           Specifically, the noise image (or None), the pixel_scale, and the noise variance,
           as a tuple (im, scale, var).
        """

        if self.logger:
            self.logger.debug('RealGalaxyCatalog %d: Start getNoise',i)
        if self.noise_file_name is None:
            im = None
        else:
            if i >= len(self.noise_file_name):
                raise IndexError(
                    'index %d given to getNoise is out of range (0..%d)'%(
                        i,len(self.noise_file_name)-1))
            if self.noise_file_name[i] in self.saved_noise_im:
                im = self.saved_noise_im[self.noise_file_name[i]]
                if self.logger:
                    self.logger.debug('RealGalaxyCatalog %d: Got saved noise im',i)
            else:
                self.noise_lock.acquire()
                # Again, a second check in case two processes get here at the same time.
                if self.noise_file_name[i] in self.saved_noise_im:
                    im = self.saved_noise_im[self.noise_file_name[i]]
                    if self.logger:
                        self.logger.debug('RealGalaxyCatalog %d: Got saved noise im',i)
                else:
                    from galsim._pyfits import pyfits
                    with pyfits.open(self.noise_file_name[i]) as fits:
                        array = fits[0].data
                    im = galsim.Image(np.ascontiguousarray(array.astype(np.float64)),
                                      scale=self.pixel_scale[i])
                    self.saved_noise_im[self.noise_file_name[i]] = im
                    if self.logger:
                        self.logger.debug('RealGalaxyCatalog %d: Built noise im',i)
                self.noise_lock.release()

        return im, self.pixel_scale[i], self.variance[i]
Exemplo n.º 14
0
def test_psf():
    """Test the two kinds of PSF files we have in DES.
    """
    data_dir = 'des_data'
    psfex_file = "DECam_00154912_12_psfcat.psf"
    fitpsf_file = "DECam_00154912_12_fitpsf.fits"
    wcs_file = "DECam_00154912_12_header.fits"

    wcs = galsim.FitsWCS(wcs_file, dir=data_dir)

    # We don't require that the files in example_data_dir have been downloaded.  If they
    # haven't, then we just directly set the comparison values that we want here.
    example_data_dir = '../examples/des/des_data'
    cat_file = "DECam_00154912_12_cat.fits"
    image_file = "DECam_00154912_12.fits.fz"

    try:
        cat = galsim.Catalog(cat_file, hdu=2, dir=example_data_dir)
        size = numpy.array([ cat.getFloat(i,'FLUX_RADIUS') for i in range(cat.nobjects) ])
        mag = numpy.array([ cat.getFloat(i,'MAG_AUTO') for i in range(cat.nobjects) ])
        flags = numpy.array([ cat.getInt(i,'FLAGS') for i in range(cat.nobjects) ])
        index = numpy.array(range(cat.nobjects))
        xvals = numpy.array([ cat.getFloat(i,'X_IMAGE') for i in range(cat.nobjects) ])
        yvals = numpy.array([ cat.getFloat(i,'Y_IMAGE') for i in range(cat.nobjects) ])

        # Pick bright small objects as probable stars
        mask = (flags == 0) & (mag < 14) & (mag > 13) & (size > 2) & (size < 2.5)
        idx = numpy.argsort(size[mask])

        # This choice of a star is fairly isolated from neighbors, isn't too near an edge or a tape
        # bump, and doesn't have any noticeable image artifacts in its vicinity.
        x = xvals[mask][idx][27]
        y = yvals[mask][idx][27]
        print('Using x,y = ',x,y)
        image_pos = galsim.PositionD(x,y)
        print('size, mag = ',size[mask][idx][27], mag[mask][idx][27])

        data = galsim.fits.read(image_file, dir=example_data_dir)
        b = galsim.BoundsI(int(x)-15, int(x)+16, int(y)-15, int(y)+16)
        data_stamp = data[b]

        header = galsim.fits.FitsHeader(image_file, dir=example_data_dir)
        sky_level = header['SKYBRITE']
        data_stamp -= sky_level

        raw_meas = data_stamp.FindAdaptiveMom()
        print('raw_meas = ',raw_meas)
        ref_size = raw_meas.moments_sigma
        ref_shape = raw_meas.observed_shape
        print('ref size: ',ref_size)
        print('ref shape: ',ref_shape)

    except IOError:
        x,y = 1195.64074707, 1276.63427734
        image_pos = galsim.PositionD(x,y)
        b = galsim.BoundsI(int(x)-15, int(x)+16, int(y)-15, int(y)+16)
        ref_size = 1.80668628216
        ref_shape = galsim.Shear(g1=0.022104322221,g2=-0.130925191715)

    # First the PSFEx model using the wcs_file to get the model is sky coordinates.
    psfex = galsim.des.DES_PSFEx(psfex_file, wcs_file, dir=data_dir)
    psf = psfex.getPSF(image_pos)

    # The getLocalWCS function should return a local WCS
    assert psfex.getLocalWCS(image_pos).isLocal()

    # Draw the postage stamp image
    # Note: the PSF already includes the pixel response, so draw with method 'no_pixel'.
    stamp = psf.drawImage(wcs=wcs.local(image_pos), bounds=b, method='no_pixel')
    print('wcs = ',wcs.local(image_pos))
    meas = stamp.FindAdaptiveMom()
    print('meas = ',meas)
    print('pixel scale = ',stamp.wcs.minLinearScale(image_pos=image_pos))
    print('cf sizes: ',ref_size, meas.moments_sigma)
    print('cf shapes: ',ref_shape, meas.observed_shape)
    # The agreement for a single star is not great of course, not even 2 decimals.
    # Divide by 2 to get agreement at 2 dp.
    numpy.testing.assert_almost_equal(meas.moments_sigma/2, ref_size/2, decimal=2,
                                      err_msg="PSFEx size doesn't match")
    numpy.testing.assert_almost_equal(meas.observed_shape.g1/2, ref_shape.g1/2, decimal=2,
                                      err_msg="PSFEx shape.g1 doesn't match")
    numpy.testing.assert_almost_equal(meas.observed_shape.g2/2, ref_shape.g2/2, decimal=2,
                                      err_msg="PSFEx shape.g2 doesn't match")

    # Repeat without the wcs_file argument, so the model is in chip coordinates.
    # Also check the functionality where the file is already open.
    with pyfits.open(os.path.join(data_dir, psfex_file)) as hdu_list:
        psfex = galsim.des.DES_PSFEx(hdu_list[1])
    psf = psfex.getPSF(image_pos)

    # In this case, the getLocalWCS function won't return anything useful.
    assert psfex.getLocalWCS(image_pos) is None

    # Draw the postage stamp image.  This time in image coords, so pixel_scale = 1.0.
    stamp = psf.drawImage(bounds=b, scale=1.0, method='no_pixel')
    meas = stamp.FindAdaptiveMom()
    numpy.testing.assert_almost_equal(meas.moments_sigma/2, ref_size/2, decimal=2,
                                      err_msg="no-wcs PSFEx size doesn't match")
    numpy.testing.assert_almost_equal(meas.observed_shape.g1/2, ref_shape.g1/2, decimal=2,
                                      err_msg="no-wcs PSFEx shape.g1 doesn't match")
    numpy.testing.assert_almost_equal(meas.observed_shape.g2/2, ref_shape.g2/2, decimal=2,
                                      err_msg="no-wcs PSFEx shape.g2 doesn't match")

    with assert_raises(TypeError):
        # file_name must be a string.
        galsim.des.DES_PSFEx(psf, wcs=wcs_file, dir=data_dir)
    with assert_raises(galsim.GalSimError):
        # Cannot provide both image_file_name and wcs
        galsim.des.DES_PSFEx(psfex_file, image_file_name=wcs_file, wcs=wcs_file, dir=data_dir)
    with assert_raises((IOError, OSError)):
        # This one doesn't exist.
        galsim.des.DES_PSFEx('nonexistant.psf', wcs=wcs_file, dir=data_dir)
    with assert_raises(OSError):
        # This one exists, but has invalid header parameters.
        galsim.des.DES_PSFEx('invalid_psfcat.psf', wcs=wcs_file, dir=data_dir)

    # Now the shapelet PSF model.  This model is already in sky coordinates, so no wcs_file needed.
    fitpsf = galsim.des.DES_Shapelet(os.path.join(data_dir,fitpsf_file))
    psf = fitpsf.getPSF(image_pos)

    # Draw the postage stamp image
    # Again, the PSF already includes the pixel response.
    stamp = psf.drawImage(wcs=wcs.local(image_pos), bounds=b, method='no_pixel')
    meas = stamp.FindAdaptiveMom()
    numpy.testing.assert_almost_equal(meas.moments_sigma/2, ref_size/2, decimal=2,
                                      err_msg="Shapelet PSF size doesn't match")
    numpy.testing.assert_almost_equal(meas.observed_shape.g1/2, ref_shape.g1/2, decimal=2,
                                      err_msg="Shapelet PSF shape.g1 doesn't match")
    numpy.testing.assert_almost_equal(meas.observed_shape.g2/2, ref_shape.g2/2, decimal=2,
                                      err_msg="Shapelet PSF shape.g2 doesn't match")

    with assert_raises(galsim.GalSimBoundsError):
        fitpsf.getPSF(image_pos = galsim.PositionD(4000, 5000))
Exemplo n.º 15
0
def storePSFImages(PSF_dict, filename, bandpass_list=None, clobber=False):
    """
    This is a routine to store images of chromatic WFIRST PSFs in different bands for each SCA.  It
    takes an output dict of PSFs (`PSF_dict`) directly from getPSF().  The output will be a file
    (`filename`) that has all the images, along with an HDU that contains a FITS table indicating
    the bandpasses, SCAs, and other information needed to reconstruct the PSF information.

    This routine is not meant to work for PSFs from getPSF() that are completely achromatic.  The
    reason for this is that those PSFs are quite fast to generate, so there is little benefit to
    storing them.

    @param PSF_dict            A dict of PSF objects for each SCA, in the same format as output by
                               the getPSF() routine (though it can take versions that have been
                               modified, for example in the inclusion of an SED).
    @param filename            The name of the file to which the images and metadata should be
                               written; extension should be *.fits.
    @param bandpass_list       A list of bandpass names for which images should be generated and
                               stored.  If None, all WFIRST imaging passbands are used.
                               [default: None]
    @param clobber             Should the routine clobber `filename` (if they already exist)?
                               [default: False]
    """
    from galsim._pyfits import pyfits
    # Check for sane input PSF_dict.
    if len(PSF_dict) == 0 or len(PSF_dict) > galsim.wfirst.n_sca or \
            min(PSF_dict.keys()) < 1 or max(PSF_dict.keys()) > galsim.wfirst.n_sca:
        raise ValueError("PSF_dict must come from getPSF()!")

    # Check if file already exists and warn about clobbering.
    if os.path.exists(filename):
        if clobber is False:
            raise ValueError("Output file already exists, and clobber is not set!")
        else:
            import warnings
            warnings.warn("Output file already exists, and will be clobbered.")

    # Check that bandpass list input is okay.  It should be strictly a subset of the default list of
    # bandpasses.
    if bandpass_list is None:
        bandpass_list = default_bandpass_list
    else:
        if not isinstance(bandpass_list[0], str):
            raise ValueError("Expected input list of bandpass names!")
        if not set(bandpass_list).issubset(default_bandpass_list):
            err_msg = ''
            for item in default_bandpass_list:
                err_msg += item+' '
            raise ValueError("Bandpass list must be a subset of the default list, containing %s"
                             %err_msg)

    # Get all the WFIRST bandpasses.
    bandpass_dict = galsim.wfirst.getBandpasses()

    # Loop through making images and lists of their relevant parameters.
    im_list = []
    bp_name_list = []
    SCA_index_list = []
    for SCA in PSF_dict.keys():
        PSF = PSF_dict[SCA]
        if not isinstance(PSF, galsim.ChromaticOpticalPSF) and \
                not isinstance(PSF, galsim.InterpolatedChromaticObject):
            raise RuntimeError("Error, PSFs are not ChromaticOpticalPSFs.")
        star = galsim.Gaussian(sigma=1.e-8, flux=1.)

        for bp_name in bandpass_list:
            bandpass = bandpass_dict[bp_name]
            star_sed = galsim.SED(lambda x:1, 'nm', 'flambda').withFlux(1, bandpass)
            obj = galsim.Convolve(star*star_sed, PSF)

            im = obj.drawImage(bandpass, scale=0.5*galsim.wfirst.pixel_scale,
                               method='no_pixel')
            im_list.append(im)
            bp_name_list.append(bp_name)
            SCA_index_list.append(SCA)

    # Save images to file.
    n_ims = len(im_list)
    galsim.fits.writeMulti(im_list, filename, clobber=clobber)

    # Add data to file, after constructing a FITS table.  Watch out for clobbering.
    bp_names = pyfits.Column(name='bandpass', format='A10', array=np.array(bp_name_list))
    SCA_indices = pyfits.Column(name='SCA', format='J', array=np.array(SCA_index_list))
    cols = pyfits.ColDefs([bp_names, SCA_indices])
    tbhdu = pyfits.BinTableHDU.from_columns(cols)
    f = pyfits.open(filename, mode='update')
    f.append(tbhdu)
    f.flush()
    f.close()
Exemplo n.º 16
0
    def read(self):
        from galsim._pyfits import pyfits
        if isinstance(self.file_name, basestring):
            hdu = pyfits.open(self.file_name)[1]
        else:
            hdu = self.file_name
        # Number of parameters used for the interpolation.  We require this to be 2.
        pol_naxis = hdu.header['POLNAXIS']
        if pol_naxis != 2:
            raise IOError("PSFEx: Expected POLNAXIS == 2, got %d"%pol_naxis)

        # These are the names of the two axes.  Should be X_IMAGE, Y_IMAGE.
        # If they aren't, then the way we use the interpolation will be wrong.
        # Well, really they can also be XWIN_IMAGE, etc.  So just check that it 
        # starts with X and ends with IMAGE.
        pol_name1 = hdu.header['POLNAME1']
        if not (pol_name1.startswith('X') and pol_name1.endswith('IMAGE')):
            raise IOError("PSFEx: Expected POLNAME1 == X*_IMAGE, got %s"%pol_name1)
        pol_name2 = hdu.header['POLNAME2']
        if not (pol_name2.startswith('Y') and pol_name2.endswith('IMAGE')):
            raise IOError("PSFEx: Expected POLNAME2 == Y*_IMAGE, got %s"%pol_name2)

        # Zero points and scale.  Interpolation is in terms of (x-x0)/xscale, (y-y0)/yscale
        pol_zero1 = hdu.header['POLZERO1']
        pol_zero2 = hdu.header['POLZERO2']
        pol_scal1 = hdu.header['POLSCAL1']
        pol_scal2 = hdu.header['POLSCAL2']

        # This defines the number of "context groups".
        # Here is Emmanuel's explanation:
        #
        #     POLNGRP is the number of "context groups". A group represents a set of variables 
        #     (SExtractor measurements or FITS header parameters if preceded with ":") which share 
        #     the same maximum polynomial degree. For instance if x and y are in group 1, and the 
        #     degree of that group is 2, and z is in group 2 with degree 1, the polynomial will 
        #     consist of:
        #         1, x, x^2, y, y.x, y^2, z, z.x^2, z.y, z.y.x, z.y^2
        #     (see eq 14 in https://www.astromatic.net/pubsvn/software/psfex/trunk/doc/psfex.pdf )
        #     By default, POLNGRP is 1 and the group contains X_IMAGE and Y_IMAGE measurements 
        #     from SExtractor.
        #
        # For now, we require this to be 1, since I didn't have any files with POLNGRP != 1 to 
        # test on.
        pol_ngrp = hdu.header['POLNGRP']
        if pol_ngrp != 1:
            raise IOError("PSFEx: Current implementation requires POLNGRP == 1, got %d"%pol_ngrp)

        # Which group each item is in.  We require group 1.
        pol_group1 = hdu.header['POLGRP1']
        if pol_group1 != 1:
            raise IOError("PSFEx: Expected POLGRP1 == 1, got %s"%pol_group1)
        pol_group2 = hdu.header['POLGRP2']
        if pol_group2 != 1:
            raise IOError("PSFEx: Expected POLGRP2 == 1, got %s"%pol_group2)

        # The degree of the polynomial.  E.g. POLDEG1 = 2 means the values will be:
        #     1, x, x^2, y, xy, y^2
        # If we start allowing POLNGRP > 1, there is a separate degree for each group.
        pol_deg = hdu.header['POLDEG1']

        # The number of axes in the basis object.  We require this to be 3.
        psf_naxis = hdu.header['PSFNAXIS']
        if psf_naxis != 3:
            raise IOError("PSFEx: Expected PSFNAXIS == 3, got %d"%psfnaxis)

        # The first two axes are the image size of the PSF postage stamp.
        psf_axis1 = hdu.header['PSFAXIS1']
        psf_axis2 = hdu.header['PSFAXIS2']

        # The third axis is the direction of the polynomial interpolation.  So it should
        # be equal to (d+1)(d+2)/2.
        psf_axis3 = hdu.header['PSFAXIS3']
        if psf_axis3 != ((pol_deg+1)*(pol_deg+2))/2:
            raise IOError("PSFEx: POLDEG and PSFAXIS3 disagree")

        # This is the PSF "sample size".  Again, from Emmanuel:
        #
        #     PSF_SAMP is the sampling step of the PSF. PSF_SAMP=0.5 means that the PSF model has 
        #     two samples per original image pixel (superresolution, so in automatic mode it is a 
        #     sign that the original images were undersampled)
        #
        # In other words, it can be thought of as a unit conversion:
        #     "image pixels" / "psfex pixels"
        # So 1 image pixel = (1/psf_samp) psfex pixels.
        psf_samp = hdu.header['PSF_SAMP']

        # The basis object is a data cube (assuming PSFNAXIS==3)
        # Note: older pyfits versions don't get the shape right.
        # For newer pyfits versions the reshape command should be a no op.
        basis = hdu.data.field('PSF_MASK')[0].reshape(psf_axis3,psf_axis2,psf_axis1)
        # Make sure this turned out right.
        if basis.shape[0] != psf_axis3:
            raise IOError("PSFEx: PSFAXIS3 disagrees with actual basis size")
        if basis.shape[1] != psf_axis2:
            raise IOError("PSFEx: PSFAXIS2 disagrees with actual basis size")
        if basis.shape[2] != psf_axis1:
            raise IOError("PSFEx: PSFAXIS1 disagrees with actual basis size")

        # Save some of these values for use in building the interpolated images
        self.basis = basis
        self.fit_order = pol_deg
        self.fit_size = psf_axis3
        self.x_zero = pol_zero1
        self.y_zero = pol_zero2
        self.x_scale = pol_scal1
        self.y_scale = pol_scal2
        self.sample_scale = psf_samp
Exemplo n.º 17
0
def test_wfirst_bandpass():
    """Test the WFIRST bandpasses for basic sanity.
    """
    from galsim._pyfits import pyfits

    # Obtain the bandpasses with AB_zeropoint set
    exp_time = 200.  # non WFIRST exposure time
    bp = galsim.wfirst.getBandpasses(AB_zeropoint=True, exptime=exp_time)

    # Check if the zeropoints have been set correctly
    AB_spec = lambda x: (3631e-23)*exp_time*(np.pi)*(100.**2)*\
              (galsim.wfirst.diameter**2)*(1-galsim.wfirst.obscuration**2)/4.
    AB_sed = galsim.SED(spec=AB_spec, wave_type='nm', flux_type='fnu')
    for filter_name, filter_ in bp.items():
        mag = AB_sed.calculateMagnitude(bandpass=filter_)
        np.testing.assert_almost_equal(
            mag,
            0.0,
            decimal=6,
            err_msg="Zeropoint not set accurately enough for bandpass filter \
            {0}".format(filter_name))

    # Do a slightly less trivial check of bandpass-related calculations:
    # Jeff Kruk (at Goddard) took an SED template from the Castelli-Kurucz library, normalized it to
    # a particular magnitude in SDSS g band, and checked the count rates he expects for the WFIRST
    # bands.  I (RM) independently did the same calculation (downloading the templates and bandpass
    # myself and using GalSim for all the important bits of the calculation) and my results agree a
    # the 5% level.  Given that I didn't quite have the same SED, we were very happy with this level
    # of agreement.  The unit test below reproduces this test, and requires agreement at the 10%
    # level.
    # Jeff used the C-K template with solar metallicity, T=9550K, surface gravity logg=3.95.  I
    # downloaded a grid of templates and just used the nearest one, which has solar metallicity,
    # T=9500K, surface gravity logg=4.0.
    with pyfits.open(os.path.join('wfirst_files', 'ckp00_9500.fits')) as fits:
        sed_data = fits[1].data
    lam = sed_data.WAVELENGTH.astype(np.float64)
    t = sed_data.g40.astype(np.float64)
    sed_tab = galsim.LookupTable(x=lam, f=t, interpolant='linear')
    sed = galsim.SED(sed_tab, wave_type='A', flux_type='flambda')

    # Now take the SDSS g bandpass:
    gfile = '/Users/rmandelb/Downloads/g.dat'
    bp_dat = np.loadtxt(os.path.join('wfirst_files', 'g.dat')).transpose()
    bp_tab = galsim.LookupTable(x=bp_dat[0, :],
                                f=bp_dat[1, :],
                                interpolant='linear')
    bp_ref = galsim.Bandpass(bp_tab, wave_type='A')
    # Set an AB zeropoint using WFIRST params:
    eff_diam = 100. * galsim.wfirst.diameter * np.sqrt(
        1. - galsim.wfirst.obscuration**2)
    bp_ref = bp_ref.withZeropoint('AB',
                                  effective_diameter=eff_diam,
                                  exptime=galsim.wfirst.exptime)
    # Now get a new SED that has magnitude -0.093 in this filter, since that's the normalization
    # that Jeff imposed for his tests.
    sed = sed.withMagnitude(-0.093, bp_ref)

    # Reference count rates, from Jeff:
    reference = {}
    reference['Z087'] = 1.98e10
    reference['Y106'] = 1.97e10
    reference['J129'] = 1.52e10
    reference['H158'] = 1.11e10
    reference['F184'] = 0.58e10
    reference['W149'] = 4.34e10

    # Only 10% accuracy required because did not use quite the same stellar template.  Fortunately,
    # bugs can easily lead to orders of magnitude errors, so this unit test is still pretty
    # non-trivial.
    for filter_name, filter_ in bp.items():
        flux = sed.calculateFlux(filter_)
        count_rate = flux / galsim.wfirst.exptime
        np.testing.assert_allclose(
            count_rate,
            reference[filter_name],
            rtol=0.1,
            err_msg="Count rate for stellar model not as expected for bandpass "
            "{0}".format(filter_name))
Exemplo n.º 18
0
def storePSFImages(PSF_dict, filename, bandpass_list=None, clobber=False):
    """
    This is a routine to store images of chromatic WFIRST PSFs in different bands for each SCA.  It
    takes an output dict of PSFs (`PSF_dict`) directly from getPSF().  The output will be a file
    (`filename`) that has all the images, along with an HDU that contains a FITS table indicating
    the bandpasses, SCAs, and other information needed to reconstruct the PSF information.

    This routine is not meant to work for PSFs from getPSF() that are completely achromatic.  The
    reason for this is that those PSFs are quite fast to generate, so there is little benefit to
    storing them.

    @param PSF_dict            A dict of PSF objects for each SCA, in the same format as output by
                               the getPSF() routine (though it can take versions that have been
                               modified, for example in the inclusion of an SED).
    @param filename            The name of the file to which the images and metadata should be
                               written; extension should be *.fits.
    @param bandpass_list       A list of bandpass names for which images should be generated and
                               stored.  If None, all WFIRST imaging passbands are used.
                               [default: None]
    @param clobber             Should the routine clobber `filename` (if they already exist)?
                               [default: False]
    """
    from galsim._pyfits import pyfits
    # Check for sane input PSF_dict.
    if len(PSF_dict) == 0 or len(PSF_dict) > galsim.wfirst.n_sca or \
            min(PSF_dict.keys()) < 1 or max(PSF_dict.keys()) > galsim.wfirst.n_sca:
        raise ValueError("PSF_dict must come from getPSF()!")

    # Check if file already exists and warn about clobbering.
    if os.path.exists(filename):
        if clobber is False:
            raise ValueError(
                "Output file already exists, and clobber is not set!")
        else:
            import warnings
            warnings.warn("Output file already exists, and will be clobbered.")

    # Check that bandpass list input is okay.  It should be strictly a subset of the default list of
    # bandpasses.
    if bandpass_list is None:
        bandpass_list = default_bandpass_list
    else:
        if not isinstance(bandpass_list[0], str):
            raise ValueError("Expected input list of bandpass names!")
        if not set(bandpass_list).issubset(default_bandpass_list):
            err_msg = ''
            for item in default_bandpass_list:
                err_msg += item + ' '
            raise ValueError(
                "Bandpass list must be a subset of the default list, containing %s"
                % err_msg)

    # Get all the WFIRST bandpasses.
    bandpass_dict = galsim.wfirst.getBandpasses()

    # Loop through making images and lists of their relevant parameters.
    im_list = []
    bp_name_list = []
    SCA_index_list = []
    for SCA in PSF_dict:
        PSF = PSF_dict[SCA]
        if not isinstance(PSF, galsim.ChromaticOpticalPSF) and \
                not isinstance(PSF, galsim.InterpolatedChromaticObject):
            raise RuntimeError("Error, PSFs are not ChromaticOpticalPSFs.")
        star = galsim.Gaussian(sigma=1.e-8, flux=1.)

        for bp_name in bandpass_list:
            bandpass = bandpass_dict[bp_name]
            star_sed = galsim.SED(lambda x: 1, 'nm',
                                  'flambda').withFlux(1, bandpass)
            obj = galsim.Convolve(star * star_sed, PSF)

            im = obj.drawImage(bandpass,
                               scale=0.5 * galsim.wfirst.pixel_scale,
                               method='no_pixel')
            im_list.append(im)
            bp_name_list.append(bp_name)
            SCA_index_list.append(SCA)

    # Save images to file.
    n_ims = len(im_list)
    galsim.fits.writeMulti(im_list, filename, clobber=clobber)

    # Add data to file, after constructing a FITS table.  Watch out for clobbering.
    bp_names = pyfits.Column(name='bandpass',
                             format='A10',
                             array=np.array(bp_name_list))
    SCA_indices = pyfits.Column(name='SCA',
                                format='J',
                                array=np.array(SCA_index_list))
    cols = pyfits.ColDefs([bp_names, SCA_indices])
    tbhdu = pyfits.BinTableHDU.from_columns(cols)
    f = pyfits.open(filename, mode='update')
    f.append(tbhdu)
    f.flush()
    f.close()
Exemplo n.º 19
0
def test_psf():
    """Test the two kinds of PSF files we have in DES.
    """
    data_dir = 'des_data'
    psfex_file = "DECam_00154912_12_psfcat.psf"
    fitpsf_file = "DECam_00154912_12_fitpsf.fits"
    wcs_file = "DECam_00154912_12_header.fits"

    wcs = galsim.FitsWCS(wcs_file, dir=data_dir)

    # We don't require that the files in example_data_dir have been downloaded.  If they
    # haven't, then we just directly set the comparison values that we want here.
    example_data_dir = '../examples/des/des_data'
    cat_file = "DECam_00154912_12_cat.fits"
    image_file = "DECam_00154912_12.fits.fz"

    try:
        cat = galsim.Catalog(cat_file, hdu=2, dir=example_data_dir)
        size = numpy.array(
            [cat.getFloat(i, 'FLUX_RADIUS') for i in range(cat.nobjects)])
        mag = numpy.array(
            [cat.getFloat(i, 'MAG_AUTO') for i in range(cat.nobjects)])
        flags = numpy.array(
            [cat.getInt(i, 'FLAGS') for i in range(cat.nobjects)])
        index = numpy.array(range(cat.nobjects))
        xvals = numpy.array(
            [cat.getFloat(i, 'X_IMAGE') for i in range(cat.nobjects)])
        yvals = numpy.array(
            [cat.getFloat(i, 'Y_IMAGE') for i in range(cat.nobjects)])

        # Pick bright small objects as probable stars
        mask = (flags
                == 0) & (mag < 14) & (mag > 13) & (size > 2) & (size < 2.5)
        idx = numpy.argsort(size[mask])

        # This choice of a star is fairly isolated from neighbors, isn't too near an edge or a tape
        # bump, and doesn't have any noticeable image artifacts in its vicinity.
        x = xvals[mask][idx][27]
        y = yvals[mask][idx][27]
        print('Using x,y = ', x, y)
        image_pos = galsim.PositionD(x, y)
        print('size, mag = ', size[mask][idx][27], mag[mask][idx][27])

        data = galsim.fits.read(image_file, dir=example_data_dir)
        b = galsim.BoundsI(int(x) - 15, int(x) + 16, int(y) - 15, int(y) + 16)
        data_stamp = data[b]

        header = galsim.fits.FitsHeader(image_file, dir=example_data_dir)
        sky_level = header['SKYBRITE']
        data_stamp -= sky_level

        raw_meas = data_stamp.FindAdaptiveMom()
        print('raw_meas = ', raw_meas)
        ref_size = raw_meas.moments_sigma
        ref_shape = raw_meas.observed_shape
        print('ref size: ', ref_size)
        print('ref shape: ', ref_shape)

    except IOError:
        x, y = 1195.64074707, 1276.63427734
        image_pos = galsim.PositionD(x, y)
        b = galsim.BoundsI(int(x) - 15, int(x) + 16, int(y) - 15, int(y) + 16)
        ref_size = 1.80668628216
        ref_shape = galsim.Shear(g1=0.022104322221, g2=-0.130925191715)

    # First the PSFEx model using the wcs_file to get the model is sky coordinates.
    psfex = galsim.des.DES_PSFEx(psfex_file, wcs_file, dir=data_dir)
    psf = psfex.getPSF(image_pos)

    # The getLocalWCS function should return a local WCS
    assert psfex.getLocalWCS(image_pos).isLocal()

    # Draw the postage stamp image
    # Note: the PSF already includes the pixel response, so draw with method 'no_pixel'.
    stamp = psf.drawImage(wcs=wcs.local(image_pos),
                          bounds=b,
                          method='no_pixel')
    print('wcs = ', wcs.local(image_pos))
    meas = stamp.FindAdaptiveMom()
    print('meas = ', meas)
    print('pixel scale = ', stamp.wcs.minLinearScale(image_pos=image_pos))
    print('cf sizes: ', ref_size, meas.moments_sigma)
    print('cf shapes: ', ref_shape, meas.observed_shape)
    # The agreement for a single star is not great of course, not even 2 decimals.
    # Divide by 2 to get agreement at 2 dp.
    numpy.testing.assert_almost_equal(meas.moments_sigma / 2,
                                      ref_size / 2,
                                      decimal=2,
                                      err_msg="PSFEx size doesn't match")
    numpy.testing.assert_almost_equal(meas.observed_shape.g1 / 2,
                                      ref_shape.g1 / 2,
                                      decimal=2,
                                      err_msg="PSFEx shape.g1 doesn't match")
    numpy.testing.assert_almost_equal(meas.observed_shape.g2 / 2,
                                      ref_shape.g2 / 2,
                                      decimal=2,
                                      err_msg="PSFEx shape.g2 doesn't match")

    # Repeat without the wcs_file argument, so the model is in chip coordinates.
    # Also check the functionality where the file is already open.
    with pyfits.open(os.path.join(data_dir, psfex_file)) as hdu_list:
        psfex = galsim.des.DES_PSFEx(hdu_list[1])
    psf = psfex.getPSF(image_pos)

    # In this case, the getLocalWCS function won't return anything useful.
    assert psfex.getLocalWCS(image_pos) is None

    # Draw the postage stamp image.  This time in image coords, so pixel_scale = 1.0.
    stamp = psf.drawImage(bounds=b, scale=1.0, method='no_pixel')
    meas = stamp.FindAdaptiveMom()
    numpy.testing.assert_almost_equal(
        meas.moments_sigma / 2,
        ref_size / 2,
        decimal=2,
        err_msg="no-wcs PSFEx size doesn't match")
    numpy.testing.assert_almost_equal(
        meas.observed_shape.g1 / 2,
        ref_shape.g1 / 2,
        decimal=2,
        err_msg="no-wcs PSFEx shape.g1 doesn't match")
    numpy.testing.assert_almost_equal(
        meas.observed_shape.g2 / 2,
        ref_shape.g2 / 2,
        decimal=2,
        err_msg="no-wcs PSFEx shape.g2 doesn't match")

    # Now the shapelet PSF model.  This model is already in sky coordinates, so no wcs_file needed.
    fitpsf = galsim.des.DES_Shapelet(os.path.join(data_dir, fitpsf_file))
    psf = fitpsf.getPSF(image_pos)

    # Draw the postage stamp image
    # Again, the PSF already includes the pixel response.
    stamp = psf.drawImage(wcs=wcs.local(image_pos),
                          bounds=b,
                          method='no_pixel')
    meas = stamp.FindAdaptiveMom()
    numpy.testing.assert_almost_equal(
        meas.moments_sigma / 2,
        ref_size / 2,
        decimal=2,
        err_msg="Shapelet PSF size doesn't match")
    numpy.testing.assert_almost_equal(
        meas.observed_shape.g1 / 2,
        ref_shape.g1 / 2,
        decimal=2,
        err_msg="Shapelet PSF shape.g1 doesn't match")
    numpy.testing.assert_almost_equal(
        meas.observed_shape.g2 / 2,
        ref_shape.g2 / 2,
        decimal=2,
        err_msg="Shapelet PSF shape.g2 doesn't match")
Exemplo n.º 20
0
def test_read():
    """Test reading a FitsHeader from an existing FITS file
    """
    import time
    t1 = time.time()

    # Older pyfits versions treat the blank rows differently, so it comes out as 213.
    # I don't know exactly when it switched, but for < 3.1, I'll just update this to 
    # whatever the initial value is.
    tpv_len = 215

    def check_tpv(header):
        """Check that the header object has correct values from the tpv.fits file
        """
        # Check using a few different access methods.
        assert header['TIME-OBS'] == '04:28:14.105'
        assert header.get('FILTER') == 'I'
        assert header['AIRMASS'] == 1.185
        assert len(header) == tpv_len
        assert 'ADC' in header
        assert ('FILPOS',6) in header.items()
        assert ('FILPOS',6) in header.iteritems()
        assert 'OBSERVAT' in header.keys()
        assert 'OBSERVAT' in header.iterkeys()
        assert 54384.18627436 in header.values() # MJD-OBS
        assert 54384.18627436 in header.itervalues()

    file_name = 'tpv.fits'
    dir = 'fits_files'
    # First option: give a file_name
    header = galsim.FitsHeader(file_name=os.path.join(dir,file_name))
    if pyfits_version < '3.1':
        tpv_len = len(header)
    check_tpv(header)
    do_pickle(header)
    # Let the FitsHeader init handle the dir
    header = galsim.FitsHeader(file_name=file_name, dir=dir)
    check_tpv(header)
    do_pickle(header)
    # If the first arg is a str, then it should be interpreted as a file name
    header = galsim.FitsHeader(file_name, dir=dir)
    check_tpv(header)
    # If you pass in a pyfits hdulist, that should also work
    hdu_list = pyfits.open(os.path.join(dir,file_name))
    header = galsim.FitsHeader(hdu_list=hdu_list)
    check_tpv(header)
    do_pickle(header)
    # Can explicitly give an hdu number to use.  In this case, there is only 1, so need to use 0.
    header = galsim.FitsHeader(hdu_list=hdu_list, hdu=0)
    check_tpv(header)
    do_pickle(header)
    # If you pass in a pyfits Header object, that should also work
    header = galsim.FitsHeader(header=hdu_list[0].header)
    check_tpv(header)
    do_pickle(header)
    # The header is the first parameter, so don't need to name it.
    header = galsim.FitsHeader(hdu_list[0].header)
    check_tpv(header)


    # Remove an item from the header
    # Start with file_name constructor, to test that the repr is changed by the edit.
    header = galsim.FitsHeader(file_name=os.path.join(dir,file_name))
    del header['AIRMASS']
    assert 'AIRMASS' not in header
    if pyfits_version >= '3.1':
        assert len(header) == tpv_len-1
    do_pickle(header)

    # Should be able to get with a default value if the key is not present
    assert header.get('AIRMASS', 2.0) == 2.0
    # key should still not be in the header
    assert 'AIRMASS' not in header
    if pyfits_version >= '3.1':
        assert len(header) == tpv_len-1

    # Add items to a header
    header['AIRMASS'] = 2
    assert header.get('AIRMASS') == 2

    # Overwrite an existing value
    header['AIRMASS'] = 1.7
    assert header.get('AIRMASS') == 1.7

    # Set with a comment field
    header['AIRMASS'] = (1.9, 'The airmass of the observation')
    assert header.get('AIRMASS') == 1.9

    # Update with a dict
    d = { 'AIRMASS' : 1.185 }
    header.update(d)
    assert header.get('AIRMASS') == 1.185
    # We are essentially back to where we started, except the len won't be right.
    # Deleting a key removed an item, but setting it overwrote a blank item.
    # But if we add back another one of these, we should be back to the original values.
    header.append('','', useblanks=False)
    check_tpv(header)
    do_pickle(header)

    # Clear all values
    header.clear()
    assert 'AIRMASS' not in header
    assert 'FILTER' not in header
    assert len(header) == 0
    do_pickle(header)

    t2 = time.time()
    print 'time for %s = %.2f'%(funcname(),t2-t1)
Exemplo n.º 21
0
    def read(self):
        from galsim._pyfits import pyfits
        if isinstance(self.file_name, basestring):
            hdu = pyfits.open(self.file_name)[1]
        else:
            hdu = self.file_name
        # Number of parameters used for the interpolation.  We require this to be 2.
        pol_naxis = hdu.header['POLNAXIS']
        if pol_naxis != 2:
            raise IOError("PSFEx: Expected POLNAXIS == 2, got %d" % pol_naxis)

        # These are the names of the two axes.  Should be X_IMAGE, Y_IMAGE.
        # If they aren't, then the way we use the interpolation will be wrong.
        # Well, really they can also be XWIN_IMAGE, etc.  So just check that it
        # starts with X and ends with IMAGE.
        pol_name1 = hdu.header['POLNAME1']
        if not (pol_name1.startswith('X') and pol_name1.endswith('IMAGE')):
            raise IOError("PSFEx: Expected POLNAME1 == X*_IMAGE, got %s" %
                          pol_name1)
        pol_name2 = hdu.header['POLNAME2']
        if not (pol_name2.startswith('Y') and pol_name2.endswith('IMAGE')):
            raise IOError("PSFEx: Expected POLNAME2 == Y*_IMAGE, got %s" %
                          pol_name2)

        # Zero points and scale.  Interpolation is in terms of (x-x0)/xscale, (y-y0)/yscale
        pol_zero1 = hdu.header['POLZERO1']
        pol_zero2 = hdu.header['POLZERO2']
        pol_scal1 = hdu.header['POLSCAL1']
        pol_scal2 = hdu.header['POLSCAL2']

        # This defines the number of "context groups".
        # Here is Emmanuel's explanation:
        #
        #     POLNGRP is the number of "context groups". A group represents a set of variables
        #     (SExtractor measurements or FITS header parameters if preceded with ":") which share
        #     the same maximum polynomial degree. For instance if x and y are in group 1, and the
        #     degree of that group is 2, and z is in group 2 with degree 1, the polynomial will
        #     consist of:
        #         1, x, x^2, y, y.x, y^2, z, z.x^2, z.y, z.y.x, z.y^2
        #     (see eq 14 in https://www.astromatic.net/pubsvn/software/psfex/trunk/doc/psfex.pdf )
        #     By default, POLNGRP is 1 and the group contains X_IMAGE and Y_IMAGE measurements
        #     from SExtractor.
        #
        # For now, we require this to be 1, since I didn't have any files with POLNGRP != 1 to
        # test on.
        pol_ngrp = hdu.header['POLNGRP']
        if pol_ngrp != 1:
            raise IOError(
                "PSFEx: Current implementation requires POLNGRP == 1, got %d" %
                pol_ngrp)

        # Which group each item is in.  We require group 1.
        pol_group1 = hdu.header['POLGRP1']
        if pol_group1 != 1:
            raise IOError("PSFEx: Expected POLGRP1 == 1, got %s" % pol_group1)
        pol_group2 = hdu.header['POLGRP2']
        if pol_group2 != 1:
            raise IOError("PSFEx: Expected POLGRP2 == 1, got %s" % pol_group2)

        # The degree of the polynomial.  E.g. POLDEG1 = 2 means the values will be:
        #     1, x, x^2, y, xy, y^2
        # If we start allowing POLNGRP > 1, there is a separate degree for each group.
        pol_deg = hdu.header['POLDEG1']

        # The number of axes in the basis object.  We require this to be 3.
        psf_naxis = hdu.header['PSFNAXIS']
        if psf_naxis != 3:
            raise IOError("PSFEx: Expected PSFNAXIS == 3, got %d" % psfnaxis)

        # The first two axes are the image size of the PSF postage stamp.
        psf_axis1 = hdu.header['PSFAXIS1']
        psf_axis2 = hdu.header['PSFAXIS2']

        # The third axis is the direction of the polynomial interpolation.  So it should
        # be equal to (d+1)(d+2)/2.
        psf_axis3 = hdu.header['PSFAXIS3']
        if psf_axis3 != ((pol_deg + 1) * (pol_deg + 2)) / 2:
            raise IOError("PSFEx: POLDEG and PSFAXIS3 disagree")

        # This is the PSF "sample size".  Again, from Emmanuel:
        #
        #     PSF_SAMP is the sampling step of the PSF. PSF_SAMP=0.5 means that the PSF model has
        #     two samples per original image pixel (superresolution, so in automatic mode it is a
        #     sign that the original images were undersampled)
        #
        # In other words, it can be thought of as a unit conversion:
        #     "image pixels" / "psfex pixels"
        # So 1 image pixel = (1/psf_samp) psfex pixels.
        psf_samp = hdu.header['PSF_SAMP']

        # The basis object is a data cube (assuming PSFNAXIS==3)
        # Note: older pyfits versions don't get the shape right.
        # For newer pyfits versions the reshape command should be a no op.
        basis = hdu.data.field('PSF_MASK')[0].reshape(psf_axis3, psf_axis2,
                                                      psf_axis1)
        # Make sure this turned out right.
        if basis.shape[0] != psf_axis3:
            raise IOError("PSFEx: PSFAXIS3 disagrees with actual basis size")
        if basis.shape[1] != psf_axis2:
            raise IOError("PSFEx: PSFAXIS2 disagrees with actual basis size")
        if basis.shape[2] != psf_axis1:
            raise IOError("PSFEx: PSFAXIS1 disagrees with actual basis size")

        # Save some of these values for use in building the interpolated images
        self.basis = basis
        self.fit_order = pol_deg
        self.fit_size = psf_axis3
        self.x_zero = pol_zero1
        self.y_zero = pol_zero2
        self.x_scale = pol_scal1
        self.y_scale = pol_scal2
        self.sample_scale = psf_samp
Exemplo n.º 22
0
    def __init__(self, file_name=None, sample=None, image_dir=None, dir=None, preload=False,
                 noise_dir=None, logger=None, _nobjects_only=False):
        if sample is not None and file_name is not None:
            raise ValueError("Cannot specify both the sample and file_name!")

        from galsim._pyfits import pyfits
        self.file_name, self.image_dir, self.noise_dir, _ = \
            _parse_files_dirs(file_name, image_dir, dir, noise_dir, sample)

        with pyfits.open(self.file_name) as fits:
            self.cat = fits[1].data
        self.nobjects = len(self.cat) # number of objects in the catalog
        if _nobjects_only: return  # Exit early if that's all we needed.
        ident = self.cat.field('ident') # ID for object in the training sample

        # We want to make sure that the ident array contains all strings.
        # Strangely, ident.astype(str) produces a string with each element == '1'.
        # Hence this way of doing the conversion:
        self.ident = [ "%s"%val for val in ident ]

        self.gal_file_name = self.cat.field('gal_filename') # file containing the galaxy image
        self.psf_file_name = self.cat.field('PSF_filename') # file containing the PSF image

        # Add the directories:
        # Note the strip call.  Sometimes the filenames have an extra space at the end.
        # This gets rid of that space.
        self.gal_file_name = [ os.path.join(self.image_dir,f.strip()) for f in self.gal_file_name ]
        self.psf_file_name = [ os.path.join(self.image_dir,f.strip()) for f in self.psf_file_name ]

        # We don't require the noise_filename column.  If it is not present, we will use
        # Uncorrelated noise based on the variance column.
        try:
            self.noise_file_name = self.cat.field('noise_filename') # file containing the noise cf
            self.noise_file_name = [ os.path.join(self.noise_dir,f) for f in self.noise_file_name ]
        except:
            self.noise_file_name = None

        self.gal_hdu = self.cat.field('gal_hdu') # HDU containing the galaxy image
        self.psf_hdu = self.cat.field('PSF_hdu') # HDU containing the PSF image
        self.pixel_scale = self.cat.field('pixel_scale') # pixel scale for image (could be different
        # if we have training data from other datasets... let's be general here and make it a
        # vector in case of mixed training set)
        self.variance = self.cat.field('noise_variance') # noise variance for image
        self.mag = self.cat.field('mag')   # apparent magnitude
        self.band = self.cat.field('band') # bandpass in which apparent mag is measured, e.g., F814W
        self.weight = self.cat.field('weight') # weight factor to account for size-dependent
                                               # probability
        if 'stamp_flux' in self.cat.names:
            self.stamp_flux = self.cat.field('stamp_flux')

        self.saved_noise_im = {}
        self.loaded_files = {}
        self.logger = logger

        # The pyfits commands aren't thread safe.  So we need to make sure the methods that
        # use pyfits are not run concurrently from multiple threads.
        from multiprocessing import Lock
        self.gal_lock = Lock()  # Use this when accessing gal files
        self.psf_lock = Lock()  # Use this when accessing psf files
        self.loaded_lock = Lock()  # Use this when opening new files from disk
        self.noise_lock = Lock()  # Use this for building the noise image(s) (usually just one)

        # Preload all files if desired
        if preload: self.preload()
        self._preload = preload
Exemplo n.º 23
0
def test_read():
    """Test reading a FitsHeader from an existing FITS file
    """
    tpv_len = 215

    def check_tpv(header):
        """Check that the header object has correct values from the tpv.fits file
        """
        # Check using a few different access methods.
        assert header['TIME-OBS'] == '04:28:14.105'
        assert header.get('FILTER') == 'I'
        assert header['AIRMASS'] == 1.185
        assert len(header) == tpv_len
        assert 'ADC' in header
        assert ('FILPOS', 6) in header.items()
        assert ('FILPOS', 6) in header.iteritems()
        assert 'OBSERVAT' in header.keys()
        assert 'OBSERVAT' in header.iterkeys()
        assert 54384.18627436 in header.values()  # MJD-OBS
        assert 54384.18627436 in header.itervalues()

    file_name = 'tpv.fits'
    dir = 'fits_files'
    # First option: give a file_name
    header = galsim.FitsHeader(file_name=os.path.join(dir, file_name))
    check_tpv(header)
    do_pickle(header)
    # Let the FitsHeader init handle the dir
    header = galsim.FitsHeader(file_name=file_name, dir=dir)
    check_tpv(header)
    do_pickle(header)
    # If the first arg is a str, then it should be interpreted as a file name
    header = galsim.FitsHeader(file_name, dir=dir)
    check_tpv(header)
    # If you pass in a pyfits hdulist, that should also work
    with pyfits.open(os.path.join(dir, file_name)) as hdu_list:
        header = galsim.FitsHeader(hdu_list=hdu_list)
    check_tpv(header)
    do_pickle(header)
    # Can explicitly give an hdu number to use.  In this case, there is only 1, so need to use 0.
    with pyfits.open(os.path.join(dir, file_name)) as hdu_list:
        header = galsim.FitsHeader(hdu_list=hdu_list, hdu=0)
    check_tpv(header)
    do_pickle(header)
    # Can explicitly give an hdu number to use.  In this case, there is only 1, so need to use 0.
    header = galsim.FitsHeader(file_name=file_name, dir=dir, hdu=0)
    check_tpv(header)
    do_pickle(header)
    # If you pass in a pyfits Header object, that should also work
    with pyfits.open(os.path.join(dir, file_name)) as hdu_list:
        header = galsim.FitsHeader(header=hdu_list[0].header)
    check_tpv(header)
    do_pickle(header)
    # The header is the first parameter, so don't need to name it.
    with pyfits.open(os.path.join(dir, file_name)) as hdu_list:
        header = galsim.FitsHeader(hdu_list[0].header)
    check_tpv(header)
    # FitsHeader can read from a compressed file too
    header = galsim.FitsHeader(file_name=file_name + '.gz',
                               dir=dir,
                               compression='auto')
    check_tpv(header)
    do_pickle(header)
    header = galsim.FitsHeader(file_name=file_name + '.gz',
                               dir=dir,
                               compression='gzip')
    check_tpv(header)
    do_pickle(header)

    assert_raises(TypeError,
                  galsim.FitsHeader,
                  file_name=file_name,
                  header=header)
    with pyfits.open(os.path.join(dir, file_name)) as hdu_list:
        assert_raises(TypeError,
                      galsim.FitsHeader,
                      file_name=file_name,
                      hdu_list=hdu_list)
        assert_raises(TypeError,
                      galsim.FitsHeader,
                      header=header,
                      hdu_list=hdu_list)

    # Remove an item from the header
    # Start with file_name constructor, to test that the repr is changed by the edit.
    orig_header = header
    header = galsim.FitsHeader(file_name=os.path.join(dir, file_name))
    assert header == orig_header
    del header['AIRMASS']
    assert 'AIRMASS' not in header
    assert len(header) == tpv_len - 1
    assert header != orig_header
    do_pickle(header)

    # Should be able to get with a default value if the key is not present
    assert header.get('AIRMASS', 2.0) == 2.0
    # key should still not be in the header
    assert 'AIRMASS' not in header
    assert len(header) == tpv_len - 1
    assert header != orig_header

    # Add items to a header
    header['AIRMASS'] = 2
    assert header.get('AIRMASS') == 2
    assert header != orig_header

    # Pop does a similar thing:
    assert header.pop('AIRMASS') == 2.0
    assert 'AIRMASS' not in header

    # Works if not preset, given default
    assert header.pop('AIRMASS', 2.0) == 2.0
    assert 'AIRMASS' not in header
    header['AIRMASS'] = 2
    assert header['AIRMASS'] == 2

    # Get real value if preset and given default value
    assert header.pop('AIRMASS', 1.9) == 2.0
    assert 'AIRMASS' not in header
    header['AIRMASS'] = 2
    assert header['AIRMASS'] == 2

    # Overwrite an existing value
    header['AIRMASS'] = 1.7
    assert header.get('AIRMASS') == 1.7
    assert header != orig_header

    # Set with a comment field
    header['AIRMASS'] = (1.9, 'The airmass of the observation')
    assert header.get('AIRMASS') == 1.9
    assert header != orig_header

    # Update with a dict
    d = {'AIRMASS': 1.185}
    header.update(d)
    assert header.get('AIRMASS') == 1.185

    # Extend is similar, but skip duplicates, and must be a FitsHeader
    h = galsim.FitsHeader({'AIRMASS': 1.2, 'RA': 123})
    header.extend(h)
    assert header['AIRMASS'] == 1.185  # unchanged
    assert header['RA'] == 123

    # If replace = true, then update to the new values.
    h['DEC'] = -22.5
    header.extend(h, replace=True)
    assert header['AIRMASS'] == 1.2  # changed
    assert header['RA'] == 123
    assert header['DEC'] == -22.5

    # We are essentially back to where we started, except the len won't be right.
    # Deleting a key removed an item each time, but setting it overwrote a blank item.
    # But if we add back another few of these, we should be back to the original values.
    header.append('', '', useblanks=False)
    header.append('', '', useblanks=False)
    header.append('', '', useblanks=False)
    header['AIRMASS'] = 1.185
    check_tpv(header)
    do_pickle(header)
    assert header != orig_header  # It's still not equal, because the AIRMASS item is in a
    # different location in the list, which is relevant for equality.

    # Clear all values
    header.clear()
    assert 'AIRMASS' not in header
    assert 'FILTER' not in header
    assert len(header) == 0
    do_pickle(header)
    assert header != orig_header
Exemplo n.º 24
0
def test_wfirst_bandpass():
    """Test the WFIRST bandpasses for basic sanity.
    """
    from galsim._pyfits import pyfits

    # Obtain the bandpasses with AB_zeropoint set
    exp_time = 200. # non WFIRST exposure time
    bp = galsim.wfirst.getBandpasses(AB_zeropoint=True, exptime=exp_time)

    # Check if the zeropoints have been set correctly
    AB_spec = lambda x: (3631e-23)*exp_time*(np.pi)*(100.**2)*\
              (galsim.wfirst.diameter**2)*(1-galsim.wfirst.obscuration**2)/4.
    AB_sed = galsim.SED(spec=AB_spec, wave_type='nm', flux_type='fnu')
    for filter_name, filter_ in bp.items():
        mag = AB_sed.calculateMagnitude(bandpass=filter_)
        np.testing.assert_almost_equal(mag,0.0,decimal=6,
            err_msg="Zeropoint not set accurately enough for bandpass filter \
            {0}".format(filter_name))

    # Do a slightly less trivial check of bandpass-related calculations:
    # Jeff Kruk (at Goddard) took an SED template from the Castelli-Kurucz library, normalized it to
    # a particular magnitude in SDSS g band, and checked the count rates he expects for the WFIRST
    # bands.  I (RM) independently did the same calculation (downloading the templates and bandpass
    # myself and using GalSim for all the important bits of the calculation) and my results agree a
    # the 5% level.  Given that I didn't quite have the same SED, we were very happy with this level
    # of agreement.  The unit test below reproduces this test, and requires agreement at the 10%
    # level.
    # Jeff used the C-K template with solar metallicity, T=9550K, surface gravity logg=3.95.  I
    # downloaded a grid of templates and just used the nearest one, which has solar metallicity,
    # T=9500K, surface gravity logg=4.0.
    with pyfits.open(os.path.join('wfirst_files','ckp00_9500.fits')) as fits:
        sed_data = fits[1].data
    lam = sed_data.WAVELENGTH.astype(np.float64)
    t = sed_data.g40.astype(np.float64)
    sed_tab = galsim.LookupTable(x=lam, f=t, interpolant='linear')
    sed = galsim.SED(sed_tab, wave_type='A', flux_type='flambda')

    # Now take the SDSS g bandpass:
    gfile =  '/Users/rmandelb/Downloads/g.dat'
    bp_dat = np.loadtxt(os.path.join('wfirst_files','g.dat')).transpose()
    bp_tab = galsim.LookupTable(x=bp_dat[0,:], f=bp_dat[1,:], interpolant='linear')
    bp_ref = galsim.Bandpass(bp_tab, wave_type='A')
    # Set an AB zeropoint using WFIRST params:
    eff_diam = 100.*galsim.wfirst.diameter*np.sqrt(1.-galsim.wfirst.obscuration**2)
    bp_ref = bp_ref.withZeropoint('AB', effective_diameter=eff_diam, exptime=galsim.wfirst.exptime)
    # Now get a new SED that has magnitude -0.093 in this filter, since that's the normalization
    # that Jeff imposed for his tests.
    sed = sed.withMagnitude(-0.093, bp_ref)

    # Reference count rates, from Jeff:
    reference = {}
    reference['Z087'] = 1.98e10
    reference['Y106'] = 1.97e10
    reference['J129'] = 1.52e10
    reference['H158'] = 1.11e10
    reference['F184'] = 0.58e10
    reference['W149'] = 4.34e10

    # Only 10% accuracy required because did not use quite the same stellar template.  Fortunately,
    # bugs can easily lead to orders of magnitude errors, so this unit test is still pretty
    # non-trivial.
    for filter_name, filter_ in bp.items():
        flux = sed.calculateFlux(filter_)
        count_rate = flux / galsim.wfirst.exptime
        np.testing.assert_allclose(
            count_rate, reference[filter_name], rtol=0.1,
            err_msg="Count rate for stellar model not as expected for bandpass "
            "{0}".format(filter_name))
Exemplo n.º 25
0
    def __init__(self, file_name=None, sample=None, image_dir=None, dir=None, preload=False,
                 noise_dir=None, logger=None, _nobjects_only=False):
        if sample is not None and file_name is not None:
            raise ValueError("Cannot specify both the sample and file_name!")

        from galsim._pyfits import pyfits
        self.file_name, self.image_dir, self.noise_dir, _ = \
            _parse_files_dirs(file_name, image_dir, dir, noise_dir, sample)

        with pyfits.open(self.file_name) as fits:
            self.cat = fits[1].data
        self.nobjects = len(self.cat) # number of objects in the catalog
        if _nobjects_only: return  # Exit early if that's all we needed.
        ident = self.cat.field('ident') # ID for object in the training sample

        # We want to make sure that the ident array contains all strings.
        # Strangely, ident.astype(str) produces a string with each element == '1'.
        # Hence this way of doing the conversion:
        self.ident = [ "%s"%val for val in ident ]

        self.gal_file_name = self.cat.field('gal_filename') # file containing the galaxy image
        self.psf_file_name = self.cat.field('PSF_filename') # file containing the PSF image

        # Add the directories:
        # Note the strip call.  Sometimes the filenames have an extra space at the end.
        # This gets rid of that space.
        self.gal_file_name = [ os.path.join(self.image_dir,f.strip()) for f in self.gal_file_name ]
        self.psf_file_name = [ os.path.join(self.image_dir,f.strip()) for f in self.psf_file_name ]

        # We don't require the noise_filename column.  If it is not present, we will use
        # Uncorrelated noise based on the variance column.
        try:
            self.noise_file_name = self.cat.field('noise_filename') # file containing the noise cf
            self.noise_file_name = [ os.path.join(self.noise_dir,f) for f in self.noise_file_name ]
        except:
            self.noise_file_name = None

        self.gal_hdu = self.cat.field('gal_hdu') # HDU containing the galaxy image
        self.psf_hdu = self.cat.field('PSF_hdu') # HDU containing the PSF image
        self.pixel_scale = self.cat.field('pixel_scale') # pixel scale for image (could be different
        # if we have training data from other datasets... let's be general here and make it a
        # vector in case of mixed training set)
        self.variance = self.cat.field('noise_variance') # noise variance for image
        self.mag = self.cat.field('mag')   # apparent magnitude
        self.band = self.cat.field('band') # bandpass in which apparent mag is measured, e.g., F814W
        # The weight factor should be a float value >=0 (so that random selections of indices can
        # use it to remove any selection effects in the catalog creation process).
        # Here we renormalize by the maximum weight.  If the maximum is below 1, that just means
        # that all galaxies were subsampled at some level, and here we only want to account for
        # relative selection effects within the catalog, not absolute subsampling.  If the maximum
        # is above 1, then our random number generation test used to draw a weighted sample will
        # fail since we use uniform deviates in the range 0 to 1.
        weight = self.cat.field('weight')
        self.weight = weight/np.max(weight)
        if 'stamp_flux' in self.cat.names:
            self.stamp_flux = self.cat.field('stamp_flux')

        self.saved_noise_im = {}
        self.loaded_files = {}
        self.logger = logger

        # The pyfits commands aren't thread safe.  So we need to make sure the methods that
        # use pyfits are not run concurrently from multiple threads.
        from multiprocessing import Lock
        self.gal_lock = Lock()  # Use this when accessing gal files
        self.psf_lock = Lock()  # Use this when accessing psf files
        self.loaded_lock = Lock()  # Use this when opening new files from disk
        self.noise_lock = Lock()  # Use this for building the noise image(s) (usually just one)

        # Preload all files if desired
        if preload: self.preload()
        self._preload = preload
Exemplo n.º 26
0
def test_roman_bandpass():
    """Test the Roman bandpasses for basic sanity.
    """
    from galsim._pyfits import pyfits

    # Obtain the bandpasses with AB_zeropoint set
    bp = galsim.roman.getBandpasses(AB_zeropoint=True)

    # Check if the zeropoints have been set correctly
    AB_spec = lambda x: (3631e-23)
    AB_sed = galsim.SED(spec=AB_spec, wave_type='nm', flux_type='fnu')
    for filter_name, filter_ in bp.items():
        mag = AB_sed.calculateMagnitude(bandpass=filter_)
        np.testing.assert_almost_equal(
            mag,
            0.0,
            decimal=6,
            err_msg="Zeropoint not set accurately enough for bandpass filter "
            + filter_name)

    # Do a slightly less trivial check of bandpass-related calculations:
    # Jeff Kruk (at Goddard) took an SED template from the Castelli-Kurucz library, normalized it to
    # a particular magnitude in SDSS g band, and checked the count rates he expects for the Roman
    # bands.  I (RM) independently did the same calculation (downloading the templates and bandpass
    # myself and using GalSim for all the important bits of the calculation) and my results agree a
    # the 5% level.  Given that I didn't quite have the same SED, we were very happy with this level
    # of agreement.  The unit test below reproduces this test, and requires agreement at the 10%
    # level.
    # Jeff used the C-K template with solar metallicity, T=9550K, surface gravity logg=3.95.  I
    # downloaded a grid of templates and just used the nearest one, which has solar metallicity,
    # T=9500K, surface gravity logg=4.0.
    with pyfits.open(os.path.join('roman_files', 'ckp00_9500.fits')) as fits:
        sed_data = fits[1].data
    lam = sed_data.WAVELENGTH.astype(np.float64)
    t = sed_data.g40.astype(np.float64)
    sed_tab = galsim.LookupTable(x=lam, f=t, interpolant='linear')
    sed = galsim.SED(sed_tab, wave_type='A', flux_type='flambda')

    # Now take the SDSS g bandpass:
    # gfile =  '/Users/rmandelb/Downloads/g.dat'
    bp_dat = np.loadtxt(os.path.join('roman_files', 'g.dat')).transpose()
    bp_tab = galsim.LookupTable(x=bp_dat[0, :],
                                f=bp_dat[1, :],
                                interpolant='linear')
    bp_ref = galsim.Bandpass(bp_tab, wave_type='A').withZeropoint('AB')
    # Now get a new SED that has magnitude -0.093 in this filter, since that's the normalization
    # that Jeff imposed for his tests.
    sed = sed.withMagnitude(-0.093, bp_ref)

    # Reference count rates, from Jeff:
    reference = {}
    reference['Z087'] = 1.98e10
    reference['Y106'] = 1.97e10
    reference['J129'] = 1.52e10
    reference['H158'] = 1.11e10
    reference['F184'] = 0.58e10
    reference['W149'] = 4.34e10

    # Only 15% accuracy required because did not use quite the same stellar template.  Fortunately,
    # bugs can easily lead to orders of magnitude errors, so this unit test is still pretty
    # non-trivial.
    for filter_name, filter_ in bp.items():
        flux = sed.calculateFlux(filter_)  # photons / cm^2 / s
        count_rate = flux * galsim.roman.collecting_area  # photons / s
        print(count_rate, reference[filter_name])
        np.testing.assert_allclose(
            count_rate,
            reference[filter_name],
            rtol=0.15,
            err_msg="Count rate for stellar model not as expected for bandpass "
            "{0}".format(filter_name))

    # Finally, compare against some external zeropoint calculations from the Roman microlensing
    # group: https://roman.ipac.caltech.edu/sims/MABuLS_sim.html
    # They calculated instrumental zero points, defined such that the flux is 1 photon/sec (taking
    # into account the Roman collecting area).  We convert ours to their definition by adding
    # `delta_zp` calculated below:
    area_eff = galsim.roman.collecting_area
    delta_zp = 2.5 * np.log10(area_eff)
    # Define the zeropoints that they calculated:
    ref_zp = {'W149': 27.554, 'Z087': 26.163}
    for key in ref_zp.keys():
        galsim_zp = bp[key].zeropoint + delta_zp
        # They use slightly different versions of the bandpasses, so we only require agreement to
        # 0.1 mag.
        print('zp for %s: ' % key, galsim_zp, ref_zp[key])
        np.testing.assert_allclose(galsim_zp,
                                   ref_zp[key],
                                   atol=0.1,
                                   err_msg="Wrong zeropoint for bandpass " +
                                   key)

    # Note: the difference is not due to our default thinning.  This isn't any better.
    nothin_bp = galsim.roman.getBandpasses(AB_zeropoint=True,
                                           default_thin_trunc=False)
    for key in ref_zp.keys():
        galsim_zp = nothin_bp[key].zeropoint + delta_zp
        print('nothin zp for %s: ' % key, galsim_zp, ref_zp[key])
        np.testing.assert_allclose(galsim_zp,
                                   ref_zp[key],
                                   atol=0.1,
                                   err_msg="Wrong zeropoint for bandpass " +
                                   key)

    # Even with fairly extreme thinning, the error is still only 0.15 mag.
    verythin_bp = galsim.roman.getBandpasses(AB_zeropoint=True,
                                             default_thin_trunc=False,
                                             relative_throughput=0.05,
                                             rel_err=0.1)
    for key in ref_zp.keys():
        galsim_zp = verythin_bp[key].zeropoint + delta_zp
        print('verythin zp for %s: ' % key, galsim_zp, ref_zp[key])
        np.testing.assert_allclose(galsim_zp,
                                   ref_zp[key],
                                   atol=0.15,
                                   err_msg="Wrong zeropoint for bandpass " +
                                   key)

    with assert_raises(TypeError):
        galsim.roman.getBandpasses(default_thin_trunc=False, rel_tp=0.05)
    with assert_warns(galsim.GalSimWarning):
        galsim.roman.getBandpasses(relative_throughput=0.05, rel_err=0.1)

    # Can also not bother to set the zeropoint.
    nozp_bp = galsim.roman.getBandpasses(AB_zeropoint=False)
    for key in nozp_bp:
        assert nozp_bp[key].zeropoint is None
Exemplo n.º 27
0
def test_wfirst_bandpass():
    """Test the WFIRST bandpasses for basic sanity.
    """
    from galsim._pyfits import pyfits

    # Obtain the bandpasses with AB_zeropoint set
    bp = galsim.wfirst.getBandpasses(AB_zeropoint=True)

    # Check if the zeropoints have been set correctly
    AB_spec = lambda x: (3631e-23)
    AB_sed = galsim.SED(spec=AB_spec, wave_type='nm', flux_type='fnu')
    for filter_name, filter_ in bp.items():
        mag = AB_sed.calculateMagnitude(bandpass=filter_)
        np.testing.assert_almost_equal(mag,0.0,decimal=6,
            err_msg="Zeropoint not set accurately enough for bandpass filter "+filter_name)

    # Do a slightly less trivial check of bandpass-related calculations:
    # Jeff Kruk (at Goddard) took an SED template from the Castelli-Kurucz library, normalized it to
    # a particular magnitude in SDSS g band, and checked the count rates he expects for the WFIRST
    # bands.  I (RM) independently did the same calculation (downloading the templates and bandpass
    # myself and using GalSim for all the important bits of the calculation) and my results agree a
    # the 5% level.  Given that I didn't quite have the same SED, we were very happy with this level
    # of agreement.  The unit test below reproduces this test, and requires agreement at the 10%
    # level.
    # Jeff used the C-K template with solar metallicity, T=9550K, surface gravity logg=3.95.  I
    # downloaded a grid of templates and just used the nearest one, which has solar metallicity,
    # T=9500K, surface gravity logg=4.0.
    with pyfits.open(os.path.join('wfirst_files','ckp00_9500.fits')) as fits:
        sed_data = fits[1].data
    lam = sed_data.WAVELENGTH.astype(np.float64)
    t = sed_data.g40.astype(np.float64)
    sed_tab = galsim.LookupTable(x=lam, f=t, interpolant='linear')
    sed = galsim.SED(sed_tab, wave_type='A', flux_type='flambda')

    # Now take the SDSS g bandpass:
    # gfile =  '/Users/rmandelb/Downloads/g.dat'
    bp_dat = np.loadtxt(os.path.join('wfirst_files','g.dat')).transpose()
    bp_tab = galsim.LookupTable(x=bp_dat[0,:], f=bp_dat[1,:], interpolant='linear')
    bp_ref = galsim.Bandpass(bp_tab, wave_type='A').withZeropoint('AB')
    # Now get a new SED that has magnitude -0.093 in this filter, since that's the normalization
    # that Jeff imposed for his tests.
    sed = sed.withMagnitude(-0.093, bp_ref)

    # Reference count rates, from Jeff:
    reference = {}
    reference['Z087'] = 1.98e10
    reference['Y106'] = 1.97e10
    reference['J129'] = 1.52e10
    reference['H158'] = 1.11e10
    reference['F184'] = 0.58e10
    reference['W149'] = 4.34e10

    # Only 15% accuracy required because did not use quite the same stellar template.  Fortunately,
    # bugs can easily lead to orders of magnitude errors, so this unit test is still pretty
    # non-trivial.
    for filter_name, filter_ in bp.items():
        flux = sed.calculateFlux(filter_)  # photons / cm^2 / s
        count_rate = flux * galsim.wfirst.collecting_area  # photons / s
        print(count_rate, reference[filter_name])
        np.testing.assert_allclose(
            count_rate, reference[filter_name], rtol=0.15,
            err_msg="Count rate for stellar model not as expected for bandpass "
            "{0}".format(filter_name))

    # Finally, compare against some external zeropoint calculations from the WFIRST microlensing
    # group: https://wfirst.ipac.caltech.edu/sims/MABuLS_sim.html
    # They calculated instrumental zero points, defined such that the flux is 1 photon/sec (taking
    # into account the WFIRST collecting area).  We convert ours to their definition by adding
    # `delta_zp` calculated below:
    area_eff = galsim.wfirst.collecting_area
    delta_zp = 2.5 * np.log10(area_eff)
    # Define the zeropoints that they calculated:
    ref_zp = {
        'W149': 27.554,
        'Z087': 26.163
        }
    for key in ref_zp.keys():
        galsim_zp = bp[key].zeropoint + delta_zp
        # They use slightly different versions of the bandpasses, so we only require agreement to
        # 0.1 mag.
        print('zp for %s: '%key, galsim_zp, ref_zp[key])
        np.testing.assert_allclose(galsim_zp, ref_zp[key], atol=0.1,
                                   err_msg="Wrong zeropoint for bandpass "+key)

    # Note: the difference is not due to our default thinning.  This isn't any better.
    nothin_bp = galsim.wfirst.getBandpasses(AB_zeropoint=True, default_thin_trunc=False)
    for key in ref_zp.keys():
        galsim_zp = nothin_bp[key].zeropoint + delta_zp
        print('nothin zp for %s: '%key, galsim_zp, ref_zp[key])
        np.testing.assert_allclose(galsim_zp, ref_zp[key], atol=0.1,
                                   err_msg="Wrong zeropoint for bandpass "+key)

    # Even with fairly extreme thinning, the error is still only 0.15 mag.
    verythin_bp = galsim.wfirst.getBandpasses(AB_zeropoint=True, default_thin_trunc=False,
                                              relative_throughput=0.05, rel_err=0.1)
    for key in ref_zp.keys():
        galsim_zp = verythin_bp[key].zeropoint + delta_zp
        print('verythin zp for %s: '%key, galsim_zp, ref_zp[key])
        np.testing.assert_allclose(galsim_zp, ref_zp[key], atol=0.15,
                                   err_msg="Wrong zeropoint for bandpass "+key)

    with assert_raises(TypeError):
        galsim.wfirst.getBandpasses(default_thin_trunc=False, rel_tp=0.05)
    with assert_warns(galsim.GalSimWarning):
        galsim.wfirst.getBandpasses(relative_throughput=0.05, rel_err=0.1)

    # Can also not bother to set the zeropoint.
    nozp_bp = galsim.wfirst.getBandpasses(AB_zeropoint=False)
    for key in nozp_bp:
        assert nozp_bp[key].zeropoint is None
Exemplo n.º 28
0
def test_nan_fits():
    """Test reading in a FITS file that has NAN.0 entries in the header.

    This test is specifically in response to issue #602.
    """
    import warnings
    from galsim._pyfits import pyfits
    # Older pyfits versions don't have this, so just skip this test then.
    if not hasattr(pyfits, 'verify'): return

    # The problematic file:
    file_name = "des_data/DECam_00158414_01.fits.fz"

    # These are the values we should be reading in:
    ref_bounds = galsim.BoundsI(xmin=1, xmax=2048, ymin=1, ymax=4096)
    ref_wcs = galsim.GSFitsWCS(_data=[
        'TPV',
        numpy.array([13423.2, 6307.333]),
        numpy.array([[-4.410051713005e-09, 7.286844513153e-05],
                     [-7.285161461796e-05, 3.936353853081e-09]]),
        galsim.CelestialCoord(1.1502513773465992 *
                              galsim.radians, -0.9862866578241959 *
                              galsim.radians),
        numpy.array([[[
            0.004336243600183, -0.01133740904139, 0.01202041999278,
            -0.004357212119479
        ], [1.013741474567, -0.01657049389296, 0.005805882078771, 0.0],
                      [0.008865811106037, -0.007472254968395, 0.0, 0.0],
                      [0.0008534196190617, 0.0, 0.0, 0.0]],
                     [[
                         0.002619866608142, 0.9931356822158, 0.008771460618847,
                         -0.003739430249945
                     ],
                      [
                          -0.009422336649176, 0.01826140592329,
                          -0.009387805146152, 0.0
                      ], [-0.01066967054507, 0.007202907073747, 0.0, 0.0],
                      [-0.003683686751425, 0.0, 0.0, 0.0]]]), None, None
    ])

    # First just read the file directly, not using galsim.fits.read
    with pyfits.open(file_name) as fp:
        try:
            data = fp[1].data
            print('Able to read FITS file with NAN.0 without any problem.')
        except:
            print('Running verify to fix the problematic FITS header.')
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore",
                                        category=pyfits.verify.VerifyWarning)
                fp[1].verify('fix')
            # This should work now.
            data = fp[1].data
        header = fp[1].header

    assert data.shape == ref_bounds.numpyShape()

    # Check a direct read of the header with GSFitsWCS
    wcs = galsim.GSFitsWCS(header=header)
    assert wcs == ref_wcs

    # Now read it with GalSim's fits.read function.
    # Reading this file will emit verification warnings, so we'll ignore those here for the
    # test.  But the result should be a valid image.
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore", category=pyfits.verify.VerifyWarning)
        im = galsim.fits.read(file_name)

    assert im.bounds == ref_bounds
    assert im.wcs == ref_wcs
Exemplo n.º 29
0
def test_read():
    """Test reading a FitsHeader from an existing FITS file
    """
    tpv_len = 215

    def check_tpv(header):
        """Check that the header object has correct values from the tpv.fits file
        """
        # Check using a few different access methods.
        assert header['TIME-OBS'] == '04:28:14.105'
        assert header.get('FILTER') == 'I'
        assert header['AIRMASS'] == 1.185
        assert len(header) == tpv_len
        assert 'ADC' in header
        assert ('FILPOS',6) in header.items()
        assert ('FILPOS',6) in header.iteritems()
        assert 'OBSERVAT' in header.keys()
        assert 'OBSERVAT' in header.iterkeys()
        assert 54384.18627436 in header.values() # MJD-OBS
        assert 54384.18627436 in header.itervalues()

    file_name = 'tpv.fits'
    dir = 'fits_files'
    # First option: give a file_name
    header = galsim.FitsHeader(file_name=os.path.join(dir,file_name))
    check_tpv(header)
    do_pickle(header)
    # Let the FitsHeader init handle the dir
    header = galsim.FitsHeader(file_name=file_name, dir=dir)
    check_tpv(header)
    do_pickle(header)
    # If the first arg is a str, then it should be interpreted as a file name
    header = galsim.FitsHeader(file_name, dir=dir)
    check_tpv(header)
    # If you pass in a pyfits hdulist, that should also work
    with pyfits.open(os.path.join(dir,file_name)) as hdu_list:
        header = galsim.FitsHeader(hdu_list=hdu_list)
    check_tpv(header)
    do_pickle(header)
    # Can explicitly give an hdu number to use.  In this case, there is only 1, so need to use 0.
    with pyfits.open(os.path.join(dir,file_name)) as hdu_list:
        header = galsim.FitsHeader(hdu_list=hdu_list, hdu=0)
    check_tpv(header)
    do_pickle(header)
    # Can explicitly give an hdu number to use.  In this case, there is only 1, so need to use 0.
    header = galsim.FitsHeader(file_name=file_name, dir=dir, hdu=0)
    check_tpv(header)
    do_pickle(header)
    # If you pass in a pyfits Header object, that should also work
    with pyfits.open(os.path.join(dir,file_name)) as hdu_list:
        header = galsim.FitsHeader(header=hdu_list[0].header)
    check_tpv(header)
    do_pickle(header)
    # The header is the first parameter, so don't need to name it.
    with pyfits.open(os.path.join(dir,file_name)) as hdu_list:
        header = galsim.FitsHeader(hdu_list[0].header)
    check_tpv(header)
    # FitsHeader can read from a compressed file too
    header = galsim.FitsHeader(file_name=file_name + '.gz', dir=dir, compression='auto')
    check_tpv(header)
    do_pickle(header)
    header = galsim.FitsHeader(file_name=file_name + '.gz', dir=dir, compression='gzip')
    check_tpv(header)
    do_pickle(header)

    assert_raises(TypeError, galsim.FitsHeader, file_name=file_name, header=header)
    with pyfits.open(os.path.join(dir,file_name)) as hdu_list:
        assert_raises(TypeError, galsim.FitsHeader, file_name=file_name, hdu_list=hdu_list)
        assert_raises(TypeError, galsim.FitsHeader, header=header, hdu_list=hdu_list)

    # Remove an item from the header
    # Start with file_name constructor, to test that the repr is changed by the edit.
    orig_header = header
    header = galsim.FitsHeader(file_name=os.path.join(dir,file_name))
    assert header == orig_header
    del header['AIRMASS']
    assert 'AIRMASS' not in header
    assert len(header) == tpv_len-1
    assert header != orig_header
    do_pickle(header)

    # Should be able to get with a default value if the key is not present
    assert header.get('AIRMASS', 2.0) == 2.0
    # key should still not be in the header
    assert 'AIRMASS' not in header
    assert len(header) == tpv_len-1
    assert header != orig_header

    # Add items to a header
    header['AIRMASS'] = 2
    assert header.get('AIRMASS') == 2
    assert header != orig_header

    # Pop does a similar thing:
    assert header.pop('AIRMASS') == 2.0
    assert 'AIRMASS' not in header

    # Works if not preset, given default
    assert header.pop('AIRMASS', 2.0) == 2.0
    assert 'AIRMASS' not in header
    header['AIRMASS'] = 2
    assert header['AIRMASS'] == 2

    # Get real value if preset and given default value
    assert header.pop('AIRMASS', 1.9) == 2.0
    assert 'AIRMASS' not in header
    header['AIRMASS'] = 2
    assert header['AIRMASS'] == 2

    # Overwrite an existing value
    header['AIRMASS'] = 1.7
    assert header.get('AIRMASS') == 1.7
    assert header != orig_header

    # Set with a comment field
    header['AIRMASS'] = (1.9, 'The airmass of the observation')
    assert header.get('AIRMASS') == 1.9
    assert header != orig_header

    # Update with a dict
    d = { 'AIRMASS' : 1.185 }
    header.update(d)
    assert header.get('AIRMASS') == 1.185
    # We are essentially back to where we started, except the len won't be right.
    # Deleting a key removed an item each time, but setting it overwrote a blank item.
    # But if we add back another few of these, we should be back to the original values.
    header.append('','', useblanks=False)
    header.append('','', useblanks=False)
    header.append('','', useblanks=False)
    check_tpv(header)
    do_pickle(header)
    assert header != orig_header  # It's still not equal, because the AIRMASS item is in a
                                  # different location in the list, which is relevant for equality.

    # Clear all values
    header.clear()
    assert 'AIRMASS' not in header
    assert 'FILTER' not in header
    assert len(header) == 0
    do_pickle(header)
    assert header != orig_header
Exemplo n.º 30
0
def test_read():
    """Test reading a FitsHeader from an existing FITS file
    """
    # Older pyfits versions treat the blank rows differently, so it comes out as 213.
    # I don't know exactly when it switched, but for < 3.1, I'll just update this to
    # whatever the initial value is.
    tpv_len = 215

    def check_tpv(header):
        """Check that the header object has correct values from the tpv.fits file
        """
        # Check using a few different access methods.
        assert header['TIME-OBS'] == '04:28:14.105'
        assert header.get('FILTER') == 'I'
        assert header['AIRMASS'] == 1.185
        assert len(header) == tpv_len
        assert 'ADC' in header
        assert ('FILPOS', 6) in header.items()
        assert ('FILPOS', 6) in header.iteritems()
        assert 'OBSERVAT' in header.keys()
        assert 'OBSERVAT' in header.iterkeys()
        assert 54384.18627436 in header.values()  # MJD-OBS
        assert 54384.18627436 in header.itervalues()

    file_name = 'tpv.fits'
    dir = 'fits_files'
    # First option: give a file_name
    header = galsim.FitsHeader(file_name=os.path.join(dir, file_name))
    if pyfits_version < '3.1':
        tpv_len = len(header)
    check_tpv(header)
    do_pickle(header)
    # Let the FitsHeader init handle the dir
    header = galsim.FitsHeader(file_name=file_name, dir=dir)
    check_tpv(header)
    do_pickle(header)
    # If the first arg is a str, then it should be interpreted as a file name
    header = galsim.FitsHeader(file_name, dir=dir)
    check_tpv(header)
    # If you pass in a pyfits hdulist, that should also work
    with pyfits.open(os.path.join(dir, file_name)) as hdu_list:
        header = galsim.FitsHeader(hdu_list=hdu_list)
    check_tpv(header)
    do_pickle(header)
    # Can explicitly give an hdu number to use.  In this case, there is only 1, so need to use 0.
    with pyfits.open(os.path.join(dir, file_name)) as hdu_list:
        header = galsim.FitsHeader(hdu_list=hdu_list, hdu=0)
    check_tpv(header)
    do_pickle(header)
    # Can explicitly give an hdu number to use.  In this case, there is only 1, so need to use 0.
    header = galsim.FitsHeader(file_name=file_name, dir=dir, hdu=0)
    check_tpv(header)
    do_pickle(header)
    # If you pass in a pyfits Header object, that should also work
    with pyfits.open(os.path.join(dir, file_name)) as hdu_list:
        header = galsim.FitsHeader(header=hdu_list[0].header)
    check_tpv(header)
    do_pickle(header)
    # The header is the first parameter, so don't need to name it.
    with pyfits.open(os.path.join(dir, file_name)) as hdu_list:
        header = galsim.FitsHeader(hdu_list[0].header)
    check_tpv(header)
    # FitsHeader can read from a compressed file too
    header = galsim.FitsHeader(file_name=file_name + '.gz',
                               dir=dir,
                               compression='auto')
    check_tpv(header)
    do_pickle(header)
    header = galsim.FitsHeader(file_name=file_name + '.gz',
                               dir=dir,
                               compression='gzip')
    check_tpv(header)
    do_pickle(header)

    # Remove an item from the header
    # Start with file_name constructor, to test that the repr is changed by the edit.
    orig_header = header
    header = galsim.FitsHeader(file_name=os.path.join(dir, file_name))
    assert header == orig_header
    del header['AIRMASS']
    assert 'AIRMASS' not in header
    if pyfits_version >= '3.1':
        assert len(header) == tpv_len - 1
    assert header != orig_header
    do_pickle(header)

    # Should be able to get with a default value if the key is not present
    assert header.get('AIRMASS', 2.0) == 2.0
    # key should still not be in the header
    assert 'AIRMASS' not in header
    if pyfits_version >= '3.1':
        assert len(header) == tpv_len - 1
    assert header != orig_header

    # Add items to a header
    header['AIRMASS'] = 2
    assert header.get('AIRMASS') == 2
    assert header != orig_header

    # Overwrite an existing value
    header['AIRMASS'] = 1.7
    assert header.get('AIRMASS') == 1.7
    assert header != orig_header

    # Set with a comment field
    header['AIRMASS'] = (1.9, 'The airmass of the observation')
    assert header.get('AIRMASS') == 1.9
    assert header != orig_header

    # Update with a dict
    d = {'AIRMASS': 1.185}
    header.update(d)
    assert header.get('AIRMASS') == 1.185
    # We are essentially back to where we started, except the len won't be right.
    # Deleting a key removed an item, but setting it overwrote a blank item.
    # But if we add back another one of these, we should be back to the original values.
    header.append('', '', useblanks=False)
    check_tpv(header)
    do_pickle(header)
    assert header != orig_header  # It's still not equal, because the AIRMASS item is in a
    # different location in the list, which is relevant for equality.

    # Clear all values
    header.clear()
    assert 'AIRMASS' not in header
    assert 'FILTER' not in header
    assert len(header) == 0
    do_pickle(header)
    assert header != orig_header
Exemplo n.º 31
0
    def read(self):
        from galsim._pyfits import pyfits
        if isinstance(self.file_name, str):
            hdu_list = pyfits.open(self.file_name)
            hdu = hdu_list[1]
        else:
            hdu = self.file_name
            hdu_list = None
        # Number of parameters used for the interpolation.  We require this to be 2.
        pol_naxis = hdu.header['POLNAXIS']

        # These are the names of the two axes.  Should be X_IMAGE, Y_IMAGE.
        # If they aren't, then the way we use the interpolation will be wrong.
        # Well, really they can also be XWIN_IMAGE, etc.  So just check that it
        # starts with X and ends with IMAGE.
        pol_name1 = hdu.header['POLNAME1']
        pol_name2 = hdu.header['POLNAME2']

        # Zero points and scale.  Interpolation is in terms of (x-x0)/xscale, (y-y0)/yscale
        pol_zero1 = hdu.header['POLZERO1']
        pol_zero2 = hdu.header['POLZERO2']
        pol_scal1 = hdu.header['POLSCAL1']
        pol_scal2 = hdu.header['POLSCAL2']

        # This defines the number of "context groups".
        # Here is Emmanuel's explanation:
        #
        #     POLNGRP is the number of "context groups". A group represents a set of variables
        #     (SExtractor measurements or FITS header parameters if preceded with ":") which share
        #     the same maximum polynomial degree. For instance if x and y are in group 1, and the
        #     degree of that group is 2, and z is in group 2 with degree 1, the polynomial will
        #     consist of:
        #         1, x, x^2, y, y.x, y^2, z, z.x^2, z.y, z.y.x, z.y^2
        #     (see eq 14 in https://www.astromatic.net/pubsvn/software/psfex/trunk/doc/psfex.pdf )
        #     By default, POLNGRP is 1 and the group contains X_IMAGE and Y_IMAGE measurements
        #     from SExtractor.
        #
        # For now, we require this to be 1, since I didn't have any files with POLNGRP != 1 to
        # test on.
        pol_ngrp = hdu.header['POLNGRP']

        # Which group each item is in.  We require group 1.
        pol_group1 = hdu.header['POLGRP1']
        pol_group2 = hdu.header['POLGRP2']

        # The degree of the polynomial.  E.g. POLDEG1 = 2 means the values will be:
        #     1, x, x^2, y, xy, y^2
        # If we start allowing POLNGRP > 1, there is a separate degree for each group.
        pol_deg = hdu.header['POLDEG1']

        # The number of axes in the basis object.  We require this to be 3.
        psf_naxis = hdu.header['PSFNAXIS']

        # The first two axes are the image size of the PSF postage stamp.
        psf_axis1 = hdu.header['PSFAXIS1']
        psf_axis2 = hdu.header['PSFAXIS2']

        # The third axis is the direction of the polynomial interpolation.  So it should
        # be equal to (d+1)(d+2)/2.
        psf_axis3 = hdu.header['PSFAXIS3']

        # This is the PSF "sample size".  Again, from Emmanuel:
        #
        #     PSF_SAMP is the sampling step of the PSF. PSF_SAMP=0.5 means that the PSF model has
        #     two samples per original image pixel (superresolution, so in automatic mode it is a
        #     sign that the original images were undersampled)
        #
        # In other words, it can be thought of as a unit conversion:
        #     "image pixels" / "psfex pixels"
        # So 1 image pixel = (1/psf_samp) psfex pixels.
        psf_samp = hdu.header['PSF_SAMP']

        # The basis object is a data cube (assuming PSFNAXIS==3)
        basis = hdu.data.field('PSF_MASK')[0]

        # Make sure to close the hdu before we might raise exceptions.
        if hdu_list:
            hdu_list.close()

        # Check for valid values of all these things.
        # Not sure which of these are actually required in PSFEx files, but this implementation
        # assumes these things are true, so if this fails, we probably need to rework some aspect
        # of this code.
        try:
            assert pol_naxis == 2
            assert pol_name1.startswith('X') and pol_name1.endswith('IMAGE')
            assert pol_name2.startswith('Y') and pol_name2.endswith('IMAGE')
            assert pol_ngrp == 1
            assert pol_group1 == 1
            assert pol_group2 == 1
            assert psf_naxis == 3
            assert psf_axis3 == ((pol_deg+1)*(pol_deg+2))//2
            assert basis.shape[0] == psf_axis3
            assert basis.shape[1] == psf_axis2
            assert basis.shape[2] == psf_axis1
        except AssertionError as e:
            raise OSError("PSFEx file %s is not as expected.\n%r"%(self.file_name, e))

        # Save some of these values for use in building the interpolated images
        self.basis = basis
        self.fit_order = pol_deg
        self.fit_size = psf_axis3
        self.x_zero = pol_zero1
        self.y_zero = pol_zero2
        self.x_scale = pol_scal1
        self.y_scale = pol_scal2
        self.sample_scale = psf_samp
Exemplo n.º 32
0
def test_nan_fits():
    """Test reading in a FITS file that has NAN.0 entries in the header.

    This test is specifically in response to issue #602.
    """
    import warnings
    from galsim._pyfits import pyfits
    import time
    t1 = time.time()

    # The problematic file:
    file_name = "des_data/DECam_00158414_01.fits.fz"

    # These are the values we should be reading in:
    ref_bounds = galsim.BoundsI(xmin=1, xmax=2048, ymin=1, ymax=4096)
    ref_wcs = galsim.GSFitsWCS(_data = [
            'TPV',
            numpy.array([13423.2, 6307.333]),
            numpy.array([[-4.410051713005e-09, 7.286844513153e-05],
                   [-7.285161461796e-05, 3.936353853081e-09]]),
            galsim.CelestialCoord(1.1502513773465992 * galsim.radians,
                                  -0.9862866578241959 * galsim.radians),
            numpy.array(
                    [[[0.004336243600183, -0.01133740904139, 0.01202041999278, -0.004357212119479],
                      [1.013741474567, -0.01657049389296, 0.005805882078771, 0.0],
                      [0.008865811106037, -0.007472254968395, 0.0, 0.0],
                      [0.0008534196190617, 0.0, 0.0, 0.0]],
                     [[0.002619866608142, 0.9931356822158, 0.008771460618847, -0.003739430249945],
                      [-0.009422336649176, 0.01826140592329, -0.009387805146152, 0.0],
                      [-0.01066967054507, 0.007202907073747, 0.0, 0.0],
                      [-0.003683686751425, 0.0, 0.0, 0.0]]
                    ]),
            None, None])

    # First just read the file directly, not using galsim.fits.read
    fp = pyfits.open(file_name)
    try:
        data = fp[1].data
        print 'Able to read FITS file with NAN.0 without any problem.'
    except:
        print 'Running verify to fix the problematic FITS header.'
        with warnings.catch_warnings():
            warnings.filterwarnings("ignore",category=pyfits.verify.VerifyWarning)
            fp[1].verify('fix')
        # This should work now.
        data = fp[1].data
    assert data.shape == ref_bounds.numpyShape()

    # Check a direct read of the header with GSFitsWCS
    header = fp[1].header
    wcs = galsim.GSFitsWCS(header=header)
    assert wcs == ref_wcs

    # Now read it with GalSim's fits.read function.
    # Reading this file will emit verification warnings, so we'll ignore those here for the
    # test.  But the result should be a valid image.
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore",category=pyfits.verify.VerifyWarning)
        im = galsim.fits.read(file_name)

    assert im.bounds == ref_bounds
    assert im.wcs == ref_wcs

    t2 = time.time()
    print 'time for %s = %.2f'%(funcname(),t2-t1)