Commit 42c737c13279f5fa71d51575b2a6ab58a8e98bb9

Authored by Jürgen Knödlseder
1 parent a3805817

Reduce memory requirements for ctobssim.

The memory needs for ctobssim have been reduced by first working on a local copy of the CTA observation (this de-allocates properly and memory cache associated to the response functions), and by saving and disposing the event lists immediately if called using the execute() method (this is the method called when the tool is run from the command line).
ChangeLog
1 -2014-10-17 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu> 1 +2014-10-24 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu>
2 2
3 * Version 0.8.0 released 3 * Version 0.8.0 released
4 ======================== 4 ========================
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 Support energy dispersion in ctobssim, ctlike and ctmodel 17 Support energy dispersion in ctobssim, ctlike and ctmodel
18 Correct ctobssim for thread safety 18 Correct ctobssim for thread safety
19 Correctly apply time references in ctselect 19 Correctly apply time references in ctselect
  20 + Reduce memory usage
20 Move obsutils to ctools namespace 21 Move obsutils to ctools namespace
21 Add several functions to obsutils 22 Add several functions to obsutils
22 Enhance documentation 23 Enhance documentation
@@ -2,7 +2,7 @@ New Features and Important Changes in ctools 00-08-00 @@ -2,7 +2,7 @@ New Features and Important Changes in ctools 00-08-00
2 2
3 Author(s) : Juergen Knoedlseder 3 Author(s) : Juergen Knoedlseder
4 4
5 -17 October 2014 5 +24 October 2014
6 6
7 7
8 1. Introduction 8 1. Introduction
@@ -48,6 +48,12 @@ avoiding concurrent memory access (#1258). @@ -48,6 +48,12 @@ avoiding concurrent memory access (#1258).
48 ctobssim now also supports the handling of energy dispersion. Energy 48 ctobssim now also supports the handling of energy dispersion. Energy
49 dispersion can be switched on or off using the hidden edisp parameter. 49 dispersion can be switched on or off using the hidden edisp parameter.
50 50
  51 +The memory needs for ctobssim have been reduced by first working on a
  52 +local copy of the CTA observation (this de-allocates properly and memory
  53 +cache associated to the response functions), and by saving and disposing
  54 +the event lists immediately if called using the execute() method (this
  55 +is the method called when the tool is run from the command line).
  56 +
51 57
52 6. ctselect - CTA event selection 58 6. ctselect - CTA event selection
53 --------------------------------- 59 ---------------------------------
src/ctobssim/ctobssim.cpp
@@ -92,7 +92,7 @@ ctobssim::ctobssim(const GObservations&amp; obs) : @@ -92,7 +92,7 @@ ctobssim::ctobssim(const GObservations&amp; obs) :
92 * 92 *
93 * Constructs application using command line arguments for parameter setting. 93 * Constructs application using command line arguments for parameter setting.
94 ***************************************************************************/ 94 ***************************************************************************/
95 -ctobssim::ctobssim(int argc, char *argv[]) : 95 +ctobssim::ctobssim(int argc, char *argv[]) :
96 ctool(CTOBSSIM_NAME, CTOBSSIM_VERSION, argc, argv) 96 ctool(CTOBSSIM_NAME, CTOBSSIM_VERSION, argc, argv)
97 { 97 {
98 // Initialise members 98 // Initialise members
@@ -218,18 +218,16 @@ void ctobssim::run(void) @@ -218,18 +218,16 @@ void ctobssim::run(void)
218 log << std::endl; 218 log << std::endl;
219 } 219 }
220 220
221 - // Write seed values into logger  
222 - if (logTerse()) {  
223 - log << std::endl;  
224 - log.header1("Seed values");  
225 - for (int i = 0; i < m_rans.size(); ++i) {  
226 - log << gammalib::parformat("Seed "+gammalib::str(i));  
227 - log << gammalib::str(m_rans[i].seed()) << std::endl;  
228 - } 221 + // Special mode: if read ahead is specified we know that we called
  222 + // the execute() method, hence files are saved immediately and event
  223 + // lists are disposed afterwards.
  224 + if (read_ahead()) {
  225 + m_save_and_dispose = true;
229 } 226 }
230 227
231 - // Set energy dispersion flag for all CTA observations and save old  
232 - // values in save_edisp vector 228 + // Determine the number of valid CTA observations, set energy dispersion flag
  229 + // for all CTA observations and save old values in save_edisp vector
  230 + int n_observations = 0;
233 std::vector<bool> save_edisp; 231 std::vector<bool> save_edisp;
234 save_edisp.assign(m_obs.size(), false); 232 save_edisp.assign(m_obs.size(), false);
235 for (int i = 0; i < m_obs.size(); ++i) { 233 for (int i = 0; i < m_obs.size(); ++i) {
@@ -237,6 +235,43 @@ void ctobssim::run(void) @@ -237,6 +235,43 @@ void ctobssim::run(void)
237 if (obs != NULL) { 235 if (obs != NULL) {
238 save_edisp[i] = obs->response()->apply_edisp(); 236 save_edisp[i] = obs->response()->apply_edisp();
239 obs->response()->apply_edisp(m_apply_edisp); 237 obs->response()->apply_edisp(m_apply_edisp);
  238 + n_observations++;
  239 + }
  240 + }
  241 +
  242 + // If more than a single observation has been handled then make sure that
  243 + // an XML file will be used for storage
  244 + if (n_observations > 1) {
  245 + m_use_xml = true;
  246 + }
  247 +
  248 + // Write execution mode into logger
  249 + if (logTerse()) {
  250 + log << std::endl;
  251 + log.header1("Execution mode");
  252 + log << gammalib::parformat("Event list management");
  253 + if (m_save_and_dispose) {
  254 + log << "Save and dispose (reduces memory needs)" << std::endl;
  255 + }
  256 + else {
  257 + log << "Keep events in memory" << std::endl;
  258 + }
  259 + log << gammalib::parformat("Output format");
  260 + if (m_use_xml) {
  261 + log << "Write Observation Definition XML file" << std::endl;
  262 + }
  263 + else {
  264 + log << "Write single event list FITS file" << std::endl;
  265 + }
  266 + }
  267 +
  268 + // Write seed values into logger
  269 + if (logTerse()) {
  270 + log << std::endl;
  271 + log.header1("Seed values");
  272 + for (int i = 0; i < m_rans.size(); ++i) {
  273 + log << gammalib::parformat("Seed "+gammalib::str(i));
  274 + log << gammalib::str(m_rans[i].seed()) << std::endl;
240 } 275 }
241 } 276 }
242 277
@@ -263,9 +298,6 @@ void ctobssim::run(void) @@ -263,9 +298,6 @@ void ctobssim::run(void)
263 } 298 }
264 } 299 }
265 300
266 - // Initialise counters  
267 - int n_observations = 0;  
268 -  
269 // From here on the code can be parallelized if OpenMP support 301 // From here on the code can be parallelized if OpenMP support
270 // is enabled. The code in the following block corresponds to the 302 // is enabled. The code in the following block corresponds to the
271 // code that will be executed in each thread 303 // code that will be executed in each thread
@@ -288,11 +320,11 @@ void ctobssim::run(void) @@ -288,11 +320,11 @@ void ctobssim::run(void)
288 wrklog.max_size(10000000); 320 wrklog.max_size(10000000);
289 321
290 // Loop over all observation in the container. If OpenMP support 322 // Loop over all observation in the container. If OpenMP support
291 - // is enabled, this looped will be parallelized. 323 + // is enabled, this loop will be parallelized.
292 #pragma omp for 324 #pragma omp for
293 for (int i = 0; i < m_obs.size(); ++i) { 325 for (int i = 0; i < m_obs.size(); ++i) {
294 326
295 - // Get CTA observation 327 + // Get pointer on CTA observation
296 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]); 328 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
297 329
298 // Continue only if observation is a CTA observation 330 // Continue only if observation is a CTA observation
@@ -308,26 +340,55 @@ void ctobssim::run(void) @@ -308,26 +340,55 @@ void ctobssim::run(void)
308 } 340 }
309 } 341 }
310 342
311 - // Increment counter  
312 - n_observations++; 343 + // Work on a clone of the CTA observation. This makes sure that
  344 + // any memory allocated for computing (for example a response
  345 + // cache) is properly de-allocated on exit of this run
  346 + GCTAObservation obs_clone = *obs;
313 347
314 // Save number of events before entering simulation 348 // Save number of events before entering simulation
315 - int events_before = obs->events()->size(); 349 + int events_before = obs_clone.events()->size();
316 350
317 // Simulate source events 351 // Simulate source events
318 - simulate_source(obs, models, m_rans[i], &wrklog); 352 + simulate_source(&obs_clone, models, m_rans[i], &wrklog);
319 353
320 // Simulate source events 354 // Simulate source events
321 - simulate_background(obs, models, m_rans[i], &wrklog); 355 + simulate_background(&obs_clone, models, m_rans[i], &wrklog);
322 356
323 // Dump simulation results 357 // Dump simulation results
324 if (logNormal()) { 358 if (logNormal()) {
325 wrklog << gammalib::parformat("MC events"); 359 wrklog << gammalib::parformat("MC events");
326 - wrklog << obs->events()->size() - events_before; 360 + wrklog << obs_clone.events()->size() - events_before;
327 wrklog << " (all models)"; 361 wrklog << " (all models)";
328 wrklog << std::endl; 362 wrklog << std::endl;
329 } 363 }
330 364
  365 + // If requested, event lists are saved immediately
  366 + if (m_save_and_dispose) {
  367 +
  368 + // Set event output file name. If multiple observations are
  369 + // handled, build the filename from prefix and observation
  370 + // index. Otherwise use the outfile parameter.
  371 + std::string outfile;
  372 + if (m_use_xml) {
  373 + m_prefix = (*this)["prefix"].string();
  374 + outfile = m_prefix + gammalib::str(i) + ".fits";
  375 + }
  376 + else {
  377 + outfile = (*this)["outfile"].filename();
  378 + }
  379 +
  380 + // Store output file name in original observation
  381 + obs->eventfile(outfile);
  382 +
  383 + // Save observation into FITS file
  384 + obs_clone.save(outfile, clobber());
  385 +
  386 + }
  387 +
  388 + // ... otherwise append the event list to the original observation
  389 + else {
  390 + obs->events(*(obs_clone.events()));
  391 + }
331 392
332 } // endif: CTA observation found 393 } // endif: CTA observation found
333 394
@@ -341,12 +402,6 @@ void ctobssim::run(void) @@ -341,12 +402,6 @@ void ctobssim::run(void)
341 } 402 }
342 403
343 } // end pragma omp parallel 404 } // end pragma omp parallel
344 -  
345 - // If more than a single observation has been handled then make sure that  
346 - // an XML file will be used for storage  
347 - if (n_observations > 1) {  
348 - m_use_xml = true;  
349 - }  
350 405
351 // Restore energy dispersion flag for all CTA observations 406 // Restore energy dispersion flag for all CTA observations
352 for (int i = 0; i < m_obs.size(); ++i) { 407 for (int i = 0; i < m_obs.size(); ++i) {
@@ -409,6 +464,142 @@ void ctobssim::save(void) @@ -409,6 +464,142 @@ void ctobssim::save(void)
409 464
410 465
411 /***********************************************************************//** 466 /***********************************************************************//**
  467 + * @brief Return observation container
  468 + *
  469 + * @return Reference to observation container
  470 + ***************************************************************************/
  471 +const GObservations& ctobssim::obs(void) const
  472 +{
  473 + // If event lists have been disposed, load them now into the observation
  474 + // container
  475 + if (m_save_and_dispose) {
  476 +
  477 + // Loop over all observation in the container
  478 + for (int i = 0; i < m_obs.size(); ++i) {
  479 +
  480 + // Get CTA observation
  481 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
  482 +
  483 + // Handle only CTA observations
  484 + if (obs != NULL) {
  485 +
  486 + // Load observation from FITS file
  487 + obs->load(obs->eventfile());
  488 +
  489 + } // endif: observation was a CTA observations
  490 +
  491 + } // endfor: looped over CTA observations
  492 +
  493 + // Now we have the event lists hence we can reset the disposal flag
  494 + m_save_and_dispose = false;
  495 +
  496 + } // endif: event lists have been disposed
  497 +
  498 + // Return observation container
  499 + return m_obs;
  500 +}
  501 +
  502 +
  503 +/*==========================================================================
  504 + = =
  505 + = Private methods =
  506 + = =
  507 + ==========================================================================*/
  508 +
  509 +/***********************************************************************//**
  510 + * @brief Initialise class members
  511 + *
  512 + * The thrown area is fixed to pi*(2500^2) m2, which is the same value that
  513 + * is used in the Monte Carlo simulations (information from Konrad Bernloehr)
  514 + ***************************************************************************/
  515 +void ctobssim::init_members(void)
  516 +{
  517 + // Initialise user parameters
  518 + m_infile.clear();
  519 + m_outfile.clear();
  520 + m_prefix.clear();
  521 + m_seed = 1;
  522 + m_ra = 0.0;
  523 + m_dec = 0.0;
  524 + m_rad = 0.0;
  525 + m_tmin = 0.0;
  526 + m_tmax = 0.0;
  527 + m_emin = 0.0;
  528 + m_emax = 0.0;
  529 + m_apply_edisp = false;
  530 + m_deadc = 1.0;
  531 +
  532 + // Initialise protected members
  533 + m_rans.clear();
  534 + m_obs.clear();
  535 + m_use_xml = false;
  536 + m_save_and_dispose = false;
  537 +
  538 + // Set fixed parameters
  539 + m_area = 19634954.0 * 1.0e4; //!< pi*(2500^2) m^2
  540 + m_max_photons = 1000000; //!< Maximum number of photons / time slice
  541 +
  542 + // Set CTA time reference. G_CTA_MJDREF is the CTA reference MJD,
  543 + // which is defined in GCTALib.hpp. This is somehow a kluge. We need
  544 + // a better mechanism to implement the CTA reference MJD.
  545 + m_cta_ref.set(G_CTA_MJDREF, "s", "TT", "LOCAL");
  546 +
  547 + // Initialise first event identifier
  548 + m_event_id = 1;
  549 +
  550 + // Return
  551 + return;
  552 +}
  553 +
  554 +
  555 +/***********************************************************************//**
  556 + * @brief Copy class members
  557 + *
  558 + * @param[in] app Application.
  559 + ***************************************************************************/
  560 +void ctobssim::copy_members(const ctobssim& app)
  561 +{
  562 + // Copy user parameters
  563 + m_infile = app.m_infile;
  564 + m_outfile = app.m_outfile;
  565 + m_prefix = app.m_prefix;
  566 + m_seed = app.m_seed;
  567 + m_ra = app.m_ra;
  568 + m_dec = app.m_dec;
  569 + m_rad = app.m_rad;
  570 + m_tmin = app.m_tmin;
  571 + m_tmax = app.m_tmax;
  572 + m_emin = app.m_emin;
  573 + m_emax = app.m_emax;
  574 + m_apply_edisp = app.m_apply_edisp;
  575 + m_deadc = app.m_deadc;
  576 +
  577 + // Copy protected members
  578 + m_area = app.m_area;
  579 + m_max_photons = app.m_max_photons;
  580 + m_rans = app.m_rans;
  581 + m_obs = app.m_obs;
  582 + m_use_xml = app.m_use_xml;
  583 + m_save_and_dispose = app.m_save_and_dispose;
  584 + m_cta_ref = app.m_cta_ref;
  585 + m_event_id = app.m_event_id;
  586 +
  587 + // Return
  588 + return;
  589 +}
  590 +
  591 +
  592 +/***********************************************************************//**
  593 + * @brief Delete class members
  594 + ***************************************************************************/
  595 +void ctobssim::free_members(void)
  596 +{
  597 + // Return
  598 + return;
  599 +}
  600 +
  601 +
  602 +/***********************************************************************//**
412 * @brief Get application parameters 603 * @brief Get application parameters
413 * 604 *
414 * Get all task parameters from parameter file or (if required) by querying 605 * Get all task parameters from parameter file or (if required) by querying
@@ -473,6 +664,16 @@ void ctobssim::get_parameters(void) @@ -473,6 +664,16 @@ void ctobssim::get_parameters(void)
473 m_prefix = (*this)["prefix"].string(); 664 m_prefix = (*this)["prefix"].string();
474 } 665 }
475 666
  667 + // Make sure that all observations hold a CTA event list
  668 + for (int i = 0; i < m_obs.size(); ++i) {
  669 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
  670 + if (obs != NULL) {
  671 + if (dynamic_cast<const GCTAEventList*>(obs->events()) == NULL) {
  672 + set_list(obs);
  673 + }
  674 + }
  675 + }
  676 +
476 // Initialise random number generators. We initialise here one random 677 // Initialise random number generators. We initialise here one random
477 // number generator per observation so that each observation will 678 // number generator per observation so that each observation will
478 // get it's own random number generator. This will lead to identical 679 // get it's own random number generator. This will lead to identical
@@ -624,21 +825,17 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models, @@ -624,21 +825,17 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models,
624 } 825 }
625 826
626 // Get CTA response 827 // Get CTA response
627 - const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(obs->response()); 828 + const GCTAResponseIrf* rsp =
  829 + dynamic_cast<const GCTAResponseIrf*>(obs->response());
628 if (rsp == NULL) { 830 if (rsp == NULL) {
629 std::string msg = "Response is not an IRF response.\n" + 831 std::string msg = "Response is not an IRF response.\n" +
630 obs->response()->print(); 832 obs->response()->print();
631 throw GException::invalid_value(G_SIMULATE_SOURCE, msg); 833 throw GException::invalid_value(G_SIMULATE_SOURCE, msg);
632 } 834 }
633 835
634 - // Make sure that the observation holds a CTA event list. If this  
635 - // is not the case then allocate and attach a CTA event list now.  
636 - if (dynamic_cast<const GCTAEventList*>(obs->events()) == NULL) {  
637 - set_list(obs);  
638 - }  
639 -  
640 // Get pointer on event list (circumvent const correctness) 836 // Get pointer on event list (circumvent const correctness)
641 - GCTAEventList* events = static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events())); 837 + GCTAEventList* events =
  838 + static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events()));
642 839
643 // Extract simulation region. 840 // Extract simulation region.
644 GSkyDir dir = events->roi().centre().dir(); 841 GSkyDir dir = events->roi().centre().dir();
@@ -898,7 +1095,7 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models, @@ -898,7 +1095,7 @@ void ctobssim::simulate_source(GCTAObservation* obs, const GModels&amp; models,
898 ***************************************************************************/ 1095 ***************************************************************************/
899 void ctobssim::simulate_background(GCTAObservation* obs, 1096 void ctobssim::simulate_background(GCTAObservation* obs,
900 const GModels& models, 1097 const GModels& models,
901 - GRan& ran, 1098 + GRan& ran,
902 GLog* wrklog) 1099 GLog* wrklog)
903 { 1100 {
904 // Continue only if observation pointer is valid 1101 // Continue only if observation pointer is valid
@@ -909,12 +1106,6 @@ void ctobssim::simulate_background(GCTAObservation* obs, @@ -909,12 +1106,6 @@ void ctobssim::simulate_background(GCTAObservation* obs,
909 wrklog = &log; 1106 wrklog = &log;
910 } 1107 }
911 1108
912 - // Make sure that the observation holds a CTA event list. If this  
913 - // is not the case then allocate and attach a CTA event list now.  
914 - if (dynamic_cast<const GCTAEventList*>(obs->events()) == NULL) {  
915 - set_list(obs);  
916 - }  
917 -  
918 // Get pointer on event list (circumvent const correctness) 1109 // Get pointer on event list (circumvent const correctness)
919 GCTAEventList* events = 1110 GCTAEventList* events =
920 static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events())); 1111 static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events()));
@@ -934,7 +1125,7 @@ void ctobssim::simulate_background(GCTAObservation* obs, @@ -934,7 +1125,7 @@ void ctobssim::simulate_background(GCTAObservation* obs,
934 // Get simulated CTA event list. Note that this method 1125 // Get simulated CTA event list. Note that this method
935 // includes the deadtime correction. 1126 // includes the deadtime correction.
936 GCTAEventList* list = 1127 GCTAEventList* list =
937 - dynamic_cast<GCTAEventList*>(model->mc(*obs, ran)); 1128 + dynamic_cast<GCTAEventList*>(model->mc(*obs, ran));
938 1129
939 // Continue only if we got a CTA event list 1130 // Continue only if we got a CTA event list
940 if (list != NULL) { 1131 if (list != NULL) {
@@ -984,103 +1175,6 @@ void ctobssim::simulate_background(GCTAObservation* obs, @@ -984,103 +1175,6 @@ void ctobssim::simulate_background(GCTAObservation* obs,
984 } 1175 }
985 1176
986 1177
987 -/*==========================================================================  
988 - = =  
989 - = Private methods =  
990 - = =  
991 - ==========================================================================*/  
992 -  
993 -/***********************************************************************//**  
994 - * @brief Initialise class members  
995 - *  
996 - * The thrown area is fixed to pi*(2500^2) m2, which is the same value that  
997 - * is used in the Monte Carlo simulations (information from Konrad Bernloehr)  
998 - ***************************************************************************/  
999 -void ctobssim::init_members(void)  
1000 -{  
1001 - // Initialise user parameters  
1002 - m_infile.clear();  
1003 - m_outfile.clear();  
1004 - m_prefix.clear();  
1005 - m_seed = 1;  
1006 - m_ra = 0.0;  
1007 - m_dec = 0.0;  
1008 - m_rad = 0.0;  
1009 - m_tmin = 0.0;  
1010 - m_tmax = 0.0;  
1011 - m_emin = 0.0;  
1012 - m_emax = 0.0;  
1013 - m_apply_edisp = false;  
1014 - m_deadc = 1.0;  
1015 -  
1016 - // Initialise protected members  
1017 - m_rans.clear();  
1018 - m_obs.clear();  
1019 - m_use_xml = false;  
1020 -  
1021 - // Set fixed parameters  
1022 - m_area = 19634954.0 * 1.0e4; //!< pi*(2500^2) m^2  
1023 - m_max_photons = 1000000; //!< Maximum number of photons / time slice  
1024 -  
1025 - // Set CTA time reference. G_CTA_MJDREF is the CTA reference MJD,  
1026 - // which is defined in GCTALib.hpp. This is somehow a kluge. We need  
1027 - // a better mechanism to implement the CTA reference MJD.  
1028 - m_cta_ref.set(G_CTA_MJDREF, "s", "TT", "LOCAL");  
1029 -  
1030 - // Initialise first event identifier  
1031 - m_event_id = 1;  
1032 -  
1033 - // Return  
1034 - return;  
1035 -}  
1036 -  
1037 -  
1038 -/***********************************************************************//**  
1039 - * @brief Copy class members  
1040 - *  
1041 - * @param[in] app Application.  
1042 - ***************************************************************************/  
1043 -void ctobssim::copy_members(const ctobssim& app)  
1044 -{  
1045 - // Copy user parameters  
1046 - m_infile = app.m_infile;  
1047 - m_outfile = app.m_outfile;  
1048 - m_prefix = app.m_prefix;  
1049 - m_seed = app.m_seed;  
1050 - m_ra = app.m_ra;  
1051 - m_dec = app.m_dec;  
1052 - m_rad = app.m_rad;  
1053 - m_tmin = app.m_tmin;  
1054 - m_tmax = app.m_tmax;  
1055 - m_emin = app.m_emin;  
1056 - m_emax = app.m_emax;  
1057 - m_apply_edisp = app.m_apply_edisp;  
1058 - m_deadc = app.m_deadc;  
1059 -  
1060 - // Copy protected members  
1061 - m_area = app.m_area;  
1062 - m_max_photons = app.m_max_photons;  
1063 - m_rans = app.m_rans;  
1064 - m_obs = app.m_obs;  
1065 - m_use_xml = app.m_use_xml;  
1066 - m_cta_ref = app.m_cta_ref;  
1067 - m_event_id = app.m_event_id;  
1068 -  
1069 - // Return  
1070 - return;  
1071 -}  
1072 -  
1073 -  
1074 -/***********************************************************************//**  
1075 - * @brief Delete class members  
1076 - ***************************************************************************/  
1077 -void ctobssim::free_members(void)  
1078 -{  
1079 - // Return  
1080 - return;  
1081 -}  
1082 -  
1083 -  
1084 /***********************************************************************//** 1178 /***********************************************************************//**
1085 * @brief Save event list in FITS format. 1179 * @brief Save event list in FITS format.
1086 * 1180 *
@@ -1089,14 +1183,19 @@ void ctobssim::free_members(void) @@ -1089,14 +1183,19 @@ void ctobssim::free_members(void)
1089 ***************************************************************************/ 1183 ***************************************************************************/
1090 void ctobssim::save_fits(void) 1184 void ctobssim::save_fits(void)
1091 { 1185 {
1092 - // Get output filename  
1093 - m_outfile = (*this)["outfile"].filename(); 1186 + // Save only if event list has not yet been saved and disposed
  1187 + if (!m_save_and_dispose) {
  1188 +
  1189 + // Get output filename
  1190 + m_outfile = (*this)["outfile"].filename();
1094 1191
1095 - // Get CTA observation from observation container  
1096 - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]); 1192 + // Get CTA observation from observation container
  1193 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
1097 1194
1098 - // Save observation into FITS file  
1099 - obs->save(m_outfile, clobber()); 1195 + // Save observation into FITS file
  1196 + obs->save(m_outfile, clobber());
  1197 +
  1198 + } // endif: event list has not yet been saved and disposed
1100 1199
1101 // Return 1200 // Return
1102 return; 1201 return;
@@ -1131,34 +1230,37 @@ void ctobssim::save_xml(void) @@ -1131,34 +1230,37 @@ void ctobssim::save_xml(void)
1131 " definition files." << std::endl; 1230 " definition files." << std::endl;
1132 } 1231 }
1133 1232
1134 - // Initialise file number  
1135 - int file_num = 0; 1233 + // Save only if event lists have not yet been saved and disposed
  1234 + if (!m_save_and_dispose) {
1136 1235
1137 - // Loop over all observation in the container  
1138 - for (int i = 0; i < m_obs.size(); ++i) { 1236 + // Loop over all observation in the container
  1237 + for (int i = 0; i < m_obs.size(); ++i) {
1139 1238
1140 - // Get CTA observation  
1141 - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]); 1239 + // Get CTA observation
  1240 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
1142 1241
1143 - // Handle only CTA observations  
1144 - if (obs != NULL) { 1242 + // Handle only CTA observations
  1243 + if (obs != NULL) {
1145 1244
1146 - // Set event output file name  
1147 - std::string outfile = m_prefix + gammalib::str(file_num) + ".fits"; 1245 + // Continue only if there is an event list (it may have been disposed)
  1246 + if (obs->events()->size() != 0) {
1148 1247
1149 - // Store output file name in observation  
1150 - obs->eventfile(outfile); 1248 + // Set event output file name
  1249 + std::string outfile = m_prefix + gammalib::str(i) + ".fits";
1151 1250
1152 - // Save observation into FITS file  
1153 - obs->save(outfile, clobber()); 1251 + // Store output file name in observation
  1252 + obs->eventfile(outfile);
1154 1253
1155 - // Increment file number  
1156 - file_num++; 1254 + // Save observation into FITS file
  1255 + obs->save(outfile, clobber());
1157 1256
  1257 + }
1158 1258
1159 - } // endif: observation was a CTA observations 1259 + } // endif: observation was a CTA observations
1160 1260
1161 - } // endfor: looped over observations 1261 + } // endfor: looped over observations
  1262 +
  1263 + } // endif: event list has not yet been saved and disposed
1162 1264
1163 // Save observations in XML file 1265 // Save observations in XML file
1164 m_obs.save(m_outfile); 1266 m_obs.save(m_outfile);
src/ctobssim/ctobssim.hpp
@@ -101,25 +101,14 @@ protected: @@ -101,25 +101,14 @@ protected:
101 double m_deadc; //!< Average deadtime correction 101 double m_deadc; //!< Average deadtime correction
102 102
103 // Protected members 103 // Protected members
104 - double m_area; //!< Surface area for simulation (cm2)  
105 - int m_max_photons; //!< Maximum number of photons/slice  
106 - std::vector<GRan> m_rans; //!< Random number generators  
107 - GObservations m_obs; //!< Observation container  
108 - bool m_use_xml; //!< Use XML file instead of FITS file  
109 - GTimeReference m_cta_ref; //!< CTA time reference  
110 - int m_event_id; //!< Event identifier 104 + mutable GObservations m_obs; //!< Observation container
  105 + mutable bool m_save_and_dispose; //!< Save and dispose immediately
  106 + double m_area; //!< Surface area for simulation (cm2)
  107 + int m_max_photons; //!< Maximum number of photons/slice
  108 + std::vector<GRan> m_rans; //!< Random number generators
  109 + bool m_use_xml; //!< Use XML file instead of FITS file
  110 + GTimeReference m_cta_ref; //!< CTA time reference
  111 + int m_event_id; //!< Event identifier
111 }; 112 };
112 113
113 -  
114 -/***********************************************************************//**  
115 - * @brief Return observation container  
116 - *  
117 - * @return Reference to observation container  
118 - ***************************************************************************/  
119 -inline  
120 -const GObservations& ctobssim::obs(void) const  
121 -{  
122 - return m_obs;  
123 -}  
124 -  
125 #endif /* CTOBSSIM_HPP */ 114 #endif /* CTOBSSIM_HPP */