Line data Source code
1 : !--------------------------------------------------------------------------------------------------!
2 : ! CP2K: A general program to perform molecular dynamics simulations !
3 : ! Copyright 2000-2025 CP2K developers group <https://cp2k.org> !
4 : ! !
5 : ! SPDX-License-Identifier: GPL-2.0-or-later !
6 : !--------------------------------------------------------------------------------------------------!
7 :
8 : ! **************************************************************************************************
9 : !> \brief Collection of simple mathematical functions and subroutines
10 : !> \par History
11 : !> FUNCTION angle updated and FUNCTION dihedral angle added; cleaned
12 : !> (13.03.2004,MK)
13 : !> \author MK (15.11.1998)
14 : ! **************************************************************************************************
15 : MODULE mathlib
16 :
17 : USE kinds, ONLY: default_string_length,&
18 : dp
19 : USE mathconstants, ONLY: euler,&
20 : fac,&
21 : oorootpi,&
22 : z_one,&
23 : z_zero
24 : #include "../base/base_uses.f90"
25 :
26 : IMPLICIT NONE
27 :
28 : PRIVATE
29 :
30 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'mathlib'
31 : REAL(KIND=dp), PARAMETER :: eps_geo = 1.0E-6_dp
32 :
33 : ! Public subroutines
34 :
35 : PUBLIC :: build_rotmat, &
36 : jacobi, &
37 : diamat_all, &
38 : invmat, &
39 : invmat_symm, &
40 : invert_matrix, &
41 : get_pseudo_inverse_svd, &
42 : get_pseudo_inverse_diag, &
43 : symmetrize_matrix, &
44 : unit_matrix, diag, &
45 : erfc_cutoff, &
46 : diag_antisym, &
47 : diag_complex, &
48 : gemm_square
49 :
50 : ! Public functions
51 :
52 : PUBLIC :: angle, &
53 : binomial, &
54 : binomial_gen, &
55 : multinomial, &
56 : det_3x3, &
57 : dihedral_angle, &
58 : gcd, &
59 : inv_3x3, &
60 : lcm, &
61 : vector_product, &
62 : pswitch, &
63 : rotate_vector, &
64 : reflect_vector, &
65 : expint, abnormal_value, &
66 : get_diag, &
67 : set_diag
68 :
69 : INTERFACE det_3x3
70 : MODULE PROCEDURE det_3x3_1, det_3x3_2
71 : END INTERFACE
72 :
73 : INTERFACE invert_matrix
74 : MODULE PROCEDURE invert_matrix_d, invert_matrix_z
75 : END INTERFACE
76 :
77 : INTERFACE set_diag
78 : MODULE PROCEDURE set_diag_scalar_d, set_diag_scalar_z
79 : END INTERFACE
80 :
81 : INTERFACE swap
82 : MODULE PROCEDURE swap_scalar, swap_vector
83 : END INTERFACE
84 :
85 : INTERFACE unit_matrix
86 : MODULE PROCEDURE unit_matrix_d, unit_matrix_z
87 : END INTERFACE
88 :
89 : INTERFACE gemm_square
90 : MODULE PROCEDURE zgemm_square_2, zgemm_square_3, dgemm_square_2, dgemm_square_3
91 : END INTERFACE
92 :
93 : CONTAINS
94 :
95 : ! **************************************************************************************************
96 : !> \brief Polynomial (5th degree) switching function
97 : !> f(a) = 1 .... f(b) = 0 with f'(a) = f"(a) = f'(b) = f"(b) = 0
98 : !> \param x ...
99 : !> \param a ...
100 : !> \param b ...
101 : !> \param order ...
102 : !> \return =0 : f(x)
103 : !> \return =1 : f'(x)
104 : !> \return =2 : f"(x)
105 : ! **************************************************************************************************
106 16030 : FUNCTION pswitch(x, a, b, order) RESULT(fx)
107 : REAL(KIND=dp) :: x, a, b
108 : INTEGER :: order
109 : REAL(KIND=dp) :: fx
110 :
111 : REAL(KIND=dp) :: u, u2, u3
112 :
113 16030 : CPASSERT(b > a)
114 16030 : IF (x < a .OR. x > b) THEN
115 : ! outside switching intervall
116 14990 : IF (order > 0) THEN
117 : ! derivatives are 0
118 : fx = 0.0_dp
119 : ELSE
120 7495 : IF (x < a) THEN
121 : ! x < a => f(x) = 1
122 : fx = 1.0_dp
123 : ELSE
124 : ! x > b => f(x) = 0
125 7288 : fx = 0.0_dp
126 : END IF
127 : END IF
128 : ELSE
129 : ! renormalized coordinate
130 1040 : u = (x - a)/(b - a)
131 1560 : SELECT CASE (order)
132 : CASE (0)
133 520 : u2 = u*u
134 520 : u3 = u2*u
135 520 : fx = 1._dp - 10._dp*u3 + 15._dp*u2*u2 - 6._dp*u2*u3
136 : CASE (1)
137 520 : u2 = u*u
138 520 : fx = -30._dp*u2 + 60._dp*u*u2 - 30._dp*u2*u2
139 520 : fx = fx/(b - a)
140 : CASE (2)
141 0 : u2 = u*u
142 0 : fx = -60._dp*u + 180._dp*u2 - 120._dp*u*u2
143 0 : fx = fx/(b - a)**2
144 : CASE DEFAULT
145 1040 : CPABORT('order not defined')
146 : END SELECT
147 : END IF
148 :
149 16030 : END FUNCTION pswitch
150 :
151 : ! **************************************************************************************************
152 : !> \brief determines if a value is not normal (e.g. for Inf and Nan)
153 : !> based on IO to work also under optimization.
154 : !> \param a input value
155 : !> \return TRUE for NaN and Inf
156 : ! **************************************************************************************************
157 347240 : LOGICAL FUNCTION abnormal_value(a)
158 : REAL(KIND=dp) :: a
159 :
160 : CHARACTER(LEN=32) :: buffer
161 :
162 347240 : abnormal_value = .FALSE.
163 : ! the function should work when compiled with -ffast-math and similar
164 : ! unfortunately, that option asserts that all numbers are normals,
165 : ! which the compiler uses to optimize the function to .FALSE. if based on the IEEE module
166 : ! therefore, pass this to the Fortran runtime/printf, if things are NaN or Inf, error out.
167 347240 : WRITE (buffer, *) a
168 347240 : IF (INDEX(buffer, "N") /= 0 .OR. INDEX(buffer, "n") /= 0) abnormal_value = .TRUE.
169 :
170 347240 : END FUNCTION abnormal_value
171 :
172 : ! **************************************************************************************************
173 : !> \brief Calculation of the angle between the vectors a and b.
174 : !> The angle is returned in radians.
175 : !> \param a ...
176 : !> \param b ...
177 : !> \return ...
178 : !> \date 14.10.1998
179 : !> \author MK
180 : !> \version 1.0
181 : ! **************************************************************************************************
182 625481 : PURE FUNCTION angle(a, b) RESULT(angle_ab)
183 : REAL(KIND=dp), DIMENSION(:), INTENT(IN) :: a, b
184 : REAL(KIND=dp) :: angle_ab
185 :
186 : REAL(KIND=dp) :: length_of_a, length_of_b
187 625481 : REAL(KIND=dp), DIMENSION(SIZE(a, 1)) :: a_norm, b_norm
188 :
189 2501924 : length_of_a = SQRT(DOT_PRODUCT(a, a))
190 2501924 : length_of_b = SQRT(DOT_PRODUCT(b, b))
191 :
192 625481 : IF ((length_of_a > eps_geo) .AND. (length_of_b > eps_geo)) THEN
193 2501924 : a_norm(:) = a(:)/length_of_a
194 2501924 : b_norm(:) = b(:)/length_of_b
195 2501924 : angle_ab = ACOS(MIN(MAX(DOT_PRODUCT(a_norm, b_norm), -1.0_dp), 1.0_dp))
196 : ELSE
197 : angle_ab = 0.0_dp
198 : END IF
199 :
200 625481 : END FUNCTION angle
201 :
202 : ! **************************************************************************************************
203 : !> \brief The binomial coefficient n over k for 0 <= k <= n is calculated,
204 : !> otherwise zero is returned.
205 : !> \param n ...
206 : !> \param k ...
207 : !> \return ...
208 : !> \date 08.03.1999
209 : !> \author MK
210 : !> \version 1.0
211 : ! **************************************************************************************************
212 343397237 : ELEMENTAL FUNCTION binomial(n, k) RESULT(n_over_k)
213 : INTEGER, INTENT(IN) :: n, k
214 : REAL(KIND=dp) :: n_over_k
215 :
216 343397237 : IF ((k >= 0) .AND. (k <= n)) THEN
217 340606299 : n_over_k = fac(n)/(fac(n - k)*fac(k))
218 : ELSE
219 : n_over_k = 0.0_dp
220 : END IF
221 :
222 343397237 : END FUNCTION binomial
223 :
224 : ! **************************************************************************************************
225 : !> \brief The generalized binomial coefficient z over k for 0 <= k <= n is calculated.
226 : !> (z) z*(z-1)*...*(z-k+2)*(z-k+1)
227 : !> ( ) = ---------------------------
228 : !> (k) k!
229 : !> \param z ...
230 : !> \param k ...
231 : !> \return ...
232 : !> \date 11.11.2019
233 : !> \author FS
234 : !> \version 1.0
235 : ! **************************************************************************************************
236 171640 : ELEMENTAL FUNCTION binomial_gen(z, k) RESULT(z_over_k)
237 : REAL(KIND=dp), INTENT(IN) :: z
238 : INTEGER, INTENT(IN) :: k
239 : REAL(KIND=dp) :: z_over_k
240 :
241 : INTEGER :: i
242 :
243 171640 : IF (k >= 0) THEN
244 : z_over_k = 1.0_dp
245 257460 : DO i = 1, k
246 257460 : z_over_k = z_over_k*(z - i + 1)/REAL(i, dp)
247 : END DO
248 : ELSE
249 : z_over_k = 0.0_dp
250 : END IF
251 :
252 171640 : END FUNCTION binomial_gen
253 :
254 : ! **************************************************************************************************
255 : !> \brief Calculates the multinomial coefficients
256 : !> \param n ...
257 : !> \param k ...
258 : !> \return ...
259 : !> \author Ole Schuett
260 : ! **************************************************************************************************
261 8982 : PURE FUNCTION multinomial(n, k) RESULT(res)
262 : INTEGER, INTENT(IN) :: n
263 : INTEGER, DIMENSION(:), INTENT(IN) :: k
264 : REAL(KIND=dp) :: res
265 :
266 : INTEGER :: i
267 : REAL(KIND=dp) :: denom
268 :
269 71856 : IF (ALL(k >= 0) .AND. SUM(k) == n) THEN
270 5280 : denom = 1.0_dp
271 21120 : DO i = 1, SIZE(k)
272 21120 : denom = denom*fac(k(i))
273 : END DO
274 5280 : res = fac(n)/denom
275 : ELSE
276 : res = 0.0_dp
277 : END IF
278 :
279 8982 : END FUNCTION multinomial
280 :
281 : ! **************************************************************************************************
282 : !> \brief The rotation matrix rotmat which rotates a vector about a
283 : !> rotation axis defined by the vector a is build up.
284 : !> The rotation angle is phi (radians).
285 : !> \param phi ...
286 : !> \param a ...
287 : !> \param rotmat ...
288 : !> \date 16.10.1998
289 : !> \author MK
290 : !> \version 1.0
291 : ! **************************************************************************************************
292 15759 : PURE SUBROUTINE build_rotmat(phi, a, rotmat)
293 : REAL(KIND=dp), INTENT(IN) :: phi
294 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: a
295 : REAL(KIND=dp), DIMENSION(3, 3), INTENT(OUT) :: rotmat
296 :
297 : REAL(KIND=dp) :: cosp, cost, length_of_a, sinp
298 : REAL(KIND=dp), DIMENSION(3) :: d
299 :
300 15759 : length_of_a = SQRT(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
301 : ! Check the length of the vector a
302 15759 : IF (length_of_a > eps_geo) THEN
303 :
304 63036 : d(:) = a(:)/length_of_a
305 :
306 15759 : cosp = COS(phi)
307 15759 : sinp = SIN(phi)
308 15759 : cost = 1.0_dp - cosp
309 :
310 15759 : rotmat(1, 1) = d(1)*d(1)*cost + cosp
311 15759 : rotmat(1, 2) = d(1)*d(2)*cost - d(3)*sinp
312 15759 : rotmat(1, 3) = d(1)*d(3)*cost + d(2)*sinp
313 15759 : rotmat(2, 1) = d(2)*d(1)*cost + d(3)*sinp
314 15759 : rotmat(2, 2) = d(2)*d(2)*cost + cosp
315 15759 : rotmat(2, 3) = d(2)*d(3)*cost - d(1)*sinp
316 15759 : rotmat(3, 1) = d(3)*d(1)*cost - d(2)*sinp
317 15759 : rotmat(3, 2) = d(3)*d(2)*cost + d(1)*sinp
318 15759 : rotmat(3, 3) = d(3)*d(3)*cost + cosp
319 : ELSE
320 0 : CALL unit_matrix(rotmat)
321 : END IF
322 :
323 15759 : END SUBROUTINE build_rotmat
324 :
325 : ! **************************************************************************************************
326 : !> \brief Returns the determinante of the 3x3 matrix a.
327 : !> \param a ...
328 : !> \return ...
329 : !> \date 13.03.2004
330 : !> \author MK
331 : !> \version 1.0
332 : ! **************************************************************************************************
333 16462660 : PURE FUNCTION det_3x3_1(a) RESULT(det_a)
334 : REAL(KIND=dp), DIMENSION(3, 3), INTENT(IN) :: a
335 : REAL(KIND=dp) :: det_a
336 :
337 : det_a = a(1, 1)*(a(2, 2)*a(3, 3) - a(2, 3)*a(3, 2)) + &
338 : a(1, 2)*(a(2, 3)*a(3, 1) - a(2, 1)*a(3, 3)) + &
339 16462660 : a(1, 3)*(a(2, 1)*a(3, 2) - a(2, 2)*a(3, 1))
340 :
341 16462660 : END FUNCTION det_3x3_1
342 :
343 : ! **************************************************************************************************
344 : !> \brief Returns the determinante of the 3x3 matrix a given by its columns.
345 : !> \param a1 ...
346 : !> \param a2 ...
347 : !> \param a3 ...
348 : !> \return ...
349 : !> \date 13.03.2004
350 : !> \author MK
351 : !> \version 1.0
352 : ! **************************************************************************************************
353 6272 : PURE FUNCTION det_3x3_2(a1, a2, a3) RESULT(det_a)
354 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: a1, a2, a3
355 : REAL(KIND=dp) :: det_a
356 :
357 : det_a = a1(1)*(a2(2)*a3(3) - a3(2)*a2(3)) + &
358 : a2(1)*(a3(2)*a1(3) - a1(2)*a3(3)) + &
359 6272 : a3(1)*(a1(2)*a2(3) - a2(2)*a1(3))
360 :
361 6272 : END FUNCTION det_3x3_2
362 :
363 : ! **************************************************************************************************
364 : !> \brief Diagonalize the symmetric n by n matrix a using the LAPACK
365 : !> library. Only the upper triangle of matrix a is used.
366 : !> Externals (LAPACK 3.0)
367 : !> \param a ...
368 : !> \param eigval ...
369 : !> \param dac ...
370 : !> \date 29.03.1999
371 : !> \par Variables
372 : !> - a : Symmetric matrix to be diagonalized (input; upper triangle) ->
373 : !> - eigenvectors of the matrix a (output).
374 : !> - dac : If true, then the divide-and-conquer algorithm is applied.
375 : !> - eigval : Eigenvalues of the matrix a (output).
376 : !> \author MK
377 : !> \version 1.0
378 : ! **************************************************************************************************
379 74793 : SUBROUTINE diamat_all(a, eigval, dac)
380 : REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT) :: a
381 : REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: eigval
382 : LOGICAL, INTENT(IN), OPTIONAL :: dac
383 :
384 : CHARACTER(len=*), PARAMETER :: routineN = 'diamat_all'
385 :
386 : INTEGER :: handle, info, liwork, lwork, n, nb
387 74793 : INTEGER, ALLOCATABLE, DIMENSION(:) :: iwork
388 : INTEGER, EXTERNAL :: ilaenv
389 : LOGICAL :: divide_and_conquer
390 74793 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: work
391 :
392 : EXTERNAL dsyev, dsyevd
393 :
394 74793 : CALL timeset(routineN, handle)
395 :
396 : ! Get the size of the matrix a
397 74793 : n = SIZE(a, 1)
398 :
399 : ! Check the size of matrix a
400 74793 : IF (SIZE(a, 2) /= n) THEN
401 0 : CPABORT("Check the size of matrix a (parameter #1)")
402 : END IF
403 :
404 : ! Check the size of vector eigval
405 74793 : IF (SIZE(eigval) /= n) THEN
406 0 : CPABORT("The dimension of vector eigval is too small")
407 : END IF
408 :
409 : ! Check, if the divide-and-conquer algorithm is requested
410 :
411 74793 : IF (PRESENT(dac)) THEN
412 205 : divide_and_conquer = dac
413 : ELSE
414 : divide_and_conquer = .FALSE.
415 : END IF
416 :
417 : ! Get the optimal work storage size
418 :
419 205 : IF (divide_and_conquer) THEN
420 205 : lwork = 2*n**2 + 6*n + 1
421 205 : liwork = 5*n + 3
422 : ELSE
423 74588 : nb = ilaenv(1, "DSYTRD", "U", n, -1, -1, -1)
424 74588 : lwork = (nb + 2)*n
425 : END IF
426 :
427 : ! Allocate work storage
428 :
429 224379 : ALLOCATE (work(lwork))
430 74793 : IF (divide_and_conquer) THEN
431 615 : ALLOCATE (iwork(liwork))
432 : END IF
433 :
434 : ! Diagonalize the matrix a
435 :
436 74793 : info = 0
437 : IF (divide_and_conquer) THEN
438 205 : CALL dsyevd("V", "U", n, a, n, eigval, work, lwork, iwork, liwork, info)
439 : ELSE
440 74978 : CALL dsyev("V", "U", n, a, n, eigval, work, lwork, info)
441 : END IF
442 :
443 74793 : IF (info /= 0) THEN
444 0 : IF (divide_and_conquer) THEN
445 0 : CPABORT("The matrix diagonalization with dsyevd failed")
446 : ELSE
447 0 : CPABORT("The matrix diagonalization with dsyev failed")
448 : END IF
449 : END IF
450 :
451 : ! Release work storage
452 74793 : DEALLOCATE (work)
453 :
454 74793 : IF (divide_and_conquer) THEN
455 205 : DEALLOCATE (iwork)
456 : END IF
457 :
458 74793 : CALL timestop(handle)
459 :
460 149586 : END SUBROUTINE diamat_all
461 :
462 : ! **************************************************************************************************
463 : !> \brief Returns the dihedral angle, i.e. the angle between the planes
464 : !> defined by the vectors (-ab,bc) and (cd,-bc).
465 : !> The dihedral angle is returned in radians.
466 : !> \param ab ...
467 : !> \param bc ...
468 : !> \param cd ...
469 : !> \return ...
470 : !> \date 13.03.2004
471 : !> \author MK
472 : !> \version 1.0
473 : ! **************************************************************************************************
474 116 : PURE FUNCTION dihedral_angle(ab, bc, cd) RESULT(dihedral_angle_abcd)
475 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: ab, bc, cd
476 : REAL(KIND=dp) :: dihedral_angle_abcd
477 :
478 : REAL(KIND=dp) :: det_abcd
479 : REAL(KIND=dp), DIMENSION(3) :: abc, bcd
480 :
481 464 : abc = vector_product(bc, -ab)
482 464 : bcd = vector_product(cd, -bc)
483 : ! Calculate the normal vectors of the planes
484 : ! defined by the points a,b,c and b,c,d
485 :
486 464 : det_abcd = det_3x3(abc, bcd, -bc)
487 116 : dihedral_angle_abcd = SIGN(1.0_dp, det_abcd)*angle(abc, bcd)
488 :
489 116 : END FUNCTION dihedral_angle
490 :
491 : ! **************************************************************************************************
492 : !> \brief Return the diagonal elements of matrix a as a vector.
493 : !> \param a ...
494 : !> \return ...
495 : !> \date 20.11.1998
496 : !> \author MK
497 : !> \version 1.0
498 : ! **************************************************************************************************
499 55764 : PURE FUNCTION get_diag(a) RESULT(a_diag)
500 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: a
501 : REAL(KIND=dp), &
502 : DIMENSION(MIN(SIZE(a, 1), SIZE(a, 2))) :: a_diag
503 :
504 : INTEGER :: i, n
505 :
506 55764 : n = MIN(SIZE(a, 1), SIZE(a, 2))
507 :
508 5640990 : DO i = 1, n
509 5640990 : a_diag(i) = a(i, i)
510 : END DO
511 :
512 55764 : END FUNCTION get_diag
513 :
514 : ! **************************************************************************************************
515 : !> \brief Returns the inverse of the 3 x 3 matrix a.
516 : !> \param a ...
517 : !> \return ...
518 : !> \date 13.03.2004
519 : !> \author MK
520 : !> \version 1.0
521 : ! **************************************************************************************************
522 15704799 : PURE FUNCTION inv_3x3(a) RESULT(a_inv)
523 : REAL(KIND=dp), DIMENSION(3, 3), INTENT(IN) :: a
524 : REAL(KIND=dp), DIMENSION(3, 3) :: a_inv
525 :
526 : REAL(KIND=dp) :: det_a
527 :
528 15704799 : det_a = 1.0_dp/det_3x3(a)
529 :
530 15704799 : a_inv(1, 1) = (a(2, 2)*a(3, 3) - a(3, 2)*a(2, 3))*det_a
531 15704799 : a_inv(2, 1) = (a(2, 3)*a(3, 1) - a(3, 3)*a(2, 1))*det_a
532 15704799 : a_inv(3, 1) = (a(2, 1)*a(3, 2) - a(3, 1)*a(2, 2))*det_a
533 :
534 15704799 : a_inv(1, 2) = (a(1, 3)*a(3, 2) - a(3, 3)*a(1, 2))*det_a
535 15704799 : a_inv(2, 2) = (a(1, 1)*a(3, 3) - a(3, 1)*a(1, 3))*det_a
536 15704799 : a_inv(3, 2) = (a(1, 2)*a(3, 1) - a(3, 2)*a(1, 1))*det_a
537 :
538 15704799 : a_inv(1, 3) = (a(1, 2)*a(2, 3) - a(2, 2)*a(1, 3))*det_a
539 15704799 : a_inv(2, 3) = (a(1, 3)*a(2, 1) - a(2, 3)*a(1, 1))*det_a
540 15704799 : a_inv(3, 3) = (a(1, 1)*a(2, 2) - a(2, 1)*a(1, 2))*det_a
541 :
542 15704799 : END FUNCTION inv_3x3
543 :
544 : ! **************************************************************************************************
545 : !> \brief returns inverse of matrix using the lapack routines DGETRF and DGETRI
546 : !> \param a ...
547 : !> \param info ...
548 : ! **************************************************************************************************
549 6190 : SUBROUTINE invmat(a, info)
550 : REAL(KIND=dp), INTENT(INOUT) :: a(:, :)
551 : INTEGER, INTENT(OUT) :: info
552 :
553 : CHARACTER(LEN=*), PARAMETER :: routineN = 'invmat'
554 :
555 : INTEGER :: handle, lwork, n
556 6190 : INTEGER, ALLOCATABLE :: ipiv(:)
557 6190 : REAL(KIND=dp), ALLOCATABLE :: work(:)
558 :
559 6190 : CALL timeset(routineN, handle)
560 :
561 6190 : n = SIZE(a, 1)
562 6190 : lwork = 20*n
563 18570 : ALLOCATE (ipiv(n))
564 18570 : ALLOCATE (work(lwork))
565 59582 : ipiv = 0
566 1074030 : work = 0._dp
567 6190 : info = 0
568 437640 : CALL dgetrf(n, n, a, n, ipiv, info)
569 6190 : IF (info == 0) THEN
570 437592 : CALL dgetri(n, a, n, ipiv, work, lwork, info)
571 : END IF
572 6190 : DEALLOCATE (ipiv, work)
573 :
574 6190 : CALL timestop(handle)
575 :
576 6190 : END SUBROUTINE invmat
577 :
578 : ! **************************************************************************************************
579 : !> \brief returns inverse of real symmetric, positive definite matrix
580 : !> \param a matrix
581 : !> \param potrf if cholesky decomposition of a was already done using dpotrf.
582 : !> If not given, cholesky decomposition of a will be done before inversion.
583 : !> \param uplo indicating if the upper or lower triangle of a is stored.
584 : !> \author Dorothea Golze [02.2015]
585 : ! **************************************************************************************************
586 2430 : SUBROUTINE invmat_symm(a, potrf, uplo)
587 : REAL(KIND=dp), INTENT(INOUT) :: a(:, :)
588 : LOGICAL, INTENT(IN), OPTIONAL :: potrf
589 : CHARACTER(LEN=1), INTENT(IN), OPTIONAL :: uplo
590 :
591 : CHARACTER(LEN=*), PARAMETER :: routineN = 'invmat_symm'
592 :
593 : CHARACTER(LEN=1) :: myuplo
594 : INTEGER :: handle, info, n
595 : LOGICAL :: do_potrf
596 :
597 2430 : CALL timeset(routineN, handle)
598 :
599 2430 : do_potrf = .TRUE.
600 2430 : IF (PRESENT(potrf)) do_potrf = potrf
601 :
602 2430 : myuplo = 'U'
603 2430 : IF (PRESENT(uplo)) myuplo = uplo
604 :
605 2430 : n = SIZE(a, 1)
606 2430 : info = 0
607 :
608 : ! do cholesky decomposition
609 2430 : IF (do_potrf) THEN
610 3413 : CALL dpotrf(myuplo, n, a, n, info)
611 2213 : IF (info /= 0) CPABORT("DPOTRF failed")
612 : END IF
613 :
614 : ! do inversion using the cholesky decomposition
615 3630 : CALL dpotri(myuplo, n, a, n, info)
616 2430 : IF (info /= 0) CPABORT("Matrix inversion failed")
617 :
618 : ! complete the matrix
619 2430 : IF ((myuplo == "U") .OR. (myuplo == "u")) THEN
620 2430 : CALL symmetrize_matrix(a, "upper_to_lower")
621 : ELSE
622 0 : CALL symmetrize_matrix(a, "lower_to_upper")
623 : END IF
624 :
625 2430 : CALL timestop(handle)
626 :
627 2430 : END SUBROUTINE invmat_symm
628 :
629 : ! **************************************************************************************************
630 : !> \brief Compute the inverse of the n by n real matrix a using the LAPACK
631 : !> library
632 : !> \param a ...
633 : !> \param a_inverse ...
634 : !> \param eval_error ...
635 : !> \param option ...
636 : !> \param improve ...
637 : !> \date 23.03.1999
638 : !> \par Variables
639 : !> - a : Real matrix to be inverted (input).
640 : !> - a_inverse: Inverse of the matrix a (output).
641 : !> - a_lu : LU factorization of matrix a.
642 : !> - a_norm : Norm of matrix a.
643 : !> - error : Estimated error of the inversion.
644 : !> - r_cond : Reciprocal condition number of the matrix a.
645 : !> - trans : "N" => invert a
646 : !> - "T" => invert transpose(a)
647 : !> \author MK
648 : !> \version 1.0
649 : !> \note NB add improve argument, used to disable call to dgerfs
650 : ! **************************************************************************************************
651 2210 : SUBROUTINE invert_matrix_d(a, a_inverse, eval_error, option, improve)
652 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: a
653 : REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: a_inverse
654 : REAL(KIND=dp), INTENT(OUT) :: eval_error
655 : CHARACTER(LEN=1), INTENT(IN), OPTIONAL :: option
656 : LOGICAL, INTENT(IN), OPTIONAL :: improve
657 :
658 : CHARACTER(LEN=1) :: norm, trans
659 : CHARACTER(LEN=default_string_length) :: message
660 : INTEGER :: info, iter, n
661 2210 : INTEGER, ALLOCATABLE, DIMENSION(:) :: ipiv, iwork
662 : LOGICAL :: do_improve
663 : REAL(KIND=dp) :: a_norm, old_eval_error, r_cond
664 2210 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: berr, ferr, work
665 2210 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: a_lu, b
666 : REAL(KIND=dp), EXTERNAL :: dlange
667 :
668 : EXTERNAL dgecon, dgerfs, dgetrf, dgetrs
669 :
670 : ! Check for optional parameter
671 2210 : IF (PRESENT(option)) THEN
672 498 : trans = option
673 : ELSE
674 1712 : trans = "N"
675 : END IF
676 :
677 2210 : IF (PRESENT(improve)) THEN
678 536 : do_improve = improve
679 : ELSE
680 : do_improve = .TRUE.
681 : END IF
682 :
683 : ! Get the dimension of matrix a
684 2210 : n = SIZE(a, 1)
685 :
686 : ! Check array dimensions
687 2210 : IF (n == 0) THEN
688 0 : CPABORT("Matrix to be inverted of zero size")
689 : END IF
690 :
691 2210 : IF (n /= SIZE(a, 2)) THEN
692 0 : CPABORT("Check the array bounds of parameter #1")
693 : END IF
694 :
695 2210 : IF ((n /= SIZE(a_inverse, 1)) .OR. &
696 : (n /= SIZE(a_inverse, 2))) THEN
697 0 : CPABORT("Check the array bounds of parameter #2")
698 : END IF
699 :
700 : ! Allocate work storage
701 8840 : ALLOCATE (a_lu(n, n))
702 6630 : ALLOCATE (b(n, n))
703 6630 : ALLOCATE (berr(n))
704 4420 : ALLOCATE (ferr(n))
705 6630 : ALLOCATE (ipiv(n))
706 4420 : ALLOCATE (iwork(n))
707 6630 : ALLOCATE (work(4*n))
708 :
709 2459074 : a_lu(1:n, 1:n) = a(1:n, 1:n)
710 :
711 : ! Compute the LU factorization of the matrix a
712 2210 : CALL dgetrf(n, n, a_lu, n, ipiv, info)
713 :
714 2210 : IF (info /= 0) THEN
715 0 : CPABORT("The LU factorization in dgetrf failed")
716 : END IF
717 :
718 : ! Compute the norm of the matrix a
719 :
720 2210 : IF (trans == "N") THEN
721 1960 : norm = '1'
722 : ELSE
723 250 : norm = 'I'
724 : END IF
725 :
726 2210 : a_norm = dlange(norm, n, n, a, n, work)
727 :
728 : ! Compute the reciprocal of the condition number of a
729 :
730 2210 : CALL dgecon(norm, n, a_lu, n, a_norm, r_cond, work, iwork, info)
731 :
732 2210 : IF (info /= 0) THEN
733 0 : CPABORT("The computation of the condition number in dgecon failed")
734 : END IF
735 :
736 2210 : IF (r_cond < EPSILON(0.0_dp)) THEN
737 0 : WRITE (message, "(A,ES10.3)") "R_COND =", r_cond
738 : CALL cp_abort(__LOCATION__, &
739 : "Bad condition number "//TRIM(message)//" (smaller than the machine "// &
740 0 : "working precision)")
741 : END IF
742 :
743 : ! Solve a system of linear equations using the LU factorization computed by dgetrf
744 :
745 2210 : CALL unit_matrix(a_inverse)
746 :
747 2210 : CALL dgetrs(trans, n, n, a_lu, n, ipiv, a_inverse, n, info)
748 :
749 2210 : IF (info /= 0) THEN
750 0 : CPABORT("Solving the system of linear equations in dgetrs failed")
751 : END IF
752 :
753 : ! Improve the computed solution iteratively
754 2210 : CALL unit_matrix(b) ! Initialize right-hand sides
755 :
756 2210 : eval_error = 0.0_dp
757 :
758 2210 : IF (do_improve) THEN
759 6368 : DO iter = 1, 10
760 :
761 : CALL dgerfs(trans, n, n, a, n, a_lu, n, ipiv, b, n, a_inverse, n, ferr, berr, &
762 6130 : work, iwork, info)
763 :
764 6130 : IF (info /= 0) THEN
765 0 : CPABORT("Improving the computed solution in dgerfs failed")
766 : END IF
767 :
768 6130 : old_eval_error = eval_error
769 170810 : eval_error = MAXVAL(ferr)
770 :
771 6368 : IF (ABS(eval_error - old_eval_error) <= EPSILON(1.0_dp)) EXIT
772 :
773 : END DO
774 : END IF
775 :
776 : ! Release work storage
777 2210 : DEALLOCATE (work)
778 2210 : DEALLOCATE (iwork)
779 2210 : DEALLOCATE (ipiv)
780 2210 : DEALLOCATE (ferr)
781 2210 : DEALLOCATE (berr)
782 2210 : DEALLOCATE (b)
783 2210 : DEALLOCATE (a_lu)
784 :
785 2210 : END SUBROUTINE invert_matrix_d
786 :
787 : ! **************************************************************************************************
788 : !> \brief Compute the inverse of the n by n complex matrix a using the LAPACK
789 : !> library
790 : !> \param a ...
791 : !> \param a_inverse ...
792 : !> \param eval_error ...
793 : !> \param option ...
794 : !> \date 08.06.2009
795 : !> \par Variables
796 : !> - a : Complex matrix to be inverted (input).
797 : !> - a_inverse: Inverse of the matrix a (output).
798 : !> - a_lu : LU factorization of matrix a.
799 : !> - a_norm : Norm of matrix a.
800 : !> - error : Estimated error of the inversion.
801 : !> - r_cond : Reciprocal condition number of the matrix a.
802 : !> - trans : "N" => invert a
803 : !> - "T" => invert transpose(a)
804 : !> \author MK
805 : !> \version 1.0
806 : ! **************************************************************************************************
807 0 : SUBROUTINE invert_matrix_z(a, a_inverse, eval_error, option)
808 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN) :: a
809 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: a_inverse
810 : REAL(KIND=dp), INTENT(OUT) :: eval_error
811 : CHARACTER(LEN=1), INTENT(IN), OPTIONAL :: option
812 :
813 : CHARACTER(LEN=1) :: norm, trans
814 : CHARACTER(LEN=default_string_length) :: message
815 0 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:) :: work
816 0 : COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: a_lu, b
817 : INTEGER :: info, iter, n
818 0 : INTEGER, ALLOCATABLE, DIMENSION(:) :: ipiv
819 : REAL(KIND=dp) :: a_norm, old_eval_error, r_cond
820 0 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: berr, ferr, rwork
821 : REAL(KIND=dp), EXTERNAL :: zlange
822 :
823 : EXTERNAL zgecon, zgerfs, zgetrf, zgetrs
824 :
825 : ! Check for optional parameter
826 0 : IF (PRESENT(option)) THEN
827 0 : trans = option
828 : ELSE
829 0 : trans = "N"
830 : END IF
831 :
832 : ! Get the dimension of matrix a
833 0 : n = SIZE(a, 1)
834 :
835 : ! Check array dimensions
836 0 : IF (n == 0) THEN
837 0 : CPABORT("Matrix to be inverted of zero size")
838 : END IF
839 :
840 0 : IF (n /= SIZE(a, 2)) THEN
841 0 : CPABORT("Check the array bounds of parameter #1")
842 : END IF
843 :
844 0 : IF ((n /= SIZE(a_inverse, 1)) .OR. &
845 : (n /= SIZE(a_inverse, 2))) THEN
846 0 : CPABORT("Check the array bounds of parameter #2")
847 : END IF
848 :
849 : ! Allocate work storage
850 0 : ALLOCATE (a_lu(n, n))
851 0 : ALLOCATE (b(n, n))
852 0 : ALLOCATE (berr(n))
853 0 : ALLOCATE (ferr(n))
854 0 : ALLOCATE (ipiv(n))
855 0 : ALLOCATE (rwork(2*n))
856 0 : ALLOCATE (work(2*n))
857 :
858 0 : a_lu(1:n, 1:n) = a(1:n, 1:n)
859 :
860 : ! Compute the LU factorization of the matrix a
861 0 : CALL zgetrf(n, n, a_lu, n, ipiv, info)
862 :
863 0 : IF (info /= 0) THEN
864 0 : CPABORT("The LU factorization in dgetrf failed")
865 : END IF
866 :
867 : ! Compute the norm of the matrix a
868 :
869 0 : IF (trans == "N") THEN
870 0 : norm = '1'
871 : ELSE
872 0 : norm = 'I'
873 : END IF
874 :
875 0 : a_norm = zlange(norm, n, n, a, n, work)
876 :
877 : ! Compute the reciprocal of the condition number of a
878 :
879 0 : CALL zgecon(norm, n, a_lu, n, a_norm, r_cond, work, rwork, info)
880 :
881 0 : IF (info /= 0) THEN
882 0 : CPABORT("The computation of the condition number in dgecon failed")
883 : END IF
884 :
885 0 : IF (r_cond < EPSILON(0.0_dp)) THEN
886 0 : WRITE (message, "(A,ES10.3)") "R_COND =", r_cond
887 : CALL cp_abort(__LOCATION__, &
888 : "Bad condition number "//TRIM(message)//" (smaller than the machine "// &
889 0 : "working precision)")
890 : END IF
891 :
892 : ! Solve a system of linear equations using the LU factorization computed by dgetrf
893 :
894 0 : CALL unit_matrix(a_inverse)
895 :
896 0 : CALL zgetrs(trans, n, n, a_lu, n, ipiv, a_inverse, n, info)
897 :
898 0 : IF (info /= 0) THEN
899 0 : CPABORT("Solving the system of linear equations in dgetrs failed")
900 : END IF
901 :
902 : ! Improve the computed solution iteratively
903 0 : CALL unit_matrix(b) ! Initialize right-hand sides
904 :
905 0 : eval_error = 0.0_dp
906 :
907 0 : DO iter = 1, 10
908 :
909 : CALL zgerfs(trans, n, n, a, n, a_lu, n, ipiv, b, n, a_inverse, n, ferr, berr, &
910 0 : work, rwork, info)
911 :
912 0 : IF (info /= 0) THEN
913 0 : CPABORT("Improving the computed solution in dgerfs failed")
914 : END IF
915 :
916 0 : old_eval_error = eval_error
917 0 : eval_error = MAXVAL(ferr)
918 :
919 0 : IF (ABS(eval_error - old_eval_error) <= EPSILON(1.0_dp)) EXIT
920 :
921 : END DO
922 :
923 : ! Release work storage
924 0 : DEALLOCATE (work)
925 0 : DEALLOCATE (rwork)
926 0 : DEALLOCATE (ipiv)
927 0 : DEALLOCATE (ferr)
928 0 : DEALLOCATE (berr)
929 0 : DEALLOCATE (b)
930 0 : DEALLOCATE (a_lu)
931 :
932 0 : END SUBROUTINE invert_matrix_z
933 :
934 : ! **************************************************************************************************
935 : !> \brief returns the pseudoinverse of a real, square matrix using singular
936 : !> value decomposition
937 : !> \param a matrix a
938 : !> \param a_pinverse pseudoinverse of matrix a
939 : !> \param rskip parameter for setting small singular values to zero
940 : !> \param determinant determinant of matrix a (optional output)
941 : !> \param sval array holding singular values of matrix a (optional output)
942 : !> \author Dorothea Golze [02.2015]
943 : ! **************************************************************************************************
944 535 : SUBROUTINE get_pseudo_inverse_svd(a, a_pinverse, rskip, determinant, sval)
945 : REAL(KIND=dp), DIMENSION(:, :) :: a, a_pinverse
946 : REAL(KIND=dp), INTENT(IN) :: rskip
947 : REAL(KIND=dp), INTENT(OUT), OPTIONAL :: determinant
948 : REAL(KIND=dp), DIMENSION(:), INTENT(INOUT), &
949 : OPTIONAL, POINTER :: sval
950 :
951 : CHARACTER(LEN=*), PARAMETER :: routineN = 'get_pseudo_inverse_svd'
952 :
953 : INTEGER :: handle, i, info, lwork, n
954 535 : INTEGER, ALLOCATABLE, DIMENSION(:) :: iwork
955 535 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: sig, work
956 535 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: sig_plus, temp_mat, u, vt
957 :
958 535 : CALL timeset(routineN, handle)
959 :
960 535 : n = SIZE(a, 1)
961 7490 : ALLOCATE (u(n, n), vt(n, n), sig(n), sig_plus(n, n), iwork(8*n), work(1), temp_mat(n, n))
962 48679 : u(:, :) = 0.0_dp
963 48679 : vt(:, :) = 0.0_dp
964 2821 : sig(:) = 0.0_dp
965 48679 : sig_plus = 0.0_dp
966 1070 : work = 0.0_dp
967 18823 : iwork = 0
968 535 : IF (PRESENT(determinant)) determinant = 1.0_dp
969 :
970 : ! work size query
971 535 : lwork = -1
972 : CALL dgesdd('A', n, n, a(1, 1), n, sig(1), u(1, 1), n, vt(1, 1), n, work(1), &
973 535 : lwork, iwork(1), info)
974 :
975 535 : IF (info /= 0) THEN
976 0 : CPABORT("ERROR in DGESDD: Could not retrieve work array sizes")
977 : END IF
978 535 : lwork = INT(work(1))
979 535 : DEALLOCATE (work)
980 1605 : ALLOCATE (work(lwork))
981 :
982 : ! do SVD
983 : CALL dgesdd('A', n, n, a(1, 1), n, sig(1), u(1, 1), n, vt(1, 1), n, work(1), &
984 535 : lwork, iwork(1), info)
985 :
986 535 : IF (info /= 0) THEN
987 0 : CPABORT("SVD failed")
988 : END IF
989 :
990 535 : IF (PRESENT(sval)) THEN
991 0 : CPASSERT(.NOT. ASSOCIATED(sval))
992 0 : ALLOCATE (sval(n))
993 0 : sval(:) = sig
994 : END IF
995 :
996 : ! set singular values that are too small to zero
997 2821 : DO i = 1, n
998 50965 : IF (sig(i) > rskip*MAXVAL(sig)) THEN
999 2250 : IF (PRESENT(determinant)) &
1000 0 : determinant = determinant*sig(i)
1001 2250 : sig_plus(i, i) = 1._dp/sig(i)
1002 : ELSE
1003 36 : sig_plus(i, i) = 0.0_dp
1004 : END IF
1005 : END DO
1006 :
1007 : ! build pseudoinverse: V*sig_plus*UT
1008 535 : CALL dgemm("N", "T", n, n, n, 1._dp, sig_plus, n, u, n, 0._dp, temp_mat, n)
1009 535 : CALL dgemm("T", "N", n, n, n, 1._dp, vt, n, temp_mat, n, 0._dp, a_pinverse, n)
1010 :
1011 535 : DEALLOCATE (u, vt, sig, iwork, work, sig_plus, temp_mat)
1012 :
1013 535 : CALL timestop(handle)
1014 :
1015 535 : END SUBROUTINE get_pseudo_inverse_svd
1016 :
1017 : ! **************************************************************************************************
1018 : !> \brief returns the pseudoinverse of a real, symmetric and positive definite
1019 : !> matrix using diagonalization.
1020 : !> \param a matrix a
1021 : !> \param a_pinverse pseudoinverse of matrix a
1022 : !> \param rskip parameter for setting small eigenvalues to zero
1023 : !> \author Dorothea Golze [02.2015]
1024 : ! **************************************************************************************************
1025 1161 : SUBROUTINE get_pseudo_inverse_diag(a, a_pinverse, rskip)
1026 : REAL(KIND=dp), DIMENSION(:, :) :: a, a_pinverse
1027 : REAL(KIND=dp), INTENT(IN) :: rskip
1028 :
1029 : CHARACTER(LEN=*), PARAMETER :: routineN = 'get_pseudo_inverse_diag'
1030 :
1031 : INTEGER :: handle, i, info, lwork, n
1032 1161 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eig, work
1033 1161 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: dinv, temp_mat
1034 :
1035 1161 : CALL timeset(routineN, handle)
1036 :
1037 1161 : info = 0
1038 1161 : n = SIZE(a, 1)
1039 9288 : ALLOCATE (dinv(n, n), eig(n), work(1), temp_mat(n, n))
1040 186519 : dinv(:, :) = 0.0_dp
1041 15075 : eig(:) = 0.0_dp
1042 2322 : work(:) = 0.0_dp
1043 186519 : temp_mat = 0.0_dp
1044 :
1045 : ! work size query
1046 1161 : lwork = -1
1047 1161 : CALL dsyev('V', 'U', n, a, n, eig(1), work(1), lwork, info)
1048 1161 : IF (info /= 0) THEN
1049 0 : CPABORT("ERROR in DSYEV: Could not retrieve work array sizes")
1050 : END IF
1051 1161 : lwork = INT(work(1))
1052 1161 : DEALLOCATE (work)
1053 3483 : ALLOCATE (work(lwork))
1054 474237 : work = 0.0_dp
1055 :
1056 : ! get eigenvalues and eigenvectors
1057 1161 : CALL dsyev('V', 'U', n, a, n, eig(1), work(1), lwork, info)
1058 :
1059 1161 : IF (info /= 0) THEN
1060 0 : CPABORT("Matrix diagonalization failed")
1061 : END IF
1062 :
1063 : ! set eigenvalues that are too small to zero
1064 15075 : DO i = 1, n
1065 200433 : IF (eig(i) > rskip*MAXVAL(eig)) THEN
1066 12454 : dinv(i, i) = 1.0_dp/eig(i)
1067 : ELSE
1068 1460 : dinv(i, i) = 0._dp
1069 : END IF
1070 : END DO
1071 :
1072 : ! build pseudoinverse: U*dinv*UT
1073 1161 : CALL dgemm("N", "T", n, n, n, 1._dp, dinv, n, a, n, 0._dp, temp_mat, n)
1074 1161 : CALL dgemm("N", "N", n, n, n, 1._dp, a, n, temp_mat, n, 0._dp, a_pinverse, n)
1075 :
1076 1161 : DEALLOCATE (eig, work, dinv, temp_mat)
1077 :
1078 1161 : CALL timestop(handle)
1079 :
1080 1161 : END SUBROUTINE get_pseudo_inverse_diag
1081 :
1082 : ! **************************************************************************************************
1083 : !> \brief Reflection of the vector a through a mirror plane defined by the
1084 : !> normal vector b. The reflected vector a is stored in a_mirror.
1085 : !> \param a ...
1086 : !> \param b ...
1087 : !> \return ...
1088 : !> \date 16.10.1998
1089 : !> \author MK
1090 : !> \version 1.0
1091 : ! **************************************************************************************************
1092 19487 : PURE FUNCTION reflect_vector(a, b) RESULT(a_mirror)
1093 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: a, b
1094 : REAL(KIND=dp), DIMENSION(3) :: a_mirror
1095 :
1096 : REAL(KIND=dp) :: length_of_b, scapro
1097 : REAL(KIND=dp), DIMENSION(3) :: d
1098 :
1099 19487 : length_of_b = SQRT(b(1)*b(1) + b(2)*b(2) + b(3)*b(3))
1100 :
1101 19487 : IF (length_of_b > eps_geo) THEN
1102 :
1103 77948 : d(:) = b(:)/length_of_b
1104 :
1105 : ! Calculate the mirror image a_mirror of the vector a
1106 19487 : scapro = a(1)*d(1) + a(2)*d(2) + a(3)*d(3)
1107 :
1108 77948 : a_mirror(:) = a(:) - 2.0_dp*scapro*d(:)
1109 :
1110 : ELSE
1111 :
1112 0 : a_mirror(:) = 0.0_dp
1113 :
1114 : END IF
1115 :
1116 19487 : END FUNCTION reflect_vector
1117 :
1118 : ! **************************************************************************************************
1119 : !> \brief Rotation of the vector a about an rotation axis defined by the
1120 : !> vector b. The rotation angle is phi (radians). The rotated vector
1121 : !> a is stored in a_rot.
1122 : !> \param a ...
1123 : !> \param phi ...
1124 : !> \param b ...
1125 : !> \return ...
1126 : !> \date 16.10.1998
1127 : !> \author MK
1128 : !> \version 1.0
1129 : ! **************************************************************************************************
1130 4098 : PURE FUNCTION rotate_vector(a, phi, b) RESULT(a_rot)
1131 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: a
1132 : REAL(KIND=dp), INTENT(IN) :: phi
1133 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: b
1134 : REAL(KIND=dp), DIMENSION(3) :: a_rot
1135 :
1136 : REAL(KIND=dp) :: length_of_b
1137 : REAL(KIND=dp), DIMENSION(3, 3) :: rotmat
1138 :
1139 4098 : length_of_b = SQRT(b(1)*b(1) + b(2)*b(2) + b(3)*b(3))
1140 4098 : IF (length_of_b > eps_geo) THEN
1141 :
1142 : ! Build up the rotation matrix rotmat
1143 3964 : CALL build_rotmat(phi, b, rotmat)
1144 :
1145 : ! Rotate the vector a by phi about the axis defined by vector b
1146 63424 : a_rot(:) = MATMUL(rotmat, a)
1147 :
1148 : ELSE
1149 :
1150 536 : a_rot(:) = 0.0_dp
1151 :
1152 : END IF
1153 :
1154 4098 : END FUNCTION rotate_vector
1155 :
1156 : ! **************************************************************************************************
1157 : !> \brief Set the diagonal elements of matrix a to b.
1158 : !> \param a ...
1159 : !> \param b ...
1160 : !> \date 20.11.1998
1161 : !> \author MK
1162 : !> \version 1.0
1163 : ! **************************************************************************************************
1164 36960 : PURE SUBROUTINE set_diag_scalar_d(a, b)
1165 : REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT) :: a
1166 : REAL(KIND=dp), INTENT(IN) :: b
1167 :
1168 : INTEGER :: i, n
1169 :
1170 36960 : n = MIN(SIZE(a, 1), SIZE(a, 2))
1171 204841 : DO i = 1, n
1172 204841 : a(i, i) = b
1173 : END DO
1174 :
1175 36960 : END SUBROUTINE set_diag_scalar_d
1176 :
1177 : ! **************************************************************************************************
1178 : !> \brief ...
1179 : !> \param a ...
1180 : !> \param b ...
1181 : ! **************************************************************************************************
1182 0 : PURE SUBROUTINE set_diag_scalar_z(a, b)
1183 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(INOUT) :: a
1184 : COMPLEX(KIND=dp), INTENT(IN) :: b
1185 :
1186 : INTEGER :: i, n
1187 :
1188 0 : n = MIN(SIZE(a, 1), SIZE(a, 2))
1189 0 : DO i = 1, n
1190 0 : a(i, i) = b
1191 : END DO
1192 :
1193 0 : END SUBROUTINE set_diag_scalar_z
1194 :
1195 : ! **************************************************************************************************
1196 : !> \brief Symmetrize the matrix a.
1197 : !> \param a ...
1198 : !> \param option ...
1199 : !> \date 16.10.1998
1200 : !> \author MK
1201 : !> \version 1.0
1202 : ! **************************************************************************************************
1203 19038 : SUBROUTINE symmetrize_matrix(a, option)
1204 : REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT) :: a
1205 : CHARACTER(LEN=*), INTENT(IN) :: option
1206 :
1207 : INTEGER :: i, n
1208 :
1209 19038 : n = MIN(SIZE(a, 1), SIZE(a, 2))
1210 :
1211 19038 : IF (option == "lower_to_upper") THEN
1212 0 : DO i = 1, n - 1
1213 0 : a(i, i + 1:n) = a(i + 1:n, i)
1214 : END DO
1215 19038 : ELSE IF (option == "upper_to_lower") THEN
1216 432207 : DO i = 1, n - 1
1217 22848474 : a(i + 1:n, i) = a(i, i + 1:n)
1218 : END DO
1219 36 : ELSE IF (option == "anti_lower_to_upper") THEN
1220 0 : DO i = 1, n - 1
1221 0 : a(i, i + 1:n) = -a(i + 1:n, i)
1222 : END DO
1223 36 : ELSE IF (option == "anti_upper_to_lower") THEN
1224 564 : DO i = 1, n - 1
1225 4716 : a(i + 1:n, i) = -a(i, i + 1:n)
1226 : END DO
1227 : ELSE
1228 0 : CPABORT("Invalid option <"//TRIM(option)//"> was specified for parameter #2")
1229 : END IF
1230 :
1231 19038 : END SUBROUTINE symmetrize_matrix
1232 :
1233 : ! **************************************************************************************************
1234 : !> \brief Set the matrix a to be a unit matrix.
1235 : !> \param a ...
1236 : !> \date 16.10.1998
1237 : !> \author MK
1238 : !> \version 1.0
1239 : ! **************************************************************************************************
1240 36960 : PURE SUBROUTINE unit_matrix_d(a)
1241 : REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT) :: a
1242 :
1243 6733604 : a(:, :) = 0.0_dp
1244 36960 : CALL set_diag(a, 1.0_dp)
1245 :
1246 36960 : END SUBROUTINE unit_matrix_d
1247 :
1248 : ! **************************************************************************************************
1249 : !> \brief ...
1250 : !> \param a ...
1251 : ! **************************************************************************************************
1252 0 : PURE SUBROUTINE unit_matrix_z(a)
1253 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(INOUT) :: a
1254 :
1255 0 : a(:, :) = (0.0_dp, 0.0_dp)
1256 0 : CALL set_diag(a, (1.0_dp, 0.0_dp))
1257 :
1258 0 : END SUBROUTINE unit_matrix_z
1259 :
1260 : ! **************************************************************************************************
1261 : !> \brief Calculation of the vector product c = a x b.
1262 : !> \param a ...
1263 : !> \param b ...
1264 : !> \return ...
1265 : !> \date 16.10.1998
1266 : !> \author MK
1267 : !> \version 1.0
1268 : ! **************************************************************************************************
1269 7659 : PURE FUNCTION vector_product(a, b) RESULT(c)
1270 : REAL(KIND=dp), DIMENSION(3), INTENT(IN) :: a, b
1271 : REAL(KIND=dp), DIMENSION(3) :: c
1272 :
1273 7659 : c(1) = a(2)*b(3) - a(3)*b(2)
1274 7659 : c(2) = a(3)*b(1) - a(1)*b(3)
1275 7659 : c(3) = a(1)*b(2) - a(2)*b(1)
1276 :
1277 7659 : END FUNCTION vector_product
1278 :
1279 : ! **************************************************************************************************
1280 : !> \brief computes the greatest common divisor of two number
1281 : !> \param a ...
1282 : !> \param b ...
1283 : !> \return ...
1284 : !> \author Joost VandeVondele
1285 : ! **************************************************************************************************
1286 988090 : ELEMENTAL FUNCTION gcd(a, b)
1287 : INTEGER, INTENT(IN) :: a, b
1288 : INTEGER :: gcd
1289 :
1290 : INTEGER :: aa, ab, l, rem, s
1291 :
1292 988090 : aa = ABS(a)
1293 988090 : ab = ABS(b)
1294 988090 : IF (aa < ab) THEN
1295 : s = aa
1296 : l = ab
1297 : ELSE
1298 968196 : s = ab
1299 968196 : l = aa
1300 : END IF
1301 988090 : IF (s /= 0) THEN
1302 : DO
1303 988090 : rem = MOD(l, s)
1304 988090 : IF (rem == 0) EXIT
1305 : l = s
1306 988090 : s = rem
1307 : END DO
1308 : GCD = s
1309 : ELSE
1310 : GCD = l
1311 : END IF
1312 988090 : END FUNCTION gcd
1313 :
1314 : ! **************************************************************************************************
1315 : !> \brief computes the least common multiplier of two numbers
1316 : !> \param a ...
1317 : !> \param b ...
1318 : !> \return ...
1319 : !> \author Joost VandeVondele
1320 : ! **************************************************************************************************
1321 474108 : ELEMENTAL FUNCTION lcm(a, b)
1322 : INTEGER, INTENT(IN) :: a, b
1323 : INTEGER :: lcm
1324 :
1325 : INTEGER :: tmp
1326 :
1327 474108 : tmp = gcd(a, b)
1328 474108 : IF (tmp == 0) THEN
1329 : lcm = 0
1330 : ELSE
1331 : ! could still overflow if the true lcm is larger than maxint
1332 474108 : lcm = ABS((a/tmp)*b)
1333 : END IF
1334 474108 : END FUNCTION lcm
1335 :
1336 : ! **************************************************************************************************
1337 : !> \brief computes the exponential integral
1338 : !> Ei(x) = Int(exp(-x*t)/t,t=1..infinity) x>0
1339 : !> \param x ...
1340 : !> \return ...
1341 : !> \author JGH (adapted from Numerical recipies)
1342 : ! **************************************************************************************************
1343 0 : FUNCTION ei(x)
1344 : REAL(dp) :: x, ei
1345 :
1346 : INTEGER, PARAMETER :: maxit = 100
1347 : REAL(dp), PARAMETER :: eps = EPSILON(0.0_dp), &
1348 : fpmin = TINY(0.0_dp)
1349 :
1350 : INTEGER :: k
1351 : REAL(dp) :: fact, prev, sum1, term
1352 :
1353 0 : IF (x <= 0._dp) THEN
1354 0 : CPABORT("Invalid argument")
1355 : END IF
1356 :
1357 0 : IF (x < fpmin) THEN
1358 0 : ei = LOG(x) + euler
1359 0 : ELSE IF (x <= -LOG(EPS)) THEN
1360 : sum1 = 0._dp
1361 : fact = 1._dp
1362 0 : DO k = 1, maxit
1363 0 : fact = fact*x/REAL(k, dp)
1364 0 : term = fact/REAL(k, dp)
1365 0 : sum1 = sum1 + term
1366 0 : IF (term < eps*sum1) EXIT
1367 : END DO
1368 0 : ei = sum1 + LOG(x) + euler
1369 : ELSE
1370 : sum1 = 0._dp
1371 : term = 1._dp
1372 0 : DO k = 1, maxit
1373 0 : prev = term
1374 0 : term = term*REAL(k, dp)/x
1375 0 : IF (term < eps) EXIT
1376 0 : IF (term < prev) THEN
1377 0 : sum1 = sum1 + term
1378 : ELSE
1379 0 : sum1 = sum1 - prev
1380 0 : EXIT
1381 : END IF
1382 : END DO
1383 0 : ei = EXP(x)*(1._dp + sum1)/x
1384 : END IF
1385 :
1386 0 : END FUNCTION ei
1387 :
1388 : ! **************************************************************************************************
1389 : !> \brief computes the exponential integral
1390 : !> En(x) = Int(exp(-x*t)/t^n,t=1..infinity) x>0, n=0,1,..
1391 : !> Note: Ei(-x) = -E1(x)
1392 : !> \param n ...
1393 : !> \param x ...
1394 : !> \return ...
1395 : !> \par History
1396 : !> 05.2007 Created
1397 : !> \author Manuel Guidon (adapted from Numerical recipies)
1398 : ! **************************************************************************************************
1399 262050896 : ELEMENTAL IMPURE FUNCTION expint(n, x)
1400 : INTEGER, INTENT(IN) :: n
1401 : REAL(dp), INTENT(IN) :: x
1402 : REAL(dp) :: expint
1403 :
1404 : INTEGER, PARAMETER :: maxit = 100
1405 : REAL(dp), PARAMETER :: eps = 6.e-14_dp, euler = 0.5772156649015328606065120_dp, &
1406 : fpmin = TINY(0.0_dp)
1407 :
1408 : INTEGER :: i, ii, nm1
1409 : REAL(dp) :: a, b, c, d, del, fact, h, psi
1410 :
1411 262050896 : nm1 = n - 1
1412 :
1413 262050896 : IF (n < 0 .OR. x < 0.0_dp .OR. (x == 0.0_dp .AND. (n == 0 .OR. n == 1))) THEN
1414 0 : CPABORT("Invalid argument")
1415 262050896 : ELSE IF (n == 0) THEN !Special case.
1416 0 : expint = EXP(-x)/x
1417 262050896 : ELSE IF (x == 0.0_dp) THEN !Another special case.
1418 0 : expint = 1.0_dp/nm1
1419 262050896 : ELSE IF (x > 1.0_dp) THEN !Lentz's algorithm (5.2).
1420 213921911 : b = x + n
1421 213921911 : c = 1.0_dp/FPMIN
1422 213921911 : d = 1.0_dp/b
1423 213921911 : h = d
1424 6056783668 : DO i = 1, MAXIT
1425 6056783668 : a = -i*(nm1 + i)
1426 6056783668 : b = b + 2.0_dp
1427 6056783668 : d = 1.0_dp/(a*d + b)
1428 6056783668 : c = b + a/c
1429 6056783668 : del = c*d
1430 6056783668 : h = h*del
1431 6056783668 : IF (ABS(del - 1.0_dp) < EPS) THEN
1432 213921911 : expint = h*EXP(-x)
1433 213921911 : RETURN
1434 : END IF
1435 : END DO
1436 0 : CPABORT("continued fraction failed in expint")
1437 : ELSE !Evaluate series.
1438 48128985 : IF (nm1 /= 0) THEN !Set first term.
1439 0 : expint = 1.0_dp/nm1
1440 : ELSE
1441 48128985 : expint = -LOG(x) - euler
1442 : END IF
1443 : fact = 1.0_dp
1444 423129382 : DO i = 1, MAXIT
1445 423129382 : fact = -fact*x/i
1446 423129382 : IF (i /= nm1) THEN
1447 423129382 : del = -fact/(i - nm1)
1448 : ELSE
1449 : psi = -euler !Compute I(n).
1450 0 : DO ii = 1, nm1
1451 0 : psi = psi + 1.0_dp/ii
1452 : END DO
1453 0 : del = fact*(-LOG(x) + psi)
1454 : END IF
1455 423129382 : expint = expint + del
1456 423129382 : IF (ABS(del) < ABS(expint)*EPS) RETURN
1457 : END DO
1458 0 : CPABORT("series failed in expint")
1459 : END IF
1460 :
1461 : END FUNCTION expint
1462 :
1463 : ! **************************************************************************************************
1464 : !> \brief Jacobi matrix diagonalization. The eigenvalues are returned in
1465 : !> vector d and the eigenvectors are returned in matrix v in ascending
1466 : !> order.
1467 : !>
1468 : !> \param a ...
1469 : !> \param d ...
1470 : !> \param v ...
1471 : !> \par History
1472 : !> - Creation (20.11.98, Matthias Krack)
1473 : ! **************************************************************************************************
1474 32482 : SUBROUTINE jacobi(a, d, v)
1475 : REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT) :: a
1476 : REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: d
1477 : REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: v
1478 :
1479 : INTEGER :: n
1480 :
1481 32482 : n = SIZE(d(:))
1482 :
1483 : ! Diagonalize matrix a
1484 32482 : CALL diag(n, a, d, v)
1485 :
1486 : ! Sort eigenvalues and eigenvector in ascending order
1487 32482 : CALL eigsrt(n, d, v)
1488 :
1489 32482 : END SUBROUTINE jacobi
1490 :
1491 : ! **************************************************************************************************
1492 : !> \brief Diagonalize matrix a. The eigenvalues are returned in vector d
1493 : !> and the eigenvectors are returned in matrix v.
1494 : !>
1495 : !> \param n matrix/vector extent (problem size)
1496 : !> \param a matrix to be diagonalised
1497 : !> \param d vector of eigenvalues
1498 : !> \param v matrix of eigenvectors
1499 : !> \par History
1500 : !> - Creation (20.11.98, Matthias Krack)
1501 : ! **************************************************************************************************
1502 32482 : SUBROUTINE diag(n, a, d, v)
1503 : INTEGER, INTENT(IN) :: n
1504 : REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT) :: a
1505 : REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: d
1506 : REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: v
1507 :
1508 : CHARACTER(len=*), PARAMETER :: routineN = 'diag'
1509 : REAL(KIND=dp), PARAMETER :: a_eps = 1.0E-10_dp, d_eps = 1.0E-3_dp
1510 :
1511 : INTEGER :: handle, i, ip, iq
1512 : REAL(KIND=dp) :: a_max, apq, c, d_min, dip, diq, g, h, s, &
1513 : t, tau, theta, tresh
1514 32482 : REAL(KIND=dp), DIMENSION(n) :: b, z
1515 :
1516 32482 : CALL timeset(routineN, handle)
1517 :
1518 32482 : a_max = 0.0_dp
1519 118711 : DO ip = 1, n - 1
1520 944857 : a_max = MAX(a_max, MAXVAL(ABS(a(ip, ip + 1:n))))
1521 118711 : b(ip) = a(ip, ip) ! get_diag(a)
1522 : END DO
1523 32482 : b(n) = a(n, n)
1524 :
1525 32482 : CALL unit_matrix(v)
1526 :
1527 : ! Go for 50 iterations
1528 157691 : DO i = 1, 50
1529 966282 : d = b
1530 1123973 : d_min = MAX(d_eps, MINVAL(ABS(b)))
1531 157691 : IF (a_max < a_eps*d_min) THEN
1532 32482 : CALL timestop(handle)
1533 : RETURN
1534 : END IF
1535 196684 : tresh = MERGE(a_max, 0.0_dp, (i < 4))
1536 815089 : z = 0.0_dp
1537 689880 : DO ip = 1, n - 1
1538 6258460 : DO iq = ip + 1, n
1539 5568580 : dip = d(ip)
1540 5568580 : diq = d(iq)
1541 5568580 : apq = a(ip, iq)
1542 5568580 : g = 100.0_dp*ABS(apq)
1543 6133251 : IF (tresh < ABS(apq)) THEN
1544 3255373 : h = diq - dip
1545 3255373 : IF ((ABS(h) + g) /= ABS(h)) THEN
1546 2469113 : theta = 0.5_dp*h/apq
1547 2469113 : t = 1.0_dp/(ABS(theta) + SQRT(1.0_dp + theta**2))
1548 2469113 : IF (theta < 0.0_dp) t = -t
1549 : ELSE
1550 786260 : t = apq/h
1551 : END IF
1552 3255373 : c = 1.0_dp/SQRT(1.0_dp + t**2)
1553 3255373 : s = t*c
1554 3255373 : tau = s/(1.0_dp + c)
1555 3255373 : h = t*apq
1556 3255373 : z(ip) = z(ip) - h
1557 3255373 : z(iq) = z(iq) + h
1558 3255373 : d(ip) = dip - h
1559 3255373 : d(iq) = diq + h
1560 3255373 : a(ip, iq) = 0.0_dp
1561 3255373 : CALL jrotate(a(1:ip - 1, ip), a(1:ip - 1, iq), s, tau)
1562 3255373 : CALL jrotate(a(ip, ip + 1:iq - 1), a(ip + 1:iq - 1, iq), s, tau)
1563 3255373 : CALL jrotate(a(ip, iq + 1:n), a(iq, iq + 1:n), s, tau)
1564 3255373 : CALL jrotate(v(:, ip), v(:, iq), s, tau)
1565 : ELSE IF ((4 < i) .AND. &
1566 0 : ((ABS(dip) + g) == ABS(dip)) .AND. &
1567 2313207 : ((ABS(diq) + g) == ABS(diq))) THEN
1568 0 : a(ip, iq) = 0.0_dp
1569 : END IF
1570 : END DO
1571 : END DO
1572 815089 : b = b + z
1573 : a_max = 0.0_dp
1574 689880 : DO ip = 1, n - 1
1575 6823131 : a_max = MAX(a_max, MAXVAL(ABS(a(ip, ip + 1:n))))
1576 : END DO
1577 : END DO
1578 0 : WRITE (*, '(/,T2,A,/)') 'Too many iterations in jacobi'
1579 :
1580 0 : CALL timestop(handle)
1581 :
1582 : END SUBROUTINE diag
1583 :
1584 : ! **************************************************************************************************
1585 : !> \brief Perform a Jacobi rotation of the vectors a and b.
1586 : !>
1587 : !> \param a ...
1588 : !> \param b ...
1589 : !> \param ss ...
1590 : !> \param tt ...
1591 : !> \par History
1592 : !> - Creation (20.11.98, Matthias Krack)
1593 : ! **************************************************************************************************
1594 13021492 : PURE SUBROUTINE jrotate(a, b, ss, tt)
1595 : REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: a, b
1596 : REAL(KIND=dp), INTENT(IN) :: ss, tt
1597 :
1598 : REAL(KIND=dp) :: u, v
1599 :
1600 13021492 : u = 1.0_dp - ss*tt
1601 13021492 : v = ss/u
1602 :
1603 221681316 : a = a*u - b*ss
1604 221681316 : b = b*(u + ss*v) + a*v
1605 :
1606 13021492 : END SUBROUTINE jrotate
1607 :
1608 : ! **************************************************************************************************
1609 : !> \brief Sort the values in vector d in ascending order and swap the
1610 : !> corresponding columns of matrix v.
1611 : !>
1612 : !> \param n ...
1613 : !> \param d ...
1614 : !> \param v ...
1615 : !> \par History
1616 : !> - Creation (20.11.98, Matthias Krack)
1617 : ! **************************************************************************************************
1618 32482 : SUBROUTINE eigsrt(n, d, v)
1619 : INTEGER, INTENT(IN) :: n
1620 : REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: d
1621 : REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT) :: v
1622 :
1623 : INTEGER :: i, j
1624 :
1625 118711 : DO i = 1, n - 1
1626 944857 : j = SUM(MINLOC(d(i:n))) + i - 1
1627 118711 : IF (j /= i) THEN
1628 43817 : CALL swap(d(i), d(j))
1629 43817 : CALL swap(v(:, i), v(:, j))
1630 : END IF
1631 : END DO
1632 :
1633 32482 : END SUBROUTINE eigsrt
1634 :
1635 : ! **************************************************************************
1636 : !> \brief Swap two scalars
1637 : !>
1638 : !> \param a ...
1639 : !> \param b ...
1640 : !> \par History
1641 : !> - Creation (20.11.98, Matthias Krack)
1642 : ! **************************************************************************************************
1643 43817 : ELEMENTAL SUBROUTINE swap_scalar(a, b)
1644 : REAL(KIND=dp), INTENT(INOUT) :: a, b
1645 :
1646 : REAL(KIND=dp) :: c
1647 :
1648 43817 : c = a
1649 43817 : a = b
1650 43817 : b = c
1651 :
1652 43817 : END SUBROUTINE swap_scalar
1653 :
1654 : ! **************************************************************************
1655 : !> \brief Swap two vectors
1656 : !>
1657 : !> \param a ...
1658 : !> \param b ...
1659 : !> \par History
1660 : !> - Creation (20.11.98, Matthias Krack)
1661 : ! **************************************************************************************************
1662 43817 : SUBROUTINE swap_vector(a, b)
1663 : REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: a, b
1664 :
1665 : INTEGER :: i, n
1666 : REAL(KIND=dp) :: c
1667 :
1668 43817 : n = SIZE(a)
1669 :
1670 43817 : IF (n /= SIZE(b)) THEN
1671 0 : CPABORT("Check the array bounds of the parameters")
1672 : END IF
1673 :
1674 938599 : DO i = 1, n
1675 894782 : c = a(i)
1676 894782 : a(i) = b(i)
1677 938599 : b(i) = c
1678 : END DO
1679 :
1680 43817 : END SUBROUTINE swap_vector
1681 :
1682 : ! **************************************************************************************************
1683 : !> \brief - compute a truncation radius for the shortrange operator
1684 : !> \param eps target accuracy!> \param omg screening parameter
1685 : !> \param omg ...
1686 : !> \param r_cutoff cutoff radius
1687 : !> \par History
1688 : !> 10.2012 created [Hossein Banihashemian]
1689 : !> 05.2019 moved here from hfx_types (A. Bussy)
1690 : !> \author Hossein Banihashemian
1691 : ! **************************************************************************************************
1692 66 : SUBROUTINE erfc_cutoff(eps, omg, r_cutoff)
1693 : IMPLICIT NONE
1694 :
1695 : REAL(dp), INTENT(in) :: eps, omg
1696 : REAL(dp), INTENT(out) :: r_cutoff
1697 :
1698 : CHARACTER(LEN=*), PARAMETER :: routineN = 'erfc_cutoff'
1699 :
1700 : REAL(dp), PARAMETER :: abstol = 1E-10_dp, soltol = 1E-16_dp
1701 : REAL(dp) :: r0, f0, fprime0, delta_r
1702 : INTEGER :: iter, handle
1703 : INTEGER, PARAMETER :: iterMAX = 1000
1704 :
1705 66 : CALL timeset(routineN, handle)
1706 :
1707 : ! initial guess assuming that we are in the asymptotic regime of the erf, and the solution is about 10.
1708 66 : r0 = SQRT(-LOG(eps*omg*10**2))/omg
1709 66 : CALL eval_transc_func(r0, eps, omg, f0, fprime0)
1710 :
1711 638 : DO iter = 1, iterMAX
1712 638 : delta_r = f0/fprime0
1713 638 : r0 = r0 - delta_r
1714 638 : CALL eval_transc_func(r0, eps, omg, f0, fprime0)
1715 638 : IF (ABS(delta_r) < abstol .OR. ABS(f0) < soltol) EXIT
1716 : END DO
1717 66 : CPASSERT(iter <= itermax)
1718 66 : r_cutoff = r0
1719 :
1720 66 : CALL timestop(handle)
1721 : CONTAINS
1722 : ! **************************************************************************************************
1723 : !> \brief ...
1724 : !> \param r ...
1725 : !> \param eps ...
1726 : !> \param omega ...
1727 : !> \param fn ...
1728 : !> \param df ...
1729 : ! **************************************************************************************************
1730 704 : ELEMENTAL SUBROUTINE eval_transc_func(r, eps, omega, fn, df)
1731 : REAL(dp), INTENT(in) :: r, eps, omega
1732 : REAL(dp), INTENT(out) :: fn, df
1733 :
1734 : REAL(dp) :: qr
1735 :
1736 704 : qr = omega*r
1737 704 : fn = ERFC(qr) - r*eps
1738 704 : df = -2.0_dp*oorootpi*omega*EXP(-qr**2) - eps
1739 704 : END SUBROUTINE eval_transc_func
1740 : END SUBROUTINE erfc_cutoff
1741 :
1742 : ! **************************************************************************************************
1743 : !> \brief Diagonalizes a local complex Hermitian matrix using LAPACK. Based on cp_cfm_heevd
1744 : !> \param matrix Hermitian matrix is preserved
1745 : !> \param eigenvectors ...
1746 : !> \param eigenvalues ...
1747 : !> \author A. Bussy
1748 : ! **************************************************************************************************
1749 40311 : SUBROUTINE diag_complex(matrix, eigenvectors, eigenvalues)
1750 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN) :: matrix
1751 : COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT) :: eigenvectors
1752 : REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: eigenvalues
1753 :
1754 : CHARACTER(len=*), PARAMETER :: routineN = 'diag_complex'
1755 :
1756 40311 : COMPLEX(KIND=dp), DIMENSION(:), ALLOCATABLE :: work
1757 : INTEGER :: handle, info, liwork, lrwork, lwork, n
1758 40311 : INTEGER, DIMENSION(:), ALLOCATABLE :: iwork
1759 40311 : REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: rwork
1760 :
1761 40311 : CALL timeset(routineN, handle)
1762 :
1763 40311 : IF (SIZE(matrix, 1) /= SIZE(matrix, 2)) CPABORT("Expected square matrix")
1764 : ! IF (MAXVAL(ABS(matrix - CONJG(TRANSPOSE(matrix)))) > 1e-14_dp) CPABORT("Expected hermitian matrix")
1765 :
1766 40311 : n = SIZE(matrix, 1)
1767 40311 : ALLOCATE (iwork(1), rwork(1), work(1))
1768 :
1769 : ! work space query
1770 40311 : lwork = -1
1771 40311 : lrwork = -1
1772 40311 : liwork = -1
1773 :
1774 40311 : CALL zheevd('V', 'U', n, eigenvectors, n, eigenvalues, work, lwork, rwork, lrwork, iwork, liwork, info)
1775 :
1776 40311 : lwork = CEILING(REAL(work(1), KIND=dp))
1777 40311 : lrwork = CEILING(rwork(1))
1778 40311 : liwork = iwork(1)
1779 :
1780 40311 : DEALLOCATE (iwork, rwork, work)
1781 282177 : ALLOCATE (iwork(liwork), rwork(lrwork), work(lwork))
1782 1600945 : eigenvectors(:, :) = matrix(:, :)
1783 :
1784 : ! final diagonalization
1785 40311 : CALL zheevd('V', 'U', n, eigenvectors, n, eigenvalues, work, lwork, rwork, lrwork, iwork, liwork, info)
1786 :
1787 40311 : DEALLOCATE (iwork, rwork, work)
1788 :
1789 40311 : IF (info /= 0) CPABORT("Diagonalisation of a complex matrix failed")
1790 :
1791 40311 : CALL timestop(handle)
1792 :
1793 40311 : END SUBROUTINE diag_complex
1794 :
1795 : ! **************************************************************************************************
1796 : !> \brief Helper routine for diagonalizing anti symmetric matrices
1797 : !> \param matrix ...
1798 : !> \param evecs ...
1799 : !> \param evals ...
1800 : ! **************************************************************************************************
1801 3539 : SUBROUTINE diag_antisym(matrix, evecs, evals)
1802 : REAL(dp), DIMENSION(:, :) :: matrix
1803 : COMPLEX(dp), DIMENSION(:, :) :: evecs
1804 : COMPLEX(dp), DIMENSION(:) :: evals
1805 :
1806 : COMPLEX(dp), ALLOCATABLE, DIMENSION(:, :) :: matrix_c
1807 : INTEGER :: n
1808 : REAL(dp), ALLOCATABLE, DIMENSION(:) :: eigenvalues
1809 :
1810 3539 : IF (SIZE(matrix, 1) /= SIZE(matrix, 2)) CPABORT("Expected square matrix")
1811 : ! IF (MAXVAL(ABS(matrix + TRANSPOSE(matrix))) > 1e-14_dp) CPABORT("Expected anti-symmetric matrix")
1812 :
1813 3539 : n = SIZE(matrix, 1)
1814 21234 : ALLOCATE (matrix_c(n, n), eigenvalues(n))
1815 :
1816 235869 : matrix_c(:, :) = CMPLX(0.0_dp, -matrix, kind=dp)
1817 3539 : CALL diag_complex(matrix_c, evecs, eigenvalues)
1818 27874 : evals = CMPLX(0.0_dp, eigenvalues, kind=dp)
1819 :
1820 3539 : DEALLOCATE (matrix_c, eigenvalues)
1821 3539 : END SUBROUTINE diag_antisym
1822 : ! **************************************************************************************************
1823 : !> \brief Square array multiplication via LAPACK routines, leaves inputs unchanged
1824 : !> \param A_in Input matrix 1
1825 : !> \param A_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 1
1826 : !> \param B_in Input matrix 2
1827 : !> \param B_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 2
1828 : !> \param C_out Output matrix
1829 : !> \par History
1830 : !> 11.2025 created [Stepan Marek]
1831 : ! **************************************************************************************************
1832 1922 : SUBROUTINE zgemm_square_2(A_in, A_trans, B_in, B_trans, C_out)
1833 : COMPLEX(kind=dp), DIMENSION(:, :), INTENT(IN) :: A_in
1834 : CHARACTER, INTENT(IN) :: A_trans
1835 : COMPLEX(kind=dp), DIMENSION(:, :), INTENT(IN) :: B_in
1836 : CHARACTER, INTENT(IN) :: B_trans
1837 : COMPLEX(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: C_out
1838 :
1839 : CHARACTER(len=*), PARAMETER :: routineN = 'zgemm_square_2'
1840 :
1841 : INTEGER :: handle, n
1842 :
1843 1922 : n = SIZE(A_in, 1)
1844 1922 : IF (n /= SIZE(A_in, 2)) CPABORT("Non-square array 1 (A).")
1845 1922 : IF (n /= SIZE(B_in, 1)) CPABORT("Incompatible (rows) array 2 (B).")
1846 1922 : IF (n /= SIZE(B_in, 2)) CPABORT("Non-square array 2 (B).")
1847 1922 : IF (n /= SIZE(C_out, 1)) CPABORT("Incompatible (rows) result array 3 (C).")
1848 1922 : IF (n /= SIZE(C_out, 2)) CPABORT("Incompatible (cols) result array 3 (C).")
1849 1922 : IF (.NOT. (A_trans == 'N' .OR. A_trans == 'n' .OR. &
1850 : A_trans == 'T' .OR. A_trans == 't' .OR. &
1851 : A_trans == 'C' .OR. A_trans == 'c')) &
1852 0 : CPABORT("Unknown transpose character for array 1 (A).")
1853 1922 : IF (.NOT. (B_trans == 'N' .OR. B_trans == 'n' .OR. &
1854 : B_trans == 'T' .OR. B_trans == 't' .OR. &
1855 : B_trans == 'C' .OR. B_trans == 'c')) &
1856 0 : CPABORT("Unknown transpose character for array 2 (B).")
1857 :
1858 1922 : CALL timeset(routineN, handle)
1859 :
1860 1922 : CALL ZGEMM(A_trans, B_trans, n, n, n, z_one, A_in, n, B_in, n, z_zero, C_out, n)
1861 :
1862 1922 : CALL timestop(handle)
1863 :
1864 1922 : END SUBROUTINE zgemm_square_2
1865 : ! **************************************************************************************************
1866 : !> \brief Square array multiplication via LAPACK routines, leaves inputs unchanged, real matrices
1867 : !> \param A_in Input matrix 1
1868 : !> \param A_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 1
1869 : !> \param B_in Input matrix 2
1870 : !> \param B_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 2
1871 : !> \param C_out Output matrix
1872 : !> \par History
1873 : !> 11.2025 created [Stepan Marek]
1874 : ! **************************************************************************************************
1875 2 : SUBROUTINE dgemm_square_2(A_in, A_trans, B_in, B_trans, C_out)
1876 : REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: A_in
1877 : CHARACTER, INTENT(IN) :: A_trans
1878 : REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: B_in
1879 : CHARACTER, INTENT(IN) :: B_trans
1880 : REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: C_out
1881 :
1882 : CHARACTER(len=*), PARAMETER :: routineN = 'dgemm_square_2'
1883 :
1884 : INTEGER :: handle, n
1885 :
1886 2 : n = SIZE(A_in, 1)
1887 2 : IF (n /= SIZE(A_in, 2)) CPABORT("Non-square array 1 (A).")
1888 2 : IF (n /= SIZE(B_in, 1)) CPABORT("Incompatible (rows) array 2 (B).")
1889 2 : IF (n /= SIZE(B_in, 2)) CPABORT("Non-square array 2 (B).")
1890 2 : IF (n /= SIZE(C_out, 1)) CPABORT("Incompatible (rows) result array 3 (C).")
1891 2 : IF (n /= SIZE(C_out, 2)) CPABORT("Incompatible (cols) result array 3 (C).")
1892 2 : IF (.NOT. (A_trans == 'N' .OR. A_trans == 'n' .OR. &
1893 : A_trans == 'T' .OR. A_trans == 't' .OR. &
1894 : A_trans == 'C' .OR. A_trans == 'c')) &
1895 0 : CPABORT("Unknown transpose character for array 1 (A).")
1896 2 : IF (.NOT. (B_trans == 'N' .OR. B_trans == 'n' .OR. &
1897 : B_trans == 'T' .OR. B_trans == 't' .OR. &
1898 : B_trans == 'C' .OR. B_trans == 'c')) &
1899 0 : CPABORT("Unknown transpose character for array 2 (B).")
1900 :
1901 2 : CALL timeset(routineN, handle)
1902 :
1903 2 : CALL DGEMM(A_trans, B_trans, n, n, n, 1.0_dp, A_in, n, B_in, n, 0.0_dp, C_out, n)
1904 :
1905 2 : CALL timestop(handle)
1906 :
1907 2 : END SUBROUTINE dgemm_square_2
1908 : ! **************************************************************************************************
1909 : !> \brief Square array multiplication via LAPACK routines, leaves inputs unchanged, for 3 matrices
1910 : !> \param A_in Input matrix 1
1911 : !> \param A_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 1
1912 : !> \param B_in Input matrix 2
1913 : !> \param B_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 2
1914 : !> \param C_in Input matrix 3
1915 : !> \param C_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 3
1916 : !> \param D_out Output matrix
1917 : !> \par History
1918 : !> 11.2025 created [Stepan Marek]
1919 : ! **************************************************************************************************
1920 28564 : SUBROUTINE zgemm_square_3(A_in, A_trans, B_in, B_trans, C_in, C_trans, D_out)
1921 : COMPLEX(kind=dp), DIMENSION(:, :), INTENT(IN) :: A_in
1922 : CHARACTER, INTENT(IN) :: A_trans
1923 : COMPLEX(kind=dp), DIMENSION(:, :), INTENT(IN) :: B_in
1924 : CHARACTER, INTENT(IN) :: B_trans
1925 : COMPLEX(kind=dp), DIMENSION(:, :), INTENT(IN) :: C_in
1926 : CHARACTER, INTENT(IN) :: C_trans
1927 : COMPLEX(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: D_out
1928 :
1929 : CHARACTER(len=*), PARAMETER :: routineN = 'zgemm_square_3'
1930 :
1931 28564 : COMPLEX(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: work
1932 : INTEGER :: handle, n
1933 :
1934 28564 : n = SIZE(A_in, 1)
1935 28564 : IF (n /= SIZE(A_in, 2)) CPABORT("Non-square array 1 (A).")
1936 28564 : IF (n /= SIZE(B_in, 1)) CPABORT("Incompatible (rows) array 2 (B).")
1937 28564 : IF (n /= SIZE(B_in, 2)) CPABORT("Non-square array 2 (B).")
1938 28564 : IF (n /= SIZE(C_in, 1)) CPABORT("Incompatible (rows) array 3 (C).")
1939 28564 : IF (n /= SIZE(C_in, 2)) CPABORT("Non-square array 3 (C).")
1940 28564 : IF (n /= SIZE(D_out, 1)) CPABORT("Incompatible (rows) result array 4 (D).")
1941 28564 : IF (n /= SIZE(D_out, 2)) CPABORT("Incompatible (cols) result array 4 (D).")
1942 28564 : IF (.NOT. (A_trans == 'N' .OR. A_trans == 'n' .OR. &
1943 : A_trans == 'T' .OR. A_trans == 't' .OR. &
1944 : A_trans == 'C' .OR. A_trans == 'c')) &
1945 0 : CPABORT("Unknown transpose character for array 1 (A).")
1946 28564 : IF (.NOT. (B_trans == 'N' .OR. B_trans == 'n' .OR. &
1947 : B_trans == 'T' .OR. B_trans == 't' .OR. &
1948 : B_trans == 'C' .OR. B_trans == 'c')) &
1949 0 : CPABORT("Unknown transpose character for array 2 (B).")
1950 28564 : IF (.NOT. (C_trans == 'N' .OR. C_trans == 'n' .OR. &
1951 : C_trans == 'T' .OR. C_trans == 't' .OR. &
1952 : C_trans == 'C' .OR. C_trans == 'c')) &
1953 0 : CPABORT("Unknown transpose character for array 3 (C).")
1954 :
1955 28564 : CALL timeset(routineN, handle)
1956 :
1957 1158112 : ALLOCATE (work(n, n), source=z_zero)
1958 :
1959 28564 : CALL ZGEMM(A_trans, B_trans, n, n, n, z_one, A_in, n, B_in, n, z_zero, work, n)
1960 28564 : CALL ZGEMM('N', C_trans, n, n, n, z_one, work, n, C_in, n, z_zero, D_out, n)
1961 :
1962 28564 : DEALLOCATE (work)
1963 :
1964 28564 : CALL timestop(handle)
1965 :
1966 28564 : END SUBROUTINE zgemm_square_3
1967 : ! **************************************************************************************************
1968 : !> \brief Square array multiplication via LAPACK routines, leaves inputs unchanged, for 3 matrices
1969 : !> \param A_in Input matrix 1
1970 : !> \param A_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 1
1971 : !> \param B_in Input matrix 2
1972 : !> \param B_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 2
1973 : !> \param C_in Input matrix 3
1974 : !> \param C_trans 'N' - no transpose, 'T' - transpose, 'C' - hermitian conj. of matrix 3
1975 : !> \param D_out Output matrix
1976 : !> \par History
1977 : !> 11.2025 created [Stepan Marek]
1978 : ! **************************************************************************************************
1979 4 : SUBROUTINE dgemm_square_3(A_in, A_trans, B_in, B_trans, C_in, C_trans, D_out)
1980 : REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: A_in
1981 : CHARACTER, INTENT(IN) :: A_trans
1982 : REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: B_in
1983 : CHARACTER, INTENT(IN) :: B_trans
1984 : REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: C_in
1985 : CHARACTER, INTENT(IN) :: C_trans
1986 : REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: D_out
1987 :
1988 : CHARACTER(len=*), PARAMETER :: routineN = 'dgemm_square_3'
1989 :
1990 : INTEGER :: handle, n
1991 4 : REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: work
1992 :
1993 4 : n = SIZE(A_in, 1)
1994 4 : IF (n /= SIZE(A_in, 2)) CPABORT("Non-square array 1 (A).")
1995 4 : IF (n /= SIZE(B_in, 1)) CPABORT("Incompatible (rows) array 2 (B).")
1996 4 : IF (n /= SIZE(B_in, 2)) CPABORT("Non-square array 2 (B).")
1997 4 : IF (n /= SIZE(C_in, 1)) CPABORT("Incompatible (rows) array 3 (C).")
1998 4 : IF (n /= SIZE(C_in, 2)) CPABORT("Non-square array 3 (C).")
1999 4 : IF (n /= SIZE(D_out, 1)) CPABORT("Incompatible (rows) result array 4 (D).")
2000 4 : IF (n /= SIZE(D_out, 2)) CPABORT("Incompatible (cols) result array 4 (D).")
2001 4 : IF (.NOT. (A_trans == 'N' .OR. A_trans == 'n' .OR. &
2002 : A_trans == 'T' .OR. A_trans == 't' .OR. &
2003 : A_trans == 'C' .OR. A_trans == 'c')) &
2004 0 : CPABORT("Unknown transpose character for array 1 (A).")
2005 4 : IF (.NOT. (B_trans == 'N' .OR. B_trans == 'n' .OR. &
2006 : B_trans == 'T' .OR. B_trans == 't' .OR. &
2007 : B_trans == 'C' .OR. B_trans == 'c')) &
2008 0 : CPABORT("Unknown transpose character for array 2 (B).")
2009 4 : IF (.NOT. (C_trans == 'N' .OR. C_trans == 'n' .OR. &
2010 : C_trans == 'T' .OR. C_trans == 't' .OR. &
2011 : C_trans == 'C' .OR. C_trans == 'c')) &
2012 0 : CPABORT("Unknown transpose character for array 3 (C).")
2013 :
2014 4 : CALL timeset(routineN, handle)
2015 :
2016 64 : ALLOCATE (work(n, n), source=0.0_dp)
2017 :
2018 4 : CALL DGEMM(A_trans, B_trans, n, n, n, 1.0_dp, A_in, n, B_in, n, 0.0_dp, work, n)
2019 4 : CALL DGEMM('N', C_trans, n, n, n, 1.0_dp, work, n, C_in, n, 0.0_dp, D_out, n)
2020 :
2021 4 : DEALLOCATE (work)
2022 :
2023 4 : CALL timestop(handle)
2024 :
2025 4 : END SUBROUTINE dgemm_square_3
2026 :
2027 : END MODULE mathlib
|