def compare_image_integrators(): import galsim.integ import time pixel_scale = 0.2 stamp_size = 128 gal = galsim.Chromatic(galsim.Gaussian(half_light_radius=0.5), disk_SED) PSF_500 = galsim.Gaussian(half_light_radius=PSF_hlr) PSF = galsim.ChromaticAtmosphere(PSF_500, 500.0, zenith_angle) final = galsim.Convolve([gal, PSF]) image = galsim.ImageD(stamp_size, stamp_size, scale=pixel_scale) # truth flux x = np.union1d(disk_SED.wave_list, bandpass.wave_list) x = x[(x <= bandpass.red_limit) & (x >= bandpass.blue_limit)] target = np.trapz(disk_SED(x) * bandpass(x), x) print('target') print(' {:14.11f}'.format(target)) t1 = time.time() print('midpoint') for N in [10, 30, 100, 300, 1000, 3000]: image = final.drawImage(bandpass, image=image, integrator=galsim.integ.ContinuousIntegrator( rule=galsim.integ.midpt, N=N)) mom = silentgetmoments(image) outstring = ' {:4d} {:14.11f} {:14.11f} {:14.11f} {:14.11f} {:14.11f} {:14.11f} {:14.11f}' print( outstring.format(N, image.array.sum(), image.array.sum() - target, *mom)) t2 = time.time() print('time for midpoint = %.2f' % (t2 - t1)) print('trapezoidal') for N in [10, 30, 100, 300, 1000, 3000]: image = final.drawImage(bandpass, image=image, integrator=galsim.integ.ContinuousIntegrator( rule=np.trapz, N=N)) mom = silentgetmoments(image) outstring = ' {:4d} {:14.11f} {:14.11f} {:14.11f} {:14.11f} {:14.11f} {:14.11f} {:14.11f}' print( outstring.format(N, image.array.sum(), image.array.sum() - target, *mom)) t3 = time.time() print('time for trapezoidal = %.2f' % (t3 - t2))
def test_crg_roundtrip_larger_target_psf(): """Test that drawing a chromatic galaxy with a color gradient directly using an LSST-size PSF is equivalent to first drawing the galaxy to HST-like images, and then using ChromaticRealGalaxy to produce an LSST-like image. """ # load some spectra bulge_SED = (galsim.SED( os.path.join(sedpath, 'CWW_E_ext.sed'), wave_type='ang', flux_type='flambda').thin(rel_err=1e-3).withFluxDensity( target_flux_density=0.3, wavelength=500.0)) disk_SED = (galsim.SED( os.path.join(sedpath, 'CWW_Sbc_ext.sed'), wave_type='ang', flux_type='flambda').thin(rel_err=1e-3).withFluxDensity( target_flux_density=0.3, wavelength=500.0)) bulge = galsim.Sersic(n=4, half_light_radius=0.6) * bulge_SED disk = galsim.Sersic(n=1, half_light_radius=0.4) * disk_SED # Decenter components a bit to make the test more complicated disk = disk.shift(0.05, 0.1) gal = (bulge + disk).shear(g1=0.3, g2=0.1) # Much faster to just use some achromatic HST-like PSFs. We'll make them slightly different in # each band though. f606w_PSF = galsim.ChromaticObject(galsim.Gaussian(half_light_radius=0.05)) f814w_PSF = galsim.ChromaticObject(galsim.Gaussian(half_light_radius=0.07)) LSSTPSF = galsim.ChromaticAtmosphere(galsim.Kolmogorov(fwhm=0.7), 600.0, zenith_angle=0.0 * galsim.degrees) f606w = galsim.Bandpass(os.path.join(bppath, "ACS_wfc_F606W.dat"), 'nm').truncate() f814w = galsim.Bandpass(os.path.join(bppath, "ACS_wfc_F814W.dat"), 'nm') LSST_i = galsim.Bandpass(os.path.join(bppath, "LSST_r.dat"), 'nm') truth_image = galsim.Convolve(LSSTPSF, gal).drawImage(LSST_i, nx=24, ny=24, scale=0.2) f606w_image = galsim.Convolve(f606w_PSF, gal).drawImage(f606w, nx=192, ny=192, scale=0.03) f814w_image = galsim.Convolve(f814w_PSF, gal).drawImage(f814w, nx=192, ny=192, scale=0.03) crg = galsim.ChromaticRealGalaxy.makeFromImages( images=[f606w_image, f814w_image], bands=[f606w, f814w], PSFs=[f606w_PSF, f814w_PSF], xis=[galsim.UncorrelatedNoise(1e-16)] * 2, SEDs=[bulge_SED, disk_SED]) test_image = galsim.Convolve(crg, LSSTPSF).drawImage(LSST_i, nx=24, ny=24, scale=0.2) truth_mom = galsim.hsm.FindAdaptiveMom(truth_image) test_mom = galsim.hsm.FindAdaptiveMom(test_image) np.testing.assert_allclose(test_mom.moments_amp, truth_mom.moments_amp, rtol=1e-3, atol=0) np.testing.assert_allclose(test_mom.moments_centroid.x, truth_mom.moments_centroid.x, rtol=0., atol=1e-2) np.testing.assert_allclose(test_mom.moments_centroid.y, truth_mom.moments_centroid.y, rtol=0., atol=1e-2) np.testing.assert_allclose(test_mom.moments_sigma, truth_mom.moments_sigma, rtol=1e-3, atol=0) np.testing.assert_allclose(test_mom.observed_shape.g1, truth_mom.observed_shape.g1, rtol=0, atol=1e-4) np.testing.assert_allclose(test_mom.observed_shape.g2, truth_mom.observed_shape.g2, rtol=0, atol=1e-4) # Invalid arguments with assert_raises(ValueError): crg = galsim.ChromaticRealGalaxy.makeFromImages( images=[f606w_image], bands=[f606w, f814w], PSFs=[f606w_PSF, f814w_PSF], xis=[galsim.UncorrelatedNoise(1e-16)] * 2, SEDs=[bulge_SED, disk_SED]) with assert_raises(ValueError): crg = galsim.ChromaticRealGalaxy.makeFromImages( images=[f606w_image, f814w_image], bands=[f606w], PSFs=[f606w_PSF, f814w_PSF], xis=[galsim.UncorrelatedNoise(1e-16)] * 2, SEDs=[bulge_SED, disk_SED]) with assert_raises(ValueError): crg = galsim.ChromaticRealGalaxy.makeFromImages( images=[f606w_image, f814w_image], bands=[f606w, f814w], PSFs=[f606w_PSF], xis=[galsim.UncorrelatedNoise(1e-16)] * 2, SEDs=[bulge_SED, disk_SED]) with assert_raises(ValueError): crg = galsim.ChromaticRealGalaxy.makeFromImages( images=[f606w_image, f814w_image], bands=[f606w, f814w], PSFs=[f606w_PSF, f814w_PSF], xis=[galsim.UncorrelatedNoise(1e-16)], SEDs=[bulge_SED, disk_SED]) with assert_raises(ValueError): crg = galsim.ChromaticRealGalaxy.makeFromImages( images=[f606w_image, f814w_image], bands=[f606w, f814w], PSFs=[f606w_PSF, f814w_PSF], xis=[galsim.UncorrelatedNoise(1e-16)] * 2, SEDs=[bulge_SED, disk_SED, disk_SED])
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 &' )
sed1 = galsim.SED(spec="wave",wave_type='nm',flux_type='fphotons') sed1 = sed1.withFlux(gal1_flux_thru_r,filters['r']) sed2 = galsim.SED(spec="300-wave",wave_type = 'nm',flux_type = 'fphotons') sed2 = sed1.withFlux(gal2_flux_thru_r,filters['r']) gal1 = galsim.Gaussian(flux=1.,sigma=gal1_sigma) psf1 = galsim.Moffat(beta=2.5,fwhm=0.5) ra = galsim.HMS_Angle("14:03:13") dec = galsim.DMS_Angle("54:20:57") m101 = galsim.CelestialCoord(ra,dec) latitude = 19.8207*galsim.degrees HA=-1.0*galsim.hours za,pa = galsim.dcr.zenith_parallactic_angles(m101,HA=HA,latitude=latitude) chrom_psf1 = galsim.ChromaticAtmosphere(psf1,500.,obj_coord=m101,latitude=latitude,HA=HA) gal2 = galsim.Gaussian(flux=1.,sigma=gal2_sigma) psf2 = galsim.Gaussian(flux=1.,sigma = psf2_sigma) ra = galsim.HMS_Angle("14:13:13") dec = galsim.DMS_Angle("54:50:57") rand_thing_next_to_m101 = galsim.CelestialCoord(ra,dec) latitude_seoul = 37.5665*galsim.degrees #seoul za,pa = galsim.dcr.zenith_parallactic_angles(rand_thing_next_to_m101,HA=HA,latitude=latitude) chrom_psf2 = galsim.ChromaticAtmosphere(psf2,500.,obj_coord=rand_thing_next_to_m101,latitude=latitude_seoul,HA=HA) chrom_gal1 = gal1*sed1 chrom_gal2 = gal2*sed2 chrom_total1 = galsim.Convolve([chrom_gal1,chrom_psf1])
def test_dcr(): """Test the dcr surface op """ # This tests that implementing DCR with the surface op is equivalent to using # ChromaticAtmosphere. # We use fairly extreme choices for the parameters to make the comparison easier, so # we can still get good discrimination of any errors with only 10^6 photons. zenith_angle = 45 * galsim.degrees # Larger angle has larger DCR. parallactic_angle = 129 * galsim.degrees # Something random, not near 0 or 180 pixel_scale = 0.03 # Small pixel scale means shifts are many pixels, rather than a fraction. alpha = -1.2 # The normal alpha is -0.2, so this is exaggerates the effect. bandpass = galsim.Bandpass('LSST_r.dat', 'nm') base_wavelength = bandpass.effective_wavelength base_wavelength += 500 # This exaggerates the effects fairly substantially. sed = galsim.SED('CWW_E_ext.sed', wave_type='ang', flux_type='flambda') flux = 1.e6 base_PSF = galsim.Kolmogorov(fwhm=0.3) # Use ChromaticAtmosphere im1 = galsim.ImageD(50, 50, scale=pixel_scale) star = galsim.DeltaFunction() * sed star = star.withFlux(flux, bandpass=bandpass) chrom_PSF = galsim.ChromaticAtmosphere(base_PSF, base_wavelength=base_wavelength, zenith_angle=zenith_angle, parallactic_angle=parallactic_angle, alpha=alpha) chrom = galsim.Convolve(star, chrom_PSF) chrom.drawImage(bandpass, image=im1) # Use PhotonDCR im2 = galsim.ImageD(50, 50, scale=pixel_scale) dcr = galsim.PhotonDCR(base_wavelength=base_wavelength, zenith_angle=zenith_angle, parallactic_angle=parallactic_angle, alpha=alpha) achrom = base_PSF.withFlux(flux) rng = galsim.BaseDeviate(31415) wave_sampler = galsim.WavelengthSampler(sed, bandpass, rng) surface_ops = [wave_sampler, dcr] achrom.drawImage(image=im2, method='phot', rng=rng, surface_ops=surface_ops) im1 /= flux # Divide by flux, so comparison is on a relative basis. im2 /= flux printval(im2, im1, show=False) np.testing.assert_almost_equal( im2.array, im1.array, decimal=4, err_msg="PhotonDCR didn't match ChromaticAtmosphere") # Repeat with thinned bandpass and SED to check that thin still works well. im3 = galsim.ImageD(50, 50, scale=pixel_scale) thin = 0.1 # Even higher also works. But this is probably enough. thin_bandpass = bandpass.thin(thin) thin_sed = sed.thin(thin) print('len bp = %d => %d' % (len(bandpass.wave_list), len(thin_bandpass.wave_list))) print('len sed = %d => %d' % (len(sed.wave_list), len(thin_sed.wave_list))) wave_sampler = galsim.WavelengthSampler(thin_sed, thin_bandpass, rng) achrom.drawImage(image=im3, method='phot', rng=rng, surface_ops=surface_ops) im3 /= flux printval(im3, im1, show=False) np.testing.assert_almost_equal( im3.array, im1.array, decimal=4, err_msg="thinning factor %f led to 1.e-4 level inaccuracy" % thin) # Check scale_unit im4 = galsim.ImageD(50, 50, scale=pixel_scale / 60) dcr = galsim.PhotonDCR(base_wavelength=base_wavelength, zenith_angle=zenith_angle, parallactic_angle=parallactic_angle, scale_unit='arcmin', alpha=alpha) surface_ops = [wave_sampler, dcr] achrom.dilate(1. / 60).drawImage(image=im4, method='phot', rng=rng, surface_ops=surface_ops) im4 /= flux printval(im4, im1, show=False) np.testing.assert_almost_equal( im4.array, im1.array, decimal=4, err_msg="PhotonDCR with scale_unit=arcmin, didn't match") # Check some other valid options # alpha = 0 means don't do any size scaling. # obj_coord, HA and latitude are another option for setting the angles # pressure, temp, and water pressure are settable. # Also use a non-trivial WCS. wcs = galsim.FitsWCS('des_data/DECam_00154912_12_header.fits') image = galsim.Image(50, 50, wcs=wcs) bandpass = galsim.Bandpass('LSST_r.dat', wave_type='nm').thin(0.1) base_wavelength = bandpass.effective_wavelength lsst_lat = galsim.Angle.from_dms('-30:14:23.76') lsst_long = galsim.Angle.from_dms('-70:44:34.67') local_sidereal_time = 3.14 * galsim.hours # Not pi. This is the time for this observation. im5 = galsim.ImageD(50, 50, wcs=wcs) obj_coord = wcs.toWorld(im5.true_center) base_PSF = galsim.Kolmogorov(fwhm=0.9) achrom = base_PSF.withFlux(flux) dcr = galsim.PhotonDCR( base_wavelength=bandpass.effective_wavelength, obj_coord=obj_coord, HA=local_sidereal_time - obj_coord.ra, latitude=lsst_lat, pressure=72, # default is 69.328 temperature=290, # default is 293.15 H2O_pressure=0.9) # default is 1.067 #alpha=0) # default is 0, so don't need to set it. surface_ops = [wave_sampler, dcr] achrom.drawImage(image=im5, method='phot', rng=rng, surface_ops=surface_ops) im6 = galsim.ImageD(50, 50, wcs=wcs) star = galsim.DeltaFunction() * sed star = star.withFlux(flux, bandpass=bandpass) chrom_PSF = galsim.ChromaticAtmosphere( base_PSF, base_wavelength=bandpass.effective_wavelength, obj_coord=obj_coord, HA=local_sidereal_time - obj_coord.ra, latitude=lsst_lat, pressure=72, temperature=290, H2O_pressure=0.9, alpha=0) chrom = galsim.Convolve(star, chrom_PSF) chrom.drawImage(bandpass, image=im6) im5 /= flux # Divide by flux, so comparison is on a relative basis. im6 /= flux printval(im5, im6, show=False) np.testing.assert_almost_equal( im5.array, im6.array, decimal=3, err_msg="PhotonDCR with alpha=0 didn't match") # Also check invalid parameters zenith_coord = galsim.CelestialCoord(13.54 * galsim.hours, lsst_lat) assert_raises( TypeError, galsim.PhotonDCR, zenith_angle=zenith_angle, parallactic_angle=parallactic_angle) # base_wavelength is required assert_raises(TypeError, galsim.PhotonDCR, base_wavelength=500, parallactic_angle=parallactic_angle ) # zenith_angle (somehow) is required assert_raises( TypeError, galsim.PhotonDCR, 500, zenith_angle=34.4, parallactic_angle=parallactic_angle) # zenith_angle must be Angle assert_raises(TypeError, galsim.PhotonDCR, 500, zenith_angle=zenith_angle, parallactic_angle=34.5) # parallactic_angle must be Angle assert_raises(TypeError, galsim.PhotonDCR, 500, obj_coord=obj_coord, latitude=lsst_lat) # Missing HA assert_raises(TypeError, galsim.PhotonDCR, 500, obj_coord=obj_coord, HA=local_sidereal_time - obj_coord.ra) # Missing latitude assert_raises(TypeError, galsim.PhotonDCR, 500, obj_coord=obj_coord) # Need either zenith_coord, or (HA,lat) assert_raises(TypeError, galsim.PhotonDCR, 500, obj_coord=obj_coord, zenith_coord=zenith_coord, HA=local_sidereal_time - obj_coord.ra) # Can't have both HA and zenith_coord assert_raises(TypeError, galsim.PhotonDCR, 500, obj_coord=obj_coord, zenith_coord=zenith_coord, latitude=lsst_lat) # Can't have both lat and zenith_coord assert_raises(TypeError, galsim.PhotonDCR, 500, zenith_angle=zenith_angle, parallactic_angle=parallactic_angle, H20_pressure=1.) # invalid (misspelled) assert_raises(ValueError, galsim.PhotonDCR, 500, zenith_angle=zenith_angle, parallactic_angle=parallactic_angle, scale_unit='inches') # invalid scale_unit # Invalid to use dcr without some way of setting wavelengths. assert_raises(galsim.GalSimError, achrom.drawImage, im2, method='phot', surface_ops=[dcr])
def test_CRG(args): """Predict an LSST or Euclid image given HST images of a galaxy with color gradients.""" t0 = time.time() print("Constructing chromatic PSFs") in_PSF = galsim.ChromaticAiry(lam=700, diam=2.4) if args.lsst_psf: out_PSF = galsim.ChromaticAtmosphere(galsim.Kolmogorov(fwhm=0.6), 500.0, zenith_angle=0 * galsim.degrees, parallactic_angle=0.0 * galsim.degrees) else: out_PSF = galsim.ChromaticAiry(lam=700, diam=1.2) # Euclid-like print("Constructing filters and SEDs") waves = np.arange(550.0, 900.1, 10.0) visband = galsim.Bandpass(galsim.LookupTable(waves, np.ones_like(waves), interpolant='linear'), wave_type='nm') split_points = np.linspace(550.0, 900.0, args.Nim + 1, endpoint=True) bands = [ visband.truncate(blue_limit=blim, red_limit=rlim) for blim, rlim in zip(split_points[:-1], split_points[1:]) ] outband = visband.truncate(blue_limit=args.out_blim, red_limit=args.out_rlim) maxk = max([ out_PSF.evaluateAtWavelength(waves[0]).maxK(), out_PSF.evaluateAtWavelength(waves[-1]).maxK() ]) SEDs = [ galsim.SED(galsim.LookupTable(waves, waves**i, interpolant='linear'), wave_type='nm', flux_type='fphotons').withFlux(1.0, visband) for i in range(args.NSED) ] print("Construction input noise correlation functions") rng = galsim.BaseDeviate(args.seed) in_xis = [ galsim.getCOSMOSNoise(cosmos_scale=args.in_scale, rng=rng).dilate(1 + i * 0.05).rotate( 30 * i * galsim.degrees) for i in range(args.Nim) ] print("Constructing galaxy") components = [galsim.Gaussian(half_light_radius=0.3).shear(e1=0.1)] for i in range(1, args.Nim): components.append( galsim.Gaussian(half_light_radius=0.3 + 0.1 * np.cos(i)).shear( e=0.4 + np.cos(i) * 0.4, beta=i * galsim.radians).shift(0.4 * i, -0.4 * i)) gal = galsim.Add([c * s for c, s in zip(components, SEDs)]) gal = gal.shift(-gal.centroid(visband)) in_prof = galsim.Convolve(gal, in_PSF) out_prof = galsim.Convolve(gal, out_PSF) print("Drawing input images") in_Nx = args.in_Nx in_Ny = args.in_Ny if args.in_Ny is not None else in_Nx in_imgs = [ in_prof.drawImage(band, nx=in_Nx, ny=in_Ny, scale=args.in_scale) for band in bands ] [ img.addNoiseSNR(xi, args.SNR, preserve_flux=True) for xi, img in zip(in_xis, in_imgs) ] print("Drawing true output image") out_img = out_prof.drawImage(outband, nx=args.out_Nx, ny=args.out_Nx, scale=args.out_scale) # Now "deconvolve" the chromatic HST PSF while asserting the correct SEDs. print("Constructing ChromaticRealGalaxy") crg = galsim.ChromaticRealGalaxy.makeFromImages(in_imgs, bands, in_PSF, in_xis, SEDs=SEDs, maxk=maxk) # crg should be effectively the same thing as gal now. Let's test. crg_prof = galsim.Convolve(crg, out_PSF) crg_img = crg_prof.drawImage(outband, nx=args.out_Nx, ny=args.out_Nx, scale=args.out_scale) print("Max comparison:", out_img.array.max(), crg_img.array.max()) print("Sum comparison:", out_img.array.sum(), crg_img.array.sum()) print("Took {} seconds".format(time.time() - t0)) if args.plot: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec in_extent = [ -in_Nx * args.in_scale / 2, in_Nx * args.in_scale / 2, -in_Ny * args.in_scale / 2, in_Ny * args.in_scale / 2 ] out_extent = [ -args.out_Nx * args.out_scale / 2, args.out_Nx * args.out_scale / 2, -args.out_Nx * args.out_scale / 2, args.out_Nx * args.out_scale / 2 ] fig = plt.figure(figsize=(10, 5)) outer_grid = gridspec.GridSpec(2, 1) # Input images inner_grid = gridspec.GridSpecFromSubplotSpec(1, args.Nim, outer_grid[0]) for i, img in enumerate(in_imgs): ax = plt.Subplot(fig, inner_grid[i]) im = ax.imshow(img.array, extent=in_extent, cmap='viridis') ax.set_title("band[{}] input".format(i)) # ax.set_xticks([]) # ax.set_yticks([]) fig.add_subplot(ax) plt.colorbar(im) inner_grid = gridspec.GridSpecFromSubplotSpec(1, 3, outer_grid[1]) # Output image, truth, and residual ax = plt.Subplot(fig, inner_grid[0]) ax.set_title("True output") im = ax.imshow(out_img.array, extent=out_extent, cmap='viridis') # ax.set_xticks([]) # ax.set_yticks([]) fig.add_subplot(ax) plt.colorbar(im) ax = plt.Subplot(fig, inner_grid[1]) ax.set_title("Reconstructed output") # ax.set_xticks([]) # ax.set_yticks([]) im = ax.imshow(crg_img.array, extent=out_extent, cmap='viridis') fig.add_subplot(ax) plt.colorbar(im) ax = plt.Subplot(fig, inner_grid[2]) ax.set_title("Residual") ax.set_xticks([]) ax.set_yticks([]) resid = crg_img.array - out_img.array vmin, vmax = np.percentile(resid, [5.0, 95.0]) v = np.max([np.abs(vmin), np.abs(vmax)]) im = ax.imshow(resid, extent=out_extent, cmap='seismic', vmin=-v, vmax=v) fig.add_subplot(ax) plt.colorbar(im) plt.tight_layout() plt.show()