Commit 6ec18d4538e2e6ba7dd67a85e1b5d2db7558d1be

Authored by Jürgen Knödlseder
2 parents c600f360 28a6964e

Merge branch '1330-ctmodel-multiple-handling' into add-base-class

ChangeLog
... ... @@ -6,6 +6,7 @@
6 6 Clean-up public interface
7 7 Adapt to GammaLib 0.9.0 interface
8 8 ctbin now always merges event lists into a single counts cube
  9 + ctmodel now always creates a single model cube
9 10 Add ctexpcube tool (generates exposure cube)
10 11 Add ctpsfcube tool (generates PSF cube)
11 12 Add ctbkgcube tool (generates background cube)
... ...
... ... @@ -98,8 +98,26 @@ extension in the FITS file.
98 98 None
99 99  
100 100  
101   -10. ctmodel - CTA counts map modelling
102   ---------------------------------------
  101 +10. ctmodel - CTA counts cube modelling
  102 +---------------------------------------
  103 +WARNING: The philosophy behind the ctmodel tool changed fundamentally!
  104 +
  105 +ctmodel now always produces a single model cube on output. There are
  106 +two important input parameters: "infile" and "obsfile". "infile"
  107 +specifies a counts or model cube that is used to define the binning
  108 +of the model cube. The functionnality for this parameter is as before.
  109 +If "infile=NONE", the cube will be defined according to task parameters
  110 +that specify the spatial and the energy binning. The "obsfile"
  111 +parameter is new and allows specifying the observation definition
  112 +for which the model cube should be computed. If an event list or a
  113 +counts cube is presented, the pointing and Good Time Intervals will
  114 +be extracted from the FITS file header. If an observation definition
  115 +XML file is presented, ctmodel will now loop over all CTA observations
  116 +in the file and add the model values to the model cube. If no
  117 +response information is associated to the "obsfile" (which is typically
  118 +the case for event lists or counts cubes), the "caldb" and "irf"
  119 +parameters will be used to identify the response.
  120 +
103 121 ctmodel now has full access to the calibration database, allowing
104 122 the use for 2D response functions in FITS files.
105 123  
... ...
pyext/ctmodel.i
1 1 /***************************************************************************
2   - * ctmodel - CTA counts model tool *
  2 + * ctmodel - CTA model cube generation tool *
3 3 * ----------------------------------------------------------------------- *
4 4 * copyright (C) 2012-2014 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
... ... @@ -20,7 +20,7 @@
20 20 ***************************************************************************/
21 21 /**
22 22 * @file ctmodel.i
23   - * @brief CTA counts model tool definition
  23 + * @brief CTA model cube tool definition
24 24 * @author Juergen Knoedlseder
25 25 */
26 26 %{
... ... @@ -51,6 +51,7 @@ public:
51 51 void run(void);
52 52 void save(void);
53 53 const GObservations& obs(void) const;
  54 + const GCTAEventCube& cube(void) const;
54 55 };
55 56  
56 57  
... ...
src/ctmodel/ctmodel.cpp
1 1 /***************************************************************************
2   - * ctmodel - CTA counts model tool *
  2 + * ctmodel - CTA model cube generation tool *
3 3 * ----------------------------------------------------------------------- *
4 4 * copyright (C) 2012-2014 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
... ... @@ -20,7 +20,7 @@
20 20 ***************************************************************************/
21 21 /**
22 22 * @file ctmodel.cpp
23   - * @brief CTA counts model tool implementation
  23 + * @brief CTA model cube tool implementation
24 24 * @author Juergen Knoedlseder
25 25 */
26 26  
... ... @@ -33,8 +33,7 @@
33 33 #include "GTools.hpp"
34 34  
35 35 /* __ Method name definitions ____________________________________________ */
36   -#define G_SETUP_OBS "ctmodel::setup_obs()"
37   -#define G_MODEL_MAP "ctmodel::model_map(GCTAObservation*)"
  36 +#define G_GET_EBOUNDS "ctmodel::get_ebounds()"
38 37  
39 38 /* __ Debug definitions __________________________________________________ */
40 39  
... ... @@ -212,10 +211,10 @@ void ctmodel::execute(void)
212 211 // Signal that some parameters should be read ahead
213 212 m_read_ahead = true;
214 213  
215   - // Create the model map(s)
  214 + // Create model cube
216 215 run();
217 216  
218   - // Save the model map(s) into FITS file
  217 + // Save model cube into FITS file
219 218 save();
220 219  
221 220 // Return
... ... @@ -276,23 +275,15 @@ void ctmodel::run(void)
276 275 // Write header
277 276 if (logTerse()) {
278 277 log << std::endl;
279   - if (m_obs.size() > 1) {
280   - log.header1("Generate model maps");
281   - }
282   - else {
283   - log.header1("Generate model map");
284   - }
  278 + log.header1("Generate model cube");
285 279 }
286 280  
287   - // Initialise observation counter
288   - int n_observations = 0;
  281 + // Initialise counts cube
  282 + init_cube();
289 283  
290 284 // Loop over all observations in the container
291 285 for (int i = 0; i < m_obs.size(); ++i) {
292 286  
293   - // Initialise event input and output filenames
294   - m_infiles.push_back("");
295   -
296 287 // Get CTA observation
297 288 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
298 289  
... ... @@ -309,35 +300,18 @@ void ctmodel::run(void)
309 300 }
310 301 }
311 302  
312   - // Increment number of observations
313   - n_observations++;
314   -
315   - // Save event file name (for possible saving)
316   - m_infiles[i] = obs->eventfile();
317   -
318   - // Generate model map
319   - model_map(obs, m_obs.models());
  303 + // Fill the cube
  304 + fill_cube(obs);
320 305  
321 306 } // endif: CTA observation found
322 307  
323 308 } // endfor: looped over observations
324 309  
325   - // If more than a single observation has been handled then make sure
326   - // that an XML file will be used for storage
327   - if (n_observations > 1) {
328   - m_use_xml = true;
329   - }
330   -
331   - // Write observation(s) into logger
  310 + // Log cube
332 311 if (logTerse()) {
333 312 log << std::endl;
334   - if (m_obs.size() > 1) {
335   - log.header1("Observations after model map generation");
336   - }
337   - else {
338   - log.header1("Observation after model map generation");
339   - }
340   - log << m_obs << std::endl;
  313 + log.header1("Model cube");
  314 + log << m_cube << std::endl;
341 315 }
342 316  
343 317 // Restore energy dispersion flag for all CTA observations
... ... @@ -354,43 +328,135 @@ void ctmodel::run(void)
354 328  
355 329  
356 330 /***********************************************************************//**
357   - * @brief Save model map(s)
358   - *
359   - * This method saves the model map(s) into FITS file(s). There are two
360   - * modes, depending on the m_use_xml flag.
  331 + * @brief Save model cube
361 332 *
362   - * If m_use_xml is true, all model map(s) will be saved into FITS files,
363   - * where the output filenames are constructued from the input filenames by
364   - * prepending the m_prefix string to name. Any path information will be
365   - * stripped form the input name, hence event files will be written into the
366   - * local working directory (unless some path information is present
367   - * in the prefix). In addition, an XML file will be created that gathers
368   - * the filename information for the model map(s). If an XML file was present
369   - * on input, all metadata information will be copied from this input file.
370   - *
371   - * If m_use_xml is false, the model map will be saved into a FITS file.
  333 + * Saves the model cube into a FITS file specified using the "outfile"
  334 + * task parameter.
372 335 ***************************************************************************/
373 336 void ctmodel::save(void)
374 337 {
375 338 // Write header
376 339 if (logTerse()) {
377 340 log << std::endl;
378   - if (m_obs.size() > 1) {
379   - log.header1("Save observations");
380   - }
381   - else {
382   - log.header1("Save observation");
383   - }
  341 + log.header1("Save cube");
384 342 }
385 343  
386   - // Case A: Save model map(s) and XML metadata information
387   - if (m_use_xml) {
388   - save_xml();
389   - }
  344 + // Make sure we have the FITS filename
  345 + m_outfile = (*this)["outfile"].filename();
390 346  
391   - // Case B: Save model map as FITS file
392   - else {
393   - save_fits();
  347 + // Save model cube into FITS file
  348 + m_cube.save(m_outfile, clobber());
  349 +
  350 + // Return
  351 + return;
  352 +}
  353 +
  354 +
  355 +/*==========================================================================
  356 + = =
  357 + = Private methods =
  358 + = =
  359 + ==========================================================================*/
  360 +
  361 +/***********************************************************************//**
  362 + * @brief Initialise class members
  363 + ***************************************************************************/
  364 +void ctmodel::init_members(void)
  365 +{
  366 + // Initialise members
  367 + m_infile.clear();
  368 + m_obsfile.clear();
  369 + m_outfile.clear();
  370 + m_caldb.clear();
  371 + m_irf.clear();
  372 + m_srcmdl.clear();
  373 + m_proj.clear();
  374 + m_coordsys.clear();
  375 + m_ebinalg.clear();
  376 + m_ebinfile.clear();
  377 + m_ra = 0.0;
  378 + m_dec = 0.0;
  379 + m_deadc = 1.0;
  380 + m_tmin = 0.0;
  381 + m_tmax = 0.0;
  382 + m_emin = 0.0;
  383 + m_emax = 0.0;
  384 + m_enumbins = 0;
  385 + m_xref = 0.0;
  386 + m_yref = 0.0;
  387 + m_binsz = 0.0;
  388 + m_nxpix = 0;
  389 + m_nypix = 0;
  390 + m_apply_edisp = false;
  391 +
  392 + // Initialise protected members
  393 + m_obs.clear();
  394 + m_cube.clear();
  395 + m_ebounds.clear();
  396 + m_gti.clear();
  397 + m_read_ahead = false;
  398 +
  399 + // Set logger properties
  400 + log.date(true);
  401 +
  402 + // Return
  403 + return;
  404 +}
  405 +
  406 +
  407 +/***********************************************************************//**
  408 + * @brief Copy class members
  409 + *
  410 + * @param[in] app Application.
  411 + ***************************************************************************/
  412 +void ctmodel::copy_members(const ctmodel& app)
  413 +{
  414 + // Copy attributes
  415 + m_infile = app.m_infile;
  416 + m_obsfile = app.m_obsfile;
  417 + m_outfile = app.m_outfile;
  418 + m_caldb = app.m_caldb;
  419 + m_irf = app.m_irf;
  420 + m_srcmdl = app.m_srcmdl;
  421 + m_proj = app.m_proj;
  422 + m_coordsys = app.m_coordsys;
  423 + m_ebinalg = app.m_ebinalg;
  424 + m_ebinfile = app.m_ebinfile;
  425 + m_ra = app.m_ra;
  426 + m_dec = app.m_dec;
  427 + m_deadc = app.m_deadc;
  428 + m_tmin = app.m_tmin;
  429 + m_tmax = app.m_tmax;
  430 + m_emin = app.m_emin;
  431 + m_emax = app.m_emax;
  432 + m_enumbins = app.m_enumbins;
  433 + m_xref = app.m_xref;
  434 + m_yref = app.m_yref;
  435 + m_binsz = app.m_binsz;
  436 + m_nxpix = app.m_nxpix;
  437 + m_nypix = app.m_nypix;
  438 + m_apply_edisp = app.m_apply_edisp;
  439 +
  440 + // Copy protected members
  441 + m_obs = app.m_obs;
  442 + m_cube = app.m_cube;
  443 + m_ebounds = app.m_ebounds;
  444 + m_gti = app.m_gti;
  445 + m_read_ahead = app.m_read_ahead;
  446 +
  447 + // Return
  448 + return;
  449 +}
  450 +
  451 +
  452 +/***********************************************************************//**
  453 + * @brief Delete class members
  454 + ***************************************************************************/
  455 +void ctmodel::free_members(void)
  456 +{
  457 + // Write separator into logger
  458 + if (logTerse()) {
  459 + log << std::endl;
394 460 }
395 461  
396 462 // Return
... ... @@ -406,62 +472,77 @@ void ctmodel::save(void)
406 472 ***************************************************************************/
407 473 void ctmodel::get_parameters(void)
408 474 {
409   - // If we do not have any observations in the container then get all
410   - // information to build (at least) one
411   - if (m_obs.size() == 0) {
  475 + // Read input and optionally output cube filenames
  476 + m_infile = (*this)["infile"].filename();
  477 + if (m_read_ahead) {
  478 + m_outfile = (*this)["outfile"].filename();
  479 + }
412 480  
413   - // Read general parameters. Only read the output parameters if
414   - // required, and also only read the model XML filename if no
415   - // model is yet in the container.
416   - m_infile = (*this)["infile"].filename();
417   - if (m_read_ahead) {
418   - m_outfile = (*this)["outfile"].filename();
419   - m_prefix = (*this)["prefix"].string();
420   - }
421   - m_caldb = (*this)["caldb"].string();
422   - m_irf = (*this)["irf"].string();
423   - if (m_obs.models().size() == 0) {
424   - m_srcmdl = (*this)["srcmdl"].filename();
  481 + // Read model definiton file if required
  482 + if (m_obs.models().size() == 0) {
  483 + m_srcmdl = (*this)["srcmdl"].filename();
  484 + }
  485 +
  486 + // Get energy dispersion flag parameters
  487 + m_apply_edisp = (*this)["edisp"].boolean();
  488 +
  489 + // If the observation container is empty then read the parameters that
  490 + // are necessary to fill it
  491 + if (m_obs.size() == 0) {
  492 +
  493 + // Read observation definition filename and response parameters
  494 + m_obsfile = (*this)["obsfile"].filename();
  495 + m_caldb = (*this)["caldb"].string();
  496 + m_irf = (*this)["irf"].string();
  497 +
  498 + // If no observation definition file has been specified then read all
  499 + // parameters that are necessary to create an observation from scratch
  500 + // (see method setup_obs)
  501 + if ((m_obsfile == "NONE") || (gammalib::strip_whitespace(m_obsfile) == "")) {
  502 + m_ra = (*this)["ra"].real();
  503 + m_dec = (*this)["dec"].real();
  504 + m_deadc = (*this)["deadc"].real();
  505 + m_tmin = (*this)["tmin"].real();
  506 + m_tmax = (*this)["tmax"].real();
  507 + m_emin = (*this)["emin"].real();
  508 + m_emax = (*this)["emax"].real();
425 509 }
426 510  
427   - // If there is no input filename then read all parameters that
428   - // are required to build a model map from scratch
429   - if ((m_infile == "NONE") || (gammalib::strip_whitespace(m_infile) == "")) {
430   - m_ra = (*this)["ra"].real();
431   - m_dec = (*this)["dec"].real();
432   - m_deadc = (*this)["deadc"].real();
433   - m_tmin = (*this)["tmin"].real();
434   - m_tmax = (*this)["tmax"].real();
435   - m_emin = (*this)["emin"].real();
436   - m_emax = (*this)["emax"].real();
437   - m_enumbins = (*this)["enumbins"].integer();
438   - m_proj = (*this)["proj"].string();
439   - m_coordsys = (*this)["coordsys"].string();
440   - m_xref = (*this)["xref"].real();
441   - m_yref = (*this)["yref"].real();
442   - m_binsz = (*this)["binsz"].real();
443   - m_nxpix = (*this)["nxpix"].integer();
444   - m_nypix = (*this)["nypix"].integer();
  511 + }
  512 +
  513 + // Check if we need response information
  514 + for (int i = 0; i < m_obs.size(); ++i) {
  515 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
  516 + if (obs != NULL) {
  517 + if (!obs->has_response()) {
  518 + m_caldb = (*this)["caldb"].string();
  519 + m_irf = (*this)["irf"].string();
  520 + break;
  521 + }
445 522 }
446 523 }
447 524  
448   - // ... otherwise, read only the parameters that are required for the
449   - // model generation.
450   - else {
451   - if (m_read_ahead) {
452   - m_outfile = (*this)["outfile"].filename();
453   - m_prefix = (*this)["prefix"].string();
  525 + // If no cube file has been specified then read all parameters that
  526 + // are necessary to create the cube from scratch (see method init_cube)
  527 + if ((m_infile == "NONE") || (gammalib::strip_whitespace(m_infile) == "")) {
  528 + m_ebinalg = (*this)["ebinalg"].string();
  529 + if (m_ebinalg == "FILE") {
  530 + m_ebinfile = (*this)["ebinfile"].filename();
454 531 }
455   - m_caldb = (*this)["caldb"].string();
456   - m_irf = (*this)["irf"].string();
457   - if (m_obs.models().size() == 0) {
458   - m_srcmdl = (*this)["srcmdl"].filename();
  532 + else {
  533 + m_emin = (*this)["emin"].real();
  534 + m_emax = (*this)["emax"].real();
  535 + m_enumbins = (*this)["enumbins"].integer();
459 536 }
  537 + m_proj = (*this)["proj"].string();
  538 + m_coordsys = (*this)["coordsys"].string();
  539 + m_xref = (*this)["xref"].real();
  540 + m_yref = (*this)["yref"].real();
  541 + m_binsz = (*this)["binsz"].real();
  542 + m_nxpix = (*this)["nxpix"].integer();
  543 + m_nypix = (*this)["nypix"].integer();
460 544 }
461 545  
462   - // Get other parameters
463   - m_apply_edisp = (*this)["edisp"].boolean();
464   -
465 546 // Return
466 547 return;
467 548 }
... ... @@ -470,37 +551,33 @@ void ctmodel::get_parameters(void)
470 551 /***********************************************************************//**
471 552 * @brief Setup observation container
472 553 *
473   - * @exception GException::no_cube
474   - * No event cube found in CTA observation.
475   - *
476   - * This method sets up the observation container for processing. There are
477   - * two cases:
  554 + * This method makes sure that we have an observation container with an
  555 + * associated model and that all CTA observations have a valid response
  556 + * functions.
478 557 *
479   - * If there are no observations in the actual observation container, the
480   - * method will check in "infile" parameter. If this parameter is "NONE" or
481   - * empty, the task parameters will be used to construct a model map.
482   - * Otherwise, the method first tries to interpret the "infile" parameter as
483   - * a counts map, and attemps loading of the file in an event cube. If this
484   - * fails, the method tries to interpret the "infile" parameter as an
485   - * observation definition XML file. If this also fails, an exception will
486   - * be thrown.
  558 + * If on input the observation container is empty, the method sets up an
  559 + * observation container by either loading the information from an
  560 + * observation definition XML file, by loading an event list or a counts
  561 + * cube as a single observation, or by constructing an observation from
  562 + * the scratch from the task parameters.
487 563 *
488   - * If observations exist already in the observation container, the method
489   - * will simply keep them.
  564 + * If no model exists yet in the observation container, the method will load
  565 + * the model from the file given by the task parameter "srcmdl".
490 566 *
491   - * Test if all CTA observations contain counts maps.
492   - *
493   - * Finally, if no models exist so far in the observation container, the
494   - * models will be loaded from the model XML file.
  567 + * The method then loops over all observations in the container and makes
  568 + * sure that all CTA observations have a valid response function. If no
  569 + * response function exists so far, the response function will be set
  570 + * using the "caldb" and "irf" task parameters.
495 571 ***************************************************************************/
496 572 void ctmodel::setup_obs(void)
497 573 {
498   - // If there are no observations in the container then try to build some
  574 + // If there are no observations in the container then get them either
  575 + // from the observation definition file or from the task parameters
499 576 if (m_obs.size() == 0) {
500 577  
501   - // If no input filename has been specified, then create a model map
502   - // from the task parameters
503   - if ((m_infile == "NONE") || (gammalib::strip_whitespace(m_infile) == "")) {
  578 + // If no observation definition file has been specified, then create
  579 + // an observation container from the task parameters
  580 + if ((m_obsfile == "NONE") || (gammalib::strip_whitespace(m_obsfile) == "")) {
504 581  
505 582 // Set pointing direction
506 583 GCTAPointing pnt;
... ... @@ -508,27 +585,27 @@ void ctmodel::setup_obs(void)
508 585 skydir.radec_deg(m_ra, m_dec);
509 586 pnt.dir(skydir);
510 587  
511   - // Setup energy range covered by model
512   - GEnergy emin(m_emin, "TeV");
513   - GEnergy emax(m_emax, "TeV");
514   - GEbounds ebds(m_enumbins, emin, emax);
515   -
516 588 // Setup time interval covered by model
517 589 GGti gti;
518 590 GTime tmin(m_tmin);
519 591 GTime tmax(m_tmax);
520 592 gti.append(tmin, tmax);
521 593  
522   - // Setup skymap
523   - GSkymap map = GSkymap(m_proj, m_coordsys,
524   - m_xref, m_yref, -m_binsz, m_binsz,
525   - m_nxpix, m_nypix, m_enumbins);
526   -
527   - // Create model cube from sky map
528   - GCTAEventCube cube(map, ebds, gti);
  594 + // Setup energy range covered by model
  595 + GEnergy emin(m_emin, "TeV");
  596 + GEnergy emax(m_emax, "TeV");
  597 + GEbounds ebounds(1, emin, emax);
529 598  
530   - // Allocate CTA observation
  599 + // Allocate CTA observation and empty event list
531 600 GCTAObservation obs;
  601 + GCTAEventList list;
  602 +
  603 + // Set event list GTI and energy boundaries and append event list
  604 + // to observation (we need in fact only the GTI for the
  605 + // computations)
  606 + list.gti(gti);
  607 + list.ebounds(ebounds);
  608 + obs.events(list);
532 609  
533 610 // Set CTA observation attributes
534 611 obs.pointing(pnt);
... ... @@ -536,34 +613,25 @@ void ctmodel::setup_obs(void)
536 613 obs.livetime(gti.ontime()*m_deadc);
537 614 obs.deadc(m_deadc);
538 615  
539   - // Set event cube in observation
540   - obs.events(cube);
541   -
542 616 // Append CTA observation to container
543 617 m_obs.append(obs);
544 618  
545   - // Signal that no XML file should be used for storage
546   - m_use_xml = false;
547   -
548   - } // endif: created model map from task parameters
  619 + } // endif: created single CTA observation from task parameters
549 620  
550 621 // ... otherwise try to load information from the file
551 622 else {
552 623  
553   - // First try to open the file as a counts map
  624 + // First try to open the file as an event list or counts map
554 625 try {
555 626  
556 627 // Allocate CTA observation
557 628 GCTAObservation obs;
558 629  
559   - // Load counts map in CTA observation
560   - obs.load(m_infile);
  630 + // Load CTA observation from FITS file
  631 + obs.load(m_obsfile);
561 632  
562 633 // Append CTA observation to container
563 634 m_obs.append(obs);
564   -
565   - // Signal that no XML file should be used for storage
566   - m_use_xml = false;
567 635  
568 636 }
569 637  
... ... @@ -572,10 +640,7 @@ void ctmodel::setup_obs(void)
572 640  
573 641 // Load observations from XML file. This will throw
574 642 // an exception if it fails.
575   - m_obs.load(m_infile);
576   -
577   - // Signal that XML file should be used for storage
578   - m_use_xml = true;
  643 + m_obs.load(m_obsfile);
579 644  
580 645 }
581 646  
... ... @@ -589,8 +654,7 @@ void ctmodel::setup_obs(void)
589 654 m_obs.models(GModels(m_srcmdl));
590 655 }
591 656  
592   - // Check if all CTA observations contain an event cube and setup response
593   - // for all observations
  657 + // Setup response for all observations
594 658 for (int i = 0; i < m_obs.size(); ++i) {
595 659  
596 660 // Is this observation a CTA observation?
... ... @@ -599,12 +663,6 @@ void ctmodel::setup_obs(void)
599 663 // Yes ...
600 664 if (obs != NULL) {
601 665  
602   - // Throw an exception if this observation does not contain
603   - // an event cube
604   - if (dynamic_cast<const GCTAEventCube*>(obs->events()) == NULL) {
605   - throw GException::no_cube(G_SETUP_OBS);
606   - }
607   -
608 666 // Set response if we don't have one
609 667 if (!obs->has_response()) {
610 668  
... ... @@ -635,211 +693,133 @@ void ctmodel::setup_obs(void)
635 693  
636 694  
637 695 /***********************************************************************//**
638   - * @brief Generate model map
639   - *
640   - * @param[in] obs CTA observation pointer.
641   - * @param[in] models Model container.
  696 + * @brief Initialise model cube
642 697 *
643   - * @exception GException::no_cube
644   - * No event cube found in CTA observation.
  698 + * Initialises model cube by either reading the cube definition from an
  699 + * existing cube file specified by the "infile" parameter, or by constructing
  700 + * a cube from the energy and spatial binning information provided by the
  701 + * task parameters. The GTIs of the model cube will be undefined on
  702 + * exit.
645 703 ***************************************************************************/
646   -void ctmodel::model_map(GCTAObservation* obs, const GModels& models)
  704 +void ctmodel::init_cube(void)
647 705 {
648   - // Continue only if observation pointer is valid
649   - if (obs != NULL) {
  706 + // Clear model cube and GTIs
  707 + m_cube.clear();
  708 + m_gti.clear();
650 709  
651   - // Get event cube pointer
652   - GCTAEventCube* cube =
653   - const_cast<GCTAEventCube*>(dynamic_cast<const GCTAEventCube*>(obs->events()));
  710 + // If no cube file has been specified then create a cube from the task
  711 + // parameters
  712 + if ((m_infile == "NONE") || (gammalib::strip_whitespace(m_infile) == "")) {
654 713  
655   - // Throw an exception if the observation does not hold and event
656   - // cube
657   - if (cube == NULL) {
658   - throw GException::no_cube(G_MODEL_MAP);
659   - }
  714 + // Set dummy GTI that is needed for event cube creation
  715 + GGti gti;
  716 + gti.append(GTime(0.0), GTime(0.1234));
  717 +
  718 + // Set energy boundaries
  719 + get_ebounds();
660 720  
661   - // Initialise statistics
662   - double sum = 0.0;
  721 + // Setup skymap
  722 + GSkymap map = GSkymap(m_proj, m_coordsys,
  723 + m_xref, m_yref, -m_binsz, m_binsz,
  724 + m_nxpix, m_nypix, m_enumbins);
663 725  
664   - // Loop over all events in counts map
665   - for (int i = 0; i < cube->size(); ++i) {
  726 + // Create model cube
  727 + m_cube = GCTAEventCube(map, m_ebounds, gti);
666 728  
667   - // Get event bin
668   - GCTAEventBin* bin = (*cube)[i];
669   -
670   - // Compute model value for event bin
671   - double model =
672   - models.eval(*(const_cast<const GCTAEventBin*>(bin)), *obs) *
673   - bin->size();
  729 + }
674 730  
675   - // Store value
676   - bin->counts(model);
  731 + // ... otherwise load cube from file
  732 + else {
677 733  
678   - // Sum all events
679   - sum += model;
680   - }
  734 + // Load cube
  735 + m_cube.load(m_infile);
681 736  
682   - // Log results
683   - if (logTerse()) {
684   - log << gammalib::parformat("Model events in cube");
685   - log << sum << std::endl;
  737 + // Set all cube bins to zero
  738 + for (int i = 0; i < m_cube.size(); ++i) {
  739 + m_cube[i]->counts(0.0);
686 740 }
687 741  
688   - } // endif: observation pointer was not valid
  742 + }
689 743  
690 744 // Return
691 745 return;
692 746 }
693   -
694 747  
695   -/*==========================================================================
696   - = =
697   - = Private methods =
698   - = =
699   - ==========================================================================*/
700 748  
701 749 /***********************************************************************//**
702   - * @brief Initialise class members
  750 + * @brief Fill model into model cube
  751 + *
  752 + * @param[in] obs CTA observation.
  753 + *
  754 + * Adds the expected number of events for a given observation to the events
  755 + * that are already found in the model cube. The method also updates the
  756 + * GTI of the model cube so that cube GTI is a list of the GTIs of all
  757 + * observations that were used to generate the model cube.
703 758 ***************************************************************************/
704   -void ctmodel::init_members(void)
  759 +void ctmodel::fill_cube(const GCTAObservation* obs)
705 760 {
706   - // Initialise members
707   - m_infile.clear();
708   - m_outfile.clear();
709   - m_prefix.clear();
710   - m_caldb.clear();
711   - m_irf.clear();
712   - m_srcmdl.clear();
713   - m_proj.clear();
714   - m_coordsys.clear();
715   - m_ra = 0.0;
716   - m_dec = 0.0;
717   - m_deadc = 1.0;
718   - m_tmin = 0.0;
719   - m_tmax = 0.0;
720   - m_emin = 0.0;
721   - m_emax = 0.0;
722   - m_enumbins = 0;
723   - m_xref = 0.0;
724   - m_yref = 0.0;
725   - m_binsz = 0.0;
726   - m_nxpix = 0;
727   - m_nypix = 0;
728   - m_apply_edisp = false;
729   -
730   - // Initialise protected members
731   - m_obs.clear();
732   - m_infiles.clear();
733   - m_use_xml = false;
734   - m_read_ahead = false;
735   -
736   - // Set logger properties
737   - log.date(true);
738   -
739   - // Return
740   - return;
741   -}
  761 + // Continue only if observation pointer is valid
  762 + if (obs != NULL) {
742 763  
  764 + // Get energy boundaries and GTI references for observation
  765 + const GEbounds& ebounds = obs->events()->ebounds();
  766 + const GGti& gti = obs->events()->gti();
743 767  
744   -/***********************************************************************//**
745   - * @brief Copy class members
746   - *
747   - * @param[in] app Application.
748   - ***************************************************************************/
749   -void ctmodel::copy_members(const ctmodel& app)
750   -{
751   - // Copy attributes
752   - m_infile = app.m_infile;
753   - m_outfile = app.m_outfile;
754   - m_prefix = app.m_prefix;
755   - m_caldb = app.m_caldb;
756   - m_irf = app.m_irf;
757   - m_srcmdl = app.m_srcmdl;
758   - m_proj = app.m_proj;
759   - m_coordsys = app.m_coordsys;
760   - m_ra = app.m_ra;
761   - m_dec = app.m_dec;
762   - m_deadc = app.m_deadc;
763   - m_tmin = app.m_tmin;
764   - m_tmax = app.m_tmax;
765   - m_emin = app.m_emin;
766   - m_emax = app.m_emax;
767   - m_enumbins = app.m_enumbins;
768   - m_xref = app.m_xref;
769   - m_yref = app.m_yref;
770   - m_binsz = app.m_binsz;
771   - m_nxpix = app.m_nxpix;
772   - m_nypix = app.m_nypix;
773   - m_apply_edisp = app.m_apply_edisp;
  768 + // Initialise statistics
  769 + double sum = 0.0;
  770 + int num_outside_ebds = 0;
774 771  
775   - // Copy protected members
776   - m_obs = app.m_obs;
777   - m_infiles = app.m_infiles;
778   - m_use_xml = app.m_use_xml;
779   - m_read_ahead = app.m_read_ahead;
  772 + // Setup cube GTIs for this observation
  773 + m_cube.gti(obs->events()->gti());
780 774  
781   - // Return
782   - return;
783   -}
  775 + // Loop over all cube bins
  776 + for (int i = 0; i < m_cube.size(); ++i) {
784 777  
  778 + // Get cube bin
  779 + GCTAEventBin* bin = m_cube[i];
785 780  
786   -/***********************************************************************//**
787   - * @brief Delete class members
788   - ***************************************************************************/
789   -void ctmodel::free_members(void)
790   -{
791   - // Write separator into logger
792   - if (logTerse()) {
793   - log << std::endl;
794   - }
  781 + // Skip bin if it is outside the energy range of the observation
  782 + if (!ebounds.contains(bin->energy())) {
  783 + num_outside_ebds++;
  784 + continue;
  785 + }
795 786  
796   - // Return
797   - return;
798   -}
  787 + // Get actual bin value
  788 + double value = bin->counts();
  789 +
  790 + // Compute model value for cube bin
  791 + double model = m_obs.models().eval(*bin, *obs) * bin->size();
799 792  
  793 + // Add model to actual value
  794 + value += model;
  795 + sum += model;
800 796  
801   -/***********************************************************************//**
802   - * @brief Set output file name.
803   - *
804   - * @param[in] filename Input file name.
805   - *
806   - * Converts an input filename into an output filename by prepending the
807   - * prefix stored in the member m_prefix to the input filename. Any path will
808   - * be stripped from the input filename. Also a trailing ".gz" will be
809   - * stripped.
810   - ***************************************************************************/
811   -std::string ctmodel::set_outfile_name(const std::string& filename) const
812   -{
813   - // Split input filename into path elements
814   - std::vector<std::string> elements = gammalib::split(filename, "/");
  797 + // Store value
  798 + bin->counts(value);
815 799  
816   - // The last path element is the filename
817   - std::string outname = m_prefix + elements[elements.size()-1];
  800 + } // endfor: looped over all cube bins
818 801  
819   - // Strip any ".gz"
820   - outname = gammalib::strip_chars(outname, ".gz");
821   -
822   - // Return output filename
823   - return outname;
824   -}
  802 + // Append GTIs of observation to list of GTIs
  803 + m_gti.extend(gti);
825 804  
  805 + // Update GTIs
  806 + m_cube.gti(m_gti);
826 807  
827   -/***********************************************************************//**
828   - * @brief Save model map in FITS format.
829   - *
830   - * Save the model map as a FITS file. The filename of the FITS file is
831   - * specified by the m_outfile member.
832   - ***************************************************************************/
833   -void ctmodel::save_fits(void)
834   -{
835   - // Get output filename
836   - m_outfile = (*this)["outfile"].filename();
  808 + // Log results
  809 + if (logTerse()) {
  810 + log << gammalib::parformat("Model events in cube");
  811 + log << sum << std::endl;
  812 + log << gammalib::parformat("Cube bins outside energy range");
  813 + log << num_outside_ebds << std::endl;
  814 + }
837 815  
838   - // Get CTA observation from observation container
839   - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
  816 + // Log cube
  817 + if (logExplicit()) {
  818 + log.header2("Model cube");
  819 + log << m_cube << std::endl;
  820 + }
840 821  
841   - // Save model map
842   - save_model_map(obs, m_outfile);
  822 + } // endif: observation was valid
843 823  
844 824 // Return
845 825 return;
... ... @@ -847,83 +827,76 @@ void ctmodel::save_fits(void)
847 827  
848 828  
849 829 /***********************************************************************//**
850   - * @brief Save model map(s) in XML format.
  830 + * @brief Get the energy boundaries
  831 + *
  832 + * @exception GException::invalid_value
  833 + * No valid energy boundary extension found.
851 834 *
852   - * Save the model map(s) into FITS files and write the file path information
853   - * into a XML file. The filename of the XML file is specified by the
854   - * m_outfile member, the filename(s) of the model map(s) are built by
855   - * prepending the prefix given by the m_prefix member to the input model
856   - * map(s) filenames. Any path present in the input filename will be stripped,
857   - * i.e. the model map(s) will be written in the local working directory
858   - * (unless a path is specified in the m_prefix member).
  835 + * Get the energy boundaries according to the user parameters. The method
  836 + * supports loading of energy boundary information from the EBOUNDS or
  837 + * ENERGYBINS extension, or setting energy boundaries using a linear
  838 + * or logarithmical spacing.
  839 + *
  840 + * @todo Ultimately this method should go in a support library as it is
  841 + * used by several ctools.
859 842 ***************************************************************************/
860   -void ctmodel::save_xml(void)
  843 +void ctmodel::get_ebounds(void)
861 844 {
862   - // Get output filename and prefix
863   - m_outfile = (*this)["outfile"].filename();
864   - m_prefix = (*this)["prefix"].string();
865   -
866   - // Issue warning if output filename has no .xml suffix
867   - std::string suffix = gammalib::tolower(m_outfile.substr(m_outfile.length()-4,4));
868   - if (suffix != ".xml") {
869   - log << "*** WARNING: Name of observation definition output file \""+
870   - m_outfile+"\"" << std::endl;
871   - log << "*** WARNING: does not terminate with \".xml\"." << std::endl;
872   - log << "*** WARNING: This is not an error, but might be misleading."
873   - " It is recommended" << std::endl;
874   - log << "*** WARNING: to use the suffix \".xml\" for observation"
875   - " definition files." << std::endl;
876   - }
877   -
878   - // Loop over all observation in the container
879   - for (int i = 0; i < m_obs.size(); ++i) {
880   -
881   - // Get CTA observation
882   - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
883   -
884   - // Handle only CTA observations
885   - if (obs != NULL) {
886   -
887   - // Set event output file name
888   - std::string outfile = set_outfile_name(m_infiles[i]);
889   -
890   - // Store output file name in observation
891   - obs->eventfile(outfile);
892   -
893   - // Save event list
894   - save_model_map(obs, outfile);
895   -
896   - } // endif: observation was a CTA observations
  845 + // Initialse energy boundary information
  846 + m_ebounds.clear();
  847 +
  848 + // Check whether energy binning information should be read from a FITS
  849 + // file ...
  850 + if (m_ebinalg == "FILE") {
  851 +
  852 + // Open energy boundary file using the EBOUNDS or ENERGYBINS
  853 + // extension. Throw an exception if opening fails.
  854 + GFits file(m_ebinfile);
  855 + if (file.contains("EBOUNDS")) {
  856 + file.close();
  857 + m_ebounds.load(m_ebinfile,"EBOUNDS");
  858 + }
  859 + else if (file.contains("ENERGYBINS")) {
  860 + file.close();
  861 + m_ebounds.load(m_ebinfile,"ENERGYBINS");
  862 + }
  863 + else {
  864 + file.close();
  865 + std::string msg = "No extension with name \"EBOUNDS\" or"
  866 + " \"ENERGYBINS\" found in FITS file"
  867 + " \""+m_ebinfile+"\".\n"
  868 + "An \"EBOUNDS\" or \"ENERGYBINS\" extension"
  869 + " is required if the parameter \"ebinalg\""
  870 + " is set to \"FILE\".";
  871 + throw GException::invalid_value(G_GET_EBOUNDS, msg);
  872 + }
  873 +
  874 + // Set enumbins parameter to number of ebounds
  875 + m_enumbins = m_ebounds.size();
897 876  
898   - } // endfor: looped over observations
  877 + } // endif: ebinalg was "FILE"
899 878  
900   - // Save observations in XML file
901   - m_obs.save(m_outfile);
  879 + // ... otherwise use a linear or a logarithmically-spaced energy binning
  880 + else {
902 881  
903   - // Return
904   - return;
905   -}
  882 + // Initialise log mode for ebinning
  883 + bool log = true;
906 884  
  885 + // check if algorithm is linear
  886 + if (m_ebinalg == "LIN") {
  887 + log = false;
  888 + }
907 889  
908   -/***********************************************************************//**
909   - * @brief Save a single model map into a FITS file
910   - *
911   - * @param[in] obs Pointer to CTA observation.
912   - * @param[in] outfile Output file name.
913   - *
914   - * This method saves a single model map into a FITS file. The method does
915   - * nothing if the observation pointer is not valid.
916   - ***************************************************************************/
917   -void ctmodel::save_model_map(const GCTAObservation* obs,
918   - const std::string& outfile) const
919   -{
920   - // Save only if observation is valid
921   - if (obs != NULL) {
  890 + // todo: should we also check if m_ebinalg is "LOG"
  891 + // and throw an exception if neither LIN/LOG/FILE
  892 + // is given?
922 893  
923   - // Save observation into FITS file
924   - obs->save(outfile, clobber());
  894 + // Setup energy range covered by data
  895 + GEnergy emin(m_emin, "TeV");
  896 + GEnergy emax(m_emax, "TeV");
  897 + m_ebounds = GEbounds(m_enumbins, emin, emax, log);
925 898  
926   - } // endif: observation was valid
  899 + } //endif: ebinalg was not "FILE"
927 900  
928 901 // Return
929 902 return;
... ...
src/ctmodel/ctmodel.hpp
1 1 /***************************************************************************
2   - * ctmodel - CTA counts model tool *
  2 + * ctmodel - CTA model cube generation tool *
3 3 * ----------------------------------------------------------------------- *
4 4 * copyright (C) 2012-2014 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
... ... @@ -20,7 +20,7 @@
20 20 ***************************************************************************/
21 21 /**
22 22 * @file ctmodel.hpp
23   - * @brief CTA counts model tool definition
  23 + * @brief CTA model cube tool definition
24 24 * @author Juergen Knoedlseder
25 25 */
26 26  
... ... @@ -69,25 +69,23 @@ public:
69 69 void run(void);
70 70 void save(void);
71 71 const GObservations& obs(void) const;
  72 + const GCTAEventCube& cube(void) const;
72 73  
73 74 protected:
74 75 // Protected methods
75   - void init_members(void);
76   - void copy_members(const ctmodel& app);
77   - void free_members(void);
78   - void get_parameters(void);
79   - void setup_obs(void);
80   - void model_map(GCTAObservation* obs, const GModels& models);
81   - std::string set_outfile_name(const std::string& filename) const;
82   - void save_fits(void);
83   - void save_xml(void);
84   - void save_model_map(const GCTAObservation* obs,
85   - const std::string& outfile) const;
86   -
  76 + void init_members(void);
  77 + void copy_members(const ctmodel& app);
  78 + void free_members(void);
  79 + void get_parameters(void);
  80 + void setup_obs(void);
  81 + void init_cube(void);
  82 + void fill_cube(const GCTAObservation* obs);
  83 + void get_ebounds(void);
  84 +
87 85 // User parameters
88   - std::string m_infile; //!< Input counts map or XML file
89   - std::string m_outfile; //!< Output model map or XML file
90   - std::string m_prefix; //!< Prefix for multiple model maps
  86 + std::string m_infile; //!< Input counts cube
  87 + std::string m_obsfile; //!< Event list, counts map or observation definition XML file
  88 + std::string m_outfile; //!< Output model cube
91 89 std::string m_caldb; //!< Calibration database
92 90 std::string m_irf; //!< Instrument response functions
93 91 std::string m_srcmdl; //!< Source model
... ... @@ -96,6 +94,8 @@ protected:
96 94 double m_deadc; //!< Deadtime correction factor
97 95 double m_tmin; //!< Start time
98 96 double m_tmax; //!< Stop time
  97 + std::string m_ebinalg; //!< Algorithm for energy binning
  98 + std::string m_ebinfile; //!< FITS-file containing energy binning
99 99 double m_emin; //!< Lower energy
100 100 double m_emax; //!< Upper energy
101 101 int m_enumbins; //!< Number of energy bins
... ... @@ -109,10 +109,11 @@ protected:
109 109 bool m_apply_edisp; //!< Apply energy dispersion?
110 110  
111 111 // Protected members
112   - GObservations m_obs; //!< Observation container
113   - std::vector<std::string> m_infiles; //!< Input event filenames
114   - bool m_use_xml; //!< Use XML file instead of FITS file
115   - bool m_read_ahead; //!< Read ahead parameters
  112 + GObservations m_obs; //!< Observation container
  113 + GCTAEventCube m_cube; //!< Model cube
  114 + GEbounds m_ebounds; //!< Energy boundaries
  115 + GGti m_gti; //!< Model cube GTIs
  116 + bool m_read_ahead; //!< Read ahead parameters
116 117 };
117 118  
118 119  
... ... @@ -127,4 +128,16 @@ const GObservations&amp; ctmodel::obs(void) const
127 128 return m_obs;
128 129 }
129 130  
  131 +
  132 +/***********************************************************************//**
  133 + * @brief Return model cube
  134 + *
  135 + * @return Reference to model cube
  136 + ***************************************************************************/
  137 +inline
  138 +const GCTAEventCube& ctmodel::cube(void) const
  139 +{
  140 + return m_cube;
  141 +}
  142 +
130 143 #endif /* CTMODEL_HPP */
... ...
src/ctmodel/ctmodel.par
... ... @@ -5,7 +5,7 @@
5 5 # ------------------------------------------------------------------------- #
6 6 # #
7 7 # File: ctmodel.par #
8   -# Version: 00-02-00 #
  8 +# Version: 00-03-00 #
9 9 # #
10 10 # Author: Juergen Knoedlseder #
11 11 # Jurgen.Knodlseder@irap.omp.eu #
... ... @@ -19,22 +19,19 @@
19 19 #===================
20 20 infile, f, a, "NONE",,, "Input counts map or observation definition file"
21 21 outfile, f, a, "modmap.fits",,, "Output counts map or observation definition file"
22   -prefix, s, h, "modmap_",,, "Prefix for counts maps in observation definition file"
23   -caldb, s, a, "$CTOOLS/share/caldb/data/cta/dummy",,, "Calibration database"
24   -irf, s, a, "cta_dummy_irf",,, "Instrument response function"
25   -edisp, b, h, no,,, "Apply energy dispersion?"
26 22 srcmdl, f, a, "$CTOOLS/share/models/crab.xml",,, "Source model"
  23 +edisp, b, h, no,,, "Apply energy dispersion?"
27 24 #
28   -# Instrument parameters
29   -#======================
30   -ra, r, a, 83.63, 0,360, "RA of pointing (degrees)"
31   -dec, r, a, 22.01, -90, 90, "Dec of pointing (degrees)"
32   -deadc, r, h, 0.95, 0, 1, "Average deadtime correction factor"
33   -#
34   -# Time range parameters
35   -#======================
36   -tmin, r, a, 0.0, 0, , "Start time (MET in s)"
37   -tmax, r, a, 1800.0, 0, , "End time (MET in s)"
  25 +# Observation parameters
  26 +#=======================
  27 +obsfile, f, a, "NONE",,, "Observation definition file"
  28 +caldb, s, a, "$CTOOLS/share/caldb/data/cta/dummy",,, "Calibration database"
  29 +irf, s, a, "cta_dummy_irf",,, "Instrument response function"
  30 +ra, r, a, 83.63,0,360, "RA of pointing (degrees)"
  31 +dec, r, a, 22.01,-90,90, "Dec of pointing (degrees)"
  32 +tmin, r, a, 0.0,0,, "Start time (MET in s)"
  33 +tmax, r, a, 1800.0,0,, "End time (MET in s)"
  34 +deadc, r, h, 0.95,0,1, "Average deadtime correction factor"
38 35 #
39 36 # Energy binning parameters
40 37 #==========================
... ...
test/test_ctmodel.py
... ... @@ -68,9 +68,10 @@ class Test(gammalib.GPythonTestSuite):
68 68 model = ctools.ctmodel()
69 69 model["infile"].filename("NONE")
70 70 model["outfile"].filename("modmap.fits")
  71 + model["srcmdl"].filename(self.model_name)
  72 + model["obsfile"].filename("NONE")
71 73 model["caldb"].string(self.caldb)
72 74 model["irf"].string(self.irf)
73   - model["srcmdl"].filename(self.model_name)
74 75 model["ra"].real(83.63)
75 76 model["dec"].real(22.01)
76 77 model["tmin"].real(0.0)
... ...
test/test_ctools.sh
... ... @@ -197,6 +197,7 @@ $ECHO -n &quot;Test ctmodel: &quot;
197 197 #
198 198 # Run 1
199 199 $ctmodel infile="cntmap.fits" \
  200 + obsfile="cntmap.fits" \
200 201 outfile="modmap1.fits" \
201 202 caldb="irf" \
202 203 irf="cta_dummy_irf" \
... ... @@ -212,6 +213,7 @@ fi
212 213 #
213 214 # Run 3
214 215 $ctmodel infile="NONE" \
  216 + obsfile="NONE" \
215 217 outfile="modmap3.fits" \
216 218 caldb="irf" \
217 219 irf="cta_dummy_irf" \
... ... @@ -241,6 +243,7 @@ fi
241 243 #
242 244 # Run 2
243 245 $ctmodel infile="cntmap2.fits" \
  246 + obsfile="cntmap2.fits" \
244 247 outfile="modmap2.fits" \
245 248 caldb="irf" \
246 249 irf="cta_dummy_irf" \
... ...