def test_lsst_y_focus(): # Check that applying reasonable focus depth (from O'Connor++06) indeed leads to smaller spot # size for LSST y-band. rng = galsim.BaseDeviate(9876543210) bandpass = galsim.Bandpass("LSST_y.dat", wave_type='nm') sed = galsim.SED("1", wave_type='nm', flux_type='flambda') obj = galsim.Gaussian(fwhm=1e-5) oversampling = 32 photon_ops0 = [ galsim.WavelengthSampler(sed, bandpass, rng=rng), galsim.FRatioAngles(1.234, 0.606, rng=rng), galsim.FocusDepth(0.0), galsim.Refraction(3.9) ] img0 = obj.drawImage( sensor=galsim.SiliconSensor(), method='phot', n_photons=100000, photon_ops=photon_ops0, scale=0.2/oversampling, nx=32*oversampling, ny=32*oversampling, rng=rng ) T0 = img0.calculateMomentRadius() T0 *= 10*oversampling/0.2 # arcsec => microns # O'Connor finds minimum spot size when the focus depth is ~ -12 microns. Our sensor isn't # necessarily the same as the one there though; our minimum seems to be around -6 microns. # That could be due to differences in the design of the sensor though. We just use -6 microns # here, which is still useful to test the sign of the `depth` parameter and the interaction of # the 4 different surface operators required to produce this effect, and is roughly consistent # with O'Connor. depth1 = -6. # microns, negative means surface is intrafocal depth1 /= 10 # microns => pixels photon_ops1 = [ galsim.WavelengthSampler(sed, bandpass, rng=rng), galsim.FRatioAngles(1.234, 0.606, rng=rng), galsim.FocusDepth(depth1), galsim.Refraction(3.9) ] img1 = obj.drawImage( sensor=galsim.SiliconSensor(), method='phot', n_photons=100000, photon_ops=photon_ops1, scale=0.2/oversampling, nx=32*oversampling, ny=32*oversampling, rng=rng ) T1 = img1.calculateMomentRadius() T1 *= 10*oversampling/0.2 # arcsec => microns np.testing.assert_array_less(T1, T0)
def test_wavelength_sampler(): nphotons = 1000 obj = galsim.Exponential(flux=1.7, scale_radius=2.3) rng = galsim.UniformDeviate(1234) photon_array = obj.SBProfile.shoot(nphotons, rng) bppath = os.path.abspath(os.path.join(path, "../examples/data/")) sedpath = os.path.abspath(os.path.join(path, "../share/")) sed = galsim.SED(os.path.join(sedpath, 'CWW_E_ext.sed'), 'nm', 'flambda').thin() bandpass = galsim.Bandpass(os.path.join(bppath, 'LSST_r.dat'), 'nm').thin() sampler = galsim.WavelengthSampler(sed, bandpass, rng) sampler.applyTo(photon_array) # Note: the underlying functionality of the sampleWavelengths function is tested # in test_sed.py. So here we are really just testing that the wrapper class is # properly writing to the photon_array.wavelengths array. assert photon_array.hasAllocatedWavelengths() assert not photon_array.hasAllocatedAngles() print('mean wavelength = ',np.mean(photon_array.wavelength)) print('min wavelength = ',np.min(photon_array.wavelength)) print('max wavelength = ',np.max(photon_array.wavelength)) assert np.min(photon_array.wavelength) > bandpass.blue_limit assert np.max(photon_array.wavelength) < bandpass.red_limit # This is a regression test based on the value at commit 0b0cc764a9 np.testing.assert_almost_equal(np.mean(photon_array.wavelength), 616.92072, decimal=3) # Test that using this as a surface op work properly. # First do the shooting and clipping manually. im1 = galsim.Image(64,64,scale=1) im1.setCenter(0,0) photon_array.flux[photon_array.wavelength < 600] = 0. photon_array.addTo(im1.image.view()) # Make a dummy surface op that clips any photons with lambda < 600 class Clip600(object): def applyTo(self, photon_array): photon_array.flux[photon_array.wavelength < 600] = 0. # Use (a new) sampler and clip600 as surface_ops in drawImage im2 = galsim.Image(64,64,scale=1) im2.setCenter(0,0) clip600 = Clip600() rng2 = galsim.BaseDeviate(1234) sampler2 = galsim.WavelengthSampler(sed, bandpass, rng2) obj.drawImage(im2, method='phot', n_photons=nphotons, use_true_center=False, surface_ops=[sampler2,clip600], rng=rng2) print('sum = ',im1.array.sum(),im2.array.sum()) np.testing.assert_array_equal(im1.array, im2.array)
def test_photon_io(): """Test the ability to read and write photons to a file """ nphotons = 1000 obj = galsim.Exponential(flux=1.7, scale_radius=2.3) rng = galsim.UniformDeviate(1234) image = obj.drawImage(method='phot', n_photons=nphotons, save_photons=True, rng=rng) photons = image.photons assert photons.size() == len(photons) == nphotons with assert_raises(galsim.GalSimIncompatibleValuesError): obj.drawImage(method='phot', n_photons=nphotons, save_photons=True, maxN=1.e5) file_name = 'output/photons1.dat' photons.write(file_name) photons1 = galsim.PhotonArray.read(file_name) assert photons1.size() == nphotons assert not photons1.hasAllocatedWavelengths() assert not photons1.hasAllocatedAngles() np.testing.assert_array_equal(photons1.x, photons.x) np.testing.assert_array_equal(photons1.y, photons.y) np.testing.assert_array_equal(photons1.flux, photons.flux) sed = galsim.SED(os.path.join(sedpath, 'CWW_E_ext.sed'), 'nm', 'flambda').thin() bandpass = galsim.Bandpass(os.path.join(bppath, 'LSST_r.dat'), 'nm').thin() wave_sampler = galsim.WavelengthSampler(sed, bandpass, rng) angle_sampler = galsim.FRatioAngles(1.3, 0.3, rng) ops = [wave_sampler, angle_sampler] for op in ops: op.applyTo(photons) file_name = 'output/photons2.dat' photons.write(file_name) photons2 = galsim.PhotonArray.read(file_name) assert photons2.size() == nphotons assert photons2.hasAllocatedWavelengths() assert photons2.hasAllocatedAngles() np.testing.assert_array_equal(photons2.x, photons.x) np.testing.assert_array_equal(photons2.y, photons.y) np.testing.assert_array_equal(photons2.flux, photons.flux) np.testing.assert_array_equal(photons2.dxdz, photons.dxdz) np.testing.assert_array_equal(photons2.dydz, photons.dydz) np.testing.assert_array_equal(photons2.wavelength, photons.wavelength)
def __init__(self, obs_md, seed, nxy=64, pixel_scale=0.2): self.psf = SNRdocumentPSF(obs_md.OpsimMetaData['FWHMgeom']) self._rng = galsim.UniformDeviate(seed) self.nxy = nxy self.pixel_scale = pixel_scale fratio = 1.234 obscuration = 0.606 angles = galsim.FRatioAngles(fratio, obscuration, self._rng) self.bandpass = gs_bandpasses(obs_md.bandpass) self.sed = galsim.SED(lambda x: 1, 'nm', 'flambda').withFlux(1., self.bandpass) waves = galsim.WavelengthSampler(sed=self.sed, bandpass=self.bandpass, rng=self._rng) self.surface_ops = (waves, angles)
def waves(self): if self._waves is None: sed = galsim.SED(galsim.LookupTable(x=self.skyModel.wave, f=self.skyModel.spec[0, :]), wave_type='nm', flux_type='flambda') bandPassName = self.obs_metadata.bandpass bandpass = self.bandpassDict[bandPassName] index = np.where(bandpass.sb != 0) gs_bandpass \ = galsim.Bandpass(galsim.LookupTable(x=bandpass.wavelen[index], f=bandpass.sb[index]), wave_type='nm') self._waves = galsim.WavelengthSampler(sed=sed, bandpass=gs_bandpass, rng=self.randomNumbers) return self._waves
def test_surface_ops(): # Based on test_sensor.py:test_wavelengths_and_angles, but massively simplified. rng = galsim.BaseDeviate(1234) fratio = 1.2 obscuration = 0.2 assigner = galsim.FRatioAngles(fratio, obscuration, rng=rng) sed = galsim.SED('CWW_E_ext.sed', 'nm', 'flambda').thin() bandpass = galsim.Bandpass('LSST_i.dat', 'nm').thin() sampler = galsim.WavelengthSampler(sed, bandpass, rng=rng) obj = galsim.Gaussian(flux=353, sigma=0.3) im = galsim.Image(63, 63, scale=1) check_dep(obj.drawImage, im, method='phot', surface_ops=[sampler, assigner], rng=rng, save_photons=True) rng.reset(1234) assigner.ud.reset(rng) sampler.rng.reset(rng) photons = check_dep(obj.makePhot, surface_ops=[sampler, assigner], rng=rng) assert photons == im.photons rng.reset(1234) assigner.ud.reset(rng) sampler.rng.reset(rng) _, photons2 = check_dep(obj.drawPhot, image=im.copy(), surface_ops=[sampler, assigner], rng=rng) assert photons2 == im.photons
def main(argv): """ Make images to be used for characterizing the brighter-fatter effect - Each fits file is 5 x 5 postage stamps. - Each postage stamp is 40 x 40 pixels. - There are 3 sets of 5 images each. The 5 images are at 5 different flux levels - The three sets are (bf_1) B-F off, (bf_2) B-F on, diffusion off, (bf_3) B-F and diffusion on - Each image is in output/bf_set/bf_nfile.fits, where set ranges from 1-3 and nfile ranges from 1-5. """ logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("bf_plots") # Add the wavelength info bppath = "../../share/bandpasses/" sedpath = "../../share/" sed = galsim.SED(os.path.join(sedpath, 'CWW_E_ext.sed'), 'nm', 'flambda').thin() # Add the directions (seems to work - CL) fratio = 1.2 obscuration = 0.2 seed = 12345 assigner = galsim.FRatioAngles(fratio, obscuration, seed) bandpass = galsim.Bandpass(os.path.join(bppath, 'LSST_r.dat'), 'nm').thin() rng3 = galsim.BaseDeviate(1234) sampler = galsim.WavelengthSampler(sed, bandpass, rng3) # Define some parameters we'll use below. # Normally these would be read in from some parameter file. nx_tiles = 10 # ny_tiles = 10 # stamp_xsize = 40 # stamp_ysize = 40 # random_seed = 6424512 # pixel_scale = 0.2 # arcsec / pixel sky_level = 0.01 # ADU / arcsec^2 # Make output directory if not already present. if not os.path.isdir('output'): os.mkdir('output') gal_sigma = 0.2 # arcsec psf_sigma = 0.01 # arcsec pixel_scale = 0.2 # arcsec / pixel noise = 0.01 # standard deviation of the counts in each pixel shift_radius = 0.2 # arcsec (=pixels) logger.info('Starting bf_plots using:') logger.info(' - image with %d x %d postage stamps',nx_tiles,ny_tiles) logger.info(' - postage stamps of size %d x %d pixels',stamp_xsize,stamp_ysize) logger.info(' - Centroid shifts up to = %.2f pixels',shift_radius) rng = galsim.BaseDeviate(5678) sensor1 = galsim.Sensor() sensor2 = galsim.SiliconSensor(rng=rng, diffusion_factor=0.0) sensor3 = galsim.SiliconSensor(rng=rng) for set in range(1,4): starttime = time.time() exec("sensor = sensor%d"%set) for nfile in range(1,6): # Make bf_x directory if not already present. if not os.path.isdir('output/bf_%d'%set): os.mkdir('output/bf_%d'%set) gal_file_name = os.path.join('output','bf_%d/bf_%d.fits'%(set,nfile)) sex_file_name = os.path.join('output','bf_%d/bf_%d_SEX.fits.cat.reg'%(set,nfile)) sexfile = open(sex_file_name, 'w') gal_flux = 2.0e5 * nfile # total counts on the image # Define the galaxy profile gal = galsim.Gaussian(flux=gal_flux, sigma=gal_sigma) logger.debug('Made galaxy profile') # Define the PSF profile psf = galsim.Gaussian(flux=1., sigma=psf_sigma) # PSF flux should always = 1 logger.debug('Made PSF profile') # This profile is placed with different orientations and noise realizations # at each postage stamp in the gal image. gal_image = galsim.ImageF(stamp_xsize * nx_tiles-1 , stamp_ysize * ny_tiles-1, scale=pixel_scale) psf_image = galsim.ImageF(stamp_xsize * nx_tiles-1 , stamp_ysize * ny_tiles-1, scale=pixel_scale) shift_radius_sq = shift_radius**2 first_in_pair = True # Make pairs that are rotated by 90 degrees k = 0 for iy in range(ny_tiles): for ix in range(nx_tiles): # The normal procedure for setting random numbers in GalSim is to start a new # random number generator for each object using sequential seed values. # This sounds weird at first (especially if you were indoctrinated by Numerical # Recipes), but for the boost random number generator we use, the "random" # number sequences produced from sequential initial seeds are highly uncorrelated. # # The reason for this procedure is that when we use multiple processes to build # our images, we want to make sure that the results are deterministic regardless # of the way the objects get parcelled out to the different processes. # # Of course, this script isn't using multiple processes, so it isn't required here. # However, we do it nonetheless in order to get the same results as the config # version of this demo script (demo5.yaml). ud = galsim.UniformDeviate(random_seed+k) # Any kind of random number generator can take another RNG as its first # argument rather than a seed value. This makes both objects use the same # underlying generator for their pseudo-random values. #gd = galsim.GaussianDeviate(ud, sigma=gal_ellip_rms) # The -1's in the next line are to provide a border of # 1 pixel between postage stamps b = galsim.BoundsI(ix*stamp_xsize+1 , (ix+1)*stamp_xsize-1, iy*stamp_ysize+1 , (iy+1)*stamp_ysize-1) sub_gal_image = gal_image[b] sub_psf_image = psf_image[b] # Great08 randomized the locations of the two galaxies in each pair, # but for simplicity, we just do them in sequential postage stamps. if first_in_pair: # Use a random orientation: beta = ud() * 2. * math.pi * galsim.radians # Determine the ellipticity to use for this galaxy. ellip = 0.0 first_in_pair = False else: # Use the previous ellip and beta + 90 degrees beta += 90 * galsim.degrees first_in_pair = True # Make a new copy of the galaxy with an applied e1/e2-type distortion # by specifying the ellipticity and a real-space position angle this_gal = gal#gal.shear(e=ellip, beta=beta) # Apply a random shift_radius: rsq = 2 * shift_radius_sq while (rsq > shift_radius_sq): dx = (2*ud()-1) * shift_radius dy = (2*ud()-1) * shift_radius rsq = dx**2 + dy**2 this_gal = this_gal.shift(dx,dy) # Note that the shifted psf that we create here is purely for the purpose of being able # to draw a separate, shifted psf image. We do not use it when convolving the galaxy # with the psf. this_psf = psf.shift(dx,dy) # Make the final image, convolving with the (unshifted) psf final_gal = galsim.Convolve([psf,this_gal]) # Draw the image if ix == 0 and iy == 0: final_gal.drawImage(sub_gal_image, method = 'phot', sensor=sensor, surface_ops=[sampler, assigner], rng = rng, save_photons = True) photon_file = os.path.join('output','bf_%d/bf_%d_nx_%d_ny_%d_photon_file.fits'%(set,nfile,ix,iy)) sub_gal_image.photons.write(photon_file) else: final_gal.drawImage(sub_gal_image, method = 'phot', sensor=sensor, surface_ops=[sampler, assigner], rng = rng) # Now add an appropriate amount of noise to get our desired S/N # There are lots of definitions of S/N, but here is the one used by Great08 # We use a weighted integral of the flux: # S = sum W(x,y) I(x,y) / sum W(x,y) # N^2 = Var(S) = sum W(x,y)^2 Var(I(x,y)) / (sum W(x,y))^2 # Now we assume that Var(I(x,y)) is constant so # Var(I(x,y)) = noise_var # We also assume that we are using a matched filter for W, so W(x,y) = I(x,y). # Then a few things cancel and we find that # S/N = sqrt( sum I(x,y)^2 / noise_var ) # # The above procedure is encapsulated in the function image.addNoiseSNR which # sets the flux appropriately given the variance of the noise model. # In our case, noise_var = sky_level_pixel sky_level_pixel = sky_level * pixel_scale**2 noise = galsim.PoissonNoise(ud, sky_level=sky_level_pixel) #sub_gal_image.addNoiseSNR(noise, gal_signal_to_noise) # Draw the PSF image # No noise on PSF images. Just draw it as is. this_psf.drawImage(sub_psf_image) # For first instance, measure moments """ if ix==0 and iy==0: psf_shape = sub_psf_image.FindAdaptiveMom() temp_e = psf_shape.observed_shape.e if temp_e > 0.0: g_to_e = psf_shape.observed_shape.g / temp_e else: g_to_e = 0.0 logger.info('Measured best-fit elliptical Gaussian for first PSF image: ') logger.info(' g1, g2, sigma = %7.4f, %7.4f, %7.4f (pixels)', g_to_e*psf_shape.observed_shape.e1, g_to_e*psf_shape.observed_shape.e2, psf_shape.moments_sigma) """ x = b.center().x y = b.center().y logger.info('Galaxy (%d,%d): center = (%.0f,%0.f) (e,beta) = (%.4f,%.3f)', ix,iy,x,y,ellip,beta/galsim.radians) k = k+1 sexline = 'circle %f %f %f\n'%(x+dx/pixel_scale,y+dy/pixel_scale,gal_sigma/pixel_scale) sexfile.write(sexline) sexfile.close() logger.info('Done making images of postage stamps') # Now write the images to disk. #psf_image.write(psf_file_name) #logger.info('Wrote PSF file %s',psf_file_name) gal_image.write(gal_file_name) logger.info('Wrote image to %r',gal_file_name) # using %r adds quotes around filename for us finishtime = time.time() print("Time to complete set %d = %.2f seconds\n"%(set, finishtime-starttime))
def test_sensor_wavelengths_and_angles(): print('Starting test_wavelengths_and_angles') sys.stdout.flush() bppath = os.path.join(galsim.meta_data.share_dir, "bandpasses") sedpath = os.path.join(galsim.meta_data.share_dir, "SEDs") sed = galsim.SED(os.path.join(sedpath, 'CWW_E_ext.sed'), 'nm', 'flambda').thin() # Add the directions (not currently working?? seems to work - CL) fratio = 1.2 obscuration = 0.2 seed = 12345 assigner = galsim.FRatioAngles(fratio, obscuration, seed) obj = galsim.Gaussian(flux=3539, sigma=0.3) if __name__ == "__main__": bands = ['r', 'i', 'z', 'y'] else: bands = ['i'] # Only test the i band for nosetests for band in bands: bandpass = galsim.Bandpass(os.path.join(bppath, 'LSST_%s.dat'%band), 'nm').thin() rng3 = galsim.BaseDeviate(1234) sampler = galsim.WavelengthSampler(sed, bandpass, rng3) rng4 = galsim.BaseDeviate(5678) silicon = galsim.SiliconSensor(rng=rng4, diffusion_factor=0.0) # We'll draw the same object using SiliconSensor im1 = galsim.ImageD(64, 64, scale=0.3) # Will use sensor=silicon, no wavelengths im2 = galsim.ImageD(64, 64, scale=0.3) # Will use sensor=silicon, with wavelengths im3 = galsim.ImageD(64, 64, scale=0.3) # Will use sensor=silicon, with angles im4 = galsim.ImageD(64, 64, scale=0.3) # Will use sensor=silicon, with wavelengths, angles rng1 = galsim.BaseDeviate(5678) rng2 = galsim.BaseDeviate(5678) rng3 = galsim.BaseDeviate(5678) rng4 = galsim.BaseDeviate(5678) # Use photon shooting first obj.drawImage(im1, method='phot', sensor=silicon, rng=rng1) obj.drawImage(im2, method='phot', sensor=silicon, surface_ops=[sampler], rng=rng2) obj.drawImage(im3, method='phot', sensor=silicon, surface_ops=[assigner], rng=rng3) obj.drawImage(im4, method='phot', sensor=silicon, surface_ops=[sampler, assigner], rng=rng4) r1 = im1.calculateMomentRadius(flux=obj.flux) r2 = im2.calculateMomentRadius(flux=obj.flux) r3 = im3.calculateMomentRadius(flux=obj.flux) r4 = im4.calculateMomentRadius(flux=obj.flux) print('Testing Wavelength and Angle sampling - %s band'%band) print('Flux = %.0f: sum peak radius'%obj.flux) print('No lamb, no angles: %.1f %.2f %f'%( im1.array.sum(),im1.array.max(), r1)) print('W/ lamb, no angles: %.1f %.2f %f'%( im2.array.sum(),im2.array.max(), r2)) print('No lamb, w/ angles: %.1f %.2f %f'%( im3.array.sum(),im3.array.max(), r3)) print('W/ lamb, w/ angles: %.1f %.2f %f'%( im4.array.sum(),im4.array.max(), r4)) # r4 should be greater than r1 with wavelengths and angles turned on. sigma_r = 1. / np.sqrt(obj.flux) * im1.scale print('check r4 > r1 due to added wavelengths and angles') print('r1 = %f, r4 = %f, 2*sigma_r = %f'%(r1,r4,2.*sigma_r)) assert r4 > r1 # Now check fft obj.drawImage(im1, method='fft', sensor=silicon, rng=rng1) obj.drawImage(im2, method='fft', sensor=silicon, surface_ops=[sampler], rng=rng2) obj.drawImage(im3, method='fft', sensor=silicon, surface_ops=[assigner], rng=rng3) obj.drawImage(im4, method='fft', sensor=silicon, surface_ops=[sampler, assigner], rng=rng4) r1 = im1.calculateMomentRadius(flux=obj.flux) r2 = im2.calculateMomentRadius(flux=obj.flux) r3 = im3.calculateMomentRadius(flux=obj.flux) r4 = im4.calculateMomentRadius(flux=obj.flux) print('Testing Wavelength and Angle sampling - %s band'%band) print('Flux = %.0f: sum peak radius'%obj.flux) print('No lamb, no angles: %.1f %.2f %f'%( im1.array.sum(),im1.array.max(), r1)) print('W/ lamb, no angles: %.1f %.2f %f'%( im2.array.sum(),im2.array.max(), r2)) print('No lamb, w/ angles: %.1f %.2f %f'%( im3.array.sum(),im3.array.max(), r3)) print('W/ lamb, w/ angles: %.1f %.2f %f'%( im4.array.sum(),im4.array.max(), r4)) # r4 should be greater than r1 with wavelengths and angles turned on. sigma_r = 1. / np.sqrt(obj.flux) * im1.scale print('check r4 > r1 due to added wavelengths and angles') print('r1 = %f, r4 = %f, 2*sigma_r = %f'%(r1,r4,2.*sigma_r)) assert r4 > r1
import galsim bd = galsim.BaseDeviate(12) depths = np.linspace(-25, 25, 41) # microns obj = galsim.Gaussian(sigma=1e-4) sed = galsim.SED("1", wave_type='nm', flux_type='flambda') fig, ax = plt.subplots() oversampling = 16 for filter in ['g', 'z', 'y']: bandpass = galsim.Bandpass("LSST_{}.dat".format(filter), wave_type='nm') Ts = [] for depth in depths: depth_pix = depth / 10 surface_ops = [ galsim.WavelengthSampler(sed, bandpass, rng=bd), galsim.FRatioAngles(1.234, 0.606, rng=bd), galsim.FocusDepth(depth_pix), galsim.Refraction(3.9) # approx number for Silicon ] img = obj.drawImage( sensor=galsim.SiliconSensor(), method='phot', n_photons=1_000_000, surface_ops=surface_ops, scale=0.2 / oversampling, # oversample pixels to better resolve PSF size nx=32 * oversampling, # 6.4 arcsec stamp ny=32 * oversampling, ) Ts.append(img.calculateMomentRadius())
return photon_array sky_sed_file = 'sky_sed.txt' band = 'r' bandpass_file = '%s_band.txt' % band gs_sed = galsim.SED(sky_sed_file, wave_type='nm', flux_type='flambda').thin(rel_err=0.1) gs_bandpass = galsim.Bandpass(bandpass_file, wave_type='nm').thin(rel_err=0.1) print('made sed, bandpass') seed = 140101 rng = galsim.UniformDeviate(seed) waves = galsim.WavelengthSampler(sed=gs_sed, bandpass=gs_bandpass, rng=rng) fratio = 1.234 obscuration = 0.606 angles = galsim.FRatioAngles(fratio, obscuration, rng) treering_func = galsim.SiliconSensor.simple_treerings(0.26, 47.) treering_center = galsim.PositionD(0, 0) skyCounts = 800. print('skyCounts = ', skyCounts) bundles_per_pix = 0 # Note: bundling doesn't work right if using tree rings. chunk_size = int(1e7) image = galsim.ImageF(2000, 500) print('image bounds = ', image.bounds)
def test_resume(): """Test that the resume option for accumulate works properly. """ # Note: This test is based on a script devel/lsst/treering_skybg_check.py rng = galsim.UniformDeviate(314159) if __name__ == "__main__": flux_per_pixel = 40 nx = 200 ny = 200 block_size = int(1.2e5) nrecalc = 1.e6 else: flux_per_pixel = 40 nx = 20 ny = 20 block_size = int(1.3e3) nrecalc = 1.e4 expected_num_photons = nx * ny * flux_per_pixel pd = galsim.PoissonDeviate(rng, mean=expected_num_photons) num_photons = int(pd()) # Poisson realization of the given expected number of photons. #nrecalc = num_photons / 2 # Only recalc once. flux_per_photon = 1 print('num_photons = ',num_photons,' .. expected = ',expected_num_photons) # Use treerings to make sure that aspect of the setup is preserved properly on resume treering_func = galsim.SiliconSensor.simple_treerings(0.5, 250.) treering_center = galsim.PositionD(-1000,0) sensor1 = galsim.SiliconSensor(rng=rng.duplicate(), nrecalc=nrecalc, treering_func=treering_func, treering_center=treering_center) sensor2 = galsim.SiliconSensor(rng=rng.duplicate(), nrecalc=nrecalc, treering_func=treering_func, treering_center=treering_center) sensor3 = galsim.SiliconSensor(rng=rng.duplicate(), nrecalc=nrecalc, treering_func=treering_func, treering_center=treering_center) waves = galsim.WavelengthSampler(sed = galsim.SED('1', 'nm', 'fphotons'), bandpass = galsim.Bandpass('LSST_r.dat', 'nm'), rng=rng) angles = galsim.FRatioAngles(1.2, 0.4, rng) im1 = galsim.ImageF(nx,ny) # Will not use resume im2 = galsim.ImageF(nx,ny) # Will use resume im3 = galsim.ImageF(nx,ny) # Will run all photons in one pass t_resume = 0 t_no_resume = 0 all_photons = galsim.PhotonArray(num_photons) n_added = 0 first = True while num_photons > 0: print(num_photons,'photons left. image min/max =',im1.array.min(),im1.array.max()) nphot = min(block_size, num_photons) num_photons -= nphot t0 = time.time() photons = galsim.PhotonArray(int(nphot)) rng.generate(photons.x) # 0..1 so far photons.x *= nx photons.x += 0.5 # Now from xmin-0.5 .. xmax+0.5 rng.generate(photons.y) photons.y *= ny photons.y += 0.5 photons.flux = flux_per_photon waves.applyTo(photons) angles.applyTo(photons) all_photons.x[n_added:n_added+nphot] = photons.x all_photons.y[n_added:n_added+nphot] = photons.y all_photons.flux[n_added:n_added+nphot] = photons.flux all_photons.dxdz[n_added:n_added+nphot] = photons.dxdz all_photons.dydz[n_added:n_added+nphot] = photons.dydz all_photons.wavelength[n_added:n_added+nphot] = photons.wavelength n_added += nphot t1 = time.time() sensor1.accumulate(photons, im1) t2 = time.time() sensor2.accumulate(photons, im2, resume = not first) first = False t3 = time.time() print('Times = ',t1-t0,t2-t1,t3-t2) t_resume += t3-t2 t_no_resume += t2-t1 print('max diff = ',np.max(np.abs(im1.array - im2.array))) print('max rel diff = ',np.max(np.abs(im1.array - im2.array)/np.abs(im2.array))) np.testing.assert_almost_equal(im2.array/expected_num_photons, im1.array/expected_num_photons, decimal=5) print('Time with resume = ',t_resume) print('Time without resume = ',t_no_resume) assert t_resume < t_no_resume # The resume path should be exactly the same as doing all the photons at once. sensor3.accumulate(all_photons, im3) np.testing.assert_array_equal(im2.array, im3.array) # If resume is used either with the wrong image or on the first call to accumulate, then # this should raise an exception. assert_raises(RuntimeError, sensor3.accumulate, all_photons, im1, resume=True) sensor4 = galsim.SiliconSensor(rng=rng.duplicate(), nrecalc=nrecalc, treering_func=treering_func, treering_center=treering_center) assert_raises(RuntimeError, sensor4.accumulate, all_photons, im1, resume=True)
def main(): pr = cProfile.Profile() pr.enable() rng = galsim.UniformDeviate(8675309) wcs = galsim.FitsWCS('../tests/des_data/DECam_00154912_12_header.fits') image = galsim.Image(xsize, ysize, wcs=wcs) bandpass = galsim.Bandpass('LSST_r.dat', wave_type='nm').thin(0.1) base_wavelength = bandpass.effective_wavelength angles = galsim.FRatioAngles(fratio, obscuration, rng) sensor = galsim.SiliconSensor(rng=rng, nrecalc=nrecalc) # Figure out the local_sidereal time from the observation location and time. lsst_lat = '-30:14:23.76' lsst_long = '-70:44:34.67' obs_time = '2012-11-24 03:37:25.023964' # From the header of the wcs file obs = astropy.time.Time(obs_time, scale='utc', location=(lsst_long + 'd', lsst_lat + 'd')) local_sidereal_time = obs.sidereal_time('apparent').value # Convert the ones we need below to galsim Angles. local_sidereal_time *= galsim.hours lsst_lat = galsim.Angle.from_dms(lsst_lat) times = [] mem = [] phot = [] t0 = time.clock() for iobj in range(nobjects): sys.stderr.write('.') psf = make_psf(rng) gal = make_gal(rng) obj = galsim.Convolve(psf, gal) sed = get_sed(rng) waves = galsim.WavelengthSampler(sed=sed, bandpass=bandpass, rng=rng) image_pos = get_pos(rng) sky_coord = wcs.toWorld(image_pos) bounds, offset = calculate_bounds(obj, image_pos, image) ha = local_sidereal_time - sky_coord.ra dcr = galsim.PhotonDCR(base_wavelength=base_wavelength, obj_coord=sky_coord, HA=ha, latitude=lsst_lat) surface_ops = (waves, angles, dcr) obj.drawImage(method='phot', image=image[bounds], offset=offset, rng=rng, sensor=sensor, surface_ops=surface_ops) times.append(time.clock() - t0) mem.append(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss) phot.append(obj.flux) image.write('phot.fits') phot = np.cumsum(phot) make_plots(times, mem, phot) pr.disable() ps = pstats.Stats(pr).sort_stats('time') ps.print_stats(20)
def test_dcr_moments(): """Check that DCR gets the direction of the moment changes correct for some simple geometries. i.e. Basically check the sign conventions used in the DCR code. """ # First, the basics. # 1. DCR shifts blue photons closer to zenith, because the index of refraction larger. # cf. http://lsstdesc.github.io/chroma/ # 2. Galsim models profiles as seen from Earth with North up (and therefore East left). # 3. Hour angle is negative when the object is in the east (soon after rising, say), # zero when crossing the zenith meridian, and then positive to the west. # Use g-band, where the effect is more dramatic across the band than in redder bands. # Also use a reference wavelength significantly to the red, so there should be a net # overall shift towards zenith as well as a shear along the line to zenith. bandpass = galsim.Bandpass('LSST_g.dat', 'nm').thin(0.1) base_wavelength = 600 # > red end of g band # Uniform across the band is fine for this. sed = galsim.SED('1', wave_type='nm', flux_type='fphotons') rng = galsim.BaseDeviate(31415) wave_sampler = galsim.WavelengthSampler(sed, bandpass, rng) star = galsim.Kolmogorov(fwhm=0.3, flux=1.e6) # 10^6 photons should be enough. im = galsim.ImageD( 50, 50, scale=0.05) # Small pixel scale, so shift is many pixels. ra = 0 * galsim.degrees # Completely irrelevant here. lat = -20 * galsim.degrees # Also doesn't really matter much. # 1. HA < 0, Dec < lat Spot should be shifted up and right. e2 > 0. dcr = galsim.PhotonDCR(base_wavelength=base_wavelength, HA=-2 * galsim.hours, latitude=lat, obj_coord=galsim.CelestialCoord( ra, lat - 20 * galsim.degrees)) surface_ops = [wave_sampler, dcr] star.drawImage(image=im, method='phot', rng=rng, surface_ops=surface_ops) moments = galsim.utilities.unweighted_moments(im, origin=im.true_center) print('1. HA < 0, Dec < lat: ', moments) assert moments['My'] > 0 # up assert moments['Mx'] > 0 # right assert moments['Mxy'] > 0 # e2 > 0 # 2. HA = 0, Dec < lat Spot should be shifted up. e1 < 0, e2 ~= 0. dcr = galsim.PhotonDCR(base_wavelength=base_wavelength, HA=0 * galsim.hours, latitude=lat, obj_coord=galsim.CelestialCoord( ra, lat - 20 * galsim.degrees)) surface_ops = [wave_sampler, dcr] star.drawImage(image=im, method='phot', rng=rng, surface_ops=surface_ops) moments = galsim.utilities.unweighted_moments(im, origin=im.true_center) print('2. HA = 0, Dec < lat: ', moments) assert moments['My'] > 0 # up assert abs(moments['Mx']) < 0.05 # not left or right assert moments['Mxx'] < moments['Myy'] # e1 < 0 assert abs(moments['Mxy']) < 0.1 # e2 ~= 0 # 3. HA > 0, Dec < lat Spot should be shifted up and left. e2 < 0. dcr = galsim.PhotonDCR(base_wavelength=base_wavelength, HA=2 * galsim.hours, latitude=lat, obj_coord=galsim.CelestialCoord( ra, lat - 20 * galsim.degrees)) surface_ops = [wave_sampler, dcr] star.drawImage(image=im, method='phot', rng=rng, surface_ops=surface_ops) moments = galsim.utilities.unweighted_moments(im, origin=im.true_center) print('3. HA > 0, Dec < lat: ', moments) assert moments['My'] > 0 # up assert moments['Mx'] < 0 # left assert moments['Mxy'] < 0 # e2 < 0 # 4. HA < 0, Dec = lat Spot should be shifted right. e1 > 0, e2 ~= 0. dcr = galsim.PhotonDCR(base_wavelength=base_wavelength, HA=-2 * galsim.hours, latitude=lat, obj_coord=galsim.CelestialCoord(ra, lat)) surface_ops = [wave_sampler, dcr] star.drawImage(image=im, method='phot', rng=rng, surface_ops=surface_ops) moments = galsim.utilities.unweighted_moments(im, origin=im.true_center) print('4. HA < 0, Dec = lat: ', moments) assert abs(moments['My'] ) < 1. # not up or down (Actually slightly down in the south.) assert moments['Mx'] > 0 # right assert moments['Mxx'] > moments['Myy'] # e1 > 0 assert abs( moments['Mxy'] ) < 2. # e2 ~= 0 (Actually slightly negative because of curvature.) # 5. HA = 0, Dec = lat Spot should not be shifted. e1 ~= 0, e2 ~= 0. dcr = galsim.PhotonDCR(base_wavelength=base_wavelength, HA=0 * galsim.hours, latitude=lat, obj_coord=galsim.CelestialCoord(ra, lat)) surface_ops = [wave_sampler, dcr] star.drawImage(image=im, method='phot', rng=rng, surface_ops=surface_ops) moments = galsim.utilities.unweighted_moments(im, origin=im.true_center) print('5. HA = 0, Dec = lat: ', moments) assert abs(moments['My']) < 0.05 # not up or down assert abs(moments['Mx']) < 0.05 # not left or right assert abs(moments['Mxx'] - moments['Myy']) < 0.1 # e1 ~= 0 assert abs(moments['Mxy']) < 0.1 # e2 ~= 0 # 6. HA > 0, Dec = lat Spot should be shifted left. e1 > 0, e2 ~= 0. dcr = galsim.PhotonDCR(base_wavelength=base_wavelength, HA=2 * galsim.hours, latitude=lat, obj_coord=galsim.CelestialCoord(ra, lat)) surface_ops = [wave_sampler, dcr] star.drawImage(image=im, method='phot', rng=rng, surface_ops=surface_ops) moments = galsim.utilities.unweighted_moments(im, origin=im.true_center) print('6. HA > 0, Dec = lat: ', moments) assert abs(moments['My'] ) < 1. # not up or down (Actually slightly down in the south.) assert moments['Mx'] < 0 # left assert moments['Mxx'] > moments['Myy'] # e1 > 0 assert abs( moments['Mxy'] ) < 2. # e2 ~= 0 (Actually slgihtly positive because of curvature.) # 7. HA < 0, Dec > lat Spot should be shifted down and right. e2 < 0. dcr = galsim.PhotonDCR(base_wavelength=base_wavelength, HA=-2 * galsim.hours, latitude=lat, obj_coord=galsim.CelestialCoord( ra, lat + 20 * galsim.degrees)) surface_ops = [wave_sampler, dcr] star.drawImage(image=im, method='phot', rng=rng, surface_ops=surface_ops) moments = galsim.utilities.unweighted_moments(im, origin=im.true_center) print('7. HA < 0, Dec > lat: ', moments) assert moments['My'] < 0 # down assert moments['Mx'] > 0 # right assert moments['Mxy'] < 0 # e2 < 0 # 8. HA = 0, Dec > lat Spot should be shifted down. e1 < 0, e2 ~= 0. dcr = galsim.PhotonDCR(base_wavelength=base_wavelength, HA=0 * galsim.hours, latitude=lat, obj_coord=galsim.CelestialCoord( ra, lat + 20 * galsim.degrees)) surface_ops = [wave_sampler, dcr] star.drawImage(image=im, method='phot', rng=rng, surface_ops=surface_ops) moments = galsim.utilities.unweighted_moments(im, origin=im.true_center) print('8. HA = 0, Dec > lat: ', moments) assert moments['My'] < 0 # down assert abs(moments['Mx']) < 0.05 # not left or right assert moments['Mxx'] < moments['Myy'] # e1 < 0 assert abs(moments['Mxy']) < 0.1 # e2 ~= 0 # 9. HA > 0, Dec > lat Spot should be shifted down and left. e2 > 0. dcr = galsim.PhotonDCR(base_wavelength=base_wavelength, HA=2 * galsim.hours, latitude=lat, obj_coord=galsim.CelestialCoord( ra, lat + 20 * galsim.degrees)) surface_ops = [wave_sampler, dcr] star.drawImage(image=im, method='phot', rng=rng, surface_ops=surface_ops) moments = galsim.utilities.unweighted_moments(im, origin=im.true_center) print('9. HA > 0, Dec > lat: ', moments) assert moments['My'] < 0 # down assert moments['Mx'] < 0 # left assert moments['Mxy'] > 0 # e2 > 0
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_wavelength_sampler(): nphotons = 1000 obj = galsim.Exponential(flux=1.7, scale_radius=2.3) rng = galsim.UniformDeviate(1234) photon_array = obj.shoot(nphotons, rng) sed = galsim.SED(os.path.join(sedpath, 'CWW_E_ext.sed'), 'A', 'flambda').thin() bandpass = galsim.Bandpass(os.path.join(bppath, 'LSST_r.dat'), 'nm').thin() sampler = galsim.WavelengthSampler(sed, bandpass, rng) sampler.applyTo(photon_array) # Note: the underlying functionality of the sampleWavelengths function is tested # in test_sed.py. So here we are really just testing that the wrapper class is # properly writing to the photon_array.wavelengths array. assert photon_array.hasAllocatedWavelengths() assert not photon_array.hasAllocatedAngles() print('mean wavelength = ', np.mean(photon_array.wavelength)) print('min wavelength = ', np.min(photon_array.wavelength)) print('max wavelength = ', np.max(photon_array.wavelength)) assert np.min(photon_array.wavelength) > bandpass.blue_limit assert np.max(photon_array.wavelength) < bandpass.red_limit # This is a regression test based on the value at commit 134a119 np.testing.assert_allclose(np.mean(photon_array.wavelength), 622.755128, rtol=1.e-4) # If we use a flat SED (in photons/nm), then the mean sampled wavelength should very closely # match the bandpass effective wavelength. photon_array2 = galsim.PhotonArray(100000) sed2 = galsim.SED('1', 'nm', 'fphotons') sampler2 = galsim.WavelengthSampler(sed2, bandpass, rng) sampler2.applyTo(photon_array2) np.testing.assert_allclose( np.mean(photon_array2.wavelength), bandpass.effective_wavelength, rtol=0, atol=0.2, # 2 Angstrom accuracy is pretty good err_msg="Mean sampled wavelength not close to effective_wavelength") # Test that using this as a surface op works properly. # First do the shooting and clipping manually. im1 = galsim.Image(64, 64, scale=1) im1.setCenter(0, 0) photon_array.flux[photon_array.wavelength < 600] = 0. photon_array.addTo(im1) # Make a dummy surface op that clips any photons with lambda < 600 class Clip600(object): def applyTo(self, photon_array, local_wcs=None): photon_array.flux[photon_array.wavelength < 600] = 0. # Use (a new) sampler and clip600 as surface_ops in drawImage im2 = galsim.Image(64, 64, scale=1) im2.setCenter(0, 0) clip600 = Clip600() rng2 = galsim.BaseDeviate(1234) sampler2 = galsim.WavelengthSampler(sed, bandpass, rng2) obj.drawImage(im2, method='phot', n_photons=nphotons, use_true_center=False, surface_ops=[sampler2, clip600], rng=rng2) print('sum = ', im1.array.sum(), im2.array.sum()) np.testing.assert_array_equal(im1.array, im2.array)
def drawObject(self, gsObject): """ Draw an astronomical object on all of the relevant FITS files. @param [in] gsObject is an instantiation of the GalSimCelestialObject class carrying all of the information for the object whose image is to be drawn @param [out] outputString is a string denoting which detectors the astronomical object illumines, suitable for output in the GalSim InstanceCatalog """ # find the detectors which the astronomical object illumines outputString, \ detectorList, \ centeredObj = self.findAllDetectors(gsObject) # Make sure this object is marked as "drawn" since we only # care that this method has been called for this object. self.drawn_objects.add(gsObject.uniqueId) # Compute the realized object fluxes (as drawn from the # corresponding Poisson distribution) for each band and return # right away if all values are zero in order to save compute. fluxes = [gsObject.flux(bandpassName) for bandpassName in self.bandpassDict] realized_fluxes = [galsim.PoissonDeviate(self._rng, mean=f)() for f in fluxes] if all([f == 0 for f in realized_fluxes]): return outputString if len(detectorList) == 0: # there is nothing to draw return outputString self._addNoiseAndBackground(detectorList) # Create a surface operation to sample incident angles and a # galsim.SED object for sampling the wavelengths of the # incident photons. fratio = 1.234 # From https://www.lsst.org/scientists/keynumbers obscuration = 0.606 # (8.4**2 - 6.68**2)**0.5 / 8.4 angles = galsim.FRatioAngles(fratio, obscuration, self._rng) sed_lut = galsim.LookupTable(x=gsObject.sed.wavelen, f=gsObject.sed.flambda) gs_sed = galsim.SED(sed_lut, wave_type='nm', flux_type='flambda', redshift=0.) local_hour_angle \ = self.getHourAngle(self.obs_metadata.mjd.TAI, self.obs_metadata.pointingRA)*galsim.degrees obs_latitude = self.observatory.getLatitude().asDegrees()*galsim.degrees ra_obs, dec_obs = observedFromPupilCoords(gsObject.xPupilRadians, gsObject.yPupilRadians, obs_metadata=self.obs_metadata) obj_coord = galsim.CelestialCoord(ra_obs*galsim.degrees, dec_obs*galsim.degrees) for bandpassName, realized_flux in zip(self.bandpassDict, realized_fluxes): gs_bandpass = self.gs_bandpass_dict[bandpassName] waves = galsim.WavelengthSampler(sed=gs_sed, bandpass=gs_bandpass, rng=self._rng) dcr = galsim.PhotonDCR(base_wavelength=gs_bandpass.effective_wavelength, HA=local_hour_angle, latitude=obs_latitude, obj_coord=obj_coord) # Set the object flux to the value realized from the # Poisson distribution. obj = centeredObj.withFlux(realized_flux) for detector in detectorList: name = self._getFileName(detector=detector, bandpassName=bandpassName) xPix, yPix = detector.camera_wrapper\ .pixelCoordsFromPupilCoords(gsObject.xPupilRadians, gsObject.yPupilRadians, chipName=detector.name, obs_metadata=self.obs_metadata) # Ensure the rng used by the sensor object is set to the desired state. self.sensor[detector.name].rng.reset(self._rng) surface_ops = [waves, dcr, angles] # Desired position to draw the object. image_pos = galsim.PositionD(xPix, yPix) # Find a postage stamp region to draw onto. Use (sky # noise)/3. as the nominal minimum surface brightness # for rendering an extended object. keep_sb_level = np.sqrt(self.sky_bg_per_pixel)/3. bounds = self.getStampBounds(gsObject, realized_flux, image_pos, keep_sb_level, 3*keep_sb_level) # Ensure the bounds of the postage stamp lie within the image. bounds = bounds & self.detectorImages[name].bounds if bounds.isDefined(): # offset is relative to the "true" center of the postage stamp. offset = image_pos - bounds.true_center obj.drawImage(method='phot', offset=offset, rng=self._rng, maxN=int(1e6), image=self.detectorImages[name][bounds], sensor=self.sensor[detector.name], surface_ops=surface_ops, add_to_image=True, poisson_flux=False, gain=detector.photParams.gain) # If we are writing centroid files,store the entry. if self.centroid_base_name is not None: centroid_tuple = (detector.fileName, bandpassName, gsObject.uniqueId, gsObject.flux(bandpassName), xPix, yPix) self.centroid_list.append(centroid_tuple) self.write_checkpoint() return outputString