Skip to content

Commit 9a438f6

Browse files
authored
Merge pull request #5 from fortran-fans/add_shift
Add `fftshift/ifftshift` utility function.
2 parents dcfce88 + f41698f commit 9a438f6

File tree

8 files changed

+267
-10
lines changed

8 files changed

+267
-10
lines changed

doc/specs/fftpack.md

Lines changed: 89 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Pure function.
3232
This argument is `intent(in)`.
3333
The length of the sequence to be transformed.
3434

35-
`wsave`: Shall be a `real` array.
35+
`wsave`: Shall be a `real` and rank-1 array.
3636
This argument is `intent(out)`.
3737
A work array which must be dimensioned at least `4*n+15`.
3838
The same work array can be used for both `zfftf` and `zfftb`
@@ -88,7 +88,7 @@ Pure function.
8888
This argument is `intent(in)`.
8989
The length of the `complex` sequence `c`. The method is more efficient when `n` is the product of small primes.
9090

91-
`c`: Shall be a `complex` array.
91+
`c`: Shall be a `complex` and rank-1 array.
9292
This argument is `intent(inout)`.
9393
A `complex` array of length `n` which contains the sequence.
9494
```
@@ -101,7 +101,7 @@ for j=1,...,n
101101
where i=sqrt(-1)
102102
```
103103

104-
`wsave`: Shall be a `real` array.
104+
`wsave`: Shall be a `real` and rank-1 array.
105105
This argument is `intent(inout)`.
106106
A `real` work array which must be dimensioned at least `4n+15` in the program that calls `zfftf`.
107107
The wsave array must be initialized by calling subroutine `zffti(n,wsave)` and a different `wsave` array must be used for each different value of `n`.
@@ -162,7 +162,7 @@ Pure function.
162162
This argument is `intent(in)`.
163163
The length of the `complex` sequence `c`. The method is more efficient when `n` is the product of small primes.
164164

165-
`c`: Shall be a `complex` array.
165+
`c`: Shall be a `complex` and rank-1 array.
166166
This argument is `intent(inout)`.
167167
A `complex` array of length `n` which contains the sequence.
168168
```
@@ -175,7 +175,7 @@ for j=1,...,n
175175
where i=sqrt(-1)
176176
```
177177

178-
`wsave`: Shall be a `real` array.
178+
`wsave`: Shall be a `real` and rank-1 array.
179179
This argument is `intent(inout)`.
180180
A `real` work array which must be dimensioned at least `4n+15` in the program that calls `zfftf`. The `wsave` array must be initialized by calling subroutine `zffti(n,wsave)` and a different `wsave` array must be used for each different value of `n`. This initialization does not have to be repeated so long as `n` remains unchanged thus subsequent transforms can be obtained faster than the first. The same `wsave` array can be used by `zfftf` and `zfftb`.
181181
Contains initialization calculations which must not be destroyed between calls of subroutine `zfftf` or `zfftb`.
@@ -218,7 +218,7 @@ Pure function.
218218

219219
### Argument
220220

221-
`x`: Shall be a `complex` array.
221+
`x`: Shall be a `complex` and rank-1 array.
222222
This argument is `intent(in)`.
223223

224224
`n`: Shall be an `integer` scalar.
@@ -270,7 +270,7 @@ Pure function.
270270

271271
### Argument
272272

273-
`x`: Shall be a `complex` array.
273+
`x`: Shall be a `complex` and rank-1 array.
274274
This argument is `intent(in)`.
275275

276276
`n`: Shall be an `integer` scalar.
@@ -303,3 +303,85 @@ program demo_ifft
303303
print *, ifft(fft(x), 3) !! [(6.0,2.0), (10.3,-1.0), (13.73,-1.0)]
304304
end program demo_ifft
305305
```
306+
307+
## `fftshift`
308+
309+
### Description
310+
311+
Rearranges the Fourier transform by moving the zero-frequency component to the center of the array.
312+
313+
### Status
314+
315+
Experimental.
316+
317+
### Class
318+
319+
Pure function.
320+
321+
### Snytax
322+
323+
`result = [[fftpack(module):fftshift(interface)]](x)`
324+
325+
### Argument
326+
327+
`x`: Shall be a `complex/real` and rank-1 array.
328+
This argument is `intent(in)`.
329+
330+
### Return value
331+
332+
Returns the `complex/real` and rank-1 Fourier transform by moving the zero-frequency component to the center of the array.
333+
334+
### Example
335+
336+
```fortran
337+
program demo_fftshift
338+
use fftpack, only: fftshift
339+
complex(kind=8) :: c(5) = [1, 2, 3, 4, 5]
340+
real(kind=8) :: x(5) = [1, 2, 3, 4, 5]
341+
print *, fftshift(c(1:4)) !! [(3.0,0.0), (4.0,0.0), (1.0,0.0), (2.0,0.0)]
342+
print *, fftshift(c) !! [(4.0,0.0), (5.0,0.0), (1.0,0.0), (2.0,0.0), (3.0,0.0)]
343+
print *, fftshift(x(1:4)) !! [3.0, 4.0, 1.0, 2.0]
344+
print *, fftshift(x) !! [4.0, 5.0, 1.0, 2.0, 3.0]
345+
end program demo_fftshift
346+
```
347+
348+
## `ifftshift`
349+
350+
### Description
351+
352+
Rearranges the Fourier transform with zero frequency shifting back to the original transform output. In other words, `ifftshift` is the result of undoing `fftshift`.
353+
354+
### Status
355+
356+
Experimental.
357+
358+
### Class
359+
360+
Pure function.
361+
362+
### Snytax
363+
364+
`result = [[fftpack(module):ifftshift(interface)]](x)`
365+
366+
### Argument
367+
368+
`x`: Shall be a `complex/real` and rank-1 array.
369+
This argument is `intent(in)`.
370+
371+
### Return value
372+
373+
Returns the `complex/real` and rank-1 Fourier transform with zero frequency shifting back to the original transform output.
374+
375+
### Example
376+
377+
```fortran
378+
program demo_ifftshift
379+
use fftpack, only: fftshift, ifftshift
380+
complex(kind=8) :: c(5) = [1, 2, 3, 4, 5]
381+
real(kind=8) :: x(5) = [1, 2, 3, 4, 5]
382+
print *, ifftshift(fftshift(c(1:4))) !! [(1.0,0.0), (2.0,0.0), (3.0,0.0), (4.0,0.0)]
383+
print *, ifftshift(fftshift(c) ) !! [(1.0,0.0), (2.0,0.0), (3.0,0.0), (4.0,0.0), (5.0,0.0)]
384+
print *, ifftshift(fftshift(x(1:4))) !! [1.0, 2.0, 3.0, 4.0]
385+
print *, ifftshift(fftshift(x)) !! [1.0, 2.0, 3.0, 4.0, 5.0]
386+
end program demo_ifftshift
387+
```

fpm.toml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,14 @@ main = "test_fftpack_fft.f90"
3333
[[test]]
3434
name = "fftpack_ifft"
3535
source-dir = "test"
36-
main = "test_fftpack_ifft.f90"
36+
main = "test_fftpack_ifft.f90"
37+
38+
[[test]]
39+
name = "fftpack_fftshift"
40+
source-dir = "test"
41+
main = "test_fftpack_fftshift.f90"
42+
43+
[[test]]
44+
name = "fftpack_ifftshift"
45+
source-dir = "test"
46+
main = "test_fftpack_ifftshift.f90"

src/fftpack.f90

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
module fftpack
22

3-
use iso_fortran_env, only: dp => real64
3+
use, intrinsic :: iso_fortran_env, only: dp => real64
44
implicit none
55
private
66

77
public :: zffti, zfftf, zfftb
88
public :: fft, ifft
9+
public :: fftshift, ifftshift
910

1011
interface
1112

@@ -67,4 +68,34 @@ pure module function ifft_cdp(x, n) result(result)
6768
end function ifft_cdp
6869
end interface ifft
6970

71+
!> Version: experimental
72+
!>
73+
!> Shifts zero-frequency component to center of spectrum.
74+
!> ([Specifiction](../page/specs/fftpack.html#fftshift))
75+
interface fftshift
76+
pure module function fftshift_cdp(x) result(result)
77+
complex(kind=dp), intent(in) :: x(:)
78+
complex(kind=dp), allocatable :: result(:)
79+
end function fftshift_cdp
80+
pure module function fftshift_rdp(x) result(result)
81+
real(kind=dp), intent(in) :: x(:)
82+
real(kind=dp), allocatable :: result(:)
83+
end function fftshift_rdp
84+
end interface fftshift
85+
86+
!> Version: experimental
87+
!>
88+
!> Shifts zero-frequency component to beginning of spectrum.
89+
!> ([Specifiction](../page/specs/fftpack.html#ifftshift))
90+
interface ifftshift
91+
pure module function ifftshift_cdp(x) result(result)
92+
complex(kind=dp), intent(in) :: x(:)
93+
complex(kind=dp), allocatable :: result(:)
94+
end function ifftshift_cdp
95+
pure module function ifftshift_rdp(x) result(result)
96+
real(kind=dp), intent(in) :: x(:)
97+
real(kind=dp), allocatable :: result(:)
98+
end function ifftshift_rdp
99+
end interface ifftshift
100+
70101
end module fftpack

src/fftpack_fftshift.f90

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
submodule(fftpack) fftpack_fftshift
2+
3+
contains
4+
5+
!> Shifts zero-frequency component to center of spectrum for `complex` type.
6+
pure module function fftshift_cdp(x) result(result)
7+
complex(kind=dp), intent(in) :: x(:)
8+
complex(kind=dp), allocatable :: result(:)
9+
10+
result = cshift(x, shift=-floor(0.5_dp*size(x)))
11+
12+
end function fftshift_cdp
13+
14+
!> Shifts zero-frequency component to center of spectrum for `real` type.
15+
pure module function fftshift_rdp(x) result(result)
16+
real(kind=dp), intent(in) :: x(:)
17+
real(kind=dp), allocatable :: result(:)
18+
19+
result = cshift(x, shift=-floor(0.5_dp*size(x)))
20+
21+
end function fftshift_rdp
22+
23+
end submodule fftpack_fftshift

src/fftpack_ifftshift.f90

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
submodule(fftpack) fftpack_ifftshift
2+
3+
contains
4+
5+
!> Shifts zero-frequency component to beginning of spectrum for `complex` type.
6+
pure module function ifftshift_cdp(x) result(result)
7+
complex(kind=dp), intent(in) :: x(:)
8+
complex(kind=dp), allocatable :: result(:)
9+
10+
result = cshift(x, shift=-ceiling(0.5_dp*size(x)))
11+
12+
end function ifftshift_cdp
13+
14+
!> Shifts zero-frequency component to beginning of spectrum for `real` type.
15+
pure module function ifftshift_rdp(x) result(result)
16+
real(kind=dp), intent(in) :: x(:)
17+
real(kind=dp), allocatable :: result(:)
18+
19+
result = cshift(x, shift=-ceiling(0.5_dp*size(x)))
20+
21+
end function ifftshift_rdp
22+
23+
end submodule fftpack_ifftshift

test/test_fftpack_fftshift.f90

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
program tester
2+
3+
call test_fftpack_fftshift_complex
4+
call test_fftpack_fftshift_real
5+
print *, "All tests in `test_fftpack_fftshift` passed."
6+
7+
contains
8+
9+
subroutine check(condition, msg)
10+
logical, intent(in) :: condition
11+
character(*), intent(in) :: msg
12+
if (.not. condition) error stop msg
13+
end subroutine check
14+
15+
subroutine test_fftpack_fftshift_complex
16+
use fftpack, only: fftshift
17+
use iso_fortran_env, only: dp => real64
18+
19+
complex(kind=dp) :: xeven(4) = [1, 2, 3, 4]
20+
complex(kind=dp) :: xodd(5) = [1, 2, 3, 4, 5]
21+
22+
call check(all(fftshift(xeven) == [complex(kind=dp) :: 3, 4, 1, 2]), &
23+
msg="all(fftshift(xeven) == [complex(kind=dp) :: 3, 4, 1, 2]) failed.")
24+
call check(all(fftshift(xodd) == [complex(kind=dp) :: 4, 5, 1, 2, 3]), &
25+
msg="all(fftshift(xodd) == [complex(kind=dp) :: 4, 5, 1, 2, 3]) failed.")
26+
27+
end subroutine test_fftpack_fftshift_complex
28+
29+
subroutine test_fftpack_fftshift_real
30+
use fftpack, only: fftshift
31+
use iso_fortran_env, only: dp => real64
32+
33+
real(kind=dp) :: xeven(4) = [1, 2, 3, 4]
34+
real(kind=dp) :: xodd(5) = [1, 2, 3, 4, 5]
35+
36+
call check(all(fftshift(xeven) == [real(kind=dp) :: 3, 4, 1, 2]), &
37+
msg="all(fftshift(xeven) == [real(kind=dp) :: 3, 4, 1, 2]) failed.")
38+
call check(all(fftshift(xodd) == [real(kind=dp) :: 4, 5, 1, 2, 3]), &
39+
msg="all(fftshift(xodd) == [real(kind=dp) :: 4, 5, 1, 2, 3]) failed.")
40+
41+
end subroutine test_fftpack_fftshift_real
42+
43+
end program tester

test/test_fftpack_ifft.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
program tester
22

33
call test_fftpack_ifft()
4-
print *, "All tests in `test_fftpack_fft` passed."
4+
print *, "All tests in `test_fftpack_ifft` passed."
55

66
contains
77

test/test_fftpack_ifftshift.f90

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
program tester
2+
3+
call test_fftpack_ifftshift_complex
4+
call test_fftpack_ifftshift_real
5+
print *, "All tests in `test_fftpack_ifftshift` passed."
6+
7+
contains
8+
9+
subroutine check(condition, msg)
10+
logical, intent(in) :: condition
11+
character(*), intent(in) :: msg
12+
if (.not. condition) error stop msg
13+
end subroutine check
14+
15+
subroutine test_fftpack_ifftshift_complex
16+
use fftpack, only: ifftshift
17+
use iso_fortran_env, only: dp => real64
18+
integer :: i
19+
20+
complex(kind=dp) :: xeven(4) = [3, 4, 1, 2]
21+
complex(kind=dp) :: xodd(5) = [4, 5, 1, 2, 3]
22+
23+
call check(all(ifftshift(xeven) == [complex(kind=dp) ::(i, i=1, 4)]), &
24+
msg="all(ifftshift(xeven) == [complex(kind=dp) ::(i, i=1, 4)]) failed.")
25+
call check(all(ifftshift(xodd) == [complex(kind=dp) ::(i, i=1, 5)]), &
26+
msg="all(ifftshift(xodd) == [complex(kind=dp) ::(i, i=1, 5)]) failed.")
27+
28+
end subroutine test_fftpack_ifftshift_complex
29+
30+
subroutine test_fftpack_ifftshift_real
31+
use fftpack, only: ifftshift
32+
use iso_fortran_env, only: dp => real64
33+
integer :: i
34+
35+
real(kind=dp) :: xeven(4) = [3, 4, 1, 2]
36+
real(kind=dp) :: xodd(5) = [4, 5, 1, 2, 3]
37+
38+
call check(all(ifftshift(xeven) == [real(kind=dp) ::(i, i=1, 4)]), &
39+
msg="all(ifftshift(xeven) == [real(kind=dp) ::(i, i=1, 4)]) failed.")
40+
call check(all(ifftshift(xodd) == [real(kind=dp) ::(i, i=1, 5)]), &
41+
msg="all(ifftshift(xodd) == [real(kind=dp) ::(i, i=1, 5)]) failed.")
42+
43+
end subroutine test_fftpack_ifftshift_real
44+
45+
end program tester

0 commit comments

Comments
 (0)