TABLE OF CONTENTS


ABINIT/atomden [ Functions ]

[ Top ] [ Functions ]

NAME

 atomden

FUNCTION

 Construct atomic proto-bulk density (i.e. the superposed density 
 from neutral, isolated atoms at the bulk atomic positions).
 This is useful if one wants to construct the bonding density:
 
 rho^{bnd} = rho^{bulk}(r) 
                 - \sum_{\alpha}\rho^{atm}_{\alpha}(r-R_{\alpha})

 Where rho^{bulk} is the bulk density, rho^{atm} the atomic density
 and the index \alpha sums over all atoms. the R_{\alpha} are the
 atomic positions in the bulk. This routine calculates the sum over
 rho^{atm}_{\alpha}(r-R_{\alpha}) on a grid.

 Units are atomic.

COPYRIGHT

 Copyright (C) 2005-2018 ABINIT group (SM,VR,FJ,MT)
 This file is distributed under the terms of the
 GNU General Public License, see ~ABINIT/COPYING
 or http://www.gnu.org/copyleft/gpl.txt .

INPUTS

 calctype : type of calculation
          'replace' zero the input/output density array
          'add'     add to the input/output density array
 natom : number of atoms in cell
 ntypat : number of different types of atoms in cell
 typat(natom) : type of each atom
 ngrid : number of gridpoints
 r_vec_grid(3,ngrid) : real (non-reduced) coordinates for grid points
 rho(ngrid) : input/output density array
 a(3),b(3),c(3) : real-space basis vectors
 atom_pos(3,natom) : reduced coordinates for atomic positions
 natomgr(ntypat) : number of gridpoints for each atomic density grid
 natomgrmax : max(natomgr(ntypat))
 atomrgrid(natomgrmax,ntypat)
 density(natomgrmax,ntypat)

OUTPUT

 rho(ngrid) : input/output density array

SIDE EFFECTS

NOTES

 There are two ways to compile the proto density in real space
 for a solid. One alternative is that the density is calculated
 for an extended grid encompassing the sphere of points around
 one atom, and the results are folded back into the unit cell.
 On the other hand one can, around each grid point, identify the
 number of atoms in a sphere equivalent to the length of the radial
 grid for each type of atom.
 The second approach, with some modification, is taken here. The
 numer of atoms in a supercell cell are listed such that the supercell
 encompasses the atoms which could contribute to any point in the grid.
 That list is kept and cycled through, to avoid recalculating it at
 each point.
 Note that the density calculated from the atom is the spherical
 average, since there is no preferred direction without any
 external field (and it's simpler)

PARENTS

      read_atomden

CHILDREN

      sort_dp,spline,splint,wrtout,xmpi_barrier,xmpi_sum_master

SOURCE

 75 #if defined HAVE_CONFIG_H
 76 #include "config.h"
 77 #endif
 78 
 79 #include "abi_common.h"
 80 
 81 subroutine atomden(MPI_enreg,natom,ntypat,typat,ngrid,r_vec_grid,rho,a,b,c,atom_pos, &
 82 &                  natomgr,natomgrmax,atomrgrid,density,prtvol,calctype)
 83 
 84  use defs_basis
 85  use defs_abitypes
 86  use m_profiling_abi
 87  use m_xmpi
 88  use m_splines
 89  use m_sort
 90 
 91 !This section has been created automatically by the script Abilint (TD).
 92 !Do not modify the following lines by hand.
 93 #undef ABI_FUNC
 94 #define ABI_FUNC 'atomden'
 95  use interfaces_14_hidewrite
 96 !End of the abilint section
 97 
 98  implicit none
 99 
100 !Arguments ------------------------------------
101 !scalars
102  integer,intent(in) :: natom,ntypat,ngrid,natomgrmax,prtvol
103  character(len=7),intent(in) :: calctype
104 !arrays
105  type(MPI_type),intent(in) :: MPI_enreg
106  integer,intent(in) :: typat(natom),natomgr(ntypat)
107  real(dp),intent(in) :: r_vec_grid(3,ngrid),a(3),b(3),c(3)
108  real(dp),intent(in) :: atom_pos(3,natom),atomrgrid(natomgrmax,ntypat)
109  real(dp),intent(in) :: density(natomgrmax,ntypat)
110  real(dp),intent(inout) :: rho(ngrid)
111 
112 !Local variables-------------------------------
113 !scalars
114  character(len=500) :: message
115  integer :: cnt,delta,i,l,m,n,iatom,itypat,igrid,n_cells,n_grid_p
116  integer :: ierr,spaceComm,nprocs,master,rank,remainder
117  real(dp) :: a_norm,b_norm,c_norm
118  real(dp) :: r_max,R_sphere_max,dp_dummy,ybcbeg,ybcend
119 !arrays
120  integer :: n_equiv_atoms(ntypat),grid_index(ngrid)
121  integer :: my_start_equiv_atoms(ntypat)
122  integer :: my_end_equiv_atoms(ntypat)
123  integer :: l_min(ntypat),m_min(ntypat),n_min(ntypat)
124  integer :: l_max(ntypat),m_max(ntypat),n_max(ntypat)
125  real(dp) :: center(3),dp_vec_dummy(3),delta_a(3),delta_b(3),delta_c(3)
126  real(dp) :: r_atom(3),grid_distances(ngrid)
127  integer, allocatable :: new_index(:),i_1d_dummy(:)
128  real(dp),allocatable :: equiv_atom_dist(:,:),equiv_atom_pos(:,:,:),rho_temp(:,:)
129  real(dp),allocatable :: dp_1d_dummy(:),dp_2d_dummy(:,:),ypp(:)
130  real(dp),allocatable :: x_fit(:),y_fit(:)
131 
132 
133 ! ************************************************************************
134 
135 !initialise and check parallel execution
136  spaceComm=MPI_enreg%comm_cell
137  nprocs=xmpi_comm_size(spaceComm)
138  rank=MPI_enreg%me_kpt
139 
140  master=0
141  
142 !initialise variables and vectors
143  a_norm = sqrt(dot_product(a,a))
144  b_norm = sqrt(dot_product(b,b))
145  c_norm = sqrt(dot_product(c,c))
146  center = (a+b+c)*half
147  dp_dummy = dot_product(a,b)/(b_norm*b_norm)
148  dp_vec_dummy = dp_dummy*b
149  delta_a = a - dp_vec_dummy
150  dp_dummy = dot_product(b,a)/(a_norm*a_norm)
151  dp_vec_dummy = dp_dummy*a
152  delta_b = b - dp_vec_dummy
153  dp_dummy = dot_product(c,(a+b))/(dot_product((a+b),(a+b)))
154  dp_vec_dummy = dp_dummy*(a+b)
155  delta_c = c - dp_vec_dummy
156  ABI_ALLOCATE(rho_temp,(ngrid,ntypat))
157  rho_temp = zero
158 
159 !write(std_out,*) '*** --- In atomden --- ***'
160 !write(std_out,*) ' a_norm:',a_norm,' b_norm:',b_norm,' c_norm:',c_norm
161 !write(std_out,*) 'delta_a:',delta_a,'delta_b:',delta_b,'delta_c:',delta_c
162 !write(std_out,*) ' center:',center
163 
164 !Find supercell which will contain all possible contributions
165 !for all atoms, and enumerate positions for all atoms
166 !TODO list of atoms can be "pruned", i.e identify all atoms
167 !that can't possibly contribute and remove from list.
168 !Should be most important for very oblique cells
169  do itypat=1,ntypat
170    R_sphere_max = atomrgrid(natomgr(itypat),itypat)
171    l_min(itypat) = -ceiling(R_sphere_max/sqrt(dot_product(delta_a,delta_a)))
172    l_max(itypat) = -l_min(itypat)
173    m_min(itypat) = -ceiling(R_sphere_max/sqrt(dot_product(delta_b,delta_b)))
174    m_max(itypat) = -m_min(itypat) 
175    n_min(itypat) = -ceiling(R_sphere_max/sqrt(dot_product(delta_c,delta_c)))
176    n_max(itypat) = -n_min(itypat)
177    n_cells = (l_max(itypat)-l_min(itypat)+1) &
178 &   *(m_max(itypat)-m_min(itypat)+1) &
179 &   *(n_max(itypat)-n_min(itypat)+1)
180    n_equiv_atoms(itypat) = 0
181    do iatom=1,natom
182      if (typat(iatom)==itypat) then
183        n_equiv_atoms(itypat) = n_equiv_atoms(itypat) + n_cells
184      end if ! if type=itypat
185    end do ! number of atoms per cell
186    if ((rank==master).and.(prtvol>9)) then
187      write(message,'(a)') '*** --- In atomden --- find box ***'
188      call wrtout(std_out,message,'COLL')
189      write(message,'(a,I4)') ' itypat:',itypat
190      call wrtout(std_out,message,'COLL')
191      write(message,'(2(a,I4))') ' l_min:',l_min(itypat),' l_max:',l_max(itypat)
192      call wrtout(std_out,message,'COLL')
193      write(message,'(2(a,I4))') ' m_min:',m_min(itypat),' m_max:',m_max(itypat)
194      call wrtout(std_out,message,'COLL')
195      write(message,'(2(a,I4))') ' n_min:',n_min(itypat),' n_max:',n_max(itypat)
196      call wrtout(std_out,message,'COLL')
197      write(message,'(2(a,I4))') ' n_equiv_atoms:',n_equiv_atoms(itypat)
198      call wrtout(std_out,message,'COLL')
199    end if
200  end do !atom type
201 
202 !allocate arrays
203  n = maxval(n_equiv_atoms) 
204  ABI_ALLOCATE(equiv_atom_pos,(3,n,ntypat))
205  ABI_ALLOCATE(equiv_atom_dist,(n,ntypat))
206  equiv_atom_pos = zero
207  equiv_atom_dist = zero
208 
209 !Find positions and distance of atoms from center of cell
210  do itypat=1,ntypat
211    i = 1
212    do l=l_min(itypat),l_max(itypat)
213      do m=m_min(itypat),m_max(itypat)
214        do n=n_min(itypat),n_max(itypat)
215          do iatom=1,natom
216            if (typat(iatom)==itypat) then
217              if (i>n_equiv_atoms(itypat)) then 
218                MSG_ERROR('atomden: i>n_equiv_atoms')
219              end if
220              equiv_atom_pos(:,i,itypat) = (atom_pos(1,iatom)+dble(l))*a &
221 &             + (atom_pos(2,iatom)+dble(m))*b &
222 &             + (atom_pos(3,iatom)+dble(n))*c
223              dp_vec_dummy = equiv_atom_pos(:,i,itypat)-center
224              equiv_atom_dist(i,itypat) = &
225 &             sqrt(dot_product(dp_vec_dummy,dp_vec_dummy))
226              i = i + 1
227            end if
228          end do
229        end do !n
230      end do !m
231    end do !l
232 !  write(std_out,*) '*** --- In atomden --- find equiv ***'
233 !  write(std_out,*) ' itypat:',itypat
234 !  write(std_out,*) ' equiv_atom_pos:'
235 !  write(std_out,*) equiv_atom_pos(:,:,itypat)
236 !  write(std_out,*) ' equiv_atom_dist:',equiv_atom_dist(:,itypat)
237  end do !atom type
238 
239 !Sort the atoms after distance so that the density from the ones
240 !furthest away can be added first. This is to prevent truncation error.
241  do itypat=1,ntypat
242    n = n_equiv_atoms(itypat)
243    ABI_ALLOCATE(dp_1d_dummy,(n))
244    ABI_ALLOCATE(new_index,(n))
245    ABI_ALLOCATE(dp_2d_dummy,(3,n))
246    dp_1d_dummy = equiv_atom_dist(1:n,itypat)
247    dp_2d_dummy = equiv_atom_pos(1:3,1:n,itypat)
248    do i=1,n
249      new_index(i) = i
250    end do
251    call sort_dp(n,dp_1d_dummy,new_index,tol14)
252    do i=1,n
253 !    write(std_out,*) i,' -> ',new_index(i)
254      equiv_atom_pos(1:3,n+1-i,itypat) = dp_2d_dummy(1:3,new_index(i))
255      equiv_atom_dist(1:n,itypat) = dp_1d_dummy 
256    end do
257    ABI_DEALLOCATE(dp_1d_dummy)
258    ABI_DEALLOCATE(new_index)
259    ABI_DEALLOCATE(dp_2d_dummy)
260 !  write(std_out,*) '*** --- In atomden ---  sorting atoms ***'
261 !  write(std_out,*) ' itypat:',itypat
262 !  write(std_out,*) ' equiv_atom_pos:'
263 !  write(std_out,*) equiv_atom_pos(:,:,itypat) 
264 !  write(std_out,*) ' equiv_atom_dist:',equiv_atom_dist(:,itypat) 
265  end do ! atom type
266 
267 !Divide the work in case of parallel execution
268  if (nprocs==1) then ! Make sure everything runs with one proc
269    if (prtvol>9) then
270      write(message,'(a)') '  In atomden - number of processors:     1'
271      call wrtout(std_out,message,'COLL')
272      write(message,'(a)') '  Calculation of proto-atomic density done in serial'
273      call wrtout(std_out,message,'COLL')
274    end if
275    do itypat=1,ntypat
276      if (prtvol>9) then
277        write(message,'(a,I6)') '  Number of equivalent atoms:',n_equiv_atoms(itypat)
278        call wrtout(std_out,message,'COLL')
279      end if
280      my_start_equiv_atoms(itypat) = 1
281      my_end_equiv_atoms(itypat) = n_equiv_atoms(itypat)
282    end do
283  else
284    if (rank==master.and.prtvol>9) then
285      write(message,'(a,I5)') '  In atomden - number of processors:',nprocs
286      call wrtout(std_out,message,'COLL')
287      write(message,'(a)') '  Calculation of proto-atomic density done in parallel'
288      call wrtout(std_out,message,'COLL')
289    end if
290    do itypat=1,ntypat
291      if (rank==master.and.prtvol>9) then
292        write(message,'(a,I6)') '  Number of equivalent atoms:',n_equiv_atoms(itypat)
293        call wrtout(std_out,message,'COLL')
294      end if
295 !    Divide the atoms among the processors by shuffling indices
296      delta = int(floor(real(n_equiv_atoms(itypat))/real(nprocs)))
297      remainder = n_equiv_atoms(itypat)-nprocs*delta
298      my_start_equiv_atoms(itypat) = 1+rank*delta
299      my_end_equiv_atoms(itypat) = (rank+1)*delta
300 !    Divide the remainder points among the processors
301 !    by shuffling indices
302      if ((rank+1)>remainder) then
303        my_start_equiv_atoms(itypat) = my_start_equiv_atoms(itypat) + remainder
304        my_end_equiv_atoms(itypat) = my_end_equiv_atoms(itypat) + remainder
305      else
306        my_start_equiv_atoms(itypat) = my_start_equiv_atoms(itypat) + rank
307        my_end_equiv_atoms(itypat) = my_end_equiv_atoms(itypat) + rank + 1
308      end if
309      if (prtvol>9) then
310        write(message,'(a,I3)') '          For atom type: ',itypat
311        call wrtout(std_out,message,'PERS')
312 !      write(message,'(a,I6)') '  I''ll take atoms from: ',my_start_equiv_atoms(itypat)
313 !      call wrtout(std_out,message,'PERS')
314 !      write(message,'(a,I6)') '           total for me: ',my_end_equiv_atoms(itypat)
315 !      call wrtout(std_out,message,'PERS')
316        write(message,'(a,I6)') '            total for me: ', &
317 &       my_end_equiv_atoms(itypat)+1-my_start_equiv_atoms(itypat)
318        call wrtout(std_out,message,'PERS')
319      end if
320    end do
321  end if
322 
323 !Loop over types of atoms and equivalent atoms and
324 !interpolate density onto grid
325  do itypat=1,ntypat
326 !  do iatom=my_start_equiv_atoms(itypat),my_end_equiv_atoms(itypat)
327    
328    cnt = 0
329    iatom = rank+1 - nprocs
330 !  Parallel execution of loop
331    do
332      cnt = cnt + 1
333      iatom = iatom + nprocs
334      if (iatom>n_equiv_atoms(itypat)) exit ! Exit if index is too large
335 
336      if (mod(cnt,100)==0.and.prtvol>0) then
337        write(message,'(2(a,I6))') ' atoms so far',cnt,' of: ',n_equiv_atoms(itypat)/nprocs
338        call wrtout(std_out,message,'PERS')
339      end if
340      
341      r_max = atomrgrid(natomgr(itypat),itypat) 
342      r_atom = equiv_atom_pos(:,iatom,itypat)
343 
344 !    Set up an array with the gridpoint distances
345      i = 1
346      grid_distances = zero
347      grid_index = 0
348      do igrid=1,ngrid
349        dp_vec_dummy(:) = r_vec_grid(:,igrid) - r_atom(:)
350        dp_dummy = sqrt(dot_product(dp_vec_dummy,dp_vec_dummy))
351        if (dp_dummy <= r_max) then
352          grid_distances(i) = dp_dummy
353          grid_index(i) = igrid      
354          i = i + 1
355        else
356          cycle ! cycle if point is too far away
357        end if
358      end do
359      n_grid_p = i - 1
360 
361      if (n_grid_p==0) cycle ! Cycle if no point needs
362 !    to be interpolated
363      
364 !    Sort points to be interpolated in ascending order
365      ABI_ALLOCATE(dp_1d_dummy,(n_grid_p))
366      ABI_ALLOCATE(new_index,(n_grid_p))
367      ABI_ALLOCATE(i_1d_dummy,(n_grid_p))
368      dp_1d_dummy = grid_distances(1:n_grid_p)
369      do i=1,n_grid_p
370        new_index(i) = i 
371      end do
372      call sort_dp(n_grid_p,dp_1d_dummy,new_index,tol16)
373      grid_distances(1:n_grid_p) = dp_1d_dummy
374      i_1d_dummy = grid_index(1:n_grid_p)
375      do i=1,n_grid_p
376 !      write(std_out,*) i_1d_dummy(i),' -> ',i_1d_dummy(new_index(i))
377        grid_index(i) = i_1d_dummy(new_index(i))
378      end do
379      ABI_DEALLOCATE(dp_1d_dummy)
380      ABI_DEALLOCATE(new_index)
381      ABI_DEALLOCATE(i_1d_dummy)
382 
383 !    Interpolate density onto all grid points
384      ABI_ALLOCATE(ypp,(natomgr(itypat)))
385      ABI_ALLOCATE(x_fit,(n_grid_p))
386      ABI_ALLOCATE(y_fit,(n_grid_p))
387      ypp = zero; y_fit = zero
388      ybcbeg = zero; ybcend = zero
389      x_fit = grid_distances(1:n_grid_p)
390      call spline(atomrgrid(1:natomgr(itypat),itypat), &
391 &     density(1:natomgr(itypat),itypat), &
392 &     natomgr(itypat),ybcbeg,ybcend,ypp)
393      call splint(natomgr(itypat),atomrgrid(1:natomgr(itypat),itypat), &
394 &     density(1:natomgr(itypat),itypat),ypp,n_grid_p, &
395 &     x_fit,y_fit)
396      
397 !    Save the interpolated points to grid
398      do i=1,n_grid_p
399        rho_temp(grid_index(i),itypat) = rho_temp(grid_index(i),itypat) + y_fit(i)
400      end do
401      ABI_DEALLOCATE(ypp)
402      ABI_DEALLOCATE(x_fit)
403      ABI_DEALLOCATE(y_fit)
404 
405    end do ! n equiv atoms
406  end do ! type of atom
407 
408 !Collect all contributions to rho_temp if
409 !we are running in parallel
410  if (nprocs>1) then
411    call xmpi_barrier(spaceComm)
412    call xmpi_sum_master(rho_temp,master,spaceComm,ierr)
413    call xmpi_barrier(spaceComm)
414    if (prtvol>9) then
415      write(message,'(a)') '  In atomden - contributions to rho_temp collected'
416      call wrtout(std_out,message,'PERS')
417    end if
418  end if
419 
420 !Now rho_temp contains the atomic protodensity for each atom.
421 !Check whether this is to replace or be added to the input/output array
422 !and sum up contributions
423  if (trim(calctype)=='replace') rho = zero
424  do itypat=1,ntypat
425    rho(:) = rho(:) + rho_temp(:,itypat)
426  end do
427 
428 !deallocations
429  if (allocated(rho_temp))  then
430    ABI_DEALLOCATE(rho_temp)
431  end if
432  if (allocated(equiv_atom_pos))  then
433    ABI_DEALLOCATE(equiv_atom_pos)
434  end if
435  if (allocated(equiv_atom_dist))  then
436    ABI_DEALLOCATE(equiv_atom_dist)
437  end if
438 !if (allocated()) deallocate()
439 
440  return
441 
442  end subroutine atomden