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