Commit 7acb69d5790f3b66cfb652b29d2bf8dbeddfa328

Authored by Jürgen Knödlseder
1 parent ff4fc707

Derive csspec script from cslikelihood base class and add "statistic" parameter

ChangeLog
... ... @@ -3,6 +3,7 @@
3 3 * Version 1.5.0 released
4 4 ========================
5 5  
  6 + Add "statistic" parameter to all likelihood tools (#2237)
6 7 Rename ctlike "stat" parameter to "statistic" (#2237)
7 8 Enable handling of On/Off observations in csspec (#2231)
8 9 Add csphagen script (#2208)
... ...
... ... @@ -127,6 +127,11 @@ ctulimit - Compute upper limit
127 127 Add "statistic" parameter to allow setting of optimizer statistic (#2237).
128 128  
129 129  
  130 +support - Support classes
  131 +-------------------------
  132 +Add ctobservation.obs() method to set an observation container.
  133 +
  134 +
130 135 cscaldb - Inspect calibration database
131 136 --------------------------------------
132 137 None
... ... @@ -238,6 +243,10 @@ None
238 243  
239 244 csspec - Generates Spectral Energy Distribution
240 245 -----------------------------------------------
  246 +The csspec script now derived from the cslikelihood base class. A "statistic"
  247 +parameter was added to allow setting the optimizer statistic for the spectrum
  248 +generation (#2237).
  249 +
241 250 The csspec script now can also handle On/Off spectra for SED generation (#2231).
242 251  
243 252 The csspec script now can also handle non-CTA data. For that purpose a "method"
... ...
cscripts/csspec.par
... ... @@ -37,6 +37,7 @@ emax, r, a, 100.0,,, "Upper energy limit (TeV)"
37 37 enumbins, i, a, 20,,, "Number of energy bins"
38 38 ebinalg, s, a, LOG,LIN|LOG|FILE,, "Binning algorithm"
39 39 ebinfile, f, a, NONE,,, "Name of the file containing the energy bin definition"
  40 +statistic, s, h, DEFAULT,DEFAULT|CSTAT|WSTAT|CHI2,, "Optimization statistic"
40 41 calc_ts, b, h, yes,,, "Compute TS value in each bin"
41 42 calc_ulim, b, h, yes,,, "Compute upper limit in each bin"
42 43 fix_srcs, b, h, yes,,, "Fix other skymodel parameters"
... ...
cscripts/csspec.py
... ... @@ -26,12 +26,12 @@ import ctools
26 26 # ============ #
27 27 # csspec class #
28 28 # ============ #
29   -class csspec(ctools.cscript):
  29 +class csspec(ctools.cslikelihood):
30 30 """
31 31 Generates a spectrum
32 32  
33   - This class implements the generation of a spectral energy distribution
34   - from data.
  33 + This class implements the generation of a Spectral Energy Distribution
  34 + (SED) from gamma-ray observations.
35 35 """
36 36  
37 37 # Constructors and destructors
... ... @@ -39,9 +39,8 @@ class csspec(ctools.cscript):
39 39 """
40 40 Constructor
41 41 """
42   - # Set name
43   - self._name = 'csspec'
44   - self._version = ctools.__version__
  42 + # Initialise application by calling the appropriate class constructor
  43 + self._init_cslikelihood('csspec', ctools.__version__, argv)
45 44  
46 45 # Initialise data members
47 46 self._ebounds = gammalib.GEbounds()
... ... @@ -50,12 +49,6 @@ class csspec(ctools.cscript):
50 49 self._onoff_mode = False
51 50 self._method = 'AUTO'
52 51  
53   - # Initialise observation container from constructor arguments
54   - self._obs, argv = self._set_input_obs(argv)
55   -
56   - # Initialise application by calling the appropriate class constructor
57   - self._init_cscript(argv)
58   -
59 52 # Return
60 53 return
61 54  
... ... @@ -73,13 +66,16 @@ class csspec(ctools.cscript):
73 66 Get parameters from parfile and setup the observation
74 67 """
75 68 # Set observation if not done before
76   - if self._obs == None or self._obs.size() == 0:
  69 + if self.obs().size() == 0:
77 70 self._require_inobs('csspec::get_parameters()')
78   - self._obs = self._get_observations()
  71 + self.obs(self._get_observations())
  72 +
  73 + # Set observation statistic
  74 + self._set_obs_statistic(gammalib.toupper(self['statistic'].string()))
79 75  
80 76 # Set models if we have none
81   - if self._obs.models().size() == 0:
82   - self._obs.models(self['inmodel'].filename())
  77 + if self.obs().models().size() == 0:
  78 + self.obs().models(self['inmodel'].filename())
83 79  
84 80 # Query source name
85 81 self['srcname'].string()
... ... @@ -92,7 +88,7 @@ class csspec(ctools.cscript):
92 88 n_unbinned = 0
93 89 n_binned = 0
94 90 n_onoff = 0
95   - for obs in self._obs:
  91 + for obs in self.obs():
96 92 if obs.classname() == 'GCTAObservation':
97 93 if obs.eventtype() == 'CountsCube':
98 94 n_binned += 1
... ... @@ -101,7 +97,7 @@ class csspec(ctools.cscript):
101 97 elif obs.classname() == 'GCTAOnOffObservation':
102 98 n_onoff += 1
103 99 n_cta = n_unbinned + n_binned + n_onoff
104   - n_other = self._obs.size() - n_cta
  100 + n_other = self.obs().size() - n_cta
105 101  
106 102 # If spectrum method is not "NODES" then set spectrum method and
107 103 # script mode according to type of CTA observations
... ... @@ -194,9 +190,9 @@ class csspec(ctools.cscript):
194 190 # should work reasonably well since for each observation the
195 191 # relevant energy bins will be selected.
196 192 if self._binned_mode:
197   - cube_ebounds = self._obs[0].events().ebounds()
  193 + cube_ebounds = self.obs()[0].events().ebounds()
198 194 else:
199   - cube_ebounds = self._obs[0].rmf().etrue()
  195 + cube_ebounds = self.obs()[0].rmf().etrue()
200 196  
201 197 # Loop over user energy boundaries and collect all energy bins
202 198 # that overlap
... ... @@ -244,7 +240,7 @@ class csspec(ctools.cscript):
244 240 ' Specify overlapping energy range.'
245 241 raise RuntimeError(msg)
246 242  
247   - # Unbinned mode
  243 + # Unbinned mode
248 244 else:
249 245 self._ebounds = self._create_ebounds()
250 246  
... ... @@ -260,15 +256,15 @@ class csspec(ctools.cscript):
260 256  
261 257 # Log counts cube energy range for binned mode
262 258 if self._binned_mode:
263   - cube_ebounds = self._obs[0].events().ebounds()
  259 + cube_ebounds = self.obs()[0].events().ebounds()
264 260 value = '%s - %s' % (str(cube_ebounds.emin()),
265 261 str(cube_ebounds.emax()))
266 262 self._log_value(gammalib.TERSE, 'Counts cube energy range', value)
267 263  
268 264 # Log RMF energy range for On/Off mode
269 265 elif self._onoff_mode:
270   - etrue = self._obs[0].rmf().etrue()
271   - ereco = self._obs[0].rmf().emeasured()
  266 + etrue = self.obs()[0].rmf().etrue()
  267 + ereco = self.obs()[0].rmf().emeasured()
272 268 vtrue = '%s - %s' % (str(etrue.emin()), str(etrue.emax()))
273 269 vreco = '%s - %s' % (str(ereco.emin()), str(ereco.emax()))
274 270 self._log_value(gammalib.TERSE, 'RMF true energy range', vtrue)
... ... @@ -291,7 +287,7 @@ class csspec(ctools.cscript):
291 287 self._log_header1(gammalib.TERSE, 'Adjust model parameters')
292 288  
293 289 # Adjust model parameters dependent on input user parameters
294   - for model in self._obs.models():
  290 + for model in self.obs().models():
295 291  
296 292 # Initialise TS flag for all models to false
297 293 model.tscalc(False)
... ... @@ -350,7 +346,7 @@ class csspec(ctools.cscript):
350 346 models = gammalib.GModels()
351 347  
352 348 # Loop over model containers
353   - for model in self._obs.models():
  349 + for model in self.obs().models():
354 350  
355 351 # If we deal with source model then replace the spectral model
356 352 # by a node function
... ... @@ -360,7 +356,7 @@ class csspec(ctools.cscript):
360 356 energies = gammalib.GEnergies()
361 357 for i in range(self._ebounds.size()):
362 358 energies.append(self._ebounds.elogmean(i))
363   -
  359 +
364 360 # Setup spectral node function
365 361 spectrum = gammalib.GModelSpectralNodes(model.spectral(), energies)
366 362 spectrum.autoscale()
... ... @@ -388,7 +384,7 @@ class csspec(ctools.cscript):
388 384 models.append(model)
389 385  
390 386 # Put new model in observation containers
391   - self._obs.models(models)
  387 + self.obs().models(models)
392 388  
393 389 # Return
394 390 return
... ... @@ -463,9 +459,11 @@ class csspec(ctools.cscript):
463 459 rmf[idst_true, idst_reco] = obs.rmf()[isrc_true, isrc_reco]
464 460  
465 461 # Set On/Off observations
466   - id = obs.id()
  462 + id = obs.id()
  463 + statistic = obs.statistic()
467 464 obs = gammalib.GCTAOnOffObservation(pha_on, pha_off, arf, rmf)
468 465 obs.id(id)
  466 + obs.statistic(statistic)
469 467  
470 468 # Return observation
471 469 return obs
... ... @@ -493,7 +491,7 @@ class csspec(ctools.cscript):
493 491 self._log_header3(gammalib.EXPLICIT, 'Filtering cube')
494 492  
495 493 # Select layers
496   - cubemask = ctools.ctcubemask(self._obs)
  494 + cubemask = ctools.ctcubemask(self.obs())
497 495 cubemask['regfile'] = 'NONE'
498 496 cubemask['ra'] = 'UNDEFINED'
499 497 cubemask['dec'] = 'UNDEFINED'
... ... @@ -516,12 +514,12 @@ class csspec(ctools.cscript):
516 514  
517 515 # Loop over all input observations and select energy bins for
518 516 # all On/Off observations
519   - for run in self._obs:
  517 + for run in self.obs():
520 518 if run.classname() == 'GCTAOnOffObservation':
521 519 obs.append(self._select_onoff_obs(run, emin, emax))
522 520  
523 521 # Append models
524   - obs.models(self._obs.models())
  522 + obs.models(self.obs().models())
525 523  
526 524 # Use ctselect for unbinned analysis
527 525 else:
... ... @@ -530,7 +528,7 @@ class csspec(ctools.cscript):
530 528 self._log_header3(gammalib.EXPLICIT, 'Selecting events')
531 529  
532 530 # Select events
533   - select = ctools.ctselect(self._obs)
  531 + select = ctools.ctselect(self.obs())
534 532 select['ra'] = 'UNDEFINED'
535 533 select['dec'] = 'UNDEFINED'
536 534 select['rad'] = 'UNDEFINED'
... ... @@ -707,7 +705,7 @@ class csspec(ctools.cscript):
707 705 self._log_header3(gammalib.EXPLICIT, 'Performing fit')
708 706  
709 707 # Perform maximum likelihood fit
710   - like = ctools.ctlike(self._obs)
  708 + like = ctools.ctlike(self.obs())
711 709 like['edisp'] = self['edisp'].boolean()
712 710 like.run()
713 711  
... ... @@ -856,7 +854,7 @@ class csspec(ctools.cscript):
856 854 self._get_parameters()
857 855  
858 856 # Write input observation container into logger
859   - self._log_observations(gammalib.NORMAL, self._obs, 'Input observation')
  857 + self._log_observations(gammalib.NORMAL, self.obs(), 'Input observation')
860 858  
861 859 # Write spectral binning into logger
862 860 self._log_spectral_binning()
... ... @@ -931,7 +929,7 @@ class csspec(ctools.cscript):
931 929  
932 930 # Set default name is user name is empty
933 931 if not name:
934   - user_name = self._name
  932 + user_name = self._name()
935 933 else:
936 934 user_name = name
937 935  
... ... @@ -959,7 +957,7 @@ class csspec(ctools.cscript):
959 957 Set model
960 958 """
961 959 # Copy models
962   - self._obs.models(models.clone())
  960 + self.obs().models(models.clone())
963 961  
964 962 # Return
965 963 return
... ...
doc/source/users/reference_manual/csspec.rst
... ... @@ -68,7 +68,7 @@ General parameters
68 68  
69 69 ``caldb [string]``
70 70 Calibration database.
71   -
  71 +
72 72 ``irf [string]``
73 73 Instrumental response function.
74 74  
... ... @@ -89,19 +89,27 @@ General parameters
89 89  
90 90 ``emin [real]``
91 91 Lower energy limit of events (in TeV).
92   -
  92 +
93 93 ``emax [real]``
94 94 Upper energy limit of events (in TeV).
95   -
  95 +
96 96 ``enumbins [integer]``
97 97 Number of energy bins.
98   -
  98 +
99 99 ``ebinalg <FILE|LIN|LOG> [string]``
100 100 Algorithm for defining energy bins.
101   -
  101 +
102 102 ``ebinfile [file]``
103 103 Name of the file containing the energy bin definition.
104 104  
  105 +``(statistic = DEFAULT) <DEFAULT|CSTAT|WSTAT|CHI2> [string]``
  106 + Optimization statistic. ``DEFAULT`` uses the default statistic for all
  107 + observations, which is ``CSTAT`` or the statistic specified in the
  108 + observation definition XML file. ``CSTAT`` uses the C statistic for
  109 + all observations, ``WSTAT`` uses the W statistic for all On/Off
  110 + observations, and ``CHI2`` uses the Chi squared statistic for all
  111 + binned or stacked observations.
  112 +
105 113 ``(calc_ts = yes) [boolean]``
106 114 Compute TS for each spectral point?
107 115  
... ... @@ -124,21 +132,23 @@ Standard parameters
124 132 ``(chatter = 2) [integer]``
125 133 Verbosity of the executable:
126 134 ``chatter = 0``: no information will be logged
127   -
  135 +
128 136 ``chatter = 1``: only errors will be logged
129   -
  137 +
130 138 ``chatter = 2``: errors and actions will be logged
131   -
  139 +
132 140 ``chatter = 3``: report about the task execution
133   -
  141 +
134 142 ``chatter = 4``: detailed report about the task execution
135   -
  143 +
136 144 ``(clobber = yes) [boolean]``
137   - Specifies whether an existing source spectrum output file should be overwritten.
138   -
  145 + Specifies whether an existing source spectrum output file should be
  146 + overwritten.
  147 +
139 148 ``(debug = no) [boolean]``
140   - Enables debug mode. In debug mode the executable will dump any log file output to the console.
141   -
  149 + Enables debug mode. In debug mode the executable will dump any log file
  150 + output to the console.
  151 +
142 152 ``(mode = ql) [string]``
143 153 Mode of automatic parameters (default is "ql", i.e. "query and learn").
144 154  
... ...
pyext/ctlikelihood.i
... ... @@ -141,15 +141,23 @@ public:
141 141 # The function supports either an observation container, or an argument
142 142 # list or no argument as "argv" parameter. The function also writes the
143 143 # header in the log file and switches the date on for logging.
144   -def _init_cslikelihood(self, argv):
  144 +def _init_cslikelihood(self, name, version, argv):
145 145 if len(argv) > 0 and isinstance(argv[0],gammalib.GObservations):
146   - cslikelihood.__init__(self, self._name, self._version, argv[0])
  146 + cslikelihood.__init__(self, name, version, argv[0])
147 147 elif len(argv) > 0:
148   - cslikelihood.__init__(self, self._name, self._version, *argv)
  148 + cslikelihood.__init__(self, name, version, *argv)
149 149 else:
150   - cslikelihood.__init__(self, self._name, self._version)
  150 + cslikelihood.__init__(self, name, version)
151 151 # Set logger properties
152 152 self._log_header()
153 153 self._log.date(True)
154 154 cslikelihood._init_cslikelihood = _init_cslikelihood
  155 +
  156 +# Execute the script
  157 +def _execute(self):
  158 + self.logFileOpen()
  159 + self._read_ahead(True)
  160 + self.run()
  161 + self.save()
  162 +cslikelihood.execute = _execute
155 163 %}
... ...
pyext/ctobservation.i
... ... @@ -88,6 +88,7 @@ public:
88 88 virtual void save(void) = 0;
89 89  
90 90 // Public methods
  91 + void obs(const GObservations& obs);
91 92 const GObservations& obs(void) const;
92 93 void write_ogip_keywords(GFitsHDU* hdu) const;
93 94  
... ... @@ -142,19 +143,19 @@ public:
142 143 # The function supports either an observation container, or an argument
143 144 # list or no argument as "argv" parameter. The function also writes the
144 145 # header in the log file and switches the date on for logging.
145   -def _init_csobservation(self, argv):
  146 +def _init_csobservation(self, name, version, argv):
146 147 if len(argv) > 0 and isinstance(argv[0],gammalib.GObservations):
147   - csobservation.__init__(self, self._name, self._version, argv[0])
  148 + csobservation.__init__(self, name, version, argv[0])
148 149 elif len(argv) > 0:
149   - csobservation.__init__(self, self._name, self._version, *argv)
  150 + csobservation.__init__(self, name, version, *argv)
150 151 else:
151   - csobservation.__init__(self, self._name, self._version)
  152 + csobservation.__init__(self, name, version)
152 153 # Set logger properties
153 154 self._log_header()
154 155 self._log.date(True)
155 156 csobservation._init_csobservation = _init_csobservation
156 157  
157   -# Define an iterator over all observations
  158 +# Define an iterator over all unbinned observations
158 159 def _unbinned_observations(self):
159 160 obs = self._first_unbinned_observation()
160 161 while obs != None:
... ... @@ -162,4 +163,12 @@ def _unbinned_observations(self):
162 163 obs = self._next_unbinned_observation()
163 164 ctool._unbinned_observations = _unbinned_observations
164 165 cscript._unbinned_observations = _unbinned_observations
  166 +
  167 +# Execute the script
  168 +def _execute(self):
  169 + self.logFileOpen()
  170 + self._read_ahead(True)
  171 + self.run()
  172 + self.save()
  173 +csobservation.execute = _execute
165 174 %}
... ...
src/support/ctlikelihood.cpp
... ... @@ -305,6 +305,9 @@ double ctlikelihood::evaluate(GModelPar&amp; par, const double&amp; value)
305 305 * For On/Off CTA observations of type GCTAOnOffObservation the fit statistic
306 306 * is always set. Possible values are @c POISSON, @c CSTAT or @c WSTAT (case
307 307 * insensitive).
  308 + *
  309 + * If @p statistic is any other string the fit statistic of the observations
  310 + * will not be modified.
308 311 ***************************************************************************/
309 312 void ctlikelihood::set_obs_statistic(const std::string& statistic)
310 313 {
... ...