ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
ctobssim.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * ctobssim - Observation simulator tool *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2011-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 ctobssim.cpp
23  * @brief Observation simulator 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 <typeinfo>
33 #include "ctobssim.hpp"
34 #include "GTools.hpp"
35 #include "GFits.hpp"
36 
37 /* __ OpenMP section _____________________________________________________ */
38 #ifdef _OPENMP
39 #include <omp.h>
40 #endif
41 
42 /* __ Method name definitions ____________________________________________ */
43 #define G_GET_PARAMETERS "ctobssim::get_parameters()"
44 #define G_SIMULATE_SOURCE "ctobssim::simulate_source(GCTAObservation*, "\
45  "GModels&, GRan&, GLog*)"
46 #define G_SIMULATE_INTERVAL "ctobssim::simulate_interval(GCTAObservation*"\
47  ", GCTAEventList*, GCTAResponseIrf*, GModels&, GTime&"\
48  ", GTime&, GEnergy&, GEnergy&, GEnergy&, GEnergy&"\
49  ", GSkyDir&, double&, double&, GRan&, GLog*, int&)"
50 #define G_GET_AREA "ctobssim::get_area(GCTAObservation* obs, GEnergy&, "\
51  "GEnergy&)"
52 
53 /* __ Constants __________________________________________________________ */
54 const double g_roi_margin = 0.5; //!< Simulation radius margin (degrees)
55 
56 /* __ Debug definitions __________________________________________________ */
57 //#define G_SOURCE_DEBUG
58 //#define G_BACKGROUND_DEBUG
59 
60 /* __ Coding definitions _________________________________________________ */
61 
62 
63 /*==========================================================================
64  = =
65  = Constructors/destructors =
66  = =
67  ==========================================================================*/
68 
69 /***********************************************************************//**
70  * @brief Void constructor
71  *
72  * Constructs an empty ctobssim tool.
73  ***************************************************************************/
75 {
76  // Initialise members
77  init_members();
78 
79  // Return
80  return;
81 }
82 
83 
84 /***********************************************************************//**
85  * @brief Observations constructor
86  *
87  * @param[in] obs Observation container.
88  *
89  * Constructs ctobssim tool from an observation container.
90  ***************************************************************************/
91 ctobssim::ctobssim(const GObservations& obs) :
92  ctobservation(CTOBSSIM_NAME, VERSION, obs)
93 {
94  // Initialise members
95  init_members();
96 
97  // Return
98  return;
99 }
100 
101 
102 /***********************************************************************//**
103  * @brief Command line constructor
104  *
105  * @param[in] argc Number of arguments in command line.
106  * @param[in] argv Array of command line arguments.
107  *
108  * Constructs ctobssim tool using command line arguments for user parameter
109  * setting.
110  ***************************************************************************/
111 ctobssim::ctobssim(int argc, char *argv[]) :
112  ctobservation(CTOBSSIM_NAME, VERSION, argc, argv)
113 {
114  // Initialise members
115  init_members();
116 
117  // Return
118  return;
119 }
120 
121 
122 /***********************************************************************//**
123  * @brief Copy constructor
124  *
125  * @param[in] app Application.
126  *
127  * Constructs ctobssim tool from another ctobssim instance.
128  ***************************************************************************/
130 {
131  // Initialise members
132  init_members();
133 
134  // Copy members
135  copy_members(app);
136 
137  // Return
138  return;
139 }
140 
141 
142 /***********************************************************************//**
143  * @brief Destructor
144  *
145  * Destructs ctobssim tool.
146  ***************************************************************************/
148 {
149  // Free members
150  free_members();
151 
152  // Return
153  return;
154 }
155 
156 
157 /*==========================================================================
158  = =
159  = Operators =
160  = =
161  ==========================================================================*/
162 
163 /***********************************************************************//**
164  * @brief Assignment operator
165  *
166  * @param[in] app ctobssim tool.
167  * @return ctobssim tool.
168  *
169  * Assigns ctobssim tool.
170  ***************************************************************************/
172 {
173  // Execute only if object is not identical
174  if (this != &app) {
175 
176  // Copy base class members
177  this->ctobservation::operator=(app);
178 
179  // Free members
180  free_members();
181 
182  // Initialise members
183  init_members();
184 
185  // Copy members
186  copy_members(app);
187 
188  } // endif: object was not identical
189 
190  // Return this object
191  return *this;
192 }
193 
194 
195 /*==========================================================================
196  = =
197  = Public methods =
198  = =
199  ==========================================================================*/
200 
201 /***********************************************************************//**
202  * @brief Clear ctobssim tool
203  *
204  * Clears ctobssim tool.
205  ***************************************************************************/
206 void ctobssim::clear(void)
207 {
208  // Free members
209  free_members();
211  this->ctool::free_members();
212 
213  // Clear base class (needed to conserve tool name and version)
214  this->GApplication::clear();
215 
216  // Initialise members
217  this->ctool::init_members();
219  init_members();
220 
221  // Write header into logger
222  log_header();
223 
224  // Return
225  return;
226 }
227 
228 
229 /***********************************************************************//**
230  * @brief Process the ctobssim tool.
231  *
232  * Gets the user parameters, loops over all CTA observations in the
233  * observation container, and simulate events for each observation.
234  ***************************************************************************/
236 {
237  // Get parameters
238  get_parameters();
239 
240  // Special mode: if read ahead is specified we know that we called
241  // the execute() method, hence files are saved immediately and event
242  // lists are disposed afterwards.
243  if (read_ahead()) {
244  m_save_and_dispose = true;
245  }
246 
247  // Set energy dispersion flags of all CTA observations and save old
248  // values in save_edisp vector
249  std::vector<bool> save_edisp = set_edisp(m_obs, m_apply_edisp);
250 
251  // Determine the number of valid CTA observations
252  int n_observations = 0;
253  for (int i = 0; i < m_obs.size(); ++i) {
254  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
255  if (obs != NULL) {
256  n_observations++;
257  }
258  }
259 
260  // If more than a single observation has been handled then make sure that
261  // an XML file will be used for storage
262  if (n_observations > 1) {
263  m_use_xml = true;
264  }
265 
266  // Write execution model into logger
267  log_header1(NORMAL, "Execution mode");
268  std::string mode = (m_save_and_dispose)
269  ? "Save and dispose (reduces memory needs)"
270  : "Keep events in memory";
271  std::string xml = (m_use_xml)
272  ? "Write Observation Definition XML file"
273  : "Write single event list FITS file";
274  log_value(NORMAL, "Event list management", mode);
275  log_value(NORMAL, "Output format", xml);
276 
277  // Write seed values into logger
278  log_header1(NORMAL, "Seed values");
279  for (int i = 0; i < m_rans.size(); ++i) {
280  log_value(NORMAL, "Seed "+gammalib::str(i),
281  gammalib::str(m_rans[i].seed()));
282  }
283 
284  // Write input observation container into logger
285  log_observations(NORMAL, m_obs, "Input observation");
286 
287  // Write header
288  log_header1(TERSE, gammalib::number("Simulate observation", m_obs.size()));
289 
290  // From here on the code can be parallelized if OpenMP support
291  // is enabled. The code in the following block corresponds to the
292  // code that will be executed in each thread
293  #pragma omp parallel
294  {
295  // Each thread will have it's own logger to avoid conflicts
296  GLog wrklog;
297  if (logDebug()) {
298  wrklog.cout(true);
299  }
300 
301  // Allocate and initialize copies for multi-threading
302  GModels models(m_obs.models());
303 
304  // Copy configuration from application logger to thread logger
305  wrklog.date(log.date());
306  wrklog.name(log.name());
307 
308  // Set a big value to avoid flushing
309  wrklog.buffer_size(10000000);
310 
311  // Loop over all observation in the container. If OpenMP support
312  // is enabled, this loop will be parallelized.
313  #pragma omp for
314  for (int i = 0; i < m_obs.size(); ++i) {
315 
316  // Write header for observation
317  if (logTerse()) {
318  wrklog.header3(get_obs_header(m_obs[i]));
319  }
320 
321  // Get pointer on CTA observation
322  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
323 
324  // Skip observation if it's not CTA
325  if (obs == NULL) {
326  if (logTerse()) {
327  wrklog << " Skipping ";
328  wrklog << m_obs[i]->instrument();
329  wrklog << " observation" << std::endl;
330  }
331  continue;
332  }
333 
334  // Skip observation if we have a binned observation
335  if (obs->eventtype() == "CountsCube") {
336  if (logTerse()) {
337  wrklog << " Skipping binned ";
338  wrklog << obs->instrument();
339  wrklog << " observation" << std::endl;
340  }
341  continue;
342  }
343 
344  // If observation identifier is not yet set then set it now
345  if (obs->id().empty()) {
346  char buffer[256];
347  std::sprintf(buffer, "%6.6d", i + m_startindex);
348  obs->id(std::string(buffer));
349  }
350 
351  // Remove now all events from the event list but keep the
352  // event list information such as ROI, Good Time Intervals,
353  // energy boundaries. This will also keep additional columns
354  // in an event list file.
355  GCTAEventList* events = static_cast<GCTAEventList*>(obs->events());
356  events->remove(0, events->size());
357 
358  // Work on a clone of the CTA observation. This makes sure that
359  // any memory allocated for computing (for example a response
360  // cache) is properly de-allocated on exit of this run
361  GCTAObservation obs_clone = *obs;
362 
363  // Save number of events before entering simulation
364  int events_before = obs_clone.events()->size();
365 
366  // Simulate source events
367  simulate_source(&obs_clone, models, m_rans[i], &wrklog);
368 
369  // Simulate background events
370  simulate_background(&obs_clone, models, m_rans[i], &wrklog);
371 
372  // Set Monte Carlo identifier and name correspondance
373  set_mc_id_names(&obs_clone, models, &wrklog);
374 
375  // Dump simulation results
376  if (logTerse()) {
377  wrklog << gammalib::parformat("MC events");
378  wrklog << obs_clone.events()->size() - events_before;
379  wrklog << " (all models)";
380  wrklog << std::endl;
381  }
382 
383  // Append the event list to the original observation
384  obs->events(*(obs_clone.events()));
385 
386  // If requested, event lists are saved immediately
387  if (m_save_and_dispose) {
388 
389  // Set event output file name
390  std::string outfile = this->outfile(i);
391 
392  // Store output file name in original observation
393  obs->eventfile(outfile);
394 
395  // Save observation into FITS file. This is a critical zone
396  // to avoid multiple threads writing simultaneously
397  #pragma omp critical(ctobssim_run)
398  {
399  //obs_clone.save(outfile, clobber());
400  obs->save(outfile, clobber());
401  }
402 
403  // Dispose events
404  obs->dispose_events();
405 
406  } // endif: save and dispose requested
407 
408  } // endfor: looped over observations
409 
410  // At the end, the content of the thread logger is added to
411  // the application logger
412  #pragma omp critical (log)
413  {
414  log << wrklog;
415  }
416 
417  } // end pragma omp parallel
418 
419  // Restore energy dispersion flags of all CTA observations
420  restore_edisp(m_obs, save_edisp);
421 
422  // Optionally publish event list(s)
423  if ((*this)["publish"].boolean()) {
424  publish();
425  }
426 
427  // Return
428  return;
429 }
430 
431 
432 /***********************************************************************//**
433  * @brief Save the selected event list(s)
434  *
435  * This method saves the selected event list(s) into FITS file(s). There are
436  * two modes, depending on the m_use_xml flag.
437  *
438  * If m_use_xml is true, all selected event list(s) will be saved into FITS
439  * files, where the output filenames are constructued from the input
440  * filenames by prepending the m_prefix string to name. Any path information
441  * will be stripped form the input name, hence event files will be written
442  * into the local working directory (unless some path information is present
443  * in the prefix). In addition, an XML file will be created that gathers
444  * the filename information for the selected event list(s). If an XML file
445  * was present on input, all metadata information will be copied from this
446  * input file.
447  *
448  * If m_use_xml is false, the selected event list will be saved into a FITS
449  * file.
450  ***************************************************************************/
451 void ctobssim::save(void)
452 {
453  // Write header into logger
454  log_header1(TERSE, gammalib::number("Save observation", m_obs.size()));
455 
456  // Case A: Save event file(s) and XML metadata information
457  if (m_use_xml) {
458  save_xml();
459  }
460 
461  // Case B: Save event file as FITS file
462  else {
463  save_fits();
464  }
465 
466  // Return
467  return;
468 }
469 
470 
471 /***********************************************************************//**
472  * @brief Publish event lists
473  *
474  * @param[in] name Event list name.
475  ***************************************************************************/
476 void ctobssim::publish(const std::string& name)
477 {
478  // Write header into logger
479  log_header1(TERSE, gammalib::number("Publish event list", m_obs.size()));
480 
481  // Loop over all observation in the container
482  for (int i = 0; i < m_obs.size(); ++i) {
483 
484  // Get CTA observation
485  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
486 
487  // Handle only CTA observations
488  if (obs != NULL) {
489 
490  // Continue only if there is an event list
491  if (obs->events()->size() != 0) {
492 
493  // Set default name if user name is empty
494  std::string user_name(name);
495  if (user_name.empty()) {
496  user_name = CTOBSSIM_NAME;
497  }
498 
499  // If there are several event lists then add an index
500  if (m_use_xml) {
501  user_name += gammalib::str(i);
502  }
503 
504  // Write event list name into logger
505  log_value(NORMAL, "Event list name", user_name);
506 
507  // Write events into in-memory FITS file
508  GFits fits;
509  obs->write(fits);
510 
511  // Publish
512  fits.publish(gammalib::extname_cta_events, user_name);
513 
514  } // endif: there were events
515 
516  } // endif: observation was a CTA observation
517 
518  } // endfor: looped over observations
519 
520  // Return
521  return;
522 }
523 
524 
525 /*==========================================================================
526  = =
527  = Private methods =
528  = =
529  ==========================================================================*/
530 
531 /***********************************************************************//**
532  * @brief Initialise class members
533  *
534  * The thrown area is fixed to pi*(2500^2) m2, which is the same value that
535  * is used in the Monte Carlo simulations (information from Konrad Bernloehr)
536  ***************************************************************************/
538 {
539  // Initialise user parameters
540  m_outevents.clear();
541  m_prefix.clear();
542  m_startindex = 1;
543  m_seed = 1;
544  m_eslices = 10;
545  m_apply_edisp = false;
546  m_max_rate = 1.0e6;
547 
548  // Initialise protected members
549  m_models.clear();
550  m_rans.clear();
551  m_save_and_dispose = false;
552 
553  // Set fixed parameters
554  m_max_photons = 1000000; //!< Maximum number of photons / time slice
555 
556  // Initialise first event identifier
557  m_event_id = 1;
558 
559  // Return
560  return;
561 }
562 
563 
564 /***********************************************************************//**
565  * @brief Copy class members
566  *
567  * @param[in] app Application.
568  ***************************************************************************/
570 {
571  // Copy user parameters
572  m_outevents = app.m_outevents;
573  m_prefix = app.m_prefix;
575  m_seed = app.m_seed;
576  m_eslices = app.m_eslices;
578  m_max_rate = app.m_max_rate;
579 
580  // Copy protected members
581  m_models = app.m_models;
584  m_rans = app.m_rans;
585  m_event_id = app.m_event_id;
586 
587  // Return
588  return;
589 }
590 
591 
592 /***********************************************************************//**
593  * @brief Delete class members
594  ***************************************************************************/
596 {
597  // Return
598  return;
599 }
600 
601 
602 /***********************************************************************//**
603  * @brief Get application parameters
604  *
605  * Get all task parameters from parameter file or (if required) by querying
606  * the user. Most parameters are only required if no observation exists so
607  * far in the observation container. In this case, a single CTA observation
608  * will be added to the container, using the definition provided in the
609  * parameter file.
610  ***************************************************************************/
612 {
613  // Initialise seed vector
614  m_rans.clear();
615 
616  // If there are no observations in container then load them via user
617  // parameters
618  if (m_obs.size() == 0) {
619 
620  // Throw exception if counts cube is given
622 
623  // Get observation container
625 
626  }
627 
628  // ... otherwise add response information and energy boundaries in case
629  // that they are missing
630  else {
632  }
633 
634  // If models have been specified using the models() method then use
635  // these models instead of any models that may already exist in the
636  // observations.
637  if (m_models.size() > 0) {
638  m_obs.models(m_models);
639  }
640 
641  // Set observation boundaries
642  set_obs_bounds();
643 
644  // Read model definition file if required
645  if (m_obs.models().size() == 0) {
646  std::string inmodel = (*this)["inmodel"].filename();
647  m_obs.models(inmodel);
648  }
649 
650  // Get other parameters
651  m_seed = (*this)["seed"].integer();
652  m_eslices = (*this)["eslices"].integer();
653  m_apply_edisp = (*this)["edisp"].boolean();
654  m_max_rate = (*this)["maxrate"].real();
655  m_startindex = (*this)["startindex"].integer();
656 
657  // If needed later, query output filename and prefix now
658  if (read_ahead()) {
659  (*this)["outevents"].query();
660  (*this)["prefix"].query();
661  }
662 
663  // Initialise random number generators. We initialise here one random
664  // number generator per observation so that each observation will
665  // get it's own random number generator. This will lead to identical
666  // results independently of code parallelization with OpenMP. The
667  // seeds for all random number generators are derived randomly but
668  // fully deterministacally from the seed parameter, so that a given
669  // seed parameter leads always to the same set of simulated events, and
670  // this independently of parallelization.
671 
672  // Get a random number generator for seed determination
673  GRan master(m_seed);
674 
675  // Allocate vector of random number generator seeds
676  std::vector<unsigned long long int> seeds;
677 
678  // Loop over all observations in the container
679  for (int i = 0; i < m_obs.size(); ++i) {
680 
681  // Allocate new seed value
682  unsigned long long int new_seed;
683 
684  // Determine new seed value. We make sure that the new seed
685  // value has not been used already for another observation.
686  bool repeat = false;
687  do {
688  new_seed = (unsigned long long int)(master.uniform() * 1.0e10) +
689  m_seed;
690  repeat = false;
691  for (int j = 0; j < seeds.size(); ++j) {
692  if (new_seed == seeds[j]) {
693  repeat = true;
694  break;
695  }
696  }
697  } while(repeat);
698 
699  // Add the seed to the vector for bookkeeping
700  seeds.push_back(new_seed);
701 
702  // Use the seed to create a random number generator for the
703  // actual observation
704  m_rans.push_back(GRan(new_seed));
705 
706  } // endfor: looped over observations
707 
708  // Set number of OpenMP threads
709  #ifdef _OPENMP
710  int nthreads = (*this)["nthreads"].integer();
711  if (nthreads > 0) {
712  omp_set_num_threads(nthreads);
713  }
714  #endif
715 
716  // Write parameters into logger
717  log_parameters(TERSE);
718 
719  // Return
720  return;
721 }
722 
723 
724 /***********************************************************************//**
725  * @brief Simulate source events from photon list
726  *
727  * @param[in] obs Pointer on CTA observation.
728  * @param[in] models Model list.
729  * @param[in,out] ran Random number generator.
730  * @param[in] wrklog Pointer to logger.
731  *
732  * Simulate source events from a photon list for a given CTA observation and
733  * all source models. The events are stored in form of an event list in the
734  * observation.
735  *
736  * The method will loop over all Good Time Intervals (GTI) to perform the
737  * simulations. Within a given GTI the requested energy range is split into
738  * a number of energy slices so that the simulation area can be adapted to
739  * the effective area of the instrument (this avoids simulating a large
740  * number of sources photons at low energies while accepting only few of
741  * them do to the small effective area). In case that the flux of a source
742  * is large, the simulation may be done within time slices so that the
743  * memory requirements won't get too large.
744  *
745  * This method does nothing if the observation pointer is NULL. It verifies
746  * if the observation has a CTA IRF response.
747  ***************************************************************************/
748 void ctobssim::simulate_source(GCTAObservation* obs,
749  const GModels& models,
750  GRan& ran,
751  GLog* wrklog)
752 {
753  // Debug code: signal that we step into the simulate_source method
754  #if defined(G_SOURCE_DEBUG)
755  std::cout << "ctobssim::simulate_source: in" << std::endl;
756  #endif
757 
758  // Continue only if observation pointer is valid
759  if (obs != NULL) {
760 
761  // If no logger is specified then use the default logger
762  if (wrklog == NULL) {
763  wrklog = &log;
764  }
765 
766  // Get pointer on event list
767  GCTAEventList* events = static_cast<GCTAEventList*>(obs->events());
768 
769  // Get CTA response
770  const GCTAResponseIrf* rsp =
771  dynamic_cast<const GCTAResponseIrf*>(obs->response());
772  if (rsp == NULL) {
773  std::string cls = std::string(typeid(obs->response()).name());
774  std::string msg = "Response of type \""+cls+"\" is not a CTA "
775  "IRF response. Please make sure that a CTA "
776  "IRF response is contained in the CTA "
777  "observation.";
778  throw GException::invalid_value(G_SIMULATE_SOURCE, msg);
779  }
780 
781  // Extract simulation region from event list ROI
782  GSkyDir dir = events->roi().centre().dir();
783  double rad = events->roi().radius() + g_roi_margin;
784 
785  // Determine energy boundaries for simulation
786  GEbounds ebounds = get_ebounds(events->ebounds());
787 
788  // Log simulation cone information
789  if (logTerse()) {
790  *wrklog << gammalib::parformat("Simulation cone");
791  *wrklog << "RA=" << dir.ra_deg() << " deg";
792  *wrklog << ", Dec=" << dir.dec_deg() << " deg";
793  *wrklog << ", radius=" << rad << " deg" << std::endl;
794  }
795 
796  // Initialise indentation for logging
797  int indent = 0;
798 
799  // Initialise photon and event counters
800  std::vector<int> nphotons(models.size(),0);
801  std::vector<int> nevents(models.size(),0);
802 
803  // Loop over all Good Time Intervals
804  for (int it = 0; it < events->gti().size(); ++it) {
805 
806  // Extract time interval and time reference
807  GTime tmin = events->gti().tstart(it);
808  GTime tmax = events->gti().tstop(it);
809  GTimeReference tref = events->gti().reference();
810 
811  // Log time interval. Increment indentation if there are
812  // several Good Time Intervals.
813  if (logNormal()) {
814  if (events->gti().size() > 1) {
815  indent++;
816  wrklog->indent(indent);
817  }
818  *wrklog << gammalib::parformat("Time interval", indent);
819  *wrklog << tmin.convert(tref);
820  *wrklog << " - ";
821  *wrklog << tmax.convert(tref);
822  *wrklog << " s" << std::endl;
823  }
824 
825  // Loop over all energy boundaries
826  for (int ie = 0; ie < ebounds.size(); ++ie) {
827 
828  // Set reconstructed energy interval
829  GEnergy ereco_min = ebounds.emin(ie);
830  GEnergy ereco_max = ebounds.emax(ie);
831 
832  // Set true photon energy limits for simulation. If the
833  // observation has energy dispersion then add a margin.
834  GEnergy etrue_min = ereco_min;
835  GEnergy etrue_max = ereco_max;
836  if (rsp->use_edisp()) {
837  etrue_min = rsp->ebounds(etrue_min).emin();
838  etrue_max = rsp->ebounds(etrue_max).emax();
839  }
840 
841  // Determine simulation area
842  double area = get_area(obs, etrue_min, etrue_max);
843 
844  // Log energy range and simulation area
845  if (logNormal()) {
846  *wrklog << gammalib::parformat("Photon energy range", indent);
847  *wrklog << etrue_min << " - " << etrue_max << std::endl;
848  *wrklog << gammalib::parformat("Event energy range", indent);
849  *wrklog << ereco_min << " - " << ereco_max << std::endl;
850  }
851 
852  // Increment indentation if there are several energy
853  // boundaries
854  if (logNormal()) {
855  if (ebounds.size() > 1) {
856  indent++;
857  wrklog->indent(indent);
858  }
859  }
860 
861  // Log simulation area
862  if (logNormal()) {
863  *wrklog << gammalib::parformat("Simulation area", indent);
864  *wrklog << area << " cm2" << std::endl;
865  }
866 
867  // Save state of event counter before doing the simulation
868  int nevents_before = events->size();
869 
870  // Simulate events for this time and energy interval
871  simulate_interval(obs, rsp, events, models, tmin, tmax,
872  etrue_min, etrue_max, ereco_min, ereco_max,
873  dir, rad, area,
874  ran, wrklog, indent, nphotons, nevents);
875 
876  // Log simulation results
877  if (logNormal()) {
878  *wrklog << gammalib::parformat("MC source events", indent);
879  *wrklog << events->size() - nevents_before;
880  *wrklog << " (all source models)";
881  *wrklog << std::endl;
882  }
883 
884  // Reset indentation if there were several energy boundaries
885  if (logNormal()) {
886  if (ebounds.size() > 1) {
887  indent--;
888  wrklog->indent(indent);
889 
890  }
891  }
892 
893  } // endfor: looped over all energy boundaries
894 
895  // Reset indentation if there were several Good Time Intervals
896  if (logNormal()) {
897  if (events->gti().size() > 1) {
898  indent--;
899  wrklog->indent(indent);
900  }
901  }
902 
903  } // endfor: looped over all time intervals
904 
905  // Reset indentation
906  wrklog->indent(0);
907 
908  // Log simulation summary
909  if (logTerse()) {
910  for (int i = 0; i < models.size(); ++i) {
911  const GModelSky* model = dynamic_cast<const GModelSky*>(models[i]);
912  if (model == NULL) {
913  continue;
914  }
915  if (!model->is_valid(obs->instrument(), obs->id())) {
916  continue;
917  }
918  *wrklog << gammalib::parformat("MC source photons");
919  *wrklog << nphotons[i];
920  if (model->name().length() > 0) {
921  *wrklog << " [" << model->name() << "]";
922  }
923  *wrklog << std::endl;
924  *wrklog << gammalib::parformat("MC source events");
925  *wrklog << nevents[i];
926  if (model->name().length() > 0) {
927  *wrklog << " [" << model->name() << "]";
928  }
929  *wrklog << std::endl;
930  }
931  }
932 
933  } // endif: observation pointer was valid
934 
935  // Debug code: signal that we are down with the simulate_source method
936  #if defined(G_SOURCE_DEBUG)
937  std::cout << "ctobssim::simulate_source: out" << std::endl;
938  #endif
939 
940  // Return
941  return;
942 }
943 
944 
945 /***********************************************************************//**
946  * @brief Simulate source events for a time and energy interval
947  *
948  * @param[in] obs Pointer on CTA observation.
949  * @param[in] rsp Pointer on CTA IRF response.
950  * @param[in,out] events Pointer on CTA event list.
951  * @param[in] models Model list.
952  * @param[in] tmin Start time.
953  * @param[in] tmax Stop time.
954  * @param[in] etrue_min Minimum true energy.
955  * @param[in] etrue_max Maximum true energy.
956  * @param[in] ereco_min Minimum reconstructed energy.
957  * @param[in] ereco_max Maximum reconstructed energy.
958  * @param[in] dir Simulation cone centre.
959  * @param[in] rad Simulation cone radius (degrees).
960  * @param[in] area Simulation area (cm^2).
961  * @param[in,out] ran Random number generator.
962  * @param[in] wrklog Pointer to logger.
963  * @param[in,out] indent Logger indent.
964  * @param[in,out] nphotons Number of photons for all models.
965  * @param[in,out] nevents Number of events for all models.
966  *
967  * Simulate source events for a time and energy interval.
968  ***************************************************************************/
969 void ctobssim::simulate_interval(GCTAObservation* obs,
970  const GCTAResponseIrf* rsp,
971  GCTAEventList* events,
972  const GModels& models,
973  const GTime& tmin,
974  const GTime& tmax,
975  const GEnergy& etrue_min,
976  const GEnergy& etrue_max,
977  const GEnergy& ereco_min,
978  const GEnergy& ereco_max,
979  const GSkyDir& dir,
980  const double& rad,
981  const double& area,
982  GRan& ran,
983  GLog* wrklog,
984  int& indent,
985  std::vector<int>& nphotons,
986  std::vector<int>& nevents)
987 {
988  // Debug code: signal that we step into the simulate_interval method
989  #if defined(G_SOURCE_DEBUG)
990  std::cout << "ctobssim::simulate_interval: in";
991  std::cout << " Etrue=[" << etrue_min << "," << etrue_max << "]";
992  std::cout << " Ereco=[" << ereco_min << "," << ereco_max << "]";
993  std::cout << std::endl;
994  #endif
995 
996  // Loop over all models
997  for (int i = 0; i < models.size(); ++i) {
998 
999  // Get sky model (NULL if not a sky model)
1000  const GModelSky* model = dynamic_cast<const GModelSky*>(models[i]);
1001 
1002  // If the model is not a sky model then skip the model
1003  if (model == NULL) {
1004  continue;
1005  }
1006 
1007  // If the model does not apply to the instrument and observation
1008  // identifier then skip the model
1009  if (!model->is_valid(obs->instrument(), obs->id())) {
1010  continue;
1011  }
1012 
1013  // Determine duration of a time slice by limiting the number of
1014  // simulated photons to m_max_photons. The photon rate is estimated
1015  // from the model flux and used to set the duration of the time
1016  // slice.
1017  double flux = get_model_flux(model, etrue_min, etrue_max, dir, rad,
1018  indent, wrklog);
1019  double rate = flux * area;
1020  double duration = 1800.0; // default: 1800 sec
1021  if (rate > 0.0) {
1022  duration = m_max_photons / rate;
1023  if (duration < 1.0) { // not <1 sec
1024  duration = 1.0;
1025  }
1026  else if (duration > 180000.0) { // not >50 hr
1027  duration = 180000.0;
1028  }
1029  }
1030 
1031  // Skip model if photon rate is 0
1032  if (rate <= 0.0) {
1033  continue;
1034  }
1035 
1036  // Log photon rate
1037  if (logNormal()) {
1038  *wrklog << gammalib::parformat("Photon rate", indent);
1039  *wrklog << rate << " photons/s";
1040  if (model->name().length() > 0) {
1041  *wrklog << " [" << model->name() << "]";
1042  }
1043  *wrklog << std::endl;
1044  }
1045 
1046  // If photon rate exceeds the maximum photon rate that is allowed
1047  // then throw an exception
1048  if (rate > m_max_rate) {
1049  std::string mod = (model->name().length() > 0) ?
1050  model->name() : "Unknown";
1051  std::string msg = "Photon rate "+gammalib::str(rate)+
1052  " photons/s for model \""+mod+"\" exceeds "
1053  "maximum allowed photon rate of "+
1054  gammalib::str(m_max_rate)+" photons/s. "
1055  "Please check the parameters of model "
1056  "\""+mod+"\" or increase the value of the "
1057  "\"maxrate\" parameter.";
1058  throw GException::invalid_value(G_SIMULATE_INTERVAL, msg);
1059  }
1060 
1061  // To reduce memory requirements we split long time intervals into
1062  // several time slices
1063  GTime tstart = tmin;
1064  GTime tstop = tstart + duration;
1065 
1066  // Save state of photon and event counters before doing the
1067  // simulation
1068  int nphotons_before = nphotons[i];
1069  int nevents_before = nevents[i];
1070 
1071  // Loop over time slices
1072  while (tstart < tmax) {
1073 
1074  // Make sure that tstop <= tmax
1075  if (tstop > tmax) {
1076  tstop = tmax;
1077  }
1078 
1079  // Log time slice
1080  if (logExplicit()) {
1081  if (tmax - tmin > duration) {
1082  indent++;
1083  wrklog->indent(indent);
1084  }
1085  *wrklog << gammalib::parformat("Time slice", indent);
1086  *wrklog << tstart.convert(obs->gti().reference()) << " - ";
1087  *wrklog << tstop.convert(obs->gti().reference()) << " s";
1088  if (model->name().length() > 0) {
1089  *wrklog << " [" << model->name() << "]";
1090  }
1091  *wrklog << std::endl;
1092  }
1093 
1094  // Simulate time slice
1095  simulate_time_slice(obs, rsp, events, model, i+1, tstart, tstop,
1096  etrue_min, etrue_max, ereco_min, ereco_max,
1097  dir, rad, area,
1098  ran, wrklog, indent,
1099  nphotons[i], nevents[i]);
1100 
1101  // Go to next time slice
1102  tstart = tstop;
1103  tstop = tstart + duration;
1104 
1105  // Reset indentation
1106  if (logExplicit()) {
1107  if (tmax - tmin > duration) {
1108  indent--;
1109  wrklog->indent(indent);
1110  }
1111  }
1112 
1113  } // endwhile: looped over time slices
1114 
1115  // Log simulation results
1116  if (logNormal()) {
1117  *wrklog << gammalib::parformat("MC source photons", indent);
1118  *wrklog << nphotons[i] - nphotons_before;
1119  if (model->name().length() > 0) {
1120  *wrklog << " [" << model->name() << "]";
1121  }
1122  *wrklog << std::endl;
1123  *wrklog << gammalib::parformat("MC source events", indent);
1124  *wrklog << nevents[i] - nevents_before;
1125  if (model->name().length() > 0) {
1126  *wrklog << " [" << model->name() << "]";
1127  }
1128  *wrklog << std::endl;
1129 
1130  }
1131 
1132  } // endfor: looped over models
1133 
1134  // Debug code: signal that we are down with the simulate_interval method
1135  #if defined(G_SOURCE_DEBUG)
1136  std::cout << "ctobssim::simulate_interval: out" << std::endl;
1137  #endif
1138 
1139  // Return
1140  return;
1141 }
1142 
1143 
1144 /***********************************************************************//**
1145  * @brief Simulate source events for a time slice
1146  *
1147  * @param[in] obs Pointer on CTA observation.
1148  * @param[in] rsp Pointer on CTA response.
1149  * @param[in,out] events Pointer on CTA event list.
1150  * @param[in] model Model.
1151  * @param[in] mc_id Monte Carlo identifier of model.
1152  * @param[in] tstart Start time.
1153  * @param[in] tstop Stop time.
1154  * @param[in] etrue_min Minimum true energy.
1155  * @param[in] etrue_max Maximum true energy.
1156  * @param[in] ereco_min Minimum reconstructed energy.
1157  * @param[in] ereco_max Maximum reconstructed energy.
1158  * @param[in] dir Simulation cone centre.
1159  * @param[in] rad Simulation cone radius (degrees).
1160  * @param[in] area Simulation area (cm^2).
1161  * @param[in,out] ran Random number generator.
1162  * @param[in] wrklog Pointer to logger.
1163  * @param[in,out] indent Logger indent.
1164  * @param[in,out] nphotons Number of photons.
1165  * @param[in,out] nevents Number of events.
1166  *
1167  * Simulate source events for a time slice.
1168  ***************************************************************************/
1169 void ctobssim::simulate_time_slice(GCTAObservation* obs,
1170  const GCTAResponseIrf* rsp,
1171  GCTAEventList* events,
1172  const GModelSky* model,
1173  const int& mc_id,
1174  const GTime& tstart,
1175  const GTime& tstop,
1176  const GEnergy& etrue_min,
1177  const GEnergy& etrue_max,
1178  const GEnergy& ereco_min,
1179  const GEnergy& ereco_max,
1180  const GSkyDir& dir,
1181  const double& rad,
1182  const double& area,
1183  GRan& ran,
1184  GLog* wrklog,
1185  int& indent,
1186  int& nphotons,
1187  int& nevents)
1188 {
1189  // Debug code: signal that we step into the model MC method
1190  #if defined(G_SOURCE_DEBUG)
1191  std::cout << "ctobssim::simulate_time_slice: model->mc in" << std::endl;
1192  #endif
1193 
1194  // Get photons
1195  GPhotons photons = model->mc(area, dir, rad, etrue_min, etrue_max,
1196  tstart, tstop, ran);
1197 
1198  // Debug code: signal that we came back
1199  #if defined(G_SOURCE_DEBUG)
1200  std::cout << "ctobssim::simulate_time_slice: model->mc out" << std::endl;
1201  #endif
1202 
1203  // Dump number of simulated photons
1204  if (logExplicit()) {
1205  *wrklog << gammalib::parformat("MC source photons/slice", indent);
1206  *wrklog << photons.size();
1207  if (model->name().length() > 0) {
1208  *wrklog << " [" << model->name() << "]";
1209  }
1210  *wrklog << std::endl;
1211  }
1212 
1213  // Simulate events from photons
1214  for (int i = 0; i < photons.size(); ++i) {
1215 
1216  // Increment photon counter
1217  nphotons++;
1218 
1219  // Debug code: signal that we step into the response MC method
1220  #if defined(G_SOURCE_DEBUG)
1221  std::cout << "ctobssim::simulate_time_slice: rsp->mc in" << std::endl;
1222  #endif
1223 
1224  // Simulate event. Note that this method includes the deadtime
1225  // correction.
1226  GCTAEventAtom* event = rsp->mc(area, photons[i], *obs, ran);
1227 
1228  // Debug code: signal that we came back
1229  #if defined(G_SOURCE_DEBUG)
1230  std::cout << "ctobssim::simulate_time_slice: rsp->mc out" << std::endl;
1231  #endif
1232 
1233  // Use event only if it exists and if it falls within ROI, the
1234  // reconstructed energy interval and the time slice
1235  if (event != NULL) {
1236  if (events->roi().contains(*event) &&
1237  event->energy() >= ereco_min &&
1238  event->energy() <= ereco_max &&
1239  event->time() >= tstart &&
1240  event->time() <= tstop) {
1241 
1242  // Set event identifier
1243  event->event_id(m_event_id);
1244 
1245  // Set Monte Carlo identifier
1246  event->mc_id(mc_id);
1247 
1248  // Append event
1249  events->append(*event);
1250 
1251  // Increment counters
1252  m_event_id++;
1253  nevents++;
1254  }
1255  delete event;
1256  }
1257 
1258  } // endfor: looped over events
1259 
1260  // Signal that event list contains Monte Carlo identifiers
1261  events->has_mc_id(true);
1262 
1263  // Return
1264  return;
1265 }
1266 
1267 
1268 /***********************************************************************//**
1269  * @brief Get energy boundaries
1270  *
1271  * @param[in] ebounds Energy boundaries of events.
1272  * @return Energy boundaries for simulation.
1273  *
1274  * Get the energy boundaries for the source simulation.
1275  *
1276  * @todo Need to take care of the (rare) case that there are distinct
1277  * energy boundaries in the event list. In that case we need to
1278  * create mixed energy boundaries.
1279  ***************************************************************************/
1280 GEbounds ctobssim::get_ebounds(const GEbounds& ebounds) const
1281 {
1282  // Set energy bins
1283  GEbounds ebins(m_eslices, ebounds.emin(), ebounds.emax());
1284 
1285  // Return energy bins
1286  return (ebins);
1287 }
1288 
1289 
1290 /***********************************************************************//**
1291  * @brief Get simulation area (cm^2)
1292  *
1293  * @param[in] obs Pointer on CTA observation.
1294  * @param[in] emin Minimum true energy.
1295  * @param[in] emax Maximum true energy.
1296  * @return Simulation area (cm^2).
1297  *
1298  * Get the simulation area for an energy interval in units of cm^2. This is
1299  * done by extracting the maximum effective area value within the energy
1300  * range [emin,emax] and by multiplying this value by 2 for security. The
1301  * effective area is sampled at 100 energy values within the energy interval.
1302  ***************************************************************************/
1303 double ctobssim::get_area(GCTAObservation* obs,
1304  const GEnergy& emin,
1305  const GEnergy& emax) const
1306 {
1307  // Get CTA response
1308  const GCTAResponseIrf* rsp =
1309  dynamic_cast<const GCTAResponseIrf*>(obs->response());
1310  if (rsp == NULL) {
1311  std::string cls = std::string(typeid(rsp).name());
1312  std::string msg = "Response of type \""+cls+"\" is not a CTA IRF "
1313  "response. Please make sure that a CTA IRF "
1314  "response is contained in the CTA observation.";
1315  throw GException::invalid_value(G_GET_AREA, msg);
1316  }
1317 
1318  // Compute effective area at minimum and maximum energy
1319  const int nbins = 10;
1320  double logE = emin.log10TeV();
1321  double logEbin = (emax.log10TeV() - logE)/double(nbins-1);
1322  double area = 0.0;
1323  for (int i = 0; i < nbins; ++i, logE += logEbin) {
1324  double aeff = rsp->aeff()->max(logE, 0.0, 0.0);
1325  if (aeff > area) {
1326  area = aeff;
1327  }
1328  }
1329 
1330  // Multiply by security factor
1331  area *= 2.0;
1332 
1333  // Return simulation area
1334  return area;
1335 }
1336 
1337 
1338 /***********************************************************************//**
1339  * @brief Determine sky model flux
1340  *
1341  * @param[in] model Sky model.
1342  * @param[in] emin Minimum energy.
1343  * @param[in] emax Maximum energy.
1344  * @param[in] centre Centre of region for photon rate determination.
1345  * @param[in] radius Radius of region for photon rate determination (degrees).
1346  * @param[in] indent Indent for logging.
1347  * @param[in,out] wrklog Pointer to logger.
1348  * @return Model flux (photons/cm2/sec).
1349  ***************************************************************************/
1350 double ctobssim::get_model_flux(const GModelSky* model,
1351  const GEnergy& emin,
1352  const GEnergy& emax,
1353  const GSkyDir& centre,
1354  const double& radius,
1355  const int& indent,
1356  GLog* wrklog)
1357 {
1358  // Debug code: signal that we step into the get_model_flux method
1359  #if defined(G_SOURCE_DEBUG)
1360  std::cout << "ctobssim::get_model_flux: in";
1361  std::cout << " Etrue=[" << emin << "," << emax << "]";
1362  std::cout << std::endl;
1363  #endif
1364 
1365  // Initialise flux
1366  double flux = 0.0;
1367 
1368  // Determine the spatial model normalization within the simulation
1369  // cone and check whether the model will produce any photons in that
1370  // cone.
1371  double norm = model->spatial()->mc_norm(centre, radius);
1372  bool use_model = (norm > 0.0) ? true : false;
1373  if (logNormal()) {
1374  if (use_model) {
1375  *wrklog << gammalib::parformat("Use model", indent);
1376  }
1377  else {
1378  *wrklog << gammalib::parformat("Skip model", indent);
1379  }
1380  if (model->name().length() > 0) {
1381  *wrklog << model->name();
1382  }
1383  *wrklog << std::endl;
1384  *wrklog << gammalib::parformat("Normalization", indent);
1385  *wrklog << norm;
1386  if (model->name().length() > 0) {
1387  *wrklog << " [" << model->name() << "]";
1388  }
1389  *wrklog << std::endl;
1390  }
1391 
1392  // Continue only if model overlaps with simulation region
1393  if (use_model) {
1394 
1395  // Initialise de-allocation flag
1396  bool free_spectral = false;
1397 
1398  // Get pointer to spectral model
1399  const GModelSpectral* spectral = model->spectral();
1400 
1401  // If the spatial model is a diffuse cube then create a node
1402  // function spectral model that is the product of the diffuse
1403  // cube node function and the spectral model evaluated at the
1404  // energies of the node function
1405  GModelSpatialDiffuseCube* cube =
1406  dynamic_cast<GModelSpatialDiffuseCube*>(model->spatial());
1407  if (cube != NULL) {
1408 
1409  // Set MC cone
1410  cube->mc_cone(GSkyRegionCircle(centre, radius));
1411 
1412  // Allocate node function to replace the spectral component
1413  GModelSpectralNodes* nodes =
1414  new GModelSpectralNodes(cube->spectrum());
1415  for (int i = 0; i < nodes->nodes(); ++i) {
1416  GEnergy energy = nodes->energy(i);
1417  double intensity = nodes->intensity(i);
1418  double value = spectral->eval(energy);
1419  nodes->intensity(i, value * intensity);
1420  }
1421 
1422  // Signal that node function needs to be de-allocated later
1423  free_spectral = true;
1424 
1425  // Set the spectral model pointer to the node function
1426  spectral = nodes;
1427 
1428  // Kluge: if there are no nodes then the spectral->flux method
1429  // will throw an exception. We therefore set here the use_model
1430  // flag to false in case that there are no spectral nodes
1431  if (nodes->nodes() == 0) {
1432  use_model = false;
1433  }
1434 
1435  } // endif: spatial model was a diffuse cube
1436 
1437  // Compute flux within [emin, emax] in model from spectral
1438  // component (units: ph/cm2/s)
1439  double flux0 = (use_model) ? spectral->flux(emin, emax) : 0.0;
1440  flux = flux0 * norm;
1441 
1442  // Dump flux
1443  if (logNormal()) {
1444  *wrklog << gammalib::parformat("Flux", indent);
1445  *wrklog << flux0;
1446  if (model->name().length() > 0) {
1447  *wrklog << " [" << model->name() << "]";
1448  }
1449  *wrklog << " photons/cm2/s" << std::endl;
1450  *wrklog << gammalib::parformat("Normalized flux", indent);
1451  *wrklog << flux;
1452  if (model->name().length() > 0) {
1453  *wrklog << " [" << model->name() << "]";
1454  }
1455  *wrklog << " photons/cm2/s" << std::endl;
1456  }
1457 
1458  // Free spectral model if required
1459  if (free_spectral) delete spectral;
1460 
1461  } // endif: model overlaps with simulation region
1462 
1463  // Debug code: signal that we step out of the get_model_flux method
1464  #if defined(G_SOURCE_DEBUG)
1465  std::cout << "ctobssim::get_model_flux: out" << std::endl;
1466  #endif
1467 
1468  // Return model flux
1469  return flux;
1470 }
1471 
1472 
1473 /***********************************************************************//**
1474  * @brief Simulate background events from model
1475  *
1476  * @param[in] obs Pointer on CTA observation.
1477  * @param[in] models Models.
1478  * @param[in] ran Random number generator.
1479  * @param[in] wrklog Pointer to logger.
1480  *
1481  * Simulate background events from models. The events are stored as event
1482  * list in the observation.
1483  *
1484  * This method does nothing if the observation pointer is NULL.
1485  ***************************************************************************/
1486 void ctobssim::simulate_background(GCTAObservation* obs,
1487  const GModels& models,
1488  GRan& ran,
1489  GLog* wrklog)
1490 {
1491  // Continue only if observation pointer is valid
1492  if (obs != NULL) {
1493 
1494  // If no logger is specified then use the default logger
1495  if (wrklog == NULL) {
1496  wrklog = &log;
1497  }
1498 
1499  // Get pointer on event list (circumvent const correctness)
1500  GCTAEventList* events =
1501  static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events()));
1502 
1503  // Loop over all models
1504  for (int i = 0; i < models.size(); ++i) {
1505 
1506  // Get data model (NULL if not a data model)
1507  const GModelData* model =
1508  dynamic_cast<const GModelData*>(models[i]);
1509 
1510  // If we have a data model that applies to the present observation
1511  // then simulate events
1512  if (model != NULL &&
1513  model->is_valid(obs->instrument(), obs->id())) {
1514 
1515  // Debug code: signal that we step into the response MC method
1516  #if defined(G_BACKGROUND_DEBUG)
1517  std::cout << "ctobssim::simulate_background: model->mc in"
1518  << std::endl;
1519  #endif
1520 
1521  // Get simulated CTA event list
1522  GCTAEventList* list =
1523  dynamic_cast<GCTAEventList*>(model->mc(*obs, ran));
1524 
1525  // Debug code: signal that we came back
1526  #if defined(G_BACKGROUND_DEBUG)
1527  std::cout << "ctobssim::simulate_background: model->mc out"
1528  << std::endl;
1529  #endif
1530 
1531  // Continue only if we got a CTA event list
1532  if (list != NULL) {
1533 
1534  // Reserves space for events
1535  events->reserve(list->size()+events->size());
1536 
1537  // Initialise statistics
1538  int n_appended = 0;
1539  int n_outside_roi = 0;
1540 
1541  // Append events
1542  for (int k = 0; k < list->size(); k++) {
1543 
1544  // Get event pointer
1545  GCTAEventAtom* event = (*list)[k];
1546 
1547  // Use event only if it falls within ROI
1548  if (events->roi().contains(*event)) {
1549 
1550  // Set event identifier
1551  event->event_id(m_event_id);
1552  m_event_id++;
1553 
1554  // Set Monte Carlo identifier
1555  event->mc_id(i+1);
1556 
1557  // Append event
1558  events->append(*event);
1559 
1560  // Increment number of appended events
1561  n_appended++;
1562 
1563  } // endif: event was within ROI
1564 
1565  // ... otherwise increment outside ROI counter
1566  else {
1567  n_outside_roi++;
1568  }
1569 
1570  } // endfor: looped over all events
1571 
1572  // Dump simulation results
1573  if (logNormal()) {
1574  *wrklog << gammalib::parformat("MC events outside ROI");
1575  *wrklog << n_outside_roi << std::endl;
1576  *wrklog << gammalib::parformat("MC background events");
1577  *wrklog << n_appended << std::endl;
1578  }
1579 
1580  // Free event list
1581  delete list;
1582 
1583  } // endif: we had a CTA event list
1584 
1585  } // endif: model was valid
1586 
1587  } // endfor: looped over all models
1588 
1589  // Signal that event list contains Monte Carlo identifiers
1590  events->has_mc_id(true);
1591 
1592  } // endif: observation pointer was valid
1593 
1594  // Return
1595  return;
1596 }
1597 
1598 
1599 /***********************************************************************//**
1600  * @brief Set correspondance between Monte Carlo identifier and model names
1601  *
1602  * @param[in] obs Pointer on CTA observation.
1603  * @param[in] models Models.
1604  * @param[in] wrklog Pointer to logger.
1605  *
1606  * Sets the correspondance between Monte Carlo identifier and model names.
1607  * The method collects all Monte Carlo identifiers from the event list and
1608  * sets the corresponding source names using the
1609  * GCTAEventList::set_mc_id_names method.
1610  ***************************************************************************/
1611 void ctobssim::set_mc_id_names(GCTAObservation* obs,
1612  const GModels& models,
1613  GLog* wrklog)
1614 {
1615  // Continue only if observation pointer is valid
1616  if (obs != NULL) {
1617 
1618  // If no logger is specified then use the default logger
1619  if (wrklog == NULL) {
1620  wrklog = &log;
1621  }
1622 
1623  // Get pointer on event list (circumvent const correctness)
1624  GCTAEventList* events =
1625  static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events()));
1626 
1627  // Initialise lists
1628  std::vector<int> ids;
1629  std::vector<std::string> names;
1630 
1631  // Collect all Monte Carlo identifiers
1632  for (int i = 0; i < events->size(); ++i) {
1633 
1634  // Get Monte Carlo identifier for event
1635  int id = (*events)[i]->mc_id();
1636 
1637  // If this is the first event then push the Monte Carlo
1638  // identifier into list ...
1639  if (i == 0) {
1640  ids.push_back(id);
1641  names.push_back(models[id-1]->name());
1642  }
1643 
1644  // ... otherwise push the Monte Carlo identifier only into list
1645  // if the ID does not yet exist
1646  else {
1647  bool id_found = false;
1648  for (int k = 0; k < ids.size(); ++k) {
1649  if (ids[k] == id) {
1650  id_found = true;
1651  break;
1652  }
1653  }
1654  if (!id_found) {
1655  ids.push_back(id);
1656  names.push_back(models[id-1]->name());
1657  }
1658  }
1659 
1660  } // endfor: collected all Monte Carlo identifiers
1661 
1662  // Set correspondance for the event list
1663  if ((ids.size() > 0) && (names.size() > 0)) {
1664 
1665  // Set Monte Carlo identifiers for event list
1666  events->set_mc_id_names(ids, names);
1667 
1668  // Log Monte Carlo identifiers into log file
1669  if (logNormal()) {
1670  for (int k = 0; k < ids.size(); ++k) {
1671  *wrklog << gammalib::parformat("MC identifier " +
1672  gammalib::str(ids[k]));
1673  *wrklog << names[k] << std::endl;
1674  }
1675  }
1676 
1677  } // endif: there were Monte Carlo identifiers
1678 
1679  } // endif: observation pointer was valid
1680 
1681  // Return
1682  return;
1683 }
1684 
1685 
1686 /***********************************************************************//**
1687  * @brief Save event list in FITS format.
1688  *
1689  * Save the event list as a FITS file. The filename of the FITS file is
1690  * specified by the outfile parameter.
1691  ***************************************************************************/
1693 {
1694  // Save only if event list has not yet been saved and disposed and if
1695  // there are observations
1696  if (!m_save_and_dispose && m_obs.size() > 0) {
1697 
1698  // Get output filename
1699  m_outevents = (*this)["outevents"].filename();
1700 
1701  // Get CTA observation from observation container
1702  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
1703 
1704  // Log filename
1705  log_value(NORMAL, "Event list file", m_outevents);
1706 
1707  // Save observation into FITS file
1708  obs->save(m_outevents, clobber());
1709 
1710  // Stamp FITS file
1711  stamp(m_outevents);
1712 
1713  } // endif: event list has not yet been saved and disposed
1714 
1715  // Return
1716  return;
1717 }
1718 
1719 
1720 /***********************************************************************//**
1721  * @brief Save event list(s) in XML format.
1722  *
1723  * Save the event list(s) into FITS files and write the file path information
1724  * into a XML file. The filename of the XML file is specified by the outfile
1725  * parameter, the filename(s) of the event lists are built by prepending a
1726  * prefix to the input event list filenames. Any path present in the input
1727  * filename will be stripped, i.e. the event list(s) will be written in the
1728  * local working directory (unless a path is specified in the prefix).
1729  ***************************************************************************/
1731 {
1732  // Get output filename, prefix and start index
1733  m_outevents = (*this)["outevents"].filename();
1734 
1735  // Issue warning if output filename has no .xml suffix
1736  log_string(TERSE, warn_xml_suffix(m_outevents));
1737 
1738  // Save only if event lists have not yet been saved and disposed
1739  if (!m_save_and_dispose) {
1740 
1741  // Loop over all observation in the container
1742  for (int i = 0; i < m_obs.size(); ++i) {
1743 
1744  // Get CTA observation
1745  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
1746 
1747  // Handle only CTA observations
1748  if (obs != NULL) {
1749 
1750  // Continue only if there is an event list (it may have been disposed)
1751  if (obs->events()->size() != 0) {
1752 
1753  // Set event output file name
1754  std::string outfile = this->outfile(i);
1755 
1756  // Store output file name in observation
1757  obs->eventfile(outfile);
1758 
1759  // Log filename
1760  log_value(NORMAL, "Event list file", outfile);
1761 
1762  // Save observation into FITS file
1763  obs->save(outfile, clobber());
1764 
1765  // Stamp FITS file
1766  stamp(outfile);
1767 
1768  }
1769 
1770  } // endif: observation was a CTA observations
1771 
1772  } // endfor: looped over observations
1773 
1774  } // endif: event list has not yet been saved and disposed
1775 
1776  // Save observations in XML file
1777  m_obs.save(m_outevents);
1778 
1779  // Return
1780  return;
1781 }
1782 
1783 
1784 /***********************************************************************//**
1785  * @brief Return output filename
1786  *
1787  * @param[in] index Observation index
1788  * @return Output filename
1789  *
1790  * Return output filename for observation with @p index.
1791  ***************************************************************************/
1792 std::string ctobssim::outfile(const int& index)
1793 {
1794  // Initialise output filename
1795  std::string outfile;
1796 
1797  // If multiple observations are handled then build the filename from
1798  // prefix and observation index plus startindex. The format is
1799  // [prefix]NNNNNN.fits, where NNNNNN is a 6-digit integer
1800  if (m_use_xml) {
1801 
1802  // Get prefix and start index
1803  m_prefix = (*this)["prefix"].string();
1804  m_startindex = (*this)["startindex"].integer();
1805 
1806  // Build filename
1807  char buffer[256];
1808  std::sprintf(buffer, "%s%6.6d.fits", m_prefix.c_str(),
1809  index + m_startindex);
1810 
1811  // Set output filename
1812  outfile = std::string(buffer);
1813 
1814  }
1815 
1816  // ... otherwise use the outfile parameter
1817  else {
1818 
1819  // Get output event list file name
1820  m_outevents = (*this)["outevents"].filename();
1821 
1822  // Set output filename
1823  outfile = std::string(m_outevents);
1824  }
1825 
1826  // Return
1827  return outfile;
1828 }
std::string m_outevents
Output events file.
Definition: ctobssim.hpp:142
void save(void)
Save the selected event list(s)
Definition: ctobssim.cpp:451
int m_startindex
Start index for multiple event lists.
Definition: ctobssim.hpp:144
bool m_apply_edisp
Apply energy dispersion?
Definition: ctobssim.hpp:147
void setup_observations(GObservations &obs, const bool &response=true, const bool &list=true, const bool &cube=true)
Setup observation container.
Definition: ctool.cpp:431
virtual ~ctobssim(void)
Destructor.
Definition: ctobssim.cpp:147
#define G_GET_AREA
Definition: ctobssim.cpp:50
void process(void)
Process the ctobssim tool.
Definition: ctobssim.cpp:235
#define G_SIMULATE_SOURCE
Definition: ctobssim.cpp:44
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.
void require_inobs_nocube(const std::string &method)
Throws exception if inobs parameter is a counts cube.
Definition: ctool.cpp:1149
std::string m_prefix
Prefix for multiple event lists.
Definition: ctobssim.hpp:143
void clear(void)
Clear ctobssim tool.
Definition: ctobssim.cpp:206
std::vector< GRan > m_rans
Random number generators.
Definition: ctobssim.hpp:154
#define CTOBSSIM_NAME
Definition: ctobssim.hpp:36
double get_model_flux(const GModelSky *model, const GEnergy &emin, const GEnergy &emax, const GSkyDir &centre, const double &radius, const int &indent, GLog *wrklog)
Determine sky model flux.
Definition: ctobssim.cpp:1350
void save_fits(void)
Save event list in FITS format.
Definition: ctobssim.cpp:1692
double get_area(GCTAObservation *obs, const GEnergy &emin, const GEnergy &emax) const
Get simulation area (cm^2)
Definition: ctobssim.cpp:1303
std::string outfile(const int &index)
Return output filename.
Definition: ctobssim.cpp:1792
Observation simulator tool definition.
GEbounds get_ebounds(void)
Return energy boundaries from User parameters.
Definition: ctool.cpp:1378
GModels m_models
Optionally provided models.
Definition: ctobssim.hpp:151
Observation simulator tool.
Definition: ctobssim.hpp:50
bool m_save_and_dispose
Save and dispose immediately.
Definition: ctobssim.hpp:152
void get_parameters(void)
Get application parameters.
Definition: ctobssim.cpp:611
void log_parameters(const GChatter &chatter)
Log application parameters.
Definition: ctool.cpp:1208
int m_event_id
Event identifier.
Definition: ctobssim.hpp:155
Base class for observation tools.
void publish(const std::string &name="")
Publish event lists.
Definition: ctobssim.cpp:476
void simulate_source(GCTAObservation *obs, const GModels &models, GRan &ran, GLog *wrklog=NULL)
Simulate source events from photon list.
Definition: ctobssim.cpp:748
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_GET_PARAMETERS
Definition: ctobssim.cpp:43
void simulate_interval(GCTAObservation *obs, const GCTAResponseIrf *rsp, GCTAEventList *events, const GModels &models, const GTime &tmin, const GTime &tmax, const GEnergy &etrue_min, const GEnergy &etrue_max, const GEnergy &ereco_min, const GEnergy &ereco_max, const GSkyDir &dir, const double &rad, const double &area, GRan &ran, GLog *wrklog, int &indent, std::vector< int > &nphotons, std::vector< int > &nevents)
Simulate source events for a time and energy interval.
Definition: ctobssim.cpp:969
int m_seed
Random number generator seed.
Definition: ctobssim.hpp:145
void free_members(void)
Delete class members.
Definition: ctool.cpp:357
void set_obs_bounds()
Set observation boundaries for CTA observations.
void init_members(void)
Initialise class members.
ctobssim & operator=(const ctobssim &app)
Assignment operator.
Definition: ctobssim.cpp:171
const double g_roi_margin
Simulation radius margin (degrees)
Definition: ctobssim.cpp:54
double m_max_rate
Maximum photon rate.
Definition: ctobssim.hpp:148
#define G_SIMULATE_INTERVAL
Definition: ctobssim.cpp:46
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 copy_members(const ctobssim &app)
Copy class members.
Definition: ctobssim.cpp:569
ctobssim(void)
Void constructor.
Definition: ctobssim.cpp:74
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
GObservations get_observations(const bool &get_response=true)
Get observation container.
Definition: ctool.cpp:1885
void simulate_time_slice(GCTAObservation *obs, const GCTAResponseIrf *rsp, GCTAEventList *events, const GModelSky *model, const int &mc_id, const GTime &tstart, const GTime &tstop, const GEnergy &etrue_min, const GEnergy &etrue_max, const GEnergy &ereco_min, const GEnergy &ereco_max, const GSkyDir &dir, const double &rad, const double &area, GRan &ran, GLog *wrklog, int &indent, int &nphotons, int &nevents)
Simulate source events for a time slice.
Definition: ctobssim.cpp:1169
void models(const GModels &models)
Set models for simulation.
Definition: ctobssim.hpp:195
bool m_use_xml
Use XML file instead of FITS file for observations.
Definition: ctool.hpp:162
int m_eslices
Number of energy slices.
Definition: ctobssim.hpp:146
void simulate_background(GCTAObservation *obs, const GModels &models, GRan &ran, GLog *wrklog=NULL)
Simulate background events from model.
Definition: ctobssim.cpp:1486
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.
void save_xml(void)
Save event list(s) in XML format.
Definition: ctobssim.cpp:1730
int m_max_photons
Maximum number of photons/slice.
Definition: ctobssim.hpp:153
void free_members(void)
Delete class members.
Definition: ctobssim.cpp:595
void set_mc_id_names(GCTAObservation *obs, const GModels &models, GLog *wrklog=NULL)
Set correspondance between Monte Carlo identifier and model names.
Definition: ctobssim.cpp:1611
void init_members(void)
Initialise class members.
Definition: ctobssim.cpp:537