"""
Frb class and method functions.
"""
import numpy as np
from e13tools import docstring_substitute
from astropy.coordinates import SkyCoord
import astropy.units as u
import pyymw16 as ymw16
from . import estimate
from . import cosmology
from ._fruitbatstrings import dm_units_doc
__all__ = ["Frb"]
[docs]@docstring_substitute(dm_units=dm_units_doc)
class Frb(object):
"""
Create a :class:`~Frb` object using the observered properties of a FRB to
properties including
To define a :class:`~Frb` object all it requires is an observed dispersion
measure.
Parameters
----------
dm : float
The observed dispersion measure of the FRB. This is without Milky Way
or host galaxy subtraction.
Units: %(dm_units)s
Keyword Arguments
-----------------
name : str or None, optional
The name of the frb object. Default: *None*
raj : str or None, optional
The right ascension in J2000 coordinates of the best estimate of the
FRB position. Default: *None*
decj : str or None, optional
The declination in J2000 coordinates of the best estimate of the
FRB position. Default: *None*
gl : str or None, optional
The Galactic longitude in degrees of the best estimate of the FRB
position. Default: *None*
gb : str or None, optional
The Galactic latitude in degrees of the best estimate of the FRB
position. Default: *None*
Other Parameters
----------------
dm_galaxy : float, optional
The modelled contribution to the FRB DM by electrons in the Milky Way.
Units: %(dm_units)s Default: 0.0
dm_excess : float or None, optional
The DM excess of the FRB over the estimated Galactic DM. If
:attr:`dm_excess` is *None*, then :attr:`dm_excess` is calculated
automatically with :meth:`calc_dm_excess()`.
Units: %(dm_units)s Default: *None*
z_host : float or None, optional
The observed redshift of the localised FRB host galaxy.
Default: *None*
dm_host_est : float, optional
The estimated contribution to the measured FRB DM from originating from
the FRB's host galaxy. This value is the amount of DM the host galaxy
contributes to the observed DM, *not* the DM of the host galaxy.
Units: %(dm_units)s Default: 0.0
dm_host_loc : float, optional
The dispersion measure of a localised FRB host galaxy. This value is
*not* the contribution to the observed DM, but the DM at the host
galaxy. The observed DM is :attr:`dm_host_loc` but attenuated by a
factor of (1 + z).
Units: %(dm_units)s Default: 0.0
dm_index : float or None, optional
The dispersion measure index of the burst :math:`\\alpha` such that
:math:`\\rm{DM} \\propto \\nu^{-\\alpha}` Default: *None*
scatt_index : float or None, optional
The scattering index (:math:`\\beta`) of the FRB pulse. The
scattering index describes how the width (:math:`\\rm{W}`) of the
FRB pulse evolves with frequency :math:`\\nu` such that
:math:`\\rm{W} \\propto \\nu^{-\\beta}`. Default: *None*
snr : float or None, optional
The signal-to-noise of the burst.
Default: *None*
w_obs : float or None, optional
The observed width of the pulse obtained by a pulse fitting algorithm.
Units: :math:`\\rm{ms}` Default: *None*
s_peak_obs : float or None, optional
The observed peak flux density of the burst.
Units: :math:`\\rm{Jy}` Default: *None*
f_obs : float or None, optional
The observed fluence of the FRB. If :attr:`f_obs` is *None* and both
:attr:`w_obs` and :attr:`s_peak_obs` are not *None* then :attr:`f_obs`
is automatically calculated by :attr:`w_obs` x :attr:`s_peak_obs`
Units: :math:`\\rm{Jy\\ ms}` Default: *None*
utc : str or None, optional
The UTC time of the FRB Burst. Default: *None*
dm_uncert : float, optional
The uncertainty in the dispersion measure.
Units: %(dm_units)s Default: 0.0
z_uncert : float, optional
The uncertainty in the redshift of the FRB. Default: 0.0
**Example**
>>> import fruitbat
>>> FRB = fruitbat.Frb(879, gl="12:31:40.5", gb="3:41:10.0")
>>> FRB.calc_dm_galaxy()
>>> FRB.calc_redshift()
"""
def __init__(self, dm, *, name=None, raj=None, decj=None, gl=None, gb=None,
dm_galaxy=0.0, dm_excess=None, z_host=None, dm_host_est=0.0,
dm_host_loc=0.0, dm_index=None, scatt_index=None, snr=None,
w_obs=None, s_peak_obs=None, f_obs=None, utc=None,
dm_uncert=0.0, z_uncert=0.0):
# TO DO:
# There are a few other parameters that I should add:
# dm_index
self._name = name
self._dm = float(dm)
self._dm_uncert = float(dm_uncert)
self._dm_galaxy = float(dm_galaxy)
self._dm_host_est = float(dm_host_est)
self._dm_host_loc = float(dm_host_loc)
# Calculate dm_excess from existing parameters if it is not given.
if dm_excess is None:
self.calc_dm_excess()
else:
self._dm_excess = dm_excess
print(dm)
dm_list = [dm, dm_galaxy, dm_host_est, dm_host_loc,
dm_uncert, self.dm_excess]
for item in dm_list:
if item < 0.0:
raise ValueError("Dispersion Measure can not be negative.")
self._dm_index = dm_index
self._z_host = z_host
self._z_uncert = z_uncert
self._scatt_index = scatt_index
self._snr = snr
self._w_obs = w_obs
self._s_peak_obs = s_peak_obs
self._utc = utc
self._raj = raj
self._decj = decj
self._gl = gl
self._gb = gb
self._z = None
self._dm_igm = None
# Calculate F_obs if s_peak_obs and w_obs are given
if (not f_obs) and (s_peak_obs and w_obs):
self.calc_f_obs()
else:
self._f_obs = f_obs
# Calculate the skycoords of the FRB from (raj, decj) or (gl, gb)
if (raj and decj) or (gl and gb):
self._skycoords = self.calc_skycoords()
self._raj = self.skycoords.icrs.ra
self._decj = self.skycoords.icrs.dec
self._gl = self.skycoords.galactic.l
self._gb = self.skycoords.galactic.b
else:
self._skycoords = None
def __repr__(self):
return 'Frb({0})'.format(vars(self))
[docs] @docstring_substitute(methods=estimate.methods(string=True),
cosmo=cosmology.keys())
def calc_redshift(self, method='inoue2004', cosmology="Planck18",
subtract_host=False):
"""
Calculate the redshift of the FRB from its :attr:`dm`, :attr:`dm_excess`
or :attr:`dm_excess` - :attr:`dm_host_est`.
Parameters
----------
method : str, optional
The approximation to use when calculating the redshift.
Avaliable methods: %(methods)s
cosmology : str, optional
The method `inoue2004` has the option to choose which cosmology
to assume when performing the redshift estimation.
Avaliable cosmologies: %(cosmo)s
subtract_host : bool, optional
Subtract :attr:`dm_host_est` from the :attr:`dm_excess` before
calculating the redshift. This is is used to account for the
dispersion measure that arises from the FRB host galaxy.
Returns
-------
float
The redshift of the FRB.
Notes
-----
The methods_ section in the documentation has a discription of each
methods and where they should apply.
The cosmology_ section of the documentation has a list of the
cosmological parameters used in each cosmology method.
.. _cosmology: https://fruitbat.readthedocs.io/en/latest/user_guide/method_and_cosmology.html#cosmology
.. _methods: https://fruitbat.readthedocs.io/en/latest/user_guide/method_and_cosmology.html#methods
"""
if not isinstance(subtract_host, bool):
raise ValueError("subtract_host must be of type bool.")
if subtract_host:
input_dm = self._dm_excess - self._dm_host_est
else:
input_dm = self._dm_excess
z = estimate.redshift(dm=input_dm, method=method, cosmology=cosmology)
self._z = z
return z
[docs] def calc_skycoords(self):
"""
Calculates the skycoord position on the sky of the FRB from
(:attr:`raj`, :attr:`decj`) or (:attr:`gl`, :attr:`gb`).
Returns
-------
astropy.coordinates.sky_coordinate.SkyCoord
The sky coordinates of the FRB.
"""
if self._raj and self._decj:
skycoords = SkyCoord(self._raj, self._decj, frame="icrs",
unit=(u.hourangle, u.deg))
elif self._gl and self._gb:
skycoords = SkyCoord(self._gl, self._gb, frame="galactic",
unit=u.deg)
else:
raise ValueError("To calculate skycoords either (raj and decj)"
"or (gl, gb) must be provided")
return skycoords
[docs] def calc_dm_excess(self):
"""
Calculates the dispersion measure excess of the FRB by subtracting
the DM contribution from the Milky Way.
Returns
-------
dm_excess : float
The dispersion measure excess.
Notes
-----
:math:`\\rm{DM_{excess}}` is calculated as follows:
.. math::
DM_{excess} = DM - DM_{galaxy}
"""
dm_excess = self._dm - self._dm_galaxy
self._dm_excess = dm_excess
return dm_excess
[docs] def calc_dm_galaxy(self, model='ymw16'):
"""
Calculates the dispersion measure contribution of the Milky Way from
either (:attr:`raj`, :attr:`decj`) or (:attr:`gl`, :attr:`gb`).
Parameters
----------
model : str, optional
The Milky Way dispersion measure model. Default: ymw16
"""
# Since the YMW16 model only gives you a dispersion measure out to a
# distance within the galaxy, to get the entire DM contribution of the
# galaxy we need to specify the furthest distance in the YMW16 model.
max_galaxy_dist = 25000 # units: pc
if (self.skycoords is None and
(self.raj is not None and self.decj is not None) or
(self.gl is not None and self.gb is not None)):
self._skycoords = self.calc_skycoords()
elif not all([self.skycoords, self.raj, self.decj, self.gl, self.gb]):
raise ValueError("""Can not calculate dm_galaxy since coordinates
for FRB burst were not provided. Please provide
(raj, decj) or (gl, gb) coordinates.""")
dm_galaxy, tau_sc = ymw16.dist_to_dm(self._skycoords.galactic.l,
self._skycoords.galactic.b,
max_galaxy_dist)
self._tau_sc = tau_sc
self._dm_galaxy = dm_galaxy.value
self.calc_dm_excess()
return dm_galaxy.value
[docs] def calc_dm_igm(self):
"""
Calculates the dispersion measure of the intergalactic medium along the
line-of-sight of the FRB. This can only be done if the redshift and
dispersion measure contribution of the FRB host galaxy is known.
Returns
-------
dm_igm : float
The dispersion measure contribution of the IGM.
Notes
-----
:math:`DM_{IGM}` is calculated as follows:
.. math::
DM_{IGM} = DM_{excess} - \\frac{DM_{host,loc}}{1 + z}
"""
if self.z_host is None:
err_msg = ("z_host is None. Provide a non zero value for the "
"FRB host redshift")
raise ValueError(err_msg)
if self.dm_host_loc == 0.0:
err_msg = ("dm_host_loc = 0. The dm_igm will be the same as "
"dm_excess. Provide a non-zero value for dm_host_loc")
raise ValueError(err_msg)
dm_igm = self.dm_excess - (self.dm_host_loc / (1 + self.z_host))
self._dm_igm = dm_igm
return dm_igm
[docs] def calc_f_obs(self):
"""
Calculates the observed fluence of the FRB. This requires :attr:`w_obs`
and :attr:`s_peak_obs` to not be *None*.
Returns
-------
float
The fluence of the FRB.
Notes
-----
:math:`\\rm{F_{obs}}` is calculated as follows:
.. math::
\\rm{F_{obs} = W_{obs} \\times S_{peak, obs}}
"""
if (not self.w_obs) or (not self.s_peak_obs):
err_msg = ("calc_f_obs requires both w_obs and s_peak_obs "
"to not be None")
raise ValueError(err_msg)
f_obs = self.w_obs * self.s_peak_obs
self._f_obs = f_obs
return f_obs
# def calc_d_comov(self, cosmology):
# def pulse_width(self, freq):
# """
# The width of the pulse at a given frquency using the scattering index.
#
# Parameters
# ----------
# freq: float
# The frequency at
#
# Returns
# -------
# float:
# The width of the pulse.
# """
# coeff =
# return coeff * freq**(-self.scatt_index)
@property
def name(self):
"""
str:
The name of the FRB object.
"""
return self._name
@property
def dm(self):
"""
float:
The observed dispersion measure of the FRB.
"""
return self._dm
# @property
# def dm_uncert(self):
# """
# float: The uncertainty in the observed dispersion measure of the FRB.
# """
# return self._dm_uncert
@property
def dm_galaxy(self):
"""
float:
The Milky Way component of the dispersion measure.
"""
return self._dm_galaxy
@property
def dm_excess(self):
"""
float:
The dispersion measure with the Milky Way component subtracted.
"""
return self._dm_excess
@property
def dm_host_est(self):
"""
float:
The dispersion measure from the FRB host galaxy
"""
return self._dm_host_est
@property
def dm_host_loc(self):
"""
float:
The dispersion measure from a localised FRB host galaxy
"""
return self._dm_host_loc
# @property
# def dm_index(self):
# """
# float: The dispersion measure index of the burst.
# """
# return self._dm_index
@property
def z(self):
"""
float or None:
The estimated redshift of the burst. By default this assumes that
the entire :attr:`dm_excess` arrives from the IGM and the host
galaxy of the FRB and any surrounding material contribute nothing
to the total DM. This should be taken as an upper limit to the
bursts true redshift. To provide an estimate of the DM contribution
due to he host galaxy, set :attr:`dm_host_est` to a non-zero value
and use ``subract_host=True`` when using :meth:`calc_redshift()`.
"""
return self._z
@property
def z_host(self):
"""
float or None:
The redshift of the localised FRB host galaxy. Note that this an
observed quantity, not the estimated redshift :attr:`z` calculated with
:meth:`calc_redshift()`
"""
return self._z_host
# @property
# def z_uncert(self):
# """float: The uncertainty in the measurement of the FRB redshift"""
# return self._z_uncert
# @property
# def scatt_index(self):
# """float: The scattering index of the FRB."""
# return self._scatt_index
@property
def w_obs(self):
"""
float or None:
The observed width of the pulse obtained by a pulse fitting algorithm.
Units: :math:`\\rm{ms}`
"""
return self._w_obs
@property
def s_peak_obs(self):
"""
float or None:
The observed peak flux density of the burst. Units:
"""
return self._s_peak_obs
@property
def f_obs(self):
"""The Milky Way component of the dispersion measure."""
return self._f_obs
@property
def raj(self):
"""
astropy.coordinates.angles.Longitude or None:
The right accension in J2000 coordinates of the best estimate of the
FRB position.
"""
return self._raj
@property
def decj(self):
"""
astropy.coordinates.angles.Latitude or None:
The declination in J2000 coordinates of the best estimate of the FRB
position.
"""
return self._decj
@property
def gl(self):
"""
astropy.coordinates.angles.Longitude or None:
The longitude in galactic coordinates of the best estimate of the
FRB position.
"""
return self._gl
@property
def gb(self):
"""
astropy.coordinates.angles.Latitude or None:
The latitude in galactic coordinates of the best estimate of the
FRB position.
"""
return self._gb
@property
def skycoords(self):
"""
astropy.coordinates.sky_coordinate.SkyCoord or None: The
skycoords of the FRB. This is calculated from either
(:attr:`raj`, :attr:`decj`) or (:attr:`gl`, :attr:`gb`).
"""
return self._skycoords
@property
def dm_igm(self):
"""
"""
return self._dm_igm