ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
ctskymap.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * ctskymap - Sky mapping tool *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2011-2022 by Juergen Knoedlseder *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file ctskymap.cpp
23  * @brief Sky mapping tool implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <cstdio>
32 #include "ctskymap.hpp"
33 
34 /* __ Method name definitions ____________________________________________ */
35 #define G_GET_PARAMETERS "ctskymap::get_parameter()"
36 #define G_FILL_MAPS_COUNTS "ctskymap::fill_maps_counts(GCTAObservation*)"
37 #define G_FILL_MAPS_ACCEPTANCE "ctskymap::fill_maps_acceptance("\
38  "GCTAObservation*)"
39 #define G_RING_BOUNDING_BOX "ctskymap::ring_bounding_box(int&, int&, int&, "\
40  "int&, int&)"
41 #define G_RING_KERNEL "ctskymap::ring_kernel(double&, double&)"
42 
43 /* __ Debug definitions __________________________________________________ */
44 
45 /* __ Coding definitions _________________________________________________ */
46 
47 
48 /*==========================================================================
49  = =
50  = Constructors/destructors =
51  = =
52  ==========================================================================*/
53 
54 /***********************************************************************//**
55  * @brief Void constructor
56  *
57  * Constructs an empty sky mapping tool.
58  ***************************************************************************/
60 {
61  // Initialise members
62  init_members();
63 
64  // Return
65  return;
66 }
67 
68 
69 /***********************************************************************//**
70  * @brief Observations constructor
71  *
72  * param[in] obs Observation container.
73  *
74  * Constructs sky mapping tool from an observation container.
75  ***************************************************************************/
76 ctskymap::ctskymap(const GObservations& obs) :
77  ctobservation(CTSKYMAP_NAME, VERSION, obs)
78 {
79  // Initialise members
80  init_members();
81 
82  // Return
83  return;
84 }
85 
86 
87 
88 /***********************************************************************//**
89  * @brief Command line constructor
90  *
91  * @param[in] argc Number of arguments in command line.
92  * @param[in] argv Array of command line arguments.
93  *
94  * Constructs sky mapping tool using command line arguments for user
95  * parameter setting.
96  ***************************************************************************/
97 ctskymap::ctskymap(int argc, char *argv[]) :
98  ctobservation(CTSKYMAP_NAME, VERSION, argc, argv)
99 {
100  // Initialise members
101  init_members();
102 
103  // Return
104  return;
105 }
106 
107 
108 /***********************************************************************//**
109  * @brief Copy constructor
110  *
111  * @param[in] app Sky mapping tool.
112  ***************************************************************************/
114 {
115  // Initialise members
116  init_members();
117 
118  // Copy members
119  copy_members(app);
120 
121  // Return
122  return;
123 }
124 
125 
126 /***********************************************************************//**
127  * @brief Destructor
128  ***************************************************************************/
130 {
131  // Free members
132  free_members();
133 
134  // Return
135  return;
136 }
137 
138 
139 /*==========================================================================
140  = =
141  = Operators =
142  = =
143  ==========================================================================*/
144 
145 /***********************************************************************//**
146  * @brief Assignment operator
147  *
148  * @param[in] app Sky mapping tool.
149  * @return Sky mapping tool.
150  ***************************************************************************/
152 {
153  // Execute only if object is not identical
154  if (this != &app) {
155 
156  // Copy base class members
157  this->ctobservation::operator=(app);
158 
159  // Free members
160  free_members();
161 
162  // Initialise members
163  init_members();
164 
165  // Copy members
166  copy_members(app);
167 
168  } // endif: object was not identical
169 
170  // Return this object
171  return *this;
172 }
173 
174 
175 /*==========================================================================
176  = =
177  = Public methods =
178  = =
179  ==========================================================================*/
180 
181 /***********************************************************************//**
182  * @brief Clear sky mapping tool
183  *
184  * Clears sky mapping tool.
185  ***************************************************************************/
186 void ctskymap::clear(void)
187 {
188  // Free members
189  free_members();
190  this->ctool::free_members();
192 
193  // Clear base class (needed to conserve tool name and version)
194  this->GApplication::clear();
195 
196  // Initialise members
197  this->ctool::init_members();
199  init_members();
200 
201  // Write header into logger
202  log_header();
203 
204  // Return
205  return;
206 }
207 
208 
209 /***********************************************************************//**
210  * @brief Process the sky mapping tool
211  *
212  * Generates a sky map from event list by looping over all unbinned CTA
213  * observation in the observation container and filling all events into
214  * a sky map.
215  ***************************************************************************/
217 {
218  // Get parameters
219  get_parameters();
220 
221  // Setup maps
222  setup_maps();
223 
224  // If no input map exists then fill counts and acceptance maps from
225  // observation container
226  if (!m_has_inmap) {
227  fill_maps();
228  }
229 
230  // Compute maps
231  compute_maps();
232 
233  // Optionally publish sky map
234  if (m_publish) {
235  publish();
236  }
237 
238  // Create the output fits object
239  construct_fits();
240 
241  // Return
242  return;
243 }
244 
245 
246 /***********************************************************************//**
247  * @brief Save sky map
248  *
249  * Saves the sky map into a FITS file. The FITS file name is specified by
250  * the @p outname parameter.
251  ***************************************************************************/
252 void ctskymap::save(void)
253 {
254  // Write header
255  log_header1(TERSE, "Save sky map");
256 
257  // Save sky map if filename is valid and sky map is not empty
258  if ((*this)["outmap"].is_valid() && !m_skymap.is_empty()) {
259 
260  // Get sky map filename
261  m_outmap = (*this)["outmap"].filename();
262 
263  // Save FITS file to disk
264  m_fits.saveto(m_outmap, clobber());
265 
266  } // endif: filename and map were not empty
267 
268  // Write into logger what has been done
269  std::string fname = (m_outmap.is_empty()) ? "NONE" : m_outmap.url();
270  if (m_skymap.is_empty()) {
271  fname.append(" (map is empty, no file created)");
272  }
273  log_value(NORMAL, "Sky map file", fname);
274 
275  // Return
276  return;
277 }
278 
279 
280 /***********************************************************************//**
281  * @brief Construct GFits object consisting of all maps
282  *
283  * Assembles all of the skymaps into a GFits object. This object is saved
284  * when ctskymap::save() is called. the object can also be accessed by
285  * calling ctskymap::fits().
286  ***************************************************************************/
288 {
289  // Clear FITS object
290  m_fits.clear();
291 
292  // Write sky map into FITS object
293  write_map(m_fits, m_skymap, "SKYMAP");
294 
295  // If background subtraction is requested then write the background map,
296  // the significance map, the counts map and the acceptance map to the
297  // FITS file
298  if (m_bkgsubtract != "NONE") {
299 
300  // Write background map into FITS file
301  write_map(m_fits, m_bkgmap, "BACKGROUND");
302 
303  // Write significance map into FITS file
304  write_map(m_fits, m_sigmap, "SIGNIFICANCE");
305 
306  // Write counts map into FITS file
307  write_map(m_fits, m_counts, "COUNTS");
308 
309  // Write acceptance map into FITS file
310  write_map(m_fits, m_acceptance, "ACCEPTANCE");
311 
312  // If RING background subtraction is requested then write also
313  // the exclusion map to the FITS file
314  if (m_bkgsubtract == "RING") {
315  write_map(m_fits, m_exclmap, "EXCLUSION");
316  }
317 
318  } // endif: background subtraction was requested
319 
320  // Stamp FITS file
321  stamp(m_fits);
322 
323  // Return
324  return;
325 }
326 
327 
328 /***********************************************************************//**
329  * @brief Publish sky map
330  *
331  * @param[in] name Sky map name.
332  ***************************************************************************/
333 void ctskymap::publish(const std::string& name)
334 {
335  // Write header into logger
336  log_header1(TERSE, "Publish sky map");
337 
338  // Set default name if user name is empty
339  std::string user_name(name);
340  if (user_name.empty()) {
341  user_name = CTSKYMAP_NAME;
342  }
343 
344  // Write sky map name into logger
345  log_value(NORMAL, "Sky map name", user_name);
346 
347  // Publish sky map
348  m_skymap.publish(user_name);
349 
350  // Return
351  return;
352 }
353 
354 
355 /*==========================================================================
356  = =
357  = Private methods =
358  = =
359  ==========================================================================*/
360 
361 /***********************************************************************//**
362  * @brief Initialise class members
363  ***************************************************************************/
365 {
366  // Initialise User parameters
367  m_outmap.clear();
368  m_inexclusion.clear();
369  m_emin = 0.0;
370  m_emax = 0.0;
371  m_bkgsubtract = "NONE";
372  m_roiradius = 0.0;
373  m_inradius = 0.0;
374  m_outradius = 0.0;
375  m_iterations = 0;
376  m_threshold = 5.0;
377  m_usefft = true;
378  m_publish = false;
379  m_chatter = static_cast<GChatter>(2);
380 
381  // Initialise members
382  m_has_inmap = false;
383  m_skymap.clear();
384  m_bkgmap.clear();
385  m_sigmap.clear();
386  m_counts.clear();
387  m_acceptance.clear();
388  m_exclmap.clear();
389  m_fits.clear();
390 
391  // Initialise cache
392  m_solidangle.clear();
393  m_dirs.clear();
394  m_cos_roiradius = 1.0;
395  m_cos_inradius = 1.0;
396  m_cos_outradius = 1.0;
397 
398  // Return
399  return;
400 }
401 
402 
403 /***********************************************************************//**
404  * @brief Copy class members
405  *
406  * @param[in] app Application.
407  ***************************************************************************/
409 {
410  // Copy User parameters
411  m_outmap = app.m_outmap;
413  m_emin = app.m_emin;
414  m_emax = app.m_emax;
416  m_roiradius = app.m_roiradius;
417  m_inradius = app.m_inradius;
418  m_outradius = app.m_outradius;
420  m_threshold = app.m_threshold;
421  m_usefft = app.m_usefft;
422  m_publish = app.m_publish;
423  m_chatter = app.m_chatter;
424 
425  // Copy members
426  m_has_inmap = app.m_has_inmap;
427  m_skymap = app.m_skymap;
428  m_bkgmap = app.m_bkgmap;
429  m_sigmap = app.m_sigmap;
430  m_counts = app.m_counts;
432  m_exclmap = app.m_exclmap;
433  m_fits = app.m_fits;
434 
435  // Copy cache
437  m_dirs = app.m_dirs;
441 
442  // Return
443  return;
444 }
445 
446 
447 /***********************************************************************//**
448  * @brief Delete class members
449  ***************************************************************************/
451 {
452  // Return
453  return;
454 }
455 
456 
457 /***********************************************************************//**
458  * @brief Get application parameters
459  *
460  * Get all task parameters from parameter file or (if required) by querying
461  * the user. Most parameters are only required if no observation exists so
462  * far in the observation container. In this case, a single CTA observation
463  * will be added to the container, using the definition provided in the
464  * parameter file.
465  ***************************************************************************/
467 {
468  // So far we have no valid input map
469  m_has_inmap = false;
470 
471  // If an input map was specified then extract try loading the COUNTS
472  // and ACCEPTANCE extensions from that file
473  if ((*this)["inmap"].is_valid()) {
474 
475  // Read inmap filename
476  GFilename inmap = (*this)["inmap"].filename();
477 
478  // Open file
479  GFits fits(inmap);
480 
481  // Continue only if required extensions exist
482  if (fits.contains("COUNTS") && fits.contains("ACCEPTANCE")) {
483 
484  // Get HDUs
485  GFitsHDU &counts = *fits["COUNTS"];
486  GFitsHDU &acceptance = *fits["ACCEPTANCE"];
487 
488  // Read counts and acceptance map
489  m_counts.read(counts);
490  m_acceptance.read(acceptance);
491 
492  // Extract OGIP parameters
493  read_ogip_keywords(&counts);
494 
495  // Extract energy range
496  m_emin = (counts.has_card("E_MIN")) ? counts.real("E_MIN") : 0.0;
497  m_emax = (counts.has_card("E_MAX")) ? counts.real("E_MAX") : 0.0;
498 
499  // Signal availability of sky map
500  m_has_inmap = true;
501  }
502  else {
503  std::string msg = "Input sky map \""+inmap.url()+"\" does not "
504  "contain \"COUNTS\" and \"ACCEPTANCE\" "
505  "extensions.";
506  throw GException::invalid_value(G_GET_PARAMETERS, msg);
507  }
508  fits.close();
509  }
510 
511  // If no valid input map exists then request observations
512  if (!m_has_inmap) {
513 
514  // Setup observations from "inobs" parameter. Do not request response
515  // information and do not accept counts cubes.
516  setup_observations(m_obs, false, true, false);
517 
518  // Create counts and acceptance maps from the User parameters
521 
522  // Get further parameters
523  m_emin = (*this)["emin"].real();
524  m_emax = (*this)["emax"].real();
525 
526  } // endif: no valid input map existed
527 
528  // Get background method
529  m_bkgsubtract = (*this)["bkgsubtract"].string();
530 
531  // Get RING background parameters
532  if (m_bkgsubtract == "RING") {
533 
534  // Get (or query) parameters
535  m_roiradius = (*this)["roiradius"].real();
536  m_inradius = (*this)["inradius"].real();
537  m_outradius = (*this)["outradius"].real();
538  m_iterations = (*this)["iterations"].integer();
539  if (m_iterations > 0) {
540  m_threshold = (*this)["threshold"].real();
541  }
542 
543  (*this)["inexclusion"].query();
544  m_usefft = (*this)["usefft"].boolean();
545 
546  // Make sure that (roiradius < inradius < outradius)
547  if (m_roiradius > m_inradius) {
548  std::string msg("\"roiradius\" must be smaller than \"inradius\"");
549  throw GException::invalid_value(G_GET_PARAMETERS, msg);
550  }
551  else if (m_inradius > m_outradius) {
552  std::string msg("\"inradius\" must be smaller than \"outradius\"");
553  throw GException::invalid_value(G_GET_PARAMETERS, msg);
554  }
555 
556  } // endif: read RING background parameters
557 
558  // If background subtraction is requested then make sure that the CTA
559  // observations in the observation container have response information
560  if (m_bkgsubtract != "NONE" && !m_has_inmap) {
562  }
563 
564  // Get remaining parameters
565  m_publish = (*this)["publish"].boolean();
566  m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
567 
568  // If needed later, query output filename now
569  if (read_ahead()) {
570  (*this)["outmap"].query();
571  }
572 
573  // Write parameters into logger
574  log_parameters(TERSE);
575 
576  // Return
577  return;
578 }
579 
580 
581 /***********************************************************************//**
582  * @brief Setup maps
583  *
584  * Setup maps for sky map generation.
585  ***************************************************************************/
587 {
588  // Clear cache
589  m_solidangle.clear();
590  m_dirs.clear();
591 
592  // Create background map and significance map if background subtraction
593  // is requested
594  if (m_bkgsubtract != "NONE") {
595 
596  // Setup the exclusions map
597  if (m_exclmap.is_empty()) {
599  }
600  else {
602  }
603 
604  // Cache the pixel solid angles and sky directions
605  m_solidangle.reserve(m_counts.npix());
606  m_dirs.reserve(m_counts.npix());
607  for (int i = 0; i < m_counts.npix(); ++i) {
608  m_solidangle.push_back(m_counts.solidangle(i));
609  m_dirs.push_back(m_counts.inx2dir(i));
610  }
611 
612  } // endif: background subtraction selected
613 
614  // Compute cosine of ring radii for RING background method
615  if (m_bkgsubtract == "RING") {
616  m_cos_roiradius = std::cos(m_roiradius * gammalib::deg2rad);
617  m_cos_inradius = std::cos(m_inradius * gammalib::deg2rad);
618  m_cos_outradius = std::cos(m_outradius * gammalib::deg2rad);
619  }
620 
621  // Return
622  return;
623 }
624 
625 
626 /***********************************************************************//**
627  * @brief Generates map of pixel exclusions
628  *
629  * Generates a sky map of the pixels that are to be excluded from the
630  * background estimation. Pixels with values different from 0 will be
631  * excluded.
632  ***************************************************************************/
634 {
635  // Create exlusion map
637 
638  // Set all pixels to 0 (no pixel excluded)
639  m_exclmap = 0.0;
640 
641  // Generate map only if exclusion map filename is valid
642  if ((*this)["inexclusion"].is_valid()) {
643 
644  // Get exclusion map filename
645  m_inexclusion = (*this)["inexclusion"].filename();
646 
647  // Fill the exclusions based on the regions supplied
648  if (m_inexclusion.is_fits()) {
650  }
651  else {
653  }
654 
655  } // endif: filename was valid
656 
657  // Return
658  return;
659 }
660 
661 
662 /***********************************************************************//**
663  * @brief Fills exclusions map from FITS image
664  *
665  * @param[in] filename FITS image file name.
666  *
667  * Sets all exclusion map pixels to 1 that correspond to non-zero pixels in
668  * the exclusion sky map FITS file.
669  ***************************************************************************/
670 void ctskymap::setup_exclusion_map_fits(const GFilename& filename)
671 {
672  // Load the fits image to the exclusion map
673  m_exclmap = GSkyMap(filename);
674 
675  // Verify exclusion map
677 
678  // Return
679  return;
680 }
681 
682 
683 /***********************************************************************//**
684  * @brief Fills exclusions map from DS9 region file
685  *
686  * @param[in] filename DS9 region file name.
687  *
688  * Sets all exclusion map pixels to 1 that are contained in any of the DS9
689  * regions.
690  ***************************************************************************/
691 void ctskymap::setup_exclusion_map_region(const GFilename& filename)
692 {
693  // Load the exclusion regions
694  GSkyRegions regions(filename);
695 
696  // Loop through the individual pixels in the exclusion map
697  for (int i = 0; i < m_exclmap.npix(); ++i) {
698 
699  // Get the pixel position
700  GSkyDir dir = m_exclmap.pix2dir(i);
701 
702  // If pixel position overlaps with the regions
703  if (regions.contains(dir)) {
704  m_exclmap(i) = 1.0;
705  }
706 
707  } // endfor: looped over exclusion map pixels
708 
709  // Return
710  return;
711 }
712 
713 
714 /***********************************************************************//**
715  * @brief Verifys that exclusion map points in fov of counts
716  *
717  * Adjust exclusion map. Reinitialise exclusion map via the counts object
718  * and transfer the masked areas provided by the current exclusion map to the
719  * updated fov.
720  ***************************************************************************/
722 {
723  // Initialise working map
724  GSkyMap dest_map = m_counts;
725 
726  // Set all pixels to 0 (no pixel excluded)
727  dest_map = 0.0;
728 
729  // Loop through the individual pixels in the exclusion map
730  for (int i = 0; i < dest_map.npix(); ++i) {
731 
732  // Get the pixel direction
733  GSkyDir dir = dest_map.pix2dir(i);
734 
735  // Check this sky position in the exclusion map
736  if (m_exclmap.contains(dir) && (m_exclmap(m_exclmap.dir2inx(dir)) != 0.0)) {
737 
738  // Set the pixel to 1
739  dest_map(i) = 1.0;
740 
741  } // endif: pixel,region overlap check
742 
743  } // endfor: looped over exclusion map pixels
744 
745  // Set exclusion map
746  m_exclmap = dest_map;
747 
748  // Return
749  return;
750 }
751 
752 
753 /***********************************************************************//**
754  * @brief Fill maps from observation container
755  *
756  * Fill counts and optionally acceptance maps from data in the observation
757  * container.
758  ***************************************************************************/
760 {
761  // Write input observation container into logger
762  log_observations(NORMAL, m_obs, "Input observation");
763 
764  // Write header into logger
765  log_header1(TERSE, gammalib::number("Find unbinned observation",
766  m_obs.size()));
767 
768  // Find all unbinned CTA observations in m_obs
769  std::vector<GCTAObservation*> obs_list(0);
770  for (GCTAObservation* obs = first_unbinned_observation(); obs != NULL;
772 
773  // Push observation into list
774  obs_list.push_back(obs);
775 
776  // Write message
777  std::string msg = " Including unbinned "+obs->instrument()+
778  " observation";
779  log_string(NORMAL, msg);
780  }
781 
782  // Write header into logger
783  log_header1(TERSE, gammalib::number("Fill map from observation",
784  m_obs.size()));
785 
786  // Loop over all unbinned CTA observations in the container
787  #pragma omp parallel for
788  for (int i = 0; i < obs_list.size(); ++i) {
789 
790  // Get pointer to observation
791  GCTAObservation* obs = obs_list[i];
792 
793  // Fill events into counts map
794  fill_maps_counts(obs);
795 
796  // Optionally compute acceptance sky map
797  if (m_bkgsubtract != "NONE") {
799  }
800 
801  // Dispose events to free memory
802  obs->dispose_events();
803 
804  } // endfor: looped over observations
805 
806  // Return
807  return;
808 }
809 
810 
811 /***********************************************************************//**
812  * @brief Fill events into counts map
813  *
814  * @param[in] obs CTA observation.
815  *
816  * @exception GException::invalid_value
817  * Observation does not contain an event list.
818  *
819  * Fills the events found in a CTA events list into a sky map. The method
820  * adds the events to the m_counts member.
821  ***************************************************************************/
822 void ctskymap::fill_maps_counts(GCTAObservation* obs)
823 {
824  // Get non-const pointer on a CTA event list
825  GCTAEventList* events = dynamic_cast<GCTAEventList*>
826  (const_cast<GEvents*>(obs->events()));
827 
828  // Make sure that the observation holds a CTA event list. If this
829  // is not the case then throw an exception.
830  if (events == NULL) {
831  std::string msg = "Specified observation does not contain a CTA event "
832  "list. Please specify a CTA observation containing "
833  "a CTA event list as argument.";
834  throw GException::invalid_value(G_FILL_MAPS_COUNTS, msg);
835  }
836 
837  // Setup energy range covered by data
838  GEnergy emin;
839  GEnergy emax;
840  GEbounds ebds;
841  emin.TeV(m_emin);
842  emax.TeV(m_emax);
843 
844  // Initialise binning statistics
845  int num_outside_roi = 0;
846  int num_outside_map = 0;
847  int num_outside_erange = 0;
848  int num_in_map = 0;
849 
850  // Extract region of interest from observation
851  GCTARoi roi = obs->roi();
852 
853  // Fill sky map
854  for (int i = 0; i < events->size(); ++i) {
855 
856  // Get event
857  GCTAEventAtom* event = (*events)[i];
858 
859  // Skip if energy is out of range
860  if (event->energy() < emin || event->energy() > emax) {
861  num_outside_erange++;
862  continue;
863  }
864 
865  // Determine sky pixel
866  GCTAInstDir* inst = (GCTAInstDir*)&(event->dir());
867  GSkyDir dir = inst->dir();
868  GSkyPixel pixel = m_counts.dir2pix(dir);
869 
870  // Skip if pixel is out of range
871  if (pixel.x() < -0.5 || pixel.x() > (m_counts.nx() - 0.5) ||
872  pixel.y() < -0.5 || pixel.y() > (m_counts.ny() - 0.5)) {
873  num_outside_map++;
874  continue;
875  }
876 
877  // If RoI is valid then skip if instrument direction is not within RoI
878  if (roi.is_valid() && !roi.contains(*inst)) {
879  num_outside_roi++;
880  continue;
881  }
882 
883  // Fill event in skymap
884  #pragma omp critical(ctskymap_fill_maps_counts_1)
885  m_counts(pixel) += 1.0;
886  num_in_map++;
887 
888  } // endfor: looped over all events
889 
890  // Log binning results
891  #pragma omp critical(ctskymap_fill_maps_counts_2)
892  {
893  log_header3(TERSE, get_obs_header(obs));
894  log_value(NORMAL, "Events in list", obs->events()->size());
895  log_value(NORMAL, "Events in map", num_in_map);
896  log_value(NORMAL, "Events outside RoI", num_outside_roi);
897  log_value(NORMAL, "Events outside map area", num_outside_map);
898  log_value(NORMAL, "Events outside energies", num_outside_erange);
899 
900  // Write sky map into header
901  log_header1(EXPLICIT, "Sky map");
902  log_string(EXPLICIT, m_counts.print(m_chatter));
903  }
904 
905  // Return
906  return;
907 }
908 
909 
910 /***********************************************************************//**
911  * @brief Compute background acceptance sky map based on IRF template
912  *
913  * @param[in] obs CTA observation.
914  *
915  * @exception GException::invalid_value
916  * No response information available for observation.
917  * No background template available in instrument response function.
918  *
919  * Computes a background acceptance sky map for a given observation and adds
920  * the estimate to the acceptance sky map.
921  *
922  * If the response contains a background template, that template is used to
923  * compute the background acceptance map.
924  *
925  * If no background template is available, the outcome depends on the
926  * background subtraction method. If ``IRF`` background subtraction is
927  * requested, an exception is thrown. For ``RING`` background subtraction,
928  * a constant background rate is assumed.
929  ***************************************************************************/
930 void ctskymap::fill_maps_acceptance(GCTAObservation* obs)
931 {
932  // Initialise IRF background template pointer
933  const GCTABackground* bkg = NULL;
934 
935  // Get IRF response
936  const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>
937  (obs->response());
938 
939  // Throw an exception if observation has no instrument response function
940  if ((rsp == NULL) && (m_bkgsubtract != "RING")) {
941  std::string msg = "No response information available for "+
942  get_obs_header(obs)+" to compute IRF background. "
943  "Please specify response information or use "
944  "another background subtraction method.";
945  throw GException::invalid_value(G_FILL_MAPS_ACCEPTANCE, msg);
946  }
947 
948  // ... otherwise get IRF background template
949  else {
950 
951  // Get IRF background template
952  bkg = rsp->background();
953 
954  // Throw an exception if observation has no IRF background template
955  if ((bkg == NULL) && (m_bkgsubtract != "RING")) {
956  std::string msg = "No IRF background template found in instrument "
957  "response function for "+
958  get_obs_header(obs)+". Please specify an "
959  "instrument response function containing a "
960  "background template.";
961  throw GException::invalid_value(G_FILL_MAPS_ACCEPTANCE, msg);
962  }
963 
964  } // endelse: got IRF background template
965 
966  // Set minimum and maximum energy as GEnergy instances
967  GEnergy emin(m_emin, "TeV");
968  GEnergy emax(m_emax, "TeV");
969 
970  // Extract region of interest center and radius from observation and
971  // compute the cosine of the RoI radius. This allows to mimimize as
972  // much as possible the trigonometric computations. Note that the cosine
973  // of an angle is maximal for an angle of zero. This explains later why
974  // we chose ">=" to test whether an angle is smaller than a given radius.
975  GSkyDir roi_centre = obs->roi().centre().dir();
976  double cos_roi_radius = std::cos(obs->roi().radius() * gammalib::deg2rad);
977 
978  // Extract exposure
979  double exposure = obs->livetime();
980 
981  // Initialise statistics
982  double total = 0.0;
983 
984  // Loop over all acceptance map pixels
985  for (int i = 0; i < m_acceptance.npix(); ++i) {
986 
987  // Get sky direction of pixel
988  GSkyDir& skydir = m_dirs[i];
989 
990  // Skip pixel if it is not within the RoI
991  if (roi_centre.cos_dist(skydir) < cos_roi_radius) {
992  continue;
993  }
994 
995  // Convert sky direction to instrument direction
996  GCTAInstDir instdir = obs->pointing().instdir(skydir);
997 
998  // Compute background value
999  double value = (bkg != NULL) ? bkg->rate_ebin(instdir, emin, emax) : 1.0;
1000 
1001  // Multiply background rate with livetime and solid angle
1002  value *= exposure * m_solidangle[i];
1003 
1004  // Add number of background events to acceptance map
1005  #pragma omp critical(ctskymap_fill_maps_acceptance_1)
1006  m_acceptance(i) += value;
1007 
1008  // Update total number of background events
1009  total += value;
1010 
1011  } // endfor: looped over acceptance map pixels
1012 
1013  // Log acceptance results
1014  #pragma omp critical(ctskymap_fill_maps_acceptance_2)
1015  {
1016  // Set message string
1017  std::string msg = (bkg != NULL) ? "Use IRF background template"
1018  : "Use constant background rate";
1019 
1020  // Log information
1021  log_value(NORMAL, "Background estimate", msg);
1022  log_value(NORMAL, "Events in background", int(total+0.5));
1023  }
1024 
1025  // Return
1026  return;
1027 }
1028 
1029 
1030 /***********************************************************************//**
1031  * @brief Compute sky map, background map and significance map
1032  *
1033  * Computes the sky map, background map and significance map. The following
1034  * background subtraction methods are supported:
1035  *
1036  * NONE
1037  * ====
1038  *
1039  * The sky map is simply the binned counts map, no background and
1040  * significance maps are computed.
1041  *
1042  * IRF
1043  * ===
1044  *
1045  * The background map is the acceptance map, and this background map is
1046  * subtracted from the counts map and assigned to the sky map. The
1047  * significance \f$\sigma_i\f$ is computed assuming Poisson statistic in
1048  * the Gaussian limit, using
1049  *
1050  * \f[\sigma_i = \frac{N_i - B_i}{\sqrt{N_i}}\f]
1051  *
1052  * where
1053  * \f$N_i\f$ is the number of observed counts and
1054  * \f$B_i\f$ is the estimated number of background counts.
1055  *
1056  * RING
1057  * ====
1058  *
1059  * For the @c RING method the Li & Ma significance is computed for each
1060  * pixel, using the @c roiradius, @c inradius and @c outradius parameters
1061  * to specify the radius of the On region and the Off background ring,
1062  * respectively. Depending on the @c usefft parameter, either an FFT is used
1063  * to compute the number of events and the acceptance of the On and Off
1064  * regions, or a direct computation is performed. While the former is faster
1065  * it is less accurate since it assumes Euclidean distances in the sky map.
1066  * The latter is slower but compute exact distance between pixels of the sky
1067  * map. Use FFT if the sky map is close to a cartesian grid, and use the
1068  * direct method if the sky map shows important distortions.
1069  ***************************************************************************/
1071 {
1072  // Write header into logger
1073  log_header1(TERSE, "Compute maps");
1074 
1075  // If no background subtraction is requested then simply assign the
1076  // counts map to the skymap
1077  if (m_bkgsubtract == "NONE") {
1078  m_skymap = m_counts;
1079  }
1080 
1081  // else if IRF background is selected then use the acceptance as background
1082  else if (m_bkgsubtract == "IRF") {
1085  m_sigmap = (m_counts - m_acceptance) / sqrt(m_counts);
1086  }
1087 
1088  // else if RING background is selected then compute the On
1089  else if (m_bkgsubtract == "RING") {
1090 
1091  // Compute initial RING background
1092  if (m_usefft) {
1094  }
1095  else {
1097  }
1098 
1099  // Store copy of exclusion regions
1100  GSkyMap exclmap = m_exclmap;
1101 
1102  // Iterative computation of exclusion regions
1103  for (int iter = 0; iter < m_iterations; ++iter) {
1104 
1105  // Set exclusion region from significance map
1106  m_exclmap = exclmap;
1107  for (int i = 0; i < m_exclmap.npix(); ++i) {
1108  if (m_sigmap(i) > m_threshold) {
1109  m_exclmap(i) = 1.0;
1110  }
1111  }
1112 
1113  // Re-compute RING background
1114  if (m_usefft) {
1116  }
1117  else {
1119  }
1120 
1121  } // endfor: looped over iterations
1122 
1123  }
1124 
1125  // Return
1126  return;
1127 }
1128 
1129 
1130 /***********************************************************************//**
1131  * @brief Compute the maps for RING background using a FFT
1132  *
1133  * Computes the Li & Ma significance for each sky map pixel and replaces
1134  * the sky and background maps by the On- and Off-count maps. The computation
1135  * is done using a FFT.
1136  ***************************************************************************/
1138 {
1139  // Log message about what is being done
1140  log_header3(NORMAL, "Computing ring background map (FFT method)");
1141 
1142  // Initialise maps
1143  m_skymap = m_counts;
1144  m_bkgmap = m_counts;
1145  m_sigmap = m_counts;
1146  m_skymap = 0.0;
1147  m_bkgmap = 0.0;
1148  m_sigmap = 0.0;
1149 
1150  // Initialise statistics
1151  int num_bad_alpha = 0;
1152 
1153  // Set-up sky and acceptance maps with excluded pixels according to the
1154  // exclusion map
1155  GSkyMap excl_counts = m_counts;
1156  GSkyMap excl_acceptance = m_acceptance;
1157  for (int i = 0; i < m_counts.npix(); ++i) {
1158  if (m_exclmap(i) != 0.0) {
1159  excl_counts(i) = 0.0;
1160  excl_acceptance(i) = 0.0;
1161  }
1162  }
1163 
1164  // Convolve maps for On-region and Off-region
1165  GSkyMap on_counts = ring_convolve(m_counts, 0.0, m_roiradius);
1166  GSkyMap on_alpha = ring_convolve(m_acceptance, 0.0, m_roiradius);
1167  GSkyMap off_counts = ring_convolve(excl_counts, m_inradius, m_outradius);
1168  GSkyMap off_alpha = ring_convolve(excl_acceptance, m_inradius, m_outradius);
1169 
1170  // Compute maps by loop over sky map pixels
1171  for (int i = 0; i < m_sigmap.npix(); ++i) {
1172 
1173  // Get On-counts, Off-counts and alpha value for this bin
1174  double n_on = on_counts(i);
1175  double n_off = off_counts(i);
1176  double alpha;
1177  if (off_alpha(i) == 0.0) {
1178  alpha = 0.0;
1179  n_on = 0.0;
1180  n_off = 0.0;
1181  }
1182  else {
1183  alpha = on_alpha(i) / off_alpha(i);
1184  }
1185 
1186  // Store the On and alpha-weighted Off-counts
1187  m_bkgmap(i) = alpha * n_off;
1188  m_skymap(i) = n_on - m_bkgmap(i);
1189 
1190  // If alpha is zero then increment the bad alpha counter
1191  if (alpha == 0.0) {
1192  num_bad_alpha++;
1193  }
1194 
1195  // ... otherwise compute and store the results
1196  else {
1197  m_sigmap(i) = sigma_li_ma(n_on, n_off, alpha);
1198  }
1199 
1200  } // endfor: looped over all pixels
1201 
1202  // Log the number of bad-alpha bins
1203  log_value(NORMAL, "Total number of pixels", m_sigmap.npix());
1204  log_value(NORMAL, "Pixels with alpha=0", num_bad_alpha);
1205 
1206  // Now take the square root. Since some bins can be negative, first
1207  // take the absolute value of the map, square root that, then multiply
1208  // each bin by its sign to preserve the +/- significance.
1209  m_sigmap = sign(m_sigmap) * sqrt(abs(m_sigmap));
1210 
1211  // Return
1212  return;
1213 }
1214 
1215 
1216 /***********************************************************************//**
1217  * @brief Compute the pixel significance for RING background
1218  *
1219  * Computes the Li & Ma significance for each sky map pixel and replaces
1220  * the sky and background maps by the On- and Off-count maps. The computation
1221  * is done by computing the exact distances between pixels in the sky map.
1222  ***************************************************************************/
1224 {
1225  // Log message about what is being done
1226  log_header3(NORMAL, "Computing ring background map (direct method)");
1227  log_value(NORMAL, "Total pixels to process", m_counts.npix());
1228 
1229  // Initialise maps
1230  m_skymap = m_counts;
1231  m_bkgmap = m_counts;
1232  m_sigmap = m_counts;
1233  m_skymap = 0.0;
1234  m_bkgmap = 0.0;
1235  m_sigmap = 0.0;
1236 
1237  // Initialise statistics
1238  int num_bad_alpha = 0;
1239 
1240  // Set progress logging frequency. By default the user is updated on the
1241  // progress when another 10% of pixels is processed, however, if there
1242  // are many pixels the user is updated each 100000 pixels.
1243  int n_logpix = m_counts.npix() / 10;
1244  if (n_logpix > 100000) {
1245  n_logpix = 100000;
1246  }
1247 
1248  // Compute maps by loop over sky map pixels
1249  #pragma omp parallel for
1250  for (int i = 0; i < m_sigmap.npix(); ++i) {
1251 
1252  // Initialise the on/off counts and alpha for this bin
1253  double n_on = 0.0;
1254  double n_off = 0.0;
1255  double alpha = 0.0;
1256 
1257  // Update user about progress (only if there are more than 10000 pixels)
1258  if (i % n_logpix == 0 && n_logpix > 10000) {
1259  #pragma omp critical(ctskymap_map_significance_ring)
1260  log_value(NORMAL, "Pixels remaining", m_sigmap.npix()-i);
1261  }
1262 
1263  // Compute the alpha and counts for this bin
1264  compute_ring_values(i, m_counts, m_acceptance, n_on, n_off, alpha);
1265 
1266  // Store the On and alpha-weighted Off-counts
1267  m_bkgmap(i) = alpha * n_off;
1268  m_skymap(i) = n_on - m_bkgmap(i);
1269 
1270  // If alpha is zero then increment the bad alpha counter
1271  if (alpha == 0.0) {
1272  num_bad_alpha++;
1273  #pragma omp critical(ctskymap_compute_maps_ring_direct)
1274  m_sigmap(i) = 0.0;
1275  }
1276 
1277  // ... otherwise store the results
1278  else {
1279 
1280  // Compute and store significance (Li & Ma eq. 17)
1281  double sigma = sigma_li_ma(n_on, n_off, alpha);
1282  #pragma omp critical(ctskymap_compute_maps_ring_direct)
1283  m_sigmap(i) = sigma;
1284 
1285  } // endelse: alpha was non-zero
1286 
1287  } // endfor: looped over all pixels
1288 
1289  // Log the number of bad-alpha bins
1290  log_value(NORMAL, "Pixels with alpha=0", num_bad_alpha);
1291 
1292  // Now take the square root. Since some bins can be negative, first
1293  // take the absolute value of the map, square root that, then multiply
1294  // each bin by its sign to preserve the +/- significance.
1295  m_sigmap = sign(m_sigmap) * sqrt(abs(m_sigmap));
1296 
1297  // Return
1298  return;
1299 }
1300 
1301 
1302 /***********************************************************************//**
1303  * @brief Computes Non, Noff and alpha for a counts map and sensitivity map
1304  *
1305  * @param[in] ipixel Sky map pixel to consider.
1306  * @param[in] counts Counts map.
1307  * @param[in] background Background map.
1308  * @param[out] non Returned estimate of On-counts.
1309  * @param[out] noff Returned estimate of Off-counts.
1310  * @param[out] alpha Returned estimate of alpha.
1311  *
1312  * Computes the On- and Off-counts values at a given position from in counts
1313  * map and the alpha parameter from the background map.
1314  *
1315  * To speed-up the computations the method considers only the pixels within
1316  * a bounding box that comprises the outer background ring radius. The method
1317  * considers wrapping around of pixel indices in the Right Ascension /
1318  * Galactic longitude direction.
1319  *
1320  * If the alpha of the ring region is zero the values of @p non, @p noff,
1321  * and @p alpha will be set to zero.
1322  ***************************************************************************/
1323 void ctskymap::compute_ring_values(const int& ipixel,
1324  const GSkyMap& counts,
1325  const GSkyMap& background,
1326  double& non,
1327  double& noff,
1328  double& alpha)
1329 {
1330  // Initialise return values (non, noff and alpha)
1331  non = 0.0;
1332  noff = 0.0;
1333  alpha = 0.0;
1334 
1335  // Initialise On and Off alpha
1336  double alpha_on = 0.0;
1337  double alpha_off = 0.0;
1338 
1339  // Get sky direction of pixel
1340  GSkyDir& position = m_dirs[ipixel];
1341 
1342  // Get pixel bounding box
1343  int ix_start;
1344  int ix_stop;
1345  int iy_start;
1346  int iy_stop;
1347  ring_bounding_box(ipixel, ix_start, ix_stop, iy_start, iy_stop);
1348 
1349  // Get number of x pixels in sky map
1350  int nx = counts.nx();
1351 
1352  // Loop over x pixels of bounding box
1353  for (int ix_nowrap = ix_start; ix_nowrap < ix_stop; ++ix_nowrap) {
1354 
1355  // Compute x pixel index by wrapping the pixel index into the
1356  // interval [0,nx[
1357  int ix = ix_nowrap;
1358  if (ix < 0) {
1359  ix += nx;
1360  }
1361  else if (ix >= nx) {
1362  ix -= nx;
1363  }
1364 
1365  // Initialise pixel index
1366  int i = ix + iy_start * nx;
1367 
1368  // Loop over y pixels of bounding box
1369  for (int iy = iy_start; iy < iy_stop; ++iy, i += nx) {
1370 
1371  // Get the index and sky direction of this pixel
1372  GSkyDir& skydir = m_dirs[i];
1373 
1374  // Only consider pixels within the outer radius of the background
1375  // region
1376  if (position.cos_dist(skydir) >= m_cos_outradius) {
1377 
1378  // Check if pixel is inside the background region
1379  if ((m_exclmap(i) == 0.0) &&
1380  (position.cos_dist(skydir) < m_cos_inradius)) {
1381 
1382  // Update n_off
1383  noff += counts(i);
1384 
1385  // Update alpha_off
1386  alpha_off += background(i);
1387 
1388  }
1389 
1390  // ... otherwise check if pixel is inside source region
1391  else if ((position.cos_dist(skydir) >= m_cos_roiradius) ||
1392  (ipixel == i)) {
1393 
1394  // Update n_on
1395  non += counts(i);
1396 
1397  // Update alpha_on
1398  alpha_on += background(i);
1399 
1400  } // endif: source and background region check
1401 
1402  } // endif: pixel is within outer radius of background region
1403 
1404  } // endfor: looped over y pixels
1405 
1406  } // endfor: looped over x pixels
1407 
1408  // Compute alpha. If the off region does not have any sensitivity then
1409  // set Non = Noff = 0
1410  if (alpha_off == 0.0) {
1411  alpha = 0.0;
1412  non = 0.0;
1413  noff = 0.0;
1414  }
1415  else {
1416  alpha = alpha_on / alpha_off;
1417  }
1418 
1419  // Return
1420  return;
1421 }
1422 
1423 
1424 /***********************************************************************//**
1425  * @brief Computes bounding box for RING background computation
1426  *
1427  * @param[in] ipixel Sky map pixel to consider.
1428  * @param[out] ix1 Index of first pixel in x.
1429  * @param[out] ix2 Index after last pixel in x.
1430  * @param[out] iy1 Index of first pixel in y.
1431  * @param[out] iy2 Index after last pixel in y.
1432  *
1433  * Computes the bounding box that contained the background ring for a
1434  * specific pixel for the RING background method.
1435  *
1436  * The method determines the local pixel scale for the requested pixel and
1437  * draws a bounding box with 1.5 times the outer background ring radius
1438  * around the pixel. In the x direction the pixel indices are unconstrained,
1439  * and the client has to assure that the pixel value is comprised within
1440  * the validity range (this is required to handle the longitude wrap around).
1441  * In the y direction the pixel indices are constrained to [0,ny], where
1442  * ny is the number of y pixels in the sky map.
1443  ***************************************************************************/
1444 void ctskymap::ring_bounding_box(const int& ipixel, int& ix1, int& ix2,
1445  int& iy1, int& iy2) const
1446 {
1447  // Get number of pixels in x and y direction
1448  int nx = m_counts.nx();
1449  int ny = m_counts.ny();
1450 
1451  // Get x and y index of pixel to consider
1452  int ix0 = ipixel % nx;
1453  int iy0 = ipixel / nx;
1454 
1455  // Get pointer on WCS projection
1456  const GWcs* wcs = dynamic_cast<const GWcs*>(m_counts.projection());
1457  if (wcs == NULL) {
1458  std::string msg = "Sky map is not a WCS projection. Method is only "
1459  "valid for WCS projections.";
1460  throw GException::invalid_value(G_RING_BOUNDING_BOX, msg);
1461  }
1462 
1463  // Get X and Y step size
1464  double dx_binsz = wcs->cdelt(0);
1465  double dy_binsz = wcs->cdelt(1);
1466 
1467  // Compute pixel increment in x-direction
1468  double dx = 0.0;
1469  int ix = (ix0 > 0) ? ix0 - 1 : ix0 + 1;
1470  if (ix < nx) {
1471  dx = m_dirs[ipixel].dist_deg(m_dirs[ix+iy0*nx]);
1472  }
1473  if (dx < dx_binsz) {
1474  dx = dx_binsz;
1475  }
1476 
1477  // Compute pixel increment in y-direction
1478  double dy = 0.0;
1479  int iy = (iy0 > 0) ? iy0 - 1 : iy0 + 1;
1480  if (iy < ny) {
1481  dx = m_dirs[ipixel].dist_deg(m_dirs[ix0+iy*nx]);
1482  }
1483  if (dy < dy_binsz) {
1484  dy = dy_binsz;
1485  }
1486 
1487  // Compute bounding box half size in x and y. The outer radius is
1488  // multiplied by 1.5 to have some margin in case of map distortions
1489  int nx_bbox = int(1.5 * m_outradius / dx);
1490  int ny_bbox = int(1.5 * m_outradius / dy);
1491 
1492  // Compute index range for x. We allow that indices are smaller than 0
1493  // or equal or larger than nx to handle wrap around, which needs to be
1494  // done in the client method.
1495  if (2*nx_bbox < nx) {
1496  ix1 = ix0 - nx_bbox;
1497  ix2 = ix0 + nx_bbox;
1498  }
1499  else {
1500  ix1 = 0;
1501  ix2 = nx;
1502  }
1503 
1504  // Compute index range for y. The index range is restricted to [0,ny].
1505  if (2*ny_bbox < ny) {
1506  iy1 = iy0 - ny_bbox;
1507  iy2 = iy0 + ny_bbox;
1508  if (iy1 < 0) {
1509  iy1 = 0;
1510  }
1511  if (iy2 > ny) {
1512  iy2 = ny;
1513  }
1514  }
1515  else {
1516  iy1 = 0;
1517  iy2 = ny;
1518  }
1519 
1520  // Return
1521  return;
1522 }
1523 
1524 
1525 /***********************************************************************//**
1526  * @brief Return FFT kernel for background ring
1527  *
1528  * @param[in] map Sky map to be convolved with a background ring.
1529  * @param[in] rmin Minimum ring radius (degrees).
1530  * @param[in] rmax Maximum ring radius (degrees).
1531  * @return FFT kernel.
1532  *
1533  * Computes the FFT kernel for a backgrund ring.
1534  ***************************************************************************/
1535 GSkyMap ctskymap::ring_convolve(const GSkyMap& map, const double& rmin,
1536  const double& rmax) const
1537 {
1538  // Copy input map
1539  GSkyMap conv_map = map;
1540 
1541  // Get FFT of ring kernel
1542  GFft fft_kernel(ring_kernel(rmin, rmax));
1543 
1544  // Extract sky map into Ndarray
1545  GNdarray array(conv_map.nx(), conv_map.ny());
1546  double *dst = array.data();
1547  for (int i = 0; i < conv_map.npix(); ++i) {
1548  *dst++ = conv_map(i);
1549  }
1550 
1551  // FFT of sky map
1552  GFft fft_array = GFft(array);
1553 
1554  // Multiply FFT of sky map with FFT of kernel
1555  GFft fft_smooth = fft_array * fft_kernel;
1556 
1557  // Backward transform sky map
1558  GNdarray smooth = fft_smooth.backward();
1559 
1560  // Insert sky map
1561  double *src = smooth.data();
1562  for (int i = 0; i < conv_map.npix(); ++i) {
1563  conv_map(i) = *src++;
1564  }
1565 
1566  // Return convolved map
1567  return conv_map;
1568 }
1569 
1570 
1571 /***********************************************************************//**
1572  * @brief Return FFT kernel for background ring
1573  *
1574  * @param[in] rmin Minimum ring radius (degrees).
1575  * @param[in] rmax Maximum ring radius (degrees).
1576  * @return FFT kernel.
1577  *
1578  * @exception GException::invalid_value
1579  * Sky map is not a WCS projection.
1580  *
1581  * Computes the FFT kernel for a background ring. The computation is done
1582  * assuming that the sky map represents a cartesian grid, and distances are
1583  * computed in that grid using Euclidean distances. The @p rmin and @p rmax
1584  * arguments refer to a ring radius in these Euclidean distances.
1585  ***************************************************************************/
1586 GNdarray ctskymap::ring_kernel(const double& rmin, const double& rmax) const
1587 {
1588  // Get pointer on WCS projection
1589  const GWcs* wcs = dynamic_cast<const GWcs*>(m_counts.projection());
1590  if (wcs == NULL) {
1591  std::string msg = "Sky map is not a WCS projection. Method is only "
1592  "valid for WCS projections.";
1593  throw GException::invalid_value(G_RING_KERNEL, msg);
1594  }
1595 
1596  // Get X and Y step size
1597  double dx = wcs->cdelt(0);
1598  double dy = wcs->cdelt(1);
1599 
1600  // Initialise kernel
1601  GNdarray kern(m_counts.nx(), m_counts.ny());
1602 
1603  // Fill kernel
1604  for (int ix1 = 0, ix2 = m_counts.nx(); ix1 < m_counts.nx(); ++ix1, --ix2) {
1605  double x = ix1 * dx;
1606  double xqs = x * x;
1607  for (int iy1 = 0, iy2 = m_counts.ny(); iy1 < m_counts.ny(); ++iy1, --iy2) {
1608  double y = iy1 * dy;
1609  double r = std::sqrt(xqs + y*y);
1610  if ((r >= rmin) && (r <= rmax)) {
1611  kern(ix1,iy1) += 1.0;
1612  if (ix2 < m_counts.nx()) {
1613  kern(ix2,iy1) += 1.0;
1614  }
1615  if (iy2 < m_counts.ny()) {
1616  kern(ix1,iy2) += 1.0;
1617  }
1618  if ((ix2 < m_counts.nx()) && (iy2 < m_counts.ny())) {
1619  kern(ix2,iy2) += 1.0;
1620  }
1621  }
1622  }
1623  }
1624 
1625  // Return kernel
1626  return kern;
1627 }
1628 
1629 
1630 /***********************************************************************//**
1631  * @brief Compute significance following Li & Ma
1632  *
1633  * @param[in] n_on Number of On-counts.
1634  * @param[in] n_off Number of Off-counts.
1635  * @param[in] alpha Alpha parameters.
1636  * @return Significance.
1637  *
1638  * Computes the significance following Li & Ma, Equation (17).
1639  ***************************************************************************/
1640 double ctskymap::sigma_li_ma(const double& n_on,
1641  const double& n_off,
1642  const double& alpha) const
1643 {
1644  // Allocate result
1645  double sigma;
1646 
1647  // Handle special case of no On-counts
1648  if (n_on == 0.0) {
1649  sigma = -2.0 * n_off * std::log(1.0+alpha);
1650  }
1651 
1652  // Handle special case of no Off-counts
1653  else if (n_off == 0.0) {
1654  sigma = 2.0 * n_on * std::log((1.0+alpha)/alpha);
1655  }
1656 
1657  // ... otherwise handle general case
1658  else {
1659  sigma = (n_on < (alpha*n_off) ? -2.0 : 2.0) *
1660  (n_on * std::log((1.0+alpha) * n_on / (alpha * (n_on+n_off))) +
1661  n_off * std::log((1.0+alpha) * n_off / (n_on+n_off)));
1662  }
1663 
1664  // Return sigma
1665  return sigma;
1666 }
1667 
1668 
1669 /***********************************************************************//**
1670  * @brief Write sky map into FITS file
1671  *
1672  * @param[in,out] fits FITS file.
1673  * @param[in] map Sky map.
1674  * @param[in] extname Extension name.
1675  *
1676  * Write one sky map with the extension name and keywords into the FITS file.
1677  ***************************************************************************/
1678 void ctskymap::write_map(GFits& fits, const GSkyMap& map, const std::string& extname) const
1679 {
1680  // Write map into FITS file
1681  GFitsHDU* hdu = map.write(fits);
1682 
1683  // Set map extension name
1684  if (hdu != NULL) {
1685  hdu->extname(extname);
1686  }
1687 
1688  // Write keywords into map extension
1689  write_ogip_keywords(hdu);
1690  write_hdu_keywords(hdu);
1691 
1692  // Return
1693  return;
1694 }
1695 
1696 
1697 /***********************************************************************//**
1698  * @brief Write keywords in FITS HDU
1699  *
1700  * @param[in,out] hdu Pointer to FITS HDU.
1701  *
1702  * Writes keywords in FITS HDU.
1703  ***************************************************************************/
1704 void ctskymap::write_hdu_keywords(GFitsHDU* hdu) const
1705 {
1706  // Continue only if pointer is valid
1707  if (hdu != NULL) {
1708 
1709  // Set keywords
1710  hdu->card("BKGSUB", m_bkgsubtract, "Background substraction method");
1711  hdu->card("E_MIN", m_emin, "[TeV] Lower energy boundary");
1712  hdu->card("E_MAX", m_emax, "[TeV] Upper energy boundary");
1713  hdu->card("EUNIT", "TeV", "Units for E_MIN and E_MAX");
1714 
1715  // Write RING background keywords
1716  if (m_bkgsubtract == "RING") {
1717  hdu->card("USEFFT", m_usefft, "Use FFT for RING background");
1718  hdu->card("ROIRAD", m_roiradius, "[deg] Source region radius");
1719  hdu->card("INRAD", m_inradius, "[deg] Inner background ring radius");
1720  hdu->card("OUTRAD", m_outradius, "[deg] Outer background ring radius");
1721  hdu->card("ITER", m_iterations, "Exclusion map iterations");
1722  hdu->card("THRES", m_threshold, "Exclusion map threshold");
1723  hdu->card("EXCLMAP", m_inexclusion.url(), "Exclusion map name");
1724  }
1725 
1726  } // endif: pointer was valid
1727 
1728  // Return
1729  return;
1730 }
#define G_RING_KERNEL
Definition: ctskymap.cpp:41
double m_roiradius
Region of interest radius for RING bkg.
Definition: ctskymap.hpp:111
void setup_exclusion_map(void)
Generates map of pixel exclusions.
Definition: ctskymap.cpp:633
void setup_maps(void)
Setup maps.
Definition: ctskymap.cpp:586
void clear(void)
Clear sky mapping tool.
Definition: ctskymap.cpp:186
void set_response(GObservations &obs)
Set response for all CTA observations in container.
Definition: ctool.cpp:1614
GNdarray ring_kernel(const double &rmin, const double &rmax) const
Return FFT kernel for background ring.
Definition: ctskymap.cpp:1586
int m_iterations
Number of iterations for RING background.
Definition: ctskymap.hpp:114
#define G_FILL_MAPS_ACCEPTANCE
Definition: ctskymap.cpp:37
GFilename m_outmap
Output file name.
Definition: ctskymap.hpp:106
void setup_observations(GObservations &obs, const bool &response=true, const bool &list=true, const bool &cube=true)
Setup observation container.
Definition: ctool.cpp:431
GFits m_fits
Output GFits object.
Definition: ctskymap.hpp:128
void setup_exclusion_map_region(const GFilename &filename)
Fills exclusions map from DS9 region file.
Definition: ctskymap.cpp:691
const GFits & fits(void) const
Return fits container.
Definition: ctskymap.hpp:157
bool m_publish
Publish sky map?
Definition: ctskymap.hpp:117
GFilename m_inexclusion
Exclusion map file name.
Definition: ctskymap.hpp:107
double m_threshold
Threshold for RING background.
Definition: ctskymap.hpp:115
double m_inradius
Inner ring radius for RING background.
Definition: ctskymap.hpp:112
bool m_usefft
Use FFT for RING background.
Definition: ctskymap.hpp:116
void ring_bounding_box(const int &ipixel, int &ix1, int &ix2, int &iy1, int &iy2) const
Computes bounding box for RING background computation.
Definition: ctskymap.cpp:1444
GSkyMap create_map(const GObservations &obs)
Create a skymap from user parameters.
Definition: ctool.cpp:937
void free_members(void)
Delete class members.
Definition: ctskymap.cpp:450
void init_members(void)
Initialise class members.
Definition: ctool.cpp:321
const GObservations & obs(void) const
Return observation container.
GSkyMap m_bkgmap
Background map.
Definition: ctskymap.hpp:123
void fill_maps_acceptance(GCTAObservation *obs)
Compute background acceptance sky map based on IRF template.
Definition: ctskymap.cpp:930
void setup_exclusion_map_fits(const GFilename &filename)
Fills exclusions map from FITS image.
Definition: ctskymap.cpp:670
GCTAObservation * next_unbinned_observation(void)
Return next unbinned CTA observation.
GSkyMap m_counts
Counts map.
Definition: ctskymap.hpp:125
virtual ~ctskymap(void)
Destructor.
Definition: ctskymap.cpp:129
GSkyMap ring_convolve(const GSkyMap &map, const double &rmin, const double &rmax) const
Return FFT kernel for background ring.
Definition: ctskymap.cpp:1535
std::string m_bkgsubtract
Background subtraction method.
Definition: ctskymap.hpp:110
void fill_maps(void)
Fill maps from observation container.
Definition: ctskymap.cpp:759
void copy_members(const ctskymap &app)
Copy class members.
Definition: ctskymap.cpp:408
double m_cos_outradius
Cosine of outer ring radius.
Definition: ctskymap.hpp:135
void write_map(GFits &fits, const GSkyMap &map, const std::string &extname) const
Write sky map into FITS file.
Definition: ctskymap.cpp:1678
Sky mapping tool.
Definition: ctskymap.hpp:45
GChatter m_chatter
Chattiness.
Definition: ctskymap.hpp:118
void log_parameters(const GChatter &chatter)
Log application parameters.
Definition: ctool.cpp:1208
Base class for observation tools.
void read_ogip_keywords(GFitsHDU *hdu) const
Read OGIP keywords from FITS HDU.
void compute_ring_values(const int &ipixel, const GSkyMap &counts, const GSkyMap &background, double &non, double &noff, double &alpha)
Computes Non, Noff and alpha for a counts map and sensitivity map.
Definition: ctskymap.cpp:1323
void write_hdu_keywords(GFitsHDU *hdu) const
Write keywords in FITS HDU.
Definition: ctskymap.cpp:1704
double m_cos_roiradius
Cosine of RoI radius.
Definition: ctskymap.hpp:133
void free_members(void)
Delete class members.
#define G_FILL_MAPS_COUNTS
Definition: ctskymap.cpp:36
std::vector< GSkyDir > m_dirs
Cached pixel directions.
Definition: ctskymap.hpp:132
void get_parameters(void)
Get application parameters.
Definition: ctskymap.cpp:466
void save(void)
Save sky map.
Definition: ctskymap.cpp:252
std::string get_obs_header(const GObservation *obs) const
Return observation header string.
Definition: ctool.cpp:2090
double sigma_li_ma(const double &n_on, const double &n_off, const double &alpha) const
Compute significance following Li &amp; Ma.
Definition: ctskymap.cpp:1640
void init_members(void)
Initialise class members.
Definition: ctskymap.cpp:364
#define G_GET_PARAMETERS
Definition: ctskymap.cpp:35
void write_ogip_keywords(GFitsHDU *hdu) const
Write OGIP keywords in FITS HDU.
void process(void)
Process the sky mapping tool.
Definition: ctskymap.cpp:216
void fill_maps_counts(GCTAObservation *obs)
Fill events into counts map.
Definition: ctskymap.cpp:822
GSkyMap m_skymap
Sky map.
Definition: ctskymap.hpp:122
void free_members(void)
Delete class members.
Definition: ctool.cpp:357
double m_outradius
Outer ring radius for RING background.
Definition: ctskymap.hpp:113
void init_members(void)
Initialise class members.
std::vector< double > m_solidangle
Cached pixel solid angles.
Definition: ctskymap.hpp:131
GCTAObservation * first_unbinned_observation(void)
Return first unbinned CTA observation.
#define CTSKYMAP_NAME
Definition: ctskymap.hpp:35
GSkyMap m_acceptance
Acceptance map.
Definition: ctskymap.hpp:126
void compute_maps(void)
Compute sky map, background map and significance map.
Definition: ctskymap.cpp:1070
ctobservation & operator=(const ctobservation &app)
Assignment operator.
double m_emax
Maximum energy (TeV)
Definition: ctskymap.hpp:109
ctskymap(void)
Void constructor.
Definition: ctskymap.cpp:59
const bool & read_ahead(void) const
Signal whether parameters should be read ahead.
Definition: ctool.hpp:177
double m_emin
Minimum energy (TeV)
Definition: ctskymap.hpp:108
bool m_has_inmap
Has valid input map.
Definition: ctskymap.hpp:121
void compute_maps_ring_fft(void)
Compute the maps for RING background using a FFT.
Definition: ctskymap.cpp:1137
double m_cos_inradius
Cosine of inner ring radius.
Definition: ctskymap.hpp:134
void publish(const std::string &name="")
Publish sky map.
Definition: ctskymap.cpp:333
void log_observations(const GChatter &chatter, const GObservations &obs, const std::string &what="Observation")
Log observation container.
Definition: ctool.cpp:1251
GObservations m_obs
Observation container.
void adjust_exclusion_map(void)
Verifys that exclusion map points in fov of counts.
Definition: ctskymap.cpp:721
void compute_maps_ring_direct(void)
Compute the pixel significance for RING background.
Definition: ctskymap.cpp:1223
GSkyMap m_exclmap
Exclusion map for RING background.
Definition: ctskymap.hpp:127
ctskymap & operator=(const ctskymap &app)
Assignment operator.
Definition: ctskymap.cpp:151
GSkyMap m_sigmap
Significance map.
Definition: ctskymap.hpp:124
Sky mapping tool definition.
void construct_fits(void)
Construct GFits object consisting of all maps.
Definition: ctskymap.cpp:287
#define G_RING_BOUNDING_BOX
Definition: ctskymap.cpp:39