ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
ctmodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * ctmodel - Model cube generation tool *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2012-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 ctmodel.cpp
23  * @brief Model cube generation 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 "ctmodel.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_GET_PARAMETERS "ctmodel::get_parameters()"
42 #define G_GET_OBS "ctmodel::get_obs()"
43 #define G_FILL_CUBE "ctmodel::fill_cube(GCTAObservation*)"
44 
45 /* __ Debug definitions __________________________________________________ */
46 
47 /* __ Coding definitions _________________________________________________ */
48 
49 /* __ Constants __________________________________________________________ */
50 const GEnergy g_energy_margin(1.0e-12, "TeV");
51 
52 
53 /*==========================================================================
54  = =
55  = Constructors/destructors =
56  = =
57  ==========================================================================*/
58 
59 /***********************************************************************//**
60  * @brief Void constructor
61  ***************************************************************************/
63 {
64  // Initialise members
65  init_members();
66 
67  // Return
68  return;
69 }
70 
71 
72 /***********************************************************************//**
73  * @brief Observations constructor
74  *
75  * param[in] obs Observation container.
76  *
77  * This method creates an instance of the class by copying an existing
78  * observations container.
79  ***************************************************************************/
80 ctmodel::ctmodel(const GObservations& obs) :
81  ctobservation(CTMODEL_NAME, VERSION, obs)
82 {
83  // Initialise members
84  init_members();
85 
86  // Return
87  return;
88 }
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 ctmodel::ctmodel(int argc, char *argv[]) :
99  ctobservation(CTMODEL_NAME, VERSION, argc, argv)
100 {
101  // Initialise members
102  init_members();
103 
104  // Return
105  return;
106 }
107 
108 
109 /***********************************************************************//**
110  * @brief Copy constructor
111  *
112  * @param[in] app Application.
113  ***************************************************************************/
115 {
116  // Initialise members
117  init_members();
118 
119  // Copy members
120  copy_members(app);
121 
122  // Return
123  return;
124 }
125 
126 
127 /***********************************************************************//**
128  * @brief Destructor
129  ***************************************************************************/
131 {
132  // Free members
133  free_members();
134 
135  // Return
136  return;
137 }
138 
139 
140 /*==========================================================================
141  = =
142  = Operators =
143  = =
144  ==========================================================================*/
145 
146 /***********************************************************************//**
147  * @brief Assignment operator
148  *
149  * @param[in] app Application.
150  * @return Application.
151  ***************************************************************************/
153 {
154  // Execute only if object is not identical
155  if (this != &app) {
156 
157  // Copy base class members
158  this->ctobservation::operator=(app);
159 
160  // Free members
161  free_members();
162 
163  // Initialise members
164  init_members();
165 
166  // Copy members
167  copy_members(app);
168 
169  } // endif: object was not identical
170 
171  // Return this object
172  return *this;
173 }
174 
175 
176 /*==========================================================================
177  = =
178  = Public methods =
179  = =
180  ==========================================================================*/
181 
182 /***********************************************************************//**
183  * @brief Clear ctmodel tool
184  *
185  * Clears ctmodel tool.
186  ***************************************************************************/
187 void ctmodel::clear(void)
188 {
189  // Free members
190  free_members();
192  this->ctool::free_members();
193 
194  // Clear base class (needed to conserve tool name and version)
195  this->GApplication::clear();
196 
197  // Initialise members
198  this->ctool::init_members();
200  init_members();
201 
202  // Write header into logger
203  log_header();
204 
205  // Return
206  return;
207 }
208 
209 
210 /***********************************************************************//**
211  * @brief Generate the model map(s)
212  *
213  * This method reads the task parameters from the parfile, sets up the
214  * observation container, loops over all CTA observations in the container
215  * and generates a model map for each CTA observation.
216  ***************************************************************************/
218 {
219  // Get task parameters
220  get_parameters();
221 
222  // Extract cube properties
224 
225  // Set energy dispersion flags of all CTA observations and save old
226  // values in save_edisp vector
227  std::vector<bool> save_edisp = set_edisp(m_obs, m_apply_edisp);
228 
229  // Write input observation container into logger
230  log_observations(NORMAL, m_obs, "Input observation");
231 
232  // Write input model container into logger
233  log_models(NORMAL, m_obs.models(), "Input model");
234 
235  // Write header
236  log_header1(TERSE, "Generate model cube");
237 
238  // Loop over all observations in the container
239  #pragma omp parallel
240  {
241  // Create a models object for this core
242  GModels models(m_obs.models());
243 
244  #pragma omp for schedule(static,1)
245  for (int i = 0; i < m_obs.size(); ++i) {
246 
247  // Get CTA observation
248  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
249 
250  // Skip observation if it's not CTA
251  if (obs == NULL) {
252  log_header3(TERSE, get_obs_header(m_obs[i]));
253  std::string msg = " Skipping "+m_obs[i]->instrument()+
254  " observation";
255  log_string(NORMAL, msg);
256  }
257 
258  // Fill cube and leave loop if we are binned mode (meaning we
259  // only have one binned observation)
260  else if (m_binned) {
261  fill_cube(obs, models);
262  i = m_obs.size();
263  }
264 
265  // Skip observation if we have a binned observation
266  else if (obs->eventtype() == "CountsCube") {
267  log_header3(TERSE, get_obs_header(m_obs[i]));
268  std::string msg = " Skipping binned "+m_obs[i]->instrument()+
269  " observation";
270  log_string(NORMAL, msg);
271  }
272 
273  // Otherwise, everything seems to be fine, so fill the cube from obs
274  else {
275  // Fill the cube
276  fill_cube(obs, models);
277 
278  // Dispose events to free memory if event file exists on disk
279  if (obs->eventfile().length() > 0 && obs->eventfile().exists()) {
280  obs->dispose_events();
281  }
282  }
283 
284  } // endfor: looped over observations
285 
286  } // end openmp parallel section
287 
288  // Write model cube into header
289  log_header1(NORMAL, "Model cube");
290  log_string(NORMAL, m_cube.print());
291 
292  // Restore energy dispersion flags of all CTA observations
293  restore_edisp(m_obs, save_edisp);
294 
295  // Optionally publish model cube
296  if (m_publish) {
297  publish();
298  }
299 
300  // Return
301  return;
302 }
303 
304 
305 /***********************************************************************//**
306  * @brief Save model cube
307  *
308  * Saves the model cube into a FITS file specified using the "outfile"
309  * task parameter.
310  ***************************************************************************/
311 void ctmodel::save(void)
312 {
313  // Write header
314  log_header1(TERSE, "Save model cube");
315 
316  // Save only if filename is valid and cube has some size
317  if ((*this)["outcube"].is_valid() && m_cube.size() > 0) {
318 
319  // Get model cube filename
320  m_outcube = (*this)["outcube"].filename();
321 
322  // Save cube
323  m_cube.save(m_outcube, clobber());
324 
325  // Write mandatory keywords
326  GFits fits(m_outcube);
327  for (int i = 0; i < fits.size(); ++i) {
328  fits[i]->card("RA_PNT", m_ra_pnt, "[deg] Pointing Right Ascension");
329  fits[i]->card("DEC_PNT", m_dec_pnt, "[deg] Pointing Declination");
330  }
331  fits.save(true);
332 
333  // Stamp model cube
334  stamp(m_outcube);
335 
336  }
337 
338  // Write into logger what has been done
339  std::string fname = (m_outcube.is_empty()) ? "NONE" : m_outcube.url();
340  if (m_cube.size() == 0) {
341  fname.append(" (cube is empty, no file created)");
342  }
343  log_value(NORMAL, "Model cube file", fname);
344 
345  // Return
346  return;
347 }
348 
349 
350 /***********************************************************************//**
351  * @brief Publish model cube
352  *
353  * @param[in] name Model cube name.
354  ***************************************************************************/
355 void ctmodel::publish(const std::string& name)
356 {
357  // Write header into logger
358  log_header1(TERSE, "Publish model cube");
359 
360  // Set default name is user name is empty
361  std::string user_name(name);
362  if (user_name.empty()) {
363  user_name = CTMODEL_NAME;
364  }
365 
366  // Log filename
367  log_value(NORMAL, "Model cube name", user_name);
368 
369  // Publish model cube
370  m_cube.counts().publish(user_name);
371 
372  // Return
373  return;
374 }
375 
376 
377 /***********************************************************************//**
378  * @brief Set model cube
379  *
380  * @param[in] cube Model cube.
381  *
382  * Set model cube and set all cube bins to zero.
383  ***************************************************************************/
384 void ctmodel::cube(const GCTAEventCube& cube)
385 {
386  // Set cube
387  m_cube = cube;
388 
389  // Set all cube bins to zero
390  for (int i = 0; i < m_cube.size(); ++i) {
391  m_cube[i]->counts(0.0);
392  }
393 
394  // Signal that cube has been set
395  m_has_cube = true;
396 
397  // Return
398  return;
399 }
400 
401 
402 /*==========================================================================
403  = =
404  = Private methods =
405  = =
406  ==========================================================================*/
407 
408 /***********************************************************************//**
409  * @brief Initialise class members
410  ***************************************************************************/
412 {
413  // Initialise members
414  m_outcube.clear();
415  m_apply_edisp = false;
416  m_publish = false;
417  m_chatter = static_cast<GChatter>(2);
418 
419  // Initialise protected members
420  m_cube.clear();
421  m_gti.clear();
422  m_has_cube = false;
423  m_append_cube = false;
424  m_binned = false;
425  m_dir.clear();
426  m_solidangle.clear();
427  m_energy.clear();
428  m_ewidth.clear();
429  m_time.clear();
430  m_ra_pnt = 0.0;
431  m_dec_pnt = 0.0;
432 
433  // Return
434  return;
435 }
436 
437 
438 /***********************************************************************//**
439  * @brief Copy class members
440  *
441  * @param[in] app Application.
442  ***************************************************************************/
444 {
445  // Copy attributes
446  m_outcube = app.m_outcube;
448  m_publish = app.m_publish;
449  m_chatter = app.m_chatter;
450 
451  // Copy protected members
452  m_cube = app.m_cube;
453  m_gti = app.m_gti;
454  m_has_cube = app.m_has_cube;
456  m_binned = app.m_binned;
457  m_dir = app.m_dir;
459  m_energy = app.m_energy;
460  m_ewidth = app.m_ewidth;
461  m_time = app.m_time;
462  m_ra_pnt = app.m_ra_pnt;
463  m_dec_pnt = app.m_dec_pnt;
464 
465  // Return
466  return;
467 }
468 
469 
470 /***********************************************************************//**
471  * @brief Delete class members
472  ***************************************************************************/
474 {
475  // Return
476  return;
477 }
478 
479 
480 /***********************************************************************//**
481  * @brief Get application parameters
482  *
483  * Get all task parameters from parameter file or (if required) by querying
484  * the user. The parameters are read in the correct order.
485  ***************************************************************************/
487 {
488  // Reset cube append flag
489  m_append_cube = false;
490 
491  // If there are no observations in container then load them via user
492  // parameters.
493  if (m_obs.size() == 0) {
494  get_obs();
495  }
496 
497  // ... otherwise add response information and energy boundaries in case
498  // that they are missing
499  else {
501  }
502 
503  // If we have now excactly one CTA observation (but no cube has yet been
504  // appended to the observation) then check whether this observation
505  // is a binned observation, and if yes, extract the counts cube for
506  // model generation
507  if ((m_obs.size() == 1) && (m_append_cube == false)) {
508 
509  // Get CTA observation
510  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
511 
512  // Continue only if observation is a CTA observation
513  if (obs != NULL) {
514 
515  // Check for binned observation
516  if (obs->eventtype() == "CountsCube") {
517 
518  // Set cube from binned observation
519  GCTAEventCube* evtcube = dynamic_cast<GCTAEventCube*>
520  (const_cast<GEvents*>(obs->events()));
521  cube(*evtcube);
522 
523  // Signal that cube has been set
524  m_has_cube = true;
525 
526  // Signal that we are in binned mode
527  m_binned = true;
528 
529  } // endif: observation was binned
530 
531  } // endif: observation was CTA
532 
533  } // endif: had exactly one observation
534 
535  // Read model definition file if required
536  if (m_obs.models().size() == 0) {
537 
538  // Get model filename
539  std::string inmodel = (*this)["inmodel"].filename();
540 
541  // Load models from file
542  m_obs.models(inmodel);
543 
544  } // endif: there were no models
545 
546  // Get energy dispersion flag parameters
547  m_apply_edisp = (*this)["edisp"].boolean();
548 
549  // If we do not have yet a counts cube for model computation then check
550  // whether we should read it from the "incube" parameter or whether we
551  // should create it from scratch using the task parameters
552  if (!m_has_cube) {
553 
554  // If the cube filename is valid the load the cube and set all cube
555  // bins to zero
556  if ((*this)["incube"].is_valid()) {
557 
558  // Get cube definition filename
559  std::string incube = (*this)["incube"].filename();
560 
561  // Load cube from given file
562  m_cube.load(incube);
563 
564  // Set all cube bins to zero
565  for (int i = 0; i < m_cube.size(); ++i) {
566  m_cube[i]->counts(0.0);
567  }
568 
569  } // endif: cube filename was valid
570 
571  // ... otherwise create a cube from the user parameters
572  else {
574  }
575 
576  // Signal that cube has been set
577  m_has_cube = true;
578 
579  } // endif: we had no cube yet
580 
581  // Get remaining parameters
582  m_publish = (*this)["publish"].boolean();
583  m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
584 
585  // If needed later, query output filename now
586  if (read_ahead()) {
587  (*this)["outcube"].query();
588  }
589 
590  // If cube should be appended to first observation then do that now.
591  // This is a kluge that makes sure that the cube is passed as part
592  // of the observation in case that a cube response is used. The kluge
593  // is needed because the GCTACubeSourceDiffuse::set method needs to
594  // get the full event cube from the observation. It is also at this
595  // step that the GTI, which may just be a dummy GTI when create_cube()
596  // has been used, will be set.
597  if (m_append_cube) {
598 
599  //TODO: Check that energy boundaries are compatible
600 
601  // Attach GTI of observations to model cube
602  m_cube.gti(m_obs[0]->events()->gti());
603 
604  // Attach model cube to observations
605  m_obs[0]->events(m_cube);
606 
607  } // endif: cube was scheduled for appending
608 
609  // Set number of OpenMP threads
610  #ifdef _OPENMP
611  int nthreads = (*this)["nthreads"].integer();
612  if (nthreads > 0) {
613  omp_set_num_threads(nthreads);
614  }
615  #endif
616 
617  // Write parameters into logger
618  log_parameters(TERSE);
619 
620  // Return
621  return;
622 }
623 
624 
625 /***********************************************************************//**
626  * @brief Get observation container
627  *
628  * Get an observation container according to the user parameters. The method
629  * supports loading of a individual FITS file or an observation definition
630  * file in XML format.
631  *
632  * If the input filename is empty, the method checks for the existence of the
633  * "expcube", "psfcube" and "bkgcube" parameters. If file names have been
634  * specified, the method loads the files and creates a dummy events cube that
635  * is appended to the observation container.
636  *
637  * If no file names are specified for the "expcube", "psfcube" or "bkgcube"
638  * parameters, the method reads the necessary parameters to build a CTA
639  * observation from scratch.
640  *
641  * The method sets m_append_cube = true and m_binned = true in case that
642  * a stacked observation is requested (as detected by the presence of the
643  * "expcube", "psfcube", and "bkgcube" parameters). In that case, it appended
644  * a dummy event cube to the observation.
645  *
646  * @todo Support stacked energy dispersion
647  ***************************************************************************/
649 {
650  // If no observation definition file has been specified then read all
651  // parameters that are necessary to create an observation from scratch
652  if ((*this)["inobs"].is_undefined()) {
653 
654  // If the filenames are valid then build an observation from cube
655  // response information
656  if ((*this)["expcube"].is_valid() &&
657  (*this)["psfcube"].is_valid() &&
658  (*this)["bkgcube"].is_valid()) {
659 
660  // Get response cube filenames
661  GFilename edispcube;
662  bool query_edisp = (*this)["edisp"].boolean();
663  GFilename expcube = (*this)["expcube"].filename();
664  GFilename psfcube = (*this)["psfcube"].filename();
665  if (query_edisp && (*this)["edispcube"].is_valid()) {
666  edispcube = (*this)["edispcube"].filename();
667  }
668  GFilename bkgcube = (*this)["bkgcube"].filename();
669 
670  // Get exposure, PSF and background cubes
671  GCTACubeExposure exposure(expcube);
672  GCTACubePsf psf(psfcube);
673  GCTACubeBackground background(bkgcube);
674 
675  // Create energy boundaries
676  GEbounds ebounds = create_ebounds();
677 
678  // Create dummy sky map cube
679  GSkyMap map("CAR","GAL",0.0,0.0,1.0,1.0,1,1,ebounds.size());
680 
681  // Create event cube
682  GCTAEventCube cube(map, ebounds, exposure.gti());
683 
684  // Create CTA observation
685  GCTAObservation cta;
686  cta.events(cube);
687 
688  // Set name of Cherenkov telescope
689  cta.instrument((*this)["instrument"].string());
690 
691  // If querying of energy dispersion cube is requested then
692  // query it now
693  if (query_edisp) {
694 
695  // If filename is valid then use energy dispersion ...
696  if ((*this)["edispcube"].is_valid()) {
697 
698  // Load energy dispersion cube
699  GCTACubeEdisp edisp(edispcube);
700 
701  // Set response with all four cubes
702  cta.response(exposure, psf, edisp, background);
703 
704  } // endif: energy dispersion cube was provided
705 
706  // ... otherwise throw an exception
707  else {
708  std::string msg = "Energy dispersion requested but no "
709  "energy dispersion cube was specified.";
710  throw GException::invalid_value(G_GET_OBS, msg);
711  }
712 
713  } // endif: energy dispersion needed
714 
715  // ... otherwise work without energy dispersion
716  else {
717  cta.response(exposure, psf, background);
718  }
719 
720  // Append observation to container
721  m_obs.append(cta);
722 
723  // Signal that we are in binned mode
724  m_binned = true;
725 
726  // Signal that we appended a cube
727  m_append_cube = true;
728 
729  } // endif: cube response information was available
730 
731  // ... otherwise build an observation from IRF response information
732  else {
733 
734  // Create CTA observation
735  GCTAObservation cta = create_cta_obs();
736 
737  // Set response
738  set_obs_response(&cta);
739 
740  // Append observation to container
741  m_obs.append(cta);
742 
743  }
744 
745  } // endif: filename was not valid
746 
747  // ... otherwise we have a file name
748  else {
749 
750  // Get the filename from the input parameters
751  std::string filename = (*this)["inobs"].filename();
752 
753  // If file is a FITS file then create an empty CTA observation
754  // and load file into observation
755  if (GFilename(filename).is_fits()) {
756 
757  // Allocate empty CTA observation
758  GCTAObservation cta;
759 
760  // Load data
761  cta.load(filename);
762 
763  // Set response
764  set_obs_response(&cta);
765 
766  // Append observation to container
767  m_obs.append(cta);
768 
769  // Signal that no XML file should be used for storage
770  m_use_xml = false;
771 
772  }
773 
774  // ... otherwise load file into observation container
775  else {
776 
777  // Load observations from XML file
778  m_obs.load(filename);
779 
780  // For all observations that have no response, set the response
781  // from the task parameters
783 
784  // Set observation boundary parameters (emin, emax, rad)
785  set_obs_bounds();
786 
787  // Signal that XML file should be used for storage
788  m_use_xml = true;
789 
790  } // endelse: file was an XML file
791 
792  }
793 
794  // Return
795  return;
796 
797 }
798 
799 
800 /***********************************************************************//**
801  * @brief Extract cube properties in data members
802  ***************************************************************************/
804 {
805  // Clear cube properties
806  m_dir.clear();
807  m_solidangle.clear();
808  m_energy.clear();
809  m_ewidth.clear();
810 
811  // Get number of spatial pixels and energy layers in counts cube
812  int npix = m_cube.npix();
813  int nebins = m_cube.ebins();
814 
815  // Reserve capacity in vectors
816  m_dir.reserve(npix);
817  m_solidangle.reserve(npix);
818  m_energy.reserve(nebins);
819  m_ewidth.reserve(nebins);
820 
821  // Loop over all the spatial bins and extract spatial cube properties
822  for (int i = 0; i < npix; ++i) {
823 
824  // Get cube bin
825  GCTAEventBin* bin = m_cube[i];
826 
827  // Extract sky direction and solid angle
828  m_dir.push_back(bin->dir());
829  m_solidangle.push_back(bin->solidangle());
830 
831  } // endfor: looped over spatial bins
832 
833  // Loop over all the spectral bins and extract spectral cube properties
834  for (int iebin = 0, ibin = 0; iebin < nebins; ++iebin, ibin += npix) {
835 
836  // Get cube bin
837  GCTAEventBin* bin = m_cube[ibin];
838 
839  // Extract energy and energy width
840  m_energy.push_back(bin->energy());
841  m_ewidth.push_back(bin->ewidth());
842 
843  } // endfor: looped over spectral bins
844 
845  // Set time
846  m_time = m_cube[0]->time();
847 
848  // Return
849  return;
850 }
851 
852 
853 /***********************************************************************//**
854  * @brief Fill models into model cube
855  *
856  * @param[in] obs CTA observation.
857  * @param[in] models Model container.
858  *
859  * Adds the expected number of events for a given observation to the events
860  * that are already found in the model cube. The method also updates the
861  * GTI of the model cube so that cube GTI is a list of the GTIs of all
862  * observations that were used to generate the model cube.
863  ***************************************************************************/
864 void ctmodel::fill_cube(const GCTAObservation* obs, GModels& models)
865 {
866  // Get number of spatial and spectral bins in counts cube
867  int npix = m_cube.npix();
868  int nebins = m_cube.ebins();
869 
870  // Get reference to the pointing for DETX and DETY computation, if
871  // required
872  const GCTAPointing& pnt = obs->pointing();
873 
874  // Store Right Ascension and Declination for output model
875  m_ra_pnt = pnt.dir().ra_deg();
876  m_dec_pnt = pnt.dir().dec_deg();
877 
878  // Get references to GTI and energy boundaries for the event list
879  const GGti& gti = obs->events()->gti();
880  const GEbounds& obs_ebounds = obs->ebounds();
881 
882  // Get cube energy boundaries
883  const GEbounds& cube_ebounds = m_cube.ebounds();
884 
885  // Get counts cube usage flags
886  std::vector<bool> usage = cube_layer_usage(cube_ebounds, obs_ebounds);
887 
888  // Initialise empty, invalid RoI
889  GCTARoi roi;
890 
891  // Retrieve RoI in case we have an unbinned observation
892  if (obs->eventtype() == "EventList") {
893  roi = obs->roi();
894  }
895 
896  // Initialise statistics
897  double sum = 0.0;
898  int num_outside_ebds = 0;
899  int num_outside_roi = 0;
900 
901  // Extract copy of models from observation container
902  GModels ex_models = trim_models(models, roi);
903 
904  // Get pointer to event cube pixels
905  double* pixels = const_cast<double*>(m_cube.counts().pixels());
906 
907  // Initialise event bin
908  GCTAEventBin bin;
909 
910  // Set event bin attributes that are constant for a counts cube
911  bin.time(m_time);
912  bin.ontime(obs->ontime());
913  bin.weight(1.0);
914 
915  // Flag if the CTA observation is a counts cube
916  bool obs_is_cube = (obs->eventtype() == "CountsCube");
917 
918  // Loop over all the spatial bins
919  for (int i = 0; i < npix; ++i) {
920 
921  // If RoI is valid then skip bin if it is outside the RoI of the
922  // observation
923  if (roi.is_valid() && !roi.contains(m_dir[i])) {
924  num_outside_roi += nebins;
925  continue;
926  }
927 
928  // Get instrument direction for spatial pixel and recompute DETX and
929  // DETY based on the pointing of the observation.
930  GCTAInstDir dir = pnt.instdir(m_dir[i].dir());
931 
932  // Set instrument direction, solid angle and pixel index of bin
933  bin.dir(dir);
934  bin.solidangle(m_solidangle[i]);
935  bin.ipix(i);
936 
937  // Loop over all of the energy bins of the cube
938  for (int iebin = 0, ibin = i; iebin < nebins; ++iebin, ibin += npix) {
939 
940  // Skip bin if the corresponding counts cube energy bin is not fully
941  // contained in the event list energy range. This avoids having
942  // partially filled bins.
943  if (!usage[iebin]) {
944  num_outside_ebds++;
945  continue;
946  }
947 
948  // Set energy, energy width and energy index
949  bin.energy(m_energy[iebin]);
950  bin.ewidth(m_ewidth[iebin]);
951  bin.ieng(iebin);
952 
953  // If observation is a counts cube then set the proper weight for
954  // this bin
955  if (obs_is_cube) {
956  bin.weight(m_cube[ibin]->weight());
957  }
958 
959  // Compute model value for cube bin
960  double model = models.eval(bin, *obs) * bin.size();
961 
962  // Sum model
963  sum += model;
964 
965  // Store value
966  #pragma omp critical(ctmodel_fill_cube)
967  pixels[ibin] += model;
968 
969  } // endfor: looped over all spatial bins
970 
971  } // endfor: looped over all cube bins
972 
973  // Re-append the models to the main model
974  models.extend(ex_models);
975 
976  // Update GTIs
977  #pragma omp critical(ctmodel_fill_cube)
978  {
979  // Append GTIs of observation to list of GTIs
980  m_gti.extend(gti);
981 
982  // Update GTIs
983  m_cube.gti(m_gti);
984  }
985 
986  // Log filling results
987  #pragma omp critical(ctmodel_fill_cube)
988  {
989  log_header3(TERSE, get_obs_header(obs));
990  log_value(NORMAL, "Model events in cube", sum);
991  log_value(NORMAL, "Bins outside energy range", num_outside_ebds);
992  log_value(NORMAL, "Bins outside RoI", num_outside_roi);
993  }
994 
995  // Write model cube into header
996  if (m_chatter >= EXPLICIT) {
997  log_header2(EXPLICIT, "Model cube");
998  log_string(EXPLICIT, m_cube.print(m_chatter));
999  }
1000 
1001  // Return
1002  return;
1003 }
1004 
1005 
1006 /***********************************************************************//**
1007  * @brief Find the models falling inside a defined region of interest.
1008  *
1009  * @param[in,out] all_models Model container to be trimmed.
1010  * @param[in] roi Observation region of interest.
1011  * @return New model container containing only models inside RoI.
1012  *
1013  * Note that a buffer is added to the observation region to ensure that
1014  * point sources (which have radius=0) are appropriately included if they
1015  * fall near the edge of the observation.
1016  ***************************************************************************/
1017 GModels ctmodel::trim_models(GModels& all_models, const GCTARoi& roi)
1018 {
1019  // Create the model container containing only models in RoI
1020  GModels excluded_models;
1021 
1022  // Do nothing if roi is not valid. This will be the case for binned data
1023  // sets as they have the default roi radius of 0. In this case we would
1024  // remove ALL models, which is obviously incorrect. So we keep all models
1025  // when filling based on a counts cube.
1026  if (!roi.is_valid()) {
1027  return excluded_models;
1028  }
1029 
1030  // Remove all models that dont overlap with the region of interest. Note
1031  // that an extra factor is used since point sources have regions of
1032  // radius 0, which is a problem when they fall just barely outside the
1033  // RoI. This ensures point sources are appropriately included.
1034  GSkyRegionCircle obsreg(roi.centre().dir(), roi.radius()+0.5);
1035 
1036  // Loop over the models in the passed container
1037  for (int i = 0; i < all_models.size(); ++i) {
1038 
1039  // Cast the model to a GModelSky object
1040  GModelSky* model = dynamic_cast<GModelSky*>(all_models[i]);
1041 
1042  // If model is not a GModelSky model than skip
1043  if (model == NULL) {
1044  continue;
1045  }
1046 
1047  // Otherwise, exclude the model if it isn't a diffuse cube and it
1048  // doesn't overlap with the observation.
1049  if (!(model->spatial()->code() == GMODEL_SPATIAL_DIFFUSE) &&
1050  (!model->spatial()->region()->overlaps(obsreg))) {
1051 
1052  // Append model to the excluded models
1053  excluded_models.append(*model);
1054 
1055  // Remove model
1056  all_models.remove(i);
1057 
1058  // Decrement to prevent skipping models
1059  i--;
1060  }
1061 
1062  } // endfor: looped over all models
1063 
1064  // Return excluded models
1065  return excluded_models;
1066 }
void log_models(const GChatter &chatter, const GModels &models, const std::string &what="Model")
Log model container.
Definition: ctool.cpp:1285
void set_response(GObservations &obs)
Set response for all CTA observations in container.
Definition: ctool.cpp:1614
GCTAObservation create_cta_obs(void)
Create a CTA observation from User parameters.
Definition: ctool.cpp:1056
void setup_observations(GObservations &obs, const bool &response=true, const bool &list=true, const bool &cube=true)
Setup observation container.
Definition: ctool.cpp:431
bool m_append_cube
Signal to append cube.
Definition: ctmodel.hpp:97
void process(void)
Generate the model map(s)
Definition: ctmodel.cpp:217
std::vector< GCTAInstDir > m_dir
Cube directions.
Definition: ctmodel.hpp:99
bool m_apply_edisp
Apply energy dispersion?
Definition: ctmodel.hpp:89
void clear(void)
Clear ctmodel tool.
Definition: ctmodel.cpp:187
bool m_has_cube
Signal if cube exists.
Definition: ctmodel.hpp:96
#define CTMODEL_NAME
Definition: ctmodel.hpp:36
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
ctmodel(void)
Void constructor.
Definition: ctmodel.cpp:62
GFilename m_outcube
Output model cube.
Definition: ctmodel.hpp:88
void free_members(void)
Delete class members.
Definition: ctmodel.cpp:473
void init_members(void)
Initialise class members.
Definition: ctool.cpp:321
const GObservations & obs(void) const
Return observation container.
std::vector< double > m_solidangle
Cube solid angles.
Definition: ctmodel.hpp:100
#define G_GET_OBS
Definition: ctmodel.cpp:42
void models(const GModels &models)
Set models.
Definition: ctmodel.hpp:141
Model cube generation tool.
Definition: ctmodel.hpp:53
GCTAEventCube m_cube
Model cube.
Definition: ctmodel.hpp:94
double m_dec_pnt
Declination Ascension of pointing.
Definition: ctmodel.hpp:105
void get_obs(void)
Get observation container.
Definition: ctmodel.cpp:648
bool m_publish
Publish model cube?
Definition: ctmodel.hpp:90
void publish(const std::string &name="")
Publish model cube.
Definition: ctmodel.cpp:355
double m_ra_pnt
Right Ascension of pointing.
Definition: ctmodel.hpp:104
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
void init_members(void)
Initialise class members.
Definition: ctmodel.cpp:411
void copy_members(const ctmodel &app)
Copy class members.
Definition: ctmodel.cpp:443
Base class for observation tools.
GTime m_time
Cube time.
Definition: ctmodel.hpp:103
GChatter m_chatter
Chattiness.
Definition: ctmodel.hpp:91
void save(void)
Save model cube.
Definition: ctmodel.cpp:311
std::vector< GEnergy > m_energy
Cube energies.
Definition: ctmodel.hpp:101
void free_members(void)
Delete class members.
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 set_obs_bounds()
Set observation boundaries for CTA observations.
void extract_cube_properties(void)
Extract cube properties in data members.
Definition: ctmodel.cpp:803
void init_members(void)
Initialise class members.
GModels trim_models(GModels &all_models, const GCTARoi &roi)
Find the models falling inside a defined region of interest.
Definition: ctmodel.cpp:1017
GCTAEventCube create_cube(const GObservations &obs)
Create a CTA event cube from user parameters.
Definition: ctool.cpp:1006
const GCTAEventCube & cube(void) const
Return model cube.
Definition: ctmodel.hpp:115
ctobservation & operator=(const ctobservation &app)
Assignment operator.
const bool & read_ahead(void) const
Signal whether parameters should be read ahead.
Definition: ctool.hpp:177
void restore_edisp(GObservations &obs, const std::vector< bool > &edisp) const
Restore energy dispersion flags of CTA observations.
Definition: ctool.cpp:1689
std::vector< bool > set_edisp(GObservations &obs, const bool &edisp) const
Set energy dispersion to CTA observations.
Definition: ctool.cpp:1649
virtual ~ctmodel(void)
Destructor.
Definition: ctmodel.cpp:130
bool m_binned
Signal binned mode.
Definition: ctmodel.hpp:98
bool m_use_xml
Use XML file instead of FITS file for observations.
Definition: ctool.hpp:162
void fill_cube(const GCTAObservation *obs, GModels &models)
Fill models into model cube.
Definition: ctmodel.cpp:864
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.
ctmodel & operator=(const ctmodel &app)
Assignment operator.
Definition: ctmodel.cpp:152
void set_obs_response(GCTAObservation *obs)
Set response for CTA observation.
Definition: ctool.cpp:1752
void get_parameters(void)
Get application parameters.
Definition: ctmodel.cpp:486
GGti m_gti
Model cube GTIs.
Definition: ctmodel.hpp:95
const GEnergy g_energy_margin(1.0e-12,"TeV")
Model cube generation tool definition.
std::vector< GEnergy > m_ewidth
Cube energy widths.
Definition: ctmodel.hpp:102