/***************************************************************************
* GLATResponseTable.cpp - Fermi/LAT Response table class *
* ----------------------------------------------------------------------- *
* copyright (C) 2008-2013 by Juergen Knoedlseder *
* ----------------------------------------------------------------------- *
* *
* 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, see . *
* *
***************************************************************************/
/**
* @file GLATResponseTable.cpp
* @brief Fermi/LAT response table class implementation
* @author Juergen Knoedlseder
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include "GLATResponseTable.hpp"
#include "GTools.hpp"
#include "GException.hpp"
#include "GFitsTableFloatCol.hpp"
/* __ Method name definitions ____________________________________________ */
#define G_READ "GLATResponseTable::read(GFitsTable&)"
#define G_INDEX "GLATResponseTable::index(int&, int&)"
#define G_ENERGY "GLATResponseTable::energy(int&)"
#define G_ENERGY_LO "GLATResponseTable::energy_lo(int&)"
#define G_ENERGY_HI "GLATResponseTable::energy_hi(int&)"
#define G_COSTHETA_LO "GLATResponseTable::costheta_lo(int&)"
#define G_COSTHETA_HI "GLATResponseTable::costheta_hi(int&)"
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Debug definitions __________________________________________________ */
/* __ Constants __________________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
GLATResponseTable::GLATResponseTable(void)
{
// Initialise class members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param table Response table.
***************************************************************************/
GLATResponseTable::GLATResponseTable(const GLATResponseTable& table)
{
// Initialise class members
init_members();
// Copy members
copy_members(table);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
GLATResponseTable::~GLATResponseTable(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param table Response table.
* @return Response table.
***************************************************************************/
GLATResponseTable& GLATResponseTable::operator=(const GLATResponseTable& table)
{
// Execute only if object is not identical
if (this != &table) {
// Free members
free_members();
// Initialise private members
init_members();
// Copy members
copy_members(table);
} // endif: object was not identical
// Return this object
return *this;
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Clear instance
*
* This method properly resets the object to an initial state.
***************************************************************************/
void GLATResponseTable::clear(void)
{
// Free class members
free_members();
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Clone instance
***************************************************************************/
GLATResponseTable* GLATResponseTable::clone(void) const
{
return new GLATResponseTable(*this);
}
/***********************************************************************//**
* @brief Read response table from FITS table HDU
*
* @param[in] hdu Response table HDU.
*
* The response table definition is assumed to be stored in 4 vector columns
* with names
* ENERG_LO (energy bins lower boundary)
* ENERG_HI (energy bins upper boundary)
* CTHETA_LO (cos theta bins lower boundary)
* CTHETA_HI (cos theta bins upper boundary)
***************************************************************************/
void GLATResponseTable::read(const GFitsTable& hdu)
{
// Clear instance
clear();
// Get pointers to table columns
const GFitsTableCol* energy_lo = hdu["ENERG_LO"];
const GFitsTableCol* energy_hi = hdu["ENERG_HI"];
const GFitsTableCol* ctheta_lo = hdu["CTHETA_LO"];
const GFitsTableCol* ctheta_hi = hdu["CTHETA_HI"];
// Extract number of bins
m_energy_num = energy_lo->number();
m_ctheta_num = ctheta_lo->number();
// Allocate memory
if (m_energy_num > 0) {
m_energy_lo = new double[m_energy_num];
m_energy_hi = new double[m_energy_num];
}
if (m_ctheta_num > 0) {
m_ctheta_lo = new double[m_ctheta_num];
m_ctheta_hi = new double[m_ctheta_num];
}
// Transfer data
for (int i = 0; i < m_energy_num; ++i) {
m_energy_lo[i] = energy_lo->real(0,i);
m_energy_hi[i] = energy_hi->real(0,i);
}
for (int i = 0; i < m_ctheta_num; ++i) {
m_ctheta_lo[i] = ctheta_lo->real(0,i);
m_ctheta_hi[i] = ctheta_hi->real(0,i);
}
// Set energy nodes (log10 of energy)
if (m_energy_num > 0) {
double *logE = new double[m_energy_num];
for (int i = 0; i < m_energy_num; ++i) {
logE[i] = 0.5 * (log10(m_energy_lo[i]) + log10(m_energy_hi[i]));
//ST method logE[i] = log10(sqrt(m_energy_lo[i]*m_energy_hi[i]));
m_energy.push_back(std::pow(10.0, logE[i]));
}
m_logE.nodes(m_energy_num, logE);
delete [] logE;
}
// Set cos theta nodes
if (m_ctheta_num > 0) {
double *ctheta = new double[m_ctheta_num];
for (int i = 0; i < m_ctheta_num; ++i) {
ctheta[i] = 0.5 * (m_ctheta_lo[i] + m_ctheta_hi[i]);
}
m_ctheta.nodes(m_ctheta_num, ctheta);
delete [] ctheta;
}
// Return
return;
}
/***********************************************************************//**
* @brief Write response table into FITS table
*
* @param[in] hdu Fits table HDU.
***************************************************************************/
void GLATResponseTable::write(GFitsTable& hdu) const
{
// Allocate floating point vector columns
GFitsTableFloatCol col_energy_lo = GFitsTableFloatCol("ENERG_LO", 1, m_energy_num);
GFitsTableFloatCol col_energy_hi = GFitsTableFloatCol("ENERG_HI", 1, m_energy_num);
GFitsTableFloatCol col_ctheta_lo = GFitsTableFloatCol("CTHETA_LO", 1, m_ctheta_num);
GFitsTableFloatCol col_ctheta_hi = GFitsTableFloatCol("CTHETA_HI", 1, m_ctheta_num);
// Set column values
for (int i = 0; i < m_energy_num; ++i) {
col_energy_lo(0,i) = m_energy_lo[i];
col_energy_hi(0,i) = m_energy_hi[i];
}
for (int i = 0; i < m_ctheta_num; ++i) {
col_ctheta_lo(0,i) = m_ctheta_lo[i];
col_ctheta_hi(0,i) = m_ctheta_hi[i];
}
// Append columns to boundary table
hdu.append(col_energy_lo);
hdu.append(col_energy_hi);
hdu.append(col_ctheta_lo);
hdu.append(col_ctheta_hi);
// Return
return;
}
/***********************************************************************//**
* @brief Return table index
*
* @param[in] ie Energy bin (starting from 0)
* @param[in] ic cos theta bin (starting from 0)
*
* @exception GException::out_of_range
* Energy or cos theta bin index out of range
*
* Computes table index from energy and cos theta bin indices.
***************************************************************************/
int GLATResponseTable::index(const int& ie, const int& ic) const
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (ie < 0 || ie >= m_energy_num ||
ic < 0 || ic >= m_ctheta_num) {
throw GException::out_of_range(G_INDEX, ie, ic,
m_energy_num-1, m_ctheta_num-1);
}
#endif
// Compute index
int index = ic * m_energy_num + ie;
// Return index
return index;
}
/***********************************************************************//**
* @brief Return mean energy of bin (units: MeV)
*
* @param[in] ie Index of energy bin (starting from 0)
*
* @exception GException::out_of_range
* Energy bin index out of range
*
* The mean energy is actually computed from the logarithmic average of
* lower and upper boundaries:
* \f$E=10^{0.5 \times (\log{E_{\rm min}} + \log{E_{\rm max}})}\f$
* Note that this energy is stored in the m_energy array, hence to convert
* to MeV we simply have to the it to the power of 10.
*
* @todo Store also linear energies to avoid conversion.
***************************************************************************/
double GLATResponseTable::energy(const int& ie) const
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (ie < 0 || ie >= m_energy_num) {
throw GException::out_of_range(G_ENERGY, ie, 0, m_energy_num-1);
}
#endif
// Determine mean energy of bin
//double mean = 0.5 * (log10(m_energy_lo[ie]) + log10(m_energy_hi[ie]));
//double mean = m_logE[ie];
//double energy = pow(10.0, mean);
// Return mean energy
return (m_energy[ie]);
}
/***********************************************************************//**
* @brief Set indices and weighting for bi-linear interpolation of 2D array
*
* @param[in] logE Base 10 logarithm of the energy (MeV).
* @param[in] ctheta Cosine of zenith angle.
*
* Bi-linear interpolation is performed in log10 of energy and in cos theta.
***************************************************************************/
void GLATResponseTable::set(const double& logE, const double& ctheta)
{
// Flag no change of values
bool change = false;
// Set energy interpolation
if (logE != m_last_energy) {
m_logE.set_value(logE);
m_last_energy = logE;
change = true;
}
// Set cos(theta) interpolation
if (ctheta != m_last_ctheta) {
m_ctheta.set_value(ctheta);
m_last_ctheta = ctheta;
change = true;
}
// If change occured then update interpolation indices and weighting
// factors
if (change) {
// Set array indices for bi-linear interpolation
int inx_ctheta_left = m_ctheta.inx_left() * m_energy_num;
int inx_ctheta_right = m_ctheta.inx_right() * m_energy_num;
m_inx1 = m_logE.inx_left() + inx_ctheta_left;
m_inx2 = m_logE.inx_left() + inx_ctheta_right;
m_inx3 = m_logE.inx_right() + inx_ctheta_left;
m_inx4 = m_logE.inx_right() + inx_ctheta_right;
// Set weighting factors for bi-linear interpolation
m_wgt1 = m_logE.wgt_left() * m_ctheta.wgt_left();
m_wgt2 = m_logE.wgt_left() * m_ctheta.wgt_right();
m_wgt3 = m_logE.wgt_right() * m_ctheta.wgt_left();
m_wgt4 = m_logE.wgt_right() * m_ctheta.wgt_right();
} // endif: logE or ctheta changed
// Return
return;
}
/***********************************************************************//**
* @brief Perform bi-linear interpolation of 2D array
*
* @param[in] logE Base 10 logarithm of the energy (MeV).
* @param[in] ctheta Cosine of zenith angle.
* @param[in] array Array to be interpolated.
*
* Bi-linear interpolation is performed in log10 of energy and in cos theta.
* The array is stored in a std::vector object with the logE axis varying
* more rapidely.
***************************************************************************/
double GLATResponseTable::interpolate(const double& logE,
const double& ctheta,
const std::vector& array)
{
// Set interpolation indices and weights
set(logE, ctheta);
// Perform bi-linear interpolation
double value = m_wgt1 * array[m_inx1] +
m_wgt2 * array[m_inx2] +
m_wgt3 * array[m_inx3] +
m_wgt4 * array[m_inx4];
// Return bi-linear interpolated value
return value;
}
/***********************************************************************//**
* @brief Perform bi-linear interpolation of 3D array
*
* @param[in] logE Base 10 logarithm of the energy (MeV).
* @param[in] ctheta Cosine of zenith angle.
* @param[in] array Array to be interpolated.
* @param[in] offset Offset if 3D array in 1st dimension.
* @param[in] size Size of 3D array in 1st dimension.
*
* Bi-linear interpolation is performed in log10 of energy and in cos theta.
* The array is stored in a std::vector object.
***************************************************************************/
double GLATResponseTable::interpolate(const double& logE,
const double& ctheta,
const std::vector& array,
const int& offset,
const int& size)
{
// Set interpolation indices and weights
set(logE, ctheta);
// Perform bi-linear interpolation
double value = m_wgt1 * array[m_inx1*size+offset] +
m_wgt2 * array[m_inx2*size+offset] +
m_wgt3 * array[m_inx3*size+offset] +
m_wgt4 * array[m_inx4*size+offset];
// Return bi-linear interpolated value
return value;
}
/***********************************************************************//**
* @brief Return lower bin energy (units: MeV)
*
* @param[in] inx Index of energy bin (starting from 0)
*
* @exception GException::out_of_range
* Energy bin index out of range
***************************************************************************/
double GLATResponseTable::energy_lo(const int& inx) const
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (inx < 0 || inx >= m_energy_num) {
throw GException::out_of_range(G_ENERGY_LO, inx, 0, m_energy_num-1);
}
#endif
// Return mean energy
return m_energy_lo[inx];
}
/***********************************************************************//**
* @brief Return upper bin energy (units: MeV)
*
* @param[in] inx Index of energy bin (starting from 0)
*
* @exception GException::out_of_range
* Energy bin index out of range
***************************************************************************/
double GLATResponseTable::energy_hi(const int& inx) const
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (inx < 0 || inx >= m_energy_num) {
throw GException::out_of_range(G_ENERGY_HI, inx, 0, m_energy_num-1);
}
#endif
// Return mean energy
return m_energy_hi[inx];
}
/***********************************************************************//**
* @brief Return lower bin cos theta
*
* @param[in] inx Index of cos theta bin (starting from 0)
*
* @exception GException::out_of_range
* Cos theta bin index out of range
***************************************************************************/
double GLATResponseTable::costheta_lo(const int& inx) const
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (inx < 0 || inx >= m_ctheta_num) {
throw GException::out_of_range(G_COSTHETA_LO, inx, 0, m_ctheta_num-1);
}
#endif
// Return mean energy
return m_ctheta_lo[inx];
}
/***********************************************************************//**
* @brief Return upper bin cos theta
*
* @param[in] inx Index of cos theta bin (starting from 0)
*
* @exception GException::out_of_range
* Cos theta bin index out of range
***************************************************************************/
double GLATResponseTable::costheta_hi(const int& inx) const
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (inx < 0 || inx >= m_ctheta_num) {
throw GException::out_of_range(G_COSTHETA_HI, inx, 0, m_ctheta_num-1);
}
#endif
// Return mean energy
return m_ctheta_hi[inx];
}
/***********************************************************************//**
* @brief Return indices of 4 corners used for interpolation
***************************************************************************/
std::vector GLATResponseTable::indices(void) const
{
// Define vector
std::vector incides;
// Push indices on vector
incides.push_back(m_inx1);
incides.push_back(m_inx2);
incides.push_back(m_inx3);
incides.push_back(m_inx4);
// Return vector
return incides;
}
/***********************************************************************//**
* @brief Return energies of 4 corners used for interpolation
***************************************************************************/
std::vector GLATResponseTable::energies(void) const
{
// Define vector
std::vector energies;
// Push energies on vector
if (m_energy_num > 0) {
energies.push_back(m_energy[m_logE.inx_left()]);
energies.push_back(m_energy[m_logE.inx_left()]);
energies.push_back(m_energy[m_logE.inx_right()]);
energies.push_back(m_energy[m_logE.inx_right()]);
}
// Return vector
return energies;
}
/***********************************************************************//**
* @brief Return weights of 4 corners used for interpolation
***************************************************************************/
std::vector GLATResponseTable::weights(void) const
{
// Define vector
std::vector weights;
// Push indices on vector
weights.push_back(m_wgt1);
weights.push_back(m_wgt2);
weights.push_back(m_wgt3);
weights.push_back(m_wgt4);
// Return vector
return weights;
}
/***********************************************************************//**
* @brief Print response table information
*
* @param[in] chatter Chattiness (defaults to NORMAL).
* @return String containing response table information.
***************************************************************************/
std::string GLATResponseTable::print(const GChatter& chatter) const
{
// Initialise result string
std::string result;
// Continue only if chatter is not silent
if (chatter != SILENT) {
// Append header
result.append("=== GLATResponseTable ===");
// Append information
result.append("\n"+gammalib::parformat("Number of energy bins") +
gammalib::str(nenergies()));
result.append("\n"+gammalib::parformat("Number of cos theta bins") +
gammalib::str(ncostheta()));
} // endif: chatter was not silent
// Return result
return result;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void GLATResponseTable::init_members(void)
{
// Initialise members
m_energy_num = 0;
m_ctheta_num = 0;
m_energy.clear();
m_logE.clear();
m_ctheta.clear();
m_energy_lo = NULL;
m_energy_hi = NULL;
m_ctheta_lo = NULL;
m_ctheta_hi = NULL;
m_last_energy = -1.0;
m_last_ctheta = -1.0;
m_inx1 = 0;
m_inx2 = 0;
m_inx3 = 0;
m_inx4 = 0;
m_wgt1 = 0.0;
m_wgt2 = 0.0;
m_wgt3 = 0.0;
m_wgt4 = 0.0;
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] table Response table.
***************************************************************************/
void GLATResponseTable::copy_members(const GLATResponseTable& table)
{
// Copy number of bins
m_energy_num = table.m_energy_num;
m_ctheta_num = table.m_ctheta_num;
m_energy = table.m_energy;
m_logE = table.m_logE;
m_ctheta = table.m_ctheta;
m_last_energy = table.m_last_energy;
m_last_ctheta = table.m_last_ctheta;
m_inx1 = table.m_inx1;
m_inx2 = table.m_inx2;
m_inx3 = table.m_inx3;
m_inx4 = table.m_inx4;
m_wgt1 = table.m_wgt1;
m_wgt2 = table.m_wgt2;
m_wgt3 = table.m_wgt3;
m_wgt4 = table.m_wgt4;
// Copy energy bins
if (m_energy_num > 0) {
m_energy_lo = new double[m_energy_num];
m_energy_hi = new double[m_energy_num];
for (int i=0; i < m_energy_num; ++i) {
m_energy_lo[i] = table.m_energy_lo[i];
m_energy_hi[i] = table.m_energy_hi[i];
}
}
// Copy cos theta bins
if (m_ctheta_num > 0) {
m_ctheta_lo = new double[m_ctheta_num];
m_ctheta_hi = new double[m_ctheta_num];
for (int i=0; i < m_ctheta_num; ++i) {
m_ctheta_lo[i] = table.m_ctheta_lo[i];
m_ctheta_hi[i] = table.m_ctheta_hi[i];
}
}
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void GLATResponseTable::free_members(void)
{
// Free memory
if (m_energy_lo != NULL) delete [] m_energy_lo;
if (m_energy_hi != NULL) delete [] m_energy_hi;
if (m_ctheta_lo != NULL) delete [] m_ctheta_lo;
if (m_ctheta_hi != NULL) delete [] m_ctheta_hi;
// Signal that memory is free
m_energy_lo = NULL;
m_energy_hi = NULL;
m_ctheta_lo = NULL;
m_ctheta_hi = NULL;
// Return
return;
}