ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
ctbin.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * ctbin - Event binning tool *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2010-2022 by Juergen Knoedlseder *
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 ctbin.cpp
23  * @brief Event binning tool implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <cstdio>
32 #include "ctbin.hpp"
33 #include "GTools.hpp"
34 
35 /* __ OpenMP section _____________________________________________________ */
36 #ifdef _OPENMP
37 #include <omp.h>
38 #endif
39 
40 /* __ Method name definitions ____________________________________________ */
41 #define G_CUBE "ctbin::cube(int&)"
42 #define G_GET_PARAMETERS "ctbin::get_parameters()"
43 #define G_FILL_CUBE "ctbin::fill_cube(GCTAObservation*)"
44 #define G_SET_WEIGHTS "ctbin::set_weights(GCTAObservation*)"
45 
46 /* __ Debug definitions __________________________________________________ */
47 
48 /* __ Coding definitions _________________________________________________ */
49 
50 /* __ Constants __________________________________________________________ */
51 
52 
53 /*==========================================================================
54  = =
55  = Constructors/destructors =
56  = =
57  ==========================================================================*/
58 
59 /***********************************************************************//**
60  * @brief Void constructor
61  *
62  * Constructs an empty event binning tool.
63  ***************************************************************************/
65 {
66  // Initialise members
67  init_members();
68 
69  // Return
70  return;
71 }
72 
73 
74 /***********************************************************************//**
75  * @brief Observations constructor
76  *
77  * param[in] obs Observation container.
78  *
79  * Constructs event binning tool from an observation container.
80  ***************************************************************************/
81 ctbin::ctbin(const GObservations& obs) :
82  ctobservation(CTBIN_NAME, VERSION, obs)
83 {
84  // Initialise members
85  init_members();
86 
87  // Return
88  return;
89 }
90 
91 
92 /***********************************************************************//**
93  * @brief Command line constructor
94  *
95  * @param[in] argc Number of arguments in command line.
96  * @param[in] argv Array of command line arguments.
97  *
98  * Constructs event binning tool using command line arguments for user
99  * parameter setting.
100  ***************************************************************************/
101 ctbin::ctbin(int argc, char *argv[]) :
102  ctobservation(CTBIN_NAME, VERSION, argc, argv)
103 {
104  // Initialise members
105  init_members();
106 
107  // Return
108  return;
109 }
110 
111 
112 /***********************************************************************//**
113  * @brief Copy constructor
114  *
115  * @param[in] app Event binning tool.
116  *
117  * Constructs event binning tool from another event binning tool.
118  ***************************************************************************/
120 {
121  // Initialise members
122  init_members();
123 
124  // Copy members
125  copy_members(app);
126 
127  // Return
128  return;
129 }
130 
131 
132 /***********************************************************************//**
133  * @brief Destructor
134  *
135  * Destructs event binning tool.
136  ***************************************************************************/
138 {
139  // Free members
140  free_members();
141 
142  // Return
143  return;
144 }
145 
146 
147 /*==========================================================================
148  = =
149  = Operators =
150  = =
151  ==========================================================================*/
152 
153 /***********************************************************************//**
154  * @brief Assignment operator
155  *
156  * @param[in] app Event binning tool.
157  * @return Event binning tool.
158  *
159  * Assigns event binning tool.
160  ***************************************************************************/
162 {
163  // Execute only if object is not identical
164  if (this != &app) {
165 
166  // Copy base class members
167  this->ctobservation::operator=(app);
168 
169  // Free members
170  free_members();
171 
172  // Initialise members
173  init_members();
174 
175  // Copy members
176  copy_members(app);
177 
178  } // endif: object was not identical
179 
180  // Return this object
181  return *this;
182 }
183 
184 
185 /*==========================================================================
186  = =
187  = Public methods =
188  = =
189  ==========================================================================*/
190 
191 /***********************************************************************//**
192  * @brief Clear event binning tool
193  *
194  * Clears event binning tool.
195  ***************************************************************************/
196 void ctbin::clear(void)
197 {
198  // Free members
199  free_members();
201  this->ctool::free_members();
202 
203  // Clear base class (needed to conserve tool name and version)
204  this->GApplication::clear();
205 
206  // Initialise members
207  this->ctool::init_members();
209  init_members();
210 
211  // Write header into logger
212  log_header();
213 
214  // Return
215  return;
216 }
217 
218 
219 /***********************************************************************//**
220  * @brief Process the event binning tool
221  *
222  * Gets the user parameters and loops over all CTA observations in the
223  * observation container to bin the events into either a single or a set of
224  * counts cube. All observations in the observation container that do not
225  * contain CTA event lists will be skipped.
226  ***************************************************************************/
227 void ctbin::process(void)
228 {
229  // Get task parameters
230  get_parameters();
231 
232  // Initialise sky direction cache for stacking
234 
235  // Write input observation container into logger
236  log_observations(NORMAL, m_obs, "Input observation");
237 
238  // Write header into logger
239  log_header1(TERSE, gammalib::number("Find unbinned observation",
240  m_obs.size()));
241 
242  // Find all unbinned CTA observations in m_obs
243  std::vector<GCTAObservation*> obs_list(0);
244  double mean_ra = 0.0;
245  double mean_dec = 0.0;
246  for (GCTAObservation* obs = first_unbinned_observation(); obs != NULL;
248 
249  // Push observation into list
250  obs_list.push_back(obs);
251 
252  // Get pointing
253  const GCTAPointing& pnt = obs->pointing();
254 
255  // Add to coordinates
256  mean_ra += pnt.dir().ra_deg();
257  mean_dec += pnt.dir().dec_deg();
258 
259  // Write message
260  std::string msg = " Including unbinned "+obs->instrument()+
261  " observation";
262  log_string(NORMAL, msg);
263 
264  } // endfor: looped over all unbinned observations
265 
266  // Set number of relevant observations
267  int nobs = obs_list.size();
268 
269  // Compute mean pointing
270  if (nobs > 0) {
271  mean_ra /= double(nobs);
272  mean_dec /= double(nobs);
273  m_mean_pnt.radec_deg(mean_ra, mean_dec);
274  }
275 
276  // Initialise arrays
277  if (nobs > 0) {
278 
279  // Determine number of cubes
280  int ncubes = (m_stack) ? 1 : nobs;
281 
282  // Allocate cubes
283  m_counts = std::vector<GSkyMap>(ncubes);
284  m_weights = std::vector<GSkyMap>(ncubes);
285  m_cubes = std::vector<GCTAEventCube>(ncubes);
286 
287  }
288  else {
289  m_counts.clear();
290  m_weights.clear();
291  m_cubes.clear();
292  }
293 
294  // Set mean pointing usage
295  std::string use;
296  if (m_stack && nobs > 0) {
297  use = (*this)["usepnt"].boolean() ? "yes" : "no";
298  }
299  else {
300  use = "no";
301  }
302 
303  // Log observation selection results
304  log_header3(NORMAL, "Summary");
305  log_value(NORMAL, "Number of observations", nobs);
306  log_value(NORMAL, "Mean pointing", m_mean_pnt.print());
307  log_value(NORMAL, "Use mean pointing", use);
308 
309  // Write header into logger
310  log_header1(TERSE, gammalib::number("Bin observation", nobs));
311 
312  // Loop over all unbinned CTA observations in the container
313  #pragma omp parallel for
314  for (int i = 0; i < nobs; ++i) {
315 
316  // Get pointer to observation
317  GCTAObservation* obs = obs_list[i];
318 
319  // Determine counts cube slot
320  int islot = (m_stack) ? 0 : i;
321 
322  // If slot is empty then allocate cubes
323  #pragma omp critical(ctbin_run)
324  if (m_counts[islot].is_empty()) {
325  m_counts[islot] = create_cube(obs);
326  m_weights[islot] = create_cube(obs);
327  }
328 
329  // Fill counts cube
330  fill_cube(obs, m_counts[islot]);
331 
332  // Set the counts cube weights
333  set_weights(obs, m_weights[islot]);
334 
335  // Dispose events to free memory
336  obs->dispose_events();
337 
338  } // endfor: looped over observations
339 
340  // Combine GTIs of all observations and compute total ontime and livetime
341  for (int i = 0; i < nobs; ++i) {
342 
343  // Get pointer to observation
344  GCTAObservation* obs = obs_list[i];
345 
346  // Get Good Time Intervals for observation
347  GGti gti = obs->gti();
348 
349  // If resulting GTI is not filled then copy over the reference from
350  // the observation and use it for the resulting GTI
351  if (m_gti.is_empty()) {
352  m_gti.reference(gti.reference());
353  }
354 
355  // Append GTIs
356  m_gti.extend(gti);
357 
358  // Update ontime and livetime
359  m_ontime += obs->ontime();
360  m_livetime += obs->livetime();
361 
362  } // endfor: looped over all observations
363 
364  // Post process data for a stacked cube
365  if (m_stack && nobs > 0) {
366 
367  // Compute livetime fraction per energy bin so that varying energy
368  // thresholds are correctly taken into account in the weighting
369  // array. Computational speed is not optimum, we have to see later
370  // whether this is an issue.
371  for (int iebin = 0; iebin < m_ebounds.size(); ++iebin) {
372  double livetime_ebin = 0.0;
373  for (int i = 0; i < obs_list.size(); ++i) {
374  std::vector<bool> usage =
375  cube_layer_usage(m_ebounds, obs_list[i]->ebounds());
376  if (usage[iebin]) {
377  livetime_ebin += obs_list[i]->livetime();
378  }
379  }
380  if (m_livetime > 0.0) {
381  double fraction = livetime_ebin / m_livetime;
382  for (int pixel = 0; pixel < m_counts[0].npix(); ++pixel) {
383  if (m_weights[0](pixel, iebin) > 0.0) {
384  m_weights[0](pixel, iebin) *= fraction;
385  }
386  }
387  }
388  }
389 
390  // Set a single cube in the observation container
392 
393  } // endif: stacked cube post processing
394 
395  // Post process data for non-stacked cubes
396  else {
397 
398  // Loop over all relevant observations
399  for (int i = 0; i < nobs; ++i) {
400 
401  // Get pointer to observation
402  GCTAObservation* obs = obs_list[i];
403 
404  // Build counts cube
405  m_cubes[i] = GCTAEventCube(m_counts[i], m_weights[i], m_ebounds,
406  obs->gti());
407 
408  // Attach event cube in the correct slot of the observation
409  // container
410  obs->events(m_cubes[i]);
411 
412  // Construct unique filename for possible saving of the counts
413  // cube into FITS files. Each observation has a unique pair of
414  // instrument() and id() attributes, hence we construct a lower
415  // case file name from these attributes, making sure that all
416  // whitespaces are converted into "_" characters. Note that the
417  // id() attribute is allowed to be blank, which can only happen
418  // for a single observation.
419  std::string inst = gammalib::tolower(obs->instrument());
420  std::string id = gammalib::tolower(obs->id());
421  inst = gammalib::replace_segment(inst, " ", "_");
422  id = gammalib::replace_segment(id, " ", "_");
423  std::string filename = m_prefix + inst;
424  if (!id.empty()) {
425  filename += "_" + id;
426  }
427  filename += ".fits";
428 
429  // Set filename of observation
430  obs->eventfile(filename);
431 
432  } // endfor: looped over relevant observations
433 
434  } // endelse: non-stacked cube post processing
435 
436  // Write resulting observation container into logger
437  log_observations(NORMAL, m_obs, "Binned observation");
438 
439  // Optionally publish counts cube
440  if (m_publish) {
441  publish();
442  }
443 
444  // Return
445  return;
446 }
447 
448 
449 /***********************************************************************//**
450  * @brief Return event cube at index
451  *
452  * @param[in] index Cube index (0,...,cubes()-1).
453  * @return Reference to event cube.
454  *
455  * @exception GException::out_of_range
456  * Event cube index out of range.
457  *
458  * Returns a reference to the event cube at the given index.
459  ***************************************************************************/
460 const GCTAEventCube& ctbin::cube(const int& index) const
461 {
462  // Compile option: raise an exception if index is out of range
463  if (index < 0 || index >= cubes()) {
464  throw GException::out_of_range(G_CUBE, "Cube index", index, cubes());
465  }
466 
467  // Return cube
468  return m_cubes[index];
469 }
470 
471 
472 /***********************************************************************//**
473  * @brief Save counts cube
474  *
475  * Saves the counts cube. If the counts cube was stacked single FITS file
476  * will be written. Otherwise each generated counts cube will be written
477  * into a FITS file, and an observation definition XML file will be written
478  * out that contains an observation for each counts cube.
479  ***************************************************************************/
480 void ctbin::save(void)
481 {
482  // Write header
483  log_header1(TERSE, "Save counts cube");
484 
485  // If counts cube was stacked then save first observation
486  if (m_stack) {
487 
488  // Save only if filename is valid and if there is at least one
489  // observation
490  if ((*this)["outobs"].is_valid() && m_obs.size() > 0) {
491 
492  // Get counts cube filename
493  GFilename outobs = (*this)["outobs"].filename();
494 
495  // Get CTA observation from observation container
496  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
497 
498  // Save only if observation is valid
499  if (obs != NULL) {
500 
501  // Log counts cube file name
502  log_value(NORMAL, "Counts cube file", outobs.url());
503 
504  // Save cube
505  obs->save(outobs, clobber());
506 
507  // Stamp counts cube
508  stamp(outobs);
509 
510  } // endif: observation was valid
511 
512  } // endif: outobs file was valid
513 
514  } // endif: counts cube was stacked
515 
516  // ... otherwise write out all relevant counts cubes and the observation
517  // definition XML file
518  else {
519 
520  // Loop over all observations
521  for (int i = 0; i < m_obs.size(); ++i) {
522 
523  // Get CTA observation from observation container
524  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
525 
526  // Continue only if observation is valid
527  if (obs != NULL) {
528 
529  // Retrieve filename
530  std::string filename = obs->eventfile();
531 
532  // Save counts cube
533  obs->save(filename, clobber());
534 
535  // Stamp counts cube
536  stamp(filename);
537 
538  } // endif: observation was valid
539 
540  } // endfor: looped over all observations
541 
542  // Write XML file
543  if ((*this)["outobs"].is_valid()) {
544 
545  // Get observation definition XML filename
546  std::string outobs = (*this)["outobs"].filename();
547 
548  // Issue warning if output filename has no .xml suffix
549  log_string(TERSE, warn_xml_suffix(outobs));
550 
551  // Save XML file
552  m_obs.save(outobs);
553  }
554 
555  } // endelse: counts cube were not stacked
556 
557  // Return
558  return;
559 }
560 
561 
562 /***********************************************************************//**
563  * @brief Publish counts cube
564  *
565  * @param[in] name Counts cube name.
566  ***************************************************************************/
567 void ctbin::publish(const std::string& name)
568 {
569  // Write header into logger
570  log_header1(TERSE, "Publish counts cube");
571 
572  // Set default name is user name is empty
573  std::string user_name(name);
574  if (user_name.empty()) {
575  user_name = CTBIN_NAME;
576  }
577 
578  // Write counts cube name into logger
579  log_value(NORMAL, "Counts cube name", user_name);
580 
581  // Publish first counts cube if it exists
582  if (cubes() > 0) {
583  m_counts[0].publish(user_name);
584  }
585 
586  // Return
587  return;
588 }
589 
590 
591 /*==========================================================================
592  = =
593  = Private methods =
594  = =
595  ==========================================================================*/
596 
597 /***********************************************************************//**
598  * @brief Initialise class members
599  ***************************************************************************/
601 {
602  // Initialise members
603  //m_usepnt = false;
604  m_stack = true;
605  m_prefix.clear();
606  m_publish = false;
607  m_chatter = static_cast<GChatter>(2);
608 
609  // Initialise protected members
610  m_cubes.clear();
611  m_counts.clear();
612  m_weights.clear();
613  m_mean_pnt.clear();
614  m_ebounds.clear();
615  m_gti.clear();
616  m_ontime = 0.0;
617  m_livetime = 0.0;
618 
619  // Initialise cache members
620  m_dirs.clear();
621 
622  // Return
623  return;
624 }
625 
626 
627 /***********************************************************************//**
628  * @brief Copy class members
629  *
630  * @param[in] app Application.
631  ***************************************************************************/
633 {
634  // Copy attributes
635  //m_usepnt = app.m_usepnt;
636  m_stack = app.m_stack;
637  m_prefix = app.m_prefix;
638  m_publish = app.m_publish;
639  m_chatter = app.m_chatter;
640 
641  // Copy protected members
642  m_cubes = app.m_cubes;
643  m_counts = app.m_counts;
644  m_weights = app.m_weights;
645  m_mean_pnt = app.m_mean_pnt;
646  m_ebounds = app.m_ebounds;
647  m_gti = app.m_gti;
648  m_ontime = app.m_ontime;
649  m_livetime = app.m_livetime;
650 
651  // Copy cache members
652  m_dirs = app.m_dirs;
653 
654  // Return
655  return;
656 }
657 
658 
659 /***********************************************************************//**
660  * @brief Delete class members
661  ***************************************************************************/
663 {
664  // Return
665  return;
666 }
667 
668 
669 /***********************************************************************//**
670  * @brief Get application parameters
671  *
672  * Get all task parameters from parameter file or (if required) by querying
673  * the user. Most parameters are only required if no observation exists so
674  * far in the observation container. In this case, a single CTA observation
675  * will be added to the container, using the definition provided in the
676  * parameter file.
677  ***************************************************************************/
679 {
680  // Setup observations from "inobs" parameter. Do not request response
681  // information and do not accept counts cubes.
682  setup_observations(m_obs, false, true, false);
683 
684  // Create an event cube to query task parameters
685  GCTAEventCube cube = this->ctool::create_cube(m_obs);
686 
687  // Get energy boundaries
688  m_ebounds = cube.ebounds();
689 
690  // Get stack and prefix parameters
691  m_stack = (*this)["stack"].boolean();
692  m_prefix = (*this)["prefix"].string();
693 
694  // Get remaining parameters
695  m_publish = (*this)["publish"].boolean();
696  m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
697 
698  // If needed later, query output filename now
699  if (read_ahead()) {
700  (*this)["outobs"].query();
701  }
702 
703  // Set number of OpenMP threads
704  #ifdef _OPENMP
705  int nthreads = (*this)["nthreads"].integer();
706  if (nthreads > 0) {
707  omp_set_num_threads(nthreads);
708  }
709  #endif
710 
711  // Write parameters into logger
712  log_parameters(TERSE);
713 
714  // Return
715  return;
716 }
717 
718 
719 /***********************************************************************//**
720  * @brief Initialise sky direction cache for cube stack
721  *
722  * Initialises a cache that holds the sky directions of each counts cube
723  * pixel for a stacked analysis. This will speed up the computations later.
724  *
725  * If no stacking is requested, the method does nothing.
726  ***************************************************************************/
728 {
729  // Continue only is stacking is requested
730  if (m_stack) {
731 
732  // Create a sky map from task parameters
733  GSkyMap map = this->ctool::create_map(m_obs);
734 
735  // Initialise sky direction
736  m_dirs.resize(map.npix());
737 
738  // Compute sky direction for each pixel
739  for (int pixel = 0; pixel < map.npix(); ++pixel) {
740  m_dirs[pixel] = map.inx2dir(pixel);
741  }
742 
743  } // endif: stacking was requested
744 
745  // Return
746  return;
747 }
748 
749 
750 /***********************************************************************//**
751  * @brief Create counts cube sky map for current observation
752  *
753  * @param[in] obs CTA observation.
754  *
755  * Creates a counts cube sky map for current observations.
756  ***************************************************************************/
757 GSkyMap ctbin::create_cube(const GCTAObservation* obs)
758 {
759  // Read coordinate system and projection
760  std::string coordsys = (*this)["coordsys"].string();
761  std::string proj = (*this)["proj"].string();
762 
763  // Initialise sky map reference
764  double xref = 0.0;
765  double yref = 0.0;
766 
767  // If pointing direction should be used then extract sky map reference
768  // from observation(s)
769  if ((*this)["usepnt"].boolean()) {
770 
771  // If stacking is requested then use the mean pointing direction
772  if (m_stack) {
773  if (gammalib::toupper(coordsys) == "GAL") {
774  xref = m_mean_pnt.l_deg();
775  yref = m_mean_pnt.b_deg();
776  }
777  else {
778  xref = m_mean_pnt.ra_deg();
779  yref = m_mean_pnt.dec_deg();
780  }
781  }
782 
783  // ... otherwise use the pointing direction of the observation
784  else {
785 
786  // Get pointing
787  const GCTAPointing& pnt = obs->pointing();
788 
789  // Extract coordinates
790  if (gammalib::toupper(coordsys) == "GAL") {
791  xref = pnt.dir().l_deg();
792  yref = pnt.dir().b_deg();
793  }
794  else {
795  xref = pnt.dir().ra_deg();
796  yref = pnt.dir().dec_deg();
797  }
798 
799  } // endelse: use pointing direction of observation
800 
801  } // endif: use pointing direction
802 
803  // ... otherwise use User defined sky map reference
804  else {
805  xref = (*this)["xref"].real();
806  yref = (*this)["yref"].real();
807  }
808 
809  // Read sky map pixel size and number of bins
810  double binsz = (*this)["binsz"].real();
811  int nxpix = (*this)["nxpix"].integer();
812  int nypix = (*this)["nypix"].integer();
813 
814  // Read energy boundaries
815  GEbounds ebounds = create_ebounds();
816 
817  // Initialise counts cube
818  GSkyMap cube = GSkyMap(proj, coordsys, xref, yref, -binsz, binsz,
819  nxpix, nypix, ebounds.size());
820 
821  // Return cube
822  return cube;
823 }
824 
825 
826 /***********************************************************************//**
827  * @brief Fill events into counts cube
828  *
829  * @param[in] obs CTA observation.
830  * @param[in,out] counts Counts cube
831  *
832  * @exception GException::invalid_value
833  * No event list or valid RoI found in observation.
834  *
835  * Fills the events from an event list into the counts cube.
836  ***************************************************************************/
837 void ctbin::fill_cube(const GCTAObservation* obs, GSkyMap& counts)
838 {
839  // Make sure that the observation holds a CTA event list. If this is
840  // not the case then throw an exception.
841  const GCTAEventList* events = dynamic_cast<const GCTAEventList*>
842  (obs->events());
843  if (events == NULL) {
844  std::string msg = "CTA Observation does not contain an event "
845  "list. An event list is needed to fill the "
846  "counts cube.";
847  throw GException::invalid_value(G_FILL_CUBE, msg);
848  }
849 
850  // Get the RoI
851  const GCTARoi& roi = obs->roi();
852 
853  // Check for RoI sanity
854  if (!roi.is_valid()) {
855  std::string msg = "No valid RoI found in input observation "
856  "\""+obs->name()+"\". Run ctselect to specify "
857  "an RoI for this observation before running "
858  "ctbin.";
859  throw GException::invalid_value(G_FILL_CUBE, msg);
860  }
861 
862  // Get counts cube usage flags
863  std::vector<bool> usage = cube_layer_usage(m_ebounds, obs->ebounds());
864 
865  // Initialise binning statistics
866  int num_outside_roi = 0;
867  int num_invalid_wcs = 0;
868  int num_outside_map = 0;
869  int num_outside_ebds = 0;
870  int num_in_map = 0;
871 
872  // Extract counts cube boundaries
873  double pixel_x_max = counts.nx() - 0.5;
874  double pixel_y_max = counts.ny() - 0.5;
875 
876  // Fill counts sky map
877  for (int i = 0; i < events->size(); ++i) {
878 
879  // Get event
880  const GCTAEventAtom* event = (*events)[i];
881 
882  // Determine event sky direction
883  GCTAInstDir* inst = (GCTAInstDir*)&(event->dir());
884  GSkyDir dir = inst->dir();
885 
886  // Skip event if it is outside the RoI
887  if (roi.centre().dir().dist_deg(dir) > roi.radius()) {
888  num_outside_roi++;
889  continue;
890  }
891 
892  // Determine sky pixel
893  GSkyPixel pixel;
894  try {
895  pixel = counts.dir2pix(dir);
896  }
897  catch (std::exception &e) {
898  num_invalid_wcs++;
899  continue;
900  }
901 
902  // Skip event if corresponding counts cube pixel is outside the
903  // counts cube map range
904  if (pixel.x() < -0.5 || pixel.x() > pixel_x_max ||
905  pixel.y() < -0.5 || pixel.y() > pixel_y_max) {
906  num_outside_map++;
907  continue;
908  }
909 
910  // Determine counts cube energy bin
911  int iebin = m_ebounds.index(event->energy());
912 
913  // Skip event if the corresponding counts cube energy bin is not
914  // fully contained in the event list energy range. This avoids
915  // having partially filled bins.
916  if (iebin == -1 || !usage[iebin]) {
917  num_outside_ebds++;
918  continue;
919  }
920 
921  // Fill event in skymap
922  #pragma omp atomic
923  counts(pixel, iebin) += 1.0;
924 
925  // Increment number of maps
926  num_in_map++;
927 
928  } // endfor: looped over all events
929 
930  // Log filling results
931  #pragma omp critical(ctbin_fill_cube)
932  {
933  log_header3(TERSE, get_obs_header(obs));
934  log_value(NORMAL, "Events in list", obs->events()->size());
935  log_value(NORMAL, "Events in cube", num_in_map);
936  log_value(NORMAL, "Events outside RoI", num_outside_roi);
937  log_value(NORMAL, "Events with invalid WCS", num_invalid_wcs);
938  log_value(NORMAL, "Events outside cube area", num_outside_map);
939  log_value(NORMAL, "Events outside energy bins", num_outside_ebds);
940  }
941 
942  // Return
943  return;
944 }
945 
946 
947 /***********************************************************************//**
948  * @brief Set counts cube weights for a given observation
949  *
950  * @param[in] obs CTA observation.
951  * @param[in,out] weights Counts cube weights.
952  *
953  * @exception GException::invalid_value
954  * No event list or valid RoI found in observation.
955  *
956  * Sets the counts cube weights for all bins that are considered for the
957  * specific observation to unity.
958  ***************************************************************************/
959 void ctbin::set_weights(const GCTAObservation* obs, GSkyMap& weights)
960 {
961  // Get the RoI
962  const GCTARoi& roi = obs->roi();
963 
964  // Check for RoI sanity
965  if (!roi.is_valid()) {
966  std::string msg = "No valid RoI found in input observation "
967  "\""+obs->name()+"\". Run ctselect to specify "
968  "an RoI for this observation before running "
969  "ctbin.";
970  throw GException::invalid_value(G_SET_WEIGHTS, msg);
971  }
972 
973  // Get the RoI centre and radius in radians
974  GSkyDir roi_centre = roi.centre().dir();
975  double roi_radius = roi.radius() * gammalib::deg2rad;
976 
977  // Get counts cube usage flags
978  std::vector<bool> usage = cube_layer_usage(m_ebounds, obs->ebounds());
979 
980  // Set sky direction cache availability flag
981  bool cache = (m_stack && m_dirs.size() == weights.npix());
982 
983  // Loop over all pixels in counts cube
984  for (int pixel = 0; pixel < weights.npix(); ++pixel) {
985 
986  // Skip pixel if it is outside the RoI
987  if (cache) {
988  if (m_dirs[pixel].dist(roi_centre) > roi_radius) {
989  continue;
990  }
991  }
992  else {
993  if (roi_centre.dist(weights.inx2dir(pixel)) > roi_radius) {
994  continue;
995  }
996  }
997 
998  // Loop over all energy layers of counts cube
999  for (int iebin = 0; iebin < m_ebounds.size(); ++iebin){
1000 
1001  // Skip energy layer if the usage flag is false
1002  if (!usage[iebin]) {
1003  continue;
1004  }
1005 
1006  // Signal that bin was filled
1007  weights(pixel, iebin) = 1.0;
1008 
1009  } // endfor: looped over energy layers of counts cube
1010 
1011  } // endfor: looped over pixels of counts cube
1012 
1013  // Return
1014  return;
1015 }
1016 
1017 
1018 /***********************************************************************//**
1019  * @brief Create output observation container.
1020  *
1021  * Creates an output observation container that combines all input CTA
1022  * observation into a single stacked observation. All non-CTA observations
1023  * and all binned CTA observations that were present in the observation
1024  * container are append to the observation container so that they can
1025  * be used by other tools. The method furthermore conserves any response
1026  * information in case that a single CTA observation is provided to support
1027  * binned analysis.
1028  ***************************************************************************/
1030 {
1031  // Set event cube in first slot
1032  m_cubes[0] = GCTAEventCube(m_counts[0], m_weights[0], m_ebounds, m_gti);
1033 
1034  // If we have only a single CTA observation in the container, then
1035  // keep that observation and just attach the event cube to it. Reset
1036  // the filename, otherwise we still will have the old event filename
1037  // in the log file.
1038  if (m_obs.size() == 1) {
1039 
1040  // Attach event cube to CTA observation
1041  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
1042  if (obs != NULL) {
1043 
1044  // Change the event type if we had an unbinned observation
1045  if (obs->eventtype() == "EventList") {
1046 
1047  // Assign cube to the observation
1048  obs->events(m_cubes[0]);
1049 
1050  }
1051 
1052  // ... otherwise the input observation was binned and hence
1053  // skipped. In that case we simply append an empty counts
1054  // cube
1055  else {
1056 
1057  // Create empty counts cube
1058  GCTAEventCube cube(m_counts[0], m_weights[0], m_ebounds,
1059  obs->gti());
1060 
1061  // Assign empty cube to observation
1062  obs->events(cube);
1063 
1064  }
1065 
1066  // Reset file name
1067  obs->eventfile("");
1068 
1069  } // endif: obervation was valid
1070 
1071  } // endif: we only had one observation in the container
1072 
1073  // ... otherwise put a single CTA observation in container and
1074  // append all observations that have not been used in the binning
1075  // to the container
1076  else {
1077 
1078  // Allocate observation container
1079  GObservations container;
1080 
1081  // Allocate CTA observation.
1082  GCTAObservation obs;
1083 
1084  // Attach event cube to CTA observation
1085  obs.events(m_cubes[0]);
1086 
1087  // Compute average pointing direction for all CTA event lists and
1088  // extract first instrument
1089  double ra = 0.0;
1090  double dec = 0.0;
1091  double number = 0.0;
1092  std::string instrument = "";
1093  for (int i = 0; i < m_obs.size(); ++i) {
1094  GCTAObservation* cta = dynamic_cast<GCTAObservation*>(m_obs[i]);
1095  if ((cta != NULL) && (cta->eventtype() == "EventList")) {
1096  ra += cta->pointing().dir().ra();
1097  dec += cta->pointing().dir().dec();
1098  number += 1.0;
1099  if (instrument.empty()) {
1100  instrument = cta->instrument();
1101  }
1102  }
1103  }
1104  if (number > 0.0) {
1105  ra /= number;
1106  dec /= number;
1107  }
1108  if (instrument.empty()) {
1109  instrument = "cta";
1110  }
1111  GSkyDir dir;
1112  dir.radec(ra, dec);
1113  GCTAPointing pointing(dir);
1114 
1115  // Compute deadtime correction
1116  double deadc = (m_ontime > 0.0) ? m_livetime / m_ontime : 0.0;
1117 
1118  // Set CTA observation attributes
1119  obs.instrument(instrument);
1120  obs.pointing(pointing);
1121  obs.ra_obj(dir.ra_deg()); //!< Dummy
1122  obs.dec_obj(dir.dec_deg()); //!< Dummy
1123  obs.ontime(m_ontime);
1124  obs.livetime(m_livetime);
1125  obs.deadc(deadc);
1126 
1127  // Set models in observation container
1128  container.models(m_obs.models());
1129 
1130  // Append CTA observation
1131  container.append(obs);
1132 
1133  // Copy over all remaining non-CTA observations
1134  for (int i = 0; i < m_obs.size(); ++i) {
1135  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
1136  if (obs == NULL) {
1137  container.append(*m_obs[i]);
1138  }
1139  else if (obs->eventtype() != "EventList") {
1140  container.append(*m_obs[i]);
1141  }
1142  }
1143 
1144  // Set observation container
1145  m_obs = container;
1146 
1147  } // endelse: there was not a single CTA observation
1148 
1149  // Return
1150  return;
1151 }
void init_members(void)
Initialise class members.
Definition: ctbin.cpp:600
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 clear(void)
Clear event binning tool.
Definition: ctbin.cpp:196
std::vector< bool > cube_layer_usage(const GEbounds &cube_ebounds, const GEbounds &list_ebounds) const
Determine the counts cube layer usage.
Definition: ctool.cpp:2194
GEbounds m_ebounds
Energy boundaries.
Definition: ctbin.hpp:112
std::string warn_xml_suffix(const GFilename &filename) const
Set warning string if file has no .xml suffix.
Definition: ctool.cpp:2427
GSkyMap create_map(const GObservations &obs)
Create a skymap from user parameters.
Definition: ctool.cpp:937
virtual ~ctbin(void)
Destructor.
Definition: ctbin.cpp:137
void init_members(void)
Initialise class members.
Definition: ctool.cpp:321
const GObservations & obs(void) const
Return observation container.
GCTAObservation * next_unbinned_observation(void)
Return next unbinned CTA observation.
std::vector< GSkyDir > m_dirs
Cached GSkyDir for all pixels.
Definition: ctbin.hpp:118
void free_members(void)
Delete class members.
Definition: ctbin.cpp:662
Event binning tool.
Definition: ctbin.hpp:69
ctbin(void)
Void constructor.
Definition: ctbin.cpp:64
bool m_stack
Output one stacked cube or multiple cubes.
Definition: ctbin.hpp:102
void set_weights(const GCTAObservation *obs, GSkyMap &weights)
Set counts cube weights for a given observation.
Definition: ctbin.cpp:959
std::vector< GCTAEventCube > m_cubes
Event cubes.
Definition: ctbin.hpp:108
void log_parameters(const GChatter &chatter)
Log application parameters.
Definition: ctool.cpp:1208
GEbounds create_ebounds(void)
Create energy boundaries from user parameters.
Definition: ctool.cpp:612
Base class for observation tools.
void copy_members(const ctbin &app)
Copy class members.
Definition: ctbin.cpp:632
ctbin & operator=(const ctbin &app)
Assignment operator.
Definition: ctbin.cpp:161
void free_members(void)
Delete class members.
std::string get_obs_header(const GObservation *obs) const
Return observation header string.
Definition: ctool.cpp:2090
#define G_SET_WEIGHTS
Definition: ctbin.cpp:44
void free_members(void)
Delete class members.
Definition: ctool.cpp:357
void init_members(void)
Initialise class members.
GCTAObservation * first_unbinned_observation(void)
Return first unbinned CTA observation.
GCTAEventCube create_cube(const GObservations &obs)
Create a CTA event cube from user parameters.
Definition: ctool.cpp:1006
GSkyDir m_mean_pnt
Mean pointing.
Definition: ctbin.hpp:111
const GCTAEventCube & cube(const int &index=0) const
Return event cube at index.
Definition: ctbin.cpp:460
void save(void)
Save counts cube.
Definition: ctbin.cpp:480
std::vector< GSkyMap > m_weights
List of event cube weights.
Definition: ctbin.hpp:110
ctobservation & operator=(const ctobservation &app)
Assignment operator.
GGti m_gti
Stacked Good time intervals.
Definition: ctbin.hpp:113
const bool & read_ahead(void) const
Signal whether parameters should be read ahead.
Definition: ctool.hpp:177
#define CTBIN_NAME
Definition: ctbin.hpp:34
void init_sky_dir_cache(void)
Initialise sky direction cache for cube stack.
Definition: ctbin.cpp:727
bool m_publish
Publish counts cube?
Definition: ctbin.hpp:104
int cubes(void) const
Return number of event cubes.
Definition: ctbin.hpp:130
void get_parameters(void)
Get application parameters.
Definition: ctbin.cpp:678
void fill_cube(const GCTAObservation *obs, GSkyMap &counts)
Fill events into counts cube.
Definition: ctbin.cpp:837
Event binning tool definition.
double m_ontime
Total ontime.
Definition: ctbin.hpp:114
void publish(const std::string &name="")
Publish counts cube.
Definition: ctbin.cpp:567
void log_observations(const GChatter &chatter, const GObservations &obs, const std::string &what="Observation")
Log observation container.
Definition: ctool.cpp:1251
void obs_cube_stacked(void)
Create output observation container.
Definition: ctbin.cpp:1029
GObservations m_obs
Observation container.
#define G_FILL_CUBE
Definition: ctbin.cpp:43
std::vector< GSkyMap > m_counts
List of event cube counts.
Definition: ctbin.hpp:109
void process(void)
Process the event binning tool.
Definition: ctbin.cpp:227
double m_livetime
Total livetime.
Definition: ctbin.hpp:115
GChatter m_chatter
Chattiness.
Definition: ctbin.hpp:105
#define G_CUBE
Definition: ctbin.cpp:41
GSkyMap create_cube(const GCTAObservation *obs)
Create counts cube sky map for current observation.
Definition: ctbin.cpp:757
std::string m_prefix
Prefix for multiple cubes.
Definition: ctbin.hpp:103