Commit eac7d1c21bc2c55bca923523f99ea4486abb62c1
Merge branch '1290-SpecpointsAndTS' into integration
Showing
2 changed files
with
215 additions
and
1 deletions
examples/make_spectrum.py
0 → 100755
| 1 | +#! /usr/bin/env python | ||
| 2 | +# ========================================================================== | ||
| 3 | +# This script shows how to make a spectrum using obsutils. | ||
| 4 | +# | ||
| 5 | +# Copyright (C) 2014 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 gammalib | ||
| 22 | +import ctools | ||
| 23 | +try: | ||
| 24 | + import matplotlib.pyplot as plt | ||
| 25 | + has_matplotlib = True | ||
| 26 | +except: | ||
| 27 | + has_matplotlib = False | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +# =============== # | ||
| 31 | +# Make a spectrum # | ||
| 32 | +# =============== # | ||
| 33 | +def make_spectrum(): | ||
| 34 | + """ | ||
| 35 | + """ | ||
| 36 | + # Set script parameters | ||
| 37 | + model_name = "${CTOOLS}/share/models/crab.xml" | ||
| 38 | + caldb = "dummy" | ||
| 39 | + irf = "cta_dummy_irf" | ||
| 40 | + ra = 83.63 | ||
| 41 | + dec = 22.01 | ||
| 42 | + rad_sim = 3.0 | ||
| 43 | + tstart = 0.0 | ||
| 44 | + tstop = 1800.0 | ||
| 45 | + emin = 0.1 | ||
| 46 | + emax = 100.0 | ||
| 47 | + | ||
| 48 | + # Simulate events | ||
| 49 | + sim = ctools.ctobssim() | ||
| 50 | + sim["infile"].filename(model_name) | ||
| 51 | + sim["caldb"].string(caldb) | ||
| 52 | + sim["irf"].string(irf) | ||
| 53 | + sim["ra"].real(ra) | ||
| 54 | + sim["dec"].real(dec) | ||
| 55 | + sim["rad"].real(rad_sim) | ||
| 56 | + sim["tmin"].real(tstart) | ||
| 57 | + sim["tmax"].real(tstop) | ||
| 58 | + sim["emin"].real(emin) | ||
| 59 | + sim["emax"].real(emax) | ||
| 60 | + sim.run() | ||
| 61 | + | ||
| 62 | + # Generate an energy binning | ||
| 63 | + e_min = gammalib.GEnergy(emin, "TeV") | ||
| 64 | + e_max = gammalib.GEnergy(emax, "TeV") | ||
| 65 | + ebounds = gammalib.GEbounds(10, e_min, e_max) | ||
| 66 | + | ||
| 67 | + # Generate spectral points | ||
| 68 | + spectrum = ctools.obsutils.spectrum(sim.obs(), "Crab", ebounds) | ||
| 69 | + | ||
| 70 | + # Return spectrum | ||
| 71 | + return spectrum | ||
| 72 | + | ||
| 73 | + | ||
| 74 | +# ============= # | ||
| 75 | +# Plot spectrum # | ||
| 76 | +# ============= # | ||
| 77 | +def plot_spectrum(spectrum): | ||
| 78 | + """ | ||
| 79 | + Plot spectrum. | ||
| 80 | + """ | ||
| 81 | + # Create figure | ||
| 82 | + plt.figure(1) | ||
| 83 | + plt.title("Crab spectrum") | ||
| 84 | + | ||
| 85 | + # Plot spectrum | ||
| 86 | + plt.loglog(spectrum['energy']['value'], \ | ||
| 87 | + spectrum['flux']['value'], 'ro', label='Crab') | ||
| 88 | + plt.errorbar(spectrum['energy']['value'], \ | ||
| 89 | + spectrum['flux']['value'], \ | ||
| 90 | + spectrum['flux']['ed_value'], fmt=None, ecolor='r') | ||
| 91 | + | ||
| 92 | + # Put labels | ||
| 93 | + plt.xlabel("Energy ("+spectrum['energy']['unit']+")") | ||
| 94 | + plt.ylabel("Flux ("+spectrum['flux']['unit']+")") | ||
| 95 | + plt.legend(loc="lower left") | ||
| 96 | + | ||
| 97 | + # Show spectrum | ||
| 98 | + plt.show() | ||
| 99 | + | ||
| 100 | + # Return | ||
| 101 | + return | ||
| 102 | + | ||
| 103 | + | ||
| 104 | +#==========================# | ||
| 105 | +# Main routine entry point # | ||
| 106 | +#==========================# | ||
| 107 | +if __name__ == '__main__': | ||
| 108 | + """ | ||
| 109 | + Generate a Crab spectrum. | ||
| 110 | + """ | ||
| 111 | + # Generate spectrum | ||
| 112 | + spectrum = make_spectrum() | ||
| 113 | + | ||
| 114 | + # Plot spectrum | ||
| 115 | + if has_matplotlib: | ||
| 116 | + plot_spectrum(spectrum) | ||
| 117 | + | ||
| 0 | \ No newline at end of file | 118 | \ No newline at end of file |
pyext/ctools/obsutils.py
| @@ -20,6 +20,7 @@ | @@ -20,6 +20,7 @@ | ||
| 20 | # ========================================================================== | 20 | # ========================================================================== |
| 21 | import ctools | 21 | import ctools |
| 22 | import gammalib | 22 | import gammalib |
| 23 | +import math | ||
| 23 | 24 | ||
| 24 | 25 | ||
| 25 | # ===================== # | 26 | # ===================== # |
| @@ -124,7 +125,7 @@ def sim(obs, log=False, debug=False, edisp=False, seed=0, nbins=0, | @@ -124,7 +125,7 @@ def sim(obs, log=False, debug=False, edisp=False, seed=0, nbins=0, | ||
| 124 | # ================ # | 125 | # ================ # |
| 125 | # Fit observations # | 126 | # Fit observations # |
| 126 | # ================ # | 127 | # ================ # |
| 127 | -def fit(obs, log=False, debug=False, edisp=False): | 128 | +def fit(obs, log=False, debug=False, edisp=False, tscalc=False): |
| 128 | """ | 129 | """ |
| 129 | Perform maximum likelihood fitting of observations in the container. | 130 | Perform maximum likelihood fitting of observations in the container. |
| 130 | 131 | ||
| @@ -145,9 +146,14 @@ def fit(obs, log=False, debug=False, edisp=False): | @@ -145,9 +146,14 @@ def fit(obs, log=False, debug=False, edisp=False): | ||
| 145 | # Optionally switch-on debugging model | 146 | # Optionally switch-on debugging model |
| 146 | if debug: | 147 | if debug: |
| 147 | like["debug"].boolean(True) | 148 | like["debug"].boolean(True) |
| 149 | + | ||
| 150 | + # Optionally turn on TS calculation | ||
| 151 | + if tscalc: | ||
| 152 | + like["tscalc"].boolean(True) | ||
| 148 | 153 | ||
| 149 | # Optionally apply energy dispersion | 154 | # Optionally apply energy dispersion |
| 150 | like["edisp"].boolean(edisp) | 155 | like["edisp"].boolean(edisp) |
| 156 | + like["edisp"].boolean(edisp) | ||
| 151 | 157 | ||
| 152 | # Run ctlike application. | 158 | # Run ctlike application. |
| 153 | like.run() | 159 | like.run() |
| @@ -516,3 +522,94 @@ def set_obs_patterns(pattern, ra=83.6331, dec=22.0145, offset=1.5): | @@ -516,3 +522,94 @@ def set_obs_patterns(pattern, ra=83.6331, dec=22.0145, offset=1.5): | ||
| 516 | 522 | ||
| 517 | # Return observation definition list | 523 | # Return observation definition list |
| 518 | return obsdeflist | 524 | return obsdeflist |
| 525 | + | ||
| 526 | + | ||
| 527 | +# ================================================================= # | ||
| 528 | +# Generate a spectrum by fitting an observation in energy intervals # | ||
| 529 | +# ================================================================= # | ||
| 530 | +def spectrum(obs, source, ebounds): | ||
| 531 | + """ | ||
| 532 | + Returns spectral points in the given binning for the passed observations | ||
| 533 | + and source. | ||
| 534 | + | ||
| 535 | + Please note that specpoints can make spectra for any given spectral | ||
| 536 | + model, as long as it has a "Prefactor" or "Integral" parameter which | ||
| 537 | + scales the function | ||
| 538 | + | ||
| 539 | + Parameters: | ||
| 540 | + obs - Observation container | ||
| 541 | + source - Source name for which spectrum should be computed | ||
| 542 | + ebounds - GEbounds energy boundaries | ||
| 543 | + Keywords: | ||
| 544 | + None | ||
| 545 | + """ | ||
| 546 | + # Check if parameters Prefactor or Integral exist to scale mdoel | ||
| 547 | + normpar = "Prefactor" | ||
| 548 | + srcmodel = obs.models()[source] | ||
| 549 | + if not srcmodel.has_par(normpar): | ||
| 550 | + if srcmodel.has_par("Integral"): | ||
| 551 | + normpar = "Integral" | ||
| 552 | + else: | ||
| 553 | + print("Error: your spectral model of "+source+"does not"+\ | ||
| 554 | + "have a scaling parameter named 'Prefactor'"+\ | ||
| 555 | + " or 'Integral'") | ||
| 556 | + return | ||
| 557 | + | ||
| 558 | + # Fix all parameters except prefactor | ||
| 559 | + opt_spectral = srcmodel.spectral() | ||
| 560 | + for model in obs.models(): | ||
| 561 | + for par in range(model.size()): | ||
| 562 | + model[par].fix() | ||
| 563 | + opt_spectral[normpar].free() | ||
| 564 | + | ||
| 565 | + # Define spectrum dictionary, units are in format readable by astropy | ||
| 566 | + spec = {"energy":{"value":[],"eu_value":[],"ed_value":[],"unit":"none"}, | ||
| 567 | + "flux" :{"value":[],"eu_value":[],"ed_value":[],"unit":"none"}, | ||
| 568 | + "TS" :{"value":[],"eu_value":[],"ed_value":[],"unit":"none"}} | ||
| 569 | + | ||
| 570 | + # Loop over energy bins, fit and write result to spectrum | ||
| 571 | + for i in range(len(ebounds)): | ||
| 572 | + | ||
| 573 | + # Get logarithmic bin center and flux value of all energy fit | ||
| 574 | + ecenter = ebounds.elogmean(i) | ||
| 575 | + allflux = opt_spectral.eval(ecenter, gammalib.GTime(0.0)) | ||
| 576 | + | ||
| 577 | + # Clone observations and reset energy thresholds | ||
| 578 | + select = ctools.ctselect(obs) | ||
| 579 | + select["ra"].real(-1.0) | ||
| 580 | + select["dec"].real(-1.0) | ||
| 581 | + select["rad"].real(-1.0) | ||
| 582 | + select["tmin"].real(0.0) | ||
| 583 | + select["tmax"].real(0.0) | ||
| 584 | + select["emin"].real(ebounds.emin(i).TeV()) | ||
| 585 | + select["emax"].real(ebounds.emax(i).TeV()) | ||
| 586 | + select.run() | ||
| 587 | + | ||
| 588 | + # Refit and write out to spectrum | ||
| 589 | + binlike = fit(select.obs(), log=False, debug=False, edisp=False, tscalc=True) | ||
| 590 | + binopt_spectral = binlike.obs().models()[source].spectral() | ||
| 591 | + binflux = binopt_spectral.eval(ecenter, gammalib.GTime(0.0)) | ||
| 592 | + if binopt_spectral[normpar].value() > 0.0: | ||
| 593 | + relerr = binopt_spectral[normpar].error()/binopt_spectral[normpar].value() | ||
| 594 | + else: | ||
| 595 | + relerr = 0.0 | ||
| 596 | + | ||
| 597 | + # Store and convert to TeV from MeV | ||
| 598 | + spec["energy"]["value"].append(ecenter.TeV()) | ||
| 599 | + spec["energy"]["ed_value"].append(ecenter.TeV()-ebounds.emin(i).TeV()) | ||
| 600 | + spec["energy"]["eu_value"].append(ebounds.emax(i).TeV()-ecenter.TeV()) | ||
| 601 | + spec["energy"]["unit"] = "TeV" | ||
| 602 | + | ||
| 603 | + spec["flux"]["value"].append(binflux*1.0e6) | ||
| 604 | + spec["flux"]["ed_value"].append(relerr*binflux*1.0e6) | ||
| 605 | + spec["flux"]["eu_value"].append(relerr*binflux*1.0e6) | ||
| 606 | + spec["flux"]["unit"] = "ph/cm2/s/TeV" | ||
| 607 | + | ||
| 608 | + spec["TS"]["value"].append(binlike.obs().models()[source].ts()) | ||
| 609 | + | ||
| 610 | + # Debugging??? | ||
| 611 | + #print(spec["energy"]["value"]) | ||
| 612 | + #print(spec["flux"]["value"]) | ||
| 613 | + | ||
| 614 | + # Return spectrum | ||
| 615 | + return spec |