diff --git a/AUTHORS b/AUTHORS index 9be2de7..90ed436 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,7 +3,13 @@ Rolf Buehler (rolf.buehler@desy.de) Jean-Baptiste Cayrou (cayrou@etud.insa-toulouse.fr) Christoph Deil (Christoph.Deil@mpi-hd.mpg.de) Florent Forest (florent.forest@isae.fr) +Lucie Gerard (lgerard@wgs18.zeuthen.desy.de) +Maria Haupt (maria.haupt@desy.de) +Nathan Kelley-Hoskins (nathan.kelley-hoskins@desy.de) Juergen Knoedlseder (jurgen.knodlseder@irap.omp.eu) Chia-Chun Lu (Chia-Chun.Lu@mpi-hd.mpg.de) +Pierrick Martin (pierrick.martin@irap.omp.eu) Michael Mayer (michael.mayer@physik.hu-berlin.de) - +Stefan Ohm (stefan.ohm81@gmail.com) +David Sanchez (david.sanchez@lapp.in2p3.fr) +Hubert Siejkowski (h.siejkowski@cyfronet.pl) diff --git a/pyext/ctool.i b/pyext/ctool.i index 0b82c0e..5059b7f 100644 --- a/pyext/ctool.i +++ b/pyext/ctool.i @@ -84,29 +84,26 @@ public: virtual void execute(void); // Make methods private in Python by prepending an underscore - %rename(_read_ahead) read_ahead() const; - %rename(_time_reference) time_reference() const; - %rename(_get_observations) get_observations(const bool& get_response = true); - %rename(_setup_observations) setup_observations(GObservations& obs); - %rename(_create_ebounds) create_ebounds(); - %rename(_create_map) create_map(const GObservations& obs); - %rename(_create_cube) create_cube(const GObservations& obs); - %rename(_create_cta_obs) create_cta_obs(); - %rename(_require_inobs) require_inobs(const std::string& method); - %rename(_require_inobs_nolist) require_inobs_nolist(const std::string& method); - %rename(_require_inobs_nocube) require_inobs_nocube(const std::string& method); - %rename(_log_observations) log_observations(const GChatter& chatter, - const GObservations& obs, - const std::string& what); - %rename(_log_models) log_models(const GChatter& chatter, - const GModels& models, - const std::string& what); - %rename(_set_response) set_response(GObservations& obs); - %rename(_set_obs_response) set_obs_response(GCTAObservation* obs); - %rename(_set_obs_bounds) set_obs_bounds(GObservations& obs); - %rename(_get_mean_pointing) get_mean_pointing(const GObservations& obs); - %rename(_get_current_rss) get_current_rss(); - %rename(_get_obs_header) get_obs_header(); + %rename(_read_ahead) read_ahead; + %rename(_time_reference) time_reference; + %rename(_get_observations) get_observations; + %rename(_setup_observations) setup_observations; + %rename(_create_ebounds) create_ebounds; + %rename(_create_map) create_map; + %rename(_create_cube) create_cube; + %rename(_create_cta_obs) create_cta_obs; + %rename(_require_inobs) require_inobs; + %rename(_require_inobs_nolist) require_inobs_nolist; + %rename(_require_inobs_nocube) require_inobs_nocube; + %rename(_log_observations) log_observations; + %rename(_log_models) log_models; + %rename(_set_response) set_response; + %rename(_set_obs_response) set_obs_response; + %rename(_set_obs_bounds) set_obs_bounds; + %rename(_get_mean_pointing) get_mean_pointing; + %rename(_get_current_rss) get_current_rss; + %rename(_get_obs_header) get_obs_header; + %rename(_warn_too_few_energies) warn_too_few_energies; // Protected methods const bool& read_ahead(void) const; @@ -140,6 +137,10 @@ public: GSkyDir get_mean_pointing(const GObservations& obs); size_t get_current_rss(void); std::string get_obs_header(const GObservation* obs); + GEnergies insert_energy_boundaries(const GEnergies& energies, + const GCTAObservation& obs); + // Protected warning strings + std::string warn_too_few_energies(const GEnergies& energies) const; }; diff --git a/src/ctbin/ctbin.cpp b/src/ctbin/ctbin.cpp index 383e6b4..d2bb22e 100644 --- a/src/ctbin/ctbin.cpp +++ b/src/ctbin/ctbin.cpp @@ -345,7 +345,7 @@ void ctbin::publish(const std::string& name) user_name = CTBIN_NAME; } - // Log counts cube name + // Write counts cube name into logger log_value(NORMAL, "Counts cube name", user_name); // Publish counts cube diff --git a/src/ctbkgcube/ctbkgcube.cpp b/src/ctbkgcube/ctbkgcube.cpp index 46fcfa1..5d3b4bd 100644 --- a/src/ctbkgcube/ctbkgcube.cpp +++ b/src/ctbkgcube/ctbkgcube.cpp @@ -218,23 +218,7 @@ void ctbkgcube::run(void) get_parameters(); // Warn if there are not enough energy bins - int n = m_background.energies().size(); - double logEmin = std::log10(m_background.energies()[0].TeV()); - double logEmax = std::log10(m_background.energies()[n-1].TeV()); - int nrequired = int((logEmax - logEmin) * 25.0); - if (n < nrequired) { - std::string msg = "\nWARNING: Only "+gammalib::str(n-1)+" energy bins " - "have been requested. This may be too few energy " - "bins.\n At least 25 bins per decade in " - "energy are recommended, which in your case\n" - " would be "+gammalib::str(nrequired-1)+ - " bins. Consider increasing the number of energy " - "bins."; - log_string(TERSE, msg); - } - - // Initialise exposure cube - init_cube(); + log_string(TERSE, warn_too_few_energies(m_background.energies())); // Write input observation container into logger log_observations(NORMAL, m_obs, "Input observation"); @@ -242,6 +226,9 @@ void ctbkgcube::run(void) // Write input model container into logger log_models(NORMAL, m_obs.models(), "Input model"); + // Initialise exposure cube + init_cube(); + // Write header log_header1(TERSE, "Prepare model"); @@ -296,11 +283,11 @@ void ctbkgcube::run(void) // Assign background models to container m_obs.models(m_bkgmdl); - // Set pointer on logger depending on chattiness - GLog* logp = (logNormal()) ? &log : NULL; + // Set pointer to logger dependent on chattiness + GLog* logger = (logNormal()) ? &log : NULL; // Fill background cube from observations - m_background.fill(m_obs, logp); + m_background.fill(m_obs, logger); // Create a background model for the output background cube and append // that model to the input model in place of the original @@ -356,28 +343,33 @@ void ctbkgcube::save(void) m_outcube = (*this)["outcube"].filename(); m_outmodel = (*this)["outmodel"].filename(); - // Save only if filename is non-empty - if (!m_outcube.is_empty()) { + // Determine whether the cube is empty + bool cube_is_empty = ((m_background.cube().nx() == 0) || + (m_background.cube().ny() == 0) || + (m_background.cube().nmaps() == 0)); - // Log background cube file name - log_value(NORMAL, "Background cube file", m_outcube.url()); + // Determine whether a model definition file should be written + bool write_moddef = ((!m_outmodel.is_empty()) && + (gammalib::tolower(m_outmodel.url()) != "none")); - // Save background cube + // Save background cube if filename and the background cube are not empty + if (!m_outcube.is_empty() && !cube_is_empty) { m_background.save(m_outcube, clobber()); - } - // Write output models if filename is valid - if ((!m_outmodel.is_empty()) && - (gammalib::tolower(m_outmodel.url()) != "none")) { - - // Log model definition file name - log_value(NORMAL, "Model definition file", m_outmodel.url()); - - // Save output model for stacked analyses + // Save model definition file if requested + if (write_moddef) { m_outmdl.save(m_outmodel.url()); + } + // Write into logger what has been done + std::string fname = (m_outcube.is_empty()) ? "NONE" : m_outcube.url(); + std::string mname = (write_moddef) ? m_outmodel.url() : "NONE"; + if (cube_is_empty) { + fname.append(" (cube is empty, no file created)"); } + log_value(NORMAL, "Background cube file", fname); + log_value(NORMAL, "Model definition file", mname); // Return return; @@ -394,7 +386,7 @@ void ctbkgcube::publish(const std::string& name) // Write header log_header1(TERSE, "Publish background cube"); - // Set default name is user name is empty + // Set default name if user name is empty std::string user_name(name); if (user_name.empty()) { user_name = CTBKGCUBE_NAME; @@ -588,9 +580,6 @@ void ctbkgcube::init_cube(void) // If requested, insert energies at all event list energy boundaries if (m_addbounds) { - // Set pointer on logger depending on chattiness - GLog* logp = (logNormal()) ? &log : NULL; - // Loop over all observations for (int i = 0; i < m_obs.size(); ++i) { @@ -609,7 +598,7 @@ void ctbkgcube::init_cube(void) } // Insert energy boundaries - energies = insert_energy_boundaries(energies, *cta, logp); + energies = insert_energy_boundaries(energies, *cta); } // endfor: looped over all observations @@ -623,4 +612,4 @@ void ctbkgcube::init_cube(void) // Return return; -}; +} diff --git a/src/ctedispcube/ctedispcube.cpp b/src/ctedispcube/ctedispcube.cpp index 35de96a..95435f1 100644 --- a/src/ctedispcube/ctedispcube.cpp +++ b/src/ctedispcube/ctedispcube.cpp @@ -227,35 +227,23 @@ void ctedispcube::run(void) // Get task parameters get_parameters(); - // Write header - if (logTerse()) { - log << std::endl; - log.header1("Initialise energy dispersion cube"); - } + // Write input observation container into logger + log_observations(NORMAL, m_obs, "Input observation"); // Initialise energy dispersion cube init_cube(); - // Write observation(s) into logger - if (logTerse()) { - log << std::endl; - log.header1(gammalib::number("Observation",m_obs.size())); - log << m_obs.print(m_chatter) << std::endl; - } + // Write header into logger + log_header1(TERSE, "Generate energy dispersion cube"); - // Write header - if (logTerse()) { - log << std::endl; - log.header1("Generate energy dispersion cube"); - } + // Set pointer to logger dependent on chattiness + GLog* logger = (logNormal()) ? &log : NULL; // Fill energy dispersion cube - m_edispcube.fill(m_obs, &log); + m_edispcube.fill(m_obs, logger); - // Log energy dispersion cube - if (logTerse()) { - log << m_edispcube.print(m_chatter) << std::endl; - } + // Write energy dispersion cube into logger + log_string(EXPLICIT, m_edispcube.print(m_chatter)); // Return return; @@ -270,28 +258,29 @@ void ctedispcube::run(void) ***************************************************************************/ void ctedispcube::save(void) { - // Write header - if (logTerse()) { - log << std::endl; - log.header1("Save Edisp cube"); - } + // Write header into logger + log_header1(TERSE, "Save energy dispersion cube"); // Get output filename m_outcube = (*this)["outcube"].filename(); - // Save only if filename is non-empty - if (!m_outcube.is_empty()) { + // Determine whether the cube is empty + bool cube_is_empty = ((m_edispcube.cube().nx() == 0) || + (m_edispcube.cube().ny() == 0) || + (m_edispcube.cube().nmaps() == 0)); - // Log filename - if (logTerse()) { - log << gammalib::parformat("Energy dispersion cube file"); - log << m_outcube.url() << std::endl; - } - - // Save energy dispersion cube + // Save energy dispersion cube if filename and the energy dispersion cube + // are not empty + if (!m_outcube.is_empty() && !cube_is_empty) { m_edispcube.save(m_outcube, clobber()); + } + // Write into logger what has been done + std::string fname = (m_outcube.is_empty()) ? "NONE" : m_outcube.url(); + if (cube_is_empty) { + fname.append(" (cube is empty, no file created)"); } + log_value(NORMAL, "Energy dispersion cube file", fname); // Return return; @@ -430,6 +419,9 @@ void ctedispcube::get_parameters(void) ***************************************************************************/ void ctedispcube::init_cube(void) { + // Write header into logger + log_header1(TERSE, "Initialise energy dispersion cube"); + // Extract energy dispersion definition const GWcs* proj = static_cast(m_edispcube.cube().projection()); std::string wcs = m_edispcube.cube().projection()->code(); @@ -449,12 +441,6 @@ void ctedispcube::init_cube(void) // If requested, insert energies at all event list energy boundaries if (m_addbounds) { - // Set logger - GLog* logger = NULL; - if (logTerse()) { - logger = &log; - } - // Loop over all observations for (int i = 0; i < m_obs.size(); ++i) { @@ -473,7 +459,7 @@ void ctedispcube::init_cube(void) } // Insert energy boundaries - energies = insert_energy_boundaries(energies, *cta, logger); + energies = insert_energy_boundaries(energies, *cta); } // endfor: looped over all observations @@ -483,11 +469,9 @@ void ctedispcube::init_cube(void) m_edispcube = GCTACubeEdisp(wcs, coords, x, y, dx, dy, nx, ny, energies, mmax, nmbins); - // Log energy dispersion cube - if (logTerse()) { - log << m_edispcube.print(m_chatter) << std::endl; - } + // Write energy dispersion cube in logger + log_string(NORMAL, m_edispcube.print(m_chatter)); // Return return; -}; +} diff --git a/src/ctexpcube/ctexpcube.cpp b/src/ctexpcube/ctexpcube.cpp index 3da1d90..3f2f9a7 100644 --- a/src/ctexpcube/ctexpcube.cpp +++ b/src/ctexpcube/ctexpcube.cpp @@ -218,53 +218,25 @@ void ctexpcube::run(void) get_parameters(); // Warn if there are not enough energy bins - int n = m_expcube.energies().size(); - double logEmin = std::log10(m_expcube.energies()[0].TeV()); - double logEmax = std::log10(m_expcube.energies()[n-1].TeV()); - int nrequired = int((logEmax - logEmin) * 25.0); - if ((n < nrequired) && logTerse()) { - log << std::endl; - log << "WARNING: "; - log << "Only " << n-1 << " energy bins have been requested. This may be " - "too few energy bins."; - log << std::endl << " "; - log << "At least 25 bins per decade in energy are recommended, which " - "in your case"; - log << std::endl << " "; - log << "would be " << nrequired-1 << " bins. Consider increasing the " - "number of energy bins."; - log << std::endl; - } + log_string(TERSE, warn_too_few_energies(m_expcube.energies())); - // Write header - if (logTerse()) { - log << std::endl; - log.header1("Initialise exposure cube"); - } + // Write input observation container into logger + log_observations(NORMAL, m_obs, "Input observation"); // Initialise exposure cube init_cube(); - // Write observation(s) into logger - if (logTerse()) { - log << std::endl; - log.header1(gammalib::number("Observation",m_obs.size())); - log << m_obs.print(m_chatter) << std::endl; - } + // Write header into logger + log_header1(TERSE, "Generate exposure cube"); - // Write header - if (logTerse()) { - log << std::endl; - log.header1("Generate exposure cube"); - } + // Set pointer to logger dependent on chattiness + GLog* logger = (logNormal()) ? &log : NULL; // Fill exposure cube - m_expcube.fill(m_obs, &log); + m_expcube.fill(m_obs, logger); - // Log exposure cube - if (logTerse()) { - log << m_expcube.print(m_chatter) << std::endl; - } + // Write exposure cube into logger + log_string(EXPLICIT, m_expcube.print(m_chatter)); // Optionally publish exposure cube if (m_publish) { @@ -283,28 +255,28 @@ void ctexpcube::run(void) ***************************************************************************/ void ctexpcube::save(void) { - // Write header - if (logTerse()) { - log << std::endl; - log.header1("Save exposure cube"); - } + // Write header into logger + log_header1(TERSE, "Save exposure cube"); // Get exposure cube filename m_outcube = (*this)["outcube"].filename(); - // Save only if filename is non-empty - if (!m_outcube.is_empty()) { - - // Log filename - if (logTerse()) { - log << gammalib::parformat("Exposure cube file"); - log << m_outcube.url() << std::endl; - } + // Determine whether the cube is empty + bool cube_is_empty = ((m_expcube.cube().nx() == 0) || + (m_expcube.cube().ny() == 0) || + (m_expcube.cube().nmaps() == 0)); - // Save exposure cube + // Save exposure cube if filename and the exposure cube are not empty + if (!m_outcube.is_empty() && !cube_is_empty) { m_expcube.save(m_outcube, clobber()); + } + // Write into logger what has been done + std::string fname = (m_outcube.is_empty()) ? "NONE" : m_outcube.url(); + if (cube_is_empty) { + fname.append(" (cube is empty, no file created)"); } + log_value(NORMAL, "Exposure cube file", fname); // Return return; @@ -318,23 +290,17 @@ void ctexpcube::save(void) ***************************************************************************/ void ctexpcube::publish(const std::string& name) { - // Write header - if (logTerse()) { - log << std::endl; - log.header1("Publish exposure cube"); - } + // Write header into logger + log_header1(TERSE, "Publish exposure cube"); - // Set default name is user name is empty + // Set default name if user name is empty std::string user_name(name); if (user_name.empty()) { user_name = CTEXPCUBE_NAME; } - // Log filename - if (logTerse()) { - log << gammalib::parformat("Publish exposure cube"); - log << user_name << std::endl; - } + // Write exposure cube name into logger + log_value(NORMAL, "Counts cube name", user_name); // Publish exposure cube m_expcube.cube().publish(user_name); @@ -475,6 +441,9 @@ void ctexpcube::get_parameters(void) ***************************************************************************/ void ctexpcube::init_cube(void) { + // Write header into logger + log_header1(TERSE, "Initialise exposure cube"); + // Extract exposure cube definition const GWcs* proj = static_cast(m_expcube.cube().projection()); std::string wcs = m_expcube.cube().projection()->code(); @@ -492,12 +461,6 @@ void ctexpcube::init_cube(void) // If requested, insert energies at all event list energy boundaries if (m_addbounds) { - // Set logger - GLog* logger = NULL; - if (logTerse()) { - logger = &log; - } - // Loop over all observations for (int i = 0; i < m_obs.size(); ++i) { @@ -516,7 +479,7 @@ void ctexpcube::init_cube(void) } // Insert energy boundaries - energies = insert_energy_boundaries(energies, *cta, logger); + energies = insert_energy_boundaries(energies, *cta); } // endfor: looped over all observations @@ -525,11 +488,9 @@ void ctexpcube::init_cube(void) // Setup exposure cube m_expcube = GCTACubeExposure(wcs, coords, x, y, dx, dy, nx, ny, energies); - // Log exposure cube - if (logTerse()) { - log << m_expcube.print(m_chatter) << std::endl; - } + // Write exposure cube in logger + log_string(NORMAL, m_expcube.print(m_chatter)); // Return return; -}; +} diff --git a/src/ctobssim/ctobssim.cpp b/src/ctobssim/ctobssim.cpp index d581984..d6302ce 100644 --- a/src/ctobssim/ctobssim.cpp +++ b/src/ctobssim/ctobssim.cpp @@ -324,7 +324,7 @@ void ctobssim::run(void) wrklog.name(log.name()); // Set a big value to avoid flushing - wrklog.max_size(10000000); + wrklog.buffer_size(10000000); // Loop over all observation in the container. If OpenMP support // is enabled, this loop will be parallelized. diff --git a/src/ctpsfcube/ctpsfcube.cpp b/src/ctpsfcube/ctpsfcube.cpp index a4d435d..3ffb0dd 100644 --- a/src/ctpsfcube/ctpsfcube.cpp +++ b/src/ctpsfcube/ctpsfcube.cpp @@ -216,35 +216,20 @@ void ctpsfcube::run(void) // Get task parameters get_parameters(); - // Write header - if (logTerse()) { - log << std::endl; - log.header1("Initialise PSF cube"); - } + // Write input observation container into logger + log_observations(NORMAL, m_obs, "Input observation"); - // Initialise exposure cube + // Initialise PSF cube init_cube(); - // Write observation(s) into logger - if (logTerse()) { - log << std::endl; - log.header1(gammalib::number("Observation",m_obs.size())); - log << m_obs.print(m_chatter) << std::endl; - } - - // Write header - if (logTerse()) { - log << std::endl; - log.header1("Generate PSF cube"); - } + // Write header into logger + log_header1(TERSE, "Generate PSF cube"); // Fill PSF cube m_psfcube.fill(m_obs, &log); - // Log PSF cube - if (logTerse()) { - log << m_psfcube.print(m_chatter) << std::endl; - } + // Write PSF cube into logger + log_string(EXPLICIT, m_psfcube.print(m_chatter)); // Return return; @@ -253,31 +238,34 @@ void ctpsfcube::run(void) /***********************************************************************//** * @brief Save PSF cube + * + * Saves the PSF cube into a FITS file. A file is only created if the + * "outcube" parameter is not empty and if a PSF cube has been computed. ***************************************************************************/ void ctpsfcube::save(void) { - // Write header - if (logTerse()) { - log << std::endl; - log.header1("Save PSF cube"); - } + // Write header into logger + log_header1(TERSE, "Save PSF cube"); // Get output filename m_outcube = (*this)["outcube"].filename(); - // Save only if filename is non-empty - if (!m_outcube.is_empty()) { - - // Log filename - if (logTerse()) { - log << gammalib::parformat("PSF cube file"); - log << m_outcube.url() << std::endl; - } + // Determine whether the cube is empty + bool cube_is_empty = ((m_psfcube.cube().nx() == 0) || + (m_psfcube.cube().ny() == 0) || + (m_psfcube.cube().nmaps() == 0)); - // Save PSF cube + // Save PSF cube if filename and the PSF cube are not empty + if (!m_outcube.is_empty() && !cube_is_empty) { m_psfcube.save(m_outcube, clobber()); + } + // Write into logger what has been done + std::string fname = (m_outcube.is_empty()) ? "NONE" : m_outcube.url(); + if (cube_is_empty) { + fname.append(" (cube is empty, no file created)"); } + log_value(NORMAL, "PSF cube file", fname); // Return return; @@ -381,7 +369,7 @@ void ctpsfcube::get_parameters(void) } - // ... otherwise setup the exposure cube from the counts map + // ... otherwise setup the PSF cube from the counts map else { // Load event cube from filename @@ -416,7 +404,10 @@ void ctpsfcube::get_parameters(void) ***************************************************************************/ void ctpsfcube::init_cube(void) { - // Extract exposure cube definition + // Write header into logger + log_header1(TERSE, "Initialise PSF cube"); + + // Extract PSF cube definition const GWcs* proj = static_cast(m_psfcube.cube().projection()); std::string wcs = m_psfcube.cube().projection()->code(); std::string coords = m_psfcube.cube().projection()->coordsys(); @@ -435,12 +426,6 @@ void ctpsfcube::init_cube(void) // If requested, insert energies at all event list energy boundaries if (m_addbounds) { - // Set logger - GLog* logger = NULL; - if (logTerse()) { - logger = &log; - } - // Loop over all observations for (int i = 0; i < m_obs.size(); ++i) { @@ -459,7 +444,7 @@ void ctpsfcube::init_cube(void) } // Insert energy boundaries - energies = insert_energy_boundaries(energies, *cta, logger); + energies = insert_energy_boundaries(energies, *cta); } // endfor: looped over all observations @@ -469,11 +454,9 @@ void ctpsfcube::init_cube(void) m_psfcube = GCTACubePsf(wcs, coords, x, y, dx, dy, nx, ny, energies, dmax, ndbins); - // Log PSF cube - if (logTerse()) { - log << m_psfcube.print(m_chatter) << std::endl; - } + // Write PSF cube in logger + log_string(NORMAL, m_psfcube.print(m_chatter)); // Return return; -}; +} diff --git a/src/support/ctool.cpp b/src/support/ctool.cpp index a9c92af..9eb8a3e 100644 --- a/src/support/ctool.cpp +++ b/src/support/ctool.cpp @@ -68,15 +68,14 @@ /***********************************************************************//** * @brief Void constructor + * + * Constructs an empty ctools. ***************************************************************************/ ctool::ctool(void) : GApplication() { // Initialise members init_members(); - // Write header into logger - log_header(); - // Return return; } @@ -87,6 +86,16 @@ ctool::ctool(void) : GApplication() * * @param[in] name Application name. * @param[in] version Application version. + * + * Constructs a ctool from the @p name and @p version. The constructor uses + * the equivalent GApplication constructor to set the parameter filename to + * ".par" and the log filename to "".log. The parameters will be + * loaded from the parameter file. + * + * No log file will be opened. To open the log file an explicit call to the + * logFileOpen() method is required. + * + * This constructor should be used for using a ctool from Python. ***************************************************************************/ ctool::ctool(const std::string& name, const std::string& version) : GApplication(name, version) @@ -94,9 +103,6 @@ ctool::ctool(const std::string& name, const std::string& version) : // Initialise members init_members(); - // Write header into logger - log_header(); - // Return return; } @@ -109,6 +115,16 @@ ctool::ctool(const std::string& name, const std::string& version) : * @param[in] version Application version. * @param[in] argc Number of arguments in command line. * @param[in] argv Array of command line arguments. + * + * Constructs a ctool from the @p name, @p version and command line + * arguments. The constructor uses the equivalent GApplication constructor + * to set the parameter filename to ".par" and the log filename to + * "".log. The parameters will be loaded from the parameter file. + * In addition, the constructor opens the log file. + * + * If the "--help" option is provided as command line argument a help text + * about the usage of the ctool will be shown in the console and the ctool + * will exit. No log file will be opened in that case. ***************************************************************************/ ctool::ctool(const std::string& name, const std::string& version, int argc, char *argv[]) : @@ -123,8 +139,8 @@ ctool::ctool(const std::string& name, const std::string& version, // Initialise members init_members(); - // Write header into logger - log_header(); + // Open the log file + logFileOpen(); // Return return; @@ -135,6 +151,8 @@ ctool::ctool(const std::string& name, const std::string& version, * @brief Copy constructor * * @param[in] app Application. + * + * Construct a ctools from another ctool instance. ***************************************************************************/ ctool::ctool(const ctool& app) : GApplication(app) { @@ -172,6 +190,8 @@ ctool::~ctool(void) * @brief Assignment operator * * @param[in] app Application. + * + * Assigns one ctool to another. ***************************************************************************/ ctool& ctool::operator=(const ctool& app) { @@ -207,7 +227,8 @@ ctool& ctool::operator=(const ctool& app) * @brief Execute application * * This is the main execution method of a ctool. The method is invoked when - * the executable is called from command line. + * the executable is called from the command line. It signals that output + * parameters should be read ahead, runs the tools, and saves the results. ***************************************************************************/ void ctool::execute(void) { @@ -275,9 +296,6 @@ void ctool::copy_members(const ctool& app) ***************************************************************************/ void ctool::free_members(void) { - // Write separator into logger - log_string(TERSE, ""); - // Return return; } @@ -1374,10 +1392,11 @@ std::string ctool::get_obs_header(const GObservation* obs) * @param[in] energies Energies. * @param[in] obs Observation container. * @return Energies. + * + * Inserts the energy boundaries of an observation in a list of @p energies. ***************************************************************************/ GEnergies ctool::insert_energy_boundaries(const GEnergies& energies, - const GCTAObservation& obs, - GLog* log) const + const GCTAObservation& obs) { // Create copy of input energies GEnergies engs = energies; @@ -1428,11 +1447,8 @@ GEnergies ctool::insert_energy_boundaries(const GEnergies& energies, engs.append(energy); } - // Log energy insertion - if (log != NULL) { - *log << gammalib::parformat("Insert energy"); - *log << energy.print() << std::endl; - } + // Log energy insertion. Circumvent const correctness + log_value(NORMAL, "Insert energy", energy.print()); } // endif: energy insertion requested @@ -1444,7 +1460,63 @@ GEnergies ctool::insert_energy_boundaries(const GEnergies& energies, /***********************************************************************//** - * @brief Dumps help text in the console + * @brief Set warning string if there are too few energies + * + * @param[in] energies Energies. + * @return Warning string. + * + * Sets a warning string if there are too few @p energies in the container. + * For a binned or stacked analysis to be accurate, at least 25 bins per + * decade are required. If the provided number of energies is less than this + * number a warning string will be returned. Otherwise an empty string will + * be returned. + ***************************************************************************/ +std::string ctool::warn_too_few_energies(const GEnergies& energies) const +{ + // Initialise warning + std::string warning; + + // Retrieve the number of energies + int n = energies.size(); + + // Compute the required number of energy bins + double logEmin = std::log10(energies[0].TeV()); + double logEmax = std::log10(energies[n-1].TeV()); + int nrequired = int((logEmax - logEmin) * 25.0); + + // Warn if there are not enough energy bins + if (n < nrequired) { + warning.append("\nWARNING: Only "+gammalib::str(n-1)+" energy bins " + "have been requested. This may be too few energy " + "bins." + "\n At least 25 bins per decade in energy are " + "recommended, which for the given" + "\n energy range would be "+ + gammalib::str(nrequired)+" bins. Consider increasing " + "the number of energy bins."); + } + + // Return warning + return warning; +} + + +/*========================================================================== + = = + = Private methods for SWIG = + = = + ==========================================================================*/ + +/***********************************************************************//** + * @brief Dump help text in the console + * + * Dumps the help text for the ctool into the console. The help text is + * located in the folder + * + * $CTOOLS//share/help/ + * + * of the ctools installation and the help file has the name ".txt", + * where stands for the name of the ctool. ***************************************************************************/ void ctool::provide_help(void) const { diff --git a/src/support/ctool.hpp b/src/support/ctool.hpp index b3c20ff..b2dabf0 100644 --- a/src/support/ctool.hpp +++ b/src/support/ctool.hpp @@ -101,8 +101,10 @@ protected: size_t get_current_rss(void); std::string get_obs_header(const GObservation* obs); GEnergies insert_energy_boundaries(const GEnergies& energies, - const GCTAObservation& obs, - GLog* log = NULL) const; + const GCTAObservation& obs); + + // Protected warning strings + std::string warn_too_few_energies(const GEnergies& energies) const; // Protected members bool m_read_ahead; //!< Read ahead output parameters diff --git a/test/test_ctbin.py b/test/test_ctbin.py index dea3cdd..a54f042 100755 --- a/test/test_ctbin.py +++ b/test/test_ctbin.py @@ -134,7 +134,6 @@ class Test(test): bin.clear() # Now set ctbin parameters - #bin = ctools.ctbin() bin['inobs'] = self._events bin['outcube'] = 'ctbin_py1.fits' bin['ebinalg'] = 'LOG' @@ -159,7 +158,7 @@ class Test(test): self._check_observation(bin, 5542) self._check_cube(bin.cube(), 5542) - # Test copy constructor + # Copy ctbin tool cpy_bin = bin.copy() # Check content of observation container and counts cube diff --git a/test/test_ctbkgcube.py b/test/test_ctbkgcube.py index e0d6120..573f9af 100755 --- a/test/test_ctbkgcube.py +++ b/test/test_ctbkgcube.py @@ -18,6 +18,7 @@ # along with this program. If not, see . # # ========================================================================== +import os import gammalib import ctools from testing import test @@ -85,7 +86,7 @@ class Test(test): 'Check successful execution from command line') # Check result file - self._check_result_file('ctbkgcube_cmd1.fits') + self._check_result_files('ctbkgcube_cmd1') # Setup ctbkgcube command cmd = ctbkgcube+' inobs="event_file_that_does_not_exist.fits"'+ \ @@ -111,8 +112,29 @@ class Test(test): """ Test ctbkgcube from Python """ - # Set-up ctbkgcube + # Allocate ctbkgcube bkgcube = ctools.ctbkgcube() + + # Check that empty ctbkgcube tool holds a background cube that has + # no energy bins + self._check_cube(bkgcube.bkgcube(), nenergies=0) + + # Check that saving does not nothing + bkgcube['outcube'] = 'ctbkgcube_py0.fits' + bkgcube['outmodel'] = 'ctbkgcube_py0.xml' + bkgcube['logfile'] = 'ctbkgcube_py0.log' + bkgcube.logFileOpen() + bkgcube.save() + self.test_assert(not os.path.isfile('ctbkgcube_py0.fits'), + 'Check that no background cube has been created') + + # Check model container + self._check_models(gammalib.GModels('ctbkgcube_py0.xml'), nmodels=0) + + # Check that clearing does not lead to an exception or segfault + bkgcube.clear() + + # Now set ctbkgcube parameters bkgcube['inobs'] = self._events bkgcube['inmodel'] = self._model bkgcube['incube'] = 'NONE' @@ -120,7 +142,7 @@ class Test(test): bkgcube['irf'] = self._irf bkgcube['ebinalg'] = 'LOG' bkgcube['emin'] = 0.1 - bkgcube['emax'] = 100 + bkgcube['emax'] = 100.0 bkgcube['enumbins'] = 20 bkgcube['nxpix'] = 10 bkgcube['nypix'] = 10 @@ -139,45 +161,43 @@ class Test(test): bkgcube.run() bkgcube.save() - # Check result file - self._check_result_file('ctbkgcube_py1.fits') - - # Set-up ctbkgcube from counts cube - bkgcube = ctools.ctbkgcube() - bkgcube['inobs'] = self._events - bkgcube['inmodel'] = self._model - bkgcube['incube'] = self._cntcube - bkgcube['caldb'] = self._caldb - bkgcube['irf'] = self._irf - bkgcube['outcube'] = 'ctbkgcube_py2.fits' - bkgcube['outmodel'] = 'ctbkgcube_py2.xml' - bkgcube['logfile'] = 'ctbkgcube_py2.log' - bkgcube['chatter'] = 3 - bkgcube['publish'] = True - bkgcube['addbounds'] = True - - # Execute ctbkgcube tool - bkgcube.logFileOpen() # Make sure we get a log file - bkgcube.execute() - - # Check result file - self._check_result_file('ctbkgcube_py2.fits') - - # Copy ctbkgcube tool and execute copied tool - cpy_bkgcube = bkgcube - cpy_bkgcube['outcube'] = 'ctbkgcube_py3.fits' - cpy_bkgcube['outmodel'] = 'ctbkgcube_py3.xml' - cpy_bkgcube['logfile'] = 'ctbkgcube_py3.log' - cpy_bkgcube['chatter'] = 4 + # Check result files + self._check_result_files('ctbkgcube_py1') + + # Copy ctbkgcube tool + cpy_bkgcube = bkgcube.copy() + + # Check background cube and model container of ctbkgcube copy + self._check_cube(cpy_bkgcube.bkgcube()) + self._check_models(cpy_bkgcube.models()) + + # Execute copy of ctbkgcube tool again, now with a higher chatter + # level than before. In addition, use counts cube to define the + # background cube + #cpy_bkgcube['inobs'] = self._events + #cpy_bkgcube['inmodel'] = self._model + cpy_bkgcube['incube'] = self._cntcube + #cpy_bkgcube['caldb'] = self._caldb + #cpy_bkgcube['irf'] = self._irf + cpy_bkgcube['outcube'] = 'ctbkgcube_py2.fits' + cpy_bkgcube['outmodel'] = 'ctbkgcube_py2.xml' + cpy_bkgcube['logfile'] = 'ctbkgcube_py2.log' + cpy_bkgcube['chatter'] = 3 + cpy_bkgcube['publish'] = True + cpy_bkgcube['addbounds'] = True + cpy_bkgcube.logFileOpen() # Make sure we get a log file cpy_bkgcube.execute() - # Check result file - self._check_result_file('ctbkgcube_py3.fits') + # Check result files + self._check_result_files('ctbkgcube_py2') - # Clear ctbkgcube tool - bkgcube.clear() + # Now clear copy of ctbkgcube tool + cpy_bkgcube.clear() - # TODO: Do some test after clearing of ctbkgcube tool + # Check that the cleared copy has also cleared the background cube + # and model container + self._check_cube(cpy_bkgcube.bkgcube(), nenergies=0) + self._check_models(cpy_bkgcube.models(), nmodels=0) # Prepare observation container obs1 = gammalib.GCTAObservation(self._events) @@ -195,47 +215,94 @@ class Test(test): # Set-up ctbkgcube from observation container bkgcube = ctools.ctbkgcube(obs) - #bkgcube['inobs'] = self._events - #bkgcube['inmodel'] = self._model - bkgcube['incube'] = 'NONE' - bkgcube['caldb'] = self._caldb - bkgcube['irf'] = self._irf - bkgcube['ebinalg'] = 'LOG' - bkgcube['emin'] = 0.1 - bkgcube['emax'] = 100 - bkgcube['enumbins'] = 20 - bkgcube['nxpix'] = 10 - bkgcube['nypix'] = 10 - bkgcube['binsz'] = 0.4 - bkgcube['coordsys'] = 'CEL' - bkgcube['proj'] = 'CAR' - bkgcube['xref'] = 83.63 - bkgcube['yref'] = 22.01 - bkgcube['outcube'] = 'ctbkgcube_py4.fits' - bkgcube['outmodel'] = 'ctbkgcube_py4.xml' - bkgcube['logfile'] = 'ctbkgcube_py4.log' - bkgcube['chatter'] = 4 + bkgcube['incube'] = 'NONE' + bkgcube['caldb'] = self._caldb + bkgcube['irf'] = self._irf + bkgcube['ebinalg'] = 'LOG' + bkgcube['emin'] = 0.2 + bkgcube['emax'] = 150.0 + bkgcube['enumbins'] = 20 + bkgcube['nxpix'] = 10 + bkgcube['nypix'] = 10 + bkgcube['binsz'] = 0.4 + bkgcube['coordsys'] = 'CEL' + bkgcube['proj'] = 'CAR' + bkgcube['xref'] = 83.63 + bkgcube['yref'] = 22.01 + bkgcube['outcube'] = 'ctbkgcube_py3.fits' + bkgcube['outmodel'] = 'ctbkgcube_py3.xml' + bkgcube['logfile'] = 'ctbkgcube_py3.log' + bkgcube['addbounds'] = True + bkgcube['chatter'] = 4 # Execute ctbkgcube tool bkgcube.logFileOpen() # Make sure we get a log file bkgcube.execute() - # Check result file - self._check_result_file('ctbkgcube_py4.fits') + # Check result files + self._check_result_files('ctbkgcube_py3', nenergies=23) # Return return - # Check result file - def _check_result_file(self, filename): + # Check result files + def _check_result_files(self, filename, nenergies=21, nmodels=2): """ Check result file + + Parameters + ---------- + filename : str + Test file name without extension + nenergies : int, optional + Number of energy bins """ - # Open result file - result = gammalib.GCTACubeBackground(filename) + # Open background cube + cube = gammalib.GCTACubeBackground(filename+'.fits') + + # Open model definition file + models = gammalib.GModels(filename+'.xml') + # Check cube + self._check_cube(cube, nenergies=nenergies) + + # Check models + self._check_models(models, nmodels=nmodels) + + # Return + return + + # Check background cube + def _check_cube(self, cube, nenergies=21): + """ + Check background cube + + Parameters + ---------- + cube : `~gammalib.GCTACubeBackground` + Background cube + nenergies : int, optional + Number of energy bins + """ # Check dimensions - self.test_value(len(result.energies()), 21, 'Check for 21 energy values') + self.test_value(len(cube.energies()), nenergies, + 'Check number of energy maps') # Return return + + # Check model container + def _check_models(self, models, nmodels=2): + """ + Check model container + + Parameters + ---------- + models : `~gammalib.GModels` + Model container + """ + # Check number of models + self.test_value(models.size(), nmodels, 'Check number of models') + + # Return + return diff --git a/test/test_ctedispcube.py b/test/test_ctedispcube.py index ac29b05..9c03035 100755 --- a/test/test_ctedispcube.py +++ b/test/test_ctedispcube.py @@ -18,6 +18,7 @@ # along with this program. If not, see . # # ========================================================================== +import os import gammalib import ctools from testing import test @@ -109,16 +110,32 @@ class Test(test): """ Test ctedispcube from Python """ - # Set-up ctedispcube + # Allocate ctedispcube edispcube = ctools.ctedispcube() + + # Check that empty ctedispcube tool holds an energy dispersion + # cube that has no energy and migration bins + self._check_cube(edispcube.edispcube(), nenergies=0, nmigras=0) + + # Check that saving does not nothing + edispcube['outcube'] = 'ctedispcube_py0.fits' + edispcube['logfile'] = 'ctedispcube_py0.log' + edispcube.logFileOpen() + edispcube.save() + self.test_assert(not os.path.isfile('ctedispcube_py0.fits'), + 'Check that no energy dispersion cube has been created') + + # Check that clearing does not lead to an exception or segfault + edispcube.clear() + + # Now set ctedispcube parameters edispcube['inobs'] = self._events edispcube['incube'] = 'NONE' - edispcube['outcube'] = 'ctedispcube_py1.fits' edispcube['caldb'] = self._caldb edispcube['irf'] = self._irf edispcube['ebinalg'] = 'LOG' edispcube['emin'] = 0.1 - edispcube['emax'] = 100 + edispcube['emax'] = 100.0 edispcube['enumbins'] = 20 edispcube['nxpix'] = 10 edispcube['nypix'] = 10 @@ -129,31 +146,88 @@ class Test(test): edispcube['yref'] = 22.01 edispcube['migramax'] = 2.0 edispcube['migrabins'] = 10 + edispcube['outcube'] = 'ctedispcube_py1.fits' edispcube['logfile'] = 'ctedispcube_py1.log' edispcube['chatter'] = 2 # Run ctedispcube tool - edispcube.logFileOpen() # Make sure we get a log file + edispcube.logFileOpen() # Make sure we get a log file edispcube.run() edispcube.save() # Check result file self._check_result_file('ctedispcube_py1.fits') + # Copy ctedispcube tool + cpy_edispcube = edispcube.copy() + + # Check energy dispersion cube of ctedispcube copy + self._check_cube(cpy_edispcube.edispcube()) + + # Execute copy of ctedispcube tool again, now with a higher chatter + # level than before + cpy_edispcube['emin'] = 0.2 + cpy_edispcube['emax'] = 150.0 + cpy_edispcube['outcube'] = 'ctedispcube_py2.fits' + cpy_edispcube['logfile'] = 'ctedispcube_py2.log' + cpy_edispcube['addbounds'] = True + cpy_edispcube['chatter'] = 3 + cpy_edispcube.logFileOpen() # Needed to get a new log file + cpy_edispcube.execute() + + # Check result file + self._check_result_file('ctedispcube_py2.fits', nenergies=23) + + # Now clear copy of ctedispcube tool + cpy_edispcube.clear() + + # Check that the cleared copy has also cleared the energy dispersion + # cube + self._check_cube(cpy_edispcube.edispcube(), nenergies=0, nmigras=0) + # Return return # Check result file - def _check_result_file(self, filename): + def _check_result_file(self, filename, nenergies=21): """ Check result file + + Parameters + ---------- + filename : str + Energy dispersion cube file name + nenergies : int, optional + Number of energy bins """ - # Open result file - result = gammalib.GCTACubeEdisp(filename) + # Open energy dispersion cube + cube = gammalib.GCTACubeEdisp(filename) + + # Check cube + self._check_cube(cube, nenergies=nenergies) + # Return + return + + # Check energy dispersion cube + def _check_cube(self, cube, nenergies=21, nmigras=10): + """ + Check energy dispersion cube + + Parameters + ---------- + cube : `~gammalib.GCTACubeEdisp` + Energy dispersion cube + nenergies : int, optional + Number of energy bins + nmigras : int, optional + Number of migration bins + """ # Check dimensions - self.test_value(len(result.energies()), 21, 'Check for 21 energy maps') - self.test_value(len(result.migras()), 10, 'Check for 10 migration bins') + self.test_value(len(cube.energies()), nenergies, + 'Check number of energy maps') + self.test_value(len(cube.migras()), nmigras, + 'Check number of migration bins') # Return return diff --git a/test/test_ctexpcube.py b/test/test_ctexpcube.py index 1e545ce..60cf22e 100755 --- a/test/test_ctexpcube.py +++ b/test/test_ctexpcube.py @@ -18,6 +18,7 @@ # along with this program. If not, see . # # ========================================================================== +import os import gammalib import ctools from testing import test @@ -107,16 +108,32 @@ class Test(test): """ Test ctexpcube from Python """ - # Set-up ctexpcube + # Allocate ctexpcube expcube = ctools.ctexpcube() + + # Check that empty ctexpcube tool holds an exposure cube that has + # no energy bins + self._check_cube(expcube.expcube(), nenergies=0) + + # Check that saving does not nothing + expcube['outcube'] = 'ctexpcube_py0.fits' + expcube['logfile'] = 'ctexpcube_py0.log' + expcube.logFileOpen() + expcube.save() + self.test_assert(not os.path.isfile('ctexpcube_py0.fits'), + 'Check that no exposure cube has been created') + + # Check that clearing does not lead to an exception or segfault + expcube.clear() + + # Now set ctexpcube parameters expcube['inobs'] = self._events expcube['incube'] = 'NONE' - expcube['outcube'] = 'ctexpcube_py1.fits' expcube['caldb'] = self._caldb expcube['irf'] = self._irf expcube['ebinalg'] = 'LOG' expcube['emin'] = 0.1 - expcube['emax'] = 100 + expcube['emax'] = 100.0 expcube['enumbins'] = 20 expcube['nxpix'] = 200 expcube['nypix'] = 200 @@ -126,6 +143,7 @@ class Test(test): expcube['xref'] = 83.63 expcube['yref'] = 22.01 expcube['logfile'] = 'ctexpcube_py1.log' + expcube['outcube'] = 'ctexpcube_py1.fits' expcube['chatter'] = 2 # Run ctexpcube tool @@ -136,19 +154,72 @@ class Test(test): # Check result file self._check_result_file('ctexpcube_py1.fits') + # Copy ctexpcube tool + cpy_expcube = expcube.copy() + + # Check exposure cube of ctexpcube copy + self._check_cube(cpy_expcube.expcube()) + + # Execute copy of ctexpcube tool again, now with a higher chatter + # level than before + cpy_expcube['emin'] = 0.2 + cpy_expcube['emax'] = 150.0 + cpy_expcube['outcube'] = 'ctexpcube_py2.fits' + cpy_expcube['logfile'] = 'ctexpcube_py2.log' + cpy_expcube['publish'] = True + cpy_expcube['addbounds'] = True + cpy_expcube['chatter'] = 3 + cpy_expcube.logFileOpen() # Needed to get a new log file + cpy_expcube.execute() + + # Check result file + self._check_result_file('ctexpcube_py2.fits', nenergies=23) + + # Now clear copy of ctexpcube tool + cpy_expcube.clear() + + # Check that the cleared copy has also cleared the exposure cube + self._check_cube(cpy_expcube.expcube(), nenergies=0) + # Return return # Check result file - def _check_result_file(self, filename): + def _check_result_file(self, filename, nenergies=21): """ Check result file + + Parameters + ---------- + filename : str + Exposure cube file name + nenergies : int, optional + Number of energy bins """ - # Open result file - result = gammalib.GCTACubeExposure(filename) + # Open exposure cube + cube = gammalib.GCTACubeExposure(filename) + + # Check cube + self._check_cube(cube, nenergies=nenergies) + # Return + return + + # Check exposure cube + def _check_cube(self, cube, nenergies=21): + """ + Check exposure cube + + Parameters + ---------- + cube : `~gammalib.GCTACubeExposure` + Exposure cube + nenergies : int, optional + Number of energy bins + """ # Check dimensions - self.test_value(len(result.energies()), 21, 'Check for 21 energy values') + self.test_value(len(cube.energies()), nenergies, + 'Check number of energy maps') # Return return diff --git a/test/test_ctpsfcube.py b/test/test_ctpsfcube.py index 405a508..0d3a50a 100755 --- a/test/test_ctpsfcube.py +++ b/test/test_ctpsfcube.py @@ -18,6 +18,7 @@ # along with this program. If not, see . # # ========================================================================== +import os import gammalib import ctools from testing import test @@ -109,8 +110,25 @@ class Test(test): """ Test ctpsfcube from Python """ - # Set-up ctpsfcube + # Allocate ctpsfcube psfcube = ctools.ctpsfcube() + + # Check that empty ctpsfcube tool holds a PSF cube that has no energy + # bins + self._check_cube(psfcube.psfcube(), nenergies=0) + + # Check that saving does not nothing + psfcube['outcube'] = 'ctpsfcube_py0.fits' + psfcube['logfile'] = 'ctpsfcube_py0.log' + psfcube.logFileOpen() + psfcube.save() + self.test_assert(not os.path.isfile('ctpsfcube_py0.fits'), + 'Check that no PSF cube has been created') + + # Check that clearing does not lead to an exception or segfault + psfcube.clear() + + # Now set ctpsfcube parameters psfcube['inobs'] = self._events psfcube['incube'] = 'NONE' psfcube['outcube'] = 'ctpsfcube_py1.fits' @@ -140,19 +158,71 @@ class Test(test): # Check result file self._check_result_file('ctpsfcube_py1.fits') + # Copy ctpsfcube tool + cpy_psfcube = psfcube.copy() + + # Check PSF cube of ctpsfcube copy + self._check_cube(cpy_psfcube.psfcube()) + + # Execute copy of ctpsfcube tool again, now with a higher chatter + # level than before + cpy_psfcube['emin'] = 0.2 + cpy_psfcube['emax'] = 150.0 + cpy_psfcube['outcube'] = 'ctpsfcube_py2.fits' + cpy_psfcube['logfile'] = 'ctpsfcube_py2.log' + cpy_psfcube['addbounds'] = True + cpy_psfcube['chatter'] = 3 + cpy_psfcube.logFileOpen() # Needed to get a new log file + cpy_psfcube.execute() + + # Check result file + self._check_result_file('ctpsfcube_py2.fits', nenergies=23) + + # Now clear copy of ctpsfcube tool + cpy_psfcube.clear() + + # Check that the cleared copy has also cleared the PSF cube + self._check_cube(cpy_psfcube.psfcube(), nenergies=0) + # Return return # Check result file - def _check_result_file(self, filename): + def _check_result_file(self, filename, nenergies=21): """ Check result file + + Parameters + ---------- + filename : str + PSF cube file name + nenergies : int, optional + Number of energy bins """ - # Open result file - result = gammalib.GCTACubePsf(filename) + # Open PSF cube + cube = gammalib.GCTACubePsf(filename) + # Check cube + self._check_cube(cube, nenergies=nenergies) + + # Return + return + + # Check PSF cube + def _check_cube(self, cube, nenergies=21): + """ + Check PSF cube + + Parameters + ---------- + cube : `~gammalib.GCTACubePsf` + PSF cube + nenergies : int, optional + Number of energy bins + """ # Check dimensions - self.test_value(len(result.energies()), 21, 'Check for 21 energy values') + self.test_value(len(cube.energies()), nenergies, + 'Check number of energy maps') # Return return