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
80 type(c_ptr) function oct_finufft_opts_init(nthreads) bind(c, name='oct_finufft_opts_init')
81 import :: c_ptr, c_int
82 integer(c_int), value :: nthreads
83 end function oct_finufft_opts_init
84
85 subroutine oct_finufft_opts_end(opts) bind(c, name='oct_finufft_opts_end')
86 import :: c_ptr
87 type(c_ptr), value :: opts
88 end subroutine oct_finufft_opts_end
89
90 integer(c_int) function finufft_makeplan(type_, dim, n_modes, iflag, n_transf, tol, plan, opts) &
91 bind(c, name='finufft_makeplan')
92 import :: c_int, c_int64_t, c_double, c_ptr
93 integer(c_int), value :: type_
94 integer(c_int), value :: dim
95 integer(c_int64_t), intent(in) :: n_modes(*)
96 integer(c_int), value :: iflag
97 integer(c_int), value :: n_transf
98 real(c_double), value :: tol
99 type(c_ptr), intent(out) :: plan
100 type(c_ptr), value :: opts
101 end function finufft_makeplan
102
103 integer(c_int) function finufft_setpts(plan, M, xj, yj, zj, N, s, t, u) &
104 bind(c, name='finufft_setpts')
105 import :: c_int, c_int64_t, c_ptr
106 type(c_ptr), value :: plan
107 integer(c_int64_t), value :: M
108 type(c_ptr), value :: xj, yj, zj
109 integer(c_int64_t), value :: N
110 type(c_ptr), value :: s, t, u
111 end function finufft_setpts
112
113 integer(c_int) function finufft_execute(plan, cj, fk) bind(c, name='finufft_execute')
114 import :: c_int, c_ptr, c_double_complex
115 type(c_ptr), value :: plan
116 complex(c_double_complex), intent(inout) :: cj(*)
117 complex(c_double_complex), intent(in) :: fk(*)
118 end function finufft_execute
119
120 integer(c_int) function finufft_execute_adjoint(plan, cj, fk) bind(c, name='finufft_execute_adjoint')
121 import :: c_int, c_ptr, c_double_complex
122 type(c_ptr), value :: plan
123 complex(c_double_complex), intent(in) :: cj(*)
124 complex(c_double_complex), intent(inout) :: fk(*)
125 end function finufft_execute_adjoint
126
127 integer(c_int) function finufft_destroy(plan) bind(c, name='finufft_destroy')
128 import :: c_int, c_ptr
129 type(c_ptr), value :: plan
130 end function finufft_destroy
131
132 end interface
133#endif
134
135
136contains
137
138 ! ---------------------------------------------------------
139 subroutine finufft_read_options(finufft, namespace)
140 type(finufft_t), intent(inout) :: finufft
141 type(namespace_t), intent(in) :: namespace
143 push_sub(finufft_read_options)
144
145 !%Variable FINUFFTTolerance
146 !%Type float
147 !%Default 1e-8
148 !%Section Mesh::FFTs
149 !%Description
150 !% Requested relative tolerance of the non-equispaced FFTs. Tighter
151 !% tolerances widen the spreading kernel and are more expensive.
152 !%End
153 call parse_variable(namespace, 'FINUFFTTolerance', 1.0e-8_real64, finufft%tol)
154
155 if (finufft%tol <= m_zero) then
156 call messages_input_error(namespace, 'FINUFFTTolerance')
157 end if
159 pop_sub(finufft_read_options)
160 end subroutine finufft_read_options
161
162 ! ---------------------------------------------------------
163 subroutine finufft_init(finufft, finufft_options, N, dim, M, nthreads)
164 type(finufft_t), intent(inout) :: finufft
165 type(finufft_t), intent(in) :: finufft_options
166 integer, intent(in) :: n(3)
167 integer, intent(in) :: dim
168 integer, intent(in) :: m(3)
169 integer, intent(in), optional :: nthreads
170
171#ifdef HAVE_NFFT
172 integer(c_int64_t) :: n_modes(3)
173 integer(c_int) :: ier
174 type(c_ptr) :: finufft_opts
175 integer(c_int) :: n_threads
176#endif
177
178 push_sub(finufft_init)
179
180 call profiling_in("FINUFFT_INIT")
181
182 finufft%dim = dim
183 finufft%N(:) = n(:)
184 finufft%M(:) = m(:)
185 ! unused directions hold a single mode and a single node
186 finufft%N(dim+1:3) = 1
187 finufft%M(dim+1:3) = 1
188
189 finufft%tol = finufft_options%tol
190
191#ifdef HAVE_NFFT
192 n_modes(1:3) = int(finufft%N(1:3), c_int64_t)
193 n_threads = optional_default(nthreads, 0_c_int)
194
195 ! Selects the FINUFFT defaults, including the mode ordering relied on here
196 ! (modeord = 0, modes -N/2 .. N/2-1)
197 finufft_opts = oct_finufft_opts_init(n_threads)
198 if (.not. c_associated(finufft_opts)) then
199 message(1) = "Could not allocate the FINUFFT options."
200 call messages_fatal(1)
201 end if
202
203 ier = finufft_makeplan(int(finufft_type2, c_int), int(dim, c_int), n_modes, &
204 int(finufft_iflag, c_int), 1_c_int, real(finufft%tol, c_double), finufft%plan, finufft_opts)
205 call finufft_check(ier, 'finufft_makeplan')
206
207 call oct_finufft_opts_end(finufft_opts)
208#endif
209
210 call profiling_out("FINUFFT_INIT")
211
212 pop_sub(finufft_init)
213 end subroutine finufft_init
214
215 ! ---------------------------------------------------------
221 subroutine finufft_precompute(finufft, X1, X2, X3)
222 type(finufft_t), intent(inout) :: finufft
223 real(real64), intent(in) :: x1(:)
224 real(real64), optional, intent(in) :: x2(:)
225 real(real64), optional, intent(in) :: x3(:)
226
227 real(real64) :: v1(1:finufft%m(1)), v2(1:finufft%m(2)), v3(1:finufft%m(3))
228 real(real64) :: inv_spacing
229 integer(c_int64_t) :: nj
230 integer :: i1, i2, i3, jj
231#ifdef HAVE_NFFT
232 integer(c_int) :: ier
233 type(c_ptr) :: yj_ptr, zj_ptr
234#endif
235
236 push_sub(finufft_precompute)
237
238 call profiling_in("FINUFFT_PRECOMPUTE")
239
240 finufft%norm = m_one
241
242 assert(size(x1) == finufft%M(1))
243 call finufft_scale_nodes(x1, v1, inv_spacing)
244 finufft%norm = finufft%norm * inv_spacing
245
246 v2 = m_zero
247 if (finufft%dim >= 2) then
248 assert(present(x2))
249 assert(size(x2) == finufft%M(2))
250 call finufft_scale_nodes(x2, v2, inv_spacing)
251 finufft%norm = finufft%norm * inv_spacing
252 end if
253
254 v3 = m_zero
255 if (finufft%dim >= 3) then
256 assert(present(x3))
257 assert(size(x3) == finufft%M(3))
258 call finufft_scale_nodes(x3, v3, inv_spacing)
259 finufft%norm = finufft%norm * inv_spacing
260 end if
261
262 nj = int(finufft%M(1), c_int64_t)*int(finufft%M(2), c_int64_t)*int(finufft%M(3), c_int64_t)
263
264 safe_deallocate_p(finufft%xj)
265 safe_deallocate_p(finufft%yj)
266 safe_deallocate_p(finufft%zj)
267 safe_allocate(finufft%xj(1:nj))
268 if (finufft%dim >= 2) then
269 safe_allocate(finufft%yj(1:nj))
270 end if
271 if (finufft%dim >= 3) then
272 safe_allocate(finufft%zj(1:nj))
273 end if
274
275 ! Fortran order, matching the transform arrays, so execute needs no repacking
276 jj = 0
277 do i3 = 1, finufft%M(3)
278 do i2 = 1, finufft%M(2)
279 do i1 = 1, finufft%M(1)
280 jj = jj + 1
281 finufft%xj(jj) = m_two*m_pi*v1(i1)
282 if (finufft%dim >= 2) finufft%yj(jj) = m_two*m_pi*v2(i2)
283 if (finufft%dim >= 3) finufft%zj(jj) = m_two*m_pi*v3(i3)
284 end do
285 end do
286 end do
287
288#ifdef HAVE_NFFT
289 yj_ptr = c_null_ptr
290 zj_ptr = c_null_ptr
291 if (finufft%dim >= 2) yj_ptr = c_loc(finufft%yj)
292 if (finufft%dim >= 3) zj_ptr = c_loc(finufft%zj)
293
294 ! The trailing arguments only apply to type 3
295 ier = finufft_setpts(finufft%plan, nj, c_loc(finufft%xj), yj_ptr, zj_ptr, &
296 0_c_int64_t, c_null_ptr, c_null_ptr, c_null_ptr)
297 call finufft_check(ier, 'finufft_setpts')
298#endif
300 call profiling_out("FINUFFT_PRECOMPUTE")
301
302 write(message(1), '(a)') "Info: FINUFFT plan precomputed."
303 call messages_info(1)
304
305 pop_sub(finufft_precompute)
306 end subroutine finufft_precompute
307
308 ! ---------------------------------------------------------
311 subroutine finufft_scale_nodes(X, v, inv_spacing)
312 real(real64), intent(in) :: X(:)
313 real(real64), intent(out) :: v(:)
314 real(real64), intent(out) :: inv_spacing
315
316 real(real64) :: length, cc
317 real(real64) :: spacing(size(v) - 1)
318 integer :: i
319
320 push_sub(finufft_scale_nodes)
321
322 assert(size(x) == size(v))
323 assert(size(x) > 1)
324
325 ! the sample nodes must be in [-1/2, 1/2)
326 length = (maxval(x) - minval(x))*(m_one + m_epsilon)
327 cc = (minval(x) + maxval(x))/m_two
328 v = (x - cc)/length
329
330 spacing = [(v(i+1) - v(i), i = 1, size(v)-1)]
331 inv_spacing = m_one/minval(abs(spacing))
332
333 pop_sub(finufft_scale_nodes)
334 end subroutine finufft_scale_nodes
335
336 ! ---------------------------------------------------------
337 subroutine finufft_write_info(finufft)
338 type(finufft_t), intent(in) :: finufft
339
340 integer :: idir
341
342 push_sub(finufft_write_info)
343
344 call messages_write("Info: FINUFFT parameters")
345 call messages_new_line()
346
347 call messages_write(" Fourier coefficients N = ")
348 do idir = 1, finufft%dim
349 call messages_write(finufft%N(idir))
350 if (idir < finufft%dim) call messages_write(" x ")
351 end do
352 call messages_new_line()
353
354 call messages_write(" Spatial nodes M = ")
355 do idir = 1, finufft%dim
356 call messages_write(finufft%M(idir))
357 if (idir < finufft%dim) call messages_write(" x ")
358 end do
360
361 call messages_write(" Requested tolerance tol = ")
362 call messages_write(finufft%tol)
363 call messages_new_line()
364
365 call messages_info()
366
367 pop_sub(finufft_write_info)
368 end subroutine finufft_write_info
369
370 ! ---------------------------------------------------------
371 subroutine finufft_end(finufft)
372 type(finufft_t), intent(inout) :: finufft
373
374#ifdef HAVE_NFFT
375 integer(c_int) :: ier
376#endif
377
378 push_sub(finufft_end)
379
380#ifdef HAVE_NFFT
381 if (c_associated(finufft%plan)) then
382 ier = finufft_destroy(finufft%plan)
383 call finufft_check(ier, 'finufft_destroy')
384 finufft%plan = c_null_ptr
385 end if
386#endif
387
388 safe_deallocate_p(finufft%xj)
389 safe_deallocate_p(finufft%yj)
390 safe_deallocate_p(finufft%zj)
391
392 pop_sub(finufft_end)
393 end subroutine finufft_end
394
395 ! ---------------------------------------------------------
396#ifdef HAVE_NFFT
397
398 subroutine finufft_check(ier, routine)
399 integer(c_int), intent(in) :: ier
400 character(*), intent(in) :: routine
401
402 if (ier == 0) return
403
404 write(message(1), '(3a,i0,a)') "FINUFFT routine ", trim(routine), &
405 " failed with error code ", ier, "."
406 call messages_fatal(1)
407 end subroutine finufft_check
408#endif
409
410#include "undef.F90"
411#include "real.F90"
412#include "finufft_inc.F90"
413
414#include "undef.F90"
415#include "complex.F90"
416#include "finufft_inc.F90"
417
418end module finufft_oct_m
419
420!! Local Variables:
421!! mode: f90
422!! coding: utf-8
423!! 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:326
subroutine, public zfinufft_forward(finufft, in, out)
Evaluate the Fourier modes on the non-equispaced nodes.
Definition: finufft.F90:558
subroutine, public zfinufft_backward(finufft, in, out)
Project the non-equispaced nodes back onto the Fourier modes.
Definition: finufft.F90:577
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:300
subroutine, public finufft_read_options(finufft, namespace)
Definition: finufft.F90:166
subroutine, public finufft_init(finufft, finufft_options, N, dim, M, nthreads)
Definition: finufft.F90:190
subroutine, public finufft_end(finufft)
Definition: finufft.F90:360
subroutine, public dfinufft_backward(finufft, in, out)
Project the non-equispaced nodes back onto the Fourier modes.
Definition: finufft.F90:466
subroutine, public dfinufft_forward(finufft, in, out)
Evaluate the Fourier modes on the non-equispaced nodes.
Definition: finufft.F90:447
subroutine, public finufft_precompute(finufft, X1, X2, X3)
Hand the spatial nodes to FINUFFT.
Definition: finufft.F90:222
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