Source code for pyuvsim.analyticbeam

# -*- mode: python; coding: utf-8 -*
# Copyright (c) 2018 Radio Astronomy Software Group
# Licensed under the 3-clause BSD License

from __future__ import absolute_import, division, print_function

import numpy as np
from scipy.special import spherical_jn as jn


[docs]class AnalyticBeam(object): """ Defines an object with similar functionality to pyuvdata.UVBeam Directly calculates jones matrices at given azimuths and zenith angles from analytic functions. Supports uniform (unit response in all directions), gaussian, and Airy function beam types. """ supported_types = ['uniform', 'gaussian', 'airy'] @profile def __init__(self, type, sigma=None, diameter=None): if type in self.supported_types: self.type = type else: raise ValueError('type not recognized') self.sigma = sigma self.diameter = diameter self.data_normalization = 'peak' def peak_normalize(self): pass
[docs] @profile def interp(self, az_array, za_array, freq_array): """ Evaluate the primary beam at given az, za locations (in radians). (similar to UVBeam.interp) Args: az_array: az values to evaluate at in radians (same length as za_array) The azimuth here has the UVBeam convention: North of East(East=0, North=pi/2) za_array: za values to evaluate at in radians (same length as az_array) freq_array: frequency values to evaluate at Returns: an array of beam values, shape (Naxes_vec, Nspws, Nfeeds or Npols, Nfreqs or freq_array.size if freq_array is passed, Npixels/(Naxis1, Naxis2) or az_array.size if az/za_arrays are passed) an array of interpolated basis vectors (or self.basis_vector_array if az/za_arrays are not passed), shape: (Naxes_vec, Ncomponents_vec, Npixels/(Naxis1, Naxis2) or az_array.size if az/za_arrays are passed) """ if self.type == 'uniform': interp_data = np.zeros((2, 1, 2, freq_array.size, az_array.size), dtype=np.float) interp_data[1, 0, 0, :, :] = 1 interp_data[0, 0, 1, :, :] = 1 interp_data[1, 0, 1, :, :] = 1 interp_data[0, 0, 0, :, :] = 1 interp_basis_vector = None elif self.type == 'gaussian': if self.sigma is None: raise ValueError("Sigma needed for gaussian beam -- units: radians") interp_data = np.zeros((2, 1, 2, freq_array.size, az_array.size), dtype=np.float) # gaussian beam only depends on Zenith Angle (symmetric is azimuth) # standard deviation of sigma is refereing to the standard deviation of e-field beam! values = np.exp(-(za_array**2) / (2 * self.sigma**2)) # copy along freq. axis values = np.broadcast_to(values, (freq_array.size, az_array.size)) interp_data[1, 0, 0, :, :] = values interp_data[0, 0, 1, :, :] = values interp_data[1, 0, 1, :, :] = values interp_data[0, 0, 0, :, :] = values interp_basis_vector = None elif self.type == 'airy': if self.diameter is None: raise ValueError("Diameter needed for airy beam -- units: meters") interp_data = np.zeros((2, 1, 2, freq_array.size, az_array.size), dtype=np.float) za_grid, f_grid = np.meshgrid(za_array, freq_array) xvals = self.diameter / 2. * np.sin(za_grid) * 2. * np.pi * f_grid / 3e8 values = np.zeros_like(xvals) values[xvals > 0.] = 2. * jn(1, xvals[xvals > 0.]) / xvals[xvals > 0.] values[xvals == 0.] = 1. interp_data[1, 0, 0, :, :] = values interp_data[0, 0, 1, :, :] = values interp_data[1, 0, 1, :, :] = values interp_data[0, 0, 0, :, :] = values interp_basis_vector = None else: raise ValueError('no interp for this type: ', self.type) return interp_data, interp_basis_vector
def __eq__(self, other): if not isinstance(other, self.__class__): return False if self.type == 'gaussian': return ((self.type == other.type) and (self.sigma == other.sigma)) elif self.type == 'uniform': return other.type == 'uniform' elif self.type == 'airy': return ((self.type == other.type) and (self.diameter == other.diameter)) else: return False