Commit c93d18a699e0ced3c1e9cf6f0d43f88bf9d7b731

Authored by Jürgen Knödlseder
1 parent 9faf87f1

Correct handling of FITS extension names.

inst/cta/src/GCTAAeff2D.cpp
... ... @@ -395,8 +395,9 @@ void GCTAAeff2D::save(const GFilename& filename, const bool& clobber) const
395 395 // Get extension name
396 396 std::string extname = filename.extname("EFFECTIVE AREA");
397 397  
398   - // Open or create FITS file (without extension)
399   - GFits fits(filename, true);
  398 + // Open or create FITS file (without extension name since the requested
  399 + // extension may not yet exist in the file)
  400 + GFits fits(filename.url(), true);
400 401  
401 402 // Remove extension if it exists already
402 403 if (fits.contains(extname)) {
... ...
inst/cta/src/GCTABackground3D.cpp
... ... @@ -448,8 +448,9 @@ void GCTABackground3D::save(const GFilename& filename, const bool& clobber) cons
448 448 // Get extension name
449 449 std::string extname = filename.extname("BACKGROUND");
450 450  
451   - // Open or create FITS file
452   - GFits fits(filename, true);
  451 + // Open or create FITS file (without extension name since the requested
  452 + // extension may not yet exist in the file)
  453 + GFits fits(filename.url(), true);
453 454  
454 455 // Remove extension if it exists already
455 456 if (fits.contains(extname)) {
... ...
inst/cta/src/GCTAEdisp2D.cpp
... ... @@ -461,8 +461,9 @@ void GCTAEdisp2D::save(const GFilename& filename, const bool& clobber) const
461 461 // Get extension name
462 462 std::string extname = filename.extname("ENERGY DISPERSION");
463 463  
464   - // Open or create FITS file
465   - GFits fits(filename, true);
  464 + // Open or create FITS file (without extension name since the requested
  465 + // extension may not yet exist in the file)
  466 + GFits fits(filename.url(), true);
466 467  
467 468 // Remove extension if it exists already
468 469 if (fits.contains(extname)) {
... ...
inst/cta/src/GCTAEventList.cpp
... ... @@ -303,8 +303,9 @@ void GCTAEventList::load(const GFilename& filename)
303 303 void GCTAEventList::save(const GFilename& filename,
304 304 const bool& clobber) const
305 305 {
306   - // Open or create FITS file
307   - GFits fits(filename, true);
  306 + // Open or create FITS file (without extension name since the requested
  307 + // extension may not yet exist in the file)
  308 + GFits fits(filename.url(), true);
308 309  
309 310 // Write event list
310 311 write(fits);
... ...
inst/cta/src/GCTAPsf2D.cpp
... ... @@ -425,8 +425,9 @@ void GCTAPsf2D::save(const GFilename& filename, const bool& clobber) const
425 425 // Get extension name
426 426 std::string extname = filename.extname("POINT SPREAD FUNCTION");
427 427  
428   - // Open or create FITS file
429   - GFits fits(filename, true);
  428 + // Open or create FITS file (without extension name since the requested
  429 + // extension may not yet exist in the file)
  430 + GFits fits(filename.url(), true);
430 431  
431 432 // Remove extension if it exists already
432 433 if (fits.contains(extname)) {
... ...
inst/cta/src/GCTAPsfKing.cpp
... ... @@ -423,8 +423,9 @@ void GCTAPsfKing::save(const GFilename& filename, const bool& clobber) const
423 423 // Get extension name
424 424 std::string extname = filename.extname("POINT SPREAD FUNCTION");
425 425  
426   - // Open or create FITS file
427   - GFits fits(filename, true);
  426 + // Open or create FITS file (without extension name since the requested
  427 + // extension may not yet exist in the file)
  428 + GFits fits(filename.url(), true);
428 429  
429 430 // Remove extension if it exists already
430 431 if (fits.contains(extname)) {
... ...
src/obs/GEbounds.cpp
... ... @@ -599,31 +599,37 @@ void GEbounds::load(const GFilename& filename)
599 599 * @brief Save energy boundaries into FITS file
600 600 *
601 601 * @param[in] filename FITS file name.
602   - * @param[in] clobber Overwrite any existing file (default: false)?
603   - * @param[in] unit Energy units (default: "keV")
  602 + * @param[in] clobber Overwrite an existing energy boundaries extension?
  603 + * @param[in] unit Energy unit
604 604 *
605 605 * Saves energy boundaries into a FITS file. If a file with the given
606 606 * @p filename does not yet exist it will be created, otherwise the method
607   - * opens the existing file. The method will create (or replace an existing)
608   - * energy boundaries extension. The extension name can be specified as part
609   - * of the @p filename, or if no extension name is given, is assumed to be
610   - * "EBOUNDS".
  607 + * opens the existing file. Energy boundaries can only be appended to an
  608 + * existing file if the @p clobber flag is set to `true` (otherwise an
  609 + * exception is thrown).
611 610 *
612   - * An existing file will only be modified if the @p clobber flag is set to
613   - * true.
  611 + * The method will append a binary FITS table containing the energy
  612 + * boundaries to the FITS file. The extension name can be specified as part
  613 + * of the @p filename. For example the @p filename
  614 + *
  615 + * myfile.fits[ENERGY BOUNDARIES]
  616 + *
  617 + * will save the energy boundaries in the `ENERGY BOUNDARIES` extension of
  618 + * the `myfile.fits` file. If the extension exists already in the file it
  619 + * will be replaced, otherwise a new extension will be created. If no
  620 + * extension name is provided, the method will use `EBOUNDS` as the default
  621 + * extension name for energy boundaries.
614 622 ***************************************************************************/
615 623 void GEbounds::save(const GFilename& filename,
616 624 const bool& clobber,
617 625 const std::string& unit) const
618 626 {
619   - // Get extension name
620   - std::string extname = filename.extname("EBOUNDS");
621   -
622   - // Open or create FITS file
623   - GFits fits(filename, true);
  627 + // Open or create FITS file (without extension name since the requested
  628 + // extension may not yet exist in the file)
  629 + GFits fits(filename.url(), true);
624 630  
625 631 // Write energy boundaries to FITS file
626   - write(fits, extname, unit);
  632 + write(fits, filename.extname("EBOUNDS"), unit);
627 633  
628 634 // Save to file
629 635 fits.save(clobber);
... ...
src/obs/GEnergies.cpp
... ... @@ -565,30 +565,36 @@ void GEnergies::load(const GFilename& filename)
565 565  
566 566  
567 567 /***********************************************************************//**
568   - * @brief Save energies to FITS file
  568 + * @brief Save energies into FITS file
569 569 *
570 570 * @param[in] filename FITS filename.
571   - * @param[in] clobber Overwrite any existing energies extension?
  571 + * @param[in] clobber Overwrite an existing energies extension?
572 572 *
573   - * Saves energiesinto a FITS file. If a file with the given @p filename does
574   - * not yet exist it will be created, otherwise the method opens the existing
575   - * file. The method will create (or replace an existing) energies extension.
576   - * The extension name can be specified as part of the @p filename, or if no
577   - * extension name is given, is assumed to be "ENERGIES".
  573 + * Saves energies into a FITS file. If a file with the given @p filename
  574 + * does not yet exist it will be created, otherwise the method opens the
  575 + * existing file. Energies can only be appended to an existing file if the
  576 + * @p clobber flag is set to `true` (otherwise an exception is thrown).
578 577 *
579   - * An existing file will only be modified if the @p clobber flag is set to
580   - * true.
  578 + * The method will append a binary FITS table containing the energies to the
  579 + * FITS file. The extension name can be specified as part of the
  580 + * @p filename. For example the @p filename
  581 + *
  582 + * myfile.fits[ENERGY VALUES]
  583 + *
  584 + * will save the energies in the `ENERGY VALUES` extension of the
  585 + * `myfile.fits` file. If the extension exists already in the file it will be
  586 + * replaced, otherwise a new extension will be created. If no extension name
  587 + * is provided, the method will use `ENERGIES` as the default extension name
  588 + * for energies.
581 589 ***************************************************************************/
582 590 void GEnergies::save(const GFilename& filename, const bool& clobber) const
583 591 {
584   - // Get extension name
585   - std::string extname = filename.extname("ENERGIES");
586   -
587   - // Open or create FITS file
588   - GFits fits(filename, true);
  592 + // Open or create FITS file (without extension name since the requested
  593 + // extension may not yet exist in the file)
  594 + GFits fits(filename.url(), true);
589 595  
590 596 // Write energies to FITS file
591   - write(fits, extname);
  597 + write(fits, filename.extname("ENERGIES"));
592 598  
593 599 // Save to file
594 600 fits.save(clobber);
... ...
src/obs/GGti.cpp
... ... @@ -602,31 +602,37 @@ void GGti::load(const GFilename& filename)
602 602  
603 603  
604 604 /***********************************************************************//**
605   - * @brief Save Good Time Intervals intervals to FITS file
  605 + * @brief Save Good Time Intervals into FITS file
606 606 *
607 607 * @param[in] filename FITS filename.
608   - * @param[in] clobber Modify any existing FITS file? (default: false)
  608 + * @param[in] clobber Overwrite an existing Good Time Interval extension?
609 609 *
610 610 * Saves Good Time Intervals into a FITS file. If a file with the given
611 611 * @p filename does not yet exist it will be created, otherwise the method
612   - * opens the existing file. The method will create (or replace an existing)
613   - * Good Time Interval extension. The extension name can be specified as part
614   - * of the @p filename, or if no extension name is given, is assumed to be
615   - * "GTI".
  612 + * opens the existing file. Good Time Intervals can only be appended to an
  613 + * existing file if the @p clobber flag is set to `true` (otherwise an
  614 + * exception is thrown).
616 615 *
617   - * An existing file will only be modified if the @p clobber flag is set to
618   - * true.
  616 + * The method will append a binary FITS table containing the Good Time
  617 + * Intervals to the FITS file. The extension name can be specified as part
  618 + * of the @p filename. For example the @p filename
  619 + *
  620 + * myfile.fits[GOOD TIME INTERVALS]
  621 + *
  622 + * will save the Good Time Intervals in the `GOOD TIME INTERVALS` extension
  623 + * of the `myfile.fits` file. If the extension exists already in the file it
  624 + * will be replaced, otherwise a new extension will be created. If no
  625 + * extension name is provided, the method will use `GTI` as the default
  626 + * extension name for Good Time Intervals.
619 627 ***************************************************************************/
620 628 void GGti::save(const GFilename& filename, const bool& clobber) const
621 629 {
622   - // Get extension name
623   - std::string extname = filename.extname("GTI");
624   -
625   - // Open or create FITS file
626   - GFits fits(filename, true);
  630 + // Open or create FITS file (without extension name since the requested
  631 + // extension may not yet exist in the file)
  632 + GFits fits(filename.url(), true);
627 633  
628 634 // Write GTI to FITS object
629   - write(fits, extname);
  635 + write(fits, filename.extname("GTI"));
630 636  
631 637 // Save to file
632 638 fits.save(clobber);
... ...
src/support/GNodeArray.cpp
... ... @@ -721,26 +721,35 @@ void GNodeArray::load(const GFilename& filename)
721 721  
722 722  
723 723 /***********************************************************************//**
724   - * @brief Save nodes to FITS file
  724 + * @brief Save node array into FITS file
725 725 *
726 726 * @param[in] filename FITS filename.
727   - * @param[in] clobber Overwrite any existing nodes extension? (default: false)
  727 + * @param[in] clobber Overwrite an existing node array extension?
728 728 *
729 729 * Saves node array into a FITS file. If a file with the given @p filename
730 730 * does not yet exist it will be created, otherwise the method opens the
731   - * existing file. The method will create (or replace an existing) node array
732   - * extension. The extension name can be specified as part of the @p filename,
733   - * or if no extension name is given, is assumed to be "NODES".
  731 + * existing file. Node arrays can only be appended to an existing file if the
  732 + * @p clobber flag is set to `true` (otherwise an exception is thrown).
734 733 *
735   - * An existing file will only be modified if the @p clobber flag is set to
736   - * true.
  734 + * The method will append a binary FITS table containing the node array to
  735 + * the FITS file. The extension name can be specified as part of the
  736 + * @p filename. For example the @p filename
  737 + *
  738 + * myfile.fits[NODE ARRAY]
  739 + *
  740 + * will save the node array in the `NODE ARRAY` extension of the
  741 + * `myfile.fits` file. If the extension exists already in the file it will be
  742 + * replaced, otherwise a new extension will be created. If no extension name
  743 + * is provided, the method will use `NODES` as the default extension name
  744 + * for the node array.
737 745 ***************************************************************************/
738 746 void GNodeArray::save(const GFilename& filename, const bool& clobber) const
739 747 {
740   - // Open or create FITS file
741   - GFits fits(filename, true);
  748 + // Open or create FITS file (without extension name since the requested
  749 + // extension may not yet exist in the file)
  750 + GFits fits(filename.url(), true);
742 751  
743   - // Write GTI to FITS object
  752 + // Write node array to FITS object
744 753 write(fits, filename.extname("NODES"));
745 754  
746 755 // Save to file
... ...
src/xspec/GArf.cpp
... ... @@ -252,11 +252,18 @@ const double& GArf::at(const int& index) const
252 252 * @brief Load Auxiliary Response File
253 253 *
254 254 * @param[in] filename File name.
  255 + *
  256 + * Loads the Auxiliary Response File from the `SPECRESP` extension of the
  257 + * FITS file.
255 258 ***************************************************************************/
256 259 void GArf::load(const GFilename& filename)
257 260 {
258   - // Open FITS file
259   - GFits fits(filename);
  261 + // Clear response
  262 + clear();
  263 +
  264 + // Open FITS file (without extension name as the user is not allowed
  265 + // to modify the extension names)
  266 + GFits fits(filename.url());
260 267  
261 268 // Get ARF table
262 269 const GFitsTable& table = *fits.table("SPECRESP");
... ... @@ -268,7 +275,7 @@ void GArf::load(const GFilename& filename)
268 275 fits.close();
269 276  
270 277 // Store filename
271   - m_filename = filename;
  278 + m_filename = filename.url();
272 279  
273 280 // Return
274 281 return;
... ... @@ -279,21 +286,30 @@ void GArf::load(const GFilename& filename)
279 286 * @brief Save Auxiliary Response File
280 287 *
281 288 * @param[in] filename File name.
282   - * @param[in] clobber Overwrite existing file? (defaults: false)
  289 + * @param[in] clobber Overwrite existing file?
  290 + *
  291 + * Saves the Auxiliary Response File into a FITS file. If a file with the
  292 + * given @p filename does not yet exist it will be created. If the file
  293 + * exists it can be overwritten if the @p clobber flag is set to `true`.
  294 + * Otherwise an exception is thrown.
  295 + *
  296 + * The method will save the `SPECRESP` binary FITS table into the FITS file
  297 + * that contains the values of the Auxiliary Response File.
283 298 ***************************************************************************/
284 299 void GArf::save(const GFilename& filename, const bool& clobber) const
285 300 {
286   - // Open FITS file
  301 + // Create FITS file
287 302 GFits fits;
288 303  
289 304 // Write ARF into file
290 305 write(fits);
291 306  
292   - // Close FITS file
293   - fits.saveto(filename, clobber);
  307 + // Save to file (without extension name since the requested extension
  308 + // may not yet exist in the file)
  309 + fits.saveto(filename.url(), clobber);
294 310  
295 311 // Store filename
296   - m_filename = filename;
  312 + m_filename = filename.url();
297 313  
298 314 // Return
299 315 return;
... ... @@ -304,6 +320,19 @@ void GArf::save(const GFilename& filename, const bool& clobber) const
304 320 * @brief Read Auxiliary Response File
305 321 *
306 322 * @param[in] table ARF FITS table.
  323 + *
  324 + * Reads the Auxiliary Response File from a FITS table. The true energy
  325 + * boundaries are expected in the `ENERG_LO` and `ENERG_HI` columns, the
  326 + * response information is expected in the `SPECRESP` column.
  327 + *
  328 + * The method will analyze the unit of the `SPECRESP` column, and if either
  329 + * `m^2` or `m2` are encountered, multiply the values of the column by
  330 + * \f$10^4\f$ to convert the response into units of \f$cm^2\f$. Units of the
  331 + * `ENERG_LO` and `ENERG_HI` columns are also interpreted for conversion.
  332 + *
  333 + * See
  334 + * http://heasarc.gsfc.nasa.gov/docs/heasarc/caldb/docs/memos/cal_gen_92_002/cal_gen_92_002.html#tth_sEc4
  335 + * for details about the Auxiliary Response File format.
307 336 ***************************************************************************/
308 337 void GArf::read(const GFitsTable& table)
309 338 {
... ... @@ -349,6 +378,18 @@ void GArf::read(const GFitsTable& table)
349 378 * @brief Write Auxiliary Response File
350 379 *
351 380 * @param[in] fits FITS file.
  381 + *
  382 + * Writes the Auxiliary Response File into the `SPECRESP` extension of the
  383 + * FITS file. An existing extension with the same name will be removed from
  384 + * the FITS file before writing.
  385 + *
  386 + * The method writes the boundaries of the true energy bins into the
  387 + * `ENERG_LO` and `ENERG_HI` columns, response information will be written
  388 + * into the `SPECRESP` column.
  389 + *
  390 + * See
  391 + * http://heasarc.gsfc.nasa.gov/docs/heasarc/caldb/docs/memos/cal_gen_92_002/cal_gen_92_002.html#tth_sEc4
  392 + * for details about the Auxiliary Response File format.
352 393 ***************************************************************************/
353 394 void GArf::write(GFits& fits) const
354 395 {
... ... @@ -405,7 +446,7 @@ void GArf::write(GFits& fits) const
405 446 /***********************************************************************//**
406 447 * @brief Print Auxiliary Response File
407 448 *
408   - * @param[in] chatter Chattiness (defaults to NORMAL).
  449 + * @param[in] chatter Chattiness.
409 450 * @return String containing Auxiliary Response File information.
410 451 ***************************************************************************/
411 452 std::string GArf::print(const GChatter& chatter) const
... ...
src/xspec/GPha.cpp
... ... @@ -330,16 +330,18 @@ void GPha::fill(const GEnergy& energy, const double& value)
330 330 *
331 331 * @param[in] filename File name.
332 332 *
333   - * Loads the Pulse Height Analyzer spectrum from a FITS file. If an EBOUNDS
334   - * extension is given, the energy boundaries information is also loaded
  333 + * Loads the Pulse Height Analyzer spectrum from the `SPECTRUM` extension
  334 + * of the FITS file. If the file contains also an `EBOUNDS` extension the
  335 + * energy boundaries of all Pulse Height Analyzer channels are also loaded.
335 336 ***************************************************************************/
336 337 void GPha::load(const GFilename& filename)
337 338 {
338   - // Clear any existing models
  339 + // Clear spectrum
339 340 clear();
340 341  
341   - // Open FITS file
342   - GFits fits(filename);
  342 + // Open FITS file (without extension name as the user is not allowed
  343 + // to modify the extension names)
  344 + GFits fits(filename.url());
343 345  
344 346 // Get PHA table
345 347 const GFitsTable& pha = *fits.table("SPECTRUM");
... ... @@ -362,7 +364,7 @@ void GPha::load(const GFilename& filename)
362 364 fits.close();
363 365  
364 366 // Store filename
365   - m_filename = filename;
  367 + m_filename = filename.url();
366 368  
367 369 // Return
368 370 return;
... ... @@ -373,30 +375,32 @@ void GPha::load(const GFilename& filename)
373 375 * @brief Save Pulse Height Analyzer spectrum
374 376 *
375 377 * @param[in] filename File name.
376   - * @param[in] clobber Overwrite existing file? (default: false)
  378 + * @param[in] clobber Overwrite existing file?
377 379 *
378   - * Saves Pulse Height Analyzer spectrum and energy boundaries into a FITS
379   - * file. If a file with the given @p filename does not yet exist it will be
380   - * created, otherwise the method opens the existing file. The method will
381   - * create (or replace an existing) Pulse Height Analyzer spectrum and energy
382   - * boundary extensions.
  380 + * Saves the Pulse Height Analyzer spectrum and energy boundaries into a
  381 + * FITS file. If a file with the given @p filename does not yet exist it
  382 + * will be created. If the file exists it can be overwritten if the
  383 + * @p clobber flag is set to `true`. Otherwise an exception is thrown.
383 384 *
384   - * An existing file will only be modified if the @p clobber flag is set to
385   - * true.
  385 + * The method will save two binary FITS tables into the FITS file: a
  386 + * `SPECTRUM` extension that contains the channel values of the Pulse
  387 + * Height Analyzer spectrum and an `EBOUNDS` extension that contains the
  388 + * energy boundaries for all channels.
386 389 ***************************************************************************/
387 390 void GPha::save(const GFilename& filename, const bool& clobber) const
388 391 {
389   - // Open or create FITS file
390   - GFits fits(filename, true);
  392 + // Create FITS file
  393 + GFits fits;
391 394  
392 395 // Write PHA into file
393 396 write(fits);
394 397  
395   - // Close FITS file
396   - fits.save(clobber);
  398 + // Save to file (without extension name since the requested extension
  399 + // may not yet exist in the file)
  400 + fits.saveto(filename.url(), clobber);
397 401  
398 402 // Store filename
399   - m_filename = filename;
  403 + m_filename = filename.url();
400 404  
401 405 // Return
402 406 return;
... ... @@ -406,10 +410,18 @@ void GPha::save(const GFilename& filename, const bool& clobber) const
406 410 /***********************************************************************//**
407 411 * @brief Read Pulse Height Analyzer spectrum
408 412 *
409   - * @param[in] table PHA FITS table.
  413 + * @param[in] table FITS table.
410 414 *
411 415 * @exception GException::invalid_value
412 416 * Mismatch between PHA file and energy boundaries.
  417 + *
  418 + * Reads the Pulse Height Analyzer spectrum from a FITS table. The channel
  419 + * values are expected in the `COUNTS` column of the table. All other
  420 + * columns are ignored.
  421 + *
  422 + * See
  423 + * https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/spectra/ogip_92_007/node5.html
  424 + * for details about the Pulse Height Analyzer spectrum format.
413 425 ***************************************************************************/
414 426 void GPha::read(const GFitsTable& table)
415 427 {
... ... @@ -417,17 +429,16 @@ void GPha::read(const GFitsTable& table)
417 429 const GFitsTableCol* col_data = table["COUNTS"];
418 430  
419 431 // Extract number of channels in FITS file
420   - int length = table.integer("NAXIS2");
  432 + int length = col_data->length();
421 433  
422   - // Check whether column length is okay
  434 + // Check whether column length is consistent with energy boundaries
423 435 if (m_ebounds.size() > 0) {
424 436 if (m_ebounds.size() != length) {
425   - std::string msg = "Number of channels in PHA file ("
426   - ""+gammalib::str(length)+") mismatches the"
427   - " number of energy boundaries ("
428   - ""+gammalib::str(m_ebounds.size())+") that"
429   - " are defined in the GPha instance.\n"
430   - "Please define the correct energy boundaries.";
  437 + std::string msg = "Mismatch between the "+gammalib::str(length)+
  438 + " channels in the PHA file and the "+
  439 + gammalib::str(m_ebounds.size())+" energy "
  440 + "boundaries. Please correct either the energy "
  441 + "boundaris or the PHA file.";
431 442 throw GException::invalid_value(G_READ, msg);
432 443 }
433 444 }
... ... @@ -449,6 +460,20 @@ void GPha::read(const GFitsTable& table)
449 460 * @brief Write Pulse Height Analyzer spectrum
450 461 *
451 462 * @param[in] fits FITS file.
  463 + *
  464 + * Writes the Pulse Height Analyzer spectrum into `SPECTRUM` and `EBOUNDS`
  465 + * extensions of the FITS file. Extensions with these names will be removed
  466 + * from the FITS file before writing.
  467 + *
  468 + * The columns `CHANNEL`, `COUNTS`, `STAT_ERR`, `SYS_ERR`, `QUALITY`,
  469 + * `GROUPING`, `AREASCAL`, and `BACKSCAL` will be written into the `SPECTRUM`
  470 + * extension, but only the `CHANNEL` and `COUNTS` columns will be filled with
  471 + * values. Note that the channels start from 1 in the Pulse Height Analyzer
  472 + * spectrum.
  473 + *
  474 + * See
  475 + * https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/spectra/ogip_92_007/node5.html
  476 + * for details about the PHA file format.
452 477 ***************************************************************************/
453 478 void GPha::write(GFits& fits) const
454 479 {
... ... @@ -516,7 +541,7 @@ void GPha::write(GFits& fits) const
516 541 /***********************************************************************//**
517 542 * @brief Print Pulse Height Analyzer spectrum
518 543 *
519   - * @param[in] chatter Chattiness (defaults to NORMAL).
  544 + * @param[in] chatter Chattiness.
520 545 * @return String containing Pulse Height Analyzer spectrum information.
521 546 ***************************************************************************/
522 547 std::string GPha::print(const GChatter& chatter) const
... ...
src/xspec/GRmf.cpp
... ... @@ -369,20 +369,25 @@ GEbounds GRmf::emeasured(const GEnergy& etrue) const
369 369 * @brief Load Redistribution Matrix File
370 370 *
371 371 * @param[in] filename File name.
  372 + *
  373 + * Loads the Redistribution Matrix File from the `MATRIX` extension of the
  374 + * FITS file. If the file contains also an `EBOUNDS` extension the energy
  375 + * boundaries of all measured energies are also loaded.
372 376 ***************************************************************************/
373 377 void GRmf::load(const GFilename& filename)
374 378 {
375   - // Clear any existing models
  379 + // Clear matrix
376 380 clear();
377 381  
378   - // Open FITS file
379   - GFits fits(filename);
  382 + // Open FITS file (without extension name as the user is not allowed
  383 + // to modify the extension names)
  384 + GFits fits(filename.url());
380 385  
381 386 // Read measured energy boundaries
382   - m_ebds_measured.load(filename);
  387 + m_ebds_measured.load(filename.url());
383 388  
384 389 // Get RMF table
385   - const GFitsTable& table = *fits.table(filename.extname("MATRIX"));
  390 + const GFitsTable& table = *fits.table("MATRIX");
386 391  
387 392 // Read RMF data
388 393 read(table);
... ... @@ -391,7 +396,7 @@ void GRmf::load(const GFilename& filename)
391 396 fits.close();
392 397  
393 398 // Store filename
394   - m_filename = filename;
  399 + m_filename = filename.url();
395 400  
396 401 // Return
397 402 return;
... ... @@ -402,24 +407,36 @@ void GRmf::load(const GFilename& filename)
402 407 * @brief Save Redistribution Matrix File
403 408 *
404 409 * @param[in] filename File name.
405   - * @param[in] clobber Overwrite existing file? (default: false)
406   - * @param[in] unit Energy unit (default: "keV")
  410 + * @param[in] clobber Overwrite existing file?
  411 + * @param[in] unit Energy unit.
  412 + *
  413 + * Saves the Redistribution Matrix File into a FITS file. If a file with the
  414 + * given @p filename does not yet exist it will be created. If the file
  415 + * exists it can be overwritten if the @p clobber flag is set to `true`.
  416 + * Otherwise an exception is thrown.
  417 + *
  418 + * The method will save two binary FITS tables into the FITS file: a
  419 + * `MATRIX` extension that contains the Redistribution Matrix File elements,
  420 + * and an `EBOUNDS` extension that contains the measured energy boundaries
  421 + * for all matrix. The @p unit argument specifies the unit of the true
  422 + * energies that will be saved in the `MATRIX` extension.
407 423 ***************************************************************************/
408 424 void GRmf::save(const GFilename& filename,
409 425 const bool& clobber,
410 426 const std::string& unit) const
411 427 {
412   - // Open or create FITS file
413   - GFits fits(filename, true);
  428 + // Create FITS file
  429 + GFits fits;
414 430  
415 431 // Write RMF into file
416 432 write(fits, unit);
417 433  
418   - // Save to file
419   - fits.save(clobber);
  434 + // Save to file (without extension name since the requested extension
  435 + // may not yet exist in the file)
  436 + fits.saveto(filename.url(), clobber);
420 437  
421 438 // Store filename
422   - m_filename = filename;
  439 + m_filename = filename.url();
423 440  
424 441 // Return
425 442 return;
... ... @@ -430,6 +447,18 @@ void GRmf::save(const GFilename& filename,
430 447 * @brief Read Redistribution Matrix File
431 448 *
432 449 * @param[in] table RMF FITS table.
  450 + *
  451 + * Reads the Redistribution Matrix File from a FITS table. The true energy
  452 + * bins are expected in the `ENERG_LO` and `ENERG_HI` columns. Information
  453 + * for matrix compression are stored in the `N_GRP`, `F_CHAN` and `N_CHAN`
  454 + * columns, and the matrix elements are stored in the `MATRIX` column.
  455 + *
  456 + * The method automatically decompresses the Redistribution Matrix File and
  457 + * stores the matrix elements in a spare matrix.
  458 + *
  459 + * See
  460 + * http://heasarc.gsfc.nasa.gov/docs/heasarc/caldb/docs/memos/cal_gen_92_002/cal_gen_92_002.html#tth_sEc3
  461 + * for details about the Redistribution Matrix File file format.
433 462 ***************************************************************************/
434 463 void GRmf::read(const GFitsTable& table)
435 464 {
... ... @@ -497,7 +526,23 @@ void GRmf::read(const GFitsTable& table)
497 526 * @brief Write Redistribution Matrix File
498 527 *
499 528 * @param[in] fits FITS file.
500   - * @param[in] unit Energy unit (defaults to "keV")
  529 + * @param[in] unit Energy unit.
  530 + *
  531 + * Writes the Redistribution Matrix File into `MATRIX` and `EBOUNDS`
  532 + * extensions of the FITS file. Extensions with these names will be removed
  533 + * from the FITS file before writing.
  534 + *
  535 + * The true energy bins are written into the `ENERG_LO` and `ENERG_HI`
  536 + * columns. The units of the energies in these columns is specified by the
  537 + * @p unit argument. Information for matrix compression are stored in the
  538 + * `N_GRP`, `F_CHAN` and `N_CHAN` columns, and the matrix elements are
  539 + * written into the `MATRIX` column.
  540 + *
  541 + * The measured energy bins are written into the `EBOUNDS` extension.
  542 + *
  543 + * See
  544 + * http://heasarc.gsfc.nasa.gov/docs/heasarc/caldb/docs/memos/cal_gen_92_002/cal_gen_92_002.html#tth_sEc3
  545 + * for details about the Redistribution Matrix File file format.
501 546 ***************************************************************************/
502 547 void GRmf::write(GFits& fits, const std::string& unit) const
503 548 {
... ... @@ -629,7 +674,7 @@ void GRmf::write(GFits& fits, const std::string& unit) const
629 674 /***********************************************************************//**
630 675 * @brief Print Redistribution Matrix File
631 676 *
632   - * @param[in] chatter Chattiness (defaults to NORMAL).
  677 + * @param[in] chatter Chattiness.
633 678 * @return String containing Redistribution Matrix File information.
634 679 ***************************************************************************/
635 680 std::string GRmf::print(const GChatter& chatter) const
... ...
test/test_GObservation.cpp
... ... @@ -252,6 +252,24 @@ void TestGObservation::test_ebounds(void)
252 252 test_assert(!containment.contains(GEnergy(200.0, "MeV"), GEnergy(2000.0, "MeV")), "Energy bin [200,2000] MeV should not be contained.");
253 253 test_assert(!containment.contains(GEnergy(80.0, "MeV"), GEnergy(2000.0, "MeV")), "Energy bin [80,2000] MeV should not be contained.");
254 254  
  255 + // Remove test file
  256 + GFilename filename("test_ebounds.fits");
  257 + filename.remove();
  258 +
  259 + // Check saving
  260 + containment.save("test_ebounds.fits");
  261 + GEbounds load1("test_ebounds.fits");
  262 + test_value(load1.size(), 1, "GEbounds should have 1 element.");
  263 + test_value(load1.emin().MeV(), 100.0, 1.0e-10, "Minimum energy should be 100.");
  264 + test_value(load1.emax().MeV(), 1000.0, 1.0e-10, "Maximum energy should be 1000.");
  265 +
  266 + // Check saving in a different extnsion
  267 + ebds.save("test_ebounds.fits[ENERGY BOUNDARIES 2]", true);
  268 + GEbounds load2("test_ebounds.fits[ENERGY BOUNDARIES 2]");
  269 + test_value(load2.size(), 1, "GEbounds should have 1 element.");
  270 + test_value(load2.emin().MeV(), 1.0, 1.0e-10, "Minimum energy should be 1.");
  271 + test_value(load2.emax().MeV(), 10.0, 1.0e-10, "Maximum energy should be 10.");
  272 +
255 273 // Return
256 274 return;
257 275 }
... ... @@ -379,6 +397,12 @@ void TestGObservation::test_gti(void)
379 397 test_value(gti.tstart().secs(), 1.0, 1.0e-7, "Start time should be 1.");
380 398 test_value(gti.tstop().secs(), 1000.0, 1.0e-7, "Stop time should be 1000.");
381 399  
  400 + // Remove test file
  401 + GFilename file1("test_gti1.fits");
  402 + GFilename file2("test_gti2.fits");
  403 + file1.remove();
  404 + file2.remove();
  405 +
382 406 // Check saving in and loading from FITS file
383 407 gti.save("test_gti1.fits", true);
384 408 GGti test1("test_gti1.fits");
... ... @@ -480,6 +504,24 @@ void TestGObservation::test_gti(void)
480 504 test_value(test5.tstart().secs(), 1.0, 1.0e-7, "Start time should be 1.");
481 505 test_value(test5.tstop().secs(), 10000.0, 1.0e-7, "Stop time should be 10000.");
482 506  
  507 + // Remove test file
  508 + GFilename filename("test_gti.fits");
  509 + filename.remove();
  510 +
  511 + // Check saving
  512 + test4.save("test_gti.fits");
  513 + GGti load1("test_gti.fits");
  514 + test_value(load1.size(), 4, "GGti should have 4 intervals.");
  515 + test_value(load1.tstart().secs(), 1.0, 1.0e-7, "Start time should be 1.");
  516 + test_value(load1.tstop().secs(), 10000.0, 1.0e-7, "Stop time should be 10000.");
  517 +
  518 + // Check saving in a different extnsion
  519 + test3.save("test_gti.fits[GOOD TIME INTERVALS]", true);
  520 + GGti load2("test_gti.fits[GOOD TIME INTERVALS]");
  521 + test_value(load2.size(), 1, "GGti should have 1 interval.");
  522 + test_value(load2.tstart().convert(GTimeReference(51544.5, "s")), 0.0, 1.0e-7, "Start time should be 0.");
  523 + test_value(load2.tstop().convert(GTimeReference(51544.5, "s")), 100.0, 1.0e-7, "Stop time should be 100.");
  524 +
483 525 // Return
484 526 return;
485 527 }
... ... @@ -984,6 +1026,10 @@ void TestGObservation::test_energies(void)
984 1026 test_value(energies[i].MeV(), double(i));
985 1027 }
986 1028  
  1029 + // Remove test file
  1030 + GFilename filename("test_energies.fits");
  1031 + filename.remove();
  1032 +
987 1033 // Save and reload energies
988 1034 test_try("Saving and loading");
989 1035 try {
... ... @@ -1012,6 +1058,21 @@ void TestGObservation::test_energies(void)
1012 1058 test_try_failure(e);
1013 1059 }
1014 1060  
  1061 + // Save and reload energies in another extension
  1062 + test_try("Saving and loading in another extension");
  1063 + try {
  1064 + energies.save("test_energies.fits[NEW ENERGIES]", true);
  1065 + energies.clear();
  1066 + energies.load("test_energies.fits[NEW ENERGIES]");
  1067 + test_try_success();
  1068 + }
  1069 + catch (std::exception &e) {
  1070 + test_try_failure(e);
  1071 + }
  1072 + for (int i = 0; i < 4; ++i) {
  1073 + test_value(energies[i].MeV(), double(i));
  1074 + }
  1075 +
1015 1076 // Check linear energies
1016 1077 energies.clear();
1017 1078 energies.set_lin(3, GEnergy(1.0, "MeV"), GEnergy(3.0, "MeV"));
... ...
test/test_GSupport.cpp
... ... @@ -381,6 +381,30 @@ void TestGSupport::test_node_array(void)
381 381 test_value(nodes.wgt_left(), 0.25, 1.0e-6, "Expected weight 0.25");
382 382 test_value(nodes.wgt_right(), 0.75, 1.0e-6, "Expected weight 0.75");
383 383  
  384 + // Remove test file
  385 + GFilename filename("test_nodes.fits");
  386 + filename.remove();
  387 +
  388 + // Check saving
  389 + nodes.save("test_nodes.fits");
  390 + GNodeArray load1("test_nodes.fits");
  391 + test_value(load1.size(), 3, "GNodeArray should have 3 nodes.");
  392 + test_value(load1[0], 1.0, 1.0e-7, "First node should be 1.");
  393 + test_value(load1[1], 3.0, 1.0e-7, "First node should be 3.");
  394 + test_value(load1[2], 4.0, 1.0e-7, "First node should be 4.");
  395 +
  396 + // Check saving in a different extnsion
  397 + nodes.append(10.0);
  398 + nodes.append(11.0);
  399 + nodes.save("test_nodes.fits[NODE ARRAY]", true);
  400 + GNodeArray load2("test_nodes.fits[NODE ARRAY]");
  401 + test_value(load2.size(), 5, "GNodeArray should have 5 nodes.");
  402 + test_value(load2[0], 1.0, 1.0e-7, "First node should be 1.");
  403 + test_value(load2[1], 3.0, 1.0e-7, "First node should be 3.");
  404 + test_value(load2[2], 4.0, 1.0e-7, "First node should be 4.");
  405 + test_value(load2[3], 10.0, 1.0e-7, "First node should be 10.");
  406 + test_value(load2[4], 11.0, 1.0e-7, "First node should be 11.");
  407 +
384 408 // Return
385 409 return;
386 410 }
... ...