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 angles(self): if self._angles is None: fratio = 1.234 # From https://www.lsst.org/scientists/keynumbers obscuration = 0.606 # (8.4**2 - 6.68**2)**0.5 / 8.4 self._angles \ = galsim.FRatioAngles(fratio, obscuration, self.randomNumbers) return self._angles
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 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
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()) Ts = np.array(Ts) / 0.2 * 10 * oversampling # convert arcsec -> micron
def test_photon_angles(): """Test the photon_array function """ # Make a photon array seed = 12345 ud = galsim.UniformDeviate(seed) gal = galsim.Sersic(n=4, half_light_radius=1) photon_array = gal.SBProfile.shoot(100000, ud) # Add the directions (N.B. using the same seed as for generating the photon array # above. The fact that it is the same does not matter here; the testing routine # only needs to have a definite seed value so the consistency of the results with # expectations can be evaluated precisely fratio = 1.2 obscuration = 0.2 # rng can be None, an existing BaseDeviate, or an integer for rng in [ None, ud, 12345 ]: assigner = galsim.FRatioAngles(fratio, obscuration, rng) assigner.applyTo(photon_array) dxdz = photon_array.getDXDZArray() dydz = photon_array.getDYDZArray() phi = np.arctan2(dydz,dxdz) tantheta = np.sqrt(np.square(dxdz) + np.square(dydz)) sintheta = np.sin(np.arctan(tantheta)) # Check that the values are within the ranges expected # (The test on phi really can't fail, because it is only testing the range of the # arctan2 function.) np.testing.assert_array_less(-phi, np.pi, "Azimuth angles outside possible range") np.testing.assert_array_less(phi, np.pi, "Azimuth angles outside possible range") fov_angle = np.arctan(0.5 / fratio) obscuration_angle = obscuration * fov_angle np.testing.assert_array_less(-sintheta, -np.sin(obscuration_angle), "Inclination angles outside possible range") np.testing.assert_array_less(sintheta, np.sin(fov_angle), "Inclination angles outside possible range") # Compare these slopes with the expected distributions (uniform in azimuth # over all azimiths and uniform in sin(inclination) over the range of # allowed inclinations # Only test this for the last one, so we make sure we have a deterministic result. # (The above tests should be reliable even for the default rng.) phi_histo, phi_bins = np.histogram(phi, bins=100) sintheta_histo, sintheta_bins = np.histogram(sintheta, bins=100) phi_ref = float(np.sum(phi_histo))/phi_histo.size sintheta_ref = float(np.sum(sintheta_histo))/sintheta_histo.size chisqr_phi = np.sum(np.square(phi_histo - phi_ref)/phi_ref) / phi_histo.size chisqr_sintheta = np.sum(np.square(sintheta_histo - sintheta_ref) / sintheta_ref) / sintheta_histo.size print('chisqr_phi = ',chisqr_phi) print('chisqr_sintheta = ',chisqr_sintheta) assert 0.9 < chisqr_phi < 1.1, "Distribution in azimuth is not nearly uniform" assert 0.9 < chisqr_sintheta < 1.1, "Distribution in sin(inclination) is not nearly uniform" # Try some invalid inputs try: np.testing.assert_raises(ValueError, galsim.FRatioAngles, fratio=-0.3) np.testing.assert_raises(ValueError, galsim.FRatioAngles, fratio=1.2, obscuration=-0.3) np.testing.assert_raises(ValueError, galsim.FRatioAngles, fratio=1.2, obscuration=1.0) np.testing.assert_raises(ValueError, galsim.FRatioAngles, fratio=1.2, obscuration=1.9) except ImportError: pass
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) num_photons = int(np.prod(image.array.shape) * skyCounts) print('num_photons = ', num_photons)
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 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
def test_focus_depth(): bd = galsim.BaseDeviate(1234) for _ in range(100): # Test that FocusDepth is additive photon_array = galsim.PhotonArray(1000) photon_array2 = galsim.PhotonArray(1000) photon_array.x = 0.0 photon_array.y = 0.0 photon_array2.x = 0.0 photon_array2.y = 0.0 galsim.FRatioAngles(1.234, obscuration=0.606, rng=bd).applyTo(photon_array) photon_array2.dxdz[:] = photon_array.dxdz photon_array2.dydz[:] = photon_array.dydz fd1 = galsim.FocusDepth(1.1) fd2 = galsim.FocusDepth(2.2) fd3 = galsim.FocusDepth(3.3) fd1.applyTo(photon_array) fd2.applyTo(photon_array) fd3.applyTo(photon_array2) np.testing.assert_allclose(photon_array.x, photon_array2.x, rtol=0, atol=1e-15) np.testing.assert_allclose(photon_array.y, photon_array2.y, rtol=0, atol=1e-15) # Assuming focus is at x=y=0, then # intrafocal (depth < 0) => (x > 0 => dxdz < 0) # extrafocal (depth > 0) => (x > 0 => dxdz > 0) # We applied an extrafocal operation above, so check for corresponding # relation between x, dxdz np.testing.assert_array_less(0, photon_array.x * photon_array.dxdz) # transforming by depth and -depth is null fd4 = galsim.FocusDepth(-3.3) fd4.applyTo(photon_array) np.testing.assert_allclose(photon_array.x, 0.0, rtol=0, atol=1e-15) np.testing.assert_allclose(photon_array.y, 0.0, rtol=0, atol=1e-15) # Check that invalid photon array is trapped pa = galsim.PhotonArray(10) fd = galsim.FocusDepth(1.0) with np.testing.assert_raises(galsim.GalSimError): fd.applyTo(pa) # Check that we can infer depth from photon positions before and after... for _ in range(100): photon_array = galsim.PhotonArray(1000) photon_array2 = galsim.PhotonArray(1000) ud = galsim.UniformDeviate(bd) ud.generate(photon_array.x) ud.generate(photon_array.y) photon_array.x -= 0.5 photon_array.y -= 0.5 galsim.FRatioAngles(1.234, obscuration=0.606, rng=bd).applyTo(photon_array) photon_array2.x[:] = photon_array.x photon_array2.y[:] = photon_array.y photon_array2.dxdz[:] = photon_array.dxdz photon_array2.dydz[:] = photon_array.dydz depth = ud() - 0.5 galsim.FocusDepth(depth).applyTo(photon_array2) np.testing.assert_allclose( (photon_array2.x - photon_array.x) / photon_array.dxdz, depth) np.testing.assert_allclose( (photon_array2.y - photon_array.y) / photon_array.dydz, depth) np.testing.assert_allclose(photon_array.dxdz, photon_array2.dxdz) np.testing.assert_allclose(photon_array.dydz, photon_array2.dydz)