ctools 2.1.0
Loading...
Searching...
No Matches
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 __________________________________________________________ */
54const 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
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 ***************************************************************************/
91ctobssim::ctobssim(const GObservations& obs) :
92 ctobservation(CTOBSSIM_NAME, VERSION, obs)
93{
94 // Initialise 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 ***************************************************************************/
111ctobssim::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 ***************************************************************************/
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
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 ***************************************************************************/
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 ***************************************************************************/
476void 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;
574 m_startindex = app.m_startindex;
575 m_seed = app.m_seed;
576 m_eslices = app.m_eslices;
577 m_apply_edisp = app.m_apply_edisp;
578 m_max_rate = app.m_max_rate;
579
580 // Copy protected members
581 m_models = app.m_models;
582 m_save_and_dispose = app.m_save_and_dispose;
583 m_max_photons = app.m_max_photons;
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
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 ***************************************************************************/
748void 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 ***************************************************************************/
969void 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 ***************************************************************************/
1169void 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 ***************************************************************************/
1280GEbounds 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 ***************************************************************************/
1303double 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 ***************************************************************************/
1350double 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 ***************************************************************************/
1486void 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 ***************************************************************************/
1611void 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 ***************************************************************************/
1792std::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}
Base class for observation tools.
void free_members(void)
Delete class members.
ctobservation & operator=(const ctobservation &app)
Assignment operator.
GObservations m_obs
Observation container.
const GObservations & obs(void) const
Return observation container.
void set_obs_bounds()
Set observation boundaries for CTA observations.
void init_members(void)
Initialise class members.
Observation simulator tool.
Definition ctobssim.hpp:50
bool m_apply_edisp
Apply energy dispersion?
Definition ctobssim.hpp:147
void save(void)
Save the selected event list(s)
Definition ctobssim.cpp:451
std::string outfile(const int &index)
Return output filename.
void clear(void)
Clear ctobssim tool.
Definition ctobssim.cpp:206
int m_startindex
Start index for multiple event lists.
Definition ctobssim.hpp:144
void process(void)
Process the ctobssim tool.
Definition ctobssim.cpp:235
void init_members(void)
Initialise class members.
Definition ctobssim.cpp:537
void simulate_background(GCTAObservation *obs, const GModels &models, GRan &ran, GLog *wrklog=NULL)
Simulate background events from model.
std::vector< GRan > m_rans
Random number generators.
Definition ctobssim.hpp:154
void publish(const std::string &name="")
Publish event lists.
Definition ctobssim.cpp:476
void free_members(void)
Delete class members.
Definition ctobssim.cpp:595
void simulate_source(GCTAObservation *obs, const GModels &models, GRan &ran, GLog *wrklog=NULL)
Simulate source events from photon list.
Definition ctobssim.cpp:748
void set_mc_id_names(GCTAObservation *obs, const GModels &models, GLog *wrklog=NULL)
Set correspondance between Monte Carlo identifier and model names.
double m_max_rate
Maximum photon rate.
Definition ctobssim.hpp:148
std::string m_prefix
Prefix for multiple event lists.
Definition ctobssim.hpp:143
virtual ~ctobssim(void)
Destructor.
Definition ctobssim.cpp:147
void save_xml(void)
Save event list(s) in XML format.
double get_area(GCTAObservation *obs, const GEnergy &emin, const GEnergy &emax) const
Get simulation area (cm^2)
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 get_parameters(void)
Get application parameters.
Definition ctobssim.cpp:611
bool m_save_and_dispose
Save and dispose immediately.
Definition ctobssim.hpp:152
void save_fits(void)
Save event list in FITS format.
ctobssim & operator=(const ctobssim &app)
Assignment operator.
Definition ctobssim.cpp:171
int m_event_id
Event identifier.
Definition ctobssim.hpp:155
int m_max_photons
Maximum number of photons/slice.
Definition ctobssim.hpp:153
GModels m_models
Optionally provided models.
Definition ctobssim.hpp:151
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.
std::string m_outevents
Output events file.
Definition ctobssim.hpp:142
void copy_members(const ctobssim &app)
Copy class members.
Definition ctobssim.cpp:569
ctobssim(void)
Void constructor.
Definition ctobssim.cpp:74
void models(const GModels &models)
Set models for simulation.
Definition ctobssim.hpp:195
int m_eslices
Number of energy slices.
Definition ctobssim.hpp:146
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.
void require_inobs_nocube(const std::string &method)
Throws exception if inobs parameter is a counts cube.
Definition ctool.cpp:1149
bool m_use_xml
Use XML file instead of FITS file for observations.
Definition ctool.hpp:162
void restore_edisp(GObservations &obs, const std::vector< bool > &edisp) const
Restore energy dispersion flags of CTA observations.
Definition ctool.cpp:1689
void free_members(void)
Delete class members.
Definition ctool.cpp:357
void log_observations(const GChatter &chatter, const GObservations &obs, const std::string &what="Observation")
Log observation container.
Definition ctool.cpp:1251
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
const bool & read_ahead(void) const
Signal whether parameters should be read ahead.
Definition ctool.hpp:177
void log_parameters(const GChatter &chatter)
Log application parameters.
Definition ctool.cpp:1208
void init_members(void)
Initialise class members.
Definition ctool.cpp:321
void setup_observations(GObservations &obs, const bool &response=true, const bool &list=true, const bool &cube=true)
Setup observation container.
Definition ctool.cpp:431
std::string warn_xml_suffix(const GFilename &filename) const
Set warning string if file has no .xml suffix.
Definition ctool.cpp:2427
std::string get_obs_header(const GObservation *obs) const
Return observation header string.
Definition ctool.cpp:2090
GEbounds get_ebounds(void)
Return energy boundaries from User parameters.
Definition ctool.cpp:1378
#define G_GET_PARAMETERS
Definition ctbin.cpp:42
#define G_SIMULATE_SOURCE
Definition ctobssim.cpp:44
const double g_roi_margin
Simulation radius margin (degrees)
Definition ctobssim.cpp:54
#define G_SIMULATE_INTERVAL
Definition ctobssim.cpp:46
#define G_GET_AREA
Definition ctobssim.cpp:50
Observation simulator tool definition.
#define CTOBSSIM_NAME
Definition ctobssim.hpp:36