Repository: StingraySoftware/stingray

Issue:
pyfftw3 warning occurs three times
When importing Stingray, it throws the "pyfftw3 not installed, using built-in scipy fft" warning 3 times: once in fourier.py, once in bispectrum.py, and once in crosscorrelation.py. It would probably be better to only throw it once in fourier.py so as to not overwhelm the user with triplicate information.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/StingraySoftware/stingray/pull/643?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware) Report
> Merging [#643](https://codecov.io/gh/StingraySoftware/stingray/pull/643?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware) (b736e34) into [main](https://codecov.io/gh/StingraySoftware/stingray/commit/4e694cbafdb87e862e614e9c6eb2d75046b13b9e?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware) (4e694cb) will **decrease** coverage by `6.83%`.
> The diff coverage is `78.57%`.

[![Impacted file tree graph](https://codecov.io/gh/StingraySoftware/stingray/pull/643/graphs/tree.svg?width=650&height=150&src=pr&token=FjWeFfhU9F&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware)](https://codecov.io/gh/StingraySoftware/stingray/pull/643?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware)

```diff
@@            Coverage Diff             @@
##             main     #643      +/-   ##
==========================================
- Coverage   97.22%   90.38%   -6.84%     
==========================================
  Files          41       41              
  Lines        7555     7537      -18     
==========================================
- Hits         7345     6812     -533     
- Misses        210      725     +515     
```


| [Impacted Files](https://codecov.io/gh/StingraySoftware/stingray/pull/643?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware) | Coverage Δ | |
|---|---|---|
| [stingray/utils.py](https://codecov.io/gh/StingraySoftware/stingray/pull/643/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware#diff-c3RpbmdyYXkvdXRpbHMucHk=) | `95.19% <66.66%> (-3.79%)` | :arrow_down: |
| [stingray/bispectrum.py](https://codecov.io/gh/StingraySoftware/stingray/pull/643/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware#diff-c3RpbmdyYXkvYmlzcGVjdHJ1bS5weQ==) | `94.03% <100.00%> (-0.16%)` | :arrow_down: |
| [stingray/crosscorrelation.py](https://codecov.io/gh/StingraySoftware/stingray/pull/643/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware#diff-c3RpbmdyYXkvY3Jvc3Njb3JyZWxhdGlvbi5weQ==) | `96.46% <100.00%> (-0.13%)` | :arrow_down: |
| [stingray/fourier.py](https://codecov.io/gh/StingraySoftware/stingray/pull/643/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware#diff-c3RpbmdyYXkvZm91cmllci5weQ==) | `99.72% <100.00%> (-0.01%)` | :arrow_down: |
| [stingray/pulse/accelsearch.py](https://codecov.io/gh/StingraySoftware/stingray/pull/643/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware#diff-c3RpbmdyYXkvcHVsc2UvYWNjZWxzZWFyY2gucHk=) | `93.14% <100.00%> (-0.30%)` | :arrow_down: |
| [stingray/pulse/overlapandsave/test\_ols.py](https://codecov.io/gh/StingraySoftware/stingray/pull/643/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware#diff-c3RpbmdyYXkvcHVsc2Uvb3ZlcmxhcGFuZHNhdmUvdGVzdF9vbHMucHk=) | `68.62% <100.00%> (-31.38%)` | :arrow_down: |
| [stingray/largememory.py](https://codecov.io/gh/StingraySoftware/stingray/pull/643/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware#diff-c3RpbmdyYXkvbGFyZ2VtZW1vcnkucHk=) | `10.99% <0.00%> (-84.05%)` | :arrow_down: |
| [stingray/pulse/\_\_init\_\_.py](https://codecov.io/gh/StingraySoftware/stingray/pull/643/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware#diff-c3RpbmdyYXkvcHVsc2UvX19pbml0X18ucHk=) | `76.92% <0.00%> (-23.08%)` | :arrow_down: |
| [stingray/base.py](https://codecov.io/gh/StingraySoftware/stingray/pull/643/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware#diff-c3RpbmdyYXkvYmFzZS5weQ==) | `79.27% <0.00%> (-20.73%)` | :arrow_down: |
| ... and [11 more](https://codecov.io/gh/StingraySoftware/stingray/pull/643/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/StingraySoftware/stingray/pull/643?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/StingraySoftware/stingray/pull/643?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware). Last update [4e694cb...b736e34](https://codecov.io/gh/StingraySoftware/stingray/pull/643?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=StingraySoftware).


Patch:
diff --git a/stingray/bispectrum.py b/stingray/bispectrum.py
index 693207ff..c19221d3 100644
--- a/stingray/bispectrum.py
+++ b/stingray/bispectrum.py
@@ -5,16 +5,11 @@ from scipy.linalg import toeplitz
 import warnings
 import matplotlib.pyplot as plt
 
-try:
-    from pyfftw.interfaces.scipy_fft import fftshift, fft2, ifftshift, fft
-except ImportError:
-    warnings.warn("pyfftw not installed. Using standard scipy fft")
-    from scipy.fft import fftshift, fft2, ifftshift, fft
-
 from  scipy.linalg import hankel
 
 from stingray import lightcurve
 import stingray.utils as utils
+from stingray.utils import fftshift, fft2, ifftshift, fft
 
 __all__ = ["Bispectrum"]
 
diff --git a/stingray/crosscorrelation.py b/stingray/crosscorrelation.py
index 0b238162..bb42cc03 100644
--- a/stingray/crosscorrelation.py
+++ b/stingray/crosscorrelation.py
@@ -2,12 +2,7 @@ import warnings
 import numpy as np
 from scipy import signal
 import matplotlib.pyplot as plt
-
-try:
-    from pyfftw.interfaces.scipy_fft import ifft, fftfreq
-except ImportError:
-    warnings.warn("pyfftw not installed. Using standard scipy fft")
-    from scipy.fft import ifft, fftfreq
+from stingray.utils import ifft, fftfreq
 
 from stingray.lightcurve import Lightcurve
 from stingray.crossspectrum import Crossspectrum, AveragedCrossspectrum
diff --git a/stingray/fourier.py b/stingray/fourier.py
index 544b9fdd..9dc77070 100644
--- a/stingray/fourier.py
+++ b/stingray/fourier.py
@@ -7,16 +7,7 @@ from astropy.table import Table
 
 from .gti import (generate_indices_of_segment_boundaries_binned,
                   generate_indices_of_segment_boundaries_unbinned)
-from .utils import histogram, show_progress, sum_if_not_none_or_initialize
-
-try:
-    import pyfftw
-    from pyfftw.interfaces.numpy_fft import fft, fftfreq
-
-    pyfftw.interfaces.cache.enable()
-except ImportError:
-    warnings.warn("pyfftw not installed. Using standard scipy fft")
-    from scipy.fft import fft, fftfreq
+from .utils import histogram, show_progress, sum_if_not_none_or_initialize, fft, fftfreq
 
 
 def positive_fft_bins(n_bin, include_zero=False):
diff --git a/stingray/multitaper.py b/stingray/multitaper.py
index f63b2c04..23b8477b 100644
--- a/stingray/multitaper.py
+++ b/stingray/multitaper.py
@@ -6,16 +6,15 @@ from stingray.powerspectrum import Powerspectrum
 import warnings
 
 import numpy as np
-import scipy
 import scipy.optimize
 import scipy.stats
-from scipy import signal, fft, interpolate
+from scipy import signal, interpolate
 
 from astropy.timeseries import LombScargle
 
 from .events import EventList
 from .lightcurve import Lightcurve
-from .utils import rebin_data, simon
+from .utils import rebin_data, simon, fft, rfft, rfftfreq
 
 __all__ = [
     "Multitaper"
@@ -335,7 +334,7 @@ class Multitaper(Powerspectrum):
         data_multitaper = np.tile(data_multitaper, (len(eigvals), 1))
         data_multitaper = np.multiply(data_multitaper, dpss_tapers)
 
-        freq_response = scipy.fft.rfft(data_multitaper, n=lc.n)
+        freq_response = rfft(data_multitaper, n=lc.n)
 
         # Adjust DC and maybe Nyquist, depending on one-sided transform
         freq_response[..., 0] /= np.sqrt(2.)
@@ -343,7 +342,7 @@ class Multitaper(Powerspectrum):
             freq_response[..., -1] /= np.sqrt(2.)
 
         freq_response = freq_response[..., 1:-1]
-        freq_multitaper = scipy.fft.rfftfreq(lc.n, d=lc.dt)[1:-1]
+        freq_multitaper = rfftfreq(lc.n, d=lc.dt)[1:-1]
 
         if adaptive:
             psd_multitaper, weights_multitaper = \
@@ -767,7 +766,7 @@ class Multitaper(Powerspectrum):
         # freq_mtls = freq_mtls[1:]
 
         # The frequencies rest of Stingray uses
-        freq_multitaper_ls = fft.rfftfreq(
+        freq_multitaper_ls = rfftfreq(
             n=lc.n, d=lc.dt)[1:-1]  # Avoiding zero
 
         for values in dpss_data_interpolated:
diff --git a/stingray/pulse/accelsearch.py b/stingray/pulse/accelsearch.py
index 644b41f7..98856f66 100644
--- a/stingray/pulse/accelsearch.py
+++ b/stingray/pulse/accelsearch.py
@@ -17,18 +17,9 @@ except ImportError:
     def show_progress(a, **kwargs):
         return a
 
-try:
-    import pyfftw
-    from pyfftw.interfaces.numpy_fft import fft, fftfreq, fftn, ifftn
-    HAS_PYFFTW = True
-except ImportError:
-    warnings.warn("Using standard numpy fft")
-    from scipy.fft import fft, fftfreq, fftn, ifftn
-    HAS_PYFFTW = False
-
 from stingray.pulse.overlapandsave.ols import ols
 
-from ..utils import njit
+from ..utils import njit, fft, fftfreq, fftn, ifftn, HAS_PYFFTW
 from ..stats import pds_probability, pds_detection_level
 from ..gti import create_gti_mask
 
diff --git a/stingray/spectroscopy.py b/stingray/spectroscopy.py
index 014f2293..d5ec7c6f 100644
--- a/stingray/spectroscopy.py
+++ b/stingray/spectroscopy.py
@@ -1,13 +1,12 @@
 import numpy as np
 from scipy.stats import binned_statistic
-from scipy.fft import fft, ifft
 from scipy.optimize import brent
 
 
 from astropy.table import Table
 from astropy.modeling.models import Lorentz1D
 from stingray import Lightcurve, Crossspectrum
-from stingray.utils import standard_error, find_nearest
+from stingray.utils import standard_error, find_nearest, fft, ifft
 from stingray.filters import Optimal1D, Window1D
 
 
diff --git a/stingray/utils.py b/stingray/utils.py
index 2b6123fd..3830a1ad 100644
--- a/stingray/utils.py
+++ b/stingray/utils.py
@@ -14,6 +14,19 @@ from numpy import histogram as histogram_np
 from numpy import histogram2d as histogram2d_np
 from .base import interpret_times
 
+try:
+    import pyfftw
+    from pyfftw.interfaces.numpy_fft import (
+        ifft, fft, fftfreq, fftn, ifftn, fftshift, fft2, ifftshift, rfft, rfftfreq)
+
+    pyfftw.interfaces.cache.enable()
+    HAS_PYFFTW = True
+except ImportError:
+    warnings.warn("pyfftw not installed. Using standard scipy fft")
+    from numpy.fft import (
+        ifft, fft, fftfreq, fftn, ifftn, fftshift, fft2, ifftshift, rfft, rfftfreq)
+    HAS_PYFFTW = False
+
 
 # If numba is installed, import jit. Otherwise, define an empty decorator with
 # the same name.


Test patch:
diff --git a/stingray/pulse/overlapandsave/test_ols.py b/stingray/pulse/overlapandsave/test_ols.py
index b68cdecc..964e2d3a 100644
--- a/stingray/pulse/overlapandsave/test_ols.py
+++ b/stingray/pulse/overlapandsave/test_ols.py
@@ -3,11 +3,7 @@ import numpy as np
 from scipy.signal import fftconvolve
 from .ols import prepareh, olsStep, ols, nextprod
 
-try:
-    from pyfftw.interfaces.numpy_fft import fft, fftfreq, fftn, ifftn
-    HAS_PYFFTW = True
-except ImportError:
-    HAS_PYFFTW = False
+from stingray.utils import fft, fftfreq, fftn, ifftn, HAS_PYFFTW
 
 
 @pytest.mark.skipif("not HAS_PYFFTW")
diff --git a/stingray/tests/test_crosscorrelation.py b/stingray/tests/test_crosscorrelation.py
index 07052fe5..4625c2bc 100644
--- a/stingray/tests/test_crosscorrelation.py
+++ b/stingray/tests/test_crosscorrelation.py
@@ -1,5 +1,4 @@
 import numpy as np
-import scipy.fft
 
 import pytest
 import warnings
@@ -9,6 +8,7 @@ from stingray import Lightcurve
 from stingray import Crossspectrum
 from stingray.crosscorrelation import CrossCorrelation, AutoCorrelation
 from stingray.exceptions import StingrayError
+from stingray.utils import ifft, fftfreq
 
 import matplotlib.pyplot as plt
 
@@ -100,9 +100,9 @@ class TestCrossCorrelation(object):
     def test_crossparam_input(self):
         # need to create new results to check against
         spec = Crossspectrum(self.lc1, self.lc2, fullspec=True)
-        ifft = abs(scipy.fft.ifft(spec.power).real)
-        time = scipy.fft.fftfreq(len(ifft), spec.df)
-        time, resultifft = (list(t) for t in zip(*sorted(zip(time, ifft))))
+        iff = abs(ifft(spec.power).real)
+        time = fftfreq(len(iff), spec.df)
+        time, resultifft = (list(t) for t in zip(*sorted(zip(time, iff))))
         cr2 = CrossCorrelation(cross=spec)
         lags_result = np.array([-2, -1, 0, 1, 2])
 
diff --git a/stingray/tests/test_spectroscopy.py b/stingray/tests/test_spectroscopy.py
index 49c13d0e..d2e00577 100644
--- a/stingray/tests/test_spectroscopy.py
+++ b/stingray/tests/test_spectroscopy.py
@@ -3,9 +3,9 @@ import pytest
 import stingray.spectroscopy as spec
 from stingray.filters import Window1D
 from stingray import Lightcurve, AveragedPowerspectrum, AveragedCrossspectrum, Powerspectrum, Crossspectrum
+from stingray.utils import fft
 from stingray.modeling import fit_lorentzians, fit_crossspectrum, fit_powerspectrum
 from stingray.filters import Optimal1D, Window1D
-from scipy.fft import fft
 from astropy.modeling import models
 from astropy.table import Table, Column