Line data Source code
1 : !!****m*ABINIT/m_lwf
2 : !! NAME
3 : !! m_lwf
4 : !!
5 : !! FUNCTION
6 : !! Module for the lattice Wannier function
7 : !! Container type is defined, and destruction, print subroutines
8 : !! as well as the central mkphdos
9 : !!
10 : !! COPYRIGHT
11 : !! Copyright (C) 1999-2026 ABINIT group (HeXu)
12 : !! This file is distributed under the terms of the
13 : !! GNU General Public Licence, see ~abinit/COPYING
14 : !! or http://www.gnu.org/copyleft/gpl.txt .
15 : !! For the initials of contributors, see ~abinit/doc/developers/contributors.txt .
16 : !!
17 : !! SOURCE
18 :
19 :
20 : !! Todolist:
21 : !! - LWF output filename change
22 : !! - LWF specific outputs: lwf_masses, original crystal structure
23 : !! - Units of Hamiltonian need to be changed to eV-Angstrom units
24 : !! - dipole-dipole
25 : !! - MPI
26 : !! - exclude_bands
27 :
28 : #if defined HAVE_CONFIG_H
29 : #include "config.h"
30 : #endif
31 :
32 : #include "abi_common.h"
33 :
34 : module m_lwf
35 :
36 : use, intrinsic :: iso_c_binding
37 : use defs_basis
38 : use m_errors
39 : use m_xmpi
40 : use m_abicore
41 : use m_htetra
42 : use m_numeric_tools
43 : use m_cgtools
44 : use m_crystal
45 : use m_nctk
46 : use m_atprj
47 : use m_sortph
48 : use m_ddb
49 : use netcdf
50 : use m_supercell
51 : use m_dtset
52 : use m_krank
53 :
54 : use m_fstrings, only : ltoa
55 : use m_io_tools, only : open_file
56 : use m_geometry, only : mkrdim, symredcart, normv, phdispl_cart2red
57 : use m_dynmat, only : gtdyn9, dfpt_phfrq, dfpt_prtph, pheigvec_normalize, massmult_and_breaksym, phdispl_from_eigvec
58 : use m_bz_mesh, only : make_path
59 : use m_ifc, only : ifc_type
60 : use m_anaddb_dataset, only : anaddb_dataset_type
61 : use m_kpts, only : kpts_ibz_from_kptrlatt
62 : use m_scdm_math, only : build_Rgrid
63 : use m_wannier_builder, only : WannierBuilder_witheigen_t
64 : use m_wann_netcdf, only : IOWannNC
65 :
66 :
67 : implicit none
68 :
69 : type LatticeWannier
70 : type(WannierBuilder_witheigen_t):: scdm
71 : type(ifc_type), pointer:: ifc
72 : type(crystal_t), pointer:: crystal
73 : integer:: qptrlatt(3, 3)
74 : real(dp):: shiftq(3)
75 : integer:: nqibz
76 : real(dp), allocatable:: qibz(:, :), qweights(:)
77 : integer:: nR, natom
78 : integer, allocatable:: Rlist(:, :)
79 : real(dp), allocatable:: eigenvalues(:, :) ! iband, iqpt
80 : complex(dp), allocatable:: eigenvectors(:,:, :) ! ibasis, iband, iqpt
81 : integer:: comm, nprocs, my_rank
82 : contains
83 : procedure:: initialize
84 : procedure:: finalize
85 : procedure:: init_mpi
86 : procedure:: sanity_check
87 : procedure:: prepare_qpoints
88 : procedure:: prepare_Rlist
89 : procedure:: get_ifc_eigens
90 : procedure:: run_all
91 : procedure:: write_lwf_nc
92 : procedure:: print_Rlist
93 : procedure :: write_bands
94 : end type LatticeWannier
95 :
96 : private
97 :
98 : public:: run_lattice_wannier
99 :
100 : contains
101 3 : subroutine initialize(self, ifc, crystal, dtset, comm)
102 : class(LatticeWannier), intent(inout):: self
103 : integer, intent(in):: comm
104 : type(ifc_type), target, intent(in):: ifc
105 : type(crystal_t), target, intent(in):: crystal
106 : type(anaddb_dataset_type), intent(in):: dtset
107 6 : real(dp):: symcart(3, 3, crystal%nsym)
108 : integer:: isym
109 : ! TODO: add exclude_bands
110 : integer:: exclude_bands(0)
111 :
112 3 : character(len = 500+dtset%lwf_nwann*10):: msg
113 : real(dp):: mu, sigma
114 :
115 3 : self%ifc => ifc
116 3 : self%crystal => crystal
117 :
118 : ! mpi vars
119 3 : call self%init_mpi(comm)
120 :
121 : ! sanity check
122 3 : call self%sanity_check(dtset)
123 :
124 147 : do isym = 1, crystal%nsym
125 147 : call symredcart(crystal%rprimd, crystal%gprimd, symcart(:,:,isym), crystal%symrel(:,:,isym))
126 : end do
127 :
128 : ! prepare qpoints
129 3 : call self%prepare_qpoints(crystal, dtset)
130 3 : call self%prepare_Rlist()
131 : ! prepare eigen values and eigen vectors
132 :
133 3 : call self%get_ifc_eigens(ifc, crystal)
134 :
135 : ! set mu and sigma from cm^-1 to eigenvalue
136 3 : if(dtset%lwfflag == 1 .or. dtset%lwfflag == 2) then
137 6 : mu = freq_to_eigenval(dtset%lwf_mu/Ha_cmm1)
138 3 : sigma = freq_to_eigenval(dtset%lwf_sigma/Ha_cmm1)
139 : end if
140 :
141 : ! set up scdm
142 :
143 : call self%scdm%initialize( &
144 : & kpts = self%qibz, kweights = self%qweights, Rlist = self%Rlist, &
145 : & nwann = dtset%lwf_nwann, nbasis=self%natom*3, nband=self%natom*3, disentangle_func_type = dtset%lwf_disentangle, &
146 : & mu = mu, sigma = sigma, exclude_bands = exclude_bands, &
147 3 : & project_to_anchor = (dtset%lwf_anchor_proj > 0 ), method = dtset%lwfflag)
148 3 : call self%scdm%set_eigen(evals = self%eigenvalues, psi = self%eigenvectors)
149 :
150 3 : if(dtset%lwfflag == 1) then
151 2 : write(msg, '(a)') ' Constructing LWF with SCDM-k method.'
152 6 : call wrtout([ab_out, std_out], msg )
153 2 : write(msg, '(a, i0)') ' Number of LWF: ', dtset%lwf_nwann
154 6 : call wrtout([ab_out, std_out], msg )
155 2 : write(msg, '(a, 3f8.5)') ' Anchor Points q-point: ', &
156 4 : & dtset%lwf_anchor_qpt(1), dtset%lwf_anchor_qpt(2), dtset%lwf_anchor_qpt(3)
157 : !write(msg, '(2a) ') 'Anchor points band indices: ', trim(ltoa(self%scdm%anchor_ibands))
158 6 : call wrtout([ab_out, std_out], msg )
159 :
160 2 : if(dtset%lwf_anchor_iband(1) > 0) then
161 2 : call self%scdm%set_anchor( dtset%lwf_anchor_qpt, dtset%lwf_anchor_iband)
162 : else
163 0 : call self%scdm%set_anchor(anchor_kpt = dtset%lwf_anchor_qpt)
164 : end if
165 : ! output information
166 1 : else if(dtset%lwfflag == 2) then
167 : ! TODO: projected lattice wannier function
168 1 : write(msg, '(a)') ' Constructing LWF with projected wannier function method.'
169 3 : call wrtout([ab_out, std_out], msg )
170 1 : call self%scdm%set_disp_projector(dtset%lwf_projector)
171 1 : write(msg, '(2a)') ' The projectors: ', trim(ltoa(dtset%lwf_projector))
172 3 : call wrtout([ab_out, std_out], msg )
173 : end if
174 :
175 3 : end subroutine initialize
176 :
177 3 : subroutine finalize(self)
178 : class(LatticeWannier), intent(inout):: self
179 3 : call self%scdm%finalize()
180 3 : ABI_SFREE(self%qibz)
181 3 : ABI_SFREE(self%qweights)
182 3 : ABI_SFREE(self%eigenvalues)
183 3 : ABI_SFREE(self%eigenvectors)
184 3 : ABI_SFREE(self%Rlist)
185 3 : end subroutine finalize
186 :
187 3 : subroutine init_mpi(self, comm)
188 : class(LatticeWannier), intent(inout):: self
189 : integer, intent(in):: comm
190 3 : self%comm = comm
191 3 : self%nprocs = xmpi_comm_size(comm)
192 3 : self%my_rank = xmpi_comm_rank(comm)
193 3 : end subroutine init_mpi
194 :
195 3 : subroutine sanity_check(self, dtset)
196 : class(LatticeWannier), intent(inout):: self
197 : type(anaddb_dataset_type), intent(in):: dtset
198 3 : if(self%nprocs /= 1) then
199 0 : ABI_ERROR(" MPI is not yet implemented for Lattice Wannier function.")
200 : end if
201 :
202 3 : if(dtset%dipdip > 0) then
203 0 : ABI_ERROR(" dipdip is not yet implemented for Lattice Wannier function.")
204 : end if
205 3 : end subroutine sanity_check
206 :
207 :
208 3 : subroutine prepare_qpoints(self, crystal, dtset)
209 : class(LatticeWannier), intent(inout):: self
210 : type(crystal_t), intent(in):: crystal
211 : type(anaddb_dataset_type), intent(in):: dtset
212 : integer:: nqshft = 1
213 : real(dp):: lwf_qshift(3, 1)
214 : integer:: nqbz
215 3 : real(dp), allocatable:: qbz(:, :)
216 : integer:: in_qptrlatt(3, 3), new_qptrlatt(3, 3)
217 3 : integer, allocatable:: bz2ibz_smap(:,:)!, bz2ibz(:)
218 3 : real(dp), allocatable:: new_shiftq(:,:)
219 : integer, parameter:: bcorr0 = 0, master = 0
220 : integer:: my_qptopt
221 : character(len = 500):: msg
222 : integer:: iqpt, nkpout
223 : ! Copied from m_phonons/mkphdos
224 3 : in_qptrlatt = 0
225 3 : in_qptrlatt(1, 1) = dtset%lwf_ngqpt(1)
226 3 : in_qptrlatt(2, 2) = dtset%lwf_ngqpt(2)
227 3 : in_qptrlatt(3, 3) = dtset%lwf_ngqpt(3)
228 :
229 : ! TODO: shift is now not supported.
230 3 : nqshft = 1
231 3 : lwf_qshift(:, :) = 0.0_dp
232 :
233 : ! TODO: HeXu: symmetry is not used. Check if it is available.
234 : ! In m_phonons, mkphdos, there is the comment:
235 : ! Rotate e(q) to get e(Sq) to account for symmetrical q-points in BZ.
236 : ! eigenvectors indeed are not invariant under rotation. See e.g. Eq 39-40 of PhysRevB.76.165108 [[cite:Giustino2007]].
237 : ! In principle there's a phase due to nonsymmorphic translations but we here need |e(Sq)_iatom|**2
238 3 : my_qptopt = 3
239 :
240 : call kpts_ibz_from_kptrlatt(crystal, in_qptrlatt, my_qptopt, nqshft, lwf_qshift, &
241 : & self%nqibz, self%qibz, self%qweights, nqbz, qbz, new_kptrlatt = new_qptrlatt, &
242 3 : & new_shiftk = new_shiftq, bz2ibz = bz2ibz_smap)
243 :
244 3 : ABI_FREE(bz2ibz_smap)
245 : !ABI_FREE(bz2ibz)
246 :
247 39 : self%qptrlatt = new_qptrlatt
248 12 : self%shiftq(:) = new_shiftq(:, 1) ! only one shift in output
249 3 : if (self%my_rank == master) then
250 3 : write(msg, "(3a, i0)")" LWF ngqpt: ", trim(ltoa(dtset%lwf_ngqpt)), ", qptopt: ", my_qptopt
251 9 : call wrtout([ab_out, std_out], msg)
252 3 : write(msg, "(2(a, i0))")" Number of q-points in the IBZ: ", self%nqibz, ", number of MPI processes: ", self%nprocs
253 9 : call wrtout([ab_out, std_out], msg)
254 :
255 3 : write(msg, "(a)") " List of q-points: "
256 9 : call wrtout([ab_out, std_out], msg)
257 :
258 3 : write(msg,'(a,i8)')' Grid q points for sampling in the reciprocal space : ',self%nqibz
259 9 : call wrtout([ab_out, std_out],msg,'COLL')
260 3 : nkpout=self%nqibz
261 3 : if(self%nqibz>80)then
262 0 : call wrtout([ab_out, std_out],' greater than 80, so only write 20 of them ','COLL')
263 0 : nkpout=20
264 : end if
265 195 : do iqpt=1,nkpout
266 192 : write(msg, '(1x,i2,a2,3es16.8)' )iqpt,') ',self%qibz(1,iqpt),self%qibz(2,iqpt),self%qibz(3,iqpt)
267 579 : call wrtout([ab_out, std_out], msg, 'COLL')
268 : end do
269 : end if
270 3 : ABI_SFREE(new_shiftq)
271 3 : ABI_SFREE(qbz)
272 3 : end subroutine prepare_qpoints
273 :
274 3 : subroutine print_Rlist(self, dtset)
275 : class(LatticeWannier), intent(inout):: self
276 : type(anaddb_dataset_type) :: dtset
277 : integer :: master=0
278 : integer :: nRout, ii
279 : character(len = 500):: msg
280 :
281 3 : if (self%my_rank == master) then
282 3 : write(msg, "(2a)")" LWF is transformed to the real space cells: ", trim(ltoa(dtset%lwf_ngqpt))
283 9 : call wrtout([ab_out, std_out], msg)
284 3 : write(msg, "((a, i0))")" Number of cells: ", self%nR
285 9 : call wrtout([ab_out, std_out], msg)
286 :
287 3 : write(msg,'(a,i8)')' R-vectors for cells in the real space : ',self%nR
288 9 : call wrtout([ab_out, std_out],msg,'COLL')
289 3 : nRout=self%nR
290 3 : if(self%nR>80)then
291 0 : call wrtout([ab_out, std_out],' greater than 80, so only write 20 of them ','COLL')
292 0 : nRout=20
293 : end if
294 195 : do ii=1,nRout
295 192 : write(msg, '(1x,i2,a2,3i8)' )ii,') ',self%Rlist(1,ii),self%Rlist(2,ii),self%Rlist(3,ii)
296 579 : call wrtout([ab_out, std_out], msg, 'COLL')
297 : end do
298 : end if
299 3 : end subroutine print_Rlist
300 :
301 :
302 :
303 :
304 3 : subroutine prepare_Rlist(self)
305 : class(LatticeWannier), intent(inout):: self
306 : integer:: qptrlatt(3), i
307 3 : self%nR=1
308 12 : do i = 1, 3
309 9 : qptrlatt(i) = self%qptrlatt(i, i)
310 12 : self%nR=self%nR*qptrlatt(i)
311 : end do
312 3 : call build_Rgrid(qptrlatt, self%Rlist)
313 3 : end subroutine prepare_Rlist
314 :
315 2886 : elemental function freq_to_eigenval(f) result (evalue)
316 : real(dp), intent(in):: f
317 : real(dp):: evalue
318 6 : if(f < -1d-16) then
319 144 : evalue = - f*f
320 2742 : else if (f > 1d-16) then
321 2731 : evalue = f*f
322 : else
323 : evalue = 0.0_dp
324 : end if
325 : end function freq_to_eigenval
326 :
327 3 : subroutine get_ifc_eigens(self, ifc, crystal)
328 : class(LatticeWannier), intent(inout):: self
329 : type(ifc_type), intent(in):: ifc
330 : type(crystal_t), intent(in):: crystal
331 6 : real(dp):: eigvec(2, 3, Crystal%natom, 3*Crystal%natom), phfrq(3*Crystal%natom)
332 6 : real(dp):: displ(2*3*Crystal%natom*3*Crystal%natom)
333 : integer:: iq_ibz
334 : integer:: natom, natom3
335 : integer:: iatom, iband, i3
336 : complex(dp):: phase
337 3 : natom = crystal%natom
338 3 : self%natom = natom
339 3 : natom3 = natom*3
340 12 : ABI_MALLOC(self%eigenvalues, (natom3, self%nqibz))
341 15 : ABI_MALLOC(self%eigenvectors, (natom3, natom3, self%nqibz))
342 : !print *, "trans:", ifc%trans
343 : !print *, "xred:", crystal%xred
344 195 : do iq_ibz = 1, self%nqibz
345 192 : call ifc%fourq(crystal, self%qibz(:,iq_ibz), phfrq, displ, out_eigvec = eigvec)
346 : ! frequency to eigenvalues
347 3072 : self%eigenvalues(:, iq_ibz) = freq_to_eigenval(phfrq)
348 : ! remove phases from eigenvector
349 1155 : do iatom = 1, natom
350 : ! to remove the phase factor exp(iqr)
351 : !phase = exp(-cmplx(0.0_dp, two_pi)*dot_product(crystal%xred(:, iatom), self%qibz(:, iq_ibz) ))
352 3840 : phase = exp(-cmplx(0.0_dp, two_pi)*dot_product(ifc%trans(:, iatom), self%qibz(:, iq_ibz)))
353 15552 : do iband=1, natom3
354 58560 : do i3 = 1, 3
355 : self%eigenvectors((iatom-1)*3+i3, iband, iq_ibz ) = &
356 57600 : & CMPLX(eigvec(1, i3, iatom, iband), eigvec(2, i3, iatom, iband)) * phase
357 : end do
358 : end do
359 : end do
360 : end do
361 3 : end subroutine get_ifc_eigens
362 :
363 3 : subroutine write_lwf_nc(self, prefix)
364 : class(LatticeWannier), intent(inout):: self
365 : character(len=*), intent(in) :: prefix
366 : character(len=500) :: msg
367 : type(IOWannNC):: ncfile
368 3 : call self%scdm%create_ncfile(trim(prefix)//"_lwf.nc", ncfile)
369 3 : call self%scdm%write_wann_netcdf(ncfile, wannR_unit='dimensionless', HwannR_unit='Ha')
370 : !NCF_CHECK(self%crystal%ncwrite(ncfile%ncid))
371 3 : call self%scdm%close_ncfile(ncfile)
372 3 : write(msg, '(a)') ' LWF construction finished.'
373 9 : call wrtout([ab_out, std_out], msg)
374 3 : write(msg, '(a)') ' LWF coefficients and Hamiltonian writen to file: '//trim(prefix)//"_lwf.nc ."
375 9 : call wrtout([ab_out, std_out], msg)
376 3 : end subroutine write_lwf_nc
377 :
378 :
379 3 : subroutine run_all(self, prefix, dtset)
380 : class(LatticeWannier), intent(inout):: self
381 : character(len=*), intent(in) :: prefix
382 : type(anaddb_dataset_type), intent(in):: dtset
383 3 : call self%scdm%construct_wannier()
384 3 : call self%print_Rlist(dtset)
385 3 : call self%write_lwf_nc(prefix = prefix)
386 3 : if(dtset%nqpath>0) then
387 2 : call self%write_bands(prefix, dtset)
388 : end if
389 3 : end subroutine run_all
390 :
391 3 : subroutine run_lattice_wannier(ifc, crystal, dtset, prefix, comm)
392 : integer, intent(in):: comm
393 : character(len=*), intent(in):: prefix
394 : type(ifc_type), intent(in):: ifc
395 : type(crystal_t), intent(in):: crystal
396 : type(anaddb_dataset_type), intent(in):: dtset
397 3 : type(LatticeWannier):: lwf
398 3 : call lwf%initialize(ifc, crystal, dtset, comm)
399 3 : call lwf%run_all(prefix, dtset)
400 3 : call lwf%finalize()
401 3 : end subroutine run_lattice_wannier
402 :
403 :
404 2 : subroutine write_bands(self, prefix, inp)
405 : class(LatticeWannier), intent(inout):: self
406 : character(len=*), intent(in):: prefix
407 : type(anaddb_dataset_type), intent(in):: inp
408 2 : integer,allocatable :: ndiv(:)
409 : integer :: nfineqpath
410 2 : real(dp),allocatable :: fineqpath(:,:)
411 2 : real(dp),allocatable :: phfrq(:, :)
412 2 : complex(dp), allocatable :: eigvec(:,:, :)
413 : !real(dp),allocatable :: weights(:)
414 : integer :: iq, iband
415 : real(dp) :: f
416 6 : ABI_MALLOC(ndiv,(inp%nqpath-1))
417 2 : call make_path(inp%nqpath,inp%qpath,self%Crystal%gmet,'G',inp%ndivsm,ndiv,nfineqpath,fineqpath,std_out)
418 2 : ABI_FREE(ndiv)
419 :
420 8 : ABI_MALLOC(phfrq, (inp%lwf_nwann, nfineqpath))
421 10 : ABI_MALLOC(eigvec, (inp%lwf_nwann, inp%lwf_nwann, nfineqpath))
422 :
423 2 : call self%scdm%get_wannier_eigen_klist(fineqpath, nfineqpath, phfrq, eigvec)
424 346 : do iq=1, nfineqpath
425 1378 : do iband =1, inp%lwf_nwann
426 1032 : f=phfrq(iband, iq)
427 1376 : if (f>1.0d-9) then
428 648 : phfrq(iband, iq)= sqrt(f)
429 384 : else if (f<-1.0d-9) then
430 383 : phfrq(iband, iq)= -sqrt(-f)
431 : else
432 1 : phfrq(iband, iq)= 0.0_dp
433 : end if
434 :
435 : end do
436 : end do
437 :
438 2 : call write_phfrq(trim(prefix)//"_lwf_PHFRQ", inp%lwf_nwann, nfineqpath, phfrq)
439 :
440 2 : ABI_FREE(phfrq)
441 2 : ABI_FREE(eigvec)
442 2 : ABI_FREE(fineqpath)
443 2 : end subroutine write_bands
444 :
445 2 : subroutine write_phfrq(path,nlwf,nqpts,phfreq)
446 :
447 : !Arguments ------------------------------------
448 : !scalars
449 : integer,intent(in) :: nqpts, nlwf
450 : character(len=*),intent(in) :: path
451 : !arrays
452 : !real(dp),intent(in) :: qpoints(3,nqpts)
453 : real(dp),intent(in) :: phfreq(nlwf,nqpts)
454 :
455 : !Local variables-------------------------------
456 : !scalars
457 : integer :: nphmodes, iq, iunit
458 : !real(dp) :: dummy
459 : character(len=300) :: fmt
460 : character(len=500) :: msg
461 :
462 : ! *************************************************************************
463 :
464 2 : nphmodes = nlwf
465 : !dummy = qpoints(1,1); dummy = weights(1)
466 2 : if (open_file(path, msg, newunit=iunit, form="formatted", status="unknown", action="write") /= 0) then
467 0 : ABI_ERROR(msg)
468 : end if
469 :
470 2 : write (iunit, '(a)') '# ABINIT generated LWF phonon band structure file. All in Ha atomic units'
471 2 : write (iunit, '(a)') '# '
472 2 : write (iunit, '(a,i0)') '# number_of_qpoints ', nqpts
473 2 : write (iunit, '(a,i0)') '# number_of_phonon_modes ', nphmodes
474 2 : write (iunit, '(a)') '# '
475 2 : write (fmt,'(a,i0,a)') "(I5, ", nphmodes, "E20.10)"
476 346 : do iq= 1, nqpts
477 346 : write (iunit, fmt) iq, phfreq(:,iq)
478 : end do
479 2 : close(iunit)
480 :
481 2 : end subroutine write_phfrq
482 :
483 :
484 :
485 0 : end module m_lwf
486 : !!***
|