def test_deltaFunction_convolution():
    """Test convolutions using the Delta function.
    """
    # Convolve two delta functions
    delta = galsim.DeltaFunction(flux=2.0)
    delta2 = galsim.DeltaFunction(flux=3.0)
    delta_delta = galsim.Convolve(delta, delta2)
    np.testing.assert_almost_equal(delta_delta.flux, 6.0)

    # Test that no-ops on gaussians dont affect convolution
    gauss = galsim.Gaussian(sigma=1.0)

    delta_shr = delta.shear(g1=0.3, g2=0.1)
    delta_conv = galsim.Convolve(gauss, delta_shr)
    np.testing.assert_almost_equal(delta_conv.flux, 2.0)

    delta_dil = delta.dilate(2.0)
    delta_conv = galsim.Convolve(gauss, delta_dil)
    np.testing.assert_almost_equal(delta_conv.flux, 2.0)

    delta_tfm = delta.transform(dudx=1.25, dudy=0.0, dvdx=0.0, dvdy=0.8)
    delta_conv = galsim.Convolve(gauss, delta_tfm)
    np.testing.assert_almost_equal(delta_conv.flux, 2.0)

    delta_rot = delta.rotate(45 * galsim.radians)
    delta_conv = galsim.Convolve(gauss, delta_rot)
    np.testing.assert_almost_equal(delta_conv.flux, 2.0)
Beispiel #2
0
def test_gaussian_shoot():
    """Test Gaussian with photon shooting.  Particularly the flux of the final image.
    """
    rng = galsim.BaseDeviate(1234)
    obj = galsim.Gaussian(fwhm=3.5, flux=1.e4)
    im = galsim.Image(100, 100, scale=1)
    im.setCenter(0, 0)
    added_flux, photons = obj.drawPhot(im,
                                       poisson_flux=False,
                                       rng=rng.duplicate())
    print('obj.flux = ', obj.flux)
    print('added_flux = ', added_flux)
    print('photon fluxes = ', photons.flux.min(), '..', photons.flux.max())
    print('image flux = ', im.array.sum())
    assert np.isclose(added_flux, obj.flux)
    assert np.isclose(im.array.sum(), obj.flux)
    photons2 = obj.makePhot(poisson_flux=False, rng=rng.duplicate())
    assert photons2 == photons, "Gaussian makePhot not equivalent to drawPhot"

    # Can treat the profile as a convolution of a delta function and put it in a photon_ops list.
    delta = galsim.DeltaFunction(flux=1.e4)
    psf = galsim.Gaussian(fwhm=3.5)
    photons3 = delta.makePhot(poisson_flux=False,
                              rng=rng.duplicate(),
                              photon_ops=[psf])
    assert photons3 == photons, "Using Gaussian in photon_ops not equivalent to drawPhot"
Beispiel #3
0
def test_sk_shoot():
    """Test SecondKick with photon shooting.  Particularly the flux of the final image.
    """
    rng = galsim.BaseDeviate(1234)
    obj = galsim.SecondKick(lam=500, r0=0.2, diam=4, flux=1.e4)
    im = galsim.Image(500, 500, scale=1)
    im.setCenter(0, 0)
    added_flux, photons = obj.drawPhot(im,
                                       poisson_flux=False,
                                       rng=rng.duplicate())
    print('obj.flux = ', obj.flux)
    print('added_flux = ', added_flux)
    print('photon fluxes = ', photons.flux.min(), '..', photons.flux.max())
    print('image flux = ', im.array.sum())
    assert np.isclose(added_flux, obj.flux)
    assert np.isclose(im.array.sum(), obj.flux)
    photons2 = obj.makePhot(poisson_flux=False, rng=rng.duplicate())
    assert photons2 == photons, "SecondKick makePhot not equivalent to drawPhot"

    # Can treat the profile as a convolution of a delta function and put it in a photon_ops list.
    delta = galsim.DeltaFunction(flux=1.e4)
    psf = galsim.SecondKick(lam=500, r0=0.2, diam=4)
    photons3 = delta.makePhot(poisson_flux=False,
                              rng=rng.duplicate(),
                              photon_ops=[psf])
    np.testing.assert_allclose(photons3.x, photons.x)
    np.testing.assert_allclose(photons3.y, photons.y)
    np.testing.assert_allclose(photons3.flux, photons.flux)
Beispiel #4
0
    def star2gs(self, index, gsparams):

        # Convert from dict to actual GsParams object
        # TODO: Currently fails if gsparams isn't passed!
        if gsparams: gsp = galsim.GSParams(**gsparams)
        else: gsp = None

        # List of individual band GSObjects
        gsobjects = []

        # Iterate over all desired bands
        for band in self.bands:
            if self.data_version == 'y3v02':

                # Needed for all mag calculations
                gmag = self.catalog['g_Corr'][index]

                # Grab current band magnitude
                if band == 'g':
                    mag = gmag
                elif band == 'r':
                    mag = gmag - self.catalog['gr_Corr'][index]
                elif band == 'i':
                    mag = gmag - self.catalog['gr_Corr'][index] - self.catalog[
                        'ri_Corr'][index]
                elif band == 'z':
                    mag = gmag - self.catalog['gr_Corr'][index] - self.catalog['ri_Corr'][index] \
                               - self.catalog['iz_Corr'][index]
                else:
                    raise ValueError(
                        'Band {} is not an allowed band input '.format(band) +
                        'for data_version of {}!'.format(self.data_version))

                # Now convert to flux
                flux = np.power(10.0, 0.4 * (self.zeropoint - mag))

                # (star cats calibrated at zp=30)
                # NOTE: Will just use `scale_flux` parameter in galsim config for now
                # flux_factor = ...

                # Stars are treated as a delta function, to be convolved w/ set PSF
                star = galsim.DeltaFunction(flux)

            else:
                # Should already be checked by this point, but just to be safe:
                raise ValueError(
                    'There is no implementation for `star2gs` for data_version '
                    + 'of {}'.format(self.data_version))

            # Add galaxy in given band to list
            gsobjects.append(star)

        # NOTE: If multiple bands were passed, the fluxes are simply added together.
        gs_star = galsim.Add(gsobjects)

        return gs_star
Beispiel #5
0
    def draw_star(self):
        st_model = galsim.DeltaFunction(flux=1.)
        st_model = st_model.evaluateAtWavelength(
            self.bpass.effective_wavelength)
        # reassign correct flux
        starflux = 1.
        st_model = st_model.withFlux(starflux)
        st_model = galsim.Convolve(st_model, self.psf)

        return st_model
Beispiel #6
0
    def timing(self, flux, sensor=None):
        point = galsim.DeltaFunction(flux=flux)
        star = galsim.Convolve(point*self.sed, self.psf._getPSF())

        image = galsim.Image(self.nxy, self.nxy)
        surface_ops = self.surface_ops if sensor is not None else ()
        t0 = time.clock()
        image = star.drawImage(method='phot', bandpass=self.bandpass,
                               image=image, scale=self.pixel_scale,
                               rng=self._rng, sensor=sensor,
                               surface_ops=surface_ops)
        return time.clock() - t0
Beispiel #7
0
def test_deltaFunction_flux_scaling():
    """Test flux scaling for Delta function.
    """
    # decimal point to go to for parameter value comparisons
    param_decimal = 12
    test_flux = 17.9

    # init with flux only (should be ok given last tests)
    obj = galsim.DeltaFunction(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.DeltaFunction(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.DeltaFunction(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.DeltaFunction(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.DeltaFunction(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).")

    obj = galsim.DeltaFunction(flux=test_flux)
    obj2 = obj.withFlux(test_flux*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 obj.withFlux(flux).")
    # Then test new obj2 flux
    np.testing.assert_almost_equal(
        obj2.flux, test_flux * 2., decimal=param_decimal,
        err_msg="Flux param inconsistent after obj.withFlux(flux).")
def test_deltaFunction_properties():
    """Test some basic properties of the Delta function profile
    """
    test_flux = 17.9
    delta = galsim.DeltaFunction(flux=test_flux)
    # Check that we are centered on (0, 0)
    cen = galsim.PositionD(0, 0)
    np.testing.assert_equal(delta.centroid, cen)
    offcen = galsim.PositionD(1, 1)
    # Check Fourier properties
    np.testing.assert_equal(delta.kValue(cen), (1 + 0j) * test_flux)
    np.testing.assert_equal(delta.kValue(offcen), (1 + 0j) * test_flux)
    import math
    assert delta.xValue(cen) > 1.e10
    np.testing.assert_almost_equal(delta.xValue(offcen), 0)
    assert delta.maxk > 1.e10
    assert delta.stepk > 1.e10
    # Check input flux vs output flux
    for inFlux in np.logspace(-2, 2, 10):
        delta = galsim.DeltaFunction(flux=inFlux)
        outFlux = delta.flux
        np.testing.assert_almost_equal(outFlux, inFlux)
Beispiel #9
0
def make_a_star(ud, wcs, affine, optics, sbparams):
    """
    makes a star-like object for injection into larger image.
    """
    
    # Choose a random RA, Dec around the sky_center.
    dec = sbparams.center_dec + (ud()-0.5) * sbparams.image_ysize_arcsec * galsim.arcsec
    ra = sbparams.center_ra + (ud()-0.5) * sbparams.image_xsize_arcsec / numpy.cos(dec) * galsim.arcsec
    world_pos = galsim.CelestialCoord(ra,dec)
    
    # We will need the image position as well, so use the wcs to get that
    image_pos = wcs.toImage(world_pos)
    
    # We also need this in the tangent plane, which we call "world coordinates" here,
    # This is still an x/y corrdinate 
    uv_pos = affine.toWorld(image_pos)

    # Draw star flux at random; based on distribution of star fluxes in real images  
    #flux_dist = galsim.DistDeviate(ud, function = lambda x:x**-1.5, x_min = 799.2114, x_max = 890493.9)
    flux_dist = galsim.DistDeviate(ud, function = lambda x:x**-1.5, x_min = 533, x_max = 59362)
    star_flux = flux_dist()
    
    # Generate PSF at location of star, convolve with optical model to make a star
    deltastar = galsim.DeltaFunction(flux=star_flux)  
    jitter_psf = galsim.Gaussian(flux=1,fwhm=0.05)
    star=galsim.Convolve([jitter_psf,deltastar,optics])

    star_stamp = star.drawImage(wcs=wcs.local(image_pos)) # before it was scale = 0.206, and that was bad!
    star_stamp.setCenter(image_pos.x,image_pos.y)
    
    star_truth=truth()
    star_truth.ra = ra.deg; star_truth.dec = dec.deg
    star_truth.x = image_pos.x; star_truth.y =image_pos.y

    try:
        star_truth.fwhm=star.calculateFWHM()
    except galsim.errors.GalSimError:
        logger.debug('fwhm calculation failed')
        star_truth.fwhm=-9999.0

    try:
        star_truth.mom_size=star_stamp.FindAdaptiveMom().moments_sigma
    except galsim.errors.GalSimError:
        logger.debug('sigma calculation failed')
        star_truth.mom_size=-9999.

    return star_stamp, star_truth
Beispiel #10
0
def test_airy_shoot():
    """Test Airy with photon shooting.  Particularly the flux of the final image.
    """
    # Airy patterns have *very* extended wings, so make this really small and the image really
    # big to make sure we capture all the photons.
    rng = galsim.BaseDeviate(1234)
    obj = galsim.Airy(lam_over_diam=0.01, flux=1.e4)
    im = galsim.Image(1000, 1000, scale=1)
    im.setCenter(0, 0)
    added_flux, photons = obj.drawPhot(im,
                                       poisson_flux=False,
                                       rng=rng.duplicate())
    print('obj.flux = ', obj.flux)
    print('added_flux = ', added_flux)
    print('photon fluxes = ', photons.flux.min(), '..', photons.flux.max())
    print('image flux = ', im.array.sum())
    assert np.isclose(added_flux, obj.flux)
    assert np.isclose(im.array.sum(), obj.flux)
    photons2 = obj.makePhot(poisson_flux=False, rng=rng)
    assert photons2 == photons, "Airy makePhot not equivalent to drawPhot"

    obj = galsim.Airy(lam_over_diam=0.01, flux=1.e4, obscuration=0.4)
    added_flux, photons = obj.drawPhot(im,
                                       poisson_flux=False,
                                       rng=rng.duplicate())
    print('obj.flux = ', obj.flux)
    print('added_flux = ', added_flux)
    print('photon fluxes = ', photons.flux.min(), '..', photons.flux.max())
    print('image flux = ', im.array.sum())
    assert np.isclose(added_flux, obj.flux)
    assert np.isclose(im.array.sum(), obj.flux)
    photons2 = obj.makePhot(poisson_flux=False, rng=rng.duplicate())
    assert photons2 == photons, "Airy makePhot not equivalent to drawPhot"

    # Can treat the profile as a convolution of a delta function and put it in a photon_ops list.
    delta = galsim.DeltaFunction(flux=1.e4)
    psf = galsim.Airy(lam_over_diam=0.01, obscuration=0.4)
    photons3 = delta.makePhot(poisson_flux=False,
                              rng=rng.duplicate(),
                              photon_ops=[psf])
    assert photons3 == photons, "Using Airy in photon_ops not equivalent to drawPhot"
Beispiel #11
0
def test_moffat_shoot():
    """Test Moffat with photon shooting.  Particularly the flux of the final image.
    """
    rng = galsim.BaseDeviate(1234)
    obj = galsim.Moffat(fwhm=3.5, beta=4.7, flux=1.e4)
    im = galsim.Image(500, 500, scale=1)
    im.setCenter(0, 0)
    added_flux, photons = obj.drawPhot(im,
                                       poisson_flux=False,
                                       rng=rng.duplicate())
    print('obj.flux = ', obj.flux)
    print('added_flux = ', added_flux)
    print('photon fluxes = ', photons.flux.min(), '..', photons.flux.max())
    print('image flux = ', im.array.sum())
    assert np.isclose(added_flux, obj.flux)
    assert np.isclose(im.array.sum(), obj.flux)
    photons2 = obj.makePhot(poisson_flux=False, rng=rng)
    assert photons2 == photons, "Moffat makePhot not equivalent to drawPhot"

    # Note: low beta has large wings, so don't go too low.  Also, reduce fwhm a bit.
    obj = galsim.Moffat(fwhm=1.5, beta=1.9, flux=1.e4)
    added_flux, photons = obj.drawPhot(im,
                                       poisson_flux=False,
                                       rng=rng.duplicate())
    print('obj.flux = ', obj.flux)
    print('added_flux = ', added_flux)
    print('photon fluxes = ', photons.flux.min(), '..', photons.flux.max())
    print('image flux = ', im.array.sum())
    assert np.isclose(added_flux, obj.flux)
    assert np.isclose(im.array.sum(), obj.flux)
    photons2 = obj.makePhot(poisson_flux=False, rng=rng.duplicate())
    assert photons2 == photons, "Moffat makePhot not equivalent to drawPhot"

    # Can treat the profile as a convolution of a delta function and put it in a photon_ops list.
    delta = galsim.DeltaFunction(flux=1.e4)
    psf = galsim.Moffat(fwhm=1.5, beta=1.9)
    photons3 = delta.makePhot(poisson_flux=False,
                              rng=rng.duplicate(),
                              photon_ops=[psf])
    assert photons3 == photons, "Using Moffat in photon_ops not equivalent to drawPhot"
Beispiel #12
0
    def applyPSF(self, xPupil=None, yPupil=None, obj=None, **kwargs):
        """
        Apply the PSF to a GalSim GSObject

        This method accepts the x and y pupil coordinates in arc seconds as well
        as a GalSim GSObject.  The method calculates the PSF parameters based on xPupil
        and yPupil, constructs a Galsim GSObject corresponding to the PSF function, and convolves
        the PSF with the GSObject, returning the result of the convolution.

        In the case of point sources, this object returns the raw PSF, rather than attempting
        a convolution (since there is nothing to convolve with).

        @param [in] xPupil the x pupil coordinate in arc seconds

        @param [in] yPupil the y pupil coordinate in arc seconds

        @param [in] obj is a GalSim GSObject (an astronomical object) with which
        to convolve the PSF (optional)
        """

        #use the user-defined _getPSF method to calculate the PSF at these specific
        #coordinates and (optionally) wavelength
        psf = self._getPSF(xPupil=xPupil, yPupil=yPupil, **kwargs)

        if obj is None:
            #if there is no object, use a DeltaFunction as a point source
            obj = galsim.DeltaFunction()

        #convolve obj with the psf
        if isinstance(psf, galsim.Convolution):
            # If the psf is itself a Convolution object, convolve obj
            # with the individual components to ensure that the
            # obj_list of the returned obj lists those components
            # separately.
            return galsim.Convolution([obj] + psf.obj_list)
        else:
            return galsim.Convolve(obj, psf)
Beispiel #13
0
def make_a_star(ud, wcs, affine, optics, sbparams):
    """
    makes a star-like object for injection into larger image.
    """

    # Choose a random RA, Dec around the sky_center.
    dec = sbparams.center_dec + (
        ud() - 0.5) * sbparams.image_ysize_arcsec * galsim.arcsec
    ra = sbparams.center_ra + (ud(
    ) - 0.5) * sbparams.image_xsize_arcsec / numpy.cos(dec) * galsim.arcsec
    world_pos = galsim.CelestialCoord(ra, dec)

    # We will need the image position as well, so use the wcs to get that
    image_pos = wcs.toImage(world_pos)

    # We also need this in the tangent plane, which we call "world coordinates" here,
    # This is still an x/y corrdinate
    uv_pos = affine.toWorld(image_pos)

    # Draw star flux at random; based on distribution of star fluxes in real images
    flux_dist = galsim.DistDeviate(ud,
                                   function=lambda x: x**-1.5,
                                   x_min=799.2114,
                                   x_max=890493.9)
    #star_flux = 1E4
    star_flux = flux_dist()

    # Generate PSF at location of star, convolve with optical model to make a star
    deltastar = galsim.DeltaFunction(flux=star_flux)
    #this_psf = psf.getPSF(image_pos)
    #star=galsim.Convolve([optics, this_psf,deltastar])
    gauss_psf = galsim.Gaussian(flux=1, fwhm=0.5)
    star = galsim.Convolve([gauss_psf, deltastar])

    # Account for the fractional part of the position
    # cf. demo9.py for an explanation of this nominal position stuff.
    x_nominal = image_pos.x + 0.5
    y_nominal = image_pos.y + 0.5
    ix_nominal = int(math.floor(x_nominal + 0.5))
    iy_nominal = int(math.floor(y_nominal + 0.5))
    dx = x_nominal - ix_nominal
    dy = y_nominal - iy_nominal
    offset = galsim.PositionD(dx, dy)
    #star_stamp = star.drawImage(wcs=wcs.local(image_pos), offset=offset, method='no_pixel')
    #star_stamp.setCenter(ix_nominal,iy_nominal)
    star_stamp = star.drawImage(wcs=wcs.local(image_pos))
    star_stamp.setCenter(image_pos.x, image_pos.y)

    star_truth = truth()
    star_truth.ra = ra.deg
    star_truth.dec = dec.deg
    star_truth.x = image_pos.x
    star_truth.y = image_pos.y

    try:
        star_truth.fwhm = star.calculateFWHM()
    except galsim.errors.GalSimError:
        logger.debug('fwhm calculation failed')
        star_truth.fwhm = -9999.0

    try:
        star_truth.mom_size = star_stamp.FindAdaptiveMom().moments_sigma
    except galsim.errors.GalSimError:
        logger.debug('sigma calculation failed')
        star_truth.mom_size = -9999.

    return star_stamp, star_truth
Beispiel #14
0
def test_geometric_shoot():
    """Test that geometric photon shooting is reasonably consistent with Fourier optics."""
    jmax = 20
    bd = galsim.BaseDeviate(1111111)
    u = galsim.UniformDeviate(bd)

    lam = 500.0
    diam = 4.0

    for i in range(4):  # Do a few random tests.  Takes about 1 sec.
        aberrations = [0]+[u()*0.1 for i in range(jmax)]
        opt_psf = galsim.OpticalPSF(diam=diam, lam=lam, aberrations=aberrations,
                                    geometric_shooting=True)
        # Use really good seeing, so that the optics contribution actually matters.
        atm_psf = galsim.Kolmogorov(fwhm=0.4)

        psf = galsim.Convolve(opt_psf, atm_psf)
        u1 = u.duplicate()
        im_shoot = psf.drawImage(nx=256, ny=256, scale=0.2, method='phot', n_photons=100000, rng=u)
        im_fft = psf.drawImage(nx=256, ny=256, scale=0.2)

        printval(im_fft, im_shoot)
        shoot_moments = galsim.hsm.FindAdaptiveMom(im_shoot)
        fft_moments = galsim.hsm.FindAdaptiveMom(im_fft)

        # 40th of a pixel centroid tolerance.
        np.testing.assert_allclose(
            shoot_moments.moments_centroid.x, fft_moments.moments_centroid.x, rtol=0, atol=0.025,
            err_msg="")
        np.testing.assert_allclose(
            shoot_moments.moments_centroid.y, fft_moments.moments_centroid.y, rtol=0, atol=0.025,
            err_msg="")
        # 2% size tolerance
        np.testing.assert_allclose(
            shoot_moments.moments_sigma, fft_moments.moments_sigma, rtol=0.02, atol=0,
            err_msg="")
        # Not amazing ellipticity consistency at the moment.  0.01 tolerance.
        print(fft_moments.observed_shape)
        print(shoot_moments.observed_shape)
        np.testing.assert_allclose(
            shoot_moments.observed_shape.g1, fft_moments.observed_shape.g1, rtol=0, atol=0.01,
            err_msg="")
        np.testing.assert_allclose(
            shoot_moments.observed_shape.g2, fft_moments.observed_shape.g2, rtol=0, atol=0.01,
            err_msg="")

        # Check the flux
        # The Airy part sends a lot of flux off the edge, so this test is a little loose.
        added_flux = im_shoot.added_flux
        print('psf.flux = ',psf.flux)
        print('added_flux = ',added_flux)
        print('image flux = ',im_shoot.array.sum())
        assert np.isclose(added_flux, psf.flux, rtol=3.e-4)
        assert np.isclose(im_shoot.array.sum(), psf.flux, rtol=3.e-4)

        # Check doing this with photon_ops
        im_shoot2 = opt_psf.drawImage(nx=256, ny=256, scale=0.2, method='phot',
                                      n_photons=100000, rng=u1.duplicate(),
                                      photon_ops=[atm_psf])
        np.testing.assert_allclose(im_shoot2.array, im_shoot.array)
        im_shoot3 = galsim.DeltaFunction().drawImage(nx=256, ny=256, scale=0.2, method='phot',
                                                     n_photons=100000, rng=u1.duplicate(),
                                                     photon_ops=[opt_psf, atm_psf])
        np.testing.assert_allclose(im_shoot3.array, im_shoot.array)
Beispiel #15
0
def main(argv):
    # Where to find and output data.
    path, filename = os.path.split(__file__)
    outpath = os.path.abspath(os.path.join(path, "output/"))

    # Just use a few galaxies, to save time.  Note that we are going to put 4000 galaxy images into
    # our big image, so if we have n_use=10, each galaxy will appear 400 times.  Users who want a
    # more interesting image with greater variation in the galaxy population can change `n_use` to
    # something larger (but it should be <=100, the number of galaxies in this small example
    # catalog).  With 4000 galaxies in a 4k x 4k image with the WFIRST pixel scale, the effective
    # galaxy number density is 74/arcmin^2.  This is not the number density that is expected for a
    # sample that is so bright (I<23.5) but it makes the image more visually interesting.  One could
    # think of it as what you'd get if you added up several images at once, making the images for a
    # sample that is much deeper have the same S/N as that for an I<23.5 sample in a single image.
    n_use = 10
    n_tot = 4000

    # Default is to use all filters.  Specify e.g. 'YJH' to only do Y106, J129, and H158.
    use_filters = 'H158'

    # quick and dirty command line parsing.
    for var in argv:
        if var.startswith('data='): datapath = var[5:]
        if var.startswith('out='): outpath = var[4:]
        if var.startswith('nuse='): n_use = int(var[5:])
        if var.startswith('ntot='): n_tot = int(var[5:])
        if var.startswith('filters='): use_filters = var[8:].upper()

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

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

    # Initialize (pseudo-)random number generator.
    random_seed = 123456
    rng = galsim.BaseDeviate(random_seed)

    # Generate a Poisson noise model.
    poisson_noise = galsim.PoissonNoise(rng)
    logger.info('Poisson noise model created.')

    # Read in the WFIRST filters, setting an AB zeropoint appropriate for this telescope given its
    # diameter and (since we didn't use any keyword arguments to modify this) using the typical
    # exposure time for WFIRST images.  By default, this routine truncates the parts of the
    # bandpasses that are near 0 at the edges, and thins them by the default amount.
    filters = wfirst.getBandpasses(AB_zeropoint=True)
    logger.debug('Read in WFIRST imaging filters.')

    # Here we carry out the initial steps that are necessary to get a fully chromatic PSF.  We use
    # the getPSF() routine in the WFIRST module, which knows all about the telescope parameters
    # (diameter, bandpasses, obscuration, etc.).  Note that we arbitrarily choose a single SCA
    # (Sensor Chip Assembly) rather than all of them, for faster calculations, and use a simple
    # representation of the struts for faster calculations.  To do a more exact calculation of the
    # chromaticity and pupil plane configuration, remove the `approximate_struts` and the `n_waves`
    # keyword from the call to getPSF():

    PA = [20, 50]
    for i in range(2):
        use_SCA = 7  # This could be any number from 1...18
        logger.info('Doing expensive pre-computation of PSF.')
        t1 = time.time()
        logger.setLevel(logging.DEBUG)

        # We choose a particular (RA, dec) location on the sky for our observation.
        ra_targ = 90. * galsim.degrees
        dec_targ = -10. * galsim.degrees
        targ_pos = galsim.CelestialCoord(ra=ra_targ, dec=dec_targ)
        # Get the WCS for an observation at this position.  We are not supplying a date, so the routine
        # will assume it's the vernal equinox.  We are also not supplying a position angle for the
        # observatory, which means that it will just find the optimal one (the one that has the solar
        # panels pointed most directly towards the Sun given this targ_pos and date).  The output of
        # this routine is a dict of WCS objects, one for each SCA.  We then take the WCS for the SCA
        # that we are using.
        wcs_dict = wfirst.getWCS(world_pos=targ_pos,
                                 PA=PA[i] * galsim.radians,
                                 SCAs=use_SCA)
        wcs = wcs_dict[use_SCA]
        # We need to find the center position for this SCA.  We'll tell it to give us a CelestialCoord
        # corresponding to (X, Y) = (wfirst.n_pix/2, wfirst.n_pix/2).
        SCA_cent_pos = wcs.toWorld(
            galsim.PositionD(wfirst.n_pix / 2, wfirst.n_pix / 2))

        # Need to make a separate PSF for each filter.  We are, however, ignoring the
        # position-dependence of the PSF within each SCA, just using the PSF at the center of the SCA
        # (default kwargs).
        PSFs = {}
        for filter_name, filter_ in filters.items():
            logger.info('PSF pre-computation for SCA %d, filter %s.' %
                        (use_SCA, filter_name))
            PSFs[filter_name] = galsim.roman.getPSF(use_SCA,
                                                    filter_name,
                                                    n_waves=10,
                                                    wcs=wcs,
                                                    pupil_bin=4)
        t2 = time.time()
        logger.info('Done PSF precomputation in %.1f seconds!' % (t2 - t1))

        # Define the size of the postage stamp that we use for each individual galaxy within the larger
        # image, and for the PSF images.
        stamp_size = 256

        # We randomly distribute points in (X, Y) on the CCD.
        # If we had a real galaxy catalog with positions in terms of RA, dec we could use wcs.toImage()
        # to find where those objects should be in terms of (X, Y).
        pos_rng = galsim.UniformDeviate(random_seed)

        # Calculate the sky level for each filter, and draw the PSF and the galaxies through the
        # filters.
        for filter_name, filter_ in filters.items():
            if use_filters is not None and filter_name[0] not in use_filters:
                logger.info('Skipping filter {0}.'.format(filter_name))
                continue

            logger.info('Beginning work for {0}.'.format(filter_name))

            # Drawing PSF.  Note that the PSF object intrinsically has a flat SED, so if we convolve it
            # with a galaxy, it will properly take on the SED of the galaxy.  For the sake of this demo,
            # we will simply convolve with a 'star' that has a flat SED and unit flux in this band, so
            # that the PSF image will be normalized to unit flux. This does mean that the PSF image
            # being drawn here is not quite the right PSF for the galaxy.  Indeed, the PSF for the
            # galaxy effectively varies within it, since it differs for the bulge and the disk.  To make
            # a real image, one would have to choose SEDs for stars and convolve with a star that has a
            # reasonable SED, but we just draw with a flat SED for this demo.
            out_filename = os.path.join(outpath,
                                        'demo13_PSF_' + str(i) + '.fits')
            # Generate a point source.
            point = galsim.DeltaFunction(flux=1.)
            # Use a flat SED here, but could use something else.  A stellar SED for instance.
            # Or a typical galaxy SED.  Depending on your purpose for drawing the PSF.
            star_sed = galsim.SED(lambda x: 1, 'nm', 'flambda').withFlux(
                1., filter_)  # Give it unit flux in this filter.
            star = galsim.Convolve(point * star_sed, PSFs[filter_name])
            img_psf = galsim.ImageF(64, 64)
            star.drawImage(bandpass=filter_,
                           image=img_psf,
                           scale=wfirst.pixel_scale)
            img_psf.write(out_filename)
            logger.debug(
                'Created PSF with flat SED for {0}-band'.format(filter_name))

            # Set up the full image that will contain all the individual galaxy images, with information
            # about WCS:
            final_image = galsim.ImageF(wfirst.n_pix, wfirst.n_pix, wcs=wcs)
Beispiel #16
0
print('flux = ', flux)
image = galsim.Image(galsim.roman.n_pix, galsim.roman.n_pix, wcs=WCS)
xyI = image.center
sky_level = galsim.roman.getSkyLevel(bandpass, world_pos=WCS.toWorld(xyI))

for high, app in [(False, True), (False, False), (True, True), (True, False)]:
    PSF = galsim.roman.getPSF(
        1,
        'H158',
        SCA_pos=None,
        approximate_struts=app,
        wavelength=bandpass.effective_wavelength,
        high_accuracy=high,
    )

    star = galsim.DeltaFunction() * flux
    star = galsim.Convolve(star, PSF)

    t0 = time.time()
    star_stamp = star.drawImage(wcs=WCS.local(xyI), center=xyI)
    t1 = time.time()

    image.fill(sky_level)
    #Doing the sky right means doing the next line. But that's slow, and not relevant to this test.
    #WCS.makeSkyImage(image, sky_level)

    b = image.bounds & star_stamp.bounds
    image[b] += star_stamp[b]
    image += galsim.roman.thermal_backgrounds['H158'] * galsim.roman.exptime
    image.addNoise(galsim.PoissonNoise(rng))
    image -= np.median(image.array)
Beispiel #17
0
def main(spherex_filter_name, output_name, params):
    '''
    Main script to run the SPHEREx GalSim simulation.
    USAGE: main(spherex_filter_name , image_output_filename , params)
    where
        - spherex_filter_name: SPHEREx filter name as in the SPHEREx filter file (e.g., spherex_lvf1_m1)
        - image_output_filename: file name of FITS file (note that if the file name is the same, new images are added to the HDU list)
        - params: a dictionary with parameters to set up the simulation (see below)

    OUTPUT: Outputs a FITS file with the simulated image in the HDU list with the name of the SPHEREx filter. It also creates
            "truth" catalogs in the flux units of the image and magnitude. They are saved as [NAME]_[SPHEREx-FILTER].csv
    '''

    ## DEFINITIONS ########
    # Define some parameters here
    pixel_scale = params["pixel_scale"]  # arcsec/pixel
    image_size_arcsec = params[
        "image_size_arcmin"] * 60  # image size in arcseconds
    noise_sigma = params[
        "noise_sigma"]  # ADU  (Just use simple Gaussian noise here. Variance would be the square)
    obj_density_per_arcminsq = params[
        "obj_density_per_arcminsq"]  # number density of galaxies per arcmin squared
    star_density_per_arcminsq = params[
        "star_density_per_arcminsq"]  # number density of stars [stars/arcmins]
    star_mags_range = params[
        "star_mags_range"]  # magnitude limits for stars [bright , faint] in AB mags
    center_ra = params[
        "center_ra"] * galsim.hours  # The RA of the center of the image on the sky
    center_dec = params[
        "center_dec"] * galsim.degrees  # The Dec of the center of the image on the sky
    random_seed = params["random_seed"]  # random seed
    in_grid = params[
        "in_grid"]  # If True, align galaxies/stars in grid. CURRENTLY ONLY WORKS FOR EITHER STARS OR GALAXIES!
    grid_margin = params["grid_margin"]  # grid margin in %
    psf_fwhm = params["psf_fwhm"]  # PSF FWHM (gaussian) in arcseconds
    psf_file_name = params[
        "psf_file_name"]  # PSF file name. This is used if not "none"
    image_output_filename = os.path.join(params["output_path"], output_name)
    #####################

    ## Set up Logger
    #logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout)
    logging.basicConfig(format="%(message)s",
                        level=logging.INFO,
                        filename=image_output_filename.replace(
                            ".fits", ".log"))
    logger = logging.getLogger("SPHEREx_sim for filter %s" %
                               spherex_filter_name)
    logger.info("SPHEREx_sim for filter %s" % spherex_filter_name)

    ## LOAD EXTERNAL CATALOGS ###

    ## Load the central wavelength of the SPHEREx filters
    # name - name of the filter
    # lam - wavelength in Angstroms
    spherex_filters = ascii.read(params["spherex_filter_file"],
                                 names=["name", "lam"])
    print("%g filters found in the filter file" % len(spherex_filters))

    ## Load SPHEREx SED catalog matched to GalSim shape catalog
    # col10 - col109 are the 100 SPHEREx filters
    sed_catalog = Table.read(params["spherex_sed_file"])
    print("Number of galaxies in the SED catalog: %g" % len(sed_catalog))

    ###########################

    # size of image in pixels
    image_size = int(image_size_arcsec / pixel_scale)

    # Number of objects
    nobj_tot = int(obj_density_per_arcminsq * image_size_arcsec**2 / 3600)
    logger.info("Simulating %g galaxies" % (nobj_tot))
    print("Simulating %g galaxies" % (nobj_tot))
    nstar_tot = int(star_density_per_arcminsq * image_size_arcsec**2 / 3600)
    logger.info("Simulating %g stars" % (nstar_tot))
    print("Simulating %g stars" % (nstar_tot))

    # If stars/galaxies should be arranged in a grid, we need to create the
    # grid points. Note that if sqrt(number objects) is not an integer, we have
    # to add some more grid points (which are then not used.) We assume a square here.
    if in_grid:

        # adjust numbers to make grid points (assume square)
        nobj_tot_generate = np.ceil(
            np.sqrt(nobj_tot)
        )**2  # round up to the next square. This number is used to generate points.
        print(
            "Number of grid points to generate for Galaxies: %g (nbr of Galaxies: %g)"
            % (nobj_tot_generate, nobj_tot))
        logger.info(
            "Number of grid points to generate for Galaxies: %g (nbr of Galaxies: %g)"
            % (nobj_tot_generate, nobj_tot))

        nstar_tot_generate = np.ceil(
            np.sqrt(nstar_tot)
        )**2  # round up to the next square. This number is used to generate points.
        print(
            "Number of grid points to generate for Stars: %g (nbr of Stars: %g)"
            % (nstar_tot_generate, nstar_tot))
        logger.info(
            "Number of grid points to generate for Stars: %g (nbr of Stars: %g)"
            % (nstar_tot_generate, nstar_tot))

        # now generate grid (these are relative to center as a fraction of total image width)
        if nobj_tot > 0:
            obj_ra_ids = np.linspace(start=grid_margin / 100,
                                     stop=(1 - grid_margin / 100),
                                     num=int(np.sqrt(nobj_tot_generate)))
            obj_dec_ids = np.linspace(start=grid_margin / 100,
                                      stop=(1 - grid_margin / 100),
                                      num=int(np.sqrt(nobj_tot_generate)))
            obj_radec_ids = [(rr, dd) for rr in obj_ra_ids
                             for dd in obj_dec_ids]
        if nstar_tot > 0:
            star_ra_ids = np.linspace(start=grid_margin / 100,
                                      stop=(1 - grid_margin / 100),
                                      num=int(np.sqrt(nstar_tot_generate)))
            star_dec_ids = np.linspace(start=grid_margin / 100,
                                       stop=(1 - grid_margin / 100),
                                       num=int(np.sqrt(nstar_tot_generate)))
            star_radec_ids = [(rr, dd) for rr in star_ra_ids
                              for dd in star_dec_ids]

    # set numpy random seed
    np.random.seed = random_seed

    logger.info('Starting')

    ## Read in galaxy catalog
    # The COSMOSCatalog contains stamps and parametric fits to the COSMOS galaxies. Here
    # down to a magnitude of 25.2AB. (Fill in rest with point sources for example)
    # New: here user can also load a selection of the catalog, cosmos_cat_name is a parameter now.
    cosmos_cat_name = params[
        "galsim_input_catalog_name"]  #'real_galaxy_catalog_25.2.fits'
    cosmos_cat_path = params["galsim_shape_directory"]
    cosmos_cat = galsim.COSMOSCatalog(cosmos_cat_name,
                                      dir=cosmos_cat_path,
                                      exclusion_level="marginal")
    logger.info('Read in %d galaxies from catalog', cosmos_cat.nobjects)

    ## Load PSF
    # Note that one can define the pixel scale of the PSF. Usually it's the same
    # as the image. Also, check if user provided PSF
    if psf_file_name == "none":
        psf = galsim.Gaussian(fwhm=psf_fwhm, flux=1)
        logger.info('Create Gaussian PSF')
    else:
        logger.info("Using user provided PSF in FITS form.")
        if os.path.exists(psf_file_name):
            psf = galsim.InterpolatedImage(psf_file_name,
                                           scale=pixel_scale,
                                           flux=1)
        else:
            logger.info("PSF file does not exist.")
            print("ERROR: PSF file does not exist! - abort")
            quit()

    ## Get correct SPHEREx filter band ID
    spherex_filter_id = np.where(
        spherex_filters["name"] == spherex_filter_name)[0]  # from 0 to 99
    if len(spherex_filter_id) == 0:
        logger.info("Filter %s was not found. Abort." % spherex_filter_name)
        quit()

    ## Flux conversion from HST counts to MJy/sr
    flux_scaling_zp = 10**(-0.4 * (25.94734 - 8.9)) / (
        pixel_scale**2) / 2.350443e-5  # from HST counts to MJy/sr

    ## Setup the image:
    full_image = galsim.ImageF(image_size, image_size)

    # Set the lower-left corner pixel
    full_image.setOrigin(1, 1)

    # Initiate the random number
    rng = galsim.BaseDeviate(random_seed)

    # We keep track of how much noise is already in the image from the RealGalaxies.
    # The default initial value is all pixels = 0.
    noise_image = galsim.ImageF(image_size, image_size)
    noise_image.setOrigin(1, 1)

    ## Set up the WCS system
    theta = 0.0 * galsim.degrees
    # ( dudx  dudy ) = ( cos(theta)  -sin(theta) ) * pixel_scale
    # ( dvdx  dvdy )   ( sin(theta)   cos(theta) )
    # Aside: You can call numpy trig functions on Angle objects directly, rather than getting
    #        their values in radians first.  Or, if you prefer, you can write things like
    #        theta.sin() or theta.cos(), which are equivalent.
    dudx = np.cos(theta) * pixel_scale
    dudy = -np.sin(theta) * pixel_scale
    dvdx = np.sin(theta) * pixel_scale
    dvdy = np.cos(theta) * pixel_scale
    affine = galsim.AffineTransform(dudx,
                                    dudy,
                                    dvdx,
                                    dvdy,
                                    origin=full_image.true_center)

    # We can also put it on the celestial sphere to give it a bit more realism.
    # The TAN projection takes a (u,v) coordinate system on a tangent plane and projects
    # that plane onto the sky using a given point as the tangent point.  The tangent
    # point should be given as a CelestialCoord.
    sky_center = galsim.CelestialCoord(ra=center_ra, dec=center_dec)

    # The third parameter, units, defaults to arcsec, but we make it explicit here.
    # It sets the angular units of the (u,v) intermediate coordinate system.
    wcs = galsim.TanWCS(affine, sky_center, units=galsim.arcsec)
    full_image.wcs = wcs

    # prepare truth catalog
    truth_catalog = Table(names=[
        "IDENT", "NUMBER", "ra", "dec", "fluxtot", "fluxtot_real", "magtot",
        "magtot_real", "theta", "flag_star"
    ],
                          dtype=[
                              np.int, np.int, np.float64, np.float64,
                              np.float64, np.float64, np.float64, np.float64,
                              np.float64, np.int
                          ])

    ## Now we need to loop over our GALAXIES -----------
    logger.info('Creating galaxies')
    time1 = time.time()
    gal_counter = 0
    if nobj_tot > 0:
        for k in range(nobj_tot):

            # The usual random number generator using a different seed for each galaxy.
            ud = galsim.UniformDeviate(random_seed + k + 1)

            # Choose a random RA, Dec around the sky_center.
            # Note that for this to come out close to a square shape, we need to account for the
            # cos(dec) part of the metric: ds^2 = dr^2 + r^2 d(dec)^2 + r^2 cos^2(dec) d(ra)^2
            # So need to calculate dec first.
            if not in_grid:
                dec = center_dec + (ud() -
                                    0.5) * image_size_arcsec * galsim.arcsec
                ra = center_ra + (ud() - 0.5) * image_size_arcsec / np.cos(
                    dec) * galsim.arcsec
            if in_grid:
                dec = center_dec + (obj_radec_ids[k][1] -
                                    0.5) * image_size_arcsec * galsim.arcsec
                ra = center_ra + (
                    obj_radec_ids[k][0] -
                    0.5) * image_size_arcsec / np.cos(dec) * galsim.arcsec

            world_pos = galsim.CelestialCoord(ra, dec)

            # We will need the image position as well, so use the wcs to get that
            image_pos = wcs.toImage(world_pos)

            # pick a galaxy from the COSMOS catalog
            gal = cosmos_cat.makeGalaxy(gal_type='parametric', rng=ud)
            gal_index = gal.index.copy(
            )  # this is the galaxy index linked to the cosmos_cat (created by GalSim)

            # NOTE: in the following, use gal.index and cosmos_cat.real_cat.KEY to access the parameters in the real catalog.
            #print( "Flux comparison (catalog / from mag / used): (%g , %g , %g)" % (cosmos_cat.real_cat.stamp_flux[gal_index] , 10**(-0.4*(cosmos_cat.real_cat.mag[gal_index]  - 25.94734)) , gal.flux) )

            # Apply a random rotation
            gal_theta = ud() * 2.0 * np.pi * galsim.radians
            gal = gal.rotate(gal_theta)

            # Rescale the flux of the galaxy ----
            # NOTE: we cannot assign a flux via gal.flux = A, but we can do gal *= B for a factor B.
            # Therefore we have to compute the flux relative to the ACS flux to get it in ACS counts.
            # After that, we have to apply the zeropoint correction.

            # 0) get the flux of this galaxy in this SPHEREx filter
            # SPHEREx filter ID was derived above outside of the FOR loop
            idx = np.where(sed_catalog["IDENT"] == int(
                cosmos_cat.real_cat.ident[gal_index]))[
                    0]  # row number of galaxy in SED catalog
            if len(idx
                   ) == 0:  # if a galaxy does not have an SED, just skip it.
                continue
            gal_SPHEREX_fnu = float(
                sed_catalog["col%g" % (10 + int(spherex_filter_id))][idx])

            # 1) scale the flux according to current filter.
            gal_ACS_counts_new = gal_SPHEREX_fnu * 10**(
                -0.4 * (-48.6 - 25.94734)
            )  # converts the SPHEREx f_nu [erg/s/cm2/A] to the ACS counts.
            flux_scaling_sed = gal_ACS_counts_new / gal.flux  # flux scaling for SED

            # 2) flux scaling to change zero point from counts to MJy/sr
            # did that above outside the for loop

            # 3) apply the flux scaling
            gal *= flux_scaling_sed  # scale flux to reflect SED
            gal *= flux_scaling_zp  # scale flux to reflect SPHEREx zeropoint (flux is not in SPHEREx flux units)

            # -------

            # Convolve with the PSF.
            final = galsim.Convolve(psf, gal)

            # Account for the fractional part of the position
            # cf. demo9.py for an explanation of this nominal position stuff.
            x_nominal = image_pos.x + 0.5
            y_nominal = image_pos.y + 0.5
            ix_nominal = int(math.floor(x_nominal + 0.5))
            iy_nominal = int(math.floor(y_nominal + 0.5))
            dx = x_nominal - ix_nominal
            dy = y_nominal - iy_nominal
            offset = galsim.PositionD(dx, dy)

            # We use method='no_pixel' here because the PSF image that we are using includes the
            # pixel response already.
            stamp = final.drawImage(wcs=wcs.local(image_pos),
                                    offset=offset,
                                    method='no_pixel')

            # note that when PSF FWHM <~ pixsize then the input flux is not correct!
            # Here we measure the total flux again on the PSF-convolved but noiseless stamp.
            flux_real = np.nansum(stamp.array)

            # Recenter the stamp at the desired position:
            stamp.setCenter(ix_nominal, iy_nominal)

            # Find the overlapping bounds:
            bounds = stamp.bounds & full_image.bounds

            # Finally, add the stamp to the full image.
            full_image[bounds] += stamp[bounds]

            # update truth catalog
            truth_catalog.add_row([
                int(cosmos_cat.real_cat.ident[gal_index]),
                int(sed_catalog["NUMBER"][idx]), ra.deg, dec.deg, gal.flux,
                flux_real,
                -2.5 * np.log10(gal.flux / flux_scaling_zp) + 25.94734,
                -2.5 * np.log10(flux_real / flux_scaling_zp) + 25.94734,
                float(gal_theta.deg), 0
            ])

            gal_counter += 1
        ## ENDFOR for adding galaxies

    time2 = time.time()
    tot_time = time2 - time1
    logger.info('Galaxies created in t=%f s' % tot_time)
    logger.info('%g Galaxies created in the end' % gal_counter)
    print('%g Galaxies created in the end' % gal_counter)
    ## END for GALAXIES --------------

    ## Now we need to loop over our STARS/POINT SOURCES -----------
    logger.info('Creating stars/point sources')

    # get star magnitudes
    star_mags = np.random.uniform(low=star_mags_range[0],
                                  high=star_mags_range[1],
                                  size=nstar_tot)
    time1 = time.time()
    for p in range(nstar_tot):

        # The usual random number generator using a different seed for each star.
        # note here that we want to add nobj_tot, else we end up with the same positions again!
        ud = galsim.UniformDeviate(random_seed + p + 1 + nobj_tot)

        # Choose a random RA, Dec around the sky_center.
        # Note that for this to come out close to a square shape, we need to account for the
        # cos(dec) part of the metric: ds^2 = dr^2 + r^2 d(dec)^2 + r^2 cos^2(dec) d(ra)^2
        # So need to calculate dec first.
        if not in_grid:
            dec = center_dec + (ud() - 0.5) * image_size_arcsec * galsim.arcsec
            ra = center_ra + (
                ud() - 0.5) * image_size_arcsec / np.cos(dec) * galsim.arcsec
        if in_grid:
            dec = center_dec + (star_radec_ids[k][1] -
                                0.5) * image_size_arcsec * galsim.arcsec
            ra = center_ra + (
                star_radec_ids[k][0] -
                0.5) * image_size_arcsec / np.cos(dec) * galsim.arcsec

        world_pos = galsim.CelestialCoord(ra, dec)

        # We will need the image position as well, so use the wcs to get that
        image_pos = wcs.toImage(world_pos)

        # Create star (which is a Delta Function with total flux of 1)
        star = galsim.DeltaFunction(flux=1.)

        # Get flux of point source
        star_acs_counts = 10**(-0.4 * (star_mags[p] - 25.94734))
        star *= star_acs_counts
        star *= flux_scaling_zp

        # Convolve with the PSF.
        final = galsim.Convolve(psf, star)

        # Account for the fractional part of the position
        # cf. demo9.py for an explanation of this nominal position stuff.
        x_nominal = image_pos.x + 0.5
        y_nominal = image_pos.y + 0.5
        ix_nominal = int(math.floor(x_nominal + 0.5))
        iy_nominal = int(math.floor(y_nominal + 0.5))
        dx = x_nominal - ix_nominal
        dy = y_nominal - iy_nominal
        offset = galsim.PositionD(dx, dy)

        # We use method='no_pixel' here because the PSF image that we are using includes the
        # pixel response already.
        stamp = final.drawImage(wcs=wcs.local(image_pos),
                                offset=offset,
                                method='no_pixel')

        # note that when PSF FWHM <~ pixsize then the input flux is not correct!
        # Here we measure the total flux again on the PSF-convolved but noiseless stamp.
        flux_real = np.nansum(stamp.array)

        # Recenter the stamp at the desired position:
        stamp.setCenter(ix_nominal, iy_nominal)

        # Find the overlapping bounds:
        bounds = stamp.bounds & full_image.bounds

        # Finally, add the stamp to the full image.
        full_image[bounds] += stamp[bounds]

        # update truth catalog
        truth_catalog.add_row([
            int(p),
            int(-99), ra.deg, dec.deg, star.flux, flux_real,
            -2.5 * np.log10(star.flux / flux_scaling_zp) + 25.94734,
            -2.5 * np.log10(flux_real / flux_scaling_zp) + 25.94734, 0.0, 1
        ])
    ## ENDFOR for adding stars

    time2 = time.time()
    tot_time = time2 - time1
    logger.info('Stars created in t=%f s' % tot_time)
    ## END for STARS --------------

    ## Now add Gaussian noise with this variance to the final image.  We have to do this step
    # at the end, rather than adding to individual postage stamps, in order to get the noise
    # level right in the overlap regions between postage stamps.

    ## If we want to add correlated noise of an image, we can compute this here.
    # For this we would need a noise image. For example a residual image or something
    # similar. For now, we skip correlated noise and just use plain gaussian noise.
    #cn = galsim.CorrelatedNoise(image, rng=rng)
    #full_image.addNoise(cn)
    noise = galsim.GaussianNoise(rng, sigma=noise_sigma)
    full_image.addNoise(noise)
    logger.info('Added noise to final large image')

    ## Write image to disk. If an image is already available, add this image to the HDU.
    # Else create a new image first.
    if os.path.exists(
            image_output_filename
    ):  # load existing image if exists, else create a new image
        with fits.open(image_output_filename) as hdul:
            galsim.fits.write(full_image,
                              hdu_list=hdul)  # add to existing HDUL
            hdul[-1].header[
                "EXTNAME"] = spherex_filter_name  # add extension name
            hdul[-1].header["LAMBDA"] = str(
                float(spherex_filters["lam"]
                      [spherex_filter_id]))  # add filter lambda
            hdul.writeto(image_output_filename, overwrite=True)  # write
    else:
        full_image.write(image_output_filename)  # create a new image
        with fits.open(image_output_filename) as hdul:
            hdul[0].header[
                "EXTNAME"] = spherex_filter_name  # add extension name
            hdul[0].header["LAMBDA"] = str(
                float(spherex_filters["lam"]
                      [spherex_filter_id]))  # add filter lambda
            hdul.writeto(image_output_filename, overwrite=True)  # write

    logger.info('Wrote image to %r', image_output_filename)

    ## write truth catalog
    truth_catalog_filename = image_output_filename.replace(
        ".fits", "_%s.csv" % spherex_filter_name)
    truth_catalog.write(truth_catalog_filename, overwrite=True, format="csv")
    logger.info('Wrote truth catalog to %r', truth_catalog_filename)

    print("--- ALL DONE! ----")
Beispiel #18
0
def main(argv):

    ## fixed parameters
    random_seed = 314
    rng = galsim.BaseDeviate(random_seed)
    random_dir = galsim.UniformDeviate(rng)
    poisson_noise = galsim.PoissonNoise(rng)
    dither_i = 22535
    use_SCA = 1
    filter_ = 'H158'
    stamp_size = 32
    hlr = 1.0
    bpass = wfirst.getBandpasses(AB_zeropoint=True)[filter_]
    galaxy_sed_n = galsim.SED('Mrk_33_spec.dat',
                              wave_type='Ang',
                              flux_type='flambda')

    ## variable arguments
    gal_num = int(sys.argv[1])
    PA1 = int(sys.argv[2])
    PA2 = int(sys.argv[3])
    galaxy_model = sys.argv[4]
    PSF_model = sys.argv[5]
    shape = sys.argv[6]
    output_name = sys.argv[7]

    # when using more galaxies than the length of truth file.
    res_noshear = np.zeros(gal_num,
                           dtype=[('ind', int), ('flux', float), ('g1', float),
                                  ('g2', float), ('e1', float), ('e2', float),
                                  ('snr', float), ('hlr', float),
                                  ('flags', int)])
    res_1p = np.zeros(gal_num,
                      dtype=[('ind', int), ('flux', float), ('g1', float),
                             ('g2', float), ('e1', float), ('e2', float),
                             ('snr', float), ('hlr', float), ('flags', int)])
    res_1m = np.zeros(gal_num,
                      dtype=[('ind', int), ('flux', float), ('g1', float),
                             ('g2', float), ('e1', float), ('e2', float),
                             ('snr', float), ('hlr', float), ('flags', int)])
    res_2p = np.zeros(gal_num,
                      dtype=[('ind', int), ('flux', float), ('g1', float),
                             ('g2', float), ('e1', float), ('e2', float),
                             ('snr', float), ('hlr', float), ('flags', int)])
    res_2m = np.zeros(gal_num,
                      dtype=[('ind', int), ('flux', float), ('g1', float),
                             ('g2', float), ('e1', float), ('e2', float),
                             ('snr', float), ('hlr', float), ('flags', int)])
    if shape == 'metacal':
        res_tot = [res_noshear, res_1p, res_1m, res_2p, res_2m]
    elif shape == 'noboot':
        res_tot = [res_noshear, res_1p, res_1m, res_2p, res_2m]
    elif shape == 'ngmix':
        res_tot = [res_noshear]

    PSF = getPSF(PSF_model, use_SCA, filter_, bpass)
    position_angle1 = PA1  #degrees
    position_angle2 = PA2  #degrees
    wcs1, sky_level1 = get_wcs(dither_i, use_SCA, filter_, stamp_size,
                               position_angle1)
    wcs2, sky_level2 = get_wcs(dither_i, use_SCA, filter_, stamp_size,
                               position_angle2)
    wcs = [wcs1, wcs2]
    sky_level = [sky_level1, sky_level2]

    t0 = time.time()
    for i_gal in range(gal_num):
        if i_gal % size != rank:
            continue

        if i_gal % 100 == 0:
            print('rank', rank, 'object number, ', i_gal)

        gal_model = None
        st_model = None

        if galaxy_model == "Gaussian":
            tot_mag = np.random.choice(cat)
            sed = galsim.SED('CWW_E_ext.sed', 'A', 'flambda')
            sed = sed.withMagnitude(tot_mag, bpass)
            flux = sed.calculateFlux(bpass)
            gal_model = galsim.Gaussian(
                half_light_radius=hlr, flux=1.
            )  # needs to normalize the flux before multiplying by sed. For bdf, there are bulge, disk, knots fractions to sum to 1.
            ## making galaxy sed
            #knots = galsim.RandomKnots(10, half_light_radius=1.3, flux=100)
            #knots = make_sed_model(galsim.ChromaticObject(knots), galaxy_sed_n, filter_, bpass)
            #gal_model = galsim.Add([gal_model, knots])
            gal_model = sed * gal_model
            ## shearing
            if i_gal % 2 == 0:
                gal_model = gal_model.shear(g1=0.02, g2=0)
                g1 = 0.02
                g2 = 0
            else:
                gal_model = gal_model.shear(g1=-0.02, g2=0)
                g1 = -0.02
                g2 = 0
        elif galaxy_model == "exponential":
            tot_mag = np.random.choice(cat)
            sed = galsim.SED('CWW_E_ext.sed', 'A', 'flambda')
            sed = sed.withMagnitude(tot_mag, bpass)
            flux = sed.calculateFlux(bpass)
            gal_model = galsim.Exponential(half_light_radius=hlr, flux=1.)
            ## making galaxy sed ## random knots should be used for bdf model
            #knots = galsim.RandomKnots(10, half_light_radius=1.3, flux=1.)
            #knots = make_sed_model(galsim.ChromaticObject(knots), galaxy_sed_n, filter_, bpass)
            #gal_model = galsim.Add([gal_model, knots])
            gal_model = sed * gal_model
            ## shearing
            if i_gal % 2 == 0:
                gal_model = gal_model.shear(g1=0, g2=0.02)
                g1 = 0
                g2 = 0.02
            else:
                gal_model = gal_model.shear(g1=0, g2=-0.02)
                g1 = 0
                g2 = -0.02

        gal_model = gal_model * galsim.wfirst.collecting_area * galsim.wfirst.exptime

        flux_ = gal_model.calculateFlux(bpass)
        #mag_ = gal_model.calculateMagnitude(bpass)
        # This makes the object achromatic, which speeds up drawing and convolution
        gal_model = gal_model.evaluateAtWavelength(bpass.effective_wavelength)
        # Reassign correct flux
        gal_model = gal_model.withFlux(flux_)
        gal_model = galsim.Convolve(gal_model, PSF)

        st_model = galsim.DeltaFunction(flux=1.)
        st_model = st_model.evaluateAtWavelength(bpass.effective_wavelength)
        # reassign correct flux
        starflux = 1.
        st_model = st_model.withFlux(starflux)
        st_model = galsim.Convolve(st_model, PSF)

        stamp_size_factor = old_div(
            int(gal_model.getGoodImageSize(wfirst.pixel_scale)), stamp_size)
        if stamp_size_factor == 0:
            stamp_size_factor = 1

        # Galsim world coordinate object (ra,dec)
        """
        ra = cat[i_gal]['ra']
        dec = cat[i_gal]['dec']
        int_e1 = cat[i_gal]['int_e1']
        int_e2 = cat[i_gal]['int_e2']
        radec = galsim.CelestialCoord(ra*galsim.radians, dec*galsim.radians)
        # Galsim image coordinate object 
        xy = wcs.toImage(radec)
        # Galsim integer image coordinate object 
        xyI = galsim.PositionI(int(xy.x),int(xy.y))
        """
        #xyI = galsim.PositionI(int(stamp_size_factor*stamp_size), int(stamp_size_factor*stamp_size))
        #b = galsim.BoundsI( xmin=1,
        #                    xmax=xyI.x,
        #                    ymin=1,
        #                    ymax=xyI.y)
        #print(xyI.x, int(stamp_size_factor*stamp_size), xyI.x-old_div(int(stamp_size_factor*stamp_size),2)+1)
        #print(b)
        # Create postage stamp for galaxy
        #print("galaxy ", i_gal, ra, dec, int_e1, int_e2)

        ## translational dither check (multiple exposures)
        """
        position_angle1=20*random_dir() #degrees
        position_angle2=position_angle1 #degrees
        wcs1, sky_level1 = get_wcs(dither_i, use_SCA, filter_, stamp_size, position_angle1)
        wcs2, sky_level2 = get_wcs(dither_i, use_SCA, filter_, stamp_size, position_angle2)
        wcs=[wcs1, wcs2]
        sky_level=[sky_level1, sky_level2]
        """

        sca_center = [
            wcs[0].toWorld(
                galsim.PositionI(old_div(wfirst.n_pix, 2),
                                 old_div(wfirst.n_pix, 2))), wcs[1].toWorld(
                                     galsim.PositionI(old_div(wfirst.n_pix, 2),
                                                      old_div(wfirst.n_pix,
                                                              2)))
        ]
        gal_radec = sca_center[0]
        thetas = [position_angle1 * (np.pi / 180) * galsim.radians]
        offsets = []
        gals = []
        psfs = []
        skys = []
        for i in range(2):
            gal_stamp = None
            psf_stamp = None
            xy = wcs[i].toImage(gal_radec)  # galaxy position
            xyI = galsim.PositionI(int(xy.x), int(xy.y))
            b = galsim.BoundsI(
                xmin=xyI.x - old_div(int(stamp_size_factor * stamp_size), 2) +
                1,
                ymin=xyI.y - old_div(int(stamp_size_factor * stamp_size), 2) +
                1,
                xmax=xyI.x + old_div(int(stamp_size_factor * stamp_size), 2),
                ymax=xyI.y + old_div(int(stamp_size_factor * stamp_size), 2))
            #---------------------------------------#
            # if the image does not use a real wcs. #
            #---------------------------------------#
            #b = galsim.BoundsI( xmin=1,
            #                    xmax=int(stamp_size_factor*stamp_size),
            #                    ymin=1,
            #                    ymax=int(stamp_size_factor*stamp_size))
            gal_stamp = galsim.Image(b, wcs=wcs[i])  #scale=wfirst.pixel_scale)
            psf_stamp = galsim.Image(b, wcs=wcs[i])  #scale=wfirst.pixel_scale)

            ## translational dithering test
            #dx = 0 #random_dir() - 0.5
            #dy = 0 #random_dir() - 0.5
            #offset = np.array((dx,dy))

            offset = xy - gal_stamp.true_center  # original galaxy position - stamp center
            gal_model.drawImage(image=gal_stamp, offset=offset)
            st_model.drawImage(image=psf_stamp, offset=offset)

            sigma = wfirst.read_noise
            read_noise = galsim.GaussianNoise(rng, sigma=sigma)

            im, sky_image = add_background(gal_stamp,
                                           sky_level[i],
                                           b,
                                           thermal_backgrounds=None,
                                           filter_='H158',
                                           phot=False)
            #im.addNoise(read_noise)
            gal_stamp = add_poisson_noise(rng,
                                          im,
                                          sky_image=sky_image,
                                          phot=False)
            #sky_image = add_poisson_noise(rng, sky_image, sky_image=sky_image, phot=False)
            gal_stamp -= sky_image

            # set a simple jacobian to the stamps before sending them to ngmix
            # old center of the stamp
            origin_x = gal_stamp.origin.x
            origin_y = gal_stamp.origin.y
            gal_stamp.setOrigin(0, 0)
            psf_stamp.setOrigin(0, 0)
            new_pos = galsim.PositionD(xy.x - origin_x, xy.y - origin_y)
            wcs_transf = gal_stamp.wcs.affine(image_pos=new_pos)
            new_wcs = galsim.JacobianWCS(wcs_transf.dudx, wcs_transf.dudy,
                                         wcs_transf.dvdx, wcs_transf.dvdy)
            gal_stamp.wcs = new_wcs
            psf_stamp.wcs = new_wcs

            offsets.append(offset)
            gals.append(gal_stamp)
            psfs.append(psf_stamp)
            skys.append(sky_image)
        print(gals)
        res_tot = get_coadd_shape(cat, gals, psfs, offsets, skys, i_gal, hlr,
                                  res_tot, g1, g2, shape)
    print(res_tot)
    ## send and receive objects from one processor to others
    if rank != 0:
        # send res_tot to rank 0 processor
        comm.send(res_tot, dest=0)
    else:
        for i in range(comm.size):
            if i == 0:
                continue
            # for other processors, receive res_tot.
            res_ = comm.recv(source=i)
            for j in range(len(res_tot)):
                for col in res_tot[j].dtype.names:
                    res_tot[j][col] += res_[j][col]

    if rank == 0:
        dirr = output_name
        for i in range(len(res_tot)):
            fio.write(dirr + '_sim_' + str(i) + '.fits', res_tot[i])

    if rank == 0:
        bias = residual_bias(res_tot, shape)
        #final = residual_bias_correction(res_tot,R11,R22,R12,R21)
        print(time.time() - t0)

    return None
def make_a_galaxy(ud,wcs,psf,affine,fitcat,cosmos_cat,nfw,optics,bandpass,sbparams):
    """
    Method to make a single galaxy object and return stamp for 
    injecting into larger GalSim image
    """
    # Choose a random RA, Dec around the sky_center.
    # Note that for this to come out close to a square shape, we need to account for the
    # cos(dec) part of the metric: ds^2 = dr^2 + r^2 d(dec)^2 + r^2 cos^2(dec) d(ra)^2
    # So need to calculate dec first.
    """
    dec = sbparams.center_dec + (ud()-0.5) * sbparams.image_ysize_arcsec * galsim.arcsec
    ra = sbparams.center_ra + (ud()-0.5) * sbparams.image_xsize_arcsec / numpy.cos(dec) * galsim.arcsec
    world_pos = galsim.CelestialCoord(ra,dec)
    # We will need the image position as well, so use the wcs to get that
    image_pos = wcs.toImage(world_pos)
   
    # We also need this in the tangent plane, which we call "world coordinates" here.
    # This is still an x/y corrdinate 
    uv_pos = affine.toWorld(image_pos)
    """
    
    
    # Choose a random RA, Dec around the sky_center. Start with XY coords,
    # or else image doesn't get filled!
    center_dec=wcs.center.dec
    center_ra=wcs.center.ra
    center_coords = galsim.CelestialCoord(center_ra,center_dec)
    centerpix = wcs.toImage(center_coords)
    
    x = (2.*ud()-1) * 0.5 * sbparams.image_xsize
    y = (2.*ud()-1) * 0.5 * sbparams.image_ysize
   
    # We will need the image position as well, so use the wcs to get that
    image_pos = galsim.PositionD(x+centerpix.x,y+centerpix.y)
    world_pos = wcs.toWorld(image_pos)
    ra=world_pos.ra; dec = world_pos.dec

    # We also need this in the tangent plane, which we call "world coordinates" here,
    # This is still an x/y corrdinate 
    uv_pos = affine.toWorld(image_pos)
    logger.debug('made it through WCS calculations...')
    
    gal_z = 0    
    
    # Get the reduced shears and magnification at this point

    g1 = 0.0; g2 = 0.0
    mu = 1.0
   
    
    # Generate PSF at location of star, convolve with optical model to make a star
    star_flux = 1E4
    deltastar = galsim.DeltaFunction(flux=star_flux)  
    this_psf = psf.getPSF(image_pos)
    logger.debug("obtained PSF at image position")
    
    final = galsim.Convolve([this_psf, deltastar])
    logger.debug("Convolved galaxy and PSF at image position")
   
    
    # Account for the fractional part of the position
    # cf. demo9.py for an explanation of this nominal position stuff.
    x_nominal = image_pos.x + 0.5
    y_nominal = image_pos.y + 0.5
    ix_nominal = int(math.floor(x_nominal+0.5))
    iy_nominal = int(math.floor(y_nominal+0.5))
    dx = x_nominal - ix_nominal
    dy = y_nominal - iy_nominal
    offset = galsim.PositionD(dx,dy)
    position=[ix_nominal,iy_nominal,ra.deg,dec.deg]
    
    # We use method='no_pixel' here because the SDSS PSF image that we are using includes the
    # pixel response already.
    this_stamp_image = galsim.Image(64, 64,wcs=wcs.local(image_pos))

    """ I highly doubt this is the problem (the stamp_image bit) but you never know. Keep in mind """
    stamp = final.drawImage(image=this_stamp_image, offset=offset, method='no_pixel')
    stamp.setCenter(ix_nominal,iy_nominal)
    logger.debug('drew & centered galaxy!')    
    
    galaxy_truth=truth()
    galaxy_truth.ra=ra.deg; galaxy_truth.dec=dec.deg
    galaxy_truth.x=ix_nominal; galaxy_truth.y=iy_nominal
    galaxy_truth.g1=g1; galaxy_truth.g2=g2
    galaxy_truth.mu = mu; galaxy_truth.z = gal_z
    galaxy_truth.flux = stamp.added_flux
    logger.debug('created truth values')
    
    try:
        galaxy_truth.fwhm=final.calculateFWHM()
    except galsim.errors.GalSimError:
        logger.debug('fwhm calculation failed')
        galaxy_truth.fwhm=-9999.0

    try:
        galaxy_truth.mom_size=stamp.FindAdaptiveMom().moments_sigma
    except galsim.errors.GalSimError:
        logger.debug('sigma calculation failed')
        galaxy_truth.mom_size=-9999.
        
    return stamp, galaxy_truth
def make_a_star(ud, wcs, psf, affine, optics, sbparams):
    """
    makes a star-like object for injection into larger image.
    """

    
    center_dec=wcs.center.dec
    center_ra=wcs.center.ra
    center_coords = galsim.CelestialCoord(center_ra,center_dec)
    centerpix = wcs.toImage(center_coords)

    x = (2.*ud()-1) * 0.5 * sbparams.image_xsize
    y = (2.*ud()-1) * 0.5 * sbparams.image_ysize

    # We will need the image position as well, so use the wcs to get that
    
    image_pos = galsim.PositionD(x+centerpix.x,y+centerpix.y)
    world_pos = wcs.toWorld(image_pos)
    ra=world_pos.ra; dec = world_pos.dec
          
    star_flux = 1E4
    
    # Generate PSF at location of star, convolve with optical model to make a star
    deltastar = galsim.DeltaFunction(flux=star_flux)  
    this_psf = psf.getPSF(image_pos)
    star=galsim.Convolve([this_psf,deltastar])
    #gauss_psf = galsim.Gaussian(flux=1,fwhm=0.25)
    #star=galsim.Convolve([gauss_psf,deltastar])

        
    # Account for the fractional part of the position
    # cf. demo9.py for an explanation of this nominal position stuff.
    x_nominal = image_pos.x + 0.5
    y_nominal = image_pos.y + 0.5
    ix_nominal = int(math.floor(x_nominal+0.5))
    iy_nominal = int(math.floor(y_nominal+0.5))
    dx = x_nominal - ix_nominal
    dy = y_nominal - iy_nominal
    offset = galsim.PositionD(dx,dy)
    star_stamp = star.drawImage(wcs=wcs.local(image_pos), offset=offset, method='no_pixel')

    # Recenter the stamp at the desired position:
    star_stamp.setCenter(ix_nominal,iy_nominal)
    
    star_truth=truth()
    star_truth.ra = ra.deg; star_truth.dec = dec.deg
    star_truth.x = ix_nominal; star_truth.y = iy_nominal

    try:
        star_truth.fwhm=star.calculateFWHM()
    except galsim.errors.GalSimError:
        logger.debug('fwhm calculation failed')
        star_truth.fwhm=-9999.0

    try:
        star_truth.mom_size=star_stamp.FindAdaptiveMom().moments_sigma
    except galsim.errors.GalSimError:
        logger.debug('sigma calculation failed')
        star_truth.mom_size=-9999.

    return star_stamp, star_truth
def make_cluster_galaxy(ud, wcs, psf, affine, centerpix, cluster_cat, optics, bandpass, sbparams):
    """
    Method to make a single galaxy object and return stamp for 
    injecting into larger GalSim image

    Galaxies defined here are not lensed, and are magnified to
    look more "cluster-like." 
    """
    
    center_dec=wcs.center.dec
    center_ra=wcs.center.ra 
    center_coords = galsim.CelestialCoord(center_ra,center_dec)
    centerpix = wcs.toImage(center_coords)
    
    # Choose a random position within 200 pixels of the sky_center
    radius = 180
    max_rsq = radius**2
    while True:  # (This is essentially a do..while loop.)
        x = (2.*ud()-1) * radius 
        y = (2.*ud()-1) * radius 
        rsq = x**2 + y**2
        
        if rsq <= max_rsq: break
    
    # We will need the image position as well, so use the wcs to get that,
    # plus a small gaussian jitter so cluster doesn't look too box-like
    image_pos = galsim.PositionD(x+centerpix.x+(ud()-0.5)*30,y+centerpix.y+(ud()-0.5)*30)
    world_pos = wcs.toWorld(image_pos)
    ra=world_pos.ra; dec = world_pos.dec
   
    # Fixed redshift for cluster galaxies
    gal_z = 0.0
    # FIXME: This appears to be missing and should be fixed????
    g1 = 0.0; g2 = 0.0
    mu = 1.0
    
    star_flux = 1E4
    deltastar = galsim.DeltaFunction(flux=star_flux)  
    this_psf = psf.getPSF(image_pos)
    final=galsim.Convolve([this_psf,deltastar])
    logger.debug("Convolved star and PSF at galaxy position")

    
    # Account for the fractional part of the position
    # cf. demo9.py for an explanation of this nominal position stuff.
    x_nominal = image_pos.x + 0.5
    y_nominal = image_pos.y + 0.5
    ix_nominal = int(math.floor(x_nominal+0.5))
    iy_nominal = int(math.floor(y_nominal+0.5))
    dx = x_nominal - ix_nominal
    dy = y_nominal - iy_nominal
    offset = galsim.PositionD(dx,dy)
    position=[ix_nominal,iy_nominal,ra.deg,dec.deg]
    
    # Draw galaxy image
    this_stamp_image = galsim.Image(64, 64,wcs=wcs.local(image_pos))
    cluster_stamp = final.drawImage(image=this_stamp_image, offset=offset,method='no_pixel')
    cluster_stamp.setCenter(ix_nominal,iy_nominal)
    logger.debug('drew & centered galaxy!')    

    cluster_galaxy_truth=truth()
    cluster_galaxy_truth.ra=ra.deg; cluster_galaxy_truth.dec=dec.deg
    cluster_galaxy_truth.x=ix_nominal; cluster_galaxy_truth.y=iy_nominal
    cluster_galaxy_truth.g1=g1; cluster_galaxy_truth.g2=g2
    cluster_galaxy_truth.mu = mu; cluster_galaxy_truth.z = gal_z
    cluster_galaxy_truth.flux = cluster_stamp.added_flux
    logger.debug('created truth values')
    
    try:
        cluster_galaxy_truth.fwhm=final.calculateFWHM()
    except galsim.errors.GalSimError:
        logger.debug('fwhm calculation failed')
        cluster_galaxy_truth.fwhm=-9999.0

    try:
        cluster_galaxy_truth.mom_size=cluster_stamp.FindAdaptiveMom().moments_sigma
    except:
        logger.debug('sigma calculation failed')
        cluster_galaxy_truth.mom_size=-9999.
    
    return cluster_stamp, cluster_galaxy_truth
Beispiel #22
0
    def getObj(self,
               index,
               gsparams=None,
               rng=None,
               bandpass=None,
               chromatic=False,
               exp_time=30):
        params = self.objinfo[index]

        magnorm = self.getMagNorm(index)
        if magnorm >= 50:
            # Mark of invalid object apparently
            return None

        if gsparams is not None:
            gsparams = galsim.GSParams(**gsparams)

        # Make the object according to the values in the objinfo

        # Note: params here starts at 12, so all indices are 12 less than in previous code.
        if params[0].lower() == 'point':
            obj = galsim.DeltaFunction(gsparams=gsparams)

        elif params[0].lower() == 'sersic2d':
            a = float(params[1])
            b = float(params[2])
            if b > a:
                # Invalid, but existing code just lets it pass.
                return None
            pa = float(params[3])
            if self.flip_g2:
                # Previous code first did PA = 360 - params[3]
                # Then beta = 90 + PA
                beta = float(90 - pa) * galsim.degrees
            else:
                beta = float(90 + pa) * galsim.degrees

            n = float(params[4])
            # GalSim can amortize some calculations for Sersics, but only if n is the same
            # as a previous galaxy.  So quantize the n values at 0.05.  There's no way anyone
            # cares about this at higher resolution than that.
            # For now, this is not actually helpful, since n is always either 1 or 4, but if
            # we ever start having more variable n, this will prevent it from redoing Hankel
            # integrals for every galaxy.
            n = round(n * 20.) / 20.

            hlr = (a * b)**0.5  # geometric mean of a and b is close to right.
            # XXX: Note: Previous code had hlr = a, which is wrong. (?)  Galaxies were too large.
            #      Especially when they were more elliptical.  Oops.
            # TODO: Maybe not?  Check if this should be a.
            obj = galsim.Sersic(n=n, half_light_radius=hlr, gsparams=gsparams)
            shear = galsim.Shear(q=b / a, beta=beta)
            obj = obj._shear(shear)
            g1, g2, mu = self.getLens(index)
            obj = obj._lens(g1, g2, mu)

        elif params[0].lower() == 'knots':
            a = float(params[1])
            b = float(params[2])
            if b > a:
                return None
            pa = float(params[3])
            if self.flip_g2:
                beta = float(90 - pa) * galsim.degrees
            else:
                beta = float(90 + pa) * galsim.degrees
            npoints = int(params[4])
            if npoints <= 0:
                # Again, weird, but previous code just lets this pass without comment.
                return None
            hlr = (a * b)**0.5
            obj = galsim.RandomKnots(npoints=npoints,
                                     half_light_radius=hlr,
                                     rng=rng,
                                     gsparams=gsparams)
            shear = galsim.Shear(q=b / a, beta=beta)
            obj = obj._shear(shear)
            # TODO: These look bad in space images (cf. Troxel's talks about Roman sims.)
            #       Should convolve this by a smallish Gaussian *here*:
            #       I'd guess 0.3 arcsec is a good choice for the fwhm of this Gaussian.
            # obj = galsim.Convolve(obj, galsim.Gaussian(fwhm=0.3))
            g1, g2, mu = self.getLens(index)
            obj = obj._lens(g1, g2, mu)

        elif (params[0].endswith('.fits') or params[0].endswith('.fits.gz')):
            fits_file = find_file_path(params[0], get_image_dirs())
            pixel_scale = float(params[1])
            theta = float(params[2])
            obj = galsim.InterpolatedImage(fits_file,
                                           scale=pixel_scale,
                                           gsparams=gsparams)
            if theta != 0.:
                obj = obj.rotate(-theta * galsim.degrees)
            g1, g2, mu = self.getLens(index)
            obj = obj._lens(g1, g2, mu)

        else:
            raise RuntimeError("Do not know how to handle object type: %s" %
                               params[0])

        # The seds are normalized to correspond to magnorm=0.
        # The flux for the given magnorm is 10**(-0.4*magnorm)
        # The constant here, 0.9210340371976184 = 0.4 * log(10)
        flux = math.exp(-0.9210340371976184 * magnorm)

        # This gives the normalization in photons/cm^2/sec.
        # Multiply by area and exptime to get photons.
        fAt = flux * self._rubin_area * exp_time

        sed = self.getSED(index)
        if chromatic:
            return obj.withFlux(fAt) * sed
        else:
            flux = sed.calculateFlux(bandpass) * fAt
            return obj.withFlux(flux)
Beispiel #23
0
def test_dcr():
    """Test the dcr surface op
    """
    # This tests that implementing DCR with the surface op is equivalent to using
    # ChromaticAtmosphere.
    # We use fairly extreme choices for the parameters to make the comparison easier, so
    # we can still get good discrimination of any errors with only 10^6 photons.
    zenith_angle = 45 * galsim.degrees  # Larger angle has larger DCR.
    parallactic_angle = 129 * galsim.degrees  # Something random, not near 0 or 180
    pixel_scale = 0.03  # Small pixel scale means shifts are many pixels, rather than a fraction.
    alpha = -1.2  # The normal alpha is -0.2, so this is exaggerates the effect.

    bandpass = galsim.Bandpass('LSST_r.dat', 'nm')
    base_wavelength = bandpass.effective_wavelength
    base_wavelength += 500  # This exaggerates the effects fairly substantially.

    sed = galsim.SED('CWW_E_ext.sed', wave_type='ang', flux_type='flambda')

    flux = 1.e6
    base_PSF = galsim.Kolmogorov(fwhm=0.3)

    # Use ChromaticAtmosphere
    im1 = galsim.ImageD(50, 50, scale=pixel_scale)
    star = galsim.DeltaFunction() * sed
    star = star.withFlux(flux, bandpass=bandpass)
    chrom_PSF = galsim.ChromaticAtmosphere(base_PSF,
                                           base_wavelength=base_wavelength,
                                           zenith_angle=zenith_angle,
                                           parallactic_angle=parallactic_angle,
                                           alpha=alpha)
    chrom = galsim.Convolve(star, chrom_PSF)
    chrom.drawImage(bandpass, image=im1)

    # Use PhotonDCR
    im2 = galsim.ImageD(50, 50, scale=pixel_scale)
    dcr = galsim.PhotonDCR(base_wavelength=base_wavelength,
                           zenith_angle=zenith_angle,
                           parallactic_angle=parallactic_angle,
                           alpha=alpha)
    achrom = base_PSF.withFlux(flux)
    rng = galsim.BaseDeviate(31415)
    wave_sampler = galsim.WavelengthSampler(sed, bandpass, rng)
    surface_ops = [wave_sampler, dcr]
    achrom.drawImage(image=im2,
                     method='phot',
                     rng=rng,
                     surface_ops=surface_ops)

    im1 /= flux  # Divide by flux, so comparison is on a relative basis.
    im2 /= flux
    printval(im2, im1, show=False)
    np.testing.assert_almost_equal(
        im2.array,
        im1.array,
        decimal=4,
        err_msg="PhotonDCR didn't match ChromaticAtmosphere")

    # Repeat with thinned bandpass and SED to check that thin still works well.
    im3 = galsim.ImageD(50, 50, scale=pixel_scale)
    thin = 0.1  # Even higher also works.  But this is probably enough.
    thin_bandpass = bandpass.thin(thin)
    thin_sed = sed.thin(thin)
    print('len bp = %d => %d' %
          (len(bandpass.wave_list), len(thin_bandpass.wave_list)))
    print('len sed = %d => %d' % (len(sed.wave_list), len(thin_sed.wave_list)))
    wave_sampler = galsim.WavelengthSampler(thin_sed, thin_bandpass, rng)
    achrom.drawImage(image=im3,
                     method='phot',
                     rng=rng,
                     surface_ops=surface_ops)

    im3 /= flux
    printval(im3, im1, show=False)
    np.testing.assert_almost_equal(
        im3.array,
        im1.array,
        decimal=4,
        err_msg="thinning factor %f led to 1.e-4 level inaccuracy" % thin)

    # Check scale_unit
    im4 = galsim.ImageD(50, 50, scale=pixel_scale / 60)
    dcr = galsim.PhotonDCR(base_wavelength=base_wavelength,
                           zenith_angle=zenith_angle,
                           parallactic_angle=parallactic_angle,
                           scale_unit='arcmin',
                           alpha=alpha)
    surface_ops = [wave_sampler, dcr]
    achrom.dilate(1. / 60).drawImage(image=im4,
                                     method='phot',
                                     rng=rng,
                                     surface_ops=surface_ops)
    im4 /= flux
    printval(im4, im1, show=False)
    np.testing.assert_almost_equal(
        im4.array,
        im1.array,
        decimal=4,
        err_msg="PhotonDCR with scale_unit=arcmin, didn't match")

    # Check some other valid options
    # alpha = 0 means don't do any size scaling.
    # obj_coord, HA and latitude are another option for setting the angles
    # pressure, temp, and water pressure are settable.
    # Also use a non-trivial WCS.
    wcs = galsim.FitsWCS('des_data/DECam_00154912_12_header.fits')
    image = galsim.Image(50, 50, wcs=wcs)
    bandpass = galsim.Bandpass('LSST_r.dat', wave_type='nm').thin(0.1)
    base_wavelength = bandpass.effective_wavelength
    lsst_lat = galsim.Angle.from_dms('-30:14:23.76')
    lsst_long = galsim.Angle.from_dms('-70:44:34.67')
    local_sidereal_time = 3.14 * galsim.hours  # Not pi. This is the time for this observation.

    im5 = galsim.ImageD(50, 50, wcs=wcs)
    obj_coord = wcs.toWorld(im5.true_center)
    base_PSF = galsim.Kolmogorov(fwhm=0.9)
    achrom = base_PSF.withFlux(flux)
    dcr = galsim.PhotonDCR(
        base_wavelength=bandpass.effective_wavelength,
        obj_coord=obj_coord,
        HA=local_sidereal_time - obj_coord.ra,
        latitude=lsst_lat,
        pressure=72,  # default is 69.328
        temperature=290,  # default is 293.15
        H2O_pressure=0.9)  # default is 1.067
    #alpha=0)            # default is 0, so don't need to set it.
    surface_ops = [wave_sampler, dcr]
    achrom.drawImage(image=im5,
                     method='phot',
                     rng=rng,
                     surface_ops=surface_ops)

    im6 = galsim.ImageD(50, 50, wcs=wcs)
    star = galsim.DeltaFunction() * sed
    star = star.withFlux(flux, bandpass=bandpass)
    chrom_PSF = galsim.ChromaticAtmosphere(
        base_PSF,
        base_wavelength=bandpass.effective_wavelength,
        obj_coord=obj_coord,
        HA=local_sidereal_time - obj_coord.ra,
        latitude=lsst_lat,
        pressure=72,
        temperature=290,
        H2O_pressure=0.9,
        alpha=0)
    chrom = galsim.Convolve(star, chrom_PSF)
    chrom.drawImage(bandpass, image=im6)

    im5 /= flux  # Divide by flux, so comparison is on a relative basis.
    im6 /= flux
    printval(im5, im6, show=False)
    np.testing.assert_almost_equal(
        im5.array,
        im6.array,
        decimal=3,
        err_msg="PhotonDCR with alpha=0 didn't match")

    # Also check invalid parameters
    zenith_coord = galsim.CelestialCoord(13.54 * galsim.hours, lsst_lat)
    assert_raises(
        TypeError,
        galsim.PhotonDCR,
        zenith_angle=zenith_angle,
        parallactic_angle=parallactic_angle)  # base_wavelength is required
    assert_raises(TypeError,
                  galsim.PhotonDCR,
                  base_wavelength=500,
                  parallactic_angle=parallactic_angle
                  )  # zenith_angle (somehow) is required
    assert_raises(
        TypeError,
        galsim.PhotonDCR,
        500,
        zenith_angle=34.4,
        parallactic_angle=parallactic_angle)  # zenith_angle must be Angle
    assert_raises(TypeError,
                  galsim.PhotonDCR,
                  500,
                  zenith_angle=zenith_angle,
                  parallactic_angle=34.5)  # parallactic_angle must be Angle
    assert_raises(TypeError,
                  galsim.PhotonDCR,
                  500,
                  obj_coord=obj_coord,
                  latitude=lsst_lat)  # Missing HA
    assert_raises(TypeError,
                  galsim.PhotonDCR,
                  500,
                  obj_coord=obj_coord,
                  HA=local_sidereal_time - obj_coord.ra)  # Missing latitude
    assert_raises(TypeError, galsim.PhotonDCR, 500,
                  obj_coord=obj_coord)  # Need either zenith_coord, or (HA,lat)
    assert_raises(TypeError,
                  galsim.PhotonDCR,
                  500,
                  obj_coord=obj_coord,
                  zenith_coord=zenith_coord,
                  HA=local_sidereal_time -
                  obj_coord.ra)  # Can't have both HA and zenith_coord
    assert_raises(TypeError,
                  galsim.PhotonDCR,
                  500,
                  obj_coord=obj_coord,
                  zenith_coord=zenith_coord,
                  latitude=lsst_lat)  # Can't have both lat and zenith_coord
    assert_raises(TypeError,
                  galsim.PhotonDCR,
                  500,
                  zenith_angle=zenith_angle,
                  parallactic_angle=parallactic_angle,
                  H20_pressure=1.)  # invalid (misspelled)
    assert_raises(ValueError,
                  galsim.PhotonDCR,
                  500,
                  zenith_angle=zenith_angle,
                  parallactic_angle=parallactic_angle,
                  scale_unit='inches')  # invalid scale_unit

    # Invalid to use dcr without some way of setting wavelengths.
    assert_raises(galsim.GalSimError,
                  achrom.drawImage,
                  im2,
                  method='phot',
                  surface_ops=[dcr])
Beispiel #24
0
def make_a_star(ud, wcs=None, psf=None, affine=None):
    logger.debug('entered make a star method...')

    # Choose a random RA, Dec around the sky_center. Start with XY coords,
    # or else image doesn't get filled!

    center_dec = wcs.center.dec
    center_ra = wcs.center.ra
    center_coords = galsim.CelestialCoord(center_ra, center_dec)
    centerpix = wcs.toImage(center_coords)

    x = (2. * ud() - 1) * 0.5 * image_xsize
    y = (2. * ud() - 1) * 0.5 * image_ysize

    # We will need the image position as well, so use the wcs to get that

    image_pos = galsim.PositionD(x + centerpix.x, y + centerpix.y)
    world_pos = wcs.toWorld(image_pos)
    ra = world_pos.ra
    dec = world_pos.dec

    # We also need this in the tangent plane, which we call "world coordinates" here,
    # This is still an x/y corrdinate

    uv_pos = affine.toWorld(image_pos)
    logger.debug('made it through WCS calculations...')

    # Draw star flux at random; based on distribution of star fluxes in real images
    # Generate PSF at location of star, convolve simple Airy with the PSF to make a star

    flux_dist = galsim.DistDeviate(ud,
                                   function=lambda x: x**-1.,
                                   x_min=1226.2965,
                                   x_max=1068964.0)
    star_flux = flux_dist()
    shining_star = galsim.DeltaFunction(flux=star_flux)
    logger.debug('created star object with flux')

    # Final profile is the convolution of PSF and galaxy image
    # Can include any number of things in the list, all of which are convolved
    # together to make the final flux profile.

    this_psf = psf.getPSF(image_pos)
    star = galsim.Convolve([shining_star, this_psf])
    logger.debug('convolved star & psf')

    # Account for the fractional part of the position, and
    # recenter the stamp at the desired position.
    # (cf. demo9.py for an explanation of  nominal position stuff.)

    x_nominal = image_pos.x + 0.5
    y_nominal = image_pos.y + 0.5
    ix_nominal = int(math.floor(x_nominal + 0.5))
    iy_nominal = int(math.floor(y_nominal + 0.5))
    dx = x_nominal - ix_nominal
    dy = y_nominal - iy_nominal
    offset = galsim.PositionD(dx, dy)
    star_image = galsim.Image(512, 512, wcs=wcs.local(image_pos))
    star_stamp = star.drawImage(image=star_image,
                                offset=offset,
                                method='no_pixel')
    star_stamp.setCenter(ix_nominal, iy_nominal)
    logger.debug('made a star_stamp')

    star_truth = truth()
    star_truth.ra = ra.deg
    star_truth.dec = dec.deg
    star_truth.x = ix_nominal
    star_truth.y = iy_nominal
    star_truth.flux = star_stamp.added_flux
    try:
        star_truth.fwhm = final.CalculateFWHM()
        star_truth.mom_size = star_stamp.FindAdaptiveMom().moments_sigma
    except:
        star_truth.fwhm = -9999.0
        star_truth.mom_size = star_stamp.FindAdaptiveMom().moments_sigma
    logger.debug('made it through star recentering')
    results = star_stamp.FindAdaptiveMom()
    logger.debug('HSM reports that the image has observed shape and size:')
    logger.debug('    e1 = %.3f, e2 = %.3f, sigma = %.3f (pixels)',
                 results.observed_shape.e1, results.observed_shape.e2,
                 results.moments_sigma)

    return star_stamp, star_truth
Beispiel #25
0
def test_deltaFunction():
    """Test the generation of a Delta function profile
    """
    # Check construction with no arguments gives expected result
    delta = galsim.DeltaFunction()
    np.testing.assert_almost_equal(delta.flux, 1.0)
    check_basic(delta, "DeltaFunction")
    do_pickle(delta)

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

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

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

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

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

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

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

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

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

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

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

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

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

    # Test kvalues
    do_kvalue(delta_conv, myImg, "Delta Function")
import sys, os
import math
import logging
import time
import galsim as galsim
import galsim.wfirst as wfirst

stamp_size = [1600, 32, 256]
SCA = 1
filter_ = 'H158'
bpass = wfirst.getBandpasses(AB_zeropoint=True)[filter_]
psf = wfirst.getPSF(SCA,
                    filter_,
                    pupil_bin=4,
                    n_waves=10,
                    wavelength=bpass.effective_wavelength)

for stamp in stamp_size:
    st_model = galsim.DeltaFunction(flux=1.)
    st_model = st_model.evaluateAtWavelength(bpass.effective_wavelength)
    # reassign correct flux
    starflux = 1.
    st_model = st_model.withFlux(starflux)
    st_model = galsim.Convolve(st_model, psf)

    xyI = galsim.PositionI(int(stamp), int(stamp))
    b = galsim.BoundsI(xmin=1, xmax=xyI.x, ymin=1, ymax=xyI.y)
    psf_stamp = galsim.Image(b, scale=wfirst.pixel_scale)
    st_model.drawImage(image=psf_stamp)
    psf_stamp.write('psf_size_pupil4_' + str(stamp) + '.fits')
Beispiel #27
0
def main(argv):
    # Where to find and output data.
    path, filename = os.path.split(__file__)
    outpath = os.path.abspath(os.path.join(path, "output/"))

    # Just use a few galaxies, to save time.  Note that we are going to put 4000 galaxy images into
    # our big image, so if we have n_use=10, each galaxy will appear 400 times.  Users who want a
    # more interesting image with greater variation in the galaxy population can change `n_use` to
    # something larger (but it should be <=100, the number of galaxies in this small example
    # catalog).  With 4000 galaxies in a 4k x 4k image with the WFIRST pixel scale, the effective
    # galaxy number density is 74/arcmin^2.  This is not the number density that is expected for a
    # sample that is so bright (I<23.5) but it makes the image more visually interesting.  One could
    # think of it as what you'd get if you added up several images at once, making the images for a
    # sample that is much deeper have the same S/N as that for an I<23.5 sample in a single image.
    n_use = 10
    n_tot = 4000

    # Default is to use all filters.  Specify e.g. 'YJH' to only do Y106, J129, and H158.
    use_filters = None

    # quick and dirty command line parsing.
    for var in argv:
        if var.startswith('data='): datapath = var[5:]
        if var.startswith('out='): outpath = var[4:]
        if var.startswith('nuse='): n_use = int(var[5:])
        if var.startswith('ntot='): n_tot = int(var[5:])
        if var.startswith('filters='): use_filters = var[8:].upper()

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

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

    # Initialize (pseudo-)random number generator.
    random_seed = 123456
    rng = galsim.BaseDeviate(random_seed)

    # Generate a Poisson noise model.
    poisson_noise = galsim.PoissonNoise(rng)
    logger.info('Poisson noise model created.')

    # Read in the WFIRST filters, setting an AB zeropoint appropriate for this telescope given its
    # diameter and (since we didn't use any keyword arguments to modify this) using the typical
    # exposure time for WFIRST images.  By default, this routine truncates the parts of the
    # bandpasses that are near 0 at the edges, and thins them by the default amount.
    filters = wfirst.getBandpasses(AB_zeropoint=True)
    logger.debug('Read in WFIRST imaging filters.')

    logger.info('Reading from a parametric COSMOS catalog.')
    # Read in a galaxy catalog - just a random subsample of 100 galaxies for F814W<23.5 from COSMOS.
    cat_file_name = 'real_galaxy_catalog_23.5_example_fits.fits'
    dir = 'data'
    # Use the routine that can take COSMOS real or parametric galaxy information, and tell it we
    # want parametric galaxies that represent an I<23.5 sample.
    cat = galsim.COSMOSCatalog(cat_file_name, dir=dir, use_real=False)
    logger.info('Read in %d galaxies from catalog'%cat.nobjects)

    # Here we carry out the initial steps that are necessary to get a fully chromatic PSF.  We use
    # the getPSF() routine in the WFIRST module, which knows all about the telescope parameters
    # (diameter, bandpasses, obscuration, etc.).  Note that we arbitrarily choose a single SCA
    # (Sensor Chip Assembly) rather than all of them, for faster calculations, and use a simple
    # representation of the struts for faster calculations.  To do a more exact calculation of the
    # chromaticity and pupil plane configuration, remove the `approximate_struts` and the `n_waves`
    # keyword from the call to getPSF():
    use_SCA = 7 # This could be any number from 1...18
    logger.info('Doing expensive pre-computation of PSF.')
    t1 = time.time()
    logger.setLevel(logging.DEBUG)
    # Need to make a separate PSF for each filter.  We are, however, ignoring the
    # position-dependence of the PSF within each SCA, just using the PSF at the center of the SCA
    # (default kwargs).
    PSFs = {}
    for filter_name, filter_ in filters.items():
        logger.info('PSF pre-computation for SCA %d, filter %s.'%(use_SCA, filter_name))
        PSFs[filter_name] = wfirst.getPSF(use_SCA, filter_name,
                                          approximate_struts=True, n_waves=10, logger=logger)
    logger.setLevel(logging.INFO)
    t2 = time.time()
    logger.info('Done PSF precomputation in %.1f seconds!'%(t2-t1))

    # Define the size of the postage stamp that we use for each individual galaxy within the larger
    # image, and for the PSF images.
    stamp_size = 256

    # We choose a particular (RA, dec) location on the sky for our observation.
    ra_targ = 90.*galsim.degrees
    dec_targ = -10.*galsim.degrees
    targ_pos = galsim.CelestialCoord(ra=ra_targ, dec=dec_targ)
    # Get the WCS for an observation at this position.  We are not supplying a date, so the routine
    # will assume it's the vernal equinox.  We are also not supplying a position angle for the
    # observatory, which means that it will just find the optimal one (the one that has the solar
    # panels pointed most directly towards the Sun given this targ_pos and date).  The output of
    # this routine is a dict of WCS objects, one for each SCA.  We then take the WCS for the SCA
    # that we are using.
    wcs_list = wfirst.getWCS(world_pos=targ_pos, SCAs=use_SCA)
    wcs = wcs_list[use_SCA]
    # We need to find the center position for this SCA.  We'll tell it to give us a CelestialCoord
    # corresponding to (X, Y) = (wfirst.n_pix/2, wfirst.n_pix/2).
    SCA_cent_pos = wcs.toWorld(galsim.PositionD(wfirst.n_pix/2, wfirst.n_pix/2))

    # We randomly distribute points in (X, Y) on the CCD.
    # If we had a real galaxy catalog with positions in terms of RA, dec we could use wcs.toImage()
    # to find where those objects should be in terms of (X, Y).
    pos_rng = galsim.UniformDeviate(random_seed)
    # Make a list of (X, Y, F814W magnitude, n_rot, flip) values.
    # (X, Y) give the position of the galaxy centroid (or the center of the postage stamp into which
    # we draw the galaxy) in the big image.
    # F814W magnitudes are randomly drawn from the catalog, and are used to create a more realistic
    # flux distribution for the galaxies instead of just having the 10 flux values for the galaxies
    # we have chosen to draw.
    # n_rot says how many 90 degree rotations to include for a given realization of each galaxy, so
    # it doesn't appear completely identical each time we put it in the image.
    # flip is a random number that will determine whether we include an x-y flip for this appearance
    # of the galaxy or not.
    x_stamp = []
    y_stamp = []
    mag_stamp = []
    n_rot_stamp = []
    flip_stamp = []
    for i_gal in range(n_tot):
        x_stamp.append(pos_rng()*wfirst.n_pix)
        y_stamp.append(pos_rng()*wfirst.n_pix)
        # Note that we could use wcs.toWorld() to get the (RA, dec) for these (x, y) positions.  Or,
        # if we had started with (RA, dec) positions, we could have used wcs.toImage() to get the
        # CCD coordinates for those positions.
        mag_stamp.append(cat.param_cat['mag_auto'][int(pos_rng()*cat.nobjects)])
        n_rot_stamp.append(int(4*pos_rng()))
        flip_stamp.append(pos_rng())

    # Make the 2-component parametric GSObjects for each object, including chromaticity (roughly
    # appropriate SEDs per galaxy component, at the appropriate galaxy redshift).  Note that since
    # the PSF is position-independent within the SCA, we can simply do the convolution with that PSF
    # now instead of using a different one for each position.  We also have to include the correct
    # flux scaling: The catalog returns objects that would be observed by HST in 1 second
    # exposures. So for our telescope we scale up by the relative area and exposure time.  Note that
    # what is important is the *effective* area after taking into account obscuration.
    logger.info('Processing the objects in the catalog to get GSObject representations')
    # Choose a random set of unique indices in the catalog (will be the same each time script is
    # run, due to use of the same random seed):
    rand_indices = []
    while len(rand_indices)<n_use:
        tmp_ind = int(pos_rng()*cat.nobjects)
        if tmp_ind not in rand_indices:
            rand_indices.append(tmp_ind)
    obj_list = cat.makeGalaxy(rand_indices, chromatic=True, gal_type='parametric')
    hst_eff_area = 2.4**2 * (1.-0.33**2)
    wfirst_eff_area = galsim.wfirst.diameter**2 * (1.-galsim.wfirst.obscuration**2)
    flux_scaling = (wfirst_eff_area/hst_eff_area) * wfirst.exptime
    mag_list = []
    for ind in range(len(obj_list)):
        # First, let's check what magnitude this object has in F814W.  We want to do this because
        # (to inject some variety into our images) we are going to rescale the fluxes in all bands
        # for different instances of this galaxy in the final image in order to get a reasonable S/N
        # distribution.  So we need to save the original magnitude in F814W, to compare with a
        # randomly drawn one from the catalog.  This is not something that most users would need to
        # do.
        mag_list.append(cat.param_cat['mag_auto'][cat.orig_index[rand_indices[ind]]])

    # Calculate the sky level for each filter, and draw the PSF and the galaxies through the
    # filters.
    for filter_name, filter_ in filters.items():
        if use_filters is not None and filter_name[0] not in use_filters:
            logger.info('Skipping filter {0}.'.format(filter_name))
            continue

        logger.info('Beginning work for {0}.'.format(filter_name))

        # Drawing PSF.  Note that the PSF object intrinsically has a flat SED, so if we convolve it
        # with a galaxy, it will properly take on the SED of the galaxy.  For the sake of this demo,
        # we will simply convolve with a 'star' that has a flat SED and unit flux in this band, so
        # that the PSF image will be normalized to unit flux. This does mean that the PSF image
        # being drawn here is not quite the right PSF for the galaxy.  Indeed, the PSF for the
        # galaxy effectively varies within it, since it differs for the bulge and the disk.  To make
        # a real image, one would have to choose SEDs for stars and convolve with a star that has a
        # reasonable SED, but we just draw with a flat SED for this demo.
        out_filename = os.path.join(outpath, 'demo13_PSF_{0}.fits'.format(filter_name))
        # Generate a point source.
        point = galsim.DeltaFunction(flux=1.)
        # Use a flat SED here, but could use something else.  A stellar SED for instance.
        # Or a typical galaxy SED.  Depending on your purpose for drawing the PSF.
        star_sed = galsim.SED(lambda x:1, 'nm', 'flambda').withFlux(1.,filter_)  # Give it unit flux in this filter.
        star = galsim.Convolve(point*star_sed, PSFs[filter_name])
        img_psf = galsim.ImageF(64,64)
        star.drawImage(bandpass=filter_, image=img_psf, scale=wfirst.pixel_scale)
        img_psf.write(out_filename)
        logger.debug('Created PSF with flat SED for {0}-band'.format(filter_name))

        # Set up the full image that will contain all the individual galaxy images, with information
        # about WCS:
        final_image = galsim.ImageF(wfirst.n_pix,wfirst.n_pix, wcs=wcs)

        # Draw the galaxies into the image.
        for i_gal in range(n_use):
            logger.info('Drawing image for the object at row %d in the input catalog'%i_gal)

            # We want to only draw the galaxy once (for speed), not over and over with different
            # sub-pixel offsets.  For this reason we ignore the sub-pixel offset entirely.  Note
            # that we are setting the postage stamp to have the average WFIRST pixel scale.  This is
            # simply an approximation for the purpose of speed; really, one should draw directly
            # into final_image, which has the appropriate WCS for WFIRST.  In that case, the image
            # of the galaxy might look different in different parts of the detector due to the WCS
            # (including distortion), and we would have to re-draw each time.  To keep the demo
            # relatively quick, we are just using the approximate average pixel scale and drawing
            # once.
            stamp = galsim.Image(stamp_size, stamp_size, scale=wfirst.pixel_scale)

            # Convolve the chromatic galaxy and the chromatic PSF for this bandpass, and rescale flux.
            final = galsim.Convolve(flux_scaling*obj_list[ind], PSFs[filter_name])
            final.drawImage(filter_, image=stamp)

            # Have to find where to place it:
            for i_gal_use in range(i_gal*n_tot//n_use, (i_gal+1)*n_tot//n_use):
                # Account for the fractional part of the position:
                ix = int(math.floor(x_stamp[i_gal_use]+0.5))
                iy = int(math.floor(y_stamp[i_gal_use]+0.5))
                # We don't actually use this offset.
                offset = galsim.PositionD(x_stamp[i_gal]-ix, y_stamp[i_gal]-iy)

                # Create a nominal bound for the postage stamp given the integer part of the
                # position.
                stamp_bounds = galsim.BoundsI(ix-0.5*stamp_size, ix+0.5*stamp_size-1,
                                              iy-0.5*stamp_size, iy+0.5*stamp_size-1)

                # Find the overlapping bounds between the large image and the individual postage
                # stamp.
                bounds = stamp_bounds & final_image.bounds

                # Just to inject a bit of variety into the image, so it isn't *quite* as obvious
                # that we've repeated the same 10 objects over and over, we randomly rotate the
                # postage stamp by some factor of 90 degrees and possibly include a random flip.
                if flip_stamp[i_gal_use] > 0.5:
                    new_arr = numpy.ascontiguousarray(
                        numpy.rot90(stamp.array, n_rot_stamp[i_gal_use]))
                else:
                    new_arr = numpy.ascontiguousarray(
                        numpy.fliplr(numpy.rot90(stamp.array, n_rot_stamp[i_gal_use])))
                stamp_rot = galsim.Image(new_arr, scale=stamp.scale)
                stamp_rot.setOrigin(galsim.PositionI(stamp_bounds.xmin, stamp_bounds.ymin))

                # Rescale the flux to match that of a randomly chosen galaxy in the galaxy, but
                # keeping the same SED as for this particular galaxy.  This gives a bit more
                # variety in the flux values and SNR of the galaxies in the image without having
                # to render images of many more objects.
                flux_scaling = 10**(-0.4*(mag_stamp[i_gal_use]-mag_list[i_gal]))

                # Copy the image into the right place in the big image.
                final_image[bounds] += flux_scaling * stamp_rot[bounds]

        # Now we're done with the per-galaxy drawing for this image.  The rest will be done for the
        # entire image at once.
        logger.info('Postage stamps of all galaxies drawn on a single big image for this filter.')
        logger.info('Adding the sky level, noise and detector non-idealities.')

        # First we get the amount of zodaical light for a position corresponding to the center of
        # this SCA.  The results are provided in units of e-/arcsec^2, using the default WFIRST
        # exposure time since we did not explicitly specify one.  Then we multiply this by a factor
        # >1 to account for the amount of stray light that is expected.  If we do not provide a date
        # for the observation, then it will assume that it's the vernal equinox (sun at (0,0) in
        # ecliptic coordinates) in 2025.
        sky_level = wfirst.getSkyLevel(filters[filter_name], world_pos=SCA_cent_pos)
        sky_level *= (1.0 + wfirst.stray_light_fraction)
        # Make a image of the sky that takes into account the spatially variable pixel scale.  Note
        # that makeSkyImage() takes a bit of time.  If you do not care about the variable pixel
        # scale, you could simply compute an approximate sky level in e-/pix by multiplying
        # sky_level by wfirst.pixel_scale**2, and add that to final_image.
        sky_image = final_image.copy()
        wcs.makeSkyImage(sky_image, sky_level)
        # This image is in units of e-/pix.  Finally we add the expected thermal backgrounds in this
        # band.  These are provided in e-/pix/s, so we have to multiply by the exposure time.
        sky_image += wfirst.thermal_backgrounds[filter_name]*wfirst.exptime
        # Adding sky level to the image.
        final_image += sky_image

        # Now that all sources of signal (from astronomical objects and background) have been added
        # to the image, we can start adding noise and detector effects.  There is a utility,
        # galsim.wfirst.allDetectorEffects(), that can apply ALL implemented noise and detector
        # effects in the proper order.  Here we step through the process and explain these in a bit
        # more detail without using that utility.

        # First, we include the expected Poisson noise:
        final_image.addNoise(poisson_noise)

        # The subsequent steps account for the non-ideality of the detectors.

        # 1) Reciprocity failure:
        # Reciprocity, in the context of photography, is the inverse relationship between the
        # incident flux (I) of a source object and the exposure time (t) required to produce a given
        # response(p) in the detector, i.e., p = I*t. However, in NIR detectors, this relation does
        # not hold always. The pixel response to a high flux is larger than its response to a low
        # flux. This flux-dependent non-linearity is known as 'reciprocity failure', and the
        # approximate amount of reciprocity failure for the WFIRST detectors is known, so we can
        # include this detector effect in our images.

        if diff_mode:
            # Save the image before applying the transformation to see the difference
            save_image = final_image.copy()

        # If we had wanted to, we could have specified a different exposure time than the default
        # one for WFIRST, but otherwise the following routine does not take any arguments.
        wfirst.addReciprocityFailure(final_image)
        logger.debug('Included reciprocity failure in {0}-band image'.format(filter_name))

        if diff_mode:
            # Isolate the changes due to reciprocity failure.
            diff = final_image-save_image

            out_filename = os.path.join(outpath,'demo13_RecipFail_{0}.fits'.format(filter_name))
            final_image.write(out_filename)
            out_filename = os.path.join(outpath,
                                        'demo13_diff_RecipFail_{0}.fits'.format(filter_name))
            diff.write(out_filename)

        # At this point in the image generation process, an integer number of photons gets
        # detected, hence we have to round the pixel values to integers:
        final_image.quantize()

        # 2) Adding dark current to the image:
        # Even when the detector is unexposed to any radiation, the electron-hole pairs that
        # are generated within the depletion region due to finite temperature are swept by the
        # high electric field at the junction of the photodiode. This small reverse bias
        # leakage current is referred to as 'dark current'. It is specified by the average
        # number of electrons reaching the detectors per unit time and has an associated
        # Poisson noise since it is a random event.
        dark_current = wfirst.dark_current*wfirst.exptime
        dark_noise = galsim.DeviateNoise(galsim.PoissonDeviate(rng, dark_current))
        final_image.addNoise(dark_noise)

        # NOTE: Sky level and dark current might appear like a constant background that can be
        # simply subtracted. However, these contribute to the shot noise and matter for the
        # non-linear effects that follow. Hence, these must be included at this stage of the
        # image generation process. We subtract these backgrounds in the end.

        # 3) Applying a quadratic non-linearity:
        # In order to convert the units from electrons to ADU, we must use the gain factor. The gain
        # has a weak dependency on the charge present in each pixel. This dependency is accounted
        # for by changing the pixel values (in electrons) and applying a constant nominal gain
        # later, which is unity in our demo.

        # Save the image before applying the transformation to see the difference:
        if diff_mode:
            save_image = final_image.copy()

        # Apply the WFIRST nonlinearity routine, which knows all about the nonlinearity expected in
        # the WFIRST detectors.
        wfirst.applyNonlinearity(final_image)
        # Note that users who wish to apply some other nonlinearity function (perhaps for other NIR
        # detectors, or for CCDs) can use the more general nonlinearity routine, which uses the
        # following syntax:
        # final_image.applyNonlinearity(NLfunc=NLfunc)
        # with NLfunc being a callable function that specifies how the output image pixel values
        # should relate to the input ones.
        logger.debug('Applied nonlinearity to {0}-band image'.format(filter_name))
        if diff_mode:
            diff = final_image-save_image

            out_filename = os.path.join(outpath,'demo13_NL_{0}.fits'.format(filter_name))
            final_image.write(out_filename)
            out_filename = os.path.join(outpath,'demo13_diff_NL_{0}.fits'.format(filter_name))
            diff.write(out_filename)

            # Save this image to do the diff after applying IPC.
            save_image = final_image.copy()

        # 4) Including Interpixel capacitance:
        # The voltage read at a given pixel location is influenced by the charges present in the
        # neighboring pixel locations due to capacitive coupling of sense nodes. This interpixel
        # capacitance effect is modeled as a linear effect that is described as a convolution of a
        # 3x3 kernel with the image.  The WFIRST IPC routine knows about the kernel already, so the
        # user does not have to supply it.
        wfirst.applyIPC(final_image)
        logger.debug('Applied interpixel capacitance to {0}-band image'.format(filter_name))

        if diff_mode:
            # Isolate the changes due to the interpixel capacitance effect.
            diff = final_image-save_image

            out_filename = os.path.join(outpath,'demo13_IPC_{0}.fits'.format(filter_name))
            final_image.write(out_filename)
            out_filename = os.path.join(outpath,'demo13_diff_IPC_{0}.fits'.format(filter_name))
            diff.write(out_filename)

        # 5) Adding read noise:
        # Read noise is the noise due to the on-chip amplifier that converts the charge into an
        # analog voltage.  We already applied the Poisson noise due to the sky level, so read noise
        # should just be added as Gaussian noise:
        read_noise = galsim.GaussianNoise(rng, sigma=wfirst.read_noise)
        final_image.addNoise(read_noise)
        logger.debug('Added readnoise to {0}-band image'.format(filter_name))

        # We divide by the gain to convert from e- to ADU. Currently, the gain value in the WFIRST
        # module is just set to 1, since we don't know what the exact gain will be, although it is
        # expected to be approximately 1. Eventually, this may change when the camera is assembled,
        # and there may be a different value for each SCA. For now, there is just a single number,
        # which is equal to 1.
        final_image /= wfirst.gain

        # Finally, the analog-to-digital converter reads in an integer value.
        final_image.quantize()
        # Note that the image type after this step is still a float.  If we want to actually
        # get integer values, we can do new_img = galsim.Image(final_image, dtype=int)

        # Since many people are used to viewing background-subtracted images, we provide a
        # version with the background subtracted (also rounding that to an int).
        sky_image.quantize()
        tot_sky_image = (sky_image + round(dark_current))/wfirst.gain
        tot_sky_image.quantize()
        final_image -= tot_sky_image

        logger.debug('Subtracted background for {0}-band image'.format(filter_name))
        # Write the final image to a file.
        out_filename = os.path.join(outpath,'demo13_{0}.fits'.format(filter_name))
        final_image.write(out_filename)

        logger.info('Completed {0}-band image.'.format(filter_name))

    logger.info('You can display the output in ds9 with a command line that looks something like:')
    logger.info('ds9 -zoom 0.5 -scale limits -500 1000 -rgb '+
                '-red output/demo13_H158.fits '+
                '-green output/demo13_J129.fits '+
                '-blue output/demo13_Y106.fits')
def make_a_galaxy(ud, wcs, affine, fitcat, cosmos_cat, nfw, optics, bandpass,
                  sbparams):
    """
    Method to make a single galaxy object and return stamp for 
    injecting into larger GalSim image
    """
    # Choose a random RA, Dec around the sky_center.
    # Note that for this to come out close to a square shape, we need to account for the
    # cos(dec) part of the metric: ds^2 = dr^2 + r^2 d(dec)^2 + r^2 cos^2(dec) d(ra)^2
    # So need to calculate dec first.
    dec = sbparams.center_dec + (
        ud() - 0.5) * sbparams.image_ysize_arcsec * galsim.arcsec
    ra = sbparams.center_ra + (ud(
    ) - 0.5) * sbparams.image_xsize_arcsec / numpy.cos(dec) * galsim.arcsec
    world_pos = galsim.CelestialCoord(ra, dec)
    # We will need the image position as well, so use the wcs to get that
    image_pos = wcs.toImage(world_pos)

    # We also need this in the tangent plane, which we call "world coordinates" here.
    # This is still an x/y corrdinate
    uv_pos = affine.toWorld(image_pos)
    """
    # Create chromatic galaxy
    gal = cosmos_cat.makeGalaxy(gal_type='parametric', rng=ud)
    #logger.debug('created chromatic galaxy')

    # Obtain galaxy redshift from the COSMOS profile fit catalog
    gal_z=fitcat['zphot'][gal.index]
    
    # Apply a random rotation
    theta = ud()*2.0*numpy.pi*galsim.radians
    gal = gal.rotate(theta)
    # This automatically scales up the noise variance (if there is any) by flux_scaling**2.
    gal *= sbparams.flux_scaling
    
    logger.debug('rescaled galaxy with scaling factor %f' % sbparams.flux_scaling)
    
    
    # Get the reduced shears and magnification at this point
    try:
        nfw_shear, mu = nfw_lensing(nfw, uv_pos, gal_z)
        g1=nfw_shear.g1; g2=nfw_shear.g2
        gal = gal.lens(g1, g2, mu)
        
    except:
        print("could not lens galaxy at z = %f, setting default values..." % gal_z)
        g1 = 0.0; g2 = 0.0
        mu = 1.0

    """

    g1 = 0.0
    g2 = 0.0
    mu = 1.0
    gal_z = 0

    # Generate PSF at location of star, convolve with optical model to make a star
    star_flux = 1E4
    deltastar = galsim.DeltaFunction(flux=star_flux)
    #this_psf = psf.getPSF(image_pos)
    #final=galsim.Convolve([optics, this_psf,deltastar])
    gauss_psf = galsim.Gaussian(flux=1, fwhm=0.35)
    final = galsim.Convolve([gauss_psf, deltastar])

    logger.debug("Convolved star and PSF at galaxy position")

    # Account for the fractional part of the position
    # cf. demo9.py for an explanation of this nominal position stuff.
    x_nominal = image_pos.x + 0.5
    y_nominal = image_pos.y + 0.5
    ix_nominal = int(math.floor(x_nominal + 0.5))
    iy_nominal = int(math.floor(y_nominal + 0.5))
    dx = x_nominal - ix_nominal
    dy = y_nominal - iy_nominal
    offset = galsim.PositionD(dx, dy)
    position = [ix_nominal, iy_nominal, ra.deg, dec.deg]

    # We use method='no_pixel' here because the SDSS PSF image that we are using includes the
    # pixel response already.
    this_stamp_image = galsim.Image(64, 64, wcs=wcs.local(image_pos))
    """ I doubt this is the problem (the stamp_image bit) but you never know. 
    Keep in mind for later testing """
    stamp = final.drawImage(image=this_stamp_image,
                            offset=offset,
                            method='no_pixel')
    stamp.setCenter(ix_nominal, iy_nominal)
    logger.debug('drew & centered galaxy!')

    galaxy_truth = truth()
    galaxy_truth.ra = ra.deg
    galaxy_truth.dec = dec.deg
    galaxy_truth.x = ix_nominal
    galaxy_truth.y = iy_nominal
    galaxy_truth.g1 = g1
    galaxy_truth.g2 = g2
    galaxy_truth.mu = mu
    galaxy_truth.z = gal_z
    galaxy_truth.flux = stamp.added_flux
    logger.debug('created truth values')

    try:
        galaxy_truth.fwhm = final.calculateFWHM()
    except galsim.errors.GalSimError:
        logger.debug('fwhm calculation failed')
        galaxy_truth.fwhm = -9999.0

    try:
        galaxy_truth.mom_size = stamp.FindAdaptiveMom().moments_sigma
    except galsim.errors.GalSimError:
        logger.debug('sigma calculation failed')
        galaxy_truth.mom_size = -9999.

    return stamp, galaxy_truth