Source code for pyuvsim.antenna

# Copyright (c) 2018 Radio Astronomy Software Group
# Licensed under the 3-clause BSD License
"""Definition of Antenna objects, to describe a single interferometric element."""

import astropy.units as units
import numpy as np
import numpy.typing as npt

from . import utils as simutils


[docs]class Antenna: """ Describe a single interferometric element. One of these defined for each antenna in the array. Parameters ---------- name : str Name of this antenna. number : int Number of this antenna. enu_position : array_like of float Position of this antenna in meters in the East, North, Up frame centered on the telescope location. beam_id : int Index of the beam for this antenna from :class:`~BeamList`. """ def __init__( self, name: str, number: int, enu_position: npt.NDArray[float], beam_id: int ): assert isinstance(name, str), "name must be a string" assert isinstance(number, int | np.int32 | np.int64), "number must be an int" assert isinstance(beam_id, int | np.int32 | np.int64), "beam_id must be an int" self.name = name self.number = number self.pos_enu = enu_position * units.m self.beam_id = beam_id
[docs] def get_beam_jones( self, array, source_alt_az, frequency, reuse_spline=True, interpolation_function=None, freq_interp_kind=None, beam_interp_check=True, ): """ Calculate the jones matrix for this antenna in the direction of sources. A Nfeeds x 2 x Nsources array of Efield vectors in Az/Alt. Parameters ---------- array : Telescope object Provides the beam list. source_alt_az : array_like Positions to evaluate in alt/az, where source_alt_az[0] gives list of alts soruce_alt_az[1] gives list of corresponding az frequency : float or Quantity Frequency. Assumed to be Hz if float. reuse_spline : bool Option to keep and reuse interpolation splines in :class:`pyuvdata.UVBeam`. interpolation_function: str Set the angular interpolation function on the :class:`pyuvdata.UVBeam`. See :meth:`pyuvdata.UVBeam.interp` for options. freq_interp_kind : str Interpolation method for frequencies. See :meth:`pyuvdata.UVBeam.interp` for options. beam_interp_check : bool Option to check whether the beam covers all the skymodel az/za values to ensure that they are covered by the intrinsic data array. Checking them can be quite computationally expensive. Defaults to True to run the check, can be set to False if the beam coverage is known. Returns ------- jones_matrix : array_like of float Jones matricies for each source location, shape (Nfeeds,2, Ncomponents). The first axis is feed, the second axis is vector component on the sky in az/za. """ # convert to UVBeam az/za convention source_za, source_az = simutils.altaz_to_zenithangle_azimuth( source_alt_az[0], source_alt_az[1] ) if isinstance(frequency, units.Quantity): freq = np.array([frequency.to("Hz").value]) else: freq = np.array([frequency]) if self.beam_id not in range(len(array.beam_list)): raise ValueError( f"This antenna beam_id is {self.beam_id}, which is too large " f"for the beam_list, which has length {len(array.beam_list)}." ) if array.beam_list.beam_type != "efield": raise ValueError("Beam type must be efield!") if ( array.beam_list.data_normalization is not None and array.beam_list.data_normalization != "peak" ): raise ValueError("UVBeams must be peak normalized.") bi = array.beam_list[self.beam_id] interp_kwargs = { "az_array": source_az, "za_array": source_za, "freq_array": freq, "reuse_spline": reuse_spline, "check_azza_domain": beam_interp_check, } if interpolation_function is not None: interp_kwargs["interpolation_function"] = interpolation_function spline_opts = array.beam_list.spline_interp_opts if spline_opts is not None: interp_kwargs["spline_opts"] = spline_opts bl_freq_interp_kind = array.beam_list.freq_interp_kind if freq_interp_kind is None: freq_interp_kind = bl_freq_interp_kind if freq_interp_kind is not None: interp_kwargs["freq_interp_kind"] = freq_interp_kind interp_data = bi.compute_response(**interp_kwargs) Ncomponents = source_za.shape[-1] Nfeeds = interp_data.shape[1] # interp_data has shape: # (Naxes_vec, Nfeeds, 1 (freq), Ncomponents (source positions)) jones_matrix = np.zeros((Nfeeds, 2, Ncomponents), dtype=complex) # first axis is feed, second axis is theta, phi (opposite order of beam!) jones_matrix[:, 0] = interp_data[1, :, 0, :] jones_matrix[:, 1] = interp_data[0, :, 0, :] return jones_matrix
def __eq__(self, other): """Test for equality of objects.""" return ( (self.name == other.name) and np.allclose( self.pos_enu.to("m").value, other.pos_enu.to("m").value, atol=1e-3 ) and (self.beam_id == other.beam_id) ) def __gt__(self, other): """Test for larger antenna number.""" return self.number > other.number def __ge__(self, other): """Test for larger or equal antenna number.""" return self.number >= other.number def __lt__(self, other): """Test for smaller antenna number.""" return not self.__ge__(other) def __le__(self, other): """Test for smaller or equal antenna number.""" return not self.__gt__(other)