Last commit for src/ext/lclist.f90: 45cae2ab930729ac4821fd8cd5acd3f17d1c1afe

Fix another instance of the optional argument bug that wasn't caught before.

Bruno Mundim [2017-01-23 17:03:41]
Fix another instance of the optional argument bug that wasn't caught before.
!----------------------------------------------------------------------
!      lclist.f90 - implementation of a linked cell neighbor list
!----------------------------------------------------------------------
!+ This file is part of the AENET package.
!+
!+ Copyright (C) 2012-2016 Nongnuch Artrith and Alexander Urban
!+
!+ This program is free software: you can redistribute it and/or modify
!+ it under the terms of the GNU General Public License as published by
!+ the Free Software Foundation, either version 3 of the License, or
!+ (at your option) any later version.
!+
!+ This program is distributed in the hope that it will be useful, but
!+ WITHOUT ANY WARRANTY; without even the implied warranty of
!+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
!+ General Public License for more details.
!+
!+ You should have received a copy of the GNU General Public License
!+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
!----------------------------------------------------------------------

module lclist

  !--------------------------------------------------------------------!
  ! A simple, but universal neighbourlist implementation without any   !
  ! restrictions on the lattice vectors or on the number of atoms in   !
  ! the system.  It is equally well suited for small unit cells, where !
  ! |lattice vectors| << R_cut, and for unit cells that are much       !
  ! larger than the interaction radius.                                !
  !                                                                    !
  ! If the simulation cell is large wrt. the interaction cut-off, it   !
  ! is partitioned into cells and the information about the atoms in   !
  ! each cell is stored in a linked cell list.                         !
  !--------------------------------------------------------------------!
  ! 2011-11-05 Alexander Urban (AU)                                    !
  !--------------------------------------------------------------------!

  use sortlib, only: argsort

  implicit none
  save

  public  :: lcl_init,               &
             lcl_final,              &
             lcl_print_info,         &
             lcl_nmax_cell,          &
             lcl_nmax_nblist,        &
             lcl_nmax_nbdist,        &
             lcl_nblist,             &
             lcl_nbdist,             &
             lcl_nbdist_cart

  private :: cell_assign_atoms,      &
             cell_multiples,         &
             cell_get,               &
             cell_add,               &
             wrap_cooLatt,           &
             cell_volume,            &
             inner_sphere,           &
             vproduct,               &
             translation_vectors


  double precision, parameter, private :: PI = 3.1415926535897931d0

  !--------------------------------------------------------------------!

  logical,                                         private :: pbc
  double precision, dimension(3,3),                private :: latticeVec
  double precision, dimension(3,3),                private :: gridVec
  integer,                                         private :: nAtoms
  integer,          dimension(:),   pointer,       private :: atomType
  double precision, dimension(:,:), pointer,       private :: cooLatt

  double precision,                                private :: r_min
  double precision,                                private :: r_max
  integer,          dimension(3),                  private :: nCells
  integer,          dimension(:,:,:), allocatable, private :: cell
  integer,          dimension(:),     allocatable, private :: atomList
  integer,          dimension(:,:),   allocatable, private :: cellList

  integer,                                         private :: nCvecs
  integer,          dimension(:,:),   allocatable, private :: Cvec

  integer,                                         private :: nTvecs
  integer,          dimension(:,:),   allocatable, private :: Tvec
  double precision, dimension(:),     allocatable, private :: TvecLen

  integer,                                         private :: nmax_cell
  integer,                                         private :: nmax_nblist
  integer,                                         private :: nmax_nbdist

  logical,                                         private :: isInit = .false.

contains

  subroutine lcl_init(r_min_in, r_max_in, latticeVec_in, &
                      nAtoms_in, atomType_in, cooLatt_in, pbc_in)

    implicit none

    double precision,                                 intent(in) :: r_min_in
    double precision,                                 intent(in) :: r_max_in
    double precision, dimension(3,3),                 intent(in) :: latticeVec_in
    integer,                                          intent(in) :: nAtoms_in
    integer,          dimension(nAtoms_in),   target, intent(in) :: atomType_in
    double precision, dimension(3,nAtoms_in), target, intent(in) :: cooLatt_in
    logical, optional,                                intent(in) :: pbc_in

    if (isInit) then
       write(0,*) "Error: module already initialized in `lclist_init'."
       return
    end if

    r_min           = r_min_in
    r_max           = r_max_in
    nAtoms          = nAtoms_in
    latticeVec(:,:) = latticeVec_in(:,:)
    atomType        => atomType_in(:)
    cooLatt         => cooLatt_in(:,:)
    if (present(pbc_in)) then
       pbc = pbc_in
    else
       pbc = .true.
    end if

    ! get optimal divison of the unit cell:
    call cell_multiples(r_max, latticeVec, gridVec, nCells)
    allocate(cell(nCells(3),nCells(2),nCells(1)), atomList(nAtoms), &
             cellList(3,nAtoms))

    ! assign atoms to cells:
    call cell_assign_atoms(nCells, nAtoms, cooLatt, cell, atomList, cellList)

    ! set up half start of vectors pointing to cells within range of r_max:
    nCvecs = 0
    call translation_vectors(r_max, gridVec, nCvecs, Cvec, nc=nCells)
    allocate(Cvec(3,nCvecs))
    call translation_vectors(r_max, gridVec, nCVecs, cVec, nc=nCells)

    ! set up half start of translation vectors pointing to periodic
    ! images of the unit cell within range of r_max:
    nTvecs = 0
    if (pbc) then
       call translation_vectors(r_max, latticeVec, nTvecs, Tvec, Tnorm=TvecLen)
       allocate(Tvec(3,nTvecs), TvecLen(nTvecs))
       call translation_vectors(r_max, latticeVec, nTvecs, Tvec, Tnorm=TvecLen)
    end if

    isInit = .true.

    ! max. number of neighbours and max. number of atoms per cell:
    nmax_cell   = lcl_nmax_cell()
    nmax_nblist = lcl_nmax_nblist()
    nmax_nbdist = lcl_nmax_nbdist(r_min, r_max)

  end subroutine lcl_init

  !--------------------------------------------------------------------!

  subroutine lcl_final()

    implicit none

    if (.not. isInit) return

    if (allocated(cell)) deallocate(cell, atomList, cellList)
    if (allocated(Cvec)) deallocate(Cvec)
    if (allocated(Tvec)) deallocate(Tvec, TvecLen)

    cooLatt  => null()
    atomType => null()

    isInit = .false.

  end subroutine lcl_final

  !--------------------------------------------------------------------!
  !       print information about the linked cell list to stdout       !
  !--------------------------------------------------------------------!

  subroutine lcl_print_info()

    implicit none

    write(*,*) 'Linked Cell List (Neighbourlist)'
    write(*,*) '--------------------------------'
    write(*,*)

    if (.not. isInit) then
       write(*,*) "Module not initialized."
       write(*,*)
       return
    end if

    write(*,'(1x,"Number of atoms             : ",I10)')      nAtoms
    write(*,'(1x,"Minimal distance            : ",ES10.3)')   r_min
    write(*,'(1x,"Cut-off radius              : ",ES10.3)')   r_max
    write(*,'(1x,"Number of cells             : ",3(I4,2x))') nCells
    write(*,'(1x,"Cells within cut-off        : ",I10)')      2*nCvecs + 1
    write(*,'(1x,"Translation vectors         : ",I10)')      2*nTvecs + 1
    write(*,'(1x,"Max. atoms per cell         : ",I10)')      nmax_cell
    write(*,'(1x,"Max. possible neighbour IDs : ",I10)')      nmax_nblist
    write(*,'(1x,"Max. real neighbours        : ",I10)')      nmax_nbdist
    write(*,*)

  end subroutine lcl_print_info

  !--------------------------------------------------------------------!
  !        max. number of neighbour candidates within cut-off          !
  !--------------------------------------------------------------------!

  function lcl_nmax_nblist() result(nmax)

    implicit none

    integer          :: nmax

    nmax = min(lcl_nmax_cell()*(2*nCvecs + 1), nAtoms-1)

  end function lcl_nmax_nblist

  !--------------------------------------------------------------------!
  !          max. number of (real) neighbours within cut-off           !
  !--------------------------------------------------------------------!

  function lcl_nmax_nbdist(rmin, rmax) result(nmax)

    implicit none

    double precision, intent(in) :: rmin, rmax
    integer                      :: nmax

    double precision :: V_atom, V_cut

    V_atom = (0.5d0*rmin)**3
    V_cut  = (rmax+0.5d0*rmin)**3

    ! max number of atoms assuming close packing
    ! pi/(s*sqrt(2)) ~ 0.7405
    nmax = ceiling(V_cut/V_atom*0.7405d0)

  end function lcl_nmax_nbdist

  !--------------------------------------------------------------------!
  !           estimated max. average number of atoms per cell          !
  !--------------------------------------------------------------------!

  function lcl_nmax_cell() result(nmax)

    implicit none

    integer          :: nmax
    double precision :: V_cell, V_atom

    if (.not. isInit) then
       write(0,*) "Error: module not initialized in `max_atoms_per_cell'."
       stop
    end if

    V_cell = cell_volume(gridVec)
    ! effective atom volume in a densly packed crystal structure:
    ! 3*sqrt(2)/pi * 4/3*pi * r^3 = 4*sqrt(2) * r^3
    V_atom = sqrt(32.0d0)*(0.5d0*r_min)**3
    ! wrong?! assuming the also partial spheres in the cell.
    ! V_atom = 8.0d0/27.0d0*(0.5d0*r_min)**3
    nmax   = ceiling(V_cell/V_atom)

  end function lcl_nmax_cell

  !--------------------------------------------------------------------!
  !                      retrieve neighbour list                       !
  !                                                                    !
  ! This routine returns the complete list of atom numbers of possible !
  ! neighbours.  It is not checked, if the returned atoms really are   !
  ! within the cut-off.                                                !
  !--------------------------------------------------------------------!

  subroutine lcl_nblist(iatom, nnb, nblist, stat)

    implicit none

    integer,                 intent(in)    :: iatom
    integer,                 intent(inout) :: nnb
    integer, dimension(nnb), intent(out)   :: nblist
    integer,       optional, intent(out)   :: stat

    integer, dimension(3) :: ic0, ic
    integer               :: inb, iat
    integer               :: iv, sgn

    if (.not. isInit) then
       write(0,*) "Error: module not initialized in `nblist'."
       stop
    end if

    if ((iatom < 1) .or. (iatom > nAtoms)) then
       write(0,*) "Error: invalid atom number in `nblist' : ", iatom
       stop
    end if

    if ((nnb < nmax_nblist) .and. (.not. present(stat))) then
       write(0,*) "Error: array nblist too small in `nblist'."
       stop
    end if

    if (present(stat)) stat = 0
    if (nnb == 0) return

    ! iatom is in cell ic0:
    ic0(1:3) = cellList(1:3,iatom)

    ! (1) other atoms in the same cell:

    inb = 0
    iat = cell(ic0(3),ic0(2),ic0(1))
    if (iat /= 0) then
       if (iat /= iatom) then
          inb = inb + 1
          if (inb > nnb) then
             if (present(stat)) stat = 1
             return
          end if
          nblist(inb) = iat
       end if
       do while(atomList(iat) /= 0)
          iat = atomList(iat)
          if (iat == iatom) cycle
          inb = inb + 1
          if (inb > nnb) then
             if (present(stat)) stat = 2
             return
          end if
          nblist(inb) = iat
       end do
    end if

    ! (2) atoms in neighbouring cells:

    do iv = 1, nCvecs
    do sgn = 1, -1, -2

       if (pbc) then
          ! cell coordinates obeying PBC
          ic(1) = mod(ic0(1) + sgn*Cvec(1,iv) + nCells(1) - 1, nCells(1)) + 1
          ic(2) = mod(ic0(2) + sgn*Cvec(2,iv) + nCells(2) - 1, nCells(2)) + 1
          ic(3) = mod(ic0(3) + sgn*Cvec(3,iv) + nCells(3) - 1, nCells(3)) + 1
       else
          ! isolated structures
          ic(1) = ic0(1) + sgn*Cvec(1,iv)
          ic(2) = ic0(2) + sgn*Cvec(2,iv)
          ic(3) = ic0(3) + sgn*Cvec(3,iv)
          if (any(ic > nCells) .or. any(ic < 1)) cycle
       end if

       ! all atoms in that cell:
       iat = cell(ic(3),ic(2),ic(1))
       if (iat /= 0) then
          if (.not. any(nblist(1:inb) == iat)) then
             inb = inb + 1
             if (inb > nnb) then
                if (present(stat)) stat = 3
                return
             end if
             nblist(inb) = iat
          end if
          do while(atomList(iat) /= 0)
             iat = atomList(iat)
             if (.not. any(nblist(1:inb) == iat)) then
                inb = inb + 1
                if (inb > nnb) then
                   if (present(stat)) stat = 4
                   return
                end if
                nblist(inb) = iat
             end if
          end do
       end if

    end do
    end do

    nnb = inb

  end subroutine lcl_nblist

  !--------------------------------------------------------------------!
  !      retrieve real PBC coordinates of the neighbouring atoms       !
  !--------------------------------------------------------------------!

  subroutine lcl_nbdist(iatom, nnb, nbcoo, nbdist, r_cut, itype, stat)

    implicit none

    integer,                            intent(in)    :: iatom
    integer,                            intent(inout) :: nnb
    double precision, dimension(3,nnb), intent(out)   :: nbcoo
    double precision, dimension(nnb),   intent(out)   :: nbdist
    double precision, optional,         intent(in)    :: r_cut
    integer,          optional,         intent(in)    :: itype
    integer,          optional,         intent(out)   :: stat

    integer,          dimension(nmax_nblist)          :: nblist
    integer                                           :: nnb_tot, inb, nnb2
    integer                                           :: iat, iT, sgn
    double precision                                  :: Rc, Rc2, dist2
    double precision, dimension(3)                    :: coo2, cart

    if (.not. isInit) then
       write(0,*) "Error: module not initialized in `nbdist'."
       stop
    end if

    if ((iatom < 1) .or. (iatom > nAtoms)) then
       write(0,*) "Error: invalid atom number in `nbdist' : ", iatom
       stop
    end if

    if ((nnb < nmax_nbdist) .and. (.not. present(stat))) then
       write(0,*) "Error: array nbcoo too small in `nbdist'. (1)"
       stop
    end if

    if (present(stat)) stat = 0
    if (present(r_cut)) then
       Rc = r_cut
    else
       Rc = r_max
    end if
    Rc2 = Rc*Rc
    nnb_tot = 0

    ! (1) get neighbour list:

    nnb2 = nmax_nblist
    call lcl_nblist(iatom, nnb2, nblist)

    ! (2) check distance do the periodic images of the central atom:

    nnb_tot = 0
    if ( (.not. present(itype)) .or. (atomType(iatom) == itype)) then
       do iT = 1, nTvecs
          if (TvecLen(iT) > Rc) exit
          nnb_tot = nnb_tot + 2
          if (nnb_tot > nnb) then
             if (present(stat)) then
                stat = 1
                return
             else
                write(0,*) "Error: array nbcoo too small in `nbdist'. (2)"
                stop
             end if
          end if ! nbcoo too small
          nbcoo(1,nnb_tot-1) = cooLatt(1,iatom) + dble(Tvec(1,iT))
          nbcoo(2,nnb_tot-1) = cooLatt(2,iatom) + dble(Tvec(2,iT))
          nbcoo(3,nnb_tot-1) = cooLatt(3,iatom) + dble(Tvec(3,iT))
          nbdist(nnb_tot-1)  = TvecLen(iT)
          nbcoo(1,nnb_tot)   = cooLatt(1,iatom) - dble(Tvec(1,iT))
          nbcoo(2,nnb_tot)   = cooLatt(2,iatom) - dble(Tvec(2,iT))
          nbcoo(3,nnb_tot)   = cooLatt(3,iatom) - dble(Tvec(3,iT))
          nbdist(nnb_tot)    = TvecLen(iT)
       end do
    end if ! correct atom type

    ! (3) calculate PBC distances for all atoms in the neighbourlist:

    do inb = 1, nnb2
       iat = nblist(inb)
       if ( present(itype) .and. (atomType(iat) /= itype)) cycle
       ! in home unit cell:
       cart(1:3) = cooLatt(1:3,iat) - cooLatt(1:3,iatom)
       cart(1:3) = matmul(latticeVec, cart(1:3))
       dist2 = sum(cart*cart)
       if (dist2 <= Rc2) then
          nnb_tot = nnb_tot + 1
          if (nnb_tot > nnb) then
             if (present(stat)) then
                stat = 2
                return
             else
                write(0,*) "Error: array nbcoo too small in `nbdist'. (3)"
                stop
             end if
          end if ! nbcoo too small
          nbcoo(1:3,nnb_tot) = cooLatt(1:3,iat)
          nbdist(nnb_tot)    = sqrt(dist2)
       end if ! within cut-off
       ! in periodic images:
       do iT = 1, nTvecs
       do sgn = 1, -1, -2
          coo2(1) = cooLatt(1,iat) + dble(sgn*Tvec(1,iT))
          coo2(2) = cooLatt(2,iat) + dble(sgn*Tvec(2,iT))
          coo2(3) = cooLatt(3,iat) + dble(sgn*Tvec(3,iT))
          cart(1:3) = coo2(1:3) - cooLatt(1:3,iatom)
          cart(1:3) = matmul(latticeVec, cart(1:3))
          dist2 = sum(cart*cart)
          if (dist2 <= Rc2) then
             nnb_tot = nnb_tot + 1
             if (nnb_tot > nnb) then
                if (present(stat)) then
                   stat = 3
                   return
                else
                   write(0,*) "Error: array nbcoo too small in `ndist'. (4)"
                   stop
                end if
             end if ! nbcoo too small
             nbcoo(1:3,nnb_tot) = coo2(1:3)
             nbdist(nnb_tot)    = sqrt(dist2)
          end if ! within cut-off
       end do
       end do
    end do

    ! (4) return number of neighbours found:

    nnb = nnb_tot

  end subroutine lcl_nbdist

  !--------------------------------------------------------------------!
  !    same as `lcl_nbdist', but cartesian coordinates are returned    !
  !--------------------------------------------------------------------!

  subroutine lcl_nbdist_cart(iatom, nnb, nbcoo, nbdist, r_cut, itype, &
                             nblist, nbtype, stat)

    implicit none

    integer,                                    intent(in)    :: iatom
    integer,                                    intent(inout) :: nnb
    double precision, dimension(3,nnb),         intent(out)   :: nbcoo
    double precision, dimension(nnb),           intent(out)   :: nbdist
    double precision,                 optional, intent(in)    :: r_cut
    integer,                          optional, intent(in)    :: itype
    integer,          dimension(nnb), optional, intent(out)   :: nblist
    integer,          dimension(nnb), optional, intent(out)   :: nbtype
    integer,                          optional, intent(out)   :: stat

    integer,          dimension(nmax_nblist)          :: nblist_loc
    integer                                           :: nnb_tot, inb, nnb2
    integer                                           :: iat, iT, sgn
    double precision                                  :: Rc, Rc2, dist2
    double precision, dimension(3)                    :: coo1, coo2, cart

    if (.not. isInit) then
       write(0,*) "Error: module not initialized in `nbdist'."
       stop
    end if

    if ((iatom < 1) .or. (iatom > nAtoms)) then
       write(0,*) "Error: invalid atom number in `nbdist' : ", iatom
       stop
    end if

    if ((nnb < nmax_nbdist) .and. (.not. present(stat))) then
       write(0,*) "Error: array nbcoo too small in `nbdist_cart'. (1)"
       stop
    end if

    if (present(stat)) stat = 0
    if (present(r_cut)) then
       Rc = r_cut
    else
       Rc = r_max
    end if
    Rc2 = Rc*Rc
    nnb_tot = 0

    ! (1) get neighbour list:

    nnb2 = nmax_nblist
    call lcl_nblist(iatom, nnb2, nblist_loc)

    ! (2) check distance do the periodic images of the central atom:

    nnb_tot = 0
    if ( (.not. present(itype)) .or. (atomType(iatom) == itype)) then
       do iT = 1, nTvecs
          if (TvecLen(iT) > Rc) exit
          nnb_tot = nnb_tot + 2
          if (nnb_tot > nnb) then
             if (present(stat)) then
                stat = 1
                return
             else
                write(0,*) "Error: array nbcoo too small in `nbdist_cart'. (2)"
                stop
             end if
          end if ! nbcoo too small
          if (present(nblist)) then
             nblist(nnb_tot-1) = iatom
             nblist(nnb_tot)   = iatom
          end if
          if (present(nbtype)) then
             nbtype(nnb_tot-1) = atomType(iatom)
             nbtype(nnb_tot)   = atomType(iatom)
          end if
          nbcoo(1,nnb_tot-1) = cooLatt(1,iatom) + dble(Tvec(1,iT))
          nbcoo(2,nnb_tot-1) = cooLatt(2,iatom) + dble(Tvec(2,iT))
          nbcoo(3,nnb_tot-1) = cooLatt(3,iatom) + dble(Tvec(3,iT))
          nbcoo(:,nnb_tot-1) = matmul(latticeVec, nbcoo(:,nnb_tot-1))
          nbdist(nnb_tot-1)  = TvecLen(iT)
          nbcoo(1,nnb_tot)   = cooLatt(1,iatom) - dble(Tvec(1,iT))
          nbcoo(2,nnb_tot)   = cooLatt(2,iatom) - dble(Tvec(2,iT))
          nbcoo(3,nnb_tot)   = cooLatt(3,iatom) - dble(Tvec(3,iT))
          nbcoo(:,nnb_tot)   = matmul(latticeVec, nbcoo(:,nnb_tot))
          nbdist(nnb_tot)    = TvecLen(iT)
       end do
    end if ! correct atom type

    ! (3) calculate PBC distances for all atoms in the neighbourlist:

    ! cartesian coordinates of central atom:
    coo1(1:3) = matmul(latticeVec, cooLatt(1:3,iatom))

    do inb = 1, nnb2
       iat = nblist_loc(inb)
       if ( present(itype) .and. (atomType(iat) /= itype)) cycle
       ! in home unit cell:
       coo2(1:3) = matmul(latticeVec, cooLatt(1:3,iat))
       cart(1:3) = coo2(1:3) - coo1(1:3)
       dist2 = sum(cart*cart)
       if (dist2 <= Rc2) then
          nnb_tot = nnb_tot + 1
          if (nnb_tot > nnb) then
             if (present(stat)) then
                stat = 2
                return
             else
                write(0,*) "Error: array nbcoo too small in `nbdist_cart'. (3)"
                stop
             end if
          end if ! nbcoo too small
          if(present(nblist)) nblist(nnb_tot) = iat
          if(present(nbtype)) nbtype(nnb_tot) = atomType(iat)
          nbcoo(1:3,nnb_tot) = coo2(1:3)
          nbdist(nnb_tot)    = sqrt(dist2)
       end if ! within cut-off
       ! in periodic images:
       do iT = 1, nTvecs
       do sgn = 1, -1, -2
          coo2(1) = cooLatt(1,iat) + dble(sgn*Tvec(1,iT))
          coo2(2) = cooLatt(2,iat) + dble(sgn*Tvec(2,iT))
          coo2(3) = cooLatt(3,iat) + dble(sgn*Tvec(3,iT))
          coo2(:) = matmul(latticeVec, coo2(1:3))
          cart(:) = coo2(1:3) - coo1(1:3)
          dist2 = sum(cart*cart)
          if (dist2 <= Rc2) then
             nnb_tot = nnb_tot + 1
             if (nnb_tot > nnb) then
                if (present(stat)) then
                   stat = 3
                   return
                else
                   write(0,*) "Error: array nbcoo too small in `nbdist_cart'. (4)"
                   stop
                end if
             end if ! nbcoo too small
             if(present(nblist)) nblist(nnb_tot) = iat
             if(present(nbtype)) nbtype(nnb_tot) = atomType(iat)
             nbcoo(1:3,nnb_tot) = coo2(1:3)
             nbdist(nnb_tot)    = sqrt(dist2)
          end if ! within cut-off
       end do
       end do
    end do

    ! (4) return number of neighbours found:

    nnb = nnb_tot

  end subroutine lcl_nbdist_cart


  !====================================================================!
  !                                                                    !
  !                         private procedures                         !
  !                                                                    !
  !====================================================================!



  !--------------------------------------------------------------------!
  !                       assign atoms to cells                        !
  !--------------------------------------------------------------------!

  subroutine cell_assign_atoms(nc, nAtoms, cooLatt, cell, atomList, &
                               cellList)

    implicit none

    integer,          dimension(3),                 intent(in)    :: nc
    integer,                                        intent(in)    :: nAtoms
    double precision, dimension(3,nAtoms),          intent(inout) :: cooLatt
    integer,          dimension(nc(3),nc(2),nc(1)), intent(out)   :: cell
    integer,          dimension(nAtoms),            intent(out)   :: atomList
    integer,          dimension(3,nAtoms),          intent(out)   :: cellList

    integer               :: iatom
    integer, dimension(3) :: ic

    cell(:,:,:)   = 0
    atomList(:)   = 0
    cellList(:,:) = 0

    do iatom = 1, nAtoms
       ! coordinates of isolated structures will be checked,
       ! but will not be wrapped
       call wrap_cooLatt(cooLatt(1:3,iatom))
       ic(1:3) = cell_get(cooLatt(1:3,iatom), nc)
       call cell_add(iatom, ic, nAtoms, nc, cell, atomList, cellList)
    end do

  end subroutine cell_assign_atoms

  !--------------------------------------------------------------------!
  !                determine the optimal cell division                 !
  !--------------------------------------------------------------------!

  subroutine cell_multiples(r_max, latticeVec, gridVec, nCells)

    implicit none

    double precision,                 intent(in)  :: r_max
    double precision, dimension(3,3), intent(in)  :: latticeVec
    double precision, dimension(3,3), intent(out) :: gridVec
    integer,          dimension(3),   intent(out) :: nCells

    double precision, dimension(3)   :: v
    double precision, dimension(3)   :: h
    double precision                 :: h_min
    double precision                 :: r

    ! compute the three heights:

    v  = vproduct(latticeVec(:,1),latticeVec(:,2))
    v  = v/sqrt(sum(v*v))
    h(3) = abs(sum(v*latticeVec(:,3)))

    v  = vproduct(latticeVec(:,1),latticeVec(:,3))
    v  = v/sqrt(sum(v*v))
    h(2) = abs(sum(v*latticeVec(:,2)))

    v  = vproduct(latticeVec(:,2),latticeVec(:,3))
    v  = v/sqrt(sum(v*v))
    h(1) = abs(sum(v*latticeVec(:,1)))

    ! shortest height:

    h_min = minval(h)

    ! compute multiples of h_min wrt. each height:

    nCells(1) = floor(h(1)/h_min)
    nCells(2) = floor(h(2)/h_min)
    nCells(3) = floor(h(3)/h_min)

    ! compute largest sphere that fits into such a cell:

    gridVec(:,1) = latticeVec(:,1)/dble(nCells(1))
    gridVec(:,2) = latticeVec(:,2)/dble(nCells(2))
    gridVec(:,3) = latticeVec(:,3)/dble(nCells(3))
    call inner_sphere(gridVec, r)

    ! reduce cell size if that sphere is unnecessary large:

    if (r > 0.5d0*r_max) then
       nCells(1) = floor(nCells(1)*(2.0d0*r/r_max))
       nCells(2) = floor(nCells(2)*(2.0d0*r/r_max))
       nCells(3) = floor(nCells(3)*(2.0d0*r/r_max))
       gridVec(:,1) = latticeVec(:,1)/dble(nCells(1))
       gridVec(:,2) = latticeVec(:,2)/dble(nCells(2))
       gridVec(:,3) = latticeVec(:,3)/dble(nCells(3))
    end if

  end subroutine cell_multiples


  !====================================================================!
  !                                                                    !
  !                 operations on the linked cell list                 !
  !                                                                    !
  !====================================================================!

  !--------------------------------------------------------------------!
  !             get cell for specific lattice coordinates              !
  !--------------------------------------------------------------------!

  function cell_get(coo, nc) result(ic)

    implicit none

    double precision, dimension(3), intent(in) :: coo
    integer,          dimension(3), intent(in) :: nc
    integer,          dimension(3)             :: ic

    ic(1) = nint(coo(1)*dble(nc(1)) + 0.5d0)
    ic(2) = nint(coo(2)*dble(nc(2)) + 0.5d0)
    ic(3) = nint(coo(3)*dble(nc(3)) + 0.5d0)

  end function cell_get

  !--------------------------------------------------------------------!
  !                          add atom to cell                          !
  !--------------------------------------------------------------------!

  subroutine cell_add(iatom, ic, nAtoms, nc, cell, atomList, cellList)

    implicit none

    integer,                               intent(in)    :: iatom
    integer, dimension(3),                 intent(in)    :: ic
    integer,                               intent(in)    :: nAtoms
    integer, dimension(3),                 intent(in)    :: nc
    integer, dimension(nc(3),nc(2),nc(1)), intent(inout) :: cell
    integer, dimension(nAtoms),            intent(inout) :: atomList
    integer, dimension(3,nAtoms),          intent(inout) :: cellList

    atomList(iatom) = cell(ic(3),ic(2),ic(1))
    cell(ic(3),ic(2),ic(1)) = iatom

    cellList(1:3,iatom) = ic(1:3)

  end subroutine cell_add

  !--------------------------------------------------------------------!
  !                       remove atom from cell                        !
  !--------------------------------------------------------------------!

!!$  subroutine cell_del(iatom, ic, nAtoms, nc, cell, atomList)
!!$
!!$    implicit none
!!$
!!$    integer,                               intent(in)    :: iatom
!!$    integer, dimension(3),                 intent(in)    :: ic
!!$    integer,                               intent(in)    :: nAtoms
!!$    integer, dimension(3),                 intent(in)    :: nc
!!$    integer, dimension(nc(3),nc(2),nc(1)), intent(inout) :: cell
!!$    integer, dimension(nAtoms),            intent(inout) :: atomList
!!$
!!$    integer :: i
!!$
!!$    if (cell(ic(3),ic(2),ic(1)) == iatom) then
!!$       cell(ic(3),ic(2),ic(1)) = atomList(iatom)
!!$    else
!!$       i = cell(ic(3),ic(2),ic(1))
!!$       do while(atomList(i) /= iatom)
!!$          if (i == 0) then
!!$             write(0,*) "Warning: atom not in cell in `cell_del'."
!!$             return
!!$          end if
!!$          i = atomList(i)
!!$       end do
!!$       atomList(i) = atomlist(iatom)
!!$    end if
!!$
!!$  end subroutine cell_del



  !====================================================================!
  !                                                                    !
  !                        auxilliary functions                        !
  !                                                                    !
  !====================================================================!



  !--------------------------------------------------------------------!
  !          wrap lattice coordinates to the interval [0,1[
  !--------------------------------------------------------------------!

  subroutine wrap_cooLatt(coo)

    implicit none

    double precision, dimension(3), intent(inout) :: coo

    if (.not. pbc) then
       if (any(coo < 0.0d0) .or. any(coo > 1.0d0)) then
          write(0,*) "Warning: atoms outside of bounding box in LC list!"
       end if
       return
    end if

    do while(coo(1) < 0.0d0)
       coo(1) = coo(1) + 1.0d0
    end do
    do while(coo(1) >= 1.0d0)
       coo(1) = coo(1) - 1.0d0
    end do

    do while(coo(2) < 0.0d0)
       coo(2) = coo(2) + 1.0d0
    end do
    do while(coo(2) >= 1.0d0)
       coo(2) = coo(2) - 1.0d0
    end do

    do while(coo(3) < 0.0d0)
       coo(3) = coo(3) + 1.0d0
    end do
    do while(coo(3) >= 1.0d0)
       coo(3) = coo(3) - 1.0d0
    end do

  end subroutine wrap_cooLatt

  !--------------------------------------------------------------------!
  !               volume of the cell spanned by a1,a2,a3               !
  !--------------------------------------------------------------------!

  function cell_volume(avec) result(V)

    implicit none

    double precision, dimension(3,3), intent(in) :: avec
    double precision                             :: V

    V = avec(1,1)*avec(2,2)*avec(3,3) &
      + avec(2,1)*avec(3,2)*avec(1,3) &
      + avec(3,1)*avec(1,2)*avec(2,3) &
      - avec(3,1)*avec(2,2)*avec(1,3) &
      - avec(1,1)*avec(3,2)*avec(2,3) &
      - avec(2,1)*avec(1,2)*avec(3,3)

    V = abs(V)

  end function cell_volume

  !--------------------------------------------------------------------!
  !          largest sphere within a cell spanned by a1,a2,a3          !
  !--------------------------------------------------------------------!

  subroutine inner_sphere(avec, r, c)

    implicit none

    double precision, dimension(3,3),           intent(in)  :: avec
    double precision,                           intent(out) :: r
    double precision, dimension(3),   optional, intent(out) :: c

    double precision, dimension(3) :: v
    double precision               :: h

    ! determine the shortest of the three heights:

    v = vproduct(avec(:,1),avec(:,2))
    v = v/sqrt(sum(v*v))
    h = abs(sum(v*avec(:,3)))
    r = h

    v = vproduct(avec(:,1),avec(:,3))
    v = v/sqrt(sum(v*v))
    h = abs(sum(v*avec(:,2)))
    r = min(r, h)

    v = vproduct(avec(:,2),avec(:,3))
    v = v/sqrt(sum(v*v))
    h = abs(sum(v*avec(:,1)))
    r = min(r, h)

    r = 0.5d0*r

    if (present(c)) c = 0.5d0*(avec(:,1) + avec(:,2) + avec(:,3))

  end subroutine inner_sphere

  !------------------------------------------------------------------!
  !                       vector/cross product                       !
  !------------------------------------------------------------------!

  function vproduct(a,b) result(c)

    implicit none

    double precision, dimension(3), intent(in) :: a, b
    double precision, dimension(3)             :: c

    c(1) = a(2)*b(3) - a(3)*b(2)
    c(2) = a(3)*b(1) - a(1)*b(3)
    c(3) = a(1)*b(2) - a(2)*b(1)

  end function vproduct

  !--------------------------------------------------------------------!
  !              periodic images within a cut-off radius               !
  !                                                                    !
  ! The subroutine returns the positive `half star' of translation     !
  ! vectors NOT including T = (0,0,0).                                 !
  !--------------------------------------------------------------------!

  subroutine translation_vectors(Rc, avec, nT, T, Tnorm, nc)

    implicit none

    !------------------------------------------------------------------!
    ! Rc         : cut-off radius                                      !
    ! avec(i,j)  : i-th component of lattice/cell vector j             !
    ! nT         : on entry: dimension of array T --> dimension(3,nT)  !
    !              on exit : number of T vectors found (can be > nT)   !
    !              --> the routine can be used just to calculate the   !
    !                  number of T vectors by setting nT = 0.          !
    ! T(i,j)     : (output) i-th component of the j-th T vector        !
    ! Tnorm(j)   : norm/length of the j-th T vector                    !
    !------------------------------------------------------------------!

    double precision,                          intent(in)    :: Rc
    double precision, dimension(3,3),          intent(in)    :: avec
    integer,                                   intent(inout) :: nT
    integer,          dimension(3,nT),         intent(out)   :: T
    double precision, dimension(nT), optional, intent(out)   :: Tnorm
    integer,          dimension(3),  optional, intent(in)    :: nc

    double precision,      parameter :: EPS = 1.0d-6

    double precision, dimension(3,8)              :: corner
    integer                                       :: ic, ic2, iT
    integer                                       :: i1, i2, i3
    integer                                       :: s2, s3
    logical                                       :: acc1, acc2, acc3
    double precision                              :: Rc2
    double precision, dimension(3)                :: v1, v2
    double precision                              :: vnorm1, vnorm2
    integer,          dimension(:),   allocatable :: idx
    integer,          dimension(:,:), allocatable :: T2
    double precision, dimension(:),   allocatable :: Tnorm2

    Rc2 = Rc*Rc
    iT  = 0

    ! 1/2 way to the corners of the cell spanned by avec:

    ic = 0
    do i3 = 0, 1
    do i2 = 0, 1
    do i1 = 0, 1
       ic = ic + 1
       corner(1:3,ic) = (dble(i1) - 0.5d0)*avec(1:3,1) &
                      + (dble(i2) - 0.5d0)*avec(1:3,2) &
                      + (dble(i3) - 0.5d0)*avec(1:3,3)
    end do
    end do
    end do

    ! loop over T vectors of increasing length:

    i3 = 0
    loop3 : do
       acc3 = .false.
       if (present(nc) .and. i3 >= nc(3)) exit loop3
       i2 = 0
       loop2 : do
          acc2 = .false.
          if (present(nc) .and. i2 >= nc(2)) exit loop2
          i1 = 0
          loop1 : do
             acc1 = .false.
             if (present(nc) .and. i1 >= nc(1)) exit loop1

             ! don't include T = (0,0,0)
             if (all((/i1,i2,i3/) == 0)) then
                acc2 = .true.
                i1 = i1 + 1
                cycle loop1
             end if

             sign3 : do s3 =  1, -1, -2
             sign2 : do s2 =  1, -1, -2

                v1(1:3) = dble(i1)*avec(1:3,1) &
                        + dble(s2*i2)*avec(1:3,2) &
                        + dble(s3*i3)*avec(1:3,3)

                ! loop over the eight corners of the cell:
                loopc  : do ic  = 1, 8
                loopc2 : do ic2 = 1, 8
                   v2(1:3) = v1(1:3) + corner(1:3,ic) - corner(1:3,ic2)
                   vnorm2 = sum(v2*v2)
                   if (vnorm2 - Rc2 < EPS) then
                      acc1 = .true.
                      iT = iT + 1
                      if (iT <= nT) then
                         T(1:3,iT) = (/ i1, s2*i2, s3*i3 /)
                         if (present(Tnorm)) then
                            vnorm1    = sum(v1*v1)
                            Tnorm(iT) = sqrt(vnorm1)
                         end if
                      end if
                      exit loopc
                   end if
                end do loopc2
                end do loopc

                if ((i1 == 0) .or. (i2 == 0)) exit sign2
             end do sign2
             if (((i1 == 0) .and. (i2 == 0)) .or. (i3 == 0)) exit sign3
             end do sign3

             if (.not. acc1) exit loop1
             acc2 = .true.
             i1 = i1 + 1
          end do loop1
          if (.not. acc2) exit loop2
          acc3 = .true.
          i2 = i2 + 1
       end do loop2
       if (.not. acc3) exit loop3
       i3 = i3 + 1
    end do loop3

    ! sort T vectors by length, but only if all of them were stored:

    if (present(Tnorm) .and. (iT <= nT)) then
       nT = iT
       allocate(idx(nT), T2(3,nT), Tnorm2(nT))
       call argsort(Tnorm(1:nT), idx)
       T2(:,1:nT)   = T(:,1:nT)
       Tnorm2(1:nT) = Tnorm(1:nT)
       do iT = 1, nT
          T(:,iT)   = T2(:,idx(iT))
          Tnorm(iT) = Tnorm2(idx(iT))
       end do
       deallocate(idx, T2, Tnorm2)
    else
       nT = iT
    end if

  end subroutine translation_vectors

  !--------------------------------------------------------------------!
  ! This procedure is not working properly, because it considers the   !
  ! outer sphere of the cell instead of the inner one.  Debug it when  !
  ! you feel bored.                                                    !
  !--------------------------------------------------------------------!

!!$  subroutine translation_vectors_old(Rc, avec, nT, T, Tnorm, nc)
!!$
!!$    !------------------------------------------------------------------!
!!$    ! Rc         : cut-off radius                                      !
!!$    ! avec(i,j)  : i-th component of lattice/cell vector j             !
!!$    ! nT         : on entry: dimension of array T --> dimension(3,nT)  !
!!$    !              on exit : number of T vectors found (can be > nT)   !
!!$    !              --> the routine can be used just to calculate the   !
!!$    !                  number of T vectors by setting nT = 0.          !
!!$    ! T(i,j)     : (output) i-th component of the j-th T vector        !
!!$    ! Tnorm(j)   : norm/length of the j-th T vector                    !
!!$    !------------------------------------------------------------------!
!!$
!!$    double precision,                          intent(in)    :: Rc
!!$    double precision, dimension(3,3),          intent(in)    :: avec
!!$    integer,                                   intent(inout) :: nT
!!$    integer,          dimension(3,nT),         intent(out)   :: T
!!$    double precision, dimension(nT), optional, intent(out)   :: Tnorm
!!$    integer,          dimension(3),  optional, intent(in)    :: nc
!!$
!!$    double precision,      parameter :: EPS = 1.0d-6
!!$
!!$    double precision, dimension(3,8)              :: corner
!!$    integer                                       :: ic, ic2
!!$    integer                                       :: i, i1, i2, i3
!!$    integer                                       :: s2, s3
!!$    double precision, dimension(3)                :: v, v2
!!$    double precision                              :: vnorm, v2norm
!!$    double precision, dimension(3)                :: a2
!!$    double precision                              :: f
!!$    integer,          dimension(3)                :: mult
!!$    integer                                       :: iT, nT_needed
!!$    integer,          dimension(:),   allocatable :: idx
!!$    integer,          dimension(:,:), allocatable :: T2
!!$    double precision, dimension(:),   allocatable :: Tnorm2
!!$
!!$    ! corners of the cell spanned by avec:
!!$
!!$    ic = 0
!!$    do i3 = 0, 1
!!$    do i2 = 0, 1
!!$    do i1 = 0, 1
!!$       ic = ic + 1
!!$       corner(1:3,ic) = dble(i1)*avec(1:3,1) &
!!$                      + dble(i2)*avec(1:3,2) &
!!$                      + dble(i3)*avec(1:3,3)
!!$    end do
!!$    end do
!!$    end do
!!$
!!$    ! find vector v(:) that connects the two corners that are
!!$    ! furthest away from each other:
!!$
!!$    v(:)  = corner(:,2) - corner(:,1)
!!$    vnorm = sum(v*v)
!!$    do ic = 1, 8
!!$    do ic2 = ic+1, 8
!!$       v2(:)  = corner(:,ic2) - corner(:,ic)
!!$       v2norm = sum(v2*v2)
!!$       if (vnorm < v2norm) then
!!$          vnorm = v2norm
!!$          v     = v2
!!$       end if
!!$    end do
!!$    end do
!!$    vnorm = sqrt(vnorm)
!!$
!!$    ! lengths^2 of the lattice vectors:
!!$
!!$    do i = 1, 3
!!$       a2(i) = sum(avec(:,i)*avec(:,i))
!!$    end do
!!$
!!$    ! Determine f_i for {f_i*a_i} i=1..3 span a supercell
!!$    ! that is large enough.  Round up to get full cells.
!!$    !
!!$    ! f_i = |v*a_i|/|a_i|^2 * (2Rc + |v|)/|v|
!!$
!!$    do i = 1, 3
!!$       f = abs(sum(v*avec(:,i)))
!!$       f = f/a2(i) * (2.0d0*Rc + vnorm)/vnorm
!!$       mult(i) = ceiling(0.5d0*(f - 1.0d0))
!!$    end do
!!$
!!$    ! In each lattice vector direction `i' we need -mult(i)...+mult(i)
!!$    ! cells.  Due to PBC, cells might be identical
!!$    ! --> make sure that mult(i) < nc(i)
!!$
!!$    if (present(nc)) then
!!$       do i = 1, 3
!!$          mult(i) = min(mult(i), nc(i)-1)
!!$       end do
!!$    end if
!!$
!!$    ! the number of T vectors (in the half star) are
!!$    ! nT_needed T vectors are needed
!!$
!!$    nT_needed = (2*mult(1) + 1)*(2*mult(2) + 1)*(2*mult(3) + 1)
!!$    nT_needed = (nT_needed - 1)/2
!!$    if (nT < nT_needed) then
!!$       ! array too small --> just return the number of T vectors
!!$       nT = nT_needed
!!$       return
!!$    end if
!!$
!!$    ! set up the T vectors:
!!$
!!$    iT = 0
!!$    do i1 = 0, mult(1)
!!$    do i2 = 0, mult(2)
!!$    do i3 = 0, mult(3)
!!$       if (all((/ i1, i2, i3/) == 0)) cycle
!!$       sgn2 : do s2 = 1, -1, -2
!!$       sgn3 : do s3 = 1, -1, -2
!!$          iT = iT + 1
!!$          T(:,iT) = (/ i1, s2*i2, s3*i3 /)
!!$          if (present(Tnorm)) then
!!$             v(1:3) =    i1*avec(:,1) &
!!$                    + s2*i2*avec(:,2) &
!!$                    + s3*i3*avec(:,3)
!!$             Tnorm(iT) = sqrt(sum(v*v))
!!$          end if
!!$          if (((i1 == 0) .and. (i2 == 0)) .or. (i3 == 0)) exit sgn3
!!$       end do sgn3
!!$       if ((i1 == 0) .or. (i2 == 0)) exit sgn2
!!$       end do sgn2
!!$    end do
!!$    end do
!!$    end do
!!$
!!$    ! sort T vectors by length, if their length was computed:
!!$
!!$    if (present(Tnorm)) then
!!$       nT = iT
!!$       allocate(idx(nT), T2(3,nT), Tnorm2(nT))
!!$       call argsort(Tnorm(1:nT), idx)
!!$       T2(:,1:nT)   = T(:,1:nT)
!!$       Tnorm2(1:nT) = Tnorm(1:nT)
!!$       do iT = 1, nT
!!$          T(:,iT)   = T2(:,idx(iT))
!!$          Tnorm(iT) = Tnorm2(idx(iT))
!!$       end do
!!$       deallocate(idx, T2, Tnorm2)
!!$    end if
!!$
!!$  end subroutine translation_vectors_old

end module lclist
ViewGit