def _set_wcs_choose(self, jacobian, **kw): """ create a galsim JacobianWCS from the input ngmix.Jacobian, as well as pixel objects """ self.jacobian=jacobian wcs_convention=kw.get("wcs_convention",None) logger.debug(" wcs convention: %s" % wcs_convention) if wcs_convention==1: self.gs_wcs = galsim.JacobianWCS(jacobian.dudrow, jacobian.dudcol, jacobian.dvdrow, jacobian.dvdcol) elif wcs_convention==2: self.gs_wcs = galsim.JacobianWCS(jacobian.dudcol, jacobian.dudrow, jacobian.dvdcol, jacobian.dvdrow) elif wcs_convention==3: self.gs_wcs = galsim.JacobianWCS(jacobian.dvdcol, jacobian.dvdrow, jacobian.dudcol, jacobian.dudrow) else: raise ValueError("bad wcs_convention: %s" % wcs_convention)
def exposures(): # Obtain observation list, psf list, weight from wfirst_simple_sim.py gals_array=[fio.FITS('mcal_gal_0.fits')[-1].read(), fio.FITS('mcal_gal_1.fits')[-1].read()] psfs_array=[fio.FITS('mcal_psf_0.fits')[-1].read(), fio.FITS('mcal_psf_1.fits')[-1].read()] offsets=[galsim.PositionD(-0.5,-0.5), galsim.PositionD(0.48010974293083564,0.24526554339263384)] skys_array=[fio.FITS('mcal_sky_0.fits')[-1].read(), fio.FITS('mcal_sky_1.fits')[-1].read()] gal_true=[galsim.PositionD(31.5,31.5), galsim.PositionD(31.5,31.5)] gal_jacobs=[galsim.JacobianWCS(0.10379201786865774, -0.037313406917181026, 0.03741492083530528, 0.1017841347351108), galsim.JacobianWCS(0.04693577579580337, -0.09835662875798407, 0.09984826919988712, 0.045587756760917426)] obs_list, psf_list, w = get_exp_list(gals_array, psfs_array, offsets, skys_array, gal_true, gal_jacobs, psf2=None) return obs_list,psf_list,np.array(w)
def construct_image(self, band_index, uniform_deviate): world_origin = self.image_parameters.world_origin.as_galsim_position() degrees_per_pixel = self.image_parameters.degrees_per_pixel() wcs = ( # Here we implement the confusing mapping X <-> Dec, Y <-> RA galsim.JacobianWCS(0, degrees_per_pixel, degrees_per_pixel, 0).withOrigin(galsim.PositionI(0, 0), world_origin=world_origin)) image = galsim.ImageF( self.image_parameters.width_px, self.image_parameters.height_px, wcs=wcs, ) for index, light_source in enumerate(self._light_sources): sys.stdout.write('Band {} source {}\r'.format( band_index + 1, index + 1)) sys.stdout.flush() galsim_light_source = light_source.get_galsim_light_source( band_index, self.psf_sigma_pixels * self.image_parameters.degrees_per_pixel(), self.image_parameters, ) galsim_light_source.drawImage( image, add_to_image=True, method='phot', max_extra_noise=self.band_sky_level_nmgy[band_index] * self.image_parameters.band_nelec_per_nmgy[band_index] / 1000.0, rng=uniform_deviate, ) self._add_sky_background(image, band_index, uniform_deviate) return image
def test_wcs(): """Reproduce an error Erin found and reported in #834. This was never an error in a released version, just temporarily on master, but the mistake hadn't been caught by any of our unit tests, so this test catches the error. """ wcs = galsim.JacobianWCS(0.01, -0.26, -0.28, -0.03) gal = galsim.Exponential(half_light_radius=1.1, flux=237) psf = galsim.Moffat(beta=3.5, half_light_radius=0.9) obs = galsim.Convolve(gal, psf) obs_im = obs.drawImage(nx=32, ny=32, offset=(0.3, -0.2), wcs=wcs) psf_im = psf.drawImage(nx=32, ny=32, wcs=wcs) ii = galsim.InterpolatedImage(obs_im) psf_ii = galsim.InterpolatedImage(psf_im) psf_inv = galsim.Deconvolve(psf_ii) ii_nopsf = galsim.Convolve(ii, psf_inv) newpsf = galsim.Moffat(beta=3.5, half_light_radius=0.95) newpsf = newpsf.dilate(1.02) new_ii = ii_nopsf.shear(g1=0.01, g2=0.0) new_ii = galsim.Convolve(new_ii, newpsf) new_im = new_ii.drawImage(image=obs_im.copy(), method='no_pixel') np.testing.assert_almost_equal(new_im.array.sum() / 237, obs_im.array.sum() / 237, decimal=1)
def __str__(self): s = str(self.original) dudx, dudy, dvdx, dvdy = self.jac.flatten() if dudx != 1 or dudy != 0 or dvdx != 0 or dvdy != 1: # Figure out the shear/rotate/dilate calls that are equivalent. jac = galsim.JacobianWCS(dudx, dudy, dvdx, dvdy) scale, shear, theta, flip = jac.getDecomposition() single = None if flip: single = 0 # Special value indicating to just use transform. if abs(theta.rad()) > 1.e-12: if single is None: single = '.rotate(%s)' % theta else: single = 0 if shear.getG() > 1.e-12: if single is None: single = '.shear(%s)' % shear else: single = 0 if abs(scale - 1.0) > 1.e-12: if single is None: single = '.expand(%s)' % scale else: single = 0 if single == 0: # If flip or there are two components, then revert to transform as simpler. single = '.transform(%s,%s,%s,%s)' % (dudx, dudy, dvdx, dvdy) s += single if self.offset.x != 0 or self.offset.y != 0: s += '.shift(%s,%s)' % (self.offset.x, self.offset.y) if self.flux_ratio != 1.: #s += '.withScaledFlux(%s)'%self.flux_ratio s += ' * %s' % self.flux_ratio return s
def test_decam_wavefront(): file_name = 'input/Science-20121120s1-v20i2.fits' extname = 'Science-20121120s1-v20i2' if __name__ == '__main__': logger = piff.config.setup_logger(verbose=2) else: logger = piff.config.setup_logger(log_file='output/test_decam.log') knn = piff.des.DECamWavefront(file_name, extname, logger=logger) n_samples = 2000 np_rng = np.random.RandomState(1234) ccdnums = np_rng.randint(1, 63, n_samples) star_list = [] for ccdnum in ccdnums: # make some basic images, pass Xi as properties # Draw the PSF onto an image. Let's go ahead and give it a non-trivial WCS. wcs = galsim.JacobianWCS(0.26, 0.05, -0.08, -0.29) image = galsim.Image(64,64, wcs=wcs) # set icen and jcen icen = np_rng.randint(100, 2048) jcen = np_rng.randint(100, 4096) image.setCenter(icen, jcen) image_pos = image.center stardata = piff.StarData(image, image_pos, properties={'chipnum': ccdnum}) star = piff.Star(stardata, None) star_list.append(star) # get the focal positions star_list = piff.des.DECamInfo().pixel_to_focalList(star_list) star_list_predicted = knn.interpolateList(star_list) # test misalignment misalignment = {'z04d': 10, 'z10x': 10, 'z09y': -10} knn.misalign_wavefront(misalignment) star_list_misaligned = knn.interpolateList(star_list) # test the prediction algorithm y_predicted = np.array([s.fit.params for s in star_list_predicted]) y_misaligned = np.array([s.fit.params for s in star_list_misaligned]) X = np.array([knn.getProperties(s) for s in star_list]) # check the misalignments work np.testing.assert_array_almost_equal(y_predicted[:,0], y_misaligned[:,0] - misalignment['z04d']) np.testing.assert_array_almost_equal(y_predicted[:,5], y_misaligned[:,5] - misalignment['z09y'] * X[:,0]) np.testing.assert_array_almost_equal(y_predicted[:,6], y_misaligned[:,6] - misalignment['z10x'] * X[:,1]) # Check shape of misalignment if array np.testing.assert_raises(ValueError, knn.misalign_wavefront, knn.misalignment[:,:2]) np.testing.assert_raises(ValueError, knn.misalign_wavefront, knn.misalignment[:-1,:]) # empty dict is equivalent to no misalignment knn.misalign_wavefront({}) np.testing.assert_equal(knn.misalignment, 0.)
def test_Gaussian(): """This is about the simplest possible model I could think of. It just uses the HSM adaptive moments routine to measure the moments, and then it models the PSF as a Gaussian. """ # Here is the true PSF sigma = 1.3 g1 = 0.23 g2 = -0.17 psf = galsim.Gaussian(sigma=sigma).shear(g1=g1, g2=g2) # Draw the PSF onto an image. Let's go ahead and give it a non-trivial WCS. wcs = galsim.JacobianWCS(0.26, 0.05, -0.08, -0.29) image = galsim.Image(64,64, wcs=wcs) # This is only going to come out right if we (unphysically) don't convolve by the pixel. psf.drawImage(image, method='no_pixel') # Make a StarData instance for this image stardata = piff.StarData(image, image.true_center) star = piff.Star(stardata, None) # Fit the model from the image model = piff.Gaussian(include_pixel=False) star = model.initialize(star) fit = model.fit(star).fit print('True sigma = ',sigma,', model sigma = ',fit.params[0]) print('True g1 = ',g1,', model g1 = ',fit.params[1]) print('True g2 = ',g2,', model g2 = ',fit.params[2]) # This test is pretty accurate, since we didn't add any noise and didn't convolve by # the pixel, so the image is very accurately a sheared Gaussian. true_params = [ sigma, g1, g2 ] np.testing.assert_almost_equal(fit.params[0], sigma, decimal=7) np.testing.assert_almost_equal(fit.params[1], g1, decimal=7) np.testing.assert_almost_equal(fit.params[2], g2, decimal=7) np.testing.assert_almost_equal(fit.params, true_params, decimal=7) # Now test running it via the config parser config = { 'model' : { 'type' : 'Gaussian', 'include_pixel': False } } if __name__ == '__main__': logger = piff.config.setup_logger(verbose=2) else: logger = piff.config.setup_logger(log_file='output/test_Gaussian.log') model = piff.Model.process(config['model'], logger) fit = model.fit(star).fit # Same tests. np.testing.assert_almost_equal(fit.params[0], sigma, decimal=7) np.testing.assert_almost_equal(fit.params[1], g1, decimal=7) np.testing.assert_almost_equal(fit.params[2], g2, decimal=7) np.testing.assert_almost_equal(fit.params, true_params, decimal=7)
def get_galsim_wcs(self): """ get a galsim.JacobianWCS object with the same contents as self """ import galsim dudx = self.dudcol dudy = self.dudrow dvdx = self.dvdcol dvdy = self.dvdrow return galsim.JacobianWCS(dudx, dudy, dvdx, dvdy)
def test_table_GSInterp(): def f(x_): return x_ + 2 * x_ * x_ + 3 * np.sin(2 * x_) x = np.linspace(0.1, 3.3, 25) y = np.arange( 20 ) # Need something big enough to avoid having the interpolant fall off the edge yy, xx = np.meshgrid(y, x) interpolants = ['lanczos3', 'lanczos3F', 'lanczos7', 'sinc', 'quintic'] for interpolant in interpolants: z = f(xx) tab = galsim.LookupTable(x, z[:, 0], interpolant=interpolant) do_pickle(tab) # Use InterpolatedImage to validate wcs = galsim.JacobianWCS( (max(x) - min(x)) / (len(x) - 1), 0, 0, (max(y) - min(y)) / (len(y) - 1), ) img = galsim.Image(z.T, wcs=wcs) ii = galsim.InterpolatedImage( img, use_true_center=True, offset=(galsim.PositionD(img.xmin, img.ymin) - img.true_center), x_interpolant=interpolant, normalization='sb', calculate_maxk=False, calculate_stepk=False).shift(min(x), min(y)) # Check single value functionality. x1 = 2.3 np.testing.assert_allclose(tab(x1), ii.xValue(x1, 10), atol=1e-10, rtol=0) # Check vectorized output newx = np.linspace(0.2, 3.1, 15) np.testing.assert_allclose(tab(newx), np.array([ii.xValue(x_, 10) for x_ in newx]), atol=1e-10, rtol=0)
def test_extra_interp(): # Test that specifying extra_interp_properties works properly # TODO: This is a very bare bones test of the interface. There is basically no test of # this functionality at all yet. TBD! sigma = 1.3 g1 = 0.23 g2 = -0.17 psf = galsim.Gaussian(sigma=sigma).shear(g1=g1, g2=g2) wcs = galsim.JacobianWCS(0.26, 0.05, -0.08, -0.29) image = galsim.Image(64, 64, wcs=wcs) psf.drawImage(image, method='no_pixel') # use g-i color as an extra property for interpolation. props = dict(gi_color=0.3) print('props = ', props) star = piff.Star(piff.StarData(image, image.true_center, properties=props), None) model = piff.Gaussian(fastfit=True, include_pixel=False) interp = piff.Mean() psf = piff.SimplePSF(model, interp, extra_interp_properties=['gi_color']) assert psf.extra_interp_properties == ['gi_color'] # Note: Mean doesn't actually do anything useful with the extra properties, so this # isn't really testing anything other than that the code doesn't completely break. pointing = galsim.CelestialCoord(-5 * galsim.arcmin, -25 * galsim.degrees) psf.fit([star], wcs={0: wcs}, pointing=pointing) # Not much of a check here. Just check that it actually draws something with flux ~= 1 im = psf.draw(x=5, y=7, gi_color=0.3) np.testing.assert_allclose(im.array.sum(), 1.0, rtol=1.e-3) # Check missing or extra properties with np.testing.assert_raises(TypeError): psf.draw(x=5, y=7) with np.testing.assert_raises(TypeError): psf.draw(x=5, y=7, gi_color=0.3, ri_color=3) # No stars raises an error. (This can happen in practice if all stars are excluded on input.) with np.testing.assert_raises(RuntimeError): psf.fit([], wcs={0: wcs}, pointing=pointing) # Also for SingleChipPSf psf2 = piff.SingleChipPSF(psf, extra_interp_properties=['gi_color']) assert psf2.extra_interp_properties == ['gi_color'] with np.testing.assert_raises(TypeError): psf2.draw(x=5, y=7, chipnum=0)
def wcs_approx(self, gal_stamp, psf_stamp): #self.make_stamp() # set a simple jacobian to the stamps before sending them to ngmix # old center of the stamp origin_x = gal_stamp.origin.x origin_y = gal_stamp.origin.y gal_stamp.setOrigin(0, 0) psf_stamp.setOrigin(0, 0) new_pos = galsim.PositionD(self.xy.x - origin_x, self.xy.y - origin_y) wcs_transf = gal_stamp.wcs.affine(image_pos=new_pos) new_wcs = galsim.JacobianWCS(wcs_transf.dudx, wcs_transf.dudy, wcs_transf.dvdx, wcs_transf.dvdy) gal_stamp.wcs = new_wcs psf_stamp.wcs = new_wcs return gal_stamp, psf_stamp
def constructModelImage(self, params=None, pixelScale=None, jacobian=None, shape=None): """Construct model image from parameters @param params lmfit.Parameters object or python dictionary with param values to use, or None to use self.params @param pixelScale pixel scale in arcseconds to use for model image, or None to use self.pixelScale. @param jacobian An optional 2x2 Jacobian distortion matrix to apply to the forward model. Note that this is relative to the pixelScale above. Use self.jacobian if this is None. @param shape (nx, ny) shape for model image, or None to use the shape of self.maskedImage @returns numpy array image """ if params is None: params = self.params if shape is None: shape = self.maskedImage.getImage().getArray().shape if pixelScale is None: pixelScale = self.pixelScale if jacobian is None: jacobian = self.jacobian try: v = params.valuesdict() except AttributeError: v = params optPsf = self._getOptPsf(v) if 'r0' in v: atmPsf = galsim.Kolmogorov(lam=self.wavelength, r0=v['r0']) psf = galsim.Convolve(optPsf, atmPsf) else: psf = optPsf psf = psf.shift(v['dx'], v['dy']) * v['flux'] wcs = galsim.JacobianWCS(*list(pixelScale * jacobian.ravel())) modelImg = psf.drawImage(nx=shape[0], ny=shape[1], wcs=wcs) return modelImg.array
def generate_data(n_samples=100): # generate as Norm(0, 1) for all parameters np_rng = np.random.RandomState(1234) X = np_rng.normal(0, 1, size=(n_samples, len(keys))) y = np_rng.normal(0, 1, size=(n_samples, ntarget)) star_list = [] for Xi, yi in zip(X, y): wcs = galsim.JacobianWCS(0.26, 0.05, -0.08, -0.29) image = galsim.Image(64,64, wcs=wcs) properties = {k:v for k,v in zip(keys, Xi)} stardata = piff.StarData(image, image.true_center, properties=properties) # params = np.array([yi[ith] for ith in attr_target]) params = yi starfit = piff.StarFit(params) star = piff.Star(stardata, starfit) star_list.append(star) return star_list
def read(cls, fits, extname): """Read stars from a FITS file. :param fits: An open fitsio.FITS object :param extname: The name of the extension to read from :returns: a list of Star instances """ assert extname in fits colnames = fits[extname].get_colnames() for key in [ 'x', 'y', 'u', 'v', 'dudx', 'dudy', 'dvdx', 'dvdy', 'xmin', 'xmax', 'ymin', 'ymax', 'flux', 'center', 'chisq' ]: assert key in colnames colnames.remove(key) data = fits[extname].read() x_list = data['x'] y_list = data['y'] u_list = data['u'] v_list = data['v'] dudx = data['dudx'] dudy = data['dudy'] dvdx = data['dvdx'] dvdy = data['dvdy'] xmin = data['xmin'] xmax = data['xmax'] ymin = data['ymin'] ymax = data['ymax'] flux = data['flux'] center = data['center'] chisq = data['chisq'] if 'params' in colnames: params = data['params'] colnames.remove('params') else: params = [None] * len(data) if 'params_var' in colnames: params_var = data['params_var'] colnames.remove('params_var') else: params_var = [None] * len(data) if 'point_ra' in colnames: pointing_list = [ galsim.CelestialCoord(row['point_ra'] * galsim.degrees, row['point_dec'] * galsim.degrees) for row in data ] colnames.remove('point_ra') colnames.remove('point_dec') else: pointing_list = [None] * len(data) fit_list = [ StarFit(p, flux=f, center=c, chisq=x, params_var=pv) for (p, f, c, x, pv) in zip(params, flux, center, chisq, params_var) ] # The rest of the columns are the data properties prop_list = [{c: row[c] for c in colnames} for row in data] wcs_list = [ galsim.JacobianWCS(*jac) for jac in zip(dudx, dudy, dvdx, dvdy) ] pos_list = [galsim.PositionD(*pos) for pos in zip(x_list, y_list)] wpos_list = [galsim.PositionD(*pos) for pos in zip(u_list, v_list)] wcs_list = [ w.withOrigin(p, wp) for w, p, wp in zip(wcs_list, pos_list, wpos_list) ] bounds_list = [galsim.BoundsI(*b) for b in zip(xmin, xmax, ymin, ymax)] image_list = [ galsim.Image(bounds=b, wcs=w) for b, w in zip(bounds_list, wcs_list) ] weight_list = [ galsim.Image(init_value=1.0, bounds=b, wcs=w) for b, w in zip(bounds_list, wcs_list) ] data_list = [ StarData(im, pos, weight=w, properties=prop, pointing=point) for im, pos, w, prop, point in zip( image_list, pos_list, weight_list, prop_list, pointing_list) ] stars = [Star(d, f) for (d, f) in zip(data_list, fit_list)] return stars
def test_table2d_GSInterp(): def f(x_, y_): return 2 * y_ * y_ + 3 * x_ * x_ + 4 * x_ * y_ - np.cos(x_) x = np.linspace(0.1, 3.3, 25) y = np.linspace(0.2, 10.4, 75) yy, xx = np.meshgrid(y, x) # Note the ordering of both input and output here! interpolants = ['lanczos3', 'lanczos3F', 'lanczos7', 'sinc', 'quintic'] for interpolant in interpolants: z = f(xx, yy) tab2d = galsim.LookupTable2D(x, y, z, interpolant=interpolant) do_pickle(tab2d) # Make sure precomputed-hash gets covered hash(tab2d) # Use InterpolatedImage to validate wcs = galsim.JacobianWCS( (max(x) - min(x)) / (len(x) - 1), 0, 0, (max(y) - min(y)) / (len(y) - 1), ) img = galsim.Image(z.T, wcs=wcs) ii = galsim.InterpolatedImage( img, use_true_center=True, offset=(galsim.PositionD(img.xmin, img.ymin) - img.true_center), x_interpolant=interpolant, normalization='sb', calculate_maxk=False, calculate_stepk=False).shift(min(x), min(y)) # Check single value functionality. x1, y1 = 2.3, 3.2 np.testing.assert_allclose(tab2d(x1, y1), ii.xValue(x1, y1), atol=1e-10, rtol=0) # Check vectorized output newx = np.linspace(0.2, 3.1, 15) newy = np.linspace(0.3, 10.1, 25) newyy, newxx = np.meshgrid(newy, newx) np.testing.assert_allclose( tab2d(newxx, newyy).ravel(), np.array([ ii.xValue(x_, y_) for x_, y_ in zip(newxx.ravel(), newyy.ravel()) ]).ravel(), atol=1e-10, rtol=0) np.testing.assert_array_almost_equal(tab2d(newxx, newyy), tab2d(newx, newy, grid=True)) # Check that edge_mode='wrap' works tab2d = galsim.LookupTable2D(x, y, z, edge_mode='wrap') ref_dfdx, ref_dfdy = tab2d.gradient(newxx, newyy) test_dfdx, test_dfdy = tab2d.gradient(newxx + 3 * tab2d.xperiod, newyy) np.testing.assert_array_almost_equal(ref_dfdx, test_dfdx) np.testing.assert_array_almost_equal(ref_dfdy, test_dfdy) test_dfdx, test_dfdy = tab2d.gradient(newxx, newyy + 13 * tab2d.yperiod) np.testing.assert_array_almost_equal(ref_dfdx, test_dfdx) np.testing.assert_array_almost_equal(ref_dfdy, test_dfdy) test_dfdx, test_dfdy = tab2d.gradient(newx, newy + 13 * tab2d.yperiod, grid=True) np.testing.assert_array_almost_equal(ref_dfdx, test_dfdx) np.testing.assert_array_almost_equal(ref_dfdy, test_dfdy) # Test mix of inside and outside original boundary test_dfdx, test_dfdy = tab2d.gradient( np.dstack([newxx, newxx + 3 * tab2d.xperiod]), np.dstack([newyy, newyy])) np.testing.assert_array_almost_equal(ref_dfdx, test_dfdx[:, :, 0]) np.testing.assert_array_almost_equal(ref_dfdy, test_dfdy[:, :, 0]) np.testing.assert_array_almost_equal(ref_dfdx, test_dfdx[:, :, 1]) np.testing.assert_array_almost_equal(ref_dfdy, test_dfdy[:, :, 1])
def make_jacobian(dudx, dudy, dvdx, dvdy, x, y): j = galsim.JacobianWCS(dudx, dudy, dvdx, dvdy) return j.withOrigin(galsim.PositionD(x, y))
def test_meds(): """ Create two objects, each with three exposures. Save them to a MEDS file. Load the MEDS file. Compare the created objects with the one read by MEDS. """ # initialise empty MultiExposureObject list objlist = [] # we will be using 2 objects for testing, each with 3 cutouts n_obj_test = 2 n_cut_test = 3 # set the image size box_size = 32 # first obj img11 = galsim.Image(box_size, box_size, init_value=111) img12 = galsim.Image(box_size, box_size, init_value=112) img13 = galsim.Image(box_size, box_size, init_value=113) seg11 = galsim.Image(box_size, box_size, init_value=121) seg12 = galsim.Image(box_size, box_size, init_value=122) seg13 = galsim.Image(box_size, box_size, init_value=123) wth11 = galsim.Image(box_size, box_size, init_value=131) wth12 = galsim.Image(box_size, box_size, init_value=132) wth13 = galsim.Image(box_size, box_size, init_value=133) psf11 = galsim.Image(box_size, box_size, init_value=141) psf12 = galsim.Image(box_size, box_size, init_value=142) psf13 = galsim.Image(box_size, box_size, init_value=143) dudx = 11.1 dudy = 11.2 dvdx = 11.3 dvdy = 11.4 x0 = 11.5 y0 = 11.6 wcs11 = galsim.AffineTransform(dudx, dudy, dvdx, dvdy, galsim.PositionD(x0, y0)) dudx = 12.1 dudy = 12.2 dvdx = 12.3 dvdy = 12.4 wcs12 = galsim.JacobianWCS(dudx, dudy, dvdx, dvdy) wcs13 = galsim.PixelScale(13) # create lists images = [img11, img12, img13] weight = [wth11, wth12, wth13] seg = [seg11, seg12, seg13] psf = [psf11, psf12, psf13] wcs = [wcs11, wcs12, wcs13] # create object obj1 = galsim.des.MultiExposureObject(images=images, weight=weight, seg=seg, psf=psf, wcs=wcs, id=1) # second obj img21 = galsim.Image(box_size, box_size, init_value=211) img22 = galsim.Image(box_size, box_size, init_value=212) img23 = galsim.Image(box_size, box_size, init_value=213) seg21 = galsim.Image(box_size, box_size, init_value=221) seg22 = galsim.Image(box_size, box_size, init_value=222) seg23 = galsim.Image(box_size, box_size, init_value=223) wth21 = galsim.Image(box_size, box_size, init_value=231) wth22 = galsim.Image(box_size, box_size, init_value=332) wth23 = galsim.Image(box_size, box_size, init_value=333) psf21 = galsim.Image(box_size, box_size, init_value=241) psf22 = galsim.Image(box_size, box_size, init_value=342) psf23 = galsim.Image(box_size, box_size, init_value=343) dudx = 21.1 dudy = 21.2 dvdx = 21.3 dvdy = 21.4 x0 = 21.5 y0 = 21.6 wcs21 = galsim.AffineTransform(dudx, dudy, dvdx, dvdy, galsim.PositionD(x0, y0)) dudx = 22.1 dudy = 22.2 dvdx = 22.3 dvdy = 22.4 wcs22 = galsim.JacobianWCS(dudx, dudy, dvdx, dvdy) wcs23 = galsim.PixelScale(23) # create lists images = [img21, img22, img23] weight = [wth21, wth22, wth23] seg = [seg21, seg22, seg23] psf = [psf21, psf22, psf23] wcs = [wcs21, wcs22, wcs23] # create object # This time put the wcs in the image and get it there. img21.wcs = wcs21 img22.wcs = wcs22 img23.wcs = wcs23 obj2 = galsim.des.MultiExposureObject(images=images, weight=weight, seg=seg, psf=psf, id=2) obj3 = galsim.des.MultiExposureObject(images=images, id=3) # create an object list objlist = [obj1, obj2] # save objects to MEDS file filename_meds = 'output/test_meds.fits' galsim.des.WriteMEDS(objlist, filename_meds, clobber=True) bad1 = galsim.Image(32, 48, init_value=0) bad2 = galsim.Image(35, 35, init_value=0) bad3 = galsim.Image(48, 48, init_value=0) with assert_raises(TypeError): galsim.des.MultiExposureObject(images=img11) with assert_raises(galsim.GalSimValueError): galsim.des.MultiExposureObject(images=[]) with assert_raises(galsim.GalSimValueError): galsim.des.MultiExposureObject(images=[bad1]) with assert_raises(galsim.GalSimValueError): galsim.des.MultiExposureObject(images=[bad2]) with assert_raises(galsim.GalSimValueError): galsim.des.MultiExposureObject(images=[img11, bad3]) with assert_raises(TypeError): galsim.des.MultiExposureObject(images=images, weight=wth11) with assert_raises(galsim.GalSimValueError): galsim.des.MultiExposureObject(images=images, weight=[]) with assert_raises(galsim.GalSimValueError): galsim.des.MultiExposureObject(images=[img11], weight=[bad3]) with assert_raises(galsim.GalSimValueError): galsim.des.MultiExposureObject(images=[img11], psf=[bad1]) with assert_raises(galsim.GalSimValueError): galsim.des.MultiExposureObject(images=[img11], psf=[bad2]) with assert_raises(galsim.GalSimValueError): galsim.des.MultiExposureObject(images=[img11, img12], psf=[bad2, psf12]) with assert_raises(TypeError): galsim.des.MultiExposureObject(images=images, wcs=wcs11) celestial_wcs = galsim.FitsWCS("DECam_00154912_12_header.fits", dir='des_data') with assert_raises(galsim.GalSimValueError): galsim.des.MultiExposureObject(images=[img11], wcs=[celestial_wcs]) # Check the one with no psf, weight, etc. filename_meds2 = 'output/test_meds_image_only.fits' galsim.des.WriteMEDS([obj3], filename_meds2, clobber=True) # Note that while there are no tests prior to this, the above still checks for # syntax errors in the meds creation software, so it's still worth running as part # of the normal unit tests. # But for the rest of the tests, we'll use the meds module to make sure our code # stays in sync with any changes there. try: import meds # Meds will import this, so check for this too. import fitsio except ImportError: print( 'Failed to import either meds or fitsio. Unable to do tests of meds file.' ) return # Run meds module's validate function try: meds.util.validate_meds(filename_meds) meds.util.validate_meds(filename_meds2) except AttributeError: print( 'Seems to be the wrong meds package. Unable to do tests of meds file.' ) return m = meds.MEDS(filename_meds) # Check the image_info extension: ref_info = meds.util.get_image_info_dtype(1) info = m.get_image_info() print('info = ', info) for name, dt in ref_info: dt = numpy.dtype(dt) print(name, dt, info.dtype[name], dt.char, info.dtype[name].char) assert name in info.dtype.names, "column %s not present in image_info extension" % name # I think S and U for this purpose are equivalent. # But I'm finding S in the reference, and U in info. c = info.dtype[name].char c = 'S' if c == 'U' else c assert dt.char == c, "column %s is the wrong type" % name # Check the basic structure of the object_data extension cat = m.get_cat() ref_data = meds.util.get_meds_output_dtype(1) for tup in ref_data: # Some of these tuples have 3 items, not 2. The last two are the full dtype tuple. name = tup[0] if len(tup) == 2: dt = tup[1] else: dt = tup[1:] dt = numpy.dtype(dt) print(name, dt, cat.dtype[name], dt.char, cat.dtype[name].char) assert name in cat.dtype.names, "column %s not present in object_data extension" % name assert dt.char == cat.dtype[ name].char, "column %s is the wrong type" % name # Check that we have the right number of objects. n_obj = len(cat) print('number of objects is %d' % n_obj) numpy.testing.assert_equal(n_obj, n_obj_test, err_msg="MEDS file has wrong number of objects") # loop over objects and exposures - test get_cutout for iobj in range(n_obj): # check ID is correct numpy.testing.assert_equal( cat['id'][iobj], iobj + 1, err_msg="MEDS file has wrong id for object %d" % iobj) # get number of cutouts and check if it's right n_cut = cat['ncutout'][iobj] numpy.testing.assert_equal( n_cut, n_cut_test, err_msg="MEDS file has wrong ncutout for object %d" % iobj) # loop over cutouts for icut in range(n_cut): # get the images etc to compare with originals img = m.get_cutout(iobj, icut, type='image') wth = m.get_cutout(iobj, icut, type='weight') seg = m.get_cutout(iobj, icut, type='seg') psf = m.get_psf(iobj, icut) wcs_meds = m.get_jacobian(iobj, icut) # Note: col == x, row == y. wcs_array_meds = numpy.array([ wcs_meds['dudcol'], wcs_meds['dudrow'], wcs_meds['dvdcol'], wcs_meds['dvdrow'], wcs_meds['col0'], wcs_meds['row0'] ]) # compare numpy.testing.assert_array_equal( img, objlist[iobj].images[icut].array, err_msg="MEDS cutout has wrong img for object %d" % iobj) numpy.testing.assert_array_equal( wth, objlist[iobj].weight[icut].array, err_msg="MEDS cutout has wrong wth for object %d" % iobj) numpy.testing.assert_array_equal( seg, objlist[iobj].seg[icut].array, err_msg="MEDS cutout has wrong seg for object %d" % iobj) numpy.testing.assert_array_equal( psf, objlist[iobj].psf[icut].array, err_msg="MEDS cutout has wrong psf for object %d" % iobj) wcs_orig = objlist[iobj].wcs[icut] wcs_array_orig = numpy.array([ wcs_orig.dudx, wcs_orig.dudy, wcs_orig.dvdx, wcs_orig.dvdy, wcs_orig.origin.x, wcs_orig.origin.y ]) numpy.testing.assert_array_equal( wcs_array_meds, wcs_array_orig, err_msg="MEDS cutout has wrong wcs for object %d" % iobj) # get the mosaic to compare with originals img = m.get_mosaic(iobj, type='image') wth = m.get_mosaic(iobj, type='weight') seg = m.get_mosaic(iobj, type='seg') # There is currently no get_mosaic option for the psfs. #psf = m.get_mosaic( iobj, type='psf') psf = numpy.concatenate( [m.get_psf(iobj, icut) for icut in range(n_cut)], axis=0) # get the concatenated images - create the true mosaic true_mosaic_img = numpy.concatenate( [x.array for x in objlist[iobj].images], axis=0) true_mosaic_wth = numpy.concatenate( [x.array for x in objlist[iobj].weight], axis=0) true_mosaic_seg = numpy.concatenate( [x.array for x in objlist[iobj].seg], axis=0) true_mosaic_psf = numpy.concatenate( [x.array for x in objlist[iobj].psf], axis=0) # compare numpy.testing.assert_array_equal( true_mosaic_img, img, err_msg="MEDS mosaic has wrong img for object %d" % iobj) numpy.testing.assert_array_equal( true_mosaic_wth, wth, err_msg="MEDS mosaic has wrong wth for object %d" % iobj) numpy.testing.assert_array_equal( true_mosaic_seg, seg, err_msg="MEDS mosaic has wrong seg for object %d" % iobj) numpy.testing.assert_array_equal( true_mosaic_psf, psf, err_msg="MEDS mosaic has wrong psf for object %d" % iobj)
def test_interleaveImages(): # 1a) With galsim Gaussian g = galsim.Gaussian(sigma=3.7, flux=1000.) gal = galsim.Convolve([g, galsim.Pixel(1.0)]) im_list = [] offset_list = [] n = 2 for j in range(n): for i in range(n): im = galsim.Image(16 * n, 16 * n) offset = galsim.PositionD(-(i + 0.5) / n + 0.5, -(j + 0.5) / n + 0.5) offset_list.append(offset) gal.drawImage(image=im, method='no_pixel', offset=offset, scale=0.5) im_list.append(im) scale = im.scale # Input to N as an int img = galsim.utilities.interleaveImages(im_list, n, offsets=offset_list) im = galsim.Image(16 * n * n, 16 * n * n) g = galsim.Gaussian(sigma=3.7, flux=1000. * n * n) gal = galsim.Convolve([g, galsim.Pixel(1.0)]) gal.drawImage(image=im, method='no_pixel', offset=galsim.PositionD(0.0, 0.0), scale=1. * scale / n) np.testing.assert_almost_equal( img.array, im.array, 6, err_msg="Interleaved Gaussian images do not match") assert im.wcs == img.wcs # 1b) With im_list and offsets permuted offset_list = [] # An elegant way of generating the default offsets DX = np.arange(0.0, -1.0, -1.0 / n) DX -= DX.mean() DY = DX for dy in DY: for dx in DX: offset = galsim.PositionD(dx, dy) offset_list.append(offset) np.random.seed(42) # for generating the same random permutation everytime rand_idx = np.random.permutation(len(offset_list)) im_list_randperm = [im_list[idx] for idx in rand_idx] offset_list_randperm = [offset_list[idx] for idx in rand_idx] # Input to N as a tuple img_randperm = galsim.utilities.interleaveImages( im_list_randperm, (n, n), offsets=offset_list_randperm) np.testing.assert_array_equal( img_randperm.array, img.array, err_msg="Interleaved images do not match when 'offsets' is supplied") assert img_randperm.scale == img.scale # 1c) Catching errors in offsets offset_list = [] im_list = [] n = 5 # Generate approximate offsets DX = np.array([-0.67, -0.33, 0., 0.33, 0.67]) DY = DX for dy in DY: for dx in DX: offset = galsim.PositionD(dx, dy) offset_list.append(offset) im = galsim.Image(16, 16) gal.drawImage(image=im, offset=offset, method='no_pixel') im_list.append(im) try: N = (n, n) np.testing.assert_raises(ValueError, galsim.utilities.interleaveImages, im_list, N, offset_list) except ImportError: print("The assert_raises tests require nose") offset_list = [] im_list = [] n = 5 DX = np.arange(0., 1., 1. / n) DY = DX for dy in DY: for dx in DX: offset = galsim.PositionD(dx, dy) offset_list.append(offset) im = galsim.Image(16, 16) gal.drawImage(image=im, offset=offset, method='no_pixel') im_list.append(im) try: N = (n, n) np.testing.assert_raises(ValueError, galsim.utilities.interleaveImages, im_list, N, offset_list) except ImportError: print("The assert_raises tests require nose") # 2a) Increase resolution along one direction - square to rectangular images n = 2 g = galsim.Gaussian(sigma=3.7, flux=100.) gal1 = g.shear(g=1. * (n**2 - 1) / (n**2 + 1), beta=0.0 * galsim.radians) im_list = [] offset_list = [] # Generating offsets in a natural way DY = np.arange(0.0, 1.0, 1.0 / (n * n)) DY -= DY.mean() for dy in DY: im = galsim.Image(16, 16) offset = galsim.PositionD(0.0, dy) offset_list.append(offset) gal1.drawImage(im, offset=offset, method='no_pixel', scale=2.0) im_list.append(im) img = galsim.utilities.interleaveImages(im_list, N=[1, n**2], offsets=offset_list, add_flux=False, suppress_warnings=True) im = galsim.Image(16, 16 * n * n) # The interleaved image has the total flux averaged out since `add_flux = False' gal = galsim.Gaussian(sigma=3.7 * n, flux=100.) gal.drawImage(image=im, method='no_pixel', scale=2.0) np.testing.assert_array_equal( im.array, img.array, err_msg="Sheared gaussian not interleaved correctly") assert img.wcs == galsim.JacobianWCS(2.0, 0.0, 0.0, 2. / (n**2)) # 2b) Increase resolution along one direction - rectangular to square images n = 2 g = galsim.Gaussian(sigma=3.7, flux=100.) gal2 = g.shear(g=1. * (n**2 - 1) / (n**2 + 1), beta=90. * galsim.degrees) im_list = [] offset_list = [] # Generating offsets in a natural way DX = np.arange(0.0, 1.0, 1.0 / n**2) DX -= DX.mean() for dx in DX: offset = galsim.PositionD(dx, 0.0) offset_list.append(offset) im = galsim.Image(16, 16 * n * n) gal2.drawImage(im, offset=offset, method='no_pixel', scale=3.0) im_list.append(im) img = galsim.utilities.interleaveImages(im_list, N=np.array([n**2, 1]), offsets=offset_list, suppress_warnings=True) im = galsim.Image(16 * n * n, 16 * n * n) gal = galsim.Gaussian(sigma=3.7, flux=100. * n * n) scale = im_list[0].scale gal.drawImage(image=im, scale=1. * scale / n, method='no_pixel') np.testing.assert_almost_equal( im.array, img.array, 12, err_msg="Sheared gaussian not interleaved correctly") assert img.wcs == galsim.JacobianWCS(1. * scale / n**2, 0.0, 0.0, scale) # 3) Check compatability with deInterleaveImage n = 3 g = galsim.Gaussian(sigma=3.7, flux=100.) # break symmetry to detect possible bugs in deInterleaveImage gal = g.shear(g=0.2, beta=0. * galsim.degrees) im_list = [] offset_list = [] # Generating offsets in the order they would be returned by deInterleaveImage, for convenience for i in range(n): for j in range(n): im = galsim.Image(16 * n, 16 * n) offset = galsim.PositionD(-(i + 0.5) / n + 0.5, -(j + 0.5) / n + 0.5) offset_list.append(offset) gal.drawImage(image=im, method='no_pixel', offset=offset, scale=0.5) im.setOrigin(3, 3) # for non-trivial bounds im_list.append(im) img = galsim.utilities.interleaveImages(im_list, N=n, offsets=offset_list) im_list_1, offset_list_1 = galsim.utilities.deInterleaveImage(img, N=n) for k in range(n**2): assert offset_list_1[k] == offset_list[k] np.testing.assert_array_equal(im_list_1[k].array, im_list[k].array) assert im_list_1[k].wcs == im_list[k].wcs assert im_list[k].origin == img.origin assert im_list[k].bounds == im_list_1[k].bounds # Checking for non-default flux option img = galsim.utilities.interleaveImages(im_list, N=n, offsets=offset_list, add_flux=False) im_list_2, offset_list_2 = galsim.utilities.deInterleaveImage( img, N=n, conserve_flux=True) for k in range(n**2): assert offset_list_2[k] == offset_list[k] np.testing.assert_array_equal(im_list_2[k].array, im_list[k].array) assert im_list_2[k].wcs == im_list[k].wcs
def test_simple(): """Initial simple test of Gaussian, Kolmogorov, and Moffat PSFs. """ # Here is the true PSF scale = 1.3 g1 = 0.23 g2 = -0.17 du = 0.1 dv = 0.4 for fiducial in [fiducial_gaussian, fiducial_kolmogorov, fiducial_moffat]: print() print("fiducial = ", fiducial) print() psf = fiducial.dilate(scale).shear(g1=g1, g2=g2).shift(du, dv) # Draw the PSF onto an image. Let's go ahead and give it a non-trivial WCS. wcs = galsim.JacobianWCS(0.26, 0.05, -0.08, -0.29) image = galsim.Image(64, 64, wcs=wcs) # This is only going to come out right if we (unphysically) don't convolve by the pixel. psf.drawImage(image, method='no_pixel') # Make a StarData instance for this image stardata = piff.StarData(image, image.true_center) fiducial_star = piff.Star(stardata, None) # First try fastfit. print('Fast fit') model = piff.GSObjectModel(fiducial, fastfit=True, include_pixel=False) fit = model.fit(model.initialize(fiducial_star)).fit print('True scale = ', scale, ', model scale = ', fit.params[0]) print('True g1 = ', g1, ', model g1 = ', fit.params[1]) print('True g2 = ', g2, ', model g2 = ', fit.params[2]) print('True du = ', du, ', model du = ', fit.center[0]) print('True dv = ', dv, ', model dv = ', fit.center[1]) # This test is fairly accurate, since we didn't add any noise and didn't convolve by # the pixel, so the image is very accurately a sheared GSObject. np.testing.assert_allclose(fit.params[0], scale, rtol=1e-4) np.testing.assert_allclose(fit.params[1], g1, rtol=0, atol=1e-7) np.testing.assert_allclose(fit.params[2], g2, rtol=0, atol=1e-7) np.testing.assert_allclose(fit.center[0], du, rtol=0, atol=1e-7) np.testing.assert_allclose(fit.center[1], dv, rtol=0, atol=1e-7) # Now try fastfit=False. print('Slow fit') model = piff.GSObjectModel(fiducial, fastfit=False, include_pixel=False) fit = model.fit(model.initialize(fiducial_star)).fit print('True scale = ', scale, ', model scale = ', fit.params[0]) print('True g1 = ', g1, ', model g1 = ', fit.params[1]) print('True g2 = ', g2, ', model g2 = ', fit.params[2]) print('True du = ', du, ', model du = ', fit.center[0]) print('True dv = ', dv, ', model dv = ', fit.center[1]) np.testing.assert_allclose(fit.params[0], scale, rtol=1e-6) np.testing.assert_allclose(fit.params[1], g1, rtol=0, atol=1e-6) np.testing.assert_allclose(fit.params[2], g2, rtol=0, atol=1e-6) np.testing.assert_allclose(fit.center[0], du, rtol=0, atol=1e-6) np.testing.assert_allclose(fit.center[1], dv, rtol=0, atol=1e-6) # Now test running it via the config parser config = { 'model': { 'type': 'GSObjectModel', 'gsobj': repr(fiducial), 'include_pixel': False } } if __name__ == '__main__': logger = piff.config.setup_logger(verbose=3) else: logger = piff.config.setup_logger(verbose=1) model = piff.Model.process(config['model'], logger) fit = model.fit(model.initialize(fiducial_star)).fit # Same tests. np.testing.assert_allclose(fit.params[0], scale, rtol=1e-6) np.testing.assert_allclose(fit.params[1], g1, rtol=0, atol=1e-6) np.testing.assert_allclose(fit.params[2], g2, rtol=0, atol=1e-6) np.testing.assert_allclose(fit.center[0], du, rtol=0, atol=1e-6) np.testing.assert_allclose(fit.center[1], dv, rtol=0, atol=1e-6) # Also need to test ability to serialize outfile = os.path.join('output', 'gsobject_test.fits') with fitsio.FITS(outfile, 'rw', clobber=True) as f: model.write(f, 'psf_model') with fitsio.FITS(outfile, 'r') as f: roundtrip_model = piff.GSObjectModel.read(f, 'psf_model') assert model.__dict__ == roundtrip_model.__dict__ # Finally, we should also test with pixel convolution included. This really only makes # sense for fastfit=False, since HSM FindAdaptiveMom doesn't account for the pixel shape # in its measurements. # Draw the PSF onto an image. Let's go ahead and give it a non-trivial WCS. wcs = galsim.JacobianWCS(0.26, 0.05, -0.08, -0.29) image = galsim.Image(64, 64, wcs=wcs) psf.drawImage(image, method='auto') # Make a StarData instance for this image stardata = piff.StarData(image, image.true_center) fiducial_star = piff.Star(stardata, None) print('Slow fit, pixel convolution included.') model = piff.GSObjectModel(fiducial, fastfit=False, include_pixel=True) star = model.initialize(fiducial_star) star = model.fit( star, fastfit=True) # Get better results with one round of fastfit. # Use a no op convert_func, just to touch that branch in the code. convert_func = lambda prof: prof fit = model.fit(star, convert_func=convert_func).fit print('True scale = ', scale, ', model scale = ', fit.params[0]) print('True g1 = ', g1, ', model g1 = ', fit.params[1]) print('True g2 = ', g2, ', model g2 = ', fit.params[2]) print('True du = ', du, ', model du = ', fit.center[0]) print('True dv = ', dv, ', model dv = ', fit.center[1]) # Accuracy goals are a bit looser here since it's harder to fit with the pixel involved. np.testing.assert_allclose(fit.params[0], scale, rtol=1e-6) np.testing.assert_allclose(fit.params[1], g1, rtol=0, atol=1e-6) np.testing.assert_allclose(fit.params[2], g2, rtol=0, atol=1e-6) np.testing.assert_allclose(fit.center[0], du, rtol=0, atol=1e-5) np.testing.assert_allclose(fit.center[1], dv, rtol=0, atol=1e-5)
def test_direct(): """ Simple test for directly instantiated Gaussian, Kolmogorov, and Moffat without going through GSObjectModel explicitly. """ # Here is the true PSF scale = 1.3 g1 = 0.23 g2 = -0.17 du = 0.1 dv = 0.4 gsobjs = [ galsim.Gaussian(sigma=1.0), galsim.Kolmogorov(half_light_radius=1.0), galsim.Moffat(half_light_radius=1.0, beta=3.0), galsim.Moffat(half_light_radius=1.0, beta=2.5, trunc=3.0) ] models = [ piff.Gaussian(fastfit=True, include_pixel=False), piff.Kolmogorov(fastfit=True, include_pixel=False), piff.Moffat(fastfit=True, beta=3.0, include_pixel=False), piff.Moffat(fastfit=True, beta=2.5, trunc=3.0, include_pixel=False) ] for gsobj, model in zip(gsobjs, models): print() print("gsobj = ", gsobj) print() psf = gsobj.dilate(scale).shear(g1=g1, g2=g2).shift(du, dv) # Draw the PSF onto an image. Let's go ahead and give it a non-trivial WCS. wcs = galsim.JacobianWCS(0.26, 0.05, -0.08, -0.29) image = galsim.Image(64, 64, wcs=wcs) # This is only going to come out right if we (unphysically) don't convolve by the pixel. psf.drawImage(image, method='no_pixel') # Make a StarData instance for this image stardata = piff.StarData(image, image.true_center) star = piff.Star(stardata, None) star = model.initialize(star) # First try fastfit. print('Fast fit') fit = model.fit(star).fit print('True scale = ', scale, ', model scale = ', fit.params[0]) print('True g1 = ', g1, ', model g1 = ', fit.params[1]) print('True g2 = ', g2, ', model g2 = ', fit.params[2]) print('True du = ', du, ', model du = ', fit.center[0]) print('True dv = ', dv, ', model dv = ', fit.center[1]) # This test is fairly accurate, since we didn't add any noise and didn't convolve by # the pixel, so the image is very accurately a sheared GSObject. # These tests are more strict above. The truncated Moffat included here but not there # doesn't work quite as well. np.testing.assert_allclose(fit.params[0], scale, rtol=1e-4) np.testing.assert_allclose(fit.params[1], g1, rtol=0, atol=1e-5) np.testing.assert_allclose(fit.params[2], g2, rtol=0, atol=1e-5) np.testing.assert_allclose(fit.center[0], du, rtol=0, atol=1e-5) np.testing.assert_allclose(fit.center[1], dv, rtol=0, atol=1e-5) # Also need to test ability to serialize outfile = os.path.join('output', 'gsobject_direct_test.fits') with fitsio.FITS(outfile, 'rw', clobber=True) as f: model.write(f, 'psf_model') with fitsio.FITS(outfile, 'r') as f: roundtrip_model = piff.GSObjectModel.read(f, 'psf_model') assert model.__dict__ == roundtrip_model.__dict__ # repeat with fastfit=False models = [ piff.Gaussian(fastfit=False, include_pixel=False), piff.Kolmogorov(fastfit=False, include_pixel=False), piff.Moffat(fastfit=False, beta=3.0, include_pixel=False), piff.Moffat(fastfit=False, beta=2.5, trunc=3.0, include_pixel=False) ] for gsobj, model in zip(gsobjs, models): print() print("gsobj = ", gsobj) print() psf = gsobj.dilate(scale).shear(g1=g1, g2=g2).shift(du, dv) # Draw the PSF onto an image. Let's go ahead and give it a non-trivial WCS. wcs = galsim.JacobianWCS(0.26, 0.05, -0.08, -0.29) image = galsim.Image(64, 64, wcs=wcs) # This is only going to come out right if we (unphysically) don't convolve by the pixel. psf.drawImage(image, method='no_pixel') # Make a StarData instance for this image stardata = piff.StarData(image, image.true_center) star = piff.Star(stardata, None) star = model.initialize(star) print('Slow fit') fit = model.fit(star).fit print('True scale = ', scale, ', model scale = ', fit.params[0]) print('True g1 = ', g1, ', model g1 = ', fit.params[1]) print('True g2 = ', g2, ', model g2 = ', fit.params[2]) print('True du = ', du, ', model du = ', fit.center[0]) print('True dv = ', dv, ', model dv = ', fit.center[1]) # This test is fairly accurate, since we didn't add any noise and didn't convolve by # the pixel, so the image is very accurately a sheared GSObject. np.testing.assert_allclose(fit.params[0], scale, rtol=1e-5) np.testing.assert_allclose(fit.params[1], g1, rtol=0, atol=1e-5) np.testing.assert_allclose(fit.params[2], g2, rtol=0, atol=1e-5) np.testing.assert_allclose(fit.center[0], du, rtol=0, atol=1e-5) np.testing.assert_allclose(fit.center[1], dv, rtol=0, atol=1e-5) # Also need to test ability to serialize outfile = os.path.join('output', 'gsobject_direct_test.fits') with fitsio.FITS(outfile, 'rw', clobber=True) as f: model.write(f, 'psf_model') with fitsio.FITS(outfile, 'r') as f: roundtrip_model = piff.GSObjectModel.read(f, 'psf_model') assert model.__dict__ == roundtrip_model.__dict__
def test_interleaveImages(): import time t1 = time.time() # 1a) With galsim Gaussian g = galsim.Gaussian(sigma=3.7, flux=1000.) gal = galsim.Convolve([g, galsim.Pixel(1.0)]) im_list = [] offset_list = [] n = 2 for j in xrange(n): for i in xrange(n): im = galsim.Image(16 * n, 16 * n) offset = galsim.PositionD(-(i + 0.5) / n + 0.5, -(j + 0.5) / n + 0.5) offset_list.append(offset) gal.drawImage(image=im, method='no_pixel', offset=offset, scale=0.5) im_list.append(im) scale = im.scale # Input to N as an int img = galsim.utilities.interleaveImages(im_list, n, offsets=offset_list) im = galsim.Image(16 * n * n, 16 * n * n) g = galsim.Gaussian(sigma=3.7, flux=1000. * n * n) gal = galsim.Convolve([g, galsim.Pixel(1.0)]) gal.drawImage(image=im, method='no_pixel', offset=galsim.PositionD(0.0, 0.0), scale=1. * scale / n) np.testing.assert_array_equal(img.array,im.array,\ err_msg="Interleaved Gaussian images do not match") assert im.wcs == img.wcs # 1b) With im_list and offsets permuted offset_list = [] # An elegant way of generating the default offsets DX = np.arange(0.0, -1.0, -1.0 / n) DX -= DX.mean() DY = DX for dy in DY: for dx in DX: offset = galsim.PositionD(dx, dy) offset_list.append(offset) np.random.seed(42) # for generating the same random permutation everytime rand_idx = np.random.permutation(len(offset_list)) im_list_randperm = [im_list[idx] for idx in rand_idx] offset_list_randperm = [offset_list[idx] for idx in rand_idx] # Input to N as a tuple img_randperm = galsim.utilities.interleaveImages( im_list_randperm, (n, n), offsets=offset_list_randperm) np.testing.assert_array_equal(img_randperm.array,img.array,\ err_msg="Interleaved images do not match when 'offsets' is supplied") assert img_randperm.scale == img.scale # 1c) Catching errors in offsets offset_list = [] im_list = [] n = 5 # Generate approximate offsets DX = np.array([-0.67, -0.33, 0., 0.33, 0.67]) DY = DX for dy in DY: for dx in DX: offset = galsim.PositionD(dx, dy) offset_list.append(offset) im = galsim.Image(16, 16) gal.drawImage(image=im, offset=offset, method='no_pixel') im_list.append(im) try: N = (n, n) np.testing.assert_raises(ValueError, galsim.utilities.interleaveImages, im_list, N, offset_list) except ImportError: print "The assert_raises tests require nose" offset_list = [] im_list = [] n = 5 DX = np.arange(0., 1., 1. / n) DY = DX for dy in DY: for dx in DX: offset = galsim.PositionD(dx, dy) offset_list.append(offset) im = galsim.Image(16, 16) gal.drawImage(image=im, offset=offset, method='no_pixel') im_list.append(im) try: N = (n, n) np.testing.assert_raises(ValueError, galsim.utilities.interleaveImages, im_list, N, offset_list) except ImportError: print "The assert_raises tests require nose" # 2a) Increase resolution along one direction - square to rectangular images n = 2 g = galsim.Gaussian(sigma=3.7, flux=100.) gal1 = g.shear(g=1. * (n**2 - 1) / (n**2 + 1), beta=0.0 * galsim.radians) im_list = [] offset_list = [] # Generating offsets in a natural way DY = np.arange(0.0, 1.0, 1.0 / (n * n)) DY -= DY.mean() for dy in DY: im = galsim.Image(16, 16) offset = galsim.PositionD(0.0, dy) offset_list.append(offset) gal1.drawImage(im, offset=offset, method='no_pixel', scale=2.0) im_list.append(im) img = galsim.utilities.interleaveImages(im_list, N=[1, n**2], offsets=offset_list, add_flux=False, suppress_warnings=True) im = galsim.Image(16, 16 * n * n) # The interleaved image has the total flux averaged out since `add_flux = False' gal = galsim.Gaussian(sigma=3.7 * n, flux=100.) gal.drawImage(image=im, method='no_pixel', scale=2.0) np.testing.assert_array_equal( im.array, img.array, err_msg="Sheared gaussian not interleaved correctly") assert img.wcs == galsim.JacobianWCS(2.0, 0.0, 0.0, 2. / (n**2)) # 2b) Increase resolution along one direction - rectangular to square images n = 2 g = galsim.Gaussian(sigma=3.7, flux=100.) gal2 = g.shear(g=1. * (n**2 - 1) / (n**2 + 1), beta=90. * galsim.degrees) im_list = [] offset_list = [] # Generating offsets in a natural way DX = np.arange(0.0, 1.0, 1.0 / n**2) DX -= DX.mean() for dx in DX: offset = galsim.PositionD(dx, 0.0) offset_list.append(offset) im = galsim.Image(16, 16 * n * n) gal2.drawImage(im, offset=offset, method='no_pixel', scale=3.0) im_list.append(im) img = galsim.utilities.interleaveImages(im_list, N=np.array([n**2, 1]), offsets=offset_list, suppress_warnings=True) im = galsim.Image(16 * n * n, 16 * n * n) gal = galsim.Gaussian(sigma=3.7, flux=100. * n * n) scale = im_list[0].scale gal.drawImage(image=im, scale=1. * scale / n, method='no_pixel') np.testing.assert_array_equal( im.array, img.array, err_msg="Sheared gaussian not interleaved correctly") assert img.wcs == galsim.JacobianWCS(1. * scale / n**2, 0.0, 0.0, scale) t2 = time.time() print 'time for %s = %.2f' % (funcname(), t2 - t1)
def deInterleaveImage(image, N, conserve_flux=False,suppress_warnings=False): """ The routine to do the opposite of what 'interleaveImages' routine does. It generates a (uniform) dither sequence of low resolution images from a high resolution image. Many pixel level detector effects, such as interpixel capacitance, persistence, charge diffusion etc. can be included only on images drawn at the native pixel scale, which happen to be undersampled in most cases. Nyquist-sampled images that also include the effects of detector non-idealities can be obtained by drawing multiple undersampled images (with the detector effects included) that are offset from each other by a fraction of a pixel. If the offsets are uniformly spaced, then images can be combined using 'interleaveImages' into a Nyquist-sampled image. Drawing multiple low resolution images of a light profile can be a lot slower than drawing a high resolution image of the same profile, even if the total number of pixels is the same. A uniformly offset dither sequence can be extracted from a well-resolved image that is drawn by convolving the surface brightness profile explicitly with the native pixel response and setting a lower sampling scale (or higher sampling rate) using the `pixel_scale' argument in drawImage() routine and setting the `method' parameter to `no_pixel'. Here is an example script using this routine: Interleaving four Gaussian images --------------------------------- >>> n = 2 >>> gal = galsim.Gaussian(sigma=2.8) >>> gal_pix = galsim.Convolve([gal,galsim.Pixel(scale=1.0)]) >>> img = gal_pix.drawImage(gal_pix,scale=1.0/n,method='no_pixel') >>> im_list, offsets = galsim.utilities.deInterleaveImage(img,N=n) >>> for im in im_list: >>> im.applyNonlinearity(lambda x: x-0.01*x**2) #detector effects >>> img_new = galsim.utilities.interleaveImages(im_list,N=n,offsets) @param image Input image from which lower resolution images are extracted. @param N Number of images extracted in either directions. It can be of type 'int' if equal number of images are extracted in both directions or a list or tuple of two integers, containing the number of images in x and y directions respectively. @param conserve_flux Should the routine output images that have, on average, same total pixel values as the input image (True) or should the pixel values summed over all the images equal the sum of pixel values of the input image (False)? [default: False] @param suppress_warnings Suppresses the warnings about the pixel scale of the output, if True. [default: False] @returns a list of images and offsets to reconstruct the input image using 'interleaveImages'. """ if isinstance(N,int): n1,n2 = N,N elif hasattr(N,'__iter__'): if len(N)==2: n1,n2 = N else: raise TypeError("'N' has to be a list or a tuple of two integers") if not (n1 == int(n1) and n2 == int(n2)): raise TypeError("'N' has to be of type int or a list or a tuple of two integers") n1 = int(n1) n2 = int(n2) else: raise TypeError("'N' has to be of type int or a list or a tuple of two integers") if not isinstance(image,galsim.Image): raise TypeError("'image' has to be an instance of galsim.Image") y_size,x_size = image.array.shape if x_size%n1 or y_size%n2: raise ValueError("The value of 'N' is incompatible with the dimensions of the image to "+ +"be 'deinterleaved'") im_list, offsets = [], [] for i in range(n1): for j in range(n2): # The tricky part - going from array indices to Image coordinates (x,y) # DX[i'] = -(i+0.5)/n+0.5 = -i/n + 0.5*(n-1)/n # i = -n DX[i'] + 0.5*(n-1) dx,dy = -(i+0.5)/n1+0.5,-(j+0.5)/n2+0.5 offset = galsim.PositionD(dx,dy) img_arr = image.array[j::n2,i::n1].copy() img = galsim.Image(img_arr) if conserve_flux is True: img *= n1*n2 im_list.append(img) offsets.append(offset) wcs = image.wcs if wcs is not None and wcs.isUniform(): jac = wcs.jacobian() for img in im_list: img_wcs = galsim.JacobianWCS(jac.dudx*n1,jac.dudy*n2,jac.dvdx*n1,jac.dvdy*n2) ## Since pixel scale WCS is not equal to its jacobian, checking if img_wcs is a pixel ## scale img_wcs_decomp = img_wcs.getDecomposition() if img_wcs_decomp[1].g==0: img.wcs = galsim.PixelScale(img_wcs_decomp[0]) else: img.wcs = img_wcs ## Preserve the origin so that the interleaved image has the same bounds as the image ## that is being deinterleaved. img.setOrigin(image.origin()) elif suppress_warnings is False: import warnings warnings.warn("Individual images could not be assigned a WCS automatically.") return im_list, offsets
def test_var(): """Check that the variance estimate in params_var is sane. """ # Here is the true PSF scale = 1.3 g1 = 0.23 g2 = -0.17 du = 0.1 dv = 0.4 flux = 500 wcs = galsim.JacobianWCS(0.26, 0.05, -0.08, -0.29) noise = 0.2 gsobjs = [ galsim.Gaussian(sigma=1.0), galsim.Kolmogorov(half_light_radius=1.0), galsim.Moffat(half_light_radius=1.0, beta=3.0), galsim.Moffat(half_light_radius=1.0, beta=2.5, trunc=3.0) ] # Mix of centered = True/False, # fastfit = True/False, # include_pixel = True/False models = [ piff.Gaussian(fastfit=False, include_pixel=False, centered=False), piff.Kolmogorov(fastfit=True, include_pixel=True, centered=False), piff.Moffat(fastfit=False, beta=4.8, include_pixel=True, centered=True), piff.Moffat(fastfit=True, beta=2.5, trunc=3.0, include_pixel=False, centered=True) ] names = ['Gaussian', 'Kolmogorov', 'Moffat3', 'Moffat2.5'] for gsobj, model, name in zip(gsobjs, models, names): print() print("gsobj = ", gsobj) print() psf = gsobj.dilate(scale).shear(g1=g1, g2=g2).shift(du, dv).withFlux(flux) image = psf.drawImage(nx=64, ny=64, wcs=wcs, method='no_pixel') weight = image.copy() weight.fill(1 / noise**2) # Save this one without noise. image1 = image.copy() image1.addNoise(galsim.GaussianNoise(sigma=noise)) # Make a StarData instance for this image stardata = piff.StarData(image, image.true_center, weight) star = piff.Star(stardata, None) star = model.initialize(star) fit = model.fit(star).fit file_name = 'input/test_%s_var.npz' % name print(file_name) if not os.path.isfile(file_name): num_runs = 1000 all_params = [] for i in range(num_runs): image1 = image.copy() image1.addNoise(galsim.GaussianNoise(sigma=noise)) sd = piff.StarData(image1, image1.true_center, weight) s = piff.Star(sd, None) try: s = model.initialize(s) s = model.fit(s) except RuntimeError as e: # Occasionally hsm fails. print('Caught ', e) continue print(s.fit.params) all_params.append(s.fit.params) var = np.var(all_params, axis=0) np.savez(file_name, var=var) var = np.load(file_name)['var'] print('params = ', fit.params) print('empirical var = ', var) print('piff estimate = ', fit.params_var) print('ratio = ', fit.params_var / var) print('max ratio = ', np.max(fit.params_var / var)) print('min ratio = ', np.min(fit.params_var / var)) print('mean ratio = ', np.mean(fit.params_var / var)) # Note: The fastfit=False estimates are better -- typically better than 10% # The fastfit=True estimates are much rougher. Especially size. Need rtol=0.3. np.testing.assert_allclose(fit.params_var, var, rtol=0.3)
def test_jacobian_smoke(kind): dudcol = 0.25 dudrow = 0.1 dvdcol = -0.4 dvdrow = 0.34 col = 5.6 row = -10.4 if kind == 'row-col': jac = Jacobian(col=col, row=row, dudcol=dudcol, dudrow=dudrow, dvdcol=dvdcol, dvdrow=dvdrow) elif kind == 'x-y': jac = Jacobian(x=col, y=row, dudx=dudcol, dudy=dudrow, dvdx=dvdcol, dvdy=dvdrow) else: wcs = galsim.JacobianWCS(dudx=dudcol, dudy=dudrow, dvdx=dvdcol, dvdy=dvdrow) jac = Jacobian(x=col, y=row, wcs=wcs) assert np.allclose(jac.row0, row) assert np.allclose(jac.col0, col) assert np.allclose(jac.dudcol, dudcol) assert np.allclose(jac.dudrow, dudrow) assert np.allclose(jac.dvdcol, dvdcol) assert np.allclose(jac.dvdrow, dvdrow) assert np.allclose(jac.det, dudcol * dvdrow - dudrow * dvdcol) assert np.allclose(jac.sdet, np.sqrt(np.abs(dudcol * dvdrow - dudrow * dvdcol))) assert np.allclose(jac.scale, np.sqrt(np.abs(dudcol * dvdrow - dudrow * dvdcol))) r, c = 20.0, -44.5 v, u = jac.get_vu(r, c) assert np.allclose(v, dvdrow * (r - row) + dvdcol * (c - col)) assert np.allclose(u, dudrow * (r - row) + dudcol * (c - col)) v, u = jac(r, c) assert np.allclose(v, dvdrow * (r - row) + dvdcol * (c - col)) assert np.allclose(u, dudrow * (r - row) + dudcol * (c - col)) v, u = 20.0, -44.5 r, c = jac.get_rowcol(v, u) assert np.allclose(r, (dudcol * v - dvdcol * u) / jac.det + row) assert np.allclose(c, (-dudrow * v + dvdrow * u) / jac.det + col) gs_wcs = jac.get_galsim_wcs() assert np.allclose(gs_wcs.dudx, jac.dudcol) assert np.allclose(gs_wcs.dudy, jac.dudrow) assert np.allclose(gs_wcs.dvdx, jac.dvdcol) assert np.allclose(gs_wcs.dvdy, jac.dvdrow) cpy_jac = jac.copy() cpy_jac.set_cen(row=-11, col=-12) assert np.allclose(jac.row0, row) assert np.allclose(jac.col0, col) assert np.allclose(cpy_jac.row0, -11) assert np.allclose(cpy_jac.col0, -12)
def _run_moments(*, n_sims, rng, dudx, dudy, dvdx, dvdy): """Run metacal on an image composed of stamps w/ constant noise. Parameters ---------- n_sims : int The number of objects to run. rng : np.random.RandomState An RNG to use. dudx : float The du/dx Jacobian component. dudy : float The du/dy Jacobian component. dydx : float The dv/dx Jacobian component. dvdy : float The dv/dy Jacobian component. Returns ------- result : dict A dictionary with each of the metacal catalogs. """ method = 'no_pixel' stamp_size = 33 psf_stamp_size = 33 cen = (stamp_size - 1) / 2 psf_cen = (psf_stamp_size - 1) / 2 s2n = 1e16 flux = 1e6 galsim_jac = galsim.JacobianWCS(dudx=dudx, dudy=dudy, dvdx=dvdx, dvdy=dvdy) gal = galsim.Exponential(half_light_radius=0.5).withFlux(flux) psf = galsim.Gaussian(fwhm=0.9).withFlux(1) obj = galsim.Convolve(gal, psf) obj_im = obj.drawImage(nx=111, ny=111).array noise = np.sqrt(np.sum(obj_im**2)) / s2n data = [] for ind in tqdm.trange(n_sims): ################################ # make the obs # psf psf_im = psf.drawImage(nx=psf_stamp_size, ny=psf_stamp_size, wcs=galsim_jac, method=method).array psf_noise = np.sqrt(np.sum(psf_im**2)) / 10000 wgt = np.ones_like(psf_im) / psf_noise**2 psf_im += (rng.normal(size=psf_im.shape) * psf_noise) psf_jac = ngmix.Jacobian(x=psf_cen, y=psf_cen, dudx=dudx, dudy=dudy, dvdx=dvdx, dvdy=dvdy) psf_obs = ngmix.Observation(image=psf_im, weight=wgt, jacobian=psf_jac) # now render object scale = psf_jac.scale shift = rng.uniform(low=-scale / 2, high=scale / 2, size=2) _obj = obj.shift(dx=shift[0], dy=shift[1]) xy = galsim_jac.toImage(galsim.PositionD(shift)) im = _obj.drawImage(nx=stamp_size, ny=stamp_size, wcs=galsim_jac, method=method).array jac = ngmix.Jacobian(x=cen + xy.x, y=cen + xy.y, dudx=dudx, dudy=dudy, dvdx=dvdx, dvdy=dvdy) wgt = np.ones_like(im) / noise**2 nse = rng.normal(size=im.shape) * noise im += (rng.normal(size=im.shape) * noise) obs = ngmix.Observation(image=im, weight=wgt, noise=nse, bmask=np.zeros_like(im, dtype=np.int32), ormask=np.zeros_like(im, dtype=np.int32), jacobian=jac, psf=psf_obs) # build the mbobs mbobs = ngmix.MultiBandObsList() obslist = ngmix.ObsList() obslist.append(obs) mbobs.append(obslist) mbobs.meta['id'] = ind + 1 # these settings do not matter that much I think mbobs[0].meta['Tsky'] = 1 mbobs[0].meta['magzp_ref'] = 26.5 mbobs[0][0].meta['orig_col'] = ind + 1 mbobs[0][0].meta['orig_row'] = ind + 1 ################################ # run the fitters try: res = _run_moments_fitter(mbobs, rng) except Exception as e: print(e) res = None if res is not None: data.append(res) if len(data) > 0: res = data else: res = None return res
import numpy as np import galsim import pytest from ..gauss_pix_psf import GaussPixPSF @pytest.mark.parametrize('wcs', [ galsim.PixelScale(0.263), galsim.PixelScale(0.5), galsim.JacobianWCS(-0.2634420129421214, 0.0006503502840044033, -0.0003966040125006026, -0.26354105026622404) ]) def test_gauss_pix_psf_smoke(wcs): psf_model = GaussPixPSF() psf = psf_model.getPSF(galsim.PositionD(x=1, y=2), wcs) psf_im = psf.drawImage(nx=53, ny=53, wcs=wcs).array seed = 4098 rng = np.random.RandomState(seed=seed) g1 = rng.normal() * 0.01 g2 = rng.normal() * 0.01 fwhm = (rng.uniform(low=-0.1, high=0.1) + 1.0) * 0.9 gs = galsim.Gaussian(fwhm=fwhm).shear(g1=g1, g2=g2).withFlux(1.0) test_im = gs.drawImage(nx=53, ny=53, wcs=wcs).array assert np.allclose(psf_im, test_im, atol=2e-5, rtol=0) def test_gauss_pix_psf_reproducible(): wcs = galsim.PixelScale(0.5)
def test_metacal_tracking(): """Test that the noise tracking works for the metacal use case involving deconvolution and reconvolution by almost the same PSF. This test is similar to the above test_uncorrelated_noise_tracking, except the modifications are based on what is done for the metacal procedure. """ import math def check_noise(noise_image, noise, msg): # A helper function to check that the current noise in the image is properly described # by the given CorrelatedNoise object noise2 = galsim.CorrelatedNoise(noise_image) print('noise = ', noise) print('noise2 = ', noise2) np.testing.assert_almost_equal(noise.getVariance(), noise2.getVariance(), decimal=CHECKNOISE_NDECIMAL, err_msg=msg + ': variance does not match.') cf_im1 = galsim.Image(8, 8, wcs=noise_image.wcs) cf_im2 = cf_im1.copy() noise.drawImage(image=cf_im1) noise2.drawImage(image=cf_im2) np.testing.assert_almost_equal(cf_im1.array, cf_im2.array, decimal=CHECKNOISE_NDECIMAL, err_msg=msg + ': image of cf does not match.') def check_symm_noise(noise_image, msg): # A helper funciton to see if a noise image has 4-fold symmetric noise. im2 = noise_image.copy() # Clear out any wcs to make the test simpler im2.wcs = galsim.PixelScale(1.) noise = galsim.CorrelatedNoise(im2) cf = noise.drawImage( galsim.Image(bounds=galsim.BoundsI(-1, 1, -1, 1), scale=1)) # First check the variance print('variance: ', cf(0, 0), noise.getVariance()) np.testing.assert_almost_equal(cf(0, 0) / noise.getVariance(), 1.0, decimal=VAR_NDECIMAL, err_msg=msg + ':: noise variance is wrong.') cf_plus = np.array([cf(1, 0), cf(-1, 0), cf(0, 1), cf(0, -1)]) cf_cross = np.array([cf(1, 1), cf(-1, -1), cf(-1, 1), cf(1, -1)]) print('plus pattern: ', cf_plus) print('diff relative to dc: ', (cf_plus - np.mean(cf_plus)) / cf(0, 0)) print('cross pattern: ', cf_cross) print('diff relative to dc: ', (cf_cross - np.mean(cf_cross)) / cf(0, 0)) # For now, don't make these asserts. Just print whether they will pass or fail. if True: if np.all(np.abs((cf_plus - np.mean(cf_plus)) / cf(0, 0)) < 0.01): print('plus test passes') else: print('*** FAIL ***') print(msg + ': plus pattern is not constant') if np.all( np.abs((cf_cross - np.mean(cf_cross)) / cf(0, 0)) < 0.01): print('cross test passes') else: print('*** FAIL ***') print(msg + ': cross pattern is not constant') else: np.testing.assert_almost_equal( (cf_plus - np.mean(cf_plus)) / cf(0, 0), 0.0, decimal=2, err_msg=msg + ': plus pattern is not constant') np.testing.assert_almost_equal( (cf_cross - np.mean(cf_cross)) / cf(0, 0), 0.0, decimal=2, err_msg=msg + ': cross pattern is not constant') seed = 1234567 # For use as a unit test, we need a specific seed #seed = 0 # During testing, it's useful to see how numbers flop around to know if # something is systematic or random. rng = galsim.BaseDeviate(seed) noise_var = 1.3 im_size = 1024 dg = 0.1 # This is bigger than metacal would use, but it makes the test easier. # Use a non-trivial wcs... #wcs = galsim.JacobianWCS(0.26, 0.04, -0.04, -0.26) # Rotation + flip. No shear. wcs = galsim.JacobianWCS(0.26, 0.03, 0.08, -0.21) # Fully complex #dudx = 0.12*0.26 #dudy = 1.10*0.26 #dvdx = -0.915*0.26 #dvdy = -0.04*0.26 #wcs = galsim.JacobianWCS(dudx, dudy, dvdx, dvdy) # Even more extreme # And an asymmetric PSF #orig_psf = galsim.Gaussian(fwhm=0.9).shear(g1=0.05, g2=0.03) # This one is small enough not to be fully Nyquist sampled, which makes things harder. orig_psf = galsim.Gaussian(fwhm=0.7).shear(g1=0.05, g2=0.03) # pixel is the pixel in world coords pixel = wcs.toWorld(galsim.Pixel(scale=1)) pixel_inv = galsim.Deconvolve(pixel) psf_image = orig_psf.drawImage(nx=im_size, ny=im_size, wcs=wcs) # Metacal only has access to the PSF as an image, so use this from here on. psf = galsim.InterpolatedImage(psf_image) psf_nopix = galsim.Convolve([psf, pixel_inv]) psf_inv = galsim.Deconvolve(psf) # Not what is done currently, but using a smoother target PSF helps make sure the # zeros in the deconvolved PSF get adequately zeroed out. def get_target_psf(psf): dk = 0.1 # The resolution in k space for the KImage small_kval = 1.e-2 # Find the k where the given psf hits this kvalue smaller_kval = 3.e-3 # Target PSF will have this kvalue at the same k kim = psf.drawKImage(scale=dk) karr_r = kim.real.array # Find the smallest r where the kval < small_kval nk = karr_r.shape[0] kx, ky = np.meshgrid(np.arange(-nk / 2, nk / 2), np.arange(-nk / 2, nk / 2)) ksq = (kx**2 + ky**2) * dk**2 ksq_max = np.min(ksq[karr_r < small_kval * psf.flux]) # We take our target PSF to be the (round) Gaussian that is even smaller at this ksq # exp(-0.5 * ksq_max * sigma_sq) = smaller_kval sigma_sq = -2. * np.log(smaller_kval) / ksq_max return galsim.Gaussian(sigma=np.sqrt(sigma_sq)) # The target PSF dilates the part without the pixel, but reconvolve by the real pixel. #psf_target_nopix = psf_nopix.dilate(1. + 2.*dg) #psf_target_nopix = orig_psf.dilate(1. + 4.*dg) psf_target_nopix = get_target_psf(psf_nopix.shear(g1=dg)) print('PSF target HLR = ', psf_target_nopix.calculateHLR()) print('PSF target FWHM = ', psf_target_nopix.calculateFWHM()) psf_target = galsim.Convolve([psf_target_nopix, pixel]) # Make an image of pure (white) Gaussian noise # Normally, there would be a galaxy in this image, but for the tests, we just have noise. obs_image = galsim.Image(im_size, im_size, init_value=0, wcs=wcs) obs_image.addNoise( galsim.GaussianNoise(rng=rng, sigma=math.sqrt(noise_var))) # The noise on this image should be describable as an UncorrelatedNoise object: noise = galsim.UncorrelatedNoise(variance=noise_var, wcs=wcs) check_noise(obs_image, noise, 'initial UncorrelatedNoise model is wrong') # Make an InterpolatedImage profile to use for manipulating this image # We can get away with no padding here, since our image is so large, but normally, you would # probably want to pad this with noise padding. ii = galsim.InterpolatedImage(obs_image, pad_factor=1) ii.noise = noise # If we draw it back, the attached noise attribute should still be correct check_noise(ii.drawImage(obs_image.copy(), method='no_pixel'), ii.noise, 'noise model is wrong for InterpolatedImage') # Here is the metacal process for which we want to understand the noise. # We'll try a few different methods. shear = galsim.Shear(g1=dg) sheared_obj = galsim.Convolve(ii, psf_inv).shear(shear) final_obj = galsim.Convolve(psf_target, sheared_obj) final_image = final_obj.drawImage(obs_image.copy(), method='no_pixel') try: check_symm_noise(final_image, 'Initial image') #didnt_fail = True # This bit doesn't work while we are not actually raising exceptions in check_symm_noise # So we expect to see **FAIL** text at this point. print( 'The above tests are expected to **FAIL**. This is not a problem.' ) didnt_fail = False except AssertionError as e: print('As expected initial image fails symmetric noise test:') print(e) didnt_fail = False if didnt_fail: assert False, 'Initial image was expected to fail symmetric noise test, but passed.' if True: print('\n\nStrategy 1:') # Strategy 1: Use the noise attribute attached to ii and use it to either whiten or # symmetrize the noise in the final image. # Note: The check_noise tests fail. I think because the convolve and deconvolve impose # a maxk = that of the psf. Which is too small for an accurate rendering of the # correlation function (here just an autocorrelation of a Pixel. # The whiten tests kind of work, but they add a lot of extra noise. Much more than # strategy 4 below. So the level of correlation remaining is pretty well below the # dc variance. Symmetrize doesn't add much noise, but the residual correlation is about # the same, which means it doesn't pass the test relative to the lower dc variance. # First, deconvolve and reconvolve by the same PSF: test_obj = galsim.Convolve([ii, psf, psf_inv]) # This fails... if False: check_noise( test_obj.drawImage(obs_image.copy(), method='no_pixel'), test_obj.noise, 'noise model is wrong after convolve/deconvolve by psf') # Now use a slightly dilated PSF for the reconvolution: test_obj = galsim.Convolve([ii, psf_target, psf_inv]) if False: check_noise( test_obj.drawImage(obs_image.copy(), method='no_pixel'), test_obj.noise, 'noise model is wrong for dilated target psf') # Finally, include the shear step. This was done above with sheared_obj, final_obj. if False: check_noise(final_image, final_obj.noise, 'noise model is wrong when including small shear') # If we whiten using this noise model, we should get back to white noise. t3 = time.time() final_image2 = final_image.copy() # Don't clobber the original final_var = final_image2.whitenNoise(final_obj.noise) t4 = time.time() print('Noise tracking method with whiten: final_var = ', final_var) print('Check: direct variance = ', np.var(final_image2.array)) check_symm_noise(final_image2, 'noise whitening does not work') print('Time for noise tracking with whiten = ', t4 - t3) # Using symmetrizeNoise should add less noise, but also work. t3 = time.time() final_image2 = final_image.copy() final_var = final_image2.symmetrizeNoise(final_obj.noise) t4 = time.time() print('Noise tracking method with symmetrize: final_var = ', final_var) print('Check: direct variance = ', np.var(final_image2.array)) check_symm_noise(final_image2, 'noise symmetrizing does not work') print('Time for noise tracking with symmetrize = ', t4 - t3) if True: print('\n\nStrategy 2:') # Strategy 2: Don't trust the noise tracking. Track a noise image through the same process # and then measure the noise from that image. Use it to either whiten or # symmetrize the noise in the final image. # Note: This method doesn't work any better. The added noise for whitening is even more # than strategy 1. And the remaining correlations are still similarly significant for the # symmetrize version. A little smaller than strategy 1, but not enough to pass our tests. # Make another noise image, since we don't actually have access to a pure noise image # for real objects. But we should be able to estimate the variance in the image. t3 = time.time() noise_image = galsim.Image(im_size, im_size, init_value=0, wcs=wcs) noise_image.addNoise( galsim.GaussianNoise(rng=rng, sigma=math.sqrt(noise_var))) noise_ii = galsim.InterpolatedImage(noise_image, pad_factor=1) sheared_noise_obj = galsim.Convolve(noise_ii, psf_inv).shear(shear) final_noise_obj = galsim.Convolve(psf_target, sheared_noise_obj) final_noise_image = final_noise_obj.drawImage(obs_image.copy(), method='no_pixel') # Use this to construct an appropriate CorrelatedNoise object noise = galsim.CorrelatedNoise(final_noise_image) t4 = time.time() final_image2 = final_image.copy() final_var = final_image2.whitenNoise(noise) t5 = time.time() check_noise( final_noise_image, noise, 'noise model is wrong when direct measuring the final noise image') print('Direct noise method with whiten: final_var = ', final_var) # Neither of these work currently, so maybe a bug in the whitening code? # Or possibly in my logic here. check_symm_noise( final_image2, 'whitening the noise using direct noise model failed') print('Time for direct noise with whitening = ', t5 - t3) t6 = time.time() final_image2 = final_image.copy() final_var = final_image2.symmetrizeNoise(noise) t7 = time.time() print('Direct noise method with symmetrize: final_var = ', final_var) check_symm_noise( final_image2, 'symmetrizing the noise using direct noise model failed') print('Time for direct noise with symmetrizing = ', t7 - t6 + t4 - t3) if False: print('\n\nStrategy 3:') # Strategy 3: Make a noise field and do the same operations as we do to the main image # except use the opposite shear value. Add this noise field to the final # image to get a symmetric noise field. # Note: This method works! But only for square pixels. However, they may be rotated # or flipped. Just not sheared. # Update: I think this method won't ever work for non-square pixels. The reason it works # for square pixels is that in that case, it is equivalent to strategy 4. t3 = time.time() # Make another noise image rev_image = galsim.Image(im_size, im_size, init_value=0, wcs=wcs) rev_image.addNoise( galsim.GaussianNoise(rng=rng, sigma=math.sqrt(noise_var))) rev_ii = galsim.InterpolatedImage(rev_image, pad_factor=1) rev_sheared_obj = galsim.Convolve(rev_ii, psf_inv).shear(-shear) rev_final_obj = galsim.Convolve(psf_target, rev_sheared_obj) rev_final_image = rev_final_obj.drawImage(obs_image.copy(), method='no_pixel') # Add the reverse-sheared noise image to the original image. final_image2 = final_image + rev_final_image t4 = time.time() # The noise variance in the end should be 2x as large as the original final_var = np.var(final_image2.array) print('Reverse shear method: final_var = ', final_var) check_symm_noise(final_image2, 'using reverse shear does not work') print('Time for reverse shear method = ', t4 - t3) if True: print('\n\nStrategy 4:') # Strategy 4: Make a noise field and do the same operations as we do to the main image, # then rotate it by 90 degress and add it to the final image. # This method works! Even for an arbitrarily sheared wcs. t3 = time.time() # Make another noise image noise_image = galsim.Image(im_size, im_size, init_value=0, wcs=wcs) noise_image.addNoise( galsim.GaussianNoise(rng=rng, sigma=math.sqrt(noise_var))) noise_ii = galsim.InterpolatedImage(noise_image, pad_factor=1) noise_sheared_obj = galsim.Convolve(noise_ii, psf_inv).shear(shear) noise_final_obj = galsim.Convolve(psf_target, noise_sheared_obj) noise_final_image = noise_final_obj.drawImage(obs_image.copy(), method='no_pixel') # Rotate the image by 90 degrees rot_noise_final_image = galsim.Image( np.ascontiguousarray(np.rot90(noise_final_image.array))) # Add the rotated noise image to the original image. final_image2 = final_image + rot_noise_final_image t4 = time.time() # The noise variance in the end should be 2x as large as the original final_var = np.var(final_image2.array) print('Rotate image method: final_var = ', final_var) check_symm_noise(final_image2, 'using rotated noise image does not work') print('Time for rotate image method = ', t4 - t3) if False: print('\n\nStrategy 5:') # Strategy 5: The same as strategy 3, except we target the effective net transformation # done by strategy 4. # I think this strategy probably can't work for non-square pixels, because the shear # happens before the convolution by the PSF. And if the wcs is non-square, then the # PSF is sheared relative to the pixels. That shear isn't being accounted for here, # so the net result isn't equivalent to rotating by 90 degrees at the end. t3 = time.time() # Make another noise image rev_image = galsim.Image(im_size, im_size, init_value=0, wcs=wcs) rev_image.addNoise( galsim.GaussianNoise(rng=rng, sigma=math.sqrt(noise_var))) rev_ii = galsim.InterpolatedImage(rev_image, pad_factor=1) # Find the effective transformation to apply in sky coordinates that matches what # you would get by applying the shear in sky coords, going to image coords and then # rotating by 90 degrees. # # If J is the jacobian of the wcs, and S1 is the applied shear, then we want to find # S2 such that J^-1 S2 = R90 J^-1 S1 jac = wcs.jacobian() J = jac.getMatrix() Jinv = jac.inverse().getMatrix() S1 = shear.getMatrix() R90 = np.array([[0, -1], [1, 0]]) S2 = J.dot(R90).dot(Jinv).dot(S1) scale, rev_shear, rev_theta, flip = galsim.JacobianWCS( *S2.flatten()).getDecomposition() # Flip should be False, and scale should be essentially 1.0. assert flip == False assert abs(scale - 1.) < 1.e-8 rev_sheared_obj = galsim.Convolve( rev_ii, psf_inv).rotate(rev_theta).shear(rev_shear) rev_final_obj = galsim.Convolve(psf_target, rev_sheared_obj) rev_final_image = rev_final_obj.drawImage(obs_image.copy(), method='no_pixel') # Add the reverse-sheared noise image to the original image. final_image2 = final_image + rev_final_image t4 = time.time() # The noise variance in the end should be 2x as large as the original final_var = np.var(final_image2.array) print('Alternate reverse shear method: final_var = ', final_var) check_symm_noise(final_image2, 'using alternate reverse shear does not work') print('Time for alternate reverse shear method = ', t4 - t3)
def interleaveImages(im_list, N, offsets, add_flux=True, suppress_warnings=False, catch_offset_errors=True): """ Interleaves the pixel values from two or more images and into a single larger image. This routine converts a list of images taken at a series of (uniform) dither offsets into a single higher resolution image, where the value in each final pixel is the observed pixel value from exactly one of the original images. It can be used to build a Nyquist-sampled image from a set of images that were observed with pixels larger than the Nyquist scale. In the original observed images, the integration of the surface brightness over the pixels is equivalent to convolution by the pixel profile and then sampling at the centers of the pixels. This procedure simulates an observation sampled at a higher resolution than the original images, while retaining the original pixel convolution. Such an image can be obtained in a fairly simple manner in simulations of surface brightness profiles by convolving them explicitly with the native pixel response and setting a lower sampling scale (or higher sampling rate) using the `pixel_scale' argument in drawImage() routine and setting the `method' parameter to `no_pixel'. However, pixel level detector effects can be included only on images drawn at the native pixel scale, which happen to be undersampled in most cases. Nyquist-sampled images that also include the effects of detector non-idealities can be obtained by drawing multiple undersampled images (with the detector effects included) that are offset from each other by a fraction of a pixel. This is similar to other procedures that build a higher resolution image from a set of low resolution images, such as MultiDrizzle and IMCOM. A disadvantage of this routine compared to ther others is that the images must be offset in equal steps in each direction. This is difficult to acheive with real observations but can be precisely acheived in a series of simulated images. An advantage of this procedure is that the noise in the final image is not correlated as the pixel values are each taken from just a single input image. Thus, this routine preserves the noise properties of the pixels. Here's an example script using this routine: Interleaving two Gaussian images along the x-axis ------------------------------------------------- >>> n = 2 >>> gal = galsim.Gaussian(sigma=2.8) >>> DX = numpy.arange(0.0,1.0,1./n) >>> DX -= DX.mean() >>> im_list, offsets = [], [] >>> for dx in DX: ... offset = galsim.PositionD(dx,0.0) ... offsets.append(offset) ... im = galsim.Image(16,16) ... gal.drawImage(image=im,offset=offset,scale=1.0) ... im.applyNonlinearity(lambda x: x - 0.01*x**2) # detector effects ... im_list.append(im) >>> img = galsim.utilities.interleaveImages(im_list=im_list,N=(n,1),offsets=offsets) @param im_list A list containing the galsim.Image instances to be interleaved. @param N Number of images to interleave in either directions. It can be of type `int' if equal number of images are interleaved in both directions or a list or tuple of two integers, containing the number of images in x and y directions respectively. @param offsets A list containing the offsets as galsim.PositionD instances corresponding to every image in `im_list'. The offsets must be spaced equally and must span an entire pixel area. The offset values must be symmetric around zero, hence taking positive and negative values, with upper and lower limits of +0.5 and -0.5 (limit values excluded). @param add_flux Should the routine add the fluxes of all the images (True) or average them (False)? [default: True] @param suppress_warnings Suppresses the warnings about the pixel scale of the output, if True. [default: False] @param catch_offset_errors Checks for the consistency of `offsets` with `N` and raises Errors if inconsistencies found (True). Recommended, but could slow down the routine a little. [default: True] @returns the interleaved image """ if isinstance(N,int): n1,n2 = N,N elif hasattr(N,'__iter__'): if len(N)==2: n1,n2 = N else: raise TypeError("'N' has to be a list or a tuple of two integers") if not (n1 == int(n1) and n2 == int(n2)): raise TypeError("'N' has to be of type int or a list or a tuple of two integers") n1 = int(n1) n2 = int(n2) else: raise TypeError("'N' has to be of type int or a list or a tuple of two integers") if len(im_list)<2: raise TypeError("'im_list' needs to have at least two instances of galsim.Image") if (n1*n2 != len(im_list)): raise ValueError("'N' is incompatible with the number of images in 'im_list'") if len(im_list)!=len(offsets): raise ValueError("'im_list' and 'offsets' must be lists of same length") for offset in offsets: if not isinstance(offset,galsim.PositionD): raise TypeError("'offsets' must be a list of galsim.PositionD instances") if not isinstance(im_list[0],galsim.Image): raise TypeError("'im_list' must be a list of galsim.Image instances") # These should be the same for all images in `im_list'. y_size, x_size = im_list[0].array.shape wcs = im_list[0].wcs for im in im_list[1:]: if not isinstance(im,galsim.Image): raise TypeError("'im_list' must be a list of galsim.Image instances") if im.array.shape != (y_size,x_size): raise ValueError("All galsim.Image instances in 'im_list' must be of the same size") if im.wcs != wcs: raise ValueError( "All galsim.Image instances in 'im_list' must have the same WCS") img_array = np.zeros((n2*y_size,n1*x_size)) # The tricky part - going from (x,y) Image coordinates to array indices # DX[i'] = -(i+0.5)/n+0.5 = -i/n + 0.5*(n-1)/n # i = -n DX[i'] + 0.5*(n-1) for k in range(len(offsets)): dx, dy = offsets[k].x, offsets[k].y i = int(round((n1-1)*0.5-n1*dx)) j = int(round((n2-1)*0.5-n2*dy)) if catch_offset_errors is True: err_i = (n1-1)*0.5-n1*dx - round((n1-1)*0.5-n1*dx) err_j = (n2-1)*0.5-n2*dy - round((n2-1)*0.5-n2*dy) tol = 1.e-6 if abs(err_i)>tol or abs(err_j)>tol: raise ValueError("'offsets' must be a list of galsim.PositionD instances with x " +"values spaced by 1/{0} and y values by 1/{1} around 0 for N = ".format(n1,n2)+str(N)) if i<0 or j<0 or i>=x_size or j>=y_size: raise ValueError("'offsets' must be a list of galsim.PositionD instances with x " +"values spaced by 1/{0} and y values by 1/{1} around 0 for N = ".format(n1,n2)+str(N)) img_array[j::n2,i::n1] = im_list[k].array[:,:] img = galsim.Image(img_array) if not add_flux: # Fix the flux normalization img /= 1.0*len(im_list) # Assign an appropriate WCS for the output if wcs is not None and wcs.isUniform(): jac = wcs.jacobian() dudx, dudy, dvdx, dvdy = jac.dudx, jac.dudy, jac.dvdx, jac.dvdy img_wcs = galsim.JacobianWCS(1.*dudx/n1,1.*dudy/n2,1.*dvdx/n1,1.*dvdy/n2) ## Since pixel scale WCS is not equal to its jacobian, checking if img_wcs is a pixel scale img_wcs_decomp = img_wcs.getDecomposition() if img_wcs_decomp[1].g==0: ## getDecomposition returns scale,shear,angle,flip img.wcs = galsim.PixelScale(img_wcs_decomp[0]) else: img.wcs = img_wcs elif suppress_warnings is False: import warnings warnings.warn("Interleaved image could not be assigned a WCS automatically.") # Assign a possibly non-trivial origin and warn if individual image have different origins. orig = im_list[0].origin() img.setOrigin(orig) for im in im_list[1:]: if not im.origin()==orig: import warnings warnings.warn("Images in `im_list' have multiple values for origin. Assigning the \ origin of the first Image instance in 'im_list' to the interleaved image.") break return img
def test_shapelet_fit(): """Test fitting a Shapelet decomposition of an image """ for method, norm in [('no_pixel','f'), ('sb','sb')]: # We fit a shapelet approximation of a distorted Moffat profile: flux = 20 psf = galsim.Moffat(beta=3.4, half_light_radius=1.2, flux=flux) psf = psf.shear(g1=0.11,g2=0.07).shift(0.03,0.04) scale = 0.2 pixel = galsim.Pixel(scale) conv = galsim.Convolve([psf,pixel]) im1 = conv.drawImage(scale=scale, method=method) sigma = 1.2 # Match half-light-radius as a decent first approximation. shapelet = galsim.Shapelet.fit(sigma, 10, im1, normalization=norm) print('fitted shapelet coefficients = ',shapelet.bvec) # Check flux print('flux = ',shapelet.flux,' cf. ',flux) np.testing.assert_almost_equal(shapelet.flux / flux, 1., 1, err_msg="Fitted shapelet has the wrong flux") # Test centroid print('centroid = ',shapelet.centroid,' cf. ',conv.centroid) np.testing.assert_almost_equal(shapelet.centroid.x, conv.centroid.x, 2, err_msg="Fitted shapelet has the wrong centroid (x)") np.testing.assert_almost_equal(shapelet.centroid.y, conv.centroid.y, 2, err_msg="Fitted shapelet has the wrong centroid (y)") # Test drawing image from shapelet im2 = im1.copy() shapelet.drawImage(im2, method=method) # Check that images are close to the same: print('norm(diff) = ',np.sum((im1.array-im2.array)**2)) print('norm(im) = ',np.sum(im1.array**2)) print('max(diff) = ',np.max(np.abs(im1.array-im2.array))) print('max(im) = ',np.max(np.abs(im1.array))) peak_scale = np.max(np.abs(im1.array))*3 # Check agreement to within 3% of peak value. np.testing.assert_almost_equal(im2.array/peak_scale, im1.array/peak_scale, decimal=2, err_msg="Shapelet version not a good match to original") # Remeasure -- should now be very close to the same. shapelet2 = galsim.Shapelet.fit(sigma, 10, im2, normalization=norm) np.testing.assert_equal(shapelet.sigma, shapelet2.sigma, err_msg="Second fitted shapelet has the wrong sigma") np.testing.assert_equal(shapelet.order, shapelet2.order, err_msg="Second fitted shapelet has the wrong order") np.testing.assert_almost_equal(shapelet.bvec, shapelet2.bvec, 6, err_msg="Second fitted shapelet coefficients do not match original") # Test drawing off center im2 = im1.copy() offset = galsim.PositionD(0.3,1.4) shapelet.drawImage(im2, method=method, offset=offset) shapelet2 = galsim.Shapelet.fit(sigma, 10, im2, normalization=norm, center=im2.true_center + offset) np.testing.assert_equal(shapelet.sigma, shapelet2.sigma, err_msg="Second fitted shapelet has the wrong sigma") np.testing.assert_equal(shapelet.order, shapelet2.order, err_msg="Second fitted shapelet has the wrong order") np.testing.assert_almost_equal(shapelet.bvec, shapelet2.bvec, 6, err_msg="Second fitted shapelet coefficients do not match original") assert_raises(ValueError, galsim.Shapelet.fit, sigma, 10, im1, normalization='invalid') # Haven't gotten around to implementing this yet... im2.wcs = galsim.JacobianWCS(0.2,0.01,0.01,0.2) with assert_raises(NotImplementedError): galsim.Shapelet.fit(sigma, 10, im2)
def main(argv): ## fixed parameters random_seed = 314 rng = galsim.BaseDeviate(random_seed) random_dir = galsim.UniformDeviate(rng) poisson_noise = galsim.PoissonNoise(rng) dither_i = 22535 use_SCA = 1 filter_ = 'H158' stamp_size = 32 hlr = 1.0 bpass = wfirst.getBandpasses(AB_zeropoint=True)[filter_] galaxy_sed_n = galsim.SED('Mrk_33_spec.dat', wave_type='Ang', flux_type='flambda') ## variable arguments gal_num = int(sys.argv[1]) PA1 = int(sys.argv[2]) PA2 = int(sys.argv[3]) galaxy_model = sys.argv[4] PSF_model = sys.argv[5] shape = sys.argv[6] output_name = sys.argv[7] # when using more galaxies than the length of truth file. res_noshear = np.zeros(gal_num, dtype=[('ind', int), ('flux', float), ('g1', float), ('g2', float), ('e1', float), ('e2', float), ('snr', float), ('hlr', float), ('flags', int)]) res_1p = np.zeros(gal_num, dtype=[('ind', int), ('flux', float), ('g1', float), ('g2', float), ('e1', float), ('e2', float), ('snr', float), ('hlr', float), ('flags', int)]) res_1m = np.zeros(gal_num, dtype=[('ind', int), ('flux', float), ('g1', float), ('g2', float), ('e1', float), ('e2', float), ('snr', float), ('hlr', float), ('flags', int)]) res_2p = np.zeros(gal_num, dtype=[('ind', int), ('flux', float), ('g1', float), ('g2', float), ('e1', float), ('e2', float), ('snr', float), ('hlr', float), ('flags', int)]) res_2m = np.zeros(gal_num, dtype=[('ind', int), ('flux', float), ('g1', float), ('g2', float), ('e1', float), ('e2', float), ('snr', float), ('hlr', float), ('flags', int)]) if shape == 'metacal': res_tot = [res_noshear, res_1p, res_1m, res_2p, res_2m] elif shape == 'noboot': res_tot = [res_noshear, res_1p, res_1m, res_2p, res_2m] elif shape == 'ngmix': res_tot = [res_noshear] PSF = getPSF(PSF_model, use_SCA, filter_, bpass) position_angle1 = PA1 #degrees position_angle2 = PA2 #degrees wcs1, sky_level1 = get_wcs(dither_i, use_SCA, filter_, stamp_size, position_angle1) wcs2, sky_level2 = get_wcs(dither_i, use_SCA, filter_, stamp_size, position_angle2) wcs = [wcs1, wcs2] sky_level = [sky_level1, sky_level2] t0 = time.time() for i_gal in range(gal_num): if i_gal % size != rank: continue if i_gal % 100 == 0: print('rank', rank, 'object number, ', i_gal) gal_model = None st_model = None if galaxy_model == "Gaussian": tot_mag = np.random.choice(cat) sed = galsim.SED('CWW_E_ext.sed', 'A', 'flambda') sed = sed.withMagnitude(tot_mag, bpass) flux = sed.calculateFlux(bpass) gal_model = galsim.Gaussian( half_light_radius=hlr, flux=1. ) # needs to normalize the flux before multiplying by sed. For bdf, there are bulge, disk, knots fractions to sum to 1. ## making galaxy sed #knots = galsim.RandomKnots(10, half_light_radius=1.3, flux=100) #knots = make_sed_model(galsim.ChromaticObject(knots), galaxy_sed_n, filter_, bpass) #gal_model = galsim.Add([gal_model, knots]) gal_model = sed * gal_model ## shearing if i_gal % 2 == 0: gal_model = gal_model.shear(g1=0.02, g2=0) g1 = 0.02 g2 = 0 else: gal_model = gal_model.shear(g1=-0.02, g2=0) g1 = -0.02 g2 = 0 elif galaxy_model == "exponential": tot_mag = np.random.choice(cat) sed = galsim.SED('CWW_E_ext.sed', 'A', 'flambda') sed = sed.withMagnitude(tot_mag, bpass) flux = sed.calculateFlux(bpass) gal_model = galsim.Exponential(half_light_radius=hlr, flux=1.) ## making galaxy sed ## random knots should be used for bdf model #knots = galsim.RandomKnots(10, half_light_radius=1.3, flux=1.) #knots = make_sed_model(galsim.ChromaticObject(knots), galaxy_sed_n, filter_, bpass) #gal_model = galsim.Add([gal_model, knots]) gal_model = sed * gal_model ## shearing if i_gal % 2 == 0: gal_model = gal_model.shear(g1=0, g2=0.02) g1 = 0 g2 = 0.02 else: gal_model = gal_model.shear(g1=0, g2=-0.02) g1 = 0 g2 = -0.02 gal_model = gal_model * galsim.wfirst.collecting_area * galsim.wfirst.exptime flux_ = gal_model.calculateFlux(bpass) #mag_ = gal_model.calculateMagnitude(bpass) # This makes the object achromatic, which speeds up drawing and convolution gal_model = gal_model.evaluateAtWavelength(bpass.effective_wavelength) # Reassign correct flux gal_model = gal_model.withFlux(flux_) gal_model = galsim.Convolve(gal_model, PSF) st_model = galsim.DeltaFunction(flux=1.) st_model = st_model.evaluateAtWavelength(bpass.effective_wavelength) # reassign correct flux starflux = 1. st_model = st_model.withFlux(starflux) st_model = galsim.Convolve(st_model, PSF) stamp_size_factor = old_div( int(gal_model.getGoodImageSize(wfirst.pixel_scale)), stamp_size) if stamp_size_factor == 0: stamp_size_factor = 1 # Galsim world coordinate object (ra,dec) """ ra = cat[i_gal]['ra'] dec = cat[i_gal]['dec'] int_e1 = cat[i_gal]['int_e1'] int_e2 = cat[i_gal]['int_e2'] radec = galsim.CelestialCoord(ra*galsim.radians, dec*galsim.radians) # Galsim image coordinate object xy = wcs.toImage(radec) # Galsim integer image coordinate object xyI = galsim.PositionI(int(xy.x),int(xy.y)) """ #xyI = galsim.PositionI(int(stamp_size_factor*stamp_size), int(stamp_size_factor*stamp_size)) #b = galsim.BoundsI( xmin=1, # xmax=xyI.x, # ymin=1, # ymax=xyI.y) #print(xyI.x, int(stamp_size_factor*stamp_size), xyI.x-old_div(int(stamp_size_factor*stamp_size),2)+1) #print(b) # Create postage stamp for galaxy #print("galaxy ", i_gal, ra, dec, int_e1, int_e2) ## translational dither check (multiple exposures) """ position_angle1=20*random_dir() #degrees position_angle2=position_angle1 #degrees wcs1, sky_level1 = get_wcs(dither_i, use_SCA, filter_, stamp_size, position_angle1) wcs2, sky_level2 = get_wcs(dither_i, use_SCA, filter_, stamp_size, position_angle2) wcs=[wcs1, wcs2] sky_level=[sky_level1, sky_level2] """ sca_center = [ wcs[0].toWorld( galsim.PositionI(old_div(wfirst.n_pix, 2), old_div(wfirst.n_pix, 2))), wcs[1].toWorld( galsim.PositionI(old_div(wfirst.n_pix, 2), old_div(wfirst.n_pix, 2))) ] gal_radec = sca_center[0] thetas = [position_angle1 * (np.pi / 180) * galsim.radians] offsets = [] gals = [] psfs = [] skys = [] for i in range(2): gal_stamp = None psf_stamp = None xy = wcs[i].toImage(gal_radec) # galaxy position xyI = galsim.PositionI(int(xy.x), int(xy.y)) b = galsim.BoundsI( xmin=xyI.x - old_div(int(stamp_size_factor * stamp_size), 2) + 1, ymin=xyI.y - old_div(int(stamp_size_factor * stamp_size), 2) + 1, xmax=xyI.x + old_div(int(stamp_size_factor * stamp_size), 2), ymax=xyI.y + old_div(int(stamp_size_factor * stamp_size), 2)) #---------------------------------------# # if the image does not use a real wcs. # #---------------------------------------# #b = galsim.BoundsI( xmin=1, # xmax=int(stamp_size_factor*stamp_size), # ymin=1, # ymax=int(stamp_size_factor*stamp_size)) gal_stamp = galsim.Image(b, wcs=wcs[i]) #scale=wfirst.pixel_scale) psf_stamp = galsim.Image(b, wcs=wcs[i]) #scale=wfirst.pixel_scale) ## translational dithering test #dx = 0 #random_dir() - 0.5 #dy = 0 #random_dir() - 0.5 #offset = np.array((dx,dy)) offset = xy - gal_stamp.true_center # original galaxy position - stamp center gal_model.drawImage(image=gal_stamp, offset=offset) st_model.drawImage(image=psf_stamp, offset=offset) sigma = wfirst.read_noise read_noise = galsim.GaussianNoise(rng, sigma=sigma) im, sky_image = add_background(gal_stamp, sky_level[i], b, thermal_backgrounds=None, filter_='H158', phot=False) #im.addNoise(read_noise) gal_stamp = add_poisson_noise(rng, im, sky_image=sky_image, phot=False) #sky_image = add_poisson_noise(rng, sky_image, sky_image=sky_image, phot=False) gal_stamp -= sky_image # set a simple jacobian to the stamps before sending them to ngmix # old center of the stamp origin_x = gal_stamp.origin.x origin_y = gal_stamp.origin.y gal_stamp.setOrigin(0, 0) psf_stamp.setOrigin(0, 0) new_pos = galsim.PositionD(xy.x - origin_x, xy.y - origin_y) wcs_transf = gal_stamp.wcs.affine(image_pos=new_pos) new_wcs = galsim.JacobianWCS(wcs_transf.dudx, wcs_transf.dudy, wcs_transf.dvdx, wcs_transf.dvdy) gal_stamp.wcs = new_wcs psf_stamp.wcs = new_wcs offsets.append(offset) gals.append(gal_stamp) psfs.append(psf_stamp) skys.append(sky_image) print(gals) res_tot = get_coadd_shape(cat, gals, psfs, offsets, skys, i_gal, hlr, res_tot, g1, g2, shape) print(res_tot) ## send and receive objects from one processor to others if rank != 0: # send res_tot to rank 0 processor comm.send(res_tot, dest=0) else: for i in range(comm.size): if i == 0: continue # for other processors, receive res_tot. res_ = comm.recv(source=i) for j in range(len(res_tot)): for col in res_tot[j].dtype.names: res_tot[j][col] += res_[j][col] if rank == 0: dirr = output_name for i in range(len(res_tot)): fio.write(dirr + '_sim_' + str(i) + '.fits', res_tot[i]) if rank == 0: bias = residual_bias(res_tot, shape) #final = residual_bias_correction(res_tot,R11,R22,R12,R21) print(time.time() - t0) return None