Esempio n. 1
0
def test_exponential_flux_scaling():
    """Test flux scaling for Exponential.
    """
    # decimal point to go to for parameter value comparisons
    param_decimal = 12
    test_flux = 17.9
    test_scale = 1.8

    # init with scale and flux only (should be ok given last tests)
    obj = galsim.Exponential(scale_radius=test_scale, 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.Exponential(scale_radius=test_scale, 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.Exponential(scale_radius=test_scale, 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.Exponential(scale_radius=test_scale, 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.Exponential(scale_radius=test_scale, 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).")
Esempio n. 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.")
Esempio n. 3
0
def test_sigma():
    """Test the calculateMomentRadius method.
    """
    import time
    t1 = time.time()

    # 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(flux=e1.flux)
    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(flux=e1.flux, 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.")

    t2 = time.time()
    print 'time for %s = %.2f'%(funcname(),t2-t1)
Esempio n. 4
0
def galSimFakeSersic(flux,
                     gal,
                     psfImage=None,
                     scaleRad=False,
                     returnObj=True,
                     expAll=False,
                     devAll=False,
                     plotFake=False,
                     trunc=0,
                     drawMethod="auto",
                     addPoisson=False,
                     scale=1.0,
                     transform=None,
                     addShear=False):
    """
    Make a fake single Sersic galaxy using the galSim.Sersic function

    Inputs: total flux of the galaxy, and a record array that stores the
    necessary parameters [reffPix, nSersic, axisRatio, posAng]

    Output: a 2-D image array of the galaxy model  OR
            a GalSim object of the model

    Options:
        psfImage:     PSF image for convolution
        trunc:        Flux of Sersic models will truncate at trunc * reffPix
                      radius; trunc=0 means no truncation
        drawMethod:   The method for drawImage: ['auto', 'fft', 'real_space']
        addPoisson:   Add Poisson noise
        plotFake:     Generate a PNG figure of the model
        expAll:       Input model will be seen as nSersic=1
        devAll:       Input model will be seen as nSersic=4
        returnObj:    If TRUE, will return the GSObj, instead of the image array
    """

    # Convert the numpy.float32 into normal float format
    nSersic = float(gal["sersic_n"])
    reff = float(gal["reff"])
    axisRatio = float(gal["b_a"])
    posAng = float(gal["theta"])

    # Truncate the flux at trunc x reff
    if trunc > 0:
        trunc = trunc * reff

    # Make sure Sersic index is not too large
    if nSersic > 6.0:
        raise ValueError("Sersic index is too large! Should be <= 6.0")
    # Check the axisRatio value
    if axisRatio <= 0.24:
        raise ValueError("Axis Ratio is too small! Should be >= 0.24")

    # Make the Sersic model based on flux, re, and Sersic index
    if nSersic == 1.0 or expAll:
        if scaleRad:
            serObj = galsim.Exponential(scale_radius=reff)
        else:
            serObj = galsim.Exponential(half_light_radius=reff)
        if expAll:
            print " * This model is treated as a n=1 Exponential disk : %d" % (
                gal["ID"])
    elif nSersic == 4.0 or devAll:
        serObj = galsim.DeVaucouleurs(half_light_radius=reff, trunc=trunc)
        if devAll:
            print " * This model is treated as a n=4 De Vaucouleurs model: %d" % (
                gal["ID"])
    elif nSersic <= 0.9:
        serObj = galsim.Sersic(nSersic, half_light_radius=reff)
    else:
        serObj = galsim.Sersic(nSersic, half_light_radius=reff, trunc=trunc)

    # If necessary, apply the Axis Ratio (q=b/a) using the Shear method
    if axisRatio < 1.0:
        serObj = serObj.shear(q=axisRatio, beta=0.0 * galsim.degrees)

    # If necessary, apply the Position Angle (theta) using the Rotate method
    #if posAng != 0.0 or posAng != 180.0:
    serObj = serObj.rotate((90.0 - posAng) * galsim.degrees)

    # If necessary, apply addtion shear (e.g. for weak lensing test)
    if addShear:
        try:
            g1 = float(gal['g1'])
            g2 = float(gal['g2'])
            serObj = serObj.shear(g1=g1, g2=g2)
        except ValueError:
            # TODO: Should check other options
            warnings.warn(
                "Can not find g1 or g2 in the input! No shear has been added!")

    #do the transformation from sky to pixel coordinates, if given
    if transform is not None:
        serObj = serObj.transform(*tuple(transform.ravel()))

    # Convolve the Sersic model using the provided PSF image
    if psfImage is not None:
        # Convert the PSF Image Array into a GalSim Object
        # Norm=True by default
        psfObj = arrayToGSObj(psfImage, norm=True)
        serFinal = galsim.Convolve([serObj, psfObj])
    else:
        serFinal = serObj

    # Pass the flux to the object
    serFinal = serFinal.withFlux(float(flux))

    # Make a PNG figure of the fake galaxy to check if everything is Ok
    # TODO: For test, should be removed later
    if plotFake:
        plotFakeGalaxy(serFinal, galID=gal['ID'])

    # Now, by default, the function will just return the GSObj
    if returnObj:
        return serFinal
    else:
        return galSimDrawImage(serFinal,
                               method=drawMethod,
                               scale=scale,
                               addPoisson=addPoisson)
Esempio n. 5
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

    # To turn on the debugging messages:
    #logger.setLevel(logging.DEBUG)

    # 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]
        logger.info('psf %d: %s', ipsf + 1, psf)
        # Note that this implicitly calls str(psf).  We've made an effort to give all GalSim
        # objects an informative but relatively succinct str representation.  Some details may
        # be missing, but it should look essentially like how you would create the object.
        logger.debug('repr = %r', psf)
        # The repr() version are a bit more pedantic in form and should be completely informative,
        # to the point where two objects that are not identical should never have equal repr
        # strings. As such the repr strings may in some cases be somewhat unwieldy.  For instance,
        # since we set non-default gsparams in these, the repr includes that information, but
        # it is omitted from the str for brevity.
        for igal in range(len(gals)):
            gal = gals[igal]
            gal_name = gal_names[igal]
            logger.info('   galaxy %d: %s', igal + 1, gal)
            logger.debug('   repr = %r', gal)
            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 + 1)

                # 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 + 1)
                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: flux = %.2e, hlr = %.2f, ellip = (%.2f,%.2f)',
                    k, 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)
Esempio n. 6
0
def _run_ml(*, n_sims, rng, dudx, dudy, dvdx, dvdy):
    """Run metacal on an image composed of stamps w/ constant noise.

    Parameters
    ----------
    n_sims : int
        The number of objects to run.
    rng : np.random.RandomState
        An RNG to use.
    dudx : float
        The du/dx Jacobian component.
    dudy : float
        The du/dy Jacobian component.
    dydx : float
        The dv/dx Jacobian component.
    dvdy : float
        The dv/dy Jacobian component.

    Returns
    -------
    result : dict
        A dictionary with each of the metacal catalogs.
    """
    method = 'no_pixel'

    stamp_size = 33
    psf_stamp_size = 33

    cen = (stamp_size - 1) / 2
    psf_cen = (psf_stamp_size - 1) / 2

    s2n = 1e16
    flux = 1e6

    galsim_jac = galsim.JacobianWCS(dudx=dudx, dudy=dudy, dvdx=dvdx, dvdy=dvdy)

    gal = galsim.Exponential(half_light_radius=0.5).withFlux(flux)
    psf = galsim.Gaussian(fwhm=0.9).withFlux(1)
    obj = galsim.Convolve(gal, psf)
    obj_im = obj.drawImage(nx=111, ny=111).array
    noise = np.sqrt(np.sum(obj_im**2)) / s2n

    data = []
    for ind in tqdm.trange(n_sims):
        ################################
        # make the obs

        # psf
        psf_im = psf.drawImage(nx=psf_stamp_size,
                               ny=psf_stamp_size,
                               wcs=galsim_jac,
                               method=method).array
        psf_noise = np.sqrt(np.sum(psf_im**2)) / 10000
        wgt = np.ones_like(psf_im) / psf_noise**2
        psf_im += (rng.normal(size=psf_im.shape) * psf_noise)
        psf_jac = ngmix.Jacobian(x=psf_cen,
                                 y=psf_cen,
                                 dudx=dudx,
                                 dudy=dudy,
                                 dvdx=dvdx,
                                 dvdy=dvdy)
        psf_obs = ngmix.Observation(image=psf_im, weight=wgt, jacobian=psf_jac)

        # now render object
        scale = psf_jac.scale
        shift = rng.uniform(low=-scale / 2, high=scale / 2, size=2)
        _obj = obj.shift(dx=shift[0], dy=shift[1])
        xy = galsim_jac.toImage(galsim.PositionD(shift))
        im = _obj.drawImage(nx=stamp_size,
                            ny=stamp_size,
                            wcs=galsim_jac,
                            method=method).array
        jac = ngmix.Jacobian(x=cen + xy.x,
                             y=cen + xy.y,
                             dudx=dudx,
                             dudy=dudy,
                             dvdx=dvdx,
                             dvdy=dvdy)
        wgt = np.ones_like(im) / noise**2
        nse = rng.normal(size=im.shape) * noise
        im += (rng.normal(size=im.shape) * noise)
        obs = ngmix.Observation(image=im,
                                weight=wgt,
                                noise=nse,
                                bmask=np.zeros_like(im, dtype=np.int32),
                                ormask=np.zeros_like(im, dtype=np.int32),
                                jacobian=jac,
                                psf=psf_obs)

        # build the mbobs
        mbobs = ngmix.MultiBandObsList()
        obslist = ngmix.ObsList()
        obslist.append(obs)
        mbobs.append(obslist)

        mbobs.meta['id'] = ind + 1
        # these settings do not matter that much I think
        mbobs[0].meta['Tsky'] = 1
        mbobs[0].meta['magzp_ref'] = 26.5
        mbobs[0][0].meta['orig_col'] = ind + 1
        mbobs[0][0].meta['orig_row'] = ind + 1

        ################################
        # run the fitters
        try:
            res = _run_ml_fitter(mbobs, rng)
        except Exception as e:
            print('err:', e, type(e))
            res = None

        if res is not None:
            data.append(res)

    if len(data) > 0:
        res = data
    else:
        res = None

    return res
Esempio n. 7
0
def test_operations_simple():
    """Simple test of operations on InterpolatedImage: shear, magnification, rotation, shifting."""
    import time
    t1 = time.time()

    # Make some nontrivial image that can be described in terms of sums and convolutions of
    # GSObjects.  We want this to be somewhat hard to describe, but should be at least
    # critically-sampled, so put in an Airy PSF.
    gal_flux = 1000.
    pix_scale = 0.03  # arcsec
    bulge_frac = 0.3
    bulge_hlr = 0.3  # arcsec
    bulge_e = 0.15
    bulge_pos_angle = 30. * galsim.degrees
    disk_hlr = 0.6  # arcsec
    disk_e = 0.5
    disk_pos_angle = 60. * galsim.degrees
    lam = 800  # nm    NB: don't use lambda - that's a reserved word.
    tel_diam = 2.4  # meters
    lam_over_diam = lam * 1.e-9 / tel_diam  # radians
    lam_over_diam *= 206265  # arcsec
    im_size = 512

    bulge = galsim.Sersic(4, half_light_radius=bulge_hlr)
    bulge.applyShear(e=bulge_e, beta=bulge_pos_angle)
    disk = galsim.Exponential(half_light_radius=disk_hlr)
    disk.applyShear(e=disk_e, beta=disk_pos_angle)
    gal = bulge_frac * bulge + (1. - bulge_frac) * disk
    gal.setFlux(gal_flux)
    psf = galsim.Airy(lam_over_diam)
    pix = galsim.Pixel(pix_scale)
    obj = galsim.Convolve(gal, psf, pix)
    im = obj.draw(dx=pix_scale)

    # Turn it into an InterpolatedImage with default param settings
    int_im = galsim.InterpolatedImage(im)

    # Shear it, and compare with expectations from GSObjects directly
    test_g1 = -0.07
    test_g2 = 0.1
    test_decimal = 2  # in % difference, i.e. 2 means 1% agreement
    comp_region = 30  # compare the central region of this linear size
    test_int_im = int_im.createSheared(g1=test_g1, g2=test_g2)
    ref_obj = obj.createSheared(g1=test_g1, g2=test_g2)
    # make large images
    im = galsim.ImageD(im_size, im_size)
    ref_im = galsim.ImageD(im_size, im_size)
    test_int_im.draw(image=im, dx=pix_scale)
    ref_obj.draw(image=ref_im, dx=pix_scale)
    # define subregion for comparison
    new_bounds = galsim.BoundsI(1, comp_region, 1, comp_region)
    new_bounds.shift((im_size - comp_region) / 2, (im_size - comp_region) / 2)
    im_sub = im.subImage(new_bounds)
    ref_im_sub = ref_im.subImage(new_bounds)
    diff_im = im_sub - ref_im_sub
    rel = diff_im / im_sub
    zeros_arr = np.zeros((comp_region, comp_region))
    # require relative difference to be smaller than some amount
    np.testing.assert_array_almost_equal(
        rel.array,
        zeros_arr,
        test_decimal,
        err_msg='Sheared InterpolatedImage disagrees with reference')

    # Magnify it, and compare with expectations from GSObjects directly
    test_mag = 1.08
    test_decimal = 2  # in % difference, i.e. 2 means 1% agreement
    comp_region = 30  # compare the central region of this linear size
    test_int_im = int_im.createMagnified(test_mag)
    ref_obj = obj.createMagnified(test_mag)
    # make large images
    im = galsim.ImageD(im_size, im_size)
    ref_im = galsim.ImageD(im_size, im_size)
    test_int_im.draw(image=im, dx=pix_scale)
    ref_obj.draw(image=ref_im, dx=pix_scale)
    # define subregion for comparison
    new_bounds = galsim.BoundsI(1, comp_region, 1, comp_region)
    new_bounds.shift((im_size - comp_region) / 2, (im_size - comp_region) / 2)
    im_sub = im.subImage(new_bounds)
    ref_im_sub = ref_im.subImage(new_bounds)
    diff_im = im_sub - ref_im_sub
    rel = diff_im / im_sub
    zeros_arr = np.zeros((comp_region, comp_region))
    # require relative difference to be smaller than some amount
    np.testing.assert_array_almost_equal(
        rel.array,
        zeros_arr,
        test_decimal,
        err_msg='Magnified InterpolatedImage disagrees with reference')

    # Lens it (shear and magnify), and compare with expectations from GSObjects directly
    test_g1 = -0.03
    test_g2 = -0.04
    test_mag = 0.74
    test_decimal = 2  # in % difference, i.e. 2 means 1% agreement
    comp_region = 30  # compare the central region of this linear size
    test_int_im = int_im.createLensed(test_g1, test_g2, test_mag)
    ref_obj = obj.createLensed(test_g1, test_g2, test_mag)
    # make large images
    im = galsim.ImageD(im_size, im_size)
    ref_im = galsim.ImageD(im_size, im_size)
    test_int_im.draw(image=im, dx=pix_scale)
    ref_obj.draw(image=ref_im, dx=pix_scale)
    # define subregion for comparison
    new_bounds = galsim.BoundsI(1, comp_region, 1, comp_region)
    new_bounds.shift((im_size - comp_region) / 2, (im_size - comp_region) / 2)
    im_sub = im.subImage(new_bounds)
    ref_im_sub = ref_im.subImage(new_bounds)
    diff_im = im_sub - ref_im_sub
    rel = diff_im / im_sub
    zeros_arr = np.zeros((comp_region, comp_region))
    # require relative difference to be smaller than some amount
    np.testing.assert_array_almost_equal(
        rel.array,
        zeros_arr,
        test_decimal,
        err_msg='Lensed InterpolatedImage disagrees with reference')

    # Rotate it, and compare with expectations from GSObjects directly
    test_rot_angle = 32. * galsim.degrees
    test_decimal = 2  # in % difference, i.e. 2 means 1% agreement
    comp_region = 30  # compare the central region of this linear size
    test_int_im = int_im.createRotated(test_rot_angle)
    ref_obj = obj.createRotated(test_rot_angle)
    # make large images
    im = galsim.ImageD(im_size, im_size)
    ref_im = galsim.ImageD(im_size, im_size)
    test_int_im.draw(image=im, dx=pix_scale)
    ref_obj.draw(image=ref_im, dx=pix_scale)
    # define subregion for comparison
    new_bounds = galsim.BoundsI(1, comp_region, 1, comp_region)
    new_bounds.shift((im_size - comp_region) / 2, (im_size - comp_region) / 2)
    im_sub = im.subImage(new_bounds)
    ref_im_sub = ref_im.subImage(new_bounds)
    diff_im = im_sub - ref_im_sub
    rel = diff_im / im_sub
    zeros_arr = np.zeros((comp_region, comp_region))
    # require relative difference to be smaller than some amount
    np.testing.assert_array_almost_equal(
        rel.array,
        zeros_arr,
        test_decimal,
        err_msg='Rotated InterpolatedImage disagrees with reference')

    # Shift it, and compare with expectations from GSObjects directly
    x_shift = -0.31
    y_shift = 0.87
    test_decimal = 2  # in % difference, i.e. 2 means 1% agreement
    comp_region = 30  # compare the central region of this linear size
    test_int_im = int_im.createShifted(x_shift, y_shift)
    ref_obj = obj.createShifted(x_shift, y_shift)
    # make large images
    im = galsim.ImageD(im_size, im_size)
    ref_im = galsim.ImageD(im_size, im_size)
    test_int_im.draw(image=im, dx=pix_scale)
    ref_obj.draw(image=ref_im, dx=pix_scale)
    # define subregion for comparison
    new_bounds = galsim.BoundsI(1, comp_region, 1, comp_region)
    new_bounds.shift((im_size - comp_region) / 2, (im_size - comp_region) / 2)
    im_sub = im.subImage(new_bounds)
    ref_im_sub = ref_im.subImage(new_bounds)
    diff_im = im_sub - ref_im_sub
    rel = diff_im / im_sub
    zeros_arr = np.zeros((comp_region, comp_region))
    # require relative difference to be smaller than some amount
    np.testing.assert_array_almost_equal(
        rel.array,
        zeros_arr,
        test_decimal,
        err_msg='Shifted InterpolatedImage disagrees with reference')

    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
Esempio n. 8
0
def test_drawK_Exponential_Moffat():
    """Test the drawK function using known symmetries of the Exponential Hankel transform (which is
    a Moffat with beta=1.5).

    See http://mathworld.wolfram.com/HankelTransform.html.
    """
    import time
    t1 = time.time()

    test_flux = 4.1  # Choose a non-unity flux
    test_scale_radius = 13.  # ...likewise for scale_radius
    test_imsize = 45  # Dimensions of comparison image, doesn't need to be large

    # Define an Exponential GSObject
    gal = galsim.Exponential(scale_radius=test_scale_radius, flux=test_flux)
    # Then define a related object which is in fact the opposite number in the Hankel transform pair
    # For the Exponential we need a Moffat, with scale_radius=1/scale_radius.  The total flux under
    # this Moffat with unit amplitude at r=0 is is pi * scale_radius**(-2) / (beta - 1)
    #  = 2. * pi * scale_radius**(-2) in this case, so it works analagously to the Gaussian above.
    gal_hankel = galsim.Moffat(beta=1.5,
                               scale_radius=1. / test_scale_radius,
                               flux=test_flux * 2. * np.pi /
                               test_scale_radius**2)

    # Do a basic flux test: the total flux of the gal should equal gal_Hankel(k=(0, 0))
    np.testing.assert_almost_equal(
        gal.getFlux(),
        gal_hankel.xValue(galsim.PositionD(0., 0.)),
        decimal=12,
        err_msg=
        "Test object flux does not equal k=(0, 0) mode of its Hankel transform conjugate."
    )

    image_test = galsim.ImageD(test_imsize, test_imsize)
    rekimage_test = galsim.ImageD(test_imsize, test_imsize)
    imkimage_test = galsim.ImageD(test_imsize, test_imsize)

    # Then compare these two objects at a couple of different scale (reasonably matched for size)
    for scale_test in (0.15 / test_scale_radius, 0.6 / test_scale_radius):
        gal.drawK(re=rekimage_test, im=imkimage_test, scale=scale_test)
        gal_hankel.draw(image_test,
                        scale=scale_test,
                        use_true_center=False,
                        normalization="sb")
        np.testing.assert_array_almost_equal(
            rekimage_test.array,
            image_test.array,
            decimal=12,
            err_msg=
            "Test object drawK() and draw() from Hankel conjugate do not match for grid "
            + "spacing scale = " + str(scale_test))
        np.testing.assert_array_almost_equal(
            imkimage_test.array,
            np.zeros_like(imkimage_test.array),
            decimal=12,
            err_msg=
            "Non-zero imaginary part for drawK from test object that is purely centred on "
            + "the origin.")

    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
Esempio n. 9
0
def test_offset():
    """Test the offset parameter to the draw and drawShoot function.
    """
    import time
    t1 = time.time()

    scale = 0.23

    # Use some more exact GSParams.  We'll be comparing FFT images to real-space convolved values,
    # so we don't want to suffer from our overall accuracy being only about 10^-3.
    # Update: It turns out the only one I needed to reduce to obtain the accuracy I wanted
    # below is maxk_threshold.  Perhaps this is a sign that we ought to lower it in general?
    params = galsim.GSParams(maxk_threshold=1.e-4)

    # We use a simple Exponential for our object:
    gal = galsim.Exponential(flux=test_flux, scale_radius=0.5, gsparams=params)
    pix = galsim.Pixel(scale, gsparams=params)
    obj = galsim.Convolve([gal, pix], gsparams=params)

    # The shapes of the images we will build
    # Make sure all combinations of odd/even are represented.
    shape_list = [(256, 256), (256, 243), (249, 260), (255, 241), (270, 260)]

    # Some reasonable (x,y) values at which to test the xValues (near the center)
    xy_list = [(128, 128), (123, 131), (126, 124)]

    # The offsets to test
    offset_list = [(1, -3), (0.3, -0.1), (-2.3, -1.2)]

    # Make the images somewhat large so the moments are measured accurately.
    for nx, ny in shape_list:
        #print '\n\n\nnx,ny = ',nx,ny

        # First check that the image agrees with our calculation of the center
        cenx = (nx + 1.) / 2.
        ceny = (ny + 1.) / 2.
        #print 'cen = ',cenx,ceny
        im = galsim.ImageD(nx, ny, scale=scale)
        true_center = im.bounds.trueCenter()
        np.testing.assert_almost_equal(
            cenx, true_center.x, 6,
            "im.bounds.trueCenter().x is wrong for (nx,ny) = %d,%d" % (nx, ny))
        np.testing.assert_almost_equal(
            ceny, true_center.y, 6,
            "im.bounds.trueCenter().y is wrong for (nx,ny) = %d,%d" % (nx, ny))

        # Check that the default draw command puts the centroid in the center of the image.
        obj.draw(im, normalization='sb')
        moments = getmoments(im)
        #print 'moments = ',moments
        np.testing.assert_almost_equal(
            moments[0], cenx, 5,
            "obj.draw(im) not centered correctly for (nx,ny) = %d,%d" %
            (nx, ny))
        np.testing.assert_almost_equal(
            moments[1], ceny, 5,
            "obj.draw(im) not centered correctly for (nx,ny) = %d,%d" %
            (nx, ny))

        # Test that a few pixel values match xValue.
        # Note: we don't expect the FFT drawn image to match the xValues precisely, since the
        # latter use real-space convolution, so they should just match to our overall accuracy
        # requirement, which is something like 1.e-3 or so.  But an image of just the galaxy
        # should use real-space drawing, so should be pretty much exact.
        im2 = galsim.ImageD(nx, ny, scale=scale)
        gal.draw(im2, normalization='sb')
        for x, y in xy_list:
            #print 'x,y = ',x,y
            #print 'im(x,y) = ',im(x,y)
            u = (x - cenx) * scale
            v = (y - ceny) * scale
            #print 'xval(x-cenx,y-ceny) = ',obj.xValue(galsim.PositionD(u,v))
            np.testing.assert_almost_equal(
                im(x, y), obj.xValue(galsim.PositionD(u, v)), 2,
                "im(%d,%d) does not match xValue(%f,%f)" % (x, y, u, v))
            np.testing.assert_almost_equal(
                im2(x, y), gal.xValue(galsim.PositionD(u, v)), 6,
                "im2(%d,%d) does not match xValue(%f,%f)" % (x, y, u, v))

        # Check that offset moves the centroid by the right amount.
        for offx, offy in offset_list:
            # For integer offsets, we expect the centroids to come out pretty much exact.
            # (Only edge effects of the image should produce any error, and those are very small.)
            # However, for non-integer effects, we don't actually expect the centroids to be
            # right, even with perfect image rendering.  To see why, imagine using a delta function
            # for the galaxy.  The centroid changes discretely, not continuously as the offset
            # varies.  The effect isn't as severe of course for our Exponential, but the effect
            # is still there in part.  Hence, only use 2 decimal places for non-integer offsets.
            if offx == int(offx) and offy == int(offy):
                decimal = 4
            else:
                decimal = 2

            #print 'offx,offy = ',offx,offy
            offset = galsim.PositionD(offx, offy)
            obj.draw(im, normalization='sb', offset=offset)
            moments = getmoments(im)
            #print 'moments = ',moments
            np.testing.assert_almost_equal(
                moments[0], cenx + offx, decimal,
                "obj.draw(im,offset) not centered correctly for (nx,ny) = %d,%d"
                % (nx, ny))
            np.testing.assert_almost_equal(
                moments[1], ceny + offy, decimal,
                "obj.draw(im,offset) not centered correctly for (nx,ny) = %d,%d"
                % (nx, ny))
            # Test that a few pixel values match xValue
            gal.draw(im2, normalization='sb', offset=offset)
            for x, y in xy_list:
                #print 'x,y = ',x,y
                #print 'im(x,y) = ',im(x,y)
                u = (x - cenx - offx) * scale
                v = (y - ceny - offy) * scale
                #print 'xval(x-cenx-offx,y-ceny-offy) = ',obj.xValue(galsim.PositionD(u,v))
                np.testing.assert_almost_equal(
                    im(x, y), obj.xValue(galsim.PositionD(u, v)), 2,
                    "im(%d,%d) does not match xValue(%f,%f)" % (x, y, u, v))
                np.testing.assert_almost_equal(
                    im2(x, y), gal.xValue(galsim.PositionD(u, v)), 6,
                    "im2(%d,%d) does not match xValue(%f,%f)" % (x, y, u, v))

            # Check that applyShift also moves the centroid by the right amount.
            shifted_obj = obj.createShifted(offset * scale)
            shifted_obj.draw(im, normalization='sb')
            moments = getmoments(im)
            #print 'moments = ',moments
            np.testing.assert_almost_equal(
                moments[0], cenx + offx, decimal,
                "shifted_obj.draw(im) not centered correctly for (nx,ny) = %d,%d"
                % (nx, ny))
            np.testing.assert_almost_equal(
                moments[1], ceny + offy, decimal,
                "shifted_obj.draw(im) not centered correctly for (nx,ny) = %d,%d"
                % (nx, ny))
            # Test that a few pixel values match xValue
            shifted_gal = gal.createShifted(offset * scale)
            shifted_gal.draw(im2, normalization='sb')
            for x, y in xy_list:
                #print 'x,y = ',x,y
                #print 'im(x,y) = ',im(x,y)
                u = (x - cenx) * scale
                v = (y - ceny) * scale
                #print 'shifted xval(x-cenx,y-ceny) = ',shifted_obj.xValue(galsim.PositionD(u,v))
                np.testing.assert_almost_equal(
                    im(x, y), shifted_obj.xValue(galsim.PositionD(u, v)), 2,
                    "im(%d,%d) does not match shifted xValue(%f,%f)" %
                    (x, y, x - cenx, y - ceny))
                np.testing.assert_almost_equal(
                    im2(x, y), shifted_gal.xValue(galsim.PositionD(u, v)), 6,
                    "im2(%d,%d) does not match shifted xValue(%f,%f)" %
                    (x, y, x - cenx, y - ceny))
                u = (x - cenx - offx) * scale
                v = (y - ceny - offy) * scale
                #print 'xval(x-cenx-offx,y-ceny-offy) = ',obj.xValue(galsim.PositionD(u,v))
                np.testing.assert_almost_equal(
                    im(x, y), obj.xValue(galsim.PositionD(u, v)), 2,
                    "im(%d,%d) does not match xValue(%f,%f)" % (x, y, u, v))
                np.testing.assert_almost_equal(
                    im2(x, y), gal.xValue(galsim.PositionD(u, v)), 6,
                    "im2(%d,%d) does not match xValue(%f,%f)" % (x, y, u, v))

        # Chcek the image's definition of the nominal center
        nom_cenx = (nx + 2) / 2
        nom_ceny = (ny + 2) / 2
        nominal_center = im.bounds.center()
        np.testing.assert_almost_equal(
            nom_cenx, nominal_center.x, 6,
            "im.bounds.center().x is wrong for (nx,ny) = %d,%d" % (nx, ny))
        np.testing.assert_almost_equal(
            nom_ceny, nominal_center.y, 6,
            "im.bounds.center().y is wrong for (nx,ny) = %d,%d" % (nx, ny))

        # Check that use_true_center = false is consistent with an offset by 0 or 0.5 pixels.
        obj.draw(im, normalization='sb', use_true_center=False)
        moments = getmoments(im)
        #print 'moments = ',moments
        np.testing.assert_almost_equal(
            moments[0], nom_cenx, 4,
            "obj.draw(im, use_true_center=False) not centered correctly for (nx,ny) = %d,%d"
            % (nx, ny))
        np.testing.assert_almost_equal(
            moments[1], nom_ceny, 4,
            "obj.draw(im, use_true_center=False) not centered correctly for (nx,ny) = %d,%d"
            % (nx, ny))
        cen_offset = galsim.PositionD(nom_cenx - cenx, nom_ceny - ceny)
        #print 'cen_offset = ',cen_offset
        obj.draw(im2, normalization='sb', offset=cen_offset)
        np.testing.assert_array_almost_equal(
            im.array, im2.array, 6,
            "obj.draw(im, offset=%f,%f) different from use_true_center=False")

    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
Esempio n. 10
0
def test_draw_methods():
    """Test the the different method options do the right thing.
    """
    import time
    t1 = time.time()

    # We use a simple Exponential for our object:
    obj = galsim.Exponential(flux=test_flux, scale_radius=1.09)
    test_scale = 0.28
    pix = galsim.Pixel(scale=test_scale)
    obj_pix = galsim.Convolve(obj, pix)

    N = 64
    im1 = galsim.ImageD(N, N, scale=test_scale)

    # auto and fft should be equivalent to drawing obj_pix with no_pixel
    im1 = obj.drawImage(image=im1)
    im2 = obj_pix.drawImage(image=im1.copy(), method='no_pixel')
    print 'im1 flux diff = ', abs(im1.array.sum() - test_flux)
    np.testing.assert_almost_equal(
        im1.array.sum(), test_flux, 2,
        "obj.drawImage() produced image with wrong flux")
    print 'im2 flux diff = ', abs(im2.array.sum() - test_flux)
    np.testing.assert_almost_equal(
        im2.array.sum(), test_flux, 2,
        "obj_pix.drawImage(no_pixel) produced image with wrong flux")
    print 'im1, im2 max diff = ', abs(im1.array - im2.array).max()
    np.testing.assert_array_almost_equal(
        im1.array, im2.array, 6,
        "obj.drawImage() differs from obj_pix.drawImage(no_pixel)")
    im3 = obj.drawImage(image=im1.copy(), method='fft')
    print 'im1, im3 max diff = ', abs(im1.array - im3.array).max()
    np.testing.assert_array_almost_equal(
        im1.array, im3.array, 6,
        "obj.drawImage(fft) differs from obj.drawImage")

    # real_space should be similar, but not precisely equal.
    im4 = obj.drawImage(image=im1.copy(), method='real_space')
    print 'im1, im4 max diff = ', abs(im1.array - im4.array).max()
    np.testing.assert_array_almost_equal(
        im1.array, im4.array, 4,
        "obj.drawImage(real_space) differs from obj.drawImage")

    # sb should match xValue for pixel centers.  And be scale**2 factor different from no_pixel.
    im5 = obj.drawImage(image=im1.copy(), method='sb', use_true_center=False)
    im5.setCenter(0, 0)
    print 'im5(0,0) = ', im5(0, 0)
    print 'obj.xValue(0,0) = ', obj.xValue(0., 0.)
    np.testing.assert_almost_equal(
        im5(0, 0), obj.xValue(0., 0.), 6,
        "obj.drawImage(sb) values do not match surface brightness given by xValue"
    )
    np.testing.assert_almost_equal(
        im5(3, 2), obj.xValue(3 * test_scale, 2 * test_scale), 6,
        "obj.drawImage(sb) values do not match surface brightness given by xValue"
    )
    im5 = obj.drawImage(image=im5, method='sb')
    print 'im5(0,0) = ', im5(0, 0)
    print 'obj.xValue(dx/2,dx/2) = ', obj.xValue(test_scale / 2.,
                                                 test_scale / 2.)
    np.testing.assert_almost_equal(
        im5(0, 0), obj.xValue(0.5 * test_scale, 0.5 * test_scale), 6,
        "obj.drawImage(sb) values do not match surface brightness given by xValue"
    )
    np.testing.assert_almost_equal(
        im5(3, 2), obj.xValue(3.5 * test_scale, 2.5 * test_scale), 6,
        "obj.drawImage(sb) values do not match surface brightness given by xValue"
    )
    im6 = obj.drawImage(image=im1.copy(), method='no_pixel')
    print 'im6, im5*scale**2 max diff = ', abs(im6.array - im5.array *
                                               test_scale**2).max()
    np.testing.assert_array_almost_equal(
        im5.array * test_scale**2, im6.array, 6,
        "obj.drawImage(sb) * scale**2 differs from obj.drawImage(no_pixel)")

    # Drawing a truncated object, auto should be identical to real_space
    obj = galsim.Sersic(flux=test_flux, n=3.7, half_light_radius=2, trunc=4)
    obj_pix = galsim.Convolve(obj, pix)

    # auto and real_space should be equivalent to drawing obj_pix with no_pixel
    im1 = obj.drawImage(image=im1)
    im2 = obj_pix.drawImage(image=im1.copy(), method='no_pixel')
    print 'im1 flux diff = ', abs(im1.array.sum() - test_flux)
    np.testing.assert_almost_equal(
        im1.array.sum(), test_flux, 2,
        "obj.drawImage() produced image with wrong flux")
    print 'im2 flux diff = ', abs(im2.array.sum() - test_flux)
    np.testing.assert_almost_equal(
        im2.array.sum(), test_flux, 2,
        "obj_pix.drawImage(no_pixel) produced image with wrong flux")
    print 'im1, im2 max diff = ', abs(im1.array - im2.array).max()
    np.testing.assert_array_almost_equal(
        im1.array, im2.array, 6,
        "obj.drawImage() differs from obj_pix.drawImage(no_pixel)")
    im4 = obj.drawImage(image=im1.copy(), method='real_space')
    print 'im1, im4 max diff = ', abs(im1.array - im4.array).max()
    np.testing.assert_array_almost_equal(
        im1.array, im4.array, 6,
        "obj.drawImage(real_space) differs from obj.drawImage")

    # fft should be similar, but not precisely equal.
    import warnings
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        # This emits a warning about convolving two things with hard edges.
        im3 = obj.drawImage(image=im1.copy(), method='fft')
    print 'im1, im3 max diff = ', abs(im1.array - im3.array).max()
    np.testing.assert_array_almost_equal(
        im1.array,
        im3.array,
        3,  # Should be close, but not exact.
        "obj.drawImage(fft) differs from obj.drawImage")

    # sb should match xValue for pixel centers.  And be scale**2 factor different from no_pixel.
    im5 = obj.drawImage(image=im1.copy(), method='sb')
    im5.setCenter(0, 0)
    print 'im5(0,0) = ', im5(0, 0)
    print 'obj.xValue(dx/2,dx/2) = ', obj.xValue(test_scale / 2.,
                                                 test_scale / 2.)
    np.testing.assert_almost_equal(
        im5(0, 0), obj.xValue(0.5 * test_scale, 0.5 * test_scale), 6,
        "obj.drawImage(sb) values do not match surface brightness given by xValue"
    )
    np.testing.assert_almost_equal(
        im5(3, 2), obj.xValue(3.5 * test_scale, 2.5 * test_scale), 6,
        "obj.drawImage(sb) values do not match surface brightness given by xValue"
    )
    im6 = obj.drawImage(image=im1.copy(), method='no_pixel')
    print 'im6, im5*scale**2 max diff = ', abs(im6.array - im5.array *
                                               test_scale**2).max()
    np.testing.assert_array_almost_equal(
        im5.array * test_scale**2, im6.array, 6,
        "obj.drawImage(sb) * scale**2 differs from obj.drawImage(no_pixel)")

    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
Esempio n. 11
0
def test_draw():
    """Test the various optional parameters to the draw function.
       In particular test the parameters image, dx, and wmult in various combinations.
    """
    import time
    t1 = time.time()

    # We use a simple Exponential for our object:
    obj = galsim.Exponential(flux=test_flux, scale_radius=2)

    # First test draw() with no kwargs.  It should:
    #   - create a new image
    #   - return the new image
    #   - set the scale to obj.nyquistDx()
    #   - set the size large enough to contain 99.5% of the flux
    im1 = obj.draw()
    dx_nyq = obj.nyquistDx()
    np.testing.assert_almost_equal(
        im1.scale, dx_nyq, 9, "obj.draw() produced image with wrong scale")
    #print 'im1.bounds = ',im1.bounds
    assert im1.bounds == galsim.BoundsI(
        1, 56, 1, 56), ("obj.draw() produced image with wrong bounds")
    np.testing.assert_almost_equal(CalculateScale(im1), 2, 1,
                                   "Measured wrong scale after obj.draw()")

    # The flux is only really expected to come out right if the object has been
    # convoled with a pixel:
    obj2 = galsim.Convolve([obj, galsim.Pixel(im1.scale)])
    im2 = obj2.draw()
    dx_nyq = obj2.nyquistDx()
    np.testing.assert_almost_equal(
        im2.scale, dx_nyq, 9, "obj2.draw() produced image with wrong scale")
    np.testing.assert_almost_equal(
        im2.array.astype(float).sum(), test_flux, 2,
        "obj2.draw() produced image with wrong flux")
    assert im2.bounds == galsim.BoundsI(
        1, 56, 1, 56), ("obj2.draw() produced image with wrong bounds")
    np.testing.assert_almost_equal(CalculateScale(im2), 2, 1,
                                   "Measured wrong scale after obj2.draw()")

    # Test if we provide an image argument.  It should:
    #   - write to the existing image
    #   - also return that image
    #   - set the scale to obj2.nyquistDx()
    #   - zero out any existing data
    im3 = galsim.ImageD(56, 56)
    im4 = obj2.draw(im3)
    np.testing.assert_almost_equal(
        im3.scale, dx_nyq, 9, "obj2.draw(im3) produced image with wrong scale")
    np.testing.assert_almost_equal(
        im3.array.sum(), test_flux, 2,
        "obj2.draw(im3) produced image with wrong flux")
    np.testing.assert_almost_equal(
        im3.array.sum(),
        im2.array.astype(float).sum(), 6,
        "obj2.draw(im3) produced image with different flux than im2")
    np.testing.assert_almost_equal(
        CalculateScale(im3), 2, 1, "Measured wrong scale after obj2.draw(im3)")
    np.testing.assert_array_equal(im3.array, im4.array,
                                  "im4 = obj2.draw(im3) produced im4 != im3")
    im3.fill(9.8)
    np.testing.assert_array_equal(
        im3.array, im4.array, "im4 = obj2.draw(im3) produced im4 is not im3")
    im4 = obj2.draw(im3)
    np.testing.assert_almost_equal(
        im3.array.sum(),
        im2.array.astype(float).sum(), 6,
        "obj2.draw(im3) doesn't zero out existing data")

    # Test if we provide an image with undefined bounds.  It should:
    #   - resize the provided image
    #   - also return that image
    #   - set the scale to obj2.nyquistDx()
    im5 = galsim.ImageD()
    obj2.draw(im5)
    np.testing.assert_almost_equal(
        im5.scale, dx_nyq, 9, "obj2.draw(im5) produced image with wrong scale")
    np.testing.assert_almost_equal(
        im5.array.sum(), test_flux, 2,
        "obj2.draw(im5) produced image with wrong flux")
    np.testing.assert_almost_equal(
        CalculateScale(im5), 2, 1, "Measured wrong scale after obj2.draw(im5)")
    np.testing.assert_almost_equal(
        im5.array.sum(),
        im2.array.astype(float).sum(), 6,
        "obj2.draw(im5) produced image with different flux than im2")
    assert im5.bounds == galsim.BoundsI(
        1, 56, 1, 56), ("obj2.draw(im5) produced image with wrong bounds")

    # Test if we provide wmult.  It should:
    #   - create a new image that is wmult times larger in each direction.
    #   - return the new image
    #   - set the scale to obj2.nyquistDx()
    im6 = obj2.draw(wmult=4.)
    np.testing.assert_almost_equal(
        im6.scale, dx_nyq, 9,
        "obj2.draw(wmult) produced image with wrong scale")
    # Can assert accuracy to 4 decimal places now, since we're capturing much more
    # of the flux on the image.
    np.testing.assert_almost_equal(
        im6.array.astype(float).sum(), test_flux, 4,
        "obj2.draw(wmult) produced image with wrong flux")
    np.testing.assert_almost_equal(
        CalculateScale(im6), 2, 2,
        "Measured wrong scale after obj2.draw(wmult)")
    #print 'im6.bounds = ',im6.bounds
    assert im6.bounds == galsim.BoundsI(
        1, 220, 1, 220), ("obj2.draw(wmult) produced image with wrong bounds")

    # Test if we provide an image argument and wmult.  It should:
    #   - write to the existing image
    #   - also return that image
    #   - set the scale to obj2.nyquistDx()
    #   - zero out any existing data
    #   - the calculation of the convolution should be slightly more accurate than for im3
    im3.setZero()
    im5.setZero()
    obj2.draw(im3, wmult=4.)
    obj2.draw(im5)
    np.testing.assert_almost_equal(
        im3.scale, dx_nyq, 9, "obj2.draw(im3) produced image with wrong scale")
    np.testing.assert_almost_equal(
        im3.array.sum(), test_flux, 2,
        "obj2.draw(im3,wmult) produced image with wrong flux")
    np.testing.assert_almost_equal(
        CalculateScale(im3), 2, 1,
        "Measured wrong scale after obj2.draw(im3,wmult)")
    assert ((im3.array - im5.array)**2).sum() > 0, (
        "obj2.draw(im3,wmult) produced the same image as without wmult")

    # Test if we provide a dx to use.  It should:
    #   - create a new image using that dx for the scale
    #   - return the new image
    #   - set the size large enough to contain 99.5% of the flux
    im7 = obj2.draw(scale=0.51)
    np.testing.assert_almost_equal(
        im7.scale, 0.51, 9, "obj2.draw(dx) produced image with wrong scale")
    np.testing.assert_almost_equal(
        im7.array.astype(float).sum(), test_flux, 2,
        "obj2.draw(dx) produced image with wrong flux")
    np.testing.assert_almost_equal(CalculateScale(im7), 2, 1,
                                   "Measured wrong scale after obj2.draw(dx)")
    #print 'im7.bounds = ',im7.bounds
    assert im7.bounds == galsim.BoundsI(
        1, 68, 1, 68), ("obj2.draw(dx) produced image with wrong bounds")

    # Test with dx and wmult.  It should:
    #   - create a new image using that dx for the scale
    #   - set the size a factor of wmult times larger in each direction.
    #   - return the new image
    im8 = obj2.draw(scale=0.51, wmult=4.)
    np.testing.assert_almost_equal(
        im8.scale, 0.51, 9,
        "obj2.draw(dx,wmult) produced image with wrong scale")
    np.testing.assert_almost_equal(
        im8.array.astype(float).sum(), test_flux, 4,
        "obj2.draw(dx,wmult) produced image with wrong flux")
    np.testing.assert_almost_equal(
        CalculateScale(im8), 2, 2,
        "Measured wrong scale after obj2.draw(dx,wmult)")
    #print 'im8.bounds = ',im8.bounds
    assert im8.bounds == galsim.BoundsI(
        1, 270, 1,
        270), ("obj2.draw(dx,wmult) produced image with wrong bounds")

    # Test if we provide an image with a defined scale.  It should:
    #   - write to the existing image
    #   - use the image's scale
    im9 = galsim.ImageD(200, 200, scale=0.51)
    obj2.draw(im9)
    np.testing.assert_almost_equal(
        im9.scale, 0.51, 9, "obj2.draw(im9) produced image with wrong scale")
    np.testing.assert_almost_equal(
        im9.array.sum(), test_flux, 4,
        "obj2.draw(im9) produced image with wrong flux")
    np.testing.assert_almost_equal(
        CalculateScale(im9), 2, 2, "Measured wrong scale after obj2.draw(im9)")

    # Test if we provide an image with a defined scale <= 0.  It should:
    #   - write to the existing image
    #   - set the scale to obj2.nyquistDx()
    im9.scale = -0.51
    im9.setZero()
    obj2.draw(im9)
    np.testing.assert_almost_equal(
        im9.scale, dx_nyq, 9, "obj2.draw(im9) produced image with wrong scale")
    np.testing.assert_almost_equal(
        im9.array.sum(), test_flux, 4,
        "obj2.draw(im9) produced image with wrong flux")
    np.testing.assert_almost_equal(
        CalculateScale(im9), 2, 2, "Measured wrong scale after obj2.draw(im9)")
    im9.scale = 0
    im9.setZero()
    obj2.draw(im9)
    np.testing.assert_almost_equal(
        im9.scale, dx_nyq, 9, "obj2.draw(im9) produced image with wrong scale")
    np.testing.assert_almost_equal(
        im9.array.sum(), test_flux, 4,
        "obj2.draw(im9) produced image with wrong flux")
    np.testing.assert_almost_equal(
        CalculateScale(im9), 2, 2, "Measured wrong scale after obj2.draw(im9)")

    # Test if we provide an image and dx.  It should:
    #   - write to the existing image
    #   - use the provided dx
    #   - write the new dx value to the image's scale
    im9.scale = 0.73
    im9.setZero()
    obj2.draw(im9, scale=0.51)
    np.testing.assert_almost_equal(
        im9.scale, 0.51, 9,
        "obj2.draw(im9,dx) produced image with wrong scale")
    np.testing.assert_almost_equal(
        im9.array.sum(), test_flux, 4,
        "obj2.draw(im9,dx) produced image with wrong flux")
    np.testing.assert_almost_equal(
        CalculateScale(im9), 2, 2,
        "Measured wrong scale after obj2.draw(im9,dx)")

    # Test if we provide an image and dx <= 0.  It should:
    #   - write to the existing image
    #   - set the scale to obj2.nyquistDx()
    im9.scale = 0.73
    im9.setZero()
    obj2.draw(im9, scale=-0.51)
    np.testing.assert_almost_equal(
        im9.scale, dx_nyq, 9,
        "obj2.draw(im9,dx<0) produced image with wrong scale")
    np.testing.assert_almost_equal(
        im9.array.sum(), test_flux, 4,
        "obj2.draw(im9,dx<0) produced image with wrong flux")
    np.testing.assert_almost_equal(
        CalculateScale(im9), 2, 2,
        "Measured wrong scale after obj2.draw(im9,dx<0)")
    im9.scale = 0.73
    im9.setZero()
    obj2.draw(im9, scale=0)
    np.testing.assert_almost_equal(
        im9.scale, dx_nyq, 9,
        "obj2.draw(im9,scale=0) produced image with wrong scale")
    np.testing.assert_almost_equal(
        im9.array.sum(), test_flux, 4,
        "obj2.draw(im9,scale=0) produced image with wrong flux")
    np.testing.assert_almost_equal(
        CalculateScale(im9), 2, 2,
        "Measured wrong scale after obj2.draw(im9,scale=0)")

    # Test if we provide nx, ny, and scale.  It should:
    #   - create a new image with the right size
    #   - set the scale
    im10 = obj2.draw(nx=200, ny=100, scale=dx_nyq)
    np.testing.assert_almost_equal(
        im10.array.shape, (100, 200), 9,
        "obj2.draw(nx=200, ny=100) produced image with wrong size")
    np.testing.assert_almost_equal(
        im10.scale, dx_nyq, 9,
        "obj2.draw(nx=200, ny=100) produced image with wrong scale")
    np.testing.assert_almost_equal(
        im10.array.sum(), test_flux, 4,
        "obj2.draw(nx=200, ny=100) produced image with wrong flux")

    try:
        # Test if we provide nx, ny, and no scale.  It should:
        #   - raise a ValueError
        im10 = galsim.ImageF()
        kwargs = {'nx': 200, 'ny': 100}
        np.testing.assert_raises(ValueError, obj2.draw, kwargs)

        # Test if we provide nx, ny, scale, and an existing image.  It should:
        #   - raise a ValueError
        im10 = galsim.ImageF()
        kwargs = {'nx': 200, 'ny': 100, 'scale': dx_nyq, 'image': im10}
        np.testing.assert_raises(ValueError, obj2.draw, kwargs)
    except ImportError:
        print 'The assert_raises tests require nose'

    # Test if we provide bounds and scale.  It should:
    #   - create a new image with the right size
    #   - set the scale
    bounds = galsim.BoundsI(1, 200, 1, 100)
    im10 = obj2.draw(bounds=bounds, scale=dx_nyq)
    np.testing.assert_almost_equal(
        im10.array.shape, (100, 200), 9,
        "obj2.draw(bounds=galsim.Bounds(1,200,1,100)) produced image with wrong size"
    )
    np.testing.assert_almost_equal(
        im10.scale, dx_nyq, 9,
        "obj2.draw(bounds=galsim.Bounds(1,200,1,100)) produced image with wrong scale"
    )
    np.testing.assert_almost_equal(
        im10.array.sum(), test_flux, 4,
        "obj2.draw(bounds=galsim.Bounds(1,200,1,100)) produced image with wrong flux"
    )

    try:
        # Test if we provide bounds and no scale.  It should:
        #   - raise a ValueError
        bounds = galsim.BoundsI(1, 200, 1, 100)
        kwargs = {'bounds': bounds}
        np.testing.assert_raises(ValueError, obj2.draw, kwargs)

        # Test if we provide bounds, scale, and an existing image.  It should:
        #   - raise a ValueError
        bounds = galsim.BoundsI(1, 200, 1, 100)
        kwargs = {'bounds': bounds, 'scale': dx_nyq, 'image': im10}
        np.testing.assert_raises(ValueError, obj2.draw, kwargs)
    except ImportError:
        print 'The assert_raises tests require nose'

    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
Esempio n. 12
0
def main(argv):
    """
    A little bit more sophisticated, but still pretty basic:
      - Use a sheared, exponential profile for the galaxy.
      - Convolve it by a circular Moffat PSF.
      - Add Poisson 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("demo2")

    gal_flux = 1.e5  # counts
    gal_r0 = 0.7  # arcsec radius
    g1 = 0.8  # shear
    g2 = 0.1  # shear
    psf_beta = 5  #
    psf_re = 1.0  # arcsec

    # Fixed parameters
    nx = 33
    ny = 33
    pixel_scale = 0.2  # arcsec / pixel
    sky_level = 0  # counts / arcsec^2

    # This time use a particular seed, so the image is deterministic.
    # This is the same seed that is used in demo2.yaml, which means the images produced
    # by the two methods will be precisely identical.
    random_seed = 1534225

    logger.info('Starting demo script 2 using:')
    logger.info(
        '    - sheared (%.2f,%.2f) exponential galaxy (flux = %.1e, scale radius = %.2f),',
        g1, g2, gal_flux, gal_r0)
    logger.info('    - circular Moffat PSF (beta = %.1f, re = %.2f),',
                psf_beta, psf_re)
    logger.info('    - pixel scale = %.2f,', pixel_scale)
    logger.info('    - Poisson noise (sky level = %.1e).', sky_level)

    # Initialize the (pseudo-)random number generator that we will be using below.
    # For a technical reason that will be explained later (demo9.py), we add 1 to the
    # given random seed here.
    rng = galsim.BaseDeviate(random_seed + 1)

    # Define the galaxy profile.
    gal = galsim.Exponential(flux=gal_flux, scale_radius=gal_r0)

    # Shear the galaxy by some value.
    # There are quite a few ways you can use to specify a shape.
    # q, beta      Axis ratio and position angle: q = b/a, 0 < q < 1
    # e, beta      Ellipticity and position angle: |e| = (1-q^2)/(1+q^2)
    # g, beta      ("Reduced") Shear and position angle: |g| = (1-q)/(1+q)
    # eta, beta    Conformal shear and position angle: eta = ln(1/q)
    # e1,e2        Ellipticity components: e1 = e cos(2 beta), e2 = e sin(2 beta)
    # g1,g2        ("Reduced") shear components: g1 = g cos(2 beta), g2 = g sin(2 beta)
    # eta1,eta2    Conformal shear components: eta1 = eta cos(2 beta), eta2 = eta sin(2 beta)
    gal = gal.shear(g1=g1, g2=g2)
    logger.debug('Made galaxy profile')

    # Define the PSF profile.
    psf = galsim.Moffat(beta=psf_beta, flux=1., half_light_radius=psf_re)
    logger.debug('Made PSF profile')

    # Final profile is the convolution of these.
    final = galsim.Convolve([gal, psf])
    logger.debug('Convolved components into final profile')

    # Draw the image with a particular pixel scale.
    image = final.drawImage(scale=pixel_scale)
    # The "effective PSF" is the PSF as drawn on an image, which includes the convolution
    # by the pixel response.  We label it epsf here.
    image_epsf = psf.drawImage(scale=pixel_scale)
    logger.debug('Made image of the profile')

    # To get Poisson noise on the image, we will use a class called PoissonNoise.
    # However, we want the noise to correspond to what you would get with a significant
    # flux from tke sky.  This is done by telling PoissonNoise to add noise from a
    # sky level in addition to the counts currently in the image.
    #
    # One wrinkle here is that the PoissonNoise class needs the sky level in each pixel,
    # while we have a sky_level in counts per arcsec^2.  So we need to convert:
    sky_level_pixel = sky_level * pixel_scale**2
    noise = galsim.PoissonNoise(rng, sky_level=sky_level_pixel)
    image.addNoise(noise)
    logger.debug('Added Poisson noise')

    # Write the image to a file.
    if not os.path.isdir('output'):
        os.mkdir('output')
    file_name = os.path.join('output', 'demo2.fits')
    file_name_epsf = os.path.join('output', 'demo2_epsf.fits')
    image.write(file_name)
    image_epsf.write(file_name_epsf)
    logger.info('Wrote image to %r', file_name)
    logger.info('Wrote effective PSF image to %r', file_name_epsf)

    results = galsim.hsm.EstimateShear(image, image_epsf)

    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(
        'When carrying out Regaussianization PSF correction, HSM reports distortions'
    )
    logger.info('    e1, e2 = %.3f, %.3f', results.corrected_e1,
                results.corrected_e2)
    logger.info(
        'Expected values in the limit that noise and non-Gaussianity are negligible:'
    )
    exp_shear = galsim.Shear(g1=g1, g2=g2)
    logger.info('    g1, g2 = %.3f, %.3f', exp_shear.e1, exp_shear.e2)

    ## NR added

    plt.imshow(image.array)
    plt.show()

    return image
Esempio n. 13
0
def main(argv):
    # Where to find and output data
    path, filename = os.path.split(__file__)
    datapath = os.path.abspath(os.path.join(path, "data/"))
    outpath = os.path.abspath(os.path.join(path, "output/"))

    # 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("demo12")

    # initialize (pseudo-)random number generator
    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))
        # Here we create some galsim.SED objects to hold star or galaxy spectra.  The most
        # convenient way to create realistic spectra is to read them in from a two-column ASCII
        # file, where the first column is wavelength and the second column is flux. Wavelengths in
        # the example SED files are in Angstroms, flux in flambda.  The default wavelength type for
        # galsim.SED is nanometers, however, so we need to override by specifying
        # `wave_type = 'Ang'`.
        SED = galsim.SED(SED_filename, wave_type='Ang')
        # The normalization of SEDs affects how many photons are eventually drawn into an image.
        # One way to control this normalization is to specify the flux density in photons per nm
        # at a particular wavelength.  For example, here we normalize such that the photon density
        # is 1 photon per nm at 500 nm.
        SEDs[SED_name] = SED.withFluxDensity(target_flux_density=1.0,
                                             wavelength=500)
    logger.debug('Successfully read in SEDs')

    # read in the LSST filters
    filter_names = 'ugrizy'
    filters = {}
    for filter_name in filter_names:
        filter_filename = os.path.join(datapath,
                                       'LSST_{0}.dat'.format(filter_name))
        # Here we create some galsim.Bandpass objects to represent the filters we're observing
        # through.  These include the entire imaging system throughput including the atmosphere,
        # reflective and refractive optics, filters, and the CCD quantum efficiency.  These are
        # also conveniently read in from two-column ASCII files where the first column is
        # wavelength and the second column is dimensionless flux. The example filter files have
        # units of nanometers and dimensionless throughput, which is exactly what galsim.Bandpass
        # expects, so we just specify the filename.
        filters[filter_name] = galsim.Bandpass(filter_filename)
        # For speed, we can thin out the wavelength sampling of the filter a bit.
        # In the following line, `rel_err` specifies the relative error when integrating over just
        # the filter (however, this is not necessarily the relative error when integrating over the
        # filter times an SED)
        filters[filter_name] = filters[filter_name].thin(rel_err=1e-4)
    logger.debug('Read in filters')

    pixel_scale = 0.2  # arcseconds

    #-----------------------------------------------------------------------------------------------
    # Part A: chromatic de Vaucouleurs galaxy

    # Here we create a chromatic version of a de Vaucouleurs profile using the Chromatic class.
    # This class lets one create chromatic versions of any galsim GSObject class.  The first
    # argument is the GSObject instance to be chromaticized, and the second argument is the
    # profile's SED.

    logger.info('')
    logger.info('Starting part A: chromatic De Vaucouleurs galaxy')
    redshift = 0.8
    mono_gal = galsim.DeVaucouleurs(half_light_radius=0.5)
    SED = SEDs['CWW_E_ext'].atRedshift(redshift)
    gal = galsim.Chromatic(mono_gal, SED)

    # You can still shear, shift, and dilate the resulting chromatic object.
    gal = gal.shear(g1=0.5, g2=0.3).dilate(1.05).shift((0.0, 0.1))
    logger.debug('Created Chromatic')

    # convolve with PSF to make final profile
    PSF = galsim.Moffat(fwhm=0.6, beta=2.5)
    final = galsim.Convolve([gal, PSF])
    logger.debug('Created final profile')

    # draw profile through LSST filters
    gaussian_noise = galsim.GaussianNoise(rng, sigma=0.1)
    for filter_name, filter_ in filters.iteritems():
        img = galsim.ImageF(64, 64, scale=pixel_scale)
        final.drawImage(filter_, image=img)
        img.addNoise(gaussian_noise)
        logger.debug('Created {0}-band image'.format(filter_name))
        out_filename = os.path.join(outpath,
                                    'demo12a_{0}.fits'.format(filter_name))
        galsim.fits.write(img, out_filename)
        logger.debug('Wrote {0}-band image to disk'.format(filter_name))
        logger.info('Added flux for {0}-band image: {1}'.format(
            filter_name, img.added_flux))

    logger.info(
        'You can display the output in ds9 with a command line that looks something like:'
    )
    logger.info(
        'ds9 output/demo12a_*.fits -match scale -zoom 2 -match frame image &')

    #-----------------------------------------------------------------------------------------------
    # Part B: chromatic bulge+disk galaxy

    logger.info('')
    logger.info('Starting part B: chromatic bulge+disk galaxy')
    redshift = 0.8
    # make a bulge ...
    mono_bulge = galsim.DeVaucouleurs(half_light_radius=0.5)
    bulge_SED = SEDs['CWW_E_ext'].atRedshift(redshift)
    # The `*` operator can be used as a shortcut for creating a chromatic version of a GSObject:
    bulge = mono_bulge * bulge_SED
    bulge = bulge.shear(g1=0.12, g2=0.07)
    logger.debug('Created bulge component')
    # ... and a disk ...
    mono_disk = galsim.Exponential(half_light_radius=2.0)
    disk_SED = SEDs['CWW_Im_ext'].atRedshift(redshift)
    disk = mono_disk * disk_SED
    disk = disk.shear(g1=0.4, g2=0.2)
    logger.debug('Created disk component')
    # ... and then combine them.
    bdgal = 1.1 * (
        0.8 * bulge + 4 * disk
    )  # you can add and multiply ChromaticObjects just like GSObjects
    bdfinal = galsim.Convolve([bdgal, PSF])
    # Note that at this stage, our galaxy is chromatic but our PSF is still achromatic.  Part C)
    # below will dive into chromatic PSFs.
    logger.debug('Created bulge+disk galaxy final profile')

    # draw profile through LSST filters
    gaussian_noise = galsim.GaussianNoise(rng, sigma=0.02)
    for filter_name, filter_ in filters.iteritems():
        img = galsim.ImageF(64, 64, scale=pixel_scale)
        bdfinal.drawImage(filter_, image=img)
        img.addNoise(gaussian_noise)
        logger.debug('Created {0}-band image'.format(filter_name))
        out_filename = os.path.join(outpath,
                                    'demo12b_{0}.fits'.format(filter_name))
        galsim.fits.write(img, out_filename)
        logger.debug('Wrote {0}-band image to disk'.format(filter_name))
        logger.info('Added flux for {0}-band image: {1}'.format(
            filter_name, img.added_flux))

    logger.info(
        'You can display the output in ds9 with a command line that looks something like:'
    )
    logger.info(
        'ds9 -rgb -blue -scale limits -0.2 0.8 output/demo12b_r.fits -green -scale limits'
        +
        ' -0.25 1.0 output/demo12b_i.fits -red -scale limits -0.25 1.0 output/demo12b_z.fits'
        + ' -zoom 2 &')

    #-----------------------------------------------------------------------------------------------
    # Part C: chromatic PSF

    logger.info('')
    logger.info('Starting part C: chromatic PSF')
    redshift = 0.0
    mono_gal = galsim.Exponential(half_light_radius=0.5)
    SED = SEDs['CWW_Im_ext'].atRedshift(redshift)
    # Here's another way to set the normalization of the SED.  If we want 50 counts to be drawn
    # when observing an object with this SED through the LSST g-band filter, for instance, then we
    # can do:
    SED = SED.withFlux(50.0, filters['g'])
    # The flux drawn through other bands, which sample different parts of the SED and have different
    # throughputs, will, of course, be different.
    gal = mono_gal * SED
    gal = gal.shear(g1=0.5, g2=0.3)
    logger.debug('Created `Chromatic` galaxy')

    # For a ground-based PSF, two chromatic effects are introduced by the atmosphere:
    # (i) differential chromatic refraction (DCR), and (ii) wavelength-dependent seeing.
    #
    # DCR shifts the position of the PSF as a function of wavelength.  Blue light is shifted
    # toward the zenith slightly more than red light.
    #
    # Kolmogorov turbulence in the atmosphere leads to a seeing size (e.g., FWHM) that scales with
    # wavelength to the (-0.2) power.
    #
    # The ChromaticAtmosphere function will attach both of these effects to a fiducial PSF at
    # some fiducial wavelength.

    # First we define a monochromatic PSF to be the fiducial PSF.
    PSF_500 = galsim.Moffat(beta=2.5, fwhm=0.5)
    # Then we use ChromaticAtmosphere to manipulate this fiducial PSF as a function of wavelength.
    # ChromaticAtmosphere also needs to know the wavelength of the fiducial PSF, and the location
    # and orientation of the object with respect to the zenith.  This final piece of information
    # can be specified in several ways (see the ChromaticAtmosphere docstring for all of them).
    # Here are a couple ways: let's pretend our object is located near M101 on the sky, we observe
    # it 1 hour before it transits and we're observing from Mauna Kea.
    ra = galsim.HMS_Angle("14:03:13")  # hours : minutes : seconds
    dec = galsim.DMS_Angle("54:20:57")  # degrees : minutes : seconds
    m101 = galsim.CelestialCoord(ra, dec)
    latitude = 19.8207 * galsim.degrees  # latitude of Mauna Kea
    HA = -1.0 * galsim.hours  # Hour angle = one hour before transit

    # Then we can compute the zenith angle and parallactic angle (which is is the position angle
    # of the zenith measured from North through East) of this object:
    za, pa = galsim.dcr.zenith_parallactic_angles(m101,
                                                  HA=HA,
                                                  latitude=latitude)
    # And then finally, create the chromatic PSF
    PSF = galsim.ChromaticAtmosphere(PSF_500,
                                     500.0,
                                     zenith_angle=za,
                                     parallactic_angle=pa)
    # We could have also just passed `m101`, `latitude` and `HA` to ChromaticAtmosphere directly:
    PSF = galsim.ChromaticAtmosphere(PSF_500,
                                     500.0,
                                     obj_coord=m101,
                                     latitude=latitude,
                                     HA=HA)
    # and proceed like normal.

    # convolve with galaxy to create final profile
    final = galsim.Convolve([gal, PSF])
    logger.debug('Created chromatic PSF finale profile')

    # Draw profile through LSST filters
    gaussian_noise = galsim.GaussianNoise(rng, sigma=0.03)
    for filter_name, filter_ in filters.iteritems():
        img = galsim.ImageF(64, 64, scale=pixel_scale)
        final.drawImage(filter_, image=img)
        img.addNoise(gaussian_noise)
        logger.debug('Created {0}-band image'.format(filter_name))
        out_filename = os.path.join(outpath,
                                    'demo12c_{0}.fits'.format(filter_name))
        galsim.fits.write(img, out_filename)
        logger.debug('Wrote {0}-band image to disk'.format(filter_name))
        logger.info('Added flux for {0}-band image: {1}'.format(
            filter_name, img.added_flux))

    logger.info(
        'You can display the output in ds9 with a command line that looks something like:'
    )
    logger.info(
        'ds9 output/demo12c_*.fits -match scale -zoom 2 -match frame image -blink &'
    )
Esempio n. 14
0
def test_exponential():
    """Test the generation of a specific exp profile against a known result.
    """
    re = 1.0
    # Note the factor below should really be 1.6783469900166605, but the value of 1.67839 is
    # retained here as it was used by SBParse to generate the original known result (this changed
    # in commit b77eb05ab42ecd31bc8ca03f1c0ae4ee0bc0a78b.
    # The value of this test for regression purposes is not harmed by retaining the old scaling, it
    # just means that the half light radius chosen for the test is not really 1, but 0.999974...
    r0 = re / 1.67839
    savedImg = galsim.fits.read(os.path.join(imgdir, "exp_1.fits"))
    dx = 0.2
    myImg = galsim.ImageF(savedImg.bounds, scale=dx)
    myImg.setCenter(0, 0)

    expon = galsim.Exponential(flux=1., scale_radius=r0)
    expon.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 Exponential disagrees with expected result")

    # Check with default_params
    expon = galsim.Exponential(flux=1.,
                               scale_radius=r0,
                               gsparams=default_params)
    expon.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 Exponential with default_params disagrees with expected result"
    )
    expon = galsim.Exponential(flux=1.,
                               scale_radius=r0,
                               gsparams=galsim.GSParams())
    expon.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 Exponential with GSParams() disagrees with expected result"
    )

    # Use non-unity values.
    expon = galsim.Exponential(flux=1.7, scale_radius=0.91)
    gsp = galsim.GSParams(xvalue_accuracy=1.e-8, kvalue_accuracy=1.e-8)
    expon2 = galsim.Exponential(flux=1.7, scale_radius=0.91, gsparams=gsp)
    assert expon2 != expon
    assert expon2 == expon.withGSParams(gsp)

    check_basic(expon, "Exponential")

    # Test photon shooting.
    do_shoot(expon, myImg, "Exponential")

    # Test kvalues
    do_kvalue(expon, myImg, "Exponential")

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

    # Should raise an exception if both scale_radius and half_light_radius are provided.
    assert_raises(TypeError,
                  galsim.Exponential,
                  scale_radius=3,
                  half_light_radius=1)
    # Or neither.
    assert_raises(TypeError, galsim.Exponential)
Esempio n. 15
0
def get_catsim_galaxy(entry,
                      filt,
                      survey,
                      no_disk=False,
                      no_bulge=False,
                      no_agn=False):
    """Returns a bulge/disk/agn Galsim galaxy profile based on entry.

    This function returns a composite galsim galaxy profile with bulge, disk and AGN based on the
    parameters in entry, and using observing conditions defined by the survey
    and the filter.

    Credit: WeakLensingDeblending (https://github.com/LSSTDESC/WeakLensingDeblending)

    Args:
        entry (astropy.table.Table): single astropy line containing information on the galaxy
        survey (btk.survey.Survey): BTK Survey object
        filt (btk.survey.Filter): BTK Filter object
        no_disk (bool): Sets the flux for the disk to zero
        no_bulge (bool): Sets the flux for the bulge to zero
        no_agn (bool): Sets the flux for the AGN to zero
    Returns:
        Galsim galaxy profile
    """
    components = []
    total_flux = get_flux(entry[filt.name + "_ab"], filt, survey)
    # Calculate the flux of each component in detected electrons.
    total_fluxnorm = entry["fluxnorm_disk"] + entry["fluxnorm_bulge"] + entry[
        "fluxnorm_agn"]
    disk_flux = 0.0 if no_disk else entry[
        "fluxnorm_disk"] / total_fluxnorm * total_flux
    bulge_flux = 0.0 if no_bulge else entry[
        "fluxnorm_bulge"] / total_fluxnorm * total_flux
    agn_flux = 0.0 if no_agn else entry[
        "fluxnorm_agn"] / total_fluxnorm * total_flux

    if disk_flux + bulge_flux + agn_flux == 0:
        raise SourceNotVisible

    if disk_flux > 0:
        beta_radians = np.radians(entry["pa_disk"])
        if bulge_flux > 0:
            assert entry["pa_disk"] == entry[
                "pa_bulge"], "Sersic components have different beta."
        a_d, b_d = entry["a_d"], entry["b_d"]
        disk_hlr_arcsecs = np.sqrt(a_d * b_d)
        disk_q = b_d / a_d
        disk = galsim.Exponential(flux=disk_flux,
                                  half_light_radius=disk_hlr_arcsecs).shear(
                                      q=disk_q,
                                      beta=beta_radians * galsim.radians)
        components.append(disk)

    if bulge_flux > 0:
        beta_radians = np.radians(entry["pa_bulge"])
        a_b, b_b = entry["a_b"], entry["b_b"]
        bulge_hlr_arcsecs = np.sqrt(a_b * b_b)
        bulge_q = b_b / a_b
        bulge = galsim.DeVaucouleurs(
            flux=bulge_flux, half_light_radius=bulge_hlr_arcsecs).shear(
                q=bulge_q, beta=beta_radians * galsim.radians)
        components.append(bulge)

    if agn_flux > 0:
        agn = galsim.Gaussian(flux=agn_flux, sigma=1e-8)
        components.append(agn)

    profile = galsim.Add(components)
    return profile
Esempio n. 16
0
    def _buildParametric(record,
                         gsparams=None,
                         chromatic=False,
                         bandpass=None,
                         sed=None):
        # Get fit parameters.  For 'sersicfit', the result is an array of 8 numbers for each
        # galaxy:
        #     SERSICFIT[0]: intensity of light profile at the half-light radius.
        #     SERSICFIT[1]: half-light radius measured along the major axis, in units of pixels
        #                   in the COSMOS lensing data reductions (0.03 arcsec).
        #     SERSICFIT[2]: Sersic n.
        #     SERSICFIT[3]: q, the ratio of minor axis to major axis length.
        #     SERSICFIT[4]: boxiness, currently fixed to 0, meaning isophotes are all
        #                   elliptical.
        #     SERSICFIT[5]: x0, the central x position in pixels.
        #     SERSICFIT[6]: y0, the central y position in pixels.
        #     SERSICFIT[7]: phi, the position angle in radians.  If phi=0, the major axis is
        #                   lined up with the x axis of the image.
        # For 'bulgefit', the result is an array of 16 parameters that comes from doing a
        # 2-component sersic fit.  The first 8 are the parameters for the disk, with n=1, and
        # the last 8 are for the bulge, with n=4.
        bparams = record['bulgefit']
        sparams = record['sersicfit']
        # Get the status flag for the fits.  Entries 0 and 4 in 'fit_status' are relevant for
        # bulgefit and sersicfit, respectively.
        bstat = record['fit_status'][0]
        sstat = record['fit_status'][4]
        # Get the precomputed bulge-to-total flux ratio for the 2-component fits.
        dvc_btt = record['fit_dvc_btt']
        # Get the precomputed median absolute deviation for the 1- and 2-component fits.
        # These quantities are used to ascertain whether the 2-component fit is really
        # justified, or if the 1-component Sersic fit is sufficient to describe the galaxy
        # light profile.
        bmad = record['fit_mad_b']
        smad = record['fit_mad_s']

        # First decide if we can / should use bulgefit, otherwise sersicfit.  This decision
        # process depends on: the status flags for the fits, the bulge-to-total ratios (if near
        # 0 or 1, just use single component fits), the sizes for the bulge and disk (if <=0 then
        # use single component fits), the axis ratios for the bulge and disk (if <0.051 then use
        # single component fits), and a comparison of the median absolute deviations to see
        # which is better.  The reason for the 0.051 cutoff is that the fits were bound at 0.05
        # as a minimum, so anything below 0.051 generally means that the fitter hit the boundary
        # for the 2-component fits, typically meaning that we don't have enough information to
        # make reliable 2-component fits.
        use_bulgefit = True
        if (bstat < 1 or bstat > 4 or dvc_btt < 0.1 or dvc_btt > 0.9
                or np.isnan(dvc_btt) or bparams[9] <= 0 or bparams[1] <= 0
                or bparams[11] < 0.051 or bparams[3] < 0.051 or smad < bmad):
            use_bulgefit = False
        # Then check if sersicfit is viable; if not, this galaxy is a total failure.
        # Note that we can avoid including these in the catalog in the first place by using
        # `exclude_fail=True` when making the catalog.
        if sstat < 1 or sstat > 4 or sparams[1] <= 0 or sparams[0] <= 0:
            raise RuntimeError("Cannot make parametric model for this galaxy!")

        # If we're supposed to use the 2-component fits, get all the parameters.
        if use_bulgefit:
            # Bulge parameters:
            # Minor-to-major axis ratio:
            bulge_q = bparams[11]
            # Position angle, now represented as a galsim.Angle:
            bulge_beta = bparams[15] * galsim.radians
            # We have to convert from the stored half-light radius along the major axis, to an
            # azimuthally averaged one (multiplying by sqrt(bulge_q)).  We also have to convert
            # to our native units of arcsec, from units of COSMOS pixels.
            bulge_hlr = cosmos_pix_scale * np.sqrt(bulge_q) * bparams[9]
            # The stored quantity is the surface brightness at the half-light radius.  We have
            # to convert to total flux within an n=4 surface brightness profile.
            bulge_flux = 2.0 * np.pi * 3.607 * (
                bulge_hlr**2) * bparams[8] / cosmos_pix_scale**2
            # Disk parameters, defined analogously:
            disk_q = bparams[3]
            disk_beta = bparams[7] * galsim.radians
            disk_hlr = cosmos_pix_scale * np.sqrt(disk_q) * bparams[1]
            disk_flux = 2.0 * np.pi * 1.901 * (
                disk_hlr**2) * bparams[0] / cosmos_pix_scale**2
            bfrac = bulge_flux / (bulge_flux + disk_flux)
            # Make sure the bulge-to-total flux ratio is not nonsense.
            if bfrac < 0 or bfrac > 1 or np.isnan(bfrac):
                raise RuntimeError(
                    "Cannot make parametric model for this galaxy")

            # Then make the two components of the galaxy.
            if chromatic:
                # We define the GSObjects with flux=1, then multiply by an SED defined to have
                # the appropriate (observed) magnitude at the redshift in the COSMOS passband.
                z = record['zphot']
                target_bulge_mag = record['mag_auto'] - 2.5 * math.log10(bfrac)
                bulge_sed = sed[0].atRedshift(z).withMagnitude(
                    target_bulge_mag, bandpass)
                bulge = galsim.DeVaucouleurs(half_light_radius=bulge_hlr,
                                             gsparams=gsparams)
                bulge *= bulge_sed
                target_disk_mag = record['mag_auto'] - 2.5 * math.log10(
                    (1. - bfrac))
                disk_sed = sed[1].atRedshift(z).withMagnitude(
                    target_disk_mag, bandpass)
                disk = galsim.Exponential(half_light_radius=disk_hlr,
                                          gsparams=gsparams)
                disk *= disk_sed
            else:
                bulge = galsim.DeVaucouleurs(flux=bulge_flux,
                                             half_light_radius=bulge_hlr,
                                             gsparams=gsparams)
                disk = galsim.Exponential(flux=disk_flux,
                                          half_light_radius=disk_hlr,
                                          gsparams=gsparams)

            # Apply shears for intrinsic shape.
            if bulge_q < 1.:
                bulge = bulge.shear(q=bulge_q, beta=bulge_beta)
            if disk_q < 1.:
                disk = disk.shear(q=disk_q, beta=disk_beta)

            gal = bulge + disk
        else:
            # Do a similar manipulation to the stored quantities for the single Sersic profiles.

            gal_n = sparams[2]
            # Fudge this if it is at the edge of the allowed n values.  Since GalSim (as of
            # #325 and #449) allow Sersic n in the range 0.3<=n<=6, the only problem is that
            # the fits occasionally go as low as n=0.2.  The fits in this file only go to n=6,
            # so there is no issue with too-high values, but we also put a guard on that side
            # in case other samples are swapped in that go to higher value of sersic n.
            if gal_n < 0.3: gal_n = 0.3
            if gal_n > 6.0: gal_n = 6.0
            gal_q = sparams[3]
            gal_beta = sparams[7] * galsim.radians
            gal_hlr = cosmos_pix_scale * np.sqrt(gal_q) * sparams[1]
            # Below is the calculation of the full Sersic n-dependent quantity that goes into
            # the conversion from surface brightness to flux, which here we're calling
            # 'prefactor'.  In the n=4 and n=1 cases above, this was precomputed, but here we
            # have to calculate for each value of n.
            tmp_ser = galsim.Sersic(gal_n,
                                    half_light_radius=gal_hlr,
                                    gsparams=gsparams)
            gal_flux = sparams[0] / tmp_ser.xValue(
                0, gal_hlr) / cosmos_pix_scale**2

            if chromatic:
                gal = galsim.Sersic(gal_n,
                                    flux=1.,
                                    half_light_radius=gal_hlr,
                                    gsparams=gsparams)
                if gal_n < 1.5:
                    use_sed = sed[1]  # disk
                elif gal_n >= 1.5 and gal_n < 3.0:
                    use_sed = sed[2]  # intermediate
                else:
                    use_sed = sed[0]  # bulge
                target_mag = record['mag_auto']
                z = record['zphot']
                gal *= use_sed.atRedshift(z).withMagnitude(
                    target_mag, bandpass)
            else:
                gal = galsim.Sersic(gal_n,
                                    flux=gal_flux,
                                    half_light_radius=gal_hlr,
                                    gsparams=gsparams)

            # Apply shears for intrinsic shape.
            if gal_q < 1.:
                gal = gal.shear(q=gal_q, beta=gal_beta)

        return gal
Esempio n. 17
0
print "Sky noise: {}".format(etc.sigma_sky)

try:
    os.mkdir(args.outdir)
except:
    pass

psfimg = psf.drawImage(nx=args.nx, ny=args.nx, scale=0.2)
psfimg.write(os.path.join(args.outdir, 'psf.fits'))

infile = h5py.File(args.h5read)
for i, datum in enumerate(infile['data']):
    ra, dec, z, r, mag, e1, e2, g1, g2 = datum
    flux = etc.flux(mag)
    print i, mag, flux, e1, e2
    gal = (galsim.Exponential(half_light_radius=0.3).shear(e1=e1, e2=e2).shear(
        e1=g1, e2=g2).withFlux(flux))

    obj = galsim.Convolve(gal, psf)
    img = obj.drawImage(nx=args.nx, ny=args.nx, scale=0.2)
    img.addNoise(noise)
    h1 = galsim.FitsHeader()
    h1['RA'] = ra
    h1['DEC'] = dec
    h1['z'] = z
    h1['r'] = r
    h1['mag'] = mag
    h1['e1'] = e1
    h1['e2'] = e2
    h1['g1'] = g1
    h1['g2'] = g2
    img.header = h1
Esempio n. 18
0
def test_photon_array():
    """Test the basic methods of PhotonArray class
    """
    nphotons = 1000

    # First create from scratch
    photon_array = galsim.PhotonArray(nphotons)
    assert len(photon_array.x) == nphotons
    assert len(photon_array.y) == nphotons
    assert len(photon_array.flux) == nphotons
    assert not photon_array.hasAllocatedWavelengths()
    assert not photon_array.hasAllocatedAngles()

    # Initial values should all be 0
    np.testing.assert_array_equal(photon_array.x, 0.)
    np.testing.assert_array_equal(photon_array.y, 0.)
    np.testing.assert_array_equal(photon_array.flux, 0.)

    # Check picklability
    do_pickle(photon_array)

    # Check assignment via numpy [:]
    photon_array.x[:] = 5
    photon_array.y[:] = 17
    photon_array.flux[:] = 23
    np.testing.assert_array_equal(photon_array.x, 5.)
    np.testing.assert_array_equal(photon_array.y, 17.)
    np.testing.assert_array_equal(photon_array.flux, 23.)

    # Check assignment directly to the attributes
    photon_array.x = 25
    photon_array.y = 37
    photon_array.flux = 53
    np.testing.assert_array_equal(photon_array.x, 25.)
    np.testing.assert_array_equal(photon_array.y, 37.)
    np.testing.assert_array_equal(photon_array.flux, 53.)

    # Now create from shooting a profile
    obj = galsim.Exponential(flux=1.7, scale_radius=2.3)
    rng = galsim.UniformDeviate(1234)
    photon_array = obj.shoot(nphotons, rng)
    orig_x = photon_array.x.copy()
    orig_y = photon_array.y.copy()
    orig_flux = photon_array.flux.copy()
    assert len(photon_array.x) == nphotons
    assert len(photon_array.y) == nphotons
    assert len(photon_array.flux) == nphotons
    assert not photon_array.hasAllocatedWavelengths()
    assert not photon_array.hasAllocatedAngles()

    # Check arithmetic ops
    photon_array.x *= 5
    photon_array.y += 17
    photon_array.flux /= 23
    np.testing.assert_almost_equal(photon_array.x, orig_x * 5.)
    np.testing.assert_almost_equal(photon_array.y, orig_y + 17.)
    np.testing.assert_almost_equal(photon_array.flux, orig_flux / 23.)

    # Check picklability again with non-zero values
    do_pickle(photon_array)

    # Now assign to the optional arrays
    photon_array.dxdz = 0.17
    assert photon_array.hasAllocatedAngles()
    assert not photon_array.hasAllocatedWavelengths()
    np.testing.assert_array_equal(photon_array.dxdz, 0.17)
    np.testing.assert_array_equal(photon_array.dydz, 0.)

    photon_array.dydz = 0.59
    np.testing.assert_array_equal(photon_array.dxdz, 0.17)
    np.testing.assert_array_equal(photon_array.dydz, 0.59)

    # Start over to check that assigning to wavelength leaves dxdz, dydz alone.
    photon_array = obj.shoot(nphotons, rng)
    photon_array.wavelength = 500.
    assert photon_array.hasAllocatedWavelengths()
    assert not photon_array.hasAllocatedAngles()
    np.testing.assert_array_equal(photon_array.wavelength, 500)

    photon_array.dxdz = 0.23
    photon_array.dydz = 0.88
    photon_array.wavelength = 912.
    assert photon_array.hasAllocatedWavelengths()
    assert photon_array.hasAllocatedAngles()
    np.testing.assert_array_equal(photon_array.dxdz, 0.23)
    np.testing.assert_array_equal(photon_array.dydz, 0.88)
    np.testing.assert_array_equal(photon_array.wavelength, 912)

    # Check picklability again with non-zero values for everything
    do_pickle(photon_array)
Esempio n. 19
0
 def __generateGAL(self, re):
     self.gal = galsim.Exponential(flux=1.0, half_light_radius=re * self.galData.resolution)
Esempio n. 20
0
    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 = [[], [], [], []]
RGe2 = [[], [], [], []]
Me1 = [[], [], [], []]
Me2 = [[], [], [], []]
Wucha = [[], [], [], []]
Esempio n. 21
0
    def _inject_fake_objects(self, mbobs):
        """
        inject a simple model for quick tests
        """
        import galsim

        iconf = self.config['inject']

        model_name = iconf['model']
        hlr = iconf['hlr']
        flux = iconf['flux']

        if model_name == 'exp':
            model = galsim.Exponential(
                half_light_radius=hlr,
                flux=flux,
            )

        elif model_name == 'bdf':

            fracdev = iconf['fracdev']
            model = galsim.Add(
                galsim.Exponential(
                    half_light_radius=hlr,
                    flux=(1 - fracdev),
                ), galsim.DeVaucouleurs(
                    half_light_radius=hlr,
                    flux=fracdev,
                )).withFlux(flux)
        else:
            raise ValueError('bad model: "%s"' % model_name)

        if 'psf' in iconf:
            psf_model = galsim.Gaussian(fwhm=iconf['psf']['fwhm'], )
            method = 'fft'
        else:
            psf_model = None
            method = 'no_pixel'

        Tfake = ngmix.moments.fwhm_to_T(hlr / 0.5)

        for obslist in mbobs:
            obslist.meta['Tsky'] = Tfake
            for obs in obslist:

                gsimage = galsim.Image(
                    obs.image.copy(),
                    wcs=obs.jacobian.get_galsim_wcs(),
                )

                if psf_model is None:
                    psf_gsimage = galsim.Image(
                        obs.psf.image / obs.psf.image.sum(),
                        wcs=obs.psf.jacobian.get_galsim_wcs(),
                    )
                    psf_to_conv = galsim.InterpolatedImage(
                        psf_gsimage,
                        #x_interpolant='lanczos15',
                    )
                    obs.psf.image = psf_gsimage.array

                else:

                    pshape = obs.psf.image.shape
                    psf_gsimage = psf_model.drawImage(
                        nx=pshape[1],
                        ny=pshape[0],
                        wcs=obs.psf.jacobian.get_galsim_wcs(),
                    )

                    psf_to_conv = galsim.InterpolatedImage(psf_gsimage, )
                    obs.psf.image = psf_gsimage.array

                tmodel = galsim.Convolve(
                    model,
                    psf_to_conv,
                )

                tmodel.drawImage(
                    image=gsimage,
                    method=method,
                )

                image = gsimage.array

                wtmax = obs.weight.max()
                err = np.sqrt(1.0 / wtmax)

                image += self.rng.normal(
                    scale=err,
                    size=image.shape,
                )

                obs.image = image
Esempio n. 22
0
def test_gsparams():
    """Test withGSParams with some non-default gsparams
    """
    obj1 = galsim.Exponential(half_light_radius=1.7)
    obj2 = galsim.Pixel(scale=0.2)
    gsp = galsim.GSParams(folding_threshold=1.e-4, maxk_threshold=1.e-4, maximum_fft_size=1.e4)
    gsp2 = galsim.GSParams(folding_threshold=1.e-2, maxk_threshold=1.e-2)

    # Convolve
    conv = galsim.Convolve(obj1, obj2)
    conv1 = conv.withGSParams(gsp)
    assert conv.gsparams == galsim.GSParams()
    assert conv1.gsparams == gsp
    assert conv1.obj_list[0].gsparams == gsp
    assert conv1.obj_list[1].gsparams == gsp

    conv2 = galsim.Convolve(obj1.withGSParams(gsp), obj2.withGSParams(gsp))
    conv3 = galsim.Convolve(galsim.Exponential(half_light_radius=1.7, gsparams=gsp),
                            galsim.Pixel(scale=0.2))
    conv4 = galsim.Convolve(obj1, obj2, gsparams=gsp)
    assert conv != conv1
    assert conv1 == conv2
    assert conv1 == conv3
    assert conv1 == conv4
    print('stepk = ',conv.stepk, conv1.stepk)
    assert conv1.stepk < conv.stepk
    print('maxk = ',conv.maxk, conv1.maxk)
    assert conv1.maxk > conv.maxk

    conv5 = galsim.Convolve(obj1, obj2, gsparams=gsp, propagate_gsparams=False)
    assert conv5 != conv4
    assert conv5.gsparams == gsp
    assert conv5.obj_list[0].gsparams == galsim.GSParams()
    assert conv5.obj_list[1].gsparams == galsim.GSParams()

    conv6 = conv5.withGSParams(gsp2)
    assert conv6 != conv5
    assert conv6.gsparams == gsp2
    assert conv6.obj_list[0].gsparams == galsim.GSParams()
    assert conv6.obj_list[1].gsparams == galsim.GSParams()

    # AutoConvolve
    conv = galsim.AutoConvolve(obj1)
    conv1 = conv.withGSParams(gsp)
    assert conv.gsparams == galsim.GSParams()
    assert conv1.gsparams == gsp
    assert conv1.orig_obj.gsparams == gsp

    conv2 = galsim.AutoConvolve(obj1.withGSParams(gsp))
    conv3 = galsim.AutoConvolve(obj1, gsparams=gsp)
    assert conv != conv1
    assert conv1 == conv2
    assert conv1 == conv3
    print('stepk = ',conv.stepk, conv1.stepk)
    assert conv1.stepk < conv.stepk
    print('maxk = ',conv.maxk, conv1.maxk)
    assert conv1.maxk > conv.maxk

    conv4 = galsim.AutoConvolve(obj1, gsparams=gsp, propagate_gsparams=False)
    assert conv4 != conv3
    assert conv4.gsparams == gsp
    assert conv4.orig_obj.gsparams == galsim.GSParams()

    conv5 = conv4.withGSParams(gsp2)
    assert conv5 != conv4
    assert conv5.gsparams == gsp2
    assert conv5.orig_obj.gsparams == galsim.GSParams()

    # AutoCorrelate
    conv = galsim.AutoCorrelate(obj1)
    conv1 = conv.withGSParams(gsp)
    assert conv.gsparams == galsim.GSParams()
    assert conv1.gsparams == gsp
    assert conv1.orig_obj.gsparams == gsp

    conv2 = galsim.AutoCorrelate(obj1.withGSParams(gsp))
    conv3 = galsim.AutoCorrelate(obj1, gsparams=gsp)
    assert conv != conv1
    assert conv1 == conv2
    assert conv1 == conv3
    print('stepk = ',conv.stepk, conv1.stepk)
    assert conv1.stepk < conv.stepk
    print('maxk = ',conv.maxk, conv1.maxk)
    assert conv1.maxk > conv.maxk

    conv4 = galsim.AutoCorrelate(obj1, gsparams=gsp, propagate_gsparams=False)
    assert conv4 != conv3
    assert conv4.gsparams == gsp
    assert conv4.orig_obj.gsparams == galsim.GSParams()

    conv5 = conv4.withGSParams(gsp2)
    assert conv5 != conv4
    assert conv5.gsparams == gsp2
    assert conv5.orig_obj.gsparams == galsim.GSParams()

    # Deconvolve
    conv = galsim.Convolve(obj1, galsim.Deconvolve(obj2))
    conv1 = conv.withGSParams(gsp)
    assert conv.gsparams == galsim.GSParams()
    assert conv1.gsparams == gsp
    assert conv1.obj_list[0].gsparams == gsp
    assert conv1.obj_list[1].gsparams == gsp
    assert conv1.obj_list[1].orig_obj.gsparams == gsp

    conv2 = galsim.Convolve(obj1, galsim.Deconvolve(obj2.withGSParams(gsp)))
    conv3 = galsim.Convolve(obj1.withGSParams(gsp), galsim.Deconvolve(obj2))
    conv4 = galsim.Convolve(obj1, galsim.Deconvolve(obj2, gsparams=gsp))
    assert conv != conv1
    assert conv1 == conv2
    assert conv1 == conv3
    assert conv1 == conv4
    print('stepk = ',conv.stepk, conv1.stepk)
    assert conv1.stepk < conv.stepk
    print('maxk = ',conv.maxk, conv1.maxk)
    assert conv1.maxk > conv.maxk

    conv5 = galsim.Convolve(obj1, galsim.Deconvolve(obj2, gsparams=gsp, propagate_gsparams=False))
    assert conv5 != conv4
    assert conv5.gsparams == gsp
    assert conv5.obj_list[0].gsparams == gsp
    assert conv5.obj_list[1].gsparams == gsp
    assert conv5.obj_list[1].orig_obj.gsparams == galsim.GSParams()

    conv6 = conv5.withGSParams(gsp2)
    assert conv6 != conv5
    assert conv6.gsparams == gsp2
    assert conv6.obj_list[0].gsparams == gsp2
    assert conv6.obj_list[1].gsparams == gsp2
    assert conv6.obj_list[1].orig_obj.gsparams == galsim.GSParams()

    # FourierSqrt
    conv = galsim.Convolve(obj1, galsim.FourierSqrt(obj2))
    conv1 = conv.withGSParams(gsp)
    assert conv.gsparams == galsim.GSParams()
    assert conv1.gsparams == gsp
    assert conv1.obj_list[0].gsparams == gsp
    assert conv1.obj_list[1].gsparams == gsp
    assert conv1.obj_list[1].orig_obj.gsparams == gsp

    conv2 = galsim.Convolve(obj1, galsim.FourierSqrt(obj2.withGSParams(gsp)))
    conv3 = galsim.Convolve(obj1.withGSParams(gsp), galsim.FourierSqrt(obj2))
    conv4 = galsim.Convolve(obj1, galsim.FourierSqrt(obj2, gsparams=gsp))
    assert conv != conv1
    assert conv1 == conv2
    assert conv1 == conv3
    assert conv1 == conv4
    print('stepk = ',conv.stepk, conv1.stepk)
    assert conv1.stepk < conv.stepk
    print('maxk = ',conv.maxk, conv1.maxk)
    assert conv1.maxk > conv.maxk

    conv5 = galsim.Convolve(obj1, galsim.FourierSqrt(obj2, gsparams=gsp, propagate_gsparams=False))
    assert conv5 != conv4
    assert conv5.gsparams == gsp
    assert conv5.obj_list[0].gsparams == gsp
    assert conv5.obj_list[1].gsparams == gsp
    assert conv5.obj_list[1].orig_obj.gsparams == galsim.GSParams()

    conv6 = conv5.withGSParams(gsp2)
    assert conv6 != conv5
    assert conv6.gsparams == gsp2
    assert conv6.obj_list[0].gsparams == gsp2
    assert conv6.obj_list[1].gsparams == gsp2
    assert conv6.obj_list[1].orig_obj.gsparams == galsim.GSParams()
Esempio n. 23
0
def test_hsmparams_nodefault():
    """Test that when non-default hsmparams are used, the results change."""
    import time
    t1 = time.time()

    # First make some profile
    bulge = galsim.DeVaucouleurs(half_light_radius = 0.3)
    disk = galsim.Exponential(half_light_radius = 0.5)
    disk.applyShear(e1=0.2, e2=-0.3)
    psf = galsim.Kolmogorov(fwhm = 0.6)
    pix = galsim.Pixel(0.18)
    gal = bulge + disk   # equal weighting, i.e., B/T=0.5
    tot_gal = galsim.Convolve(gal, psf, pix)
    tot_psf = galsim.Convolve(psf, pix)
    tot_gal_image = tot_gal.draw(dx=0.18)
    tot_psf_image = tot_psf.draw(dx=0.18)

    # Check that recompute_flux changes give results that are as expected
    test_t = time.time()
    res = galsim.hsm.EstimateShear(tot_gal_image, tot_psf_image)
    dt = time.time() - test_t
    res2 = galsim.hsm.EstimateShear(tot_gal_image, tot_psf_image, recompute_flux = 'sum')
    assert(res.moments_amp < res2.moments_amp),'Incorrect behavior with recompute_flux=sum'
    res3 = galsim.hsm.EstimateShear(tot_gal_image, tot_psf_image, recompute_flux = 'none')
    assert(res3.moments_amp == 0),'Incorrect behavior with recompute_flux=none'

    # Check that results, timing change as expected with nsig_rg
    # For this, use Gaussian as galaxy and for ePSF, i.e., no extra pixel response
    p = galsim.Gaussian(fwhm=10.)
    g = galsim.Gaussian(fwhm=20.)
    g.applyShear(g1=0.5)
    obj = galsim.Convolve(g, p)
    im = obj.draw(dx=1.)
    psf_im = p.draw(dx=1.)
    test_t1 = time.time()
    g_res = galsim.hsm.EstimateShear(im, psf_im)
    test_t2 = time.time()
    g_res2 = galsim.hsm.EstimateShear(im, psf_im, hsmparams=galsim.hsm.HSMParams(nsig_rg=0.))
    dt2 = time.time()-test_t2
    dt1 = test_t2-test_t1
    if test_timing:
        assert(dt2 > dt1),'Should take longer to estimate shear without truncation of galaxy'
    assert(not equal_hsmshapedata(g_res, g_res2)),'Results should differ with diff nsig_rg'

    # Check that results, timing change as expected with max_moment_nsig2
    test_t2 = time.time()
    res2 = galsim.hsm.EstimateShear(tot_gal_image, tot_psf_image,
                                    hsmparams=galsim.hsm.HSMParams(max_moment_nsig2 = 9.))
    dt2 = time.time() - test_t2
    if test_timing:
        assert(dt2 < dt),'Should be faster to estimate shear with lower max_moment_nsig2'
    assert(not equal_hsmshapedata(res, res2)),'Outputs same despite change in max_moment_nsig2'
    assert(res.moments_sigma > res2.moments_sigma),'Sizes do not change as expected'
    assert(res.moments_amp > res2.moments_amp),'Amplitudes do not change as expected'

    # Check that max_amoment, max_ashift work as expected
    try:
        np.testing.assert_raises(RuntimeError, galsim.hsm.EstimateShear, tot_gal_image,
                                 tot_psf_image, hsmparams=galsim.hsm.HSMParams(max_amoment = 10.))
        np.testing.assert_raises(RuntimeError, galsim.hsm.EstimateShear, tot_gal_image,
                                 tot_psf_image, guess_x_centroid=47.,
                                 hsmparams=galsim.hsm.HSMParams(max_ashift=0.1))
    except ImportError:
        print 'The assert_raises tests require nose'


    t2 = time.time()
    print 'time for %s = %.2f'%(funcname(),t2-t1)
Esempio n. 24
0
def test_wavelength_sampler():
    nphotons = 1000
    obj = galsim.Exponential(flux=1.7, scale_radius=2.3)
    rng = galsim.UniformDeviate(1234)

    photon_array = obj.shoot(nphotons, rng)

    sed = galsim.SED(os.path.join(sedpath, 'CWW_E_ext.sed'), 'A',
                     'flambda').thin()
    bandpass = galsim.Bandpass(os.path.join(bppath, 'LSST_r.dat'), 'nm').thin()

    sampler = galsim.WavelengthSampler(sed, bandpass, rng)
    sampler.applyTo(photon_array)

    # Note: the underlying functionality of the sampleWavelengths function is tested
    # in test_sed.py.  So here we are really just testing that the wrapper class is
    # properly writing to the photon_array.wavelengths array.

    assert photon_array.hasAllocatedWavelengths()
    assert not photon_array.hasAllocatedAngles()

    print('mean wavelength = ', np.mean(photon_array.wavelength))
    print('min wavelength = ', np.min(photon_array.wavelength))
    print('max wavelength = ', np.max(photon_array.wavelength))

    assert np.min(photon_array.wavelength) > bandpass.blue_limit
    assert np.max(photon_array.wavelength) < bandpass.red_limit

    # This is a regression test based on the value at commit 134a119
    np.testing.assert_allclose(np.mean(photon_array.wavelength),
                               622.755128,
                               rtol=1.e-4)

    # If we use a flat SED (in photons/nm), then the mean sampled wavelength should very closely
    # match the bandpass effective wavelength.
    photon_array2 = galsim.PhotonArray(100000)
    sed2 = galsim.SED('1', 'nm', 'fphotons')
    sampler2 = galsim.WavelengthSampler(sed2, bandpass, rng)
    sampler2.applyTo(photon_array2)
    np.testing.assert_allclose(
        np.mean(photon_array2.wavelength),
        bandpass.effective_wavelength,
        rtol=0,
        atol=0.2,  # 2 Angstrom accuracy is pretty good
        err_msg="Mean sampled wavelength not close to effective_wavelength")

    # Test that using this as a surface op works properly.

    # First do the shooting and clipping manually.
    im1 = galsim.Image(64, 64, scale=1)
    im1.setCenter(0, 0)
    photon_array.flux[photon_array.wavelength < 600] = 0.
    photon_array.addTo(im1)

    # Make a dummy surface op that clips any photons with lambda < 600
    class Clip600(object):
        def applyTo(self, photon_array, local_wcs=None):
            photon_array.flux[photon_array.wavelength < 600] = 0.

    # Use (a new) sampler and clip600 as surface_ops in drawImage
    im2 = galsim.Image(64, 64, scale=1)
    im2.setCenter(0, 0)
    clip600 = Clip600()
    rng2 = galsim.BaseDeviate(1234)
    sampler2 = galsim.WavelengthSampler(sed, bandpass, rng2)
    obj.drawImage(im2,
                  method='phot',
                  n_photons=nphotons,
                  use_true_center=False,
                  surface_ops=[sampler2, clip600],
                  rng=rng2)
    print('sum = ', im1.array.sum(), im2.array.sum())
    np.testing.assert_array_equal(im1.array, im2.array)
Esempio n. 25
0
def main(argv):
    """
    Make images similar to that done for the Great08 challenge:
      - Each fits file is 10 x 10 postage stamps.
        (The real Great08 images are 100x100, but in the interest of making the Demo
         script a bit quicker, we only build 100 stars and 100 galaxies.)
      - Each postage stamp is 40 x 40 pixels.
      - One image is all stars.
      - A second image is all galaxies.
      - Applied shear is the same for each galaxy.
      - Galaxies are oriented randomly, but in pairs to cancel shape noise.
      - Noise is Poisson using a nominal sky value of 1.e6.
      - Galaxies are Exponential profiles.
    """
    logging.basicConfig(format="%(message)s",
                        level=logging.INFO,
                        stream=sys.stdout)
    logger = logging.getLogger("demo5")

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

    nx_tiles = 10  #
    ny_tiles = 10  #
    stamp_xsize = 40  #
    stamp_ysize = 40  #

    random_seed = 6424512  #

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

    # Make output directory if not already present.
    if not os.path.isdir('output'):
        os.mkdir('output')
    psf_file_name = os.path.join('output', 'g08_psf.fits')
    psf_beta = 3  #
    psf_fwhm = 2.85  # arcsec (=pixels)
    psf_trunc = 2. * psf_fwhm  # arcsec (=pixels)
    psf_e1 = -0.019  #
    psf_e2 = -0.007  #

    gal_file_name = os.path.join('output', 'g08_gal.fits')
    gal_signal_to_noise = 200  # Great08 "LowNoise" run
    gal_resolution = 0.98  # r_gal / r_psf (use r = half_light_radius)
    # Note: Great08 defined their resolution as r_obs / r_psf, using the convolved
    #       size rather than the pre-convolved size.
    #       Therefore, our r_gal/r_psf = 0.98 approximately corresponds to
    #       their r_obs / r_psf = 1.4.

    gal_ellip_rms = 0.2  # using "distortion" definition of ellipticity:
    #   e = (a^2-b^2)/(a^2+b^2), where a and b are the
    #   semi-major and semi-minor axes, respectively.
    gal_ellip_max = 0.6  # Maximum value of e, to avoid getting near e=1.
    gal_g1 = 0.013  # Applied shear, using normal shear definition:
    gal_g2 = -0.008  #   g = (a-b)/(a+b)

    shift_radius = 1.0  # arcsec (=pixels)

    logger.info('Starting demo script 5 using:')
    logger.info('    - image with %d x %d postage stamps', nx_tiles, ny_tiles)
    logger.info('    - postage stamps of size %d x %d pixels', stamp_xsize,
                stamp_ysize)
    logger.info('    - Moffat PSF (beta = %.1f, FWHM = %.2f, trunc = %.2f),',
                psf_beta, psf_fwhm, psf_trunc)
    logger.info('    - PSF ellip = (%.3f,%.3f)', psf_e1, psf_e2)
    logger.info('    - Exponential galaxies')
    logger.info('    - Resolution (r_gal / r_psf) = %.2f', gal_resolution)
    logger.info('    - Ellipticities have rms = %.1f, max = %.1f',
                gal_ellip_rms, gal_ellip_max)
    logger.info('    - Applied gravitational shear = (%.3f,%.3f)', gal_g1,
                gal_g2)
    logger.info('    - Poisson noise (sky level = %.1e).', sky_level)
    logger.info('    - Centroid shifts up to = %.2f pixels', shift_radius)

    # Define the PSF profile
    psf = galsim.Moffat(beta=psf_beta, fwhm=psf_fwhm, trunc=psf_trunc)

    # When something can be constructed from multiple sizes, e.g. Moffat, then
    # you can get any size out even if it wasn't the way the object was constructed.
    # In this case, we extract the half-light radius, even though we built it with fwhm.
    # We'll use this later to set the galaxy's half-light radius in terms of a resolution.
    psf_re = psf.getHalfLightRadius()

    psf = psf.shear(e1=psf_e1, e2=psf_e2)
    logger.debug('Made PSF profile')

    # Define the galaxy profile

    # First figure out the size we need from the resolution
    gal_re = psf_re * gal_resolution

    # Make the galaxy profile starting with flux = 1.
    gal = galsim.Exponential(flux=1., half_light_radius=gal_re)
    logger.debug('Made galaxy profile')

    # This profile is placed with different orientations and noise realizations
    # at each postage stamp in the gal image.
    gal_image = galsim.ImageF(stamp_xsize * nx_tiles - 1,
                              stamp_ysize * ny_tiles - 1,
                              scale=pixel_scale)
    psf_image = galsim.ImageF(stamp_xsize * nx_tiles - 1,
                              stamp_ysize * ny_tiles - 1,
                              scale=pixel_scale)

    shift_radius_sq = shift_radius**2

    first_in_pair = True  # Make pairs that are rotated by 90 degrees

    k = 0
    for iy in range(ny_tiles):
        for ix in range(nx_tiles):
            # The normal procedure for setting random numbers in GalSim is to start a new
            # random number generator for each object using sequential seed values.
            # This sounds weird at first (especially if you were indoctrinated by Numerical
            # Recipes), but for the boost random number generator we use, the "random"
            # number sequences produced from sequential initial seeds are highly uncorrelated.
            #
            # The reason for this procedure is that when we use multiple processes to build
            # our images, we want to make sure that the results are deterministic regardless
            # of the way the objects get parcelled out to the different processes.
            #
            # Of course, this script isn't using multiple processes, so it isn't required here.
            # However, we do it nonetheless in order to get the same results as the config
            # version of this demo script (demo5.yaml).
            ud = galsim.UniformDeviate(random_seed + k)

            # Any kind of random number generator can take another RNG as its first
            # argument rather than a seed value.  This makes both objects use the same
            # underlying generator for their pseudo-random values.
            gd = galsim.GaussianDeviate(ud, sigma=gal_ellip_rms)

            # The -1's in the next line are to provide a border of
            # 1 pixel between postage stamps
            b = galsim.BoundsI(ix * stamp_xsize + 1,
                               (ix + 1) * stamp_xsize - 1,
                               iy * stamp_ysize + 1,
                               (iy + 1) * stamp_ysize - 1)
            sub_gal_image = gal_image[b]
            sub_psf_image = psf_image[b]

            # Great08 randomized the locations of the two galaxies in each pair,
            # but for simplicity, we just do them in sequential postage stamps.
            if first_in_pair:
                # Use a random orientation:
                beta = ud() * 2. * math.pi * galsim.radians

                # Determine the ellipticity to use for this galaxy.
                ellip = 1
                while (ellip > gal_ellip_max):
                    # Don't do `ellip = math.fabs(gd())`
                    # Python basically implements this as a macro, so gd() is called twice!
                    val = gd()
                    ellip = math.fabs(val)

                first_in_pair = False
            else:
                # Use the previous ellip and beta + 90 degrees
                beta += 90 * galsim.degrees
                first_in_pair = True

            # Make a new copy of the galaxy with an applied e1/e2-type distortion
            # by specifying the ellipticity and a real-space position angle
            this_gal = gal.shear(e=ellip, beta=beta)

            # Apply the gravitational reduced shear by specifying g1/g2
            this_gal = this_gal.shear(g1=gal_g1, g2=gal_g2)

            # Apply a random shift_radius:
            rsq = 2 * shift_radius_sq
            while (rsq > shift_radius_sq):
                dx = (2 * ud() - 1) * shift_radius
                dy = (2 * ud() - 1) * shift_radius
                rsq = dx**2 + dy**2

            this_gal = this_gal.shift(dx, dy)
            # Note that the shifted psf that we create here is purely for the purpose of being able
            # to draw a separate, shifted psf image.  We do not use it when convolving the galaxy
            # with the psf.
            this_psf = psf.shift(dx, dy)

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

            # Draw the image
            final_gal.drawImage(sub_gal_image)

            # Now add an appropriate amount of noise to get our desired S/N
            # There are lots of definitions of S/N, but here is the one used by Great08
            # We use a weighted integral of the flux:
            #   S = sum W(x,y) I(x,y) / sum W(x,y)
            #   N^2 = Var(S) = sum W(x,y)^2 Var(I(x,y)) / (sum W(x,y))^2
            # Now we assume that Var(I(x,y)) is constant so
            #   Var(I(x,y)) = noise_var
            # We also assume that we are using a matched filter for W, so W(x,y) = I(x,y).
            # Then a few things cancel and we find that
            # S/N = sqrt( sum I(x,y)^2 / noise_var )
            #
            # The above procedure is encapsulated in the function image.addNoiseSNR which
            # sets the flux appropriately given the variance of the noise model.
            # In our case, noise_var = sky_level_pixel
            sky_level_pixel = sky_level * pixel_scale**2
            noise = galsim.PoissonNoise(ud, sky_level=sky_level_pixel)
            sub_gal_image.addNoiseSNR(noise, gal_signal_to_noise)

            # Draw the PSF image
            # No noise on PSF images.  Just draw it as is.
            this_psf.drawImage(sub_psf_image)

            # For first instance, measure moments
            if ix == 0 and iy == 0:
                psf_shape = sub_psf_image.FindAdaptiveMom()
                temp_e = psf_shape.observed_shape.e
                if temp_e > 0.0:
                    g_to_e = psf_shape.observed_shape.g / temp_e
                else:
                    g_to_e = 0.0
                logger.info(
                    'Measured best-fit elliptical Gaussian for first PSF image: '
                )
                logger.info('  g1, g2, sigma = %7.4f, %7.4f, %7.4f (pixels)',
                            g_to_e * psf_shape.observed_shape.e1,
                            g_to_e * psf_shape.observed_shape.e2,
                            psf_shape.moments_sigma)

            x = b.center().x
            y = b.center().y
            logger.info(
                'Galaxy (%d,%d): center = (%.0f,%0.f)  (e,beta) = (%.4f,%.3f)',
                ix, iy, x, y, ellip, beta / galsim.radians)
            k = k + 1

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

    # Now write the images to disk.
    psf_image.write(psf_file_name)
    logger.info('Wrote PSF file %s', psf_file_name)

    gal_image.write(gal_file_name)
    logger.info('Wrote image to %r',
                gal_file_name)  # using %r adds quotes around filename for us
Esempio n. 26
0
def test_photon_array():
    """Test the basic methods of PhotonArray class
    """
    nphotons = 1000

    # First create from scratch
    photon_array = galsim.PhotonArray(nphotons)
    assert len(photon_array.x) == nphotons
    assert len(photon_array.y) == nphotons
    assert len(photon_array.flux) == nphotons
    assert not photon_array.hasAllocatedWavelengths()
    assert not photon_array.hasAllocatedAngles()

    # Initial values should all be 0
    np.testing.assert_array_equal(photon_array.x, 0.)
    np.testing.assert_array_equal(photon_array.y, 0.)
    np.testing.assert_array_equal(photon_array.flux, 0.)

    # Check picklability
    do_pickle(photon_array)

    # Check assignment via numpy [:]
    photon_array.x[:] = 5
    photon_array.y[:] = 17
    photon_array.flux[:] = 23
    np.testing.assert_array_equal(photon_array.x, 5.)
    np.testing.assert_array_equal(photon_array.y, 17.)
    np.testing.assert_array_equal(photon_array.flux, 23.)

    # Check assignment directly to the attributes
    photon_array.x = 25
    photon_array.y = 37
    photon_array.flux = 53
    np.testing.assert_array_equal(photon_array.x, 25.)
    np.testing.assert_array_equal(photon_array.y, 37.)
    np.testing.assert_array_equal(photon_array.flux, 53.)

    # Now create from shooting a profile
    obj = galsim.Exponential(flux=1.7, scale_radius=2.3)
    rng = galsim.UniformDeviate(1234)
    photon_array = obj.shoot(nphotons, rng)
    orig_x = photon_array.x.copy()
    orig_y = photon_array.y.copy()
    orig_flux = photon_array.flux.copy()
    assert len(photon_array.x) == nphotons
    assert len(photon_array.y) == nphotons
    assert len(photon_array.flux) == nphotons
    assert not photon_array.hasAllocatedWavelengths()
    assert not photon_array.hasAllocatedAngles()

    # Check arithmetic ops
    photon_array.x *= 5
    photon_array.y += 17
    photon_array.flux /= 23
    np.testing.assert_almost_equal(photon_array.x, orig_x * 5.)
    np.testing.assert_almost_equal(photon_array.y, orig_y + 17.)
    np.testing.assert_almost_equal(photon_array.flux, orig_flux / 23.)

    # Check picklability again with non-zero values
    do_pickle(photon_array)

    # Now assign to the optional arrays
    photon_array.dxdz = 0.17
    assert photon_array.hasAllocatedAngles()
    assert not photon_array.hasAllocatedWavelengths()
    np.testing.assert_array_equal(photon_array.dxdz, 0.17)
    np.testing.assert_array_equal(photon_array.dydz, 0.)

    photon_array.dydz = 0.59
    np.testing.assert_array_equal(photon_array.dxdz, 0.17)
    np.testing.assert_array_equal(photon_array.dydz, 0.59)

    # Start over to check that assigning to wavelength leaves dxdz, dydz alone.
    photon_array = obj.shoot(nphotons, rng)
    photon_array.wavelength = 500.
    assert photon_array.hasAllocatedWavelengths()
    assert not photon_array.hasAllocatedAngles()
    np.testing.assert_array_equal(photon_array.wavelength, 500)

    photon_array.dxdz = 0.23
    photon_array.dydz = 0.88
    photon_array.wavelength = 912.
    assert photon_array.hasAllocatedWavelengths()
    assert photon_array.hasAllocatedAngles()
    np.testing.assert_array_equal(photon_array.dxdz, 0.23)
    np.testing.assert_array_equal(photon_array.dydz, 0.88)
    np.testing.assert_array_equal(photon_array.wavelength, 912)

    # Check toggling is_corr
    assert not photon_array.isCorrelated()
    photon_array.setCorrelated()
    assert photon_array.isCorrelated()
    photon_array.setCorrelated(False)
    assert not photon_array.isCorrelated()
    photon_array.setCorrelated(True)
    assert photon_array.isCorrelated()

    # Check rescaling the total flux
    flux = photon_array.flux.sum()
    np.testing.assert_almost_equal(photon_array.getTotalFlux(), flux)
    photon_array.scaleFlux(17)
    np.testing.assert_almost_equal(photon_array.getTotalFlux(), 17 * flux)
    photon_array.setTotalFlux(199)
    np.testing.assert_almost_equal(photon_array.getTotalFlux(), 199)

    # Check rescaling the positions
    x = photon_array.x.copy()
    y = photon_array.y.copy()
    photon_array.scaleXY(1.9)
    np.testing.assert_almost_equal(photon_array.x, 1.9 * x)
    np.testing.assert_almost_equal(photon_array.y, 1.9 * y)

    # Check ways to assign to photons
    pa1 = galsim.PhotonArray(50)
    pa1.x = photon_array.x[:50]
    for i in range(50):
        pa1.y[i] = photon_array.y[i]
    pa1.flux[0:50] = photon_array.flux[:50]
    pa1.dxdz = photon_array.dxdz[:50]
    pa1.dydz = photon_array.dydz[:50]
    pa1.wavelength = photon_array.wavelength[:50]
    np.testing.assert_almost_equal(pa1.x, photon_array.x[:50])
    np.testing.assert_almost_equal(pa1.y, photon_array.y[:50])
    np.testing.assert_almost_equal(pa1.flux, photon_array.flux[:50])
    np.testing.assert_almost_equal(pa1.dxdz, photon_array.dxdz[:50])
    np.testing.assert_almost_equal(pa1.dydz, photon_array.dydz[:50])
    np.testing.assert_almost_equal(pa1.wavelength,
                                   photon_array.wavelength[:50])

    # Check assignAt
    pa2 = galsim.PhotonArray(100)
    pa2.assignAt(0, pa1)
    pa2.assignAt(50, pa1)
    np.testing.assert_almost_equal(pa2.x[:50], pa1.x)
    np.testing.assert_almost_equal(pa2.y[:50], pa1.y)
    np.testing.assert_almost_equal(pa2.flux[:50], pa1.flux)
    np.testing.assert_almost_equal(pa2.dxdz[:50], pa1.dxdz)
    np.testing.assert_almost_equal(pa2.dydz[:50], pa1.dydz)
    np.testing.assert_almost_equal(pa2.wavelength[:50], pa1.wavelength)
    np.testing.assert_almost_equal(pa2.x[50:], pa1.x)
    np.testing.assert_almost_equal(pa2.y[50:], pa1.y)
    np.testing.assert_almost_equal(pa2.flux[50:], pa1.flux)
    np.testing.assert_almost_equal(pa2.dxdz[50:], pa1.dxdz)
    np.testing.assert_almost_equal(pa2.dydz[50:], pa1.dydz)
    np.testing.assert_almost_equal(pa2.wavelength[50:], pa1.wavelength)

    # Error if it doesn't fit.
    assert_raises(ValueError, pa2.assignAt, 90, pa1)

    # Test some trivial usage of makeFromImage
    zero = galsim.Image(4, 4, init_value=0)
    photons = galsim.PhotonArray.makeFromImage(zero)
    print('photons = ', photons)
    assert len(photons) == 16
    np.testing.assert_array_equal(photons.flux, 0.)

    ones = galsim.Image(4, 4, init_value=1)
    photons = galsim.PhotonArray.makeFromImage(ones)
    print('photons = ', photons)
    assert len(photons) == 16
    np.testing.assert_almost_equal(photons.flux, 1.)

    tens = galsim.Image(4, 4, init_value=8)
    photons = galsim.PhotonArray.makeFromImage(tens, max_flux=5.)
    print('photons = ', photons)
    assert len(photons) == 32
    np.testing.assert_almost_equal(photons.flux, 4.)

    assert_raises(ValueError,
                  galsim.PhotonArray.makeFromImage,
                  zero,
                  max_flux=0.)
    assert_raises(ValueError,
                  galsim.PhotonArray.makeFromImage,
                  zero,
                  max_flux=-2)

    # Check some other errors
    undef = galsim.Image()
    assert_raises(galsim.GalSimUndefinedBoundsError, pa2.addTo, undef)

    # Check picklability again with non-zero values for everything
    do_pickle(photon_array)
Esempio n. 27
0
def test_add_flux_scaling():
    """Test flux scaling for Add.
    """
    import time
    t1 = time.time()

    # decimal point to go to for parameter value comparisons
    param_decimal = 12

    # init with Gaussian and Exponential only (should be ok given last tests)
    obj = galsim.Add([
        galsim.Gaussian(sigma=test_sigma, flux=test_flux * .5),
        galsim.Exponential(scale_radius=test_scale, flux=test_flux * .5)
    ])
    obj *= 2.
    np.testing.assert_almost_equal(
        obj.getFlux(),
        test_flux * 2.,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __imul__.")
    obj = galsim.Add([
        galsim.Gaussian(sigma=test_sigma, flux=test_flux * .5),
        galsim.Exponential(scale_radius=test_scale, flux=test_flux * .5)
    ])
    obj /= 2.
    np.testing.assert_almost_equal(
        obj.getFlux(),
        test_flux / 2.,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __idiv__.")
    obj = galsim.Add([
        galsim.Gaussian(sigma=test_sigma, flux=test_flux * .5),
        galsim.Exponential(scale_radius=test_scale, flux=test_flux * .5)
    ])
    obj2 = obj * 2.
    # First test that original obj is unharmed... (also tests that .copy() is working)
    np.testing.assert_almost_equal(
        obj.getFlux(),
        test_flux,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __rmul__ (original).")
    # Then test new obj2 flux
    np.testing.assert_almost_equal(
        obj2.getFlux(),
        test_flux * 2.,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __rmul__ (result).")
    obj = galsim.Add([
        galsim.Gaussian(sigma=test_sigma, flux=test_flux * .5),
        galsim.Exponential(scale_radius=test_scale, flux=test_flux * .5)
    ])
    obj2 = 2. * obj
    # First test that original obj is unharmed... (also tests that .copy() is working)
    np.testing.assert_almost_equal(
        obj.getFlux(),
        test_flux,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __mul__ (original).")
    # Then test new obj2 flux
    np.testing.assert_almost_equal(
        obj2.getFlux(),
        test_flux * 2.,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __mul__ (result).")
    obj = galsim.Add([
        galsim.Gaussian(sigma=test_sigma, flux=test_flux * .5),
        galsim.Exponential(scale_radius=test_scale, flux=test_flux * .5)
    ])
    obj2 = obj / 2.
    # First test that original obj is unharmed... (also tests that .copy() is working)
    np.testing.assert_almost_equal(
        obj.getFlux(),
        test_flux,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __div__ (original).")
    # Then test new obj2 flux
    np.testing.assert_almost_equal(
        obj2.getFlux(),
        test_flux / 2.,
        decimal=param_decimal,
        err_msg="Flux param inconsistent after __div__ (result).")
    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
Esempio n. 28
0
def main(argv):
    """
    Make a fits image cube using parameters from an input catalog
      - 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.
      - Only galaxies.  No stars.
      - PSF is Moffat
      - Each galaxy is bulge plus disk: deVaucouleurs + Exponential.
      - The catalog's columns are:
         0 PSF beta (Moffat exponent)
         1 PSF FWHM
         2 PSF e1
         3 PSF e2
         4 PSF trunc
         5 Disc half-light-radius
         6 Disc e1
         7 Disc e2
         8 Bulge half-light-radius
         9 Bulge e1
        10 Bulge e2
        11 Galaxy dx (the two components have same center)
        12 Galaxy dy
      - Applied shear is the same for each galaxy
      - Noise is Poisson using a nominal sky value of 1.e6
    """
    logging.basicConfig(format="%(message)s",
                        level=logging.INFO,
                        stream=sys.stdout)
    logger = logging.getLogger("demo4")

    # Define some parameters we'll use below and make directories if needed.
    cat_file_name = os.path.join('input', 'galsim_default_input.asc')
    if not os.path.isdir('output'):
        os.mkdir('output')
    multi_file_name = os.path.join('output', 'multi.fits')

    random_seed = 8241573
    sky_level = 1.e6  # ADU / arcsec^2
    pixel_scale = 1.0  # arcsec / pixel  (size units in input catalog are pixels)
    gal_flux = 1.e6  # arbitrary choice, makes nice (not too) noisy images
    gal_g1 = -0.009  #
    gal_g2 = 0.011  #
    xsize = 64  # pixels
    ysize = 64  # pixels

    logger.info('Starting demo script 4 using:')
    logger.info('    - parameters taken from catalog %r', cat_file_name)
    logger.info('    - Moffat PSF (parameters from catalog)')
    logger.info('    - pixel scale = %.2f', pixel_scale)
    logger.info('    - Bulge + Disc galaxies (parameters from catalog)')
    logger.info('    - Applied gravitational shear = (%.3f,%.3f)', gal_g1,
                gal_g2)
    logger.info('    - Poisson noise (sky level = %.1e).', sky_level)

    # Read in the input catalog
    cat = galsim.Catalog(cat_file_name)

    # save a list of the galaxy images in the "images" list variable:
    images = []
    for k in range(cat.nobjects):
        # Initialize the (pseudo-)random number generator that we will be using below.
        # Use a different random seed for each object to get different noise realizations.
        # Using sequential random seeds here is safer than it sounds.  We use Mersenne Twister
        # random number generators that are designed to be used with this kind of seeding.
        # However, to be extra safe, we actually initialize one random number generator with this
        # seed, generate and throw away two random values with that, and then use the next value
        # to seed a completely different Mersenne Twister RNG.  The result is that successive
        # RNGs created this way produce very independent random number streams.
        rng = galsim.BaseDeviate(random_seed + k + 1)

        # Take the Moffat beta from the first column (called 0) of the input catalog:
        # Note: cat.get(k,col) returns a string.  To get the value as a float, use either
        #       cat.getFloat(k,col) or float(cat.get(k,col))
        beta = cat.getFloat(k, 0)
        # A Moffat's size may be either scale_radius, fwhm, or half_light_radius.
        # Here we use fwhm, taking from the catalog as well.
        fwhm = cat.getFloat(k, 1)
        # A Moffat 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).
        trunc = cat.getFloat(k, 4)
        # Note: You may omit the flux, since the default is flux=1.
        psf = galsim.Moffat(beta=beta, fwhm=fwhm, trunc=trunc)

        # Take the (e1, e2) shape parameters from the catalog as well.
        psf = psf.shear(e1=cat.getFloat(k, 2), e2=cat.getFloat(k, 3))

        # Galaxy is a bulge + disk with parameters taken from the catalog:
        disk = galsim.Exponential(flux=0.6,
                                  half_light_radius=cat.getFloat(k, 5))
        disk = disk.shear(e1=cat.getFloat(k, 6), e2=cat.getFloat(k, 7))

        bulge = galsim.DeVaucouleurs(flux=0.4,
                                     half_light_radius=cat.getFloat(k, 8))
        bulge = bulge.shear(e1=cat.getFloat(k, 9), e2=cat.getFloat(k, 10))

        # The flux of an Add object is the sum of the component fluxes.
        # Note that in demo3.py, a similar addition was performed by the binary operator "+".
        gal = galsim.Add([disk, bulge])
        # This flux may be overridden by withFlux.  The relative fluxes of the components
        # remains the same, but the total flux is set to gal_flux.
        gal = gal.withFlux(gal_flux)
        gal = gal.shear(g1=gal_g1, g2=gal_g2)

        # The center of the object is normally placed at the center of the postage stamp image.
        # You can change that with shift:
        gal = gal.shift(dx=cat.getFloat(k, 11), dy=cat.getFloat(k, 12))

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

        # Draw the profile
        image = galsim.ImageF(xsize, ysize)
        final.drawImage(image, scale=pixel_scale)

        # Add Poisson noise to the image:
        image.addNoise(galsim.PoissonNoise(rng, sky_level * pixel_scale**2))

        logger.info('Drew image for object at row %d in the input catalog' % k)

        # Add the image to our list of images
        images.append(image)

    # Now write the images to a multi-extension fits file.  Each image will be in its own HDU.
    galsim.fits.writeMulti(images, multi_file_name)
    logger.info('Images written to multi-extension fits file %r',
                multi_file_name)
Esempio n. 29
0
def test_hlr():
    """Test the calculateHLR method.
    """
    import time
    t1 = time.time()

    # 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)
    test_hlr = im.calculateHLR(flux=e1.flux)
    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+size*2, 8234, 8234+size)
    bounds = galsim.BoundsI(1, 1+size*2, 1, 1+size)
    #offset = galsim.PositionD(29,1)
    offset = galsim.PositionD(0,0)
    im = e1.drawImage(scale=0.1, bounds=bounds, offset=offset)
    test_hlr = im.calculateHLR(flux=e1.flux, 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.")

    t2 = time.time()
    print 'time for %s = %.2f'%(funcname(),t2-t1)
Esempio n. 30
0
def test_hsmparams_nodefault():
    """Test that when non-default hsmparams are used, the results change."""
    import time
    # First make some profile
    bulge = galsim.DeVaucouleurs(half_light_radius=0.3)
    disk = galsim.Exponential(half_light_radius=0.5)
    disk = disk.shear(e1=0.2, e2=-0.3)
    psf = galsim.Kolmogorov(fwhm=0.6)
    gal = bulge + disk  # equal weighting, i.e., B/T=0.5
    tot_gal = galsim.Convolve(gal, psf)
    tot_gal_image = tot_gal.drawImage(scale=0.18)
    tot_psf_image = psf.drawImage(scale=0.18)

    # Check that recompute_flux changes give results that are as expected
    test_t = time.time()
    res = galsim.hsm.EstimateShear(tot_gal_image, tot_psf_image)
    dt = time.time() - test_t
    res2 = galsim.hsm.EstimateShear(tot_gal_image,
                                    tot_psf_image,
                                    recompute_flux='sum')
    assert (res.moments_amp <
            res2.moments_amp), 'Incorrect behavior with recompute_flux=sum'
    res3 = galsim.hsm.EstimateShear(tot_gal_image,
                                    tot_psf_image,
                                    recompute_flux='none')
    assert (
        res3.moments_amp == 0), 'Incorrect behavior with recompute_flux=none'

    # Check that results, timing change as expected with nsig_rg
    # For this, use Gaussian as galaxy and for ePSF, i.e., no extra pixel response
    p = galsim.Gaussian(fwhm=10.)
    g = galsim.Gaussian(fwhm=20.)
    g = g.shear(g1=0.5)
    obj = galsim.Convolve(g, p)
    # HSM allows a slop of 1.e-8 on nsig_rg, which means that default float32 images don't
    # actually end up with different result when using nsig_rg=0. rather than 3.
    im = obj.drawImage(scale=1., method='no_pixel', dtype=float)
    psf_im = p.drawImage(scale=1., method='no_pixel', dtype=float)
    test_t1 = time.time()
    g_res = galsim.hsm.EstimateShear(im, psf_im)
    test_t2 = time.time()
    g_res2 = galsim.hsm.EstimateShear(
        im, psf_im, hsmparams=galsim.hsm.HSMParams(nsig_rg=0.))
    dt2 = time.time() - test_t2
    dt1 = test_t2 - test_t1
    if test_timing:
        assert (
            dt2 > dt1
        ), 'Should take longer to estimate shear without truncation of galaxy'
    assert (not equal_hsmshapedata(
        g_res, g_res2)), 'Results should differ with diff nsig_rg'
    assert g_res != g_res2, 'Results should differ with diff nsig_rg'

    # Check that results, timing change as expected with max_moment_nsig2
    test_t2 = time.time()
    res2 = galsim.hsm.EstimateShear(
        tot_gal_image,
        tot_psf_image,
        hsmparams=galsim.hsm.HSMParams(max_moment_nsig2=9.))
    dt2 = time.time() - test_t2
    if test_timing:
        assert (
            dt2 < dt
        ), 'Should be faster to estimate shear with lower max_moment_nsig2'
    assert (not equal_hsmshapedata(
        res, res2)), 'Outputs same despite change in max_moment_nsig2'
    assert res != res2, 'Outputs same despite change in max_moment_nsig2'
    assert (res.moments_sigma >
            res2.moments_sigma), 'Sizes do not change as expected'
    assert (res.moments_amp >
            res2.moments_amp), 'Amplitudes do not change as expected'

    # Check that max_amoment, max_ashift work as expected
    assert_raises(galsim.GalSimError,
                  galsim.hsm.EstimateShear,
                  tot_gal_image,
                  tot_psf_image,
                  hsmparams=galsim.hsm.HSMParams(max_amoment=10.))
    assert_raises(galsim.GalSimError,
                  galsim.hsm.EstimateShear,
                  tot_gal_image,
                  tot_psf_image,
                  guess_centroid=galsim.PositionD(47.,
                                                  tot_gal_image.true_center.y),
                  hsmparams=galsim.hsm.HSMParams(max_ashift=0.1))