Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

weighting MC gamma to MAGIC log-parabola spectrum #302

Merged
merged 12 commits into from
Mar 30, 2020
Merged
98 changes: 85 additions & 13 deletions lstchain/mc/mc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import numpy as np
import astropy.units as u
import sys
from gammapy.modeling.models import LogParabolaSpectralModel

__all__ = [
'power_law_integrated_distribution',
Expand Down Expand Up @@ -66,63 +68,133 @@ def int_diff_sp(emin, emax, sp_idx, e0):

return integral_e

def rate(emin, emax, sp_idx, cone, area, norm, e0):
def rate(shape, emin, emax, param, cone, area):
moralejo marked this conversation as resolved.
Show resolved Hide resolved
"""
Calculates the rate of events for a power-law distribution,
in a given energy range, collection area and solid angle

Parameters
----------
shape: `string` weighted spectrum shape
emin: `float` minimum energy
emax: `float` maximum energy
sp_idx:`float` spectral index of the power-law distribution
param: `dict` with weighted spectral parameters
cone: `float` angle [deg] for the solid angle cone
area: `float` collection area [cm**2]
norm: `float` normalization of the differential energy spectrum at e0
e0: `float` normalization energy

if shape is 'PowerLaw':
param should include 'f0','e0','alpha'
dFdE = f0 * np.power(E / e0, alpha)

if shape is 'LogParabola':
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @SeiyaNozaki, I'm happy with the changes and would propose this to be accepted if @moralejo approves, just one additional question: if the shape given is LogParabola and no beta parameter is given, will the call throw an error or is there any default value?
Also, what if shape == PowerLaw and I introduce a beta parameter in the call?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @rlopezcoto ,
For the first case(LogParabola without beta), we will get error.
But the second case(PowerLaw with beta), no error and just other parameters are used for the calcurlation.
Would it be better to add the function to check if the param match the shape?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what kind of error in the first case? (a generic python one I guess).
It would be great if you could check that we are giving the correct parameters depending on the function introduced.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what kind of error in the first case? (a generic python one I guess).

Yes, just a KeyError.

>>> crab_par_hegra
{'f0': <Quantity 2.83e-11 1 / (cm2 s TeV)>, 'alpha': -2.62, 'e0': <Quantity 1. TeV>}
>>> rate_ = rate("LogParabola", 0.1 * u.TeV, 100 * u.TeV, crab_par_hegra, 0, 0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/cta/Work/Install/anaconda3/envs/cta-dev/lib/python3.7/site-packages/lstchain-0.4.5.post9+git66dd663-py3.7.egg/lstchain/mc/mc.py", line 107, in rate
    log_parabola =  LogParabolaSpectralModel.from_log10(amplitude=param['f0'], reference=param['e0'], alpha=-1*param['alpha'], beta=-1*param['beta'])
KeyError: 'beta'

It would be great if you could check that we are giving the correct parameters depending on the function introduced.

I'll introduce such a function in a few days.

param should include 'f0','e0','alpha','beta'
dFdE = f0 * np.power(E / e0, alpha + beta * np.log10(E/e0))

Returns
----------
rate: `float` rate of events

TODO: Introduce any spectral form
"""

if(cone == 0):
omega = 1
else:
omega = 2 * np.pi * (1-np.cos(cone)) * u.sr

integral = int_diff_sp(emin, emax, sp_idx, e0)

rate = norm * area * omega * integral
if(shape == "PowerLaw"):
if(len(param) != 3):
print("param included {} parameters, not 3".format(len(param)))
print("param should include 'f0', 'e0', 'alpha'")
sys.exit(1)

for key in ['f0','e0','alpha']:
if(key not in param.keys()):
print("{} is missing in param".format(key))
print("param should include 'f0', 'e0', 'alpha'")
sys.exit(1)

integral = param['f0'] * int_diff_sp(emin, emax, param['alpha'], param['e0'])

elif(shape == "LogParabola"):
if(len(param) != 4):
print("param included {} parameters, not 4".format(len(param)))
print("param should include 'f0', 'e0', 'alpha', 'beta'")
sys.exit(1)

for key in ['f0','e0','alpha', 'beta']:
if(key not in param.keys()):
print("{} is missing in param".format(key))
print("param should include 'f0', 'e0', 'alpha', 'beta'")
sys.exit(1)

log_parabola = LogParabolaSpectralModel.from_log10(amplitude=param['f0'], reference=param['e0'], alpha=-1*param['alpha'], beta=-1*param['beta'])
integral = log_parabola.integral(emin, emax)

rate = area * omega * integral

return rate


def weight(emin, emax, sim_sp_idx, w_sp_idx, rate, nev, e0):
def weight(shape, emin, emax, sim_sp_idx, rate, nev, w_param):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, explain w_param, especially signs.

"""
Calculates the weight of events to transform a power-law distribution
with spectral index sim_sp_idx to a power-law distribution with
spectral index w_sp_idx

Parameters
----------
shape: `string` estimated spectrum shape
emin: `float` minimum energy
emax: `float` maximum energy
sim_sp_idx: `float` simulated spectral index of the power-law distribution
w_sp_idx: `float` weighted spectral index of the power-law distribution
rate: `float` rate of simulated events
nev: `int` number of simulated events
e0: `float` normalization energy
w_param: `dict` with weighted spectral parameters

if shape is 'PowerLaw':
w_param should include 'f0','e0','alpha'
dFdE = f0 * np.power(E / e0, alpha)

if shape is 'LogParabola':
w_param should include 'f0','e0','alpha','beta'
dFdE = f0 * np.power(E / e0, alpha + beta * np.log10(E/e0))

Returns
----------
weight: `float` rate of events
"""

sim_integral = nev / int_diff_sp(emin, emax, sim_sp_idx, e0)
norm_sim = sim_integral * int_diff_sp(emin, emax, w_sp_idx, e0)
sim_integral = nev / int_diff_sp(emin, emax, sim_sp_idx, w_param['e0'])

if(shape == "PowerLaw"):
if(len(w_param) != 3):
print("param included {} parameters, not 3".format(len(w_param)))
print("param should include 'f0', 'e0', 'alpha'")
sys.exit(1)

for key in ['f0','e0','alpha']:
if(key not in w_param.keys()):
print("{} is missing in param".format(key))
print("param should include 'f0', 'e0', 'alpha'")
sys.exit(1)

norm_sim = sim_integral * int_diff_sp(emin, emax, w_param['alpha'], w_param['e0'])

elif(shape == "LogParabola"):
if(len(w_param) != 4):
print("param included {} parameters, not 4".format(len(w_param)))
print("param should include 'f0', 'e0', 'alpha', 'beta'")
sys.exit(1)

for key in ['f0','e0','alpha', 'beta']:
if(key not in w_param.keys()):
print("{} is missing in param".format(key))
print("param should include 'f0', 'e0', 'alpha', 'beta'")
sys.exit(1)

log_parabola = LogParabolaSpectralModel.from_log10(amplitude=sim_integral, reference=w_param['e0'], alpha=-1*w_param['alpha'], beta=-1*w_param['beta'])

norm_sim = log_parabola.integral(emin, emax)

weight = rate / norm_sim

Expand Down
48 changes: 18 additions & 30 deletions lstchain/mc/sensitivity.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import astropy.units as u
from .plot_utils import sensitivity_minimization_plot, plot_positions_survived_events
from .mc import rate, weight
from lstchain.spectra.crab import crab_hegra
from lstchain.spectra.crab import crab_hegra,crab_magic
from lstchain.spectra.proton import proton_bess
from gammapy.stats.poisson import excess_matching_significance_on_off
from lstchain.reco.utils import reco_source_position_sky
Expand Down Expand Up @@ -291,10 +291,10 @@ def ring_containment(angdist2, ring_radius, ring_halfwidth):
return contained, area

def find_best_cuts_sensitivity(simtelfile_gammas, simtelfile_protons,
dl2_file_g, dl2_file_p,
nfiles_gammas, nfiles_protons,
n_bins_energy, n_bins_gammaness, n_bins_theta2, noff,
obstime = 50 * 3600 * u.s):
dl2_file_g, dl2_file_p,
nfiles_gammas, nfiles_protons,
n_bins_energy, n_bins_gammaness, n_bins_theta2, noff,
obstime = 50 * 3600 * u.s):

"""
Main function to find the best cuts to calculate the sensitivity
Expand Down Expand Up @@ -364,21 +364,15 @@ def find_best_cuts_sensitivity(simtelfile_gammas, simtelfile_protons,
n_sim_bin = y

# Rates and weights
rate_g = rate(mc_par_g['emin'], mc_par_g['emax'], crab_par['alpha'],
mc_par_g['cone'], mc_par_g['area_sim'],
crab_par['f0'], crab_par['e0'])
rate_g = rate("PowerLaw", mc_par_g['emin'], mc_par_g['emax'], crab_par, mc_par_g['cone'], mc_par_g['area_sim'])

rate_p = rate(mc_par_p['emin'], mc_par_p['emax'], proton_par['alpha'],
mc_par_p['cone'], mc_par_p['area_sim'],
proton_par['f0'], proton_par['e0'])
rate_p = rate("PowerLaw", mc_par_p['emin'], mc_par_p['emax'], proton_par, mc_par_p['cone'], mc_par_p['area_sim'])

w_g = weight(mc_par_g['emin'], mc_par_g['emax'], mc_par_g['sp_idx'],
crab_par['alpha'], rate_g,
mc_par_g['sim_ev'], crab_par['e0'])
w_g = weight("PowerLaw", mc_par_g['emin'], mc_par_g['emax'], mc_par_g['sp_idx'], rate_g,
mc_par_g['sim_ev'], crab_par)

w_p = weight(mc_par_p['emin'], mc_par_p['emax'], mc_par_p['sp_idx'],
proton_par['alpha'], rate_p,
mc_par_p['sim_ev'], proton_par['e0'])
w_p = weight("PowerLaw", mc_par_p['emin'], mc_par_p['emax'], mc_par_p['sp_idx'], rate_p,
mc_par_p['sim_ev'], proton_par)

if (w_g.unit == u.Unit("sr / s")):
print("You are using diffuse gammas to estimate point-like sensitivity")
Expand Down Expand Up @@ -604,22 +598,16 @@ def sensitivity(simtelfile_gammas, simtelfile_protons,
n_sim_bin = y

# Rates and weights
rate_g = rate(mc_par_g['emin'], mc_par_g['emax'], crab_par['alpha'],
mc_par_g['cone'], mc_par_g['area_sim'],
crab_par['f0'], crab_par['e0'])
rate_g = rate("PowerLaw", mc_par_g['emin'], mc_par_g['emax'], crab_par, mc_par_g['cone'], mc_par_g['area_sim'])

rate_p = rate(mc_par_p['emin'], mc_par_p['emax'], proton_par['alpha'],
mc_par_p['cone'], mc_par_p['area_sim'],
proton_par['f0'], proton_par['e0'])
rate_p = rate("PowerLaw", mc_par_p['emin'], mc_par_p['emax'], proton_par, mc_par_p['cone'], mc_par_p['area_sim'])

w_g = weight(mc_par_g['emin'], mc_par_g['emax'], mc_par_g['sp_idx'],
crab_par['alpha'], rate_g,
mc_par_g['sim_ev'], crab_par['e0'])

w_p = weight(mc_par_p['emin'], mc_par_p['emax'], mc_par_p['sp_idx'],
proton_par['alpha'], rate_p,
mc_par_p['sim_ev'], proton_par['e0'])
w_g = weight("PowerLaw", mc_par_g['emin'], mc_par_g['emax'], mc_par_g['sp_idx'], rate_g,
mc_par_g['sim_ev'], crab_par)

w_p = weight("PowerLaw", mc_par_p['emin'], mc_par_p['emax'], mc_par_p['sp_idx'], rate_p,
mc_par_p['sim_ev'], proton_par)

if (w_g.unit == u.Unit("sr / s")):
print("You are using diffuse gammas to estimate point-like sensitivity")
print("These results will make no sense")
Expand Down
19 changes: 9 additions & 10 deletions lstchain/mc/tests/test_mc.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,27 @@ def test_diff_sp():

def test_rate():

shape = 'PowerLaw'
emin = 20. # u.GeV
emax = 300.e3 # u.GeV
spectral_index = -3.
e0 = 300. # u.GeV
param = {'f0':1.e-11, 'e0':300, 'alpha': -3}
area = 1.e9
cone = 0
norm = 1.e-11

np.testing.assert_allclose(rate(emin, emax, spectral_index,
cone, area, norm, e0),
np.testing.assert_allclose(rate(shape, emin, emax, param,
cone, area),
337.5, rtol=1e-3)

def test_weight():


shape = 'PowerLaw'
emin = 10. # u.GeV
emax = 50.e3 # u.GeV
sim_sp_idx = -2.
w_sp_idx = -2.6
e0 = 1000. # u.GeV
w_param = {'f0':1.e-11, 'e0':1000, 'alpha': -2.6}
rate = 8.
nev = 1.e6

np.testing.assert_allclose(weight(emin, emax, sim_sp_idx,
w_sp_idx, rate, nev, e0),
np.testing.assert_allclose(weight(shape, emin, emax, sim_sp_idx,
rate, nev, w_param),
8.07e-7, rtol=1e-3)