/***************************************************************************
* GCTAEventList.cpp - CTA event list class *
* ----------------------------------------------------------------------- *
* copyright (C) 2010-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 GCTAEventList.cpp
* @brief CTA event list class implementation
* @author Juergen Knoedlseder
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "GTools.hpp"
#include "GMath.hpp"
#include "GFilename.hpp"
#include "GFits.hpp"
#include "GFitsTable.hpp"
#include "GFitsBinTable.hpp"
#include "GFitsTableFloatCol.hpp"
#include "GFitsTableDoubleCol.hpp"
#include "GFitsTableULongCol.hpp"
#include "GTime.hpp"
#include "GTimeReference.hpp"
#include "GCTAEventList.hpp"
#include "GCTASupport.hpp"
/* __ Method name definitions ____________________________________________ */
#define G_OPERATOR "GCTAEventList::operator[](int&)"
#define G_ROI "GCTAEventList::roi(GRoi&)"
#define G_FETCH "GCTAEventList::fetch()"
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Debug definitions __________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
*
* Constructs empty event list.
***************************************************************************/
GCTAEventList::GCTAEventList(void) : GEventList()
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief File name constructor
*
* @param[in] filename Counts cube filename.
*
* Constructs event list by loading the events from a FITS file.
***************************************************************************/
GCTAEventList::GCTAEventList(const GFilename& filename) : GEventList()
{
// Initialise members
init_members();
// Load event list
load(filename);
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] list Event list.
*
* Constructs event list by coping from another event list.
***************************************************************************/
GCTAEventList::GCTAEventList(const GCTAEventList& list) : GEventList(list)
{
// Initialise members
init_members();
// Copy members
copy_members(list);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
*
* Destructs event list.
***************************************************************************/
GCTAEventList::~GCTAEventList(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] list Event list.
* @return Event list.
*
* Assigns events from another event list.
***************************************************************************/
GCTAEventList& GCTAEventList::operator=(const GCTAEventList& list)
{
// Execute only if object is not identical
if (this != &list) {
// Copy base class members
this->GEventList::operator=(list);
// Free members
free_members();
// Initialise members
init_members();
// Copy members
copy_members(list);
} // endif: object was not identical
// Return this object
return *this;
}
/***********************************************************************//**
* @brief Event atom access operator
*
* @param[in] index Event index [0,...,size()-1].
* @return Pointer to event atom.
*
* @exception GException::out_of_range
* Event index outside valid range.
*
* Returns pointer to an event atom.
***************************************************************************/
GCTAEventAtom* GCTAEventList::operator[](const int& index)
{
// Make sure that the events are online
fetch();
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (index < 0 || index >= size()) {
throw GException::out_of_range(G_OPERATOR, "Event index",
index, size());
}
#endif
// Return pointer
return (&(m_events[index]));
}
/***********************************************************************//**
* @brief Event atom access operator
*
* @param[in] index Event index [0,...,size()-1].
* @return Pointer to event atom.
*
* @exception GException::out_of_range
* Event index outside valid range.
*
* Returns pointer to an event atom.
***************************************************************************/
const GCTAEventAtom* GCTAEventList::operator[](const int& index) const
{
// Make sure that the events are online
fetch();
// Optionally check if the index is valid
#if defined(G_RANGE_CHECK)
if (index < 0 || index >= size()) {
throw GException::out_of_range(G_OPERATOR, "Event index",
index, size());
}
#endif
// Return pointer
return (&(m_events[index]));
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Clear event list
*
* Clear event list.
***************************************************************************/
void GCTAEventList::clear(void)
{
// Free class members (base and derived classes, derived class first)
free_members();
this->GEventList::free_members();
this->GEvents::free_members();
// Initialise members
this->GEvents::init_members();
this->GEventList::init_members();
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Clone event list
*
* @return Pointer to deep copy of event list.
*
* Returns a pointer to a deep copy of the event list.
***************************************************************************/
GCTAEventList* GCTAEventList::clone(void) const
{
// Clone event list
return new GCTAEventList(*this);
}
/***********************************************************************//**
* @brief Load events from FITS file.
*
* @param[in] filename FITS file name.
*
* Loads the event list from a FITS file. See the read() method for details.
***************************************************************************/
void GCTAEventList::load(const GFilename& filename)
{
// Open FITS file
GFits fits(filename);
// Read event list
read(fits);
// Close FITS file
fits.close();
// Return
return;
}
/***********************************************************************//**
* @brief Save events into FITS file.
*
* @param[in] filename FITS file name.
* @param[in] clobber Overwrite existing FITS file (default: false).
*
* Saves the event list into a FITS file. See the write() method for details.
***************************************************************************/
void GCTAEventList::save(const GFilename& filename,
const bool& clobber) const
{
// Open or create FITS file (without extension name since the requested
// extension may not yet exist in the file)
GFits fits(filename.url(), true);
// Write event list
write(fits);
// Save FITS file
fits.save(clobber);
// Return
return;
}
/***********************************************************************//**
* @brief Read events from FITS file.
*
* @param[in] fits FITS file.
*
* Reads the event list from a FITS file.
*
* The events will be read by default from the extension "EVENTS" unless
* an extension name is explicitly specified in the FITS file name. The
* FITS header of the extension will be scanned for data sub-space keywords
* to extract the energy boundaries, the region of interest, as well as the
* extension name for the Good Time Intervals. If no extension name for
* Good Time Intervals is found it is expected that the Good Time Intervals
* reside in the "GTI" extension.
*
* If a Good Time Intervals is found in the same FITS file, the Good Time
* Intervals will be loaded. Otherwise, a single Good Time Interval will
* be assumed based on the "TSTART" and "TSTOP" keywords found in the header
* of the event list.
*
* The method clears the event list before reading, thus any events that
* existed before in the event list will be lost. Note that the method will
* actually not read the events but only the event metadata. The events are
* only read from the FITS file when they are actually need. This reduces
* the memory requirements. Events can be manually loaded into memory using
* the fetch() method. Events can be unloaded using the dispose() method, but
* the user has to take care of saving the events to disk in case he wants
* to keep them for further use.
***************************************************************************/
void GCTAEventList::read(const GFits& fits)
{
// Clear object
clear();
// Store the FITS file name
m_filename = fits.filename();
// Initialise events extension name
std::string extname = fits.filename().extname("EVENTS");
// Get event list HDU
const GFitsTable& events = *fits.table(extname);
// Determine number of events from event list HDU
m_num_events = events.nrows();
// Signal presence of "DETX" and "DETY" columns
if (events.contains("DETX") && events.contains("DETY")) {
m_has_detxy = true;
}
else {
m_has_detxy = false;
}
// Signal presence of "PHASE" column
if (events.contains("PHASE")) {
m_has_phase = true;
}
else {
m_has_phase = false;
}
// If the file name contains an expression then load the events now
// (we apparently can't do this later properly, not fully sure why this
// is the case)
if (m_filename.has_expression()) {
read_events(events);
}
// Read GTI extension name from data sub-space keyword
std::string gti_extname = gammalib::read_ds_gti_extname(events);
// If no GTI extension name was found then
if (gti_extname.empty()) {
gti_extname = "GTI";
}
// If GTI extension is present in FITS file then read Good Time Intervals
// from that extension
if (fits.contains(gti_extname)) {
const GFitsTable& gti = *fits.table(gti_extname);
m_gti_extname = gti_extname;
m_gti.read(gti);
}
// ... otherwise build GTI from TSTART and TSTOP
else {
// Read start and stop time
double tstart = events.real("TSTART");
double tstop = events.real("TSTOP");
// Create time reference from header information
GTimeReference timeref(events);
// Set GTI time reference
m_gti.reference(timeref);
// Set start and stop time
GTime start(tstart, m_gti.reference());
GTime stop(tstop, m_gti.reference());
// Append start and stop time as single time interval to GTI
m_gti.append(start, stop);
} // endelse: GTI built from TSTART and TSTOP
// Read region of interest from data sub-space keyword
m_roi = gammalib::read_ds_roi(events);
// Read energy boundaries from data sub-space keyword
m_ebounds = gammalib::read_ds_ebounds(events);
// Return
return;
}
/***********************************************************************//**
* @brief Write CTA events and Good Time Intervals into FITS file
*
* @param[in] fits FITS file.
*
* Writes the CTA event list and the Good Time intervals into a FITS file.
*
* The events will be written by default into the extension "EVENTS" unless
* an extension name is explicitly specified in the FITS file name. The
* method also writes the data sub-space keywords in the FITS header of the
* events table.
*
* In addition, the method will also append a table containing the Good Time
* Intervals of the events to the FITS file. The extension name for the Good
* Time Intervals is either taken from the m_gti_extname member, or if empty,
* is set to "GTI".
***************************************************************************/
void GCTAEventList::write(GFits& fits) const
{
// Set event extension name
std::string evtname = fits.filename().extname("EVENTS");
// Set GTI extension name
std::string gtiname = (m_gti_extname.empty()) ? "GTI" : m_gti_extname;
// Write events and GTIs
write(fits, evtname, gtiname);
// Return
return;
}
/***********************************************************************//**
* @brief Write CTA events and Good Time Intervals into FITS file
*
* @param[in] fits FITS file.
* @param[in] evtname Event FITS extension name.
* @param[in] gtiname Good Time Intervals FITS extension name.
*
* Writes the CTA event list and the Good Time intervals into a FITS file.
*
* The events will be written into the extension @p evtname while the Good
* Time Intervals will be written into the extension @p gtiname.
***************************************************************************/
void GCTAEventList::write(GFits& fits, const std::string& evtname,
const std::string& gtiname) const
{
// Allocate empty FITS binary table
GFitsBinTable table;
// Write events into binary table
write_events(table);
// Set FITS extension name
table.extname(evtname);
// Write data selection keywords
write_ds_keys(table, gtiname);
// Append event table to FITS file
fits.append(table);
// Write GTI extension
gti().write(fits, gtiname);
// Store FITS file name
m_filename = fits.filename();
// Return
return;
}
/***********************************************************************//**
* @brief Set ROI
*
* @param[in] roi ROI.
*
* @exception GException::invalid_argument
* Invalid region of interest class specified.
*
* Sets the region of interest of the event list.
***************************************************************************/
void GCTAEventList::roi(const GRoi& roi)
{
// Cast ROI dynamically
const GCTARoi* ptr = dynamic_cast(&roi);
// Throw exception if ROI is not of correct type
if (ptr == NULL) {
std::string cls = std::string(typeid(&roi).name());
std::string msg = "Invalid region of interest type \""+cls+"\" "
"provided on input. Please specify a \"GCTARoi\" "
"object as argument.";
throw GException::invalid_argument(G_ROI, msg);
}
// Set ROI
m_roi = *ptr;
// Return
return;
}
/***********************************************************************//**
* @brief Append event to event list
*
* @param[in] event Event.
*
* Appends an event to the end of the event list.
***************************************************************************/
void GCTAEventList::append(const GCTAEventAtom& event)
{
// Make sure that the events are online
fetch();
// Append event
m_events.push_back(event);
// Append an element to all additional columns
for (int i = 0; i < m_columns.size(); ++i) {
m_columns[i]->insert(m_columns[i]->length(),1);
}
// Set number of events
m_num_events = m_events.size();
// Set event index
int index = m_num_events - 1;
m_events[index].m_index = index;
// Return
return;
}
/***********************************************************************//**
* @brief Remove events from event list
*
* @param[in] index Index from which on events should be removed.
* @param[in] number Number of event to remove (default: 1).
*
* Removes events from the event list. This method does nothing if @p index
* points beyond the event list. The method does also gently accept
* @p number arguments where @p index + @p number reach beyond the event
* list. In that case, all events from event @p index on will be removed.
***************************************************************************/
void GCTAEventList::remove(const int& index, const int& number)
{
// Make sure that the events are online
fetch();
// Continue only if index is valid
if (index < size()) {
// Determine number of elements to remove
int n_remove = (index + number > size()) ? size() - index : number;
// Remove events
m_events.erase(m_events.begin() + index,
m_events.begin() + index + n_remove);
// Remove elements from additional columns
for (int i = 0; i < m_columns.size(); ++i) {
m_columns[i]->remove(index, n_remove);
}
// Set number of events
m_num_events = m_events.size();
} // endif: index was valid
// Return
return;
}
/***********************************************************************//**
* @brief Fetch events
*
* @exception GException::file_error
* File not found.
* Unable to load events.
* @exception GException::invalid_value
* No file name has been specified.
* Fetched number of events mismatches the expectation.
*
* Fetches the events by reading them from a FITS file. This method does
* nothing if the events are already loaded, if there is nothing to fetch,
* or if the m_filename member is empty.
*
* The method is thread save. The method furthermore checks whether the
* loaded number of events corresponds to the expectation. If also checks
* whether the file from which events should be loaded actually exists.
***************************************************************************/
void GCTAEventList::fetch(void) const
{
// Continue only if events are not loaded but there are events to fetch
if (m_events.empty() && size() > 0) {
// Continue only if the file name is not empty
if (!m_filename.is_empty()) {
// Throw an exception if the file does not exist
if (!m_filename.exists()) {
std::string msg = "File \""+m_filename+"\" not found. "
"Cannot fetch events. Maybe the file has "
"been deleted in the meantime.";
GException::file_error(G_FETCH, msg);
}
// Initialise exception flag
bool has_exception = false;
// Load events. Catch any exception. Put the code into a critical
// zone as it might be called from within a parallelized thread.
#pragma omp critical
{
try {
// Open FITS file
GFits fits(m_filename);
// Initialise events extension name
std::string extname = fits.filename().extname("EVENTS");
// Get event list HDU
const GFitsTable& events = *fits.table(extname);
// Load event data
read_events(events);
// Close FITS file
fits.close();
}
catch (...) {
has_exception = true;
}
}
// Throw an exception if an exception has occured
if (has_exception) {
std::string msg = "Unable to load events from file \""+
m_filename+"\".";
throw GException::file_error(G_FETCH, msg);
}
// Throw an exception if the number of events does not correspond
// to the expectation
if (m_events.size() != size()) {
std::string msg = "Loaded "+gammalib::str(m_events.size())+
" events from FITS file while "+
gammalib::str(size())+" events were "
"expected. This should never happen!";
throw GException::invalid_value(G_FETCH, msg);
}
} // endif: filename was not empty
// Throw an exception if the FITS file name is not known
else {
std::string msg = "Unable to fetch "+gammalib::str(size())+
" events since the name of the FITS file from "
"which to fetch the events is not known.";
throw GException::invalid_value(G_FETCH, msg);
}
} // endif: there were no events
// Return
return;
}
/***********************************************************************//**
* @brief Dispose events
*
* Disposes the events to save memory. The method free's all memory
* associated to the events. The method should be used with care as it does
* not check whether the events can actually be recovered using the fetch()
* method. Eventually, the events should be saved before so that fetch()
* can recover them.
***************************************************************************/
void GCTAEventList::dispose(void) const
{
// Free additional columns
for (int i = 0; i < m_columns.size(); ++i) {
delete m_columns[i];
m_columns[i] = NULL;
}
// Clear events and additional columns
m_events.clear();
m_columns.clear();
// Return
return;
}
/***********************************************************************//**
* @brief Print event list information
*
* @param[in] chatter Chattiness (defaults to NORMAL).
* @return String containing event list information.
***************************************************************************/
std::string GCTAEventList::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("=== GCTAEventList ===");
// Append number of events and disposal information
result.append("\n"+gammalib::parformat("Number of events") +
gammalib::str(size()));
if (m_events.empty()) {
if (!m_filename.is_empty()) {
result.append(" (disposed in \""+m_filename+"\")");
}
else {
result.append(" (disposed without possibility to recover)");
}
}
else {
result.append(" (loaded)");
}
// Append GTI interval
result.append("\n"+gammalib::parformat("Time interval"));
if (gti().size() > 0) {
result.append(gammalib::str(tstart().mjd()));
result.append(" - ");
result.append(gammalib::str(tstop().mjd())+" days");
}
else {
result.append("not defined");
}
// Append energy intervals
if (gammalib::reduce(chatter) > SILENT) {
if (ebounds().size() > 0) {
result.append("\n"+ebounds().print(gammalib::reduce(chatter)));
}
else {
result.append("\n"+gammalib::parformat("Energy intervals") +
"not defined");
}
}
else {
result.append("\n"+gammalib::parformat("Energy interval"));
if (ebounds().size() > 0) {
result.append(gammalib::str(emin().TeV()));
result.append(" - ");
result.append(gammalib::str(emax().TeV())+" TeV");
}
else {
result.append("not defined");
}
}
// Append ROI
if (gammalib::reduce(chatter) > SILENT) {
if (roi().radius() > 0) {
result.append("\n"+roi().print(gammalib::reduce(chatter)));
}
else {
result.append("\n"+gammalib::parformat("Region of interest") +
"not defined");
}
}
else {
result.append("\n"+gammalib::parformat("Region of interest"));
if (roi().radius() > 0) {
result.append(roi().centre().print());
result.append(" Radius=");
result.append(gammalib::str(roi().radius())+" deg");
}
else {
result.append("not defined");
}
}
// EXPLICIT: Append IRF cache
if (chatter >= EXPLICIT) {
for (int i = 0; i < m_irf_names.size(); ++i) {
result.append("\n"+gammalib::parformat("IRF cache " +
gammalib::str(i)));
result.append(m_irf_names[i]+" = ");
int num = 0;
for (int k = 0; k < size(); ++k) {
if ((m_irf_values[i])[k] != -1.0) {
num++;
}
}
result.append(gammalib::str(num)+" values");
}
} // endif: chatter was explicit
} // endif: chatter was not silent
// Return result
return result;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void GCTAEventList::init_members(void)
{
// Initialise members
m_roi.clear();
m_events.clear();
m_columns.clear();
m_filename.clear();
m_num_events = 0;
m_gti_extname = "GTI"; //!< Default GTI extension name
m_has_phase = false;
m_has_detxy = true;
// Initialise cache
m_irf_names.clear();
m_irf_values.clear();
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] list Event list.
***************************************************************************/
void GCTAEventList::copy_members(const GCTAEventList& list)
{
// Copy members
m_roi = list.m_roi;
m_events = list.m_events;
m_filename = list.m_filename;
m_num_events = list.m_num_events;
m_gti_extname = list.m_gti_extname;
m_has_phase = list.m_has_phase;
m_has_detxy = list.m_has_detxy;
// Copy cache
m_irf_names = list.m_irf_names;
m_irf_values = list.m_irf_values;
// Copy column pointers
m_columns.clear();
for (int i = 0; i < list.m_columns.size(); ++i) {
m_columns.push_back((list.m_columns[i]->clone()));
}
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void GCTAEventList::free_members(void)
{
// Free columns
for (int i = 0; i < m_columns.size(); ++i) {
delete m_columns[i];
m_columns[i] = NULL;
}
// Return
return;
}
/***********************************************************************//**
* @brief Read CTA events from FITS table
*
* @param[in] table FITS table.
*
* This method reads the CTA event list from a FITS table into memory.
* The following columns are mandatory in the FITS table:
*
* - EVENT_ID
* - TIME
* - RA
* - DEC
* - ENERGY
*
* Optionally, the
*
* - DETX
* - DETY
* - PHASE
*
* columns are read. Any other columns present in the FITS table will also be
* read, and written into the FITS table when the write_events() method is
* called.
***************************************************************************/
void GCTAEventList::read_events(const GFitsTable& table) const
{
// Dispose any existing events
dispose();
// Extract number of events in FITS file
int num = table.nrows();
// If there are events then load them
if (num > 0) {
// Reserve data
m_events.reserve(num);
// Get column pointers
const GFitsTableCol* ptr_eid = table["EVENT_ID"];
const GFitsTableCol* ptr_time = table["TIME"];
const GFitsTableCol* ptr_ra = table["RA"];
const GFitsTableCol* ptr_dec = table["DEC"];
const GFitsTableCol* ptr_energy = table["ENERGY"];
// Optionally get pointers to DETX and DETY columns
const GFitsTableCol* ptr_detx = (m_has_detxy) ? table["DETX"] : NULL;
const GFitsTableCol* ptr_dety = (m_has_detxy) ? table["DETY"] : NULL;
// Optionally get pointer to PHASE column
const GFitsTableCol* ptr_phase = (m_has_phase) ? table["PHASE"] : NULL;
// Copy data from columns into GCTAEventAtom objects
for (int i = 0; i < num; ++i) {
// Allocate event
GCTAEventAtom event;
// Set mandatory information
event.m_time.set(ptr_time->real(i), m_gti.reference());
event.m_dir.dir().radec_deg(ptr_ra->real(i), ptr_dec->real(i));
event.m_energy.TeV(ptr_energy->real(i));
event.m_event_id = ptr_eid->integer(i);
event.m_index = i;
// If available, set detector coordinates in radians
if (m_has_detxy) {
event.m_dir.detx(ptr_detx->real(i)*gammalib::deg2rad);
event.m_dir.dety(ptr_dety->real(i)*gammalib::deg2rad);
}
// If available, set pulse phase
if (m_has_phase) {
event.m_phase = ptr_phase->real(i);
}
// Append event
m_events.push_back(event);
} // endfor: looped over all events
// Loop over table and find optional columns
for (int i = 0; i < table.ncols(); ++i) {
// Get column pointer
const GFitsTableCol* col = table[i];
// Get column name
const std::string name = col->name();
// If column was mandatory or optional then skip it ...
if (name == "EVENT_ID" ||
name == "TIME" ||
name == "RA" ||
name == "DEC" ||
name == "ENERGY" ||
name == "DETX" ||
name == "DETY" ||
name == "PHASE") {
continue;
}
// ... otherwise keep a copy
else {
m_columns.push_back(col->clone());
}
} // endfor: loop over optional columns
} // endif: there were events
// Return
return;
}
/***********************************************************************//**
* @brief Write CTA events into FITS table
*
* @param[in] hdu FITS binary table.
*
* Write the CTA event list into a FITS binary table.
*
* The following mandatory columns will be written into the FITS table:
*
* - EVENT_ID
* - TIME
* - RA
* - DEC
* - ENERGY
*
* If available, also the
*
* - DETX
* - DETY
* - PHASE
*
* columns are written. Any other columns that were read by the read_events()
* method will be also written into the table.
***************************************************************************/
void GCTAEventList::write_events(GFitsBinTable& hdu) const
{
// Make sure that the events are online
fetch();
// Set extension name
hdu.extname("EVENTS");
// If there are events then write them now
if (size() > 0) {
// Allocate mandatory columns
GFitsTableULongCol col_eid = GFitsTableULongCol("EVENT_ID", size());
GFitsTableDoubleCol col_time = GFitsTableDoubleCol("TIME", size());
GFitsTableFloatCol col_ra = GFitsTableFloatCol("RA", size());
GFitsTableFloatCol col_dec = GFitsTableFloatCol("DEC", size());
GFitsTableFloatCol col_energy = GFitsTableFloatCol("ENERGY", size());
// Set units of columns
// (see http://fits.gsfc.nasa.gov/standard30/fits_standard30aa.pdf)
col_time.unit("s");
col_ra.unit("deg");
col_dec.unit("deg");
col_energy.unit("TeV");
// Fill mandatory columns
for (int i = 0; i < size(); ++i) {
col_eid(i) = m_events[i].m_event_id;
col_time(i) = m_events[i].time().convert(m_gti.reference());
col_ra(i) = m_events[i].dir().dir().ra_deg();
col_dec(i) = m_events[i].dir().dir().dec_deg();
col_energy(i) = m_events[i].energy().TeV();
}
// Append mandatory columns to table
hdu.append(col_eid);
hdu.append(col_time);
hdu.append(col_ra);
hdu.append(col_dec);
hdu.append(col_energy);
// If available, add detector coordinates in degrees
if (m_has_detxy) {
// Allocate columns
GFitsTableFloatCol col_detx = GFitsTableFloatCol("DETX", size());
GFitsTableFloatCol col_dety = GFitsTableFloatCol("DETY", size());
// Set units of columns
// (see http://fits.gsfc.nasa.gov/standard30/fits_standard30aa.pdf)
col_detx.unit("deg");
col_dety.unit("deg");
// Fill columns
for (int i = 0; i < size(); ++i) {
col_detx(i) = m_events[i].dir().detx() * gammalib::rad2deg;
col_dety(i) = m_events[i].dir().dety() * gammalib::rad2deg;
}
// Append columns to table
hdu.append(col_detx);
hdu.append(col_dety);
}
// If available, add event phase
if (m_has_phase) {
// Allocate columns
GFitsTableFloatCol col_phase = GFitsTableFloatCol("PHASE", size());
// Fill columns
for (int i = 0; i < size(); ++i) {
col_phase(i) = m_events[i].m_phase;
}
// Append columns to table
hdu.append(col_phase);
}
// Append other columns to table
for (int i = 0; i < m_columns.size(); ++i) {
hdu.append(*m_columns[i]);
}
} // endif: there were events to write
// Return
return;
}
/***********************************************************************//**
* @brief Write data sub-space keywords into FITS HDU
*
* @param[in] hdu FITS HDU.
* @param[in] gtiname Good Time Interval FITS extension.
*
* Writes the data sub-space keywords for an event list into the FITS HDU.
* The following keywords will be written:
*
* DSTYP1 = "TIME" / Data sub-space type
* DSUNI1 = "s" / Data sub-space unit
* DSVAL1 = "TABLE" / Data sub-space value
* DSREF1 = ":[extname]" / Data sub-space reference
* DSTYP2 = "ENERGY" / Data sub-space type
* DSUNI2 = "TeV" / Data sub-space unit
* DSVAL2 = "[emin]:[emax]" / Data sub-space value
* DSTYP3 = "POS(RA,DEC)" / Data sub-space type
* DSUNI3 = "deg" / Data sub-space unit
* DSVAL3 = "CIRCLE([ra],[dec],[rad])" / Data sub-space value
*
* where
*
* [extname] is the GTI extension name @p gtiname
* [emin] is the minimum event energy in TeV
* [emax] is the maximum event energy in TeV
* [ra] is the Right Ascension of the Region of Interest centre in degrees
* [dec] is the Declination of the Region of Interest centre in degrees
* [rad] is the radius of the Region of Interest in degrees
***************************************************************************/
void GCTAEventList::write_ds_keys(GFitsHDU& hdu, const std::string& gtiname) const
{
// Set ROI parameters
double ra = roi().centre().dir().ra_deg();
double dec = roi().centre().dir().dec_deg();
double rad = roi().radius();
// Set energy range parameters
double e_min = emin().TeV();
double e_max = emax().TeV();
// Set energy selection string
std::string dsval2 = gammalib::str(e_min) + ":" +
gammalib::str(e_max);
// Set Good Time Intervals extension name
std::string dsref1 = ":"+gtiname;
// Add time selection keywords
hdu.card("DSTYP1", "TIME", "Data sub-space type");
hdu.card("DSUNI1", "s", "Data sub-space unit");
hdu.card("DSVAL1", "TABLE", "Data sub-space value");
hdu.card("DSREF1", dsref1, "Data sub-space reference");
// Add energy range selection
hdu.card("DSTYP2", "ENERGY", "Data sub-space type");
hdu.card("DSUNI2", "TeV", "Data sub-space unit");
hdu.card("DSVAL2", dsval2, "Data sub-space value");
// Initialise number of NDSKEYS
int ndskeys = 2;
// Add acceptance cone only if RoI information is valid
if (m_roi.is_valid()) {
// Set cone selection string
std::string dsval3 = "CIRCLE(" +
gammalib::str(ra) + "," +
gammalib::str(dec) + "," +
gammalib::str(rad) + ")";
// Write DS keywords
hdu.card("DSTYP3", "POS(RA,DEC)", "Data sub-space type");
hdu.card("DSUNI3", "deg", "Data sub-space unit");
hdu.card("DSVAL3", dsval3, "Data sub-space value");
ndskeys++;
} // endif: RoI was valid
// Set number of data selection keys
hdu.card("NDSKEYS", ndskeys, "Number of data sub-space keys");
// Return
return;
}
/***********************************************************************//**
* @brief Initialize IRF cache for a given model
*
* @param[in] name Model name.
* @return Cache index (-1 if invalid).
***************************************************************************/
int GCTAEventList::irf_cache_init(const std::string& name) const
{
// Initialise cache index
int index = irf_cache_index(name);
// Continue only if model does not yet exist
if (index == -1) {
// Add model name and vector to cache. The vector is initialized
// to -1, which signals that no cache values exist
m_irf_names.push_back(name);
m_irf_values.push_back(std::vector(size(), -1.0));
// Set index
index = m_irf_names.size()-1;
} // endif: model cache did not yet exist
// Return index
return index;
}
/***********************************************************************//**
* @brief Determines the cache index for a given model name
*
* @param[in] name Model name.
* @return Cache index (-1 if model has not been found).
***************************************************************************/
int GCTAEventList::irf_cache_index(const std::string& name) const
{
// Initialise index
int index = -1;
// Continue only if there are models in cache
if (!m_irf_names.empty()) {
// Search for model name
for (int i = 0; i < m_irf_names.size(); ++i) {
if (m_irf_names[i] == name) {
index = i;
break;
}
}
} // endif: there were models in cache
// Return index
return index;
}
/***********************************************************************//**
* @brief Get cache IRF value
*
* @param[in] name Model name.
* @param[in] index Event index [0,...,size()-1].
* @return IRF value (-1 if no cache value found).
***************************************************************************/
double GCTAEventList::irf_cache(const std::string& name, const int& index) const
{
// Initialise IRF value to invalid value
double irf = -1.0;
// Get cache index. Continue only if index is valid
int icache = irf_cache_index(name);
if (icache != -1) {
irf = (m_irf_values[icache])[index];
}
// Return IRF value
return irf;
}
/***********************************************************************//**
* @brief Set cache IRF value
*
* @param[in] name Model name.
* @param[in] index Event index [0,...,size()-1].
* @param[in] irf IRF value.
***************************************************************************/
void GCTAEventList::irf_cache(const std::string& name, const int& index,
const double& irf) const
{
// Initialize cache index. Continue only if index is valid
int icache = irf_cache_init(name);
if (icache != -1) {
(m_irf_values[icache])[index] = irf;
}
// Return
return;
}