Branch data Line data Source code
1 : : !!****m*ABINIT/m_scdm_math
2 : : !! NAME
3 : : !! m_scdm_math
4 : : !!
5 : : !! FUNCTION
6 : : !! Math functions used by SCDM
7 : : !! (select columns of density matrix method) for getting wannier functions.
8 : : !!
9 : : !! COPYRIGHT
10 : : !! Copyright (C) 2005-2026 ABINIT group (hexu)
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 : :
22 : :
23 : : #include "abi_common.h"
24 : :
25 : : module m_scdm_math
26 : : use defs_basis
27 : : use m_abicore
28 : : use m_errors
29 : : implicit none
30 : : real(dp), parameter, public:: tpi = 2*PI
31 : : complex(dp), parameter, public:: tpi_im = cmplx(0.0_dp, tpi, kind = dp)
32 : :
33 : :
34 : : ! wrapper for LAPACK routine ZGEEV
35 : : type, public:: eigensolver
36 : : integer:: ndim = -1, lwork = 0
37 : : complex(dp), allocatable :: work(:)
38 : : real(dp), allocatable:: rwork(:)
39 : : contains
40 : : procedure:: run => eigensolver_run
41 : : procedure:: finalize => eigensolver_finalize
42 : : end type eigensolver
43 : :
44 : :
45 : : public:: complex_QRCP_piv_only
46 : : public:: real_svd
47 : : public:: complex_svd
48 : : public:: gaussian
49 : : public:: fermi
50 : : public:: insertion_sort_double
51 : : public:: build_Rgrid
52 : : private
53 : :
54 : : contains
55 : :
56 : : !
57 [ - + - + ]: 2 : subroutine complex_QRCP_Piv_only(A, Piv)
58 : : complex(dp), intent(in):: A(:, :)
59 : : integer, intent(inout):: Piv(:)
60 : 4 : real(dp):: rwork(size(A, 2)*2)
61 : 4 : complex(dp):: tau(min(size(A, 1), size(A, 2)))
62 : : integer:: m, n
63 : 2 : complex(dp), allocatable:: work(:)
64 : : complex(dp):: tmp_work(2)
65 : : integer:: lwork
66 : : integer:: info
67 : : EXTERNAL ZGEQP3
68 : :
69 : 2 : m = size(A, 1)
70 : 2 : n = size(A, 2)
71 [ + + ]: 62 : rwork(:)=0.0_dp
72 [ - + - - : 2 : call ZGEQP3(m, n, A, m, piv, tau, tmp_work, -1, rwork, info)
- - - - -
+ - - - -
- + - - -
- - + -
- ]
73 [ - + ]: 2 : if(info /= 0) then
74 : 0 : ABI_ERROR("Error in doing QRCP")
75 : : endif
76 [ + + ]: 32 : Piv(:) = 0
77 [ + + ]: 62 : rwork(:) = 0.0_DP
78 : 2 : lwork = INT(AINT(REAL(tmp_work(1))))
79 : :
80 [ - + + - : 6 : ABI_MALLOC(work, (lwork))
+ - + - -
+ - + ]
81 [ + + ]: 1026 : work(:) = (0.0_DP, 0.0_DP)
82 : :
83 [ - + - - : 2 : CALL ZGEQP3(m, n, A, m, piv, tau, work, lwork, rwork, info)
- - - - -
+ - - - -
- + - - -
- - + -
- ]
84 [ - + ]: 2 : if(info /= 0) then
85 : 0 : ABI_ERROR("Error in doing QRCP")
86 : : endif
87 [ + - ]: 2 : ABI_SFREE(work)
88 : 2 : end subroutine complex_QRCP_Piv_only
89 : :
90 : :
91 : :
92 : : ! wrapper for LAPACK routine DGESVD
93 : : ! A = U*S*VT
94 : : ! Note: A is overwritten by U
95 : : ! S is the singular values
96 : : ! VT is the transpose of V
97 [ # # # # : 0 : subroutine real_svd(A, U, S, VT)
# # # # ]
98 : : real(dp), intent(in):: A(:, :)
99 : : real(dp), intent(inout):: U(:, :), S(:), VT(:,:)
100 : : integer:: LWMAX
101 : : real(dp):: tmp(5)
102 : 0 : real(dp), allocatable:: WORK(:)
103 : : integer :: M, N
104 : : integer:: LDA, LDU, LDVT
105 : : integer:: INFO, LWORK
106 : : EXTERNAL DGESVD
107 : 0 : M = size(A, 1)
108 : 0 : N = size(A, 2)
109 : 0 : LDA = M
110 : 0 : LDU = M
111 : 0 : LDVT = N
112 : 0 : LWORK = -1
113 : 0 : LWMAX = max(size(A, 1), size(A, 2))*10
114 : : CALL DGESVD( 'All', 'All', M, N, A, LDA, S, U, LDU, VT, LDVT, &
115 [ # # # # : 0 : & tmp, LWORK, INFO )
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
116 : 0 : LWORK = MIN( LWMAX, INT( tmp( 1 ) ) )
117 : :
118 [ # # # # : 0 : ABI_MALLOC(work, (lwork))
# # # # #
# # # ]
119 : : CALL DGESVD( 'All', 'All', M, N, A, LDA, S, U, LDU, VT, LDVT, &
120 [ # # # # : 0 : WORK, LWORK, INFO )
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
121 [ # # ]: 0 : IF( INFO .GT. 0 ) THEN
122 : 0 : ABI_ERROR('The algorithm computing SVD failed to converge.')
123 : 0 : STOP
124 : : END IF
125 [ # # ]: 0 : ABI_SFREE(work)
126 : 0 : end subroutine real_svd
127 : :
128 : : ! wrapper for LAPACK routine ZGESVD
129 : : ! A = U*S*VT
130 : : ! Note: A is overwritten by U
131 : : ! S is the singular values
132 : : ! VT is the transpose of V
133 : : ! mode: A, or S, or N or O, see
134 : : ! https://netlib.org/lapack/explore-html/d3/da8/group__complex16_g_esing_gad6f0c85f3cca2968e1ef901d2b6014ee.html
135 [ - + - + : 192 : subroutine complex_svd(A, U, S, VT, mode)
- + - + ]
136 : : complex(dp), intent(in):: A(:, :)
137 : : complex(dp), intent(inout):: U(:, :), VT(:,:)
138 : : real(dp), intent(inout):: S(:)
139 : : character, intent(in):: mode ! A, or S, or N/O
140 : : integer:: LWMAX
141 : : complex(dp):: tmp(2)
142 : 192 : complex(dp), allocatable:: WORK(:)
143 : 192 : real(dp), allocatable:: rwork(:)
144 : : integer :: M, N
145 : : integer:: LDA, LDU, LDVT
146 : : integer:: INFO, LWORK
147 : : EXTERNAL ZGESVD
148 : :
149 : 192 : M = size(A, 1)
150 : 192 : N = size(A, 2)
151 : 192 : LWMAX = max(size(A, 1), size(A, 2))*10
152 : 192 : LDA = M
153 : 192 : LDU = M
154 : 192 : LDVT = N
155 : 192 : LWORK = -1
156 [ + - - + : 576 : ABI_MALLOC(rwork, (min(M, N)*6))
+ - + - +
- - + -
+ ]
157 : : CALL ZGESVD( mode, mode, M, N, A, LDA, S, U, LDU, VT, LDVT, &
158 [ - + - - : 192 : & tmp, LWORK, rwork, INFO )
- - - - -
+ - - - -
- + - - -
- - - - +
- - - - -
- - + - -
- - - + -
- - + - -
- - - + -
- - - ]
159 : 192 : LWORK = MIN( LWMAX, INT( tmp( 1 ) ) )
160 [ - + + - : 576 : ABI_MALLOC(work, (lwork))
+ - + - -
+ - + ]
161 : : CALL ZGESVD( mode, mode, M, N, A, LDA, S, U, LDU, VT, LDVT, &
162 [ - + - - : 192 : WORK, LWORK, rwork, INFO )
- - - - -
+ - - - -
- + - - -
- - - - +
- - - - -
- - + - -
- - - + -
- - + - -
- - - + -
- - - ]
163 [ - + ]: 192 : IF( INFO .GT. 0 ) THEN
164 : 0 : ABI_ERROR('The algorithm computing SVD failed to converge.')
165 : 0 : STOP
166 : : END IF
167 [ + - ]: 192 : ABI_SFREE(work)
168 [ + - ]: 192 : ABI_SFREE(rwork)
169 : 192 : end subroutine complex_svd
170 : :
171 : :
172 : 0 : function gaussian(x, mu, sigma) result(y)
173 : : real(dp), intent(in):: x, mu, sigma
174 : : real(dp):: y
175 : 0 : y = exp(-1.0 * (x-mu)**2/sigma**2)
176 : 0 : end function gaussian
177 : :
178 : : !===============================================================
179 : : ! Complementary error function.
180 : : !===============================================================
181 : : pure function erfcd(x) result(y)
182 : : real(dp), intent(in):: x
183 : : real(dp):: y
184 : : real(dp):: t, z
185 : : z = abs(x)
186 : : t = 1.0 / ( 1.0+0.5*z )
187 : :
188 : : y = t*exp( -z*z - 1.26551223+t * &
189 : : ( 1.00002368+t * ( 0.37409196+t * &
190 : : ( 0.09678418+t * (-0.18628806+t * &
191 : : ( 0.27886807+t * (-1.13520398+t * &
192 : : ( 1.48851587+t * (-0.82215223+t * 0.17087277 )))))))))
193 : : if ( x .lt. 0.0 ) y = 2.0-y
194 : :
195 : : end function erfcd
196 : :
197 : 1935 : function fermi(x, mu, sigma) result(y)
198 : : real(dp), intent(in):: x, mu, sigma
199 : : real(dp):: y
200 : 1935 : y = 0.5*erfc((x-mu) / sigma)
201 : 1935 : end function fermi
202 : :
203 : : ! wrapper for LAPACK routine ZHEEV
204 : : ! evecs are used as the input matrix which will be overwritten
205 : : ! evals are the eigenvalues
206 [ - + - + ]: 344 : subroutine eigensolver_run(self, evals, evecs)
207 : : class(eigensolver), intent(inout):: self
208 : : real(dp), intent(inout):: evals(:)
209 : : complex(dp), intent(inout):: evecs(:,:)
210 : : integer :: info
211 : : external ZHEEV
212 [ + - ]: 344 : if (self%ndim == -1) then
213 : 344 : self%ndim = size(evecs, 1)
214 : 344 : self%lwork = -1
215 [ - + - + ]: 344 : ABI_MALLOC(self%work, (1))
216 [ + - - + : 1032 : ABI_MALLOC(self%rwork, (3*size(evecs, 1)-2))
+ - + - +
- - + - +
- + ]
217 [ - + - - : 344 : call ZHEEV('V', 'U', self%ndim, evecs, self%ndim, evals, self%work, self%lwork, self%rwork, info)
- - - - -
+ - - - -
- + - - -
- - + -
- ]
218 : 344 : self%lwork = INT(self%work(1))
219 : 344 : ABI_SFREE(self%work)
220 [ - + + - : 1032 : ABI_MALLOC(self%work, (self%lwork))
+ - + - -
+ - + -
+ ]
221 [ # # ]: 0 : else if (self%ndim /= size(evecs, 1)) then
222 : 0 : ABI_ERROR("Eigensovler: The size of the evecs is not the same as previous one.")
223 : : end if
224 [ - + - - : 344 : call ZHEEV('V', 'U', self%ndim, evecs, self%ndim, evals, self%work, self%lwork, self%rwork, info)
- - - - -
+ - - - -
- + - - -
- - + -
- ]
225 : 344 : end subroutine eigensolver_run
226 : :
227 : : ! free the memory used by eigensolver
228 : 344 : subroutine eigensolver_finalize(self)
229 : : class(eigensolver), intent(inout):: self
230 : 344 : self%ndim = -1
231 : 344 : self%lwork = -1
232 [ + - ]: 344 : ABI_SFREE(self%work)
233 [ + - ]: 344 : ABI_SFREE(self%rwork)
234 : 344 : end subroutine eigensolver_finalize
235 : :
236 : : !----------------------------------------------------------------------
237 : : !> @brief insertion_sort_int: sort a array using insertion sort algorithm
238 : : !> it is a memory safe method but is generally slow.
239 : : !> @param[inout] a: the array to be sorted. and will output inplace
240 : : !> @param[inout] order (optional) the sorted index, it can be used to sort
241 : : !> other arrays so that the order in consistent.
242 : : !----------------------------------------------------------------------
243 [ # # ]: 0 : subroutine insertion_sort_double(a, order)
244 : : real(dp), intent(inout):: a(:)
245 : : integer, optional, intent(inout):: order(size(a))
246 : : integer:: n, i, j
247 : : real(dp):: v
248 : 0 : n = size(a)
249 [ # # ]: 0 : if (present(order)) then
250 [ # # ]: 0 : do i = 1, n
251 : 0 : order(i)=i
252 : : end do
253 : : end if
254 [ # # ]: 0 : do i = 2, n
255 : 0 : v = a(i)
256 : 0 : j = i-1
257 [ # # ]: 0 : do while(j >= 1 )
258 [ # # ]: 0 : if (a(j)<=v) exit
259 : 0 : a(j+1)=a(j)
260 [ # # ]: 0 : if(present(order)) order(j+1)=order(j)
261 : 0 : j = j-1
262 : : end do
263 : 0 : a(j+1)=v
264 [ # # ]: 0 : if(present(order)) order(j+1)=i
265 : : end do
266 : :
267 [ # # ]: 0 : end subroutine insertion_sort_double
268 : :
269 : :
270 : : ! -4/2-> -1, 4/2->2. Then (-1, 0, 1, 2)
271 : 126 : pure function div2(x) result(y)
272 : : integer, intent(in):: x
273 : : integer:: y
274 [ + - - + : 126 : if(mod(x, 2)==0 .and. x < 0) then
+ - - + +
- - + ]
275 : 63 : y = x/2+1
276 : : else
277 : 63 : y = x/2
278 : : endif
279 : : end function div2
280 : :
281 : : ! build the Rlist for the given kmesh
282 : : ! Rlist is a 3*n array, where n is the number of R vectors
283 : 3 : subroutine build_Rgrid(kmesh, Rlist)
284 : : integer, intent(in):: kmesh(3)
285 : : integer, allocatable, intent(inout):: Rlist(:,:)
286 : : integer:: n, i1, i2, i3, i
287 : :
288 : 3 : n = kmesh(1) * kmesh(2) *kmesh(3)
289 [ - + + - : 9 : ABI_MALLOC(Rlist, (3, n))
+ - + - -
+ - + -
+ ]
290 : 3 : i = 0
291 : : ! Note that C/Fortran integer division is "truncate towards 0" division,
292 : : ! whereas Python one is "floor" division.
293 : : ! For C/Fortran, the behavior for even and odd numbers is
294 : : ! not consistent and need special treatment in div2.
295 [ + - + - : 21 : do i3 = div2(-kmesh(3)), div2(kmesh(3))
+ + ]
296 [ + - + - : 87 : do i2 = div2(-kmesh(2)), div2(kmesh(2))
+ + ]
297 [ + - + - : 348 : do i1 = div2(-kmesh(1)), div2(kmesh(1))
+ + ]
298 : 192 : i = i+1
299 [ + + ]: 816 : Rlist(:, i) = [i1, i2, i3]
300 : : end do
301 : : end do
302 : : end do
303 : 3 : end subroutine build_Rgrid
304 : :
305 : :
306 : :
307 [ # # # # : 0 : end module m_scdm_math
# # # # #
# # # #
# ]
308 : :
309 : : !!***
|