ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
ctcubemask.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * ctcubemask - Cube filter tool *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2014-2022 by Chia-Chun Lu *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file ctcubemask.cpp
23  * @brief Cube filter tool implementation
24  * @author Chia-Chun Lu
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <cstdio>
32 #include "ctcubemask.hpp"
33 #include "GTools.hpp"
34 
35 
36 /* __ Method name definitions ____________________________________________ */
37 #define G_APPLY_MASK "ctcubemask::apply_mask(GCTAObservation*)"
38 
39 /* __ Debug definitions __________________________________________________ */
40 
41 /* __ Coding definitions _________________________________________________ */
42 
43 
44 /*==========================================================================
45  = =
46  = Constructors/destructors =
47  = =
48  ==========================================================================*/
49 
50 /***********************************************************************//**
51  * @brief Void constructor
52  ***************************************************************************/
54 {
55  // Initialise members
56  init_members();
57 
58  // Return
59  return;
60 }
61 
62 
63 /***********************************************************************//**
64  * @brief Observations constructor
65  *
66  * param[in] obs Observation container.
67  *
68  * This constructor creates an instance of the class that is initialised from
69  * an observation container.
70  ***************************************************************************/
71 ctcubemask::ctcubemask(const GObservations& obs) :
72  ctobservation(CTCUBEMASK_NAME, VERSION, obs)
73 {
74  // Initialise members
75  init_members();
76 
77  // Return
78  return;
79 }
80 
81 
82 
83 
84 /***********************************************************************//**
85  * @brief Command line constructor
86  *
87  * @param[in] argc Number of arguments in command line.
88  * @param[in] argv Array of command line arguments.
89  ***************************************************************************/
90 ctcubemask::ctcubemask(int argc, char *argv[]) :
91  ctobservation(CTCUBEMASK_NAME, VERSION, argc, argv)
92 {
93  // Initialise members
94  init_members();
95 
96  // Return
97  return;
98 }
99 
100 
101 /***********************************************************************//**
102  * @brief Copy constructor
103  *
104  * @param[in] app Application.
105  ***************************************************************************/
107 {
108  // Initialise members
109  init_members();
110 
111  // Copy members
112  copy_members(app);
113 
114  // Return
115  return;
116 }
117 
118 
119 /***********************************************************************//**
120  * @brief Destructor
121  ***************************************************************************/
123 {
124  // Free members
125  free_members();
126 
127  // Return
128  return;
129 }
130 
131 
132 /*==========================================================================
133  = =
134  = Operators =
135  = =
136  ==========================================================================*/
137 
138 /***********************************************************************//**
139  * @brief Assignment operator
140  *
141  * @param[in] app Application.
142  * @return Application.
143  ***************************************************************************/
145 {
146  // Execute only if object is not identical
147  if (this != &app) {
148 
149  // Copy base class members
150  this->ctobservation::operator=(app);
151 
152  // Free members
153  free_members();
154 
155  // Initialise members
156  init_members();
157 
158  // Copy members
159  copy_members(app);
160 
161  } // endif: object was not identical
162 
163  // Return this object
164  return *this;
165 }
166 
167 
168 /*==========================================================================
169  = =
170  = Public methods =
171  = =
172  ==========================================================================*/
173 
174 /***********************************************************************//**
175  * @brief Clear ctcubemask tool
176  *
177  * Clears ctcubemask tool.
178  ***************************************************************************/
180 {
181  // Free members
182  free_members();
184  this->ctool::free_members();
185 
186  // Clear base class (needed to conserve tool name and version)
187  this->GApplication::clear();
188 
189  // Initialise members
190  this->ctool::init_members();
192  init_members();
193 
194  // Write header into logger
195  log_header();
196 
197  // Return
198  return;
199 }
200 
201 
202 /***********************************************************************//**
203  * @brief Mask data cube
204  *
205  * This method reads in the application parameters and loops over all
206  * observations that were found to apply a mask on the event cube.
207  ***************************************************************************/
209 {
210  // Get parameters
211  get_parameters();
212 
213  // Write input observation container into logger
214  log_observations(NORMAL, m_obs, "Input observation");
215 
216  // Write header into logger
217  log_header1(TERSE, "Apply mask");
218 
219  // Initialise counters
220  int n_observations = 0;
221 
222  // Loop over all observation in the container
223  for (int i = 0; i < m_obs.size(); ++i) {
224 
225  // Initialise event input and output filenames
226  m_infiles.push_back("");
227 
228  // Get CTA observation
229  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
230 
231  // Continue only if observation is a CTA observation
232  if (obs != NULL) {
233 
234  // Write header for the current observation
235  log_header3(TERSE, get_obs_header(obs));
236 
237  // Increment counter
238  n_observations++;
239 
240  // Save event file name (for possible saving)
241  m_infiles[i] = obs->eventfile();
242 
243  // Apply mask on the event cube
244  apply_mask(obs);
245 
246  } // endif: had a CTA observation
247 
248  } // endfor: looped over all observations
249 
250  // If more than a single observation has been handled then make sure that
251  // an XML file will be used for storage
252  if (n_observations > 1) {
253  m_use_xml = true;
254  }
255 
256  // Write resulting observation container into logger
257  log_observations(NORMAL, m_obs, "Observations after event bin masking");
258 
259  // Optionally publish counts cube
260  if (m_publish) {
261  publish();
262  }
263 
264  // Return
265  return;
266 }
267 
268 
269 /***********************************************************************//**
270  * @brief Save the masked event cube(s)
271  *
272  * This method saves the masked event cube(s) into FITS file(s). There are
273  * two modes, depending on the m_use_xml flag.
274  *
275  * If m_use_xml is true, all masked event cube(s) will be saved into FITS
276  * files, where the output filenames are constructued from the input
277  * filenames by prepending the m_prefix string to name. Any path information
278  * will be stripped form the input name, hence event cube files will be written
279  * into the local working directory (unless some path information is present
280  * in the prefix). In addition, an XML file will be created that gathers
281  * the filename information for the masked event cube(s). If an XML file
282  * was present on input, all metadata information will be copied from this
283  * input file.
284  *
285  * If m_use_xml is false, the masked event cubes will be saved into a FITS
286  * file.
287  ***************************************************************************/
289 {
290  // Write header into logger
291  log_header1(TERSE, gammalib::number("Save counts cube", m_obs.size()));
292 
293  // Get counts cube filename
294  m_outcube = (*this)["outcube"].filename();
295 
296  // Case A: Save counts cube(s) and XML metadata information
297  if (m_use_xml) {
298 
299  // Get prefix
300  m_prefix = (*this)["prefix"].string();
301 
302  // Save XML
303  save_xml();
304  }
305 
306  // Case B: Save counts cube as FITS file
307  else {
308  save_fits();
309  }
310 
311  // Return
312  return;
313 }
314 
315 
316 /***********************************************************************//**
317  * @brief Publish counts cube
318  *
319  * @param[in] name Counts cube name.
320  *
321  * Publishes the first counts cube in the observation container on the VO
322  * Hub.
323  ***************************************************************************/
324 void ctcubemask::publish(const std::string& name)
325 {
326  // Write header into logger
327  log_header1(TERSE, "Publish counts cube");
328 
329  // Set default name is user name is empty
330  std::string user_name(name);
331  if (user_name.empty()) {
332  user_name = CTCUBEMASK_NAME;
333  }
334 
335  // Get first CTA observation from observation container
336  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
337  if (obs != NULL) {
338 
339  // Get counts cube
340  GCTAEventCube* cube = dynamic_cast<GCTAEventCube*>(obs->events());
341  if (cube != NULL) {
342 
343  // Log counts cube name
344  log_value(NORMAL, "Counts cube name", user_name);
345 
346  // Publish counts cube
347  cube->counts().publish(user_name);
348 
349  }
350  }
351 
352  // Return
353  return;
354 }
355 
356 
357 /*==========================================================================
358  = =
359  = Private methods =
360  = =
361  ==========================================================================*/
362 
363 /***********************************************************************//**
364  * @brief Initialise class members
365  ***************************************************************************/
367 {
368  // Initialise parameters
369  m_outcube.clear();
370  m_prefix.clear();
371  m_emin = 0.0;
372  m_emax = 0.0;
373  m_publish = false;
374 
375  // Initialise protected members
376  m_infiles.clear();
377  m_select_energy = true;
378 
379  // Return
380  return;
381 }
382 
383 
384 /***********************************************************************//**
385  * @brief Copy class members
386  *
387  * @param[in] app Application.
388  ***************************************************************************/
390 {
391  // Copy parameters
392  m_outcube = app.m_outcube;
393  m_prefix = app.m_prefix;
394  m_emin = app.m_emin;
395  m_emax = app.m_emax;
396  m_publish = app.m_publish;
397 
398  // Copy protected members
399  m_infiles = app.m_infiles;
401 
402  // Return
403  return;
404 }
405 
406 
407 /***********************************************************************//**
408  * @brief Delete class members
409  ***************************************************************************/
411 {
412  // Return
413  return;
414 }
415 
416 
417 /***********************************************************************//**
418  * @brief Get application parameters
419  *
420  * Get all task parameters from parameter file or (if required) by querying
421  * the user.
422  *
423  * This method also loads observations if no observations are yet allocated.
424  * Observations are either loaded from a single CTA even cube, or from a
425  * XML file using the metadata information that is stored in that file.
426  ***************************************************************************/
428 {
429  // Initialise selection flags
430  m_select_energy = true;
431 
432  // Setup observations from "inobs" parameter. Do not request response
433  // information and do not accept event lists.
434  setup_observations(m_obs, false, false, true);
435 
436  // Query "regfile" parameter
437  (*this)["regfile"].query();
438 
439  // Query the RoI
440  GCTARoi roi = get_roi();
441 
442  // Check for sanity of energy selection parameters
443  if ((*this)["emin"].is_valid() && (*this)["emax"].is_valid()) {
444  m_emin = (*this)["emin"].real();
445  m_emax = (*this)["emax"].real();
446  m_select_energy = true;
447  }
448  else {
449  m_select_energy = false;
450  }
451 
452  // Get remaining parameters
453  m_publish = (*this)["publish"].boolean();
454 
455  // If needed later, query output filename and prefix now
456  if (read_ahead()) {
457  (*this)["outcube"].query();
458  (*this)["prefix"].query();
459  }
460 
461  // Write parameters into logger
462  log_parameters(TERSE);
463 
464  // Return
465  return;
466 }
467 
468 
469 /***********************************************************************//**
470  * @brief Apply mask to event cube
471  *
472  * @param[in] obs CTA observation.
473  *
474  * Apply mask to an event cube. The mask sets content of event bins
475  * outside the region of interest or the energy of interest to -1.
476  * These pixels will be ignored in likelihood fitting.
477  ***************************************************************************/
478 void ctcubemask::apply_mask(GCTAObservation* obs)
479 {
480  // Get pointer to CTA event cube. Continue only if we have a cube
481  GCTAEventCube* cube = dynamic_cast<GCTAEventCube*>
482  (const_cast<GEvents*>(obs->events()));
483  if (cube != NULL) {
484 
485  // Extract event cube and energy boundaries
486  GSkyMap map = cube->counts();
487  const GEbounds& ebounds = cube->ebounds();
488 
489  // If no energy selection is required set energy boundaries to cube boundaries
490  if (!m_select_energy) {
491  m_emin = ebounds.emin().TeV();
492  m_emax = ebounds.emax().TeV();
493  }
494 
495  // Initialise energy selection
496  int npix = map.npix();
497  int n_ebin = ebounds.size();
498  int e_idx1 = 0;
499  int e_idx2 = n_ebin;
500 
501  // Determine number of events before masking
502  double sum_before = 0.0;
503  for (int i = 0; i < n_ebin; ++i) {
504  for (int pixel = 0; pixel < npix; ++pixel) {
505  if (map(pixel, i) >= 0.0) {
506  sum_before += map(pixel, i);
507  }
508  }
509  }
510 
511  // Loop over ebounds to find the first valid energy band
512  for (int i = 0; i < n_ebin; ++i) {
513  double emin = ebounds.emin(i).TeV() + 1.0e-6; // Rounding tolerance
514  if (emin >= m_emin) {
515  e_idx1 = i;
516  break;
517  }
518  }
519 
520  // Loop over ebounds to find the last valid energy band
521  for (int i = n_ebin-1; i >= 0; i--) {
522  double emax = ebounds.emax(i).TeV() - 1.0e-6; // Rounding tolerance
523  if (emax <= m_emax) {
524  e_idx2 = i;
525  break;
526  }
527  }
528 
529  // Set all pixels outside the desired energy bands to -1.0
530  for (int i = 0; i < e_idx1; ++i) {
531  for (int pixel = 0; pixel < npix; ++pixel) {
532  map(pixel,i) = -1.0;
533  }
534  }
535  for (int i = e_idx2 + 1; i < n_ebin; ++i) {
536  for (int pixel = 0; pixel < npix; ++pixel) {
537  map(pixel,i) = -1.0;
538  }
539  }
540 
541  // Log selected energy band
542  log_value(NORMAL, "Selected energy band",
543  gammalib::str(ebounds.emin(e_idx1).TeV())+" - "+
544  gammalib::str(ebounds.emax(e_idx2).TeV())+" TeV");
545 
546  // Set all pixels inside selected energy bands but outside RoI
547  // to -1.0 if requested
548  GCTARoi roi = get_roi(obs->pointing());
549  if (roi.is_valid()) {
550  GSkyRegionCircle circle(roi.centre().dir(), roi.radius());
551  for (int i = e_idx1; i <= e_idx2; ++i) {
552  for (int pixel = 0; pixel < npix; ++pixel) {
553  GSkyDir dir = map.inx2dir(pixel);
554  if (!circle.contains(dir)) {
555  map(pixel,i) = -1.0;
556  }
557  }
558  }
559 
560  // Log selected RoI
561  log_value(NORMAL, "Selected RoI",
562  "RA="+gammalib::str(circle.centre().ra_deg())+" deg, "+
563  "DEC="+gammalib::str(circle.centre().dec_deg())+" deg, "+
564  "Radius="+gammalib::str(circle.radius())+" deg");
565 
566  } // endif: applied RoI selection
567 
568  // Set all pixels inside selected energy bands and inside exclusion
569  // regions to -1.0
570  if ((*this)["regfile"].is_valid()) {
571  GSkyRegions regions = GSkyRegions((*this)["regfile"].filename());
572  for (int i = e_idx1; i <= e_idx2; ++i) {
573  for (int pixel = 0; pixel < npix; ++pixel) {
574  GSkyDir dir = map.inx2dir(pixel);
575  if (regions.contains(dir)) {
576  map(pixel,i) = -1.0;
577  }
578  }
579  }
580 
581  // Log exclusion regions
582  for (int i = 0; i < regions.size(); ++i) {
583  log_value(NORMAL, "Exclusion region "+gammalib::str(i+1),
584  region_string(*(regions[i])));
585  }
586  }
587  else {
588  log_value(NORMAL, "Exclusion regions", "None");
589  }
590 
591  // Determine number of events after masking
592  double sum_after = 0.0;
593  for (int i = 0; i < n_ebin; ++i) {
594  for (int pixel = 0; pixel < npix; ++pixel) {
595  if (map(pixel, i) >= 0.0) {
596  sum_after += map(pixel, i);
597  }
598  }
599  }
600 
601  // Write number of events before and after masking into logger
602  log_value(NORMAL, "Events before masking", sum_before);
603  log_value(NORMAL, "Events after masking", sum_after);
604 
605  // Put back map into the event cube
606  cube->counts(map);
607 
608  } // endif: observation contained an event cube
609 
610  // Return
611  return;
612 }
613 
614 
615 /***********************************************************************//**
616  * @brief Return region string
617  *
618  * @param[in] region Sky region.
619  *
620  * Returns a formatted region string for logging.
621  ***************************************************************************/
622 std::string ctcubemask::region_string(const GSkyRegion& region) const
623 {
624  // Initialise region string
625  std::string rstring;
626 
627  // Get pointer to circle
628  const GSkyRegionCircle* circle = dynamic_cast<const GSkyRegionCircle*>(&region);
629 
630  // If the sky region is a circle then append Right Ascenscion, Declination
631  // and Radius to the region string
632  if (circle != NULL) {
633  rstring.append("RA="+gammalib::str(circle->centre().ra_deg())+" deg, "+
634  "DEC="+gammalib::str(circle->centre().dec_deg())+" deg, "+
635  "Radius="+gammalib::str(circle->radius())+" deg");
636  }
637 
638  // Return region string
639  return rstring;
640 }
641 
642 
643 /***********************************************************************//**
644  * @brief Set output file name.
645  *
646  * @param[in] filename Input file name.
647  *
648  * This converts an input filename into an output filename by prepending a
649  * prefix to the input filename. Any path will be stripped from the input
650  * filename.
651  ***************************************************************************/
652 std::string ctcubemask::set_outfile_name(const std::string& filename) const
653 {
654  // Split input filename into path elements
655  std::vector<std::string> elements = gammalib::split(filename, "/");
656 
657  // The last path element is the filename
658  std::string outname = m_prefix + elements[elements.size()-1];
659 
660  // Return output filename
661  return outname;
662 }
663 
664 
665 /***********************************************************************//**
666  * @brief Save counts cube in FITS format.
667  *
668  * Save the counts cube as a FITS file. The filename of the FITS file is
669  * specified by the m_outfile member.
670  ***************************************************************************/
672 {
673  // Save only if there are observations
674  if (m_obs.size() > 0) {
675 
676  // Get CTA observation from observation container
677  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
678 
679  // Handle only CTA observations
680  if (obs != NULL) {
681 
682  // Log counts cube file name
683  log_value(NORMAL, "Counts cube file", m_outcube.url());
684 
685  // Save counts cube
686  obs->save(m_outcube, clobber());
687 
688  // Stamp counts cube
689  stamp(m_outcube);
690 
691  } // endif: observation was a CTA observation
692 
693  }
694 
695  // Return
696  return;
697 }
698 
699 
700 /***********************************************************************//**
701  * @brief Save counts cube(s) in XML format.
702  *
703  * Save the counts cube(s) into FITS files and write the file path
704  * information into an XML file. The filename of the XML file is specified by
705  * the m_outfile member, the filename(s) of the counts cube(s) are built by
706  * prepending the prefix given by the m_prefix member to the input counts
707  * cube(s) filenames. Any path present in the input filename will be
708  * stripped, i.e. the counts cube(s) will be written in the local working
709  * directory (unless a path is specified in the m_prefix member).
710  ***************************************************************************/
712 {
713  // Issue warning if output filename has no .xml suffix
714  log_string(TERSE, warn_xml_suffix(m_outcube.url()));
715 
716  // Loop over all observation in the container
717  for (int i = 0; i < m_obs.size(); ++i) {
718 
719  // Get CTA observation
720  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
721 
722  // Handle only CTA observations
723  if (obs != NULL) {
724 
725  // Set event output file name
726  std::string outfile = set_outfile_name(m_infiles[i]);
727 
728  // Store output file name in observation
729  obs->eventfile(outfile);
730 
731  // Log counts cube file name
732  log_value(NORMAL, "Counts cube file", outfile);
733 
734  // Save counts cube
735  obs->save(outfile, clobber());
736 
737  // Stamp counts cube
738  stamp(outfile);
739 
740  } // endif: observation was a CTA observations
741 
742  } // endfor: looped over observations
743 
744  // Save observations in XML file
745  m_obs.save(m_outcube);
746 
747  // Return
748  return;
749 }
GCTARoi get_roi(const GCTAPointing &pnt=GCTAPointing())
Return RoI from User parameters.
Definition: ctool.cpp:1325
void apply_mask(GCTAObservation *obs)
Apply mask to event cube.
Definition: ctcubemask.cpp:478
void setup_observations(GObservations &obs, const bool &response=true, const bool &list=true, const bool &cube=true)
Setup observation container.
Definition: ctool.cpp:431
void init_members(void)
Initialise class members.
Definition: ctcubemask.cpp:366
bool m_select_energy
Perform energy selection.
Definition: ctcubemask.hpp:87
GFilename m_outcube
Output event list or XML file.
Definition: ctcubemask.hpp:79
Cube filter tool.
Definition: ctcubemask.hpp:47
std::string warn_xml_suffix(const GFilename &filename) const
Set warning string if file has no .xml suffix.
Definition: ctool.cpp:2427
void init_members(void)
Initialise class members.
Definition: ctool.cpp:321
const GObservations & obs(void) const
Return observation container.
ctcubemask & operator=(const ctcubemask &app)
Assignment operator.
Definition: ctcubemask.cpp:144
void save_xml(void)
Save counts cube(s) in XML format.
Definition: ctcubemask.cpp:711
void publish(const std::string &name="")
Publish counts cube.
Definition: ctcubemask.cpp:324
std::string m_prefix
Prefix for multiple counts maps.
Definition: ctcubemask.hpp:80
void save(void)
Save the masked event cube(s)
Definition: ctcubemask.cpp:288
std::string region_string(const GSkyRegion &region) const
Return region string.
Definition: ctcubemask.cpp:622
void copy_members(const ctcubemask &app)
Copy class members.
Definition: ctcubemask.cpp:389
#define CTCUBEMASK_NAME
Definition: ctcubemask.hpp:39
void get_parameters(void)
Get application parameters.
Definition: ctcubemask.cpp:427
void log_parameters(const GChatter &chatter)
Log application parameters.
Definition: ctool.cpp:1208
Base class for observation tools.
ctcubemask(void)
Void constructor.
Definition: ctcubemask.cpp:53
void free_members(void)
Delete class members.
Cube filter tool definition.
std::string get_obs_header(const GObservation *obs) const
Return observation header string.
Definition: ctool.cpp:2090
void free_members(void)
Delete class members.
Definition: ctool.cpp:357
void init_members(void)
Initialise class members.
virtual ~ctcubemask(void)
Destructor.
Definition: ctcubemask.cpp:122
double m_emin
Lower energy.
Definition: ctcubemask.hpp:81
ctobservation & operator=(const ctobservation &app)
Assignment operator.
const bool & read_ahead(void) const
Signal whether parameters should be read ahead.
Definition: ctool.hpp:177
std::vector< std::string > m_infiles
Input event filenames.
Definition: ctcubemask.hpp:86
bool m_use_xml
Use XML file instead of FITS file for observations.
Definition: ctool.hpp:162
void process(void)
Mask data cube.
Definition: ctcubemask.cpp:208
void clear(void)
Clear ctcubemask tool.
Definition: ctcubemask.cpp:179
std::string set_outfile_name(const std::string &filename) const
Set output file name.
Definition: ctcubemask.cpp:652
void log_observations(const GChatter &chatter, const GObservations &obs, const std::string &what="Observation")
Log observation container.
Definition: ctool.cpp:1251
GObservations m_obs
Observation container.
bool m_publish
Publish counts cube?
Definition: ctcubemask.hpp:83
void save_fits(void)
Save counts cube in FITS format.
Definition: ctcubemask.cpp:671
void free_members(void)
Delete class members.
Definition: ctcubemask.cpp:410
double m_emax
Upper energy.
Definition: ctcubemask.hpp:82