mod_eigvects.f90 Source File


Source Code

!-----------------------------------------------------------------------------------------!
! Copyright (c) 2016 Peter Grünberg Institut, Forschungszentrum Jülich, Germany           !
! This file is part of kk-prime@juKKR and available as free software under the conditions !
! of the MIT license as expressed in the LICENSE file in more detail.                     !
!-----------------------------------------------------------------------------------------!


module mod_eigvects

implicit none

  private
  public :: orthgonalize_wavefunc, normalize_wavefunc, calc_norm_wavefunc, calc_proj_wavefunc,&
          & normeigv_new, compare_eigv, rewrite_eigv_atom,compare_eigv_memopt,normeigv_new_memopt



contains

  subroutine rewrite_eigv_atom(inc,nstates,rveig,rveig_atom)

    use type_inc
    implicit none

    type(inc_type), intent(in)  :: inc
    integer,        intent(in)  :: nstates
    double complex, intent(in)  :: rveig(inc%almso, nstates)
    double complex, intent(out) :: rveig_atom(inc%lmmaxso, inc%natypd, nstates)

    integer :: istate, ispin, iatom, lm1, lm1p, lm1s

    rveig_atom = (0d0,0d0)
    do istate=1,nstates
      do ispin=1,2
        do iatom=1,inc%natypd
          do lm1=1,inc%lmmax
            lm1p = (ispin-1)*inc%alm   + (iatom-1)*inc%lmmax + lm1
            lm1s = (ispin-1)*inc%lmmax + lm1
            rveig_atom(lm1s,iatom,istate) = rveig(lm1p,istate)
          end do!lm1
        end do!iatom
      end do!ispin
    end do!ient 

  end subroutine rewrite_eigv_atom



  subroutine compare_eigv(inc, LVref, RV2, LMout, uio)
    ! Compare an reference-eigenvector (LVref) with several other
    !   eigenvectors (contained in RV2) and find the one with the
    !   largest overlap (index LMout). However, only consider
    !   those eigenvectors LV2(:,i2) when take(i2)=.true..


    use type_inc
    use mod_mathtools, only: bubblesort

    implicit none

    type(inc_type), intent(in) :: inc
    double complex, intent(in) :: LVref(inc%almso), &
                                & RV2(inc%almso,inc%almso)
    integer, intent(out)       :: LMout
    integer, optional, intent(in) :: uio

    integer          :: lm2, sorted(inc%almso)
    double complex   :: cprojs(inc%almso)
    double precision :: projs(inc%almso), projmax

    !calculate projections on other eigenvectors
    cprojs = (0d0, 0d0)
    projs  = 0d0
    do lm2=1,inc%almso
        cprojs(lm2) = dot_product(LVref,RV2(:,lm2))
        projs(lm2)  = dble(cprojs(lm2))**2 + dimag(cprojs(lm2))**2
    end do!lm2


    !find corresponding eigenvector
    projmax = 0d0
    LMout =  0
    do lm2=1,inc%almso
      if(projs(lm2)>projmax) then
        projmax = projs(lm2)
        LMout   = lm2
      end if!proj==projmax
    end do!lm2


    if(present(uio)) then
      call bubblesort(inc%almso, projs, sorted)
      write(uio,"(5X,6(I8,16X))") sorted(inc%almso-5:inc%almso)
      write(uio,"(5X,6(8X,E16.6))") projs(sorted(inc%almso-5:inc%almso))
    end if!present(uio)


  end subroutine compare_eigv





  subroutine compare_eigv_memopt(inc, LVref, RV2, nb_ev, LMout, uio)
    ! Compare an reference-eigenvector (LVref) with several other
    !   eigenvectors (contained in RV2) and find the one with the
    !   largest overlap (index LMout). However, only consider
    !   those eigenvectors LV2(:,i2) when take(i2)=.true..


    use type_inc
    use mod_mathtools, only: bubblesort

    implicit none

    type(inc_type), intent(in) :: inc
    double complex, intent(in) :: LVref(inc%almso), &
                                & RV2(inc%almso,inc%neig)
    integer, intent(in)        :: nb_ev
    integer, intent(out)       :: LMout
    integer, optional, intent(in) :: uio

    integer          :: lm2, sorted(inc%neig)
    double complex   :: cprojs(inc%neig)
    double precision :: projs(inc%neig), projmax

    !calculate projections on other eigenvectors
    cprojs = (0d0, 0d0)
    projs  = 0d0
    do lm2=1,nb_ev
        cprojs(lm2) = dot_product(LVref,RV2(:,lm2))
        projs(lm2)  = dble(cprojs(lm2))**2 + dimag(cprojs(lm2))**2
    end do!lm2


    !find corresponding eigenvector
    projmax = 0d0
    LMout =  0
    do lm2=1,nb_ev
      if(projs(lm2)>projmax) then
        projmax = projs(lm2)
        LMout   = lm2
      end if!proj==projmax
    end do!lm2

    if(present(uio)) then
      call bubblesort(inc%neig, projs, sorted)
      write(uio,"(5X,6(I8,16X))") sorted(inc%neig-5:inc%neig)
      write(uio,"(5X,6(8X,E16.6))") projs(sorted(inc%neig-5:inc%neig))
    end if!present(uio)


  end subroutine compare_eigv_memopt





  subroutine orthgonalize_wavefunc(inc, rhod_norm, rveig_atom)
  ! Modifies two coefficient-vectors rveig_atom(:,:,1) and rveig(:,:,2) such that
  !   the corresponding wavefunctions |psi> = \sum_{LL'} R_{LL'} * Y_L * c_L'
  !   are orthogonal to each other.
  !                                put into this subroutine by B.Zimmermann
  !                                                       created: 05.09.12

  use type_inc
  implicit none

    !Arguments
    type(inc_type), intent(in)    :: inc
    double complex, intent(in)    :: rhod_norm(inc%lmmaxso, inc%lmmaxso, inc%natypd)
    double complex, intent(inout) :: rveig_atom(inc%lmmaxso, inc%natypd, 2)

    !Locals
    double complex :: norm(1), proj


      norm = (0d0, 0d0)
      proj = (0d0, 0d0)

      call calc_norm_wavefunc(inc, 1, rhod_norm, rveig_atom(:,:,1), norm)
      call calc_proj_wavefunc(inc, rhod_norm, rveig_atom, proj)

      !orthogonalize
      rveig_atom(:,:,2) = rveig_atom(:,:,2) - proj/norm(1) * rveig_atom(:,:,1)
       
  end subroutine orthgonalize_wavefunc





  subroutine normalize_wavefunc(inc, nstates, rhod_norm, rveig_atom)
  ! Modifies n coefficient-vectors rveig_atom(:,:,i) (i=1..n) such that
  !   the corresponding wavefunctions |psi> = \sum_{LL'} R_{LL'} * Y_L * c_L'
  !   are normalized to 1, i.e. <psi|psi> = <c|rhod_norm|c> 1
  !                                put into this subroutine by B.Zimmermann
  !                                                       created: 05.09.12

  use type_inc
  implicit none

    !Arguments
    type(inc_type), intent(in)    :: inc
    integer,        intent(in)    :: nstates
    double complex, intent(in)    :: rhod_norm(inc%lmmaxso, inc%lmmaxso, inc%natypd)
    double complex, intent(inout) :: rveig_atom(inc%lmmaxso, inc%natypd, nstates)

    !Locals
    integer :: iatom, istate
    double complex :: norm(nstates)

    ! calculate the norm
    call calc_norm_wavefunc(inc, nstates, rhod_norm, rveig_atom, norm)

    ! normalize wavefunctions
    do istate=1,nstates
      rveig_atom(:,:,istate) = rveig_atom(:,:,istate) / CDSQRT(norm(istate))
    end do!ient

  end subroutine normalize_wavefunc





  subroutine calc_norm_wavefunc(inc, nstates, rhod_norm, rveig_atom, norm, norm_atom)
  ! Calculate the norm of n wavefunctions, optionally also atomwise contribution.
  use type_inc
  implicit none

    !Arguments
    type(inc_type), intent(in)  :: inc
    integer,        intent(in)  :: nstates
    double complex, intent(in)  :: rhod_norm(inc%lmmaxso, inc%lmmaxso, inc%natypd)
    double complex, intent(in)  :: rveig_atom(inc%lmmaxso, inc%natypd, nstates)
    double complex, intent(out) :: norm(nstates)
    double complex, intent(out), optional :: norm_atom(inc%natypd,nstates)

    !Locals
    logical :: atomwise
    integer :: iatom, istate
    double complex :: ztmp, ckhelp(inc%lmmaxso)

    !Parameter
    double complex, parameter :: CONE=(1d0,0d0), CZERO=(0d0, 0d0)

    atomwise = present(norm_atom)

    ! calculate the norm
    norm = CZERO
    if(atomwise) norm_atom = CZERO
    do istate=1,nstates
      do iatom=1,inc%natypd
        ckhelp = CZERO
        ztmp   = CZERO
        call ZGEMM('N', 'N', inc%lmmaxso, 1, inc%lmmaxso, CONE, rhod_norm(:,:,iatom), inc%lmmaxso, rveig_atom(:,iatom,istate), inc%lmmaxso, CZERO, ckhelp, inc%lmmaxso)
        call ZGEMM('C','N', 1, 1, inc%lmmaxso,CONE, rveig_atom(:,iatom,istate), inc%lmmaxso, ckhelp, inc%lmmaxso, CZERO, ztmp, 1)
        norm(istate) = norm(istate) + ztmp
        if(atomwise) norm_atom(iatom,istate)= ztmp
      end do!iatom
    end do!istate

  end subroutine calc_norm_wavefunc





  subroutine calc_proj_wavefunc(inc, rhod_norm, rveig_atom, proj)
  ! calculate projection from wavefunction 2 onto wavefunction 1
  use type_inc
  implicit none

    !Arguments
    type(inc_type), intent(in)  :: inc
    double complex, intent(in)  :: rhod_norm(inc%lmmaxso, inc%lmmaxso, inc%natypd)
    double complex, intent(in)  :: rveig_atom(inc%lmmaxso, inc%natypd, 2)
    double complex, intent(out) :: proj

    !Locals
    integer        :: iatom
    double complex :: ckhelp(inc%lmmaxso), ztmp

    !Parameter
    double complex, parameter :: CONE=(1d0,0d0), CZERO=(0d0, 0d0)
 
    proj = CZERO
    do iatom=1,inc%natypd
      ztmp = CZERO
      ckhelp = CZERO

      call ZGEMM('N', 'N', inc%lmmaxso, 1, inc%lmmaxso, CONE, rhod_norm(:,:,iatom), inc%lmmaxso, rveig_atom(:,iatom,2), inc%lmmaxso, CZERO, ckhelp, inc%lmmaxso)
      call ZGEMM('C','N', 1, 1, inc%lmmaxso,CONE, rveig_atom(:,iatom,1), inc%lmmaxso, ckhelp, inc%lmmaxso, CZERO, ztmp, 1)
      proj = proj + ztmp
    end do!iatom

  end subroutine calc_proj_wavefunc




subroutine normeigv_new(almso, LV, RV)

  integer,        intent(in)    :: almso
  double complex, intent(inout) :: LV(almso,almso), RV(almso,almso)

  integer        :: lm1, lm2
  double complex :: snorm, norm, proj

  double complex, parameter :: cone=(1d0,0d0)

  !normalize correctly
  do lm2=1,almso
    snorm=sqrt(dot_product(LV(:,lm2), RV(:,lm2)))
    RV(:,lm2) = RV(:,lm2)/snorm
    LV(:,lm2) = LV(:,lm2)/conjg(snorm)
  end do!lm2

  !perform checks
  do lm1=1,almso
    norm = dot_product(LV(:,lm1),RV(:,lm1))
    if(abs(norm-CONE)>1d-3) write(*,*) "norm not 1, (lm, norm)=", lm1, norm
  end do

  do lm1=1,almso
    do lm2=lm1+1,almso
      proj = dot_product(LV(:,lm2),RV(:,lm1))
      if(abs(proj)>1d-4) write(*,*) "proj not 0, (lm1, lm2, proj)=", lm1, lm2, proj
    end do
  end do

end subroutine normeigv_new




subroutine normeigv_new_memopt(almso, nb_ev, LV, RV)

  integer,        intent(in)    :: almso, nb_ev
  double complex, intent(inout) :: LV(almso,nb_ev), RV(almso,nb_ev)

  integer        :: lm1, lm2
  double complex :: snorm, norm, proj

  double complex, parameter :: cone=(1d0,0d0)

  !normalize correctly
  do lm2=1,nb_ev
    snorm=sqrt(dot_product(LV(:,lm2), RV(:,lm2)))
    RV(:,lm2) = RV(:,lm2)/snorm
    LV(:,lm2) = LV(:,lm2)/conjg(snorm)
  end do!lm2

  !perform checks
  do lm1=1,nb_ev
    norm = dot_product(LV(:,lm1),RV(:,lm1))
    if(abs(norm-CONE)>1d-3) write(*,*) "norm not 1, (lm, norm)=", lm1, norm
  end do

  if(nb_ev>1) then ! with memory optimization number of eigen values can be <= 1
    do lm1=1,nb_ev
      do lm2=lm1+1,nb_ev
        proj = dot_product(LV(:,lm2),RV(:,lm1))
        if(abs(proj)>1d-4) write(*,*) "proj not 0, (lm1, lm2, proj)=", lm1, lm2, proj
      end do
    end do
  end if!almso>1

end subroutine normeigv_new_memopt




SUBROUTINE CHECKDEGENERACY(ALMSO, eps_degenerate, EIGW, ENT, ENTW)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Checks, if eigenvalues the eigenvalues contained !
! in 'W' are degenerate. The returned array 'ENT'  !
! contains the order of degeneracy and ENTW(LM1,:) !
! contains the LM-indices of the other degenerate  !
! eigenvalues, corresponding to the one with index !
! LM1.                                             !
!                    Put into the sbroutine by     !
!                       b.zimmermann, jan.2011     !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  IMPLICIT NONE

  INTEGER,          INTENT (IN)  :: ALMSO
  DOUBLE PRECISION, INTENT (IN)  :: eps_degenerate
  DOUBLE COMPLEX,   INTENT (IN)  :: EIGW(ALMSO)
  INTEGER,          INTENT (OUT) :: ENT(ALMSO), ENTW(ALMSO,ALMSO)

  INTEGER LM1, LM2, DONE(ALMSO), PNTR

  ENT(: )   = 0
  ENTW(:,:) = 0
  DONE(:)   = 0
  DO LM1=1,ALMSO
    IF (DONE(LM1)==0) THEN

      !Find degenerate LM's (and store in row of lowest LM)
      ENT(LM1)    = 1
      ENTW(1,LM1) = LM1
      DONE(LM1)   = 1
      PNTR        = 1
      DO WHILE (ENTW(PNTR,LM1)/=0)
        DO LM2=ENTW(PNTR,LM1)+1,ALMSO
          IF( (ABS(EIGW(ENTW(PNTR,LM1))-EIGW(LM2)) < eps_degenerate) .and. (DONE(LM2)==0) ) THEN
            ENT(LM1)=ENT(LM1)+1
            ENTW(ENT(LM1),LM1)=LM2
            DONE(LM2)=1
          END IF!eps_degenerate
        END DO!LM2
        PNTR = PNTR+1
      END DO!WHILE

      !Copy row from lowest LM to its degenerate partners
      DO LM2=2,ENT(LM1)
        ENTW(:,  ENTW(LM2,LM1)) = ENTW(:,LM1)
        ENTW(1,  ENTW(LM2,LM1)) = ENTW(LM2,LM1)
        ENTW(LM2,ENTW(LM2,LM1)) = LM1
        ENT(ENTW(LM2,LM1)) = ENT(LM1)
      END DO
    END IF!DONE
  END DO!LM1

!!TESTOUTPUT
!  DO LM1=1,ALMSO
!    write(*,*) LM1, ENT(LM1)
!  END DO
!  write(*,*) '---'
!  DO LM1=1,ALMSO
!    write(*,"((8I))") LM1, (ENTW(LM2,LM1), LM2=1,ENT(LM1))
!  END DO
!  STOP

END SUBROUTINE






end module mod_eigvects