Line data Source code
1 : !!****m* ABINIT/m_opernla_ylm_mv
2 : !! NAME
3 : !! m_opernla_ylm_mv
4 : !!
5 : !! FUNCTION
6 : !!
7 : !! COPYRIGHT
8 : !! Copyright (C) 2021-2026 ABINIT group (LB,MT)
9 : !! This file is distributed under the terms of the
10 : !! GNU General Public License, see ~abinit/COPYING
11 : !! or http://www.gnu.org/copyleft/gpl.txt .
12 : !!
13 : !! SOURCE
14 :
15 : #if defined HAVE_CONFIG_H
16 : #include "config.h"
17 : #endif
18 :
19 : #include "abi_common.h"
20 :
21 : module m_opernla_ylm_mv
22 :
23 : use defs_basis
24 : use m_abicore
25 : use m_errors
26 : use m_xmpi
27 : #if defined HAVE_OPENMP
28 : use OMP_LIB
29 : #endif
30 :
31 : use defs_abitypes, only : MPI_type
32 : use m_time, only : timab
33 :
34 : implicit none
35 :
36 : private
37 : !!***
38 :
39 : public :: opernla_ylm_mv
40 : integer,public,save :: opernla_mv_counter = -1
41 : integer,public,save :: opernla_mv_dgemv_counter = -1
42 : !!***
43 : !!***
44 :
45 : contains
46 : !!***
47 :
48 : !!****f* ABINIT/opernla_ylm_mv
49 : !! NAME
50 : !! opernla_ylm_mv
51 : !!
52 : !! FUNCTION
53 : !! "matrix-vector" alternative implementation of "opernla_ylm".
54 : !!
55 : !! For a given wave-function |c>, get all projected scalars
56 : !! <p_lmn|c> where |p_lmn> are non-local projectors
57 : !! With:
58 : !! <p_lmn|c>=4pi/sqrt(vol) (i)^l Sum_g[c(g).f_nl(g).Y_lm(g).exp(2pi.i.g.R)]
59 : !!
60 : !! Here this is done in 3 steps:
61 : !! (1) compute for every g : scal(g) = c(g).exp(2pi.i.g.R)
62 : !! (2) compute for every lmn : scal(lmn) = Sum_g[scal(g).f_nl(g).Y_lm(g)]
63 : !! (3) compute for every lmn : <p_lmn|c> = 4pi/sqrt(vol).(i)^l.scal(lmn)
64 : !!
65 : !! Step (2) is a real-matrix/complex-vector multiplication, here two options are possible:
66 : !! - case nloalg(1)=2 : compute the real and imaginary parts separately using two calls of DGMEV
67 : !! - case nloalg(1)=3 : in order to read the matrix only once, we compute both real and imaginary parts at the same time "by hand"
68 : !!
69 : !! Depending on the achitecture and the available blas library, one option could be more interesting than an other...
70 : !!
71 : !! INPUTS
72 : !! choice=chooses possible output:
73 : !! if choice>=0: compute projected scalars
74 : !! if choice<0: same as choice>0 but use already computed projected scalars
75 : !! cplex=1 if <p_lmn|c> scalars are real (equivalent to istwfk>1)
76 : !! 2 if <p_lmn|c> scalars are complex
77 : !! dimffnl=second dimension of ffnl
78 : !! ffnl(npw,dimffnl,nlmn)= nonlocal quantities containing nonlocal form factors
79 : !! ia3=gives the number of the first atom in the subset presently treated
80 : !! indlmn(6,nlmn)= array giving l,m,n,lm,ln,s for i=lmn
81 : !! istwf_k=option parameter that describes the storage of wfs
82 : !! matblk=dimension of the array ph3d
83 : !! mpi_enreg=information about MPI parallelization
84 : !! nincat=number of atoms in the subset here treated
85 : !! nlmn=number of (l,m,n) numbers for current type of atom
86 : !! nloalg(3)=governs the choice of the algorithm for non-local operator.
87 : !! npw=number of plane waves in reciprocal space
88 : !! nspinor=number of spinorial components of the wavefunctions (on current proc)
89 : !! ph3d(2,npw,matblk)=three-dimensional phase factors
90 : !! ucvol=unit cell volume (bohr^3)
91 : !! vect(2,npw*my_nspinor)=starting vector in reciprocal space
92 : !!
93 : !! OUTPUT
94 : !! gx(cplex,nlmn,nincat,nspinor)= projected scalars
95 : !!
96 : !! SIDE EFFECTS
97 : !!
98 : !! NOTES
99 : !! 1-Not available yet for openMP
100 : !! 2-Operate for one type of atom, and within this given type of atom,
101 : !! for a subset of at most nincat atoms.
102 : !! 3-projector derivatives (abs(choice)>1) are not implemented yet
103 : !!
104 : !! SOURCE
105 :
106 0 : subroutine opernla_ylm_mv(choice,cplex,dimffnl,ffnl,gx,&
107 0 : & ia3,indlmn,istwf_k,matblk,mpi_enreg,nincat,nlmn,&
108 0 : & nloalg,npw,nspinor,ph3d,ucvol,vect)
109 :
110 : !Arguments ------------------------------------
111 : !scalars
112 : integer,intent(in) :: choice,cplex,dimffnl,ia3,istwf_k,matblk
113 : integer,intent(in) :: nincat,nlmn,npw,nspinor
114 : real(dp),intent(in) :: ucvol
115 : type(MPI_type),intent(in) :: mpi_enreg
116 : !arrays
117 : integer,intent(in) :: indlmn(6,nlmn),nloalg(3)
118 : real(dp),intent(in),target :: ffnl(npw,dimffnl,nlmn)
119 : real(dp),intent(in) :: ph3d(2,npw,matblk)
120 : real(dp),intent(in) :: vect(:,:)
121 : real(dp),intent(out) :: gx(cplex,nlmn,nincat,nspinor)
122 :
123 : !Local variables-------------------------------
124 : !scalars
125 : logical :: use_dgemv
126 : integer :: ia,iaph3d,ierr,il,ilmn,ipw,ipw0,ipwshft,ispinor,jpw
127 : real(dp) :: wt
128 : !arrays
129 : real(dp) :: buffer_r,buffer_i,tsec(2)
130 0 : real(dp),pointer :: ffnl_loc(:,:)
131 0 : real(dp),allocatable :: scali(:),scalr(:)
132 0 : real(dp),allocatable :: scalr_lmn(:),scali_lmn(:)
133 : complex(dp) :: ctmp,cil(4)
134 : ! *************************************************************************
135 :
136 0 : if (choice==-1) return
137 :
138 : !Some checks
139 0 : if (abs(choice)>1) then
140 0 : ABI_ERROR('Only abs(choice)<=1 is available for now.')
141 : end if
142 0 : if (nloalg(1)<2.or.nloalg(1)>10) then
143 0 : ABI_ERROR('nloalg(1) should be between 2 and 10.')
144 : end if
145 : ! nthreads=1
146 : !#if defined HAVE_OPENMP
147 : ! nthreads=OMP_GET_NUM_THREADS()
148 : !#endif
149 : ! if (nthreads>1) then
150 : ! ABI_ERROR('Only nthreads=1 is available for now.')
151 : ! end if
152 :
153 0 : use_dgemv = nloalg(1)==2.or.nloalg(1)==5.or.nloalg(1)==7
154 : if (choice>=0.or.abs(choice)>1) then
155 0 : if (use_dgemv) then
156 0 : if(opernla_mv_dgemv_counter>=0) opernla_mv_dgemv_counter = opernla_mv_dgemv_counter + 1
157 : else
158 0 : if(opernla_mv_counter>=0) opernla_mv_counter = opernla_mv_counter + 1
159 : end if
160 : end if
161 :
162 : !Useful variables
163 0 : wt=four_pi/sqrt(ucvol);if (cplex==1) wt=2.d0*wt
164 0 : ipw0=1;if (istwf_k==2.and.mpi_enreg%me_g0_fft==1) ipw0=2
165 :
166 : !Allocate work space
167 0 : ABI_MALLOC(scalr,(npw))
168 0 : ABI_MALLOC(scali,(npw))
169 0 : ABI_MALLOC(scalr_lmn,(nlmn))
170 0 : ABI_MALLOC(scali_lmn,(nlmn))
171 :
172 0 : ffnl_loc => ffnl(:,1,:)
173 :
174 : ! i^l
175 0 : cil(1) = ( 1.0_DP, 0.0_DP) * wt
176 0 : cil(2) = ( 0.0_DP, 1.0_DP) * wt
177 0 : cil(3) = (-1.0_DP, 0.0_DP) * wt
178 0 : cil(4) = ( 0.0_DP,-1.0_DP) * wt
179 :
180 : ! FIXME OpenMP parallelism is still flawed here so forcing NUM_THREADS=1 here
181 : ! Bug reproducible with v9[71] and bounds checks enabled
182 : !$OMP PARALLEL PRIVATE(il,ilmn,ipw,jpw), &
183 : !$OMP PRIVATE(ispinor,ipwshft,ia,iaph3d) &
184 : !$OMP NUM_THREADS(1)
185 :
186 : !Loop on spinorial components
187 0 : do ispinor =1,nspinor
188 0 : ipwshft=(ispinor-1)*npw
189 :
190 : ! Loop on atoms (blocking)
191 0 : do ia=1,nincat
192 0 : iaph3d=ia;if (nloalg(2)>0) iaph3d=ia+ia3-1
193 : ! Step (1) : Compute scal(g) = c(g).exp(2pi.i.g.R)
194 : !$OMP DO
195 0 : do ipw=ipw0,npw
196 0 : jpw=ipw+ipwshft
197 0 : scalr(ipw)=(vect(1,jpw)*ph3d(1,ipw,iaph3d)-vect(2,jpw)*ph3d(2,ipw,iaph3d))
198 0 : scali(ipw)=(vect(2,jpw)*ph3d(1,ipw,iaph3d)+vect(1,jpw)*ph3d(2,ipw,iaph3d))
199 : end do
200 : !$OMP END DO
201 :
202 : !$OMP SINGLE
203 0 : if (ipw0==2) then
204 0 : scalr(1)=half*vect(1,1+ipwshft)*ph3d(1,1,iaph3d)
205 0 : scali(1)=half*vect(1,1+ipwshft)*ph3d(2,1,iaph3d)
206 : end if
207 : !$OMP END SINGLE
208 :
209 : ! --------------------------------------------------------------------
210 : ! ALL CHOICES:
211 : ! Accumulate Gx
212 : ! --------------------------------------------------------------------
213 :
214 0 : if (choice>=0) then
215 :
216 : ! Step (2) : Compute scal(lmn) = Sum_g[scal(g).f_nl(g).Y_lm(g)]
217 0 : if (use_dgemv) then
218 0 : call DGEMV('T',npw,nlmn,1.0_DP,ffnl_loc,npw,scalr,1,0.0_DP,scalr_lmn,1)
219 0 : call DGEMV('T',npw,nlmn,1.0_DP,ffnl_loc,npw,scali,1,0.0_DP,scali_lmn,1)
220 : else
221 0 : do ilmn=1,nlmn
222 : ! do ipw=1,npw
223 : ! scalr_lmn(ilmn) = scalr_lmn(ilmn) + scalr(ipw) * ffnl_loc(ipw,ilmn)
224 : ! scali_lmn(ilmn) = scali_lmn(ilmn) + scali(ipw) * ffnl_loc(ipw,ilmn)
225 : ! end do
226 : !$OMP SINGLE
227 : buffer_r = 0.0_DP
228 : buffer_i = 0.0_DP
229 : !$OMP END SINGLE
230 : !$OMP DO REDUCTION(+:buffer_r,buffer_i)
231 0 : do ipw=1,npw
232 0 : buffer_r = buffer_r + scalr(ipw) * ffnl_loc(ipw,ilmn)
233 0 : buffer_i = buffer_i + scali(ipw) * ffnl_loc(ipw,ilmn)
234 : end do
235 : !$OMP END DO
236 : !$OMP SINGLE
237 0 : scalr_lmn(ilmn) = buffer_r
238 0 : scali_lmn(ilmn) = buffer_i
239 : !$OMP END SINGLE
240 : end do
241 : end if
242 : ! Step (3) : Compute gx(lmn) = 4pi/sqrt(vol) (i)^l scal(lmn)
243 : !$OMP SINGLE
244 0 : if (cplex==2) then
245 0 : do ilmn=1,nlmn
246 0 : il=mod(indlmn(1,ilmn),4)+1
247 0 : ctmp = cil(il) * cmplx(scalr_lmn(ilmn),scali_lmn(ilmn),kind=DP)
248 0 : gx(1,ilmn,ia,ispinor) = real(ctmp)
249 0 : gx(2,ilmn,ia,ispinor) = aimag(ctmp)
250 : end do
251 : else
252 0 : do ilmn=1,nlmn
253 0 : il=mod(indlmn(1,ilmn),4)+1
254 0 : ctmp = cil(il) * cmplx(scalr_lmn(ilmn),scali_lmn(ilmn),kind=DP)
255 0 : gx(1,ilmn,ia,ispinor) = real(ctmp)
256 : end do
257 : end if
258 : !$OMP END SINGLE
259 :
260 : end if
261 :
262 : end do ! End loop on atoms
263 :
264 : end do ! End loop on spinorial components
265 : !$OMP END PARALLEL
266 :
267 : !Deallocate temporary space
268 0 : ABI_FREE(scalr)
269 0 : ABI_FREE(scali)
270 0 : ABI_FREE(scalr_lmn)
271 0 : ABI_FREE(scali_lmn)
272 :
273 : !Has to reduce arrays in case of FFT parallelization
274 0 : if (mpi_enreg%nproc_fft>1) then
275 0 : call timab(48,1,tsec)
276 0 : if (choice>=0) then
277 0 : call xmpi_sum(gx,mpi_enreg%comm_fft,ierr)
278 : end if
279 0 : call timab(48,2,tsec)
280 : end if
281 :
282 0 : end subroutine opernla_ylm_mv
283 : !!***
284 :
285 : end module m_opernla_ylm_mv
286 : !!***
|