Commit f1ce5f252cfe20e6b3ab286362f4784b98a43831

Authored by Jürgen Knödlseder
1 parent 58ac71c8

Introduce energy slices in ctobssim to accelerate computations, in particular at low energies.

ChangeLog
1 -2016-02-11 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu> 1 +2016-02-17 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu>
2 2
3 * Version 1.1.0 released 3 * Version 1.1.0 released
4 ======================== 4 ========================
5 5
  6 + Subdivide simulation energy range in ctobssim (#1673)
6 Delete existing events before simulation in ctobssim (#1663) 7 Delete existing events before simulation in ctobssim (#1663)
7 Add csiactcopy script (#1661) 8 Add csiactcopy script (#1661)
8 ctselect allows usage of non-default events and GTI extensions (#1598) 9 ctselect allows usage of non-default events and GTI extensions (#1598)
@@ -2,7 +2,7 @@ New Features and Important Changes in ctools 1.1.0 @@ -2,7 +2,7 @@ New Features and Important Changes in ctools 1.1.0
2 2
3 Author(s) : Juergen Knoedlseder 3 Author(s) : Juergen Knoedlseder
4 4
5 -11 February 2016 5 +17 February 2016
6 6
7 7
8 1. Introduction 8 1. Introduction
@@ -28,6 +28,16 @@ None @@ -28,6 +28,16 @@ None
28 28
29 5. ctobssim - CTA observation simulation 29 5. ctobssim - CTA observation simulation
30 ---------------------------------------- 30 ----------------------------------------
  31 +The simulation energy range is subdivided internally to avoid having a too
  32 +large difference between simulation area and effective area. Before, there
  33 +was a simulation area of 19634954 m^2 that was applied to all energies, which
  34 +led to the simulation of a huge number of low energy photons that were thrown
  35 +away later due to the small effective area. Now, the energy range is subdivided
  36 +(controlled using the "eslices" parameter), and for each energy range, the
  37 +simulation area is computed from the maximum effective area in that energy
  38 +range multiplied by a security factor of 2. This leads to a much more efficient
  39 +simulation of low-energy events (#1673).
  40 +
31 Any existing events in an event list are now removed before starting the 41 Any existing events in an event list are now removed before starting the
32 simulation to enable providing an existing event file on input from which 42 simulation to enable providing an existing event file on input from which
33 only the observation attributes are extracted (for example GTI, ROI, energy 43 only the observation attributes are extracted (for example GTI, ROI, energy
src/ctobssim/ctobssim.cpp
@@ -29,6 +29,7 @@ @@ -29,6 +29,7 @@
29 #include <config.h> 29 #include <config.h>
30 #endif 30 #endif
31 #include <cstdio> 31 #include <cstdio>
  32 +#include <typeinfo>
32 #include "ctobssim.hpp" 33 #include "ctobssim.hpp"
33 #include "GTools.hpp" 34 #include "GTools.hpp"
34 35
@@ -36,6 +37,12 @@ @@ -36,6 +37,12 @@
36 #define G_GET_PARAMETERS "ctobssim::get_parameters()" 37 #define G_GET_PARAMETERS "ctobssim::get_parameters()"
37 #define G_SIMULATE_SOURCE "ctobssim::simulate_source(GCTAObservation*, "\ 38 #define G_SIMULATE_SOURCE "ctobssim::simulate_source(GCTAObservation*, "\
38 "GModels&, GRan&, GLog*)" 39 "GModels&, GRan&, GLog*)"
  40 +#define G_SIMULATE_INTERVAL "ctobssim::simulate_interval(GCTAObservation*"\
  41 + ", GCTAEventList*, GCTAResponseIrf*, GModels&, GTime&"\
  42 + ", GTime&, GEnergy&, GEnergy&, GSkyDir&, double&"\
  43 + ", double&, GRan&, GLog*, int&)"
  44 +#define G_GET_AREA "ctobssim::get_area(GCTAObservation* obs, GEnergy&, "\
  45 + "GEnergy&)"
39 46
40 /* __ Constants __________________________________________________________ */ 47 /* __ Constants __________________________________________________________ */
41 const double g_roi_margin = 0.5; //!< Simulation radius margin (degrees) 48 const double g_roi_margin = 0.5; //!< Simulation radius margin (degrees)
@@ -382,7 +389,7 @@ void ctobssim::run(void) @@ -382,7 +389,7 @@ void ctobssim::run(void)
382 simulate_background(&obs_clone, models, m_rans[i], &wrklog); 389 simulate_background(&obs_clone, models, m_rans[i], &wrklog);
383 390
384 // Dump simulation results 391 // Dump simulation results
385 - if (logNormal()) { 392 + if (logTerse()) {
386 wrklog << gammalib::parformat("MC events"); 393 wrklog << gammalib::parformat("MC events");
387 wrklog << obs_clone.events()->size() - events_before; 394 wrklog << obs_clone.events()->size() - events_before;
388 wrklog << " (all models)"; 395 wrklog << " (all models)";
@@ -512,6 +519,7 @@ void ctobssim::init_members(void) @@ -512,6 +519,7 @@ void ctobssim::init_members(void)
512 m_outevents.clear(); 519 m_outevents.clear();
513 m_prefix.clear(); 520 m_prefix.clear();
514 m_seed = 1; 521 m_seed = 1;
  522 + m_eslices = 10;
515 m_apply_edisp = false; 523 m_apply_edisp = false;
516 m_max_rate = 1.0e6; 524 m_max_rate = 1.0e6;
517 525
@@ -521,8 +529,7 @@ void ctobssim::init_members(void) @@ -521,8 +529,7 @@ void ctobssim::init_members(void)
521 m_save_and_dispose = false; 529 m_save_and_dispose = false;
522 530
523 // Set fixed parameters 531 // Set fixed parameters
524 - m_area = 19634954.0 * 1.0e4; //!< pi*(2500^2) m^2  
525 - m_max_photons = 1000000; //!< Maximum number of photons / time slice 532 + m_max_photons = 1000000; //!< Maximum number of photons / time slice
526 533
527 // Initialise first event identifier 534 // Initialise first event identifier
528 m_event_id = 1; 535 m_event_id = 1;
@@ -543,11 +550,11 @@ void ctobssim::copy_members(const ctobssim&amp; app) @@ -543,11 +550,11 @@ void ctobssim::copy_members(const ctobssim&amp; app)
543 m_outevents = app.m_outevents; 550 m_outevents = app.m_outevents;
544 m_prefix = app.m_prefix; 551 m_prefix = app.m_prefix;
545 m_seed = app.m_seed; 552 m_seed = app.m_seed;
  553 + m_eslices = app.m_eslices;
546 m_apply_edisp = app.m_apply_edisp; 554 m_apply_edisp = app.m_apply_edisp;
547 m_max_rate = app.m_max_rate; 555 m_max_rate = app.m_max_rate;
548 556
549 // Copy protected members 557 // Copy protected members
550 - m_area = app.m_area;  
551 m_max_photons = app.m_max_photons; 558 m_max_photons = app.m_max_photons;
552 m_rans = app.m_rans; 559 m_rans = app.m_rans;
553 m_obs = app.m_obs; 560 m_obs = app.m_obs;
@@ -608,6 +615,7 @@ void ctobssim::get_parameters(void) @@ -608,6 +615,7 @@ void ctobssim::get_parameters(void)
608 615
609 // Get other parameters 616 // Get other parameters
610 m_seed = (*this)["seed"].integer(); 617 m_seed = (*this)["seed"].integer();
  618 + m_eslices = (*this)["eslices"].integer();
611 m_apply_edisp = (*this)["edisp"].boolean(); 619 m_apply_edisp = (*this)["edisp"].boolean();
612 m_max_rate = (*this)["maxrate"].real(); 620 m_max_rate = (*this)["maxrate"].real();
613 621
@@ -673,17 +681,29 @@ void ctobssim::get_parameters(void) @@ -673,17 +681,29 @@ void ctobssim::get_parameters(void)
673 * 681 *
674 * @param[in] obs Pointer on CTA observation. 682 * @param[in] obs Pointer on CTA observation.
675 * @param[in] models Model list. 683 * @param[in] models Model list.
676 - * @param[in] ran Random number generator. 684 + * @param[in,out] ran Random number generator.
677 * @param[in] wrklog Pointer to logger. 685 * @param[in] wrklog Pointer to logger.
678 * 686 *
679 - * Simulate source events from a photon list for a given CTA observation.  
680 - * The events are stored in as event list in the observation. 687 + * Simulate source events from a photon list for a given CTA observation and
  688 + * all source models. The events are stored in form of an event list in the
  689 + * observation.
681 * 690 *
682 - * This method does nothing if the observation pointer is NULL. It also  
683 - * verifies if the observation has a valid pointing and response. 691 + * The method will loop over all Good Time Intervals (GTI) to perform the
  692 + * simulations. Within a given GTI the requested energy range is split into
  693 + * a number of energy slices so that the simulation area can be adapted to
  694 + * the effective area of the instrument (this avoids simulating a large
  695 + * number of sources photons at low energies while accepting only few of
  696 + * them do to the small effective area). In case that the flux of a source
  697 + * is large, the simulation may be done within time slices so that the
  698 + * memory requirements won't get too large.
  699 + *
  700 + * This method does nothing if the observation pointer is NULL. It verifies
  701 + * if the observation has a CTA IRF response.
684 ***************************************************************************/ 702 ***************************************************************************/
685 -void ctobssim::simulate_source(GCTAObservation* obs, const GModels& models,  
686 - GRan& ran, GLog* wrklog) 703 +void ctobssim::simulate_source(GCTAObservation* obs,
  704 + const GModels& models,
  705 + GRan& ran,
  706 + GLog* wrklog)
687 { 707 {
688 // Continue only if observation pointer is valid 708 // Continue only if observation pointer is valid
689 if (obs != NULL) { 709 if (obs != NULL) {
@@ -693,36 +713,42 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models, @@ -693,36 +713,42 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models,
693 wrklog = &log; 713 wrklog = &log;
694 } 714 }
695 715
  716 + // Get pointer on event list
  717 + GCTAEventList* events = static_cast<GCTAEventList*>(obs->events());
  718 +
696 // Get CTA response 719 // Get CTA response
697 - const GCTAResponseIrf* rsp =  
698 - dynamic_cast<const GCTAResponseIrf*>(obs->response()); 720 + const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(obs->response());
699 if (rsp == NULL) { 721 if (rsp == NULL) {
700 - std::string msg = "Response is not an IRF response.\n" +  
701 - obs->response()->print(); 722 + std::string cls = std::string(typeid(obs->response()).name());
  723 + std::string msg = "Response of type \""+cls+"\" is not a CTA "
  724 + "IRF response. Please make sure that a CTA "
  725 + "IRF response is contained in the CTA "
  726 + "observation.";
702 throw GException::invalid_value(G_SIMULATE_SOURCE, msg); 727 throw GException::invalid_value(G_SIMULATE_SOURCE, msg);
703 } 728 }
704 729
705 - // Get pointer on event list (circumvent const correctness)  
706 - GCTAEventList* events =  
707 - static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events()));  
708 -  
709 - // Extract simulation region. 730 + // Extract simulation region from event list ROI
710 GSkyDir dir = events->roi().centre().dir(); 731 GSkyDir dir = events->roi().centre().dir();
711 double rad = events->roi().radius() + g_roi_margin; 732 double rad = events->roi().radius() + g_roi_margin;
712 733
713 - // Dump simulation cone information  
714 - if (logNormal()) {  
715 - *wrklog << gammalib::parformat("Simulation area");  
716 - *wrklog << m_area << " cm2" << std::endl; 734 + // Determine energy boundaries for simulation
  735 + GEbounds ebounds = get_ebounds(events->ebounds());
  736 +
  737 + // Log simulation cone information
  738 + if (logTerse()) {
717 *wrklog << gammalib::parformat("Simulation cone"); 739 *wrklog << gammalib::parformat("Simulation cone");
718 *wrklog << "RA=" << dir.ra_deg() << " deg"; 740 *wrklog << "RA=" << dir.ra_deg() << " deg";
719 *wrklog << ", Dec=" << dir.dec_deg() << " deg"; 741 *wrklog << ", Dec=" << dir.dec_deg() << " deg";
720 - *wrklog << ", r=" << rad << " deg" << std::endl; 742 + *wrklog << ", radius=" << rad << " deg" << std::endl;
721 } 743 }
722 744
723 // Initialise indentation for logging 745 // Initialise indentation for logging
724 int indent = 0; 746 int indent = 0;
725 747
  748 + // Initialise photon and event counters
  749 + std::vector<int> nphotons(models.size(),0);
  750 + std::vector<int> nevents(models.size(),0);
  751 +
726 // Loop over all Good Time Intervals 752 // Loop over all Good Time Intervals
727 for (int it = 0; it < events->gti().size(); ++it) { 753 for (int it = 0; it < events->gti().size(); ++it) {
728 754
@@ -730,7 +756,8 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models, @@ -730,7 +756,8 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models,
730 GTime tmin = events->gti().tstart(it); 756 GTime tmin = events->gti().tstart(it);
731 GTime tmax = events->gti().tstop(it); 757 GTime tmax = events->gti().tstop(it);
732 758
733 - // Dump time interval 759 + // Log time interval. Increment indentation if there are
  760 + // several Good Time Intervals.
734 if (logNormal()) { 761 if (logNormal()) {
735 if (events->gti().size() > 1) { 762 if (events->gti().size() > 1) {
736 indent++; 763 indent++;
@@ -744,14 +771,14 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models, @@ -744,14 +771,14 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models,
744 } 771 }
745 772
746 // Loop over all energy boundaries 773 // Loop over all energy boundaries
747 - for (int ie = 0; ie < events->ebounds().size(); ++ie) { 774 + for (int ie = 0; ie < ebounds.size(); ++ie) {
748 775
749 - // Extract energy boundaries  
750 - GEnergy emin = events->ebounds().emin(ie);  
751 - GEnergy emax = events->ebounds().emax(ie); 776 + // Set energy interval
  777 + GEnergy emin = ebounds.emin(ie);
  778 + GEnergy emax = ebounds.emax(ie);
752 779
753 - // Set true photon energy limits for simulation. If observation  
754 - // has energy dispersion then add margin 780 + // Set true photon energy limits for simulation. If the
  781 + // observation has energy dispersion then add a margin.
755 GEnergy e_true_min = emin; 782 GEnergy e_true_min = emin;
756 GEnergy e_true_max = emax; 783 GEnergy e_true_max = emax;
757 if (rsp->use_edisp()) { 784 if (rsp->use_edisp()) {
@@ -759,210 +786,51 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models, @@ -759,210 +786,51 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models,
759 e_true_max = rsp->ebounds(e_true_max).emax(); 786 e_true_max = rsp->ebounds(e_true_max).emax();
760 } 787 }
761 788
762 - // Dump energy range 789 + // Determine simulation area
  790 + double area = get_area(obs, emin, emax);
  791 +
  792 + // Log energy range and simulation area
763 if (logNormal()) { 793 if (logNormal()) {
764 - if (events->ebounds().size() > 1) {  
765 - indent++;  
766 - wrklog->indent(indent);  
767 - }  
768 *wrklog << gammalib::parformat("Photon energy range", indent); 794 *wrklog << gammalib::parformat("Photon energy range", indent);
769 *wrklog << e_true_min << " - " << e_true_max << std::endl; 795 *wrklog << e_true_min << " - " << e_true_max << std::endl;
770 *wrklog << gammalib::parformat("Event energy range", indent); 796 *wrklog << gammalib::parformat("Event energy range", indent);
771 *wrklog << emin << " - " << emax << std::endl; 797 *wrklog << emin << " - " << emax << std::endl;
772 } 798 }
773 799
774 - // Loop over all sky models  
775 - for (int i = 0; i < models.size(); ++i) {  
776 -  
777 - // Get sky model (NULL if not a sky model)  
778 - const GModelSky* model =  
779 - dynamic_cast<const GModelSky*>(models[i]);  
780 -  
781 - // If we have a sky model that applies to the present  
782 - // observation then simulate events  
783 - if (model != NULL &&  
784 - model->is_valid(obs->instrument(), obs->id())) {  
785 -  
786 - // Determine duration of a time slice by limiting the  
787 - // number of simulated photons to m_max_photons.  
788 - // The photon rate is estimated from the model flux  
789 - // and used to set the duration of the time slice.  
790 - double flux = get_model_flux(model, emin, emax,  
791 - dir, rad,  
792 - indent, wrklog);  
793 - double rate = flux * m_area;  
794 - double duration = 1800.0; // default: 1800 sec  
795 - if (rate > 0.0) {  
796 - duration = m_max_photons / rate;  
797 - if (duration < 1.0) { // not <1 sec  
798 - duration = 1.0;  
799 - }  
800 - else if (duration > 180000.0) { // not >50 hr  
801 - duration = 180000.0;  
802 - }  
803 - }  
804 - GTime tslice(duration, "sec");  
805 -  
806 - // Skip model if photon rate is 0  
807 - if (rate <= 0.0) {  
808 - continue;  
809 - }  
810 -  
811 - // Dump photon rate  
812 - if (logNormal()) {  
813 - *wrklog << gammalib::parformat("Photon rate", indent);  
814 - *wrklog << rate << " photons/s";  
815 - if (model->name().length() > 0) {  
816 - *wrklog << " [" << model->name() << "]";  
817 - }  
818 - *wrklog << std::endl;  
819 - } 800 + // Increment indentation if there are several energy
  801 + // boundaries
  802 + if (logNormal()) {
  803 + if (ebounds.size() > 1) {
  804 + indent++;
  805 + wrklog->indent(indent);
  806 + }
  807 + }
820 808
821 - // If photon rate exceeds the maximum photon rate  
822 - // then throw an exception  
823 - if (rate > m_max_rate) {  
824 - std::string modnam = (model->name().length() > 0) ?  
825 - model->name() : "Unknown";  
826 - std::string msg = "Photon rate "+  
827 - gammalib::str(rate)+  
828 - " photons/s for model \""+  
829 - modnam+"\" exceeds maximum"  
830 - " allowed photon rate of "+  
831 - gammalib::str(m_max_rate)+  
832 - " photons/s. Please check"  
833 - " the model parameters for"  
834 - " model \""+modnam+"\" or"  
835 - " increase the value of the"  
836 - " hidden \"maxrate\""  
837 - " parameter.";  
838 - throw GException::invalid_value(G_SIMULATE_SOURCE, msg);  
839 - } 809 + // Log simulation area
  810 + if (logNormal()) {
  811 + *wrklog << gammalib::parformat("Simulation area", indent);
  812 + *wrklog << area << " cm2" << std::endl;
  813 + }
840 814
841 - // To reduce memory requirements we split long time  
842 - // intervals into several slices.  
843 - GTime tstart = tmin;  
844 - GTime tstop = tstart + tslice;  
845 -  
846 - // Initialise cumulative photon counters  
847 - int nphotons = 0;  
848 -  
849 - // Loop over time slices  
850 - while (tstart < tmax) {  
851 -  
852 - // Make sure that tstop <= tmax  
853 - if (tstop > tmax) {  
854 - tstop = tmax;  
855 - }  
856 -  
857 - // Dump time slice  
858 - if (logExplicit()) {  
859 - if (tmax - tmin > tslice) {  
860 - indent++;  
861 - wrklog->indent(indent);  
862 - }  
863 - *wrklog << gammalib::parformat("Time slice", indent);  
864 - *wrklog << tstart.convert(m_cta_ref);  
865 - *wrklog << " - ";  
866 - *wrklog << tstop.convert(m_cta_ref);  
867 - *wrklog << " s";  
868 - if (model->name().length() > 0) {  
869 - *wrklog << " [" << model->name() << "]";  
870 - }  
871 - *wrklog << std::endl;  
872 - }  
873 -  
874 - // Get photons  
875 - GPhotons photons = model->mc(m_area, dir, rad,  
876 - e_true_min, e_true_max,  
877 - tstart, tstop, ran);  
878 -  
879 - // Dump number of simulated photons  
880 - if (logExplicit()) {  
881 - *wrklog << gammalib::parformat("MC source photons/slice", indent);  
882 - *wrklog << photons.size();  
883 - if (model->name().length() > 0) {  
884 - *wrklog << " [" << model->name() << "]";  
885 - }  
886 - *wrklog << std::endl;  
887 - }  
888 -  
889 - // Simulate events from photons  
890 - for (int i = 0; i < photons.size(); ++i) {  
891 -  
892 - // Increment photon counter  
893 - nphotons++;  
894 -  
895 - // Simulate event. Note that this method  
896 - // includes the deadtime correction.  
897 - GCTAEventAtom* event = rsp->mc(m_area,  
898 - photons[i],  
899 - *obs,  
900 - ran);  
901 -  
902 - if (event != NULL) {  
903 -  
904 - // Use event only if it falls within ROI  
905 - // energy boundary and time slice  
906 - if (events->roi().contains(*event) &&  
907 - event->energy() >= emin &&  
908 - event->energy() <= emax &&  
909 - event->time() >= tstart &&  
910 - event->time() <= tstop) {  
911 - event->event_id(m_event_id);  
912 - events->append(*event);  
913 - m_event_id++;  
914 - }  
915 - delete event;  
916 - }  
917 -  
918 - } // endfor: looped over events  
919 -  
920 - // Go to next time slice  
921 - tstart = tstop;  
922 - tstop = tstart + tslice;  
923 -  
924 - // Reset indentation  
925 - if (logExplicit()) {  
926 - if (tmax - tmin > tslice) {  
927 - indent--;  
928 - wrklog->indent(indent);  
929 - }  
930 - }  
931 -  
932 - } // endwhile: looped over time slices  
933 -  
934 - // Dump simulation results  
935 - if (logNormal()) {  
936 - *wrklog << gammalib::parformat("MC source photons", indent);  
937 - *wrklog << nphotons;  
938 - if (model->name().length() > 0) {  
939 - *wrklog << " [" << model->name() << "]";  
940 - }  
941 - *wrklog << std::endl;  
942 - *wrklog << gammalib::parformat("MC source events", indent);  
943 - *wrklog << events->size();  
944 - if (model->name().length() > 0) {  
945 - *wrklog << " [" << model->name() << "]";  
946 - }  
947 - *wrklog << std::endl; 815 + // Save state of event counter before doing the simulation
  816 + int nevents_before = events->size();
948 817
949 - }  
950 -  
951 - } // endif: model was a sky model 818 + // Simulate events for this time and energy interval
  819 + simulate_interval(obs, rsp, events, models, tmin, tmax,
  820 + e_true_min, e_true_max, dir, rad, area,
  821 + ran, wrklog, indent, nphotons, nevents);
952 822
953 - } // endfor: looped over models  
954 -  
955 - // Dump simulation results 823 + // Log simulation results
956 if (logNormal()) { 824 if (logNormal()) {
957 *wrklog << gammalib::parformat("MC source events", indent); 825 *wrklog << gammalib::parformat("MC source events", indent);
958 - *wrklog << events->size(); 826 + *wrklog << events->size() - nevents_before;
959 *wrklog << " (all source models)"; 827 *wrklog << " (all source models)";
960 *wrklog << std::endl; 828 *wrklog << std::endl;
961 } 829 }
962 830
963 - // Reset indentation 831 + // Reset indentation if there were several energy boundaries
964 if (logNormal()) { 832 if (logNormal()) {
965 - if (events->ebounds().size() > 1) { 833 + if (ebounds.size() > 1) {
966 indent--; 834 indent--;
967 wrklog->indent(indent); 835 wrklog->indent(indent);
968 836
@@ -971,7 +839,7 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models, @@ -971,7 +839,7 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models,
971 839
972 } // endfor: looped over all energy boundaries 840 } // endfor: looped over all energy boundaries
973 841
974 - // Reset indentation 842 + // Reset indentation if there were several Good Time Intervals
975 if (logNormal()) { 843 if (logNormal()) {
976 if (events->gti().size() > 1) { 844 if (events->gti().size() > 1) {
977 indent--; 845 indent--;
@@ -984,6 +852,31 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models, @@ -984,6 +852,31 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models,
984 // Reset indentation 852 // Reset indentation
985 wrklog->indent(0); 853 wrklog->indent(0);
986 854
  855 + // Log simulation summary
  856 + if (logTerse()) {
  857 + for (int i = 0; i < models.size(); ++i) {
  858 + const GModelSky* model = dynamic_cast<const GModelSky*>(models[i]);
  859 + if (model == NULL) {
  860 + continue;
  861 + }
  862 + if (!model->is_valid(obs->instrument(), obs->id())) {
  863 + continue;
  864 + }
  865 + *wrklog << gammalib::parformat("MC source photons");
  866 + *wrklog << nphotons[i];
  867 + if (model->name().length() > 0) {
  868 + *wrklog << " [" << model->name() << "]";
  869 + }
  870 + *wrklog << std::endl;
  871 + *wrklog << gammalib::parformat("MC source events");
  872 + *wrklog << nevents[i];
  873 + if (model->name().length() > 0) {
  874 + *wrklog << " [" << model->name() << "]";
  875 + }
  876 + *wrklog << std::endl;
  877 + }
  878 + }
  879 +
987 } // endif: observation pointer was valid 880 } // endif: observation pointer was valid
988 881
989 // Return 882 // Return
@@ -992,6 +885,335 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models, @@ -992,6 +885,335 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models,
992 885
993 886
994 /***********************************************************************//** 887 /***********************************************************************//**
  888 + * @brief Simulate source events for a time and energy interval
  889 + *
  890 + * @param[in] obs Pointer on CTA observation.
  891 + * @param[in] rsp Pointer on CTA IRF response.
  892 + * @param[in,out] events Pointer on CTA event list.
  893 + * @param[in] models Model list.
  894 + * @param[in] tmin Start time.
  895 + * @param[in] tmax Stop time.
  896 + * @param[in] emin Minimum energy.
  897 + * @param[in] emax Maximum energy.
  898 + * @param[in] dir Simulation cone centre.
  899 + * @param[in] rad Simulation cone radius (degrees).
  900 + * @param[in] area Simulation area (cm^2).
  901 + * @param[in,out] ran Random number generator.
  902 + * @param[in] wrklog Pointer to logger.
  903 + * @param[in,out] indent Logger indent.
  904 + * @param[in,out] nphotons Number of photons for all models.
  905 + * @param[in,out] nevents Number of events for all models.
  906 + *
  907 + * Simulate source events for a time and energy interval.
  908 + ***************************************************************************/
  909 +void ctobssim::simulate_interval(GCTAObservation* obs,
  910 + const GCTAResponseIrf* rsp,
  911 + GCTAEventList* events,
  912 + const GModels& models,
  913 + const GTime& tmin,
  914 + const GTime& tmax,
  915 + const GEnergy& emin,
  916 + const GEnergy& emax,
  917 + const GSkyDir& dir,
  918 + const double& rad,
  919 + const double& area,
  920 + GRan& ran,
  921 + GLog* wrklog,
  922 + int& indent,
  923 + std::vector<int>& nphotons,
  924 + std::vector<int>& nevents)
  925 +{
  926 + // Loop over all models
  927 + for (int i = 0; i < models.size(); ++i) {
  928 +
  929 + // Get sky model (NULL if not a sky model)
  930 + const GModelSky* model = dynamic_cast<const GModelSky*>(models[i]);
  931 +
  932 + // If the model is not a sky model then skip the model
  933 + if (model == NULL) {
  934 + continue;
  935 + }
  936 +
  937 + // If the model does not apply to the instrument and observation
  938 + // identifier then skip the model
  939 + if (!model->is_valid(obs->instrument(), obs->id())) {
  940 + continue;
  941 + }
  942 +
  943 + // Determine duration of a time slice by limiting the number of
  944 + // simulated photons to m_max_photons. The photon rate is estimated
  945 + // from the model flux and used to set the duration of the time
  946 + // slice.
  947 + double flux = get_model_flux(model, emin, emax, dir, rad,
  948 + indent, wrklog);
  949 + double rate = flux * area;
  950 + double duration = 1800.0; // default: 1800 sec
  951 + if (rate > 0.0) {
  952 + duration = m_max_photons / rate;
  953 + if (duration < 1.0) { // not <1 sec
  954 + duration = 1.0;
  955 + }
  956 + else if (duration > 180000.0) { // not >50 hr
  957 + duration = 180000.0;
  958 + }
  959 + }
  960 + GTime tslice(duration, "sec");
  961 +
  962 + // Skip model if photon rate is 0
  963 + if (rate <= 0.0) {
  964 + continue;
  965 + }
  966 +
  967 + // Log photon rate
  968 + if (logNormal()) {
  969 + *wrklog << gammalib::parformat("Photon rate", indent);
  970 + *wrklog << rate << " photons/s";
  971 + if (model->name().length() > 0) {
  972 + *wrklog << " [" << model->name() << "]";
  973 + }
  974 + *wrklog << std::endl;
  975 + }
  976 +
  977 + // If photon rate exceeds the maximum photon rate that is allowed
  978 + // then throw an exception
  979 + if (rate > m_max_rate) {
  980 + std::string mod = (model->name().length() > 0) ?
  981 + model->name() : "Unknown";
  982 + std::string msg = "Photon rate "+gammalib::str(rate)+
  983 + " photons/s for model \""+mod+"\" exceeds "
  984 + "maximum allowed photon rate of "+
  985 + gammalib::str(m_max_rate)+" photons/s. "
  986 + "Please check the model parameters for "
  987 + "model \""+mod+"\" or increase the value "
  988 + "of the \"maxrate\" parameter.";
  989 + throw GException::invalid_value(G_SIMULATE_INTERVAL, msg);
  990 + }
  991 +
  992 + // To reduce memory requirements we split long time intervals into
  993 + // several time slices
  994 + GTime tstart = tmin;
  995 + GTime tstop = tstart + tslice;
  996 +
  997 + // Save state of photon and event counters before doing the
  998 + // simulation
  999 + int nphotons_before = nphotons[i];
  1000 + int nevents_before = nevents[i];
  1001 +
  1002 + // Loop over time slices
  1003 + while (tstart < tmax) {
  1004 +
  1005 + // Make sure that tstop <= tmax
  1006 + if (tstop > tmax) {
  1007 + tstop = tmax;
  1008 + }
  1009 +
  1010 + // Log time slice
  1011 + if (logExplicit()) {
  1012 + if (tmax - tmin > tslice) {
  1013 + indent++;
  1014 + wrklog->indent(indent);
  1015 + }
  1016 + *wrklog << gammalib::parformat("Time slice", indent);
  1017 + *wrklog << tstart.convert(m_cta_ref) << " - ";
  1018 + *wrklog << tstop.convert(m_cta_ref) << " s";
  1019 + if (model->name().length() > 0) {
  1020 + *wrklog << " [" << model->name() << "]";
  1021 + }
  1022 + *wrklog << std::endl;
  1023 + }
  1024 +
  1025 + // Simulate time slice
  1026 + simulate_time_slice(obs, rsp, events, model, tstart, tstop,
  1027 + emin, emax, dir, rad, area,
  1028 + ran, wrklog, indent,
  1029 + nphotons[i], nevents[i]);
  1030 +
  1031 + // Go to next time slice
  1032 + tstart = tstop;
  1033 + tstop = tstart + tslice;
  1034 +
  1035 + // Reset indentation
  1036 + if (logExplicit()) {
  1037 + if (tmax - tmin > tslice) {
  1038 + indent--;
  1039 + wrklog->indent(indent);
  1040 + }
  1041 + }
  1042 +
  1043 + } // endwhile: looped over time slices
  1044 +
  1045 + // Log simulation results
  1046 + if (logNormal()) {
  1047 + *wrklog << gammalib::parformat("MC source photons", indent);
  1048 + *wrklog << nphotons[i] - nphotons_before;
  1049 + if (model->name().length() > 0) {
  1050 + *wrklog << " [" << model->name() << "]";
  1051 + }
  1052 + *wrklog << std::endl;
  1053 + *wrklog << gammalib::parformat("MC source events", indent);
  1054 + *wrklog << nevents[i] - nevents_before;
  1055 + if (model->name().length() > 0) {
  1056 + *wrklog << " [" << model->name() << "]";
  1057 + }
  1058 + *wrklog << std::endl;
  1059 +
  1060 + }
  1061 +
  1062 + } // endfor: looped over models
  1063 +
  1064 + // Return
  1065 + return;
  1066 +}
  1067 +
  1068 +
  1069 +/***********************************************************************//**
  1070 + * @brief Simulate source events for a time slice
  1071 + *
  1072 + * @param[in] obs Pointer on CTA observation.
  1073 + * @param[in,out] events Pointer on CTA event list.
  1074 + * @param[in] models Model list.
  1075 + * @param[in] tmin Start time.
  1076 + * @param[in] tmax Stop time.
  1077 + * @param[in] emin Minimum energy.
  1078 + * @param[in] emax Maximum energy.
  1079 + * @param[in] dir Simulation cone centre.
  1080 + * @param[in] rad Simulation cone radius (degrees).
  1081 + * @param[in] area Simulation area (cm^2).
  1082 + * @param[in,out] ran Random number generator.
  1083 + * @param[in] wrklog Pointer to logger.
  1084 + * @param[in,out] indent Logger indent.
  1085 + * @param[in,out] nphotons Number of photons.
  1086 + * @param[in,out] nevents Number of events.
  1087 + *
  1088 + * Simulate source events for a time slice.
  1089 + ***************************************************************************/
  1090 +void ctobssim::simulate_time_slice(GCTAObservation* obs,
  1091 + const GCTAResponseIrf* rsp,
  1092 + GCTAEventList* events,
  1093 + const GModelSky* model,
  1094 + const GTime& tstart,
  1095 + const GTime& tstop,
  1096 + const GEnergy& emin,
  1097 + const GEnergy& emax,
  1098 + const GSkyDir& dir,
  1099 + const double& rad,
  1100 + const double& area,
  1101 + GRan& ran,
  1102 + GLog* wrklog,
  1103 + int& indent,
  1104 + int& nphotons,
  1105 + int& nevents)
  1106 +{
  1107 + // Get photons
  1108 + GPhotons photons = model->mc(area, dir, rad, emin, emax, tstart, tstop,
  1109 + ran);
  1110 +
  1111 + // Dump number of simulated photons
  1112 + if (logExplicit()) {
  1113 + *wrklog << gammalib::parformat("MC source photons/slice", indent);
  1114 + *wrklog << photons.size();
  1115 + if (model->name().length() > 0) {
  1116 + *wrklog << " [" << model->name() << "]";
  1117 + }
  1118 + *wrklog << std::endl;
  1119 + }
  1120 +
  1121 + // Simulate events from photons
  1122 + for (int i = 0; i < photons.size(); ++i) {
  1123 +
  1124 + // Increment photon counter
  1125 + nphotons++;
  1126 +
  1127 + // Simulate event. Note that this method includes the deadtime
  1128 + // correction.
  1129 + GCTAEventAtom* event = rsp->mc(area, photons[i], *obs, ran);
  1130 +
  1131 + // Use event only if it exists and if it falls within ROI, the
  1132 + // energy interval and the time slice
  1133 + if (event != NULL) {
  1134 + if (events->roi().contains(*event) &&
  1135 + event->energy() >= emin &&
  1136 + event->energy() <= emax &&
  1137 + event->time() >= tstart &&
  1138 + event->time() <= tstop) {
  1139 + event->event_id(m_event_id);
  1140 + events->append(*event);
  1141 + m_event_id++;
  1142 + nevents++;
  1143 + }
  1144 + delete event;
  1145 + }
  1146 +
  1147 + } // endfor: looped over events
  1148 +
  1149 + // Return
  1150 + return;
  1151 +}
  1152 +
  1153 +
  1154 +/***********************************************************************//**
  1155 + * @brief Get energy boundaries
  1156 + *
  1157 + * @param[in] ebounds Energy boundaries of events.
  1158 + * @return Energy boundaries for simulation.
  1159 + *
  1160 + * Get the energy boundaries for the source simulation.
  1161 + *
  1162 + * @todo Need to take care of the (rare) case that there are distinct
  1163 + * energy boundaries in the event list. In that case we need to
  1164 + * create mixed energy boundaries.
  1165 + ***************************************************************************/
  1166 +GEbounds ctobssim::get_ebounds(const GEbounds& ebounds) const
  1167 +{
  1168 + // Set energy bins
  1169 + GEbounds ebins(m_eslices, ebounds.emin(), ebounds.emax());
  1170 +
  1171 + // Return energy bins
  1172 + return (ebins);
  1173 +}
  1174 +
  1175 +
  1176 +/***********************************************************************//**
  1177 + * @brief Get simulation area (cm^2)
  1178 + *
  1179 + * @param[in] obs Pointer on CTA observation.
  1180 + * @param[in] emin Minimum energy.
  1181 + * @param[in] emax Maximum energy.
  1182 + * @return Simulation area (cm^2).
  1183 + *
  1184 + * Get the simulation area for an energy interval in units of cm^2.
  1185 + ***************************************************************************/
  1186 +double ctobssim::get_area(GCTAObservation* obs,
  1187 + const GEnergy& emin,
  1188 + const GEnergy& emax) const
  1189 +{
  1190 + // Get CTA response
  1191 + const GCTAResponseIrf* rsp =
  1192 + dynamic_cast<const GCTAResponseIrf*>(obs->response());
  1193 + if (rsp == NULL) {
  1194 + std::string cls = std::string(typeid(rsp).name());
  1195 + std::string msg = "Response of type \""+cls+"\" is not a CTA IRF "
  1196 + "response. Please make sure that a CTA IRF "
  1197 + "response is contained in the CTA observation.";
  1198 + throw GException::invalid_value(G_GET_AREA, msg);
  1199 + }
  1200 +
  1201 + // Compute effective area at minimum and maximum energy
  1202 + double area_emin = rsp->aeff()->max(emin.log10TeV(), 0.0, 0.0);
  1203 + double area_emax = rsp->aeff()->max(emax.log10TeV(), 0.0, 0.0);
  1204 +
  1205 + // Use maximum of both
  1206 + double area = (area_emin > area_emax) ? area_emin : area_emax;
  1207 +
  1208 + // Multiply by security factor
  1209 + area *= 2.0;
  1210 +
  1211 + // Return simulation area
  1212 + return area;
  1213 +}
  1214 +
  1215 +
  1216 +/***********************************************************************//**
995 * @brief Determine sky model flux 1217 * @brief Determine sky model flux
996 * 1218 *
997 * @param[in] model Sky model. 1219 * @param[in] model Sky model.
@@ -1000,7 +1222,7 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models, @@ -1000,7 +1222,7 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models,
1000 * @param[in] centre Centre of region for photon rate determination. 1222 * @param[in] centre Centre of region for photon rate determination.
1001 * @param[in] radius Radius of region for photon rate determination (degrees). 1223 * @param[in] radius Radius of region for photon rate determination (degrees).
1002 * @param[in] indent Indent for logging. 1224 * @param[in] indent Indent for logging.
1003 - * @param[in,out] wrklog Pointer to logger (default: NULL). 1225 + * @param[in,out] wrklog Pointer to logger.
1004 * @return Model flux (photons/cm2/sec). 1226 * @return Model flux (photons/cm2/sec).
1005 ***************************************************************************/ 1227 ***************************************************************************/
1006 double ctobssim::get_model_flux(const GModelSky* model, 1228 double ctobssim::get_model_flux(const GModelSky* model,
src/ctobssim/ctobssim.hpp
1 /*************************************************************************** 1 /***************************************************************************
2 * ctobssim - Observation simulator tool * 2 * ctobssim - Observation simulator tool *
3 * ----------------------------------------------------------------------- * 3 * ----------------------------------------------------------------------- *
4 - * copyright (C) 2011-2015 by Juergen Knoedlseder * 4 + * copyright (C) 2011-2016 by Juergen Knoedlseder *
5 * ----------------------------------------------------------------------- * 5 * ----------------------------------------------------------------------- *
6 * * 6 * *
7 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -34,7 +34,7 @@ @@ -34,7 +34,7 @@
34 34
35 /* __Definitions _________________________________________________________ */ 35 /* __Definitions _________________________________________________________ */
36 #define CTOBSSIM_NAME "ctobssim" 36 #define CTOBSSIM_NAME "ctobssim"
37 -#define CTOBSSIM_VERSION "1.0.0" 37 +#define CTOBSSIM_VERSION "1.1.0"
38 38
39 39
40 /***********************************************************************//** 40 /***********************************************************************//**
@@ -71,38 +71,74 @@ public: @@ -71,38 +71,74 @@ public:
71 71
72 protected: 72 protected:
73 // Protected methods 73 // Protected methods
74 - void init_members(void);  
75 - void copy_members(const ctobssim& app);  
76 - void free_members(void);  
77 - void get_parameters(void);  
78 - void simulate_source(GCTAObservation* obs,  
79 - const GModels& models,  
80 - GRan& ran,  
81 - GLog* wrklog = NULL);  
82 - double get_model_flux(const GModelSky* model,  
83 - const GEnergy& emin,  
84 - const GEnergy& emax,  
85 - const GSkyDir& centre,  
86 - const double& radius,  
87 - const int& indent,  
88 - GLog* wrklog = NULL);  
89 - void simulate_background(GCTAObservation* obs,  
90 - const GModels& models,  
91 - GRan& ran,  
92 - GLog* wrklog = NULL);  
93 - void save_fits(void);  
94 - void save_xml(void); 74 + void init_members(void);
  75 + void copy_members(const ctobssim& app);
  76 + void free_members(void);
  77 + void get_parameters(void);
  78 + void simulate_source(GCTAObservation* obs,
  79 + const GModels& models,
  80 + GRan& ran,
  81 + GLog* wrklog = NULL);
  82 + void simulate_interval(GCTAObservation* obs,
  83 + const GCTAResponseIrf* rsp,
  84 + GCTAEventList* events,
  85 + const GModels& models,
  86 + const GTime& tmin,
  87 + const GTime& tmax,
  88 + const GEnergy& emin,
  89 + const GEnergy& emax,
  90 + const GSkyDir& dir,
  91 + const double& rad,
  92 + const double& area,
  93 + GRan& ran,
  94 + GLog* wrklog,
  95 + int& indent,
  96 + std::vector<int>& nphotons,
  97 + std::vector<int>& nevents);
  98 + void simulate_time_slice(GCTAObservation* obs,
  99 + const GCTAResponseIrf* rsp,
  100 + GCTAEventList* events,
  101 + const GModelSky* model,
  102 + const GTime& tstart,
  103 + const GTime& tstop,
  104 + const GEnergy& emin,
  105 + const GEnergy& emax,
  106 + const GSkyDir& dir,
  107 + const double& rad,
  108 + const double& area,
  109 + GRan& ran,
  110 + GLog* wrklog,
  111 + int& indent,
  112 + int& nphotons,
  113 + int& nevents);
  114 + GEbounds get_ebounds(const GEbounds& ebounds) const;
  115 + double get_area(GCTAObservation* obs,
  116 + const GEnergy& emin,
  117 + const GEnergy& emax) const;
  118 + double get_model_flux(const GModelSky* model,
  119 + const GEnergy& emin,
  120 + const GEnergy& emax,
  121 + const GSkyDir& centre,
  122 + const double& radius,
  123 + const int& indent,
  124 + GLog* wrklog);
  125 + void simulate_background(GCTAObservation* obs,
  126 + const GModels& models,
  127 + GRan& ran,
  128 + GLog* wrklog = NULL);
  129 + void save_fits(void);
  130 + void save_xml(void);
95 131
96 // User parameters 132 // User parameters
97 std::string m_outevents; //!< Output events file 133 std::string m_outevents; //!< Output events file
98 std::string m_prefix; //!< Prefix for multiple event lists 134 std::string m_prefix; //!< Prefix for multiple event lists
99 int m_seed; //!< Random number generator seed 135 int m_seed; //!< Random number generator seed
  136 + int m_eslices; //!< Number of energy slices
100 bool m_apply_edisp; //!< Apply energy dispersion? 137 bool m_apply_edisp; //!< Apply energy dispersion?
101 138
102 // Protected members 139 // Protected members
103 mutable GObservations m_obs; //!< Observation container 140 mutable GObservations m_obs; //!< Observation container
104 mutable bool m_save_and_dispose; //!< Save and dispose immediately 141 mutable bool m_save_and_dispose; //!< Save and dispose immediately
105 - double m_area; //!< Surface area for simulation (cm2)  
106 int m_max_photons; //!< Maximum number of photons/slice 142 int m_max_photons; //!< Maximum number of photons/slice
107 double m_max_rate; //!< Maximum photon rate 143 double m_max_rate; //!< Maximum photon rate
108 std::vector<GRan> m_rans; //!< Random number generators 144 std::vector<GRan> m_rans; //!< Random number generators
src/ctobssim/ctobssim.par
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 # ------------------------------------------------------------------------- # 5 # ------------------------------------------------------------------------- #
6 # # 6 # #
7 # File: ctobssim.par # 7 # File: ctobssim.par #
8 -# Version: 1.0.0 # 8 +# Version: 1.1.0 #
9 # # 9 # #
10 # Author: Jurgen Knodlseder # 10 # Author: Jurgen Knodlseder #
11 # Jurgen.Knodlseder@irap.omp.eu # 11 # Jurgen.Knodlseder@irap.omp.eu #
@@ -38,6 +38,7 @@ emin, r, a, 0.1,,, &quot;Lower energy limit (TeV)&quot; @@ -38,6 +38,7 @@ emin, r, a, 0.1,,, &quot;Lower energy limit (TeV)&quot;
38 emax, r, a, 100.0,,, "Upper energy limit (TeV)" 38 emax, r, a, 100.0,,, "Upper energy limit (TeV)"
39 deadc, r, h, 0.95,0,1, "Average deadtime correction factor" 39 deadc, r, h, 0.95,0,1, "Average deadtime correction factor"
40 maxrate, r, h, 1.0e6,,, "Maximum photon rate" 40 maxrate, r, h, 1.0e6,,, "Maximum photon rate"
  41 +eslices, i, h, 10,1,100,"Number of energy slices"
41 42
42 # 43 #
43 # Standard parameters 44 # Standard parameters