def get_mwabeam(self, delays, frequency=150e6): """ beam=MWA_GW.get_mwa_gwmap(delays, frequency=150e6) """ # first go from altitude to zenith angle theta_horz = np.pi / 2 - self.AltAz_down.alt.radian phi_horz = self.AltAz_down.az.radian beamX, beamY = primary_beam.MWA_Tile_analytic(theta_horz, phi_horz, freq=frequency, delays=delays, zenithnorm=True, power=True) return ((beamX + beamY) * 0.5)
def plot_beam_pattern(obsid, obsfreq, obstime, ra, dec, cutoff=0.1): # extra imports from MWA_Tools to access database and beam models from mwa_pb import primary_beam as pb _az = np.linspace(0, 360, 3600) _za = np.linspace(0, 90, 900) az, za = np.meshgrid(_az, _za) ## TARGET TRACKING ## times = Time(obstime, format='gps') target = SkyCoord(ra, dec, unit=(u.hourangle, u.deg)) location = EarthLocation(lat=-26.7033 * u.deg, lon=116.671 * u.deg, height=377.827 * u.m) altaz = target.transform_to(AltAz(obstime=times, location=location)) targetAZ = altaz.az.deg targetZA = 90 - altaz.alt.deg colours = cm.viridis(np.linspace(1, 0, len(targetAZ))) ## MWA BEAM CALCULATIONS ## delays = get_common_obs_metadata(obsid)[ 4] # x-pol and y-pol delays for obsid _, ptAZ, ptZA = mwa_alt_az_za(obsid) #ptAZ, _, ptZA = dbq.get_beam_pointing(obsid) # Az and ZA in degrees for obsid logger.info("obs pointing: ({0}, {1})".format(ptAZ, ptZA)) xp, yp = pb.MWA_Tile_analytic(np.radians(za), np.radians(az), obsfreq * 1e6, delays=delays, zenithnorm=True) #interp=True) pattern = np.sqrt((xp + yp) / 2.0).real # sum to get "total intensity" logger.debug("Pattern: {0}".format(pattern)) pmax = pattern.max() hpp = 0.5 * pmax # half-power point logger.info("tile pattern maximum: {0:.3f}".format(pmax)) logger.info("tile pattern half-max: {0:.3f}".format(hpp)) pattern[np.where(pattern < cutoff)] = 0 # ignore everything below cutoff # figure out the fwhm fwhm_idx = np.where((pattern > 0.498 * pmax) & (pattern < 0.502 * pmax)) fwhm_az_idx = fwhm_idx[1] fwhm_za_idx = fwhm_idx[0] # collapse beam pattern along axes pattern_ZAcol = pattern.mean(axis=0) pattern_AZcol = pattern.mean(axis=1) # figure out beam pattern value at target tracking points track_lines = [] logger.info("beam power at target track points:") for ta, tz in zip(targetAZ, targetZA): xp, yp = pb.MWA_Tile_analytic(np.radians([[tz]]), np.radians([[ta]]), obsfreq * 1e6, delays=delays, zenithnorm=True) #interp=False bp = (xp + yp) / 2 track_lines.append(bp[0]) logger.info("({0:.2f},{1:.2f}) = {2:.3f}".format(ta, tz, bp[0][0])) ## PLOTTING ## fig = plt.figure(figsize=(10, 8)) gs = gridspec.GridSpec(4, 4) axP = plt.subplot(gs[1:, 0:3]) axAZ = plt.subplot(gs[0, :3]) axZA = plt.subplot(gs[1:, 3]) axtxt = plt.subplot(gs[0, 3]) # info text in right-hand corner axis axtxt.axis('off') infostr = """Obs ID: {0} Frequency: {1:.2f}MHz Beam Pmax: {2:.3f} Beam half-Pmax: {3:.3f} """.format(obsid, obsfreq, pmax, hpp) axtxt.text(0.01, 0.5, infostr, verticalalignment='center') logger.debug("az: {0}, za: {1}, pattern: {2}, pmax: {3}".format( az, za, pattern, pmax)) # plot the actual beam patter over sky p = axP.contourf(az, za, pattern, 100, cmap=plt.get_cmap('gist_yarg'), vmax=pmax) # plot beam contours axP.scatter(_az[fwhm_az_idx], _za[fwhm_za_idx], marker='.', s=1, color='r') # plot the fwhm border axP.plot(ptAZ, ptZA, marker="+", ms=8, ls="", color='C0') # plot the tile beam pointing for ta, tz, c in zip(targetAZ, targetZA, colours): axP.plot(ta, tz, marker="x", ms=8, ls="", color=c) # plot the target track through the beam axP.set_xlim(0, 360) axP.set_ylim(0, 90) axP.set_xticks(np.arange(0, 361, 60)) axP.set_xlabel("Azimuth (deg)") axP.set_ylabel("Zenith angle (deg)") # setup and configure colourbar cbar_ax = fig.add_axes([0.122, -0.01, 0.58, 0.03]) cbar = plt.colorbar(p, cax=cbar_ax, orientation='horizontal', label="Zenith normalised power") cbar.ax.plot(hpp / pmax, [0.5], 'r.') for l, c in zip(track_lines, colours): cbar.ax.plot([l / pmax, l / pmax], [0, 1], color=c, lw=1.5) # plot collapsed beam patterns: axAZ.plot(_az, pattern_ZAcol, color='k') # collapsed along ZA for ta, c in zip(targetAZ, colours): # draw tracking points axAZ.axvline(ta, color=c) axAZ.set_xlim(0, 360) axAZ.set_xticks(np.arange(0, 361, 60)) axAZ.set_xticklabels([]) axAZ.set_yscale('log') axZA.plot(pattern_AZcol, _za, color='k') # collapsed along AZ for tz, c in zip(targetZA, colours): # draw tracking points axZA.axhline(tz, color=c) axZA.set_ylim(0, 90) axZA.set_yticklabels([]) axZA.set_xscale('log') plt.savefig("{0}_{1:.2f}MHz_flattile.png".format(obsid, obsfreq), bbox_inches='tight')
def plot_beam(obs, fname, target, freq, time): logger.info("Getting observation metadata") metadata = get_obs_metadata(obs) logger.info("Loading data from file (this may take a while...)") theta, phi, beam = load_data(fname) logger.info("Re-normalising beam-pattern") az, za = np.meshgrid(np.radians(sorted(set(phi))), np.radians(sorted(set(theta)))) delays = [metadata['xdelays'], metadata['ydelays']] gx, gy = pb.MWA_Tile_analytic(za, az, freq=freq * 1e6, delays=delays, power=True, zenithnorm=True) tile_beam = (gx + gy) / 2.0 # re-normalise tied-array beam to 1 at maximum and then apply tile beam # this acts to reduce the effects of simulation resolution (for plotting purposes) beam /= beam.max() beam *= np.ravel(tile_beam) # start setup for plotting lower_contour = 1e-2 upper_contour = beam.max() fill_min = 7e-3 fill_max = 0.95 * beam.max() beam[beam <= fill_min] = 0 beam[beam >= fill_max] = fill_max fig = plt.figure(figsize=(10, 8)) ax = fig.add_subplot(111, polar=True, aspect='equal') # plot the beam pattern logger.info("Plotting beam pattern (this may take a while, too...)") cf_levels = np.linspace(lower_contour, upper_contour, num=20) #cf_levels_log = np.logspace(np.log10(lower_contour), np.log10(upper_contour), num=20) logger.info(" contour levels: max,min = {0}, {1}".format( cf_levels.max(), cf_levels.min())) cf_cmap = plt.get_cmap('gray_r') logger.info(" beam levels: max,min = {0},{1}".format( beam.max(), beam.min())) #sys.exit() logger.info(" plotting tied-array beam pattern contours") #cf_norm = LogNorm(vmin=cf_levels.min(), vmax=beam.max()) #cf = ax.tricontourf(np.radians(phi), np.radians(theta), beam, cmap=cf_cmap, norm=cf_norm, levels=cf_levels) cf = ax.tricontourf(np.radians(phi), np.radians(theta), beam, cmap=cf_cmap, levels=cf_levels) cf.cmap.set_over('k') cf.cmap.set_under('white') # color bar setup logger.info(" assigning colorbar") cbar_levels = np.linspace(fill_min, fill_max, num=6) #cbar_levels_log = np.logspace(np.log10(fill_min), np.log10(fill_max), num=6) cbar = plt.colorbar(cf, shrink=0.9, pad=0.08) cbar.set_label(label="zenith normalised power", size=20, labelpad=20) cbar.set_ticks(cbar_levels) cbar.set_ticklabels([r"${0:.3f}$".format(x) for x in cbar_levels]) cbar.ax.tick_params(labelsize=18) # plot the pointing centre logger.info(" plotting observation pointing centre") ax.plot(np.radians(metadata["az"]), np.radians(metadata["za"]), ls="", marker="+", ms=8, color='cyan', zorder=1002, label="pointing centre") # plot the target position on sky if target is not None: logger.info(" plotting target position") target_az = target.altaz.az.rad target_za = np.pi / 2 - target.altaz.alt.rad # get beam power for target bpt_x, bpt_y = pb.MWA_Tile_analytic(target_za, target_az, freq=freq * 1e6, delays=delays, power=True, zenithnorm=True) bpt = (bpt_x + bpt_y) / 2.0 lognormbpt = log_normalise(bpt, cf_levels.min(), beam.max()) logger.info("Beam power @ source: {0}".format(bpt)) logger.info(" log-normalised: {0}".format(lognormbpt)) # plot the target position on sky ax.plot(target_az, target_za, ls="", marker="o", color='C3', zorder=1002, label=" target:{} ".format(bpt)) # plot the target on the color bar cbar.ax.plot(0.5, lognormbpt, color='C3', marker="o") # draw grid ax.grid(color='k', ls="--", lw=0.5) # azimuth labels ax.set_xticks(np.radians([0, 45, 90, 135, 180, 225, 270, 315])) ax.set_xticklabels([ r"${0:d}^\circ$".format(int(np.ceil(x))) for x in np.degrees(ax.get_xticks()) ], color='k') # zenith angle labels ax.set_rlabel_position(250) ax.set_ylim(0, np.pi / 2) ax.set_yticks(np.radians([20, 40, 60, 80])) ax.set_yticklabels([ r"${0:d}^\circ$".format(int(np.ceil(x))) for x in np.degrees(ax.get_yticks()) ], color='k') # title ax.set_title( "MWA tied-array beam\naz = {0:.2f}, za = {1:.2f}, freq = {2:.2f}MHz\n{3}" .format(metadata["az"], metadata["za"], freq, time.iso)) logger.info("Saving figure") #plt.savefig("{0}_{1:.2f}MHz_tabeam.eps".format(obs, freq), bbox_inches="tight", format="eps") plt.savefig("{0}_{1:.2f}MHz_tabeam.png".format(obs, freq), bbox_inches="tight")
def get_beam_power_over_time(beam_meta_data, names_ra_dec, dt=296, centeronly=True, verbose=False, option='analytic', degrees=False, start_time=0): """ Calulates the power (gain at coordinate/gain at zenith) for each source over time. get_beam_power_over_time(beam_meta_data, names_ra_dec, dt=296, centeronly=True, verbose=False, option = 'analytic') Args: beam_meta_data: [obsid,ra, dec, time, delays,centrefreq, channels] obsid metadata obtained from meta.get_common_obs_metadata names_ra_dec: and array in the format [[source_name, RAJ, DecJ]] dt: time step in seconds for power calculations (default 296) centeronly: only calculates for the centre frequency (default True) verbose: prints extra data to (default False) option: primary beam model [analytic, advanced, full_EE] start_time: the time in seconds from the begining of the observation to start calculating at """ obsid, _, _, time, delays, centrefreq, channels = beam_meta_data names_ra_dec = np.array(names_ra_dec) logger.info("Calculating beam power for OBS ID: {0}".format(obsid)) 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) if not centeronly: PowersX = np.zeros((len(names_ra_dec), Ntimes, len(channels))) PowersY = np.zeros((len(names_ra_dec), Ntimes, len(channels))) # in Hz frequencies = np.array(channels) * 1.28e6 else: PowersX = np.zeros((len(names_ra_dec), Ntimes, 1)) PowersY = np.zeros((len(names_ra_dec), Ntimes, 1)) 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 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]) 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(len(frequencies)): #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) PowersX[:, itime, ifreq] = rX PowersY[:, itime, ifreq] = rY if verbose is False: sys.stdout = sys.__stdout__ Powers = 0.5 * (PowersX + PowersY) return Powers
def calc_jones(az, za, target_freq_Hz=205e6): za_rad = za * math.pi / 180.0 az_rad = az * math.pi / 180.0 za_rad = np.array([[za_rad]]) az_rad = np.array([[az_rad]]) gridpoint = mwa_sweet_spots.find_closest_gridpoint(az, za) print("Az = %.2f [deg], za = %.2f [deg], gridpoint = %d" % (az, za, gridpoint[0])) delays = gridpoint[4] delays = np.vstack((delays, delays)) print(delays) print("-----------") Jones_FullEE = primary_beam.MWA_Tile_full_EE(za_rad, az_rad, target_freq_Hz, delays=delays, zenithnorm=True, jones=True, interp=True) # swap axis to have Jones martix in the 1st Jones_FullEE_swap = np.swapaxes(np.swapaxes(Jones_FullEE, 0, 2), 1, 3) # TEST if equivalent to : Jones_FullEE_2D = Jones_FullEE_swap[:, :, 0, 0] # Jones_FullEE_2D = np.array([ [Jones_FullEE_swap[0, 0][0][0], Jones_FullEE_swap[0, 1][0][0]] , [Jones_FullEE_swap[1, 0][0][0], Jones_FullEE_swap[1, 1][0][0]] ]) print("Jones FullEE:") print("----------------------") print(Jones_FullEE) print("----------------------") print(Jones_FullEE_2D) print("----------------------") beams = {} beams['XX'], beams['YY'] = primary_beam.MWA_Tile_full_EE(za_rad, az_rad, target_freq_Hz, delays=delays, zenithnorm=True, power=True) print("Beams power = %.4f / %.4f" % (beams['XX'], beams['YY'])) # Average Embeded Element model: print("size(delays) = %d" % np.size(delays)) Jones_AEE = primary_beam.MWA_Tile_advanced(za_rad, az_rad, target_freq_Hz, delays=delays, jones=True) # Jones_AEE = primary_beam.MWA_Tile_advanced(np.array([[0]]), np.array([[0]]), target_freq_Hz,delays=delays, zenithnorm=True, jones=True) Jones_AEE_swap = np.swapaxes(np.swapaxes(Jones_AEE, 0, 2), 1, 3) Jones_AEE_2D = Jones_AEE_swap[:, :, 0, 0] # Jones_AEE_2D = np.array([ [Jones_AEE_swap[0, 0][0][0], Jones_AEE_swap[0, 1][0][0]], [Jones_AEE_swap[1, 0][0][0], Jones_AEE_swap[1, 1][0][0]] ]) print("----------------------") print("Jones AEE:") print("----------------------") print(Jones_AEE) print("----------------------") print(Jones_AEE_2D) print("----------------------") Jones_Anal = primary_beam.MWA_Tile_analytic(za_rad, az_rad, target_freq_Hz, delays=delays, zenithnorm=True, jones=True) Jones_Anal_swap = np.swapaxes(np.swapaxes(Jones_Anal, 0, 2), 1, 3) Jones_Anal_2D = Jones_Anal_swap[:, :, 0, 0] print("---------------------- COMPARISON OF JONES MATRICES FEE vs. AEE ----------------------") print("Jones FullEE:") print("----------------------") print(Jones_FullEE_2D) print("----------------------") print() print("Jones AEE:") print("----------------------") print(Jones_AEE_2D) print("----------------------") print() print("----------------------") print("Jones Analytic:") print("----------------------") print(Jones_Anal_2D) print("----------------------") return (Jones_FullEE_2D, Jones_AEE_2D, Jones_Anal_2D)
def calc_ratio(dec, target_freq_Hz, gridpoint): za = dec + 26.7 az = 0 print("\n\n\n\n") print("################################# DEC = %.2f #################################" % dec) za_rad = za * math.pi / 180.0 az_rad = az * math.pi / 180.0 za_rad = np.array([[za_rad]]) az_rad = np.array([[az_rad]]) # non-realistic scenario - I am ~tracking the source with the closest sweetspot - whereas I should be staying at the same sweetspot # gridpoint=find_closest_gridpoint(za) # print("dec=%.4f [deg] -> za=%.4f [deg] - %s" % (dec, za, gridpoint)) delays = gridpoint[4] delays = np.vstack((delays, delays)) print(delays) print("-----------") Jones_FullEE = primary_beam.MWA_Tile_full_EE(za_rad, az_rad, target_freq_Hz, delays=delays, zenithnorm=True, jones=True, interp=True) # swap axis to have Jones martix in the 1st Jones_FullEE_swap = np.swapaxes(np.swapaxes(Jones_FullEE, 0, 2), 1, 3) # TEST if equivalent to : Jones_FullEE_2D = Jones_FullEE_swap[:, :, 0, 0] # Jones_FullEE_2D = np.array([ [Jones_FullEE_swap[0, 0][0][0], Jones_FullEE_swap[0, 1][0][0]], [Jones_FullEE_swap[1, 0][0][0], Jones_FullEE_swap[1, 1][0][0]] ]) print("Jones FullEE:") print("----------------------") print(Jones_FullEE) print("----------------------") print(Jones_FullEE_2D) print("----------------------") # Average Embeded Element model: print("size(delays) = %d" % np.size(delays)) Jones_AEE = primary_beam.MWA_Tile_advanced(za_rad, az_rad, target_freq_Hz, delays=delays, jones=True) # Jones_AEE = primary_beam.MWA_Tile_advanced(np.array([[0]]), np.array([[0]]), target_freq_Hz, delays=delays, zenithnorm=True, jones=True) Jones_AEE_swap = np.swapaxes(np.swapaxes(Jones_AEE, 0, 2), 1, 3) Jones_AEE_2D = Jones_AEE_swap[:, :, 0, 0] # Jones_AEE_2D = np.array([ [Jones_AEE_swap[0, 0][0][0], Jones_AEE_swap[0, 1][0][0]], [Jones_AEE_swap[1, 0][0][0], Jones_AEE_swap[1, 1][0][0]] ]) print("----------------------") print("Jones AEE:") print("----------------------") print(Jones_AEE) print("----------------------") print(Jones_AEE_2D) print("----------------------") # Analytical Model: # beams = {} # beams['XX'], beams['YY'] = primary_beam.MWA_Tile_analytic(za_rad, az_rad, target_freq_Hz, delays=delays, zenithnorm=True, jones=True) Jones_Anal = primary_beam.MWA_Tile_analytic(za_rad, az_rad, target_freq_Hz, delays=delays, zenithnorm=True, jones=True) Jones_Anal_swap = np.swapaxes(np.swapaxes(Jones_Anal, 0, 2), 1, 3) Jones_Anal_2D = Jones_Anal_swap[:, :, 0, 0] # Jones_Anal_2D = np.array([ [Jones_Anal_swap[0, 0][0][0], Jones_Anal_swap[0, 1][0][0]] , [Jones_Anal_swap[1, 0][0][0],Jones_Anal_swap[1, 1][0][0]] ]) print("----------------------") print("Jones Analytic:") print("----------------------") print(Jones_Anal) print("----------------------") print(Jones_Anal_2D) print("----------------------") print("TEST:") print("----------------------") print("%.8f %.8f" % (Jones_Anal_2D[0, 0], Jones_Anal_2D[0, 1])) print("%.8f %.8f" % (Jones_Anal_2D[1, 0], Jones_Anal_2D[1, 1])) print("----------------------") # Use Jones_FullEE_2D as REAL sky and then ... B_sky = np.array([[1, 0], [0, 1]]) Jones_FullEE_2D_H = np.transpose(Jones_FullEE_2D.conj()) B_app = np.dot(Jones_FullEE_2D, np.dot(B_sky, Jones_FullEE_2D_H)) # E x B x E^H # test the procedure itself: Jones_FullEE_2D_H_Inv = inv2x2(Jones_FullEE_2D_H) Jones_FullEE_2D_Inv = inv2x2(Jones_FullEE_2D) B_sky_cal = np.dot(Jones_FullEE_2D_Inv, np.dot(B_app, Jones_FullEE_2D_H_Inv)) print(B_sky) print("Recovered using FullEE model:") print(B_sky_cal) # calibrate back using AEE model : # Jones_AEE_2D=Jones_Anal_2D # overwrite Jones_AEE_2D with Analytic to use it for calibration Jones_AEE_2D_H = np.transpose(Jones_AEE_2D.conj()) Jones_AEE_2D_H_Inv = inv2x2(Jones_AEE_2D_H) Jones_AEE_2D_Inv = inv2x2(Jones_AEE_2D) B_sky_cal = np.dot(Jones_AEE_2D_Inv, np.dot(B_app, Jones_AEE_2D_H_Inv)) print("Recovered using AEE model:") print(B_sky_cal) # I_cal = B_sky_cal[0, 0] + B_sky_cal[1, 1] # print "FINAL : %.8f ratio = %.8f / 2 = %.8f" % (dec,abs(I_cal),(abs(I_cal)/2.00)) # ratio = abs(B_sky_cal[0][0] / B_sky_cal[1][1]) # FINAL VALUES for the paper to compare with Figure 4 in GLEAM paper : ratio_ms = B_sky_cal[0][0] / B_sky_cal[1][1] gleam_ratio = ratio_ms gleam_XX = B_sky_cal[0][0] gleam_YY = B_sky_cal[1][1] # gleam_q_leakage = (B_sky_cal[0][0] - B_sky_cal[1][1]) / (B_sky_cal[0][0] + B_sky_cal[1][1]) print("DEBUG (DEC = %.2f deg) : GLEAM-ratio = %.4f = (%s / %s)" % (dec, gleam_ratio, gleam_XX, gleam_YY)) return (gleam_ratio.real, gleam_XX, gleam_YY)
logger.info("Correcting with AEE(%s) model at frequency %.2f Hz" % (model, target_freq_Hz)) # logging.getLogger("mwa_tile").setLevel(logging.DEBUG) Jones = primary_beam.MWA_Tile_advanced(za, az, target_freq_Hz, delays=delays, zenithnorm=options.zenithnorm, jones=True) if options.wsclean_image: sign_uv = -1 elif model == 'analytic' or model == '2014': logger.info("Correcting with analytic model") Jones = primary_beam.MWA_Tile_analytic(za, az, target_freq_Hz, delays=delays, zenithnorm=options.zenithnorm, jones=True) if options.wsclean_image: sign_q = -1 else: logger.error('Unknown model: %s' % model) sys.exit() # Swap axes so that Jones 2x2 matrix forms the first 2 dimensions Jones = np.swapaxes(np.swapaxes(Jones, 0, 2), 1, 3) Joneses[model] = Jones # Keep for further analysis # visibilities corrected for Direction Independed Gain (DIG) - it is in former step in CASA: Vij_corrected = Vij # Skip beam correction of cal solutions - this is done in CASA
def get_beam_power(obsid_data, sources, beam_model="analytic", centeronly=True): obsid, _, _, duration, delays, centrefreq, channels = obsid_data # Figure out GPS times to evaluate the beam in order to map the drift scan midtimes = np.array([ float(obsid) + 0.5 * duration ]) # although only includes one element, could in future be extended Ntimes = len(midtimes) # Make an EarthLocation for the MWA MWA_LAT = -26.7033 # degrees MWA_LON = 116.671 # degrees MWA_HEIGHT = 377.827 # metres mwa_location = EarthLocation(lat=MWA_LAT * u.deg, lon=MWA_LON * u.deg, height=MWA_HEIGHT * u.m) # Pre-allocate arrays for the beam patterns if not centeronly: PowersX = np.zeros((len(sources), Ntimes, len(channels))) PowersY = np.zeros((len(sources), Ntimes, len(channels))) frequencies = np.array(channels) * 1.28e6 else: PowersX = np.zeros((len(sources), Ntimes, 1)) PowersY = np.zeros((len(sources), Ntimes, 1)) frequencies = [centrefreq] # Create arrays of the RAs and DECs to be sampled (already in degrees) #print "Constructing RA and DEC arrays for beam model..." logger.info("Constructing RA and DEC arrays for beam model...") RAs = np.array([x[0] for x in sources]) Decs = np.array([x[1] for x in sources]) 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 # Turn RAs and DECs into SkyCoord objects that are to be fed to Alt/Az conversion coords = SkyCoord(RAs, Decs, unit=(u.deg, u.deg)) # Make times to feed to Alt/Az conversion obstimes = Time(midtimes, format='gps', scale='utc') #print "Converting RA and DEC to Alt/Az and computing beam pattern..." logger.info( "Converting RA and DEC to Alt/Az and computing beam pattern...") for t, time in enumerate(obstimes): # Convert to Alt/Az given MWA position and observing time altaz = coords.transform_to(AltAz(obstime=time, location=mwa_location)) # Change Altitude to Zenith Angle theta = np.pi / 2 - altaz.alt.rad phi = altaz.az.rad # Calculate beam pattern for each frequency, and store the results in a ndarray for f, freq in enumerate(frequencies): if beam_model == "analytic": rX, rY = primary_beam.MWA_Tile_analytic(theta, phi, freq=freq, delays=delays, zenithnorm=True, power=True) elif beam_model == "FEE": rX, rY = primary_beam.MWA_Tile_full_EE(theta, phi, freq=freq, delays=delays, zenithnorm=True, power=True) else: #print "Unrecognised beam model '{0}'. Defaulting to 'analytic'." logger.warning( "Unrecognised beam model '{0}'. Defaulting to 'analytic'.") rX, rY = primary_beam.MWA_Tile_analytic(theta, phi, freq=freq, delays=delays, zenithnorm=True, power=True) PowersX[:, t, f] = rX PowersY[:, t, f] = rY # Convert X and Y powers into total intensity Powers = 0.5 * (PowersX + PowersY) return Powers
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) obsfreq - the centre observing frequency for the observation eff - the array efficiency (frequency and pointing dependent, currently require engineers to calculate for us...) flagged_tiles - the flagged tiles from the calibration solution (in the RTS format) xpos - x position of the tiles ypos - y position of the tiles zpos - z position of the tiles theta_res - the zenith angle resolution in degrees phi_res - the azimuth resolution in degrees za_chunk - list of ZA to compute array factor for write - whether to actually write a file to disk rank - mpi rank (thread index) Return: results - a list of lists cotaining [ZA, Az, beam power], for all ZA and Az in the given band """ obsid = data['obsid'] ra = data['ra'] dec = data['dec'] times = data['time'] delays = data['delays'] xpos = data['x'] ypos = data['y'] zpos = data['z'] cable_delays = data['cd'] theta_res = data['tres'] phi_res = data['pres'] beam_model = data['beam'] coplanar = data['coplanar'] ord_version = data['ord_version'] no_delays = data['no_delays'] plot_jobid = data['plot_jobid'] # work out core depent part of data to process nfreq = len(data['freqs']) ifreq = rank % nfreq ichunk = rank // nfreq obsfreq = data['freqs'][ifreq] logger.info( "rank {:3d} Calculating phase of each sky position for each tile".format(rank)) # calculate the relevent wavenumber for (theta,phi) #logger.info( "rank {:3d} Calculating wavenumbers".format(rank)) if ord_version: #gx, gy, gz = calc_geometric_delay_distance((np.pi / 2) - az, za) gx, gy, gz = calc_geometric_delay_distance(az, za) logger.debug("rank {:3d} Wavenumbers shapes: {} {} {}".format(rank, gx.shape, gy.shape, gz.shape)) # phase of each sky position for each tile ph_tile = cal_phase_ord(xpos, ypos, zpos, cable_delays, gx, gy, gz, obsfreq, coplanar=coplanar, no_delays=no_delays) gx = gy = gz = None # Dereference for garbage collection else: kx, ky, kz = calcWaveNumbers(obsfreq, (np.pi / 2) - az, za) #logger.debug("kx[0] {} ky[0] {} kz[0] {}".format(kx[0], ky[0], kz[0])) logger.debug("rank {:3d} Wavenumbers shapes: {} {} {}".format(rank, kx.shape, ky.shape, kz.shape)) # phase of each sky position for each tile ph_tile = calcSkyPhase(xpos, ypos, zpos, kx, ky, kz, coplanar=coplanar) kx = ky = kz = None # Dereference for garbage collection requests.get('https://ws.mwatelescope.org/progress/update', params={'jobid':plot_jobid, 'workid':rank, 'current':2, 'total':5, 'desc':'Tile beam'}) # calculate the tile beam at the given Az,ZA pixel logger.info( "rank {:3d} Calculating tile beam".format(rank)) if beam_model == 'hyperbeam': # This method is no longer needed as mwa_pb uses hyperbeam jones = beam.calc_jones_array(az, za, obsfreq, delays, [1.0] * 16, True) jones = jones.reshape(za.shape[0], 1, 2, 2) vis = pb.mwa_tile.makeUnpolInstrumentalResponse(jones, jones) jones = None # Dereference for garbage collection tile_xpol, tile_ypol = (vis[:, :, 0, 0].real, vis[:, :, 1, 1].real) vis = None # Dereference for garbage collection elif beam_model == 'analytic': tile_xpol, tile_ypol = pb.MWA_Tile_analytic(za, az, freq=obsfreq, delays=[delays, delays], zenithnorm=True, power=True) elif beam_model == 'advanced': tile_xpol, tile_ypol = pb.MWA_Tile_advanced(za, az, freq=obsfreq, delays=[delays, delays], zenithnorm=True, power=True) elif beam_model == 'full_EE': tile_xpol, tile_ypol = pb.MWA_Tile_full_EE(za, az, freq=obsfreq, delays=np.array([delays, delays]), zenithnorm=True, power=True, interp=False) #logger.info("rank {:3d} Combining tile pattern".format(rank)) tile_pattern = np.divide(np.add(tile_xpol, tile_ypol), 2.0) tile_pattern = tile_pattern.flatten() logger.debug("max(tile_pattern) {}".format(max(tile_pattern))) omega_A_times = [] sum_B_T_times = [] sum_B_times = [] phased_array_pattern_times = [] for ti, time in enumerate(times): requests.get('https://ws.mwatelescope.org/progress/update', params={'jobid':plot_jobid, 'workid':rank, 'current':3+ti, 'total' :3+len(times), 'desc':'{}/{} array factor'.format(1+ti, len(times))}) # get the target azimuth and zenith angle in radians and degrees # these are defined in the normal sense: za = 90 - elevation, az = angle east of North (i.e. E=90) logger.info( "rank {:3d} Calculating phase of each sky position for the target at time {}".format(rank, time)) srcAz, srcZA, _, _ = getTargetAZZA(ra, dec, time)# calculate the target (kx,ky,kz) if ord_version: #t_gx, t_gy, t_gz = calc_geometric_delay_distance((np.pi / 2) - srcAz, srcZA) t_gx, t_gy, t_gz = calc_geometric_delay_distance(srcAz, srcZA) # phase of each sky position for each tile ph_target = cal_phase_ord(xpos, ypos, zpos, cable_delays, t_gx, t_gy, t_gz, obsfreq, coplanar=coplanar, no_delays=no_delays) else: target_kx, target_ky, target_kz = calcWaveNumbers(obsfreq, (np.pi / 2) - srcAz, srcZA) # Get phase of target for each tile phase of each sky position for each tile ph_target = calcSkyPhase(xpos, ypos, zpos, target_kx, target_ky, target_kz) # determine the interference pattern seen for each tile logger.info( "rank {:3d} Calculating array_factor".format(rank)) #logger.debug("rank {:3d} ti: {} ph_tile {}".format(rank, ti, ph_tile)) #logger.debug("rank {:3d} ti: {} ph_target {}".format(rank, ti, ph_target)) array_factor, array_factor_power = calcArrayFactor(ph_tile, ph_target) ph_target = None # Dereference for garbage collection #logger.debug("array_factor_power[0] {}".format(array_factor_power[0])) logger.debug("rank {:3d} array_factor[0] {}".format(rank, array_factor[0])) logger.debug("rank {:3d} array_factor shapes: {}".format(rank, array_factor.shape)) logger.debug("rank {:3d} array factor maximum = {}".format(rank, np.amax(array_factor_power))) # calculate the phased array power pattern logger.info( "rank {:3d} Calculating phased array pattern".format(rank)) logger.debug("rank {:3d} tile_pattern.shape {} array_factor_power.shape {}".format(rank, tile_pattern.shape, array_factor_power.shape)) phased_array_pattern = np.multiply(tile_pattern, array_factor_power) #phased_array_pattern = tile_pattern[0][0] * np.abs(array_factor)**2 # indexing due to tile_pattern now being a 2-D array phased_array_pattern_times.append(phased_array_pattern) # add this contribution to the beam solid angle logger.info( "rank {:3d} Calculating omega_A".format(rank)) #omega_A_array = np.sin(za) * array_factor_power * np.radians(theta_res) * np.radians(phi_res) #omega_A_array = np.multiply(np.multiply(np.multiply(np.sin(za), array_factor_power), np.radians(theta_res)), np.radians(phi_res)) omega_A_array = np.multiply(array_factor_power, pixel_area) logger.debug("rank {:3d} omega_A_array shapes: {}".format(rank, omega_A_array.shape)) omega_A = np.sum(omega_A_array) logger.debug("rank {:3d} freq {:.2f}MHz beam_area: {}".format(rank, obsfreq/1e6, omega_A)) omega_A_times.append(omega_A) #logger.debug("omega_A[1] vals: za[1] {}, array_factor_power[1] {}, theta_res {}, phi_res {}".format(za[1], array_factor_power[1], theta_res, phi_res)) #logger.debug("omega_A[1] {}".format(omega_A_array[1])) # Do a partial sum over the sky and frequency to be finished outside of the function sum_B_T, sum_B = partial_convolve_sky_map(az, za, pixel_area, phased_array_pattern, obsfreq, time) sum_B_T_times.append(sum_B_T) sum_B_times.append(sum_B) ph_tile = None # Dereference for garbage collection # Average over time #omega_A = np.average(omega_A_times) #sum_B_T = np.average(sum_B_T_times) #sum_B = np.average(sum_B_times) #phased_array_pattern = np.average(phased_array_pattern_times, axis=0) logger.debug("rank {:3d} phased_array_pattern: {}".format(rank, phased_array_pattern)) logger.debug("rank {:3d} omega_A_times: {}".format(rank, omega_A_times)) logger.info("rank {:3d} done".format(rank)) # return lists of results for each time step return np.array(omega_A_times), np.array(sum_B_T_times), np.array(sum_B_times), np.array(phased_array_pattern_times)
def get_mwapointing_grid(self, frequency=150e6, minprob=0.01, minelevation=45, returndelays=False, returnpower=False): """ :rtype: SkyCoord :param frequency: Frequency in Hz :param minprob: Minimum probability value :param minelevation: Minimum elevation angle, in degrees :param returndelays: If True, return delay values as well as MWA grid points :param returnpower: If True, return delay values AND primary beam power in that direction as well as MWA grid points :return: astropy.coordinates.SkyCoord object containing MWA coordinate grid """ """ RADec=MWA_GW.get_mwapointing_grid(frequency=150e6, minprob=0.01, minelevation=45 returndelays=False, returnpower=False) if returndelays=True, returns: RADec,delays if returnpower=True, returns: RADec,delays,power """ if (self.MWA_grid is None) or (self.MWA_grid.data is None): self.critical('Unable to find MWA grid points') if not (returndelays or returnpower): return None else: if returnpower: return None, None, None else: return None, None # has it been downsampled already gwmap = self.gwmap_down AltAz = self.AltAz_down gridRADec = self.MWA_grid.get_radec() self.debug('Computing pointing for %s' % (self.obstime)) # figure out what fraction is above horizon pointingmap = (gwmap * (AltAz.alt > 0)).sum() if pointingmap < minprob: self.info( 'Insufficient power (%.3f) above horizon (>%.3f required)\n' % (pointingmap, minprob)) if not (returndelays or returnpower): return None else: if returnpower: return None, None, None else: return None, None # first go from altitude to zenith angle theta_horz = np.pi / 2 - AltAz.alt.radian phi_horz = AltAz.az.radian mapsum = np.zeros((len(self.MWA_grid.data))) start = timer() for igrid in range(len(self.MWA_grid.data)): beamX, beamY = primary_beam.MWA_Tile_analytic( theta_horz, phi_horz, freq=frequency, delays=self.MWA_grid.data[igrid]['delays'], zenithnorm=True, power=True) mapsum[igrid] = ((beamX + beamY) * 0.5 * gwmap).sum() # this is the best grid point # such that it is over our minimum elevation igrid = np.where(mapsum == mapsum[ self.MWA_grid.data['elevation'] > minelevation].max())[0][0] end = timer() self.info("Best pointing found in %.1f s" % (end - start)) self.info("Looped over %d grid points" % (len(self.MWA_grid.data))) if not (self.MWA_grid.data['elevation'][igrid] > minelevation): self.info('Elevation %.1f deg too low\n' % self.MWA_grid.data['elevation'][igrid]) # too close to horizon if not (returndelays or returnpower): return None else: if returnpower: return None, None, None else: return None, None msg = 'Best pointing at RA,Dec=%.1f,%.1f; Az,El=%.1f,%.1f: power=%.3f' self.info(msg % (gridRADec[igrid].ra.value, gridRADec[igrid].dec.value, self.MWA_grid.data['azimuth'][igrid], self.MWA_grid.data['elevation'][igrid], mapsum[igrid])) if mapsum[igrid] < minprob: msg = 'Pointing at Az,El=%.1f,%.1f has power=%.3f < min power (%.3f)\n' self.info(msg % (self.MWA_grid.data['azimuth'][igrid], self.MWA_grid.data['elevation'][igrid], mapsum[igrid], minprob)) if not (returndelays or returnpower): return None else: if returnpower: return None, None, None else: return None, None if not (returndelays or returnpower): return gridRADec[igrid] else: if not returnpower: return gridRADec[igrid], self.MWA_grid.data[igrid]['delays'] else: return gridRADec[igrid], self.MWA_grid.data[igrid][ 'delays'], mapsum[igrid]
def createArrayFactor(targetAZ, targetZA, targetAZdeg, targetZAdeg, obsid, delays, time, obsfreq, eff, flagged_tiles, theta_res, phi_res, coplanar, zenith, za_chunk, write): """ Primary function to calculate the array factor with the given information. Input: targetAZ, targetZA, targetAZdeg, targetZAdeg: target sky position (Az, ZA) in radians and degrees obsid - the observation ID for which to create the phased array beam delays - the beamformer delays required for point tile beam (should be a set of 16 numbers) obstime - the time at which to evaluate the target position (as we need Azimuth and Zenith angle, which are time dependent) obsfreq - the centre observing frequency for the observation eff - the array efficiency (frequency and pointing dependent, currently require engineers to calculate for us...) flagged_tiles - the flagged tiles from the calibration solution (in the RTS format) theta_res - the zenith angle resolution in degrees phi_res - the azimuth resolution in degrees coplanar - whether or not to assume that the z-components of antenna locations is 0m zenith - force the pointing to be zenith (i.e. ZA = 0deg, Az = 0deg) za_chunk - list of ZA to compute array factor for write - whether to actually write a file to disk Return: results - a list of lists cotaining [ZA, Az, beam power], for all ZA and Az in the given band """ # convert frequency to wavelength obswl = obsfreq / c.value # calculate the target (kx,ky,kz) target_kx, target_ky, target_kz = calcWaveNumbers(obswl, (np.pi / 2) - targetAZ, targetZA) results = [] # is this the last process? rank = size - 1 # we will also calculate the beam area contribution of this part of the sky omega_A = 0 array_factor_max = -1 # TODO: this is the important bit, and I'm sure we are doing this the dumb, slow way... # for each ZA "pixel", 90deg inclusive for za in za_chunk: # for each Az "pixel", 360deg not included for az in genAZZA(0, 2 * np.pi, np.radians(phi_res)): # calculate the relevent wavenumber for (theta,phi) kx, ky, kz = calcWaveNumbers(obswl, (np.pi / 2) - az, za) array_factor = 0 + 0.j # determine the interference pattern seen for each tile for x, y, z in zip(xpos, ypos, zpos): ph = kx * x + ky * y + kz * z ph_target = target_kx * x + target_ky * y + target_kz * z array_factor += np.cos(ph - ph_target) + 1.j * np.sin(ph - ph_target) # normalise to unity at pointing position array_factor /= len(xpos) array_factor_power = np.abs(array_factor)**2 # keep track of maximum value calculated if (array_factor_power > array_factor_max): array_factor_max = array_factor_power # calculate the tile beam at the given Az,ZA pixel #tile_xpol,tile_ypol = pb.MWA_Tile_analytic([[za]],[[az]],freq=obsfreq,delays=[delays,delays],power=True,zenithnorm=True,interp=False) tile_xpol, tile_ypol = pb.MWA_Tile_analytic(za, az, freq=obsfreq, delays=delays, power=True, zenithnorm=True) tile_pattern = (tile_xpol + tile_ypol) / 2.0 # calculate the phased array power pattern phased_array_pattern = tile_pattern * array_factor_power #phased_array_pattern = tile_pattern[0][0] * np.abs(array_factor)**2 # indexing due to tile_pattern now being a 2-D array # append results to a reference list for later results.append([np.degrees(za), np.degrees(az), phased_array_pattern]) # add this contribution to the beam solid angle omega_A += np.sin(za) * array_factor_power * np.radians(theta_res) * np.radians(phi_res) logger.info("worker {0}, array factor maximum = {1}".format(rank, array_factor_max)) # write a file based on rank of the process being used if write: logger.info("writing file from worker {0}".format(rank)) with open(oname.replace(".dat",".{0}.dat".format(rank)), 'w') as f: if rank == 0: # if master process, write the header information first and then the data f.write("##File Type: Far field\n##File Format: 3\n##Source: mwa_tiedarray\n##Date: {0}\n".format(time.iso)) f.write("** File exported by FEKO kernel version 7.0.1-482\n\n") f.write("#Request Name: FarField\n#Frequency: {0}\n".format(obsfreq)) f.write("#Coordinate System: Spherical\n#No. of Theta Samples: {0}\n#No. of Phi Samples: {1}\n".format(ntheta, nphi)) f.write("#Result Type: Gain\n#No. of Header Lines: 1\n") f.write('#\t"Theta"\t"Phi"\t"Re(Etheta)"\t"Im(Etheta)"\t"Re(Ephi)"\t"Im(Ephi)"\t"Gain(Theta)"\t"Gain(Phi)"\t"Gain(Total)"\n') for res in results: # write each line of the data # we actually need to rotate out phi values by: phi = pi/2 - az because that's what FEKO expects. # values are calculated using that convetion, so we need to represent that here f.write("{0}\t{1}\t0\t0\t0\t0\t0\t0\t{2}\n".format(res[0], res[1], res[2])) else: logger.warn("worker {0} not writing".format(rank)) return omega_A
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
def plot_beam(obs, target, cal, freq): metadata = get_common_obs_metadata(obs) phi = np.linspace(0, 360, 3600) theta = np.linspace(0, 90, 900) # make coordinate grid az, za = np.meshgrid(np.radians(phi), np.radians(theta)) # compute beam and plot delays = metadata[4] #x and y delays logger.debug("delays: {0}".format(delays)) logger.debug("freq*1e6: {0}".format(freq * 1e6)) logger.debug("za: {0}".format(za)) logger.debug("az: {0}".format(az)) gx, gy = pb.MWA_Tile_analytic(za, az, freq=int(freq * 1e6), delays=delays, power=True, zenithnorm=True) beam = (gx + gy) / 2.0 fig = plt.figure(figsize=(10, 8)) ax = fig.add_subplot(111, polar=True, aspect='auto') # filled contour setup lower_contour = 7e-3 upper_contour = beam.max() fill_min = 1e-2 # 1% of zenith power fill_max = 0.95 * beam.max() # 95% max beam power ( != zenith power) Z = np.copy(beam) Z[Z <= fill_min] = 0 Z[Z >= fill_max] = fill_max cc_levels = np.logspace(np.log10(lower_contour), np.log10(upper_contour), num=20) cc_cmap = plt.get_cmap('gray_r') cc_norm = LogNorm(vmin=cc_levels.min(), vmax=beam.max()) cc = ax.contourf(az, za, Z, levels=cc_levels, cmap=cc_cmap, norm=cc_norm, zorder=1000) cc.cmap.set_over('black') # anything over max is black cc.cmap.set_under('white') # anything under min is white # contour lines steup cs_levels = np.logspace(np.log10(fill_min), np.log10(fill_max), num=5) cs_cmap = plt.get_cmap('gist_heat') cs_norm = LogNorm(vmin=cs_levels.min(), vmax=cs_levels.max()) cs = ax.contour(az, za, beam, levels=cs_levels, cmap=cs_cmap, norm=cs_norm, zorder=1001) # color bar setup cbarCC = plt.colorbar(cc, pad=0.08, shrink=0.9) cbarCC.set_label(label="zenith normalised power", size=20) cbarCC.set_ticks(cs_levels) cbarCC.set_ticklabels([r"${0:.2f}$".format(x) for x in cs_levels]) cbarCC.ax.tick_params(labelsize=18) #add lines from the contours to the filled color map cbarCC.add_lines(cs) # plot the pointing centre of the tile beam _, pointing_AZ, pointing_ZA = mwa_alt_az_za(obs) ax.plot(np.radians(pointing_AZ), np.radians(pointing_ZA), ls="", marker="+", ms=8, color='C3', zorder=1002, label="pointing centre") if target is not None: # color map for tracking target positions target_colors = cm.viridis(np.linspace(0, 1, len(target.obstime.gps))) logger.info("obs time length: {0} seconds".format( len(target.obstime.gps))) for t, color in zip(target, target_colors): target_az = t.altaz.az.rad target_za = np.pi / 2 - t.altaz.alt.rad # get beam power for target bpt_x, bpt_y = pb.MWA_Tile_analytic( target_za, target_az, freq=freq * 1e6, delays=[metadata[4][0], metadata[4][1]], power=True, zenithnorm=True) bpt = (bpt_x + bpt_y) / 2.0 lognormbpt = log_normalise(bpt, cc_levels.min(), beam.max()) logger.debug("bpt, cc_levels, beam: {0}, {1}, {2}".format( bpt, cc_levels.min(), beam.max())) logger.info("Beam power @ source @ gps: {0}: {1}".format( t.obstime.gps, bpt)) logger.info("log-normalised BP: {0}".format(lognormbpt)) # plot the target position on sky ax.plot(target_az, target_za, ls="", marker="o", color=color, zorder=1002, label="target @ {0} ({1})".format(t.obstime.gps, bpt)) # plot the target on the color bar cbarCC.ax.plot(0.5, lognormbpt, color=color, marker="o") if cal is not None: # color map for tracking calibrator position calibrator_colors = cm.viridis(np.linspace(0, 1, len(cal.obstime.gps))) for c, color in zip(cal, calibrator_colors): cal_az = c.altaz.az.rad cal_za = np.pi / 2 - c.altaz.alt.rad # get the beam power for calibrator bpc_x, bpc_y = pb.MWA_Tile_analytic([[cal_za]], [[cal_az]], freq=freq * 1e6, delays=metadata[4], power=True, zenithnorm=True) bpc = (bpc_x + bpc_y) / 2.0 lognormbpc = log_normalise(bpc, cc_levels.min(), beam.max()) logger.info("Beam power @ calibrator @ gps:{0}: {1:.3f}".format( c.obstime.gps, bpc[0][0])) logger.info("log-normalised BP: {0:.3f}".format(lognormbpc[0][0])) # plot the calibrator position on sky ax.plot(cal_az, cal_za, ls="", marker="^", color=color, zorder=1002, label="cal @ {0} ({1:.2f})".format(c.obstime.gps, bpc[0][0])) # plot the calibrator on the color bar cbarCC.ax.plot(0.5, lognormbpc, color=color, marker="^") # draw grid ax.grid(color='k', ls="--", lw=0.5) # azimuth labels ax.set_xticks(np.radians([0, 45, 90, 135, 180, 225, 270, 315])) ax.set_xticklabels([ r"${0:d}^\circ$".format(int(np.ceil(x))) for x in np.degrees(ax.get_xticks()) ], color='k') #Zenith angle labels ax.set_rlabel_position(250) ax.set_yticks(np.radians([20, 40, 60, 80])) ax.set_yticklabels([ r"${0:d}^\circ$".format(int(np.ceil(x))) for x in np.degrees(ax.get_yticks()) ], color='k') # Title ax.set_title( "MWA Tile beam (FEE)\naz = {0:.2f}, za = {1:.2f}, freq = {2:.2f}MHz\nobsid = {3}" .format(pointing_AZ, pointing_ZA, freq, obs)) plt.legend(bbox_to_anchor=(0.95, -0.05), ncol=2) #plt.savefig("{0}_{1:.2f}MHz_tile.eps".format(obs, freq), bbox_inches="tight", format="eps") logger.info("Saving plot as output file: {0}_{1:.2f}MHz_tile.png".format( obs, freq)) plt.savefig("{0}_{1:.2f}MHz_tile.png".format(obs, freq), bbox_inches="tight")