Line data Source code
1 : !!****m* ABINIT/m_lattice_harmonic_primitive_potential
2 : !! NAME
3 : !! m_lattice_harmonic_primitive_potential
4 : !!
5 : !! FUNCTION
6 : !! This module contains the example lattice primitive potential. It is not the
7 : !! one really used. The purpose of this is to show how to extend multibinit
8 : !! to new degree of freedom
9 : !!
10 : !! Datatypes:
11 : !! lattice_harmonic_primitive_potential_t
12 : !!
13 : !! Subroutines:
14 : !!
15 : !! COPYRIGHT
16 : !! Copyright (C) 2001-2026 ABINIT group (hexu)
17 : !! This file is distributed under the terms of the
18 : !! GNU General Public License, see ~abinit/COPYING
19 : !! or http://www.gnu.org/copyleft/gpl.txt .
20 : !! For the initials of contributors, see ~abinit/doc/developers/contributors.txt .
21 : !!
22 : !! SOURCE
23 :
24 :
25 : #if defined HAVE_CONFIG_H
26 : #include "config.h"
27 : #endif
28 : #include "abi_common.h"
29 :
30 : module m_lattice_harmonic_primitive_potential
31 : use, intrinsic :: iso_c_binding
32 : !use m_dynamic_array, only: int_array_type, real_array_type, int2d_array_type
33 : use defs_basis
34 : use m_abicore
35 : use m_errors
36 : use m_nctk
37 : #if defined HAVE_NETCDF
38 : use netcdf
39 : #endif
40 : use m_xmpi
41 : use m_mathfuncs, only: eigensh
42 : use m_multibinit_dataset, only: multibinit_dtset_type
43 : use m_multibinit_cell, only: mbcell_t, mbsupercell_t
44 : use m_primitive_potential, only: primitive_potential_t
45 : use m_abstract_potential, only: abstract_potential_t
46 : use m_dynamic_array, only: int2d_array_type
47 : use m_supercell_maker, only: supercell_maker_t
48 : use m_spmat_ndcoo, only: ndcoo_mat_t
49 : use m_lattice_harmonic_potential, only: lattice_harmonic_potential_t
50 : implicit none
51 : private
52 : !!***
53 :
54 :
55 : !-------------------------------------------------------------------!
56 : ! An harmonic potential which has only the IFC (with no dipole-dipole)
57 : !
58 : ! IFC is written in a coefficient matrix M(R, i, j)=val
59 : !-------------------------------------------------------------------!
60 : type, public, extends(primitive_potential_t) :: lattice_harmonic_primitive_potential_t
61 : integer :: natom ! number of atoms
62 : type(ndcoo_mat_t) :: coeff ! A N-dimensional COO matrix.
63 : ! The indices are (ind_R, i, j). Note that xyz is included in i and j.
64 : integer, allocatable :: Rlist(:,:) ! The list of R points (3, number of R-points)
65 : !. Rlist(:, ind_R) is a R-vector.
66 : real(dp) :: ref_energy=0.0 ! reference energy
67 : contains
68 : procedure:: initialize
69 : procedure:: finalize
70 : procedure :: load_from_files ! load potential from files listed in files file
71 : procedure :: load_from_netcdf ! load potential from a netcdf file
72 : procedure:: fill_supercell ! fill a supercell potential
73 : procedure :: get_hamk ! generate hamiltonian for one k point.
74 : procedure :: get_eigen ! eigen values and eigen vectors
75 : end type lattice_harmonic_primitive_potential_t
76 :
77 : contains
78 :
79 : !-------------------------------------------------------------------!
80 : ! Initialize
81 : ! Input:
82 : ! primcell: the reference primitive cell.
83 : !-------------------------------------------------------------------!
84 5 : subroutine initialize(self, primcell)
85 : class(lattice_harmonic_primitive_potential_t), intent(inout) :: self
86 : type(mbcell_t), target, intent(inout) :: primcell
87 : !integer, intent(in) :: nspin
88 5 : self%primcell=>primcell
89 5 : self%label="lattice_harmonic_primitive_potential"
90 5 : self%has_spin=.False.
91 5 : self%has_displacement=.True.
92 5 : self%has_strain=.False.
93 5 : self%has_lwf=.False.
94 5 : end subroutine initialize
95 :
96 :
97 : !-------------------------------------------------------------------!
98 : ! Finalize
99 : !-------------------------------------------------------------------!
100 5 : subroutine finalize(self)
101 : class(lattice_harmonic_primitive_potential_t), intent(inout) :: self
102 5 : call self%coeff%finalize()
103 5 : ABI_FREE(self%Rlist)
104 5 : nullify(self%primcell)
105 5 : self%natom=0
106 5 : self%label="Destroyed lattice_harmonic_primitive_potential"
107 5 : call self%primitive_potential_t%finalize()
108 5 : end subroutine finalize
109 :
110 :
111 : !-------------------------------------------------------------------!
112 : ! Load from files:
113 : !> params: parameters
114 : !> fnames: file names from files file
115 : !-------------------------------------------------------------------!
116 5 : subroutine load_from_files(self, params, fnames)
117 : class(lattice_harmonic_primitive_potential_t), intent(inout) :: self
118 : type(multibinit_dtset_type), intent(in) :: params
119 : character(len=fnlen), intent(in) :: fnames(:)
120 : ABI_UNUSED(fnames)
121 5 : if(trim(params%latt_pot_fname)=='') then
122 0 : call self%load_from_netcdf(fnames(3))
123 : else
124 5 : call self%load_from_netcdf(params%latt_pot_fname)
125 : end if
126 5 : end subroutine load_from_files
127 :
128 : !-------------------------------------------------------------------!
129 : ! load potential from netcdf file
130 : ! Note that the lattic part of the primitive cell is also loaded.
131 : ! Input:
132 : ! fname: filename
133 : !-------------------------------------------------------------------!
134 5 : subroutine load_from_netcdf(self, fname)
135 : class(lattice_harmonic_primitive_potential_t), intent(inout) :: self
136 : character(len=fnlen), intent(in) :: fname
137 : integer :: ncid, ierr
138 : integer :: iR, nR, natom, natom3
139 : real(dp) :: ref_energy
140 : real(dp) :: cell(3,3)
141 5 : real(dp), allocatable :: xcart(:,:), masses(:)
142 5 : integer, allocatable :: zion(:)
143 5 : real(dp), allocatable :: ifc_vallist(:,:,:)
144 : integer :: varid, i, j
145 : #if defined HAVE_NETCDF
146 5 : ierr=nf90_open(trim(fname), NF90_NOWRITE, ncid)
147 5 : NCF_CHECK_MSG(ierr, "Open netcdf file "//trim(fname))
148 :
149 5 : ierr=nctk_get_dim(ncid, "ifc_nR" , nR)
150 5 : ierr=nctk_get_dim(ncid, "natom", natom)
151 5 : ierr=nctk_get_dim(ncid, "natom3", natom3)
152 :
153 :
154 15 : ABI_MALLOC(masses, (natom))
155 15 : ABI_MALLOC(xcart, (3, natom))
156 15 : ABI_MALLOC(zion,(natom))
157 :
158 25 : ABI_MALLOC(ifc_vallist, (natom3, natom3, nR))
159 15 : ABI_MALLOC(self%Rlist,(3, nR))
160 :
161 :
162 5 : ierr =nf90_inq_varid(ncid, "ref_energy", varid)
163 5 : NCF_CHECK_MSG(ierr, "ref_energy")
164 5 : ierr = nf90_get_var(ncid, varid, ref_energy)
165 5 : NCF_CHECK_MSG(ierr, "ref_energy")
166 :
167 :
168 5 : ierr =nf90_inq_varid(ncid, "ref_masses", varid)
169 5 : NCF_CHECK_MSG(ierr, "ref_masses")
170 5 : ierr = nf90_get_var(ncid, varid, masses)
171 5 : NCF_CHECK_MSG(ierr, "ref_masses")
172 :
173 5 : ierr =nf90_inq_varid(ncid, "ref_xcart", varid)
174 5 : NCF_CHECK_MSG(ierr, "ref_xcart")
175 5 : ierr = nf90_get_var(ncid, varid, xcart)
176 5 : NCF_CHECK_MSG(ierr, "ref_xcart")
177 :
178 5 : ierr =nf90_inq_varid(ncid, "ref_cell", varid)
179 5 : NCF_CHECK_MSG(ierr, "rec_cell")
180 5 : ierr = nf90_get_var(ncid, varid, cell)
181 5 : NCF_CHECK_MSG(ierr, "ref_cell")
182 :
183 5 : ierr =nf90_inq_varid(ncid, "ref_zion", varid)
184 5 : NCF_CHECK_MSG(ierr, "ref_zion")
185 5 : ierr = nf90_get_var(ncid, varid, zion)
186 5 : NCF_CHECK_MSG(ierr, "ref_zion")
187 :
188 : ! Unit conversions
189 5 : ref_energy = ref_energy * eV_Ha
190 30 : masses(:) = masses(:) * amu_emass
191 65 : cell(:,:) = cell(:,:) / Bohr_Ang
192 105 : xcart(:,:) = xcart(:,:) /Bohr_Ang
193 :
194 5 : self%natom=natom
195 5 : self%ref_energy=ref_energy
196 5 : call self%primcell%set_lattice(natom, cell, xcart, masses, zion)
197 :
198 20 : call self%coeff%initialize([ nR, natom3, natom3 ])
199 5 : ierr =nf90_inq_varid(ncid, "ifc_Rlist", varid)
200 5 : NCF_CHECK_MSG(ierr, "ifc_Rlist")
201 5 : ierr = nf90_get_var(ncid, varid, self%Rlist)
202 5 : NCF_CHECK_MSG(ierr, "ifc_Rlist")
203 :
204 5 : ierr =nf90_inq_varid(ncid, "ifc_vallist", varid)
205 5 : NCF_CHECK_MSG(ierr, "ifc_vallist")
206 5 : ierr = nf90_get_var(ncid, varid, ifc_vallist)
207 5 : NCF_CHECK_MSG(ierr, "ifc_vallist")
208 :
209 32540 : ifc_vallist(:,:,:) = ifc_vallist(:,:,:) * eV_Ha * (Bohr_Ang * Bohr_Ang)
210 :
211 140 : do iR =1, nR
212 2165 : do i=1 , natom3
213 32535 : do j=1, natom3
214 32400 : if (abs(ifc_vallist(j, i, iR))>1e-3) then
215 : ! NOTE: in fortran the order of index in reversed when reading netcdf array.
216 11460 : call self%coeff%add_entry([iR, i, j], ifc_vallist(j, i, iR))
217 : end if
218 : end do
219 : end do
220 : end do
221 :
222 5 : ierr=nf90_close(ncid)
223 5 : NCF_CHECK_MSG(ierr, "Close netcdf file")
224 :
225 5 : ABI_FREE(masses)
226 5 : ABI_FREE(xcart)
227 5 : ABI_FREE(zion)
228 5 : ABI_FREE(ifc_vallist)
229 : #else
230 : NETCDF_NOTENABLED_ERROR()
231 : #endif
232 :
233 5 : end subroutine load_from_netcdf
234 :
235 :
236 : !-------------------------------------------------------------------!
237 : !Fill supercell
238 : ! Inputs:
239 : ! scmaker : supercell_maker_t
240 : ! Output:
241 : ! scpot: a class pointer to an ABSTRACT potential.
242 : ! Not a harmonic potential because this is inherited from
243 : ! an abstract_primitive_potential_t, which doesn't know
244 : ! the type of the supercell potential.
245 : !
246 : !-------------------------------------------------------------------!
247 5 : subroutine fill_supercell(self, scmaker, params, scpot, supercell)
248 : use m_spmat_convert, only: COO_to_dense
249 :
250 : class(lattice_harmonic_primitive_potential_t) , intent(inout) :: self
251 : type(supercell_maker_t), intent(inout) :: scmaker
252 : type(multibinit_dtset_type), intent(inout) :: params
253 : class(abstract_potential_t), pointer, intent(inout) :: scpot
254 : type(mbsupercell_t), target :: supercell
255 :
256 : integer :: natom, sc_natom
257 : integer :: inz, iR, R(3), i, j, icell
258 5 : integer, allocatable :: ilist_sc(:), jlist_sc(:), Rlist_sc(:,:)
259 : real(dp):: val
260 :
261 5 : ABI_UNUSED_A(params)
262 :
263 :
264 5 : natom=self%natom
265 5 : sc_natom= natom* scmaker%ncells
266 :
267 : !! NOTE: the code below can be used as a pattern to build supercell from primitivecell.
268 : ! Step 1: allocate the scpot as a corresponding supercell potential
269 5 : ABI_MALLOC_TYPE_SCALAR(lattice_harmonic_potential_t, scpot)
270 : ! Fortran does not know the functions specific to the derived class pointer.
271 : ! Only the ones inheritated from abstract class,
272 : ! unless select type is used:
273 : select type(scpot)
274 : type is (lattice_harmonic_potential_t)
275 5 : call scpot%initialize(sc_natom)
276 5 : call scpot%set_supercell(supercell)
277 : ! IFC is an COO_mat_t, which has the index of R1, R2, R3, i, j and the value of val
278 : ! list of index R: coeff%ind%data(1, 1:coeff%nnz)
279 : ! list of i: coeff%ind%data(2, 1:coeff%nnz)
280 : ! list of j: coeff%ind%data(3, 1:coeff%nnz)
281 : ! IFC: (ind) = val
282 2870 : do inz =1 , self%coeff%nnz
283 : ! For each non-zero entry in the coeff matrix
284 : ! get the R, i, and j, val
285 2865 : iR=self%coeff%ind%data(1,inz)
286 11460 : R(:) = self%Rlist(:, iR)
287 2865 : i=self%coeff%ind%data(2, inz)
288 2865 : j=self%coeff%ind%data(3, inz)
289 2865 : val=self%coeff%val%data(inz)
290 : ! translate i to i in supercell.
291 : ! No need to allocate, it is done by trans_i . but remember to deallocate!
292 : ! nbasis is the number in one primitive cell.
293 : ! e.g. there are 3*natom possible i (3: x, y, z) in each primitive cell.
294 2865 : call scmaker%trans_i(nbasis=self%natom*3, i=i, i_sc=ilist_sc )
295 : ! translate j, Rj to supercell.
296 2865 : call scmaker%trans_j_and_Rj(nbasis=self%natom*3, j=j, Rj=R, j_sc=jlist_sc, Rj_sc=Rlist_sc)
297 : ! values are repeated in cells
298 534614 : do icell=1, scmaker%ncells
299 534609 : call scpot%add_term(ilist_sc(icell), jlist_sc(icell), val )
300 : end do
301 : end do
302 :
303 10 : call scpot%set_ref_energy(self%ref_energy * scmaker%ncells)
304 :
305 : ! Test the phonon energy
306 : !call COO_to_dense(scpot%coeff, real_sc_evecs)
307 : !sc_evecs(:,:) = real_sc_evecs(:,:)
308 : !call eigensh(sc_evals, sc_evecs)
309 :
310 : end select
311 :
312 :
313 5 : ABI_SFREE(ilist_sc)
314 5 : ABI_SFREE(jlist_sc)
315 5 : ABI_SFREE(Rlist_sc)
316 :
317 5 : ABI_UNUSED_A(params)
318 :
319 5 : end subroutine fill_supercell
320 :
321 :
322 : !-------------------------------------------------------------------!
323 : ! calculate hamiltonian at k point (or more precisely, q point)
324 : ! H_ij(k) = sum_R H_ij(R) exp( i2pi k.dot.R)
325 : !-------------------------------------------------------------------!
326 0 : subroutine get_hamk(self, kpoint, hamk)
327 : class(lattice_harmonic_primitive_potential_t), intent(in) :: self
328 : real(dp), intent(in) :: kpoint(3)
329 : complex(dp), intent(inout) :: hamk(:,:)
330 : integer :: inz, iR, R(3), i, j
331 : real(dp) :: val
332 0 : hamk(:,:) = cmplx(0.0,0.0)
333 0 : do inz =1, self%coeff%nnz
334 0 : iR=self%coeff%ind%data(1,inz)
335 0 : R(:) = self%Rlist(:, iR)
336 0 : i=self%coeff%ind%data(2, inz)
337 0 : j=self%coeff%ind%data(3, inz)
338 0 : val=self%coeff%val%data(inz)
339 0 : hamk(i, j) =hamk(i, j) + val*exp(cmplx(0.0,2.0) *pi * dot_product(kpoint, R))
340 : end do
341 0 : end subroutine get_hamk
342 :
343 : !-------------------------------------------------------------------!
344 : !calculate eigenvalue and eigen vector
345 : !-------------------------------------------------------------------!
346 0 : subroutine get_eigen(self, kpoint, evals, evecs)
347 : class(lattice_harmonic_primitive_potential_t), intent(in) :: self
348 : real(dp), intent(in) :: kpoint(3)
349 : real(dp), intent(inout) :: evals(:)
350 : complex(dp), intent(inout) :: evecs(:,:)
351 0 : call self%get_hamk(kpoint, evecs)
352 : ! The evecs array is reused both as the matrix and eigenvectors.
353 0 : call eigensh(evals, evecs)
354 0 : end subroutine get_eigen
355 :
356 20 : end module m_lattice_harmonic_primitive_potential
|