Octopus
hirshfeld.F90
Go to the documentation of this file.
1!! Copyright (C) 2015 X. Andrade
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
21module hirshfeld_oct_m
22 use atom_oct_m
23 use debug_oct_m
25 use global_oct_m
26 use, intrinsic :: iso_fortran_env
28 use mesh_oct_m
32 use parser_oct_m
34 use ps_oct_m
36 use space_oct_m
40
41 implicit none
42
43 private
44 public :: &
52
53 type hirshfeld_t
54 private
55 class(mesh_t), pointer :: mesh
56 type(atom_t), pointer :: atom(:)
57 real(real64), pointer :: pos(:,:)
58 integer :: natoms
59 type(lattice_vectors_t), pointer :: latt
60 real(real64), allocatable :: total_density(:)
61 real(real64), allocatable :: free_volume(:)
62 real(real64), allocatable :: free_vol_r3(:,:)
63 integer :: nspin
64 type(namespace_t) :: namespace
65 end type hirshfeld_t
66
67 real(real64), parameter, public :: TOL_HIRSHFELD = 1e-9_real64
68
69contains
70
71 subroutine hirshfeld_init(this, mesh, namespace, space, latt, atom, natoms, pos, nspin)
72 type(hirshfeld_t), intent(out) :: this
73 class(mesh_t), target, intent(in) :: mesh
74 type(namespace_t), intent(in) :: namespace
75 class(space_t), intent(in) :: space
76 type(lattice_vectors_t), target, intent(in):: latt
77 type(atom_t), target, intent(in) :: atom(:)
78 integer, intent(in) :: natoms
79 real(real64), target, intent(in) :: pos(1:space%dim,1:natoms)
80 integer, intent(in) :: nspin
81
82 integer :: iatom, ip, isp
83 real(real64) :: rr, atom_pos(space%dim), rmax, den
84 real(real64), allocatable :: atom_density(:, :)
85 type(ps_t), pointer :: ps
86 type(lattice_iterator_t) :: latt_iter
87 integer :: icell
88 push_sub(hirshfeld_init)
89
90 call profiling_in("HIRSHFELD_INIT")
91
92 this%mesh => mesh
93 this%atom => atom
94 this%natoms = natoms
95 this%nspin = nspin
96 this%pos => pos
97 this%latt => latt
98 this%namespace = namespace
99
100 safe_allocate(this%total_density(1:mesh%np))
101 safe_allocate(this%free_volume(1:natoms))
102 safe_allocate(this%free_vol_r3(1:mesh%np, 1:natoms))
103 safe_allocate(atom_density(1:mesh%np, 1:nspin))
104
105 !$omp parallel private(iatom)
106 !$omp do simd
107 do ip = 1, mesh%np
108 this%total_density(ip) = m_zero
109 end do
110 !$omp end do simd nowait
111 do iatom = 1, natoms
112 !$omp do simd
113 do ip = 1, mesh%np
114 this%free_vol_r3(ip, iatom) = m_zero
115 end do
116 !$omp end do simd nowait
117 end do
118 !$omp end parallel
119
120 do iatom = 1, natoms
121 select type(spec=>atom(iatom)%species)
122 type is(pseudopotential_t)
123 ps => spec%ps
124 class default
125 assert(.false.)
126 end select
127
128 rmax = m_zero
129 do isp = 1, nspin
130 rmax = max(rmax, ps%density(isp)%x_threshold)
131 end do
132
133 latt_iter = lattice_iterator_t(latt, rmax)
134 do icell = 1, latt_iter%n_cells
135 atom_pos = pos(:, iatom) + latt_iter%get(icell)
136 !We get the non periodized density
137 !We need to do it to have the r^3 correctly computed for periodic systems
138 call species_atom_density_np(atom(iatom)%species, namespace, atom_pos, mesh, nspin, atom_density)
139
140 !$omp parallel do private(rr, den)
141 do ip = 1, mesh%np
142 rr = norm2(mesh%x(:, ip) - atom_pos)
143 den = sum(atom_density(ip, 1:nspin))
144
145 this%total_density(ip) = this%total_density(ip) + den
146 this%free_vol_r3(ip, iatom) = this%free_vol_r3(ip, iatom) + den*rr**3
147 end do
148 end do
149 this%free_volume(iatom) = dmf_integrate(this%mesh, this%free_vol_r3(:, iatom), reduce = .false.)
150 end do
152 call this%mesh%allreduce(this%free_volume)
154 safe_deallocate_a(atom_density)
156 call profiling_out("HIRSHFELD_INIT")
159 end subroutine hirshfeld_init
160
161 ! ------------------------------------------------
163 subroutine hirshfeld_end(this)
164 type(hirshfeld_t), intent(inout) :: this
165
167
168 safe_deallocate_a(this%total_density)
169 safe_deallocate_a(this%free_volume)
170 safe_deallocate_a(this%free_vol_r3)
171
172 nullify(this%mesh)
173 nullify(this%pos)
174 nullify(this%atom)
175 nullify(this%latt)
176
177 pop_sub(hirshfeld_end)
178 end subroutine hirshfeld_end
179
180 ! -----------------------------------------------
181
182 subroutine hirshfeld_charge(this, space, iatom, density, charge)
183 type(hirshfeld_t), intent(in) :: this
184 class(space_t), intent(in) :: space
185 integer, intent(in) :: iatom
186 real(real64), intent(in) :: density(:, :)
187 real(real64), intent(out) :: charge
188
189 integer :: ip
190 real(real64) :: dens_ip
191 real(real64), allocatable :: atom_density(:, :), hirshfeld_density(:)
192
193 push_sub(hirshfeld_charge)
194
195 call profiling_in("HIRSHFELD_CHARGE")
196
197 assert(allocated(this%total_density))
198
199 safe_allocate(atom_density(1:this%mesh%np, 1:this%nspin))
200 safe_allocate(hirshfeld_density(1:this%mesh%np))
201
202 call species_atom_density(this%atom(iatom)%species, this%namespace, space, this%latt, &
203 this%pos(:, iatom), this%mesh, this%nspin, atom_density)
204
205 !$omp parallel do simd private(dens_ip)
206 do ip = 1, this%mesh%np
207 dens_ip = sum(atom_density(ip, 1:this%nspin))
208 if (abs(dens_ip) > tol_hirshfeld) then
209 hirshfeld_density(ip) = sum(density(ip, 1:this%nspin))*dens_ip/this%total_density(ip)
210 else
211 hirshfeld_density(ip) = 0.0_real64
212 end if
213 end do
214
215 charge = dmf_integrate(this%mesh, hirshfeld_density)
216
217 safe_deallocate_a(atom_density)
218 safe_deallocate_a(hirshfeld_density)
219
220 call profiling_out("HIRSHFELD_CHARGE")
221
222 pop_sub(hirshfeld_charge)
223 end subroutine hirshfeld_charge
224
225 ! -----------------------------------------------
226
227 subroutine hirshfeld_volume_ratio(this, iatom, density, volume_ratio)
228 type(hirshfeld_t), intent(in) :: this
229 integer, intent(in) :: iatom
230 real(real64), intent(in) :: density(:, :)
231 real(real64), intent(out) :: volume_ratio
232
233 integer :: ip
234 real(real64), allocatable :: hirshfeld_density(:)
235
236
237 push_sub(hirshfeld_volume_ratio)
238
239 call profiling_in("HIRSHFELD_VOLUME_RATIO")
240
241 assert(allocated(this%total_density))
242
243 safe_allocate(hirshfeld_density(1:this%mesh%np))
244
245 !$omp parallel do
246 do ip = 1, this%mesh%np
247 if (this%total_density(ip) > tol_hirshfeld) then
248 hirshfeld_density(ip) = this%free_vol_r3(ip, iatom)*sum(density(ip, 1:this%nspin))/this%total_density(ip)
249 else
250 hirshfeld_density(ip) = 0.0_real64
251 end if
252 end do
253
254 volume_ratio = dmf_integrate(this%mesh, hirshfeld_density)/this%free_volume(iatom)
255
256 safe_deallocate_a(hirshfeld_density)
257
258 call profiling_out("HIRSHFELD_VOLUME_RATIO")
259
261 end subroutine hirshfeld_volume_ratio
262
263 ! -----------------------------------------------
264
265 subroutine hirshfeld_density_derivative(this, iatom, ddensity)
266 type(hirshfeld_t), intent(in) :: this
267 integer, intent(in) :: iatom
268 real(real64), intent(out) :: ddensity(:)
269
270 integer :: ip
271 real(real64), allocatable :: atom_density(:, :)
272
274
275 call profiling_in("HIRSHFELD_DENSITY_DER")
276
277 safe_allocate(atom_density(1:this%mesh%np, 1:this%nspin))
278
279 !$omp parallel do simd
280 do ip = 1, this%mesh%np
281 if (abs(this%total_density(ip)) > tol_hirshfeld) then
282 ddensity(ip) = this%free_vol_r3(ip, iatom)/(this%total_density(ip)*this%free_volume(iatom))
283 else
284 ddensity(ip) = m_zero
285 end if
286 end do
287
288 safe_deallocate_a(atom_density)
289
290 call profiling_out("HIRSHFELD_DENSITY_DER")
291
293 end subroutine hirshfeld_density_derivative
294
295 ! -----------------------------------------------
296 !dvadrr_ij = \frac{\delta V_i}{\delta \vec{x_j}}
297 subroutine hirshfeld_position_derivative(this, space, iatom, jatom, density, dposition)
298 type(hirshfeld_t), intent(in) :: this
299 class(space_t), intent(in) :: space
300 integer, intent(in) :: iatom
301 integer, intent(in) :: jatom
302 real(real64), intent(in) :: density(:, :)
303 real(real64), contiguous, intent(out) :: dposition(:)
304
305 integer :: ip, idir, icell, jcell, isp
306 real(real64) :: atom_dens, atom_der,rri, rri2, rrj, tdensity, rij, rmax_isqu, rmax_jsqu
307 real(real64) :: pos_i(space%dim), pos_j(space%dim), rmax_i, rmax_j
308 real(real64), allocatable :: grad(:, :), atom_density(:, :), atom_derivative(:, :)
309 type(lattice_iterator_t) :: latt_iter_i, latt_iter_j
310 type(ps_t), pointer :: ps_i, ps_j
311 real(real64) :: tmp, xxi(space%dim), xxj(space%dim)
312
313 real(real64) :: tol_spacing
314
316
317 tol_spacing = maxval(this%mesh%spacing(1:space%dim))
318
319 call profiling_in("HIRSHFELD_POSITION_DER")
320
321 dposition(1:space%dim) = m_zero
323 select type(spec=> this%atom(iatom)%species)
324 type is(pseudopotential_t)
325 ps_i => spec%ps
326 class default
327 assert(.false.)
328 end select
329 select type(spec=> this%atom(jatom)%species)
330 type is(pseudopotential_t)
331 ps_j => spec%ps
332 class default
333 assert(.false.)
334 end select
335
336 rmax_i = m_zero
337 rmax_j = m_zero
338 do isp = 1, this%nspin
339 rmax_i = max(rmax_i, ps_i%density(isp)%x_threshold)
340 rmax_j = max(rmax_j, ps_j%density_der(isp)%x_threshold)
341 end do
342
343 rmax_isqu = rmax_i**2
344 rmax_jsqu = rmax_j**2
345
346 safe_allocate(grad(1:this%mesh%np, 1:space%dim))
347 grad(1:this%mesh%np, 1:space%dim) = m_zero
348 safe_allocate(atom_derivative(1:this%mesh%np, 1:this%nspin))
349 safe_allocate(atom_density(1:this%mesh%np, 1:this%nspin))
350
351 latt_iter_j = lattice_iterator_t(this%latt, rmax_j)
352 do jcell = 1, latt_iter_j%n_cells
353
354 pos_j = this%pos(:, jatom) + latt_iter_j%get(jcell)
355 atom_derivative(1:this%mesh%np, 1:this%nspin) = m_zero
356 call species_atom_density_derivative_np(this%atom(jatom)%species, this%namespace, pos_j, this%mesh, &
357 min(this%nspin, 2), atom_derivative(1:this%mesh%np, 1:this%nspin))
358
359 ! jcells further away from this distance cannot respect the following 'if' condition with respect to the i atom in this icell
360 latt_iter_i = lattice_iterator_t(this%latt, (rmax_j+rmax_i))
361 do icell = 1, latt_iter_i%n_cells
362
363 pos_i = pos_j + latt_iter_i%get(icell) + (this%pos(:, iatom) - this%pos(:, jatom))
364 rij = norm2(pos_i - pos_j)
365
366 if (rij - (rmax_j+rmax_i) < tol_spacing) then
367
368 !We get the non periodized density
369 !We need to do it to have the r^3 correctly computed for periodic systems
370 call species_atom_density_np(this%atom(iatom)%species, this%namespace, pos_i, this%mesh, this%nspin, &
371 atom_density(1:this%mesh%np, 1:this%nspin))
372
373 !$omp parallel do private(xxi, rri, rri2, xxj, rrj, tdensity, atom_dens, atom_der, tmp, idir)
374 do ip = 1, this%mesh%np
375 if (this%total_density(ip)< tol_hirshfeld) cycle
376
377 xxi = this%mesh%x(:, ip) - pos_i
378 rri2 = sum(xxi**2)
379 if (rri2 - rmax_isqu > tol_spacing) cycle ! In this case atom_dens = 0
380
381 xxj = this%mesh%x(:, ip) - pos_j
382 rrj = sum(xxj**2)
383 if (rrj - rmax_jsqu > tol_spacing) cycle ! In this case atom_der = 0
384
385 rri = sqrt(rri2)
386 rrj = sqrt(rrj)
387
388 tdensity = sum(density(ip, 1:this%nspin))
389 atom_dens = sum(atom_density(ip, 1:this%nspin))
390 atom_der = sum(atom_derivative(ip, 1:this%nspin))
391
392 if (rrj > tol_hirshfeld) then
393 tmp = rri*rri2*atom_dens*tdensity/this%total_density(ip)**2
394 do idir = 1, space%dim
395 grad(ip, idir) = grad(ip, idir) - tmp*atom_der*xxj(idir)/rrj
396 end do
397 end if
398
399 !Only if we really have the same atoms
400 if (iatom == jatom .and. rij < tol_hirshfeld) then
401 grad(ip, :) = grad(ip, :) + (m_three*rri*atom_dens + rri2*atom_der)*tdensity/this%total_density(ip)*xxi
402 end if
403
404 end do
405
406 end if
407 end do
408 end do
409
410 dposition = dmf_integrate(this%mesh, space%dim, grad) / this%free_volume(iatom)
411
412 safe_deallocate_a(atom_density)
413 safe_deallocate_a(atom_derivative)
414 safe_deallocate_a(grad)
415
416 call profiling_out("HIRSHFELD_POSITION_DER")
417
419 end subroutine hirshfeld_position_derivative
420
421end module hirshfeld_oct_m
422
423!! Local Variables:
424!! mode: f90
425!! coding: utf-8
426!! End:
double sqrt(double __x) __attribute__((__nothrow__
This module calculates the derivatives (gradients, Laplacians, etc.) of a function.
real(real64), parameter, public m_zero
Definition: global.F90:200
real(real64), parameter, public m_three
Definition: global.F90:203
subroutine, public hirshfeld_init(this, mesh, namespace, space, latt, atom, natoms, pos, nspin)
Definition: hirshfeld.F90:167
subroutine, public hirshfeld_charge(this, space, iatom, density, charge)
Definition: hirshfeld.F90:278
subroutine, public hirshfeld_end(this)
Definition: hirshfeld.F90:259
subroutine, public hirshfeld_density_derivative(this, iatom, ddensity)
Definition: hirshfeld.F90:361
subroutine, public hirshfeld_position_derivative(this, space, iatom, jatom, density, dposition)
Definition: hirshfeld.F90:393
subroutine, public hirshfeld_volume_ratio(this, iatom, density, volume_ratio)
Definition: hirshfeld.F90:323
This module defines various routines, operating on mesh functions.
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
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
Definition: ps.F90:116
subroutine, public species_atom_density_np(species, namespace, pos, mesh, spin_channels, rho)
subroutine, public species_atom_density_derivative_np(species, namespace, pos, mesh, spin_channels, drho)
subroutine, public species_atom_density(species, namespace, space, latt, pos, mesh, spin_channels, rho)
The following class implements a lattice iterator. It allows one to loop over all cells that are with...
A type storing the information and data about a pseudopotential.
Definition: ps.F90:188