sitesym.F90 Source File


This file depends on

sourcefile~~sitesym.f90~~EfferentGraph sourcefile~sitesym.f90 sitesym.F90 sourcefile~comms.f90 comms.F90 sourcefile~sitesym.f90->sourcefile~comms.f90 sourcefile~constants.f90 constants.F90 sourcefile~sitesym.f90->sourcefile~constants.f90 sourcefile~error.f90 error.F90 sourcefile~sitesym.f90->sourcefile~error.f90 sourcefile~utility.f90 utility.F90 sourcefile~sitesym.f90->sourcefile~utility.f90 sourcefile~wannier90_types.f90 wannier90_types.F90 sourcefile~sitesym.f90->sourcefile~wannier90_types.f90 sourcefile~comms.f90->sourcefile~constants.f90 sourcefile~error_base.f90 error_base.F90 sourcefile~comms.f90->sourcefile~error_base.f90 sourcefile~error.f90->sourcefile~comms.f90 sourcefile~error.f90->sourcefile~error_base.f90 sourcefile~utility.f90->sourcefile~comms.f90 sourcefile~utility.f90->sourcefile~constants.f90 sourcefile~utility.f90->sourcefile~error.f90 sourcefile~wannier90_types.f90->sourcefile~constants.f90

Files dependent on this one

sourcefile~~sitesym.f90~~AfferentGraph sourcefile~sitesym.f90 sitesym.F90 sourcefile~disentangle.f90 disentangle.F90 sourcefile~disentangle.f90->sourcefile~sitesym.f90 sourcefile~overlap.f90 overlap.F90 sourcefile~overlap.f90->sourcefile~sitesym.f90 sourcefile~wannier_prog.f90 wannier_prog.F90 sourcefile~wannier_prog.f90->sourcefile~sitesym.f90 sourcefile~library_extra.f90 library_extra.F90 sourcefile~wannier_prog.f90->sourcefile~library_extra.f90 sourcefile~library_interface.f90 library_interface.F90 sourcefile~wannier_prog.f90->sourcefile~library_interface.f90 sourcefile~wannierise.f90 wannierise.F90 sourcefile~wannierise.f90->sourcefile~sitesym.f90 sourcefile~library_extra.f90->sourcefile~overlap.f90 sourcefile~library_extra.f90->sourcefile~library_interface.f90 sourcefile~library_interface.f90->sourcefile~disentangle.f90 sourcefile~library_interface.f90->sourcefile~overlap.f90 sourcefile~library_interface.f90->sourcefile~wannierise.f90 sourcefile~c_interface.f90 c_interface.F90 sourcefile~c_interface.f90->sourcefile~library_interface.f90

Source Code

!-*- 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_sitesym:                                               !
! Reference:                                                 !
!    R. Sakuma, Symmetry-adapted Wannier functions           !
!    in the maximal localization procedure,                  !
!    Phys Rev B 87, 235109 (2013)                            !
!                                                            !
!------------------------------------------------------------!

module w90_sitesym

  !! Routines to impose the site symmetry during minimisation of spread

  use w90_constants, only: dp, cmplx_1, cmplx_0
  use w90_comms, only: w90_comm_type
  use w90_error, only: w90_error_type, set_error_alloc, set_error_dealloc, set_error_fatal, &
                       set_error_file

  implicit none

  private

  public  :: sitesym_dealloc
  public  :: sitesym_dis_extract_symmetry
  public  :: sitesym_read
  public  :: sitesym_replace_d_matrix_band
  public  :: sitesym_slim_d_matrix_band
  public  :: sitesym_symmetrize_gradient
  public  :: sitesym_symmetrize_rotation
  public  :: sitesym_symmetrize_u_matrix
  public  :: sitesym_symmetrize_zmatrix

contains

  !================================================!
  subroutine sitesym_slim_d_matrix_band(num_bands, num_kpts, sitesym, lwindow_in)
    !================================================!
    ! not called !

    use w90_wannier90_types, only: sitesym_type

    implicit none

    integer, intent(in) :: num_bands
    integer, intent(in) :: num_kpts
    logical, optional, intent(in) :: lwindow_in(num_bands, num_kpts)
    type(sitesym_type), intent(inout) :: sitesym

    integer :: ik, i, j, nb, ir
    integer :: nindx(num_bands)

    do ir = 1, sitesym%nkptirr
      ik = sitesym%ir2ik(ir)
      j = 0
      do i = 1, num_bands
        if (lwindow_in(i, ik)) then
          j = j + 1
          nindx(j) = i
        end if
      end do
      nb = j
      do j = 1, nb
        i = nindx(j)
        sitesym%d_matrix_band(1:nb, j, :, ir) = sitesym%d_matrix_band(nindx(1:nb), i, :, ir)
        if (nb .lt. num_bands) then
          sitesym%d_matrix_band(nb + 1:, j, :, ir) = 0
        end if
      end do
    end do

    return
  end subroutine sitesym_slim_d_matrix_band

  !================================================!
  subroutine sitesym_replace_d_matrix_band(sitesym, num_wann, error, comm)
    !================================================!

    use w90_wannier90_types, only: sitesym_type

    implicit none

    integer, intent(in) :: num_wann
    type(sitesym_type), intent(inout) :: sitesym
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    integer :: ierr

    deallocate (sitesym%d_matrix_band, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating sitesym%d_matrix_band in sitesym_replace_d_matrix_band', comm)
      return
    end if
    allocate (sitesym%d_matrix_band(num_wann, num_wann, sitesym%nsymmetry, sitesym%nkptirr), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating sitesym%d_matrix_band in sitesym_replace_d_matrix_band', comm)
      return
    end if

    sitesym%d_matrix_band = sitesym%d_matrix_wann

    return
  end subroutine sitesym_replace_d_matrix_band

  !================================================!
  subroutine sitesym_symmetrize_u_matrix(sitesym, umat, num_bands, ndim, num_kpts, num_wann, &
                                         stdout, error, comm, lwindow_in)
    !================================================!
    !
    ! calculate U(Rk)=d(R,k)*U(k)*D^{\dagger}(R,k) in the following two cases:
    !
    ! 1. "disentanglement" phase (present(lwindow))
    !    ndim=num_bands
    !
    ! 2. Minimization of Omega_{D+OD} (.not.present(lwindow))
    !    ndim=num_wann,  d=sym%d_matrix_band
    !
    !================================================!

    use w90_wannier90_types, only: sitesym_type

    implicit none

    ! arguments
    type(sitesym_type), intent(in) :: sitesym
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    integer, intent(in) :: num_bands
    integer, intent(in) :: stdout
    integer, intent(in) :: num_wann
    integer, intent(in) :: num_kpts
    integer, intent(in) :: ndim

    complex(kind=dp), intent(inout) :: umat(:, :, :) !(ndim, num_wann, num_kpts)

    logical, optional, intent(in) :: lwindow_in(:, :) !(num_bands, num_kpts)

    ! local variables
    integer :: ik, ir, isym, irk, n
    logical :: ldone(num_kpts)
    complex(kind=dp) :: cmat(ndim, num_wann)

    if (present(lwindow_in) .and. (ndim .ne. num_bands)) then
      call set_error_fatal(error, 'ndim!=num_bands', comm)
      return
    end if
    if (.not. present(lwindow_in)) then
      if (ndim .ne. num_wann) then
        call set_error_fatal(error, 'ndim!=num_wann', comm)
        return
      end if
    end if

    ldone = .false.
    do ir = 1, sitesym%nkptirr
      ik = sitesym%ir2ik(ir)
      ldone(ik) = .true.
      if (present(lwindow_in)) then
        n = count(lwindow_in(:, ik))
      else
        n = ndim
      end if
      if (present(lwindow_in)) then
        call symmetrize_ukirr(num_wann, num_bands, ir, ndim, umat(:, :, ik), sitesym, stdout, &
                              error, comm, n)
      else
        call symmetrize_ukirr(num_wann, num_bands, ir, ndim, umat(:, :, ik), sitesym, stdout, &
                              error, comm)
      end if
      if (allocated(error)) return

      do isym = 2, sitesym%nsymmetry
        irk = sitesym%kptsym(isym, ir)
        if (ldone(irk)) cycle
        ldone(irk) = .true.
        ! cmat = d(R,k) * U(k)
        call zgemm('N', 'N', n, num_wann, n, cmplx_1, &
                   sitesym%d_matrix_band(:, :, isym, ir), ndim, &
                   umat(:, :, ik), ndim, cmplx_0, cmat, ndim)

        ! umat(Rk) = cmat*D^{+}(R,k) = d(R,k) * U(k) * D^{+}(R,k)
        call zgemm('N', 'C', n, num_wann, num_wann, cmplx_1, cmat, ndim, &
                   sitesym%d_matrix_wann(:, :, isym, ir), num_wann, cmplx_0, umat(:, :, irk), ndim)
      end do
    end do
    if (any(.not. ldone)) then
      call set_error_fatal(error, 'error in sitesym_symmetrize_u_matrix', comm)
      return
    end if

    return
  end subroutine sitesym_symmetrize_u_matrix

  !================================================!
  subroutine sitesym_symmetrize_gradient(sitesym, grad, imode, num_kpts, num_wann, error, comm)
    !================================================!

    use w90_error, only: w90_error_type, set_error_fatal
    use w90_utility, only: utility_zgemm
    use w90_wannier90_types, only: sitesym_type

    implicit none

    ! arguments
    type(sitesym_type), intent(in) :: sitesym
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm
    integer, intent(in) :: imode, num_wann, num_kpts
    complex(kind=dp), intent(inout) :: grad(:, :, :) !(num_wann, num_wann, num_kpts)

    ! local variables
    integer :: ik, ir, isym, irk, ngk

    complex(kind=dp) :: grad_total(num_wann, num_wann)
    complex(kind=dp) :: cmat1(num_wann, num_wann)
    complex(kind=dp) :: cmat2(num_wann, num_wann)

    logical :: lfound(num_kpts)

    if (imode .eq. 1) then
      lfound = .false.
      do ir = 1, sitesym%nkptirr
        ik = sitesym%ir2ik(ir)
        grad_total = grad(:, :, ik)
        lfound(ik) = .true.
        do isym = 2, sitesym%nsymmetry
          irk = sitesym%kptsym(isym, ir)
          if (lfound(irk)) cycle
          lfound(irk) = .true.
          !
          ! cmat1 = D(R,k)^{+} G(Rk) D(R,k)
          ! cmat2 = D(R,k)^{\dagger} G(Rk)
          !
          call utility_zgemm(cmat2, sitesym%d_matrix_wann(:, :, isym, ir), 'C', grad(:, :, irk), 'N', num_wann)
          call utility_zgemm(cmat1, cmat2, 'N', sitesym%d_matrix_wann(:, :, isym, ir), 'N', num_wann)
          grad_total = grad_total + cmat1
        end do
        grad(:, :, ik) = grad_total
      end do
      do ik = 1, num_kpts
        if (sitesym%ir2ik(sitesym%ik2ir(ik)) .ne. ik) grad(:, :, ik) = 0
      end do
    elseif (imode .eq. 2) then
      ! JJ, 20 July 2022, note:
      ! previously the following algorithm was *also applied* after the above for "mode 1"
      ! changed such that two algorithms are mutually exclusive.
      ! old results (test case testw90_disentanglement_sawfs) require mode 1 followed by mode 2
      ! see call in wannierise's wann_domega() routine
      ! surely the two modes do the same thing??? if not, then replace elseif with endif as before --JJ

      !
      ! grad -> 1/N_{R'} \sum_{R'} D^{+}(R',k) grad D(R',k)
      ! where R' k = k
      !
      do ir = 1, sitesym%nkptirr
        ik = sitesym%ir2ik(ir)
        ngk = count(sitesym%kptsym(:, ir) .eq. ik)
        if (ngk .eq. 1) cycle
        grad_total = grad(:, :, ik)
        do isym = 2, sitesym%nsymmetry
          if (sitesym%kptsym(isym, ir) .ne. ik) cycle
          !
          ! calculate cmat1 = D^{+}(R,k) G(Rk) D(R,k)
          !
          ! step 1: cmat2 =  G(Rk) D(R,k)
          call utility_zgemm(cmat2, grad(:, :, ik), 'N', sitesym%d_matrix_wann(:, :, isym, ir), 'N', num_wann)
          ! step 2: cmat1 = D^{+}(R,k) * cmat2
          call utility_zgemm(cmat1, sitesym%d_matrix_wann(:, :, isym, ir), 'C', cmat2, 'N', num_wann)
          grad_total = grad_total + cmat1
        end do
        grad(:, :, ik) = grad_total/ngk
      end do
    else
      call set_error_fatal(error, 'unknown mode argument in sitesym_symmetrize_gradient', comm)
      return
    end if
    return
  end subroutine sitesym_symmetrize_gradient

  !================================================!
  subroutine sitesym_symmetrize_rotation(sitesym, urot, num_kpts, num_wann, error, comm)
    !================================================!
    use w90_utility, only: utility_zgemm
    use w90_wannier90_types, only: sitesym_type
    use w90_error, only: w90_error_type, set_error_fatal

    implicit none

    ! arguments
    type(sitesym_type), intent(in) :: sitesym
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    integer, intent(in) :: num_wann, num_kpts
    complex(kind=dp), intent(inout) :: urot(num_wann, num_wann, num_kpts)

    ! local variables
    integer :: ik, ir, isym, irk
    complex(kind=dp) :: cmat1(num_wann, num_wann)
    complex(kind=dp) :: cmat2(num_wann, num_wann)

    logical :: ldone(num_kpts)

    ldone = .false.
    do ir = 1, sitesym%nkptirr
      ik = sitesym%ir2ik(ir)
      ldone(ik) = .true.
      do isym = 2, sitesym%nsymmetry
        irk = sitesym%kptsym(isym, ir)
        if (irk .eq. ik) cycle
        if (ldone(irk)) cycle
        ldone(irk) = .true.
        ! cmat2 = UROT(k)*D(R,k)^{\dagger}
        call utility_zgemm(cmat2, urot(:, :, ik), 'N', &
                           sitesym%d_matrix_wann(:, :, isym, ir), 'C', num_wann)
        ! cmat1 = D(R,k)*cmat2
        call utility_zgemm(cmat1, sitesym%d_matrix_wann(:, :, isym, ir), 'N', &
                           cmat2, 'N', num_wann)
        urot(:, :, irk) = cmat1(:, :)
      end do
    end do
    if (any(.not. ldone)) then
      call set_error_fatal(error, 'error in sitesym_symmetrize_rotation', comm)
      return
    end if

    return
  end subroutine sitesym_symmetrize_rotation

  !================================================!
  subroutine sitesym_symmetrize_zmatrix(sitesym, czmat, num_bands, num_kpts, lwindow_in)
    !================================================!
    !
    !    Z(k) <- \sum_{R} d^{+}(R,k) Z(Rk) d(R,k)
    !
    !================================================!

    use w90_wannier90_types, only: sitesym_type
    implicit none

    ! arguments
    type(sitesym_type), intent(in) :: sitesym

    integer, intent(in) :: num_bands
    integer, intent(in) :: num_kpts

    complex(kind=dp), intent(inout) :: czmat(num_bands, num_bands, num_kpts)

    logical, intent(in) :: lwindow_in(num_bands, num_kpts)

    ! local variables
    logical :: lfound(num_kpts)
    integer :: ik, ir, isym, irk, nd
    complex(kind=dp) :: cztmp(num_bands, num_bands)
    complex(kind=dp) :: cmat1(num_bands, num_bands)
    complex(kind=dp) :: cmat2(num_bands, num_bands)

    lfound = .false.
    do ir = 1, sitesym%nkptirr
      ik = sitesym%ir2ik(ir)
      nd = count(lwindow_in(:, ik))
      lfound(ik) = .true.
      do isym = 2, sitesym%nsymmetry
        irk = sitesym%kptsym(isym, ir)
        if (lfound(irk)) cycle
        lfound(irk) = .true.
        ! cmat1 = Z(R,k)*d(R,k)
        call zgemm('N', 'N', nd, nd, nd, cmplx_1, czmat(:, :, irk), num_bands, &
                   sitesym%d_matrix_band(:, :, isym, ir), num_bands, cmplx_0, cmat1, num_bands)
        ! cmat2 = d^{+}(R,k) Z(R,k) d(R,k) = d^{+}(R,k) cmat1
        call zgemm('C', 'N', nd, nd, nd, cmplx_1, sitesym%d_matrix_band(:, :, isym, ir), &
                   num_bands, cmat1, num_bands, cmplx_0, cmat2, num_bands)
        czmat(:, :, ik) = czmat(:, :, ik) + cmat2(:, :)
      end do

      cztmp(:, :) = czmat(:, :, ik)
      do isym = 2, sitesym%nsymmetry
        irk = sitesym%kptsym(isym, ir)
        if (irk .ne. ik) cycle
        call zgemm('N', 'N', nd, nd, nd, cmplx_1, cztmp, num_bands, &
                   sitesym%d_matrix_band(:, :, isym, ir), num_bands, cmplx_0, cmat1, num_bands)
        ! cmat2 = d^{+}(R,k) Z(R,k) d(R,k) = d^{+}(R,k) cmat1
        call zgemm('C', 'N', nd, nd, nd, cmplx_1, sitesym%d_matrix_band(:, :, isym, ir), &
                   num_bands, cmat1, num_bands, cmplx_0, cmat2, num_bands)
        czmat(:, :, ik) = czmat(:, :, ik) + cmat2(:, :)
      end do
      czmat(:, :, ik) = czmat(:, :, ik)/count(sitesym%kptsym(:, ir) .eq. ik)
    end do

    return
  end subroutine sitesym_symmetrize_zmatrix

  !================================================!
  subroutine symmetrize_ukirr(num_wann, num_bands, ir, ndim, umat, &
                              sitesym, stdout, error, comm, n)
    !================================================!
    !
    !  calculate u~(k)=1/N_{R'} \sum_{R'} d^{+}(R',k) u(k) D(R',k)
    !  where R'k = k
    !  and orthonormalize it
    !
    !================================================!

    use w90_wannier90_types, only: sitesym_type
    use w90_error, only: w90_error_type, set_error_fatal, set_error_unconv
    implicit none

    ! arguments
    type(sitesym_type), intent(in) :: sitesym
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    integer, intent(in) :: ir, ndim
    integer, intent(in) :: num_bands
    integer, intent(in) :: num_wann
    integer, intent(in) :: stdout
    integer, optional, intent(in) :: n

    complex(kind=dp), intent(inout) :: umat(ndim, num_wann)

    ! local variables
    integer :: isym, ngk, i, iter, ntmp
    integer, parameter :: niter = 100
    real(kind=dp)    :: diff
    complex(kind=dp) :: usum(ndim, num_wann)
    complex(kind=dp) :: cmat_sub(ndim, num_wann)
    complex(kind=dp) :: cmat(ndim, num_wann)
    complex(kind=dp) :: cmat2(num_wann, num_wann)

    !write(stdout,"(a)") '-- symmetrize_ukirr --'
    if (present(n)) then
      if (ndim .ne. num_bands) then
        call set_error_fatal(error, 'ndim!=num_bands', comm)
        return
      end if
      ntmp = n
    else
      if (ndim .ne. num_wann) then
        call set_error_fatal(error, 'ndim!=num_wann', comm)
        return
      end if
      ntmp = ndim
    end if

    ngk = count(sitesym%kptsym(:, ir) .eq. sitesym%ir2ik(ir))
    if (ngk .eq. 1) then
      call orthogonalize_u(ndim, num_wann, umat, ntmp, error, comm)
      return
    end if

    do iter = 1, niter
      usum(:, :) = 0
      cmat2(:, :) = 0
      do i = 1, num_wann
        cmat2(i, i) = cmat2(i, i) + ngk
      end do
      do isym = 1, sitesym%nsymmetry
        if (sitesym%kptsym(isym, ir) .ne. sitesym%ir2ik(ir)) cycle
        !
        ! cmat = d^{+}(R,k) U(k) D(R,k)
        ! size of umat: umat(ndim,num_wann)
        !
        ! cmat_sub = U(k) D(R,k)
        call zgemm('N', 'N', ntmp, num_wann, num_wann, cmplx_1, umat, ndim, &
                   sitesym%d_matrix_wann(:, :, isym, ir), num_wann, cmplx_0, cmat_sub, ndim)
        ! cmat = d^{+}(R,k) * cmat_sub
        call zgemm('C', 'N', ntmp, num_wann, ntmp, cmplx_1, sitesym%d_matrix_band(:, :, isym, ir), &
                   ndim, cmat_sub, ndim, cmplx_0, cmat, ndim)

        usum(:, :) = usum(:, :) + cmat(:, :)
        ! check
        cmat2(:, :) = cmat2(:, :) - &
                      matmul(conjg(transpose(umat(:ntmp, :))), cmat(:ntmp, :))
      end do ! isym
      diff = sum(abs(cmat2))
      if (diff .lt. sitesym%symmetrize_eps) exit
      if (iter .eq. niter) then
        write (stdout, "(a)") 'Error in symmetrize_u: not converged'
        write (stdout, "(a)") 'Either eps is too small or specified irreps is not'
        write (stdout, "(a)") '  compatible with the bands'
        write (stdout, "(a,2e20.10)") 'diff,eps=', diff, sitesym%symmetrize_eps
        call set_error_unconv(error, 'symmetrize_ukirr: not converged', comm)
        return
      end if
      usum = usum/ngk
      call orthogonalize_u(ndim, num_wann, usum, ntmp, error, comm)
      if (allocated(error)) return

      umat(:, :) = usum
    end do ! iter

    return
  end subroutine symmetrize_ukirr

  !================================================!
  subroutine orthogonalize_u(ndim, m, u, n, error, comm)
    !================================================!

    implicit none

    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm
    integer, intent(in) :: ndim, m
    complex(kind=dp), intent(inout) :: u(ndim, m)
    integer, intent(in) :: n

    complex(kind=dp), allocatable :: smat(:, :), evecl(:, :), evecr(:, :)
    complex(kind=dp), allocatable :: WORK(:)
    real(kind=dp), allocatable :: eig(:), RWORK(:)
    integer :: INFO, i, j, l, LWORK, ierr

    if (n .lt. m) then
      call set_error_fatal(error, 'n<m', comm)
      return
    end if
    allocate (smat(n, m)); smat(1:n, 1:m) = u(1:n, 1:m)
    allocate (evecl(n, n), evecr(m, m))
    allocate (eig(min(m, n)))

    allocate (RWORK(5*min(n, m)))
    LWORK = 2*min(m, n) + max(m, n)
    allocate (WORK(LWORK))

    ! Singular-value decomposition
    call zgesvd('A', 'A', n, m, smat, n, eig, evecl, n, evecr, m, WORK, LWORK, RWORK, INFO)
    if (info .ne. 0) then
      call set_error_fatal(error, ' ERROR: IN ZGESVD IN orthogonalize_u', comm)
      return
    end if
    deallocate (smat, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating smat in orthogonalize_u', comm)
      return
    end if
    deallocate (eig, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating eig in orthogonalize_u', comm)
      return
    end if
    deallocate (WORK, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating WORK in orthogonalize_u', comm)
      return
    end if
    deallocate (RWORK, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating RWORK in orthogonalize_u', comm)
      return
    end if

    ! u_matrix is the initial guess for the unitary rotation of the
    ! basis states given by the subroutine extract
    u = 0
    do j = 1, m
    do l = 1, m
      do i = 1, n
        u(i, j) = u(i, j) + evecl(i, l)*evecr(l, j)
      end do
    end do
    end do

    deallocate (evecl, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating evecl in orthogonalize_u', comm)
      return
    end if
    deallocate (evecr, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating evecr in orthogonalize_u', comm)
      return
    end if
  end subroutine orthogonalize_u

  !================================================!
  subroutine sitesym_dis_extract_symmetry(sitesym, lambda, umat, zmat, ik, n, num_bands, num_wann, &
                                          stdout, error, comm)
    !================================================!
    !
    !   minimize Omega_I by steepest descendent
    !
    !   delta U_{mu I}(k) = Z_{mu mu'}*U_{mu' I}(k)
    !                        - \sum_{J} lambda_{JI} U_{mu J}(k)
    !   lambda_{JI}=U^{*}_{mu J} Z_{mu mu'} U_{mu' I}
    !
    !================================================!

    use w90_wannier90_types, only: sitesym_type

    implicit none

    ! arguments
    type(sitesym_type), intent(in) :: sitesym
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    integer, intent(in) :: num_bands
    integer, intent(in) :: stdout
    integer, intent(in) :: num_wann
    integer, intent(in) :: ik, n

    complex(kind=dp), intent(in) :: zmat(:, :) !(num_bands, num_bands)
    complex(kind=dp), intent(out) :: lambda(:, :) !(num_wann, num_wann)
    complex(kind=dp), intent(inout) :: umat(:, :) !(num_bands, num_wann)

    ! local variables
    complex(kind=dp), allocatable :: umatnew(:, :) !(num_bands, num_wann)
    complex(kind=dp), allocatable :: ZU(:, :) !(num_bands, num_wann)
    complex(kind=dp), allocatable :: deltaU(:, :) !(num_bands, num_wann)
    complex(kind=dp), allocatable :: carr(:) !(num_bands)
    integer :: i, m, INFO, IFAIL(2), IWORK(5*2)
    complex(kind=dp) :: HP(3), SP(3), V(2, 2), CWORK(2*2)
    real(kind=dp)    :: W(2), RWORK(7*2), sp3
    integer :: iter, ierr
    integer, parameter :: niter = 50

    allocate (umatnew(num_bands, num_wann), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating umatnew in sitesym_dis_extract_symmetry', comm)
      return
    end if
    allocate (ZU(num_bands, num_wann), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating ZU in sitesym_dis_extract_symmetry', comm)
      return
    end if
    allocate (deltaU(num_bands, num_wann), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating deltaU in sitesym_dis_extract_symmetry', comm)
      return
    end if
    allocate (carr(num_bands), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating carr in sitesym_dis_extract_symmetry', comm)
      return
    end if

    do iter = 1, niter
      !  Z*U
      call zgemm('N', 'N', n, num_wann, n, cmplx_1, zmat, num_bands, umat, num_bands, cmplx_0, ZU, &
                 num_bands)
      ! lambda = U^{+}*Z*U
      call zgemm('C', 'N', num_wann, num_wann, n, cmplx_1, umat, num_bands, ZU, num_bands, &
                 cmplx_0, lambda, num_wann)

      deltaU(:, :) = ZU(:, :) - matmul(umat, lambda)
      if (sum(abs(deltaU(:n, :))) .lt. 1e-10) return

      ! band-by-band minimization
      do i = 1, num_wann
        ! diagonalize 2x2 matrix
        HP(1) = real(dot_product(umat(1:n, i), ZU(1:n, i)), kind=dp)
        HP(2) = dot_product(ZU(1:n, i), deltaU(1:n, i)) ! (1,2) matrix element
        carr(1:n) = matmul(zmat(1:n, 1:n), deltaU(1:n, i))
        HP(3) = real(dot_product(deltaU(1:n, i), carr(1:n)), kind=dp) ! (2,2)

        SP(1) = real(dot_product(umat(1:n, i), umat(1:n, i)), kind=dp)
        SP(2) = dot_product(umat(1:n, i), deltaU(1:n, i))
        SP(3) = real(dot_product(deltaU(1:n, i), deltaU(1:n, i)), kind=dp)

        sp3 = real(SP(3), kind=dp)
        if (abs(sp3) .lt. 1e-10) then
          umatnew(:, i) = umat(:, i)
          cycle
        end if
        call ZHPGVX(1, 'V', 'A', 'U', 2, HP, SP, 0.0_dp, 0.0_dp, 0, 0, &
                    -1.0_dp, m, W, V, 2, CWORK, RWORK, IWORK, IFAIL, INFO)
        if (INFO .ne. 0) then
          write (stdout, *) 'error in sitesym_dis_extract_symmetry: INFO=', INFO
          if (INFO .gt. 0) then
            if (INFO .le. 2) then
              write (stdout, *) INFO, ' eigenvectors failed to converge'
              write (stdout, *) IFAIL(1:INFO)
            else
              write (stdout, *) ' S is not positive definite'
              write (stdout, *) 'sp3=', sp3
            end if
            call set_error_fatal(error, 'error at sitesym_dis_extract_symmetry', comm)
            return
          end if
        end if
        ! choose the larger eigenstate
        umatnew(:, i) = V(1, 2)*umat(:, i) + V(2, 2)*deltaU(:, i)
      end do ! i
      call symmetrize_ukirr(num_wann, num_bands, sitesym%ik2ir(ik), num_bands, umatnew, sitesym, &
                            stdout, error, comm, n)
      if (allocated(error)) return

      umat(:, :) = umatnew(:, :)
    end do ! iter

    deallocate (umatnew, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating umatnew in sitesym_dis_extract_symmetry', comm)
      return
    end if
    deallocate (ZU, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating ZU in sitesym_dis_extract_symmetry', comm)
      return
    end if
    deallocate (deltaU, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating deltaU in sitesym_dis_extract_symmetry', comm)
      return
    end if
    deallocate (carr, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating carr in sitesym_dis_extract_symmetry', comm)
      return
    end if

    return
  end subroutine sitesym_dis_extract_symmetry

  !================================================!
  subroutine sitesym_read(sitesym, num_bands, num_kpts, num_wann, seedname, error, comm)
    !================================================!

    use w90_wannier90_types, only: sitesym_type

    implicit none

    ! arguments
    type(sitesym_type), intent(inout) :: sitesym
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    integer, intent(in) :: num_bands
    integer, intent(in) :: num_wann
    integer, intent(in) :: num_kpts
    character(len=*), intent(in)  :: seedname

    ! local variables
    integer :: iu, ibnum, iknum, ierr

    open (newunit=iu, file=trim(seedname)//".dmn", form='formatted', status='old', action='read')
    read (iu, *)
    read (iu, *) ibnum, sitesym%nsymmetry, sitesym%nkptirr, iknum
    if (ibnum .ne. num_bands) then
      call set_error_file(error, "Error: Number of bands is not correct (sitesym_read)", comm)
      return
    end if
    if (iknum .ne. num_kpts) then
      call set_error_file(error, "Error: Number of k-points is not correct (sitesym_read)", comm)
      return
    end if

    allocate (sitesym%ik2ir(num_kpts), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating sitesym%ik2ir in sitesym_read', comm)
      return
    end if
    allocate (sitesym%ir2ik(sitesym%nkptirr), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating sitesym%ir2ik in sitesym_read', comm)
      return
    end if
    allocate (sitesym%kptsym(sitesym%nsymmetry, sitesym%nkptirr), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating sitesym%kptsym in sitesym_read', comm)
      return
    end if
    allocate (sitesym%d_matrix_band(num_bands, num_bands, sitesym%nsymmetry, sitesym%nkptirr), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating sitesym%d_matrix_band in sitesym_read', comm)
      return
    end if
    allocate (sitesym%d_matrix_wann(num_wann, num_wann, sitesym%nsymmetry, sitesym%nkptirr), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating sitesym%d_matrix_wann in sitesym_read', comm)
      return
    end if

    read (iu, *) sitesym%ik2ir
    read (iu, *) sitesym%ir2ik
    read (iu, *) sitesym%kptsym
    read (iu, *) sitesym%d_matrix_wann
    read (iu, *) sitesym%d_matrix_band
    close (iu)

    return
  end subroutine sitesym_read

  !================================================!
  subroutine sitesym_dealloc(sitesym, error, comm)
    !================================================!

    use w90_wannier90_types, only: sitesym_type

    implicit none

    type(sitesym_type), intent(inout) :: sitesym
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm
    integer :: ierr

    deallocate (sitesym%ik2ir, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating sitesym%ik2ir in sitesym_dealloc', comm)
      return
    end if
    deallocate (sitesym%ir2ik, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating sitesym%ir2ik in sitesym_dealloc', comm)
      return
    end if
    deallocate (sitesym%kptsym, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating sitesym%kptsym in sitesym_dealloc', comm)
      return
    end if
    deallocate (sitesym%d_matrix_band, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating sitesym%d_matrix_band in sitesym_dealloc', comm)
      return
    end if
    deallocate (sitesym%d_matrix_wann, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating sitesym%d_matrix_wann in sitesym_dealloc', comm)
      return
    end if

    return
  end subroutine sitesym_dealloc

end module w90_sitesym