1
|
"""
|
2
|
Published Crab nebula reference spectra.
|
3
|
========================================
|
4
|
|
5
|
The Crab is often used as a standard candle in gamma-ray astronomy.
|
6
|
Statements like "this source has a flux of 10 % Crab" or
|
7
|
"our sensitivity is 2 % Crab" are common.
|
8
|
|
9
|
Here we provide a reference of what the Crab flux actually is according
|
10
|
to several publications.
|
11
|
|
12
|
Really everyone should be using DEFAULT_REF = 'meyer', but actually
|
13
|
e.g. for CTA Konrad uses 'hegra' to state sensitivity in Crab units.
|
14
|
'hess_ecpl' is exceptionally bad and doesn't agree with the current
|
15
|
result we get for the Crab, the cutoff or curvature is at higher energies.
|
16
|
|
17
|
Unless noted otherwise:
|
18
|
* diff_flux @ 1 TeV in units cm^-2 s^-1 TeV^-1
|
19
|
* int_flux > 1 TeV in units cm^-2 s^-1
|
20
|
"""
|
21
|
import logging
|
22
|
import numpy as np
|
23
|
|
24
|
|
25
|
|
26
|
hess_pl = {'diff_flux': 3.45e-11,
|
27
|
'index': 2.63,
|
28
|
'int_flux': 2.26e-11}
|
29
|
|
30
|
|
31
|
|
32
|
hess_ecpl = {'diff_flux': 3.76e-11,
|
33
|
'index': 2.39,
|
34
|
'cutoff': 14.3,
|
35
|
'int_flux': 2.27e-11}
|
36
|
|
37
|
|
38
|
hegra = {'diff_flux': 2.83e-11,
|
39
|
'index': 2.62,
|
40
|
'int_flux': 1.75e-11}
|
41
|
|
42
|
|
43
|
|
44
|
|
45
|
meyer = {'diff_flux': 3.3457e-11,
|
46
|
'index': 2.5362,
|
47
|
'int_flux': 2.0744e-11}
|
48
|
refs = {'meyer': meyer,
|
49
|
'hegra': hegra,
|
50
|
'hess_pl': hess_pl,
|
51
|
'hess_ecpl': hess_ecpl}
|
52
|
|
53
|
DEFAULT_REF = 'meyer'
|
54
|
|
55
|
|
56
|
def meyer(energy, energyFlux=False):
|
57
|
"""Differential Crab flux at a given energy (TeV).
|
58
|
|
59
|
erg cm^-2 s^-1 if energyFlux=True
|
60
|
cm^-1 s^-1 TeV^-1 if energyFlux=False
|
61
|
|
62
|
@see: Meyer et al., 2010arXiv1008.4524M, Appendix D"""
|
63
|
p = np.array([-0.00449161, 0, 0.0473174,
|
64
|
- 0.179475, -0.53616, -10.2708])
|
65
|
log_e = np.log10(np.asarray(energy))
|
66
|
log_f = np.poly1d(p)(log_e)
|
67
|
f = 10 ** log_f
|
68
|
if energyFlux:
|
69
|
return f
|
70
|
else:
|
71
|
erg_to_ev = 624150947960.7719
|
72
|
return 1e-12 * erg_to_ev * f / energy ** 2
|
73
|
|
74
|
|
75
|
def diff_flux(e=1, ref=DEFAULT_REF):
|
76
|
"""Differential Crab flux (cm^-2 s^-1 TeV^-1) at energy e (TeV)
|
77
|
according to some reference publication"""
|
78
|
if ref == 'hegra':
|
79
|
f = hegra['diff_flux']
|
80
|
g = hegra['index']
|
81
|
return f * e ** (-g)
|
82
|
elif ref == 'hess_pl':
|
83
|
f = hess_pl['diff_flux']
|
84
|
g = hess_pl['index']
|
85
|
return f * e ** (-g)
|
86
|
elif ref == 'hess_ecpl':
|
87
|
f = hess_ecpl['diff_flux']
|
88
|
g = hess_ecpl['index']
|
89
|
e_c = hess_ecpl['cutoff']
|
90
|
return f * e ** (-g) * np.exp(-e / e_c)
|
91
|
elif ref == 'meyer':
|
92
|
return meyer(e)
|
93
|
else:
|
94
|
raise ValueError('Unknown ref: %s' % ref)
|
95
|
|
96
|
|
97
|
def int_flux(e1=1, e2=1e4, ref=DEFAULT_REF):
|
98
|
"""Integral Crab flux (cm^-2 s^-1) in energy band e1 to e2 (TeV)
|
99
|
according to some reference publication"""
|
100
|
|
101
|
|
102
|
"""
|
103
|
In [36]: spec.crab.int_flux(0.2, 1e4, ref='hess_ecpl')
|
104
|
[ 2.43196827e-10] [ 2.61476507e-18]
|
105
|
Out[36]: array([ 2.43196827e-10])
|
106
|
|
107
|
In [37]: spec.crab.int_flux(0.2, 1e5, ref='hess_ecpl')
|
108
|
Warning: The algorithm does not converge. Roundoff error is detected
|
109
|
in the extrapolation table. It is assumed that the requested tolerance
|
110
|
cannot be achieved, and that the returned result (if full_output = 1) is
|
111
|
the best which can be obtained.
|
112
|
[ 2.43283459e-10] [ 4.37319063e-10]
|
113
|
Out[37]: array([ 2.43283459e-10])
|
114
|
|
115
|
In [38]: spec.crab.int_flux(0.2, 1e6, ref='hess_ecpl')
|
116
|
[ 6.40098358e-48] [ 1.27271100e-47]
|
117
|
Out[38]: array([ 6.40098358e-48])
|
118
|
"""
|
119
|
from scipy.integrate import quad
|
120
|
|
121
|
|
122
|
e1, e2 = np.asarray(e1, dtype=float), np.asarray(e2, dtype=float)
|
123
|
npoints = e1.size
|
124
|
e1, e2 = e1.reshape(npoints), e2.reshape(npoints)
|
125
|
I, I_err = np.empty_like(e1), np.empty_like(e2)
|
126
|
for ii in range(npoints):
|
127
|
I[ii], I_err[ii] = quad(diff_flux, e1[ii], e2[ii],
|
128
|
(ref), epsabs=1e-20)
|
129
|
return I
|
130
|
|
131
|
|
132
|
def spectral_index(e=1, ref=DEFAULT_REF):
|
133
|
"""Spectral index (positive number) at energy e (TeV)"""
|
134
|
|
135
|
|
136
|
eps = 1 + 1e-3
|
137
|
f1 = diff_flux(e, ref)
|
138
|
f2 = diff_flux(eps * e, ref)
|
139
|
return (np.log10(f1) - np.log10(f2)) / np.log10(eps)
|
140
|
|
141
|
|
142
|
def to_crab(f, apply_mask=True, min=1e-3, max=300):
|
143
|
"""Convert to Crab units"""
|
144
|
f_crab_ref = int_flux(e1=1, ref='meyer')
|
145
|
logging.info('Crab integral flux above 1 TeV according '
|
146
|
'to Meyer et al. model ist {0} cm^-2 s^-1'
|
147
|
''.format(f_crab_ref))
|
148
|
|
149
|
|
150
|
f_crab = 100 * f / 1e4 / f_crab_ref
|
151
|
if apply_mask:
|
152
|
|
153
|
|
154
|
mask = (f_crab < min) | (f_crab > max)
|
155
|
f_crab[mask] = np.nan
|
156
|
return f_crab
|