Octopus
poisson_fmm.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2006 M. Marques, A. Castro, A. Rubio, G. Bertsch,
2!! J. Alberdi-Rodriguez, P. Garcia RisueƱo, M. Oliveira
3!!
4!! This program is free software; you can redistribute it and/or modify
5!! it under the terms of the GNU General Public License as published by
6!! the Free Software Foundation; either version 2, or (at your option)
7!! any later version.
8!!
9!! This program is distributed in the hope that it will be useful,
10!! but WITHOUT ANY WARRANTY; without even the implied warranty of
11!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12!! GNU General Public License for more details.
13!!
14!! You should have received a copy of the GNU General Public License
15!! along with this program; if not, write to the Free Software
16!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17!! 02110-1301, USA.
18!!
19
20#include "global.h"
21#ifdef HAVE_LIBFM
22#include "fcs_fconfig.h"
23#endif
24
28 use cube_oct_m
29 use debug_oct_m
31 use fft_oct_m
32 use global_oct_m
33 use index_oct_m
35 use, intrinsic :: iso_fortran_env
38 use math_oct_m
39 use mesh_oct_m
43 use mpi_oct_m
47 use parser_oct_m
49 use space_oct_m
51
52#ifdef HAVE_LIBFM
53 use fcs_module
54 use iso_fortran_env
55#endif
56
57 implicit none
58
59 private
60
61 public :: &
66
67#ifndef HAVE_LIBFM
68 integer, parameter :: fcs_integer_kind_isoc = 4
69#endif
70
71 type poisson_fmm_t
72 private
73 real(real64) :: delta_E_fmm
74 real(real64) :: alpha_fmm
75 type(mpi_grp_t) :: all_nodes_grp
76 type(mpi_grp_t) :: perp_grp
77 integer(kind = fcs_integer_kind_isoc) :: nlocalcharges
78 integer :: sp
79 integer :: ep
80 integer, allocatable :: disps(:)
81 integer, allocatable :: dsize(:)
82 type(nl_operator_t) :: corrector
83 type(derivatives_t), pointer :: der
84 type(c_ptr) :: handle
85 end type poisson_fmm_t
86
87contains
88
89 ! ---------------------------------------------------------
92 subroutine poisson_fmm_init(this, space, der, all_nodes_comm)
93 type(poisson_fmm_t), intent(out) :: this
94 class(space_t), intent(in) :: space
95 type(derivatives_t), target, intent(in) :: der
96 type(MPI_Comm), intent(in) :: all_nodes_comm
97
98#ifdef HAVE_LIBFM
99 integer :: is
100 logical, allocatable :: remains(:)
101 integer, allocatable :: dend(:)
102 integer :: subcomm, cdim
103 character(len = 8) :: method
104 type(c_ptr) :: ret
105 type(mesh_t), pointer :: mesh
106
107 logical :: short_range_flag
108 real(kind = fcs_real_kind_isoc), dimension(3) :: box_a
109 real(kind = fcs_real_kind_isoc), dimension(3) :: box_b
110 real(kind = fcs_real_kind_isoc), dimension(3) :: box_c
111 real(kind = fcs_real_kind_isoc), dimension(3) :: offset
112 logical, dimension(3) :: periodicity
113 integer(kind = fcs_integer_kind_isoc) :: total_particles
114
115 integer(kind = fcs_integer_kind_isoc) :: local_particle_count
116 real(kind = fcs_real_kind_isoc), dimension(8) :: local_charges
117 real(kind = fcs_real_kind_isoc), dimension(24) :: local_coordinates
119 push_sub(poisson_fmm_init)
120
121 short_range_flag = .true.
122 offset = (/m_zero,m_zero,m_zero/)
123 periodicity = (/.false.,.false.,.false./)
124 local_particle_count = -1
125
126 method = "fmm"
127 !%Variable DeltaEFMM
128 !%Type float
129 !%Default 0.0001
130 !%Section Hamiltonian::Poisson
131 !%Description
132 !% Dimensionless parameter for relative convergence of <tt>PoissonSolver = FMM</tt>.
133 !% Sets energy error bound.
134 !% Strong inhomogeneous systems may violate the error bound.
135 !% For inhomogeneous systems we have an error-controlled sequential version available
136 !% (from Ivo Kabadshow).
137 !%
138 !% Our implementation of FMM (based on H. Dachsel, <i>J. Chem. Phys.</i> <b>131</b>,
139 !% 244102 (2009)) can keep the error of the Hartree energy below an
140 !% arbitrary bound. The quotient of the value chosen for the maximum
141 !% error in the Hartree energy and the value of the Hartree energy is
142 !% <tt>DeltaEFMM</tt>.
143 !%
144 !%End
145 call parse_variable(parser, 'DeltaEFMM', 1e-4_real64, this%delta_E_fmm)
146
147 !%Variable AlphaFMM
148 !%Type float
149 !%Default 0.291262136
150 !%Section Hamiltonian::Poisson
151 !%Description
152 !% Dimensionless parameter for the correction of the self-interaction of the
153 !% electrostatic Hartree potential, when using <tt>PoissonSolver = FMM</tt>.
154 !%
155 !% Octopus represents charge density on a real-space grid, each
156 !% point containing a value <math>\rho</math> corresponding to the charge
157 !% density in the cell centered in such point. Therefore, the
158 !% integral for the Hartree potential at point <math>i</math>, <math>V_H(i)</math>, can be reduced to a summation:
159 !%
160 !% <math>V_H(i) = \frac{\Omega}{4\pi\varepsilon_0} \sum_{i \neq j}
161 !% \frac{\rho(\vec{r}(j))}{|\vec{r}(j) - \vec{r}(i)|} + V_{self.int.}(i)</math>
162 !% where <math>\Omega</math> is the volume element of the mesh, and <math>\vec{r}(j)</math> is the
163 !% position of the point <math>j</math>. The <math>V_{self.int.}(i)</math> corresponds to
164 !% the integral over the cell centered on the point <math>i</math> that is necessary to
165 !% calculate the Hartree potential at point <math>i</math>:
166 !%
167 !% <math>V_{self.int.}(i)=\frac{1}{4\pi\varepsilon_0}
168 !% \int_{\Omega(i)}d\vec{r} \frac{\rho(\vec{r}(i))}{|\vec{r}-\vec{r}(i)|}</math>
169 !%
170 !% In the FMM version implemented into Octopus, a correction method
171 !% for <math>V_H(i)</math> is used
172 !% (see Garc&iacute;a-Risue&ntilde;o <i>et al.</i>, <i>J. Comp. Chem.</i> <b>35</b>, 427 (2014)).
173 !% This method defines cells neighbouring cell <math>i</math>, which
174 !% have volume <math>\Omega(i)/8</math> (in 3D) and charge density obtained by
175 !% interpolation. In the calculation of <math>V_H(i)</math>, in order to avoid
176 !% double counting of charge, and to cancel part of the errors arising
177 !% from considering the distances constant in the summation above, a
178 !% term <math>-\alpha_{FMM}V_{self.int.}(i)</math> is added to the summation (see
179 !% the paper for the explicit formulae).
180 !%End
181 call parse_variable(parser, 'AlphaFMM', 0.291262136_real64, this%alpha_fmm)
182
183 ! FMM: Variable periodic sets periodicity
184 ! 0 = open system
185 ! 1 = 1D periodic system
186 ! 2 = 2D periodic system
187 ! 3 = 3D periodic system
188
189 call mpi_grp_init(this%all_nodes_grp, all_nodes_comm)
190
191 this%der => der
192
193 if (mpi_world%size == 1) then
194 cdim = 1
196 safe_allocate(this%disps(1))
197 safe_allocate(dend(1))
198 safe_allocate(this%dsize(1))
199
200 dend = der%mesh%np
201 this%sp = 1
202 this%ep = der%mesh%np
203 this%dsize(1) = der%mesh%np
204 this%disps = 0
205 this%nlocalcharges = this%dsize(1)
207 subcomm = this%all_nodes_grp%comm
208
209 else
210#ifdef HAVE_MPI
211 call mpi_cartdim_get(this%all_nodes_grp%comm, cdim, mpi_err)
212
213 safe_allocate(remains(1:cdim))
214
215 remains = .true.
216 remains(1) = .false.
217
218 call mpi_cart_sub(this%all_nodes_grp%comm, remains(1), subcomm, mpi_err)
219 call mpi_grp_init(this%perp_grp, subcomm)
220
221 safe_allocate(this%disps(1:this%perp_grp%size))
222 safe_allocate(dend(1:this%perp_grp%size))
223 safe_allocate(this%dsize(1:this%perp_grp%size))
224
225 call multicomm_divide_range(der%mesh%np, this%perp_grp%size, this%disps, dend, this%dsize)
226
227 this%sp = this%disps(this%perp_grp%rank + 1)
228 this%ep = dend(this%perp_grp%rank + 1)
229 this%nlocalcharges = this%dsize(this%perp_grp%rank + 1)
230 this%nlocalcharges = der%mesh%np
231 this%disps = this%disps - 1
232
233#endif
234 end if
235
236 mesh => der%mesh
237
238 if (space%is_periodic()) then
239 select type (box => mesh%box)
240 type is (box_parallelepiped_t)
241 if (.not. all(box%half_length == box%half_length(1))) then
242 call messages_not_implemented("FMM Poisson solver with non-cubic boxes", namespace=namespace)
243 end if
244 class default
245 call messages_not_implemented("FMM Poisson solver with non-cubic boxes", namespace=namespace)
246 end select
247 end if
248
249 total_particles = mesh%np_global
250 ret = fcs_init(this%handle, trim(adjustl(method)) // c_null_char, subcomm)
251
252 box_a = (/mesh%box%bounding_box_l(1)*2+1,m_zero,m_zero/)
253 box_b = (/m_zero,mesh%box%bounding_box_l(2)*2+1,m_zero/)
254 box_c = (/m_zero,m_zero,mesh%box%bounding_box_l(3)*2+1/)
255
256 ret = fcs_common_set(this%handle, short_range_flag, box_a, box_b, box_c, offset, periodicity, total_particles)
257
258 ! this is how you set a relative error in scafacos for the FMM
259 ret = fcs_fmm_set_tolerance_energy(this%handle, this%delta_E_fmm)
260
261 call nl_operator_init(this%corrector, "FMM Correction")
262 call stencil_star_get_lapl(this%corrector%stencil, der%dim, 2)
263 call nl_operator_build(this%der%mesh, this%corrector, der%mesh%np, const_w = .not. this%der%mesh%use_curvilinear)
264
265 do is = 1, this%corrector%stencil%size
266
267 select case (sum(abs(this%corrector%stencil%points(1:der%dim, is))))
268 case (0)
269 this%corrector%w_re(is, 1) = 27.0_real64/32.0_real64 + &
270 (m_one - this%alpha_fmm)*m_two*m_pi*(m_three/(m_pi*m_four))**(m_two/m_three)
271 case (1)
272 this%corrector%w_re(is, 1) = 0.0625_real64
273 case (2)
274 this%corrector%w_re(is, 1) = -0.0625_real64*0.25_real64
275 end select
276
277 this%corrector%w_re(is, 1) = this%corrector%w_re(is, 1)*der%mesh%spacing(1)*der%mesh%spacing(2)
278 end do
279
280 call nl_operator_output_weights(this%corrector)
281 pop_sub(poisson_fmm_init)
282#endif
283 end subroutine poisson_fmm_init
284
285 ! ---------------------------------------------------------
287 subroutine poisson_fmm_end(this)
288 type(poisson_fmm_t), intent(inout) :: this
289
290#ifdef HAVE_LIBFM
291 type(c_ptr) :: ret
292
293 push_sub(poisson_fmm_end)
294
295 call nl_operator_end(this%corrector)
296
297 if (mpi_world%size > 1) call mpi_comm_free(this%perp_grp%comm, mpi_err)
298 safe_deallocate_a(this%disps)
299 safe_deallocate_a(this%dsize)
300 ret = fcs_destroy(this%handle)
301
302 pop_sub(poisson_fmm_end)
303#endif
304 end subroutine poisson_fmm_end
305
306 ! ---------------------------------------------------------
312 subroutine poisson_fmm_solve(this, der, pot, rho)
313 type(poisson_fmm_t), intent(in) :: this
314 type(derivatives_t), target, intent(in) :: der
315 real(real64), intent(inout) :: pot(:)
316 real(real64), intent(in) :: rho(:)
317
318#ifdef HAVE_LIBFM
319 integer(int64) :: totalcharges
320
321 real(kind = fcs_real_kind_isoc), allocatable :: q(:)
322 real(kind = fcs_real_kind_isoc), allocatable :: pot_lib_fmm(:)
323 real(kind = fcs_real_kind_isoc), allocatable :: xyz(:)
324 real(kind = fcs_real_kind_isoc), allocatable :: fields(:)
325 integer(kind = fcs_integer_kind_isoc) :: index
326 real(real64), allocatable :: rho_tmp(:), pot_tmp(:)
327 real(real64) :: aux
328 integer :: ii, jj, ip, gip
329 type(c_ptr) :: ret
330 integer, allocatable :: ix(:)
331 type(mesh_t), pointer :: mesh
332
333
334 push_sub(poisson_fmm_solve)
335
336 mesh => der%mesh
337
338 call profiling_in("POISSON_FMM")
339
340 ! allocate buffers.
341 safe_allocate(q(this%sp:this%ep))
342 safe_allocate(xyz(1:3 * (this%nlocalcharges)))
343 safe_allocate(pot_lib_fmm(this%sp:this%ep))
344 safe_allocate(fields(1:3 * (this%nlocalcharges)))
345
346 totalcharges = mesh%np_global
347
348 if (.not. mesh%use_curvilinear) then
349 do ii = this%sp, this%ep
350 q(ii) = rho(ii) * mesh%vol_pp(1)
351 end do
352 else
353 do ii = this%sp, this%ep
354 q(ii) = rho(ii) * mesh%vol_pp(ii)
355 end do
356 end if
357
358 ! invert the indices
359 index = 0
360 do ii = this%sp, this%ep
361 do jj = 1, der%dim
362 index = index + 1
363 xyz(index) = mesh%x(ii, jj)
364 end do
365 end do
366
367 pot_lib_fmm = m_zero
368 fields = m_zero
369
370 call profiling_in("FMM_LIB")
371 ret = fcs_tune(this%handle, this%nlocalcharges, this%nlocalcharges, xyz(1), q(this%sp))
372 ret = fcs_run(this%handle, this%nlocalcharges, this%nlocalcharges, xyz(1), q(this%sp), fields(1), &
373 pot_lib_fmm(this%sp))
374 call profiling_out("FMM_LIB")
375
376 call profiling_in("FMM_GATHER")
377 if (mpi_world%size > 1) then
378 !now we need to allgather the results between "states"
379 call this%perp_grp%allgatherv(pot_lib_fmm(this%sp:), this%nlocalcharges, mpi_double_precision, &
380 pot, this%dsize, this%disps, mpi_double_precision)
381 else
382 pot = pot_lib_fmm
383 end if
384 call profiling_out("FMM_GATHER")
385
386 ! Correction for cell self-interaction in 2D (cell assumed to be circular)
387 if (mesh%box%dim == 2) then
388 aux = m_two*m_pi * mesh%spacing(1)
389 do ii = 1, mesh%np
390 pot(ii) = pot(ii) + aux*rho(ii)
391 end do
392 end if
393
394 safe_deallocate_a(q)
395 safe_deallocate_a(xyz)
396 safe_deallocate_a(pot_lib_fmm)
397
398 ! Apply the parallel correction
399 call profiling_in("FMM_CORR")
400
401 safe_allocate(ix(1:mesh%box%dim))
402 safe_allocate(rho_tmp(1:mesh%np_part))
403 safe_allocate(pot_tmp(1:mesh%np))
404
405 call lalg_copy(mesh%np, rho, rho_tmp)
406
407 ! FMM just calculates contributions from other cells. for
408 ! self-interaction cell integration, we include (as traditional in
409 ! octopus) an approximate integration using a spherical cell whose
410 ! volume is the volume of the actual cell Next line is only valid
411 ! for 3D
412 if (mesh%box%dim == 3) then
413 if (.not. mesh%use_curvilinear .and. (mesh%spacing(1) == mesh%spacing(2)) .and. &
414 (mesh%spacing(2) == mesh%spacing(3)) .and. &
415 (mesh%spacing(1) == mesh%spacing(3))) then
416
417 call dderivatives_perform(this%corrector, der, rho_tmp, pot_tmp)
418
419 do ip = 1, mesh%np
420 pot(ip) = pot(ip) + pot_tmp(ip)
421 end do
422
423 else ! Not common mesh; we add the self-interaction of the cell
424 do ii = 1, mesh%np
425 aux = m_two*m_pi*(m_three*mesh%vol_pp(ii)/(m_pi*m_four))**(m_two/m_three)
426 pot(ii) = pot(ii) + aux*rho_tmp(ii)
427 end do
428 end if
429 end if
430
431 safe_deallocate_a(ix)
432 safe_deallocate_a(rho_tmp)
433 safe_deallocate_a(pot_tmp)
434
435 call profiling_out("FMM_CORR")
436 call profiling_out("POISSON_FMM")
437
438 pop_sub(poisson_fmm_solve)
439#endif
440 end subroutine poisson_fmm_solve
441
442end module poisson_fmm_oct_m
443
444!! Local Variables:
445!! mode: f90
446!! coding: utf-8
447!! End:
Copies a vector x, to a vector y.
Definition: lalg_basic.F90:185
Module implementing boundary conditions in Octopus.
Definition: boundaries.F90:122
This module calculates the derivatives (gradients, Laplacians, etc.) of a function.
subroutine, public dderivatives_perform(op, der, ff, op_ff, ghost_update, set_bc, factor)
apply a nl_operator to a mesh function
Fast Fourier Transform module. This module provides a single interface that works with different FFT ...
Definition: fft.F90:118
real(real64), parameter, public m_two
Definition: global.F90:189
real(real64), parameter, public m_zero
Definition: global.F90:187
real(real64), parameter, public m_four
Definition: global.F90:191
real(real64), parameter, public m_pi
some mathematical constants
Definition: global.F90:185
real(real64), parameter, public m_one
Definition: global.F90:188
real(real64), parameter, public m_three
Definition: global.F90:190
This module implements the index, used for the mesh points.
Definition: index.F90:122
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:115
This module defines various routines, operating on mesh functions.
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:118
subroutine, public messages_not_implemented(feature, namespace)
Definition: messages.F90:1125
type(mpi_grp_t), public mpi_world
Definition: mpi.F90:262
subroutine mpi_grp_init(grp, comm)
Initialize MPI group instance.
Definition: mpi.F90:341
integer, public mpi_err
used to store return values of mpi calls
Definition: mpi.F90:265
This module handles the communicators for the various parallelization strategies.
Definition: multicomm.F90:145
subroutine, public multicomm_divide_range(nobjs, nprocs, istart, ifinal, lsize, scalapack_compat)
Divide the range of numbers [1, nobjs] between nprocs processors.
Definition: multicomm.F90:1037
This module defines non-local operators.
subroutine, public nl_operator_init(op, label)
initialize an instance of a non-local operator by setting the label
subroutine, public nl_operator_output_weights(this)
subroutine, public nl_operator_end(op)
subroutine, public nl_operator_build(space, mesh, op, np, const_w, regenerate)
Some general things and nomenclature:
Definition: par_vec.F90:171
subroutine, public poisson_fmm_init(this, space, der, all_nodes_comm)
Initialises the FMM parameters and vectors. Also it calls to the library initialisation.
subroutine, public poisson_fmm_solve(this, der, pot, rho)
Both direct solvers and FMM calculate the Hartree potential via direct additions, without solving the...
subroutine, public poisson_fmm_end(this)
Release memory and call to end the library.
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
Definition: profiling.F90:623
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
Definition: profiling.F90:552
This module defines routines, generating operators for a star stencil.
subroutine, public stencil_star_get_lapl(this, dim, order)
Class implementing a parallelepiped box. Currently this is restricted to a rectangular cuboid (all th...
class representing derivatives
Describes mesh distribution to nodes.
Definition: mesh.F90:185
int true(void)