1
1
import numpy as np
2
2
import astropy .units as u
3
+ from functools import partial
3
4
4
5
POINT_SOURCE_FLUX_UNIT = (1 / u .GeV / u .s / u .m ** 2 ).unit
5
6
FLUX_UNIT = POINT_SOURCE_FLUX_UNIT / u .sr
6
7
8
+ PDG_COSMIC_RAY_FLUX = 1.8e4 * FLUX_UNIT
9
+ PDG_COSMIC_RAY_E_REF = 1 * u .GeV
10
+ PDG_COSMIC_RAY_INDEX = - 2.7
11
+
7
12
8
13
@u .quantity_input
9
14
def random_power (
10
15
spectral_index ,
11
16
e_min : u .TeV ,
12
17
e_max : u .TeV ,
13
18
size ,
14
- e_ref : u .TeV = 1 * u .TeV ,
19
+ e_ref : u .TeV = 1 * u .TeV ,
15
20
) -> u .TeV :
16
21
r'''
17
22
Draw random numbers from a power law distribution
@@ -98,7 +103,7 @@ def power_law_exponential_cutoff(
98
103
e_ref: Quantity[energy]
99
104
The reference energy
100
105
'''
101
- tau = np .exp (energy / e_cutoff )
106
+ tau = np .exp (- energy / e_cutoff )
102
107
return power_law (energy , flux_normalization , spectral_index , e_ref ) * tau
103
108
104
109
@@ -175,63 +180,152 @@ def li_ma_significance(n_on, n_off, alpha=0.2):
175
180
176
181
177
182
@u .quantity_input
178
- def calc_weight_power_to_logparabola (
183
+ def calc_weights_powerlaw (
179
184
energy : u .TeV ,
185
+ obstime : u .hour ,
186
+ n_events ,
187
+ e_min : u .TeV ,
188
+ e_max : u .TeV ,
180
189
simulated_index ,
181
- target_a ,
182
- target_b ,
190
+ scatter_radius : u .m ,
191
+ target_index ,
192
+ flux_normalization : (POINT_SOURCE_FLUX_UNIT , FLUX_UNIT ),
183
193
e_ref : u .TeV = 1 * u .TeV ,
194
+ sample_fraction = 1 ,
195
+ viewcone = None ,
184
196
):
185
197
'''
186
- Reweight simulated events from a simulated power law with
187
- spectral index `spectral_index` to a curved power law with parameters `a` and `b`
198
+ Calculate event weights, so that simulated
199
+ events are reweighted to a physical power law flux
200
+ '''
188
201
189
- phi(E) = phi_0 (E / E_ref)^(a + b * log10(E / E_ref))
202
+ phi_sim = calc_simulated_flux_normalization (
203
+ obstime = obstime ,
204
+ n_events = n_events ,
205
+ e_min = e_min ,
206
+ e_max = e_max ,
207
+ e_ref = e_ref ,
208
+ simulated_index = simulated_index ,
209
+ scatter_radius = scatter_radius ,
210
+ viewcone = viewcone ,
211
+ )
190
212
191
- Parameters
192
- ----------
193
- energy: float or array-like
194
- Energy of the events
195
- simulated_index: float
196
- Spectral index of the simulated power law
197
- target_a: float
198
- Parameter `a` of the target curved power law
199
- target_b: float
200
- Parameter `b` of the target curved power law
213
+ e = (energy / e_ref ).to_value (u .dimensionless_unscaled )
214
+ weights = flux_normalization / phi_sim * e ** (target_index - simulated_index )
215
+ return weights .to (u .dimensionless_unscaled ) / sample_fraction
216
+
217
+
218
+ @u .quantity_input
219
+ def calc_weights_logparabola (
220
+ energy : u .TeV ,
221
+ obstime : u .hour ,
222
+ n_events ,
223
+ e_min : u .TeV ,
224
+ e_max : u .TeV ,
225
+ simulated_index ,
226
+ scatter_radius : u .m ,
227
+ target_a ,
228
+ target_b ,
229
+ flux_normalization : (POINT_SOURCE_FLUX_UNIT , FLUX_UNIT ),
230
+ e_ref : u .TeV = 1 * u .TeV ,
231
+ sample_fraction = 1 ,
232
+ viewcone = None ,
233
+ ):
201
234
'''
202
- return (energy / e_ref ) ** (
203
- target_a + target_b * np .log10 (energy / e_ref ) - simulated_index
235
+ Calculate event weights, so that simulated
236
+ events are reweighted to a physical power law flux
237
+ '''
238
+
239
+ phi_sim = calc_simulated_flux_normalization (
240
+ obstime = obstime ,
241
+ n_events = n_events ,
242
+ e_min = e_min ,
243
+ e_max = e_max ,
244
+ e_ref = e_ref ,
245
+ simulated_index = simulated_index ,
246
+ scatter_radius = scatter_radius ,
247
+ viewcone = viewcone ,
204
248
)
205
249
250
+ e = (energy / e_ref ).to_value (u .dimensionless_unscaled )
251
+ exp = target_a + np .log10 (e ) * target_b - simulated_index
252
+ weights = flux_normalization / phi_sim * e ** exp
253
+ return weights .to (u .dimensionless_unscaled ) / sample_fraction
254
+
206
255
207
256
@u .quantity_input
208
- def calc_weight_change_index (
257
+ def calc_weights_exponential_cutoff (
209
258
energy : u .TeV ,
259
+ obstime : u .hour ,
260
+ n_events ,
261
+ e_min : u .TeV ,
262
+ e_max : u .TeV ,
210
263
simulated_index ,
264
+ scatter_radius : u .m ,
211
265
target_index ,
266
+ target_e_cutoff ,
267
+ flux_normalization : (POINT_SOURCE_FLUX_UNIT , FLUX_UNIT ),
212
268
e_ref : u .TeV = 1 * u .TeV ,
269
+ sample_fraction = 1 ,
270
+ viewcone = None ,
213
271
):
214
272
'''
215
- Reweight simulated events from one power law index to another
273
+ Calculate event weights, so that simulated
274
+ events are reweighted to a physical power law flux with exponential cutoff
275
+ '''
216
276
217
- Parameters
218
- ----------
219
- energy: float or array-like
220
- Energy of the events
221
- simulated_index: float
222
- Spectral index of the simulated power law
223
- target_index: float
224
- Spectral index of the target power law
277
+ phi_sim = calc_simulated_flux_normalization (
278
+ obstime = obstime ,
279
+ n_events = n_events ,
280
+ e_min = e_min ,
281
+ e_max = e_max ,
282
+ e_ref = e_ref ,
283
+ simulated_index = simulated_index ,
284
+ scatter_radius = scatter_radius ,
285
+ viewcone = viewcone ,
286
+ )
287
+
288
+ e = (energy / e_ref ).to_value (u .dimensionless_unscaled )
289
+ tau = np .exp (- energy / target_e_cutoff )
290
+ weights = flux_normalization / phi_sim * e ** (target_index - simulated_index ) * tau
291
+ return weights .to (u .dimensionless_unscaled ) / sample_fraction
292
+
293
+
294
+ def calc_simulated_flux_normalization (
295
+ obstime : u .hour ,
296
+ n_events ,
297
+ e_min : u .TeV ,
298
+ e_max : u .TeV ,
299
+ simulated_index ,
300
+ scatter_radius : u .m ,
301
+ e_ref : u .TeV ,
302
+ viewcone = None ,
303
+ ):
304
+ '''
305
+ Calculate the flux normalization for simulated events drawn
306
+ from a power law for a certain observation time.
225
307
'''
226
- return (energy / e_ref ) ** (target_index - simulated_index )
308
+ if viewcone is not None :
309
+ solid_angle = 2 * np .pi * (1 - np .cos (viewcone )) * u .sr
310
+ else :
311
+ solid_angle = 1
312
+
313
+ A = np .pi * scatter_radius ** 2
314
+
315
+ delta = e_max ** (simulated_index + 1 ) - e_min ** (simulated_index + 1 )
316
+
317
+ nom = (simulated_index + 1 ) * e_ref ** simulated_index * n_events
318
+ denom = (A * obstime * solid_angle ) * delta
319
+
320
+ return nom / denom
227
321
228
322
229
323
@u .quantity_input
230
324
def calc_gamma_obstime (
231
325
n_events ,
232
326
spectral_index ,
233
327
flux_normalization : (FLUX_UNIT , POINT_SOURCE_FLUX_UNIT ),
234
- max_impact : u .m ,
328
+ scatter_radius : u .m ,
235
329
e_min : u .TeV ,
236
330
e_max : u .TeV ,
237
331
e_ref : u .TeV = 1 * u .TeV ,
@@ -267,8 +361,8 @@ def calc_gamma_obstime(
267
361
Spectral index of the simulated power law, including the sign,
268
362
so typically -2.7 or -2
269
363
flux_normalization: float
270
- Flux normalization of the simulated power law
271
- max_impact : Quantity[length]
364
+ Flux normalization of the target power law
365
+ scatter_radius : Quantity[length]
272
366
Maximal simulated impact
273
367
e_min: Quantity[energy]
274
368
Mimimal simulated energy
@@ -280,16 +374,18 @@ def calc_gamma_obstime(
280
374
if spectral_index >= - 1 :
281
375
raise ValueError ('spectral_index must be < -1' )
282
376
283
- numerator = n_events * (spectral_index + 1 )
284
-
285
- A = max_impact ** 2 * np .pi
286
- t1 = A * flux_normalization * e_ref
287
- t2 = (e_max / e_ref )** (spectral_index + 1 )
288
- t3 = (e_min / e_ref )** (spectral_index + 1 )
289
-
290
- denominator = t1 * (t2 - t3 )
377
+ t_ref = 1 * u .hour
378
+ phi_sim = calc_simulated_flux_normalization (
379
+ obstime = t_ref ,
380
+ n_events = n_events ,
381
+ e_min = e_min ,
382
+ e_max = e_max ,
383
+ e_ref = e_ref ,
384
+ simulated_index = spectral_index ,
385
+ scatter_radius = scatter_radius ,
386
+ )
291
387
292
- return numerator / denominator
388
+ return ( t_ref * phi_sim / flux_normalization ). to ( u . hour )
293
389
294
390
295
391
@u .quantity_input
@@ -311,20 +407,20 @@ def power_law_integral(
311
407
res = flux_normalization * e_ref / int_index * e_term
312
408
313
409
if flux_normalization .unit .is_equivalent (FLUX_UNIT ):
314
- return res .to (1 / u . m ** 2 / u . s / u . sr )
315
- return res .to (1 / u . m ** 2 / u . s )
410
+ return res .to (FLUX_UNIT )
411
+ return res .to (POINT_SOURCE_FLUX_UNIT )
316
412
317
413
318
414
@u .quantity_input
319
415
def calc_proton_obstime (
320
416
n_events ,
321
417
spectral_index ,
322
- max_impact : u .m ,
418
+ scatter_radius : u .m ,
323
419
viewcone : u .deg ,
324
420
e_min : u .TeV ,
325
421
e_max : u .TeV ,
326
- flux_normalization : FLUX_UNIT = 1.8e4 * FLUX_UNIT ,
327
- e_ref : u .GeV = 1 * u . GeV ,
422
+ flux_normalization : FLUX_UNIT = PDG_COSMIC_RAY_FLUX ,
423
+ e_ref : u .GeV = PDG_COSMIC_RAY_E_REF ,
328
424
) -> u .s :
329
425
'''
330
426
Calculate the equivalent observation time for a proton montecarlo set
@@ -335,7 +431,7 @@ def calc_proton_obstime(
335
431
Number of simulated events
336
432
spectral_index: float
337
433
Spectral index of the simulated power law
338
- max_impact : float
434
+ scatter_radius : float
339
435
Maximal simulated impact in m
340
436
viewcone: float
341
437
Viewcone in degrees
@@ -348,11 +444,31 @@ def calc_proton_obstime(
348
444
Default value is (29.2) of
349
445
http://pdg.lbl.gov/2016/reviews/rpp2016-rev-cosmic-rays.pdf
350
446
'''
351
- area = np . pi * max_impact ** 2
352
- solid_angle = 2 * np . pi * ( 1 - np . cos ( viewcone )) * u . sr
447
+ if spectral_index >= - 1 :
448
+ raise ValueError ( 'spectral_index must be < -1' )
353
449
354
- expected_integral_flux = power_law_integral (
355
- flux_normalization , spectral_index , e_min , e_max , e_ref
450
+ t_ref = 1 * u .hour
451
+ phi_sim = calc_simulated_flux_normalization (
452
+ obstime = t_ref ,
453
+ n_events = n_events ,
454
+ e_min = e_min ,
455
+ e_max = e_max ,
456
+ e_ref = e_ref ,
457
+ simulated_index = spectral_index ,
458
+ scatter_radius = scatter_radius ,
459
+ viewcone = viewcone ,
356
460
)
357
461
358
- return n_events / area / solid_angle / expected_integral_flux
462
+ return (t_ref * phi_sim / flux_normalization ).to (u .hour )
463
+
464
+
465
+ calc_weights_cosmic_rays = partial (
466
+ calc_weights_powerlaw ,
467
+ target_index = PDG_COSMIC_RAY_INDEX ,
468
+ flux_normalization = PDG_COSMIC_RAY_FLUX ,
469
+ e_ref = PDG_COSMIC_RAY_E_REF ,
470
+ )
471
+ calc_weights_cosmic_rays .__doc__ = '''
472
+ Calculate event weights, so that simulated
473
+ events are reweighted to the PDG cosmic rays spectrum
474
+ '''
0 commit comments