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))
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'))
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
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
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
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
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
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]
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
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)