Exemple #1
0
def test_crg_noise_draw_transform_commutativity():
    """Test commutativity of ChromaticRealGalaxy correlated noise under operations of drawImage and
    applying transformations.
    """
    LSST_i = galsim.Bandpass(os.path.join(bppath, "LSST_r.dat"), 'nm')
    f606w_cat = galsim.RealGalaxyCatalog('AEGIS_F606w_catalog.fits',
                                         dir=image_dir)
    f814w_cat = galsim.RealGalaxyCatalog('AEGIS_F814w_catalog.fits',
                                         dir=image_dir)

    psf = galsim.Gaussian(fwhm=0.6)
    crg = galsim.ChromaticRealGalaxy([f606w_cat, f814w_cat],
                                     id=14886,
                                     maxk=psf.maxk)

    factor = 1.5
    g1 = g2 = 0.1
    mu = 1.2
    theta = 45 * galsim.degrees
    jac = [1.1, 0.1, -0.1, 1.2]

    orig = galsim.Convolve(crg, psf)
    orig.drawImage(LSST_i)

    draw_transform_img = galsim.ImageD(16, 16, scale=0.2)
    transform_draw_img = draw_transform_img.copy()

    multiplied = orig * factor
    multiplied.drawImage(LSST_i)  # needed to populate noise property
    (orig.noise * factor**2).drawImage(image=draw_transform_img)
    multiplied.noise.drawImage(image=transform_draw_img)
    np.testing.assert_array_almost_equal(draw_transform_img.array,
                                         transform_draw_img.array)

    divided = orig / factor
    divided.drawImage(LSST_i)
    (orig.noise / factor**2).drawImage(image=draw_transform_img)
    divided.noise.drawImage(image=transform_draw_img)
    np.testing.assert_array_almost_equal(draw_transform_img.array,
                                         transform_draw_img.array)

    expanded = orig.expand(factor)
    expanded.drawImage(LSST_i)
    orig.noise.expand(factor).drawImage(image=draw_transform_img)
    expanded.noise.drawImage(image=transform_draw_img)
    np.testing.assert_array_almost_equal(draw_transform_img.array,
                                         transform_draw_img.array)

    dilated = orig.dilate(factor)
    dilated.drawImage(LSST_i)
    orig.noise.dilate(factor).drawImage(image=draw_transform_img)
    dilated.noise.drawImage(image=transform_draw_img)
    np.testing.assert_array_almost_equal(draw_transform_img.array,
                                         transform_draw_img.array)

    magnified = orig.magnify(mu)
    magnified.drawImage(LSST_i)
    orig.noise.magnify(mu).drawImage(image=draw_transform_img)
    magnified.noise.drawImage(image=transform_draw_img)
    np.testing.assert_array_almost_equal(draw_transform_img.array,
                                         transform_draw_img.array)

    lensed = orig.lens(g1, g2, mu)
    lensed.drawImage(LSST_i)
    orig.noise.lens(g1, g2, mu).drawImage(image=draw_transform_img)
    lensed.noise.drawImage(image=transform_draw_img)
    np.testing.assert_array_almost_equal(draw_transform_img.array,
                                         transform_draw_img.array)

    rotated = orig.rotate(theta)
    rotated.drawImage(LSST_i)
    orig.noise.rotate(theta).drawImage(image=draw_transform_img)
    rotated.noise.drawImage(image=transform_draw_img)
    np.testing.assert_array_almost_equal(draw_transform_img.array,
                                         transform_draw_img.array)

    sheared = orig.shear(g1=g1, g2=g2)
    sheared.drawImage(LSST_i)
    orig.noise.shear(g1=g1, g2=g2).drawImage(image=draw_transform_img)
    sheared.noise.drawImage(image=transform_draw_img)
    np.testing.assert_array_almost_equal(draw_transform_img.array,
                                         transform_draw_img.array)

    transformed = orig.transform(*jac)
    transformed.drawImage(LSST_i)
    orig.noise.transform(*jac).drawImage(image=draw_transform_img)
    transformed.noise.drawImage(image=transform_draw_img)
    np.testing.assert_array_almost_equal(draw_transform_img.array,
                                         transform_draw_img.array)
Exemple #2
0
def test_fwhm():
    """Test the calculateFWHM method.
    """
    # Compare the calculation for a simple Gaussian.
    g1 = galsim.Gaussian(sigma=5, flux=1.7)

    print('g1 native fwhm = ', g1.fwhm)
    print('g1.calculateFWHM = ', g1.calculateFWHM())
    # These should be exactly equal.
    np.testing.assert_equal(
        g1.fwhm,
        g1.calculateFWHM(),
        err_msg="Gaussian.calculateFWHM() returned wrong value.")

    # Check for a convolution of two Gaussians.  Should be equivalent, but now will need to
    # do the calculation.
    g2 = galsim.Convolve(galsim.Gaussian(sigma=3, flux=1.3),
                         galsim.Gaussian(sigma=4, flux=23))
    test_fwhm = g2.calculateFWHM()
    print('g2.calculateFWHM = ', test_fwhm)
    print('ratio - 1 = ', test_fwhm / g1.fwhm - 1)
    np.testing.assert_almost_equal(
        test_fwhm / g1.fwhm,
        1.0,
        decimal=3,
        err_msg="Gaussian.calculateFWHM() is not accurate.")

    # The default scale already accurate to around 3 dp.  Using scale = 0.1 is accurate to 8 dp.
    test_fwhm = g2.calculateFWHM(scale=0.1)
    print('g2.calculateFWHM(scale=0.1) = ', test_fwhm)
    print('ratio - 1 = ', test_fwhm / g1.fwhm - 1)
    np.testing.assert_almost_equal(
        test_fwhm / g1.fwhm,
        1.0,
        decimal=8,
        err_msg="Gaussian.calculateFWHM(scale=0.1) is not accurate.")

    # Finally, we don't expect this to be accurate, but make sure the code can handle having
    # only the central pixel higher than half-maximum.
    test_fwhm = g2.calculateFWHM(scale=20)
    print('g2.calculateFWHM(scale=20) = ', test_fwhm)
    print('ratio - 1 = ', test_fwhm / g1.fwhm - 1)
    np.testing.assert_almost_equal(
        test_fwhm / g1.fwhm / 10,
        0.1,
        decimal=1,
        err_msg="Gaussian.calculateFWHM(scale=20) is not accurate.")

    # Next, use an Exponential profile
    e1 = galsim.Exponential(scale_radius=5, flux=1.7)

    # The true fwhm for this is analytic, but not an attribute.
    e1_fwhm = 2. * np.log(2.0) * e1.scale_radius
    print('true e1 fwhm = ', e1_fwhm)

    # Test with the default scale and size.
    test_fwhm = e1.calculateFWHM()
    print('e1.calculateFWHM = ', test_fwhm)
    print('ratio - 1 = ', test_fwhm / e1_fwhm - 1)
    np.testing.assert_almost_equal(
        test_fwhm / e1_fwhm,
        1.0,
        decimal=3,
        err_msg="Exponential.calculateFWHM() is not accurate.")

    # The default scale already accurate to around 3 dp.  Using scale = 0.1 is accurate to 7 dp.
    # We can also decrease the size, which should still be accurate, but maybe a little faster.
    # Go a bit more that fwhm in units of the pixels.
    size = int(1.2 * e1_fwhm / 0.1)
    test_fwhm = e1.calculateFWHM(scale=0.1, size=size)
    print('e1.calculateFWHM(scale=0.1) = ', test_fwhm)
    print('ratio - 1 = ', test_fwhm / e1_fwhm - 1)
    np.testing.assert_almost_equal(
        test_fwhm / e1_fwhm,
        1.0,
        decimal=7,
        err_msg="Exponential.calculateFWHM(scale=0.1) is not accurate.")

    # Check that it works if the centroid is not at the origin
    e3 = e1.shift(2, 3)
    test_fwhm = e3.calculateFWHM(scale=0.1)
    print('e3.calculateFWHM(scale=0.1) = ', test_fwhm)
    print('ratio - 1 = ', test_fwhm / e1_fwhm - 1)
    np.testing.assert_almost_equal(
        test_fwhm / e1_fwhm,
        1.0,
        decimal=6,
        err_msg="shifted Exponential FWHM is not accurate.")

    # Can set a centroid manually.  This should be equivalent to the default.
    print('e3.centroid = ', e3.centroid())
    test_fwhm = e3.calculateFWHM(scale=0.1, centroid=e3.centroid())
    np.testing.assert_almost_equal(
        test_fwhm / e1_fwhm,
        1.0,
        decimal=6,
        err_msg="shifted FWHM with explicit centroid is not accurate.")

    # Check the image version.
    im = e1.drawImage(scale=0.1, method='sb')
    test_fwhm = im.calculateFWHM(Imax=e1.xValue(0, 0))
    print('im.calculateFWHM() = ', test_fwhm)
    print('ratio - 1 = ', test_fwhm / e1_fwhm - 1)
    np.testing.assert_almost_equal(
        test_fwhm / e1_fwhm,
        1.0,
        decimal=6,
        err_msg="image.calculateFWHM is not accurate.")

    # Check that a non-square image works correctly.  Also, not centered anywhere in particular.
    bounds = galsim.BoundsI(-1234, -1234 + size * 2, 8234, 8234 + size)
    offset = galsim.PositionD(29, 1)
    im = e1.drawImage(scale=0.1, bounds=bounds, offset=offset, method='sb')
    test_fwhm = im.calculateFWHM(Imax=e1.xValue(0, 0),
                                 center=im.trueCenter() + offset)
    print('im.calculateFWHM() = ', test_fwhm)
    print('ratio - 1 = ', test_fwhm / e1_fwhm - 1)
    np.testing.assert_almost_equal(
        test_fwhm / e1_fwhm,
        1.0,
        decimal=6,
        err_msg="non-square image.calculateFWHM is not accurate.")
Exemple #3
0
def test_real_galaxy_ideal():
    """Test accuracy of various calculations with fake Gaussian RealGalaxy vs. ideal expectations"""
    import time
    t1 = time.time()
    # read in faked Gaussian RealGalaxy from file
    rgc = galsim.RealGalaxyCatalog(catalog_file, image_dir)
    rg = galsim.RealGalaxy(rgc, index=ind_fake)
    # as a side note, make sure it behaves okay given a legit RNG and a bad RNG
    # or when trying to specify the galaxy too many ways
    rg_1 = galsim.RealGalaxy(rgc, index=ind_fake, rng=galsim.BaseDeviate(1234))
    rg_2 = galsim.RealGalaxy(rgc, random=True)
    try:
        np.testing.assert_raises(TypeError,
                                 galsim.RealGalaxy,
                                 rgc,
                                 index=ind_fake,
                                 rng='foo')
        np.testing.assert_raises(AttributeError,
                                 galsim.RealGalaxy,
                                 rgc,
                                 index=ind_fake,
                                 id=0)
        np.testing.assert_raises(AttributeError,
                                 galsim.RealGalaxy,
                                 rgc,
                                 index=ind_fake,
                                 random=True)
        np.testing.assert_raises(AttributeError,
                                 galsim.RealGalaxy,
                                 rgc,
                                 id=0,
                                 random=True)
        np.testing.assert_raises(AttributeError, galsim.RealGalaxy, rgc)
    except ImportError:
        print 'The assert_raises tests require nose'

    do_pickle(
        rgc, lambda x: [
            x.getGal(ind_fake),
            x.getPSF(ind_fake),
            x.getNoiseProperties(ind_fake)
        ])
    do_pickle(
        rgc,
        lambda x: drawNoise(x.getNoise(ind_fake, rng=galsim.BaseDeviate(123))))
    do_pickle(rgc)
    do_pickle(
        rg, lambda x: [
            x.gal_image, x.psf_image,
            repr(x.noise), x.original_psf.flux, x.original_gal.flux, x.flux
        ])
    do_pickle(rg, lambda x: x.drawImage(nx=20, ny=20, scale=0.7))
    do_pickle(rg_1, lambda x: x.drawImage(nx=20, ny=20, scale=0.7))
    do_pickle(rg)
    do_pickle(rg_1)

    ## for the generation of the ideal right answer, we need to add the intrinsic shape of the
    ## galaxy and the lensing shear using the rule for addition of distortions which is ugly, but oh
    ## well:
    (d1, d2) = galsim.utilities.g1g2_to_e1e2(fake_gal_shear1, fake_gal_shear2)
    (d1app, d2app) = galsim.utilities.g1g2_to_e1e2(targ_applied_shear1,
                                                   targ_applied_shear2)
    denom = 1.0 + d1 * d1app + d2 * d2app
    dapp_sq = d1app**2 + d2app**2
    d1tot = (d1 + d1app + d2app / dapp_sq * (1.0 - np.sqrt(1.0 - dapp_sq)) *
             (d2 * d1app - d1 * d2app)) / denom
    d2tot = (d2 + d2app + d1app / dapp_sq * (1.0 - np.sqrt(1.0 - dapp_sq)) *
             (d1 * d2app - d2 * d1app)) / denom

    # convolve with a range of Gaussians, with and without shear (note, for this test all the
    # original and target ePSFs are Gaussian - there's no separate pixel response so that everything
    # can be calculated analytically)
    for tps in targ_pixel_scale:
        for tpf in targ_PSF_fwhm:
            for tps1 in targ_PSF_shear1:
                for tps2 in targ_PSF_shear2:
                    print 'tps,tpf,tps1,tps2 = ', tps, tpf, tps1, tps2
                    # make target PSF
                    targ_PSF = galsim.Gaussian(fwhm=tpf).shear(g1=tps1,
                                                               g2=tps2)
                    # simulate image
                    sim_image = galsim.simReal(rg,
                                               targ_PSF,
                                               tps,
                                               g1=targ_applied_shear1,
                                               g2=targ_applied_shear2,
                                               rand_rotate=False,
                                               target_flux=fake_gal_flux)
                    # galaxy sigma, in units of pixels on the final image
                    sigma_ideal = (fake_gal_fwhm / tps) * fwhm_to_sigma
                    # compute analytically the expected galaxy moments:
                    mxx_gal, myy_gal, mxy_gal = ellip_to_moments(
                        d1tot, d2tot, sigma_ideal)
                    # compute analytically the expected PSF moments:
                    targ_PSF_e1, targ_PSF_e2 = galsim.utilities.g1g2_to_e1e2(
                        tps1, tps2)
                    targ_PSF_sigma = (tpf / tps) * fwhm_to_sigma
                    mxx_PSF, myy_PSF, mxy_PSF = ellip_to_moments(
                        targ_PSF_e1, targ_PSF_e2, targ_PSF_sigma)
                    # get expected e1, e2, sigma for the PSF-convolved image
                    tot_e1, tot_e2, tot_sigma = moments_to_ellip(
                        mxx_gal + mxx_PSF, myy_gal + myy_PSF,
                        mxy_gal + mxy_PSF)

                    # compare with images that are expected
                    expected_gaussian = galsim.Gaussian(flux=fake_gal_flux,
                                                        sigma=tps * tot_sigma)
                    expected_gaussian = expected_gaussian.shear(e1=tot_e1,
                                                                e2=tot_e2)
                    expected_image = galsim.ImageD(sim_image.array.shape[0],
                                                   sim_image.array.shape[1])
                    expected_gaussian.drawImage(expected_image,
                                                scale=tps,
                                                method='no_pixel')
                    printval(expected_image, sim_image)
                    np.testing.assert_array_almost_equal(
                        sim_image.array,
                        expected_image.array,
                        decimal=3,
                        err_msg=
                        "Error in comparison of ideal Gaussian RealGalaxy calculations"
                    )

    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
Exemple #4
0
def test_scattered():
    """Test aspects of building an Scattered image
    """
    import copy
    import time
    t1 = time.time()

    # Name some variables to make it easier to be sure they are the same value in the config dict
    # as when we build the image manually.
    size = 48
    stamp_size = 20
    scale = 0.45
    flux = 17
    sigma = 0.7
    x1 = 23.1
    y1 = 27.3
    x2 = 13.4
    y2 = 31.9
    x3 = 39.8
    y3 = 19.7

    # This part of the config will be the same for all tests
    base_config = {'gal': {'type': 'Gaussian', 'sigma': sigma, 'flux': flux}}

    # Check that the stamps are centered at the correct location for both odd and even stamp size.
    base_config['image'] = {
        'type': 'Scattered',
        'size': size,
        'pixel_scale': scale,
        'stamp_size': stamp_size,
        'image_pos': {
            'type': 'XY',
            'x': x1,
            'y': y1
        },
        'nobjects': 1
    }
    for convention in [0, 1]:
        for test_stamp_size in [stamp_size, stamp_size + 1]:
            # Deep copy to make sure we don't have any "current_val" caches present.
            config = copy.deepcopy(base_config)
            config['image']['stamp_size'] = test_stamp_size
            config['image']['index_convention'] = convention

            image, _, _, _ = galsim.config.BuildImage(config)
            np.testing.assert_equal(image.getXMin(), convention)
            np.testing.assert_equal(image.getYMin(), convention)

            xgrid, ygrid = np.meshgrid(
                np.arange(size) + image.getXMin(),
                np.arange(size) + image.getYMin())
            obs_flux = np.sum(image.array)
            cenx = np.sum(xgrid * image.array) / flux
            ceny = np.sum(ygrid * image.array) / flux
            ixx = np.sum((xgrid - cenx)**2 * image.array) / flux
            ixy = np.sum((xgrid - cenx) * (ygrid - ceny) * image.array) / flux
            iyy = np.sum((ygrid - ceny)**2 * image.array) / flux
            np.testing.assert_almost_equal(obs_flux / flux, 1, decimal=3)
            np.testing.assert_almost_equal(cenx, x1, decimal=3)
            np.testing.assert_almost_equal(ceny, y1, decimal=3)
            np.testing.assert_almost_equal(ixx / (sigma / scale)**2,
                                           1,
                                           decimal=1)
            np.testing.assert_almost_equal(ixy, 0., decimal=3)
            np.testing.assert_almost_equal(iyy / (sigma / scale)**2,
                                           1,
                                           decimal=1)

    # Check that stamp_xsize, stamp_ysize, image_pos use the object count, rather than the
    # image count.
    config = copy.deepcopy(base_config)
    config['image'] = {
        'type': 'Scattered',
        'size': size,
        'pixel_scale': scale,
        'stamp_xsize': {
            'type': 'Sequence',
            'first': stamp_size
        },
        'stamp_ysize': {
            'type': 'Sequence',
            'first': stamp_size
        },
        'image_pos': {
            'type':
            'List',
            'items': [
                galsim.PositionD(x1, y1),
                galsim.PositionD(x2, y2),
                galsim.PositionD(x3, y3)
            ]
        },
        'nobjects': 3
    }

    image, _, _, _ = galsim.config.BuildImage(config)

    image2 = galsim.ImageF(size, size, scale=scale)
    image2.setZero()
    gal = galsim.Gaussian(sigma=sigma, flux=flux)

    for (i, x, y) in [(0, x1, y1), (1, x2, y2), (2, x3, y3)]:
        stamp = galsim.ImageF(stamp_size + i, stamp_size + i, scale=scale)
        if (stamp_size + i) % 2 == 0:
            x += 0.5
            y += 0.5
        ix = int(np.floor(x + 0.5))
        iy = int(np.floor(y + 0.5))
        stamp.setCenter(ix, iy)
        dx = x - ix
        dy = y - iy
        gal.drawImage(stamp, offset=(dx, dy))
        b = image2.bounds & stamp.bounds
        image2[b] += stamp[b]

    np.testing.assert_almost_equal(image.array, image2.array)

    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
Exemple #5
0
def test_sigma():
    """Test the calculateMomentRadius method.
    """
    # Compare the calculation for a simple Gaussian.
    g1 = galsim.Gaussian(sigma=5, flux=1.7)

    print('g1 native sigma = ', g1.sigma)
    print('g1.calculateMomentRadius = ', g1.calculateMomentRadius())
    # These should be exactly equal.
    np.testing.assert_equal(
        g1.sigma,
        g1.calculateMomentRadius(),
        err_msg="Gaussian.calculateMomentRadius() returned wrong value.")
    np.testing.assert_equal(
        g1.sigma,
        g1.calculateMomentRadius(rtype='trace'),
        err_msg="Gaussian.calculateMomentRadius(trace) returned wrong value.")
    np.testing.assert_equal(
        g1.sigma,
        g1.calculateMomentRadius(rtype='det'),
        err_msg="Gaussian.calculateMomentRadius(det) returned wrong value.")
    np.testing.assert_equal(
        (g1.sigma, g1.sigma),
        g1.calculateMomentRadius(rtype='both'),
        err_msg="Gaussian.calculateMomentRadius(both) returned wrong value.")

    # Check for a convolution of two Gaussians.  Should be equivalent, but now will need to
    # do the calculation.
    g2 = galsim.Convolve(galsim.Gaussian(sigma=3, flux=1.3),
                         galsim.Gaussian(sigma=4, flux=23))
    test_sigma = g2.calculateMomentRadius()
    print('g2.calculateMomentRadius = ', test_sigma)
    print('ratio - 1 = ', test_sigma / g1.sigma - 1)
    np.testing.assert_almost_equal(
        test_sigma / g1.sigma,
        1.0,
        decimal=1,
        err_msg="Gaussian.calculateMomentRadius() is not accurate.")

    # The default scale and size is only accurate to around 1 dp.  Using scale = 0.1 is accurate
    # to 4 dp.
    test_sigma = g2.calculateMomentRadius(scale=0.1)
    print('g2.calculateMomentRadius(scale=0.1) = ', test_sigma)
    print('ratio - 1 = ', test_sigma / g1.sigma - 1)
    np.testing.assert_almost_equal(
        test_sigma / g1.sigma,
        1.0,
        decimal=4,
        err_msg="Gaussian.calculateMomentRadius(scale=0.1) is not accurate.")

    # In this case, the different calculations are eqivalent:
    np.testing.assert_almost_equal(
        test_sigma,
        g2.calculateMomentRadius(scale=0.1, rtype='trace'),
        err_msg="Gaussian.calculateMomentRadius(trace) is not accurate.")
    np.testing.assert_almost_equal(
        test_sigma,
        g2.calculateMomentRadius(scale=0.1, rtype='det'),
        err_msg="Gaussian.calculateMomentRadius(trace) is not accurate.")
    np.testing.assert_almost_equal(
        (test_sigma, test_sigma),
        g2.calculateMomentRadius(scale=0.1, rtype='both'),
        err_msg="Gaussian.calculateMomentRadius(trace) is not accurate.")

    # However, when we shear it, the default (det) measure stays equal to the original sigma, but
    # the trace measure increases by a factor of (1-e^2)^0.25
    g3 = g2.shear(e1=0.4, e2=0.3)
    esq = 0.4**2 + 0.3**2
    sheared_sigma = g3.calculateMomentRadius(scale=0.1)
    print('g3.calculateMomentRadius(scale=0.1) = ', sheared_sigma)
    print('ratio - 1 = ', sheared_sigma / g1.sigma - 1)
    sheared_sigma2 = g3.calculateMomentRadius(scale=0.1, rtype='trace')
    print('g3.calculateMomentRadius(scale=0.1,trace) = ', sheared_sigma2)
    print('ratio = ', sheared_sigma2 / g1.sigma)
    print('(1-e^2)^-0.25 = ', (1 - esq)**-0.25)
    print('ratio - 1 = ', sheared_sigma2 / (g1.sigma * (1. - esq)**-0.25) - 1)
    np.testing.assert_almost_equal(
        sheared_sigma / g1.sigma,
        1.0,
        decimal=4,
        err_msg=
        "sheared Gaussian.calculateMomentRadius(scale=0.1) is not accurate.")
    np.testing.assert_almost_equal(
        sheared_sigma2 / (g1.sigma * (1. - esq)**-0.25),
        1.0,
        decimal=4,
        err_msg=
        "sheared Gaussian.calculateMomentRadius(scale=0.1,trace) is not accurate."
    )

    # Next, use an Exponential profile
    e1 = galsim.Exponential(scale_radius=5, flux=1.7)

    # The true "sigma" for this is analytic, but not an attribute.
    e1_sigma = np.sqrt(3.0) * e1.scale_radius
    print('true e1 sigma = sqrt(3) * e1.scale_radius = ', e1_sigma)

    # Test with the default scale and size.
    test_sigma = e1.calculateMomentRadius()
    print('e1.calculateMomentRadius = ', test_sigma)
    print('ratio - 1 = ', test_sigma / e1_sigma - 1)
    np.testing.assert_almost_equal(
        test_sigma / e1_sigma,
        1.0,
        decimal=1,
        err_msg="Exponential.calculateMomentRadius() is not accurate.")

    # The default scale and size is only accurate to around 1 dp.  This time we have to both
    # decrease the scale and also increase the size to get 4 dp of precision.
    test_sigma = e1.calculateMomentRadius(scale=0.1, size=2000)
    print('e1.calculateMomentRadius(scale=0.1) = ', test_sigma)
    print('ratio - 1 = ', test_sigma / e1_sigma - 1)
    np.testing.assert_almost_equal(
        test_sigma / e1_sigma,
        1.0,
        decimal=4,
        err_msg="Exponential.calculateMomentRadius(scale=0.1) is not accurate."
    )

    # Check that it works if the centroid is not at the origin
    e3 = e1.shift(2, 3)
    test_sigma = e3.calculateMomentRadius(scale=0.1, size=2000)
    print('e1.calculateMomentRadius(scale=0.1) = ', test_sigma)
    print('ratio - 1 = ', test_sigma / e1_sigma - 1)
    np.testing.assert_almost_equal(
        test_sigma / e1_sigma,
        1.0,
        decimal=4,
        err_msg="shifted Exponential MomentRadius is not accurate.")

    # Can set a centroid manually.  This should be equivalent to the default.
    print('e3.centroid = ', e3.centroid())
    test_sigma = e3.calculateMomentRadius(scale=0.1,
                                          size=2000,
                                          centroid=e3.centroid())
    np.testing.assert_almost_equal(
        test_sigma / e1_sigma,
        1.0,
        decimal=4,
        err_msg="shifted MomentRadius with explicit centroid is not accurate.")

    # Check the image version.
    size = 2000
    im = e1.drawImage(scale=0.1, nx=size, ny=size)
    test_sigma = im.calculateMomentRadius()
    print('im.calculateMomentRadius() = ', test_sigma)
    print('ratio - 1 = ', test_sigma / e1_sigma - 1)
    np.testing.assert_almost_equal(
        test_sigma / e1_sigma,
        1.0,
        decimal=4,
        err_msg="image.calculateMomentRadius is not accurate.")

    # Check that a non-square image works correctly.  Also, not centered anywhere in particular.
    bounds = galsim.BoundsI(-1234, -1234 + size * 2, 8234, 8234 + size)
    offset = galsim.PositionD(29, 1)
    im = e1.drawImage(scale=0.1, bounds=bounds, offset=offset)
    test_hlr = im.calculateMomentRadius(center=im.trueCenter() + offset)
    print('im.calculateMomentRadius() = ', test_sigma)
    print('ratio - 1 = ', test_sigma / e1_sigma - 1)
    np.testing.assert_almost_equal(
        test_sigma / e1_sigma,
        1.0,
        decimal=4,
        err_msg="non-square image.calculateMomentRadius is not accurate.")
nmult = 5  # i.e., consider 1*imsize linear scale, 2*imsize linear scale, up to (nmult-1)*imsize linear scale
psf_fwhm = 3.5
pixel_scale = 1.0
ntest = 200
sky_level = 1.0e6
gal_flux = 1.0e4
gal_e1 = 0.2
gal_e2 = 0.1
sn_noisy = 25.0  # desired S/N for noisy case
seed = 1234
save_im = 0

print "Doing all tests with ", ntest, " trials"

# for 48x48 image, Gaussian PSF with FWHM=2.5 pix and Gaussian galaxy with same size, make objects
epsf = galsim.Gaussian(fwhm=psf_fwhm)  # let's say this is the epsf
gal = galsim.Gaussian(fwhm=psf_fwhm, flux=gal_flux)
exp_sigma = math.sqrt(2.) * gal.getSigma()
gal_hlr = gal.getHalfLightRadius()
gal.applyShear(e1=gal_e1, e2=gal_e2)
obj = galsim.Convolve(gal, epsf)

# do tests with noiseless images of various sizes
for sizemult in range(1, nmult):
    this_imsize = imsize * sizemult
    im_obj = galsim.ImageF(this_imsize, this_imsize)
    im_epsf = galsim.ImageF(this_imsize, this_imsize)
    im_obj = obj.draw(image=im_obj, dx=pixel_scale)
    im_epsf = epsf.draw(image=im_epsf, dx=pixel_scale)
    if save_im:
        im_obj.write('im_obj.fits')
Exemple #7
0
def test_gaussian():
    """Test the generation of a specific Gaussian profile against a known result.
    """
    savedImg = galsim.fits.read(os.path.join(imgdir, "gauss_1.fits"))
    savedImg.setCenter(0, 0)
    dx = 0.2
    myImg = galsim.ImageF(savedImg.bounds, scale=dx)
    myImg.setCenter(0, 0)

    gauss = galsim.Gaussian(flux=1, sigma=1)
    # Reference images were made with old centering, which is equivalent to use_true_center=False.
    myImg = gauss.drawImage(myImg,
                            scale=dx,
                            method="sb",
                            use_true_center=False)
    np.testing.assert_array_almost_equal(
        myImg.array,
        savedImg.array,
        5,
        err_msg="Using GSObject Gaussian disagrees with expected result")
    np.testing.assert_almost_equal(
        myImg.array.sum(dtype=float) * dx**2,
        myImg.added_flux,
        5,
        err_msg="Gaussian profile GSObject::draw returned wrong added_flux")

    # Check a non-square image
    print(myImg.bounds)
    recImg = galsim.ImageF(45, 66)
    recImg.setCenter(0, 0)
    recImg = gauss.drawImage(recImg,
                             scale=dx,
                             method="sb",
                             use_true_center=False)
    np.testing.assert_array_almost_equal(
        recImg[savedImg.bounds].array,
        savedImg.array,
        5,
        err_msg=
        "Drawing Gaussian on non-square image disagrees with expected result")
    np.testing.assert_almost_equal(
        recImg.array.sum(dtype=float) * dx**2,
        recImg.added_flux,
        5,
        err_msg=
        "Gaussian profile GSObject::draw on non-square image returned wrong added_flux"
    )

    # Check with default_params
    gauss = galsim.Gaussian(flux=1, sigma=1, gsparams=default_params)
    gauss.drawImage(myImg, scale=0.2, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
        myImg.array,
        savedImg.array,
        5,
        err_msg=
        "Using GSObject Gaussian with default_params disagrees with expected result"
    )
    gauss = galsim.Gaussian(flux=1, sigma=1, gsparams=galsim.GSParams())
    gauss.drawImage(myImg, scale=0.2, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
        myImg.array,
        savedImg.array,
        5,
        err_msg=
        "Using GSObject Gaussian with GSParams() disagrees with expected result"
    )

    # Use non-unity values.
    gauss = galsim.Gaussian(flux=1.7, sigma=2.3)
    check_basic(gauss, "Gaussian")

    # Test photon shooting.
    do_shoot(gauss, myImg, "Gaussian")

    # Test kvalues
    do_kvalue(gauss, myImg, "Gaussian")

    # Check picklability
    do_pickle(galsim.GSParams())  # Check GSParams explicitly here too.
    do_pickle(
        galsim.GSParams(minimum_fft_size=12,
                        maximum_fft_size=40,
                        folding_threshold=1.e-1,
                        maxk_threshold=2.e-1,
                        kvalue_accuracy=3.e-1,
                        xvalue_accuracy=4.e-1,
                        shoot_accuracy=5.e-1,
                        realspace_relerr=6.e-1,
                        realspace_abserr=7.e-1,
                        integration_relerr=8.e-1,
                        integration_abserr=9.e-1))
    do_pickle(gauss, lambda x: x.drawImage(method='no_pixel'))
    do_pickle(gauss)

    # Should raise an exception if >=2 radii are provided.
    assert_raises(TypeError,
                  galsim.Gaussian,
                  sigma=3,
                  half_light_radius=1,
                  fwhm=2)
    assert_raises(TypeError, galsim.Gaussian, half_light_radius=1, fwhm=2)
    assert_raises(TypeError, galsim.Gaussian, sigma=3, fwhm=2)
    assert_raises(TypeError, galsim.Gaussian, sigma=3, half_light_radius=1)
    # Or none.
    assert_raises(TypeError, galsim.Gaussian)

    # Finally, test the noise property for things that don't have any noise set.
    assert gauss.noise is None
    # And accessing the attribute from the class should indicate that it is a lazyproperty
    assert 'lazy_property' in str(galsim.GSObject._noise)

    # And check that trying to use GSObject directly is an error.
    assert_raises(NotImplementedError, galsim.GSObject)
Exemple #8
0
e20 = numpy.random.normal(loc=0.0, scale=0.3, size=n)
g10 = numpy.random.uniform(-0.01, 0.01, m)
g20 = numpy.random.uniform(-0.01, 0.01, m)
for i in range(n):
    e20[i] = 0
    if ((e10[i]**2) + (e20[i]**2) > 0.64):
        e10[i] = 0.0
        e20[i] = 0.0

gsparams=galsim.GSParams(folding_threshold=1.e-2,maxk_threshold=2.e-3,\
                         xvalue_accuracy=1.e-4,kvalue_accuracy=1.e-4,\
                         shoot_accuracy=1.e-4,minimum_fft_size=64)

#psf1=galsim.Moffat(fwhm=psf_fwhm[0],beta=2.5,gsparams=gsparams) #to be continue
#psf1=psf1.shear(e1=e1_psf,e2=e2_psf)
psf1 = galsim.Gaussian(fwhm=psf_fwhm[0], gsparams=gsparams)

#psf1=galsim.Kolmogorov(fwhm=psf_fwhm[0],gsparams=gsparams)
gal1 = galsim.Gaussian(half_light_radius=2, gsparams=gsparams)
gal2 = galsim.Exponential(half_light_radius=1, gsparams=gsparams)
gal3 = galsim.DeVaucouleurs(half_light_radius=1, gsparams=gsparams)
gal4 = galsim.Sersic(half_light_radius=1, n=2.5,
                     gsparams=gsparams)  #to be continue

psf = psf1
final_epsf_image = psf1.drawImage(scale=pixel_scale)
gal = [gal1, gal2, gal3, gal4]

BJe1 = [[], [], [], []]
BJe2 = [[], [], [], []]
RGe1 = [[], [], [], []]
Exemple #9
0
def test_interleaveImages():
    import time
    t1 = time.time()

    # 1a) With galsim Gaussian
    g = galsim.Gaussian(sigma=3.7, flux=1000.)
    gal = galsim.Convolve([g, galsim.Pixel(1.0)])
    im_list = []
    offset_list = []
    n = 2
    for j in xrange(n):
        for i in xrange(n):
            im = galsim.Image(16 * n, 16 * n)
            offset = galsim.PositionD(-(i + 0.5) / n + 0.5,
                                      -(j + 0.5) / n + 0.5)
            offset_list.append(offset)
            gal.drawImage(image=im,
                          method='no_pixel',
                          offset=offset,
                          scale=0.5)
            im_list.append(im)

    scale = im.scale

    # Input to N as an int
    img = galsim.utilities.interleaveImages(im_list, n, offsets=offset_list)
    im = galsim.Image(16 * n * n, 16 * n * n)
    g = galsim.Gaussian(sigma=3.7, flux=1000. * n * n)
    gal = galsim.Convolve([g, galsim.Pixel(1.0)])
    gal.drawImage(image=im,
                  method='no_pixel',
                  offset=galsim.PositionD(0.0, 0.0),
                  scale=1. * scale / n)
    np.testing.assert_array_equal(img.array,im.array,\
        err_msg="Interleaved Gaussian images do not match")

    assert im.wcs == img.wcs

    # 1b) With im_list and offsets permuted
    offset_list = []
    # An elegant way of generating the default offsets
    DX = np.arange(0.0, -1.0, -1.0 / n)
    DX -= DX.mean()
    DY = DX
    for dy in DY:
        for dx in DX:
            offset = galsim.PositionD(dx, dy)
            offset_list.append(offset)

    np.random.seed(42)  # for generating the same random permutation everytime
    rand_idx = np.random.permutation(len(offset_list))
    im_list_randperm = [im_list[idx] for idx in rand_idx]
    offset_list_randperm = [offset_list[idx] for idx in rand_idx]
    # Input to N as a tuple
    img_randperm = galsim.utilities.interleaveImages(
        im_list_randperm, (n, n), offsets=offset_list_randperm)

    np.testing.assert_array_equal(img_randperm.array,img.array,\
        err_msg="Interleaved images do not match when 'offsets' is supplied")
    assert img_randperm.scale == img.scale

    # 1c) Catching errors in offsets
    offset_list = []
    im_list = []
    n = 5
    # Generate approximate offsets
    DX = np.array([-0.67, -0.33, 0., 0.33, 0.67])
    DY = DX
    for dy in DY:
        for dx in DX:
            offset = galsim.PositionD(dx, dy)
            offset_list.append(offset)
            im = galsim.Image(16, 16)
            gal.drawImage(image=im, offset=offset, method='no_pixel')
            im_list.append(im)

    try:
        N = (n, n)
        np.testing.assert_raises(ValueError, galsim.utilities.interleaveImages,
                                 im_list, N, offset_list)
    except ImportError:
        print "The assert_raises tests require nose"

    offset_list = []
    im_list = []
    n = 5
    DX = np.arange(0., 1., 1. / n)
    DY = DX
    for dy in DY:
        for dx in DX:
            offset = galsim.PositionD(dx, dy)
            offset_list.append(offset)
            im = galsim.Image(16, 16)
            gal.drawImage(image=im, offset=offset, method='no_pixel')
            im_list.append(im)

    try:
        N = (n, n)
        np.testing.assert_raises(ValueError, galsim.utilities.interleaveImages,
                                 im_list, N, offset_list)
    except ImportError:
        print "The assert_raises tests require nose"

    # 2a) Increase resolution along one direction - square to rectangular images
    n = 2
    g = galsim.Gaussian(sigma=3.7, flux=100.)
    gal1 = g.shear(g=1. * (n**2 - 1) / (n**2 + 1), beta=0.0 * galsim.radians)
    im_list = []
    offset_list = []

    # Generating offsets in a natural way
    DY = np.arange(0.0, 1.0, 1.0 / (n * n))
    DY -= DY.mean()
    for dy in DY:
        im = galsim.Image(16, 16)
        offset = galsim.PositionD(0.0, dy)
        offset_list.append(offset)
        gal1.drawImage(im, offset=offset, method='no_pixel', scale=2.0)
        im_list.append(im)

    img = galsim.utilities.interleaveImages(im_list,
                                            N=[1, n**2],
                                            offsets=offset_list,
                                            add_flux=False,
                                            suppress_warnings=True)
    im = galsim.Image(16, 16 * n * n)
    # The interleaved image has the total flux averaged out since `add_flux = False'
    gal = galsim.Gaussian(sigma=3.7 * n, flux=100.)
    gal.drawImage(image=im, method='no_pixel', scale=2.0)

    np.testing.assert_array_equal(
        im.array,
        img.array,
        err_msg="Sheared gaussian not interleaved correctly")
    assert img.wcs == galsim.JacobianWCS(2.0, 0.0, 0.0, 2. / (n**2))

    # 2b) Increase resolution along one direction - rectangular to square images
    n = 2
    g = galsim.Gaussian(sigma=3.7, flux=100.)
    gal2 = g.shear(g=1. * (n**2 - 1) / (n**2 + 1), beta=90. * galsim.degrees)
    im_list = []
    offset_list = []

    # Generating offsets in a natural way
    DX = np.arange(0.0, 1.0, 1.0 / n**2)
    DX -= DX.mean()
    for dx in DX:
        offset = galsim.PositionD(dx, 0.0)
        offset_list.append(offset)
        im = galsim.Image(16, 16 * n * n)
        gal2.drawImage(im, offset=offset, method='no_pixel', scale=3.0)
        im_list.append(im)

    img = galsim.utilities.interleaveImages(im_list,
                                            N=np.array([n**2, 1]),
                                            offsets=offset_list,
                                            suppress_warnings=True)
    im = galsim.Image(16 * n * n, 16 * n * n)
    gal = galsim.Gaussian(sigma=3.7, flux=100. * n * n)
    scale = im_list[0].scale
    gal.drawImage(image=im, scale=1. * scale / n, method='no_pixel')

    np.testing.assert_array_equal(
        im.array,
        img.array,
        err_msg="Sheared gaussian not interleaved correctly")
    assert img.wcs == galsim.JacobianWCS(1. * scale / n**2, 0.0, 0.0, scale)

    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
Exemple #10
0
def main(argv):
    """
    Make a fits image cube using real COSMOS galaxies from a catalog describing the training
    sample.

      - The number of images in the cube matches the number of rows in the catalog.
      - Each image size is computed automatically by GalSim based on the Nyquist size.
      - Both galaxies and stars.
      - PSF is a double Gaussian, the same for each galaxy.
      - Galaxies are randomly rotated to remove the imprint of any lensing shears in the COSMOS
        data.
      - The same shear is applied to each galaxy.
      - Noise is Poisson using a nominal sky value of 1.e6 ADU/arcsec^2,
        the noise in the original COSMOS data.
    """
    logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout)
    logger = logging.getLogger("demo6")

    # Define some parameters we'll use below.

    cat_file_name = 'real_galaxy_catalog_23.5_example.fits'
    dir = 'data'
    # Make output directory if not already present.
    if not os.path.isdir('output'):
        os.mkdir('output')
    cube_file_name = os.path.join('output','cube_real.fits')
    psf_file_name = os.path.join('output','psf_real.fits')

    random_seed = 1512413
    sky_level = 1.e6        # ADU / arcsec^2
    pixel_scale = 0.16      # arcsec
    gal_flux = 1.e5         # arbitrary choice, makes nice (not too) noisy images
    gal_g1 = -0.027         #
    gal_g2 = 0.031          #
    gal_mu = 1.082          # mu = ( (1-kappa)^2 - g1^2 - g2^2 )^-1
    psf_inner_fwhm = 0.6    # arcsec
    psf_outer_fwhm = 2.3    # arcsec
    psf_inner_fraction = 0.8  # fraction of total PSF flux in the inner Gaussian
    psf_outer_fraction = 0.2  # fraction of total PSF flux in the inner Gaussian
    ngal = 100  

    logger.info('Starting demo script 6 using:')
    logger.info('    - real galaxies from catalog %r',cat_file_name)
    logger.info('    - double Gaussian PSF')
    logger.info('    - pixel scale = %.2f',pixel_scale)
    logger.info('    - Applied gravitational shear = (%.3f,%.3f)',gal_g1,gal_g2)
    logger.info('    - Poisson noise (sky level = %.1e).', sky_level)
    
    # Read in galaxy catalog
    # Note: dir is the directory both for the catalog itself and also the directory prefix
    # for the image files listed in the catalog.
    # If the images are in a different directory, you may also specify image_dir, which gives
    # the relative path from dir to wherever the images are located.
    real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=dir)
    logger.info('Read in %d real galaxies from catalog', real_galaxy_catalog.nobjects)

    # Make the double Gaussian PSF
    psf1 = galsim.Gaussian(fwhm = psf_inner_fwhm, flux = psf_inner_fraction)
    psf2 = galsim.Gaussian(fwhm = psf_outer_fwhm, flux = psf_outer_fraction)
    psf = psf1+psf2
    # Draw the PSF with no noise.
    psf_image = psf.drawImage(scale = pixel_scale)
    # write to file
    psf_image.write(psf_file_name)
    logger.info('Created PSF and wrote to file %r',psf_file_name)

    # Build the images
    all_images = []
    for k in range(ngal):
        logger.debug('Start work on image %d',k)
        t1 = time.time()

        # Initialize the random number generator we will be using.
        rng = galsim.UniformDeviate(random_seed+k+1)

        gal = galsim.RealGalaxy(real_galaxy_catalog, index = k)
        logger.debug('   Read in training sample galaxy and PSF from file')
        t2 = time.time()

        # Set the flux
        gal = gal.withFlux(gal_flux)

        # Rotate by a random angle
        theta = 2.*math.pi * rng() * galsim.radians
        gal = gal.rotate(theta)

        # Apply the desired shear
        gal = gal.shear(g1=gal_g1, g2=gal_g2)

        # Also apply a magnification mu = ( (1-kappa)^2 - |gamma|^2 )^-1
        # This conserves surface brightness, so it scales both the area and flux.
        gal = gal.magnify(gal_mu)
        
        # Make the combined profile
        final = galsim.Convolve([psf, gal])

        # Offset by up to 1/2 pixel in each direction
        # We had previously (in demo4 and demo5) used shift(dx,dy) as a way to shift the center of 
        # the image.  Since that is applied to the galaxy, the units are arcsec (since the galaxy 
        # profile itself doesn't know about the pixel scale).  Here, the offset applies to the 
        # drawn image, which does know about the pixel scale, so the units of offset are pixels, 
        # not arcsec.  Here, we apply an offset of up to half a pixel in each direction.
        dx = rng() - 0.5
        dy = rng() - 0.5

        # Draw the profile
        if k == 0:
            # Note that the offset argument may be a galsim.PositionD object or a tuple (dx,dy).
            im = final.drawImage(scale=pixel_scale, offset=(dx,dy))
            xsize, ysize = im.array.shape
        else:
            im = galsim.ImageF(xsize,ysize)
            final.drawImage(im, scale=pixel_scale, offset=(dx,dy))

        logger.debug('   Drew image')
        t3 = time.time()

        # Add a constant background level
        background = sky_level * pixel_scale**2
        im += background

        # Add Poisson noise.  This time, we don't give a sky_level, since we have already
        # added it to the image, so we don't want any more added.  The sky_level parameter
        # really defines how much _extra_ sky should be added above what is already in the image.
        im.addNoise(galsim.PoissonNoise(rng)) 

        logger.debug('   Added Poisson noise')
        t4 = time.time()

        # Store that into the list of all images
        all_images += [im]
        t5 = time.time()

        logger.debug('   Times: %f, %f, %f, %f',t2-t1, t3-t2, t4-t3, t5-t4)
        logger.info('Image %d: size = %d x %d, total time = %f sec', k, xsize, ysize, t5-t1)

    logger.info('Done making images of galaxies')

    # Now write the image to disk.
    # We write the images to a fits data cube.
    galsim.fits.writeCube(all_images, cube_file_name)
    logger.info('Wrote image to fits data cube %r',cube_file_name)
def test_deltaFunction():
    """Test the generation of a Delta function profile
    """
    # Check construction with no arguments gives expected result
    delta = galsim.DeltaFunction()
    np.testing.assert_almost_equal(delta.flux, 1.0)
    check_basic(delta, "DeltaFunction")
    do_pickle(delta)

    # Check with default_params
    delta = galsim.DeltaFunction(flux=1, gsparams=default_params)
    np.testing.assert_almost_equal(delta.flux, 1.0)

    test_flux = 17.9
    delta = galsim.DeltaFunction(flux=test_flux)
    np.testing.assert_almost_equal(delta.flux, test_flux)
    check_basic(delta, "DeltaFunction")
    do_pickle(delta)

    gsp = galsim.GSParams(xvalue_accuracy=1.e-8, kvalue_accuracy=1.e-8)
    delta2 = galsim.DeltaFunction(flux=test_flux, gsparams=gsp)
    assert delta2 != delta
    assert delta2 == delta.withGSParams(gsp)

    # Test operations with no-ops on DeltaFunction
    delta_shr = delta.shear(g1=0.3, g2=0.1)
    np.testing.assert_almost_equal(delta_shr.flux, test_flux)

    delta_dil = delta.dilate(2.0)
    np.testing.assert_almost_equal(delta_dil.flux, test_flux)

    delta_rot = delta.rotate(45 * galsim.radians)
    np.testing.assert_almost_equal(delta_rot.flux, test_flux)

    delta_tfm = delta.transform(dudx=1.25, dudy=0., dvdx=0., dvdy=0.8)
    np.testing.assert_almost_equal(delta_tfm.flux, test_flux)

    delta_shift = delta.shift(1.,2.)
    np.testing.assert_almost_equal(delta_shift.flux, test_flux)

    # These aren't no ops, since they do in fact alter the flux.
    delta_exp = delta.expand(2.0)
    np.testing.assert_almost_equal(delta_exp.flux, test_flux * 4)

    delta_mag = delta.magnify(2.0)
    np.testing.assert_almost_equal(delta_mag.flux, test_flux * 2)

    delta_tfm = delta.transform(dudx=1.4, dudy=0.2, dvdx=0.4, dvdy=1.2)
    np.testing.assert_almost_equal(delta_tfm.flux, test_flux * (1.4*1.2-0.2*0.4))

    # Test simple translation of DeltaFunction
    delta2 = delta.shift(1.,2.)
    offcen = galsim.PositionD(1, 2)
    np.testing.assert_equal(delta2.centroid, offcen)
    assert delta2.xValue(offcen) > 1.e10
    np.testing.assert_almost_equal(delta2.xValue(galsim.PositionD(0,0)), 0)

    # Test photon shooting.
    gauss = galsim.Gaussian(sigma = 1.0)
    delta_conv = galsim.Convolve(gauss,delta)
    myImg = galsim.ImageF()
    do_shoot(delta_conv,myImg,"Delta Function")

    # Test kvalues
    do_kvalue(delta_conv,myImg,"Delta Function")
Exemple #12
0
def main(argv):
    """
    About as simple as it gets:
      - Use a circular Gaussian profile for the galaxy.
      - Convolve it by a circular Gaussian PSF.
      - Add Gaussian noise to the image.
    """
    # In non-script code, use getLogger(__name__) at module scope instead.
    logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout)
    logger = logging.getLogger("demo1") 

    gal_flux = 1.e5    # total counts on the image
    gal_sigma = 2.     # arcsec
    psf_sigma = 1.     # arcsec
    pixel_scale = 0.2  # arcsec / pixel
    noise = 30.        # standard deviation of the counts in each pixel

    logger.info('Starting demo script 1 using:')
    logger.info('    - circular Gaussian galaxy (flux = %.1e, sigma = %.1f),',gal_flux,gal_sigma)
    logger.info('    - circular Gaussian PSF (sigma = %.1f),',psf_sigma)
    logger.info('    - pixel scale = %.2f,',pixel_scale)
    logger.info('    - Gaussian noise (sigma = %.2f).',noise)

    # Define the galaxy profile
    gal = galsim.Gaussian(flux=gal_flux, sigma=gal_sigma)
    logger.debug('Made galaxy profile')

    # Define the PSF profile
    psf = galsim.Gaussian(flux=1., sigma=psf_sigma) # PSF flux should always = 1
    logger.debug('Made PSF profile')

    # Final profile is the convolution of these
    # Can include any number of things in the list, all of which are convolved 
    # together to make the final flux profile.
    final = galsim.Convolve([gal, psf])
    logger.debug('Convolved components into final profile')

    # Draw the image with a particular pixel scale, given in arcsec/pixel.
    # The returned image has a member, added_flux, which is gives the total flux actually added to 
    # the image.  One could use this value to check if the image is large enough for some desired
    # accuracy level.  Here, we just ignore it.
    image = final.drawImage(scale=pixel_scale)
    logger.debug('Made image of the profile: flux = %f, added_flux = %f',gal_flux,image.added_flux)

    # Add Gaussian noise to the image with specified sigma
    image.addNoise(galsim.GaussianNoise(sigma=noise))
    logger.debug('Added Gaussian noise')

    # Write the image to a file
    if not os.path.isdir('output'):
        os.mkdir('output')
    file_name = os.path.join('output','demo1.fits')
    # Note: if the file already exists, this will overwrite it.
    image.write(file_name)
    logger.info('Wrote image to %r' % file_name)  # using %r adds quotes around filename for us

    results = image.FindAdaptiveMom()

    logger.info('HSM reports that the image has observed shape and size:')
    logger.info('    e1 = %.3f, e2 = %.3f, sigma = %.3f (pixels)', results.observed_shape.e1,
                results.observed_shape.e2, results.moments_sigma)
    logger.info('Expected values in the limit that pixel response and noise are negligible:')
    logger.info('    e1 = %.3f, e2 = %.3f, sigma = %.3f', 0.0, 0.0, 
                math.sqrt(gal_sigma**2 + psf_sigma**2)/pixel_scale) 
def compute_val(x0s, hlr1, hlr2, flux1, flux2, e1, e2, plot_last_gal=False):
    """Computes blending parametrs for pair of blended input galaxy
    parametrs"""
    var = 1e-10
    nx, ny = 161, 161
    y0 = 0
    noise = galsim.GaussianNoise(sigma=var**0.5)
    psf = galsim.Gaussian(fwhm=0.7)
    psf_im = psf.drawImage(scale=0.2, nx=19, ny=19)
    psf_array = psf_im.array
    r = np.ones([len(x0s), 2]) * -1
    unit_dist = np.ones(len(x0s)) * -1
    num_detections = np.zeros(len(x0s))
    flux_b_on = np.zeros([len(x0s), 2])
    ret = []
    sig = [0, 0]
    for i, x0 in enumerate(x0s):
        im1_b_off = gal_im(hlr1, flux1, e1, 0, y0, psf, nx, ny)
        im2_b_off = gal_im(hlr2, flux2, e2, x0, y0, psf, nx, ny)
        im_b_on = im1_b_off + im2_b_off
        variance_array1 = get_var_arr(im1_b_off, noise, snr=500)
        variance_array2 = get_var_arr(im2_b_off, noise, snr=500)
        variance_array = get_var_arr(im_b_on, noise, snr=500)
        catalog1, hfpt1 = deblend_run(im1_b_off.array, variance_array1,
                                      psf_array)
        catalog2, hfpt2 = deblend_run(im2_b_off.array, variance_array2,
                                      psf_array)
        catalog, hfpt_blended = deblend_run(im_b_on.array, variance_array,
                                            psf_array)
        children = catalog[catalog['deblend_nChild'] == 0]
        if (len(catalog1) != 1) or (len(catalog2) != 1):
            print "multi detection in blending off: SKIPPED "
            continue
        elif len(children) == 0:
            print "No children in blending on"
            continue
        elif len(children) > 2:
            print "More than 2 children in blending on"
            continue
        elif np.isnan(children['base_SdssShape_flux']).any():
            print "Nan value found"
            continue
        tree = spatial.KDTree(zip([nx / 2, nx / 2 + x0],
                                  [ny / 2, ny / 2 + y0]))
        z = zip(children['base_SdssCentroid_x'],
                children['base_SdssCentroid_y'])
        match = tree.query(z, distance_upper_bound=15)
        select = ~np.isinf(match[0])
        if len(select) == 0:
            print "detected center do not match true object"
            continue
        flux_b_on[i][match[1][select]] = children['base_SdssShape_flux']
        # All good! Now procced with measurement
        num_detections[i] = len(children)
        ret.append(i)

        sig[0] = get_sig_m(catalog1['base_SdssShape_xx'],
                           catalog1['base_SdssShape_yy'],
                           catalog1['base_SdssShape_xy'])
        sig[1] = get_sig_m(catalog2['base_SdssShape_xx'],
                           catalog2['base_SdssShape_yy'],
                           catalog2['base_SdssShape_xy'])
        r[i][0] = met_rho(hfpt1[0], hfpt2)
        r[i][1] = met_rho(hfpt2[0], hfpt1)
        # import ipdb;ipdb.set_trace()
        if len(children) == 1:
            sig0 = get_sig_m(children['base_SdssShape_xx'],
                             children['base_SdssShape_yy'],
                             children['base_SdssShape_xy'])
            unit_dist[i] = sig0 + sig[(match[1][select] + 1) % 2]
        else:
            sigs = get_sig_m(children['base_SdssShape_xx'],
                             children['base_SdssShape_yy'],
                             children['base_SdssShape_xy'])
            unit_dist[i] = sigs.sum()
    s = np.array([x0s / unit_dist] * 2).T
    # Make sure galaxy detected most is first in output array
    q = np.where(flux_b_on == 0)
    amb = mode(q[1]).mode[0]
    rho = np.array([r.T[(amb + 1) % 2], r.T[amb]]).T
    flux_out = np.array([flux_b_on.T[(amb + 1) % 2], flux_b_on.T[amb]]).T
    met = [rho[ret], s[ret]]
    if plot_last_gal is True:
        plot_gal(im1_b_off, im2_b_off, im_b_on, psf_im)
    return met, num_detections[ret], flux_out[ret], amb
Exemple #14
0
def test_randwalk_defaults():
    """
    Create a random walk galaxy and test that the getters work for
    default inputs
    """

    # try constructing with mostly defaults
    npoints = 100
    hlr = 8.0
    rng = galsim.BaseDeviate(1234)
    rw = check_dep(galsim.RandomWalk, npoints, half_light_radius=hlr, rng=rng)

    assert rw.npoints == npoints, "expected npoints==%d, got %d" % (npoints,
                                                                    rw.npoints)
    assert rw.input_half_light_radius==hlr,\
        "expected hlr==%g, got %g" % (hlr, rw.input_half_light_radius)

    nobj = len(rw.points)
    assert nobj == npoints, "expected %d objects, got %d" % (npoints, nobj)

    pts = rw.points
    assert pts.shape == (
        npoints,
        2), "expected (%d,2) shape for points, got %s" % (npoints, pts.shape)
    np.testing.assert_almost_equal(rw.centroid.x, np.mean(pts[:, 0]))
    np.testing.assert_almost_equal(rw.centroid.y, np.mean(pts[:, 1]))

    gsp = galsim.GSParams(xvalue_accuracy=1.e-8, kvalue_accuracy=1.e-8)
    rng2 = galsim.BaseDeviate(1234)
    rw2 = check_dep(galsim.RandomWalk,
                    npoints,
                    half_light_radius=hlr,
                    rng=rng2,
                    gsparams=gsp)
    assert rw2 != rw
    assert rw2 == rw.withGSParams(gsp)

    # Check that they produce identical images.
    psf = galsim.Gaussian(sigma=0.8)
    conv1 = galsim.Convolve(rw.withGSParams(gsp), psf)
    conv2 = galsim.Convolve(rw2, psf)
    im1 = conv1.drawImage()
    im2 = conv2.drawImage()
    assert im1 == im2

    # Check that image is not sensitive to use of rng by other objects.
    rng3 = galsim.BaseDeviate(1234)
    rw3 = check_dep(galsim.RandomWalk,
                    npoints,
                    half_light_radius=hlr,
                    rng=rng3)
    rng3.discard(523)
    conv1 = galsim.Convolve(rw, psf)
    conv3 = galsim.Convolve(rw3, psf)
    im1 = conv1.drawImage()
    im3 = conv2.drawImage()
    assert im1 == im3

    # Run some basic tests of correctness
    check_basic(conv1, "RandomWalk")
    im = galsim.ImageD(64, 64, scale=0.5)
    do_shoot(conv1, im, "RandomWalk")
    do_kvalue(conv1, im, "RandomWalk")
    do_pickle(rw)
    do_pickle(conv1)
    do_pickle(conv1, lambda x: x.drawImage(scale=1))
Exemple #15
0
def main(argv):
    """
    Make a fits image cube where each frame has two images of the same galaxy drawn 
    with regular FFT convolution and with photon shooting.

    We do this for 5 different PSFs and 5 different galaxies, each with 4 different (random)
    fluxes, sizes, and shapes.
    """
    logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout)
    logger = logging.getLogger("demo7")

    # To turn off logging:
    #logger.propagate = False

    # Define some parameters we'll use below.

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

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

    random_seed = 553728
    sky_level = 1.e4        # ADU / arcsec^2
    pixel_scale = 0.28      # arcsec
    nx = 64
    ny = 64

    gal_flux_min = 1.e4     # Range for galaxy flux
    gal_flux_max = 1.e5  
    gal_hlr_min = 0.3       # arcsec
    gal_hlr_max = 1.3       # arcsec
    gal_e_min = 0.          # Range for ellipticity
    gal_e_max = 0.8

    psf_fwhm = 0.65         # arcsec

    # This script is set up as a comparison between using FFTs for doing the convolutions and
    # shooting photons.  The two methods have trade-offs in speed and accuracy which vary
    # with the kind of profile being drawn and the S/N of the object, among other factors.
    # In addition, for each method, there are a number of parameters GalSim uses that control 
    # aspects of the calculation that further affect the speed and accuracy.  
    #
    # We encapsulate these parameters with an object called GSParams.  The default values
    # are intended to be accurate enough for normal precision shear tests, without sacrificing
    # too much speed.  
    #
    # Any PSF or galaxy object can be given a gsparams argument on construction that can 
    # have different values to make the calculation more or less accurate (typically trading
    # off for speed or memory).  
    #
    # In this script, we adjust some of the values slightly, just to show you how it works.
    # You could play around with these values and see what effect they have on the drawn images.
    # Usually, it requires a pretty drastic change in these parameters for you to be able to 
    # notice the difference by eye.  But subtle effects that may impact the shapes of galaxies
    # can happen well before then.

    # Type help(galsim.GSParams) for the complete list of parameters and more detailed
    # documentation, including the default values for each parameter.
    gsparams = galsim.GSParams(
        folding_threshold=1.e-2, # maximum fractional flux that may be folded around edge of FFT
        maxk_threshold=2.e-3,    # k-values less than this may be excluded off edge of FFT
        xvalue_accuracy=1.e-4,   # approximations in real space aim to be this accurate
        kvalue_accuracy=1.e-4,   # approximations in fourier space aim to be this accurate
        shoot_accuracy=1.e-4,    # approximations in photon shooting aim to be this accurate
        minimum_fft_size=64)     # minimum size of ffts

    logger.info('Starting demo script 7')

    # Make the PSF profiles:
    psf1 = galsim.Gaussian(fwhm = psf_fwhm, gsparams=gsparams)
    psf2 = galsim.Moffat(fwhm = psf_fwhm, beta = 2.4, gsparams=gsparams)
    psf3_inner = galsim.Gaussian(fwhm = psf_fwhm, flux = 0.8, gsparams=gsparams)
    psf3_outer = galsim.Gaussian(fwhm = 2*psf_fwhm, flux = 0.2, gsparams=gsparams)
    psf3 = psf3_inner + psf3_outer
    atmos = galsim.Gaussian(fwhm = psf_fwhm, gsparams=gsparams)
    # The OpticalPSF and set of Zernike values chosen below correspond to a reasonably well aligned,
    # smallish ~0.3m / 12 inch diameter telescope with a central obscuration of ~0.12m or 5 inches
    # diameter, being used in optical wavebands.  
    # In the Noll convention, the value of the Zernike coefficient also gives the RMS optical path
    # difference across a circular pupil.  An RMS difference of ~0.5 or larger indicates that parts
    # of the wavefront are in fully destructive interference, and so we might expect aberrations to
    # become strong when Zernike aberrations summed in quadrature approach 0.5 wave.
    # The aberrations chosen in this case correspond to operating close to a 0.25 wave RMS optical
    # path difference.  Unlike in demo3, we specify the aberrations by making a list that we pass
    # in using the 'aberrations' kwarg.  The order of aberrations starting from index 4 is defocus,
    # astig1, astig2, coma1, coma2, trefoil1, trefoil2, spher as in the Noll convention.
    # We ignore the first 4 values so that the index number corresponds to the Zernike index
    # in the Noll convention. This will be particularly convenient once we start allowing 
    # coefficients beyond spherical (index 11).  c.f. The Wikipedia page about the Noll indices:
    #
    #     http://en.wikipedia.org/wiki/Zernike_polynomials#Zernike_polynomials

    aberrations = [ 0.0 ] * 12          # Set the initial size.
    aberrations[4] = 0.06               # Noll index 4 = Defocus
    aberrations[5:7] = [ 0.12, -0.08 ]  # Noll index 5,6 = Astigmatism
    aberrations[7:9] = [ 0.07, 0.04 ]   # Noll index 7,8 = Coma
    aberrations[11] = -0.13             # Noll index 11 = Spherical
    # You could also define these all at once if that is more convenient:
    #aberrations = [0.0, 0.0, 0.0, 0.0, 0.06, 0.12, -0.08, 0.07, 0.04, 0.0, 0.0, -0.13]

    optics = galsim.OpticalPSF(
        lam_over_diam = 0.6 * psf_fwhm, obscuration = 0.4, aberrations = aberrations,
        gsparams=gsparams)
    psf4 = galsim.Convolve([atmos, optics]) # Convolve inherits the gsparams from the first 
                                            # item in the list.  (Or you can supply a gsparams
                                            # argument explicitly if you want to override this.)
    atmos = galsim.Kolmogorov(fwhm = psf_fwhm, gsparams=gsparams)
    optics = galsim.Airy(lam_over_diam = 0.3 * psf_fwhm, gsparams=gsparams)
    psf5 = galsim.Convolve([atmos,optics])
    psfs = [psf1, psf2, psf3, psf4, psf5]
    psf_names = ["Gaussian", "Moffat", "Double Gaussian", "OpticalPSF", "Kolmogorov * Airy"]
    psf_times = [0,0,0,0,0]
    psf_fft_times = [0,0,0,0,0]
    psf_phot_times = [0,0,0,0,0]

    # Make the galaxy profiles:
    gal1 = galsim.Gaussian(half_light_radius = 1, gsparams=gsparams)
    gal2 = galsim.Exponential(half_light_radius = 1, gsparams=gsparams)
    gal3 = galsim.DeVaucouleurs(half_light_radius = 1, gsparams=gsparams)
    gal4 = galsim.Sersic(half_light_radius = 1, n = 2.5, gsparams=gsparams)
    # A Sersic profile may be truncated if desired.
    # The units for this are expected to be arcsec (or specifically -- whatever units
    # you are using for all the size values as defined by the pixel_scale).
    bulge = galsim.Sersic(half_light_radius = 0.7, n = 3.2, trunc = 8.5, gsparams=gsparams)
    disk = galsim.Sersic(half_light_radius = 1.2, n = 1.5, gsparams=gsparams)
    gal5 = 0.4*bulge + 0.6*disk  # Net half-light radius is only approximate for this one.
    gals = [gal1, gal2, gal3, gal4, gal5]
    gal_names = ["Gaussian", "Exponential", "Devaucouleurs", "n=2.5 Sersic", "Bulge + Disk"]
    gal_times = [0,0,0,0,0]
    gal_fft_times = [0,0,0,0,0]
    gal_phot_times = [0,0,0,0,0]

    # Other times to keep track of:
    setup_times = 0
    fft_times = 0
    phot_times = 0
    noise_times = 0

    # Loop over combinations of psf, gal, and make 4 random choices for flux, size, shape.
    all_images = []
    k = 0
    for ipsf in range(len(psfs)):
        psf = psfs[ipsf]
        psf_name = psf_names[ipsf]
        for igal in range(len(gals)):
            gal = gals[igal]
            gal_name = gal_names[igal]
            for i in range(4):
                logger.debug('Start work on image %d',i)
                t1 = time.time()

                # Initialize the random number generator we will be using.
                rng = galsim.UniformDeviate(random_seed+k)

                # Generate random variates:
                flux = rng() * (gal_flux_max-gal_flux_min) + gal_flux_min

                # Use a new variable name, since we'll want to keep the original unmodified.
                this_gal = gal.withFlux(flux)

                hlr = rng() * (gal_hlr_max-gal_hlr_min) + gal_hlr_min
                this_gal = this_gal.dilate(hlr)

                beta_ellip = rng() * 2*math.pi * galsim.radians
                ellip = rng() * (gal_e_max-gal_e_min) + gal_e_min
                gal_shape = galsim.Shear(e=ellip, beta=beta_ellip)
                this_gal = this_gal.shear(gal_shape)

                # Build the final object by convolving the galaxy and PSF.
                final = galsim.Convolve([this_gal, psf])

                # Create the large, double width output image
                # Rather than provide a scale= argument to the drawImage commands, we can also
                # set the pixel scale in the image constructor.
                # Note: You can also change it after the construction with im.scale=pixel_scale
                image = galsim.ImageF(2*nx+2, ny, scale=pixel_scale)

                # Assign the following two Image "views", fft_image and phot_image.
                # Using the syntax below, these are views into the larger image.  
                # Changes/additions to the sub-images referenced by the views are automatically 
                # reflected in the original image.
                fft_image = image[galsim.BoundsI(1, nx, 1, ny)]
                phot_image = image[galsim.BoundsI(nx+3, 2*nx+2, 1, ny)]

                logger.debug('   Read in training sample galaxy and PSF from file')
                t2 = time.time()

                # Draw the profile
                # This default rendering method (method='auto') usually defaults to FFT, since
                # that is normally the most efficient method.  However, we can also set method
                # to 'fft' explicitly to force it to always use FFTs for the convolution
                # by the pixel response.  (In this case, it doesn't have any effect, since
                # the 'auto' method would have always chosen 'fft' anyway, so this is just
                # for illustrative purposes.)
                final.drawImage(fft_image, method='fft')

                logger.debug('   Drew fft image.  Total drawn flux = %f.  .flux = %f',
                             fft_image.array.sum(),final.getFlux())
                t3 = time.time()

                # Add Poisson noise
                sky_level_pixel = sky_level * pixel_scale**2
                fft_image.addNoise(galsim.PoissonNoise(rng, sky_level=sky_level_pixel))

                t4 = time.time()

                # The next two lines are just to get the output from this demo script
                # to match the output from the parsing of demo7.yaml.
                rng = galsim.UniformDeviate(random_seed+k)
                rng(); rng(); rng(); rng();

                # Repeat for photon shooting image.
                # The max_extra_noise parameter indicates how much extra noise per pixel we are
                # willing to tolerate.  The sky noise will be adding a variance of sky_level_pixel,
                # so we allow up to 1% of that extra.
                final.drawImage(phot_image, method='phot', max_extra_noise=sky_level_pixel/100,
                                rng=rng)
                t5 = time.time()

                # For photon shooting, galaxy already has Poisson noise, so we want to make 
                # sure not to add that noise again!  Thus, we just add sky noise, which 
                # is Poisson with the mean = sky_level_pixel
                pd = galsim.PoissonDeviate(rng, mean=sky_level_pixel)
                # DeviateNoise just adds the action of the given deviate to every pixel.
                phot_image.addNoise(galsim.DeviateNoise(pd))
                # For PoissonDeviate, the mean is not zero, so for a background-subtracted
                # image, we need to subtract the mean back off when we are done.
                phot_image -= sky_level_pixel

                logger.debug('   Added Poisson noise.  Image fluxes are now %f and %f',
                             fft_image.array.sum(), phot_image.array.sum())
                t6 = time.time()

                # Store that into the list of all images
                all_images += [image]

                k = k+1
                logger.info('%d: %s * %s, flux = %.2e, hlr = %.2f, ellip = (%.2f,%.2f)',
                            k,gal_name, psf_name, flux, hlr, gal_shape.getE1(), gal_shape.getE2())
                logger.debug('   Times: %f, %f, %f, %f, %f',t2-t1, t3-t2, t4-t3, t5-t4, t6-t5)

                psf_times[ipsf] += t6-t1
                psf_fft_times[ipsf] += t3-t2
                psf_phot_times[ipsf] += t5-t4
                gal_times[igal] += t6-t1
                gal_fft_times[igal] += t3-t2
                gal_phot_times[igal] += t5-t4
                setup_times += t2-t1
                fft_times += t3-t2
                phot_times += t5-t4
                noise_times += t4-t3 + t6-t5

    logger.info('Done making images of galaxies')
    logger.info('')
    logger.info('Some timing statistics:')
    logger.info('   Total time for setup steps = %f',setup_times)
    logger.info('   Total time for regular fft drawing = %f',fft_times)
    logger.info('   Total time for photon shooting = %f',phot_times)
    logger.info('   Total time for adding noise = %f',noise_times)
    logger.info('')
    logger.info('Breakdown by PSF type:')
    for ipsf in range(len(psfs)):
        logger.info('   %s: Total time = %f  (fft: %f, phot: %f)',
                    psf_names[ipsf],psf_times[ipsf],psf_fft_times[ipsf],psf_phot_times[ipsf])
    logger.info('')
    logger.info('Breakdown by Galaxy type:')
    for igal in range(len(gals)):
        logger.info('   %s: Total time = %f  (fft: %f, phot: %f)',
                    gal_names[igal],gal_times[igal],gal_fft_times[igal],gal_phot_times[igal])
    logger.info('')

    # Now write the image to disk.
    # With any write command, you can optionally compress the file using several compression
    # schemes:
    #   'gzip' uses gzip on the full output file.
    #   'bzip2' uses bzip2 on the full output file.
    #   'rice' uses rice compression on the image, leaving the fits headers readable.
    #   'gzip_tile' uses gzip in tiles on the output image, leaving the fits headers readable.
    #   'hcompress' uses hcompress on the image, but it is only valid for 2-d data, so it 
    #               doesn't work for writeCube.
    #   'plio' uses plio on the image, but it is only valid for positive integer data.
    # Furthermore, the first three have standard filename extensions associated with them,
    # so if you don't specify a compression, but the filename ends with '.gz', '.bz2' or '.fz',
    # the corresponding compression will be selected automatically.
    # In other words, the `compression='gzip'` specification is actually optional here:
    galsim.fits.writeCube(all_images, file_name, compression='gzip')
    logger.info('Wrote fft image to fits data cube %r',file_name)
Exemple #16
0
def drawimg(catalog,
            simgalimgfilepath="test.fits",
            simtrugalimgfilepath=None,
            simpsfimgfilepath=None,
            gsparams=None,
            sersiccut=None):
    """
	Turns a catalog as obtained from drawcat into FITS images.
	Only the position jitter and the pixel noise are randomized. All the other info is taken from the input catalog.
	So simply call me several times for the same input to get different realizations of the same galaxies.
	To specify the PSFs, add a meta["psf"] ImageInfo object to the catalog.
		
	:param catalog: an input catalog of galaxy shape parameters, as returned by drawcat.
		The corresponding stampsize must be provided as catalog.meta["stampsize"].
		If you specify a psf image in catalog.meta["psf"], your catalog must of course also contain
		PSF coordinates for that image.
		If no PSF stamps are specifed, the code will look for Gaussian PSF parameters in the catalog.
		If such parameters are not given, no PSF convolution is done.
		
	:param simgalimgfilepath: where I write my output image of simulated and noisy galaxies
	:param simtrugalimgfilepath: (optional) where I write the image without convolution and noise
	:param simpsfimgfilepath: (optional) where I write the PSFs
	
	:param sersiccut: cuts the sersic profile at this number of rad
	
	.. note::
		See this function in MomentsML v4 (great3) for attempts to speed up galsim by playing with fft params, accuracy, etc...
	
	.. note::
		About speed, if you specify trunc, better express the scale radius.
		But scale radius is crazy dependent on n, so I keep half-light-radius
		http://galsim-developers.github.io/GalSim/classgalsim_1_1base_1_1_sersic.html
		
		
	.. note::
		To use the hacks, give "metadict":{"hack":"nicobackgals"} as drawcatkwargs to sim.run.multi()...
	
	"""
    starttime = datetime.now()

    hack = catalog.meta.get("hack", None)

    if hack is None:  # The default behavior, without specific gsparams or tricks.

        if gsparams is None:
            gsparams = galsim.GSParams(maximum_fft_size=10240)

        if "nx" not in catalog.meta.keys() or "ny" not in catalog.meta.keys():
            raise RuntimeError(
                "Provide nx and ny in the meta data of the input catalog to drawimg."
            )
        if "stampsize" not in catalog.meta.keys():
            raise RuntimeError(
                "Provide stampsize in the meta data of the input catalog to drawimg."
            )

        nx = catalog.meta["nx"]
        ny = catalog.meta["ny"]
        stampsize = catalog.meta["stampsize"]  # The stamps I'm going to draw

        logger.info("Drawing images of %i galaxies on a %i x %i grid..." %
                    (len(catalog), nx, ny))
        logger.info("The stampsize for the simulated galaxies is %i." %
                    (stampsize))

        # A special function checks the combination of settings in the provided catalog:
        todo = checkcat(catalog)

        if "loadpsfimg" in todo:
            psfimg = catalog.meta["psf"].load(
            )  # Loading the actual GalSim Image
            psfinfo = catalog.meta["psf"]

        if "tru_pixel" in todo:
            # This is only if you want "effective pixels" larger than the actual pixels (related to SBE, normally you don't want this).
            pix = galsim.Pixel(
                catalog["tru_pixel"]
                [0])  # We have checked in checkcat that all values are equal.

        # Galsim random number generators
        rng = galsim.BaseDeviate()
        ud = galsim.UniformDeviate()  # This gives a random float in [0, 1)

        # We prepare the big images :
        gal_image = galsim.ImageF(stampsize * nx, stampsize * ny)
        trugal_image = galsim.ImageF(stampsize * nx, stampsize * ny)
        psf_image = galsim.ImageF(stampsize * nx, stampsize * ny)

        gal_image.scale = 1.0  # we use pixels as units. Note that if you change something here, you also have to change the jitter.
        trugal_image.scale = 1.0
        psf_image.scale = 1.0

        # And loop through the catalog:
        for row in catalog:

            # Some simplistic progress indication:
            fracdone = float(row.index) / len(catalog)
            if row.index % 500 == 0:
                logger.info("%6.2f%% done (%i/%i) " %
                            (fracdone * 100.0, row.index, len(catalog)))

            # We will draw this galaxy in a postage stamp, but first we need the bounds of this stamp.
            ix = int(row["ix"])
            iy = int(row["iy"])
            assert ix < nx and iy < ny
            bounds = galsim.BoundsI(
                ix * stampsize + 1, (ix + 1) * stampsize, iy * stampsize + 1,
                (iy + 1) *
                stampsize)  # Default Galsim convention, index starts at 1.
            gal_stamp = gal_image[bounds]
            trugal_stamp = trugal_image[bounds]
            psf_stamp = psf_image[bounds]

            # We draw the desired profile
            profile_type = params.profile_types[row["tru_type"]]

            if profile_type == "Sersic":
                if sersiccut is None:
                    trunc = 0
                else:
                    trunc = float(row["tru_rad"]) * sersiccut
                gal = galsim.Sersic(n=float(row["tru_sersicn"]),
                                    half_light_radius=float(row["tru_rad"]),
                                    flux=float(row["tru_flux"]),
                                    gsparams=gsparams,
                                    trunc=trunc)
                # We make this profile elliptical
                gal = gal.shear(g1=row["tru_g1"], g2=row["tru_g2"]
                                )  # This adds the ellipticity to the galaxy

            elif profile_type == "Gaussian":

                gal = galsim.Gaussian(flux=float(row["tru_flux"]),
                                      sigma=float(row["tru_sigma"]),
                                      gsparams=gsparams)
                # We make this profile elliptical
                gal = gal.shear(g1=row["tru_g1"], g2=row["tru_g2"]
                                )  # This adds the ellipticity to the galaxy

            elif profile_type == "EBulgeDisk":

                # A more advanced Bulge + Disk model
                # It needs GalSim version master, as of April 2017 (probably 1.5).

                # Get a Sersic bulge:
                bulge = galsim.Sersic(n=row["tru_bulge_sersicn"],
                                      half_light_radius=row["tru_bulge_hlr"],
                                      flux=row["tru_bulge_flux"])
                # Make it elliptical:
                bulge_ell = galsim.Shear(g=row["tru_bulge_g"],
                                         beta=row["tru_theta"] *
                                         galsim.degrees)
                bulge = bulge.shear(bulge_ell)

                # Get a disk
                scale_radius = row[
                    "tru_disk_hlr"] / galsim.Exponential._hlr_factor
                disk = galsim.InclinedExponential(
                    inclination=row["tru_disk_tilt"] * galsim.degrees,
                    scale_radius=scale_radius,
                    flux=row["tru_disk_flux"],
                    scale_h_over_r=row["tru_disk_scale_h_over_r"])
                # Rotate it in the same orientation as the bulge:
                disk = disk.rotate(row["tru_theta"] * galsim.degrees)

                # And we add those profiles, as done in GalSim demo3.py :
                gal = bulge + disk

            else:
                raise RuntimeError("Unknown galaxy profile!")

            # And now we add lensing, if s1, s2 and mu are different from no lensing...
            if row["tru_s1"] != 0 or row["tru_s2"] != 0 or row["tru_mu"] != 1:
                gal = gal.lens(float(row["tru_s1"]), float(row["tru_s2"]),
                               float(row["tru_mu"]))
            else:
                pass
                #logger.info("No lensing!")

            # We apply some jitter to the position of this galaxy
            xjitter = ud(
            ) - 0.5  # This is the minimum amount -- should we do more, as real galaxies are not that well centered in their stamps ?
            yjitter = ud() - 0.5
            gal = gal.shift(xjitter, yjitter)

            # We draw the pure unconvolved galaxy
            if simtrugalimgfilepath != None:
                gal.drawImage(
                    trugal_stamp,
                    method="auto")  # Will convolve by the sampling pixel.

            # We prepare/get the PSF and do the convolution:

            # Should the final operation skip the convolution by the pixel (because the PSF already is in large pixels) ?
            skip_pixel_conv = False

            if "usegausspsf" in todo:

                if row["tru_psf_sigma"] < 0.0:
                    raise RuntimeError("Unknown hack!")
                else:
                    psf = galsim.Gaussian(flux=1., sigma=row["tru_psf_sigma"])
                    psf = psf.shear(g1=row["tru_psf_g1"], g2=row["tru_psf_g2"])

                    # Let's apply some jitter to the position of the PSF (not sure if this is required, but should not harm ?)
                    psf_xjitter = ud() - 0.5
                    psf_yjitter = ud() - 0.5
                    psf = psf.shift(psf_xjitter, psf_yjitter)

                if simpsfimgfilepath != None:
                    psf.drawImage(
                        psf_stamp,
                        method="auto")  # Will convolve by the sampling pixel.

                if "tru_pixel" in todo:  # Not sure if this should only apply to gaussian PSFs, but so far this seems OK.
                    # Remember that this is an "additional" pixel convolution, not the usual sampling-related convolution that happens in drawImage.
                    galconv = galsim.Convolve([gal, psf, pix])

                else:
                    galconv = galsim.Convolve([gal, psf])

            elif "loadpsfimg" in todo:

                (inputpsfstamp,
                 flag) = tools.image.getstamp(row[psfinfo.xname],
                                              row[psfinfo.yname], psfimg,
                                              psfinfo.stampsize)
                psfpixelscale = getattr(
                    psfinfo, "pixelscale", 1.0
                )  # Using getattr so that it works with old objects as well
                if psfpixelscale > 0.5:
                    #logger.warning("You seem to be using a sampled PSF with large pixels (e.g., observed stars). I'll do my best and skip the pixel conv, but this might well lead to errors.")
                    skip_pixel_conv = True
                if flag != 0:
                    raise RuntimeError("Could not extract a %ix%i stamp at (%.2f, %.2f) from the psfimg %s" %\
                     (psfinfo.stampsize, psfinfo.stampsize, row[psfinfo.xname], row[psfinfo.yname], psfinfo.name))
                psf = galsim.InterpolatedImage(inputpsfstamp,
                                               flux=1.0,
                                               scale=psfpixelscale)
                if simpsfimgfilepath != None:
                    psf.drawImage(
                        psf_stamp, method="no_pixel"
                    )  # psf_stamp has a different size than inputpsfstamp, so this could lead to problems one day.

                #galconv = galsim.Convolve([gal,psf], real_space=False)
                galconv = galsim.Convolve([gal, psf])

            elif "nopsf" in todo:
                # Nothing to do
                galconv = gal

            else:
                raise RuntimeError("Bug in todo.")

            # Draw the convolved galaxy
            if skip_pixel_conv == False:
                galconv.drawImage(
                    gal_stamp, method="auto"
                )  # This will convolve by the image sampling pixel. Don't do this yourself ahead!
            else:
                #logger.warning("NOT computing any pixel convolution")
                galconv.drawImage(
                    gal_stamp, method="no_pixel"
                )  # Simply uses pixel-center values. Know what you are doing, see doc of galsim.

            # And add noise to the convolved galaxy:
            gal_stamp.addNoise(
                galsim.CCDNoise(rng,
                                sky_level=float(row["tru_sky_level"]),
                                gain=float(row["tru_gain"]),
                                read_noise=float(row["tru_read_noise"])))

        logger.info("Done with drawing, now writing output FITS files ...")

        gal_image.write(simgalimgfilepath)

        if simtrugalimgfilepath != None:
            trugal_image.write(simtrugalimgfilepath)

        if simpsfimgfilepath != None:
            psf_image.write(simpsfimgfilepath)

    elif hack == "nicobackgals":
        """
		This is taken and/or adapted from Nico's simulation code, to be sure to do the same thing.
			- Nico uses GalSim in units of arcsec (instaed of pixels), and we suspect that this affects the convolution.
			- Nico has different gsparams
			- Nico truncates Sersic profiles
			
		"""
        logger.info("Using special hack for nicobackgals")

        gsparams = galsim.GSParams(xvalue_accuracy=2.e-4,
                                   kvalue_accuracy=2.e-4,
                                   maxk_threshold=5.e-3,
                                   folding_threshold=1.e-2)
        pixel_scale = 0.1

        if "nx" not in catalog.meta.keys() or "ny" not in catalog.meta.keys():
            raise RuntimeError(
                "Provide nx and ny in the meta data of the input catalog to drawimg."
            )
        if "stampsize" not in catalog.meta.keys():
            raise RuntimeError(
                "Provide stampsize in the meta data of the input catalog to drawimg."
            )

        nx = catalog.meta["nx"]
        ny = catalog.meta["ny"]
        stampsize = catalog.meta["stampsize"]  # The stamps I'm going to draw

        logger.info("Drawing images of %i galaxies on a %i x %i grid..." %
                    (len(catalog), nx, ny))
        logger.info("The stampsize for the simulated galaxies is %i." %
                    (stampsize))

        # Galsim random number generators
        rng = galsim.BaseDeviate()
        ud = galsim.UniformDeviate()  # This gives a random float in [0, 1)

        # We prepare the big image, and set the pixel scale
        gal_image = galsim.ImageF(stampsize * nx,
                                  stampsize * ny,
                                  scale=pixel_scale)


        psf = galsim.Airy(lam=800, diam=1.2, obscuration=0.3, scale_unit=galsim.arcsec, flux=1./3) + \
         galsim.Airy(lam=700, diam=1.2, obscuration=0.3, scale_unit=galsim.arcsec, flux=1./3) + \
         galsim.Airy(lam=600, diam=1.2, obscuration=0.3, scale_unit=galsim.arcsec, flux=1./3)

        for row in catalog:

            # Some simplistic progress indication:
            fracdone = float(row.index) / len(catalog)
            if row.index % 500 == 0:
                logger.info("%6.2f%% done (%i/%i) " %
                            (fracdone * 100.0, row.index, len(catalog)))

            # We will draw this galaxy in a postage stamp, but first we need the bounds of this stamp.
            ix = int(row["ix"])
            iy = int(row["iy"])
            assert ix < nx and iy < ny
            bounds = galsim.BoundsI(
                ix * stampsize + 1, (ix + 1) * stampsize, iy * stampsize + 1,
                (iy + 1) *
                stampsize)  # Default Galsim convention, index starts at 1.
            gal_stamp = gal_image[bounds]

            # Drawing the galaxy
            half_light_radius = float(row["tru_rad"]) * pixel_scale
            gal = galsim.Sersic(n=float(row["tru_sersicn"]),
                                half_light_radius=half_light_radius,
                                flux=float(row["tru_flux"]),
                                gsparams=gsparams,
                                trunc=half_light_radius * 4.5)
            # We make this profile elliptical
            gal = gal.shear(
                g1=row["tru_g1"],
                g2=row["tru_g2"])  # This adds the ellipticity to the galaxy

            # And now we add lensing, if s1, s2 and mu are different from no lensing...
            if row["tru_s1"] != 0 or row["tru_s2"] != 0 or row["tru_mu"] != 1:
                gal = gal.lens(float(row["tru_s1"]), float(row["tru_s2"]),
                               float(row["tru_mu"]))
            else:
                pass
                #logger.info("No lensing!")

            # We apply some jitter to the position of this galaxy # it seems this has to be given in the image scale...
            xjitter = (ud() - 0.5) * pixel_scale
            yjitter = (ud() - 0.5) * pixel_scale
            gal = gal.shift(xjitter, yjitter)

            final = galsim.Convolve([psf, gal])

            final.drawImage(gal_stamp)

            # And add noise to the convolved galaxy:
            gal_stamp.addNoise(
                galsim.CCDNoise(rng,
                                sky_level=float(row["tru_sky_level"]),
                                gain=float(row["tru_gain"]),
                                read_noise=float(row["tru_read_noise"])))

        logger.info("Done with drawing, now writing output FITS files ...")

        gal_image.write(simgalimgfilepath)

    else:
        raise RuntimeError("Unknown hack")

    endtime = datetime.now()
    logger.info("This drawing took %s" % (str(endtime - starttime)))
Exemple #17
0
  print "syntax: sim1.py g1gal g2gal g1psf g2psf"
  sys.exit (1)
    
gal_flux=1000
gal_e1=float(sys.argv[1])
gal_e2=float(sys.argv[2])
gal_sigma=0.5/0.27 / 2.35 # Gaussian sigma such that FWHM~0.5''

psf_sigma=0.9/0.27 / 2.35 # Gaussian sigma such that FWHM~0.9''
psf_e1=float(sys.argv[3])
psf_e2=float(sys.argv[4])

psf_fluxmax=15000
    
# Define the true galaxy profile
gal = galsim.Gaussian(flux=gal_flux, sigma=gal_sigma)
gal = gal.shear(galsim.Shear(g1=gal_e1, g2=gal_e2))

# Define the PSF profile
psf = galsim.Gaussian(flux=1., sigma=psf_sigma) # PSF flux should always = 1
psf = psf.shear(galsim.Shear(g1=psf_e1, g2=psf_e2))

galobserved = galsim.Convolve([gal, psf])
galobserved = galobserved.shift(0.5,0.5) # shift to be centered on a pixel

galimage_nocd = galobserved.drawImage(scale=1)

psf.setFlux(psf.getFlux()*psf_fluxmax/psf.xValue(0,0))
psfobserved = psf.shift(0.5,0.5) # shift to be centered on a pixel
psfimage_nocd = psfobserved.drawImage(scale=1)
Exemple #18
0
def test_realspace_convolve():
    """Test the real-space convolution of a Moffat and a Box profile against a known result.
    """
    dx = 0.2
    # Note: Using an image created from Maple "exact" calculations.
    saved_img = galsim.fits.read(os.path.join(imgdir, "moffat_pixel.fits"))
    img = galsim.ImageF(saved_img.bounds, scale=dx)
    img.setCenter(0,0)

    # Code was formerly:
    # psf = galsim.Moffat(beta=1.5, truncationFWHM=4, flux=1, half_light_radius=1)
    #
    # ...but this is no longer quite so simple since we changed the handling of trunc to be in
    # physical units.  However, the same profile can be constructed using
    # fwhm=1.0927449310213702,
    # as calculated by interval bisection in devutils/external/calculate_moffat_radii.py
    fwhm_backwards_compatible = 1.0927449310213702
    psf = galsim.Moffat(beta=1.5, half_light_radius=1,
                        trunc=4*fwhm_backwards_compatible, flux=1)
    #psf = galsim.Moffat(beta=1.5, fwhm=fwhm_backwards_compatible,
                        #trunc=4*fwhm_backwards_compatible, flux=1)
    pixel = galsim.Pixel(scale=dx, flux=1.)
    conv = galsim.Convolve([psf,pixel],real_space=True)
    conv.drawImage(img,scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
            img.array, saved_img.array, 5,
            err_msg="Using GSObject Convolve([psf,pixel]) disagrees with expected result")

    # Check with default_params
    conv = galsim.Convolve([psf,pixel],real_space=True,gsparams=default_params)
    conv.drawImage(img,scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
            img.array, saved_img.array, 5,
            err_msg="Using GSObject Convolve([psf,pixel]) with default_params disagrees with "
            "expected result")
    conv = galsim.Convolve([psf,pixel],real_space=True,gsparams=galsim.GSParams())
    conv.drawImage(img,scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
            img.array, saved_img.array, 5,
            err_msg="Using GSObject Convolve([psf,pixel]) with GSParams() disagrees with "
            "expected result")

    # Other ways to do the convolution:
    conv = galsim.Convolve(psf,pixel,real_space=True)
    conv.drawImage(img,scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
            img.array, saved_img.array, 5,
            err_msg="Using GSObject Convolve(psf,pixel) disagrees with expected result")

    # The real-space convolution algorithm is not (trivially) independent of the order of
    # the two things being convolved.  So check the opposite order.
    conv = galsim.Convolve([pixel,psf],real_space=True)
    conv.drawImage(img,scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
            img.array, saved_img.array, 5,
            err_msg="Using GSObject Convolve([pixel,psf]) disagrees with expected result")

    check_basic(conv, "Truncated Moffat*Box", approx_maxsb=True)

    # Test kvalues
    do_kvalue(conv,img,"Truncated Moffat*Box")

    # Check picklability
    do_pickle(conv, lambda x: x.drawImage(method='sb'))
    do_pickle(conv)

    # Check some warnings that should be raised
    # More than 2 with only hard edges gives a warning either way. (Different warnings though.)
    assert_warns(galsim.GalSimWarning, galsim.Convolve, [psf, psf, pixel])
    assert_warns(galsim.GalSimWarning, galsim.Convolve, [psf, psf, pixel], real_space=False)
    assert_warns(galsim.GalSimWarning, galsim.Convolve, [psf, psf, pixel], real_space=True)
    # 2 with hard edges gives a warning if we ask it not to use real_space
    assert_warns(galsim.GalSimWarning, galsim.Convolve, [psf, pixel], real_space=False)
    # >2 of any kind give a warning if we ask it to use real_space
    g = galsim.Gaussian(sigma=2)
    assert_warns(galsim.GalSimWarning, galsim.Convolve, [g, g, g], real_space=True)
    # non-analytic profiles cannot do real_space
    d = galsim.Deconvolve(galsim.Gaussian(sigma=2))
    assert_warns(galsim.GalSimWarning, galsim.Convolve, [g, d], real_space=True)
    assert_raises(TypeError, galsim.Convolve, [g, d], real_space='true')

    # Repeat some of the above for AutoConvolve and AutoCorrelate
    conv = galsim.AutoConvolve(psf,real_space=True)
    check_basic(conv, "AutoConvolve Truncated Moffat", approx_maxsb=True)
    do_kvalue(conv,img,"AutoConvolve Truncated Moffat")
    do_pickle(conv)

    conv = galsim.AutoCorrelate(psf,real_space=True)
    check_basic(conv, "AutoCorrelate Truncated Moffat", approx_maxsb=True)
    do_kvalue(conv,img,"AutoCorrelate Truncated Moffat")
    do_pickle(conv)

    assert_warns(galsim.GalSimWarning, galsim.AutoConvolve, psf, real_space=False)
    assert_warns(galsim.GalSimWarning, galsim.AutoConvolve, d, real_space=True)
    assert_warns(galsim.GalSimWarning, galsim.AutoCorrelate, psf, real_space=False)
    assert_warns(galsim.GalSimWarning, galsim.AutoCorrelate, d, real_space=True)
    assert_raises(TypeError, galsim.AutoConvolve, d, real_space='true')
    assert_raises(TypeError, galsim.AutoCorrelate, d, real_space='true')
Exemple #19
0
def test_gaussian_radii():
    """Test initialization of Gaussian with different types of radius specification.
    """
    import math
    test_hlr = 1.7
    test_fwhm = 1.8
    test_sigma = 1.9

    # Test constructor using half-light-radius:
    test_gal = galsim.Gaussian(flux=1., half_light_radius=test_hlr)
    hlr_sum = radial_integrate(test_gal, 0., test_hlr)
    print('hlr_sum = ', hlr_sum)
    np.testing.assert_almost_equal(
        hlr_sum,
        0.5,
        decimal=4,
        err_msg="Error in Gaussian constructor with half-light radius")

    # test that fwhm provides correct FWHM
    got_fwhm = test_gal.fwhm
    test_fwhm_ratio = (test_gal.xValue(galsim.PositionD(.5 * got_fwhm, 0.)) /
                       test_gal.xValue(galsim.PositionD(0., 0.)))
    print('fwhm ratio = ', test_fwhm_ratio)
    np.testing.assert_almost_equal(
        test_fwhm_ratio,
        0.5,
        decimal=4,
        err_msg="Error in FWHM for Gaussian initialized with half-light radius"
    )

    # test that sigma provides correct sigma
    got_sigma = test_gal.sigma
    test_sigma_ratio = (test_gal.xValue(galsim.PositionD(got_sigma, 0.)) /
                        test_gal.xValue(galsim.PositionD(0., 0.)))
    print('sigma ratio = ', test_sigma_ratio)
    np.testing.assert_almost_equal(
        test_sigma_ratio,
        math.exp(-0.5),
        decimal=4,
        err_msg="Error in sigma for Gaussian initialized with half-light radius"
    )

    # Test constructor using sigma:
    test_gal = galsim.Gaussian(flux=1., sigma=test_sigma)
    center = test_gal.xValue(galsim.PositionD(0, 0))
    ratio = test_gal.xValue(galsim.PositionD(test_sigma, 0)) / center
    print('sigma ratio = ', ratio)
    np.testing.assert_almost_equal(
        ratio,
        np.exp(-0.5),
        decimal=4,
        err_msg="Error in Gaussian constructor with sigma")

    # then test that image indeed has the correct HLR properties when radially integrated
    got_hlr = test_gal.half_light_radius
    hlr_sum = radial_integrate(test_gal, 0., got_hlr)
    print('hlr_sum (profile initialized with sigma) = ', hlr_sum)
    np.testing.assert_almost_equal(
        hlr_sum,
        0.5,
        decimal=4,
        err_msg=
        "Error in half light radius for Gaussian initialized with sigma.")

    # test that fwhm provides correct FWHM
    got_fwhm = test_gal.fwhm
    test_fwhm_ratio = (test_gal.xValue(galsim.PositionD(.5 * got_fwhm, 0.)) /
                       test_gal.xValue(galsim.PositionD(0., 0.)))
    print('fwhm ratio = ', test_fwhm_ratio)
    np.testing.assert_almost_equal(
        test_fwhm_ratio,
        0.5,
        decimal=4,
        err_msg="Error in FWHM for Gaussian initialized with sigma.")

    # Test constructor using FWHM:
    test_gal = galsim.Gaussian(flux=1., fwhm=test_fwhm)
    center = test_gal.xValue(galsim.PositionD(0, 0))
    ratio = test_gal.xValue(galsim.PositionD(test_fwhm / 2., 0)) / center
    print('fwhm ratio = ', ratio)
    np.testing.assert_almost_equal(
        ratio,
        0.5,
        decimal=4,
        err_msg="Error in Gaussian constructor with fwhm")

    # then test that image indeed has the correct HLR properties when radially integrated
    got_hlr = test_gal.half_light_radius
    hlr_sum = radial_integrate(test_gal, 0., got_hlr)
    print('hlr_sum (profile initialized with fwhm) = ', hlr_sum)
    np.testing.assert_almost_equal(
        hlr_sum,
        0.5,
        decimal=4,
        err_msg="Error in half light radius for Gaussian initialized with FWHM."
    )

    # test that sigma provides correct sigma
    got_sigma = test_gal.sigma
    test_sigma_ratio = (test_gal.xValue(galsim.PositionD(got_sigma, 0.)) /
                        test_gal.xValue(galsim.PositionD(0., 0.)))
    print('sigma ratio = ', test_sigma_ratio)
    np.testing.assert_almost_equal(
        test_sigma_ratio,
        math.exp(-0.5),
        decimal=4,
        err_msg="Error in sigma for Gaussian initialized with FWHM.")

    # Check that the properties don't work after modifying the original.
    # Note: I test all the modifiers here.  For the rest of the profile types, I'll
    # just confirm that it is true of shear.  I don't think that has any chance
    # of missing anything.
    test_gal_flux1 = test_gal * 3.
    assert_raises(AttributeError, getattr, test_gal_flux1, "fwhm")
    assert_raises(AttributeError, getattr, test_gal_flux1, "half_light_radius")
    assert_raises(AttributeError, getattr, test_gal_flux1, "sigma")

    test_gal_flux2 = test_gal.withFlux(3.)
    assert_raises(AttributeError, getattr, test_gal_flux2, "fwhm")
    assert_raises(AttributeError, getattr, test_gal_flux2, "half_light_radius")
    assert_raises(AttributeError, getattr, test_gal_flux2, "sigma")

    test_gal_shear = test_gal.shear(g1=0.3, g2=0.1)
    assert_raises(AttributeError, getattr, test_gal_shear, "fwhm")
    assert_raises(AttributeError, getattr, test_gal_shear, "half_light_radius")
    assert_raises(AttributeError, getattr, test_gal_shear, "sigma")

    test_gal_rot = test_gal.rotate(theta=0.5 * galsim.radians)
    assert_raises(AttributeError, getattr, test_gal_rot, "fwhm")
    assert_raises(AttributeError, getattr, test_gal_rot, "half_light_radius")
    assert_raises(AttributeError, getattr, test_gal_rot, "sigma")

    test_gal_shift = test_gal.shift(dx=0.11, dy=0.04)
    assert_raises(AttributeError, getattr, test_gal_shift, "fwhm")
    assert_raises(AttributeError, getattr, test_gal_shift, "half_light_radius")
    assert_raises(AttributeError, getattr, test_gal_shift, "sigma")
Exemple #20
0
def test_deconvolve():
    """Test that deconvolution works as expected
    """
    dx = 0.4
    myImg1 = galsim.ImageF(80,80, scale=dx)
    myImg1.setCenter(0,0)
    myImg2 = galsim.ImageF(80,80, scale=dx)
    myImg2.setCenter(0,0)

    psf = galsim.Moffat(beta=3.8, fwhm=1.3, flux=5)
    inv_psf = galsim.Deconvolve(psf)
    psf.drawImage(myImg1, method='no_pixel')
    conv = galsim.Convolve(psf,psf,inv_psf)
    conv.drawImage(myImg2, method='no_pixel')
    printval(myImg1, myImg2)
    np.testing.assert_array_almost_equal(
            myImg1.array, myImg2.array, 4,
            err_msg="Image of Deconvolve * obj^2 doesn't match obj alone")

    cen = galsim.PositionD(0,0)
    np.testing.assert_equal(inv_psf.centroid, cen)
    np.testing.assert_almost_equal(inv_psf.flux, 1./psf.flux)
    # This doesn't really have any meaning, but this is what we've assigned to a deconvolve max_sb.
    np.testing.assert_almost_equal(inv_psf.max_sb, -psf.max_sb / psf.flux**2)

    check_basic(inv_psf, "Deconvolve(Moffat)", do_x=False)

    # Also check Deconvolve with an asymmetric profile.
    obj1 = galsim.Gaussian(sigma=3., flux=4).shift(-0.2, -0.4)
    obj2 = galsim.Gaussian(sigma=6., flux=1.3).shift(0.3, 0.3)
    obj = galsim.Add(obj1, obj2)
    inv_obj = galsim.Deconvolve(obj)
    conv = galsim.Convolve([inv_obj, obj, obj])
    conv.drawImage(myImg1, method='no_pixel')
    obj.drawImage(myImg2, method='no_pixel')
    printval(myImg1, myImg2)
    np.testing.assert_array_almost_equal(
            myImg1.array, myImg2.array, 4,
            err_msg="Image of Deconvolve of asymmetric sum of Gaussians doesn't match obj alone")

    np.testing.assert_equal(inv_obj.centroid, -obj.centroid)
    np.testing.assert_almost_equal(inv_obj.flux, 1./obj.flux)
    np.testing.assert_almost_equal(inv_obj.max_sb, -obj.max_sb / obj.flux**2)

    check_basic(inv_obj, "Deconvolve(asym)", do_x=False)

    # Check picklability
    do_pickle(inv_obj)

    # And a significantly transformed deconvolve object
    jac = (0.3, -0.8, -0.7, 0.4)
    transformed_obj = obj.transform(*jac)
    transformed_inv_obj = inv_obj.transform(*jac)
    # Fix the flux -- most of the transformation commutes with deconvolution, but not flux scaling
    transformed_inv_obj /= transformed_obj.flux * transformed_inv_obj.flux
    check_basic(transformed_inv_obj, "transformed Deconvolve(asym)", do_x=False)
    conv = galsim.Convolve([transformed_inv_obj, transformed_obj, obj])
    conv.drawImage(myImg1, method='no_pixel')
    printval(myImg1, myImg2)
    np.testing.assert_array_almost_equal(
            myImg1.array, myImg2.array, 4,
            err_msg="Transformed Deconvolve didn't cancel transformed original")

    np.testing.assert_equal(transformed_inv_obj.centroid, -transformed_obj.centroid)
    np.testing.assert_almost_equal(transformed_inv_obj.flux, 1./transformed_obj.flux)
    np.testing.assert_almost_equal(transformed_inv_obj.max_sb,
                                   -transformed_obj.max_sb / transformed_obj.flux**2)

    check_basic(transformed_inv_obj, "transformed Deconvolve(asym)", do_x=False)

    # Check picklability
    do_pickle(transformed_inv_obj)

    # Should raise an exception for invalid arguments
    assert_raises(TypeError, galsim.Deconvolve)
    assert_raises(TypeError, galsim.Deconvolve, myImg1)
    assert_raises(TypeError, galsim.Deconvolve, [psf])
    assert_raises(TypeError, galsim.Deconvolve, psf, psf)
    assert_raises(TypeError, galsim.Deconvolve, psf, real_space=False)
    assert_raises(TypeError, galsim.Deconvolution)
    assert_raises(TypeError, galsim.Deconvolution, myImg1)
    assert_raises(TypeError, galsim.Deconvolution, [psf])
    assert_raises(TypeError, galsim.Deconvolution, psf, psf)
    assert_raises(TypeError, galsim.Deconvolution, psf, real_space=False)

    assert_raises(NotImplementedError, inv_obj.xValue, galsim.PositionD(0,0))
    assert_raises(NotImplementedError, inv_obj.drawReal, myImg1)
    assert_raises(NotImplementedError, inv_obj.shoot, 1)
Exemple #21
0
def test_gaussian_flux_scaling():
    """Test flux scaling for Gaussian.
    """
    # decimal point to go to for parameter value comparisons
    param_decimal = 12
    test_flux = 17.9
    test_sigma = 1.8

    # init with sigma and flux only (should be ok given last tests)
    obj = galsim.Gaussian(sigma=test_sigma, flux=test_flux)
    obj *= 2.
    np.testing.assert_almost_equal(
        obj.flux,
        test_flux * 2.,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __imul__.")
    obj = galsim.Gaussian(sigma=test_sigma, flux=test_flux)
    obj /= 2.
    np.testing.assert_almost_equal(
        obj.flux,
        test_flux / 2.,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __idiv__.")
    obj = galsim.Gaussian(sigma=test_sigma, flux=test_flux)
    obj2 = obj * 2.
    # First test that original obj is unharmed...
    np.testing.assert_almost_equal(
        obj.flux,
        test_flux,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __rmul__ (original).")
    # Then test new obj2 flux
    np.testing.assert_almost_equal(
        obj2.flux,
        test_flux * 2.,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __rmul__ (result).")
    obj = galsim.Gaussian(sigma=test_sigma, flux=test_flux)
    obj2 = 2. * obj
    # First test that original obj is unharmed...
    np.testing.assert_almost_equal(
        obj.flux,
        test_flux,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __mul__ (original).")
    # Then test new obj2 flux
    np.testing.assert_almost_equal(
        obj2.flux,
        test_flux * 2.,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __mul__ (result).")
    obj = galsim.Gaussian(sigma=test_sigma, flux=test_flux)
    obj2 = obj / 2.
    # First test that original obj is unharmed...
    np.testing.assert_almost_equal(
        obj.flux,
        test_flux,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __div__ (original).")
    # Then test new obj2 flux
    np.testing.assert_almost_equal(
        obj2.flux,
        test_flux / 2.,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __div__ (result).")
Exemple #22
0
def test_autoconvolve():
    """Test that auto-convolution works the same as convolution with itself.
    """
    dx = 0.4
    myImg1 = galsim.ImageF(80,80, scale=dx)
    myImg1.setCenter(0,0)
    myImg2 = galsim.ImageF(80,80, scale=dx)
    myImg2.setCenter(0,0)

    psf = galsim.Moffat(beta=3.8, fwhm=1.3, flux=5)
    conv = galsim.Convolve([psf,psf])
    conv.drawImage(myImg1, method='no_pixel')
    conv2 = galsim.AutoConvolve(psf)
    conv2.drawImage(myImg2, method='no_pixel')
    printval(myImg1, myImg2)
    np.testing.assert_array_almost_equal(
            myImg1.array, myImg2.array, 4,
            err_msg="Moffat convolved with self disagrees with AutoConvolve result")

    # Check with default_params
    conv = galsim.AutoConvolve(psf, gsparams=default_params)
    conv.drawImage(myImg1, method='no_pixel')
    np.testing.assert_array_almost_equal(
            myImg1.array, myImg2.array, 4,
            err_msg="Using AutoConvolve with default_params disagrees with expected result")
    conv = galsim.AutoConvolve(psf, gsparams=galsim.GSParams())
    conv.drawImage(myImg1, method='no_pixel')
    np.testing.assert_array_almost_equal(
            myImg1.array, myImg2.array, 4,
            err_msg="Using AutoConvolve with GSParams() disagrees with expected result")

    check_basic(conv, "AutoConvolve(Moffat)")

    cen = galsim.PositionD(0,0)
    np.testing.assert_equal(conv2.centroid, cen)
    np.testing.assert_almost_equal(conv2.flux, psf.flux**2)
    np.testing.assert_array_less(conv2.xValue(cen), conv2.max_sb)

    # Check picklability
    do_pickle(conv2, lambda x: x.drawImage(method='no_pixel'))
    do_pickle(conv2)

    # Test photon shooting.
    do_shoot(conv2,myImg2,"AutoConvolve(Moffat)")

    # For a symmetric profile, AutoCorrelate is the same thing:
    conv2 = galsim.AutoCorrelate(psf)
    conv2.drawImage(myImg2, method='no_pixel')
    printval(myImg1, myImg2)
    np.testing.assert_array_almost_equal(
            myImg1.array, myImg2.array, 4,
            err_msg="Moffat convolved with self disagrees with AutoCorrelate result")

    # And check AutoCorrelate with gsparams:
    conv2 = galsim.AutoCorrelate(psf, gsparams=default_params)
    conv2.drawImage(myImg1, method='no_pixel')
    np.testing.assert_array_almost_equal(
            myImg1.array, myImg2.array, 4,
            err_msg="Using AutoCorrelate with default_params disagrees with expected result")
    conv2 = galsim.AutoCorrelate(psf, gsparams=galsim.GSParams())
    conv2.drawImage(myImg1, method='no_pixel')
    np.testing.assert_array_almost_equal(
            myImg1.array, myImg2.array, 4,
            err_msg="Using AutoCorrelate with GSParams() disagrees with expected result")

    cen = galsim.PositionD(0,0)
    np.testing.assert_equal(conv2.centroid, cen)
    np.testing.assert_almost_equal(conv2.flux, psf.flux**2)
    np.testing.assert_array_less(conv2.xValue(cen), conv2.max_sb)

    # Also check AutoConvolve with an asymmetric profile.
    # (AutoCorrelate with this profile is done below...)
    obj1 = galsim.Gaussian(sigma=3., flux=4).shift(-0.2, -0.4)
    obj2 = galsim.Gaussian(sigma=6., flux=1.3).shift(0.3, 0.3)
    add = galsim.Add(obj1, obj2)
    conv = galsim.Convolve([add, add])
    conv.drawImage(myImg1, method='no_pixel')
    autoconv = galsim.AutoConvolve(add)
    autoconv.drawImage(myImg2, method='no_pixel')
    printval(myImg1, myImg2)
    np.testing.assert_array_almost_equal(
            myImg1.array, myImg2.array, 4,
            err_msg="Asymmetric sum of Gaussians convolved with self disagrees with "+
            "AutoConvolve result")

    cen = 2. * add.centroid
    np.testing.assert_equal(autoconv.centroid, cen)
    np.testing.assert_almost_equal(autoconv.flux, add.flux**2)
    np.testing.assert_array_less(autoconv.xValue(cen), autoconv.max_sb)

    check_basic(autoconv, "AutoConvolve(asym)")

    # Should raise an exception for invalid arguments
    assert_raises(TypeError, galsim.AutoConvolve)
    assert_raises(TypeError, galsim.AutoConvolve, myImg1)
    assert_raises(TypeError, galsim.AutoConvolve, [psf])
    assert_raises(TypeError, galsim.AutoConvolve, psf, psf)
    assert_raises(TypeError, galsim.AutoConvolve, psf, realspace=False)
    assert_raises(TypeError, galsim.AutoConvolution)
    assert_raises(TypeError, galsim.AutoConvolution, myImg1)
    assert_raises(TypeError, galsim.AutoConvolution, [psf])
    assert_raises(TypeError, galsim.AutoConvolution, psf, psf)
    assert_raises(TypeError, galsim.AutoConvolution, psf, realspace=False)
Exemple #23
0
def test_gaussmom_smoke(g1_true, g2_true, wcs_g1, wcs_g2, weight_fac):
    rng = np.random.RandomState(seed=100)

    fwhm = 0.9
    image_size = 107
    cen = (image_size - 1) / 2
    gs_wcs = galsim.ShearWCS(0.125, galsim.Shear(g1=wcs_g1,
                                                 g2=wcs_g2)).jacobian()

    obj = galsim.Gaussian(fwhm=fwhm).shear(g1=g1_true,
                                           g2=g2_true).withFlux(400)
    im = obj.drawImage(nx=image_size,
                       ny=image_size,
                       wcs=gs_wcs,
                       method='no_pixel').array
    noise = np.sqrt(np.sum(im**2)) / 1e18
    wgt = np.ones_like(im) / noise**2
    scale = np.sqrt(gs_wcs.pixelArea())

    g1arr = []
    g2arr = []
    Tarr = []

    fitter = GaussMom(fwhm=fwhm * weight_fac)

    for _ in range(50):
        shift = rng.uniform(low=-scale / 2, high=scale / 2, size=2)
        xy = gs_wcs.toImage(galsim.PositionD(shift))

        im = obj.shift(dx=shift[0],
                       dy=shift[1]).drawImage(nx=image_size,
                                              ny=image_size,
                                              wcs=gs_wcs,
                                              method='no_pixel',
                                              dtype=np.float64).array

        jac = Jacobian(y=cen + xy.y,
                       x=cen + xy.x,
                       dudx=gs_wcs.dudx,
                       dudy=gs_wcs.dudy,
                       dvdx=gs_wcs.dvdx,
                       dvdy=gs_wcs.dvdy)

        _im = im + (rng.normal(size=im.shape) * noise)
        obs = Observation(image=_im, weight=wgt, jacobian=jac)

        # use a huge weight so that we get the raw moments back out
        res = fitter.go(obs=obs)
        if res['flags'] == 0:
            if weight_fac > 1:
                # for unweighted we need to convert e to g
                _g1, _g2 = e1e2_to_g1g2(res['e'][0], res['e'][1])
            else:
                # we are weighting by the round gaussian before shearing.
                # Turns out this gives e that equals the shear g
                _g1, _g2 = res['e'][0], res['e'][1]
            g1arr.append(_g1)
            g2arr.append(_g2)
            Tarr.append(res['pars'][4])

    g1 = np.mean(g1arr)
    g2 = np.mean(g2arr)

    gtol = 1e-9
    assert np.abs(g1 - g1_true) < gtol, (g1,
                                         np.std(g1arr) / np.sqrt(len(g1arr)))
    assert np.abs(g2 - g2_true) < gtol, (g2,
                                         np.std(g2arr) / np.sqrt(len(g2arr)))

    # T test should only pass when the weight function is constant so
    # weight_fac needs to be rally big
    if g1_true == 0 and g2_true == 0 and weight_fac > 1:
        T = np.mean(Tarr)
        assert np.abs(T - fwhm_to_T(fwhm)) < 1e-6
Exemple #24
0
def test_convolve_noise():
    """Test that noise propagation works properly for compount objects.
    """
    obj1 = galsim.Gaussian(sigma=1.7)
    obj2 = galsim.Gaussian(sigma=2.3)
    obj1.noise = galsim.UncorrelatedNoise(variance=0.3, scale=0.2)
    obj2.noise = galsim.UncorrelatedNoise(variance=0.5, scale=0.2)
    obj3 = galsim.Gaussian(sigma=2.9)

    # Convolve convolves the noise from a single component
    conv2 = galsim.Convolution([obj1,obj3])
    noise = galsim.Convolve([obj1.noise._profile, obj3, obj3])
    # xValue is too slow here.  Use drawImage to get variance.  (Just as CorrelatedNoise does.)
    variance = noise.drawImage(nx=1, ny=1, scale=1., method='sb')(1,1)
    np.testing.assert_almost_equal(conv2.noise.getVariance(), variance,
            err_msg = "Convolution of two objects did not correctly propagate noise varinace")
    conv2 = galsim.Convolution([obj2,obj3])
    noise = galsim.Convolve([obj2.noise._profile, obj3, obj3])
    variance = noise.drawImage(nx=1, ny=1, scale=1., method='sb')(1,1)
    np.testing.assert_almost_equal(conv2.noise.getVariance(), variance,
            err_msg = "Convolution of two objects did not correctly propagate noise varinace")

    # Convolution of multiple objects with noise attributes raises a warning and fails
    # to propagate noise properly.  (It takes the input noise from the first one.)
    conv2 = galsim.Convolution(obj1, obj2)
    conv3 = galsim.Convolution(obj1, obj2, obj3)
    with assert_warns(galsim.GalSimWarning):
        assert conv2.noise == obj1.noise.convolvedWith(obj2)
    with assert_warns(galsim.GalSimWarning):
        assert conv3.noise == obj1.noise.convolvedWith(galsim.Convolve(obj2,obj3))

    # Convolution with only one object uses that object's noise
    conv1 = galsim.Convolution(obj1)
    assert conv1.noise == obj1.noise

    # Other types don't propagate noise and give a warning about it.
    deconv = galsim.Deconvolution(obj2)
    autoconv = galsim.AutoConvolution(obj2)
    autocorr = galsim.AutoCorrelation(obj2)
    four = galsim.FourierSqrt(obj2)
    with assert_warns(galsim.GalSimWarning):
        assert deconv.noise is None
    with assert_warns(galsim.GalSimWarning):
        assert autoconv.noise is None
    with assert_warns(galsim.GalSimWarning):
        assert autocorr.noise is None
    with assert_warns(galsim.GalSimWarning):
        assert four.noise is None

    obj2.noise = None  # Remove obj2 noise for the rest.
    conv2 = galsim.Convolution(obj1, obj2)
    noise = galsim.Convolve([obj1.noise._profile, obj2, obj2])
    variance = noise.drawImage(nx=1, ny=1, scale=1., method='sb')(1,1)
    np.testing.assert_almost_equal(conv2.noise.getVariance(), variance,
            err_msg = "Convolution of two objects did not correctly propagate noise varinace")

    conv3 = galsim.Convolution(obj1, obj2, obj3)
    noise = galsim.Convolve([obj1.noise._profile, obj2, obj2, obj3, obj3])
    variance = noise.drawImage(nx=1, ny=1, scale=1., method='sb')(1,1)
    np.testing.assert_almost_equal(conv3.noise.getVariance(), variance,
            err_msg = "Convolution of three objects did not correctly propagate noise varinace")

    deconv = galsim.Deconvolution(obj2)
    autoconv = galsim.AutoConvolution(obj2)
    autocorr = galsim.AutoCorrelation(obj2)
    four = galsim.FourierSqrt(obj2)
    assert deconv.noise is None
    assert autoconv.noise is None
    assert autocorr.noise is None
    assert four.noise is None
Exemple #25
0
def test_hlr():
    """Test the calculateHLR method.
    """
    # Compare the calculation for a simple Gaussian.
    g1 = galsim.Gaussian(sigma=5, flux=1.7)

    print('g1 native hlr = ', g1.half_light_radius)
    print('g1.calculateHLR = ', g1.calculateHLR())
    print('nyquist scale = ', g1.nyquistScale())
    # These should be exactly equal.
    np.testing.assert_equal(
        g1.half_light_radius,
        g1.calculateHLR(),
        err_msg="Gaussian.calculateHLR() returned wrong value.")

    # Check for a convolution of two Gaussians.  Should be equivalent, but now will need to
    # do the calculation.
    g2 = galsim.Convolve(galsim.Gaussian(sigma=3, flux=1.3),
                         galsim.Gaussian(sigma=4, flux=23))
    test_hlr = g2.calculateHLR()
    print('g2.calculateHLR = ', test_hlr)
    print('ratio - 1 = ', test_hlr / g1.half_light_radius - 1)
    np.testing.assert_almost_equal(
        test_hlr / g1.half_light_radius,
        1.0,
        decimal=1,
        err_msg="Gaussian.calculateHLR() is not accurate.")

    # The default scale is only accurate to around 1 dp.  Using scale = 0.1 is accurate to 3 dp.
    # Note: Nyquist scale is about 4.23 for this profile.
    test_hlr = g2.calculateHLR(scale=0.1)
    print('g2.calculateHLR(scale=0.1) = ', test_hlr)
    print('ratio - 1 = ', test_hlr / g1.half_light_radius - 1)
    np.testing.assert_almost_equal(
        test_hlr / g1.half_light_radius,
        1.0,
        decimal=3,
        err_msg="Gaussian.calculateHLR(scale=0.1) is not accurate.")

    # Finally, we don't expect this to be accurate, but make sure the code can handle having
    # more than half the flux in the central pixel.
    test_hlr = g2.calculateHLR(scale=15)
    print('g2.calculateHLR(scale=15) = ', test_hlr)
    print('ratio - 1 = ', test_hlr / g1.half_light_radius - 1)
    np.testing.assert_almost_equal(
        test_hlr / g1.half_light_radius / 10,
        0.1,
        decimal=1,
        err_msg="Gaussian.calculateHLR(scale=15) is not accurate.")

    # Next, use an Exponential profile
    e1 = galsim.Exponential(scale_radius=5, flux=1.7)

    print('e1 native hlr = ', e1.half_light_radius)
    print('e1.calculateHLR = ', e1.calculateHLR())
    print('nyquist scale = ', e1.nyquistScale())
    # These should be exactly equal.
    np.testing.assert_equal(
        e1.half_light_radius,
        e1.calculateHLR(),
        err_msg="Exponential.calculateHLR() returned wrong value.")

    # Check for a convolution with a delta function.  Should be equivalent, but now will need to
    # do the calculation.
    e2 = galsim.Convolve(galsim.Exponential(scale_radius=5, flux=1.3),
                         galsim.Gaussian(sigma=1.e-4, flux=23))
    test_hlr = e2.calculateHLR()
    print('e2.calculateHLR = ', test_hlr)
    print('ratio - 1 = ', test_hlr / e1.half_light_radius - 1)
    np.testing.assert_almost_equal(
        test_hlr / e1.half_light_radius,
        1.0,
        decimal=1,
        err_msg="Exponential.calculateHLR() is not accurate.")

    # The default scale is only accurate to around 1 dp.  Using scale = 0.1 is accurate to 3 dp.
    # Note: Nyquist scale is about 1.57 for this profile.
    # We can also decrease the size, which should still be accurate, but maybe a little faster.
    # Go a bit more that 2*hlr in units of the pixels.
    size = int(2.2 * e1.half_light_radius / 0.1)
    test_hlr = e2.calculateHLR(scale=0.1, size=size)
    print('e2.calculateHLR(scale=0.1) = ', test_hlr)
    print('ratio - 1 = ', test_hlr / e1.half_light_radius - 1)
    np.testing.assert_almost_equal(
        test_hlr / e1.half_light_radius,
        1.0,
        decimal=3,
        err_msg="Exponential.calculateHLR(scale=0.1) is not accurate.")

    # Check that it works if the centroid is not at the origin
    e3 = e2.shift(2, 3)
    test_hlr = e3.calculateHLR(scale=0.1)
    print('e3.calculateHLR(scale=0.1) = ', test_hlr)
    print('ratio - 1 = ', test_hlr / e1.half_light_radius - 1)
    np.testing.assert_almost_equal(
        test_hlr / e1.half_light_radius,
        1.0,
        decimal=3,
        err_msg="shifted Exponential HLR is not accurate.")

    # Can set a centroid manually.  This should be equivalent to the default.
    print('e3.centroid = ', e3.centroid())
    test_hlr = e3.calculateHLR(scale=0.1, centroid=e3.centroid())
    np.testing.assert_almost_equal(
        test_hlr / e1.half_light_radius,
        1.0,
        decimal=3,
        err_msg="shifted HLR with explicit centroid is not accurate.")

    # The calculateHLR method can also return other radii like r90, rather than r50 using the
    # parameter flux_fraction.  This is also analytic for Exponential
    r90 = 3.889720170 * e1.scale_radius
    test_r90 = e2.calculateHLR(scale=0.1, flux_frac=0.9)
    print('r90 = ', r90)
    print('e2.calculateHLR(scale=0.1, flux_frac=0.9) = ', test_r90)
    print('ratio - 1 = ', test_r90 / r90 - 1)
    np.testing.assert_almost_equal(
        test_r90 / r90,
        1.0,
        decimal=3,
        err_msg="Exponential r90 calculation is not accurate.")

    # Check the image version.
    im = e1.drawImage(scale=0.1, nx=2048,
                      ny=2048)  # Needs to be large to get enough flux for the
    # image to get it to 3 digits of accuracy.
    test_hlr = im.calculateHLR()
    print('im.calculateHLR() = ', test_hlr)
    print('ratio - 1 = ', test_hlr / e1.half_light_radius - 1)
    np.testing.assert_almost_equal(
        test_hlr / e1.half_light_radius,
        1.0,
        decimal=3,
        err_msg="image.calculateHLR is not accurate.")

    # Check that a non-square image works correctly.  Also, not centered anywhere in particular.
    bounds = galsim.BoundsI(-1234, -1234 + 2048, 8234, 8234 + 2099)
    offset = galsim.PositionD(29, 1)
    im = e1.drawImage(scale=0.1, bounds=bounds, offset=offset)
    test_hlr = im.calculateHLR(center=im.trueCenter() + offset)
    print('im.calculateHLR() = ', test_hlr)
    print('ratio - 1 = ', test_hlr / e1.half_light_radius - 1)
    np.testing.assert_almost_equal(
        test_hlr / e1.half_light_radius,
        1.0,
        decimal=3,
        err_msg="non-square image.calculateHLR is not accurate.")
Exemple #26
0
def main(argv):

    cat_file_name = 'real_galaxy_catalog_example.fits'
    dir = 'data'
    # Make output directory if not already present.
    if not os.path.isdir('output'):
        os.mkdir('output')
    cube_file_name = os.path.join('output', 'cube_real.fits')
    psf_file_name = os.path.join('output', 'psf_real.fits')

    random_seed = 1512413
    sky_level = 1.e6  # ADU / arcsec^2
    pixel_scale = 0.16  # arcsec
    gal_flux = 1.e5  # arbitrary choice, makes nice (not too) noisy images
    psf_inner_fwhm = 0.1  # arcsec
    psf_outer_fwhm = 0.6  # arcsec
    psf_inner_fraction = 0.8  # fraction of total PSF flux in the inner Gaussian
    psf_outer_fraction = 0.2  # fraction of total PSF flux in the inner Gaussian
    redshift = 0.05
    ngal = 30

    # SED -----------------------------------------------------
    path, filename = os.path.split(__file__)
    datapath = os.path.abspath(os.path.join(path, "data/"))
    outpath = os.path.abspath(os.path.join(path, "output/"))
    random_seed = 1234567
    rng = galsim.BaseDeviate(random_seed)

    # read in SEDs
    SED_names = ['CWW_E_ext', 'CWW_Sbc_ext', 'CWW_Scd_ext', 'CWW_Im_ext']
    SEDs = {}
    for SED_name in SED_names:
        SED_filename = os.path.join(datapath, '{0}.sed'.format(SED_name))
        SED = galsim.SED(SED_filename, wave_type='Ang')
        SEDs[SED_name] = SED.withFluxDensity(target_flux_density=1.0,
                                             wavelength=500)

    filter_names = 'gri'
    filters = {}

    for filter_name in filter_names:
        filter_filename = os.path.join(datapath,
                                       'LSST_{0}.dat'.format(filter_name))
        filters[filter_name] = galsim.Bandpass(filter_filename)
        filters[filter_name] = filters[filter_name].thin(rel_err=1e-4)

    #--------------------------------------------------------------------
    real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=dir)

    psf = galsim.Gaussian(fwhm=psf_inner_fwhm, flux=psf_inner_fraction)
    # Draw the PSF with no noise.
    psf_image = psf.drawImage(scale=pixel_scale)
    # write to file
    psf_image.write(psf_file_name)
    # Build the images
    for k in range(ngal):

        # Initialize the random number generator we will be using.
        rng = galsim.UniformDeviate(random_seed + k)

        gal = galsim.RealGalaxy(real_galaxy_catalog, index=k)

        # Set the flux
        #gal = gal.withFlux(gal_flux)
        SED = SEDs['CWW_E_ext'].atRedshift(redshift)
        #SED       = SEDs['CWW_Sbc_ext'].atRedshift(redshift)
        galcol = galsim.Chromatic(gal, SED)
        final = galsim.Convolve([galcol, psf])

        dx = rng() - 0.5
        dy = rng() - 0.5

        # Draw the profile
        galcol = galsim.Chromatic(final, SED)
        for filter_name, filter_ in filters.iteritems():
            img = galsim.ImageF(78, 78, scale=pixel_scale)
            final.drawImage(filter_, image=img)
            out_filename = os.path.join(
                outpath, 'realcolor_{0}_{1}.fits'.format(filter_name, str(k)))
            galsim.fits.write(img, out_filename)
Exemple #27
0
def test_sersic():
    """Test the generation of a specific Sersic profile against a known result.
    """
    # Test Sersic
    savedImg = galsim.fits.read(os.path.join(imgdir, "sersic_3_1.fits"))
    dx = 0.2
    myImg = galsim.ImageF(savedImg.bounds, scale=dx)
    myImg.setCenter(0, 0)

    sersic = galsim.Sersic(n=3, flux=1, half_light_radius=1)
    sersic.drawImage(myImg, scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
        myImg.array,
        savedImg.array,
        5,
        err_msg="Using GSObject Sersic disagrees with expected result")

    # Check with default_params
    sersic = galsim.Sersic(n=3,
                           flux=1,
                           half_light_radius=1,
                           gsparams=default_params)
    sersic.drawImage(myImg, scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
        myImg.array,
        savedImg.array,
        5,
        err_msg=
        "Using GSObject Sersic with default_params disagrees with expected result"
    )
    sersic = galsim.Sersic(n=3,
                           flux=1,
                           half_light_radius=1,
                           gsparams=galsim.GSParams())
    sersic.drawImage(myImg, scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
        myImg.array,
        savedImg.array,
        5,
        err_msg=
        "Using GSObject Sersic with GSParams() disagrees with expected result")

    # Use non-unity values.
    sersic = galsim.Sersic(n=3, flux=1.7, half_light_radius=2.3)
    gsp = galsim.GSParams(xvalue_accuracy=1.e-8, kvalue_accuracy=1.e-8)
    sersic2 = galsim.Sersic(n=3, flux=1.7, half_light_radius=2.3, gsparams=gsp)
    assert sersic2 != sersic
    assert sersic2 == sersic.withGSParams(gsp)
    assert sersic2 == sersic.withGSParams(xvalue_accuracy=1.e-8,
                                          kvalue_accuracy=1.e-8)
    check_basic(sersic, "Sersic")

    # Test photon shooting.
    # Convolve with a small gaussian to smooth out the central peak.
    sersic2 = galsim.Convolve(sersic, galsim.Gaussian(sigma=0.3))
    do_shoot(sersic2, myImg, "Sersic")

    # Test kvalues
    do_kvalue(sersic, myImg, "Sersic")

    # Check picklability
    do_pickle(sersic, lambda x: x.drawImage(method='no_pixel'))
    do_pickle(sersic)

    # Now repeat everything using a truncation.  (Above had no truncation.)

    # Test Truncated Sersic
    # Don't use an integer truncation, since we don't want the truncation line to pass directly
    # through the center of a pixel where numerical rounding differences may decide whether the
    # value is zero or not.
    # This regression test compares to an image built using the code base at 82259f0
    savedImg = galsim.fits.read(os.path.join(imgdir, "sersic_3_1_10.fits"))
    myImg = galsim.ImageF(savedImg.bounds, scale=dx)
    myImg.setCenter(0, 0)

    sersic = galsim.Sersic(n=3, flux=1, half_light_radius=1, trunc=9.99)
    sersic.drawImage(myImg, scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
        myImg.array,
        savedImg.array,
        5,
        err_msg="Using truncated GSObject Sersic disagrees with expected result"
    )

    # Use non-unity values.
    test_flux = 1.8
    sersic = galsim.Sersic(n=3,
                           flux=test_flux,
                           half_light_radius=2.3,
                           trunc=5.9)
    cen = galsim.PositionD(0, 0)
    np.testing.assert_equal(sersic.centroid, cen)
    np.testing.assert_almost_equal(sersic.kValue(cen), (1 + 0j) * test_flux)
    np.testing.assert_almost_equal(sersic.flux, test_flux)
    np.testing.assert_almost_equal(sersic.xValue(cen), sersic.max_sb)

    check_basic(sersic, "Truncated Sersic")

    # Test photon shooting.
    # Convolve with a small gaussian to smooth out the central peak.
    sersic2 = galsim.Convolve(sersic, galsim.Gaussian(sigma=0.3))
    do_shoot(sersic2, myImg, "Truncated Sersic")

    # Test kvalues
    do_kvalue(sersic, myImg, "Truncated Sersic")

    # Check picklability
    do_pickle(sersic, lambda x: x.drawImage(method='no_pixel'))
    do_pickle(sersic)

    # n=4 is also called DeVaucouleurs
    sersic = galsim.Sersic(n=4, flux=1.7, half_light_radius=2.3, trunc=5.9)
    devauc = galsim.DeVaucouleurs(flux=1.7, half_light_radius=2.3, trunc=5.9)
    assert devauc == sersic
    check_basic(devauc, "DeVaucouleurs")

    # Check for normalization consistencies with kValue checks. xValues tested in test_sersic_radii.

    # For half-light radius specified truncated Sersic, with flux_untruncated flag set
    sersic = galsim.Sersic(n=3,
                           flux=test_flux,
                           half_light_radius=1,
                           trunc=10,
                           flux_untruncated=True)
    do_kvalue(
        sersic, myImg,
        "Truncated Sersic w/ flux_untruncated, half-light radius specified")

    # For scale radius specified Sersic
    sersic = galsim.Sersic(n=3, flux=test_flux, scale_radius=0.05)
    do_kvalue(sersic, myImg, "Sersic w/ scale radius specified")

    # For scale radius specified truncated Sersic
    sersic = galsim.Sersic(n=3, flux=test_flux, scale_radius=0.05, trunc=10)
    do_kvalue(sersic, myImg, "Truncated Sersic w/ scale radius specified")

    # For scale radius specified truncated Sersic, with flux_untruncated flag set
    sersic = galsim.Sersic(n=3,
                           flux=test_flux,
                           scale_radius=0.05,
                           trunc=10,
                           flux_untruncated=True)
    do_kvalue(sersic, myImg,
              "Truncated Sersic w/ flux_untruncated, scale radius specified")

    # Test severely truncated Sersic
    sersic = galsim.Sersic(n=4,
                           flux=test_flux,
                           half_light_radius=1,
                           trunc=1.45)
    do_kvalue(sersic, myImg, "Severely truncated n=4 Sersic")

    # Should raise an exception if both scale_radius and half_light_radius are provided.
    assert_raises(TypeError,
                  galsim.Sersic,
                  n=1.2,
                  scale_radius=3,
                  half_light_radius=1)
    assert_raises(TypeError, galsim.Sersic, n=1.2)
    assert_raises(TypeError,
                  galsim.DeVaucouleurs,
                  scale_radius=3,
                  half_light_radius=1)
    assert_raises(TypeError, galsim.DeVaucouleurs)

    # Allowed range is [0.3, 6.2]
    assert_raises(ValueError, galsim.Sersic, n=0.2, scale_radius=3)
    assert_raises(ValueError, galsim.Sersic, n=6.3, scale_radius=3)

    # trunc must be > sqrt(2) * hlr
    assert_raises(ValueError,
                  galsim.Sersic,
                  n=3,
                  half_light_radius=1,
                  trunc=1.4)
    assert_raises(ValueError,
                  galsim.DeVaucouleurs,
                  half_light_radius=1,
                  trunc=1.4)

    # Other errors
    assert_raises(TypeError, galsim.Sersic, scale_radius=3)
    assert_raises(ValueError, galsim.Sersic, n=3, scale_radius=3, trunc=-1)
    assert_raises(ValueError, galsim.DeVaucouleurs, scale_radius=3, trunc=-1)
Exemple #28
0
def draw_neighbor(neighbors_config=None, psf=None):
    gsparams = None
    if gsparams is None:
        gsparams = galsim.GSParams(maximum_fft_size=10240)

    #begin of possible kwargsneigh

    profile_type = neighbors_config['profile_type']
    tru_g1 = neighbors_config['tru_g1']
    tru_g2 = neighbors_config['tru_g2']

    if (profile_type == "Sersic"):
        sersiccut = neighbors_config['Sersic']['sersiccut']
        tru_rad = neighbors_config['Sersic']['tru_rad']
        tru_sersicn = neighbors_config['Sersic']['tru_sersicn']
        tru_sb = neighbors_config['Sersic']['tru_sb']
        tru_flux = np.pi * tru_rad * tru_rad * tru_sb

        if sersiccut is None:
            trunc = 0
        else:
            trunc = tru_rad * sersiccut

        gal = galsim.Sersic(n=tru_sersicn,
                            half_light_radius=tru_rad,
                            flux=tru_flux,
                            gsparams=gsparams,
                            trunc=trunc)
        # We make this profile elliptical
        gal = gal.shear(g1=tru_g1,
                        g2=tru_g2)  # This adds the ellipticity to the galaxy

    elif profile_type == "Gaussian":
        tru_flux = neighbors_config['Gaussian']['tru_flux']
        tru_sigma = neighbors_config['Gaussian']['tru_sigma']

        gal = galsim.Gaussian(flux=tru_flux,
                              sigma=tru_sigma,
                              gsparams=gsparams)
        # We make this profile elliptical
        gal = gal.shear(g1=tru_g1,
                        g2=tru_g2)  # This adds the ellipticity to the galaxy

    elif profile_type == 'EBulgeDisk':
        # A more advanced Bulge + Disk model
        # It needs GalSim version master, as of April 2017 (probably 1.5).

        # Get a Sersic bulge:
        tru_bulge_sersicn = neighbors_config['EBulgeDisk']['tru_bulge_sersicn']
        tru_bulge_rad = neighbors_config['EBulgeDisk']['tru_bulge_rad']
        tru_bulge_flux = neighbors_config['EBulgeDisk']['tru_bulge_flux']
        tru_disk_hlr = neighbors_config['EBulgeDisk']['tru_disk_hlr']
        tru_disk_tilt = neighbors_config['EBulgeDisk']['tru_disk_tilt']
        tru_disk_flux = neighbors_config['EBulgeDisk']['tru_disk_flux']
        tru_theta = neighbors_config['EBulgeDisk']['tru_theta']

        bulge = galsim.Sersic(n=tru_bulge_sersicn,
                              half_light_radius=tru_bulge_rad,
                              flux=tru_bulge_flux)
        # Make it elliptical:
        #bulge_ell = galsim.Shear(g=row["tru_bulge_g"], beta=row["tru_theta"] * galsim.degrees)
        #bulge = bulge.shear(bulge_ell)
        bulge = bulge.shear(g1=tru_g1, g2=tru_g2)

        # Get a disk
        scale_radius = tru_disk_hlr / galsim.Exponential._hlr_factor
        disk = galsim.InclinedExponential(
            inclination=tru_disk_tilt * galsim.degrees,
            scale_radius=scale_radius,
            flux=tru_disk_flux,
            scale_h_over_r=tru_disk_scale_h_over_r)
        # Rotate it in the same orientation as the bulge:
        disk = disk.rotate(tru_theta * galsim.degrees)

        # And we add those profiles, as done in GalSim demo3.py :
        gal = bulge + disk

    elif profile_type == 'Gaussian_PSF':  #you meant to draw stars all of them identical
        gal = galsim.Gaussian(flux=neighbors_config['Gaussian_PSF']['flux'],
                              sigma=neighbors_config['Gaussian_PSF']['flux'])
        #gal = gal.shear(g1=tru_g1, g2=tru_g2)

    elif profile_type == 'Stamp_PSF':
        raise RuntimeError(
            "Neighbors with Stamp_PSF, but PSF stamp is not defined in catalog"
        )
        gal = psf
        #gal = gal.shear(g1=tru_g1, g2=tru_g2)
    else:
        raise RuntimeError("Unknown galaxy profile!")

    return gal
Exemple #29
0
def test_convolve():
    nphotons = 1000000

    obj = galsim.Gaussian(flux=1.7, sigma=2.3)
    rng = galsim.UniformDeviate(1234)
    pa1 = obj.shoot(nphotons, rng)
    pa2 = obj.shoot(nphotons, rng)

    # If not correlated then convolve is deterministic
    conv_x = pa1.x + pa2.x
    conv_y = pa1.y + pa2.y
    conv_flux = pa1.flux * pa2.flux * nphotons

    np.testing.assert_allclose(np.sum(pa1.flux), 1.7)
    np.testing.assert_allclose(np.sum(pa2.flux), 1.7)
    np.testing.assert_allclose(np.sum(conv_flux), 1.7 * 1.7)

    np.testing.assert_allclose(np.sum(pa1.x**2) / nphotons, 2.3**2, rtol=0.01)
    np.testing.assert_allclose(np.sum(pa2.x**2) / nphotons, 2.3**2, rtol=0.01)
    np.testing.assert_allclose(np.sum(conv_x**2) / nphotons,
                               2. * 2.3**2,
                               rtol=0.01)

    np.testing.assert_allclose(np.sum(pa1.y**2) / nphotons, 2.3**2, rtol=0.01)
    np.testing.assert_allclose(np.sum(pa2.y**2) / nphotons, 2.3**2, rtol=0.01)
    np.testing.assert_allclose(np.sum(conv_y**2) / nphotons,
                               2. * 2.3**2,
                               rtol=0.01)

    pa3 = galsim.PhotonArray(nphotons)
    pa3.assignAt(0, pa1)  # copy from pa1
    pa3.convolve(pa2)
    np.testing.assert_allclose(pa3.x, conv_x)
    np.testing.assert_allclose(pa3.y, conv_y)
    np.testing.assert_allclose(pa3.flux, conv_flux)

    # If one of them is correlated, it is still deterministic.
    pa3.assignAt(0, pa1)
    pa3.setCorrelated()
    pa3.convolve(pa2)
    np.testing.assert_allclose(pa3.x, conv_x)
    np.testing.assert_allclose(pa3.y, conv_y)
    np.testing.assert_allclose(pa3.flux, conv_flux)

    pa3.assignAt(0, pa1)
    pa3.setCorrelated(False)
    pa2.setCorrelated()
    pa3.convolve(pa2)
    np.testing.assert_allclose(pa3.x, conv_x)
    np.testing.assert_allclose(pa3.y, conv_y)
    np.testing.assert_allclose(pa3.flux, conv_flux)

    # But if both are correlated, then it's not this simple.
    pa3.assignAt(0, pa1)
    pa3.setCorrelated()
    assert pa3.isCorrelated()
    assert pa2.isCorrelated()
    pa3.convolve(pa2)
    with assert_raises(AssertionError):
        np.testing.assert_allclose(pa3.x, conv_x)
    with assert_raises(AssertionError):
        np.testing.assert_allclose(pa3.y, conv_y)
    np.testing.assert_allclose(np.sum(pa3.flux), 1.7 * 1.7)
    np.testing.assert_allclose(np.sum(pa3.x**2) / nphotons,
                               2 * 2.3**2,
                               rtol=0.01)
    np.testing.assert_allclose(np.sum(pa3.y**2) / nphotons,
                               2 * 2.3**2,
                               rtol=0.01)

    # Error to have different lengths
    pa4 = galsim.PhotonArray(50, pa1.x[:50], pa1.y[:50], pa1.flux[:50])
    assert_raises(galsim.GalSimError, pa1.convolve, pa4)
Exemple #30
0
def test_area_norm():
    """Check that area_norm works as expected"""
    f606w_cat = galsim.RealGalaxyCatalog('AEGIS_F606w_catalog.fits',
                                         dir=image_dir)
    f814w_cat = galsim.RealGalaxyCatalog('AEGIS_F814w_catalog.fits',
                                         dir=image_dir)

    psf = galsim.Gaussian(fwhm=0.6)

    rng = galsim.BaseDeviate(5772)
    crg1 = galsim.ChromaticRealGalaxy([f606w_cat, f814w_cat],
                                      random=True,
                                      rng=rng.duplicate())
    crg2 = galsim.ChromaticRealGalaxy([f606w_cat, f814w_cat],
                                      random=True,
                                      rng=rng.duplicate(),
                                      area_norm=galsim.real.HST_area)
    assert crg1 != crg2
    LSST_i = galsim.Bandpass(os.path.join(bppath, "LSST_r.dat"), 'nm')
    obj1 = galsim.Convolve(crg1, psf)
    obj2 = galsim.Convolve(crg2, psf)
    im1 = obj1.drawImage(LSST_i, exptime=1, area=1)
    im2 = obj2.drawImage(LSST_i, exptime=1, area=galsim.real.HST_area)
    printval(im1, im2)
    np.testing.assert_array_almost_equal(im1.array, im2.array)
    np.testing.assert_almost_equal(
        obj1.noise.getVariance(),
        obj2.noise.getVariance() * galsim.real.HST_area**2)

    # area_norm is equivalant to an overall scaling
    crg3 = galsim.ChromaticRealGalaxy([f606w_cat, f814w_cat],
                                      random=True,
                                      rng=rng.duplicate())
    crg3 /= galsim.real.HST_area
    obj3 = galsim.Convolve(crg3, psf)
    im3 = obj3.drawImage(LSST_i, exptime=1, area=galsim.real.HST_area)
    np.testing.assert_array_almost_equal(im3.array, im2.array)
    np.testing.assert_almost_equal(obj3.noise.getVariance(),
                                   obj2.noise.getVariance())

    rg1 = galsim.RealGalaxy(f606w_cat, index=1)
    rg2 = galsim.RealGalaxy(f606w_cat, index=1, area_norm=galsim.real.HST_area)
    assert rg1 != rg2
    obj1 = galsim.Convolve(rg1, psf)
    obj2 = galsim.Convolve(rg2, psf)
    im1 = obj1.drawImage()
    im2 = obj2.drawImage(exptime=1, area=galsim.real.HST_area)
    printval(im1, im2)
    np.testing.assert_array_almost_equal(im1.array, im2.array)
    np.testing.assert_almost_equal(
        obj1.noise.getVariance(),
        obj2.noise.getVariance() * galsim.real.HST_area**2)

    # area_norm is equivalant to an overall scaling
    rg3 = galsim.RealGalaxy(f606w_cat, index=1)
    rg3 /= galsim.real.HST_area
    obj3 = galsim.Convolve(rg3, psf)
    im3 = obj3.drawImage(exptime=1, area=galsim.real.HST_area)
    np.testing.assert_array_almost_equal(im3.array, im2.array)
    np.testing.assert_almost_equal(obj3.noise.getVariance(),
                                   obj2.noise.getVariance())