ctools 2.1.0.dev
Loading...
Searching...
No Matches
ctbin.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * ctbin - Event binning tool *
3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2010-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 ctbin.cpp
23 * @brief Event binning 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 "ctbin.hpp"
33#include "GTools.hpp"
34
35/* __ OpenMP section _____________________________________________________ */
36#ifdef _OPENMP
37#include <omp.h>
38#endif
39
40/* __ Method name definitions ____________________________________________ */
41#define G_CUBE "ctbin::cube(int&)"
42#define G_GET_PARAMETERS "ctbin::get_parameters()"
43#define G_FILL_CUBE "ctbin::fill_cube(GCTAObservation*)"
44#define G_SET_WEIGHTS "ctbin::set_weights(GCTAObservation*)"
45
46/* __ Debug definitions __________________________________________________ */
47
48/* __ Coding definitions _________________________________________________ */
49
50/* __ Constants __________________________________________________________ */
51
52
53/*==========================================================================
54 = =
55 = Constructors/destructors =
56 = =
57 ==========================================================================*/
58
59/***********************************************************************//**
60 * @brief Void constructor
61 *
62 * Constructs an empty event binning tool.
63 ***************************************************************************/
65{
66 // Initialise members
68
69 // Return
70 return;
71}
72
73
74/***********************************************************************//**
75 * @brief Observations constructor
76 *
77 * param[in] obs Observation container.
78 *
79 * Constructs event binning tool from an observation container.
80 ***************************************************************************/
81ctbin::ctbin(const GObservations& obs) :
82 ctobservation(CTBIN_NAME, VERSION, obs)
83{
84 // Initialise members
86
87 // Return
88 return;
89}
90
91
92/***********************************************************************//**
93 * @brief Command line constructor
94 *
95 * @param[in] argc Number of arguments in command line.
96 * @param[in] argv Array of command line arguments.
97 *
98 * Constructs event binning tool using command line arguments for user
99 * parameter setting.
100 ***************************************************************************/
101ctbin::ctbin(int argc, char *argv[]) :
102 ctobservation(CTBIN_NAME, VERSION, argc, argv)
103{
104 // Initialise members
105 init_members();
106
107 // Return
108 return;
109}
110
111
112/***********************************************************************//**
113 * @brief Copy constructor
114 *
115 * @param[in] app Event binning tool.
116 *
117 * Constructs event binning tool from another event binning tool.
118 ***************************************************************************/
120{
121 // Initialise members
122 init_members();
123
124 // Copy members
125 copy_members(app);
126
127 // Return
128 return;
129}
130
131
132/***********************************************************************//**
133 * @brief Destructor
134 *
135 * Destructs event binning tool.
136 ***************************************************************************/
138{
139 // Free members
140 free_members();
141
142 // Return
143 return;
144}
145
146
147/*==========================================================================
148 = =
149 = Operators =
150 = =
151 ==========================================================================*/
152
153/***********************************************************************//**
154 * @brief Assignment operator
155 *
156 * @param[in] app Event binning tool.
157 * @return Event binning tool.
158 *
159 * Assigns event binning tool.
160 ***************************************************************************/
162{
163 // Execute only if object is not identical
164 if (this != &app) {
165
166 // Copy base class members
167 this->ctobservation::operator=(app);
168
169 // Free members
170 free_members();
171
172 // Initialise members
173 init_members();
174
175 // Copy members
176 copy_members(app);
177
178 } // endif: object was not identical
179
180 // Return this object
181 return *this;
182}
183
184
185/*==========================================================================
186 = =
187 = Public methods =
188 = =
189 ==========================================================================*/
190
191/***********************************************************************//**
192 * @brief Clear event binning tool
193 *
194 * Clears event binning tool.
195 ***************************************************************************/
196void ctbin::clear(void)
197{
198 // Free members
199 free_members();
201 this->ctool::free_members();
202
203 // Clear base class (needed to conserve tool name and version)
204 this->GApplication::clear();
205
206 // Initialise members
207 this->ctool::init_members();
209 init_members();
210
211 // Write header into logger
212 log_header();
213
214 // Return
215 return;
216}
217
218
219/***********************************************************************//**
220 * @brief Process the event binning tool
221 *
222 * Gets the user parameters and loops over all CTA observations in the
223 * observation container to bin the events into either a single or a set of
224 * counts cube. All observations in the observation container that do not
225 * contain CTA event lists will be skipped.
226 ***************************************************************************/
228{
229 // Get task parameters
231
232 // Initialise sky direction cache for stacking
234
235 // Write input observation container into logger
236 log_observations(NORMAL, m_obs, "Input observation");
237
238 // Write header into logger
239 log_header1(TERSE, gammalib::number("Find unbinned observation",
240 m_obs.size()));
241
242 // Find all unbinned CTA observations in m_obs
243 std::vector<GCTAObservation*> obs_list(0);
244 double mean_ra = 0.0;
245 double mean_dec = 0.0;
246 for (GCTAObservation* obs = first_unbinned_observation(); obs != NULL;
248
249 // Push observation into list
250 obs_list.push_back(obs);
251
252 // Get pointing
253 const GCTAPointing& pnt = obs->pointing();
254
255 // Add to coordinates
256 mean_ra += pnt.dir().ra_deg();
257 mean_dec += pnt.dir().dec_deg();
258
259 // Write message
260 std::string msg = " Including unbinned "+obs->instrument()+
261 " observation";
262 log_string(NORMAL, msg);
263
264 } // endfor: looped over all unbinned observations
265
266 // Set number of relevant observations
267 int nobs = obs_list.size();
268
269 // Compute mean pointing
270 if (nobs > 0) {
271 mean_ra /= double(nobs);
272 mean_dec /= double(nobs);
273 m_mean_pnt.radec_deg(mean_ra, mean_dec);
274 }
275
276 // Initialise arrays
277 if (nobs > 0) {
278
279 // Determine number of cubes
280 int ncubes = (m_stack) ? 1 : nobs;
281
282 // Allocate cubes
283 m_counts = std::vector<GSkyMap>(ncubes);
284 m_weights = std::vector<GSkyMap>(ncubes);
285 m_cubes = std::vector<GCTAEventCube>(ncubes);
286
287 }
288 else {
289 m_counts.clear();
290 m_weights.clear();
291 m_cubes.clear();
292 }
293
294 // Set mean pointing usage
295 std::string use;
296 if (m_stack && nobs > 0) {
297 use = (*this)["usepnt"].boolean() ? "yes" : "no";
298 }
299 else {
300 use = "no";
301 }
302
303 // Log observation selection results
304 log_header3(NORMAL, "Summary");
305 log_value(NORMAL, "Number of observations", nobs);
306 log_value(NORMAL, "Mean pointing", m_mean_pnt.print());
307 log_value(NORMAL, "Use mean pointing", use);
308
309 // Write header into logger
310 log_header1(TERSE, gammalib::number("Bin observation", nobs));
311
312 // Loop over all unbinned CTA observations in the container
313 #pragma omp parallel for
314 for (int i = 0; i < nobs; ++i) {
315
316 // Get pointer to observation
317 GCTAObservation* obs = obs_list[i];
318
319 // Determine counts cube slot
320 int islot = (m_stack) ? 0 : i;
321
322 // If slot is empty then allocate cubes
323 #pragma omp critical(ctbin_run)
324 if (m_counts[islot].is_empty()) {
325 m_counts[islot] = create_cube(obs);
326 m_weights[islot] = create_cube(obs);
327 }
328
329 // Fill counts cube
330 fill_cube(obs, m_counts[islot]);
331
332 // Set the counts cube weights
333 set_weights(obs, m_weights[islot]);
334
335 // Dispose events to free memory
336 obs->dispose_events();
337
338 } // endfor: looped over observations
339
340 // Combine GTIs of all observations and compute total ontime and livetime
341 for (int i = 0; i < nobs; ++i) {
342
343 // Get pointer to observation
344 GCTAObservation* obs = obs_list[i];
345
346 // Get Good Time Intervals for observation
347 GGti gti = obs->gti();
348
349 // If resulting GTI is not filled then copy over the reference from
350 // the observation and use it for the resulting GTI
351 if (m_gti.is_empty()) {
352 m_gti.reference(gti.reference());
353 }
354
355 // Append GTIs
356 m_gti.extend(gti);
357
358 // Update ontime and livetime
359 m_ontime += obs->ontime();
360 m_livetime += obs->livetime();
361
362 } // endfor: looped over all observations
363
364 // Post process data for a stacked cube
365 if (m_stack && nobs > 0) {
366
367 // Compute livetime fraction per energy bin so that varying energy
368 // thresholds are correctly taken into account in the weighting
369 // array. Computational speed is not optimum, we have to see later
370 // whether this is an issue.
371 for (int iebin = 0; iebin < m_ebounds.size(); ++iebin) {
372 double livetime_ebin = 0.0;
373 for (int i = 0; i < obs_list.size(); ++i) {
374 std::vector<bool> usage =
375 cube_layer_usage(m_ebounds, obs_list[i]->ebounds());
376 if (usage[iebin]) {
377 livetime_ebin += obs_list[i]->livetime();
378 }
379 }
380 if (m_livetime > 0.0) {
381 double fraction = livetime_ebin / m_livetime;
382 for (int pixel = 0; pixel < m_counts[0].npix(); ++pixel) {
383 if (m_weights[0](pixel, iebin) > 0.0) {
384 m_weights[0](pixel, iebin) *= fraction;
385 }
386 }
387 }
388 }
389
390 // Set a single cube in the observation container
392
393 } // endif: stacked cube post processing
394
395 // Post process data for non-stacked cubes
396 else {
397
398 // Loop over all relevant observations
399 for (int i = 0; i < nobs; ++i) {
400
401 // Get pointer to observation
402 GCTAObservation* obs = obs_list[i];
403
404 // Build counts cube
405 m_cubes[i] = GCTAEventCube(m_counts[i], m_weights[i], m_ebounds,
406 obs->gti());
407
408 // Attach event cube in the correct slot of the observation
409 // container
410 obs->events(m_cubes[i]);
411
412 // Construct unique filename for possible saving of the counts
413 // cube into FITS files. Each observation has a unique pair of
414 // instrument() and id() attributes, hence we construct a lower
415 // case file name from these attributes, making sure that all
416 // whitespaces are converted into "_" characters. Note that the
417 // id() attribute is allowed to be blank, which can only happen
418 // for a single observation.
419 std::string inst = gammalib::tolower(obs->instrument());
420 std::string id = gammalib::tolower(obs->id());
421 inst = gammalib::replace_segment(inst, " ", "_");
422 id = gammalib::replace_segment(id, " ", "_");
423 std::string filename = m_prefix + inst;
424 if (!id.empty()) {
425 filename += "_" + id;
426 }
427 filename += ".fits";
428
429 // Set filename of observation
430 obs->eventfile(filename);
431
432 } // endfor: looped over relevant observations
433
434 } // endelse: non-stacked cube post processing
435
436 // Write resulting observation container into logger
437 log_observations(NORMAL, m_obs, "Binned observation");
438
439 // Optionally publish counts cube
440 if (m_publish) {
441 publish();
442 }
443
444 // Return
445 return;
446}
447
448
449/***********************************************************************//**
450 * @brief Return event cube at index
451 *
452 * @param[in] index Cube index (0,...,cubes()-1).
453 * @return Reference to event cube.
454 *
455 * @exception GException::out_of_range
456 * Event cube index out of range.
457 *
458 * Returns a reference to the event cube at the given index.
459 ***************************************************************************/
460const GCTAEventCube& ctbin::cube(const int& index) const
461{
462 // Compile option: raise an exception if index is out of range
463 if (index < 0 || index >= cubes()) {
464 throw GException::out_of_range(G_CUBE, "Cube index", index, cubes());
465 }
466
467 // Return cube
468 return m_cubes[index];
469}
470
471
472/***********************************************************************//**
473 * @brief Save counts cube
474 *
475 * Saves the counts cube. If the counts cube was stacked single FITS file
476 * will be written. Otherwise each generated counts cube will be written
477 * into a FITS file, and an observation definition XML file will be written
478 * out that contains an observation for each counts cube.
479 ***************************************************************************/
480void ctbin::save(void)
481{
482 // Write header
483 log_header1(TERSE, "Save counts cube");
484
485 // If counts cube was stacked then save first observation
486 if (m_stack) {
487
488 // Save only if filename is valid and if there is at least one
489 // observation
490 if ((*this)["outobs"].is_valid() && m_obs.size() > 0) {
491
492 // Get counts cube filename
493 GFilename outobs = (*this)["outobs"].filename();
494
495 // Get CTA observation from observation container
496 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
497
498 // Save only if observation is valid
499 if (obs != NULL) {
500
501 // Log counts cube file name
502 log_value(NORMAL, "Counts cube file", outobs.url());
503
504 // Save cube
505 obs->save(outobs, clobber());
506
507 // Stamp counts cube
508 stamp(outobs);
509
510 } // endif: observation was valid
511
512 } // endif: outobs file was valid
513
514 } // endif: counts cube was stacked
515
516 // ... otherwise write out all relevant counts cubes and the observation
517 // definition XML file
518 else {
519
520 // Loop over all observations
521 for (int i = 0; i < m_obs.size(); ++i) {
522
523 // Get CTA observation from observation container
524 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
525
526 // Continue only if observation is valid
527 if (obs != NULL) {
528
529 // Retrieve filename
530 std::string filename = obs->eventfile();
531
532 // Save counts cube
533 obs->save(filename, clobber());
534
535 // Stamp counts cube
536 stamp(filename);
537
538 } // endif: observation was valid
539
540 } // endfor: looped over all observations
541
542 // Write XML file
543 if ((*this)["outobs"].is_valid()) {
544
545 // Get observation definition XML filename
546 std::string outobs = (*this)["outobs"].filename();
547
548 // Issue warning if output filename has no .xml suffix
549 log_string(TERSE, warn_xml_suffix(outobs));
550
551 // Save XML file
552 m_obs.save(outobs);
553 }
554
555 } // endelse: counts cube were not stacked
556
557 // Return
558 return;
559}
560
561
562/***********************************************************************//**
563 * @brief Publish counts cube
564 *
565 * @param[in] name Counts cube name.
566 ***************************************************************************/
567void ctbin::publish(const std::string& name)
568{
569 // Write header into logger
570 log_header1(TERSE, "Publish counts cube");
571
572 // Set default name is user name is empty
573 std::string user_name(name);
574 if (user_name.empty()) {
575 user_name = CTBIN_NAME;
576 }
577
578 // Write counts cube name into logger
579 log_value(NORMAL, "Counts cube name", user_name);
580
581 // Publish first counts cube if it exists
582 if (cubes() > 0) {
583 m_counts[0].publish(user_name);
584 }
585
586 // Return
587 return;
588}
589
590
591/*==========================================================================
592 = =
593 = Private methods =
594 = =
595 ==========================================================================*/
596
597/***********************************************************************//**
598 * @brief Initialise class members
599 ***************************************************************************/
601{
602 // Initialise members
603 //m_usepnt = false;
604 m_stack = true;
605 m_prefix.clear();
606 m_publish = false;
607 m_chatter = static_cast<GChatter>(2);
608
609 // Initialise protected members
610 m_cubes.clear();
611 m_counts.clear();
612 m_weights.clear();
613 m_mean_pnt.clear();
614 m_ebounds.clear();
615 m_gti.clear();
616 m_ontime = 0.0;
617 m_livetime = 0.0;
618
619 // Initialise cache members
620 m_dirs.clear();
621
622 // Return
623 return;
624}
625
626
627/***********************************************************************//**
628 * @brief Copy class members
629 *
630 * @param[in] app Application.
631 ***************************************************************************/
633{
634 // Copy attributes
635 //m_usepnt = app.m_usepnt;
636 m_stack = app.m_stack;
637 m_prefix = app.m_prefix;
638 m_publish = app.m_publish;
639 m_chatter = app.m_chatter;
640
641 // Copy protected members
642 m_cubes = app.m_cubes;
643 m_counts = app.m_counts;
644 m_weights = app.m_weights;
645 m_mean_pnt = app.m_mean_pnt;
646 m_ebounds = app.m_ebounds;
647 m_gti = app.m_gti;
648 m_ontime = app.m_ontime;
649 m_livetime = app.m_livetime;
650
651 // Copy cache members
652 m_dirs = app.m_dirs;
653
654 // Return
655 return;
656}
657
658
659/***********************************************************************//**
660 * @brief Delete class members
661 ***************************************************************************/
663{
664 // Return
665 return;
666}
667
668
669/***********************************************************************//**
670 * @brief Get application parameters
671 *
672 * Get all task parameters from parameter file or (if required) by querying
673 * the user. Most parameters are only required if no observation exists so
674 * far in the observation container. In this case, a single CTA observation
675 * will be added to the container, using the definition provided in the
676 * parameter file.
677 ***************************************************************************/
679{
680 // Setup observations from "inobs" parameter. Do not request response
681 // information and do not accept counts cubes.
682 setup_observations(m_obs, false, true, false);
683
684 // Create an event cube to query task parameters
685 GCTAEventCube cube = this->ctool::create_cube(m_obs);
686
687 // Get energy boundaries
688 m_ebounds = cube.ebounds();
689
690 // Get stack and prefix parameters
691 m_stack = (*this)["stack"].boolean();
692 m_prefix = (*this)["prefix"].string();
693
694 // Get remaining parameters
695 m_publish = (*this)["publish"].boolean();
696 m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
697
698 // If needed later, query output filename now
699 if (read_ahead()) {
700 (*this)["outobs"].query();
701 }
702
703 // Set number of OpenMP threads
704 #ifdef _OPENMP
705 int nthreads = (*this)["nthreads"].integer();
706 if (nthreads > 0) {
707 omp_set_num_threads(nthreads);
708 }
709 #endif
710
711 // Write parameters into logger
712 log_parameters(TERSE);
713
714 // Return
715 return;
716}
717
718
719/***********************************************************************//**
720 * @brief Initialise sky direction cache for cube stack
721 *
722 * Initialises a cache that holds the sky directions of each counts cube
723 * pixel for a stacked analysis. This will speed up the computations later.
724 *
725 * If no stacking is requested, the method does nothing.
726 ***************************************************************************/
728{
729 // Continue only is stacking is requested
730 if (m_stack) {
731
732 // Create a sky map from task parameters
733 GSkyMap map = this->ctool::create_map(m_obs);
734
735 // Initialise sky direction
736 m_dirs.resize(map.npix());
737
738 // Compute sky direction for each pixel
739 for (int pixel = 0; pixel < map.npix(); ++pixel) {
740 m_dirs[pixel] = map.inx2dir(pixel);
741 }
742
743 } // endif: stacking was requested
744
745 // Return
746 return;
747}
748
749
750/***********************************************************************//**
751 * @brief Create counts cube sky map for current observation
752 *
753 * @param[in] obs CTA observation.
754 *
755 * Creates a counts cube sky map for current observations.
756 ***************************************************************************/
757GSkyMap ctbin::create_cube(const GCTAObservation* obs)
758{
759 // Read coordinate system and projection
760 std::string coordsys = (*this)["coordsys"].string();
761 std::string proj = (*this)["proj"].string();
762
763 // Initialise sky map reference
764 double xref = 0.0;
765 double yref = 0.0;
766
767 // If pointing direction should be used then extract sky map reference
768 // from observation(s)
769 if ((*this)["usepnt"].boolean()) {
770
771 // If stacking is requested then use the mean pointing direction
772 if (m_stack) {
773 if (gammalib::toupper(coordsys) == "GAL") {
774 xref = m_mean_pnt.l_deg();
775 yref = m_mean_pnt.b_deg();
776 }
777 else {
778 xref = m_mean_pnt.ra_deg();
779 yref = m_mean_pnt.dec_deg();
780 }
781 }
782
783 // ... otherwise use the pointing direction of the observation
784 else {
785
786 // Get pointing
787 const GCTAPointing& pnt = obs->pointing();
788
789 // Extract coordinates
790 if (gammalib::toupper(coordsys) == "GAL") {
791 xref = pnt.dir().l_deg();
792 yref = pnt.dir().b_deg();
793 }
794 else {
795 xref = pnt.dir().ra_deg();
796 yref = pnt.dir().dec_deg();
797 }
798
799 } // endelse: use pointing direction of observation
800
801 } // endif: use pointing direction
802
803 // ... otherwise use User defined sky map reference
804 else {
805 xref = (*this)["xref"].real();
806 yref = (*this)["yref"].real();
807 }
808
809 // Read sky map pixel size and number of bins
810 double binsz = (*this)["binsz"].real();
811 int nxpix = (*this)["nxpix"].integer();
812 int nypix = (*this)["nypix"].integer();
813
814 // Read energy boundaries
815 GEbounds ebounds = create_ebounds();
816
817 // Initialise counts cube
818 GSkyMap cube = GSkyMap(proj, coordsys, xref, yref, -binsz, binsz,
819 nxpix, nypix, ebounds.size());
820
821 // Return cube
822 return cube;
823}
824
825
826/***********************************************************************//**
827 * @brief Fill events into counts cube
828 *
829 * @param[in] obs CTA observation.
830 * @param[in,out] counts Counts cube
831 *
832 * @exception GException::invalid_value
833 * No event list or valid RoI found in observation.
834 *
835 * Fills the events from an event list into the counts cube.
836 ***************************************************************************/
837void ctbin::fill_cube(const GCTAObservation* obs, GSkyMap& counts)
838{
839 // Make sure that the observation holds a CTA event list. If this is
840 // not the case then throw an exception.
841 const GCTAEventList* events = dynamic_cast<const GCTAEventList*>
842 (obs->events());
843 if (events == NULL) {
844 std::string msg = "CTA Observation does not contain an event "
845 "list. An event list is needed to fill the "
846 "counts cube.";
847 throw GException::invalid_value(G_FILL_CUBE, msg);
848 }
849
850 // Get the RoI
851 const GCTARoi& roi = obs->roi();
852
853 // Check for RoI sanity
854 if (!roi.is_valid()) {
855 std::string msg = "No valid RoI found in input observation "
856 "\""+obs->name()+"\". Run ctselect to specify "
857 "an RoI for this observation before running "
858 "ctbin.";
859 throw GException::invalid_value(G_FILL_CUBE, msg);
860 }
861
862 // Get counts cube usage flags
863 std::vector<bool> usage = cube_layer_usage(m_ebounds, obs->ebounds());
864
865 // Initialise binning statistics
866 int num_outside_roi = 0;
867 int num_invalid_wcs = 0;
868 int num_outside_map = 0;
869 int num_outside_ebds = 0;
870 int num_in_map = 0;
871
872 // Extract counts cube boundaries
873 double pixel_x_max = counts.nx() - 0.5;
874 double pixel_y_max = counts.ny() - 0.5;
875
876 // Fill counts sky map
877 for (int i = 0; i < events->size(); ++i) {
878
879 // Get event
880 const GCTAEventAtom* event = (*events)[i];
881
882 // Determine event sky direction
883 GCTAInstDir* inst = (GCTAInstDir*)&(event->dir());
884 GSkyDir dir = inst->dir();
885
886 // Skip event if it is outside the RoI
887 if (roi.centre().dir().dist_deg(dir) > roi.radius()) {
888 num_outside_roi++;
889 continue;
890 }
891
892 // Determine sky pixel
893 GSkyPixel pixel;
894 try {
895 pixel = counts.dir2pix(dir);
896 }
897 catch (std::exception &e) {
898 num_invalid_wcs++;
899 continue;
900 }
901
902 // Skip event if corresponding counts cube pixel is outside the
903 // counts cube map range
904 if (pixel.x() < -0.5 || pixel.x() > pixel_x_max ||
905 pixel.y() < -0.5 || pixel.y() > pixel_y_max) {
906 num_outside_map++;
907 continue;
908 }
909
910 // Determine counts cube energy bin
911 int iebin = m_ebounds.index(event->energy());
912
913 // Skip event if the corresponding counts cube energy bin is not
914 // fully contained in the event list energy range. This avoids
915 // having partially filled bins.
916 if (iebin == -1 || !usage[iebin]) {
917 num_outside_ebds++;
918 continue;
919 }
920
921 // Fill event in skymap
922 #pragma omp atomic
923 counts(pixel, iebin) += 1.0;
924
925 // Increment number of maps
926 num_in_map++;
927
928 } // endfor: looped over all events
929
930 // Log filling results
931 #pragma omp critical(ctbin_fill_cube)
932 {
933 log_header3(TERSE, get_obs_header(obs));
934 log_value(NORMAL, "Events in list", obs->events()->size());
935 log_value(NORMAL, "Events in cube", num_in_map);
936 log_value(NORMAL, "Events outside RoI", num_outside_roi);
937 log_value(NORMAL, "Events with invalid WCS", num_invalid_wcs);
938 log_value(NORMAL, "Events outside cube area", num_outside_map);
939 log_value(NORMAL, "Events outside energy bins", num_outside_ebds);
940 }
941
942 // Return
943 return;
944}
945
946
947/***********************************************************************//**
948 * @brief Set counts cube weights for a given observation
949 *
950 * @param[in] obs CTA observation.
951 * @param[in,out] weights Counts cube weights.
952 *
953 * @exception GException::invalid_value
954 * No event list or valid RoI found in observation.
955 *
956 * Sets the counts cube weights for all bins that are considered for the
957 * specific observation to unity.
958 ***************************************************************************/
959void ctbin::set_weights(const GCTAObservation* obs, GSkyMap& weights)
960{
961 // Get the RoI
962 const GCTARoi& roi = obs->roi();
963
964 // Check for RoI sanity
965 if (!roi.is_valid()) {
966 std::string msg = "No valid RoI found in input observation "
967 "\""+obs->name()+"\". Run ctselect to specify "
968 "an RoI for this observation before running "
969 "ctbin.";
970 throw GException::invalid_value(G_SET_WEIGHTS, msg);
971 }
972
973 // Get the RoI centre and radius in radians
974 GSkyDir roi_centre = roi.centre().dir();
975 double roi_radius = roi.radius() * gammalib::deg2rad;
976
977 // Get counts cube usage flags
978 std::vector<bool> usage = cube_layer_usage(m_ebounds, obs->ebounds());
979
980 // Set sky direction cache availability flag
981 bool cache = (m_stack && m_dirs.size() == weights.npix());
982
983 // Loop over all pixels in counts cube
984 for (int pixel = 0; pixel < weights.npix(); ++pixel) {
985
986 // Skip pixel if it is outside the RoI
987 if (cache) {
988 if (m_dirs[pixel].dist(roi_centre) > roi_radius) {
989 continue;
990 }
991 }
992 else {
993 if (roi_centre.dist(weights.inx2dir(pixel)) > roi_radius) {
994 continue;
995 }
996 }
997
998 // Loop over all energy layers of counts cube
999 for (int iebin = 0; iebin < m_ebounds.size(); ++iebin){
1000
1001 // Skip energy layer if the usage flag is false
1002 if (!usage[iebin]) {
1003 continue;
1004 }
1005
1006 // Signal that bin was filled
1007 weights(pixel, iebin) = 1.0;
1008
1009 } // endfor: looped over energy layers of counts cube
1010
1011 } // endfor: looped over pixels of counts cube
1012
1013 // Return
1014 return;
1015}
1016
1017
1018/***********************************************************************//**
1019 * @brief Create output observation container.
1020 *
1021 * Creates an output observation container that combines all input CTA
1022 * observation into a single stacked observation. All non-CTA observations
1023 * and all binned CTA observations that were present in the observation
1024 * container are append to the observation container so that they can
1025 * be used by other tools. The method furthermore conserves any response
1026 * information in case that a single CTA observation is provided to support
1027 * binned analysis.
1028 ***************************************************************************/
1030{
1031 // Set event cube in first slot
1032 m_cubes[0] = GCTAEventCube(m_counts[0], m_weights[0], m_ebounds, m_gti);
1033
1034 // If we have only a single CTA observation in the container, then
1035 // keep that observation and just attach the event cube to it. Reset
1036 // the filename, otherwise we still will have the old event filename
1037 // in the log file.
1038 if (m_obs.size() == 1) {
1039
1040 // Attach event cube to CTA observation
1041 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
1042 if (obs != NULL) {
1043
1044 // Change the event type if we had an unbinned observation
1045 if (obs->eventtype() == "EventList") {
1046
1047 // Assign cube to the observation
1048 obs->events(m_cubes[0]);
1049
1050 }
1051
1052 // ... otherwise the input observation was binned and hence
1053 // skipped. In that case we simply append an empty counts
1054 // cube
1055 else {
1056
1057 // Create empty counts cube
1058 GCTAEventCube cube(m_counts[0], m_weights[0], m_ebounds,
1059 obs->gti());
1060
1061 // Assign empty cube to observation
1062 obs->events(cube);
1063
1064 }
1065
1066 // Reset file name
1067 obs->eventfile("");
1068
1069 } // endif: obervation was valid
1070
1071 } // endif: we only had one observation in the container
1072
1073 // ... otherwise put a single CTA observation in container and
1074 // append all observations that have not been used in the binning
1075 // to the container
1076 else {
1077
1078 // Allocate observation container
1079 GObservations container;
1080
1081 // Allocate CTA observation.
1082 GCTAObservation obs;
1083
1084 // Attach event cube to CTA observation
1085 obs.events(m_cubes[0]);
1086
1087 // Compute average pointing direction for all CTA event lists and
1088 // extract first instrument
1089 double ra = 0.0;
1090 double dec = 0.0;
1091 double number = 0.0;
1092 std::string instrument = "";
1093 for (int i = 0; i < m_obs.size(); ++i) {
1094 GCTAObservation* cta = dynamic_cast<GCTAObservation*>(m_obs[i]);
1095 if ((cta != NULL) && (cta->eventtype() == "EventList")) {
1096 ra += cta->pointing().dir().ra();
1097 dec += cta->pointing().dir().dec();
1098 number += 1.0;
1099 if (instrument.empty()) {
1100 instrument = cta->instrument();
1101 }
1102 }
1103 }
1104 if (number > 0.0) {
1105 ra /= number;
1106 dec /= number;
1107 }
1108 if (instrument.empty()) {
1109 instrument = "cta";
1110 }
1111 GSkyDir dir;
1112 dir.radec(ra, dec);
1113 GCTAPointing pointing(dir);
1114
1115 // Compute deadtime correction
1116 double deadc = (m_ontime > 0.0) ? m_livetime / m_ontime : 0.0;
1117
1118 // Set CTA observation attributes
1119 obs.instrument(instrument);
1120 obs.pointing(pointing);
1121 obs.ra_obj(dir.ra_deg()); //!< Dummy
1122 obs.dec_obj(dir.dec_deg()); //!< Dummy
1123 obs.ontime(m_ontime);
1124 obs.livetime(m_livetime);
1125 obs.deadc(deadc);
1126
1127 // Set models in observation container
1128 container.models(m_obs.models());
1129
1130 // Append CTA observation
1131 container.append(obs);
1132
1133 // Copy over all remaining non-CTA observations
1134 for (int i = 0; i < m_obs.size(); ++i) {
1135 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
1136 if (obs == NULL) {
1137 container.append(*m_obs[i]);
1138 }
1139 else if (obs->eventtype() != "EventList") {
1140 container.append(*m_obs[i]);
1141 }
1142 }
1143
1144 // Set observation container
1145 m_obs = container;
1146
1147 } // endelse: there was not a single CTA observation
1148
1149 // Return
1150 return;
1151}
Event binning tool.
Definition ctbin.hpp:69
GSkyMap create_cube(const GCTAObservation *obs)
Create counts cube sky map for current observation.
Definition ctbin.cpp:757
void set_weights(const GCTAObservation *obs, GSkyMap &weights)
Set counts cube weights for a given observation.
Definition ctbin.cpp:959
GEbounds m_ebounds
Energy boundaries.
Definition ctbin.hpp:112
void process(void)
Process the event binning tool.
Definition ctbin.cpp:227
GGti m_gti
Stacked Good time intervals.
Definition ctbin.hpp:113
std::string m_prefix
Prefix for multiple cubes.
Definition ctbin.hpp:103
void obs_cube_stacked(void)
Create output observation container.
Definition ctbin.cpp:1029
ctbin & operator=(const ctbin &app)
Assignment operator.
Definition ctbin.cpp:161
void fill_cube(const GCTAObservation *obs, GSkyMap &counts)
Fill events into counts cube.
Definition ctbin.cpp:837
GSkyDir m_mean_pnt
Mean pointing.
Definition ctbin.hpp:111
std::vector< GSkyMap > m_weights
List of event cube weights.
Definition ctbin.hpp:110
void init_sky_dir_cache(void)
Initialise sky direction cache for cube stack.
Definition ctbin.cpp:727
ctbin(void)
Void constructor.
Definition ctbin.cpp:64
std::vector< GSkyMap > m_counts
List of event cube counts.
Definition ctbin.hpp:109
void get_parameters(void)
Get application parameters.
Definition ctbin.cpp:678
void clear(void)
Clear event binning tool.
Definition ctbin.cpp:196
std::vector< GCTAEventCube > m_cubes
Event cubes.
Definition ctbin.hpp:108
double m_livetime
Total livetime.
Definition ctbin.hpp:115
bool m_publish
Publish counts cube?
Definition ctbin.hpp:104
virtual ~ctbin(void)
Destructor.
Definition ctbin.cpp:137
double m_ontime
Total ontime.
Definition ctbin.hpp:114
bool m_stack
Output one stacked cube or multiple cubes.
Definition ctbin.hpp:102
void copy_members(const ctbin &app)
Copy class members.
Definition ctbin.cpp:632
void free_members(void)
Delete class members.
Definition ctbin.cpp:662
const GCTAEventCube & cube(const int &index=0) const
Return event cube at index.
Definition ctbin.cpp:460
void save(void)
Save counts cube.
Definition ctbin.cpp:480
GChatter m_chatter
Chattiness.
Definition ctbin.hpp:105
int cubes(void) const
Return number of event cubes.
Definition ctbin.hpp:130
std::vector< GSkyDir > m_dirs
Cached GSkyDir for all pixels.
Definition ctbin.hpp:118
void init_members(void)
Initialise class members.
Definition ctbin.cpp:600
void publish(const std::string &name="")
Publish counts cube.
Definition ctbin.cpp:567
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.
GCTAObservation * first_unbinned_observation(void)
Return first unbinned CTA observation.
const GObservations & obs(void) const
Return observation container.
void init_members(void)
Initialise class members.
GEbounds create_ebounds(void)
Create energy boundaries from user parameters.
Definition ctool.cpp:612
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
GCTAEventCube create_cube(const GObservations &obs)
Create a CTA event cube from user parameters.
Definition ctool.cpp:1006
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::vector< bool > cube_layer_usage(const GEbounds &cube_ebounds, const GEbounds &list_ebounds) const
Determine the counts cube layer usage.
Definition ctool.cpp:2194
std::string warn_xml_suffix(const GFilename &filename) const
Set warning string if file has no .xml suffix.
Definition ctool.cpp:2427
std::string get_obs_header(const GObservation *obs) const
Return observation header string.
Definition ctool.cpp:2090
#define G_FILL_CUBE
Definition ctbin.cpp:43
#define G_CUBE
Definition ctbin.cpp:41
#define G_SET_WEIGHTS
Definition ctbin.cpp:44
Event binning tool definition.
#define CTBIN_NAME
Definition ctbin.hpp:34