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.
Files changed (63) hide show
  1. simmes-0.1.1/LICENSE +7 -0
  2. simmes-0.1.1/PKG-INFO +10 -0
  3. simmes-0.1.1/README.md +120 -0
  4. simmes-0.1.1/setup.cfg +4 -0
  5. simmes-0.1.1/setup.py +14 -0
  6. simmes-0.1.1/simmes/GRB.py +408 -0
  7. simmes-0.1.1/simmes/PLOTS.py +642 -0
  8. simmes-0.1.1/simmes/RSP.py +337 -0
  9. simmes-0.1.1/simmes/SPECFUNC.py +337 -0
  10. simmes-0.1.1/simmes/__init__.py +0 -0
  11. simmes-0.1.1/simmes/bayesian_block.py +122 -0
  12. simmes-0.1.1/simmes/fluence.py +31 -0
  13. simmes-0.1.1/simmes/simulations.py +354 -0
  14. simmes-0.1.1/simmes/unit_tests/__init__.py +0 -0
  15. simmes-0.1.1/simmes/unit_tests/test_class_SPECFUNC.py +163 -0
  16. simmes-0.1.1/simmes/unit_tests/test_package_bayesian_block.py +53 -0
  17. simmes-0.1.1/simmes/unit_tests/test_package_cosmology.py +41 -0
  18. simmes-0.1.1/simmes/unit_tests/test_package_det_ang_dependence.py +50 -0
  19. simmes-0.1.1/simmes/util_packages/__init__.py +0 -0
  20. simmes-0.1.1/simmes/util_packages/cosmology.py +48 -0
  21. simmes-0.1.1/simmes/util_packages/datatypes.py +13 -0
  22. simmes-0.1.1/simmes/util_packages/det_ang_dependence.py +89 -0
  23. simmes-0.1.1/simmes/util_packages/files-det-ang-dependence/gridnum_imx_imy.txt +32 -0
  24. simmes-0.1.1/simmes/util_packages/files-det-ang-dependence/pcode-imx-imy.dat +101 -0
  25. simmes-0.1.1/simmes/util_packages/files-det-ang-dependence/pcode-map.img +11364 -0
  26. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_01.rsp +107 -0
  27. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_02.rsp +116 -0
  28. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_03.rsp +107 -0
  29. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_04.rsp +116 -0
  30. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_05.rsp +114 -0
  31. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_07.rsp +123 -0
  32. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_08.rsp +111 -0
  33. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_09.rsp +113 -0
  34. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_10.rsp +129 -0
  35. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_11.rsp +113 -0
  36. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_12.rsp +111 -0
  37. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_13.rsp +123 -0
  38. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_14.rsp +113 -0
  39. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_15.rsp +119 -0
  40. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_16.rsp +108 -0
  41. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_17.rsp +111 -0
  42. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_18.rsp +108 -0
  43. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_19.rsp +119 -0
  44. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_20.rsp +113 -0
  45. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_21.rsp +123 -0
  46. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_22.rsp +111 -0
  47. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_23.rsp +113 -0
  48. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_24.rsp +129 -0
  49. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_25.rsp +113 -0
  50. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_26.rsp +111 -0
  51. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_27.rsp +123 -0
  52. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_29.rsp +114 -0
  53. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_30.rsp +116 -0
  54. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_31.rsp +107 -0
  55. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_32.rsp +116 -0
  56. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/BAT_alldet_grid_33.rsp +114 -0
  57. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/grb_110422A_preslew.rsp +109 -0
  58. simmes-0.1.1/simmes/util_packages/files-swiftBAT-resp-mats/grb_160121A_preslew.rsp +128 -1
  59. simmes-0.1.1/simmes/util_packages/globalconstants.py +17 -0
  60. simmes-0.1.1/simmes.egg-info/PKG-INFO +10 -0
  61. simmes-0.1.1/simmes.egg-info/SOURCES.txt +61 -0
  62. simmes-0.1.1/simmes.egg-info/dependency_links.txt +1 -0
  63. 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
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
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;