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, &
50
51#ifdef HAVE_NFFT
52
55 integer, parameter :: FINUFFT_TYPE2 = 2
56 integer, parameter :: FINUFFT_IFLAG = -1
57#endif
58
59 type finufft_t
60 private
61
62 integer :: N(3)
63 integer :: M(3)
64 integer :: dim
65 real(real64), public :: norm
66
67 real(real64) :: tol
68
69 type(c_ptr) :: plan = c_null_ptr
70
73 real(real64), pointer :: xj(:) => null()
74 real(real64), pointer :: yj(:) => null()
75 real(real64), pointer :: zj(:) => null()
76
77 end type finufft_t
78
79#ifdef HAVE_NFFT
80
81 interface
82
84 type(c_ptr) function oct_finufft_opts_init(nthreads) bind(c, name='oct_finufft_opts_init')
85 import :: c_ptr, c_int
86 integer(c_int), value :: nthreads
87 end function oct_finufft_opts_init
88
89 subroutine oct_finufft_opts_end(opts) bind(c, name='oct_finufft_opts_end')
90 import :: c_ptr
91 type(c_ptr), value :: opts
92 end subroutine oct_finufft_opts_end
93
94 integer(c_int) function finufft_makeplan(type_, dim, n_modes, iflag, n_transf, tol, plan, opts) &
95 bind(c, name='finufft_makeplan')
96 import :: c_int, c_int64_t, c_double, c_ptr
97 integer(c_int), value :: type_
98 integer(c_int), value :: dim
99 integer(c_int64_t), intent(in) :: n_modes(*)
100 integer(c_int), value :: iflag
101 integer(c_int), value :: n_transf
102 real(c_double), value :: tol
103 type(c_ptr), intent(out) :: plan
104 type(c_ptr), value :: opts
105 end function finufft_makeplan
106
107 integer(c_int) function finufft_setpts(plan, M, xj, yj, zj, N, s, t, u) &
108 bind(c, name='finufft_setpts')
109 import :: c_int, c_int64_t, c_ptr
110 type(c_ptr), value :: plan
111 integer(c_int64_t), value :: M
112 type(c_ptr), value :: xj, yj, zj
113 integer(c_int64_t), value :: N
114 type(c_ptr), value :: s, t, u
115 end function finufft_setpts
116
117 integer(c_int) function finufft_execute(plan, cj, fk) bind(c, name='finufft_execute')
118 import :: c_int, c_ptr, c_double_complex
119 type(c_ptr), value :: plan
120 complex(c_double_complex), intent(inout) :: cj(*)
121 complex(c_double_complex), intent(in) :: fk(*)
122 end function finufft_execute
123
124 integer(c_int) function finufft_execute_adjoint(plan, cj, fk) bind(c, name='finufft_execute_adjoint')
125 import :: c_int, c_ptr, c_double_complex
126 type(c_ptr), value :: plan
127 complex(c_double_complex), intent(in) :: cj(*)
128 complex(c_double_complex), intent(inout) :: fk(*)
129 end function finufft_execute_adjoint
130
131 integer(c_int) function finufft_destroy(plan) bind(c, name='finufft_destroy')
132 import :: c_int, c_ptr
133 type(c_ptr), value :: plan
134 end function finufft_destroy
135
136 end interface
137#endif
138
139
140contains
141
142 ! ---------------------------------------------------------
143 subroutine finufft_read_options(finufft, namespace)
144 type(finufft_t), intent(inout) :: finufft
145 type(namespace_t), intent(in) :: namespace
147 push_sub(finufft_read_options)
148
149 !%Variable FINUFFTTolerance
150 !%Type float
151 !%Default 1e-8
152 !%Section Mesh::FFTs
153 !%Description
154 !% Requested relative tolerance of the non-equispaced FFTs. Tighter
155 !% tolerances widen the spreading kernel and are more expensive.
156 !%End
157 call parse_variable(namespace, 'FINUFFTTolerance', 1.0e-8_real64, finufft%tol)
158
159 if (finufft%tol <= m_zero) then
160 call messages_input_error(namespace, 'FINUFFTTolerance')
161 end if
163 pop_sub(finufft_read_options)
164 end subroutine finufft_read_options
165
166 ! ---------------------------------------------------------
169 subroutine finufft_set_tolerance(finufft, tol)
170 type(finufft_t), intent(inout) :: finufft
171 real(real64), intent(in) :: tol
172
173 push_sub(finufft_set_tolerance)
174
175 finufft%tol = tol
176
177 pop_sub(finufft_set_tolerance)
178 end subroutine finufft_set_tolerance
179
180 ! ---------------------------------------------------------
181 subroutine finufft_init(finufft, finufft_options, N, dim, M, nthreads)
182 type(finufft_t), intent(inout) :: finufft
183 type(finufft_t), intent(in) :: finufft_options
184 integer, intent(in) :: n(3)
185 integer, intent(in) :: dim
186 integer, intent(in) :: m(3)
187 integer, intent(in), optional :: nthreads
188
189#ifdef HAVE_NFFT
190 integer(c_int64_t) :: n_modes(3)
191 integer(c_int) :: ier
192 type(c_ptr) :: finufft_opts
193 integer(c_int) :: n_threads
194#endif
196 push_sub(finufft_init)
197
198 call profiling_in("FINUFFT_INIT")
199
200 finufft%dim = dim
201 finufft%N(:) = n(:)
202 finufft%M(:) = m(:)
203 ! unused directions hold a single mode and a single node
204 finufft%N(dim+1:3) = 1
205 finufft%M(dim+1:3) = 1
206
207 finufft%tol = finufft_options%tol
208
209#ifdef HAVE_NFFT
210 n_modes(1:3) = int(finufft%N(1:3), c_int64_t)
211 n_threads = optional_default(nthreads, 0_c_int)
212
213 ! Selects the FINUFFT defaults, including the mode ordering relied on here
214 ! (modeord = 0, modes -N/2 .. N/2-1)
215 finufft_opts = oct_finufft_opts_init(n_threads)
216 if (.not. c_associated(finufft_opts)) then
217 message(1) = "Could not allocate the FINUFFT options."
218 call messages_fatal(1)
219 end if
220
221 ier = finufft_makeplan(int(finufft_type2, c_int), int(dim, c_int), n_modes, &
222 int(finufft_iflag, c_int), 1_c_int, real(finufft%tol, c_double), finufft%plan, finufft_opts)
223 call finufft_check(ier, 'finufft_makeplan')
224
225 call oct_finufft_opts_end(finufft_opts)
226#endif
227
228 call profiling_out("FINUFFT_INIT")
229
230 pop_sub(finufft_init)
231 end subroutine finufft_init
232
233 ! ---------------------------------------------------------
239 subroutine finufft_precompute(finufft, X1, X2, X3, period)
240 type(finufft_t), intent(inout) :: finufft
241 real(real64), contiguous, intent(in) :: x1(:)
242 real(real64), contiguous, optional, intent(in) :: x2(:)
243 real(real64), contiguous, optional, intent(in) :: x3(:)
245 real(real64), optional, intent(in) :: period(:)
246
247 real(real64) :: v1(1:finufft%m(1)), v2(1:finufft%m(2)), v3(1:finufft%m(3))
248 real(real64) :: inv_spacing, period_(3)
249 integer(c_int64_t) :: nj
250 integer :: i1, i2, i3, jj
251#ifdef HAVE_NFFT
252 integer(c_int) :: ier
253 type(c_ptr) :: yj_ptr, zj_ptr
254#endif
255
256 push_sub(finufft_precompute)
257
258 call profiling_in("FINUFFT_PRECOMPUTE")
259
260 finufft%norm = m_one
261
262 period_ = m_zero
263 if (present(period)) period_(1:3) = period(1:3)
264
265 assert(size(x1) == finufft%M(1))
266 call finufft_scale_nodes(x1, v1, inv_spacing, period_(1))
267 finufft%norm = finufft%norm * inv_spacing
268
269 v2 = m_zero
270 if (finufft%dim >= 2) then
271 assert(present(x2))
272 assert(size(x2) == finufft%M(2))
273 call finufft_scale_nodes(x2, v2, inv_spacing, period_(2))
274 finufft%norm = finufft%norm * inv_spacing
275 end if
276
277 v3 = m_zero
278 if (finufft%dim >= 3) then
279 assert(present(x3))
280 assert(size(x3) == finufft%M(3))
281 call finufft_scale_nodes(x3, v3, inv_spacing, period_(3))
282 finufft%norm = finufft%norm * inv_spacing
283 end if
284
285 nj = int(finufft%M(1), c_int64_t)*int(finufft%M(2), c_int64_t)*int(finufft%M(3), c_int64_t)
286
287 safe_deallocate_p(finufft%xj)
288 safe_deallocate_p(finufft%yj)
289 safe_deallocate_p(finufft%zj)
290 safe_allocate(finufft%xj(1:nj))
291 if (finufft%dim >= 2) then
292 safe_allocate(finufft%yj(1:nj))
293 end if
294 if (finufft%dim >= 3) then
295 safe_allocate(finufft%zj(1:nj))
296 end if
297
298 ! Fortran order, matching the transform arrays, so execute needs no repacking
299 jj = 0
300 do i3 = 1, finufft%M(3)
301 do i2 = 1, finufft%M(2)
302 do i1 = 1, finufft%M(1)
303 jj = jj + 1
304 finufft%xj(jj) = m_two*m_pi*v1(i1)
305 if (finufft%dim >= 2) finufft%yj(jj) = m_two*m_pi*v2(i2)
306 if (finufft%dim >= 3) finufft%zj(jj) = m_two*m_pi*v3(i3)
307 end do
308 end do
309 end do
310
311#ifdef HAVE_NFFT
312 yj_ptr = c_null_ptr
313 zj_ptr = c_null_ptr
314 if (finufft%dim >= 2) yj_ptr = c_loc(finufft%yj)
315 if (finufft%dim >= 3) zj_ptr = c_loc(finufft%zj)
316
317 ! The trailing arguments only apply to type 3
318 ier = finufft_setpts(finufft%plan, nj, c_loc(finufft%xj), yj_ptr, zj_ptr, &
319 0_c_int64_t, c_null_ptr, c_null_ptr, c_null_ptr)
320 call finufft_check(ier, 'finufft_setpts')
321#endif
322
323 call profiling_out("FINUFFT_PRECOMPUTE")
324
325 write(message(1), '(a)') "Info: FINUFFT plan precomputed."
326 call messages_info(1)
327
328 pop_sub(finufft_precompute)
329 end subroutine finufft_precompute
330
331 ! ---------------------------------------------------------
335 subroutine finufft_set_nodes(finufft, xj, yj, zj, period)
336 type(finufft_t), intent(inout) :: finufft
337 real(real64), contiguous, intent(in) :: xj(:)
338 real(real64), contiguous, intent(in) :: yj(:)
339 real(real64), contiguous, intent(in) :: zj(:)
340 real(real64), contiguous, intent(in) :: period(:)
341
342 integer(c_int64_t) :: nj
343#ifdef HAVE_NFFT
344 integer(c_int) :: ier
345#endif
346
347 push_sub(finufft_set_nodes)
348
349 call profiling_in("FINUFFT_SET_NODES")
350
351 assert(finufft%dim == 3)
352
353 nj = int(finufft%M(1), c_int64_t)
354 assert(size(xj) == nj)
355 assert(size(yj) == nj)
356 assert(size(zj) == nj)
357
358 finufft%norm = m_one
359
360 safe_deallocate_p(finufft%xj)
361 safe_deallocate_p(finufft%yj)
362 safe_deallocate_p(finufft%zj)
363 safe_allocate(finufft%xj(1:nj))
364 safe_allocate(finufft%yj(1:nj))
365 safe_allocate(finufft%zj(1:nj))
366
367 finufft%xj = m_two*m_pi*xj/period(1)
368 finufft%yj = m_two*m_pi*yj/period(2)
369 finufft%zj = m_two*m_pi*zj/period(3)
370
371#ifdef HAVE_NFFT
372 ier = finufft_setpts(finufft%plan, nj, c_loc(finufft%xj), c_loc(finufft%yj), c_loc(finufft%zj), &
373 0_c_int64_t, c_null_ptr, c_null_ptr, c_null_ptr)
374 call finufft_check(ier, 'finufft_setpts')
375#endif
376
377 call profiling_out("FINUFFT_SET_NODES")
378
379 pop_sub(finufft_set_nodes)
380 end subroutine finufft_set_nodes
381
382 ! ---------------------------------------------------------
384 subroutine finufft_modes_to_nodes(finufft, modes, values)
385 type(finufft_t), intent(in) :: finufft
386 complex(real64), contiguous, intent(in) :: modes(:,:,:)
387 complex(real64), contiguous, intent(out) :: values(:)
388
389#ifdef HAVE_NFFT
390 integer(c_int) :: ier
391#endif
392
393 push_sub(finufft_modes_to_nodes)
394
395 call profiling_in("FINUFFT_MODES_TO_NODES")
396
397#ifdef HAVE_NFFT
398 ier = finufft_execute(finufft%plan, values, modes)
399 call finufft_check(ier, 'finufft_execute')
400#else
401 values = m_z0
402#endif
403
404 call profiling_out("FINUFFT_MODES_TO_NODES")
405
407 end subroutine finufft_modes_to_nodes
408
409 ! ---------------------------------------------------------
411 subroutine finufft_nodes_to_modes(finufft, values, modes)
412 type(finufft_t), intent(in) :: finufft
413 complex(real64), contiguous, intent(in) :: values(:)
414 complex(real64), contiguous, intent(out) :: modes(:,:,:)
415
416#ifdef HAVE_NFFT
417 integer(c_int) :: ier
418#endif
419
420 push_sub(finufft_nodes_to_modes)
421
422 call profiling_in("FINUFFT_NODES_TO_MODES")
423
424#ifdef HAVE_NFFT
425 ier = finufft_execute_adjoint(finufft%plan, values, modes)
426 call finufft_check(ier, 'finufft_execute_adjoint')
427#else
428 modes = m_z0
429#endif
430
431 call profiling_out("FINUFFT_NODES_TO_MODES")
432
434 end subroutine finufft_nodes_to_modes
435
436 ! ---------------------------------------------------------
441 subroutine finufft_scale_nodes(X, v, inv_spacing, period)
442 real(real64), contiguous, intent(in) :: X(:)
443 real(real64), contiguous, intent(out) :: v(:)
444 real(real64), intent(out) :: inv_spacing
445 real(real64), intent(in) :: period
446
447 real(real64) :: length, cc
448 real(real64) :: spacing(size(v) - 1)
449 integer :: i
450
451 push_sub(finufft_scale_nodes)
452
453 assert(size(x) == size(v))
454 assert(size(x) > 1)
455
456 if (period > m_zero) then
457 v = x/period
458 inv_spacing = real(size(v), real64)
459 else
460 ! the sample nodes must be in [-1/2, 1/2)
461 length = (maxval(x) - minval(x))*(m_one + m_epsilon)
462 cc = (minval(x) + maxval(x))/m_two
463 v = (x - cc)/length
464
465 spacing = [(v(i+1) - v(i), i = 1, size(v)-1)]
466 inv_spacing = m_one/minval(abs(spacing))
467 end if
468
469 pop_sub(finufft_scale_nodes)
470 end subroutine finufft_scale_nodes
471
472 ! ---------------------------------------------------------
473 subroutine finufft_write_info(finufft)
474 type(finufft_t), intent(in) :: finufft
475
476 integer :: idir
477
478 push_sub(finufft_write_info)
479
480 call messages_write("Info: FINUFFT parameters")
481 call messages_new_line()
482
483 call messages_write(" Fourier coefficients N = ")
484 do idir = 1, finufft%dim
485 call messages_write(finufft%N(idir))
486 if (idir < finufft%dim) call messages_write(" x ")
487 end do
488 call messages_new_line()
489
490 call messages_write(" Spatial nodes M = ")
491 do idir = 1, finufft%dim
492 call messages_write(finufft%M(idir))
493 if (idir < finufft%dim) call messages_write(" x ")
494 end do
496
497 call messages_write(" Requested tolerance tol = ")
498 call messages_write(finufft%tol)
499 call messages_new_line()
500
501 call messages_info()
502
503 pop_sub(finufft_write_info)
504 end subroutine finufft_write_info
505
506 ! ---------------------------------------------------------
507 subroutine finufft_end(finufft)
508 type(finufft_t), intent(inout) :: finufft
509
510#ifdef HAVE_NFFT
511 integer(c_int) :: ier
512#endif
513
514 push_sub(finufft_end)
515
516#ifdef HAVE_NFFT
517 if (c_associated(finufft%plan)) then
518 ier = finufft_destroy(finufft%plan)
519 call finufft_check(ier, 'finufft_destroy')
520 finufft%plan = c_null_ptr
521 end if
522#endif
523
524 safe_deallocate_p(finufft%xj)
525 safe_deallocate_p(finufft%yj)
526 safe_deallocate_p(finufft%zj)
527
528 pop_sub(finufft_end)
529 end subroutine finufft_end
530
531 ! ---------------------------------------------------------
532#ifdef HAVE_NFFT
533
534 subroutine finufft_check(ier, routine)
535 integer(c_int), intent(in) :: ier
536 character(*), intent(in) :: routine
537
538 if (ier == 0) return
539
540 write(message(1), '(3a,i0,a)') "FINUFFT routine ", trim(routine), &
541 " failed with error code ", ier, "."
542 call messages_fatal(1)
543 end subroutine finufft_check
544#endif
545
546#include "undef.F90"
547#include "real.F90"
548#include "finufft_inc.F90"
549
550#include "undef.F90"
551#include "complex.F90"
552#include "finufft_inc.F90"
553
554end module finufft_oct_m
555
556!! Local Variables:
557!! mode: f90
558!! coding: utf-8
559!! 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:462
subroutine, public finufft_set_nodes(finufft, xj, yj, zj, period)
Hand scattered nodes to FINUFFT, mapping one period of the mode grid onto the [-1/2,...
Definition: finufft.F90:324
subroutine, public zfinufft_forward(finufft, in, out)
Evaluate the Fourier modes on the non-equispaced nodes.
Definition: finufft.F90:694
subroutine, public zfinufft_backward(finufft, in, out)
Project the non-equispaced nodes back onto the Fourier modes.
Definition: finufft.F90:713
subroutine, public finufft_precompute(finufft, X1, X2, X3, period)
Hand the spatial nodes to FINUFFT.
Definition: finufft.F90:240
subroutine, public finufft_read_options(finufft, namespace)
Definition: finufft.F90:170
subroutine, public finufft_init(finufft, finufft_options, N, dim, M, nthreads)
Definition: finufft.F90:208
subroutine, public finufft_end(finufft)
Definition: finufft.F90:496
subroutine, public finufft_nodes_to_modes(finufft, values, modes)
Project the scattered nodes onto the Fourier modes.
Definition: finufft.F90:400
subroutine, public finufft_modes_to_nodes(finufft, modes, values)
Evaluate the Fourier modes on the scattered nodes.
Definition: finufft.F90:373
subroutine finufft_scale_nodes(X, v, inv_spacing, period)
Map the nodes onto [-1/2, 1/2) and return the reciprocal of the smallest scaled spacing....
Definition: finufft.F90:430
subroutine, public dfinufft_backward(finufft, in, out)
Project the non-equispaced nodes back onto the Fourier modes.
Definition: finufft.F90:602
subroutine, public dfinufft_forward(finufft, in, out)
Evaluate the Fourier modes on the non-equispaced nodes.
Definition: finufft.F90:583
subroutine, public finufft_set_tolerance(finufft, tol)
Set the requested tolerance directly, for callers that take it from an input variable of their own ra...
Definition: finufft.F90:196
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
complex(real64), parameter, public m_z0
Definition: global.F90:210
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