Octopus
finufft.F90
Go to the documentation of this file.
1!! Copyright (C) 2026 Octopus developers
2!!
3!! This program is free software; you can redistribute it and/or modify
4!! it under the terms of the GNU General Public License as published by
5!! the Free Software Foundation; either version 2, or (at your option)
6!! any later version.
7!!
8!! This program is distributed in the hope that it will be useful,
9!! but WITHOUT ANY WARRANTY; without even the implied warranty of
10!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11!! GNU General Public License for more details.
12!!
13!! You should have received a copy of the GNU General Public License
14!! along with this program; if not, write to the Free Software
15!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16!! 02110-1301, USA.
17!!
18
19
20#include "global.h"
21
23module finufft_oct_m
24 use debug_oct_m
25 use global_oct_m
26 use, intrinsic :: iso_c_binding
29 use parser_oct_m
31 implicit none
32
33 private
34
35 public :: &
36 finufft_t, &
46
47#ifdef HAVE_NFFT
48
51 integer, parameter :: FINUFFT_TYPE2 = 2
52 integer, parameter :: FINUFFT_IFLAG = -1
53#endif
54
55 type finufft_t
56 private
57
58 integer :: N(3)
59 integer :: M(3)
60 integer :: dim
61 real(real64), public :: norm
62
63 real(real64) :: tol
64
65 type(c_ptr) :: plan = c_null_ptr
66
69 real(real64), pointer :: xj(:) => null()
70 real(real64), pointer :: yj(:) => null()
71 real(real64), pointer :: zj(:) => null()
72
73 end type finufft_t
74
75#ifdef HAVE_NFFT
76
77 interface
78
79 integer(c_int) function finufft_makeplan(type_, dim, n_modes, iflag, n_transf, tol, plan, opts) &
80 bind(c, name='finufft_makeplan')
81 import :: c_int, c_int64_t, c_double, c_ptr
82 integer(c_int), value :: type_
83 integer(c_int), value :: dim
84 integer(c_int64_t), intent(in) :: n_modes(*)
85 integer(c_int), value :: iflag
86 integer(c_int), value :: n_transf
87 real(c_double), value :: tol
88 type(c_ptr), intent(out) :: plan
89 type(c_ptr), value :: opts
90 end function finufft_makeplan
91
92 integer(c_int) function finufft_setpts(plan, M, xj, yj, zj, N, s, t, u) &
93 bind(c, name='finufft_setpts')
94 import :: c_int, c_int64_t, c_ptr
95 type(c_ptr), value :: plan
96 integer(c_int64_t), value :: M
97 type(c_ptr), value :: xj, yj, zj
98 integer(c_int64_t), value :: N
99 type(c_ptr), value :: s, t, u
100 end function finufft_setpts
101
102 integer(c_int) function finufft_execute(plan, cj, fk) bind(c, name='finufft_execute')
103 import :: c_int, c_ptr, c_double_complex
104 type(c_ptr), value :: plan
105 complex(c_double_complex), intent(inout) :: cj(*)
106 complex(c_double_complex), intent(in) :: fk(*)
107 end function finufft_execute
108
109 integer(c_int) function finufft_execute_adjoint(plan, cj, fk) bind(c, name='finufft_execute_adjoint')
110 import :: c_int, c_ptr, c_double_complex
111 type(c_ptr), value :: plan
112 complex(c_double_complex), intent(in) :: cj(*)
113 complex(c_double_complex), intent(inout) :: fk(*)
114 end function finufft_execute_adjoint
115
116 integer(c_int) function finufft_destroy(plan) bind(c, name='finufft_destroy')
117 import :: c_int, c_ptr
118 type(c_ptr), value :: plan
119 end function finufft_destroy
120
121 end interface
122#endif
123
124
125contains
126
127 ! ---------------------------------------------------------
128 subroutine finufft_read_options(finufft, namespace)
129 type(finufft_t), intent(inout) :: finufft
130 type(namespace_t), intent(in) :: namespace
131
132 push_sub(finufft_read_options)
133
134 !%Variable FINUFFTTolerance
135 !%Type float
136 !%Default 1e-8
137 !%Section Mesh::FFTs
138 !%Description
139 !% Requested relative tolerance of the non-equispaced FFTs. Tighter
140 !% tolerances widen the spreading kernel and are more expensive.
141 !%End
142 call parse_variable(namespace, 'FINUFFTTolerance', 1.0e-8_real64, finufft%tol)
143
144 if (finufft%tol <= m_zero) then
145 call messages_input_error(namespace, 'FINUFFTTolerance')
146 end if
149 end subroutine finufft_read_options
151 ! ---------------------------------------------------------
152 subroutine finufft_init(finufft, finufft_options, N, dim, M)
153 type(finufft_t), intent(inout) :: finufft
154 type(finufft_t), intent(in) :: finufft_options
155 integer, intent(in) :: n(3)
156 integer, intent(in) :: dim
157 integer, intent(in) :: m(3)
159#ifdef HAVE_NFFT
160 integer(c_int64_t) :: n_modes(3)
161 integer(c_int) :: ier
162 type(c_ptr) :: default_opts
163#endif
164
165 push_sub(finufft_init)
166
167 call profiling_in("FINUFFT_INIT")
168
169 finufft%dim = dim
170 finufft%N(:) = n(:)
171 finufft%M(:) = m(:)
172 ! unused directions hold a single mode and a single node
173 finufft%N(dim+1:3) = 1
174 finufft%M(dim+1:3) = 1
175
176 finufft%tol = finufft_options%tol
177
178#ifdef HAVE_NFFT
179 n_modes(1:3) = int(finufft%N(1:3), c_int64_t)
180
181 ! Selects the FINUFFT defaults, including the mode ordering relied on here
182 ! (modeord = 0, modes -N/2 .. N/2-1)
183 default_opts = c_null_ptr
184
185 ier = finufft_makeplan(int(finufft_type2, c_int), int(dim, c_int), n_modes, &
186 int(finufft_iflag, c_int), 1_c_int, real(finufft%tol, c_double), finufft%plan, default_opts)
187 call finufft_check(ier, 'finufft_makeplan')
188#endif
190 call profiling_out("FINUFFT_INIT")
191
192 pop_sub(finufft_init)
193 end subroutine finufft_init
194
195 ! ---------------------------------------------------------
201 subroutine finufft_precompute(finufft, X1, X2, X3)
202 type(finufft_t), intent(inout) :: finufft
203 real(real64), intent(in) :: x1(:)
204 real(real64), optional, intent(in) :: x2(:)
205 real(real64), optional, intent(in) :: x3(:)
206
207 real(real64) :: v1(1:finufft%m(1)), v2(1:finufft%m(2)), v3(1:finufft%m(3))
208 real(real64) :: inv_spacing
209 integer(c_int64_t) :: nj
210 integer :: i1, i2, i3, jj
211#ifdef HAVE_NFFT
212 integer(c_int) :: ier
213 type(c_ptr) :: yj_ptr, zj_ptr
214#endif
215
216 push_sub(finufft_precompute)
217
218 call profiling_in("FINUFFT_PRECOMPUTE")
219
220 finufft%norm = m_one
221
222 assert(size(x1) == finufft%M(1))
223 call finufft_scale_nodes(x1, v1, inv_spacing)
224 finufft%norm = finufft%norm * inv_spacing
225
227 if (finufft%dim >= 2) then
228 assert(present(x2))
229 assert(size(x2) == finufft%M(2))
230 call finufft_scale_nodes(x2, v2, inv_spacing)
231 finufft%norm = finufft%norm * inv_spacing
232 end if
233
234 v3 = m_zero
235 if (finufft%dim >= 3) then
236 assert(present(x3))
237 assert(size(x3) == finufft%M(3))
238 call finufft_scale_nodes(x3, v3, inv_spacing)
239 finufft%norm = finufft%norm * inv_spacing
240 end if
241
242 nj = int(finufft%M(1), c_int64_t)*int(finufft%M(2), c_int64_t)*int(finufft%M(3), c_int64_t)
243
244 safe_deallocate_p(finufft%xj)
245 safe_deallocate_p(finufft%yj)
246 safe_deallocate_p(finufft%zj)
247 safe_allocate(finufft%xj(1:nj))
248 if (finufft%dim >= 2) then
249 safe_allocate(finufft%yj(1:nj))
250 end if
251 if (finufft%dim >= 3) then
252 safe_allocate(finufft%zj(1:nj))
253 end if
254
255 ! Fortran order, matching the transform arrays, so execute needs no repacking
256 jj = 0
257 do i3 = 1, finufft%M(3)
258 do i2 = 1, finufft%M(2)
259 do i1 = 1, finufft%M(1)
260 jj = jj + 1
261 finufft%xj(jj) = m_two*m_pi*v1(i1)
262 if (finufft%dim >= 2) finufft%yj(jj) = m_two*m_pi*v2(i2)
263 if (finufft%dim >= 3) finufft%zj(jj) = m_two*m_pi*v3(i3)
264 end do
265 end do
266 end do
267
268#ifdef HAVE_NFFT
269 yj_ptr = c_null_ptr
270 zj_ptr = c_null_ptr
271 if (finufft%dim >= 2) yj_ptr = c_loc(finufft%yj)
272 if (finufft%dim >= 3) zj_ptr = c_loc(finufft%zj)
273
274 ! The trailing arguments only apply to type 3
275 ier = finufft_setpts(finufft%plan, nj, c_loc(finufft%xj), yj_ptr, zj_ptr, &
276 0_c_int64_t, c_null_ptr, c_null_ptr, c_null_ptr)
277 call finufft_check(ier, 'finufft_setpts')
278#endif
279
280 call profiling_out("FINUFFT_PRECOMPUTE")
281
282 write(message(1), '(a)') "Info: FINUFFT plan precomputed."
283 call messages_info(1)
284
285 pop_sub(finufft_precompute)
286 end subroutine finufft_precompute
287
288 ! ---------------------------------------------------------
291 subroutine finufft_scale_nodes(X, v, inv_spacing)
292 real(real64), intent(in) :: x(:)
293 real(real64), intent(out) :: v(:)
294 real(real64), intent(out) :: inv_spacing
295
296 real(real64) :: length, cc
297 real(real64) :: spacing(size(v) - 1)
298 integer :: i
299
300 push_sub(finufft_scale_nodes)
301
302 assert(size(x) == size(v))
303 assert(size(x) > 1)
305 ! the sample nodes must be in [-1/2, 1/2)
306 length = (maxval(x) - minval(x))*(m_one + m_epsilon)
307 cc = (minval(x) + maxval(x))/m_two
308 v = (x - cc)/length
309
310 spacing = [(v(i+1) - v(i), i = 1, size(v)-1)]
311 inv_spacing = m_one/minval(abs(spacing))
312
313 pop_sub(finufft_scale_nodes)
314 end subroutine finufft_scale_nodes
315
316 ! ---------------------------------------------------------
317 subroutine finufft_write_info(finufft)
318 type(finufft_t), intent(in) :: finufft
319
320 integer :: idir
321
322 push_sub(finufft_write_info)
323
324 call messages_write("Info: FINUFFT parameters")
325 call messages_new_line()
326
327 call messages_write(" Fourier coefficients N = ")
328 do idir = 1, finufft%dim
329 call messages_write(finufft%N(idir))
330 if (idir < finufft%dim) call messages_write(" x ")
331 end do
332 call messages_new_line()
333
334 call messages_write(" Spatial nodes M = ")
335 do idir = 1, finufft%dim
336 call messages_write(finufft%M(idir))
337 if (idir < finufft%dim) call messages_write(" x ")
338 end do
339 call messages_new_line()
340
341 call messages_write(" Requested tolerance tol = ")
342 call messages_write(finufft%tol)
343 call messages_new_line()
344
345 call messages_info()
346
347 pop_sub(finufft_write_info)
348 end subroutine finufft_write_info
349
350 ! ---------------------------------------------------------
351 subroutine finufft_end(finufft)
352 type(finufft_t), intent(inout) :: finufft
353
354#ifdef HAVE_NFFT
355 integer(c_int) :: ier
356#endif
357
358 push_sub(finufft_end)
359
360#ifdef HAVE_NFFT
361 if (c_associated(finufft%plan)) then
362 ier = finufft_destroy(finufft%plan)
363 call finufft_check(ier, 'finufft_destroy')
364 finufft%plan = c_null_ptr
365 end if
366#endif
367
368 safe_deallocate_p(finufft%xj)
369 safe_deallocate_p(finufft%yj)
370 safe_deallocate_p(finufft%zj)
371
372 pop_sub(finufft_end)
373 end subroutine finufft_end
374
375 ! ---------------------------------------------------------
376#ifdef HAVE_NFFT
377
378 subroutine finufft_check(ier, routine)
379 integer(c_int), intent(in) :: ier
380 character(*), intent(in) :: routine
381
382 if (ier == 0) return
383
384 write(message(1), '(3a,i0,a)') "FINUFFT routine ", trim(routine), &
385 " failed with error code ", ier, "."
386 call messages_fatal(1)
387 end subroutine finufft_check
388#endif
389
390#include "undef.F90"
391#include "real.F90"
392#include "finufft_inc.F90"
393
394#include "undef.F90"
395#include "complex.F90"
396#include "finufft_inc.F90"
397
398end module finufft_oct_m
399
400!! Local Variables:
401!! mode: f90
402!! coding: utf-8
403!! End:
Non-equispaced FFTs through the C interface of the FINUFFT library.
Definition: finufft.F90:118
subroutine, public finufft_write_info(finufft)
Definition: finufft.F90:331
subroutine, public zfinufft_forward(finufft, in, out)
Evaluate the Fourier modes on the non-equispaced nodes.
Definition: finufft.F90:563
subroutine, public zfinufft_backward(finufft, in, out)
Project the non-equispaced nodes back onto the Fourier modes.
Definition: finufft.F90:582
subroutine finufft_scale_nodes(X, v, inv_spacing)
Map the nodes onto [-1/2, 1/2) and return the reciprocal of the smallest scaled spacing.
Definition: finufft.F90:305
subroutine, public finufft_init(finufft, finufft_options, N, dim, M)
Definition: finufft.F90:190
subroutine, public finufft_read_options(finufft, namespace)
Definition: finufft.F90:166
subroutine, public finufft_end(finufft)
Definition: finufft.F90:365
subroutine, public dfinufft_backward(finufft, in, out)
Project the non-equispaced nodes back onto the Fourier modes.
Definition: finufft.F90:471
subroutine, public dfinufft_forward(finufft, in, out)
Evaluate the Fourier modes on the non-equispaced nodes.
Definition: finufft.F90:452
subroutine, public finufft_precompute(finufft, X1, X2, X3)
Hand the spatial nodes to FINUFFT.
Definition: finufft.F90:227
real(real64), parameter, public m_two
Definition: global.F90:202
real(real64), parameter, public m_zero
Definition: global.F90:200
real(real64), parameter, public m_pi
some mathematical constants
Definition: global.F90:198
real(real64), parameter, public m_epsilon
Definition: global.F90:216
real(real64), parameter, public m_one
Definition: global.F90:201
subroutine, public messages_new_line()
Definition: messages.F90:1089
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:162
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:410
subroutine, public messages_input_error(namespace, var, details, row, column)
Definition: messages.F90:691
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:594
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
Definition: profiling.F90:631
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
Definition: profiling.F90:554