/***************************************************************************
* GPha.cpp - XSPEC Pulse Height Analyzer class *
* ----------------------------------------------------------------------- *
* copyright (C) 2013-2017 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 GPha.cpp
* @brief XSPEC Pulse Height Analyzer class implementation
* @author Juergen Knoedlseder
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include "GPha.hpp"
#include "GException.hpp"
#include "GTools.hpp"
#include "GEnergy.hpp"
#include "GFits.hpp"
#include "GFitsTable.hpp"
#include "GFitsBinTable.hpp"
#include "GFitsTableShortCol.hpp"
#include "GFitsTableFloatCol.hpp"
/* __ Method name definitions ____________________________________________ */
#define G_AT "GPha::at(int&)"
#define G_READ "GPha::read(GFitsTable*)"
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Debug definitions __________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
GPha::GPha(void)
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief File constructor
*
* @param[in] filename File name.
***************************************************************************/
GPha::GPha(const GFilename& filename)
{
// Initialise members
init_members();
// Load PHA file
load(filename);
// Return
return;
}
/***********************************************************************//**
* @brief Energy boundary constructor
*
* @param[in] ebds Energy boundaries.
***************************************************************************/
GPha::GPha(const GEbounds& ebds)
{
// Initialise members
init_members();
// Set energy boundaries
m_ebounds = ebds;
// Initialize spectrum
m_counts.assign(ebds.size(), 0.0);
// Return
return;
}
/***********************************************************************//**
* @brief Energy bins constructor
*
* @param[in] bins Number of energy bins.
***************************************************************************/
GPha::GPha(const int& bins)
{
// Initialise members
init_members();
// Initialize spectrum
m_counts.assign(bins, 0.0);
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] pha Pulse Height Analyzer spectrum.
***************************************************************************/
GPha::GPha(const GPha& pha)
{
// Initialise members
init_members();
// Copy members
copy_members(pha);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
GPha::~GPha(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] pha Pulse Height Analyzer spectrum.
* @return Pulse Height Analyzer spectrum.
***************************************************************************/
GPha& GPha::operator=(const GPha& pha)
{
// Execute only if object is not identical
if (this != &pha) {
// Free members
free_members();
// Initialise members
init_members();
// Copy members
copy_members(pha);
} // endif: object was not identical
// Return
return *this;
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Clear object.
*
* Reset object to a clean initial state.
***************************************************************************/
void GPha::clear(void)
{
// Free memory
free_members();
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Clone object
*
* @return Pulse Height Analyzer spectrum.
***************************************************************************/
GPha* GPha::clone(void) const
{
// Clone client
return new GPha(*this);
}
/***********************************************************************//**
* @brief Return content of spectral bin
*
* @param[in] index Bin index [0,...,size()-1].
*
* @exception GException::out_of_range
* Spectral bin index is out of range.
*
* Returns reference to content of spectral bin with specified @p index.
***************************************************************************/
double& GPha::at(const int& index)
{
// Raise exception if index is out of range
if (index < 0 || index >= size()) {
throw GException::out_of_range(G_AT, index, 0, size()-1);
}
// Return reference
return (m_counts[index]);
}
/***********************************************************************//**
* @brief Return content of spectral bin (const version)
*
* @param[in] index Bin index [0,...,size()-1].
*
* @exception GException::out_of_range
* Spectral bin index is out of range.
*
* Returns reference to content of spectral bin with specified @p index.
***************************************************************************/
const double& GPha::at(const int& index) const
{
// Raise exception if index is out of range
if (index < 0 || index >= size()) {
throw GException::out_of_range(G_AT, index, 0, size()-1);
}
// Return reference
return (m_counts[index]);
}
/***********************************************************************//**
* @brief Number of counts in spectrum
*
* Returns the number of counts in the spectrum.
***************************************************************************/
double GPha::counts(void) const
{
// Initialise counts
double counts = 0.0;
// Compute content
for (int i = 0; i < m_counts.size(); ++i) {
counts += m_counts[i];
}
// Return counts
return counts;
}
/***********************************************************************//**
* @brief Fill spectrum with a value.
*
* @param[in] energy Energy.
* @param[in] value Fill value (defaults to 1.0).
*
* Fills the specified @p value at a given @p energy in the spectrum.
***************************************************************************/
void GPha::fill(const GEnergy& energy, const double& value)
{
// Get index
int index = m_ebounds.index(energy);
// If index is not valid, then check for underflow, overflow or outflow
if (index == -1) {
if (energy < m_ebounds.emin()) {
m_underflow += value;
}
else if (energy >= m_ebounds.emax()) {
m_overflow += value;
}
else {
m_outflow += value;
}
}
// ... otherwise fill the histogram
else {
m_counts[index] += value;
}
// Return
return;
}
/***********************************************************************//**
* @brief Load Pulse Height Analyzer spectrum
*
* @param[in] filename File name.
*
* Loads the Pulse Height Analyzer spectrum from the `SPECTRUM` extension
* of the FITS file. If the file contains also an `EBOUNDS` extension the
* energy boundaries of all Pulse Height Analyzer channels are also loaded.
***************************************************************************/
void GPha::load(const GFilename& filename)
{
// Clear spectrum
clear();
// Open FITS file (without extension name as the user is not allowed
// to modify the extension names)
GFits fits(filename.url());
// Get PHA table
const GFitsTable& pha = *fits.table(gammalib::extname_pha);
// Read PHA data
read(pha);
// Optionally read energy boundaries
if (fits.contains(gammalib::extname_ebounds)) {
// Get energy boundary table
const GFitsTable& ebounds = *fits.table(gammalib::extname_ebounds);
// Read energy boundaries
m_ebounds.read(ebounds);
} // endif: had energy boundary table
// Close FITS file
fits.close();
// Store filename
m_filename = filename.url();
// Return
return;
}
/***********************************************************************//**
* @brief Save Pulse Height Analyzer spectrum
*
* @param[in] filename File name.
* @param[in] clobber Overwrite existing file?
*
* Saves the Pulse Height Analyzer spectrum and energy boundaries into a
* FITS file. If a file with the given @p filename does not yet exist it
* will be created. If the file exists it can be overwritten if the
* @p clobber flag is set to `true`. Otherwise an exception is thrown.
*
* The method will save two binary FITS tables into the FITS file: a
* `SPECTRUM` extension that contains the channel values of the Pulse
* Height Analyzer spectrum and an `EBOUNDS` extension that contains the
* energy boundaries for all channels.
***************************************************************************/
void GPha::save(const GFilename& filename, const bool& clobber) const
{
// Create FITS file
GFits fits;
// Write PHA into file
write(fits);
// Save to file (without extension name since the requested extension
// may not yet exist in the file)
fits.saveto(filename.url(), clobber);
// Store filename
m_filename = filename.url();
// Return
return;
}
/***********************************************************************//**
* @brief Read Pulse Height Analyzer spectrum
*
* @param[in] table FITS table.
*
* @exception GException::invalid_value
* Mismatch between PHA file and energy boundaries.
*
* Reads the Pulse Height Analyzer spectrum from a FITS table. The channel
* values are expected in the `COUNTS` column of the table. All other
* columns are ignored.
*
* See
* https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/spectra/ogip_92_007/node5.html
* for details about the Pulse Height Analyzer spectrum format.
***************************************************************************/
void GPha::read(const GFitsTable& table)
{
// Clear spectrum
clear();
// Get data column
const GFitsTableCol* col_data = table["COUNTS"];
// Extract number of channels in FITS file
int length = col_data->nrows();
// Check whether column length is consistent with energy boundaries
if (m_ebounds.size() > 0) {
if (m_ebounds.size() != length) {
std::string msg = "Mismatch between the "+gammalib::str(length)+
" channels in the PHA file and the "+
gammalib::str(m_ebounds.size())+" energy "
"boundaries. Please correct either the energy "
"boundaris or the PHA file.";
throw GException::invalid_value(G_READ, msg);
}
}
// Initialize spectrum
m_counts.assign(length, 0.0);
// Copy data
for (int i = 0; i < length; ++i) {
m_counts[i] = col_data->real(i);
}
// Read keywords
m_exposure = (table.has_card("EXPOSURE")) ? table.real("EXPOSURE") : 0.0;
// Return
return;
}
/***********************************************************************//**
* @brief Write Pulse Height Analyzer spectrum
*
* @param[in] fits FITS file.
*
* Writes the Pulse Height Analyzer spectrum into `SPECTRUM` and `EBOUNDS`
* extensions of the FITS file. Extensions with these names will be removed
* from the FITS file before writing.
*
* The columns `CHANNEL`, `COUNTS`, `STAT_ERR`, `SYS_ERR`, `QUALITY`,
* `GROUPING`, `AREASCAL`, and `BACKSCAL` will be written into the `SPECTRUM`
* extension, but only the `CHANNEL` and `COUNTS` columns will be filled with
* values. Note that the channels start from 1 in the Pulse Height Analyzer
* spectrum.
*
* See
* https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/spectra/ogip_92_007/node5.html
* for details about the PHA file format.
***************************************************************************/
void GPha::write(GFits& fits) const
{
// Remove extensions if they exist already
if (fits.contains(gammalib::extname_ebounds)) {
fits.remove(gammalib::extname_ebounds);
}
if (fits.contains(gammalib::extname_pha)) {
fits.remove(gammalib::extname_pha);
}
// Set column length
int length = size();
// Continue only if there are bins
if (length > 0) {
// Create new binary table
GFitsBinTable hdu;
// Allocate floating point vector columns
GFitsTableShortCol col_chan("CHANNEL", length);
GFitsTableFloatCol col_data("COUNTS", length);
GFitsTableFloatCol col_stat("STAT_ERR", length);
GFitsTableFloatCol col_syst("SYS_ERR", length);
GFitsTableShortCol col_qual("QUALITY", length);
GFitsTableShortCol col_grpg("GROUPING", length);
GFitsTableFloatCol col_area("AREASCAL", length);
GFitsTableFloatCol col_back("BACKSCAL", length);
// Fill columns
for (int i = 0; i < length; ++i) {
col_chan(i) = i+1; // Channels start at 1
col_data(i) = float(m_counts[i]);
}
// Set table attributes
hdu.extname(gammalib::extname_pha);
// Append columns to table
hdu.append(col_chan);
hdu.append(col_data);
hdu.append(col_stat);
hdu.append(col_syst);
hdu.append(col_qual);
hdu.append(col_grpg);
hdu.append(col_area);
hdu.append(col_back);
// Write keywords
hdu.card("EXPOSURE", m_exposure, "[s] Deadtime corrected exposure time");
// Append HDU to FITS file
fits.append(hdu);
// Optionally append energy boundaries
if (m_ebounds.size() > 0) {
m_ebounds.write(fits);
}
} // endif: there were data to write
// Return
return;
}
/***********************************************************************//**
* @brief Print Pulse Height Analyzer spectrum
*
* @param[in] chatter Chattiness.
* @return String containing Pulse Height Analyzer spectrum information.
***************************************************************************/
std::string GPha::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("=== GPha ===");
// Append energy boundary information
result.append("\n"+gammalib::parformat("Number of bins"));
result.append(gammalib::str(size()));
result.append("\n"+gammalib::parformat("Energy range"));
if (m_ebounds.size() > 0) {
result.append(m_ebounds.emin().print());
result.append(" - ");
result.append(m_ebounds.emax().print());
}
else {
result.append("not specified");
}
// Append content information
result.append("\n"+gammalib::parformat("Total number of counts"));
result.append(gammalib::str(counts()));
result.append("\n"+gammalib::parformat("Underflow counts"));
result.append(gammalib::str(m_underflow));
result.append("\n"+gammalib::parformat("Overflow counts"));
result.append(gammalib::str(m_overflow));
result.append("\n"+gammalib::parformat("Outflow counts"));
result.append(gammalib::str(m_outflow));
} // endif: chatter was not silent
// Return result
return result;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void GPha::init_members(void)
{
// Initialise members
m_filename.clear();
m_ebounds.clear();
m_counts.clear();
m_underflow = 0.0;
m_overflow = 0.0;
m_outflow = 0.0;
m_exposure = 0.0;
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] pha Pulse Height Analyzer spectrum.
***************************************************************************/
void GPha::copy_members(const GPha& pha)
{
// Copy members
m_filename = pha.m_filename;
m_counts = pha.m_counts;
m_underflow = pha.m_underflow;
m_overflow = pha.m_overflow;
m_outflow = pha.m_outflow;
m_ebounds = pha.m_ebounds;
m_exposure = pha.m_exposure;
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void GPha::free_members(void)
{
// Return
return;
}