def _ecliptic_obliquity(epoch): # Routine to return the obliquity of the ecliptic for a given date. # We need to figure out the time in Julian centuries from J2000 for this epoch. t = (epoch - 2000.) / 100. # Then we use the last (most recent) formula listed under # http://en.wikipedia.org/wiki/Ecliptic#Obliquity_of_the_ecliptic, from # JPL's 2010 calculations. ep = galsim.DMS_Angle('23:26:21.406') ep -= galsim.DMS_Angle('00:00:46.836769') * t ep -= galsim.DMS_Angle('00:00:0.0001831') * (t**2) ep += galsim.DMS_Angle('00:00:0.0020034') * (t**3) # There are even higher order terms, but they are really not important for any reasonable # calculation we could ever do with GalSim. return ep
def read_image_header(row, img_file): """Read some information from the image header and write into the df row. """ hdu = 0 # Note: The next line usually works, but fitsio doesn't support CONTINUE lines, which DES # image headers sometimes include. #h = fitsio.read_header(img_file, hdu) # I don't care about any of the lines the sometimes use CONITNUE (e.g. OBSERVER), so I # just remove them and make the header with the rest of the entries. f = fitsio.FITS(img_file) header_list = f[hdu].read_header_list() header_list = [ d for d in header_list if 'CONTINUE' not in d['name'] ] h = fitsio.FITSHDR(header_list) try: date = h['DATE-OBS'] date, time = date.strip().split('T',1) filter = h['FILTER'] filter = filter.split()[0] sat = h['SATURATE'] fwhm = h['FWHM'] ccdnum = int(h['CCDNUM']) detpos = h['DETPOS'].strip() telra = h['TELRA'] teldec = h['TELDEC'] telha = h['HA'] if galsim.__version__ >= '1.5.1': telra = galsim.Angle.from_hms(telra) / galsim.degrees teldec = galsim.Angle.from_dms(teldec) / galsim.degrees telha = galsim.Angle.from_hms(telha) / galsim.degrees else: telra = galsim.HMS_Angle(telra) / galsim.degrees teldec = galsim.DMS_Angle(teldec) / galsim.degrees telha = galsim.HMS_Angle(telha) / galsim.degrees airmass = float(h.get('AIRMASS',-999)) sky = float(h.get('SKYBRITE',-999)) sigsky = float(h.get('SKYSIGMA',-999)) tiling = int(h.get('TILING',0)) hex = int(h.get('HEX',0)) except Exception as e: logger.info("Caught %s",e) logger.info("Cannot read header information from %s", img_file) raise row['date'] = date row['time'] = time row['sat'] = sat row['fits_filter'] = filter row['fits_fwhm'] = fwhm row['fits_ccdnum'] = ccdnum row['telra'] = telra row['teldec'] = teldec row['telha'] = telha row['airmass'] = airmass row['sky'] = sky row['sigsky'] = sigsky row['tiling'] = tiling row['hex'] = hex
def main(argv): # Where to find and output data path, filename = os.path.split(__file__) datapath = os.path.abspath(os.path.join(path, "data/")) outpath = os.path.abspath(os.path.join(path, "output/")) # In non-script code, use getLogger(__name__) at module scope instead. logging.basicConfig(format="%(message)s", level=logging.INFO, stream=sys.stdout) logger = logging.getLogger("demo12") # initialize (pseudo-)random number generator random_seed = 1234567 rng = galsim.BaseDeviate(random_seed) # read in SEDs SED_names = ['CWW_E_ext', 'CWW_Sbc_ext', 'CWW_Scd_ext', 'CWW_Im_ext'] SEDs = {} for SED_name in SED_names: SED_filename = os.path.join(galsim.meta_data.share_dir, '{0}.sed'.format(SED_name)) # Here we create some galsim.SED objects to hold star or galaxy spectra. The most # convenient way to create realistic spectra is to read them in from a two-column ASCII # file, where the first column is wavelength and the second column is flux. Wavelengths in # the example SED files are in Angstroms, flux in flambda. We use a set of files that are # distributed with GalSim in the share/ directory. SED = galsim.SED(SED_filename, wave_type='Ang', flux_type='flambda') # The normalization of SEDs affects how many photons are eventually drawn into an image. # One way to control this normalization is to specify the flux density in photons per nm # at a particular wavelength. For example, here we normalize such that the photon density # is 1 photon per nm at 500 nm. SEDs[SED_name] = SED.withFluxDensity(target_flux_density=1.0, wavelength=500) logger.debug('Successfully read in SEDs') # read in the LSST filters filter_names = 'ugrizy' filters = {} for filter_name in filter_names: filter_filename = os.path.join(datapath, 'LSST_{0}.dat'.format(filter_name)) # Here we create some galsim.Bandpass objects to represent the filters we're observing # through. These include the entire imaging system throughput including the atmosphere, # reflective and refractive optics, filters, and the CCD quantum efficiency. These are # also conveniently read in from two-column ASCII files where the first column is # wavelength and the second column is dimensionless throughput. The example filter files # units of nanometers for the wavelength type, so we specify that using the required # `wave_type` argument. filters[filter_name] = galsim.Bandpass(filter_filename, wave_type='nm') # For speed, we can thin out the wavelength sampling of the filter a bit. # In the following line, `rel_err` specifies the relative error when integrating over just # the filter (however, this is not necessarily the relative error when integrating over the # filter times an SED). filters[filter_name] = filters[filter_name].thin(rel_err=1e-4) logger.debug('Read in filters') pixel_scale = 0.2 # arcseconds #----------------------------------------------------------------------------------------------- # Part A: chromatic de Vaucouleurs galaxy # Here we create a chromatic version of a de Vaucouleurs profile using the Chromatic class. # This class lets one create chromatic versions of any galsim GSObject class. The first # argument is the GSObject instance to be chromaticized, and the second argument is the # profile's SED. logger.info('') logger.info('Starting part A: chromatic De Vaucouleurs galaxy') redshift = 0.8 mono_gal = galsim.DeVaucouleurs(half_light_radius=0.5) SED = SEDs['CWW_E_ext'].atRedshift(redshift) gal = galsim.Chromatic(mono_gal, SED) # You can still shear, shift, and dilate the resulting chromatic object. gal = gal.shear(g1=0.5, g2=0.3).dilate(1.05).shift((0.0, 0.1)) logger.debug('Created Chromatic') # convolve with PSF to make final profile PSF = galsim.Moffat(fwhm=0.6, beta=2.5) final = galsim.Convolve([gal, PSF]) logger.debug('Created final profile') # draw profile through LSST filters gaussian_noise = galsim.GaussianNoise(rng, sigma=0.1) for filter_name, filter_ in filters.iteritems(): img = galsim.ImageF(64, 64, scale=pixel_scale) final.drawImage(filter_, image=img) img.addNoise(gaussian_noise) logger.debug('Created {0}-band image'.format(filter_name)) out_filename = os.path.join(outpath, 'demo12a_{0}.fits'.format(filter_name)) galsim.fits.write(img, out_filename) logger.debug('Wrote {0}-band image to disk'.format(filter_name)) logger.info('Added flux for {0}-band image: {1}'.format( filter_name, img.added_flux)) logger.info( 'You can display the output in ds9 with a command line that looks something like:' ) logger.info( 'ds9 output/demo12a_*.fits -match scale -zoom 2 -match frame image &') #----------------------------------------------------------------------------------------------- # Part B: chromatic bulge+disk galaxy logger.info('') logger.info('Starting part B: chromatic bulge+disk galaxy') redshift = 0.8 # make a bulge ... mono_bulge = galsim.DeVaucouleurs(half_light_radius=0.5) bulge_SED = SEDs['CWW_E_ext'].atRedshift(redshift) # The `*` operator can be used as a shortcut for creating a chromatic version of a GSObject: bulge = mono_bulge * bulge_SED bulge = bulge.shear(g1=0.12, g2=0.07) logger.debug('Created bulge component') # ... and a disk ... mono_disk = galsim.Exponential(half_light_radius=2.0) disk_SED = SEDs['CWW_Im_ext'].atRedshift(redshift) disk = mono_disk * disk_SED disk = disk.shear(g1=0.4, g2=0.2) logger.debug('Created disk component') # ... and then combine them. bdgal = 1.1 * ( 0.8 * bulge + 4 * disk ) # you can add and multiply ChromaticObjects just like GSObjects bdfinal = galsim.Convolve([bdgal, PSF]) # Note that at this stage, our galaxy is chromatic but our PSF is still achromatic. Part C) # below will dive into chromatic PSFs. logger.debug('Created bulge+disk galaxy final profile') # draw profile through LSST filters gaussian_noise = galsim.GaussianNoise(rng, sigma=0.02) for filter_name, filter_ in filters.iteritems(): img = galsim.ImageF(64, 64, scale=pixel_scale) bdfinal.drawImage(filter_, image=img) img.addNoise(gaussian_noise) logger.debug('Created {0}-band image'.format(filter_name)) out_filename = os.path.join(outpath, 'demo12b_{0}.fits'.format(filter_name)) galsim.fits.write(img, out_filename) logger.debug('Wrote {0}-band image to disk'.format(filter_name)) logger.info('Added flux for {0}-band image: {1}'.format( filter_name, img.added_flux)) logger.info( 'You can display the output in ds9 with a command line that looks something like:' ) logger.info( 'ds9 -rgb -blue -scale limits -0.2 0.8 output/demo12b_r.fits -green -scale limits' + ' -0.25 1.0 output/demo12b_i.fits -red -scale limits -0.25 1.0 output/demo12b_z.fits' + ' -zoom 2 &') #----------------------------------------------------------------------------------------------- # Part C: chromatic PSF logger.info('') logger.info('Starting part C: chromatic PSF') redshift = 0.0 mono_gal = galsim.Exponential(half_light_radius=0.5) SED = SEDs['CWW_Im_ext'].atRedshift(redshift) # Here's another way to set the normalization of the SED. If we want 50 counts to be drawn # when observing an object with this SED through the LSST g-band filter, for instance, then we # can do: SED = SED.withFlux(50.0, filters['g']) # The flux drawn through other bands, which sample different parts of the SED and have different # throughputs, will, of course, be different. gal = mono_gal * SED gal = gal.shear(g1=0.5, g2=0.3) logger.debug('Created `Chromatic` galaxy') # For a ground-based PSF, two chromatic effects are introduced by the atmosphere: # (i) differential chromatic refraction (DCR), and (ii) wavelength-dependent seeing. # # DCR shifts the position of the PSF as a function of wavelength. Blue light is shifted # toward the zenith slightly more than red light. # # Kolmogorov turbulence in the atmosphere leads to a seeing size (e.g., FWHM) that scales with # wavelength to the (-0.2) power. # # The ChromaticAtmosphere function will attach both of these effects to a fiducial PSF at # some fiducial wavelength. # First we define a monochromatic PSF to be the fiducial PSF. PSF_500 = galsim.Moffat(beta=2.5, fwhm=0.5) # Then we use ChromaticAtmosphere to manipulate this fiducial PSF as a function of wavelength. # ChromaticAtmosphere also needs to know the wavelength of the fiducial PSF, and the location # and orientation of the object with respect to the zenith. This final piece of information # can be specified in several ways (see the ChromaticAtmosphere docstring for all of them). # Here are a couple ways: let's pretend our object is located near M101 on the sky, we observe # it 1 hour before it transits and we're observing from Mauna Kea. ra = galsim.HMS_Angle("14:03:13") # hours : minutes : seconds dec = galsim.DMS_Angle("54:20:57") # degrees : minutes : seconds m101 = galsim.CelestialCoord(ra, dec) latitude = 19.8207 * galsim.degrees # latitude of Mauna Kea HA = -1.0 * galsim.hours # Hour angle = one hour before transit # Then we can compute the zenith angle and parallactic angle (which is is the position angle # of the zenith measured from North through East) of this object: za, pa = galsim.dcr.zenith_parallactic_angles(m101, HA=HA, latitude=latitude) # And then finally, create the chromatic PSF PSF = galsim.ChromaticAtmosphere(PSF_500, 500.0, zenith_angle=za, parallactic_angle=pa) # We could have also just passed `m101`, `latitude` and `HA` to ChromaticAtmosphere directly: PSF = galsim.ChromaticAtmosphere(PSF_500, 500.0, obj_coord=m101, latitude=latitude, HA=HA) # and proceed like normal. # convolve with galaxy to create final profile final = galsim.Convolve([gal, PSF]) logger.debug('Created chromatic PSF finale profile') # Draw profile through LSST filters gaussian_noise = galsim.GaussianNoise(rng, sigma=0.03) for filter_name, filter_ in filters.iteritems(): img = galsim.ImageF(64, 64, scale=pixel_scale) final.drawImage(filter_, image=img) img.addNoise(gaussian_noise) logger.debug('Created {0}-band image'.format(filter_name)) out_filename = os.path.join(outpath, 'demo12c_{0}.fits'.format(filter_name)) galsim.fits.write(img, out_filename) logger.debug('Wrote {0}-band image to disk'.format(filter_name)) logger.info('Added flux for {0}-band image: {1}'.format( filter_name, img.added_flux)) logger.info( 'You can display the output in ds9 with a command line that looks something like:' ) logger.info( 'ds9 output/demo12c_*.fits -match scale -zoom 2 -match frame image -blink &' )
def setup_wcs(config, ndim, nu_axis=False): pixel_scale = config.getfloat('skymodel', 'pixel_scale') * galsim.arcsec fov = config.getfloat('skymodel', 'field_of_view') * galsim.arcmin image_size = int((fov / galsim.arcmin) / (pixel_scale / galsim.arcmin)) ra_field = config.get('field', 'field_ra') ra_field_gs = galsim.HMS_Angle(ra_field) dec_field = config.get('field', 'field_dec') dec_field_gs = galsim.DMS_Angle(dec_field) n_ifs = config.getint('observation', 'n_IFs') bw = config.getfloat('observation', 'total_bandwidth') base_freq = config.getfloat('observation', 'lowest_frequency') n_chan = config.getint('observation', 'n_channels') msname = config.get('pipeline', 'project_name') + '.ms' msname = config.get('pipeline', 'data_path') + msname imagename = msname + '.image' channel_width = bw / (n_chan * n_ifs) if_width = bw / n_ifs w = wcs.WCS(naxis=ndim) if ndim == 4: if nu_axis: ''' w.wcs.naxis = [float(image_size), float(image_size), 1, n_chan*n_ifs] ''' w.wcs.crpix = [float(image_size) / 2, float(image_size) / 2, 1, 1] w.wcs.cdelt = [ pixel_scale / galsim.degrees, pixel_scale / galsim.degrees, bw, 1 ] w.wcs.crval = [ ra_field_gs / galsim.degrees, dec_field_gs / galsim.degrees, base_freq + bw / 2, 1 ] w.wcs.ctype = ['RA---SIN', 'DEC--SIN', 'FREQ', 'STOKES'] w.wcs.cunit = ['deg', 'deg', 'Hz', ''] else: ''' w.wcs.naxis = [float(image_size), float(image_size), 1, 1] ''' w.wcs.crpix = [float(image_size) / 2, float(image_size) / 2, 1, 1] w.wcs.cdelt = [ pixel_scale / galsim.degrees, pixel_scale / galsim.degrees, bw, 1 ] w.wcs.crval = [ ra_field_gs / galsim.degrees, dec_field_gs / galsim.degrees, base_freq + bw / 2, 1 ] w.wcs.ctype = ['RA---SIN', 'DEC--SIN', 'FREQ', 'STOKES'] w.wcs.cunit = ['deg', 'deg', 'Hz', ''] elif ndim == 2: w.wcs.crpix = [float(image_size) / 2, float(image_size) / 2] w.wcs.cdelt = [ pixel_scale / galsim.degrees, pixel_scale / galsim.degrees ] w.wcs.crval = [ ra_field_gs / galsim.degrees, dec_field_gs / galsim.degrees ] w.wcs.ctype = ['RA---SIN', 'DEC--SIN'] w.wcs.cunit = ['deg', 'deg'] return w
def test_ecliptic(): """Test the conversion from equatorial to ecliptic coordinates.""" # Use locations of ecliptic poles from http://en.wikipedia.org/wiki/Ecliptic_pole north_pole = galsim.CelestialCoord(galsim.HMS_Angle('18:00:00.00'), galsim.DMS_Angle('66:33:38.55')) el, b = north_pole.ecliptic() # North pole should have b=90 degrees, with el being completely arbitrary. numpy.testing.assert_almost_equal(b.rad(), pi / 2, decimal=6) south_pole = galsim.CelestialCoord(galsim.HMS_Angle('06:00:00.00'), galsim.DMS_Angle('-66:33:38.55')) el, b = south_pole.ecliptic() # South pole should have b=-90 degrees, with el being completely arbitrary. numpy.testing.assert_almost_equal(b.rad(), -pi / 2, decimal=6) # Also confirm that positions that should be the same in equatorial and ecliptic coordinates are # actually the same: vernal_equinox = galsim.CelestialCoord(0. * galsim.radians, 0. * galsim.radians) el, b = vernal_equinox.ecliptic() numpy.testing.assert_almost_equal(b.rad(), 0., decimal=6) numpy.testing.assert_almost_equal(el.rad(), 0., decimal=6) autumnal_equinox = galsim.CelestialCoord(pi * galsim.radians, 0. * galsim.radians) el, b = autumnal_equinox.ecliptic() numpy.testing.assert_almost_equal(el.rad(), pi, decimal=6) numpy.testing.assert_almost_equal(b.rad(), 0., decimal=6) # Finally, test the results of using a date to get ecliptic coordinates with respect to the sun, # instead of absolute ones. For this, use dates and times of vernal and autumnal equinox # in 2014 from # http://wwp.greenwichmeantime.com/longest-day/ # and the conversion to Julian dates from # http://www.aavso.org/jd-calculator import datetime vernal_eq_date = datetime.datetime(2014, 3, 20, 16, 57, 0) el, b = vernal_equinox.ecliptic(epoch=2014) el_rel, b_rel = vernal_equinox.ecliptic(epoch=2014, date=vernal_eq_date) # Vernal equinox: should have (el, b) = (el_rel, b_rel) = 0.0 numpy.testing.assert_almost_equal(el_rel.rad(), el.rad(), decimal=3) numpy.testing.assert_almost_equal(b_rel.rad(), b.rad(), decimal=6) # Now do the autumnal equinox: should have (el, b) = (pi, 0) = (el_rel, b_rel) when we look at # the time of the vernal equinox. el, b = autumnal_equinox.ecliptic(epoch=2014) el_rel, b_rel = autumnal_equinox.ecliptic(epoch=2014, date=vernal_eq_date) numpy.testing.assert_almost_equal(el_rel.rad(), el.rad(), decimal=3) numpy.testing.assert_almost_equal(b_rel.rad(), b.rad(), decimal=6) # And check that if it's the date of the autumnal equinox (sun at (180, 0)) but we're looking at # the position of the vernal equinox (0, 0), then (el_rel, b_rel) = (-180, 0) autumnal_eq_date = datetime.datetime(2014, 9, 23, 2, 29, 0) el_rel, b_rel = vernal_equinox.ecliptic(epoch=2014, date=autumnal_eq_date) numpy.testing.assert_almost_equal(el_rel.rad(), -pi, decimal=3) numpy.testing.assert_almost_equal(b_rel.rad(), 0., decimal=6) # And check that if it's the date of the vernal equinox (sun at (0, 0)) but we're looking at # the position of the autumnal equinox (180, 0), then (el_rel, b_rel) = (180, 0) el_rel, b_rel = autumnal_equinox.ecliptic(epoch=2014, date=vernal_eq_date) numpy.testing.assert_almost_equal(el_rel.rad(), pi, decimal=3) numpy.testing.assert_almost_equal(b_rel.rad(), 0., decimal=6) # Check round-trips: go from CelestialCoord to ecliptic back to equatorial, and make sure # results are the same. This includes use of a function that isn't available to users, but we # use it for a few things so we should still make sure it's working properly. from galsim.celestial import _ecliptic_to_equatorial north_pole_2 = _ecliptic_to_equatorial(north_pole.ecliptic(epoch=2014), 2014) numpy.testing.assert_almost_equal(north_pole.ra.rad(), north_pole_2.ra.rad(), decimal=6) numpy.testing.assert_almost_equal(north_pole.dec.rad(), north_pole_2.dec.rad(), decimal=6) south_pole_2 = _ecliptic_to_equatorial(south_pole.ecliptic(epoch=2014), 2014) numpy.testing.assert_almost_equal(south_pole.ra.rad(), south_pole_2.ra.rad(), decimal=6) numpy.testing.assert_almost_equal(south_pole.dec.rad(), south_pole_2.dec.rad(), decimal=6) vernal_equinox_2 = _ecliptic_to_equatorial( vernal_equinox.ecliptic(epoch=2014), 2014) numpy.testing.assert_almost_equal(vernal_equinox.ra.rad(), vernal_equinox_2.ra.rad(), decimal=6) numpy.testing.assert_almost_equal(vernal_equinox.dec.rad(), vernal_equinox_2.dec.rad(), decimal=6) autumnal_equinox_2 = _ecliptic_to_equatorial( autumnal_equinox.ecliptic(epoch=2014), 2014) numpy.testing.assert_almost_equal(autumnal_equinox.ra.rad(), autumnal_equinox_2.ra.rad(), decimal=6) numpy.testing.assert_almost_equal(autumnal_equinox.dec.rad(), autumnal_equinox_2.dec.rad(), decimal=6)
# In[4]: #define the components of galaxies sed1 = galsim.SED(spec="wave",wave_type='nm',flux_type='fphotons') sed1 = sed1.withFlux(gal1_flux_thru_r,filters['r']) sed2 = galsim.SED(spec="300-wave",wave_type = 'nm',flux_type = 'fphotons') sed2 = sed1.withFlux(gal2_flux_thru_r,filters['r']) gal1 = galsim.Gaussian(flux=1.,sigma=gal1_sigma) psf1 = galsim.Moffat(beta=2.5,fwhm=0.5) ra = galsim.HMS_Angle("14:03:13") dec = galsim.DMS_Angle("54:20:57") m101 = galsim.CelestialCoord(ra,dec) latitude = 19.8207*galsim.degrees HA=-1.0*galsim.hours za,pa = galsim.dcr.zenith_parallactic_angles(m101,HA=HA,latitude=latitude) chrom_psf1 = galsim.ChromaticAtmosphere(psf1,500.,obj_coord=m101,latitude=latitude,HA=HA) gal2 = galsim.Gaussian(flux=1.,sigma=gal2_sigma) psf2 = galsim.Gaussian(flux=1.,sigma = psf2_sigma) ra = galsim.HMS_Angle("14:13:13") dec = galsim.DMS_Angle("54:50:57") rand_thing_next_to_m101 = galsim.CelestialCoord(ra,dec) latitude_seoul = 37.5665*galsim.degrees #seoul za,pa = galsim.dcr.zenith_parallactic_angles(rand_thing_next_to_m101,HA=HA,latitude=latitude) chrom_psf2 = galsim.ChromaticAtmosphere(psf2,500.,obj_coord=rand_thing_next_to_m101,latitude=latitude_seoul,HA=HA)
def runSkyModel(config): # image properties data_path = config.get('pipeline', 'data_path') pixel_scale = config.getfloat('skymodel', 'pixel_scale') * galsim.arcsec fov = config.getfloat('skymodel', 'field_of_view') * galsim.arcmin image_size = int((fov / galsim.arcmin) / (pixel_scale / galsim.arcmin)) ra_field = config.get('field', 'field_ra') ra_field_gs = galsim.HMS_Angle(ra_field) dec_field = config.get('field', 'field_dec') dec_field_gs = galsim.DMS_Angle(dec_field) cat_file_name = config.get('field', 'catalogue') print('Loading catalogue from {0} ...'.format(cat_file_name)) cat = fits.getdata(cat_file_name) nobj = len(cat) cat_wcs = ast_wcs.WCS(naxis=2) cat_wcs.wcs.crpix = [image_size / 2, image_size / 2] cat_wcs.wcs.cdelt = [ pixel_scale / galsim.degrees, pixel_scale / galsim.degrees ] cat_wcs.wcs.crval = [0.e0, 0.e0] cat_wcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] gal_ra = cat['latitude'] gal_dec = cat['longitude'] gal_e1 = cat['e1'] gal_e2 = cat['e2'] gal_flux = cat['I1400'] #mjy gal_r0 = cat['size'] / 2. g1 = 0 g2 = 0 print('...done.') full_image = galsim.ImageF(image_size, image_size, scale=pixel_scale) im_center = full_image.bounds.trueCenter() sky_center = galsim.CelestialCoord(ra=ra_field_gs, dec=dec_field_gs) # - on dx's since the ra axis is flipped. dudx = -pixel_scale / galsim.arcsec dudy = 0. dvdx = 0. dvdy = pixel_scale / galsim.arcsec image_center = full_image.trueCenter() affine = galsim.AffineTransform(dudx, dudy, dvdx, dvdy, origin=full_image.trueCenter()) wcs = galsim.TanWCS(affine, sky_center, units=galsim.arcsec) full_image.wcs = wcs tstart = time.time() nobj = 200 for i in range(nobj): sys.stdout.write('\rAdding source {0} of {1} to skymodel...'.format( i + 1, nobj)) gal = galsim.Exponential(scale_radius=gal_r0[i], flux=gal_flux[i]) ellipticity = galsim.Shear(e1=gal_e1[i], e2=gal_e2[i]) shear = galsim.Shear(g1=g1[i], g2=g2[i]) total_shear = ellipticity + shear gal = gal.shear(total_shear) x, y = cat_wcs.wcs_world2pix(gal_ra[i], gal_dec[i], 0) x = float(x) y = float(y) # Account for the fractional part of the position: ix = int(np.floor(x + 0.5)) iy = int(np.floor(y + 0.5)) offset = galsim.PositionD(x - ix, y - iy) stamp = gal.drawImage(scale=pixel_scale / galsim.arcsec, offset=offset) stamp.setCenter(ix, iy) bounds = stamp.bounds & full_image.bounds full_image[bounds] += stamp[bounds] sys.stdout.flush() tend = time.time() print('\n...done in {0} seconds.'.format(tend - tstart)) all_gals_fname = data_path + config.get('field', 'fitsname') print('Writing image data to {0} ...'.format(all_gals_fname)) image_data = full_image.array write4dImage(all_gals_fname, image_data, pixel_scale / galsim.degrees, obs_ra=ra_field_gs / galsim.degrees, obs_dec=dec_field_gs / galsim.degrees, obs_freq=config.getfloat('observation', 'lowest_frequency')) print('...done.') print('runSkyModel complete.')
def runSkyModel(config): '''Simulate a sky model from a T-RECS catalogue. Parameters ---------- config : configparser ConfigParser configuration containing necessary sections. ''' data_path = config.get('pipeline', 'data_path') # Set some image properties pixel_scale = config.getfloat('skymodel', 'pixel_scale')*galsim.arcsec fov = config.getfloat('skymodel', 'field_of_view')*galsim.arcmin image_size = int((fov/galsim.arcmin)/(pixel_scale/galsim.arcmin)) ra_field = config.get('field', 'field_ra') ra_field_gs = galsim.HMS_Angle(ra_field) dec_field = config.get('field', 'field_dec') dec_field_gs = galsim.DMS_Angle(dec_field) w_twod = setup_wcs(config, ndim=2) w_fourd = setup_wcs(config, ndim=4) header_twod = w_twod.to_header() header_fourd = w_fourd.to_header() # Load the catalogue cat_file_name = config.get('field', 'catalogue') print('Loading catalogue from {0} ...'.format(cat_file_name)) cat = Table() if config.get('skymodel', 'catalogue_origin') == 'trecs': cat_read = Table.read(cat_file_name, format='ascii') cat['ra_offset'] = cat_read['lon'] # deg cat['ra_offset'].unit = 'deg' cat['dec_offset'] = cat_read['lat'] # deg cat['dec_offset'].unit = 'deg' cat['dec_abs'] = dec_field_gs / galsim.degrees + cat['dec_offset'] dec_abs_radians = cat['dec_abs']*galsim.degrees / galsim.radians cat['ra_abs'] = ra_field_gs / galsim.degrees + cat['ra_offset']/np.cos(np.asarray(dec_abs_radians, dtype=float)) cat['integrated_flux'] = cat_read['flux']*1.e-3 # Jy cat['integrated_flux'].unit = 'Jy' cat['size'] = cat_read['size'] # arcsec cat['size'].unit = 'arcsec' cat['peak_flux'] = cat['integrated_flux'] / (2.*cat['size']*arcsectorad) cat['peak_flux'].unit = 'Jy' cat['e1'] = cat_read['e1'] cat['e2'] = cat_read['e2'] cat['g1'] = cat_read['gamma1'] # 0 cat['g2'] = cat_read['gamma2'] # 0 elif config.get('skymodel', 'catalogue_origin') == 'pybdsm': cat_read = Table.read(cat_file_name, format='fits') cat['ra_abs'] = cat_read['RA'] # deg cat['dec_abs'] = cat_read['DEC'] # deg cat['dec_offset'] = cat['dec_abs'] - dec_field_gs / galsim.degrees dec_abs_radians = cat['dec_abs']*galsim.degrees / galsim.radians cat['ra_offset'] = (cat['ra_abs'] - ra_field_gs / galsim.degrees)*np.cos(np.asarray(dec_abs_radians, dtype=float)) cat['integrated_flux'] = cat_read['Total_flux'] # Jy cat['size'] = cat_read['Maj']*degtoarcsec # deg cat['size'].unit = 'arcsec' cat['peak_flux'] = cat_read['Peak_flux'] # Jy cat['q'] = cat_read['Min']/cat_read['Maj'] cat['position_angle'] = np.arctan2(cat_read['Min'], cat_read['Maj']) cat['mod_e'] = (1. - cat['q']**2.)/(1. + cat['q']**2.) cat['e1'] = cat['mod_e']*np.cos(2.*cat['position_angle']) cat['e2'] = cat['mod_e']*np.sin(2.*cat['position_angle']) cat['g1'] = 0.e0 cat['g2'] = 0.e0 # fov cut ra_offset_max = 0.9*(fov/2) / galsim.degrees dec_offset_max = 0.9*(fov/2) / galsim.degrees fov_cut = (abs(cat['ra_offset']) < ra_offset_max)*(abs(cat['dec_offset']) < dec_offset_max) cat = cat[fov_cut] # flux cuts if config.getboolean('skymodel', 'highfluxcut'): highflux_cut = cat['peak_flux'] < config.getfloat('skymodel', 'highfluxcut_value') cat = cat[highflux_cut] if config.getboolean('skymodel', 'lowfluxcut'): lowflux_cut = cat['peak_flux'] > config.getfloat('skymodel', 'lowfluxcut_value') cat = cat[lowflux_cut] if config.getboolean('skymodel', 'highsizecut'): highsize_cut = cat['size'] < config.getfloat('skymodel', 'highsizecut_value') cat = cat[highsize_cut] if config.getboolean('skymodel', 'lowsizecut'): lowsize_cut = cat['size'] > config.getfloat('skymodel', 'lowsizecut_value') cat = cat[lowsize_cut] if config.get('skymodel', 'sizescale')=='constant': cat['size'] = np.ones_like(cat['size'])*config.getfloat('skymodel', 'sizescale_constant_value') # number of sources, on grid if requested if config.getboolean('skymodel', 'grid'): nobj = int(np.sqrt(config.getint('skymodel', 'ngals')))**2. cat['ra_offset'] = np.linspace(-ra_offset_max, ra_offset_max, nobj) cat['dec_offset'] = np.linspace(-ra_offset_max, ra_offset_max, nobj) else: nobj = len(cat) if config.getint('skymodel', 'ngals') > -1: nobj = config.getint('skymodel', 'ngals') cat = cat[:nobj] # flux range if config.get('skymodel', 'fluxscale')=='constant': cat['integrated_flux'] = np.ones_like(cat['integrated_flux'])*config.getfloat('skymodel', 'fluxscale_constant_value') cat['peak_flux'] = cat['integrated_flux'] / (2.*cat['size']*arcsectorad) # scale flux cat['integrated_flux'] = cat['integrated_flux']*config.getfloat('skymodel', 'flux_factor') cat['peak_flux'] = cat['peak_flux']*config.getfloat('skymodel', 'flux_factor') # write out catalogue cat.write(config.get('pipeline', 'data_path')+config.get('pipeline', 'project_name')+'_truthcat.txt', format='ascii') ix_arr = np.ones(nobj) iy_arr = np.ones(nobj) print('...done.') # Create the galsim image full_image = galsim.ImageF(image_size, image_size, scale=pixel_scale/galsim.arcsec) im_center = full_image.bounds.trueCenter() sky_center = galsim.CelestialCoord(ra=ra_field_gs, dec=dec_field_gs) # Create a WCS for the galsim image full_image.wcs, origin = galsim.wcs.readFromFitsHeader(header_twod) tstart=time.time() # Draw the galaxies onto the galsim image for i,cat_gal in enumerate(cat): sys.stdout.write('\rAdding source {0} of {1} to skymodel...'.format(i+1, nobj)) # choose the profile if config.get('skymodel', 'galaxy_profile')=='exponential': gal = galsim.Exponential(scale_radius=cat_gal['size']/2., flux=cat_gal['integrated_flux'], gsparams=big_fft_params) elif config.get('skymodel', 'galaxy_profile')=='gaussian': gal = galsim.Gaussian(fwhm=cat_gal['size'], flux=cat_gal['integrated_flux'], gsparams=big_fft_params) elif config.get('skymodel', 'galaxy_profile')=='matched-exponential': gauss_gal = galsim.Gaussian(fwhm=cat_gal['size'], flux=cat_gal['integrated_flux']) gal = galsim.Exponential(half_light_radius=gauss_gal.getHalfLightRadius(), flux=cat_gal['integrated_flux'], gsparams=big_fft_params) del gauss_gal # calculate the total ellipticity ellipticity = galsim.Shear(e1=cat_gal['e1'],e2=cat_gal['e2']) shear = galsim.Shear(g1=cat_gal['g1'],g2=cat_gal['g2']) if config.getboolean('skymodel', 'doshear'): total_shear = ellipticity + shear else: total_shear = ellipticity gal = gal.shear(total_shear) x, y = w_twod.wcs_world2pix(cat_gal['ra_abs'], cat_gal['dec_abs'], 0,) x = float(x) y = float(y) # Account for the fractional part of the position: ix = int(np.floor(x+0.5)) iy = int(np.floor(y+0.5)) ix_arr[i] = ix iy_arr[i] = iy offset = galsim.PositionD(x-ix, y-iy) # Create the sub-image for this galaxy stamp = gal.drawImage(scale=pixel_scale/galsim.arcsec, offset=offset) stamp.setCenter(ix, iy) # Add the sub-image to the full iamge bounds = stamp.bounds & full_image.bounds full_image[bounds] += stamp[bounds] sys.stdout.flush() if config.getboolean('skymodel', 'doagn'): # Load the catalogue cat_file_name = config.get('field', 'agncatalogue') print('Loading catalogue from {0} ...'.format(cat_file_name)) cat = Table.read(cat_file_name, format='ascii') ra_offset = cat['lon(deg)'] dec_offset = cat['lat(deg)'] ra_offset_max = 0.9*(fov/2) / galsim.degrees dec_offset_max = 0.9*(fov/2) / galsim.degrees fov_cut = (abs(ra_offset) < ra_offset_max)*(abs(dec_offset) < dec_offset_max) #pdb.set_trace() cat = cat[fov_cut] if config.getboolean('skymodel', 'highfluxcut'): highflux_cut = cat['flux(mJy)']*1.e-3 < 100.e-6 cat = cat[highflux_cut] if config.getboolean('skymodel', 'lowfluxcut'): lowflux_cut = cat['flux(mJy)']*1.e-3 > 25.e-6 cat = cat[lowflux_cut] if config.getboolean('skymodel', 'highsizecut'): highsize_cut = cat['size(arcsec)']/2. < 10 cat = cat[highsize_cut] if config.getboolean('skymodel', 'lowsizecut'): lowsize_cut = cat['size(arcsec)']/2. > 0.75 cat = cat[lowsize_cut] if config.getboolean('skymodel', 'grid'): nobj = int(np.sqrt(config.getint('skymodel', 'ngals')))**2. gal_ra_offset = np.linspace(-ra_offset_max, ra_offset_max, nobj) gal_dec_offset = np.linspace(-ra_offset_max, ra_offset_max, nobj) else: gal_ra_offset = cat['lon(deg)'] gal_dec_offset = cat['lat(deg)'] nobj = len(cat) if config.getint('skymodel', 'ngals') > -1: nobj = config.getint('skymodel', 'ngals') gal_dec = dec_field_gs / galsim.degrees + gal_dec_offset gal_dec_radians = (gal_dec*galsim.degrees) / galsim.radians gal_ra = ra_field_gs / galsim.degrees + gal_ra_offset/np.cos(np.asarray(gal_dec_radians, dtype=float)) gal_e1 = cat['e1'] gal_e2 = cat['e2'] gal_flux = cat['flux(mJy)']*1.e-3 #mjy convert to Jy gal_size = cat['size(arcsec)']/(2.) g1 = cat['gamma1'] g2 = cat['gamma2'] rs = cat['Rs'] if config.get('skymodel', 'fluxscale')=='constant': gal_flux = np.ones_like(gal_flux)*100e-6 ra_abs, dec_abs = w_twod.wcs_world2pix(gal_ra, gal_dec, 0,) truthcat = np.column_stack([gal_ra, gal_dec, ra_abs, dec_abs, gal_e1, gal_e2, gal_flux, gal_size, g1, g2]) np.savetxt(config.get('pipeline', 'data_path')+config.get('pipeline', 'project_name')+'-agn_truthcat.txt', truthcat[:nobj]) ix_arr = np.ones(nobj) iy_arr = np.ones(nobj) posang = random.uniform(0,2.*np.pi, nobj) print('...done.') # Draw the galaxies onto the galsim image for i in range(nobj): sys.stdout.write('\rAdding agn source {0} of {1} to skymodel...'.format(i+1, nobj)) if (rs[i] < 0.01) or (cat_gal['size'] < config.getfloat('skymodel', 'pixel_scale')/2): x, y = w_twod.wcs_world2pix(cat_gal['ra_abs'], cat_gal['dec_abs'], 0,) x = float(x) y = float(y) # Account for the fractional part of the position: ix = int(np.floor(x+0.5)) iy = int(np.floor(y+0.5)) ix_arr[i] = ix iy_arr[i] = iy offset = galsim.PositionD(x-ix, y-iy) # Create the sub-image for this galaxy stamp = gal.drawImage(scale=pixel_scale/galsim.arcsec, offset=offset) stamp.setCenter(ix, iy) cen = stamp.array.shape # Add the hotspots as single pixel point sources stamp.array[cen[0]/2, cen[1]/2] += cat_gal['integrated_flux'] # Add the sub-image to the full iamge bounds = stamp.bounds & full_image.bounds full_image[bounds] += stamp[bounds] else: lobe_flux = cat_gal['integrated_flux']*0.99 hs_flux = cat_gal['integrated_flux'] - lobe_flux hs1_flux = hs_flux/3. hs2_flux = hs_flux/3. hs3_flux = hs_flux/3. hs_offset = rs[i]*cat_gal['size'] lobe_offset = cat_gal['size']*0.6 lobe1 = galsim.Gaussian(sigma=cat_gal['size']*0.25, flux=lobe_flux/2., gsparams=big_fft_params) lobe2 = galsim.Gaussian(sigma=cat_gal['size']*0.25, flux=lobe_flux/2., gsparams=big_fft_params) lobe1 = lobe1.shear(e1=0.3,e2=0) lobe2 = lobe2.shear(e1=0.3,e2=0) lobe1 = lobe1.shift(-lobe_offset,0) lobe2 = lobe2.shift(lobe_offset,0) gal = lobe1 + lobe2 gal = gal.rotate(posang[i]*galsim.radians) total_shear = galsim.Shear(g1=cat_gal['g1'], g2=cat_gal['g2']) gal = gal.shear(total_shear) x, y = w_twod.wcs_world2pix(cat_gal['ra_abs'], cat_gal['dec_abs'], 0,) x = float(x) y = float(y) # Account for the fractional part of the position: ix = int(np.floor(x+0.5)) iy = int(np.floor(y+0.5)) ix_arr[i] = ix iy_arr[i] = iy offset = galsim.PositionD(x-ix, y-iy) hs_offset_pixels = hs_offset*pixel_scale/galsim.arcsec hs_ix_offset = hs_offset*np.sin(posang[i]) / (pixel_scale/galsim.arcsec) hs_iy_offset = hs_offset*np.cos(posang[i]) / (pixel_scale/galsim.arcsec) # Create the sub-image for this galaxy stamp = gal.drawImage(scale=pixel_scale/galsim.arcsec, offset=offset) stamp.setCenter(ix, iy) cen = stamp.array.shape # Add the hotspots as single pixel point sources stamp.array[cen[0]/2, cen[1]/2] += hs1_flux stamp.array[cen[0]/2+hs_ix_offset, cen[1]/2+hs_iy_offset] += hs2_flux stamp.array[cen[0]/2-hs_ix_offset, cen[1]/2-hs_iy_offset] += hs3_flux if config.getboolean('skymodel', 'pickleagn'): pickle.dump(stamp.array, open('agn_{0}.p'.format(i), 'wb')) # Add the sub-image to the full iamge bounds = stamp.bounds & full_image.bounds full_image[bounds] += stamp[bounds] sys.stdout.flush() tend = time.time() print('\n...done in {0} seconds.'.format(tend-tstart)) all_gals_fname = data_path+config.get('field', 'fitsname') print('Writing image data to {0} ...'.format(all_gals_fname)) # Extract the numpy array from the galsim image image_data = full_image.array # Write out the image with the 4D FITS header correct for e.g. casa simulation #write4dImage(all_gals_fname, image_data, # pixel_scale / galsim.degrees, # obs_ra=ra_field_gs / galsim.degrees, # obs_dec=dec_field_gs / galsim.degrees, # obs_freq=config.getfloat('observation', 'lowest_frequency')) if config.getboolean('primarybeam', 'dopb'): nstokes = config.getint('primarybeam', 'nstokes') nfreq = config.getint('primarybeam', 'nfreq') bw = config.getfloat('observation', 'total_bandwidth') base_freq = config.getfloat('observation', 'lowest_frequency') freq_width = bw / nfreq image_cube = np.empty((nstokes,nfreq)+image_data.shape) stokes_list = ['I', 'Q', 'U'][:nstokes] freq_list = base_freq + np.arange(nfreq)*freq_width for i_stokes, stokes in enumerate(stokes_list): for i_freq, freq in enumerate(freq_list): image_cube[i_stokes,i_freq] = image_data*primary_beam(config, freq) hdu = fits.PrimaryHDU(image_cube, header=header_fourd) else: hdu = fits.PrimaryHDU(np.expand_dims(np.expand_dims(image_data, axis=0), axis=0), header=header_fourd) hdulist = fits.HDUList([hdu]) hdulist.writeto(all_gals_fname, clobber=True) print('...done.') if config.getboolean('skymodel', 'im3cat'): np.savetxt(config.get('pipeline', 'data_path')+config.get('pipeline', 'project_name')+'_im3cat.txt', np.column_stack([np.arange(nobj), ix_arr, iy_arr])) # Write out the image with the 4D FITS header correct for e.g. casa simulation write4dImage(all_gals_fname, image_data, pixel_scale / galsim.degrees, obs_ra=ra_field_gs / galsim.degrees, obs_dec=dec_field_gs / galsim.degrees, obs_freq=config.getfloat('observation', 'lowest_frequency')) print('runSkyModel complete.')
def runSkyModel(config): '''Simulate a sky model from a T-RECS catalogue. Parameters ---------- config : configparser ConfigParser configuration containing necessary sections. ''' data_path = config.get('pipeline', 'data_path') # Set some image properties pixel_scale = config.getfloat('skymodel', 'pixel_scale')*galsim.arcsec fov = config.getfloat('skymodel', 'field_of_view')*galsim.arcmin image_size = int((fov/galsim.arcmin)/(pixel_scale/galsim.arcmin)) ra_field = config.get('field', 'field_ra') ra_field_gs = galsim.HMS_Angle(ra_field) dec_field = config.get('field', 'field_dec') dec_field_gs = galsim.DMS_Angle(dec_field) # Load the catalogue cat_file_name = config.get('field', 'catalogue') print('Loading catalogue from {0} ...'.format(cat_file_name)) cat = fits.getdata(cat_file_name) # Set up a WCS for the catalogue cat_wcs = ast_wcs.WCS(naxis=2) cat_wcs.wcs.crpix = [image_size/2, image_size/2] cat_wcs.wcs.cdelt = [pixel_scale/galsim.degrees, pixel_scale/galsim.degrees] cat_wcs.wcs.crval = [0.e0, 0.e0] cat_wcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] cat_fov_max = float(fov/galsim.arcmin)/(2.*60.) # cat is generated on 1 degsq fov_cut = (abs(cat['latitude']) < cat_fov_max)*(abs(cat['longitude']) < cat_fov_max) #pdb.set_trace() cat = cat[fov_cut] if config.getboolean('skymodel', 'highfluxcut'): highflux_cut = cat['I1400']*1.e-3 < 500.e-6 cat = cat[highflux_cut] if config.getboolean('skymodel', 'lowfluxcut'): lowflux_cut = cat['I1400']*1.e-3 > 25.e-6 cat = cat[lowflux_cut] if config.getboolean('skymodel', 'highsizecut'): highsize_cut = cat['size']/2. < 10 cat = cat[highsize_cut] if config.getboolean('skymodel', 'lowsizecut'): lowsize_cut = cat['size']/2. > 0.75 cat = cat[lowsize_cut] gal_ra = cat['latitude'] gal_dec = cat['longitude'] gal_e1 = cat['e1'] gal_e2 = cat['e2'] gal_flux = cat['I1400']*1.e-3 #mjy convert to Jy gal_r0 = cat['size']/2.# ????? Factor of 2? g1 = 0 g2 = 0 if config.get('skymodel', 'fluxscale')=='constant': gal_flux = np.ones_like(gal_flux)*100e-6 nobj = len(cat) if config.getint('skymodel', 'ngals') > -1: nobj = config.getint('skymodel', 'ngals') ix_arr = np.ones(nobj) iy_arr = np.ones(nobj) print('...done.') # Create the galsim image full_image = galsim.ImageF(image_size, image_size, scale=pixel_scale) im_center = full_image.bounds.trueCenter() sky_center = galsim.CelestialCoord(ra=ra_field_gs, dec=dec_field_gs) # Create a WCS for the galsim image dudx = -pixel_scale / galsim.arcsec # - on dx's since the ra axis is flipped. dudy = 0. dvdx = 0. dvdy = pixel_scale / galsim.arcsec image_center = full_image.trueCenter() affine = galsim.AffineTransform(dudx, dudy, dvdx, dvdy, origin=full_image.trueCenter()) wcs = galsim.TanWCS(affine, sky_center, units=galsim.arcsec) full_image.wcs = wcs tstart=time.time() # Draw the galaxies onto the galsim image for i in range(nobj): sys.stdout.write('\rAdding source {0} of {1} to skymodel...'.format(i+1, nobj)) gal = galsim.Exponential(scale_radius=gal_r0[i], flux=gal_flux[i]) ellipticity = galsim.Shear(e1=gal_e1[i],e2=gal_e2[i]) shear = galsim.Shear(g1=g1[i],g2=g2[i]) total_shear = ellipticity + shear gal = gal.shear(total_shear) x, y = cat_wcs.wcs_world2pix(gal_ra[i], gal_dec[i], 0) x = float(x) y = float(y) # Account for the fractional part of the position: ix = int(np.floor(x+0.5)) iy = int(np.floor(y+0.5)) ix_arr[i] = ix iy_arr[i] = iy offset = galsim.PositionD(x-ix, y-iy) # Create the sub-image for this galaxy stamp = gal.drawImage(scale=pixel_scale/galsim.arcsec, offset=offset) stamp.setCenter(ix, iy) # Add the sub-image to the full iamge bounds = stamp.bounds & full_image.bounds full_image[bounds] += stamp[bounds] sys.stdout.flush() tend = time.time() print('\n...done in {0} seconds.'.format(tend-tstart)) all_gals_fname = data_path+config.get('field', 'fitsname') print('Writing image data to {0} ...'.format(all_gals_fname)) # Extract the numpy array from the galsim image image_data = full_image.array # Write out the image with the 4D FITS header correct for e.g. casa simulation write4dImage(all_gals_fname, image_data, pixel_scale / galsim.degrees, obs_ra=ra_field_gs / galsim.degrees, obs_dec=dec_field_gs / galsim.degrees, obs_freq=config.getfloat('observation', 'lowest_frequency')) print('...done.') if config.getboolean('skymodel', 'im3cat'): np.savetxt(config.get('pipeline', 'data_path')+'im3cat.txt', np.column_stack([np.arange(nobj), ix_arr, iy_arr])) print('runSkyModel complete.')
def makeThumbnails(config): # read in catalogue # extract thumbnails at all points in catalogue data_path = config.get('pipeline', 'data_path') # Set some image properties pixel_scale = config.getfloat('skymodel', 'pixel_scale') * galsim.arcsec fov = config.getfloat('skymodel', 'field_of_view') * galsim.arcmin image_size = int((fov / galsim.arcmin) / (pixel_scale / galsim.arcmin)) ra_field = config.get('field', 'field_ra') ra_field_gs = galsim.HMS_Angle(ra_field) dec_field = config.get('field', 'field_dec') dec_field_gs = galsim.DMS_Angle(dec_field) # Load the wcs header = fits.getheader( config.get('pipeline', 'data_path') + config.get('field', 'fitsname')) w_twod = ast_wcs.WCS(header) # Load the catalogue cat_file_name = config.get('field', 'catalogue') print('Loading catalogue from {0} ...'.format(cat_file_name)) cat = fits.getdata(cat_file_name) ra_offset = cat['longitude'] dec_offset = cat['latitude'] ra_offset_max = 0.9 * (fov / 2) / galsim.degrees dec_offset_max = 0.9 * (fov / 2) / galsim.degrees fov_cut = (abs(ra_offset) < ra_offset_max) * (abs(dec_offset) < dec_offset_max) #pdb.set_trace() cat = cat[fov_cut] if config.getboolean('skymodel', 'highfluxcut'): highflux_cut = cat['I1400'] * 1.e-3 < 500.e-6 cat = cat[highflux_cut] if config.getboolean('skymodel', 'lowfluxcut'): lowflux_cut = cat['I1400'] * 1.e-3 > 25.e-6 cat = cat[lowflux_cut] if config.getboolean('skymodel', 'highsizecut'): highsize_cut = cat['size'] / 2. < 10 cat = cat[highsize_cut] if config.getboolean('skymodel', 'lowsizecut'): lowsize_cut = cat['size'] / 2. > 0.75 cat = cat[lowsize_cut] gal_ra_offset = cat[' longitude'] gal_dec_offset = cat['latitude'] gal_dec = dec_field_gs / galsim.degrees + gal_dec_offset gal_dec_radians = (gal_dec * galsim.degrees) / galsim.radians gal_ra = ra_field_gs / galsim.degrees + gal_ra_offset / np.cos( np.asarray(gal_dec_radians, dtype=float)) gal_e1 = cat['e1'] gal_e2 = cat['e2'] gal_flux = cat['I1400'] * 1.e-3 #mjy convert to Jy gal_r0 = cat['size'] / 2. # ????? Factor of 2? g1 = 0 g2 = 0 if config.get('skymodel', 'fluxscale') == 'constant': gal_flux = np.ones_like(gal_flux) * np.median(gal_flux) * 1.e4 nobj = len(cat) if config.getint('skymodel', 'ngals') > 0: nobj = config.getint('skymodel', 'ngals') print('...done.') #pdb.set_trace() for i in range(nobj): plt.close('all') sys.stdout.write( '\rCreating thumbnail for source {0} of {1}...'.format( i + 1, nobj)) npix_stamp = 10 * int((gal_r0[i]) / (pixel_scale / galsim.arcsec)) x, y, _, _ = w_twod.wcs_world2pix(gal_ra[i], gal_dec[i], 0, 0, 0) x = float(x) y = float(y) # Account for the fractional part of the position: ix = int(np.floor(x + 0.5)) iy = int(np.floor(y + 0.5)) plt.figure(i, figsize=(9, 7.5)) plt.subplot(221) im = fits.getdata( config.get('pipeline', 'data_path') + config.get('field', 'fitsname')) im = im[0, 0] stamp = im[iy - npix_stamp / 2:iy + npix_stamp / 2, ix - npix_stamp / 2:ix + npix_stamp / 2] plt.imshow(stamp, cmap='afmhot', origin='lower', interpolation='nearest') plt.subplot(222) im = fits.getdata( config.get('pipeline', 'data_path') + config.get('pipeline', 'project_name') + '.wsclean-dirty.fits') im = im[0, 0, :, ::-1] stamp = im[iy - npix_stamp / 2:iy + npix_stamp / 2, ix - npix_stamp / 2:ix + npix_stamp / 2] plt.imshow(stamp, cmap='afmhot', origin='lower', interpolation='nearest') #pdb.set_trace() plt.subplot(223) if config.get('imager', 'type') == 'wsclean': im = fits.getdata(config.get('pipeline', 'data_path')+\ config.get('pipeline', 'project_name')+'.wsclean-image.fits') im = im[0, 0, :, ::-1] elif config.get('imager', 'type') == 'casa': im = fits.getdata(config.get('pipeline', 'data_path')+\ config.get('pipeline', 'project_name')+'.casa-cs.image.fits') im = im[0, 0, :, ::-1] size_diff = im.shape[0] - image_size im = im[size_diff / 2:-size_diff / 2, size_diff / 2:-size_diff / 2] stamp = im[iy - npix_stamp / 2:iy + npix_stamp / 2, ix - npix_stamp / 2:ix + npix_stamp / 2] plt.imshow(stamp, cmap='afmhot', origin='lower', interpolation='nearest') plt.subplot(224) if config.get('imager', 'type') == 'wsclean': im = fits.getdata(config.get('pipeline', 'data_path')+\ config.get('pipeline', 'project_name')+'.wsclean-model.fits') im = im[0, 0, :, ::-1] elif config.get('imager', 'type') == 'casa': im = fits.getdata(config.get('pipeline', 'data_path')+\ config.get('pipeline', 'project_name')+'.casa-cs.model.fits') im = im[0, 0, :, ::-1] size_diff = im.shape[0] - image_size im = im[size_diff / 2:-size_diff / 2, size_diff / 2:-size_diff / 2] stamp = im[iy - npix_stamp / 2:iy + npix_stamp / 2, ix - npix_stamp / 2:ix + npix_stamp / 2] plt.imshow(stamp, cmap='afmhot', origin='lower', interpolation='nearest') plt.savefig(config.get('pipeline', 'data_path') + config.get('pipeline', 'project_name') + 'source_{0}.png'.format(i), bbox_inches='tight', dpi=160) sys.stdout.flush() print('\n...done.')
def read_image_header(img_file): """Read some information from the image header. Returns (date, time, filter, ccdnum, detpos, telra, teldec, ha, airmass, sky, sigsky, fwhm, tiling, hex, wcs) """ print 'Start read_image_header' print img_file import galsim import astropy.io.fits as pyfits if img_file.endswith('fz'): hdu = 1 else: hdu = 0 # fitsio is a bit faster here. 11 sec/exp rather than 12, so not a huge difference, but still. with pyfits.open(img_file) as pyf: #print pyf #print pyf[hdu] h = pyf[hdu].header #h = pyf[hdu].read_header() #print 'opened' # DATE-OBS looks like '2012-12-03T07:38:54.174780', so split on T. date = h['DATE-OBS'] date, time = date.strip().split('T', 1) # FILTER looks like 'z DECam SDSS c0004 9260.0 1520.0', so split on white space filter = h['FILTER'] filter = filter.split()[0] # CCDNUM is 1-62. DETPOS is a string such as 'S29 '. Strip off the whitespace. ccdnum = h['CCDNUM'] ccdnum = int(ccdnum) detpos = h['DETPOS'] detpos = detpos.strip() #print 'detpos = ',detpos # TELRA, TELDEC look like '-62:30:22.320'. Use GalSim to convert to decimal degrees. telra = h['TELRA'] teldec = h['TELDEC'] telra = galsim.HMS_Angle(telra) / galsim.degrees teldec = galsim.DMS_Angle(teldec) / galsim.degrees #print 'telra, teldec = ',telra,teldec # HA looks like '03:12:02.70''. Use GalSim to convert to decimal degrees. ha = h['HA'] ha = galsim.HMS_Angle(ha) / galsim.degrees # A few more items to grab from the header, but allow default values for these: airmass = float(h.get('AIRMASS', -999)) sky = float(h.get('SKYBRITE', -999)) sigsky = float(h.get('SKYSIGMA', -999)) fwhm = float(h.get('FWHM', -999)) tiling = int(h.get('TILING', 0)) hex = int(h.get('HEX', 0)) # Use Galsim to read WCS wcs = galsim.FitsWCS(header=h) #print 'wcs = ',wcs return (date, time, filter, ccdnum, detpos, telra, teldec, ha, airmass, sky, sigsky, fwhm, tiling, hex, wcs)
def setPointing(self, logger=None): """Set the pointing attribute based on the input ra, dec (given in the initializer) There are a number of ways the pointing can be specified. Even this is probably not sufficiently generic for all applications, but it's a start. 1. numerical values (in Hours, Degrees respective) for ra, dec 2. hh:mm:ss.ssss, dd:mm:ss.ssss strings giving hours/degrees, minutes, seconds for each 3. FITS header key words to read to get the ra, dec values 4. None, which will attempt to find the spatial center of all the input images using the midpoint of the min/max ra and dec values of the image corners according to their individual WCS functions. [Not implemented currently.] """ import fitsio import galsim ra = self.ra dec = self.dec if (ra is None) != (dec is None): raise ValueError("Only one of ra, dec was specified") if ra is None: if self.images[0].wcs.isCelestial(): if len(self.images) == 1: # Here we can just use the image center. im = self.images[0] self.pointing = im.wcs.toWorld(im.trueCenter()) if logger: logger.info("Setting pointing to image center: %.3f h, %.3f d", self.pointing.ra / galsim.hours, self.pointing.dec / galsim.degrees) else: raise NotImplemented("The automatic pointing calculation is not implemented yet.") else: self.pointing = None elif type(ra) in [float, int]: ra = float(ra) * galsim.hours dec = float(dec) * galsim.degrees self.pointing = galsim.CelestialCoord(ra,dec) if logger: logger.info("Setting pointing to: %.3f h, %.3f d", self.pointing.ra / galsim.hours, self.pointing.dec / galsim.degrees) elif str(ra) != ra: raise ValueError("Unable to parse input ra: %s"%ra) elif str(dec) != dec: raise ValueError("Unable to parse input dec: %s"%dec) elif ':' in ra and ':' in dec: ra = galsim.HMS_Angle(ra) dec = galsim.DMS_Angle(dec) self.pointing = galsim.CelestialCoord(ra,dec) if logger: logger.info("Setting pointing to: %.3f h, %.3f d", self.pointing.ra / galsim.hours, self.pointing.dec / galsim.degrees) else: file_name = self.image_files[0] if logger: if len(self.image_files) == 1: logger.info("Setting pointing from keywords %s, %s", ra, dec) else: logger.info("Setting pointing from keywords %s, %s in %s", ra, dec, file_name) fits = fitsio.FITS(file_name) hdu = 1 if file_name.endswith('.fz') else 0 header = fits[hdu].read_header() self.ra = header[ra] self.dec = header[dec] # Recurse to do further parsing. self.setPointing(logger)