Octopus
poisson_nfft.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#include "global.h"
20
38 use debug_oct_m
40 use global_oct_m
42 use mesh_oct_m
47 use space_oct_m
48 use unit_oct_m
50
51 implicit none
52
53 private
54 public :: &
60
62 private
63 type(finufft_t) :: finufft
64 integer :: nn(3)
65 real(real64), allocatable :: kernel(:,:,:)
66 end type poisson_nfft_t
67
68contains
69
70 ! ---------------------------------------------------------
71 subroutine poisson_nfft_init(this, namespace, space, mesh, threshold)
72 type(poisson_nfft_t), intent(out) :: this
73 type(namespace_t), intent(in) :: namespace
74 class(space_t), intent(in) :: space
75 class(mesh_t), intent(in) :: mesh
76 real(real64), intent(in) :: threshold
77
78 type(finufft_t) :: options
79 real(real64) :: bounds(2, space%dim), aa(3), spacing, spacing_min, r_c, ll(3)
80 integer :: idir
81
82 push_sub(poisson_nfft_init)
83
84#ifndef HAVE_NFFT
85 message(1) = 'PoissonSolver = nfft requires FINUFFT but that library was not linked.'
86 call messages_fatal(1, namespace=namespace)
87#endif
88
89 assert(space%dim == 3)
90
91 ! Every domain would have to reduce the whole mode grid to complete the quadrature,
92 ! which does not fit for a large mesh
93 if (mesh%parallel_in_domains) then
94 call messages_not_implemented('nfft Poisson solver with domain parallelization', namespace=namespace)
95 end if
96
97 ! The density lives in the box, so these half widths about the origin bound it,
98 ! whatever the shape of the box and however the mesh samples it
99 bounds = mesh%box%bounds()
100 aa = m_zero
101 do idir = 1, space%dim
102 aa(idir) = maxval(abs(bounds(:, idir)))
103 end do
104
105 ! The coarsest sampling of the mesh sets how far the modes reach, and only the
106 ! mesh knows it
107 if (mesh%use_curvilinear) then
108 spacing = maxval(mesh%vol_pp(1:mesh%np))**(m_one/m_three)
109 spacing_min = minval(mesh%vol_pp(1:mesh%np))**(m_one/m_three)
110 else
111 spacing = mesh%vol_pp(1)**(m_one/m_three)
112 spacing_min = spacing
113 end if
114
115 ! The kernel truncated at the diameter of the box reproduces free boundary
116 ! conditions, provided the mode grid keeps the periodic images beyond the cutoff.
117 ! The modes reach as far as the coarsest sampling of the mesh, which is as far as a
118 ! quadrature over the mesh points goes: beyond it the sum aliases and the potential
119 ! degrades.
120 r_c = m_two*norm2(aa)
121 ll = m_two*aa + r_c
122 this%nn = 2*ceiling(m_half*ll/spacing) ! even, so that the modes straddle G = 0
123 ll = this%nn*spacing ! only grows, so the images stay out
124
125 call poisson_nfft_build_3d_0d(this, ll, r_c)
126
127 call finufft_set_tolerance(options, threshold)
128 call finufft_init(this%finufft, options, this%nn, space%dim, [mesh%np, 1, 1])
129 call finufft_set_nodes(this%finufft, mesh%x_t(1:mesh%np, 1), mesh%x_t(1:mesh%np, 2), &
130 mesh%x_t(1:mesh%np, 3), ll)
131
132 call messages_write("Info: NFFT Poisson solver")
133 call messages_new_line()
134 call messages_write(" Fourier modes =")
135 do idir = 1, 3
136 call messages_write(this%nn(idir))
137 if (idir < 3) call messages_write(" x ")
138 end do
139 call messages_new_line()
140 call messages_write(" Mesh spacing, min .. max =")
141 call messages_write(units_from_atomic(units_out%length, spacing_min))
142 call messages_write(" ..")
143 call messages_write(units_from_atomic(units_out%length, spacing))
144 call messages_write(' [' // trim(units_abbrev(units_out%length)) // ']')
145 call messages_new_line()
146 call messages_write(" Cutoff radius =")
148 call messages_write(' [' // trim(units_abbrev(units_out%length)) // ']')
149 call messages_info(namespace=namespace)
150
151 pop_sub(poisson_nfft_init)
152 end subroutine poisson_nfft_init
153
154 ! ---------------------------------------------------------
160 subroutine poisson_nfft_build_3d_0d(this, ll, r_c)
161 type(poisson_nfft_t), intent(inout) :: this
162 real(real64), intent(in) :: ll(3)
163 real(real64), intent(in) :: r_c
164
165 real(real64) :: dg(3), gg(3), gmod2
166 integer :: i1, i2, i3
167
169
170 dg = m_two*m_pi/ll
171
172 safe_allocate(this%kernel(1:this%nn(1), 1:this%nn(2), 1:this%nn(3)))
173
174 ! modes -N/2 .. N/2-1 per direction, the ordering FINUFFT uses
175 !$omp parallel do private(i1, i2, gg, gmod2)
176 do i3 = 1, this%nn(3)
177 gg(3) = (i3 - this%nn(3)/2 - 1)*dg(3)
178 do i2 = 1, this%nn(2)
179 gg(2) = (i2 - this%nn(2)/2 - 1)*dg(2)
180 do i1 = 1, this%nn(1)
181 gg(1) = (i1 - this%nn(1)/2 - 1)*dg(1)
182
183 gmod2 = sum(gg**2)
184 if (gmod2 > m_epsilon) then
185 this%kernel(i1, i2, i3) = poisson_cutoff_3d_0d(sqrt(gmod2), r_c)/gmod2
186 else
187 this%kernel(i1, i2, i3) = m_half*r_c**2
188 end if
189 end do
190 end do
191 end do
192 !$omp end parallel do
193
194 ! mode -N/2 has no partner at +N/2, and that asymmetry leaves the convolution of a
195 ! real density with a spurious imaginary part, so drop it
196 this%kernel(1, :, :) = m_zero
197 this%kernel(:, 1, :) = m_zero
198 this%kernel(:, :, 1) = m_zero
199
200 call lalg_scal(this%nn(1), this%nn(2), this%nn(3), m_four*m_pi/product(ll), this%kernel)
201
203 end subroutine poisson_nfft_build_3d_0d
204
205 ! ---------------------------------------------------------
206 subroutine dpoisson_nfft_solve(this, mesh, pot, rho)
207 type(poisson_nfft_t), intent(in) :: this
208 class(mesh_t), intent(in) :: mesh
209 real(real64), contiguous, intent(out) :: pot(:)
210 real(real64), contiguous, intent(in) :: rho(:)
211
212 complex(real64), allocatable :: values(:)
213 integer :: ip
214
215 push_sub(dpoisson_nfft_solve)
216
217 safe_allocate(values(1:mesh%np))
218
219 if (mesh%use_curvilinear) then
220 !$omp parallel do
221 do ip = 1, mesh%np
222 values(ip) = mesh%vol_pp(ip)*rho(ip)
223 end do
224 !$omp end parallel do
225 else
226 !$omp parallel do
227 do ip = 1, mesh%np
228 values(ip) = rho(ip)
229 end do
230 !$omp end parallel do
231 call lalg_scal(mesh%np, mesh%vol_pp(1), values)
232 end if
233
234 call poisson_nfft_convolve(this, values)
235
236 !$omp parallel do
237 do ip = 1, mesh%np
238 pot(ip) = real(values(ip), real64)
239 end do
240 !$omp end parallel do
241
242 safe_deallocate_a(values)
243
244 pop_sub(dpoisson_nfft_solve)
245 end subroutine dpoisson_nfft_solve
246
247 ! ---------------------------------------------------------
250 subroutine zpoisson_nfft_solve(this, mesh, pot, rho)
251 type(poisson_nfft_t), intent(in) :: this
252 class(mesh_t), intent(in) :: mesh
253 complex(real64), contiguous, intent(out) :: pot(:)
254 complex(real64), contiguous, intent(in) :: rho(:)
256 complex(real64), allocatable :: values(:)
257 integer :: ip
258
259 push_sub(zpoisson_nfft_solve)
260
261 safe_allocate(values(1:mesh%np))
262
263 if (mesh%use_curvilinear) then
264 !$omp parallel do
265 do ip = 1, mesh%np
266 values(ip) = mesh%vol_pp(ip)*rho(ip)
267 end do
268 !$omp end parallel do
269 else
270 call lalg_copy(mesh%np, rho, values)
271 call lalg_scal(mesh%np, mesh%vol_pp(1), values)
272 end if
273
274 call poisson_nfft_convolve(this, values)
275
276 call lalg_copy(mesh%np, values, pot)
277
278 safe_deallocate_a(values)
279
280 pop_sub(zpoisson_nfft_solve)
281 end subroutine zpoisson_nfft_solve
282
283 ! ---------------------------------------------------------
285 subroutine poisson_nfft_convolve(this, values)
286 type(poisson_nfft_t), intent(in) :: this
287 complex(real64), contiguous, intent(inout) :: values(:)
288
289 complex(real64), allocatable :: modes(:,:,:)
290 integer :: i1, i2, i3
291
292 push_sub(poisson_nfft_convolve)
293
294 call profiling_in("POISSON_NFFT_SOLVE")
295
296 safe_allocate(modes(1:this%nn(1), 1:this%nn(2), 1:this%nn(3)))
297
298 call finufft_nodes_to_modes(this%finufft, values, modes)
299
300 !$omp parallel do private(i1, i2)
301 do i3 = 1, this%nn(3)
302 do i2 = 1, this%nn(2)
303 do i1 = 1, this%nn(1)
304 modes(i1, i2, i3) = this%kernel(i1, i2, i3)*modes(i1, i2, i3)
305 end do
306 end do
307 end do
308 !$omp end parallel do
309
310 call finufft_modes_to_nodes(this%finufft, modes, values)
311
312 safe_deallocate_a(modes)
313
314 call profiling_out("POISSON_NFFT_SOLVE")
315
316 pop_sub(poisson_nfft_convolve)
317 end subroutine poisson_nfft_convolve
318
319 ! ---------------------------------------------------------
320 subroutine poisson_nfft_end(this)
321 type(poisson_nfft_t), intent(inout) :: this
322
323 push_sub(poisson_nfft_end)
324
325 call finufft_end(this%finufft)
326 safe_deallocate_a(this%kernel)
327
328 pop_sub(poisson_nfft_end)
329 end subroutine poisson_nfft_end
330
331end module poisson_nfft_oct_m
332
333!! Local Variables:
334!! mode: f90
335!! coding: utf-8
336!! End:
Copies a vector x, to a vector y.
Definition: lalg_basic.F90:220
scales a vector by a constant
Definition: lalg_basic.F90:170
Non-equispaced FFTs through the C interface of the FINUFFT library.
Definition: finufft.F90:118
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 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, 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_four
Definition: global.F90:204
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_half
Definition: global.F90:206
real(real64), parameter, public m_one
Definition: global.F90:201
real(real64), parameter, public m_three
Definition: global.F90:203
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
subroutine, public messages_not_implemented(feature, namespace)
Definition: messages.F90:1068
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_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:594
real(real64) function, public poisson_cutoff_3d_0d(x, r)
Poisson solver on the mesh points themselves, through non-equispaced FFTs.
subroutine poisson_nfft_build_3d_0d(this, ll, r_c)
The Coulomb kernel of a finite system on the mode grid.
subroutine, public dpoisson_nfft_solve(this, mesh, pot, rho)
subroutine, public poisson_nfft_end(this)
subroutine, public zpoisson_nfft_solve(this, mesh, pot, rho)
The transform is complex either way, so a complex density costs the same as a real one and needs no s...
subroutine, public poisson_nfft_init(this, namespace, space, mesh, threshold)
subroutine poisson_nfft_convolve(this, values)
Convolve the weighted density on the nodes with the kernel, in place.
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
Definition: profiling.F90:668
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
Definition: profiling.F90:600
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
Definition: unit.F90:134
character(len=20) pure function, public units_abbrev(this)
Definition: unit.F90:225
This module defines the unit system, used for input and output.
type(unit_system_t), public units_out
Describes mesh distribution to nodes.
Definition: mesh.F90:188