Commit 7acb69d5790f3b66cfb652b29d2bf8dbeddfa328
1 parent
ff4fc707
Derive csspec script from cslikelihood base class and add "statistic" parameter
Showing
8 changed files
with
99 additions
and
60 deletions
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) | ... | ... |
NEWS
| ... | ... | @@ -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& par, const double& 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 | { | ... | ... |