/*************************************************************************** * GCOMObservation.cpp - COMPTEL Observation class * * ----------------------------------------------------------------------- * * copyright (C) 2012-2013 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ /** * @file GCOMObservation.cpp * @brief COMPTEL observation class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include #include "GObservationRegistry.hpp" #include "GException.hpp" #include "GFits.hpp" #include "GTools.hpp" #include "GCOMException.hpp" #include "GCOMObservation.hpp" #include "GCOMEventCube.hpp" #include "GCOMSupport.hpp" /* __ Globals ____________________________________________________________ */ const GCOMObservation g_obs_com_seed; const GObservationRegistry g_obs_com_registry(&g_obs_com_seed); /* __ Method name definitions ____________________________________________ */ #define G_RESPONSE "GCOMObservation::response(GResponse&)" #define G_READ "GCOMObservation::read(GXmlElement&)" #define G_WRITE "GCOMObservation::write(GXmlElement&)" #define G_LOAD_DRB "GCOMObservation::load_drb(std::string&)" #define G_LOAD_DRG "GCOMObservation::load_drg(std::string&)" /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ /* __ Debug definitions __________________________________________________ */ /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Void constructor * * Creates empty class instance. ***************************************************************************/ GCOMObservation::GCOMObservation(void) : GObservation() { // Initialise members init_members(); // Return return; } /***********************************************************************//** * @brief Instrument constructor * * @param[in] drename Event cube name. * @param[in] drbname Background cube name. * @param[in] drgname Geometry cube name. * @param[in] drxname Exposure map name. * * Creates COMPTEL observation by loading the following FITS files: * * DRE - Events cube * DRB - Background model cube * DRG - Geometry factors cube * DRX - Exposure map * * Each of the four files is mandatory. ***************************************************************************/ GCOMObservation::GCOMObservation(const std::string& drename, const std::string& drbname, const std::string& drgname, const std::string& drxname) : GObservation() { // Initialise members init_members(); // Load observation load(drename, drbname, drgname, drxname); // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] obs COMPTEL observation. * * Creates class instance by copying an existing COMPTEL observation. ***************************************************************************/ GCOMObservation::GCOMObservation(const GCOMObservation& obs) : GObservation(obs) { // Initialise members init_members(); // Copy members copy_members(obs); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GCOMObservation::~GCOMObservation(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] obs COMPTEL observation. * @return COMPTEL observation. * * Assign COMPTEL observation to this object. ***************************************************************************/ GCOMObservation& GCOMObservation::operator= (const GCOMObservation& obs) { // Execute only if object is not identical if (this != &obs) { // Copy base class members this->GObservation::operator=(obs); // Free members free_members(); // Initialise members init_members(); // Copy members copy_members(obs); } // endif: object was not identical // Return this object return *this; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear instance ***************************************************************************/ void GCOMObservation::clear(void) { // Free members free_members(); this->GObservation::free_members(); // Initialise members this->GObservation::init_members(); init_members(); // Return return; } /***********************************************************************//** * @brief Clone instance * * @return Pointer to deep copy of COMPTEL observation. ***************************************************************************/ GCOMObservation* GCOMObservation::clone(void) const { return new GCOMObservation(*this); } /***********************************************************************//** * @brief Set response function * * @param[in] rsp Response function. * * @exception GException::rsp_invalid_type * Specified response is not of type GCOMResponse. * * Sets the response function for the observation. The argument has to be of * type GCOMResponse, otherwise an exception is thrown. ***************************************************************************/ void GCOMObservation::response(const GResponse& rsp) { // Get pointer on COM response const GCOMResponse* comrsp = dynamic_cast(&rsp); if (comrsp == NULL) { throw GException::rsp_invalid_type(G_RESPONSE, typeid(&rsp).name(), "Expected type \"GCOMResponse\"."); } // Clone response function m_response = *comrsp; // Return return; } /***********************************************************************//** * @brief Set response function * * @param[in] iaqname Name of COMPTEL IAQ response file. * @param[in] caldb Optional name of calibration database. * * Sets the response function by loading the response information from an * IAQ file. ***************************************************************************/ void GCOMObservation::response(const std::string& iaqname, const std::string& caldb) { // Clear COM response function m_response.clear(); // Set calibration database m_response.caldb(caldb); // Load instrument response function m_response.load(iaqname); // Return return; } /***********************************************************************//** * @brief Read observation from XML element * * @param[in] xml XML element. * * @exception GException::xml_invalid_parnum * Invalid number of parameters found in XML element. * @exception GException::xml_invalid_parnames * Invalid parameter names found in XML element. * * Reads information for a COMPTEL observation from an XML element. * The expected format of the XML element is * * * * * * * * * * for a binned observation. ***************************************************************************/ void GCOMObservation::read(const GXmlElement& xml) { // Clear observation clear(); // Initialise FITS file names std::string drename; std::string drbname; std::string drgname; std::string drxname; std::string iaqname; // Extract instrument name m_instrument = xml.attribute("instrument"); // Determine number of parameter nodes in XML element int npars = xml.elements("parameter"); // Verify that XML element has exactly 5 parameters if (xml.elements() != 5 || npars != 5) { throw GException::xml_invalid_parnum(G_READ, xml, "COMPTEL observation requires exactly 5 parameters."); } // Extract parameters int npar[] = {0, 0, 0, 0, 0}; for (int i = 0; i < npars; ++i) { // Get parameter element const GXmlElement* par = xml.element("parameter", i); // Handle EventList if (par->attribute("name") == "DRE") { drename = par->attribute("file"); npar[0]++; } // Handle DRB else if (par->attribute("name") == "DRB") { drbname = par->attribute("file"); npar[1]++; } // Handle DRG else if (par->attribute("name") == "DRG") { drgname = par->attribute("file"); npar[2]++; } // Handle DRX else if (par->attribute("name") == "DRX") { drxname = par->attribute("file"); npar[3]++; } // Handle IAQ else if (par->attribute("name") == "IAQ") { iaqname = par->attribute("file"); npar[4]++; } } // endfor: looped over all parameters // Verify that all parameters were found if (npar[0] != 1 || npar[1] != 1 || npar[2] != 1 || npar[3] != 1 || npar[4] != 1) { throw GException::xml_invalid_parnames(G_READ, xml, "Require \"DRE\", \"DRB\", \"DRG\", \"DRX\" and \"IAQ\"" " parameters."); } // Load observation load(drename, drbname, drgname, drxname); // Load IAQ response(iaqname); // Return return; } /***********************************************************************//** * @brief Write observation into XML element * * @param[in] xml XML element. * * @exception GException::xml_invalid_parnum * Invalid number of parameters found in XML element. * @exception GException::xml_invalid_parnames * Invalid parameter names found in XML element. * * Writes information for a COMPTEL observation into an XML element. The * expected format of the XML element is * * * * * * * * * * for a binned observation. ***************************************************************************/ void GCOMObservation::write(GXmlElement& xml) const { // If XML element has 0 nodes then append 5 parameter nodes if (xml.elements() == 0) { xml.append(GXmlElement("parameter name=\"DRE\"")); xml.append(GXmlElement("parameter name=\"DRB\"")); xml.append(GXmlElement("parameter name=\"DRG\"")); xml.append(GXmlElement("parameter name=\"DRX\"")); xml.append(GXmlElement("parameter name=\"IAQ\"")); } // Verify that XML element has exactly 5 parameters if (xml.elements() != 5 || xml.elements("parameter") != 5) { throw GException::xml_invalid_parnum(G_WRITE, xml, "COMPTEL observation requires exactly 5 parameters."); } // Set or update parameter attributes int npar[] = {0, 0, 0, 0, 0}; for (int i = 0; i < 5; ++i) { // Get parameter element GXmlElement* par = xml.element("parameter", i); // Handle DRE if (par->attribute("name") == "DRE") { par->attribute("file", m_drename); npar[0]++; } // Handle DRB else if (par->attribute("name") == "DRB") { par->attribute("file", m_drbname); npar[1]++; } // Handle DRG else if (par->attribute("name") == "DRG") { par->attribute("file", m_drgname); npar[2]++; } // Handle DRX else if (par->attribute("name") == "DRX") { par->attribute("file", m_drxname); npar[3]++; } // Handle IAQ else if (par->attribute("name") == "IAQ") { std::string iaqname = ""; iaqname = m_response.iaqname(); par->attribute("file", iaqname); npar[4]++; } } // endfor: looped over all parameters // Verify that all required parameters are present if (npar[0] != 1 || npar[1] != 1 || npar[2] != 1 || npar[3] != 1 || npar[4] != 1) { throw GException::xml_invalid_parnames(G_WRITE, xml, "Require \"DRE\", \"DRB\", \"DRG\", \"DRX\" and \"IAQ\"" " parameters."); } // Return return; } /***********************************************************************//** * @brief Load data for binned analysis * * @param[in] drename Event cube name. * @param[in] drbname Background cube name. * @param[in] drgname Geometry cube name. * @param[in] drxname Exposure map name. * * Load event cube from DRE file, background model from DRB file, geometry * factors from DRG file and the exposure map from the DRX file. All files * are mandatory. ***************************************************************************/ void GCOMObservation::load(const std::string& drename, const std::string& drbname, const std::string& drgname, const std::string& drxname) { // Load DRE load_dre(drename); // Load DRB load_drb(drbname); // Load DRG load_drg(drgname); // Load DRX load_drx(drxname); // Return return; } /***********************************************************************//** * @brief Print observation information * * @param[in] chatter Chattiness (defaults to NORMAL). * @return String containing observation information. ***************************************************************************/ std::string GCOMObservation::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("=== GCOMObservation ==="); // Append information result.append("\n"+gammalib::parformat("Name")+name()); result.append("\n"+gammalib::parformat("Identifier")+id()); result.append("\n"+gammalib::parformat("Instrument")+instrument()); result.append("\n"+gammalib::parformat("Statistics")+statistics()); result.append("\n"+gammalib::parformat("Ontime")); result.append(gammalib::str(ontime())+" sec"); result.append("\n"+gammalib::parformat("Livetime")); result.append(gammalib::str(livetime())+" sec"); result.append("\n"+gammalib::parformat("Deadtime correction")); result.append(gammalib::str(m_deadc)); result.append("\n"+gammalib::parformat("Energy band")); result.append(gammalib::str(ewidth())+" MeV"); // Append pointing result.append("\n"+m_pointing.print(gammalib::reduce(chatter))); // Append response result.append("\n"+response().print(gammalib::reduce(chatter))); // Append events if (m_events != NULL) { result.append("\n"+m_events->print(gammalib::reduce(chatter))); } else { result.append("\n"+gammalib::parformat("Events")+"undefined"); } // Append DRB, DRG and DRX //result.append("\n"+m_drb.print(chatter)); //result.append("\n"+m_drg.print(chatter)); //result.append("\n"+m_drx.print(chatter)); } // endif: chatter was not silent // Return result return result; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GCOMObservation::init_members(void) { // Initialise members m_instrument = "COM"; m_drename.clear(); m_drbname.clear(); m_drgname.clear(); m_drxname.clear(); m_drb.clear(); m_drg.clear(); m_drx.clear(); m_pointing.clear(); m_response.clear(); m_obs_id = 0; m_ontime = 0.0; m_livetime = 0.0; m_deadc = 0.0; m_ewidth = 0.0; // Return return; } /***********************************************************************//** * @brief Copy class members * * @param[in] obs COMPTEL observation. ***************************************************************************/ void GCOMObservation::copy_members(const GCOMObservation& obs) { // Copy members m_instrument = obs.m_instrument; m_drename = obs.m_drename; m_drbname = obs.m_drbname; m_drgname = obs.m_drgname; m_drxname = obs.m_drxname; m_drb = obs.m_drb; m_drg = obs.m_drg; m_drx = obs.m_drx; m_response = obs.m_response; m_pointing = obs.m_pointing; m_obs_id = obs.m_obs_id; m_ontime = obs.m_ontime; m_livetime = obs.m_livetime; m_deadc = obs.m_deadc; m_ewidth = obs.m_ewidth; // Return return; } /***********************************************************************//** * @brief Delete class members ***************************************************************************/ void GCOMObservation::free_members(void) { // Return return; } /***********************************************************************//** * @brief Load event cube data from DRE file * * @param[in] drename DRE filename. ***************************************************************************/ void GCOMObservation::load_dre(const std::string& drename) { // Delete any existing event container (do not call clear() as we do not // want to delete the response function) if (m_events != NULL) delete m_events; m_events = NULL; // Allocate event cube m_events = new GCOMEventCube; // Open FITS file GFits file(drename); // Read event cube m_events->read(file); // Read observation attributes from primary extension GFitsHDU* hdu = file[0]; read_attributes(hdu); // Close FITS file file.close(); // Store event filename m_drename = drename; // Return return; } /***********************************************************************//** * @brief Load background model from DRB file * * @param[in] drbname DRB filename. * * @exception GCOMException::incompatible_dataspace * DRB data space incompatible with DRE data space. * * Load the background model from the primary image of the specified FITS * file. ***************************************************************************/ void GCOMObservation::load_drb(const std::string& drbname) { // Open FITS file GFits file(drbname); // Get image const GFitsImage& image = *file.image("Primary"); // Load background model as sky map m_drb.read(image); // Correct WCS projection (HEASARC data format kluge) com_wcs_mer2car(m_drb); // Close FITS file file.close(); // Check map dimensions if (!check_map(m_drb)) { throw GCOMException::incompatible_dataspace(G_LOAD_DRB, "DRB data cube incompatible with DRE data cube."); } // Store DRB filename m_drbname = drbname; // Return return; } /***********************************************************************//** * @brief Load geometry factors from DRG file * * @param[in] drgname DRG filename. * * @exception GCOMException::incompatible_dataspace * DRG data space incompatible with DRE data space. * * Load the geometry factors from the primary image of the specified FITS * file. ***************************************************************************/ void GCOMObservation::load_drg(const std::string& drgname) { // Open FITS file GFits file(drgname); // Get image const GFitsImage& image = *file.image("Primary"); // Load geometry factors as sky map m_drg.read(image); // Correct WCS projection (HEASARC data format kluge) com_wcs_mer2car(m_drg); // Close FITS file file.close(); // Check map dimensions if (!check_map(m_drg)) { throw GCOMException::incompatible_dataspace(G_LOAD_DRG, "DRG data cube incompatible with DRE data cube."); } // Store DRG filename m_drgname = drgname; // Return return; } /***********************************************************************//** * @brief Load exposure from DRX file * * @param[in] drxname DRX filename. * * Load the exposure map from the primary image of the specified FITS file. ***************************************************************************/ void GCOMObservation::load_drx(const std::string& drxname) { // Open FITS file GFits file(drxname); // Get HDU const GFitsImage& image = *file.image("Primary"); // Load exposure map as sky map m_drx.read(image); // Correct WCS projection (HEASARC data format kluge) com_wcs_mer2car(m_drx); // Close FITS file file.close(); // Store DRX filename m_drxname = drxname; // Return return; } /***********************************************************************//** * @brief Check if sky map is compatible with event cube * * @param[in] map Sky map. * @return True if map is compatible, false otherwise. * * Compares the dimension and the WCS definition of a sky map to that of the * event cube. If both are identical, true is returned, false otherwise. ***************************************************************************/ bool GCOMObservation::check_map(const GSkymap& map) const { // Get reference to event cube map const GSkymap& ref = dynamic_cast(m_events)->map(); // Compare dimensions bool same_dimension = ((map.nx() == ref.nx()) && (map.ny() == ref.ny()) && (map.nmaps() == ref.nmaps())); // Compare projections bool same_projection = (*(map.projection()) == *(ref.projection())); // Return return (same_dimension && same_projection); } /***********************************************************************//** * @brief Read observation attributes * * @param[in] hdu FITS HDU pointer * * Reads COM observation attributes from HDU. Mandatory attributes are * * RA_SCZ - Right Ascension of pointing * DEC_SCZ - Declination of pointing * TSTART - Start time (days) * TSTOP - Stop time (days) * E_MIN - Minimum energy (MeV) * E_MAX - Maximum energy (MeV) * * and optional attributes are * * OBS_ID - Observation identifier * OBJECT - Object * * Based on TSTART and TSTOP the ontime is computed. The deadtime fraction * is fixed to 15%, hence DEADC=0.85. The livetime is then computed by * multiplying the deadtime correction by the ontime, i.e. * LIVETIME = ONTIME * DEADC. * * Nothing is done if the HDU pointer is NULL. ***************************************************************************/ void GCOMObservation::read_attributes(const GFitsHDU* hdu) { // Continue only if HDU is valid if (hdu != NULL) { // Read observation information m_obs_id = (hdu->has_card("OBS_ID")) ? hdu->real("OBS_ID") : 0; m_name = (hdu->has_card("OBJECT")) ? hdu->string("OBJECT") : "unknown"; // Compute ontime double tstart = hdu->real("TSTART"); double tstop = hdu->real("TSTOP"); m_ontime = (tstop - tstart) * 86400.0; m_deadc = 0.85; m_livetime = m_deadc * m_ontime; // Compute energy width double emin = hdu->real("E_MIN"); double emax = hdu->real("E_MAX"); m_ewidth = emax - emin; // Set pointing information double ra_scz = hdu->real("RA_SCZ"); double dec_scz = hdu->real("DEC_SCZ"); m_pointing.radec_deg(ra_scz, dec_scz); } // endif: HDU was valid // Return return; } /***********************************************************************//** * @brief Write observation attributes * * @param[in] hdu FITS HDU pointer * * Nothing is done if the HDU pointer is NULL. * * @todo Implement method. ***************************************************************************/ void GCOMObservation::write_attributes(GFitsHDU* hdu) const { // Continue only if HDU is valid if (hdu != NULL) { } // endif: HDU was valid // Return return; }