mod_neighbors_list.f90 Source File


This file depends on

sourcefile~~mod_neighbors_list.f90~~EfferentGraph sourcefile~mod_neighbors_list.f90 mod_neighbors_list.f90 sourcefile~mod_memory.f90 mod_memory.f90 sourcefile~mod_neighbors_list.f90->sourcefile~mod_memory.f90 sourcefile~mod_profiling.f90 mod_profiling.f90 sourcefile~mod_neighbors_list.f90->sourcefile~mod_profiling.f90 sourcefile~mod_adjacency_mat.f90 mod_adjacency_mat.f90 sourcefile~mod_neighbors_list.f90->sourcefile~mod_adjacency_mat.f90 sourcefile~mod_io.f90 mod_io.f90 sourcefile~mod_neighbors_list.f90->sourcefile~mod_io.f90 sourcefile~mod_constants.f90 mod_constants.f90 sourcefile~mod_neighbors_list.f90->sourcefile~mod_constants.f90 sourcefile~mod_memory.f90->sourcefile~mod_io.f90 sourcefile~mod_memory.f90->sourcefile~mod_constants.f90 sourcefile~mod_profiling.f90->sourcefile~mod_memory.f90 sourcefile~mod_profiling.f90->sourcefile~mod_io.f90 sourcefile~mod_profiling.f90->sourcefile~mod_constants.f90 sourcefile~mod_adjacency_mat.f90->sourcefile~mod_memory.f90 sourcefile~mod_utils.f90 mod_utils.f90 sourcefile~mod_adjacency_mat.f90->sourcefile~mod_utils.f90 sourcefile~mod_io.f90->sourcefile~mod_constants.f90 sourcefile~mod_utils.f90->sourcefile~mod_memory.f90 sourcefile~mod_utils.f90->sourcefile~mod_constants.f90

Files dependent on this one

sourcefile~~mod_neighbors_list.f90~~AfferentGraph sourcefile~mod_neighbors_list.f90 mod_neighbors_list.f90 sourcefile~mod_nonbonded.f90 mod_nonbonded.f90 sourcefile~mod_nonbonded.f90->sourcefile~mod_neighbors_list.f90 sourcefile~mod_mmpol.f90 mod_mmpol.f90 sourcefile~mod_mmpol.f90->sourcefile~mod_nonbonded.f90 sourcefile~mod_link_atom.f90 mod_link_atom.f90 sourcefile~mod_mmpol.f90->sourcefile~mod_link_atom.f90 sourcefile~mod_link_atom.f90->sourcefile~mod_nonbonded.f90 sourcefile~mod_prm.f90 mod_prm.f90 sourcefile~mod_link_atom.f90->sourcefile~mod_prm.f90 sourcefile~mod_interface.f90 mod_interface.f90 sourcefile~mod_interface.f90->sourcefile~mod_nonbonded.f90 sourcefile~mod_interface.f90->sourcefile~mod_mmpol.f90 sourcefile~mod_interface.f90->sourcefile~mod_link_atom.f90 sourcefile~mod_interface.f90->sourcefile~mod_prm.f90 sourcefile~mod_qm_helper.f90 mod_qm_helper.f90 sourcefile~mod_interface.f90->sourcefile~mod_qm_helper.f90 sourcefile~mod_geomgrad.f90 mod_geomgrad.f90 sourcefile~mod_interface.f90->sourcefile~mod_geomgrad.f90 sourcefile~mod_polarization.f90 mod_polarization.f90 sourcefile~mod_interface.f90->sourcefile~mod_polarization.f90 sourcefile~mod_inputloader.f90 mod_inputloader.f90 sourcefile~mod_interface.f90->sourcefile~mod_inputloader.f90 sourcefile~mod_prm.f90->sourcefile~mod_nonbonded.f90 sourcefile~mod_qm_helper.f90->sourcefile~mod_nonbonded.f90 sourcefile~mod_qm_helper.f90->sourcefile~mod_mmpol.f90 sourcefile~mod_qm_helper.f90->sourcefile~mod_link_atom.f90 sourcefile~mod_qm_helper.f90->sourcefile~mod_prm.f90 sourcefile~mod_geomgrad.f90->sourcefile~mod_mmpol.f90 sourcefile~mod_geomgrad.f90->sourcefile~mod_polarization.f90 sourcefile~test_si_geomgrad_num.f90 test_SI_geomgrad_num.f90 sourcefile~test_si_geomgrad_num.f90->sourcefile~mod_mmpol.f90 sourcefile~test_si_geomgrad_num.f90->sourcefile~mod_interface.f90 sourcefile~mod_c_interface.f90 mod_c_interface.f90 sourcefile~mod_c_interface.f90->sourcefile~mod_mmpol.f90 sourcefile~mod_c_interface.f90->sourcefile~mod_interface.f90 sourcefile~mod_c_interface.f90->sourcefile~mod_qm_helper.f90 sourcefile~mod_polarization.f90->sourcefile~mod_mmpol.f90 sourcefile~mod_inputloader.f90->sourcefile~mod_mmpol.f90 sourcefile~mod_inputloader.f90->sourcefile~mod_prm.f90 sourcefile~test_si_init.f90 test_SI_init.f90 sourcefile~test_si_init.f90->sourcefile~mod_interface.f90 sourcefile~test_si_geomgrad.f90 test_SI_geomgrad.f90 sourcefile~test_si_geomgrad.f90->sourcefile~mod_interface.f90 sourcefile~test_si_geomgrad.f90->sourcefile~test_si_geomgrad_num.f90 sourcefile~test_si_potential.f90 test_SI_potential.f90 sourcefile~test_si_potential.f90->sourcefile~mod_interface.f90

Contents


Source Code

#include "f_cart_components.h"
module mod_neighbor_list
    use mod_memory, only: ip, rp, lp, mallocate, mfree
    use mod_adjacency_mat, only: yale_sparse
    use mod_io, only: ommp_message, fatal_error

    implicit none
    private

    type ommp_neigh_list
        integer(ip) :: n
        !! Number of particles in the system
        real(rp) :: cutoff
        !! Cutoff distance for the neighbor list
        integer(ip) :: cellf = 1
        !! Number of subdivision of 'standard' cell. If you create smaller
        !! cells ([[cutoff]]/[[cellf]]) you can sensibly increase the performanc
        !! of the algorithm. See 10.1016/S0010-4655(98)00203-3
        real(rp) :: celld
        !! Dimension of each cell
        integer(ip) :: ncell(3)
        !! Number of cell along each dimension
        integer(ip) :: ncells
        !! Total number of cells
        real(rp) :: offset(3)
        !! Coordinates offset along each dimension (that is the minimum value of
        !! each coordinate.
        integer(ip) :: nneigh
        !! Number of neighbor cells
        integer(ip), allocatable :: neigh_offset(:)
        !! Offsets of neighbor cells
        integer(ip), allocatable :: p2c(:)
        !! Cell of each particle
        type(yale_sparse) :: c2p
        !! Particles contained in each cell, sparse matrix format
    end type ommp_neigh_list

    public :: ommp_neigh_list, nl_init, nl_terminate, nl_update, get_ith_nl

    contains

        subroutine nl_init(nl, c, cutoff, f)
            implicit none
            
            real(rp), intent(in) :: c(:,:)
            !! Coordinates in input
            real(rp), intent(in) :: cutoff
            !! Cut off distance
            integer(ip), intent(in) :: f
            !! Subdivision required for each cell

            type(ommp_neigh_list), intent(inout) :: nl
            !! Neigh list object to initialize

            if(size(c,1) /= 3) then
                call fatal_error("In nl_init, coordinates should be shaped 3xn")
            end if
            nl%n = size(c,2)
            nl%cutoff = cutoff
            nl%cellf = f
            if(nl%cellf > 2 .or. nl%cellf < 0) then
                call fatal_error("Subdivision required is not implemented")
            end if
            nl%nneigh = (nl%cellf*2+1)**3
            call mallocate('nl_init [neigh_offset]', nl%nneigh, nl%neigh_offset)

            nl%celld = nl%cutoff / nl%cellf
            call mallocate('nl_init [p2c]', nl%n, nl%p2c)
            call nl_update(nl, c)
        end subroutine

        subroutine nl_terminate(nl)
            use mod_adjacency_mat, only : matfree
            
            implicit none

            type(ommp_neigh_list), intent(inout) :: nl

            call matfree(nl%c2p)
            call mfree('nl_terminate [p2c]', nl%p2c)
            call mfree('nl_terminate [neigh_offset]', nl%neigh_offset)
        end subroutine

        subroutine nl_update(nl, c)
            use mod_io, only: ommp_message
            use mod_profiling, only: time_push, time_pull
            use mod_adjacency_mat, only: reverse_grp_tab
            use mod_constants, only: OMMP_VERBOSE_LOW
            implicit none
            
            type(ommp_neigh_list), intent(inout) :: nl
            !! Neigh list object to initialize
            real(rp), intent(in) :: c(3,nl%n)
            !! Coordinates in input

            integer(ip) :: i, j, k, l, cc(3), ccmap(3)

            call time_push()
            call ommp_message('Updating neighbor lists', OMMP_VERBOSE_LOW)
            do i=1, 3
                !! TODO this should be improved 
                nl%offset(i) = minval(c(i,:))
                nl%ncell(i) = ceiling((maxval(c(i,:)) - nl%offset(i)) / nl%celld)
            end do
            nl%ncells = product(nl%ncell)
            
            ccmap(_x_) = nl%ncell(_y_) * nl%ncell(_z_)
            ccmap(_y_) = nl%ncell(_z_)
            ccmap(_z_) = 1

            l = 1
            do i=-nl%cellf, nl%cellf
                do j=-nl%cellf, nl%cellf
                    do k=-nl%cellf, nl%cellf
                        nl%neigh_offset(l) = i * ccmap(1) + j * ccmap(2) + k * ccmap(3)
                        l = l + 1
                    end do
                end do
            end do

            ! Each particle is assigned to a cell
            do i=1, nl%n
                do j=1, 3
                    cc(j) = floor((c(j,i)-nl%offset(j)) / nl%celld)
                end do
                nl%p2c(i) = dot_product(cc, ccmap) + 1
            end do
            
            ! Revert assignation to get neighbor list!
            ! The number of cell could be different...
            if(allocated(nl%c2p%ri)) then
              if(size(nl%c2p%ri) /= nl%ncells+1) then
                ! This automatically calls for the reallocation in 
                ! reverse_grp_tab.
                call mfree('nl_update [ri]', nl%c2p%ri)
              end if
            end if

            call reverse_grp_tab(nl%p2c, nl%c2p, nl%ncells)
            call time_pull("Neighbor list update")
        end subroutine

        subroutine get_ith_nl(nl, i, c, neigh, dist, nn)
            !! Once that the neighbor list have been initialized and
            !! updated, this function provide a logical array for atom
            !! [[i]] with all interactions that should be computed and
            !! corresponding distances.
            implicit none

            type(ommp_neigh_list), intent(in) :: nl
            !! Neigh list object
            integer(ip), intent(in) :: i
            !! Index of atom for which the neigbor list is required
            real(rp), intent(in) :: c(3,nl%n)
            !! Coordinates in input
            integer(ip), intent(out) :: neigh(nl%n)
            !! Integer array with neighbors' indexes.
            !! Only the first nn elements are valid
            real(rp), intent(out) :: dist(nl%n)
            !! Array for returning distances.
            !! Only the first nn elements are valid
            integer(ip), intent(out) :: nn
            !! Number of neighbors


            integer(ip) :: icell, j, jid, jp, jjp
            real(rp) :: vdist(3), d2, thr2

            icell = nl%p2c(i)
            thr2 = nl%cutoff * nl%cutoff

            nn = 0
            do j=1, nl%nneigh
                jid = icell + nl%neigh_offset(j)
                if(jid > 0 .and. jid <= nl%ncells) then
                    do jp=nl%c2p%ri(jid), nl%c2p%ri(jid+1)-1
                        jjp = nl%c2p%ci(jp)
                        vdist = c(:,i)-c(:,jjp)
                        d2 = dot_product(vdist, vdist)
                        if(d2 < thr2) then
                            nn = nn + 1
                            dist(nn) = sqrt(d2)
                            neigh(nn) = jjp
                        end if
                    end do
                end if
            end do
        end subroutine
end module