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