/***************************************************************************
* GCTAResponseTable.cpp - CTA response table class *
* ----------------------------------------------------------------------- *
* copyright (C) 2012-2016 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 GCTAResponseTable.cpp
* @brief CTA response table class implementation
* @author Juergen Knoedlseder
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "GTools.hpp"
#include "GMath.hpp"
#include "GException.hpp"
#include "GFitsBinTable.hpp"
#include "GFitsTableFloatCol.hpp"
#include "GCTAResponseTable.hpp"
/* __ Method name definitions ____________________________________________ */
#define G_OPERATOR1 "GCTAResponseTable::operator()(double&)"
#define G_OPERATOR2 "GCTAResponseTable::operator()(double&, double&)"
#define G_OPERATOR3 "GCTAResponseTable::operator()(double&, double&,"\
" double&)"
#define G_ELEMENT_OPERATOR1 "GCTAResponseTable::operator()(int&)"
#define G_ELEMENT_OPERATOR2 "GCTAResponseTable::operator()(int&, int&)"
#define G_INX_OPERATOR1 "GCTAResponseTable::operator()(int&, double&)"
#define G_INX_OPERATOR2 "GCTAResponseTable::operator()(int&, double&,"\
" double&)"
#define G_INX_OPERATOR3 "GCTAResponseTable::operator()(int&, double&,"\
" double&, double&)"
#define G_TABLE "GCTAResponseTable::table(std::string&)"
#define G_SCALE "GCTAResponseTable::scale(int&, double&)"
#define G_AXIS "GCTAResponseTable::axis(std::string&)"
#define G_AXIS_BINS "GCTAResponseTable::axis_bins(int&)"
#define G_AXIS_LO_NAME "GCTAResponseTable::axis_lo_name(int&)"
#define G_AXIS_HI_NAME "GCTAResponseTable::axis_hi_name(int&)"
#define G_AXIS_LO_UNIT "GCTAResponseTable::axis_lo_unit(int&)"
#define G_AXIS_HI_UNIT "GCTAResponseTable::axis_hi_unit(int&)"
#define G_UNIT "GCTAResponseTable::unit(int&)"
#define G_AXIS_LO "GCTAResponseTable::axis_lo(int&, int&)"
#define G_AXIS_HI "GCTAResponseTable::axis_hi(int&, int&)"
#define G_AXIS_LINEAR "GCTAResponseTable::axis_linear(int&)"
#define G_AXIS_LOG10 "GCTAResponseTable::axis_log10(int&)"
#define G_AXIS_RADIANS "GCTAResponseTable::axis_radians(int&)"
#define G_AXIS_NODES "GCTAResponseTable::axis_nodes(int&)"
#define G_APPEND_AXIS "GCTAResponseTable::append_axis(std::vector&,"\
" std::vector&, std::string&, std::string&)"
#define G_APPEND_TABLE "GCTAResponseTable::append_table(std::string&,"\
" std::string&)"
#define G_READ "GCTAResponseTable::read(GFitsTable&)"
#define G_READ_COLNAMES "GCTAResponseTable::read_colnames(GFitsTable&)"
#define G_READ_AXES "GCTAResponseTable::read_axes(GFitsTable&)"
#define G_READ_TABLES "GCTAResponseTable::read_tables(GFitsTable&)"
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Debug definitions __________________________________________________ */
/* __ Constants __________________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
*
* Construct an empty CTA response table.
***************************************************************************/
GCTAResponseTable::GCTAResponseTable(void)
{
// Initialise class members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] table Response table.
*
* Construct a CTA response table by copying information from an existing
* response table. A deep copy is performed for construction, so that the
* original object may be destroyed after construction the new object.
***************************************************************************/
GCTAResponseTable::GCTAResponseTable(const GCTAResponseTable& table)
{
// Initialise class members
init_members();
// Copy members
copy_members(table);
// Return
return;
}
/***********************************************************************//**
* @brief FITS table constructor
*
* @param[in] hdu FITS table HDU.
*
* Construct a CTA response table from the information in a FITS table HDU.
* Refer to the read() method for details.
***************************************************************************/
GCTAResponseTable::GCTAResponseTable(const GFitsTable& hdu)
{
// Initialise class members
init_members();
// Read table information from HDU
read(hdu);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
*
* Destroys the CTA response table.
***************************************************************************/
GCTAResponseTable::~GCTAResponseTable(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] table Response table.
* @return Response table.
*
* Assigns a CTA response table to another object. The method is assigning
* a deep copy of the response table, so that the original object can be
* destroyed after assignment without any loss of information.
***************************************************************************/
GCTAResponseTable& GCTAResponseTable::operator=(const GCTAResponseTable& 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;
}
/***********************************************************************//**
* @brief Linear interpolation operator for 1D tables
*
* @param[in] arg Value.
* @return Vector of linearly interpolated response tables.
*
* @exception GException::invalid_value
* Invalid response table dimension.
*
* Evaluates all response tables at a given value. This method applies to
* one-dimensional tables only. If the tables have a different dimension
* an exception is thrown.
*
* The evaluation is performed by a linear interpolation of the table. If
* the specified value lies outside the range covered by the table, the
* table is linearly extrapolated from using either the first or the last
* two vector elements.
*
* @todo Write down formula.
***************************************************************************/
std::vector GCTAResponseTable::operator()(const double& arg) const
{
// Throw exception if table is not 1D
if (axes() != 1) {
std::string msg = "Invalid response table dimension "+
gammalib::str(axes())+" encountered. Response "
"table needs to be one-dimensional.";
throw GException::invalid_value(G_OPERATOR1, msg);
}
// Initialise result vector
std::vector result(m_ntables);
// Set indices and weighting factors for interpolation
update(arg);
// Perform 1D interpolation
for (int i = 0; i < m_ntables; ++i) {
result[i] = m_wgt_left * m_tables[i][m_inx_left] +
m_wgt_right * m_tables[i][m_inx_right];
}
// Return result vector
return result;
}
/***********************************************************************//**
* @brief Bilinear interpolation operator for 2D tables
*
* @param[in] arg1 Value for first axis.
* @param[in] arg2 Value for second axis.
* @return Vector of bilinearly interpolated response tables.
*
* @exception GException::invalid_value
* Invalid response table dimension.
*
* Evaluates all response tables at a given value. This method applies to
* two-dimensional tables only. If the tables have a different dimension
* an exception is thrown.
*
* The evaluation is performed by a bilinear interpolation of the table. If
* the specified value lies outside the range covered by the table, the
* table is linearly extrapolated from using either the first or the last
* two vector elements.
*
* @todo Write down formula.
***************************************************************************/
std::vector GCTAResponseTable::operator()(const double& arg1,
const double& arg2) const
{
// Throw exception if table is not 2D
if (axes() != 2) {
std::string msg = "Invalid response table dimension "+
gammalib::str(axes())+" encountered. Response "
"table needs to be two-dimensional.";
throw GException::invalid_value(G_OPERATOR2, msg);
}
// Initialise result vector
std::vector result(m_ntables);
// Set indices and weighting factors for interpolation
update(arg1, arg2);
// Perform 2D interpolation
for (int i = 0; i < m_ntables; ++i) {
result[i] = m_wgt1 * m_tables[i][m_inx1] +
m_wgt2 * m_tables[i][m_inx2] +
m_wgt3 * m_tables[i][m_inx3] +
m_wgt4 * m_tables[i][m_inx4];
}
// Return result vector
return result;
}
/***********************************************************************//**
* @brief Trilinear interpolation operator for 3D tables
*
* @param[in] arg1 Value for first axis.
* @param[in] arg2 Value for second axis.
* @param[in] arg3 Value for third axis.
* @return Vector of trilinearly interpolated response tables.
*
* @exception GException::invalid_value
* Invalid response table dimension.
*
* Evaluates all response tables at a given value. This method applies to
* three-dimensional tables only. If the tables have a different dimension
* an exception is thrown.
*
* The evaluation is performed by a trilinear interpolation of the table. If
* the specified value lies outside the range covered by the table, the
* table is linearly extrapolated from using either the first or the last
* two vector elements.
*
* @todo Write down formula.
***************************************************************************/
std::vector GCTAResponseTable::operator()(const double& arg1,
const double& arg2,
const double& arg3) const
{
// Throw exception if table is not 3D
if (axes() != 3) {
std::string msg = "Invalid response table dimension "+
gammalib::str(axes())+" encountered. Response "
"table needs to be tri-dimensional.";
throw GException::invalid_value(G_OPERATOR3, msg);
}
// Initialise result vector
std::vector result(m_ntables);
// Set indices and weighting factors for interpolation
update(arg1, arg2, arg3);
// Perform 3D interpolation
for (int i = 0; i < m_ntables; ++i) {
result[i] = m_wgt1 * m_tables[i][m_inx1] +
m_wgt2 * m_tables[i][m_inx2] +
m_wgt3 * m_tables[i][m_inx3] +
m_wgt4 * m_tables[i][m_inx4] +
m_wgt5 * m_tables[i][m_inx5] +
m_wgt6 * m_tables[i][m_inx6] +
m_wgt7 * m_tables[i][m_inx7] +
m_wgt8 * m_tables[i][m_inx8] ;
}
// Return result vector
return result;
}
/***********************************************************************//**
* @brief Table element access operator
*
* @param[in] element Table element index [0,...,elements()-1].
* @return Table element value.
*
* @exception GException::out_of_range
* Invalid element index.
*
* Returns the value of a table element for the first response table.
***************************************************************************/
double& GCTAResponseTable::operator()(const int& element)
{
// Optionally check if the index and element is valid
#if defined(G_RANGE_CHECK)
if (element < 0 || element >= elements()) {
throw GException::out_of_range(G_ELEMENT_OPERATOR1, "Element index",
element, elements());
}
#endif
// Return elements
return (m_tables[0][element]);
}
/***********************************************************************//**
* @brief Table element access operator (const version)
*
* @param[in] element Table element index [0,...,elements()-1].
* @return Table element value.
*
* @exception GException::out_of_range
* Invalid element index.
*
* Returns the value of a table element for the first response table.
***************************************************************************/
const double& GCTAResponseTable::operator()(const int& element) const
{
// Optionally check if the index and element is valid
#if defined(G_RANGE_CHECK)
if (element < 0 || element >= elements()) {
throw GException::out_of_range(G_ELEMENT_OPERATOR1, "Element index",
element, elements());
}
#endif
// Return elements
return (m_tables[0][element]);
}
/***********************************************************************//**
* @brief Table element access operator
*
* @param[in] table Table index [0,...,tables()-1].
* @param[in] element Element index [0,...,elements()-1].
* @return Response table element.
*
* @exception GException::out_of_range
* Invalid table or element index.
*
* Returns the value of a table element for the response table with index
* @p table.
***************************************************************************/
double& GCTAResponseTable::operator()(const int& table, const int& element)
{
// Optionally check if the index and element is valid
#if defined(G_RANGE_CHECK)
if (table < 0 || table >= tables()) {
throw GException::out_of_range(G_ELEMENT_OPERATOR2, "Table index",
table, tables());
}
if (element < 0 || element >= elements()) {
throw GException::out_of_range(G_ELEMENT_OPERATOR2, "Element index",
element, elements());
}
#endif
// Return elements
return (m_tables[table][element]);
}
/***********************************************************************//**
* @brief Table element access operator (const version)
*
* @param[in] table Table index [0,...,tables()-1].
* @param[in] element Element index [0,...,elements()-1].
* @return Response table element.
*
* @exception GException::out_of_range
* Invalid table or element index.
*
* Returns the value of a table element for the response table with index
* @p table.
***************************************************************************/
const double& GCTAResponseTable::operator()(const int& table, const int& element) const
{
// Optionally check if the index and element is valid
#if defined(G_RANGE_CHECK)
if (table < 0 || table >= tables()) {
throw GException::out_of_range(G_ELEMENT_OPERATOR2, "Table index",
table, tables());
}
if (element < 0 || element >= elements()) {
throw GException::out_of_range(G_ELEMENT_OPERATOR2, "Element index",
element, elements());
}
#endif
// Return elements
return (m_tables[table][element]);
}
/***********************************************************************//**
* @brief Linear interpolation operator for 1D tables
*
* @param[in] table Table index [0,...,tables()-1].
* @param[in] arg Value.
* @return Linearly interpolated response table.
*
* @exception GException::invalid_value
* Invalid response table dimension.
* @exception GException::out_of_range
* Invalid table index.
*
* Evaluates one response table at a given value. This method applies to
* one-dimensional response tables. If the table has a different dimension
* an exception is thrown.
*
* The evaluation is performed by a linear interpolation of the table values.
* If the specified value lies outside the range covered by the table, the
* table is linearly extrapolated from using either the first or the last
* two vector elements.
*
* @todo Write down formula.
***************************************************************************/
double GCTAResponseTable::operator()(const int& table, const double& arg) const
{
// Throw exception if table is not 1D
if (axes() != 1) {
std::string msg = "Invalid response table dimension "+
gammalib::str(axes())+" encountered. Response "
"table needs to be one-dimensional.";
throw GException::invalid_value(G_INX_OPERATOR1, msg);
}
// Throw exception if index is not valid
#if defined(G_RANGE_CHECK)
if (table < 0 || table >= tables()) {
throw GException::out_of_range(G_INX_OPERATOR1, "Table index",
table, tables());
}
#endif
// Set indices and weighting factors for interpolation
update(arg);
// Perform 1D interpolation
double result = m_wgt_left * m_tables[table][m_inx_left] +
m_wgt_right * m_tables[table][m_inx_right];
// Return result
return result;
}
/***********************************************************************//**
* @brief Bilinear interpolation operator for 2D tables
*
* @param[in] table Table index [0,...,tables()-1].
* @param[in] arg1 Value for first axis.
* @param[in] arg2 Value for second axis.
* @return Bilinearly interpolated response table.
*
* @exception GException::invalid_value
* Invalid response table dimension.
* @exception GException::out_of_range
* Invalid table index.
*
* Evaluates one response table at a given value. This method applies to
* two-dimensional response tables. If the table has a different dimension
* an exception is thrown.
*
* The evaluation is performed by a bilinear interpolation of the table
* values. If the specified value lies outside the range covered by the
* table, the table is linearly extrapolated from using either the first
* or the last two vector elements.
*
* @todo Write down formula.
***************************************************************************/
double GCTAResponseTable::operator()(const int& table,
const double& arg1,
const double& arg2) const
{
// Throw exception if table is not 2D
if (axes() != 2) {
std::string msg = "Invalid response table dimension "+
gammalib::str(axes())+" encountered. Response "
"table needs to be two-dimensional.";
throw GException::invalid_value(G_INX_OPERATOR2, msg);
}
// Throw exception if index is not valid
#if defined(G_RANGE_CHECK)
if (table < 0 || table >= tables()) {
throw GException::out_of_range(G_INX_OPERATOR2, "Table index",
table, tables());
}
#endif
// Set indices and weighting factors for interpolation
update(arg1, arg2);
// Perform 2D interpolation
double result = m_wgt1 * m_tables[table][m_inx1] +
m_wgt2 * m_tables[table][m_inx2] +
m_wgt3 * m_tables[table][m_inx3] +
m_wgt4 * m_tables[table][m_inx4];
// Return result
return result;
}
/***********************************************************************//**
* @brief Trilinear interpolation operator for 3D tables
*
* @param[in] table Table index [0,...,tables()-1].
* @param[in] arg1 Value for first axis.
* @param[in] arg2 Value for second axis.
* @param[in] arg3 Value for second axis.
* @return Trilinearly interpolated response table.
*
* @exception GException::invalid_value
* Invalid response table dimension.
* @exception GException::out_of_range
* Invalid table index.
*
* Evaluates one response table at a given value. This method applies to
* three-dimensional response tables. If the table has a different dimension
* an exception is thrown.
*
* The evaluation is performed by a trilinear interpolation of the table
* values. If the specified value lies outside the range covered by the
* table, the table is linearly extrapolated from using either the first
* or the last two vector elements.
*
* @todo Write down formula.
***************************************************************************/
double GCTAResponseTable::operator()(const int& table,
const double& arg1,
const double& arg2,
const double& arg3) const
{
// Throw exception if table is not 2D
if (axes() != 3) {
std::string msg = "Invalid response table dimension "+
gammalib::str(axes())+" encountered. Response "
"table needs to be three-dimensional.";
throw GException::invalid_value(G_INX_OPERATOR3, msg);
}
// Throw exception if index is not valid
#if defined(G_RANGE_CHECK)
if (table < 0 || table >= tables()) {
throw GException::out_of_range(G_INX_OPERATOR3, "Table index",
table, tables());
}
#endif
// Set indices and weighting factors for interpolation
update(arg1, arg2, arg3);
// Perform 3D interpolation
double result = m_wgt1 * m_tables[table][m_inx1] +
m_wgt2 * m_tables[table][m_inx2] +
m_wgt3 * m_tables[table][m_inx3] +
m_wgt4 * m_tables[table][m_inx4] +
m_wgt5 * m_tables[table][m_inx5] +
m_wgt6 * m_tables[table][m_inx6] +
m_wgt7 * m_tables[table][m_inx7] +
m_wgt8 * m_tables[table][m_inx8];
// Return result
return result;
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Clear response table
*
* Clears the response table.
***************************************************************************/
void GCTAResponseTable::clear(void)
{
// Free class members
free_members();
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Clone response table
*
* @return Deep copy of response table instance.
*
* Returns a pointer to a deep copy of the response table.
***************************************************************************/
GCTAResponseTable* GCTAResponseTable::clone(void) const
{
return new GCTAResponseTable(*this);
}
/***********************************************************************//**
* @brief Check whether a table exists
*
* @param[in] name Column name
* @return True if table exists.
*
* Checks whether a table exists.
***************************************************************************/
bool GCTAResponseTable::has_table(const std::string& name) const
{
// Initialise existence flag
bool exists = false;
// Loop over all tables to find index
int table = 0;
for (; table < tables(); ++table) {
if (m_colname_table[table] == name) {
exists = true;
break;
}
}
// Return existence flag
return exists;
}
/***********************************************************************//**
* @brief Check whether an axis exists
*
* @param[in] name Column name
* @return True if axis exists.
*
* Check whether an axis exists.
***************************************************************************/
bool GCTAResponseTable::has_axis(const std::string& name) const
{
// Initialise existence flag
bool exists = false;
// Build column names
std::string col_lo = name + "_LO";
std::string col_hi = name + "_HI";
// Loop over all axes to find index
int axis = 0;
for (; axis < axes(); ++axis) {
if ((axis_lo_name(axis) == col_lo) &&
(axis_hi_name(axis) == col_hi)) {
exists = true;
break;
}
}
// Return existence flag
return exists;
}
/***********************************************************************//**
* @brief Determine index of table
*
* @param[in] name Column name
* @return Table index.
*
* @exception GException::invalid_value
* Table not found.
*
* Determines the index of a table @p name. An exception is thrown if the
* table is not found.
***************************************************************************/
int GCTAResponseTable::table(const std::string& name) const
{
// Loop over all tables to find index
int table = 0;
for (; table < tables(); ++table) {
if (m_colname_table[table] == name) {
break;
}
}
// Throw an exception if table has not been found
if (table >= tables()) {
std::string msg = "Table \""+name+"\" not found. Please verify the "
"response table.";
throw GException::invalid_value(G_TABLE, msg);
}
// Return table index
return table;
}
/***********************************************************************//**
* @brief Return table unit
*
* @param[in] table Table index [0,...,tables()-1].
* @return Unit of table.
*
* @exception GException::out_of_range
* Table index out of range.
*
* Returns the unit of the table.
***************************************************************************/
const std::string& GCTAResponseTable::unit(const int& table) const
{
// Optionally check if the table index is valid
#if defined(G_RANGE_CHECK)
if (table < 0 || table >= tables()) {
throw GException::out_of_range(G_UNIT, "Table index", table, tables());
}
#endif
// Return units
return (m_units_table[table]);
}
/***********************************************************************//**
* @brief Scale table
*
* @param[in] table Table index [0,...,tables()-1].
* @param[in] scale Scaling factor.
*
* @exception GException::out_of_range
* Axis index out of range.
*
* Multiplies all values in a table by the scaling factor @p scale.
***************************************************************************/
void GCTAResponseTable::scale(const int& table, const double& scale)
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (table < 0 || table >= tables()) {
throw GException::out_of_range(G_SCALE, "Table index", table,
tables());
}
#endif
// Scale table values
for (int i = 0; i < m_nelements; ++i) {
m_tables[table][i] *= scale;
}
// Return
return;
}
/***********************************************************************//**
* @brief Determine index of an axis
*
* @param[in] name Column name
* @return Axis index.
*
* @exception GException::invalid_value
* Axis definition columns not found.
*
* Determines the index of an axis @p name, where the lower and upper bin
* edges of the axis are stored in the columns @p name_LO and @p name_HI.
* An exception is thrown if the axis is not found.
***************************************************************************/
int GCTAResponseTable::axis(const std::string& name) const
{
// Build column names
std::string col_lo = name + "_LO";
std::string col_hi = name + "_HI";
// Loop over all axes to find index
int axis = 0;
for (; axis < axes(); ++axis) {
if ((axis_lo_name(axis) == col_lo) &&
(axis_hi_name(axis) == col_hi)) {
break;
}
}
// Throw an exception if axis has not been found
if (axis >= axes()) {
std::string msg = "Axis definition columns for axis \""+name+"\" not "
"found. Please verify the axis names in the "
"response table.";
throw GException::invalid_value(G_AXIS, msg);
}
// Return axis index
return axis;
}
/***********************************************************************//**
* @brief Return number bins in an axis
*
* @param[in] axis Axis index [0,...,axes()-1].
* @return Number of bins along the specified axis.
*
* @exception GException::out_of_range
* Axis index out of range.
*
* Returns the number of bins in the specified @p axis.
***************************************************************************/
int GCTAResponseTable::axis_bins(const int& axis) const
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (axis < 0 || axis >= axes()) {
throw GException::out_of_range(G_AXIS_BINS, "Axis index", axis, axes());
}
#endif
// Return axis length
return (m_axis_lo[axis].size());
}
/***********************************************************************//**
* @brief Return lower bin boundary for bin in axis
*
* @param[in] axis Axis index [0,...,axes()-1].
* @param[in] bin Bin index [0,...,axis_bins(axis)-1].
* @return Lower bin boundary.
*
* @exception GException::out_of_range
* Axis or bin index out of range.
*
* Returns the lower boundary for a given @p bin of the specified @p axis.
***************************************************************************/
const double& GCTAResponseTable::axis_lo(const int& axis, const int& bin) const
{
// Check if index is valid
#if defined(G_RANGE_CHECK)
if (axis < 0 || axis >= axes()) {
throw GException::out_of_range(G_AXIS_LO, "Axis index", axis, axes());
}
if (bin < 0 || bin >= m_axis_lo[axis].size()) {
throw GException::out_of_range(G_AXIS_LO, "Bin index", bin,
m_axis_lo[axis].size());
}
#endif
// Return bin boundary
return (m_axis_lo[axis][bin]);
}
/***********************************************************************//**
* @brief Return upper bin boundary for bin in axis
*
* @param[in] axis Axis index [0,...,axes()-1].
* @param[in] bin Bin index [0,...,axis_bins(axis)-1].
* @return Upper bin boundary.
*
* @exception GException::out_of_range
* Axis or bin index out of range.
*
* Returns the upper boundary for a given @p bin of the specified @p axis.
***************************************************************************/
const double& GCTAResponseTable::axis_hi(const int& axis, const int& bin) const
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (axis < 0 || axis >= axes()) {
throw GException::out_of_range(G_AXIS_HI, "Axis index", axis, axes());
}
if (bin < 0 || bin >= m_axis_hi[axis].size()) {
throw GException::out_of_range(G_AXIS_HI, "Bin index", bin,
m_axis_hi[axis].size());
}
#endif
// Return bin boundary
return (m_axis_hi[axis][bin]);
}
/***********************************************************************//**
* @brief Return lower bin boundary FITS table column name for axis
*
* @param[in] axis Axis index [0,...,axes()-1].
* @return FITS table column name of lower bin boundary.
*
* @exception GException::out_of_range
* Axis index out of range.
*
* Returns the FITS table column name of the lower boundary for the specified
* @p axis.
***************************************************************************/
const std::string& GCTAResponseTable::axis_lo_name(const int& axis) const
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (axis < 0 || axis >= axes()) {
throw GException::out_of_range(G_AXIS_LO_NAME, "Axis index", axis, axes());
}
#endif
// Return units
return (m_colname_lo[axis]);
}
/***********************************************************************//**
* @brief Return upper bin boundary FITS table column name for axis
*
* @param[in] axis Axis index [0,...,axes()-1].
* @return FITS table column name of upper bin boundary.
*
* @exception GException::out_of_range
* Axis index out of range.
*
* Returns the FITS table column name of the upper boundary for the specified
* @p axis.
***************************************************************************/
const std::string& GCTAResponseTable::axis_hi_name(const int& axis) const
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (axis < 0 || axis >= axes()) {
throw GException::out_of_range(G_AXIS_HI_NAME, "Axis index", axis, axes());
}
#endif
// Return units
return (m_colname_hi[axis]);
}
/***********************************************************************//**
* @brief Return lower bin boundary unit for axis
*
* @param[in] axis Axis index [0,...,axes()-1].
* @return Unit of lower bin boundary.
*
* @exception GException::out_of_range
* Axis index out of range.
*
* Returns the unit of the lower boundary for the specified @p axis.
***************************************************************************/
const std::string& GCTAResponseTable::axis_lo_unit(const int& axis) const
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (axis < 0 || axis >= axes()) {
throw GException::out_of_range(G_AXIS_LO_UNIT, "Axis index", axis, axes());
}
#endif
// Return units
return (m_units_lo[axis]);
}
/***********************************************************************//**
* @brief Return upper bin boundary unit for axis
*
* @param[in] axis Axis index [0,...,axes()-1].
* @return Unit of upper bin boundary.
*
* @exception GException::out_of_range
* Axis index out of range.
*
* Returns the unit of the upper boundary for the specified @p axis.
***************************************************************************/
const std::string& GCTAResponseTable::axis_hi_unit(const int& axis) const
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (axis < 0 || axis >= axes()) {
throw GException::out_of_range(G_AXIS_HI_UNIT, "Axis index", axis, axes());
}
#endif
// Return units
return (m_units_hi[axis]);
}
/***********************************************************************//**
* @brief Set nodes for a linear axis
*
* @param[in] axis Axis index [0,...,axes()-1].
*
* @exception GException::out_of_range
* Axis index out of range.
*
* Set axis nodes so that each node is the linear mean of the lower and upper
* bin boundary, i.e.
* \f[ n_i = 0.5 \times ({\rm LO}_i + {\rm HI}_i) \f]
* where
* \f$n_i\f$ is node \f$i\f$,
* \f${\rm LO}_i\f$ is the lower bin boundary for bin \f$i\f$, and
* \f${\rm HI}_i\f$ is the upper bin boundary for bin \f$i\f$.
***************************************************************************/
void GCTAResponseTable::axis_linear(const int& axis)
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (axis < 0 || axis >= axes()) {
throw GException::out_of_range(G_AXIS_LINEAR, "Axis index", axis,
axes());
}
#endif
// Get number of bins
int bins = axis_bins(axis);
// Allocate node values
std::vector axis_nodes(bins);
// Compute nodes
for (int i = 0; i < bins; ++i) {
axis_nodes[i] = 0.5*(m_axis_lo[axis][i] + m_axis_hi[axis][i]);
}
// Set node array
m_axis_nodes[axis] = GNodeArray(axis_nodes);
// Return
return;
}
/***********************************************************************//**
* @brief Set nodes for a logarithmic (base 10) axis
*
* @param[in] axis Axis index [0,...,axes()-1].
*
* @exception GException::out_of_range
* Axis index out of range.
*
* Set axis nodes so that each node is the logarithmic mean of the lower and
* upper bin boundary, i.e.
* \f[ n_i = \log \sqrt{{\rm LO}_i \times {\rm HI}_i} \f]
* where
* \f$n_i\f$ is node \f$i\f$,
* \f${\rm LO}_i\f$ is the lower bin boundary for bin \f$i\f$, and
* \f${\rm HI}_i\f$ is the upper bin boundary for bin \f$i\f$.
*
* @todo Check that none of the axis boundaries is non-positive.
***************************************************************************/
void GCTAResponseTable::axis_log10(const int& axis)
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (axis < 0 || axis >= axes()) {
throw GException::out_of_range(G_AXIS_LOG10, "Axis index", axis,
axes());
}
#endif
// Get number of bins
int bins = axis_bins(axis);
// Allocate node values
std::vector axis_nodes(bins);
// Compute nodes
for (int i = 0; i < bins; ++i) {
axis_nodes[i] = std::log10(std::sqrt(m_axis_lo[axis][i] *
m_axis_hi[axis][i]));
}
// Set node array
m_axis_nodes[axis] = GNodeArray(axis_nodes);
// Return
return;
}
/***********************************************************************//**
* @brief Set nodes for a radians axis
*
* @param[in] axis Axis index [0,...,axes()-1].
*
* @exception GException::out_of_range
* Axis index out of range.
*
* Set axis nodes so that each node is the lo mean of the lower and upper
* bin boundary in radians, i.e.
* \f[ n_i = \frac{\pi}{360} \times ({\rm LO}_i + {\rm HI}_i) \f]
* where
* \f$n_i\f$ is node \f$i\f$,
* \f${\rm LO}_i\f$ is the lower bin boundary for bin \f$i\f$, and
* \f${\rm HI}_i\f$ is the upper bin boundary for bin \f$i\f$.
***************************************************************************/
void GCTAResponseTable::axis_radians(const int& axis)
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (axis < 0 || axis >= axes()) {
throw GException::out_of_range(G_AXIS_RADIANS, "Axis index", axis,
axes());
}
#endif
// Get number of bins
int bins = axis_bins(axis);
// Allocate node values
std::vector axis_nodes(bins);
// Compute nodes
for (int i = 0; i < bins; ++i) {
axis_nodes[i] = 0.5*(m_axis_lo[axis][i] + m_axis_hi[axis][i]) *
gammalib::deg2rad;
}
// Set node array
m_axis_nodes[axis] = GNodeArray(axis_nodes);
// Return
return;
}
/***********************************************************************//**
* @brief Append an axis to the response table
*
* @param[in] axis_lo Lower axis boundaries.
* @param[in] axis_hi Upper axis boundaries.
* @param[in] name Axis name.
* @param[in] unit Axis unit.
*
* @exception GException::invalid_argument
* Incompatible axis boundaries.
*
* Append an axis to the response table.
***************************************************************************/
void GCTAResponseTable::append_axis(const std::vector& axis_lo,
const std::vector& axis_hi,
const std::string& name,
const std::string& unit)
{
// Throw an exception if the length of axis_lo and axis_hi are different
if (axis_lo.size() != axis_hi.size()) {
std::string msg = "Number of elements in lower axis boundaries "
"mismatches the number of elements in upper axis "
"boundaries ("+gammalib::str(axis_lo.size())+
" != "+gammalib::str(axis_hi.size())+").";
throw GException::invalid_argument(G_APPEND_AXIS, msg);
}
// Set axis names
std::string name_lo = name + "_LO";
std::string name_hi = name + "_HI";
// Append axis
m_colname_lo.push_back(name_lo);
m_colname_hi.push_back(name_hi);
m_axis_lo.push_back(axis_lo);
m_axis_hi.push_back(axis_hi);
m_units_lo.push_back(unit);
m_units_hi.push_back(unit);
// Increment number of axes
m_naxes++;
// Compute the cube size
m_nelements = axis_bins(0);
for (int i = 1; i < axes(); ++i) {
m_nelements *= axis_bins(i);
}
// Return
return;
}
/***********************************************************************//**
* @brief Append table to response table
*
* @param[in] name Table name.
* @param[in] unit Table unit.
*
* @exception GException::invalid_value
* No axes have been specified.
*
* Append a table to the response table. The number of elements in the table
* is the product of the length of all axes. All elements are set to zero by
* default.
***************************************************************************/
void GCTAResponseTable::append_table(const std::string& name,
const std::string& unit)
{
// Throw an exception message when m_nelement is zero
if (m_nelements == 0) {
std::string msg = "No axis columns have been defined. Please append "
"axis columns before appending table columns.";
throw GException::invalid_value(G_APPEND_TABLE, msg);
}
// Append table column name and unit
m_colname_table.push_back(name);
m_units_table.push_back(unit);
// Initialise empty table column
std::vector table(m_nelements, 0.0);
// Append column
m_tables.push_back(table);
// Increment number of table columns
m_ntables++;
// Return
return;
}
/***********************************************************************//**
* @brief Return axis nodes
*
* @param[in] axis Axis index [0,...,axes()-1].
* @return Node array for axis.
*
* @exception GException::out_of_range
* Axis index out of range.
*
* Returns the node array of the specified @p axis.
***************************************************************************/
const GNodeArray& GCTAResponseTable::axis_nodes(const int& axis) const
{
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (axis < 0 || axis >= axes()) {
throw GException::out_of_range(G_AXIS_NODES, "Axis index", axis,
axes());
}
#endif
// Return node array
return (m_axis_nodes[axis]);
}
/***********************************************************************//**
* @brief Read response table from FITS table HDU
*
* @param[in] table FITS table.
*
* Reads CTA response table information from a FITS table. The FITS table
* is expected to have a single row, and axes and table information are
* found in vector columns. Axes information is expected to be placed
* before table information.
*
* Each axis is defined by two vector columns of equal width, describing the
* lower and upper limits for each bin. The column names for this boundary
* information terminates by "_LO" and "HI", respectively (upper case). It
* is expected that the "_LO" column preceeds the "_HI" column.
*
* Following the axes columns are table columns of equal width. The width
* of each table column is given by the product of the lengths of all
* axes. It is furthermore expected that the first axis is the most rapidely
* varying index of the vector.
***************************************************************************/
void GCTAResponseTable::read(const GFitsTable& table)
{
// Clear instance
clear();
// Read column names
read_colnames(table);
// Read axes
read_axes(table);
// Read tables
read_tables(table);
// Return
return;
}
/***********************************************************************//**
* @brief Write response table into FITS table HDU
*
* @param[in] table FITS table.
*
* Writes the response table in a FITS table HDU.
*
* Axes columns are written in floating point columns with the suffixes "_LO"
* and "_HI". After the axes columns the response tables are written in
* floating point columns.
***************************************************************************/
void GCTAResponseTable::write(GFitsTable& table) const
{
// Initialise dimension vector
std::vector dim;
// Loop over all response table axes
for (int iaxis = 0; iaxis < m_naxes; ++iaxis) {
// Create axis columns
GFitsTableFloatCol col_lo(m_colname_lo[iaxis], 1,
m_axis_lo[iaxis].size());
GFitsTableFloatCol col_hi(m_colname_hi[iaxis], 1,
m_axis_hi[iaxis].size());
// Loop through all elements in this axis column
for (int i = 0; i < m_axis_lo[iaxis].size(); ++i) {
col_lo(0,i) = m_axis_lo[iaxis][i];
col_hi(0,i) = m_axis_hi[iaxis][i];
}
// Set column units
col_lo.unit(m_units_lo[iaxis]);
col_hi.unit(m_units_hi[iaxis]);
// Append column to FITS table
table.append(col_lo);
table.append(col_hi);
// Append dimension
dim.push_back(axis_bins(iaxis));
} // endif: looped over all axes in response table
// Loop over all tables
for (int itable = 0; itable < m_ntables; ++itable) {
// Create table column
GFitsTableFloatCol col_table(m_colname_table[itable], 1,
m_tables[itable].size());
// Loop through elements in this table column
for (int i = 0; i < m_tables[itable].size() ; ++i) {
col_table(0,i) = m_tables[itable][i];
}
// Set column unit
col_table.unit(m_units_table[itable]);
// Set column dimension
col_table.dim(dim);
// Append column to table
table.append(col_table);
} // endfor: looped over all axes in response table
// Return
return;
}
/***********************************************************************//**
* @brief Print response table information
*
* @param[in] chatter Chattiness (defaults to NORMAL).
* @return String containing response table information.
*
* Puts CTA response table information into a std::string object for
* printing.
***************************************************************************/
std::string GCTAResponseTable::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("=== GCTAResponseTable ===");
// Append information
result.append("\n"+gammalib::parformat("Dimension") +
gammalib::str(axes()));
// Append axes information
for (int i = 0; i < axes(); ++i) {
result.append("\n"+gammalib::parformat("Axis " +
gammalib::str(i)));
result.append(gammalib::str(axis_bins(i)));
result.append(" (");
result.append(m_colname_lo[i]);
if (!m_units_lo[i].empty()) {
result.append(" ["+m_units_lo[i]+"]");
}
result.append(", ");
result.append(m_colname_hi[i]);
if (!m_units_hi[i].empty()) {
result.append(" ["+m_units_hi[i]+"]");
}
result.append(")");
}
// Append table information
for (int i = 0; i < tables(); ++i) {
result.append("\n"+gammalib::parformat("Table " +
gammalib::str(i)));
result.append(m_colname_table[i]);
if (!m_units_table[i].empty()) {
result.append(" ["+m_units_table[i]+"]");
}
}
} // endif: chatter was not silent
// Return result
return result;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise response table members
*
* Initialises all members of the response table.
***************************************************************************/
void GCTAResponseTable::init_members(void)
{
// Initialise members
m_naxes = 0;
m_ntables = 0;
m_nelements = 0;
m_colname_lo.clear();
m_colname_hi.clear();
m_colname_table.clear();
m_axis_lo.clear();
m_axis_hi.clear();
m_units_lo.clear();
m_units_hi.clear();
m_units_table.clear();
m_axis_nodes.clear();
m_tables.clear();
// Initialise cache
m_inx_left = 0;
m_inx_right = 0;
m_wgt_left = 0.0;
m_wgt_right = 0.0;
m_inx1 = 0;
m_inx2 = 0;
m_inx3 = 0;
m_inx4 = 0;
m_inx5 = 0;
m_inx6 = 0;
m_inx7 = 0;
m_inx8 = 0;
m_wgt1 = 0.0;
m_wgt2 = 0.0;
m_wgt3 = 0.0;
m_wgt4 = 0.0;
m_wgt5 = 0.0;
m_wgt6 = 0.0;
m_wgt7 = 0.0;
m_wgt8 = 0.0;
// Return
return;
}
/***********************************************************************//**
* @brief Copy response table members
*
* @param[in] table Response table.
*
* Copies all response table members.
***************************************************************************/
void GCTAResponseTable::copy_members(const GCTAResponseTable& table)
{
// Copy number of bins
m_naxes = table.m_naxes;
m_ntables = table.m_ntables;
m_nelements = table.m_nelements;
m_colname_lo = table.m_colname_lo;
m_colname_hi = table.m_colname_hi;
m_colname_table = table.m_colname_table;
m_axis_lo = table.m_axis_lo;
m_axis_hi = table.m_axis_hi;
m_units_lo = table.m_units_lo;
m_units_hi = table.m_units_hi;
m_units_table = table.m_units_table;
m_axis_nodes = table.m_axis_nodes;
m_tables = table.m_tables;
// Copy cache
m_inx_left = table.m_inx_left;
m_inx_right = table.m_inx_right;
m_wgt_left = table.m_wgt_left;
m_wgt_right = table.m_wgt_right;
m_inx1 = table.m_inx1;
m_inx2 = table.m_inx2;
m_inx3 = table.m_inx3;
m_inx4 = table.m_inx4;
m_inx5 = table.m_inx5;
m_inx6 = table.m_inx6;
m_inx7 = table.m_inx7;
m_inx8 = table.m_inx8;
m_wgt1 = table.m_wgt1;
m_wgt2 = table.m_wgt2;
m_wgt3 = table.m_wgt3;
m_wgt4 = table.m_wgt4;
m_wgt5 = table.m_wgt5;
m_wgt6 = table.m_wgt6;
m_wgt7 = table.m_wgt7;
m_wgt8 = table.m_wgt8;
// Return
return;
}
/***********************************************************************//**
* @brief Delete response table members
*
* De-allocates any memory that was allocated by the response table.
***************************************************************************/
void GCTAResponseTable::free_members(void)
{
// Return
return;
}
/***********************************************************************//**
* @brief Read column names from FITS HDU
*
* @param[in] hdu Response table HDU.
*
* @exception GException::invalid_value
* Invalid response table format encountered.
*
* Read the response table column names from the HDU. Column names
* terminating with "_LO" and "_HI" define the table axes, while all other
* column names define response tables. It is assumed that table axes are
* given in subsequent order, with the first column corresponding to the
* lower bin boundaries of the first axis. Lower bin boundaries are indicated
* by the "_LO" termination. Upper bin boundaries are assumed to follow
* immediately the lower bin boundaires and are designated by the "_HI"
* termination.
*
* This method sets the following members:
* m_colname_lo - Column names of lower boundaries
* m_colname_hi - Column names of upper boundaries
* m_colname_table - Column names of tables
* m_naxes - Number of axes
* m_ntables - Number of tables
*
* @todo Implement exceptions for invalid HDU format
***************************************************************************/
void GCTAResponseTable::read_colnames(const GFitsTable& hdu)
{
// Clear column name arrays
m_naxes = 0;
m_ntables = 0;
m_colname_lo.clear();
m_colname_hi.clear();
m_colname_table.clear();
// Initialise search mode. There are three search modes:
// 0 - we're looking for the next axis by searching for a column
// terminating with "_LO"
// 1 - we're looking for the upper boundary of an axis, terminating
// with "_HI"
// 2 - we're looking for a table column
int mode = 0;
std::string lo_column;
std::string next_column;
// Extract column names for all axes
for (int i = 0; i < hdu.ncols(); ++i) {
// Get column name and unit
std::string colname = hdu[i]->name();
// If we search for a "_LO" column, check if we have one. If one
// is found, change the search mode to 1 and set the expected name
// for the "_HI" column. If none is found, change the search
// mode to 2 since from now on we should only have table
// columns.
if (mode == 0) {
size_t pos = colname.rfind("_LO");
if (pos != std::string::npos) {
mode = 1;
lo_column = colname;
next_column = colname.substr(0, pos) + "_HI";
}
else {
if (colname.rfind("_HI") != std::string::npos) {
std::string msg = "Column \""+colname+"\" encountered "
"without preceeding \"_LO\" column. "
"Please correct response table format.";
throw GException::invalid_value(G_READ_COLNAMES, msg);
}
else {
mode = 2;
m_colname_table.push_back(colname);
}
}
}
// If we search for a "_HI" column, check if we have the
// expected column name. If this is the case, switch back to
// search mode 0 to get the next "_LO" column. Otherwise
// throw an exception.
else if (mode == 1) {
if (colname == next_column) {
mode = 0;
m_colname_lo.push_back(lo_column);
m_colname_hi.push_back(next_column);
}
else {
std::string msg = "Expected column \""+next_column+"\" not "
"found. The axis upper bin boundary "
"column has to follow immediately the "
"lower bin boundary column.";
throw GException::invalid_value(G_READ_COLNAMES, msg);
}
}
// If we search for a table column, make sure that we have
// neither a "_LO" nor a "_HI" column.
else {
if (colname.rfind("_LO") != std::string::npos) {
std::string msg = "Column \""+colname+"\" encountered while "
"searching for table columns. All "
"lower bin boundary columns have to be "
"placed before the table columns.";
throw GException::invalid_value(G_READ_COLNAMES, msg);
}
else if (colname.rfind("_HI") != std::string::npos) {
std::string msg = "Column \""+colname+"\" encountered while "
"searching for table columns. All "
"upper bin boundary columns have to be "
"placed before the table columns.";
throw GException::invalid_value(G_READ_COLNAMES, msg);
}
else {
m_colname_table.push_back(colname);
}
}
} // endfor: looped over all columns
// Store number of axes
m_naxes = m_colname_lo.size();
// Store number of tables
m_ntables = m_colname_table.size();
// Return
return;
}
/***********************************************************************//**
* @brief Read axes definitions from FITS HDU
*
* @param[in] hdu Response table HDU.
*
* @exception GException::invalid_value
* Incompatible axes columns encoutered.
*
* Reads the lower and upper boundaries for all response table axes from the
* FITS HDU. The method verifies if the lower and upper boundary axes have
* the same vector dimensions.
*
* The method also allocates the nodes for each axis, assuming that axis will
* be used linearily, where each node is given by the linear mean of the
* lower and upper boundary. In case that the axis should be used
* logarithmically, the method axis_log10() can be used to change to a
* logarithmic scale. The axis_linear() can be used to switch back to a
* linear scale.
*
* This method sets the following members:
* m_axis_lo - Axes lower boundaries
* m_axis_hi - Axes upper boundaries
* m_axis_nodes - Axes mean values
***************************************************************************/
void GCTAResponseTable::read_axes(const GFitsTable& hdu)
{
// Clear axes arrays
m_axis_lo.clear();
m_axis_hi.clear();
m_axis_nodes.clear();
// Loop over all dimensions
for (int i = 0; i < axes(); ++i) {
// Get pointers to table columns
const GFitsTableCol* col_lo = hdu[m_colname_lo[i]];
const GFitsTableCol* col_hi = hdu[m_colname_hi[i]];
// Extract number of bins. Make sure that both columns have the
// same number of bins
int num = col_lo->number();
if (num != col_hi->number()) {
std::string msg = "Axis lower bin boundary column \""+
m_colname_lo[i]+"\" contains "+
gammalib::str(num)+" elements while upper "
"bin boundary column \""+m_colname_hi[i]+
"\" contains "+gammalib::str(col_hi->number())+
" elements. Both columns need to contain the "
"same number of elements.";
throw GException::invalid_value(G_READ_AXES, msg);
}
// Initialise axis and node arrays
std::vector axis_lo(num);
std::vector axis_hi(num);
std::vector axis_nodes(num);
// Copy axis information into arrays
for (int k = 0; k < num; ++k) {
axis_lo[k] = col_lo->real(0,k);
axis_hi[k] = col_hi->real(0,k);
axis_nodes[k] = 0.5*(axis_lo[k] + axis_hi[k]);
}
// Push axis array on storage
m_axis_lo.push_back(axis_lo);
m_axis_hi.push_back(axis_hi);
// Push units on storage
m_units_lo.push_back(col_lo->unit());
m_units_hi.push_back(col_hi->unit());
// Create node array
m_axis_nodes.push_back(GNodeArray(axis_nodes));
} // endfor: looped over all dimensions
// Return
return;
}
/***********************************************************************//**
* @brief Read tables
*
* @param[in] hdu Response table HDU.
*
* @exception GException::invalid_value
* Table vector of bad size encountered.
*
* Reads the tables from the FITS HDU.
*
* The method also sets m_nelements which gives the number of elements in a
* response table.
***************************************************************************/
void GCTAResponseTable::read_tables(const GFitsTable& hdu)
{
// Clear tables
m_tables.clear();
// Compute expected cube size
m_nelements = axis_bins(0);
for (int i = 1; i < axes(); ++i) {
m_nelements *= axis_bins(i);
}
// Loop over all tables
for (int i = 0; i < tables(); ++i) {
// Get pointer to table column
const GFitsTableCol* col = hdu[m_colname_table[i]];
// Extract number of bins. Verify that the number of bins
// corresponds to the expectation.
int num = col->number();
if (num != m_nelements) {
std::string msg = "Table column \""+m_colname_table[i]+"\" "
"contains "+gammalib::str(num)+" elements "
"while "+gammalib::str(m_nelements)+" "
"elements were expected from the axis "
"definitions. Please check the response "
"table for consistency.";
throw GException::invalid_value(G_READ_TABLES, msg);
}
// Initialise table
std::vector table(num);
// Copy table values
for (int k = 0; k < num; ++k) {
table[k] = col->real(0,k);
}
// Push table into storage
m_tables.push_back(table);
// Push units on storage
m_units_table.push_back(col->unit());
} // endfor: looped over all tables
// Return
return;
}
/***********************************************************************//**
* @brief Update 1D cache
*
* @param[in] arg Argument.
*
* Updates the 1D interpolation cache. The interpolation cache is composed
* of two indices and weights that define 2 data values of the 2D table
* that are used for linear interpolation.
*
* @todo Write down formula
***************************************************************************/
void GCTAResponseTable::update(const double& arg) const
{
// Get pointer to node array
const GNodeArray* nodes = &(m_axis_nodes[0]);
// Set value for node array
nodes->set_value(arg);
// Set indices and weighting factors for interpolation
m_inx_left = nodes->inx_left();
m_inx_right = nodes->inx_right();
m_wgt_left = nodes->wgt_left();
m_wgt_right = nodes->wgt_right();
// Return
return;
}
/***********************************************************************//**
* @brief Update 2D cache
*
* @param[in] arg1 Argument for first axis.
* @param[in] arg2 Argument for second axis.
*
* Updates the 2D interpolation cache. The interpolation cache is composed
* of four indices and weights that define 4 data values of the 2D table
* that are used for bilinear interpolation.
*
* @todo Write down formula
***************************************************************************/
void GCTAResponseTable::update(const double& arg1, const double& arg2) const
{
// Get pointers to node arrays
const GNodeArray* nodes1 = &(m_axis_nodes[0]);
const GNodeArray* nodes2 = &(m_axis_nodes[1]);
// Set values for node arrays
nodes1->set_value(arg1);
nodes2->set_value(arg2);
// Compute offsets
int size1 = axis_bins(0);
int offset_left = nodes2->inx_left() * size1;
int offset_right = nodes2->inx_right() * size1;
// Set indices for bi-linear interpolation
m_inx1 = nodes1->inx_left() + offset_left;
m_inx2 = nodes1->inx_left() + offset_right;
m_inx3 = nodes1->inx_right() + offset_left;
m_inx4 = nodes1->inx_right() + offset_right;
// Set weighting factors for bi-linear interpolation
m_wgt1 = nodes1->wgt_left() * nodes2->wgt_left();
m_wgt2 = nodes1->wgt_left() * nodes2->wgt_right();
m_wgt3 = nodes1->wgt_right() * nodes2->wgt_left();
m_wgt4 = nodes1->wgt_right() * nodes2->wgt_right();
// Return
return;
}
/***********************************************************************//**
* @brief Update 3D cache
*
* @param[in] arg1 Argument for first axis.
* @param[in] arg2 Argument for second axis.
* @param[in] arg3 Argument for third axis.
*
* Updates the 3D interpolation cache. The interpolation cache is composed
* of four indices and weights that define 4 data values of the 2D table
* that are used for bilinear interpolation.
*
* @todo Write down formula
***************************************************************************/
void GCTAResponseTable::update(const double& arg1, const double& arg2,
const double& arg3) const
{
// Get pointers to node arrays (circumvent const correctness)
const GNodeArray* nodes1 = &(m_axis_nodes[0]);
const GNodeArray* nodes2 = &(m_axis_nodes[1]);
const GNodeArray* nodes3 = &(m_axis_nodes[2]);
// Set values for node arrays
nodes1->set_value(arg1);
nodes2->set_value(arg2);
nodes3->set_value(arg3);
// Compute offsets
int size1 = axis_bins(0);
int size2 = axis_bins(1);
int offset_left_2 = nodes2->inx_left() * size1;
int offset_right_2 = nodes2->inx_right() * size1;
int offset_left_3 = nodes3->inx_left() * size1 * size2;
int offset_right_3 = nodes3->inx_right() * size1 * size2;
// Set indices for tri-linear interpolation
m_inx1 = nodes1->inx_left() + offset_left_2 + offset_left_3 ;
m_inx2 = nodes1->inx_left() + offset_left_2 + offset_right_3;
m_inx3 = nodes1->inx_left() + offset_right_2 + offset_left_3 ;
m_inx4 = nodes1->inx_left() + offset_right_2 + offset_right_3;
m_inx5 = nodes1->inx_right() + offset_left_2 + offset_left_3 ;
m_inx6 = nodes1->inx_right() + offset_left_2 + offset_right_3;
m_inx7 = nodes1->inx_right() + offset_right_2 + offset_left_3 ;
m_inx8 = nodes1->inx_right() + offset_right_2 + offset_right_3;
// Set weighting factors for tri-linear interpolation
m_wgt1 = nodes1->wgt_left() * nodes2->wgt_left() * nodes3->wgt_left();
m_wgt2 = nodes1->wgt_left() * nodes2->wgt_left() * nodes3->wgt_right();
m_wgt3 = nodes1->wgt_left() * nodes2->wgt_right() * nodes3->wgt_left();
m_wgt4 = nodes1->wgt_left() * nodes2->wgt_right() * nodes3->wgt_right();
m_wgt5 = nodes1->wgt_right() * nodes2->wgt_left() * nodes3->wgt_left();
m_wgt6 = nodes1->wgt_right() * nodes2->wgt_left() * nodes3->wgt_right();
m_wgt7 = nodes1->wgt_right() * nodes2->wgt_right() * nodes3->wgt_left();
m_wgt8 = nodes1->wgt_right() * nodes2->wgt_right() * nodes3->wgt_right();
// Return
return;
}