Commit 8af322b8bb79769f87189b2e0bc32f321b4380bb

Authored by Jürgen Knödlseder
1 parent 0e005d47

Change ctmodel logic in a way similar to ctbin.

ctmodel now always produces a single model cube on output. There are two important input parameters: "infile" and "obsfile". "infile" specifies a counts or model cube that is used to define the binning of the model cube. The functionnality for this parameter is as before. If "infile=NONE", the cube will be defined according to task parameters that specify the spatial and the energy binning. The "obsfile" parameter is new and allows specifying the observation definition for which the model cube should be computed. If an event list or a counts cube is presented, the pointing and Good Time Intervals will be extracted from the FITS file header. If an observation definition XML file is presented, ctmodel will now loop over all CTA observations in the file and add the model values to the model cube. If no response information is associated to the "obsfile" (which is typically the case for event lists or counts cubes), the "caldb" and "irf" parameters will be used to identify the response.
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,78 @@ 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 + if (m_obs.models().size() == 0) {
  508 + m_srcmdl = (*this)["srcmdl"].filename();
  509 + }
425 510 }
426 511  
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();
  512 + }
  513 +
  514 + // Check if we need response information
  515 + for (int i = 0; i < m_obs.size(); ++i) {
  516 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
  517 + if (obs != NULL) {
  518 + if (!obs->has_response()) {
  519 + m_caldb = (*this)["caldb"].string();
  520 + m_irf = (*this)["irf"].string();
  521 + break;
  522 + }
445 523 }
446 524 }
447 525  
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();
  526 + // If no cube file has been specified then read all parameters that
  527 + // are necessary to create the cube from scratch (see method init_cube)
  528 + if ((m_infile == "NONE") || (gammalib::strip_whitespace(m_infile) == "")) {
  529 + m_ebinalg = (*this)["ebinalg"].string();
  530 + if (m_ebinalg == "FILE") {
  531 + m_ebinfile = (*this)["ebinfile"].filename();
454 532 }
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();
  533 + else {
  534 + m_emin = (*this)["emin"].real();
  535 + m_emax = (*this)["emax"].real();
  536 + m_enumbins = (*this)["enumbins"].integer();
459 537 }
  538 + m_proj = (*this)["proj"].string();
  539 + m_coordsys = (*this)["coordsys"].string();
  540 + m_xref = (*this)["xref"].real();
  541 + m_yref = (*this)["yref"].real();
  542 + m_binsz = (*this)["binsz"].real();
  543 + m_nxpix = (*this)["nxpix"].integer();
  544 + m_nypix = (*this)["nypix"].integer();
460 545 }
461 546  
462   - // Get other parameters
463   - m_apply_edisp = (*this)["edisp"].boolean();
464   -
465 547 // Return
466 548 return;
467 549 }
... ... @@ -470,37 +552,33 @@ void ctmodel::get_parameters(void)
470 552 /***********************************************************************//**
471 553 * @brief Setup observation container
472 554 *
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:
478   - *
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.
  555 + * This method makes sure that we have an observation container with an
  556 + * associated model and that all CTA observations have a valid response
  557 + * functions.
487 558 *
488   - * If observations exist already in the observation container, the method
489   - * will simply keep them.
  559 + * If on input the observation container is empty, the method sets up an
  560 + * observation container by either loading the information from an
  561 + * observation definition XML file, by loading an event list or a counts
  562 + * cube as a single observation, or by constructing an observation from
  563 + * the scratch from the task parameters.
490 564 *
491   - * Test if all CTA observations contain counts maps.
  565 + * If no model exists yet in the observation container, the method will load
  566 + * the model from the file given by the task parameter "srcmdl".
492 567 *
493   - * Finally, if no models exist so far in the observation container, the
494   - * models will be loaded from the model XML file.
  568 + * The method then loops over all observations in the container and makes
  569 + * sure that all CTA observations have a valid response function. If no
  570 + * response function exists so far, the response function will be set
  571 + * using the "caldb" and "irf" task parameters.
495 572 ***************************************************************************/
496 573 void ctmodel::setup_obs(void)
497 574 {
498   - // If there are no observations in the container then try to build some
  575 + // If there are no observations in the container then get them either
  576 + // from the observation definition file or from the task parameters
499 577 if (m_obs.size() == 0) {
500 578  
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) == "")) {
  579 + // If no observation definition file has been specified, then create
  580 + // an observation container from the task parameters
  581 + if ((m_obsfile == "NONE") || (gammalib::strip_whitespace(m_obsfile) == "")) {
504 582  
505 583 // Set pointing direction
506 584 GCTAPointing pnt;
... ... @@ -508,27 +586,20 @@ void ctmodel::setup_obs(void)
508 586 skydir.radec_deg(m_ra, m_dec);
509 587 pnt.dir(skydir);
510 588  
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 589 // Setup time interval covered by model
517 590 GGti gti;
518 591 GTime tmin(m_tmin);
519 592 GTime tmax(m_tmax);
520 593 gti.append(tmin, tmax);
521 594  
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);
529   -
530   - // Allocate CTA observation
  595 + // Allocate CTA observation and empty event list
531 596 GCTAObservation obs;
  597 + GCTAEventList list;
  598 +
  599 + // Set event list GTI and append event list to observation
  600 + // (we need in fact only the GTI for the computations)
  601 + list.gti(gti);
  602 + obs.events(list);
532 603  
533 604 // Set CTA observation attributes
534 605 obs.pointing(pnt);
... ... @@ -536,34 +607,25 @@ void ctmodel::setup_obs(void)
536 607 obs.livetime(gti.ontime()*m_deadc);
537 608 obs.deadc(m_deadc);
538 609  
539   - // Set event cube in observation
540   - obs.events(cube);
541   -
542 610 // Append CTA observation to container
543 611 m_obs.append(obs);
544 612  
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
  613 + } // endif: created single CTA observation from task parameters
549 614  
550 615 // ... otherwise try to load information from the file
551 616 else {
552 617  
553   - // First try to open the file as a counts map
  618 + // First try to open the file as an event list or counts map
554 619 try {
555 620  
556 621 // Allocate CTA observation
557 622 GCTAObservation obs;
558 623  
559   - // Load counts map in CTA observation
560   - obs.load(m_infile);
  624 + // Load CTA observation from FITS file
  625 + obs.load(m_obsfile);
561 626  
562 627 // Append CTA observation to container
563 628 m_obs.append(obs);
564   -
565   - // Signal that no XML file should be used for storage
566   - m_use_xml = false;
567 629  
568 630 }
569 631  
... ... @@ -572,10 +634,7 @@ void ctmodel::setup_obs(void)
572 634  
573 635 // Load observations from XML file. This will throw
574 636 // 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;
  637 + m_obs.load(m_obsfile);
579 638  
580 639 }
581 640  
... ... @@ -589,8 +648,7 @@ void ctmodel::setup_obs(void)
589 648 m_obs.models(GModels(m_srcmdl));
590 649 }
591 650  
592   - // Check if all CTA observations contain an event cube and setup response
593   - // for all observations
  651 + // Setup response for all observations
594 652 for (int i = 0; i < m_obs.size(); ++i) {
595 653  
596 654 // Is this observation a CTA observation?
... ... @@ -599,12 +657,6 @@ void ctmodel::setup_obs(void)
599 657 // Yes ...
600 658 if (obs != NULL) {
601 659  
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 660 // Set response if we don't have one
609 661 if (!obs->has_response()) {
610 662  
... ... @@ -635,106 +687,53 @@ void ctmodel::setup_obs(void)
635 687  
636 688  
637 689 /***********************************************************************//**
638   - * @brief Generate model map
  690 + * @brief Initialise model cube
639 691 *
640   - * @param[in] obs CTA observation pointer.
641   - * @param[in] models Model container.
642   - *
643   - * @exception GException::no_cube
644   - * No event cube found in CTA observation.
  692 + * Initialises model cube by either reading the cube definition from an
  693 + * existing cube file specified by the "infile" parameter, or by constructing
  694 + * a cube from the energy and spatial binning information provided by the
  695 + * task parameters. The GTIs of the model cube will be undefined on
  696 + * exit.
645 697 ***************************************************************************/
646   -void ctmodel::model_map(GCTAObservation* obs, const GModels& models)
  698 +void ctmodel::init_cube(void)
647 699 {
648   - // Continue only if observation pointer is valid
649   - if (obs != NULL) {
  700 + // Clear model cube and GTIs
  701 + m_cube.clear();
  702 + m_gti.clear();
650 703  
651   - // Get event cube pointer
652   - GCTAEventCube* cube =
653   - const_cast<GCTAEventCube*>(dynamic_cast<const GCTAEventCube*>(obs->events()));
  704 + // If no cube file has been specified then create a cube from the task
  705 + // parameters
  706 + if ((m_infile == "NONE") || (gammalib::strip_whitespace(m_infile) == "")) {
654 707  
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   - }
  708 + // Set dummy GTI that is needed for event cube creation
  709 + GGti gti;
  710 + gti.append(GTime(0.0), GTime(0.1234));
  711 +
  712 + // Set energy boundaries
  713 + get_ebounds();
660 714  
661   - // Initialise statistics
662   - double sum = 0.0;
  715 + // Setup skymap
  716 + GSkymap map = GSkymap(m_proj, m_coordsys,
  717 + m_xref, m_yref, -m_binsz, m_binsz,
  718 + m_nxpix, m_nypix, m_enumbins);
663 719  
664   - // Loop over all events in counts map
665   - for (int i = 0; i < cube->size(); ++i) {
  720 + // Create model cube
  721 + m_cube = GCTAEventCube(map, m_ebounds, gti);
666 722  
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();
  723 + }
674 724  
675   - // Store value
676   - bin->counts(model);
  725 + // ... otherwise load cube from file
  726 + else {
677 727  
678   - // Sum all events
679   - sum += model;
680   - }
  728 + // Load cube
  729 + m_cube.load(m_infile);
681 730  
682   - // Log results
683   - if (logTerse()) {
684   - log << gammalib::parformat("Model events in cube");
685   - log << sum << std::endl;
  731 + // Set all cube bins to zero
  732 + for (int i = 0; i < m_cube.size(); ++i) {
  733 + m_cube[i]->counts(0.0);
686 734 }
687 735  
688   - } // endif: observation pointer was not valid
689   -
690   - // Return
691   - return;
692   -}
693   -
694   -
695   -/*==========================================================================
696   - = =
697   - = Private methods =
698   - = =
699   - ==========================================================================*/
700   -
701   -/***********************************************************************//**
702   - * @brief Initialise class members
703   - ***************************************************************************/
704   -void ctmodel::init_members(void)
705   -{
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);
  736 + }
738 737  
739 738 // Return
740 739 return;
... ... @@ -742,104 +741,64 @@ void ctmodel::init_members(void)
742 741  
743 742  
744 743 /***********************************************************************//**
745   - * @brief Copy class members
  744 + * @brief Fill model into model cube
746 745 *
747   - * @param[in] app Application.
  746 + * @param[in] obs CTA observation.
  747 + *
  748 + * Adds the expected number of events for a given observation to the events
  749 + * that are already found in the model cube. The method also updates the
  750 + * GTI of the model cube so that cube GTI is a list of the GTIs of all
  751 + * observations that were used to generate the model cube.
748 752 ***************************************************************************/
749   -void ctmodel::copy_members(const ctmodel& app)
  753 +void ctmodel::fill_cube(const GCTAObservation* obs)
750 754 {
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;
  755 + // Continue only if observation pointer is valid
  756 + if (obs != NULL) {
774 757  
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;
  758 + // Initialise statistics
  759 + double sum = 0.0;
780 760  
781   - // Return
782   - return;
783   -}
  761 + // Setup cube GTIs for this observation
  762 + m_cube.gti(obs->events()->gti());
784 763  
  764 + // Loop over all cube bins
  765 + for (int i = 0; i < m_cube.size(); ++i) {
785 766  
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   - }
795   -
796   - // Return
797   - return;
798   -}
  767 + // Get cube bin and actual value
  768 + GCTAEventBin* bin = m_cube[i];
  769 + double value = bin->counts();
  770 +
  771 + // Compute model value for cube bin
  772 + double model = m_obs.models().eval(*bin, *obs) * bin->size();
799 773  
  774 + // Add model to actual value
  775 + value += model;
  776 + sum += model;
800 777  
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, "/");
  778 + // Store value
  779 + bin->counts(value);
815 780  
816   - // The last path element is the filename
817   - std::string outname = m_prefix + elements[elements.size()-1];
  781 + } // endfor: looped over all cube bins
818 782  
819   - // Strip any ".gz"
820   - outname = gammalib::strip_chars(outname, ".gz");
821   -
822   - // Return output filename
823   - return outname;
824   -}
  783 + // Append GTIs of observation to list of GTIs
  784 + m_gti.extend(obs->events()->gti());
825 785  
  786 + // Update GTIs
  787 + m_cube.gti(m_gti);
826 788  
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();
  789 + // Log results
  790 + if (logTerse()) {
  791 + log << gammalib::parformat("Model events in cube");
  792 + log << sum << std::endl;
  793 + }
837 794  
838   - // Get CTA observation from observation container
839   - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
  795 + // Log cube
  796 + if (logExplicit()) {
  797 + log.header2("Model cube");
  798 + log << m_cube << std::endl;
  799 + }
840 800  
841   - // Save model map
842   - save_model_map(obs, m_outfile);
  801 + } // endif: observation was valid
843 802  
844 803 // Return
845 804 return;
... ... @@ -847,83 +806,76 @@ void ctmodel::save_fits(void)
847 806  
848 807  
849 808 /***********************************************************************//**
850   - * @brief Save model map(s) in XML format.
  809 + * @brief Get the energy boundaries
  810 + *
  811 + * @exception GException::invalid_value
  812 + * No valid energy boundary extension found.
851 813 *
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).
  814 + * Get the energy boundaries according to the user parameters. The method
  815 + * supports loading of energy boundary information from the EBOUNDS or
  816 + * ENERGYBINS extension, or setting energy boundaries using a linear
  817 + * or logarithmical spacing.
  818 + *
  819 + * @todo Ultimately this method should go in a support library as it is
  820 + * used by several ctools.
859 821 ***************************************************************************/
860   -void ctmodel::save_xml(void)
  822 +void ctmodel::get_ebounds(void)
861 823 {
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
  824 + // Initialse energy boundary information
  825 + m_ebounds.clear();
  826 +
  827 + // Check whether energy binning information should be read from a FITS
  828 + // file ...
  829 + if (m_ebinalg == "FILE") {
  830 +
  831 + // Open energy boundary file using the EBOUNDS or ENERGYBINS
  832 + // extension. Throw an exception if opening fails.
  833 + GFits file(m_ebinfile);
  834 + if (file.contains("EBOUNDS")) {
  835 + file.close();
  836 + m_ebounds.load(m_ebinfile,"EBOUNDS");
  837 + }
  838 + else if (file.contains("ENERGYBINS")) {
  839 + file.close();
  840 + m_ebounds.load(m_ebinfile,"ENERGYBINS");
  841 + }
  842 + else {
  843 + file.close();
  844 + std::string msg = "No extension with name \"EBOUNDS\" or"
  845 + " \"ENERGYBINS\" found in FITS file"
  846 + " \""+m_ebinfile+"\".\n"
  847 + "An \"EBOUNDS\" or \"ENERGYBINS\" extension"
  848 + " is required if the parameter \"ebinalg\""
  849 + " is set to \"FILE\".";
  850 + throw GException::invalid_value(G_GET_EBOUNDS, msg);
  851 + }
  852 +
  853 + // Set enumbins parameter to number of ebounds
  854 + m_enumbins = m_ebounds.size();
897 855  
898   - } // endfor: looped over observations
  856 + } // endif: ebinalg was "FILE"
899 857  
900   - // Save observations in XML file
901   - m_obs.save(m_outfile);
  858 + // ... otherwise use a linear or a logarithmically-spaced energy binning
  859 + else {
902 860  
903   - // Return
904   - return;
905   -}
  861 + // Initialise log mode for ebinning
  862 + bool log = true;
906 863  
  864 + // check if algorithm is linear
  865 + if (m_ebinalg == "LIN") {
  866 + log = false;
  867 + }
907 868  
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) {
  869 + // todo: should we also check if m_ebinalg is "LOG"
  870 + // and throw an exception if neither LIN/LOG/FILE
  871 + // is given?
922 872  
923   - // Save observation into FITS file
924   - obs->save(outfile, clobber());
  873 + // Setup energy range covered by data
  874 + GEnergy emin(m_emin, "TeV");
  875 + GEnergy emax(m_emax, "TeV");
  876 + m_ebounds = GEbounds(m_enumbins, emin, emax, log);
925 877  
926   - } // endif: observation was valid
  878 + } //endif: ebinalg was not "FILE"
927 879  
928 880 // Return
929 881 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" \
... ...