示例#1
0
/***********************************************************************//**
 * @brief Read GTIs from HDU.
 *
 * @param[in] hdu GTI table.
 *
 * Reads the Good Time Intervals from the GTI extension. Since the Fermi
 * LAT Science Tools do not set corrently the time reference for source
 * maps, the method automatically adds this missing information so that
 * the time reference is set correctly. The time reference that is assumed
 * for Fermi LAT is
 *
 *      MJDREFI 51910
 *      MJDREFF 0.00074287037037037
 * 
 ***************************************************************************/
void GLATEventCube::read_gti(const GFitsTable& hdu)
{
    // Work on a local copy of the HDU to make the kluge work
    GFitsTable* hdu_local = hdu.clone();

    // Kluge: modify HDU table in case that the MJDREF header keyword is
    // blank. This happens for Fermi LAT source maps since the Science
    // Tools do not properly write out the time reference. We hard-code
    // here the Fermi LAT time reference to circumvent the problem.
    if (hdu.has_card("MJDREF")) {
        if (gammalib::strip_whitespace(hdu.string("MJDREF")).empty()) {
            hdu_local->header().remove("MJDREF");
            hdu_local->card("MJDREFI", 51910,
                            "Integer part of MJD reference");
            hdu_local->card("MJDREFF", 0.00074287037037037,
                            "Fractional part of MJD reference");
        }
    }

    // Read Good Time Intervals
    m_gti.read(*hdu_local);

    // Set time
    set_times();

    // Return
    return;
}
示例#2
0
/***********************************************************************//**
 * @brief Write response table into FITS table
 *
 * @param[in] hdu Fits table HDU.
 ***************************************************************************/
void GLATResponseTable::write(GFitsTable& hdu) const
{
    // Allocate floating point vector columns
    GFitsTableFloatCol col_energy_lo = GFitsTableFloatCol("ENERG_LO",  1, m_energy_num);
    GFitsTableFloatCol col_energy_hi = GFitsTableFloatCol("ENERG_HI",  1, m_energy_num);
    GFitsTableFloatCol col_ctheta_lo = GFitsTableFloatCol("CTHETA_LO", 1, m_ctheta_num);
    GFitsTableFloatCol col_ctheta_hi = GFitsTableFloatCol("CTHETA_HI", 1, m_ctheta_num);

    // Set column values
    for (int i = 0; i < m_energy_num; ++i) {
        col_energy_lo(0,i) = m_energy_lo[i];
        col_energy_hi(0,i) = m_energy_hi[i];
    }
    for (int i = 0; i < m_ctheta_num; ++i) {
        col_ctheta_lo(0,i) = m_ctheta_lo[i];
        col_ctheta_hi(0,i) = m_ctheta_hi[i];
    }

    // Append columns to boundary table
    hdu.append(col_energy_lo);
    hdu.append(col_energy_hi);
    hdu.append(col_ctheta_lo);
    hdu.append(col_ctheta_hi);

    // Return
    return;
}
示例#3
0
/***********************************************************************//**
 * @brief Check input filename
 *
 * @param[in] filename File name.
 *
 * This method checks if the input FITS file is correct.
 ***************************************************************************/
std::string ctselect::check_infile(const std::string& filename) const
{
    // Initialise message string
    std::string message = "";

    // Open FITS file
    GFits file(filename);

    // Check for EVENTS HDU
    GFitsTable* table = NULL;
    try {

        // Get pointer to FITS table
        table = file.table("EVENTS");

        // Initialise list of missing columns
        std::vector<std::string> missing;

        // Check for existence of TIME column
        if (!table->hascolumn("TIME")) {
            missing.push_back("TIME");
        }

        // Check for existence of ENERGY column
        if (!table->hascolumn("ENERGY")) {
            missing.push_back("ENERGY");
        }

        // Check for existence of RA column
        if (!table->hascolumn("RA")) {
            missing.push_back("RA");
        }

        // Check for existence of DEC column
        if (!table->hascolumn("DEC")) {
            missing.push_back("DEC");
        }

        // Set error message for missing columns
        if (missing.size() > 0) {
            message = "The following columns are missing in the"
                      " \"EVENTS\" extension of input file \""
                    + m_outfile + "\": ";
            for (int i = 0; i < missing.size(); ++i) {
                message += "\"" + missing[i] + "\"";
                if (i < missing.size()-1) {
                    message += ", ";
                }
            }
        }

    }
    catch (GException::fits_hdu_not_found& e) {
        message = "No \"EVENTS\" extension found in input file \""
                + m_outfile + "\".";
    }

    // Return
    return message;
}
示例#4
0
文件: GGti.cpp 项目: TarekHC/gammalib
/***********************************************************************//**
 * @brief Read Good Time Intervals and time reference from FITS table
 *
 * @param[in] table FITS table.
 *
 * Reads the Good Time Intervals and time reference from a FITS table.
 ***************************************************************************/
void GGti::read(const GFitsTable& table)
{
    // Free members
    free_members();

    // Initialise attributes
    init_members();

    // Read time reference
    m_reference.read(table);

    // Extract GTI information from FITS table
    m_num = table.integer("NAXIS2");
    if (m_num > 0) {

        // Set GTIs
        m_start = new GTime[m_num];
        m_stop  = new GTime[m_num];
        for (int i = 0; i < m_num; ++i) {
            m_start[i].set(table["START"]->real(i), m_reference);
            m_stop[i].set(table["STOP"]->real(i), m_reference);
        }

        // Set attributes
        set_attributes();

    }

    // Return
    return;
}
示例#5
0
/***********************************************************************//**
 * @brief Load livetime cube from FITS file
 *
 * @param[in] table FITS table.
 ***************************************************************************/
void GLATLtCubeMap::read(const GFitsTable& table)
{
    // Clear object
    clear();

    // Load skymap
    m_map.read(table);

    // Set costheta binning scheme
    std::string scheme =
        gammalib::strip_whitespace(gammalib::toupper(table.string("THETABIN")));
    m_sqrt_bin = (scheme == "SQRT(1-COSTHETA)");

    // Read attributes
    m_num_ctheta = table.integer("NBRBINS");
    m_num_phi    = table.integer("PHIBINS");
    m_min_ctheta = table.real("COSMIN");

    // Return
    return;
}
示例#6
0
/***********************************************************************//**
 * @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->length();

    // 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;
}
示例#7
0
/***********************************************************************//**
 * @brief Read effective area from FITS table
 *
 * @param[in] hdu FITS table.
 *
 * @exception GLATException::inconsistent_response
 *            Inconsistent response table encountered
 *
 * The effective area is converted into units of cm2.
 ***************************************************************************/
void GLATAeff::read_aeff(const GFitsTable& hdu)
{
    // Clear array
    m_aeff.clear();

    // Get energy and cos theta bins in response table
    m_aeff_bins.read(hdu);

    // Set minimum cos(theta)
    m_min_ctheta = m_aeff_bins.costheta_lo(0);

    // Continue only if there are effective area bins
    int size = m_aeff_bins.size();
    if (size > 0) {

        // Allocate arrays
        m_aeff.reserve(size);

        // Get pointer to effective area column
        const GFitsTableCol* ptr = hdu["EFFAREA"];

        // Check consistency of effective area table
        int num = ptr->number();
        if (num != size) {
            throw GLATException::inconsistent_response(G_READ_AEFF, num, size);
        }

        // Copy data and convert from m2 into cm2
        for (int i = 0; i < size; ++i) {
            m_aeff.push_back(ptr->real(0,i) * 1.0e4);
        }

    } // endif: there were effective area bins

    // Set detector section using the DETNAM keyword in the HDU
    std::string detnam = gammalib::strip_whitespace(hdu.string("DETNAM"));
    m_front            = (detnam == "FRONT");
    m_back             = (detnam == "BACK");

    // Return
    return;
}
示例#8
0
/***********************************************************************//**
 * @brief Read energy boundaries from FITS table
 *
 * @param[in] table FITS table.
 *
 * Reads the energy boundaries from a FITS table. The method interprets the
 * energy units provide in the FITS header. If no energy units are found it
 * is assumed that the energies are stored in units of keV.
 ***************************************************************************/
void GEbounds::read(const GFitsTable& table)
{
    // Free members
    free_members();

    // Initialise attributes
    init_members();

    // Extract energy boundary information from FITS table
    m_num = table.integer("NAXIS2");
    if (m_num > 0) {

        // Allocate memory
        m_min = new GEnergy[m_num];
        m_max = new GEnergy[m_num];

        // Get units
        std::string emin_unit = table["E_MIN"]->unit();
        std::string emax_unit = table["E_MAX"]->unit();
        if (emin_unit.empty()) {
            emin_unit = "keV";
        }
        if (emax_unit.empty()) {
            emax_unit = "keV";
        }

        // Copy information
        for (int i = 0; i < m_num; ++i) {
            m_min[i](table["E_MIN"]->real(i), emin_unit);
            m_max[i](table["E_MAX"]->real(i), emax_unit);
        }

        // Set attributes
        set_attributes();

    } // endif: there were channels to read

    // Return
    return;
}
示例#9
0
/***********************************************************************//**
 * @brief Read spectrum from FITS file
 *
 * @param[in] table FITS table.
 *
 * @exception GMWLException::bad_file_format
 *            Table has invalid format
 *
 * Read spectrum from FITS table. The table is expected to be in one of the
 * three following formats:
 * 2 columns: energy, flux
 * 3 columns: energy, flux, e_flux
 * 4 columns or more:  energy, e_energy, flux, e_flux, ...
 *
 * @todo Investigate whether we can exploit UCDs for identifying the correct
 * columns or for determining the units.
 ***************************************************************************/
void GMWLSpectrum::read_fits(const GFitsTable& table)
{
    // Reset spectrum
    m_data.clear();

    // Initialise column pointers columns
    const GFitsTableCol* c_energy     = NULL;
    const GFitsTableCol* c_energy_err = NULL;
    const GFitsTableCol* c_flux       = NULL;
    const GFitsTableCol* c_flux_err   = NULL;

    // Extract column pointers
    if (table.ncols() == 2) {
        c_energy = table[0];
        c_flux   = table[1];
    }
    else if (table.ncols() == 3) {
        c_energy   = table[0];
        c_flux     = table[1];
        c_flux_err = table[2];
    }
    else if (table.ncols() > 3) {
        c_energy     = table[0];
        c_energy_err = table[1];
        c_flux       = table[2];
        c_flux_err   = table[3];
    }
    else {
        throw GMWLException::bad_file_format(G_READ_FITS,
                             "At least 2 columns are expected is table \""+
                              table.extname()+"\".");
    }

    // Read spectral points and add to spectrum
    for (int i = 0; i < table.nrows(); ++i) {
        GMWLDatum datum;
        if (c_energy     != NULL) {
            datum.m_eng = conv_energy(c_energy->real(i), c_energy->unit());
        }
        if (c_energy_err != NULL) {
            datum.m_eng_err = conv_energy(c_energy_err->real(i), c_energy->unit());
        }
        if (c_flux       != NULL) {
            datum.m_flux = conv_flux(datum.m_eng, c_flux->real(i), c_flux->unit());
        }
        if (c_flux_err   != NULL) {
            datum.m_flux_err = conv_flux(datum.m_eng, c_flux_err->real(i), c_flux_err->unit());
        }
        m_data.push_back(datum);
    }

    // Get telescope name
    if (table.has_card("TELESCOP")) {
        m_telescope = table.string("TELESCOP");
    }
    else {
        m_telescope = "unknown";
    }

    // Get instrument name
    if (table.has_card("INSTRUME")) {
        m_instrument = table.string("INSTRUME");
    }
    else {
        m_instrument = "unknown";
    }

    // Set energy boundaries
    set_ebounds();

    // Return
    return;
}
示例#10
0
/***********************************************************************//**
 * @brief Read Auxiliary Response File
 *
 * @param[in] table ARF FITS table.
 *
 * Reads the Auxiliary Response File from a FITS table. The true energy
 * boundaries are expected in the `ENERG_LO` and `ENERG_HI` columns, the
 * response information is expected in the `SPECRESP` column.
 *
 * The method will analyze the unit of the `SPECRESP` column, and if either
 * `m^2` or `m2` are encountered, multiply the values of the column by
 * \f$10^4\f$ to convert the response into units of \f$cm^2\f$. Units of the
 * `ENERG_LO` and `ENERG_HI` columns are also interpreted for conversion.
 *
 * See
 * http://heasarc.gsfc.nasa.gov/docs/heasarc/caldb/docs/memos/cal_gen_92_002/cal_gen_92_002.html#tth_sEc4
 * for details about the Auxiliary Response File format.
 ***************************************************************************/
void GArf::read(const GFitsTable& table)
{
    // Clear members
    clear();

    // Get pointer to data columns
    const GFitsTableCol* energy_lo = table["ENERG_LO"];
    const GFitsTableCol* energy_hi = table["ENERG_HI"];
    const GFitsTableCol* specresp  = table["SPECRESP"];

    // Determine effective area conversion factor. Internal
    // units are cm^2
    std::string u_specresp =
        gammalib::tolower(gammalib::strip_whitespace(specresp->unit()));
    double      c_specresp = 1.0;
    if (u_specresp == "m^2" || u_specresp == "m2") {
        c_specresp = 10000.0;
    }

    // Extract number of energy bins
    int num = energy_lo->length();

    // Set energy bins
    for (int i = 0; i < num; ++i) {

        // Append energy bin
        GEnergy emin(energy_lo->real(i), energy_lo->unit());
        GEnergy emax(energy_hi->real(i), energy_hi->unit());
        m_ebounds.append(emin, emax);

        // Append effective area value
        double aeff = specresp->real(i) * c_specresp;
        m_specresp.push_back(aeff);

    } // endfor: looped over energy bins

    // Read any additional columns
    for (int icol = 0; icol < table.ncols(); ++icol) {

        // Fall through if the column is a standard column
        std::string colname(table[icol]->name());
        if ((colname == "ENERG_LO") ||
                (colname == "ENERG_HI") ||
                (colname == "SPECRESP")) {
            continue;
        }

        // Get pointer to column
        const GFitsTableCol* column = table[icol];

        // Set column vector
        std::vector<double> coldata;
        for (int i = 0; i < num; ++i) {
            coldata.push_back(column->real(i));
        }

        // Append column
        append(colname, coldata);

    } // endfor: looped over all additional columns

    // Return
    return;
}
示例#11
0
/***********************************************************************//**
 * @brief Read LAT events from FITS table.
 *
 * @param[in] table Event table.
 *
 * Read the LAT events from the event table.
 ***************************************************************************/
void GLATEventList::read_events(const GFitsTable& table)
{
    // Clear existing events
    m_events.clear();

    // Allocate space for keyword name
    char keyword[10];

    // Extract number of events in FT1 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
        const GFitsTableCol* ptr_time    = table["TIME"];
        const GFitsTableCol* ptr_energy  = table["ENERGY"];
        const GFitsTableCol* ptr_ra      = table["RA"];
        const GFitsTableCol* ptr_dec     = table["DEC"];
        const GFitsTableCol* ptr_theta   = table["THETA"];
        const GFitsTableCol* ptr_phi     = table["PHI"];
        const GFitsTableCol* ptr_zenith  = table["ZENITH_ANGLE"];
        const GFitsTableCol* ptr_azimuth = table["EARTH_AZIMUTH_ANGLE"];
        const GFitsTableCol* ptr_eid     = table["EVENT_ID"];
        const GFitsTableCol* ptr_rid     = table["RUN_ID"];
        const GFitsTableCol* ptr_recon   = table["RECON_VERSION"];
        const GFitsTableCol* ptr_calib   = table["CALIB_VERSION"];
        const GFitsTableCol* ptr_class   = table["EVENT_CLASS"];
        const GFitsTableCol* ptr_conv    = table["CONVERSION_TYPE"];
        const GFitsTableCol* ptr_ltime   = table["LIVETIME"];

        // Copy data from columns into GLATEventAtom objects
        GLATEventAtom event;
        for (int i = 0; i < num; ++i) {
            event.m_time.set(ptr_time->real(i), m_gti.reference());
            event.m_energy.MeV(ptr_energy->real(i));
            event.m_dir.dir().radec_deg(ptr_ra->real(i), ptr_dec->real(i));
            event.m_theta               = ptr_theta->real(i);
            event.m_phi                 = ptr_phi->real(i);
            event.m_zenith_angle        = ptr_zenith->real(i);
            event.m_earth_azimuth_angle = ptr_azimuth->real(i);
            event.m_event_id            = ptr_eid->integer(i);
            event.m_run_id              = ptr_rid->integer(i);
            event.m_recon_version       = ptr_recon->integer(i);
            event.m_calib_version[0]    = ptr_calib->integer(i,0);
            event.m_calib_version[1]    = ptr_calib->integer(i,1);
            event.m_calib_version[2]    = ptr_calib->integer(i,2);
            event.m_event_class         = ptr_class->integer(i);
            event.m_conversion_type     = ptr_conv->integer(i);
            event.m_livetime            = ptr_ltime->real(i);
            m_events.push_back(event);
        }

        // Extract number of diffuse response labels
        int num_difrsp = table.integer("NDIFRSP");

        // Allocate diffuse response components
        if (num_difrsp > 0) {

            // Reserve space
            m_difrsp_label.reserve(num_difrsp);

            // Allocate components
            for (int i = 0; i < num; ++i) {
                m_events[i].m_difrsp = new double[num_difrsp];
            }

            // Load diffuse columns
            for (int k = 0; k < num_difrsp; ++k) {

                // Set keyword name
                std::sprintf(keyword, "DIFRSP%d", k);

                // Get DIFRSP label
                if (table.has_card(std::string(keyword))) {
                    m_difrsp_label.push_back(table.string(std::string(keyword)));
                }
                else {
                    m_difrsp_label.push_back("NONE");
                }

                // Get column pointer
                const GFitsTableCol* ptr_dif = table[std::string(keyword)];

                // Copy data from columns into GLATEventAtom objects
                for (int i = 0; i < num; ++i) {
                    m_events[i].m_difrsp[k] = ptr_dif->real(i);
                }

            } // endfor: looped over diffuse columns

        } // endif: diffuse components found

    } // endif: events found

    // Return
    return;
}
示例#12
0
/***********************************************************************//**
 * @brief Read CTA PSF vector
 *
 * @param[in] table FITS table.
 *
 * This method reads a CTA PSF vector from the FITS HDU. Note that the
 * energies are converted to TeV. Conversion is done based on the units
 * provided for the energy columns. Units that are recognized are 'keV',
 * 'MeV', 'GeV', and 'TeV' (case independent).
 *
 * The Gaussian width parameter may be either given in 68% containment radius
 * (R68) or in sigma (ANGRES40; corresponding to a 38% containment radius).
 * The former format is used for CTA and H.E.S.S. data, the latter for
 * MAGIC data. All these things should be more uniform once we have a well
 * defined format.
 ***************************************************************************/
void GCTAPsfVector::read(const GFitsTable& table)
{
    // Clear arrays
    m_logE.clear();
    m_r68.clear();
    m_sigma.clear();

    // Set conversion factor from 68% containment radius to 1 sigma
    const double conv = 0.6624305 * gammalib::deg2rad;

    // Get pointers to table columns
    const GFitsTableCol* energy_lo = table["ENERG_LO"];
    const GFitsTableCol* energy_hi = table["ENERG_HI"];

    // Handle various data formats (H.E.S.S. and MAGIC)
    const GFitsTableCol* r68;
    double               r68_scale = 1.0;
    if (table.contains("R68")) {
        r68 = table["R68"];
    }
    else {
        r68 = table["ANGRES40"];
        r68_scale = 1.0 / 0.6624305; // MAGIC PSF is already 1 sigma
    }

    // Determine unit conversion factors (default: TeV)
    std::string u_energy_lo = gammalib::tolower(gammalib::strip_whitespace(energy_lo->unit()));
    std::string u_energy_hi = gammalib::tolower(gammalib::strip_whitespace(energy_hi->unit()));
    double c_energy_lo = 1.0;
    double c_energy_hi = 1.0;
    if (u_energy_lo == "kev") {
        c_energy_lo = 1.0e-9;
    }
    else if (u_energy_lo == "mev") {
        c_energy_lo = 1.0e-6;
    }
    else if (u_energy_lo == "gev") {
        c_energy_lo = 1.0e-3;
    }
    if (u_energy_hi == "kev") {
        c_energy_hi = 1.0e-9;
    }
    else if (u_energy_hi == "mev") {
        c_energy_hi = 1.0e-6;
    }
    else if (u_energy_hi == "gev") {
        c_energy_hi = 1.0e-3;
    }

    // Extract number of energy bins
    int num = energy_lo->length();

    // Set nodes
    for (int i = 0; i < num; ++i) {
    
        // Compute log10 mean energy in TeV
        double e_min = energy_lo->real(i) * c_energy_lo;
        double e_max = energy_hi->real(i) * c_energy_hi;
        double logE  = 0.5 * (log10(e_min) + log10(e_max));
        
        // Extract r68 value and scale as required
        double r68_value = r68->real(i) * r68_scale;
        
        // Store log10 mean energy and r68 value
        m_logE.append(logE);
        m_r68.push_back(r68_value);
        m_sigma.push_back(r68_value*conv);

    } // endfor: looped over nodes
    
    // Return
    return;
}
示例#13
0
/***********************************************************************//**
 * @brief Read column names from FITS HDU
 *
 * @param[in] hdu Response table HDU.
 *
 * @exception GCTAException::bad_rsp_table_format
 *            Bad response table format encountered in FITS HDU.
 *
 * Read the response table column names from the HDU. Column names
 * terminating with "_LO" and "_HI" define the parameter space axes, while
 * all other column names define response parameter data cubes. It is
 * assumed that parameter space axes are given in subsequent order, with
 * the first column corresponding to the lower bin boundaries of the first
 * axis. Lower bin boundaries are indicated by the "_LO" termination. Upper
 * bin boundaries are assumed to follow immediately the lower bin boundaires
 * and are designated by the "_HI" termination.
 *
 * This method sets the following members:
 *    m_colname_lo - Column names of lower boundaries
 *    m_colname_hi - Column names of upper boundaries
 *    m_colname_par - Column names of parameters
 *    m_naxes - Number of axes
 *    m_npars - Number of parameters
 *
 * In case that the HDU pointer is not valid (NULL), this method clears the
 * column names and does nothing else.
 *
 * @todo Implement exceptions for invalid HDU format
 ***************************************************************************/
void GCTAResponseTable::read_colnames(const GFitsTable& hdu)
{
    // Clear column name arrays
    m_naxes = 0;
    m_npars = 0;
    m_colname_lo.clear();
    m_colname_hi.clear();
    m_colname_par.clear();
    
    // Initialise search mode. There are three search modes:
    // 0 - we're looking for the next axis by searching for a column
    //     terminating with "_LO"
    // 1 - we're looking for the upper boundary of an axis, terminating
    //     with "_HI"
    // 2 - we're looking for a parameter column
    int         mode = 0;
    std::string lo_column;
    std::string next_column;

    // Extract column names for all axes
    for (int i = 0; i < hdu.ncols(); ++i) {

        // Get column name
        std::string colname = hdu[i]->name();

        // If we search for a "_LO" column, check if we have one. If one
        // is found, change the search mode to 1 and set the expected name
        // for the "_HI" column. If none is found, change the search
        // mode to 2 since from now on we should only have parameter
        // columns.
        if (mode == 0) {
            size_t pos = colname.rfind("_LO");
            if (pos != std::string::npos) {
                mode        = 1;
                lo_column   = colname;
                next_column = colname.substr(0, pos) + "_HI";
            }
            else {
                if (colname.rfind("_HI") != std::string::npos) {
                    std::string message = "Column '" +
                                          colname +
                                          "' encountered without"
                                          " preceeding '_LO' column.";
                    throw GCTAException::bad_rsp_table_format(G_READ_COLNAMES,
                                                              message);
                }
                else {
                    mode = 2;
                    m_colname_par.push_back(colname);
                }
            }
        }

        // If we search for a "_HI" column, check if we have the
        // expected column name. If this is the case, switch back to
        // search mode 0 to get the next "_LO" column. Otherwise
        // throw an exception.
        else if (mode == 1) {
            if (colname == next_column) {
                mode = 0;
                m_colname_lo.push_back(lo_column);
                m_colname_hi.push_back(next_column);
            }
            else {
                std::string message = "Expected column '" +
                                      next_column +
                                      "' not found. '_HI' columns have"
                                      " to be placed immediately after"
                                      " corresponding '_LO' columns.";
                throw GCTAException::bad_rsp_table_format(G_READ_COLNAMES,
                                                          message);
            }
        }

        // If we search for a parameter column, make sure that we have
        // neither a "_LO" nor a "_HI" column.
        else {
            if (colname.rfind("_LO") != std::string::npos) {
                std::string message = "Column '" +
                                      colname +
                                      "' found. All '_LO' columns have to"
                                      " be placed before the parameter"
                                      " columns.";
                throw GCTAException::bad_rsp_table_format(G_READ_COLNAMES,
                                                          message);
            }
            else if (colname.rfind("_HI") != std::string::npos) {
                std::string message = "Column '" +
                                      colname +
                                      "' found. All '_HI' columns have to"
                                      " be placed before the parameter"
                                      " columns.";
                throw GCTAException::bad_rsp_table_format(G_READ_COLNAMES,
                                                          message);
            }
            else {
                m_colname_par.push_back(colname);
            }
        }

    } // endfor: looped over all column names

    // Store number of axes
    m_naxes = m_colname_lo.size();

    // Store number of parameters
    m_npars = m_colname_par.size();

    // Return
    return;
}