Branch data 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 : : !!***
|