Inconsistency in ifft of FrequencySeries
Created by: MaxMelching
I am using gwpy 3.0.7 (Python 3.11.6) and noticed the following when using Fourier transformations: the f=0-component seems to be treated inconsistently. In TimeSeries.fft(), we multiply every Fourier coefficient by 2, except for the f=0 one (understandably so). In FrequencySeries.ifft(), however, every Fourier coefficient seems to be divided by 2, including the f=0 one (at least that is my understanding of the code).
Here is an example:
from gwpy.timeseries import TimeSeries
from gwpy.frequencyseries import FrequencySeries
timeseries1 = TimeSeries([1.0, 0.0, -1.0, 0.0], sample_rate=1.0)
print(f'first time series:')
print(timeseries1)
print(f'ifft of fft of first time series:')
print(timeseries1.fft().ifft())
print('----------------------------------------')
timeseries2 = TimeSeries([1.0, 2.0, 3.0, 2.0, 1.0, 0.0], sample_rate=1.0)
print(f'second time series:')
print(timeseries2)
print(f'ifft of fft of second time series:')
print(timeseries2.fft().ifft())
print('----------------------------------------')
frequencyseries1 = FrequencySeries([1.0, 0.0, -1.0, 0.0], df=1.0)
print(f'first frequency series:')
print(frequencyseries1)
print(f'fft of ifft of first frequency series')
print(frequencyseries1.ifft().fft())
print('----------------------------------------')
frequencyseries2 = FrequencySeries([1.0, 2.0, 3.0, 4.0], df=1.0)
print(f'second frequency series:')
print(frequencyseries2)
print(f'fft of ifft of second frequency series:')
print(frequencyseries2.ifft().fft())
which gives the following output:
first time series:
TimeSeries([ 1., 0., -1., 0.]
unit: dimensionless,
t0: 0.0 s,
dt: 1.0 s,
name: None,
channel: None)
ifft of fft of first time series:
TimeSeries([ 1., 0., -1., 0.]
unit: dimensionless,
t0: 0.0 1 / Hz,
dt: 1.0 1 / Hz,
name: None,
channel: None)
----------------------------------------
second time series:
TimeSeries([1., 2., 3., 2., 1., 0.]
unit: dimensionless,
t0: 0.0 s,
dt: 1.0 s,
name: None,
channel: None)
ifft of fft of second time series:
TimeSeries([ 0.25, 1.25, 2.25, 1.25, 0.25, -0.75]
unit: dimensionless,
t0: 0.0 1 / Hz,
dt: 1.0 1 / Hz,
name: None,
channel: None)
----------------------------------------
first frequency series:
FrequencySeries([ 1., 0., -1., 0.]
unit: dimensionless,
f0: 0.0 Hz,
df: 1.0 Hz,
epoch: None,
name: None,
channel: None)
fft of ifft of first frequency series
FrequencySeries([ 0.5+0.00000000e+00j, 0. -3.70074342e-17j,
-1. -3.70074342e-17j, 0. +0.00000000e+00j]
unit: dimensionless,
f0: 0.0 Hz,
df: 1.0 Hz,
epoch: 0.0,
name: None,
channel: None)
----------------------------------------
second frequency series:
FrequencySeries([1., 2., 3., 4.]
unit: dimensionless,
f0: 0.0 Hz,
df: 1.0 Hz,
epoch: None,
name: None,
channel: None)
fft of ifft of second frequency series:
FrequencySeries([0.5+0.00000000e+00j, 2. +3.70074342e-17j,
3. +3.70074342e-17j, 4. +0.00000000e+00j]
unit: dimensionless,
f0: 0.0 Hz,
df: 1.0 Hz,
epoch: 0.0,
name: None,
channel: None)
Replacing
dift = npfft.irfft(self.value * nout) / 2
in the ifft method with something like
coeffs = self.value.copy()
coeffs[1:] = coeffs[1:] / 2
dift = npfft.irfft(coeffs * nout)
seems to solve the issue, at least for this example code (although I am not sure how good this solution actually is with respect to "good" coding; perhaps, we could also divide the dift
accordingly). This is also what my explanation/interpretation above is based on.