def test_knots_transform(): """Test that overridden transformations give equivalent results as the normal methods. """ def test_op(rw, op): print(op) rw1 = eval('rw.' + op) rw2 = eval('super(galsim.RandomKnots,rw).' + op) # Need to convolve by a psf to get reasonable results for fft drawing. psf = galsim.Moffat(beta=1.5, fwhm=0.9) conv1 = galsim.Convolve(rw1, psf) conv2 = galsim.Convolve(rw2, psf) im1 = conv1.drawImage(nx=16, ny=16, scale=0.3) im2 = conv2.drawImage(nx=16, ny=16, scale=0.3) np.testing.assert_almost_equal(im1.array, im2.array, decimal=3, err_msg='RandomKnots with op ' + op) if __name__ == '__main__': npoints = 20 else: npoints = 3 # Not too many, so this test doesn't take forever. hlr = 1.7 flux = 1000 rng = galsim.BaseDeviate(1234) rw = galsim.RandomKnots(npoints, profile=galsim.Exponential(half_light_radius=hlr, flux=flux), rng=rng) if __name__ == '__main__': # First relatively trivial tests of no ops test_op(rw, 'withScaledFlux(1.0)') test_op(rw, 'expand(1.0)') test_op(rw, 'dilate(1.0)') test_op(rw, 'shear(g1=0, g2=0)') test_op(rw, 'rotate(0 * galsim.degrees)') test_op(rw, 'transform(1., 0., 0., 1.)') test_op(rw, 'shift(0., 0.)') test_op(rw, 'rotate(23 * galsim.degrees)' ) # no op, since original is isotropic # These are fundamental, since these are the methods we override. Always test these. test_op(rw, 'withFlux(23)') test_op(rw, 'withScaledFlux(23)') test_op(rw, 'expand(1.2)') test_op(rw, 'dilate(1.2)') test_op(rw, 'shear(g1=0.1, g2=-0.03)') test_op(rw, '_shear(galsim.Shear(0.03 + 1j*0.09))') test_op(rw.shear(g1=0.05, g2=0), 'rotate(23 * galsim.degrees)') test_op(rw, 'transform(1.2, 0.1, -0.2, 1.1)') test_op(rw, 'shift(0.3, 0.9)') test_op(rw, '_shift(galsim.PositionD(-0.3, 0.2))') if __name__ == '__main__': # A couple more that are currently not overridden, but call out to the above functions. test_op(rw, 'magnify(1.2)') test_op(rw, 'lens(0.03, 0.07, 1.12)')
def _generate_bdk( hlr, flux, vary=False, rng=None, gsrng=None, knots_hlr_frac=0.25, max_knots_disk_frac=0.1, # fraction of disk light max_bulge_shift_frac=0.1, # fraction of hlr max_bulge_rot=np.pi / 4, ): if vary: bulge_frac = _generate_bulge_frac(rng) else: bulge_frac = 0.5 all_disk_frac = (1.0 - bulge_frac) knots_hlr = knots_hlr_frac * hlr if vary: knots_sub_frac = _generate_knots_sub_frac(rng, max_knots_disk_frac) else: knots_sub_frac = max_knots_disk_frac disk_frac = (1 - knots_sub_frac) * all_disk_frac knots_frac = knots_sub_frac * all_disk_frac bulge = DeVaucouleurs(half_light_radius=hlr, flux=flux * bulge_frac) disk = Exponential(half_light_radius=hlr, flux=flux * disk_frac) if gsrng is None: # fixed galaxy, so fix the rng gsrng = galsim.BaseDeviate(123) knots = galsim.RandomKnots( npoints=10, half_light_radius=knots_hlr, flux=flux * knots_frac, rng=gsrng, ) if vary: bulge = _shift_bulge(rng, bulge, hlr, max_bulge_shift_frac) if vary: g1disk, g2disk = _generate_g1g2(rng) g1bulge, g2bulge = g1disk, g2disk if vary: g1bulge, g2bulge = _rotate_bulge(rng, max_bulge_rot, g1bulge, g2bulge) bulge = bulge.shear(g1=g1bulge, g2=g2bulge) disk = disk.shear(g1=g1disk, g2=g2disk) knots = knots.shear(g1=g1disk, g2=g2disk) return galsim.Add(bulge, disk, knots)
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 get_model(self): profile = galsim.Exponential( flux=self.flux, half_light_radius=self.hlr, ) obj = galsim.RandomKnots( self.n_knots, profile=profile, ) return obj
def test_knots_config(): """ test we get the same object using a configuration and the explicit constructor """ hlr = 2.0 flux = np.pi gal_config1 = { 'type': 'RandomKnots', 'npoints': 100, 'half_light_radius': hlr, 'flux': flux, } gal_config2 = { 'type': 'RandomKnots', 'npoints': 150, 'profile': { 'type': 'Exponential', 'half_light_radius': hlr, 'flux': flux, } } for gal_config in (gal_config1, gal_config2): config = { 'gal': gal_config, 'rng': galsim.BaseDeviate(31415), } rwc = galsim.config.BuildGSObject(config, 'gal')[0] print(repr(rwc._profile)) rw = galsim.RandomKnots( gal_config['npoints'], half_light_radius=hlr, flux=flux, ) assert rw.npoints==rwc.npoints,\ "expected npoints==%d, got %d" % (rw.npoints, rwc.npoints) assert rw.input_half_light_radius==rwc.input_half_light_radius,\ "expected hlr==%g, got %g" % (rw.input_half_light_radius, rw.input_half_light_radius) nobj = len(rw.points) nobjc = len(rwc.points) assert nobj == nobjc, "expected %d objects, got %d" % (nobj, nobjc) pts = rw.points ptsc = rwc.points assert (pts.shape == ptsc.shape),\ "expected %s shape for points, got %s" % (pts.shape,ptsc.shape)
def test_knots_repr(): """ test the repr and str work, and that a new object can be created using eval """ npoints=100 hlr = 8.0 flux=1 rw1=galsim.RandomKnots( npoints, half_light_radius=hlr, flux=flux, ) rw2=galsim.RandomKnots( npoints, profile=galsim.Exponential(half_light_radius=hlr, flux=flux), ) for rw in (rw1, rw2): # just make sure str() works, don't require eval to give # a consistent object back st=str(rw) # require eval(repr(rw)) to give a consistent object back new_rw = eval(repr(rw)) assert new_rw.npoints == rw.npoints,\ "expected npoints=%d got %d" % (rw.npoints,new_rw.npoints) mess="expected input_half_light_radius=%.16g got %.16g" assert new_rw.input_half_light_radius == rw.input_half_light_radius,\ mess % (rw.input_half_light_radius,new_rw.input_half_light_radius) assert new_rw.flux == rw.flux,\ "expected flux=%.16g got %.16g" % (rw.flux,new_rw.flux)
def test_knots_valid_inputs(): """ Create a random walk galaxy and test that the getters work for valid non-default inputs """ # try constructing with mostly defaults npoints = 100 hlr = 8.0 flux = 3.5 seed = 35 rng = galsim.UniformDeviate(seed) args = (npoints, ) kw1 = {'half_light_radius': hlr, 'flux': flux, 'rng': rng} prof = galsim.Exponential(half_light_radius=hlr, flux=flux) kw2 = {'profile': prof, 'rng': rng} # version of profile with a transformation prof = galsim.Exponential(half_light_radius=hlr, flux=flux) prof = prof.shear(g1=-0.05, g2=0.025) kw3 = {'profile': prof, 'rng': rng} for kw in (kw1, kw2, kw3): rw = galsim.RandomKnots(*args, **kw) assert rw.npoints == npoints, "expected npoints==%d, got %d" % ( npoints, rw.npoints) assert rw.flux==flux,\ "expected flux==%g, got %g" % (flux, rw.flux) if kw is not kw3: # only test if not a transformation object assert rw.input_half_light_radius==hlr,\ "expected hlr==%g, got %g" % (hlr, rw.input_half_light_radius) pts = rw.points nobj = len(pts) assert nobj == npoints == npoints, "expected %d objects, got %d" % ( npoints, nobj) pts = rw.points assert pts.shape == (npoints, 2), "expected (%d,2) shape for points, got %s" % ( npoints, pts.shape)
def test_knots_sed(): """Test RandomKnots with an SED This test is in response to isse #1064, a bug discovered by Troxel. """ sed = galsim.SED('CWW_E_ext.sed', 'A', 'flambda') knots = galsim.RandomKnots(10, half_light_radius=1.3, flux=100) gal1 = galsim.ChromaticObject(knots) * sed gal2 = knots * sed # This line used to fail. do_pickle(gal1) do_pickle(gal2) # They don't test as ==, since they are formed differently. But they are functionally equal: bandpass = galsim.Bandpass('LSST_r.dat', 'nm') psf = galsim.Gaussian(fwhm=0.7) final1 = galsim.Convolve(gal1, psf) final2 = galsim.Convolve(gal2, psf) im1 = final1.drawImage(bandpass, scale=0.4) im2 = final2.drawImage(bandpass, scale=0.4) np.testing.assert_array_equal(im1.array, im2.array)
def RandomWalk(*args, **kwargs): from . import depr depr('RandomWalk', 2.2, 'RandomKnots') return galsim.RandomKnots(*args, **kwargs)
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 getObj(self, index, gsparams=None, rng=None, bandpass=None, chromatic=False, exp_time=30): params = self.objinfo[index] magnorm = self.getMagNorm(index) if magnorm >= 50: # Mark of invalid object apparently return None if gsparams is not None: gsparams = galsim.GSParams(**gsparams) # Make the object according to the values in the objinfo # Note: params here starts at 12, so all indices are 12 less than in previous code. if params[0].lower() == 'point': obj = galsim.DeltaFunction(gsparams=gsparams) elif params[0].lower() == 'sersic2d': a = float(params[1]) b = float(params[2]) if b > a: # Invalid, but existing code just lets it pass. return None pa = float(params[3]) if self.flip_g2: # Previous code first did PA = 360 - params[3] # Then beta = 90 + PA beta = float(90 - pa) * galsim.degrees else: beta = float(90 + pa) * galsim.degrees n = float(params[4]) # GalSim can amortize some calculations for Sersics, but only if n is the same # as a previous galaxy. So quantize the n values at 0.05. There's no way anyone # cares about this at higher resolution than that. # For now, this is not actually helpful, since n is always either 1 or 4, but if # we ever start having more variable n, this will prevent it from redoing Hankel # integrals for every galaxy. n = round(n * 20.) / 20. hlr = (a * b)**0.5 # geometric mean of a and b is close to right. # XXX: Note: Previous code had hlr = a, which is wrong. (?) Galaxies were too large. # Especially when they were more elliptical. Oops. # TODO: Maybe not? Check if this should be a. obj = galsim.Sersic(n=n, half_light_radius=hlr, gsparams=gsparams) shear = galsim.Shear(q=b / a, beta=beta) obj = obj._shear(shear) g1, g2, mu = self.getLens(index) obj = obj._lens(g1, g2, mu) elif params[0].lower() == 'knots': a = float(params[1]) b = float(params[2]) if b > a: return None pa = float(params[3]) if self.flip_g2: beta = float(90 - pa) * galsim.degrees else: beta = float(90 + pa) * galsim.degrees npoints = int(params[4]) if npoints <= 0: # Again, weird, but previous code just lets this pass without comment. return None hlr = (a * b)**0.5 obj = galsim.RandomKnots(npoints=npoints, half_light_radius=hlr, rng=rng, gsparams=gsparams) shear = galsim.Shear(q=b / a, beta=beta) obj = obj._shear(shear) # TODO: These look bad in space images (cf. Troxel's talks about Roman sims.) # Should convolve this by a smallish Gaussian *here*: # I'd guess 0.3 arcsec is a good choice for the fwhm of this Gaussian. # obj = galsim.Convolve(obj, galsim.Gaussian(fwhm=0.3)) g1, g2, mu = self.getLens(index) obj = obj._lens(g1, g2, mu) elif (params[0].endswith('.fits') or params[0].endswith('.fits.gz')): fits_file = find_file_path(params[0], get_image_dirs()) pixel_scale = float(params[1]) theta = float(params[2]) obj = galsim.InterpolatedImage(fits_file, scale=pixel_scale, gsparams=gsparams) if theta != 0.: obj = obj.rotate(-theta * galsim.degrees) g1, g2, mu = self.getLens(index) obj = obj._lens(g1, g2, mu) else: raise RuntimeError("Do not know how to handle object type: %s" % params[0]) # The seds are normalized to correspond to magnorm=0. # The flux for the given magnorm is 10**(-0.4*magnorm) # The constant here, 0.9210340371976184 = 0.4 * log(10) flux = math.exp(-0.9210340371976184 * magnorm) # This gives the normalization in photons/cm^2/sec. # Multiply by area and exptime to get photons. fAt = flux * self._rubin_area * exp_time sed = self.getSED(index) if chromatic: return obj.withFlux(fAt) * sed else: flux = sed.calculateFlux(bandpass) * fAt return obj.withFlux(flux)
def test_knots_defaults(): """ Create a random walk galaxy and test that the getters work for default inputs """ # try constructing with mostly defaults npoints = 100 hlr = 8.0 rng = galsim.BaseDeviate(1234) rw = galsim.RandomKnots(npoints, half_light_radius=hlr, rng=rng) assert rw.npoints == npoints, "expected npoints==%d, got %d" % (npoints, rw.npoints) assert rw.input_half_light_radius==hlr,\ "expected hlr==%g, got %g" % (hlr, rw.input_half_light_radius) nobj = len(rw.points) assert nobj == npoints, "expected %d objects, got %d" % (npoints, nobj) pts = rw.points assert pts.shape == ( npoints, 2), "expected (%d,2) shape for points, got %s" % (npoints, pts.shape) np.testing.assert_almost_equal(rw.centroid.x, np.mean(pts[:, 0])) np.testing.assert_almost_equal(rw.centroid.y, np.mean(pts[:, 1])) gsp = galsim.GSParams(xvalue_accuracy=1.e-8, kvalue_accuracy=1.e-8) rng2 = galsim.BaseDeviate(1234) rw2 = galsim.RandomKnots(npoints, half_light_radius=hlr, rng=rng2, gsparams=gsp) assert rw2 != rw assert rw2 == rw.withGSParams(gsp) assert rw2 == rw.withGSParams(xvalue_accuracy=1.e-8, kvalue_accuracy=1.e-8) # Check that they produce identical images. psf = galsim.Gaussian(sigma=0.8) conv1 = galsim.Convolve(rw.withGSParams(gsp), psf) conv2 = galsim.Convolve(rw2, psf) im1 = conv1.drawImage() im2 = conv2.drawImage() assert im1 == im2 # Check that image is not sensitive to use of rng by other objects. rng3 = galsim.BaseDeviate(1234) rw3 = galsim.RandomKnots(npoints, half_light_radius=hlr, rng=rng3) rng3.discard(523) conv1 = galsim.Convolve(rw, psf) conv3 = galsim.Convolve(rw3, psf) im1 = conv1.drawImage() im3 = conv2.drawImage() assert im1 == im3 # Run some basic tests of correctness check_basic(conv1, "RandomKnots") im = galsim.ImageD(64, 64, scale=0.5) do_shoot(conv1, im, "RandomKnots") do_kvalue(conv1, im, "RandomKnots") do_pickle(rw) do_pickle(conv1) do_pickle(conv1, lambda x: x.drawImage(scale=1)) # Check negative flux rw3 = rw.withFlux(-2.3) assert rw3 == galsim.RandomKnots(npoints, half_light_radius=hlr, rng=galsim.BaseDeviate(1234), flux=-2.3) conv = galsim.Convolve(rw3, psf) check_basic(conv, "RandomKnots with negative flux")
def test_knots_invalid_inputs(): """ Create a random walk galaxy and test that the the correct exceptions are raised for invalid inputs """ npoints = 100 hlr = 8.0 flux = 1.0 # try sending wrong type for npoints with assert_raises(GalSimValueError): galsim.RandomKnots('blah', half_light_radius=1, flux=3) # try sending neither profile or hlr with assert_raises(GalSimIncompatibleValuesError): galsim.RandomKnots(npoints) # try with rng wrong type with assert_raises(TypeError): galsim.RandomKnots(npoints, half_light_radius=hlr, rng=37) # wrong type for profile with assert_raises(GalSimIncompatibleValuesError): galsim.RandomKnots(npoints, profile=3.5) # wrong type for npoints npoints_bad = [35] with assert_raises(TypeError): galsim.RandomKnots(npoints_bad, half_light_radius=hlr) # wrong type for hlr with assert_raises(GalSimRangeError): galsim.RandomKnots(npoints, half_light_radius=-1.5) # wrong type for flux with assert_raises(TypeError): galsim.RandomKnots(npoints, flux=[3.5], half_light_radius=hlr) # sending flux with a profile prof = galsim.Exponential(half_light_radius=hlr, flux=2.0) with assert_raises(GalSimIncompatibleValuesError): galsim.RandomKnots(npoints, flux=flux, profile=prof) # sending hlr with a profile with assert_raises(GalSimIncompatibleValuesError): galsim.RandomKnots(npoints, half_light_radius=3, profile=prof) # bad value for npoints npoints_bad = -35 with assert_raises(GalSimRangeError): galsim.RandomKnots(npoints_bad, half_light_radius=hlr) # bad value for hlr with assert_raises(GalSimRangeError): galsim.RandomKnots(npoints, half_light_radius=-1.5)
def gen_mock_lsbg(self, galaxy, zp=HSC_zeropoint, pixel_scale=HSC_pixel_scale, verbose=True): ''' Generate mock low surface brightness galaxies. ''' import galsim from galsim import Angle, Image, InterpolatedImage, degrees from galsim.fitswcs import AstropyWCS from galsim.interpolant import Lanczos big_fft_params = galsim.GSParams(maximum_fft_size=20000) if not isinstance(galaxy['comp'], list): galaxy['comp'] = list(galaxy['comp']) if len(galaxy['comp']) == 1: galaxy['flux_fraction'] = [1.0] # print some information if verbose: print('# Generating mock galaxy.') print(' - Total components: ', len(galaxy['comp'])) print(' - Types: ', [c['model'].__name__ for c in galaxy['comp']]) print(' - Flux fraction: ', galaxy['flux_fraction']) # Empty canvas field = np.empty_like(self.bkg.images[0]) model_images = np.empty_like(self.bkg.images) # Calculate RA, DEC of the mock galaxy y_cen = self.bkg.images.shape[2] / 2 x_cen = self.bkg.images.shape[1] / 2 galaxy['ra'], galaxy['dec'] = self.bkg.wcs.wcs_pix2world( x_cen, y_cen, 0) # Calculate flux based on i-band mag and SED i_band_loc = np.argwhere(np.array(list(self.channels)) == 'i')[0][ 0] # location of i-band in `channels` seds = np.array([c['sed'] for c in galaxy['comp']]) # Normalize SED w.r.t i-band seds /= seds[:, i_band_loc][:, np.newaxis] tot_sed = np.sum(seds * np.array(galaxy['flux_fraction'])[:, np.newaxis], axis=0) for i, band in enumerate(self.channels): galaxy[f'{band}mag'] = -2.5 * np.log10(tot_sed[i]) + galaxy['imag'] if verbose: print(f' - Magnitude in {self.channels}: ', [round(galaxy[f'{band}mag'], 1) for band in self.channels]) #### Star generating mock galaxy in each band #### for i, band in enumerate(self.channels): # griz # Random number seed # This random number seed should be fixed across bands!!! rng = galsim.BaseDeviate(23333) # Sky background level sky_SB = 29 # mag/arcsec^2 sky_level = 10**((zp - sky_SB) / 2.5) # counts / arcsec^2 # Define the PSF interp_psf = InterpolatedImage(Image(self.bkg.psfs[i] / self.bkg.psfs[i].sum(), dtype=float), scale=pixel_scale, x_interpolant=Lanczos(3)) # Total flux for all components tot_flux = 10**((zp - galaxy[f'{band}mag']) / 2.5) gal_list = [] for k, comp in enumerate(galaxy['comp']): # Define the galaxy gal = comp['model'](**comp['model_params'], gsparams=big_fft_params) gal_shape = galsim.Shear( **comp['shear_params']) # Shear the galaxy gal = gal.shear(gal_shape) if 'shift' in comp.keys(): # Shift the center gal = gal.shift(comp['shift']) # Add star forming knots if 'n_knots' in comp.keys() and comp['n_knots'] > 0: if not 'knots_frac' in comp.keys(): raise KeyError( '`knots_frac` must be provided to generate star forming knots!' ) else: if 'knots_sed' in comp.keys(): knot_frac = comp['knots_frac'] * \ (comp['knots_sed'] / np.sum(comp['knots_sed']))[i] else: knot_frac = comp['knots_frac'] * 0.25 # flat SED knots = galsim.RandomKnots( comp['n_knots'], half_light_radius=comp['model_params'] ['half_light_radius'], flux=knot_frac, rng=rng) gal = galsim.Add([gal, knots]) gal = gal.withFlux(tot_flux * galaxy['flux_fraction'][k]) # Get Flux gal_list.append(gal) # Adding all components together gal = galsim.Add(gal_list) # Convolve galaxy with PSF final = galsim.Convolve([gal, interp_psf]) # Draw the image with a particular pixel scale. gal_image = final.drawImage(scale=pixel_scale, nx=field.shape[1], ny=field.shape[0]) # Add noise sky_sigma = hsc_sky[f'{band}'] / 3.631 * \ 10**((zp - 22.5) / 2.5) * pixel_scale**2 noise = galsim.GaussianNoise(rng, sigma=sky_sigma) # gal_image.addNoise(noise) # Generate mock image model_img = gal_image.array model_images[i] = model_img # Generate variance map mock_model = Data(images=model_images, variances=None, masks=None, channels=self.channels, wcs=None, weights=None, psfs=self.bkg.psfs, info=galaxy) # Finished!!! self.model = mock_model # model only has `images`, `channels`, `psfs`, and `info`! self.set_mock() # mock has other things, including modified variances.
def make_basic_sim(outDir, gname, Id0, ny=100, nx=100, do_write=True, return_array=False): """ Make basic galaxy image simulation (isolated) Parameters: outDir (str): output directory gname (str): shear distortion setup Id0 (int): index of the simulation ny (int): number of galaxies in y direction nx (int): number of galaxies in x direction do_write (bool): whether write output [default: True] return_array (bool): whether return galaxy array [default: False] """ ngrid = 64 scale = 0.168 # Get the shear information gList = np.array([-0.02, 0., 0.02]) gList = gList[[eval(i) for i in gname.split('-')[-1]]] if gname.split('-')[0] == 'g1': g1 = gList[0] g2 = 0. elif gname.split('-')[0] == 'g2': g1 = 0. g2 = gList[0] else: raise ValueError('cannot decide g1 or g2') logging.info( 'Processing for %s, and shears for four redshift bins are %s.' % (gname, gList)) # PSF psfFWHM = eval(outDir.split('_psf')[-1]) / 100. logging.info('The FWHM for PSF is: %s arcsec' % psfFWHM) psfInt = galsim.Moffat(beta=3.5, fwhm=psfFWHM, trunc=psfFWHM * 4.) psfInt = psfInt.shear(e1=0.02, e2=-0.02) #psfImg = psfInt.drawImage(nx=45,ny=45,scale=scale) gal_image = galsim.ImageF(nx * ngrid, ny * ngrid, scale=scale) gal_image.setOrigin(0, 0) outFname = os.path.join(outDir, 'image-%d-%s.fits' % (Id0, gname)) if os.path.exists(outFname): logging.info('Already have the outcome.') if do_write: logging.info('Nothing to write.') if return_array: return fitsio.read(outFname) else: return None ud = galsim.UniformDeviate(Id0 + 212) bigfft = galsim.GSParams(maximum_fft_size=10240) if 'basic' in outDir: rotArray = make_ringrot_radians(7) # 2**7*8=1024 galaxy ID # 2**7 different rotations and dilations # for each galaxy ID 10000 parametric galaxies logging.info('We have %d rotation realizations' % len(rotArray)) irot = Id0 // 8 if irot >= len(rotArray): logging.info('galaxy image index greater than %d' % len(rotArray * 8)) ang = rotArray[irot] * galsim.radians rescale = 1. + (ud() - 0.5) * 0.1 logging.info('%s' % rescale) # cosmos group ID =0...7 # we use 80000 galsim galaxies repeatedly cgid = int(Id0 % 8) logging.info('Making Basic Simulation. ID: %d, cosmos GID: %d.' % (Id0, cgid)) logging.info('The rotating angle is %.2f radians.' % rotArray[irot]) # Galsim galaxies directory = os.path.join(os.environ['homeWrk'],\ 'COSMOS/galsim_train/COSMOS_25.2_training_sample/') assert os.path.isdir(directory), 'cannot find galsim galaxies' catName = 'real_galaxy_catalog_25.2.fits' cosmos_cat = galsim.COSMOSCatalog(catName, dir=directory) # Basic parameters flux_scaling = 2.587 # catalog cosmo252 = cosmoHSTGal('252') cosmo252.readHSTsample() # cgid=0...7 hscCat = cosmo252.catused[cgid * nx * ny:(cgid + 1) * nx * ny] for i, ss in enumerate(hscCat): ix = i % nx iy = i // nx b = galsim.BoundsI(ix * ngrid, (ix + 1) * ngrid - 1, iy * ngrid, (iy + 1) * ngrid - 1) # each galaxy gal = cosmos_cat.makeGalaxy(gal_type='parametric',\ index=ss['index'],gsparams=bigfft) # rescale the radius by 'rescale' and keep surface brightness the same gal = gal.expand(rescale) # rotate by 'ang' gal = gal.rotate(ang) # accounting for zeropoint difference between COSMOS HST and HSC gal = gal * flux_scaling # shear distortion gal = gal.shear(g1=g1, g2=g2) if 'Shift' in outDir: # Galaxies is randomly shifted # This shift ensure that the offset to (ngrid//2,ngrid//2) is an isotropic circle dx = (ud() + 0.5) * scale dy = (ud() + 0.5) * scale if i == 0: logging.info('%.2f,%.2f' % (dx, dy)) gal = gal.shift(dx, dy) elif 'Center' in outDir: # Galaxies is located at (ngrid//2,ngrid//2) dx = 0.5 * scale dy = 0.5 * scale gal = gal.shift(dx, dy) else: #Galaxies is located at (ngrid//2-0.5,ngrid//2-0.5)) pass gal = galsim.Convolve([psfInt, gal], gsparams=bigfft) # draw galaxy sub_img = gal_image[b] gal.drawImage(sub_img, add_to_image=True) del gal, b, sub_img del hscCat, cosmos_cat, cosmo252, psfInt gc.collect() elif 'small' in outDir: # use galaxies with random knots # we only support three versions of small galaxies with different radius irr = eval(outDir.split('_psf')[0].split('small')[-1]) if irr == 0: radius = 0.07 elif irr == 1: radius = 0.15 elif irr == 2: radius = 0.20 else: raise ValueError('Something wrong with the outDir! we only support' 'three versions of small galaxies') logging.info('Making Small Simulation with Random Knots.') logging.info('Radius: %s, ID: %s.' % (radius, Id0)) npoints = 20 gal0 = galsim.RandomKnots(half_light_radius=radius,\ npoints=npoints,flux=10.,rng=ud) for ix in range(100): for iy in range(100): igal = ix * 100 + iy b = galsim.BoundsI(ix*ngrid,(ix+1)*ngrid-1,\ iy*ngrid,(iy+1)*ngrid-1) if igal % 4 == 0 and igal != 0: gal0= galsim.RandomKnots(half_light_radius=radius,\ npoints=npoints,flux=10.,rng=ud,gsparams=bigfft) sub_img = gal_image[b] ang = igal % 4 * np.pi / 4. * galsim.radians gal = gal0.rotate(ang) # Shear the galaxy gal = gal.shear(g1=g1, g2=g2) gal = galsim.Convolve([psfInt, gal], gsparams=bigfft) # Draw the galaxy image gal.drawImage(sub_img, add_to_image=True) del gal, b, sub_img gc.collect() gc.collect() else: raise ValueError("outDir should cotain 'basic' or 'small'!!") del ud if do_write: gal_image.write(outFname, clobber=True) if return_array: return gal_image.array