Commit fe07c6cf980df683b23fdc051114e523f45588f5
1 parent
be2350c0
Add show_lightcurve example script
Showing
10 changed files
with
262 additions
and
36 deletions
NEWS
| 1 | 1 | New Features and Important Changes in ctools 1.2.0 |
| 2 | 2 | |
| 3 | -1 December 2016 | |
| 3 | +26 January 2017 | |
| 4 | 4 | |
| 5 | 5 | |
| 6 | 6 | Introduction |
| ... | ... | @@ -195,6 +195,9 @@ in particular the "PhotonFlux" and "EnergyFlux" names (#1832). |
| 195 | 195 | |
| 196 | 196 | The class now derives from ctlikelihood (#1828). |
| 197 | 197 | |
| 198 | +The tool now makes sure that the intensity parameter of the source of interest | |
| 199 | +is free in the model optimisation that preceeds the upper limit computation. | |
| 200 | + | |
| 198 | 201 | |
| 199 | 202 | cterror - Likelihood profile error computation |
| 200 | 203 | ---------------------------------------------- |
| ... | ... | @@ -232,11 +235,14 @@ definition XML file (#1881). |
| 232 | 235 | |
| 233 | 236 | Add cssrcdetect script to detect sources in a sky map (#1617). |
| 234 | 237 | |
| 238 | +Improve the upper limit computation in the cslightcrv script. Now all source | |
| 239 | +parameters, except the source intensity, are set to the initial values given | |
| 240 | +in the model definition file for upper limit computation. | |
| 235 | 241 | |
| 236 | 242 | |
| 237 | 243 | Examples |
| 238 | 244 | -------- |
| 239 | -None | |
| 245 | +Add show_lightcurve script to display a light curve. | |
| 240 | 246 | |
| 241 | 247 | |
| 242 | 248 | caldb | ... | ... |
cscripts/Makefile.am
| ... | ... | @@ -166,6 +166,7 @@ testdata = $(top_srcdir)/test/data/crab.xml \ |
| 166 | 166 | $(top_srcdir)/test/data/sensitivity_integral.dat \ |
| 167 | 167 | $(top_srcdir)/test/data/skymap.fits \ |
| 168 | 168 | $(top_srcdir)/test/data/spectrum.fits \ |
| 169 | + $(top_srcdir)/test/data/lightcurve.fits \ | |
| 169 | 170 | $(top_srcdir)/test/data/ts.dat \ |
| 170 | 171 | $(top_srcdir)/test/data/models_spatial.xml \ |
| 171 | 172 | $(top_srcdir)/test/data/models_spectral.xml \ | ... | ... |
cscripts/cslightcrv.py
| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | # ========================================================================== |
| 3 | 3 | # Generates a lightcurve. |
| 4 | 4 | # |
| 5 | -# Copyright (C) 2014-2016 Michael Mayer | |
| 5 | +# Copyright (C) 2014-2017 Michael Mayer | |
| 6 | 6 | # |
| 7 | 7 | # This program is free software: you can redistribute it and/or modify |
| 8 | 8 | # it under the terms of the GNU General Public License as published by |
| ... | ... | @@ -69,7 +69,7 @@ class cslightcrv(ctools.cscript): |
| 69 | 69 | """ |
| 70 | 70 | # Set name |
| 71 | 71 | self._name = 'cslightcrv' |
| 72 | - self._version = '1.2.0' | |
| 72 | + self._version = '1.3.0' | |
| 73 | 73 | |
| 74 | 74 | # Initialise some members |
| 75 | 75 | self._srcname = '' |
| ... | ... | @@ -335,18 +335,26 @@ class cslightcrv(ctools.cscript): |
| 335 | 335 | table.append(ts) |
| 336 | 336 | |
| 337 | 337 | # Append upper limit column "UpperLimit" |
| 338 | - ulimit = gammalib.GFitsTableDoubleCol('UpperLimit', nrows) | |
| 339 | - ulimit.unit('ph/cm2/s') | |
| 338 | + ul_diff = gammalib.GFitsTableDoubleCol('DiffUpperLimit', nrows) | |
| 339 | + ul_flux = gammalib.GFitsTableDoubleCol('FluxUpperLimit', nrows) | |
| 340 | + ul_eflux = gammalib.GFitsTableDoubleCol('EFluxUpperLimit', nrows) | |
| 341 | + ul_diff.unit('ph/cm2/s/MeV') | |
| 342 | + ul_flux.unit('ph/cm2/s') | |
| 343 | + ul_eflux.unit('erg/cm2/s') | |
| 340 | 344 | for i, result in enumerate(results): |
| 341 | - ulimit[i] = result['ulimit'] | |
| 342 | - table.append(ulimit) | |
| 345 | + ul_diff[i] = result['ul_diff'] | |
| 346 | + ul_flux[i] = result['ul_flux'] | |
| 347 | + ul_eflux[i] = result['ul_eflux'] | |
| 348 | + table.append(ul_diff) | |
| 349 | + table.append(ul_flux) | |
| 350 | + table.append(ul_eflux) | |
| 343 | 351 | |
| 344 | 352 | # Return table |
| 345 | 353 | return table |
| 346 | 354 | |
| 347 | 355 | def _compute_ulimit(self, obs): |
| 348 | 356 | """ |
| 349 | - Computes upper flux limit | |
| 357 | + Computes upper limit | |
| 350 | 358 | |
| 351 | 359 | Parameters |
| 352 | 360 | ---------- |
| ... | ... | @@ -355,35 +363,57 @@ class cslightcrv(ctools.cscript): |
| 355 | 363 | |
| 356 | 364 | Returns |
| 357 | 365 | ------- |
| 358 | - ulimit : float | |
| 359 | - Upper flux limit (-1.0 of not computed) | |
| 366 | + ul_diff, ul_flux, ul_eflux : tuple of float | |
| 367 | + Upper limits, -1.0 of not computed | |
| 360 | 368 | """ |
| 361 | 369 | # Initialise upper flux limit |
| 362 | - ulimit_value = -1.0 | |
| 363 | - | |
| 370 | + ul_diff = -1.0 | |
| 371 | + ul_flux = -1.0 | |
| 372 | + ul_eflux = -1.0 | |
| 373 | + | |
| 364 | 374 | # Perform computation only if requested |
| 365 | 375 | if self['calc_ulim'].boolean(): |
| 366 | 376 | |
| 367 | 377 | # Write header in logger |
| 368 | 378 | if self._logExplicit(): |
| 369 | - self._log.header3('Computing upper flux limit') | |
| 370 | - | |
| 371 | - # Create upper limit object | |
| 372 | - ulimit = ctools.ctulimit(obs) | |
| 373 | - ulimit['srcname'] = self._srcname | |
| 374 | - ulimit['eref'] = 1.0 | |
| 375 | - | |
| 376 | - # Try to run upper limit and catch any exceptions | |
| 379 | + self._log.header3('Computing upper limit') | |
| 380 | + | |
| 381 | + # Create copy of observations | |
| 382 | + cpy_obs = obs.copy() | |
| 383 | + | |
| 384 | + # Fix parameters of source of interest in copy of observations. | |
| 385 | + # This assures that the original spectral parameters and position | |
| 386 | + # are used in the upper limit search. The ctulimit tool makes sure | |
| 387 | + # that the flux parameter is freed when needed. | |
| 388 | + source = cpy_obs.models()[self._srcname] | |
| 389 | + for par in source: | |
| 390 | + if par.is_free(): | |
| 391 | + par.fix() | |
| 392 | + | |
| 393 | + # Create instance of upper limit tool | |
| 394 | + ulimit = ctools.ctulimit(cpy_obs) | |
| 395 | + ulimit['srcname'] = self._srcname | |
| 396 | + ulimit['eref'] = 1.0 | |
| 397 | + ulimit['emin'] = self['emin'].real() | |
| 398 | + ulimit['emax'] = self['emax'].real() | |
| 399 | + ulimit['sigma_min'] = 0.0 | |
| 400 | + ulimit['sigma_max'] = 3.0 | |
| 401 | + | |
| 402 | + # Try to run upper limit tool and catch any exceptions | |
| 377 | 403 | try: |
| 378 | 404 | ulimit.run() |
| 379 | - ulimit_value = ulimit.flux_ulimit() | |
| 405 | + ul_diff = ulimit.diff_ulimit() | |
| 406 | + ul_flux = ulimit.flux_ulimit() | |
| 407 | + ul_eflux = ulimit.eflux_ulimit() | |
| 380 | 408 | except: |
| 381 | 409 | if self._logTerse(): |
| 382 | - self._log('Upper limit flux calculation failed.\n') | |
| 383 | - ulimit_value = -1.0 | |
| 410 | + self._log('Upper limit calculation failed.\n') | |
| 411 | + ul_diff = -1.0 | |
| 412 | + ul_flux = -1.0 | |
| 413 | + ul_eflux = -1.0 | |
| 384 | 414 | |
| 385 | - # Return upper limit | |
| 386 | - return ulimit_value | |
| 415 | + # Return upper limit tuple | |
| 416 | + return ul_diff, ul_flux, ul_eflux | |
| 387 | 417 | |
| 388 | 418 | def _bin_observation(self, obs): |
| 389 | 419 | """ |
| ... | ... | @@ -517,7 +547,9 @@ class cslightcrv(ctools.cscript): |
| 517 | 547 | result = {'mjd': tmean.mjd(), |
| 518 | 548 | 'e_mjd': twidth / gammalib.sec_in_day, |
| 519 | 549 | 'ts': 0.0, |
| 520 | - 'ulimit': 0.0, | |
| 550 | + 'ul_diff': 0.0, | |
| 551 | + 'ul_flux': 0.0, | |
| 552 | + 'ul_eflux': 0.0, | |
| 521 | 553 | 'pars': pars, |
| 522 | 554 | 'values': {}} |
| 523 | 555 | |
| ... | ... | @@ -581,9 +613,12 @@ class cslightcrv(ctools.cscript): |
| 581 | 613 | result['values']['e_'+par] = source.spectral()[par].error() |
| 582 | 614 | |
| 583 | 615 | # Calculate upper limit (-1 if not computed) |
| 584 | - ulimit_value = self._compute_ulimit(like.obs()) | |
| 585 | - if ulimit_value > 0.0: | |
| 586 | - result['ulimit'] = ulimit_value | |
| 616 | + #ul_diff, ul_flux, ul_eflux = self._compute_ulimit(like.obs()) | |
| 617 | + ul_diff, ul_flux, ul_eflux = self._compute_ulimit(obs) | |
| 618 | + if ul_diff > 0.0: | |
| 619 | + result['ul_diff'] = ul_diff | |
| 620 | + result['ul_flux'] = ul_flux | |
| 621 | + result['ul_eflux'] = ul_eflux | |
| 587 | 622 | |
| 588 | 623 | # Extract Test Statistic value |
| 589 | 624 | if self['calc_ts'].boolean(): |
| ... | ... | @@ -601,9 +636,13 @@ class cslightcrv(ctools.cscript): |
| 601 | 636 | unit = source.spectral()[par].unit() |
| 602 | 637 | self._log_value(gammalib.NORMAL, par, |
| 603 | 638 | str(value)+' +/- '+str(error)+' '+unit) |
| 604 | - if result['ulimit'] > 0.0: | |
| 639 | + if ul_diff > 0.0: | |
| 640 | + self._log_value(gammalib.NORMAL, 'Upper flux limit', | |
| 641 | + str(result['ul_diff'])+' ph/cm2/s/MeV') | |
| 642 | + self._log_value(gammalib.NORMAL, 'Upper flux limit', | |
| 643 | + str(result['ul_flux'])+' ph/cm2/s') | |
| 605 | 644 | self._log_value(gammalib.NORMAL, 'Upper flux limit', |
| 606 | - str(result['ulimit'])+' ph/cm2/s') | |
| 645 | + str(result['ul_eflux'])+' erg/cm2/s') | |
| 607 | 646 | if self['calc_ts'].boolean(): |
| 608 | 647 | self._log_value(gammalib.NORMAL, 'Test Statistic', result['ts']) |
| 609 | 648 | ... | ... |
examples/Makefile.am
examples/README.md
| ... | ... | @@ -70,5 +70,8 @@ the ctools. |
| 70 | 70 | `show_spectrum.py` |
| 71 | 71 | > Displays spectrum generated by the csspec script. |
| 72 | 72 | |
| 73 | +`show_lightcurve.py` | |
| 74 | +> Displays lightcurve generated by the cslightcrv script. | |
| 75 | + | |
| 73 | 76 | `show_ts_distribution.py` |
| 74 | 77 | > Displays Test Statistic histograms generated using the cstsdist script. | ... | ... |
examples/show_lightcurve.py
0 → 100755
| 1 | +#! /usr/bin/env python | |
| 2 | +# ========================================================================== | |
| 3 | +# Display lightcurve generated by cslightcrv | |
| 4 | +# | |
| 5 | +# Copyright (C) 2017 Juergen Knoedlseder | |
| 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 | +import sys | |
| 22 | +import gammalib | |
| 23 | +import cscripts | |
| 24 | +try: | |
| 25 | + import matplotlib.pyplot as plt | |
| 26 | + plt.figure() | |
| 27 | + plt.close() | |
| 28 | +except (ImportError, RuntimeError): | |
| 29 | + print('This script needs the "matplotlib" module') | |
| 30 | + sys.exit() | |
| 31 | + | |
| 32 | + | |
| 33 | +# =============== # | |
| 34 | +# Plot lightcurve # | |
| 35 | +# =============== # | |
| 36 | +def plot_lightcurve(filename, plotfile): | |
| 37 | + """ | |
| 38 | + Plot lightcurve | |
| 39 | + | |
| 40 | + Parameters | |
| 41 | + ---------- | |
| 42 | + filename : str | |
| 43 | + Name of lightcurve FITS file | |
| 44 | + plotfile : str | |
| 45 | + Plot file name | |
| 46 | + """ | |
| 47 | + # Read spectrum file | |
| 48 | + fits = gammalib.GFits(filename) | |
| 49 | + table = fits.table(1) | |
| 50 | + c_mjd = table['MJD'] | |
| 51 | + c_emjd = table['e_MJD'] | |
| 52 | + c_flux = table['EnergyFlux'] | |
| 53 | + c_eflux = table['e_EnergyFlux'] | |
| 54 | + c_ts = table['TS'] | |
| 55 | + c_upper = table['EFluxUpperLimit'] | |
| 56 | + | |
| 57 | + # Initialise arrays to be filled | |
| 58 | + mjd = [] | |
| 59 | + e_mjd = [] | |
| 60 | + flux = [] | |
| 61 | + e_flux = [] | |
| 62 | + ul_mjd = [] | |
| 63 | + ul_e_mjd = [] | |
| 64 | + ul_flux = [] | |
| 65 | + ul_e_flux = [] | |
| 66 | + | |
| 67 | + # Loop over rows of the file | |
| 68 | + nrows = table.nrows() | |
| 69 | + for row in range(nrows): | |
| 70 | + | |
| 71 | + # Get Test Statistic, flux and flux error | |
| 72 | + ts = c_ts.real(row) | |
| 73 | + flx = c_flux.real(row) | |
| 74 | + e_flx = c_eflux.real(row) | |
| 75 | + | |
| 76 | + # If Test Statistic is larger than 9 and twice the flux error is | |
| 77 | + # smaller than the flux, then append flux point ... | |
| 78 | + if ts > 9.0 and 2.0*e_flx < flx: | |
| 79 | + mjd.append(c_mjd.real(row)) | |
| 80 | + e_mjd.append(c_emjd.real(row)) | |
| 81 | + flux.append(c_flux.real(row)) | |
| 82 | + e_flux.append(c_eflux.real(row)) | |
| 83 | + | |
| 84 | + # ... otherwise append upper limit | |
| 85 | + else: | |
| 86 | + ul_mjd.append(c_mjd.real(row)) | |
| 87 | + ul_e_mjd.append(c_emjd.real(row)) | |
| 88 | + ul_flux.append(c_upper.real(row)) | |
| 89 | + ul_e_flux.append(0.5*c_upper.real(row)) | |
| 90 | + | |
| 91 | + # Plot the spectrum | |
| 92 | + plt.figure() | |
| 93 | + plt.semilogy() | |
| 94 | + plt.grid() | |
| 95 | + plt.errorbar(mjd, flux, yerr=e_flux, xerr=[e_mjd, e_mjd], | |
| 96 | + fmt='ro') | |
| 97 | + plt.errorbar(ul_mjd, ul_flux, xerr=[ul_e_mjd, ul_e_mjd], | |
| 98 | + yerr=ul_e_flux, uplims=True, fmt='ro') | |
| 99 | + plt.xlabel('MJD (days)') | |
| 100 | + plt.ylabel(r'E $\times$ dN/dE (erg cm$^{-2}$ s$^{-1}$)') | |
| 101 | + | |
| 102 | + # Show figure | |
| 103 | + if len(plotfile) > 0: | |
| 104 | + plt.savefig(plotfile) | |
| 105 | + else: | |
| 106 | + plt.show() | |
| 107 | + | |
| 108 | + # Return | |
| 109 | + return | |
| 110 | + | |
| 111 | + | |
| 112 | +# =============== # | |
| 113 | +# Show lightcurve # | |
| 114 | +# =============== # | |
| 115 | +def show_lightcurve(): | |
| 116 | + """ | |
| 117 | + Show lightcurve | |
| 118 | + """ | |
| 119 | + # Set usage string | |
| 120 | + usage = 'show_lightcurve.py [-p plotfile] [file]' | |
| 121 | + | |
| 122 | + # Set default options | |
| 123 | + options = [{'option': '-p', 'value': ''}] | |
| 124 | + | |
| 125 | + # Get arguments and options from command line arguments | |
| 126 | + args, options = cscripts.ioutils.get_args_options(options, usage) | |
| 127 | + | |
| 128 | + # Extract script parameters from options | |
| 129 | + plotfile = options[0]['value'] | |
| 130 | + | |
| 131 | + # Plot lightcurve | |
| 132 | + plot_lightcurve(args[0], plotfile) | |
| 133 | + | |
| 134 | + # Return | |
| 135 | + return | |
| 136 | + | |
| 137 | + | |
| 138 | +# ======================== # | |
| 139 | +# Main routine entry point # | |
| 140 | +# ======================== # | |
| 141 | +if __name__ == '__main__': | |
| 142 | + | |
| 143 | + # Show lightcurve | |
| 144 | + show_lightcurve() | ... | ... |
examples/show_spectrum.py
test/data/lightcurve.fits
0 → 100644
No preview for this file type
test/test_cslightcrv.py
| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | # ========================================================================== |
| 3 | 3 | # This scripts performs unit tests for the cslightcrv script. |
| 4 | 4 | # |
| 5 | -# Copyright (C) 2016 Juergen Knoedlseder | |
| 5 | +# Copyright (C) 2016-2017 Juergen Knoedlseder | |
| 6 | 6 | # |
| 7 | 7 | # This program is free software: you can redistribute it and/or modify |
| 8 | 8 | # it under the terms of the GNU General Public License as published by |
| ... | ... | @@ -239,7 +239,8 @@ class Test(test): |
| 239 | 239 | """ |
| 240 | 240 | # Expected column names |
| 241 | 241 | cols = ['MJD', 'e_MJD', 'Prefactor', 'e_Prefactor', |
| 242 | - 'Index', 'e_Index', 'TS', 'UpperLimit'] | |
| 242 | + 'Index', 'e_Index', 'TS', 'DiffUpperLimit', | |
| 243 | + 'FluxUpperLimit', 'EFluxUpperLimit'] | |
| 243 | 244 | |
| 244 | 245 | # Open FITS file |
| 245 | 246 | fits = gammalib.GFits(filename) | ... | ... |
test/test_examples.py.in
| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | # ========================================================================== |
| 3 | 3 | # Performs unit tests for the ctools example scripts |
| 4 | 4 | # |
| 5 | -# Copyright (C) 2016 Juergen Knoedlseder | |
| 5 | +# Copyright (C) 2016-2017 Juergen Knoedlseder | |
| 6 | 6 | # |
| 7 | 7 | # This program is free software: you can redistribute it and/or modify |
| 8 | 8 | # it under the terms of the GNU General Public License as published by |
| ... | ... | @@ -128,6 +128,8 @@ class Test(gammalib.GPythonTestSuite): |
| 128 | 128 | 'Test show_sensitivity.py') |
| 129 | 129 | self.append(self.test_show_spectrum, |
| 130 | 130 | 'Test show_spectrum.py') |
| 131 | + self.append(self.test_show_lightcurve, | |
| 132 | + 'Test show_lightcurve.py') | |
| 131 | 133 | self.append(self.test_show_ts_distribution, |
| 132 | 134 | 'Test ts_distribution.py') |
| 133 | 135 | self.append(self.test_show_obs, |
| ... | ... | @@ -401,6 +403,20 @@ class Test(gammalib.GPythonTestSuite): |
| 401 | 403 | # Return |
| 402 | 404 | return |
| 403 | 405 | |
| 406 | + # Test show_lightcurve | |
| 407 | + def test_show_lightcurve(self): | |
| 408 | + """ | |
| 409 | + Test show_lightcurve | |
| 410 | + """ | |
| 411 | + # Set arguments | |
| 412 | + args = '-p example_show_lightcurve.eps %s/lightcurve.fits' % self._datadir | |
| 413 | + | |
| 414 | + # Execute script | |
| 415 | + self._execute_python('show_lightcurve', args=args) | |
| 416 | + | |
| 417 | + # Return | |
| 418 | + return | |
| 419 | + | |
| 404 | 420 | # Test show_ts_distribution |
| 405 | 421 | def test_show_ts_distribution(self): |
| 406 | 422 | """ | ... | ... |