def test_crg_noise_draw_transform_commutativity(): """Test commutativity of ChromaticRealGalaxy correlated noise under operations of drawImage and applying transformations. """ LSST_i = galsim.Bandpass(os.path.join(bppath, "LSST_r.dat"), 'nm') f606w_cat = galsim.RealGalaxyCatalog('AEGIS_F606w_catalog.fits', dir=image_dir) f814w_cat = galsim.RealGalaxyCatalog('AEGIS_F814w_catalog.fits', dir=image_dir) psf = galsim.Gaussian(fwhm=0.6) crg = galsim.ChromaticRealGalaxy([f606w_cat, f814w_cat], id=14886, maxk=psf.maxk) factor = 1.5 g1 = g2 = 0.1 mu = 1.2 theta = 45 * galsim.degrees jac = [1.1, 0.1, -0.1, 1.2] orig = galsim.Convolve(crg, psf) orig.drawImage(LSST_i) draw_transform_img = galsim.ImageD(16, 16, scale=0.2) transform_draw_img = draw_transform_img.copy() multiplied = orig * factor multiplied.drawImage(LSST_i) # needed to populate noise property (orig.noise * factor**2).drawImage(image=draw_transform_img) multiplied.noise.drawImage(image=transform_draw_img) np.testing.assert_array_almost_equal(draw_transform_img.array, transform_draw_img.array) divided = orig / factor divided.drawImage(LSST_i) (orig.noise / factor**2).drawImage(image=draw_transform_img) divided.noise.drawImage(image=transform_draw_img) np.testing.assert_array_almost_equal(draw_transform_img.array, transform_draw_img.array) expanded = orig.expand(factor) expanded.drawImage(LSST_i) orig.noise.expand(factor).drawImage(image=draw_transform_img) expanded.noise.drawImage(image=transform_draw_img) np.testing.assert_array_almost_equal(draw_transform_img.array, transform_draw_img.array) dilated = orig.dilate(factor) dilated.drawImage(LSST_i) orig.noise.dilate(factor).drawImage(image=draw_transform_img) dilated.noise.drawImage(image=transform_draw_img) np.testing.assert_array_almost_equal(draw_transform_img.array, transform_draw_img.array) magnified = orig.magnify(mu) magnified.drawImage(LSST_i) orig.noise.magnify(mu).drawImage(image=draw_transform_img) magnified.noise.drawImage(image=transform_draw_img) np.testing.assert_array_almost_equal(draw_transform_img.array, transform_draw_img.array) lensed = orig.lens(g1, g2, mu) lensed.drawImage(LSST_i) orig.noise.lens(g1, g2, mu).drawImage(image=draw_transform_img) lensed.noise.drawImage(image=transform_draw_img) np.testing.assert_array_almost_equal(draw_transform_img.array, transform_draw_img.array) rotated = orig.rotate(theta) rotated.drawImage(LSST_i) orig.noise.rotate(theta).drawImage(image=draw_transform_img) rotated.noise.drawImage(image=transform_draw_img) np.testing.assert_array_almost_equal(draw_transform_img.array, transform_draw_img.array) sheared = orig.shear(g1=g1, g2=g2) sheared.drawImage(LSST_i) orig.noise.shear(g1=g1, g2=g2).drawImage(image=draw_transform_img) sheared.noise.drawImage(image=transform_draw_img) np.testing.assert_array_almost_equal(draw_transform_img.array, transform_draw_img.array) transformed = orig.transform(*jac) transformed.drawImage(LSST_i) orig.noise.transform(*jac).drawImage(image=draw_transform_img) transformed.noise.drawImage(image=transform_draw_img) np.testing.assert_array_almost_equal(draw_transform_img.array, transform_draw_img.array)
def test_fwhm(): """Test the calculateFWHM method. """ # Compare the calculation for a simple Gaussian. g1 = galsim.Gaussian(sigma=5, flux=1.7) print('g1 native fwhm = ', g1.fwhm) print('g1.calculateFWHM = ', g1.calculateFWHM()) # These should be exactly equal. np.testing.assert_equal( g1.fwhm, g1.calculateFWHM(), err_msg="Gaussian.calculateFWHM() returned wrong value.") # Check for a convolution of two Gaussians. Should be equivalent, but now will need to # do the calculation. g2 = galsim.Convolve(galsim.Gaussian(sigma=3, flux=1.3), galsim.Gaussian(sigma=4, flux=23)) test_fwhm = g2.calculateFWHM() print('g2.calculateFWHM = ', test_fwhm) print('ratio - 1 = ', test_fwhm / g1.fwhm - 1) np.testing.assert_almost_equal( test_fwhm / g1.fwhm, 1.0, decimal=3, err_msg="Gaussian.calculateFWHM() is not accurate.") # The default scale already accurate to around 3 dp. Using scale = 0.1 is accurate to 8 dp. test_fwhm = g2.calculateFWHM(scale=0.1) print('g2.calculateFWHM(scale=0.1) = ', test_fwhm) print('ratio - 1 = ', test_fwhm / g1.fwhm - 1) np.testing.assert_almost_equal( test_fwhm / g1.fwhm, 1.0, decimal=8, err_msg="Gaussian.calculateFWHM(scale=0.1) is not accurate.") # Finally, we don't expect this to be accurate, but make sure the code can handle having # only the central pixel higher than half-maximum. test_fwhm = g2.calculateFWHM(scale=20) print('g2.calculateFWHM(scale=20) = ', test_fwhm) print('ratio - 1 = ', test_fwhm / g1.fwhm - 1) np.testing.assert_almost_equal( test_fwhm / g1.fwhm / 10, 0.1, decimal=1, err_msg="Gaussian.calculateFWHM(scale=20) is not accurate.") # Next, use an Exponential profile e1 = galsim.Exponential(scale_radius=5, flux=1.7) # The true fwhm for this is analytic, but not an attribute. e1_fwhm = 2. * np.log(2.0) * e1.scale_radius print('true e1 fwhm = ', e1_fwhm) # Test with the default scale and size. test_fwhm = e1.calculateFWHM() print('e1.calculateFWHM = ', test_fwhm) print('ratio - 1 = ', test_fwhm / e1_fwhm - 1) np.testing.assert_almost_equal( test_fwhm / e1_fwhm, 1.0, decimal=3, err_msg="Exponential.calculateFWHM() is not accurate.") # The default scale already accurate to around 3 dp. Using scale = 0.1 is accurate to 7 dp. # We can also decrease the size, which should still be accurate, but maybe a little faster. # Go a bit more that fwhm in units of the pixels. size = int(1.2 * e1_fwhm / 0.1) test_fwhm = e1.calculateFWHM(scale=0.1, size=size) print('e1.calculateFWHM(scale=0.1) = ', test_fwhm) print('ratio - 1 = ', test_fwhm / e1_fwhm - 1) np.testing.assert_almost_equal( test_fwhm / e1_fwhm, 1.0, decimal=7, err_msg="Exponential.calculateFWHM(scale=0.1) is not accurate.") # Check that it works if the centroid is not at the origin e3 = e1.shift(2, 3) test_fwhm = e3.calculateFWHM(scale=0.1) print('e3.calculateFWHM(scale=0.1) = ', test_fwhm) print('ratio - 1 = ', test_fwhm / e1_fwhm - 1) np.testing.assert_almost_equal( test_fwhm / e1_fwhm, 1.0, decimal=6, err_msg="shifted Exponential FWHM is not accurate.") # Can set a centroid manually. This should be equivalent to the default. print('e3.centroid = ', e3.centroid()) test_fwhm = e3.calculateFWHM(scale=0.1, centroid=e3.centroid()) np.testing.assert_almost_equal( test_fwhm / e1_fwhm, 1.0, decimal=6, err_msg="shifted FWHM with explicit centroid is not accurate.") # Check the image version. im = e1.drawImage(scale=0.1, method='sb') test_fwhm = im.calculateFWHM(Imax=e1.xValue(0, 0)) print('im.calculateFWHM() = ', test_fwhm) print('ratio - 1 = ', test_fwhm / e1_fwhm - 1) np.testing.assert_almost_equal( test_fwhm / e1_fwhm, 1.0, decimal=6, err_msg="image.calculateFWHM is not accurate.") # Check that a non-square image works correctly. Also, not centered anywhere in particular. bounds = galsim.BoundsI(-1234, -1234 + size * 2, 8234, 8234 + size) offset = galsim.PositionD(29, 1) im = e1.drawImage(scale=0.1, bounds=bounds, offset=offset, method='sb') test_fwhm = im.calculateFWHM(Imax=e1.xValue(0, 0), center=im.trueCenter() + offset) print('im.calculateFWHM() = ', test_fwhm) print('ratio - 1 = ', test_fwhm / e1_fwhm - 1) np.testing.assert_almost_equal( test_fwhm / e1_fwhm, 1.0, decimal=6, err_msg="non-square image.calculateFWHM is not accurate.")
def test_real_galaxy_ideal(): """Test accuracy of various calculations with fake Gaussian RealGalaxy vs. ideal expectations""" import time t1 = time.time() # read in faked Gaussian RealGalaxy from file rgc = galsim.RealGalaxyCatalog(catalog_file, image_dir) rg = galsim.RealGalaxy(rgc, index=ind_fake) # as a side note, make sure it behaves okay given a legit RNG and a bad RNG # or when trying to specify the galaxy too many ways rg_1 = galsim.RealGalaxy(rgc, index=ind_fake, rng=galsim.BaseDeviate(1234)) rg_2 = galsim.RealGalaxy(rgc, random=True) try: np.testing.assert_raises(TypeError, galsim.RealGalaxy, rgc, index=ind_fake, rng='foo') np.testing.assert_raises(AttributeError, galsim.RealGalaxy, rgc, index=ind_fake, id=0) np.testing.assert_raises(AttributeError, galsim.RealGalaxy, rgc, index=ind_fake, random=True) np.testing.assert_raises(AttributeError, galsim.RealGalaxy, rgc, id=0, random=True) np.testing.assert_raises(AttributeError, galsim.RealGalaxy, rgc) except ImportError: print 'The assert_raises tests require nose' do_pickle( rgc, lambda x: [ x.getGal(ind_fake), x.getPSF(ind_fake), x.getNoiseProperties(ind_fake) ]) do_pickle( rgc, lambda x: drawNoise(x.getNoise(ind_fake, rng=galsim.BaseDeviate(123)))) do_pickle(rgc) do_pickle( rg, lambda x: [ x.gal_image, x.psf_image, repr(x.noise), x.original_psf.flux, x.original_gal.flux, x.flux ]) do_pickle(rg, lambda x: x.drawImage(nx=20, ny=20, scale=0.7)) do_pickle(rg_1, lambda x: x.drawImage(nx=20, ny=20, scale=0.7)) do_pickle(rg) do_pickle(rg_1) ## for the generation of the ideal right answer, we need to add the intrinsic shape of the ## galaxy and the lensing shear using the rule for addition of distortions which is ugly, but oh ## well: (d1, d2) = galsim.utilities.g1g2_to_e1e2(fake_gal_shear1, fake_gal_shear2) (d1app, d2app) = galsim.utilities.g1g2_to_e1e2(targ_applied_shear1, targ_applied_shear2) denom = 1.0 + d1 * d1app + d2 * d2app dapp_sq = d1app**2 + d2app**2 d1tot = (d1 + d1app + d2app / dapp_sq * (1.0 - np.sqrt(1.0 - dapp_sq)) * (d2 * d1app - d1 * d2app)) / denom d2tot = (d2 + d2app + d1app / dapp_sq * (1.0 - np.sqrt(1.0 - dapp_sq)) * (d1 * d2app - d2 * d1app)) / denom # convolve with a range of Gaussians, with and without shear (note, for this test all the # original and target ePSFs are Gaussian - there's no separate pixel response so that everything # can be calculated analytically) for tps in targ_pixel_scale: for tpf in targ_PSF_fwhm: for tps1 in targ_PSF_shear1: for tps2 in targ_PSF_shear2: print 'tps,tpf,tps1,tps2 = ', tps, tpf, tps1, tps2 # make target PSF targ_PSF = galsim.Gaussian(fwhm=tpf).shear(g1=tps1, g2=tps2) # simulate image sim_image = galsim.simReal(rg, targ_PSF, tps, g1=targ_applied_shear1, g2=targ_applied_shear2, rand_rotate=False, target_flux=fake_gal_flux) # galaxy sigma, in units of pixels on the final image sigma_ideal = (fake_gal_fwhm / tps) * fwhm_to_sigma # compute analytically the expected galaxy moments: mxx_gal, myy_gal, mxy_gal = ellip_to_moments( d1tot, d2tot, sigma_ideal) # compute analytically the expected PSF moments: targ_PSF_e1, targ_PSF_e2 = galsim.utilities.g1g2_to_e1e2( tps1, tps2) targ_PSF_sigma = (tpf / tps) * fwhm_to_sigma mxx_PSF, myy_PSF, mxy_PSF = ellip_to_moments( targ_PSF_e1, targ_PSF_e2, targ_PSF_sigma) # get expected e1, e2, sigma for the PSF-convolved image tot_e1, tot_e2, tot_sigma = moments_to_ellip( mxx_gal + mxx_PSF, myy_gal + myy_PSF, mxy_gal + mxy_PSF) # compare with images that are expected expected_gaussian = galsim.Gaussian(flux=fake_gal_flux, sigma=tps * tot_sigma) expected_gaussian = expected_gaussian.shear(e1=tot_e1, e2=tot_e2) expected_image = galsim.ImageD(sim_image.array.shape[0], sim_image.array.shape[1]) expected_gaussian.drawImage(expected_image, scale=tps, method='no_pixel') printval(expected_image, sim_image) np.testing.assert_array_almost_equal( sim_image.array, expected_image.array, decimal=3, err_msg= "Error in comparison of ideal Gaussian RealGalaxy calculations" ) t2 = time.time() print 'time for %s = %.2f' % (funcname(), t2 - t1)
def test_scattered(): """Test aspects of building an Scattered image """ import copy import time t1 = time.time() # Name some variables to make it easier to be sure they are the same value in the config dict # as when we build the image manually. size = 48 stamp_size = 20 scale = 0.45 flux = 17 sigma = 0.7 x1 = 23.1 y1 = 27.3 x2 = 13.4 y2 = 31.9 x3 = 39.8 y3 = 19.7 # This part of the config will be the same for all tests base_config = {'gal': {'type': 'Gaussian', 'sigma': sigma, 'flux': flux}} # Check that the stamps are centered at the correct location for both odd and even stamp size. base_config['image'] = { 'type': 'Scattered', 'size': size, 'pixel_scale': scale, 'stamp_size': stamp_size, 'image_pos': { 'type': 'XY', 'x': x1, 'y': y1 }, 'nobjects': 1 } for convention in [0, 1]: for test_stamp_size in [stamp_size, stamp_size + 1]: # Deep copy to make sure we don't have any "current_val" caches present. config = copy.deepcopy(base_config) config['image']['stamp_size'] = test_stamp_size config['image']['index_convention'] = convention image, _, _, _ = galsim.config.BuildImage(config) np.testing.assert_equal(image.getXMin(), convention) np.testing.assert_equal(image.getYMin(), convention) xgrid, ygrid = np.meshgrid( np.arange(size) + image.getXMin(), np.arange(size) + image.getYMin()) obs_flux = np.sum(image.array) cenx = np.sum(xgrid * image.array) / flux ceny = np.sum(ygrid * image.array) / flux ixx = np.sum((xgrid - cenx)**2 * image.array) / flux ixy = np.sum((xgrid - cenx) * (ygrid - ceny) * image.array) / flux iyy = np.sum((ygrid - ceny)**2 * image.array) / flux np.testing.assert_almost_equal(obs_flux / flux, 1, decimal=3) np.testing.assert_almost_equal(cenx, x1, decimal=3) np.testing.assert_almost_equal(ceny, y1, decimal=3) np.testing.assert_almost_equal(ixx / (sigma / scale)**2, 1, decimal=1) np.testing.assert_almost_equal(ixy, 0., decimal=3) np.testing.assert_almost_equal(iyy / (sigma / scale)**2, 1, decimal=1) # Check that stamp_xsize, stamp_ysize, image_pos use the object count, rather than the # image count. config = copy.deepcopy(base_config) config['image'] = { 'type': 'Scattered', 'size': size, 'pixel_scale': scale, 'stamp_xsize': { 'type': 'Sequence', 'first': stamp_size }, 'stamp_ysize': { 'type': 'Sequence', 'first': stamp_size }, 'image_pos': { 'type': 'List', 'items': [ galsim.PositionD(x1, y1), galsim.PositionD(x2, y2), galsim.PositionD(x3, y3) ] }, 'nobjects': 3 } image, _, _, _ = galsim.config.BuildImage(config) image2 = galsim.ImageF(size, size, scale=scale) image2.setZero() gal = galsim.Gaussian(sigma=sigma, flux=flux) for (i, x, y) in [(0, x1, y1), (1, x2, y2), (2, x3, y3)]: stamp = galsim.ImageF(stamp_size + i, stamp_size + i, scale=scale) if (stamp_size + i) % 2 == 0: x += 0.5 y += 0.5 ix = int(np.floor(x + 0.5)) iy = int(np.floor(y + 0.5)) stamp.setCenter(ix, iy) dx = x - ix dy = y - iy gal.drawImage(stamp, offset=(dx, dy)) b = image2.bounds & stamp.bounds image2[b] += stamp[b] np.testing.assert_almost_equal(image.array, image2.array) t2 = time.time() print 'time for %s = %.2f' % (funcname(), t2 - t1)
def test_sigma(): """Test the calculateMomentRadius method. """ # Compare the calculation for a simple Gaussian. g1 = galsim.Gaussian(sigma=5, flux=1.7) print('g1 native sigma = ', g1.sigma) print('g1.calculateMomentRadius = ', g1.calculateMomentRadius()) # These should be exactly equal. np.testing.assert_equal( g1.sigma, g1.calculateMomentRadius(), err_msg="Gaussian.calculateMomentRadius() returned wrong value.") np.testing.assert_equal( g1.sigma, g1.calculateMomentRadius(rtype='trace'), err_msg="Gaussian.calculateMomentRadius(trace) returned wrong value.") np.testing.assert_equal( g1.sigma, g1.calculateMomentRadius(rtype='det'), err_msg="Gaussian.calculateMomentRadius(det) returned wrong value.") np.testing.assert_equal( (g1.sigma, g1.sigma), g1.calculateMomentRadius(rtype='both'), err_msg="Gaussian.calculateMomentRadius(both) returned wrong value.") # Check for a convolution of two Gaussians. Should be equivalent, but now will need to # do the calculation. g2 = galsim.Convolve(galsim.Gaussian(sigma=3, flux=1.3), galsim.Gaussian(sigma=4, flux=23)) test_sigma = g2.calculateMomentRadius() print('g2.calculateMomentRadius = ', test_sigma) print('ratio - 1 = ', test_sigma / g1.sigma - 1) np.testing.assert_almost_equal( test_sigma / g1.sigma, 1.0, decimal=1, err_msg="Gaussian.calculateMomentRadius() is not accurate.") # The default scale and size is only accurate to around 1 dp. Using scale = 0.1 is accurate # to 4 dp. test_sigma = g2.calculateMomentRadius(scale=0.1) print('g2.calculateMomentRadius(scale=0.1) = ', test_sigma) print('ratio - 1 = ', test_sigma / g1.sigma - 1) np.testing.assert_almost_equal( test_sigma / g1.sigma, 1.0, decimal=4, err_msg="Gaussian.calculateMomentRadius(scale=0.1) is not accurate.") # In this case, the different calculations are eqivalent: np.testing.assert_almost_equal( test_sigma, g2.calculateMomentRadius(scale=0.1, rtype='trace'), err_msg="Gaussian.calculateMomentRadius(trace) is not accurate.") np.testing.assert_almost_equal( test_sigma, g2.calculateMomentRadius(scale=0.1, rtype='det'), err_msg="Gaussian.calculateMomentRadius(trace) is not accurate.") np.testing.assert_almost_equal( (test_sigma, test_sigma), g2.calculateMomentRadius(scale=0.1, rtype='both'), err_msg="Gaussian.calculateMomentRadius(trace) is not accurate.") # However, when we shear it, the default (det) measure stays equal to the original sigma, but # the trace measure increases by a factor of (1-e^2)^0.25 g3 = g2.shear(e1=0.4, e2=0.3) esq = 0.4**2 + 0.3**2 sheared_sigma = g3.calculateMomentRadius(scale=0.1) print('g3.calculateMomentRadius(scale=0.1) = ', sheared_sigma) print('ratio - 1 = ', sheared_sigma / g1.sigma - 1) sheared_sigma2 = g3.calculateMomentRadius(scale=0.1, rtype='trace') print('g3.calculateMomentRadius(scale=0.1,trace) = ', sheared_sigma2) print('ratio = ', sheared_sigma2 / g1.sigma) print('(1-e^2)^-0.25 = ', (1 - esq)**-0.25) print('ratio - 1 = ', sheared_sigma2 / (g1.sigma * (1. - esq)**-0.25) - 1) np.testing.assert_almost_equal( sheared_sigma / g1.sigma, 1.0, decimal=4, err_msg= "sheared Gaussian.calculateMomentRadius(scale=0.1) is not accurate.") np.testing.assert_almost_equal( sheared_sigma2 / (g1.sigma * (1. - esq)**-0.25), 1.0, decimal=4, err_msg= "sheared Gaussian.calculateMomentRadius(scale=0.1,trace) is not accurate." ) # Next, use an Exponential profile e1 = galsim.Exponential(scale_radius=5, flux=1.7) # The true "sigma" for this is analytic, but not an attribute. e1_sigma = np.sqrt(3.0) * e1.scale_radius print('true e1 sigma = sqrt(3) * e1.scale_radius = ', e1_sigma) # Test with the default scale and size. test_sigma = e1.calculateMomentRadius() print('e1.calculateMomentRadius = ', test_sigma) print('ratio - 1 = ', test_sigma / e1_sigma - 1) np.testing.assert_almost_equal( test_sigma / e1_sigma, 1.0, decimal=1, err_msg="Exponential.calculateMomentRadius() is not accurate.") # The default scale and size is only accurate to around 1 dp. This time we have to both # decrease the scale and also increase the size to get 4 dp of precision. test_sigma = e1.calculateMomentRadius(scale=0.1, size=2000) print('e1.calculateMomentRadius(scale=0.1) = ', test_sigma) print('ratio - 1 = ', test_sigma / e1_sigma - 1) np.testing.assert_almost_equal( test_sigma / e1_sigma, 1.0, decimal=4, err_msg="Exponential.calculateMomentRadius(scale=0.1) is not accurate." ) # Check that it works if the centroid is not at the origin e3 = e1.shift(2, 3) test_sigma = e3.calculateMomentRadius(scale=0.1, size=2000) print('e1.calculateMomentRadius(scale=0.1) = ', test_sigma) print('ratio - 1 = ', test_sigma / e1_sigma - 1) np.testing.assert_almost_equal( test_sigma / e1_sigma, 1.0, decimal=4, err_msg="shifted Exponential MomentRadius is not accurate.") # Can set a centroid manually. This should be equivalent to the default. print('e3.centroid = ', e3.centroid()) test_sigma = e3.calculateMomentRadius(scale=0.1, size=2000, centroid=e3.centroid()) np.testing.assert_almost_equal( test_sigma / e1_sigma, 1.0, decimal=4, err_msg="shifted MomentRadius with explicit centroid is not accurate.") # Check the image version. size = 2000 im = e1.drawImage(scale=0.1, nx=size, ny=size) test_sigma = im.calculateMomentRadius() print('im.calculateMomentRadius() = ', test_sigma) print('ratio - 1 = ', test_sigma / e1_sigma - 1) np.testing.assert_almost_equal( test_sigma / e1_sigma, 1.0, decimal=4, err_msg="image.calculateMomentRadius is not accurate.") # Check that a non-square image works correctly. Also, not centered anywhere in particular. bounds = galsim.BoundsI(-1234, -1234 + size * 2, 8234, 8234 + size) offset = galsim.PositionD(29, 1) im = e1.drawImage(scale=0.1, bounds=bounds, offset=offset) test_hlr = im.calculateMomentRadius(center=im.trueCenter() + offset) print('im.calculateMomentRadius() = ', test_sigma) print('ratio - 1 = ', test_sigma / e1_sigma - 1) np.testing.assert_almost_equal( test_sigma / e1_sigma, 1.0, decimal=4, err_msg="non-square image.calculateMomentRadius is not accurate.")
nmult = 5 # i.e., consider 1*imsize linear scale, 2*imsize linear scale, up to (nmult-1)*imsize linear scale psf_fwhm = 3.5 pixel_scale = 1.0 ntest = 200 sky_level = 1.0e6 gal_flux = 1.0e4 gal_e1 = 0.2 gal_e2 = 0.1 sn_noisy = 25.0 # desired S/N for noisy case seed = 1234 save_im = 0 print "Doing all tests with ", ntest, " trials" # for 48x48 image, Gaussian PSF with FWHM=2.5 pix and Gaussian galaxy with same size, make objects epsf = galsim.Gaussian(fwhm=psf_fwhm) # let's say this is the epsf gal = galsim.Gaussian(fwhm=psf_fwhm, flux=gal_flux) exp_sigma = math.sqrt(2.) * gal.getSigma() gal_hlr = gal.getHalfLightRadius() gal.applyShear(e1=gal_e1, e2=gal_e2) obj = galsim.Convolve(gal, epsf) # do tests with noiseless images of various sizes for sizemult in range(1, nmult): this_imsize = imsize * sizemult im_obj = galsim.ImageF(this_imsize, this_imsize) im_epsf = galsim.ImageF(this_imsize, this_imsize) im_obj = obj.draw(image=im_obj, dx=pixel_scale) im_epsf = epsf.draw(image=im_epsf, dx=pixel_scale) if save_im: im_obj.write('im_obj.fits')
def test_gaussian(): """Test the generation of a specific Gaussian profile against a known result. """ savedImg = galsim.fits.read(os.path.join(imgdir, "gauss_1.fits")) savedImg.setCenter(0, 0) dx = 0.2 myImg = galsim.ImageF(savedImg.bounds, scale=dx) myImg.setCenter(0, 0) gauss = galsim.Gaussian(flux=1, sigma=1) # Reference images were made with old centering, which is equivalent to use_true_center=False. myImg = gauss.drawImage(myImg, scale=dx, method="sb", use_true_center=False) np.testing.assert_array_almost_equal( myImg.array, savedImg.array, 5, err_msg="Using GSObject Gaussian disagrees with expected result") np.testing.assert_almost_equal( myImg.array.sum(dtype=float) * dx**2, myImg.added_flux, 5, err_msg="Gaussian profile GSObject::draw returned wrong added_flux") # Check a non-square image print(myImg.bounds) recImg = galsim.ImageF(45, 66) recImg.setCenter(0, 0) recImg = gauss.drawImage(recImg, scale=dx, method="sb", use_true_center=False) np.testing.assert_array_almost_equal( recImg[savedImg.bounds].array, savedImg.array, 5, err_msg= "Drawing Gaussian on non-square image disagrees with expected result") np.testing.assert_almost_equal( recImg.array.sum(dtype=float) * dx**2, recImg.added_flux, 5, err_msg= "Gaussian profile GSObject::draw on non-square image returned wrong added_flux" ) # Check with default_params gauss = galsim.Gaussian(flux=1, sigma=1, gsparams=default_params) gauss.drawImage(myImg, scale=0.2, method="sb", use_true_center=False) np.testing.assert_array_almost_equal( myImg.array, savedImg.array, 5, err_msg= "Using GSObject Gaussian with default_params disagrees with expected result" ) gauss = galsim.Gaussian(flux=1, sigma=1, gsparams=galsim.GSParams()) gauss.drawImage(myImg, scale=0.2, method="sb", use_true_center=False) np.testing.assert_array_almost_equal( myImg.array, savedImg.array, 5, err_msg= "Using GSObject Gaussian with GSParams() disagrees with expected result" ) # Use non-unity values. gauss = galsim.Gaussian(flux=1.7, sigma=2.3) check_basic(gauss, "Gaussian") # Test photon shooting. do_shoot(gauss, myImg, "Gaussian") # Test kvalues do_kvalue(gauss, myImg, "Gaussian") # Check picklability do_pickle(galsim.GSParams()) # Check GSParams explicitly here too. do_pickle( galsim.GSParams(minimum_fft_size=12, maximum_fft_size=40, folding_threshold=1.e-1, maxk_threshold=2.e-1, kvalue_accuracy=3.e-1, xvalue_accuracy=4.e-1, shoot_accuracy=5.e-1, realspace_relerr=6.e-1, realspace_abserr=7.e-1, integration_relerr=8.e-1, integration_abserr=9.e-1)) do_pickle(gauss, lambda x: x.drawImage(method='no_pixel')) do_pickle(gauss) # Should raise an exception if >=2 radii are provided. assert_raises(TypeError, galsim.Gaussian, sigma=3, half_light_radius=1, fwhm=2) assert_raises(TypeError, galsim.Gaussian, half_light_radius=1, fwhm=2) assert_raises(TypeError, galsim.Gaussian, sigma=3, fwhm=2) assert_raises(TypeError, galsim.Gaussian, sigma=3, half_light_radius=1) # Or none. assert_raises(TypeError, galsim.Gaussian) # Finally, test the noise property for things that don't have any noise set. assert gauss.noise is None # And accessing the attribute from the class should indicate that it is a lazyproperty assert 'lazy_property' in str(galsim.GSObject._noise) # And check that trying to use GSObject directly is an error. assert_raises(NotImplementedError, galsim.GSObject)
e20 = numpy.random.normal(loc=0.0, scale=0.3, size=n) g10 = numpy.random.uniform(-0.01, 0.01, m) g20 = numpy.random.uniform(-0.01, 0.01, m) for i in range(n): e20[i] = 0 if ((e10[i]**2) + (e20[i]**2) > 0.64): e10[i] = 0.0 e20[i] = 0.0 gsparams=galsim.GSParams(folding_threshold=1.e-2,maxk_threshold=2.e-3,\ xvalue_accuracy=1.e-4,kvalue_accuracy=1.e-4,\ shoot_accuracy=1.e-4,minimum_fft_size=64) #psf1=galsim.Moffat(fwhm=psf_fwhm[0],beta=2.5,gsparams=gsparams) #to be continue #psf1=psf1.shear(e1=e1_psf,e2=e2_psf) psf1 = galsim.Gaussian(fwhm=psf_fwhm[0], gsparams=gsparams) #psf1=galsim.Kolmogorov(fwhm=psf_fwhm[0],gsparams=gsparams) gal1 = galsim.Gaussian(half_light_radius=2, gsparams=gsparams) gal2 = galsim.Exponential(half_light_radius=1, gsparams=gsparams) gal3 = galsim.DeVaucouleurs(half_light_radius=1, gsparams=gsparams) gal4 = galsim.Sersic(half_light_radius=1, n=2.5, gsparams=gsparams) #to be continue psf = psf1 final_epsf_image = psf1.drawImage(scale=pixel_scale) gal = [gal1, gal2, gal3, gal4] BJe1 = [[], [], [], []] BJe2 = [[], [], [], []] RGe1 = [[], [], [], []]
def test_interleaveImages(): import time t1 = time.time() # 1a) With galsim Gaussian g = galsim.Gaussian(sigma=3.7, flux=1000.) gal = galsim.Convolve([g, galsim.Pixel(1.0)]) im_list = [] offset_list = [] n = 2 for j in xrange(n): for i in xrange(n): im = galsim.Image(16 * n, 16 * n) offset = galsim.PositionD(-(i + 0.5) / n + 0.5, -(j + 0.5) / n + 0.5) offset_list.append(offset) gal.drawImage(image=im, method='no_pixel', offset=offset, scale=0.5) im_list.append(im) scale = im.scale # Input to N as an int img = galsim.utilities.interleaveImages(im_list, n, offsets=offset_list) im = galsim.Image(16 * n * n, 16 * n * n) g = galsim.Gaussian(sigma=3.7, flux=1000. * n * n) gal = galsim.Convolve([g, galsim.Pixel(1.0)]) gal.drawImage(image=im, method='no_pixel', offset=galsim.PositionD(0.0, 0.0), scale=1. * scale / n) np.testing.assert_array_equal(img.array,im.array,\ err_msg="Interleaved Gaussian images do not match") assert im.wcs == img.wcs # 1b) With im_list and offsets permuted offset_list = [] # An elegant way of generating the default offsets DX = np.arange(0.0, -1.0, -1.0 / n) DX -= DX.mean() DY = DX for dy in DY: for dx in DX: offset = galsim.PositionD(dx, dy) offset_list.append(offset) np.random.seed(42) # for generating the same random permutation everytime rand_idx = np.random.permutation(len(offset_list)) im_list_randperm = [im_list[idx] for idx in rand_idx] offset_list_randperm = [offset_list[idx] for idx in rand_idx] # Input to N as a tuple img_randperm = galsim.utilities.interleaveImages( im_list_randperm, (n, n), offsets=offset_list_randperm) np.testing.assert_array_equal(img_randperm.array,img.array,\ err_msg="Interleaved images do not match when 'offsets' is supplied") assert img_randperm.scale == img.scale # 1c) Catching errors in offsets offset_list = [] im_list = [] n = 5 # Generate approximate offsets DX = np.array([-0.67, -0.33, 0., 0.33, 0.67]) DY = DX for dy in DY: for dx in DX: offset = galsim.PositionD(dx, dy) offset_list.append(offset) im = galsim.Image(16, 16) gal.drawImage(image=im, offset=offset, method='no_pixel') im_list.append(im) try: N = (n, n) np.testing.assert_raises(ValueError, galsim.utilities.interleaveImages, im_list, N, offset_list) except ImportError: print "The assert_raises tests require nose" offset_list = [] im_list = [] n = 5 DX = np.arange(0., 1., 1. / n) DY = DX for dy in DY: for dx in DX: offset = galsim.PositionD(dx, dy) offset_list.append(offset) im = galsim.Image(16, 16) gal.drawImage(image=im, offset=offset, method='no_pixel') im_list.append(im) try: N = (n, n) np.testing.assert_raises(ValueError, galsim.utilities.interleaveImages, im_list, N, offset_list) except ImportError: print "The assert_raises tests require nose" # 2a) Increase resolution along one direction - square to rectangular images n = 2 g = galsim.Gaussian(sigma=3.7, flux=100.) gal1 = g.shear(g=1. * (n**2 - 1) / (n**2 + 1), beta=0.0 * galsim.radians) im_list = [] offset_list = [] # Generating offsets in a natural way DY = np.arange(0.0, 1.0, 1.0 / (n * n)) DY -= DY.mean() for dy in DY: im = galsim.Image(16, 16) offset = galsim.PositionD(0.0, dy) offset_list.append(offset) gal1.drawImage(im, offset=offset, method='no_pixel', scale=2.0) im_list.append(im) img = galsim.utilities.interleaveImages(im_list, N=[1, n**2], offsets=offset_list, add_flux=False, suppress_warnings=True) im = galsim.Image(16, 16 * n * n) # The interleaved image has the total flux averaged out since `add_flux = False' gal = galsim.Gaussian(sigma=3.7 * n, flux=100.) gal.drawImage(image=im, method='no_pixel', scale=2.0) np.testing.assert_array_equal( im.array, img.array, err_msg="Sheared gaussian not interleaved correctly") assert img.wcs == galsim.JacobianWCS(2.0, 0.0, 0.0, 2. / (n**2)) # 2b) Increase resolution along one direction - rectangular to square images n = 2 g = galsim.Gaussian(sigma=3.7, flux=100.) gal2 = g.shear(g=1. * (n**2 - 1) / (n**2 + 1), beta=90. * galsim.degrees) im_list = [] offset_list = [] # Generating offsets in a natural way DX = np.arange(0.0, 1.0, 1.0 / n**2) DX -= DX.mean() for dx in DX: offset = galsim.PositionD(dx, 0.0) offset_list.append(offset) im = galsim.Image(16, 16 * n * n) gal2.drawImage(im, offset=offset, method='no_pixel', scale=3.0) im_list.append(im) img = galsim.utilities.interleaveImages(im_list, N=np.array([n**2, 1]), offsets=offset_list, suppress_warnings=True) im = galsim.Image(16 * n * n, 16 * n * n) gal = galsim.Gaussian(sigma=3.7, flux=100. * n * n) scale = im_list[0].scale gal.drawImage(image=im, scale=1. * scale / n, method='no_pixel') np.testing.assert_array_equal( im.array, img.array, err_msg="Sheared gaussian not interleaved correctly") assert img.wcs == galsim.JacobianWCS(1. * scale / n**2, 0.0, 0.0, scale) t2 = time.time() print 'time for %s = %.2f' % (funcname(), t2 - t1)
def main(argv): """ Make a fits image cube using real COSMOS galaxies from a catalog describing the training sample. - The number of images in the cube matches the number of rows in the catalog. - Each image size is computed automatically by GalSim based on the Nyquist size. - Both galaxies and stars. - PSF is a double Gaussian, the same for each galaxy. - Galaxies are randomly rotated to remove the imprint of any lensing shears in the COSMOS data. - The same shear is applied to each galaxy. - Noise is Poisson using a nominal sky value of 1.e6 ADU/arcsec^2, the noise in the original COSMOS data. """ logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("demo6") # Define some parameters we'll use below. cat_file_name = 'real_galaxy_catalog_23.5_example.fits' dir = 'data' # Make output directory if not already present. if not os.path.isdir('output'): os.mkdir('output') cube_file_name = os.path.join('output','cube_real.fits') psf_file_name = os.path.join('output','psf_real.fits') random_seed = 1512413 sky_level = 1.e6 # ADU / arcsec^2 pixel_scale = 0.16 # arcsec gal_flux = 1.e5 # arbitrary choice, makes nice (not too) noisy images gal_g1 = -0.027 # gal_g2 = 0.031 # gal_mu = 1.082 # mu = ( (1-kappa)^2 - g1^2 - g2^2 )^-1 psf_inner_fwhm = 0.6 # arcsec psf_outer_fwhm = 2.3 # arcsec psf_inner_fraction = 0.8 # fraction of total PSF flux in the inner Gaussian psf_outer_fraction = 0.2 # fraction of total PSF flux in the inner Gaussian ngal = 100 logger.info('Starting demo script 6 using:') logger.info(' - real galaxies from catalog %r',cat_file_name) logger.info(' - double Gaussian PSF') logger.info(' - pixel scale = %.2f',pixel_scale) logger.info(' - Applied gravitational shear = (%.3f,%.3f)',gal_g1,gal_g2) logger.info(' - Poisson noise (sky level = %.1e).', sky_level) # Read in galaxy catalog # Note: dir is the directory both for the catalog itself and also the directory prefix # for the image files listed in the catalog. # If the images are in a different directory, you may also specify image_dir, which gives # the relative path from dir to wherever the images are located. real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=dir) logger.info('Read in %d real galaxies from catalog', real_galaxy_catalog.nobjects) # Make the double Gaussian PSF psf1 = galsim.Gaussian(fwhm = psf_inner_fwhm, flux = psf_inner_fraction) psf2 = galsim.Gaussian(fwhm = psf_outer_fwhm, flux = psf_outer_fraction) psf = psf1+psf2 # Draw the PSF with no noise. psf_image = psf.drawImage(scale = pixel_scale) # write to file psf_image.write(psf_file_name) logger.info('Created PSF and wrote to file %r',psf_file_name) # Build the images all_images = [] for k in range(ngal): logger.debug('Start work on image %d',k) t1 = time.time() # Initialize the random number generator we will be using. rng = galsim.UniformDeviate(random_seed+k+1) gal = galsim.RealGalaxy(real_galaxy_catalog, index = k) logger.debug(' Read in training sample galaxy and PSF from file') t2 = time.time() # Set the flux gal = gal.withFlux(gal_flux) # Rotate by a random angle theta = 2.*math.pi * rng() * galsim.radians gal = gal.rotate(theta) # Apply the desired shear gal = gal.shear(g1=gal_g1, g2=gal_g2) # Also apply a magnification mu = ( (1-kappa)^2 - |gamma|^2 )^-1 # This conserves surface brightness, so it scales both the area and flux. gal = gal.magnify(gal_mu) # Make the combined profile final = galsim.Convolve([psf, gal]) # Offset by up to 1/2 pixel in each direction # We had previously (in demo4 and demo5) used shift(dx,dy) as a way to shift the center of # the image. Since that is applied to the galaxy, the units are arcsec (since the galaxy # profile itself doesn't know about the pixel scale). Here, the offset applies to the # drawn image, which does know about the pixel scale, so the units of offset are pixels, # not arcsec. Here, we apply an offset of up to half a pixel in each direction. dx = rng() - 0.5 dy = rng() - 0.5 # Draw the profile if k == 0: # Note that the offset argument may be a galsim.PositionD object or a tuple (dx,dy). im = final.drawImage(scale=pixel_scale, offset=(dx,dy)) xsize, ysize = im.array.shape else: im = galsim.ImageF(xsize,ysize) final.drawImage(im, scale=pixel_scale, offset=(dx,dy)) logger.debug(' Drew image') t3 = time.time() # Add a constant background level background = sky_level * pixel_scale**2 im += background # Add Poisson noise. This time, we don't give a sky_level, since we have already # added it to the image, so we don't want any more added. The sky_level parameter # really defines how much _extra_ sky should be added above what is already in the image. im.addNoise(galsim.PoissonNoise(rng)) logger.debug(' Added Poisson noise') t4 = time.time() # Store that into the list of all images all_images += [im] t5 = time.time() logger.debug(' Times: %f, %f, %f, %f',t2-t1, t3-t2, t4-t3, t5-t4) logger.info('Image %d: size = %d x %d, total time = %f sec', k, xsize, ysize, t5-t1) logger.info('Done making images of galaxies') # Now write the image to disk. # We write the images to a fits data cube. galsim.fits.writeCube(all_images, cube_file_name) logger.info('Wrote image to fits data cube %r',cube_file_name)
def test_deltaFunction(): """Test the generation of a Delta function profile """ # Check construction with no arguments gives expected result delta = galsim.DeltaFunction() np.testing.assert_almost_equal(delta.flux, 1.0) check_basic(delta, "DeltaFunction") do_pickle(delta) # Check with default_params delta = galsim.DeltaFunction(flux=1, gsparams=default_params) np.testing.assert_almost_equal(delta.flux, 1.0) test_flux = 17.9 delta = galsim.DeltaFunction(flux=test_flux) np.testing.assert_almost_equal(delta.flux, test_flux) check_basic(delta, "DeltaFunction") do_pickle(delta) gsp = galsim.GSParams(xvalue_accuracy=1.e-8, kvalue_accuracy=1.e-8) delta2 = galsim.DeltaFunction(flux=test_flux, gsparams=gsp) assert delta2 != delta assert delta2 == delta.withGSParams(gsp) # Test operations with no-ops on DeltaFunction delta_shr = delta.shear(g1=0.3, g2=0.1) np.testing.assert_almost_equal(delta_shr.flux, test_flux) delta_dil = delta.dilate(2.0) np.testing.assert_almost_equal(delta_dil.flux, test_flux) delta_rot = delta.rotate(45 * galsim.radians) np.testing.assert_almost_equal(delta_rot.flux, test_flux) delta_tfm = delta.transform(dudx=1.25, dudy=0., dvdx=0., dvdy=0.8) np.testing.assert_almost_equal(delta_tfm.flux, test_flux) delta_shift = delta.shift(1.,2.) np.testing.assert_almost_equal(delta_shift.flux, test_flux) # These aren't no ops, since they do in fact alter the flux. delta_exp = delta.expand(2.0) np.testing.assert_almost_equal(delta_exp.flux, test_flux * 4) delta_mag = delta.magnify(2.0) np.testing.assert_almost_equal(delta_mag.flux, test_flux * 2) delta_tfm = delta.transform(dudx=1.4, dudy=0.2, dvdx=0.4, dvdy=1.2) np.testing.assert_almost_equal(delta_tfm.flux, test_flux * (1.4*1.2-0.2*0.4)) # Test simple translation of DeltaFunction delta2 = delta.shift(1.,2.) offcen = galsim.PositionD(1, 2) np.testing.assert_equal(delta2.centroid, offcen) assert delta2.xValue(offcen) > 1.e10 np.testing.assert_almost_equal(delta2.xValue(galsim.PositionD(0,0)), 0) # Test photon shooting. gauss = galsim.Gaussian(sigma = 1.0) delta_conv = galsim.Convolve(gauss,delta) myImg = galsim.ImageF() do_shoot(delta_conv,myImg,"Delta Function") # Test kvalues do_kvalue(delta_conv,myImg,"Delta Function")
def main(argv): """ About as simple as it gets: - Use a circular Gaussian profile for the galaxy. - Convolve it by a circular Gaussian PSF. - Add Gaussian noise to the image. """ # In non-script code, use getLogger(__name__) at module scope instead. logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("demo1") gal_flux = 1.e5 # total counts on the image gal_sigma = 2. # arcsec psf_sigma = 1. # arcsec pixel_scale = 0.2 # arcsec / pixel noise = 30. # standard deviation of the counts in each pixel logger.info('Starting demo script 1 using:') logger.info(' - circular Gaussian galaxy (flux = %.1e, sigma = %.1f),',gal_flux,gal_sigma) logger.info(' - circular Gaussian PSF (sigma = %.1f),',psf_sigma) logger.info(' - pixel scale = %.2f,',pixel_scale) logger.info(' - Gaussian noise (sigma = %.2f).',noise) # Define the galaxy profile gal = galsim.Gaussian(flux=gal_flux, sigma=gal_sigma) logger.debug('Made galaxy profile') # Define the PSF profile psf = galsim.Gaussian(flux=1., sigma=psf_sigma) # PSF flux should always = 1 logger.debug('Made PSF profile') # Final profile is the convolution of these # Can include any number of things in the list, all of which are convolved # together to make the final flux profile. final = galsim.Convolve([gal, psf]) logger.debug('Convolved components into final profile') # Draw the image with a particular pixel scale, given in arcsec/pixel. # The returned image has a member, added_flux, which is gives the total flux actually added to # the image. One could use this value to check if the image is large enough for some desired # accuracy level. Here, we just ignore it. image = final.drawImage(scale=pixel_scale) logger.debug('Made image of the profile: flux = %f, added_flux = %f',gal_flux,image.added_flux) # Add Gaussian noise to the image with specified sigma image.addNoise(galsim.GaussianNoise(sigma=noise)) logger.debug('Added Gaussian noise') # Write the image to a file if not os.path.isdir('output'): os.mkdir('output') file_name = os.path.join('output','demo1.fits') # Note: if the file already exists, this will overwrite it. image.write(file_name) logger.info('Wrote image to %r' % file_name) # using %r adds quotes around filename for us results = image.FindAdaptiveMom() logger.info('HSM reports that the image has observed shape and size:') logger.info(' e1 = %.3f, e2 = %.3f, sigma = %.3f (pixels)', results.observed_shape.e1, results.observed_shape.e2, results.moments_sigma) logger.info('Expected values in the limit that pixel response and noise are negligible:') logger.info(' e1 = %.3f, e2 = %.3f, sigma = %.3f', 0.0, 0.0, math.sqrt(gal_sigma**2 + psf_sigma**2)/pixel_scale)
def compute_val(x0s, hlr1, hlr2, flux1, flux2, e1, e2, plot_last_gal=False): """Computes blending parametrs for pair of blended input galaxy parametrs""" var = 1e-10 nx, ny = 161, 161 y0 = 0 noise = galsim.GaussianNoise(sigma=var**0.5) psf = galsim.Gaussian(fwhm=0.7) psf_im = psf.drawImage(scale=0.2, nx=19, ny=19) psf_array = psf_im.array r = np.ones([len(x0s), 2]) * -1 unit_dist = np.ones(len(x0s)) * -1 num_detections = np.zeros(len(x0s)) flux_b_on = np.zeros([len(x0s), 2]) ret = [] sig = [0, 0] for i, x0 in enumerate(x0s): im1_b_off = gal_im(hlr1, flux1, e1, 0, y0, psf, nx, ny) im2_b_off = gal_im(hlr2, flux2, e2, x0, y0, psf, nx, ny) im_b_on = im1_b_off + im2_b_off variance_array1 = get_var_arr(im1_b_off, noise, snr=500) variance_array2 = get_var_arr(im2_b_off, noise, snr=500) variance_array = get_var_arr(im_b_on, noise, snr=500) catalog1, hfpt1 = deblend_run(im1_b_off.array, variance_array1, psf_array) catalog2, hfpt2 = deblend_run(im2_b_off.array, variance_array2, psf_array) catalog, hfpt_blended = deblend_run(im_b_on.array, variance_array, psf_array) children = catalog[catalog['deblend_nChild'] == 0] if (len(catalog1) != 1) or (len(catalog2) != 1): print "multi detection in blending off: SKIPPED " continue elif len(children) == 0: print "No children in blending on" continue elif len(children) > 2: print "More than 2 children in blending on" continue elif np.isnan(children['base_SdssShape_flux']).any(): print "Nan value found" continue tree = spatial.KDTree(zip([nx / 2, nx / 2 + x0], [ny / 2, ny / 2 + y0])) z = zip(children['base_SdssCentroid_x'], children['base_SdssCentroid_y']) match = tree.query(z, distance_upper_bound=15) select = ~np.isinf(match[0]) if len(select) == 0: print "detected center do not match true object" continue flux_b_on[i][match[1][select]] = children['base_SdssShape_flux'] # All good! Now procced with measurement num_detections[i] = len(children) ret.append(i) sig[0] = get_sig_m(catalog1['base_SdssShape_xx'], catalog1['base_SdssShape_yy'], catalog1['base_SdssShape_xy']) sig[1] = get_sig_m(catalog2['base_SdssShape_xx'], catalog2['base_SdssShape_yy'], catalog2['base_SdssShape_xy']) r[i][0] = met_rho(hfpt1[0], hfpt2) r[i][1] = met_rho(hfpt2[0], hfpt1) # import ipdb;ipdb.set_trace() if len(children) == 1: sig0 = get_sig_m(children['base_SdssShape_xx'], children['base_SdssShape_yy'], children['base_SdssShape_xy']) unit_dist[i] = sig0 + sig[(match[1][select] + 1) % 2] else: sigs = get_sig_m(children['base_SdssShape_xx'], children['base_SdssShape_yy'], children['base_SdssShape_xy']) unit_dist[i] = sigs.sum() s = np.array([x0s / unit_dist] * 2).T # Make sure galaxy detected most is first in output array q = np.where(flux_b_on == 0) amb = mode(q[1]).mode[0] rho = np.array([r.T[(amb + 1) % 2], r.T[amb]]).T flux_out = np.array([flux_b_on.T[(amb + 1) % 2], flux_b_on.T[amb]]).T met = [rho[ret], s[ret]] if plot_last_gal is True: plot_gal(im1_b_off, im2_b_off, im_b_on, psf_im) return met, num_detections[ret], flux_out[ret], amb
def test_randwalk_defaults(): """ Create a random walk galaxy and test that the getters work for default inputs """ # try constructing with mostly defaults npoints = 100 hlr = 8.0 rng = galsim.BaseDeviate(1234) rw = check_dep(galsim.RandomWalk, npoints, half_light_radius=hlr, rng=rng) assert rw.npoints == npoints, "expected npoints==%d, got %d" % (npoints, rw.npoints) assert rw.input_half_light_radius==hlr,\ "expected hlr==%g, got %g" % (hlr, rw.input_half_light_radius) nobj = len(rw.points) assert nobj == npoints, "expected %d objects, got %d" % (npoints, nobj) pts = rw.points assert pts.shape == ( npoints, 2), "expected (%d,2) shape for points, got %s" % (npoints, pts.shape) np.testing.assert_almost_equal(rw.centroid.x, np.mean(pts[:, 0])) np.testing.assert_almost_equal(rw.centroid.y, np.mean(pts[:, 1])) gsp = galsim.GSParams(xvalue_accuracy=1.e-8, kvalue_accuracy=1.e-8) rng2 = galsim.BaseDeviate(1234) rw2 = check_dep(galsim.RandomWalk, npoints, half_light_radius=hlr, rng=rng2, gsparams=gsp) assert rw2 != rw assert rw2 == rw.withGSParams(gsp) # Check that they produce identical images. psf = galsim.Gaussian(sigma=0.8) conv1 = galsim.Convolve(rw.withGSParams(gsp), psf) conv2 = galsim.Convolve(rw2, psf) im1 = conv1.drawImage() im2 = conv2.drawImage() assert im1 == im2 # Check that image is not sensitive to use of rng by other objects. rng3 = galsim.BaseDeviate(1234) rw3 = check_dep(galsim.RandomWalk, npoints, half_light_radius=hlr, rng=rng3) rng3.discard(523) conv1 = galsim.Convolve(rw, psf) conv3 = galsim.Convolve(rw3, psf) im1 = conv1.drawImage() im3 = conv2.drawImage() assert im1 == im3 # Run some basic tests of correctness check_basic(conv1, "RandomWalk") im = galsim.ImageD(64, 64, scale=0.5) do_shoot(conv1, im, "RandomWalk") do_kvalue(conv1, im, "RandomWalk") do_pickle(rw) do_pickle(conv1) do_pickle(conv1, lambda x: x.drawImage(scale=1))
def main(argv): """ Make a fits image cube where each frame has two images of the same galaxy drawn with regular FFT convolution and with photon shooting. We do this for 5 different PSFs and 5 different galaxies, each with 4 different (random) fluxes, sizes, and shapes. """ logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("demo7") # To turn off logging: #logger.propagate = False # Define some parameters we'll use below. # Make output directory if not already present. if not os.path.isdir('output'): os.mkdir('output') file_name = os.path.join('output','cube_phot.fits.gz') random_seed = 553728 sky_level = 1.e4 # ADU / arcsec^2 pixel_scale = 0.28 # arcsec nx = 64 ny = 64 gal_flux_min = 1.e4 # Range for galaxy flux gal_flux_max = 1.e5 gal_hlr_min = 0.3 # arcsec gal_hlr_max = 1.3 # arcsec gal_e_min = 0. # Range for ellipticity gal_e_max = 0.8 psf_fwhm = 0.65 # arcsec # This script is set up as a comparison between using FFTs for doing the convolutions and # shooting photons. The two methods have trade-offs in speed and accuracy which vary # with the kind of profile being drawn and the S/N of the object, among other factors. # In addition, for each method, there are a number of parameters GalSim uses that control # aspects of the calculation that further affect the speed and accuracy. # # We encapsulate these parameters with an object called GSParams. The default values # are intended to be accurate enough for normal precision shear tests, without sacrificing # too much speed. # # Any PSF or galaxy object can be given a gsparams argument on construction that can # have different values to make the calculation more or less accurate (typically trading # off for speed or memory). # # In this script, we adjust some of the values slightly, just to show you how it works. # You could play around with these values and see what effect they have on the drawn images. # Usually, it requires a pretty drastic change in these parameters for you to be able to # notice the difference by eye. But subtle effects that may impact the shapes of galaxies # can happen well before then. # Type help(galsim.GSParams) for the complete list of parameters and more detailed # documentation, including the default values for each parameter. gsparams = galsim.GSParams( folding_threshold=1.e-2, # maximum fractional flux that may be folded around edge of FFT maxk_threshold=2.e-3, # k-values less than this may be excluded off edge of FFT xvalue_accuracy=1.e-4, # approximations in real space aim to be this accurate kvalue_accuracy=1.e-4, # approximations in fourier space aim to be this accurate shoot_accuracy=1.e-4, # approximations in photon shooting aim to be this accurate minimum_fft_size=64) # minimum size of ffts logger.info('Starting demo script 7') # Make the PSF profiles: psf1 = galsim.Gaussian(fwhm = psf_fwhm, gsparams=gsparams) psf2 = galsim.Moffat(fwhm = psf_fwhm, beta = 2.4, gsparams=gsparams) psf3_inner = galsim.Gaussian(fwhm = psf_fwhm, flux = 0.8, gsparams=gsparams) psf3_outer = galsim.Gaussian(fwhm = 2*psf_fwhm, flux = 0.2, gsparams=gsparams) psf3 = psf3_inner + psf3_outer atmos = galsim.Gaussian(fwhm = psf_fwhm, gsparams=gsparams) # The OpticalPSF and set of Zernike values chosen below correspond to a reasonably well aligned, # smallish ~0.3m / 12 inch diameter telescope with a central obscuration of ~0.12m or 5 inches # diameter, being used in optical wavebands. # In the Noll convention, the value of the Zernike coefficient also gives the RMS optical path # difference across a circular pupil. An RMS difference of ~0.5 or larger indicates that parts # of the wavefront are in fully destructive interference, and so we might expect aberrations to # become strong when Zernike aberrations summed in quadrature approach 0.5 wave. # The aberrations chosen in this case correspond to operating close to a 0.25 wave RMS optical # path difference. Unlike in demo3, we specify the aberrations by making a list that we pass # in using the 'aberrations' kwarg. The order of aberrations starting from index 4 is defocus, # astig1, astig2, coma1, coma2, trefoil1, trefoil2, spher as in the Noll convention. # We ignore the first 4 values so that the index number corresponds to the Zernike index # in the Noll convention. This will be particularly convenient once we start allowing # coefficients beyond spherical (index 11). c.f. The Wikipedia page about the Noll indices: # # http://en.wikipedia.org/wiki/Zernike_polynomials#Zernike_polynomials aberrations = [ 0.0 ] * 12 # Set the initial size. aberrations[4] = 0.06 # Noll index 4 = Defocus aberrations[5:7] = [ 0.12, -0.08 ] # Noll index 5,6 = Astigmatism aberrations[7:9] = [ 0.07, 0.04 ] # Noll index 7,8 = Coma aberrations[11] = -0.13 # Noll index 11 = Spherical # You could also define these all at once if that is more convenient: #aberrations = [0.0, 0.0, 0.0, 0.0, 0.06, 0.12, -0.08, 0.07, 0.04, 0.0, 0.0, -0.13] optics = galsim.OpticalPSF( lam_over_diam = 0.6 * psf_fwhm, obscuration = 0.4, aberrations = aberrations, gsparams=gsparams) psf4 = galsim.Convolve([atmos, optics]) # Convolve inherits the gsparams from the first # item in the list. (Or you can supply a gsparams # argument explicitly if you want to override this.) atmos = galsim.Kolmogorov(fwhm = psf_fwhm, gsparams=gsparams) optics = galsim.Airy(lam_over_diam = 0.3 * psf_fwhm, gsparams=gsparams) psf5 = galsim.Convolve([atmos,optics]) psfs = [psf1, psf2, psf3, psf4, psf5] psf_names = ["Gaussian", "Moffat", "Double Gaussian", "OpticalPSF", "Kolmogorov * Airy"] psf_times = [0,0,0,0,0] psf_fft_times = [0,0,0,0,0] psf_phot_times = [0,0,0,0,0] # Make the galaxy profiles: gal1 = galsim.Gaussian(half_light_radius = 1, gsparams=gsparams) gal2 = galsim.Exponential(half_light_radius = 1, gsparams=gsparams) gal3 = galsim.DeVaucouleurs(half_light_radius = 1, gsparams=gsparams) gal4 = galsim.Sersic(half_light_radius = 1, n = 2.5, gsparams=gsparams) # A Sersic profile may be truncated if desired. # The units for this are expected to be arcsec (or specifically -- whatever units # you are using for all the size values as defined by the pixel_scale). bulge = galsim.Sersic(half_light_radius = 0.7, n = 3.2, trunc = 8.5, gsparams=gsparams) disk = galsim.Sersic(half_light_radius = 1.2, n = 1.5, gsparams=gsparams) gal5 = 0.4*bulge + 0.6*disk # Net half-light radius is only approximate for this one. gals = [gal1, gal2, gal3, gal4, gal5] gal_names = ["Gaussian", "Exponential", "Devaucouleurs", "n=2.5 Sersic", "Bulge + Disk"] gal_times = [0,0,0,0,0] gal_fft_times = [0,0,0,0,0] gal_phot_times = [0,0,0,0,0] # Other times to keep track of: setup_times = 0 fft_times = 0 phot_times = 0 noise_times = 0 # Loop over combinations of psf, gal, and make 4 random choices for flux, size, shape. all_images = [] k = 0 for ipsf in range(len(psfs)): psf = psfs[ipsf] psf_name = psf_names[ipsf] for igal in range(len(gals)): gal = gals[igal] gal_name = gal_names[igal] for i in range(4): logger.debug('Start work on image %d',i) t1 = time.time() # Initialize the random number generator we will be using. rng = galsim.UniformDeviate(random_seed+k) # Generate random variates: flux = rng() * (gal_flux_max-gal_flux_min) + gal_flux_min # Use a new variable name, since we'll want to keep the original unmodified. this_gal = gal.withFlux(flux) hlr = rng() * (gal_hlr_max-gal_hlr_min) + gal_hlr_min this_gal = this_gal.dilate(hlr) beta_ellip = rng() * 2*math.pi * galsim.radians ellip = rng() * (gal_e_max-gal_e_min) + gal_e_min gal_shape = galsim.Shear(e=ellip, beta=beta_ellip) this_gal = this_gal.shear(gal_shape) # Build the final object by convolving the galaxy and PSF. final = galsim.Convolve([this_gal, psf]) # Create the large, double width output image # Rather than provide a scale= argument to the drawImage commands, we can also # set the pixel scale in the image constructor. # Note: You can also change it after the construction with im.scale=pixel_scale image = galsim.ImageF(2*nx+2, ny, scale=pixel_scale) # Assign the following two Image "views", fft_image and phot_image. # Using the syntax below, these are views into the larger image. # Changes/additions to the sub-images referenced by the views are automatically # reflected in the original image. fft_image = image[galsim.BoundsI(1, nx, 1, ny)] phot_image = image[galsim.BoundsI(nx+3, 2*nx+2, 1, ny)] logger.debug(' Read in training sample galaxy and PSF from file') t2 = time.time() # Draw the profile # This default rendering method (method='auto') usually defaults to FFT, since # that is normally the most efficient method. However, we can also set method # to 'fft' explicitly to force it to always use FFTs for the convolution # by the pixel response. (In this case, it doesn't have any effect, since # the 'auto' method would have always chosen 'fft' anyway, so this is just # for illustrative purposes.) final.drawImage(fft_image, method='fft') logger.debug(' Drew fft image. Total drawn flux = %f. .flux = %f', fft_image.array.sum(),final.getFlux()) t3 = time.time() # Add Poisson noise sky_level_pixel = sky_level * pixel_scale**2 fft_image.addNoise(galsim.PoissonNoise(rng, sky_level=sky_level_pixel)) t4 = time.time() # The next two lines are just to get the output from this demo script # to match the output from the parsing of demo7.yaml. rng = galsim.UniformDeviate(random_seed+k) rng(); rng(); rng(); rng(); # Repeat for photon shooting image. # The max_extra_noise parameter indicates how much extra noise per pixel we are # willing to tolerate. The sky noise will be adding a variance of sky_level_pixel, # so we allow up to 1% of that extra. final.drawImage(phot_image, method='phot', max_extra_noise=sky_level_pixel/100, rng=rng) t5 = time.time() # For photon shooting, galaxy already has Poisson noise, so we want to make # sure not to add that noise again! Thus, we just add sky noise, which # is Poisson with the mean = sky_level_pixel pd = galsim.PoissonDeviate(rng, mean=sky_level_pixel) # DeviateNoise just adds the action of the given deviate to every pixel. phot_image.addNoise(galsim.DeviateNoise(pd)) # For PoissonDeviate, the mean is not zero, so for a background-subtracted # image, we need to subtract the mean back off when we are done. phot_image -= sky_level_pixel logger.debug(' Added Poisson noise. Image fluxes are now %f and %f', fft_image.array.sum(), phot_image.array.sum()) t6 = time.time() # Store that into the list of all images all_images += [image] k = k+1 logger.info('%d: %s * %s, flux = %.2e, hlr = %.2f, ellip = (%.2f,%.2f)', k,gal_name, psf_name, flux, hlr, gal_shape.getE1(), gal_shape.getE2()) logger.debug(' Times: %f, %f, %f, %f, %f',t2-t1, t3-t2, t4-t3, t5-t4, t6-t5) psf_times[ipsf] += t6-t1 psf_fft_times[ipsf] += t3-t2 psf_phot_times[ipsf] += t5-t4 gal_times[igal] += t6-t1 gal_fft_times[igal] += t3-t2 gal_phot_times[igal] += t5-t4 setup_times += t2-t1 fft_times += t3-t2 phot_times += t5-t4 noise_times += t4-t3 + t6-t5 logger.info('Done making images of galaxies') logger.info('') logger.info('Some timing statistics:') logger.info(' Total time for setup steps = %f',setup_times) logger.info(' Total time for regular fft drawing = %f',fft_times) logger.info(' Total time for photon shooting = %f',phot_times) logger.info(' Total time for adding noise = %f',noise_times) logger.info('') logger.info('Breakdown by PSF type:') for ipsf in range(len(psfs)): logger.info(' %s: Total time = %f (fft: %f, phot: %f)', psf_names[ipsf],psf_times[ipsf],psf_fft_times[ipsf],psf_phot_times[ipsf]) logger.info('') logger.info('Breakdown by Galaxy type:') for igal in range(len(gals)): logger.info(' %s: Total time = %f (fft: %f, phot: %f)', gal_names[igal],gal_times[igal],gal_fft_times[igal],gal_phot_times[igal]) logger.info('') # Now write the image to disk. # With any write command, you can optionally compress the file using several compression # schemes: # 'gzip' uses gzip on the full output file. # 'bzip2' uses bzip2 on the full output file. # 'rice' uses rice compression on the image, leaving the fits headers readable. # 'gzip_tile' uses gzip in tiles on the output image, leaving the fits headers readable. # 'hcompress' uses hcompress on the image, but it is only valid for 2-d data, so it # doesn't work for writeCube. # 'plio' uses plio on the image, but it is only valid for positive integer data. # Furthermore, the first three have standard filename extensions associated with them, # so if you don't specify a compression, but the filename ends with '.gz', '.bz2' or '.fz', # the corresponding compression will be selected automatically. # In other words, the `compression='gzip'` specification is actually optional here: galsim.fits.writeCube(all_images, file_name, compression='gzip') logger.info('Wrote fft image to fits data cube %r',file_name)
def drawimg(catalog, simgalimgfilepath="test.fits", simtrugalimgfilepath=None, simpsfimgfilepath=None, gsparams=None, sersiccut=None): """ Turns a catalog as obtained from drawcat into FITS images. Only the position jitter and the pixel noise are randomized. All the other info is taken from the input catalog. So simply call me several times for the same input to get different realizations of the same galaxies. To specify the PSFs, add a meta["psf"] ImageInfo object to the catalog. :param catalog: an input catalog of galaxy shape parameters, as returned by drawcat. The corresponding stampsize must be provided as catalog.meta["stampsize"]. If you specify a psf image in catalog.meta["psf"], your catalog must of course also contain PSF coordinates for that image. If no PSF stamps are specifed, the code will look for Gaussian PSF parameters in the catalog. If such parameters are not given, no PSF convolution is done. :param simgalimgfilepath: where I write my output image of simulated and noisy galaxies :param simtrugalimgfilepath: (optional) where I write the image without convolution and noise :param simpsfimgfilepath: (optional) where I write the PSFs :param sersiccut: cuts the sersic profile at this number of rad .. note:: See this function in MomentsML v4 (great3) for attempts to speed up galsim by playing with fft params, accuracy, etc... .. note:: About speed, if you specify trunc, better express the scale radius. But scale radius is crazy dependent on n, so I keep half-light-radius http://galsim-developers.github.io/GalSim/classgalsim_1_1base_1_1_sersic.html .. note:: To use the hacks, give "metadict":{"hack":"nicobackgals"} as drawcatkwargs to sim.run.multi()... """ starttime = datetime.now() hack = catalog.meta.get("hack", None) if hack is None: # The default behavior, without specific gsparams or tricks. if gsparams is None: gsparams = galsim.GSParams(maximum_fft_size=10240) if "nx" not in catalog.meta.keys() or "ny" not in catalog.meta.keys(): raise RuntimeError( "Provide nx and ny in the meta data of the input catalog to drawimg." ) if "stampsize" not in catalog.meta.keys(): raise RuntimeError( "Provide stampsize in the meta data of the input catalog to drawimg." ) nx = catalog.meta["nx"] ny = catalog.meta["ny"] stampsize = catalog.meta["stampsize"] # The stamps I'm going to draw logger.info("Drawing images of %i galaxies on a %i x %i grid..." % (len(catalog), nx, ny)) logger.info("The stampsize for the simulated galaxies is %i." % (stampsize)) # A special function checks the combination of settings in the provided catalog: todo = checkcat(catalog) if "loadpsfimg" in todo: psfimg = catalog.meta["psf"].load( ) # Loading the actual GalSim Image psfinfo = catalog.meta["psf"] if "tru_pixel" in todo: # This is only if you want "effective pixels" larger than the actual pixels (related to SBE, normally you don't want this). pix = galsim.Pixel( catalog["tru_pixel"] [0]) # We have checked in checkcat that all values are equal. # Galsim random number generators rng = galsim.BaseDeviate() ud = galsim.UniformDeviate() # This gives a random float in [0, 1) # We prepare the big images : gal_image = galsim.ImageF(stampsize * nx, stampsize * ny) trugal_image = galsim.ImageF(stampsize * nx, stampsize * ny) psf_image = galsim.ImageF(stampsize * nx, stampsize * ny) gal_image.scale = 1.0 # we use pixels as units. Note that if you change something here, you also have to change the jitter. trugal_image.scale = 1.0 psf_image.scale = 1.0 # And loop through the catalog: for row in catalog: # Some simplistic progress indication: fracdone = float(row.index) / len(catalog) if row.index % 500 == 0: logger.info("%6.2f%% done (%i/%i) " % (fracdone * 100.0, row.index, len(catalog))) # We will draw this galaxy in a postage stamp, but first we need the bounds of this stamp. ix = int(row["ix"]) iy = int(row["iy"]) assert ix < nx and iy < ny bounds = galsim.BoundsI( ix * stampsize + 1, (ix + 1) * stampsize, iy * stampsize + 1, (iy + 1) * stampsize) # Default Galsim convention, index starts at 1. gal_stamp = gal_image[bounds] trugal_stamp = trugal_image[bounds] psf_stamp = psf_image[bounds] # We draw the desired profile profile_type = params.profile_types[row["tru_type"]] if profile_type == "Sersic": if sersiccut is None: trunc = 0 else: trunc = float(row["tru_rad"]) * sersiccut gal = galsim.Sersic(n=float(row["tru_sersicn"]), half_light_radius=float(row["tru_rad"]), flux=float(row["tru_flux"]), gsparams=gsparams, trunc=trunc) # We make this profile elliptical gal = gal.shear(g1=row["tru_g1"], g2=row["tru_g2"] ) # This adds the ellipticity to the galaxy elif profile_type == "Gaussian": gal = galsim.Gaussian(flux=float(row["tru_flux"]), sigma=float(row["tru_sigma"]), gsparams=gsparams) # We make this profile elliptical gal = gal.shear(g1=row["tru_g1"], g2=row["tru_g2"] ) # This adds the ellipticity to the galaxy elif profile_type == "EBulgeDisk": # A more advanced Bulge + Disk model # It needs GalSim version master, as of April 2017 (probably 1.5). # Get a Sersic bulge: bulge = galsim.Sersic(n=row["tru_bulge_sersicn"], half_light_radius=row["tru_bulge_hlr"], flux=row["tru_bulge_flux"]) # Make it elliptical: bulge_ell = galsim.Shear(g=row["tru_bulge_g"], beta=row["tru_theta"] * galsim.degrees) bulge = bulge.shear(bulge_ell) # Get a disk scale_radius = row[ "tru_disk_hlr"] / galsim.Exponential._hlr_factor disk = galsim.InclinedExponential( inclination=row["tru_disk_tilt"] * galsim.degrees, scale_radius=scale_radius, flux=row["tru_disk_flux"], scale_h_over_r=row["tru_disk_scale_h_over_r"]) # Rotate it in the same orientation as the bulge: disk = disk.rotate(row["tru_theta"] * galsim.degrees) # And we add those profiles, as done in GalSim demo3.py : gal = bulge + disk else: raise RuntimeError("Unknown galaxy profile!") # And now we add lensing, if s1, s2 and mu are different from no lensing... if row["tru_s1"] != 0 or row["tru_s2"] != 0 or row["tru_mu"] != 1: gal = gal.lens(float(row["tru_s1"]), float(row["tru_s2"]), float(row["tru_mu"])) else: pass #logger.info("No lensing!") # We apply some jitter to the position of this galaxy xjitter = ud( ) - 0.5 # This is the minimum amount -- should we do more, as real galaxies are not that well centered in their stamps ? yjitter = ud() - 0.5 gal = gal.shift(xjitter, yjitter) # We draw the pure unconvolved galaxy if simtrugalimgfilepath != None: gal.drawImage( trugal_stamp, method="auto") # Will convolve by the sampling pixel. # We prepare/get the PSF and do the convolution: # Should the final operation skip the convolution by the pixel (because the PSF already is in large pixels) ? skip_pixel_conv = False if "usegausspsf" in todo: if row["tru_psf_sigma"] < 0.0: raise RuntimeError("Unknown hack!") else: psf = galsim.Gaussian(flux=1., sigma=row["tru_psf_sigma"]) psf = psf.shear(g1=row["tru_psf_g1"], g2=row["tru_psf_g2"]) # Let's apply some jitter to the position of the PSF (not sure if this is required, but should not harm ?) psf_xjitter = ud() - 0.5 psf_yjitter = ud() - 0.5 psf = psf.shift(psf_xjitter, psf_yjitter) if simpsfimgfilepath != None: psf.drawImage( psf_stamp, method="auto") # Will convolve by the sampling pixel. if "tru_pixel" in todo: # Not sure if this should only apply to gaussian PSFs, but so far this seems OK. # Remember that this is an "additional" pixel convolution, not the usual sampling-related convolution that happens in drawImage. galconv = galsim.Convolve([gal, psf, pix]) else: galconv = galsim.Convolve([gal, psf]) elif "loadpsfimg" in todo: (inputpsfstamp, flag) = tools.image.getstamp(row[psfinfo.xname], row[psfinfo.yname], psfimg, psfinfo.stampsize) psfpixelscale = getattr( psfinfo, "pixelscale", 1.0 ) # Using getattr so that it works with old objects as well if psfpixelscale > 0.5: #logger.warning("You seem to be using a sampled PSF with large pixels (e.g., observed stars). I'll do my best and skip the pixel conv, but this might well lead to errors.") skip_pixel_conv = True if flag != 0: raise RuntimeError("Could not extract a %ix%i stamp at (%.2f, %.2f) from the psfimg %s" %\ (psfinfo.stampsize, psfinfo.stampsize, row[psfinfo.xname], row[psfinfo.yname], psfinfo.name)) psf = galsim.InterpolatedImage(inputpsfstamp, flux=1.0, scale=psfpixelscale) if simpsfimgfilepath != None: psf.drawImage( psf_stamp, method="no_pixel" ) # psf_stamp has a different size than inputpsfstamp, so this could lead to problems one day. #galconv = galsim.Convolve([gal,psf], real_space=False) galconv = galsim.Convolve([gal, psf]) elif "nopsf" in todo: # Nothing to do galconv = gal else: raise RuntimeError("Bug in todo.") # Draw the convolved galaxy if skip_pixel_conv == False: galconv.drawImage( gal_stamp, method="auto" ) # This will convolve by the image sampling pixel. Don't do this yourself ahead! else: #logger.warning("NOT computing any pixel convolution") galconv.drawImage( gal_stamp, method="no_pixel" ) # Simply uses pixel-center values. Know what you are doing, see doc of galsim. # And add noise to the convolved galaxy: gal_stamp.addNoise( galsim.CCDNoise(rng, sky_level=float(row["tru_sky_level"]), gain=float(row["tru_gain"]), read_noise=float(row["tru_read_noise"]))) logger.info("Done with drawing, now writing output FITS files ...") gal_image.write(simgalimgfilepath) if simtrugalimgfilepath != None: trugal_image.write(simtrugalimgfilepath) if simpsfimgfilepath != None: psf_image.write(simpsfimgfilepath) elif hack == "nicobackgals": """ This is taken and/or adapted from Nico's simulation code, to be sure to do the same thing. - Nico uses GalSim in units of arcsec (instaed of pixels), and we suspect that this affects the convolution. - Nico has different gsparams - Nico truncates Sersic profiles """ logger.info("Using special hack for nicobackgals") gsparams = galsim.GSParams(xvalue_accuracy=2.e-4, kvalue_accuracy=2.e-4, maxk_threshold=5.e-3, folding_threshold=1.e-2) pixel_scale = 0.1 if "nx" not in catalog.meta.keys() or "ny" not in catalog.meta.keys(): raise RuntimeError( "Provide nx and ny in the meta data of the input catalog to drawimg." ) if "stampsize" not in catalog.meta.keys(): raise RuntimeError( "Provide stampsize in the meta data of the input catalog to drawimg." ) nx = catalog.meta["nx"] ny = catalog.meta["ny"] stampsize = catalog.meta["stampsize"] # The stamps I'm going to draw logger.info("Drawing images of %i galaxies on a %i x %i grid..." % (len(catalog), nx, ny)) logger.info("The stampsize for the simulated galaxies is %i." % (stampsize)) # Galsim random number generators rng = galsim.BaseDeviate() ud = galsim.UniformDeviate() # This gives a random float in [0, 1) # We prepare the big image, and set the pixel scale gal_image = galsim.ImageF(stampsize * nx, stampsize * ny, scale=pixel_scale) psf = galsim.Airy(lam=800, diam=1.2, obscuration=0.3, scale_unit=galsim.arcsec, flux=1./3) + \ galsim.Airy(lam=700, diam=1.2, obscuration=0.3, scale_unit=galsim.arcsec, flux=1./3) + \ galsim.Airy(lam=600, diam=1.2, obscuration=0.3, scale_unit=galsim.arcsec, flux=1./3) for row in catalog: # Some simplistic progress indication: fracdone = float(row.index) / len(catalog) if row.index % 500 == 0: logger.info("%6.2f%% done (%i/%i) " % (fracdone * 100.0, row.index, len(catalog))) # We will draw this galaxy in a postage stamp, but first we need the bounds of this stamp. ix = int(row["ix"]) iy = int(row["iy"]) assert ix < nx and iy < ny bounds = galsim.BoundsI( ix * stampsize + 1, (ix + 1) * stampsize, iy * stampsize + 1, (iy + 1) * stampsize) # Default Galsim convention, index starts at 1. gal_stamp = gal_image[bounds] # Drawing the galaxy half_light_radius = float(row["tru_rad"]) * pixel_scale gal = galsim.Sersic(n=float(row["tru_sersicn"]), half_light_radius=half_light_radius, flux=float(row["tru_flux"]), gsparams=gsparams, trunc=half_light_radius * 4.5) # We make this profile elliptical gal = gal.shear( g1=row["tru_g1"], g2=row["tru_g2"]) # This adds the ellipticity to the galaxy # And now we add lensing, if s1, s2 and mu are different from no lensing... if row["tru_s1"] != 0 or row["tru_s2"] != 0 or row["tru_mu"] != 1: gal = gal.lens(float(row["tru_s1"]), float(row["tru_s2"]), float(row["tru_mu"])) else: pass #logger.info("No lensing!") # We apply some jitter to the position of this galaxy # it seems this has to be given in the image scale... xjitter = (ud() - 0.5) * pixel_scale yjitter = (ud() - 0.5) * pixel_scale gal = gal.shift(xjitter, yjitter) final = galsim.Convolve([psf, gal]) final.drawImage(gal_stamp) # And add noise to the convolved galaxy: gal_stamp.addNoise( galsim.CCDNoise(rng, sky_level=float(row["tru_sky_level"]), gain=float(row["tru_gain"]), read_noise=float(row["tru_read_noise"]))) logger.info("Done with drawing, now writing output FITS files ...") gal_image.write(simgalimgfilepath) else: raise RuntimeError("Unknown hack") endtime = datetime.now() logger.info("This drawing took %s" % (str(endtime - starttime)))
print "syntax: sim1.py g1gal g2gal g1psf g2psf" sys.exit (1) gal_flux=1000 gal_e1=float(sys.argv[1]) gal_e2=float(sys.argv[2]) gal_sigma=0.5/0.27 / 2.35 # Gaussian sigma such that FWHM~0.5'' psf_sigma=0.9/0.27 / 2.35 # Gaussian sigma such that FWHM~0.9'' psf_e1=float(sys.argv[3]) psf_e2=float(sys.argv[4]) psf_fluxmax=15000 # Define the true galaxy profile gal = galsim.Gaussian(flux=gal_flux, sigma=gal_sigma) gal = gal.shear(galsim.Shear(g1=gal_e1, g2=gal_e2)) # Define the PSF profile psf = galsim.Gaussian(flux=1., sigma=psf_sigma) # PSF flux should always = 1 psf = psf.shear(galsim.Shear(g1=psf_e1, g2=psf_e2)) galobserved = galsim.Convolve([gal, psf]) galobserved = galobserved.shift(0.5,0.5) # shift to be centered on a pixel galimage_nocd = galobserved.drawImage(scale=1) psf.setFlux(psf.getFlux()*psf_fluxmax/psf.xValue(0,0)) psfobserved = psf.shift(0.5,0.5) # shift to be centered on a pixel psfimage_nocd = psfobserved.drawImage(scale=1)
def test_realspace_convolve(): """Test the real-space convolution of a Moffat and a Box profile against a known result. """ dx = 0.2 # Note: Using an image created from Maple "exact" calculations. saved_img = galsim.fits.read(os.path.join(imgdir, "moffat_pixel.fits")) img = galsim.ImageF(saved_img.bounds, scale=dx) img.setCenter(0,0) # Code was formerly: # psf = galsim.Moffat(beta=1.5, truncationFWHM=4, flux=1, half_light_radius=1) # # ...but this is no longer quite so simple since we changed the handling of trunc to be in # physical units. However, the same profile can be constructed using # fwhm=1.0927449310213702, # as calculated by interval bisection in devutils/external/calculate_moffat_radii.py fwhm_backwards_compatible = 1.0927449310213702 psf = galsim.Moffat(beta=1.5, half_light_radius=1, trunc=4*fwhm_backwards_compatible, flux=1) #psf = galsim.Moffat(beta=1.5, fwhm=fwhm_backwards_compatible, #trunc=4*fwhm_backwards_compatible, flux=1) pixel = galsim.Pixel(scale=dx, flux=1.) conv = galsim.Convolve([psf,pixel],real_space=True) conv.drawImage(img,scale=dx, method="sb", use_true_center=False) np.testing.assert_array_almost_equal( img.array, saved_img.array, 5, err_msg="Using GSObject Convolve([psf,pixel]) disagrees with expected result") # Check with default_params conv = galsim.Convolve([psf,pixel],real_space=True,gsparams=default_params) conv.drawImage(img,scale=dx, method="sb", use_true_center=False) np.testing.assert_array_almost_equal( img.array, saved_img.array, 5, err_msg="Using GSObject Convolve([psf,pixel]) with default_params disagrees with " "expected result") conv = galsim.Convolve([psf,pixel],real_space=True,gsparams=galsim.GSParams()) conv.drawImage(img,scale=dx, method="sb", use_true_center=False) np.testing.assert_array_almost_equal( img.array, saved_img.array, 5, err_msg="Using GSObject Convolve([psf,pixel]) with GSParams() disagrees with " "expected result") # Other ways to do the convolution: conv = galsim.Convolve(psf,pixel,real_space=True) conv.drawImage(img,scale=dx, method="sb", use_true_center=False) np.testing.assert_array_almost_equal( img.array, saved_img.array, 5, err_msg="Using GSObject Convolve(psf,pixel) disagrees with expected result") # The real-space convolution algorithm is not (trivially) independent of the order of # the two things being convolved. So check the opposite order. conv = galsim.Convolve([pixel,psf],real_space=True) conv.drawImage(img,scale=dx, method="sb", use_true_center=False) np.testing.assert_array_almost_equal( img.array, saved_img.array, 5, err_msg="Using GSObject Convolve([pixel,psf]) disagrees with expected result") check_basic(conv, "Truncated Moffat*Box", approx_maxsb=True) # Test kvalues do_kvalue(conv,img,"Truncated Moffat*Box") # Check picklability do_pickle(conv, lambda x: x.drawImage(method='sb')) do_pickle(conv) # Check some warnings that should be raised # More than 2 with only hard edges gives a warning either way. (Different warnings though.) assert_warns(galsim.GalSimWarning, galsim.Convolve, [psf, psf, pixel]) assert_warns(galsim.GalSimWarning, galsim.Convolve, [psf, psf, pixel], real_space=False) assert_warns(galsim.GalSimWarning, galsim.Convolve, [psf, psf, pixel], real_space=True) # 2 with hard edges gives a warning if we ask it not to use real_space assert_warns(galsim.GalSimWarning, galsim.Convolve, [psf, pixel], real_space=False) # >2 of any kind give a warning if we ask it to use real_space g = galsim.Gaussian(sigma=2) assert_warns(galsim.GalSimWarning, galsim.Convolve, [g, g, g], real_space=True) # non-analytic profiles cannot do real_space d = galsim.Deconvolve(galsim.Gaussian(sigma=2)) assert_warns(galsim.GalSimWarning, galsim.Convolve, [g, d], real_space=True) assert_raises(TypeError, galsim.Convolve, [g, d], real_space='true') # Repeat some of the above for AutoConvolve and AutoCorrelate conv = galsim.AutoConvolve(psf,real_space=True) check_basic(conv, "AutoConvolve Truncated Moffat", approx_maxsb=True) do_kvalue(conv,img,"AutoConvolve Truncated Moffat") do_pickle(conv) conv = galsim.AutoCorrelate(psf,real_space=True) check_basic(conv, "AutoCorrelate Truncated Moffat", approx_maxsb=True) do_kvalue(conv,img,"AutoCorrelate Truncated Moffat") do_pickle(conv) assert_warns(galsim.GalSimWarning, galsim.AutoConvolve, psf, real_space=False) assert_warns(galsim.GalSimWarning, galsim.AutoConvolve, d, real_space=True) assert_warns(galsim.GalSimWarning, galsim.AutoCorrelate, psf, real_space=False) assert_warns(galsim.GalSimWarning, galsim.AutoCorrelate, d, real_space=True) assert_raises(TypeError, galsim.AutoConvolve, d, real_space='true') assert_raises(TypeError, galsim.AutoCorrelate, d, real_space='true')
def test_gaussian_radii(): """Test initialization of Gaussian with different types of radius specification. """ import math test_hlr = 1.7 test_fwhm = 1.8 test_sigma = 1.9 # Test constructor using half-light-radius: test_gal = galsim.Gaussian(flux=1., half_light_radius=test_hlr) hlr_sum = radial_integrate(test_gal, 0., test_hlr) print('hlr_sum = ', hlr_sum) np.testing.assert_almost_equal( hlr_sum, 0.5, decimal=4, err_msg="Error in Gaussian constructor with half-light radius") # test that fwhm provides correct FWHM got_fwhm = test_gal.fwhm test_fwhm_ratio = (test_gal.xValue(galsim.PositionD(.5 * got_fwhm, 0.)) / test_gal.xValue(galsim.PositionD(0., 0.))) print('fwhm ratio = ', test_fwhm_ratio) np.testing.assert_almost_equal( test_fwhm_ratio, 0.5, decimal=4, err_msg="Error in FWHM for Gaussian initialized with half-light radius" ) # test that sigma provides correct sigma got_sigma = test_gal.sigma test_sigma_ratio = (test_gal.xValue(galsim.PositionD(got_sigma, 0.)) / test_gal.xValue(galsim.PositionD(0., 0.))) print('sigma ratio = ', test_sigma_ratio) np.testing.assert_almost_equal( test_sigma_ratio, math.exp(-0.5), decimal=4, err_msg="Error in sigma for Gaussian initialized with half-light radius" ) # Test constructor using sigma: test_gal = galsim.Gaussian(flux=1., sigma=test_sigma) center = test_gal.xValue(galsim.PositionD(0, 0)) ratio = test_gal.xValue(galsim.PositionD(test_sigma, 0)) / center print('sigma ratio = ', ratio) np.testing.assert_almost_equal( ratio, np.exp(-0.5), decimal=4, err_msg="Error in Gaussian constructor with sigma") # then test that image indeed has the correct HLR properties when radially integrated got_hlr = test_gal.half_light_radius hlr_sum = radial_integrate(test_gal, 0., got_hlr) print('hlr_sum (profile initialized with sigma) = ', hlr_sum) np.testing.assert_almost_equal( hlr_sum, 0.5, decimal=4, err_msg= "Error in half light radius for Gaussian initialized with sigma.") # test that fwhm provides correct FWHM got_fwhm = test_gal.fwhm test_fwhm_ratio = (test_gal.xValue(galsim.PositionD(.5 * got_fwhm, 0.)) / test_gal.xValue(galsim.PositionD(0., 0.))) print('fwhm ratio = ', test_fwhm_ratio) np.testing.assert_almost_equal( test_fwhm_ratio, 0.5, decimal=4, err_msg="Error in FWHM for Gaussian initialized with sigma.") # Test constructor using FWHM: test_gal = galsim.Gaussian(flux=1., fwhm=test_fwhm) center = test_gal.xValue(galsim.PositionD(0, 0)) ratio = test_gal.xValue(galsim.PositionD(test_fwhm / 2., 0)) / center print('fwhm ratio = ', ratio) np.testing.assert_almost_equal( ratio, 0.5, decimal=4, err_msg="Error in Gaussian constructor with fwhm") # then test that image indeed has the correct HLR properties when radially integrated got_hlr = test_gal.half_light_radius hlr_sum = radial_integrate(test_gal, 0., got_hlr) print('hlr_sum (profile initialized with fwhm) = ', hlr_sum) np.testing.assert_almost_equal( hlr_sum, 0.5, decimal=4, err_msg="Error in half light radius for Gaussian initialized with FWHM." ) # test that sigma provides correct sigma got_sigma = test_gal.sigma test_sigma_ratio = (test_gal.xValue(galsim.PositionD(got_sigma, 0.)) / test_gal.xValue(galsim.PositionD(0., 0.))) print('sigma ratio = ', test_sigma_ratio) np.testing.assert_almost_equal( test_sigma_ratio, math.exp(-0.5), decimal=4, err_msg="Error in sigma for Gaussian initialized with FWHM.") # Check that the properties don't work after modifying the original. # Note: I test all the modifiers here. For the rest of the profile types, I'll # just confirm that it is true of shear. I don't think that has any chance # of missing anything. test_gal_flux1 = test_gal * 3. assert_raises(AttributeError, getattr, test_gal_flux1, "fwhm") assert_raises(AttributeError, getattr, test_gal_flux1, "half_light_radius") assert_raises(AttributeError, getattr, test_gal_flux1, "sigma") test_gal_flux2 = test_gal.withFlux(3.) assert_raises(AttributeError, getattr, test_gal_flux2, "fwhm") assert_raises(AttributeError, getattr, test_gal_flux2, "half_light_radius") assert_raises(AttributeError, getattr, test_gal_flux2, "sigma") test_gal_shear = test_gal.shear(g1=0.3, g2=0.1) assert_raises(AttributeError, getattr, test_gal_shear, "fwhm") assert_raises(AttributeError, getattr, test_gal_shear, "half_light_radius") assert_raises(AttributeError, getattr, test_gal_shear, "sigma") test_gal_rot = test_gal.rotate(theta=0.5 * galsim.radians) assert_raises(AttributeError, getattr, test_gal_rot, "fwhm") assert_raises(AttributeError, getattr, test_gal_rot, "half_light_radius") assert_raises(AttributeError, getattr, test_gal_rot, "sigma") test_gal_shift = test_gal.shift(dx=0.11, dy=0.04) assert_raises(AttributeError, getattr, test_gal_shift, "fwhm") assert_raises(AttributeError, getattr, test_gal_shift, "half_light_radius") assert_raises(AttributeError, getattr, test_gal_shift, "sigma")
def test_deconvolve(): """Test that deconvolution works as expected """ dx = 0.4 myImg1 = galsim.ImageF(80,80, scale=dx) myImg1.setCenter(0,0) myImg2 = galsim.ImageF(80,80, scale=dx) myImg2.setCenter(0,0) psf = galsim.Moffat(beta=3.8, fwhm=1.3, flux=5) inv_psf = galsim.Deconvolve(psf) psf.drawImage(myImg1, method='no_pixel') conv = galsim.Convolve(psf,psf,inv_psf) conv.drawImage(myImg2, method='no_pixel') printval(myImg1, myImg2) np.testing.assert_array_almost_equal( myImg1.array, myImg2.array, 4, err_msg="Image of Deconvolve * obj^2 doesn't match obj alone") cen = galsim.PositionD(0,0) np.testing.assert_equal(inv_psf.centroid, cen) np.testing.assert_almost_equal(inv_psf.flux, 1./psf.flux) # This doesn't really have any meaning, but this is what we've assigned to a deconvolve max_sb. np.testing.assert_almost_equal(inv_psf.max_sb, -psf.max_sb / psf.flux**2) check_basic(inv_psf, "Deconvolve(Moffat)", do_x=False) # Also check Deconvolve with an asymmetric profile. obj1 = galsim.Gaussian(sigma=3., flux=4).shift(-0.2, -0.4) obj2 = galsim.Gaussian(sigma=6., flux=1.3).shift(0.3, 0.3) obj = galsim.Add(obj1, obj2) inv_obj = galsim.Deconvolve(obj) conv = galsim.Convolve([inv_obj, obj, obj]) conv.drawImage(myImg1, method='no_pixel') obj.drawImage(myImg2, method='no_pixel') printval(myImg1, myImg2) np.testing.assert_array_almost_equal( myImg1.array, myImg2.array, 4, err_msg="Image of Deconvolve of asymmetric sum of Gaussians doesn't match obj alone") np.testing.assert_equal(inv_obj.centroid, -obj.centroid) np.testing.assert_almost_equal(inv_obj.flux, 1./obj.flux) np.testing.assert_almost_equal(inv_obj.max_sb, -obj.max_sb / obj.flux**2) check_basic(inv_obj, "Deconvolve(asym)", do_x=False) # Check picklability do_pickle(inv_obj) # And a significantly transformed deconvolve object jac = (0.3, -0.8, -0.7, 0.4) transformed_obj = obj.transform(*jac) transformed_inv_obj = inv_obj.transform(*jac) # Fix the flux -- most of the transformation commutes with deconvolution, but not flux scaling transformed_inv_obj /= transformed_obj.flux * transformed_inv_obj.flux check_basic(transformed_inv_obj, "transformed Deconvolve(asym)", do_x=False) conv = galsim.Convolve([transformed_inv_obj, transformed_obj, obj]) conv.drawImage(myImg1, method='no_pixel') printval(myImg1, myImg2) np.testing.assert_array_almost_equal( myImg1.array, myImg2.array, 4, err_msg="Transformed Deconvolve didn't cancel transformed original") np.testing.assert_equal(transformed_inv_obj.centroid, -transformed_obj.centroid) np.testing.assert_almost_equal(transformed_inv_obj.flux, 1./transformed_obj.flux) np.testing.assert_almost_equal(transformed_inv_obj.max_sb, -transformed_obj.max_sb / transformed_obj.flux**2) check_basic(transformed_inv_obj, "transformed Deconvolve(asym)", do_x=False) # Check picklability do_pickle(transformed_inv_obj) # Should raise an exception for invalid arguments assert_raises(TypeError, galsim.Deconvolve) assert_raises(TypeError, galsim.Deconvolve, myImg1) assert_raises(TypeError, galsim.Deconvolve, [psf]) assert_raises(TypeError, galsim.Deconvolve, psf, psf) assert_raises(TypeError, galsim.Deconvolve, psf, real_space=False) assert_raises(TypeError, galsim.Deconvolution) assert_raises(TypeError, galsim.Deconvolution, myImg1) assert_raises(TypeError, galsim.Deconvolution, [psf]) assert_raises(TypeError, galsim.Deconvolution, psf, psf) assert_raises(TypeError, galsim.Deconvolution, psf, real_space=False) assert_raises(NotImplementedError, inv_obj.xValue, galsim.PositionD(0,0)) assert_raises(NotImplementedError, inv_obj.drawReal, myImg1) assert_raises(NotImplementedError, inv_obj.shoot, 1)
def test_gaussian_flux_scaling(): """Test flux scaling for Gaussian. """ # decimal point to go to for parameter value comparisons param_decimal = 12 test_flux = 17.9 test_sigma = 1.8 # init with sigma and flux only (should be ok given last tests) obj = galsim.Gaussian(sigma=test_sigma, flux=test_flux) obj *= 2. np.testing.assert_almost_equal( obj.flux, test_flux * 2., decimal=param_decimal, err_msg="Flux param inconsistent after __imul__.") obj = galsim.Gaussian(sigma=test_sigma, flux=test_flux) obj /= 2. np.testing.assert_almost_equal( obj.flux, test_flux / 2., decimal=param_decimal, err_msg="Flux param inconsistent after __idiv__.") obj = galsim.Gaussian(sigma=test_sigma, flux=test_flux) obj2 = obj * 2. # First test that original obj is unharmed... np.testing.assert_almost_equal( obj.flux, test_flux, decimal=param_decimal, err_msg="Flux param inconsistent after __rmul__ (original).") # Then test new obj2 flux np.testing.assert_almost_equal( obj2.flux, test_flux * 2., decimal=param_decimal, err_msg="Flux param inconsistent after __rmul__ (result).") obj = galsim.Gaussian(sigma=test_sigma, flux=test_flux) obj2 = 2. * obj # First test that original obj is unharmed... np.testing.assert_almost_equal( obj.flux, test_flux, decimal=param_decimal, err_msg="Flux param inconsistent after __mul__ (original).") # Then test new obj2 flux np.testing.assert_almost_equal( obj2.flux, test_flux * 2., decimal=param_decimal, err_msg="Flux param inconsistent after __mul__ (result).") obj = galsim.Gaussian(sigma=test_sigma, flux=test_flux) obj2 = obj / 2. # First test that original obj is unharmed... np.testing.assert_almost_equal( obj.flux, test_flux, decimal=param_decimal, err_msg="Flux param inconsistent after __div__ (original).") # Then test new obj2 flux np.testing.assert_almost_equal( obj2.flux, test_flux / 2., decimal=param_decimal, err_msg="Flux param inconsistent after __div__ (result).")
def test_autoconvolve(): """Test that auto-convolution works the same as convolution with itself. """ dx = 0.4 myImg1 = galsim.ImageF(80,80, scale=dx) myImg1.setCenter(0,0) myImg2 = galsim.ImageF(80,80, scale=dx) myImg2.setCenter(0,0) psf = galsim.Moffat(beta=3.8, fwhm=1.3, flux=5) conv = galsim.Convolve([psf,psf]) conv.drawImage(myImg1, method='no_pixel') conv2 = galsim.AutoConvolve(psf) conv2.drawImage(myImg2, method='no_pixel') printval(myImg1, myImg2) np.testing.assert_array_almost_equal( myImg1.array, myImg2.array, 4, err_msg="Moffat convolved with self disagrees with AutoConvolve result") # Check with default_params conv = galsim.AutoConvolve(psf, gsparams=default_params) conv.drawImage(myImg1, method='no_pixel') np.testing.assert_array_almost_equal( myImg1.array, myImg2.array, 4, err_msg="Using AutoConvolve with default_params disagrees with expected result") conv = galsim.AutoConvolve(psf, gsparams=galsim.GSParams()) conv.drawImage(myImg1, method='no_pixel') np.testing.assert_array_almost_equal( myImg1.array, myImg2.array, 4, err_msg="Using AutoConvolve with GSParams() disagrees with expected result") check_basic(conv, "AutoConvolve(Moffat)") cen = galsim.PositionD(0,0) np.testing.assert_equal(conv2.centroid, cen) np.testing.assert_almost_equal(conv2.flux, psf.flux**2) np.testing.assert_array_less(conv2.xValue(cen), conv2.max_sb) # Check picklability do_pickle(conv2, lambda x: x.drawImage(method='no_pixel')) do_pickle(conv2) # Test photon shooting. do_shoot(conv2,myImg2,"AutoConvolve(Moffat)") # For a symmetric profile, AutoCorrelate is the same thing: conv2 = galsim.AutoCorrelate(psf) conv2.drawImage(myImg2, method='no_pixel') printval(myImg1, myImg2) np.testing.assert_array_almost_equal( myImg1.array, myImg2.array, 4, err_msg="Moffat convolved with self disagrees with AutoCorrelate result") # And check AutoCorrelate with gsparams: conv2 = galsim.AutoCorrelate(psf, gsparams=default_params) conv2.drawImage(myImg1, method='no_pixel') np.testing.assert_array_almost_equal( myImg1.array, myImg2.array, 4, err_msg="Using AutoCorrelate with default_params disagrees with expected result") conv2 = galsim.AutoCorrelate(psf, gsparams=galsim.GSParams()) conv2.drawImage(myImg1, method='no_pixel') np.testing.assert_array_almost_equal( myImg1.array, myImg2.array, 4, err_msg="Using AutoCorrelate with GSParams() disagrees with expected result") cen = galsim.PositionD(0,0) np.testing.assert_equal(conv2.centroid, cen) np.testing.assert_almost_equal(conv2.flux, psf.flux**2) np.testing.assert_array_less(conv2.xValue(cen), conv2.max_sb) # Also check AutoConvolve with an asymmetric profile. # (AutoCorrelate with this profile is done below...) obj1 = galsim.Gaussian(sigma=3., flux=4).shift(-0.2, -0.4) obj2 = galsim.Gaussian(sigma=6., flux=1.3).shift(0.3, 0.3) add = galsim.Add(obj1, obj2) conv = galsim.Convolve([add, add]) conv.drawImage(myImg1, method='no_pixel') autoconv = galsim.AutoConvolve(add) autoconv.drawImage(myImg2, method='no_pixel') printval(myImg1, myImg2) np.testing.assert_array_almost_equal( myImg1.array, myImg2.array, 4, err_msg="Asymmetric sum of Gaussians convolved with self disagrees with "+ "AutoConvolve result") cen = 2. * add.centroid np.testing.assert_equal(autoconv.centroid, cen) np.testing.assert_almost_equal(autoconv.flux, add.flux**2) np.testing.assert_array_less(autoconv.xValue(cen), autoconv.max_sb) check_basic(autoconv, "AutoConvolve(asym)") # Should raise an exception for invalid arguments assert_raises(TypeError, galsim.AutoConvolve) assert_raises(TypeError, galsim.AutoConvolve, myImg1) assert_raises(TypeError, galsim.AutoConvolve, [psf]) assert_raises(TypeError, galsim.AutoConvolve, psf, psf) assert_raises(TypeError, galsim.AutoConvolve, psf, realspace=False) assert_raises(TypeError, galsim.AutoConvolution) assert_raises(TypeError, galsim.AutoConvolution, myImg1) assert_raises(TypeError, galsim.AutoConvolution, [psf]) assert_raises(TypeError, galsim.AutoConvolution, psf, psf) assert_raises(TypeError, galsim.AutoConvolution, psf, realspace=False)
def test_gaussmom_smoke(g1_true, g2_true, wcs_g1, wcs_g2, weight_fac): rng = np.random.RandomState(seed=100) fwhm = 0.9 image_size = 107 cen = (image_size - 1) / 2 gs_wcs = galsim.ShearWCS(0.125, galsim.Shear(g1=wcs_g1, g2=wcs_g2)).jacobian() obj = galsim.Gaussian(fwhm=fwhm).shear(g1=g1_true, g2=g2_true).withFlux(400) im = obj.drawImage(nx=image_size, ny=image_size, wcs=gs_wcs, method='no_pixel').array noise = np.sqrt(np.sum(im**2)) / 1e18 wgt = np.ones_like(im) / noise**2 scale = np.sqrt(gs_wcs.pixelArea()) g1arr = [] g2arr = [] Tarr = [] fitter = GaussMom(fwhm=fwhm * weight_fac) for _ in range(50): shift = rng.uniform(low=-scale / 2, high=scale / 2, size=2) xy = gs_wcs.toImage(galsim.PositionD(shift)) im = obj.shift(dx=shift[0], dy=shift[1]).drawImage(nx=image_size, ny=image_size, wcs=gs_wcs, method='no_pixel', dtype=np.float64).array jac = Jacobian(y=cen + xy.y, x=cen + xy.x, dudx=gs_wcs.dudx, dudy=gs_wcs.dudy, dvdx=gs_wcs.dvdx, dvdy=gs_wcs.dvdy) _im = im + (rng.normal(size=im.shape) * noise) obs = Observation(image=_im, weight=wgt, jacobian=jac) # use a huge weight so that we get the raw moments back out res = fitter.go(obs=obs) if res['flags'] == 0: if weight_fac > 1: # for unweighted we need to convert e to g _g1, _g2 = e1e2_to_g1g2(res['e'][0], res['e'][1]) else: # we are weighting by the round gaussian before shearing. # Turns out this gives e that equals the shear g _g1, _g2 = res['e'][0], res['e'][1] g1arr.append(_g1) g2arr.append(_g2) Tarr.append(res['pars'][4]) g1 = np.mean(g1arr) g2 = np.mean(g2arr) gtol = 1e-9 assert np.abs(g1 - g1_true) < gtol, (g1, np.std(g1arr) / np.sqrt(len(g1arr))) assert np.abs(g2 - g2_true) < gtol, (g2, np.std(g2arr) / np.sqrt(len(g2arr))) # T test should only pass when the weight function is constant so # weight_fac needs to be rally big if g1_true == 0 and g2_true == 0 and weight_fac > 1: T = np.mean(Tarr) assert np.abs(T - fwhm_to_T(fwhm)) < 1e-6
def test_convolve_noise(): """Test that noise propagation works properly for compount objects. """ obj1 = galsim.Gaussian(sigma=1.7) obj2 = galsim.Gaussian(sigma=2.3) obj1.noise = galsim.UncorrelatedNoise(variance=0.3, scale=0.2) obj2.noise = galsim.UncorrelatedNoise(variance=0.5, scale=0.2) obj3 = galsim.Gaussian(sigma=2.9) # Convolve convolves the noise from a single component conv2 = galsim.Convolution([obj1,obj3]) noise = galsim.Convolve([obj1.noise._profile, obj3, obj3]) # xValue is too slow here. Use drawImage to get variance. (Just as CorrelatedNoise does.) variance = noise.drawImage(nx=1, ny=1, scale=1., method='sb')(1,1) np.testing.assert_almost_equal(conv2.noise.getVariance(), variance, err_msg = "Convolution of two objects did not correctly propagate noise varinace") conv2 = galsim.Convolution([obj2,obj3]) noise = galsim.Convolve([obj2.noise._profile, obj3, obj3]) variance = noise.drawImage(nx=1, ny=1, scale=1., method='sb')(1,1) np.testing.assert_almost_equal(conv2.noise.getVariance(), variance, err_msg = "Convolution of two objects did not correctly propagate noise varinace") # Convolution of multiple objects with noise attributes raises a warning and fails # to propagate noise properly. (It takes the input noise from the first one.) conv2 = galsim.Convolution(obj1, obj2) conv3 = galsim.Convolution(obj1, obj2, obj3) with assert_warns(galsim.GalSimWarning): assert conv2.noise == obj1.noise.convolvedWith(obj2) with assert_warns(galsim.GalSimWarning): assert conv3.noise == obj1.noise.convolvedWith(galsim.Convolve(obj2,obj3)) # Convolution with only one object uses that object's noise conv1 = galsim.Convolution(obj1) assert conv1.noise == obj1.noise # Other types don't propagate noise and give a warning about it. deconv = galsim.Deconvolution(obj2) autoconv = galsim.AutoConvolution(obj2) autocorr = galsim.AutoCorrelation(obj2) four = galsim.FourierSqrt(obj2) with assert_warns(galsim.GalSimWarning): assert deconv.noise is None with assert_warns(galsim.GalSimWarning): assert autoconv.noise is None with assert_warns(galsim.GalSimWarning): assert autocorr.noise is None with assert_warns(galsim.GalSimWarning): assert four.noise is None obj2.noise = None # Remove obj2 noise for the rest. conv2 = galsim.Convolution(obj1, obj2) noise = galsim.Convolve([obj1.noise._profile, obj2, obj2]) variance = noise.drawImage(nx=1, ny=1, scale=1., method='sb')(1,1) np.testing.assert_almost_equal(conv2.noise.getVariance(), variance, err_msg = "Convolution of two objects did not correctly propagate noise varinace") conv3 = galsim.Convolution(obj1, obj2, obj3) noise = galsim.Convolve([obj1.noise._profile, obj2, obj2, obj3, obj3]) variance = noise.drawImage(nx=1, ny=1, scale=1., method='sb')(1,1) np.testing.assert_almost_equal(conv3.noise.getVariance(), variance, err_msg = "Convolution of three objects did not correctly propagate noise varinace") deconv = galsim.Deconvolution(obj2) autoconv = galsim.AutoConvolution(obj2) autocorr = galsim.AutoCorrelation(obj2) four = galsim.FourierSqrt(obj2) assert deconv.noise is None assert autoconv.noise is None assert autocorr.noise is None assert four.noise is None
def test_hlr(): """Test the calculateHLR method. """ # Compare the calculation for a simple Gaussian. g1 = galsim.Gaussian(sigma=5, flux=1.7) print('g1 native hlr = ', g1.half_light_radius) print('g1.calculateHLR = ', g1.calculateHLR()) print('nyquist scale = ', g1.nyquistScale()) # These should be exactly equal. np.testing.assert_equal( g1.half_light_radius, g1.calculateHLR(), err_msg="Gaussian.calculateHLR() returned wrong value.") # Check for a convolution of two Gaussians. Should be equivalent, but now will need to # do the calculation. g2 = galsim.Convolve(galsim.Gaussian(sigma=3, flux=1.3), galsim.Gaussian(sigma=4, flux=23)) test_hlr = g2.calculateHLR() print('g2.calculateHLR = ', test_hlr) print('ratio - 1 = ', test_hlr / g1.half_light_radius - 1) np.testing.assert_almost_equal( test_hlr / g1.half_light_radius, 1.0, decimal=1, err_msg="Gaussian.calculateHLR() is not accurate.") # The default scale is only accurate to around 1 dp. Using scale = 0.1 is accurate to 3 dp. # Note: Nyquist scale is about 4.23 for this profile. test_hlr = g2.calculateHLR(scale=0.1) print('g2.calculateHLR(scale=0.1) = ', test_hlr) print('ratio - 1 = ', test_hlr / g1.half_light_radius - 1) np.testing.assert_almost_equal( test_hlr / g1.half_light_radius, 1.0, decimal=3, err_msg="Gaussian.calculateHLR(scale=0.1) is not accurate.") # Finally, we don't expect this to be accurate, but make sure the code can handle having # more than half the flux in the central pixel. test_hlr = g2.calculateHLR(scale=15) print('g2.calculateHLR(scale=15) = ', test_hlr) print('ratio - 1 = ', test_hlr / g1.half_light_radius - 1) np.testing.assert_almost_equal( test_hlr / g1.half_light_radius / 10, 0.1, decimal=1, err_msg="Gaussian.calculateHLR(scale=15) is not accurate.") # Next, use an Exponential profile e1 = galsim.Exponential(scale_radius=5, flux=1.7) print('e1 native hlr = ', e1.half_light_radius) print('e1.calculateHLR = ', e1.calculateHLR()) print('nyquist scale = ', e1.nyquistScale()) # These should be exactly equal. np.testing.assert_equal( e1.half_light_radius, e1.calculateHLR(), err_msg="Exponential.calculateHLR() returned wrong value.") # Check for a convolution with a delta function. Should be equivalent, but now will need to # do the calculation. e2 = galsim.Convolve(galsim.Exponential(scale_radius=5, flux=1.3), galsim.Gaussian(sigma=1.e-4, flux=23)) test_hlr = e2.calculateHLR() print('e2.calculateHLR = ', test_hlr) print('ratio - 1 = ', test_hlr / e1.half_light_radius - 1) np.testing.assert_almost_equal( test_hlr / e1.half_light_radius, 1.0, decimal=1, err_msg="Exponential.calculateHLR() is not accurate.") # The default scale is only accurate to around 1 dp. Using scale = 0.1 is accurate to 3 dp. # Note: Nyquist scale is about 1.57 for this profile. # We can also decrease the size, which should still be accurate, but maybe a little faster. # Go a bit more that 2*hlr in units of the pixels. size = int(2.2 * e1.half_light_radius / 0.1) test_hlr = e2.calculateHLR(scale=0.1, size=size) print('e2.calculateHLR(scale=0.1) = ', test_hlr) print('ratio - 1 = ', test_hlr / e1.half_light_radius - 1) np.testing.assert_almost_equal( test_hlr / e1.half_light_radius, 1.0, decimal=3, err_msg="Exponential.calculateHLR(scale=0.1) is not accurate.") # Check that it works if the centroid is not at the origin e3 = e2.shift(2, 3) test_hlr = e3.calculateHLR(scale=0.1) print('e3.calculateHLR(scale=0.1) = ', test_hlr) print('ratio - 1 = ', test_hlr / e1.half_light_radius - 1) np.testing.assert_almost_equal( test_hlr / e1.half_light_radius, 1.0, decimal=3, err_msg="shifted Exponential HLR is not accurate.") # Can set a centroid manually. This should be equivalent to the default. print('e3.centroid = ', e3.centroid()) test_hlr = e3.calculateHLR(scale=0.1, centroid=e3.centroid()) np.testing.assert_almost_equal( test_hlr / e1.half_light_radius, 1.0, decimal=3, err_msg="shifted HLR with explicit centroid is not accurate.") # The calculateHLR method can also return other radii like r90, rather than r50 using the # parameter flux_fraction. This is also analytic for Exponential r90 = 3.889720170 * e1.scale_radius test_r90 = e2.calculateHLR(scale=0.1, flux_frac=0.9) print('r90 = ', r90) print('e2.calculateHLR(scale=0.1, flux_frac=0.9) = ', test_r90) print('ratio - 1 = ', test_r90 / r90 - 1) np.testing.assert_almost_equal( test_r90 / r90, 1.0, decimal=3, err_msg="Exponential r90 calculation is not accurate.") # Check the image version. im = e1.drawImage(scale=0.1, nx=2048, ny=2048) # Needs to be large to get enough flux for the # image to get it to 3 digits of accuracy. test_hlr = im.calculateHLR() print('im.calculateHLR() = ', test_hlr) print('ratio - 1 = ', test_hlr / e1.half_light_radius - 1) np.testing.assert_almost_equal( test_hlr / e1.half_light_radius, 1.0, decimal=3, err_msg="image.calculateHLR is not accurate.") # Check that a non-square image works correctly. Also, not centered anywhere in particular. bounds = galsim.BoundsI(-1234, -1234 + 2048, 8234, 8234 + 2099) offset = galsim.PositionD(29, 1) im = e1.drawImage(scale=0.1, bounds=bounds, offset=offset) test_hlr = im.calculateHLR(center=im.trueCenter() + offset) print('im.calculateHLR() = ', test_hlr) print('ratio - 1 = ', test_hlr / e1.half_light_radius - 1) np.testing.assert_almost_equal( test_hlr / e1.half_light_radius, 1.0, decimal=3, err_msg="non-square image.calculateHLR is not accurate.")
def main(argv): cat_file_name = 'real_galaxy_catalog_example.fits' dir = 'data' # Make output directory if not already present. if not os.path.isdir('output'): os.mkdir('output') cube_file_name = os.path.join('output', 'cube_real.fits') psf_file_name = os.path.join('output', 'psf_real.fits') random_seed = 1512413 sky_level = 1.e6 # ADU / arcsec^2 pixel_scale = 0.16 # arcsec gal_flux = 1.e5 # arbitrary choice, makes nice (not too) noisy images psf_inner_fwhm = 0.1 # arcsec psf_outer_fwhm = 0.6 # arcsec psf_inner_fraction = 0.8 # fraction of total PSF flux in the inner Gaussian psf_outer_fraction = 0.2 # fraction of total PSF flux in the inner Gaussian redshift = 0.05 ngal = 30 # SED ----------------------------------------------------- path, filename = os.path.split(__file__) datapath = os.path.abspath(os.path.join(path, "data/")) outpath = os.path.abspath(os.path.join(path, "output/")) random_seed = 1234567 rng = galsim.BaseDeviate(random_seed) # read in SEDs SED_names = ['CWW_E_ext', 'CWW_Sbc_ext', 'CWW_Scd_ext', 'CWW_Im_ext'] SEDs = {} for SED_name in SED_names: SED_filename = os.path.join(datapath, '{0}.sed'.format(SED_name)) SED = galsim.SED(SED_filename, wave_type='Ang') SEDs[SED_name] = SED.withFluxDensity(target_flux_density=1.0, wavelength=500) filter_names = 'gri' filters = {} for filter_name in filter_names: filter_filename = os.path.join(datapath, 'LSST_{0}.dat'.format(filter_name)) filters[filter_name] = galsim.Bandpass(filter_filename) filters[filter_name] = filters[filter_name].thin(rel_err=1e-4) #-------------------------------------------------------------------- real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=dir) psf = galsim.Gaussian(fwhm=psf_inner_fwhm, flux=psf_inner_fraction) # Draw the PSF with no noise. psf_image = psf.drawImage(scale=pixel_scale) # write to file psf_image.write(psf_file_name) # Build the images for k in range(ngal): # Initialize the random number generator we will be using. rng = galsim.UniformDeviate(random_seed + k) gal = galsim.RealGalaxy(real_galaxy_catalog, index=k) # Set the flux #gal = gal.withFlux(gal_flux) SED = SEDs['CWW_E_ext'].atRedshift(redshift) #SED = SEDs['CWW_Sbc_ext'].atRedshift(redshift) galcol = galsim.Chromatic(gal, SED) final = galsim.Convolve([galcol, psf]) dx = rng() - 0.5 dy = rng() - 0.5 # Draw the profile galcol = galsim.Chromatic(final, SED) for filter_name, filter_ in filters.iteritems(): img = galsim.ImageF(78, 78, scale=pixel_scale) final.drawImage(filter_, image=img) out_filename = os.path.join( outpath, 'realcolor_{0}_{1}.fits'.format(filter_name, str(k))) galsim.fits.write(img, out_filename)
def test_sersic(): """Test the generation of a specific Sersic profile against a known result. """ # Test Sersic savedImg = galsim.fits.read(os.path.join(imgdir, "sersic_3_1.fits")) dx = 0.2 myImg = galsim.ImageF(savedImg.bounds, scale=dx) myImg.setCenter(0, 0) sersic = galsim.Sersic(n=3, flux=1, half_light_radius=1) sersic.drawImage(myImg, scale=dx, method="sb", use_true_center=False) np.testing.assert_array_almost_equal( myImg.array, savedImg.array, 5, err_msg="Using GSObject Sersic disagrees with expected result") # Check with default_params sersic = galsim.Sersic(n=3, flux=1, half_light_radius=1, gsparams=default_params) sersic.drawImage(myImg, scale=dx, method="sb", use_true_center=False) np.testing.assert_array_almost_equal( myImg.array, savedImg.array, 5, err_msg= "Using GSObject Sersic with default_params disagrees with expected result" ) sersic = galsim.Sersic(n=3, flux=1, half_light_radius=1, gsparams=galsim.GSParams()) sersic.drawImage(myImg, scale=dx, method="sb", use_true_center=False) np.testing.assert_array_almost_equal( myImg.array, savedImg.array, 5, err_msg= "Using GSObject Sersic with GSParams() disagrees with expected result") # Use non-unity values. sersic = galsim.Sersic(n=3, flux=1.7, half_light_radius=2.3) gsp = galsim.GSParams(xvalue_accuracy=1.e-8, kvalue_accuracy=1.e-8) sersic2 = galsim.Sersic(n=3, flux=1.7, half_light_radius=2.3, gsparams=gsp) assert sersic2 != sersic assert sersic2 == sersic.withGSParams(gsp) assert sersic2 == sersic.withGSParams(xvalue_accuracy=1.e-8, kvalue_accuracy=1.e-8) check_basic(sersic, "Sersic") # Test photon shooting. # Convolve with a small gaussian to smooth out the central peak. sersic2 = galsim.Convolve(sersic, galsim.Gaussian(sigma=0.3)) do_shoot(sersic2, myImg, "Sersic") # Test kvalues do_kvalue(sersic, myImg, "Sersic") # Check picklability do_pickle(sersic, lambda x: x.drawImage(method='no_pixel')) do_pickle(sersic) # Now repeat everything using a truncation. (Above had no truncation.) # Test Truncated Sersic # Don't use an integer truncation, since we don't want the truncation line to pass directly # through the center of a pixel where numerical rounding differences may decide whether the # value is zero or not. # This regression test compares to an image built using the code base at 82259f0 savedImg = galsim.fits.read(os.path.join(imgdir, "sersic_3_1_10.fits")) myImg = galsim.ImageF(savedImg.bounds, scale=dx) myImg.setCenter(0, 0) sersic = galsim.Sersic(n=3, flux=1, half_light_radius=1, trunc=9.99) sersic.drawImage(myImg, scale=dx, method="sb", use_true_center=False) np.testing.assert_array_almost_equal( myImg.array, savedImg.array, 5, err_msg="Using truncated GSObject Sersic disagrees with expected result" ) # Use non-unity values. test_flux = 1.8 sersic = galsim.Sersic(n=3, flux=test_flux, half_light_radius=2.3, trunc=5.9) cen = galsim.PositionD(0, 0) np.testing.assert_equal(sersic.centroid, cen) np.testing.assert_almost_equal(sersic.kValue(cen), (1 + 0j) * test_flux) np.testing.assert_almost_equal(sersic.flux, test_flux) np.testing.assert_almost_equal(sersic.xValue(cen), sersic.max_sb) check_basic(sersic, "Truncated Sersic") # Test photon shooting. # Convolve with a small gaussian to smooth out the central peak. sersic2 = galsim.Convolve(sersic, galsim.Gaussian(sigma=0.3)) do_shoot(sersic2, myImg, "Truncated Sersic") # Test kvalues do_kvalue(sersic, myImg, "Truncated Sersic") # Check picklability do_pickle(sersic, lambda x: x.drawImage(method='no_pixel')) do_pickle(sersic) # n=4 is also called DeVaucouleurs sersic = galsim.Sersic(n=4, flux=1.7, half_light_radius=2.3, trunc=5.9) devauc = galsim.DeVaucouleurs(flux=1.7, half_light_radius=2.3, trunc=5.9) assert devauc == sersic check_basic(devauc, "DeVaucouleurs") # Check for normalization consistencies with kValue checks. xValues tested in test_sersic_radii. # For half-light radius specified truncated Sersic, with flux_untruncated flag set sersic = galsim.Sersic(n=3, flux=test_flux, half_light_radius=1, trunc=10, flux_untruncated=True) do_kvalue( sersic, myImg, "Truncated Sersic w/ flux_untruncated, half-light radius specified") # For scale radius specified Sersic sersic = galsim.Sersic(n=3, flux=test_flux, scale_radius=0.05) do_kvalue(sersic, myImg, "Sersic w/ scale radius specified") # For scale radius specified truncated Sersic sersic = galsim.Sersic(n=3, flux=test_flux, scale_radius=0.05, trunc=10) do_kvalue(sersic, myImg, "Truncated Sersic w/ scale radius specified") # For scale radius specified truncated Sersic, with flux_untruncated flag set sersic = galsim.Sersic(n=3, flux=test_flux, scale_radius=0.05, trunc=10, flux_untruncated=True) do_kvalue(sersic, myImg, "Truncated Sersic w/ flux_untruncated, scale radius specified") # Test severely truncated Sersic sersic = galsim.Sersic(n=4, flux=test_flux, half_light_radius=1, trunc=1.45) do_kvalue(sersic, myImg, "Severely truncated n=4 Sersic") # Should raise an exception if both scale_radius and half_light_radius are provided. assert_raises(TypeError, galsim.Sersic, n=1.2, scale_radius=3, half_light_radius=1) assert_raises(TypeError, galsim.Sersic, n=1.2) assert_raises(TypeError, galsim.DeVaucouleurs, scale_radius=3, half_light_radius=1) assert_raises(TypeError, galsim.DeVaucouleurs) # Allowed range is [0.3, 6.2] assert_raises(ValueError, galsim.Sersic, n=0.2, scale_radius=3) assert_raises(ValueError, galsim.Sersic, n=6.3, scale_radius=3) # trunc must be > sqrt(2) * hlr assert_raises(ValueError, galsim.Sersic, n=3, half_light_radius=1, trunc=1.4) assert_raises(ValueError, galsim.DeVaucouleurs, half_light_radius=1, trunc=1.4) # Other errors assert_raises(TypeError, galsim.Sersic, scale_radius=3) assert_raises(ValueError, galsim.Sersic, n=3, scale_radius=3, trunc=-1) assert_raises(ValueError, galsim.DeVaucouleurs, scale_radius=3, trunc=-1)
def draw_neighbor(neighbors_config=None, psf=None): gsparams = None if gsparams is None: gsparams = galsim.GSParams(maximum_fft_size=10240) #begin of possible kwargsneigh profile_type = neighbors_config['profile_type'] tru_g1 = neighbors_config['tru_g1'] tru_g2 = neighbors_config['tru_g2'] if (profile_type == "Sersic"): sersiccut = neighbors_config['Sersic']['sersiccut'] tru_rad = neighbors_config['Sersic']['tru_rad'] tru_sersicn = neighbors_config['Sersic']['tru_sersicn'] tru_sb = neighbors_config['Sersic']['tru_sb'] tru_flux = np.pi * tru_rad * tru_rad * tru_sb if sersiccut is None: trunc = 0 else: trunc = tru_rad * sersiccut gal = galsim.Sersic(n=tru_sersicn, half_light_radius=tru_rad, flux=tru_flux, gsparams=gsparams, trunc=trunc) # We make this profile elliptical gal = gal.shear(g1=tru_g1, g2=tru_g2) # This adds the ellipticity to the galaxy elif profile_type == "Gaussian": tru_flux = neighbors_config['Gaussian']['tru_flux'] tru_sigma = neighbors_config['Gaussian']['tru_sigma'] gal = galsim.Gaussian(flux=tru_flux, sigma=tru_sigma, gsparams=gsparams) # We make this profile elliptical gal = gal.shear(g1=tru_g1, g2=tru_g2) # This adds the ellipticity to the galaxy elif profile_type == 'EBulgeDisk': # A more advanced Bulge + Disk model # It needs GalSim version master, as of April 2017 (probably 1.5). # Get a Sersic bulge: tru_bulge_sersicn = neighbors_config['EBulgeDisk']['tru_bulge_sersicn'] tru_bulge_rad = neighbors_config['EBulgeDisk']['tru_bulge_rad'] tru_bulge_flux = neighbors_config['EBulgeDisk']['tru_bulge_flux'] tru_disk_hlr = neighbors_config['EBulgeDisk']['tru_disk_hlr'] tru_disk_tilt = neighbors_config['EBulgeDisk']['tru_disk_tilt'] tru_disk_flux = neighbors_config['EBulgeDisk']['tru_disk_flux'] tru_theta = neighbors_config['EBulgeDisk']['tru_theta'] bulge = galsim.Sersic(n=tru_bulge_sersicn, half_light_radius=tru_bulge_rad, flux=tru_bulge_flux) # Make it elliptical: #bulge_ell = galsim.Shear(g=row["tru_bulge_g"], beta=row["tru_theta"] * galsim.degrees) #bulge = bulge.shear(bulge_ell) bulge = bulge.shear(g1=tru_g1, g2=tru_g2) # Get a disk scale_radius = tru_disk_hlr / galsim.Exponential._hlr_factor disk = galsim.InclinedExponential( inclination=tru_disk_tilt * galsim.degrees, scale_radius=scale_radius, flux=tru_disk_flux, scale_h_over_r=tru_disk_scale_h_over_r) # Rotate it in the same orientation as the bulge: disk = disk.rotate(tru_theta * galsim.degrees) # And we add those profiles, as done in GalSim demo3.py : gal = bulge + disk elif profile_type == 'Gaussian_PSF': #you meant to draw stars all of them identical gal = galsim.Gaussian(flux=neighbors_config['Gaussian_PSF']['flux'], sigma=neighbors_config['Gaussian_PSF']['flux']) #gal = gal.shear(g1=tru_g1, g2=tru_g2) elif profile_type == 'Stamp_PSF': raise RuntimeError( "Neighbors with Stamp_PSF, but PSF stamp is not defined in catalog" ) gal = psf #gal = gal.shear(g1=tru_g1, g2=tru_g2) else: raise RuntimeError("Unknown galaxy profile!") return gal
def test_convolve(): nphotons = 1000000 obj = galsim.Gaussian(flux=1.7, sigma=2.3) rng = galsim.UniformDeviate(1234) pa1 = obj.shoot(nphotons, rng) pa2 = obj.shoot(nphotons, rng) # If not correlated then convolve is deterministic conv_x = pa1.x + pa2.x conv_y = pa1.y + pa2.y conv_flux = pa1.flux * pa2.flux * nphotons np.testing.assert_allclose(np.sum(pa1.flux), 1.7) np.testing.assert_allclose(np.sum(pa2.flux), 1.7) np.testing.assert_allclose(np.sum(conv_flux), 1.7 * 1.7) np.testing.assert_allclose(np.sum(pa1.x**2) / nphotons, 2.3**2, rtol=0.01) np.testing.assert_allclose(np.sum(pa2.x**2) / nphotons, 2.3**2, rtol=0.01) np.testing.assert_allclose(np.sum(conv_x**2) / nphotons, 2. * 2.3**2, rtol=0.01) np.testing.assert_allclose(np.sum(pa1.y**2) / nphotons, 2.3**2, rtol=0.01) np.testing.assert_allclose(np.sum(pa2.y**2) / nphotons, 2.3**2, rtol=0.01) np.testing.assert_allclose(np.sum(conv_y**2) / nphotons, 2. * 2.3**2, rtol=0.01) pa3 = galsim.PhotonArray(nphotons) pa3.assignAt(0, pa1) # copy from pa1 pa3.convolve(pa2) np.testing.assert_allclose(pa3.x, conv_x) np.testing.assert_allclose(pa3.y, conv_y) np.testing.assert_allclose(pa3.flux, conv_flux) # If one of them is correlated, it is still deterministic. pa3.assignAt(0, pa1) pa3.setCorrelated() pa3.convolve(pa2) np.testing.assert_allclose(pa3.x, conv_x) np.testing.assert_allclose(pa3.y, conv_y) np.testing.assert_allclose(pa3.flux, conv_flux) pa3.assignAt(0, pa1) pa3.setCorrelated(False) pa2.setCorrelated() pa3.convolve(pa2) np.testing.assert_allclose(pa3.x, conv_x) np.testing.assert_allclose(pa3.y, conv_y) np.testing.assert_allclose(pa3.flux, conv_flux) # But if both are correlated, then it's not this simple. pa3.assignAt(0, pa1) pa3.setCorrelated() assert pa3.isCorrelated() assert pa2.isCorrelated() pa3.convolve(pa2) with assert_raises(AssertionError): np.testing.assert_allclose(pa3.x, conv_x) with assert_raises(AssertionError): np.testing.assert_allclose(pa3.y, conv_y) np.testing.assert_allclose(np.sum(pa3.flux), 1.7 * 1.7) np.testing.assert_allclose(np.sum(pa3.x**2) / nphotons, 2 * 2.3**2, rtol=0.01) np.testing.assert_allclose(np.sum(pa3.y**2) / nphotons, 2 * 2.3**2, rtol=0.01) # Error to have different lengths pa4 = galsim.PhotonArray(50, pa1.x[:50], pa1.y[:50], pa1.flux[:50]) assert_raises(galsim.GalSimError, pa1.convolve, pa4)
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())