def getPsf(fwhm, psfFile='psfFake.fits'):
    """Make a PSF image, and pass the PSF object.

    @param fwhm:
        FWHM of the PSF in pixel
    """
    logging.basicConfig(format="%(message)s",
                        level=logging.INFO,
                        stream=sys.stdout)
    logger = logging.getLogger("psf")

    nx, ny = np.floor(fwhm * 10), np.floor(fwhm * 10)
    scale = 1.0
    logger.info('PSF: FWHM=%4.1f (%d, %d)', fwhm, nx, ny)

    # Make a PSF Image
    psfImg = galsim.ImageF(nx, ny)
    psfCen = psfImg.bounds.trueCenter()
    psfWcs = galsim.OffsetWCS(scale=scale, origin=psfCen)
    psfImg.wcs = psfWcs

    # PSF model
    psf = galsim.Gaussian(fwhm=fwhm)

    # Draw PSF image
    psf.drawImage(psfImg, method='no_pixel')

    # Save the fits image
    galsim.fits.write(psfImg, psfFile)
    logger.info('Write the PSF to %s', psfFile)

    return psf
Exemple #2
0
def main(argv):
    """
    Make images using variable PSF and shear:
      - The main image is 10 x 10 postage stamps.
      - Each postage stamp is 48 x 48 pixels.
      - The second HDU has the corresponding PSF image.
      - Applied shear is from a power spectrum P(k) ~ k^1.8.
      - Galaxies are real galaxies oriented in a ring test of 20 each.
      - The PSF is Gaussian with FWHM, ellipticity and position angle functions of (x,y)
      - Noise is Poisson using a nominal sky value of 1.e6.
    """
    logging.basicConfig(format="%(message)s",
                        level=logging.INFO,
                        stream=sys.stdout)
    logger = logging.getLogger("demo10")

    # Define some parameters we'll use below.
    # Normally these would be read in from some parameter file.

    n_tiles = 10  # number of tiles in each direction.
    stamp_size = 48  # pixels

    pixel_scale = 0.44  # arcsec / pixel
    sky_level = 1.e6  # ADU / arcsec^2

    # The random seed is used for both the power spectrum realization and the random properties
    # of the galaxies.
    random_seed = 3339201

    # Make output directory if not already present.
    if not os.path.isdir('output'):
        os.mkdir('output')

    file_name = os.path.join('output', 'power_spectrum.fits')

    # These will be created for each object below.  The values we'll use will be functions
    # of (x,y) relative to the center of the image.  (r = sqrt(x^2+y^2))
    # psf_fwhm = 0.9 + 0.5 * (r/100)^2  -- arcsec
    # psf_e = 0.4 * (r/100)^1.5         -- large value at the edge, so visible by eye.
    # psf_beta = atan2(y/x) + pi/2      -- tangential pattern

    gal_dilation = 3  # Make the galaxies a bit larger than their original size.
    gal_signal_to_noise = 100  # Pretty high.
    psf_signal_to_noise = 1000  # Even higher.

    logger.info('Starting demo script 10')

    # Read in galaxy catalog
    cat_file_name = 'real_galaxy_catalog_23.5_example.fits'
    dir = 'data'
    real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=dir)
    logger.info('Read in %d real galaxies from catalog',
                real_galaxy_catalog.nobjects)

    # List of IDs to use.  We select 5 particularly irregular galaxies for this demo.
    # Then we'll choose randomly from this list.
    id_list = [106416, 106731, 108402, 116045, 116448]

    # Make the 5 galaxies we're going to use here rather than remake them each time.
    # This means the Fourier transforms of the real galaxy images don't need to be recalculated
    # each time, so it's a bit more efficient.
    gal_list = [
        galsim.RealGalaxy(real_galaxy_catalog, id=id) for id in id_list
    ]
    # Grab the index numbers before we transform them and lose the index attribute.
    cosmos_index = [gal.index for gal in gal_list]

    # Make the galaxies a bit larger than their original observed size.
    gal_list = [gal.dilate(gal_dilation) for gal in gal_list]

    # Setup the PowerSpectrum object we'll be using:
    ps = galsim.PowerSpectrum(lambda k: k**1.8)
    # The argument here is "e_power_function" which defines the E-mode power to use.

    # There is also a b_power_function if you want to include any B-mode power:
    #     ps = galsim.PowerSpectrum(e_power_function, b_power_function)

    # You may even omit the e_power_function argument and have a pure B-mode power spectrum.
    #     ps = galsim.PowerSpectrum(b_power_function = b_power_function)

    # All the random number generator classes derive from BaseDeviate.
    # When we construct another kind of deviate class from any other
    # kind of deviate class, the two share the same underlying random number
    # generator.  Sometimes it can be clearer to just construct a BaseDeviate
    # explicitly and then construct anything else you need from that.
    # Note: A BaseDeviate cannot be used to generate any values.  It can
    # only be used in the constructor for other kinds of deviates.
    # The seeds for the objects are random_seed+1..random_seed+nobj.
    # The seeds for things at the image or file level use random_seed itself.
    nobj = n_tiles * n_tiles
    rng = galsim.BaseDeviate(random_seed)

    # Have the PowerSpectrum object build a grid of shear values for us to use.
    grid_g1, grid_g2 = ps.buildGrid(grid_spacing=stamp_size * pixel_scale,
                                    ngrid=n_tiles,
                                    rng=rng)

    # Setup the images:
    gal_image = galsim.ImageF(stamp_size * n_tiles, stamp_size * n_tiles)
    psf_image = galsim.ImageF(stamp_size * n_tiles, stamp_size * n_tiles)

    # Update the image WCS to use the image center as the origin of the WCS.
    # The class that acts like a PixelScale except for this offset is called OffsetWCS.
    im_center = gal_image.true_center
    wcs = galsim.OffsetWCS(scale=pixel_scale, origin=im_center)
    gal_image.wcs = wcs
    psf_image.wcs = wcs

    # We will place the tiles in a random order.  To do this, we make two lists for the
    # ix and iy values.  Then we apply a random permutation to the lists (in tandem).
    ix_list = []
    iy_list = []
    for ix in range(n_tiles):
        for iy in range(n_tiles):
            ix_list.append(ix)
            iy_list.append(iy)
    # This next function will use the given random number generator, rng, and use it to
    # randomly permute any number of lists.  All lists will have the same random permutation
    # applied.
    galsim.random.permute(rng, ix_list, iy_list)

    # Initialize the OutputCatalog for the truth values
    names = [
        'gal_num', 'x_image', 'y_image', 'psf_e1', 'psf_e2', 'psf_fwhm',
        'cosmos_id', 'cosmos_index', 'theta', 'g1', 'g2', 'shift_x', 'shift_y'
    ]
    types = [
        int, float, float, float, float, float, str, int, float, float, float,
        float, float
    ]
    truth_catalog = galsim.OutputCatalog(names, types)

    # Build each postage stamp:
    for k in range(nobj):
        # The usual random number generator using a different seed for each galaxy.
        rng = galsim.BaseDeviate(random_seed + k + 1)

        # Determine the bounds for this stamp and its center position.
        ix = ix_list[k]
        iy = iy_list[k]
        b = galsim.BoundsI(ix * stamp_size + 1, (ix + 1) * stamp_size,
                           iy * stamp_size + 1, (iy + 1) * stamp_size)
        sub_gal_image = gal_image[b]
        sub_psf_image = psf_image[b]

        pos = wcs.toWorld(b.true_center)
        # The image comes out as about 211 arcsec across, so we define our variable
        # parameters in terms of (r/100 arcsec), so roughly the scale size of the image.
        rsq = (pos.x**2 + pos.y**2)
        r = math.sqrt(rsq)

        psf_fwhm = 0.9 + 0.5 * rsq / 100**2  # arcsec
        psf_e = 0.4 * (r / 100.)**1.5
        psf_beta = (math.atan2(pos.y, pos.x) + math.pi / 2) * galsim.radians

        # Define the PSF profile
        psf = galsim.Gaussian(fwhm=psf_fwhm)
        psf_shape = galsim.Shear(e=psf_e, beta=psf_beta)
        psf = psf.shear(psf_shape)

        # Define the galaxy profile:

        # For this demo, we are doing a ring test where the same galaxy profile is drawn at many
        # orientations stepped uniformly in angle, making a ring in e1-e2 space.
        # We're drawing each profile at 20 different orientations and then skipping to the
        # next galaxy in the list.  So theta steps by 1/20 * 360 degrees:
        theta_deg = (k % 20) * 360. / 20
        theta = theta_deg * galsim.degrees

        # The index needs to increment every 20 objects so we use k/20 using integer math.
        index = k // 20
        gal = gal_list[index]

        # This makes a new copy so we're not changing the object in the gal_list.
        gal = gal.rotate(theta)

        # Apply the shear from the power spectrum.  We should either turn the gridded shears
        # grid_g1[iy, ix] and grid_g2[iy, ix] into gridded reduced shears using a utility called
        # galsim.lensing.theoryToObserved, or use ps.getShear() which by default gets the reduced
        # shear.  ps.getShear() is also more flexible because it can get the shear at positions that
        # are not on the original grid, as long as they are contained within the bounds of the full
        # grid. So in this example we'll use ps.getShear().
        alt_g1, alt_g2 = ps.getShear(pos)
        gal = gal.shear(g1=alt_g1, g2=alt_g2)

        # Apply half-pixel shift in a random direction.
        shift_r = pixel_scale * 0.5
        ud = galsim.UniformDeviate(rng)
        t = ud() * 2. * math.pi
        dx = shift_r * math.cos(t)
        dy = shift_r * math.sin(t)
        gal = gal.shift(dx, dy)

        # Make the final image, convolving with the psf
        final = galsim.Convolve([psf, gal])

        # Draw the image
        final.drawImage(sub_gal_image)

        # For the PSF image, we don't match the galaxy shift.  Rather, we use the offset
        # parameter to drawImage to apply a random offset of up to 0.5 pixels in each direction.
        # Note the difference in units between shift and offset.  The shift is applied to the
        # surface brightness profile, so it is in sky coordinates (as all dimension are for
        # GSObjects), which are arcsec here.  The offset though is applied to the image itself,
        # so it is in pixels.  Hence, we don't multiply by pixel_scale.
        psf_dx = ud() - 0.5
        psf_dy = ud() - 0.5
        psf_offset = galsim.PositionD(psf_dx, psf_dy)

        # Draw the PSF image:
        # We use real space integration over the pixels to avoid some of the
        # artifacts that can show up with Fourier convolution.
        # The level of the artifacts is quite low, but when drawing with
        # so little noise, they are apparent with ds9's zscale viewing.
        psf.drawImage(sub_psf_image, method='real_space', offset=psf_offset)

        # Build the noise model: Poisson noise with a given sky level.
        sky_level_pixel = sky_level * pixel_scale**2
        noise = galsim.PoissonNoise(rng, sky_level=sky_level_pixel)

        # Add noise to the PSF image, using the normal noise model, but scaling the
        # PSF flux high enough to reach the desired signal-to-noise.
        # See demo5.py for more info about how this works.
        sub_psf_image.addNoiseSNR(noise, psf_signal_to_noise)

        # And also to the galaxy image using its signal-to-noise.
        sub_gal_image.addNoiseSNR(noise, gal_signal_to_noise)

        # Add the truth values to the truth catalog
        row = [
            k, b.true_center.x, b.true_center.y, psf_shape.e1,
            psf_shape.e2, psf_fwhm, id_list[index], cosmos_index[index],
            (theta_deg % 360.), alt_g1, alt_g2, dx, dy
        ]
        truth_catalog.addRow(row)

        logger.info('Galaxy (%d,%d): position relative to center = %s', ix, iy,
                    str(pos))

    logger.info('Done making images of postage stamps')

    # In this case, we'll attach the truth catalog as an additional HDU in the same file as
    # the image data.
    truth_hdu = truth_catalog.writeFitsHdu()

    # Now write the images to disk.
    images = [gal_image, psf_image, truth_hdu]
    # Any items in the "images" list that is already an hdu is just used directly.
    # The actual images are converted to FITS hdus that contain the image data.
    galsim.fits.writeMulti(images, file_name)
    logger.info('Wrote image to %r', file_name)
Exemple #3
0
def main(argv):
    """
    Make images using variable PSF and shear:
      - The main image is 10 x 10 postage stamps.
      - Each postage stamp is 48 x 48 pixels.
      - The second HDU has the corresponding PSF image.
      - Applied shear is from a power spectrum P(k) ~ k^1.8.
      - Galaxies are real galaxies oriented in a ring test of 20 each.
      - The PSF is Gaussian with FWHM, ellipticity and position angle functions of (x,y)
      - Noise is Poisson using a nominal sky value of 1.e6.
    """
    logging.basicConfig(format="%(message)s",
                        level=logging.INFO,
                        stream=sys.stdout)
    logger = logging.getLogger("demo10")

    # Define some parameters we'll use below.
    # Normally these would be read in from some parameter file.

    n_tiles = 10  # number of tiles in each direction.
    stamp_size = 48  # pixels

    pixel_scale = 0.44  # arcsec / pixel
    sky_level = 1.e6  # ADU / arcsec^2

    # The random seed is used for both the power spectrum realization and the random properties
    # of the galaxies.
    random_seed = 3339201

    # Make output directory if not already present.
    if not os.path.isdir('output'):
        os.mkdir('output')

    file_name = os.path.join('output', 'power_spectrum.fits')

    # These will be created for each object below.  The values we'll use will be functions
    # of (x,y) relative to the center of the image.  (r = sqrt(x^2+y^2))
    # psf_fwhm = 0.9 + 0.5 * (r/100)^2  -- arcsec
    # psf_e = 0.4 * (r/100)^1.5         -- large value at the edge, so visible by eye.
    # psf_beta = atan2(y/x) + pi/2      -- tangential pattern

    gal_dilation = 3  # Make the galaxies a bit larger than their original size.
    gal_signal_to_noise = 100  # Pretty high.
    psf_signal_to_noise = 1000  # Even higher.

    logger.info('Starting demo script 10')

    # Read in galaxy catalog
    cat_file_name = 'real_galaxy_catalog_example.fits'
    dir = 'data'
    real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=dir)
    logger.info('Read in %d real galaxies from catalog',
                real_galaxy_catalog.nobjects)

    # List of IDs to use.  We select 5 particularly irregular galaxies for this demo.
    # Then we'll choose randomly from this list.
    id_list = [106416, 106731, 108402, 116045, 116448]

    # Make the 5 galaxies we're going to use here rather than remake them each time.
    # This means the Fourier transforms of the real galaxy images don't need to be recalculated
    # each time, so it's a bit more efficient.
    gal_list = [
        galsim.RealGalaxy(real_galaxy_catalog, id=id) for id in id_list
    ]

    # Make the galaxies a bit larger than their original observed size.
    gal_list = [gal.dilate(gal_dilation) for gal in gal_list]

    # Setup the PowerSpectrum object we'll be using:
    ps = galsim.PowerSpectrum(lambda k: k**1.8)
    # The argument here is "e_power_function" which defines the E-mode power to use.

    # There is also a b_power_function if you want to include any B-mode power:
    #     ps = galsim.PowerSpectrum(e_power_function, b_power_function)

    # You may even omit the e_power_function argument and have a pure B-mode power spectrum.
    #     ps = galsim.PowerSpectrum(b_power_function = b_power_function)

    # All the random number generator classes derive from BaseDeviate.
    # When we construct another kind of deviate class from any other
    # kind of deviate class, the two share the same underlying random number
    # generator.  Sometimes it can be clearer to just construct a BaseDeviate
    # explicitly and then construct anything else you need from that.
    # Note: A BaseDeviate cannot be used to generate any values.  It can
    # only be used in the constructor for other kinds of deviates.
    # The seeds for the objects are random_seed..random_seed+nobj-1 (which comes later),
    # so use the next one.
    nobj = n_tiles * n_tiles
    rng = galsim.BaseDeviate(random_seed + nobj)

    # Setup the images:
    gal_image = galsim.ImageF(stamp_size * n_tiles, stamp_size * n_tiles)
    psf_image = galsim.ImageF(stamp_size * n_tiles, stamp_size * n_tiles)

    # Update the image WCS to use the image center as the origin of the WCS.
    # The class that acts like a PixelScale except for this offset is called OffsetWCS.
    im_center = gal_image.bounds.trueCenter()
    wcs = galsim.OffsetWCS(scale=pixel_scale, origin=im_center)
    gal_image.wcs = wcs
    psf_image.wcs = wcs

    # We will place the tiles in a random order.  To do this, we make two lists for the
    # ix and iy values.  Then we apply a random permutation to the lists (in tandem).
    ix_list = []
    iy_list = []
    for ix in range(n_tiles):
        for iy in range(n_tiles):
            ix_list.append(ix)
            iy_list.append(iy)
    # This next function will use the given random number generator, rng, and use it to
    # randomly permute any number of lists.  All lists will have the same random permutation
    # applied.
    galsim.random.permute(rng, ix_list, iy_list)

    # Now have the PowerSpectrum object build a grid of shear values for us to use.
    # Also, because of some technical details about how the config stuff handles the random
    # number generator here, we need to duplicate the rng object if we want to have the
    # two output files match.  This means that technically, the same sequence of random numbers
    # will be used in building the grid as will be used by the other uses of rng (permuting the
    # postage stamps and adding noise).  But since they are used in such completely different
    # ways, it is hard to imagine how this could lead to any kind of bias in the images.
    grid_g1, grid_g2 = ps.buildGrid(grid_spacing=stamp_size * pixel_scale,
                                    ngrid=n_tiles,
                                    rng=rng.duplicate())

    # Build each postage stamp:
    for k in range(nobj):
        # The usual random number generator using a different seed for each galaxy.
        rng = galsim.BaseDeviate(random_seed + k)

        # Determine the bounds for this stamp and its center position.
        ix = ix_list[k]
        iy = iy_list[k]
        b = galsim.BoundsI(ix * stamp_size + 1, (ix + 1) * stamp_size,
                           iy * stamp_size + 1, (iy + 1) * stamp_size)
        sub_gal_image = gal_image[b]
        sub_psf_image = psf_image[b]

        pos = wcs.toWorld(b.trueCenter())
        # The image comes out as about 211 arcsec across, so we define our variable
        # parameters in terms of (r/100 arcsec), so roughly the scale size of the image.
        r = math.sqrt(pos.x**2 + pos.y**2) / 100
        psf_fwhm = 0.9 + 0.5 * r**2  # arcsec
        psf_e = 0.4 * r**1.5
        psf_beta = (math.atan2(pos.y, pos.x) + math.pi / 2) * galsim.radians

        # Define the PSF profile
        psf = galsim.Gaussian(fwhm=psf_fwhm)
        psf = psf.shear(e=psf_e, beta=psf_beta)

        # Define the galaxy profile:

        # For this demo, we are doing a ring test where the same galaxy profile is drawn at many
        # orientations stepped uniformly in angle, making a ring in e1-e2 space.
        # We're drawing each profile at 20 different orientations and then skipping to the
        # next galaxy in the list.  So theta steps by 1/20 * 360 degrees:
        theta = k / 20. * 360. * galsim.degrees

        # The index needs to increment every 20 objects so we use k/20 using integer math.
        index = k / 20
        gal = gal_list[index]

        # This makes a new copy so we're not changing the object in the gal_list.
        gal = gal.rotate(theta)

        # Apply the shear from the power spectrum.  We should either turn the gridded shears
        # grid_g1[iy, ix] and grid_g2[iy, ix] into gridded reduced shears using a utility called
        # galsim.lensing.theoryToObserved, or use ps.getShear() which by default gets the reduced
        # shear.  ps.getShear() is also more flexible because it can get the shear at positions that
        # are not on the original grid, as long as they are contained within the bounds of the full
        # grid. So in this example we'll use ps.getShear().
        alt_g1, alt_g2 = ps.getShear(pos)
        gal = gal.shear(g1=alt_g1, g2=alt_g2)

        # Apply half-pixel shift in a random direction.
        shift_r = pixel_scale * 0.5
        ud = galsim.UniformDeviate(rng)
        theta = ud() * 2. * math.pi
        dx = shift_r * math.cos(theta)
        dy = shift_r * math.sin(theta)
        gal = gal.shift(dx, dy)

        # Make the final image, convolving with the psf
        final = galsim.Convolve([psf, gal])

        # Draw the image
        final.drawImage(sub_gal_image)

        # Now add noise to get our desired S/N
        # See demo5.py for more info about how this works.
        sky_level_pixel = sky_level * pixel_scale**2
        noise = galsim.PoissonNoise(rng, sky_level=sky_level_pixel)
        sub_gal_image.addNoiseSNR(noise, gal_signal_to_noise)

        # For the PSF image, we also shift the PSF by the same amount.
        psf = psf.shift(dx, dy)

        # Draw the PSF image:
        # We use real space integration over the pixels to avoid some of the
        # artifacts that can show up with Fourier convolution.
        # The level of the artifacts is quite low, but when drawing with
        # so little noise, they are apparent with ds9's zscale viewing.
        psf.drawImage(sub_psf_image, method='real_space')

        # Again, add noise, but at higher S/N this time.
        sub_psf_image.addNoiseSNR(noise, psf_signal_to_noise)

        logger.info('Galaxy (%d,%d): position relative to center = %s', ix, iy,
                    str(pos))

    logger.info('Done making images of postage stamps')

    # Now write the images to disk.
    images = [gal_image, psf_image]
    galsim.fits.writeMulti(images, file_name)
    logger.info('Wrote image to %r', file_name)
def galaxy2(sky=0.15, galSNR=6000.0, fwhm=7.0):
    """Make the second fake galaxy.

    @param  sky:
        Per pixel sky value, used to get Poisson noise (default=0.2)

    @param  galSNR:
        The SNR of the galaxy, used to get the noise (default=5000.0)

    @param  fwhm:
        FWHM of the Gaussian PSF used to convolve the image (default=6.0)

    ------
    Three component edge-on disk galaxy with bulge+disk+halo   :

        Component 1: xCen=420.0, yCen=380; FLux=300;
                     Re=8.0;   n=3.0, q=0.8, PA1=45.0

        Component 2: xCen=420.0, yCen=380; FLux=500;
                     Re=40.0;  n=0.8, q=0.1, PA1=45.0

        Component 3: xCen=420.0, yCen=380; FLux=600;
                     Re=35.0;  n=1.5, q=0.6, PA1=40.0

        Contamination 1: FLux=100; Re=20.0; n=2.5, q=0.7, PA1=0.0

        Contamination 2: FLux=200; Re=30.0; n=2.0, q=0.6, PA1=75.0
    """
    logging.basicConfig(format="%(message)s",
                        level=logging.INFO,
                        stream=sys.stdout)
    logger = logging.getLogger("galaxy2")

    # Basic information of the image
    nx, ny = 800, 800
    exptime = 100.0
    scale = 1.0
    logger.info('Galaxy2: Sky=%6.2f, SNR=%5d', sky, galSNR)

    # Get the PSF, and save the PSF image
    psf = getPsf(fwhm, psfFile='galaxy2_psf.fits')

    # Make a GalSim Image
    gal2Img = galsim.ImageF(nx, ny)
    imgCen = gal2Img.bounds.trueCenter()
    imgWcs = galsim.OffsetWCS(scale=scale, origin=imgCen)
    gal2Img.wcs = imgWcs

    # Component 1: flux1 = 300; reff1 = 8.0; nser1 = 3.0; q1 = 0.8; pa1 = 45.0
    comp1 = galsim.Sersic(
        n=3.0, half_light_radius=8.0, flux=(300.0 * exptime)).shear(
            q=0.8,
            beta=(0.0 * galsim.degrees)).rotate(45.0 * galsim.degrees).shift(
                (420.0 - (nx / 2.0)), (380.0 - (ny / 2.0)))

    # Component 1: flux1 = 500; reff1 = 40.0; nser1 = 0.8; q1 = 0.1; pa1 = 45.0
    comp2 = galsim.Sersic(n=0.8,
                          half_light_radius=40.0,
                          flux=(500.0 * exptime)).shear(
                              q=0.10, beta=(0.0 * galsim.degrees)).rotate(
                                  45.0 * galsim.degrees).shift(
                                      (420.0 - (nx / 2.0)),
                                      (380.0 - (ny / 2.0)))

    # Component 3: flux1 = 600; reff1 = 35.0; nser1 = 1.5; q1 = 0.6; pa1 = 40.0
    comp3 = galsim.Sersic(n=1.5,
                          half_light_radius=35.0,
                          flux=(600.0 * exptime)).shear(
                              q=0.6, beta=(0.0 * galsim.degrees)).rotate(
                                  40.0 * galsim.degrees).shift(
                                      (420.0 - (nx / 2.0)),
                                      (380.0 - (ny / 2.0)))

    # Contamination 1:
    cont1 = galsim.Sersic(n=2.5,
                          half_light_radius=20.0,
                          flux=(100.0 * exptime)).shear(
                              q=0.7, beta=(0.0 * galsim.degrees)).rotate(
                                  0.0 * galsim.degrees).shift(50, 200)

    # Contamination 2:
    cont2 = galsim.Sersic(n=2.0,
                          half_light_radius=30.0,
                          flux=(200.0 * exptime)).shear(
                              q=0.6, beta=(0.0 * galsim.degrees)).rotate(
                                  75.0 * galsim.degrees).shift(-300, -190)

    # Add all components together
    gal2 = galsim.Add([comp1, comp2, comp3, cont1, cont2])

    # Convolution
    gal2Conv = galsim.Convolve([psf, gal2])

    # Draw the image
    gal2Conv.drawImage(gal2Img, method='no_pixel')

    # Add Noise
    rng = random.seed(datetime.now())
    noise = galsim.PoissonNoise(rng, sky_level=sky)
    gal2Img.addNoiseSNR(noise, galSNR)

    # Save the FITS file
    gal2File = 'galaxy2_img.fits'
    logger.info('Write to FITS file : %s', gal2File)
    galsim.fits.write(gal2Img, gal2File)

    # Save the PNG picture
    savePng(gal2Img, pngFile='galaxy2_img.png')

    return gal2Img
Exemple #5
0
def test_withOrigin():
    from test_wcs import Cubic

    # First EuclideantWCS types:

    wcs_list = [
        galsim.OffsetWCS(0.3, galsim.PositionD(1, 1), galsim.PositionD(10,
                                                                       23)),
        galsim.OffsetShearWCS(0.23, galsim.Shear(g1=0.1, g2=0.3),
                              galsim.PositionD(12, 43)),
        galsim.AffineTransform(0.01, 0.26, -0.26, 0.02,
                               galsim.PositionD(12, 43)),
        galsim.UVFunction(ufunc=lambda x, y: 0.2 * x,
                          vfunc=lambda x, y: 0.2 * y),
        galsim.UVFunction(ufunc=lambda x, y: 0.2 * x,
                          vfunc=lambda x, y: 0.2 * y,
                          xfunc=lambda u, v: u / scale,
                          yfunc=lambda u, v: v / scale),
        galsim.UVFunction(ufunc='0.2*x + 0.03*y', vfunc='0.01*x + 0.2*y'),
    ]

    color = 0.3
    for wcs in wcs_list:
        # Original version of the shiftOrigin tests in do_nonlocal_wcs using deprecated name.
        new_origin = galsim.PositionI(123, 321)
        wcs3 = check_dep(wcs.withOrigin, new_origin)
        assert wcs != wcs3, name + ' is not != wcs.withOrigin(pos)'
        wcs4 = wcs.local(wcs.origin, color=color)
        assert wcs != wcs4, name + ' is not != wcs.local()'
        assert wcs4 != wcs, name + ' is not != wcs.local() (reverse)'
        world_origin = wcs.toWorld(wcs.origin, color=color)
        if wcs.isUniform():
            if wcs.world_origin == galsim.PositionD(0, 0):
                wcs2 = wcs.local(wcs.origin,
                                 color=color).withOrigin(wcs.origin)
                assert wcs == wcs2, name + ' is not equal after wcs.local().withOrigin(origin)'
            wcs2 = wcs.local(wcs.origin,
                             color=color).withOrigin(wcs.origin,
                                                     wcs.world_origin)
            assert wcs == wcs2, name + ' not equal after wcs.local().withOrigin(origin,world_origin)'
        world_pos1 = wcs.toWorld(galsim.PositionD(0, 0), color=color)
        wcs3 = check_dep(wcs.withOrigin, new_origin)
        world_pos2 = wcs3.toWorld(new_origin, color=color)
        np.testing.assert_almost_equal(
            world_pos2.x, world_pos1.x, 7,
            'withOrigin(new_origin) returned wrong world position')
        np.testing.assert_almost_equal(
            world_pos2.y, world_pos1.y, 7,
            'withOrigin(new_origin) returned wrong world position')
        new_world_origin = galsim.PositionD(5352.7, 9234.3)
        wcs5 = check_dep(wcs.withOrigin,
                         new_origin,
                         new_world_origin,
                         color=color)
        world_pos3 = wcs5.toWorld(new_origin, color=color)
        np.testing.assert_almost_equal(
            world_pos3.x, new_world_origin.x, 7,
            'withOrigin(new_origin, new_world_origin) returned wrong position')
        np.testing.assert_almost_equal(
            world_pos3.y, new_world_origin.y, 7,
            'withOrigin(new_origin, new_world_origin) returned wrong position')

    # Now some CelestialWCS types
    cubic_u = Cubic(2.9e-5, 2000., 'u')
    cubic_v = Cubic(-3.7e-5, 2000., 'v')
    center = galsim.CelestialCoord(23 * galsim.degrees, -13 * galsim.degrees)
    radec = lambda x, y: center.deproject_rad(
        cubic_u(x, y) * 0.2, cubic_v(x, y) * 0.2, projection='lambert')
    wcs_list = [
        galsim.RaDecFunction(radec),
        galsim.AstropyWCS('1904-66_TAN.fits', dir='fits_files'),
        galsim.GSFitsWCS('tpv.fits', dir='fits_files'),
        galsim.FitsWCS('sipsample.fits', dir='fits_files'),
    ]

    for wcs in wcs_list:
        # Original version of the shiftOrigin tests in do_celestial_wcs using deprecated name.
        new_origin = galsim.PositionI(123, 321)
        wcs3 = wcs.shiftOrigin(new_origin)
        assert wcs != wcs3, name + ' is not != wcs.shiftOrigin(pos)'
        wcs4 = wcs.local(wcs.origin)
        assert wcs != wcs4, name + ' is not != wcs.local()'
        assert wcs4 != wcs, name + ' is not != wcs.local() (reverse)'
        world_pos1 = wcs.toWorld(galsim.PositionD(0, 0))
        wcs3 = wcs.shiftOrigin(new_origin)
        world_pos2 = wcs3.toWorld(new_origin)
        np.testing.assert_almost_equal(
            world_pos2.distanceTo(world_pos1) / galsim.arcsec, 0, 7,
            'shiftOrigin(new_origin) returned wrong world position')