ctools 2.1.0.dev
Loading...
Searching...
No Matches
ctmodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * ctmodel - Model cube generation tool *
3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2012-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 ctmodel.cpp
23 * @brief Model cube generation 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 "ctmodel.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_GET_PARAMETERS "ctmodel::get_parameters()"
42#define G_GET_OBS "ctmodel::get_obs()"
43#define G_FILL_CUBE "ctmodel::fill_cube(GCTAObservation*)"
44
45/* __ Debug definitions __________________________________________________ */
46
47/* __ Coding definitions _________________________________________________ */
48
49/* __ Constants __________________________________________________________ */
50const GEnergy g_energy_margin(1.0e-12, "TeV");
51
52
53/*==========================================================================
54 = =
55 = Constructors/destructors =
56 = =
57 ==========================================================================*/
58
59/***********************************************************************//**
60 * @brief Void constructor
61 ***************************************************************************/
63{
64 // Initialise members
66
67 // Return
68 return;
69}
70
71
72/***********************************************************************//**
73 * @brief Observations constructor
74 *
75 * param[in] obs Observation container.
76 *
77 * This method creates an instance of the class by copying an existing
78 * observations container.
79 ***************************************************************************/
80ctmodel::ctmodel(const GObservations& obs) :
81 ctobservation(CTMODEL_NAME, VERSION, obs)
82{
83 // Initialise members
85
86 // Return
87 return;
88}
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 ***************************************************************************/
98ctmodel::ctmodel(int argc, char *argv[]) :
99 ctobservation(CTMODEL_NAME, VERSION, argc, argv)
100{
101 // Initialise members
102 init_members();
103
104 // Return
105 return;
106}
107
108
109/***********************************************************************//**
110 * @brief Copy constructor
111 *
112 * @param[in] app Application.
113 ***************************************************************************/
115{
116 // Initialise members
117 init_members();
118
119 // Copy members
120 copy_members(app);
121
122 // Return
123 return;
124}
125
126
127/***********************************************************************//**
128 * @brief Destructor
129 ***************************************************************************/
131{
132 // Free members
133 free_members();
134
135 // Return
136 return;
137}
138
139
140/*==========================================================================
141 = =
142 = Operators =
143 = =
144 ==========================================================================*/
145
146/***********************************************************************//**
147 * @brief Assignment operator
148 *
149 * @param[in] app Application.
150 * @return Application.
151 ***************************************************************************/
153{
154 // Execute only if object is not identical
155 if (this != &app) {
156
157 // Copy base class members
158 this->ctobservation::operator=(app);
159
160 // Free members
161 free_members();
162
163 // Initialise members
164 init_members();
165
166 // Copy members
167 copy_members(app);
168
169 } // endif: object was not identical
170
171 // Return this object
172 return *this;
173}
174
175
176/*==========================================================================
177 = =
178 = Public methods =
179 = =
180 ==========================================================================*/
181
182/***********************************************************************//**
183 * @brief Clear ctmodel tool
184 *
185 * Clears ctmodel tool.
186 ***************************************************************************/
188{
189 // Free members
190 free_members();
192 this->ctool::free_members();
193
194 // Clear base class (needed to conserve tool name and version)
195 this->GApplication::clear();
196
197 // Initialise members
198 this->ctool::init_members();
200 init_members();
201
202 // Write header into logger
203 log_header();
204
205 // Return
206 return;
207}
208
209
210/***********************************************************************//**
211 * @brief Generate the model map(s)
212 *
213 * This method reads the task parameters from the parfile, sets up the
214 * observation container, loops over all CTA observations in the container
215 * and generates a model map for each CTA observation.
216 ***************************************************************************/
218{
219 // Get task parameters
221
222 // Extract cube properties
224
225 // Set energy dispersion flags of all CTA observations and save old
226 // values in save_edisp vector
227 std::vector<bool> save_edisp = set_edisp(m_obs, m_apply_edisp);
228
229 // Write input observation container into logger
230 log_observations(NORMAL, m_obs, "Input observation");
231
232 // Write input model container into logger
233 log_models(NORMAL, m_obs.models(), "Input model");
234
235 // Write header
236 log_header1(TERSE, "Generate model cube");
237
238 // Loop over all observations in the container
239 #pragma omp parallel
240 {
241 // Create a models object for this core
242 GModels models(m_obs.models());
243
244 #pragma omp for schedule(static,1)
245 for (int i = 0; i < m_obs.size(); ++i) {
246
247 // Get CTA observation
248 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
249
250 // Skip observation if it's not CTA
251 if (obs == NULL) {
252 log_header3(TERSE, get_obs_header(m_obs[i]));
253 std::string msg = " Skipping "+m_obs[i]->instrument()+
254 " observation";
255 log_string(NORMAL, msg);
256 }
257
258 // Fill cube and leave loop if we are binned mode (meaning we
259 // only have one binned observation)
260 else if (m_binned) {
262 i = m_obs.size();
263 }
264
265 // Skip observation if we have a binned observation
266 else if (obs->eventtype() == "CountsCube") {
267 log_header3(TERSE, get_obs_header(m_obs[i]));
268 std::string msg = " Skipping binned "+m_obs[i]->instrument()+
269 " observation";
270 log_string(NORMAL, msg);
271 }
272
273 // Otherwise, everything seems to be fine, so fill the cube from obs
274 else {
275 // Fill the cube
277
278 // Dispose events to free memory if event file exists on disk
279 if (obs->eventfile().length() > 0 && obs->eventfile().exists()) {
280 obs->dispose_events();
281 }
282 }
283
284 } // endfor: looped over observations
285
286 } // end openmp parallel section
287
288 // Write model cube into header
289 log_header1(NORMAL, "Model cube");
290 log_string(NORMAL, m_cube.print());
291
292 // Restore energy dispersion flags of all CTA observations
293 restore_edisp(m_obs, save_edisp);
294
295 // Optionally publish model cube
296 if (m_publish) {
297 publish();
298 }
299
300 // Return
301 return;
302}
303
304
305/***********************************************************************//**
306 * @brief Save model cube
307 *
308 * Saves the model cube into a FITS file specified using the "outfile"
309 * task parameter.
310 ***************************************************************************/
312{
313 // Write header
314 log_header1(TERSE, "Save model cube");
315
316 // Save only if filename is valid and cube has some size
317 if ((*this)["outcube"].is_valid() && m_cube.size() > 0) {
318
319 // Get model cube filename
320 m_outcube = (*this)["outcube"].filename();
321
322 // Save cube
323 m_cube.save(m_outcube, clobber());
324
325 // Write mandatory keywords
326 GFits fits(m_outcube);
327 for (int i = 0; i < fits.size(); ++i) {
328 fits[i]->card("RA_PNT", m_ra_pnt, "[deg] Pointing Right Ascension");
329 fits[i]->card("DEC_PNT", m_dec_pnt, "[deg] Pointing Declination");
330 }
331 fits.save(true);
332
333 // Stamp model cube
334 stamp(m_outcube);
335
336 }
337
338 // Write into logger what has been done
339 std::string fname = (m_outcube.is_empty()) ? "NONE" : m_outcube.url();
340 if (m_cube.size() == 0) {
341 fname.append(" (cube is empty, no file created)");
342 }
343 log_value(NORMAL, "Model cube file", fname);
344
345 // Return
346 return;
347}
348
349
350/***********************************************************************//**
351 * @brief Publish model cube
352 *
353 * @param[in] name Model cube name.
354 ***************************************************************************/
355void ctmodel::publish(const std::string& name)
356{
357 // Write header into logger
358 log_header1(TERSE, "Publish model cube");
359
360 // Set default name is user name is empty
361 std::string user_name(name);
362 if (user_name.empty()) {
363 user_name = CTMODEL_NAME;
364 }
365
366 // Log filename
367 log_value(NORMAL, "Model cube name", user_name);
368
369 // Publish model cube
370 m_cube.counts().publish(user_name);
371
372 // Return
373 return;
374}
375
376
377/***********************************************************************//**
378 * @brief Set model cube
379 *
380 * @param[in] cube Model cube.
381 *
382 * Set model cube and set all cube bins to zero.
383 ***************************************************************************/
384void ctmodel::cube(const GCTAEventCube& cube)
385{
386 // Set cube
387 m_cube = cube;
388
389 // Set all cube bins to zero
390 for (int i = 0; i < m_cube.size(); ++i) {
391 m_cube[i]->counts(0.0);
392 }
393
394 // Signal that cube has been set
395 m_has_cube = true;
396
397 // Return
398 return;
399}
400
401
402/*==========================================================================
403 = =
404 = Private methods =
405 = =
406 ==========================================================================*/
407
408/***********************************************************************//**
409 * @brief Initialise class members
410 ***************************************************************************/
412{
413 // Initialise members
414 m_outcube.clear();
415 m_apply_edisp = false;
416 m_publish = false;
417 m_chatter = static_cast<GChatter>(2);
418
419 // Initialise protected members
420 m_cube.clear();
421 m_gti.clear();
422 m_has_cube = false;
423 m_append_cube = false;
424 m_binned = false;
425 m_dir.clear();
426 m_solidangle.clear();
427 m_energy.clear();
428 m_ewidth.clear();
429 m_time.clear();
430 m_ra_pnt = 0.0;
431 m_dec_pnt = 0.0;
432
433 // Return
434 return;
435}
436
437
438/***********************************************************************//**
439 * @brief Copy class members
440 *
441 * @param[in] app Application.
442 ***************************************************************************/
444{
445 // Copy attributes
446 m_outcube = app.m_outcube;
447 m_apply_edisp = app.m_apply_edisp;
448 m_publish = app.m_publish;
449 m_chatter = app.m_chatter;
450
451 // Copy protected members
452 m_cube = app.m_cube;
453 m_gti = app.m_gti;
454 m_has_cube = app.m_has_cube;
455 m_append_cube = app.m_append_cube;
456 m_binned = app.m_binned;
457 m_dir = app.m_dir;
458 m_solidangle = app.m_solidangle;
459 m_energy = app.m_energy;
460 m_ewidth = app.m_ewidth;
461 m_time = app.m_time;
462 m_ra_pnt = app.m_ra_pnt;
463 m_dec_pnt = app.m_dec_pnt;
464
465 // Return
466 return;
467}
468
469
470/***********************************************************************//**
471 * @brief Delete class members
472 ***************************************************************************/
474{
475 // Return
476 return;
477}
478
479
480/***********************************************************************//**
481 * @brief Get application parameters
482 *
483 * Get all task parameters from parameter file or (if required) by querying
484 * the user. The parameters are read in the correct order.
485 ***************************************************************************/
487{
488 // Reset cube append flag
489 m_append_cube = false;
490
491 // If there are no observations in container then load them via user
492 // parameters.
493 if (m_obs.size() == 0) {
494 get_obs();
495 }
496
497 // ... otherwise add response information and energy boundaries in case
498 // that they are missing
499 else {
501 }
502
503 // If we have now excactly one CTA observation (but no cube has yet been
504 // appended to the observation) then check whether this observation
505 // is a binned observation, and if yes, extract the counts cube for
506 // model generation
507 if ((m_obs.size() == 1) && (m_append_cube == false)) {
508
509 // Get CTA observation
510 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
511
512 // Continue only if observation is a CTA observation
513 if (obs != NULL) {
514
515 // Check for binned observation
516 if (obs->eventtype() == "CountsCube") {
517
518 // Set cube from binned observation
519 GCTAEventCube* evtcube = dynamic_cast<GCTAEventCube*>
520 (const_cast<GEvents*>(obs->events()));
521 cube(*evtcube);
522
523 // Signal that cube has been set
524 m_has_cube = true;
525
526 // Signal that we are in binned mode
527 m_binned = true;
528
529 } // endif: observation was binned
530
531 } // endif: observation was CTA
532
533 } // endif: had exactly one observation
534
535 // Read model definition file if required
536 if (m_obs.models().size() == 0) {
537
538 // Get model filename
539 std::string inmodel = (*this)["inmodel"].filename();
540
541 // Load models from file
542 m_obs.models(inmodel);
543
544 } // endif: there were no models
545
546 // Get energy dispersion flag parameters
547 m_apply_edisp = (*this)["edisp"].boolean();
548
549 // If we do not have yet a counts cube for model computation then check
550 // whether we should read it from the "incube" parameter or whether we
551 // should create it from scratch using the task parameters
552 if (!m_has_cube) {
553
554 // If the cube filename is valid the load the cube and set all cube
555 // bins to zero
556 if ((*this)["incube"].is_valid()) {
557
558 // Get cube definition filename
559 std::string incube = (*this)["incube"].filename();
560
561 // Load cube from given file
562 m_cube.load(incube);
563
564 // Set all cube bins to zero
565 for (int i = 0; i < m_cube.size(); ++i) {
566 m_cube[i]->counts(0.0);
567 }
568
569 } // endif: cube filename was valid
570
571 // ... otherwise create a cube from the user parameters
572 else {
574 }
575
576 // Signal that cube has been set
577 m_has_cube = true;
578
579 } // endif: we had no cube yet
580
581 // Get remaining parameters
582 m_publish = (*this)["publish"].boolean();
583 m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
584
585 // If needed later, query output filename now
586 if (read_ahead()) {
587 (*this)["outcube"].query();
588 }
589
590 // If cube should be appended to first observation then do that now.
591 // This is a kluge that makes sure that the cube is passed as part
592 // of the observation in case that a cube response is used. The kluge
593 // is needed because the GCTACubeSourceDiffuse::set method needs to
594 // get the full event cube from the observation. It is also at this
595 // step that the GTI, which may just be a dummy GTI when create_cube()
596 // has been used, will be set.
597 if (m_append_cube) {
598
599 //TODO: Check that energy boundaries are compatible
600
601 // Attach GTI of observations to model cube
602 m_cube.gti(m_obs[0]->events()->gti());
603
604 // Attach model cube to observations
605 m_obs[0]->events(m_cube);
606
607 } // endif: cube was scheduled for appending
608
609 // Set number of OpenMP threads
610 #ifdef _OPENMP
611 int nthreads = (*this)["nthreads"].integer();
612 if (nthreads > 0) {
613 omp_set_num_threads(nthreads);
614 }
615 #endif
616
617 // Write parameters into logger
618 log_parameters(TERSE);
619
620 // Return
621 return;
622}
623
624
625/***********************************************************************//**
626 * @brief Get observation container
627 *
628 * Get an observation container according to the user parameters. The method
629 * supports loading of a individual FITS file or an observation definition
630 * file in XML format.
631 *
632 * If the input filename is empty, the method checks for the existence of the
633 * "expcube", "psfcube" and "bkgcube" parameters. If file names have been
634 * specified, the method loads the files and creates a dummy events cube that
635 * is appended to the observation container.
636 *
637 * If no file names are specified for the "expcube", "psfcube" or "bkgcube"
638 * parameters, the method reads the necessary parameters to build a CTA
639 * observation from scratch.
640 *
641 * The method sets m_append_cube = true and m_binned = true in case that
642 * a stacked observation is requested (as detected by the presence of the
643 * "expcube", "psfcube", and "bkgcube" parameters). In that case, it appended
644 * a dummy event cube to the observation.
645 *
646 * @todo Support stacked energy dispersion
647 ***************************************************************************/
649{
650 // If no observation definition file has been specified then read all
651 // parameters that are necessary to create an observation from scratch
652 if ((*this)["inobs"].is_undefined()) {
653
654 // If the filenames are valid then build an observation from cube
655 // response information
656 if ((*this)["expcube"].is_valid() &&
657 (*this)["psfcube"].is_valid() &&
658 (*this)["bkgcube"].is_valid()) {
659
660 // Get response cube filenames
661 GFilename edispcube;
662 bool query_edisp = (*this)["edisp"].boolean();
663 GFilename expcube = (*this)["expcube"].filename();
664 GFilename psfcube = (*this)["psfcube"].filename();
665 if (query_edisp && (*this)["edispcube"].is_valid()) {
666 edispcube = (*this)["edispcube"].filename();
667 }
668 GFilename bkgcube = (*this)["bkgcube"].filename();
669
670 // Get exposure, PSF and background cubes
671 GCTACubeExposure exposure(expcube);
672 GCTACubePsf psf(psfcube);
673 GCTACubeBackground background(bkgcube);
674
675 // Create energy boundaries
676 GEbounds ebounds = create_ebounds();
677
678 // Create dummy sky map cube
679 GSkyMap map("CAR","GAL",0.0,0.0,1.0,1.0,1,1,ebounds.size());
680
681 // Create event cube
682 GCTAEventCube cube(map, ebounds, exposure.gti());
683
684 // Create CTA observation
685 GCTAObservation cta;
686 cta.events(cube);
687
688 // Set name of Cherenkov telescope
689 cta.instrument((*this)["instrument"].string());
690
691 // If querying of energy dispersion cube is requested then
692 // query it now
693 if (query_edisp) {
694
695 // If filename is valid then use energy dispersion ...
696 if ((*this)["edispcube"].is_valid()) {
697
698 // Load energy dispersion cube
699 GCTACubeEdisp edisp(edispcube);
700
701 // Set response with all four cubes
702 cta.response(exposure, psf, edisp, background);
703
704 } // endif: energy dispersion cube was provided
705
706 // ... otherwise throw an exception
707 else {
708 std::string msg = "Energy dispersion requested but no "
709 "energy dispersion cube was specified.";
710 throw GException::invalid_value(G_GET_OBS, msg);
711 }
712
713 } // endif: energy dispersion needed
714
715 // ... otherwise work without energy dispersion
716 else {
717 cta.response(exposure, psf, background);
718 }
719
720 // Append observation to container
721 m_obs.append(cta);
722
723 // Signal that we are in binned mode
724 m_binned = true;
725
726 // Signal that we appended a cube
727 m_append_cube = true;
728
729 } // endif: cube response information was available
730
731 // ... otherwise build an observation from IRF response information
732 else {
733
734 // Create CTA observation
735 GCTAObservation cta = create_cta_obs();
736
737 // Set response
738 set_obs_response(&cta);
739
740 // Append observation to container
741 m_obs.append(cta);
742
743 }
744
745 } // endif: filename was not valid
746
747 // ... otherwise we have a file name
748 else {
749
750 // Get the filename from the input parameters
751 std::string filename = (*this)["inobs"].filename();
752
753 // If file is a FITS file then create an empty CTA observation
754 // and load file into observation
755 if (GFilename(filename).is_fits()) {
756
757 // Allocate empty CTA observation
758 GCTAObservation cta;
759
760 // Load data
761 cta.load(filename);
762
763 // Set response
764 set_obs_response(&cta);
765
766 // Append observation to container
767 m_obs.append(cta);
768
769 // Signal that no XML file should be used for storage
770 m_use_xml = false;
771
772 }
773
774 // ... otherwise load file into observation container
775 else {
776
777 // Load observations from XML file
778 m_obs.load(filename);
779
780 // For all observations that have no response, set the response
781 // from the task parameters
783
784 // Set observation boundary parameters (emin, emax, rad)
786
787 // Signal that XML file should be used for storage
788 m_use_xml = true;
789
790 } // endelse: file was an XML file
791
792 }
793
794 // Return
795 return;
796
797}
798
799
800/***********************************************************************//**
801 * @brief Extract cube properties in data members
802 ***************************************************************************/
804{
805 // Clear cube properties
806 m_dir.clear();
807 m_solidangle.clear();
808 m_energy.clear();
809 m_ewidth.clear();
810
811 // Get number of spatial pixels and energy layers in counts cube
812 int npix = m_cube.npix();
813 int nebins = m_cube.ebins();
814
815 // Reserve capacity in vectors
816 m_dir.reserve(npix);
817 m_solidangle.reserve(npix);
818 m_energy.reserve(nebins);
819 m_ewidth.reserve(nebins);
820
821 // Loop over all the spatial bins and extract spatial cube properties
822 for (int i = 0; i < npix; ++i) {
823
824 // Get cube bin
825 GCTAEventBin* bin = m_cube[i];
826
827 // Extract sky direction and solid angle
828 m_dir.push_back(bin->dir());
829 m_solidangle.push_back(bin->solidangle());
830
831 } // endfor: looped over spatial bins
832
833 // Loop over all the spectral bins and extract spectral cube properties
834 for (int iebin = 0, ibin = 0; iebin < nebins; ++iebin, ibin += npix) {
835
836 // Get cube bin
837 GCTAEventBin* bin = m_cube[ibin];
838
839 // Extract energy and energy width
840 m_energy.push_back(bin->energy());
841 m_ewidth.push_back(bin->ewidth());
842
843 } // endfor: looped over spectral bins
844
845 // Set time
846 m_time = m_cube[0]->time();
847
848 // Return
849 return;
850}
851
852
853/***********************************************************************//**
854 * @brief Fill models into model cube
855 *
856 * @param[in] obs CTA observation.
857 * @param[in] models Model container.
858 *
859 * Adds the expected number of events for a given observation to the events
860 * that are already found in the model cube. The method also updates the
861 * GTI of the model cube so that cube GTI is a list of the GTIs of all
862 * observations that were used to generate the model cube.
863 ***************************************************************************/
864void ctmodel::fill_cube(const GCTAObservation* obs, GModels& models)
865{
866 // Get number of spatial and spectral bins in counts cube
867 int npix = m_cube.npix();
868 int nebins = m_cube.ebins();
869
870 // Get reference to the pointing for DETX and DETY computation, if
871 // required
872 const GCTAPointing& pnt = obs->pointing();
873
874 // Store Right Ascension and Declination for output model
875 m_ra_pnt = pnt.dir().ra_deg();
876 m_dec_pnt = pnt.dir().dec_deg();
877
878 // Get references to GTI and energy boundaries for the event list
879 const GGti& gti = obs->events()->gti();
880 const GEbounds& obs_ebounds = obs->ebounds();
881
882 // Get cube energy boundaries
883 const GEbounds& cube_ebounds = m_cube.ebounds();
884
885 // Get counts cube usage flags
886 std::vector<bool> usage = cube_layer_usage(cube_ebounds, obs_ebounds);
887
888 // Initialise empty, invalid RoI
889 GCTARoi roi;
890
891 // Retrieve RoI in case we have an unbinned observation
892 if (obs->eventtype() == "EventList") {
893 roi = obs->roi();
894 }
895
896 // Initialise statistics
897 double sum = 0.0;
898 int num_outside_ebds = 0;
899 int num_outside_roi = 0;
900
901 // Extract copy of models from observation container
902 GModels ex_models = trim_models(models, roi);
903
904 // Get pointer to event cube pixels
905 double* pixels = const_cast<double*>(m_cube.counts().pixels());
906
907 // Initialise event bin
908 GCTAEventBin bin;
909
910 // Set event bin attributes that are constant for a counts cube
911 bin.time(m_time);
912 bin.ontime(obs->ontime());
913 bin.weight(1.0);
914
915 // Flag if the CTA observation is a counts cube
916 bool obs_is_cube = (obs->eventtype() == "CountsCube");
917
918 // Loop over all the spatial bins
919 for (int i = 0; i < npix; ++i) {
920
921 // If RoI is valid then skip bin if it is outside the RoI of the
922 // observation
923 if (roi.is_valid() && !roi.contains(m_dir[i])) {
924 num_outside_roi += nebins;
925 continue;
926 }
927
928 // Get instrument direction for spatial pixel and recompute DETX and
929 // DETY based on the pointing of the observation.
930 GCTAInstDir dir = pnt.instdir(m_dir[i].dir());
931
932 // Set instrument direction, solid angle and pixel index of bin
933 bin.dir(dir);
934 bin.solidangle(m_solidangle[i]);
935 bin.ipix(i);
936
937 // Loop over all of the energy bins of the cube
938 for (int iebin = 0, ibin = i; iebin < nebins; ++iebin, ibin += npix) {
939
940 // Skip bin if the corresponding counts cube energy bin is not fully
941 // contained in the event list energy range. This avoids having
942 // partially filled bins.
943 if (!usage[iebin]) {
944 num_outside_ebds++;
945 continue;
946 }
947
948 // Set energy, energy width and energy index
949 bin.energy(m_energy[iebin]);
950 bin.ewidth(m_ewidth[iebin]);
951 bin.ieng(iebin);
952
953 // If observation is a counts cube then set the proper weight for
954 // this bin
955 if (obs_is_cube) {
956 bin.weight(m_cube[ibin]->weight());
957 }
958
959 // Compute model value for cube bin
960 double model = models.eval(bin, *obs) * bin.size();
961
962 // Sum model
963 sum += model;
964
965 // Store value
966 #pragma omp critical(ctmodel_fill_cube)
967 pixels[ibin] += model;
968
969 } // endfor: looped over all spatial bins
970
971 } // endfor: looped over all cube bins
972
973 // Re-append the models to the main model
974 models.extend(ex_models);
975
976 // Update GTIs
977 #pragma omp critical(ctmodel_fill_cube)
978 {
979 // Append GTIs of observation to list of GTIs
980 m_gti.extend(gti);
981
982 // Update GTIs
983 m_cube.gti(m_gti);
984 }
985
986 // Log filling results
987 #pragma omp critical(ctmodel_fill_cube)
988 {
989 log_header3(TERSE, get_obs_header(obs));
990 log_value(NORMAL, "Model events in cube", sum);
991 log_value(NORMAL, "Bins outside energy range", num_outside_ebds);
992 log_value(NORMAL, "Bins outside RoI", num_outside_roi);
993 }
994
995 // Write model cube into header
996 if (m_chatter >= EXPLICIT) {
997 log_header2(EXPLICIT, "Model cube");
998 log_string(EXPLICIT, m_cube.print(m_chatter));
999 }
1000
1001 // Return
1002 return;
1003}
1004
1005
1006/***********************************************************************//**
1007 * @brief Find the models falling inside a defined region of interest.
1008 *
1009 * @param[in,out] all_models Model container to be trimmed.
1010 * @param[in] roi Observation region of interest.
1011 * @return New model container containing only models inside RoI.
1012 *
1013 * Note that a buffer is added to the observation region to ensure that
1014 * point sources (which have radius=0) are appropriately included if they
1015 * fall near the edge of the observation.
1016 ***************************************************************************/
1017GModels ctmodel::trim_models(GModels& all_models, const GCTARoi& roi)
1018{
1019 // Create the model container containing only models in RoI
1020 GModels excluded_models;
1021
1022 // Do nothing if roi is not valid. This will be the case for binned data
1023 // sets as they have the default roi radius of 0. In this case we would
1024 // remove ALL models, which is obviously incorrect. So we keep all models
1025 // when filling based on a counts cube.
1026 if (!roi.is_valid()) {
1027 return excluded_models;
1028 }
1029
1030 // Remove all models that dont overlap with the region of interest. Note
1031 // that an extra factor is used since point sources have regions of
1032 // radius 0, which is a problem when they fall just barely outside the
1033 // RoI. This ensures point sources are appropriately included.
1034 GSkyRegionCircle obsreg(roi.centre().dir(), roi.radius()+0.5);
1035
1036 // Loop over the models in the passed container
1037 for (int i = 0; i < all_models.size(); ++i) {
1038
1039 // Cast the model to a GModelSky object
1040 GModelSky* model = dynamic_cast<GModelSky*>(all_models[i]);
1041
1042 // If model is not a GModelSky model than skip
1043 if (model == NULL) {
1044 continue;
1045 }
1046
1047 // Otherwise, exclude the model if it isn't a diffuse cube and it
1048 // doesn't overlap with the observation.
1049 if (!(model->spatial()->code() == GMODEL_SPATIAL_DIFFUSE) &&
1050 (!model->spatial()->region()->overlaps(obsreg))) {
1051
1052 // Append model to the excluded models
1053 excluded_models.append(*model);
1054
1055 // Remove model
1056 all_models.remove(i);
1057
1058 // Decrement to prevent skipping models
1059 i--;
1060 }
1061
1062 } // endfor: looped over all models
1063
1064 // Return excluded models
1065 return excluded_models;
1066}
Model cube generation tool.
Definition ctmodel.hpp:53
double m_dec_pnt
Declination Ascension of pointing.
Definition ctmodel.hpp:105
GFilename m_outcube
Output model cube.
Definition ctmodel.hpp:88
const GCTAEventCube & cube(void) const
Return model cube.
Definition ctmodel.hpp:115
void get_parameters(void)
Get application parameters.
Definition ctmodel.cpp:486
void process(void)
Generate the model map(s)
Definition ctmodel.cpp:217
void save(void)
Save model cube.
Definition ctmodel.cpp:311
GModels trim_models(GModels &all_models, const GCTARoi &roi)
Find the models falling inside a defined region of interest.
Definition ctmodel.cpp:1017
std::vector< GEnergy > m_energy
Cube energies.
Definition ctmodel.hpp:101
GTime m_time
Cube time.
Definition ctmodel.hpp:103
std::vector< GEnergy > m_ewidth
Cube energy widths.
Definition ctmodel.hpp:102
void init_members(void)
Initialise class members.
Definition ctmodel.cpp:411
void copy_members(const ctmodel &app)
Copy class members.
Definition ctmodel.cpp:443
ctmodel & operator=(const ctmodel &app)
Assignment operator.
Definition ctmodel.cpp:152
void extract_cube_properties(void)
Extract cube properties in data members.
Definition ctmodel.cpp:803
void publish(const std::string &name="")
Publish model cube.
Definition ctmodel.cpp:355
GChatter m_chatter
Chattiness.
Definition ctmodel.hpp:91
bool m_has_cube
Signal if cube exists.
Definition ctmodel.hpp:96
void free_members(void)
Delete class members.
Definition ctmodel.cpp:473
void clear(void)
Clear ctmodel tool.
Definition ctmodel.cpp:187
ctmodel(void)
Void constructor.
Definition ctmodel.cpp:62
bool m_append_cube
Signal to append cube.
Definition ctmodel.hpp:97
GCTAEventCube m_cube
Model cube.
Definition ctmodel.hpp:94
void get_obs(void)
Get observation container.
Definition ctmodel.cpp:648
std::vector< GCTAInstDir > m_dir
Cube directions.
Definition ctmodel.hpp:99
void fill_cube(const GCTAObservation *obs, GModels &models)
Fill models into model cube.
Definition ctmodel.cpp:864
virtual ~ctmodel(void)
Destructor.
Definition ctmodel.cpp:130
std::vector< double > m_solidangle
Cube solid angles.
Definition ctmodel.hpp:100
bool m_apply_edisp
Apply energy dispersion?
Definition ctmodel.hpp:89
double m_ra_pnt
Right Ascension of pointing.
Definition ctmodel.hpp:104
GGti m_gti
Model cube GTIs.
Definition ctmodel.hpp:95
bool m_publish
Publish model cube?
Definition ctmodel.hpp:90
void models(const GModels &models)
Set models.
Definition ctmodel.hpp:141
bool m_binned
Signal binned mode.
Definition ctmodel.hpp:98
Base class for observation tools.
void free_members(void)
Delete class members.
ctobservation & operator=(const ctobservation &app)
Assignment operator.
GObservations m_obs
Observation container.
const GObservations & obs(void) const
Return observation container.
void set_obs_bounds()
Set observation boundaries for CTA observations.
void init_members(void)
Initialise class members.
void set_obs_response(GCTAObservation *obs)
Set response for CTA observation.
Definition ctool.cpp:1752
void set_response(GObservations &obs)
Set response for all CTA observations in container.
Definition ctool.cpp:1614
bool m_use_xml
Use XML file instead of FITS file for observations.
Definition ctool.hpp:162
void restore_edisp(GObservations &obs, const std::vector< bool > &edisp) const
Restore energy dispersion flags of CTA observations.
Definition ctool.cpp:1689
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
void log_observations(const GChatter &chatter, const GObservations &obs, const std::string &what="Observation")
Log observation container.
Definition ctool.cpp:1251
std::vector< bool > set_edisp(GObservations &obs, const bool &edisp) const
Set energy dispersion to CTA observations.
Definition ctool.cpp:1649
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
GCTAObservation create_cta_obs(void)
Create a CTA observation from User parameters.
Definition ctool.cpp:1056
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 get_obs_header(const GObservation *obs) const
Return observation header string.
Definition ctool.cpp:2090
void log_models(const GChatter &chatter, const GModels &models, const std::string &what="Model")
Log model container.
Definition ctool.cpp:1285
const GEnergy g_energy_margin(1.0e-12, "TeV")
#define G_GET_OBS
Definition ctmodel.cpp:42
Model cube generation tool definition.
#define CTMODEL_NAME
Definition ctmodel.hpp:36