def test_area_norm(): """Check that area_norm works as expected""" f606w_cat = galsim.RealGalaxyCatalog('AEGIS_F606w_catalog.fits', dir=image_dir) f814w_cat = galsim.RealGalaxyCatalog('AEGIS_F814w_catalog.fits', dir=image_dir) psf = galsim.Gaussian(fwhm=0.6) rng = galsim.BaseDeviate(5772) crg1 = galsim.ChromaticRealGalaxy([f606w_cat, f814w_cat], random=True, rng=rng.duplicate()) crg2 = galsim.ChromaticRealGalaxy([f606w_cat, f814w_cat], random=True, rng=rng.duplicate(), area_norm=galsim.real.HST_area) assert crg1 != crg2 LSST_i = galsim.Bandpass(os.path.join(bppath, "LSST_r.dat"), 'nm') obj1 = galsim.Convolve(crg1, psf) obj2 = galsim.Convolve(crg2, psf) im1 = obj1.drawImage(LSST_i, exptime=1, area=1) im2 = obj2.drawImage(LSST_i, exptime=1, area=galsim.real.HST_area) printval(im1, im2) np.testing.assert_array_almost_equal(im1.array, im2.array) np.testing.assert_almost_equal( obj1.noise.getVariance(), obj2.noise.getVariance() * galsim.real.HST_area**2) # area_norm is equivalant to an overall scaling crg3 = galsim.ChromaticRealGalaxy([f606w_cat, f814w_cat], random=True, rng=rng.duplicate()) crg3 /= galsim.real.HST_area obj3 = galsim.Convolve(crg3, psf) im3 = obj3.drawImage(LSST_i, exptime=1, area=galsim.real.HST_area) np.testing.assert_array_almost_equal(im3.array, im2.array) np.testing.assert_almost_equal(obj3.noise.getVariance(), obj2.noise.getVariance()) rg1 = galsim.RealGalaxy(f606w_cat, index=1) rg2 = galsim.RealGalaxy(f606w_cat, index=1, area_norm=galsim.real.HST_area) assert rg1 != rg2 obj1 = galsim.Convolve(rg1, psf) obj2 = galsim.Convolve(rg2, psf) im1 = obj1.drawImage() im2 = obj2.drawImage(exptime=1, area=galsim.real.HST_area) printval(im1, im2) np.testing.assert_array_almost_equal(im1.array, im2.array) np.testing.assert_almost_equal( obj1.noise.getVariance(), obj2.noise.getVariance() * galsim.real.HST_area**2) # area_norm is equivalant to an overall scaling rg3 = galsim.RealGalaxy(f606w_cat, index=1) rg3 /= galsim.real.HST_area obj3 = galsim.Convolve(rg3, psf) im3 = obj3.drawImage(exptime=1, area=galsim.real.HST_area) np.testing.assert_array_almost_equal(im3.array, im2.array) np.testing.assert_almost_equal(obj3.noise.getVariance(), obj2.noise.getVariance())
def test_real_galaxy_saved(): """Test accuracy of various calculations with real RealGalaxy vs. stored SHERA result""" # read in real RealGalaxy from file # rgc = galsim.RealGalaxyCatalog(catalog_file, dir=image_dir) # This is an alternate way to give the directory -- as part of the catalog file name. full_catalog_file = os.path.join(image_dir, catalog_file) rgc = galsim.RealGalaxyCatalog(full_catalog_file) rg = galsim.RealGalaxy(rgc, index=ind_real) # read in expected result for some shear shera_image = galsim.fits.read(shera_file) shera_target_PSF_image = galsim.fits.read(shera_target_PSF_file) shera_target_PSF_image.scale = shera_target_pixel_scale # simulate the same galaxy with GalSim tmp_gal = rg.withFlux(shera_target_flux).shear(g1=targ_applied_shear1, g2=targ_applied_shear2) tmp_psf = galsim.InterpolatedImage(shera_target_PSF_image) tmp_gal = galsim.Convolve(tmp_gal, tmp_psf) sim_image = tmp_gal.drawImage(scale=shera_target_pixel_scale, method='no_pixel') # there are centroid issues when comparing Shera vs. SBProfile outputs, so compare 2nd moments # instead of images sbp_res = sim_image.FindAdaptiveMom() shera_res = shera_image.FindAdaptiveMom() np.testing.assert_almost_equal( sbp_res.observed_shape.e1, shera_res.observed_shape.e1, 2, err_msg="Error in comparison with SHERA result: e1") np.testing.assert_almost_equal( sbp_res.observed_shape.e2, shera_res.observed_shape.e2, 2, err_msg="Error in comparison with SHERA result: e2") np.testing.assert_almost_equal( sbp_res.moments_sigma, shera_res.moments_sigma, 2, err_msg="Error in comparison with SHERA result: sigma") check_basic(rg, "RealGalaxy", approx_maxsb=True) # Check picklability do_pickle( rgc, lambda x: [ x.getGal(ind_real), x.getPSF(ind_real), x.getNoiseProperties(ind_real) ]) do_pickle( rgc, lambda x: drawNoise(x.getNoise(ind_real, rng=galsim.BaseDeviate(123)))) do_pickle( rg, lambda x: galsim.Convolve([x, galsim.Gaussian(sigma=1.7)]). drawImage(nx=20, ny=20, scale=0.7)) do_pickle(rgc) do_pickle(rg)
def sample(self, idx, px, py, flux, filter_norm, filters, psfs): obj = SimObj() obj.index = idx obj.x = px obj.y = py idx = np.random.randint(100) morphology = galsim.RealGalaxy(self.real_galaxy_catalog, index=idx, flux=flux) sed, redshift = self.generate_sed() sed = sed.withFlux(1, filters[filter_norm]) sed = np.array([sed.calculateFlux(f) for _, f in filters.items()]) convolved_image = { band: galsim.Convolve([morphology * sed[s], psfs[band]]) for s, band in enumerate(filters) } obj.index = idx obj.redshift = redshift obj.is_star = False obj.catalog_index = idx obj.component = "mix" obj.sed = sed / np.sum(sed) return [obj], [convolved_image]
def test_real_galaxy_saved(): """Test accuracy of various calculations with real RealGalaxy vs. stored SHERA result""" import time t1 = time.time() # read in real RealGalaxy from file rgc = galsim.RealGalaxyCatalog(catalog_file, image_dir) rg = galsim.RealGalaxy(rgc, index=ind_real) # read in expected result for some shear shera_image = galsim.fits.read(shera_file) shera_target_PSF_image = galsim.fits.read(shera_target_PSF_file) # simulate the same galaxy with GalSim sim_image = galsim.simReal(rg, shera_target_PSF_image, shera_target_pixel_scale, g1=targ_applied_shear1, g2=targ_applied_shear2, rand_rotate=False, target_flux=shera_target_flux) # there are centroid issues when comparing Shera vs. SBProfile outputs, so compare 2nd moments # instead of images sbp_res = sim_image.FindAdaptiveMom() shera_res = shera_image.FindAdaptiveMom() np.testing.assert_almost_equal( sbp_res.observed_shape.e1, shera_res.observed_shape.e1, 2, err_msg="Error in comparison with SHERA result: e1") np.testing.assert_almost_equal( sbp_res.observed_shape.e2, shera_res.observed_shape.e2, 2, err_msg="Error in comparison with SHERA result: e2") np.testing.assert_almost_equal( sbp_res.moments_sigma, shera_res.moments_sigma, 2, err_msg="Error in comparison with SHERA result: sigma") # Check picklability do_pickle( rgc, lambda x: [ x.getGal(ind_real), x.getPSF(ind_real), x.getNoiseProperties(ind_real) ]) do_pickle( rgc, lambda x: drawNoise(x.getNoise(ind_real, rng=galsim.BaseDeviate(123)))) do_pickle( rg, lambda x: galsim.Convolve([x, galsim.Gaussian(sigma=1.7)]). drawImage(nx=20, ny=20, scale=0.7)) do_pickle(rgc) do_pickle(rg) t2 = time.time() print 'time for %s = %.2f' % (funcname(), t2 - t1)
def test_real_galaxy_makeFromImage(): """Test accuracy of various calculations with fake Gaussian RealGalaxy vs. ideal expectations""" # read in faked Gaussian RealGalaxy from file rgc = galsim.RealGalaxyCatalog(catalog_file, dir=image_dir) rg = galsim.RealGalaxy(rgc, index=1) gal_image = rg.gal_image psf = rg.original_psf xi = rg.noise rg_2 = galsim.RealGalaxy.makeFromImage(gal_image, psf, xi) check_basic(rg_2, "RealGalaxy", approx_maxsb=True) do_pickle( rg_2, lambda x: [ x.gal_image, x.psf_image, repr(x.noise), x.original_psf.flux, x.original_gal.flux, x.flux ]) do_pickle(rg_2, lambda x: x.drawImage(nx=20, ny=20, scale=0.7)) do_pickle(rg_2) # See if we get reasonably consistent results for rg and rg_2 psf = galsim.Kolmogorov(fwhm=0.6) obj1 = galsim.Convolve(psf, rg) obj2 = galsim.Convolve(psf, rg_2) im1 = obj1.drawImage(scale=0.2, nx=12, ny=12) im2 = obj2.drawImage(image=im1.copy()) atol = obj1.flux * 3e-5 np.testing.assert_allclose(im1.array, im2.array, rtol=0, atol=atol)
def _BuildRealGalaxy(config, key, base, ignore, gsparams): """@brief Build a RealGalaxy type GSObject from user input. """ if 'real_catalog' not in base: raise ValueError( "No real galaxy catalog available for building type = RealGalaxy") real_cat = base['real_catalog'] # Special: if index is Sequence or Random, and max isn't set, set it to real_cat.nobjects-1 if 'id' not in config: galsim.config.SetDefaultIndex(config, real_cat.nobjects) kwargs, safe = galsim.config.GetAllParams( config, key, base, req=galsim.__dict__['RealGalaxy']._req_params, opt=galsim.__dict__['RealGalaxy']._opt_params, single=galsim.__dict__['RealGalaxy']._single_params, ignore=ignore) if gsparams: kwargs['gsparams'] = galsim.GSParams(**gsparams) if 'rng' not in base: raise ValueError("No base['rng'] available for %s.type = RealGalaxy" % (key)) kwargs['rng'] = base['rng'] if 'index' in kwargs: index = kwargs['index'] if index >= real_cat.nobjects: raise IndexError( "%s index has gone past the number of entries in the catalog" % param_name) return galsim.RealGalaxy(real_cat, **kwargs), safe
def test_cosmos_fluxnorm(): """Check for flux normalization properties of COSMOSCatalog class.""" # Check that if we make a RealGalaxy catalog, and a COSMOSCatalog, and draw the real object, the # fluxes should match very well. These correspond to 1s exposures. test_ind = 54 rand_seed = 12345 cat = galsim.COSMOSCatalog( file_name='real_galaxy_catalog_23.5_example.fits', dir=datapath, exclusion_level='none') rgc = galsim.RealGalaxyCatalog( file_name='real_galaxy_catalog_23.5_example.fits', dir=datapath) final_psf = galsim.Airy(diam=1.2, lam=800.) # PSF twice as big as HST in F814W. gal1 = cat.makeGalaxy(test_ind, gal_type='real', rng=galsim.BaseDeviate(rand_seed)) gal2 = galsim.RealGalaxy(rgc, index=test_ind, rng=galsim.BaseDeviate(rand_seed)) gal1 = galsim.Convolve(gal1, final_psf) gal2 = galsim.Convolve(gal2, final_psf) im1 = gal1.drawImage(scale=0.05) im2 = gal2.drawImage(scale=0.05) # Then check that if we draw a parametric representation that is achromatic, that the flux # matches reasonably well (won't be exact because model isn't perfect). gal1_param = cat.makeGalaxy(test_ind, gal_type='parametric', chromatic=False) gal1_param_final = galsim.Convolve(gal1_param, final_psf) im1_param = gal1_param_final.drawImage(scale=0.05) # Then check the same for a chromatic parametric representation that is drawn into the same # band. bp_file = os.path.join(galsim.meta_data.share_dir, 'bandpasses', 'ACS_wfc_F814W.dat') bandpass = galsim.Bandpass(bp_file, wave_type='nm').withZeropoint(25.94) #34.19) gal1_chrom = cat.makeGalaxy(test_ind, gal_type='parametric', chromatic=True) gal1_chrom = galsim.Convolve(gal1_chrom, final_psf) im1_chrom = gal1_chrom.drawImage(bandpass, scale=0.05) ref_val = [im1.array.sum(), im1.array.sum(), im1.array.sum()] test_val = [im2.array.sum(), im1_param.array.sum(), im1_chrom.array.sum()] np.testing.assert_allclose( ref_val, test_val, rtol=0.1, err_msg='Flux normalization problem in COSMOS galaxies') # Finally, check that the original COSMOS info is stored properly after transformations, for # both Sersic galaxies (like galaxy 0 in the catalog) and the one that is gal1_param above. gal0_param = cat.makeGalaxy(0, gal_type='parametric', chromatic=False) assert hasattr(gal0_param.shear(g1=0.05).original, 'index'), \ 'Sersic galaxy does not retain index information after transformation' assert hasattr(gal1_param.shear(g1=0.05).original, 'index'), \ 'Bulge+disk galaxy does not retain index information after transformation'
def _BuildRealGalaxy(config, base, ignore, gsparams, logger, param_name='RealGalaxy'): """@brief Build a RealGalaxy from the real_catalog input item. """ real_cat = galsim.config.GetInputObj('real_catalog', config, base, param_name) # Special: if index is Sequence or Random, and max isn't set, set it to nobjects-1. # But not if they specify 'id' which overrides that. if 'id' not in config: galsim.config.SetDefaultIndex(config, real_cat.getNObjects()) kwargs, safe = galsim.config.GetAllParams(config, base, req = galsim.__dict__['RealGalaxy']._req_params, opt = galsim.__dict__['RealGalaxy']._opt_params, single = galsim.__dict__['RealGalaxy']._single_params, ignore = ignore + ['num']) if gsparams: kwargs['gsparams'] = galsim.GSParams(**gsparams) if 'rng' not in base: raise ValueError("No base['rng'] available for type = %s"%param_name) kwargs['rng'] = base['rng'] if 'index' in kwargs: index = kwargs['index'] if index >= real_cat.getNObjects(): raise IndexError( "%s index has gone past the number of entries in the catalog"%index) kwargs['real_galaxy_catalog'] = real_cat if logger: logger.debug('obj %d: %s kwargs = %s',base['obj_num'],param_name,kwargs) gal = galsim.RealGalaxy(**kwargs) return gal, safe
def _BuildRealGalaxy(config, key, base, ignore, gsparams, logger): """@brief Build a RealGalaxy from the real_catalog input item. """ if 'real_catalog' not in base: raise ValueError( "No real galaxy catalog available for building type = RealGalaxy") if 'num' in config: num, safe = ParseValue(config, 'num', base, int) else: num, safe = (0, True) ignore.append('num') if num < 0: raise ValueError("Invalid num < 0 supplied for RealGalaxy: num = %d" % num) if num >= len(base['real_catalog']): raise ValueError( "Invalid num supplied for RealGalaxy (too large): num = %d" % num) real_cat = base['real_catalog'][num] # Special: if index is Sequence or Random, and max isn't set, set it to nobjects-1. # But not if they specify 'id' which overrides that. if 'id' not in config: galsim.config.SetDefaultIndex(config, real_cat.getNObjects()) kwargs, safe1 = galsim.config.GetAllParams( config, key, base, req=galsim.__dict__['RealGalaxy']._req_params, opt=galsim.__dict__['RealGalaxy']._opt_params, single=galsim.__dict__['RealGalaxy']._single_params, ignore=ignore) safe = safe and safe1 if gsparams: kwargs['gsparams'] = galsim.GSParams(**gsparams) if logger and galsim.RealGalaxy._takes_logger: kwargs['logger'] = logger if 'rng' not in base: raise ValueError("No base['rng'] available for %s.type = RealGalaxy" % (key)) kwargs['rng'] = base['rng'] if 'index' in kwargs: index = kwargs['index'] if index >= real_cat.getNObjects(): raise IndexError( "%s index has gone past the number of entries in the catalog" % index) kwargs['real_galaxy_catalog'] = real_cat if logger: logger.debug('obj %d: RealGalaxy kwargs = %s', base['obj_num'], str(kwargs)) gal = galsim.RealGalaxy(**kwargs) return gal, safe
def _makeReal(self, indices, noise_pad_size, rng, gsparams): return [ galsim.RealGalaxy(self.real_cat, index=self.orig_index[i], noise_pad_size=noise_pad_size, rng=rng, gsparams=gsparams) for i in indices ]
def test_cosmos_fluxnorm(): """Check for flux normalization properties of COSMOSCatalog class.""" import time t1 = time.time() # Check that if we make a RealGalaxy catalog, and a COSMOSCatalog, and draw the real object, the # fluxes should match very well. These correspond to 1s exposures. test_ind = 54 rand_seed = 12345 cat = galsim.COSMOSCatalog(file_name='real_galaxy_catalog_example.fits', dir=datapath, exclude_fail=False, exclude_bad=False) rgc = galsim.RealGalaxyCatalog( file_name='real_galaxy_catalog_example.fits', dir=datapath) final_psf = galsim.Airy(diam=1.2, lam=800.) # PSF twice as big as HST in F814W. gal1 = cat.makeGalaxy(test_ind, gal_type='real', rng=galsim.BaseDeviate(rand_seed)) gal2 = galsim.RealGalaxy(rgc, index=test_ind, rng=galsim.BaseDeviate(rand_seed)) gal1 = galsim.Convolve(gal1, final_psf) gal2 = galsim.Convolve(gal2, final_psf) im1 = gal1.drawImage(scale=0.05) im2 = gal2.drawImage(scale=0.05) # Then check that if we draw a parametric representation that is achromatic, that the flux # matches reasonably well (won't be exact because model isn't perfect). gal1_param = cat.makeGalaxy(test_ind, gal_type='parametric', chromatic=False) gal1_param = galsim.Convolve(gal1_param, final_psf) im1_param = gal1_param.drawImage(scale=0.05) # Then check the same for a chromatic parametric representation that is drawn into the same # band. bp_file = os.path.join(galsim.meta_data.share_dir, 'wfc_F814W.dat.gz') bandpass = galsim.Bandpass(bp_file, wave_type='ang').thin().withZeropoint( 25.94) #34.19) gal1_chrom = cat.makeGalaxy(test_ind, gal_type='parametric', chromatic=True) gal1_chrom = galsim.Convolve(gal1_chrom, final_psf) im1_chrom = gal1_chrom.drawImage(bandpass, scale=0.05) ref_val = [im1.array.sum(), im1.array.sum(), im1.array.sum()] test_val = [im2.array.sum(), im1_param.array.sum(), im1_chrom.array.sum()] np.testing.assert_allclose( ref_val, test_val, rtol=0.1, err_msg='Flux normalization problem in COSMOS galaxies') t2 = time.time() print 'time for %s = %.2f' % (funcname(), t2 - t1)
def _makeSingleGalaxy(cosmos_catalog, index, gal_type, noise_pad_size=5, deep=False, rng=None, sersic_prec=0.05, gsparams=None): # A static function that mimics the functionality of COSMOSCatalog.makeGalaxy() # for single index and chromatic=False. # The only point of this class is to circumvent some pickling issues when using # config objects with type : COSMOSGalaxy. It's a staticmethod, which means it # cannot use any self attributes. Just methods. (Which also means we can use it # through a proxy COSMOSCatalog object, which we need for the config layer.) if not cosmos_catalog.canMakeReal(): if gal_type is None: gal_type = 'parametric' elif gal_type != 'parametric': raise ValueError("Only 'parametric' galaxy type is allowed when use_real == False") if gal_type not in ['real', 'parametric']: raise ValueError("Invalid galaxy type %r"%gal_type) if gal_type == 'real' and rng is None: rng = galsim.BaseDeviate() if gal_type == 'real': real_params = cosmos_catalog.getRealParams(index) gal = galsim.RealGalaxy(real_params, noise_pad_size=noise_pad_size, rng=rng, gsparams=gsparams) else: record = cosmos_catalog.getParametricRecord(index) gal = COSMOSCatalog._buildParametric(record, sersic_prec, gsparams, chromatic=False) # If trying to use the 23.5 sample and "fake" a deep sample, rescale the size and flux as # suggested in the GREAT3 handbook. if deep: if self.use_sample == '23.5': # Rescale the flux to get a limiting mag of 25 in F814W when starting with a # limiting mag of 23.5. Make the galaxies a factor of 0.6 smaller and appropriately # fainter. flux_factor = 10.**(-0.4*1.5) size_factor = 0.6 gal = gal.dilate(size_factor) * flux_factor else: import warnings warnings.warn( 'Ignoring `deep` argument, because the sample being used already '+ 'corresponds to a flux limit of F814W<25.2') # Store the orig_index as gal.index, since the above RealGalaxy initialization just sets it # as 0. Plus, it isn't set at all if we make a parametric galaxy. And if we are doing the # deep scaling, then it gets messed up by that. If we have done some transformations, and # are also doing later transformation, it will take the `original` attribute that is already # there. So having `index` doesn't help, and we also need `original.index`. gal.index = cosmos_catalog.getOrigIndex(index) if hasattr(gal, 'original'): gal.original.index = cosmos_catalog.getOrigIndex(index) return gal
def test_ne(): """ Check that inequality works as expected.""" rgc = galsim.RealGalaxyCatalog(catalog_file, dir=image_dir) f606w_cat = galsim.RealGalaxyCatalog('AEGIS_F606w_catalog.fits', dir=image_dir) f814w_cat = galsim.RealGalaxyCatalog('AEGIS_F814w_catalog.fits', dir=image_dir) crg1 = galsim.ChromaticRealGalaxy([f606w_cat, f814w_cat], index=0) crg2 = galsim.ChromaticRealGalaxy([f606w_cat, f814w_cat], index=1) covspec1 = crg1.covspec covspec2 = crg2.covspec gsp = galsim.GSParams(folding_threshold=1.1e-3) objs = [ galsim.RealGalaxy(rgc, index=0), galsim.RealGalaxy(rgc, index=1), galsim.RealGalaxy(rgc, index=0, x_interpolant='Linear'), galsim.RealGalaxy(rgc, index=0, k_interpolant='Linear'), galsim.RealGalaxy(rgc, index=0, flux=1.1), galsim.RealGalaxy(rgc, index=0, flux_rescale=1.2), galsim.RealGalaxy(rgc, index=0, area_norm=2), galsim.RealGalaxy(rgc, index=0, pad_factor=1.1), galsim.RealGalaxy(rgc, index=0, noise_pad_size=5.0), galsim.RealGalaxy(rgc, index=0, gsparams=gsp), crg1, crg2, galsim.ChromaticRealGalaxy([f606w_cat, f814w_cat], index=0, k_interpolant='Linear'), covspec1, covspec2 ] all_obj_diff(objs) for obj in objs[:-5]: do_pickle(obj) # CovarianceSpectrum and ChromaticRealGalaxy are both reprable, but their reprs are rather # large, so the eval(repr) checks take a long time. # Therefore, run them from command line, but not from pytest. if __name__ == '__main__': do_pickle(crg1) do_pickle(covspec1) else: do_pickle(crg1, irreprable=True) do_pickle(covspec1, irreprable=True)
def test_flux(): """ Check that setting flux and flux_rescale work properly""" rgc = galsim.RealGalaxyCatalog(catalog_file, dir=image_dir) psf = galsim.Gaussian(sigma=1.7) rg1 = galsim.RealGalaxy(rgc, index=0) rg2 = galsim.RealGalaxy(rgc, index=0, flux=1.7) rg3 = galsim.RealGalaxy(rgc, index=0, flux=17) rg4 = galsim.RealGalaxy(rgc, index=0, flux=-17) rg5 = galsim.RealGalaxy(rgc, index=0, flux_rescale=1.7) rg6 = galsim.RealGalaxy(rgc, index=0, flux_rescale=-17) np.testing.assert_almost_equal(rg2.flux, 1.7) np.testing.assert_almost_equal(rg3.flux, 17) np.testing.assert_almost_equal(rg4.flux, -17) np.testing.assert_almost_equal(rg5.flux, 1.7 * rg1.flux) np.testing.assert_almost_equal(rg6.flux, -17 * rg1.flux) check_basic(rg2, "RealGalaxy flux=1.7", approx_maxsb=True) check_basic(rg3, "RealGalaxy flux=17", approx_maxsb=True) check_basic(rg4, "RealGalaxy flux=-17", approx_maxsb=True) check_basic(rg5, "RealGalaxy flux_rescale=1.7", approx_maxsb=True) check_basic(rg6, "RealGalaxy flux_rescale=-17", approx_maxsb=True) do_pickle(rg2) do_pickle(rg3) do_pickle(rg4) do_pickle(rg5) do_pickle(rg6)
def galSimRealGalaxy(flux, real_galaxy_catalog, index=None, psfImage=None, random=False, returnObj=True, plotFake=False, drawMethod='auto', addPoisson=False, scale=1.0, transform=None): """ Real galaxy """ if index is None: random = True realObj = galsim.RealGalaxy(real_galaxy_catalog, index=index, random=random) index = realObj.index # Pass the flux to the object realObj = realObj.withFlux(flux) #do the transformation from sky to pixel coordinates, if given if transform is not None: realObj = realObj.transform(*tuple(transform.ravel())) # Convolve the Sersic model using the provided PSF image if psfImage is not None: # Convert the PSF Image Array into a GalSim Object # Norm=True by default psfObj = arrayToGSObj(psfImage, norm=True) realFinal = galsim.Convolve([realObj, psfObj]) else: realFinal = realFinal # Make a PNG figure of the fake galaxy to check if everything is Ok # TODO: For test, should be removed later if plotFake: plotFakeGalaxy(realFinal, galID=index, suffix='realga') # Now, by default, the function will just return the GSObj if returnObj: return realFinal else: return galSimDrawImage(realFinal, method=drawMethod, scale=scale, addPoisson=addPoisson)
def test_ne(): """ Check that inequality works as expected.""" rgc = galsim.RealGalaxyCatalog(catalog_file, dir=image_dir) gsp = galsim.GSParams(folding_threshold=1.1e-3) gals = [ galsim.RealGalaxy(rgc, index=0), galsim.RealGalaxy(rgc, index=1), galsim.RealGalaxy(rgc, index=0, x_interpolant='Linear'), galsim.RealGalaxy(rgc, index=0, k_interpolant='Linear'), galsim.RealGalaxy(rgc, index=0, flux=1.1), galsim.RealGalaxy(rgc, index=0, pad_factor=1.1), galsim.RealGalaxy(rgc, index=0, noise_pad_size=5.0), galsim.RealGalaxy(rgc, index=0, gsparams=gsp) ] all_obj_diff(gals)
def _BuildRealGalaxy(config, base, ignore, gsparams, logger, param_name='RealGalaxy'): """Build a RealGalaxy from the real_catalog input item. """ real_cat = galsim.config.GetInputObj('real_catalog', config, base, param_name) # Special: if index is Sequence or Random, and max isn't set, set it to nobjects-1. # But not if they specify 'id' or have 'random=True', which overrides that. if 'id' not in config: if 'random' not in config: galsim.config.SetDefaultIndex(config, real_cat.getNObjects()) else: if not config['random']: galsim.config.SetDefaultIndex(config, real_cat.getNObjects()) # Need to do this to avoid being caught by the GetAllParams() call, which will flag # it if it has 'index' and 'random' set (but 'random' is False, so really it's OK). del config['random'] kwargs, safe = galsim.config.GetAllParams( config, base, req=galsim.__dict__['RealGalaxy']._req_params, opt=galsim.__dict__['RealGalaxy']._opt_params, single=galsim.__dict__['RealGalaxy']._single_params, ignore=ignore + ['num']) if gsparams: kwargs['gsparams'] = galsim.GSParams(**gsparams) kwargs['rng'] = galsim.config.GetRNG(config, base, logger, param_name) if 'index' in kwargs: index = kwargs['index'] if index >= real_cat.getNObjects() or index < 0: raise galsim.GalSimConfigError( "index=%s has gone past the number of entries in the RealGalaxyCatalog" % index) kwargs['real_galaxy_catalog'] = real_cat logger.debug('obj %d: %s kwargs = %s', base.get('obj_num', 0), param_name, kwargs) gal = galsim.RealGalaxy(**kwargs) return gal, safe
def _makeSingleGalaxy(cosmos_catalog, index, gal_type, noise_pad_size=5, deep=False, rng=None, gsparams=None): # A static function that mimics the functionality of COSMOSCatalog.makeGalaxy() # for single index and chromatic=False. # The only point of this class is to circumvent some pickling issues when using # config objects with type : COSMOSGalaxy. It's a staticmethod, which means it # cannot use any self attributes. Just methods. (Which also means we can use it # through a proxy COSMOSCatalog object, which we need for the config layer.) if not cosmos_catalog.canMakeReal(): if gal_type is None: gal_type = 'parametric' elif gal_type != 'parametric': raise ValueError( "Only 'parametric' galaxy type is allowed when use_real == False" ) if gal_type not in ['real', 'parametric']: raise ValueError("Invalid galaxy type %r" % gal_type) if gal_type == 'real' and rng is None: rng = galsim.BaseDeviate() if gal_type == 'real': real_params = cosmos_catalog.getRealParams(index) gal = galsim.RealGalaxy(real_params, noise_pad_size=noise_pad_size, rng=rng, gsparams=gsparams) else: record = cosmos_catalog.getParametricRecord(index) gal = COSMOSCatalog._buildParametric(record, gsparams=gsparams) # If deep, rescale the size and flux if deep: # Rescale the flux to get a limiting mag of 25 in F814W. Current limiting mag is 23.5, # so it's a magnitude difference of 1.5. Make the galaxies a factor of 0.6 smaller and # appropriately fainter. flux_factor = 10.**(-0.4 * 1.5) size_factor = 0.6 gal = gal.dilate(size_factor) * flux_factor return gal
def test_ne(): import time t1 = time.time() rgc = galsim.RealGalaxyCatalog(catalog_file, dir=image_dir) gsp = galsim.GSParams(folding_threshold=1.1e-3) gals = [galsim.RealGalaxy(rgc, index=0), galsim.RealGalaxy(rgc, index=1), galsim.RealGalaxy(rgc, index=0, x_interpolant='Linear'), galsim.RealGalaxy(rgc, index=0, k_interpolant='Linear'), galsim.RealGalaxy(rgc, index=0, flux=1.1), galsim.RealGalaxy(rgc, index=0, pad_factor=1.1), galsim.RealGalaxy(rgc, index=0, noise_pad_size=5.0), galsim.RealGalaxy(rgc, index=0, gsparams=gsp)] all_obj_diff(gals) t2 = time.time() print 'time for %s = %.2f'%(funcname(),t2-t1)
def test_real_galaxy_saved(): """Test accuracy of various calculations with real RealGalaxy vs. stored SHERA result""" import time t1 = time.time() # read in real RealGalaxy from file rgc = galsim.RealGalaxyCatalog(catalog_file, image_dir) rg = galsim.RealGalaxy(rgc, index=ind_real) # read in expected result for some shear shera_image = galsim.fits.read(shera_file) shera_target_PSF_image = galsim.fits.read(shera_target_PSF_file) # simulate the same galaxy with GalSim sim_image = galsim.simReal(rg, shera_target_PSF_image, shera_target_pixel_scale, g1=targ_applied_shear1, g2=targ_applied_shear2, rand_rotate=False, target_flux=shera_target_flux) # there are centroid issues when comparing Shera vs. SBProfile outputs, so compare 2nd moments # instead of images sbp_res = sim_image.FindAdaptiveMom() shera_res = shera_image.FindAdaptiveMom() np.testing.assert_almost_equal( sbp_res.observed_shape.e1, shera_res.observed_shape.e1, 2, err_msg="Error in comparison with SHERA result: e1") np.testing.assert_almost_equal( sbp_res.observed_shape.e2, shera_res.observed_shape.e2, 2, err_msg="Error in comparison with SHERA result: e2") np.testing.assert_almost_equal( sbp_res.moments_sigma, shera_res.moments_sigma, 2, err_msg="Error in comparison with SHERA result: sigma") t2 = time.time() print 'time for %s = %.2f' % (funcname(), t2 - t1)
def main(argv): """ Make a fits image cube using real COSMOS galaxies from a catalog describing the training sample. - The number of images in the cube matches the number of rows in the catalog. - Each image size is computed automatically by GalSim based on the Nyquist size. - Both galaxies and stars. - PSF is a double Gaussian, the same for each galaxy. - Galaxies are randomly rotated to remove the imprint of any lensing shears in the COSMOS data. - The same shear is applied to each galaxy. - Noise is Poisson using a nominal sky value of 1.e6 ADU/arcsec^2, the noise in the original COSMOS data. """ logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("demo6") # Define some parameters we'll use below. cat_file_name = 'real_galaxy_catalog_23.5_example.fits' dir = 'data' # Make output directory if not already present. if not os.path.isdir('output'): os.mkdir('output') cube_file_name = os.path.join('output','cube_real.fits') psf_file_name = os.path.join('output','psf_real.fits') random_seed = 1512413 sky_level = 1.e6 # ADU / arcsec^2 pixel_scale = 0.16 # arcsec gal_flux = 1.e5 # arbitrary choice, makes nice (not too) noisy images gal_g1 = -0.027 # gal_g2 = 0.031 # gal_mu = 1.082 # mu = ( (1-kappa)^2 - g1^2 - g2^2 )^-1 psf_inner_fwhm = 0.6 # arcsec psf_outer_fwhm = 2.3 # arcsec psf_inner_fraction = 0.8 # fraction of total PSF flux in the inner Gaussian psf_outer_fraction = 0.2 # fraction of total PSF flux in the inner Gaussian ngal = 100 logger.info('Starting demo script 6 using:') logger.info(' - real galaxies from catalog %r',cat_file_name) logger.info(' - double Gaussian PSF') logger.info(' - pixel scale = %.2f',pixel_scale) logger.info(' - Applied gravitational shear = (%.3f,%.3f)',gal_g1,gal_g2) logger.info(' - Poisson noise (sky level = %.1e).', sky_level) # Read in galaxy catalog # Note: dir is the directory both for the catalog itself and also the directory prefix # for the image files listed in the catalog. # If the images are in a different directory, you may also specify image_dir, which gives # the relative path from dir to wherever the images are located. real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=dir) logger.info('Read in %d real galaxies from catalog', real_galaxy_catalog.nobjects) # Make the double Gaussian PSF psf1 = galsim.Gaussian(fwhm = psf_inner_fwhm, flux = psf_inner_fraction) psf2 = galsim.Gaussian(fwhm = psf_outer_fwhm, flux = psf_outer_fraction) psf = psf1+psf2 # Draw the PSF with no noise. psf_image = psf.drawImage(scale = pixel_scale) # write to file psf_image.write(psf_file_name) logger.info('Created PSF and wrote to file %r',psf_file_name) # Build the images all_images = [] for k in range(ngal): logger.debug('Start work on image %d',k) t1 = time.time() # Initialize the random number generator we will be using. rng = galsim.UniformDeviate(random_seed+k+1) gal = galsim.RealGalaxy(real_galaxy_catalog, index = k) logger.debug(' Read in training sample galaxy and PSF from file') t2 = time.time() # Set the flux gal = gal.withFlux(gal_flux) # Rotate by a random angle theta = 2.*math.pi * rng() * galsim.radians gal = gal.rotate(theta) # Apply the desired shear gal = gal.shear(g1=gal_g1, g2=gal_g2) # Also apply a magnification mu = ( (1-kappa)^2 - |gamma|^2 )^-1 # This conserves surface brightness, so it scales both the area and flux. gal = gal.magnify(gal_mu) # Make the combined profile final = galsim.Convolve([psf, gal]) # Offset by up to 1/2 pixel in each direction # We had previously (in demo4 and demo5) used shift(dx,dy) as a way to shift the center of # the image. Since that is applied to the galaxy, the units are arcsec (since the galaxy # profile itself doesn't know about the pixel scale). Here, the offset applies to the # drawn image, which does know about the pixel scale, so the units of offset are pixels, # not arcsec. Here, we apply an offset of up to half a pixel in each direction. dx = rng() - 0.5 dy = rng() - 0.5 # Draw the profile if k == 0: # Note that the offset argument may be a galsim.PositionD object or a tuple (dx,dy). im = final.drawImage(scale=pixel_scale, offset=(dx,dy)) xsize, ysize = im.array.shape else: im = galsim.ImageF(xsize,ysize) final.drawImage(im, scale=pixel_scale, offset=(dx,dy)) logger.debug(' Drew image') t3 = time.time() # Add a constant background level background = sky_level * pixel_scale**2 im += background # Add Poisson noise. This time, we don't give a sky_level, since we have already # added it to the image, so we don't want any more added. The sky_level parameter # really defines how much _extra_ sky should be added above what is already in the image. im.addNoise(galsim.PoissonNoise(rng)) logger.debug(' Added Poisson noise') t4 = time.time() # Store that into the list of all images all_images += [im] t5 = time.time() logger.debug(' Times: %f, %f, %f, %f',t2-t1, t3-t2, t4-t3, t5-t4) logger.info('Image %d: size = %d x %d, total time = %f sec', k, xsize, ysize, t5-t1) logger.info('Done making images of galaxies') # Now write the image to disk. # We write the images to a fits data cube. galsim.fits.writeCube(all_images, cube_file_name) logger.info('Wrote image to fits data cube %r',cube_file_name)
def test_real_galaxy_ideal(): """Test accuracy of various calculations with fake Gaussian RealGalaxy vs. ideal expectations""" ind_fake = 1 # index of mock galaxy (Gaussian) in catalog fake_gal_fwhm = 0.7 # arcsec fake_gal_shear1 = 0.29 # shear representing intrinsic shape component 1 fake_gal_shear2 = -0.21 # shear representing intrinsic shape component 2 # note non-round, to detect possible issues with x<->y or others that might not show up using # circular galaxy fake_gal_flux = 1000.0 fake_gal_orig_PSF_fwhm = 0.1 # arcsec fake_gal_orig_PSF_shear1 = 0.0 fake_gal_orig_PSF_shear2 = -0.07 # read in faked Gaussian RealGalaxy from file rgc = galsim.RealGalaxyCatalog(catalog_file, dir=image_dir) assert len(rgc) == rgc.getNObjects() == rgc.nobjects == len(rgc.cat) rg = galsim.RealGalaxy(rgc, index=ind_fake) # as a side note, make sure it behaves okay given a legit RNG and a bad RNG # or when trying to specify the galaxy too many ways rg_1 = galsim.RealGalaxy(rgc, index=ind_fake, rng=galsim.BaseDeviate(1234)) rg_2 = galsim.RealGalaxy(rgc, random=True) assert_raises(TypeError, galsim.RealGalaxy, rgc, index=ind_fake, rng='foo') assert_raises(TypeError, galsim.RealGalaxy, rgc) assert_raises(TypeError, galsim.RealGalaxy, rgc, index=ind_fake, flux=12, flux_rescale=2) assert_raises(ValueError, galsim.RealGalaxy, rgc, index=ind_fake, id=0) assert_raises(ValueError, galsim.RealGalaxy, rgc, index=ind_fake, random=True) assert_raises(ValueError, galsim.RealGalaxy, rgc, id=0, random=True) # Different RNGs give different random galaxies. rg_3 = galsim.RealGalaxy(rgc, random=True, rng=galsim.BaseDeviate(12345)) rg_4 = galsim.RealGalaxy(rgc, random=True, rng=galsim.BaseDeviate(67890)) assert rg_3.index != rg_4.index, 'Different seeds did not give different random objects!' gsp = galsim.GSParams(xvalue_accuracy=1.e-8, kvalue_accuracy=1.e-8) rg_5 = galsim.RealGalaxy(rgc, random=True, rng=galsim.BaseDeviate(67890), gsparams=gsp) assert rg_5 != rg_4 assert rg_5 == rg_4.withGSParams(gsp) check_basic(rg, "RealGalaxy", approx_maxsb=True) check_basic(rg_1, "RealGalaxy", approx_maxsb=True) check_basic(rg_2, "RealGalaxy", approx_maxsb=True) do_pickle( rgc, lambda x: [ x.getGalImage(ind_fake), x.getPSFImage(ind_fake), x.getNoiseProperties(ind_fake) ]) do_pickle( rgc, lambda x: drawNoise(x.getNoise(ind_fake, rng=galsim.BaseDeviate(123)))) do_pickle(rgc) do_pickle( rg, lambda x: [ x.gal_image, x.psf_image, repr(x.noise), x.original_psf.flux, x.original_gal.flux, x.flux ]) do_pickle(rg, lambda x: x.drawImage(nx=20, ny=20, scale=0.7)) do_pickle(rg_1, lambda x: x.drawImage(nx=20, ny=20, scale=0.7)) do_pickle(rg) do_pickle(rg_1) ## for the generation of the ideal right answer, we need to add the intrinsic shape of the ## galaxy and the lensing shear using the rule for addition of distortions which is ugly, but oh ## well: targ_pixel_scale = [0.18, 0.25] # arcsec targ_PSF_fwhm = [0.7, 1.0] # arcsec targ_PSF_shear1 = [-0.03, 0.0] targ_PSF_shear2 = [0.05, -0.08] targ_applied_shear1 = 0.06 targ_applied_shear2 = -0.04 fwhm_to_sigma = 1.0 / (2.0 * np.sqrt(2.0 * np.log(2.0))) (d1, d2) = galsim.utilities.g1g2_to_e1e2(fake_gal_shear1, fake_gal_shear2) (d1app, d2app) = galsim.utilities.g1g2_to_e1e2(targ_applied_shear1, targ_applied_shear2) denom = 1.0 + d1 * d1app + d2 * d2app dapp_sq = d1app**2 + d2app**2 d1tot = (d1 + d1app + d2app / dapp_sq * (1.0 - np.sqrt(1.0 - dapp_sq)) * (d2 * d1app - d1 * d2app)) / denom d2tot = (d2 + d2app + d1app / dapp_sq * (1.0 - np.sqrt(1.0 - dapp_sq)) * (d1 * d2app - d2 * d1app)) / denom # convolve with a range of Gaussians, with and without shear (note, for this test all the # original and target ePSFs are Gaussian - there's no separate pixel response so that everything # can be calculated analytically) for tps in targ_pixel_scale: for tpf in targ_PSF_fwhm: for tps1 in targ_PSF_shear1: for tps2 in targ_PSF_shear2: print('tps,tpf,tps1,tps2 = ', tps, tpf, tps1, tps2) # make target PSF targ_PSF = galsim.Gaussian(fwhm=tpf).shear(g1=tps1, g2=tps2) # simulate image tmp_gal = rg.withFlux(fake_gal_flux).shear( g1=targ_applied_shear1, g2=targ_applied_shear2) final_tmp_gal = galsim.Convolve(targ_PSF, tmp_gal) sim_image = final_tmp_gal.drawImage(scale=tps, method='no_pixel') # galaxy sigma, in units of pixels on the final image sigma_ideal = (fake_gal_fwhm / tps) * fwhm_to_sigma # compute analytically the expected galaxy moments: mxx_gal, myy_gal, mxy_gal = ellip_to_moments( d1tot, d2tot, sigma_ideal) # compute analytically the expected PSF moments: targ_PSF_e1, targ_PSF_e2 = galsim.utilities.g1g2_to_e1e2( tps1, tps2) targ_PSF_sigma = (tpf / tps) * fwhm_to_sigma mxx_PSF, myy_PSF, mxy_PSF = ellip_to_moments( targ_PSF_e1, targ_PSF_e2, targ_PSF_sigma) # get expected e1, e2, sigma for the PSF-convolved image tot_e1, tot_e2, tot_sigma = moments_to_ellip( mxx_gal + mxx_PSF, myy_gal + myy_PSF, mxy_gal + mxy_PSF) # compare with images that are expected expected_gaussian = galsim.Gaussian(flux=fake_gal_flux, sigma=tps * tot_sigma) expected_gaussian = expected_gaussian.shear(e1=tot_e1, e2=tot_e2) expected_image = galsim.ImageD(sim_image.array.shape[0], sim_image.array.shape[1]) expected_gaussian.drawImage(expected_image, scale=tps, method='no_pixel') printval(expected_image, sim_image) np.testing.assert_array_almost_equal( sim_image.array, expected_image.array, decimal=3, err_msg= "Error in comparison of ideal Gaussian RealGalaxy calculations" )
def main(argv): """ Make images using variable PSF and shear: - The main image is 10 x 10 postage stamps. - Each postage stamp is 48 x 48 pixels. - The second HDU has the corresponding PSF image. - Applied shear is from a power spectrum P(k) ~ k^1.8. - Galaxies are real galaxies oriented in a ring test of 20 each. - The PSF is Gaussian with FWHM, ellipticity and position angle functions of (x,y) - Noise is Poisson using a nominal sky value of 1.e6. """ logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("demo10") # Define some parameters we'll use below. # Normally these would be read in from some parameter file. n_tiles = 10 # number of tiles in each direction. stamp_size = 48 # pixels pixel_scale = 0.44 # arcsec / pixel sky_level = 1.e6 # ADU / arcsec^2 # The random seed is used for both the power spectrum realization and the random properties # of the galaxies. random_seed = 3339201 # Make output directory if not already present. if not os.path.isdir('output'): os.mkdir('output') file_name = os.path.join('output', 'power_spectrum.fits') # These will be created for each object below. The values we'll use will be functions # of (x,y) relative to the center of the image. (r = sqrt(x^2+y^2)) # psf_fwhm = 0.9 + 0.5 * (r/100)^2 -- arcsec # psf_e = 0.4 * (r/100)^1.5 -- large value at the edge, so visible by eye. # psf_beta = atan2(y/x) + pi/2 -- tangential pattern gal_dilation = 3 # Make the galaxies a bit larger than their original size. gal_signal_to_noise = 100 # Pretty high. psf_signal_to_noise = 1000 # Even higher. logger.info('Starting demo script 10') # Read in galaxy catalog cat_file_name = 'real_galaxy_catalog_23.5_example.fits' dir = 'data' real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=dir) logger.info('Read in %d real galaxies from catalog', real_galaxy_catalog.nobjects) # List of IDs to use. We select 5 particularly irregular galaxies for this demo. # Then we'll choose randomly from this list. id_list = [106416, 106731, 108402, 116045, 116448] # Make the 5 galaxies we're going to use here rather than remake them each time. # This means the Fourier transforms of the real galaxy images don't need to be recalculated # each time, so it's a bit more efficient. gal_list = [ galsim.RealGalaxy(real_galaxy_catalog, id=id) for id in id_list ] # Grab the index numbers before we transform them and lose the index attribute. cosmos_index = [gal.index for gal in gal_list] # Make the galaxies a bit larger than their original observed size. gal_list = [gal.dilate(gal_dilation) for gal in gal_list] # Setup the PowerSpectrum object we'll be using: ps = galsim.PowerSpectrum(lambda k: k**1.8) # The argument here is "e_power_function" which defines the E-mode power to use. # There is also a b_power_function if you want to include any B-mode power: # ps = galsim.PowerSpectrum(e_power_function, b_power_function) # You may even omit the e_power_function argument and have a pure B-mode power spectrum. # ps = galsim.PowerSpectrum(b_power_function = b_power_function) # All the random number generator classes derive from BaseDeviate. # When we construct another kind of deviate class from any other # kind of deviate class, the two share the same underlying random number # generator. Sometimes it can be clearer to just construct a BaseDeviate # explicitly and then construct anything else you need from that. # Note: A BaseDeviate cannot be used to generate any values. It can # only be used in the constructor for other kinds of deviates. # The seeds for the objects are random_seed+1..random_seed+nobj. # The seeds for things at the image or file level use random_seed itself. nobj = n_tiles * n_tiles rng = galsim.BaseDeviate(random_seed) # Have the PowerSpectrum object build a grid of shear values for us to use. grid_g1, grid_g2 = ps.buildGrid(grid_spacing=stamp_size * pixel_scale, ngrid=n_tiles, rng=rng) # Setup the images: gal_image = galsim.ImageF(stamp_size * n_tiles, stamp_size * n_tiles) psf_image = galsim.ImageF(stamp_size * n_tiles, stamp_size * n_tiles) # Update the image WCS to use the image center as the origin of the WCS. # The class that acts like a PixelScale except for this offset is called OffsetWCS. im_center = gal_image.true_center wcs = galsim.OffsetWCS(scale=pixel_scale, origin=im_center) gal_image.wcs = wcs psf_image.wcs = wcs # We will place the tiles in a random order. To do this, we make two lists for the # ix and iy values. Then we apply a random permutation to the lists (in tandem). ix_list = [] iy_list = [] for ix in range(n_tiles): for iy in range(n_tiles): ix_list.append(ix) iy_list.append(iy) # This next function will use the given random number generator, rng, and use it to # randomly permute any number of lists. All lists will have the same random permutation # applied. galsim.random.permute(rng, ix_list, iy_list) # Initialize the OutputCatalog for the truth values names = [ 'gal_num', 'x_image', 'y_image', 'psf_e1', 'psf_e2', 'psf_fwhm', 'cosmos_id', 'cosmos_index', 'theta', 'g1', 'g2', 'shift_x', 'shift_y' ] types = [ int, float, float, float, float, float, str, int, float, float, float, float, float ] truth_catalog = galsim.OutputCatalog(names, types) # Build each postage stamp: for k in range(nobj): # The usual random number generator using a different seed for each galaxy. rng = galsim.BaseDeviate(random_seed + k + 1) # Determine the bounds for this stamp and its center position. ix = ix_list[k] iy = iy_list[k] b = galsim.BoundsI(ix * stamp_size + 1, (ix + 1) * stamp_size, iy * stamp_size + 1, (iy + 1) * stamp_size) sub_gal_image = gal_image[b] sub_psf_image = psf_image[b] pos = wcs.toWorld(b.true_center) # The image comes out as about 211 arcsec across, so we define our variable # parameters in terms of (r/100 arcsec), so roughly the scale size of the image. rsq = (pos.x**2 + pos.y**2) r = math.sqrt(rsq) psf_fwhm = 0.9 + 0.5 * rsq / 100**2 # arcsec psf_e = 0.4 * (r / 100.)**1.5 psf_beta = (math.atan2(pos.y, pos.x) + math.pi / 2) * galsim.radians # Define the PSF profile psf = galsim.Gaussian(fwhm=psf_fwhm) psf_shape = galsim.Shear(e=psf_e, beta=psf_beta) psf = psf.shear(psf_shape) # Define the galaxy profile: # For this demo, we are doing a ring test where the same galaxy profile is drawn at many # orientations stepped uniformly in angle, making a ring in e1-e2 space. # We're drawing each profile at 20 different orientations and then skipping to the # next galaxy in the list. So theta steps by 1/20 * 360 degrees: theta_deg = (k % 20) * 360. / 20 theta = theta_deg * galsim.degrees # The index needs to increment every 20 objects so we use k/20 using integer math. index = k // 20 gal = gal_list[index] # This makes a new copy so we're not changing the object in the gal_list. gal = gal.rotate(theta) # Apply the shear from the power spectrum. We should either turn the gridded shears # grid_g1[iy, ix] and grid_g2[iy, ix] into gridded reduced shears using a utility called # galsim.lensing.theoryToObserved, or use ps.getShear() which by default gets the reduced # shear. ps.getShear() is also more flexible because it can get the shear at positions that # are not on the original grid, as long as they are contained within the bounds of the full # grid. So in this example we'll use ps.getShear(). alt_g1, alt_g2 = ps.getShear(pos) gal = gal.shear(g1=alt_g1, g2=alt_g2) # Apply half-pixel shift in a random direction. shift_r = pixel_scale * 0.5 ud = galsim.UniformDeviate(rng) t = ud() * 2. * math.pi dx = shift_r * math.cos(t) dy = shift_r * math.sin(t) gal = gal.shift(dx, dy) # Make the final image, convolving with the psf final = galsim.Convolve([psf, gal]) # Draw the image final.drawImage(sub_gal_image) # For the PSF image, we don't match the galaxy shift. Rather, we use the offset # parameter to drawImage to apply a random offset of up to 0.5 pixels in each direction. # Note the difference in units between shift and offset. The shift is applied to the # surface brightness profile, so it is in sky coordinates (as all dimension are for # GSObjects), which are arcsec here. The offset though is applied to the image itself, # so it is in pixels. Hence, we don't multiply by pixel_scale. psf_dx = ud() - 0.5 psf_dy = ud() - 0.5 psf_offset = galsim.PositionD(psf_dx, psf_dy) # Draw the PSF image: # We use real space integration over the pixels to avoid some of the # artifacts that can show up with Fourier convolution. # The level of the artifacts is quite low, but when drawing with # so little noise, they are apparent with ds9's zscale viewing. psf.drawImage(sub_psf_image, method='real_space', offset=psf_offset) # Build the noise model: Poisson noise with a given sky level. sky_level_pixel = sky_level * pixel_scale**2 noise = galsim.PoissonNoise(rng, sky_level=sky_level_pixel) # Add noise to the PSF image, using the normal noise model, but scaling the # PSF flux high enough to reach the desired signal-to-noise. # See demo5.py for more info about how this works. sub_psf_image.addNoiseSNR(noise, psf_signal_to_noise) # And also to the galaxy image using its signal-to-noise. sub_gal_image.addNoiseSNR(noise, gal_signal_to_noise) # Add the truth values to the truth catalog row = [ k, b.true_center.x, b.true_center.y, psf_shape.e1, psf_shape.e2, psf_fwhm, id_list[index], cosmos_index[index], (theta_deg % 360.), alt_g1, alt_g2, dx, dy ] truth_catalog.addRow(row) logger.info('Galaxy (%d,%d): position relative to center = %s', ix, iy, str(pos)) logger.info('Done making images of postage stamps') # In this case, we'll attach the truth catalog as an additional HDU in the same file as # the image data. truth_hdu = truth_catalog.writeFitsHdu() # Now write the images to disk. images = [gal_image, psf_image, truth_hdu] # Any items in the "images" list that is already an hdu is just used directly. # The actual images are converted to FITS hdus that contain the image data. galsim.fits.writeMulti(images, file_name) logger.info('Wrote image to %r', file_name)
def test_real_galaxy_ideal(): """Test accuracy of various calculations with fake Gaussian RealGalaxy vs. ideal expectations""" import time t1 = time.time() # read in faked Gaussian RealGalaxy from file rgc = galsim.RealGalaxyCatalog(catalog_file, dir=image_dir) rg = galsim.RealGalaxy(rgc, index=ind_fake) # as a side note, make sure it behaves okay given a legit RNG and a bad RNG # or when trying to specify the galaxy too many ways rg_1 = galsim.RealGalaxy(rgc, index = ind_fake, rng = galsim.BaseDeviate(1234)) rg_2 = galsim.RealGalaxy(rgc, random=True) try: np.testing.assert_raises(TypeError, galsim.RealGalaxy, rgc, index=ind_fake, rng='foo') np.testing.assert_raises(AttributeError, galsim.RealGalaxy, rgc, index=ind_fake, id=0) np.testing.assert_raises(AttributeError, galsim.RealGalaxy, rgc, index=ind_fake, random=True) np.testing.assert_raises(AttributeError, galsim.RealGalaxy, rgc, id=0, random=True) np.testing.assert_raises(AttributeError, galsim.RealGalaxy, rgc) except ImportError: print 'The assert_raises tests require nose' do_pickle(rgc, lambda x: [ x.getGal(ind_fake), x.getPSF(ind_fake), x.getNoiseProperties(ind_fake) ]) do_pickle(rgc, lambda x: drawNoise(x.getNoise(ind_fake,rng=galsim.BaseDeviate(123)))) do_pickle(rgc) do_pickle(rg, lambda x: [ x.gal_image, x.psf_image, repr(x.noise), x.original_psf.flux, x.original_gal.flux, x.flux ]) do_pickle(rg, lambda x: x.drawImage(nx=20, ny=20, scale=0.7)) do_pickle(rg_1, lambda x: x.drawImage(nx=20, ny=20, scale=0.7)) do_pickle(rg) do_pickle(rg_1) ## for the generation of the ideal right answer, we need to add the intrinsic shape of the ## galaxy and the lensing shear using the rule for addition of distortions which is ugly, but oh ## well: (d1, d2) = galsim.utilities.g1g2_to_e1e2(fake_gal_shear1, fake_gal_shear2) (d1app, d2app) = galsim.utilities.g1g2_to_e1e2(targ_applied_shear1, targ_applied_shear2) denom = 1.0 + d1*d1app + d2*d2app dapp_sq = d1app**2 + d2app**2 d1tot = (d1 + d1app + d2app/dapp_sq*(1.0 - np.sqrt(1.0-dapp_sq))*(d2*d1app - d1*d2app))/denom d2tot = (d2 + d2app + d1app/dapp_sq*(1.0 - np.sqrt(1.0-dapp_sq))*(d1*d2app - d2*d1app))/denom # convolve with a range of Gaussians, with and without shear (note, for this test all the # original and target ePSFs are Gaussian - there's no separate pixel response so that everything # can be calculated analytically) for tps in targ_pixel_scale: for tpf in targ_PSF_fwhm: for tps1 in targ_PSF_shear1: for tps2 in targ_PSF_shear2: print 'tps,tpf,tps1,tps2 = ',tps,tpf,tps1,tps2 # make target PSF targ_PSF = galsim.Gaussian(fwhm = tpf).shear(g1=tps1, g2=tps2) # simulate image sim_image = galsim.simReal( rg, targ_PSF, tps, g1 = targ_applied_shear1, g2 = targ_applied_shear2, rand_rotate = False, target_flux = fake_gal_flux) # galaxy sigma, in units of pixels on the final image sigma_ideal = (fake_gal_fwhm/tps)*fwhm_to_sigma # compute analytically the expected galaxy moments: mxx_gal, myy_gal, mxy_gal = ellip_to_moments(d1tot, d2tot, sigma_ideal) # compute analytically the expected PSF moments: targ_PSF_e1, targ_PSF_e2 = galsim.utilities.g1g2_to_e1e2(tps1, tps2) targ_PSF_sigma = (tpf/tps)*fwhm_to_sigma mxx_PSF, myy_PSF, mxy_PSF = ellip_to_moments( targ_PSF_e1, targ_PSF_e2, targ_PSF_sigma) # get expected e1, e2, sigma for the PSF-convolved image tot_e1, tot_e2, tot_sigma = moments_to_ellip( mxx_gal+mxx_PSF, myy_gal+myy_PSF, mxy_gal+mxy_PSF) # compare with images that are expected expected_gaussian = galsim.Gaussian( flux = fake_gal_flux, sigma = tps*tot_sigma) expected_gaussian = expected_gaussian.shear(e1 = tot_e1, e2 = tot_e2) expected_image = galsim.ImageD( sim_image.array.shape[0], sim_image.array.shape[1]) expected_gaussian.drawImage(expected_image, scale=tps, method='no_pixel') printval(expected_image,sim_image) np.testing.assert_array_almost_equal( sim_image.array, expected_image.array, decimal = 3, err_msg = "Error in comparison of ideal Gaussian RealGalaxy calculations") t2 = time.time() print 'time for %s = %.2f'%(funcname(),t2-t1)
def test_cosmos_random(): """Check the random object functionality of the COSMOS catalog.""" # For most of this test, we'll use the selectRandomIndex() routine, which does not try to # construct the GSObjects. This makes the test go fast. However, we will at the end have a # test to ensure that calling makeGalaxy() while requesting a random object has the same # behavior as using selectRandomIndex() in limited cases. # Initialize the catalog. The first will have weights, while the second will not (since they # are in the RealGalaxyCatalog). cat = galsim.COSMOSCatalog( file_name='real_galaxy_catalog_23.5_example.fits', dir=datapath) cat_param = galsim.COSMOSCatalog( file_name='real_galaxy_catalog_23.5_example.fits', dir=datapath, use_real=False) assert cat_param.real_cat is None assert cat.real_cat is not None # Check for exception handling if bad inputs given for the random functionality. assert_raises(ValueError, cat.selectRandomIndex, 0) assert_raises(ValueError, cat.selectRandomIndex, 10.7) assert_raises(TypeError, cat.selectRandomIndex, 10, rng=3) # Check that random objects give the right <weight> without/with weighting. wt = cat.real_cat.weight[cat.orig_index] wt /= np.max(wt) avg_weight_val = np.sum(wt) / len(wt) wavg_weight_val = np.sum(wt**2) / np.sum(wt) with assert_raises(AssertionError): np.testing.assert_almost_equal(avg_weight_val, wavg_weight_val, 3) # Make sure we use enough objects that the mean weights converge properly. randind_wt = cat.selectRandomIndex(30000, rng=galsim.BaseDeviate(1234)) wtrand = cat.real_cat.weight[cat.orig_index[randind_wt]] / \ np.max(cat.real_cat.weight[cat.orig_index]) # The average value of wtrand should be wavgw_weight_val in this case, since we used the weights # to probabilistically select galaxies. np.testing.assert_almost_equal( np.mean(wtrand), wavg_weight_val, 3, err_msg='Average weight for random sample is wrong') # The example catalog doesn't have weights, so it does unweighted selection, which emits a # warning. We know about this and want to ignore it here. with assert_warns(galsim.GalSimWarning): randind = cat_param.selectRandomIndex(30000, rng=galsim.BaseDeviate(1234)) wtrand = cat.real_cat.weight[cat.orig_index[randind]] / \ np.max(cat.real_cat.weight[cat.orig_index]) # The average value of wtrand should be avg_weight_val, since we did not do a weighted # selection. np.testing.assert_almost_equal( np.mean(wtrand), avg_weight_val, 3, err_msg='Average weight for random sample is wrong') # Check for consistency of randoms with same random seed. Do this both for the weighted and the # unweighted calculation. # Check for inconsistency of randoms with different random seed, or same seed but without/with # weighting. rng1 = galsim.BaseDeviate(1234) rng2 = galsim.BaseDeviate(1234) ind1 = cat.selectRandomIndex(10, rng=rng1) ind2 = cat.selectRandomIndex(10, rng=rng2) np.testing.assert_array_equal( ind1, ind2, err_msg='Different random indices selected with same seed!') with assert_warns(galsim.GalSimWarning): ind1p = cat_param.selectRandomIndex(10, rng=rng1) with assert_warns(galsim.GalSimWarning): ind2p = cat_param.selectRandomIndex(10, rng=rng2) np.testing.assert_array_equal( ind1p, ind2p, err_msg='Different random indices selected with same seed!') rng3 = galsim.BaseDeviate(5678) ind3 = cat.selectRandomIndex(10, rng=rng3) with assert_warns(galsim.GalSimWarning): ind3p = cat_param.selectRandomIndex(10) # initialize RNG based on time assert_raises(AssertionError, np.testing.assert_array_equal, ind1, ind1p) assert_raises(AssertionError, np.testing.assert_array_equal, ind1, ind3) assert_raises(AssertionError, np.testing.assert_array_equal, ind1p, ind3p) # Finally, make sure that directly calling selectRandomIndex() gives the same random ones as # makeGalaxy(). We'll do one real object because they are slower, and multiple parametric (just # to make sure that the multi-object selection works consistently). use_seed = 567 obj = cat.makeGalaxy(rng=galsim.BaseDeviate(use_seed)) ind = cat.selectRandomIndex(1, rng=galsim.BaseDeviate(use_seed)) obj_2 = cat.makeGalaxy(ind) # Note: for real galaxies we cannot require that obj==obj_2, just that obj.index==obj_2.index. # That's because we want to make sure the same galaxy is being randomly selected, but we cannot # require that noise padding be the same, given the inconsistency in how the BaseDeviates are # used in the above cases. assert obj.index == obj_2.index, 'makeGalaxy selects random objects inconsistently' n_random = 3 with assert_warns(galsim.GalSimWarning): # Warns because we aren't using weights objs = cat_param.makeGalaxy(rng=galsim.BaseDeviate(use_seed), n_random=n_random) with assert_warns(galsim.GalSimWarning): inds = cat_param.selectRandomIndex(n_random, rng=galsim.BaseDeviate(use_seed)) objs_2 = cat_param.makeGalaxy(inds) for i in range(n_random): # With parametric objects there is no noise padding, so we can require completely identical # GSObjects (not just equal indices). assert objs[i] == objs_2[ i], 'makeGalaxy selects random objects inconsistently' # Finally, check for consistency with random object selected from RealGalaxyCatalog. For this # case, we need to make another COSMOSCatalog that does not flag the bad objects. use_seed = 31415 cat = galsim.COSMOSCatalog( file_name='real_galaxy_catalog_23.5_example.fits', dir=datapath, exclusion_level='none') rgc = galsim.RealGalaxyCatalog( file_name='real_galaxy_catalog_23.5_example.fits', dir=datapath) ind_cc = cat.selectRandomIndex(1, rng=galsim.BaseDeviate(use_seed)) foo = galsim.RealGalaxy(rgc, random=True, rng=galsim.BaseDeviate(use_seed)) ind_rgc = foo.index assert ind_cc==ind_rgc,\ 'Different weighted random index selected from COSMOSCatalog and RealGalaxyCatalog' # Also check for the unweighted case. Just remove that info from the catalogs and redo the # test. cat.real_cat = None del rgc.weight with assert_warns(galsim.GalSimWarning): ind_cc = cat.selectRandomIndex(1, rng=galsim.BaseDeviate(use_seed)) foo = galsim.RealGalaxy(rgc, random=True, rng=galsim.BaseDeviate(use_seed)) ind_rgc = foo.index assert ind_cc==ind_rgc,\ 'Different unweighted random index selected from COSMOSCatalog and RealGalaxyCatalog' # Check that setting _n_rng_calls properly tracks the RNG calls for n_random=1 and >1. test_seed = 123456 ud = galsim.UniformDeviate(test_seed) with assert_warns(galsim.GalSimWarning): obj, n_rng_calls = cat.selectRandomIndex(1, rng=ud, _n_rng_calls=True) ud2 = galsim.UniformDeviate(test_seed) ud2.discard(n_rng_calls) assert ud() == ud2( ), '_n_rng_calls kwarg did not give proper tracking of RNG calls' ud = galsim.UniformDeviate(test_seed) with assert_warns(galsim.GalSimWarning): obj, n_rng_calls = cat.selectRandomIndex(17, rng=ud, _n_rng_calls=True) ud2 = galsim.UniformDeviate(test_seed) ud2.discard(n_rng_calls) assert ud() == ud2( ), '_n_rng_calls kwarg did not give proper tracking of RNG calls' # Invalid to both privide index and ask for random selection with assert_raises(galsim.GalSimIncompatibleValuesError): cat_param.makeGalaxy(index=(11, 13, 17), n_random=3)
def test_flip(): """Test several ways to flip a profile """ # The Shapelet profile has the advantage of being fast and not circularly symmetric, so # it is a good test of the actual code for doing the flips (in SBTransform). # But since the bug Rachel reported in #645 was actually in SBInterpolatedImage # (one calculation implicitly assumed dx > 0), it seems worthwhile to run through all the # classes to make sure we hit everything with negative steps for dx and dy. prof_list = [ galsim.Shapelet(sigma=0.17, order=2, bvec=[1.7, 0.01,0.03, 0.29, 0.33, -0.18]), ] if __name__ == "__main__": image_dir = './real_comparison_images' catalog_file = 'test_catalog.fits' rgc = galsim.RealGalaxyCatalog(catalog_file, dir=image_dir) # Some of these are slow, so only do the Shapelet test as part of the normal unit tests. prof_list += [ galsim.Airy(lam_over_diam=0.17, flux=1.7), galsim.Airy(lam_over_diam=0.17, obscuration=0.2, flux=1.7), # Box gets rendered with real-space convolution. The default accuracy isn't quite # enough to get the flip to match at 6 decimal places. galsim.Box(0.17, 0.23, flux=1.7, gsparams=galsim.GSParams(realspace_relerr=1.e-6)), # Without being convolved by anything with a reasonable k cutoff, this needs # a very large fft. galsim.DeVaucouleurs(half_light_radius=0.17, flux=1.7), # I don't really understand why this needs a lower maxk_threshold to work, but # without it, the k-space tests fail. galsim.Exponential(scale_radius=0.17, flux=1.7, gsparams=galsim.GSParams(maxk_threshold=1.e-4)), galsim.Gaussian(sigma=0.17, flux=1.7), galsim.Kolmogorov(fwhm=0.17, flux=1.7), galsim.Moffat(beta=2.5, fwhm=0.17, flux=1.7), galsim.Moffat(beta=2.5, fwhm=0.17, flux=1.7, trunc=0.82), galsim.OpticalPSF(lam_over_diam=0.17, obscuration=0.2, nstruts=6, coma1=0.2, coma2=0.5, defocus=-0.1, flux=1.7), # Like with Box, we need to increase the real-space convolution accuracy. # This time lowering both relerr and abserr. galsim.Pixel(0.23, flux=1.7, gsparams=galsim.GSParams(realspace_relerr=1.e-6, realspace_abserr=1.e-8)), # Note: RealGalaxy should not be rendered directly because of the deconvolution. # Here we convolve it by a Gaussian that is slightly larger than the original PSF. galsim.Convolve([ galsim.RealGalaxy(rgc, index=0, flux=1.7), # "Real" RealGalaxy galsim.Gaussian(sigma=0.08) ]), galsim.Convolve([ galsim.RealGalaxy(rgc, index=1, flux=1.7), # "Fake" RealGalaxy galsim.Gaussian(sigma=0.08) ]), # (cf. test_real.py) galsim.Spergel(nu=-0.19, half_light_radius=0.17, flux=1.7), galsim.Spergel(nu=0., half_light_radius=0.17, flux=1.7), galsim.Spergel(nu=0.8, half_light_radius=0.17, flux=1.7), galsim.Sersic(n=2.3, half_light_radius=0.17, flux=1.7), galsim.Sersic(n=2.3, half_light_radius=0.17, flux=1.7, trunc=0.82), # The shifts here caught a bug in how SBTransform handled the recentering. # Two of the shifts (0.125 and 0.375) lead back to 0.0 happening on an integer # index, which now works correctly. galsim.Sum([ galsim.Gaussian(sigma=0.17, flux=1.7).shift(-0.2,0.125), galsim.Exponential(scale_radius=0.23, flux=3.1).shift(0.375,0.23)]), galsim.TopHat(0.23, flux=1.7), # Box and Pixel use real-space convolution. Convolve with a Gaussian to get fft. galsim.Convolve([ galsim.Box(0.17, 0.23, flux=1.7).shift(-0.2,0.1), galsim.Gaussian(sigma=0.09) ]), galsim.Convolve([ galsim.TopHat(0.17, flux=1.7).shift(-0.275,0.125), galsim.Gaussian(sigma=0.09) ]), # Test something really crazy with several layers worth of transformations galsim.Convolve([ galsim.Sum([ galsim.Gaussian(sigma=0.17, flux=1.7).shear(g1=0.1,g2=0.2).shift(2,3), galsim.Kolmogorov(fwhm=0.33, flux=3.9).transform(0.31,0.19,-0.23,0.33) * 88., galsim.Box(0.11, 0.44, flux=4).rotate(33 * galsim.degrees) / 1.9 ]).shift(-0.3,1), galsim.AutoConvolve(galsim.TopHat(0.5).shear(g1=0.3,g2=0)).rotate(3*galsim.degrees), (galsim.AutoCorrelate(galsim.Box(0.2, 0.3)) * 11).shift(3,2).shift(2,-3) * 0.31 ]).shift(0,0).transform(0,-1,-1,0).shift(-1,1) ] s = galsim.Shear(g1=0.11, g2=-0.21) s1 = galsim.Shear(g1=0.11, g2=0.21) # Appropriate for the flips around x and y axes s2 = galsim.Shear(g1=-0.11, g2=-0.21) # Appropriate for the flip around x=y # Also use shears with just a g1 to get dx != dy, but dxy, dyx = 0. q = galsim.Shear(g1=0.11, g2=0.) q1 = galsim.Shear(g1=0.11, g2=0.) # Appropriate for the flips around x and y axes q2 = galsim.Shear(g1=-0.11, g2=0.) # Appropriate for the flip around x=y decimal=6 # Oddly, these aren't as precise as I would have expected. # Even when we only go to this many digits of accuracy, the Exponential needed # a lower than default value for maxk_threshold. im = galsim.ImageD(16,16, scale=0.05) for prof in prof_list: print('prof = ',prof) # Not all profiles are expected to have a max_sb value close to the maximum pixel value, # so mark the ones where we don't want to require this to be true. close_maxsb = True name = str(prof) if ('DeVauc' in name or 'Sersic' in name or 'Spergel' in name or 'Optical' in name or 'shift' in name): close_maxsb = False # Make sure we hit all 4 fill functions. # image_x uses fillXValue with izero, jzero # image_x1 uses fillXValue with izero, jzero, and unequal dx,dy # image_x2 uses fillXValue with dxy, dyx # image_k uses fillKValue with izero, jzero # image_k1 uses fillKValue with izero, jzero, and unequal dx,dy # image_k2 uses fillKValue with dxy, dyx image_x = prof.drawImage(image=im.copy(), method='no_pixel') image_x1 = prof.shear(q).drawImage(image=im.copy(), method='no_pixel') image_x2 = prof.shear(s).drawImage(image=im.copy(), method='no_pixel') image_k = prof.drawImage(image=im.copy()) image_k1 = prof.shear(q).drawImage(image=im.copy()) image_k2 = prof.shear(s).drawImage(image=im.copy()) if close_maxsb: np.testing.assert_allclose( image_x.array.max(), prof.max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image_x1.array.max(), prof.shear(q).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image_x2.array.max(), prof.shear(s).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") # Flip around y axis (i.e. x -> -x) flip1 = prof.transform(-1, 0, 0, 1) image2_x = flip1.drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x.array, image2_x.array[:,::-1], decimal=decimal, err_msg="Flipping image around y-axis failed x test") image2_x1 = flip1.shear(q1).drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x1.array, image2_x1.array[:,::-1], decimal=decimal, err_msg="Flipping image around y-axis failed x1 test") image2_x2 = flip1.shear(s1).drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x2.array, image2_x2.array[:,::-1], decimal=decimal, err_msg="Flipping image around y-axis failed x2 test") image2_k = flip1.drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k.array, image2_k.array[:,::-1], decimal=decimal, err_msg="Flipping image around y-axis failed k test") image2_k1 = flip1.shear(q1).drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k1.array, image2_k1.array[:,::-1], decimal=decimal, err_msg="Flipping image around y-axis failed k1 test") image2_k2 = flip1.shear(s1).drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k2.array, image2_k2.array[:,::-1], decimal=decimal, err_msg="Flipping image around y-axis failed k2 test") if close_maxsb: np.testing.assert_allclose( image2_x.array.max(), flip1.max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image2_x1.array.max(), flip1.shear(q).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image2_x2.array.max(), flip1.shear(s).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") # Flip around x axis (i.e. y -> -y) flip2 = prof.transform(1, 0, 0, -1) image2_x = flip2.drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x.array, image2_x.array[::-1,:], decimal=decimal, err_msg="Flipping image around x-axis failed x test") image2_x1 = flip2.shear(q1).drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x1.array, image2_x1.array[::-1,:], decimal=decimal, err_msg="Flipping image around x-axis failed x1 test") image2_x2 = flip2.shear(s1).drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x2.array, image2_x2.array[::-1,:], decimal=decimal, err_msg="Flipping image around x-axis failed x2 test") image2_k = flip2.drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k.array, image2_k.array[::-1,:], decimal=decimal, err_msg="Flipping image around x-axis failed k test") image2_k1 = flip2.shear(q1).drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k1.array, image2_k1.array[::-1,:], decimal=decimal, err_msg="Flipping image around x-axis failed k1 test") image2_k2 = flip2.shear(s1).drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k2.array, image2_k2.array[::-1,:], decimal=decimal, err_msg="Flipping image around x-axis failed k2 test") if close_maxsb: np.testing.assert_allclose( image2_x.array.max(), flip2.max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image2_x1.array.max(), flip2.shear(q).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image2_x2.array.max(), flip2.shear(s).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") # Flip around x=y (i.e. y -> x, x -> y) flip3 = prof.transform(0, 1, 1, 0) image2_x = flip3.drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x.array, np.transpose(image2_x.array), decimal=decimal, err_msg="Flipping image around x=y failed x test") image2_x1 = flip3.shear(q2).drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x1.array, np.transpose(image2_x1.array), decimal=decimal, err_msg="Flipping image around x=y failed x1 test") image2_x2 = flip3.shear(s2).drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x2.array, np.transpose(image2_x2.array), decimal=decimal, err_msg="Flipping image around x=y failed x2 test") image2_k = flip3.drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k.array, np.transpose(image2_k.array), decimal=decimal, err_msg="Flipping image around x=y failed k test") image2_k1 = flip3.shear(q2).drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k1.array, np.transpose(image2_k1.array), decimal=decimal, err_msg="Flipping image around x=y failed k1 test") image2_k2 = flip3.shear(s2).drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k2.array, np.transpose(image2_k2.array), decimal=decimal, err_msg="Flipping image around x=y failed k2 test") if close_maxsb: np.testing.assert_allclose( image2_x.array.max(), flip3.max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image2_x1.array.max(), flip3.shear(q).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image2_x2.array.max(), flip3.shear(s).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") do_pickle(prof, lambda x: x.drawImage(image=im.copy(), method='no_pixel')) do_pickle(flip1, lambda x: x.drawImage(image=im.copy(), method='no_pixel')) do_pickle(flip2, lambda x: x.drawImage(image=im.copy(), method='no_pixel')) do_pickle(flip3, lambda x: x.drawImage(image=im.copy(), method='no_pixel')) do_pickle(prof) do_pickle(flip1) do_pickle(flip2) do_pickle(flip3)
def main(argv): """ Make a fits image cube using real COSMOS galaxies from a catalog describing the training sample. - The number of images in the cube matches the number of rows in the catalog. - Each image size is computed automatically by GalSim based on the Nyquist size. - Both galaxies and stars. - PSF is a double Gaussian, the same for each galaxy. - Galaxies are randomly rotated to remove the imprint of any lensing shears in the COSMOS data. - The same shear is applied to each galaxy. - Noise is Poisson using a nominal sky value of 1.e6 ADU/arcsec^2, the noise in the original COSMOS data. """ logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("demo6") # Define some parameters we'll use below. cat_file_name = 'real_galaxy_catalog_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/demo6 also be run from the bin directory. dir = '../examples/data' # Make output directory if not already present. if not os.path.isdir('output'): os.mkdir('output') cube_file_name = os.path.join('output', 'cube_real.fits') psf_file_name = os.path.join('output', 'psf_real.fits') random_seed = 1512413 sky_level = 1.e6 # ADU / arcsec^2 pixel_scale = 0.15 # arcsec gal_flux = 1.e5 # arbitrary choice, makes nice (not too) noisy images gal_g1 = -0.027 # gal_g2 = 0.031 # gal_mu = 1.082 # mu = ( (1-kappa)^2 - g1^2 - g2^2 )^-1 psf_inner_fwhm = 0.6 # arcsec psf_outer_fwhm = 2.3 # arcsec psf_inner_fraction = 0.8 # fraction of total PSF flux in the inner Gaussian psf_outer_fraction = 0.2 # fraction of total PSF flux in the inner Gaussian ngal = 100 logger.info('Starting demo script 6 using:') logger.info(' - real galaxies from catalog %r', cat_file_name) logger.info(' - double Gaussian PSF') logger.info(' - pixel scale = %.2f', pixel_scale) logger.info(' - Applied gravitational shear = (%.3f,%.3f)', gal_g1, gal_g2) logger.info(' - Poisson noise (sky level = %.1e).', sky_level) # Read in galaxy catalog # Note: dir is the directory both for the catalog itself and also the directory prefix # for the image files listed in the catalog. # If the images are in a different directory, you may also specify image_dir, which gives # the relative path from dir to wherever the images are located. real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=dir) # Preloading the header information usually speeds up subsequent access. # Basically, it tells pyfits to read all the headers in once and save them, rather # than re-open the galaxy catalog fits file each time you want to access a new galaxy. # If you are doing more than a few galaxies, then it seems to be worthwhile. real_galaxy_catalog.preload() logger.info('Read in %d real galaxies from catalog', real_galaxy_catalog.nobjects) # Make the ePSF # first make the double Gaussian PSF psf1 = galsim.Gaussian(fwhm=psf_inner_fwhm, flux=psf_inner_fraction) psf2 = galsim.Gaussian(fwhm=psf_outer_fwhm, flux=psf_outer_fraction) psf = psf1 + psf2 # make the pixel response pix = galsim.Pixel(pixel_scale) # convolve PSF and pixel response function to get the effective PSF (ePSF) epsf = galsim.Convolve([psf, pix]) # Draw this one with no noise. epsf_image = epsf.draw(dx=pixel_scale) # write to file epsf_image.write(psf_file_name) logger.info('Created ePSF and wrote to file %r', psf_file_name) # Build the images all_images = [] for k in range(ngal): logger.debug('Start work on image %d', k) t1 = time.time() # Initialize the random number generator we will be using. rng = galsim.UniformDeviate(random_seed + k) gal = galsim.RealGalaxy(real_galaxy_catalog, index=k) logger.debug(' Read in training sample galaxy and PSF from file') t2 = time.time() # Set the flux gal.setFlux(gal_flux) # Rotate by a random angle theta = 2. * math.pi * rng() * galsim.radians gal.applyRotation(theta) # Apply the desired shear gal.applyShear(g1=gal_g1, g2=gal_g2) # Also apply a magnification mu = ( (1-kappa)^2 - |gamma|^2 )^-1 # This conserves surface brightness, so it scales both the area and flux. gal.applyMagnification(gal_mu) # Make the combined profile final = galsim.Convolve([psf, pix, gal]) # Draw the profile if k == 0: im = final.draw(dx=pixel_scale) xsize, ysize = im.array.shape else: im = galsim.ImageF(xsize, ysize) final.draw(im, dx=pixel_scale) logger.debug(' Drew image') t3 = time.time() # Add a constant background level background = sky_level * pixel_scale**2 im += background # Add Poisson noise. This time, we don't give a sky_level, since we have already # added it to the image, so we don't want any more added. The sky_level parameter # really defines how much _extra_ sky should be added above what is already in the image. im.addNoise(galsim.PoissonNoise(rng)) logger.debug(' Added Poisson noise') t4 = time.time() # Store that into the list of all images all_images += [im] t5 = time.time() logger.debug(' Times: %f, %f, %f, %f', t2 - t1, t3 - t2, t4 - t3, t5 - t4) logger.info('Image %d: size = %d x %d, total time = %f sec', k, xsize, ysize, t5 - t1) logger.info('Done making images of galaxies') # Now write the image to disk. # We write the images to a fits data cube. galsim.fits.writeCube(all_images, cube_file_name) logger.info('Wrote image to fits data cube %r', cube_file_name)
def main(argv): """ Make images using variable PSF and shear: - The main image is 10 x 10 postage stamps. - Each postage stamp is 48 x 48 pixels. - The second HDU has the corresponding PSF image. - Applied shear is from a power spectrum P(k) ~ k^1.8. - Galaxies are real galaxies oriented in a ring test of 20 each. - The PSF is Gaussian with FWHM, ellipticity and position angle functions of (x,y) - Noise is Poisson using a nominal sky value of 1.e6. """ logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("demo10") # Define some parameters we'll use below. # Normally these would be read in from some parameter file. n_tiles = 10 # number of tiles in each direction. stamp_size = 48 # pixels pixel_scale = 0.44 # arcsec / pixel sky_level = 1.e6 # ADU / arcsec^2 # The random seed is used for both the power spectrum realization and the random properties # of the galaxies. random_seed = 3339201 # Make output directory if not already present. if not os.path.isdir('output'): os.mkdir('output') file_name = os.path.join('output', 'power_spectrum.fits') # These will be created for each object below. The values we'll use will be functions # of (x,y) relative to the center of the image. (r = sqrt(x^2+y^2)) # psf_fwhm = 0.9 + 0.5 * (r/100)^2 -- arcsec # psf_e = 0.4 * (r/100)^1.5 -- large value at the edge, so visible by eye. # psf_beta = atan2(y/x) + pi/2 -- tangential pattern gal_dilation = 3 # Make the galaxies a bit larger than their original size. gal_signal_to_noise = 100 # Pretty high. psf_signal_to_noise = 1000 # Even higher. logger.info('Starting demo script 10') # Read in galaxy catalog cat_file_name = 'real_galaxy_catalog_example.fits' dir = 'data' real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=dir) logger.info('Read in %d real galaxies from catalog', real_galaxy_catalog.nobjects) # List of IDs to use. We select 5 particularly irregular galaxies for this demo. # Then we'll choose randomly from this list. id_list = [106416, 106731, 108402, 116045, 116448] # Make the 5 galaxies we're going to use here rather than remake them each time. # This means the Fourier transforms of the real galaxy images don't need to be recalculated # each time, so it's a bit more efficient. gal_list = [ galsim.RealGalaxy(real_galaxy_catalog, id=id) for id in id_list ] # Make the galaxies a bit larger than their original observed size. gal_list = [gal.dilate(gal_dilation) for gal in gal_list] # Setup the PowerSpectrum object we'll be using: ps = galsim.PowerSpectrum(lambda k: k**1.8) # The argument here is "e_power_function" which defines the E-mode power to use. # There is also a b_power_function if you want to include any B-mode power: # ps = galsim.PowerSpectrum(e_power_function, b_power_function) # You may even omit the e_power_function argument and have a pure B-mode power spectrum. # ps = galsim.PowerSpectrum(b_power_function = b_power_function) # All the random number generator classes derive from BaseDeviate. # When we construct another kind of deviate class from any other # kind of deviate class, the two share the same underlying random number # generator. Sometimes it can be clearer to just construct a BaseDeviate # explicitly and then construct anything else you need from that. # Note: A BaseDeviate cannot be used to generate any values. It can # only be used in the constructor for other kinds of deviates. # The seeds for the objects are random_seed..random_seed+nobj-1 (which comes later), # so use the next one. nobj = n_tiles * n_tiles rng = galsim.BaseDeviate(random_seed + nobj) # Setup the images: gal_image = galsim.ImageF(stamp_size * n_tiles, stamp_size * n_tiles) psf_image = galsim.ImageF(stamp_size * n_tiles, stamp_size * n_tiles) # Update the image WCS to use the image center as the origin of the WCS. # The class that acts like a PixelScale except for this offset is called OffsetWCS. im_center = gal_image.bounds.trueCenter() wcs = galsim.OffsetWCS(scale=pixel_scale, origin=im_center) gal_image.wcs = wcs psf_image.wcs = wcs # We will place the tiles in a random order. To do this, we make two lists for the # ix and iy values. Then we apply a random permutation to the lists (in tandem). ix_list = [] iy_list = [] for ix in range(n_tiles): for iy in range(n_tiles): ix_list.append(ix) iy_list.append(iy) # This next function will use the given random number generator, rng, and use it to # randomly permute any number of lists. All lists will have the same random permutation # applied. galsim.random.permute(rng, ix_list, iy_list) # Now have the PowerSpectrum object build a grid of shear values for us to use. # Also, because of some technical details about how the config stuff handles the random # number generator here, we need to duplicate the rng object if we want to have the # two output files match. This means that technically, the same sequence of random numbers # will be used in building the grid as will be used by the other uses of rng (permuting the # postage stamps and adding noise). But since they are used in such completely different # ways, it is hard to imagine how this could lead to any kind of bias in the images. grid_g1, grid_g2 = ps.buildGrid(grid_spacing=stamp_size * pixel_scale, ngrid=n_tiles, rng=rng.duplicate()) # Build each postage stamp: for k in range(nobj): # The usual random number generator using a different seed for each galaxy. rng = galsim.BaseDeviate(random_seed + k) # Determine the bounds for this stamp and its center position. ix = ix_list[k] iy = iy_list[k] b = galsim.BoundsI(ix * stamp_size + 1, (ix + 1) * stamp_size, iy * stamp_size + 1, (iy + 1) * stamp_size) sub_gal_image = gal_image[b] sub_psf_image = psf_image[b] pos = wcs.toWorld(b.trueCenter()) # The image comes out as about 211 arcsec across, so we define our variable # parameters in terms of (r/100 arcsec), so roughly the scale size of the image. r = math.sqrt(pos.x**2 + pos.y**2) / 100 psf_fwhm = 0.9 + 0.5 * r**2 # arcsec psf_e = 0.4 * r**1.5 psf_beta = (math.atan2(pos.y, pos.x) + math.pi / 2) * galsim.radians # Define the PSF profile psf = galsim.Gaussian(fwhm=psf_fwhm) psf = psf.shear(e=psf_e, beta=psf_beta) # Define the galaxy profile: # For this demo, we are doing a ring test where the same galaxy profile is drawn at many # orientations stepped uniformly in angle, making a ring in e1-e2 space. # We're drawing each profile at 20 different orientations and then skipping to the # next galaxy in the list. So theta steps by 1/20 * 360 degrees: theta = k / 20. * 360. * galsim.degrees # The index needs to increment every 20 objects so we use k/20 using integer math. index = k / 20 gal = gal_list[index] # This makes a new copy so we're not changing the object in the gal_list. gal = gal.rotate(theta) # Apply the shear from the power spectrum. We should either turn the gridded shears # grid_g1[iy, ix] and grid_g2[iy, ix] into gridded reduced shears using a utility called # galsim.lensing.theoryToObserved, or use ps.getShear() which by default gets the reduced # shear. ps.getShear() is also more flexible because it can get the shear at positions that # are not on the original grid, as long as they are contained within the bounds of the full # grid. So in this example we'll use ps.getShear(). alt_g1, alt_g2 = ps.getShear(pos) gal = gal.shear(g1=alt_g1, g2=alt_g2) # Apply half-pixel shift in a random direction. shift_r = pixel_scale * 0.5 ud = galsim.UniformDeviate(rng) theta = ud() * 2. * math.pi dx = shift_r * math.cos(theta) dy = shift_r * math.sin(theta) gal = gal.shift(dx, dy) # Make the final image, convolving with the psf final = galsim.Convolve([psf, gal]) # Draw the image final.drawImage(sub_gal_image) # Now add noise to get our desired S/N # See demo5.py for more info about how this works. sky_level_pixel = sky_level * pixel_scale**2 noise = galsim.PoissonNoise(rng, sky_level=sky_level_pixel) sub_gal_image.addNoiseSNR(noise, gal_signal_to_noise) # For the PSF image, we also shift the PSF by the same amount. psf = psf.shift(dx, dy) # Draw the PSF image: # We use real space integration over the pixels to avoid some of the # artifacts that can show up with Fourier convolution. # The level of the artifacts is quite low, but when drawing with # so little noise, they are apparent with ds9's zscale viewing. psf.drawImage(sub_psf_image, method='real_space') # Again, add noise, but at higher S/N this time. sub_psf_image.addNoiseSNR(noise, psf_signal_to_noise) logger.info('Galaxy (%d,%d): position relative to center = %s', ix, iy, str(pos)) logger.info('Done making images of postage stamps') # Now write the images to disk. images = [gal_image, psf_image] galsim.fits.writeMulti(images, file_name) logger.info('Wrote image to %r', file_name)
def main(args): # Draw the original galaxies and measure their shapes rgc = galsim.RealGalaxyCatalog(catalog_filename, dir=catalog_dir) g1_list = [] g2_list = [] sigma_list = [] for i in range(args.first_index, args.first_index + args.nitems): test_image = galsim.ImageD(imsize, imsize) real_galaxy = galsim.RealGalaxy(rgc, index=i) real_galaxy.original_image.draw(test_image) shape = CatchAdaptiveMomErrors(test_image) if shape == -10: g1_list.append(-10) g2_list.append(-10) sigma_list.append(-10) else: g1_list.append(shape.observed_shape.g1) g2_list.append(shape.observed_shape.g2) sigma_list.append(shape.moments_sigma) g1_list = numpy.array(g1_list) g2_list = numpy.array(g2_list) # Define the config dictionaries we will use for all the following tests config_and_file_list = get_config(nitems=args.nitems, first_index=args.first_index, file_root=args.file_root) i = 1 # For printing status statements # Now, run through the various things we need to test in loops. for base_config, output_file in config_and_file_list: f = open(output_file, 'w') for padding in padding_list: # Amount of padding for interpolant in use_interpolants: # Possible interpolants print 'Angle test ', for angle in angle_list: # Possible rotation angles print i, i += 1 print_results(f, g1_list, g2_list, sigma_list, test_realgalaxy(base_config, angle=angle, x_interpolant=interpolant, padding=padding, seed=rseed + args.first_index), first_index=args.first_index) print_results(f, g1_list, g2_list, sigma_list, test_realgalaxy(base_config, angle=angle, k_interpolant=interpolant, padding=padding, seed=rseed + args.first_index), first_index=args.first_index) print '' print 'Shear/magnification test ', for ( g1, g2, mag ) in shear_and_magnification_list: # Shear and magnification print i, "(", g1, g2, ")", i += 1 print_results(f, g1_list, g2_list, sigma_list, test_realgalaxy(base_config, shear=(g1, g2), magnification=mag, x_interpolant=interpolant, padding=padding, seed=rseed + args.first_index), first_index=args.first_index) print_results(f, g1_list, g2_list, sigma_list, test_realgalaxy(base_config, shear=(g1, g2), magnification=mag, k_interpolant=interpolant, padding=padding, seed=rseed + args.first_index), first_index=args.first_index) print '' for shift in shift_list: print i, "(", shift, ")", i += 1 print_results(f, g1_list, g2_list, sigma_list, test_realgalaxy(base_config, shift=shift, x_interpolant=interpolant, padding=padding, seed=rseed + args.first_index), first_index=args.first_index) print_results(f, g1_list, g2_list, sigma_list, test_realgalaxy(base_config, shift=shift, k_interpolant=interpolant, padding=padding, seed=rseed + args.first_index), first_index=args.first_index) print '' f.close()
def test_whiten(): """Test the options in config to whiten images """ real_gal_dir = os.path.join('..','examples','data') real_gal_cat = 'real_galaxy_catalog_23.5_example.fits' config = { 'image' : { 'type' : 'Single', 'random_seed' : 1234, 'pixel_scale' : 0.05, }, 'stamp' : { 'type' : 'Basic', 'size' : 32, }, 'gal' : { 'type' : 'RealGalaxy', 'index' : 79, 'flux' : 100, }, 'psf' : { # This is really slow if we don't convolve by a PSF. 'type' : 'Gaussian', 'sigma' : 0.05 }, 'input' : { 'real_catalog' : { 'dir' : real_gal_dir , 'file_name' : real_gal_cat } } } # First build by hand (no whitening yet) rng = galsim.BaseDeviate(1234 + 1) rgc = galsim.RealGalaxyCatalog(os.path.join(real_gal_dir, real_gal_cat)) gal = galsim.RealGalaxy(rgc, index=79, flux=100, rng=rng) psf = galsim.Gaussian(sigma=0.05) final = galsim.Convolve(gal,psf) im1a = final.drawImage(nx=32, ny=32, scale=0.05) # Compare to what config builds galsim.config.ProcessInput(config) im1b, cv1b = galsim.config.BuildStamp(config, do_noise=False) np.testing.assert_equal(cv1b, 0.) np.testing.assert_equal(im1b.array, im1a.array) # Now add whitening, but no noise yet. cv1a = final.noise.whitenImage(im1a) print('From whiten, current_var = ',cv1a) galsim.config.RemoveCurrent(config) config['image']['noise'] = { 'whiten' : True, } im1c, cv1c = galsim.config.BuildStamp(config, do_noise=False) print('From BuildStamp, current_var = ',cv1c) np.testing.assert_equal(cv1c, cv1a) np.testing.assert_equal(im1c.array, im1a.array) rng1 = rng.duplicate() # Save current state of rng # 1. Gaussian noise ##### config['image']['noise'] = { 'type' : 'Gaussian', 'variance' : 50, 'whiten' : True, } galsim.config.RemoveCurrent(config) im2a = im1a.copy() im2a.addNoise(galsim.GaussianNoise(sigma=math.sqrt(50-cv1a), rng=rng)) im2b, cv2b = galsim.config.BuildStamp(config) np.testing.assert_almost_equal(cv2b, 50) np.testing.assert_almost_equal(im2b.array, im2a.array, decimal=5) # If whitening already added too much noise, raise an exception config['image']['noise']['variance'] = 1.e-5 try: np.testing.assert_raises(RuntimeError, galsim.config.BuildStamp,config) except ImportError: pass # 2. Poisson noise ##### config['image']['noise'] = { 'type' : 'Poisson', 'sky_level_pixel' : 50, 'whiten' : True, } galsim.config.RemoveCurrent(config) im3a = im1a.copy() sky = 50 - cv1a rng.reset(rng1.duplicate()) im3a.addNoise(galsim.PoissonNoise(sky_level=sky, rng=rng)) im3b, cv3b = galsim.config.BuildStamp(config) np.testing.assert_almost_equal(cv3b, 50, decimal=5) np.testing.assert_almost_equal(im3b.array, im3a.array, decimal=5) # It's more complicated if the sky is quoted per arcsec and the wcs is not uniform. config2 = galsim.config.CopyConfig(config) galsim.config.RemoveCurrent(config2) config2['image']['sky_level'] = 100 config2['image']['wcs'] = { 'type' : 'UVFunction', 'ufunc' : '0.05*x + 0.001*x**2', 'vfunc' : '0.05*y + 0.001*y**2', } del config2['image']['pixel_scale'] del config2['wcs'] config2['image']['noise']['symmetrize'] = 4 # Also switch to symmetrize, just to mix it up. del config2['image']['noise']['whiten'] rng.reset(1234+1) # Start fresh, since redoing the whitening/symmetrizing wcs = galsim.UVFunction(ufunc='0.05*x + 0.001*x**2', vfunc='0.05*y + 0.001*y**2') im3c = galsim.Image(32,32, wcs=wcs) im3c = final.drawImage(im3c) cv3c = final.noise.symmetrizeImage(im3c,4) sky = galsim.Image(im3c.bounds, wcs=wcs) wcs.makeSkyImage(sky, 100) mean_sky = np.mean(sky.array) im3c += sky extra_sky = 50 - cv3c im3c.addNoise(galsim.PoissonNoise(sky_level=extra_sky, rng=rng)) im3d, cv3d = galsim.config.BuildStamp(config2) np.testing.assert_almost_equal(cv3d, 50 + mean_sky, decimal=4) np.testing.assert_almost_equal(im3d.array, im3c.array, decimal=5) config['image']['noise']['sky_level_pixel'] = 1.e-5 try: np.testing.assert_raises(RuntimeError, galsim.config.BuildStamp,config) except ImportError: pass # 3. CCDNoise ##### config['image']['noise'] = { 'type' : 'CCD', 'sky_level_pixel' : 25, 'read_noise' : 5, 'gain' : 1, 'whiten' : True, } galsim.config.RemoveCurrent(config) im4a = im1a.copy() rn = math.sqrt(25-cv1a) rng.reset(rng1.duplicate()) im4a.addNoise(galsim.CCDNoise(sky_level=25, read_noise=rn, gain=1, rng=rng)) im4b, cv4b = galsim.config.BuildStamp(config) np.testing.assert_almost_equal(cv4b, 50, decimal=5) np.testing.assert_almost_equal(im4b.array, im4a.array, decimal=5) # Repeat with gain != 1 config['image']['noise']['gain'] = 3.7 galsim.config.RemoveCurrent(config) im5a = im1a.copy() rn = math.sqrt(25-cv1a * 3.7**2) rng.reset(rng1.duplicate()) im5a.addNoise(galsim.CCDNoise(sky_level=25, read_noise=rn, gain=3.7, rng=rng)) im5b, cv5b = galsim.config.BuildStamp(config) np.testing.assert_almost_equal(cv5b, 50, decimal=5) np.testing.assert_almost_equal(im5b.array, im5a.array, decimal=5) # And again with a non-trivial sky image galsim.config.RemoveCurrent(config2) config2['image']['noise'] = config['image']['noise'] config2['image']['noise']['symmetrize'] = 4 del config2['image']['noise']['whiten'] rng.reset(1234+1) im5c = galsim.Image(32,32, wcs=wcs) im5c = final.drawImage(im5c) cv5c = final.noise.symmetrizeImage(im5c, 4) sky = galsim.Image(im5c.bounds, wcs=wcs) wcs.makeSkyImage(sky, 100) mean_sky = np.mean(sky.array) im5c += sky rn = math.sqrt(25-cv5c * 3.7**2) im5c.addNoise(galsim.CCDNoise(sky_level=25, read_noise=rn, gain=3.7, rng=rng)) im5d, cv5d = galsim.config.BuildStamp(config2) np.testing.assert_almost_equal(cv5d, 50 + mean_sky, decimal=4) np.testing.assert_almost_equal(im5d.array, im5c.array, decimal=5) config['image']['noise']['sky_level_pixel'] = 1.e-5 config['image']['noise']['read_noise'] = 0 try: np.testing.assert_raises(RuntimeError, galsim.config.BuildStamp,config) except ImportError: pass # 4. COSMOSNoise ##### file_name = os.path.join(galsim.meta_data.share_dir,'acs_I_unrot_sci_20_cf.fits') config['image']['noise'] = { 'type' : 'COSMOS', 'file_name' : file_name, 'variance' : 50, 'whiten' : True, } galsim.config.RemoveCurrent(config) im6a = im1a.copy() rng.reset(rng1.duplicate()) noise = galsim.getCOSMOSNoise(file_name=file_name, variance=50, rng=rng) noise -= galsim.UncorrelatedNoise(cv1a, rng=rng, wcs=noise.wcs) im6a.addNoise(noise) im6b, cv6b = galsim.config.BuildStamp(config) np.testing.assert_almost_equal(cv6b, 50, decimal=5) np.testing.assert_almost_equal(im6b.array, im6a.array, decimal=5) config['image']['noise']['variance'] = 1.e-5 del config['_current_cn_tag'] try: np.testing.assert_raises(RuntimeError, galsim.config.BuildStamp,config) except ImportError: pass