def test_convolve_flux_scaling(): """Test flux scaling for Convolve. """ # decimal point to go to for parameter value comparisons param_decimal = 12 test_flux = 17.9 test_sigma = 1.8 test_hlr = 1.9 # init with Gaussian and DeVauc only (should be ok given last tests) obj = galsim.Convolve( [galsim.Gaussian(sigma=test_sigma, flux=np.sqrt(test_flux)), galsim.DeVaucouleurs(half_light_radius=test_hlr, flux=np.sqrt(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.Convolve( [galsim.Gaussian(sigma=test_sigma, flux=np.sqrt(test_flux)), galsim.DeVaucouleurs(half_light_radius=test_hlr, flux=np.sqrt(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.Convolve( [galsim.Gaussian(sigma=test_sigma, flux=np.sqrt(test_flux)), galsim.DeVaucouleurs(half_light_radius=test_hlr, flux=np.sqrt(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.Convolve( [galsim.Gaussian(sigma=test_sigma, flux=np.sqrt(test_flux)), galsim.DeVaucouleurs(half_light_radius=test_hlr, flux=np.sqrt(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.Convolve( [galsim.Gaussian(sigma=test_sigma, flux=np.sqrt(test_flux)), galsim.DeVaucouleurs(half_light_radius=test_hlr, flux=np.sqrt(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_convolve_flux_scaling(): """Test flux scaling for Convolve. """ import time t1 = time.time() # init with Gaussian and DeVauc only (should be ok given last tests) obj = galsim.Convolve( [galsim.Gaussian(sigma=test_sigma, flux=np.sqrt(test_flux)), galsim.DeVaucouleurs(half_light_radius=test_hlr, flux=np.sqrt(test_flux))]) obj *= 2. np.testing.assert_almost_equal( obj.getFlux(), test_flux * 2., decimal=param_decimal, err_msg="Flux param inconsistent after __imul__.") obj = galsim.Convolve( [galsim.Gaussian(sigma=test_sigma, flux=np.sqrt(test_flux)), galsim.DeVaucouleurs(half_light_radius=test_hlr, flux=np.sqrt(test_flux))]) obj /= 2. np.testing.assert_almost_equal( obj.getFlux(), test_flux / 2., decimal=param_decimal, err_msg="Flux param inconsistent after __idiv__.") obj = galsim.Convolve( [galsim.Gaussian(sigma=test_sigma, flux=np.sqrt(test_flux)), galsim.DeVaucouleurs(half_light_radius=test_hlr, flux=np.sqrt(test_flux))]) obj2 = obj * 2. # First test that original obj is unharmed... (also tests that .copy() is working) np.testing.assert_almost_equal( obj.getFlux(), test_flux, decimal=param_decimal, err_msg="Flux param inconsistent after __rmul__ (original).") # Then test new obj2 flux np.testing.assert_almost_equal( obj2.getFlux(), test_flux * 2., decimal=param_decimal, err_msg="Flux param inconsistent after __rmul__ (result).") obj = galsim.Convolve( [galsim.Gaussian(sigma=test_sigma, flux=np.sqrt(test_flux)), galsim.DeVaucouleurs(half_light_radius=test_hlr, flux=np.sqrt(test_flux))]) obj2 = 2. * obj # First test that original obj is unharmed... (also tests that .copy() is working) np.testing.assert_almost_equal( obj.getFlux(), test_flux, decimal=param_decimal, err_msg="Flux param inconsistent after __mul__ (original).") # Then test new obj2 flux np.testing.assert_almost_equal( obj2.getFlux(), test_flux * 2., decimal=param_decimal, err_msg="Flux param inconsistent after __mul__ (result).") obj = galsim.Convolve( [galsim.Gaussian(sigma=test_sigma, flux=np.sqrt(test_flux)), galsim.DeVaucouleurs(half_light_radius=test_hlr, flux=np.sqrt(test_flux))]) obj2 = obj / 2. # First test that original obj is unharmed... (also tests that .copy() is working) np.testing.assert_almost_equal( obj.getFlux(), test_flux, decimal=param_decimal, err_msg="Flux param inconsistent after __div__ (original).") # Then test new obj2 flux np.testing.assert_almost_equal( obj2.getFlux(), test_flux / 2., decimal=param_decimal, err_msg="Flux param inconsistent after __div__ (result).") t2 = time.time() print 'time for %s = %.2f'%(funcname(),t2-t1)
def test_largeshear(): """Test the application of a large shear to a Sersic SBProfile against a known result. """ import time t1 = time.time() e1 = 0.0 e2 = 0.5 myShear = galsim.Shear(e1=e1, e2=e2) # test the SBProfile version using applyShear savedImg = galsim.fits.read(os.path.join(imgdir, "sersic_largeshear.fits")) dx = 0.2 myImg = galsim.ImageF(savedImg.bounds, scale=dx) myImg.setCenter(0,0) devauc = galsim.DeVaucouleurs(flux=1, half_light_radius=1) devauc.applyShear(myShear) devauc.draw(myImg,scale=dx, normalization="surface brightness", use_true_center=False) np.testing.assert_array_almost_equal( myImg.array, savedImg.array, 5, err_msg="Using GSObject applyShear disagrees with expected result") devauc = galsim.DeVaucouleurs(flux=1, half_light_radius=1) devauc2 = devauc.createSheared(myShear) devauc2.draw(myImg,scale=dx, normalization="surface brightness", use_true_center=False) np.testing.assert_array_almost_equal( myImg.array, savedImg.array, 5, err_msg="Using GSObject createSheared disagrees with expected result") # Check with default_params devauc = galsim.DeVaucouleurs(flux=1, half_light_radius=1, gsparams=default_params) devauc.applyShear(myShear) devauc.draw(myImg,scale=dx, normalization="surface brightness", use_true_center=False) np.testing.assert_array_almost_equal( myImg.array, savedImg.array, 5, err_msg="Using GSObject applyShear with default_params disagrees with expected result") devauc = galsim.DeVaucouleurs(flux=1, half_light_radius=1, gsparams=galsim.GSParams()) devauc.applyShear(myShear) devauc.draw(myImg,scale=dx, normalization="surface brightness", use_true_center=False) np.testing.assert_array_almost_equal( myImg.array, savedImg.array, 5, err_msg="Using GSObject applyShear with GSParams() disagrees with expected result") # Test photon shooting. # Convolve with a small gaussian to smooth out the central peak. devauc2 = galsim.Convolve(devauc, galsim.Gaussian(sigma=0.3)) do_shoot(devauc2,myImg,"sheared DeVauc") # Test kvalues. # Testing a sheared devauc requires a rather large fft. What we really care about # testing though is the accuracy of the applyShear function. So just shear a Gaussian here. gauss = galsim.Gaussian(sigma=2.3) gauss.applyShear(myShear) do_kvalue(gauss, "sheared Gaussian") t2 = time.time() print 'time for %s = %.2f'%(funcname(),t2-t1)
def test_largeshear(): """Test the application of a large shear to a Sersic profile against a known result. """ e1 = 0.0 e2 = 0.5 myShear = galsim.Shear(e1=e1, e2=e2) savedImg = galsim.fits.read(os.path.join(imgdir, "sersic_largeshear.fits")) dx = 0.2 myImg = galsim.ImageF(savedImg.bounds, scale=dx) myImg.setCenter(0,0) devauc = galsim.DeVaucouleurs(flux=1, half_light_radius=1) devauc2 = devauc.shear(myShear) devauc2.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 shear disagrees with expected result") np.testing.assert_almost_equal( myImg.array.max(), devauc2.max_sb, 5, err_msg="sheared profile max_sb did not match maximum pixel value") # Check with default_params devauc = galsim.DeVaucouleurs(flux=1, half_light_radius=1, gsparams=default_params) devauc = devauc.shear(myShear) devauc.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 shear with default_params disagrees with expected result") devauc = galsim.DeVaucouleurs(flux=1, half_light_radius=1, gsparams=galsim.GSParams()) devauc = devauc._shear(myShear) devauc.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 shear with GSParams() disagrees with expected result") # Convolve with a small gaussian to smooth out the central peak. devauc2 = galsim.Convolve(devauc, galsim.Gaussian(sigma=0.3)) check_basic(devauc2, "sheared DeVauc") # Test photon shooting. do_shoot(devauc2,myImg,"sheared DeVauc") # Test kvalues. # Testing a sheared devauc requires a rather large fft. What we really care about # testing though is the accuracy of the shear function. So just shear a Gaussian here. gauss = galsim.Gaussian(sigma=2.3) gauss = gauss.shear(myShear) do_kvalue(gauss,myImg, "sheared Gaussian") # Check picklability do_pickle(gauss, lambda x: x.drawImage()) do_pickle(gauss) do_pickle(gauss._sbp)
def makeBulge(redshift, SEDs): bulgeG1, bulgeG2, mono_bulge_HLR = 0.12, 0.07, 0.5 mono_bulge = galsim.DeVaucouleurs(half_light_radius=mono_bulge_HLR) bulge_SED = SEDs['CWW_E_ext'].atRedshift(redshift) bulge = mono_bulge * bulge_SED bulge = bulge.shear(g1=bulgeG1, g2=bulgeG2) return bulge
def get_galsim_light_source(self, band_index, psf_sigma_degrees, image_parameters): def apply_shear_and_shift(galaxy): return (galaxy.shear( q=self._axis_ratio, beta=(90. - self._gal_angle_deg) * galsim.degrees, ).shift( self._common_fields.get_world_offset( image_parameters).as_galsim_position())) flux_nelec = (self._common_fields.get_flux_nmgy(band_index) * image_parameters.band_nelec_per_nmgy[band_index]) half_light_radius_deg = self._half_light_radius_arcsec / ARCSEC_PER_DEGREE exponential_profile = apply_shear_and_shift( galsim.Exponential( half_light_radius=half_light_radius_deg, flux=flux_nelec * (1 - self._gal_frac_dev), )) de_vaucouleurs_profile = apply_shear_and_shift( galsim.DeVaucouleurs( half_light_radius=half_light_radius_deg, flux=flux_nelec * self._gal_frac_dev, )) galaxy = exponential_profile + de_vaucouleurs_profile psf = galsim.Gaussian(flux=1, sigma=psf_sigma_degrees) return galsim.Convolve([galaxy, psf])
def _get_dev_model(self, spec): obj = galsim.DeVaucouleurs( half_light_radius=spec['hlr'], flux=spec['Flux'], ) return obj
def _get_object(self): hlr, flux = self._get_hlr_flux() disk_hlr = hlr if self.g_pdf is None: disk_g1, disk_g2 = 0.0, 0.0 else: disk_g1, disk_g2 = self.g_pdf.sample2d() all_obj = {} if 'bulge' in self['pdfs']: hlr_fac, fracdev, gfac, bulge_offset = self._get_bulge_stats() bulge_hlr = disk_hlr * hlr_fac bulge_g1, bulge_g2 = gfac * disk_g1, gfac * disk_g2 disk_flux = (1 - fracdev) * flux bulge_flux = fracdev * flux bulge = galsim.DeVaucouleurs( half_light_radius=bulge_hlr, flux=bulge_flux, ).shear( g1=bulge_g1, g2=bulge_g2, ).shift( dx=bulge_offset[1], dy=bulge_offset[0], ) all_obj['bulge'] = bulge else: disk_flux = flux disk = galsim.Exponential( half_light_radius=disk_hlr, flux=disk_flux, ).shear( g1=disk_g1, g2=disk_g2, ) all_obj['disk'] = disk if 'knots' in self['pdfs']: nknots, knots_flux = self._get_knots_stats(disk_flux) knots = galsim.RandomWalk( npoints=nknots, half_light_radius=disk_hlr, flux=knots_flux, ).shear(g1=disk_g1, g2=disk_g2) all_obj['knots'] = knots obj_cen1, obj_cen2 = self.position_pdf.sample() all_obj['cen'] = np.array([obj_cen1, obj_cen2]) return all_obj
def create_source(self, fractions, half_light_radius, minor_major_axis_ratio, position_angle): """Create a model for the on-sky profile of a single source. Size and shape parameter values for any component that is not present (because its fraction is zero) are ignored. Parameters ---------- fractions : array Array of length 2 giving the disk and bulge fractions, respectively, which must be in the range [0,1] (but this is not checked). If their sum is less than one, the remainder is modeled as a point-like component. half_light_radius : array Array of length 2 giving the disk and bulge half-light radii in arcseconds, respectively. minor_major_axis_ratio : array Array of length 2 giving the dimensionless on-sky ellipse minor / major axis ratio for the disk and bulge components, respectively. position_angle : array Array of length 2 giving the position angle in degrees of the on-sky disk and bluge ellipses, respectively. Angles are measured counter clockwise relative to the +x axis. Returns ------- galsim.GSObject A object representing the sum of all requested components with its total flux normalized to one. """ # This is a no-op but still required to define the namespace. import galsim components = [] if fractions[0] > 0: # Disk component components.append( galsim.Exponential( flux=fractions[0], half_light_radius=half_light_radius[0]).shear( q=minor_major_axis_ratio[0], beta=position_angle[0] * galsim.degrees)) if fractions[1] > 0: components.append( galsim.DeVaucouleurs( flux=fractions[1], half_light_radius=half_light_radius[1]).shear( q=minor_major_axis_ratio[1], beta=position_angle[1] * galsim.degrees)) star_fraction = 1 - fractions.sum() if star_fraction > 0: # Model a point-like source with a tiny (0.001 arcsec) Gaussian. # TODO: sigma should be in arcsec here, not microns! components.append( galsim.Gaussian(flux=star_fraction, sigma=1e-3 * self.image.scale)) # Combine the components and transform to focal-plane microns. return galsim.Add(components, gsparams=self.gsparams)
def test_ne(): """Test base.py GSObjects for not-equals.""" # Define some universal gsps gsp = galsim.GSParams(maxk_threshold=1.1e-3, folding_threshold=5.1e-3) # Sersic. Params include n, half_light_radius, scale_radius, flux, trunc, flux_untruncated # and gsparams. # The following should all test unequal: gals = [galsim.Sersic(n=1.1, half_light_radius=1.0), galsim.Sersic(n=1.2, half_light_radius=1.0), galsim.Sersic(n=1.1, half_light_radius=1.1), galsim.Sersic(n=1.1, scale_radius=1.0), galsim.Sersic(n=1.1, half_light_radius=1.0, flux=1.1), galsim.Sersic(n=1.1, half_light_radius=1.0, trunc=1.8), galsim.Sersic(n=1.1, half_light_radius=1.0, trunc=1.8, flux_untruncated=True), galsim.Sersic(n=1.1, half_light_radius=1.0, gsparams=gsp)] all_obj_diff(gals) # DeVaucouleurs. Params include half_light_radius, scale_radius, flux, trunc, flux_untruncated, # and gsparams. # The following should all test unequal: gals = [galsim.DeVaucouleurs(half_light_radius=1.0), galsim.DeVaucouleurs(half_light_radius=1.1), galsim.DeVaucouleurs(scale_radius=1.0), galsim.DeVaucouleurs(half_light_radius=1.0, flux=1.1), galsim.DeVaucouleurs(half_light_radius=1.0, trunc=2.0), galsim.DeVaucouleurs(half_light_radius=1.0, trunc=2.0, flux_untruncated=True), galsim.DeVaucouleurs(half_light_radius=1.0, gsparams=gsp)] all_obj_diff(gals)
def test_knots_hlr(): """ Create a random walk galaxy and test that the half light radius is consistent with the requested value Note for DeV profile we don't test npoints=3 because it fails """ # for checking accuracy, we need expected standard deviation of # the result interp_npts = np.array( [6, 7, 8, 9, 10, 15, 20, 30, 50, 75, 100, 150, 200, 500, 1000]) interp_hlr = np.array([ 7.511, 7.597, 7.647, 7.68, 7.727, 7.827, 7.884, 7.936, 7.974, 8.0, 8.015, 8.019, 8.031, 8.027, 8.043 ]) / 8.0 interp_std = np.array([ 2.043, 2.029, 1.828, 1.817, 1.67, 1.443, 1.235, 1.017, 0.8046, 0.6628, 0.5727, 0.4703, 0.4047, 0.255, 0.1851 ]) / 8.0 hlr = 8.0 # test these npoints npt_vals = [3, 10, 30, 60, 100, 1000] # should be within 5 sigma nstd = 5 # number of trials ntrial_vals = [100] * len(npt_vals) profs = [ galsim.Gaussian(half_light_radius=hlr), galsim.Exponential(half_light_radius=hlr), galsim.DeVaucouleurs(half_light_radius=hlr), ] for prof in profs: for ipts, npoints in enumerate(npt_vals): # DeV profile will fail for npoints==3 if isinstance(prof, galsim.DeVaucouleurs) and npoints == 3: continue ntrial = ntrial_vals[ipts] hlr_calc = np.zeros(ntrial) for i in range(ntrial): #rw=galsim.RandomKnots(npoints, hlr) rw = galsim.RandomKnots(npoints, profile=prof) hlr_calc[i] = rw.calculateHLR() mn = hlr_calc.mean() std_check = np.interp(npoints, interp_npts, interp_std * hlr) mess = "hlr for npoints: %d outside of expected range" % npoints assert abs(mn - hlr) < nstd * std_check, mess
def test_sersic_flux_scaling(): """Test flux scaling for Sersic. """ # decimal point to go to for parameter value comparisons param_decimal = 12 test_hlr = 1.8 test_flux = 17.9 test_sersic_trunc = [0., 8.5] if __name__ != "__main__": # If doing a pytest run, we don't actually need to do all 4 sersic n values. # Two should be enough to notice if there is a problem, and the full list will be tested # when running python test_base.py to try to diagnose the problem. test_sersic_n = [1.5, -4] else: test_sersic_n = [1.5, 2.5, 4, -4] # -4 means use explicit DeVauc rather than n=4 # loop through sersic n for test_n in test_sersic_n: # loop through sersic truncation for test_trunc in test_sersic_trunc: # init with hlr and flux only (should be ok given last tests) # n=-4 is code to use explicit DeVaucouleurs rather than Sersic(n=4). # It should be identical. if test_n == -4: init_obj = galsim.DeVaucouleurs(half_light_radius=test_hlr, flux=test_flux, trunc=test_trunc) else: init_obj = galsim.Sersic(test_n, half_light_radius=test_hlr, flux=test_flux, trunc=test_trunc) obj2 = init_obj * 2. np.testing.assert_almost_equal( init_obj.flux, test_flux, decimal=param_decimal, err_msg="Flux param inconsistent after __rmul__ (original).") np.testing.assert_almost_equal( obj2.flux, test_flux * 2., decimal=param_decimal, err_msg="Flux param inconsistent after __rmul__ (result).") obj2 = 2. * init_obj np.testing.assert_almost_equal( init_obj.flux, test_flux, decimal=param_decimal, err_msg="Flux param inconsistent after __mul__ (original).") np.testing.assert_almost_equal( obj2.flux, test_flux * 2., decimal=param_decimal, err_msg="Flux param inconsistent after __mul__ (result).") obj2 = init_obj / 2. np.testing.assert_almost_equal( init_obj.flux, test_flux, decimal=param_decimal, err_msg="Flux param inconsistent after __div__ (original).") np.testing.assert_almost_equal( obj2.flux, test_flux / 2., decimal=param_decimal, err_msg="Flux param inconsistent after __div__ (result).")
def __init__(self, identifier, redshift, ab_magnitude, ri_color, cosmic_shear_g1, cosmic_shear_g2, dx_arcsecs, dy_arcsecs, beta_radians, disk_flux, disk_hlr_arcsecs, disk_q, bulge_flux, bulge_hlr_arcsecs, bulge_q, agn_flux): self.identifier = identifier self.redshift = redshift self.ab_magnitude = ab_magnitude self.ri_color = ri_color self.dx_arcsecs = dx_arcsecs self.dy_arcsecs = dy_arcsecs self.cosmic_shear_g1 = cosmic_shear_g1 self.cosmic_shear_g2 = cosmic_shear_g2 components = [] # Initialize second-moments tensor. Note that we can only add the tensors for the # n = 1,4 components, as we do below, since they have the same centroid. self.second_moments = np.zeros((2, 2)) total_flux = disk_flux + bulge_flux + agn_flux self.disk_fraction = disk_flux / total_flux self.bulge_fraction = bulge_flux / total_flux if disk_flux > 0: disk = galsim.Exponential( flux=disk_flux, half_light_radius=disk_hlr_arcsecs).shear( q=disk_q, beta=beta_radians * galsim.radians) components.append(disk) self.second_moments += self.disk_fraction * sersic_second_moments( n=1, hlr=disk_hlr_arcsecs, q=disk_q, beta=beta_radians) if bulge_flux > 0: bulge = galsim.DeVaucouleurs( flux=bulge_flux, half_light_radius=bulge_hlr_arcsecs).shear( q=bulge_q, beta=beta_radians * galsim.radians) components.append(bulge) self.second_moments += self.bulge_fraction * sersic_second_moments( n=1, hlr=bulge_hlr_arcsecs, q=bulge_q, beta=beta_radians) # GalSim does not currently provide a "delta-function" component to model the AGN # so we use a very narrow Gaussian. See this GalSim issue for details: # https://github.com/GalSim-developers/GalSim/issues/533 if agn_flux > 0: agn = galsim.Gaussian(flux=agn_flux, sigma=1e-8) components.append(agn) # Combine the components into our final profile. self.profile = galsim.Add(components) # Apply transforms to build the final model. self.model = self.get_transformed_model() # Shear the second moments, if necessary. if self.cosmic_shear_g1 != 0 or self.cosmic_shear_g2 != 0: self.second_moments = sheared_second_moments( self.second_moments, self.cosmic_shear_g1, self.cosmic_shear_g2)
def test_hsmparams(): """Test the ability to set/change parameters that define how moments/shape estimation are done.""" import time t1 = time.time() # First make some profile, and make sure that we get the same answers when we specify default # hsmparams or don't specify hsmparams at all. default_hsmparams = galsim.hsm.HSMParams(nsig_rg=3.0, nsig_rg2=3.6, max_moment_nsig2=25.0, regauss_too_small=1, adapt_order=2, max_mom2_iter=400, num_iter_default=-1, bound_correct_wt=0.25, max_amoment=8000., max_ashift=15., ksb_moments_max=4, failed_moments=-1000.) bulge = galsim.DeVaucouleurs(half_light_radius = 0.3) disk = galsim.Exponential(half_light_radius = 0.5) disk.applyShear(e1=0.2, e2=-0.3) psf = galsim.Kolmogorov(fwhm = 0.6) pix = galsim.Pixel(0.18) gal = bulge + disk # equal weighting, i.e., B/T=0.5 tot_gal = galsim.Convolve(gal, psf, pix) tot_psf = galsim.Convolve(psf, pix) tot_gal_image = tot_gal.draw(dx=0.18) tot_psf_image = tot_psf.draw(dx=0.18) res = tot_gal_image.FindAdaptiveMom() res_def = tot_gal_image.FindAdaptiveMom(hsmparams = default_hsmparams) assert(equal_hsmshapedata(res, res_def)), 'Moment outputs differ when using default HSMParams' res2 = galsim.hsm.EstimateShear(tot_gal_image, tot_psf_image) res2_def = galsim.hsm.EstimateShear(tot_gal_image, tot_psf_image, hsmparams = default_hsmparams) assert(equal_hsmshapedata(res, res_def)), 'Shear outputs differ when using default HSMParams' try: # Then check failure modes: force it to fail by changing HSMParams. new_params_niter = galsim.hsm.HSMParams(max_mom2_iter = res.moments_n_iter-1) new_params_size = galsim.hsm.HSMParams(max_amoment = 0.3*res.moments_sigma**2) np.testing.assert_raises(RuntimeError, galsim.hsm.FindAdaptiveMom, tot_gal_image, hsmparams=new_params_niter) np.testing.assert_raises(RuntimeError, galsim.hsm.EstimateShear, tot_gal_image, tot_psf_image, hsmparams=new_params_size) except ImportError: print 'The assert_raises tests require nose' t2 = time.time() print 'time for %s = %.2f'%(funcname(),t2-t1)
def test_sersic_shoot(): """Test Sersic with photon shooting. Particularly the flux of the final image. """ rng = galsim.BaseDeviate(1234) obj = galsim.Sersic(n=1.5, half_light_radius=3.5, flux=1.e4) im = galsim.Image(100, 100, scale=1) im.setCenter(0, 0) added_flux, photons = obj.drawPhot(im, poisson_flux=False, rng=rng.duplicate()) print('obj.flux = ', obj.flux) print('added_flux = ', added_flux) print('photon fluxes = ', photons.flux.min(), '..', photons.flux.max()) print('image flux = ', im.array.sum()) assert np.isclose(added_flux, obj.flux) assert np.isclose(im.array.sum(), obj.flux) photons2 = obj.makePhot(poisson_flux=False, rng=rng) assert photons2 == photons, "Sersic makePhot not equivalent to drawPhot" obj = galsim.DeVaucouleurs(half_light_radius=3.5, flux=1.e4) # Need a larger image for devauc wings im = galsim.Image(1000, 1000, scale=1) im.setCenter(0, 0) added_flux, photons = obj.drawPhot(im, poisson_flux=False, rng=rng.duplicate()) print('obj.flux = ', obj.flux) print('added_flux = ', added_flux) print('photon fluxes = ', photons.flux.min(), '..', photons.flux.max()) print('image flux = ', im.array.sum()) assert np.isclose(added_flux, obj.flux) assert np.isclose(im.array.sum(), obj.flux) photons2 = obj.makePhot(poisson_flux=False, rng=rng) assert photons2 == photons, "Sersic makePhot not equivalent to drawPhot" # Can do up to around n=6 with this image if hlr is smaller. obj = galsim.Sersic(half_light_radius=0.9, n=6.2, flux=1.e4) added_flux, photons = obj.drawPhot(im, poisson_flux=False, rng=rng.duplicate()) print('obj.flux = ', obj.flux) print('added_flux = ', added_flux) print('photon fluxes = ', photons.flux.min(), '..', photons.flux.max()) print('image flux = ', im.array.sum()) assert np.isclose(added_flux, obj.flux) assert np.isclose(im.array.sum(), obj.flux) photons2 = obj.makePhot(poisson_flux=False, rng=rng) assert photons2 == photons, "Sersic makePhot not equivalent to drawPhot"
def test_devaucouleurs(): """Test various ways to build a DeVaucouleurs """ config = { 'gal1' : { 'type' : 'DeVaucouleurs' , 'half_light_radius' : 2 }, 'gal2' : { 'type' : 'DeVaucouleurs' , 'half_light_radius' : 1.7, 'flux' : 100 }, 'gal3' : { 'type' : 'DeVaucouleurs' , 'half_light_radius' : 3.5, 'flux' : 1.e6, 'ellip' : { 'type' : 'QBeta' , 'q' : 0.6, 'beta' : 0.39 * galsim.radians } }, 'gal4' : { 'type' : 'DeVaucouleurs' , 'half_light_radius' : 1, 'flux' : 50, 'dilate' : 3, 'ellip' : galsim.Shear(e1=0.3), 'rotate' : 12 * galsim.degrees, 'magnify' : 1.03, 'shear' : galsim.Shear(g1=0.03, g2=-0.05), 'shift' : { 'type' : 'XY', 'x' : 0.7, 'y' : -1.2 } }, 'gal5' : { 'type' : 'DeVaucouleurs' , 'half_light_radius' : 1, 'flux' : 50, 'gsparams' : { 'folding_threshold' : 1.e-4 } } } gal1a = galsim.config.BuildGSObject(config, 'gal1')[0] gal1b = galsim.DeVaucouleurs(half_light_radius = 2) gsobject_compare(gal1a, gal1b) gal2a = galsim.config.BuildGSObject(config, 'gal2')[0] gal2b = galsim.DeVaucouleurs(half_light_radius = 1.7, flux = 100) gsobject_compare(gal2a, gal2b) gal3a = galsim.config.BuildGSObject(config, 'gal3')[0] gal3b = galsim.DeVaucouleurs(half_light_radius = 3.5, flux = 1.e6) gal3b = gal3b.shear(q = 0.6, beta = 0.39 * galsim.radians) gsobject_compare(gal3a, gal3b) gal4a = galsim.config.BuildGSObject(config, 'gal4')[0] gal4b = galsim.DeVaucouleurs(half_light_radius = 1, flux = 50) gal4b = gal4b.dilate(3).shear(e1 = 0.3).rotate(12 * galsim.degrees).magnify(1.03) gal4b = gal4b.shear(g1 = 0.03, g2 = -0.05).shift(dx = 0.7, dy = -1.2) gsobject_compare(gal4a, gal4b) gal5a = galsim.config.BuildGSObject(config, 'gal5')[0] gsparams = galsim.GSParams(folding_threshold=1.e-4) gal5b = galsim.DeVaucouleurs(half_light_radius=1, flux=50, gsparams=gsparams) gsobject_compare(gal5a, gal5b, conv=galsim.Gaussian(sigma=1)) try: # Make sure they don't match when using the default GSParams gal5c = galsim.DeVaucouleurs(half_light_radius=1, flux=50) np.testing.assert_raises(AssertionError,gsobject_compare, gal5a, gal5c, conv=galsim.Gaussian(sigma=1)) except ImportError: print('The assert_raises tests require nose')
def render_galaxy( self, galaxy_params: Tensor, psf: galsim.GSObject, slen: int, offset: Optional[Tensor] = None, ) -> Tensor: assert offset is None or offset.shape == (2, ) if isinstance(galaxy_params, Tensor): galaxy_params = galaxy_params.cpu().detach() total_flux, disk_frac, beta_radians, disk_q, a_d, bulge_q, a_b = galaxy_params bulge_frac = 1 - disk_frac disk_flux = total_flux * disk_frac bulge_flux = total_flux * bulge_frac components = [] if disk_flux > 0: b_d = a_d * disk_q disk_hlr_arcsecs = np.sqrt(a_d * b_d) disk = galsim.Exponential( flux=disk_flux, half_light_radius=disk_hlr_arcsecs).shear( q=disk_q, beta=beta_radians * galsim.radians, ) components.append(disk) if bulge_flux > 0: b_b = bulge_q * a_b bulge_hlr_arcsecs = np.sqrt(a_b * b_b) bulge = galsim.DeVaucouleurs( flux=bulge_flux, half_light_radius=bulge_hlr_arcsecs).shear( q=bulge_q, beta=beta_radians * galsim.radians) components.append(bulge) galaxy = galsim.Add(components) gal_conv = galsim.Convolution(galaxy, psf) offset = offset if offset is None else offset.numpy() image = gal_conv.drawImage(nx=slen, ny=slen, method="auto", scale=self.pixel_scale, offset=offset) return torch.from_numpy(image.array).reshape(1, slen, slen)
def _get_bd_model(self, spec): rng = self.rng flux = spec['Flux'] fracdev = rng.uniform(low=0.0, high=1.0) bulge_flux = fracdev * flux disk_flux = (1.0 - fracdev) * flux disk_hlr = spec['hlr'] if 'bulge_size_factor' in spec: frange = spec['bulge_size_factor']['range'] bulge_hlr = disk_hlr * rng.uniform( low=frange[0], high=frange[1], ) else: bulge_hlr = disk_hlr if 'knots' in spec: disk_flux, knot_flux = self._split_flux_with_knots(spec, disk_flux) disk = galsim.Exponential( half_light_radius=disk_hlr, flux=disk_flux, ) bulge = galsim.DeVaucouleurs( half_light_radius=bulge_hlr, flux=bulge_flux, ) if 'knots' in spec: knots = self._get_knots(spec, disk_hlr, knot_flux) disk = galsim.Add([disk, knots]) disk = self._add_ellipticity(disk, spec) bulge = self._add_ellipticity(bulge, spec) return galsim.Add([disk, bulge])
def main(): p = get_params() rng = galsim.UniformDeviate(p.random_seed) #where to save images image_dir = os.path.join('/home', 'ckrawiec', 'sims', 'images') target_file_name = "stamps_{0}_.fits".format(p.id) template_file_name = "template_stamps_{0}_.fits".format(p.id) if p.noise_suppression == False: gal_name = os.path.splitext(target_file_name) else: gal_name = os.path.splitext(template_file_name) #initial noise noise = galsim.GaussianNoise(rng) #create test image to determine noise level if p.noise_suppression == False: test_psf = galsim.Moffat(3.5, half_light_radius=p.psf_hlr * p.pixel_scale) test_psf = test_psf.shear(e2=p.psf_e2) test_bulge = galsim.DeVaucouleurs(half_light_radius=( (p.gal_hlr_max - p.gal_hlr_min) / 2. + p.gal_hlr_min) * p.pixel_scale) test_disk = galsim.Exponential(half_light_radius=( (p.gal_hlr_max - p.gal_hlr_min) / 2. + p.gal_hlr_min) * p.pixel_scale) test_gal = 0.5 * test_bulge + 0.5 * test_disk test_gal = test_gal.withFlux(p.gal_flux_min) test_image = galsim.ImageF(p.stamp_size, p.stamp_size, scale=p.pixel_scale) test_final = galsim.Convolve([test_psf, test_gal]) test_final.drawImage(test_image) noise_var = test_image.addNoiseSNR(noise, p.gal_snr_min, preserve_flux=True) print noise_var #final noise noise = galsim.GaussianNoise(rng, sigma=np.sqrt(noise_var)) #make galaxy image gal_image = galsim.ImageF(p.nx_tiles * p.stamp_size, p.ny_tiles * p.stamp_size, scale=p.pixel_scale) #make ellipticity distribution from sigma ellip_dist = BobsEDist(p.gal_ellip_sigma, rng) for ifile in range(p.n_files): #all files have same psf psf = galsim.Moffat(3.5, half_light_radius=p.psf_hlr * p.pixel_scale) psf = psf.shear(e2=p.psf_e2) psf_image = galsim.ImageF(p.stamp_size, p.stamp_size, scale=p.pixel_scale) psf.drawImage(psf_image) for iy in range(p.ny_tiles): for ix in range(p.nx_tiles): b = galsim.BoundsI(ix * p.stamp_size + 1, (ix + 1) * p.stamp_size, iy * p.stamp_size + 1, (iy + 1) * p.stamp_size) sub_gal_image = gal_image[b] hlr = (rng() * (p.gal_hlr_max - p.gal_hlr_min) + p.gal_hlr_min) * p.pixel_scale flux = rng() * (p.gal_flux_max - p.gal_flux_min) + p.gal_flux_min bulge_frac = rng() e1, e2 = ellip_dist.sample() #galaxy = ExponentialDisk + DeVaucouleurs this_bulge = galsim.DeVaucouleurs(half_light_radius=hlr) this_disk = galsim.Exponential(half_light_radius=hlr) #same ellipticity this_bulge = this_bulge.shear(e1=e1, e2=e2) this_disk = this_disk.shear(e1=e1, e2=e2) #Apply a shift in disk center within one pixel shift_r = p.pixel_scale / 2. dx = shift_r * (rng() * 2. - 1.) dy = shift_r * (rng() * 2. - 1.) this_disk = this_disk.shift(dx, dy) #Apply a shift in bulge center within circle of radius hlr theta = rng() * 2. * np.pi r = rng() * hlr dx = r * np.cos(theta) dy = r * np.sin(theta) this_bulge = this_bulge.shift(dx, dy) this_gal = bulge_frac * this_bulge + (1 - bulge_frac) * this_disk this_gal = this_gal.withFlux(flux) #All galaxies have same applied shear this_gal = this_gal.shear(g1=p.g1, g2=p.g2) final = galsim.Convolve([psf, this_gal]) final.drawImage(sub_gal_image) if p.noise_suppression == False: sub_gal_image.addNoise(noise) gal_file = os.path.join( image_dir, ''.join([gal_name[0], str(ifile + 1).zfill(2), gal_name[1]])) psf_file = os.path.join( image_dir, ''.join([gal_name[0], str(ifile + 1).zfill(2), '.psf'])) gal_image.write(gal_file) psf_image.write(psf_file)
if (abs((e10[i]**2) * (e20[i]**2)) > 1.0): 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] for i in range(m): BJe1array = [[], [], [], []] BJe2array = [[], [], [], []] RGe1array = [[], [], [], []] RGe2array = [[], [], [], []] Me1array = [[], [], [], []] Me2array = [[], [], [], []] for j in range(4): for p in range(2):
of the LSST stack. But it's still better to save the script as well, even if it can't usually be run. """ import galsim import lsst.shapelet.tractor GALAXY_RADIUS = 8.0 PSF_SIGMA = 2.0 E1 = 0.3 E2 = -0.2 PROFILES = [ ("exp", galsim.Exponential(half_light_radius=GALAXY_RADIUS), 9, 8), ("ser2", galsim.Sersic(n=2.0, half_light_radius=GALAXY_RADIUS), 9, 8), ("ser3", galsim.Sersic(n=3.0, half_light_radius=GALAXY_RADIUS), 9, 8), ("dev", galsim.DeVaucouleurs(half_light_radius=GALAXY_RADIUS), 9, 8), ] MAKE_COMPONENT_IMAGES = False def makeGaussian(flux, e1, e2, sigma): g = galsim.Gaussian(flux=flux, sigma=sigma) g.applyShear(e1=e1, e2=e2) return g def main(): for profile, exactGal, nComponents, maxRadius in PROFILES: exactGal.applyShear(e1=E1, e2=E2) psf = galsim.Gaussian(sigma=PSF_SIGMA)
def test_flip(): """Test several ways to flip a profile """ # The Shapelet profile has the advantage of being fast and not circularly symmetric, so # it is a good test of the actual code for doing the flips (in SBTransform). # But since the bug Rachel reported in #645 was actually in SBInterpolatedImage # (one calculation implicitly assumed dx > 0), it seems worthwhile to run through all the # classes to make sure we hit everything with negative steps for dx and dy. prof_list = [ galsim.Shapelet(sigma=0.17, order=2, bvec=[1.7, 0.01,0.03, 0.29, 0.33, -0.18]), ] if __name__ == "__main__": image_dir = './real_comparison_images' catalog_file = 'test_catalog.fits' rgc = galsim.RealGalaxyCatalog(catalog_file, dir=image_dir) # Some of these are slow, so only do the Shapelet test as part of the normal unit tests. prof_list += [ galsim.Airy(lam_over_diam=0.17, flux=1.7), galsim.Airy(lam_over_diam=0.17, obscuration=0.2, flux=1.7), # Box gets rendered with real-space convolution. The default accuracy isn't quite # enough to get the flip to match at 6 decimal places. galsim.Box(0.17, 0.23, flux=1.7, gsparams=galsim.GSParams(realspace_relerr=1.e-6)), # Without being convolved by anything with a reasonable k cutoff, this needs # a very large fft. galsim.DeVaucouleurs(half_light_radius=0.17, flux=1.7), # I don't really understand why this needs a lower maxk_threshold to work, but # without it, the k-space tests fail. galsim.Exponential(scale_radius=0.17, flux=1.7, gsparams=galsim.GSParams(maxk_threshold=1.e-4)), galsim.Gaussian(sigma=0.17, flux=1.7), galsim.Kolmogorov(fwhm=0.17, flux=1.7), galsim.Moffat(beta=2.5, fwhm=0.17, flux=1.7), galsim.Moffat(beta=2.5, fwhm=0.17, flux=1.7, trunc=0.82), galsim.OpticalPSF(lam_over_diam=0.17, obscuration=0.2, nstruts=6, coma1=0.2, coma2=0.5, defocus=-0.1, flux=1.7), # Like with Box, we need to increase the real-space convolution accuracy. # This time lowering both relerr and abserr. galsim.Pixel(0.23, flux=1.7, gsparams=galsim.GSParams(realspace_relerr=1.e-6, realspace_abserr=1.e-8)), # Note: RealGalaxy should not be rendered directly because of the deconvolution. # Here we convolve it by a Gaussian that is slightly larger than the original PSF. galsim.Convolve([ galsim.RealGalaxy(rgc, index=0, flux=1.7), # "Real" RealGalaxy galsim.Gaussian(sigma=0.08) ]), galsim.Convolve([ galsim.RealGalaxy(rgc, index=1, flux=1.7), # "Fake" RealGalaxy galsim.Gaussian(sigma=0.08) ]), # (cf. test_real.py) galsim.Spergel(nu=-0.19, half_light_radius=0.17, flux=1.7), galsim.Spergel(nu=0., half_light_radius=0.17, flux=1.7), galsim.Spergel(nu=0.8, half_light_radius=0.17, flux=1.7), galsim.Sersic(n=2.3, half_light_radius=0.17, flux=1.7), galsim.Sersic(n=2.3, half_light_radius=0.17, flux=1.7, trunc=0.82), # The shifts here caught a bug in how SBTransform handled the recentering. # Two of the shifts (0.125 and 0.375) lead back to 0.0 happening on an integer # index, which now works correctly. galsim.Sum([ galsim.Gaussian(sigma=0.17, flux=1.7).shift(-0.2,0.125), galsim.Exponential(scale_radius=0.23, flux=3.1).shift(0.375,0.23)]), galsim.TopHat(0.23, flux=1.7), # Box and Pixel use real-space convolution. Convolve with a Gaussian to get fft. galsim.Convolve([ galsim.Box(0.17, 0.23, flux=1.7).shift(-0.2,0.1), galsim.Gaussian(sigma=0.09) ]), galsim.Convolve([ galsim.TopHat(0.17, flux=1.7).shift(-0.275,0.125), galsim.Gaussian(sigma=0.09) ]), # Test something really crazy with several layers worth of transformations galsim.Convolve([ galsim.Sum([ galsim.Gaussian(sigma=0.17, flux=1.7).shear(g1=0.1,g2=0.2).shift(2,3), galsim.Kolmogorov(fwhm=0.33, flux=3.9).transform(0.31,0.19,-0.23,0.33) * 88., galsim.Box(0.11, 0.44, flux=4).rotate(33 * galsim.degrees) / 1.9 ]).shift(-0.3,1), galsim.AutoConvolve(galsim.TopHat(0.5).shear(g1=0.3,g2=0)).rotate(3*galsim.degrees), (galsim.AutoCorrelate(galsim.Box(0.2, 0.3)) * 11).shift(3,2).shift(2,-3) * 0.31 ]).shift(0,0).transform(0,-1,-1,0).shift(-1,1) ] s = galsim.Shear(g1=0.11, g2=-0.21) s1 = galsim.Shear(g1=0.11, g2=0.21) # Appropriate for the flips around x and y axes s2 = galsim.Shear(g1=-0.11, g2=-0.21) # Appropriate for the flip around x=y # Also use shears with just a g1 to get dx != dy, but dxy, dyx = 0. q = galsim.Shear(g1=0.11, g2=0.) q1 = galsim.Shear(g1=0.11, g2=0.) # Appropriate for the flips around x and y axes q2 = galsim.Shear(g1=-0.11, g2=0.) # Appropriate for the flip around x=y decimal=6 # Oddly, these aren't as precise as I would have expected. # Even when we only go to this many digits of accuracy, the Exponential needed # a lower than default value for maxk_threshold. im = galsim.ImageD(16,16, scale=0.05) for prof in prof_list: print('prof = ',prof) # Not all profiles are expected to have a max_sb value close to the maximum pixel value, # so mark the ones where we don't want to require this to be true. close_maxsb = True name = str(prof) if ('DeVauc' in name or 'Sersic' in name or 'Spergel' in name or 'Optical' in name or 'shift' in name): close_maxsb = False # Make sure we hit all 4 fill functions. # image_x uses fillXValue with izero, jzero # image_x1 uses fillXValue with izero, jzero, and unequal dx,dy # image_x2 uses fillXValue with dxy, dyx # image_k uses fillKValue with izero, jzero # image_k1 uses fillKValue with izero, jzero, and unequal dx,dy # image_k2 uses fillKValue with dxy, dyx image_x = prof.drawImage(image=im.copy(), method='no_pixel') image_x1 = prof.shear(q).drawImage(image=im.copy(), method='no_pixel') image_x2 = prof.shear(s).drawImage(image=im.copy(), method='no_pixel') image_k = prof.drawImage(image=im.copy()) image_k1 = prof.shear(q).drawImage(image=im.copy()) image_k2 = prof.shear(s).drawImage(image=im.copy()) if close_maxsb: np.testing.assert_allclose( image_x.array.max(), prof.max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image_x1.array.max(), prof.shear(q).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image_x2.array.max(), prof.shear(s).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") # Flip around y axis (i.e. x -> -x) flip1 = prof.transform(-1, 0, 0, 1) image2_x = flip1.drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x.array, image2_x.array[:,::-1], decimal=decimal, err_msg="Flipping image around y-axis failed x test") image2_x1 = flip1.shear(q1).drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x1.array, image2_x1.array[:,::-1], decimal=decimal, err_msg="Flipping image around y-axis failed x1 test") image2_x2 = flip1.shear(s1).drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x2.array, image2_x2.array[:,::-1], decimal=decimal, err_msg="Flipping image around y-axis failed x2 test") image2_k = flip1.drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k.array, image2_k.array[:,::-1], decimal=decimal, err_msg="Flipping image around y-axis failed k test") image2_k1 = flip1.shear(q1).drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k1.array, image2_k1.array[:,::-1], decimal=decimal, err_msg="Flipping image around y-axis failed k1 test") image2_k2 = flip1.shear(s1).drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k2.array, image2_k2.array[:,::-1], decimal=decimal, err_msg="Flipping image around y-axis failed k2 test") if close_maxsb: np.testing.assert_allclose( image2_x.array.max(), flip1.max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image2_x1.array.max(), flip1.shear(q).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image2_x2.array.max(), flip1.shear(s).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") # Flip around x axis (i.e. y -> -y) flip2 = prof.transform(1, 0, 0, -1) image2_x = flip2.drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x.array, image2_x.array[::-1,:], decimal=decimal, err_msg="Flipping image around x-axis failed x test") image2_x1 = flip2.shear(q1).drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x1.array, image2_x1.array[::-1,:], decimal=decimal, err_msg="Flipping image around x-axis failed x1 test") image2_x2 = flip2.shear(s1).drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x2.array, image2_x2.array[::-1,:], decimal=decimal, err_msg="Flipping image around x-axis failed x2 test") image2_k = flip2.drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k.array, image2_k.array[::-1,:], decimal=decimal, err_msg="Flipping image around x-axis failed k test") image2_k1 = flip2.shear(q1).drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k1.array, image2_k1.array[::-1,:], decimal=decimal, err_msg="Flipping image around x-axis failed k1 test") image2_k2 = flip2.shear(s1).drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k2.array, image2_k2.array[::-1,:], decimal=decimal, err_msg="Flipping image around x-axis failed k2 test") if close_maxsb: np.testing.assert_allclose( image2_x.array.max(), flip2.max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image2_x1.array.max(), flip2.shear(q).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image2_x2.array.max(), flip2.shear(s).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") # Flip around x=y (i.e. y -> x, x -> y) flip3 = prof.transform(0, 1, 1, 0) image2_x = flip3.drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x.array, np.transpose(image2_x.array), decimal=decimal, err_msg="Flipping image around x=y failed x test") image2_x1 = flip3.shear(q2).drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x1.array, np.transpose(image2_x1.array), decimal=decimal, err_msg="Flipping image around x=y failed x1 test") image2_x2 = flip3.shear(s2).drawImage(image=im.copy(), method='no_pixel') np.testing.assert_array_almost_equal( image_x2.array, np.transpose(image2_x2.array), decimal=decimal, err_msg="Flipping image around x=y failed x2 test") image2_k = flip3.drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k.array, np.transpose(image2_k.array), decimal=decimal, err_msg="Flipping image around x=y failed k test") image2_k1 = flip3.shear(q2).drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k1.array, np.transpose(image2_k1.array), decimal=decimal, err_msg="Flipping image around x=y failed k1 test") image2_k2 = flip3.shear(s2).drawImage(image=im.copy()) np.testing.assert_array_almost_equal( image_k2.array, np.transpose(image2_k2.array), decimal=decimal, err_msg="Flipping image around x=y failed k2 test") if close_maxsb: np.testing.assert_allclose( image2_x.array.max(), flip3.max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image2_x1.array.max(), flip3.shear(q).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") np.testing.assert_allclose( image2_x2.array.max(), flip3.shear(s).max_sb*im.scale**2, rtol=0.2, err_msg="max_sb did not match maximum pixel value") do_pickle(prof, lambda x: x.drawImage(image=im.copy(), method='no_pixel')) do_pickle(flip1, lambda x: x.drawImage(image=im.copy(), method='no_pixel')) do_pickle(flip2, lambda x: x.drawImage(image=im.copy(), method='no_pixel')) do_pickle(flip3, lambda x: x.drawImage(image=im.copy(), method='no_pixel')) do_pickle(prof) do_pickle(flip1) do_pickle(flip2) do_pickle(flip3)
def main(argv): # Where to find and output data path, filename = os.path.split(__file__) datapath = os.path.abspath(os.path.join(path, "data/")) outpath = os.path.abspath(os.path.join(path, "output/")) # In non-script code, use getLogger(__name__) at module scope instead. logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("demo12") # initialize (pseudo-)random number generator random_seed = 1234567 rng = galsim.BaseDeviate(random_seed) # read in SEDs SED_names = ['CWW_E_ext', 'CWW_Sbc_ext', 'CWW_Scd_ext', 'CWW_Im_ext'] SEDs = {} for SED_name in SED_names: SED_filename = os.path.join(galsim.meta_data.share_dir, '{0}.sed'.format(SED_name)) # Here we create some galsim.SED objects to hold star or galaxy spectra. The most # convenient way to create realistic spectra is to read them in from a two-column ASCII # file, where the first column is wavelength and the second column is flux. Wavelengths in # the example SED files are in Angstroms, flux in flambda. We use a set of files that are # distributed with GalSim in the share/ directory. SED = galsim.SED(SED_filename, wave_type='Ang', flux_type='flambda') # The normalization of SEDs affects how many photons are eventually drawn into an image. # One way to control this normalization is to specify the flux density in photons per nm # at a particular wavelength. For example, here we normalize such that the photon density # is 1 photon per nm at 500 nm. SEDs[SED_name] = SED.withFluxDensity(target_flux_density=1.0, wavelength=500) logger.debug('Successfully read in SEDs') # read in the LSST filters filter_names = 'ugrizy' filters = {} for filter_name in filter_names: filter_filename = os.path.join(datapath, 'LSST_{0}.dat'.format(filter_name)) # Here we create some galsim.Bandpass objects to represent the filters we're observing # through. These include the entire imaging system throughput including the atmosphere, # reflective and refractive optics, filters, and the CCD quantum efficiency. These are # also conveniently read in from two-column ASCII files where the first column is # wavelength and the second column is dimensionless throughput. The example filter files # units of nanometers for the wavelength type, so we specify that using the required # `wave_type` argument. filters[filter_name] = galsim.Bandpass(filter_filename, wave_type='nm') # For speed, we can thin out the wavelength sampling of the filter a bit. # In the following line, `rel_err` specifies the relative error when integrating over just # the filter (however, this is not necessarily the relative error when integrating over the # filter times an SED). filters[filter_name] = filters[filter_name].thin(rel_err=1e-4) logger.debug('Read in filters') pixel_scale = 0.2 # arcseconds #----------------------------------------------------------------------------------------------- # Part A: chromatic de Vaucouleurs galaxy # Here we create a chromatic version of a de Vaucouleurs profile using the Chromatic class. # This class lets one create chromatic versions of any galsim GSObject class. The first # argument is the GSObject instance to be chromaticized, and the second argument is the # profile's SED. logger.info('') logger.info('Starting part A: chromatic De Vaucouleurs galaxy') redshift = 0.8 mono_gal = galsim.DeVaucouleurs(half_light_radius=0.5) SED = SEDs['CWW_E_ext'].atRedshift(redshift) gal = galsim.Chromatic(mono_gal, SED) # You can still shear, shift, and dilate the resulting chromatic object. gal = gal.shear(g1=0.5, g2=0.3).dilate(1.05).shift((0.0, 0.1)) logger.debug('Created Chromatic') # convolve with PSF to make final profile PSF = galsim.Moffat(fwhm=0.6, beta=2.5) final = galsim.Convolve([gal, PSF]) logger.debug('Created final profile') # draw profile through LSST filters gaussian_noise = galsim.GaussianNoise(rng, sigma=0.1) for filter_name, filter_ in filters.iteritems(): img = galsim.ImageF(64, 64, scale=pixel_scale) final.drawImage(filter_, image=img) img.addNoise(gaussian_noise) logger.debug('Created {0}-band image'.format(filter_name)) out_filename = os.path.join(outpath, 'demo12a_{0}.fits'.format(filter_name)) galsim.fits.write(img, out_filename) logger.debug('Wrote {0}-band image to disk'.format(filter_name)) logger.info('Added flux for {0}-band image: {1}'.format( filter_name, img.added_flux)) logger.info( 'You can display the output in ds9 with a command line that looks something like:' ) logger.info( 'ds9 output/demo12a_*.fits -match scale -zoom 2 -match frame image &') #----------------------------------------------------------------------------------------------- # Part B: chromatic bulge+disk galaxy logger.info('') logger.info('Starting part B: chromatic bulge+disk galaxy') redshift = 0.8 # make a bulge ... mono_bulge = galsim.DeVaucouleurs(half_light_radius=0.5) bulge_SED = SEDs['CWW_E_ext'].atRedshift(redshift) # The `*` operator can be used as a shortcut for creating a chromatic version of a GSObject: bulge = mono_bulge * bulge_SED bulge = bulge.shear(g1=0.12, g2=0.07) logger.debug('Created bulge component') # ... and a disk ... mono_disk = galsim.Exponential(half_light_radius=2.0) disk_SED = SEDs['CWW_Im_ext'].atRedshift(redshift) disk = mono_disk * disk_SED disk = disk.shear(g1=0.4, g2=0.2) logger.debug('Created disk component') # ... and then combine them. bdgal = 1.1 * ( 0.8 * bulge + 4 * disk ) # you can add and multiply ChromaticObjects just like GSObjects bdfinal = galsim.Convolve([bdgal, PSF]) # Note that at this stage, our galaxy is chromatic but our PSF is still achromatic. Part C) # below will dive into chromatic PSFs. logger.debug('Created bulge+disk galaxy final profile') # draw profile through LSST filters gaussian_noise = galsim.GaussianNoise(rng, sigma=0.02) for filter_name, filter_ in filters.iteritems(): img = galsim.ImageF(64, 64, scale=pixel_scale) bdfinal.drawImage(filter_, image=img) img.addNoise(gaussian_noise) logger.debug('Created {0}-band image'.format(filter_name)) out_filename = os.path.join(outpath, 'demo12b_{0}.fits'.format(filter_name)) galsim.fits.write(img, out_filename) logger.debug('Wrote {0}-band image to disk'.format(filter_name)) logger.info('Added flux for {0}-band image: {1}'.format( filter_name, img.added_flux)) logger.info( 'You can display the output in ds9 with a command line that looks something like:' ) logger.info( 'ds9 -rgb -blue -scale limits -0.2 0.8 output/demo12b_r.fits -green -scale limits' + ' -0.25 1.0 output/demo12b_i.fits -red -scale limits -0.25 1.0 output/demo12b_z.fits' + ' -zoom 2 &') #----------------------------------------------------------------------------------------------- # Part C: chromatic PSF logger.info('') logger.info('Starting part C: chromatic PSF') redshift = 0.0 mono_gal = galsim.Exponential(half_light_radius=0.5) SED = SEDs['CWW_Im_ext'].atRedshift(redshift) # Here's another way to set the normalization of the SED. If we want 50 counts to be drawn # when observing an object with this SED through the LSST g-band filter, for instance, then we # can do: SED = SED.withFlux(50.0, filters['g']) # The flux drawn through other bands, which sample different parts of the SED and have different # throughputs, will, of course, be different. gal = mono_gal * SED gal = gal.shear(g1=0.5, g2=0.3) logger.debug('Created `Chromatic` galaxy') # For a ground-based PSF, two chromatic effects are introduced by the atmosphere: # (i) differential chromatic refraction (DCR), and (ii) wavelength-dependent seeing. # # DCR shifts the position of the PSF as a function of wavelength. Blue light is shifted # toward the zenith slightly more than red light. # # Kolmogorov turbulence in the atmosphere leads to a seeing size (e.g., FWHM) that scales with # wavelength to the (-0.2) power. # # The ChromaticAtmosphere function will attach both of these effects to a fiducial PSF at # some fiducial wavelength. # First we define a monochromatic PSF to be the fiducial PSF. PSF_500 = galsim.Moffat(beta=2.5, fwhm=0.5) # Then we use ChromaticAtmosphere to manipulate this fiducial PSF as a function of wavelength. # ChromaticAtmosphere also needs to know the wavelength of the fiducial PSF, and the location # and orientation of the object with respect to the zenith. This final piece of information # can be specified in several ways (see the ChromaticAtmosphere docstring for all of them). # Here are a couple ways: let's pretend our object is located near M101 on the sky, we observe # it 1 hour before it transits and we're observing from Mauna Kea. ra = galsim.HMS_Angle("14:03:13") # hours : minutes : seconds dec = galsim.DMS_Angle("54:20:57") # degrees : minutes : seconds m101 = galsim.CelestialCoord(ra, dec) latitude = 19.8207 * galsim.degrees # latitude of Mauna Kea HA = -1.0 * galsim.hours # Hour angle = one hour before transit # Then we can compute the zenith angle and parallactic angle (which is is the position angle # of the zenith measured from North through East) of this object: za, pa = galsim.dcr.zenith_parallactic_angles(m101, HA=HA, latitude=latitude) # And then finally, create the chromatic PSF PSF = galsim.ChromaticAtmosphere(PSF_500, 500.0, zenith_angle=za, parallactic_angle=pa) # We could have also just passed `m101`, `latitude` and `HA` to ChromaticAtmosphere directly: PSF = galsim.ChromaticAtmosphere(PSF_500, 500.0, obj_coord=m101, latitude=latitude, HA=HA) # and proceed like normal. # convolve with galaxy to create final profile final = galsim.Convolve([gal, PSF]) logger.debug('Created chromatic PSF finale profile') # Draw profile through LSST filters gaussian_noise = galsim.GaussianNoise(rng, sigma=0.03) for filter_name, filter_ in filters.iteritems(): img = galsim.ImageF(64, 64, scale=pixel_scale) final.drawImage(filter_, image=img) img.addNoise(gaussian_noise) logger.debug('Created {0}-band image'.format(filter_name)) out_filename = os.path.join(outpath, 'demo12c_{0}.fits'.format(filter_name)) galsim.fits.write(img, out_filename) logger.debug('Wrote {0}-band image to disk'.format(filter_name)) logger.info('Added flux for {0}-band image: {1}'.format( filter_name, img.added_flux)) logger.info( 'You can display the output in ds9 with a command line that looks something like:' ) logger.info( 'ds9 output/demo12c_*.fits -match scale -zoom 2 -match frame image -blink &' )
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( alias_threshold= 1.e-2, # maximum fractional flux that may be aliased 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 pixel: pix = galsim.Pixel(xw=pixel_scale) # 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) optics = galsim.OpticalPSF(lam_over_diam=0.6 * psf_fwhm, obscuration=0.4, defocus=0.1, astig1=0.3, astig2=-0.2, coma1=0.2, coma2=0.1, spher=-0.3, 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) bulge = galsim.Sersic(half_light_radius=0.7, n=3.2, 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) # Get a new copy, we'll want to keep the original unmodified. gal1 = gal.copy() # Generate random variates: flux = rng() * (gal_flux_max - gal_flux_min) + gal_flux_min gal1.setFlux(flux) hlr = rng() * (gal_hlr_max - gal_hlr_min) + gal_hlr_min gal1.applyDilation(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) gal1.applyShear(gal_shape) # Build the final object by convolving the galaxy, PSF and pixel response. final = galsim.Convolve([psf, pix, gal1]) # For photon shooting, need a version without the pixel (see below). final_nopix = galsim.Convolve([psf, gal1]) # Create the large, double width output image image = galsim.ImageF(2 * nx + 2, ny) # Rather than provide a dx= argument to the draw commands, we can also # set the pixel scale in the image itself with setScale. image.setScale(pixel_scale) # Assign the following two "ImageViews", 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 final.draw(fft_image) 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. # Photon shooting automatically convolves by the pixel, so we've made sure not # to include it in the profile! final_nopix.drawShoot(phot_image, 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 main(argv): """ Make a fits image cube using parameters from an input catalog - The number of images in the cube matches the number of rows in the catalog. - Each image size is computed automatically by GalSim based on the Nyquist size. - Only galaxies. No stars. - PSF is Moffat - Each galaxy is bulge plus disk: deVaucouleurs + Exponential. - A fraction of the disk flux is placed into point sources, which can model knots of star formation. - The catalog's columns are: 0 PSF beta (Moffat exponent) 1 PSF FWHM 2 PSF e1 3 PSF e2 4 PSF trunc 5 Disc half-light-radius 6 Disc e1 7 Disc e2 8 Bulge half-light-radius 9 Bulge e1 10 Bulge e2 11 Galaxy dx (the two components have same center) 12 Galaxy dy - Applied shear is the same for each galaxy - Noise is Poisson using a nominal sky value of 1.e6 """ logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("demo4") # Define some parameters we'll use below and make directories if needed. cat_file_name = os.path.join('input', 'galsim_default_input.asc') if not os.path.isdir('output'): os.mkdir('output') multi_file_name = os.path.join('output', 'multi.fits') random_seed = 8241573 sky_level = 1.e6 # ADU / arcsec^2 pixel_scale = 1.0 # arcsec / pixel (size units in input catalog are pixels) gal_flux = 1.e6 # arbitrary choice, makes nice (not too) noisy images gal_g1 = -0.009 # gal_g2 = 0.011 # # the fraction of flux in each component # 40% is in the bulge, 60% in a disk. 70% of that disk light is placed # into point sources distributed as a random walk bulge_frac = 0.4 disk_frac = 0.6 knot_frac = 0.42 smooth_disk_frac = 0.18 # number of knots of star formation. To simulate a nice irregular (all the # flux is in knots) we find ~100 is a minimum number needed, but we will # just use 10 here to make the demo run fast. n_knots = 10 xsize = 64 # pixels ysize = 64 # pixels logger.info('Starting demo script 4 using:') logger.info(' - parameters taken from catalog %r', cat_file_name) logger.info(' - Moffat PSF (parameters from catalog)') logger.info(' - pixel scale = %.2f', pixel_scale) logger.info(' - Bulge + Disc galaxies (parameters from catalog)') logger.info(' - 100 Point sources, distributed as random walk') logger.info(' - Applied gravitational shear = (%.3f,%.3f)', gal_g1, gal_g2) logger.info(' - Poisson noise (sky level = %.1e).', sky_level) # Read in the input catalog cat = galsim.Catalog(cat_file_name) # save a list of the galaxy images in the "images" list variable: images = [] for k in range(cat.nobjects): # Initialize the (pseudo-)random number generator that we will be using below. # Use a different random seed for each object to get different noise realizations. # Using sequential random seeds here is safer than it sounds. We use Mersenne Twister # random number generators that are designed to be used with this kind of seeding. # However, to be extra safe, we actually initialize one random number generator with this # seed, generate and throw away two random values with that, and then use the next value # to seed a completely different Mersenne Twister RNG. The result is that successive # RNGs created this way produce very independent random number streams. rng = galsim.BaseDeviate(random_seed + k + 1) # Take the Moffat beta from the first column (called 0) of the input catalog: # Note: cat.get(k,col) returns a string. To get the value as a float, use either # cat.getFloat(k,col) or float(cat.get(k,col)) beta = cat.getFloat(k, 0) # A Moffat's size may be either scale_radius, fwhm, or half_light_radius. # Here we use fwhm, taking from the catalog as well. fwhm = cat.getFloat(k, 1) # A Moffat profile may be truncated if desired # The units for this are expected to be arcsec (or specifically -- whatever units # you are using for all the size values as defined by the pixel_scale). trunc = cat.getFloat(k, 4) # Note: You may omit the flux, since the default is flux=1. psf = galsim.Moffat(beta=beta, fwhm=fwhm, trunc=trunc) # Take the (e1, e2) shape parameters from the catalog as well. psf = psf.shear(e1=cat.getFloat(k, 2), e2=cat.getFloat(k, 3)) # Galaxy is a bulge + disk(+knots) with parameters taken from the catalog: # put some fraction of the disk light into knots of star formation disk_hlr = cat.getFloat(k, 5) disk_e1 = cat.getFloat(k, 6) disk_e2 = cat.getFloat(k, 7) bulge_hlr = cat.getFloat(k, 8) bulge_e1 = cat.getFloat(k, 9) bulge_e2 = cat.getFloat(k, 10) smooth_disk = galsim.Exponential(flux=smooth_disk_frac, half_light_radius=disk_hlr) knots = galsim.RandomKnots(n_knots, half_light_radius=disk_hlr, flux=knot_frac, rng=rng) disk = galsim.Add([smooth_disk, knots]) disk = disk.shear(e1=disk_e1, e2=disk_e2) # the rest of the light goes into the bulge bulge = galsim.DeVaucouleurs(flux=bulge_frac, half_light_radius=bulge_hlr) bulge = bulge.shear(e1=bulge_e1, e2=bulge_e2) # The flux of an Add object is the sum of the component fluxes. # Note that in demo3.py, a similar addition was performed by the binary operator "+". gal = galsim.Add([disk, bulge]) # This flux may be overridden by withFlux. The relative fluxes of the components # remains the same, but the total flux is set to gal_flux. gal = gal.withFlux(gal_flux) gal = gal.shear(g1=gal_g1, g2=gal_g2) # The center of the object is normally placed at the center of the postage stamp image. # You can change that with shift: gal = gal.shift(dx=cat.getFloat(k, 11), dy=cat.getFloat(k, 12)) final = galsim.Convolve([psf, gal]) # Draw the profile image = galsim.ImageF(xsize, ysize) final.drawImage(image, scale=pixel_scale) # Add Poisson noise to the image: image.addNoise(galsim.PoissonNoise(rng, sky_level * pixel_scale**2)) logger.info('Drew image for object at row %d in the input catalog' % k) # Add the image to our list of images images.append(image) # Now write the images to a multi-extension fits file. Each image will be in its own HDU. galsim.fits.writeMulti(images, multi_file_name) logger.info('Images written to multi-extension fits file %r', multi_file_name)
def test_hsmparams_nodefault(): """Test that when non-default hsmparams are used, the results change.""" import time # First make some profile bulge = galsim.DeVaucouleurs(half_light_radius=0.3) disk = galsim.Exponential(half_light_radius=0.5) disk = disk.shear(e1=0.2, e2=-0.3) psf = galsim.Kolmogorov(fwhm=0.6) gal = bulge + disk # equal weighting, i.e., B/T=0.5 tot_gal = galsim.Convolve(gal, psf) tot_gal_image = tot_gal.drawImage(scale=0.18) tot_psf_image = psf.drawImage(scale=0.18) # Check that recompute_flux changes give results that are as expected test_t = time.time() res = galsim.hsm.EstimateShear(tot_gal_image, tot_psf_image) dt = time.time() - test_t res2 = galsim.hsm.EstimateShear(tot_gal_image, tot_psf_image, recompute_flux='sum') assert (res.moments_amp < res2.moments_amp), 'Incorrect behavior with recompute_flux=sum' res3 = galsim.hsm.EstimateShear(tot_gal_image, tot_psf_image, recompute_flux='none') assert ( res3.moments_amp == 0), 'Incorrect behavior with recompute_flux=none' # Check that results, timing change as expected with nsig_rg # For this, use Gaussian as galaxy and for ePSF, i.e., no extra pixel response p = galsim.Gaussian(fwhm=10.) g = galsim.Gaussian(fwhm=20.) g = g.shear(g1=0.5) obj = galsim.Convolve(g, p) # HSM allows a slop of 1.e-8 on nsig_rg, which means that default float32 images don't # actually end up with different result when using nsig_rg=0. rather than 3. im = obj.drawImage(scale=1., method='no_pixel', dtype=float) psf_im = p.drawImage(scale=1., method='no_pixel', dtype=float) test_t1 = time.time() g_res = galsim.hsm.EstimateShear(im, psf_im) test_t2 = time.time() g_res2 = galsim.hsm.EstimateShear( im, psf_im, hsmparams=galsim.hsm.HSMParams(nsig_rg=0.)) dt2 = time.time() - test_t2 dt1 = test_t2 - test_t1 if test_timing: assert ( dt2 > dt1 ), 'Should take longer to estimate shear without truncation of galaxy' assert (not equal_hsmshapedata( g_res, g_res2)), 'Results should differ with diff nsig_rg' assert g_res != g_res2, 'Results should differ with diff nsig_rg' # Check that results, timing change as expected with max_moment_nsig2 test_t2 = time.time() res2 = galsim.hsm.EstimateShear( tot_gal_image, tot_psf_image, hsmparams=galsim.hsm.HSMParams(max_moment_nsig2=9.)) dt2 = time.time() - test_t2 if test_timing: assert ( dt2 < dt ), 'Should be faster to estimate shear with lower max_moment_nsig2' assert (not equal_hsmshapedata( res, res2)), 'Outputs same despite change in max_moment_nsig2' assert res != res2, 'Outputs same despite change in max_moment_nsig2' assert (res.moments_sigma > res2.moments_sigma), 'Sizes do not change as expected' assert (res.moments_amp > res2.moments_amp), 'Amplitudes do not change as expected' # Check that max_amoment, max_ashift work as expected try: np.testing.assert_raises( RuntimeError, galsim.hsm.EstimateShear, tot_gal_image, tot_psf_image, hsmparams=galsim.hsm.HSMParams(max_amoment=10.)) np.testing.assert_raises( RuntimeError, galsim.hsm.EstimateShear, tot_gal_image, tot_psf_image, guess_centroid=galsim.PositionD(47., tot_gal_image.trueCenter().y), hsmparams=galsim.hsm.HSMParams(max_ashift=0.1)) except ImportError: print('The assert_raises tests require nose')
def test_hsmparams(): """Test the ability to set/change parameters that define how moments/shape estimation are done.""" # First make some profile, and make sure that we get the same answers when we specify default # hsmparams or don't specify hsmparams at all. default_hsmparams = galsim.hsm.HSMParams(nsig_rg=3.0, nsig_rg2=3.6, max_moment_nsig2=25.0, regauss_too_small=1, adapt_order=2, convergence_threshold=1.e-6, max_mom2_iter=400, num_iter_default=-1, bound_correct_wt=0.25, max_amoment=8000., max_ashift=15., ksb_moments_max=4, failed_moments=-1000.) bulge = galsim.DeVaucouleurs(half_light_radius=0.3) disk = galsim.Exponential(half_light_radius=0.5) disk = disk.shear(e1=0.2, e2=-0.3) psf = galsim.Kolmogorov(fwhm=0.6) gal = bulge + disk # equal weighting, i.e., B/T=0.5 tot_gal = galsim.Convolve(gal, psf) tot_gal_image = tot_gal.drawImage(scale=0.18) tot_psf_image = psf.drawImage(scale=0.18) res = tot_gal_image.FindAdaptiveMom() res_def = tot_gal_image.FindAdaptiveMom(hsmparams=default_hsmparams) assert (equal_hsmshapedata( res, res_def)), 'Moment outputs differ when using default HSMParams' assert res == res_def, 'Moment outputs differ when using default HSMParams' res2 = galsim.hsm.EstimateShear(tot_gal_image, tot_psf_image) res2_def = galsim.hsm.EstimateShear(tot_gal_image, tot_psf_image, hsmparams=default_hsmparams) assert (equal_hsmshapedata( res, res_def)), 'Shear outputs differ when using default HSMParams' assert res == res_def, 'Shear outputs differ when using default HSMParams' do_pickle(default_hsmparams) do_pickle( galsim.hsm.HSMParams(nsig_rg=1.0, nsig_rg2=1.6, max_moment_nsig2=2.0, regauss_too_small=0, adapt_order=0, convergence_threshold=1.e-8, max_mom2_iter=100, num_iter_default=4, bound_correct_wt=0.05, max_amoment=80., max_ashift=5., ksb_moments_max=2, failed_moments=99.)) do_pickle(res) do_pickle(res2) do_pickle(galsim._galsim.CppShapeData()) try: # Then check failure modes: force it to fail by changing HSMParams. new_params_niter = galsim.hsm.HSMParams( max_mom2_iter=res.moments_n_iter - 1) new_params_size = galsim.hsm.HSMParams(max_amoment=0.3 * res.moments_sigma**2) np.testing.assert_raises(RuntimeError, galsim.hsm.FindAdaptiveMom, tot_gal_image, hsmparams=new_params_niter) np.testing.assert_raises(RuntimeError, galsim.hsm.EstimateShear, tot_gal_image, tot_psf_image, hsmparams=new_params_size) except ImportError: print('The assert_raises tests require nose')
def main(argv): """ Make a fits image cube where each frame has two images of the same galaxy drawn with regular FFT convolution and with photon shooting. We do this for 5 different PSFs and 5 different galaxies, each with 4 different (random) fluxes, sizes, and shapes. """ logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("demo7") # To turn off logging: #logger.propagate = False # To turn on the debugging messages: #logger.setLevel(logging.DEBUG) # Define some parameters we'll use below. # Make output directory if not already present. if not os.path.isdir('output'): os.mkdir('output') file_name = os.path.join('output', 'cube_phot.fits.gz') random_seed = 553728 sky_level = 1.e4 # ADU / arcsec^2 pixel_scale = 0.28 # arcsec nx = 64 ny = 64 gal_flux_min = 1.e4 # Range for galaxy flux gal_flux_max = 1.e5 gal_hlr_min = 0.3 # arcsec gal_hlr_max = 1.3 # arcsec gal_e_min = 0. # Range for ellipticity gal_e_max = 0.8 psf_fwhm = 0.65 # arcsec # This script is set up as a comparison between using FFTs for doing the convolutions and # shooting photons. The two methods have trade-offs in speed and accuracy which vary # with the kind of profile being drawn and the S/N of the object, among other factors. # In addition, for each method, there are a number of parameters GalSim uses that control # aspects of the calculation that further affect the speed and accuracy. # # We encapsulate these parameters with an object called GSParams. The default values # are intended to be accurate enough for normal precision shear tests, without sacrificing # too much speed. # # Any PSF or galaxy object can be given a gsparams argument on construction that can # have different values to make the calculation more or less accurate (typically trading # off for speed or memory). # # In this script, we adjust some of the values slightly, just to show you how it works. # You could play around with these values and see what effect they have on the drawn images. # Usually, it requires a pretty drastic change in these parameters for you to be able to # notice the difference by eye. But subtle effects that may impact the shapes of galaxies # can happen well before then. # Type help(galsim.GSParams) for the complete list of parameters and more detailed # documentation, including the default values for each parameter. gsparams = galsim.GSParams( folding_threshold= 1.e-2, # maximum fractional flux that may be folded around edge of FFT maxk_threshold= 2.e-3, # k-values less than this may be excluded off edge of FFT xvalue_accuracy= 1.e-4, # approximations in real space aim to be this accurate kvalue_accuracy= 1.e-4, # approximations in fourier space aim to be this accurate shoot_accuracy= 1.e-4, # approximations in photon shooting aim to be this accurate minimum_fft_size=64) # minimum size of ffts logger.info('Starting demo script 7') # Make the PSF profiles: psf1 = galsim.Gaussian(fwhm=psf_fwhm, gsparams=gsparams) psf2 = galsim.Moffat(fwhm=psf_fwhm, beta=2.4, gsparams=gsparams) psf3_inner = galsim.Gaussian(fwhm=psf_fwhm, flux=0.8, gsparams=gsparams) psf3_outer = galsim.Gaussian(fwhm=2 * psf_fwhm, flux=0.2, gsparams=gsparams) psf3 = psf3_inner + psf3_outer atmos = galsim.Gaussian(fwhm=psf_fwhm, gsparams=gsparams) # The OpticalPSF and set of Zernike values chosen below correspond to a reasonably well aligned, # smallish ~0.3m / 12 inch diameter telescope with a central obscuration of ~0.12m or 5 inches # diameter, being used in optical wavebands. # In the Noll convention, the value of the Zernike coefficient also gives the RMS optical path # difference across a circular pupil. An RMS difference of ~0.5 or larger indicates that parts # of the wavefront are in fully destructive interference, and so we might expect aberrations to # become strong when Zernike aberrations summed in quadrature approach 0.5 wave. # The aberrations chosen in this case correspond to operating close to a 0.25 wave RMS optical # path difference. Unlike in demo3, we specify the aberrations by making a list that we pass # in using the 'aberrations' kwarg. The order of aberrations starting from index 4 is defocus, # astig1, astig2, coma1, coma2, trefoil1, trefoil2, spher as in the Noll convention. # We ignore the first 4 values so that the index number corresponds to the Zernike index # in the Noll convention. This will be particularly convenient once we start allowing # coefficients beyond spherical (index 11). c.f. The Wikipedia page about the Noll indices: # # http://en.wikipedia.org/wiki/Zernike_polynomials#Zernike_polynomials aberrations = [0.0] * 12 # Set the initial size. aberrations[4] = 0.06 # Noll index 4 = Defocus aberrations[5:7] = [0.12, -0.08] # Noll index 5,6 = Astigmatism aberrations[7:9] = [0.07, 0.04] # Noll index 7,8 = Coma aberrations[11] = -0.13 # Noll index 11 = Spherical # You could also define these all at once if that is more convenient: #aberrations = [0.0, 0.0, 0.0, 0.0, 0.06, 0.12, -0.08, 0.07, 0.04, 0.0, 0.0, -0.13] optics = galsim.OpticalPSF(lam_over_diam=0.6 * psf_fwhm, obscuration=0.4, aberrations=aberrations, gsparams=gsparams) psf4 = galsim.Convolve([atmos, optics ]) # Convolve inherits the gsparams from the first # item in the list. (Or you can supply a gsparams # argument explicitly if you want to override this.) atmos = galsim.Kolmogorov(fwhm=psf_fwhm, gsparams=gsparams) optics = galsim.Airy(lam_over_diam=0.3 * psf_fwhm, gsparams=gsparams) psf5 = galsim.Convolve([atmos, optics]) psfs = [psf1, psf2, psf3, psf4, psf5] psf_names = [ "Gaussian", "Moffat", "Double Gaussian", "OpticalPSF", "Kolmogorov * Airy" ] psf_times = [0, 0, 0, 0, 0] psf_fft_times = [0, 0, 0, 0, 0] psf_phot_times = [0, 0, 0, 0, 0] # Make the galaxy profiles: gal1 = galsim.Gaussian(half_light_radius=1, gsparams=gsparams) gal2 = galsim.Exponential(half_light_radius=1, gsparams=gsparams) gal3 = galsim.DeVaucouleurs(half_light_radius=1, gsparams=gsparams) gal4 = galsim.Sersic(half_light_radius=1, n=2.5, gsparams=gsparams) # A Sersic profile may be truncated if desired. # The units for this are expected to be arcsec (or specifically -- whatever units # you are using for all the size values as defined by the pixel_scale). bulge = galsim.Sersic(half_light_radius=0.7, n=3.2, trunc=8.5, gsparams=gsparams) disk = galsim.Sersic(half_light_radius=1.2, n=1.5, gsparams=gsparams) gal5 = 0.4 * bulge + 0.6 * disk # Net half-light radius is only approximate for this one. gals = [gal1, gal2, gal3, gal4, gal5] gal_names = [ "Gaussian", "Exponential", "Devaucouleurs", "n=2.5 Sersic", "Bulge + Disk" ] gal_times = [0, 0, 0, 0, 0] gal_fft_times = [0, 0, 0, 0, 0] gal_phot_times = [0, 0, 0, 0, 0] # Other times to keep track of: setup_times = 0 fft_times = 0 phot_times = 0 noise_times = 0 # Loop over combinations of psf, gal, and make 4 random choices for flux, size, shape. all_images = [] k = 0 for ipsf in range(len(psfs)): psf = psfs[ipsf] psf_name = psf_names[ipsf] logger.info('psf %d: %s', ipsf + 1, psf) # Note that this implicitly calls str(psf). We've made an effort to give all GalSim # objects an informative but relatively succinct str representation. Some details may # be missing, but it should look essentially like how you would create the object. logger.debug('repr = %r', psf) # The repr() version are a bit more pedantic in form and should be completely informative, # to the point where two objects that are not identical should never have equal repr # strings. As such the repr strings may in some cases be somewhat unwieldy. For instance, # since we set non-default gsparams in these, the repr includes that information, but # it is omitted from the str for brevity. for igal in range(len(gals)): gal = gals[igal] gal_name = gal_names[igal] logger.info(' galaxy %d: %s', igal + 1, gal) logger.debug(' repr = %r', gal) for i in range(4): logger.debug(' Start work on image %d', i) t1 = time.time() # Initialize the random number generator we will be using. rng = galsim.UniformDeviate(random_seed + k) # 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: flux = %.2e, hlr = %.2f, ellip = (%.2f,%.2f)', k, flux, hlr, gal_shape.getE1(), gal_shape.getE2()) logger.debug(' Times: %f, %f, %f, %f, %f', t2 - t1, t3 - t2, t4 - t3, t5 - t4, t6 - t5) psf_times[ipsf] += t6 - t1 psf_fft_times[ipsf] += t3 - t2 psf_phot_times[ipsf] += t5 - t4 gal_times[igal] += t6 - t1 gal_fft_times[igal] += t3 - t2 gal_phot_times[igal] += t5 - t4 setup_times += t2 - t1 fft_times += t3 - t2 phot_times += t5 - t4 noise_times += t4 - t3 + t6 - t5 logger.info('Done making images of galaxies') logger.info('') logger.info('Some timing statistics:') logger.info(' Total time for setup steps = %f', setup_times) logger.info(' Total time for regular fft drawing = %f', fft_times) logger.info(' Total time for photon shooting = %f', phot_times) logger.info(' Total time for adding noise = %f', noise_times) logger.info('') logger.info('Breakdown by PSF type:') for ipsf in range(len(psfs)): logger.info(' %s: Total time = %f (fft: %f, phot: %f)', psf_names[ipsf], psf_times[ipsf], psf_fft_times[ipsf], psf_phot_times[ipsf]) logger.info('') logger.info('Breakdown by Galaxy type:') for igal in range(len(gals)): logger.info(' %s: Total time = %f (fft: %f, phot: %f)', gal_names[igal], gal_times[igal], gal_fft_times[igal], gal_phot_times[igal]) logger.info('') # Now write the image to disk. # With any write command, you can optionally compress the file using several compression # schemes: # 'gzip' uses gzip on the full output file. # 'bzip2' uses bzip2 on the full output file. # 'rice' uses rice compression on the image, leaving the fits headers readable. # 'gzip_tile' uses gzip in tiles on the output image, leaving the fits headers readable. # 'hcompress' uses hcompress on the image, but it is only valid for 2-d data, so it # doesn't work for writeCube. # 'plio' uses plio on the image, but it is only valid for positive integer data. # Furthermore, the first three have standard filename extensions associated with them, # so if you don't specify a compression, but the filename ends with '.gz', '.bz2' or '.fz', # the corresponding compression will be selected automatically. # In other words, the `compression='gzip'` specification is actually optional here: galsim.fits.writeCube(all_images, file_name, compression='gzip') logger.info('Wrote fft image to fits data cube %r', file_name)
def galSimFakeSersic(flux, gal, psfImage=None, scaleRad=False, returnObj=True, expAll=False, devAll=False, plotFake=False, trunc=0, drawMethod="no_pixel", addPoisson=False, scale=1.0, transform=None, addShear=False): """ Make a fake single Sersic galaxy using the galSim.Sersic function Inputs: total flux of the galaxy, and a record array that stores the necessary parameters [reffPix, nSersic, axisRatio, posAng] Output: a 2-D image array of the galaxy model OR a GalSim object of the model Options: psfImage: PSF image for convolution trunc: Flux of Sersic models will truncate at trunc * reffPix radius; trunc=0 means no truncation drawMethod: The method for drawImage: ['auto', 'fft', 'real_space'] addPoisson: Add Poisson noise plotFake: Generate a PNG figure of the model expAll: Input model will be seen as nSersic=1 devAll: Input model will be seen as nSersic=4 returnObj: If TRUE, will return the GSObj """ reff = float(gal["reff"]) posAng = float(gal["theta"]) axisRatio = float(gal["b_a"]) nSersic = float(gal["sersic_n"]) # Truncate the flux at trunc x reff if trunc > 0: trunc = trunc * reff # Make sure Sersic index is not too large if nSersic > 6.0: raise ValueError("Sersic index is too large! Should be <= 6.0") # Check the axisRatio value if axisRatio <= 0.05: raise ValueError("Axis Ratio is too small! Should be >= 0.05") # Make the Sersic model based on flux, re, and Sersic index if nSersic == 1.0 or expAll: if scaleRad: serObj = galsim.Exponential(scale_radius=reff) else: serObj = galsim.Exponential(half_light_radius=reff) if expAll: print " * Treated as a n=1 Exponential disk : %d" % (gal["ID"]) elif nSersic == 4.0 or devAll: serObj = galsim.DeVaucouleurs(half_light_radius=reff, trunc=trunc) if devAll: print " * Treated as a n=4 De Vaucouleurs model: %d" % (gal["ID"]) elif nSersic <= 0.9: serObj = galsim.Sersic(nSersic, half_light_radius=reff) else: serObj = galsim.Sersic(nSersic, half_light_radius=reff, trunc=trunc) # If necessary, apply the Axis Ratio (q=b/a) using the Shear method if axisRatio < 1.0: serObj = serObj.shear(q=axisRatio, beta=(0.0 * galsim.degrees)) # If necessary, apply the Position Angle (theta) using the Rotate method # if posAng != 0.0 or posAng != 180.0: serObj = serObj.rotate((90.0 - posAng) * galsim.degrees) # If necessary, apply addtion shear (e.g. for weak lensing test) if addShear: try: g1 = float(gal['g1']) g2 = float(gal['g2']) serObj = serObj.shear(g1=g1, g2=g2) except ValueError: warnings.warn("Can not find g1 or g2 in the input!\n", " No shear has been added!") # Do the transformation from sky to pixel coordinates, if given if transform is not None: serObj = serObj.transform(*tuple(transform.ravel())) # Pass the flux to the object serObj = serObj.withFlux(float(flux)) # Convolve the Sersic model using the provided PSF image if psfImage is not None: # Convert the PSF Image Array into a GalSim Object # Norm=True by default psfObj = arrayToGSObj(psfImage, norm=True) serFinal = galsim.Convolve([serObj, psfObj]) else: serFinal = serObj # Make a PNG figure of the fake galaxy to check if everything is Ok if plotFake: plotFakeGalaxy(serFinal, galID=gal['ID']) # Now, by default, the function will just return the GSObj if returnObj: return serFinal else: return galSimDrawImage(serFinal, method=drawMethod, scale=scale, addPoisson=addPoisson)
def _get_models(self): """ get models for each band """ import galsim rng = self.rng o = self['objects'] shift = self._get_shift() fracdev = rng.uniform( low=o['fracdev_range'][0], high=o['fracdev_range'][1], ) disk_hlr = rng.uniform( low=o['hlr_range'][0], high=o['hlr_range'][1], ) bulge_sizefrac = rng.uniform( low=o['bulge_sizefrac_range'][0], high=o['bulge_sizefrac_range'][1], ) bulge_hlr = disk_hlr * bulge_sizefrac if o['flux_range'] == 'track_hlr': flux = disk_hlr**2 * 100 else: flux = rng.uniform( low=o['flux_range'][0], high=o['flux_range'][1], ) g1disk, g2disk = self.gpdf.sample2d() bulge_angle_offset = rng.uniform( low=o['bulge_angle_offet_range'][0], high=o['bulge_angle_offet_range'][1], ) bulge_angle_offset = np.deg2rad(bulge_angle_offset) disk_shape = ngmix.Shape(g1disk, g2disk) bulge_shape = disk_shape.get_rotated(bulge_angle_offset) disk = galsim.Exponential( half_light_radius=disk_hlr, flux=(1 - fracdev) * flux, ).shear( g1=disk_shape.g1, g2=disk_shape.g2, ).shift(*shift, ) bulge = galsim.DeVaucouleurs( half_light_radius=bulge_hlr, flux=fracdev * flux, ).shear( g1=bulge_shape.g1, g2=bulge_shape.g2, ).shift(*shift, ) disk_color = o['disk_color'] bulge_color = o['bulge_color'] gdisk = disk * disk_color[0] rdisk = disk * disk_color[1] idisk = disk * disk_color[2] gbulge = bulge * bulge_color[0] rbulge = bulge * bulge_color[1] ibulge = bulge * bulge_color[2] gmodel = galsim.Add(gdisk, gbulge) rmodel = galsim.Add(rdisk, rbulge) imodel = galsim.Add(idisk, ibulge) obj_data = self._get_obj_data_struct() obj_data['shift'] = shift obj_data['flux'] = flux obj_data['hlr'] = disk_hlr return (gmodel, rmodel, imodel), obj_data