Source code for py21cmsense.beam

from abc import ABC

import attr
from astropy import constants as cnst
from astropy import units as un

from . import _utils as ut


[docs]@attr.s(frozen=True) class PrimaryBeam(ABC): """ A Base class defining a Primary Beam and the methods it requires to define. """ frequency = attr.ib( converter=ut.apply_or_convert_unit("MHz"), validator=ut.positive )
[docs] def new(self, **kwargs): """Return a clone of this instance, but change kwargs""" return attr.evolve(self, **kwargs)
[docs] def area(self, freq=None): """Beam area (sr)""" pass
[docs] def width(self, freq=None): """Beam width (rad)""" pass
[docs] def first_null(self, freq=None): """An approximation of the first null of the beam""" pass
[docs] def sq_area(self, freq=None): """The area of the beam^2""" pass
[docs] def b_eff(self, freq=None): """Effective beam area (Parsons 2014)""" pass
@property def uv_resolution(self): pass
[docs] @classmethod def from_uvbeam(cls): raise NotImplementedError("coming soon to a computer near you!")
[docs]@attr.s(frozen=True) class GaussianBeam(PrimaryBeam): """ A simple Gaussian Primary beam. Parameters ---------- frequency : float or Quantity The fiducial frequency at which the beam operates, assumed to be in MHz unless otherwise defined. dish_size : float or Quantity The size of the (assumed circular) dish, assumed to be in meters unless otherwise defined. This generates the beam size. """ dish_size = attr.ib(converter=ut.apply_or_convert_unit("m"), validator=ut.positive)
[docs] def dish_size_in_lambda(self, freq=None): """The dish size in units of wavelengths, for a given frequency. If frequency is not given, uses the instance's `frequency` """ freq = ut.apply_or_convert_unit("MHz")(freq or self.frequency) return (self.dish_size / (cnst.c / freq)).to("").value
[docs] def area(self, freq=None): """The integral of the beam over angle, in sr. If frequency is not given, uses the instance's `frequency` """ return 1.13 * self.fwhm(freq) ** 2
[docs] def width(self, freq=None): """The width of the beam (i.e. sigma), in radians If frequency is not given, uses the instance's `frequency` """ return un.rad * 0.45 / self.dish_size_in_lambda(freq)
[docs] def fwhm(self, freq=None): """The full-width half maximum of the beam If frequency is not given, uses the instance's `frequency` """ return 2.35 * self.width(freq)
[docs] def sq_area(self, freq=None): """The integral of the squared beam, in sr If frequency is not given, uses the instance's `frequency` """ return self.area(freq) / 2
[docs] def b_eff(self, freq=None): return self.area(freq) ** 2 / self.sq_area(freq)
[docs] def first_null(self, freq=None): """The angle of the first null of the beam. .. note:: The Gaussian beam has no null, and in this case we use the first null for an airy disk. If frequency is not given, uses the instance's `frequency` """ return un.rad * 1.22 / self.dish_size_in_lambda(freq)
@property def uv_resolution(self): """The appropriate resolution of a UV cell given the beam size""" return self.dish_size_in_lambda()