K_dipoles.F Source File


Source Code

!
! License-Identifier: GPL
!
! Copyright (C) 2021 The Yambo Team
!
! Authors (see AUTHORS file for details): DS AM
!
! headers
!
#include<y_memory.h>
!
subroutine K_dipoles(iq,Ken,Xk,q,X_oscillators,Dip)
 !
 use pars,          ONLY:SP,IP,pi,cZERO,cI
 use IO_m,          ONLY:io_DIP
 use LIVE_t,        ONLY:live_timing
 use vec_operate,   ONLY:v_norm
 use R_lattice,     ONLY:bz_samp,q0_def_norm,qindx_X
 use stderr,        ONLY:intc
 use com,           ONLY:msg
 use wave_func,     ONLY:WF
 use ALLOC,         ONLY:DIPOLE_ALLOC_global
 use stderr,        ONLY:STRING_match
 use interfaces,    ONLY:WF_load,WF_free
 use electrons,     ONLY:levels,spin,i_spin_majority,i_spin_minority
 use X_m,           ONLY:X_t,global_gauge,i_G_shift
 use DIPOLES,       ONLY:DIPOLE_t
 use BS_solvers,    ONLY:BSS_Vnl_included,BSS_Q_dir,BSS_E_dir,BSS_P_dir
 use BS,            ONLY:BS_T_grp,BS_K_coupling,BS_nT_grps,BSE_L_kind,BS_bands,&
&                        BS_dip_size,l_BS_trace,BS_n_eh_spaces,&
&                        l_BS_abs,l_BS_kerr,l_BS_magnons,l_BS_dichroism,l_BS_photolum,&
&                        BSE_dipole_geometry,BS_field_direction,l_BS_ares_from_res,&
&                        l_BS_mespin,l_BS_meorb
 use parallel_int,  ONLY:PP_redux_wait
 use parallel_m,    ONLY:PAR_BS_nT_col_grps,PAR_COM_eh_INDEX,PAR_IND_T_groups,&
&                        PAR_COM_Xk_ibz_INDEX,PARALLEL_default_mode
 use collision_el,  ONLY:elemental_collision,elemental_collision_free,elemental_collision_alloc
 use timing_m,      ONLY:timing
 use vec_operate,   ONLY:v_rotate,cross_product
 use y_memory_alloc
 !
 implicit none
 !
 integer       ::iq
 type(levels)  ::Ken
 type(bz_samp) ::Xk,q
 type(X_t)     ::X_oscillators
 type(DIPOLE_t)::Dip
 !
 ! Work space
 !
 logical           ::geometry_ok
 integer           ::ik_bz,ok_bz,ik,ok,isymm,osymm,iGo,iv,ic,i_sp_pol_c,i_sp_pol_v,i_T_g,i_T,&
&                    io_err,i_res_ares,i_dip
 complex(SP)       ::DIP_Splus,DIP_Smins,DIP_projected(BS_dip_size),DIP_expanded(3),DIP_bare(3)
 character(6)      ::DIP_kind
 !
 integer,      external :: OPTICS_select_q_and_G
 !
 type(elemental_collision):: BSE_scatt
 !
 call section('=','Loading Dipoles @q'//trim(intc(iq)))
 !======================================================
 call timing('BSE dipoles',OPR='start')
 !
 if (trim(global_gauge)=='length'  ) DIP_kind ="DIP_iR"
 if (trim(global_gauge)=='velocity') DIP_kind ="DIP_v"
 !
 ! Check if in 1st BZ or outside
 !
 i_G_shift=OPTICS_select_q_and_G(iq,q,X_oscillators)
 if (i_G_shift<0) call error("wrong QShift in input")
 !
 ! The residuals of the BSE are <wf_k|e^{iqr}|wf_k+q>
 ! These are trivial to compute at finite q, instead they are 
 ! obtained via the dipoles in the q->0 limit and stored to disk
 !
 if (iq==1.and.i_G_shift==1) then
   call DIPOLE_IO(Xk,Ken,Dip,'read ',io_err,'K')
 else
   call WF_load(WF,1,maxval(qindx_X(:,:,2)),BS_bands,(/1,Xk%nibz/),title='-Oscillators/R space/K_IP')
   call elemental_collision_free(BSE_scatt)
   call elemental_collision_alloc(BSE_scatt,NG=i_G_shift,TITLE="BSE") 
 endif
 !
 BSS_Vnl_included=Dip%Vnl_included
 !
 if (iq==1.and.i_G_shift==1) then
   !
   BSS_P_dir=cross_product(BSS_Q_dir,BSS_E_dir)
   !
   if (trim(BSE_dipole_geometry)/='none'.and.l_BS_kerr) then
     BSS_P_dir=0._SP
     call v_rotate(trim(BSE_dipole_geometry),-pi/2._SP,BSS_E_dir,v_out=BSS_P_dir)
     geometry_ok=abs(v_norm(BSS_P_dir)-1._SP)<1.E-5_SP .and. &
     &  abs(dot_product(BSS_E_dir,BSS_P_dir))<1.E-5_SP
     if ( .not. geometry_ok) call error(" Field direction and BSEdips do not agree")
     call msg("r"," BSEdips set in input. This imposes the propagation direction")
     BSS_Q_dir=-cross_product(BSS_E_dir,BSS_P_dir)
   endif
   !
   geometry_ok=abs(dot_product(BSS_E_dir,BSS_Q_dir))<1.E-5_SP
   !
   if ( l_BS_trace .and. .not. geometry_ok ) then
     call warning(" Propagation direction not ortogonal to field polarization")
     call warning(" Setting z for propagation and x-y for polarization")
     BSS_E_dir=(/1._SP,0._SP,0._SP/)
     BSS_P_dir=(/0._SP,1._SP,0._SP/)
     BSS_Q_dir=(/0._SP,0._SP,1._SP/)
     if (trim(BSE_dipole_geometry)/='none'.and.l_BS_kerr) BSE_dipole_geometry="xy"
   endif
   !
   ! Directions for the external field
   !
   BS_field_direction(:,1)=BSS_E_dir  ! external field
   BS_field_direction(:,2)=BSS_P_dir  ! induced polarization
   BS_field_direction(:,3)=BSS_Q_dir  ! field propagation direction
   !
 endif
 !
 call live_timing('BSE dipoles',PAR_BS_nT_col_grps)
 !
 do i_T_g=1,BS_nT_grps
   !
   if (.not.PAR_IND_T_groups%element_1D(i_T_g)) cycle
   !
   i_res_ares=BS_T_grp(i_T_g)%i_res_ares
   !
   do i_T=1,BS_T_grp(i_T_g)%size
     !
     ! Indexes
     !=========
     !
     ik_bz = BS_T_grp(i_T_g)%table(i_T,1)
     iv    = BS_T_grp(i_T_g)%table(i_T,2)
     ic    = BS_T_grp(i_T_g)%table(i_T,3)
     i_sp_pol_c = BS_T_grp(i_T_g)%table(i_T,4)
     i_sp_pol_v = BS_T_grp(i_T_g)%table(i_T,5)
     !
     if(iq/=1.or.i_G_shift/=1) then
       !
       ok_bz=qindx_X(iq,ik_bz,1)
       iGo  =qindx_X(iq,ik_bz,2)
       !
       ik   =Xk%sstar(ik_bz,1) 
       isymm=Xk%sstar(ik_bz,2) 
       !
       ok   =Xk%sstar(ok_bz,1)
       osymm=Xk%sstar(ok_bz,2)
       !
       BSE_scatt%is=(/ic,ik,isymm,i_sp_pol_c/)
       BSE_scatt%os=(/iv,ok,osymm,i_sp_pol_v/)
       BSE_scatt%qs=(/iGo,iq,1/)
       !
     endif
     !
     ! *length*   : DIP(c,v,k) is q_ver*<v|r|c> while I need q_ver*<c|r|v> = conjg(q_ver*<v|r|c>)
     ! *velocity* : DIP(c,v,k) is q_ver*<v|p|c> while I need q_ver*<c|r|v> = conjg(q_ver*<v|r|c>)
     !
     ! Absorption, KERR, PL, Dichroism, Magnetoelectric
     if(l_BS_abs.or.l_BS_kerr.or.l_BS_photolum.or.&
&       l_BS_dichroism.or.l_BS_meorb.or.l_BS_mespin) then
       !
       if(iq==1.and.i_G_shift==1) then
         call DIPOLE_rotate(ic,iv,ik_bz,i_sp_pol_c,trim(DIP_kind),Xk,DIP_bare)
         DIP_expanded=-cI*DIP_bare
         do i_dip=1,BS_dip_size
           DIP_projected(i_dip)=dot_product(BS_field_direction(:,i_dip),DIP_expanded)
         enddo
         ! This is needed to have something consistent with the definiion from scatter_Bamp at finite-q
         if (trim(global_gauge)=="length") DIP_projected=DIP_projected*q0_def_norm
       else
         if(trim(global_gauge)=="length")   call scatter_Bamp(BSE_scatt)
         if(trim(global_gauge)=="velocity") call scatter_Bamp_using_the_gradient(BSE_scatt,"o")
         DIP_projected(1)=-conjg(BSE_scatt%rhotw(i_G_shift))
       endif
       !
       BS_T_grp(i_T_g)%dipoles_opt(:BS_dip_size,i_T,1)=conjg(DIP_projected(:BS_dip_size))
       if (BS_K_coupling.and.l_BS_ares_from_res.and.iq==1) then
         ! In the coulping case at q=1 when there are 2 blocks per group
         ! See also src/bse/K_blocks.F and K_components_folded_in_serial_arrays.F
         ! Possibly to be extended also to i_G_shift==1
         BS_T_grp(i_T_g)%dipoles_opt(:BS_dip_size,i_T,2)=DIP_projected(:BS_dip_size)
       endif
       !
     endif
     !
     ! Dichroism or orbital magnetoelectric part
     if (l_BS_dichroism.or.l_BS_meorb) then
       if(iq==1.and.i_G_shift==1) then  
         call DIPOLE_rotate(ic,iv,ik_bz,i_sp_pol_c,"DIP_L",Xk,DIP_expanded)
         do i_dip=1,BS_dip_size
           DIP_projected(i_dip)=dot_product(BS_field_direction(:,i_dip),DIP_expanded)
         enddo
       else
         if (l_BS_dichroism) call error(' finite q not implemented for dichroism effect')
         if (l_BS_meorb)     call error(' finite q not implemented for magnetoelectric effect')
       endif
       BS_T_grp(i_T_g)%dipoles_orb(:BS_dip_size,i_T,1)=conjg(DIP_projected(:BS_dip_size))
       if (BS_K_coupling.and.l_BS_ares_from_res) then
         BS_T_grp(i_T_g)%dipoles_orb(:BS_dip_size,i_T,2)=DIP_projected(:BS_dip_size)
       endif
     endif
     !
     ! Magnons and Magnetoelectric spin part
     if (l_BS_magnons.or.l_BS_mespin) then
       !
       ! notice:
       ! In optics q=0 requires a special treatment due to the non analytic behaviour
       ! This is not needed for magnons.
       ! However to follow the same structure in K_IP.F fow WFs load / DIPOLE_IO here I keep it
       ! The implementation with scatter can be used to double check the spin dipoles
       !
       if(iq==1.and.i_G_shift==1) then
         call DIPOLE_rotate(ic,iv,ik_bz,i_sp_pol_c,"DIP_S",Xk,DIP_expanded)
       else
         if (l_BS_mespin) call error(' finite q not implemented for magnetoelectric effect')
         call scatter_Bamp_spin(BSE_scatt,'x')
         DIP_expanded(1)=-conjg(BSE_scatt%rhotw(1))
         call scatter_Bamp_spin(BSE_scatt,'y')
         DIP_expanded(2)=-conjg(BSE_scatt%rhotw(1))
         !The following is not used at present
         call scatter_Bamp_spin(BSE_scatt,'z')
         DIP_expanded(3)=-conjg(BSE_scatt%rhotw(1))
       endif
       !
       ! Magnons
       if (l_BS_magnons) then
         !
         ! DIP_Smins(c,v,k) is  <v|S-|c>(i_sp_pol_c), to get <c|S-|v>(i_sp_pol_v) = conjg(<v|S+|c>(i_sp_pol_c))
         ! DIP_Splus(c,v,k) is  <v|S+|c>(i_sp_pol_c), to get <c|S+|v>(i_sp_pol_v) = conjg(<v|S-|c>(i_sp_pol_c))
         !
         DIP_Splus = 0.5_SP*(DIP_expanded(1)+cI*DIP_expanded(2))  ! S+ for c\dn> transitions: <v\up|c\dn>
         DIP_Smins = 0.5_SP*(DIP_expanded(1)-cI*DIP_expanded(2))  ! S- for c\up> transitions: <v\dn|c\up>
         !
         BS_T_grp(i_T_g)%dipoles_mag(i_spin_majority,i_T,1)=conjg(DIP_Splus)   ! S- for |c\dn> trans: <c\dn|S-|v\up>(i_sp_pol_v) = conjg(<v\up|S+|c\dn>(i_sp_pol_c))
         BS_T_grp(i_T_g)%dipoles_mag(i_spin_minority,i_T,1)=conjg(DIP_Smins)   ! S+ for |c\up> trans: <c\dn|S+|v\up>(i_sp_pol_v) = conjg(<v\dn|S-|c\up>(i_sp_pol_c))
                                                                 !      or the right R residual associated to (eps_c\dn-eps_v\up)
                                                                 ! S- for |v\dn> trans: <v\dn|S-|c\up>(i_sp_pol_c) = conjg(<c\up|S+|v\dn>(i_sp_pol_v))
                                                                 ! S+ for |v\up> trans: <v\up|S+|c\dn>(i_sp_pol_c) = conjg(<c\dn|S-|v\up>(i_sp_pol_v))
         if (BS_K_coupling.and.l_BS_ares_from_res) then          ! This is the right A residual associated to (eps_v\up-eps_c\dn)
           BS_T_grp(i_T_g)%dipoles_mag(i_spin_majority,i_T,2)=DIP_Smins        ! S- for |v\dn> trans: <v\dn|S-|c\up>(i_sp_pol_c)
           BS_T_grp(i_T_g)%dipoles_mag(i_spin_minority,i_T,2)=DIP_Splus        ! S+ for |v\up> trans: <v\up|S+|c\dn>(i_sp_pol_c)
         endif
         !
       endif
       !
       !
       ! Magnetoelectric spin part
       if (l_BS_mespin) then
         do i_dip=1,BS_dip_size
           DIP_projected(i_dip)=dot_product(BS_field_direction(:,i_dip),DIP_expanded)
         enddo
         BS_T_grp(i_T_g)%dipoles_spin(:BS_dip_size,i_T,1)=conjg(DIP_projected(:BS_dip_size))
         if (BS_K_coupling.and.l_BS_ares_from_res) then
           BS_T_grp(i_T_g)%dipoles_spin(:BS_dip_size,i_T,2)=DIP_projected(:BS_dip_size)
         endif
       endif
       !
     endif
     !
   enddo
   !
   call live_timing(steps=1)
   !
 enddo
 !
 call live_timing()
 !
 if (STRING_match(BSE_L_kind,"full")) then
   do i_T_g=1,BS_nT_grps
     if (.not.allocated(BS_T_grp(i_T_g)%dipoles_opt) ) then
       YAMBO_ALLOC(BS_T_grp(i_T_g)%dipoles_opt,(BS_dip_size,BS_T_grp(i_T_g)%size,2/BS_n_eh_spaces))
       BS_T_grp(i_T_g)%dipoles_opt=cZERO
     endif
     call PP_redux_wait(BS_T_grp(i_T_g)%dipoles_opt, COMM=PAR_COM_Xk_ibz_INDEX%COMM )
     call PP_redux_wait(BS_T_grp(i_T_g)%dipoles_opt, COMM=PAR_COM_eh_INDEX%COMM )
   enddo
 endif
 !
 ! CLEAN
 !
 if (iq==1.and.i_G_shift==1.and.io_DIP) then
   !
   call DIPOLE_ALLOC_global( )
   !
   ! The call to DIPOLE_IO might have changed the PAR_K_scheme.
   ! Here I reset it to the previous value
   if(     trim(PARALLEL_default_mode)=="KQmemory") call PARALLEL_SETUP_K_scheme("K")
   if(.not.trim(PARALLEL_default_mode)=="KQmemory") call PARALLEL_SETUP_K_scheme("Kdef")
   !
 endif
 !
 if(iq>1.or.i_G_shift>1) then
   call elemental_collision_free(BSE_scatt)
   call WF_free(WF)
 endif
 !
 call timing('BSE dipoles',OPR='stop')
 !
end subroutine

📚 Documentation Pages
⏱️ Real-Time
🔗 Code API