def getPsf(fwhm, psfFile='psfFake.fits'): """Make a PSF image, and pass the PSF object. @param fwhm: FWHM of the PSF in pixel """ logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("psf") nx, ny = np.floor(fwhm * 10), np.floor(fwhm * 10) scale = 1.0 logger.info('PSF: FWHM=%4.1f (%d, %d)', fwhm, nx, ny) # Make a PSF Image psfImg = galsim.ImageF(nx, ny) psfCen = psfImg.bounds.trueCenter() psfWcs = galsim.OffsetWCS(scale=scale, origin=psfCen) psfImg.wcs = psfWcs # PSF model psf = galsim.Gaussian(fwhm=fwhm) # Draw PSF image psf.drawImage(psfImg, method='no_pixel') # Save the fits image galsim.fits.write(psfImg, psfFile) logger.info('Write the PSF to %s', psfFile) return psf
def main(argv): """ Make images using variable PSF and shear: - The main image is 10 x 10 postage stamps. - Each postage stamp is 48 x 48 pixels. - The second HDU has the corresponding PSF image. - Applied shear is from a power spectrum P(k) ~ k^1.8. - Galaxies are real galaxies oriented in a ring test of 20 each. - The PSF is Gaussian with FWHM, ellipticity and position angle functions of (x,y) - Noise is Poisson using a nominal sky value of 1.e6. """ logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("demo10") # Define some parameters we'll use below. # Normally these would be read in from some parameter file. n_tiles = 10 # number of tiles in each direction. stamp_size = 48 # pixels pixel_scale = 0.44 # arcsec / pixel sky_level = 1.e6 # ADU / arcsec^2 # The random seed is used for both the power spectrum realization and the random properties # of the galaxies. random_seed = 3339201 # Make output directory if not already present. if not os.path.isdir('output'): os.mkdir('output') file_name = os.path.join('output', 'power_spectrum.fits') # These will be created for each object below. The values we'll use will be functions # of (x,y) relative to the center of the image. (r = sqrt(x^2+y^2)) # psf_fwhm = 0.9 + 0.5 * (r/100)^2 -- arcsec # psf_e = 0.4 * (r/100)^1.5 -- large value at the edge, so visible by eye. # psf_beta = atan2(y/x) + pi/2 -- tangential pattern gal_dilation = 3 # Make the galaxies a bit larger than their original size. gal_signal_to_noise = 100 # Pretty high. psf_signal_to_noise = 1000 # Even higher. logger.info('Starting demo script 10') # Read in galaxy catalog cat_file_name = 'real_galaxy_catalog_23.5_example.fits' dir = 'data' real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=dir) logger.info('Read in %d real galaxies from catalog', real_galaxy_catalog.nobjects) # List of IDs to use. We select 5 particularly irregular galaxies for this demo. # Then we'll choose randomly from this list. id_list = [106416, 106731, 108402, 116045, 116448] # Make the 5 galaxies we're going to use here rather than remake them each time. # This means the Fourier transforms of the real galaxy images don't need to be recalculated # each time, so it's a bit more efficient. gal_list = [ galsim.RealGalaxy(real_galaxy_catalog, id=id) for id in id_list ] # Grab the index numbers before we transform them and lose the index attribute. cosmos_index = [gal.index for gal in gal_list] # Make the galaxies a bit larger than their original observed size. gal_list = [gal.dilate(gal_dilation) for gal in gal_list] # Setup the PowerSpectrum object we'll be using: ps = galsim.PowerSpectrum(lambda k: k**1.8) # The argument here is "e_power_function" which defines the E-mode power to use. # There is also a b_power_function if you want to include any B-mode power: # ps = galsim.PowerSpectrum(e_power_function, b_power_function) # You may even omit the e_power_function argument and have a pure B-mode power spectrum. # ps = galsim.PowerSpectrum(b_power_function = b_power_function) # All the random number generator classes derive from BaseDeviate. # When we construct another kind of deviate class from any other # kind of deviate class, the two share the same underlying random number # generator. Sometimes it can be clearer to just construct a BaseDeviate # explicitly and then construct anything else you need from that. # Note: A BaseDeviate cannot be used to generate any values. It can # only be used in the constructor for other kinds of deviates. # The seeds for the objects are random_seed+1..random_seed+nobj. # The seeds for things at the image or file level use random_seed itself. nobj = n_tiles * n_tiles rng = galsim.BaseDeviate(random_seed) # Have the PowerSpectrum object build a grid of shear values for us to use. grid_g1, grid_g2 = ps.buildGrid(grid_spacing=stamp_size * pixel_scale, ngrid=n_tiles, rng=rng) # Setup the images: gal_image = galsim.ImageF(stamp_size * n_tiles, stamp_size * n_tiles) psf_image = galsim.ImageF(stamp_size * n_tiles, stamp_size * n_tiles) # Update the image WCS to use the image center as the origin of the WCS. # The class that acts like a PixelScale except for this offset is called OffsetWCS. im_center = gal_image.true_center wcs = galsim.OffsetWCS(scale=pixel_scale, origin=im_center) gal_image.wcs = wcs psf_image.wcs = wcs # We will place the tiles in a random order. To do this, we make two lists for the # ix and iy values. Then we apply a random permutation to the lists (in tandem). ix_list = [] iy_list = [] for ix in range(n_tiles): for iy in range(n_tiles): ix_list.append(ix) iy_list.append(iy) # This next function will use the given random number generator, rng, and use it to # randomly permute any number of lists. All lists will have the same random permutation # applied. galsim.random.permute(rng, ix_list, iy_list) # Initialize the OutputCatalog for the truth values names = [ 'gal_num', 'x_image', 'y_image', 'psf_e1', 'psf_e2', 'psf_fwhm', 'cosmos_id', 'cosmos_index', 'theta', 'g1', 'g2', 'shift_x', 'shift_y' ] types = [ int, float, float, float, float, float, str, int, float, float, float, float, float ] truth_catalog = galsim.OutputCatalog(names, types) # Build each postage stamp: for k in range(nobj): # The usual random number generator using a different seed for each galaxy. rng = galsim.BaseDeviate(random_seed + k + 1) # Determine the bounds for this stamp and its center position. ix = ix_list[k] iy = iy_list[k] b = galsim.BoundsI(ix * stamp_size + 1, (ix + 1) * stamp_size, iy * stamp_size + 1, (iy + 1) * stamp_size) sub_gal_image = gal_image[b] sub_psf_image = psf_image[b] pos = wcs.toWorld(b.true_center) # The image comes out as about 211 arcsec across, so we define our variable # parameters in terms of (r/100 arcsec), so roughly the scale size of the image. rsq = (pos.x**2 + pos.y**2) r = math.sqrt(rsq) psf_fwhm = 0.9 + 0.5 * rsq / 100**2 # arcsec psf_e = 0.4 * (r / 100.)**1.5 psf_beta = (math.atan2(pos.y, pos.x) + math.pi / 2) * galsim.radians # Define the PSF profile psf = galsim.Gaussian(fwhm=psf_fwhm) psf_shape = galsim.Shear(e=psf_e, beta=psf_beta) psf = psf.shear(psf_shape) # Define the galaxy profile: # For this demo, we are doing a ring test where the same galaxy profile is drawn at many # orientations stepped uniformly in angle, making a ring in e1-e2 space. # We're drawing each profile at 20 different orientations and then skipping to the # next galaxy in the list. So theta steps by 1/20 * 360 degrees: theta_deg = (k % 20) * 360. / 20 theta = theta_deg * galsim.degrees # The index needs to increment every 20 objects so we use k/20 using integer math. index = k // 20 gal = gal_list[index] # This makes a new copy so we're not changing the object in the gal_list. gal = gal.rotate(theta) # Apply the shear from the power spectrum. We should either turn the gridded shears # grid_g1[iy, ix] and grid_g2[iy, ix] into gridded reduced shears using a utility called # galsim.lensing.theoryToObserved, or use ps.getShear() which by default gets the reduced # shear. ps.getShear() is also more flexible because it can get the shear at positions that # are not on the original grid, as long as they are contained within the bounds of the full # grid. So in this example we'll use ps.getShear(). alt_g1, alt_g2 = ps.getShear(pos) gal = gal.shear(g1=alt_g1, g2=alt_g2) # Apply half-pixel shift in a random direction. shift_r = pixel_scale * 0.5 ud = galsim.UniformDeviate(rng) t = ud() * 2. * math.pi dx = shift_r * math.cos(t) dy = shift_r * math.sin(t) gal = gal.shift(dx, dy) # Make the final image, convolving with the psf final = galsim.Convolve([psf, gal]) # Draw the image final.drawImage(sub_gal_image) # For the PSF image, we don't match the galaxy shift. Rather, we use the offset # parameter to drawImage to apply a random offset of up to 0.5 pixels in each direction. # Note the difference in units between shift and offset. The shift is applied to the # surface brightness profile, so it is in sky coordinates (as all dimension are for # GSObjects), which are arcsec here. The offset though is applied to the image itself, # so it is in pixels. Hence, we don't multiply by pixel_scale. psf_dx = ud() - 0.5 psf_dy = ud() - 0.5 psf_offset = galsim.PositionD(psf_dx, psf_dy) # Draw the PSF image: # We use real space integration over the pixels to avoid some of the # artifacts that can show up with Fourier convolution. # The level of the artifacts is quite low, but when drawing with # so little noise, they are apparent with ds9's zscale viewing. psf.drawImage(sub_psf_image, method='real_space', offset=psf_offset) # Build the noise model: Poisson noise with a given sky level. sky_level_pixel = sky_level * pixel_scale**2 noise = galsim.PoissonNoise(rng, sky_level=sky_level_pixel) # Add noise to the PSF image, using the normal noise model, but scaling the # PSF flux high enough to reach the desired signal-to-noise. # See demo5.py for more info about how this works. sub_psf_image.addNoiseSNR(noise, psf_signal_to_noise) # And also to the galaxy image using its signal-to-noise. sub_gal_image.addNoiseSNR(noise, gal_signal_to_noise) # Add the truth values to the truth catalog row = [ k, b.true_center.x, b.true_center.y, psf_shape.e1, psf_shape.e2, psf_fwhm, id_list[index], cosmos_index[index], (theta_deg % 360.), alt_g1, alt_g2, dx, dy ] truth_catalog.addRow(row) logger.info('Galaxy (%d,%d): position relative to center = %s', ix, iy, str(pos)) logger.info('Done making images of postage stamps') # In this case, we'll attach the truth catalog as an additional HDU in the same file as # the image data. truth_hdu = truth_catalog.writeFitsHdu() # Now write the images to disk. images = [gal_image, psf_image, truth_hdu] # Any items in the "images" list that is already an hdu is just used directly. # The actual images are converted to FITS hdus that contain the image data. galsim.fits.writeMulti(images, file_name) logger.info('Wrote image to %r', file_name)
def main(argv): """ Make images using variable PSF and shear: - The main image is 10 x 10 postage stamps. - Each postage stamp is 48 x 48 pixels. - The second HDU has the corresponding PSF image. - Applied shear is from a power spectrum P(k) ~ k^1.8. - Galaxies are real galaxies oriented in a ring test of 20 each. - The PSF is Gaussian with FWHM, ellipticity and position angle functions of (x,y) - Noise is Poisson using a nominal sky value of 1.e6. """ logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("demo10") # Define some parameters we'll use below. # Normally these would be read in from some parameter file. n_tiles = 10 # number of tiles in each direction. stamp_size = 48 # pixels pixel_scale = 0.44 # arcsec / pixel sky_level = 1.e6 # ADU / arcsec^2 # The random seed is used for both the power spectrum realization and the random properties # of the galaxies. random_seed = 3339201 # Make output directory if not already present. if not os.path.isdir('output'): os.mkdir('output') file_name = os.path.join('output', 'power_spectrum.fits') # These will be created for each object below. The values we'll use will be functions # of (x,y) relative to the center of the image. (r = sqrt(x^2+y^2)) # psf_fwhm = 0.9 + 0.5 * (r/100)^2 -- arcsec # psf_e = 0.4 * (r/100)^1.5 -- large value at the edge, so visible by eye. # psf_beta = atan2(y/x) + pi/2 -- tangential pattern gal_dilation = 3 # Make the galaxies a bit larger than their original size. gal_signal_to_noise = 100 # Pretty high. psf_signal_to_noise = 1000 # Even higher. logger.info('Starting demo script 10') # Read in galaxy catalog cat_file_name = 'real_galaxy_catalog_example.fits' dir = 'data' real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=dir) logger.info('Read in %d real galaxies from catalog', real_galaxy_catalog.nobjects) # List of IDs to use. We select 5 particularly irregular galaxies for this demo. # Then we'll choose randomly from this list. id_list = [106416, 106731, 108402, 116045, 116448] # Make the 5 galaxies we're going to use here rather than remake them each time. # This means the Fourier transforms of the real galaxy images don't need to be recalculated # each time, so it's a bit more efficient. gal_list = [ galsim.RealGalaxy(real_galaxy_catalog, id=id) for id in id_list ] # Make the galaxies a bit larger than their original observed size. gal_list = [gal.dilate(gal_dilation) for gal in gal_list] # Setup the PowerSpectrum object we'll be using: ps = galsim.PowerSpectrum(lambda k: k**1.8) # The argument here is "e_power_function" which defines the E-mode power to use. # There is also a b_power_function if you want to include any B-mode power: # ps = galsim.PowerSpectrum(e_power_function, b_power_function) # You may even omit the e_power_function argument and have a pure B-mode power spectrum. # ps = galsim.PowerSpectrum(b_power_function = b_power_function) # All the random number generator classes derive from BaseDeviate. # When we construct another kind of deviate class from any other # kind of deviate class, the two share the same underlying random number # generator. Sometimes it can be clearer to just construct a BaseDeviate # explicitly and then construct anything else you need from that. # Note: A BaseDeviate cannot be used to generate any values. It can # only be used in the constructor for other kinds of deviates. # The seeds for the objects are random_seed..random_seed+nobj-1 (which comes later), # so use the next one. nobj = n_tiles * n_tiles rng = galsim.BaseDeviate(random_seed + nobj) # Setup the images: gal_image = galsim.ImageF(stamp_size * n_tiles, stamp_size * n_tiles) psf_image = galsim.ImageF(stamp_size * n_tiles, stamp_size * n_tiles) # Update the image WCS to use the image center as the origin of the WCS. # The class that acts like a PixelScale except for this offset is called OffsetWCS. im_center = gal_image.bounds.trueCenter() wcs = galsim.OffsetWCS(scale=pixel_scale, origin=im_center) gal_image.wcs = wcs psf_image.wcs = wcs # We will place the tiles in a random order. To do this, we make two lists for the # ix and iy values. Then we apply a random permutation to the lists (in tandem). ix_list = [] iy_list = [] for ix in range(n_tiles): for iy in range(n_tiles): ix_list.append(ix) iy_list.append(iy) # This next function will use the given random number generator, rng, and use it to # randomly permute any number of lists. All lists will have the same random permutation # applied. galsim.random.permute(rng, ix_list, iy_list) # Now have the PowerSpectrum object build a grid of shear values for us to use. # Also, because of some technical details about how the config stuff handles the random # number generator here, we need to duplicate the rng object if we want to have the # two output files match. This means that technically, the same sequence of random numbers # will be used in building the grid as will be used by the other uses of rng (permuting the # postage stamps and adding noise). But since they are used in such completely different # ways, it is hard to imagine how this could lead to any kind of bias in the images. grid_g1, grid_g2 = ps.buildGrid(grid_spacing=stamp_size * pixel_scale, ngrid=n_tiles, rng=rng.duplicate()) # Build each postage stamp: for k in range(nobj): # The usual random number generator using a different seed for each galaxy. rng = galsim.BaseDeviate(random_seed + k) # Determine the bounds for this stamp and its center position. ix = ix_list[k] iy = iy_list[k] b = galsim.BoundsI(ix * stamp_size + 1, (ix + 1) * stamp_size, iy * stamp_size + 1, (iy + 1) * stamp_size) sub_gal_image = gal_image[b] sub_psf_image = psf_image[b] pos = wcs.toWorld(b.trueCenter()) # The image comes out as about 211 arcsec across, so we define our variable # parameters in terms of (r/100 arcsec), so roughly the scale size of the image. r = math.sqrt(pos.x**2 + pos.y**2) / 100 psf_fwhm = 0.9 + 0.5 * r**2 # arcsec psf_e = 0.4 * r**1.5 psf_beta = (math.atan2(pos.y, pos.x) + math.pi / 2) * galsim.radians # Define the PSF profile psf = galsim.Gaussian(fwhm=psf_fwhm) psf = psf.shear(e=psf_e, beta=psf_beta) # Define the galaxy profile: # For this demo, we are doing a ring test where the same galaxy profile is drawn at many # orientations stepped uniformly in angle, making a ring in e1-e2 space. # We're drawing each profile at 20 different orientations and then skipping to the # next galaxy in the list. So theta steps by 1/20 * 360 degrees: theta = k / 20. * 360. * galsim.degrees # The index needs to increment every 20 objects so we use k/20 using integer math. index = k / 20 gal = gal_list[index] # This makes a new copy so we're not changing the object in the gal_list. gal = gal.rotate(theta) # Apply the shear from the power spectrum. We should either turn the gridded shears # grid_g1[iy, ix] and grid_g2[iy, ix] into gridded reduced shears using a utility called # galsim.lensing.theoryToObserved, or use ps.getShear() which by default gets the reduced # shear. ps.getShear() is also more flexible because it can get the shear at positions that # are not on the original grid, as long as they are contained within the bounds of the full # grid. So in this example we'll use ps.getShear(). alt_g1, alt_g2 = ps.getShear(pos) gal = gal.shear(g1=alt_g1, g2=alt_g2) # Apply half-pixel shift in a random direction. shift_r = pixel_scale * 0.5 ud = galsim.UniformDeviate(rng) theta = ud() * 2. * math.pi dx = shift_r * math.cos(theta) dy = shift_r * math.sin(theta) gal = gal.shift(dx, dy) # Make the final image, convolving with the psf final = galsim.Convolve([psf, gal]) # Draw the image final.drawImage(sub_gal_image) # Now add noise to get our desired S/N # See demo5.py for more info about how this works. sky_level_pixel = sky_level * pixel_scale**2 noise = galsim.PoissonNoise(rng, sky_level=sky_level_pixel) sub_gal_image.addNoiseSNR(noise, gal_signal_to_noise) # For the PSF image, we also shift the PSF by the same amount. psf = psf.shift(dx, dy) # Draw the PSF image: # We use real space integration over the pixels to avoid some of the # artifacts that can show up with Fourier convolution. # The level of the artifacts is quite low, but when drawing with # so little noise, they are apparent with ds9's zscale viewing. psf.drawImage(sub_psf_image, method='real_space') # Again, add noise, but at higher S/N this time. sub_psf_image.addNoiseSNR(noise, psf_signal_to_noise) logger.info('Galaxy (%d,%d): position relative to center = %s', ix, iy, str(pos)) logger.info('Done making images of postage stamps') # Now write the images to disk. images = [gal_image, psf_image] galsim.fits.writeMulti(images, file_name) logger.info('Wrote image to %r', file_name)
def galaxy2(sky=0.15, galSNR=6000.0, fwhm=7.0): """Make the second fake galaxy. @param sky: Per pixel sky value, used to get Poisson noise (default=0.2) @param galSNR: The SNR of the galaxy, used to get the noise (default=5000.0) @param fwhm: FWHM of the Gaussian PSF used to convolve the image (default=6.0) ------ Three component edge-on disk galaxy with bulge+disk+halo : Component 1: xCen=420.0, yCen=380; FLux=300; Re=8.0; n=3.0, q=0.8, PA1=45.0 Component 2: xCen=420.0, yCen=380; FLux=500; Re=40.0; n=0.8, q=0.1, PA1=45.0 Component 3: xCen=420.0, yCen=380; FLux=600; Re=35.0; n=1.5, q=0.6, PA1=40.0 Contamination 1: FLux=100; Re=20.0; n=2.5, q=0.7, PA1=0.0 Contamination 2: FLux=200; Re=30.0; n=2.0, q=0.6, PA1=75.0 """ logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("galaxy2") # Basic information of the image nx, ny = 800, 800 exptime = 100.0 scale = 1.0 logger.info('Galaxy2: Sky=%6.2f, SNR=%5d', sky, galSNR) # Get the PSF, and save the PSF image psf = getPsf(fwhm, psfFile='galaxy2_psf.fits') # Make a GalSim Image gal2Img = galsim.ImageF(nx, ny) imgCen = gal2Img.bounds.trueCenter() imgWcs = galsim.OffsetWCS(scale=scale, origin=imgCen) gal2Img.wcs = imgWcs # Component 1: flux1 = 300; reff1 = 8.0; nser1 = 3.0; q1 = 0.8; pa1 = 45.0 comp1 = galsim.Sersic( n=3.0, half_light_radius=8.0, flux=(300.0 * exptime)).shear( q=0.8, beta=(0.0 * galsim.degrees)).rotate(45.0 * galsim.degrees).shift( (420.0 - (nx / 2.0)), (380.0 - (ny / 2.0))) # Component 1: flux1 = 500; reff1 = 40.0; nser1 = 0.8; q1 = 0.1; pa1 = 45.0 comp2 = galsim.Sersic(n=0.8, half_light_radius=40.0, flux=(500.0 * exptime)).shear( q=0.10, beta=(0.0 * galsim.degrees)).rotate( 45.0 * galsim.degrees).shift( (420.0 - (nx / 2.0)), (380.0 - (ny / 2.0))) # Component 3: flux1 = 600; reff1 = 35.0; nser1 = 1.5; q1 = 0.6; pa1 = 40.0 comp3 = galsim.Sersic(n=1.5, half_light_radius=35.0, flux=(600.0 * exptime)).shear( q=0.6, beta=(0.0 * galsim.degrees)).rotate( 40.0 * galsim.degrees).shift( (420.0 - (nx / 2.0)), (380.0 - (ny / 2.0))) # Contamination 1: cont1 = galsim.Sersic(n=2.5, half_light_radius=20.0, flux=(100.0 * exptime)).shear( q=0.7, beta=(0.0 * galsim.degrees)).rotate( 0.0 * galsim.degrees).shift(50, 200) # Contamination 2: cont2 = galsim.Sersic(n=2.0, half_light_radius=30.0, flux=(200.0 * exptime)).shear( q=0.6, beta=(0.0 * galsim.degrees)).rotate( 75.0 * galsim.degrees).shift(-300, -190) # Add all components together gal2 = galsim.Add([comp1, comp2, comp3, cont1, cont2]) # Convolution gal2Conv = galsim.Convolve([psf, gal2]) # Draw the image gal2Conv.drawImage(gal2Img, method='no_pixel') # Add Noise rng = random.seed(datetime.now()) noise = galsim.PoissonNoise(rng, sky_level=sky) gal2Img.addNoiseSNR(noise, galSNR) # Save the FITS file gal2File = 'galaxy2_img.fits' logger.info('Write to FITS file : %s', gal2File) galsim.fits.write(gal2Img, gal2File) # Save the PNG picture savePng(gal2Img, pngFile='galaxy2_img.png') return gal2Img
def test_withOrigin(): from test_wcs import Cubic # First EuclideantWCS types: wcs_list = [ galsim.OffsetWCS(0.3, galsim.PositionD(1, 1), galsim.PositionD(10, 23)), galsim.OffsetShearWCS(0.23, galsim.Shear(g1=0.1, g2=0.3), galsim.PositionD(12, 43)), galsim.AffineTransform(0.01, 0.26, -0.26, 0.02, galsim.PositionD(12, 43)), galsim.UVFunction(ufunc=lambda x, y: 0.2 * x, vfunc=lambda x, y: 0.2 * y), galsim.UVFunction(ufunc=lambda x, y: 0.2 * x, vfunc=lambda x, y: 0.2 * y, xfunc=lambda u, v: u / scale, yfunc=lambda u, v: v / scale), galsim.UVFunction(ufunc='0.2*x + 0.03*y', vfunc='0.01*x + 0.2*y'), ] color = 0.3 for wcs in wcs_list: # Original version of the shiftOrigin tests in do_nonlocal_wcs using deprecated name. new_origin = galsim.PositionI(123, 321) wcs3 = check_dep(wcs.withOrigin, new_origin) assert wcs != wcs3, name + ' is not != wcs.withOrigin(pos)' wcs4 = wcs.local(wcs.origin, color=color) assert wcs != wcs4, name + ' is not != wcs.local()' assert wcs4 != wcs, name + ' is not != wcs.local() (reverse)' world_origin = wcs.toWorld(wcs.origin, color=color) if wcs.isUniform(): if wcs.world_origin == galsim.PositionD(0, 0): wcs2 = wcs.local(wcs.origin, color=color).withOrigin(wcs.origin) assert wcs == wcs2, name + ' is not equal after wcs.local().withOrigin(origin)' wcs2 = wcs.local(wcs.origin, color=color).withOrigin(wcs.origin, wcs.world_origin) assert wcs == wcs2, name + ' not equal after wcs.local().withOrigin(origin,world_origin)' world_pos1 = wcs.toWorld(galsim.PositionD(0, 0), color=color) wcs3 = check_dep(wcs.withOrigin, new_origin) world_pos2 = wcs3.toWorld(new_origin, color=color) np.testing.assert_almost_equal( world_pos2.x, world_pos1.x, 7, 'withOrigin(new_origin) returned wrong world position') np.testing.assert_almost_equal( world_pos2.y, world_pos1.y, 7, 'withOrigin(new_origin) returned wrong world position') new_world_origin = galsim.PositionD(5352.7, 9234.3) wcs5 = check_dep(wcs.withOrigin, new_origin, new_world_origin, color=color) world_pos3 = wcs5.toWorld(new_origin, color=color) np.testing.assert_almost_equal( world_pos3.x, new_world_origin.x, 7, 'withOrigin(new_origin, new_world_origin) returned wrong position') np.testing.assert_almost_equal( world_pos3.y, new_world_origin.y, 7, 'withOrigin(new_origin, new_world_origin) returned wrong position') # Now some CelestialWCS types cubic_u = Cubic(2.9e-5, 2000., 'u') cubic_v = Cubic(-3.7e-5, 2000., 'v') center = galsim.CelestialCoord(23 * galsim.degrees, -13 * galsim.degrees) radec = lambda x, y: center.deproject_rad( cubic_u(x, y) * 0.2, cubic_v(x, y) * 0.2, projection='lambert') wcs_list = [ galsim.RaDecFunction(radec), galsim.AstropyWCS('1904-66_TAN.fits', dir='fits_files'), galsim.GSFitsWCS('tpv.fits', dir='fits_files'), galsim.FitsWCS('sipsample.fits', dir='fits_files'), ] for wcs in wcs_list: # Original version of the shiftOrigin tests in do_celestial_wcs using deprecated name. new_origin = galsim.PositionI(123, 321) wcs3 = wcs.shiftOrigin(new_origin) assert wcs != wcs3, name + ' is not != wcs.shiftOrigin(pos)' wcs4 = wcs.local(wcs.origin) assert wcs != wcs4, name + ' is not != wcs.local()' assert wcs4 != wcs, name + ' is not != wcs.local() (reverse)' world_pos1 = wcs.toWorld(galsim.PositionD(0, 0)) wcs3 = wcs.shiftOrigin(new_origin) world_pos2 = wcs3.toWorld(new_origin) np.testing.assert_almost_equal( world_pos2.distanceTo(world_pos1) / galsim.arcsec, 0, 7, 'shiftOrigin(new_origin) returned wrong world position')