Octopus
libvdwxc.F90
Go to the documentation of this file.
1#include "global.h"
2
3! Interface to libvdwxc.
4! Naming:
5! * Functions that start with libvdwxc_ are public, to be called from other parts of Octopus.
6! * Interfaces that start with vdwxc_ are actual functions of libvdwxc.
7
8
11 use cube_oct_m
13 use debug_oct_m
15 use fft_oct_m
16 use global_oct_m
18 use grid_oct_m
20 use mesh_oct_m
23 use mpi_oct_m
25 use parser_oct_m
26 use pfft_oct_m
28 use space_oct_m
30
31 implicit none
32
33 integer, parameter :: &
34 LIBVDWXC_MODE_AUTO = 1, &
36 libvdwxc_mode_mpi = 3!, &
37 !LIBVDWXC_MODE_PFFT = 3
38
39 private
40
41 public :: &
42 libvdwxc_t, &
49
50 type libvdwxc_t
51 private
52 integer, pointer :: libvdwxc_ptr
53 type(mesh_t) :: mesh
54 type(cube_t) :: cube
55 type(mesh_cube_parallel_map_t) :: mesh_cube_map
56 integer :: functional
57 logical :: debug
58 real(real64), public :: energy
59 real(real64) :: vdw_factor
60 end type libvdwxc_t
61
62#ifdef HAVE_LIBVDWXC
63 include "vdwxcfort.f90"
64#endif
65
66contains
67
68 subroutine libvdwxc_init(libvdwxc, namespace, functional)
69 type(libvdwxc_t), intent(out) :: libvdwxc
70 type(namespace_t), intent(in) :: namespace
71 integer, intent(in) :: functional
72
73 push_sub(libvdwxc_init)
74#ifdef HAVE_LIBVDWXC
75 call vdwxc_new(functional, libvdwxc%libvdwxc_ptr)
76#else
77 message(1) = "Octopus not compiled with libvdwxc"
78 call messages_fatal(1, namespace=namespace)
79#endif
80 assert(associated(libvdwxc%libvdwxc_ptr))
81 libvdwxc%functional = functional
82
83 !%Variable libvdwxcDebug
84 !%Type logical
85 !%Section Hamiltonian::XC
86 !%Description
87 !% Dump libvdwxc inputs and outputs to files.
88 !%End
89 call parse_variable(namespace, 'libvdwxcDebug', .false., libvdwxc%debug)
90 pop_sub(libvdwxc_init)
91
92 !%Variable libvdwxcVDWFactor
93 !%Type float
94 !%Section Hamiltonian::XC
95 !%Description
96 !% Prefactor of non-local van der Waals functional.
97 !% Setting a prefactor other than one is wrong, but useful
98 !% for debugging.
99 !%End
100 call parse_variable(namespace, 'libvdwxcVDWFactor', m_one, libvdwxc%vdw_factor)
101 end subroutine libvdwxc_init
102
103 subroutine libvdwxc_print(this)
104 type(libvdwxc_t), intent(in) :: this
105 push_sub(libvdwxc_print)
106
107#ifdef HAVE_LIBVDWXC
108 call vdwxc_print(this%libvdwxc_ptr)
109#endif
110
111 pop_sub(libvdwxc_print)
112 end subroutine libvdwxc_print
113
114 subroutine libvdwxc_write_info(this, iunit, namespace)
115 type(libvdwxc_t), intent(in) :: this
116 integer, optional, intent(in) :: iunit
117 type(namespace_t), optional, intent(in) :: namespace
118
119 push_sub(libvdwxc_write_info)
120
121 write(message(1), '(2x,a)') 'Correlation'
122 if (this%functional == 1) then
123 write(message(2), '(4x,a)') 'vdW-DF from libvdwxc'
124 else if (this%functional == 2) then
125 write(message(2), '(4x,a)') 'vdW-DF2 from libvdwxc'
126 else if (this%functional == 3) then
127 write(message(2), '(4x,a)') 'vdW-DF-cx from libvdwxc'
128 else
129 write(message(2), '(4x,a)') 'unknown libvdwxc functional'
130 end if
131 call messages_info(2, iunit=iunit, namespace=namespace)
132
133 pop_sub(libvdwxc_write_info)
134 end subroutine libvdwxc_write_info
135
136 subroutine libvdwxc_set_geometry(this, namespace, space, mesh)
137 type(libvdwxc_t), intent(inout) :: this
138 type(namespace_t), intent(in) :: namespace
139 class(space_t), intent(in) :: space
140 class(mesh_t), intent(in) :: mesh
141
142 integer :: blocksize
143 integer :: libvdwxc_mode
144 type(space_t) :: cube_space
146 push_sub(libvdwxc_set_geometry)
147 this%mesh = mesh
149 ! libvdwxc can use either FFTW-MPI or PFFT and Octopus should not
150 ! care too much, as long as this particular cube is properly
151 ! configured. Unfortunately most of the args to cube_init are
152 ! very entangled with FFT libraries. All we want and need is to
153 ! specify our own decomposition and pass that to libvdwxc, but
154 ! we can only say "we want to use PFFT" and then it does the
155 ! decomposition.
156 libvdwxc_mode = libvdwxc_mode_auto
157
158 !%Variable libvdwxcMode
159 !%Type integer
160 !%Section Hamiltonian::XC
161 !%Description
162 !% Whether libvdwxc should run with serial fftw3, fftw3-mpi, or pfft.
163 !% to specify fftw3-mpi in serial for debugging.
164 !% pfft is not implemented at the moment.
165 !%Option libvdwxc_mode_auto 1
166 !% Use serial fftw3 if actually running in serial, else fftw3-mpi.
167 !%Option libvdwxc_mode_serial 2
168 !% Run with serial fftw3. Works only when not parallelizing over domains.
169 !%Option libvdwxc_mode_mpi 3
170 !% Run with fftw3-mpi. Works only if Octopus is compiled with MPI.
171 !%End
172 call parse_variable(namespace, 'libvdwxcMode', libvdwxc_mode_auto, libvdwxc_mode)
173
174 if (libvdwxc_mode == libvdwxc_mode_auto) then
175 if (mesh%mpi_grp%size == 1) then
176 libvdwxc_mode = libvdwxc_mode_serial
177 else
178 libvdwxc_mode = libvdwxc_mode_mpi
179 end if
180 end if
181
182 ! TODO implement. Should dump quantities to files.
183
184 blocksize = mesh%idx%ll(3) / mesh%mpi_grp%size
185 if (mod(mesh%idx%ll(3), mesh%mpi_grp%size) /= 0) then
186 blocksize = blocksize + 1
187 end if
188
189 ! Here we need to assume that the space is periodic as libvdwxc assumes a
190 ! periodic space. This affects the conversion from lattice vectors to the spacing
191 cube_space%dim = space%dim
192 cube_space%periodic_dim = space%dim
193
194 if (libvdwxc_mode == libvdwxc_mode_serial) then
195 call cube_init(this%cube, mesh%idx%ll, namespace, cube_space, mesh%spacing, &
196 mesh%coord_system)
197 call cube_init_cube_map(this%cube, mesh)
198 else
199 call cube_init(this%cube, mesh%idx%ll, namespace, cube_space, mesh%spacing, &
200 mesh%coord_system, mpi_grp = mesh%mpi_grp, &
201 need_partition = .true., blocksize = blocksize)
202 call cube_init_cube_map(this%cube, mesh)
203 call mesh_cube_parallel_map_init(this%mesh_cube_map, mesh, this%cube)
204 end if
205
206 ! There is some low-level implementation issue where with PFFT,
207 ! the leading dimension is one smaller than normal for some reason.
208 ! Therefore we cannot use the PFFT stuff without a frightful mess.
210#ifdef HAVE_LIBVDWXC
211 call vdwxc_set_unit_cell(this%libvdwxc_ptr, &
212 this%cube%rs_n_global(3), this%cube%rs_n_global(2), this%cube%rs_n_global(1), &
213 this%cube%latt%rlattice(3, 3), this%cube%latt%rlattice(2, 3), this%cube%latt%rlattice(1, 3), &
214 this%cube%latt%rlattice(3, 2), this%cube%latt%rlattice(2, 2), this%cube%latt%rlattice(1, 2), &
215 this%cube%latt%rlattice(3, 1), this%cube%latt%rlattice(2, 1), this%cube%latt%rlattice(1, 1))
216
217 if (libvdwxc_mode == libvdwxc_mode_serial) then
218 call vdwxc_init_serial(this%libvdwxc_ptr)
219 else
220#ifdef HAVE_LIBVDWXC_MPI
221 call vdwxc_init_mpi(this%libvdwxc_ptr, mesh%mpi_grp%comm%MPI_VAL)
222#else
223 message(1) = "libvdwxc was not compiled with MPI"
224 message(2) = "Recompile libvdwxc with MPI for vdW with domain decomposition"
225 call messages_fatal(2, namespace=namespace)
226#endif
227 end if
228 call vdwxc_print(this%libvdwxc_ptr)
229#endif
230
232 end subroutine libvdwxc_set_geometry
233
234 subroutine libvdwxc_calculate(this, namespace, space, rho, gradrho, dedd, dedgd)
235 type(libvdwxc_t), intent(inout) :: this
236 type(namespace_t), intent(in) :: namespace
237 class(space_t), intent(in) :: space
238 real(real64), dimension(:,:), contiguous, intent(inout) :: rho !!! data type
239 real(real64), dimension(:,:,:), contiguous, intent(in) :: gradrho
240 real(real64), dimension(:,:), contiguous, intent(inout) :: dedd
241 real(real64), dimension(:,:,:), contiguous, intent(inout) :: dedgd
242
243 type(cube_function_t) :: cf!rhocf, sigmacf, dedrhocf, dedsigmacf
244
245 real(real64), allocatable :: workbuffer(:)
246 real(real64), allocatable :: cube_rho(:,:,:), cube_sigma(:,:,:), cube_dedrho(:,:,:), cube_dedsigma(:,:,:)
247 real(real64), dimension(3) :: energy_and_integrals_buffer
248 real(real64) :: vsum
249 integer :: ii, is, idir
250
251 push_sub(libvdwxc_calculate)
252
253 assert(size(rho, 2) == 1)
254 assert(size(gradrho, 3) == 1)
255 assert(size(dedd, 2) == 1)
256 assert(size(dedgd, 3) == 1)
257
258 ! Well. I thought we would be using four different cube functions
259 ! for rho, sigma, dedrho and dedsigma.
260 !
261 ! But since the cube has PFFT associated, any attempt to use it
262 ! (update: We do not actually use PFFT anymore, so maybe this will
263 ! not be that broken now)
264 ! results in some kind of behind-the-scenes use of a possibly
265 ! global FFT-related buffer.
266 ! (Note: actually we now use cubes without FFT lib)
267 !
268 ! cube functions take a force_alloc variable which appears to make
269 ! them allocate their own buffer (like we want), but then Octopus
270 ! segfaults on cube function free, which I suspect is a bug in the
271 ! code, or at the very least due to something so undocumented that
272 ! I cannot reasonably figure it out.
273 !
274 ! We could just disable PFFT for the cube (since we will never
275 ! actually call any FFT from Octopus) and do our own
276 ! redistribution, but the redistribution code is loaded with
277 ! references to FFT library, so this would probably be a bit
278 ! optimistic.
279 !
280 ! So we create here our own arrays over which we have reasonable control.
281
282 safe_allocate(workbuffer(1:this%mesh%np))
283 safe_allocate(cube_rho(1:this%cube%rs_n(1), 1:this%cube%rs_n(2), 1:this%cube%rs_n(3)))
284 safe_allocate(cube_sigma(1:this%cube%rs_n(1), 1:this%cube%rs_n(2), 1:this%cube%rs_n(3)))
285 safe_allocate(cube_dedrho(1:this%cube%rs_n(1), 1:this%cube%rs_n(2), 1:this%cube%rs_n(3)))
286 safe_allocate(cube_dedsigma(1:this%cube%rs_n(1), 1:this%cube%rs_n(2), 1:this%cube%rs_n(3)))
287 ! This is sigma, the absolute-squared density gradient:
288 workbuffer(:) = sum(gradrho(:, :, 1)**2, 2)
289
290 if (this%debug) then
291 call libvdwxc_write_array(rho(:, 1), 'rho')
292 call libvdwxc_write_array(workbuffer, 'gradrho')
293 end if
294
295 cube_rho = m_zero
296 cube_sigma = m_zero
297 cube_dedrho = m_zero
298 cube_dedsigma = m_zero
299
300 call dcube_function_alloc_rs(this%cube, cf, in_device = .false.)
301
302 call tocube(rho(:, 1), cube_rho)
303 call tocube(workbuffer, cube_sigma)
304
305 this%energy = m_zero
306#ifdef HAVE_LIBVDWXC
307 call vdwxc_calculate(this%libvdwxc_ptr, cube_rho, cube_sigma, cube_dedrho, cube_dedsigma, this%energy)
308#endif
309 this%energy = this%energy * this%vdw_factor
310 cube_dedrho = cube_dedrho * this%vdw_factor
311 cube_dedsigma = cube_dedsigma * this%vdw_factor
312
313 call fromcube(cube_dedrho, workbuffer)
314 ! dedd is 1:mesh%np_part for some reason
315 if (this%debug) then
316 call libvdwxc_write_array(workbuffer, 'dedrho')
317 end if
318 call lalg_axpy(this%mesh%np, m_one, workbuffer, dedd(:, 1))
319 call fromcube(cube_dedsigma, workbuffer)
320 if (this%debug) then
321 call libvdwxc_write_array(workbuffer, 'dedsigma')
322 end if
323 do ii = 1, this%mesh%np
324 dedgd(ii, :, 1) = dedgd(ii, :, 1) + m_two * workbuffer(ii) * gradrho(ii, :, 1)
325 end do
326
327 energy_and_integrals_buffer(1) = this%energy
328
329 vsum = m_zero
330 !$omp parallel do reduction(+:vsum) collapse(2)
331 do is = 1, size(rho, 2)
332 do ii = 1, this%mesh%np
333 vsum = vsum + rho(ii, is) * dedd(ii, is)
334 end do
335 end do
336 !$omp end parallel do
337 energy_and_integrals_buffer(2) = vsum * this%mesh%volume_element
338
339 vsum = m_zero
340 !$omp parallel do reduction(+:vsum) collapse(3)
341 do idir = 1, size(gradrho, 3)
342 do is = 1, size(gradrho, 2)
343 do ii = 1, this%mesh%np
344 vsum = vsum + gradrho(ii, is, idir) * dedgd(ii, is, idir)
345 end do
346 end do
347 end do
348 !$omp end parallel do
349 energy_and_integrals_buffer(3) = vsum * this%mesh%volume_element
350
351 call this%mesh%mpi_grp%allreduce_inplace(energy_and_integrals_buffer(1), 3, mpi_double_precision, mpi_sum)
352 this%energy = energy_and_integrals_buffer(1)
353 write(message(1), '(a,f18.10,a)') 'libvdwxc non-local correlation energy: ', energy_and_integrals_buffer(1), ' Ha'
354 write(message(2), '(a,f18.10)') ' n-dedn integral: ', energy_and_integrals_buffer(2)
355 write(message(3), '(a,f18.10)') ' gradn-dedgradn integral: ', energy_and_integrals_buffer(3)
356 call messages_info(3, namespace=namespace)
357
358 safe_deallocate_a(workbuffer)
359 safe_deallocate_a(cube_rho)
360 safe_deallocate_a(cube_sigma)
361 safe_deallocate_a(cube_dedrho)
362 safe_deallocate_a(cube_dedsigma)
363 call dcube_function_free_rs(this%cube, cf)
364
365 pop_sub(libvdwxc_calculate)
366
367 contains
368
369 subroutine tocube(array, cubearray)
370 real(real64), contiguous, intent(in) :: array(:)
371 real(real64), intent(out) :: cubearray(:,:,:)
372
373 push_sub(libvdwxc_calculate.tocube)
374
375 if (this%cube%parallel_in_domains) then
376 call dmesh_to_cube_parallel(this%mesh, array, this%cube, cf, this%mesh_cube_map)
377 else
378 call dmesh_to_cube(this%mesh, array, this%cube, cf)
379 end if
380 cubearray(:,:,:) = cf%dRS
382 end subroutine tocube
383
384 subroutine fromcube(cubearray, array)
385 real(real64), intent(in) :: cubearray(:,:,:)
386 real(real64), contiguous, intent(out) :: array(:)
387
389 cf%dRS = cubearray
390 if (this%cube%parallel_in_domains) then
391 call dcube_to_mesh_parallel(this%cube, cf, this%mesh, array, this%mesh_cube_map)
392 else
393 call dcube_to_mesh(this%cube, cf, this%mesh, array)
394 end if
396 end subroutine fromcube
397
398 subroutine libvdwxc_write_array(arr, fname)
399 real(real64), intent(in) :: arr(:)
400 character(len=*), intent(in) :: fname
401 integer :: ierr
402
403 call dio_function_output(option__outputformat__dx, 'libvdwxc-debug', &
404 fname, namespace, space, this%mesh, arr, unit_one, ierr)
405 end subroutine libvdwxc_write_array
406
407 end subroutine libvdwxc_calculate
408
409 subroutine libvdwxc_end(this)
410 type(libvdwxc_t), intent(inout) :: this
411 push_sub(libvdwxc_end)
412
413#ifdef HAVE_LIBVDWXC
414 call vdwxc_finalize(this%libvdwxc_ptr)
415#endif
416
417 call cube_end(this%cube)
418 call mesh_cube_parallel_map_end(this%mesh_cube_map)
419
420 pop_sub(libvdwxc_end)
421 end subroutine libvdwxc_end
422
423end module libvdwxc_oct_m
424
425!! Local Variables:
426!! mode: f90
427!! coding: utf-8
428!! End:
constant times a vector plus a vector
Definition: lalg_basic.F90:173
subroutine libvdwxc_write_array(arr, fname)
Definition: libvdwxc.F90:473
subroutine fromcube(cubearray, array)
Definition: libvdwxc.F90:459
subroutine tocube(array, cubearray)
Definition: libvdwxc.F90:444
subroutine, public dmesh_to_cube(mesh, mf, cube, cf)
Convert a function from the mesh to the cube.
subroutine, public dcube_to_mesh(cube, cf, mesh, mf)
Convert a function from the cube to the mesh.
subroutine, public dcube_function_alloc_rs(cube, cf, in_device, force_alloc)
Allocates locally the real space grid, if PFFT library is not used. Otherwise, it assigns the PFFT re...
subroutine, public dcube_function_free_rs(cube, cf)
Deallocates the real space grid.
subroutine, public dmesh_to_cube_parallel(mesh, mf, cube, cf, map)
The next two subroutines convert a function between the normal mesh and the cube in parallel.
subroutine, public dcube_to_mesh_parallel(cube, cf, mesh, mf, map)
subroutine, public cube_init(cube, nn, namespace, space, spacing, coord_system, fft_type, fft_library, dont_optimize, nn_out, mpi_grp, need_partition, tp_enlarge, blocksize)
Definition: cube.F90:204
subroutine, public cube_end(cube)
Definition: cube.F90:387
subroutine, public cube_init_cube_map(cube, mesh)
Definition: cube.F90:824
This module calculates the derivatives (gradients, Laplacians, etc.) of a function.
Fast Fourier Transform module. This module provides a single interface that works with different FFT ...
Definition: fft.F90:120
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_one
Definition: global.F90:201
This module implements the underlying real-space grid.
Definition: grid.F90:119
subroutine, public dio_function_output(how, dir, fname, namespace, space, mesh, ff, unit, ierr, pos, atoms, grp, root)
Top-level IO routine for functions defined on the mesh.
subroutine, public libvdwxc_end(this)
Definition: libvdwxc.F90:484
subroutine, public libvdwxc_init(libvdwxc, namespace, functional)
Definition: libvdwxc.F90:164
subroutine, public libvdwxc_write_info(this, iunit, namespace)
Definition: libvdwxc.F90:210
integer, parameter libvdwxc_mode_serial
Definition: libvdwxc.F90:128
integer, parameter libvdwxc_mode_mpi
Definition: libvdwxc.F90:128
subroutine, public libvdwxc_print(this)
Definition: libvdwxc.F90:199
subroutine, public libvdwxc_set_geometry(this, namespace, space, mesh)
Definition: libvdwxc.F90:232
subroutine, public libvdwxc_calculate(this, namespace, space, rho, gradrho, dedd, dedgd)
Definition: libvdwxc.F90:309
subroutine, public mesh_cube_parallel_map_end(this)
subroutine, public mesh_cube_parallel_map_init(this, mesh, cube)
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
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
The low level module to work with the PFFT library. http:
Definition: pfft.F90:128
This module defines the unit system, used for input and output.
type(unit_t), public unit_one
some special units required for particular quantities
Describes mesh distribution to nodes.
Definition: mesh.F90:187
int true(void)