Commit 997ffff511b58a071bdf5d64ab3bd5608d813719

Authored by Jürgen Knödlseder
1 parent b1668ffb

Allow specifying of input sky map in ctskymap (#2311)

ChangeLog
1   -2018-02-10
  1 +2018-02-13
2 2  
3 3 * Version 1.6.0 released
4 4 ========================
5 5  
  6 + Allow specifying of input sky map in ctskymap (#2311)
6 7 Accelerate IRF and RING background methods in ctskymap (#2309)
7 8  
8 9  
... ...
1 1 New Features and Important Changes in ctools 1.6.0
2 2  
3   -10 February 2018
  3 +13 February 2018
4 4  
5 5  
6 6 Introduction
... ... @@ -106,7 +106,22 @@ None
106 106  
107 107 ctskymap - CTA sky mapping
108 108 --------------------------
109   -Accelerate the IRF and RING background methods (#2309).
  109 +Add the "inmap", "iterations" and "threshold" parameters. The hidden
  110 +"inmap" parameter allows specifying a sky map file containing a "COUNTS"
  111 +and "ACCEPTANCE" extension which avoids looping over event lists in an
  112 +observation container. The "iterations" parameter allows the iterative
  113 +determination of an exclusion map from the resulting significance map for
  114 +the RING background method. The exclusion map will flag all pixels with
  115 +significances above the "threshold" value so that they will be excluded
  116 +in the subsequent RING background determination. Since this will impact
  117 +the pixel significances, the procedure needs to be repeated a couple of
  118 +times to stabilise (#2311).
  119 +
  120 +Accelerate the IRF and RING background methods. For the RING background
  121 +method the computation is either performed using a FFT or using a direct
  122 +computation. The FFT computation is faster, but assumed Euclidian
  123 +distances for the sky map grid, the direct computation is slower, but
  124 +accurately computes the distances between sky map pixels (#2309).
110 125  
111 126  
112 127 cttsmap - Generation of Test Statistic map
... ...
doc/source/admin/release_history/1.6.rst
... ... @@ -22,6 +22,8 @@ Bug fixes
22 22 Improvements
23 23 ------------
24 24  
  25 +* [`2311 <https://cta-redmine.irap.omp.eu/issues/2311>`_] -
  26 + Allow specifying of input skymap to :ref:`ctskymap` tool
25 27 * [`2309 <https://cta-redmine.irap.omp.eu/issues/2309>`_] -
26 28 Accelerate `IRF` and `RING` background methods in :ref:`ctskymap`
27 29  
... ...
doc/source/users/reference_manual/ctskymap.rst
... ... @@ -41,6 +41,11 @@ General parameters
41 41 Instrument response function (only required for IRF background subtraction
42 42 if no response information is provided by ``inobs``).
43 43  
  44 +``(inmap = NONE) [file]``
  45 + Input sky map file containing a ``COUNTS`` and an ``ACCEPTANCE`` extension.
  46 + Such extensions are produce by a previous run of the :ref:`ctskymap` tool with
  47 + the ``IRF`` or ``RING`` background method.
  48 +
44 49 ``outmap [file]``
45 50 Output sky map file.
46 51  
... ... @@ -90,6 +95,19 @@ Background subtraction configuration parameters
90 95 ``outradius [real]``
91 96 Outer background ring radius for ``RING`` subtraction (in degrees).
92 97  
  98 +``iterations [integer]``
  99 + Number of iterations for the automatic computation of exclusion regions.
  100 + Sky map pixels with detection significance above the value specified by the
  101 + ``threshold`` parameter will be defined as exclusion regions, and the ``RING``
  102 + background will be recomputed based on these exclusion regions. Since this
  103 + will likely change the significance of the pixels, the procedure can be
  104 + iteratively repeated, and the ``iterations`` parameter specifies how often
  105 + the procedure should be repeated.
  106 +
  107 +``threshold [real]``
  108 + Significance threshold above which pixels should be excluded for the
  109 + background computation in the ``RING`` background method.
  110 +
93 111 ``(inexclusion = NONE) [file]``
94 112 Exclusion region file as either a FITS map or DS9 region file.
95 113  
... ...
pyext/ctobservation.i
... ... @@ -94,6 +94,7 @@ public:
94 94 // Make methods private in Python by prepending an underscore
95 95 %rename(_first_unbinned_observation) first_unbinned_observation;
96 96 %rename(_next_unbinned_observation) next_unbinned_observation;
  97 + %rename(_read_ogip_keywords) read_ogip_keywords;
97 98 %rename(_write_ogip_keywords) write_ogip_keywords;
98 99 %rename(_set_obs_statistic) set_obs_statistic;
99 100 %rename(_save_events_fits) save_events_fits;
... ...
src/ctskymap/ctskymap.cpp
... ... @@ -35,6 +35,8 @@
35 35 #define G_GET_PARAMETERS "ctskymap::get_parameter()"
36 36 #define G_MAP_EVENTS "ctskymap::map_events(GCTAObservation*)"
37 37 #define G_MAP_BACKGROUND_IRF "ctskymap::map_background_irf(GCTAObservation*)"
  38 +#define G_RING_BOUNDING_BOX "ctskymap::ring_bounding_box(int&, int&, int&, "\
  39 + "int&, int&)"
38 40 #define G_RING_KERNEL "ctskymap::ring_kernel(double&, double&)"
39 41  
40 42 /* __ Debug definitions __________________________________________________ */
... ... @@ -223,58 +225,14 @@ void ctskymap::run(void)
223 225 // Setup maps
224 226 setup_maps();
225 227  
226   - // Write input observation container into logger
227   - log_observations(NORMAL, m_obs, "Input observation");
228   -
229   - // Write header into logger
230   - log_header1(TERSE, gammalib::number("Find unbinned observation",
231   - m_obs.size()));
232   -
233   - // Find all unbinned CTA observations in m_obs
234   - std::vector<GCTAObservation*> obs_list(0);
235   - for (GCTAObservation* obs = first_unbinned_observation(); obs != NULL;
236   - obs = next_unbinned_observation()) {
237   -
238   - // Push observation into list
239   - obs_list.push_back(obs);
240   -
241   - // Write message
242   - std::string msg = " Including unbinned "+obs->instrument()+
243   - " observation";
244   - log_string(NORMAL, msg);
  228 + // If no input map exists then fill counts and acceptance maps from
  229 + // observation container
  230 + if (!m_has_inmap) {
  231 + fill_maps();
245 232 }
246 233  
247   - // Write header into logger
248   - log_header1(TERSE, gammalib::number("Map observation", m_obs.size()));
249   -
250   - // Loop over all unbinned CTA observations in the container
251   - #pragma omp parallel for
252   - for (int i = 0; i < obs_list.size(); ++i) {
253   -
254   - // Get pointer to observation
255   - GCTAObservation* obs = obs_list[i];
256   -
257   - // Map events into sky map
258   - map_events(obs);
259   -
260   - // Compute background sky map (only if m_bkgsubtract != "NONE")
261   - map_background(obs);
262   -
263   - // Dispose events to free memory
264   - obs->dispose_events();
265   -
266   - } // endfor: looped over observations
267   -
268   - // If background subtraction is selected then compute significance map
269   - if (m_bkgsubtract != "NONE") {
270   -
271   - // Compute the significance map
272   - map_significance();
273   -
274   - // Subtract background map from counts map
275   - m_skymap -= m_bkgmap;
276   -
277   - } // endif: background subtraction is selected
  234 + // Compute maps
  235 + compute_maps();
278 236  
279 237 // Optionally publish sky map
280 238 if (m_publish) {
... ... @@ -289,8 +247,8 @@ void ctskymap::run(void)
289 247 /***********************************************************************//**
290 248 * @brief Save sky map
291 249 *
292   - * Saves the sky map into a FITS file. The FITS file name is specified by the
293   - * @p outname parameter.
  250 + * Saves the sky map into a FITS file. The FITS file name is specified by
  251 + * the @p outname parameter.
294 252 ***************************************************************************/
295 253 void ctskymap::save(void)
296 254 {
... ... @@ -307,39 +265,31 @@ void ctskymap::save(void)
307 265 GFits fits;
308 266  
309 267 // Write sky map into FITS file
310   - GFitsHDU* hdu = m_skymap.write(fits);
  268 + write_map(fits, m_skymap, "SKYMAP");
311 269  
312   - // Write keywords into sky map extension
313   - write_ogip_keywords(hdu);
314   - write_hdu_keywords(hdu);
315   -
316   - // If background subtraction is requested then write background map
317   - // and significance map to FITS file
  270 + // If background subtraction is requested then write the background map,
  271 + // the significance map, the counts map and the acceptance map to the
  272 + // FITS file
318 273 if (m_bkgsubtract != "NONE") {
319 274  
320 275 // Write background map into FITS file
321   - hdu = m_bkgmap.write(fits);
322   -
323   - // Set background map extension name
324   - if (hdu != NULL) {
325   - hdu->extname("BACKGROUND");
326   - }
  276 + write_map(fits, m_bkgmap, "BACKGROUND");
327 277  
328   - // Write keywords into background extension
329   - write_ogip_keywords(hdu);
330   - write_hdu_keywords(hdu);
331 278  
332 279 // Write significance map into FITS file
333   - hdu = m_sigmap.write(fits);
  280 + write_map(fits, m_sigmap, "SIGNIFICANCE");
334 281  
335   - // Set significance map extension name
336   - if (hdu != NULL) {
337   - hdu->extname("SIGNIFICANCE");
338   - }
  282 + // Write counts map into FITS file
  283 + write_map(fits, m_counts, "COUNTS");
339 284  
340   - // Write keywords into significance extension
341   - write_ogip_keywords(hdu);
342   - write_hdu_keywords(hdu);
  285 + // Write acceptance map into FITS file
  286 + write_map(fits, m_acceptance, "ACCEPTANCE");
  287 +
  288 + // If RING background subtraction is requested then write also
  289 + // the exclusion map to the FITS file
  290 + if (m_bkgsubtract == "RING") {
  291 + write_map(fits, m_exclmap, "EXCLUSION");
  292 + }
343 293  
344 294 } // endif: background subtraction was requested
345 295  
... ... @@ -401,21 +351,25 @@ void ctskymap::init_members(void)
401 351 // Initialise User parameters
402 352 m_outmap.clear();
403 353 m_inexclusion.clear();
404   - m_binsz = 0.0;
405 354 m_emin = 0.0;
406 355 m_emax = 0.0;
407 356 m_bkgsubtract = "NONE";
408 357 m_roiradius = 0.0;
409 358 m_inradius = 0.0;
410 359 m_outradius = 0.0;
  360 + m_iterations = 0;
  361 + m_threshold = 5.0;
411 362 m_usefft = true;
412 363 m_publish = false;
413 364 m_chatter = static_cast<GChatter>(2);
414 365  
415 366 // Initialise members
  367 + m_has_inmap = false;
416 368 m_skymap.clear();
417 369 m_bkgmap.clear();
418 370 m_sigmap.clear();
  371 + m_counts.clear();
  372 + m_acceptance.clear();
419 373 m_exclmap.clear();
420 374  
421 375 // Initialise cache
... ... @@ -440,22 +394,26 @@ void ctskymap::copy_members(const ctskymap&amp; app)
440 394 // Copy User parameters
441 395 m_outmap = app.m_outmap;
442 396 m_inexclusion = app.m_inexclusion;
443   - m_binsz = app.m_binsz;
444 397 m_emin = app.m_emin;
445 398 m_emax = app.m_emax;
446 399 m_bkgsubtract = app.m_bkgsubtract;
447 400 m_roiradius = app.m_roiradius;
448 401 m_inradius = app.m_inradius;
449 402 m_outradius = app.m_outradius;
  403 + m_iterations = app.m_iterations;
  404 + m_threshold = app.m_threshold;
450 405 m_usefft = app.m_usefft;
451 406 m_publish = app.m_publish;
452 407 m_chatter = app.m_chatter;
453 408  
454 409 // Copy members
455   - m_skymap = app.m_skymap;
456   - m_bkgmap = app.m_bkgmap;
457   - m_sigmap = app.m_sigmap;
458   - m_exclmap = app.m_exclmap;
  410 + m_has_inmap = app.m_has_inmap;
  411 + m_skymap = app.m_skymap;
  412 + m_bkgmap = app.m_bkgmap;
  413 + m_sigmap = app.m_sigmap;
  414 + m_counts = app.m_counts;
  415 + m_acceptance = app.m_acceptance;
  416 + m_exclmap = app.m_exclmap;
459 417  
460 418 // Copy cache
461 419 m_solidangle = app.m_solidangle;
... ... @@ -490,17 +448,61 @@ void ctskymap::free_members(void)
490 448 ***************************************************************************/
491 449 void ctskymap::get_parameters(void)
492 450 {
493   - // Setup observations from "inobs" parameter. Do not request response
494   - // information and do not accept counts cubes.
495   - setup_observations(m_obs, false, true, false);
  451 + // So far we have no valid input map
  452 + m_has_inmap = false;
  453 +
  454 + // If an input map was specified then extract try loading the COUNTS
  455 + // and ACCEPTANCE extensions from that file
  456 + GFilename inmap = (*this)["inmap"].filename();
  457 + if (inmap != "NONE") {
  458 + GFits fits(inmap);
  459 + if (fits.contains("COUNTS") && fits.contains("ACCEPTANCE")) {
  460 +
  461 + // Get HDUs
  462 + GFitsHDU &counts = *fits["COUNTS"];
  463 + GFitsHDU &acceptance = *fits["ACCEPTANCE"];
496 464  
497   - // Create sky map. Note that the method reads the relevant User parameters.
498   - m_skymap = create_map(m_obs);
  465 + // Read counts and acceptance map
  466 + m_counts.read(counts);
  467 + m_acceptance.read(acceptance);
499 468  
500   - // Get further parameters
501   - m_binsz = (*this)["binsz"].real();
502   - m_emin = (*this)["emin"].real();
503   - m_emax = (*this)["emax"].real();
  469 + // Extract OGIP parameters
  470 + read_ogip_keywords(&counts);
  471 +
  472 + // Extract energy range
  473 + m_emin = (counts.has_card("E_MIN")) ? counts.real("E_MIN") : 0.0;
  474 + m_emax = (counts.has_card("E_MAX")) ? counts.real("E_MAX") : 0.0;
  475 +
  476 + // Signal availability of sky map
  477 + m_has_inmap = true;
  478 + }
  479 + else {
  480 + std::string msg = "Input sky map \""+inmap.url()+"\" does not "
  481 + "contain \"COUNTS\" and \"ACCEPTANCE\" "
  482 + "extensions.";
  483 + throw GException::invalid_value(G_GET_PARAMETERS, msg);
  484 + }
  485 + fits.close();
  486 + }
  487 +
  488 + // If no valid input map exists then request observations
  489 + if (!m_has_inmap) {
  490 +
  491 + // Setup observations from "inobs" parameter. Do not request response
  492 + // information and do not accept counts cubes.
  493 + setup_observations(m_obs, false, true, false);
  494 +
  495 + // Create counts and acceptance maps from the User parameters
  496 + m_counts = create_map(m_obs);
  497 + m_acceptance = m_counts;
  498 +
  499 + // Get further parameters
  500 + m_emin = (*this)["emin"].real();
  501 + m_emax = (*this)["emax"].real();
  502 +
  503 + } // endif: no valid input map existed
  504 +
  505 + // Get background method
504 506 m_bkgsubtract = (*this)["bkgsubtract"].string();
505 507  
506 508 // Get RING background parameters
... ... @@ -510,16 +512,20 @@ void ctskymap::get_parameters(void)
510 512 m_roiradius = (*this)["roiradius"].real();
511 513 m_inradius = (*this)["inradius"].real();
512 514 m_outradius = (*this)["outradius"].real();
  515 + m_iterations = (*this)["iterations"].integer();
  516 + if (m_iterations > 0) {
  517 + m_threshold = (*this)["threshold"].real();
  518 + }
513 519 m_inexclusion = (*this)["inexclusion"].filename();
514 520 m_usefft = (*this)["usefft"].boolean();
515 521  
516 522 // Make sure that (roiradius < inradius < outradius)
517 523 if (m_roiradius > m_inradius) {
518   - std::string msg("'roiradius' must be smaller than 'inradius'");
  524 + std::string msg("\"roiradius\" must be smaller than \"inradius\"");
519 525 throw GException::invalid_value(G_GET_PARAMETERS, msg);
520 526 }
521 527 else if (m_inradius > m_outradius) {
522   - std::string msg("'inradius' must be smaller than 'outradius'");
  528 + std::string msg("\"inradius\" must be smaller than \"outradius\"");
523 529 throw GException::invalid_value(G_GET_PARAMETERS, msg);
524 530 }
525 531  
... ... @@ -527,7 +533,7 @@ void ctskymap::get_parameters(void)
527 533  
528 534 // If background subtraction is requested then make sure that the CTA
529 535 // observations in the observation container have response information
530   - if (m_bkgsubtract != "NONE") {
  536 + if (m_bkgsubtract != "NONE" && !m_has_inmap) {
531 537 set_response(m_obs);
532 538 }
533 539  
... ... @@ -563,19 +569,15 @@ void ctskymap::setup_maps(void)
563 569 // is requested
564 570 if (m_bkgsubtract != "NONE") {
565 571  
566   - // Create background and significance maps
567   - m_bkgmap = create_map(m_obs);
568   - m_sigmap = create_map(m_obs);
569   -
570 572 // Setup the exclusions map
571   - map_exclusions(m_inexclusion);
  573 + setup_exclusion_map(m_inexclusion);
572 574  
573 575 // Cache the pixel solid angles and sky directions
574   - m_solidangle.reserve(m_bkgmap.npix());
575   - m_dirs.reserve(m_bkgmap.npix());
576   - for (int i = 0; i < m_bkgmap.npix(); ++i) {
577   - m_solidangle.push_back(m_bkgmap.solidangle(i));
578   - m_dirs.push_back(m_bkgmap.inx2dir(i));
  576 + m_solidangle.reserve(m_counts.npix());
  577 + m_dirs.reserve(m_counts.npix());
  578 + for (int i = 0; i < m_counts.npix(); ++i) {
  579 + m_solidangle.push_back(m_counts.solidangle(i));
  580 + m_dirs.push_back(m_counts.inx2dir(i));
579 581 }
580 582  
581 583 } // endif: background subtraction selected
... ... @@ -601,10 +603,10 @@ void ctskymap::setup_maps(void)
601 603 * background estimation. Pixels with values different from 0 will be
602 604 * excluded.
603 605 ***************************************************************************/
604   -void ctskymap::map_exclusions(const GFilename& filename)
  606 +void ctskymap::setup_exclusion_map(const GFilename& filename)
605 607 {
606 608 // Create exlusion map
607   - m_exclmap = create_map(m_obs);
  609 + m_exclmap = m_counts;
608 610  
609 611 // Set all pixels to 0 (no pixel excluded)
610 612 m_exclmap = 0.0;
... ... @@ -614,10 +616,10 @@ void ctskymap::map_exclusions(const GFilename&amp; filename)
614 616  
615 617 // Fill the exclusions based on the regions supplied
616 618 if (filename.is_fits()) {
617   - map_exclusions_fits(filename);
  619 + setup_exclusion_map_fits(filename);
618 620 }
619 621 else {
620   - map_exclusions_region(filename);
  622 + setup_exclusion_map_region(filename);
621 623 }
622 624  
623 625 } // endif: filename was valid
... ... @@ -635,7 +637,7 @@ void ctskymap::map_exclusions(const GFilename&amp; filename)
635 637 * Sets all exclusion map pixels to 1 that correspond to non-zero pixels in
636 638 * the exclusion sky map FITS file.
637 639 ***************************************************************************/
638   -void ctskymap::map_exclusions_fits(const GFilename& filename)
  640 +void ctskymap::setup_exclusion_map_fits(const GFilename& filename)
639 641 {
640 642 // Load the fits image
641 643 GSkyMap inmap(filename);
... ... @@ -669,7 +671,7 @@ void ctskymap::map_exclusions_fits(const GFilename&amp; filename)
669 671 * Sets all exclusion map pixels to 1 that are contained in any of the DS9
670 672 * regions.
671 673 ***************************************************************************/
672   -void ctskymap::map_exclusions_region(const GFilename& filename)
  674 +void ctskymap::setup_exclusion_map_region(const GFilename& filename)
673 675 {
674 676 // Load the exclusion regions
675 677 GSkyRegions regions(filename);
... ... @@ -693,16 +695,75 @@ void ctskymap::map_exclusions_region(const GFilename&amp; filename)
693 695  
694 696  
695 697 /***********************************************************************//**
696   - * @brief Map events into a sky map
  698 + * @brief Fill maps from observation container
  699 + *
  700 + * Fill counts and optionally acceptance maps from data in the observation
  701 + * container.
  702 + ***************************************************************************/
  703 +void ctskymap::fill_maps(void)
  704 +{
  705 + // Write input observation container into logger
  706 + log_observations(NORMAL, m_obs, "Input observation");
  707 +
  708 + // Write header into logger
  709 + log_header1(TERSE, gammalib::number("Find unbinned observation",
  710 + m_obs.size()));
  711 +
  712 + // Find all unbinned CTA observations in m_obs
  713 + std::vector<GCTAObservation*> obs_list(0);
  714 + for (GCTAObservation* obs = first_unbinned_observation(); obs != NULL;
  715 + obs = next_unbinned_observation()) {
  716 +
  717 + // Push observation into list
  718 + obs_list.push_back(obs);
  719 +
  720 + // Write message
  721 + std::string msg = " Including unbinned "+obs->instrument()+
  722 + " observation";
  723 + log_string(NORMAL, msg);
  724 + }
  725 +
  726 + // Write header into logger
  727 + log_header1(TERSE, gammalib::number("Fill map from observation",
  728 + m_obs.size()));
  729 +
  730 + // Loop over all unbinned CTA observations in the container
  731 + #pragma omp parallel for
  732 + for (int i = 0; i < obs_list.size(); ++i) {
  733 +
  734 + // Get pointer to observation
  735 + GCTAObservation* obs = obs_list[i];
  736 +
  737 + // Fill events into counts map
  738 + fill_maps_counts(obs);
  739 +
  740 + // Optionally compute acceptance sky map
  741 + if (m_bkgsubtract != "NONE") {
  742 + fill_maps_acceptance(obs);
  743 + }
  744 +
  745 + // Dispose events to free memory
  746 + obs->dispose_events();
  747 +
  748 + } // endfor: looped over observations
  749 +
  750 + // Return
  751 + return;
  752 +}
  753 +
  754 +
  755 +/***********************************************************************//**
  756 + * @brief Fill events into counts map
697 757 *
698 758 * @param[in] obs CTA observation.
699 759 *
700 760 * @exception GException::no_list
701 761 * No event list found in observation.
702 762 *
703   - * This method maps the events found in a CTA events list into a sky map.
  763 + * Fills the events found in a CTA events list into a sky map. The method
  764 + * adds the events to the m_counts member.
704 765 ***************************************************************************/
705   -void ctskymap::map_events(GCTAObservation* obs)
  766 +void ctskymap::fill_maps_counts(GCTAObservation* obs)
706 767 {
707 768 // Get non-const pointer on a CTA event list
708 769 GCTAEventList* events = dynamic_cast<GCTAEventList*>
... ... @@ -745,11 +806,11 @@ void ctskymap::map_events(GCTAObservation* obs)
745 806 // Determine sky pixel
746 807 GCTAInstDir* inst = (GCTAInstDir*)&(event->dir());
747 808 GSkyDir dir = inst->dir();
748   - GSkyPixel pixel = m_skymap.dir2pix(dir);
  809 + GSkyPixel pixel = m_counts.dir2pix(dir);
749 810  
750 811 // Skip if pixel is out of range
751   - if (pixel.x() < -0.5 || pixel.x() > (m_skymap.nx() - 0.5) ||
752   - pixel.y() < -0.5 || pixel.y() > (m_skymap.ny() - 0.5)) {
  812 + if (pixel.x() < -0.5 || pixel.x() > (m_counts.nx() - 0.5) ||
  813 + pixel.y() < -0.5 || pixel.y() > (m_counts.ny() - 0.5)) {
753 814 num_outside_map++;
754 815 continue;
755 816 }
... ... @@ -762,7 +823,7 @@ void ctskymap::map_events(GCTAObservation* obs)
762 823  
763 824 // Fill event in skymap
764 825 #pragma omp critical(ctskymap_map_events)
765   - m_skymap(pixel) += 1.0;
  826 + m_counts(pixel) += 1.0;
766 827 num_in_map++;
767 828  
768 829 } // endfor: looped over all events
... ... @@ -779,7 +840,7 @@ void ctskymap::map_events(GCTAObservation* obs)
779 840  
780 841 // Write sky map into header
781 842 log_header1(EXPLICIT, "Sky map");
782   - log_string(EXPLICIT, m_skymap.print(m_chatter));
  843 + log_string(EXPLICIT, m_counts.print(m_chatter));
783 844 }
784 845  
785 846 // Return
... ... @@ -788,36 +849,7 @@ void ctskymap::map_events(GCTAObservation* obs)
788 849  
789 850  
790 851 /***********************************************************************//**
791   - * @brief Estimates the background in sky map for observation
792   - *
793   - * @param[in] obs CTA observation.
794   - *
795   - * Estimates the background in the sky map for a given observation and adds
796   - * this estimate to the background sky map. The background estimation method
797   - * is specified by the "bkgsubtract" parameter which can take the following
798   - * values:
799   - *
800   - * NONE - No background estimation
801   - * IRF - Background estimation based on IRF template
802   - * RING - Ring background estimation
803   - ***************************************************************************/
804   -void ctskymap::map_background(GCTAObservation* obs)
805   -{
806   - // Dispatch to appropriate background estimation method
807   - if (m_bkgsubtract == "IRF") {
808   - map_background_irf(obs);
809   - }
810   - else if (m_bkgsubtract == "RING") {
811   - map_background_irf(obs);
812   - }
813   -
814   - // Return
815   - return;
816   -}
817   -
818   -
819   -/***********************************************************************//**
820   - * @brief Estimates the background in sky map based on IRF template
  852 + * @brief Compute background acceptance sky map based on IRF template
821 853 *
822 854 * @param[in] obs CTA observation.
823 855 *
... ... @@ -825,11 +857,10 @@ void ctskymap::map_background(GCTAObservation* obs)
825 857 * No response information available for observation.
826 858 * No background template available in instrument response function.
827 859 *
828   - * Estimates the background in the sky map using the IRF template for a given
829   - * observation and adds this estimate to the background sky map. The IRF
830   - * template is integrated numerically in energy.
  860 + * Computes a background acceptance sky map using the IRF template for a
  861 + * given observation and adds the estimate to the acceptance sky map.
831 862 ***************************************************************************/
832   -void ctskymap::map_background_irf(GCTAObservation* obs)
  863 +void ctskymap::fill_maps_acceptance(GCTAObservation* obs)
833 864 {
834 865 // Get IRF response
835 866 const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>
... ... @@ -874,8 +905,8 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
874 905 // Initialise statistics
875 906 double total = 0.0;
876 907  
877   - // Loop over all background map pixels
878   - for (int i = 0; i < m_bkgmap.npix(); ++i) {
  908 + // Loop over all acceptance map pixels
  909 + for (int i = 0; i < m_acceptance.npix(); ++i) {
879 910  
880 911 // Get sky direction of pixel
881 912 GSkyDir& skydir = m_dirs[i];
... ... @@ -894,16 +925,16 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
894 925 // Multiply background rate with livetime and solid angle
895 926 value *= exposure * m_solidangle[i];
896 927  
897   - // Add number of background events to background map
898   - #pragma omp critical(ctskymap_map_background_irf)
899   - m_bkgmap(i) += value;
  928 + // Add number of background events to acceptance map
  929 + #pragma omp critical(ctskymap_map_acceptance)
  930 + m_acceptance(i) += value;
900 931  
901 932 // Update total number of background events
902 933 total += value;
903 934  
904   - } // endfor: looped over background map pixels
  935 + } // endfor: looped over acceptance map pixels
905 936  
906   - // Log background subtraction results
  937 + // Log acceptance results
907 938 #pragma omp critical(ctskymap_map_events)
908 939 {
909 940 log_value(NORMAL, "Events in background", int(total+0.5));
... ... @@ -915,14 +946,24 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
915 946  
916 947  
917 948 /***********************************************************************//**
918   - * @brief Compute the pixel significance if background subtraction is
919   - * requested
  949 + * @brief Compute sky map, background map and significance map
  950 + *
  951 + * Computes the sky map, background map and significance map. The following
  952 + * background subtraction methods are supported:
  953 + *
  954 + * NONE
  955 + * ====
920 956 *
921   - * Computes the pixel significance if a background subtraction method is
922   - * specified.
  957 + * The sky map is simply the binned counts map, no background and
  958 + * significance maps are computed.
923 959 *
924   - * For the @c IRF method Poisson statistic (in the Gaussian limit) is
925   - * assumed, and the significance \f$\sigma_i\f$ is computed using
  960 + * IRF
  961 + * ===
  962 + *
  963 + * The background map is the acceptance map, and this background map is
  964 + * subtracted from the counts map and assigned to the sky map. The
  965 + * significance \f$\sigma_i\f$ is computed assuming Poisson statistic in
  966 + * the Gaussian limit, using
926 967 *
927 968 * \f[\sigma_i = \frac{N_i - B_i}{\sqrt{N_i}}\f]
928 969 *
... ... @@ -930,6 +971,9 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
930 971 * \f$N_i\f$ is the number of observed counts and
931 972 * \f$B_i\f$ is the estimated number of background counts.
932 973 *
  974 + * RING
  975 + * ====
  976 + *
933 977 * For the @c RING method the Li & Ma significance is computed for each
934 978 * pixel, using the @c roiradius, @c inradius and @c outradius parameters
935 979 * to specify the radius of the On region and the Off background ring,
... ... @@ -941,21 +985,59 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
941 985 * map. Use FFT if the sky map is close to a cartesian grid, and use the
942 986 * direct method if the sky map shows important distortions.
943 987 ***************************************************************************/
944   -void ctskymap::map_significance(void)
  988 +void ctskymap::compute_maps(void)
945 989 {
946   - // Compute significance from "RING" method (Li & Ma eq. 17)
947   - if (m_bkgsubtract == "RING") {
  990 + // Write header into logger
  991 + log_header1(TERSE, "Compute maps");
  992 +
  993 + // If no background subtraction is requested then simply assign the
  994 + // counts map to the skymap
  995 + if (m_bkgsubtract == "NONE") {
  996 + m_skymap = m_counts;
  997 + }
  998 +
  999 + // else if IRF background is selected then use the acceptance as background
  1000 + else if (m_bkgsubtract == "IRF") {
  1001 + m_skymap = m_counts - m_acceptance;
  1002 + m_bkgmap = m_acceptance;
  1003 + m_sigmap = (m_counts - m_acceptance) / sqrt(m_counts);
  1004 + }
  1005 +
  1006 + // else if RING background is selected then compute the On
  1007 + else if (m_bkgsubtract == "RING") {
  1008 +
  1009 + // Compute initial RING background
948 1010 if (m_usefft) {
949   - map_significance_ring_fft();
  1011 + compute_maps_ring_fft();
950 1012 }
951 1013 else {
952   - map_significance_ring_direct();
  1014 + compute_maps_ring_direct();
953 1015 }
954   - }
955 1016  
956   - // Compute significance using Poisson statistics in the Gaussian limit
957   - else {
958   - m_sigmap = (m_skymap - m_bkgmap) / sqrt(m_skymap);
  1017 + // Store copy of exclusion regions
  1018 + GSkyMap exclmap = m_exclmap;
  1019 +
  1020 + // Iterative computation of exclusion regions
  1021 + for (int iter = 0; iter < m_iterations; ++iter) {
  1022 +
  1023 + // Set exclusion region from significance map
  1024 + m_exclmap = exclmap;
  1025 + for (int i = 0; i < m_exclmap.npix(); ++i) {
  1026 + if (m_sigmap(i) > m_threshold) {
  1027 + m_exclmap(i) = 1.0;
  1028 + }
  1029 + }
  1030 +
  1031 + // Re-compute RING background
  1032 + if (m_usefft) {
  1033 + compute_maps_ring_fft();
  1034 + }
  1035 + else {
  1036 + compute_maps_ring_direct();
  1037 + }
  1038 +
  1039 + } // endfor: looped over iterations
  1040 +
959 1041 }
960 1042  
961 1043 // Return
... ... @@ -964,77 +1046,79 @@ void ctskymap::map_significance(void)
964 1046  
965 1047  
966 1048 /***********************************************************************//**
967   - * @brief Compute the pixel significance for RING background
  1049 + * @brief Compute the maps for RING background using a FFT
968 1050 *
969 1051 * Computes the Li & Ma significance for each sky map pixel and replaces
970 1052 * the sky and background maps by the On- and Off-count maps. The computation
971   - * is done by computing the exact distances between pixels in the sky map.
  1053 + * is done using a FFT.
972 1054 ***************************************************************************/
973   -void ctskymap::map_significance_ring_direct(void)
  1055 +void ctskymap::compute_maps_ring_fft(void)
974 1056 {
975 1057 // Log message about what is being done
976   - log_header1(NORMAL, "Computing ring background map (direct method)");
977   - log_value(NORMAL, "Total pixels to process", m_skymap.npix());
  1058 + log_header3(NORMAL, "Computing ring background map (FFT method)");
978 1059  
979   - // Initialise On- and Off-count maps
980   - GSkyMap onmap = m_skymap;
981   - GSkyMap offmap = m_skymap;
982   - onmap = 0.0;
983   - offmap = 0.0;
  1060 + // Initialise maps
  1061 + m_skymap = m_counts;
  1062 + m_bkgmap = m_counts;
  1063 + m_sigmap = m_counts;
  1064 + m_skymap = 0.0;
  1065 + m_bkgmap = 0.0;
  1066 + m_sigmap = 0.0;
984 1067  
985 1068 // Initialise statistics
986 1069 int num_bad_alpha = 0;
987 1070  
988   - // Set progress logging frequency. By default the user is updated on the
989   - // progress when another 10% of pixels is processed, however, if there
990   - // are many pixels the user is updated each 100000 pixels.
991   - int n_logpix = m_skymap.npix() / 10;
992   - if (n_logpix > 100000) {
993   - n_logpix = 100000;
  1071 + // Set-up sky and acceptance maps with excluded pixels according to the
  1072 + // exclusion map
  1073 + GSkyMap excl_counts = m_counts;
  1074 + GSkyMap excl_acceptance = m_acceptance;
  1075 + for (int i = 0; i < m_counts.npix(); ++i) {
  1076 + if (m_exclmap(i) != 0.0) {
  1077 + excl_counts(i) = 0.0;
  1078 + excl_acceptance(i) = 0.0;
  1079 + }
994 1080 }
995 1081  
996   - // Loop over sky map pixels
997   - #pragma omp parallel for
998   - for (int i = 0; i < m_skymap.npix(); ++i) {
  1082 + // Convolve maps for On-region and Off-region
  1083 + GSkyMap on_counts = ring_convolve(m_counts, 0.0, m_roiradius);
  1084 + GSkyMap on_alpha = ring_convolve(m_acceptance, 0.0, m_roiradius);
  1085 + GSkyMap off_counts = ring_convolve(excl_counts, m_inradius, m_outradius);
  1086 + GSkyMap off_alpha = ring_convolve(excl_acceptance, m_inradius, m_outradius);
999 1087  
1000   - // Initialise the on/off counts and alpha for this bin
1001   - double n_on = 0.0;
1002   - double n_off = 0.0;
1003   - double alpha = 0.0;
  1088 + // Compute maps by loop over sky map pixels
  1089 + for (int i = 0; i < m_sigmap.npix(); ++i) {
1004 1090  
1005   - // Update user about progress (only if there are more than 10000 pixels)
1006   - if (i % n_logpix == 0 && n_logpix > 10000) {
1007   - #pragma omp critical(ctskymap_map_significance_ring)
1008   - log_value(NORMAL, "Pixels remaining", m_skymap.npix()-i);
  1091 + // Get On-counts, Off-counts and alpha value for this bin
  1092 + double n_on = on_counts(i);
  1093 + double n_off = off_counts(i);
  1094 + double alpha;
  1095 + if (off_alpha(i) == 0.0) {
  1096 + alpha = 0.0;
  1097 + n_on = 0.0;
  1098 + n_off = 0.0;
  1099 + }
  1100 + else {
  1101 + alpha = on_alpha(i) / off_alpha(i);
1009 1102 }
1010   -
1011   - // Compute the alpha and counts for this bin
1012   - compute_ring_values(i, m_skymap, m_bkgmap, n_on, n_off, alpha);
1013 1103  
1014 1104 // Store the On and alpha-weighted Off-counts
1015   - onmap(i) = n_on;
1016   - offmap(i) = alpha * n_off;
  1105 + m_bkgmap(i) = alpha * n_off;
  1106 + m_skymap(i) = n_on - m_bkgmap(i);
1017 1107  
1018 1108 // If alpha is zero then increment the bad alpha counter
1019 1109 if (alpha == 0.0) {
1020 1110 num_bad_alpha++;
1021   - #pragma omp critical(ctskymap_map_significance_ring)
1022   - m_sigmap(i) = 0.0;
1023 1111 }
1024 1112  
1025   - // ... otherwise store the results
  1113 + // ... otherwise compute and store the results
1026 1114 else {
1027   -
1028   - // Compute and store significance (Li & Ma eq. 17)
1029   - double sigma = sigma_li_ma(n_on, n_off, alpha);
1030   - #pragma omp critical(ctskymap_map_significance_ring)
1031   - m_sigmap(i) = sigma;
1032   -
1033   - } // endelse: alpha was non-zero
  1115 + m_sigmap(i) = sigma_li_ma(n_on, n_off, alpha);
  1116 + }
1034 1117  
1035 1118 } // endfor: looped over all pixels
1036 1119  
1037 1120 // Log the number of bad-alpha bins
  1121 + log_value(NORMAL, "Total number of pixels", m_sigmap.npix());
1038 1122 log_value(NORMAL, "Pixels with alpha=0", num_bad_alpha);
1039 1123  
1040 1124 // Now take the square root. Since some bins can be negative, first
... ... @@ -1042,11 +1126,6 @@ void ctskymap::map_significance_ring_direct(void)
1042 1126 // each bin by its sign to preserve the +/- significance.
1043 1127 m_sigmap = sign(m_sigmap) * sqrt(abs(m_sigmap));
1044 1128  
1045   - // Terminate by storing the on-counts map as sky map and the off-counts
1046   - // map as background map
1047   - m_skymap = onmap;
1048   - m_bkgmap = offmap;
1049   -
1050 1129 // Return
1051 1130 return;
1052 1131 }
... ... @@ -1057,68 +1136,75 @@ void ctskymap::map_significance_ring_direct(void)
1057 1136 *
1058 1137 * Computes the Li & Ma significance for each sky map pixel and replaces
1059 1138 * the sky and background maps by the On- and Off-count maps. The computation
1060   - * is done using a FFT.
  1139 + * is done by computing the exact distances between pixels in the sky map.
1061 1140 ***************************************************************************/
1062   -void ctskymap::map_significance_ring_fft(void)
  1141 +void ctskymap::compute_maps_ring_direct(void)
1063 1142 {
1064 1143 // Log message about what is being done
1065   - log_header1(NORMAL, "Computing ring background map (FFT method)");
  1144 + log_header3(NORMAL, "Computing ring background map (direct method)");
  1145 + log_value(NORMAL, "Total pixels to process", m_counts.npix());
  1146 +
  1147 + // Initialise maps
  1148 + m_skymap = m_counts;
  1149 + m_bkgmap = m_counts;
  1150 + m_sigmap = m_counts;
  1151 + m_skymap = 0.0;
  1152 + m_bkgmap = 0.0;
  1153 + m_sigmap = 0.0;
1066 1154  
1067 1155 // Initialise statistics
1068 1156 int num_bad_alpha = 0;
1069 1157  
1070   - // Set-up sky and acceptance maps with excluded pixels according to the
1071   - // exclusion map
1072   - GSkyMap skymap = m_skymap;
1073   - GSkyMap acceptance = m_bkgmap;
1074   - for (int i = 0; i < m_skymap.npix(); ++i) {
1075   - if (m_exclmap(i) != 0.0) {
1076   - skymap(i) = 0.0;
1077   - acceptance(i) = 0.0;
1078   - }
  1158 + // Set progress logging frequency. By default the user is updated on the
  1159 + // progress when another 10% of pixels is processed, however, if there
  1160 + // are many pixels the user is updated each 100000 pixels.
  1161 + int n_logpix = m_counts.npix() / 10;
  1162 + if (n_logpix > 100000) {
  1163 + n_logpix = 100000;
1079 1164 }
1080 1165  
1081   - // Convolve maps for On-region and Off-region
1082   - GSkyMap on_counts = ring_convolve(skymap, 0.0, m_roiradius);
1083   - GSkyMap on_alpha = ring_convolve(acceptance, 0.0, m_roiradius);
1084   - GSkyMap off_counts = ring_convolve(skymap, m_inradius, m_outradius);
1085   - GSkyMap off_alpha = ring_convolve(acceptance, m_inradius, m_outradius);
  1166 + // Compute maps by loop over sky map pixels
  1167 + #pragma omp parallel for
  1168 + for (int i = 0; i < m_sigmap.npix(); ++i) {
1086 1169  
1087   - // Loop over sky map pixels
1088   - for (int i = 0; i < m_skymap.npix(); ++i) {
  1170 + // Initialise the on/off counts and alpha for this bin
  1171 + double n_on = 0.0;
  1172 + double n_off = 0.0;
  1173 + double alpha = 0.0;
1089 1174  
1090   - // Get On-counts, Off-counts and alpha value for this bin
1091   - double n_on = on_counts(i);
1092   - double n_off = off_counts(i);
1093   - double alpha;
1094   - if (off_alpha(i) == 0.0) {
1095   - alpha = 0.0;
1096   - n_on = 0.0;
1097   - n_off = 0.0;
1098   - }
1099   - else {
1100   - alpha = on_alpha(i) / off_alpha(i);
  1175 + // Update user about progress (only if there are more than 10000 pixels)
  1176 + if (i % n_logpix == 0 && n_logpix > 10000) {
  1177 + #pragma omp critical(ctskymap_map_significance_ring)
  1178 + log_value(NORMAL, "Pixels remaining", m_sigmap.npix()-i);
1101 1179 }
1102 1180  
  1181 + // Compute the alpha and counts for this bin
  1182 + compute_ring_values(i, m_counts, m_acceptance, n_on, n_off, alpha);
  1183 +
1103 1184 // Store the On and alpha-weighted Off-counts
1104   - on_counts(i) = n_on;
1105   - off_counts(i) = alpha * n_off;
  1185 + m_bkgmap(i) = alpha * n_off;
  1186 + m_skymap(i) = n_on - m_bkgmap(i);
1106 1187  
1107 1188 // If alpha is zero then increment the bad alpha counter
1108 1189 if (alpha == 0.0) {
1109   - m_sigmap(i) = 0.0;
1110 1190 num_bad_alpha++;
  1191 + #pragma omp critical(ctskymap_compute_maps_ring_direct)
  1192 + m_sigmap(i) = 0.0;
1111 1193 }
1112 1194  
1113   - // ... otherwise compute and store the results
  1195 + // ... otherwise store the results
1114 1196 else {
1115   - m_sigmap(i) = sigma_li_ma(n_on, n_off, alpha);
1116   - }
  1197 +
  1198 + // Compute and store significance (Li & Ma eq. 17)
  1199 + double sigma = sigma_li_ma(n_on, n_off, alpha);
  1200 + #pragma omp critical(ctskymap_compute_maps_ring_direct)
  1201 + m_sigmap(i) = sigma;
  1202 +
  1203 + } // endelse: alpha was non-zero
1117 1204  
1118 1205 } // endfor: looped over all pixels
1119 1206  
1120 1207 // Log the number of bad-alpha bins
1121   - log_value(NORMAL, "Total number of pixels", m_skymap.npix());
1122 1208 log_value(NORMAL, "Pixels with alpha=0", num_bad_alpha);
1123 1209  
1124 1210 // Now take the square root. Since some bins can be negative, first
... ... @@ -1126,11 +1212,6 @@ void ctskymap::map_significance_ring_fft(void)
1126 1212 // each bin by its sign to preserve the +/- significance.
1127 1213 m_sigmap = sign(m_sigmap) * sqrt(abs(m_sigmap));
1128 1214  
1129   - // Terminate by storing the on-counts map as sky map and the off-counts
1130   - // map as background map
1131   - m_skymap = on_counts;
1132   - m_bkgmap = off_counts;
1133   -
1134 1215 // Return
1135 1216 return;
1136 1217 }
... ... @@ -1281,21 +1362,33 @@ void ctskymap::ring_bounding_box(const int&amp; ipixel, int&amp; ix1, int&amp; ix2,
1281 1362 int& iy1, int& iy2) const
1282 1363 {
1283 1364 // Get number of pixels in x and y direction
1284   - int nx = m_skymap.nx();
1285   - int ny = m_skymap.ny();
  1365 + int nx = m_counts.nx();
  1366 + int ny = m_counts.ny();
1286 1367  
1287 1368 // Get x and y index of pixel to consider
1288 1369 int ix0 = ipixel % nx;
1289 1370 int iy0 = ipixel / nx;
1290 1371  
  1372 + // Get pointer on WCS projection
  1373 + const GWcs* wcs = dynamic_cast<const GWcs*>(m_counts.projection());
  1374 + if (wcs == NULL) {
  1375 + std::string msg = "Sky map is not a WCS projection. Method is only "
  1376 + "valid for WCS projections.";
  1377 + throw GException::invalid_value(G_RING_BOUNDING_BOX, msg);
  1378 + }
  1379 +
  1380 + // Get X and Y step size
  1381 + double dx_binsz = wcs->cdelt(0);
  1382 + double dy_binsz = wcs->cdelt(1);
  1383 +
1291 1384 // Compute pixel increment in x-direction
1292 1385 double dx = 0.0;
1293 1386 int ix = (ix0 > 0) ? ix0 - 1 : ix0 + 1;
1294 1387 if (ix < nx) {
1295 1388 dx = m_dirs[ipixel].dist_deg(m_dirs[ix+iy0*nx]);
1296 1389 }
1297   - if (dx < m_binsz) {
1298   - dx = m_binsz;
  1390 + if (dx < dx_binsz) {
  1391 + dx = dx_binsz;
1299 1392 }
1300 1393  
1301 1394 // Compute pixel increment in y-direction
... ... @@ -1304,8 +1397,8 @@ void ctskymap::ring_bounding_box(const int&amp; ipixel, int&amp; ix1, int&amp; ix2,
1304 1397 if (iy < ny) {
1305 1398 dx = m_dirs[ipixel].dist_deg(m_dirs[ix0+iy*nx]);
1306 1399 }
1307   - if (dy < m_binsz) {
1308   - dy = m_binsz;
  1400 + if (dy < dy_binsz) {
  1401 + dy = dy_binsz;
1309 1402 }
1310 1403  
1311 1404 // Compute bounding box half size in x and y. The outer radius is
... ... @@ -1410,7 +1503,7 @@ GSkyMap ctskymap::ring_convolve(const GSkyMap&amp; map, const double&amp; rmin,
1410 1503 GNdarray ctskymap::ring_kernel(const double& rmin, const double& rmax) const
1411 1504 {
1412 1505 // Get pointer on WCS projection
1413   - const GWcs* wcs = dynamic_cast<const GWcs*>(m_skymap.projection());
  1506 + const GWcs* wcs = dynamic_cast<const GWcs*>(m_counts.projection());
1414 1507 if (wcs == NULL) {
1415 1508 std::string msg = "Sky map is not a WCS projection. Method is only "
1416 1509 "valid for WCS projections.";
... ... @@ -1422,24 +1515,24 @@ GNdarray ctskymap::ring_kernel(const double&amp; rmin, const double&amp; rmax) const
1422 1515 double dy = wcs->cdelt(1);
1423 1516  
1424 1517 // Initialise kernel
1425   - GNdarray kern(m_skymap.nx(), m_skymap.ny());
  1518 + GNdarray kern(m_counts.nx(), m_counts.ny());
1426 1519  
1427 1520 // Fill kernel
1428   - for (int ix1 = 0, ix2 = m_skymap.nx(); ix1 < m_skymap.nx(); ++ix1, --ix2) {
  1521 + for (int ix1 = 0, ix2 = m_counts.nx(); ix1 < m_counts.nx(); ++ix1, --ix2) {
1429 1522 double x = ix1 * dx;
1430 1523 double xqs = x * x;
1431   - for (int iy1 = 0, iy2 = m_skymap.ny(); iy1 < m_skymap.ny(); ++iy1, --iy2) {
  1524 + for (int iy1 = 0, iy2 = m_counts.ny(); iy1 < m_counts.ny(); ++iy1, --iy2) {
1432 1525 double y = iy1 * dy;
1433 1526 double r = std::sqrt(xqs + y*y);
1434 1527 if ((r >= rmin) && (r <= rmax)) {
1435 1528 kern(ix1,iy1) += 1.0;
1436   - if (ix2 < m_skymap.nx()) {
  1529 + if (ix2 < m_counts.nx()) {
1437 1530 kern(ix2,iy1) += 1.0;
1438 1531 }
1439   - if (iy2 < m_skymap.ny()) {
  1532 + if (iy2 < m_counts.ny()) {
1440 1533 kern(ix1,iy2) += 1.0;
1441 1534 }
1442   - if ((ix2 < m_skymap.nx()) && (iy2 < m_skymap.ny())) {
  1535 + if ((ix2 < m_counts.nx()) && (iy2 < m_counts.ny())) {
1443 1536 kern(ix2,iy2) += 1.0;
1444 1537 }
1445 1538 }
... ... @@ -1486,6 +1579,34 @@ double ctskymap::sigma_li_ma(const double&amp; n_on,
1486 1579  
1487 1580  
1488 1581 /***********************************************************************//**
  1582 + * @brief Write sky map into FITS file
  1583 + *
  1584 + * @param[in,out] fits FITS file.
  1585 + * @param[in] map Sky map.
  1586 + * @param[in] extname Extension name.
  1587 + *
  1588 + * Write one sky map with the extension name and keywords into the FITS file.
  1589 + ***************************************************************************/
  1590 +void ctskymap::write_map(GFits& fits, const GSkyMap& map, const std::string& extname) const
  1591 +{
  1592 + // Write map into FITS file
  1593 + GFitsHDU* hdu = map.write(fits);
  1594 +
  1595 + // Set map extension name
  1596 + if (hdu != NULL) {
  1597 + hdu->extname(extname);
  1598 + }
  1599 +
  1600 + // Write keywords into map extension
  1601 + write_ogip_keywords(hdu);
  1602 + write_hdu_keywords(hdu);
  1603 +
  1604 + // Return
  1605 + return;
  1606 +}
  1607 +
  1608 +
  1609 +/***********************************************************************//**
1489 1610 * @brief Write keywords in FITS HDU
1490 1611 *
1491 1612 * @param[in,out] hdu Pointer to FITS HDU.
... ... @@ -1505,9 +1626,13 @@ void ctskymap::write_hdu_keywords(GFitsHDU* hdu) const
1505 1626  
1506 1627 // Write RING background keywords
1507 1628 if (m_bkgsubtract == "RING") {
1508   - hdu->card("ROIRAD", m_roiradius, "[deg] Source region radius");
1509   - hdu->card("INRAD", m_inradius, "[deg] Inner background ring radius");
1510   - hdu->card("OUTRAD", m_outradius, "[deg] Outer background ring radius");
  1629 + hdu->card("USEFFT", m_usefft, "Use FFT for RING background");
  1630 + hdu->card("ROIRAD", m_roiradius, "[deg] Source region radius");
  1631 + hdu->card("INRAD", m_inradius, "[deg] Inner background ring radius");
  1632 + hdu->card("OUTRAD", m_outradius, "[deg] Outer background ring radius");
  1633 + hdu->card("ITER", m_iterations, "Exclusion map iterations");
  1634 + hdu->card("THRES", m_threshold, "Exclusion map threshold");
  1635 + hdu->card("EXCLMAP", m_inexclusion.url(), "Exclusion map name");
1511 1636 }
1512 1637  
1513 1638 } // endif: pointer was valid
... ...
src/ctskymap/ctskymap.hpp
... ... @@ -69,15 +69,15 @@ protected:
69 69 void free_members(void);
70 70 void get_parameters(void);
71 71 void setup_maps(void);
72   - void map_exclusions(const GFilename& filename);
73   - void map_exclusions_fits(const GFilename& filename);
74   - void map_exclusions_region(const GFilename& filename);
75   - void map_events(GCTAObservation* obs);
76   - void map_background(GCTAObservation* obs);
77   - void map_background_irf(GCTAObservation* obs);
78   - void map_significance(void);
79   - void map_significance_ring_direct(void);
80   - void map_significance_ring_fft(void);
  72 + void setup_exclusion_map(const GFilename& filename);
  73 + void setup_exclusion_map_fits(const GFilename& filename);
  74 + void setup_exclusion_map_region(const GFilename& filename);
  75 + void fill_maps(void);
  76 + void fill_maps_counts(GCTAObservation* obs);
  77 + void fill_maps_acceptance(GCTAObservation* obs);
  78 + void compute_maps(void);
  79 + void compute_maps_ring_fft(void);
  80 + void compute_maps_ring_direct(void);
81 81 void compute_ring_values(const int& ipixel,
82 82 const GSkyMap& counts,
83 83 const GSkyMap& background,
... ... @@ -92,26 +92,33 @@ protected:
92 92 double sigma_li_ma(const double& n_on,
93 93 const double& n_off,
94 94 const double& alpha) const;
  95 + void write_map(GFits& fits,
  96 + const GSkyMap& map,
  97 + const std::string& extname) const;
95 98 void write_hdu_keywords(GFitsHDU* hdu) const;
96 99  
97 100 // User parameters
98 101 GFilename m_outmap; //!< Output file name
99 102 GFilename m_inexclusion; //!< Exclusion map file name
100   - double m_binsz; //!< Sky map binsize
101 103 double m_emin; //!< Minimum energy (TeV)
102 104 double m_emax; //!< Maximum energy (TeV)
103 105 std::string m_bkgsubtract; //!< Background subtraction method
104 106 double m_roiradius; //!< Region of interest radius for RING bkg.
105 107 double m_inradius; //!< Inner ring radius for RING background
106 108 double m_outradius; //!< Outer ring radius for RING background
  109 + int m_iterations; //!< Number of iterations for RING background
  110 + double m_threshold; //!< Threshold for RING background
107 111 bool m_usefft; //!< Use FFT for RING background
108 112 bool m_publish; //!< Publish sky map?
109 113 GChatter m_chatter; //!< Chattiness
110 114  
111 115 // Protected members
  116 + bool m_has_inmap; //!< Has valid input map
112 117 GSkyMap m_skymap; //!< Sky map
113 118 GSkyMap m_bkgmap; //!< Background map
114 119 GSkyMap m_sigmap; //!< Significance map
  120 + GSkyMap m_counts; //!< Counts map
  121 + GSkyMap m_acceptance; //!< Acceptance map
115 122 GSkyMap m_exclmap; //!< Exclusion map for RING background
116 123  
117 124 // Caching variables to prevent multiple computations of the same thing
... ...
src/ctskymap/ctskymap.par
... ... @@ -19,6 +19,7 @@
19 19 inobs, f, a, events.fits,,, "Input event list or observation definition XML file"
20 20 caldb, s, a, prod2,,, "Calibration database"
21 21 irf, s, a, South_0.5h,,, "Instrument response function"
  22 +inmap, f, h, NONE,,, "Input skymap file"
22 23 outmap, f, a, skymap.fits,,, "Output skymap file"
23 24  
24 25 #
... ... @@ -46,8 +47,10 @@ bkgsubtract, s, a, NONE, NONE|IRF|RING,, &quot;Background subtraction method&quot;
46 47 roiradius, r, a, 0.2,,, "Source region radius for estimating on-counts (degrees)"
47 48 inradius, r, a, 0.6,,, "Inner background ring radius (degrees)"
48 49 outradius, r, a, 0.8,,, "Outer background ring radius (degrees)"
  50 +iterations, i, a, 0,0,100, "Number of iterations for exclusion regions computation"
  51 +threshold, r, a, 5.0,,, "Significance threshold for exclusion regions computation"
49 52 inexclusion, f, h, NONE,,, "Exclusion map or DS9 region file name"
50   -usefft, b, h, yes,,, "Use FFT for ring computation?"
  53 +usefft, b, h, yes,,, "Use FFT for ring computation?"
51 54  
52 55 #
53 56 # Standard parameters
... ...
src/support/ctobservation.cpp
... ... @@ -319,6 +319,48 @@ const GCTAObservation* ctobservation::next_unbinned_observation(void) const
319 319  
320 320  
321 321 /***********************************************************************//**
  322 + * @brief Read OGIP keywords from FITS HDU
  323 + *
  324 + * @param[in,out] hdu Pointer to FITS HDU.
  325 + *
  326 + * Read OGIP keywords from FITS HDU.
  327 + ***************************************************************************/
  328 +void ctobservation::read_ogip_keywords(GFitsHDU* hdu) const
  329 +{
  330 + // Continue only if pointer is valid
  331 + if (hdu != NULL) {
  332 +
  333 + // Get observation information
  334 + m_ogip_telescope = (hdu->has_card("TELESCOP")) ? hdu->string("TELESCOP") : "";
  335 +
  336 + // Get observation time information
  337 + std::string date_obs = (hdu->has_card("DATE-OBS")) ? hdu->string("DATE-OBS") : "";
  338 + std::string time_obs = (hdu->has_card("TIME-OBS")) ? hdu->string("TIME-OBS") : "";
  339 + std::string date_end = (hdu->has_card("DATE-END")) ? hdu->string("DATE-END") : "";
  340 + std::string time_end = (hdu->has_card("TIME-END")) ? hdu->string("TIME-END") : "";
  341 + m_ogip_telapse = (hdu->has_card("TELAPSE")) ? hdu->real("TELAPSE") : 0.0;
  342 + m_ogip_ontime = (hdu->has_card("ONTIME")) ? hdu->real("ONTIME") : 0.0;
  343 + m_ogip_livetime = (hdu->has_card("LIVETIME")) ? hdu->real("LIVETIME") : 0.0;
  344 + m_ogip_exposure = (hdu->has_card("EXPOSURE")) ? hdu->real("EXPOSURE") : 0.0;
  345 +
  346 + // Get OGIP start and stop time
  347 + std::string tstart = date_obs + "T" + time_obs;
  348 + std::string tstop = date_end + "T" + time_end;
  349 + if (tstart != "T") {
  350 + m_ogip_tstart.utc(tstart);
  351 + }
  352 + if (tstop != "T") {
  353 + m_ogip_tstop.utc(tstop);
  354 + }
  355 +
  356 + } // endif: pointer was valid
  357 +
  358 + // Return
  359 + return;
  360 +}
  361 +
  362 +
  363 +/***********************************************************************//**
322 364 * @brief Write OGIP keywords in FITS HDU
323 365 *
324 366 * @param[in,out] hdu Pointer to FITS HDU.
... ...
src/support/ctobservation.hpp
1 1 /***************************************************************************
2 2 * ctobservation - Base class for observation tools *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2016-2017 by Juergen Knoedlseder *
  4 + * copyright (C) 2016-2018 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -73,6 +73,7 @@ protected:
73 73 GCTAObservation* next_unbinned_observation(void);
74 74 const GCTAObservation* first_unbinned_observation(void) const;
75 75 const GCTAObservation* next_unbinned_observation(void) const;
  76 + void read_ogip_keywords(GFitsHDU* hdu) const;
76 77 void write_ogip_keywords(GFitsHDU* hdu) const;
77 78 void set_obs_statistic(const std::string& statistic);
78 79 void save_events_fits(void);
... ...
src/support/ctool.cpp
... ... @@ -650,7 +650,11 @@ GEbounds ctool::create_ebounds(void)
650 650 ***************************************************************************/
651 651 GSkyMap ctool::create_map(const GObservations& obs)
652 652 {
653   - // Read task parameters
  653 + // Read coordinate system and projection
  654 + std::string coordsys = (*this)["coordsys"].string();
  655 + std::string proj = (*this)["proj"].string();
  656 +
  657 + // Read sky map reference
654 658 double xref = 0.0;
655 659 double yref = 0.0;
656 660 bool usepnt = (*this)["usepnt"].boolean();
... ... @@ -658,11 +662,11 @@ GSkyMap ctool::create_map(const GObservations&amp; obs)
658 662 xref = (*this)["xref"].real();
659 663 yref = (*this)["yref"].real();
660 664 }
661   - std::string proj = (*this)["proj"].string();
662   - std::string coordsys = (*this)["coordsys"].string();
663   - double binsz = (*this)["binsz"].real();
664   - int nxpix = (*this)["nxpix"].integer();
665   - int nypix = (*this)["nypix"].integer();
  665 +
  666 + // Read sky map pixel size and number of bins
  667 + double binsz = (*this)["binsz"].real();
  668 + int nxpix = (*this)["nxpix"].integer();
  669 + int nypix = (*this)["nypix"].integer();
666 670  
667 671 // If requested, get pointing from observations
668 672 if (usepnt) {
... ...