def test_invalid_config(): # Test a few invalid uses of the config parsing. if __name__ == '__main__': logger = piff.config.setup_logger(verbose=2) else: logger = piff.config.setup_logger(log_file='output/test_invalid_config.log') image_file = os.path.join('output','simple_image.fits') cat_file = os.path.join('output','simple_cat.fits') psf_file = os.path.join('output','simple_psf.fits') config = { 'input' : { 'image_file_name' : image_file, 'cat_file_name' : cat_file, 'flag_col' : 'flag', 'use_flag' : 1, 'skip_flag' : 4, }, 'psf' : { 'model' : { 'type' : 'Gaussian', 'fastfit': True, 'include_pixel': False}, 'interp' : { 'type' : 'Mean' }, 'max_iter' : 10, 'chisq_thresh' : 0.2, }, 'output' : { 'file_name' : psf_file }, } # Invalid variable specification with np.testing.assert_raises(ValueError): piff.parse_variables(config, ['verbose:0'], logger=logger) # process needs both input and psf with np.testing.assert_raises(ValueError): piff.process(config={'input':config['input']}, logger=logger) with np.testing.assert_raises(ValueError): piff.process(config={'psf':config['psf']}, logger=logger) # piffify also needs output with np.testing.assert_raises(ValueError): piff.piffify(config={'input':config['input']}, logger=logger) with np.testing.assert_raises(ValueError): piff.piffify(config={'psf':config['psf']}, logger=logger) with np.testing.assert_raises(ValueError): piff.piffify(config={'input':config['input'], 'psf':config['psf']}, logger=logger) # plotify doesn't need psf, but needs a 'file_name' in output with np.testing.assert_raises(ValueError): piff.plotify(config={'input':config['input']}, logger=logger) with np.testing.assert_raises(ValueError): piff.plotify(config={'input':config['input'], 'output':{}}, logger=logger) # Error if missing either model or interp config2 = copy.deepcopy(config) del config2['psf']['model'] with np.testing.assert_raises(ValueError): piff.piffify(config2, logger) config2['psf']['model'] = config['psf']['model'] del config2['psf']['interp'] with np.testing.assert_raises(ValueError): piff.piffify(config2, logger)
def test_config(): # Take DES test image, and test doing a psf run with kNN interpolator # Now test running it via the config parser psf_file = os.path.join('output', 'knn_psf.fits') config = { 'input': { 'image_file_name': 'input/DECam_00241238_01.fits.fz', 'cat_file_name': 'input/DECam_00241238_01_psfcat_tb_maxmag_17.0_magcut_3.0_findstars.fits', # What hdu is everything in? 'image_hdu': 1, 'badpix_hdu': 2, 'weight_hdu': 3, 'cat_hdu': 2, # What columns in the catalog have things we need? 'x_col': 'XWIN_IMAGE', 'y_col': 'YWIN_IMAGE', 'ra': 'TELRA', 'dec': 'TELDEC', 'gain': 'GAINA', 'sky_col': 'BACKGROUND', # How large should the postage stamp cutouts of the stars be? 'stamp_size': 31, }, 'psf': { 'model': { 'type': 'GSObjectModel', 'fastfit': True, 'gsobj': 'galsim.Gaussian(sigma=1.0)' }, 'interp': { 'type': 'kNNInterp', 'keys': ['u', 'v'], 'n_neighbors': 115, } }, 'output': { 'file_name': psf_file }, } if __name__ != '__main__': config['verbose'] = 0 config['input']['nstars'] = 20 config['psf']['interp']['n_neighbors'] = 19 test_factor = 0.04 else: test_factor = 0.01 psf = piff.process(config) # by using n_neighbors = 115, when there are only 117 stars in the catalog, we should expect # that the standard deviation of the interpolated parameters should be small, since almost the # same set of stars are being averaged in every case. nstars = len(psf.stars) np.testing.assert_array_less( np.std([s.fit.params for s in psf.stars], axis=0), test_factor * np.mean([s.fit.params for s in psf.stars], axis=0), err_msg="Interpolated parameters show too much variation.")
def test_config(): # Take DES test image, and test doing a psf run with kNN interpolator # Now test running it via the config parser psf_file = os.path.join('output','knn_psf.fits') config = { 'input' : { 'image_file_name' : 'input/DECam_00241238_01.fits.fz', 'cat_file_name' : 'input/DECam_00241238_01_psfcat_tb_maxmag_17.0_magcut_3.0_findstars.fits', # What hdu is everything in? 'image_hdu': 1, 'badpix_hdu': 2, 'weight_hdu': 3, 'cat_hdu': 2, # What columns in the catalog have things we need? 'x_col': 'XWIN_IMAGE', 'y_col': 'YWIN_IMAGE', 'ra': 'TELRA', 'dec': 'TELDEC', 'gain': 'GAINA', 'sky_col': 'BACKGROUND', # How large should the postage stamp cutouts of the stars be? 'stamp_size': 31, }, 'psf' : { 'model' : { 'type': 'GSObjectModel', 'fastfit': True, 'gsobj': 'galsim.Gaussian(sigma=1.0)' }, 'interp' : { 'type': 'kNNInterp', 'keys': ['u', 'v'], 'n_neighbors': 115,} }, 'output' : { 'file_name' : psf_file }, } if __name__ != '__main__': config['verbose'] = 0 config['input']['nstars'] = 20 config['psf']['interp']['n_neighbors'] = 19 test_factor = 0.04 else: test_factor = 0.01 psf = piff.process(config) # by using n_neighbors = 115, when there are only 117 stars in the catalog, we should expect # that the standard deviation of the interpolated parameters should be small, since almost the # same set of stars are being averaged in every case. nstars = len(psf.stars) np.testing.assert_array_less( np.std([s.fit.params for s in psf.stars], axis=0), test_factor*np.mean([s.fit.params for s in psf.stars], axis=0), err_msg="Interpolated parameters show too much variation.")
def test_focal(): """This test uses 2 input files and two catalogs, but does the interpolation over the whole field of view. """ # Give them different wcs's. # The centers should be separated by ~0.25 arcsec/pixel * 2048 pixels / cos(dec) = 565 arcsec # The actual separation of 10 arcmin gives a bit of a gap between the chips. wcs1 = galsim.TanWCS( galsim.AffineTransform(0.26, 0.05, -0.08, -0.24, galsim.PositionD(1024,1024)), galsim.CelestialCoord(-5 * galsim.arcmin, -25 * galsim.degrees) ) wcs2 = galsim.TanWCS( galsim.AffineTransform(0.25, -0.02, 0.01, 0.24, galsim.PositionD(1024,1024)), galsim.CelestialCoord(5 * galsim.arcmin, -25 * galsim.degrees) ) field_center = galsim.CelestialCoord(0 * galsim.degrees, -25 * galsim.degrees) if __name__ == '__main__': nstars = 20 # per ccd else: nstars = 3 # per ccd rng = np.random.RandomState(1234) x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 u, v = field_center.project_rad(*wcs1._radec(x.copy(),y.copy()), projection='gnomonic') e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u*v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u*v data1 = np.array(list(zip(x,y,e1,e2,s)), dtype=[ ('x',float), ('y',float), ('e1',float), ('e2',float), ('s',float) ]) np.testing.assert_array_equal(data1['x'] , x) np.testing.assert_array_equal(data1['y'] , y) np.testing.assert_array_equal(data1['e1'] , e1) np.testing.assert_array_equal(data1['e2'] , e2) np.testing.assert_array_equal(data1['s'] , s) im1 = drawImage(2048, 2048, wcs1, x, y, e1, e2, s) im1.write('output/test_focal_im1.fits') fitsio.write('output/test_focal_cat1.fits', data1, clobber=True) x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 u, v = field_center.project_rad(*wcs2._radec(x.copy(),y.copy()), projection='gnomonic') # Same functions of u,v, but using the positions on chip 2 e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u*v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u*v data2 = np.array(list(zip(x,y,e1,e2,s)), dtype=[ ('x',float), ('y',float), ('e1',float), ('e2',float), ('s',float) ]) im2 = drawImage(2048, 2048, wcs2, x, y, e1, e2, s) im2.write('output/test_focal_im2.fits') fitsio.write('output/test_focal_cat2.fits', data2, clobber=True) # Try to fit with the right model (Moffat) and interpolant (2nd order polyomial) # Should work very well, since no noise. config = { 'input' : { 'image_file_name' : 'output/test_focal_im?.fits', 'cat_file_name' : 'output/test_focal_cat?.fits', 'x_col' : 'x', 'y_col' : 'y', 'ra' : 0., 'dec' : -25., }, 'psf' : { 'type' : 'Simple', 'model' : { 'type' : 'Moffat', 'beta' : 2.5 }, 'interp' : { 'type' : 'Polynomial', 'order' : 2 } } } if __name__ != '__main__': config['verbose'] = 0 psf = piff.process(config) for data, wcs in [(data1,wcs1), (data2,wcs2)]: for k in range(nstars): x = data['x'][k] y = data['y'][k] e1 = data['e1'][k] e2 = data['e2'][k] s = data['s'][k] #print('k,x,y = ',k,x,y) #print(' true s,e1,e2 = ',s,e1,e2) image_pos = galsim.PositionD(x,y) star = piff.Star.makeTarget(x=x, y=y, wcs=wcs, stamp_size=48, pointing=field_center) star = psf.drawStar(star) #print(' fitted s,e1,e2 = ',star.fit.params) np.testing.assert_almost_equal(star.fit.params, [s,e1,e2], decimal=6)
def test_single(): """Same as test_focal, but using the SingleCCD PSF type, which does a separate fit on each CCD. """ wcs1 = galsim.TanWCS( galsim.AffineTransform(0.26, 0.05, -0.08, -0.24, galsim.PositionD(1024,1024)), galsim.CelestialCoord(-5 * galsim.arcmin, -25 * galsim.degrees) ) wcs2 = galsim.TanWCS( galsim.AffineTransform(0.25, -0.02, 0.01, 0.24, galsim.PositionD(1024,1024)), galsim.CelestialCoord(5 * galsim.arcmin, -25 * galsim.degrees) ) field_center = galsim.CelestialCoord(0 * galsim.degrees, -25 * galsim.degrees) if __name__ == '__main__': nstars = 20 # per ccd else: nstars = 6 # per ccd rng = np.random.RandomState(1234) x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 u, v = field_center.project_rad(*wcs1._radec(x.copy(),y.copy()), projection='gnomonic') e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u*v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u*v data1 = np.array(list(zip(x,y,e1,e2,s)), dtype=[ ('x',float), ('y',float), ('e1',float), ('e2',float), ('s',float) ]) im1 = drawImage(2048, 2048, wcs1, x, y, e1, e2, s) im1.write('output/test_single_im1.fits') fitsio.write('output/test_single_cat1.fits', data1, clobber=True) x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 u, v = field_center.project_rad(*wcs2._radec(x.copy(),y.copy()), projection='gnomonic') # Same functions of u,v, but using the positions on chip 2 e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u*v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u*v data2 = np.array(list(zip(x,y,e1,e2,s)), dtype=[ ('x',float), ('y',float), ('e1',float), ('e2',float), ('s',float) ]) im2 = drawImage(2048, 2048, wcs2, x, y, e1, e2, s) im2.write('output/test_single_im2.fits') fitsio.write('output/test_single_cat2.fits', data2, clobber=True) # Try to fit with the right model (Moffat) and interpolant (2nd order polyomial) # Should work very well, since no noise. config = { 'input' : { # A third way to input these same file names. Use GalSim config values and # explicitly specify the number of images to read 'nimages' : 2, 'image_file_name' : { 'type' : 'FormattedStr', 'format' : '%s/test_single_im%d.fits', 'items' : [ 'output', '$image_num+1' ] }, 'cat_file_name' : { 'type' : 'FormattedStr', 'format' : '%s/test_single_cat%d.fits', 'items' : [ 'output', '$image_num+1' ] }, # Use chipnum = 1,2 rather than the default 0,1. 'chipnum' : '$image_num+1', 'x_col' : 'x', 'y_col' : 'y', 'ra' : 0., 'dec' : -25., }, 'psf' : { 'type' : 'SingleChip', 'model' : { 'type' : 'Moffat', 'beta' : 2.5 }, 'interp' : { 'type' : 'Polynomial', 'order' : 2 } }, } if __name__ != '__main__': config['verbose'] = 0 psf = piff.process(config) for chipnum, data, wcs in [(1,data1,wcs1), (2,data2,wcs2)]: for k in range(nstars): x = data['x'][k] y = data['y'][k] e1 = data['e1'][k] e2 = data['e2'][k] s = data['s'][k] #print('k,x,y = ',k,x,y) #print(' true s,e1,e2 = ',s,e1,e2) image_pos = galsim.PositionD(x,y) star = piff.Star.makeTarget(x=x, y=y, wcs=wcs, stamp_size=48, pointing=field_center, chipnum=chipnum) star = psf.drawStar(star) #print(' fitted s,e1,e2 = ',star.fit.params) np.testing.assert_almost_equal(star.fit.params, [s,e1,e2], decimal=6)
def test_wrongwcs(): """Same as test_focal, but the images are written out with the wrong wcs. """ wcs1 = galsim.TanWCS( galsim.AffineTransform(0.26, 0.05, -0.08, -0.24, galsim.PositionD(1024,1024)), galsim.CelestialCoord(-5 * galsim.arcmin, -25 * galsim.degrees) ) wcs2 = galsim.TanWCS( galsim.AffineTransform(0.25, -0.02, 0.01, 0.24, galsim.PositionD(1024,1024)), galsim.CelestialCoord(5 * galsim.arcmin, -25 * galsim.degrees) ) wrong_wcs = galsim.TanWCS( galsim.AffineTransform(0.25, 0, 0, 0.25, galsim.PositionD(1024,1024)), galsim.CelestialCoord(0 * galsim.arcmin, -25 * galsim.degrees) ) field_center = galsim.CelestialCoord(0 * galsim.degrees, -25 * galsim.degrees) if __name__ == '__main__': nstars = 20 # per ccd else: nstars = 3 # per ccd rng = np.random.RandomState(1234) x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 u, v = field_center.project_rad(*wcs1._radec(x.copy(),y.copy()), projection='gnomonic') e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u*v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u*v data1 = np.array(list(zip(x,y,e1,e2,s)), dtype=[ ('x',float), ('y',float), ('e1',float), ('e2',float), ('s',float) ]) im1 = drawImage(2048, 2048, wcs1, x, y, e1, e2, s) x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 u, v = field_center.project_rad(*wcs2._radec(x.copy(),y.copy()), projection='gnomonic') # Same functions of u,v, but using the positions on chip 2 e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u*v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u*v data2 = np.array(list(zip(x,y,e1,e2,s)), dtype=[ ('x',float), ('y',float), ('e1',float), ('e2',float), ('s',float) ]) im2 = drawImage(2048, 2048, wcs2, x, y, e1, e2, s) # Put in the wrong wcs before writing them to files. im1.wcs = im2.wcs = wrong_wcs im1.write('output/test_wrongwcs_im1.fits') im2.write('output/test_wrongwcs_im2.fits') fitsio.write('output/test_wrongwcs_cat1.fits', data1, clobber=True) fitsio.write('output/test_wrongwcs_cat2.fits', data2, clobber=True) config = { 'modules' : [ 'custom_wcs' ], 'input' : { 'dir' : 'output', # Normally more convenient to use a glob string, but an explicit list is also allowed. 'image_file_name' : ['test_wrongwcs_im1.fits', 'test_wrongwcs_im2.fits'], 'cat_file_name' : ['test_wrongwcs_cat1.fits', 'test_wrongwcs_cat2.fits'], 'x_col' : 'x', 'y_col' : 'y', 'ra' : 0., 'dec' : -25., # But here tell Piff the correct WCS to use. This uses a custom WCS builder, # mostly so we can test the 'modules' option. In practice, you might use a # galsim_extra Pixmappy WCS class. Or maybe an LSST DM WCS. 'wcs' : { 'type' : 'Custom' } }, 'psf' : { 'type' : 'Simple', 'model' : { 'type' : 'Moffat', 'beta' : 2.5 }, 'interp' : { 'type' : 'Polynomial', 'order' : 2 } }, } if __name__ != '__main__': config['verbose'] = 0 psf = piff.process(config) for data, wcs in [(data1,wcs1), (data2,wcs2)]: for k in range(nstars): x = data['x'][k] y = data['y'][k] e1 = data['e1'][k] e2 = data['e2'][k] s = data['s'][k] #print('k,x,y = ',k,x,y) #print(' true s,e1,e2 = ',s,e1,e2) image_pos = galsim.PositionD(x,y) star = piff.Star.makeTarget(x=x, y=y, wcs=wcs, stamp_size=48, pointing=field_center) star = psf.drawStar(star) #print(' fitted s,e1,e2 = ',star.fit.params) np.testing.assert_almost_equal(star.fit.params, [s,e1,e2], decimal=6)
def test_focal(): """This test uses 2 input files and two catalogs, but does the interpolation over the whole field of view. """ # Give them different wcs's. # The centers should be separated by ~0.25 arcsec/pixel * 2048 pixels / cos(dec) = 565 arcsec # The actual separation of 10 arcmin gives a bit of a gap between the chips. wcs1 = galsim.TanWCS( galsim.AffineTransform(0.26, 0.05, -0.08, -0.24, galsim.PositionD(1024, 1024)), galsim.CelestialCoord(-5 * galsim.arcmin, -25 * galsim.degrees)) wcs2 = galsim.TanWCS( galsim.AffineTransform(0.25, -0.02, 0.01, 0.24, galsim.PositionD(1024, 1024)), galsim.CelestialCoord(5 * galsim.arcmin, -25 * galsim.degrees)) field_center = galsim.CelestialCoord(0 * galsim.degrees, -25 * galsim.degrees) if __name__ == '__main__': nstars = 20 # per ccd else: nstars = 3 # per ccd rng = np.random.RandomState(1234) x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 u, v = field_center.project_rad(*wcs1._radec(x.copy(), y.copy()), projection='gnomonic') e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u * v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u * v data1 = np.array(list(zip(x, y, e1, e2, s)), dtype=[('x', float), ('y', float), ('e1', float), ('e2', float), ('s', float)]) np.testing.assert_array_equal(data1['x'], x) np.testing.assert_array_equal(data1['y'], y) np.testing.assert_array_equal(data1['e1'], e1) np.testing.assert_array_equal(data1['e2'], e2) np.testing.assert_array_equal(data1['s'], s) im1 = drawImage(2048, 2048, wcs1, x, y, e1, e2, s) im1.write('output/test_focal_im1.fits') fitsio.write('output/test_focal_cat1.fits', data1, clobber=True) x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 u, v = field_center.project_rad(*wcs2._radec(x.copy(), y.copy()), projection='gnomonic') # Same functions of u,v, but using the positions on chip 2 e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u * v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u * v data2 = np.array(list(zip(x, y, e1, e2, s)), dtype=[('x', float), ('y', float), ('e1', float), ('e2', float), ('s', float)]) im2 = drawImage(2048, 2048, wcs2, x, y, e1, e2, s) im2.write('output/test_focal_im2.fits') fitsio.write('output/test_focal_cat2.fits', data2, clobber=True) # Try to fit with the right model (Moffat) and interpolant (2nd order polyomial) # Should work very well, since no noise. config = { 'input': { 'image_file_name': 'output/test_focal_im?.fits', 'cat_file_name': 'output/test_focal_cat?.fits', 'x_col': 'x', 'y_col': 'y', 'ra': 0., 'dec': -25., }, 'psf': { 'type': 'Simple', 'model': { 'type': 'Moffat', 'beta': 2.5 }, 'interp': { 'type': 'Polynomial', 'order': 2 } } } if __name__ != '__main__': config['verbose'] = 0 psf = piff.process(config) for data, wcs in [(data1, wcs1), (data2, wcs2)]: for k in range(nstars): x = data['x'][k] y = data['y'][k] e1 = data['e1'][k] e2 = data['e2'][k] s = data['s'][k] #print('k,x,y = ',k,x,y) #print(' true s,e1,e2 = ',s,e1,e2) image_pos = galsim.PositionD(x, y) star = piff.Star.makeTarget(x=x, y=y, wcs=wcs, stamp_size=48, pointing=field_center) star = psf.drawStar(star) #print(' fitted s,e1,e2 = ',star.fit.params) np.testing.assert_almost_equal(star.fit.params, [s, e1, e2], decimal=6)
def test_single(): """Same as test_focal, but using the SingleCCD PSF type, which does a separate fit on each CCD. """ wcs1 = galsim.TanWCS( galsim.AffineTransform(0.26, 0.05, -0.08, -0.24, galsim.PositionD(1024, 1024)), galsim.CelestialCoord(-5 * galsim.arcmin, -25 * galsim.degrees)) wcs2 = galsim.TanWCS( galsim.AffineTransform(0.25, -0.02, 0.01, 0.24, galsim.PositionD(1024, 1024)), galsim.CelestialCoord(5 * galsim.arcmin, -25 * galsim.degrees)) field_center = galsim.CelestialCoord(0 * galsim.degrees, -25 * galsim.degrees) if __name__ == '__main__': nstars = 20 # per ccd else: nstars = 6 # per ccd rng = np.random.RandomState(1234) x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 u, v = field_center.project_rad(*wcs1._radec(x.copy(), y.copy()), projection='gnomonic') e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u * v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u * v data1 = np.array(list(zip(x, y, e1, e2, s)), dtype=[('x', float), ('y', float), ('e1', float), ('e2', float), ('s', float)]) im1 = drawImage(2048, 2048, wcs1, x, y, e1, e2, s) im1.write('output/test_single_im1.fits') fitsio.write('output/test_single_cat1.fits', data1, clobber=True) x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 u, v = field_center.project_rad(*wcs2._radec(x.copy(), y.copy()), projection='gnomonic') # Same functions of u,v, but using the positions on chip 2 e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u * v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u * v data2 = np.array(list(zip(x, y, e1, e2, s)), dtype=[('x', float), ('y', float), ('e1', float), ('e2', float), ('s', float)]) im2 = drawImage(2048, 2048, wcs2, x, y, e1, e2, s) im2.write('output/test_single_im2.fits') fitsio.write('output/test_single_cat2.fits', data2, clobber=True) # Try to fit with the right model (Moffat) and interpolant (2nd order polyomial) # Should work very well, since no noise. config = { 'input': { # A third way to input these same file names. Use GalSim config values and # explicitly specify the number of images to read 'nimages': 2, 'image_file_name': { 'type': 'FormattedStr', 'format': '%s/test_single_im%d.fits', 'items': ['output', '$image_num+1'] }, 'cat_file_name': { 'type': 'FormattedStr', 'format': '%s/test_single_cat%d.fits', 'items': ['output', '$image_num+1'] }, # Use chipnum = 1,2 rather than the default 0,1. 'chipnum': '$image_num+1', 'x_col': 'x', 'y_col': 'y', 'ra': 0., 'dec': -25., }, 'psf': { 'type': 'SingleChip', 'model': { 'type': 'Moffat', 'beta': 2.5 }, 'interp': { 'type': 'Polynomial', 'order': 2 } }, } if __name__ != '__main__': config['verbose'] = 0 psf = piff.process(config) for chipnum, data, wcs in [(1, data1, wcs1), (2, data2, wcs2)]: for k in range(nstars): x = data['x'][k] y = data['y'][k] e1 = data['e1'][k] e2 = data['e2'][k] s = data['s'][k] #print('k,x,y = ',k,x,y) #print(' true s,e1,e2 = ',s,e1,e2) image_pos = galsim.PositionD(x, y) star = piff.Star.makeTarget(x=x, y=y, wcs=wcs, stamp_size=48, pointing=field_center, chipnum=chipnum) star = psf.drawStar(star) #print(' fitted s,e1,e2 = ',star.fit.params) np.testing.assert_almost_equal(star.fit.params, [s, e1, e2], decimal=6)
def test_wrongwcs(): """Same as test_focal, but the images are written out with the wrong wcs. """ wcs1 = galsim.TanWCS( galsim.AffineTransform(0.26, 0.05, -0.08, -0.24, galsim.PositionD(1024, 1024)), galsim.CelestialCoord(-5 * galsim.arcmin, -25 * galsim.degrees)) wcs2 = galsim.TanWCS( galsim.AffineTransform(0.25, -0.02, 0.01, 0.24, galsim.PositionD(1024, 1024)), galsim.CelestialCoord(5 * galsim.arcmin, -25 * galsim.degrees)) wrong_wcs = galsim.TanWCS( galsim.AffineTransform(0.25, 0, 0, 0.25, galsim.PositionD(1024, 1024)), galsim.CelestialCoord(0 * galsim.arcmin, -25 * galsim.degrees)) field_center = galsim.CelestialCoord(0 * galsim.degrees, -25 * galsim.degrees) if __name__ == '__main__': nstars = 20 # per ccd else: nstars = 3 # per ccd rng = np.random.RandomState(1234) x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 u, v = field_center.project_rad(*wcs1._radec(x.copy(), y.copy()), projection='gnomonic') e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u * v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u * v data1 = np.array(list(zip(x, y, e1, e2, s)), dtype=[('x', float), ('y', float), ('e1', float), ('e2', float), ('s', float)]) im1 = drawImage(2048, 2048, wcs1, x, y, e1, e2, s) x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 u, v = field_center.project_rad(*wcs2._radec(x.copy(), y.copy()), projection='gnomonic') # Same functions of u,v, but using the positions on chip 2 e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u * v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u * v data2 = np.array(list(zip(x, y, e1, e2, s)), dtype=[('x', float), ('y', float), ('e1', float), ('e2', float), ('s', float)]) im2 = drawImage(2048, 2048, wcs2, x, y, e1, e2, s) # Put in the wrong wcs before writing them to files. im1.wcs = im2.wcs = wrong_wcs im1.write('output/test_wrongwcs_im1.fits') im2.write('output/test_wrongwcs_im2.fits') fitsio.write('output/test_wrongwcs_cat1.fits', data1, clobber=True) fitsio.write('output/test_wrongwcs_cat2.fits', data2, clobber=True) config = { 'modules': ['custom_wcs'], 'input': { 'dir': 'output', # Normally more convenient to use a glob string, but an explicit list is also allowed. 'image_file_name': ['test_wrongwcs_im1.fits', 'test_wrongwcs_im2.fits'], 'cat_file_name': ['test_wrongwcs_cat1.fits', 'test_wrongwcs_cat2.fits'], 'x_col': 'x', 'y_col': 'y', 'ra': 0., 'dec': -25., # But here tell Piff the correct WCS to use. This uses a custom WCS builder, # mostly so we can test the 'modules' option. In practice, you might use a # galsim_extra Pixmappy WCS class. Or maybe an LSST DM WCS. 'wcs': { 'type': 'Custom' } }, 'psf': { 'type': 'Simple', 'model': { 'type': 'Moffat', 'beta': 2.5 }, 'interp': { 'type': 'Polynomial', 'order': 2 } }, } if __name__ != '__main__': config['verbose'] = 0 psf = piff.process(config) for data, wcs in [(data1, wcs1), (data2, wcs2)]: for k in range(nstars): x = data['x'][k] y = data['y'][k] e1 = data['e1'][k] e2 = data['e2'][k] s = data['s'][k] #print('k,x,y = ',k,x,y) #print(' true s,e1,e2 = ',s,e1,e2) image_pos = galsim.PositionD(x, y) star = piff.Star.makeTarget(x=x, y=y, wcs=wcs, stamp_size=48, pointing=field_center) star = psf.drawStar(star) #print(' fitted s,e1,e2 = ',star.fit.params) np.testing.assert_almost_equal(star.fit.params, [s, e1, e2], decimal=6)
def test_parallel(): # Run the same test as test_single, but using nproc wcs1 = galsim.TanWCS( galsim.AffineTransform(0.26, 0.05, -0.08, -0.24, galsim.PositionD(1024, 1024)), galsim.CelestialCoord(-5 * galsim.arcmin, -25 * galsim.degrees)) wcs2 = galsim.TanWCS( galsim.AffineTransform(0.25, -0.02, 0.01, 0.24, galsim.PositionD(1024, 1024)), galsim.CelestialCoord(5 * galsim.arcmin, -25 * galsim.degrees)) field_center = galsim.CelestialCoord(0 * galsim.degrees, -25 * galsim.degrees) if __name__ == '__main__': nstars = 20 # per ccd else: nstars = 6 # per ccd rng = np.random.RandomState(1234) x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 ra1, dec1 = wcs1.toWorld(x, y, units='rad') u, v = field_center.project_rad(ra1, dec1, projection='gnomonic') e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u * v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u * v data1 = np.array(list(zip(x, y, e1, e2, s)), dtype=[('x', float), ('y', float), ('e1', float), ('e2', float), ('s', float)]) im1 = drawImage(2048, 2048, wcs1, x, y, e1, e2, s) im1.write('output/test_parallel_im1.fits') x = rng.random_sample(nstars) * 2000 + 24 y = rng.random_sample(nstars) * 2000 + 24 ra2, dec2 = wcs2.toWorld(x, y, units='rad') u, v = field_center.project_rad(ra1, dec1, projection='gnomonic') # Same functions of u,v, but using the positions on chip 2 e1 = 0.02 + 2.e-5 * u - 3.e-9 * u**2 + 2.e-9 * v**2 e2 = -0.04 - 3.e-5 * v + 1.e-9 * u * v + 3.e-9 * v**2 s = 0.3 + 8.e-9 * (u**2 + v**2) - 1.e-9 * u * v data2 = np.array(list(zip(x, y, e1, e2, s)), dtype=[('x', float), ('y', float), ('e1', float), ('e2', float), ('s', float)]) im2 = drawImage(2048, 2048, wcs2, x, y, e1, e2, s) im2.write('output/test_parallel_im2.fits') ra12 = np.concatenate([ra1, ra2]) dec12 = np.concatenate([dec1, dec2]) data12 = np.array(list(zip(ra12, dec12)), dtype=[('ra', float), ('dec', float)]) fitsio.write('output/test_parallel.fits', data12, clobber=True) # im3 is blank. Will give errors trying to measure PSF from it. im3 = galsim.Image(2048, 2048, wcs=wcs2) im3.write('output/test_parallel_im3.fits') psf_file = os.path.join('output', 'test_single.fits') config = { 'input': { # A third way to input these same file names. Use GalSim config values and # explicitly specify the number of images to read 'nimages': 2, 'image_file_name': { 'type': 'FormattedStr', 'format': '%s/test_parallel_im%d.fits', 'items': ['output', '$image_num+1'], }, 'cat_file_name': 'output/test_parallel.fits', 'chipnum': '$image_num+1', 'ra_col': 'ra', 'dec_col': 'dec', 'ra_units': 'rad', 'dec_units': 'rad', 'nproc': -1, }, 'psf': { 'type': 'SingleChip', 'model': { 'type': 'Moffat', 'beta': 2.5, }, 'interp': { 'type': 'Polynomial', 'order': 2, }, 'nproc': 2, }, 'output': { 'file_name': psf_file, }, } with CaptureLog(level=2) as cl: piff.piffify(config, logger=cl.logger) psf = piff.read(psf_file) for chipnum, data, wcs in [(1, data1, wcs1), (2, data2, wcs2)]: for k in range(nstars): x = data['x'][k] y = data['y'][k] e1 = data['e1'][k] e2 = data['e2'][k] s = data['s'][k] image_pos = galsim.PositionD(x, y) star = piff.Star.makeTarget(x=x, y=y, wcs=wcs, stamp_size=48, pointing=field_center, chipnum=chipnum) star = psf.drawStar(star) np.testing.assert_almost_equal(star.fit.params, [s, e1, e2], decimal=6) # Finally, check that the logger properly captures the subprocess logs with CaptureLog(level=2) as cl: psf = piff.process(config, cl.logger) #print('with nproc=2, log = ',cl.output) assert "Processing catalog 1" in cl.output assert "Processing catalog 2" in cl.output assert "Building solution for chip 1" in cl.output assert "Building solution for chip 2" in cl.output # Check that errors in the solution get properly reported. config['input']['nimages'] = 3 with CaptureLog(level=2) as cl: psf = piff.process(config, cl.logger) assert "Removed 6 stars in initialize" in cl.output assert "No stars. Cannot find PSF model." in cl.output assert "Solutions failed for chipnums: [3]" in cl.output # Check that errors in the multiprocessing input get properly reported. config['input']['ra_col'] = 'invalid' with CaptureLog(level=2) as cl: with np.testing.assert_raises(ValueError): psf = piff.process(config, cl.logger) assert "ra_col = invalid is not a column" in cl.output # With nproc=1, the error is raised directly. config['input']['nproc'] = 1 config['verbose'] = 0 with np.testing.assert_raises(ValueError): psf = piff.process(config) # But just the input error. Not the one in fitting. config['psf']['nproc'] = 1 config['input']['ra_col'] = 'ra' config['verbose'] = 1 with CaptureLog(level=1) as cl: psf = piff.process(config, logger=cl.logger) assert "No stars. Cannot find PSF model." in cl.output assert "Ignoring this failure and continuing on." in cl.output