示例#1
0
文件: value.py 项目: inonchiu/GalSim
def _GenerateFromRandomDistribution(param, param_name, base, value_type):
    """@brief Return a random value drawn from a user-defined probability distribution
    """
    if 'rng' not in base:
        raise ValueError("No rng available for %s.type = RandomDistribution"%param_name)
    rng = base['rng']

    opt = {'function' : str, 'interpolant' : str, 'npoints' : int, 
           'x_min' : float, 'x_max' : float }
    kwargs, safe = GetAllParams(param, param_name, base, opt=opt)
    
    if '_distdev' in param:
        # The overhead for making a DistDeviate is large enough that we'd rather not do it every 
        # time, so first check if we've already made one:
        distdev = param['_distdev']
        if param['_distdev_kwargs'] != kwargs:
            distdev=galsim.DistDeviate(rng,**kwargs)
            param['_distdev'] = distdev
            param['_distdev_kwargs'] = kwargs
    else:
        # Otherwise, just go ahead and make a new one.
        distdev=galsim.DistDeviate(rng,**kwargs)
        param['_distdev'] = distdev
        param['_distdev_kwargs'] = kwargs

    # Typically, the rng will change between successive calls to this, so reset the 
    # seed.  (The other internal calculations don't need to be redone unless the rest of the
    # kwargs have been changed.)
    distdev.reset(rng)

    val = distdev()
    #print base['obj_num'],'distdev = ',val
    return val, False
示例#2
0
 def generateFieldParameters(self, rng, field_index):
     """Generate the constant shear values (two components) for this field."""
     theta = 2.0*rng()*np.pi
     shear_rng = galsim.DistDeviate(rng, function = lambda g : g,
                                    x_min = self.min_g, x_max = self.max_g)
     g = shear_rng()
     return dict(g1=g*np.cos(2.0*theta), g2=g*np.sin(2.0*theta), mu=1.)
示例#3
0
def make_a_star(ud, wcs, psf, affine):
    # Choose a random RA, Dec around the sky_center.
    dec = center_dec + (ud() - 0.5) * image_ysize_arcsec * galsim.arcsec
    ra = center_ra + (
        ud() - 0.5) * 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
    # 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=799.2114,
                                   x_max=890493.9)
    """
    flux_dist = galsim.DistDeviate(ud, function = lambda x:x**-1.5,
                                       x_min = 3000,
                                       x_max = 30000)
    """
    star_flux = flux_dist()
    shining_star = galsim.Airy(lam=lam,
                               obscuration=0.380,
                               diam=tel_diam,
                               scale_unit=galsim.arcsec,
                               flux=star_flux)
    this_psf = psf.getPSF(image_pos)
    star = galsim.Convolve([shining_star, optics, this_psf])
    #star=galsim.Convolve([shining_star,this_psf])
    # 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 = 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

    return star_stamp, star_truth
def make_a_star(ud, this_im_wcs, psf, affine):
    # Choose a random RA, Dec around the sky_center.
    center_dec = this_im_wcs.center.dec
    center_ra = this_im_wcs.center.ra
    dec = center_dec + (ud() - 0.5) * image_ysize_arcsec * galsim.arcsec
    ra = center_ra + (
        ud() - 0.5) * 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 = this_im_wcs.posToImage(world_pos)

    # We also need this in the tangent plane, which we call "world coordinates" here,
    # since the PowerSpectrum class is really defined on that plane, not in (ra,dec).
    # 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
    # Generate PSF at location of star, convolve simple Airy with the PSF to make a star

    flux_dist = galsim.DistDeviate(
        ud,
        function=
        '/Users/jemcclea/Research/GalSim/examples/output/empirical_psfs/v2/stars_flux300_prob.txt'
    )  #,interpolant='floor')
    star_flux = (flux_dist()) * (exp_time / 300.)
    shining_star = galsim.Airy(lam=lam,
                               obscuration=0.3840,
                               diam=tel_diam,
                               scale_unit=galsim.arcsec,
                               flux=star_flux)
    this_psf = psf.getPSF(image_pos)
    star = galsim.Convolve([shining_star, this_psf])

    # 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=this_im_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

    return star_stamp, star_truth
示例#5
0
def _GenerateFromRandomDistribution(config, base, value_type):
    """@brief Return a random value drawn from a user-defined probability distribution
    """
    rng = galsim.config.GetRNG(config, base)

    ignore = [ 'x', 'f', 'x_log', 'f_log' ]
    opt = {'function' : str, 'interpolant' : str, 'npoints' : int,
           'x_min' : float, 'x_max' : float }
    kwargs, safe = galsim.config.GetAllParams(config, base, opt=opt, ignore=ignore)

    # Allow the user to give x,f instead of function to define a LookupTable.
    if 'x' in config or 'f' in config:
        if 'x' not in config or 'f' not in config:
            raise galsim.GalSimConfigError(
                "Both x and f must be provided for type=RandomDistribution")
        if 'function' in kwargs:
            raise galsim.GalSimConfigError(
                "Cannot provide function with x,f for type=RandomDistribution")
        x = config['x']
        f = config['f']
        x_log = config.get('x_log', False)
        f_log = config.get('f_log', False)
        interpolant = kwargs.pop('interpolant', 'spline')
        kwargs['function'] = galsim.LookupTable(x=x, f=f, x_log=x_log, f_log=f_log,
                                                interpolant=interpolant)
    else:
        if 'function' not in kwargs:
            raise galsim.GalSimConfigError(
                "function or x,f  must be provided for type=RandomDistribution")
        if 'x_log' in config or 'f_log' in config:
            raise galsim.GalSimConfigError(
                "x_log, f_log are invalid with function for type=RandomDistribution")

    if '_distdev' not in config or config['_distdev_kwargs'] != kwargs:
        # The overhead for making a DistDeviate is large enough that we'd rather not do it every
        # time, so first check if we've already made one:
        distdev=galsim.DistDeviate(rng,**kwargs)
        config['_distdev'] = distdev
        config['_distdev_kwargs'] = kwargs
    else:
        distdev = config['_distdev']

    # Typically, the rng will change between successive calls to this, so reset the
    # seed.  (The other internal calculations don't need to be redone unless the rest of the
    # kwargs have been changed.)
    distdev.reset(rng)

    val = distdev()
    #print(base['obj_num'],'distdev = ',val)
    return val, False
示例#6
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
示例#7
0
    def sampleWavelength(self, nphotons, bandpass, rng=None, npoints=256):
        """ Sample a number of random wavelength values from the SED, possibly as observed through
        a bandpass.

        @param nphotons  Number of samples (photons) to randomly draw.
        @param bandpass  A Bandpass object representing a filter, or None to sample over the full 
                         SED wavelength range.
        @param rng       If provided, a random number generator that is any kind of BaseDeviate 
                         object. If `rng` is None, one will be automatically created, using the 
                         time as a seed. [default: None]
        @param npoints   Number of points DistDeviate should use for its internal interpolation
                         tables. [default: 256]
        """
        if int(nphotons) != nphotons:
            raise TypeError("'nphotons' must be integer type")
        nphotons = int(nphotons)

        if bandpass is None:
            sed = self
        else:
            sed = self * bandpass

        # Speed up the integration by skipping the overhead of __call__
        sed._make_fast_spec()
        a = 1 / (1.0 + sed.redshift)
        fn = lambda x: sed._fast_spec(a * x)

        # Create a lookup dict for storing the deviate (save construction time)
        if not hasattr(self, 'deviate'):
            self.deviate = dict()

        key = (bandpass, npoints)
        try:
            dev = self.deviate[key]
        except KeyError:
            dev = galsim.DistDeviate(function=fn,
                                     x_min=sed.blue_limit,
                                     x_max=sed.red_limit,
                                     npoints=npoints)
            self.deviate[key] = dev

        # Reset the deviate explicitly
        if rng is not None: dev.reset(rng)

        ret = np.empty(nphotons)
        dev.generate(ret)
        return ret
示例#8
0
def test_float_value():
    """Test various ways to generate a float value
    """
    import time
    t1 = time.time()

    config = {
        'input' : { 'catalog' : { 'dir' : 'config_input', 'file_name' : 'catalog.txt' } },

        'val1' : 9.9,
        'val2' : int(400),
        'str1' : '8.73',
        'str2' : '2.33e-9',
        'str3' : '6.e-9', 
        'cat1' : { 'type' : 'InputCatalog' , 'col' : 0 },
        'cat2' : { 'type' : 'InputCatalog' , 'col' : 1 },
        'ran1' : { 'type' : 'Random', 'min' : 0.5, 'max' : 3 },
        'ran2' : { 'type' : 'Random', 'min' : -5, 'max' : 0 },
        'gauss1' : { 'type' : 'RandomGaussian', 'sigma' : 1 },
        'gauss2' : { 'type' : 'RandomGaussian', 'sigma' : 3, 'mean' : 4 },
        'gauss3' : { 'type' : 'RandomGaussian', 'sigma' : 1.5, 'min' : -2, 'max' : 2 },
        'gauss4' : { 'type' : 'RandomGaussian', 'sigma' : 0.5, 'min' : 0, 'max' : 0.8 },
        'gauss5' : { 'type' : 'RandomGaussian',
                     'sigma' : 0.3, 'mean' : 0.5, 'min' : 0, 'max' : 0.5 },
        'dist1' : { 'type' : 'RandomDistribution', 'function' : 'config_input/distribution.txt', 
                    'interpolant' : 'linear' },
        'dist2' : { 'type' : 'RandomDistribution', 'function' : 'config_input/distribution2.txt', 
                    'interpolant' : 'linear' },
        'dist3' : { 'type' : 'RandomDistribution', 'function' : 'x*x', 
                    'x_min' : 0., 'x_max' : 2.0 },
        'seq1' : { 'type' : 'Sequence' },
        'seq2' : { 'type' : 'Sequence', 'step' : 0.1 },
        'seq3' : { 'type' : 'Sequence', 'first' : 1.5, 'step' : 0.5 },
        'seq4' : { 'type' : 'Sequence', 'first' : 10, 'step' : -2 },
        'seq5' : { 'type' : 'Sequence', 'first' : 1, 'last' : 2.1, 'repeat' : 2 },
        'list1' : { 'type' : 'List', 'items' : [ 73, 8.9, 3.14 ] },
        'list2' : { 'type' : 'List',
                    'items' : [ 0.6, 1.8, 2.1, 3.7, 4.3, 5.5, 6.1, 7.0, 8.6, 9.3, 10.8, 11.2 ],
                    'index' : { 'type' : 'Sequence', 'first' : 10, 'step' : -3 } }
    }

    galsim.config.ProcessInput(config)

    # Test direct values
    val1 = galsim.config.ParseValue(config,'val1',config, float)[0]
    np.testing.assert_almost_equal(val1, 9.9)

    val2 = galsim.config.ParseValue(config,'val2',config, float)[0]
    np.testing.assert_almost_equal(val2, 400)

    # Test conversions from strings
    str1 = galsim.config.ParseValue(config,'str1',config, float)[0]
    np.testing.assert_almost_equal(str1, 8.73)

    str2 = galsim.config.ParseValue(config,'str2',config, float)[0]
    np.testing.assert_almost_equal(str2, 2.33e-9)

    str3 = galsim.config.ParseValue(config,'str3',config, float)[0]
    np.testing.assert_almost_equal(str3, 6.0e-9)

    # Test values read from an InputCatalog
    input_cat = galsim.InputCatalog(dir='config_input', file_name='catalog.txt')
    cat1 = []
    cat2 = []
    for k in range(5):
        config['seq_index'] = k
        cat1.append(galsim.config.ParseValue(config,'cat1',config, float)[0])
        cat2.append(galsim.config.ParseValue(config,'cat2',config, float)[0])

    np.testing.assert_array_almost_equal(cat1, [ 1.234, 2.345, 3.456, 1.234, 2.345 ])
    np.testing.assert_array_almost_equal(cat2, [ 4.131, -900, 8000, 4.131, -900 ])

    # Test values generated from a uniform deviate
    rng = galsim.UniformDeviate(1234)
    config['rng'] = galsim.UniformDeviate(1234) # A second copy starting with the same seed.
    for k in range(6):
        ran1 = galsim.config.ParseValue(config,'ran1',config, float)[0]
        np.testing.assert_almost_equal(ran1, rng() * 2.5 + 0.5)

        ran2 = galsim.config.ParseValue(config,'ran2',config, float)[0]
        np.testing.assert_almost_equal(ran2, rng() * 5 - 5)

    # Test values generated from a Gaussian deviate
    gd = galsim.GaussianDeviate(rng)
    for k in range(6):
        gauss1 = galsim.config.ParseValue(config,'gauss1',config, float)[0]
        gd.setMean(0)
        gd.setSigma(1)
        np.testing.assert_almost_equal(gauss1, gd())

        gauss2 = galsim.config.ParseValue(config,'gauss2',config, float)[0]
        gd.setMean(4)
        gd.setSigma(3)
        np.testing.assert_almost_equal(gauss2, gd())

        gauss3 = galsim.config.ParseValue(config,'gauss3',config, float)[0]
        gd.setMean(0)
        gd.setSigma(1.5)
        gd_val = gd()
        while math.fabs(gd_val) > 2:
            gd_val = gd()
        np.testing.assert_almost_equal(gauss3, gd_val)

        gauss4 = galsim.config.ParseValue(config,'gauss4',config, float)[0]
        gd.setMean(0)
        gd.setSigma(0.5)
        gd_val = math.fabs(gd())
        while gd_val > 0.8:
            gd_val = math.fabs(gd())
        np.testing.assert_almost_equal(gauss4, gd_val)

        gauss5 = galsim.config.ParseValue(config,'gauss5',config, float)[0]
        gd.setMean(0.5)
        gd.setSigma(0.3)
        gd_val = gd()
        if gd_val > 0.5: 
            gd_val = 1-gd_val
        while gd_val < 0:
            gd_val = gd()
            if gd_val > 0.5: 
                gd_val = 1-gd_val
        np.testing.assert_almost_equal(gauss5, gd_val)

    # Test values generated from a distribution in a file
    dd=galsim.DistDeviate(rng,function='config_input/distribution.txt',interpolant='linear')
    for k in range(6):
        dist1 = galsim.config.ParseValue(config,'dist1',config, float)[0]
        np.testing.assert_almost_equal(dist1, dd())
    dd=galsim.DistDeviate(rng,function='config_input/distribution2.txt',interpolant='linear')
    for k in range(6):
        dist2 = galsim.config.ParseValue(config,'dist2',config, float)[0]
        np.testing.assert_almost_equal(dist2, dd())
    dd=galsim.DistDeviate(rng,function=lambda x: x*x,x_min=0.,x_max=2.)
    for k in range(6):
        dist3 = galsim.config.ParseValue(config,'dist3',config, float)[0]
        np.testing.assert_almost_equal(dist3, dd())

    # Test values generated from a Sequence
    seq1 = []
    seq2 = []
    seq3 = []
    seq4 = []
    seq5 = []
    for k in range(6):
        config['seq_index'] = k
        seq1.append(galsim.config.ParseValue(config,'seq1',config, float)[0])
        seq2.append(galsim.config.ParseValue(config,'seq2',config, float)[0])
        seq3.append(galsim.config.ParseValue(config,'seq3',config, float)[0])
        seq4.append(galsim.config.ParseValue(config,'seq4',config, float)[0])
        seq5.append(galsim.config.ParseValue(config,'seq5',config, float)[0])

    np.testing.assert_array_almost_equal(seq1, [ 0, 1, 2, 3, 4, 5 ])
    np.testing.assert_array_almost_equal(seq2, [ 0, 0.1, 0.2, 0.3, 0.4, 0.5 ])
    np.testing.assert_array_almost_equal(seq3, [ 1.5, 2, 2.5, 3, 3.5, 4 ])
    np.testing.assert_array_almost_equal(seq4, [ 10, 8, 6, 4, 2, 0 ])
    np.testing.assert_array_almost_equal(seq5, [ 1, 1, 2, 2, 1, 1 ])

    # Test values taken from a List
    list1 = []
    list2 = []
    for k in range(5):
        config['seq_index'] = k
        list1.append(galsim.config.ParseValue(config,'list1',config, float)[0])
        list2.append(galsim.config.ParseValue(config,'list2',config, float)[0])

    np.testing.assert_array_almost_equal(list1, [ 73, 8.9, 3.14, 73, 8.9 ])
    np.testing.assert_array_almost_equal(list2, [ 10.8, 7.0, 4.3, 1.8, 10.8 ])

    t2 = time.time()
    print 'time for %s = %.2f'%(funcname(),t2-t1)
def make_a_star(ud,wcs=None,affine=None):
    #star_flux = 1.e5    # total counts on the image
    #star_sigma = 2.     # arcsec
    
    logger.debug('entered make a star method...')
    
    # Choose a random RA, Dec around the sky_center.
    dec = center_dec + (ud()-0.5) * image_ysize_arcsec * galsim.arcsec
    ra = center_ra + (ud()-0.5) * 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)
    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
    if (exp_time==300):
        flux_dist = galsim.DistDeviate(ud, function = lambda x:x**-0.9, x_min = 799.2114, x_max = 890493.9)
    else:
        flux_dist = galsim.DistDeviate(ud, function = lambda x:x**-1., x_min = 1226.2965, x_max = 968964.0) 
    star_flux = flux_dist()
    
    star_flux = flux_dist()
    shining_star = galsim.Airy(lam=lam, obscuration=0.3840, diam=tel_diam, scale_unit=galsim.arcsec,flux=star_flux)
    logger.debug('created star object with flux')

    
    this_psf = galsim.Gaussian(flux=1., fwhm=psf_fwhm)
    star=galsim.Convolve([shining_star,this_psf])
    logger.debug('convolved star & psf')
  
    # 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 = star.drawImage(wcs=wcs.local(image_pos),offset=offset)
    logger.debug('made a star_stamp')
    
    # 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
    star_truth.flux=star_stamp.added_flux
    logger.debug('made it through star recentering and flux adding')
    
    try:
        star_truth.fwhm=final.calculateFWHM()
        #star_truth.mom_size=star_stamp.FindAdaptiveMom().moments_sigma
        star_truth.final_sigmaSize=star_stamp.FindAdaptiveMom().moments_sigma
        star_truth.nopsf_sigmaSize=-9999.

    except:
        #pdb.set_trace()
        star_truth.fwhm=-9999.0
        star_truth.final_sigmaSize=-9999.
        star_truth.nopsf_sigmaSize=-9999.

    return star_stamp, star_truth
def make_a_galaxy(ud,wcs,affine):
    """
    Method to make a single galaxy object and return stamp for 
    injecting into larger GalSim image
    """

    gsp=galsim.GSParams(maximum_fft_size=16154)

    # 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 = center_dec + (ud()-0.5) * image_ysize_arcsec * galsim.arcsec
    ra = center_ra + (ud()-0.5) * 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 the redshift from a power law distribution: N(f) ~ f^-2
    redshift_dist = galsim.DistDeviate(ud, function = lambda x:x**-2,
                                           x_min = 0.5,
                                           x_max = 1.5)
    gal_z = redshift_dist()
    
    
    # Get the reduced shears and magnification at this point
    nfw_shear, mu = nfw_lensing(nfw, uv_pos, gal_z)
    g1=nfw_shear.g1; g2=nfw_shear.g2
    
    gal = cosmos_cat.makeGalaxy(gal_type='parametric', rng=ud,gsparams=gsp)
    
    
    # Apply a random rotation
    theta = ud()*2.0*numpy.pi*galsim.radians
    gal = gal.rotate(theta)
    
    
    # Rescale the flux to match the observed A2218 flux. The flux of gal can't be edited directly,
    # so we resort to the following kludge. 
    # This automatically scales up the noise variance by flux_scaling**2.
    gal *= flux_scaling
    logger.debug('rescaled galaxy flux to %f' % flux_scaling)
    
    
    # Apply the cosmological (reduced) shear and magnification at this position using a single
    # GSObject method.

    try:
        """
        gal = gal.magnify(nfw_mu)
        gal = gal.shear(total_shear)
        """
        gal = gal.lens(g1, g2, mu)
    except:
        print("could not lens galaxy, setting default values...")
        g1 = 0.0; g2 = 0.0
        mu = 1.0
    
    # Generate PSF at location of galaxy
    # Convolve galaxy image with the PSF.
    #this_psf = psf.getPSF(image_pos)
    #logger.debug("obtained PSF at image position")

    this_psf = galsim.Gaussian(flux=1., fwhm=psf_fwhm)
    logger.debug("created Gaussian PSF at image position")
    
    final = galsim.Convolve([this_psf, gal],gsparams=gsp)
    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' for "final" because the PSFEx image that we are using includes the
    # pixel response already.
    """
    semifinal=gal.drawImage(wcs=wcs.local(image_pos), offset=offset, method='no_pixel') 
    stamp = final.drawImage(wcs=wcs.local(image_pos), offset=offset, method='no_pixel')
    """
    try:
        semifinal=gal.drawImage(wcs=wcs.local(image_pos), offset=offset) 
        stamp = final.drawImage(wcs=wcs.local(image_pos), offset=offset)
        logger.debug("Images drawn")

        # Recenter the stamp at the desired position:
        semifinal.setCenter(ix_nominal,iy_nominal)
        stamp.setCenter(ix_nominal,iy_nominal)
        logger.debug("Created stamp and set center!")
        new_variance=0.0 
    except:
        logger.debug("semifinal stamp failed -- probably too big")
                
    # If desired, one can also draw the PSF and output its moments too, as:
    # psf_stamp = galsim.ImageF(stamp.bounds)
    # psf_im=this_psf.drawImage(psf_stamp,wcs=wcs.local(image_pos), offset=offset,method='no_pixel')
     
    try:
        new_variance = stamp.whitenNoise(final.noise)
        logger.debug("whitened stamp noise")
    except:
        logger.debug("no noise to whiten")

    logger.debug("noise variance is %f" % new_variance)
    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
    galaxy_truth.variance=new_variance
    try:
        galaxy_truth.fwhm=final.calculateFWHM()
        galaxy_truth.final_sigmaSize=stamp.FindAdaptiveMom().moments_sigma
        galaxy_truth.nopsf_sigmaSize=semifinal.FindAdaptiveMom().moments_sigma
    except:
        
        logger.debug('failed at FWHM/mom_size stage')        
        galaxy_truth.fwhm=-9999.0
        galaxy_truth.mom_size=stamp.FindAdaptiveMom().moments_sigma
        galaxy_truth.final_sigmaSize=-9999.0
        galaxy_truth.nopsf_sigmaSize=-9999.0
      
    try:
        galaxy_truth.g1_nopsf=semifinal.FindAdaptiveMom().observed_shape.g1
        galaxy_truth.g2_nopsf=semifinal.FindAdaptiveMom().observed_shape.g2
    except:
        logger.debug('Could not obtain no_psf galaxy adaptive moments, setting to zero')
        galaxy_truth.g2_nopsf=0
        galaxy_truth.g1_nopsf=0
        #pdb.set_trace()

    return stamp, galaxy_truth
示例#11
0
def test_float_value():
    """Test various ways to generate a float value
    """
    import time
    t1 = time.time()

    config = {
        'input': {
            'catalog': [{
                'dir': 'config_input',
                'file_name': 'catalog.txt'
            }, {
                'dir': 'config_input',
                'file_name': 'catalog.fits'
            }],
            'dict': [{
                'dir': 'config_input',
                'file_name': 'dict.p'
            }, {
                'dir': 'config_input',
                'file_name': 'dict.json'
            }, {
                'dir': 'config_input',
                'file_name': 'dict.yaml'
            }]
        },
        'val1': 9.9,
        'val2': int(400),
        'str1': '8.73',
        'str2': '2.33e-9',
        'str3': '6.e-9',
        'cat1': {
            'type': 'Catalog',
            'col': 0
        },
        'cat2': {
            'type': 'Catalog',
            'col': 1
        },
        'cat3': {
            'type': 'Catalog',
            'num': 1,
            'col': 'float1'
        },
        'cat4': {
            'type': 'Catalog',
            'num': 1,
            'col': 'float2'
        },
        'ran1': {
            'type': 'Random',
            'min': 0.5,
            'max': 3
        },
        'ran2': {
            'type': 'Random',
            'min': -5,
            'max': 0
        },
        'gauss1': {
            'type': 'RandomGaussian',
            'sigma': 1
        },
        'gauss2': {
            'type': 'RandomGaussian',
            'sigma': 3,
            'mean': 4
        },
        'gauss3': {
            'type': 'RandomGaussian',
            'sigma': 1.5,
            'min': -2,
            'max': 2
        },
        'gauss4': {
            'type': 'RandomGaussian',
            'sigma': 0.5,
            'min': 0,
            'max': 0.8
        },
        'gauss5': {
            'type': 'RandomGaussian',
            'sigma': 0.3,
            'mean': 0.5,
            'min': 0,
            'max': 0.5
        },
        'dist1': {
            'type': 'RandomDistribution',
            'function': 'config_input/distribution.txt',
            'interpolant': 'linear'
        },
        'dist2': {
            'type': 'RandomDistribution',
            'function': 'config_input/distribution2.txt',
            'interpolant': 'linear'
        },
        'dist3': {
            'type': 'RandomDistribution',
            'function': 'x*x',
            'x_min': 0.,
            'x_max': 2.0
        },
        'dev1': {
            'type': 'RandomPoisson',
            'mean': 137
        },
        'dev2': {
            'type': 'RandomBinomial',
            'N': 17
        },
        'dev3': {
            'type': 'RandomBinomial',
            'N': 17,
            'p': 0.2
        },
        'dev4': {
            'type': 'RandomWeibull',
            'a': 1.7,
            'b': 4.3
        },
        'dev5': {
            'type': 'RandomGamma',
            'k': 1,
            'theta': 4
        },
        'dev6': {
            'type': 'RandomGamma',
            'k': 1.9,
            'theta': 4.1
        },
        'dev7': {
            'type': 'RandomChi2',
            'n': 17
        },
        'seq1': {
            'type': 'Sequence'
        },
        'seq2': {
            'type': 'Sequence',
            'step': 0.1
        },
        'seq3': {
            'type': 'Sequence',
            'first': 1.5,
            'step': 0.5
        },
        'seq4': {
            'type': 'Sequence',
            'first': 10,
            'step': -2
        },
        'seq5': {
            'type': 'Sequence',
            'first': 1,
            'last': 2.1,
            'repeat': 2
        },
        'list1': {
            'type': 'List',
            'items': [73, 8.9, 3.14]
        },
        'list2': {
            'type': 'List',
            'items':
            [0.6, 1.8, 2.1, 3.7, 4.3, 5.5, 6.1, 7.0, 8.6, 9.3, 10.8, 11.2],
            'index': {
                'type': 'Sequence',
                'first': 10,
                'step': -3
            }
        },
        'dict1': {
            'type': 'Dict',
            'key': 'f'
        },
        'dict2': {
            'type': 'Dict',
            'num': 1,
            'key': 'f'
        },
        'dict3': {
            'type': 'Dict',
            'num': 2,
            'key': 'f'
        },
        'dict4': {
            'type': 'Dict',
            'num': 2,
            'key': 'noise.models.1.gain'
        },
        'sum1': {
            'type': 'Sum',
            'items': [72, '2.33', {
                'type': 'Dict',
                'key': 'f'
            }]
        }
    }

    test_yaml = True
    try:
        galsim.config.ProcessInput(config)
    except:
        # We don't require PyYAML as a dependency, so if this fails, just remove the YAML dict.
        del config['input']['dict'][2]
        galsim.config.ProcessInput(config)
        test_yaml = False

    # Test direct values
    val1 = galsim.config.ParseValue(config, 'val1', config, float)[0]
    np.testing.assert_almost_equal(val1, 9.9)

    val2 = galsim.config.ParseValue(config, 'val2', config, float)[0]
    np.testing.assert_almost_equal(val2, 400)

    # Test conversions from strings
    str1 = galsim.config.ParseValue(config, 'str1', config, float)[0]
    np.testing.assert_almost_equal(str1, 8.73)

    str2 = galsim.config.ParseValue(config, 'str2', config, float)[0]
    np.testing.assert_almost_equal(str2, 2.33e-9)

    str3 = galsim.config.ParseValue(config, 'str3', config, float)[0]
    np.testing.assert_almost_equal(str3, 6.0e-9)

    # Test values read from a Catalog
    cat1 = []
    cat2 = []
    cat3 = []
    cat4 = []
    config['index_key'] = 'file_num'
    for k in range(5):
        config['file_num'] = k
        cat1.append(galsim.config.ParseValue(config, 'cat1', config, float)[0])
        cat2.append(galsim.config.ParseValue(config, 'cat2', config, float)[0])
        cat3.append(galsim.config.ParseValue(config, 'cat3', config, float)[0])
        cat4.append(galsim.config.ParseValue(config, 'cat4', config, float)[0])

    np.testing.assert_array_almost_equal(cat1,
                                         [1.234, 2.345, 3.456, 1.234, 2.345])
    np.testing.assert_array_almost_equal(cat2,
                                         [4.131, -900, 8000, 4.131, -900])
    np.testing.assert_array_almost_equal(cat3,
                                         [1.234, 2.345, 3.456, 1.234, 2.345])
    np.testing.assert_array_almost_equal(cat4,
                                         [4.131, -900, 8000, 4.131, -900])

    # Test values generated from a uniform deviate
    rng = galsim.UniformDeviate(1234)
    config['rng'] = galsim.UniformDeviate(
        1234)  # A second copy starting with the same seed.
    for k in range(6):
        config[
            'obj_num'] = k  # The Random type doesn't use obj_num, but this keeps it
        # from thinking current_val is still current.
        ran1 = galsim.config.ParseValue(config, 'ran1', config, float)[0]
        np.testing.assert_almost_equal(ran1, rng() * 2.5 + 0.5)

        ran2 = galsim.config.ParseValue(config, 'ran2', config, float)[0]
        np.testing.assert_almost_equal(ran2, rng() * 5 - 5)

    # Test values generated from a Gaussian deviate
    for k in range(6):
        config['obj_num'] = k
        gauss1 = galsim.config.ParseValue(config, 'gauss1', config, float)[0]
        gd = galsim.GaussianDeviate(rng, mean=0, sigma=1)
        np.testing.assert_almost_equal(gauss1, gd())

        gauss2 = galsim.config.ParseValue(config, 'gauss2', config, float)[0]
        gd = galsim.GaussianDeviate(rng, mean=4, sigma=3)
        np.testing.assert_almost_equal(gauss2, gd())

        gauss3 = galsim.config.ParseValue(config, 'gauss3', config, float)[0]
        gd = galsim.GaussianDeviate(rng, mean=0, sigma=1.5)
        gd_val = gd()
        while math.fabs(gd_val) > 2:
            gd_val = gd()
        np.testing.assert_almost_equal(gauss3, gd_val)

        gauss4 = galsim.config.ParseValue(config, 'gauss4', config, float)[0]
        gd = galsim.GaussianDeviate(rng, mean=0, sigma=0.5)
        gd_val = math.fabs(gd())
        while gd_val > 0.8:
            gd_val = math.fabs(gd())
        np.testing.assert_almost_equal(gauss4, gd_val)

        gauss5 = galsim.config.ParseValue(config, 'gauss5', config, float)[0]
        gd = galsim.GaussianDeviate(rng, mean=0.5, sigma=0.3)
        gd_val = gd()
        if gd_val > 0.5:
            gd_val = 1 - gd_val
        while gd_val < 0:
            gd_val = gd()
            if gd_val > 0.5:
                gd_val = 1 - gd_val
        np.testing.assert_almost_equal(gauss5, gd_val)

    # Test values generated from a distribution in a file
    dd = galsim.DistDeviate(rng,
                            function='config_input/distribution.txt',
                            interpolant='linear')
    for k in range(6):
        config['obj_num'] = k
        dist1 = galsim.config.ParseValue(config, 'dist1', config, float)[0]
        np.testing.assert_almost_equal(dist1, dd())
    dd = galsim.DistDeviate(rng,
                            function='config_input/distribution2.txt',
                            interpolant='linear')
    for k in range(6):
        config['obj_num'] = k
        dist2 = galsim.config.ParseValue(config, 'dist2', config, float)[0]
        np.testing.assert_almost_equal(dist2, dd())
    dd = galsim.DistDeviate(rng, function=lambda x: x * x, x_min=0., x_max=2.)
    for k in range(6):
        config['obj_num'] = k
        dist3 = galsim.config.ParseValue(config, 'dist3', config, float)[0]
        np.testing.assert_almost_equal(dist3, dd())

    # Test values generated from various other deviates
    for k in range(6):
        config['obj_num'] = k
        dev = galsim.PoissonDeviate(rng, mean=137)
        dev1 = galsim.config.ParseValue(config, 'dev1', config, float)[0]
        np.testing.assert_almost_equal(dev1, dev())

        dev = galsim.BinomialDeviate(rng, N=17)
        dev2 = galsim.config.ParseValue(config, 'dev2', config, float)[0]
        np.testing.assert_almost_equal(dev2, dev())

        dev = galsim.BinomialDeviate(rng, N=17, p=0.2)
        dev3 = galsim.config.ParseValue(config, 'dev3', config, float)[0]
        np.testing.assert_almost_equal(dev3, dev())

        dev = galsim.WeibullDeviate(rng, a=1.7, b=4.3)
        dev4 = galsim.config.ParseValue(config, 'dev4', config, float)[0]
        np.testing.assert_almost_equal(dev4, dev())

        dev = galsim.GammaDeviate(rng, k=1, theta=4)
        dev5 = galsim.config.ParseValue(config, 'dev5', config, float)[0]
        np.testing.assert_almost_equal(dev5, dev())

        dev = galsim.GammaDeviate(rng, k=1.9, theta=4.1)
        dev6 = galsim.config.ParseValue(config, 'dev6', config, float)[0]
        np.testing.assert_almost_equal(dev6, dev())

        dev = galsim.Chi2Deviate(rng, n=17)
        dev7 = galsim.config.ParseValue(config, 'dev7', config, float)[0]
        np.testing.assert_almost_equal(dev7, dev())

    # Test values generated from a Sequence
    seq1 = []
    seq2 = []
    seq3 = []
    seq4 = []
    seq5 = []
    config['index_key'] = 'file_num'
    for k in range(6):
        config['file_num'] = k
        seq1.append(galsim.config.ParseValue(config, 'seq1', config, float)[0])
    config['index_key'] = 'image_num'
    for k in range(6):
        config['image_num'] = k
        seq2.append(galsim.config.ParseValue(config, 'seq2', config, float)[0])
    config['index_key'] = 'obj_num'
    for k in range(6):
        config['obj_num'] = k
        seq3.append(galsim.config.ParseValue(config, 'seq3', config, float)[0])
    config['index_key'] = 'obj_num_in_file'
    config['start_obj_num'] = 10
    for k in range(6):
        config['obj_num'] = k + 10
        seq4.append(galsim.config.ParseValue(config, 'seq4', config, float)[0])
        seq5.append(galsim.config.ParseValue(config, 'seq5', config, float)[0])

    np.testing.assert_array_almost_equal(seq1, [0, 1, 2, 3, 4, 5])
    np.testing.assert_array_almost_equal(seq2, [0, 0.1, 0.2, 0.3, 0.4, 0.5])
    np.testing.assert_array_almost_equal(seq3, [1.5, 2, 2.5, 3, 3.5, 4])
    np.testing.assert_array_almost_equal(seq4, [10, 8, 6, 4, 2, 0])
    np.testing.assert_array_almost_equal(seq5, [1, 1, 2, 2, 1, 1])

    # Test values taken from a List
    list1 = []
    list2 = []
    config['index_key'] = 'obj_num'
    for k in range(5):
        config['obj_num'] = k
        list1.append(
            galsim.config.ParseValue(config, 'list1', config, float)[0])
        list2.append(
            galsim.config.ParseValue(config, 'list2', config, float)[0])

    np.testing.assert_array_almost_equal(list1, [73, 8.9, 3.14, 73, 8.9])
    np.testing.assert_array_almost_equal(list2, [10.8, 7.0, 4.3, 1.8, 10.8])

    # Test values read from a Dict
    dict = []
    dict.append(galsim.config.ParseValue(config, 'dict1', config, float)[0])
    dict.append(galsim.config.ParseValue(config, 'dict2', config, float)[0])
    if test_yaml:
        dict.append(
            galsim.config.ParseValue(config, 'dict3', config, float)[0])
        dict.append(
            galsim.config.ParseValue(config, 'dict4', config, float)[0])
    else:
        dict.append(0.1)
        dict.append(1.9)
    np.testing.assert_array_almost_equal(dict, [23.17, -17.23, 0.1, 1.9])

    sum1 = galsim.config.ParseValue(config, 'sum1', config, float)[0]
    np.testing.assert_almost_equal(sum1, 72 + 2.33 + 23.17)

    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
示例#12
0
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]

    redshift_dist = galsim.DistDeviate(ud,
                                       function=lambda x: x**-2,
                                       x_min=0.5,
                                       x_max=1.5)
    gal_z = redshift_dist()

    # 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 * 10
    gal.magnify(3)

    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.5)
    final = galsim.Convolve([gauss_psf, gal])

    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,scale=0.206)
    #stamp = final.drawImage(image=this_stamp_image, offset=offset, method='no_pixel')
    stamp = final.drawImage(wcs=wcs.local(image_pos))
    stamp.setCenter(image_pos.x, image_pos.y)
    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.x = image_pos.x
    galaxy_truth.y = image_pos.y
    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')
    #pdb.set_trace()
    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
示例#13
0
def atmosPSFParams(t_exp=20, diam=6.7, rng=None, N=1):
    """A subroutine to generate random parameters for the atmospheric PSF.

    This routine generates a random value of seeing from some distribution, and random values for
    the two parameters (A, theta_0) that determine the correlation function of PSF anisotropies:

        xi_+(theta) = A / (1 + theta/theta_0)

    These values are then rescaled from the fiducial values of exposure time t_exp=20s and
    telescope diameter diam=6.7m to the input values of t_exp and diam, where xi is proportional
    to 1/t_exp and 1/diam.

    The inputs are exposure time in seconds and telescope diameter in meters.  Optionally, the user
    can input a galsim.BaseDeviate (rng) to use as the pseudo-random number generator, and can use
    the keyword N to instruct the routine to return more than one set of parameters (in which case
    the return values are NumPy arrays instead of floats).

    The numbers that are returned are A (dimensionless), theta_0 (arcsec), and seeing FWHM (arcsec),
    either as three individual floats (if N=1) or as three NumPy arrays of length N (if N>1).
    """
    # Check for sane ranges of exposure time and diameter.
    if t_exp < 0:
        raise ValueError("Invalid input for exposure time!")
    if diam < 0:
        raise ValueError("Invalid input for telescope diameter!")
    if N <= 0:
        raise ValueError("Invalid input for N!")

    # Check for input RNG or make a new one.
    if rng is None:
        uniform_deviate = galsim.UniformDeviate()
    elif isinstance(rng, galsim.BaseDeviate):
        # If it's a BaseDeviate, we can convert to UniformDeviate
        uniform_deviate = galsim.UniformDeviate(rng)
    else:
        raise TypeError("The rng provided to atmosPSFParams is not a BaseDeviate")

    # Distribution for average seeing comes from the distribution for Mauna Kea, as read off Fig 1
    # in
    # http://www2.keck.hawaii.edu/optics/ScienceCase/TechSciInstrmnts/Products_SeeingVarMaunaKea.pdf
    # (R band, at zenith).  We use the distribution for the central part of the night which is an
    # intermediate case compared to the others.  Currently there is no option for correction for
    # observations that are not at zenith or not in R band.
    fwhm_arcsec = 0.05 + 0.10*np.arange(17)
    freq = (0., 0., 0., 7.5, 19., 20., 17., 13., 9., 5., 3.5, 2., 1., 1., 0.5, 0.0, 0.0)
    dd = galsim.DistDeviate(uniform_deviate, 
                            function=galsim.LookupTable(fwhm_arcsec, freq, interpolant='linear'))
    if N > 1:
        rand_seeing = np.zeros(N)
        for i in range(N):
            rand_seeing[i] = dd()
    else:
        rand_seeing = dd()

    # Now work on the PSF anisotropies:
    # For A, take a range based on ImSim for 20s exposures: 1e-4 to 8e-4 (could debate about what
    # distribution to use, but for now, flat isn't too bad).  This has to be rescaled according to
    # the exposure time and diameter.
    min_A = 1.e-4
    max_A = 8.e-4
    if N > 1:
        rand_A = np.zeros(N)
        for i in range(N):
            rand_A[i] = min_A + (max_A-min_A)*uniform_deviate()
    else:
        rand_A = min_A + (max_A-min_A)*uniform_deviate()
    rand_A *= (20./t_exp)*(6.7/diam)

    # For theta_0, take a range based on ImSim, from 0.1-2 degree (again, flat distribution).
    min_theta_0 = 0.1
    max_theta_0 = 2.0
    if N > 1:
        rand_theta_0 = np.zeros(N)
        for i in range(N):
            rand_theta_0[i] = min_theta_0 + (max_theta_0-min_theta_0)*uniform_deviate()
    else:
        rand_theta_0 = min_theta_0 + (max_theta_0-min_theta_0)*uniform_deviate()
    rand_theta_0 *= 3600. # need arcsec

    return rand_A, rand_theta_0, rand_seeing
示例#14
0
def make_a_galaxy(ud, wcs, psf, affine):
    """
    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 = center_dec + (ud() - 0.5) * image_ysize_arcsec * galsim.arcsec
    ra = center_ra + (
        ud() - 0.5) * 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,
    # since the PowerSpectrum class is really defined on that plane, not in (ra,dec).
    # This is still an x/y corrdinate
    uv_pos = affine.toWorld(image_pos)

    # Draw the redshift from a power law distribution: N(f) ~ f^-2
    # TAKEN FROM DEMO9.PY
    redshift_dist = galsim.DistDeviate(ud,
                                       function=lambda x: x**-2,
                                       x_min=0.5,
                                       x_max=1.5)
    gal_z = redshift_dist()

    # Get the reduced shears and magnification at this point
    nfw_shear, mu = nfw_lensing(nfw, uv_pos, gal_z)
    g1 = nfw_shear.g1
    g2 = nfw_shear.g2

    # Create chromatic galaxy
    bp_dir = '/Users/jemcclea/Research/GalSim/examples/data'
    bp_file = os.path.join(bp_dir, 'lum_throughput.csv')
    bandpass = galsim.Bandpass(bp_file,
                               wave_type='nm',
                               blue_limit=310,
                               red_limit=1100)
    gal = cosmos_cat.makeGalaxy(gal_type='parametric', rng=ud, chromatic=True)
    logger.debug('created chromatic galaxy')

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

    # Apply the cosmological (reduced) shear and magnification at this position using a single
    # GSObject method.
    try:
        gal = gal.lens(g1, g2, mu)
        logger.debug('sheared galaxy')
    except:
        print("could not lens galaxy, setting default values...")
        g1 = 0.0
        g2 = 0.0
        mu = 1.0

    # This automatically scales up the noise variance by flux_scaling**2.
    gal *= flux_scaling
    logger.debug('rescaled galaxy with scaling factor %f' % flux_scaling)

    # Generate PSF at location of galaxy
    # Convolve galaxy image with the PSF.
    this_psf = psf.getPSF(image_pos)
    #final_psf=galsim.Convolve(this_psf,optics)
    gsp = galsim.GSParams(maximum_fft_size=16384)
    final = galsim.Convolve([this_psf, gal], gsparams=gsp)
    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, scale=2.06)
    stamp = final.drawImage(bandpass, image=this_stamp_image,
                            offset=offset)  #, method='no_pixel')
    stamp.setCenter(ix_nominal, iy_nominal)
    logger.debug('drew & centered galaxy!')

    new_variance = 0.0

    try:
        new_variance = stamp.whitenNoise(final.noise)
        logger.debug("whitened stamp noise")
    except:
        logger.debug("parametric gal; no noise to whiten")

    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
    galaxy_truth.variance = new_variance
    logger.debug('created truth values')

    try:
        galaxy_truth.fwhm = final.calculateFWHM()
        galaxy_truth.mom_size = stamp.FindAdaptiveMom().moments_sigma
    except:
        logger.debug('fwhm or sigma calculation failed')
        galaxy_truth.fwhm = -9999.0
        galaxy_truth.mom_size = -9999.

    return stamp, galaxy_truth
示例#15
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
示例#16
0
def make_a_star(ud,wcs=None,psf=None,affine=None):
    #star_flux = 1.e5    # total counts on the image
    #star_sigma = 2.     # arcsec
    #psf_sigma = 1.     # arcsec
    logger.debug('entered make a star method...')

    # Choose a random RA, Dec around the sky_center.
    dec = center_dec + (ud()-0.5) * image_ysize_arcsec * galsim.arcsec
    ra = center_ra + (ud()-0.5) * 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)
    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 = 968964.0)
    star_flux = flux_dist()
    shining_star = galsim.Airy(lam=lam, obscuration=0.3840, diam=tel_diam, scale_unit=galsim.arcsec,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(128, 128,wcs=wcs.local(image_pos))
    star_stamp = star.drawImage(wcs=wcs.local(image_pos),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
示例#17
0
def main(argv):
    """
    Make images using constant PSF and variable shear:
      - The main image is 0.2 x 0.2 degrees.
      - Pixel scale is 0.2 arcsec, hence the image is 3600 x 3600 pixels.
      - Applied shear is from a cosmological power spectrum read in from file.
      - The PSF is a real one from SDSS, and corresponds to a convolution of atmospheric PSF,
        optical PSF, and pixel response, which has been sampled at pixel centers.  We used a PSF
        from SDSS in order to have a PSF profile that could correspond to what you see with a real
        telescope. However, in order that the galaxy resolution not be too poor, we tell GalSim that
        the pixel scale for that PSF image is 0.2" rather than 0.396".  We are simultaneously lying
        about the intrinsic size of the PSF and about the pixel scale when we do this.
      - Noise is correlated with the same spatial correlation function as found in HST COSMOS weak
        lensing science images, with a point (zero distance) variance that we normalize to 1.e4.
      - Galaxies are real galaxies, each with S/N~100 based on a point variance-only calculation
        (such as discussed in Leauthaud et al 2007).  The true SNR is somewhat lower, due to the
        presence of correlation in the noise.

    """
    logging.basicConfig(format="%(message)s",
                        level=logging.INFO,
                        stream=sys.stdout)
    logger = logging.getLogger("demo11")

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

    stamp_size = 100  # number of pixels in each dimension of galaxy images
    pixel_scale = 0.2  # arcsec/pixel
    image_size = 0.2 * galsim.degrees  # size of big image in each dimension
    image_size = int(
        (image_size / galsim.arcsec) / pixel_scale)  # convert to pixels
    image_size_arcsec = image_size * pixel_scale  # size of big image in each dimension (arcsec)
    noise_variance = 1.e4  # ADU^2
    nobj = 288  # number of galaxies in entire field
    # (This corresponds to 2 galaxies / arcmin^2)
    grid_spacing = 90.0  # The spacing between the samples for the power spectrum
    # realization (arcsec)
    gal_signal_to_noise = 100  # S/N of each galaxy

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

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

    logger.info('Starting demo script 11')

    # Read in galaxy catalog
    cat_file_name = 'real_galaxy_catalog_example.fits'
    # This script is designed to be run from the examples directory so dir is a relative path.
    # But the '../examples/' part lets bin/demo11 also be run from the bin directory.
    dir = '../examples/data'
    real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=dir)
    real_galaxy_catalog.preload()
    logger.info('Read in %d real galaxies from catalog',
                real_galaxy_catalog.nobjects)

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

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

    # Setup the PowerSpectrum object we'll be using:
    # To do this, we first have to read in the tabulated power spectrum.
    # We use a tabulated power spectrum from iCosmo (http://icosmo.org), with the following
    # cosmological parameters and survey design:
    # H_0 = 70 km/s/Mpc
    # Omega_m = 0.25
    # Omega_Lambda = 0.75
    # w_0 = -1.0
    # w_a = 0.0
    # n_s = 0.96
    # sigma_8 = 0.8
    # Smith et al. prescription for the non-linear power spectrum.
    # Eisenstein & Hu transfer function with wiggles.
    # Default dN/dz with z_med = 1.0
    # The file has, as required, just two columns which are k and P(k).  However, iCosmo works in
    # terms of ell and C_ell; ell is inverse radians and C_ell in radians^2.  Since GalSim tends to
    # work in terms of arcsec, we have to tell it that the inputs are radians^-1 so it can convert
    # to store in terms of arcsec^-1.
    pk_file = os.path.join('..', 'examples', 'data', 'cosmo-fid.zmed1.00.out')
    ps = galsim.PowerSpectrum(pk_file, units=galsim.radians)
    # The argument here is "e_power_function" which defines the E-mode power to use.
    logger.info('Set up power spectrum from tabulated P(k)')

    # Now let's read in the PSF.  It's a real SDSS PSF, which means pixel scale of 0.396".  However,
    # the typical seeing is 1.2" and we want to simulate better seeing, so we will just tell GalSim
    # that the pixel scale is 0.2".  We have to be careful with SDSS PSF images, as they have an
    # added 'soft bias' of 1000 which has been removed before creation of this file, so that the sky
    # level is properly zero.  Also, the file is bzipped, to demonstrate the new capability of
    # reading in a file that has been compressed in various ways (which GalSim can infer from the
    # filename).  We want to read the image directly into an InterpolatedImage GSObject, so we can
    # manipulate it as needed (here, the only manipulation needed is convolution).  We want a PSF
    # with flux 1, and we can set the pixel scale using a keyword.
    psf_file = os.path.join('..', 'examples', 'data',
                            'example_sdss_psf_sky0.fits.bz2')
    psf = galsim.InterpolatedImage(psf_file, dx=pixel_scale, flux=1.)
    # We do not include a pixel response function galsim.Pixel here, because the image that was read
    # in from file already included it.
    logger.info('Read in PSF image from bzipped FITS file')

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

    # The default convention for indexing an image is to follow the FITS standard where the
    # lower-left pixel is called (1,1).  However, this can be counter-intuitive to people more
    # used to C or python indexing, where indices start at 0.  It is possible to change the
    # coordinates of the lower-left pixel with the methods `setOrigin`.  For this demo, we
    # switch to 0-based indexing, so the lower-left pixel will be called (0,0).
    full_image.setOrigin(0, 0)

    # Get the center of the image in arcsec
    center = full_image.bounds.trueCenter() * pixel_scale

    # As for demo10, we use random_seed+nobj for the random numbers required for the
    # whole image.  In this case, both the power spectrum realization and the noise on the
    # full image we apply later.
    rng = galsim.BaseDeviate(random_seed + nobj)
    # We want to make random positions within our image.  However, currently for shears from a power
    # spectrum we first have to get shears on a grid of positions, and then we can choose random
    # positions within that.  So, let's make the grid.  We're going to make it as large as the
    # image, with grid points spaced by 90 arcsec (hence interpolation only happens below 90"
    # scales, below the interesting scales on which we want the shear power spectrum to be
    # represented exactly).  Lensing engine wants positions in arcsec, so calculate that:
    ps.buildGrid(grid_spacing=grid_spacing,
                 ngrid=int(image_size_arcsec / grid_spacing) + 1,
                 center=center,
                 rng=rng)
    logger.info('Made gridded shears')

    # Now we need to loop over our objects:
    for k in range(nobj):
        time1 = time.time()
        # The usual random number generator using a different seed for each galaxy.
        ud = galsim.UniformDeviate(random_seed + k)

        # Choose a random position in the image
        x = ud() * (image_size - 1)
        y = ud() * (image_size - 1)

        # Turn this into a position in arcsec
        pos = galsim.PositionD(x, y) * pixel_scale

        # Get the reduced shears and magnification at this point
        g1, g2, mu = ps.getLensing(pos=pos)

        # Construct the galaxy:
        # Select randomly from among our list of galaxies.
        index = int(ud() * len(gal_list))
        gal = gal_list[index]

        # Draw the size from a plausible size distribution: N(r) ~ r^-3.5
        # For this, we use the class DistDeviate which can draw deviates from an arbitrary
        # probability distribution.  This distribution can be defined either as a functional
        # form as we do here, or as tabulated lists of x and p values, from which the
        # function is interpolated.
        distdev = galsim.DistDeviate(ud,
                                     function=lambda x: x**-3.5,
                                     x_min=1,
                                     x_max=5)
        dilat = distdev()
        # Use createDilated rather than applyDilation, so we don't change the galaxies in the
        # original gal_list -- createDilated makes a new copy.
        gal = gal.createDilated(dilat)

        # Apply a random rotation
        theta = ud() * 2.0 * numpy.pi * galsim.radians
        gal.applyRotation(theta)

        # Apply the cosmological (reduced) shear and magnification at this position using a single
        # GSObject method.
        gal.applyLensing(g1, g2, mu)

        # Convolve with the PSF.  We don't have to include a pixel response explicitly, since the
        # SDSS PSF image that we are using included the pixel response already.
        final = galsim.Convolve(psf, gal)

        # Account for the fractional part of the position:
        x_nom = x + 0.5  # Because stamp size is even!  See discussion in demo9.py
        y_nom = y + 0.5
        ix_nom = int(math.floor(x_nom + 0.5))
        iy_nom = int(math.floor(y_nom + 0.5))
        offset = galsim.PositionD(x_nom - ix_nom, y_nom - iy_nom)

        # Draw it with our desired stamp size
        stamp = galsim.ImageF(stamp_size, stamp_size)
        final.draw(image=stamp, dx=pixel_scale, offset=offset)

        # Rescale flux to get the S/N we want.  We have to do that before we add it to the big
        # image, which might have another galaxy near that point (so our S/N calculation would
        # erroneously include the flux from the other object).
        # See demo5.py for the math behind this calculation.
        sn_meas = math.sqrt(numpy.sum(stamp.array**2) / noise_variance)
        flux_scaling = gal_signal_to_noise / sn_meas
        stamp *= flux_scaling

        # Recenter the stamp at the desired position:
        stamp.setCenter(ix_nom, iy_nom)

        # Find the overlapping bounds:
        bounds = stamp.bounds & full_image.bounds
        full_image[bounds] += stamp[bounds]

        time2 = time.time()
        tot_time = time2 - time1
        logger.info('Galaxy %d: position relative to corner = %s, t=%f s', k,
                    str(pos), tot_time)

    # Add correlated noise to the image -- the correlation function comes from the HST COSMOS images
    # and is described in more detail in the galsim.correlatednoise.getCOSMOSNoise() docstring.
    # This function requires a FITS file, stored in the GalSim repository, that represents this
    # correlation information: the path to this file is a required argument.
    cf_file_name = os.path.join('..', 'examples', 'data',
                                'acs_I_unrot_sci_20_cf.fits')

    # Then use this to initialize the correlation function that we will use to add noise to the
    # full_image.  We set the dx_cosmos keyword equal to our pixel scale, so that the noise among
    # neighboring pixels is correlated at the same level as it was among neighboring pixels in HST
    # COSMOS.  Using the original pixel scale, dx_cosmos=0.03 [arcsec], would leave very little
    # correlation among our larger 0.2 arcsec pixels. We also set the point (zero-distance) variance
    # to our desired value.
    cn = galsim.correlatednoise.getCOSMOSNoise(rng,
                                               cf_file_name,
                                               dx_cosmos=pixel_scale,
                                               variance=noise_variance)

    # Now add noise according to this correlation function to the full_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.
    full_image.addNoise(
        cn)  # Note image must have the right scale, as it does here.
    logger.info('Added noise to final large image')

    # Now write the image to disk.  It is automatically compressed with Rice compression,
    # since the filename we provide ends in .fz.
    full_image.write(file_name)
    logger.info('Wrote image to %r', file_name)
示例#18
0
    def build_file(seed, file_name, mass, nobj, rng, truth_file_name, halo_id,
                   first_obj_id):
        """A function that does all the work to build a single file.
           Returns the total time taken.
        """
        t1 = time.time()

        # Build the image onto which we will draw the galaxies.
        full_image = galsim.ImageF(image_size, image_size)

        # The "true" center of the image is allowed to be halfway between two pixels, as is the
        # case for even-sized images.  full_image.bounds.center() is an integer position,
        # which would be 1/2 pixel up and to the right of the true center in this case.
        im_center = full_image.bounds.trueCenter()

        # For the WCS, this time we use UVFunction, which lets you define arbitrary u(x,y)
        # and v(x,y) functions.  We use a simple cubic radial function to create a
        # pincushion distortion.  This is a typical kind of telescope distortion, although
        # we exaggerate the magnitude of the effect to make it more apparent.
        # The pixel size in the center of the image is 0.05, but near the corners (r=362),
        # the pixel size is approximately 0.075, which is much more distortion than is
        # normally present in typical telescopes.  But it makes the effect of the variable
        # pixel area obvious when you look at the weight image in the output files.
        ufunc1 = lambda x, y: 0.05 * x * (1. + 2.e-6 * (x**2 + y**2))
        vfunc1 = lambda x, y: 0.05 * y * (1. + 2.e-6 * (x**2 + y**2))

        # It's not required to provide the inverse functions.  However, if we don't, then
        # you will only be able to do toWorld operations, not the inverse toImage.
        # The inverse function does not have to be exact either.  For example, you could provide
        # a function that does some kind of iterative solution to whatever accuracy you care
        # about.  But in this case, we can do the exact inverse.
        #
        # Let w = sqrt(u**2 + v**2) and r = sqrt(x**2 + y**2).  Then the solutions are:
        # x = (u/w) r and y = (u/w) r, and we use Cardano's method to solve for r given w:
        # See http://en.wikipedia.org/wiki/Cubic_function#Cardano.27s_method
        #
        # w = 0.05 r + 2.e-6 * 0.05 * r**3
        # r = 100 * ( ( 5 sqrt(w**2 + 5.e3/27) + 5 w )**(1./3.) -
        #           - ( 5 sqrt(w**2 + 5.e3/27) - 5 w )**(1./3.) )

        def xfunc1(u, v):
            import math
            wsq = u * u + v * v
            if wsq == 0.:
                return 0.
            else:
                w = math.sqrt(wsq)
                temp = 5. * math.sqrt(wsq + 5.e3 / 27)
                r = 100. * ((temp + 5 * w)**(1. / 3.) -
                            (temp - 5 * w)**(1. / 3))
                return u * r / w

        def yfunc1(u, v):
            import math
            wsq = u * u + v * v
            if wsq == 0.:
                return 0.
            else:
                w = math.sqrt(wsq)
                temp = 5. * math.sqrt(wsq + 5.e3 / 27)
                r = 100. * ((temp + 5 * w)**(1. / 3.) -
                            (temp - 5 * w)**(1. / 3))
                return v * r / w

        # You could pass the above functions to UVFunction, and normally we would do that.
        # The only down side to doing so is that the specification of the WCS in the FITS
        # file is rather ugly.  GalSim is able to turn the python byte code into strings,
        # but they are basically a really ugly mess of random-looking characters.  GalSim
        # will be able to read it back in, but human readers will have no idea what WCS
        # function was used.  To see what they look like, uncomment this line and comment
        # out the later wcs line.
        #wcs = galsim.UVFunction(ufunc1, vfunc1, xfunc1, yfunc1, origin=im_center)

        # If you provide the functions as strings, then those strings will be preserved
        # in the FITS header in a form that is more legible to human readers.
        # It also has the extra benefit of matching the output from demo9.yaml, which we
        # always try to do.  The config file has no choice but to specify the functions
        # as strings.

        ufunc = '0.05 * x * (1. + 2.e-6 * (x**2 + y**2))'
        vfunc = '0.05 * y * (1. + 2.e-6 * (x**2 + y**2))'
        xfunc = (
            '( lambda w: ( 0 if w==0 else ' +
            '100.*u/w*(( 5*(w**2 + 5.e3/27.)**0.5 + 5*w )**(1./3.) - ' +
            '( 5*(w**2 + 5.e3/27.)**0.5 - 5*w )**(1./3.))))( (u**2+v**2)**0.5 )'
        )
        yfunc = (
            '( lambda w: ( 0 if w==0 else ' +
            '100.*v/w*(( 5*(w**2 + 5.e3/27.)**0.5 + 5*w )**(1./3.) - ' +
            '( 5*(w**2 + 5.e3/27.)**0.5 - 5*w )**(1./3.))))( (u**2+v**2)**0.5 )'
        )

        # The origin parameter defines where on the image should be considered (x,y) = (0,0)
        # in the WCS functions.
        wcs = galsim.UVFunction(ufunc, vfunc, xfunc, yfunc, origin=im_center)

        # Assign this wcs to full_image
        full_image.wcs = wcs

        # The weight image will hold the inverse variance for each pixel.
        # We can set the wcs directly on construction with the wcs parameter.
        weight_image = galsim.ImageF(image_size, image_size, wcs=wcs)

        # It is common for astrometric images to also have a bad pixel mask.  We don't have any
        # defect simulation currently, so our bad pixel masks are currently all zeros.
        # But someday, we plan to add defect functionality to GalSim, at which point, we'll
        # be able to mark those defects on a bad pixel mask.
        # Note: the S in ImageS means to use "short int" for the data type.
        # This is a typical choice for a bad pixel image.
        badpix_image = galsim.ImageS(image_size, image_size, wcs=wcs)

        # We also draw a PSF image at the location of every galaxy.  This isn't normally done,
        # and since some of the PSFs overlap, it's not necessarily so useful to have this kind
        # of image.  But in this case, it's fun to look at the psf image, especially with
        # something like log scaling in ds9 to see how crazy an aberrated OpticalPSF with
        # struts can look when there is no atmospheric component to blur it out.
        psf_image = galsim.ImageF(image_size, image_size, wcs=wcs)

        # We will also write some truth information to an output catalog.
        # In real simulations, it is often useful to have a catalog of the truth values
        # to compare to measurements either directly or as cuts on the galaxy sample to
        # find where systematic errors are largest.
        # For now, we just make an empty OutputCatalog object with the names and types of the
        # columns.
        names = [
            'object_id', 'halo_id', 'flux', 'radius', 'h_over_r',
            'inclination.rad', 'theta.rad', 'mu', 'redshift', 'shear.g1',
            'shear.g2', 'pos.x', 'pos.y', 'image_pos.x', 'image_pos.y',
            'halo_mass', 'halo_conc', 'halo_redshift'
        ]
        types = [
            int, int, float, float, float, float, float, float, float, float,
            float, float, float, float, float, float, float, float
        ]
        truth_cat = galsim.OutputCatalog(names, types)

        # Setup the NFWHalo stuff:
        nfw = galsim.NFWHalo(mass=mass,
                             conc=nfw_conc,
                             redshift=nfw_z_halo,
                             omega_m=omega_m,
                             omega_lam=omega_lam)
        # Note: the last two are optional.  If they are omitted, then (omega_m=0.3, omega_lam=0.7)
        # are actually the defaults.  If you only specify one of them, the other is set so that
        # the total is 1.  But you can define both values so that the total is not 1 if you want.
        # Radiation is assumed to be zero and dark energy equation of state w = -1.
        # If you want to include either radiation or more complicated dark energy models,
        # you can define your own cosmology class that defines the functions a(z), E(a), and
        # Da(z_source, z_lens).  Then you can pass this to NFWHalo as a `cosmo` parameter.

        # Make the PSF profile outside the loop to minimize the (significant) OpticalPSF
        # construction overhead.
        psf = galsim.OpticalPSF(lam=psf_lam,
                                diam=psf_D,
                                obscuration=psf_obsc,
                                nstruts=psf_nstruts,
                                strut_thick=psf_strut_thick,
                                strut_angle=psf_strut_angle,
                                defocus=psf_defocus,
                                astig1=psf_astig1,
                                astig2=psf_astig2,
                                coma1=psf_coma1,
                                coma2=psf_coma2,
                                trefoil1=psf_trefoil1,
                                trefoil2=psf_trefoil2)

        for k in range(nobj):

            # Initialize the random number generator we will be using for this object:
            ud = galsim.UniformDeviate(seed + k + 1)

            # Determine where this object is going to go.
            # We choose points randomly within a donut centered at the center of the main image
            # in order to avoid placing galaxies too close to the halo center where the lensing
            # is not weak.  We use an inner radius of 3 arcsec and an outer radius of 12 arcsec,
            # which takes us essentially to the edge of the image.
            radius = 12
            inner_radius = 3
            max_rsq = radius**2
            min_rsq = inner_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 >= min_rsq and rsq <= max_rsq: break
            pos = galsim.PositionD(x, y)

            # We also need the position in pixels to determine where to place the postage
            # stamp on the full image.
            image_pos = wcs.toImage(pos)

            # For even-sized postage stamps, the nominal center (returned by stamp.bounds.center())
            # cannot be at the true center (returned by stamp.bounds.trueCenter()) of the postage
            # stamp, since the nominal center values have to be integers.  Thus, the nominal center
            # is 1/2 pixel up and to the right of the true center.
            # If we used odd-sized postage stamps, we wouldn't need to do this.
            x_nominal = image_pos.x + 0.5
            y_nominal = image_pos.y + 0.5

            # Get the integer values of these which will be the actual nominal center of the
            # postage stamp image.
            ix_nominal = int(math.floor(x_nominal + 0.5))
            iy_nominal = int(math.floor(y_nominal + 0.5))

            # The remainder will be accounted for in an offset when we draw.
            dx = x_nominal - ix_nominal
            dy = y_nominal - iy_nominal
            offset = galsim.PositionD(dx, dy)

            # Draw the flux from a power law distribution: N(f) ~ f^-1.5
            # For this, we use the class DistDeviate which can draw deviates from an arbitrary
            # probability distribution.  This distribution can be defined either as a functional
            # form as we do here, or as tabulated lists of x and p values, from which the
            # function is interpolated.
            flux_dist = galsim.DistDeviate(ud,
                                           function=lambda x: x**-1.5,
                                           x_min=gal_flux_min,
                                           x_max=gal_flux_max)
            flux = flux_dist()

            # We introduce here another surface brightness profile, called InclinedExponential.
            # It represents a typical 3D galaxy disk profile inclined at an arbitrary angle
            # relative to face on.
            #
            #     inclination =  0 degrees corresponds to a face-on disk, which is equivalent to
            #                             the regular Exponential profile.
            #     inclination = 90 degrees corresponds to an edge-on disk.
            #
            # A random orientation corresponds to the inclination angle taking the probability
            # distribution:
            #
            #     P(inc) = 0.5 sin(inc)
            #
            # so we again use a DistDeviate to generate these values.
            inc_dist = galsim.DistDeviate(ud,
                                          function=lambda x: 0.5 * math.sin(x),
                                          x_min=0,
                                          x_max=math.pi)
            inclination = inc_dist() * galsim.radians

            # The parameters scale_radius and scale_height give the scale distances in the
            # 3D distribution:
            #
            #     I(R,z) = I_0 / (2 scale_height) * sech^2(z/scale_height) * exp(-r/scale_radius)
            #
            # These values can be given separately if desired.  However, it is often easier to
            # give the ratio scale_h_over_r as an independent value, since the radius and height
            # values are correlated, while h/r is approximately independent of h or r.
            h_over_r = ud() * (gal_h_over_r_max -
                               gal_h_over_r_min) + gal_h_over_r_min

            radius = ud() * (gal_r_max - gal_r_min) + gal_r_min

            # The inclination is around the x-axis, so we want to rotate the galaxy by a
            # random angle.
            theta = ud() * math.pi * 2. * galsim.radians

            # Make the galaxy profile with these values:
            gal = galsim.InclinedExponential(scale_radius=radius,
                                             scale_h_over_r=h_over_r,
                                             inclination=inclination,
                                             flux=flux)
            gal = gal.rotate(theta)

            # Now apply the appropriate lensing effects for this position from
            # the NFW halo mass.
            try:
                g1, g2 = nfw.getShear(pos, nfw_z_source)
                nfw_shear = galsim.Shear(g1=g1, g2=g2)
            except:
                # This shouldn't happen, since we exclude the inner 10 arcsec, but it's a
                # good idea to use the try/except block here anyway.
                import warnings
                warnings.warn(
                    "Warning: NFWHalo shear is invalid -- probably strong lensing!  "
                    + "Using shear = 0.")
                nfw_shear = galsim.Shear(g1=0, g2=0)

            nfw_mu = nfw.getMagnification(pos, nfw_z_source)
            if nfw_mu < 0:
                import warnings
                warnings.warn(
                    "Warning: mu < 0 means strong lensing!  Using mu=25.")
                nfw_mu = 25
            elif nfw_mu > 25:
                import warnings
                warnings.warn(
                    "Warning: mu > 25 means strong lensing!  Using mu=25.")
                nfw_mu = 25

            # Calculate the total shear to apply
            # Since shear addition is not commutative, it is worth pointing out that
            # the order is in the sense that the second shear is applied first, and then
            # the first shear.  i.e. The field shear is taken to be behind the cluster.
            # Kind of a cosmic shear contribution between the source and the cluster.
            # However, this is not quite the same thing as doing:
            #     gal.shear(field_shear).shear(nfw_shear)
            # since the shear addition ignores the rotation that would occur when doing the
            # above lines.  This is normally ok, because the rotation is not observable, but
            # it is worth keeping in mind.
            total_shear = nfw_shear + field_shear

            # Apply the magnification and shear to the galaxy
            gal = gal.magnify(nfw_mu)
            gal = gal.shear(total_shear)

            # Build the final object
            final = galsim.Convolve([psf, gal])

            # Draw the stamp image
            # To draw the image at a position other than the center of the image, you can
            # use the offset parameter, which applies an offset in pixels relative to the
            # center of the image.
            # We also need to provide the local wcs at the current position.
            local_wcs = wcs.local(image_pos)
            stamp = final.drawImage(wcs=local_wcs, offset=offset)

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

            # Find overlapping bounds
            bounds = stamp.bounds & full_image.bounds
            full_image[bounds] += stamp[bounds]

            # Also draw the PSF
            psf_stamp = galsim.ImageF(
                stamp.bounds)  # Use same bounds as galaxy stamp
            psf.drawImage(psf_stamp, wcs=local_wcs, offset=offset)
            psf_image[bounds] += psf_stamp[bounds]

            # Add the truth information for this object to the truth catalog
            row = ((first_obj_id + k), halo_id, flux, radius, h_over_r,
                   inclination.rad(), theta.rad(), nfw_mu, nfw_z_source,
                   total_shear.g1, total_shear.g2, pos.x, pos.y, image_pos.x,
                   image_pos.y, mass, nfw_conc, nfw_z_halo)
            truth_cat.addRow(row)

        # Add Poisson noise to the full image
        # Note: The normal calculation of Poission noise isn't quite correct right now.
        # The pixel area is variable, which means the amount of sky flux that enters each
        # pixel is also variable.  The wcs classes have a function `makeSkyImage` which
        # will fill an image with the correct amount of sky flux given the sky level
        # in units of ADU/arcsec^2.  We use the weight image as our work space for this.
        wcs.makeSkyImage(weight_image, sky_level)

        # Add this to the current full_image (temporarily).
        full_image += weight_image

        # Add Poisson noise, given the current full_image.
        # The config parser uses a different random number generator for file-level and
        # image-level values than for the individual objects.  This makes it easier to
        # parallelize the calculation if desired.  In fact, this is why we've been adding 1
        # to each seed value all along.  The seeds for the objects take the values
        # random_seed+1 .. random_seed+nobj.  The seed for the image is just random_seed,
        # which we built already (below) when we calculated how many objects need to
        # be in each file.  Use the same rng again here, since this is also at image scope.
        full_image.addNoise(galsim.PoissonNoise(rng))

        # Subtract the sky back off.
        full_image -= weight_image

        # The weight image is nominally the inverse variance of the pixel noise.  However, it is
        # common to exclude the Poisson noise from the objects themselves and only include the
        # noise from the sky photons.  The variance of the noise is just the sky level, which is
        # what is currently in the weight_image.  (If we wanted to include the variance from the
        # objects too, then we could use the full_image before we added the PoissonNoise to it.)
        # So all we need to do now is to invert the values in weight_image.
        weight_image.invertSelf()

        # Write the file to disk:
        galsim.fits.writeMulti(
            [full_image, badpix_image, weight_image, psf_image], file_name)

        # And write the truth catalog file
        truth_cat.write(truth_file_name)

        t2 = time.time()
        return t2 - t1
示例#19
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
示例#20
0
def make_a_galaxy(ud, wcs, psf, affine):
    """
    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 = center_dec + (ud() - 0.5) * image_ysize_arcsec * galsim.arcsec
    ra = center_ra + (
        ud() - 0.5) * 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,
    # since the PowerSpectrum class is really defined on that plane, not in (ra,dec).
    # This is still an x/y corrdinate
    uv_pos = affine.toWorld(image_pos)

    # Draw the redshift from a power law distribution: N(f) ~ f^-2
    # TAKEN FROM DEMO9.PY
    redshift_dist = galsim.DistDeviate(ud,
                                       function=lambda x: x**-2,
                                       x_min=0.3,
                                       x_max=1.0)
    gal_z = redshift_dist()

    # Get the reduced shears and magnification at this point
    nfw_shear, mu = nfw_lensing(nfw, uv_pos, gal_z)
    g1 = nfw_shear.g1
    g2 = nfw_shear.g2

    binom = galsim.BinomialDeviate(ud, N=1, p=0.6)
    real = binom()

    if real:
        # For real galaxies, we will want to whiten the noise in the image (below).
        gal = cosmos_cat.makeGalaxy(gal_type='real',
                                    rng=ud,
                                    noise_pad_size=14.3)
    else:
        gal = cosmos_cat.makeGalaxy(gal_type='parametric', rng=ud)

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

    # Rescale the flux to match our telescope configuration.
    # This automatically scales up the noise variance by flux_scaling**2.
    gal *= flux_scaling

    # Apply the cosmological (reduced) shear and magnification at this position using a single
    # GSObject method.
    try:
        gal = gal.lens(g1, g2, mu)
    except:
        print("could not lens galaxy, setting default values...")
        g1 = 0.0
        g2 = 0.0
        mu = 1.0

    # Generate PSF at location of galaxy
    # Convolve galaxy image with the PSF.
    this_psf = psf.getPSF(image_pos)
    #final_psf=galsim.Convolve(this_psf,optics)
    gsp = galsim.GSParams(maximum_fft_size=16384)
    final = galsim.Convolve([this_psf, gal], gsparams=gsp)

    # 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.
    stamp = final.drawImage(scale=2.06, offset=offset, method='no_pixel')

    # If desired, one can also draw the PSF and output its moments too, as:
    #  psf_stamp = psf.drawImage(wcs=wcs.local(image_pos), offset=offset, method='no_pixel')

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

    new_variance = 0.0

    if real:
        if True:
            # We use the symmetrizing option here.
            new_variance = stamp.symmetrizeNoise(final.noise, 8)
        else:
            # Here is how you would do it if you wanted to fully whiten the image.
            new_variance = stamp.whitenNoise(final.noise)

    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.variance = new_variance

    return stamp, galaxy_truth
示例#21
0
def main(argv):
    """
    Make images using constant PSF and variable shear:
      - The main image is 0.2 x 0.2 degrees.
      - Pixel scale is 0.2 arcsec, hence the image is 3600 x 3600 pixels.
      - Applied shear is from a cosmological power spectrum read in from file.
      - The PSF is a real one from SDSS, and corresponds to a convolution of atmospheric PSF,
        optical PSF, and pixel response, which has been sampled at pixel centers.  We used a PSF
        from SDSS in order to have a PSF profile that could correspond to what you see with a real
        telescope. However, in order that the galaxy resolution not be too poor, we tell GalSim that
        the pixel scale for that PSF image is 0.2" rather than 0.396".  We are simultaneously lying
        about the intrinsic size of the PSF and about the pixel scale when we do this.
      - The galaxy images include some initial correlated noise from the original HST observation.
        However, we whiten the noise of the final image so the final image has stationary 
        Gaussian noise, rather than correlated noise.
    """
    logging.basicConfig(format="%(message)s",
                        level=logging.INFO,
                        stream=sys.stdout)
    logger = logging.getLogger("demo11")

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

    base_stamp_size = 32  # number of pixels in each dimension of galaxy images
    # This will be scaled up according to the dilation.
    # Hence the "base_" prefix.

    pixel_scale = 0.2  # arcsec/pixel
    image_size = 0.2 * galsim.degrees  # size of big image in each dimension
    image_size = int(
        (image_size / galsim.arcsec) / pixel_scale)  # convert to pixels
    image_size_arcsec = image_size * pixel_scale  # size of big image in each dimension (arcsec)
    noise_variance = 1.e4  # ADU^2
    nobj = 288  # number of galaxies in entire field
    # (This corresponds to 2 galaxies / arcmin^2)
    grid_spacing = 90.0  # The spacing between the samples for the power spectrum
    # realization (arcsec)
    gal_signal_to_noise = 100  # S/N of each galaxy

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

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

    logger.info('Starting demo script 11')

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

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

    # We will cache the galaxies that we make in order to save some of the calculations that
    # happen on construction.  In particular, we don't want to recalculate the Fourier transforms
    # of the real galaxy images, so it's more efficient so make a store of RealGalaxy instances.
    # We start with them all = None, and fill them in as we make them.
    gal_list = [None] * len(id_list)

    # Setup the PowerSpectrum object we'll be using:
    # To do this, we first have to read in the tabulated shear power spectrum, often denoted
    # C_ell(ell), where ell has units of inverse angle and C_ell has units of angle^2.  However,
    # GalSim works in the flat-sky approximation, so we use this notation interchangeably with
    # P(k).  GalSim does not calculate shear power spectra for users, who must be able to provide
    # their own (or use the examples in the repository).
    #
    # Here we use a tabulated power spectrum from iCosmo (http://icosmo.org), with the following
    # cosmological parameters and survey design:
    # H_0 = 70 km/s/Mpc
    # Omega_m = 0.25
    # Omega_Lambda = 0.75
    # w_0 = -1.0
    # w_a = 0.0
    # n_s = 0.96
    # sigma_8 = 0.8
    # Smith et al. prescription for the non-linear power spectrum.
    # Eisenstein & Hu transfer function with wiggles.
    # Default dN/dz with z_med = 1.0
    # The file has, as required, just two columns which are k and P(k).  However, iCosmo works in
    # terms of ell and C_ell; ell is inverse radians and C_ell in radians^2.  Since GalSim tends to
    # work in terms of arcsec, we have to tell it that the inputs are radians^-1 so it can convert
    # to store in terms of arcsec^-1.
    pk_file = os.path.join('data', 'cosmo-fid.zmed1.00.out')
    ps = galsim.PowerSpectrum(pk_file, units=galsim.radians)
    # The argument here is "e_power_function" which defines the E-mode power to use.
    logger.info('Set up power spectrum from tabulated P(k)')

    # Now let's read in the PSF.  It's a real SDSS PSF, which means pixel scale of 0.396".  However,
    # the typical seeing is 1.2" and we want to simulate better seeing, so we will just tell GalSim
    # that the pixel scale is 0.2".  We have to be careful with SDSS PSF images, as they have an
    # added 'soft bias' of 1000 which has been removed before creation of this file, so that the sky
    # level is properly zero.  Also, the file is bzipped, to demonstrate the new capability of
    # reading in a file that has been compressed in various ways (which GalSim can infer from the
    # filename).  We want to read the image directly into an InterpolatedImage GSObject, so we can
    # manipulate it as needed (here, the only manipulation needed is convolution).  We want a PSF
    # with flux 1, and we can set the pixel scale using a keyword.
    psf_file = os.path.join('data', 'example_sdss_psf_sky0.fits.bz2')
    psf = galsim.InterpolatedImage(psf_file, scale=pixel_scale, flux=1.)
    logger.info('Read in PSF image from bzipped FITS file')

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

    # The default convention for indexing an image is to follow the FITS standard where the
    # lower-left pixel is called (1,1).  However, this can be counter-intuitive to people more
    # used to C or python indexing, where indices start at 0.  It is possible to change the
    # coordinates of the lower-left pixel with the methods `setOrigin`.  For this demo, we
    # switch to 0-based indexing, so the lower-left pixel will be called (0,0).
    full_image.setOrigin(0, 0)

    # As for demo10, we use random_seed+nobj for the random numbers required for the
    # whole image.  In this case, both the power spectrum realization and the noise on the
    # full image we apply later.
    rng = galsim.BaseDeviate(random_seed + nobj)

    # We want to make random positions within our image.  However, currently for shears from a power
    # spectrum we first have to get shears on a grid of positions, and then we can choose random
    # positions within that.  So, let's make the grid.  We're going to make it as large as the
    # image, with grid points spaced by 90 arcsec (hence interpolation only happens below 90"
    # scales, below the interesting scales on which we want the shear power spectrum to be
    # represented exactly).  The lensing engine wants positions in arcsec, so calculate that:
    ps.buildGrid(grid_spacing=grid_spacing,
                 ngrid=int(math.ceil(image_size_arcsec / grid_spacing)),
                 rng=rng.duplicate())
    logger.info('Made gridded shears')

    # 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(0, 0)

    # Make a slightly non-trivial WCS.  We'll use a slightly rotated coordinate system
    # and center it at the image center.
    theta = 0.17 * galsim.degrees
    # ( dudx  dudy ) = ( cos(theta)  -sin(theta) ) * pixel_scale
    # ( dvdx  dvdy )   ( sin(theta)   cos(theta) )
    dudx = math.cos(theta.rad()) * pixel_scale
    dudy = -math.sin(theta.rad()) * pixel_scale
    dvdx = math.sin(theta.rad()) * pixel_scale
    dvdy = math.cos(theta.rad()) * pixel_scale
    image_center = full_image.trueCenter()
    affine = galsim.AffineTransform(dudx,
                                    dudy,
                                    dvdx,
                                    dvdy,
                                    origin=full_image.trueCenter())

    # 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=19.3 * galsim.hours,
                                       dec=-33.1 * galsim.degrees)

    # 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

    # Now we need to loop over our objects:
    for k in range(nobj):
        time1 = time.time()
        # The usual random number generator using a different seed for each galaxy.
        ud = galsim.UniformDeviate(random_seed + k)

        # Draw the size from a plausible size distribution: N(r) ~ r^-2.5
        # For this, we use the class DistDeviate which can draw deviates from an arbitrary
        # probability distribution.  This distribution can be defined either as a functional
        # form as we do here, or as tabulated lists of x and p values, from which the
        # function is interpolated.
        # N.B. This calculation logically belongs later in the script, but given how the config
        #      structure works and the fact that we also use this value for the stamp size
        #      calculation, in order to get the output file to match the YAML output file, it
        #      turns out this is where we need to put this use of the random number generator.
        distdev = galsim.DistDeviate(ud,
                                     function=lambda x: x**-2.5,
                                     x_min=1,
                                     x_max=5)
        dilat = distdev()

        # Choose a random position in the image
        x = ud() * (image_size - 1)
        y = ud() * (image_size - 1)
        image_pos = galsim.PositionD(x, y)

        # Turn this into a position in world coordinates
        # We leave this in the (u,v) plane, since the PowerSpectrum class is really defined
        # on the tangent plane, not in (ra,dec).
        world_pos = affine.toWorld(image_pos)

        # Get the reduced shears and magnification at this point
        g1, g2, mu = ps.getLensing(pos=world_pos)

        # Construct the galaxy:
        # Select randomly from among our list of galaxies.
        index = int(ud() * len(gal_list))
        gal = gal_list[index]

        # If we haven't made this galaxy yet, we need to do so.
        if gal is None:
            # When whitening the image, we need to make sure the original correlated noise is
            # present throughout the whole image, otherwise the whitening will do the wrong thing
            # to the parts of the image that don't include the original image.  The RealGalaxy
            # stores the correct noise profile to use as the gal.noise attribute.  This noise
            # profile is automatically updated as we shear, dilate, convolve, etc.  But we need to
            # tell it how large to pad with this noise by hand.  This is a bit complicated for the
            # code to figure out on its own, so we have to supply the size for noise padding
            # with the noise_pad_size parameter.

            # In this case, the postage stamp will be 32 pixels for the undilated galaxies.
            # We expand the postage stamp as we dilate the galaxies, so that factor doesn't
            # come into play here.  The shear and magnification are not significant, but the
            # image can be rotated, which adds an extra factor of sqrt(2). So the net required
            # padded size is
            #     noise_pad_size = 32 * sqrt(2) * 0.2 arcsec/pixel = 9.1 arcsec
            # We round this up to 10 to be safe.
            gal = galsim.RealGalaxy(real_galaxy_catalog,
                                    rng=ud,
                                    id=id_list[index],
                                    noise_pad_size=10)
            # Save it for next time we use this galaxy.
            gal_list[index] = gal

        # Apply the dilation we calculated above.
        gal = gal.dilate(dilat)

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

        # Apply the cosmological (reduced) shear and magnification at this position using a single
        # GSObject method.
        gal = gal.lens(g1, g2, mu)

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

        # Account for the fractional part of the position:
        ix = int(math.floor(x + 0.5))
        iy = int(math.floor(y + 0.5))
        offset = galsim.PositionD(x - ix, y - iy)

        # Draw it with our desired stamp size (scaled up by the dilation factor):
        # Note: We make the stamp size odd to make the above calculation of the offset easier.
        this_stamp_size = 2 * int(math.ceil(base_stamp_size * dilat / 2)) + 1
        stamp = galsim.ImageF(this_stamp_size, this_stamp_size)

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

        # Now we can whiten or symmetrize the noise on the postage stamp.  Galsim automatically
        # propagates the noise correctly from the initial RealGalaxy object through the applied
        # shear, distortion, rotation, and convolution into the final object's noise attribute.  To
        # make the noise fully white, use the image.whitenNoise() method. The returned value is the
        # variance of the Gaussian noise that is present after the whitening process.
        #
        # However, this is often overkill for many applications.  If it is acceptable to merely end
        # up with noise with some degree of symmetry (say 4-fold or 8-fold symmetry), then you can
        # instead have GalSim just add enough noise to make the resulting noise have this kind of
        # symmetry.  Usually this requires adding significantly less additional noise, which means
        # you can have the resulting total variance be somewhat smaller.  The returned variance
        # corresponds to the zero-lag value of the noise correlation function, which will still have
        # off-diagonal elements.  We can do this step using the image.symmetrizeNoise() method.
        #new_variance = stamp.whitenNoise(final.noise)
        new_variance = stamp.symmetrizeNoise(final.noise, 8)

        # Rescale flux to get the S/N we want.  We have to do that before we add it to the big
        # image, which might have another galaxy near that point (so our S/N calculation would
        # erroneously include the flux from the other object).
        # See demo5.py for the math behind this calculation.
        sn_meas = math.sqrt(numpy.sum(stamp.array**2) / noise_variance)
        flux_scaling = gal_signal_to_noise / sn_meas
        stamp *= flux_scaling
        # This also scales up the current variance by flux_scaling**2.
        new_variance *= flux_scaling**2

        # Recenter the stamp at the desired position:
        stamp.setCenter(ix, iy)

        # Find the overlapping bounds:
        bounds = stamp.bounds & full_image.bounds
        full_image[bounds] += stamp[bounds]

        # We need to keep track of how much variance we have currently in the image, so when
        # we add more noise, we can omit what is already there.
        noise_image[bounds] += new_variance

        time2 = time.time()
        tot_time = time2 - time1
        logger.info('Galaxy %d: position relative to center = %s, t=%f s', k,
                    str(world_pos), tot_time)

    # We already have some noise in the image, but it isn't uniform.  So the first thing to do is
    # to make the Gaussian noise uniform across the whole image.  We have a special noise class
    # that can do this.  VariableGaussianNoise takes an image of variance values and applies
    # Gaussian noise with the corresponding variance to each pixel.
    # So all we need to do is build an image with how much noise to add to each pixel to get us
    # up to the maximum value that we already have in the image.
    max_current_variance = numpy.max(noise_image.array)
    noise_image = max_current_variance - noise_image
    vn = galsim.VariableGaussianNoise(rng, noise_image)
    full_image.addNoise(vn)

    # Now max_current_variance is the noise level across the full image.  We don't want to add that
    # twice, so subtract off this much from the intended noise that we want to end up in the image.
    noise_variance -= max_current_variance

    # 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.
    noise = galsim.GaussianNoise(rng, sigma=math.sqrt(noise_variance))
    full_image.addNoise(noise)
    logger.info('Added noise to final large image')

    # Now write the image to disk.  It is automatically compressed with Rice compression,
    # since the filename we provide ends in .fz.
    full_image.write(file_name)
    logger.info('Wrote image to %r', file_name)

    # Compute some sky positions of some of the pixels to compare with the values of RA, Dec
    # that ds9 reports.  ds9 always uses (1,1) for the lower left pixel, so the pixel coordinates
    # of these pixels are different by 1, but you can check that the RA and Dec values are
    # the same as what GalSim calculates.
    ra_str = sky_center.ra.hms()
    dec_str = sky_center.dec.dms()
    logger.info('Center of image    is at RA %sh %sm %ss, DEC %sd %sm %ss',
                ra_str[0:3], ra_str[3:5], ra_str[5:], dec_str[0:3],
                dec_str[3:5], dec_str[5:])
    for (x, y) in [(0, 0), (0, image_size - 1), (image_size - 1, 0),
                   (image_size - 1, image_size - 1)]:
        world_pos = wcs.toWorld(galsim.PositionD(x, y))
        ra_str = world_pos.ra.hms()
        dec_str = world_pos.dec.dms()
        logger.info('Pixel (%4d, %4d) is at RA %sh %sm %ss, DEC %sd %sm %ss',
                    x, y, ra_str[0:3], ra_str[3:5], ra_str[5:], dec_str[0:3],
                    dec_str[3:5], dec_str[5:])
    logger.info(
        'ds9 reports these pixels as (1,1), (1,3600), etc. with the same RA, Dec.'
    )