!-*- mode: F90 -*-! !------------------------------------------------------------! ! Copyright (C) 2026 Wannier Developer Group ! ! ! ! This library is free software; you can redistribute it ! ! and/or modify it under the terms of the GNU Lesser General ! ! Public License as published by the Free Software ! ! Foundation; either version 2.1 of the License, or (at your ! ! option) any later version. ! ! ! ! This library 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 Lesser General Public License for ! ! more details. ! ! ! ! You should have received a copy of the GNU Lesser General ! ! Public License along with this library; if not, see ! ! <https://www.gnu.org/licenses/>. ! ! ! ! The webpage of the Wannier90 code is ! ! <https://www.wannier.org>. ! ! ! ! The Wannier90 code is hosted on GitHub ! ! <https://github.com/wannier-developers/wannier90> ! !------------------------------------------------------------! ! ! ! w90_kmesh: operations on BZ mesh ! ! ! !------------------------------------------------------------! module w90_kmesh !! Routines to analyse the regular k-point mesh !! and determine the overlaps neccessary for a finite !! difference representation of the spread operator. !! These overlaps are defined by a set of vectors (b-vectors) which !! connect the Bloch states. !! See Eq. B1 in Appendix B of Marzari and !! Vanderbilt PRB 56 12847 (1997) use w90_constants, only: dp use w90_types, only: max_shells, num_nnmax ! used for dimensioning use w90_error, only: w90_error_type, set_error_alloc, set_error_dealloc, set_error_fatal, & set_error_input, set_error_file use w90_comms, only: w90_comm_type implicit none private ! Definitions of system variables set in this module ! nnh ! the number of b-directions (bka) ! nntot ! total number of neighbours for each k-point ! nnlist ! list of neighbours for each k-point ! neigh ! nncell ! gives BZ of each neighbour of each k-point ! wbtot ! wb ! weights associated with neighbours of each k-point ! bk ! the b-vectors that go from each k-point to its neighbours ! bka ! the b-directions (not considering inversion) from ! 1st k-point to its neighbours public :: kmesh_dealloc public :: kmesh_get public :: kmesh_write integer, parameter :: nsupcell = 5 !! Size of supercell (of recip cell) in which to search for k-point shells contains !================================================ subroutine kmesh_get(kmesh_input, kmesh_info, print_output, kpt_latt, real_lattice, num_kpts, & gamma_only, seedname, stdout, timer, error, comm) !================================================ ! !! Main routine to calculate the b-vectors ! !================================================ use w90_utility, only: utility_compar, utility_recip_lattice, utility_frac_to_cart, & utility_cart_to_frac, utility_inverse_mat use w90_io, only: io_stopwatch_start, io_stopwatch_stop use w90_types, only: kmesh_info_type, kmesh_input_type, print_output_type, timer_list_type implicit none ! arguments type(print_output_type), intent(in) :: print_output type(kmesh_info_type), intent(inout) :: kmesh_info type(kmesh_input_type), intent(inout) :: kmesh_input type(timer_list_type), intent(inout) :: timer type(w90_error_type), allocatable, intent(out) :: error type(w90_comm_type), intent(in) :: comm character(len=*), intent(in) :: seedname integer, intent(in) :: num_kpts integer, intent(in) :: stdout real(kind=dp), intent(in) :: real_lattice(3, 3) real(kind=dp), intent(in) :: kpt_latt(:, :) logical, intent(in) :: gamma_only ! local variables real(kind=dp), allocatable :: bvec_tmp(:, :), bvec_inp(:, :, :) ! bvec_inp is allocated in kmesh_shell_from_file() real(kind=dp), allocatable :: kpt_cart(:, :) real(kind=dp), allocatable :: bk_local(:, :, :) real(kind=dp), parameter :: eta = 99999999.0_dp ! eta = very large real(kind=dp) :: dist, dnn0, dnn1, bb1, bbn, ddelta real(kind=dp) :: dnn(max(kmesh_input%search_shells, 6*kmesh_input%higher_order_n)) real(kind=dp) :: recip_lattice(3, 3), volume real(kind=dp) :: vkpp(3), vkpp2(3), kpbvec(3) integer :: bnum, nbvec ! context of kmesh_input%kmesh_shell_from_file ! higher-order finite-difference integer, allocatable :: lmn(:, :) ! Order in which to search the cells (ordered in dist from origin) integer, allocatable :: nnlist_tmp(:, :), nncell_tmp(:, :, :) ![ysl] integer, allocatable :: nnshell(:, :) integer :: ifound, counter, na, nap, loop_s, loop_b, shell !, nbvec, bnum integer :: ifpos, ifneg, ierr, multi(max(kmesh_input%search_shells, 6*kmesh_input%higher_order_n)) integer :: nlist, nkp, nkp2, l, m, n, ndnn, ndnnx, ndnntot integer :: nnsh, nn, nnx, loop, i, j integer :: num_first_shells, ndnn2, nnx2, multi_cumulative, lmn_temp(3) integer :: num_x((1 + kmesh_input%higher_order_n)*(1 + 2*kmesh_input%higher_order_n)) integer :: num_y((1 + kmesh_input%higher_order_n)*(1 + 2*kmesh_input%higher_order_n)) integer :: num_z((1 + kmesh_input%higher_order_n)*(1 + 2*kmesh_input%higher_order_n)) real(kind=dp) :: bk_latt(3), inv_lattice(3, 3) real(kind=dp) :: bweight(kmesh_input%max_shells_h) real(kind=dp) :: wb_local(kmesh_input%num_nnmax_h) if (print_output%timing_level > 0) call io_stopwatch_start('kmesh: get', timer) allocate (bk_local(3, kmesh_input%num_nnmax_h, num_kpts), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating bk_local in kmesh_get', comm) return end if allocate (lmn(3, (2*kmesh_input%search_supcell_size + 1)**3), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating lmn in kmesh_get', comm) return end if allocate (nnshell(num_kpts, max(kmesh_input%search_shells, 6*kmesh_input%higher_order_n)), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating nnshell in kmesh_get', comm) return end if call utility_recip_lattice(real_lattice, recip_lattice, volume, error, comm) if (allocated(error)) return call utility_inverse_mat(recip_lattice, inv_lattice) if (print_output%iprint > 0) write (stdout, '(/1x,a)') & '*---------------------------------- K-MESH ----------------------------------*' ! Sort the cell neighbours so we loop in order of distance from the home shell call kmesh_supercell_sort(print_output, recip_lattice, lmn, timer) allocate (kpt_cart(3, num_kpts), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating kpt_cart in kmesh_get', comm) return end if do nkp = 1, num_kpts call utility_frac_to_cart(kpt_latt(:, nkp), kpt_cart(:, nkp), recip_lattice) end do ! find the distance between k-point 1 and its nearest-neighbour shells ! if we have only one k-point, the n-neighbours are its periodic images dnn0 = 0.0_dp dnn1 = eta ndnntot = 0 do nlist = 1, kmesh_input%search_shells do nkp = 1, num_kpts do loop = 1, (2*kmesh_input%search_supcell_size + 1)**3 l = lmn(1, loop); m = lmn(2, loop); n = lmn(3, loop) ! vkpp = kpt_cart(:, nkp) + matmul(lmn(:, loop), recip_lattice) dist = sqrt((kpt_cart(1, 1) - vkpp(1))**2 & + (kpt_cart(2, 1) - vkpp(2))**2 + (kpt_cart(3, 1) - vkpp(3))**2) if ((dist .gt. kmesh_input%tol) .and. (dist .gt. dnn0 + kmesh_input%tol)) then if (dist .lt. dnn1 - kmesh_input%tol) then dnn1 = dist ! found a closer shell counter = 0 end if if (dist .gt. (dnn1 - kmesh_input%tol) .and. dist .lt. (dnn1 + kmesh_input%tol)) then counter = counter + 1 ! count the multiplicity of the shell end if end if end do end do if (dnn1 .lt. eta - kmesh_input%tol) ndnntot = ndnntot + 1 dnn(nlist) = dnn1 multi(nlist) = counter dnn0 = dnn1 dnn1 = eta end do if (print_output%iprint > 0) then write (stdout, '(1x,a)') '+----------------------------------------------------------------------------+' write (stdout, '(1x,a)') '| Distance to Nearest-Neighbour Shells |' write (stdout, '(1x,a)') '| ------------------------------------ |' if (trim(print_output%length_unit) == 'Ang') then write (stdout, '(1x,a)') '| Shell Distance (Ang^-1) Multiplicity |' write (stdout, '(1x,a)') '| ----- ----------------- ------------ |' else write (stdout, '(1x,a)') '| Shell Distance (Bohr^-1) Multiplicity |' write (stdout, '(1x,a)') '| ----- ------------------ ------------ |' end if do ndnn = 1, ndnntot write (stdout, '(1x,a,11x,i3,17x,f10.6,19x,i4,12x,a)') '|', ndnn, & dnn(ndnn)/print_output%lenconfac, multi(ndnn), '|' end do write (stdout, '(1x,a)') '+----------------------------------------------------------------------------+' end if if (print_output%iprint >= 4) then ! Write out all the bvectors if (print_output%iprint > 0) then write (stdout, '(1x,"|",76(" "),"|")') write (stdout, '(1x,a)') '| Complete list of b-vectors and their lengths |' write (stdout, '(1x,"|",76(" "),"|")') write (stdout, '(1x,"+",76("-"),"+")') end if allocate (bvec_tmp(3, maxval(multi)), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating bvec_tmp in kmesh_get', comm) return end if bvec_tmp = 0.0_dp counter = 0 do shell = 1, kmesh_input%search_shells call kmesh_get_bvectors(kmesh_input, print_output, bvec_tmp(:, 1:multi(shell)), kpt_cart, & recip_lattice, dnn(shell), lmn, 1, multi(shell), num_kpts, & timer, error, comm) if (allocated(error)) return do loop = 1, multi(shell) counter = counter + 1 if (print_output%iprint > 0) write (stdout, '(a,I4,1x,a,2x,3f12.6,2x,a,2x,f12.6,a)') ' | b-vector ', counter, ': (', & bvec_tmp(:, loop)/print_output%lenconfac, ')', dnn(shell)/print_output%lenconfac, ' |' end do end do deallocate (bvec_tmp, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating bvec_tmp in kmesh_get', comm) return end if if (print_output%iprint > 0) write (stdout, '(1x,"|",76(" "),"|")') if (print_output%iprint > 0) write (stdout, '(1x,"+",76("-"),"+")') end if ! Get the shell weights to satisfy the B1 condition if (kmesh_input%kmesh_shell_from_file) then ! note: B1 condition is not tested in kmesh_shell_from_file(); test occurs below call kmesh_shell_from_file(kmesh_input, print_output, bvec_inp, bweight, dnn, kpt_cart, & recip_lattice, lmn, multi, num_kpts, seedname, stdout, timer, & error, comm) if (allocated(error)) return elseif (kmesh_input%num_shells == 0) then call kmesh_shell_automatic(kmesh_input, print_output, bweight, dnn, kpt_cart, recip_lattice, & lmn, multi, num_kpts, stdout, timer, error, comm) if (allocated(error)) return elseif (kmesh_input%num_shells > 0) then call kmesh_shell_fixed(kmesh_input, print_output, bweight, dnn, kpt_cart, recip_lattice, & lmn, multi, num_kpts, stdout, timer, error, comm) if (allocated(error)) return end if num_first_shells = kmesh_input%num_shells ! for convenience in printout error if (print_output%iprint > 0) then if (kmesh_input%higher_order_nearest_shells) then write (stdout, '(1x,a)', advance='no') '| The following shells and their multiples are used: ' else write (stdout, '(1x,a)', advance='no') '| The following shells are used: ' end if do ndnn = 1, kmesh_input%num_shells if (ndnn .eq. kmesh_input%num_shells) then write (stdout, '(i3,1x)', advance='no') kmesh_input%shell_list(ndnn) else write (stdout, '(i3,",")', advance='no') kmesh_input%shell_list(ndnn) end if end do do l = 1, 6 - kmesh_input%num_shells write (stdout, '(4x)', advance='no') end do if (kmesh_input%higher_order_nearest_shells) then write (stdout, '(20x)', advance='no') end if write (stdout, '("|")') end if kmesh_info%nntot = 0 do loop_s = 1, kmesh_input%num_shells kmesh_info%nntot = kmesh_info%nntot + multi(kmesh_input%shell_list(loop_s)) end do if (kmesh_info%nntot > kmesh_input%num_nnmax_h) then if (print_output%iprint > 0) then write (stdout, '(a,i2,a)') ' **WARNING: kmesh has found >', kmesh_input%num_nnmax_h, ' nearest neighbours**' write (stdout, '(a)') ' ' write (stdout, '(a)') ' This is probably caused by an error in your unit cell specification' write (stdout, '(a)') ' ' write (stdout, '(a)') ' If you think this is not the problem; please send your *.win file to the ' write (stdout, '(a)') ' wannier90 developers' write (stdout, '(a)') ' ' write (stdout, '(a)') ' The problem may be caused by having accidentally degenerate shells of ' write (stdout, '(a)') ' kpoints. The solution is then to rerun wannier90 specifying the b-vectors ' write (stdout, '(a)') ' in each shell. Give kmesh_shell_from_file=T in the *.win file' write (stdout, '(a)') ' and create a *.kshell file:' write (stdout, '(a)') ' ' write (stdout, '(a)') ' $> cat hexagonal.kshell' write (stdout, '(a)') ' $> 1 2' write (stdout, '(a)') ' $> 5 6 7 8' write (stdout, '(a)') ' ' write (stdout, '(a)') ' Where each line is a new shell (so num_shells in total)' write (stdout, '(a)') ' The elements are the bvectors labelled according to the following ' write (stdout, '(a)') ' list (last column is distance)' write (stdout, '(a)') ' ' end if allocate (bvec_tmp(3, maxval(multi)), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating bvec_tmp in kmesh_get', comm) return end if bvec_tmp = 0.0_dp counter = 0 do shell = 1, kmesh_input%search_shells call kmesh_get_bvectors(kmesh_input, print_output, bvec_tmp(:, 1:multi(shell)), kpt_cart, & recip_lattice, dnn(shell), lmn, 1, multi(shell), num_kpts, timer, & error, comm) if (allocated(error)) return do loop = 1, multi(shell) counter = counter + 1 if (print_output%iprint > 0) write (stdout, '(a,I4,1x,a,2x,3f12.6,2x,a,2x,f12.6,a)') ' | b-vector ', counter, ': (', & bvec_tmp(:, loop)/print_output%lenconfac, ')', dnn(shell)/print_output%lenconfac, ' |' end do end do if (print_output%iprint > 0) write (stdout, '(a)') ' ' deallocate (bvec_tmp, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating bvec_tmp in kmesh_get', comm) return end if call set_error_fatal(error, 'kmesh_get: something wrong, found too many nearest neighbours', comm) return end if ! higher-order algorithm: include 2b, 3b, ..., Nb shells, and modify bweights if (kmesh_input%higher_order_nearest_shells) then if (print_output%iprint > 0) write (stdout, '(a)') & ' | WARNING: higher_order_nearest_shells is an experimental feature, and has |', & ' | not been extensively tested. |' else kmesh_info%nntot = kmesh_info%nntot*kmesh_input%higher_order_n end if allocate (kmesh_info%nnlist(num_kpts, kmesh_info%nntot), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating nnlist in kmesh_get', comm) return end if allocate (kmesh_info%neigh(num_kpts, kmesh_info%nntot/2), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating neigh in kmesh_get', comm) return end if allocate (kmesh_info%nncell(3, num_kpts, kmesh_info%nntot), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating nncell in kmesh_get', comm) return end if allocate (kmesh_info%wb(kmesh_info%nntot), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating wb in kmesh_get', comm) return end if allocate (kmesh_info%bka(3, kmesh_info%nntot/2), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating bka in kmesh_get', comm) return end if allocate (kmesh_info%bk(3, kmesh_info%nntot, num_kpts), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating bk in kmesh_get', comm) return end if nnx = 0 do loop_s = 1, kmesh_input%num_shells do loop_b = 1, multi(kmesh_input%shell_list(loop_s)) nnx = nnx + 1 wb_local(nnx) = bweight(loop_s) end do end do ! Now build up the list of nearest-neighbour shells for each k-point. ! nnlist(nkp,1...nnx) points to the nnx neighbours (ordered along increa ! shells) of the k-point nkp. nncell(i,nkp,nnth) tells us in which BZ is ! nnth nearest-neighbour of the k-point nkp. Construct the nnx b-vectors ! go from k-point nkp to each neighbour bk(1:3,nkp,1...nnx). ! Comment: Now we have bk(3,nntot,num_kps) 09/04/2006 if (print_output%iprint > 0) then write (stdout, '(1x,a)') '+----------------------------------------------------------------------------+' write (stdout, '(1x,a)') '| Shell # Nearest-Neighbours |' write (stdout, '(1x,a)') '| ----- -------------------- |' end if !if (index(print_output%devel_flag, 'kmesh_degen') == 0) then ! ! Standard routine ! nnshell = 0 do nkp = 1, num_kpts nnx = 0 ok: do ndnnx = 1, kmesh_input%num_shells ndnn = kmesh_input%shell_list(ndnnx) do loop = 1, (2*kmesh_input%search_supcell_size + 1)**3 l = lmn(1, loop); m = lmn(2, loop); n = lmn(3, loop) vkpp2 = matmul(lmn(:, loop), recip_lattice) do nkp2 = 1, num_kpts vkpp = vkpp2 + kpt_cart(:, nkp2) dist = sqrt((kpt_cart(1, nkp) - vkpp(1))**2 & + (kpt_cart(2, nkp) - vkpp(2))**2 + (kpt_cart(3, nkp) - vkpp(3))**2) if ((dist .ge. dnn(ndnn) - kmesh_input%tol) .and. (dist .le. dnn(ndnn) + kmesh_input%tol)) then nnx = nnx + 1 nnshell(nkp, ndnn) = nnshell(nkp, ndnn) + 1 kmesh_info%nnlist(nkp, nnx) = nkp2 kmesh_info%nncell(1, nkp, nnx) = l kmesh_info%nncell(2, nkp, nnx) = m kmesh_info%nncell(3, nkp, nnx) = n bk_local(:, nnx, nkp) = vkpp(:) - kpt_cart(:, nkp) end if !if we have the right number of neighbours we can exit if (nnshell(nkp, ndnn) == multi(ndnn)) cycle ok end do end do ! check to see if too few neighbours here end do ok end do ! higher-order algorithm: include 2b, 3b, ..., Nb shells, and modify bweights if (.not. kmesh_input%higher_order_nearest_shells) then ! update num_shells, shell_list, dnn(distance to shells), multi, etc. call kmesh_shell_reconstruct(kmesh_input, num_kpts, multi, dnn, nnshell, bweight) ! update bk_local ! update nnlist(neighboring kpts), and nncell(G_lmn vectors of neighbors) do nkp = 1, num_kpts do nn = 2, kmesh_input%higher_order_n multi_cumulative = 0 do ndnn = 1, kmesh_input%num_shells/kmesh_input%higher_order_n !first-order shells ! ndnn: index of shells (first order) ! ndnn2: index of shells (nn-th-order) ndnn2 = (nn - 1)*kmesh_input%num_shells/kmesh_input%higher_order_n + ndnn do nnx = 1 + multi_cumulative, multi(ndnn) + multi_cumulative counter = 0 ! nnx: index of bvectors (first order) ! nnx2: index of bvectors (nn-th-order) nnx2 = (nn - 1)*kmesh_info%nntot/kmesh_input%higher_order_n + nnx bk_local(:, nnx2, nkp) = nn*bk_local(:, nnx, nkp) ! find nnlist and nncell !do loop = 1, (2*kmesh_input%search_supcell_size + 1)**3 ! l = lmn(1, loop); m = lmn(2, loop); n = lmn(3, loop) ! vkpp2 = matmul(lmn(:, loop), recip_lattice) !G_lmn vector ! do nkp2 = 1, num_kpts ! vkpp = vkpp2 + kpt_cart(:, nkp2) - kpt_cart(:, nkp) ! ! if kp2 - kp1 == bk_local(:, nnx2, :) ! call utility_compar(vkpp(1), bk_local(1, nnx2, nkp), ifpos, ifneg) ! if (ifpos .eq. 1) then ! counter = counter + 1 ! kmesh_info%nnlist(nkp, nnx2) = nkp2 ! kmesh_info%nncell(1, nkp, nnx2) = l ! kmesh_info%nncell(2, nkp, nnx2) = m ! kmesh_info%nncell(3, nkp, nnx2) = n ! endif ! enddo !enddo ! do not search supcell ! find nnlist(nkp2) and nncell(lmn) call utility_cart_to_frac(bk_local(:, nnx2, nkp), bk_latt, inv_lattice) lmn_temp(1) = floor(kpt_latt(1, nkp) + bk_latt(1) + 1.e-6_dp) ! e.g. 3.999999999 is 4 lmn_temp(2) = floor(kpt_latt(2, nkp) + bk_latt(2) + 1.e-6_dp) lmn_temp(3) = floor(kpt_latt(3, nkp) + bk_latt(3) + 1.e-6_dp) vkpp2 = matmul(lmn_temp, recip_lattice) !G_lmn vector vkpp = kpt_cart(:, nkp) + bk_local(:, nnx2, nkp) - vkpp2 ! k_2 = k_1 + Nb - G_lmn do nkp2 = 1, num_kpts call utility_compar(kpt_cart(:, nkp2), vkpp, ifpos, ifneg) if (ifpos .eq. 1) then counter = counter + 1 kmesh_info%nnlist(nkp, nnx2) = nkp2 kmesh_info%nncell(1, nkp, nnx2) = lmn_temp(1) kmesh_info%nncell(2, nkp, nnx2) = lmn_temp(2) kmesh_info%nncell(3, nkp, nnx2) = lmn_temp(3) end if end do if (counter == 0) then call set_error_fatal(error, 'Could not find Nb vectors in kmesh_get', comm) end if if (counter >= 2) then call set_error_fatal(error, 'Error in kmesh_get, try to modify tolerance in utility_compar', comm) end if end do multi_cumulative = multi_cumulative + multi(ndnn) end do end do end do end if nnx = 0 do loop_s = 1, kmesh_input%num_shells do loop_b = 1, multi(kmesh_input%shell_list(loop_s)) nnx = nnx + 1 wb_local(nnx) = bweight(loop_s) end do end do if (kmesh_input%kmesh_shell_from_file) then ! should this be moved to the kmesh_shell_from_file as a function to simplify? nnshell = 0 do nkp = 1, num_kpts nnx = 0 ok2: do loop = 1, (2*kmesh_input%search_supcell_size + 1)**3 l = lmn(1, loop); m = lmn(2, loop); n = lmn(3, loop) vkpp2 = matmul(lmn(:, loop), recip_lattice) do nkp2 = 1, num_kpts vkpp = vkpp2 + kpt_cart(:, nkp2) bnum = 0 do ndnnx = 1, kmesh_input%num_shells do nbvec = 1, multi(ndnnx) bnum = bnum + 1 kpbvec = kpt_cart(:, nkp) + bvec_inp(:, nbvec, ndnnx) dist = sqrt((kpbvec(1) - vkpp(1))**2 & + (kpbvec(2) - vkpp(2))**2 + (kpbvec(3) - vkpp(3))**2) if (abs(dist) < kmesh_input%tol) then nnx = nnx + 1 nnshell(nkp, ndnnx) = nnshell(nkp, ndnnx) + 1 kmesh_info%nnlist(nkp, bnum) = nkp2 kmesh_info%nncell(1, nkp, bnum) = l kmesh_info%nncell(2, nkp, bnum) = m kmesh_info%nncell(3, nkp, bnum) = n bk_local(:, bnum, nkp) = bvec_inp(:, nbvec, ndnnx) end if end do end do if (nnx == sum(multi)) exit ok2 end do end do ok2 end do end if ! kmesh_shell_from_file if (kmesh_input%higher_order_n .eq. 1 .or. kmesh_input%higher_order_nearest_shells) then do ndnnx = 1, kmesh_input%num_shells ndnn = kmesh_input%shell_list(ndnnx) if (print_output%iprint > 0) then write (stdout, '(1x,a,24x,i3,13x,i3,33x,a)') '|', ndnn, nnshell(1, ndnn), '|' end if end do else do ndnnx = 1, num_first_shells ndnn = kmesh_input%shell_list(ndnnx) if (print_output%iprint > 0) then write (stdout, '(1x,a,24x,i3,13x,i3,33x,a)') '|', ndnn, nnshell(1, ndnn), '|' end if end do do i = 2, kmesh_input%higher_order_n do ndnnx = 1, num_first_shells ndnn = kmesh_input%shell_list(ndnnx) if (print_output%iprint > 0) then write (stdout, '(1x,a,20x,i3,a,i2,13x,i3,33x,a)') '|', i, ' x', ndnn, nnshell(1, ndnn), '|' end if end do end do end if if (print_output%iprint > 0) write (stdout, '(1x,"+",76("-"),"+")') do nkp = 1, num_kpts nnx = 0 do ndnnx = 1, kmesh_input%num_shells ndnn = kmesh_input%shell_list(ndnnx) do nnsh = 1, nnshell(nkp, ndnn) bb1 = 0.0_dp bbn = 0.0_dp nnx = nnx + 1 do i = 1, 3 bb1 = bb1 + bk_local(i, nnx, 1)*bk_local(i, nnx, 1) bbn = bbn + bk_local(i, nnx, nkp)*bk_local(i, nnx, nkp) end do if (abs(sqrt(bb1) - sqrt(bbn)) .gt. kmesh_input%tol) then if (print_output%iprint > 0) write (stdout, '(1x,2f10.6)') bb1, bbn call set_error_fatal(error, 'Non-symmetric k-point neighbours in kmesh_get', comm) return end if end do end do end do ! now check that the completeness relation is satisfied for every kpoint ! We know it is true for kpt=1; but we check the rest to be safe. ! Eq. B1 in Appendix B PRB 56 12847 (1997) if ((.not. kmesh_input%skip_B1_tests) .and. kmesh_input%higher_order_nearest_shells) then do nkp = 1, num_kpts do i = 1, kmesh_input%higher_order_n if ((.not. kmesh_input%higher_order_nearest_shells) .and. i > 1) exit do j = 1, (1 + i)*(1 + 2*i) ! multiset coefficient ((3, 2i)) = (1 + i)*(1 + 2*i), 3: x,y,z, 2i: num. of b ! separate cartesian components ! e.g. for i=1 ! j=1: num_x=2, num_y=0, num_z=0 ! j=2: num_x=1, num_y=1, num_z=0 ! j=3: num_x=0, num_y=2, num_z=0 ! j=4: num_x=1, num_y=0, num_z=1 ! j=5: num_x=0, num_y=1, num_z=1 ! j=6: num_x=0, num_y=0, num_z=2 do l = 0, 2*i if ((2*i + 1)*l - l*(l - 1)/2 <= j - 1 & .and. (2*i + 1)*(l + 1) - (l + 1)*l/2 > j - 1) then num_z(j) = l exit end if end do num_y(j) = j - 1 - ((2*i + 1)*num_z(j) - num_z(j)*(num_z(j) - 1)/2) num_x(j) = 2*i - num_y(j) - num_z(j) ddelta = 0.0_dp nnx = 0 do ndnnx = 1, kmesh_input%num_shells ndnn = kmesh_input%shell_list(ndnnx) do nnsh = 1, nnshell(1, ndnn) nnx = nnx + 1 ddelta = ddelta + wb_local(nnx)*(bk_local(1, nnx, nkp)**num_x(j)) & *(bk_local(2, nnx, nkp)**num_y(j))*(bk_local(3, nnx, nkp)**num_z(j)) end do end do if (i .eq. 1 .and. (j .eq. 1 .or. j .eq. 3 .or. j .eq. 6)) then if (abs(ddelta - 1.0_dp) .gt. kmesh_input%tol) then if (print_output%iprint > 0) write (stdout, '(1x,3i3,f12.8)') num_x(j), num_y(j), num_z(j), ddelta call set_error_fatal(error, 'Eq. (B1) not satisfied in kmesh_get (1)', comm) end if else if (abs(ddelta) .gt. kmesh_input%tol) then if (print_output%iprint > 0) write (stdout, '(1x,3i3,f12.8)') num_x(j), num_y(j), num_z(j), ddelta call set_error_fatal(error, 'Eq. (B1) not satisfied in kmesh_get (2)', comm) end if end if end do end do end do end if if (print_output%iprint > 0) then write (stdout, '(1x,a)') '| Completeness relation is fully satisfied [Eq. (B1), PRB 56, 12847 (1997)] |' if ((kmesh_input%higher_order_nearest_shells) .and. (kmesh_input%higher_order_n .gt. 1)) then write (stdout, '(1x,a)') '| Completeness relations for higher-order are fully satisfied |' end if write (stdout, '(1x,"+",76("-"),"+")') end if kmesh_info%wbtot = 0.0_dp nnx = 0 do ndnnx = 1, kmesh_input%num_shells ndnn = kmesh_input%shell_list(ndnnx) do nnsh = 1, nnshell(1, ndnn) nnx = nnx + 1 kmesh_info%wbtot = kmesh_info%wbtot + wb_local(nnx) end do end do kmesh_info%nnh = kmesh_info%nntot/2 ! make list of bka vectors from neighbours of first k-point ! delete any inverse vectors as you collect them na = 0 do nn = 1, kmesh_info%nntot ifound = 0 if (na .ne. 0) then do nap = 1, na call utility_compar(kmesh_info%bka(1, nap), bk_local(1, nn, 1), ifpos, ifneg) if (ifneg .eq. 1) ifound = 1 end do end if if (ifound .eq. 0) then ! found new vector to add to set na = na + 1 kmesh_info%bka(1, na) = bk_local(1, nn, 1) kmesh_info%bka(2, na) = bk_local(2, nn, 1) kmesh_info%bka(3, na) = bk_local(3, nn, 1) end if end do if (na .ne. kmesh_info%nnh) then call set_error_fatal(error, 'Did not find right number of bk directions kmesh_get', comm) return end if if (print_output%iprint > 0) then if (trim(print_output%length_unit) == 'Ang') then write (stdout, '(1x,a)') '| b_k Vectors (Ang^-1) and Weights (Ang^2) |' write (stdout, '(1x,a)') '| ---------------------------------------- |' else write (stdout, '(1x,a)') '| b_k Vectors (Bohr^-1) and Weights (Bohr^2) |' write (stdout, '(1x,a)') '| ------------------------------------------ |' end if write (stdout, '(1x,a)') '| No. b_k(x) b_k(y) b_k(z) w_b |' write (stdout, '(1x,a)') '| --- -------------------------------- -------- |' do i = 1, kmesh_info%nntot write (stdout, '(1x,"|",11x,i3,5x,3f12.6,3x,f10.6,8x,"|")') & i, (bk_local(j, i, 1)/print_output%lenconfac, j=1, 3), wb_local(i)*print_output%lenconfac**2 end do write (stdout, '(1x,"+",76("-"),"+")') if (trim(print_output%length_unit) == 'Ang') then write (stdout, '(1x,a)') '| b_k Directions (Ang^-1) |' write (stdout, '(1x,a)') '| ----------------------- |' else write (stdout, '(1x,a)') '| b_k Directions (Bohr^-1) |' write (stdout, '(1x,a)') '| ------------------------ |' end if write (stdout, '(1x,a)') '| No. x y z |' write (stdout, '(1x,a)') '| --- -------------------------------- |' do i = 1, kmesh_info%nnh write (stdout, '(1x,"|",11x,i3,5x,3f12.6,21x,"|")') i, (kmesh_info%bka(j, i)/print_output%lenconfac, j=1, 3) end do write (stdout, '(1x,"+",76("-"),"+")') write (stdout, *) ' ' end if ! find index array do nkp = 1, num_kpts do na = 1, kmesh_info%nnh ! first, zero the index array so we can check it gets filled kmesh_info%neigh(nkp, na) = 0 ! now search through list of neighbours of this k-point do nn = 1, kmesh_info%nntot call utility_compar(kmesh_info%bka(1, na), bk_local(1, nn, nkp), ifpos, ifneg) if (ifpos .eq. 1) kmesh_info%neigh(nkp, na) = nn end do ! check found if (kmesh_info%neigh(nkp, na) .eq. 0) then if (print_output%iprint > 0) write (stdout, *) ' nkp,na=', nkp, na call set_error_fatal(error, 'kmesh_get: failed to find neighbours for this kpoint', comm) return end if end do end do !fill in the global arrays from the local ones do loop = 1, kmesh_info%nntot kmesh_info%wb(loop) = wb_local(loop) end do do loop_s = 1, num_kpts do loop = 1, kmesh_info%nntot kmesh_info%bk(:, loop, loop_s) = bk_local(:, loop, loop_s) end do end do ![ysl-b] if (gamma_only) then ! use half of the b-vectors if (num_kpts .ne. 1) then call set_error_input(error, 'Error in kmesh_get: wrong choice of gamma_only option', comm) return end if ! reassign nnlist, nncell, wb, bk allocate (nnlist_tmp(num_kpts, kmesh_info%nntot), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating nnlist_tmp in kmesh_get', comm) return end if allocate (nncell_tmp(3, num_kpts, kmesh_info%nntot), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating nncell_tmp in kmesh_get', comm) return end if nnlist_tmp(:, :) = kmesh_info%nnlist(:, :) nncell_tmp(:, :, :) = kmesh_info%nncell(:, :, :) deallocate (kmesh_info%nnlist, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating nnlist in kmesh_get', comm) return end if deallocate (kmesh_info%nncell, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating nncell in kmesh_get', comm) return end if deallocate (kmesh_info%wb, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating wb in kmesh_get', comm) return end if deallocate (kmesh_info%bk, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating bk in kmesh_get', comm) return end if kmesh_info%nntot = kmesh_info%nntot/2 allocate (kmesh_info%nnlist(num_kpts, kmesh_info%nntot), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating nnlist in kmesh_get', comm) return end if allocate (kmesh_info%nncell(3, num_kpts, kmesh_info%nntot), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating nncell in kmesh_get', comm) return end if allocate (kmesh_info%wb(kmesh_info%nntot), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating wb in kmesh_get', comm) return end if allocate (kmesh_info%bk(3, kmesh_info%nntot, num_kpts), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating bk in kmesh_get', comm) return end if na = 0 do nn = 1, 2*kmesh_info%nntot ifound = 0 if (na .ne. 0) then do nap = 1, na call utility_compar(kmesh_info%bk(1, nap, 1), bk_local(1, nn, 1), ifpos, ifneg) if (ifneg .eq. 1) ifound = 1 end do end if if (ifound .eq. 0) then ! found new vector to add to set na = na + 1 kmesh_info%bk(1, na, 1) = bk_local(1, nn, 1) kmesh_info%bk(2, na, 1) = bk_local(2, nn, 1) kmesh_info%bk(3, na, 1) = bk_local(3, nn, 1) kmesh_info%wb(na) = 2.0_dp*wb_local(nn) kmesh_info%nnlist(1, na) = nnlist_tmp(1, nn) kmesh_info%nncell(1, 1, na) = nncell_tmp(1, 1, nn) kmesh_info%nncell(2, 1, na) = nncell_tmp(2, 1, nn) kmesh_info%nncell(3, 1, na) = nncell_tmp(3, 1, nn) kmesh_info%neigh(1, na) = na ! check bk.eq.bka call utility_compar(kmesh_info%bk(1, na, 1), kmesh_info%bka(1, na), ifpos, ifneg) if (ifpos .ne. 1) then call set_error_input(error, 'Error in kmesh_get: bk is not identical to bka in gamma_only option', comm) return end if end if end do if (na .ne. kmesh_info%nnh) then call set_error_fatal(error, 'kmesh_get: Did not find right number of b-vectors in gamma_only option', comm) return end if if (print_output%iprint > 0) then write (stdout, '(1x,"+",76("-"),"+")') write (stdout, '(1x,a)') '| Gamma-point: number of the b-vectors is reduced by half |' write (stdout, '(1x,"+",76("-"),"+")') if (trim(print_output%length_unit) == 'Ang') then write (stdout, '(1x,a)') '| b_k Vectors (Ang^-1) and Weights (Ang^2) |' write (stdout, '(1x,a)') '| ---------------------------------------- |' else write (stdout, '(1x,a)') '| b_k Vectors (Bohr^-1) and Weights (Bohr^2) |' write (stdout, '(1x,a)') '| ------------------------------------------ |' end if write (stdout, '(1x,a)') '| No. b_k(x) b_k(y) b_k(z) w_b |' write (stdout, '(1x,a)') '| --- -------------------------------- -------- |' do i = 1, kmesh_info%nntot write (stdout, '(1x,"|",11x,i3,5x,3f12.6,3x,f10.6,8x,"|")') & i, (kmesh_info%bk(j, i, 1)/print_output%lenconfac, j=1, 3), kmesh_info%wb(i)*print_output%lenconfac**2 end do write (stdout, '(1x,"+",76("-"),"+")') write (stdout, *) ' ' end if deallocate (nnlist_tmp, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating nnlist_tmp in kmesh_get', comm) return end if deallocate (nncell_tmp, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating nncell_tmp in kmesh_get', comm) return end if end if ![ysl-e] ! JJ, is use_ss_functional necessarily defined here, or must it be moved to "special" !if (wann_control%use_ss_functional) then if (.not. gamma_only) then allocate (kmesh_info%nnord(kmesh_info%nntot, num_kpts), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating kmesh_info%nnord in kmesh_get', comm) return end if allocate (kmesh_info%nninv(kmesh_info%nntot, num_kpts), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating kmesh_info%nninv in kmesh_get', comm) return end if allocate (kmesh_info%nnrev(kmesh_info%nntot, num_kpts), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error in allocating kmesh_info%nnrev in kmesh_get', comm) return end if call kmesh_bvectors_perm(kmesh_info%bk(:, :, :), kmesh_info%bk(:, :, 1), num_kpts, & kmesh_info%nntot, kmesh_info%nnord, kmesh_info%nninv, & kmesh_info%nnrev, error, comm) if (allocated(error)) return end if deallocate (kpt_cart, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating kpt_cart in kmesh_get', comm) return end if deallocate (bk_local, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating bk_local in kmesh_get', comm) return end if deallocate (lmn, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating lmn in kmesh_get', comm) return end if deallocate (nnshell, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating nnshell in kmesh_get', comm) return end if if (print_output%timing_level > 0) call io_stopwatch_stop('kmesh: get', timer) return end subroutine kmesh_get !================================================! subroutine kmesh_write(exclude_bands, kmesh_info, lauto_proj, proj, print_output, kpt_latt, & real_lattice, num_kpts, num_proj, calc_only_A, spinors, seedname, timer) !==================================================================! ! ! !! Writes nnkp file (list of overlaps needed) ! ! ! Note that the format is different to (and more compact than) ! ! that used by the old f77 code. ! ! ! ! The file consists of num_kpts blocks of data, one block for each ! ! k-point of the mesh. Each block consists of nntot+1 lines, ! ! where nntot is the (integer) number of nearest neighbours ! ! belonging to k-point nkp. ! ! ! ! The first line in each block is just nntot. ! ! ! ! The second line consists of 5 integers. The first is the k-point ! ! nkp. The second to the fifth specify it's nearest neighbours ! ! k+b: the second integer points to the k-point that is the ! ! periodic image of k+b that we want; the last three integers give ! ! the G-vector, in reciprocal lattice units, that brings the ! ! k-point specified by the second integer (which is in the first ! ! BZ) to the actual k+b that we need. ! ! ! ! So wannier.nnkp specifies the nearest neighbours of each ! ! k-point, and therefore provides the information required to ! ! calculate the M_mn(k,b) matrix elements -- Marzari & Vanderbilt ! ! PRB 56, 12847 (1997) Eq. (25) -- for each pair of band indices ! ! m and n. ! !==================================================================! use w90_io, only: io_date, io_stopwatch_start, io_stopwatch_stop use w90_utility, only: utility_recip_lattice_base use w90_types, only: kmesh_info_type, kmesh_input_type, & proj_type, print_output_type, timer_list_type implicit none character(len=*), intent(in) :: seedname integer, allocatable, intent(in) :: exclude_bands(:) integer, intent(in) :: num_kpts integer, intent(inout) :: num_proj logical, intent(in) :: calc_only_A logical, intent(in) :: spinors logical, intent(in) :: lauto_proj real(kind=dp), intent(in) :: kpt_latt(:, :) real(kind=dp), intent(in) :: real_lattice(3, 3) type(kmesh_info_type), intent(in) :: kmesh_info type(print_output_type), intent(in) :: print_output type(proj_type), allocatable, intent(in) :: proj(:) ! alloc only because allocation status is tested type(timer_list_type), intent(inout) :: timer real(kind=dp) :: recip_lattice(3, 3), volume integer :: i, nkp, nn, nnkpout, num_exclude_bands character(len=9) :: cdate, ctime if (print_output%timing_level > 0) call io_stopwatch_start('kmesh: write', timer) open (newunit=nnkpout, file=trim(seedname)//'.nnkp', form='formatted') ! Date and time call io_date(cdate, ctime) write (nnkpout, '(4(a),/)') '# File written on ', cdate, ' at ', ctime ! Calc_only_A write (nnkpout, '(a,l2,/)') 'calc_only_A : ', calc_only_A ! Real lattice write (nnkpout, '(a)') 'begin real_lattice' write (nnkpout, '(3(f12.7))') (real_lattice(1, i), i=1, 3) write (nnkpout, '(3(f12.7))') (real_lattice(2, i), i=1, 3) write (nnkpout, '(3(f12.7))') (real_lattice(3, i), i=1, 3) write (nnkpout, '(a/)') 'end real_lattice' ! Reciprocal lattice call utility_recip_lattice_base(real_lattice, recip_lattice, volume) write (nnkpout, '(a)') 'begin recip_lattice' write (nnkpout, '(3f12.7)') (recip_lattice(1, i), i=1, 3) write (nnkpout, '(3f12.7)') (recip_lattice(2, i), i=1, 3) write (nnkpout, '(3f12.7)') (recip_lattice(3, i), i=1, 3) write (nnkpout, '(a/)') 'end recip_lattice' ! K-points write (nnkpout, '(a)') 'begin kpoints' write (nnkpout, '(i6)') num_kpts do nkp = 1, num_kpts write (nnkpout, '(3f14.8)') (kpt_latt(i, nkp), i=1, 3) end do write (nnkpout, '(a/)') 'end kpoints' if (spinors) then ! Projections write (nnkpout, '(a)') 'begin spinor_projections' if (allocated(proj)) then write (nnkpout, '(i6)') num_proj do i = 1, num_proj write (nnkpout, '(3(f10.5,1x),2x,3i3)') & proj(i)%site(1), proj(i)%site(2), proj(i)%site(3), & proj(i)%l, proj(i)%m, proj(i)%radial write (nnkpout, '(2x,3f11.7,1x,3f11.7,1x,f7.2)') & proj(i)%z(1), proj(i)%z(2), proj(i)%z(3), & proj(i)%x(1), proj(i)%x(2), proj(i)%x(3), & proj(i)%zona write (nnkpout, '(2x,1i3,1x,3f11.7)') & proj(i)%s, & proj(i)%s_qaxis(1), proj(i)%s_qaxis(2), proj(i)%s_qaxis(3) end do else ! No projections write (nnkpout, '(i6)') 0 end if write (nnkpout, '(a/)') 'end spinor_projections' else ! Projections write (nnkpout, '(a)') 'begin projections' if (allocated(proj)) then write (nnkpout, '(i6)') num_proj do i = 1, num_proj write (nnkpout, '(3(f10.5,1x),2x,3i3)') & proj(i)%site(1), proj(i)%site(2), proj(i)%site(3), & proj(i)%l, proj(i)%m, proj(i)%radial write (nnkpout, '(2x,3f11.7,1x,3f11.7,1x,f7.2)') & proj(i)%z(1), proj(i)%z(2), proj(i)%z(3), & proj(i)%x(1), proj(i)%x(2), proj(i)%x(3), & proj(i)%zona end do else ! No projections write (nnkpout, '(i6)') 0 end if write (nnkpout, '(a/)') 'end projections' end if ! Info for automatic generation of projections if (lauto_proj) then write (nnkpout, '(a)') 'begin auto_projections' write (nnkpout, '(i6)') num_proj write (nnkpout, '(i6)') 0 write (nnkpout, '(a/)') 'end auto_projections' end if ! Nearest neighbour k-points write (nnkpout, '(a)') 'begin nnkpts' write (nnkpout, '(i4)') kmesh_info%nntot do nkp = 1, num_kpts do nn = 1, kmesh_info%nntot write (nnkpout, '(2i8,3x,3i4)') & nkp, kmesh_info%nnlist(nkp, nn), (kmesh_info%nncell(i, nkp, nn), i=1, 3) end do end do write (nnkpout, '(a/)') 'end nnkpts' !states to exclude num_exclude_bands = 0 if (allocated(exclude_bands)) num_exclude_bands = size(exclude_bands) write (nnkpout, '(a)') 'begin exclude_bands' write (nnkpout, '(i4)') num_exclude_bands if (num_exclude_bands > 0) then do i = 1, num_exclude_bands write (nnkpout, '(i4)') exclude_bands(i) end do end if write (nnkpout, '(a)') 'end exclude_bands' close (nnkpout) if (print_output%timing_level > 0) call io_stopwatch_stop('kmesh: write', timer) return end subroutine kmesh_write !================================================ subroutine kmesh_dealloc(kmesh_info, error, comm) !================================================ !! Release memory from the kmesh module ! This routine now check to see if arrays ! are allocated, as there are some code ! paths that will not allocate on all nodes !================================================ use w90_types, only: kmesh_info_type implicit none type(kmesh_info_type), intent(inout) :: kmesh_info type(w90_error_type), allocatable, intent(out) :: error type(w90_comm_type), intent(in) :: comm integer :: ierr ! Deallocate real arrays if (allocated(kmesh_info%bk)) then deallocate (kmesh_info%bk, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating bk in kmesh_dealloc', comm) return end if end if if (allocated(kmesh_info%bka)) then deallocate (kmesh_info%bka, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating bka in kmesh_dealloc', comm) return end if end if if (allocated(kmesh_info%wb)) then deallocate (kmesh_info%wb, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating wb in kmesh_dealloc', comm) return end if end if if (allocated(kmesh_info%nnord)) then deallocate (kmesh_info%wb, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating wb in kmesh_dealloc', comm) return end if end if if (allocated(kmesh_info%nninv)) then deallocate (kmesh_info%wb, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating wb in kmesh_dealloc', comm) return end if end if if (allocated(kmesh_info%nnrev)) then deallocate (kmesh_info%wb, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating wb in kmesh_dealloc', comm) return end if end if ! Deallocate integer arrays if (allocated(kmesh_info%neigh)) then deallocate (kmesh_info%neigh, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating neigh in kmesh_dealloc', comm) return end if end if if (allocated(kmesh_info%nncell)) then deallocate (kmesh_info%nncell, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating nncell in kmesh_dealloc', comm) return end if end if if (allocated(kmesh_info%nnlist)) then deallocate (kmesh_info%nnlist, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error in deallocating nnlist in kmesh_dealloc', comm) return end if end if return end subroutine kmesh_dealloc !================================================ subroutine kmesh_supercell_sort(print_output, recip_lattice, lmn, timer) !================================================ !! We look for kpoint neighbours in a large supercell of reciprocal !! unit cells. Done sequentially this is very slow. !! Here we order the cells by the distance from the origin. !! Doing the search in this order gives a dramatic speed up !================================================ use w90_io, only: io_stopwatch_start, io_stopwatch_stop use w90_types, only: print_output_type, timer_list_type implicit none type(print_output_type), intent(in) :: print_output integer, intent(inout) :: lmn(:, :) real(kind=dp), intent(in) :: recip_lattice(3, 3) type(timer_list_type), intent(inout) :: timer integer :: counter, l, m, n, loop !! Order in which to search the cells (ordered in dist from origin) integer :: lmn_cp(3, (2*nsupcell + 1)**3), indx(1) real(kind=dp) :: pos(3) real(kind=dp) :: dist((2*nsupcell + 1)**3) real(kind=dp) :: dist_cp((2*nsupcell + 1)**3) if (print_output%timing_level > 1) call io_stopwatch_start('kmesh: supercell_sort', timer) counter = 1 lmn(:, counter) = 0 dist(counter) = 0.0_dp do l = -nsupcell, nsupcell do m = -nsupcell, nsupcell do n = -nsupcell, nsupcell if (l == 0 .and. m == 0 .and. n == 0) cycle counter = counter + 1 lmn(1, counter) = l; lmn(2, counter) = m; lmn(3, counter) = n pos = matmul(lmn(:, counter), recip_lattice) dist(counter) = sqrt(dot_product(pos, pos)) end do end do end do do loop = (2*nsupcell + 1)**3, 1, -1 indx = internal_maxloc(dist, nsupcell) dist_cp(loop) = dist(indx(1)) lmn_cp(:, loop) = lmn(:, indx(1)) dist(indx(1)) = -1.0_dp end do lmn = lmn_cp dist = dist_cp if (print_output%timing_level > 1) call io_stopwatch_stop('kmesh: supercell_sort', timer) end subroutine kmesh_supercell_sort !================================================ subroutine kmesh_get_bvectors(kmesh_input, print_output, bvector, kpt_cart, recip_lattice, & shell_dist, lmn, kpt, multi, num_kpts, timer, error, comm) !================================================ ! !! Returns the b-vectors for a given shell and kpoint. ! !================================================ use w90_io, only: io_stopwatch_start, io_stopwatch_stop use w90_types, only: kmesh_input_type, print_output_type, timer_list_type implicit none ! arguments type(print_output_type), intent(in) :: print_output type(kmesh_input_type), intent(in) :: kmesh_input type(timer_list_type), intent(inout) :: timer type(w90_error_type), allocatable, intent(out) :: error type(w90_comm_type), intent(in) :: comm integer, intent(in) :: num_kpts integer, intent(in) :: lmn(:, :) integer, intent(in) :: multi ! the number of kpoints in the shell integer, intent(in) :: kpt ! which kpt is our 'origin' real(kind=dp), intent(in) :: recip_lattice(3, 3) real(kind=dp), intent(in) ::kpt_cart(:, :) real(kind=dp), intent(in) :: shell_dist ! the bvectors real(kind=dp), intent(out) :: bvector(3, multi) ! the bvectors ! local variables integer :: loop, nkp2, num_bvec real(kind=dp) :: dist, vkpp2(3), vkpp(3) if (print_output%timing_level > 1) call io_stopwatch_start('kmesh: get_bvectors', timer) bvector = 0.0_dp num_bvec = 0 ok: do loop = 1, (2*kmesh_input%search_supcell_size + 1)**3 vkpp2 = matmul(lmn(:, loop), recip_lattice) do nkp2 = 1, num_kpts vkpp = vkpp2 + kpt_cart(:, nkp2) dist = sqrt((kpt_cart(1, kpt) - vkpp(1))**2 & + (kpt_cart(2, kpt) - vkpp(2))**2 + (kpt_cart(3, kpt) - vkpp(3))**2) if ((dist .ge. shell_dist - kmesh_input%tol) .and. (dist .le. shell_dist + kmesh_input%tol)) then num_bvec = num_bvec + 1 bvector(:, num_bvec) = vkpp(:) - kpt_cart(:, kpt) end if !if we have the right number of neighbours we can exit if (num_bvec == multi) cycle ok end do end do ok if (num_bvec < multi) then call set_error_fatal(error, 'kmesh_get_bvector: Not enough bvectors found', comm) return end if if (print_output%timing_level > 1) call io_stopwatch_stop('kmesh: get_bvectors', timer) return end subroutine kmesh_get_bvectors !================================================ subroutine kmesh_shell_automatic(kmesh_input, print_output, bweight, dnn, kpt_cart, & recip_lattice, lmn, multi, num_kpts, stdout, timer, error, & comm) !================================================ !! Find the correct set of shells to satisfy B1 !! The stratagy is: !! 1) Take the bvectors from the next shell !! 2) Reject them if they are parallel to exisiting b vectors !! 3) Test to see if we satisfy B1, if not add another shell and repeat ! !================================================ use w90_constants, only: eps5, eps6 use w90_io, only: io_stopwatch_start, io_stopwatch_stop use w90_types, only: kmesh_input_type, print_output_type, timer_list_type implicit none ! arguments type(print_output_type), intent(in) :: print_output type(kmesh_input_type), intent(inout) :: kmesh_input type(timer_list_type), intent(inout) :: timer type(w90_error_type), allocatable, intent(out) :: error type(w90_comm_type), intent(in) :: comm integer, intent(in) :: num_kpts integer, intent(in) :: stdout integer, intent(in) :: lmn(:, :) integer, intent(in) :: multi(kmesh_input%search_shells) ! the number of kpoints in the shell real(kind=dp), intent(in) :: recip_lattice(3, 3) real(kind=dp), intent(in) :: kpt_cart(:, :) real(kind=dp), intent(in) :: dnn(kmesh_input%search_shells) ! the bvectors real(kind=dp), intent(out) :: bweight(kmesh_input%max_shells_h) ! local variables integer :: loop_bn, loop_b, loop_s, info, cur_shell, ierr, loop, shell logical :: lpar real(kind=dp), allocatable :: amat(:, :), umat(:, :), vmat(:, :), smat(:, :), tmp0(:, :) real(kind=dp), allocatable :: bvector(:, :, :) ! the bvectors real(kind=dp), allocatable :: singv(:), tmp1(:), tmp2(:), tmp3(:) real(kind=dp) :: delta real(kind=dp) :: target(kmesh_input%max_shells_aux) real(kind=dp) :: work((kmesh_input%max_shells_aux)*10) ! variables for higher-order finite-difference integer, dimension(:, :), allocatable :: num_x, num_y, num_z logical :: bsat integer :: loop_order, num_of_eqs, higher_order_n_local if (kmesh_input%higher_order_nearest_shells) then higher_order_n_local = kmesh_input%higher_order_n else higher_order_n_local = 1 !find 1st-order b and weights first in this subroutine end if target = 0.0_dp; target(1) = 1.0_dp; target(3) = 1.0_dp; target(6) = 1.0_dp if (print_output%timing_level > 1) call io_stopwatch_start('kmesh: shell_automatic', timer) allocate (bvector(3, maxval(multi), kmesh_input%max_shells_h), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating bvector in kmesh_shell_automatic', comm) return end if bvector = 0.0_dp; bweight = 0.0_dp if (print_output%iprint > 0) then write (stdout, '(1x,a)') '| The b-vectors are chosen automatically |' end if ! note allocation of kmesh_input%shell list in subroutine w90_readwrite_read_kmesh_data() ! kmesh_input%num_shells = 0 in same place bsat = .false. do shell = 1, kmesh_input%search_shells cur_shell = kmesh_input%num_shells + 1 ! get the b vectors for the new shell call kmesh_get_bvectors(kmesh_input, print_output, bvector(:, 1:multi(shell), cur_shell), & kpt_cart, recip_lattice, dnn(shell), lmn, 1, multi(shell), num_kpts, & timer, error, comm) if (allocated(error)) return if (print_output%iprint >= 3) then write (stdout, '(1x,a8,1x,I2,a14,1x,I2,49x,a)') '| Shell:', shell, ' Multiplicity:', multi(shell), '|' do loop = 1, multi(shell) write (stdout, '(1x,a10,I2,1x,a1,4x,3f12.6,5x,a9,9x,a)') '| b-vector ', loop, ':', & bvector(:, loop, cur_shell)/print_output%lenconfac, '('//trim(print_output%length_unit)//'^-1)', '|' end do end if ! We check that the new shell is not parrallel to an existing shell (cosine=1) if (higher_order_n_local == 1) then lpar = .false. if (kmesh_input%num_shells > 0) then do loop_bn = 1, multi(shell) do loop_s = 1, kmesh_input%num_shells do loop_b = 1, multi(kmesh_input%shell_list(loop_s)) delta = dot_product(bvector(:, loop_bn, cur_shell), bvector(:, loop_b, loop_s))/ & sqrt(dot_product(bvector(:, loop_bn, cur_shell), bvector(:, loop_bn, cur_shell))* & dot_product(bvector(:, loop_b, loop_s), bvector(:, loop_b, loop_s))) if (abs(abs(delta) - 1.0_dp) < eps6) lpar = .true. end do end do end do end if if (lpar) then if (print_output%iprint >= 3) then write (stdout, '(1x,a)') '| This shell is linearly dependent on existing shells: Trying next shell |' end if cycle end if end if kmesh_input%num_shells = kmesh_input%num_shells + 1 kmesh_input%shell_list(kmesh_input%num_shells) = shell allocate (tmp0(kmesh_input%max_shells_aux, kmesh_input%max_shells_aux), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating tmp0 in kmesh_shell_automatic', comm) return end if allocate (tmp1(kmesh_input%max_shells_aux), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating tmp1 in kmesh_shell_automatic', comm) return end if allocate (tmp2(kmesh_input%num_shells), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating tmp2 in kmesh_shell_automatic', comm) return end if allocate (tmp3(kmesh_input%num_shells), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating tmp3 in kmesh_shell_automatic', comm) return end if allocate (amat(kmesh_input%max_shells_aux, kmesh_input%num_shells), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating amat in kmesh_shell_automatic', comm) return end if allocate (umat(kmesh_input%max_shells_aux, kmesh_input%max_shells_aux), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating umat in kmesh_shell_automatic', comm) return end if allocate (vmat(kmesh_input%num_shells, kmesh_input%num_shells), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating vmat in kmesh_shell_automatic', comm) return end if allocate (smat(kmesh_input%num_shells, kmesh_input%max_shells_aux), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating smat in kmesh_shell_automatic', comm) return end if allocate (singv(kmesh_input%num_shells), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating singv in kmesh_shell_automatic', comm) return end if amat(:, :) = 0.0_dp; umat(:, :) = 0.0_dp; vmat(:, :) = 0.0_dp; smat(:, :) = 0.0_dp; singv(:) = 0.0_dp num_of_eqs = (1 + higher_order_n_local)*(1 + 2*higher_order_n_local) allocate (num_x(higher_order_n_local, num_of_eqs), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating num_x in kmesh_shell_automatic', comm) return end if allocate (num_y(higher_order_n_local, num_of_eqs), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating num_y in kmesh_shell_automatic', comm) return end if allocate (num_z(higher_order_n_local, num_of_eqs), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating num_z in kmesh_shell_automatic', comm) return end if !find higher finite-diff weights ! make test suite(compare nnkp files) do loop_order = 1, higher_order_n_local call kmesh_get_amat(kmesh_input, amat, bvector, multi, loop_order, & num_x(loop_order, :), num_y(loop_order, :), num_z(loop_order, :)) end do info = 0 call dgesvd('A', 'A', kmesh_input%max_shells_aux, kmesh_input%num_shells, amat, & kmesh_input%max_shells_aux, singv, umat, & kmesh_input%max_shells_aux, vmat, kmesh_input%num_shells, work, kmesh_input%max_shells_aux*10, info) if (info < 0) then if (print_output%iprint > 0) then write (stdout, '(1x,a,1x,I1,1x,a)') 'kmesh_shell_automatic: Argument', abs(info), & 'of dgesvd is incorrect' end if call set_error_fatal(error, 'kmesh_shell_automatic: Problem with Singular Value Decomposition', comm) return else if (info > 0) then call set_error_fatal(error, 'kmesh_shell_automatic: Singular Value Decomposition did not converge', comm) return end if if (any(abs(singv) < eps5)) then if (kmesh_input%num_shells == 1) then call set_error_fatal(error, & 'kmesh_shell_automatic: Singular Value Decomposition has found a very small singular value', comm) return else if (print_output%iprint > 0) then write (stdout, '(1x,a)') '| SVD found small singular value, Rejecting this shell and trying the next |' end if bsat = .false. kmesh_input%num_shells = kmesh_input%num_shells - 1 goto 200 end if end if smat = 0.0_dp do loop_s = 1, kmesh_input%num_shells smat(loop_s, loop_s) = 1.0_dp/singv(loop_s) end do ! S. Ponce: The following below is correct but had to be unpacked because of PGI-15 ! bweight(1:num_shells)=matmul(transpose(vmat),matmul(smat,matmul(transpose(umat),target))) tmp0 = transpose(umat) tmp1 = matmul(tmp0, target) tmp2 = matmul(smat, tmp1) tmp3 = matmul(transpose(vmat), tmp2) bweight(1:kmesh_input%num_shells) = tmp3 if (print_output%iprint >= 2) then do loop_s = 1, kmesh_input%num_shells write (stdout, '(1x,a,I2,a,f12.7,5x,a8,36x,a)') '| Shell: ', loop_s, & ' w_b ', bweight(loop_s)*print_output%lenconfac**2, '('//trim(print_output%length_unit)//'^2)', '|' end do end if !check if the conditions including (B1) for finite-difference are satisfied bsat = .true. do loop_order = 1, higher_order_n_local call kmesh_check_condition(kmesh_input, bsat, bvector, bweight, multi, loop_order, & num_x(loop_order, :), num_y(loop_order, :), num_z(loop_order, :)) end do if (.not. bsat) then if (shell < kmesh_input%search_shells .and. print_output%iprint >= 3) then if (print_output%iprint > 0) write (stdout, '(1x,a,24x,a1)') '| B1 condition is not satisfied: Adding another shell', '|' elseif (shell == kmesh_input%search_shells) then if (print_output%iprint > 0) then write (stdout, *) ' ' write (stdout, '(1x,a,i3,a)') 'Unable to satisfy the higher-order version of B1 with any of the first ' & , kmesh_input%search_shells, ' shells' write (stdout, '(1x,a)') 'Check that you have specified your unit cell to a high precision' write (stdout, '(1x,a)') 'Low precision might cause a loss of symmetry.' write (stdout, '(1x,a)') ' ' write (stdout, '(1x,a)') 'If your cell is very long, or you have an irregular MP grid' write (stdout, '(1x,a)') 'Try increasing the parameter search_shells in the win file (default=30)' write (stdout, *) ' ' call set_error_fatal(error, 'kmesh_shell_automatic: unable to satisfy the higher-order version of B1', comm) return end if end if end if 200 continue deallocate (tmp0, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating tmp0 in kmesh_shell_automatic', comm) return end if deallocate (tmp1, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating tmp1 in kmesh_shell_automatic', comm) return end if deallocate (tmp2, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating tmp2 in kmesh_shell_automatic', comm) return end if deallocate (tmp3, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating tmp3 in kmesh_shell_automatic', comm) return end if deallocate (amat, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating amat in kmesh_shell_automatic', comm) return end if deallocate (umat, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating umat in kmesh_shell_automatic', comm) return end if deallocate (vmat, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating vmat in kmesh_shell_automatic', comm) return end if deallocate (smat, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating smat in kmesh_shell_automatic', comm) return end if deallocate (singv, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating singv in kmesh_shell_automatic', comm) return end if deallocate (num_x, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating num_x in kmesh_shell_automatic', comm) return end if deallocate (num_y, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating num_y in kmesh_shell_automatic', comm) return end if deallocate (num_z, stat=ierr) if (ierr /= 0) then call set_error_dealloc(error, 'Error deallocating num_z in kmesh_shell_automatic', comm) return end if if (bsat) exit end do if (.not. bsat) then if (print_output%iprint > 0) then write (stdout, *) ' ' write (stdout, '(1x,a,i3,a)') 'Unable to satisfy B1 with any of the first ', & kmesh_input%search_shells, ' shells' write (stdout, '(1x,a)') 'Your cell might be very long, or you may have an irregular MP grid' write (stdout, '(1x,a)') 'Try increasing the parameter search_shells in the win file (default=36)' write (stdout, *) ' ' end if call set_error_fatal(error, 'kmesh_shell_automatic: Unable to satisfy B1 condition', comm) return end if if (print_output%timing_level > 1) call io_stopwatch_stop('kmesh: shell_automatic', timer) return end subroutine kmesh_shell_automatic subroutine kmesh_shell_reconstruct(kmesh_input, num_kpts, multi, dnn, nnshell, bweight) !================================================ ! !! Include more shells to calculate higher-order finite difference: 2b, 3b, ... Nb shells !! Note: some shells are overwritten !================================================ use w90_types, only: kmesh_input_type, print_output_type implicit none ! arguments type(kmesh_input_type), intent(inout) :: kmesh_input integer, intent(in) :: num_kpts integer, intent(inout) :: multi(max(kmesh_input%search_shells, 6*kmesh_input%higher_order_n)) ! the number of bvectors in the shell real(kind=dp), intent(inout) :: dnn(max(kmesh_input%search_shells, 6*kmesh_input%higher_order_n)) integer, intent(inout) :: nnshell(num_kpts, max(kmesh_input%search_shells, 6*kmesh_input%higher_order_n)) real(kind=dp), intent(inout) :: bweight(kmesh_input%max_shells_h) ! local variables real(kind=dp) :: bweight_temp, fact integer :: shell, order, loop_j, temp_multi(kmesh_input%num_shells), temp_nnshell(num_kpts, kmesh_input%num_shells) real(kind=dp) :: temp_dnn(kmesh_input%num_shells) ! update new shells (after simplify the first-order shell list, e.g. 1,4,6, ... -> 1,2,3,...) do shell = 1, kmesh_input%num_shells temp_multi(shell) = multi(kmesh_input%shell_list(shell)) temp_nnshell(:, shell) = nnshell(:, kmesh_input%shell_list(shell)) temp_dnn(shell) = dnn(kmesh_input%shell_list(shell)) end do do shell = 1, kmesh_input%num_shells kmesh_input%shell_list(shell) = shell multi(shell) = temp_multi(shell) nnshell(:, shell) = temp_nnshell(:, shell) dnn(shell) = temp_dnn(shell) end do do order = 2, kmesh_input%higher_order_n do shell = 1, kmesh_input%num_shells kmesh_input%shell_list((order - 1)*kmesh_input%num_shells + shell) = & (order - 1)*kmesh_input%num_shells + shell multi((order - 1)*kmesh_input%num_shells + shell) = multi(shell) nnshell(:, (order - 1)*kmesh_input%num_shells + shell) = nnshell(:, shell) dnn((order - 1)*kmesh_input%num_shells + shell) = dnn(shell)*order end do end do ! calculate new bweights w_b, w_2b, ..., w_Nb do shell = 1, kmesh_input%num_shells bweight_temp = bweight(shell) do order = 1, kmesh_input%higher_order_n fact = 1.0_dp/REAL(order**2, DP) do loop_j = 1, kmesh_input%higher_order_n if (loop_j == order) cycle fact = (fact*REAL(loop_j**2, DP))/REAL(loop_j**2 - order**2, DP) end do bweight(kmesh_input%num_shells*(order - 1) + shell) = bweight_temp*fact end do end do kmesh_input%num_shells = kmesh_input%num_shells*kmesh_input%higher_order_n return end subroutine kmesh_shell_reconstruct !================================================ subroutine kmesh_get_amat(kmesh_input, amat, bvector, multi, loop_order, num_x, num_y, num_z) !================================================ ! !! Find amat(coefficients to find bweight) and the numbers of x, y, z components for a given order ! !================================================ use w90_types, only: kmesh_input_type implicit none ! arguments type(kmesh_input_type), intent(inout) :: kmesh_input real(kind=dp), intent(inout) :: amat(:, :) integer, intent(inout) :: num_x(:) integer, intent(inout) :: num_y(:) integer, intent(inout) :: num_z(:) integer, intent(in) :: multi(kmesh_input%search_shells) ! the number of kpoints in the shell integer, intent(in) :: loop_order real(kind=dp), intent(in) :: bvector(3, maxval(multi), kmesh_input%max_shells_h) ! local variables integer :: num_of_eqs, num_of_eqs_prev integer :: loop_i, loop_j, loop_s, loop_b num_of_eqs = (1 + loop_order)*(1 + 2*loop_order) !((3, 2n)) (combi. with repetition) num_of_eqs_prev = loop_order*(2*loop_order - 1) if (loop_order .eq. 1) num_of_eqs_prev = 0 do loop_s = 1, kmesh_input%num_shells do loop_i = 1, num_of_eqs ! equation index, e.g. If loop_order == 3, (1,2,...,15) -> (xxx, xxy, xyy, yyy, xxz, xyz, ..., zzz) ! find the number of z components corresponding to the current loop_i do loop_j = 0, 2*loop_order if ((2*loop_order + 1)*loop_j - loop_j*(loop_j - 1)/2 <= loop_i - 1 & .and. (2*loop_order + 1)*(loop_j + 1) - (loop_j + 1)*loop_j/2 > loop_i - 1) then num_z(loop_i) = loop_j exit end if end do ! find the number of x and y components num_y(loop_i) = loop_i - 1 - ((2*loop_order + 1)*num_z(loop_i) - num_z(loop_i) & *(num_z(loop_i) - 1)/2) num_x(loop_i) = 2*loop_order - num_y(loop_i) - num_z(loop_i) ! calculate sum_b bb...bbbb do loop_b = 1, multi(kmesh_input%shell_list(loop_s)) amat(num_of_eqs_prev + loop_i, loop_s) = amat(num_of_eqs_prev + loop_i, loop_s) & + (bvector(1, loop_b, loop_s)**num_x(loop_i)) & *(bvector(2, loop_b, loop_s)**num_y(loop_i)) & *(bvector(3, loop_b, loop_s)**num_z(loop_i)) end do end do end do end subroutine kmesh_get_amat !================================================ subroutine kmesh_check_condition(kmesh_input, bsat, bvector, bweight, multi, loop_order, num_x, num_y, num_z) !================================================ ! !! Check if the obtained bweight satisfy the conditions for finite-difference, including (B1). ! !================================================ use w90_types, only: kmesh_input_type implicit none ! arguments type(kmesh_input_type), intent(inout) :: kmesh_input logical, intent(inout) :: bsat integer, intent(inout) :: num_x(:) integer, intent(inout) :: num_y(:) integer, intent(inout) :: num_z(:) integer, intent(in) :: multi(kmesh_input%search_shells) ! the number of kpoints in the shell integer, intent(in) :: loop_order real(kind=dp), intent(in) :: bvector(3, maxval(multi), kmesh_input%max_shells_h) real(kind=dp), intent(in) :: bweight(kmesh_input%max_shells_h) ! local variables integer :: num_of_eqs integer :: loop_i, loop_s, loop_b real(kind=dp) :: delta num_of_eqs = (1 + loop_order)*(1 + 2*loop_order) do loop_i = 1, num_of_eqs delta = 0.0_dp do loop_s = 1, kmesh_input%num_shells do loop_b = 1, multi(kmesh_input%shell_list(loop_s)) delta = delta + bweight(loop_s)*(bvector(1, loop_b, loop_s)**num_x(loop_i)) & *(bvector(2, loop_b, loop_s)**num_y(loop_i)) & *(bvector(3, loop_b, loop_s)**num_z(loop_i)) end do end do if (loop_order .eq. 1 .and. (loop_i .eq. 1 .or. loop_i .eq. 3 .or. loop_i .eq. 6)) then if (abs(delta - 1.0_dp) > kmesh_input%tol) bsat = .false. else if (abs(delta) > kmesh_input%tol) bsat = .false. end if end do end subroutine kmesh_check_condition !================================================ subroutine kmesh_shell_fixed(kmesh_input, print_output, bweight, dnn, kpt_cart, recip_lattice, & lmn, multi, num_kpts, stdout, timer, error, comm) !================================================ ! !! Find the B1 weights for a set of shells specified by the user ! !================================================ use w90_constants, only: eps7 use w90_io, only: io_stopwatch_start, io_stopwatch_stop use w90_types, only: kmesh_input_type, print_output_type, timer_list_type implicit none ! arguments type(print_output_type), intent(in) :: print_output type(kmesh_input_type), intent(in) :: kmesh_input type(timer_list_type), intent(inout) :: timer type(w90_error_type), allocatable, intent(out) :: error type(w90_comm_type), intent(in) :: comm integer, intent(in) :: num_kpts integer, intent(in) :: stdout integer, intent(in) :: lmn(:, :) integer, intent(in) :: multi(kmesh_input%search_shells) ! the number of kpoints in the shell real(kind=dp), intent(in) :: recip_lattice(3, 3) real(kind=dp), intent(in) ::kpt_cart(:, :) real(kind=dp), intent(in) :: dnn(kmesh_input%search_shells) ! the bvectors real(kind=dp), intent(out) :: bweight(max_shells) ! local variables real(kind=dp), allocatable :: bvector(:, :, :) real(kind=dp) :: singv(kmesh_input%num_shells) real(kind=dp) :: amat(max_shells, kmesh_input%num_shells) real(kind=dp) :: umat(max_shells, max_shells) real(kind=dp) :: vmat(kmesh_input%num_shells, kmesh_input%num_shells) real(kind=dp) :: smat(kmesh_input%num_shells, max_shells) integer, parameter :: lwork = max_shells*10 real(kind=dp) :: work(lwork) real(kind=dp), parameter :: target(6) = (/1.0_dp, 1.0_dp, 1.0_dp, 0.0_dp, 0.0_dp, 0.0_dp/) logical :: b1sat integer :: ierr, loop_i, loop_j, loop_b, loop_s, info real(kind=dp) :: delta integer :: loop, shell if (print_output%timing_level > 1) call io_stopwatch_start('kmesh: shell_fixed', timer) allocate (bvector(3, maxval(multi), kmesh_input%num_shells), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating bvector in kmesh_shell_fixed', comm) return end if bvector = 0.0_dp; bweight = 0.0_dp amat = 0.0_dp; umat = 0.0_dp; vmat = 0.0_dp; smat = 0.0_dp; singv = 0.0_dp if (print_output%iprint > 0) then write (stdout, '(1x,a)') '| The b-vectors are set in the win file |' end if do shell = 1, kmesh_input%num_shells ! get the b vectors for this shell call kmesh_get_bvectors(kmesh_input, print_output, & bvector(:, 1:multi(kmesh_input%shell_list(shell)), shell), kpt_cart, & recip_lattice, dnn(kmesh_input%shell_list(shell)), lmn, 1, & multi(kmesh_input%shell_list(shell)), num_kpts, timer, error, comm) if (allocated(error)) return end do if (print_output%iprint >= 3) then do shell = 1, kmesh_input%num_shells write (stdout, '(1x,a8,1x,I2,a14,1x,I2,49x,a)') '| Shell:', shell, ' Multiplicity:', & multi(kmesh_input%shell_list(shell)), '|' do loop = 1, multi(kmesh_input%shell_list(shell)) write (stdout, '(1x,a10,I2,1x,a1,4x,3f12.6,5x,a9,9x,a)') '| b-vector ', loop, ':', & bvector(:, loop, shell)/print_output%lenconfac, '('//trim(print_output%length_unit)//'^-1)', '|' end do end do end if do loop_s = 1, kmesh_input%num_shells do loop_b = 1, multi(kmesh_input%shell_list(loop_s)) amat(1, loop_s) = amat(1, loop_s) + bvector(1, loop_b, loop_s)*bvector(1, loop_b, loop_s) amat(2, loop_s) = amat(2, loop_s) + bvector(2, loop_b, loop_s)*bvector(2, loop_b, loop_s) amat(3, loop_s) = amat(3, loop_s) + bvector(3, loop_b, loop_s)*bvector(3, loop_b, loop_s) amat(4, loop_s) = amat(4, loop_s) + bvector(1, loop_b, loop_s)*bvector(2, loop_b, loop_s) amat(5, loop_s) = amat(5, loop_s) + bvector(2, loop_b, loop_s)*bvector(3, loop_b, loop_s) amat(6, loop_s) = amat(6, loop_s) + bvector(3, loop_b, loop_s)*bvector(1, loop_b, loop_s) end do end do info = 0 call dgesvd('A', 'A', max_shells, kmesh_input%num_shells, amat, max_shells, singv, umat, & max_shells, vmat, kmesh_input%num_shells, work, lwork, info) if (info < 0) then if (print_output%iprint > 0) then write (stdout, '(1x,a,1x,I1,1x,a)') 'kmesh_shell_fixed: Argument', abs(info), & 'of dgesvd is incorrect' end if call set_error_fatal(error, 'kmesh_shell_fixed: Problem with Singular Value Decomposition', comm) return else if (info > 0) then call set_error_fatal(error, 'kmesh_shell_fixed: Singular Value Decomposition did not converge', comm) return end if if (any(abs(singv) < eps7)) then call set_error_fatal(error, 'kmesh_shell_fixed: Singular Value Decomposition has found a very small singular value', comm) return end if smat = 0.0_dp do loop_s = 1, kmesh_input%num_shells smat(loop_s, loop_s) = 1/singv(loop_s) end do bweight(1:kmesh_input%num_shells) = matmul(transpose(vmat), matmul(smat, matmul(transpose(umat), target))) if (print_output%iprint >= 2) then do loop_s = 1, kmesh_input%num_shells write (stdout, '(1x,a,I2,a,f12.7,5x,a8,36x,a)') '| Shell: ', loop_s, & ' w_b ', bweight(loop_s)*print_output%lenconfac**2, '('//trim(print_output%length_unit)//'^2)', '|' end do end if !check b1 b1sat = .true. if (.not. kmesh_input%skip_B1_tests) then do loop_i = 1, 3 do loop_j = loop_i, 3 delta = 0.0_dp do loop_s = 1, kmesh_input%num_shells do loop_b = 1, multi(kmesh_input%shell_list(loop_s)) delta = delta + bweight(loop_s)*bvector(loop_i, loop_b, loop_s)*bvector(loop_j, loop_b, loop_s) end do end do if (loop_i == loop_j) then if (abs(delta - 1.0_dp) > kmesh_input%tol) b1sat = .false. end if if (loop_i /= loop_j) then if (abs(delta) > kmesh_input%tol) b1sat = .false. end if end do end do end if if (.not. b1sat) then call set_error_fatal(error, 'kmesh_shell_fixed: B1 condition not satisfied', comm) return end if if (print_output%timing_level > 1) call io_stopwatch_stop('kmesh: shell_fixed', timer) return end subroutine kmesh_shell_fixed !================================================ subroutine kmesh_shell_from_file(kmesh_input, print_output, bvec_inp, bweight, dnn, kpt_cart, & recip_lattice, lmn, multi, num_kpts, seedname, stdout, timer, & error, comm) !================================================ !! Find the B1 weights for a set of b-vectors given in a file. !! This routine is activated via kmesh_shell_from_file = T !! It is not intended for regular use. ! !================================================ use w90_constants, only: eps7, maxlen use w90_io, only: io_stopwatch_start, io_stopwatch_stop use w90_types, only: kmesh_input_type, print_output_type, timer_list_type implicit none ! arguments type(print_output_type), intent(in) :: print_output type(kmesh_input_type), intent(inout) :: kmesh_input type(timer_list_type), intent(inout) :: timer type(w90_error_type), allocatable, intent(out) :: error type(w90_comm_type), intent(in) :: comm integer, intent(in) :: num_kpts, stdout integer, intent(in) :: lmn(:, :) integer, intent(inout) :: multi(kmesh_input%search_shells) ! the number of kpoints in the shell real(kind=dp), intent(in) :: recip_lattice(3, 3) real(kind=dp), intent(in) ::kpt_cart(:, :) real(kind=dp), allocatable, intent(inout) :: bvec_inp(:, :, :) real(kind=dp), intent(in) :: dnn(kmesh_input%search_shells) ! the bvectors real(kind=dp), intent(out) :: bweight(max_shells) character(len=50), intent(in) :: seedname ! local variables real(kind=dp), allocatable :: bvector(:, :) real(kind=dp), dimension(:), allocatable :: singv real(kind=dp), dimension(:, :), allocatable :: amat, umat, vmat, smat integer, parameter :: lwork = max_shells*10 real(kind=dp) :: work(lwork) integer :: bvec_list(num_nnmax, max_shells) real(kind=dp), parameter :: target(6) = (/1.0_dp, 1.0_dp, 1.0_dp, 0.0_dp, 0.0_dp, 0.0_dp/) integer :: ierr, loop_b, loop_s, info integer :: loop, shell, pos, kshell_in, counter, length, i, loop2, num_lines, tot_num_lines character(len=maxlen) :: dummy, dummy2 if (print_output%timing_level > 1) call io_stopwatch_start('kmesh: shell_from_file', timer) allocate (bvector(3, sum(multi)), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating bvector in kmesh_shell_from_file', comm) return end if bvector = 0.0_dp; bweight = 0.0_dp if (print_output%iprint > 0) then write (stdout, '(1x,a)') '| The b-vectors are defined in the kshell file |' end if counter = 1 do shell = 1, kmesh_input%search_shells ! get the b vectors call kmesh_get_bvectors(kmesh_input, print_output, bvector(:, counter:counter + multi(shell) - 1), & kpt_cart, recip_lattice, dnn(shell), lmn, 1, multi(shell), num_kpts, & timer, error, comm) if (allocated(error)) return counter = counter + multi(shell) end do open (newunit=kshell_in, file=trim(seedname)//'.kshell', form='formatted', status='old', & action='read', iostat=ierr) if (ierr /= 0) then call set_error_file(error, 'Error: Problem (1) opening input file '//trim(seedname)//'.kshell', comm) return end if num_lines = 0; tot_num_lines = 0 do read (kshell_in, '(a)', iostat=ierr) dummy if (ierr == 0) then !read ok, proceed dummy = adjustl(dummy) tot_num_lines = tot_num_lines + 1 if (.not. dummy(1:1) == '!' .and. .not. dummy(1:1) == '#') then if (len(trim(dummy)) > 0) num_lines = num_lines + 1 end if else if (ierr > 0) then !error case call set_error_input(error, 'Error: Problem (2) reading input file '//trim(seedname)//'.kshell', comm) return else if (ierr < 0) then !end of record or end of file exit end if end do rewind (kshell_in) kmesh_input%num_shells = num_lines multi(:) = 0 bvec_list = 1 counter = 0 do loop = 1, tot_num_lines read (kshell_in, '(a)', err=103, end=103) dummy2 dummy2 = adjustl(dummy2) if (dummy2(1:1) == '!' .or. dummy2(1:1) == '#' .or. (len(trim(dummy2)) == 0)) cycle counter = counter + 1 kmesh_input%shell_list(counter) = counter dummy = dummy2 length = 1 dummy = adjustl(dummy) do pos = index(dummy, ' ') dummy = dummy(pos + 1:) dummy = adjustl(dummy) if (len_trim(dummy) > 0) then length = length + 1 else exit end if end do multi(counter) = length read (dummy2, *, err=230, end=230) (bvec_list(i, loop), i=1, length) end do allocate (bvec_inp(3, maxval(multi), kmesh_input%num_shells), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating bvec_inp in kmesh_shell_from_file', comm) return end if bvec_inp = 0.0_dp do loop = 1, kmesh_input%num_shells do loop2 = 1, multi(loop) bvec_inp(:, loop2, loop) = bvector(:, bvec_list(loop2, loop)) end do end do if (print_output%iprint >= 3) then do shell = 1, kmesh_input%num_shells write (stdout, '(1x,a8,1x,I2,a14,1x,I2,49x,a)') '| Shell:', shell, ' Multiplicity:', multi(shell), '|' do loop = 1, multi(shell) write (stdout, '(1x,a10,I2,1x,a1,4x,3f12.6,5x,a9,9x,a)') '| b-vector ', loop, ':', & bvec_inp(:, loop, shell)/print_output%lenconfac, '('//trim(print_output%length_unit)//'^-1)', '|' end do end do end if allocate (amat(max_shells, kmesh_input%num_shells), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating amat in kmesh_shell_from_file', comm) return end if allocate (umat(max_shells, max_shells), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating umat in kmesh_shell_from_file', comm) return end if allocate (vmat(kmesh_input%num_shells, kmesh_input%num_shells), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating vmat in kmesh_shell_from_file', comm) return end if allocate (smat(kmesh_input%num_shells, max_shells), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating smat in kmesh_shell_from_file', comm) return end if allocate (singv(kmesh_input%num_shells), stat=ierr) if (ierr /= 0) then call set_error_alloc(error, 'Error allocating singv in kmesh_shell_from_file', comm) return end if amat = 0.0_dp; umat = 0.0_dp; vmat = 0.0_dp; smat = 0.0_dp; singv = 0.0_dp do loop_s = 1, kmesh_input%num_shells do loop_b = 1, multi(loop_s) amat(1, loop_s) = amat(1, loop_s) + bvec_inp(1, loop_b, loop_s)*bvec_inp(1, loop_b, loop_s) amat(2, loop_s) = amat(2, loop_s) + bvec_inp(2, loop_b, loop_s)*bvec_inp(2, loop_b, loop_s) amat(3, loop_s) = amat(3, loop_s) + bvec_inp(3, loop_b, loop_s)*bvec_inp(3, loop_b, loop_s) amat(4, loop_s) = amat(4, loop_s) + bvec_inp(1, loop_b, loop_s)*bvec_inp(2, loop_b, loop_s) amat(5, loop_s) = amat(5, loop_s) + bvec_inp(2, loop_b, loop_s)*bvec_inp(3, loop_b, loop_s) amat(6, loop_s) = amat(6, loop_s) + bvec_inp(3, loop_b, loop_s)*bvec_inp(1, loop_b, loop_s) end do end do info = 0 call dgesvd('A', 'A', max_shells, kmesh_input%num_shells, amat, max_shells, singv, umat, & max_shells, vmat, kmesh_input%num_shells, work, lwork, info) if (info < 0) then if (print_output%iprint > 0) then write (stdout, '(1x,a,1x,I1,1x,a)') 'kmesh_shell_fixed: Argument', abs(info), & 'of dgesvd is incorrect' end if call set_error_fatal(error, 'kmesh_shell_from_file: Problem with Singular Value Decomposition', comm) return else if (info > 0) then call set_error_fatal(error, 'kmesh_shell_from_file: Singular Value Decomposition did not converge', comm) return end if if (any(abs(singv) < eps7)) then call set_error_fatal(error, 'kmesh_shell_from_file: Singular Value Decomposition has found a very small singular value', comm) return end if smat = 0.0_dp do loop_s = 1, kmesh_input%num_shells smat(loop_s, loop_s) = 1/singv(loop_s) end do bweight(1:kmesh_input%num_shells) = matmul(transpose(vmat), matmul(smat, matmul(transpose(umat), target))) if (print_output%iprint >= 2) then do loop_s = 1, kmesh_input%num_shells write (stdout, '(1x,a,I2,a,f12.7,5x,a8,36x,a)') '| Shell: ', loop_s, & ' w_b ', bweight(loop_s)*print_output%lenconfac**2, '('//trim(print_output%length_unit)//'^2)', '|' end do end if ! note: B1 condition is not tested here; test follows this function call if (print_output%timing_level > 1) call io_stopwatch_stop('kmesh: shell_from_file', timer) return 103 call set_error_input(error, 'Error: Problem (3) reading input file '//trim(seedname)//'.kshell', comm) return 230 call set_error_input(error, 'Error: Problem reading in kmesh_shell_from_file', comm) return end subroutine kmesh_shell_from_file !================================================ function internal_maxloc(dist, nsupcell) !================================================ !! A reproducible maxloc function !! so b-vectors come in the same !! order each time !================================================ use w90_constants, only: eps8 implicit none integer, intent(in) :: nsupcell real(kind=dp), intent(in) :: dist((2*nsupcell + 1)**3) !! Distances from the origin of the unit cells in the supercell. integer :: internal_maxloc integer :: guess(1), loop, counter integer :: list((2*nsupcell + 1)**3) list = 0 counter = 1 guess = maxloc(dist) list(1) = guess(1) ! look for any degenerate values do loop = 1, (2*nsupcell + 1)**3 if (loop == guess(1)) cycle if (abs(dist(loop) - dist(guess(1))) < eps8) then counter = counter + 1 list(counter) = loop end if end do ! and always return the lowest index internal_maxloc = minval(list(1:counter)) end function internal_maxloc !================================================ subroutine kmesh_bvectors_perm(bvec, bref, num_kpt, num_bvec, perm, invperm, revind, error, comm) !================================================ ! !! Obtain possible permutation in ordering of b-vectors at different kpoints ! !================================================ implicit none ! arguments real(kind=dp), intent(in) :: bvec(:, :, :) ! set of bvecs for each k, possibly permuted, size (3,num_bvec,num_kpt) real(kind=dp), intent(in) :: bref(:, :) ! reference vector ordering, size (3,num_bvec) integer, intent(in) :: num_kpt, num_bvec integer, intent(inout) :: perm(:, :) ! assumed allocated integer, intent(inout) :: invperm(:, :) ! assumed allocated integer, intent(inout) :: revind(:, :) type(w90_error_type), allocatable, intent(out) :: error type(w90_comm_type), intent(in) :: comm ! local variables real(kind=dp), parameter :: tol = 1d-7 ! this should not be smaller than the k-point precision in the .win file integer :: ik, n, m logical :: found, found2 do ik = 1, num_kpt do n = 1, num_bvec found = .false. found2 = .false. do m = 1, num_bvec if (all(abs(bvec(:, m, ik) - bref(:, n)) < tol)) then found = .true. perm(n, ik) = m invperm(m, ik) = n ! inverse mapping, used in postw90 cycle end if if (all(abs(bvec(:, m, ik) + bref(:, n)) < tol)) then found2 = .true. revind(n, ik) = m cycle end if end do if (.not. found .or. .not. found2) then call set_error_fatal(error, & 'Unable to identify bk-vector permutation (kmesh_bvectors_perm); consider k-point precision', & comm) return end if end do end do end subroutine kmesh_bvectors_perm end module w90_kmesh