mod_spintools.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_spintools

  implicit none

  private
  public :: spin_expectationvalue, spin_crossterm, Spinrot_AlphaBeta, rotate_wavefunction, &
          & torq_expectationvalue, spinflux_expectationvalue, alpha_expectationvalue,      &
          & Spinrot_AlphaBeta_Rashba

contains





  subroutine rotate_wavefunction(lmmaxso, natypd, alpha, beta, rveig_atom_inp, rveig_atom_out)

  implicit none

    ! Arguments
    integer,          intent(in)  :: lmmaxso, natypd
    double precision, intent(in)  :: alpha, beta
    double complex,   intent(in)  :: rveig_atom_inp(lmmaxso, natypd, 2)
    double complex,   intent(out) :: rveig_atom_out(lmmaxso, natypd, 2)

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

    double precision :: alpha2, salph, calph
    double complex   :: efac

    alpha2 = alpha*0.5d0
    salph  = dsin(alpha2)
    calph  = dcos(alpha2)
    efac   = dcos(beta) + CI*sin(beta)

    rveig_atom_out(:,:,1) =  calph * rveig_atom_inp(:,:,1) + salph*efac * rveig_atom_inp(:,:,2)
    rveig_atom_out(:,:,2) = -salph * rveig_atom_inp(:,:,1) + calph*efac * rveig_atom_inp(:,:,2)

  end subroutine rotate_wavefunction





  subroutine spin_expectationvalue(inc, nstates, rhod, rveig_atom, Spin_tot, Spin_atom)
  ! Calculates the spin-expectation value of a state |psi_n> and stores
  !   them into Spin_tot(ixyz)
  !   The corresponding coefficientvector must be already properly normalized.
  !                                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(inc%lmmaxso,inc%lmmaxso,inc%natypd,4),&
                                 & rveig_atom(inc%lmmaxso,inc%natypd,nstates)
    double complex, intent(out) :: Spin_tot(3, nstates)
    double complex, intent(out), optional :: Spin_atom(3,inc%natypd,nstates)

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

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

    Spin_tot  = CZERO
    ! calculate Spin expectation value with wavefunctions as stored in rveig_atom
    do istate=1,nstates
      do ixyz=1,3

        ztmp   = CZERO
        do iatom=1,inc%natypd
          ckhelp = CZERO
          call ZGEMM('N', 'N', inc%lmmaxso, 1, inc%lmmaxso, CONE, rhod(:,:,iatom,ixyz+1), 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(iatom), 1)
        end do!iatom

        Spin_tot(ixyz,istate) = sum(ztmp)
        if(present(Spin_atom)) Spin_atom(ixyz,:,istate) = ztmp
      end do!ixyz
    end do!istate

  end subroutine spin_expectationvalue




  subroutine torq_expectationvalue(inc, nstates, torq, rveig_atom, Torq_tot, Torq_atom)
  ! Calculates the torque-expectation value of a state |psi_n> and stores
  !   them into Torq_tot(ixyz)
  !   The corresponding coefficientvector must be already properly normalized.
  !                   adapted from spin_expectationvalue by G. Geranton
  !                                                       created: 02.10.14

  use type_inc
  implicit none

    ! Arguments
    type(inc_type), intent(in)  :: inc
    integer,        intent(in)  :: nstates
    double complex, intent(in)  :: torq(inc%lmmaxso,inc%lmmaxso,inc%natypd,3),&
                                 & rveig_atom(inc%lmmaxso,inc%natypd,nstates)
    double complex, intent(out) :: Torq_tot(3,nstates)
    double complex, intent(out), optional :: Torq_atom(3,inc%natypd,nstates)

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

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

    Torq_tot  = CZERO
    ! calculate Torq expectation value with wavefunctions as stored in rveig_atom
    do istate=1,nstates
      do ixyz=1,3

        ztmp   = CZERO
        do iatom=1,inc%natypd
          ckhelp = CZERO
          call ZGEMM('N', 'N', inc%lmmaxso, 1, inc%lmmaxso, CONE, torq(:,:,iatom,ixyz), 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(iatom), 1)
        end do!iatom

        Torq_tot(ixyz,istate) = sum(ztmp)
        if(present(Torq_atom)) Torq_atom(ixyz,:,istate) = ztmp
      end do!ixyz
    end do!istate


  end subroutine torq_expectationvalue

  subroutine spinflux_expectationvalue(inc, nstates, spinflux, rveig_atom, Spinflux_atom)
  ! Calculates the spinflux-expectation value of a state |psi_n> for each atom
  ! and stores them into Spinflux_atom(ixyz)
  !   The corresponding coefficientvector must be already properly normalized.
  !                   adapted from torq_expectationvalue by G. Geranton
  !                                                       created: 29.06.15

  use type_inc
  implicit none

    ! Arguments
    type(inc_type), intent(in)  :: inc
    integer,        intent(in)  :: nstates
    double complex, intent(in)  :: spinflux(inc%lmmaxso,inc%lmmaxso,inc%natypd,3),&
                                 & rveig_atom(inc%lmmaxso,inc%natypd,nstates)
    double complex, intent(out), optional :: Spinflux_atom(3,inc%natypd,nstates)

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

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

    ! calculate spinflux expectation value with wavefunctions as stored in rveig_atom
    do istate=1,nstates
      do ixyz=1,3

        ztmp   = CZERO
        do iatom=1,inc%natypd
          ckhelp = CZERO
          call ZGEMM('N', 'N', inc%lmmaxso, 1, inc%lmmaxso, CONE, spinflux(:,:,iatom,ixyz), 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(iatom), 1)
        end do!iatom

        Spinflux_atom(ixyz,:,istate) = ztmp
      end do!ixyz
    end do!istate


  end subroutine spinflux_expectationvalue

  subroutine alpha_expectationvalue(inc, nstates, alpha, rveig_atom, Alpha_tot)

    use type_inc
    implicit none

    ! Arguments
    type(inc_type), intent(in)  :: inc
    integer,        intent(in)  :: nstates
    double complex, intent(in)  :: alpha(inc%lmmaxso,inc%lmmaxso,inc%natypd,3),&
                                 & rveig_atom(inc%lmmaxso,inc%natypd,nstates)
    double complex, intent(out) :: Alpha_tot(3,nstates)

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

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

    Alpha_tot  = CZERO
    ! calculate Torq expectation value with wavefunctions as stored in rveig_atom
    do istate=1,nstates
      do ixyz=1,3

        ztmp   = CZERO
        do iatom=1,inc%natypd
          ckhelp = CZERO
          call ZGEMM('N', 'N', inc%lmmaxso, 1, inc%lmmaxso, CONE, alpha(:,:,iatom,ixyz), 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(iatom), 1)
        end do!iatom

        Alpha_tot(ixyz,istate) = sum(ztmp)
      end do!ixyz
    end do!istate


  end subroutine alpha_expectationvalue


  subroutine Spinrot_AlphaBeta(lincombtype, nvec, Spin_ini, Scross, alpha, beta, Spin_estimated, uio)

    use mod_mathtools, only: pi
    implicit none

    ! Arguments
    integer,          intent(in)  :: lincombtype
    double precision, intent(in)  :: nvec(3)
    double complex,   intent(in)  :: Spin_ini(3), Scross(3)
    double precision, intent(out) :: alpha, beta, Spin_estimated
    integer, intent(in), optional :: uio

    ! Locals
    integer          :: icomb, combtake
    double precision :: beta_tmp(4), alphacal_tmp(4), alpha_tmp(4), gvec1(3), gvec2(3), maxv, absv
    double complex   :: Spin_unrot_n, S_12_n, S_unrot_g1, S_unrot_g2, S_12_g1, S_12_g2, xi_help, Spin_rot_test(4)

    alpha= 0d0
    beta = 0d0
    beta_tmp=0d0
    alphacal_tmp=0d0
    alpha_tmp=0d0

    ! project spin onto chosen direction
    Spin_unrot_n = sum(nvec(:)*Spin_ini(:))
    S_12_n       = sum(nvec(:)*Scross(:))

    ! chose condition for linear combination of |psi1> and |psi2>
    if(lincombtype==1) then!MAXIMIZE spin in direction of SQA

      ! calculate combinations between (alpha, alpha+pi) and (beta, beta+pi)
      beta_tmp(1) = -datan2( dimag(S_12_n),  dble(S_12_n) )
      beta_tmp(2) = beta_tmp(1)
      beta_tmp(3) = beta_tmp(1) + pi
      beta_tmp(4) = beta_tmp(1) + pi

      alphacal_tmp = dcos(beta_tmp)*dble(S_12_n) - dsin(beta_tmp)*dimag(S_12_n)

      alpha_tmp(1) = datan2(alphacal_tmp(1), dble(Spin_unrot_n))
      alpha_tmp(2) = alpha_tmp(1) + pi
      alpha_tmp(3) = datan2(alphacal_tmp(2), dble(Spin_unrot_n))
      alpha_tmp(4) = alpha_tmp(3) + pi

    elseif(lincombtype==2) then!VANISHING spin in directions perpendicular to SQA

      !construct perpendicular directions to nvec
      gvec1 = 0d0
      gvec2 = 0d0
      if(abs(nvec(1))>1d-04 .or. abs(nvec(3))>1d-04) then
        gvec1(1) =  nvec(3)
        gvec1(2) =  0d0
        gvec1(3) = -nvec(1)
      else
        gvec1(1) =  0d0
        gvec1(2) = -nvec(3)
        gvec1(3) =  nvec(2)
      end if
      gvec1 = gvec1 / dsqrt( gvec1(1)**2 + gvec1(2)**2 + gvec1(3)**2 )
      gvec2(1) = nvec(2)*gvec1(3) - nvec(3)*gvec1(2)
      gvec2(2) = nvec(3)*gvec1(1) - nvec(1)*gvec1(3)
      gvec2(3) = nvec(1)*gvec1(2) - nvec(2)*gvec1(1)
      !write(*,*) 'check norm gvec1:', dsqrt( gvec1(1)**2 + gvec1(2)**2 + gvec1(3)**2 )
      !write(*,*) 'check norm gvec2:', dsqrt( gvec2(1)**2 + gvec2(2)**2 + gvec2(3)**2 )

      S_12_g1 = gvec1(1)*Scross(1) + gvec1(2)*Scross(2) + gvec1(3)*Scross(3)
      S_12_g2 = gvec2(1)*Scross(1) + gvec2(2)*Scross(2) + gvec2(3)*Scross(3)

      S_unrot_g1 = sum(gvec1(:)*Spin_ini(:))
      S_unrot_g2 = sum(gvec2(:)*Spin_ini(:))

      xi_help = -(S_unrot_g2*conjg(S_12_g1) - S_unrot_g1*conjg(S_12_g2))/(S_unrot_g2*S_12_g1 - S_unrot_g1*S_12_g2)

      ! calculate combinations between (alpha, alpha+pi) and (beta, beta+pi)
      beta_tmp(1) = 0.5d0*datan2( dimag(xi_help),  dble(xi_help) )
      beta_tmp(2) = beta_tmp(1)
      beta_tmp(3) = beta_tmp(1) + pi
      beta_tmp(4) = beta_tmp(1) + pi

      alphacal_tmp = -dcos(beta_tmp)*dble(S_12_g1) + dsin(beta_tmp)*dimag(S_12_g1)
      !write(*,*) 'imag(S_unrot_g1)=0? : ', dimag(S_unrot_g1)

      alpha_tmp(1) = datan2(dble(S_unrot_g1), alphacal_tmp(1))
      alpha_tmp(2) = alpha_tmp(1) + pi
      alpha_tmp(3) = datan2(dble(S_unrot_g1), alphacal_tmp(2))
      alpha_tmp(4) = alpha_tmp(3) + pi

    else
       stop 'no rule to form linear combination'
    end if 

    !Calculate the expected spin-expectation with (alpha_tmp, beta_tmp)
    Spin_rot_test = dcos(alpha_tmp)*Spin_unrot_n + dsin(alpha_tmp)*(dcos(beta_tmp)*dble(S_12_n) - dsin(beta_tmp)*dimag(S_12_n))

    ! pick (alpha, beta) maximizing the absolut value of the spin in direction n
    maxv = 0d0
    do icomb=1,4
      absv = dble(Spin_rot_test(icomb))
      if(absv>maxv) then
        maxv  = absv
        combtake=icomb
      end if!absv>maxv
    end do!icomb

    alpha = alpha_tmp(combtake)
    beta  = beta_tmp(combtake)
    Spin_estimated = Spin_rot_test(combtake)

    if(present(uio)) then
      write(uio,"(6X,A)") 'Combinations of alpha/beta yield:'
      do icomb=1,4
        write(uio,"(8X,A,I0,A,2ES25.16,A)") 'icomb=', icomb, ', Spin_rot_test=(', Spin_rot_test(icomb), ')'
      end do
      write(uio,"(8X,A,I0)") 'Take combination #', combtake
    end if!present(uio)

  end subroutine Spinrot_AlphaBeta


  subroutine Spinrot_AlphaBeta_Rashba(Spin_ini_selected, Scross_selected, alpha, beta)
    
    use mod_mathtools, only: pi
    implicit none

    ! Arguments
    double complex,   intent(in)  :: Spin_ini_selected(3,2), Scross_selected(3)
    double precision, intent(out) :: alpha, beta

    ! Locals
    integer          :: nalpha, nbeta, niter, ialpha, ibeta, iiter, k_candidate, ixyz, k_take, i_kcand, ierr
    double precision :: alpha_tmp, beta_tmp, para_a1, para_b1, para_c1, para_a2, para_b2, para_c2, para_d2, para_e2, para_f2, delta, re_eibS12, im_eibS12, alpha_bounds(2), alpha0, sinalpha, cosalpha, d_alpha, ds_dalpha0, maxspin, maxspin_k, sumS(2), s_ab, s_ab2
    double precision, allocatable :: alpha_get(:), beta_get(:),ds_dalpha(:)
    !Parameter
    double complex, parameter :: CONE=(1d0, 0d0), CZERO=(0d0, 0d0), CI=(0d0, 1d0)



    ! hard coded parameters for loops to find alpha and beta
    nalpha = 1000
    nbeta  = 1000
    niter  = 1

    allocate(ds_dalpha(0:nalpha+1), STAT=ierr)
    if(ierr/=0) stop 'Problem allocating ds_dalpha'
    allocate(alpha_get(0:((nalpha+1)*(nbeta+1))), STAT=ierr)
    if(ierr/=0) stop 'Problem allocating alpha_get'
    allocate(beta_get(0:((nalpha+1)*(nbeta+1))), STAT=ierr)
    if(ierr/=0) stop 'Problem allocating beta_get'
    alpha_get = 0d0 
    beta_get  = 0d0
    k_candidate = 0
    !write(*,*) 'start beta loop'

    do ibeta=1,nbeta+1
       beta_tmp  = 2d0*pi*(ibeta-1)/nbeta
       re_eibS12 = 0d0
       im_eibS12 = 0d0
       para_a1   = 0d0
       para_b1   = 0d0
       para_c1   = 0d0
       para_a2   = 0d0
       para_b2   = 0d0
       para_c2   = 0d0
       para_d2   = 0d0
       para_e2   = 0d0
       para_f2   = 0d0
       do ixyz=1,3
          re_eibS12 = real(exp(CI*beta_tmp)*Scross_selected(ixyz))
          im_eibS12 = aimag(exp(CI*beta_tmp)*Scross_selected(ixyz))
          para_a1   = para_a1+real(Spin_ini_selected(ixyz,2))*im_eibS12
          para_b1   = para_b1+re_eibS12*im_eibS12
          para_c1   = para_c1+real(Spin_ini_selected(ixyz,1))*im_eibS12
          para_a2   = para_a2+real(Spin_ini_selected(ixyz,2))*re_eibS12
          para_b2   = para_b2+real(Spin_ini_selected(ixyz,2))**2
          para_c2   = para_c2+re_eibS12**2
          para_d2   = para_d2+real(Spin_ini_selected(ixyz,1))*real(Spin_ini_selected(ixyz,2))
          para_e2   = para_e2+real(Spin_ini_selected(ixyz,1))**2
          para_f2   = para_f2+real(Spin_ini_selected(ixyz,1))*re_eibS12
       enddo !ixyz=1,3
       ! Now paramters are set, check if alpha can be found
       delta = para_b1**2-para_a1*para_c1
       if (delta.ge.0d0)then
          alpha_tmp = 0d0
          alpha_bounds(1) = 0
          alpha_bounds(2) = 2*pi
          do iiter=1,niter
             ds_dalpha = czero
                !write(*,*) 'start alpha loop',ibeta
             do ialpha=1,nalpha+1
                d_alpha = (alpha_bounds(2)-alpha_bounds(1))/nalpha
                alpha_tmp = alpha_bounds(1)+d_alpha*(ialpha-1)
                sinalpha = sin(alpha_tmp/2d0)
                cosalpha = cos(alpha_tmp/2d0)
                ds_dalpha(ialpha) = -2d0*cosalpha**3*sinalpha*para_e2+2d0*sinalpha**3*cosalpha*para_b2+2d0*cosalpha**3*sinalpha*para_d2-2d0*sinalpha**3*cosalpha*para_d2+4d0*cosalpha**3*sinalpha*para_c2-4d0*sinalpha**3*cosalpha*para_c2+2d0*cosalpha**4*para_f2-6d0*cosalpha**2*sinalpha**2*para_f2+6d0*sinalpha**2*cosalpha**2*para_a2-2d0*sinalpha**4*para_a2
                ! check if ds_dalpha has sign change
                if ((ds_dalpha(ialpha)*ds_dalpha(ialpha-1)).lt.0d0)then
                   alpha0 = alpha_tmp-d_alpha-(ds_dalpha(ialpha-1))/(ds_dalpha(ialpha)-ds_dalpha(ialpha-1))*(d_alpha)
                   sinalpha = sin(alpha0/2d0)
                   cosalpha = cos(alpha0/2d0)
                   ds_dalpha0 =-2d0*cosalpha**3*sinalpha*para_e2+2d0*sinalpha**3*cosalpha*para_b2+2d0*cosalpha**3*sinalpha*para_d2-2d0*sinalpha**3*cosalpha*para_d2+4d0*cosalpha**3*sinalpha*para_c2-4d0*sinalpha**3*cosalpha*para_c2+2d0*cosalpha**4*para_f2-6d0*cosalpha**2*sinalpha**2*para_f2+6d0*sinalpha**2*cosalpha**2*para_a2-2d0*sinalpha**4*para_a2
                   if (ds_dalpha0.le.0d0) then
!                      alphabounds(1) = alpha0
!                      alphabounds(2) = alpha_tmp
!                   else !ds_dalpha0.le.0d0
!                      alphabounds(1) = alpha_tmp-dalpha
!                      alphabounds(2) = alpha0
                      k_candidate = k_candidate+1
                      alpha_get(k_candidate) = alpha0
                      beta_get(k_candidate) = beta_tmp
                   endif !ds_dalpha0.le.0d0
                endif !(ds_dalpha(ialpha)*ds_dalpha(ialpha-1)).lt.0d0
             enddo !ialpha=1,nalpha
                !write(*,*) 'finish alpha loop',ibeta
          enddo !iiter=1,niter
       endif !delta.ge.0d0
    enddo !ibeta=1,nbeta

    ! Now check which of the alpha/beta candidates maximize the spin
    k_take  = 0
    maxspin = 0d0
    if (k_candidate.ge.1)then
       do i_kcand=1,k_candidate
          maxspin_k = 0d0
          do ixyz=1,3
             s_ab = cos(alpha_get(i_kcand)/2d0)**2*real(Spin_ini_selected(ixyz,1))+sin(alpha_get(i_kcand)/2d0)**2*real(Spin_ini_selected(ixyz,2))+2*sin(alpha_get(i_kcand)/2d0)*cos(alpha_get(i_kcand)/2d0)*real(exp(CI*beta_get(i_kcand))*Scross_selected(ixyz))
             maxspin_k = maxspin_k + s_ab**2
          enddo !ixyz=1,3
          if (maxspin_k.gt.maxspin)then
             maxspin = maxspin_k
             k_take = i_kcand
          endif !
                !write(*,*) 'in 2nd loop',i_kcand
       enddo !i_kcand=1,k_candidate
    endif !k_candidate.ge.1
     

    ! Now k_take is the index which maximizes the total spin
    alpha = alpha_get(k_take)
    beta  = beta_get(k_take)

    sumS = 0d0 
    do ixyz=1,3
       s_ab = cos(alpha/2d0)**2*real(Spin_ini_selected(ixyz,1))+sin(alpha/2d0)**2*real(Spin_ini_selected(ixyz,2))+2*sin(alpha/2d0)*cos(alpha/2d0)*real(exp(CI*beta)*Scross_selected(ixyz))
       s_ab2 = cos(alpha/2d0)**2*real(Spin_ini_selected(ixyz,2))+sin(alpha/2d0)**2*real(Spin_ini_selected(ixyz,1))+2*sin(alpha/2d0)*cos(alpha/2d0)*real(exp(CI*beta)*conjg(Scross_selected(ixyz)))
       sumS(1) = sumS(1) + s_ab**2
       sumS(2) = sumS(2) + s_ab2**2
    enddo !ixyz=1,3
    if (sumS(1).lt.sumS(2))then
       alpha = alpha+pi
       beta = beta+pi
    endif

  end subroutine Spinrot_AlphaBeta_Rashba



   subroutine spin_crossterm(inc, rhod, rveig_atom, Spi12, Spi12_atom)
  ! Calculates the spin-crossterm 2 (degenerate) states |psi_i>.
  !   (see PhD thesis of Swantje Heers, chapter 4.6)
  !   The corresponding coefficientvector must be already properly normalized.
  !                                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(inc%lmmaxso,inc%lmmaxso,inc%natypd,4), rveig_atom(inc%lmmaxso,inc%natypd,2)
    double complex, intent(out) :: Spi12(3)
    double complex, intent(out), optional :: Spi12_atom(3,inc%natypd)

    ! Locals
    integer        :: ixyz, iatom
    double complex :: ztmp, ckhelp(inc%lmmaxso), Spi12_tmp(3,inc%natypd)

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

    Spi12 = CZERO
    Spi12_atom = CZERO

    ! calculate S^i_{c_1,c_2} = c_1^dagger  \Sigma^i c_2, i=x,y,z     Spi12 = (0d0, 0d0)
    do ixyz = 1,3
      do iatom=1,inc%natypd
        ckhelp = CZERO
        ztmp   = CZERO

        call ZGEMM('N', 'N', inc%lmmaxso, 1, inc%lmmaxso, CONE, rhod(:,:,iatom,ixyz+1), 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)

        Spi12_tmp(ixyz,iatom) = ztmp
      end do
    end do

   do ixyz = 1,3
     Spi12(ixyz) = sum(Spi12_tmp(ixyz,:))
   end do

   if(present(Spi12_atom)) Spi12_atom = Spi12_tmp

  end subroutine spin_crossterm

end module mod_spintools