def import_image_from_fits(fitsfile: str, mute_warnings=True) -> Image: """ Read an Image from fits :param fitsfile: :return: Image """ fim = Image() with warnings.catch_warnings(): warnings.simplefilter('ignore') hdulist = fits.open(arl_path(fitsfile)) fim.data = hdulist[0].data fim.wcs = WCS(arl_path(fitsfile)) hdulist.close() if len(fim.data) == 2: fim.polarisation_frame = PolarisationFrame('stokesI') else: try: fim.polarisation_frame = polarisation_frame_from_wcs( fim.wcs, fim.data.shape) except: fim.polarisation_frame = PolarisationFrame('stokesI') log.debug( "import_image_from_fits: created %s image of shape %s, size %.3f (GB)" % (fim.data.dtype, str(fim.shape), image_sizeof(fim))) log.debug("import_image_from_fits: Max, min in %s = %.6f, %.6f" % (fitsfile, fim.data.max(), fim.data.min())) assert isinstance(fim, Image) return fim
def create_test_image( canonical=True, cellsize=None, frequency=None, channel_bandwidth=None, phasecentre=None, polarisation_frame=PolarisationFrame("stokesI")) -> Image: """Create a useful test image This is the test image M31 widely used in ALMA and other simulations. It is actually part of an Halpha region in M31. :param canonical: Make the image into a 4 dimensional image :param cellsize: :param frequency: Frequency (array) in Hz :param channel_bandwidth: Channel bandwidth (array) in Hz :param phasecentre: Phase centre of image (SkyCoord) :param polarisation_frame: Polarisation frame :return: Image """ if frequency is None: frequency = [1e8] im = import_image_from_fits(arl_path("data/models/M31.MOD")) if canonical: if polarisation_frame is None: im.polarisation_frame = PolarisationFrame("stokesI") elif isinstance(polarisation_frame, PolarisationFrame): im.polarisation_frame = polarisation_frame else: raise ValueError("polarisation_frame is not valid") im = replicate_image(im, frequency=frequency, polarisation_frame=im.polarisation_frame) if cellsize is not None: im.wcs.wcs.cdelt[0] = -180.0 * cellsize / numpy.pi im.wcs.wcs.cdelt[1] = +180.0 * cellsize / numpy.pi if frequency is not None: im.wcs.wcs.crval[3] = frequency[0] if channel_bandwidth is not None: im.wcs.wcs.cdelt[3] = channel_bandwidth[0] else: if len(frequency) > 1: im.wcs.wcs.cdelt[3] = frequency[1] - frequency[0] else: im.wcs.wcs.cdelt[3] = 0.001 * frequency[0] im.wcs.wcs.radesys = 'ICRS' im.wcs.wcs.equinox = 2000.00 if phasecentre is not None: im.wcs.wcs.crval[0] = phasecentre.ra.deg im.wcs.wcs.crval[1] = phasecentre.dec.deg # WCS is 1 relative im.wcs.wcs.crpix[0] = im.data.shape[3] // 2 + 1 im.wcs.wcs.crpix[1] = im.data.shape[2] // 2 + 1 return im
def create_low_test_beam(model: Image) -> Image: """Create a test power beam for LOW using an image from OSKAR :param model: Template image :return: Image """ beam = import_image_from_fits(arl_path('data/models/SKA1_LOW_beam.fits')) # Scale the image cellsize to account for the different in frequencies. Eventually we will want to # use a frequency cube log.info("create_low_test_beam: primary beam is defined at %.3f MHz" % (beam.wcs.wcs.crval[2] * 1e-6)) nchan, npol, ny, nx = model.shape # We need to interpolate each frequency channel separately. The beam is assumed to just scale with # frequency. reprojected_beam = create_empty_image_like(model) for chan in range(nchan): model2dwcs = model.wcs.sub(2).deepcopy() model2dshape = [model.shape[2], model.shape[3]] beam2dwcs = beam.wcs.sub(2).deepcopy() # The frequency axis is the second to last in the beam frequency = model.wcs.sub(['spectral']).wcs_pix2world([chan], 0)[0] fscale = beam.wcs.wcs.crval[2] / frequency beam2dwcs.wcs.cdelt = fscale * beam.wcs.sub(2).wcs.cdelt beam2dwcs.wcs.crpix = beam.wcs.sub(2).wcs.crpix beam2dwcs.wcs.crval = model.wcs.sub(2).wcs.crval beam2dwcs.wcs.ctype = model.wcs.sub(2).wcs.ctype model2dwcs.wcs.crpix = [ model.shape[2] // 2 + 1, model.shape[3] // 2 + 1 ] beam2d = create_image_from_array(beam.data[0, 0, :, :], beam2dwcs, model.polarisation_frame) reprojected_beam2d, footprint = reproject_image(beam2d, model2dwcs, shape=model2dshape) assert numpy.max( footprint.data) > 0.0, "No overlap between beam and model" reprojected_beam2d.data *= reprojected_beam2d.data reprojected_beam2d.data[footprint.data <= 0.0] = 0.0 for pol in range(npol): reprojected_beam.data[chan, pol, :, :] = reprojected_beam2d.data[:, :] return reprojected_beam
def test_visibility_from_oskar(self): for oskar_file in [ "data/vis/vla_1src_6h/test_vla.vis", "data/vis/vla_grid_6h/test_vla.vis" ]: vis = import_visibility_from_oskar(arl_path(oskar_file)) self.assertEqual( len(numpy.unique(vis.antenna1)) + 1, len(vis.configuration.xyz)) self.assertEqual( len(numpy.unique(vis.antenna2)) + 1, len(vis.configuration.xyz))
def create_named_configuration(name: str = 'LOWBD2', **kwargs) -> Configuration: """ Standard configurations e.g. LOWBD2, MIDBD2 :param name: name of Configuration LOWBD2, LOWBD1, LOFAR, VLAA :param rmax: Maximum distance of station from the average (m) :return: For LOWBD2, setting rmax gives the following number of stations 100.0 13 300.0 94 1000.0 251 3000.0 314 10000.0 398 30000.0 476 100000.0 512 """ if name == 'LOWBD2': location = EarthLocation(lon="116.4999", lat="-26.7000", height=300.0) fc = create_configuration_from_file(antfile=arl_path("data/configurations/LOWBD2.csv"), location=location, mount='xy', names='LOWBD2_%d', diameter=35.0, **kwargs) elif name == 'LOWBD1': location = EarthLocation(lon="116.4999", lat="-26.7000", height=300.0) fc = create_configuration_from_file(antfile=arl_path("data/configurations/LOWBD1.csv"), location=location, mount='xy', names='LOWBD1_%d', diameter=35.0, **kwargs) elif name == 'LOWBD2-CORE': location = EarthLocation(lon="116.4999", lat="-26.7000", height=300.0) fc = create_configuration_from_file(antfile=arl_path("data/configurations/LOWBD2-CORE.csv"), location=location, mount='xy', names='LOWBD2_%d', diameter=35.0, **kwargs) elif name == 'LOFAR': fc = create_LOFAR_configuration(antfile=arl_path("data/configurations/LOFAR.csv"), diameter=35.0, **kwargs) elif name == 'VLAA': location = EarthLocation(lon="-107.6184", lat="34.0784", height=2124.0) fc = create_configuration_from_file(antfile=arl_path("data/configurations/VLA_A_hor_xyz.csv"), location=location, mount='altaz', names='VLA_%d', diameter=25.0, **kwargs) elif name == 'VLAA_north': location = EarthLocation(lon="-107.6184", lat="90.000", height=2124.0) fc = create_configuration_from_file(antfile=arl_path("data/configurations/VLA_A_hor_xyz.csv"), location=location, mount='altaz', names='VLA_%d', diameter=25.0, **kwargs) else: raise ValueError("No such Configuration %s" % name) return fc
def create_named_configuration(name: str = 'LOWBD2', **kwargs): """ Standard configurations e.g. LOWBD2, MIDBD2 :param name: name of Configuration LOWBD2, LOWBD1, LOFAR, VLAA :returns: Configuration """ if name == 'LOWBD2': location = EarthLocation(lon="116.4999", lat="-26.7000", height=300.0) fc = create_configuration_from_file(antfile=arl_path("data/configurations/LOWBD2.csv"), location=location, mount='xy', names='LOWBD2_%d', diameter=35.0) elif name == 'LOWBD1': location = EarthLocation(lon="116.4999", lat="-26.7000", height=300.0) fc = create_configuration_from_file(antfile=arl_path("data/configurations/LOWBD1.csv"), location=location, mount='xy', names='LOWBD1_%d', diameter=35.0) elif name == 'LOWBD2-CORE': location = EarthLocation(lon="116.4999", lat="-26.7000", height=300.0) fc = create_configuration_from_file(antfile=arl_path("data/configurations/LOWBD2-CORE.csv"), location=location, mount='xy', names='LOWBD2_%d', diameter=35.0) elif name == 'LOFAR': fc = create_LOFAR_configuration(antfile=arl_path("data/configurations/LOFAR.csv"), diameter=35.0) elif name == 'VLAA': location = EarthLocation(lon="-107.6184", lat="34.0784", height=2124.0) fc = create_configuration_from_file(antfile=arl_path("data/configurations/VLA_A_hor_xyz.csv"), location=location, mount='altaz', names='VLA_%d', diameter=25.0) elif name == 'VLAA_north': location = EarthLocation(lon="-107.6184", lat="90.000", height=2124.0) fc = create_configuration_from_file(antfile=arl_path("data/configurations/VLA_A_hor_xyz.csv"), location=location, mount='altaz', names='VLA_%d', diameter=25.0) else: fc = Configuration() raise ValueError("No such Configuration %s" % name) return fc
def create_low_test_image_from_s3(npixel=16384, polarisation_frame=PolarisationFrame( "stokesI"), cellsize=0.000015, frequency=numpy.array([1e8]), channel_bandwidth=numpy.array([1e6]), phasecentre=None, fov=20) -> Image: """Create LOW test image from S3 The input catalog was generated at http://s-cubed.physics.ox.ac.uk/s3_sex using the following query:: Database: s3_sex SQL: select * from Galaxies where (pow(10,itot_151)*1000 > 1.0) and (right_ascension between -5 and 5) and (declination between -5 and 5);; Number of rows returned: 29966 There are three possible tables to use:: data/models/S3_151MHz_10deg.csv, use fov=10 data/models/S3_151MHz_20deg.csv, use fov=20 data/models/S3_151MHz_40deg.csv, use fov=40 The component spectral index is calculated from the 610MHz and 151MHz, and then calculated for the specified frequencies. If polarisation_frame is not stokesI then the image will a polarised axis but the values will be zero. :param npixel: Number of pixels :param polarisation_frame: Polarisation frame (default PolarisationFrame("stokesI")) :param cellsize: cellsize in radians :param frequency: :param channel_bandwidth: Channel width (Hz) :param phasecentre: phasecentre (SkyCoord) :param fov: fov table to use :return: Image """ ras = [] decs = [] fluxes = [] if phasecentre is None: phasecentre = SkyCoord(ra=+180.0 * u.deg, dec=-60.0 * u.deg, frame='icrs', equinox='J2000') if polarisation_frame is None: polarisation_frame = PolarisationFrame("I") npol = polarisation_frame.npol nchan = len(frequency) shape = [nchan, npol, npixel, npixel] w = WCS(naxis=4) # The negation in the longitude is needed by definition of RA, DEC w.wcs.cdelt = [ -cellsize * 180.0 / numpy.pi, cellsize * 180.0 / numpy.pi, 1.0, channel_bandwidth[0] ] w.wcs.crpix = [npixel // 2 + 1, npixel // 2 + 1, 1.0, 1.0] w.wcs.ctype = ["RA---SIN", "DEC--SIN", 'STOKES', 'FREQ'] w.wcs.crval = [phasecentre.ra.deg, phasecentre.dec.deg, 1.0, frequency[0]] w.naxis = 4 w.wcs.radesys = 'ICRS' w.wcs.equinox = 2000.0 model = create_image_from_array(numpy.zeros(shape), w, polarisation_frame=polarisation_frame) assert fov in [10, 20, 40], "Field of view invalid: use one of %s" % ([10, 20, 40]) with open(arl_path('data/models/S3_151MHz_%ddeg.csv' % (fov))) as csvfile: readCSV = csv.reader(csvfile, delimiter=',') r = 0 for row in readCSV: # Skip first row if r > 0: ra = float(row[4]) + phasecentre.ra.deg dec = float(row[5]) + phasecentre.dec.deg alpha = (float(row[10]) - float(row[9])) / numpy.log10( 610.0 / 151.0) flux = numpy.power(10, float(row[9])) * numpy.power( frequency / 1.51e8, alpha) ras.append(ra) decs.append(dec) fluxes.append(flux) r += 1 csvfile.close() p = w.sub(2).wcs_world2pix(numpy.array(ras), numpy.array(decs), 1) total_flux = numpy.sum(fluxes) fluxes = numpy.array(fluxes) ip = numpy.round(p).astype('int') ok = numpy.where((0 <= ip[0, :]) & (npixel > ip[0, :]) & (0 <= ip[1, :]) & (npixel > ip[1, :]))[0] ps = ip[:, ok] fluxes = fluxes[ok] actual_flux = numpy.sum(fluxes) log.info('create_low_test_image_from_s3: %d sources inside the image' % (ps.shape[1])) log.info( 'create_low_test_image_from_s3: average channel flux in S3 model = %.3f, actual average channel flux in ' 'image = %.3f' % (total_flux / float(nchan), actual_flux / float(nchan))) for chan in range(nchan): for iflux, flux in enumerate(fluxes): model.data[chan, 0, ps[1, iflux], ps[0, iflux]] = flux[chan] return model
def create_low_test_skycomponents_from_gleam(flux_limit=0.1, polarisation_frame=PolarisationFrame("stokesI"), frequency=numpy.array([1e8]), kind='cubic', phasecentre=None, radius=1.0) \ -> List[Skycomponent]: """Create sky components from the GLEAM survey Stokes I is estimated from a cubic spline fit to the measured fluxes. The polarised flux is always zero. See http://www.mwatelescope.org/science/gleam-survey The catalog is available from Vizier. VIII/100 GaLactic and Extragalactic All-sky MWA survey (Hurley-Walker+, 2016) GaLactic and Extragalactic All-sky Murchison Wide Field Array (GLEAM) survey. I: A low-frequency extragalactic catalogue. Hurley-Walker N., et al., Mon. Not. R. Astron. Soc., 464, 1146-1167 (2017), 2017MNRAS.464.1146H :rtype: Union[None, List[arl.data.data_models.Skycomponent], List] :param flux_limit: Only write components brighter than this (Jy) :param polarisation_frame: Polarisation frame (default PolarisationFrame("stokesI")) :param frequency: Frequencies at which the flux will be estimated :param kind: Kind of interpolation (see scipy.interpolate.interp1d) Default: linear :param phasecentre: Desired phase centre (SkyCoord) default None implies all sources :param radius: Radius of sources selected around phasecentre (default 1.0 rad) :return: List of Skycomponents """ fitsfile = arl_path("data/models/GLEAM_EGC.fits") rad2deg = 180.0 / numpy.pi decmin = phasecentre.dec.to('deg').value - rad2deg * radius / 2.0 decmax = phasecentre.dec.to('deg').value + rad2deg * radius / 2.0 hdulist = fits.open(fitsfile, lazy_load_hdus=False) recs = hdulist[1].data[0].array # Do the simple forms of filtering in pyfits. Filtering on radious is done below. fluxes = recs['peak_flux_wide'] mask = fluxes > flux_limit filtered_recs = recs[mask] decs = filtered_recs['DEJ2000'] mask = decs > decmin filtered_recs = filtered_recs[mask] decs = filtered_recs['DEJ2000'] mask = decs < decmax filtered_recs = filtered_recs[mask] ras = filtered_recs['RAJ2000'] decs = filtered_recs['DEJ2000'] names = filtered_recs['Name'] if polarisation_frame is None: polarisation_frame = PolarisationFrame("stokesI") npol = polarisation_frame.npol nchan = len(frequency) # For every source, we read all measured fluxes and interpolate to the # required frequencies gleam_freqs = numpy.array([ 76, 84, 92, 99, 107, 115, 122, 130, 143, 151, 158, 166, 174, 181, 189, 197, 204, 212, 220, 227 ]) gleam_flux_freq = numpy.zeros([len(names), len(gleam_freqs)]) for i, f in enumerate(gleam_freqs): gleam_flux_freq[:, i] = filtered_recs['int_flux_%03d' % (f)][:] skycomps = [] for isource, name in enumerate(names): direction = SkyCoord(ra=ras[isource] * u.deg, dec=decs[isource] * u.deg) if phasecentre is None or direction.separation(phasecentre).to( 'rad').value < radius: fint = interpolate.interp1d(gleam_freqs * 1.0e6, gleam_flux_freq[isource, :], kind=kind) flux = numpy.zeros([nchan, npol]) flux[:, 0] = fint(frequency) if not numpy.isnan(flux).any(): skycomps.append( Skycomponent(direction=direction, flux=flux, frequency=frequency, name=name, shape='Point', polarisation_frame=polarisation_frame)) log.info( 'create_low_test_skycomponents_from_gleam: %d sources above flux limit %.3f' % (len(skycomps), flux_limit)) hdulist.close() return skycomps
def create_low_test_image_from_gleam(npixel=512, polarisation_frame=PolarisationFrame("stokesI"), cellsize=0.000015, frequency=numpy.array([1e8]), channel_bandwidth=numpy.array([1e6]), phasecentre=None, kind='cubic'): """Create LOW test image from the GLEAM survey Stokes I is estimated from a cubic spline fit to the measured fluxes. The polarised flux is always zero. See http://www.mwatelescope.org/science/gleam-survey The catalog is available from Vizier. VIII/100 GaLactic and Extragalactic All-sky MWA survey (Hurley-Walker+, 2016) GaLactic and Extragalactic All-sky Murchison Wide Field Array (GLEAM) survey. I: A low-frequency extragalactic catalogue. Hurley-Walker N., et al., Mon. Not. R. Astron. Soc., 464, 1146-1167 (2017), 2017MNRAS.464.1146H :param npixel: Number of pixels :param polarisation_frame: Polarisation frame (default PolarisationFrame("stokesI")) :param cellsize: cellsize in radians :param frequency: :param channel_bandwidth: Channel width (Hz) :param phasecentre: phasecentre (SkyCoord) :param kind: Kind of interpolation (see scipy.interpolate.interp1d) Default: linear :returns: Image """ fitsfile = arl_path("data/models/GLEAM_EGC.fits") hdulist = fits.open(fitsfile, lazy_load_hdus=False) recs = hdulist[1].data[0].array ras = recs['RAJ2000'] decs = recs['DEJ2000'] if phasecentre is None: phasecentre = SkyCoord(ra=+15.0 * u.deg, dec=-35.0 * u.deg, frame='icrs', equinox=2000.0) if polarisation_frame is None: polarisation_frame = PolarisationFrame("stokesI") npol = polarisation_frame.npol nchan = len(frequency) shape = [nchan, npol, npixel, npixel] w = WCS(naxis=4) # The negation in the longitude is needed by definition of RA, DEC w.wcs.cdelt = [-cellsize * 180.0 / numpy.pi, cellsize * 180.0 / numpy.pi, 1.0, channel_bandwidth[0]] w.wcs.crpix = [npixel // 2, npixel // 2, 1.0, 1.0] w.wcs.ctype = ["RA---SIN", "DEC--SIN", 'STOKES', 'FREQ'] w.wcs.crval = [phasecentre.ra.deg, phasecentre.dec.deg, 1.0, frequency[0]] w.naxis = 4 w.wcs.radesys = 'ICRS' w.wcs.equinox = 2000.0 model = create_image_from_array(numpy.zeros(shape), w, polarisation_frame=polarisation_frame) p = w.sub(2).wcs_world2pix(numpy.array(ras), numpy.array(decs), 1) p = numpy.array(p) mask = numpy.isfinite(p[0, :]) p = [p[0, mask], p[1, mask]] ip = numpy.round(p).astype('int') ok = numpy.where((0 <= ip[0, :]) & (npixel > ip[0, :]) & (0 <= ip[1, :]) & (npixel > ip[1, :]))[0] ps = ip[:, ok] # For every source, we read all measured fluxes and interpolate to the # required frequencies fluxes = [] gleam_freqs = numpy.array([76, 84, 92, 99, 107, 115, 122, 130, 143, 151, 158, 166, 174, 181, 189, 197, 204, 212, 220, 227]) for source in ok: this_source_fluxes = numpy.zeros(len(gleam_freqs)) for i, f in enumerate(gleam_freqs): this_source_fluxes[i] = recs['int_flux_%03d' % (f)][source] fint = interpolate.interp1d(gleam_freqs * 1.0e6, this_source_fluxes, kind=kind) fluxes.append(fint(frequency)) fluxes = numpy.array(fluxes) actual_flux = numpy.sum(fluxes) log.info('create_low_test_image_from_gleam: %d sources inside the image' % (ps.shape[1])) log.info('create_low_test_image_from_gleam: Average flux per channel in image = %.3f' % (actual_flux/float(nchan))) for iflux, flux in enumerate(fluxes): if not numpy.isnan(flux).any() and flux.all() > 0.0: model.data[:, 0, ps[1, iflux], ps[0, iflux]] = flux[:] hdulist.close() return model