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 * Version 1.6.0 released 3 * Version 1.6.0 released
4 ======================== 4 ========================
5 5
  6 + Allow specifying of input sky map in ctskymap (#2311)
6 Accelerate IRF and RING background methods in ctskymap (#2309) 7 Accelerate IRF and RING background methods in ctskymap (#2309)
7 8
8 9
1 New Features and Important Changes in ctools 1.6.0 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 Introduction 6 Introduction
@@ -106,7 +106,22 @@ None @@ -106,7 +106,22 @@ None
106 106
107 ctskymap - CTA sky mapping 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 cttsmap - Generation of Test Statistic map 127 cttsmap - Generation of Test Statistic map
doc/source/admin/release_history/1.6.rst
@@ -22,6 +22,8 @@ Bug fixes @@ -22,6 +22,8 @@ Bug fixes
22 Improvements 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 * [`2309 <https://cta-redmine.irap.omp.eu/issues/2309>`_] - 27 * [`2309 <https://cta-redmine.irap.omp.eu/issues/2309>`_] -
26 Accelerate `IRF` and `RING` background methods in :ref:`ctskymap` 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,6 +41,11 @@ General parameters
41 Instrument response function (only required for IRF background subtraction 41 Instrument response function (only required for IRF background subtraction
42 if no response information is provided by ``inobs``). 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 ``outmap [file]`` 49 ``outmap [file]``
45 Output sky map file. 50 Output sky map file.
46 51
@@ -90,6 +95,19 @@ Background subtraction configuration parameters @@ -90,6 +95,19 @@ Background subtraction configuration parameters
90 ``outradius [real]`` 95 ``outradius [real]``
91 Outer background ring radius for ``RING`` subtraction (in degrees). 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 ``(inexclusion = NONE) [file]`` 111 ``(inexclusion = NONE) [file]``
94 Exclusion region file as either a FITS map or DS9 region file. 112 Exclusion region file as either a FITS map or DS9 region file.
95 113
pyext/ctobservation.i
@@ -94,6 +94,7 @@ public: @@ -94,6 +94,7 @@ public:
94 // Make methods private in Python by prepending an underscore 94 // Make methods private in Python by prepending an underscore
95 %rename(_first_unbinned_observation) first_unbinned_observation; 95 %rename(_first_unbinned_observation) first_unbinned_observation;
96 %rename(_next_unbinned_observation) next_unbinned_observation; 96 %rename(_next_unbinned_observation) next_unbinned_observation;
  97 + %rename(_read_ogip_keywords) read_ogip_keywords;
97 %rename(_write_ogip_keywords) write_ogip_keywords; 98 %rename(_write_ogip_keywords) write_ogip_keywords;
98 %rename(_set_obs_statistic) set_obs_statistic; 99 %rename(_set_obs_statistic) set_obs_statistic;
99 %rename(_save_events_fits) save_events_fits; 100 %rename(_save_events_fits) save_events_fits;
src/ctskymap/ctskymap.cpp
@@ -35,6 +35,8 @@ @@ -35,6 +35,8 @@
35 #define G_GET_PARAMETERS "ctskymap::get_parameter()" 35 #define G_GET_PARAMETERS "ctskymap::get_parameter()"
36 #define G_MAP_EVENTS "ctskymap::map_events(GCTAObservation*)" 36 #define G_MAP_EVENTS "ctskymap::map_events(GCTAObservation*)"
37 #define G_MAP_BACKGROUND_IRF "ctskymap::map_background_irf(GCTAObservation*)" 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 #define G_RING_KERNEL "ctskymap::ring_kernel(double&, double&)" 40 #define G_RING_KERNEL "ctskymap::ring_kernel(double&, double&)"
39 41
40 /* __ Debug definitions __________________________________________________ */ 42 /* __ Debug definitions __________________________________________________ */
@@ -223,58 +225,14 @@ void ctskymap::run(void) @@ -223,58 +225,14 @@ void ctskymap::run(void)
223 // Setup maps 225 // Setup maps
224 setup_maps(); 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 // Optionally publish sky map 237 // Optionally publish sky map
280 if (m_publish) { 238 if (m_publish) {
@@ -289,8 +247,8 @@ void ctskymap::run(void) @@ -289,8 +247,8 @@ void ctskymap::run(void)
289 /***********************************************************************//** 247 /***********************************************************************//**
290 * @brief Save sky map 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 void ctskymap::save(void) 253 void ctskymap::save(void)
296 { 254 {
@@ -307,39 +265,31 @@ void ctskymap::save(void) @@ -307,39 +265,31 @@ void ctskymap::save(void)
307 GFits fits; 265 GFits fits;
308 266
309 // Write sky map into FITS file 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 if (m_bkgsubtract != "NONE") { 273 if (m_bkgsubtract != "NONE") {
319 274
320 // Write background map into FITS file 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 // Write significance map into FITS file 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 } // endif: background subtraction was requested 294 } // endif: background subtraction was requested
345 295
@@ -401,21 +351,25 @@ void ctskymap::init_members(void) @@ -401,21 +351,25 @@ void ctskymap::init_members(void)
401 // Initialise User parameters 351 // Initialise User parameters
402 m_outmap.clear(); 352 m_outmap.clear();
403 m_inexclusion.clear(); 353 m_inexclusion.clear();
404 - m_binsz = 0.0;  
405 m_emin = 0.0; 354 m_emin = 0.0;
406 m_emax = 0.0; 355 m_emax = 0.0;
407 m_bkgsubtract = "NONE"; 356 m_bkgsubtract = "NONE";
408 m_roiradius = 0.0; 357 m_roiradius = 0.0;
409 m_inradius = 0.0; 358 m_inradius = 0.0;
410 m_outradius = 0.0; 359 m_outradius = 0.0;
  360 + m_iterations = 0;
  361 + m_threshold = 5.0;
411 m_usefft = true; 362 m_usefft = true;
412 m_publish = false; 363 m_publish = false;
413 m_chatter = static_cast<GChatter>(2); 364 m_chatter = static_cast<GChatter>(2);
414 365
415 // Initialise members 366 // Initialise members
  367 + m_has_inmap = false;
416 m_skymap.clear(); 368 m_skymap.clear();
417 m_bkgmap.clear(); 369 m_bkgmap.clear();
418 m_sigmap.clear(); 370 m_sigmap.clear();
  371 + m_counts.clear();
  372 + m_acceptance.clear();
419 m_exclmap.clear(); 373 m_exclmap.clear();
420 374
421 // Initialise cache 375 // Initialise cache
@@ -440,22 +394,26 @@ void ctskymap::copy_members(const ctskymap&amp; app) @@ -440,22 +394,26 @@ void ctskymap::copy_members(const ctskymap&amp; app)
440 // Copy User parameters 394 // Copy User parameters
441 m_outmap = app.m_outmap; 395 m_outmap = app.m_outmap;
442 m_inexclusion = app.m_inexclusion; 396 m_inexclusion = app.m_inexclusion;
443 - m_binsz = app.m_binsz;  
444 m_emin = app.m_emin; 397 m_emin = app.m_emin;
445 m_emax = app.m_emax; 398 m_emax = app.m_emax;
446 m_bkgsubtract = app.m_bkgsubtract; 399 m_bkgsubtract = app.m_bkgsubtract;
447 m_roiradius = app.m_roiradius; 400 m_roiradius = app.m_roiradius;
448 m_inradius = app.m_inradius; 401 m_inradius = app.m_inradius;
449 m_outradius = app.m_outradius; 402 m_outradius = app.m_outradius;
  403 + m_iterations = app.m_iterations;
  404 + m_threshold = app.m_threshold;
450 m_usefft = app.m_usefft; 405 m_usefft = app.m_usefft;
451 m_publish = app.m_publish; 406 m_publish = app.m_publish;
452 m_chatter = app.m_chatter; 407 m_chatter = app.m_chatter;
453 408
454 // Copy members 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 // Copy cache 418 // Copy cache
461 m_solidangle = app.m_solidangle; 419 m_solidangle = app.m_solidangle;
@@ -490,17 +448,61 @@ void ctskymap::free_members(void) @@ -490,17 +448,61 @@ void ctskymap::free_members(void)
490 ***************************************************************************/ 448 ***************************************************************************/
491 void ctskymap::get_parameters(void) 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 m_bkgsubtract = (*this)["bkgsubtract"].string(); 506 m_bkgsubtract = (*this)["bkgsubtract"].string();
505 507
506 // Get RING background parameters 508 // Get RING background parameters
@@ -510,16 +512,20 @@ void ctskymap::get_parameters(void) @@ -510,16 +512,20 @@ void ctskymap::get_parameters(void)
510 m_roiradius = (*this)["roiradius"].real(); 512 m_roiradius = (*this)["roiradius"].real();
511 m_inradius = (*this)["inradius"].real(); 513 m_inradius = (*this)["inradius"].real();
512 m_outradius = (*this)["outradius"].real(); 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 m_inexclusion = (*this)["inexclusion"].filename(); 519 m_inexclusion = (*this)["inexclusion"].filename();
514 m_usefft = (*this)["usefft"].boolean(); 520 m_usefft = (*this)["usefft"].boolean();
515 521
516 // Make sure that (roiradius < inradius < outradius) 522 // Make sure that (roiradius < inradius < outradius)
517 if (m_roiradius > m_inradius) { 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 throw GException::invalid_value(G_GET_PARAMETERS, msg); 525 throw GException::invalid_value(G_GET_PARAMETERS, msg);
520 } 526 }
521 else if (m_inradius > m_outradius) { 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 throw GException::invalid_value(G_GET_PARAMETERS, msg); 529 throw GException::invalid_value(G_GET_PARAMETERS, msg);
524 } 530 }
525 531
@@ -527,7 +533,7 @@ void ctskymap::get_parameters(void) @@ -527,7 +533,7 @@ void ctskymap::get_parameters(void)
527 533
528 // If background subtraction is requested then make sure that the CTA 534 // If background subtraction is requested then make sure that the CTA
529 // observations in the observation container have response information 535 // observations in the observation container have response information
530 - if (m_bkgsubtract != "NONE") { 536 + if (m_bkgsubtract != "NONE" && !m_has_inmap) {
531 set_response(m_obs); 537 set_response(m_obs);
532 } 538 }
533 539
@@ -563,19 +569,15 @@ void ctskymap::setup_maps(void) @@ -563,19 +569,15 @@ void ctskymap::setup_maps(void)
563 // is requested 569 // is requested
564 if (m_bkgsubtract != "NONE") { 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 // Setup the exclusions map 572 // Setup the exclusions map
571 - map_exclusions(m_inexclusion); 573 + setup_exclusion_map(m_inexclusion);
572 574
573 // Cache the pixel solid angles and sky directions 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 } // endif: background subtraction selected 583 } // endif: background subtraction selected
@@ -601,10 +603,10 @@ void ctskymap::setup_maps(void) @@ -601,10 +603,10 @@ void ctskymap::setup_maps(void)
601 * background estimation. Pixels with values different from 0 will be 603 * background estimation. Pixels with values different from 0 will be
602 * excluded. 604 * excluded.
603 ***************************************************************************/ 605 ***************************************************************************/
604 -void ctskymap::map_exclusions(const GFilename& filename) 606 +void ctskymap::setup_exclusion_map(const GFilename& filename)
605 { 607 {
606 // Create exlusion map 608 // Create exlusion map
607 - m_exclmap = create_map(m_obs); 609 + m_exclmap = m_counts;
608 610
609 // Set all pixels to 0 (no pixel excluded) 611 // Set all pixels to 0 (no pixel excluded)
610 m_exclmap = 0.0; 612 m_exclmap = 0.0;
@@ -614,10 +616,10 @@ void ctskymap::map_exclusions(const GFilename&amp; filename) @@ -614,10 +616,10 @@ void ctskymap::map_exclusions(const GFilename&amp; filename)
614 616
615 // Fill the exclusions based on the regions supplied 617 // Fill the exclusions based on the regions supplied
616 if (filename.is_fits()) { 618 if (filename.is_fits()) {
617 - map_exclusions_fits(filename); 619 + setup_exclusion_map_fits(filename);
618 } 620 }
619 else { 621 else {
620 - map_exclusions_region(filename); 622 + setup_exclusion_map_region(filename);
621 } 623 }
622 624
623 } // endif: filename was valid 625 } // endif: filename was valid
@@ -635,7 +637,7 @@ void ctskymap::map_exclusions(const GFilename&amp; filename) @@ -635,7 +637,7 @@ void ctskymap::map_exclusions(const GFilename&amp; filename)
635 * Sets all exclusion map pixels to 1 that correspond to non-zero pixels in 637 * Sets all exclusion map pixels to 1 that correspond to non-zero pixels in
636 * the exclusion sky map FITS file. 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 // Load the fits image 642 // Load the fits image
641 GSkyMap inmap(filename); 643 GSkyMap inmap(filename);
@@ -669,7 +671,7 @@ void ctskymap::map_exclusions_fits(const GFilename&amp; filename) @@ -669,7 +671,7 @@ void ctskymap::map_exclusions_fits(const GFilename&amp; filename)
669 * Sets all exclusion map pixels to 1 that are contained in any of the DS9 671 * Sets all exclusion map pixels to 1 that are contained in any of the DS9
670 * regions. 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 // Load the exclusion regions 676 // Load the exclusion regions
675 GSkyRegions regions(filename); 677 GSkyRegions regions(filename);
@@ -693,16 +695,75 @@ void ctskymap::map_exclusions_region(const GFilename&amp; 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 * @param[in] obs CTA observation. 758 * @param[in] obs CTA observation.
699 * 759 *
700 * @exception GException::no_list 760 * @exception GException::no_list
701 * No event list found in observation. 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 // Get non-const pointer on a CTA event list 768 // Get non-const pointer on a CTA event list
708 GCTAEventList* events = dynamic_cast<GCTAEventList*> 769 GCTAEventList* events = dynamic_cast<GCTAEventList*>
@@ -745,11 +806,11 @@ void ctskymap::map_events(GCTAObservation* obs) @@ -745,11 +806,11 @@ void ctskymap::map_events(GCTAObservation* obs)
745 // Determine sky pixel 806 // Determine sky pixel
746 GCTAInstDir* inst = (GCTAInstDir*)&(event->dir()); 807 GCTAInstDir* inst = (GCTAInstDir*)&(event->dir());
747 GSkyDir dir = inst->dir(); 808 GSkyDir dir = inst->dir();
748 - GSkyPixel pixel = m_skymap.dir2pix(dir); 809 + GSkyPixel pixel = m_counts.dir2pix(dir);
749 810
750 // Skip if pixel is out of range 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 num_outside_map++; 814 num_outside_map++;
754 continue; 815 continue;
755 } 816 }
@@ -762,7 +823,7 @@ void ctskymap::map_events(GCTAObservation* obs) @@ -762,7 +823,7 @@ void ctskymap::map_events(GCTAObservation* obs)
762 823
763 // Fill event in skymap 824 // Fill event in skymap
764 #pragma omp critical(ctskymap_map_events) 825 #pragma omp critical(ctskymap_map_events)
765 - m_skymap(pixel) += 1.0; 826 + m_counts(pixel) += 1.0;
766 num_in_map++; 827 num_in_map++;
767 828
768 } // endfor: looped over all events 829 } // endfor: looped over all events
@@ -779,7 +840,7 @@ void ctskymap::map_events(GCTAObservation* obs) @@ -779,7 +840,7 @@ void ctskymap::map_events(GCTAObservation* obs)
779 840
780 // Write sky map into header 841 // Write sky map into header
781 log_header1(EXPLICIT, "Sky map"); 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 // Return 846 // Return
@@ -788,36 +849,7 @@ void ctskymap::map_events(GCTAObservation* obs) @@ -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 * @param[in] obs CTA observation. 854 * @param[in] obs CTA observation.
823 * 855 *
@@ -825,11 +857,10 @@ void ctskymap::map_background(GCTAObservation* obs) @@ -825,11 +857,10 @@ void ctskymap::map_background(GCTAObservation* obs)
825 * No response information available for observation. 857 * No response information available for observation.
826 * No background template available in instrument response function. 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 // Get IRF response 865 // Get IRF response
835 const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*> 866 const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>
@@ -874,8 +905,8 @@ void ctskymap::map_background_irf(GCTAObservation* obs) @@ -874,8 +905,8 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
874 // Initialise statistics 905 // Initialise statistics
875 double total = 0.0; 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 // Get sky direction of pixel 911 // Get sky direction of pixel
881 GSkyDir& skydir = m_dirs[i]; 912 GSkyDir& skydir = m_dirs[i];
@@ -894,16 +925,16 @@ void ctskymap::map_background_irf(GCTAObservation* obs) @@ -894,16 +925,16 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
894 // Multiply background rate with livetime and solid angle 925 // Multiply background rate with livetime and solid angle
895 value *= exposure * m_solidangle[i]; 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 // Update total number of background events 932 // Update total number of background events
902 total += value; 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 #pragma omp critical(ctskymap_map_events) 938 #pragma omp critical(ctskymap_map_events)
908 { 939 {
909 log_value(NORMAL, "Events in background", int(total+0.5)); 940 log_value(NORMAL, "Events in background", int(total+0.5));
@@ -915,14 +946,24 @@ void ctskymap::map_background_irf(GCTAObservation* obs) @@ -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 * \f[\sigma_i = \frac{N_i - B_i}{\sqrt{N_i}}\f] 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,6 +971,9 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
930 * \f$N_i\f$ is the number of observed counts and 971 * \f$N_i\f$ is the number of observed counts and
931 * \f$B_i\f$ is the estimated number of background counts. 972 * \f$B_i\f$ is the estimated number of background counts.
932 * 973 *
  974 + * RING
  975 + * ====
  976 + *
933 * For the @c RING method the Li & Ma significance is computed for each 977 * For the @c RING method the Li & Ma significance is computed for each
934 * pixel, using the @c roiradius, @c inradius and @c outradius parameters 978 * pixel, using the @c roiradius, @c inradius and @c outradius parameters
935 * to specify the radius of the On region and the Off background ring, 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,21 +985,59 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
941 * map. Use FFT if the sky map is close to a cartesian grid, and use the 985 * map. Use FFT if the sky map is close to a cartesian grid, and use the
942 * direct method if the sky map shows important distortions. 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 if (m_usefft) { 1010 if (m_usefft) {
949 - map_significance_ring_fft(); 1011 + compute_maps_ring_fft();
950 } 1012 }
951 else { 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 // Return 1043 // Return
@@ -964,77 +1046,79 @@ void ctskymap::map_significance(void) @@ -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 * Computes the Li & Ma significance for each sky map pixel and replaces 1051 * Computes the Li & Ma significance for each sky map pixel and replaces
970 * the sky and background maps by the On- and Off-count maps. The computation 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 // Log message about what is being done 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 // Initialise statistics 1068 // Initialise statistics
986 int num_bad_alpha = 0; 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 // Store the On and alpha-weighted Off-counts 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 // If alpha is zero then increment the bad alpha counter 1108 // If alpha is zero then increment the bad alpha counter
1019 if (alpha == 0.0) { 1109 if (alpha == 0.0) {
1020 num_bad_alpha++; 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 else { 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 } // endfor: looped over all pixels 1118 } // endfor: looped over all pixels
1036 1119
1037 // Log the number of bad-alpha bins 1120 // Log the number of bad-alpha bins
  1121 + log_value(NORMAL, "Total number of pixels", m_sigmap.npix());
1038 log_value(NORMAL, "Pixels with alpha=0", num_bad_alpha); 1122 log_value(NORMAL, "Pixels with alpha=0", num_bad_alpha);
1039 1123
1040 // Now take the square root. Since some bins can be negative, first 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,11 +1126,6 @@ void ctskymap::map_significance_ring_direct(void)
1042 // each bin by its sign to preserve the +/- significance. 1126 // each bin by its sign to preserve the +/- significance.
1043 m_sigmap = sign(m_sigmap) * sqrt(abs(m_sigmap)); 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 // Return 1129 // Return
1051 return; 1130 return;
1052 } 1131 }
@@ -1057,68 +1136,75 @@ void ctskymap::map_significance_ring_direct(void) @@ -1057,68 +1136,75 @@ void ctskymap::map_significance_ring_direct(void)
1057 * 1136 *
1058 * Computes the Li & Ma significance for each sky map pixel and replaces 1137 * Computes the Li & Ma significance for each sky map pixel and replaces
1059 * the sky and background maps by the On- and Off-count maps. The computation 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 // Log message about what is being done 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 // Initialise statistics 1155 // Initialise statistics
1068 int num_bad_alpha = 0; 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 // Store the On and alpha-weighted Off-counts 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 // If alpha is zero then increment the bad alpha counter 1188 // If alpha is zero then increment the bad alpha counter
1108 if (alpha == 0.0) { 1189 if (alpha == 0.0) {
1109 - m_sigmap(i) = 0.0;  
1110 num_bad_alpha++; 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 else { 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 } // endfor: looped over all pixels 1205 } // endfor: looped over all pixels
1119 1206
1120 // Log the number of bad-alpha bins 1207 // Log the number of bad-alpha bins
1121 - log_value(NORMAL, "Total number of pixels", m_skymap.npix());  
1122 log_value(NORMAL, "Pixels with alpha=0", num_bad_alpha); 1208 log_value(NORMAL, "Pixels with alpha=0", num_bad_alpha);
1123 1209
1124 // Now take the square root. Since some bins can be negative, first 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,11 +1212,6 @@ void ctskymap::map_significance_ring_fft(void)
1126 // each bin by its sign to preserve the +/- significance. 1212 // each bin by its sign to preserve the +/- significance.
1127 m_sigmap = sign(m_sigmap) * sqrt(abs(m_sigmap)); 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 // Return 1215 // Return
1135 return; 1216 return;
1136 } 1217 }
@@ -1281,21 +1362,33 @@ void ctskymap::ring_bounding_box(const int&amp; ipixel, int&amp; ix1, int&amp; ix2, @@ -1281,21 +1362,33 @@ void ctskymap::ring_bounding_box(const int&amp; ipixel, int&amp; ix1, int&amp; ix2,
1281 int& iy1, int& iy2) const 1362 int& iy1, int& iy2) const
1282 { 1363 {
1283 // Get number of pixels in x and y direction 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 // Get x and y index of pixel to consider 1368 // Get x and y index of pixel to consider
1288 int ix0 = ipixel % nx; 1369 int ix0 = ipixel % nx;
1289 int iy0 = ipixel / nx; 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 // Compute pixel increment in x-direction 1384 // Compute pixel increment in x-direction
1292 double dx = 0.0; 1385 double dx = 0.0;
1293 int ix = (ix0 > 0) ? ix0 - 1 : ix0 + 1; 1386 int ix = (ix0 > 0) ? ix0 - 1 : ix0 + 1;
1294 if (ix < nx) { 1387 if (ix < nx) {
1295 dx = m_dirs[ipixel].dist_deg(m_dirs[ix+iy0*nx]); 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 // Compute pixel increment in y-direction 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,8 +1397,8 @@ void ctskymap::ring_bounding_box(const int&amp; ipixel, int&amp; ix1, int&amp; ix2,
1304 if (iy < ny) { 1397 if (iy < ny) {
1305 dx = m_dirs[ipixel].dist_deg(m_dirs[ix0+iy*nx]); 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 // Compute bounding box half size in x and y. The outer radius is 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,7 +1503,7 @@ GSkyMap ctskymap::ring_convolve(const GSkyMap&amp; map, const double&amp; rmin,
1410 GNdarray ctskymap::ring_kernel(const double& rmin, const double& rmax) const 1503 GNdarray ctskymap::ring_kernel(const double& rmin, const double& rmax) const
1411 { 1504 {
1412 // Get pointer on WCS projection 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 if (wcs == NULL) { 1507 if (wcs == NULL) {
1415 std::string msg = "Sky map is not a WCS projection. Method is only " 1508 std::string msg = "Sky map is not a WCS projection. Method is only "
1416 "valid for WCS projections."; 1509 "valid for WCS projections.";
@@ -1422,24 +1515,24 @@ GNdarray ctskymap::ring_kernel(const double&amp; rmin, const double&amp; rmax) const @@ -1422,24 +1515,24 @@ GNdarray ctskymap::ring_kernel(const double&amp; rmin, const double&amp; rmax) const
1422 double dy = wcs->cdelt(1); 1515 double dy = wcs->cdelt(1);
1423 1516
1424 // Initialise kernel 1517 // Initialise kernel
1425 - GNdarray kern(m_skymap.nx(), m_skymap.ny()); 1518 + GNdarray kern(m_counts.nx(), m_counts.ny());
1426 1519
1427 // Fill kernel 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 double x = ix1 * dx; 1522 double x = ix1 * dx;
1430 double xqs = x * x; 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 double y = iy1 * dy; 1525 double y = iy1 * dy;
1433 double r = std::sqrt(xqs + y*y); 1526 double r = std::sqrt(xqs + y*y);
1434 if ((r >= rmin) && (r <= rmax)) { 1527 if ((r >= rmin) && (r <= rmax)) {
1435 kern(ix1,iy1) += 1.0; 1528 kern(ix1,iy1) += 1.0;
1436 - if (ix2 < m_skymap.nx()) { 1529 + if (ix2 < m_counts.nx()) {
1437 kern(ix2,iy1) += 1.0; 1530 kern(ix2,iy1) += 1.0;
1438 } 1531 }
1439 - if (iy2 < m_skymap.ny()) { 1532 + if (iy2 < m_counts.ny()) {
1440 kern(ix1,iy2) += 1.0; 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 kern(ix2,iy2) += 1.0; 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,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 * @brief Write keywords in FITS HDU 1610 * @brief Write keywords in FITS HDU
1490 * 1611 *
1491 * @param[in,out] hdu Pointer to FITS HDU. 1612 * @param[in,out] hdu Pointer to FITS HDU.
@@ -1505,9 +1626,13 @@ void ctskymap::write_hdu_keywords(GFitsHDU* hdu) const @@ -1505,9 +1626,13 @@ void ctskymap::write_hdu_keywords(GFitsHDU* hdu) const
1505 1626
1506 // Write RING background keywords 1627 // Write RING background keywords
1507 if (m_bkgsubtract == "RING") { 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 } // endif: pointer was valid 1638 } // endif: pointer was valid
src/ctskymap/ctskymap.hpp
@@ -69,15 +69,15 @@ protected: @@ -69,15 +69,15 @@ protected:
69 void free_members(void); 69 void free_members(void);
70 void get_parameters(void); 70 void get_parameters(void);
71 void setup_maps(void); 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 void compute_ring_values(const int& ipixel, 81 void compute_ring_values(const int& ipixel,
82 const GSkyMap& counts, 82 const GSkyMap& counts,
83 const GSkyMap& background, 83 const GSkyMap& background,
@@ -92,26 +92,33 @@ protected: @@ -92,26 +92,33 @@ protected:
92 double sigma_li_ma(const double& n_on, 92 double sigma_li_ma(const double& n_on,
93 const double& n_off, 93 const double& n_off,
94 const double& alpha) const; 94 const double& alpha) const;
  95 + void write_map(GFits& fits,
  96 + const GSkyMap& map,
  97 + const std::string& extname) const;
95 void write_hdu_keywords(GFitsHDU* hdu) const; 98 void write_hdu_keywords(GFitsHDU* hdu) const;
96 99
97 // User parameters 100 // User parameters
98 GFilename m_outmap; //!< Output file name 101 GFilename m_outmap; //!< Output file name
99 GFilename m_inexclusion; //!< Exclusion map file name 102 GFilename m_inexclusion; //!< Exclusion map file name
100 - double m_binsz; //!< Sky map binsize  
101 double m_emin; //!< Minimum energy (TeV) 103 double m_emin; //!< Minimum energy (TeV)
102 double m_emax; //!< Maximum energy (TeV) 104 double m_emax; //!< Maximum energy (TeV)
103 std::string m_bkgsubtract; //!< Background subtraction method 105 std::string m_bkgsubtract; //!< Background subtraction method
104 double m_roiradius; //!< Region of interest radius for RING bkg. 106 double m_roiradius; //!< Region of interest radius for RING bkg.
105 double m_inradius; //!< Inner ring radius for RING background 107 double m_inradius; //!< Inner ring radius for RING background
106 double m_outradius; //!< Outer ring radius for RING background 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 bool m_usefft; //!< Use FFT for RING background 111 bool m_usefft; //!< Use FFT for RING background
108 bool m_publish; //!< Publish sky map? 112 bool m_publish; //!< Publish sky map?
109 GChatter m_chatter; //!< Chattiness 113 GChatter m_chatter; //!< Chattiness
110 114
111 // Protected members 115 // Protected members
  116 + bool m_has_inmap; //!< Has valid input map
112 GSkyMap m_skymap; //!< Sky map 117 GSkyMap m_skymap; //!< Sky map
113 GSkyMap m_bkgmap; //!< Background map 118 GSkyMap m_bkgmap; //!< Background map
114 GSkyMap m_sigmap; //!< Significance map 119 GSkyMap m_sigmap; //!< Significance map
  120 + GSkyMap m_counts; //!< Counts map
  121 + GSkyMap m_acceptance; //!< Acceptance map
115 GSkyMap m_exclmap; //!< Exclusion map for RING background 122 GSkyMap m_exclmap; //!< Exclusion map for RING background
116 123
117 // Caching variables to prevent multiple computations of the same thing 124 // Caching variables to prevent multiple computations of the same thing
src/ctskymap/ctskymap.par
@@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
19 inobs, f, a, events.fits,,, "Input event list or observation definition XML file" 19 inobs, f, a, events.fits,,, "Input event list or observation definition XML file"
20 caldb, s, a, prod2,,, "Calibration database" 20 caldb, s, a, prod2,,, "Calibration database"
21 irf, s, a, South_0.5h,,, "Instrument response function" 21 irf, s, a, South_0.5h,,, "Instrument response function"
  22 +inmap, f, h, NONE,,, "Input skymap file"
22 outmap, f, a, skymap.fits,,, "Output skymap file" 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,8 +47,10 @@ bkgsubtract, s, a, NONE, NONE|IRF|RING,, &quot;Background subtraction method&quot;
46 roiradius, r, a, 0.2,,, "Source region radius for estimating on-counts (degrees)" 47 roiradius, r, a, 0.2,,, "Source region radius for estimating on-counts (degrees)"
47 inradius, r, a, 0.6,,, "Inner background ring radius (degrees)" 48 inradius, r, a, 0.6,,, "Inner background ring radius (degrees)"
48 outradius, r, a, 0.8,,, "Outer background ring radius (degrees)" 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 inexclusion, f, h, NONE,,, "Exclusion map or DS9 region file name" 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 # Standard parameters 56 # Standard parameters
src/support/ctobservation.cpp
@@ -319,6 +319,48 @@ const GCTAObservation* ctobservation::next_unbinned_observation(void) const @@ -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 * @brief Write OGIP keywords in FITS HDU 364 * @brief Write OGIP keywords in FITS HDU
323 * 365 *
324 * @param[in,out] hdu Pointer to FITS HDU. 366 * @param[in,out] hdu Pointer to FITS HDU.
src/support/ctobservation.hpp
1 /*************************************************************************** 1 /***************************************************************************
2 * ctobservation - Base class for observation tools * 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 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -73,6 +73,7 @@ protected: @@ -73,6 +73,7 @@ protected:
73 GCTAObservation* next_unbinned_observation(void); 73 GCTAObservation* next_unbinned_observation(void);
74 const GCTAObservation* first_unbinned_observation(void) const; 74 const GCTAObservation* first_unbinned_observation(void) const;
75 const GCTAObservation* next_unbinned_observation(void) const; 75 const GCTAObservation* next_unbinned_observation(void) const;
  76 + void read_ogip_keywords(GFitsHDU* hdu) const;
76 void write_ogip_keywords(GFitsHDU* hdu) const; 77 void write_ogip_keywords(GFitsHDU* hdu) const;
77 void set_obs_statistic(const std::string& statistic); 78 void set_obs_statistic(const std::string& statistic);
78 void save_events_fits(void); 79 void save_events_fits(void);
src/support/ctool.cpp
@@ -650,7 +650,11 @@ GEbounds ctool::create_ebounds(void) @@ -650,7 +650,11 @@ GEbounds ctool::create_ebounds(void)
650 ***************************************************************************/ 650 ***************************************************************************/
651 GSkyMap ctool::create_map(const GObservations& obs) 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 double xref = 0.0; 658 double xref = 0.0;
655 double yref = 0.0; 659 double yref = 0.0;
656 bool usepnt = (*this)["usepnt"].boolean(); 660 bool usepnt = (*this)["usepnt"].boolean();
@@ -658,11 +662,11 @@ GSkyMap ctool::create_map(const GObservations&amp; obs) @@ -658,11 +662,11 @@ GSkyMap ctool::create_map(const GObservations&amp; obs)
658 xref = (*this)["xref"].real(); 662 xref = (*this)["xref"].real();
659 yref = (*this)["yref"].real(); 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 // If requested, get pointing from observations 671 // If requested, get pointing from observations
668 if (usepnt) { 672 if (usepnt) {