ctools 2.1.0
Loading...
Searching...
No Matches
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
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 ***************************************************************************/
76ctskymap::ctskymap(const GObservations& obs) :
77 ctobservation(CTSKYMAP_NAME, VERSION, obs)
78{
79 // Initialise 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 ***************************************************************************/
97ctskymap::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 ***************************************************************************/
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
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
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 ***************************************************************************/
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 ***************************************************************************/
333void 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;
412 m_inexclusion = app.m_inexclusion;
413 m_emin = app.m_emin;
414 m_emax = app.m_emax;
415 m_bkgsubtract = app.m_bkgsubtract;
416 m_roiradius = app.m_roiradius;
417 m_inradius = app.m_inradius;
418 m_outradius = app.m_outradius;
419 m_iterations = app.m_iterations;
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;
431 m_acceptance = app.m_acceptance;
432 m_exclmap = app.m_exclmap;
433 m_fits = app.m_fits;
434
435 // Copy cache
436 m_solidangle = app.m_solidangle;
437 m_dirs = app.m_dirs;
438 m_cos_roiradius = app.m_cos_roiradius;
439 m_cos_inradius = app.m_cos_inradius;
440 m_cos_outradius = app.m_cos_outradius;
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 ***************************************************************************/
670void 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 ***************************************************************************/
691void 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
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 ***************************************************************************/
822void 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 ***************************************************************************/
930void 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") {
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
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
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 ***************************************************************************/
1323void 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 ***************************************************************************/
1444void 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 ***************************************************************************/
1535GSkyMap 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 ***************************************************************************/
1586GNdarray 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 ***************************************************************************/
1640double 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 ***************************************************************************/
1678void 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
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 ***************************************************************************/
1704void 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}
Base class for observation tools.
void free_members(void)
Delete class members.
ctobservation & operator=(const ctobservation &app)
Assignment operator.
GCTAObservation * next_unbinned_observation(void)
Return next unbinned CTA observation.
GObservations m_obs
Observation container.
void read_ogip_keywords(GFitsHDU *hdu) const
Read OGIP keywords from FITS HDU.
GCTAObservation * first_unbinned_observation(void)
Return first unbinned CTA observation.
void write_ogip_keywords(GFitsHDU *hdu) const
Write OGIP keywords in FITS HDU.
const GObservations & obs(void) const
Return observation container.
void init_members(void)
Initialise class members.
void set_response(GObservations &obs)
Set response for all CTA observations in container.
Definition ctool.cpp:1614
void free_members(void)
Delete class members.
Definition ctool.cpp:357
GSkyMap create_map(const GObservations &obs)
Create a skymap from user parameters.
Definition ctool.cpp:937
void log_observations(const GChatter &chatter, const GObservations &obs, const std::string &what="Observation")
Log observation container.
Definition ctool.cpp:1251
const bool & read_ahead(void) const
Signal whether parameters should be read ahead.
Definition ctool.hpp:177
void log_parameters(const GChatter &chatter)
Log application parameters.
Definition ctool.cpp:1208
void init_members(void)
Initialise class members.
Definition ctool.cpp:321
void setup_observations(GObservations &obs, const bool &response=true, const bool &list=true, const bool &cube=true)
Setup observation container.
Definition ctool.cpp:431
std::string get_obs_header(const GObservation *obs) const
Return observation header string.
Definition ctool.cpp:2090
Sky mapping tool.
Definition ctskymap.hpp:45
double m_threshold
Threshold for RING background.
Definition ctskymap.hpp:115
void compute_maps(void)
Compute sky map, background map and significance map.
GSkyMap ring_convolve(const GSkyMap &map, const double &rmin, const double &rmax) const
Return FFT kernel for background ring.
void fill_maps_counts(GCTAObservation *obs)
Fill events into counts map.
Definition ctskymap.cpp:822
void write_map(GFits &fits, const GSkyMap &map, const std::string &extname) const
Write sky map into FITS file.
void publish(const std::string &name="")
Publish sky map.
Definition ctskymap.cpp:333
void fill_maps_acceptance(GCTAObservation *obs)
Compute background acceptance sky map based on IRF template.
Definition ctskymap.cpp:930
void clear(void)
Clear sky mapping tool.
Definition ctskymap.cpp:186
bool m_has_inmap
Has valid input map.
Definition ctskymap.hpp:121
double m_cos_inradius
Cosine of inner ring radius.
Definition ctskymap.hpp:134
void compute_maps_ring_fft(void)
Compute the maps for RING background using a FFT.
GSkyMap m_counts
Counts map.
Definition ctskymap.hpp:125
GSkyMap m_exclmap
Exclusion map for RING background.
Definition ctskymap.hpp:127
double m_roiradius
Region of interest radius for RING bkg.
Definition ctskymap.hpp:111
void write_hdu_keywords(GFitsHDU *hdu) const
Write keywords in FITS HDU.
void fill_maps(void)
Fill maps from observation container.
Definition ctskymap.cpp:759
GFilename m_outmap
Output file name.
Definition ctskymap.hpp:106
GSkyMap m_bkgmap
Background map.
Definition ctskymap.hpp:123
void copy_members(const ctskymap &app)
Copy class members.
Definition ctskymap.cpp:408
virtual ~ctskymap(void)
Destructor.
Definition ctskymap.cpp:129
std::string m_bkgsubtract
Background subtraction method.
Definition ctskymap.hpp:110
double m_inradius
Inner ring radius for RING background.
Definition ctskymap.hpp:112
void ring_bounding_box(const int &ipixel, int &ix1, int &ix2, int &iy1, int &iy2) const
Computes bounding box for RING background computation.
void save(void)
Save sky map.
Definition ctskymap.cpp:252
void adjust_exclusion_map(void)
Verifys that exclusion map points in fov of counts.
Definition ctskymap.cpp:721
GNdarray ring_kernel(const double &rmin, const double &rmax) const
Return FFT kernel for background ring.
void compute_maps_ring_direct(void)
Compute the pixel significance for RING background.
void setup_exclusion_map_region(const GFilename &filename)
Fills exclusions map from DS9 region file.
Definition ctskymap.cpp:691
double m_emin
Minimum energy (TeV)
Definition ctskymap.hpp:108
void setup_exclusion_map(void)
Generates map of pixel exclusions.
Definition ctskymap.cpp:633
int m_iterations
Number of iterations for RING background.
Definition ctskymap.hpp:114
std::vector< double > m_solidangle
Cached pixel solid angles.
Definition ctskymap.hpp:131
bool m_publish
Publish sky map?
Definition ctskymap.hpp:117
void get_parameters(void)
Get application parameters.
Definition ctskymap.cpp:466
void free_members(void)
Delete class members.
Definition ctskymap.cpp:450
double m_cos_outradius
Cosine of outer ring radius.
Definition ctskymap.hpp:135
std::vector< GSkyDir > m_dirs
Cached pixel directions.
Definition ctskymap.hpp:132
ctskymap & operator=(const ctskymap &app)
Assignment operator.
Definition ctskymap.cpp:151
bool m_usefft
Use FFT for RING background.
Definition ctskymap.hpp:116
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.
void setup_maps(void)
Setup maps.
Definition ctskymap.cpp:586
GChatter m_chatter
Chattiness.
Definition ctskymap.hpp:118
double m_outradius
Outer ring radius for RING background.
Definition ctskymap.hpp:113
void process(void)
Process the sky mapping tool.
Definition ctskymap.cpp:216
void setup_exclusion_map_fits(const GFilename &filename)
Fills exclusions map from FITS image.
Definition ctskymap.cpp:670
void init_members(void)
Initialise class members.
Definition ctskymap.cpp:364
GFits m_fits
Output GFits object.
Definition ctskymap.hpp:128
double m_emax
Maximum energy (TeV)
Definition ctskymap.hpp:109
GFilename m_inexclusion
Exclusion map file name.
Definition ctskymap.hpp:107
void construct_fits(void)
Construct GFits object consisting of all maps.
Definition ctskymap.cpp:287
GSkyMap m_skymap
Sky map.
Definition ctskymap.hpp:122
GSkyMap m_sigmap
Significance map.
Definition ctskymap.hpp:124
double m_cos_roiradius
Cosine of RoI radius.
Definition ctskymap.hpp:133
GSkyMap m_acceptance
Acceptance map.
Definition ctskymap.hpp:126
double sigma_li_ma(const double &n_on, const double &n_off, const double &alpha) const
Compute significance following Li & Ma.
ctskymap(void)
Void constructor.
Definition ctskymap.cpp:59
const GFits & fits(void) const
Return fits container.
Definition ctskymap.hpp:157
#define G_GET_PARAMETERS
Definition ctbin.cpp:42
#define G_RING_BOUNDING_BOX
Definition ctskymap.cpp:39
#define G_FILL_MAPS_ACCEPTANCE
Definition ctskymap.cpp:37
#define G_RING_KERNEL
Definition ctskymap.cpp:41
#define G_FILL_MAPS_COUNTS
Definition ctskymap.cpp:36
Sky mapping tool definition.
#define CTSKYMAP_NAME
Definition ctskymap.hpp:35