Commit b2388ce8e1c08abe3aec886799270abe2b3b5915

Authored by Jürgen Knödlseder
2 parents ed2f1398 0401c237

Merge branch 'integration' into devel

ChangeLog
... ... @@ -4,6 +4,14 @@
4 4 ========================
5 5  
6 6  
  7 +2019-06-25
  8 +
  9 + * Bug fix version 1.6.2 released
  10 + ================================
  11 +
  12 + Fix OpenMP issue in ctbin when using energy bounds from file (#2912)
  13 +
  14 +
7 15 2019-06-05
8 16  
9 17 * Bug fix version 1.6.1 released
... ...
README.md
1 1 ctools information
2 2 ==================
3 3 * Version: 1.7.0.dev (29 May 2019)
4   -* Author: Juergen Knoedlseder (jurgen.knodlseder@irap.omp.eu)
5 4 * GammaLib dependency: 1.7.0.dev
6 5  
7 6 [![Build Status](https://cta-jenkins.irap.omp.eu/buildStatus/icon?job=ctools-integrate-os)](https://cta-jenkins.irap.omp.eu/job/ctools-integrate-os/)
... ...
configure.ac
1 1 #############################################################################
2 2 # Configuration for ctools - tools for science analysis of CTA data #
3 3 # ------------------------------------------------------------------------- #
4   -# Copyright (C) 2011-2018 Juergen Knoedlseder #
  4 +# Copyright (C) 2011-2019 Juergen Knoedlseder #
5 5 # ------------------------------------------------------------------------- #
6 6 # #
7 7 # This program is free software: you can redistribute it and/or modify #
... ...
doc/source/admin/release_history/1.6.2.rst 0 → 100644
  1 +.. _1.6.2:
  2 +
  3 +ctools 1.6.2 release (25 June 2019)
  4 +===================================
  5 +
  6 +Overview
  7 +--------
  8 +
  9 +ctools 1.6.2 is a bug fix release for ctools release 1.6.
  10 +
  11 +The following bugs were fixed:
  12 +
  13 +* [`2912 <https://cta-redmine.irap.omp.eu/issues/2912>`_] -
  14 + Fix OpenMP issue in ctbin when using energy bounds from file
... ...
doc/source/admin/release_history/index.rst
... ... @@ -7,6 +7,7 @@ Release History
7 7 :maxdepth: 1
8 8  
9 9 ctools 1.7 <1.7>
  10 + ctools 1.6.2 <1.6.2>
10 11 ctools 1.6.1 <1.6.1>
11 12 ctools 1.6 <1.6>
12 13 ctools 1.5.4 <1.5.4>
... ...
src/support/ctool.cpp
... ... @@ -586,70 +586,105 @@ GEbounds ctool::create_ebounds(void)
586 586 // Get filename
587 587 GFilename ebinfile = (*this)["ebinfile"].filename();
588 588  
589   - // If no extension name was provided then use default extension names
590   - if (!ebinfile.has_extname()) {
591   -
592   - // Open energy boundary file using the EBOUNDS or ENERGYBINS
593   - // extension or energy values using the ENERGIES extension.
594   - // Throw an exception if opening fails.
595   - GFits file(ebinfile.url());
596   - if (file.contains(gammalib::extname_ebounds)) {
597   - file.close();
598   - ebounds.load(ebinfile);
599   - }
600   - else if (file.contains("ENERGYBINS")) {
601   - file.close();
602   - ebinfile = ebinfile.url() + "[ENERGYBINS]";
603   - ebounds.load(ebinfile);
604   - }
605   - else if (file.contains(gammalib::extname_energies)) {
606   - file.close();
607   - ebounds.set(GEnergies(ebinfile));
608   - }
609   - else {
610   - file.close();
611   - std::string msg = "No extension with name \""+
612   - gammalib::extname_ebounds+"\", "
613   - "\"ENERGYBINS\" or \""+
614   - gammalib::extname_energies+"\" found in FITS "
615   - "file \""+ebinfile+"\". Please specify a "
616   - "valid energy binning file.";
617   - throw GException::invalid_value(G_CREATE_EBOUNDS, msg);
618   - }
619   - }
  589 + // Initialise exception message string
  590 + std::string msg;
  591 +
  592 + // Make loading of energy boundaries OpenMP thread save. The critical
  593 + // region is put into an exception block to prevent throwing exceptions
  594 + // within the critical block
  595 + #pragma omp critical(ctool_create_ebounds)
  596 + {
  597 + try {
620 598  
621   - // ... otherwise load energy boundaries from filename including
622   - // extension
623   - else {
  599 + // If no extension name was provided then use default extension
  600 + // names
  601 + if (!ebinfile.has_extname()) {
  602 +
  603 + // Open energy boundary file using the EBOUNDS or ENERGYBINS
  604 + // extension or energy values using the ENERGIES extension.
  605 + // If opening fails then set exception message. The
  606 + // exception is thrown later outside the critical OpenMP
  607 + // block.
  608 + GFits file(ebinfile.url());
  609 + if (file.contains(gammalib::extname_ebounds)) {
  610 + file.close();
  611 + ebounds.load(ebinfile);
  612 + }
  613 + else if (file.contains("ENERGYBINS")) {
  614 + file.close();
  615 + ebinfile = ebinfile.url() + "[ENERGYBINS]";
  616 + ebounds.load(ebinfile);
  617 + }
  618 + else if (file.contains(gammalib::extname_energies)) {
  619 + file.close();
  620 + ebounds.set(GEnergies(ebinfile));
  621 + }
  622 + else {
  623 + file.close();
  624 + msg = "No extension with name \""+
  625 + gammalib::extname_ebounds+"\", "
  626 + "\"ENERGYBINS\" or \""+
  627 + gammalib::extname_energies+"\" found in FITS "
  628 + "file \""+ebinfile+"\". Please specify a "
  629 + "valid energy binning file.";
  630 + }
624 631  
625   - // Open energy boundary file
626   - GFits file(ebinfile.url());
627   -
628   - // If FITS file does not contain requested extension then throw
629   - // an exception
630   - if (!file.contains(ebinfile.extname())) {
631   - std::string msg = "No extension \""+ebinfile.extname()+"\" "
632   - "found in energy binning file \""+
633   - ebinfile.url()+"\". Please provide a valid "
634   - "extension name.";
635   - throw GException::invalid_value(G_CREATE_EBOUNDS, msg);
636   - }
  632 + } // endif: no extension name specified
637 633  
638   - // Get table extension
639   - GFitsTable& table = *file.table(ebinfile.extname());
  634 + // ... otherwise load energy boundaries from filename including
  635 + // extension
  636 + else {
640 637  
641   - // If table contains one column then load energies, otherwise load
642   - // energy boundaries.
643   - if (table.ncols() == 1) {
644   - file.close();
645   - ebounds.set(GEnergies(ebinfile));
646   - }
647   - else {
648   - file.close();
649   - ebounds.load(ebinfile);
  638 + // Open energy boundary file
  639 + GFits file(ebinfile.url());
  640 +
  641 + // If FITS file does not contain requested extension then
  642 + // set exception message. The exception is thrown later
  643 + // outside the critical OpenMP zone
  644 + if (!file.contains(ebinfile.extname())) {
  645 + msg = "No extension \""+ebinfile.extname()+"\" "
  646 + "found in energy binning file \""+
  647 + ebinfile.url()+"\". Please provide a valid "
  648 + "extension name.";
  649 + }
  650 +
  651 + // ... otherwise load energy boundaries
  652 + else {
  653 +
  654 + // Get table extension
  655 + GFitsTable& table = *file.table(ebinfile.extname());
  656 +
  657 + // If table contains one column then load energies,
  658 + // otherwise load energy boundaries.
  659 + if (table.ncols() == 1) {
  660 + file.close();
  661 + ebounds.set(GEnergies(ebinfile));
  662 + }
  663 + else {
  664 + file.close();
  665 + ebounds.load(ebinfile);
  666 + }
  667 +
  668 + } // endelse: extension name was present in FITS file
  669 +
  670 + } // endelse: loaded energy boundaries from table extension
  671 +
  672 + } // endtry
  673 +
  674 + // Catch any exceptions that occured in OpenMP critical block
  675 + // and recover the error message
  676 + catch (const std::exception& e) {
  677 + msg = e.what();
650 678 }
651 679  
652   - } // endelse: loaded energy boundaries from table extension
  680 + } // end critical OpenMP block
  681 +
  682 + // If we have an exception message then throw an exception now. We
  683 + // have to do this since we should not throw exceptions inside OpenMP
  684 + // critical blocks
  685 + if (!msg.empty()) {
  686 + throw GException::invalid_value(G_CREATE_EBOUNDS, msg);
  687 + }
653 688  
654 689 } // endif: ebinalg was "FILE"
655 690  
... ... @@ -717,70 +752,105 @@ GEnergies ctool::create_energies(void)
717 752 // Get filename
718 753 GFilename ebinfile = (*this)["ebinfile"].filename();
719 754  
720   - // If no extension name was provided then use default extension names
721   - if (!ebinfile.has_extname()) {
  755 + // Initialise exception message string
  756 + std::string msg;
722 757  
723   - // Open energy boundary file using the EBOUNDS or ENERGYBINS
724   - // extension or energy values using the ENERGIES extension.
725   - // Throw an exception if opening fails.
726   - GFits file(ebinfile.url());
727   - if (file.contains(gammalib::extname_energies)) {
728   - file.close();
729   - energies.load(ebinfile);
730   - }
731   - else if (file.contains(gammalib::extname_ebounds)) {
732   - file.close();
733   - energies.set(GEbounds(ebinfile));
734   - }
735   - else if (file.contains("ENERGYBINS")) {
736   - file.close();
737   - ebinfile = ebinfile.url() + "[ENERGYBINS]";
738   - energies.set(GEbounds(ebinfile));
739   - }
740   - else {
741   - file.close();
742   - std::string msg = "No extension with name \""+
743   - gammalib::extname_energies+"\", "
744   - "\"ENERGYBINS\" or \""+
745   - gammalib::extname_ebounds+"\" found in FITS "
746   - "file \""+ebinfile+"\". Please specify a "
747   - "valid energy binning file.";
748   - throw GException::invalid_value(G_CREATE_ENERGIES, msg);
749   - }
750   - }
  758 + // Make loading of energies OpenMP thread save. The critical region is
  759 + // put into an exception block to prevent throwing exceptions within
  760 + // the critical block
  761 + #pragma omp critical(ctool_create_energies)
  762 + {
  763 + try {
751 764  
752   - // ... otherwise load energy boundaries from filename including
753   - // extension
754   - else {
  765 + // If no extension name was provided then use default extension
  766 + // names
  767 + if (!ebinfile.has_extname()) {
  768 +
  769 + // Open energy boundary file using the EBOUNDS or ENERGYBINS
  770 + // extension or energy values using the ENERGIES extension.
  771 + // If opening fails then set exception message. The
  772 + // exception is thrown later outside the critical OpenMP
  773 + // block.
  774 + GFits file(ebinfile.url());
  775 + if (file.contains(gammalib::extname_energies)) {
  776 + file.close();
  777 + energies.load(ebinfile);
  778 + }
  779 + else if (file.contains(gammalib::extname_ebounds)) {
  780 + file.close();
  781 + energies.set(GEbounds(ebinfile));
  782 + }
  783 + else if (file.contains("ENERGYBINS")) {
  784 + file.close();
  785 + ebinfile = ebinfile.url() + "[ENERGYBINS]";
  786 + energies.set(GEbounds(ebinfile));
  787 + }
  788 + else {
  789 + file.close();
  790 + msg = "No extension with name \""+
  791 + gammalib::extname_energies+"\", "
  792 + "\"ENERGYBINS\" or \""+
  793 + gammalib::extname_ebounds+"\" found in FITS "
  794 + "file \""+ebinfile+"\". Please specify a "
  795 + "valid energy binning file.";
  796 + }
755 797  
756   - // Open energy boundary file
757   - GFits file(ebinfile.url());
758   -
759   - // If FITS file does not contain requested extension then throw
760   - // an exception
761   - if (!file.contains(ebinfile.extname())) {
762   - std::string msg = "No extension \""+ebinfile.extname()+"\" "
763   - "found in energy node file \""+
764   - ebinfile.url()+"\". Please provide a valid "
765   - "extension name.";
766   - throw GException::invalid_value(G_CREATE_ENERGIES, msg);
767   - }
  798 + }
768 799  
769   - // Get table extension
770   - GFitsTable& table = *file.table(ebinfile.extname());
  800 + // ... otherwise load energy boundaries from filename including
  801 + // extension
  802 + else {
771 803  
772   - // If table contains one column then load energies, otherwise
773   - // load energy boundaries.
774   - if (table.ncols() == 1) {
775   - file.close();
776   - energies.load(ebinfile);
777   - }
778   - else {
779   - file.close();
780   - energies.set(GEbounds(ebinfile));
  804 + // Open energy boundary file
  805 + GFits file(ebinfile.url());
  806 +
  807 + // If FITS file does not contain requested extension then
  808 + // set exception message. The exception is thrown later
  809 + // outside the critical OpenMP zone
  810 + if (!file.contains(ebinfile.extname())) {
  811 + msg = "No extension \""+ebinfile.extname()+"\" "
  812 + "found in energy node file \""+
  813 + ebinfile.url()+"\". Please provide a valid "
  814 + "extension name.";
  815 + }
  816 +
  817 + // ... otherwise load energies
  818 + else {
  819 +
  820 + // Get table extension
  821 + GFitsTable& table = *file.table(ebinfile.extname());
  822 +
  823 + // If table contains one column then load energies,
  824 + // otherwise load energy boundaries.
  825 + if (table.ncols() == 1) {
  826 + file.close();
  827 + energies.load(ebinfile);
  828 + }
  829 + else {
  830 + file.close();
  831 + energies.set(GEbounds(ebinfile));
  832 + }
  833 +
  834 + } // endelse: extension name was present in FITS file
  835 +
  836 + } // endelse: loaded energies from table extension
  837 +
  838 + } // endtry
  839 +
  840 + // Catch any exceptions that occured in OpenMP critical block
  841 + // and recover the error message
  842 + catch (const std::exception& e) {
  843 + msg = e.what();
781 844 }
782 845  
783   - } // endelse: loaded energy boundaries from table extension
  846 + } // end critical OpenMP block
  847 +
  848 + // If we have an exception message then throw an exception now. We
  849 + // have to do this since we should not throw exceptions inside OpenMP
  850 + // critical blocks
  851 + if (!msg.empty()) {
  852 + throw GException::invalid_value(G_CREATE_ENERGIES, msg);
  853 + }
784 854  
785 855 } // endif: ebinalg was "FILE"
786 856  
... ... @@ -2203,34 +2273,40 @@ void ctool::save_event_list(const GCTAObservation* obs,
2203 2273 }
2204 2274 }
2205 2275  
2206   - // Create output FITS file
2207   - GFits outfits;
2208   -
2209   - // Write observation into FITS file
2210   - obs->write(outfits, outevt, outgti);
2211   -
2212   - // Copy all extensions other than evtname and gtiname extensions
2213   - // from the input to the output event list. The evtname and
2214   - // gtiname extensions are written by the save method, all others
2215   - // that may eventually be present have to be copied over
2216   - // explicitly.
2217   - GFits infits(infile);
2218   - for (int extno = 1; extno < infits.size(); ++extno) {
2219   - GFitsHDU* hdu = infits.at(extno);
2220   - if (hdu->extname() != evtname &&
2221   - hdu->extname() != gtiname &&
2222   - hdu->extname() != outevt &&
2223   - hdu->extname() != outgti) {
2224   - outfits.append(*hdu);
  2276 + // Make writing of FITS file OpenMP thread save
  2277 + #pragma omp critical(ctool_save_event_list)
  2278 + {
  2279 +
  2280 + // Create output FITS file
  2281 + GFits outfits;
  2282 +
  2283 + // Write observation into FITS file
  2284 + obs->write(outfits, outevt, outgti);
  2285 +
  2286 + // Copy all extensions other than evtname and gtiname extensions
  2287 + // from the input to the output event list. The evtname and
  2288 + // gtiname extensions are written by the save method, all others
  2289 + // that may eventually be present have to be copied over
  2290 + // explicitly.
  2291 + GFits infits(infile);
  2292 + for (int extno = 1; extno < infits.size(); ++extno) {
  2293 + GFitsHDU* hdu = infits.at(extno);
  2294 + if (hdu->extname() != evtname &&
  2295 + hdu->extname() != gtiname &&
  2296 + hdu->extname() != outevt &&
  2297 + hdu->extname() != outgti) {
  2298 + outfits.append(*hdu);
  2299 + }
2225 2300 }
2226   - }
2227 2301  
2228   - // Close input file
2229   - infits.close();
  2302 + // Close input file
  2303 + infits.close();
  2304 +
  2305 + // Save file to disk and close it (we need both operations)
  2306 + outfits.saveto(outname.url(), clobber());
  2307 + outfits.close();
2230 2308  
2231   - // Save file to disk and close it (we need both operations)
2232   - outfits.saveto(outname.url(), clobber());
2233   - outfits.close();
  2309 + } // end: omp critical section
2234 2310  
2235 2311 } // endif: observation was unbinned
2236 2312  
... ...