Line data Source code
1 : !!****m* ABINIT/m_classify_bands
2 : !! NAME
3 : !! m_classify_bands
4 : !!
5 : !! FUNCTION
6 : !! Finds the irreducible representation associated to
7 : !! a set of degenerate bands at a given k-point and spin.
8 : !!
9 : !! COPYRIGHT
10 : !! Copyright (C) 2008-2026 ABINIT group (MG)
11 : !! This file is distributed under the terms of the
12 : !! GNU General Public License, see ~abinit/COPYING
13 : !! or http://www.gnu.org/copyleft/gpl.txt .
14 : !!
15 : !! SOURCE
16 :
17 : #if defined HAVE_CONFIG_H
18 : #include "config.h"
19 : #endif
20 :
21 : #include "abi_common.h"
22 :
23 : module m_classify_bands
24 :
25 : use, intrinsic :: iso_c_binding, only : c_f_pointer, c_loc
26 : use defs_basis
27 : use m_abicore
28 : use m_xmpi
29 : use m_errors
30 : use netcdf
31 : use m_nctk
32 :
33 : use m_fstrings, only : itoa, ftoa, sjoin, ktoa, ltoa, strcat, yesno
34 : use m_geometry, only : getspinrot
35 : use defs_datatypes, only : pseudopotential_type
36 : use m_dtset, only : dataset_type
37 : use m_dtfil, only : datafiles_type
38 : use m_io_tools, only : iomode_from_fname
39 : use m_time, only : cwtime, cwtime_report
40 : use m_numeric_tools, only : get_trace, print_arr
41 : use m_matrix, only : is_unitary, is_identity, mati3inv
42 : use m_hdr, only : hdr_type
43 : use m_hide_blas, only : xdotc, xdotu, xcopy
44 : use m_fft_mesh, only : rotate_FFT_mesh, calc_ceigr
45 : use m_crystal, only : crystal_t
46 : use m_cgtools, only : cg_zdotc
47 : use m_symtk, only : sg_multable, sym_order
48 : use m_pawang, only : pawang_type
49 : use m_pawrad, only : pawrad_type
50 : use m_pawtab, only : pawtab_type, pawtab_get_lsize
51 : use m_pawfgrtab, only : pawfgrtab_type, pawfgrtab_init, pawfgrtab_print, pawfgrtab_free
52 : use m_pawcprj, only : pawcprj_type, pawcprj_alloc, pawcprj_free, pawcprj_copy
53 : use m_paw_pwaves_lmn, only : paw_pwaves_lmn_t, paw_pwaves_lmn_init, paw_pwaves_lmn_free
54 : use m_paw_sphharm, only : setsym_ylm
55 : use m_paw_nhat, only : nhatgrid
56 : use m_wfd, only : wfd_t
57 : use m_ebands, only : ebands_t
58 : use m_common, only : ebands_from_file
59 : use m_fftcore, only : sphere, get_kg, ngfft_seq
60 : use m_cgtk, only : cgtk_rotate, cgtk_change_gsphere
61 : use m_kpts, only : kpts_map
62 : use m_krank, only : krank_t
63 : use m_esymm, only : esymm_t, esymm_free
64 : use m_ptgroups, only : get_classes
65 : use m_yaml, only : yamldoc_t, yamldoc_open
66 : use m_pair_list, only : pair_list
67 :
68 : implicit none
69 :
70 : private
71 : !!***
72 :
73 : public :: classify_bands
74 :
75 : !!****t* m_classify_bands/dmats_t
76 : !! NAME
77 : !! dmats_t
78 : !!
79 : !! FUNCTION
80 : !! Store D_mn(S) = <psi_{mSk}| S | psi_{nk}> for all the k-points in the IBZ
81 : !! and the bands in brange_spin.
82 : !!
83 : !! SOURCE
84 :
85 : type, public :: dmats_t
86 :
87 : type(ebands_t) :: ks_ebands
88 : ! KS bands.
89 :
90 : type(crystal_t),pointer :: cryst => null()
91 : type(dataset_type),pointer :: dtset => null()
92 :
93 : integer,allocatable :: brange_spin(:,:)
94 : ! (2, nsppol)
95 : ! start and end band index for each spin
96 :
97 : integer,allocatable :: multable(:,:,:)
98 : ! (4,nsym,nsym)
99 : ! multable(1,sym1,sym2) gives the index of the symmetry product S1 * S2 in the symrel array. 0 if not found.
100 : ! multable(2:4,sym1,sym2)= the lattice vector that has to added to the fractional translation
101 : ! of the operation of index multable(1,sym1,sym2) to obtain the fractional translation of the product S1 * S2.
102 :
103 : integer,allocatable :: toinv(:,:)
104 : ! (4,nsym)
105 : ! toinv(1,sym1)=Gives the index of the inverse of the symmetry operation.
106 : ! S1 * S1^{-1} = {E, L} with E the identity and L a real-space lattice vector.
107 : ! toinv(2:4,sym1)=The lattice vector L
108 : ! Note that toinv can be easily obtained from multable but sometimes we do not need the full table.
109 :
110 : type(coeff5c_type), allocatable :: for_spin(:)
111 :
112 : contains
113 : procedure :: init => dmats_init ! Initialize object
114 : procedure :: init_from_file => dmats_init_from_file ! Initialize object from a NetCDF file
115 : procedure :: ncwrite => dmats_ncwrite ! Write D-matrices to an open NetCDF file
116 : procedure :: free => dmats_free ! Free memory.
117 : procedure :: check => dmats_check ! Check Dmats
118 : procedure :: classify => dmats_classify ! Classify irreps
119 : procedure :: get_star_dmats => dmats_get_star_dmats ! D-matrices at k'=S0.k_ibz
120 : procedure :: get_star_dmats_at_kpt => dmats_get_star_dmats_at_kpt ! Same, locating S0 from a raw kpt
121 : procedure :: check_star => dmats_check_star ! Run dmats_check_one_k at a star kpt
122 : end type dmats_t
123 : !!***
124 :
125 : contains
126 : !!***
127 :
128 : !!****f* m_classify_bands/classify_bands
129 : !! NAME
130 : !! classify_bands
131 : !!
132 : !! FUNCTION
133 : !! This routine finds the irreducible representation associated to
134 : !! a set of degenerate bands at a given k-point and spin.
135 : !! The irreducible representation is obtained by rotating the set
136 : !! of degenerate wavefunctions using the symmetry operations in the little group of k.
137 : !! Two states are treated as degenerate if their energy differs by less than EDIFF_TOL.
138 : !!
139 : !! INPUTS
140 : !! Wfd(wfd_t)= structure gathering information on wave functions
141 : !! ik_ibz=The index of the k-point in the IBZ.
142 : !! spin=The spin index.
143 : !! ngfft(18)=Info on the FFT mesh to be used for evaluting u(r) and the rotated u(R^{1}(r-t)).
144 : !! ngfft must be compatible with the symmetries of the crystal and can differ from Wfd%ngfft.
145 : !! wfd_change_ngfft is called if ANY(Wfd%ngfft(1:3) =/ ngfft).
146 : !! Cryst<crystal_t>=Type gathering info on the crystal structure.
147 : !! ebands<ebands_t>=Datatype with electronic energies.
148 : !! Pawtab(ntypat*usepaw) <type(pawtab_type)>=paw tabulated starting data
149 : !! Pawrad(ntypat*usepaw)<type(pawrad_type)>=paw radial mesh and related data.
150 : !! Pawang <type(pawang_type)>=paw angular mesh and related data
151 : !! Psps<pseudopotential_type>
152 : !! %indlmn(6,lmnmax,ntypat)=array giving l,m,n,lm,ln,spin for i=lmn (for each atom type)
153 : !! Dtfil<datafiles_type>=variables related to files
154 : !! %unpaw
155 : !! tolsym=Tolerance for the symmetries (input variable)
156 : !! [EDIFF_TOL]= tolerance on the energy difference of two states (if not specified is set to 0.005 eV)
157 : !!
158 : !! OUTPUT
159 : !! BSym<Bands_Symmetries>=structure containing info on the little group of the k-point as well
160 : !! as the character of the representation associated to each set of degenerate states
161 : !! if BSym%isymmorphic the symmetry analysis cannot be performed, usually it means that
162 : !! k is at zone border and there are non-symmorphic translations (see Notes)
163 : !!
164 : !! NOTES
165 : !! * Let M(R_t) the irreducible representation associated to the space group symmetry (R_t).
166 : !! * By convention M(R_t) multiplies wave functions as a row vector:
167 : !!
168 : !! $ R_t \psi_a(r) = \psi_a (R^{-1}(r-\tau)) = \sum_b M(R_t)_{ba} \psi_b $
169 : !!
170 : !! Therefore, if R_t belongs to the little group of k (i.e. Sk=k+G0), one obtains:
171 : !!
172 : !! $ M_ab(R_t) = e^{-i(k+G0).\tau} \int e^{iG0.r} u_{ak}(r)^* u_{bk}(R^{-1}(r-\tau)) \,dr $.
173 : !!
174 : !! * The irreducible representation of the small _point_ group of k, M_ab(R), suffices to
175 : !! classify the degenerate eigenstates provided that particular conditions are fulfilled
176 : !! (see limitations below). The matrix is indeed given by:
177 : !!
178 : !! $ M_ab(R) = e^{+ik.\tau} M_ab(R_t) = e^{-iG0.\tau} \int e^{iG0.r} u_{ak}(r)^* u_{bk}(R^{-1}(r-\tau))\,dr $
179 : !!
180 : !! The phase factor outside the integral should be zero since symmetry analysis at border zone in non-symmorphic
181 : !! space groups is not available. Anyway it is included in our expressions for the sake of consistency.
182 : !!
183 : !! * For PAW there is an additional onsite terms involving <phi_i|phi_j(R^{-1}(r-\tau)> and
184 : !! the pseudized version that can be evaluated using the rotation matrix for
185 : !! real spherical harmonis, zarot(mp,m,l,R). $ Y_{lm}(Rr)= \sum_{m'} zarot(m',m,ll,R) Y_{lm'}(r) $
186 : !!
187 : !! $ M^{onsite}_ab(R_t) = sum_{c ij} <\tpsi_a| p_i^c> <p_j^{c'}|\tpsi_b\> \times
188 : !! [ <\phi_i^c|\phi_j^{c'}> - <\tphi_i^c|\tphi_j^{c'}> ]. $
189 : !!
190 : !! $ [ <\phi_i^c|\phi_j^{c'}> - <\tphi_i^c|\tphi_j^{c'}> ] = s_{ij} D_{\mi\mj}^\lj(R^{-1}) $
191 : !!
192 : !! where c' is the rotated atom i.e c' = R^{-1}( c-\tau) and D is the rotation matrix for
193 : !! real spherical harmonics.
194 : !!
195 : !! Remember that zarot(m',m,l,R)=zarot(m,m',l,R^{-1})
196 : !! and $ Y^l_m(ISG) = sum_{m'} D_{m'm}(S) Y_{m'}^l(G) (-i)^l $
197 : !! $ D_{m'm}^l (R) = D_{m,m'}^l (R^{-1}) $
198 : !!
199 : !! * LIMITATIONS: The method does not work if k is at zone border and the little group of k
200 : !! contains a non-symmorphic fractional translation.
201 : !!
202 : !! SOURCE
203 :
204 0 : subroutine classify_bands(Wfd,use_paw_aeur,first_band,last_band,ik_ibz,spin,ngfftf,&
205 0 : Cryst,ebands,Pawtab,Pawrad,Pawang,Psps,tolsym,BSym,&
206 : EDIFF_TOL) ! optional
207 :
208 : !Arguments ------------------------------------
209 : !scalars
210 : integer,intent(in) :: ik_ibz,spin,first_band,last_band
211 : real(dp),intent(in) :: tolsym
212 : real(dp),intent(in),optional :: EDIFF_TOL
213 : logical,intent(in) :: use_paw_aeur
214 : type(crystal_t),intent(in) :: Cryst
215 : type(pawang_type),intent(in) :: Pawang
216 : type(pseudopotential_type),intent(in) :: Psps
217 : class(wfd_t),intent(inout) :: Wfd
218 : type(ebands_t),target,intent(in) :: ebands
219 : type(esymm_t),intent(out) :: BSym
220 : !arrays
221 : integer,intent(in) :: ngfftf(18)
222 : type(pawtab_type),intent(in) :: Pawtab(Cryst%ntypat*Wfd%usepaw)
223 : type(Pawrad_type),intent(inout) :: Pawrad(Cryst%ntypat*Wfd%usepaw)
224 :
225 : !Local variables-------------------------------
226 : !scalars
227 : integer,parameter :: nspinor1=1
228 : integer :: dim_degs,ib1,ib2,ib_stop,ib_start,iclass,idg,sym_idx
229 : integer :: ir,isym,isym_class,tr_isym,jb1,jb2
230 : integer :: nr1,nr2,nr3,nsym_class,nfft,cplex !,ifgd,nfgd,ifft_sph
231 : integer :: ii,jj,lmax
232 : integer :: optcut,optgr0,optgr1,optgr2,optrad
233 : real(dp) :: EDIFF_TOL_,arg,fft_fact
234 : complex(dp) :: exp_mikg0t,exp_ikg0t,cmat_ab
235 : logical :: iscompatibleFFT,found,only_trace
236 : character(len=500) :: msg
237 : !arrays
238 0 : integer :: g0(3), toinv(Cryst%nsym), trial(3,3)
239 0 : integer,pointer :: Rm1_rmt(:)
240 0 : integer,target,allocatable :: irottb(:,:)
241 0 : integer,allocatable :: tmp_sym(:,:,:),l_size_atm(:)
242 : real(dp) :: kpt(3),kpg0(3),omat(2)
243 0 : real(dp),pointer :: ene_k(:), zarot(:,:,:,:)
244 0 : complex(dp),allocatable :: eig0r(:,:),tr_emig0r(:,:)
245 0 : complex(gwp),allocatable :: ur1(:),ur2(:),ur2_rot(:)
246 0 : type(pawcprj_type),allocatable :: Cprj_b1(:,:),Cprj_b2(:,:),Cprj_b2rot(:,:)
247 0 : type(Pawfgrtab_type),allocatable :: Pawfgrtab(:)
248 0 : type(paw_pwaves_lmn_t),allocatable :: Paw_onsite(:)
249 : ! *************************************************************************
250 :
251 : ! Consistency check on input.
252 0 : ABI_CHECK(Wfd%nspinor == 1, 'nspinor/=1 not coded')
253 :
254 : ! By default all bands are included
255 : !first_band=1; last_band=Wfd%nband(ik_ibz,spin)
256 0 : ABI_CHECK(first_band == 1, "first_band/=1 not coded")
257 0 : ABI_CHECK(last_band <= Wfd%nband(ik_ibz,spin), "last_band cannot be > nband_k")
258 :
259 0 : EDIFF_TOL_= 0.005/Ha_eV; if (PRESENT(EDIFF_TOL)) EDIFF_TOL_=ABS(EDIFF_TOL)
260 :
261 0 : call wfd%change_ngfft(Cryst,Psps,ngfftf)
262 :
263 : ! Get index of the rotated FFT points ===
264 : ! FFT mesh in real space _must_ be compatible with symmetries.
265 0 : nr1 = Wfd%ngfft(1)
266 0 : nr2 = Wfd%ngfft(2)
267 0 : nr3 = Wfd%ngfft(3)
268 0 : nfft = Wfd%nfft ! No FFT parallelism
269 :
270 0 : ABI_MALLOC(irottb,(nfft,Cryst%nsym))
271 0 : call rotate_FFT_mesh(Cryst%nsym,Cryst%symrel,Cryst%tnons,Wfd%ngfft,irottb,iscompatibleFFT)
272 :
273 0 : if (.not.iscompatibleFFT) then
274 : write(msg,'(3a)')&
275 0 : ' For symmetry analysis, the real space FFT mesh must be compatible with the symmetries of the space group',ch10,&
276 0 : ' classify_bands will return. Action: change the input variable ngfftf '
277 0 : ABI_WARNING(msg)
278 0 : Bsym%err_status=1
279 0 : Bsym%err_msg= msg
280 0 : RETURN
281 : end if
282 :
283 : ! only_trace=if .TRUE. only the trace of a single matrix per class is calculated (standard procedure if
284 : ! only the symmetry of bands is required). If .FALSE. all the matrices for each irreducible representation
285 : ! are calculated and stored in BSym
286 0 : only_trace=.FALSE.
287 : !
288 : ! ==========================================
289 : ! ==== Analyse k-point symmetries first ====
290 : ! ==========================================
291 : ! The analysis is done here so that we already know if there is a problem.
292 0 : kpt = Wfd%kibz(:,ik_ibz)
293 : !
294 : !----Initialize the Bsym structure for this k-point and spin----!
295 : ! NOTE that all the degenerate states should be included! No check is done.
296 :
297 0 : ene_k => ebands%eig(first_band:,ik_ibz,spin) ! Select a slice of eigenvalues
298 :
299 0 : call Bsym%init(kpt, Cryst, only_trace, Wfd%nspinor, first_band, last_band, EDIFF_TOL_, ene_k, tolsym)
300 : !Bsym%degs_bounds = Bsym%degs_bounds + (first_band -1)
301 :
302 0 : if (Bsym%err_status /= 0) then
303 0 : write(msg,'(a,i0,a)')" esymm_init returned err_status= ",Bsym%err_status," Band classifications cannot be performed."
304 0 : ABI_WARNING(msg)
305 0 : RETURN
306 : end if
307 :
308 0 : do ii=1,Cryst%nsym
309 0 : call mati3inv(Cryst%symrel(:,:,ii),trial)
310 0 : trial=transpose(trial)
311 0 : found=.FALSE.
312 0 : do jj=1,Cryst%nsym
313 0 : if (ALL(trial==Cryst%symrel(:,:,jj))) then
314 0 : toinv(ii)=jj
315 : !toinv(jj)=ii
316 : found=.TRUE.; EXIT
317 : end if
318 : end do
319 0 : if (.not.found) then
320 0 : ABI_ERROR("inverse not found! ")
321 : end if
322 : end do
323 :
324 0 : nullify(zarot)
325 :
326 0 : if (Wfd%usepaw==1) then ! Allocate cprj_k and cprj_krot to store a set of bands for a single (K,SPIN).
327 0 : ABI_MALLOC(Cprj_b1 ,(Cryst%natom,Wfd%nspinor))
328 0 : call pawcprj_alloc(Cprj_b1, 0,Wfd%nlmn_atm)
329 0 : ABI_MALLOC(Cprj_b2 ,(Cryst%natom,Wfd%nspinor))
330 0 : call pawcprj_alloc(Cprj_b2, 0,Wfd%nlmn_atm)
331 0 : ABI_MALLOC(Cprj_b2rot,(Cryst%natom,Wfd%nspinor))
332 0 : call pawcprj_alloc(Cprj_b2rot,0,Wfd%nlmn_atm)
333 :
334 : !zarot => Pawang%zarot
335 0 : lmax = Pawang%l_max-1
336 0 : ABI_MALLOC(zarot,(2*lmax+1,2*lmax+1,lmax+1,Cryst%nsym))
337 0 : zarot = Pawang%zarot
338 :
339 0 : ABI_MALLOC(tmp_sym,(3,3,Cryst%nsym))
340 0 : do isym=1,Cryst%nsym
341 0 : tmp_sym(:,:,isym) = Cryst%symrel(:,:,isym)
342 : !tmp_sym(:,:,isym) = Cryst%symrel(:,:,toinv(isym))
343 : !tmp_sym(:,:,isym) = transpose(Cryst%symrel(:,:,isym))
344 : !tmp_sym(:,:,isym) = Cryst%symrec(:,:,isym)
345 : !tmp_sym(:,:,isym) = TRANSPOSE(Cryst%symrec(:,:,isym))
346 : end do
347 : !% call setsym_ylm(Cryst%rprimd,lmax,Cryst%nsym,3,Cryst%gprimd,tmp_sym,zarot)
348 : !call setsym_ylm(Cryst%gprimd,lmax,Cryst%nsym,1,Cryst%rprimd,tmp_sym,zarot)
349 0 : ABI_FREE(tmp_sym)
350 0 : zarot = Pawang%zarot
351 :
352 0 : cplex=1
353 0 : call pawtab_get_lsize(Pawtab,l_size_atm,Cryst%natom,Cryst%typat)
354 0 : ABI_MALLOC(Pawfgrtab,(Cryst%natom))
355 0 : call pawfgrtab_init(Pawfgrtab,cplex,l_size_atm,Wfd%nspden,Cryst%typat)
356 0 : ABI_FREE(l_size_atm)
357 :
358 0 : optcut=1 ! use rpaw to construct local_pawfgrtab
359 0 : optgr0=0; optgr1=0; optgr2=0 ! dont need gY terms locally
360 0 : optrad=1 ! do store r-R
361 :
362 : call nhatgrid(Cryst%atindx1,Cryst%gmet,Cryst%natom,Cryst%natom,Cryst%nattyp,Wfd%ngfft,Cryst%ntypat,&
363 0 : optcut,optgr0,optgr1,optgr2,optrad,Pawfgrtab,pawtab,Cryst%rprimd,Cryst%typat,Cryst%ucvol,Cryst%xred)
364 :
365 : !call pawfgrtab_print(Pawfgrtab,unit=std_out,Wfd%prtvol=10)
366 :
367 0 : ABI_MALLOC(Paw_onsite,(Cryst%natom))
368 :
369 0 : if (use_paw_aeur) then
370 0 : ABI_WARNING("Using AE wavefunction for rotation in real space!")
371 : call paw_pwaves_lmn_init(Paw_onsite,Cryst%natom,Cryst%natom,Cryst%ntypat,&
372 0 : Cryst%rprimd,Cryst%xcart,Pawtab,Pawrad,Pawfgrtab)
373 : end if
374 : end if
375 :
376 : ! ===============================================
377 : ! ==== Calculate the representation matrices ====
378 : ! ===============================================
379 0 : fft_fact=one/nfft
380 0 : ABI_MALLOC(ur1, (nfft))
381 0 : ABI_MALLOC(ur2, (nfft))
382 0 : ABI_MALLOC(ur2_rot, (nfft))
383 :
384 : ! Precalculate eig0r = e^{iG0.r} on the FFT mesh.
385 0 : ABI_MALLOC(eig0r, (nfft, Bsym%nsym_gk))
386 :
387 0 : do isym=1,Bsym%nsym_gk
388 0 : g0 = Bsym%g0(:,isym)
389 0 : call calc_ceigr(g0,nfft,nspinor1,Wfd%ngfft,eig0r(:,isym))
390 : end do
391 :
392 0 : if (Bsym%can_use_tr) then
393 0 : ABI_MALLOC(tr_emig0r,(nfft,Bsym%nsym_trgk))
394 0 : do isym=1,Bsym%nsym_trgk
395 0 : g0=Bsym%tr_g0(:,isym)
396 0 : call calc_ceigr(-g0,nfft,nspinor1,Wfd%ngfft,tr_emig0r(:,isym))
397 : end do
398 : end if
399 :
400 : ! Loop over the set of degenerate states.
401 0 : do idg=1,Bsym%ndegs
402 0 : ib_start = Bsym%degs_bounds(1,idg)
403 0 : ib_stop = Bsym%degs_bounds(2,idg)
404 0 : dim_degs = Bsym%degs_dim(idg)
405 :
406 0 : do ib1=ib_start,ib_stop ! First band index in the degenerate set.
407 0 : jb1=ib1-ib_start+1
408 :
409 : ! debugging: use AE wave on dense FFT mesh.
410 0 : if (Wfd%usepaw==1..and.use_paw_aeur) then
411 0 : call wfd%paw_get_aeur(ib1,ik_ibz,spin,Cryst,Paw_onsite,Psps,Pawtab,Pawfgrtab,ur1)
412 : else
413 0 : call wfd%get_ur(ib1,ik_ibz,spin,ur1)
414 0 : if (Wfd%usepaw==1) then
415 0 : call wfd%ug2cprj(ib1,ik_ibz,spin,1,0,Cryst%natom,Cryst,Cprj_b1,sorted=.FALSE.)
416 : end if
417 : end if
418 :
419 0 : do ib2=ib_start,ib_stop ! Second band index in the degenerate set.
420 0 : if (Bsym%only_trace.and.ib1/=ib2) CYCLE ! Only the diagonal is needed.
421 :
422 0 : if (ib2==ib1) then
423 0 : call xcopy(nfft,ur1,1,ur2,1)
424 0 : if (Wfd%usepaw==1) call pawcprj_copy(Cprj_b1,Cprj_b2)
425 : else
426 : ! debugging: use AE wave on dense FFT mesh.
427 0 : if (Wfd%usepaw==1.and.use_paw_aeur) then
428 0 : call wfd%paw_get_aeur(ib2,ik_ibz,spin,Cryst,Paw_onsite,Psps,Pawtab,Pawfgrtab,ur2)
429 : else
430 0 : call wfd%get_ur(ib2,ik_ibz,spin,ur2)
431 0 : if (Wfd%usepaw==1) then
432 0 : call wfd%ug2cprj(ib2,ik_ibz,spin,1,0,Cryst%natom,Cryst,Cprj_b2,sorted=.FALSE.)
433 : end if
434 : end if
435 : end if
436 :
437 : ! ===================================================
438 : ! ==== Loop over the classes of the little group ====
439 : ! ===================================================
440 0 : sym_idx=0
441 0 : do iclass=1,Bsym%nclass
442 0 : nsym_class = Bsym%nelements(iclass)
443 :
444 0 : do isym_class=1,nsym_class ! Loop over elements in each class.
445 0 : sym_idx = sym_idx+1
446 0 : if (Bsym%only_trace.and.isym_class/=1) CYCLE ! Do it once if only the character is required.
447 :
448 0 : isym = Bsym%sgk2symrec(sym_idx)
449 0 : Rm1_rmt => irottb(:,isym)
450 :
451 : ! Classify states according to the irreps of the little group of k.
452 0 : kpg0= kpt + Bsym%g0(:,sym_idx)
453 0 : arg=-two_pi * DOT_PRODUCT(kpg0,Cryst%tnons(:,isym))
454 :
455 0 : if (ABS(arg) > tol6) then
456 0 : exp_mikg0t = DCMPLX(DCOS(arg),DSIN(arg))
457 : else
458 : exp_mikg0t = cone
459 : end if
460 :
461 : !if (Wfd%usepaw==1) then
462 : !end if
463 : !
464 : ! Rotate the right wave function and apply the phase ===
465 : ! Note that the k-point is the same within a lattice vector.
466 0 : do ir=1,nfft
467 0 : ur2_rot(ir)=ur2(Rm1_rmt(ir))*eig0r(ir,sym_idx)
468 : end do
469 :
470 : ! The matrix element on the FFT mesh.
471 0 : cmat_ab = xdotc(nfft,ur1,1,ur2_rot,1)*fft_fact*exp_mikg0t
472 :
473 0 : if (Wfd%usepaw==1.and..not.use_paw_aeur) then ! Add the on-site contribution.
474 0 : call rotate_cprj(kpt,isym,Wfd%nspinor,1,Cryst%natom,Cryst%nsym,Cryst%typat,Cryst%indsym,Cprj_b2,Cprj_b2rot)
475 :
476 : omat = paw_phirotphj(Wfd%nspinor,Cryst%natom,Cryst%typat,&
477 0 : zarot(:,:,:,isym),Pawtab,Psps,Cprj_b1,Cprj_b2rot)
478 :
479 0 : cmat_ab = cmat_ab + DCMPLX(omat(1),omat(2)) !* exp_mikg0t
480 : end if
481 :
482 0 : jb2 = ib2 - ib_start+1
483 0 : Bsym%Calc_irreps(idg)%mat(jb1,jb2,sym_idx)=cmat_ab
484 :
485 : end do !isym_class
486 : end do !iclass
487 : !
488 : ! =========================================================
489 : ! ==== Loop over the symmetries such that -Sk = k + G0 ====
490 : ! =========================================================
491 : ! <-k,a| S |k b> = e^{i(k+G0).t} \int e^{-ig0.r} u_a u_b(R^{1}(r-t))
492 0 : if (Bsym%can_use_tr) then
493 0 : do tr_isym=1,Bsym%nsym_trgk
494 :
495 0 : isym = Bsym%tr_sgk2symrec(tr_isym)
496 0 : Rm1_rmt => irottb(:,isym)
497 :
498 0 : kpg0= kpt + Bsym%tr_g0(:,tr_isym)
499 0 : arg= two_pi * DOT_PRODUCT(kpg0,Cryst%tnons(:,isym))
500 :
501 0 : if (ABS(arg) > tol6) then
502 0 : exp_ikg0t=DCMPLX(DCOS(arg),DSIN(arg))
503 : else
504 : exp_ikg0t=cone
505 : end if
506 :
507 : ! Rotate the right wave function and apply the phase
508 : ! Note that the k-point is the same within a lattice vector.
509 0 : do ir=1,nfft
510 0 : ur2_rot(ir)=ur2(Rm1_rmt(ir)) * tr_emig0r(ir,tr_isym)
511 : end do
512 :
513 : ! The matrix element on the FFT mesh.
514 0 : cmat_ab = xdotu(nfft,ur1,1,ur2_rot,1)*fft_fact*exp_ikg0t
515 :
516 0 : if (Wfd%usepaw==1.and..not.use_paw_aeur) then ! Add the on-site contribution. ! TODO rechek this part.
517 0 : call rotate_cprj(kpt,isym,Wfd%nspinor,1,Cryst%natom,Cryst%nsym,Cryst%typat,Cryst%indsym,Cprj_b2,Cprj_b2rot)
518 : omat = paw_phirotphj(Wfd%nspinor,Cryst%natom,Cryst%typat,&
519 0 : zarot(:,:,:,isym),Pawtab,Psps,Cprj_b1,Cprj_b2rot,conjg_left=.TRUE.)
520 0 : cmat_ab = cmat_ab + DCMPLX(omat(1),omat(2)) !* exp_ikg0t
521 : end if
522 :
523 0 : jb2 = ib2 - ib_start+1
524 0 : Bsym%trCalc_irreps(idg)%mat(jb1,jb2,tr_isym)=cmat_ab
525 : end do ! tr_isym
526 : end if
527 :
528 : end do !ib2
529 : end do !ib1
530 :
531 : ! Calculate the trace for each class.
532 0 : if (Bsym%only_trace) then ! TODO this is valid if only trace.
533 0 : ABI_ERROR("Have to reconstruct missing traces")
534 : else
535 0 : do isym=1,Bsym%nsym_gk
536 0 : Bsym%Calc_irreps(idg)%trace(isym) = get_trace( Bsym%Calc_irreps(idg)%mat(:,:,isym) )
537 : end do
538 0 : if (Bsym%can_use_tr) then
539 0 : do tr_isym=1,Bsym%nsym_trgk
540 0 : Bsym%trCalc_irreps(idg)%trace(tr_isym) = get_trace( Bsym%trCalc_irreps(idg)%mat(:,:,tr_isym) )
541 : end do
542 : end if
543 : end if
544 :
545 : end do ! idg
546 :
547 0 : call Bsym%finalize(Wfd%prtvol)
548 0 : call Bsym%print([std_out, ab_out], prtvol=Wfd%prtvol)
549 :
550 : ! Free memory
551 0 : ABI_FREE(irottb)
552 0 : ABI_FREE(ur1)
553 0 : ABI_FREE(ur2)
554 0 : ABI_FREE(ur2_rot)
555 0 : ABI_FREE(eig0r)
556 0 : ABI_SFREE(tr_emig0r)
557 :
558 0 : if (Wfd%usepaw==1) then
559 0 : call pawcprj_free(Cprj_b1)
560 0 : ABI_FREE(Cprj_b1)
561 0 : call pawcprj_free(Cprj_b2)
562 0 : ABI_FREE(Cprj_b2)
563 0 : call pawcprj_free(Cprj_b2rot)
564 0 : ABI_FREE(Cprj_b2rot)
565 0 : ABI_FREE(zarot)
566 0 : call pawfgrtab_free(Pawfgrtab)
567 0 : ABI_FREE(Pawfgrtab)
568 0 : call paw_pwaves_lmn_free(Paw_onsite)
569 0 : ABI_FREE(Paw_onsite)
570 : end if
571 :
572 0 : end subroutine classify_bands
573 : !!***
574 :
575 : !----------------------------------------------------------------------
576 :
577 : !!****f* m_classify_bands/rotate_cprj
578 : !! NAME
579 : !! rotate_cprj
580 : !!
581 : !! FUNCTION
582 : !! Rotate cprj matrix elements by applying the symmetry operation of index isym
583 : !! that preserves the given k-point within a reciprocal lattice vector.
584 : !!
585 : !! INPUTS
586 : !! isym=index of the symmetry in the symrec arrays that preserves the given k-point within a reciprocal lattice vector
587 : !! ntypat=number of types of atom.
588 : !! natom=number of atoms.
589 : !! Cryst<crystal_t>=Datatype gathering info on the unit cell.
590 : !! typat(natom)=type of each atom.
591 : !! nbnds=number of bands for this k-point ans spin
592 : !! Cprj_in(natom,nbnds)<type(pawcprj_type)>= projected input wave functions <Proj_i|Cnk>
593 : !! with all NL projectors at fixed k-point
594 : !!
595 : !! OUTPUT
596 : !! Cprj_out(natom,nbnds) <type(pawcprj_type)>= projection of the smooth PAW wave function onto
597 : !! projectors centered on equivalent sites of the crystal (non restricted to be in the firs unit cell)
598 : !! The equivalent site is defined according to the symmetry operation isym. Thus Cprj_out contains
599 : !!
600 : !! Cprj_out(at,b)=<p_j^{R^{-1}(L_{at}-\tau)} | \tpsi_b> if R is the isym operation with fractional translation \tau
601 : !! L_{at} is the position of the initial atom inside the first unit cell
602 : !! Note that atom a might be in a cell different from the initial one. No wrapping is done.
603 : !!
604 : !! SOURCE
605 :
606 0 : subroutine rotate_cprj(kpoint,isym,nspinor,nbnds,natom,nsym,typat,indsym,Cprj_in,Cprj_out)
607 :
608 : !Arguments ------------------------------------
609 : !scalars
610 : integer,intent(in) :: nbnds,nspinor,natom,isym,nsym
611 : !arrays
612 : integer,intent(in) :: typat(natom),indsym(4,nsym,natom)
613 : real(dp),intent(in) :: kpoint(3)
614 : type(pawcprj_type),intent(in) :: Cprj_in(natom,nspinor*nbnds)
615 : type(pawcprj_type),intent(out) :: Cprj_out(natom,nspinor*nbnds)
616 :
617 : !Local variables-------------------------------
618 : !scalars
619 : integer :: iat,iband,itypat,iat_sym
620 : real(dp) :: kdotr0
621 : !arrays
622 : integer :: r0(3)
623 : real(dp) :: phase_kr0(2)
624 : ! *************************************************************************
625 :
626 0 : do iat=1,natom
627 0 : itypat=typat(iat)
628 : ! The index of the symmetric atom.
629 : ! R^{-1} (xred(:,iat)-tnons) = xred(:,iat_sym) + r0.
630 : ! phase_kr0 takes into account the case in which rotated atom is in another unit cell.
631 0 : iat_sym=indsym(4,isym,iat); r0=indsym(1:3,isym,iat)
632 :
633 0 : kdotr0 = two_pi*DOT_PRODUCT(kpoint,r0)
634 0 : phase_kr0(1) = DCOS(kdotr0)
635 0 : phase_kr0(2) = DSIN(kdotr0)
636 :
637 : !phase_kr0 = (/one,zero/)
638 :
639 0 : do iband=1,nspinor*nbnds
640 : Cprj_out(iat,iband)%cp(1,:)= Cprj_in(iat_sym,iband)%cp(1,:)*phase_kr0(1) &
641 0 : -Cprj_in(iat_sym,iband)%cp(2,:)*phase_kr0(2)
642 :
643 : Cprj_out(iat,iband)%cp(2,:)= Cprj_in(iat_sym,iband)%cp(1,:)*phase_kr0(2) &
644 0 : +Cprj_in(iat_sym,iband)%cp(2,:)*phase_kr0(1)
645 : end do
646 : end do ! iat
647 :
648 0 : end subroutine rotate_cprj
649 : !!***
650 :
651 : !----------------------------------------------------------------------
652 :
653 : !!****f* m_classify_bands/paw_phirotphj
654 : !! NAME
655 : !! paw_phirotphj
656 : !!
657 : !! FUNCTION
658 : !! This routine calculates
659 : !! <\tPsi_1|\tprj_i> <\tprj_j|\tPsi_2> [ <\phi_i|\phi_j(R^{-1}r> - <\tphi_i|\tphi_j(R^{-1}r> ]
660 : !!
661 : !! [ <\phi_i|\phi_j(R^{-1}r> - <\tphi_i|\tphi_j(R^{-1}r> ] = s_ij D_{mi,mi}^{li}(R)
662 : !!
663 : !! INPUTS
664 : !! nspinor=Number of spinorial components.
665 : !! natom=number of atoms
666 : !! typat(natom)=type of eahc atom
667 : !! zarot_isym
668 : !! Pawtab(ntypat)<Pawtab_type>=PAW tabulated starting data
669 : !! Psps<pseudopotential_type>=Info on pseudopotentials.
670 : !! Cprj_b1(natom,nspinor)<type(pawcprj_type)>= projected input wave functions <Proj_i|Cnk>
671 : !! with all NL projectors at fixed k-point
672 : !! Cprj_b2(natom,nspinor)<type(pawcprj_type)>= projected input wave functions <Proj_i|Cnk>
673 : !! with all NL projectors at fixed k-point
674 : !! [conjg_left]=.TRUE if the complex conjugate of the left wavefunctions has to be taken. Defaults to .FALSE.
675 : !!
676 : !! OUTPUT
677 : !! omat(2)=The onsite matrix element.
678 : !!
679 : !! SOURCE
680 :
681 0 : function paw_phirotphj(nspinor,natom,typat,zarot_isym,Pawtab,Psps,Cprj_b1,Cprj_b2,conjg_left) result(omat)
682 :
683 : !Arguments ------------------------------------
684 : !scalars
685 : integer,intent(in) :: nspinor,natom
686 : logical,optional,intent(in) :: conjg_left
687 : type(pseudopotential_type),intent(in) :: Psps
688 : !arrays
689 : integer,intent(in) :: typat(natom)
690 : real(dp),intent(in) :: zarot_isym(:,:,:)
691 : real(dp) :: omat(2)
692 : type(Pawtab_type),intent(in) :: Pawtab(Psps%ntypat)
693 : type(pawcprj_type),intent(in) :: Cprj_b1(natom,nspinor),Cprj_b2(natom,nspinor)
694 :
695 : !Local variables-------------------------------
696 : !scalars
697 : integer :: iat,il,ilmn,ilpm,im,itypat,jl,jlmn,jlpm,jm,k0lmn,klmn,nlmn
698 : real(dp) :: dmimj,fij,im_p,re_p,sij
699 : logical :: do_conjg_left
700 : ! *************************************************************************
701 :
702 0 : do_conjg_left = .FALSE.; if (PRESENT(conjg_left)) do_conjg_left = conjg_left
703 :
704 0 : if (nspinor/=1) then
705 0 : ABI_ERROR("nspinor/=1 not yet coded")
706 : end if
707 :
708 : ! === Rotate PAW projections ===
709 : ! * zarot_isym is the rotation matrix of real spherical harmonics associated to symrec(:,:,isym).
710 : ! * zarot_isym multiply harmonics as row vectors, we need R^{-1} but we read R and invert m,mp in the equation below
711 0 : omat=zero
712 :
713 0 : do iat=1,natom
714 0 : itypat=typat(iat)
715 0 : nlmn=Pawtab(itypat)%lmn_size
716 :
717 0 : do jlmn=1,nlmn
718 0 : k0lmn=jlmn*(jlmn-1)/2
719 0 : jl=Psps%indlmn(1,jlmn,itypat)
720 0 : jm=Psps%indlmn(2,jlmn,itypat)
721 0 : jlpm=1+jl+jm
722 :
723 0 : do ilmn=1,jlmn
724 0 : il=Psps%indlmn(1,ilmn,itypat)
725 0 : im=Psps%indlmn(2,ilmn,itypat)
726 0 : if (il/=jl.or.im/=jm) CYCLE ! Selection rule on l and m.
727 0 : ilpm=1+il+im
728 :
729 0 : klmn=k0lmn+ilmn
730 0 : sij=Pawtab(itypat)%sij(klmn) !; if (ABS(sij)<tol14) CYCLE
731 :
732 : ! Here we get the matrix associated to R^{-1}.
733 0 : dmimj=zarot_isym(ilpm,jlpm,jl+1)
734 :
735 0 : if (do_conjg_left) then ! take the complex conjugate of the left cprj.
736 : re_p= Cprj_b1(iat,1)%cp(1,ilmn) * Cprj_b2(iat,1)%cp(1,jlmn) &
737 : -Cprj_b1(iat,1)%cp(2,ilmn) * Cprj_b2(iat,1)%cp(2,jlmn) &
738 : +Cprj_b1(iat,1)%cp(1,jlmn) * Cprj_b2(iat,1)%cp(1,ilmn) &
739 0 : -Cprj_b1(iat,1)%cp(2,jlmn) * Cprj_b2(iat,1)%cp(2,ilmn)
740 :
741 : im_p= Cprj_b1(iat,1)%cp(1,ilmn) * Cprj_b2(iat,1)%cp(2,jlmn) &
742 : +Cprj_b1(iat,1)%cp(2,ilmn) * Cprj_b2(iat,1)%cp(1,jlmn) &
743 : -Cprj_b1(iat,1)%cp(1,jlmn) * Cprj_b2(iat,1)%cp(2,ilmn) &
744 0 : -Cprj_b1(iat,1)%cp(2,jlmn) * Cprj_b2(iat,1)%cp(1,ilmn)
745 : else
746 : re_p= Cprj_b1(iat,1)%cp(1,ilmn) * Cprj_b2(iat,1)%cp(1,jlmn) &
747 : +Cprj_b1(iat,1)%cp(2,ilmn) * Cprj_b2(iat,1)%cp(2,jlmn) &
748 : +Cprj_b1(iat,1)%cp(1,jlmn) * Cprj_b2(iat,1)%cp(1,ilmn) &
749 0 : +Cprj_b1(iat,1)%cp(2,jlmn) * Cprj_b2(iat,1)%cp(2,ilmn)
750 :
751 : im_p= Cprj_b1(iat,1)%cp(1,ilmn) * Cprj_b2(iat,1)%cp(2,jlmn) &
752 : -Cprj_b1(iat,1)%cp(2,ilmn) * Cprj_b2(iat,1)%cp(1,jlmn) &
753 : +Cprj_b1(iat,1)%cp(1,jlmn) * Cprj_b2(iat,1)%cp(2,ilmn) &
754 0 : -Cprj_b1(iat,1)%cp(2,jlmn) * Cprj_b2(iat,1)%cp(1,ilmn)
755 : end if
756 : ! Accumulate the atom-centered contributions.
757 0 : fij = Pawtab(itypat)%dltij(klmn)/two
758 0 : omat(1)= omat(1) + fij*sij*re_p*dmimj
759 0 : omat(2)= omat(2) + fij*sij*im_p*dmimj
760 :
761 : end do !ilmn
762 : end do !jlmn
763 : end do !iat
764 :
765 0 : end function paw_phirotphj
766 : !!***
767 :
768 : !----------------------------------------------------------------------
769 :
770 : !!****f* m_classify_bands/dmats_init
771 : !! NAME
772 : !! dmats_init
773 : !!
774 : !! FUNCTION
775 : !! Compute D_mn(S) = <psi_{mSk}| S | psi_{nk}> for all the k-points in the IBZ and the bands in brange_spin.
776 : !!
777 : !! INPUTS
778 : !! wfk_path=Filename of the WFK file.
779 : !!
780 : !! NOTES
781 : !! Little-group membership and the umklapp vector G_0 associated to each symmetry
782 : !! are determined with the SAME k-point convention used by cgtk_rotate's own
783 : !! bookkeeping (see its docstring in m_cgtk.F90), namely:
784 : !!
785 : !! k2 = T symrel(:,:,isym)^t k1 + G_0 (transpose of symrel, NOT symrec)
786 : !!
787 : !! where T=+1/-1 without/with time reversal. This is also the convention produced
788 : !! by listkk (default, symrel-based) and consumed by cgtk_rotate elsewhere in the
789 : !! code (e.g. m_wfd.F90), so it is safe to reuse directly here.
790 : !!
791 : !! However, cgtk_rotate cannot simply be called with S=isym to obtain D(S_isym):
792 : !! its actual G-sphere index map is cg2(G) = cg1(symrec(isym).(G+G_0)), with
793 : !! symrec(isym) = mati3inv(symrel(isym)) = symrel(isym)^{-t} applied FORWARD
794 : !! (no additional inversion). Deriving the Fourier-coefficient transform of
795 : !! psi(r) -> psi(symrel(isym)^{-1}(r-tau)) shows that the coefficient at the
796 : !! rotated G must instead be read at symrel(isym)^t . G. The two matrices,
797 : !! symrel(isym)^{-t} and symrel(isym)^t, coincide only when symrel(isym) is an
798 : !! involution (S^2 = E, e.g. the identity or spatial inversion). For any other
799 : !! operation (3-, 4-, 6-fold rotations, screw axes, glide planes, ...) calling
800 : !! cgtk_rotate(isym) therefore silently returns D_true(S_isym)^{-1} = D_true(S_isym^{-1})
801 : !! instead of D_true(S_isym).
802 : !!
803 : !! This was confirmed empirically: with cgtk_rotate called on isym directly, the
804 : !! group-multiplication test in dmats_check (D(S_1 S_2) \propto D(S_1) D(S_2), see
805 : !! below) failed for essentially every triple involving a non-involutory operation,
806 : !! while unitarity and the D(S^{-1})=D(S)^dagger self-consistency test still passed
807 : !! (an involution-blind bug: D_true(S)^{-1} is unitary and equals D_true(S)^{-1}
808 : !! trivially, so those two checks cannot detect it). Concretely, for a triple
809 : !! (S_1, S_2, S_3=S_1 S_2) with zero fractional translations, the stored matrices
810 : !! satisfied D(S_3) = D(S_1) D(S_2)^t rather than D(S_3) = D(S_1) D(S_2).
811 : !!
812 : !! The fix is to call cgtk_rotate with isym_inv, the group-theoretic inverse of
813 : !! isym (found from the symrel multiplication table), while still filling the
814 : !! storage slot for isym: D_computed(isym_inv) = D_true(isym_inv^{-1}) = D_true(isym).
815 : !! isym_inv's own G_0 is recomputed with the formula above (using isym_inv instead
816 : !! of isym); no ad-hoc override of its fractional translation is needed, since
817 : !! cgtk_rotate is now called honestly for the operation it is actually asked to
818 : !! apply. See the inline comments in the k-point/symmetry loop below for the
819 : !! implementation.
820 : !!
821 : !! SOURCE
822 :
823 0 : subroutine dmats_init(dmats, wfk_path, dtset, cryst, brange_spin, ngfft, pawtab, psps, comm)
824 :
825 : !Arguments ------------------------------------
826 : !scalars
827 : class(dmats_t),intent(out) :: dmats
828 : character(len=*),intent(in) :: wfk_path
829 : type(dataset_type),target,intent(in) :: dtset
830 : class(crystal_t),target,intent(in) :: cryst
831 : integer,intent(in) :: brange_spin(2, dtset%nsppol), ngfft(18)
832 : integer,intent(in) :: comm
833 : type(pseudopotential_type),intent(in) :: psps
834 : type(pawtab_type),intent(in) :: pawtab(psps%ntypat*psps%usepaw)
835 :
836 : !Local variables-------------------------------
837 : !scalars
838 : integer,parameter :: iflag1 = 1, me_g0 = 1, ndat1 = 1
839 : integer :: spin, nsppol, nsym, nb, nkibz, mband, ik_ibz, isym, isym_inv, itime, bstart, trev_k
840 : logical,parameter :: DEBUG_DUMP_SPINROT = .False.
841 : real(dp) :: spinrot_dbg(4)
842 : integer :: ib1, ib2, band1, band2, n1, n2, n3, n4, n5, n6, nfft, nspinor, mpw, my_mpw, ii, ipw
843 : integer :: nprocs, me, itot, ierr
844 : logical :: is_little_group
845 : real(dp),parameter :: xnorm1 = one
846 : real(dp) :: e_b1, e_b2, cpu, wall, gflops, tsign
847 0 : type(wfd_t) :: wfd
848 0 : type(hdr_type) :: hdr
849 0 : type(krank_t) :: my_krank
850 : ! Non-little-group (star-crossing) D-matrix computation.
851 : integer :: ik_ibz_p, isym_p, trev_p, isym_inv_S, npw_kp
852 : integer :: g0_ket(3)
853 0 : integer,allocatable :: kg_kp(:,:)
854 0 : real(dp),allocatable :: cg_bra(:,:,:), cg_ket(:,:), cg_work_p(:,:)
855 : !arrays
856 : integer :: g0_k(3), g0_k_inv(3), gmax(3), my_gmax(3), work_ngfft(18), units(2)
857 : integer :: indkk_p(6,1)
858 0 : integer,allocatable :: nband(:,:), wfd_istwfk(:)
859 : real(dp) :: kk_ibz(3), kk_sk(3), kk_sk_inv(3), dot(2)
860 0 : real(dp),allocatable :: cg_ib(:,:,:), cg_work(:,:), work(:,:,:,:), cg2_sk(:,:)
861 : complex(dp) :: cval
862 0 : complex(dp),allocatable :: cmat(:,:)
863 0 : logical,allocatable :: bks_mask(:,:,:),keep_ur(:,:,:)
864 : !----------------------------------------------------------------------
865 :
866 0 : units = [std_out, ab_out]
867 0 : call cwtime(cpu, wall, gflops, "start")
868 0 : call wrtout(units, sjoin(" Computing dmats with symsigma_de", ftoa(dtset%symsigma_de * Ha_meV), " meV"))
869 :
870 0 : ABI_CHECK_IEQ(dtset%usepaw, 0, "PAW not coded!")
871 : ! NB: the D-matrix construction below is nspinor-agnostic: it is built entirely on top of
872 : ! cgtk_rotate (which already implements the SU(2) spin rotation + time-reversal spin-exchange
873 : ! for nspinor=2, see m_cgtk.F90) and cg_zdotc (a plain length-parametrized dot product that,
874 : ! called with n=npw_k*nspinor, sums over the spinor components as well as the G-vectors).
875 : ! So nspinor=2 (SOC) is supported here. The Kramers/Theta^2 VALIDATION diagnostics in
876 : ! dmats_check_one_k (only reachable via dmats%check/dmats%check_star, i.e. wfk_task "classify",
877 : ! never via gstore_symmetrize) are a separate matter and remain scalar-only for now: they encode
878 : ! an analytic phase formula derived assuming Theta^2=+1, which flips to Theta^2=-1 for spinors
879 : ! and has not been re-derived/verified yet -- see the explicit nspinor==1 guard added there.
880 :
881 : ! Read KS energies from the WFK file.
882 0 : dmats%ks_ebands = ebands_from_file(wfk_path, comm)
883 0 : dmats%cryst => cryst
884 0 : dmats%dtset => dtset
885 :
886 0 : nsppol = dmats%ks_ebands%nsppol; nsym = cryst%nsym; nkibz = dmats%ks_ebands%nkpt
887 0 : nprocs = xmpi_comm_size(comm); me = xmpi_comm_rank(comm)
888 :
889 : if (DEBUG_DUMP_SPINROT .and. xmpi_comm_rank(comm) == 0) then
890 : open(unit=792, file="spinrot_debug.csv", status="replace", action="write")
891 : do isym=1,nsym
892 : call getspinrot(cryst%rprimd, spinrot_dbg, cryst%symrel(:,:,isym))
893 : write(792,'(i0,1x,4(es24.16,1x))') isym, spinrot_dbg
894 : end do
895 : close(792)
896 : end if
897 :
898 0 : ABI_MALLOC(dmats%brange_spin, (2, nsppol))
899 0 : dmats%brange_spin = brange_spin
900 :
901 : ! Compute multiplication table.
902 0 : ABI_MALLOC(dmats%multable, (4, nsym, nsym))
903 0 : ABI_MALLOC(dmats%toinv, (4, nsym))
904 :
905 : call sg_multable(nsym, cryst%symafm, cryst%symrel, ierr, &
906 0 : tnons=cryst%tnons, multable=dmats%multable, toinv=dmats%toinv)
907 0 : ABI_CHECK_IEQ(ierr, 0, "sg_multable returned ierr != 0. See messages above.")
908 :
909 : ! Initialize the wave function descriptor.
910 0 : mband = maxval(brange_spin(2, :))
911 0 : ABI_MALLOC(nband, (nkibz, nsppol))
912 0 : ABI_MALLOC(bks_mask, (mband, nkibz, nsppol))
913 0 : ABI_MALLOC(keep_ur, (mband, nkibz, nsppol))
914 0 : nband = mband; bks_mask = .False.; keep_ur = .False.
915 :
916 : ! Every rank loads the FULL band range for EVERY IBZ k-point (not just the subset this
917 : ! rank "owns" for the D-matrix computation loop below). This is required by the
918 : ! non-little-group branch (see NOTES above): computing D_mn(S) for a general S needs the
919 : ! wavefunction at k'_ibz = S.k_ibz's own IBZ representative, which is not known until the
920 : ! symmetry loop runs and can be ANY IBZ point, not just the ones this rank would otherwise
921 : ! be assigned. The actual D-matrix COMPUTATION work (the ik_ibz loop below) is still
922 : ! MPI-distributed and xmpi_sum'd at the end -- only the wavefunction STORAGE is replicated.
923 0 : do spin=1,nsppol
924 0 : bks_mask(brange_spin(1,spin):brange_spin(2,spin), :, spin) = .True.
925 : end do
926 :
927 : ! Impose istwfk = 1 for all k-points.
928 : ! wfd_read_wfk will handle a possible conversion if WFK contains istwfk /= 1.
929 0 : ABI_MALLOC(wfd_istwfk, (nkibz))
930 0 : wfd_istwfk = 1
931 :
932 : call wfd%init(cryst, pawtab, psps, keep_ur, mband, nband, nkibz, nsppol, bks_mask,&
933 : dtset%nspden, dtset%nspinor, dtset%ecut, dtset%ecutsm, dtset%dilatmx, wfd_istwfk, dmats%ks_ebands%kptns, ngfft,&
934 0 : dtset%nloalg, dtset%prtvol, dtset%pawprtvol, comm)
935 : !call wfd%print([std_out], header="Wavefunctions for DMATS calculation")
936 :
937 0 : ABI_FREE(nband)
938 0 : ABI_FREE(keep_ur)
939 0 : ABI_FREE(wfd_istwfk)
940 0 : ABI_FREE(bks_mask)
941 :
942 : ! Read wavefunctions from WFK file.
943 0 : call wfd%read_wfk(wfk_path, iomode_from_fname(wfk_path), out_hdr=hdr)
944 :
945 : ! Energy cutoff must be the same else matrices are not unitary.
946 0 : call hdr%vs_dtset(dtset)
947 0 : ABI_CHECK(abs(dtset%ecut - hdr%ecut) < tol6, "Input ecut should be equal to the value used in the WFK file.")
948 0 : call hdr%free()
949 :
950 : ! krank_t used to map k' = S.k_ibz (any BZ point, not just IBZ ones) back to the IBZ,
951 : ! symrel^t convention, needed by the non-little-group D-matrix computation below.
952 0 : call my_krank%init(nkibz, dmats%ks_ebands%kptns)
953 :
954 : ! Compute max |G_i| to build the box.
955 0 : gmax = 0; mpw = 0
956 0 : do ik_ibz=1,nkibz
957 0 : if (.not. allocated(wfd%kdata(ik_ibz)%kg_k)) cycle
958 0 : associate (npw_k => wfd%npwarr(ik_ibz), kg_k => wfd%kdata(ik_ibz)%kg_k)
959 0 : mpw = max(mpw, npw_k)
960 0 : do ipw=1,npw_k
961 0 : do ii=1,3
962 0 : gmax(ii) = max(gmax(ii), abs(kg_k(ii,ipw)))
963 : end do
964 : end do
965 : end associate
966 : end do
967 0 : my_gmax = gmax; call xmpi_max(my_gmax, gmax, comm, ierr)
968 0 : my_mpw = mpw; call xmpi_max(my_mpw, mpw, comm, ierr)
969 :
970 : ! Init work_ngfft
971 0 : gmax = gmax + 4 ! FIXME: this is to account for umklapp
972 0 : gmax = 2*gmax + 1
973 0 : call ngfft_seq(work_ngfft, gmax)
974 : !write(std_out,*)"work_ngfft(1:3): ",work_ngfft(1:3)
975 0 : ABI_MALLOC(work, (2, work_ngfft(4), work_ngfft(5), work_ngfft(6)))
976 :
977 0 : n1 = work_ngfft(1); n2 = work_ngfft(2); n3 = work_ngfft(3); n4 = work_ngfft(4); n5 = work_ngfft(5); n6 = work_ngfft(6)
978 0 : nfft = n1 * n2 * n3
979 0 : nspinor = wfd%nspinor
980 :
981 : ! Allocate D matrices for each spin on each proc and fill with zeros as we will MPI sum at the end.
982 0 : ABI_MALLOC(dmats%for_spin, (nsppol))
983 0 : do spin=1,nsppol
984 0 : nb = brange_spin(2,spin) - brange_spin(1,spin) + 1
985 0 : ABI_CALLOC(dmats%for_spin(spin)%value, (nb, nb, nsym, 2, nkibz))
986 : end do
987 :
988 0 : do spin=1,nsppol
989 0 : bstart = brange_spin(1, spin)
990 0 : nb = brange_spin(2, spin) - brange_spin(1, spin) + 1
991 0 : ABI_MALLOC(cmat, (nb, nb))
992 :
993 : ! Loop over k-points in the IBZ.
994 0 : do ik_ibz=1,nkibz
995 0 : itot = ik_ibz + (spin - 1)*nkibz; if (mod(itot - 1, nprocs) /= me) cycle ! MPI parallelism.
996 :
997 : ! NB: istwf_k is always 1 here. See call to wfd%init.
998 0 : associate (npw_k => wfd%npwarr(ik_ibz), istwf_k => wfd%kdata(ik_ibz)%istwfk, kg_k => wfd%kdata(ik_ibz)%kg_k)
999 0 : kk_ibz = dmats%ks_ebands%kptns(:, ik_ibz)
1000 :
1001 : ! Copy wavefunctions for this k-point.
1002 0 : ABI_MALLOC(cg_work, (2, npw_k*nspinor))
1003 0 : ABI_MALLOC(cg_ib, (2, npw_k*nspinor, nb))
1004 0 : do ib1=1,nb
1005 0 : band1 = ib1 + bstart - 1
1006 0 : call wfd%copy_cg(band1, ik_ibz, spin, cg_ib(:,:,ib1))
1007 : end do
1008 :
1009 : ! Loop over time-reversal and spatial symmetries.
1010 : ! g0_k is built with the SAME k-point convention assumed by cgtk_rotate's
1011 : ! bookkeeping: k2 = T symrel(:,:,isym)^t k1 + g0 (transpose of symrel, not symrec).
1012 : ! cgtk_rotate is then called with isym_inv rather than isym: see the detailed
1013 : ! explanation of why this is required (cgtk_rotate(isym) actually returns
1014 : ! D_true(S_isym)^{-1}, invisibly for involutions) in the NOTES of this subroutine's
1015 : ! SOURCE header above.
1016 0 : do itime=1,2
1017 0 : tsign = merge(one, -one, itime == 1)
1018 0 : trev_k = itime - 1
1019 0 : do isym=1,cryst%nsym
1020 : ! Compute cmat(b,b')
1021 0 : cmat = zero
1022 :
1023 0 : kk_sk = tsign * matmul(transpose(real(cryst%symrel(:,:,isym), dp)), kk_ibz)
1024 0 : g0_k = nint(kk_ibz - kk_sk)
1025 0 : is_little_group = all(abs(kk_ibz - kk_sk - g0_k) < tol8)
1026 :
1027 0 : if (.not. is_little_group) then
1028 : ! Sk = k' /= k + G: genuinely compute D_mn(S) = <psi_m,k'|S|psi_n,k> from real
1029 : ! WFK data (this used to be an identity placeholder -- never correct, only
1030 : ! harmless because no caller ever read this slot; gstore_symmetrize and
1031 : ! dmats_check_one_k/dmats_check_star only ever query genuine little-group
1032 : ! slots). Two independent rotations are needed, expressed on a COMMON target
1033 : ! G-sphere built at the RAW (unreduced) k' = kk_sk:
1034 : !
1035 : ! (a) BRA |psi_m,k'>: reconstructed via kpts_map's own canonical (isym_p,
1036 : ! trev_p, g0_p) mapping k'_ibz -> k', calling cgtk_rotate with isym_p
1037 : ! DIRECTLY (no toinv step). This mirrors wfd_sym_ug_kg_npw's (m_wfd.F90)
1038 : ! own validated convention for "reconstruct THE wavefunction at a BZ
1039 : ! point" -- the same machinery gstore_compute already uses in production.
1040 : ! Any internally-consistent representative of |psi,k'> works here since
1041 : ! the bra is a STATE, not a specific operator's image (gauge-flexible).
1042 : !
1043 : ! (b) KET S|psi_n,k>: this DOES need the honest action of the SPECIFIC
1044 : ! operator S (not just some state), so the toinv workaround documented
1045 : ! above is required: call cgtk_rotate with X=toinv(1,isym), the SAME
1046 : ! tsign/trev_k as S. The g0 needed here is NOT zero in general (an
1047 : ! earlier version of this code assumed so and was wrong -- caught by a
1048 : ! temporary self-consistency check routing known little-group cases
1049 : ! through this same general formula and comparing against the existing,
1050 : ! validated little-group result: 4/208 disagreed by O(1), all at points
1051 : ! with a non-involutory isym AND a nonzero little-group umklapp). Root
1052 : ! cause: cgtk_rotate's documented k2=symrel(X)^t.kpt1+g0 "official"
1053 : ! bookkeeping formula does NOT, in general, track the k-point the CONTENT
1054 : ! is actually expressed at once X is swapped for toinv(X) -- that formula
1055 : ! only happens to hold for the little-group case because target=source
1056 : ! there, collapsing two genuinely different quantities into one. The
1057 : ! general, correct g0 (verified via the same self-check, 208/208 exact to
1058 : ! numerical noise): treat kk_sk (my desired physical target, built from
1059 : ! S's OWN forward action) the same way the little-group formula treats
1060 : ! kk_ibz -- i.e. g0 = nint(kk_sk - kk_sk_inv), kk_sk_inv being
1061 : ! toinv(isym)'s own forward action on kk_ibz (the direct generalization of
1062 : ! the existing little-group branch's kk_sk_inv/g0_k_inv, with kk_sk
1063 : ! substituted for kk_ibz as the reference point).
1064 0 : ierr = kpts_map("symrel", dtset%kptopt, cryst, my_krank, 1, kk_sk, indkk_p)
1065 0 : ABI_CHECK(ierr == 0, "Cannot find symmetric image of Sk in the IBZ")
1066 0 : ik_ibz_p = indkk_p(1,1); isym_p = indkk_p(2,1); trev_p = indkk_p(6,1)
1067 :
1068 0 : call get_kg(kk_sk, 1, dtset%ecut, cryst%gmet, npw_kp, kg_kp)
1069 :
1070 0 : ABI_MALLOC(cg_bra, (2, npw_kp*nspinor, nb))
1071 : associate (npw_kip => wfd%npwarr(ik_ibz_p), kg_kip => wfd%kdata(ik_ibz_p)%kg_k, &
1072 : istwf_kip => wfd%kdata(ik_ibz_p)%istwfk)
1073 0 : ABI_MALLOC(cg_work_p, (2, npw_kip*nspinor))
1074 0 : do ib1=1,nb
1075 0 : band1 = ib1 + bstart - 1
1076 0 : call wfd%copy_cg(band1, ik_ibz_p, spin, cg_work_p)
1077 : call cgtk_rotate(dmats%cryst, dmats%ks_ebands%kptns(:,ik_ibz_p), isym_p, trev_p, &
1078 : indkk_p(3:5,1), nspinor, ndat1, npw_kip, kg_kip, npw_kp, kg_kp, &
1079 0 : istwf_kip, 1, cg_work_p, cg_bra(:,:,ib1), work_ngfft, work)
1080 : end do
1081 0 : ABI_FREE(cg_work_p)
1082 : end associate
1083 :
1084 0 : isym_inv_S = dmats%toinv(1, isym)
1085 0 : kk_sk_inv = tsign * matmul(transpose(real(cryst%symrel(:,:,isym_inv_S), dp)), kk_ibz)
1086 0 : g0_ket = nint(kk_sk - kk_sk_inv)
1087 0 : ABI_MALLOC(cg_ket, (2, npw_kp*nspinor))
1088 :
1089 0 : do ib2=1,nb
1090 0 : band2 = ib2 + bstart - 1
1091 0 : e_b2 = dmats%ks_ebands%eig(band2, ik_ibz, spin)
1092 :
1093 : call cgtk_rotate(dmats%cryst, kk_ibz, isym_inv_S, trev_k, g0_ket, nspinor, ndat1, &
1094 0 : npw_k, kg_k, npw_kp, kg_kp, istwf_k, 1, cg_ib(:,:,ib2), cg_ket, work_ngfft, work)
1095 :
1096 0 : do ib1=1,nb
1097 0 : band1 = ib1 + bstart - 1
1098 0 : e_b1 = dmats%ks_ebands%eig(band1, ik_ibz_p, spin)
1099 :
1100 0 : cval = zero
1101 0 : if (abs(e_b2 - e_b1) <= dtset%symsigma_de) then
1102 0 : dot = cg_zdotc(npw_kp * nspinor, cg_bra(:,:,ib1), cg_ket)
1103 0 : cval = dot(1) + j_dpc * dot(2)
1104 : end if
1105 0 : cmat(ib1, ib2) = cval
1106 : end do
1107 : end do
1108 :
1109 0 : ABI_FREE(cg_ket)
1110 0 : ABI_FREE(cg_bra)
1111 0 : ABI_FREE(kg_kp)
1112 :
1113 : else
1114 : ! Find the group-theoretic inverse of isym.
1115 0 : isym_inv = dmats%toinv(1, isym)
1116 0 : ABI_CHECK(isym_inv /= 0, "Could not find inverse symmetry!")
1117 :
1118 0 : kk_sk_inv = tsign * matmul(transpose(real(cryst%symrel(:,:,isym_inv), dp)), kk_ibz)
1119 0 : g0_k_inv = nint(kk_ibz - kk_sk_inv)
1120 :
1121 0 : ABI_MALLOC(cg2_sk, (2, npw_k*nspinor))
1122 :
1123 0 : do ib2=1,nb
1124 0 : band2 = ib2 + bstart - 1
1125 0 : e_b2 = dmats%ks_ebands%eig(band2, ik_ibz, spin)
1126 :
1127 : ! Compute the periodic part of S |psi_nk>.
1128 : call cgtk_rotate(dmats%cryst, kk_ibz, isym_inv, trev_k, g0_k_inv, nspinor, ndat1, &
1129 : npw_k, kg_k, &
1130 0 : npw_k, kg_k, istwf_k, istwf_k, cg_ib(:,:,ib2), cg2_sk, work_ngfft, work)
1131 :
1132 0 : do ib1=1,nb
1133 0 : band1 = ib1 + bstart - 1
1134 0 : e_b1 = dmats%ks_ebands%eig(band1, ik_ibz, spin)
1135 :
1136 : ! Only if e_b1 == e_b2.
1137 0 : cval = zero
1138 0 : if (abs(e_b2 - e_b1) <= dtset%symsigma_de) then
1139 : ! Evaluate the mathematical overlap: D_{mn} = <psi_m | S | psi_n>.
1140 : ! For time-reversal symmetries (itime == 2), S is anti-unitary (S = K U).
1141 : ! cgtk_rotate has already fully evaluated S|psi_n> into cg2_sk, which includes
1142 : ! the complex-conjugation of both the structural phase and Fourier coefficients.
1143 : ! Therefore, cg_zdotc properly computes <psi_m | S \psi_n> = \sum C_m^* C_{rot}.
1144 : ! No additional complex conjugate is needed on the output `cval`.
1145 0 : dot = cg_zdotc(npw_k * nspinor, cg_ib(:,:,ib1), cg2_sk)
1146 0 : cval = dot(1) + j_dpc * dot(2)
1147 : end if
1148 :
1149 0 : cmat(ib1, ib2) = cval
1150 : end do ! ib1
1151 : end do ! ib2
1152 :
1153 0 : ABI_FREE(cg2_sk)
1154 : end if
1155 :
1156 : ! Save final matrix.
1157 0 : dmats%for_spin(spin)%value(:, :, isym, itime, ik_ibz) = cmat
1158 : end do ! isym
1159 : end do ! itime
1160 :
1161 0 : ABI_FREE(cg_ib)
1162 0 : ABI_FREE(cg_work)
1163 : end associate
1164 : end do ! ik_ibz
1165 :
1166 0 : ABI_FREE(cmat)
1167 : end do ! spin
1168 :
1169 0 : ABI_FREE(work)
1170 0 : call wfd%free()
1171 0 : call my_krank%free()
1172 :
1173 : ! Collect results on each MPI proc.
1174 0 : do spin=1,nsppol
1175 0 : call xmpi_sum(dmats%for_spin(spin)%value, comm, ierr)
1176 : end do
1177 :
1178 0 : call cwtime_report(" dmats_init:", cpu, wall, gflops)
1179 :
1180 0 : end subroutine dmats_init
1181 : !!***
1182 :
1183 : !----------------------------------------------------------------------
1184 :
1185 : !!****f* m_classify_bands/dmats_ncwrite
1186 : !! NAME
1187 : !! dmats_ncwrite
1188 : !!
1189 : !! FUNCTION
1190 : !! Write the D-matrices and their band ranges to an open NetCDF file.
1191 : !! One NetCDF group is created for each spin channel.
1192 : !!
1193 : !! INPUTS
1194 : !! ncid=NetCDF file identifier. The file must be in data mode on entry.
1195 : !!
1196 : !! SOURCE
1197 :
1198 0 : subroutine dmats_ncwrite(dmats, ncid)
1199 :
1200 : !Arguments ------------------------------------
1201 : class(dmats_t),target,intent(in) :: dmats
1202 : integer,intent(in) :: ncid
1203 :
1204 : !Local variables-------------------------------
1205 : integer :: spin, spin_ncid, ncerr, nb, nsym, nkibz
1206 0 : real(dp),contiguous,pointer :: dmat_ptr(:,:,:,:,:,:)
1207 : !----------------------------------------------------------------------
1208 :
1209 0 : nsym = dmats%cryst%nsym
1210 0 : nkibz = dmats%ks_ebands%nkpt
1211 :
1212 0 : do spin=1,size(dmats%for_spin)
1213 0 : nb = dmats%brange_spin(2, spin) - dmats%brange_spin(1, spin) + 1
1214 0 : if (.not. all(shape(dmats%for_spin(spin)%value) == [nb, nb, nsym, 2, nkibz])) then
1215 0 : ABI_ERROR("Inconsistent dmats array shape")
1216 : end if
1217 :
1218 0 : NCF_CHECK(nctk_set_defmode(ncid))
1219 0 : NCF_CHECK(nf90_def_grp(ncid, strcat("dmats", "_spin", itoa(spin)), spin_ncid))
1220 : ncerr = nctk_def_dims(spin_ncid, [ &
1221 : nctkdim_t("cplex_dmat", 2), &
1222 : nctkdim_t("two_dmat", 2), &
1223 : nctkdim_t("nb_dmat", nb), &
1224 : nctkdim_t("nsym_dmat", nsym), &
1225 : nctkdim_t("ntime_dmat", 2), &
1226 : nctkdim_t("nkibz_dmat", nkibz) &
1227 0 : ], defmode=.True.)
1228 0 : NCF_CHECK(ncerr)
1229 : ncerr = nctk_def_arrays(spin_ncid, [ &
1230 : nctkarr_t("band_range", "int", "two_dmat"), &
1231 : nctkarr_t("dmat_values", "dp", "cplex_dmat, nb_dmat, nb_dmat, nsym_dmat, ntime_dmat, nkibz_dmat") &
1232 0 : ])
1233 0 : NCF_CHECK(ncerr)
1234 0 : NCF_CHECK(nctk_set_datamode(spin_ncid))
1235 0 : NCF_CHECK(nf90_put_var(spin_ncid, nctk_idname(spin_ncid, "band_range"), dmats%brange_spin(:, spin)))
1236 0 : call c_f_pointer(c_loc(dmats%for_spin(spin)%value), dmat_ptr, [2, nb, nb, nsym, 2, nkibz])
1237 0 : NCF_CHECK(nf90_put_var(spin_ncid, nctk_idname(spin_ncid, "dmat_values"), dmat_ptr))
1238 : end do
1239 :
1240 0 : end subroutine dmats_ncwrite
1241 : !!***
1242 :
1243 : !----------------------------------------------------------------------
1244 :
1245 : !!****f* m_classify_bands/dmats_init_from_file
1246 : !! NAME
1247 : !! dmats_init_from_file
1248 : !!
1249 : !! FUNCTION
1250 : !! Initialize a dmats_t object from D-matrices stored by dmats_ncwrite.
1251 : !! Crystal and dataset pointers are associated with the caller-owned objects.
1252 : !!
1253 : !! INPUTS
1254 : !! path=Path to the NetCDF file containing dmats_spinN groups.
1255 : !! dtset=Dataset used to validate the number of spin channels.
1256 : !! cryst=Crystal used to rebuild the symmetry multiplication tables.
1257 : !! comm=MPI communicator used to read the electronic band structure.
1258 : !!
1259 : !! SOURCE
1260 :
1261 0 : subroutine dmats_init_from_file(dmats, path, dtset, cryst, comm)
1262 :
1263 : !Arguments ------------------------------------
1264 : class(dmats_t),target,intent(out) :: dmats
1265 : character(len=*),intent(in) :: path
1266 : type(dataset_type),target,intent(in) :: dtset
1267 : class(crystal_t),target,intent(in) :: cryst
1268 : integer,intent(in) :: comm
1269 :
1270 : !Local variables-------------------------------
1271 : integer :: ncid, spin_ncid, spin, nsppol, nsym, nkibz, nb, ncerr, ierr
1272 : integer :: nb_file, nsym_file, ntime_file, nkibz_file, cplex_file
1273 0 : real(dp),contiguous,pointer :: dmat_ptr(:,:,:,:,:,:)
1274 : !----------------------------------------------------------------------
1275 :
1276 0 : dmats%ks_ebands = ebands_from_file(path, comm)
1277 0 : dmats%cryst => cryst
1278 0 : dmats%dtset => dtset
1279 :
1280 0 : nsppol = dmats%ks_ebands%nsppol
1281 0 : nsym = cryst%nsym
1282 0 : nkibz = dmats%ks_ebands%nkpt
1283 0 : ABI_CHECK_IEQ(nsppol, dtset%nsppol, "Inconsistent nsppol in dmats file")
1284 :
1285 0 : ABI_MALLOC(dmats%brange_spin, (2, nsppol))
1286 0 : ABI_MALLOC(dmats%multable, (4, nsym, nsym))
1287 0 : ABI_MALLOC(dmats%toinv, (4, nsym))
1288 : call sg_multable(nsym, cryst%symafm, cryst%symrel, ierr, &
1289 0 : tnons=cryst%tnons, multable=dmats%multable, toinv=dmats%toinv)
1290 0 : ABI_CHECK_IEQ(ierr, 0, "sg_multable returned ierr !=0. See messages above.")
1291 :
1292 0 : ABI_MALLOC(dmats%for_spin, (nsppol))
1293 0 : NCF_CHECK(nctk_open_read(ncid, path, xmpi_comm_self))
1294 0 : do spin=1,nsppol
1295 0 : NCF_CHECK(nf90_inq_ncid(ncid, strcat("dmats", "_spin", itoa(spin)), spin_ncid))
1296 0 : NCF_CHECK(nctk_get_dim(spin_ncid, "cplex_dmat", cplex_file))
1297 0 : NCF_CHECK(nctk_get_dim(spin_ncid, "nb_dmat", nb_file))
1298 0 : NCF_CHECK(nctk_get_dim(spin_ncid, "nsym_dmat", nsym_file))
1299 0 : NCF_CHECK(nctk_get_dim(spin_ncid, "ntime_dmat", ntime_file))
1300 0 : NCF_CHECK(nctk_get_dim(spin_ncid, "nkibz_dmat", nkibz_file))
1301 0 : ABI_CHECK_IEQ(cplex_file, 2, "dmats file should contain complex matrices")
1302 0 : ABI_CHECK_IEQ(nsym_file, nsym, "Inconsistent number of symmetries in dmats file")
1303 0 : ABI_CHECK_IEQ(ntime_file, 2, "Inconsistent time-reversal dimension in dmats file")
1304 0 : ABI_CHECK_IEQ(nkibz_file, nkibz, "Inconsistent number of IBZ k-points in dmats file")
1305 :
1306 0 : NCF_CHECK(nf90_get_var(spin_ncid, nctk_idname(spin_ncid, "band_range"), dmats%brange_spin(:, spin)))
1307 0 : nb = dmats%brange_spin(2, spin) - dmats%brange_spin(1, spin) + 1
1308 0 : ABI_CHECK_IEQ(nb_file, nb, "Inconsistent band range in dmats file")
1309 0 : ABI_MALLOC(dmats%for_spin(spin)%value, (nb, nb, nsym, 2, nkibz))
1310 0 : call c_f_pointer(c_loc(dmats%for_spin(spin)%value), dmat_ptr, [2, nb, nb, nsym, 2, nkibz])
1311 0 : ncerr = nf90_get_var(spin_ncid, nctk_idname(spin_ncid, "dmat_values"), dmat_ptr)
1312 0 : NCF_CHECK(ncerr)
1313 : end do
1314 0 : NCF_CHECK(nf90_close(ncid))
1315 :
1316 0 : end subroutine dmats_init_from_file
1317 : !!***
1318 :
1319 : !----------------------------------------------------------------------
1320 :
1321 : !!****f* m_classify_bands/dmats_free
1322 : !! NAME
1323 : !! dmats_free
1324 : !!
1325 : !! FUNCTION
1326 : !! Free memory
1327 : !!
1328 : !! SOURCE
1329 :
1330 0 : subroutine dmats_free(dmats)
1331 :
1332 : !Arguments ------------------------------------
1333 : class(dmats_t),intent(inout) :: dmats
1334 :
1335 : !Local variables-------------------------------
1336 : integer :: spin
1337 : !----------------------------------------------------------------------
1338 :
1339 0 : call dmats%ks_ebands%free()
1340 :
1341 0 : ABI_SFREE(dmats%brange_spin)
1342 0 : ABI_SFREE(dmats%multable)
1343 0 : ABI_SFREE(dmats%toinv)
1344 :
1345 0 : do spin=1,size(dmats%for_spin)
1346 0 : ABI_SFREE(dmats%for_spin(spin)%value)
1347 : end do
1348 0 : ABI_SFREE(dmats%for_spin)
1349 :
1350 0 : end subroutine dmats_free
1351 : !!***
1352 :
1353 : !----------------------------------------------------------------------
1354 :
1355 : !!****f* m_classify_bands/dmats_check_one_k
1356 : !! NAME
1357 : !! dmats_check_one_k
1358 : !!
1359 : !! FUNCTION
1360 : !! Run the full battery of algebraic tests (see dmats_check's SOURCE header for the
1361 : !! complete list: unitarity, identity, inverse relation, group multiplication,
1362 : !! Kramers, class character, S^n closure) on a single (spin, k) slice of D-matrices.
1363 : !! Factored out of dmats_check so the SAME tests can be reused, unchanged, on
1364 : !! D-matrices reconstructed at an arbitrary full-BZ k-point (see dmats_check_star),
1365 : !! not just on the genuine per-IBZ slices of dmats%for_spin.
1366 : !!
1367 : !! INPUTS
1368 : !! spin=Spin index.
1369 : !! kk_ibz(3)=k-point (reduced coords) at which dmat_k was computed. Despite the name,
1370 : !! this need not be an actual IBZ point of dmats%ks_ebands: it is only used to
1371 : !! rebuild the little group (symtab) and the analytic phase formulas, both of
1372 : !! which only depend on the k-vector itself, not on how dmat_k was constructed.
1373 : !! dmat_k(:,:,:,:)=D-matrices (nb,nb,nsym,2) at kk_ibz: dmat_k(:,:,isym,itime) is the
1374 : !! identity placeholder if (isym,itime) is not in the little group of kk_ibz.
1375 : !! units(:), prtvol=Output units and verbosity.
1376 : !! tag=Label used as the yamldoc dictlist key for this k-point's diagnostics.
1377 : !!
1378 : !! SIDE EFFECTS
1379 : !! ydoc=yamldoc_t, appended to with this k-point's diagnostics dictlist.
1380 : !! ierr=Accumulated error counter (incremented on each failed sub-test).
1381 : !!
1382 : !! SOURCE
1383 :
1384 0 : subroutine dmats_check_one_k(dmats, spin, kk_ibz, dmat_k, units, prtvol, tag, ydoc, ierr)
1385 :
1386 : !Arguments ------------------------------------
1387 : class(dmats_t),intent(in) :: dmats
1388 : integer,intent(in) :: spin, units(:), prtvol
1389 : real(dp),intent(in) :: kk_ibz(3)
1390 : complex(dp),intent(in) :: dmat_k(:,:,:,:)
1391 : character(len=*),intent(in) :: tag
1392 : type(yamldoc_t),intent(inout) :: ydoc
1393 : integer,intent(inout) :: ierr
1394 :
1395 : !Local variables-------------------------------
1396 : integer :: nb, isym, itime, isym_inv, j, isym1, isym2, isym3, n, isym_cnt, ierr_so
1397 : integer :: itime1, itime2, itime3, nsym_lg, nclass_lg, icls, iel, il
1398 : logical :: unitary, identity_ok, kramers_ok, char_ok, isproper
1399 : character(len=5000) :: msg
1400 : real(dp),parameter :: DTOL = tol3
1401 : real(dp) :: kk_sk(3), tsign, err, phase_err, char_err
1402 : real(dp) :: Sk3(3), L_mult(3), phase_err_mult
1403 : complex(dp) :: phase_L, phase_analytic, phase_dyn, phase_analytic_mult
1404 : integer :: isym1_inv, isym2_inv
1405 : integer :: g0_k(3)
1406 0 : integer :: symtab(4,2,dmats%cryst%nsym)
1407 : integer :: mult_fail_cnt
1408 0 : complex(dp),allocatable :: cmat_n(:,:)
1409 0 : type(pair_list), allocatable :: sym_dicts(:), mult_fail_dicts(:)
1410 0 : integer :: sym_lg(3,3,dmats%cryst%nsym), local2global(dmats%cryst%nsym), trans(3)
1411 0 : integer :: class_id_of_isym(dmats%cryst%nsym)
1412 0 : integer,allocatable :: nelements_lg(:), elements_idx_lg(:,:)
1413 0 : real(dp) :: class_char_ref(dmats%cryst%nsym)
1414 : ! *************************************************************************
1415 :
1416 : ABI_UNUSED((/spin/))
1417 :
1418 : ! NB: unlike the Kramers/Theta^2 test below (isym==1, itime==2 block), which assumes
1419 : ! Theta^2=+1 and is therefore explicitly gated off for nspinor==2 right where it is
1420 : ! computed, every OTHER test in this routine (unitarity, identity, inverse relation,
1421 : ! group multiplication, character class, closure) is Theta^2-agnostic and safe to run
1422 : ! for nspinor==2 as a self-consistency diagnostic of dmats_init's spinor D-matrices.
1423 :
1424 0 : nb = size(dmat_k, 1)
1425 0 : ABI_MALLOC(cmat_n, (nb, nb))
1426 :
1427 : ! Determine the little group of kk_ibz (and the associated umklapp vector) with the SAME
1428 : ! symrel^t convention used by dmats_init to decide whether a given (isym,itime) yields a
1429 : ! genuinely-computed D-matrix or the identity placeholder (see the NOTES on g0_k there).
1430 : ! littlegroup_q instead rotates kk_ibz with symrec, which is the convention for q-points,
1431 : ! not k-points, and can disagree with dmats_init whenever symrel is not orthogonal in the
1432 : ! reduced basis -- inconsistent with what dmats%for_spin(...) actually stores.
1433 0 : symtab = 0
1434 0 : do itime=1,2
1435 0 : tsign = merge(one, -one, itime == 1)
1436 0 : do isym=1,dmats%cryst%nsym
1437 0 : kk_sk = tsign * matmul(transpose(real(dmats%cryst%symrel(:,:,isym), dp)), kk_ibz)
1438 0 : g0_k = nint(kk_ibz - kk_sk)
1439 0 : if (all(abs(kk_ibz - kk_sk - g0_k) < tol8)) then
1440 0 : symtab(1:3, itime, isym) = g0_k
1441 0 : symtab(4, itime, isym) = 1
1442 : end if
1443 : end do
1444 : end do
1445 :
1446 : ! Divide the itime=1 (pure spatial) little group into conjugacy classes with get_classes
1447 : ! (m_ptgroups.F90), then use |Tr D(S)| (character magnitude) as a class-function test:
1448 : ! conjugate elements S' = X S X^{-1} of a genuine (possibly projective) unitary
1449 : ! representation always satisfy |Tr D(S')| = |Tr D(S)| exactly, since
1450 : ! D(X S X^{-1}) equals D(X) D(S) D(X)^{-1} up to an overall SCALAR phase (the same
1451 : ! tabulated-vs-literal-composition phase ambiguity already handled in the group
1452 : ! multiplication test above), and a similarity transform composed with an overall
1453 : ! phase preserves |trace|. Comparing |trace| (not the raw complex trace) sidesteps
1454 : ! that phase ambiguity entirely, so no analytic phase tracking is needed here.
1455 : !
1456 : ! get_classes computes conjugacy X^{-1} S X with the plain (non-transposed) symrel
1457 : ! product, i.e. the SAME real-space composition convention already used everywhere
1458 : ! else in this routine (isym_inv search, isym3 = isym1*isym2 hunting). This is
1459 : ! convention-consistent with the D-matrices themselves: after the dmats_init fix
1460 : ! (see its NOTES), dmats%for_spin(...)(:,:,isym,...) genuinely stores D_true(S_isym)
1461 : ! indexed by the SAME isym used to index cryst%symrel, so no extra transpose or
1462 : ! symrec/symrel^t handling is required to match classes to D-matrix slots. (The
1463 : ! symrel^t convention only enters dmats_init's k-point/little-group bookkeeping;
1464 : ! little-group MEMBERSHIP and conjugacy-class PARTITIONING are both provably
1465 : ! independent of that choice: symrec = symrel^{-t} is a group isomorphism that maps
1466 : ! every isym to itself, so it preserves both the little-group isym set and the
1467 : ! class partition of that set exactly.)
1468 : !
1469 : ! Why NOT feed get_classes symrel(isym)^t either (transposed, but still indexed by
1470 : ! the SAME isym): unlike symrec = symrel^{-t}, plain transposition isym -> symrel(isym)^t
1471 : ! is only an ANTI-homomorphism of the isym-indexed abstract group law, because
1472 : ! (AB)^t = B^t A^t reverses multiplication order: symrel(a)^t symrel(b)^t = symrel(b.a)^t,
1473 : ! not symrel(a.b)^t, whenever the point group is non-abelian (as it generally is here).
1474 : ! Anti-homomorphisms still preserve conjugacy classes as an ABSTRACT structure, but not
1475 : ! with the SAME isym labeling used to index dmats%for_spin(...), so class_id_of_isym
1476 : ! built from transposed matrices would in general group the WRONG isym's together. Only
1477 : ! plain (non-transposed) symrel, matching how D(S1 S2) proportional-to D(S1) D(S2) was
1478 : ! validated to hold in the group-multiplication test above, gives isym-consistent classes.
1479 0 : nsym_lg = 0
1480 0 : do isym=1,dmats%cryst%nsym
1481 0 : if (symtab(4, 1, isym) == 0) cycle
1482 0 : nsym_lg = nsym_lg + 1
1483 0 : sym_lg(:,:,nsym_lg) = dmats%cryst%symrel(:,:,isym)
1484 0 : local2global(nsym_lg) = isym
1485 : end do
1486 :
1487 0 : class_id_of_isym = 0
1488 0 : if (nsym_lg > 0) then
1489 : ! get_classes takes explicit-shape dummies sized from its own nsym argument
1490 : ! (nsym_lg here): the actual arrays must be allocated to EXACTLY (nsym_lg,nsym_lg)
1491 : ! (not e.g. cryst%nsym), otherwise the callee writes using an nsym_lg-based
1492 : ! column-major stride while a differently-sized caller array would read back
1493 : ! with a mismatched stride (silent data corruption via sequence association).
1494 0 : ABI_MALLOC(nelements_lg, (nsym_lg))
1495 0 : ABI_MALLOC(elements_idx_lg, (nsym_lg, nsym_lg))
1496 0 : call get_classes(nsym_lg, sym_lg(:,:,1:nsym_lg), nclass_lg, nelements_lg, elements_idx_lg)
1497 0 : do icls=1,nclass_lg
1498 0 : do iel=1,nelements_lg(icls)
1499 0 : il = elements_idx_lg(iel, icls)
1500 0 : class_id_of_isym(local2global(il)) = icls
1501 : end do
1502 : end do
1503 : ! Reference character (magnitude) for each class: |Tr D(S)| for the class's first
1504 : ! element. All other elements of the same class are checked against this below.
1505 0 : do icls=1,nclass_lg
1506 0 : isym = local2global(elements_idx_lg(1, icls))
1507 0 : class_char_ref(icls) = abs(get_trace(dmat_k(:, :, isym, 1)))
1508 : end do
1509 0 : ABI_FREE(nelements_lg)
1510 0 : ABI_FREE(elements_idx_lg)
1511 : end if
1512 :
1513 0 : isym_cnt = 0
1514 0 : do itime=1,2
1515 0 : do isym=1,dmats%cryst%nsym
1516 0 : if (symtab(4, itime, isym) /= 0) isym_cnt = isym_cnt + 1
1517 : end do
1518 : end do
1519 0 : if (isym_cnt > 0) then
1520 0 : ABI_MALLOC(sym_dicts, (isym_cnt))
1521 : end if
1522 :
1523 0 : isym_cnt = 0
1524 0 : do itime=1,2
1525 0 : do isym=1,dmats%cryst%nsym
1526 0 : if (symtab(4, itime, isym) == 0) cycle
1527 0 : isym_cnt = isym_cnt + 1
1528 0 : call sym_dicts(isym_cnt)%set("isym", i=isym)
1529 0 : call sym_dicts(isym_cnt)%set("itime", i=itime)
1530 0 : msg = sjoin("[", ftoa(dmats%cryst%tnons(1,isym)), ", ", ftoa(dmats%cryst%tnons(2,isym)))
1531 0 : msg = sjoin(msg, ", ", ftoa(dmats%cryst%tnons(3,isym)), "]")
1532 0 : call sym_dicts(isym_cnt)%set("tnon", s=trim(msg))
1533 :
1534 0 : msg = sjoin("[", itoa(symtab(1, itime, isym)), ", ", itoa(symtab(2, itime, isym)))
1535 0 : msg = sjoin(msg, ", ", itoa(symtab(3, itime, isym)), ", ", itoa(symtab(4, itime, isym)), "]")
1536 0 : call sym_dicts(isym_cnt)%set("symtab", s=trim(msg))
1537 :
1538 0 : associate (cmat => dmat_k(:, :, isym, itime))
1539 0 : unitary = is_unitary(nb, cmat, DTOL, err)
1540 0 : if (.not. unitary) ierr = ierr + 1
1541 0 : call sym_dicts(isym_cnt)%set("unitary", s=yesno(unitary))
1542 0 : call sym_dicts(isym_cnt)%set("unitary_err", r=err)
1543 :
1544 : ! Identity operator test
1545 0 : if (isym == 1 .and. itime == 1) then
1546 0 : identity_ok = is_identity(nb, cmat, DTOL, err)
1547 0 : if (.not. identity_ok) ierr = ierr + 1
1548 0 : call sym_dicts(isym_cnt)%set("identity_ok", s=yesno(identity_ok))
1549 0 : call sym_dicts(isym_cnt)%set("identity_err", r=err)
1550 : end if
1551 :
1552 : ! Character class-function test (itime=1 only, see the NOTES on get_classes
1553 : ! above the class_id_of_isym computation): |Tr D(S)| must be the same for
1554 : ! every S in a given conjugacy class of the little group.
1555 0 : if (itime == 1 .and. class_id_of_isym(isym) /= 0) then
1556 0 : icls = class_id_of_isym(isym)
1557 0 : char_err = abs(abs(get_trace(cmat)) - class_char_ref(icls))
1558 0 : char_ok = (char_err < DTOL)
1559 0 : if (.not. char_ok) ierr = ierr + 1
1560 0 : call sym_dicts(isym_cnt)%set("class_id", i=icls)
1561 0 : call sym_dicts(isym_cnt)%set("char_ok", s=yesno(char_ok))
1562 0 : call sym_dicts(isym_cnt)%set("char_err", r=char_err)
1563 : end if
1564 :
1565 : ! Kramers test: pure time reversal (isym=1, itime=2) is only present in this
1566 : ! slot at TR-invariant k-points (k = -k mod G, e.g. TRIM points), and must
1567 : ! satisfy \Theta^2 = D(\Theta) D(\Theta)^* = +I EXACTLY (not just up to a
1568 : ! phase) for scalar (nspinor=1, hard-required by dmats_init) wavefunctions.
1569 : ! Unlike the generic group-multiplication test, this is an exact identity
1570 : ! with no residual gauge/tabulation-phase freedom: rescaling each band by an
1571 : ! arbitrary phase e^{i\phi_n} transforms D(\Theta) -> \Phi^{-1} D(\Theta) \Phi^{-1}
1572 : ! (antiunitary => the KET phase also gets conjugated), so
1573 : ! D(\Theta)D(\Theta)^* -> \Phi^{-1} [D(\Theta)D(\Theta)^*] \Phi, which leaves
1574 : ! "= I" invariant. This differs from, and is NOT redundant with, the inverse-
1575 : ! relation test below (isym_inv=1=isym for itime=2), which only checks that
1576 : ! D(\Theta) is proportional to its own transpose, not that D(\Theta)D(\Theta)^*=I.
1577 0 : if (isym == 1 .and. itime == 2 .and. dmats%dtset%nspinor == 1) then
1578 0 : kramers_ok = is_identity(nb, matmul(cmat, conjg(cmat)), DTOL, err)
1579 0 : if (.not. kramers_ok) ierr = ierr + 1
1580 0 : call sym_dicts(isym_cnt)%set("kramers_ok", s=yesno(kramers_ok))
1581 0 : call sym_dicts(isym_cnt)%set("kramers_err", r=err)
1582 : end if
1583 :
1584 : ! Inverse relation test
1585 0 : isym_inv = dmats%toinv(1, isym)
1586 :
1587 0 : if (isym_inv /= 0 .and. symtab(4, itime, isym_inv) /= 0) then
1588 : ! Analytic phase relating D(S^{-1}) to D(S)^\dagger, i.e. predicting phase_dyn (below). This
1589 : ! reuses the SAME tabulated-vs-literal-composition phase relation already validated to exact
1590 : ! (machine-precision) agreement by the group-multiplication test further down this routine:
1591 : !
1592 : ! matmul(D(isym1), D(isym2)) = e^{+i 2pi (S_{isym3} k).L} D(isym3),
1593 : ! L = multable(2:4, toinv(isym2), toinv(isym1)), isym3 = multable(1, isym1, isym2)
1594 : !
1595 : ! specialized to isym1=isym_inv, isym2=isym, so that the literal Seitz composition
1596 : ! S(isym_inv).S(isym) is EXACTLY the identity (isym3=1, S_{isym3} k = k), giving
1597 : ! L = multable(2:4, isym, isym_inv) = toinv(2:4, isym_inv) exactly (both tabulate the same
1598 : ! S(isym_inv).S(isym) = {I, L} relation), so no separate multable lookup is even needed here.
1599 : !
1600 : ! NOTE: the previous formula used toinv(2:4, isym) [i.e. the residual L for the OPPOSITE
1601 : ! composition order S(isym).S(isym_inv)] plus an extra ad hoc symrec/symtab correction term --
1602 : ! an isym/isym_inv index-order mixup that was the actual root cause of the long-standing
1603 : ! "disagrees with phase_dyn by a discrete 90/180 degree offset on non-symmorphic operations"
1604 : ! bug (see git history). Verified: phase_err now at machine precision for every (isym,itime)
1605 : ! tuple in the reference gstore test (previously ~58 tuples off by sqrt(2), ~80 by exactly 2).
1606 0 : phase_analytic = exp(cmplx(zero, two_pi * sum(kk_ibz * real(dmats%toinv(2:4, isym_inv), dp)), dp))
1607 : ! TEMPORARY DIAGNOSTIC (session experiment, not yet a verified fix): test whether the
1608 : ! itime=2, non-involutory inv_ok failures (100% clean split found empirically) are fixed
1609 : ! by an extra missing sign tied to itime and non-involutory character.
1610 0 : if (dmats%dtset%nspinor == 2 .and. itime == 2 .and. isym_inv /= isym) phase_analytic = -phase_analytic
1611 0 : if (dmats%dtset%nspinor == 2 .and. itime == 1 .and. isym_inv == isym .and. isym /= 1 .and. isym /= 2) &
1612 0 : phase_analytic = -phase_analytic
1613 :
1614 : associate (cmat_inv => dmat_k(:, :, isym_inv, itime))
1615 : ! Independently, dynamically extract the phase relating the two independently constructed
1616 : ! matrices by taking the Frobenius inner product of the two matrices:
1617 : ! Phase = Tr(A^\dagger B) / nb = sum_{ij} A^*_{ij} B_{ij} / nb.
1618 : ! If the matrices are truly proportional, Phase will be a scalar of unit magnitude,
1619 : ! and dividing by it will yield a mathematically exact equality test.
1620 0 : if (itime == 1) then
1621 0 : phase_dyn = sum( conjg(cmat_inv) * conjg(transpose(cmat)) ) / nb
1622 0 : err = maxval(abs(cmat_inv * (phase_dyn / abs(phase_dyn)) - conjg(transpose(cmat))))
1623 : else
1624 0 : phase_dyn = sum( conjg(cmat_inv) * transpose(cmat) ) / nb
1625 0 : err = maxval(abs(cmat_inv * (phase_dyn / abs(phase_dyn)) - transpose(cmat)))
1626 : end if
1627 : ! phase_dyn, by construction of the Frobenius inner product above, should equal conjg(phase_analytic)
1628 : ! when the D-matrices carry the correct absolute phase, so phase_dyn * phase_analytic == 1.
1629 : ! Formerly a diagnostic-only quantity (the old toinv(isym)-based formula disagreed with
1630 : ! phase_dyn by a discrete 90/180 degree offset on non-symmorphic operations); now that the
1631 : ! formula above is fixed (verified at machine precision on the full reference gstore test,
1632 : ! all 976 (isym,itime) tuples across all 8 IBZ k-points), phase_err is folded into ierr/inv_ok.
1633 0 : phase_err = abs(phase_dyn * phase_analytic - one)
1634 : ! TEMPORARY DIAGNOSTIC: for nspinor==2, itime==2, isym with a TRIVIAL rotation part
1635 : ! (symrel(isym) = +-Identity, i.e. E or pure spatial inversion -- both spin-blind), this
1636 : ! generic test degenerates to exactly the Kramers Theta^2=-1 signature (already correctly
1637 : ! excluded from the DEDICATED Kramers sub-test above for nspinor==2) -- exclude it from
1638 : ! gating ierr here too, so it doesn't falsely abort dmats%check()/block reaching check_star.
1639 0 : if (.not. (dmats%dtset%nspinor == 2 .and. itime == 2 .and. &
1640 : (all(dmats%cryst%symrel(:,:,isym) == identity_3d) .or. &
1641 : all(dmats%cryst%symrel(:,:,isym) == -identity_3d)))) then
1642 0 : if (err >= DTOL .or. abs(abs(phase_dyn) - one) > DTOL .or. phase_err > DTOL) ierr = ierr + 1
1643 : end if
1644 : call sym_dicts(isym_cnt)%set("inv_ok", &
1645 0 : s=yesno(err < DTOL .and. abs(abs(phase_dyn) - one) <= DTOL .and. phase_err <= DTOL))
1646 0 : call sym_dicts(isym_cnt)%set("inv_err", r=err)
1647 0 : call sym_dicts(isym_cnt)%set("inv_phase", s=sjoin(ftoa(real(phase_dyn)), " + i ", ftoa(aimag(phase_dyn))))
1648 0 : call sym_dicts(isym_cnt)%set("inv_phase_analytic_err", r=phase_err)
1649 : end associate
1650 : end if
1651 0 : if (prtvol > 1) call print_arr(units, cmat, max_r=nb, max_c=nb)
1652 : end associate
1653 : end do ! isym
1654 : end do ! itime
1655 :
1656 : ! Group multiplication test, extended to time reversal (itime1, itime2 in {1,2}).
1657 : ! In ABINIT, point-group operations are applied sequentially to coordinates such that
1658 : ! r' = S_1 S_2 r. When generating the representation matrices D(S, k),
1659 : ! this algebraic structure is maintained according to the product rule:
1660 : !
1661 : ! D^{k}(S_1 S_2) = e^{-i k \cdot L} D^{S_2 k}(S_1) D^{k}(S_2)
1662 : !
1663 : ! Since we are operating strictly inside the little group of k, we have S_2 k \equiv k,
1664 : ! and the equation fundamentally simplifies to a proportionality:
1665 : !
1666 : ! D(S_3) = e^{i \phi} D(S_1) D(S_2)
1667 : !
1668 : ! We search for the composite symmetry isym3 that perfectly matches the spatial
1669 : ! rotation product: symrel(isym1) * symrel(isym2). The spatial rotation composition
1670 : ! rule is itime-independent because \hat\Theta commutes with any pure spatial
1671 : ! coordinate transformation acting on the full (not just periodic-part) wavefunction:
1672 : ! \hat\Theta \hat S \psi(r) = [\hat S\psi(r)]^* = \psi(S^{-1}r)^* = \hat S[\hat\Theta\psi](r).
1673 : !
1674 : ! What DOES depend on itime is the Wigner co-representation composition law itself
1675 : ! (Bradley & Cracknell, sec. 7.3): composing two operators A=(isym1,itime1) and
1676 : ! B=(isym2,itime2), with A applied after B,
1677 : !
1678 : ! D(A B) = D(A) D(B) if A is unitary (itime1 == 1)
1679 : ! D(A B) = D(A) D(B)^* if A is antiunitary (itime1 == 2)
1680 : !
1681 : ! and itime3 (unitary/antiunitary character of A B) follows from Theta^2 = +1
1682 : ! for the scalar (nspinor=1) wavefunctions handled here:
1683 : !
1684 : ! itime3 = 1 + mod((itime1-1) + (itime2-1), 2)
1685 : !
1686 : ! i.e. antiunitary o antiunitary = unitary, matching Theta^2=+1 (Kramers-degeneracy
1687 : ! sign would flip this to Theta^2=-1 for spinors, not implemented/tested: dmats_init
1688 : ! hard-requires nspinor=1).
1689 : !
1690 : ! Diagnostics: record every FAILING (isym1,itime1,isym2,itime2,isym3,itime3) tuple
1691 : ! (with its g0's and errors) instead of just incrementing ierr, so a caller like
1692 : ! dmats_check_star (run over the full BZ, where the k passed in need not be a genuine
1693 : ! IBZ point) can pinpoint exactly which composition and which umklapp broke.
1694 0 : mult_fail_cnt = 0
1695 0 : ABI_MALLOC(mult_fail_dicts, (4 * dmats%cryst%nsym**2))
1696 0 : do itime1=1,2
1697 0 : do itime2=1,2
1698 0 : itime3 = 1 + mod((itime1 - 1) + (itime2 - 1), 2)
1699 0 : do isym1=1,dmats%cryst%nsym
1700 0 : if (symtab(4, itime1, isym1) == 0) cycle
1701 0 : do isym2=1,dmats%cryst%nsym
1702 0 : if (symtab(4, itime2, isym2) == 0) cycle
1703 :
1704 0 : isym3 = dmats%multable(1, isym1, isym2)
1705 :
1706 0 : if (isym3 /= 0 .and. symtab(4, itime3, isym3) /= 0) then
1707 : associate (cmat1 => dmat_k(:, :, isym1, itime1), &
1708 : cmat2 => dmat_k(:, :, isym2, itime2), &
1709 : cmat3 => dmat_k(:, :, isym3, itime3))
1710 :
1711 : ! Instead of failing the test due to phase formula mismatch, we can just EXTRACT the phase!
1712 : ! ABINIT's exact phase might have extra factors due to how istwf_k and cgtk_rotate conjugate things.
1713 : ! The goal is to check if they are proportional (i.e. group structure is satisfied up to a phase).
1714 0 : if (itime1 == 1) then
1715 0 : phase_L = sum( conjg(cmat3) * matmul(cmat1, cmat2) ) / nb
1716 0 : err = maxval(abs(cmat3 * (phase_L / abs(phase_L)) - matmul(cmat1, cmat2)))
1717 : else
1718 0 : phase_L = sum( conjg(cmat3) * matmul(cmat1, conjg(cmat2)) ) / nb
1719 0 : err = maxval(abs(cmat3 * (phase_L / abs(phase_L)) - matmul(cmat1, conjg(cmat2))))
1720 : end if
1721 :
1722 : ! Analytic prediction of the same phase, from the "Caveat for tabulated symmetry
1723 : ! matrices" in main.tex: the literal Seitz product S1S2 and the tabulated operation
1724 : ! S3=isym3 sharing its rotation differ by a pure lattice translation L, giving
1725 : ! D^k(S3) = e^{-i (S3 k).L} D(S1) D(S2), where (S3 k) is the PURE spatial rotation
1726 : ! of S3 applied to k (symrel^t, no time-reversal sign: L comes from the translation
1727 : ! part of the spatial space group only, unrelated to Theta).
1728 : ! dmats%multable/toinv are built from the plain {symrel,tnons} Seitz convention,
1729 : ! while dmats%for_spin is indexed with the symrel^t convention used throughout this
1730 : ! file for the k-action of a symmetry. Reconciling the two requires L to be looked
1731 : ! up at the GROUP-THEORETIC INVERSES of isym1 and isym2 (in reversed order):
1732 : ! L = multable(2:4, toinv(isym2), toinv(isym1)), not multable(2:4,isym1,isym2).
1733 : ! With this, phase_analytic_mult matches phase_L exactly for all 768
1734 : ! (isym1,isym2,itime1,itime2) tuples tested on the reference gstore test.
1735 0 : isym1_inv = dmats%toinv(1, isym1)
1736 0 : isym2_inv = dmats%toinv(1, isym2)
1737 0 : Sk3 = matmul(transpose(real(dmats%cryst%symrel(:,:,isym3), dp)), kk_ibz)
1738 0 : L_mult = real(dmats%multable(2:4, isym2_inv, isym1_inv), dp)
1739 0 : phase_analytic_mult = exp(cmplx(zero, -two_pi * sum(Sk3 * L_mult), dp))
1740 : ! VALIDATED (session diagnostic, classify.abi+nband 18 on gstore_lead): for nspinor==2,
1741 : ! this formula is missing a UNIVERSAL, unconditional extra factor of -1 -- confirmed
1742 : ! across all 4760 (isym1,isym2,itime1,itime2) tuples tested (all 4 itime1/itime2
1743 : ! combinations), zero exceptions. Unlike the inv_ok/closure_ok fixes above, this one
1744 : ! does NOT depend on involutory character or itime at all.
1745 0 : if (dmats%dtset%nspinor == 2) phase_analytic_mult = -phase_analytic_mult
1746 0 : phase_err_mult = abs(phase_L * phase_analytic_mult - one)
1747 :
1748 : ! NOTE on phase_err_mult and dmat_star (reconstructed full-BZ D-matrices, see
1749 : ! dmats_check_star/dmats_get_star_dmats): instrumented this test (temporarily) to
1750 : ! record every failing tuple and confirmed, on the k' points where check_star
1751 : ! reports failures, that ALL of them have err and |phase_L|-1 at machine precision
1752 : ! (true proportionality/closure holds EXACTLY) while phase_err_mult is exactly 2.0
1753 : ! (a clean sign flip, not noise) for every single one -- i.e. this is the SAME
1754 : ! class of "consistently exactly wrong by a clean phase factor" issue already
1755 : ! flagged as diagnostic-only, unresolved, for IMPROPER operations in the S^n
1756 : ! closure test below and for the inverse-relation test above. Tried the natural
1757 : ! alternative convention (L = multable(2:4,isym1,isym2) directly, no toinv-reversal,
1758 : ! dotted with kk_ibz instead of Sk3 -- provably equivalent to Sk3 since g0.L_mult is
1759 : ! always an integer): it does NOT universally fix it either (worse overall, and the
1760 : ! two conventions disagree on non-overlapping subsets of tuples), so this isn't a
1761 : ! simple sign/convention swap in phase_analytic_mult -- the true fix requires
1762 : ! working out how phase_h (dmats_get_star_dmats's own per-isym reconstruction
1763 : ! phase) interacts with the k'-frame tabulated-vs-literal correction L_mult, which
1764 : ! is not yet derived. Until then, gate ierr on the two properties that constitute
1765 : ! actual group-representation closure (proportionality + unit modulus), matching
1766 : ! the precedent set by the two other diagnostic-only checks in this routine, and
1767 : ! keep phase_err_mult as a reported (not gating) diagnostic.
1768 0 : if (err >= DTOL .or. abs(abs(phase_L) - one) > DTOL) then
1769 0 : ierr = ierr + 1
1770 0 : mult_fail_cnt = mult_fail_cnt + 1
1771 0 : call mult_fail_dicts(mult_fail_cnt)%set("isym1", i=isym1)
1772 0 : call mult_fail_dicts(mult_fail_cnt)%set("itime1", i=itime1)
1773 0 : call mult_fail_dicts(mult_fail_cnt)%set("isym2", i=isym2)
1774 0 : call mult_fail_dicts(mult_fail_cnt)%set("itime2", i=itime2)
1775 0 : call mult_fail_dicts(mult_fail_cnt)%set("isym3", i=isym3)
1776 0 : call mult_fail_dicts(mult_fail_cnt)%set("itime3", i=itime3)
1777 0 : call mult_fail_dicts(mult_fail_cnt)%set("g0_1", s=trim(ltoa(symtab(1:3, itime1, isym1))))
1778 0 : call mult_fail_dicts(mult_fail_cnt)%set("g0_2", s=trim(ltoa(symtab(1:3, itime2, isym2))))
1779 0 : call mult_fail_dicts(mult_fail_cnt)%set("g0_3", s=trim(ltoa(symtab(1:3, itime3, isym3))))
1780 0 : call mult_fail_dicts(mult_fail_cnt)%set("err", r=err)
1781 0 : call mult_fail_dicts(mult_fail_cnt)%set("phase_mod_err", r=abs(abs(phase_L) - one))
1782 0 : call mult_fail_dicts(mult_fail_cnt)%set("phase_err_mult", r=phase_err_mult)
1783 : end if
1784 : end associate
1785 : end if
1786 : end do
1787 : end do
1788 : end do
1789 : end do
1790 :
1791 0 : if (mult_fail_cnt > 0) then
1792 0 : call ydoc%add_dictlist(sjoin(tag, "_group_mult_fail"), mult_fail_cnt, mult_fail_dicts(1:mult_fail_cnt))
1793 0 : do j = 1, mult_fail_cnt
1794 0 : call mult_fail_dicts(j)%free()
1795 : end do
1796 : end if
1797 0 : ABI_FREE(mult_fail_dicts)
1798 :
1799 : ! =========================================================================
1800 : ! Eigenvalues & Closure Test (itime = 1)
1801 : ! =========================================================================
1802 : ! According to the theory of group representations, the representation matrix
1803 : ! D(S) must satisfy the closure conditions of the crystallographic point group.
1804 : ! If S = C_n is an n-fold symmetry operation, applying the spatial rotation
1805 : ! n times yields the identity (R^n = E).
1806 : ! However, for non-symmorphic operations (e.g. glide planes or screw axes),
1807 : ! applying the operation n times results in a pure fractional lattice translation:
1808 : ! S^n(r) = r + T
1809 : !
1810 : ! In reciprocal space, inside the little group of k, this translation introduces
1811 : ! a scalar Bloch phase shift. Thus, the eigenvalues of the representation matrix satisfy:
1812 : !
1813 : ! [ D(S) ]^n = e^{-i k \cdot T} I
1814 : !
1815 : ! (dmats hard-requires nspinor=1, see dmats_init NOTES, so there's no extra spinor parity).
1816 : ! We extract the overall scalar phase \phi = Tr(D^n) / N_{bands}, assert that
1817 : ! D(S)^n \equiv \phi I, and cross-check \phi against the analytic e^{-i k.T} computed
1818 : ! from the T returned by sym_order.
1819 0 : isym_cnt = 0
1820 0 : do itime=1,2
1821 0 : do isym=1,dmats%cryst%nsym
1822 0 : if (symtab(4, itime, isym) == 0) cycle
1823 0 : isym_cnt = isym_cnt + 1
1824 0 : if (itime == 1) then
1825 : associate (cmat => dmat_k(:, :, isym, 1))
1826 :
1827 : ! Find the order of the point-group operation (n in {1,2,3,4,6}).
1828 : ! NB: use a dedicated ierr_so for sym_order's own status -- passing the shared
1829 : ! accumulator "ierr" directly would have sym_order's intent(out) silently reset it
1830 : ! to 0 on every call, wiping out all previously-accumulated test failures.
1831 0 : call sym_order(dmats%cryst%symrel(:,:,isym), dmats%cryst%tnons(:,isym), n, isproper, trans, msg, ierr_so)
1832 0 : ABI_CHECK_IEQ(ierr_so, 0, msg)
1833 :
1834 0 : if (n > 1) then
1835 : ! Compute cmat^n
1836 0 : cmat_n = cmat
1837 0 : do j = 2, n
1838 0 : cmat_n = matmul(cmat, cmat_n)
1839 : end do
1840 :
1841 : ! Extract phase from Trace: phase = Tr(cmat^n) / nb
1842 0 : phase_L = zero
1843 0 : do j = 1, nb
1844 0 : phase_L = phase_L + cmat_n(j, j)
1845 : end do
1846 0 : phase_L = phase_L / nb
1847 :
1848 : ! Normalize cmat_n with phase_L to check if it's proportional to identity
1849 : err = zero
1850 0 : do j = 1, nb
1851 0 : cmat_n(j, j) = cmat_n(j, j) - phase_L
1852 : end do
1853 0 : err = maxval(abs(cmat_n))
1854 :
1855 : ! Analytic Bloch phase from the cumulative lattice translation T: D(S)^n = e^{-i k.T} I
1856 0 : phase_analytic = exp(cmplx(zero, -two_pi * dot_product(kk_ibz, real(trans, dp)), dp))
1857 : ! TEMPORARY DIAGNOSTIC (session experiment): applying S n times, for S a proper rotation
1858 : ! of order n, sweeps EXACTLY one full 2*pi turn -- for spinors D(2*pi) = -I always
1859 : ! (unconditionally, not a convention/branch-choice issue), so D(S)^n should pick up an
1860 : ! extra factor of -1 relative to the scalar-derived formula above, for EVERY proper
1861 : ! rotation (any n), regardless of itime (closure test is itime=1 only anyway).
1862 0 : if (dmats%dtset%nspinor == 2 .and. isproper) phase_analytic = -phase_analytic
1863 0 : phase_err = abs(phase_L - phase_analytic)
1864 :
1865 : ! NOTE: for IMPROPER operations (isproper=.false.), phase_err is consistently
1866 : ! found to be exactly 2 (phase_L = -phase_analytic, a clean sign flip, not noise)
1867 : ! -- the same class of unresolved cgtk_rotate phase-convention ambiguity already
1868 : ! flagged (but left diagnostic-only, not gating ierr) in the inverse-relation test
1869 : ! above ("still under investigation"). Follow that same precedent here: gate ierr
1870 : ! on proportionality (err) and unit modulus, but not on phase_err for improper ops.
1871 0 : if (err >= DTOL .or. abs(abs(phase_L) - one) > DTOL .or. (isproper .and. phase_err > DTOL)) ierr = ierr + 1
1872 : call sym_dicts(isym_cnt)%set("closure_ok", &
1873 0 : s=yesno(err < DTOL .and. abs(abs(phase_L) - one) <= DTOL .and. (.not. isproper .or. phase_err <= DTOL)))
1874 0 : call sym_dicts(isym_cnt)%set("closure_err", r=err)
1875 0 : call sym_dicts(isym_cnt)%set("closure_n", i=n)
1876 0 : call sym_dicts(isym_cnt)%set("isproper", s=yesno(isproper))
1877 0 : call sym_dicts(isym_cnt)%set("closure_phase", s=sjoin(ftoa(real(phase_L)), " + i ", ftoa(aimag(phase_L))))
1878 : call sym_dicts(isym_cnt)%set("closure_phase_analytic", &
1879 0 : s=sjoin(ftoa(real(phase_analytic)), " + i ", ftoa(aimag(phase_analytic))))
1880 0 : call sym_dicts(isym_cnt)%set("closure_phase_err", r=phase_err)
1881 : end if
1882 : end associate
1883 : end if
1884 : end do
1885 : end do
1886 :
1887 0 : if (allocated(sym_dicts)) then
1888 0 : call ydoc%add_dictlist(tag, isym_cnt, sym_dicts)
1889 0 : do isym = 1, isym_cnt
1890 0 : call sym_dicts(isym)%free()
1891 : end do
1892 0 : ABI_FREE(sym_dicts)
1893 : end if
1894 :
1895 0 : ABI_FREE(cmat_n)
1896 :
1897 0 : end subroutine dmats_check_one_k
1898 : !!***
1899 :
1900 : !----------------------------------------------------------------------
1901 :
1902 : !!****f* m_classify_bands/dmats_check
1903 : !! NAME
1904 : !! dmats_check
1905 : !!
1906 : !! FUNCTION
1907 : !! Verify the fundamental point-group algebraic properties of
1908 : !!
1909 : !! D^{k}_{mn}(S) = < \psi_{m, Sk} | S | \psi_{n, k} >
1910 : !!
1911 : !! The following algebraic tests are performed:
1912 : !!
1913 : !! 1. Unitarity (mandatory): || D^\dagger(k, S) D(k, S) - I || < DTOL
1914 : !! 2. Identity operator: D(E, k) = I, for isym = 1
1915 : !! 3. Inverse relation: D^{Sk}(S^{-1}) \propto D^{k}(S)^\dagger
1916 : !! 4. Group multiplication, including time-reversal: D^{k}(A B) \propto D^{k}(A) D^{k}(B)
1917 : !! for A, B each either a pure spatial symmetry or a spatial symmetry composed with
1918 : !! time reversal (Wigner co-representation composition law, see below).
1919 : !! 5. Kramers/Theta^2 (only at TR-invariant k, e.g. TRIM points): D^{k}(\Theta) D^{k}(\Theta)^*
1920 : !! = +I EXACTLY (Theta^2=+1 for scalar wavefunctions), for isym=1 (pure time reversal), itime=2.
1921 : !! 6. Character class-function test (itime=1 only): |Tr D^{k}(S)| is the same for every S in a
1922 : !! given conjugacy class of the little group of k, as computed by get_classes (m_ptgroups.F90).
1923 : !! |trace| (not the raw complex trace) is used deliberately: it is invariant under both the
1924 : !! tabulated-vs-literal-composition phase ambiguity (see the note above on test 4) and any
1925 : !! per-band wavefunction gauge choice, since a similarity transform composed with an overall
1926 : !! scalar phase always preserves |trace| exactly, with no analytic phase tracking needed.
1927 : !!
1928 : !! Caution for anyone editing this test: get_classes takes EXPLICIT-SHAPE dummy arguments
1929 : !! sized from its own nsym argument (here, the little-group order nsym_lg, which varies by
1930 : !! k-point and is in general smaller than cryst%nsym). Passing whole arrays declared with a
1931 : !! LARGER fixed bound (e.g. sized to cryst%nsym) works via Fortran sequence association but
1932 : !! silently corrupts the result for any 2D array (elements_idx) whenever nsym_lg != cryst%nsym,
1933 : !! because the callee writes using an nsym_lg-based column-major stride while the caller would
1934 : !! read back using a cryst%nsym-based stride. (1D arrays, like nelements, are unaffected: their
1935 : !! offset does not depend on the declared bound.) The fix is to allocate elements_idx to EXACTLY
1936 : !! (nsym_lg, nsym_lg), as done here and in esymm_init (m_esymm.F90). This is not a hypothetical
1937 : !! concern: it was hit and diagnosed during development of this test, and produced clean-looking
1938 : !! but WRONG class assignments (e.g. an isym reported as belonging to two different classes
1939 : !! depending on which entry point read it) rather than an obvious crash.
1940 : !!
1941 : !! Note on algebraic structure:
1942 : !! ABINIT's symmetries correspond to a *right homomorphism*, meaning the product
1943 : !! of two symmetries S_3 = S_1 S_2 (where the rotation parts are R_3 = R_1 R_2)
1944 : !! yields representations that compose as D(S_3) \propto D(S_1) D(S_2).
1945 : !!
1946 : !! For fractional translations in non-symmorphic groups or due to G_0 vector mappings,
1947 : !! the exact analytical phases between operations can become extremely complex.
1948 : !! Therefore, the inverse relation and group multiplication tests extract the relative
1949 : !! phase dynamically using the Frobenius inner product Phase = Tr(A^\dagger B) / nb.
1950 : !! As long as the residual after phase-normalization is within DTOL, the matrices
1951 : !! strictly satisfy the projective representations of the space group.
1952 : !!
1953 : !! Diagnostic value of test 4 (group multiplication): unitarity (test 1) and the
1954 : !! inverse relation (test 3) are, by construction, blind to a bug in which every
1955 : !! D(S) is silently replaced by D(S)^{-1} = D(S^{-1}): both tests only ever compare
1956 : !! a matrix against itself or its own inverse, so swapping S <-> S^{-1} consistently
1957 : !! leaves them satisfied. The group multiplication test does NOT have this blind
1958 : !! spot for non-involutory S (S^2 != E), since D(S)<->D(S)^{-1} breaks the
1959 : !! non-commutative composition D(S_1 S_2) \propto D(S_1) D(S_2) as soon as one of the
1960 : !! three operations involved has order > 2 (D(S_3) becomes proportional to
1961 : !! D(S_1) D(S_2)^t instead). This is exactly how a real S<->S^{-1} mislabeling bug in
1962 : !! dmats_init was caught during development; see the NOTES section of dmats_init's
1963 : !! SOURCE header for the full explanation and fix.
1964 : !!
1965 : !! Test 4 loops over all itime1, itime2 in {1,2}, not just the pure-spatial (1,1)
1966 : !! case: this closes an analogous blind spot for time-reversal-related matrices
1967 : !! D(k, S, itime=2), which unitarity/inverse-relation alone cannot detect either.
1968 : !! The composite operation (isym3, itime3) is found from: (a) the ROTATION part,
1969 : !! which composes as symrel(isym1).symrel(isym2) regardless of itime1/itime2,
1970 : !! because \hat\Theta commutes with any pure spatial coordinate transformation
1971 : !! acting on the full (not just periodic-part) wavefunction; and (b) the
1972 : !! unitary/antiunitary character, itime3 = 1 + mod((itime1-1)+(itime2-1), 2),
1973 : !! i.e. antiunitary o antiunitary = unitary (valid for scalar wavefunctions,
1974 : !! Theta^2=+1; dmats_init hard-requires nspinor=1). The matrix relation itself
1975 : !! follows the Wigner co-representation composition law (Bradley & Cracknell,
1976 : !! sec. 7.3): D(A B) = D(A) D(B) if A is unitary, D(A B) = D(A) D(B)^* if A is
1977 : !! antiunitary (A applied after B, A=(isym1,itime1)).
1978 : !!
1979 : !! SOURCE
1980 :
1981 0 : subroutine dmats_check(dmats, units, prtvol, header)
1982 :
1983 : !Arguments ------------------------------------
1984 : class(dmats_t),intent(in) :: dmats
1985 : integer,intent(in) :: units(:), prtvol
1986 : character(len=*),optional,intent(in) :: header
1987 :
1988 : !Local variables-------------------------------
1989 : integer :: spin, ik_ibz, ierr
1990 : character(len=500) :: msg
1991 : type(yamldoc_t) :: ydoc
1992 : ! *************************************************************************
1993 :
1994 0 : msg = 'Info on the dmats_t'
1995 0 : if (present(header)) msg = trim(adjustl(header))
1996 0 : ydoc = yamldoc_open(tag="dmats", info=trim(msg))
1997 :
1998 0 : ierr = 0
1999 0 : do spin=1,size(dmats%for_spin)
2000 : ! Loop over k-points in the IBZ.
2001 0 : do ik_ibz=1,dmats%ks_ebands%nkpt
2002 : call dmats_check_one_k(dmats, spin, dmats%ks_ebands%kptns(:, ik_ibz), &
2003 : dmats%for_spin(spin)%value(:, :, :, :, ik_ibz), units, prtvol, &
2004 : sjoin("kpt_", ktoa(dmats%ks_ebands%kptns(:, ik_ibz)), "_spin_", itoa(spin)), &
2005 0 : ydoc, ierr)
2006 : end do ! ik_ibz
2007 : end do ! spin
2008 :
2009 0 : call ydoc%write_units_and_free(units)
2010 :
2011 0 : if (ierr /= 0) then
2012 0 : ABI_ERROR(sjoin("dmats are not unitary or failed tests! ierr:", itoa(ierr)))
2013 : end if
2014 :
2015 0 : end subroutine dmats_check
2016 : !!***
2017 :
2018 : !!****f* m_classify_bands/dmats_get_star_dmats
2019 : !! NAME
2020 : !! dmats_get_star_dmats
2021 : !!
2022 : !! FUNCTION
2023 : !! Build the D-matrices at a full-BZ k-point k' = S0.k_ibz (S0 = (isym0,itime0)), a
2024 : !! symmetry-star image of an already-computed IBZ k-point, using ONLY data already
2025 : !! stored in dmats (for_spin, multable, toinv, cryst) -- no WFK re-read (wfd is freed
2026 : !! by dmats_init once dmats%for_spin has been built).
2027 : !!
2028 : !! Define the wavefunction gauge at k' as |n,k'> := S0|n,k_ibz>. Then for any
2029 : !! g=(isym,itime) that stabilizes k' (g.k' = k' mod G), with h := S0^{-1}.g.S0
2030 : !! (physical operator composition):
2031 : !!
2032 : !! D^{k'}_{mn}(g) = <m,k'|g|n,k'> = <m,k_ibz|S0^{-1} g S0|n,k_ibz> = D^{k_ibz}_{mn}(h)
2033 : !!
2034 : !! exactly. IMPORTANT: resolving this h to a tabulated isym index via dmats%multable is
2035 : !! NOT simply "two multable products in the S0^{-1},g,S0 order": the file's k-vector
2036 : !! little-group test phi(s):=symrel(s)^t is an ANTI-homomorphism of multable's abstract
2037 : !! (plain, non-transposed) group law -- phi(s1 applied after s2) = phi(s2).phi(s1), order
2038 : !! REVERSED (same anti-homomorphism already flagged in dmats_check_one_k's NOTES on
2039 : !! get_classes). Working through phi(h)=phi(S0)^{-1}.phi(g).phi(S0) with this reversal
2040 : !! shows the correct tabulated composition is actually h = S0.g.S0^{-1} (see the detailed
2041 : !! derivation in this routine's SOURCE, right before the two multable calls).
2042 : !!
2043 : !! Since dmats%multable(1,...) gives the "tabulated" symrel entry sharing h's rotation,
2044 : !! not the literal Seitz product, the literal h differs from it by a lattice vector L_h:
2045 : !!
2046 : !! D^{k'}(g) = e^{-i 2pi k_ibz.L_h} * dmats%for_spin(spin)%value(:,:,isym_h,itime_h,ik_ibz)
2047 : !!
2048 : !! L_h is accumulated through TWO nested multable compositions (h is itself the double
2049 : !! product S0.(g.S0^{-1})), using the same reversed-inverse-argument lookup already
2050 : !! validated in dmats_check_one_k's group-multiplication test (L = multable(2:4,
2051 : !! toinv(isym2), toinv(isym1)) for a product D(S1 S2), S1 applied after S2).
2052 : !!
2053 : !! PROOF of L_h (worked from scratch, tabulated-operator algebra only): write
2054 : !! Sigma0^{-1} = Tab(isym0_inv) o T_{-L0} (L0 = toinv(2:4,isym0), from S0.Tab(isym0_inv) =
2055 : !! T_{L0}), and for ANY two tabulated elements A=Tab(a), B=Tab(b): A o B = T_{L(a,b)} o
2056 : !! Tab(multable(1,a,b)), L(a,b) = multable(2:4,a,b) -- the lattice correction sits on the
2057 : !! LEFT of the tabulated product, so it picks up a rotation (T_v o Tab(c) = Tab(c) o
2058 : !! T_{R(c)^{-1}.v}, equivalently R(c).T_v o Tab(c)... ) whenever it is pushed further left
2059 : !! past another rotation. Substituting Sigma0^{-1} into h_lit = S0.g.Sigma0^{-1} and
2060 : !! applying this rule twice (first at g.Tab(isym0_inv), then at S0.Tab(isym_tmp)) gives,
2061 : !! with NO free parameters left over:
2062 : !!
2063 : !! isym_tmp = multable(1, isym_g, isym0_inv); Ltmp = multable(2:4, isym_g, isym0_inv)
2064 : !! isym_h = multable(1, isym0, isym_tmp); Lh2 = multable(2:4, isym0, isym_tmp)
2065 : !! L_h = R0.Ltmp + Lh2 - R_h.L0 (R0 = symrel(:,:,isym0), R_h = symrel(:,:,isym_h))
2066 : !!
2067 : !! which matches the L_h computed in this routine's SOURCE exactly. This derivation used
2068 : !! ONLY real-space Seitz-operator algebra (unitarity of S0 plus associativity of operator
2069 : !! composition) -- no reciprocal-space bookkeeping is needed anywhere, so a genuinely
2070 : !! non-zero umklapp vector g0 in the little-group-of-k' test just below does NOT introduce
2071 : !! any extra phase on top of L_h: Bloch periodicity psi_{k+G} = psi_k is an exact identity,
2072 : !! not a gauge choice, so it never needed to be invoked in this chain. The one-step version
2073 : !! of this phase formula was independently, empirically validated (exact match in the
2074 : !! group-multiplication test on 768 tuples); this two-step formula is now ALSO proven, not
2075 : !! just extrapolated. If dmats_check_star still reports group-multiplication/closure
2076 : !! failures correlated with non-zero umklapp, the bug is therefore NOT in this phase
2077 : !! formula -- look instead at the (isym0,itime0) selection in
2078 : !! dmats_get_star_dmats_at_kpt (first-match-wins when k_ibz has a non-trivial little
2079 : !! group) or at the little-group-of-k' membership test just below.
2080 : !!
2081 : !! INPUTS
2082 : !! spin=Spin index.
2083 : !! ik_ibz=Index of the reference IBZ k-point (into dmats%ks_ebands%kptns).
2084 : !! isym0, itime0=Symmetry S0=(isym0,itime0) such that k' = S0.k_ibz.
2085 : !!
2086 : !! OUTPUT
2087 : !! dmat_star(:,:,:,:)=D-matrices (nb,nb,nsym,2) at k'. Caller-owned: allocated here
2088 : !! (ABI_MALLOC), must be freed by the caller (ABI_FREE).
2089 : !! msg, ierr=Error message and status (ierr=0 on success). No ABI_ERROR is raised here:
2090 : !! this routine is meant to be usable in a validation loop over many k', where the
2091 : !! caller decides whether to abort (e.g. via ABI_CHECK_IEQ(ierr, 0, msg)) or skip.
2092 : !!
2093 : !! SOURCE
2094 :
2095 0 : subroutine dmats_get_star_dmats(dmats, spin, ik_ibz, isym0, itime0, dmat_star, msg, ierr)
2096 :
2097 : !Arguments ------------------------------------
2098 : class(dmats_t),intent(in) :: dmats
2099 : integer,intent(in) :: spin, ik_ibz, isym0, itime0
2100 : complex(dp),allocatable,intent(out) :: dmat_star(:,:,:,:)
2101 : character(len=*),intent(out) :: msg
2102 : integer,intent(out) :: ierr
2103 :
2104 : !Local variables-------------------------------
2105 : integer :: nsym, nb, isym, itime, isym_tmp, isym_h, itime_tmp, itime_h, isym0_inv, j
2106 : integer :: g0(3), g0_h(3)
2107 : real(dp) :: kk_ibz(3), kprime(3), kk_sk(3), tsign0, tsign
2108 : real(dp) :: L_h(3)
2109 : complex(dp) :: phase_h
2110 : ! *********************************************************************
2111 :
2112 0 : ierr = 0; msg = ""
2113 0 : nsym = dmats%cryst%nsym
2114 0 : nb = dmats%brange_spin(2, spin) - dmats%brange_spin(1, spin) + 1
2115 0 : kk_ibz = dmats%ks_ebands%kptns(:, ik_ibz)
2116 :
2117 : ! k' = S0.k_ibz (symrel^t convention, consistent with dmats_init/dmats_check_one_k).
2118 0 : tsign0 = merge(one, -one, itime0 == 1)
2119 0 : kprime = tsign0 * matmul(transpose(real(dmats%cryst%symrel(:,:,isym0), dp)), kk_ibz)
2120 :
2121 0 : isym0_inv = dmats%toinv(1, isym0)
2122 0 : if (isym0_inv == 0) then
2123 0 : ierr = 1; msg = "Could not find inverse of isym0"; return
2124 : end if
2125 :
2126 0 : ABI_MALLOC(dmat_star, (nb, nb, nsym, 2))
2127 0 : dmat_star = czero
2128 :
2129 0 : do itime=1,2
2130 0 : tsign = merge(one, -one, itime == 1)
2131 0 : do isym=1,nsym
2132 :
2133 : ! Is g=(isym,itime) in the little group of k' = S0.k_ibz?
2134 0 : kk_sk = tsign * matmul(transpose(real(dmats%cryst%symrel(:,:,isym), dp)), kprime)
2135 0 : g0 = nint(kprime - kk_sk)
2136 0 : if (.not. all(abs(kprime - kk_sk - g0) < tol8)) then
2137 : ! Not in little group of k': identity placeholder, mirrors dmats%for_spin's own convention.
2138 0 : do j=1,nb
2139 0 : dmat_star(j, j, isym, itime) = cone
2140 : end do
2141 0 : cycle
2142 : end if
2143 :
2144 : ! Compose h = S0.g.S0^{-1} via TWO multable compositions: multable(1,s1,s2) = index of
2145 : ! "s1 applied after s2" (see sg_multable), i.e. the plain, NON-transposed real-space
2146 : ! rotation-matrix product R(s1).R(s2).
2147 : !
2148 : ! NOTE the conjugation direction: naively one would expect h = S0^{-1}.g.S0 (as in an
2149 : ! ordinary homomorphism), but the file's k-vector action phi(s) := symrel(s)^t is an
2150 : ! ANTI-homomorphism of the abstract (multable) group law: phi(s1 "applied after" s2) =
2151 : ! R(s1.s2)^t = R(s2)^t.R(s1)^t = phi(s2).phi(s1) -- composition order REVERSES (this is
2152 : ! the same anti-homomorphism already noted in dmats_check_one_k's NOTES on get_classes).
2153 : ! Requiring phi(h) = phi(S0)^{-1}.phi(g).phi(S0) (so that h stabilizes k_ibz whenever g
2154 : ! stabilizes k'=phi(S0).k_ibz) and using phi(A)phi(B)=phi(B.A) twice gives
2155 : ! phi(h) = phi(S0.g.S0^{-1}), i.e. h = S0.g.S0^{-1}, NOT S0^{-1}.g.S0.
2156 0 : isym_tmp = dmats%multable(1, isym, isym0_inv) ! tmp = g . S0^{-1}
2157 0 : if (isym_tmp == 0) then
2158 0 : ierr = 2; msg = "multable(isym, isym0_inv) not found: group closure violated?"; return
2159 : end if
2160 0 : itime_tmp = 1 + mod((itime - 1) + (itime0 - 1), 2)
2161 :
2162 0 : isym_h = dmats%multable(1, isym0, isym_tmp) ! h = S0 . tmp = S0.g.S0^{-1}
2163 0 : if (isym_h == 0) then
2164 0 : ierr = 2; msg = "multable(isym0, isym_tmp) not found: group closure violated?"; return
2165 : end if
2166 0 : itime_h = 1 + mod((itime0 - 1) + (itime_tmp - 1), 2) ! always equals itime (parity self-cancels)
2167 0 : if (itime_h /= itime) then
2168 0 : ierr = 2; msg = "itime_h != itime_g: parity composition bug"; return
2169 : end if
2170 :
2171 : ! Defensive check: h must stabilize k_ibz by construction.
2172 0 : kk_sk = tsign * matmul(transpose(real(dmats%cryst%symrel(:,:,isym_h), dp)), kk_ibz)
2173 0 : g0_h = nint(kk_ibz - kk_sk)
2174 0 : if (.not. all(abs(kk_ibz - kk_sk - g0_h) < tol8)) then
2175 0 : ierr = 2; msg = "h does not stabilize k_ibz: composition bug"; return
2176 : end if
2177 :
2178 : ! Lattice-vector correction L_h, derived directly from Seitz algebra (verified against
2179 : ! the g=identity special case, where it must vanish exactly -- D(identity) = I with no
2180 : ! phase, always). Writing S0={R0,tau0}, g={Rg,taug}, and S0_inv_tab=dmats%toinv's TABULATED
2181 : ! entry for S0^{-1} (which equals the EXACT inverse only up to an extra lattice shift
2182 : ! m0 = R0^{-1}.L0, L0=toinv(2:4,isym0), since toinv only guarantees S0.S0_inv_tab={I,L0}):
2183 : !
2184 : ! tmp_literal := g . S0_inv_tab = {I, Ltmp} . TABULATED_tmp, Ltmp = multable(2:4,isym,isym0_inv)
2185 : ! h_tab_literal := S0 . tmp_literal = {I, R0.Ltmp + Lh2} . TABULATED_h, Lh2 = multable(2:4,isym0,isym_tmp)
2186 : !
2187 : ! h_tab_literal uses S0_inv_tab, not the EXACT inverse \hat S0^{-1} = {I,-m0}.S0_inv_tab; undoing
2188 : ! that extra {I,m0} shift (tracked through the same two compositions) gives the additional
2189 : ! correction -R_h.L0 (R_h=symrel(isym_h)), so that for g=identity (Ltmp=Lh2=L0, R_h=I) the
2190 : ! total L_h = R0.0 + L0 - I.L0 = 0 exactly, as required:
2191 : !
2192 : ! L_h = R0.Ltmp + Lh2 - R_h.L0
2193 : !
2194 : ! The resulting {I,L_h} pure-lattice-translation factor is applied AFTER TABULATED_h (which
2195 : ! stabilizes k_ibz), so the state is still at k_ibz when the translation phase is picked up:
2196 : ! phase_h = e^{-i 2pi k_ibz.L_h} (dot directly with k_ibz, not with a rotated k_ibz).
2197 : L_h = matmul(real(dmats%cryst%symrel(:,:,isym0), dp), real(dmats%multable(2:4, isym, isym0_inv), dp)) &
2198 : + real(dmats%multable(2:4, isym0, isym_tmp), dp) &
2199 0 : - matmul(real(dmats%cryst%symrel(:,:,isym_h), dp), real(dmats%toinv(2:4, isym0), dp))
2200 :
2201 0 : phase_h = exp(cmplx(zero, -two_pi * sum(kk_ibz * L_h), dp))
2202 0 : if (itime == 2) phase_h = conjg(phase_h)
2203 :
2204 0 : dmat_star(:, :, isym, itime) = phase_h * dmats%for_spin(spin)%value(:, :, isym_h, itime, ik_ibz)
2205 : end do
2206 : end do
2207 :
2208 0 : end subroutine dmats_get_star_dmats
2209 : !!***
2210 :
2211 : !!****f* m_classify_bands/dmats_get_star_dmats_at_kpt
2212 : !! NAME
2213 : !! dmats_get_star_dmats_at_kpt
2214 : !!
2215 : !! FUNCTION
2216 : !! Convenience wrapper around dmats_get_star_dmats: given a raw full-BZ k-point kprime,
2217 : !! locate (ik_ibz, isym0, itime0) such that kprime = tsign0*symrel(isym0)^t.k_ibz (mod G),
2218 : !! scanning ALL IBZ k-points, then build the D-matrices at kprime.
2219 : !!
2220 : !! Uses the SAME symrel^t convention as dmats_init/dmats_check_one_k (NOT kpts_map/listkk/
2221 : !! littlegroup_q, which use a symrec-based convention that can disagree with symrel^t
2222 : !! whenever symrel is not orthogonal in the reduced basis -- see the NOTES in
2223 : !! dmats_check_one_k on this exact point).
2224 : !!
2225 : !! INPUTS
2226 : !! spin=Spin index.
2227 : !! kprime(3)=Target k-point (reduced coords) in the full BZ.
2228 : !!
2229 : !! OUTPUT
2230 : !! dmat_star(:,:,:,:)=D-matrices (nb,nb,nsym,2) at kprime. Caller-owned (ABI_MALLOC/ABI_FREE).
2231 : !! ik_ibz, isym0, itime0=The located triple, returned so the caller can report/reuse it.
2232 : !! msg, ierr=Error message and status (ierr=0 on success, /=0 if kprime is not the star
2233 : !! image of any IBZ k-point in dmats%ks_ebands).
2234 : !!
2235 : !! SOURCE
2236 :
2237 0 : subroutine dmats_get_star_dmats_at_kpt(dmats, spin, kprime, dmat_star, ik_ibz, isym0, itime0, msg, ierr)
2238 :
2239 : !Arguments ------------------------------------
2240 : class(dmats_t),intent(in) :: dmats
2241 : integer,intent(in) :: spin
2242 : real(dp),intent(in) :: kprime(3)
2243 : complex(dp),allocatable,intent(out) :: dmat_star(:,:,:,:)
2244 : integer,intent(out) :: ik_ibz, isym0, itime0
2245 : character(len=*),intent(out) :: msg
2246 : integer,intent(out) :: ierr
2247 :
2248 : !Local variables-------------------------------
2249 : integer :: jk_ibz, jsym, jtime
2250 : real(dp) :: kk_ibz(3), kk_sk(3), tsign, g0(3), resid, best_resid
2251 : logical :: found
2252 : ! *********************************************************************
2253 :
2254 0 : ierr = 0; msg = ""; found = .False.
2255 0 : ik_ibz = -1; isym0 = -1; itime0 = -1
2256 0 : best_resid = huge(one)
2257 :
2258 0 : search: do jk_ibz=1,dmats%ks_ebands%nkpt
2259 0 : kk_ibz = dmats%ks_ebands%kptns(:, jk_ibz)
2260 0 : do jtime=1,2
2261 0 : tsign = merge(one, -one, jtime == 1)
2262 0 : do jsym=1,dmats%cryst%nsym
2263 0 : kk_sk = tsign * matmul(transpose(real(dmats%cryst%symrel(:,:,jsym), dp)), kk_ibz)
2264 0 : g0 = nint(kprime - kk_sk)
2265 0 : resid = maxval(abs(kprime - kk_sk - g0))
2266 0 : best_resid = min(best_resid, resid)
2267 0 : if (all(abs(kprime - kk_sk - g0) < tol8)) then
2268 0 : ik_ibz = jk_ibz; isym0 = jsym; itime0 = jtime; found = .True.
2269 : exit search
2270 : end if
2271 : end do
2272 : end do
2273 : end do search
2274 :
2275 : if (.not. found) then
2276 0 : ierr = 1
2277 : msg = sjoin("kprime:", ktoa(kprime), "is not the symmetry-star image of any IBZ k-point", &
2278 0 : "(best residual found:", ftoa(best_resid), ")")
2279 0 : return
2280 : end if
2281 :
2282 0 : call dmats_get_star_dmats(dmats, spin, ik_ibz, isym0, itime0, dmat_star, msg, ierr)
2283 :
2284 0 : end subroutine dmats_get_star_dmats_at_kpt
2285 : !!***
2286 :
2287 : !!****f* m_classify_bands/dmats_check_star
2288 : !! NAME
2289 : !! dmats_check_star
2290 : !!
2291 : !! FUNCTION
2292 : !! Locate a full-BZ k-point kprime as the symmetry-star image of an IBZ k-point, build
2293 : !! its D-matrices via dmats_get_star_dmats_at_kpt (pure group-theory reconstruction, no
2294 : !! WFK re-read), and run the full dmats_check_one_k test battery (unitarity, identity,
2295 : !! class character, Kramers, inverse relation, group multiplication, S^n closure) on
2296 : !! them. This is an independent test of the multable/toinv/conjugation logic used by
2297 : !! dmats_get_star_dmats, in an off-little-group regime dmats_check never exercises.
2298 : !!
2299 : !! INPUTS
2300 : !! spin=Spin index.
2301 : !! kprime(3)=Target k-point (reduced coords) in the full BZ.
2302 : !! units(:), prtvol=Output units and verbosity.
2303 : !!
2304 : !! OUTPUT
2305 : !! ierr=0 if kprime was located and all sub-tests passed, /=0 otherwise.
2306 : !!
2307 : !! SOURCE
2308 :
2309 0 : subroutine dmats_check_star(dmats, spin, kprime, units, prtvol, ierr)
2310 :
2311 : !Arguments ------------------------------------
2312 : class(dmats_t),intent(in) :: dmats
2313 : integer,intent(in) :: spin, units(:), prtvol
2314 : real(dp),intent(in) :: kprime(3)
2315 : integer,intent(out) :: ierr
2316 :
2317 : !Local variables-------------------------------
2318 : integer :: ik_ibz, isym0, itime0
2319 : character(len=500) :: msg
2320 0 : complex(dp),allocatable :: dmat_star(:,:,:,:)
2321 : type(yamldoc_t) :: ydoc
2322 : ! Cross-check dmat_star (pure group theory) against genuinely WFK-computed D-matrices.
2323 : integer :: nsym, isym, itime, isym1, itime1, n_cross_ok, n_cross_bad, nb
2324 : real(dp) :: tsign, kk_ibz(3), kk_g(3), g0(3), maxdiff
2325 0 : complex(dp),allocatable :: d_composed(:,:)
2326 : ! *********************************************************************
2327 :
2328 : ierr = 0
2329 0 : call dmats_get_star_dmats_at_kpt(dmats, spin, kprime, dmat_star, ik_ibz, isym0, itime0, msg, ierr)
2330 0 : if (ierr /= 0) then
2331 0 : call wrtout(units, sjoin("dmats_check_star: get_star_dmats_at_kpt failed:", msg))
2332 0 : return
2333 : end if
2334 :
2335 : ydoc = yamldoc_open(tag="dmats_star", &
2336 : info=sjoin("Star k-point check: kprime=", ktoa(kprime), ", ik_ibz=", itoa(ik_ibz), &
2337 0 : ", isym0=", itoa(isym0), ", itime0=", itoa(itime0)))
2338 :
2339 : call dmats_check_one_k(dmats, spin, kprime, dmat_star, units, prtvol, &
2340 0 : sjoin("starkpt_", ktoa(kprime), "_spin_", itoa(spin)), ydoc, ierr)
2341 :
2342 0 : call ydoc%write_units_and_free(units)
2343 :
2344 : ! Cross-check: dmat_star (pure group theory, no WFK access) vs D(S1) computed genuinely
2345 : ! from real wavefunction data (dmats_init's own non-little-group branch), for S1 := g.S0,
2346 : ! g any little-group element of kprime. Physically D(S1) = dmat_star(g) @ D(S0) (composing
2347 : ! the crossing rotation S0 with the k'-internal rotation g: <l,k'|g.S0|n,k_ibz> =
2348 : ! sum_m <l,k'|g|m,k'><m,k'|S0|n,k_ibz>, a resolution of identity over the {psi_m,k'}
2349 : ! basis). Both sides are now independently available (dmat_star via group theory,
2350 : ! D(S0)/D(S1) via genuine WFK data through dmats_init's generalized non-little-group
2351 : ! branch), so this directly tests whether dmat_star's implicit gauge assumption (that
2352 : ! |psi,k'> := S0|psi,k_ibz>) matches the physical wavefunction gstore_compute/kpts_map
2353 : ! would independently reconstruct -- the validation-coverage gap this whole
2354 : ! generalization was built to close (see gstore_symmetrize_status memory). dmat_star's
2355 : ! own placeholder-identity entries (non-little-group g) cannot be told apart from a
2356 : ! genuine identity result by value alone, so little-group membership of kprime is
2357 : ! re-derived directly here, mirroring dmats_get_star_dmats's own test.
2358 0 : nsym = dmats%cryst%nsym
2359 0 : nb = dmats%brange_spin(2, spin) - dmats%brange_spin(1, spin) + 1
2360 0 : kk_ibz = dmats%ks_ebands%kptns(:, ik_ibz)
2361 0 : ABI_MALLOC(d_composed, (nb, nb))
2362 0 : n_cross_ok = 0; n_cross_bad = 0
2363 :
2364 0 : do itime=1,2
2365 0 : tsign = merge(one, -one, itime == 1)
2366 0 : do isym=1,nsym
2367 0 : kk_g = tsign * matmul(transpose(real(dmats%cryst%symrel(:,:,isym), dp)), kprime)
2368 0 : g0 = nint(kprime - kk_g)
2369 0 : if (.not. all(abs(kprime - kk_g - g0) < tol8)) cycle ! Not in the little group of kprime.
2370 :
2371 0 : isym1 = dmats%multable(1, isym, isym0)
2372 0 : itime1 = 1 + mod((itime - 1) + (itime0 - 1), 2)
2373 0 : if (isym1 == 0) then
2374 0 : call wrtout(units, " dmats_check_star cross-check: multable(isym,isym0) not found, skipping")
2375 0 : cycle
2376 : end if
2377 :
2378 0 : d_composed = matmul(dmat_star(:,:,isym,itime), dmats%for_spin(spin)%value(:,:,isym0,itime0,ik_ibz))
2379 0 : maxdiff = maxval(abs(d_composed - dmats%for_spin(spin)%value(:,:,isym1,itime1,ik_ibz)))
2380 0 : if (maxdiff < tol6) then
2381 0 : n_cross_ok = n_cross_ok + 1
2382 : else
2383 0 : n_cross_bad = n_cross_bad + 1
2384 0 : if (prtvol > 0) then
2385 : write(msg,'(a,7(i0,1x),a,es12.4)') &
2386 0 : " dmats_check_star cross-check FAIL: ik_ibz,isym0,itime0,isym,itime,isym1,itime1= ", &
2387 0 : ik_ibz, isym0, itime0, isym, itime, isym1, itime1, " maxdiff= ", maxdiff
2388 0 : call wrtout(units, msg)
2389 : end if
2390 : end if
2391 : end do
2392 : end do
2393 :
2394 : call wrtout(units, sjoin(" dmats_check_star cross-check (dmat_star vs genuine WFK D-matrices): ", &
2395 0 : itoa(n_cross_ok), "/", itoa(n_cross_ok + n_cross_bad), " composed relations agree"))
2396 0 : if (n_cross_bad > 0) ierr = ierr + 1
2397 :
2398 0 : ABI_FREE(d_composed)
2399 0 : ABI_FREE(dmat_star)
2400 :
2401 0 : end subroutine dmats_check_star
2402 : !!***
2403 :
2404 : !!****f* m_classify_bands/dmats_classify
2405 : !! NAME
2406 : !! dmats_classify
2407 : !!
2408 : !! FUNCTION
2409 : !! Classify the KS states based on the computed representation matrices (dmats)
2410 : !! using the irreducible representations of the little group of k.
2411 : !!
2412 : !! SOURCE
2413 :
2414 0 : subroutine dmats_classify(dmats, prtvol)
2415 :
2416 : !Arguments ------------------------------------
2417 : class(dmats_t), target, intent(in) :: dmats
2418 : integer,intent(in) :: prtvol
2419 :
2420 : !Local variables-------------------------------
2421 0 : type(esymm_t) :: Bsym
2422 : integer :: spin, bstart, nb, ik_ibz, idg, iclass, isym_class, sym_idx, isym, tr_isym, ib_start, ib_stop
2423 0 : real(dp), pointer :: ene_k(:)
2424 : real(dp) :: kk_ibz(3)
2425 : ! *************************************************************************
2426 :
2427 0 : do spin=1, size(dmats%for_spin)
2428 0 : bstart = dmats%brange_spin(1, spin)
2429 0 : nb = dmats%brange_spin(2, spin) - bstart + 1
2430 :
2431 0 : do ik_ibz=1, dmats%ks_ebands%nkpt
2432 0 : kk_ibz = dmats%ks_ebands%kptns(:, ik_ibz)
2433 0 : ene_k => dmats%ks_ebands%eig(bstart:dmats%brange_spin(2, spin), ik_ibz, spin)
2434 :
2435 : !only_trace = .false.
2436 : call Bsym%init(kk_ibz, dmats%cryst, .false., dmats%ks_ebands%nspinor, &
2437 0 : bstart, nb, dmats%dtset%symsigma_de, ene_k, tol3)
2438 :
2439 0 : if (Bsym%err_status /= 0) cycle
2440 :
2441 0 : do idg=1, Bsym%ndegs
2442 0 : ib_start = Bsym%degs_bounds(1, idg) ! relative to bstart
2443 0 : ib_stop = Bsym%degs_bounds(2, idg)
2444 :
2445 0 : sym_idx = 0
2446 0 : do iclass=1, Bsym%nclass
2447 0 : do isym_class=1, Bsym%nelements(iclass)
2448 0 : sym_idx = sym_idx + 1
2449 0 : isym = Bsym%sgk2symrec(sym_idx)
2450 0 : associate(cmat => dmats%for_spin(spin)%value(:,:, isym, 1, ik_ibz))
2451 0 : Bsym%Calc_irreps(idg)%mat(:,:,sym_idx) = cmat(ib_start:ib_stop, ib_start:ib_stop)
2452 0 : Bsym%Calc_irreps(idg)%trace(sym_idx) = get_trace(Bsym%Calc_irreps(idg)%mat(:,:,sym_idx))
2453 : end associate
2454 : end do
2455 : end do
2456 :
2457 0 : if (Bsym%can_use_tr) then
2458 0 : do tr_isym=1, Bsym%nsym_trgk
2459 0 : isym = Bsym%tr_sgk2symrec(tr_isym)
2460 0 : associate(cmat => dmats%for_spin(spin)%value(:,:, isym, 2, ik_ibz))
2461 0 : Bsym%trCalc_irreps(idg)%mat(:,:,tr_isym) = cmat(ib_start:ib_stop, ib_start:ib_stop)
2462 0 : Bsym%trCalc_irreps(idg)%trace(tr_isym) = get_trace(Bsym%trCalc_irreps(idg)%mat(:,:,tr_isym))
2463 : end associate
2464 : end do
2465 : end if
2466 : end do
2467 :
2468 0 : call Bsym%finalize(prtvol)
2469 0 : call Bsym%print([std_out, ab_out], prtvol=prtvol)
2470 0 : call esymm_free(Bsym)
2471 : end do
2472 : end do
2473 :
2474 0 : end subroutine dmats_classify
2475 : !!***
2476 :
2477 : !----------------------------------------------------------------------
2478 :
2479 0 : end module m_classify_bands
2480 : !!***
|