1- import os
2- import subprocess as sp
3- import time
41from functools import partial
52from multiprocessing import Pool
63
7- import matplotlib .pyplot as plt
84import numpy as np
95from mpi4py import MPI
106from stingray import AveragedPowerspectrum , EventList
1612
1713
1814def get_data_intervals (interval_idxs , info = None , fname = None , sample_time = None ):
19- """
20- Generate light curves for specified time intervals from event data.
15+ """Generate light curves for specified time intervals from event data.
16+
2117 Parameters
2218 ----------
2319 interval_idxs : array-like
@@ -28,17 +24,18 @@ def get_data_intervals(interval_idxs, info=None, fname=None, sample_time=None):
2824 Path to the FITS file containing event data.
2925 sample_time : float, optional
3026 The time resolution (bin width) for the light curve histogram.
27+
3128 Yields
3229 ------
3330 lc : array-like
3431 Histogrammed light curve for each specified interval.
32+
3533 Notes
3634 -----
3735 - Uses FITSTimeseriesReader to read event data from the FITS file.
3836 - Each yielded light curve corresponds to one interval in `interval_idxs`.
3937 - The number of bins is determined by the interval duration and `sample_time`.
4038 """
41-
4239 tsreader = FITSTimeseriesReader (fname , output_class = EventList )
4340 time_intervals = info ["interval_times" ][interval_idxs ]
4441 if np .shape (time_intervals ) == (2 ,):
@@ -54,8 +51,8 @@ def get_data_intervals(interval_idxs, info=None, fname=None, sample_time=None):
5451
5552
5653def single_rank_intervals (this_ranks_intervals , sample_time = None , info = None , fname = None ):
57- """
58- Generate an averaged powerspectrum from light curve intervals for a single rank.
54+ """Generate an averaged powerspectrum from light curve intervals for a single rank.
55+
5956 Parameters
6057 ----------
6158 this_ranks_intervals : list or array-like
@@ -66,18 +63,19 @@ def single_rank_intervals(this_ranks_intervals, sample_time=None, info=None, fna
6663 Dictionary containing metadata about the intervals, including "interval_times".
6764 fname : str or None, optional
6865 Filename or path to the data source, if required by `get_data_intervals`.
66+
6967 Returns
7068 -------
7169 pds : AveragedPowerspectrum
7270 The averaged powerspectrum computed from the light curve intervals.
7371 nbin : int
7472 Number of bins in each interval, calculated from the interval duration and sample time.
73+
7574 Notes
7675 -----
7776 This function extracts light curve data for the specified intervals, computes the number of bins,
7877 and returns the averaged powerspectrum using the "leahy" normalization.
7978 """
80-
8179 # print(kwargs)
8280 t_int = info ["interval_times" ][0 ]
8381 nbin = int (np .rint ((t_int [1 ] - t_int [0 ]) / sample_time ))
@@ -98,10 +96,7 @@ def single_rank_intervals(this_ranks_intervals, sample_time=None, info=None, fna
9896
9997
10098def main_none (fname , sample_time , segment_size ):
101- """
102- Process a FITS timeseries file and compute an averaged powerspectrum.
103- This function reads event data from a FITS file, processes it using the Stingray
104- library, and computes an averaged powerspectrum with Leahy normalization.
99+ """Process a FITS timeseries file and compute an averaged powerspectrum. This function reads event data from a FITS file, processes it using the Stingray library, and computes an averaged powerspectrum with Leahy normalization.
105100
106101 Parameters
107102 ----------
@@ -111,6 +106,7 @@ def main_none(fname, sample_time, segment_size):
111106 The time resolution (bin size) to use when processing the data.
112107 segment_size : float
113108 The size of each segment (in seconds) for averaging the powerspectrum.
109+
114110 Returns
115111 -------
116112 freq : numpy.ndarray
@@ -123,7 +119,6 @@ def main_none(fname, sample_time, segment_size):
123119 This function uses the standard Stingray processing pipeline and does not
124120 parallelize the computation.
125121 """
126-
127122 logger .info ("Using standard Stingray processing" )
128123 tsreader = FITSTimeseriesReader (fname , output_class = EventList )
129124
@@ -153,6 +148,7 @@ def main_mpi(fname, sample_time, segment_size):
153148 5. Partial results are combined using a binary tree reduction, where ranks pair up and
154149 send/receive data until only one rank holds the final result.
155150 6. The final result is normalized and frequency bins are computed.
151+
156152 Parameters
157153 ----------
158154 fname : str
@@ -161,6 +157,7 @@ def main_mpi(fname, sample_time, segment_size):
161157 The sampling time for the time series analysis.
162158 segment_size : float
163159 The size of each segment (interval) to be processed.
160+
164161 Returns
165162 -------
166163 output : AveragedPowerspectrum
@@ -172,7 +169,6 @@ def main_mpi(fname, sample_time, segment_size):
172169 and dependencies are available.
173170 - Only the rank responsible for the final reduction returns the results; other ranks return None.
174171 """
175-
176172 tsreader = FITSTimeseriesReader (fname , output_class = EventList )
177173
178174 def data_lookup ():
@@ -184,6 +180,7 @@ def data_lookup():
184180 "gtis" : tsreader .gti ,
185181 "interval_times" : interval_times ,
186182 "n_intervals" : len (interval_times ),
183+ "nphots" : tsreader .nphot / tsreader .exposure * segment_size ,
187184 }
188185
189186 world_comm = MPI .COMM_WORLD
@@ -280,6 +277,12 @@ def data_lookup():
280277 output .m = total_n_intervals
281278 output .gti = info ["gtis" ]
282279 output .dt = sample_time
280+ output .power_err = totals / np .sqrt (total_n_intervals )
281+ output .unnorm_power = totals / 2 * info ["nphots" ]
282+ output .unnorm_power_err = output .unnorm_power / np .sqrt (total_n_intervals )
283+ output .norm = "leahy"
284+ output .nphots = info ["nphots" ]
285+ output .n = np .rint (segment_size / sample_time ).astype (int )
283286
284287 return output
285288
@@ -315,7 +318,6 @@ def main_multiprocessing(fname, sample_time, segment_size, world_size=8):
315318 -------
316319 output : AveragedPowerspectrum
317320 The averaged power spectrum computed across all intervals and processes.
318-
319321 """
320322
321323 def data_lookup ():
@@ -354,6 +356,7 @@ def data_lookup():
354356 p = Pool (world_size )
355357
356358 totals = 0
359+ nphots = 0
357360 for results , data_size in p .imap_unordered (
358361 partial (
359362 single_rank_intervals ,
@@ -364,8 +367,10 @@ def data_lookup():
364367 this_ranks_intervals ,
365368 ):
366369 totals += results .power * results .m
370+ nphots += results .nphots * results .m
367371 logger .debug ("Results" )
368372 totals /= total_n_intervals
373+ nphots /= total_n_intervals
369374
370375 freq = np .fft .fftfreq (data_size , d = sample_time )[positive_fft_bins (data_size )]
371376
@@ -375,6 +380,12 @@ def data_lookup():
375380 output .m = total_n_intervals
376381 output .gti = info ["gtis" ]
377382 output .dt = sample_time
383+ output .power_err = totals / np .sqrt (total_n_intervals )
384+ output .unnorm_power = totals / 2 * nphots
385+ output .unnorm_power_err = output .unnorm_power / np .sqrt (total_n_intervals )
386+ output .norm = "leahy"
387+ output .nphots = nphots
388+ output .n = np .rint (segment_size / sample_time ).astype (int )
378389
379390 return output
380391
@@ -401,10 +412,12 @@ def main(args=None):
401412 "--sample_time" ,
402413 type = float ,
403414 default = 1 / 8129 / 2 ,
404- help = "Light curve bin time; if negative, interpreted"
405- + " as negative power of 2."
406- + " Default: 2^-13, or keep input lc bin time"
407- + " (whatever is larger)" ,
415+ help = (
416+ "Light curve bin time; if negative, interpreted"
417+ " as negative power of 2."
418+ " Default: 2^-13, or keep input lc bin time"
419+ " (whatever is larger)"
420+ ),
408421 )
409422
410423 parser .add_argument (
@@ -455,7 +468,10 @@ def main(args=None):
455468 pds = main_multiprocessing (fname , sample_time , segment_size , world_size = args .nproc )
456469 else :
457470 pds = main_none (fname , sample_time , segment_size )
471+
458472 if pds is None :
459473 return
474+ if args .norm != "leahy" :
475+ pds = pds .to_norm (args .norm )
460476
461477 pds .write (args .outfname )
0 commit comments