simmes 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- simmes-0.1.1/LICENSE +7 -0
- simmes-0.1.1/PKG-INFO +10 -0
- simmes-0.1.1/README.md +120 -0
- simmes-0.1.1/setup.cfg +4 -0
- simmes-0.1.1/setup.py +14 -0
- simmes-0.1.1/simmes/GRB.py +408 -0
- simmes-0.1.1/simmes/PLOTS.py +642 -0
- simmes-0.1.1/simmes/RSP.py +337 -0
- simmes-0.1.1/simmes/SPECFUNC.py +337 -0
- simmes-0.1.1/simmes/__init__.py +0 -0
- simmes-0.1.1/simmes/bayesian_block.py +122 -0
- simmes-0.1.1/simmes/fluence.py +31 -0
- simmes-0.1.1/simmes/simulations.py +354 -0
- simmes-0.1.1/simmes/unit_tests/__init__.py +0 -0
- simmes-0.1.1/simmes/unit_tests/test_class_SPECFUNC.py +163 -0
- simmes-0.1.1/simmes/unit_tests/test_package_bayesian_block.py +53 -0
- simmes-0.1.1/simmes/unit_tests/test_package_cosmology.py +41 -0
- simmes-0.1.1/simmes/unit_tests/test_package_det_ang_dependence.py +50 -0
- simmes-0.1.1/simmes/util_packages/__init__.py +0 -0
- simmes-0.1.1/simmes/util_packages/cosmology.py +48 -0
- simmes-0.1.1/simmes/util_packages/datatypes.py +13 -0
- simmes-0.1.1/simmes/util_packages/det_ang_dependence.py +89 -0
- simmes-0.1.1/simmes/util_packages/files-det-ang-dependence/gridnum_imx_imy.txt +32 -0
- simmes-0.1.1/simmes/util_packages/files-det-ang-dependence/pcode-imx-imy.dat +101 -0
- simmes-0.1.1/simmes/util_packages/files-det-ang-dependence/pcode-map.img +11364 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_01.rsp +107 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_02.rsp +116 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_03.rsp +107 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_04.rsp +116 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_05.rsp +114 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_07.rsp +123 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_08.rsp +111 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_09.rsp +113 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_10.rsp +129 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_11.rsp +113 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_12.rsp +111 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_13.rsp +123 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_14.rsp +113 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_15.rsp +119 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_16.rsp +108 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_17.rsp +111 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_18.rsp +108 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_19.rsp +119 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_20.rsp +113 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_21.rsp +123 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_22.rsp +111 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_23.rsp +113 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_24.rsp +129 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_25.rsp +113 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_26.rsp +111 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_27.rsp +123 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_29.rsp +114 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_30.rsp +116 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_31.rsp +107 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_32.rsp +116 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_33.rsp +114 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/grb_110422A_preslew.rsp +109 -0
- simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/grb_160121A_preslew.rsp +128 -1
- simmes-0.1.1/simmes/util_packages/globalconstants.py +17 -0
- simmes-0.1.1/simmes.egg-info/PKG-INFO +10 -0
- simmes-0.1.1/simmes.egg-info/SOURCES.txt +61 -0
- simmes-0.1.1/simmes.egg-info/dependency_links.txt +1 -0
- simmes-0.1.1/simmes.egg-info/top_level.txt +1 -0
simmes-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2024 Michael Moss
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
simmes-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: simmes
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: GRB measurement simulation packages
|
|
5
|
+
Home-page: https://github.com/
|
|
6
|
+
Author: Michael Moss
|
|
7
|
+
Author-email: mikejmoss3@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: gamma-ray bursts
|
|
10
|
+
License-File: LICENSE
|
simmes-0.1.1/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# GRB Measurement Simulations (simmes)
|
|
2
|
+
Author: Mike Moss
|
|
3
|
+
Contact: mikejmoss3@gmail.com
|
|
4
|
+
|
|
5
|
+
This code is pip-installable.
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
This project allows a user to measure the duration and gamma-ray fluence of simulated gamma-ray burst (GRB) prompt observations while taking into consideration observation conditions, such as the angle of the simulated GRB with respect to the detector bore-sight. This code is based on the work of [Moss et al. 2022](https://ui.adsabs.harvard.edu/abs/2022ApJ...927..157M/abstract). This library currently focuses on analysis with the Swift/BAT instrument, but can easily be applied to any coded-aperture mask instrument. Support for non-coded-aperture mask instruments is in development.
|
|
10
|
+
|
|
11
|
+
## Procedure and How-to-Use
|
|
12
|
+
|
|
13
|
+
Here are short descriptions of each file and directory in the project:
|
|
14
|
+
```
|
|
15
|
+
simmes/ # Holds the core packages and classes needed to run the code
|
|
16
|
+
├── GRB.py # Defines a GRB object to store observed and simulated light curve and spectral information
|
|
17
|
+
├── PLOTS.py # Defines the class and methods used for plotting simulation results
|
|
18
|
+
├── RSP.py # Defines the main class this code uses to store response matrices and the associated methods
|
|
19
|
+
├── SPECFUNC.py # Defines the all classes related to spectral functions used in this library to represent GRB spectra
|
|
20
|
+
├── analysis.py # Defines functions to obtain the duration and fluence measurements for many synthetic GRBs
|
|
21
|
+
├── bayesian_block.py # Defines the Bayesian block method to calculate the duration of a GRB from a supplied light curve
|
|
22
|
+
├── simulations.py # Defines all the functions necessary to simulate a GRB prompt emission observation
|
|
23
|
+
├── unit_tests/ # Holds all unit tests
|
|
24
|
+
└── util_packages/ # Holds the support packages and libraries for the main code
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Loading a Template GRB
|
|
28
|
+
First, create a GRB object that will act as a template for our simulations.
|
|
29
|
+
```
|
|
30
|
+
from simmes.GRB import GRB
|
|
31
|
+
|
|
32
|
+
template_grb = GRB()
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Next load a light curve for `template_grb`. Currently, the `GRB` class can load light curves from either .txt files or .fits files. In this example, the Swift/BAT single-channel, 1-second time bin, mask-weighted light curve for GRB 081007 is used[^1].
|
|
36
|
+
```
|
|
37
|
+
template_grb.load_light_curve("grb_081007_1chan_1s.lc", rm_trigtime=True)
|
|
38
|
+
```
|
|
39
|
+
where `rm_trigtime=True` indicates that we subtract the detector trigger time from the time axis of the light curve so that the burst trigger occurs at T=0 s.
|
|
40
|
+
|
|
41
|
+
Since this light curve is associated with a GRB observed at a redshift of `z=0.5295`, the redshift should be included with the GRB
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
template_grb.z = 0.5295
|
|
45
|
+
```
|
|
46
|
+
Alternatively, a rest-frame light curve could have been used for `template_grb` and no redshift would have been needed.
|
|
47
|
+
|
|
48
|
+
Now a spectral function must be defined for the GRB. In this example, a power law spectral function with a spectral index $\alpha = -1$ and $norm = 4$ (note: the normalization energy is set to $e_{norm} = 1$ keV by default[^2])
|
|
49
|
+
```
|
|
50
|
+
from packages.SPECFUNC import PL
|
|
51
|
+
|
|
52
|
+
spectral_function = PL(alpha=-1.,norm=4)
|
|
53
|
+
template_grb.load_specfunc( spectral_function )
|
|
54
|
+
```
|
|
55
|
+
This can be shortened to a single line, like so,
|
|
56
|
+
```
|
|
57
|
+
template_grb.load_specfunc( PL(alpha=-1.,norm=4) )
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Currently, the power law (PL), cut-off power law (CPL), and Band (Band) spectral functions are implemented.
|
|
61
|
+
|
|
62
|
+
[^1]: Light curves and spectral parameters for all Swift/BAT GRBs can be found on the online [Swift/BAT Catalog](https://swift.gsfc.nasa.gov/results/batgrbcat/)
|
|
63
|
+
[^2]: However, the spectral parameters found on the Swift/BAT catalog assume that the normalization energy is 50 keV (see the page 11 of the Third Swift/BAT GRB Catalog, [Lien et al. 2014](https://swift.gsfc.nasa.gov/results/batgrbcat/3rdBATcatalog.pdf))
|
|
64
|
+
|
|
65
|
+
### Simulating A GRB
|
|
66
|
+
Before a synthetic GRB can be simulated, the desired observing condition parameter values should be assigned and a response matrix should be created. The following example will focus on the Swift/BAT instrument, but can rather easily be applied to any coded-aperture mask instrument.
|
|
67
|
+
```
|
|
68
|
+
from simmes.RSP import ResponseMatrix
|
|
69
|
+
|
|
70
|
+
z_p = 1 # redshift to the synthetic GRB
|
|
71
|
+
imx = 0 # x-axis location of the source on the detector plane
|
|
72
|
+
imy = 0 # y-axis location of the source on the detector plane
|
|
73
|
+
ndets = 30000 # number of detectors enable on the detector plane at the time of observation
|
|
74
|
+
|
|
75
|
+
resp = ResponseMatrix() # Make response matrix object instance
|
|
76
|
+
```
|
|
77
|
+
There are many methods in place for loading response matrices. In the example below, a Swift/BAT response matrix will be loaded. The specific response matrix loaded depends on where GRB is located on the instrument detector plane (i.e., specified with the coordinates (imx, imy)). Instead of properly remaking the response matrix each time with the standard BAT tools, 30 response matrices have been stored in `/util_packages/files-swiftBAT-resp-mats/`. The response matrices roughly separate the BAT detector plane into 30 regions (see method from [Lien et al 2014](https://ui.adsabs.harvard.edu/abs/2014ApJ...783...24L/abstract)).
|
|
78
|
+
```
|
|
79
|
+
resp.load_SwiftBAT_resp(imx,imy)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
To simulate a synthetic GRB observation and measure it's duration, import and run the command
|
|
83
|
+
```
|
|
84
|
+
from simmes.simulations import simulate_observation
|
|
85
|
+
|
|
86
|
+
synth_grb = simulate_observation( template_grb, z_p, imx, imy, ndets, resp)
|
|
87
|
+
```
|
|
88
|
+
And there you have it, the `synth_grb` object is a GRB object that has a mask-weighted light curve and spectrum generated from a known template input light curve and spectrum that have been folded through the Swift/BAT response.
|
|
89
|
+
|
|
90
|
+
To obtain a duration measurement of the light curve, the `bayesian_t_blocks` method can be used. To find the T<sub>90</sub> duration, the duration percentage keyword should be set to 90, i.e., `dur_per=90` (note, this is the default value).
|
|
91
|
+
```
|
|
92
|
+
from simmes.bayesian_block import bayesian_t_blocks
|
|
93
|
+
|
|
94
|
+
duration, t_start, fluence = bayesian_t_blocks(synth_grb, dur_per=90)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Simulating Many GRBs
|
|
98
|
+
Repeating the above steps for many observing condition combinations can be tedious, so the `package_analysis` package was developed to perform many simulations based on a given list of parameter combinations. Create a parameter list by defining the specific values of $z$, $imx$, $imy$, and $ndets$ desired,
|
|
99
|
+
```
|
|
100
|
+
from simmes.simulations import make_param_list, many_simulations
|
|
101
|
+
|
|
102
|
+
z_arr = np.array([1,2,3])
|
|
103
|
+
imx_arr = np.array([0,0.5])
|
|
104
|
+
imy_arr = np.array([0,0.5])
|
|
105
|
+
ndets_arr = np.array([30000,20000,10000])
|
|
106
|
+
param_list = make_param_list(z_arr,imx_arr,imy_arr,ndets_arr)
|
|
107
|
+
```
|
|
108
|
+
`param_list` now contains a list of every unique combination of parameters possible from the four parameter lists `z_arr`, `imx_arr`, `imy_arr`, and `ndets_arr`.
|
|
109
|
+
|
|
110
|
+
Now, call the `many_simulations()` method. This requires specifying a template GRB that holds a user-defined light curve and spectral function, the parameter combination list that was just created, and a number of trials to simulate each parameter combination for.
|
|
111
|
+
```
|
|
112
|
+
trials = 10
|
|
113
|
+
sim_results = many_simulations(template_grb, param_list, trials)
|
|
114
|
+
```
|
|
115
|
+
Three important keywords should be considered when running `many_simulations()` that are default to `False`. One, if only the average duration of each parameter combination is wanted, then set `ret_ave=True`. Second, to keep a single light curve example for each parameter combination, set `keep_synth_grbs=True`. When setting `keep_synth_grbs` to `True`, a second output variable is required to hold the list of returned list of simulated GRB objects. Together this looks like
|
|
116
|
+
```
|
|
117
|
+
sim_results = many_simulations(template_grb, param_list, trials, ret_ave=True, keep_synth_grbs=True)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Plotting Simulation Results
|
simmes-0.1.1/setup.cfg
ADDED
simmes-0.1.1/setup.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name='simmes',
|
|
5
|
+
version='0.1.1',
|
|
6
|
+
description='GRB measurement simulation packages',
|
|
7
|
+
packages=find_packages(include=["simmes","simmes.*"]),
|
|
8
|
+
package_data={"":["util_packages/files-det-ang-dependence/*","util_packages/files-swiftBAT-resp-mats/*"]},
|
|
9
|
+
license="MIT",
|
|
10
|
+
keywords="gamma-ray bursts",
|
|
11
|
+
author='Michael Moss',
|
|
12
|
+
author_email='mikejmoss3@gmail.com',
|
|
13
|
+
url='https://github.com/',
|
|
14
|
+
)
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Mike Moss
|
|
3
|
+
Contact: mikejmoss3@gmail.com
|
|
4
|
+
|
|
5
|
+
Defines a GRB object to store observed and simulated light curve and spectral information.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from astropy.io import fits
|
|
11
|
+
import copy
|
|
12
|
+
|
|
13
|
+
from simmes.SPECFUNC import SPECFUNC
|
|
14
|
+
from simmes.bayesian_block import bayesian_t_blocks
|
|
15
|
+
from simmes.util_packages.cosmology import lum_dis, k_corr
|
|
16
|
+
import simmes.util_packages.globalconstants as gc
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class GRB(object):
|
|
20
|
+
"""
|
|
21
|
+
GRB class used to store observations information for observed and simulated GRBs
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
----------
|
|
25
|
+
grbname : string
|
|
26
|
+
Name of the GRB
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
def __init__(self,grbname=None,z=0,imx=0,imy=0,
|
|
30
|
+
T100_dur=None,T100_start=None,
|
|
31
|
+
spectrum=None,light_curve_fn=None):
|
|
32
|
+
|
|
33
|
+
# Assign this instance's parameters
|
|
34
|
+
self.grbname = grbname
|
|
35
|
+
self.z = z
|
|
36
|
+
self.imx, self.imy = imx, imy
|
|
37
|
+
self.T100_dur, self.T100_start = T100_dur, T100_start
|
|
38
|
+
|
|
39
|
+
self.light_curve = None # Currently loaded light curve
|
|
40
|
+
self.specfunc = None # Currently loaded spectrum
|
|
41
|
+
self.spectrafuncs = np.zeros(shape=0,dtype=[("TSTART",float),("TEND",float),("SPECFUNC",SPECFUNC)]) # Time resolved spectrum array
|
|
42
|
+
|
|
43
|
+
# Set light curve of GRB if a light curve file is supplied
|
|
44
|
+
if light_curve_fn is not None:
|
|
45
|
+
self.load_light_curve(light_curve_fn)
|
|
46
|
+
# Set spectrum of GRB if a spectrum object is supplied
|
|
47
|
+
if spectrum is not None:
|
|
48
|
+
self.spectrum = spectrum
|
|
49
|
+
|
|
50
|
+
# Duration information (will remain None until a duration finding algorithm is run on the burst)
|
|
51
|
+
self.dur_per = None
|
|
52
|
+
self.ncp_prior = None
|
|
53
|
+
self.duration, self.t_start, self.phot_fluence = None, None, None
|
|
54
|
+
|
|
55
|
+
def __copy__(self):
|
|
56
|
+
cls = self.__class__
|
|
57
|
+
result = cls.__new__(cls)
|
|
58
|
+
result.__dict__.update(self.__dict__)
|
|
59
|
+
return result
|
|
60
|
+
|
|
61
|
+
def __deepcopy__(self, memo):
|
|
62
|
+
cls = self.__class__
|
|
63
|
+
result = cls.__new__(cls)
|
|
64
|
+
memo[id(self)] = result
|
|
65
|
+
for k, v in self.__dict__.items():
|
|
66
|
+
setattr(result, k, copy.deepcopy(v, memo))
|
|
67
|
+
return result
|
|
68
|
+
|
|
69
|
+
def copy(self):
|
|
70
|
+
return copy.deepcopy(self)
|
|
71
|
+
|
|
72
|
+
def deepcopy(self):
|
|
73
|
+
return copy.deepcopy(self)
|
|
74
|
+
|
|
75
|
+
def set_duration(self, duration, t_start, phot_fluence=None, dur_per=None, ncp_prior=None):
|
|
76
|
+
"""
|
|
77
|
+
Method to set the
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
self.duration = duration
|
|
81
|
+
self.t_start = t_start
|
|
82
|
+
if phot_fluence is not None:
|
|
83
|
+
self.phot_fluence = phot_fluence
|
|
84
|
+
if dur_per is not None:
|
|
85
|
+
self.dur_per = dur_per
|
|
86
|
+
if ncp_prior is not None:
|
|
87
|
+
self.ncp_prior = ncp_prior
|
|
88
|
+
|
|
89
|
+
def get_duration(self, dur_per=90, ncp_prior=20):
|
|
90
|
+
"""
|
|
91
|
+
Method to get the duration of the lightcurve using a Bayesian block algorithm
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
# If the same duration percentage and ncp_prior are called for, return the current duration information
|
|
95
|
+
if (self.dur_per == dur_per) and (self.ncp_prior == ncp_prior):
|
|
96
|
+
return self.duration, self.t_start, self.phot_fluence
|
|
97
|
+
else:
|
|
98
|
+
# Otherwise calculate new duration information
|
|
99
|
+
self.dur_per = dur_per
|
|
100
|
+
self.ncp_prior = ncp_prior
|
|
101
|
+
self.duration, self.t_start, self.phot_fluence = bayesian_t_blocks(self,dur_per=dur_per,ncp_prior=ncp_prior)
|
|
102
|
+
|
|
103
|
+
return self.duration, self.t_start, self.phot_fluence
|
|
104
|
+
|
|
105
|
+
def get_photon_fluence(self,dur_per=90,tmin=None,tmax=None):
|
|
106
|
+
"""
|
|
107
|
+
Method to get the photon fluence in the time interval defined by the duration percentage
|
|
108
|
+
"""
|
|
109
|
+
if (tmin is not None) and (tmax is not None):
|
|
110
|
+
return np.sum(self.light_curve['RATE'][np.argmax(tmin <= self.light_curve['TIME']):np.argmax(self.light_curve['TIME'] >= tmax)]) * self.dt
|
|
111
|
+
else:
|
|
112
|
+
self.get_duration(dur_per=dur_per)
|
|
113
|
+
return np.sum(self.light_curve['RATE'][np.argmax(self.t_start <= self.light_curve['TIME']):np.argmax(self.light_curve['TIME'] >= (self.t_start + self.duration))]) * self.dt
|
|
114
|
+
|
|
115
|
+
def get_ave_photon_flux(self,dur_per=90,tmin=None,tmax=None):
|
|
116
|
+
"""
|
|
117
|
+
Method to get the average photon flux in the T100 interval
|
|
118
|
+
"""
|
|
119
|
+
if (tmin is not None) and (tmax is not None):
|
|
120
|
+
return self.get_photon_fluence(tmin=tmin,tmax=tmax)/(tmax-tmin)
|
|
121
|
+
else:
|
|
122
|
+
self.get_duration(dur_per=dur_per)
|
|
123
|
+
return self.get_photon_fluence(dur_per=dur_per)/self.duration
|
|
124
|
+
|
|
125
|
+
def load_specfunc(self, specfunc, intervals=None):
|
|
126
|
+
"""
|
|
127
|
+
Method to load a spectrum
|
|
128
|
+
|
|
129
|
+
Attributes:
|
|
130
|
+
----------
|
|
131
|
+
specfunc : SPECFUNC
|
|
132
|
+
Spectrum function object
|
|
133
|
+
intervals : 2-tuple or 2-tuple list
|
|
134
|
+
Used to indicate the start and stop time of a time-resolved spectrum. If None is given, a time-average spectrum is assumed.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
# Time resolved spectrum
|
|
138
|
+
if intervals is not None:
|
|
139
|
+
# Check if more than one time-resolved spectral function was given
|
|
140
|
+
if hasattr(specfunc, '__len__'):
|
|
141
|
+
if (len(specfunc) != len(intervals) ):
|
|
142
|
+
print("Please provide the same number of spectral functions and time intervals.")
|
|
143
|
+
return 1;
|
|
144
|
+
|
|
145
|
+
for i in range(len(specfunc)):
|
|
146
|
+
self._load_time_res_sec(specfunc[i], intervals[i])
|
|
147
|
+
else:
|
|
148
|
+
# Only a single time-resolved spectrum was loaded
|
|
149
|
+
self._load_time_res_sec(specfunc, intervals)
|
|
150
|
+
|
|
151
|
+
else:
|
|
152
|
+
# This is a time averaged spectrum
|
|
153
|
+
self.specfunc = specfunc
|
|
154
|
+
|
|
155
|
+
return 0;
|
|
156
|
+
|
|
157
|
+
def _load_time_res_sec(self, specfunc, intervals):
|
|
158
|
+
"""
|
|
159
|
+
Method to load a spectrum for a particular time interval
|
|
160
|
+
|
|
161
|
+
Attributes:
|
|
162
|
+
----------
|
|
163
|
+
specfunc : SPECFUNC
|
|
164
|
+
Spectrum function object
|
|
165
|
+
intervals : 2-tuple
|
|
166
|
+
Used to indicate the start and stop time of a time-resolved spectrum.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
# Check if this is the first loaded spectrum
|
|
170
|
+
if len(self.spectrafuncs) == 0:
|
|
171
|
+
self.spectrafuncs = np.insert(self.spectrafuncs, 0, (intervals[0], intervals[1], specfunc))
|
|
172
|
+
return 0;
|
|
173
|
+
else:
|
|
174
|
+
# If not, find the index where to insert this spectrum (according to the time)
|
|
175
|
+
for i in range(len(self.spectrafuncs)):
|
|
176
|
+
if self.spectrafuncs[i]['TSTART'] > intervals[0]:
|
|
177
|
+
# Insert the new spectrum
|
|
178
|
+
self.spectrafuncs = np.insert(self.spectrafuncs, i, (intervals[0], intervals[1], specfunc) )
|
|
179
|
+
return 0;
|
|
180
|
+
# If the new spectrum is the last to start, append it to the end
|
|
181
|
+
self.spectrafuncs = np.insert(self.spectrafuncs, len(self.spectrafuncs), (intervals[0], intervals[1], specfunc))
|
|
182
|
+
return 0;
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def make_spectrum(self, emin, emax, num_bins = None, spec_num=None):
|
|
186
|
+
"""
|
|
187
|
+
Method to evaluate the spectrum over the defined energy interval using the GRB object's spectral model and parameters
|
|
188
|
+
|
|
189
|
+
Attributes:
|
|
190
|
+
----------
|
|
191
|
+
emin, emax : float, float
|
|
192
|
+
Defines the lower and upper bounds of the energy interval over which to evaluate the spectrum
|
|
193
|
+
num_bins : int
|
|
194
|
+
Number of energy bins to use, default is 10*log(emax/emin)
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
if num_bins is None:
|
|
198
|
+
num_bins = int(np.log10(emax/emin)*20)
|
|
199
|
+
|
|
200
|
+
if spec_num is None:
|
|
201
|
+
specfunc = self.specfunc
|
|
202
|
+
else:
|
|
203
|
+
specfunc = self.spectrafuncs[spec_num]['SPECFUNC']
|
|
204
|
+
|
|
205
|
+
spectrum = specfunc.make_spectrum(emin,emax,num_bins)
|
|
206
|
+
|
|
207
|
+
return spectrum
|
|
208
|
+
|
|
209
|
+
def load_light_curve(self, file_name, t_offset=0,rm_trigtime=False,T100_dur=None,T100_start=None,det_area=None):
|
|
210
|
+
"""
|
|
211
|
+
Method to load a light curve from either a .fits or .txt file
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
# Check if this is a fits file or a text file
|
|
215
|
+
if file_name.endswith(".lc") or file_name.endswith(".fits"):
|
|
216
|
+
tmp_light_curve = fits.getdata(file_name,ext=1)
|
|
217
|
+
self.light_curve = np.zeros(shape=len(tmp_light_curve),dtype=[('TIME',float),('RATE',float),('UNC',float)])
|
|
218
|
+
self.light_curve['TIME'] = tmp_light_curve['TIME']
|
|
219
|
+
if rm_trigtime is True:
|
|
220
|
+
self.light_curve['TIME']-=fits.getheader(file_name,ext=0)['TRIGTIME']
|
|
221
|
+
self.light_curve['RATE'] = tmp_light_curve['RATE']
|
|
222
|
+
self.light_curve['UNC'] = tmp_light_curve['ERROR']
|
|
223
|
+
elif file_name.endswith(".txt"):
|
|
224
|
+
self.light_curve = np.genfromtxt(file_name,dtype=[('TIME',float),('RATE',float),('UNC',float)])
|
|
225
|
+
|
|
226
|
+
# Time bin size
|
|
227
|
+
self.dt = (self.light_curve['TIME'][1] - self.light_curve['TIME'][0])
|
|
228
|
+
|
|
229
|
+
# Correct for the size of a detector
|
|
230
|
+
if det_area is not None:
|
|
231
|
+
self.light_curve['RATE'] /= det_area
|
|
232
|
+
self.light_curve['UNC'] /= det_area
|
|
233
|
+
|
|
234
|
+
if t_offset != 0:
|
|
235
|
+
self.light_curve['TIME'] -= t_offset
|
|
236
|
+
if T100_dur is not None:
|
|
237
|
+
self.T100_dur = T100_dur
|
|
238
|
+
if T100_start is not None:
|
|
239
|
+
self.T100_start = T100_start
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def cut_light_curve(self, tmin=None, tmax=None):
|
|
243
|
+
"""
|
|
244
|
+
Method to cut light curve to only the selected interval.
|
|
245
|
+
If tmin (tmax) is left as None, the beginning (end) of the light curve is assumed.
|
|
246
|
+
|
|
247
|
+
Attributes:
|
|
248
|
+
-----------
|
|
249
|
+
tmin : float
|
|
250
|
+
The minimum time of the interval to be removed.
|
|
251
|
+
tmax : float
|
|
252
|
+
The maximum time of the interval to be removed.
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
if tmin is None:
|
|
256
|
+
tmin = self.light_curve['TIME'][0]
|
|
257
|
+
if tmax is None:
|
|
258
|
+
tmax = self.light_curve['TIME'][-1]
|
|
259
|
+
|
|
260
|
+
tmin_ind = np.argmax(self.light_curve['TIME']>=tmin) # new index at T_min
|
|
261
|
+
tmax_ind = np.argmax(self.light_curve['TIME']>=tmax) # new index at T_max
|
|
262
|
+
self.light_curve = self.light_curve[tmin_ind:tmax_ind]
|
|
263
|
+
|
|
264
|
+
def zero_light_curve_selection(self, tmin=None, tmax=None):
|
|
265
|
+
"""
|
|
266
|
+
Method to set the counts (and uncertainty) within the selected interval of the light curve to zero.
|
|
267
|
+
If tmin (tmax) is left as None, the beginning (end) of the light curve is assumed.
|
|
268
|
+
|
|
269
|
+
Attributes:
|
|
270
|
+
-----------
|
|
271
|
+
tmin : float
|
|
272
|
+
The minimum time of the interval to be removed.
|
|
273
|
+
tmax : float
|
|
274
|
+
The maximum time of the interval to be removed.
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
if tmin is None:
|
|
278
|
+
tmin = self.light_curve['TIME'][0]
|
|
279
|
+
if tmax is None:
|
|
280
|
+
tmax = self.light_curve['TIME'][-1]
|
|
281
|
+
|
|
282
|
+
self.light_curve['RATE'][np.argmax(tmin <= self.light_curve['TIME']):np.argmax(self.light_curve['TIME'] >= tmax)] *= 0
|
|
283
|
+
self.light_curve['UNC'][np.argmax(tmin <= self.light_curve['TIME']):np.argmax(self.light_curve['TIME'] >= tmax)] *= 0
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def move_to_new_frame(self, z_o, z_p, emin=gc.bol_lum[0],emax=gc.bol_lum[1],rm_bgd_sig=False):
|
|
287
|
+
"""
|
|
288
|
+
Method to shift the GRB light curve and spectra from a frame at z_o to a frame at z_p
|
|
289
|
+
|
|
290
|
+
if z_p = 0, this is the same as shifting the GRB to the source frame and the light curve returned will be the bolometric one.
|
|
291
|
+
If z_o = 0, it is assumed the GRB is in the rest frame
|
|
292
|
+
|
|
293
|
+
Attributes:
|
|
294
|
+
----------
|
|
295
|
+
z_o : float
|
|
296
|
+
Current redshift of the GRB
|
|
297
|
+
z_p : float
|
|
298
|
+
Redshift to shift the GRB to
|
|
299
|
+
emin, max : float, float
|
|
300
|
+
Spectrum energy band minimum and maximum
|
|
301
|
+
rm_bgd_sig : bool
|
|
302
|
+
Indicates whether or not to remove the background signal outside the T100 range should be removed.
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
if z_o == z_p:
|
|
306
|
+
# No chage in the light curve or spectrum.
|
|
307
|
+
return;
|
|
308
|
+
|
|
309
|
+
# Update redshift class attribute
|
|
310
|
+
self.z = z_p
|
|
311
|
+
|
|
312
|
+
# Remove background signal outside of T100 (requires that the T100 start time and duration were defined)
|
|
313
|
+
if rm_bgd_sig is True:
|
|
314
|
+
inds = np.where( (self.light_curve['TIME'] > self.T100_start) & (self.light_curve['TIME'] < (self.T100_start+self.T100_dur)) )
|
|
315
|
+
new_light_curve = np.zeros(shape=len(self.light_curve))
|
|
316
|
+
new_light_curve[inds] = self.light_curve[inds]
|
|
317
|
+
|
|
318
|
+
# Calculate k_correction factor
|
|
319
|
+
kcorr = k_corr(self.specfunc, z_o, emin, emax)/k_corr(self.specfunc, z_p, emin, emax)
|
|
320
|
+
# Move spectral function to z_p frame by correcting E_peak or temperature by the redshift (if spectral function has a peak energy or temperature)
|
|
321
|
+
for i, (key, val) in enumerate(self.specfunc.params.items()):
|
|
322
|
+
if key == "ep":
|
|
323
|
+
self.specfunc.params[key] *= (1+z_o)/(1+z_p)
|
|
324
|
+
if key == "temp":
|
|
325
|
+
self.specfunc.params[key] *= (1+z_o)/(1+z_p)
|
|
326
|
+
|
|
327
|
+
##
|
|
328
|
+
# Shift Light Curve
|
|
329
|
+
##
|
|
330
|
+
|
|
331
|
+
# Apply distance corrections to flux values (See Bloom, Frail, and Sari 2001 Equation 4)
|
|
332
|
+
dis_corr_to_z_o = 1.
|
|
333
|
+
if z_o != 0:
|
|
334
|
+
dis_corr_to_z_o = 4 * np.pi * np.power(lum_dis(z_o), 2.) / (1+z_o)
|
|
335
|
+
|
|
336
|
+
dis_corr_to_z_p = 1.
|
|
337
|
+
if z_p != 0:
|
|
338
|
+
dis_corr_to_z_p = 4 * np.pi * np.power(lum_dis(z_p), 2.) / (1+z_p)
|
|
339
|
+
|
|
340
|
+
self.light_curve['RATE'] = self.light_curve['RATE'] * kcorr * dis_corr_to_z_o / dis_corr_to_z_p
|
|
341
|
+
self.light_curve['UNC'] = self.light_curve['UNC'] * kcorr * dis_corr_to_z_o / dis_corr_to_z_p
|
|
342
|
+
|
|
343
|
+
# Apply time-dilation to light curve (i.e., correct the time binning)
|
|
344
|
+
# Calculate the start and stop times of the flux light curve in the z_p frame.
|
|
345
|
+
tpstart = self.light_curve['TIME'][0]*(1+z_p)/(1+z_o)
|
|
346
|
+
tpend = self.light_curve['TIME'][-1]*(1+z_p)/(1+z_o)
|
|
347
|
+
|
|
348
|
+
# Bin size of the light curve curve
|
|
349
|
+
bin_size = (self.light_curve['TIME'][1] - self.light_curve['TIME'][0])
|
|
350
|
+
# Create a time axis from tpstart to tpend with proper bin size
|
|
351
|
+
tmp_time_arr = np.arange(tpstart, tpend+bin_size, bin_size)
|
|
352
|
+
|
|
353
|
+
# Create an array to store the flux light curve in the z_p frame
|
|
354
|
+
flux_lc_at_z_p = np.zeros(shape=len(tmp_time_arr),dtype=([("TIME",float),("RATE",float)]))
|
|
355
|
+
flux_lc_at_z_p['TIME'] = tmp_time_arr
|
|
356
|
+
|
|
357
|
+
# Temporary light curve to store z_p frame light curve
|
|
358
|
+
tmp_light_curve = np.zeros(shape=len(tmp_time_arr),dtype = [("TIME",float),("RATE",float),("UNC",float)])
|
|
359
|
+
tmp_light_curve['TIME'] = tmp_time_arr
|
|
360
|
+
|
|
361
|
+
# We must correct for time dilation by binning the flux into z_p frame time bins
|
|
362
|
+
if z_o > z_p:
|
|
363
|
+
# The light curve must be squeezed
|
|
364
|
+
|
|
365
|
+
# For each time bin of the z_p light curve curve:
|
|
366
|
+
for i in range(len(tmp_light_curve)-1):
|
|
367
|
+
# What time bin is this?
|
|
368
|
+
curr_time_bin = tmp_light_curve['TIME'][i]
|
|
369
|
+
|
|
370
|
+
# Find the indices of the z_o frame light curve where the time axis is encompassed by curr_time_bin*(1+z_o)/(1+z_p) and (1+curr_time_bin)*(1+z_o)/(1+z_p)
|
|
371
|
+
argstart = np.argmax(self.light_curve['TIME']>=curr_time_bin*(1+z_o)/(1+z_p))
|
|
372
|
+
argend = np.argmax(self.light_curve['TIME']>=(curr_time_bin+bin_size)*(1+z_o)/(1+z_p))
|
|
373
|
+
|
|
374
|
+
# Grab the total sum of the distance corrected flux within these time bins.
|
|
375
|
+
tmp_light_curve['RATE'][i] = np.sum(self.light_curve['RATE'][argstart:argend])
|
|
376
|
+
tmp_light_curve['UNC'][i] = np.sum(np.power(self.light_curve['UNC'][argstart:argend],2)) # Add uncertainties in quadrature
|
|
377
|
+
else:
|
|
378
|
+
# z_p > z_o, the light curve must be stretched
|
|
379
|
+
|
|
380
|
+
# For each time bin of the z_o light curve:
|
|
381
|
+
for i in range(len(self.light_curve)-1):
|
|
382
|
+
# What time bin is this?
|
|
383
|
+
curr_time_bin = self.light_curve['TIME'][i]
|
|
384
|
+
|
|
385
|
+
# Grab the distance corrected flux from the z_o. This flux must be distributed across multiple time bins.
|
|
386
|
+
curr_flux_to_distribute = self.light_curve['RATE'][i]
|
|
387
|
+
curr_flux_unc_to_distribute = self.light_curve['UNC'][i]
|
|
388
|
+
|
|
389
|
+
# Find the indices of z_p light curve where the time axis encompasses curr_time_bin*(1+z_p)/(1+z_o) and (curr_time_bin+time_bin_size)*(1+z_p)/(1+z_o)
|
|
390
|
+
argstart = np.argmax(tmp_light_curve['TIME']>=(curr_time_bin-bin_size)*(1+z_p)/(1+z_o))
|
|
391
|
+
argend = np.argmax(tmp_light_curve['TIME']>=(curr_time_bin+bin_size)*(1+z_p)/(1+z_o))
|
|
392
|
+
|
|
393
|
+
# How many time bins are within the time interval of curr_time_bin*(1+z_p)/(1+z_o) and (1+curr_time_bin)*(1+z_p)/(1+z_o) ?
|
|
394
|
+
# We need to know the number of time bins because we must divide the curr_flux_to_distribute across each time bin in the z_p frame
|
|
395
|
+
# that came from the (curr_time_bin : 1+curr_time_bin) time interval in the z_o frame.
|
|
396
|
+
num_new_time_bins = len(tmp_light_curve['TIME'][argstart:argend])
|
|
397
|
+
if num_new_time_bins == 0:
|
|
398
|
+
argend = argstart+1
|
|
399
|
+
num_new_time_bins = 1
|
|
400
|
+
|
|
401
|
+
# For the bins between (argstart:argend) assign the flux value of curr_flux_to_distribute/num_new_time_bins
|
|
402
|
+
tmp_light_curve['RATE'][argstart:argend] = tmp_light_curve['RATE'][argstart:argend] + np.ones(shape=num_new_time_bins)*(curr_flux_to_distribute/num_new_time_bins)
|
|
403
|
+
tmp_light_curve['UNC'][argstart:argend] = np.sqrt(tmp_light_curve['UNC'][argstart:argend]**2 + curr_flux_unc_to_distribute**2)
|
|
404
|
+
|
|
405
|
+
# Set the light curve to the distance corrected light curve
|
|
406
|
+
self.light_curve = tmp_light_curve
|
|
407
|
+
|
|
408
|
+
return;
|