def hyperbeam(za, az, frequencies, fluxes, metafits=None): """Calculate MWA beam response using Hyperbeam package Parameters ---------- za : array-like Zenith angles in radians az : array-like Azimuth angles in radians frequencies : array-like frequency channels fluxes : array-like Source flux densities in Jys metafits : string, optional Path to observation ID metafits file, by default None Returns ------- numpy array Fluxes attenuated by MWA beam response """ hbeam = mwa_hyperbeam.FEEBeam( "/home/kariuki/mwa_pb/mwa_full_embedded_element_pattern.h5") with fits.open(metafits) as hdu: delays = list(map(int, hdu[0].header["DELAYS"].split(","))) amps = [1] * 16 beam_norm = True hjones = np.zeros((len(za), len(frequencies), 4), dtype=np.complex64) for chan in range(len(frequencies)): hjones[:, chan, :] = hbeam.calc_jones_array(az, za, int(frequencies[chan]), delays, amps, beam_norm) fluxes[:, :, 0] = (hjones[:, :, 0] * np.conj(hjones[:, :, 0]) * fluxes[:, :, 0] + hjones[:, :, 1] * np.conj(hjones[:, :, 1]) * fluxes[:, :, 0]) fluxes[:, :, 3] = (hjones[:, :, 2] * np.conj(hjones[:, :, 2]) * fluxes[:, :, 3] + hjones[:, :, 3] * np.conj(hjones[:, :, 3]) * fluxes[:, :, 3]) return fluxes
def MWA_Tile_full_EE(za, az, freq, delays=None, zenithnorm=True, power=True, jones=False, interp=True, pixels_per_deg=5): """ Use the new MWA tile model from beam_full_EE.py that includes mutual coupling and the simulated dipole response. Returns the XX and YY response to an unpolarised source. if jones=True, will return the Jones matrix instead of the XX,YY response. In this case, the power flag will be ignored. If interp=False, the pixels_per_deg will be ignored delays should be a numpy array of size (2,16), although a (16,) list or a (16,) array will also be accepted az - azimuth angles (radians), north through east. za - zenith angles (radian) """ # Convert za and az into 2D numpy arrays, because the Advanced and FullEE models require that format. if type(za) is list: za = np.array(za) if type(az) is list: az = np.array(az) if (isinstance(za, float)) and (isinstance( az, float)): # Convert float to 2D array za = np.array([[za]]) az = np.array([[az]]) dtype = 'float' elif (isinstance(za, np.ndarray)) and (isinstance(az, np.ndarray)): if (len(za.shape) == 1) and (len(az.shape) == 1): # 1D array, convert to 2D array za = za[None, :] az = az[None, :] dtype = '1D' elif (len(za.shape) == 2) and (len(az.shape) == 2): dtype = '2D' else: dtype = 'bad' else: dtype = 'bad' if dtype == 'bad': logger.error( 'ERROR - az/za data types must be the same, and either floats or 1 or 2 dimensional arrays' ) return None # If we're not interpolating, and we could import hyperbeam, then use it to # calculate the Jones matrices. if not interp and "mwa_hyperbeam" in sys.modules: # Make "hyperbeam" a global variable to the Rust object. If it doesn't # exist, we need to create one. if "hyperbeam" not in globals(): global hyperbeam try: # If this fails, it's either because MWA_BEAM_FILE isn't # defined, or there's something wrong with the file that # variable points to. hyperbeam = mwa_hyperbeam.FEEBeam() except mwa_hyperbeam.HyperbeamError: # Use the HDF5 file that's hopefully installed in mwa_pb. datadir = os.path.join(os.path.dirname(__file__), 'data') h5file = os.path.join(datadir, 'mwa_full_embedded_element_pattern.h5') hyperbeam = mwa_hyperbeam.FEEBeam(h5file) # Rather than repeat the command to hyperbeam a bunch of times for # slightly different arguments, make a partially-applied function # (lambda). f = lambda d: hyperbeam.calc_jones_array(az[0, :], za[0, :], freq_hz=freq, amps=[1] * 16, delays=d, norm_to_zenith=zenithnorm) if delays.shape[0] == 2: # Assume that both rows of the delays array are the same. j_flat = f(delays[0]) else: j_flat = f(delays) # Make j in the format that the rest of mwa_pb expects. j = j_flat.reshape((1, -1, 2, 2)) # Calculate the Jones matrices using the existing Python code. else: tile = beam_full_EE.get_AA_Cached(target_freq_Hz=freq) mybeam = beam_full_EE.Beam( tile, delays, amps=np.ones([2, 16]) ) # calling with amplitudes=1 every time - otherwise they get overwritten !!! if interp: j = mybeam.get_interp_response(az, za, pixels_per_deg) else: j = mybeam.get_response(az, za) if zenithnorm: j = tile.apply_zenith_norm_Jones(j) # Normalise # TO DO: do frequency interpolation here (with 2nd adjacent beam) # Use swapaxis to place jones matrices in last 2 dimensions # insead of first 2 dims. if len(j.shape) == 4: j = np.swapaxes(np.swapaxes(j, 0, 2), 1, 3) elif len(j.shape) == 3: # 1-D j = np.swapaxes(np.swapaxes(j, 1, 2), 0, 1) else: # single value pass if jones: if dtype == 'float': return j[0][0] elif dtype == '1D': return j[0] else: return j # Use mwa_tile makeUnpolInstrumentalResponse because we have swapped axes vis = mwa_tile.makeUnpolInstrumentalResponse(j, j) if not power: xx, yy = (np.sqrt(vis[:, :, 0, 0].real), np.sqrt(vis[:, :, 1, 1].real)) else: xx, yy = (vis[:, :, 0, 0].real, vis[:, :, 1, 1].real) if dtype == 'float': return xx[0][0], yy[0][0] elif dtype == '1D': return xx[0], yy[0] else: return xx, yy
import requests import random from astropy.table import Table from astropy.coordinates import SkyCoord import gc gc.enable() #from mwapy import ephem_utils,metadata from vcstools.metadb_utils import getmeta, get_common_obs_metadata, get_ambient_temperature from vcstools.pointing_utils import getTargetAZZA, getTargetRADec from vcstools.general_utils import setup_logger, split_remove_remainder from vcstools import data_load from mwa_pb import primary_beam as pb from mwa_pb import config import mwa_hyperbeam beam = mwa_hyperbeam.FEEBeam(config.h5file) from vcstools.beam_calc import get_Trec from vcstools.beam_sim import getTileLocations, get_obstime_duration, partial_convolve_sky_map,\ calcWaveNumbers, calcSkyPhase, calcArrayFactor, calc_pixel_area,\ calc_geometric_delay_distance, cal_phase_ord logger = logging.getLogger(__name__) def createArrayFactor(za, az, pixel_area, data): """ Primary function to calculate the array factor with the given information. Input: delays - the beamformer delays required for point tile beam (should be a set of 16 numbers) time - the time at which to evaluate the target position (as we need Azimuth and Zenith angle, which are time dependent)
def test_hyperbeam_vs_pb(): """Compares the results of the FEE beam model using mwa_pb and mwa_hyperbeam.""" beam = mwa_hyperbeam.FEEBeam(config.h5file) # Set up fake data. n = 100000 az = np.linspace(0, 0.9 * np.pi, n) za = np.linspace(0.1, 0.9 * np.pi / 2, n) freq = 167000000 delays = [0] * 16 amps = [1.0] * 16 test_decimals = 4 # Jones -------------------------------------------------- print("Jones benchmarks") # mwa_pb method start_time = time.perf_counter() pb_jones = primary_beam.MWA_Tile_full_EE(za, az, freq=freq, delays=np.array(delays), zenithnorm=True, power=True, jones=True, interp=False) print("mwa_pb: {:6.3f} s".format(time.perf_counter() - start_time)) # hyperbeam method #print(freq, delays, amps) start_time = time.perf_counter() hb_jones = beam.calc_jones_array(az, za, freq, delays, amps, True) print("hyperbeam: {:6.3f} s".format(time.perf_counter() - start_time)) hb_jones = hb_jones.reshape(n, 2, 2) # Compare Jones assert_almost_equal(pb_jones, hb_jones, decimal=test_decimals) # Power --------------------------------------------------- print("\nPower benchmarks") # mwa_pb method start_time = time.perf_counter() pb_xx, pb_yy = primary_beam.MWA_Tile_full_EE(za, az, freq=freq, delays=np.array(delays), zenithnorm=True, power=True) print("mwa_pb: {:6.3f} s".format(time.perf_counter() - start_time)) # hyperbeam method start_time = time.perf_counter() hb_jones = beam.calc_jones_array(az, za, freq, delays, amps, True) hb_jones = hb_jones.reshape(1, n, 2, 2) vis = primary_beam.mwa_tile.makeUnpolInstrumentalResponse( hb_jones, hb_jones) hb_xx, hb_yy = (vis[:, :, 0, 0].real, vis[:, :, 1, 1].real) print("hyperbeam: {:6.3f} s".format(time.perf_counter() - start_time)) # Compare power assert_almost_equal(pb_xx, hb_xx[0], decimal=test_decimals) assert_almost_equal(pb_yy, hb_yy[0], decimal=test_decimals)
def get_beam_power_over_time(names_ra_dec, common_metadata=None, dt=296, centeronly=True, verbose=False, option='analytic', degrees=False, start_time=0): """Calculates the zenith normalised power for each source over time. Parameters ---------- names_ra_dec : `list` An array in the format [[source_name, RAJ, DecJ]] common_metadata : `list`, optional The list of common metadata generated from :py:meth:`vcstools.metadb_utils.get_common_obs_metadata` dt : `int`, optional The time interval of how often powers are calculated. |br| Default: 296. centeronly : `boolean`, optional Only calculates for the centre frequency. |br| Default: `True`. verbose : `boolean`, optional If `True` will not supress the output from mwa_pb. |br| Default: `False`. option : `str`, optional The primary beam model to use out of [analytic, advanced, full_EE, hyperbeam]. |br| Default: analytic. degrees : `boolean`, optional If true assumes RAJ and DecJ are in degrees. |br| Default: `False`. start_time : `int`, optional The time in seconds from the begining of the observation to start calculating at. |br| Default: 0. Returns ------- Powers : `numpy.array`, (len(names_ra_dec), ntimes, nfreqs) The zenith normalised power for each source over time. """ if common_metadata is None: common_metadata = get_common_obs_metadata(obsid) obsid, _, _, time, delays, centrefreq, channels = common_metadata names_ra_dec = np.array(names_ra_dec) amps = [1.0] * 16 logger.debug("Calculating beam power for OBS ID: {0}".format(obsid)) if option == 'hyperbeam': if "mwa_hyperbeam" not in sys.modules: logger.error( "mwa_hyperbeam not installed so can not use hyperbeam to create a beam model. Exiting" ) sys.exit(1) beam = mwa_hyperbeam.FEEBeam(config.h5file) # Work out time steps to calculate over starttimes = np.arange(start_time, time + start_time, dt) stoptimes = starttimes + dt stoptimes[stoptimes > time] = time ntimes = len(starttimes) midtimes = float(obsid) + 0.5 * (starttimes + stoptimes) # Work out frequency steps if centeronly: if centrefreq > 1e6: logger.warning( "centrefreq is greater than 1e6, assuming input with units of Hz." ) frequencies = np.array([centrefreq]) else: frequencies = np.array([centrefreq]) * 1e6 nfreqs = 1 else: # in Hz frequencies = np.array(channels) * 1.28e6 nfreqs = len(channels) # Set up np power array PowersX = np.zeros((len(names_ra_dec), ntimes, nfreqs)) PowersY = np.zeros((len(names_ra_dec), ntimes, nfreqs)) # Convert RA and Dec to desired units if degrees: RAs = np.array(names_ra_dec[:, 1], dtype=float) Decs = np.array(names_ra_dec[:, 2], dtype=float) else: RAs, Decs = sex2deg(names_ra_dec[:, 1], names_ra_dec[:, 2]) # Then check if they're valid if len(RAs) == 0: sys.stderr.write('Must supply >=1 source positions\n') return None if not len(RAs) == len(Decs): sys.stderr.write('Must supply equal numbers of RAs and Decs\n') return None if verbose is False: #Supress print statements of the primary beam model functions sys.stdout = open(os.devnull, 'w') for itime in range(ntimes): # this differ's from the previous ephem_utils method by 0.1 degrees _, Azs, Zas = mwa_alt_az_za(midtimes[itime], ra=RAs, dec=Decs, degrees=True) # go from altitude to zenith angle theta = np.radians(Zas) phi = np.radians(Azs) for ifreq in range(nfreqs): #Decide on beam model if option == 'analytic': rX, rY = primary_beam.MWA_Tile_analytic( theta, phi, freq=frequencies[ifreq], delays=delays, zenithnorm=True, power=True) elif option == 'advanced': rX, rY = primary_beam.MWA_Tile_advanced( theta, phi, freq=frequencies[ifreq], delays=delays, zenithnorm=True, power=True) elif option == 'full_EE': rX, rY = primary_beam.MWA_Tile_full_EE(theta, phi, freq=frequencies[ifreq], delays=delays, zenithnorm=True, power=True) elif option == 'hyperbeam': jones = beam.calc_jones_array(phi, theta, int(frequencies[ifreq]), delays[0], amps, True) jones = jones.reshape(1, len(phi), 2, 2) vis = primary_beam.mwa_tile.makeUnpolInstrumentalResponse( jones, jones) rX, rY = (vis[:, :, 0, 0].real, vis[:, :, 1, 1].real) PowersX[:, itime, ifreq] = rX PowersY[:, itime, ifreq] = rY if verbose is False: sys.stdout = sys.__stdout__ Powers = 0.5 * (PowersX + PowersY) return Powers