Skip to content

Python FFT API

1-D and 2-D FFT backed by the vendored pocketfft (pure C99, libm-only). Each FFT / FFT2D instance owns an independent plan — no global state, thread-safe, multiple sizes coexist freely. CF64 transforms run natively; CF32 transforms are computed in double precision and returned as complex64.

Source: src/doppler/spectral/__init__.py


Dtype dispatch

Pass any dtype — the right C path is chosen automatically:

Input dtype C path Speed
complex64 CF32 → computed in double slower (float↔double conversion)
complex128 CF64 (native double) baseline

Examples

1-D FFT

from doppler.spectral import FFT
import numpy as np

f = FFT(1024)

# CF32 — single precision (~2× faster)
x32 = (np.random.randn(1024) + 1j * np.random.randn(1024)).astype(np.complex64)
X32 = f.execute(x32)
assert X32.dtype == np.complex64

# CF64 — double precision
x64 = np.random.randn(1024) + 1j * np.random.randn(1024)
X64 = f.execute(x64)
assert X64.dtype == np.complex128

# In-place
f.execute_inplace(x32)

Inverse FFT

fwd = FFT(1024, sign=-1)   # forward (default)
inv = FFT(1024, sign=+1)   # inverse

X = fwd.execute(x)
x_back = inv.execute(X)    # round-trip (unnormalised — divide by N)

2-D FFT

from doppler.spectral import FFT2D
import numpy as np

f2 = FFT2D(64, 64)
x = (np.random.randn(64, 64) + 1j * np.random.randn(64, 64)).astype(np.complex64)
X = f2.execute(x)
f2.execute_inplace(x)

Repeated transforms

f = FFT(1024)
for block in iq_stream:            # generator of complex64 arrays
    X = f.execute(block)           # plan reused; no re-allocation
    power = np.abs(X) ** 2

Multiple sizes in flight

small = FFT(256)
large = FFT(4096)
# Independent plans — coexist with no conflict

FFT

Allocate a reusable 1-D FFT engine for a fixed length and sign. Two pocketfft plans are created at construction time — one for CF64 and one for CF32 — so execute calls carry no plan-setup overhead. The same instance may be called repeatedly for independent input vectors of the same length. @p nthreads is accepted for API parity but is ignored; pocketfft plans are single-threaded.

Parameters:

Name Type Description Default
n int

n constructor parameter.

1024
sign int

sign constructor parameter.

-1
nthreads int

nthreads constructor parameter.

1

Examples:

Create with defaults:

>>> from doppler.spectral import FFT
>>> obj = FFT(n=1024, sign=-1, nthreads=1)

n property

n: int

N.

sign property

sign: int

Sign.

reset

reset() -> None

No-op reset (plans are immutable after creation).

execute_cf64

execute_cf64(x: complex) -> NDArray[np.complex128]

Compute an out-of-place 1-D DFT on a double-precision complex input. The output is written to a fresh caller-supplied buffer; @p in and @p out must not alias. The transform is unnormalised: the inverse DFT (sign=+1) does NOT divide by n. Both buffers must be exactly state->n elements long.

Parameters:

Name Type Description Default
x complex

Input.

required

Returns:

Type Description
NDArray[complex128]

n (number of samples written).

Examples:

>>> from doppler.spectral import FFT
>>> import numpy as np
>>> fft = FFT(n=4, sign=-1)
>>> x = np.array([1, 0, 0, 0], dtype=np.complex128)
>>> fft.execute_cf64(x).tolist()
[(1+0j), (1+0j), (1+0j), (1+0j)]

execute_cf32

execute_cf32(x: complex) -> NDArray[np.complex64]

Compute an out-of-place 1-D DFT on a single-precision complex input. Identical to fft_execute_cf64() but operates on float complex (CF32) buffers, halving memory bandwidth relative to the double-precision variant. Output is unnormalised; @p in and @p out must not alias.

Parameters:

Name Type Description Default
x complex

Input.

required

Returns:

Type Description
NDArray[complex64]

n (number of samples written).

Examples:

>>> from doppler.spectral import FFT
>>> import numpy as np
>>> fft = FFT(n=4, sign=-1)
>>> x = np.ones(4, dtype=np.complex64)
>>> fft.execute_cf32(x).tolist()
[(4+0j), 0j, 0j, 0j]

execute_inplace_cf64

execute_inplace_cf64(x: complex) -> NDArray[np.complex128]

Copy @p in into @p out, then transform @p out in-place (CF64). The copy step lets callers preserve their input while keeping the output buffer hot in cache. Semantically identical to fft_execute_cf64() for separate @p in / @p out pointers; use this variant when the caller already owns @p out and wants the result there without a second allocation.

Parameters:

Name Type Description Default
x complex

Input.

required

Returns:

Type Description
NDArray[complex128]

n (number of samples written).

Examples:

>>> from doppler.spectral import FFT
>>> import numpy as np
>>> fft = FFT(n=4, sign=-1)
>>> x = np.array([1, 0, 0, 0], dtype=np.complex128)
>>> fft.execute_inplace_cf64(x).tolist()
[(1+0j), (1+0j), (1+0j), (1+0j)]

execute_inplace_cf32

execute_inplace_cf32(x: complex) -> NDArray[np.complex64]

Copy @p in into @p out, then transform @p out in-place (CF32). Single-precision variant of fft_execute_inplace_cf64(). Copies state->n CF32 samples from @p in to @p out, then transforms @p out with the CF32 pocketfft plan. @p in is left unmodified.

Parameters:

Name Type Description Default
x complex

Input.

required

Returns:

Type Description
NDArray[complex64]

n (number of samples written).

Examples:

>>> from doppler.spectral import FFT
>>> import numpy as np
>>> fft = FFT(n=4, sign=-1)
>>> x = np.array([1, 0, 0, 0], dtype=np.complex64)
>>> fft.execute_inplace_cf32(x).tolist()
[(1+0j), (1+0j), (1+0j), (1+0j)]

destroy

destroy() -> None

Release C resources immediately.


FFT2D

Allocate a reusable 2-D FFT engine for a fixed ny×nx grid. Two pocketfft 2-D plans are built at construction time — one CF64, one CF32. All execute calls accept and return flat row-major arrays of length ny*nx; the Python layer may reshape them with .reshape(ny, nx). @p nthreads is accepted for API parity but ignored.

Parameters:

Name Type Description Default
ny int

ny constructor parameter.

64
nx int

nx constructor parameter.

64
sign int

sign constructor parameter.

-1
nthreads int

nthreads constructor parameter.

1

Examples:

Create with defaults:

>>> from doppler.spectral import FFT2D
>>> obj = FFT2D(ny=64, nx=64, sign=-1, nthreads=1)

ny property

ny: int

Ny.

nx property

nx: int

Nx.

sign property

sign: int

Sign.

reset

reset() -> None

No-op reset (plans are immutable after creation).

execute_cf64

execute_cf64(x: complex) -> NDArray[np.complex128]

Compute an out-of-place 2-D DFT on a double-precision complex grid. @p in is a flat row-major CF64 array of length nynx. The output is written to the caller-supplied @p out buffer (also nynx); the two must not alias. The transform is unnormalised.

Parameters:

Name Type Description Default
x complex

Input.

required

Returns:

Type Description
NDArray[complex128]

ny*nx (number of samples written).

Examples:

>>> from doppler.spectral import FFT2D
>>> import numpy as np
>>> fft2d = FFT2D(ny=4, nx=4, sign=-1)
>>> x = np.zeros(16, dtype=np.complex128); x[0] = 1.0
>>> out = fft2d.execute_cf64(x)
>>> out.shape, out.dtype
((16,), dtype('complex128'))
>>> bool(np.allclose(out, 1.0))
True

execute_cf32

execute_cf32(x: complex) -> NDArray[np.complex64]

Compute an out-of-place 2-D DFT on a single-precision complex grid. Single-precision variant of fft2d_execute_cf64(). Accepts and returns flat row-major CF32 arrays of length ny*nx. Output is unnormalised; @p in and @p out must not alias.

Parameters:

Name Type Description Default
x complex

Input.

required

Returns:

Type Description
NDArray[complex64]

ny*nx (number of samples written).

Examples:

>>> from doppler.spectral import FFT2D
>>> import numpy as np
>>> fft2d = FFT2D(ny=4, nx=4, sign=-1)
>>> x = np.zeros(16, dtype=np.complex64); x[0] = 1.0
>>> out = fft2d.execute_cf32(x)
>>> out.shape, out.dtype
((16,), dtype('complex64'))
>>> bool(np.allclose(out, 1.0))
True

execute_inplace_cf64

execute_inplace_cf64(x: complex) -> NDArray[np.complex128]

Copy @p in into @p out, then transform @p out in-place (CF64 2-D). The ny*nx CF64 samples from @p in are first memcpy'd to @p out; the 2-D DFT is then applied to @p out in-place. @p in is left unmodified. Useful when the caller owns @p out and wants to preserve @p in.

Parameters:

Name Type Description Default
x complex

Input.

required

Returns:

Type Description
NDArray[complex128]

ny*nx (number of samples written).

Examples:

>>> from doppler.spectral import FFT2D
>>> import numpy as np
>>> fft2d = FFT2D(ny=4, nx=4, sign=-1)
>>> x = np.zeros(16, dtype=np.complex128); x[0] = 1.0
>>> out = fft2d.execute_inplace_cf64(x)
>>> bool(np.allclose(out, 1.0))
True

execute_inplace_cf32

execute_inplace_cf32(x: complex) -> NDArray[np.complex64]

Copy @p in into @p out, then transform @p out in-place (CF32 2-D). Single-precision variant of fft2d_execute_inplace_cf64(). Copies ny*nx CF32 samples then applies the CF32 2-D pocketfft plan to @p out.

Parameters:

Name Type Description Default
x complex

Input.

required

Returns:

Type Description
NDArray[complex64]

ny*nx (number of samples written).

Examples:

>>> from doppler.spectral import FFT2D
>>> import numpy as np
>>> fft2d = FFT2D(ny=4, nx=4, sign=-1)
>>> x = np.zeros(16, dtype=np.complex64); x[0] = 1.0
>>> out = fft2d.execute_inplace_cf32(x)
>>> bool(np.allclose(out, 1.0))
True

destroy

destroy() -> None

Release C resources immediately.