Skip to content

Commit 9bad0b5

Browse files
Add formulas for bispectrum in fourier.py
1 parent 8b4b3e7 commit 9bad0b5

File tree

2 files changed

+153
-0
lines changed

2 files changed

+153
-0
lines changed

stingray/bispectrum.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from stingray import lightcurve
1111
import stingray.utils as utils
1212
from stingray.utils import fftshift, fft2, ifftshift, fft
13+
from stingray.fourier import get_flux_iterable_from_segments, bispectrum_and_bicoherence_from_iterable
1314

1415
__all__ = ["Bispectrum"]
1516

@@ -445,3 +446,100 @@ def plot_phase(self, axis=None, save=False, filename=None):
445446
else:
446447
plt.savefig(filename)
447448
return plt
449+
450+
451+
def bispectrum_and_bicoherence_from_time_array(times, gti, segment_size, dt):
452+
"""Calculate the bispectrum and the bicoherence from an array of times.
453+
454+
Parameters
455+
----------
456+
times : float `np.array`
457+
Array of times in the reference band
458+
gti : [[gti00, gti01], [gti10, gti11], ...]
459+
common good time intervals
460+
segment_size : float
461+
length of segments
462+
dt : float
463+
Time resolution of the light curves used to produce periodograms
464+
465+
Returns
466+
-------
467+
freqs : `np.array`
468+
The frequency in each row or column
469+
bispectrum: 2-d `np.array`
470+
The unnormalized bispectrum
471+
bicoherence: 2-d `np.array`
472+
The bicoherence, calculated with the normalization from
473+
Kim & Powers (1979), IEEE Trans. Plasma Sci., PS-7, 120
474+
"""
475+
476+
n_bin = np.rint(segment_size / dt).astype(int)
477+
dt = segment_size / n_bin
478+
479+
flux_iterable = get_flux_iterable_from_segments(
480+
times, gti, segment_size, n_bin
481+
)
482+
483+
return bispectrum_and_bicoherence_from_iterable(flux_iterable, dt)
484+
485+
486+
def bispectrum_and_bicoherence_from_events(events, segment_size, dt):
487+
"""Calculate the bispectrum and the bicoherence from an input event list.
488+
489+
Parameters
490+
----------
491+
events : `EventList`
492+
The input event list
493+
segment_size : float
494+
length of segments
495+
dt : float
496+
Time resolution of the light curves used to produce periodograms
497+
498+
Returns
499+
-------
500+
freqs : `np.array`
501+
The frequency in each row or column
502+
bispectrum: 2-d `np.array`
503+
The unnormalized bispectrum
504+
bicoherence: 2-d `np.array`
505+
The bicoherence, calculated with the normalization from
506+
Kim & Powers (1979), IEEE Trans. Plasma Sci., PS-7, 120
507+
"""
508+
509+
times = events.time
510+
gti = events.gti
511+
512+
return bispectrum_and_bicoherence_from_time_array(times, gti, segment_size, dt)
513+
514+
515+
def bispectrum_and_bicoherence_from_lightcurve(lightcurve, segment_size):
516+
"""Calculate the bispectrum and the bicoherence from an array of times.
517+
518+
Parameters
519+
----------
520+
lightcurve : `Lightcurve`
521+
Input light curve
522+
segment_size : float
523+
length of segments
524+
dt : float
525+
Time resolution of the light curves used to produce periodograms
526+
527+
Returns
528+
-------
529+
freqs : `np.array`
530+
The frequency in each row or column
531+
bispectrum: 2-d `np.array`
532+
The unnormalized bispectrum
533+
bicoherence: 2-d `np.array`
534+
The bicoherence, calculated with the normalization from
535+
Kim & Powers (1979), IEEE Trans. Plasma Sci., PS-7, 120
536+
"""
537+
dt = lightcurve.dt
538+
n_bin = np.rint(segment_size / dt).astype(int)
539+
540+
flux_iterable = get_flux_iterable_from_segments(
541+
lightcurve.time, lightcurve.gti, segment_size,
542+
n_bin, fluxes=lightcurve.counts
543+
)
544+
545+
return bispectrum_and_bicoherence_from_iterable(flux_iterable, dt)

stingray/fourier.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,61 @@ def get_flux_iterable_from_segments(times, gti, segment_size, n_bin=None, fluxes
880880
yield cts
881881

882882

883+
def bispectrum_and_bicoherence_from_iterable(flux_iterable, dt):
884+
"""Calculate the bispectrum and the bicoherence.
885+
886+
Parameters
887+
----------
888+
flux_iterable : `iterable` of `np.array`s or of tuples (`np.array`, `np.array`)
889+
Iterable providing either equal-length series of count measurements, or
890+
of tuples (fluxes, errors). They must all be of the same length.
891+
dt : float
892+
Time resolution of the light curves used to produce periodograms
893+
894+
Returns
895+
-------
896+
freqs : `np.array`
897+
The frequency in each row or column
898+
bispectrum: 2-d `np.array`
899+
The unnormalized bispectrum
900+
bicoherence: 2-d `np.array`
901+
The bicoherence, calculated with the normalization from
902+
Kim & Powers (1979), IEEE Trans. Plasma Sci., PS-7, 120
903+
"""
904+
905+
B = B1 = B2 = None
906+
for flux in show_progress(flux_iterable):
907+
if flux is None:
908+
continue
909+
910+
if isinstance(flux, tuple):
911+
flux, _ = flux
912+
913+
if B is None:
914+
n_bin = flux.size
915+
fgt0 = positive_fft_bins(n_bin)
916+
Nft = fgt0.stop - fgt0.start
917+
B = np.zeros((Nft, Nft), dtype=complex)
918+
B1 = np.zeros((Nft, Nft))
919+
B2 = np.zeros((Nft, Nft))
920+
freqs = np.fft.fftfreq(n_bin, dt)[fgt0]
921+
922+
ft = fft(flux)[fgt0]
923+
924+
M = 0
925+
for i in range(B.shape[0]):
926+
b1 = ft * ft[i]
927+
b2 = np.roll(ft, -i)
928+
B[i, :] += b1 * b2.conj()
929+
B1[i, :] += (b1 * b1.conj()).real
930+
B2[i, :] += (b2 * b2.conj()).real
931+
M += 1
932+
933+
BC = (B * B.conj()).real / (B1 * B2)
934+
935+
return freqs, B / M, BC
936+
937+
883938
def avg_pds_from_iterable(flux_iterable, dt, norm="frac", use_common_mean=True, silent=False):
884939
"""Calculate the average periodogram from an iterable of light curves
885940

0 commit comments

Comments
 (0)