Source code for ixpeobssim.srcmodel.gabs

#!/urs/bin/env python
#
# Copyright (C) 2015--2019, the ixpeobssim team.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

from __future__ import print_function, division


import os
import numpy

from astropy.io import fits
from astropy.coordinates import SkyCoord
from astropy.wcs import WCS

from ixpeobssim import IXPEOBSSIM_SRCMODEL
from ixpeobssim.utils.logging_ import abort
from ixpeobssim.core.spline import xInterpolatedUnivariateSplineLinear



[docs] def mapped_column_density_HI(ra, dec, map_name='LAB'): """Return the mapped H_I column density at a given position in the sky. The value is read from one of the available input maps. Note that the data in the maps are stored in Galactic coordinates, while we're giving Celestial coordinates in input, here---the transformation is handled internally. Arguments --------- ra : float Right ascension of the source (in decimal degrees). dec: float Declination of the source (in decimal degrees). map: str The HI column density map to use. Can be either 'LAB' (LAB survey) or 'DL' (Dickey & Lockman). """ # Make sure the map name makes sense. assert map_name in ['LAB', 'DL'] # Transform from Celestial to Galactic coordinates. gal_coords = SkyCoord(ra, dec, unit='deg').galactic l, b = gal_coords.l.degree, gal_coords.b.degree # Open the selected input FITS map and grab the values. file_path = os.path.join(IXPEOBSSIM_SRCMODEL,'fits','h1_nh_%s.fits' %\ map_name) if not os.path.exists(file_path): abort('Could not find %s' % file_path) with fits.open(file_path) as hdu_list: _wcs = WCS(hdu_list[0].header) _data = hdu_list[0].data row, col = [int(item) for item in _wcs.wcs_world2pix(l, b, 1)] return _data[col, row]
[docs] class xInterstellarAbsorptionModel: """Class implementing the insterstellar absorption model using the Wisconsin (Morrison and McCammon; ApJ 270, 119) cross-sections. Here we essentially read the numbers in table 2 from the paper and build an interpolated univariate linear spline with the photoabsorption cross section values. The cross section is parametrized as a set of piecewise quadratic functions fitted to the data in energy bins---see the figure below. .. image:: ../docs/figures/gabs_xsection.png Example ------- >>> from ixpeobssim.srcmodel.gabs import xInterstellarAbsorptionModel >>> model = xInterstellarAbsorptionModel() # Build a spline with the interstellar transmission factor as a function # of the photon energy >>> trans = model.transmission_factor(1.e22) >>> trans.plot() """ def __init__(self, num_samples=1000): """Constructor. Arguments --------- num_samples : int The number of data points used to sample (logarithmically) the photon energies when tabulating the absorption cross section internally. """ file_path = os.path.join(IXPEOBSSIM_SRCMODEL, 'ascii', 'XsecFitParam.txt') if not os.path.exists(file_path): abort('Could not find %s' % file_path) # Read the data from file and calculate the minimum and maximum # energies. e_lo, e_hi, c0, c1, c2 = numpy.loadtxt(file_path, delimiter=',', unpack=True) emin = e_lo[0] emax = e_hi[-1] # Sample the energy logarithmically between emin and emax. _x = numpy.logspace(numpy.log10(emin), numpy.log10(emax), num_samples) # Here comes the interesting part---the cross section is tabulated # by means of a set of piecewise quadratic functions, and the # three coefficients are provided in each energy bin. We do some # magic with the numpy.digitize() function, returning for each value # in _x its bin index in the original table. Note that we need to # clip the array removing negative numbers, as the double log/exp # operations done in defining the binning where driving the first # index to -1. _bin = (numpy.digitize(_x, e_lo) - 1).clip(min=0) # Calculate the cross section values---compact, isn't it? _y = 1.0e-24*(c0[_bin] + c1[_bin]*_x + c2[_bin]*(_x**2.))/(_x**3.) # And, finally, build the basic spline underlying the model. _fmt = dict(xlabel='Energy [keV]', ylabel='$\\sigma_{abs}$ [cm$^2$]') self.xsection = xInterpolatedUnivariateSplineLinear(_x, _y, **_fmt) def __call__(self, energy): """Return the value(s) of the photoelectric absorption cross section of the model for a value (or an array of values) of energy. Arguments --------- energy : float or array The energy (or energies) at which the cross section should be calculated. """ return self.xsection(energy)
[docs] def xsection_ecube(self): """Return a spline with the values of absorption cross section multiplied by the cube of the energy, as a function of the energy. This is essentially the same plot in Figure 1 of the original paper, as illustrated in the following figure .. image:: ../docs/figures/gabs_xsection_ecube.png """ _x = self.xsection.x _y = self(_x)*(_x**3.) _fmt = dict(xlabel='Energy [keV]', ylabel='$\\sigma_{abs} \\times E^3$ [cm$^2$~keV$^3$]') return xInterpolatedUnivariateSplineLinear(_x, _y, **_fmt)
[docs] def transmission_factor(self, column_density): """Return the transmission factor for a given column density. This is essentially returning .. math:: \\varepsilon = \\exp(-n_H\\sigma) .. image:: ../docs/figures/gabs_trans_samples.png Arguments --------- column_density : float The column density at which the transmission factor should be calculated. Warning ------- We do have an issue with the extrapolation, here, as at the current stage there is no guarantee that the result of the spline evaluation would be <= 1. We could set the ext class member of the spline to 3 before reurning it, but event that would not be right in general. This is probably not a huge issue, but it should be addressed properly. """ _x = self.xsection.x _y = numpy.exp(-column_density*self(_x)) _fmt = dict(xlabel='Energy [keV]', ylabel='Transmission factor') return xInterpolatedUnivariateSplineLinear(_x, _y, **_fmt)