jaxspec 0.0.3__py3-none-any.whl → 0.0.4__py3-none-any.whl

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.
@@ -0,0 +1,334 @@
1
+ SIMPLE = T / file does conform to FITS standard BITPIX = 8 / number of bits per data pixel NAXIS = 0 / number of data axes EXTEND = T / FITS dataset may contain extensions COMMENT FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H END XTENSION= 'BINTABLE' / binary table extension BITPIX = 8 / 8-bit bytes NAXIS = 2 / 2-dimensional binary table NAXIS1 = 12 / width of table in bytes NAXIS2 = 4096 / number of rows in table PCOUNT = 0 / size of special data area GCOUNT = 1 / one data group (required keyword) TFIELDS = 4 / number of fields in each row TTYPE1 = 'CHANNEL ' / label for field 1 TFORM1 = 'J ' / data format of field: 4-byte INTEGER TTYPE2 = 'COUNTS ' / label for field 2 TFORM2 = 'J ' / data format of field: 4-byte INTEGER TTYPE3 = 'QUALITY ' / label for field 3 TFORM3 = 'I ' / data format of field: 2-byte INTEGER TTYPE4 = 'GROUPING' / label for field 4 TFORM4 = 'I ' / data format of field: 2-byte INTEGER EXTNAME = 'SPECTRUM' / name of this binary table extension HDUCLASS= 'OGIP ' / format conforms to OGIP standard HDUCLAS1= 'SPECTRUM' / PHA dataset (OGIP memo OGIP-92-007) HDUVERS = '1.2.1 ' / Version of format (OGIP memo OGIP-92-007a) TELESCOP= 'XMM ' / mission/satellite name INSTRUME= 'EPN ' / instrument name DETNAM = 'UNKNOWN ' / detector name FILTER = 'Medium ' / filter name STOKESPR= 'UNKNOWN ' / Stokes parameter(s) STOKESWT= 'UNKNOWN ' / Stokes weighting CHANTYPE= 'PI ' / channel type (PHA, PI etc) DATE = '2024-03-20T11:51:55' / file creation date (YYYY-MM-DDThh:mm:ss UT) HISTORY Fake data file created by XSPEC version: 12.13.1 "fakeit" command FKSRC001= 'cutoffpl' FKRSP001= 'PN.rmf ' FKARF001= 'PN.arf ' RESPFILE= 'PN.rmf ' / associated redistrib matrix filename ANCRFILE= 'PN.arf ' / associated ancillary response filename CORRFILE= ' ' / associated correction filename CORRSCAL= 1. / correction file scaling factor BACKFILE= ' ' / associated background filename EXPOSURE= 10000. / exposure (in seconds) TLMIN1 = 0 / Lowest legal channel number TLMAX1 = 4095 / Highest legal channel number DETCHANS= 4096 / total number possible channels POISSERR= T / Pois. err assumed ? AREASCAL= 1. / area scaling factor BACKSCAL= 1. / background file scaling factor END �!�x��� ��a��$8 0�
2
+ ;{ D- L�
3
+ �����������8������<��H�����������~�������#��|�����n�����������������/�������X��"��_��i��-�� �����Z��������������������N�����q��<�����q��������������_��������������������A�����������a��������Y��2�����n�����d��������!���?�_�` ��
4
+ �� �+ �+
5
+ &�'��(��)��*�+�<,�-�Q.��/��0��1�02�3�4�^5��6�[7�8�(9�a:�H;~�<�=~>}??|c@|A|8B{�C{mDy�E{�F{HGyHymIy�JxtKy�Ly Mx�Nw�Ow�PxRQw�RxSvTv�UvtVu�WuXXv4Yu�ZuY[u�\u�]t�^t�_t,`s�ar�bqNcr�drBeq�fp�gpxhn�im�jm�kk�ll�mk�ni
6
+ oipg�qgUre�sd�tc�ub�v`�wa�x`�y`Oz^�{]�|]�}] ~]_\��[��]�\(�[��[F�Z��[�Z��Zn�ZH�Y��Y��X��YK�Y.�W��X�V��W��X�WN�X��W`�U��U��U��V�V&�U�T��T��T��Ty�T0�S�S�Sl�R'�R*�R��QZ�Q3�QA�P}�O��O��N��NR�L��L��K��K}�JN�I��H+�G,�F=�EI�E��D��C)�B,�Ap�@��?Z�=��=��<L�:��:X�9(�9 �7t�7��5�6��4��4#�3��3b�2{�2��2$�1��1q�0�/��/��0M�/��/|�0n�/��/��/��/5�/��/�.��/x�0,�0 �/�/��.��/��/D�.��/5�.��0�.��0,�/I�/e�/�/2�/d�/
7
+ .4 -� -�
8
+ � ��!
9
+ �!� �� �� �� �� ���� 1� �� >� x� � � u� �� 9� A� *� /��� �� �T� � �����_�v���i�q���n�v���'���:�'��������&��}�lm����Z �
10
+ . d �
11
+ I � �
12
+ !  
13
+ �q 3r
14
+ �s t u
15
+ �v w
16
+ �x $y z
17
+ �{
18
+ �|
19
+ �} (~
20
+ o
21
+ ��
22
+ f�
23
+ ��
24
+ ��
25
+ ��
26
+ �� �
27
+ ��
28
+ ��
29
+ Y�
30
+ ��
31
+ x�
32
+ t�
33
+ ,�
34
+ i�
35
+ H�
36
+ +�
37
+ ��
38
+ b�
39
+ u�
40
+ �
41
+ n�
42
+ *�
43
+ � ��
44
+ h�
45
+ � ��
46
+ �
47
+ �
48
+ � �� �� �� �� �� �� �� ��
49
+ "� �� W� �� �� �� �� `� l� 1� o� N� x� g� � I� i� C� 5� � �� =� <� j� %� i��� D� M� 1� <��� F������� L��������� ��� �[�x�����U���v���������=���p�I�]�0����:�}�������D���D�(� ���E����p�������"�����|���������������}� �
50
+ � D �
51
+ 0�1�2�3�4 5�6�7�8�9�:�;�<�=�>S?~@�A�B�C�DpE]FLGVHvI"JsK�L�MZN�O�P.Q_R'SST�UxVWX�YWZ�[�\R]�^�_R`9ab!c d�e�f�g�h�i�j�k:lm�n�o�p�q�r�s�t�u�v�w�x�y�z�{�|�}�~�����������~���g�������F�I�^�p�i�V�R�\�s�U�*�E�v�1�p� �=���E�����=���#����������8�'�.�M�������������������������� �����������������������������h�������������������o�u�r�p���S�w�9�j�G�i��������!��R����T��Z�7�>�-�i� �5�����+�
52
+ ��S���.�����;�360�� �
53
+ �  �
54
+ � � �
55
+ � � �
56
+ *
57
+ +
58
+ -
59
+ +
60
+ &
61
+ 3
62
+ '
63
+ &
64
+ 1
65
+ -
66
+
67
+ 1
68
+ &
69
+ -
70
+
71
+ !
72
+  
73
+ *
74
+ 
75
+ *
76
+ 
77
+ ,
78
+ '
79
+ ,
80
+ !
81
+ "
82
+ 1
83
+ (
84
+ )
85
+ *
86
+ %
87
+ '
88
+ /
89
+ 
90
+ !,
91
+ ",
92
+ #0
93
+ $)
94
+ %#
95
+ &
96
+ '
97
+ (,
98
+ )
99
+ *
100
+ +,
101
+ ,
102
+ -'
103
+ .
104
+ /!
105
+ 0
106
+ 1&
107
+ 2 
108
+ 3
109
+ 4%
110
+ 5
111
+ 6#
112
+ 7
113
+ 8
114
+ 9&
115
+ :
116
+ ;
117
+ <
118
+ =
119
+ >%
120
+ ?/
121
+ @
122
+ A"
123
+ B'
124
+ C'
125
+ D%
126
+ E
127
+ F
128
+ G
129
+ H(
130
+ I
131
+ J#
132
+ K
133
+ L
134
+ M-
135
+ N
136
+ O#
137
+ P
138
+ Q
139
+ R
140
+ S!
141
+ T
142
+ U
143
+ V%
144
+ W
145
+ X"
146
+ Y
147
+ Z
148
+ [
149
+ \
150
+ ]
151
+ ^
152
+ _
153
+ `
154
+ a 
155
+ b
156
+ c
157
+ d
158
+ e
159
+ f
160
+ g
161
+ h
162
+ i
163
+ j
164
+ k
165
+ l
166
+ m
167
+ n
168
+ o
169
+ p 
170
+ q
171
+ r
172
+ s
173
+ t
174
+ u
175
+ v
176
+ w
177
+ x
178
+ y
179
+ z
180
+ {
181
+ |
182
+ }
183
+ ~
184
+ 
185
+ �
186
+ �
187
+ �
188
+ �
189
+ �
190
+ �
191
+ �
192
+ �
193
+ �
194
+ �
195
+ �
196
+ �
197
+ �
198
+ �
199
+ �
200
+ �
201
+ �
202
+ �
203
+ �
204
+ �
205
+ �
206
+
207
+ �
208
+ �
209
+ �
210
+
211
+ �
212
+ �
213
+ �
214
+ �
215
+ �
216
+
217
+ �
218
+
219
+ �
220
+ �
221
+ � 
222
+ �
223
+ � 
224
+ �
225
+ �
226
+ �
227
+ �
228
+
229
+ �
230
+ �
231
+ �
232
+
233
+ �
234
+
235
+ 
236
+ � 
237
+ �
238
+ � 
239
+ �
240
+ �
241
+ � 
242
+ �
243
+ �
244
+ � 
245
+ � 
246
+
247
+ �
248
+ �
249
+ �
250
+ � 
251
+ �
252
+ �
253
+ �
254
+ �
255
+ �
256
+
257
+ 
258
+ �
259
+ � 
260
+ � 
261
+ � 
262
+ �
263
+ �
264
+ � 
265
+
266
+ � 
267
+ � 
268
+ � 
269
+ �
270
+
271
+
272
+ �
273
+ �
274
+ �
275
+ �
276
+ � 
277
+ �
278
+
279
+ 
280
+ �
281
+ � 
282
+ �
283
+ �
284
+ �
285
+ �
286
+
287
+ � 
288
+ � 
289
+
290
+
291
+ 
292
+ �
293
+ �
294
+ � 
295
+ �
296
+ � 
297
+ � 
298
+ �
299
+
300
+ � 
301
+
302
+ 
303
+ � 
304
+ �
305
+ �
306
+ � 
307
+ �
308
+ �
309
+ �
310
+ �
311
+ �
312
+ �
313
+ �
314
+ � 
315
+ � 
316
+ �
317
+ �           
318
+  
319
+  
320
+ 
321
+                
322
+    
323
+   
324
+   !
325
+  +  ,  - . /  0 1 2 3 4  5  6 7 8 9 : ; < = > ? @ A B C D
326
+  E F G  H
327
+  I J K L M N  O P Q  R
328
+  S T U V W X Y
329
+  Z [ \ ] ^ _ ` a b  c d e f g h i j k l m n o  p q r s t u
330
+  v w x y z { | } ~  � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �  � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � � �          
331
+   
332
+ 
333
  
334
+ 
1
- 
335
+   
336
+   
jaxspec/data/obsconf.py CHANGED
@@ -1,14 +1,11 @@
1
1
  import numpy as np
2
2
  import xarray as xr
3
3
  import sparse
4
+ import scipy
4
5
  from .instrument import Instrument
5
6
  from .observation import Observation
6
7
 
7
8
 
8
- def densify_xarray(xarray):
9
- return xr.DataArray(xarray.data.todense(), dims=xarray.dims, coords=xarray.coords, attrs=xarray.attrs, name=xarray.name)
10
-
11
-
12
9
  class ObsConfiguration(xr.Dataset):
13
10
  """
14
11
  Class to store the data of a folding model, which is the link between the unfolded and folded spectra.
@@ -56,8 +53,8 @@ class ObsConfiguration(xr.Dataset):
56
53
 
57
54
  out_energies = np.stack(
58
55
  (
59
- np.asarray(self.coords["e_min_folded"].data.todense(), dtype=np.float64),
60
- np.asarray(self.coords["e_max_folded"].data.todense(), dtype=np.float64),
56
+ np.asarray(self.coords["e_min_folded"].data, dtype=np.float64),
57
+ np.asarray(self.coords["e_max_folded"].data, dtype=np.float64),
61
58
  )
62
59
  )
63
60
 
@@ -67,34 +64,16 @@ class ObsConfiguration(xr.Dataset):
67
64
  def from_pha_file(
68
65
  cls, pha_path, rmf_path=None, arf_path=None, bkg_path=None, low_energy: float = 1e-20, high_energy: float = 1e20
69
66
  ):
70
- from .util import data_loader
67
+ from .util import data_path_finder
71
68
 
72
- pha, arf, rmf, bkg, metadata = data_loader(pha_path, arf_path=arf_path, rmf_path=rmf_path, bkg_path=bkg_path)
69
+ arf_path_default, rmf_path_default, bkg_path_default = data_path_finder(pha_path)
73
70
 
74
- instrument = Instrument.from_matrix(
75
- rmf.sparse_matrix,
76
- arf.specresp if arf is not None else np.ones_like(rmf.energ_lo),
77
- rmf.energ_lo,
78
- rmf.energ_hi,
79
- rmf.e_min,
80
- rmf.e_max,
81
- )
71
+ arf_path = arf_path_default if arf_path is None else arf_path
72
+ rmf_path = rmf_path_default if rmf_path is None else rmf_path
73
+ bkg_path = bkg_path_default if bkg_path is None else bkg_path
82
74
 
83
- if bkg is not None:
84
- backratio = np.where(bkg.backscal > 0.0, pha.backscal / np.where(bkg.backscal > 0, bkg.backscal, 1.0), 0.0)
85
- else:
86
- backratio = np.ones_like(pha.counts)
87
-
88
- observation = Observation.from_matrix(
89
- pha.counts,
90
- pha.grouping,
91
- pha.channel,
92
- pha.quality,
93
- pha.exposure,
94
- background=bkg.counts if bkg is not None else None,
95
- backratio=backratio,
96
- attributes=metadata,
97
- )
75
+ instrument = Instrument.from_ogip_file(rmf_path, arf_path=arf_path)
76
+ observation = Observation.from_pha_file(pha_path, bkg_path=bkg_path)
98
77
 
99
78
  return cls.from_instrument(instrument, observation, low_energy=low_energy, high_energy=high_energy)
100
79
 
@@ -102,57 +81,108 @@ class ObsConfiguration(xr.Dataset):
102
81
  def from_instrument(
103
82
  cls, instrument: Instrument, observation: Observation, low_energy: float = 1e-20, high_energy: float = 1e20
104
83
  ):
105
- # Exclude the bins flagged with bad quality
106
- quality_filter = observation.quality == 0
107
- grouping = observation.grouping * quality_filter
84
+ # First we unpack all the xarray data to classical np array for efficiency
85
+ # We also exclude the bins that are flagged with bad quality on the instrument
86
+ quality_filter = observation.quality.data == 0
87
+ grouping = scipy.sparse.csr_array(observation.grouping.data.to_scipy_sparse()) * quality_filter
88
+ e_min_channel = instrument.coords["e_min_channel"].data
89
+ e_max_channel = instrument.coords["e_max_channel"].data
90
+ e_min_unfolded = instrument.coords["e_min_unfolded"].data
91
+ e_max_unfolded = instrument.coords["e_max_unfolded"].data
92
+ redistribution = scipy.sparse.csr_array(instrument.redistribution.data.to_scipy_sparse())
93
+ area = instrument.area.data
94
+ exposure = observation.exposure.data
108
95
 
109
96
  # Computing the lower and upper energies of the bins after grouping
110
97
  # This is just a trick to compute it without 10 lines of code
111
- e_min = (xr.where(grouping > 0, grouping, np.nan) * instrument.coords["e_min_channel"]).min(
112
- skipna=True, dim="instrument_channel"
113
- )
98
+ grouping_nan = observation.grouping.data * quality_filter
99
+ grouping_nan.fill_value = np.nan
100
+ e_min = sparse.nanmin(grouping_nan * e_min_channel, axis=1).todense()
101
+ e_max = sparse.nanmax(grouping_nan * e_max_channel, axis=1).todense()
114
102
 
115
- e_max = (xr.where(grouping > 0, grouping, np.nan) * instrument.coords["e_max_channel"]).max(
116
- skipna=True, dim="instrument_channel"
117
- )
118
-
119
- transfer_matrix = grouping @ (instrument.redistribution * instrument.area * observation.exposure)
120
- transfer_matrix = transfer_matrix.assign_coords({"e_min_folded": e_min, "e_max_folded": e_max})
103
+ # Compute the transfer matrix
104
+ transfer_matrix = grouping @ (redistribution * area * exposure)
121
105
 
122
106
  # Exclude bins out of the considered energy range, and bins without contribution from the RMF
123
- row_idx = densify_xarray(((e_min > low_energy) & (e_max < high_energy)) * (grouping.sum(dim="instrument_channel") > 0))
124
-
125
- col_idx = densify_xarray(
126
- (instrument.coords["e_min_unfolded"] > 0) * (instrument.redistribution.sum(dim="instrument_channel") > 0)
127
- )
128
107
 
129
- # The transfer matrix is converted locally to csr format to allow FAST slicing
130
- transfer_matrix_scipy = transfer_matrix.data.to_scipy_sparse().tocsr()
131
- transfer_matrix_reduced = transfer_matrix_scipy[row_idx.data][:, col_idx.data]
132
- transfer_matrix_reduced = sparse.COO.from_scipy_sparse(transfer_matrix_reduced)
108
+ row_idx = (e_min > low_energy) & (e_max < high_energy) & (grouping.sum(axis=1) > 0)
109
+ col_idx = (e_min_unfolded > 0) & (redistribution.sum(axis=0) > 0)
133
110
 
134
- # A dummy zero matrix is put so that the slicing in xarray is fast
135
- transfer_matrix.data = sparse.zeros_like(transfer_matrix.data)
136
- transfer_matrix = transfer_matrix[row_idx][:, col_idx]
137
-
138
- # The reduced transfer matrix is put back in the xarray
139
- transfer_matrix.data = transfer_matrix_reduced
140
-
141
- folded_counts = observation.folded_counts.copy().where(row_idx, drop=True)
111
+ # Apply this reduction to all the relevant arrays
112
+ transfer_matrix = sparse.COO.from_scipy_sparse(transfer_matrix[row_idx][:, col_idx])
113
+ folded_counts = observation.folded_counts.data[row_idx]
114
+ folded_backratio = observation.folded_backratio.data[row_idx]
115
+ area = instrument.area.data[col_idx]
116
+ e_min_folded = e_min[row_idx]
117
+ e_max_folded = e_max[row_idx]
118
+ e_min_unfolded = e_min_unfolded[col_idx]
119
+ e_max_unfolded = e_max_unfolded[col_idx]
142
120
 
143
121
  if observation.folded_background is not None:
144
- folded_background = observation.folded_background.copy().where(row_idx, drop=True)
145
-
122
+ folded_background = observation.folded_background.data[row_idx]
146
123
  else:
147
- folded_background = None
124
+ folded_background = np.zeros_like(folded_counts)
125
+
126
+ data_dict = {
127
+ "transfer_matrix": (
128
+ ["folded_channel", "unfolded_channel"],
129
+ transfer_matrix,
130
+ {
131
+ "description": "Transfer matrix to use to fold the incoming spectrum. It is built and restricted using the grouping, redistribution matrix, effective area, quality flags and energy bands defined by the user."
132
+ },
133
+ ),
134
+ "area": (
135
+ ["unfolded_channel"],
136
+ area,
137
+ {"description": "Effective area with the same restrictions as the transfer matrix.", "units": "cm^2"},
138
+ ),
139
+ "exposure": ([], exposure, {"description": "Total exposure", "unit": "s"}),
140
+ "folded_counts": (
141
+ ["folded_channel"],
142
+ folded_counts,
143
+ {
144
+ "description": "Folded counts after grouping, with the same restrictions as the transfer matrix.",
145
+ "unit": "photons",
146
+ },
147
+ ),
148
+ "folded_backratio": (
149
+ ["folded_channel"],
150
+ folded_backratio,
151
+ {"description": "Background scaling after grouping, with the same restrictions as the transfer matrix."},
152
+ ),
153
+ "folded_background": (
154
+ ["folded_channel"],
155
+ folded_background,
156
+ {
157
+ "description": "Folded background counts after grouping, with the same restrictions as the transfer matrix.",
158
+ "unit": "photons",
159
+ },
160
+ ),
161
+ }
148
162
 
149
163
  return cls(
150
- {
151
- "transfer_matrix": transfer_matrix,
152
- "area": instrument.area.copy().where(col_idx, drop=True),
153
- "exposure": observation.exposure,
154
- "folded_backratio": observation.folded_backratio.copy().where(row_idx, drop=True),
155
- "folded_counts": folded_counts,
156
- "folded_background": folded_background,
157
- }
164
+ data_dict,
165
+ coords={
166
+ "e_min_folded": (
167
+ ["folded_channel"],
168
+ e_min_folded,
169
+ {"description": "Low energy of folded channel"},
170
+ ),
171
+ "e_max_folded": (
172
+ ["folded_channel"],
173
+ e_max_folded,
174
+ {"description": "High energy of folded channel"},
175
+ ),
176
+ "e_min_unfolded": (
177
+ ["unfolded_channel"],
178
+ e_min_unfolded,
179
+ {"description": "Low energy of unfolded channel"},
180
+ ),
181
+ "e_max_unfolded": (
182
+ ["unfolded_channel"],
183
+ e_max_unfolded,
184
+ {"description": "High energy of unfolded channel"},
185
+ ),
186
+ },
187
+ attrs=observation.attrs | instrument.attrs,
158
188
  )
@@ -1,6 +1,6 @@
1
- import os
2
1
  import numpy as np
3
2
  import xarray as xr
3
+ from .ogip import DataPHA
4
4
 
5
5
 
6
6
  class Observation(xr.Dataset):
@@ -95,11 +95,7 @@ class Observation(xr.Dataset):
95
95
  )
96
96
 
97
97
  @classmethod
98
- def from_pha_file(cls, pha_file: str | os.PathLike, **kwargs):
99
- from .util import data_loader
100
-
101
- pha, arf, rmf, bkg, metadata = data_loader(pha_file)
102
-
98
+ def from_ogip_container(cls, pha: DataPHA, bkg: DataPHA | None = None, **metadata):
103
99
  if bkg is not None:
104
100
  backratio = np.nan_to_num((pha.backscal * pha.exposure * pha.areascal) / (bkg.backscal * bkg.exposure * bkg.areascal))
105
101
  else:
@@ -116,6 +112,28 @@ class Observation(xr.Dataset):
116
112
  attributes=metadata,
117
113
  )
118
114
 
115
+ @classmethod
116
+ def from_pha_file(cls, pha_path: str, bkg_path: str | None = None, **metadata):
117
+ from .util import data_path_finder
118
+
119
+ arf_path, rmf_path, bkg_path_default = data_path_finder(pha_path)
120
+ bkg_path = bkg_path_default if bkg_path is None else bkg_path
121
+
122
+ pha = DataPHA.from_file(pha_path)
123
+ bkg = DataPHA.from_file(bkg_path) if bkg_path is not None else None
124
+
125
+ if metadata is None:
126
+ metadata = {}
127
+
128
+ metadata.update(
129
+ observation_file=pha_path,
130
+ background_file=bkg_path,
131
+ response_matrix_file=rmf_path,
132
+ ancillary_response_file=arf_path,
133
+ )
134
+
135
+ return cls.from_ogip_container(pha, bkg=bkg, **metadata)
136
+
119
137
  def plot_counts(self, **kwargs):
120
138
  """
121
139
  Plot the counts
jaxspec/data/util.py CHANGED
@@ -6,9 +6,9 @@ import haiku as hk
6
6
  from pathlib import Path
7
7
  from numpy.typing import ArrayLike
8
8
  from collections.abc import Mapping
9
- from typing import TypeVar
9
+ from typing import TypeVar, Tuple
10
+ from astropy.io import fits
10
11
 
11
- from .ogip import DataPHA, DataARF, DataRMF
12
12
  from . import Observation, Instrument, ObsConfiguration
13
13
  from ..model.abc import SpectralModel
14
14
  from ..fit import CountForwardModel
@@ -201,46 +201,32 @@ def fakeit_for_multiple_parameters(
201
201
  return fakeits[0] if len(fakeits) == 1 else fakeits
202
202
 
203
203
 
204
- def data_loader(pha_path: str, arf_path=None, rmf_path=None, bkg_path=None):
204
+ def data_path_finder(pha_path: str) -> Tuple[str | None, str | None, str | None]:
205
205
  """
206
- This function is a convenience function that allows to load PHA, ARF and RMF data
207
- from a given PHA file, using either the ARF/RMF/BKG filenames in the header or the
208
- specified filenames overwritten by the user.
209
-
206
+ This function tries its best to find the ARF, RMF and BKG files associated with a given PHA file.
210
207
  Parameters:
211
208
  pha_path: The PHA file path.
209
+
210
+ Returns:
212
211
  arf_path: The ARF file path.
213
212
  rmf_path: The RMF file path.
214
213
  bkg_path: The BKG file path.
215
214
  """
216
215
 
217
- pha = DataPHA.from_file(pha_path)
218
- directory = str(Path(pha_path).parent)
219
-
220
- if arf_path is None:
221
- if pha.ancrfile != "none" and pha.ancrfile != "":
222
- arf_path = find_file_or_compressed_in_dir(pha.ancrfile, directory)
223
-
224
- if rmf_path is None:
225
- if pha.respfile != "none" and pha.respfile != "":
226
- rmf_path = find_file_or_compressed_in_dir(pha.respfile, directory)
227
-
228
- if bkg_path is None:
229
- if pha.backfile.lower() != "none" and pha.backfile != "":
230
- bkg_path = find_file_or_compressed_in_dir(pha.backfile, directory)
216
+ def find_path(file_name: str, directory: str) -> str | None:
217
+ if file_name.lower() != "none" and file_name != "":
218
+ return find_file_or_compressed_in_dir(file_name, directory)
219
+ else:
220
+ return None
231
221
 
232
- arf = DataARF.from_file(arf_path) if arf_path is not None else None
233
- rmf = DataRMF.from_file(rmf_path) if rmf_path is not None else None
234
- bkg = DataPHA.from_file(bkg_path) if bkg_path is not None else None
222
+ header = fits.getheader(pha_path, "SPECTRUM")
223
+ directory = str(Path(pha_path).parent)
235
224
 
236
- metadata = {
237
- "observation_file": pha_path,
238
- "background_file": bkg_path,
239
- "response_matrix_file": rmf_path,
240
- "ancillary_response_file": arf_path,
241
- }
225
+ arf_path = find_path(header.get("ANCRFILE", "none"), directory)
226
+ rmf_path = find_path(header.get("RESPFILE", "none"), directory)
227
+ bkg_path = find_path(header.get("BACKFILE", "none"), directory)
242
228
 
243
- return pha, arf, rmf, bkg, metadata
229
+ return arf_path, rmf_path, bkg_path
244
230
 
245
231
 
246
232
  def find_file_or_compressed_in_dir(path: str | Path, directory: str | Path) -> str:
jaxspec/model/additive.py CHANGED
@@ -364,18 +364,17 @@ class Diskbb(AdditiveComponent):
364
364
  `Diskpbb` with $p=0.75$
365
365
 
366
366
  ??? abstract "Parameters"
367
+ * $T_{\text{in}}$ : Temperature at inner disk radius $\left[ \mathrm{keV}\right]$
367
368
  * $\text{norm}$ : $\cos i(r_{\text{in}}/d)^{2}$,
368
369
  where $r_{\text{in}}$ is "an apparent" inner disk radius $\left[\text{km}\right]$,
369
- * $d$ the distance to the source in units of $10 \text{kpc}$,
370
- * $i$ the angle of the disk ($i=0$ is face-on)
371
- * $T_{\text{in}}$ : Temperature at inner disk radius $\left[ \mathrm{keV}\right]$
370
+ $d$ the distance to the source in units of $10 \text{kpc}$, $i$ the angle of the disk ($i=0$ is face-on)
372
371
  """
373
372
 
374
373
  def continuum(self, energy):
375
- norm = hk.get_parameter("norm", [], init=HaikuConstant(1))
376
374
  p = 0.75
377
- tin = hk.get_parameter("Tin", [], init=HaikuConstant(1))
378
375
  tout = 0.0
376
+ tin = hk.get_parameter("Tin", [], init=HaikuConstant(1))
377
+ norm = hk.get_parameter("norm", [], init=HaikuConstant(1))
379
378
 
380
379
  # Tout is set to 0 as it is evaluated at R=infinity
381
380
  def integrand(kT, e, tin, p):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jaxspec
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
4
  Summary: jaxspec is a bayesian spectral fitting library for X-ray astronomy.
5
5
  License: MIT
6
6
  Author: sdupourque
@@ -10,7 +10,7 @@ Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
- Requires-Dist: arviz (>=0.17.0,<0.18.0)
13
+ Requires-Dist: arviz (>=0.17.1,<0.18.0)
14
14
  Requires-Dist: astropy (>=6.0.0,<7.0.0)
15
15
  Requires-Dist: chainconsumer (>=1.0.0,<2.0.0)
16
16
  Requires-Dist: cmasher (>=1.6.3,<2.0.0)
@@ -25,6 +25,7 @@ Requires-Dist: networkx (>=3.1,<4.0)
25
25
  Requires-Dist: numpy (>=1.26.1,<2.0.0)
26
26
  Requires-Dist: numpyro (>=0.13.2,<0.15.0)
27
27
  Requires-Dist: pandas (>=2.2.0,<3.0.0)
28
+ Requires-Dist: scipy (<1.13)
28
29
  Requires-Dist: seaborn (>=0.13.1,<0.14.0)
29
30
  Requires-Dist: simpleeval (>=0.9.13,<0.10.0)
30
31
  Requires-Dist: sparse (>=0.15.1,<0.16.0)
@@ -33,8 +34,9 @@ Description-Content-Type: text/markdown
33
34
 
34
35
  # jaxspec
35
36
 
36
- [![Read the Docs](https://img.shields.io/readthedocs/jaxspec?style=for-the-badge)](https://jaxspec.readthedocs.io/en/latest/)
37
+ [![PyPI - Version](https://img.shields.io/pypi/v/jaxspec?style=for-the-badge&logo=pypi&color=rgb(37%2C%20150%2C%20190))](https://pypi.org/project/jaxspec/)
37
38
  [![Python package](https://img.shields.io/pypi/pyversions/jaxspec?style=for-the-badge)](https://pypi.org/project/jaxspec/)
39
+ [![Read the Docs](https://img.shields.io/readthedocs/jaxspec?style=for-the-badge)](https://jaxspec.readthedocs.io/en/latest/)
38
40
  [![Codecov](https://img.shields.io/codecov/c/github/renecotyfanboy/jaxspec?style=for-the-badge)](https://app.codecov.io/gh/renecotyfanboy/jaxspec)
39
41
  [![Slack](https://img.shields.io/badge/Slack-4A154B?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/jaxspec/shared_invite/zt-2cuxkdl2f-t0EEAKP~HBEHKvIUZJL2sg)
40
42
 
@@ -18,26 +18,26 @@ jaxspec/data/example_data/PN.pha,sha256=qYXgYHa_Bg06UzHyBBOvqSCKjXcfpsZx6JGKGGBX
18
18
  jaxspec/data/example_data/PN.rmf,sha256=kbqe-C2oufc-anmd_gl7h8aKcCCsbFqg3NQGe_nLQoc,3962880
19
19
  jaxspec/data/example_data/PN_spectrum_grp20.fits,sha256=qYXgYHa_Bg06UzHyBBOvqSCKjXcfpsZx6JGKGGBXfJA,138240
20
20
  jaxspec/data/example_data/PNbackground_spectrum.fits,sha256=VeAX4MGbMkJF_vBJ3_KnouSbmjkWZ8qcT2Z8T2g7H0k,120960
21
+ jaxspec/data/example_data/fakeit.pha,sha256=IhkeWkE-b3ELECd_Uasjo9h3cXgcjCYH20wDpXJ8LMk,60480
21
22
  jaxspec/data/grouping.py,sha256=hhgBt-voiH0DDSyePacaIGsaMnrYbJM_-ZeU66keC7I,622
22
23
  jaxspec/data/instrument.py,sha256=0Ef3zhNT7ca-nHtRCGKXpbkyXpgVVpNtO8_XrMGWnyU,3984
23
- jaxspec/data/obsconf.py,sha256=EAWL5D4L3Wk3nZO5VFORTkpcTpNLqa5x0bBm-Q2CCeA,5782
24
- jaxspec/data/observation.py,sha256=DDNAleAm-oZxCxIuPlA_FZ612o1BoG7PrctV8COLY_U,5725
24
+ jaxspec/data/obsconf.py,sha256=tnXCXim6eBjZbvNbx2ViRJ3wzQhNacXRcCrLZlPTdY0,7378
25
+ jaxspec/data/observation.py,sha256=1UnFu5ihZp9z-vP_I7tsFY8jhhIJunv46JyuE-acrg0,6394
25
26
  jaxspec/data/ogip.py,sha256=d0OEEkznA7s8xPkqPrnfIvedTdIRsQuBnRK4wHoQ17M,8793
26
- jaxspec/data/util.py,sha256=qYkRO2kxM3LUdbmLGf_hGEqElfSlVGGzyTQM7_BVttI,8481
27
+ jaxspec/data/util.py,sha256=BsIVpmx2kAvAn-w6uSjC793OmPAfmDKMGGLDuk9yztY,7923
27
28
  jaxspec/fit.py,sha256=FhnedL7-_9eovErw2SESH5sbMhU-qJNdkqxkoVsLglo,9531
28
29
  jaxspec/model/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
29
30
  jaxspec/model/abc.py,sha256=O5DwgcXWfuGSYxTk_TmZcZXak1lLuEvBzJXUTd3E9tE,17851
30
- jaxspec/model/additive.py,sha256=mVkqwx9ch3fniK6KolawVqxlZ3muvg2CGgEpveEUiog,18668
31
+ jaxspec/model/additive.py,sha256=iu_n820tOdKbG5BKsFM-nVOa2_sKPWBSQ_TKS1bljSk,18656
31
32
  jaxspec/model/background.py,sha256=zej99rVfcRb75T85o3u4qeYQIgnFwGtxK8niZJ8S5mM,6872
32
33
  jaxspec/model/list.py,sha256=0RPAoscVz_zM1CWdx_Gd5wfrQWV5Nv4Kd4bSXu2ayUA,860
33
34
  jaxspec/model/multiplicative.py,sha256=sAKDkiplhdY7TsaPk7gwkR18dcXmU2nytgBCiHNBPMk,7537
34
- jaxspec/tables/apec.nc,sha256=JVsjJt2q0QwwDaEMpgeqA2RPCzxO2vVV6F7npk_Haec,74780226
35
35
  jaxspec/tables/xsect_phabs_aspl.fits,sha256=Pq_7oqYuOmEeCk4f9KVzQtfVdvAj17u2MnENx1uaUBk,86400
36
36
  jaxspec/tables/xsect_tbabs_wilm.fits,sha256=PPReRcnWccTE_BKDFLfDposw8Jbu3ms-sIv1UiSkSTU,86400
37
37
  jaxspec/tables/xsect_wabs_angr.fits,sha256=mzBzpHejC1LiB_LEv3mvxq4Zq7qPIHGQrExpcCT3QHM,86400
38
38
  jaxspec/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
39
  jaxspec/util/integrate.py,sha256=6oGYhRTevgOo47hbzdcomOCShC2cpKgF8AP7bkx-Zsw,4379
40
- jaxspec-0.0.3.dist-info/LICENSE.md,sha256=2q5XoWzddts5IqzIcgYYMOL21puU3MfO8gvT3Ype1eQ,1073
41
- jaxspec-0.0.3.dist-info/METADATA,sha256=1qLGWaTL8D_wHGjcKA5n_RSPFIS-Xpk6JCNTSkSDklk,3009
42
- jaxspec-0.0.3.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
43
- jaxspec-0.0.3.dist-info/RECORD,,
40
+ jaxspec-0.0.4.dist-info/LICENSE.md,sha256=2q5XoWzddts5IqzIcgYYMOL21puU3MfO8gvT3Ype1eQ,1073
41
+ jaxspec-0.0.4.dist-info/METADATA,sha256=JSU_vEIQjYwXmTQBXPrXL4ZC8Xfg3s60ktPDmBoB_3E,3194
42
+ jaxspec-0.0.4.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
43
+ jaxspec-0.0.4.dist-info/RECORD,,
jaxspec/tables/apec.nc DELETED
Binary file