/*************************************************************************** * GCTAEventList.cpp - CTA event atom container class * * ----------------------------------------------------------------------- * * copyright (C) 2010-2011 by Jurgen Knodlseder * * ----------------------------------------------------------------------- * * * * 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 atom container class implementation. * @author J. Knodlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include "GCTAEventList.hpp" #include "GCTAException.hpp" #include "GTools.hpp" #include "GFits.hpp" #include "GFitsTableBitCol.hpp" #include "GFitsTableFloatCol.hpp" #include "GFitsTableDoubleCol.hpp" #include "GFitsTableULongCol.hpp" #include "GFitsTableShortCol.hpp" #include "GFitsTableStringCol.hpp" /* __ Method name definitions ____________________________________________ */ #define G_OPERATOR "GCTAEventList::operator[](int&)" #define G_ROI "GCTAEventList::roi(GRoi&)" #define G_READ_DS_EBOUNDS "GCTAEventList::read_ds_ebounds(GFitsHDU*)" #define G_READ_DS_ROI "GCTAEventList::read_ds_roi(GFitsHDU*)" /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ /* __ Debug definitions __________________________________________________ */ /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Void constructor ***************************************************************************/ GCTAEventList::GCTAEventList(void) : GEventList() { // Initialise members init_members(); // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] list Event list. ***************************************************************************/ GCTAEventList::GCTAEventList(const GCTAEventList& list) : GEventList(list) { // Initialise members init_members(); // Copy members copy_members(list); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GCTAEventList::~GCTAEventList(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] list 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]. * * @exception GException::out_of_range * Event index outside valid range. * * Returns pointer to an event atom. ***************************************************************************/ GCTAEventAtom* GCTAEventList::operator[](const int& index) { // Optionally check if the index is valid #if defined(G_RANGE_CHECK) if (index < 0 || index >= size()) throw GException::out_of_range(G_OPERATOR, index, 0, size()-1); #endif // Return pointer return (&(m_events[index])); } /***********************************************************************//** * @brief Event atom access operator * * @param[in] index Event index [0,...,size()-1]. * * @exception GException::out_of_range * Event index outside valid range. * * Returns pointer to an event atom. ***************************************************************************/ const GCTAEventAtom* GCTAEventList::operator[](const int& index) const { // Optionally check if the index is valid #if defined(G_RANGE_CHECK) if (index < 0 || index >= size()) throw GException::out_of_range(G_OPERATOR, index, 0, size()-1); #endif // Return pointer return (&(m_events[index])); } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear object * * This method properly resets the object to an initial state. ***************************************************************************/ 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 object ***************************************************************************/ GCTAEventList* GCTAEventList::clone(void) const { return new GCTAEventList(*this); } /***********************************************************************//** * @brief Load events from event FITS file. * * @param[in] filename Name of FITS file from which events are loaded. * * Load CTA events from the EVENTS extension EVENTS, read the region of * interest and the energy boundaries from the data selection keywords, * and read the Good Time Intervals from the GTI extension. * * The method clears the object before loading, thus any events residing in * the object before loading will be lost. ***************************************************************************/ void GCTAEventList::load(const std::string& filename) { // Clear object clear(); // Open FITS file GFits file(filename); // Read event list read(file); // Close FITS file file.close(); // Return return; } /***********************************************************************//** * @brief Save CTA events into FITS file. * * @param[in] filename FITS filename. * @param[in] clobber Overwrite existing FITS file (default=false). * * Write the CTA event list into FITS file. ***************************************************************************/ void GCTAEventList::save(const std::string& filename, bool clobber) const { // Create empty FITS file GFits fits; // Write event list write(fits); // Save FITS file fits.saveto(filename, clobber); // Return return; } /***********************************************************************//** * @brief Read CTA events from FITS file. * * @param[in] file FITS file. * * This method read the CTA event list from a FITS file. * * The method clears the object before loading, thus any events residing in * the object before loading will be lost. ***************************************************************************/ void GCTAEventList::read(const GFits& file) { // Clear object clear(); // Get HDUs GFitsTable* events = file.table("EVENTS"); GFitsTable* gti = file.table("GTI"); // Load event data read_events(events); // Read region of interest from data selection keyword read_ds_roi(events); // Read energy boundaries from data selection keyword read_ds_ebounds(events); // Read Good Time Intervals m_gti.read(gti); // Return return; } /***********************************************************************//** * @brief Write CTA events into FITS file. * * @param[in] file FITS file. * * Write the CTA event list into FITS file. * * @todo The TELMASK column is allocated with a dummy length of 100. * @todo Implement agreed column format ***************************************************************************/ void GCTAEventList::write(GFits& file) const { // Allocate FITS binary table HDU GFitsBinTable* events = new GFitsBinTable; // Write events write_events(events); // Write data selection keywords write_ds_keys(events); // Append event table to FITS file file.append(*events); // Free binary table delete events; // Append GTI to FITS file gti().write(&file); // Return return; } /***********************************************************************//** * @brief Set ROI * * @param[in] roi ROI. * * @exception GCTAException::bad_roi_type * ROI is not of type GCTARoi. ***************************************************************************/ 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) throw GCTAException::bad_roi_type(G_ROI); // Set ROI m_roi = *ptr; // Return return; } /***********************************************************************//** * @brief Print event list information ***************************************************************************/ std::string GCTAEventList::print(void) const { // Initialise result string std::string result; // Append header result.append("=== GCTAEventList ==="); result.append("\n"+parformat("Number of events")+str(size())); // Append GTI intervals result.append("\n"+parformat("Time interval")); if (gti().size() > 0) result.append(str(tstart().met())+" - "+str(tstop().met())+" sec"); else result.append("not defined"); // Append energy intervals if (ebounds().size() > 0) result.append("\n"+ebounds().print()); else result.append("\n"+parformat("Energy intervals")+"not defined"); // Append ROI if (roi().radius() > 0) result.append("\n"+roi().print()); else result.append("\n"+parformat("Region of interest")+"not defined"); // Return result return result; } /***********************************************************************//** * @brief Append event atom to event list * * @param[in] event Event. * * Appends an event atom to the event list. ***************************************************************************/ void GCTAEventList::append(const GCTAEventAtom& event) { // Append event m_events.push_back(event); // Return return; } /***********************************************************************//** * @brief Reserves space for events * * @param[in] number Number of events. * * Reserves space for number events in the event list. ***************************************************************************/ void GCTAEventList::reserve(const int& number) { // Reserve space m_events.reserve(number); // Return return; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GCTAEventList::init_members(void) { // Initialise members m_roi.clear(); m_events.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; // Return return; } /***********************************************************************//** * @brief Delete class members ***************************************************************************/ void GCTAEventList::free_members(void) { // Return return; } /***********************************************************************//** * @brief Read CTA events from FITS table * * @param[in] table FITS table pointer. * * This method reads the CTA event list from a FITS table HDU into memory. * * @todo Implement agreed column format * @todo Declare GFitsTable::column() as const, or at least, add a const * version. Even better, add column access operators instead of the * column() method as GFitsTable is a container for columns, and * each container should have an access operator. ***************************************************************************/ void GCTAEventList::read_events(const GFitsTable* table) { // Clear existing events m_events.clear(); // Allocate space for keyword name char keyword[10]; // Continue only if HDU is valid if (table != NULL) { // Circumvent const correctness. We need this because the column() // method is not declared const. This should be corrected. //GFitsTable* hdu = (GFitsTable*)table; // Extract number of events in FITS file int num = table->integer("NAXIS2"); // If there are events then load them if (num > 0) { // Reserve data m_events.reserve(num); // Get column pointers GFitsTableULongCol* ptr_eid = (GFitsTableULongCol*)&(*table)["EVENT_ID"]; GFitsTableULongCol* ptr_oid = (GFitsTableULongCol*)&(*table)["OBS_ID"]; GFitsTableDoubleCol* ptr_time = (GFitsTableDoubleCol*)&(*table)["TIME"]; GFitsTableShortCol* ptr_multip = (GFitsTableShortCol*)&(*table)["MULTIP"]; GFitsTableFloatCol* ptr_ra = (GFitsTableFloatCol*)&(*table)["RA"]; GFitsTableFloatCol* ptr_dec = (GFitsTableFloatCol*)&(*table)["DEC"]; GFitsTableFloatCol* ptr_dir_err = (GFitsTableFloatCol*)&(*table)["DIR_ERR"]; GFitsTableFloatCol* ptr_detx = (GFitsTableFloatCol*)&(*table)["DETX"]; GFitsTableFloatCol* ptr_dety = (GFitsTableFloatCol*)&(*table)["DETY"]; GFitsTableFloatCol* ptr_alt = (GFitsTableFloatCol*)&(*table)["ALT"]; GFitsTableFloatCol* ptr_az = (GFitsTableFloatCol*)&(*table)["AZ"]; GFitsTableFloatCol* ptr_corex = (GFitsTableFloatCol*)&(*table)["COREX"]; GFitsTableFloatCol* ptr_corey = (GFitsTableFloatCol*)&(*table)["COREY"]; GFitsTableFloatCol* ptr_core_err = (GFitsTableFloatCol*)&(*table)["CORE_ERR"]; GFitsTableFloatCol* ptr_xmax = (GFitsTableFloatCol*)&(*table)["XMAX"]; GFitsTableFloatCol* ptr_xmax_err = (GFitsTableFloatCol*)&(*table)["XMAX_ERR"]; GFitsTableFloatCol* ptr_shw = (GFitsTableFloatCol*)&(*table)["SHWIDTH"]; GFitsTableFloatCol* ptr_shl = (GFitsTableFloatCol*)&(*table)["SHLENGTH"]; GFitsTableFloatCol* ptr_energy = (GFitsTableFloatCol*)&(*table)["ENERGY"]; GFitsTableFloatCol* ptr_energy_err = (GFitsTableFloatCol*)&(*table)["ENERGY_ERR"]; // Copy data from columns into GCTAEventAtom objects GCTAEventAtom event; for (int i = 0; i < num; ++i) { event.m_time.met((*ptr_time)(i)); event.m_dir.radec_deg((*ptr_ra)(i), (*ptr_dec)(i)); event.m_energy.TeV((*ptr_energy)(i)); event.m_event_id = (*ptr_eid)(i); event.m_obs_id = (*ptr_oid)(i); event.m_multip = (*ptr_multip)(i); event.m_telmask = 0; event.m_dir_err = (*ptr_dir_err)(i); event.m_detx = (*ptr_detx)(i); event.m_dety = (*ptr_dety)(i); event.m_alt = (*ptr_alt)(i); event.m_az = (*ptr_az)(i); event.m_corex = (*ptr_corex)(i); event.m_corey = (*ptr_corey)(i); event.m_core_err = (*ptr_core_err)(i); event.m_xmax = (*ptr_xmax)(i); event.m_xmax_err = (*ptr_xmax_err)(i); event.m_shwidth = (*ptr_shw)(i); event.m_shlength = (*ptr_shl)(i); event.m_energy_err = (*ptr_energy_err)(i); m_events.push_back(event); } } // endif: there were events } // endif: HDU was valid // Return return; } /***********************************************************************//** * @brief Read energy boundary data selection keywords * * @param[in] hdu FITS HDU * * @exception GCTAException::no_ebds * Invalid energy data selection encountered * * Reads the energy boundary data selection keywords by searching for a * DSTYPx keyword named "ENERGY". The data selection information is expected * to be in the format "200:50000", where the 2 arguments are the minimum * and maximum energy. The energy unit is given by the keyword DSUNIx, which * supports keV, MeV, GeV and TeV (case independent). No detailed syntax * checking is performed. * * This method clears the energy boundaries, irrespectively of whether energy * boundary information has been found in the HDU. ***************************************************************************/ void GCTAEventList::read_ds_ebounds(const GFitsHDU* hdu) { // Reset energy boundaries m_ebounds.clear(); // Continue only if HDU is valid if (hdu != NULL) { // Get number of data selection keywords int ndskeys = 0; try { ndskeys = hdu->integer("NDSKEYS"); } catch (GException::fits_key_not_found &e) { ; } // Loop over all data selection keys for (int i = 1; i <= ndskeys; ++i) { std::string type_key = "DSTYP"+str(i); std::string unit_key = "DSUNI"+str(i); std::string value_key = "DSVAL"+str(i); try { if (hdu->string(type_key) == "ENERGY") { std::string unit = toupper(hdu->string(unit_key)); std::string value = hdu->string(value_key); std::vector ebounds = split(value, ":"); if (ebounds.size() == 2) { double emin = todouble(ebounds[0]); double emax = todouble(ebounds[1]); GEnergy e_min; GEnergy e_max; if (unit == "KEV") { e_min.keV(emin); e_max.keV(emax); } else if (unit == "MEV") { e_min.MeV(emin); e_max.MeV(emax); } else if (unit == "GEV") { e_min.GeV(emin); e_max.GeV(emax); } else if (unit == "TEV") { e_min.TeV(emin); e_max.TeV(emax); } else { throw GCTAException::no_ebds(G_READ_DS_EBOUNDS, "Invalid energy unit \""+unit+ "\" encountered in data selection key \""+ unit_key+"\""); } m_ebounds.append(e_min, e_max); } else { throw GCTAException::no_ebds(G_READ_DS_EBOUNDS, "Invalid energy value \""+value+ "\" encountered in data selection key \""+ value_key+"\""); } } // endif: ENERGY type found } catch (GException::fits_key_not_found &e) { ; } } // endfor: looped over data selection keys } // endif: HDU was valid // Return return; } /***********************************************************************//** * @brief Read ROI data selection keywords * * @param[in] hdu FITS HDU * * @exception GException::no_roi * Invalid ROI data selection encountered * * Reads the ROI data selection keywords by searching for a DSTYPx keyword * named "POS(RA,DEC)". The data selection information is expected to be * in the format "CIRCLE(267.0208,-24.78,4.5)", where the 3 arguments are * Right Ascension, Declination and radius in units of degrees. No detailed * syntax checking is performed. * * This method clears the ROI, irrespectively of whether ROI information has * been found in the HDU. ***************************************************************************/ void GCTAEventList::read_ds_roi(const GFitsHDU* hdu) { // Reset ROI m_roi.clear(); // Continue only if HDU is valid if (hdu != NULL) { // Get number of data selection keywords int ndskeys = 0; try { ndskeys = hdu->integer("NDSKEYS"); } catch (GException::fits_key_not_found &e) { ; } // Loop over all data selection keys for (int i = 1; i <= ndskeys; ++i) { std::string type_key = "DSTYP"+str(i); std::string unit_key = "DSUNI"+str(i); std::string value_key = "DSVAL"+str(i); try { if (hdu->string(type_key) == "POS(RA,DEC)") { std::string unit = toupper(hdu->string(unit_key)); std::string value = hdu->string(value_key); value = strip_chars(value, "CIRCLE("); value = strip_chars(value, ")"); std::vector args = split(value, ","); if (args.size() == 3) { double ra = todouble(args[0]); double dec = todouble(args[1]); double rad = todouble(args[2]); GCTAInstDir dir; dir.radec_deg(ra, dec); m_roi.centre(dir); m_roi.radius(rad); } else { throw GException::no_roi(G_READ_DS_ROI, "Invalid acceptance cone value \""+value+ "\" encountered in data selection key \""+ value_key+"\""); } } // endif: POS(RA,DEC) type found } catch (GException::fits_key_not_found &e) { ; } } // endfor: looped over data selection keys } // endif: HDU was valid // Return return; } /***********************************************************************//** * @brief Write CTA events into FITS table * * @param[in] hdu FITS table HDU. * * Write the CTA event list into FITS table. * * @todo The TELMASK column is allocated with a dummy length of 100. * @todo Implement agreed column format ***************************************************************************/ void GCTAEventList::write_events(GFitsBinTable* hdu) const { // Continue only if HDU is valid if (hdu != NULL) { // Set extension name hdu->extname("EVENTS"); // If there are events then write them now if (size() > 0) { // Allocate columns GFitsTableULongCol col_eid = GFitsTableULongCol("EVENT_ID", size()); GFitsTableULongCol col_oid = GFitsTableULongCol("OBS_ID", size()); GFitsTableDoubleCol col_time = GFitsTableDoubleCol("TIME", size()); GFitsTableDoubleCol col_live = GFitsTableDoubleCol("TLIVE", size()); GFitsTableShortCol col_multip = GFitsTableShortCol("MULTIP", size()); GFitsTableBitCol col_telmask = GFitsTableBitCol("TELMASK", size(), 100); GFitsTableFloatCol col_ra = GFitsTableFloatCol("RA", size()); GFitsTableFloatCol col_dec = GFitsTableFloatCol("DEC", size()); GFitsTableFloatCol col_direrr = GFitsTableFloatCol("DIR_ERR", size()); GFitsTableFloatCol col_detx = GFitsTableFloatCol("DETX", size()); GFitsTableFloatCol col_dety = GFitsTableFloatCol("DETY", size()); GFitsTableFloatCol col_alt = GFitsTableFloatCol("ALT", size()); GFitsTableFloatCol col_az = GFitsTableFloatCol("AZ", size()); GFitsTableFloatCol col_corex = GFitsTableFloatCol("COREX", size()); GFitsTableFloatCol col_corey = GFitsTableFloatCol("COREY", size()); GFitsTableFloatCol col_core_err = GFitsTableFloatCol("CORE_ERR", size()); GFitsTableFloatCol col_xmax = GFitsTableFloatCol("XMAX", size()); GFitsTableFloatCol col_xmax_err = GFitsTableFloatCol("XMAX_ERR", size()); GFitsTableFloatCol col_shw = GFitsTableFloatCol("SHWIDTH", size()); GFitsTableFloatCol col_shl = GFitsTableFloatCol("SHLENGTH", size()); GFitsTableFloatCol col_energy = GFitsTableFloatCol("ENERGY", size()); GFitsTableFloatCol col_energy_err = GFitsTableFloatCol("ENERGY_ERR", size()); // Fill columns for (int i = 0; i < size(); ++i) { col_eid(i) = m_events[i].m_event_id; col_oid(i) = m_events[i].m_obs_id; col_time(i) = m_events[i].time().met(); col_live(i) = 0.0; col_multip(i) = 0; //col_telmask col_ra(i) = m_events[i].dir().ra_deg(); col_dec(i) = m_events[i].dir().dec_deg(); col_direrr(i) = m_events[i].m_dir_err; col_detx(i) = m_events[i].m_detx; col_dety(i) = m_events[i].m_dety; col_alt(i) = m_events[i].m_alt; col_az(i) = m_events[i].m_az; col_corex(i) = m_events[i].m_corex; col_corey(i) = m_events[i].m_corey; col_core_err(i) = m_events[i].m_core_err; col_xmax(i) = m_events[i].m_xmax; col_xmax_err(i) = m_events[i].m_xmax_err; col_shw(i) = m_events[i].m_shwidth; col_shl(i) = m_events[i].m_shlength; col_energy(i) = m_events[i].energy().TeV(); col_energy_err(i) = m_events[i].m_energy_err; } // endfor: looped over rows // Append columns to table hdu->append_column(col_eid); hdu->append_column(col_oid); hdu->append_column(col_time); hdu->append_column(col_live); hdu->append_column(col_multip); hdu->append_column(col_telmask); hdu->append_column(col_ra); hdu->append_column(col_dec); hdu->append_column(col_direrr); hdu->append_column(col_detx); hdu->append_column(col_dety); hdu->append_column(col_alt); hdu->append_column(col_az); hdu->append_column(col_corex); hdu->append_column(col_corey); hdu->append_column(col_core_err); hdu->append_column(col_xmax); hdu->append_column(col_xmax_err); hdu->append_column(col_shw); hdu->append_column(col_shl); hdu->append_column(col_energy); hdu->append_column(col_energy_err); } // endif: there were events to write } // endif: HDU was valid // Return return; } /***********************************************************************//** * @brief Write data selection keywords into FITS HDU * * @param[in] hdu FITS HDU pointer. * * This method does nothing if the HDU pointer is NULL. * * @todo This is a very dumb data selection keyword writing routine that does * not take into account any existing keywords. We definitely want a * more secure logic that checks for existing keywords and possible * conflicts. But for this prototype software, this code does the job. ***************************************************************************/ void GCTAEventList::write_ds_keys(GFitsHDU* hdu) const { // Continue only if HDU is valid if (hdu != NULL) { // Set ROI parameters double ra = roi().centre().ra_deg(); double dec = roi().centre().dec_deg(); double rad = roi().radius(); // Set energy range parameters double e_min = emin().TeV(); double e_max = emax().TeV(); // Set cone selection string std::string dsval2 = "CIRCLE("+str(ra)+","+str(dec)+","+str(rad)+")"; // Set energy selection string std::string dsval3 = str(e_min)+":"+str(e_max); // Add time selection keywords hdu->card("DSTYP1", "TIME", "Data selection type"); hdu->card("DSUNI1", "s", "Data selection unit"); hdu->card("DSVAL1", "TABLE", "Data selection value"); hdu->card("DSREF1", ":GTI", "Data selection reference"); // Add acceptance cone selection hdu->card("DSTYP2", "POS(RA,DEC)", "Data selection type"); hdu->card("DSUNI2", "deg", "Data selection unit"); hdu->card("DSVAL2", dsval2, "Data selection value"); // Add energy range selection hdu->card("DSTYP3", "ENERGY", "Data selection type"); hdu->card("DSUNI3", "TeV", "Data selection unit"); hdu->card("DSVAL3", dsval3, "Data selection value"); // Set number of data selection keys hdu->card("NDSKEYS", 3, "Number of data selections"); } // endif: HDU was valid // Return return; } /*========================================================================== = = = Friends = = = ==========================================================================*/