Пример #1
0
    def test_create_gaintable_from_screen_ionosphere(self):
        self.actualSetup("ionosphere")
        screen = import_image_from_fits(
            rascil_data_path('models/test_mpc_screen.fits'))
        beam = create_test_image(cellsize=0.0015,
                                 phasecentre=self.vis.phasecentre,
                                 frequency=self.frequency)

        beam = create_low_test_beam(beam, use_local=False)

        gleam_components = \
            create_low_test_skycomponents_from_gleam(flux_limit=1.0,
                                                     phasecentre=self.phasecentre,
                                                     frequency=self.frequency,
                                                     polarisation_frame=PolarisationFrame('stokesI'),
                                                     radius=0.2)

        pb_gleam_components = apply_beam_to_skycomponent(
            gleam_components, beam)

        actual_components = filter_skycomponents_by_flux(pb_gleam_components,
                                                         flux_min=1.0)

        gaintables = create_gaintable_from_screen(self.vis, actual_components,
                                                  screen)
        assert len(gaintables) == len(actual_components), len(gaintables)
        assert gaintables[0].gain.shape == (3, 94, 1, 1,
                                            1), gaintables[0].gain.shape
    def test_polarisation_frame_from_wcs_jones(self):
        vp = import_image_from_fits(
            rascil_data_path('models/MID_FEKO_VP_B2_45_1360_real.fits'))
        imag_vp = import_image_from_fits(
            rascil_data_path('models/MID_FEKO_VP_B2_45_1360_imag.fits'))
        vp.data = vp.data + 1j * imag_vp.data

        polframe = polarisation_frame_from_wcs(vp.wcs, vp.shape)

        newvp_data = vp.data.copy()
        newvp_data[:, 3] = vp.data[:, 1]
        newvp_data[:, 1] = vp.data[:, 2]
        newvp_data[:, 2] = vp.data[:, 3]
        vp.data = newvp_data

        assert vp.polarisation_frame == PolarisationFrame("linear")
def create_low_test_beam(model: Image, use_local=True) -> Image:
    """Create a test power beam for LOW using an image from OSKAR

    This is not fit for anything except the most basic testing. It does not include any form of elevation/pa dependence.

    :param model: Template image
    :return: Image
    """
    beam = import_image_from_fits(
        rascil_data_path('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.debug(
        "create_low_test_beam: LOW voltage pattern 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[footprint.data <= 0.0] = 0.0
        for pol in range(npol):
            reprojected_beam.data[chan,
                                  pol, :, :] = reprojected_beam2d.data[:, :]

    set_pb_header(reprojected_beam, use_local=use_local)
    return reprojected_beam
    def test_create_gradient(self):
        real_vp = import_image_from_fits(rascil_data_path('models/MID_GRASP_VP_real.fits'))
        gradx, grady = image_gradients(real_vp)
        
        gradxx, gradxy = image_gradients(gradx)
        gradyx, gradyy = image_gradients(grady)

        gradx.data *= real_vp.data
        grady.data *= real_vp.data
        gradxx.data *= real_vp.data
        gradxy.data *= real_vp.data
        gradyx.data *= real_vp.data
        gradyy.data *= real_vp.data

        if self.show:
            import matplotlib.pyplot as plt
            plt.clf()
            show_image(gradx, title='gradx')
            plt.show(block=False)
            plt.clf()
            show_image(grady, title='grady')
            plt.show(block=False)
        if self.persist:
            export_image_to_fits(gradx, "%s/test_image_gradients_gradx.fits" % (self.dir))
            export_image_to_fits(grady, "%s/test_image_gradients_grady.fits" % (self.dir))

        if self.show:
            import matplotlib.pyplot as plt
            plt.clf()
            show_image(gradxx, title='gradxx')
            plt.show(block=False)
            plt.clf()
            show_image(gradxy, title='gradxy')
            plt.show(block=False)
            plt.clf()
            show_image(gradyx, title='gradyx')
            plt.show(block=False)
            plt.clf()
            show_image(gradyy, title='gradyy')
            plt.show(block=False)
        if self.persist:
            export_image_to_fits(gradxx, "%s/test_image_gradients_gradxx.fits" % (self.dir))
            export_image_to_fits(gradxy, "%s/test_image_gradients_gradxy.fits" % (self.dir))
            export_image_to_fits(gradyx, "%s/test_image_gradients_gradyx.fits" % (self.dir))
            export_image_to_fits(gradyy, "%s/test_image_gradients_gradyy.fits" % (self.dir))
Пример #5
0
    def test_grid_gaintable_to_screen(self):
        self.actualSetup()
        screen = import_image_from_fits(
            rascil_data_path('models/test_mpc_screen.fits'))
        beam = create_test_image(cellsize=0.0015,
                                 phasecentre=self.vis.phasecentre,
                                 frequency=self.frequency)

        beam = create_low_test_beam(beam, use_local=False)

        gleam_components = create_low_test_skycomponents_from_gleam(
            flux_limit=1.0,
            phasecentre=self.phasecentre,
            frequency=self.frequency,
            polarisation_frame=PolarisationFrame('stokesI'),
            radius=0.2)

        pb_gleam_components = apply_beam_to_skycomponent(
            gleam_components, beam)

        actual_components = filter_skycomponents_by_flux(pb_gleam_components,
                                                         flux_min=1.0)

        gaintables = create_gaintable_from_screen(self.vis, actual_components,
                                                  screen)
        assert len(gaintables) == len(actual_components), len(gaintables)
        assert gaintables[0].gain.shape == (3, 94, 1, 1,
                                            1), gaintables[0].gain.shape

        newscreen = create_empty_image_like(screen)

        newscreen, weights = grid_gaintable_to_screen(self.vis, gaintables,
                                                      newscreen)
        assert numpy.max(numpy.abs(screen.data)) > 0.0
        if self.persist:
            export_image_to_fits(
                newscreen,
                rascil_path('test_results/test_mpc_screen_gridded.fits'))
        if self.persist:
            export_image_to_fits(
                weights,
                rascil_path(
                    'test_results/test_mpc_screen_gridded_weights.fits'))
Пример #6
0
    def test_create_gaintable_from_screen_troposphere(self):
        self.actualSetup("troposphere")
        screen = import_image_from_fits(
            rascil_data_path('models/test_mpc_screen.fits'))
        beam = create_test_image(cellsize=0.00015,
                                 phasecentre=self.vis.phasecentre,
                                 frequency=self.frequency)

        beam = create_low_test_beam(beam, use_local=False)

        s3_components = create_test_skycomponents_from_s3(
            flux_limit=0.3,
            phasecentre=self.phasecentre,
            frequency=self.frequency,
            polarisation_frame=PolarisationFrame('stokesI'),
            radius=1.5 * numpy.pi / 180.0)

        assert len(s3_components) > 0, "No S3 components selected"

        pb_s3_components = apply_beam_to_skycomponent(s3_components, beam)

        actual_components = filter_skycomponents_by_flux(pb_s3_components,
                                                         flux_max=10.0)

        assert len(
            actual_components) > 0, "No components after applying primary beam"

        gaintables = create_gaintable_from_screen(
            self.vis,
            actual_components,
            screen,
            height=3e3,
            type_atmosphere="troposphere")
        assert len(gaintables) == len(actual_components), len(gaintables)
        assert gaintables[0].gain.shape == (3, 63, 1, 1,
                                            1), gaintables[0].gain.shape
Пример #7
0
def cli_parser():
    # Get command line inputs
    import argparse

    par = argparse.ArgumentParser(
        description=
        'Distributed simulation of atmosphere induced errors for SKA-MID')
    par.add_argument(
        '--context',
        type=str,
        default='singlesource',
        help='Type of sky: s3sky or doublesource or singlesource or null')
    par.add_argument('--imaging_context',
                     type=str,
                     default='2d',
                     help='Type of imaging transforms to use: 2d or ng. '
                     'The latter requires that Nifty Gridder be installed')
    par.add_argument('--telescope',
                     type=str,
                     default='MID',
                     help='Telescope: MID or LOW')

    # Observation definition
    par.add_argument('--ra',
                     type=float,
                     default=+15.0,
                     help='Right ascension of target source (degrees)')
    par.add_argument('--declination',
                     type=float,
                     default=-45.0,
                     help='Declination  of target source (degrees)')
    par.add_argument('--frequency',
                     type=float,
                     default=1.36e9,
                     help='Frequency of observation (Hz)')
    par.add_argument('--rmax',
                     type=float,
                     default=1e5,
                     help='Maximum distance of dish from array centre (m)')
    par.add_argument('--band',
                     type=str,
                     default='B2',
                     help="Band: B1, B2 or Ku")
    par.add_argument('--integration_time',
                     type=float,
                     default=600,
                     help='Duration of single integration (s)')
    par.add_argument('--time_range',
                     type=float,
                     nargs=2,
                     default=[-6.0, 6.0],
                     help='Hour angle of observation (hours)')
    par.add_argument(
        '--npixel',
        type=int,
        default=512,
        help='Number of pixels in dirty image used for statistics')
    par.add_argument('--use_natural',
                     type=str,
                     default='False',
                     help='Use natural weighting?')
    par.add_argument('--pbradius',
                     type=float,
                     default=2.0,
                     help='Radius of s3sky sources to include (in HWHM)')
    par.add_argument(
        '--pbtype',
        type=str,
        default='MID',
        help=
        'Primary beam model: MID, MID_GAUSS, MID_FEKO_B1, MID_FEKO_B2, MID_FEKO_Ku'
    )
    par.add_argument('--flux_limit',
                     type=float,
                     default=1.0,
                     help='Flux limit in selecting sources for s3sky (Jy)')
    par.add_argument('--make_residual',
                     type=str,
                     default='True',
                     help='Make residual image?')
    par.add_argument('--selfcal',
                     type=str,
                     default='True',
                     help='Selfcalibrate?')
    par.add_argument(
        '--zerow',
        type=str,
        default='True',
        help='Set w to zero? Use together with --imaging_context 2d')
    # Control parameters
    par.add_argument('--show', type=str, default='False', help='Show images?')
    par.add_argument('--export_images',
                     type=str,
                     default='False',
                     help='Export images in fits format?')
    par.add_argument('--use_agg',
                     type=str,
                     default="True",
                     help='Use Agg matplotlib backend?')
    default_shared_path = rascil_data_path("configurations")
    par.add_argument(
        '--shared_directory',
        type=str,
        default=default_shared_path,
        help=
        'Location of configuration files (default is RASCIL data/configurations)'
    )
    # Dask parameters; matched to P3
    par.add_argument('--serial',
                     type=str,
                     default='False',
                     help='Use serial processing (very slow)')
    par.add_argument('--nthreads',
                     type=int,
                     default=4,
                     help='Number of threads in Nifty Gridder')
    par.add_argument('--memory',
                     type=int,
                     default=64,
                     help='Memory per Dask worker (GB)')
    par.add_argument('--nworkers',
                     type=int,
                     default=16,
                     help='Number of Dask workers')
    par.add_argument('--dask_worker_space',
                     type=str,
                     default=".",
                     help='Location for dask worker files')
    # Simulation parameters
    par.add_argument('--time_chunk',
                     type=float,
                     default=1800.0,
                     help="Chunking of time for simulation (s)")
    par.add_argument('--type_atmosphere',
                     type=str,
                     default='troposphere',
                     help="Type: troposphere or ionosphere")
    par.add_argument('--screen',
                     type=str,
                     default=None,
                     help="Screen as fits file")
    par.add_argument('--height',
                     type=float,
                     default=3e3,
                     help="Nominal height of screen")
    par.add_argument('--r0',
                     nargs='+',
                     type=float,
                     default=[5e3, 15e3, 45e3],
                     help="List of r0 values to test (meters)")
    return par
Пример #8
0
def cli_parser():
    # Get command line inputs
    import argparse

    par = argparse.ArgumentParser(
        description=
        'Distributed simulation of pointing induced errors for SKA-MID')
    par.add_argument('--context',
                     type=str,
                     default='singlesource',
                     help='Type of sky: s3sky or singlesource or null')
    par.add_argument('--imaging_context',
                     type=str,
                     default='2d',
                     help='Type of imaging transforms to use: 2d or ng')

    # Observation definition
    par.add_argument('--ra',
                     type=float,
                     default=+15.0,
                     help='Right ascension of target source (degrees)')
    par.add_argument('--declination',
                     type=float,
                     default=-45.0,
                     help='Declination  of target source (degrees)')
    par.add_argument('--frequency',
                     type=float,
                     default=1.36e9,
                     help='Frequency of observation (Hz)')
    par.add_argument('--rmax',
                     type=float,
                     default=1e5,
                     help='Maximum distance of dish from array centre (m)')
    par.add_argument('--band',
                     type=str,
                     default='B2',
                     help="Band: B1, B2 or Ku")
    par.add_argument('--integration_time',
                     type=float,
                     default=600,
                     help='Duration of single integration (s)')
    par.add_argument('--time_range',
                     type=float,
                     nargs=2,
                     default=[-6.0, 6.0],
                     help='Hour angle of observation (hours)')
    par.add_argument(
        '--npixel',
        type=int,
        default=512,
        help='Number of pixels in dirty image used for statistics')
    par.add_argument('--use_natural',
                     type=str,
                     default='False',
                     help='Use natural weighting?')
    par.add_argument('--snapshot',
                     type=str,
                     default='False',
                     help='Do snapshot only?')
    par.add_argument('--opposite',
                     type=str,
                     default='False',
                     help='Move source to opposite side of pointing centre')
    par.add_argument('--offset_dir',
                     type=float,
                     nargs=2,
                     default=[1.0, 0.0],
                     help='Multipliers for null offset')
    par.add_argument('--pbradius',
                     type=float,
                     default=2.0,
                     help='Radius of s3sky sources to include (in HWHM)')
    par.add_argument(
        '--pbtype',
        type=str,
        default='MID',
        help=
        'Primary beam model: MID, MID_GAUSS, MID_FEKO_B1, MID_FEKO_B2, MID_FEKO_Ku'
    )
    par.add_argument('--flux_limit',
                     type=float,
                     default=1.0,
                     help='Flux limit in selecting sources for s3sky (Jy)')
    # Control parameters
    par.add_argument('--show', type=str, default='False', help='Show images?')
    par.add_argument('--export_images',
                     type=str,
                     default='False',
                     help='Export images in fits format?')
    par.add_argument('--use_agg',
                     type=str,
                     default="True",
                     help='Use Agg matplotlib backend?')
    par.add_argument('--use_radec',
                     type=str,
                     default="False",
                     help='Calculate primary beams in RADEC?')
    default_shared_path = rascil_data_path("configurations")
    par.add_argument(
        '--shared_directory',
        type=str,
        default=default_shared_path,
        help=
        'Location of configuration files (default is RASCIL data/configurations)'
    )
    # Dask parameters; matched to P3
    par.add_argument('--serial',
                     type=str,
                     default='False',
                     help='Use serial processing (very slow)')
    par.add_argument('--nthreads',
                     type=int,
                     default=1,
                     help='Number of threads per Dask worker')
    par.add_argument('--memory',
                     type=int,
                     default=64,
                     help='Memory per Dask worker (GB)')
    par.add_argument('--nworkers',
                     type=int,
                     default=16,
                     help='Number of Dask workers')
    # Simulation parameters
    par.add_argument('--time_chunk',
                     type=float,
                     default=1800.0,
                     help="Chunking of time for simulation (s)")
    par.add_argument('--seed',
                     type=int,
                     default=5231870,
                     help="Random number seed")
    par.add_argument('--time_series',
                     type=str,
                     default='wind',
                     help="Type of time series: wind or random")
    par.add_argument(
        '--global_pe',
        type=float,
        nargs=2,
        default=[0.0, 0.0],
        help=
        'Scale (arcsec) for random global pointing error (same for all dishes and time)'
    )
    par.add_argument(
        '--static_pe',
        type=float,
        nargs=2,
        default=[0.0, 0.0],
        help='Scale (arcsec) for random static errors (same for all time)')
    par.add_argument(
        '--dynamic_pe',
        type=float,
        default=1.0,
        help=
        'Scale (arcsec) for random dynamic errors (varies with time and dish)')
    par.add_argument('--pointing_file',
                     type=str,
                     default=None,
                     help="Pointing file")
    par.add_argument('--pointing_directory',
                     type=str,
                     default=rascil_data_path('models'),
                     help='Location of wind-induced pointing files')
    return par
Пример #9
0
def create_test_skycomponents_from_s3(polarisation_frame=PolarisationFrame(
    "stokesI"),
                                      frequency=numpy.array([1e8]),
                                      channel_bandwidth=numpy.array([1e6]),
                                      phasecentre=None,
                                      fov=20,
                                      flux_limit=1e-3,
                                      radius=None):
    """Create 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

    For frequencies < 610MHz, there are three 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

    For frequencies > 610MHz, there are three tables:

        data/models/S3_1400MHz_1mJy_10deg.csv, use flux_limit>= 1e-3
        data/models/S3_1400MHz_100uJy_10deg.csv, use flux_limit < 1e-3
        data/models/S3_1400MHz_1mJy_18deg.csv, use flux_limit>= 1e-3
        data/models/S3_1400MHz_100uJy_18deg.csv, use flux_limit < 1e-3

    The component spectral index is calculated from the 610MHz and 151MHz or 1400MHz and 610MHz, 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 polarisation_frame: Polarisation frame (default PolarisationFrame("stokesI"))
    :param frequency:
    :param channel_bandwidth: Channel width (Hz)
    :param phasecentre: phasecentre (SkyCoord)
    :param fov: fov 10 | 20 | 40
    :param flux_limit: Minimum flux (Jy)
    :return: Image
    """
    check_data_directory()

    ras = []
    decs = []
    fluxes = []
    names = []

    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("stokesI")

    if numpy.max(frequency) > 6.1E8:
        if fov > 10:
            fovstr = '18'
        else:
            fovstr = '10'
        if flux_limit >= 1e-3:
            csvfilename = rascil_data_path('models/S3_1400MHz_1mJy_%sdeg.csv' %
                                           fovstr)
        else:
            csvfilename = rascil_data_path(
                'models/S3_1400MHz_100uJy_%sdeg.csv' % fovstr)
        log.info(
            'create_test_skycomponents_from_s3: Reading S3-SEX sources from %s '
            % csvfilename)
    else:
        assert fov in [
            10, 20, 40
        ], "Field of view invalid: use one of %s" % ([10, 20, 40])
        csvfilename = rascil_data_path('models/S3_151MHz_%ddeg.csv' % (fov))
        log.info(
            'create_test_skycomponents_from_s3: Reading S3-SEX sources from %s '
            % csvfilename)

    skycomps = list()

    with open(csvfilename) as csvfile:
        readCSV = csv.reader(csvfile, delimiter=',')
        r = 0
        for row in readCSV:
            # Skip first row
            if r > 0:
                ra = float(row[4]) / numpy.cos(
                    phasecentre.dec.rad) + phasecentre.ra.deg
                dec = float(row[5]) + phasecentre.dec.deg
                if numpy.max(frequency) > 6.1E8:
                    alpha = (float(row[11]) - float(row[10])) / numpy.log10(
                        1400.0 / 610.0)
                    flux = numpy.power(10, float(row[10])) * numpy.power(
                        frequency / 1.4e9, alpha)
                else:
                    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)
                if numpy.max(flux) > flux_limit:
                    ras.append(ra)
                    decs.append(dec)
                    if polarisation_frame == PolarisationFrame("stokesIQUV"):
                        polscale = numpy.array([1.0, 0.0, 0.0, 0.0])
                        fluxes.append(numpy.outer(flux, polscale))
                    else:
                        fluxes.append([[f] for f in flux])
                    names.append("S3_%s" % row[0])
            r += 1

    csvfile.close()

    assert len(fluxes) > 0, "No sources found above flux limit %s" % flux_limit

    directions = SkyCoord(ra=ras * u.deg, dec=decs * u.deg)
    if phasecentre is not None:
        separations = directions.separation(phasecentre).to('rad').value
    else:
        separations = numpy.zeros(len(names))

    for isource, name in enumerate(names):
        direction = directions[isource]
        if separations[isource] < radius:
            if not numpy.isnan(flux).any():
                skycomps.append(
                    Skycomponent(direction=direction,
                                 flux=fluxes[isource],
                                 frequency=frequency,
                                 name=names[isource],
                                 shape='Point',
                                 polarisation_frame=polarisation_frame))

    log.info(
        'create_test_skycomponents_from_s3: %d sources found above fluxlimit inside search radius'
        % len(skycomps))

    return skycomps
Пример #10
0
def create_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,
                              flux_limit=1e-3) -> Image:
    """Create MID 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

    For frequencies < 610MHz, there are three 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

    For frequencies > 610MHz, there are three tables:

        data/models/S3_1400MHz_1mJy_10deg.csv, use flux_limit>= 1e-3
        data/models/S3_1400MHz_100uJy_10deg.csv, use flux_limit < 1e-3
        data/models/S3_1400MHz_1mJy_18deg.csv, use flux_limit>= 1e-3
        data/models/S3_1400MHz_100uJy_18deg.csv, use flux_limit < 1e-3

    The component spectral index is calculated from the 610MHz and 151MHz or 1400MHz and 610MHz, 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 10 | 20 | 40
    :param flux_limit: Minimum flux (Jy)
    :return: Image
    """
    check_data_directory()

    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("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 + 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)

    if numpy.max(frequency) > 6.1E8:
        if fov > 10:
            fovstr = '18'
        else:
            fovstr = '10'
        if flux_limit >= 1e-3:
            csvfilename = rascil_data_path('models/S3_1400MHz_1mJy_%sdeg.csv' %
                                           fovstr)
        else:
            csvfilename = rascil_data_path(
                'models/S3_1400MHz_100uJy_%sdeg.csv' % fovstr)
        log.info('create_test_image_from_s3: Reading S3 sources from %s ' %
                 csvfilename)
    else:
        assert fov in [
            10, 20, 40
        ], "Field of view invalid: use one of %s" % ([10, 20, 40])
        csvfilename = rascil_data_path('models/S3_151MHz_%ddeg.csv' % (fov))
        log.info('create_test_image_from_s3: Reading S3 sources from %s ' %
                 csvfilename)

    with open(csvfilename) 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
                if numpy.max(frequency) > 6.1E8:
                    alpha = (float(row[11]) - float(row[10])) / numpy.log10(
                        1400.0 / 610.0)
                    flux = numpy.power(10, float(row[10])) * numpy.power(
                        frequency / 1.4e9, alpha)
                else:
                    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)
                if numpy.max(flux) > flux_limit:
                    ras.append(ra)
                    decs.append(dec)
                    fluxes.append(flux)
            r += 1

    csvfile.close()

    assert len(fluxes) > 0, "No sources found above flux limit %s" % flux_limit

    log.info('create_test_image_from_s3: %d sources read' % (len(fluxes)))

    p = w.sub(2).wcs_world2pix(numpy.array(ras), numpy.array(decs), 1)
    fluxes = numpy.array(fluxes)
    total_flux = numpy.sum(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_test_image_from_s3: %d sources inside the image' %
             (ps.shape[1]))

    log.info(
        'create_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
Пример #11
0
import logging

log = logging.getLogger('logger')

log.setLevel(logging.DEBUG)
log.addHandler(logging.StreamHandler(sys.stdout))
log.addHandler(logging.StreamHandler(sys.stderr))

if __name__ == '__main__':

    results_dir = rascil_path('test_results')

    # Test requires that casa be installed
    try:
        bvt = create_blockvisibility_from_ms(rascil_data_path('vis/sim-2.ms'),
                                             channum=[35, 36, 37, 38, 39])[0]
        bvt.configuration.diameter[...] = 35.0
        vt = convert_blockvisibility_to_visibility(bvt)
        vt = convert_visibility_to_stokes(vt)

        cellsize = 20.0 * numpy.pi / (180.0 * 3600.0)
        npixel = 512

        model = create_image_from_visibility(
            vt,
            cellsize=cellsize,
            npixel=npixel,
            polarisation_frame=PolarisationFrame('stokesIQUV'))
        dirty, sumwt = invert_list_serial_workflow([vt], [model],
                                                   context='2d')[0]
Пример #12
0
def simulate_pointingtable_from_timeseries(pt, type='wind', time_series_type='precision',
                                           pointing_directory=None, reference_pointing=False,
                                           seed=None):
    """Create a pointing table with time series created from PSD.

    :param pt: Pointing table to be filled
    :param type: Type of pointing: 'tracking' or 'wind'
    :param time_series_type: Type of wind condition precision|standard|degraded
    :param pointing_directory: Name of pointing file directory
    :param reference_pointing: Use reference pointing?
    :return:
    """
    if seed is not None:
        numpy.random.seed(seed)
    
    if pointing_directory is None:
        pointing_directory = rascil_data_path("models/%s" % time_series_type)
    
    pt.data['pointing'] = numpy.zeros(pt.data['pointing'].shape)
    
    ntimes, nant, nchan, nrec, _ = pt.data['pointing'].shape
    
    # Use az and el at the beginning of this pointingtable
    axis_values = pt.nominal[0, 0, 0, 0, 0]
    el = pt.nominal[0, 0, 0, 0, 1]
    
    el_deg = el * 180.0 / numpy.pi
    az_deg = axis_values * 180.0 / numpy.pi
    
    if el_deg < 30.0:
        el_deg = 15.0
    elif el_deg < (90.0 + 45.0) / 2.0:
        el_deg = 45.0
    else:
        el_deg = 90.0
    
    if abs(az_deg) < 45.0 / 2.0:
        az_deg = 0.0
    elif abs(az_deg) < (45.0 + 90.0) / 2.0:
        az_deg = 45.0
    elif abs(az_deg) < (90.0 + 135.0) / 2.0:
        az_deg = 90.0
    elif abs(az_deg) < (135.0 + 180.0) / 2.0:
        az_deg = 135.0
    else:
        az_deg = 180.0
    
    pointing_file = '%s/El%dAz%d.dat' % (pointing_directory, int(el_deg), int(az_deg))
    log.debug("simulate_pointingtable_from_timeseries: Reading wind PSD from %s" % pointing_file)
    psd = numpy.loadtxt(pointing_file)
    
    # define some arrays
    freq = psd[:, 0]
    axesdict = {
        "az": psd[:, 1],
        "el": psd[:, 2],
        "pxel": psd[:, 3],
        "pel": psd[:, 4]
    }
    
    if type == 'tracking':
        axes = ["az", "el"]
    elif type == 'wind':
        axes = ["pxel", "pel"]
    else:
        raise ValueError("Pointing type %s not known" % type)
    
    freq_interval = 0.0001
    
    for axis in axes:
        
        axis_values = axesdict[axis]
        
        if (axis == "az") or (axis == "el"):
            # determine index of maximum PSD value; add 50 for better fit
            axis_values_max_index = numpy.argwhere(axis_values == numpy.max(axis_values))[0][0] + 50
            axis_values_max_index = min(axis_values_max_index, len(axis_values))
            # max_freq = 2.0 / pt.interval[0]
            max_freq = 0.4
            freq_max_index = numpy.argwhere(freq > max_freq)[0][0]
        else:
            break_freq = 0.01  # not max; just a break
            axis_values_max_index = numpy.argwhere(freq > break_freq)[0][0]
            # max_freq = 2.0 / pt.interval[0]
            max_freq = 0.1
            freq_max_index = numpy.argwhere(freq > max_freq)[0][0]
        
        # construct regularly-spaced frequencies
        regular_freq = numpy.arange(freq[0], freq[freq_max_index], freq_interval)
        
        regular_axis_values_max_index = numpy.argwhere(
            numpy.abs(regular_freq - freq[axis_values_max_index]) == numpy.min(
                numpy.abs(regular_freq - freq[axis_values_max_index])))[0][0]
        
        # print ('Frequency break: ', freq[az_max_index])
        # print ('Max frequency: ', max_freq)
        #
        # print ('New frequency break: ', regular_freq[regular_az_max_index])
        # print ('New max frequency: ', regular_freq[-1])
        
        if axis_values_max_index >= freq_max_index:
            raise ValueError('Frequency break is higher than highest frequency; select a lower break')
        
        # use original frequency break and max frequency to fit function
        # fit polynomial to psd up to max value
        import warnings
        from numpy import RankWarning
        warnings.simplefilter('ignore', RankWarning)
        
        p_axis_values1 = numpy.polyfit(freq[:axis_values_max_index],
                                       numpy.log(axis_values[:axis_values_max_index]), 5)
        f_axis_values1 = numpy.poly1d(p_axis_values1)
        # fit polynomial to psd beyond max value
        p_axis_values2 = numpy.polyfit(freq[axis_values_max_index:freq_max_index],
                                       numpy.log(axis_values[axis_values_max_index:freq_max_index]), 5)
        f_axis_values2 = numpy.poly1d(p_axis_values2)
        
        # use new frequency break and max frequency to apply function (ensures equal spacing of frequency intervals)
        
        # resampled to construct regularly-spaced frequencies
        regular_axis_values1 = numpy.exp(f_axis_values1(regular_freq[:regular_axis_values_max_index]))
        regular_axis_values2 = numpy.exp(f_axis_values2(regular_freq[regular_axis_values_max_index:]))
        
        # join
        regular_axis_values = numpy.append(regular_axis_values1, regular_axis_values2)
        
        m0 = len(regular_axis_values)
        
        #  check rms of resampled PSD
        # df = regular_freq[1:]-regular_freq[:-1]
        # psd2rms_pxel = numpy.sqrt(numpy.sum(regular_az[:-1]*df))
        # print ('Calculate rms of resampled PSD: ', psd2rms_pxel)
        
        original_regular_freq = regular_freq
        original_regular_axis_values = regular_axis_values
        # get amplitudes from psd values
        
        if (regular_axis_values < 0).any():
            raise ValueError('Resampling returns negative power values; change fit range')
        
        amp_axis_values = numpy.sqrt(regular_axis_values * 2 * freq_interval)
        # need to scale PSD by 2* frequency interval before square rooting, then by number of modes in resampled PSD
        
        # Now we generate some random phases
        for ant in range(nant):
            regular_freq = original_regular_freq
            regular_axis_values = original_regular_axis_values
            phi_axis_values = numpy.random.rand(len(regular_axis_values)) * 2 * numpy.pi
            # create complex array
            z_axis_values = amp_axis_values * numpy.exp(1j * phi_axis_values)  # polar
            # make symmetrical frequencies
            mirror_z_axis_values = numpy.copy(z_axis_values)
            # make complex conjugates
            mirror_z_axis_values.imag -= 2 * z_axis_values.imag
            # make negative frequencies
            mirror_regular_freq = -regular_freq
            # join
            z_axis_values = numpy.append(z_axis_values, mirror_z_axis_values[::-1])
            regular_freq = numpy.append(regular_freq, mirror_regular_freq[::-1])
            
            # add a 0 Fourier term
            zav = z_axis_values
            z_axis_values = numpy.zeros([len(zav) + 1]).astype('complex')
            z_axis_values[1:] = zav
            
            # perform inverse fft
            ts = numpy.fft.ifft(z_axis_values)
            
            # set up and check scalings
            Dt = pt.interval[0]
            ts = numpy.real(ts)
            ts *= m0  # the result is scaled by number of points in the signal, so multiply - real part - by this
            
            # The output of the iFFT will be a random time series on the finite
            # (bounded, limited) time interval t = 0 to tmax = (N-1) X Dt, #
            # where Dt = 1 / (2 X Fmax)
            
            # scale to time interval
            
            # Convert from arcsec to radians
            ts *= numpy.pi / (180.0 * 3600.0)
            
            # We take reference pointing to mean that the pointing errors are zero at the beginning
            # of the set of integrations
            if reference_pointing:
                ts[:] -= ts[0]
            
            #            pt.data['time'] = times[:ntimes]
            if axis == 'az':
                pt.data['pointing'][:, ant, :, :, 0] = ts[:ntimes, numpy.newaxis, numpy.newaxis, ...]
            elif axis == 'el':
                pt.data['pointing'][:, ant, :, :, 1] = ts[:ntimes, numpy.newaxis, numpy.newaxis, ...]
            elif axis == 'pxel':
                pt.data['pointing'][:, ant, :, :, 0] = ts[:ntimes, numpy.newaxis, numpy.newaxis, ...]
            elif axis == 'pel':
                pt.data['pointing'][:, ant, :, :, 1] = ts[:ntimes, numpy.newaxis, numpy.newaxis, ...]
            else:
                raise ValueError("Unknown axis %s" % axis)
    
    return pt
Пример #13
0
 def test_read_screen(self):
     screen = import_image_from_fits(
         rascil_data_path('models/test_mpc_screen.fits'))
     assert screen.data.shape == (1, 3, 2000, 2000), screen.data.shape
def create_vp(model=None,
              telescope='MID',
              pointingcentre=None,
              padding=4,
              use_local=True):
    """ Create an image containing the dish voltage pattern for a number of cases

    :param model: Template image (Can be None for some cases)
    :param telescope:
    :return: Primary beam image
    """

    if telescope == 'MID_GAUSS':
        log.debug(
            "create_vp: Using numeric tapered Gaussian model for MID voltage pattern"
        )

        edge = numpy.power(10, -0.6)
        return create_vp_generic_numeric(model,
                                         pointingcentre=pointingcentre,
                                         diameter=15.0,
                                         blockage=0.0,
                                         edge=edge,
                                         padding=padding,
                                         use_local=use_local)
    elif telescope == 'MID':
        log.debug(
            "create_vp: Using no taper analytic model for MID voltage pattern")
        return create_vp_generic(model,
                                 pointingcentre=pointingcentre,
                                 diameter=15.0,
                                 blockage=0.0,
                                 use_local=use_local)
    elif telescope == 'MID_GRASP':
        log.debug("create_vp: Using GRASP model for MID voltage pattern")
        real_vp = import_image_from_fits(
            rascil_data_path('models/MID_GRASP_VP_real.fits'))
        imag_vp = import_image_from_fits(
            rascil_data_path('models/MID_GRASP_VP_imag.fits'))
        real_vp.data = real_vp.data + 1j * imag_vp.data
        real_vp.data /= numpy.max(numpy.abs(real_vp.data))
        return real_vp
    elif telescope == 'MID_FEKO_B1':
        log.debug("create_vp: Using FEKO model for MID voltage pattern")
        real_vp = import_image_from_fits(
            rascil_data_path('models/MID_FEKO_VP_B1_45_0765_real.fits'))
        imag_vp = import_image_from_fits(
            rascil_data_path('models/MID_FEKO_VP_B1_45_0765_imag.fits'))
        real_vp.data = real_vp.data + 1j * imag_vp.data
        real_vp.data /= numpy.max(numpy.abs(real_vp.data))
        return real_vp
    elif telescope == 'MID_FEKO_B2':
        log.debug("create_vp: Using FEKO model for MID voltage pattern")
        real_vp = import_image_from_fits(
            rascil_data_path('models/MID_FEKO_VP_B2_45_1360_real.fits'))
        imag_vp = import_image_from_fits(
            rascil_data_path('models/MID_FEKO_VP_B2_45_1360_imag.fits'))
        real_vp.data = real_vp.data + 1j * imag_vp.data
        real_vp.data /= numpy.max(numpy.abs(real_vp.data))
        return real_vp
    elif telescope == 'MID_FEKO_Ku':
        log.debug("create_vp: Using FEKO model for MID voltage pattern")
        real_vp = import_image_from_fits(
            rascil_data_path('models/MID_FEKO_VP_Ku_45_12179_real.fits'))
        imag_vp = import_image_from_fits(
            rascil_data_path('models/MID_FEKO_VP_Ku_45_12179_imag.fits'))
        real_vp.data = real_vp.data + 1j * imag_vp.data
        real_vp.data /= numpy.max(numpy.abs(real_vp.data))
        return real_vp
    elif telescope == 'MEERKAT':
        return create_vp_generic(model,
                                 pointingcentre=pointingcentre,
                                 diameter=13.5,
                                 blockage=0.0,
                                 use_local=use_local)
    elif telescope[0:3] == 'LOW':
        return create_low_test_vp(model)
    elif telescope[0:3] == 'VLA':
        return create_vp_generic(model,
                                 pointingcentre=pointingcentre,
                                 diameter=25.0,
                                 blockage=1.8,
                                 use_local=use_local)
    elif telescope[0:5] == 'ASKAP':
        return create_vp_generic(model,
                                 pointingcentre=pointingcentre,
                                 diameter=12.0,
                                 blockage=1.0,
                                 use_local=use_local)
    else:
        raise NotImplementedError('Telescope %s has no voltage pattern model' %
                                  telescope)