LCOV - code coverage report
Current view: top level - src - qs_ot.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:9ccafda) Lines: 96.0 % 1862 1787
Test Date: 2026-10-15 00:55:44 Functions: 100.0 % 56 56

            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 orbital transformations
      10              : !> \par History
      11              : !>      Added Taylor expansion based computation of the matrix functions (01.2004)
      12              : !>      added additional rotation variables for non-equivalent occupied orbs (08.2004)
      13              : !> \author Joost VandeVondele (06.2002)
      14              : ! **************************************************************************************************
      15              : MODULE qs_ot
      16              :    USE arnoldi_api,                     ONLY: arnoldi_extremal
      17              :    USE cp_dbcsr_api,                    ONLY: &
      18              :         dbcsr_add, dbcsr_copy, dbcsr_create, dbcsr_distribution_type, dbcsr_filter, &
      19              :         dbcsr_finalize, dbcsr_get_block_p, dbcsr_get_info, dbcsr_get_occupation, dbcsr_init_p, &
      20              :         dbcsr_iterator_blocks_left, dbcsr_iterator_next_block, dbcsr_iterator_start, &
      21              :         dbcsr_iterator_stop, dbcsr_iterator_type, dbcsr_multiply, dbcsr_release, dbcsr_release_p, &
      22              :         dbcsr_reserve_blocks, dbcsr_scale, dbcsr_set, dbcsr_transposed, dbcsr_type, &
      23              :         dbcsr_type_no_symmetry
      24              :    USE cp_dbcsr_cholesky,               ONLY: cp_dbcsr_cholesky_decompose,&
      25              :                                               cp_dbcsr_cholesky_invert,&
      26              :                                               cp_dbcsr_cholesky_restore
      27              :    USE cp_dbcsr_contrib,                ONLY: dbcsr_add_on_diag,&
      28              :                                               dbcsr_frobenius_norm,&
      29              :                                               dbcsr_gershgorin_norm,&
      30              :                                               dbcsr_hadamard_product,&
      31              :                                               dbcsr_scale_by_vector
      32              :    USE cp_dbcsr_diag,                   ONLY: cp_dbcsr_heevd,&
      33              :                                               cp_dbcsr_syevd
      34              :    USE kinds,                           ONLY: dp
      35              :    USE mathlib,                         ONLY: diag_complex,&
      36              :                                               diamat_all,&
      37              :                                               gemm_square
      38              :    USE message_passing,                 ONLY: mp_comm_type
      39              :    USE preconditioner,                  ONLY: apply_preconditioner
      40              :    USE preconditioner_types,            ONLY: preconditioner_type
      41              :    USE qs_ot_types,                     ONLY: qs_ot_type
      42              : #include "./base/base_uses.f90"
      43              : 
      44              :    IMPLICIT NONE
      45              :    PRIVATE
      46              : 
      47              :    PUBLIC  :: qs_ot_get_p
      48              :    PUBLIC  :: qs_ot_get_p_complex
      49              :    PUBLIC  :: qs_ot_get_orbitals
      50              :    PUBLIC  :: qs_ot_get_orbitals_complex
      51              :    PUBLIC  :: qs_ot_get_derivative
      52              :    PUBLIC  :: qs_ot_get_derivative_complex
      53              :    PUBLIC  :: qs_ot_prepare_complex_tangent_metric
      54              :    PUBLIC  :: qs_ot_get_orbitals_ref
      55              :    PUBLIC  :: qs_ot_get_orbitals_ref_complex
      56              :    PUBLIC  :: qs_ot_get_derivative_ref
      57              :    PUBLIC  :: qs_ot_get_derivative_ref_complex
      58              :    PUBLIC  :: qs_ot_apply_complex_frechet_dbcsr
      59              :    PUBLIC  :: qs_ot_antihermitian_spectral_norm
      60              :    PUBLIC  :: qs_ot_complex_exp_frechet_kernel
      61              :    PUBLIC  :: qs_ot_density_secant_hessian
      62              :    PUBLIC  :: qs_ot_density_secant_orbital_overlaps
      63              :    PUBLIC  :: qs_ot_density_secant_projected_hessian
      64              :    PUBLIC  :: qs_ot_density_tangent
      65              :    PUBLIC  :: qs_ot_fixed_n_energy_gradient
      66              :    PUBLIC  :: qs_ot_fixed_n_energy_hessian
      67              :    PUBLIC  :: qs_ot_fixed_n_multigroup_schur_block
      68              :    PUBLIC  :: qs_ot_fixed_n_schur_block
      69              :    PUBLIC  :: qs_ot_fixed_n_projector_frechet
      70              :    PUBLIC  :: qs_ot_fixed_n_response_mu_shift
      71              :    PUBLIC  :: qs_ot_finite_rotation_response
      72              :    PUBLIC  :: qs_ot_projected_response_update
      73              :    PUBLIC  :: qs_ot_symmetric_sr1_update
      74              :    PUBLIC  :: qs_ot_symmetric_abs_solve
      75              :    PUBLIC  :: qs_ot_generate_rotation
      76              :    PUBLIC  :: qs_ot_generate_rotation_complex
      77              :    PUBLIC  :: qs_ot_rot_mat_derivative
      78              :    PUBLIC  :: qs_ot_rot_mat_derivative_complex
      79              :    PUBLIC  :: qs_ot_new_preconditioner
      80              :    PRIVATE :: qs_ot_p2m_diag
      81              :    PRIVATE :: qs_ot_p2m_diag_complex
      82              :    PRIVATE :: qs_ot_complex_multiply
      83              :    PRIVATE :: qs_ot_sinc
      84              :    PRIVATE :: qs_ot_ref_poly
      85              :    PRIVATE :: qs_ot_ref_chol
      86              :    PRIVATE :: qs_ot_ref_lwdn
      87              :    PRIVATE :: qs_ot_ref_decide
      88              :    PRIVATE :: qs_ot_ref_update
      89              :    PRIVATE :: qs_ot_refine
      90              :    PRIVATE :: qs_ot_on_the_fly_localize
      91              : 
      92              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_ot'
      93              : 
      94              : CONTAINS
      95              : 
      96              : ! **************************************************************************************************
      97              : !> \brief spectral norm of a dense anti-Hermitian rotation generator
      98              : !> \param rotation_generator anti-Hermitian generator
      99              : !> \return largest absolute eigenvalue of i times the generator
     100              : ! **************************************************************************************************
     101          152 :    FUNCTION qs_ot_antihermitian_spectral_norm(rotation_generator) RESULT(norm)
     102              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: rotation_generator
     103              :       REAL(KIND=dp)                                      :: norm
     104              : 
     105          152 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :)     :: eigenvectors
     106              :       INTEGER                                            :: n
     107          152 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenvalues
     108              : 
     109          152 :       n = SIZE(rotation_generator, 1)
     110          152 :       CPASSERT(SIZE(rotation_generator, 2) == n)
     111          152 :       norm = 0.0_dp
     112          152 :       IF (n == 0) RETURN
     113          912 :       ALLOCATE (eigenvectors(n, n), eigenvalues(n))
     114              :       CALL diag_complex(CMPLX(0.0_dp, 1.0_dp, KIND=dp)*rotation_generator, &
     115         6584 :                         eigenvectors, eigenvalues)
     116         1028 :       norm = MAXVAL(ABS(eigenvalues))
     117          152 :       DEALLOCATE (eigenvalues, eigenvectors)
     118              : 
     119          152 :    END FUNCTION qs_ot_antihermitian_spectral_norm
     120              : 
     121              : ! **************************************************************************************************
     122              : !> \brief chemical-potential response for one fixed-electron-number group
     123              : !> \param weighted_energy_response sum_i chi_i de_i over the perturbed local channels
     124              : !> \param local_curvature_sum local sum_i chi_i, used as a serial fallback
     125              : !> \param fixed_n_curvature_sum global sum_i chi_i for the complete fixed-N group
     126              : !> \return first-order chemical-potential shift
     127              : ! **************************************************************************************************
     128            2 :    PURE FUNCTION qs_ot_fixed_n_response_mu_shift( &
     129              :       weighted_energy_response, local_curvature_sum, fixed_n_curvature_sum) RESULT(mu_shift)
     130              :       REAL(KIND=dp), INTENT(IN)                          :: weighted_energy_response, &
     131              :                                                             local_curvature_sum, &
     132              :                                                             fixed_n_curvature_sum
     133              :       REAL(KIND=dp)                                      :: mu_shift
     134              : 
     135              :       REAL(KIND=dp)                                      :: denominator
     136              : 
     137            2 :       denominator = fixed_n_curvature_sum
     138            2 :       IF (ABS(denominator) <= EPSILON(denominator)) denominator = local_curvature_sum
     139            2 :       mu_shift = 0.0_dp
     140            2 :       IF (ABS(denominator) > EPSILON(denominator)) mu_shift = weighted_energy_response/denominator
     141              : 
     142            2 :    END FUNCTION qs_ot_fixed_n_response_mu_shift
     143              : 
     144              : ! **************************************************************************************************
     145              : !> \brief fixed-N Mermin gradient in auxiliary-energy coordinates
     146              : !> \param rayleigh_energy diagonal expectation values of the current Hamiltonian
     147              : !> \param energy_coordinate auxiliary band energies controlling the occupations
     148              : !> \param response_weight signed weighted occupation responses chi_i
     149              : !> \param fixed_n_weight_sum global sum_i chi_i for the fixed-N group
     150              : !> \param fixed_n_weighted_residual global sum_i chi_i (h_i-e_i)
     151              : !> \param gradient projected fixed-N gradient
     152              : ! **************************************************************************************************
     153         2038 :    PURE SUBROUTINE qs_ot_fixed_n_energy_gradient( &
     154         2038 :       rayleigh_energy, energy_coordinate, response_weight, fixed_n_weight_sum, &
     155         2038 :       fixed_n_weighted_residual, gradient)
     156              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: rayleigh_energy, energy_coordinate, &
     157              :                                                             response_weight
     158              :       REAL(KIND=dp), INTENT(IN)                          :: fixed_n_weight_sum, &
     159              :                                                             fixed_n_weighted_residual
     160              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: gradient
     161              : 
     162              :       REAL(KIND=dp)                                      :: fixed_n_mean
     163              : 
     164         2038 :       fixed_n_mean = 0.0_dp
     165         2038 :       IF (ABS(fixed_n_weight_sum) > EPSILON(fixed_n_weight_sum)) THEN
     166         1994 :          fixed_n_mean = fixed_n_weighted_residual/fixed_n_weight_sum
     167              :       END IF
     168              :       gradient(:) = response_weight(:)* &
     169        16136 :                     (fixed_n_mean - (rayleigh_energy(:) - energy_coordinate(:)))
     170              : 
     171         2038 :    END SUBROUTINE qs_ot_fixed_n_energy_gradient
     172              : 
     173              : ! **************************************************************************************************
     174              : !> \brief dense fixed-N occupation Hessian in auxiliary-energy coordinates
     175              : !>
     176              : !>        H = diag(chi) - chi chi^T / sum(chi) is symmetric and has the constant-energy gauge as
     177              : !>        an exact null vector. It can be indefinite for non-monotone smearing distributions.
     178              : !> \param response_weight signed weighted occupation responses chi_i
     179              : !> \param fixed_n_weight_sum global sum_i chi_i for the fixed-N group
     180              : !> \param hessian projected fixed-N Hessian
     181              : ! **************************************************************************************************
     182           16 :    PURE SUBROUTINE qs_ot_fixed_n_energy_hessian(response_weight, fixed_n_weight_sum, hessian)
     183              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: response_weight
     184              :       REAL(KIND=dp), INTENT(IN)                          :: fixed_n_weight_sum
     185              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: hessian
     186              : 
     187              :       INTEGER                                            :: i, j, n
     188              : 
     189           16 :       n = SIZE(response_weight)
     190         1008 :       hessian(:, :) = 0.0_dp
     191          122 :       DO i = 1, n
     192          122 :          hessian(i, i) = response_weight(i)
     193              :       END DO
     194           16 :       IF (ABS(fixed_n_weight_sum) > EPSILON(fixed_n_weight_sum)) THEN
     195          122 :          DO j = 1, n
     196         1008 :             DO i = 1, n
     197              :                hessian(i, j) = hessian(i, j) - &
     198          992 :                                response_weight(i)*response_weight(j)/fixed_n_weight_sum
     199              :             END DO
     200              :          END DO
     201              :       END IF
     202         2016 :       hessian(:, :) = 0.5_dp*(hessian + TRANSPOSE(hessian))
     203              : 
     204           16 :    END SUBROUTINE qs_ot_fixed_n_energy_hessian
     205              : 
     206              : ! **************************************************************************************************
     207              : !> \brief local block of the fixed-N rotation/energy Schur complement
     208              : !>
     209              : !>        For C = D - chi chi^T / sum(chi), elimination of the auxiliary-energy block gives
     210              : !>
     211              : !>          S = A - R^T C R
     212              : !>            = (A - R^T D R) + v v^T / sum(chi),  v = R^T chi.
     213              : !>
     214              : !>        This routine builds the channel-local terms. The final rank-one term is deliberately left
     215              : !>        separate so spin/k-point channels can be coupled without assembling a global dense
     216              : !>        rotation Hessian.
     217              : !> \param rotation_hessian fixed-occupation rotation Hessian A
     218              : !> \param rayleigh_response derivative R of the Rayleigh energies with respect to rotations
     219              : !> \param response_weight local signed occupation responses chi
     220              : !> \param rotation_gradient physical rotation gradient
     221              : !> \param energy_gradient physical auxiliary-energy gradient
     222              : !> \param schur_block local block A - R^T D R
     223              : !> \param coupling_vector local part of v = R^T chi
     224              : !> \param schur_rhs local right-hand side g_x + R^T g_e
     225              : ! **************************************************************************************************
     226            2 :    SUBROUTINE qs_ot_fixed_n_schur_block( &
     227            4 :       rotation_hessian, rayleigh_response, response_weight, rotation_gradient, energy_gradient, &
     228            4 :       schur_block, coupling_vector, schur_rhs)
     229              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: rotation_hessian, rayleigh_response
     230              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: response_weight, rotation_gradient, &
     231              :                                                             energy_gradient
     232              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: schur_block
     233              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: coupling_vector, schur_rhs
     234              : 
     235              :       INTEGER                                            :: nenergy
     236              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: response_group
     237              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: coupling_matrix
     238              : 
     239            2 :       nenergy = SIZE(response_weight)
     240           10 :       ALLOCATE (response_group(nenergy), coupling_matrix(SIZE(coupling_vector), 1))
     241            8 :       response_group(:) = 1
     242              :       CALL qs_ot_fixed_n_multigroup_schur_block( &
     243              :          rotation_hessian, rayleigh_response, response_weight, response_group, &
     244            2 :          rotation_gradient, energy_gradient, schur_block, coupling_matrix, schur_rhs)
     245           14 :       coupling_vector(:) = coupling_matrix(:, 1)
     246            2 :       DEALLOCATE (response_group, coupling_matrix)
     247              : 
     248            2 :    END SUBROUTINE qs_ot_fixed_n_schur_block
     249              : 
     250              : ! **************************************************************************************************
     251              : !> \brief eliminate spin-resolved auxiliary energies while retaining every fixed-N constraint
     252              : !>
     253              : !>        Each energy variable belongs to one particle-number group. The diagonal occupation
     254              : !>        response is eliminated locally, while one coupling column per group is retained for the
     255              : !>        subsequent global chemical-potential projection. A single group reduces exactly to
     256              : !>        qs_ot_fixed_n_schur_block.
     257              : !> \param rotation_hessian finite orbital-rotation Hessian
     258              : !> \param rayleigh_response derivative of all spin-resolved Rayleigh energies
     259              : !> \param response_weight positive occupation-response weights
     260              : !> \param response_group fixed-N group of every energy variable
     261              : !> \param rotation_gradient orbital-rotation gradient
     262              : !> \param energy_gradient auxiliary-energy gradient
     263              : !> \param schur_block energy-eliminated rotation block
     264              : !> \param coupling_matrix retained chemical-potential coupling columns
     265              : !> \param schur_rhs energy-eliminated rotation right-hand side
     266              : ! **************************************************************************************************
     267          324 :    SUBROUTINE qs_ot_fixed_n_multigroup_schur_block( &
     268          648 :       rotation_hessian, rayleigh_response, response_weight, response_group, &
     269          648 :       rotation_gradient, energy_gradient, schur_block, coupling_matrix, schur_rhs)
     270              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: rotation_hessian, rayleigh_response
     271              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: response_weight
     272              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: response_group
     273              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: rotation_gradient, energy_gradient
     274              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: schur_block, coupling_matrix
     275              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: schur_rhs
     276              : 
     277              :       INTEGER                                            :: group, i, nenergy, ngroups, nrotation
     278              :       REAL(KIND=dp)                                      :: weight
     279              : 
     280          324 :       nenergy = SIZE(response_weight)
     281          324 :       nrotation = SIZE(rotation_gradient)
     282          324 :       ngroups = SIZE(coupling_matrix, 2)
     283          324 :       CPASSERT(ngroups > 0)
     284          972 :       CPASSERT(ALL(SHAPE(rotation_hessian) == [nrotation, nrotation]))
     285          972 :       CPASSERT(ALL(SHAPE(rayleigh_response) == [nenergy, nrotation]))
     286          324 :       CPASSERT(SIZE(energy_gradient) == nenergy)
     287          324 :       CPASSERT(SIZE(response_group) == nenergy)
     288         2242 :       CPASSERT(ALL(response_group >= 1 .AND. response_group <= ngroups))
     289          972 :       CPASSERT(ALL(SHAPE(schur_block) == [nrotation, nrotation]))
     290          324 :       CPASSERT(SIZE(coupling_matrix, 1) == nrotation)
     291          324 :       CPASSERT(SIZE(schur_rhs) == nrotation)
     292              : 
     293       502992 :       schur_block(:, :) = rotation_hessian(:, :)
     294        11782 :       coupling_matrix(:, :) = 0.0_dp
     295         2242 :       DO i = 1, nenergy
     296         1918 :          weight = response_weight(i)
     297              :          schur_block(:, :) = schur_block(:, :) - weight* &
     298              :                              SPREAD(rayleigh_response(i, :), DIM=2, NCOPIES=nrotation)* &
     299      5484538 :                              SPREAD(rayleigh_response(i, :), DIM=1, NCOPIES=nrotation)
     300         1918 :          group = response_group(i)
     301              :          coupling_matrix(:, group) = coupling_matrix(:, group) + &
     302        84766 :                                      weight*rayleigh_response(i, :)
     303              :       END DO
     304      1005984 :       schur_block(:, :) = 0.5_dp*(schur_block + TRANSPOSE(schur_block))
     305              :       schur_rhs(:) = rotation_gradient + &
     306        10122 :                      MATMUL(TRANSPOSE(rayleigh_response), energy_gradient)
     307              : 
     308          324 :    END SUBROUTINE qs_ot_fixed_n_multigroup_schur_block
     309              : 
     310              : ! **************************************************************************************************
     311              : !> \brief apply a positive spectral inverse of a real symmetric response matrix
     312              : !>
     313              : !>        The magnitude of every resolved eigenmode is retained, including modes with negative
     314              : !>        physical curvature. Replacing lambda by abs(lambda) gives a descent metric without the
     315              : !>        loss of response information caused by discarding the negative subspace. Unresolved
     316              : !>        null modes are projected out instead of being amplified by an artificial eigenvalue floor.
     317              : !> \param matrix real symmetric response matrix
     318              : !> \param rhs one or more right-hand sides
     319              : !> \param solution spectral-absolute inverse applied to rhs
     320              : !> \param valid whether finite input and output were obtained
     321              : !> \param relative_floor optional relative eigenvalue floor
     322              : ! **************************************************************************************************
     323          322 :    SUBROUTINE qs_ot_symmetric_abs_solve(matrix, rhs, solution, valid, relative_floor)
     324              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: matrix, rhs
     325              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: solution
     326              :       LOGICAL, INTENT(OUT)                               :: valid
     327              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: relative_floor
     328              : 
     329              :       INTEGER                                            :: i, n, nresolved
     330              :       REAL(KIND=dp)                                      :: eigenvalue_floor, relative_floor_eff, &
     331              :                                                             scale
     332          322 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenvalues
     333          322 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: eigenvectors, work
     334              : 
     335          322 :       n = SIZE(matrix, 1)
     336          966 :       CPASSERT(ALL(SHAPE(matrix) == [n, n]))
     337          322 :       CPASSERT(SIZE(rhs, 1) == n)
     338          966 :       CPASSERT(ALL(SHAPE(solution) == SHAPE(rhs)))
     339       524362 :       valid = ALL(matrix == matrix) .AND. ALL(rhs == rhs)
     340          322 :       IF (.NOT. valid) THEN
     341            0 :          solution(:, :) = 0.0_dp
     342              :          RETURN
     343              :       END IF
     344              : 
     345          322 :       relative_floor_eff = SQRT(EPSILON(1.0_dp))
     346          322 :       IF (PRESENT(relative_floor)) relative_floor_eff = MAX(relative_floor, relative_floor_eff)
     347         2898 :       ALLOCATE (eigenvalues(n), eigenvectors(n, n), work(n, SIZE(rhs, 2)))
     348       502846 :       eigenvectors(:, :) = 0.5_dp*(matrix + TRANSPOSE(matrix))
     349          322 :       CALL diamat_all(eigenvectors, eigenvalues)
     350         9778 :       scale = MAX(1.0_dp, MAXVAL(ABS(eigenvalues)))
     351          322 :       eigenvalue_floor = relative_floor_eff*scale
     352      2237422 :       work(:, :) = MATMUL(TRANSPOSE(eigenvectors), rhs)
     353          322 :       nresolved = 0
     354         9778 :       DO i = 1, n
     355         9778 :          IF (ABS(eigenvalues(i)) > eigenvalue_floor) THEN
     356        15489 :             work(i, :) = work(i, :)/ABS(eigenvalues(i))
     357         4939 :             nresolved = nresolved + 1
     358              :          ELSE
     359        14475 :             work(i, :) = 0.0_dp
     360              :          END IF
     361              :       END DO
     362      2249846 :       solution(:, :) = MATMUL(eigenvectors, work)
     363        21516 :       valid = nresolved > 0 .AND. ALL(solution == solution)
     364          610 :       IF (.NOT. valid) solution(:, :) = 0.0_dp
     365          322 :       DEALLOCATE (eigenvalues, eigenvectors, work)
     366              : 
     367          322 :    END SUBROUTINE qs_ot_symmetric_abs_solve
     368              : 
     369              : ! **************************************************************************************************
     370              : !> \brief update a baseline response direction in a small positive physical-response subspace
     371              : !>
     372              : !>        The first basis mode is the baseline response direction. With B0 the projected frozen-H
     373              : !>        Hessian and K the accepted physical correction, this routine solves
     374              : !>
     375              : !>          (B0 + K) c = g_Q.
     376              : !>
     377              : !>        The optional projected physical gradient supplies g_Q. Without it, g_Q=B0*e1, so c=e1
     378              : !>        exactly when K=0. An indefinite or unresolved total projected Hessian is rejected instead
     379              : !>        of turning its negative modes into an unrelated active direction.
     380              : !> \param reference_hessian projected frozen-H Hessian B0
     381              : !> \param response_correction projected physical response K
     382              : !> \param coefficients response coefficients c in the supplied basis
     383              : !> \param valid whether a finite, positive, sufficiently resolved solve was obtained
     384              : !> \param projected_gradient optional physical gradient projected onto the supplied basis
     385              : !> \param relative_floor optional relative positive-eigenvalue floor
     386              : ! **************************************************************************************************
     387          116 :    SUBROUTINE qs_ot_projected_response_update( &
     388          116 :       reference_hessian, response_correction, coefficients, valid, projected_gradient, relative_floor)
     389              : 
     390              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: reference_hessian, response_correction
     391              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: coefficients
     392              :       LOGICAL, INTENT(OUT)                               :: valid
     393              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN), OPTIONAL  :: projected_gradient
     394              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: relative_floor
     395              : 
     396              :       INTEGER                                            :: i, n
     397              :       REAL(KIND=dp)                                      :: eigenvalue_floor, relative_floor_eff, &
     398              :                                                             scale
     399          116 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenvalues, rhs, work
     400          116 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: eigenvectors
     401              : 
     402          116 :       n = SIZE(reference_hessian, 1)
     403          116 :       CPASSERT(n > 0)
     404          348 :       CPASSERT(ALL(SHAPE(reference_hessian) == [n, n]))
     405          348 :       CPASSERT(ALL(SHAPE(response_correction) == [n, n]))
     406          116 :       CPASSERT(SIZE(coefficients) == n)
     407          116 :       IF (PRESENT(projected_gradient)) THEN
     408          112 :          CPASSERT(SIZE(projected_gradient) == n)
     409              :       END IF
     410          348 :       coefficients(:) = 0.0_dp
     411              :       valid = ALL(reference_hessian == reference_hessian) .AND. &
     412         1624 :               ALL(response_correction == response_correction)
     413          116 :       IF (PRESENT(projected_gradient)) THEN
     414          336 :          valid = valid .AND. ALL(projected_gradient == projected_gradient)
     415              :       END IF
     416          116 :       IF (.NOT. valid) RETURN
     417              : 
     418          116 :       relative_floor_eff = 1.0E-4_dp
     419          116 :       IF (PRESENT(relative_floor)) relative_floor_eff = &
     420            0 :          MAX(relative_floor, SQRT(EPSILON(1.0_dp)))
     421          928 :       ALLOCATE (eigenvalues(n), eigenvectors(n, n), rhs(n), work(n))
     422              :       eigenvectors(:, :) = 0.5_dp* &
     423              :                            (reference_hessian + response_correction + &
     424          812 :                             TRANSPOSE(reference_hessian + response_correction))
     425          116 :       CALL diamat_all(eigenvectors, eigenvalues)
     426         1044 :       scale = MAX(MAXVAL(ABS(eigenvalues)), MAXVAL(ABS(reference_hessian)))
     427          116 :       IF (scale <= TINY(scale)) THEN
     428            0 :          valid = .FALSE.
     429            0 :          coefficients(:) = 0.0_dp
     430            0 :          DEALLOCATE (eigenvalues, eigenvectors, rhs, work)
     431            0 :          RETURN
     432              :       END IF
     433          116 :       eigenvalue_floor = relative_floor_eff*scale
     434          344 :       valid = ALL(eigenvalues > eigenvalue_floor)
     435          116 :       IF (valid) THEN
     436          342 :          rhs(:) = reference_hessian(:, 1)
     437          338 :          IF (PRESENT(projected_gradient)) rhs(:) = projected_gradient
     438          114 :          work(:) = MATMUL(TRANSPOSE(eigenvectors), rhs)
     439          342 :          DO i = 1, n
     440          342 :             work(i) = work(i)/eigenvalues(i)
     441              :          END DO
     442         1482 :          coefficients(:) = MATMUL(eigenvectors, work)
     443              :          valid = ALL(coefficients == coefficients) .AND. &
     444          684 :                  ALL(ABS(coefficients) <= HUGE(1.0_dp))
     445              :       END IF
     446          120 :       IF (.NOT. valid) coefficients(:) = 0.0_dp
     447          116 :       DEALLOCATE (eigenvalues, eigenvectors, rhs, work)
     448              : 
     449          116 :    END SUBROUTINE qs_ot_projected_response_update
     450              : 
     451              : ! **************************************************************************************************
     452              : !> \brief add one accepted symmetric response secant to a reference Hessian
     453              : !>
     454              : !>        With r=y-B0*s, the symmetric-rank-one update B=B0+r*r^T/(r^T*s) satisfies B*s=y
     455              : !>        exactly.  The signed denominator is retained because a self-consistent Hxc response can
     456              : !>        be indefinite.  Nearly orthogonal residuals are rejected instead of manufacturing a
     457              : !>        large unresolved mode.
     458              : !> \param matrix reference symmetric Hessian B0
     459              : !> \param step accepted displacement s
     460              : !> \param response measured gradient response y
     461              : !> \param updated_matrix symmetric secant Hessian B
     462              : !> \param valid whether a resolved finite update was constructed
     463              : !> \param relative_tolerance optional SR1 denominator acceptance threshold
     464              : ! **************************************************************************************************
     465            4 :    PURE SUBROUTINE qs_ot_symmetric_sr1_update( &
     466            4 :       matrix, step, response, updated_matrix, valid, relative_tolerance)
     467              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: matrix
     468              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: step, response
     469              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: updated_matrix
     470              :       LOGICAL, INTENT(OUT)                               :: valid
     471              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: relative_tolerance
     472              : 
     473              :       INTEGER                                            :: n
     474              :       REAL(KIND=dp)                                      :: denominator, residual_norm, step_norm, &
     475              :                                                             threshold, tolerance
     476            2 :       REAL(KIND=dp), DIMENSION(SIZE(step))               :: residual
     477              : 
     478            4 :       n = SIZE(step)
     479           52 :       updated_matrix(:, :) = 0.0_dp
     480              :       valid = SIZE(response) == n .AND. ALL(SHAPE(matrix) == [n, n]) .AND. &
     481           20 :               ALL(SHAPE(updated_matrix) == [n, n])
     482            4 :       IF (.NOT. valid) RETURN
     483           84 :       valid = ALL(matrix == matrix) .AND. ALL(step == step) .AND. ALL(response == response)
     484            4 :       IF (.NOT. valid) RETURN
     485              : 
     486           52 :       updated_matrix(:, :) = 0.5_dp*(matrix + TRANSPOSE(matrix))
     487           76 :       residual(:) = response - MATMUL(updated_matrix, step)
     488           16 :       denominator = DOT_PRODUCT(residual, step)
     489           16 :       residual_norm = SQRT(MAX(0.0_dp, DOT_PRODUCT(residual, residual)))
     490           16 :       step_norm = SQRT(MAX(0.0_dp, DOT_PRODUCT(step, step)))
     491            4 :       tolerance = SQRT(EPSILON(1.0_dp))
     492            4 :       IF (PRESENT(relative_tolerance)) tolerance = MAX(tolerance, relative_tolerance)
     493            4 :       threshold = tolerance*residual_norm*step_norm
     494              :       valid = residual_norm > TINY(residual_norm) .AND. step_norm > TINY(step_norm) .AND. &
     495            4 :               ABS(denominator) > threshold
     496            4 :       IF (.NOT. valid) RETURN
     497              : 
     498              :       updated_matrix(:, :) = updated_matrix + &
     499           26 :                              SPREAD(residual, DIM=2, NCOPIES=n)*SPREAD(residual, DIM=1, NCOPIES=n)/denominator
     500           50 :       updated_matrix(:, :) = 0.5_dp*(updated_matrix + TRANSPOSE(updated_matrix))
     501           26 :       valid = ALL(updated_matrix == updated_matrix)
     502              : 
     503              :    END SUBROUTINE qs_ot_symmetric_sr1_update
     504              : 
     505              : ! **************************************************************************************************
     506              : !> \brief project a self-adjoint density/Hamiltonian secant onto density-response modes
     507              : !>
     508              : !>        For an accepted Hermitian density change S and the corresponding self-consistent
     509              : !>        Hamiltonian change Y, the minimum-Frobenius-norm self-adjoint response satisfying
     510              : !>        K*S=Y is
     511              : !>
     512              : !>          K = (Y<S,.> + S<Y,.>)/<S,S> - <S,Y>S<S,.>/<S,S>**2.
     513              : !>
     514              : !>        The returned matrix is <B_q,K*B_r> for the supplied Hermitian density modes B_r.  Its
     515              : !>        density-space construction is invariant under a common complex similarity transform.
     516              : !> \param density_step accepted density-matrix change S
     517              : !> \param hamiltonian_step accepted self-consistent Hamiltonian change Y
     518              : !> \param density_modes density derivatives B_r of the coupled minimizer variables
     519              : !> \param correction projected symmetric Hxc response
     520              : !> \param valid whether a finite nonzero density secant was available
     521              : !> \param density_norm_sq optional <S,S>
     522              : !> \param response_work optional <S,Y>
     523              : ! **************************************************************************************************
     524           10 :    SUBROUTINE qs_ot_density_secant_hessian( &
     525           10 :       density_step, hamiltonian_step, density_modes, correction, valid, &
     526              :       density_norm_sq, response_work)
     527              : 
     528              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: density_step, hamiltonian_step
     529              :       COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(IN)   :: density_modes
     530              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: correction
     531              :       LOGICAL, INTENT(OUT)                               :: valid
     532              :       REAL(KIND=dp), INTENT(OUT), OPTIONAL               :: density_norm_sq, response_work
     533              : 
     534              :       INTEGER                                            :: i, j, mode, n, nmode
     535              :       REAL(KIND=dp)                                      :: density_norm, density_response_work, &
     536              :                                                             scale
     537           10 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: density_overlap, response_overlap
     538              : 
     539           10 :       n = SIZE(density_step, 1)
     540           10 :       nmode = SIZE(density_modes, 3)
     541           10 :       CPASSERT(n > 0)
     542           10 :       CPASSERT(SIZE(density_step, 2) == n)
     543           30 :       CPASSERT(ALL(SHAPE(hamiltonian_step) == [n, n]))
     544           10 :       CPASSERT(SIZE(density_modes, 1) == n)
     545           10 :       CPASSERT(SIZE(density_modes, 2) == n)
     546           30 :       CPASSERT(ALL(SHAPE(correction) == [nmode, nmode]))
     547              : 
     548           70 :       correction(:, :) = 0.0_dp
     549           10 :       valid = .FALSE.
     550           10 :       density_norm = 0.0_dp
     551           10 :       density_response_work = 0.0_dp
     552           38 :       DO j = 1, n
     553          122 :          DO i = 1, n
     554              :             density_norm = density_norm + &
     555           84 :                            REAL(CONJG(density_step(i, j))*density_step(i, j), KIND=dp)
     556              :             density_response_work = density_response_work + &
     557          112 :                                     REAL(CONJG(density_step(i, j))*hamiltonian_step(i, j), KIND=dp)
     558              :          END DO
     559              :       END DO
     560           10 :       IF (PRESENT(density_norm_sq)) density_norm_sq = density_norm
     561           10 :       IF (PRESENT(response_work)) response_work = density_response_work
     562          122 :       scale = MAXVAL(ABS(density_step))
     563           10 :       IF (scale <= TINY(1.0_dp)) RETURN
     564           10 :       IF (density_norm <= 64.0_dp*EPSILON(1.0_dp)*scale*scale .OR. nmode <= 0) RETURN
     565              : 
     566           40 :       ALLOCATE (density_overlap(nmode), response_overlap(nmode))
     567           30 :       DO mode = 1, nmode
     568          244 :          density_overlap(mode) = SUM(REAL(CONJG(density_step)*density_modes(:, :, mode), KIND=dp))
     569          254 :          response_overlap(mode) = SUM(REAL(CONJG(hamiltonian_step)*density_modes(:, :, mode), KIND=dp))
     570              :       END DO
     571              :       CALL qs_ot_density_secant_projected_hessian( &
     572           10 :          density_norm, density_response_work, density_overlap, response_overlap, correction, valid)
     573           10 :       DEALLOCATE (density_overlap, response_overlap)
     574              : 
     575              :    END SUBROUTINE qs_ot_density_secant_hessian
     576              : 
     577              : ! **************************************************************************************************
     578              : !> \brief form a projected self-adjoint Hxc response from distributed density-space overlaps
     579              : !> \param density_norm_sq <S,S>
     580              : !> \param response_work <S,Y>
     581              : !> \param density_overlap <S,B_r>
     582              : !> \param response_overlap <Y,B_r>
     583              : !> \param correction projected symmetric Hxc response <B_q,K*B_r>
     584              : !> \param valid whether finite nonzero secant data were available
     585              : !> \param secant_mode optional mode representing the accepted full density secant divided by its
     586              : !>        line-search position
     587              : !> \param secant_position signed line-search position of the accepted full density secant
     588              : ! **************************************************************************************************
     589          172 :    SUBROUTINE qs_ot_density_secant_projected_hessian( &
     590          172 :       density_norm_sq, response_work, density_overlap, response_overlap, correction, valid, &
     591              :       secant_mode, secant_position)
     592              : 
     593              :       REAL(KIND=dp), INTENT(IN)                          :: density_norm_sq, response_work
     594              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: density_overlap, response_overlap
     595              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: correction
     596              :       LOGICAL, INTENT(OUT)                               :: valid
     597              :       INTEGER, INTENT(IN), OPTIONAL                      :: secant_mode
     598              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: secant_position
     599              : 
     600              :       INTEGER                                            :: i, j, nmode
     601              :       REAL(KIND=dp)                                      :: inverse_density_norm
     602          344 :       REAL(KIND=dp), DIMENSION(SIZE(density_overlap))    :: projected_density_overlap, &
     603          172 :                                                             projected_response_overlap
     604              : 
     605          172 :       nmode = SIZE(density_overlap)
     606          172 :       CPASSERT(SIZE(response_overlap) == nmode)
     607          516 :       CPASSERT(ALL(SHAPE(correction) == [nmode, nmode]))
     608              : 
     609         1204 :       correction(:, :) = 0.0_dp
     610          172 :       valid = .FALSE.
     611          172 :       IF (nmode <= 0 .OR. density_norm_sq <= TINY(1.0_dp)) RETURN
     612          172 :       IF (density_norm_sq /= density_norm_sq .OR. response_work /= response_work) RETURN
     613          172 :       IF (ABS(density_norm_sq) > HUGE(1.0_dp) .OR. ABS(response_work) > HUGE(1.0_dp)) RETURN
     614         1032 :       IF (ANY(density_overlap /= density_overlap) .OR. ANY(response_overlap /= response_overlap)) RETURN
     615         1032 :       IF (ANY(ABS(density_overlap) > HUGE(1.0_dp)) .OR. &
     616              :           ANY(ABS(response_overlap) > HUGE(1.0_dp))) RETURN
     617          172 :       IF (PRESENT(secant_mode) .NEQV. PRESENT(secant_position)) RETURN
     618              : 
     619          516 :       projected_density_overlap(:) = density_overlap
     620          516 :       projected_response_overlap(:) = response_overlap
     621          172 :       IF (PRESENT(secant_mode)) THEN
     622          114 :          IF (secant_mode < 1 .OR. secant_mode > nmode) RETURN
     623              :          IF (secant_position /= secant_position .OR. &
     624          114 :              ABS(secant_position) > HUGE(1.0_dp) .OR. &
     625          114 :              ABS(secant_position) <= SQRT(EPSILON(1.0_dp))) RETURN
     626              :          ! The complete accepted density change can contain REF-orbital motion that is absent from
     627              :          ! a reduced rotation/occupation tangent.  S/alpha supplies its exact projected secant mode.
     628          112 :          projected_density_overlap(secant_mode) = density_norm_sq/secant_position
     629          112 :          projected_response_overlap(secant_mode) = response_work/secant_position
     630              :       END IF
     631              : 
     632          170 :       inverse_density_norm = 1.0_dp/density_norm_sq
     633          510 :       DO j = 1, nmode
     634         1190 :          DO i = 1, nmode
     635              :             correction(i, j) = inverse_density_norm* &
     636              :                                (projected_response_overlap(i)*projected_density_overlap(j) + &
     637              :                                 projected_density_overlap(i)*projected_response_overlap(j) - &
     638              :                                 response_work*inverse_density_norm* &
     639         1020 :                                 projected_density_overlap(i)*projected_density_overlap(j))
     640              :          END DO
     641              :       END DO
     642         2210 :       correction(:, :) = 0.5_dp*(correction + TRANSPOSE(correction))
     643         2380 :       valid = ALL(correction == correction) .AND. ALL(ABS(correction) <= HUGE(1.0_dp))
     644          170 :       IF (.NOT. valid) correction(:, :) = 0.0_dp
     645              : 
     646              :    END SUBROUTINE qs_ot_density_secant_projected_hessian
     647              : 
     648              : ! **************************************************************************************************
     649              : !> \brief finite-chart density tangent for coupled complex rotations and fixed-N occupations
     650              : !>
     651              : !>        The rotation contribution differentiates
     652              : !>
     653              : !>          exp(X) diag(w_k f) exp(X)^H
     654              : !>
     655              : !>        along an anti-Hermitian packed direction.  The supplied weighted occupation response is
     656              : !>        added in the same chart, and the result is returned in the current physical orbital basis.
     657              : !> \param rotation_generator current anti-Hermitian REF generator X
     658              : !> \param occupation current occupations f
     659              : !> \param kpoint_weight irreducible K-point weight w_k
     660              : !> \param rotation_step interleaved real/imaginary anti-Hermitian direction
     661              : !> \param weighted_occupation_step derivative of w_k*f, including the fixed-N mu response
     662              : !> \param density_tangent Hermitian tangent in the current physical orbital basis
     663              : !> \param difference_step optional finite-chart central-difference step
     664              : ! **************************************************************************************************
     665          444 :    SUBROUTINE qs_ot_density_tangent( &
     666          444 :       rotation_generator, occupation, kpoint_weight, rotation_step, &
     667          444 :       weighted_occupation_step, density_tangent, difference_step)
     668              : 
     669              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: rotation_generator
     670              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: occupation
     671              :       REAL(KIND=dp), INTENT(IN)                          :: kpoint_weight
     672              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: rotation_step, weighted_occupation_step
     673              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT)     :: density_tangent
     674              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: difference_step
     675              : 
     676          444 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: direction, generator_minus, &
     677          444 :          generator_plus, mode_ref, rotation, rotation_minus, rotation_plus, weighted_rotation
     678              :       INTEGER                                            :: i, j, n, nrotation, r
     679              :       REAL(KIND=dp)                                      :: step, step_scale
     680              : 
     681          444 :       n = SIZE(rotation_generator, 1)
     682          444 :       nrotation = n*(n - 1)
     683          444 :       CPASSERT(n > 0)
     684         1332 :       CPASSERT(ALL(SHAPE(rotation_generator) == [n, n]))
     685          444 :       CPASSERT(SIZE(occupation) == n)
     686          444 :       CPASSERT(SIZE(rotation_step) == nrotation)
     687          444 :       CPASSERT(SIZE(weighted_occupation_step) == n)
     688         1332 :       CPASSERT(ALL(SHAPE(density_tangent) == [n, n]))
     689          444 :       CPASSERT(kpoint_weight > 0.0_dp)
     690              : 
     691              :       ALLOCATE (direction(n, n), generator_minus(n, n), generator_plus(n, n), &
     692              :                 mode_ref(n, n), rotation(n, n), rotation_minus(n, n), &
     693         7992 :                 rotation_plus(n, n), weighted_rotation(n, n))
     694          444 :       direction(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     695          444 :       r = 0
     696         2870 :       DO i = 1, n - 1
     697        11726 :          DO j = i + 1, n
     698         8856 :             r = r + 1
     699         8856 :             direction(i, j) = CMPLX(rotation_step(r), 0.0_dp, KIND=dp)
     700         8856 :             direction(j, i) = -direction(i, j)
     701         8856 :             r = r + 1
     702              :             direction(i, j) = direction(i, j) + &
     703         8856 :                               CMPLX(0.0_dp, rotation_step(r), KIND=dp)
     704              :             direction(j, i) = direction(j, i) + &
     705        11282 :                               CMPLX(0.0_dp, rotation_step(r), KIND=dp)
     706              :          END DO
     707              :       END DO
     708          444 :       CPASSERT(r == nrotation)
     709              : 
     710        23896 :       step_scale = MAX(1.0_dp, MAXVAL(ABS(direction)))
     711          444 :       step = 1.0E-5_dp/step_scale
     712          444 :       IF (PRESENT(difference_step)) step = difference_step/step_scale
     713          444 :       CPASSERT(step > EPSILON(step))
     714        23896 :       generator_plus(:, :) = rotation_generator + step*direction
     715        23896 :       generator_minus(:, :) = rotation_generator - step*direction
     716          444 :       CALL qs_ot_dense_rotation_state(rotation_generator, rotation)
     717          444 :       CALL qs_ot_dense_rotation_state(generator_plus, rotation_plus)
     718          444 :       CALL qs_ot_dense_rotation_state(generator_minus, rotation_minus)
     719              : 
     720        23896 :       weighted_rotation(:, :) = rotation_plus
     721         3314 :       DO j = 1, n
     722        23896 :          weighted_rotation(:, j) = kpoint_weight*occupation(j)*weighted_rotation(:, j)
     723              :       END DO
     724       185274 :       mode_ref(:, :) = MATMUL(weighted_rotation, CONJG(TRANSPOSE(rotation_plus)))
     725        23896 :       weighted_rotation(:, :) = rotation_minus
     726         3314 :       DO j = 1, n
     727        23896 :          weighted_rotation(:, j) = kpoint_weight*occupation(j)*weighted_rotation(:, j)
     728              :       END DO
     729              :       mode_ref(:, :) = (mode_ref - &
     730       210058 :                         MATMUL(weighted_rotation, CONJG(TRANSPOSE(rotation_minus))))/(2.0_dp*step)
     731              : 
     732        23896 :       weighted_rotation(:, :) = rotation
     733         3314 :       DO j = 1, n
     734        23896 :          weighted_rotation(:, j) = weighted_occupation_step(j)*weighted_rotation(:, j)
     735              :       END DO
     736       210502 :       mode_ref(:, :) = mode_ref + MATMUL(weighted_rotation, CONJG(TRANSPOSE(rotation)))
     737       556266 :       density_tangent(:, :) = MATMUL(CONJG(TRANSPOSE(rotation)), MATMUL(mode_ref, rotation))
     738        47348 :       density_tangent(:, :) = 0.5_dp*(density_tangent + CONJG(TRANSPOSE(density_tangent)))
     739              : 
     740            0 :       DEALLOCATE (direction, generator_minus, generator_plus, mode_ref, rotation, &
     741          444 :                   rotation_minus, rotation_plus, weighted_rotation)
     742              : 
     743          444 :    END SUBROUTINE qs_ot_density_tangent
     744              : 
     745              : ! **************************************************************************************************
     746              : !> \brief project a physical density/Hamiltonian secant between moving orbital subspaces
     747              : !>
     748              : !>        For separately S-orthonormal endpoint orbitals C0 and C1, O=C0^H*S*C1 retains the
     749              : !>        component of the accepted density step that leaves the old subspace.  Density modes are
     750              : !>        represented in the current C1 basis and already contain the irreducible K-point weight.
     751              : !> \param overlap_start_current cross overlap O
     752              : !> \param occupation_start occupations at the accepted start
     753              : !> \param occupation_current occupations at the accepted endpoint
     754              : !> \param hamiltonian_step_start C0^H*(H1-H0)*C0
     755              : !> \param hamiltonian_step_current C1^H*(H1-H0)*C1
     756              : !> \param density_modes current-orbital density tangents
     757              : !> \param kpoint_weight irreducible K-point weight
     758              : !> \param density_norm_sq contribution to <Delta P,Delta P>
     759              : !> \param response_work contribution to <Delta P,Delta H>
     760              : !> \param density_overlap contributions <Delta P,B_r>
     761              : !> \param response_overlap contributions <Delta H,B_r>
     762              : !> \param valid whether a finite nonzero secant was available
     763              : ! **************************************************************************************************
     764          258 :    SUBROUTINE qs_ot_density_secant_orbital_overlaps( &
     765          516 :       overlap_start_current, occupation_start, occupation_current, hamiltonian_step_start, &
     766          516 :       hamiltonian_step_current, density_modes, kpoint_weight, density_norm_sq, response_work, &
     767          258 :       density_overlap, response_overlap, valid)
     768              : 
     769              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: overlap_start_current
     770              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: occupation_start, occupation_current
     771              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: hamiltonian_step_start, &
     772              :                                                             hamiltonian_step_current
     773              :       COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(IN)   :: density_modes
     774              :       REAL(KIND=dp), INTENT(IN)                          :: kpoint_weight
     775              :       REAL(KIND=dp), INTENT(OUT)                         :: density_norm_sq, response_work
     776              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: density_overlap, response_overlap
     777              :       LOGICAL, INTENT(OUT)                               :: valid
     778              : 
     779          258 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :)     :: cross_mode
     780              :       INTEGER                                            :: i, j, mode, n, nmode
     781              :       REAL(KIND=dp)                                      :: cross_density, density_scale
     782              : 
     783          258 :       n = SIZE(occupation_start)
     784          258 :       nmode = SIZE(density_modes, 3)
     785          258 :       CPASSERT(n > 0)
     786          258 :       CPASSERT(SIZE(occupation_current) == n)
     787          774 :       CPASSERT(ALL(SHAPE(overlap_start_current) == [n, n]))
     788          774 :       CPASSERT(ALL(SHAPE(hamiltonian_step_start) == [n, n]))
     789          774 :       CPASSERT(ALL(SHAPE(hamiltonian_step_current) == [n, n]))
     790          258 :       CPASSERT(SIZE(density_modes, 1) == n .AND. SIZE(density_modes, 2) == n)
     791          258 :       CPASSERT(SIZE(density_overlap) == nmode .AND. SIZE(response_overlap) == nmode)
     792              : 
     793          258 :       density_norm_sq = 0.0_dp
     794          258 :       response_work = 0.0_dp
     795          774 :       density_overlap(:) = 0.0_dp
     796          774 :       response_overlap(:) = 0.0_dp
     797          258 :       valid = .FALSE.
     798          274 :       IF (nmode <= 0 .OR. kpoint_weight <= TINY(1.0_dp)) RETURN
     799              : 
     800              :       cross_density = 0.0_dp
     801         1912 :       DO j = 1, n
     802        13706 :          DO i = 1, n
     803              :             cross_density = cross_density + occupation_start(i)*occupation_current(j)* &
     804        13448 :                             ABS(overlap_start_current(i, j))**2
     805              :          END DO
     806              :       END DO
     807              :       density_norm_sq = kpoint_weight* &
     808              :                         (SUM(occupation_start**2) + SUM(occupation_current**2) - &
     809         3566 :                          2.0_dp*cross_density)
     810              :       response_work = 0.0_dp
     811         1912 :       DO i = 1, n
     812              :          response_work = response_work + kpoint_weight* &
     813              :                          (occupation_current(i)*REAL(hamiltonian_step_current(i, i), KIND=dp) - &
     814         1912 :                           occupation_start(i)*REAL(hamiltonian_step_start(i, i), KIND=dp))
     815              :       END DO
     816              : 
     817         1032 :       ALLOCATE (cross_mode(n, n))
     818          774 :       DO mode = 1, nmode
     819              :          ! GCC 15 miscompiles the equivalent complex array expressions with -march=znver3.
     820              :          CALL gemm_square(overlap_start_current, "N", density_modes(:, :, mode), "N", &
     821          516 :                           overlap_start_current, "C", cross_mode)
     822         3824 :          DO i = 1, n
     823              :             density_overlap(mode) = density_overlap(mode) + &
     824              :                                     occupation_current(i)* &
     825              :                                     REAL(density_modes(i, i, mode), KIND=dp) - &
     826         3824 :                                     occupation_start(i)*REAL(cross_mode(i, i), KIND=dp)
     827              :          END DO
     828              :          response_overlap(mode) = &
     829              :             SUM(REAL(hamiltonian_step_current, KIND=dp)* &
     830              :                 REAL(density_modes(:, :, mode), KIND=dp) + &
     831        27670 :                 AIMAG(hamiltonian_step_current)*AIMAG(density_modes(:, :, mode)))
     832              :       END DO
     833          258 :       DEALLOCATE (cross_mode)
     834              : 
     835              :       density_scale = kpoint_weight* &
     836         3566 :                       MAX(MAXVAL(ABS(occupation_start)), MAXVAL(ABS(occupation_current)))
     837          258 :       IF (density_scale <= TINY(1.0_dp)) RETURN
     838          258 :       IF (density_norm_sq <= 64.0_dp*EPSILON(1.0_dp)*density_scale**2) RETURN
     839              :       valid = density_norm_sq == density_norm_sq .AND. response_work == response_work .AND. &
     840              :               ABS(density_norm_sq) <= HUGE(1.0_dp) .AND. ABS(response_work) <= HUGE(1.0_dp) .AND. &
     841              :               ALL(density_overlap == density_overlap) .AND. &
     842              :               ALL(response_overlap == response_overlap) .AND. &
     843              :               ALL(ABS(density_overlap) <= HUGE(1.0_dp)) .AND. &
     844         2904 :               ALL(ABS(response_overlap) <= HUGE(1.0_dp))
     845          242 :       IF (.NOT. valid) THEN
     846            0 :          density_norm_sq = 0.0_dp
     847            0 :          response_work = 0.0_dp
     848            0 :          density_overlap(:) = 0.0_dp
     849            0 :          response_overlap(:) = 0.0_dp
     850              :       END IF
     851              : 
     852           16 :    END SUBROUTINE qs_ot_density_secant_orbital_overlaps
     853              : 
     854              : ! **************************************************************************************************
     855              : !> \brief fixed-N Frechet derivative of a smooth occupation projector
     856              : !>
     857              : !>        The spectral divided-difference kernel is invariant under rotations inside a degenerate
     858              : !>        eigenspace. Its diagonal includes the chemical-potential response of the complete fixed-N
     859              : !>        group, while off-diagonal terms describe the physical change of the spectral projector.
     860              : !> \param chc projected Hermitian Hamiltonian
     861              : !> \param dchc Hermitian Hamiltonian perturbation
     862              : !> \param occupation canonical occupations associated with the eigenvalues of chc
     863              : !> \param kpoint_weight irreducible-k-point weight
     864              : !> \param response_weight signed weighted occupation responses for this channel
     865              : !> \param fixed_n_weight_sum susceptibility summed over the complete fixed-N group
     866              : !> \param projector_derivative derivative of the weighted occupation projector
     867              : !> \param density_factor optional representation-dependent density prefactor
     868              : ! **************************************************************************************************
     869            2 :    SUBROUTINE qs_ot_fixed_n_projector_frechet( &
     870            2 :       chc, dchc, occupation, kpoint_weight, response_weight, fixed_n_weight_sum, &
     871            2 :       projector_derivative, density_factor)
     872              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: chc, dchc
     873              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: occupation
     874              :       REAL(KIND=dp), INTENT(IN)                          :: kpoint_weight
     875              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: response_weight
     876              :       REAL(KIND=dp), INTENT(IN)                          :: fixed_n_weight_sum
     877              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT)     :: projector_derivative
     878              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: density_factor
     879              : 
     880            2 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :)     :: eigenvectors, kernel, work
     881              :       INTEGER                                            :: i, j, n
     882              :       REAL(KIND=dp)                                      :: coefficient, denominator, factor, &
     883              :                                                             gap_tolerance, local_weight_sum, &
     884              :                                                             mu_numerator, mu_shift, scale
     885            2 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenvalues, weighted_occupation
     886              : 
     887            2 :       n = SIZE(chc, 1)
     888            2 :       CPASSERT(n > 0)
     889            6 :       CPASSERT(ALL(SHAPE(chc) == [n, n]))
     890            6 :       CPASSERT(ALL(SHAPE(dchc) == [n, n]))
     891            2 :       CPASSERT(SIZE(occupation) == n)
     892            2 :       CPASSERT(SIZE(response_weight) == n)
     893            6 :       CPASSERT(ALL(SHAPE(projector_derivative) == [n, n]))
     894              : 
     895            2 :       factor = 1.0_dp
     896            2 :       IF (PRESENT(density_factor)) factor = density_factor
     897           26 :       projector_derivative(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     898            2 :       IF (factor == 0.0_dp .OR. kpoint_weight <= 0.0_dp) RETURN
     899              : 
     900              :       ALLOCATE (eigenvectors(n, n), kernel(n, n), work(n, n), eigenvalues(n), &
     901           22 :                 weighted_occupation(n))
     902            2 :       CALL diag_complex(chc, eigenvectors, eigenvalues)
     903          380 :       work(:, :) = MATMUL(CONJG(TRANSPOSE(eigenvectors)), MATMUL(dchc, eigenvectors))
     904            8 :       weighted_occupation(:) = factor*kpoint_weight*occupation(:)
     905              : 
     906            8 :       local_weight_sum = SUM(response_weight(:))
     907            2 :       mu_numerator = 0.0_dp
     908            8 :       DO i = 1, n
     909              :          mu_numerator = mu_numerator + response_weight(i)* &
     910            8 :                         REAL(work(i, i), KIND=dp)
     911              :       END DO
     912              :       mu_shift = qs_ot_fixed_n_response_mu_shift(mu_numerator, local_weight_sum, &
     913            2 :                                                  fixed_n_weight_sum)
     914              : 
     915            8 :       scale = MAX(1.0_dp, MAXVAL(ABS(eigenvalues(:))))
     916            2 :       gap_tolerance = SQRT(EPSILON(1.0_dp))*scale
     917            2 :       kernel(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     918            8 :       DO j = 1, n
     919           26 :          DO i = 1, n
     920           24 :             IF (i == j) THEN
     921            6 :                coefficient = -factor*response_weight(i)
     922              :                kernel(i, i) = CMPLX(coefficient*(REAL(work(i, i), KIND=dp) - mu_shift), &
     923            6 :                                     0.0_dp, KIND=dp)
     924              :             ELSE
     925           12 :                denominator = eigenvalues(i) - eigenvalues(j)
     926           12 :                IF (ABS(denominator) > gap_tolerance) THEN
     927           12 :                   coefficient = (weighted_occupation(i) - weighted_occupation(j))/denominator
     928              :                ELSE
     929            0 :                   coefficient = -0.5_dp*factor*(response_weight(i) + response_weight(j))
     930              :                END IF
     931           12 :                kernel(i, j) = coefficient*work(i, j)
     932              :             END IF
     933              :          END DO
     934              :       END DO
     935              : 
     936            2 :       projector_derivative(:, :) = MATMUL(eigenvectors, &
     937          406 :                                           MATMUL(kernel, CONJG(TRANSPOSE(eigenvectors))))
     938              :       projector_derivative(:, :) = 0.5_dp*(projector_derivative + &
     939           50 :                                            CONJG(TRANSPOSE(projector_derivative)))
     940              : 
     941            2 :       DEALLOCATE (eigenvectors, kernel, work, eigenvalues, weighted_occupation)
     942              : 
     943            2 :    END SUBROUTINE qs_ot_fixed_n_projector_frechet
     944              : 
     945              : ! **************************************************************************************************
     946              : !> \brief finite complex REF rotation Hessian and Rayleigh-energy response
     947              : !>
     948              : !>        The current projected Hamiltonian is pulled back through the finite rotation and then
     949              : !>        differentiated in the independent real-antisymmetric and imaginary-symmetric pair
     950              : !>        coordinates. This keeps the response consistent with the exponential chart used by REF
     951              : !>        OT instead of replacing it by an infinitesimal commutator away from the chart origin.
     952              : !> \param chc current projected Hermitian Hamiltonian U^H H_ref U
     953              : !> \param rotation_generator current anti-Hermitian REF generator
     954              : !> \param occupation fixed occupations attached to the rotated columns
     955              : !> \param kpoint_weight irreducible-k-point weight
     956              : !> \param rotation_gradient gradient in interleaved real/imaginary pair coordinates
     957              : !> \param rotation_hessian derivative of rotation_gradient in the same coordinates
     958              : !> \param rayleigh_response derivative of diag(U^H H_ref U) with respect to the pair coordinates
     959              : !> \param difference_step optional central finite-difference step for the Hessian action
     960              : ! **************************************************************************************************
     961          340 :    SUBROUTINE qs_ot_finite_rotation_response( &
     962          340 :       chc, rotation_generator, occupation, kpoint_weight, rotation_gradient, &
     963          340 :       rotation_hessian, rayleigh_response, difference_step)
     964              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: chc, rotation_generator
     965              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: occupation
     966              :       REAL(KIND=dp), INTENT(IN)                          :: kpoint_weight
     967              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: rotation_gradient
     968              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: rotation_hessian, rayleigh_response
     969              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: difference_step
     970              : 
     971          340 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :)     :: base_hamiltonian, generator_minus, &
     972          340 :                                                             generator_plus, rotation
     973              :       INTEGER                                            :: i, j, n, nrotation, r, s
     974              :       REAL(KIND=dp)                                      :: step
     975          340 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: occupation_scale, rayleigh, &
     976          340 :                                                             rayleigh_minus, rayleigh_plus
     977          340 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: gradient_imag, gradient_minus_imag, &
     978          340 :          gradient_minus_real, gradient_plus_imag, gradient_plus_real, gradient_real
     979              : 
     980          340 :       n = SIZE(chc, 1)
     981          340 :       nrotation = n*(n - 1)
     982          340 :       CPASSERT(n > 0)
     983         1020 :       CPASSERT(ALL(SHAPE(chc) == [n, n]))
     984         1020 :       CPASSERT(ALL(SHAPE(rotation_generator) == [n, n]))
     985          340 :       CPASSERT(SIZE(occupation) == n)
     986          340 :       CPASSERT(SIZE(rotation_gradient) == nrotation)
     987         1020 :       CPASSERT(ALL(SHAPE(rotation_hessian) == [nrotation, nrotation]))
     988         1020 :       CPASSERT(ALL(SHAPE(rayleigh_response) == [n, nrotation]))
     989          340 :       CPASSERT(kpoint_weight > 0.0_dp)
     990              : 
     991          340 :       step = 1.0E-4_dp
     992          340 :       IF (PRESENT(difference_step)) step = difference_step
     993          340 :       CPASSERT(step > SQRT(EPSILON(step)))
     994              : 
     995              :       ALLOCATE (base_hamiltonian(n, n), generator_minus(n, n), generator_plus(n, n), &
     996              :                 rotation(n, n), occupation_scale(n), rayleigh(n), rayleigh_minus(n), &
     997              :                 rayleigh_plus(n), gradient_imag(n, n), gradient_minus_imag(n, n), &
     998              :                 gradient_minus_real(n, n), gradient_plus_imag(n, n), &
     999         9520 :                 gradient_plus_real(n, n), gradient_real(n, n))
    1000              : 
    1001          340 :       CALL qs_ot_dense_rotation_state(rotation_generator, rotation)
    1002       316140 :       base_hamiltonian(:, :) = MATMUL(rotation, MATMUL(chc, CONJG(TRANSPOSE(rotation))))
    1003         2256 :       occupation_scale(:) = 2.0_dp*kpoint_weight*occupation(:)
    1004              :       CALL qs_ot_dense_rotation_gradient(rotation_generator, base_hamiltonian, occupation_scale, &
    1005          340 :                                          gradient_real, gradient_imag, rayleigh)
    1006              : 
    1007          340 :       r = 0
    1008         1916 :       DO i = 1, n - 1
    1009         7292 :          DO j = i + 1, n
    1010         5376 :             r = r + 1
    1011         5376 :             rotation_gradient(r) = gradient_real(i, j)
    1012         5376 :             r = r + 1
    1013         6952 :             rotation_gradient(r) = gradient_imag(i, j)
    1014              :          END DO
    1015              :       END DO
    1016          340 :       CPASSERT(r == nrotation)
    1017              : 
    1018              :       s = 0
    1019         1916 :       DO i = 1, n - 1
    1020         7292 :          DO j = i + 1, n
    1021       391272 :             generator_plus(:, :) = rotation_generator(:, :)
    1022       391272 :             generator_minus(:, :) = rotation_generator(:, :)
    1023         5376 :             generator_plus(i, j) = generator_plus(i, j) + step
    1024         5376 :             generator_plus(j, i) = generator_plus(j, i) - step
    1025         5376 :             generator_minus(i, j) = generator_minus(i, j) - step
    1026         5376 :             generator_minus(j, i) = generator_minus(j, i) + step
    1027         5376 :             s = s + 1
    1028              :             CALL qs_ot_dense_rotation_gradient(generator_plus, base_hamiltonian, occupation_scale, &
    1029         5376 :                                                gradient_plus_real, gradient_plus_imag, rayleigh_plus)
    1030              :             CALL qs_ot_dense_rotation_gradient(generator_minus, base_hamiltonian, occupation_scale, &
    1031         5376 :                                                gradient_minus_real, gradient_minus_imag, rayleigh_minus)
    1032              :             CALL qs_ot_pack_rotation_response( &
    1033              :                gradient_plus_real, gradient_plus_imag, gradient_minus_real, gradient_minus_imag, &
    1034              :                rayleigh_plus, rayleigh_minus, step, rotation_hessian(:, s), &
    1035         5376 :                rayleigh_response(:, s))
    1036              : 
    1037       391272 :             generator_plus(:, :) = rotation_generator(:, :)
    1038       391272 :             generator_minus(:, :) = rotation_generator(:, :)
    1039         5376 :             generator_plus(i, j) = generator_plus(i, j) + CMPLX(0.0_dp, step, KIND=dp)
    1040         5376 :             generator_plus(j, i) = generator_plus(j, i) + CMPLX(0.0_dp, step, KIND=dp)
    1041         5376 :             generator_minus(i, j) = generator_minus(i, j) - CMPLX(0.0_dp, step, KIND=dp)
    1042         5376 :             generator_minus(j, i) = generator_minus(j, i) - CMPLX(0.0_dp, step, KIND=dp)
    1043         5376 :             s = s + 1
    1044              :             CALL qs_ot_dense_rotation_gradient(generator_plus, base_hamiltonian, occupation_scale, &
    1045         5376 :                                                gradient_plus_real, gradient_plus_imag, rayleigh_plus)
    1046              :             CALL qs_ot_dense_rotation_gradient(generator_minus, base_hamiltonian, occupation_scale, &
    1047         5376 :                                                gradient_minus_real, gradient_minus_imag, rayleigh_minus)
    1048              :             CALL qs_ot_pack_rotation_response( &
    1049              :                gradient_plus_real, gradient_plus_imag, gradient_minus_real, gradient_minus_imag, &
    1050              :                rayleigh_plus, rayleigh_minus, step, rotation_hessian(:, s), &
    1051         6952 :                rayleigh_response(:, s))
    1052              :          END DO
    1053              :       END DO
    1054          340 :       CPASSERT(s == nrotation)
    1055      1235624 :       rotation_hessian(:, :) = 0.5_dp*(rotation_hessian + TRANSPOSE(rotation_hessian))
    1056              : 
    1057            0 :       DEALLOCATE (base_hamiltonian, generator_minus, generator_plus, rotation, occupation_scale, &
    1058            0 :                   rayleigh, rayleigh_minus, rayleigh_plus, gradient_imag, gradient_minus_imag, &
    1059          340 :                   gradient_minus_real, gradient_plus_imag, gradient_plus_real, gradient_real)
    1060              : 
    1061          340 :    END SUBROUTINE qs_ot_finite_rotation_response
    1062              : 
    1063              : ! **************************************************************************************************
    1064              : !> \brief pack one finite complex rotation response column
    1065              : !> \param gradient_plus_real real gradient at the positive endpoint
    1066              : !> \param gradient_plus_imag imaginary gradient at the positive endpoint
    1067              : !> \param gradient_minus_real real gradient at the negative endpoint
    1068              : !> \param gradient_minus_imag imaginary gradient at the negative endpoint
    1069              : !> \param rayleigh_plus Rayleigh energies at the positive endpoint
    1070              : !> \param rayleigh_minus Rayleigh energies at the negative endpoint
    1071              : !> \param step central finite-difference step
    1072              : !> \param hessian_column packed rotation-Hessian column
    1073              : !> \param rayleigh_column packed Rayleigh-response column
    1074              : ! **************************************************************************************************
    1075        10752 :    SUBROUTINE qs_ot_pack_rotation_response( &
    1076        10752 :       gradient_plus_real, gradient_plus_imag, gradient_minus_real, gradient_minus_imag, &
    1077        10752 :       rayleigh_plus, rayleigh_minus, step, hessian_column, rayleigh_column)
    1078              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: gradient_plus_real, gradient_plus_imag, &
    1079              :                                                             gradient_minus_real, &
    1080              :                                                             gradient_minus_imag
    1081              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: rayleigh_plus, rayleigh_minus
    1082              :       REAL(KIND=dp), INTENT(IN)                          :: step
    1083              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: hessian_column, rayleigh_column
    1084              : 
    1085              :       INTEGER                                            :: i, j, n, r
    1086              : 
    1087        10752 :       n = SIZE(gradient_plus_real, 1)
    1088        32256 :       CPASSERT(ALL(SHAPE(gradient_plus_real) == [n, n]))
    1089        32256 :       CPASSERT(ALL(SHAPE(gradient_plus_imag) == [n, n]))
    1090        32256 :       CPASSERT(ALL(SHAPE(gradient_minus_real) == [n, n]))
    1091        32256 :       CPASSERT(ALL(SHAPE(gradient_minus_imag) == [n, n]))
    1092        10752 :       CPASSERT(SIZE(hessian_column) == n*(n - 1))
    1093        10752 :       CPASSERT(SIZE(rayleigh_column) == n)
    1094              : 
    1095        10752 :       r = 0
    1096        82536 :       DO i = 1, n - 1
    1097       385896 :          DO j = i + 1, n
    1098       303360 :             r = r + 1
    1099              :             hessian_column(r) = &
    1100       303360 :                (gradient_plus_real(i, j) - gradient_minus_real(i, j))/(2.0_dp*step)
    1101       303360 :             r = r + 1
    1102              :             hessian_column(r) = &
    1103       375144 :                (gradient_plus_imag(i, j) - gradient_minus_imag(i, j))/(2.0_dp*step)
    1104              :          END DO
    1105              :       END DO
    1106        93288 :       rayleigh_column(:) = (rayleigh_plus(:) - rayleigh_minus(:))/(2.0_dp*step)
    1107              : 
    1108        10752 :    END SUBROUTINE qs_ot_pack_rotation_response
    1109              : 
    1110              : ! **************************************************************************************************
    1111              : !> \brief dense complex rotation and projected-Hamiltonian diagonal
    1112              : !> \param rotation_generator anti-Hermitian REF generator
    1113              : !> \param base_hamiltonian fixed Hamiltonian in the unrotated REF basis
    1114              : !> \param occupation_scale twice the weighted occupations
    1115              : !> \param gradient_real real-antisymmetric gradient component
    1116              : !> \param gradient_imag imaginary-symmetric gradient component
    1117              : !> \param rayleigh diagonal of the rotated Hamiltonian
    1118              : ! **************************************************************************************************
    1119        21844 :    SUBROUTINE qs_ot_dense_rotation_gradient( &
    1120        21844 :       rotation_generator, base_hamiltonian, occupation_scale, gradient_real, gradient_imag, rayleigh)
    1121              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: rotation_generator, base_hamiltonian
    1122              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: occupation_scale
    1123              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: gradient_real, gradient_imag
    1124              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: rayleigh
    1125              : 
    1126              :       COMPLEX(KIND=dp)                                   :: kernel
    1127        21844 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :)     :: covector, eigenvectors, frechet, inner, &
    1128        21844 :                                                             outer, rotation, work
    1129              :       INTEGER                                            :: i, j, n
    1130              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenvalues
    1131              : 
    1132        21844 :       n = SIZE(rotation_generator, 1)
    1133              :       ALLOCATE (covector(n, n), eigenvectors(n, n), frechet(n, n), inner(n, n), &
    1134       393192 :                 outer(n, n), rotation(n, n), work(n, n), eigenvalues(n))
    1135              :       CALL qs_ot_dense_rotation_state(rotation_generator, rotation, &
    1136        21844 :                                       eigenvectors=eigenvectors, eigenvalues=eigenvalues)
    1137              : 
    1138     13854208 :       work(:, :) = MATMUL(base_hamiltonian, rotation)
    1139      1580012 :       covector(:, :) = work(:, :)
    1140       188832 :       DO j = 1, n
    1141      1580012 :          covector(:, j) = occupation_scale(j)*covector(:, j)
    1142              :       END DO
    1143              : 
    1144        21844 :       work(:, :) = MATMUL(CONJG(TRANSPOSE(rotation)), &
    1145     40048144 :                           MATMUL(base_hamiltonian, rotation))
    1146       188832 :       DO i = 1, n
    1147       188832 :          rayleigh(i) = REAL(work(i, i), KIND=dp)
    1148              :       END DO
    1149              : 
    1150        21844 :       inner(:, :) = MATMUL(CONJG(TRANSPOSE(eigenvectors)), &
    1151     40048144 :                            MATMUL(covector, eigenvectors))
    1152       188832 :       DO j = 1, n
    1153      1580012 :          DO i = 1, n
    1154      1391180 :             kernel = CONJG(qs_ot_complex_exp_frechet_kernel(eigenvalues(i), eigenvalues(j)))
    1155      1558168 :             outer(i, j) = inner(i, j)*kernel
    1156              :          END DO
    1157              :       END DO
    1158        21844 :       frechet(:, :) = MATMUL(eigenvectors, &
    1159     40048144 :                              MATMUL(outer, CONJG(TRANSPOSE(eigenvectors))))
    1160      1580012 :       gradient_real(:, :) = REAL(frechet, KIND=dp) - TRANSPOSE(REAL(frechet, KIND=dp))
    1161      1580012 :       gradient_imag(:, :) = AIMAG(frechet) + TRANSPOSE(AIMAG(frechet))
    1162              : 
    1163        21844 :       DEALLOCATE (covector, eigenvectors, frechet, inner, outer, rotation, work, eigenvalues)
    1164              : 
    1165        21844 :    END SUBROUTINE qs_ot_dense_rotation_gradient
    1166              : 
    1167              : ! **************************************************************************************************
    1168              : !> \brief dense exponential of an anti-Hermitian REF generator
    1169              : !> \param rotation_generator anti-Hermitian generator
    1170              : !> \param rotation exp(rotation_generator)
    1171              : !> \param eigenvectors optional eigenvectors of i*rotation_generator
    1172              : !> \param eigenvalues optional eigenvalues of i*rotation_generator
    1173              : ! **************************************************************************************************
    1174        23516 :    SUBROUTINE qs_ot_dense_rotation_state(rotation_generator, rotation, eigenvectors, eigenvalues)
    1175              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: rotation_generator
    1176              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT)     :: rotation
    1177              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT), &
    1178              :          OPTIONAL                                        :: eigenvectors
    1179              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT), OPTIONAL :: eigenvalues
    1180              : 
    1181              :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :)     :: vectors, weighted_vectors
    1182              :       INTEGER                                            :: j, n
    1183              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: values
    1184              : 
    1185        23516 :       n = SIZE(rotation_generator, 1)
    1186       188128 :       ALLOCATE (vectors(n, n), weighted_vectors(n, n), values(n))
    1187              :       CALL diag_complex(CMPLX(0.0_dp, 1.0_dp, KIND=dp)*rotation_generator, &
    1188      1666624 :                         vectors, values)
    1189      1666624 :       weighted_vectors(:, :) = vectors(:, :)
    1190       201030 :       DO j = 1, n
    1191              :          weighted_vectors(:, j) = EXP(CMPLX(0.0_dp, -values(j), KIND=dp))* &
    1192      1666624 :                                   weighted_vectors(:, j)
    1193              :       END DO
    1194     29016800 :       rotation(:, :) = MATMUL(weighted_vectors, CONJG(TRANSPOSE(vectors)))
    1195      1581684 :       IF (PRESENT(eigenvectors)) eigenvectors(:, :) = vectors(:, :)
    1196       190504 :       IF (PRESENT(eigenvalues)) eigenvalues(:) = values(:)
    1197        23516 :       DEALLOCATE (vectors, weighted_vectors, values)
    1198              : 
    1199        23516 :    END SUBROUTINE qs_ot_dense_rotation_state
    1200              : 
    1201              : ! **************************************************************************************************
    1202              : !> \brief Frechet divided-difference kernel for exp(-i*evals)
    1203              : !> \param e1 ...
    1204              : !> \param e2 ...
    1205              : !> \return ...
    1206              : ! **************************************************************************************************
    1207      1503434 :    PURE FUNCTION qs_ot_complex_exp_frechet_kernel(e1, e2) RESULT(kernel)
    1208              :       REAL(KIND=dp), INTENT(IN)                          :: e1, e2
    1209              :       COMPLEX(KIND=dp)                                   :: kernel
    1210              : 
    1211              :       COMPLEX(KIND=dp)                                   :: l1, l2, x
    1212              :       INTEGER                                            :: i
    1213              : 
    1214      1503434 :       l1 = (0.0_dp, -1.0_dp)*e1
    1215      1503434 :       l2 = (0.0_dp, -1.0_dp)*e2
    1216      1503434 :       IF (ABS(l1 - l2) > 0.5_dp) THEN
    1217         8584 :          kernel = (EXP(l1) - EXP(l2))/(l1 - l2)
    1218              :       ELSE
    1219              :          x = 1.0_dp
    1220              :          kernel = 0.0_dp
    1221     25412450 :          DO i = 1, 16
    1222     23917600 :             kernel = kernel + x
    1223     25412450 :             x = x*(l1 - l2)/REAL(i + 1, KIND=dp)
    1224              :          END DO
    1225      1494850 :          kernel = kernel*EXP(l2)
    1226              :       END IF
    1227              : 
    1228      1503434 :    END FUNCTION qs_ot_complex_exp_frechet_kernel
    1229              : 
    1230              : ! **************************************************************************************************
    1231              : !> \brief apply the complex exponential Frechet kernel to sparse DBCSR Re/Im matrices
    1232              : !> \param evals generator eigenvalues
    1233              : !> \param inner_deriv_re real part of the matrix in the generator eigenbasis
    1234              : !> \param inner_deriv_im imaginary part of the matrix in the generator eigenbasis
    1235              : !> \param outer_deriv_re real part of the mapped matrix
    1236              : !> \param outer_deriv_im imaginary part of the mapped matrix
    1237              : !> \param adjoint use the adjoint Frechet kernel for gradients
    1238              : ! **************************************************************************************************
    1239         1591 :    SUBROUTINE qs_ot_apply_complex_frechet_dbcsr(evals, inner_deriv_re, inner_deriv_im, &
    1240              :                                                 outer_deriv_re, outer_deriv_im, adjoint)
    1241              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: evals
    1242              :       TYPE(dbcsr_type)                                   :: inner_deriv_re, inner_deriv_im, &
    1243              :                                                             outer_deriv_re, outer_deriv_im
    1244              :       LOGICAL, INTENT(IN), OPTIONAL                      :: adjoint
    1245              : 
    1246              :       COMPLEX(KIND=dp)                                   :: cval_in, kernel
    1247              :       INTEGER                                            :: col, i, j, max_blocks, nblocks, row
    1248         1591 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: cols, rows
    1249         1591 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_offset, col_blk_size, &
    1250         1591 :                                                             row_blk_offset, row_blk_size
    1251              :       LOGICAL                                            :: found_im, found_out_im, found_out_re, &
    1252              :                                                             found_re, use_adjoint
    1253         1591 :       REAL(dp), DIMENSION(:, :), POINTER                 :: block_in_im, block_in_re, block_out_im, &
    1254         1591 :                                                             block_out_re
    1255              :       REAL(KIND=dp)                                      :: e1, e2, im_part, re_part
    1256              :       TYPE(dbcsr_distribution_type)                      :: dist
    1257              :       TYPE(dbcsr_iterator_type)                          :: iter
    1258              : 
    1259         1591 :       use_adjoint = .FALSE.
    1260         1589 :       IF (PRESENT(adjoint)) use_adjoint = adjoint
    1261              : 
    1262              :       ! Re/Im parts can have different sparse block patterns. Build their union
    1263              :       ! explicitly so a missing partner block is interpreted as zero.
    1264         1591 :       max_blocks = 0
    1265         1591 :       CALL dbcsr_iterator_start(iter, inner_deriv_re)
    1266         2645 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1267         1054 :          CALL dbcsr_iterator_next_block(iter, row, col)
    1268         1054 :          max_blocks = max_blocks + 1
    1269              :       END DO
    1270         1591 :       CALL dbcsr_iterator_stop(iter)
    1271         1591 :       CALL dbcsr_iterator_start(iter, inner_deriv_im)
    1272         2645 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1273         1054 :          CALL dbcsr_iterator_next_block(iter, row, col)
    1274         1054 :          max_blocks = max_blocks + 1
    1275              :       END DO
    1276         1591 :       CALL dbcsr_iterator_stop(iter)
    1277         6364 :       ALLOCATE (rows(MAX(max_blocks, 1)), cols(MAX(max_blocks, 1)))
    1278         1591 :       nblocks = 0
    1279              : 
    1280         1591 :       CALL dbcsr_iterator_start(iter, inner_deriv_re)
    1281         2645 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1282         1054 :          CALL dbcsr_iterator_next_block(iter, row, col)
    1283         1054 :          CALL append_union_block(row, col)
    1284              :       END DO
    1285         1591 :       CALL dbcsr_iterator_stop(iter)
    1286         1591 :       CALL dbcsr_iterator_start(iter, inner_deriv_im)
    1287         2645 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1288         1054 :          CALL dbcsr_iterator_next_block(iter, row, col)
    1289         1054 :          CALL append_union_block(row, col)
    1290              :       END DO
    1291         1591 :       CALL dbcsr_iterator_stop(iter)
    1292              : 
    1293              :       CALL dbcsr_get_info(inner_deriv_re, distribution=dist, row_blk_size=row_blk_size, &
    1294         1591 :                           col_blk_size=col_blk_size)
    1295              :       CALL dbcsr_create(outer_deriv_re, "outer_deriv_re", dist, dbcsr_type_no_symmetry, &
    1296         1591 :                         row_blk_size, col_blk_size)
    1297              :       CALL dbcsr_create(outer_deriv_im, "outer_deriv_im", dist, dbcsr_type_no_symmetry, &
    1298         1591 :                         row_blk_size, col_blk_size)
    1299         1591 :       IF (nblocks > 0) THEN
    1300         1054 :          CALL dbcsr_reserve_blocks(outer_deriv_re, rows=rows(1:nblocks), cols=cols(1:nblocks))
    1301         1054 :          CALL dbcsr_reserve_blocks(outer_deriv_im, rows=rows(1:nblocks), cols=cols(1:nblocks))
    1302              :       END IF
    1303         1591 :       CALL dbcsr_finalize(outer_deriv_re)
    1304         1591 :       CALL dbcsr_finalize(outer_deriv_im)
    1305         1591 :       CALL dbcsr_set(outer_deriv_re, 0.0_dp)
    1306         1591 :       CALL dbcsr_set(outer_deriv_im, 0.0_dp)
    1307              : 
    1308              :       CALL dbcsr_get_info(outer_deriv_re, row_blk_offset=row_blk_offset, &
    1309         1591 :                           col_blk_offset=col_blk_offset)
    1310         1591 :       CALL dbcsr_iterator_start(iter, outer_deriv_re)
    1311         2647 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1312         1056 :          CALL dbcsr_iterator_next_block(iter, row, col)
    1313         1056 :          CALL dbcsr_get_block_p(inner_deriv_re, row, col, block_in_re, found_re)
    1314         1056 :          CALL dbcsr_get_block_p(inner_deriv_im, row, col, block_in_im, found_im)
    1315         1056 :          CALL dbcsr_get_block_p(outer_deriv_re, row, col, block_out_re, found_out_re)
    1316         1056 :          CALL dbcsr_get_block_p(outer_deriv_im, row, col, block_out_im, found_out_im)
    1317         1056 :          CPASSERT(found_out_re .AND. found_out_im)
    1318              : 
    1319        11467 :          DO i = 1, SIZE(block_out_re, 1)
    1320       122108 :          DO j = 1, SIZE(block_out_re, 2)
    1321       112232 :             e1 = evals(row_blk_offset(row) + i - 1)
    1322       112232 :             e2 = evals(col_blk_offset(col) + j - 1)
    1323       112232 :             re_part = 0.0_dp
    1324       112232 :             im_part = 0.0_dp
    1325       112232 :             IF (found_re) re_part = block_in_re(i, j)
    1326       112232 :             IF (found_im) im_part = block_in_im(i, j)
    1327       112232 :             cval_in = CMPLX(re_part, im_part, dp)
    1328       112232 :             kernel = qs_ot_complex_exp_frechet_kernel(e1, e2)
    1329       112232 :             IF (use_adjoint) kernel = CONJG(kernel)
    1330       112232 :             cval_in = cval_in*kernel
    1331       112232 :             block_out_re(i, j) = REAL(cval_in, KIND=dp)
    1332       121052 :             block_out_im(i, j) = AIMAG(cval_in)
    1333              :          END DO
    1334              :          END DO
    1335              :       END DO
    1336         1591 :       CALL dbcsr_iterator_stop(iter)
    1337        12726 :       DEALLOCATE (rows, cols)
    1338              : 
    1339              :    CONTAINS
    1340              : 
    1341              : ! **************************************************************************************************
    1342              : !> \brief append a block coordinate unless it is already present
    1343              : !> \param row_new block-row index
    1344              : !> \param col_new block-column index
    1345              : ! **************************************************************************************************
    1346         2108 :       SUBROUTINE append_union_block(row_new, col_new)
    1347              :       INTEGER, INTENT(IN)                                :: row_new, col_new
    1348              : 
    1349              :       INTEGER                                            :: iblock
    1350              : 
    1351         2110 :          DO iblock = 1, nblocks
    1352         2110 :             IF (rows(iblock) == row_new .AND. cols(iblock) == col_new) RETURN
    1353              :          END DO
    1354         1056 :          nblocks = nblocks + 1
    1355         1056 :          rows(nblocks) = row_new
    1356         1056 :          cols(nblocks) = col_new
    1357              :       END SUBROUTINE append_union_block
    1358              : 
    1359              :    END SUBROUTINE qs_ot_apply_complex_frechet_dbcsr
    1360              : 
    1361              : ! **************************************************************************************************
    1362              : !> \brief gets ready to use the preconditioner/ or renew the preconditioner
    1363              : !>        only keeps a pointer to the preconditioner.
    1364              : !>        If you change the preconditioner, you have to call this routine
    1365              : !>        you remain responsible of proper deallocate of your preconditioner
    1366              : !>        (or you can reuse it on the next step of the computation)
    1367              : !> \param qs_ot_env ...
    1368              : !> \param preconditioner ...
    1369              : ! **************************************************************************************************
    1370         9469 :    SUBROUTINE qs_ot_new_preconditioner(qs_ot_env, preconditioner)
    1371              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    1372              :       TYPE(preconditioner_type), POINTER                 :: preconditioner
    1373              : 
    1374              :       INTEGER                                            :: ncoef
    1375              : 
    1376         9469 :       qs_ot_env%preconditioner => preconditioner
    1377         9469 :       qs_ot_env%os_valid = .FALSE.
    1378         9469 :       IF (.NOT. ASSOCIATED(qs_ot_env%matrix_psc0)) THEN
    1379         9469 :          CALL dbcsr_init_p(qs_ot_env%matrix_psc0)
    1380         9469 :          CALL dbcsr_copy(qs_ot_env%matrix_psc0, qs_ot_env%matrix_sc0, 'matrix_psc0')
    1381              :       END IF
    1382         9469 :       IF (qs_ot_env%has_complex_kpoint_state .AND. &
    1383              :           .NOT. ASSOCIATED(qs_ot_env%matrix_psc0_im)) THEN
    1384          747 :          CALL dbcsr_init_p(qs_ot_env%matrix_psc0_im)
    1385          747 :          CALL dbcsr_copy(qs_ot_env%matrix_psc0_im, qs_ot_env%matrix_sc0_im, 'matrix_psc0_im')
    1386              :       END IF
    1387              : 
    1388         9469 :       IF (.NOT. qs_ot_env%use_dx) THEN
    1389         5761 :          qs_ot_env%use_dx = .TRUE.
    1390         5761 :          CALL dbcsr_init_p(qs_ot_env%matrix_dx)
    1391         5761 :          CALL dbcsr_copy(qs_ot_env%matrix_dx, qs_ot_env%matrix_gx, 'matrix_dx')
    1392         5761 :          IF (qs_ot_env%has_complex_kpoint_state) THEN
    1393          440 :             CALL dbcsr_init_p(qs_ot_env%matrix_dx_im)
    1394          440 :             CALL dbcsr_copy(qs_ot_env%matrix_dx_im, qs_ot_env%matrix_gx_im, 'matrix_dx_im')
    1395              :          END IF
    1396         5761 :          IF (qs_ot_env%settings%do_rotation) THEN
    1397           74 :             CALL dbcsr_init_p(qs_ot_env%rot_mat_dx)
    1398           74 :             CALL dbcsr_copy(qs_ot_env%rot_mat_dx, qs_ot_env%rot_mat_gx, 'rot_mat_dx')
    1399           74 :             IF (qs_ot_env%has_complex_kpoint_state) THEN
    1400           48 :                CALL dbcsr_init_p(qs_ot_env%rot_mat_dx_im)
    1401           48 :                CALL dbcsr_copy(qs_ot_env%rot_mat_dx_im, qs_ot_env%rot_mat_gx_im, 'rot_mat_dx_im')
    1402              :             END IF
    1403              :          END IF
    1404         5761 :          IF (qs_ot_env%settings%do_ener) THEN
    1405           40 :             ncoef = SIZE(qs_ot_env%ener_gx)
    1406          120 :             ALLOCATE (qs_ot_env%ener_dx(ncoef))
    1407          320 :             qs_ot_env%ener_dx = 0.0_dp
    1408              :          END IF
    1409              :       END IF
    1410              : 
    1411         9469 :    END SUBROUTINE qs_ot_new_preconditioner
    1412              : 
    1413              : ! **************************************************************************************************
    1414              : !> \brief multiply paired real/imaginary DBCSR matrices, with optional conjugate transposes
    1415              : !> \param op_a N or C
    1416              : !> \param op_b N or C
    1417              : !> \param a_re real part of A
    1418              : !> \param a_im imaginary part of A
    1419              : !> \param b_re real part of B
    1420              : !> \param b_im imaginary part of B
    1421              : !> \param c_re real part of A*B
    1422              : !> \param c_im imaginary part of A*B
    1423              : !> \param tmp real workspace shaped like C
    1424              : ! **************************************************************************************************
    1425        60036 :    SUBROUTINE qs_ot_complex_multiply(op_a, op_b, a_re, a_im, b_re, b_im, c_re, c_im, tmp)
    1426              :       CHARACTER(LEN=1), INTENT(IN)                       :: op_a, op_b
    1427              :       TYPE(dbcsr_type)                                   :: a_re, a_im, b_re, b_im, c_re, c_im, tmp
    1428              : 
    1429              :       CHARACTER(LEN=1)                                   :: db_op_a, db_op_b
    1430              :       REAL(KIND=dp)                                      :: sign_a, sign_b
    1431              : 
    1432        41124 :       SELECT CASE (op_a)
    1433              :       CASE ('N')
    1434        41124 :          db_op_a = 'N'
    1435        41124 :          sign_a = 1.0_dp
    1436              :       CASE ('C')
    1437        18912 :          db_op_a = 'T'
    1438        18912 :          sign_a = -1.0_dp
    1439              :       CASE DEFAULT
    1440        60036 :          CPABORT("Complex matrix product expects N or C for op_a")
    1441              :       END SELECT
    1442        49596 :       SELECT CASE (op_b)
    1443              :       CASE ('N')
    1444        49596 :          db_op_b = 'N'
    1445        49596 :          sign_b = 1.0_dp
    1446              :       CASE ('C')
    1447        10440 :          db_op_b = 'T'
    1448        10440 :          sign_b = -1.0_dp
    1449              :       CASE DEFAULT
    1450        60036 :          CPABORT("Complex matrix product expects N or C for op_b")
    1451              :       END SELECT
    1452              : 
    1453        60036 :       CALL dbcsr_multiply(db_op_a, db_op_b, 1.0_dp, a_re, b_re, 0.0_dp, c_re)
    1454        60036 :       CALL dbcsr_multiply(db_op_a, db_op_b, 1.0_dp, a_im, b_im, 0.0_dp, tmp)
    1455        60036 :       CALL dbcsr_add(c_re, tmp, alpha_scalar=1.0_dp, beta_scalar=-sign_a*sign_b)
    1456              : 
    1457        60036 :       CALL dbcsr_multiply(db_op_a, db_op_b, sign_b, a_re, b_im, 0.0_dp, c_im)
    1458        60036 :       CALL dbcsr_multiply(db_op_a, db_op_b, sign_a, a_im, b_re, 0.0_dp, tmp)
    1459        60036 :       CALL dbcsr_add(c_im, tmp, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    1460              : 
    1461        60036 :    END SUBROUTINE qs_ot_complex_multiply
    1462              : 
    1463              : ! **************************************************************************************************
    1464              : !> \brief ...
    1465              : !> \param qs_ot_env ...
    1466              : !> \param C_NEW ...
    1467              : !> \param SC ...
    1468              : !> \param G_OLD ...
    1469              : !> \param D ...
    1470              : ! **************************************************************************************************
    1471          700 :    SUBROUTINE qs_ot_on_the_fly_localize(qs_ot_env, C_NEW, SC, G_OLD, D)
    1472              :       !
    1473              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    1474              :       TYPE(dbcsr_type), POINTER                          :: C_NEW, SC, G_OLD, D
    1475              : 
    1476              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_on_the_fly_localize'
    1477              :       INTEGER, PARAMETER                                 :: taylor_order = 50
    1478              :       REAL(KIND=dp), PARAMETER                           :: alpha = 0.1_dp, f2_eps = 0.01_dp
    1479              : 
    1480              :       INTEGER                                            :: col, col_size, handle, i, k, n, p, row, &
    1481              :                                                             row_size
    1482          140 :       REAL(dp), DIMENSION(:, :), POINTER                 :: block
    1483              :       REAL(KIND=dp)                                      :: expfactor, f2, norm_fro, norm_gct, tmp
    1484              :       TYPE(dbcsr_distribution_type)                      :: dist
    1485              :       TYPE(dbcsr_iterator_type)                          :: iter
    1486              :       TYPE(dbcsr_type), POINTER                          :: C, Gp1, Gp2, GU, U
    1487              :       TYPE(mp_comm_type)                                 :: group
    1488              : 
    1489          140 :       CALL timeset(routineN, handle)
    1490              :       !
    1491              :       !
    1492          140 :       CALL dbcsr_get_info(C_NEW, nfullrows_total=n, nfullcols_total=k)
    1493              :       !
    1494              :       ! C = C*expm(-G)
    1495          140 :       GU => qs_ot_env%buf1_k_k_nosym ! a buffer
    1496          140 :       U => qs_ot_env%buf2_k_k_nosym ! a buffer
    1497          140 :       Gp1 => qs_ot_env%buf3_k_k_nosym ! a buffer
    1498          140 :       Gp2 => qs_ot_env%buf4_k_k_nosym ! a buffer
    1499          140 :       C => qs_ot_env%buf1_n_k ! a buffer
    1500              :       !
    1501              :       ! compute the derivative of the norm
    1502              :       !-------------------------------------------------------------------
    1503              :       ! (x^2+eps)^1/2
    1504          140 :       f2 = 0.0_dp
    1505          140 :       CALL dbcsr_copy(C, C_NEW)
    1506          140 :       CALL dbcsr_iterator_start(iter, C)
    1507          322 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1508          182 :          CALL dbcsr_iterator_next_block(iter, row, col, block, row_size=row_size, col_size=col_size)
    1509         1162 :          DO p = 1, col_size ! p
    1510         8694 :          DO i = 1, row_size ! i
    1511         7672 :             tmp = SQRT(block(i, p)**2 + f2_eps)
    1512         7672 :             f2 = f2 + tmp
    1513         8512 :             block(i, p) = block(i, p)/tmp
    1514              :          END DO
    1515              :          END DO
    1516              :       END DO
    1517          140 :       CALL dbcsr_iterator_stop(iter)
    1518          140 :       CALL dbcsr_get_info(C, group=group)
    1519          140 :       CALL group%sum(f2)
    1520              :       !
    1521              :       !
    1522          140 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, C, C_NEW, 0.0_dp, GU)
    1523              :       !
    1524              :       ! antisymetrize
    1525          140 :       CALL dbcsr_get_info(GU, distribution=dist)
    1526              :       CALL dbcsr_transposed(U, GU, shallow_data_copy=.FALSE., &
    1527              :                             use_distribution=dist, &
    1528          140 :                             transpose_distribution=.FALSE.)
    1529          140 :       CALL dbcsr_add(GU, U, alpha_scalar=-0.5_dp, beta_scalar=0.5_dp)
    1530              :       !-------------------------------------------------------------------
    1531              :       !
    1532          140 :       norm_fro = dbcsr_frobenius_norm(GU)
    1533          140 :       norm_gct = dbcsr_gershgorin_norm(GU)
    1534              :       !write(*,*) 'qs_ot_localize: ||P-I||_f=',norm_fro,' ||P-I||_GCT=',norm_gct
    1535              :       !
    1536              :       !kscale = CEILING(LOG(MIN(norm_fro,norm_gct))/LOG(2.0_dp))
    1537              :       !scale  = LOG(MIN(norm_fro,norm_gct))/LOG(2.0_dp)
    1538              :       !write(*,*) 'qs_ot_localize: scale=',scale,' kscale=',kscale
    1539              :       !
    1540              :       ! rescale for steepest descent
    1541          140 :       CALL dbcsr_scale(GU, -alpha)
    1542              :       !
    1543              :       ! compute unitary transform
    1544              :       ! zeroth and first order
    1545          140 :       expfactor = 1.0_dp
    1546          140 :       CALL dbcsr_copy(U, GU)
    1547          140 :       CALL dbcsr_scale(U, expfactor)
    1548          140 :       CALL dbcsr_add_on_diag(U, 1.0_dp)
    1549              :       ! other orders
    1550          140 :       CALL dbcsr_copy(Gp1, GU)
    1551          750 :       DO i = 2, taylor_order
    1552              :          ! new power of G
    1553          750 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, GU, Gp1, 0.0_dp, Gp2)
    1554          750 :          CALL dbcsr_copy(Gp1, Gp2)
    1555              :          ! add to the taylor expansion so far
    1556          750 :          expfactor = expfactor/REAL(i, KIND=dp)
    1557          750 :          CALL dbcsr_add(U, Gp1, alpha_scalar=1.0_dp, beta_scalar=expfactor)
    1558          750 :          norm_fro = dbcsr_frobenius_norm(Gp1)
    1559              :          !write(*,*) 'Taylor expansion i=',i,' norm(X^i)/i!=',norm_fro*expfactor
    1560          750 :          IF (norm_fro*expfactor < 1.0E-10_dp) EXIT
    1561              :       END DO
    1562              :       !
    1563              :       ! rotate MOs
    1564          140 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, C_NEW, U, 0.0_dp, C)
    1565          140 :       CALL dbcsr_copy(C_NEW, C)
    1566              :       !
    1567              :       ! rotate SC
    1568          140 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, SC, U, 0.0_dp, C)
    1569          140 :       CALL dbcsr_copy(SC, C)
    1570              :       !
    1571              :       ! rotate D_i
    1572          140 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, D, U, 0.0_dp, C)
    1573          140 :       CALL dbcsr_copy(D, C)
    1574              :       !
    1575              :       ! rotate G_i-1
    1576          140 :       IF (ASSOCIATED(G_OLD)) THEN
    1577          140 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, G_OLD, U, 0.0_dp, C)
    1578          140 :          CALL dbcsr_copy(G_OLD, C)
    1579              :       END IF
    1580              :       !
    1581          140 :       CALL timestop(handle)
    1582          140 :    END SUBROUTINE qs_ot_on_the_fly_localize
    1583              : 
    1584              : ! **************************************************************************************************
    1585              : !> \brief ...
    1586              : !> \param qs_ot_env ...
    1587              : !> \param C_OLD ...
    1588              : !> \param C_TMP ...
    1589              : !> \param C_NEW ...
    1590              : !> \param P ...
    1591              : !> \param SC ...
    1592              : !> \param update ...
    1593              : ! **************************************************************************************************
    1594         1550 :    SUBROUTINE qs_ot_ref_chol(qs_ot_env, C_OLD, C_TMP, C_NEW, P, SC, update)
    1595              :       !
    1596              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    1597              :       TYPE(dbcsr_type)                                   :: C_OLD, C_TMP, C_NEW, P, SC
    1598              :       LOGICAL, INTENT(IN)                                :: update
    1599              : 
    1600              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'qs_ot_ref_chol'
    1601              : 
    1602              :       INTEGER                                            :: handle, k, n
    1603              : 
    1604          775 :       CALL timeset(routineN, handle)
    1605              :       !
    1606          775 :       CALL dbcsr_get_info(C_NEW, nfullrows_total=n, nfullcols_total=k)
    1607              :       !
    1608              :       ! P = U'*U
    1609          775 :       CALL cp_dbcsr_cholesky_decompose(P, k, qs_ot_env%para_env, qs_ot_env%blacs_env)
    1610              :       !
    1611              :       ! C_NEW = C_OLD*inv(U)
    1612              :       CALL cp_dbcsr_cholesky_restore(C_OLD, k, P, C_NEW, op="SOLVE", pos="RIGHT", &
    1613          775 :                                      transa="N", para_env=qs_ot_env%para_env, blacs_env=qs_ot_env%blacs_env)
    1614              :       !
    1615              :       ! Update SC if needed
    1616          775 :       IF (update) THEN
    1617              :          CALL cp_dbcsr_cholesky_restore(SC, k, P, C_TMP, op="SOLVE", pos="RIGHT", &
    1618          429 :                                         transa="N", para_env=qs_ot_env%para_env, blacs_env=qs_ot_env%blacs_env)
    1619          429 :          CALL dbcsr_copy(SC, C_TMP)
    1620              :       END IF
    1621              :       !
    1622          775 :       CALL timestop(handle)
    1623          775 :    END SUBROUTINE qs_ot_ref_chol
    1624              : 
    1625              : ! **************************************************************************************************
    1626              : !> \brief ...
    1627              : !> \param qs_ot_env ...
    1628              : !> \param C_OLD ...
    1629              : !> \param C_TMP ...
    1630              : !> \param C_NEW ...
    1631              : !> \param P ...
    1632              : !> \param SC ...
    1633              : !> \param update ...
    1634              : ! **************************************************************************************************
    1635          422 :    SUBROUTINE qs_ot_ref_lwdn(qs_ot_env, C_OLD, C_TMP, C_NEW, P, SC, update)
    1636              :       !
    1637              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    1638              :       TYPE(dbcsr_type)                                   :: C_OLD, C_TMP, C_NEW, P, SC
    1639              :       LOGICAL, INTENT(IN)                                :: update
    1640              : 
    1641              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'qs_ot_ref_lwdn'
    1642              : 
    1643              :       INTEGER                                            :: handle, i, k, n
    1644              :       REAL(dp), ALLOCATABLE, DIMENSION(:)                :: eig, fun
    1645              :       TYPE(dbcsr_type), POINTER                          :: V, W
    1646              : 
    1647          422 :       CALL timeset(routineN, handle)
    1648              :       !
    1649          422 :       CALL dbcsr_get_info(C_NEW, nfullrows_total=n, nfullcols_total=k)
    1650              :       !
    1651          422 :       V => qs_ot_env%buf1_k_k_nosym ! a buffer
    1652          422 :       W => qs_ot_env%buf2_k_k_nosym ! a buffer
    1653         1688 :       ALLOCATE (eig(k), fun(k))
    1654              :       !
    1655          422 :       CALL cp_dbcsr_syevd(P, V, eig, qs_ot_env%para_env, qs_ot_env%blacs_env)
    1656              :       !
    1657              :       ! compute the P^(-1/2)
    1658         2366 :       DO i = 1, k
    1659         1944 :          IF (eig(i) <= 0.0_dp) THEN
    1660            0 :             CPABORT("P not positive definite")
    1661              :          END IF
    1662         2366 :          IF (eig(i) < 1.0E-8_dp) THEN
    1663            0 :             fun(i) = 0.0_dp
    1664              :          ELSE
    1665         1944 :             fun(i) = 1.0_dp/SQRT(eig(i))
    1666              :          END IF
    1667              :       END DO
    1668          422 :       CALL dbcsr_copy(W, V)
    1669          422 :       CALL dbcsr_scale_by_vector(V, alpha=fun, side='right')
    1670          422 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, W, V, 0.0_dp, P)
    1671              :       !
    1672              :       ! Update C
    1673          422 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, C_OLD, P, 0.0_dp, C_NEW)
    1674              :       !
    1675              :       ! Update SC if needed
    1676          422 :       IF (update) THEN
    1677          272 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, SC, P, 0.0_dp, C_TMP)
    1678          272 :          CALL dbcsr_copy(SC, C_TMP)
    1679              :       END IF
    1680              :       !
    1681          422 :       DEALLOCATE (eig, fun)
    1682              :       !
    1683          422 :       CALL timestop(handle)
    1684          422 :    END SUBROUTINE qs_ot_ref_lwdn
    1685              : 
    1686              : ! **************************************************************************************************
    1687              : !> \brief ...
    1688              : !> \param qs_ot_env ...
    1689              : !> \param C_OLD ...
    1690              : !> \param C_TMP ...
    1691              : !> \param C_NEW ...
    1692              : !> \param P ...
    1693              : !> \param SC ...
    1694              : !> \param norm_in ...
    1695              : !> \param update ...
    1696              : ! **************************************************************************************************
    1697         8066 :    SUBROUTINE qs_ot_ref_poly(qs_ot_env, C_OLD, C_TMP, C_NEW, P, SC, norm_in, update)
    1698              :       !
    1699              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    1700              :       TYPE(dbcsr_type), POINTER                          :: C_OLD, C_TMP, C_NEW, P
    1701              :       TYPE(dbcsr_type)                                   :: SC
    1702              :       REAL(dp), INTENT(IN)                               :: norm_in
    1703              :       LOGICAL, INTENT(IN)                                :: update
    1704              : 
    1705              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'qs_ot_ref_poly'
    1706              : 
    1707              :       INTEGER                                            :: handle, irefine, k, n
    1708              :       LOGICAL                                            :: quick_exit
    1709              :       REAL(dp)                                           :: norm, norm_fro, norm_gct, occ_in, &
    1710              :                                                             occ_out, rescale
    1711              :       TYPE(dbcsr_type), POINTER                          :: BUF1, BUF2, BUF_NOSYM, FT, FY
    1712              : 
    1713         4033 :       CALL timeset(routineN, handle)
    1714              :       !
    1715         4033 :       CALL dbcsr_get_info(C_NEW, nfullrows_total=n, nfullcols_total=k)
    1716              :       !
    1717         4033 :       BUF_NOSYM => qs_ot_env%buf1_k_k_nosym ! a buffer
    1718         4033 :       BUF1 => qs_ot_env%buf1_k_k_sym ! a buffer
    1719         4033 :       BUF2 => qs_ot_env%buf2_k_k_sym ! a buffer
    1720         4033 :       FY => qs_ot_env%buf3_k_k_sym ! a buffer
    1721         4033 :       FT => qs_ot_env%buf4_k_k_sym ! a buffer
    1722              :       !
    1723              :       ! initialize the norm (already computed in qs_ot_get_orbitals_ref)
    1724         4033 :       norm = norm_in
    1725              :       !
    1726              :       ! can we do a quick exit?
    1727         4033 :       quick_exit = .FALSE.
    1728         4033 :       IF (norm < qs_ot_env%settings%eps_irac_quick_exit) quick_exit = .TRUE.
    1729              :       !
    1730              :       ! lets refine
    1731         4033 :       rescale = 1.0_dp
    1732         4467 :       DO irefine = 1, qs_ot_env%settings%max_irac
    1733              :          !
    1734              :          ! rescaling
    1735         4467 :          IF (norm > 1.0_dp) THEN
    1736           12 :             CALL dbcsr_scale(P, 1.0_dp/norm)
    1737           12 :             rescale = rescale/SQRT(norm)
    1738              :          END IF
    1739              :          !
    1740              :          ! get the refinement polynomial
    1741              :          CALL qs_ot_refine(P, FY, BUF1, BUF2, qs_ot_env%settings%irac_degree, &
    1742         4467 :                            qs_ot_env%settings%eps_irac_filter_matrix)
    1743              :          !
    1744              :          ! collect the transformation
    1745         4467 :          IF (irefine == 1) THEN
    1746         4033 :             CALL dbcsr_copy(FT, FY, name='FT')
    1747              :          ELSE
    1748          434 :             CALL dbcsr_multiply('N', 'N', 1.0_dp, FT, FY, 0.0_dp, BUF1)
    1749          434 :             IF (qs_ot_env%settings%eps_irac_filter_matrix > 0.0_dp) THEN
    1750            4 :                occ_in = dbcsr_get_occupation(buf1)
    1751            4 :                CALL dbcsr_filter(buf1, qs_ot_env%settings%eps_irac_filter_matrix)
    1752            4 :                occ_out = dbcsr_get_occupation(buf1)
    1753              :             END IF
    1754          434 :             CALL dbcsr_copy(FT, BUF1, name='FT')
    1755              :          END IF
    1756              :          !
    1757              :          ! quick exit if possible
    1758         4467 :          IF (quick_exit) THEN
    1759              :             EXIT
    1760              :          END IF
    1761              :          !
    1762              :          ! P = FY^T * P * FY
    1763         1881 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, P, FY, 0.0_dp, BUF_NOSYM)
    1764         1881 :          IF (qs_ot_env%settings%eps_irac_filter_matrix > 0.0_dp) THEN
    1765            8 :             occ_in = dbcsr_get_occupation(buf_nosym)
    1766            8 :             CALL dbcsr_filter(buf_nosym, qs_ot_env%settings%eps_irac_filter_matrix)
    1767            8 :             occ_out = dbcsr_get_occupation(buf_nosym)
    1768              :          END IF
    1769         1881 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, FY, BUF_NOSYM, 0.0_dp, P)
    1770         1881 :          IF (qs_ot_env%settings%eps_irac_filter_matrix > 0.0_dp) THEN
    1771            8 :             occ_in = dbcsr_get_occupation(p)
    1772            8 :             CALL dbcsr_filter(p, qs_ot_env%settings%eps_irac_filter_matrix)
    1773            8 :             occ_out = dbcsr_get_occupation(p)
    1774              :          END IF
    1775              :          !
    1776              :          ! check ||P-1||_gct
    1777         1881 :          CALL dbcsr_add_on_diag(P, -1.0_dp)
    1778         1881 :          norm_fro = dbcsr_frobenius_norm(P)
    1779         1881 :          norm_gct = dbcsr_gershgorin_norm(P)
    1780         1881 :          CALL dbcsr_add_on_diag(P, 1.0_dp)
    1781         1881 :          norm = MIN(norm_gct, norm_fro)
    1782              :          !
    1783              :          ! printing
    1784              :          !
    1785              :          ! blows up
    1786         1881 :          IF (norm > 1.0E10_dp) THEN
    1787              :             CALL cp_abort(__LOCATION__, &
    1788              :                           "Refinement blows up! "// &
    1789              :                           "We need you to improve the code, please post your input on "// &
    1790            0 :                           "the forum https://www.cp2k.org/")
    1791              :          END IF
    1792              :          !
    1793              :          ! can we do a quick exit next step?
    1794         1881 :          IF (norm < qs_ot_env%settings%eps_irac_quick_exit) quick_exit = .TRUE.
    1795              :          !
    1796              :          ! are we done?
    1797         4467 :          IF (norm < qs_ot_env%settings%eps_irac) EXIT
    1798              :          !
    1799              :       END DO
    1800              :       !
    1801              :       ! C_NEW = C_NEW * FT * rescale
    1802         4033 :       CALL dbcsr_multiply('N', 'N', rescale, C_OLD, FT, 0.0_dp, C_NEW)
    1803         4033 :       IF (qs_ot_env%settings%eps_irac_filter_matrix > 0.0_dp) THEN
    1804            4 :          occ_in = dbcsr_get_occupation(c_new)
    1805            4 :          CALL dbcsr_filter(c_new, qs_ot_env%settings%eps_irac_filter_matrix)
    1806            4 :          occ_out = dbcsr_get_occupation(c_new)
    1807              :       END IF
    1808              :       !
    1809              :       ! update SC = SC * FY * rescale
    1810         4033 :       IF (update) THEN
    1811         1647 :          CALL dbcsr_multiply('N', 'N', rescale, SC, FT, 0.0_dp, C_TMP)
    1812         1647 :          IF (qs_ot_env%settings%eps_irac_filter_matrix > 0.0_dp) THEN
    1813            4 :             occ_in = dbcsr_get_occupation(c_tmp)
    1814            4 :             CALL dbcsr_filter(c_tmp, qs_ot_env%settings%eps_irac_filter_matrix)
    1815            4 :             occ_out = dbcsr_get_occupation(c_tmp)
    1816              :          END IF
    1817         1647 :          CALL dbcsr_copy(SC, C_TMP)
    1818              :       END IF
    1819              :       !
    1820         4033 :       CALL timestop(handle)
    1821         4033 :    END SUBROUTINE qs_ot_ref_poly
    1822              : 
    1823              : ! **************************************************************************************************
    1824              : !> \brief ...
    1825              : !> \param qs_ot_env1 ...
    1826              : !> \return ...
    1827              : ! **************************************************************************************************
    1828         8708 :    FUNCTION qs_ot_ref_update(qs_ot_env1) RESULT(update)
    1829              :       !
    1830              :       TYPE(qs_ot_type)                                   :: qs_ot_env1
    1831              :       LOGICAL                                            :: update
    1832              : 
    1833         8708 :       update = .FALSE.
    1834         6792 :       SELECT CASE (qs_ot_env1%settings%ot_method)
    1835              :       CASE ("CG", "SD")
    1836         6792 :          SELECT CASE (qs_ot_env1%settings%line_search_method)
    1837              :          CASE ("2PNT")
    1838         6792 :             IF (qs_ot_env1%line_search_count == 2) update = .TRUE.
    1839              :          CASE DEFAULT
    1840         6792 :             CPABORT("NYI")
    1841              :          END SELECT
    1842              :       CASE ("DIIS")
    1843          814 :          update = .TRUE.
    1844              :       CASE ("BROY", "LBFG")
    1845              :          ! These minimizers retain positions or secants in one fixed REF chart.
    1846          814 :          update = .FALSE.
    1847              :       CASE DEFAULT
    1848         8708 :          CPABORT("NYI")
    1849              :       END SELECT
    1850         8708 :    END FUNCTION qs_ot_ref_update
    1851              : 
    1852              : ! **************************************************************************************************
    1853              : !> \brief ...
    1854              : !> \param qs_ot_env1 ...
    1855              : !> \param norm_in ...
    1856              : !> \param ortho_irac ...
    1857              : ! **************************************************************************************************
    1858         5230 :    SUBROUTINE qs_ot_ref_decide(qs_ot_env1, norm_in, ortho_irac)
    1859              :       !
    1860              :       TYPE(qs_ot_type)                                   :: qs_ot_env1
    1861              :       REAL(dp), INTENT(IN)                               :: norm_in
    1862              :       CHARACTER(LEN=*), INTENT(INOUT)                    :: ortho_irac
    1863              : 
    1864         5230 :       ortho_irac = qs_ot_env1%settings%ortho_irac
    1865         5230 :       IF (norm_in < qs_ot_env1%settings%eps_irac_switch) ortho_irac = "POLY"
    1866         5230 :    END SUBROUTINE qs_ot_ref_decide
    1867              : 
    1868              : ! **************************************************************************************************
    1869              : !> \brief ...
    1870              : !> \param matrix_c ...
    1871              : !> \param matrix_s ...
    1872              : !> \param matrix_x ...
    1873              : !> \param matrix_sx ...
    1874              : !> \param matrix_gx_old ...
    1875              : !> \param matrix_dx ...
    1876              : !> \param qs_ot_env ...
    1877              : !> \param qs_ot_env1 ...
    1878              : ! **************************************************************************************************
    1879        10460 :    SUBROUTINE qs_ot_get_orbitals_ref(matrix_c, matrix_s, matrix_x, matrix_sx, &
    1880              :                                      matrix_gx_old, matrix_dx, qs_ot_env, qs_ot_env1)
    1881              :       !
    1882              :       TYPE(dbcsr_type), POINTER                          :: matrix_c, matrix_s, matrix_x, matrix_sx, &
    1883              :                                                             matrix_gx_old, matrix_dx
    1884              :       TYPE(qs_ot_type)                                   :: qs_ot_env, qs_ot_env1
    1885              : 
    1886              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_orbitals_ref'
    1887              : 
    1888              :       CHARACTER(LEN=4)                                   :: ortho_irac
    1889              :       INTEGER                                            :: handle, k, n
    1890              :       LOGICAL                                            :: on_the_fly_loc, update
    1891              :       REAL(dp)                                           :: norm, norm_fro, norm_gct, occ_in, occ_out
    1892              :       TYPE(dbcsr_type), POINTER                          :: C_NEW, C_OLD, C_TMP, D, G_OLD, P, S, SC
    1893              : 
    1894         5230 :       CALL timeset(routineN, handle)
    1895              : 
    1896         5230 :       CALL dbcsr_get_info(matrix_c, nfullrows_total=n, nfullcols_total=k)
    1897              :       !
    1898         5230 :       C_NEW => matrix_c
    1899         5230 :       C_OLD => matrix_x ! need to be carefully updated for the gradient !
    1900         5230 :       SC => matrix_sx ! need to be carefully updated for the gradient !
    1901         5230 :       G_OLD => matrix_gx_old ! need to be carefully updated for localization !
    1902         5230 :       D => matrix_dx ! need to be carefully updated for localization !
    1903         5230 :       S => matrix_s
    1904              : 
    1905         5230 :       P => qs_ot_env%p_k_k_sym ! a buffer
    1906         5230 :       C_TMP => qs_ot_env%buf1_n_k ! a buffer
    1907              :       !
    1908              :       ! do we need to update C_OLD and SC?
    1909         5230 :       update = qs_ot_ref_update(qs_ot_env1)
    1910              :       !
    1911              :       ! do we want to on the fly localize?
    1912              :       ! for the moment this is set from the input,
    1913              :       ! later we might want to localize every n-step or
    1914              :       ! when the sparsity increases...
    1915         5230 :       on_the_fly_loc = qs_ot_env1%settings%on_the_fly_loc
    1916              :       !
    1917              :       ! compute SC = S*C
    1918         5230 :       IF (ASSOCIATED(S)) THEN
    1919         5230 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, S, C_OLD, 0.0_dp, SC)
    1920         5230 :          IF (qs_ot_env1%settings%eps_irac_filter_matrix > 0.0_dp) THEN
    1921            4 :             occ_in = dbcsr_get_occupation(sc)
    1922            4 :             CALL dbcsr_filter(sc, qs_ot_env1%settings%eps_irac_filter_matrix)
    1923            4 :             occ_out = dbcsr_get_occupation(sc)
    1924              :          END IF
    1925              :       ELSE
    1926            0 :          CALL dbcsr_copy(SC, C_OLD)
    1927              :       END IF
    1928              :       !
    1929              :       ! compute P = C'*SC
    1930         5230 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, C_OLD, SC, 0.0_dp, P)
    1931         5230 :       IF (qs_ot_env1%settings%eps_irac_filter_matrix > 0.0_dp) THEN
    1932            4 :          occ_in = dbcsr_get_occupation(p)
    1933            4 :          CALL dbcsr_filter(p, qs_ot_env1%settings%eps_irac_filter_matrix)
    1934            4 :          occ_out = dbcsr_get_occupation(p)
    1935              :       END IF
    1936              :       !
    1937              :       ! check ||P-1||_f and ||P-1||_gct
    1938         5230 :       CALL dbcsr_add_on_diag(P, -1.0_dp)
    1939         5230 :       norm_fro = dbcsr_frobenius_norm(P)
    1940         5230 :       norm_gct = dbcsr_gershgorin_norm(P)
    1941         5230 :       CALL dbcsr_add_on_diag(P, 1.0_dp)
    1942         5230 :       norm = MIN(norm_gct, norm_fro)
    1943         5230 :       CALL qs_ot_ref_decide(qs_ot_env1, norm, ortho_irac)
    1944              :       !
    1945              :       ! select the orthogonality method
    1946          775 :       SELECT CASE (ortho_irac)
    1947              :       CASE ("CHOL")
    1948          775 :          CALL qs_ot_ref_chol(qs_ot_env, C_OLD, C_TMP, C_NEW, P, SC, update)
    1949              :       CASE ("LWDN")
    1950          422 :          CALL qs_ot_ref_lwdn(qs_ot_env, C_OLD, C_TMP, C_NEW, P, SC, update)
    1951              :       CASE ("POLY")
    1952         4033 :          CALL qs_ot_ref_poly(qs_ot_env, C_OLD, C_TMP, C_NEW, P, SC, norm, update)
    1953              :       CASE DEFAULT
    1954         5230 :          CPABORT("Wrong argument")
    1955              :       END SELECT
    1956              :       !
    1957              :       ! We update the C_i+1 and localization
    1958         5230 :       IF (update) THEN
    1959         2348 :          IF (on_the_fly_loc) THEN
    1960          140 :             CALL qs_ot_on_the_fly_localize(qs_ot_env, C_NEW, SC, G_OLD, D)
    1961              :          END IF
    1962         2348 :          CALL dbcsr_copy(C_OLD, C_NEW)
    1963              :       END IF
    1964              : 
    1965         5230 :       IF (qs_ot_env%settings%do_rotation) THEN
    1966           14 :          CALL qs_ot_generate_rotation(qs_ot_env)
    1967              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, C_NEW, qs_ot_env%rot_mat_u, &
    1968           14 :                              0.0_dp, C_TMP)
    1969           14 :          CALL dbcsr_copy(C_NEW, C_TMP)
    1970              :       END IF
    1971              :       !
    1972         5230 :       CALL timestop(handle)
    1973         5230 :    END SUBROUTINE qs_ot_get_orbitals_ref
    1974              : 
    1975              : ! **************************************************************************************************
    1976              : !> \brief update complex REF k-point orbitals and their S(k)C(k) images
    1977              : !> \param matrix_c ...
    1978              : !> \param matrix_c_im ...
    1979              : !> \param matrix_s ...
    1980              : !> \param matrix_s_im ...
    1981              : !> \param qs_ot_env ...
    1982              : !> \param qs_ot_env1 environment carrying the shared minimizer state
    1983              : ! **************************************************************************************************
    1984         3478 :    SUBROUTINE qs_ot_get_orbitals_ref_complex(matrix_c, matrix_c_im, matrix_s, matrix_s_im, &
    1985              :                                              qs_ot_env, qs_ot_env1)
    1986              : 
    1987              :       TYPE(dbcsr_type), POINTER                          :: matrix_c, matrix_c_im, matrix_s, &
    1988              :                                                             matrix_s_im
    1989              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    1990              :       TYPE(qs_ot_type), OPTIONAL                         :: qs_ot_env1
    1991              : 
    1992              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_orbitals_ref_complex'
    1993              : 
    1994              :       INTEGER                                            :: handle, i, k, n
    1995              :       LOGICAL                                            :: update
    1996         3478 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenvalues, inverse_sqrt
    1997              :       TYPE(dbcsr_type)                                   :: rotated_im, rotated_re, rotation_tmp
    1998              :       TYPE(dbcsr_type), POINTER                          :: c_im, c_re, f_im, f_re, p_im, p_re, &
    1999              :                                                             sc_im, sc_re, tmp_kk, tmp_nk, v_im, &
    2000              :                                                             v_re, w_im, w_re
    2001              : 
    2002         3478 :       CALL timeset(routineN, handle)
    2003              : 
    2004         3478 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    2005         3478 :       CPASSERT(ASSOCIATED(matrix_s))
    2006         3478 :       CPASSERT(ASSOCIATED(matrix_s_im))
    2007              : 
    2008         3478 :       c_re => qs_ot_env%matrix_x
    2009         3478 :       c_im => qs_ot_env%matrix_x_im
    2010         3478 :       f_re => qs_ot_env%matrix_ref_inv_sqrt
    2011         3478 :       f_im => qs_ot_env%matrix_ref_inv_sqrt_im
    2012         3478 :       sc_re => qs_ot_env%matrix_sx
    2013         3478 :       sc_im => qs_ot_env%matrix_sx_im
    2014         3478 :       p_re => qs_ot_env%buf1_k_k_sym
    2015         3478 :       p_im => qs_ot_env%buf2_k_k_sym
    2016         3478 :       v_re => qs_ot_env%buf3_k_k_sym
    2017         3478 :       v_im => qs_ot_env%buf4_k_k_sym
    2018         3478 :       w_re => qs_ot_env%buf1_k_k_nosym
    2019         3478 :       w_im => qs_ot_env%buf2_k_k_nosym
    2020         3478 :       tmp_kk => qs_ot_env%buf3_k_k_nosym
    2021         3478 :       tmp_nk => qs_ot_env%buf1_n_k
    2022              : 
    2023         3478 :       CALL dbcsr_get_info(c_re, nfullrows_total=n, nfullcols_total=k)
    2024         3478 :       IF (PRESENT(qs_ot_env1)) THEN
    2025         3279 :          update = qs_ot_ref_update(qs_ot_env1)
    2026              :       ELSE
    2027          199 :          update = qs_ot_ref_update(qs_ot_env)
    2028              :       END IF
    2029              : 
    2030              :       ! SC = (S_re + i*S_im) * (C_re + i*C_im)
    2031         3478 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s, c_re, 0.0_dp, sc_re)
    2032         3478 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s_im, c_im, 0.0_dp, tmp_nk)
    2033         3478 :       CALL dbcsr_add(sc_re, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2034              : 
    2035         3478 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s, c_im, 0.0_dp, sc_im)
    2036         3478 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s_im, c_re, 0.0_dp, tmp_nk)
    2037         3478 :       CALL dbcsr_add(sc_im, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2038              : 
    2039              :       ! P = C^H*S*C.  Its imaginary component is real antisymmetric.
    2040         3478 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, c_re, sc_re, 0.0_dp, p_re)
    2041         3478 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, c_im, sc_im, 0.0_dp, tmp_kk)
    2042         3478 :       CALL dbcsr_add(p_re, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2043              : 
    2044         3478 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, c_re, sc_im, 0.0_dp, p_im)
    2045         3478 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, c_im, sc_re, 0.0_dp, tmp_kk)
    2046         3478 :       CALL dbcsr_add(p_im, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2047              : 
    2048        13912 :       ALLOCATE (eigenvalues(k), inverse_sqrt(k))
    2049              :       CALL cp_dbcsr_heevd(matrix_re=p_re, matrix_im=p_im, &
    2050              :                           eigenvectors_re=v_re, eigenvectors_im=v_im, &
    2051              :                           eigenvalues=eigenvalues, para_env=qs_ot_env%para_env, &
    2052         3478 :                           blacs_env=qs_ot_env%blacs_env)
    2053        28896 :       DO i = 1, k
    2054        25418 :          IF (eigenvalues(i) <= EPSILON(1.0_dp)) THEN
    2055            0 :             CPABORT("Complex REF overlap is not positive definite")
    2056              :          END IF
    2057        28896 :          inverse_sqrt(i) = 1.0_dp/SQRT(eigenvalues(i))
    2058              :       END DO
    2059              : 
    2060              :       ! P^(-1/2) = V*diag(lambda^(-1/2))*V^H.
    2061         3478 :       CALL dbcsr_copy(w_re, v_re)
    2062         3478 :       CALL dbcsr_copy(w_im, v_im)
    2063         3478 :       CALL dbcsr_scale_by_vector(w_re, alpha=inverse_sqrt, side='right')
    2064         3478 :       CALL dbcsr_scale_by_vector(w_im, alpha=inverse_sqrt, side='right')
    2065              : 
    2066         3478 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, w_re, v_re, 0.0_dp, p_re)
    2067         3478 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, w_im, v_im, 0.0_dp, tmp_kk)
    2068         3478 :       CALL dbcsr_add(p_re, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2069              : 
    2070         3478 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, w_im, v_re, 0.0_dp, p_im)
    2071         3478 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, w_re, v_im, 0.0_dp, tmp_kk)
    2072         3478 :       CALL dbcsr_add(p_im, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2073              : 
    2074         3478 :       CALL dbcsr_copy(f_re, p_re)
    2075         3478 :       CALL dbcsr_copy(f_im, p_im)
    2076              : 
    2077              :       ! Return the physical, orthonormal orbitals without changing a rejected
    2078              :       ! line-search coordinate.
    2079         3478 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, c_re, p_re, 0.0_dp, matrix_c)
    2080         3478 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, c_im, p_im, 0.0_dp, tmp_nk)
    2081         3478 :       CALL dbcsr_add(matrix_c, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2082              : 
    2083         3478 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, c_re, p_im, 0.0_dp, matrix_c_im)
    2084         3478 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, c_im, p_re, 0.0_dp, tmp_nk)
    2085         3478 :       CALL dbcsr_add(matrix_c_im, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2086              : 
    2087         3478 :       IF (update) THEN
    2088         1372 :          CALL dbcsr_copy(c_re, matrix_c)
    2089         1372 :          CALL dbcsr_copy(c_im, matrix_c_im)
    2090              : 
    2091         1372 :          CALL dbcsr_set(f_re, 0.0_dp)
    2092         1372 :          CALL dbcsr_add_on_diag(f_re, alpha=1.0_dp)
    2093         1372 :          CALL dbcsr_set(f_im, 0.0_dp)
    2094              : 
    2095         1372 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s, c_re, 0.0_dp, sc_re)
    2096         1372 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s_im, c_im, 0.0_dp, tmp_nk)
    2097         1372 :          CALL dbcsr_add(sc_re, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2098              : 
    2099         1372 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s, c_im, 0.0_dp, sc_im)
    2100         1372 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s_im, c_re, 0.0_dp, tmp_nk)
    2101         1372 :          CALL dbcsr_add(sc_im, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2102              :       END IF
    2103              : 
    2104         3478 :       IF (qs_ot_env%settings%do_rotation) THEN
    2105         2184 :          CALL qs_ot_generate_rotation_complex(qs_ot_env)
    2106              : 
    2107         2184 :          CALL dbcsr_copy(rotated_re, matrix_c, name="rotated_re")
    2108         2184 :          CALL dbcsr_copy(rotated_im, matrix_c_im, name="rotated_im")
    2109         2184 :          CALL dbcsr_copy(rotation_tmp, matrix_c, name="rotation_tmp")
    2110              : 
    2111              :          ! C_out = Q*U for complex Q and unitary U.
    2112              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_c, qs_ot_env%rot_mat_u, &
    2113         2184 :                              0.0_dp, rotated_re)
    2114              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_c_im, qs_ot_env%rot_mat_u_im, &
    2115         2184 :                              0.0_dp, rotation_tmp)
    2116         2184 :          CALL dbcsr_add(rotated_re, rotation_tmp, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2117              : 
    2118              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_c, qs_ot_env%rot_mat_u_im, &
    2119         2184 :                              0.0_dp, rotated_im)
    2120              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_c_im, qs_ot_env%rot_mat_u, &
    2121         2184 :                              0.0_dp, rotation_tmp)
    2122         2184 :          CALL dbcsr_add(rotated_im, rotation_tmp, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2123              : 
    2124         2184 :          CALL dbcsr_copy(matrix_c, rotated_re)
    2125         2184 :          CALL dbcsr_copy(matrix_c_im, rotated_im)
    2126         2184 :          CALL dbcsr_release(rotated_re)
    2127         2184 :          CALL dbcsr_release(rotated_im)
    2128         2184 :          CALL dbcsr_release(rotation_tmp)
    2129              :       END IF
    2130              : 
    2131         3478 :       DEALLOCATE (eigenvalues, inverse_sqrt)
    2132              : 
    2133         3478 :       CALL timestop(handle)
    2134         6956 :    END SUBROUTINE qs_ot_get_orbitals_ref_complex
    2135              : 
    2136              : ! **************************************************************************************************
    2137              : !> \brief  refinement polynomial of degree 2,3 and 4 (PRB 70, 193102 (2004))
    2138              : !> \param P ...
    2139              : !> \param FY ...
    2140              : !> \param P2 ...
    2141              : !> \param T ...
    2142              : !> \param irac_degree ...
    2143              : !> \param eps_irac_filter_matrix ...
    2144              : ! **************************************************************************************************
    2145         8934 :    SUBROUTINE qs_ot_refine(P, FY, P2, T, irac_degree, eps_irac_filter_matrix)
    2146              :       TYPE(dbcsr_type), INTENT(inout)                    :: P, FY, P2, T
    2147              :       INTEGER, INTENT(in)                                :: irac_degree
    2148              :       REAL(dp), INTENT(in)                               :: eps_irac_filter_matrix
    2149              : 
    2150              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'qs_ot_refine'
    2151              : 
    2152              :       INTEGER                                            :: handle, k
    2153              :       REAL(dp)                                           :: occ_in, occ_out, r
    2154              : 
    2155         4467 :       CALL timeset(routineN, handle)
    2156              : 
    2157         4467 :       CALL dbcsr_get_info(P, nfullcols_total=k)
    2158         4467 :       SELECT CASE (irac_degree)
    2159              :       CASE (2)
    2160              :          ! C_out = C_in * ( 15/8 * I - 10/8 * P + 3/8 * P^2)
    2161            0 :          r = 3.0_dp/8.0_dp
    2162            0 :          CALL dbcsr_multiply('N', 'N', r, P, P, 0.0_dp, FY)
    2163            0 :          IF (eps_irac_filter_matrix > 0.0_dp) THEN
    2164            0 :             occ_in = dbcsr_get_occupation(fy)
    2165            0 :             CALL dbcsr_filter(fy, eps_irac_filter_matrix)
    2166            0 :             occ_out = dbcsr_get_occupation(fy)
    2167              :          END IF
    2168            0 :          r = -10.0_dp/8.0_dp
    2169            0 :          CALL dbcsr_add(FY, P, alpha_scalar=1.0_dp, beta_scalar=r)
    2170            0 :          r = 15.0_dp/8.0_dp
    2171            0 :          CALL dbcsr_add_on_diag(FY, alpha=r)
    2172              :       CASE (3)
    2173              :          ! C_out = C_in * ( 35/16 * I - 35/16 * P + 21/16 * P^2 - 5/16 P^3)
    2174            0 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, P, P, 0.0_dp, P2)
    2175            0 :          IF (eps_irac_filter_matrix > 0.0_dp) THEN
    2176            0 :             occ_in = dbcsr_get_occupation(p2)
    2177            0 :             CALL dbcsr_filter(p2, eps_irac_filter_matrix)
    2178            0 :             occ_out = dbcsr_get_occupation(p2)
    2179              :          END IF
    2180            0 :          r = -5.0_dp/16.0_dp
    2181            0 :          CALL dbcsr_multiply('N', 'N', r, P2, P, 0.0_dp, FY)
    2182            0 :          IF (eps_irac_filter_matrix > 0.0_dp) THEN
    2183            0 :             occ_in = dbcsr_get_occupation(fy)
    2184            0 :             CALL dbcsr_filter(fy, eps_irac_filter_matrix)
    2185            0 :             occ_out = dbcsr_get_occupation(fy)
    2186              :          END IF
    2187            0 :          r = 21.0_dp/16.0_dp
    2188            0 :          CALL dbcsr_add(FY, P2, alpha_scalar=1.0_dp, beta_scalar=r)
    2189            0 :          r = -35.0_dp/16.0_dp
    2190            0 :          CALL dbcsr_add(FY, P, alpha_scalar=1.0_dp, beta_scalar=r)
    2191            0 :          r = 35.0_dp/16.0_dp
    2192            0 :          CALL dbcsr_add_on_diag(FY, alpha=r)
    2193              :       CASE (4)
    2194              :          ! C_out = C_in * ( 315/128 * I - 420/128 * P + 378/128 * P^2 - 180/128 P^3 + 35/128 P^4 )
    2195              :          !       = C_in * ( 315/128 * I - 420/128 * P + 378/128 * P^2 + ( - 180/128 * P + 35/128 * P^2 ) * P^2 )
    2196         4467 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, P, P, 0.0_dp, P2) ! P^2
    2197         4467 :          IF (eps_irac_filter_matrix > 0.0_dp) THEN
    2198            8 :             occ_in = dbcsr_get_occupation(p2)
    2199            8 :             CALL dbcsr_filter(p2, eps_irac_filter_matrix)
    2200            8 :             occ_out = dbcsr_get_occupation(p2)
    2201              :          END IF
    2202         4467 :          r = -180.0_dp/128.0_dp
    2203         4467 :          CALL dbcsr_add(T, P, alpha_scalar=0.0_dp, beta_scalar=r) ! T=-180/128*P
    2204         4467 :          r = 35.0_dp/128.0_dp
    2205         4467 :          CALL dbcsr_add(T, P2, alpha_scalar=1.0_dp, beta_scalar=r) ! T=T+35/128*P^2
    2206         4467 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, T, P2, 0.0_dp, FY) ! Y=T*P^2
    2207         4467 :          IF (eps_irac_filter_matrix > 0.0_dp) THEN
    2208            8 :             occ_in = dbcsr_get_occupation(fy)
    2209            8 :             CALL dbcsr_filter(fy, eps_irac_filter_matrix)
    2210            8 :             occ_out = dbcsr_get_occupation(fy)
    2211              :          END IF
    2212         4467 :          r = 378.0_dp/128.0_dp
    2213         4467 :          CALL dbcsr_add(FY, P2, alpha_scalar=1.0_dp, beta_scalar=r) ! Y=Y+378/128*P^2
    2214         4467 :          r = -420.0_dp/128.0_dp
    2215         4467 :          CALL dbcsr_add(FY, P, alpha_scalar=1.0_dp, beta_scalar=r) ! Y=Y-420/128*P
    2216         4467 :          r = 315.0_dp/128.0_dp
    2217         4467 :          CALL dbcsr_add_on_diag(FY, alpha=r) ! Y=Y+315/128*I
    2218              :       CASE DEFAULT
    2219         4467 :          CPABORT("This irac_order NYI")
    2220              :       END SELECT
    2221         4467 :       CALL timestop(handle)
    2222         4467 :    END SUBROUTINE qs_ot_refine
    2223              : 
    2224              : ! **************************************************************************************************
    2225              : !> \brief ...
    2226              : !> \param matrix_hc ...
    2227              : !> \param matrix_x ...
    2228              : !> \param matrix_sx ...
    2229              : !> \param matrix_gx ...
    2230              : !> \param qs_ot_env ...
    2231              : ! **************************************************************************************************
    2232         6628 :    SUBROUTINE qs_ot_get_derivative_ref(matrix_hc, matrix_x, matrix_sx, matrix_gx, &
    2233              :                                        qs_ot_env)
    2234              :       TYPE(dbcsr_type), POINTER                          :: matrix_hc, matrix_x, matrix_sx, matrix_gx
    2235              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    2236              : 
    2237              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_derivative_ref'
    2238              : 
    2239              :       INTEGER                                            :: handle, k, n
    2240              :       REAL(dp)                                           :: occ_in, occ_out
    2241              :       TYPE(dbcsr_type), POINTER                          :: C, CHC, G, HC, HC_work, SC
    2242              : 
    2243         3314 :       CALL timeset(routineN, handle)
    2244              : 
    2245         3314 :       CALL dbcsr_get_info(matrix_x, nfullrows_total=n, nfullcols_total=k)
    2246              :       !
    2247         3314 :       C => matrix_x ! NBsf*NOcc
    2248         3314 :       SC => matrix_sx ! NBsf*NOcc need to be up2date
    2249         3314 :       HC => matrix_hc ! NBsf*NOcc
    2250         3314 :       G => matrix_gx ! NBsf*NOcc
    2251         3314 :       CHC => qs_ot_env%buf1_k_k_sym ! buffer
    2252              : 
    2253         3314 :       IF (qs_ot_env%settings%do_rotation) THEN
    2254              :          ! The physical orbitals are C_current=Q*U. Pull dE/dC_current
    2255              :          ! back to the unrotated REF basis before projecting it.
    2256            8 :          CALL qs_ot_rot_mat_derivative(qs_ot_env)
    2257            8 :          HC_work => qs_ot_env%buf1_n_k
    2258              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, HC, qs_ot_env%rot_mat_u, &
    2259            8 :                              0.0_dp, HC_work)
    2260              :       ELSE
    2261              :          HC_work => HC
    2262              :       END IF
    2263              : 
    2264              :       ! C'*(H*C)
    2265         3314 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, C, HC_work, 0.0_dp, CHC)
    2266         3314 :       IF (qs_ot_env%settings%eps_irac_filter_matrix > 0.0_dp) THEN
    2267            4 :          occ_in = dbcsr_get_occupation(chc)
    2268            4 :          CALL dbcsr_filter(chc, qs_ot_env%settings%eps_irac_filter_matrix)
    2269            4 :          occ_out = dbcsr_get_occupation(chc)
    2270              :       END IF
    2271              :       ! (S*C)*(C'*H*C)
    2272         3314 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, SC, CHC, 0.0_dp, G)
    2273         3314 :       IF (qs_ot_env%settings%eps_irac_filter_matrix > 0.0_dp) THEN
    2274            4 :          occ_in = dbcsr_get_occupation(g)
    2275            4 :          CALL dbcsr_filter(g, qs_ot_env%settings%eps_irac_filter_matrix)
    2276            4 :          occ_out = dbcsr_get_occupation(g)
    2277              :       END IF
    2278              :       ! G = 2*(1-S*C*C')*H*C
    2279         3314 :       CALL dbcsr_add(G, HC_work, alpha_scalar=-1.0_dp, beta_scalar=1.0_dp)
    2280              :       !
    2281         3314 :       CALL timestop(handle)
    2282         3314 :    END SUBROUTINE qs_ot_get_derivative_ref
    2283              : 
    2284              : ! **************************************************************************************************
    2285              : !> \brief complex k-point REF derivative dE/dX from H(k)C(k), S(k)C(k), and C(k)
    2286              : !> \param matrix_hc ...
    2287              : !> \param matrix_hc_im ...
    2288              : !> \param qs_ot_env ...
    2289              : !> \param matrix_hc_rotation occupation-weighted H(k)C(k) for the rotation channel
    2290              : !> \param matrix_hc_rotation_im imaginary component of matrix_hc_rotation
    2291              : ! **************************************************************************************************
    2292         4430 :    SUBROUTINE qs_ot_get_derivative_ref_complex(matrix_hc, matrix_hc_im, qs_ot_env, &
    2293              :                                                matrix_hc_rotation, matrix_hc_rotation_im)
    2294              :       TYPE(dbcsr_type), POINTER                          :: matrix_hc, matrix_hc_im
    2295              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    2296              :       TYPE(dbcsr_type), OPTIONAL, POINTER                :: matrix_hc_rotation, matrix_hc_rotation_im
    2297              : 
    2298              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_derivative_ref_complex'
    2299              : 
    2300              :       INTEGER                                            :: handle, k, n
    2301              :       REAL(dp)                                           :: occ_in, occ_out
    2302              :       TYPE(dbcsr_type)                                   :: tmp_nk
    2303              :       TYPE(dbcsr_type), POINTER :: b_im, b_re, f_im, f_re, g_im, g_re, hc_im, hc_re, &
    2304              :          hc_rotation_im, hc_rotation_re, hc_work_im, hc_work_re, q_im, q_re, sc_im, sc_re, tmp_kk
    2305              :       TYPE(dbcsr_type), TARGET                           :: hc_rot_im, hc_rot_re
    2306              : 
    2307         2215 :       CALL timeset(routineN, handle)
    2308              : 
    2309         2215 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    2310         2215 :       CPASSERT(ASSOCIATED(matrix_hc))
    2311         2215 :       CPASSERT(ASSOCIATED(matrix_hc_im))
    2312              : 
    2313         2215 :       f_re => qs_ot_env%matrix_ref_inv_sqrt
    2314         2215 :       f_im => qs_ot_env%matrix_ref_inv_sqrt_im
    2315         2215 :       sc_re => qs_ot_env%matrix_sx
    2316         2215 :       sc_im => qs_ot_env%matrix_sx_im
    2317         2215 :       hc_re => matrix_hc
    2318         2215 :       hc_im => matrix_hc_im
    2319         2215 :       hc_rotation_re => hc_re
    2320         2215 :       hc_rotation_im => hc_im
    2321         2215 :       IF (PRESENT(matrix_hc_rotation) .OR. PRESENT(matrix_hc_rotation_im)) THEN
    2322          408 :          CPASSERT(PRESENT(matrix_hc_rotation) .AND. PRESENT(matrix_hc_rotation_im))
    2323          408 :          CPASSERT(ASSOCIATED(matrix_hc_rotation))
    2324          408 :          CPASSERT(ASSOCIATED(matrix_hc_rotation_im))
    2325          408 :          hc_rotation_re => matrix_hc_rotation
    2326          408 :          hc_rotation_im => matrix_hc_rotation_im
    2327              :       END IF
    2328         2215 :       hc_work_re => hc_re
    2329         2215 :       hc_work_im => hc_im
    2330         2215 :       g_re => qs_ot_env%matrix_gx
    2331         2215 :       g_im => qs_ot_env%matrix_gx_im
    2332         2215 :       b_re => qs_ot_env%buf1_k_k_sym
    2333         2215 :       b_im => qs_ot_env%buf2_k_k_sym
    2334         2215 :       tmp_kk => qs_ot_env%buf3_k_k_sym
    2335         2215 :       q_re => qs_ot_env%buf1_n_k
    2336         2215 :       q_im => qs_ot_env%buf1_n_k_dp
    2337              : 
    2338         2215 :       CALL dbcsr_get_info(sc_re, nfullrows_total=n, nfullcols_total=k)
    2339              : 
    2340              :       ! Q = X*(X^H*S*X)^(-1/2), reconstructed from the current REF coordinate.
    2341         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%matrix_x, f_re, 0.0_dp, q_re)
    2342         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%matrix_x_im, f_im, 0.0_dp, g_re)
    2343         2215 :       CALL dbcsr_add(q_re, g_re, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2344              : 
    2345         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%matrix_x, f_im, 0.0_dp, q_im)
    2346         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%matrix_x_im, f_re, 0.0_dp, g_re)
    2347         2215 :       CALL dbcsr_add(q_im, g_re, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2348              : 
    2349         2215 :       IF (qs_ot_env%settings%do_rotation) THEN
    2350         1415 :          CALL qs_ot_generate_rotation_complex(qs_ot_env)
    2351              : 
    2352              :          ! dF/dU = Q^H*G_C for C=Q*U.
    2353              :          CALL dbcsr_multiply('T', 'N', 1.0_dp, q_re, hc_rotation_re, &
    2354         1415 :                              0.0_dp, qs_ot_env%rot_mat_dedu)
    2355         1415 :          CALL dbcsr_multiply('T', 'N', 1.0_dp, q_im, hc_rotation_im, 0.0_dp, tmp_kk)
    2356              :          CALL dbcsr_add(qs_ot_env%rot_mat_dedu, tmp_kk, &
    2357         1415 :                         alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2358              : 
    2359              :          CALL dbcsr_multiply('T', 'N', 1.0_dp, q_re, hc_rotation_im, &
    2360         1415 :                              0.0_dp, qs_ot_env%rot_mat_dedu_im)
    2361         1415 :          CALL dbcsr_multiply('T', 'N', 1.0_dp, q_im, hc_rotation_re, 0.0_dp, tmp_kk)
    2362              :          CALL dbcsr_add(qs_ot_env%rot_mat_dedu_im, tmp_kk, &
    2363         1415 :                         alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2364         1415 :          CALL qs_ot_rot_mat_derivative_complex(qs_ot_env)
    2365              : 
    2366              :          ! The REF coordinate sees G_Q=G_C*U^H.
    2367         1415 :          CALL dbcsr_copy(hc_rot_re, hc_re, name="hc_rot_re")
    2368         1415 :          CALL dbcsr_copy(hc_rot_im, hc_im, name="hc_rot_im")
    2369         1415 :          CALL dbcsr_copy(tmp_nk, hc_re, name="tmp_nk")
    2370              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, hc_re, qs_ot_env%rot_mat_u, &
    2371         1415 :                              0.0_dp, hc_rot_re)
    2372              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, hc_im, qs_ot_env%rot_mat_u_im, &
    2373         1415 :                              0.0_dp, tmp_nk)
    2374         1415 :          CALL dbcsr_add(hc_rot_re, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2375              : 
    2376              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, hc_im, qs_ot_env%rot_mat_u, &
    2377         1415 :                              0.0_dp, hc_rot_im)
    2378              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, hc_re, qs_ot_env%rot_mat_u_im, &
    2379         1415 :                              0.0_dp, tmp_nk)
    2380         1415 :          CALL dbcsr_add(hc_rot_im, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2381         1415 :          hc_work_re => hc_rot_re
    2382         1415 :          hc_work_im => hc_rot_im
    2383              :       END IF
    2384              : 
    2385              :       ! B = Q^H*G_Q.  For uniform fixed occupations B is Hermitian.
    2386         2215 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, q_re, hc_work_re, 0.0_dp, b_re)
    2387         2215 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, q_im, hc_work_im, 0.0_dp, tmp_kk)
    2388         2215 :       CALL dbcsr_add(b_re, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2389              : 
    2390         2215 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, q_re, hc_work_im, 0.0_dp, b_im)
    2391         2215 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, q_im, hc_work_re, 0.0_dp, tmp_kk)
    2392         2215 :       CALL dbcsr_add(b_im, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2393              : 
    2394         2215 :       IF (qs_ot_env%settings%eps_irac_filter_matrix > 0.0_dp) THEN
    2395            0 :          occ_in = dbcsr_get_occupation(b_re)
    2396            0 :          CALL dbcsr_filter(b_re, qs_ot_env%settings%eps_irac_filter_matrix)
    2397            0 :          occ_out = dbcsr_get_occupation(b_re)
    2398            0 :          occ_in = dbcsr_get_occupation(b_im)
    2399            0 :          CALL dbcsr_filter(b_im, qs_ot_env%settings%eps_irac_filter_matrix)
    2400            0 :          occ_out = dbcsr_get_occupation(b_im)
    2401              :       END IF
    2402              : 
    2403              :       ! S*Q = (S*X)*F.  G is temporary storage for this pair.
    2404         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sc_re, f_re, 0.0_dp, g_re)
    2405         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sc_im, f_im, 0.0_dp, q_re)
    2406         2215 :       CALL dbcsr_add(g_re, q_re, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2407              : 
    2408         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sc_re, f_im, 0.0_dp, g_im)
    2409         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sc_im, f_re, 0.0_dp, q_re)
    2410         2215 :       CALL dbcsr_add(g_im, q_re, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2411              : 
    2412              :       ! Form (S*Q)*B.  The Q workspaces are free after B has been formed.
    2413         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, g_re, b_re, 0.0_dp, q_re)
    2414         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, g_im, b_im, 0.0_dp, q_im)
    2415         2215 :       CALL dbcsr_add(q_re, q_im, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2416              : 
    2417         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, g_re, b_im, 0.0_dp, q_im)
    2418         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, g_im, b_re, 0.0_dp, g_re)
    2419         2215 :       CALL dbcsr_add(q_im, g_re, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2420              : 
    2421         2215 :       CALL dbcsr_add(q_re, hc_work_re, alpha_scalar=-1.0_dp, beta_scalar=1.0_dp)
    2422         2215 :       CALL dbcsr_add(q_im, hc_work_im, alpha_scalar=-1.0_dp, beta_scalar=1.0_dp)
    2423              : 
    2424              :       ! Pull the physical gradient back to the finite REF coordinate: G_X = (G_C-S*Q*B)*F.
    2425         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, q_re, f_re, 0.0_dp, g_re)
    2426         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, q_im, f_im, 0.0_dp, g_im)
    2427         2215 :       CALL dbcsr_add(g_re, g_im, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2428              : 
    2429         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, q_re, f_im, 0.0_dp, g_im)
    2430         2215 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, q_im, f_re, 0.0_dp, q_re)
    2431         2215 :       CALL dbcsr_add(g_im, q_re, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2432              : 
    2433         2215 :       IF (qs_ot_env%settings%do_rotation) THEN
    2434              :          CALL qs_ot_add_ref_vertical_response_complex(b_re, b_im, f_re, f_im, &
    2435         1415 :                                                       sc_re, sc_im, g_re, g_im, qs_ot_env)
    2436              :       END IF
    2437              : 
    2438         2215 :       IF (qs_ot_env%settings%eps_irac_filter_matrix > 0.0_dp) THEN
    2439            0 :          occ_in = dbcsr_get_occupation(g_re)
    2440            0 :          CALL dbcsr_filter(g_re, qs_ot_env%settings%eps_irac_filter_matrix)
    2441            0 :          occ_out = dbcsr_get_occupation(g_re)
    2442            0 :          occ_in = dbcsr_get_occupation(g_im)
    2443            0 :          CALL dbcsr_filter(g_im, qs_ot_env%settings%eps_irac_filter_matrix)
    2444            0 :          occ_out = dbcsr_get_occupation(g_im)
    2445              :       END IF
    2446              : 
    2447         2215 :       IF (qs_ot_env%settings%do_rotation) THEN
    2448         1415 :          CALL dbcsr_release(hc_rot_re)
    2449         1415 :          CALL dbcsr_release(hc_rot_im)
    2450         1415 :          CALL dbcsr_release(tmp_nk)
    2451              :       END IF
    2452              : 
    2453         2215 :       CALL timestop(handle)
    2454              : 
    2455         2215 :    END SUBROUTINE qs_ot_get_derivative_ref_complex
    2456              : 
    2457              : ! **************************************************************************************************
    2458              : !> \brief Transpose a square DBCSR matrix while retaining its target distribution.
    2459              : !> \param matrix source matrix
    2460              : !> \param transposed transposed matrix
    2461              : !> \param identity_template square matrix template
    2462              : ! **************************************************************************************************
    2463         8528 :    SUBROUTINE qs_ot_square_transpose(matrix, transposed, identity_template)
    2464              :       TYPE(dbcsr_type)                                   :: matrix, transposed, identity_template
    2465              : 
    2466              :       TYPE(dbcsr_type)                                   :: identity
    2467              : 
    2468         8528 :       CALL dbcsr_copy(transposed, matrix, name='square_transposed')
    2469         8528 :       CALL dbcsr_copy(identity, identity_template, name='transpose_identity')
    2470         8528 :       CALL dbcsr_set(identity, 0.0_dp)
    2471         8528 :       CALL dbcsr_add_on_diag(identity, 1.0_dp)
    2472         8528 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, matrix, identity, 0.0_dp, transposed)
    2473         8528 :       CALL dbcsr_release(identity)
    2474              : 
    2475         8528 :    END SUBROUTINE qs_ot_square_transpose
    2476              : 
    2477              : ! **************************************************************************************************
    2478              : !> \brief Add the vertical response of the finite complex polar REF map.
    2479              : !> \param a_re real part of Q^H G_Q
    2480              : !> \param a_im imaginary part of Q^H G_Q
    2481              : !> \param f_re real part of (X^H S X)^(-1/2)
    2482              : !> \param f_im imaginary part of (X^H S X)^(-1/2)
    2483              : !> \param sx_re real part of S X
    2484              : !> \param sx_im imaginary part of S X
    2485              : !> \param gradient_re real REF gradient, updated in place
    2486              : !> \param gradient_im imaginary REF gradient, updated in place
    2487              : !> \param qs_ot_env complex OT environment
    2488              : ! **************************************************************************************************
    2489         1415 :    SUBROUTINE qs_ot_add_ref_vertical_response_complex(a_re, a_im, f_re, f_im, &
    2490              :                                                       sx_re, sx_im, gradient_re, gradient_im, &
    2491              :                                                       qs_ot_env)
    2492              :       TYPE(dbcsr_type)                                   :: a_re, a_im, f_re, f_im, sx_re, sx_im, &
    2493              :                                                             gradient_re, gradient_im
    2494              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    2495              : 
    2496              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_add_ref_vertical_response_complex'
    2497              : 
    2498              :       INTEGER                                            :: col, handle, i, j, k, row
    2499         1415 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_offset, row_blk_offset
    2500              :       LOGICAL                                            :: found_im, found_re
    2501              :       REAL(KIND=dp)                                      :: denominator
    2502         1415 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: f_evals
    2503         1415 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: block_im, block_re
    2504              :       TYPE(dbcsr_iterator_type)                          :: iter
    2505              :       TYPE(dbcsr_type) :: anti_im, anti_re, f_work_im, f_work_re, inner_im, inner_re, sq_im, &
    2506              :          sq_re, tmp_kk, tmp_nk, v_im, v_re, vertical_im, vertical_re, work_im, work_re, z_im, z_re
    2507              : 
    2508         1415 :       CALL timeset(routineN, handle)
    2509              : 
    2510         1415 :       CALL dbcsr_get_info(a_re, nfullrows_total=k)
    2511         1415 :       IF (k == 0) THEN
    2512            0 :          CALL timestop(handle)
    2513            0 :          RETURN
    2514              :       END IF
    2515              : 
    2516              :       ! Only the anti-Hermitian part of A=Q^H G_Q couples to the unitary
    2517              :       ! component of the polar differential.
    2518         1415 :       CALL qs_ot_square_transpose(a_re, tmp_kk, qs_ot_env%rot_mat_u)
    2519         1415 :       CALL dbcsr_copy(anti_re, a_re, name='anti_re')
    2520         1415 :       CALL dbcsr_add(anti_re, tmp_kk, alpha_scalar=0.5_dp, beta_scalar=-0.5_dp)
    2521         1415 :       CALL qs_ot_square_transpose(a_im, tmp_kk, qs_ot_env%rot_mat_u)
    2522         1415 :       CALL dbcsr_copy(anti_im, a_im, name='anti_im')
    2523         1415 :       CALL dbcsr_add(anti_im, tmp_kk, alpha_scalar=0.5_dp, beta_scalar=0.5_dp)
    2524              : 
    2525              :       ! P=(X^H S X)^(1/2)=F^(-1).  In the eigenbasis of F, solve
    2526              :       ! P Z + Z P = antiherm(A) element by element.
    2527         1415 :       CALL dbcsr_copy(f_work_re, f_re, name='f_work_re')
    2528         1415 :       CALL dbcsr_copy(f_work_im, f_im, name='f_work_im')
    2529         1415 :       CALL dbcsr_copy(v_re, f_re, name='v_re')
    2530         1415 :       CALL dbcsr_copy(v_im, f_im, name='v_im')
    2531         4245 :       ALLOCATE (f_evals(k))
    2532              :       CALL cp_dbcsr_heevd(matrix_re=f_work_re, matrix_im=f_work_im, &
    2533              :                           eigenvectors_re=v_re, eigenvectors_im=v_im, eigenvalues=f_evals, &
    2534         1415 :                           para_env=qs_ot_env%para_env, blacs_env=qs_ot_env%blacs_env)
    2535        10605 :       IF (MINVAL(f_evals) <= EPSILON(1.0_dp)) THEN
    2536            0 :          CPABORT('Complex REF inverse square root is not positive definite')
    2537              :       END IF
    2538              : 
    2539              :       ! inner = V^H antiherm(A) V.
    2540         1415 :       CALL dbcsr_copy(work_re, anti_re, name='work_re')
    2541         1415 :       CALL dbcsr_copy(work_im, anti_im, name='work_im')
    2542         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, anti_re, v_re, 0.0_dp, work_re)
    2543         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, anti_im, v_im, 0.0_dp, tmp_kk)
    2544         1415 :       CALL dbcsr_add(work_re, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2545         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, anti_re, v_im, 0.0_dp, work_im)
    2546         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, anti_im, v_re, 0.0_dp, tmp_kk)
    2547         1415 :       CALL dbcsr_add(work_im, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2548              : 
    2549         1415 :       CALL dbcsr_copy(inner_re, anti_re, name='inner_re')
    2550         1415 :       CALL dbcsr_copy(inner_im, anti_im, name='inner_im')
    2551         1415 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, v_re, work_re, 0.0_dp, inner_re)
    2552         1415 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, v_im, work_im, 1.0_dp, inner_re)
    2553         1415 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, v_re, work_im, 0.0_dp, inner_im)
    2554         1415 :       CALL dbcsr_multiply('T', 'N', -1.0_dp, v_im, work_re, 1.0_dp, inner_im)
    2555              : 
    2556              :       CALL dbcsr_get_info(inner_re, row_blk_offset=row_blk_offset, &
    2557         1415 :                           col_blk_offset=col_blk_offset)
    2558         1415 :       CALL dbcsr_iterator_start(iter, inner_re)
    2559         2384 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    2560          969 :          CALL dbcsr_iterator_next_block(iter, row, col)
    2561          969 :          CALL dbcsr_get_block_p(inner_re, row, col, block_re, found_re)
    2562          969 :          CALL dbcsr_get_block_p(inner_im, row, col, block_im, found_im)
    2563          969 :          CPASSERT(found_re .AND. found_im)
    2564         9186 :          DO i = 1, SIZE(block_re, 1)
    2565        78339 :          DO j = 1, SIZE(block_re, 2)
    2566              :             denominator = 1.0_dp/f_evals(row_blk_offset(row) + i - 1) + &
    2567        70568 :                           1.0_dp/f_evals(col_blk_offset(col) + j - 1)
    2568        70568 :             block_re(i, j) = block_re(i, j)/denominator
    2569        77370 :             block_im(i, j) = block_im(i, j)/denominator
    2570              :          END DO
    2571              :          END DO
    2572              :       END DO
    2573         1415 :       CALL dbcsr_iterator_stop(iter)
    2574              : 
    2575              :       ! Z = V inner V^H.
    2576         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, v_re, inner_re, 0.0_dp, work_re)
    2577         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, v_im, inner_im, 0.0_dp, tmp_kk)
    2578         1415 :       CALL dbcsr_add(work_re, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2579         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, v_re, inner_im, 0.0_dp, work_im)
    2580         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, v_im, inner_re, 0.0_dp, tmp_kk)
    2581         1415 :       CALL dbcsr_add(work_im, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2582              : 
    2583         1415 :       CALL dbcsr_copy(z_re, anti_re, name='z_re')
    2584         1415 :       CALL dbcsr_copy(z_im, anti_im, name='z_im')
    2585         1415 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, work_re, v_re, 0.0_dp, z_re)
    2586         1415 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, work_im, v_im, 1.0_dp, z_re)
    2587         1415 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, work_im, v_re, 0.0_dp, z_im)
    2588         1415 :       CALL dbcsr_multiply('N', 'T', -1.0_dp, work_re, v_im, 1.0_dp, z_im)
    2589              : 
    2590              :       ! The adjoint vertical contribution is 2 S Q Z, with S Q=(S X)F.
    2591         1415 :       CALL dbcsr_copy(sq_re, sx_re, name='sq_re')
    2592         1415 :       CALL dbcsr_copy(sq_im, sx_im, name='sq_im')
    2593         1415 :       CALL dbcsr_copy(tmp_nk, sx_re, name='tmp_nk')
    2594         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sx_re, f_re, 0.0_dp, sq_re)
    2595         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sx_im, f_im, 0.0_dp, tmp_nk)
    2596         1415 :       CALL dbcsr_add(sq_re, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2597         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sx_re, f_im, 0.0_dp, sq_im)
    2598         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sx_im, f_re, 0.0_dp, tmp_nk)
    2599         1415 :       CALL dbcsr_add(sq_im, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2600              : 
    2601         1415 :       CALL dbcsr_copy(vertical_re, sx_re, name='vertical_re')
    2602         1415 :       CALL dbcsr_copy(vertical_im, sx_im, name='vertical_im')
    2603         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sq_re, z_re, 0.0_dp, vertical_re)
    2604         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sq_im, z_im, 0.0_dp, tmp_nk)
    2605         1415 :       CALL dbcsr_add(vertical_re, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2606         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sq_re, z_im, 0.0_dp, vertical_im)
    2607         1415 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sq_im, z_re, 0.0_dp, tmp_nk)
    2608         1415 :       CALL dbcsr_add(vertical_im, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2609         1415 :       CALL dbcsr_add(gradient_re, vertical_re, alpha_scalar=1.0_dp, beta_scalar=2.0_dp)
    2610         1415 :       CALL dbcsr_add(gradient_im, vertical_im, alpha_scalar=1.0_dp, beta_scalar=2.0_dp)
    2611              : 
    2612         1415 :       DEALLOCATE (f_evals)
    2613         1415 :       CALL dbcsr_release(anti_im)
    2614         1415 :       CALL dbcsr_release(anti_re)
    2615         1415 :       CALL dbcsr_release(f_work_im)
    2616         1415 :       CALL dbcsr_release(f_work_re)
    2617         1415 :       CALL dbcsr_release(inner_im)
    2618         1415 :       CALL dbcsr_release(inner_re)
    2619         1415 :       CALL dbcsr_release(sq_im)
    2620         1415 :       CALL dbcsr_release(sq_re)
    2621         1415 :       CALL dbcsr_release(tmp_kk)
    2622         1415 :       CALL dbcsr_release(tmp_nk)
    2623         1415 :       CALL dbcsr_release(v_im)
    2624         1415 :       CALL dbcsr_release(v_re)
    2625         1415 :       CALL dbcsr_release(vertical_im)
    2626         1415 :       CALL dbcsr_release(vertical_re)
    2627         1415 :       CALL dbcsr_release(work_im)
    2628         1415 :       CALL dbcsr_release(work_re)
    2629         1415 :       CALL dbcsr_release(z_im)
    2630         1415 :       CALL dbcsr_release(z_re)
    2631              : 
    2632         1415 :       CALL timestop(handle)
    2633              : 
    2634         2830 :    END SUBROUTINE qs_ot_add_ref_vertical_response_complex
    2635              : 
    2636              : ! **************************************************************************************************
    2637              : !> \brief computes p=x*S*x and the matrix functionals related matrices
    2638              : !> \param matrix_x ...
    2639              : !> \param matrix_sx ...
    2640              : !> \param qs_ot_env ...
    2641              : ! **************************************************************************************************
    2642       333045 :    SUBROUTINE qs_ot_get_p(matrix_x, matrix_sx, qs_ot_env)
    2643              : 
    2644              :       TYPE(dbcsr_type), POINTER                          :: matrix_x, matrix_sx
    2645              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    2646              : 
    2647              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'qs_ot_get_p'
    2648              :       REAL(KIND=dp), PARAMETER                           :: rone = 1.0_dp, rzero = 0.0_dp
    2649              : 
    2650              :       INTEGER                                            :: handle, k, max_iter, n
    2651              :       LOGICAL                                            :: converged
    2652              :       REAL(KIND=dp)                                      :: max_ev, min_ev, threshold
    2653              : 
    2654       111015 :       CALL timeset(routineN, handle)
    2655              : 
    2656       111015 :       CALL dbcsr_get_info(matrix_x, nfullrows_total=n, nfullcols_total=k)
    2657              : 
    2658              :       ! get the overlap
    2659              :       CALL dbcsr_multiply('T', 'N', rone, matrix_x, matrix_sx, rzero, &
    2660       111015 :                           qs_ot_env%matrix_p)
    2661              : 
    2662              :       ! get an upper bound for the largest eigenvalue
    2663              :       ! try using lancos first and fall back to gershgorin norm if it fails
    2664       111015 :       max_iter = 30; threshold = 1.0E-03_dp
    2665       111015 :       CALL arnoldi_extremal(qs_ot_env%matrix_p, max_ev, min_ev, converged, threshold, max_iter)
    2666       111015 :       qs_ot_env%largest_eval_upper_bound = MAX(max_ev, ABS(min_ev))
    2667              : 
    2668       111015 :       IF (.NOT. converged) qs_ot_env%largest_eval_upper_bound = dbcsr_gershgorin_norm(qs_ot_env%matrix_p)
    2669       111015 :       CALL decide_strategy(qs_ot_env)
    2670       111015 :       IF (qs_ot_env%do_taylor) THEN
    2671        59916 :          CALL qs_ot_p2m_taylor(qs_ot_env)
    2672              :       ELSE
    2673        51099 :          CALL qs_ot_p2m_diag(qs_ot_env)
    2674              :       END IF
    2675              : 
    2676       111015 :       IF (qs_ot_env%settings%do_rotation) THEN
    2677         3142 :          CALL qs_ot_generate_rotation(qs_ot_env)
    2678              :       END IF
    2679              : 
    2680       111015 :       CALL timestop(handle)
    2681              : 
    2682       111015 :    END SUBROUTINE qs_ot_get_p
    2683              : 
    2684              : ! **************************************************************************************************
    2685              : !> \brief computes U=exp(A) for the complex anti-Hermitian generator
    2686              : !>        A=rot_mat_x+i*rot_mat_x_im
    2687              : !> \param qs_ot_env a complex k-point OT environment
    2688              : ! **************************************************************************************************
    2689         4931 :    SUBROUTINE qs_ot_generate_rotation_complex(qs_ot_env)
    2690              : 
    2691              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    2692              : 
    2693              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_generate_rotation_complex'
    2694              : 
    2695              :       INTEGER                                            :: handle, k
    2696              :       REAL(KIND=dp)                                      :: rot_norm
    2697         4931 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: exp_evals_im, exp_evals_re
    2698              :       TYPE(dbcsr_type)                                   :: h_re, tmp, w_im, w_re
    2699              : 
    2700         4931 :       CALL timeset(routineN, handle)
    2701              : 
    2702         4931 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    2703         4931 :       CPASSERT(ASSOCIATED(qs_ot_env%rot_mat_x_im))
    2704         4931 :       CPASSERT(ASSOCIATED(qs_ot_env%rot_mat_u_im))
    2705              : 
    2706         4931 :       CALL dbcsr_get_info(qs_ot_env%rot_mat_x, nfullrows_total=k)
    2707         4931 :       IF (k /= 0) THEN
    2708              :          rot_norm = dbcsr_frobenius_norm(qs_ot_env%rot_mat_x) + &
    2709         4931 :                     dbcsr_frobenius_norm(qs_ot_env%rot_mat_x_im)
    2710         4931 :          IF (rot_norm <= EPSILON(1.0_dp)) THEN
    2711          990 :             CALL dbcsr_set(qs_ot_env%rot_mat_u, 0.0_dp)
    2712          990 :             CALL dbcsr_add_on_diag(qs_ot_env%rot_mat_u, 1.0_dp)
    2713          990 :             CALL dbcsr_set(qs_ot_env%rot_mat_u_im, 0.0_dp)
    2714          990 :             CALL timestop(handle)
    2715              :             RETURN
    2716              :          END IF
    2717              : 
    2718              :          ! i*A = i*X-Y is Hermitian. Its eigenvectors give
    2719              :          ! exp(A)=V*diag(exp(-i*lambda))*V^H.
    2720         3941 :          CALL dbcsr_copy(h_re, qs_ot_env%rot_mat_x_im, name="h_re")
    2721         3941 :          CALL dbcsr_scale(h_re, -1.0_dp)
    2722              :          CALL cp_dbcsr_heevd(matrix_re=h_re, matrix_im=qs_ot_env%rot_mat_x, &
    2723              :                              eigenvectors_re=qs_ot_env%rot_mat_evec_re, &
    2724              :                              eigenvectors_im=qs_ot_env%rot_mat_evec_im, &
    2725              :                              eigenvalues=qs_ot_env%rot_mat_evals, &
    2726         3941 :                              para_env=qs_ot_env%para_env, blacs_env=qs_ot_env%blacs_env)
    2727              : 
    2728        15764 :          ALLOCATE (exp_evals_re(k), exp_evals_im(k))
    2729        36059 :          exp_evals_re(:) = COS(-qs_ot_env%rot_mat_evals(:))
    2730        36059 :          exp_evals_im(:) = SIN(-qs_ot_env%rot_mat_evals(:))
    2731              : 
    2732         3941 :          CALL dbcsr_copy(w_re, qs_ot_env%rot_mat_evec_re, name="w_re")
    2733         3941 :          CALL dbcsr_scale_by_vector(w_re, alpha=exp_evals_re, side='right')
    2734         3941 :          CALL dbcsr_copy(tmp, qs_ot_env%rot_mat_evec_im, name="tmp")
    2735         3941 :          CALL dbcsr_scale_by_vector(tmp, alpha=exp_evals_im, side='right')
    2736         3941 :          CALL dbcsr_add(w_re, tmp, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2737              : 
    2738         3941 :          CALL dbcsr_copy(w_im, qs_ot_env%rot_mat_evec_im, name="w_im")
    2739         3941 :          CALL dbcsr_scale_by_vector(w_im, alpha=exp_evals_re, side='right')
    2740         3941 :          CALL dbcsr_copy(tmp, qs_ot_env%rot_mat_evec_re)
    2741         3941 :          CALL dbcsr_scale_by_vector(tmp, alpha=exp_evals_im, side='right')
    2742         3941 :          CALL dbcsr_add(w_im, tmp, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2743              : 
    2744              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, w_re, qs_ot_env%rot_mat_evec_re, &
    2745         3941 :                              0.0_dp, qs_ot_env%rot_mat_u)
    2746              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, w_im, qs_ot_env%rot_mat_evec_im, &
    2747         3941 :                              1.0_dp, qs_ot_env%rot_mat_u)
    2748              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, w_im, qs_ot_env%rot_mat_evec_re, &
    2749         3941 :                              0.0_dp, qs_ot_env%rot_mat_u_im)
    2750              :          CALL dbcsr_multiply('N', 'T', -1.0_dp, w_re, qs_ot_env%rot_mat_evec_im, &
    2751         3941 :                              1.0_dp, qs_ot_env%rot_mat_u_im)
    2752              : 
    2753         3941 :          CALL dbcsr_release(h_re)
    2754         3941 :          CALL dbcsr_release(tmp)
    2755         3941 :          CALL dbcsr_release(w_re)
    2756         3941 :          CALL dbcsr_release(w_im)
    2757         3941 :          DEALLOCATE (exp_evals_re, exp_evals_im)
    2758              :       END IF
    2759              : 
    2760         3941 :       CALL timestop(handle)
    2761              : 
    2762         8872 :    END SUBROUTINE qs_ot_generate_rotation_complex
    2763              : 
    2764              : ! **************************************************************************************************
    2765              : !> \brief pull the complex dE/dU covector back to the anti-Hermitian generator
    2766              : !>        using the adjoint Frechet derivative of exp
    2767              : !> \param qs_ot_env a complex k-point OT environment with an up-to-date U
    2768              : ! **************************************************************************************************
    2769         4126 :    SUBROUTINE qs_ot_rot_mat_derivative_complex(qs_ot_env)
    2770              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    2771              : 
    2772              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_rot_mat_derivative_complex'
    2773              : 
    2774              :       INTEGER                                            :: handle, k
    2775              :       REAL(KIND=dp)                                      :: rot_norm
    2776              :       TYPE(dbcsr_type)                                   :: frechet_im, frechet_re, inner_deriv_im, &
    2777              :                                                             inner_deriv_re, outer_deriv_im, &
    2778              :                                                             outer_deriv_re, tmp, work_im, work_re
    2779              : 
    2780         2063 :       CALL timeset(routineN, handle)
    2781              : 
    2782         2063 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    2783         2063 :       CPASSERT(ASSOCIATED(qs_ot_env%rot_mat_dedu_im))
    2784         2063 :       CPASSERT(ASSOCIATED(qs_ot_env%rot_mat_gx_im))
    2785              : 
    2786         2063 :       CALL dbcsr_get_info(qs_ot_env%rot_mat_u, nfullrows_total=k)
    2787         2063 :       IF (k /= 0) THEN
    2788              :          rot_norm = dbcsr_frobenius_norm(qs_ot_env%rot_mat_x) + &
    2789         2063 :                     dbcsr_frobenius_norm(qs_ot_env%rot_mat_x_im)
    2790         2063 :          IF (rot_norm <= EPSILON(1.0_dp)) THEN
    2791          474 :             CALL qs_ot_square_transpose(qs_ot_env%rot_mat_dedu, tmp, qs_ot_env%rot_mat_u)
    2792          474 :             CALL dbcsr_copy(qs_ot_env%rot_mat_gx, qs_ot_env%rot_mat_dedu)
    2793              :             CALL dbcsr_add(qs_ot_env%rot_mat_gx, tmp, &
    2794          474 :                            alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2795          474 :             CALL dbcsr_release(tmp)
    2796              : 
    2797          474 :             CALL qs_ot_square_transpose(qs_ot_env%rot_mat_dedu_im, tmp, qs_ot_env%rot_mat_u)
    2798          474 :             CALL dbcsr_copy(qs_ot_env%rot_mat_gx_im, qs_ot_env%rot_mat_dedu_im)
    2799              :             CALL dbcsr_add(qs_ot_env%rot_mat_gx_im, tmp, &
    2800          474 :                            alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2801          474 :             CALL dbcsr_release(tmp)
    2802          474 :             CALL timestop(handle)
    2803          474 :             RETURN
    2804              :          END IF
    2805              : 
    2806         1589 :          CALL dbcsr_copy(work_re, qs_ot_env%rot_mat_dedu, name="work_re")
    2807         1589 :          CALL dbcsr_copy(work_im, qs_ot_env%rot_mat_dedu, name="work_im")
    2808         1589 :          CALL dbcsr_copy(tmp, qs_ot_env%rot_mat_dedu, name="tmp")
    2809         1589 :          CALL dbcsr_copy(inner_deriv_re, qs_ot_env%rot_mat_dedu, name="inner_deriv_re")
    2810         1589 :          CALL dbcsr_copy(inner_deriv_im, qs_ot_env%rot_mat_dedu, name="inner_deriv_im")
    2811              : 
    2812              :          ! V^H*(dE/dU)*V, split into real and imaginary parts.
    2813              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%rot_mat_dedu, &
    2814         1589 :                              qs_ot_env%rot_mat_evec_re, 0.0_dp, work_re)
    2815              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%rot_mat_dedu_im, &
    2816         1589 :                              qs_ot_env%rot_mat_evec_im, 0.0_dp, tmp)
    2817         1589 :          CALL dbcsr_add(work_re, tmp, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2818              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%rot_mat_dedu, &
    2819         1589 :                              qs_ot_env%rot_mat_evec_im, 0.0_dp, work_im)
    2820              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%rot_mat_dedu_im, &
    2821         1589 :                              qs_ot_env%rot_mat_evec_re, 0.0_dp, tmp)
    2822         1589 :          CALL dbcsr_add(work_im, tmp, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2823              :          CALL dbcsr_multiply('T', 'N', 1.0_dp, qs_ot_env%rot_mat_evec_re, &
    2824         1589 :                              work_re, 0.0_dp, inner_deriv_re)
    2825              :          CALL dbcsr_multiply('T', 'N', 1.0_dp, qs_ot_env%rot_mat_evec_im, &
    2826         1589 :                              work_im, 1.0_dp, inner_deriv_re)
    2827              :          CALL dbcsr_multiply('T', 'N', 1.0_dp, qs_ot_env%rot_mat_evec_re, &
    2828         1589 :                              work_im, 0.0_dp, inner_deriv_im)
    2829              :          CALL dbcsr_multiply('T', 'N', -1.0_dp, qs_ot_env%rot_mat_evec_im, &
    2830         1589 :                              work_re, 1.0_dp, inner_deriv_im)
    2831              : 
    2832              :          CALL qs_ot_apply_complex_frechet_dbcsr(qs_ot_env%rot_mat_evals, &
    2833              :                                                 inner_deriv_re, inner_deriv_im, &
    2834         1589 :                                                 outer_deriv_re, outer_deriv_im, adjoint=.TRUE.)
    2835              : 
    2836              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%rot_mat_evec_re, &
    2837         1589 :                              outer_deriv_re, 0.0_dp, work_re)
    2838              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%rot_mat_evec_im, &
    2839         1589 :                              outer_deriv_im, 0.0_dp, tmp)
    2840         1589 :          CALL dbcsr_add(work_re, tmp, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2841              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%rot_mat_evec_re, &
    2842         1589 :                              outer_deriv_im, 0.0_dp, work_im)
    2843              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%rot_mat_evec_im, &
    2844         1589 :                              outer_deriv_re, 0.0_dp, tmp)
    2845         1589 :          CALL dbcsr_add(work_im, tmp, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2846              : 
    2847         1589 :          CALL dbcsr_copy(frechet_re, qs_ot_env%rot_mat_dedu, name="frechet_re")
    2848         1589 :          CALL dbcsr_copy(frechet_im, qs_ot_env%rot_mat_dedu, name="frechet_im")
    2849              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, work_re, qs_ot_env%rot_mat_evec_re, &
    2850         1589 :                              0.0_dp, frechet_re)
    2851              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, work_im, qs_ot_env%rot_mat_evec_im, &
    2852         1589 :                              1.0_dp, frechet_re)
    2853              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, work_im, qs_ot_env%rot_mat_evec_re, &
    2854         1589 :                              0.0_dp, frechet_im)
    2855              :          CALL dbcsr_multiply('N', 'T', -1.0_dp, work_re, qs_ot_env%rot_mat_evec_im, &
    2856         1589 :                              1.0_dp, frechet_im)
    2857              : 
    2858              :          ! Tangents satisfy X^T=-X and Y^T=Y for A=X+iY.
    2859         1589 :          CALL qs_ot_square_transpose(frechet_re, tmp, qs_ot_env%rot_mat_u)
    2860         1589 :          CALL dbcsr_copy(qs_ot_env%rot_mat_gx, frechet_re)
    2861              :          CALL dbcsr_add(qs_ot_env%rot_mat_gx, tmp, &
    2862         1589 :                         alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2863         1589 :          CALL qs_ot_square_transpose(frechet_im, tmp, qs_ot_env%rot_mat_u)
    2864         1589 :          CALL dbcsr_copy(qs_ot_env%rot_mat_gx_im, frechet_im)
    2865              :          CALL dbcsr_add(qs_ot_env%rot_mat_gx_im, tmp, &
    2866         1589 :                         alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2867              : 
    2868         1589 :          CALL dbcsr_release(frechet_re)
    2869         1589 :          CALL dbcsr_release(frechet_im)
    2870         1589 :          CALL dbcsr_release(inner_deriv_re)
    2871         1589 :          CALL dbcsr_release(inner_deriv_im)
    2872         1589 :          CALL dbcsr_release(outer_deriv_re)
    2873         1589 :          CALL dbcsr_release(outer_deriv_im)
    2874         1589 :          CALL dbcsr_release(tmp)
    2875         1589 :          CALL dbcsr_release(work_re)
    2876         1589 :          CALL dbcsr_release(work_im)
    2877              :       END IF
    2878              : 
    2879         1589 :       CALL timestop(handle)
    2880              : 
    2881         2063 :    END SUBROUTINE qs_ot_rot_mat_derivative_complex
    2882              : 
    2883              : ! **************************************************************************************************
    2884              : !> \brief compute P=X^H*S*X and the STRICT matrix functions for a complex K-point channel
    2885              : !> \param matrix_x real part of X
    2886              : !> \param matrix_x_im imaginary part of X
    2887              : !> \param matrix_sx real part of S*X
    2888              : !> \param matrix_sx_im imaginary part of S*X
    2889              : !> \param qs_ot_env OT channel state
    2890              : ! **************************************************************************************************
    2891         3552 :    SUBROUTINE qs_ot_get_p_complex(matrix_x, matrix_x_im, matrix_sx, matrix_sx_im, qs_ot_env)
    2892              :       TYPE(dbcsr_type), POINTER                          :: matrix_x, matrix_x_im, matrix_sx, &
    2893              :                                                             matrix_sx_im
    2894              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    2895              : 
    2896              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_p_complex'
    2897              : 
    2898              :       INTEGER                                            :: handle
    2899              : 
    2900         3552 :       CALL timeset(routineN, handle)
    2901              : 
    2902         3552 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    2903         3552 :       CPASSERT(qs_ot_env%settings%ot_algorithm == 'TOD')
    2904              : 
    2905              :       CALL qs_ot_complex_multiply('C', 'N', matrix_x, matrix_x_im, matrix_sx, matrix_sx_im, &
    2906         3552 :                                   qs_ot_env%matrix_p, qs_ot_env%matrix_p_im, qs_ot_env%matrix_buf1)
    2907         3552 :       qs_ot_env%do_taylor = .FALSE.
    2908         3552 :       CALL qs_ot_p2m_diag_complex(qs_ot_env)
    2909              : 
    2910         3552 :       CALL timestop(handle)
    2911              : 
    2912         3552 :    END SUBROUTINE qs_ot_get_p_complex
    2913              : 
    2914              : ! **************************************************************************************************
    2915              : !> \brief computes the rotation matrix rot_mat_u that is associated to a given
    2916              : !>        rot_mat_x using rot_mat_u=exp(rot_mat_x)
    2917              : !> \param qs_ot_env a valid qs_ot_env
    2918              : !> \par History
    2919              : !>      08.2004 created [Joost VandeVondele]
    2920              : !>      12.2024 Rewrite to use only real matrices [Ole Schuett]
    2921              : ! **************************************************************************************************
    2922         3158 :    SUBROUTINE qs_ot_generate_rotation(qs_ot_env)
    2923              : 
    2924              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    2925              : 
    2926              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_generate_rotation'
    2927              : 
    2928              :       INTEGER                                            :: handle, k
    2929         3158 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: exp_evals_im, exp_evals_re
    2930              :       TYPE(dbcsr_type)                                   :: buf_1, buf_2
    2931              : 
    2932         3158 :       CALL timeset(routineN, handle)
    2933              : 
    2934         3158 :       CALL dbcsr_get_info(qs_ot_env%rot_mat_x, nfullrows_total=k)
    2935              : 
    2936         3158 :       IF (k /= 0) THEN
    2937              :          ! We want to compute: rot_mat_u = exp(i*rot_mat_x)
    2938              : 
    2939              :          ! Diagonalize: matrix = i*rot_mat_x.
    2940              :          ! Note that matrix is imaginary and hermitian because rot_mat_x is real and anti-symmetric.
    2941              :          CALL cp_dbcsr_heevd(matrix_im=qs_ot_env%rot_mat_x, &  ! matrix_re omitted because it's zero
    2942              :                              eigenvectors_re=qs_ot_env%rot_mat_evec_re, &
    2943              :                              eigenvectors_im=qs_ot_env%rot_mat_evec_im, &
    2944              :                              eigenvalues=qs_ot_env%rot_mat_evals, &
    2945              :                              para_env=qs_ot_env%para_env, &
    2946         3106 :                              blacs_env=qs_ot_env%blacs_env)
    2947              : 
    2948              :          ! Compute: exp_evals = EXP(-i*rot_mat_evals)
    2949        12424 :          ALLOCATE (exp_evals_re(k), exp_evals_im(k))
    2950        16766 :          exp_evals_re(:) = COS(-qs_ot_env%rot_mat_evals(:))
    2951        16766 :          exp_evals_im(:) = SIN(-qs_ot_env%rot_mat_evals(:))
    2952              : 
    2953              :          ! Compute: rot_mat_u = \sum_ij exp_evals_ij * |rot_mat_evec_i> <rot_mat_evec_j|
    2954              :          ! Note that we need only two matrix multiplications because rot_mat_u is real.
    2955         3106 :          CALL dbcsr_copy(buf_1, qs_ot_env%rot_mat_evec_re, name="buf_1")
    2956         3106 :          CALL dbcsr_scale_by_vector(buf_1, alpha=exp_evals_re, side='right')
    2957         3106 :          CALL dbcsr_copy(buf_2, qs_ot_env%rot_mat_evec_im, name="buf_2")
    2958         3106 :          CALL dbcsr_scale_by_vector(buf_2, alpha=exp_evals_im, side='right')
    2959         3106 :          CALL dbcsr_add(buf_1, buf_2, alpha_scalar=+1.0_dp, beta_scalar=-1.0_dp)
    2960         3106 :          CALL dbcsr_multiply('N', 'T', 1.0_dp, buf_1, qs_ot_env%rot_mat_evec_re, 0.0_dp, qs_ot_env%rot_mat_u)
    2961              : 
    2962         3106 :          CALL dbcsr_copy(buf_1, qs_ot_env%rot_mat_evec_im)
    2963         3106 :          CALL dbcsr_scale_by_vector(buf_1, alpha=exp_evals_re, side='right')
    2964         3106 :          CALL dbcsr_copy(buf_2, qs_ot_env%rot_mat_evec_re)
    2965         3106 :          CALL dbcsr_scale_by_vector(buf_2, alpha=exp_evals_im, side='right')
    2966         3106 :          CALL dbcsr_add(buf_1, buf_2, alpha_scalar=+1.0_dp, beta_scalar=+1.0_dp)
    2967         3106 :          CALL dbcsr_multiply('N', 'T', 1.0_dp, buf_1, qs_ot_env%rot_mat_evec_im, 1.0_dp, qs_ot_env%rot_mat_u)
    2968              : 
    2969              :          ! Clean up.
    2970         3106 :          CALL dbcsr_release(buf_1)
    2971         3106 :          CALL dbcsr_release(buf_2)
    2972         3106 :          DEALLOCATE (exp_evals_re, exp_evals_im)
    2973              :       END IF
    2974              : 
    2975         3158 :       CALL timestop(handle)
    2976              : 
    2977         6316 :    END SUBROUTINE qs_ot_generate_rotation
    2978              : 
    2979              : ! **************************************************************************************************
    2980              : !> \brief computes the derivative fields with respect to rot_mat_x
    2981              : !> \param qs_ot_env valid qs_ot_env. In particular qs_ot_generate_rotation has to be called before
    2982              : !>                        and the rot_mat_dedu matrix has to be up to date
    2983              : !> \par History
    2984              : !>      08.2004 created [ Joost VandeVondele ]
    2985              : !>      12.2024 Rewrite to use only real matrices [Ole Schuett]
    2986              : ! **************************************************************************************************
    2987         1598 :    SUBROUTINE qs_ot_rot_mat_derivative(qs_ot_env)
    2988              :       TYPE(qs_ot_type)                         :: qs_ot_env
    2989              : 
    2990              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_rot_mat_derivative'
    2991              : 
    2992              :       INTEGER                                  :: col, handle, i, iblock, j, k, max_blocks, nblocks, row
    2993         1598 :       INTEGER, ALLOCATABLE, DIMENSION(:)       :: cols, rows
    2994         1598 :       INTEGER, DIMENSION(:), POINTER           :: col_blk_offset, col_blk_size, row_blk_offset, row_blk_size
    2995              :       REAL(KIND=dp)                            :: e1, e2
    2996              :       TYPE(dbcsr_type)                         :: outer_deriv_re, outer_deriv_im, mat_buf, &
    2997              :                                                   inner_deriv_re, inner_deriv_im
    2998              :       TYPE(dbcsr_distribution_type)            :: dist
    2999              :       TYPE(dbcsr_iterator_type)                :: iter
    3000         1598 :       REAL(dp), DIMENSION(:, :), POINTER       :: block_in_re, block_in_im, block_out_re, block_out_im
    3001              :       LOGICAL                                  :: duplicate, found_in_im, found_in_re, found_out_im, &
    3002              :                                                   found_out_re
    3003              :       REAL(KIND=dp)                            :: im_part, re_part
    3004              :       COMPLEX(dp)                              :: cval_in, cval_out
    3005         1598 :       CALL timeset(routineN, handle)
    3006              : 
    3007         1598 :       CALL dbcsr_get_info(qs_ot_env%rot_mat_u, nfullrows_total=k)
    3008         1598 :       IF (k /= 0) THEN
    3009         1572 :          CALL dbcsr_copy(qs_ot_env%matrix_buf1, qs_ot_env%rot_mat_dedu)
    3010              :          ! now we get to the derivative wrt the antisymmetric matrix rot_mat_x
    3011         1572 :          CALL dbcsr_copy(mat_buf, qs_ot_env%rot_mat_dedu, "mat_buf")
    3012              : 
    3013              :          ! inner_deriv_ij = <rot_mat_evec_i| rot_mat_dedu |rot_mat_evec_j>
    3014         1572 :          CALL dbcsr_copy(inner_deriv_re, qs_ot_env%rot_mat_dedu, "inner_deriv_re") ! TODO just create
    3015         1572 :          CALL dbcsr_copy(inner_deriv_im, qs_ot_env%rot_mat_dedu, "inner_deriv_im") ! TODO just create
    3016              : 
    3017         1572 :          CALL dbcsr_multiply('T', 'N', +1.0_dp, qs_ot_env%rot_mat_dedu, qs_ot_env%rot_mat_evec_im, 0.0_dp, mat_buf)
    3018         1572 :          CALL dbcsr_multiply('T', 'N', +1.0_dp, qs_ot_env%rot_mat_evec_im, mat_buf, 0.0_dp, inner_deriv_re)
    3019         1572 :          CALL dbcsr_multiply('T', 'N', +1.0_dp, qs_ot_env%rot_mat_evec_re, mat_buf, 0.0_dp, inner_deriv_im)
    3020              : 
    3021         1572 :          CALL dbcsr_multiply('T', 'N', +1.0_dp, qs_ot_env%rot_mat_dedu, qs_ot_env%rot_mat_evec_re, 0.0_dp, mat_buf)
    3022         1572 :          CALL dbcsr_multiply('T', 'N', +1.0_dp, qs_ot_env%rot_mat_evec_re, mat_buf, 1.0_dp, inner_deriv_re)
    3023         1572 :          CALL dbcsr_multiply('T', 'N', -1.0_dp, qs_ot_env%rot_mat_evec_im, mat_buf, 1.0_dp, inner_deriv_im)
    3024              : 
    3025              :          ! Real and imaginary products can have different sparse block patterns.
    3026              :          ! Form their union explicitly and treat a missing partner block as zero.
    3027         1572 :          max_blocks = 0
    3028         1572 :          CALL dbcsr_iterator_start(iter, inner_deriv_re)
    3029         2358 :          DO WHILE (dbcsr_iterator_blocks_left(iter))
    3030          786 :             CALL dbcsr_iterator_next_block(iter, row, col)
    3031          786 :             max_blocks = max_blocks + 1
    3032              :          END DO
    3033         1572 :          CALL dbcsr_iterator_stop(iter)
    3034         1572 :          CALL dbcsr_iterator_start(iter, inner_deriv_im)
    3035         2358 :          DO WHILE (dbcsr_iterator_blocks_left(iter))
    3036          786 :             CALL dbcsr_iterator_next_block(iter, row, col)
    3037          786 :             max_blocks = max_blocks + 1
    3038              :          END DO
    3039         1572 :          CALL dbcsr_iterator_stop(iter)
    3040              : 
    3041         6288 :          ALLOCATE (rows(MAX(max_blocks, 1)), cols(MAX(max_blocks, 1)))
    3042         1572 :          nblocks = 0
    3043         1572 :          CALL dbcsr_iterator_start(iter, inner_deriv_re)
    3044         2358 :          DO WHILE (dbcsr_iterator_blocks_left(iter))
    3045          786 :             CALL dbcsr_iterator_next_block(iter, row, col)
    3046          786 :             duplicate = .FALSE.
    3047          786 :             DO iblock = 1, nblocks
    3048          786 :                duplicate = duplicate .OR. (rows(iblock) == row .AND. cols(iblock) == col)
    3049              :             END DO
    3050         2358 :             IF (.NOT. duplicate) THEN
    3051          786 :                nblocks = nblocks + 1
    3052          786 :                rows(nblocks) = row
    3053          786 :                cols(nblocks) = col
    3054              :             END IF
    3055              :          END DO
    3056         1572 :          CALL dbcsr_iterator_stop(iter)
    3057         1572 :          CALL dbcsr_iterator_start(iter, inner_deriv_im)
    3058         2358 :          DO WHILE (dbcsr_iterator_blocks_left(iter))
    3059          786 :             CALL dbcsr_iterator_next_block(iter, row, col)
    3060          786 :             duplicate = .FALSE.
    3061         1572 :             DO iblock = 1, nblocks
    3062         1572 :                duplicate = duplicate .OR. (rows(iblock) == row .AND. cols(iblock) == col)
    3063              :             END DO
    3064         2358 :             IF (.NOT. duplicate) THEN
    3065            0 :                nblocks = nblocks + 1
    3066            0 :                rows(nblocks) = row
    3067            0 :                cols(nblocks) = col
    3068              :             END IF
    3069              :          END DO
    3070         1572 :          CALL dbcsr_iterator_stop(iter)
    3071              : 
    3072              :          CALL dbcsr_get_info(inner_deriv_re, distribution=dist, row_blk_size=row_blk_size, &
    3073         1572 :                              col_blk_size=col_blk_size)
    3074              :          CALL dbcsr_create(outer_deriv_re, "outer_deriv_re", dist, dbcsr_type_no_symmetry, &
    3075         1572 :                            row_blk_size, col_blk_size)
    3076              :          CALL dbcsr_create(outer_deriv_im, "outer_deriv_im", dist, dbcsr_type_no_symmetry, &
    3077         1572 :                            row_blk_size, col_blk_size)
    3078         1572 :          IF (nblocks > 0) THEN
    3079          786 :             CALL dbcsr_reserve_blocks(outer_deriv_re, rows=rows(1:nblocks), cols=cols(1:nblocks))
    3080          786 :             CALL dbcsr_reserve_blocks(outer_deriv_im, rows=rows(1:nblocks), cols=cols(1:nblocks))
    3081              :          END IF
    3082         1572 :          CALL dbcsr_finalize(outer_deriv_re)
    3083         1572 :          CALL dbcsr_finalize(outer_deriv_im)
    3084         1572 :          CALL dbcsr_set(outer_deriv_re, 0.0_dp)
    3085         1572 :          CALL dbcsr_set(outer_deriv_im, 0.0_dp)
    3086              : 
    3087         1572 :          CALL dbcsr_get_info(outer_deriv_re, row_blk_offset=row_blk_offset, col_blk_offset=col_blk_offset)
    3088         1572 :          CALL dbcsr_iterator_start(iter, outer_deriv_re)
    3089         2358 :          DO WHILE (dbcsr_iterator_blocks_left(iter))
    3090          786 :             CALL dbcsr_iterator_next_block(iter, row, col)
    3091          786 :             CALL dbcsr_get_block_p(inner_deriv_re, row, col, block_in_re, found_in_re)
    3092          786 :             CALL dbcsr_get_block_p(inner_deriv_im, row, col, block_in_im, found_in_im)
    3093          786 :             CALL dbcsr_get_block_p(outer_deriv_re, row, col, block_out_re, found_out_re)
    3094          786 :             CALL dbcsr_get_block_p(outer_deriv_im, row, col, block_out_im, found_out_im)
    3095          786 :             CPASSERT(found_out_re .AND. found_out_im)
    3096              : 
    3097         5953 :             DO i = 1, SIZE(block_out_re, 1)
    3098        25752 :             DO j = 1, SIZE(block_out_re, 2)
    3099        21371 :                e1 = qs_ot_env%rot_mat_evals(row_blk_offset(row) + i - 1)
    3100        21371 :                e2 = qs_ot_env%rot_mat_evals(col_blk_offset(col) + j - 1)
    3101        21371 :                re_part = 0.0_dp
    3102        21371 :                im_part = 0.0_dp
    3103        21371 :                IF (found_in_re) re_part = block_in_re(i, j)
    3104        21371 :                IF (found_in_im) im_part = block_in_im(i, j)
    3105        21371 :                cval_in = CMPLX(re_part, im_part, dp)
    3106        21371 :                cval_out = cval_in*cint(e1, e2)
    3107        21371 :                block_out_re(i, j) = REAL(cval_out)
    3108        24966 :                block_out_im(i, j) = AIMAG(cval_out)
    3109              :             END DO
    3110              :             END DO
    3111              :          END DO
    3112         1572 :          CALL dbcsr_iterator_stop(iter)
    3113         1572 :          DEALLOCATE (rows, cols)
    3114         1572 :          CALL dbcsr_release(inner_deriv_re)
    3115         1572 :          CALL dbcsr_release(inner_deriv_im)
    3116              : 
    3117              :          ! Compute: matrix_buf1 = \sum_i outer_deriv_ij * |rot_mat_evec_i> <rot_mat_evec_j|
    3118         1572 :          CALL dbcsr_multiply('N', 'N', +1.0_dp, qs_ot_env%rot_mat_evec_re, outer_deriv_re, 0.0_dp, mat_buf)
    3119         1572 :          CALL dbcsr_multiply('N', 'N', -1.0_dp, qs_ot_env%rot_mat_evec_im, outer_deriv_im, 1.0_dp, mat_buf)
    3120         1572 :          CALL dbcsr_multiply('N', 'T', +1.0_dp, mat_buf, qs_ot_env%rot_mat_evec_re, 0.0_dp, qs_ot_env%matrix_buf1)
    3121              : 
    3122         1572 :          CALL dbcsr_multiply('N', 'N', +1.0_dp, qs_ot_env%rot_mat_evec_re, outer_deriv_im, 0.0_dp, mat_buf)
    3123         1572 :          CALL dbcsr_multiply('N', 'N', +1.0_dp, qs_ot_env%rot_mat_evec_im, outer_deriv_re, 1.0_dp, mat_buf)
    3124         1572 :          CALL dbcsr_multiply('N', 'T', +1.0_dp, mat_buf, qs_ot_env%rot_mat_evec_im, 1.0_dp, qs_ot_env%matrix_buf1)
    3125              : 
    3126              :          ! Account for anti-symmetry of rot_mat_x without relying on
    3127              :          ! STRICT-only matrix buffers. REF uses the same finite chart.
    3128         1572 :          CALL qs_ot_square_transpose(qs_ot_env%matrix_buf1, mat_buf, qs_ot_env%rot_mat_u)
    3129         1572 :          CALL dbcsr_copy(qs_ot_env%rot_mat_gx, mat_buf)
    3130              :          CALL dbcsr_add(qs_ot_env%rot_mat_gx, qs_ot_env%matrix_buf1, &
    3131         1572 :                         alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    3132              : 
    3133         1572 :          CALL dbcsr_release(mat_buf)
    3134         1572 :          CALL dbcsr_release(outer_deriv_re)
    3135        11004 :          CALL dbcsr_release(outer_deriv_im)
    3136              :       END IF
    3137         3196 :       CALL timestop(handle)
    3138              :    CONTAINS
    3139              : 
    3140              : ! **************************************************************************************************
    3141              : !> \brief ...
    3142              : !> \param e1 ...
    3143              : !> \param e2 ...
    3144              : !> \return ...
    3145              : ! **************************************************************************************************
    3146        21371 :       FUNCTION cint(e1, e2)
    3147              :       REAL(KIND=dp)                                      :: e1, e2
    3148              :       COMPLEX(KIND=dp)                                   :: cint
    3149              : 
    3150              :       COMPLEX(KIND=dp)                                   :: l1, l2, x
    3151              :       INTEGER                                            :: I
    3152              : 
    3153        21371 :          l1 = (0.0_dp, -1.0_dp)*e1
    3154        21371 :          l2 = (0.0_dp, -1.0_dp)*e2
    3155        21371 :          IF (ABS(l1 - l2) > 0.5_dp) THEN
    3156         1428 :             cint = (EXP(l1) - EXP(l2))/(l1 - l2)
    3157              :          ELSE
    3158              :             x = 1.0_dp
    3159              :             cint = 0.0_dp
    3160       339031 :             DO I = 1, 16
    3161       319088 :                cint = cint + x
    3162       339031 :                x = x*(l1 - l2)/REAL(I + 1, KIND=dp)
    3163              :             END DO
    3164        19943 :             cint = cint*EXP(l2)
    3165              :          END IF
    3166        21371 :       END FUNCTION cint
    3167              :    END SUBROUTINE qs_ot_rot_mat_derivative
    3168              : 
    3169              : ! **************************************************************************************************
    3170              : !> \brief decide strategy
    3171              : !>        tries to decide if the taylor expansion of cos(sqrt(xsx)) converges rapidly enough
    3172              : !>        to make a taylor expansion of the functions cos(sqrt(xsx)) and sin(sqrt(xsx))/sqrt(xsx)
    3173              : !>        and their derivatives faster than their computation based on diagonalization since xsx can
    3174              : !>        be very small, especially during dynamics, only a few terms might indeed be needed we find
    3175              : !>        the necessary order N to have largest_eval_upper_bound**(N+1)/(2(N+1))! < eps_taylor
    3176              : !> \param qs_ot_env ...
    3177              : ! **************************************************************************************************
    3178       111015 :    SUBROUTINE decide_strategy(qs_ot_env)
    3179              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3180              : 
    3181              :       INTEGER                                            :: N
    3182              :       REAL(KIND=dp)                                      :: num_error
    3183              : 
    3184       111015 :       qs_ot_env%do_taylor = .FALSE.
    3185       111015 :       N = 0
    3186       111015 :       num_error = qs_ot_env%largest_eval_upper_bound/(2.0_dp)
    3187       470761 :       DO WHILE (num_error > qs_ot_env%settings%eps_taylor .AND. N <= qs_ot_env%settings%max_taylor)
    3188       359746 :          N = N + 1
    3189       405577 :          num_error = num_error*qs_ot_env%largest_eval_upper_bound/REAL((2*N + 1)*(2*N + 2), KIND=dp)
    3190              :       END DO
    3191              :       ! An O(P) matrix-function term can be below eps_taylor while its O(X)
    3192              :       ! orbital derivative is still needed near P=X^T*S*X=0.
    3193       111015 :       qs_ot_env%taylor_order = MAX(1, N)
    3194       111015 :       IF (qs_ot_env%taylor_order <= qs_ot_env%settings%max_taylor) THEN
    3195        59916 :          qs_ot_env%do_taylor = .TRUE.
    3196              :       END IF
    3197              : 
    3198       111015 :    END SUBROUTINE decide_strategy
    3199              : 
    3200              : ! **************************************************************************************************
    3201              : !> \brief c=(c0*cos(p^0.5)+x*sin(p^0.5)*p^(-0.5)) x rot_mat_u
    3202              : !>        this assumes that x is already ortho to S*C0, and that p is x*S*x
    3203              : !>        rot_mat_u is an optional rotation matrix
    3204              : !> \param matrix_c ...
    3205              : !> \param matrix_x ...
    3206              : !> \param qs_ot_env ...
    3207              : ! **************************************************************************************************
    3208       206150 :    SUBROUTINE qs_ot_get_orbitals(matrix_c, matrix_x, qs_ot_env)
    3209              : 
    3210              :       TYPE(dbcsr_type), POINTER                          :: matrix_c, matrix_x
    3211              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3212              : 
    3213              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_orbitals'
    3214              :       REAL(KIND=dp), PARAMETER                           :: rone = 1.0_dp, rzero = 0.0_dp
    3215              : 
    3216              :       INTEGER                                            :: handle, k, n
    3217              :       TYPE(dbcsr_type), POINTER                          :: matrix_kk
    3218              : 
    3219       103075 :       CALL timeset(routineN, handle)
    3220              : 
    3221       103075 :       CALL dbcsr_get_info(matrix_x, nfullrows_total=n, nfullcols_total=k)
    3222              : 
    3223              :       ! rotate the multiplying matrices cosp and sinp instead of the result,
    3224              :       ! this should be cheaper for large basis sets
    3225       103075 :       IF (qs_ot_env%settings%do_rotation) THEN
    3226         2938 :          matrix_kk => qs_ot_env%matrix_buf1
    3227              :          CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_cosp, &
    3228         2938 :                              qs_ot_env%rot_mat_u, rzero, matrix_kk)
    3229              :       ELSE
    3230       100137 :          matrix_kk => qs_ot_env%matrix_cosp
    3231              :       END IF
    3232              : 
    3233              :       CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_c0, matrix_kk, &
    3234       103075 :                           rzero, matrix_c)
    3235              : 
    3236       103075 :       IF (qs_ot_env%settings%do_rotation) THEN
    3237         2938 :          matrix_kk => qs_ot_env%matrix_buf1
    3238              :          CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_sinp, &
    3239         2938 :                              qs_ot_env%rot_mat_u, rzero, matrix_kk)
    3240              :       ELSE
    3241       100137 :          matrix_kk => qs_ot_env%matrix_sinp
    3242              :       END IF
    3243              :       CALL dbcsr_multiply('N', 'N', rone, matrix_x, matrix_kk, &
    3244       103075 :                           rone, matrix_c)
    3245              : 
    3246       103075 :       CALL timestop(handle)
    3247              : 
    3248       103075 :    END SUBROUTINE qs_ot_get_orbitals
    3249              : 
    3250              : ! **************************************************************************************************
    3251              : !> \brief update complex K-point orbitals with the finite STRICT transformation
    3252              : !> \param matrix_c real output orbitals
    3253              : !> \param matrix_c_im imaginary output orbitals
    3254              : !> \param matrix_s real overlap matrix
    3255              : !> \param matrix_s_im imaginary overlap matrix
    3256              : !> \param qs_ot_env OT channel state
    3257              : ! **************************************************************************************************
    3258         3160 :    SUBROUTINE qs_ot_get_orbitals_complex(matrix_c, matrix_c_im, matrix_s, matrix_s_im, qs_ot_env)
    3259              :       TYPE(dbcsr_type), POINTER                          :: matrix_c, matrix_c_im, matrix_s, &
    3260              :                                                             matrix_s_im
    3261              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3262              : 
    3263              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_orbitals_complex'
    3264              : 
    3265              :       INTEGER                                            :: handle
    3266              :       TYPE(dbcsr_type)                                   :: rotated_im, rotated_re, rotation_tmp
    3267              : 
    3268         3160 :       CALL timeset(routineN, handle)
    3269              : 
    3270         3160 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    3271         3160 :       CPASSERT(qs_ot_env%settings%ot_algorithm == 'TOD')
    3272              : 
    3273              :       CALL qs_ot_complex_multiply('N', 'N', matrix_s, matrix_s_im, &
    3274              :                                   qs_ot_env%matrix_x, qs_ot_env%matrix_x_im, &
    3275              :                                   qs_ot_env%matrix_sx, qs_ot_env%matrix_sx_im, &
    3276         3160 :                                   qs_ot_env%matrix_tmp_nk)
    3277              :       CALL qs_ot_get_p_complex(qs_ot_env%matrix_x, qs_ot_env%matrix_x_im, &
    3278         3160 :                                qs_ot_env%matrix_sx, qs_ot_env%matrix_sx_im, qs_ot_env)
    3279              : 
    3280              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_c0, qs_ot_env%matrix_c0_im, &
    3281              :                                   qs_ot_env%matrix_cosp, qs_ot_env%matrix_cosp_im, &
    3282         3160 :                                   matrix_c, matrix_c_im, qs_ot_env%matrix_tmp_nk)
    3283              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_x, qs_ot_env%matrix_x_im, &
    3284              :                                   qs_ot_env%matrix_sinp, qs_ot_env%matrix_sinp_im, &
    3285              :                                   qs_ot_env%matrix_buf_nk, qs_ot_env%matrix_buf_nk_im, &
    3286         3160 :                                   qs_ot_env%matrix_tmp_nk)
    3287         3160 :       CALL dbcsr_add(matrix_c, qs_ot_env%matrix_buf_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3288         3160 :       CALL dbcsr_add(matrix_c_im, qs_ot_env%matrix_buf_nk_im, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3289              : 
    3290         3160 :       IF (qs_ot_env%settings%do_rotation) THEN
    3291          684 :          CALL qs_ot_generate_rotation_complex(qs_ot_env)
    3292              : 
    3293          684 :          CALL dbcsr_copy(rotated_re, matrix_c, name="strict_rotated_re")
    3294          684 :          CALL dbcsr_copy(rotated_im, matrix_c_im, name="strict_rotated_im")
    3295          684 :          CALL dbcsr_copy(rotation_tmp, matrix_c, name="strict_rotation_tmp")
    3296              : 
    3297              :          ! C_out = Q(X)*U for the finite STRICT chart Q(X).
    3298              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_c, qs_ot_env%rot_mat_u, &
    3299          684 :                              0.0_dp, rotated_re)
    3300              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_c_im, qs_ot_env%rot_mat_u_im, &
    3301          684 :                              0.0_dp, rotation_tmp)
    3302          684 :          CALL dbcsr_add(rotated_re, rotation_tmp, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    3303              : 
    3304              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_c, qs_ot_env%rot_mat_u_im, &
    3305          684 :                              0.0_dp, rotated_im)
    3306              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_c_im, qs_ot_env%rot_mat_u, &
    3307          684 :                              0.0_dp, rotation_tmp)
    3308          684 :          CALL dbcsr_add(rotated_im, rotation_tmp, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3309              : 
    3310          684 :          CALL dbcsr_copy(matrix_c, rotated_re)
    3311          684 :          CALL dbcsr_copy(matrix_c_im, rotated_im)
    3312          684 :          CALL dbcsr_release(rotated_re)
    3313          684 :          CALL dbcsr_release(rotated_im)
    3314          684 :          CALL dbcsr_release(rotation_tmp)
    3315              :       END IF
    3316              : 
    3317         3160 :       CALL timestop(handle)
    3318              : 
    3319         3160 :    END SUBROUTINE qs_ot_get_orbitals_complex
    3320              : 
    3321              : ! **************************************************************************************************
    3322              : !> \brief this routines computes dE/dx=dx, with dx ortho to sc0
    3323              : !>        needs dE/dC=hc,C0,X,SX,p
    3324              : !>        if preconditioned it will not be the derivative, but the lagrangian multiplier
    3325              : !>        is changed so that P*dE/dx is the right derivative (i.e. in the allowed subspace)
    3326              : !> \param matrix_hc ...
    3327              : !> \param matrix_x ...
    3328              : !> \param matrix_sx ...
    3329              : !> \param matrix_gx ...
    3330              : !> \param qs_ot_env ...
    3331              : ! **************************************************************************************************
    3332       238683 :    SUBROUTINE qs_ot_get_derivative(matrix_hc, matrix_x, matrix_sx, matrix_gx, &
    3333              :                                    qs_ot_env)
    3334              :       TYPE(dbcsr_type), POINTER                          :: matrix_hc, matrix_x, matrix_sx, matrix_gx
    3335              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3336              : 
    3337              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_derivative'
    3338              :       REAL(KIND=dp), PARAMETER                           :: rone = 1.0_dp, rzero = 0.0_dp
    3339              : 
    3340              :       INTEGER                                            :: handle, k, n, ortho_k
    3341              :       TYPE(dbcsr_type), POINTER                          :: matrix_hc_local, matrix_target
    3342              : 
    3343        79561 :       CALL timeset(routineN, handle)
    3344              : 
    3345        79561 :       NULLIFY (matrix_hc_local)
    3346              : 
    3347        79561 :       CALL dbcsr_get_info(matrix_x, nfullrows_total=n, nfullcols_total=k)
    3348              : 
    3349              :       ! could in principle be taken inside qs_ot_get_derivative_* for increased efficiency
    3350              :       ! create a local rotated version of matrix_hc leaving matrix_hc untouched (needed
    3351              :       ! for lagrangian multipliers)
    3352        79561 :       IF (qs_ot_env%settings%do_rotation) THEN
    3353         1588 :          CALL dbcsr_copy(matrix_gx, matrix_hc) ! use gx as temporary
    3354         1588 :          CALL dbcsr_init_p(matrix_hc_local)
    3355         1588 :          CALL dbcsr_copy(matrix_hc_local, matrix_hc, name='matrix_hc_local')
    3356         1588 :          CALL dbcsr_set(matrix_hc_local, 0.0_dp)
    3357         1588 :          CALL dbcsr_multiply('N', 'T', rone, matrix_gx, qs_ot_env%rot_mat_u, rzero, matrix_hc_local)
    3358              :       ELSE
    3359        77973 :          matrix_hc_local => matrix_hc
    3360              :       END IF
    3361              : 
    3362        79561 :       IF (qs_ot_env%do_taylor) THEN
    3363        44115 :          CALL qs_ot_get_derivative_taylor(matrix_hc_local, matrix_x, matrix_sx, matrix_gx, qs_ot_env)
    3364              :       ELSE
    3365        35446 :          CALL qs_ot_get_derivative_diag(matrix_hc_local, matrix_x, matrix_sx, matrix_gx, qs_ot_env)
    3366              :       END IF
    3367              : 
    3368              :       ! and make it orthogonal
    3369        79561 :       CALL dbcsr_get_info(qs_ot_env%matrix_sc0, nfullcols_total=ortho_k)
    3370              : 
    3371        79561 :       IF (ASSOCIATED(qs_ot_env%preconditioner)) THEN
    3372        69063 :          matrix_target => qs_ot_env%matrix_psc0
    3373              :       ELSE
    3374        10498 :          matrix_target => qs_ot_env%matrix_sc0
    3375              :       END IF
    3376              :       ! first make the matrix os if not yet valid
    3377        79561 :       IF (.NOT. qs_ot_env%os_valid) THEN
    3378              :          ! this assumes that the preconditioner is a single matrix
    3379              :          ! that maps sc0 onto psc0
    3380              : 
    3381         8626 :          IF (ASSOCIATED(qs_ot_env%preconditioner)) THEN
    3382              :             CALL apply_preconditioner(qs_ot_env%preconditioner, qs_ot_env%matrix_sc0, &
    3383         7628 :                                       qs_ot_env%matrix_psc0)
    3384              :          END IF
    3385              :          CALL dbcsr_multiply('T', 'N', rone, &
    3386              :                              qs_ot_env%matrix_sc0, matrix_target, &
    3387         8626 :                              rzero, qs_ot_env%matrix_os)
    3388              :          CALL cp_dbcsr_cholesky_decompose(qs_ot_env%matrix_os, &
    3389         8626 :                                           para_env=qs_ot_env%para_env, blacs_env=qs_ot_env%blacs_env)
    3390              :          CALL cp_dbcsr_cholesky_invert(qs_ot_env%matrix_os, &
    3391              :                                        para_env=qs_ot_env%para_env, blacs_env=qs_ot_env%blacs_env, &
    3392         8626 :                                        uplo_to_full=.TRUE.)
    3393         8626 :          qs_ot_env%os_valid = .TRUE.
    3394              :       END IF
    3395              :       CALL dbcsr_multiply('T', 'N', rone, matrix_target, matrix_gx, &
    3396        79561 :                           rzero, qs_ot_env%matrix_buf1_ortho)
    3397              :       CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_os, &
    3398        79561 :                           qs_ot_env%matrix_buf1_ortho, rzero, qs_ot_env%matrix_buf2_ortho)
    3399              :       CALL dbcsr_multiply('N', 'N', -rone, qs_ot_env%matrix_sc0, &
    3400        79561 :                           qs_ot_env%matrix_buf2_ortho, rone, matrix_gx)
    3401              :       ! also treat the rot_mat gradient here
    3402        79561 :       IF (qs_ot_env%settings%do_rotation) THEN
    3403         1588 :          CALL qs_ot_rot_mat_derivative(qs_ot_env)
    3404              :       END IF
    3405              : 
    3406        79561 :       IF (qs_ot_env%settings%do_rotation) THEN
    3407         1588 :          CALL dbcsr_release_p(matrix_hc_local)
    3408              :       END IF
    3409              : 
    3410        79561 :       CALL timestop(handle)
    3411              : 
    3412        79561 :    END SUBROUTINE qs_ot_get_derivative
    3413              : 
    3414              : ! **************************************************************************************************
    3415              : !> \brief Prepare the inverse metric used to project a complex STRICT gradient.
    3416              : !>        An unusable preconditioner is detached before any minimizer history is updated.
    3417              : !> \param qs_ot_env OT channel state
    3418              : !> \param preconditioner_rejected true if the attached preconditioner was not positive definite
    3419              : ! **************************************************************************************************
    3420         3436 :    SUBROUTINE qs_ot_prepare_complex_tangent_metric(qs_ot_env, preconditioner_rejected)
    3421              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3422              :       LOGICAL, INTENT(OUT), OPTIONAL                     :: preconditioner_rejected
    3423              : 
    3424              :       INTEGER                                            :: i, k
    3425              :       REAL(KIND=dp)                                      :: eval_scale, eval_threshold
    3426              :       TYPE(dbcsr_type), POINTER                          :: target_im, target_re
    3427              : 
    3428         2944 :       IF (PRESENT(preconditioner_rejected)) preconditioner_rejected = .FALSE.
    3429         2944 :       IF (qs_ot_env%os_valid) RETURN
    3430              : 
    3431          492 :       IF (ASSOCIATED(qs_ot_env%preconditioner)) THEN
    3432          482 :          target_re => qs_ot_env%matrix_psc0
    3433          482 :          target_im => qs_ot_env%matrix_psc0_im
    3434              :          CALL apply_preconditioner(qs_ot_env%preconditioner, &
    3435              :                                    qs_ot_env%matrix_sc0, qs_ot_env%matrix_sc0_im, &
    3436          482 :                                    target_re, target_im)
    3437              :       ELSE
    3438           10 :          target_re => qs_ot_env%matrix_sc0
    3439           10 :          target_im => qs_ot_env%matrix_sc0_im
    3440              :       END IF
    3441              :       CALL qs_ot_complex_multiply('C', 'N', qs_ot_env%matrix_sc0, qs_ot_env%matrix_sc0_im, &
    3442              :                                   target_re, target_im, qs_ot_env%matrix_os, qs_ot_env%matrix_os_im, &
    3443          492 :                                   qs_ot_env%matrix_buf1)
    3444          492 :       CALL dbcsr_get_info(qs_ot_env%matrix_os, nfullrows_total=k)
    3445              :       CALL cp_dbcsr_heevd(matrix_re=qs_ot_env%matrix_os, matrix_im=qs_ot_env%matrix_os_im, &
    3446              :                           eigenvectors_re=qs_ot_env%matrix_r, eigenvectors_im=qs_ot_env%matrix_r_im, &
    3447              :                           eigenvalues=qs_ot_env%evals, para_env=qs_ot_env%para_env, &
    3448          492 :                           blacs_env=qs_ot_env%blacs_env)
    3449         2984 :       eval_scale = MAX(1.0_dp, MAXVAL(ABS(qs_ot_env%evals(1:k))))
    3450          492 :       eval_threshold = 100.0_dp*EPSILON(1.0_dp)*eval_scale
    3451         2984 :       IF (MINVAL(qs_ot_env%evals(1:k)) <= eval_threshold .AND. &
    3452              :           ASSOCIATED(qs_ot_env%preconditioner)) THEN
    3453            0 :          NULLIFY (qs_ot_env%preconditioner)
    3454            0 :          IF (PRESENT(preconditioner_rejected)) preconditioner_rejected = .TRUE.
    3455            0 :          target_re => qs_ot_env%matrix_sc0
    3456            0 :          target_im => qs_ot_env%matrix_sc0_im
    3457              :          CALL qs_ot_complex_multiply('C', 'N', qs_ot_env%matrix_sc0, qs_ot_env%matrix_sc0_im, &
    3458              :                                      target_re, target_im, qs_ot_env%matrix_os, qs_ot_env%matrix_os_im, &
    3459            0 :                                      qs_ot_env%matrix_buf1)
    3460              :          CALL cp_dbcsr_heevd(matrix_re=qs_ot_env%matrix_os, matrix_im=qs_ot_env%matrix_os_im, &
    3461              :                              eigenvectors_re=qs_ot_env%matrix_r, eigenvectors_im=qs_ot_env%matrix_r_im, &
    3462              :                              eigenvalues=qs_ot_env%evals, para_env=qs_ot_env%para_env, &
    3463            0 :                              blacs_env=qs_ot_env%blacs_env)
    3464            0 :          eval_scale = MAX(1.0_dp, MAXVAL(ABS(qs_ot_env%evals(1:k))))
    3465            0 :          eval_threshold = 100.0_dp*EPSILON(1.0_dp)*eval_scale
    3466              :       END IF
    3467         2984 :       CPASSERT(MINVAL(qs_ot_env%evals(1:k)) > eval_threshold)
    3468         2984 :       DO i = 1, k
    3469         2984 :          qs_ot_env%dum(i) = 1.0_dp/qs_ot_env%evals(i)
    3470              :       END DO
    3471          492 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1, qs_ot_env%matrix_r)
    3472          492 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1_im, qs_ot_env%matrix_r_im)
    3473          492 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1, alpha=qs_ot_env%dum, side='right')
    3474          492 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1_im, alpha=qs_ot_env%dum, side='right')
    3475              :       CALL qs_ot_complex_multiply('N', 'C', qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3476              :                                   qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3477              :                                   qs_ot_env%matrix_os, qs_ot_env%matrix_os_im, &
    3478          492 :                                   qs_ot_env%matrix_buf2)
    3479          492 :       qs_ot_env%os_valid = .TRUE.
    3480              : 
    3481              :    END SUBROUTINE qs_ot_prepare_complex_tangent_metric
    3482              : 
    3483              : ! **************************************************************************************************
    3484              : !> \brief finite complex STRICT derivative, projected onto C0^H*S*X=0
    3485              : !> \param matrix_hc real part of H(k)*C(k)
    3486              : !> \param matrix_hc_im imaginary part of H(k)*C(k)
    3487              : !> \param qs_ot_env OT channel state
    3488              : !> \param matrix_hc_rotation occupation-weighted H(k)C(k) for the rotation channel
    3489              : !> \param matrix_hc_rotation_im imaginary component of matrix_hc_rotation
    3490              : ! **************************************************************************************************
    3491         5688 :    SUBROUTINE qs_ot_get_derivative_complex(matrix_hc, matrix_hc_im, qs_ot_env, &
    3492              :                                            matrix_hc_rotation, matrix_hc_rotation_im)
    3493              :       TYPE(dbcsr_type), POINTER                          :: matrix_hc, matrix_hc_im
    3494              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3495              :       TYPE(dbcsr_type), OPTIONAL, POINTER                :: matrix_hc_rotation, matrix_hc_rotation_im
    3496              : 
    3497              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_derivative_complex'
    3498              : 
    3499              :       INTEGER                                            :: handle
    3500              :       TYPE(dbcsr_distribution_type)                      :: dist
    3501              :       TYPE(dbcsr_type)                                   :: tmp_nk
    3502              :       TYPE(dbcsr_type), POINTER                          :: hc_rotation_im, hc_rotation_re, &
    3503              :                                                             hc_work_im, hc_work_re, target_im, &
    3504              :                                                             target_re
    3505              :       TYPE(dbcsr_type), TARGET                           :: hc_rot_im, hc_rot_re
    3506              : 
    3507         2844 :       CALL timeset(routineN, handle)
    3508              : 
    3509         2844 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    3510         2844 :       CPASSERT(qs_ot_env%settings%ot_algorithm == 'TOD')
    3511              : 
    3512         2844 :       hc_rotation_re => matrix_hc
    3513         2844 :       hc_rotation_im => matrix_hc_im
    3514         2844 :       IF (PRESENT(matrix_hc_rotation) .OR. PRESENT(matrix_hc_rotation_im)) THEN
    3515          284 :          CPASSERT(PRESENT(matrix_hc_rotation) .AND. PRESENT(matrix_hc_rotation_im))
    3516          284 :          CPASSERT(ASSOCIATED(matrix_hc_rotation))
    3517          284 :          CPASSERT(ASSOCIATED(matrix_hc_rotation_im))
    3518          284 :          hc_rotation_re => matrix_hc_rotation
    3519          284 :          hc_rotation_im => matrix_hc_rotation_im
    3520              :       END IF
    3521         2844 :       hc_work_re => matrix_hc
    3522         2844 :       hc_work_im => matrix_hc_im
    3523              : 
    3524         2844 :       IF (qs_ot_env%settings%do_rotation) THEN
    3525          648 :          CALL qs_ot_generate_rotation_complex(qs_ot_env)
    3526              : 
    3527              :          ! Reconstruct the unrotated finite STRICT orbitals Q(X).
    3528              :          CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_c0, qs_ot_env%matrix_c0_im, &
    3529              :                                      qs_ot_env%matrix_cosp, qs_ot_env%matrix_cosp_im, &
    3530              :                                      qs_ot_env%matrix_buf_nk, qs_ot_env%matrix_buf_nk_im, &
    3531          648 :                                      qs_ot_env%matrix_tmp_nk)
    3532              :          CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_x, qs_ot_env%matrix_x_im, &
    3533              :                                      qs_ot_env%matrix_sinp, qs_ot_env%matrix_sinp_im, &
    3534              :                                      qs_ot_env%matrix_gx, qs_ot_env%matrix_gx_im, &
    3535          648 :                                      qs_ot_env%matrix_tmp_nk)
    3536              :          CALL dbcsr_add(qs_ot_env%matrix_buf_nk, qs_ot_env%matrix_gx, &
    3537          648 :                         alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3538              :          CALL dbcsr_add(qs_ot_env%matrix_buf_nk_im, qs_ot_env%matrix_gx_im, &
    3539          648 :                         alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3540              : 
    3541              :          ! dF/dU = Q(X)^H*G_C for C=Q(X)*U.
    3542              :          CALL qs_ot_complex_multiply('C', 'N', &
    3543              :                                      qs_ot_env%matrix_buf_nk, qs_ot_env%matrix_buf_nk_im, &
    3544              :                                      hc_rotation_re, hc_rotation_im, &
    3545              :                                      qs_ot_env%rot_mat_dedu, qs_ot_env%rot_mat_dedu_im, &
    3546          648 :                                      qs_ot_env%matrix_buf1)
    3547          648 :          CALL qs_ot_rot_mat_derivative_complex(qs_ot_env)
    3548              : 
    3549              :          ! The STRICT coordinate sees G_Q=G_C*U^H.
    3550          648 :          CALL dbcsr_copy(hc_rot_re, matrix_hc, name="strict_hc_rot_re")
    3551          648 :          CALL dbcsr_copy(hc_rot_im, matrix_hc_im, name="strict_hc_rot_im")
    3552          648 :          CALL dbcsr_copy(tmp_nk, matrix_hc, name="strict_hc_rot_tmp")
    3553              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, matrix_hc, qs_ot_env%rot_mat_u, &
    3554          648 :                              0.0_dp, hc_rot_re)
    3555              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, matrix_hc_im, qs_ot_env%rot_mat_u_im, &
    3556          648 :                              0.0_dp, tmp_nk)
    3557          648 :          CALL dbcsr_add(hc_rot_re, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3558              : 
    3559              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, matrix_hc_im, qs_ot_env%rot_mat_u, &
    3560          648 :                              0.0_dp, hc_rot_im)
    3561              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, matrix_hc, qs_ot_env%rot_mat_u_im, &
    3562          648 :                              0.0_dp, tmp_nk)
    3563          648 :          CALL dbcsr_add(hc_rot_im, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    3564          648 :          hc_work_re => hc_rot_re
    3565          648 :          hc_work_im => hc_rot_im
    3566              :       END IF
    3567              : 
    3568              :       ! Direct X contribution, H*C sinc(sqrt(P)).
    3569              :       CALL qs_ot_complex_multiply('N', 'N', hc_work_re, hc_work_im, &
    3570              :                                   qs_ot_env%matrix_sinp, qs_ot_env%matrix_sinp_im, &
    3571              :                                   qs_ot_env%matrix_gx, qs_ot_env%matrix_gx_im, &
    3572         2844 :                                   qs_ot_env%matrix_tmp_nk)
    3573              : 
    3574              :       ! Frechet contribution from X sinc(sqrt(P)).
    3575              :       CALL qs_ot_complex_multiply('C', 'N', hc_work_re, hc_work_im, &
    3576              :                                   qs_ot_env%matrix_x, qs_ot_env%matrix_x_im, &
    3577              :                                   qs_ot_env%matrix_buf2, qs_ot_env%matrix_buf2_im, &
    3578         2844 :                                   qs_ot_env%matrix_buf1)
    3579              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_buf2, qs_ot_env%matrix_buf2_im, &
    3580              :                                   qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3581              :                                   qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3582         2844 :                                   qs_ot_env%matrix_buf4)
    3583              :       CALL qs_ot_complex_multiply('C', 'N', qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3584              :                                   qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3585              :                                   qs_ot_env%matrix_buf2, qs_ot_env%matrix_buf2_im, &
    3586         2844 :                                   qs_ot_env%matrix_buf4)
    3587              :       CALL dbcsr_hadamard_product(qs_ot_env%matrix_buf2, qs_ot_env%matrix_sinp_b, &
    3588         2844 :                                   qs_ot_env%matrix_buf3)
    3589              :       CALL dbcsr_hadamard_product(qs_ot_env%matrix_buf2_im, qs_ot_env%matrix_sinp_b, &
    3590         2844 :                                   qs_ot_env%matrix_buf3_im)
    3591              : 
    3592              :       ! Frechet contribution from C0 cos(sqrt(P)).
    3593              :       CALL qs_ot_complex_multiply('C', 'N', hc_work_re, hc_work_im, &
    3594              :                                   qs_ot_env%matrix_c0, qs_ot_env%matrix_c0_im, &
    3595              :                                   qs_ot_env%matrix_buf2, qs_ot_env%matrix_buf2_im, &
    3596         2844 :                                   qs_ot_env%matrix_buf1)
    3597              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_buf2, qs_ot_env%matrix_buf2_im, &
    3598              :                                   qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3599              :                                   qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3600         2844 :                                   qs_ot_env%matrix_buf4)
    3601              :       CALL qs_ot_complex_multiply('C', 'N', qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3602              :                                   qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3603              :                                   qs_ot_env%matrix_buf2, qs_ot_env%matrix_buf2_im, &
    3604         2844 :                                   qs_ot_env%matrix_buf4)
    3605              :       CALL dbcsr_hadamard_product(qs_ot_env%matrix_buf2, qs_ot_env%matrix_cosp_b, &
    3606         2844 :                                   qs_ot_env%matrix_buf4)
    3607              :       CALL dbcsr_hadamard_product(qs_ot_env%matrix_buf2_im, qs_ot_env%matrix_cosp_b, &
    3608         2844 :                                   qs_ot_env%matrix_buf4_im)
    3609              :       CALL dbcsr_add(qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf4, &
    3610         2844 :                      alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3611              :       CALL dbcsr_add(qs_ot_env%matrix_buf3_im, qs_ot_env%matrix_buf4_im, &
    3612         2844 :                      alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3613              : 
    3614              :       ! Transform back and add the Hermitian adjoint generated by dP.
    3615              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3616              :                                   qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf3_im, &
    3617              :                                   qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3618         2844 :                                   qs_ot_env%matrix_buf2)
    3619              :       CALL qs_ot_complex_multiply('N', 'C', qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3620              :                                   qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3621              :                                   qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf3_im, &
    3622         2844 :                                   qs_ot_env%matrix_buf2)
    3623         2844 :       CALL dbcsr_get_info(qs_ot_env%matrix_buf3, distribution=dist)
    3624              :       CALL dbcsr_transposed(qs_ot_env%matrix_buf4, qs_ot_env%matrix_buf3, &
    3625              :                             shallow_data_copy=.FALSE., use_distribution=dist, &
    3626         2844 :                             transpose_distribution=.FALSE.)
    3627              :       CALL dbcsr_transposed(qs_ot_env%matrix_buf4_im, qs_ot_env%matrix_buf3_im, &
    3628              :                             shallow_data_copy=.FALSE., use_distribution=dist, &
    3629         2844 :                             transpose_distribution=.FALSE.)
    3630              :       CALL dbcsr_add(qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf4, &
    3631         2844 :                      alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3632              :       CALL dbcsr_add(qs_ot_env%matrix_buf3_im, qs_ot_env%matrix_buf4_im, &
    3633         2844 :                      alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    3634              : 
    3635              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_sx, qs_ot_env%matrix_sx_im, &
    3636              :                                   qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf3_im, &
    3637              :                                   qs_ot_env%matrix_buf_nk, qs_ot_env%matrix_buf_nk_im, &
    3638         2844 :                                   qs_ot_env%matrix_tmp_nk)
    3639              :       CALL dbcsr_add(qs_ot_env%matrix_gx, qs_ot_env%matrix_buf_nk, &
    3640         2844 :                      alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3641              :       CALL dbcsr_add(qs_ot_env%matrix_gx_im, qs_ot_env%matrix_buf_nk_im, &
    3642         2844 :                      alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3643              : 
    3644              :       ! Preconditioner-aware projection onto the complex STRICT tangent space.
    3645         2844 :       CALL qs_ot_prepare_complex_tangent_metric(qs_ot_env)
    3646         2844 :       IF (ASSOCIATED(qs_ot_env%preconditioner)) THEN
    3647         2818 :          target_re => qs_ot_env%matrix_psc0
    3648         2818 :          target_im => qs_ot_env%matrix_psc0_im
    3649              :       ELSE
    3650           26 :          target_re => qs_ot_env%matrix_sc0
    3651           26 :          target_im => qs_ot_env%matrix_sc0_im
    3652              :       END IF
    3653              : 
    3654              :       CALL qs_ot_complex_multiply('C', 'N', target_re, target_im, &
    3655              :                                   qs_ot_env%matrix_gx, qs_ot_env%matrix_gx_im, &
    3656              :                                   qs_ot_env%matrix_buf1_ortho, qs_ot_env%matrix_buf1_ortho_im, &
    3657         2844 :                                   qs_ot_env%matrix_tmp_ortho)
    3658              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_os, qs_ot_env%matrix_os_im, &
    3659              :                                   qs_ot_env%matrix_buf1_ortho, qs_ot_env%matrix_buf1_ortho_im, &
    3660              :                                   qs_ot_env%matrix_buf2_ortho, qs_ot_env%matrix_buf2_ortho_im, &
    3661         2844 :                                   qs_ot_env%matrix_tmp_ortho)
    3662              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_sc0, qs_ot_env%matrix_sc0_im, &
    3663              :                                   qs_ot_env%matrix_buf2_ortho, qs_ot_env%matrix_buf2_ortho_im, &
    3664              :                                   qs_ot_env%matrix_buf_nk, qs_ot_env%matrix_buf_nk_im, &
    3665         2844 :                                   qs_ot_env%matrix_tmp_nk)
    3666              :       CALL dbcsr_add(qs_ot_env%matrix_gx, qs_ot_env%matrix_buf_nk, &
    3667         2844 :                      alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    3668              :       CALL dbcsr_add(qs_ot_env%matrix_gx_im, qs_ot_env%matrix_buf_nk_im, &
    3669         2844 :                      alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    3670              : 
    3671         2844 :       IF (qs_ot_env%settings%do_rotation) THEN
    3672          648 :          CALL dbcsr_release(hc_rot_re)
    3673          648 :          CALL dbcsr_release(hc_rot_im)
    3674          648 :          CALL dbcsr_release(tmp_nk)
    3675              :       END IF
    3676              : 
    3677         2844 :       CALL timestop(handle)
    3678              : 
    3679         2844 :    END SUBROUTINE qs_ot_get_derivative_complex
    3680              : 
    3681              : ! **************************************************************************************************
    3682              : !> \brief ...
    3683              : !> \param matrix_hc ...
    3684              : !> \param matrix_x ...
    3685              : !> \param matrix_sx ...
    3686              : !> \param matrix_gx ...
    3687              : !> \param qs_ot_env ...
    3688              : ! **************************************************************************************************
    3689       106338 :    SUBROUTINE qs_ot_get_derivative_diag(matrix_hc, matrix_x, matrix_sx, matrix_gx, &
    3690              :                                         qs_ot_env)
    3691              : 
    3692              :       TYPE(dbcsr_type), POINTER                          :: matrix_hc, matrix_x, matrix_sx, matrix_gx
    3693              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3694              : 
    3695              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_derivative_diag'
    3696              :       REAL(KIND=dp), PARAMETER                           :: rone = 1.0_dp, rzero = 0.0_dp
    3697              : 
    3698              :       INTEGER                                            :: handle, k, n
    3699              :       TYPE(dbcsr_distribution_type)                      :: dist
    3700              : 
    3701        35446 :       CALL timeset(routineN, handle)
    3702              : 
    3703        35446 :       CALL dbcsr_get_info(matrix_x, nfullrows_total=n, nfullcols_total=k)
    3704              : 
    3705              :       ! go for the derivative now
    3706              :       ! this de/dc*(dX/dx)*sinp
    3707        35446 :       CALL dbcsr_multiply('N', 'N', rone, matrix_hc, qs_ot_env%matrix_sinp, rzero, matrix_gx)
    3708              :       ! overlap hc*x
    3709        35446 :       CALL dbcsr_multiply('T', 'N', rone, matrix_hc, matrix_x, rzero, qs_ot_env%matrix_buf2)
    3710              :       ! get it in the basis of the eigenvectors
    3711              :       CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_buf2, qs_ot_env%matrix_r, &
    3712        35446 :                           rzero, qs_ot_env%matrix_buf1)
    3713              :       CALL dbcsr_multiply('T', 'N', rone, qs_ot_env%matrix_r, qs_ot_env%matrix_buf1, &
    3714        35446 :                           rzero, qs_ot_env%matrix_buf2)
    3715              : 
    3716              :       ! get the schur product of O_uv*B_uv
    3717              :       CALL dbcsr_hadamard_product(qs_ot_env%matrix_buf2, qs_ot_env%matrix_sinp_b, &
    3718        35446 :                                   qs_ot_env%matrix_buf3)
    3719              : 
    3720              :       ! overlap hc*c0
    3721              :       CALL dbcsr_multiply('T', 'N', rone, matrix_hc, qs_ot_env%matrix_c0, rzero, &
    3722        35446 :                           qs_ot_env%matrix_buf2)
    3723              :       ! get it in the basis of the eigenvectors
    3724              :       CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_buf2, qs_ot_env%matrix_r, &
    3725        35446 :                           rzero, qs_ot_env%matrix_buf1)
    3726              :       CALL dbcsr_multiply('T', 'N', rone, qs_ot_env%matrix_r, qs_ot_env%matrix_buf1, &
    3727        35446 :                           rzero, qs_ot_env%matrix_buf2)
    3728              : 
    3729              :       CALL dbcsr_hadamard_product(qs_ot_env%matrix_buf2, qs_ot_env%matrix_cosp_b, &
    3730        35446 :                                   qs_ot_env%matrix_buf4)
    3731              : 
    3732              :       ! add the two bs and compute b+b^T
    3733              :       CALL dbcsr_add(qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf4, &
    3734        35446 :                      alpha_scalar=rone, beta_scalar=rone)
    3735              : 
    3736              :       ! get the b in the eigenvector basis
    3737              :       CALL dbcsr_multiply('N', 'T', rone, qs_ot_env%matrix_buf3, qs_ot_env%matrix_r, &
    3738        35446 :                           rzero, qs_ot_env%matrix_buf1)
    3739              :       CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_r, qs_ot_env%matrix_buf1, &
    3740        35446 :                           rzero, qs_ot_env%matrix_buf3)
    3741        35446 :       CALL dbcsr_get_info(qs_ot_env%matrix_buf3, distribution=dist)
    3742              :       CALL dbcsr_transposed(qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf3, &
    3743              :                             shallow_data_copy=.FALSE., use_distribution=dist, &
    3744        35446 :                             transpose_distribution=.FALSE.)
    3745              :       CALL dbcsr_add(qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf1, &
    3746        35446 :                      alpha_scalar=rone, beta_scalar=rone)
    3747              : 
    3748              :       ! and add to the derivative
    3749              :       CALL dbcsr_multiply('N', 'N', rone, matrix_sx, qs_ot_env%matrix_buf3, &
    3750        35446 :                           rone, matrix_gx)
    3751        35446 :       CALL timestop(handle)
    3752              : 
    3753        35446 :    END SUBROUTINE qs_ot_get_derivative_diag
    3754              : 
    3755              : ! **************************************************************************************************
    3756              : !> \brief compute the derivative of the taylor expansion below
    3757              : !> \param matrix_hc ...
    3758              : !> \param matrix_x ...
    3759              : !> \param matrix_sx ...
    3760              : !> \param matrix_gx ...
    3761              : !> \param qs_ot_env ...
    3762              : ! **************************************************************************************************
    3763       176460 :    SUBROUTINE qs_ot_get_derivative_taylor(matrix_hc, matrix_x, matrix_sx, matrix_gx, &
    3764              :                                           qs_ot_env)
    3765              : 
    3766              :       TYPE(dbcsr_type), POINTER                          :: matrix_hc, matrix_x, matrix_sx, matrix_gx
    3767              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3768              : 
    3769              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_derivative_taylor'
    3770              :       REAL(KIND=dp), PARAMETER                           :: rone = 1.0_dp, rzero = 0.0_dp
    3771              : 
    3772              :       INTEGER                                            :: handle, i, k, n
    3773              :       REAL(KIND=dp)                                      :: cosfactor, sinfactor
    3774              :       TYPE(dbcsr_distribution_type)                      :: dist
    3775              :       TYPE(dbcsr_type), POINTER                          :: matrix_left, matrix_right
    3776              : 
    3777        44115 :       CALL timeset(routineN, handle)
    3778              : 
    3779        44115 :       CALL dbcsr_get_info(matrix_x, nfullrows_total=n, nfullcols_total=k)
    3780              : 
    3781              :       ! go for the derivative now
    3782              :       ! this de/dc*(dX/dx)*sinp i.e. zeroth order
    3783        44115 :       CALL dbcsr_multiply('N', 'N', rone, matrix_hc, qs_ot_env%matrix_sinp, rzero, matrix_gx)
    3784              : 
    3785        44115 :       IF (qs_ot_env%taylor_order <= 0) THEN
    3786            0 :          CALL timestop(handle)
    3787            0 :          RETURN
    3788              :       END IF
    3789              : 
    3790              :       ! we store the matrix that will multiply sx in matrix_r
    3791        44115 :       CALL dbcsr_set(qs_ot_env%matrix_r, rzero)
    3792              : 
    3793              :       ! just better names for matrix_cosp_b and matrix_sinp_b (they are buffer space here)
    3794        44115 :       matrix_left => qs_ot_env%matrix_cosp_b
    3795        44115 :       matrix_right => qs_ot_env%matrix_sinp_b
    3796              : 
    3797              :       ! overlap hc*x and add its transpose to matrix_left
    3798        44115 :       CALL dbcsr_multiply('T', 'N', rone, matrix_hc, matrix_x, rzero, matrix_left)
    3799        44115 :       CALL dbcsr_get_info(matrix_left, distribution=dist)
    3800              :       CALL dbcsr_transposed(qs_ot_env%matrix_buf1, matrix_left, &
    3801              :                             shallow_data_copy=.FALSE., use_distribution=dist, &
    3802        44115 :                             transpose_distribution=.FALSE.)
    3803              :       CALL dbcsr_add(matrix_left, qs_ot_env%matrix_buf1, &
    3804        44115 :                      alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3805        44115 :       CALL dbcsr_copy(matrix_right, matrix_left)
    3806              : 
    3807              :       ! first order
    3808        44115 :       sinfactor = -1.0_dp/(2.0_dp*3.0_dp)
    3809              :       CALL dbcsr_add(qs_ot_env%matrix_r, matrix_left, &
    3810        44115 :                      alpha_scalar=1.0_dp, beta_scalar=sinfactor)
    3811              : 
    3812              :       !      M
    3813              :       !    OM+MO
    3814              :       ! OOM+OMO+MOO
    3815              :       !   ...
    3816        82655 :       DO i = 2, qs_ot_env%taylor_order
    3817        38540 :          sinfactor = sinfactor*(-1.0_dp)/REAL(2*i*(2*i + 1), KIND=dp)
    3818        38540 :          CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_p, matrix_left, rzero, qs_ot_env%matrix_buf1)
    3819        38540 :          CALL dbcsr_multiply('N', 'N', rone, matrix_right, qs_ot_env%matrix_p, rzero, matrix_left)
    3820        38540 :          CALL dbcsr_copy(matrix_right, matrix_left)
    3821              :          CALL dbcsr_add(matrix_left, qs_ot_env%matrix_buf1, &
    3822        38540 :                         1.0_dp, 1.0_dp)
    3823              :          CALL dbcsr_add(qs_ot_env%matrix_r, matrix_left, &
    3824        82655 :                         alpha_scalar=1.0_dp, beta_scalar=sinfactor)
    3825              :       END DO
    3826              : 
    3827              :       ! overlap hc*c0 and add its transpose to matrix_left
    3828        44115 :       CALL dbcsr_multiply('T', 'N', rone, matrix_hc, qs_ot_env%matrix_c0, rzero, matrix_left)
    3829        44115 :       CALL dbcsr_get_info(matrix_left, distribution=dist)
    3830              :       CALL dbcsr_transposed(qs_ot_env%matrix_buf1, matrix_left, &
    3831              :                             shallow_data_copy=.FALSE., use_distribution=dist, &
    3832        44115 :                             transpose_distribution=.FALSE.)
    3833        44115 :       CALL dbcsr_add(matrix_left, qs_ot_env%matrix_buf1, 1.0_dp, 1.0_dp)
    3834        44115 :       CALL dbcsr_copy(matrix_right, matrix_left)
    3835              : 
    3836              :       ! first order
    3837        44115 :       cosfactor = -1.0_dp/(1.0_dp*2.0_dp)
    3838              :       CALL dbcsr_add(qs_ot_env%matrix_r, matrix_left, &
    3839        44115 :                      alpha_scalar=1.0_dp, beta_scalar=cosfactor)
    3840              : 
    3841              :       !      M
    3842              :       !    OM+MO
    3843              :       ! OOM+OMO+MOO
    3844              :       !   ...
    3845        82655 :       DO i = 2, qs_ot_env%taylor_order
    3846        38540 :          cosfactor = cosfactor*(-1.0_dp)/REAL(2*i*(2*i - 1), KIND=dp)
    3847        38540 :          CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_p, matrix_left, rzero, qs_ot_env%matrix_buf1)
    3848        38540 :          CALL dbcsr_multiply('N', 'N', rone, matrix_right, qs_ot_env%matrix_p, rzero, matrix_left)
    3849        38540 :          CALL dbcsr_copy(matrix_right, matrix_left)
    3850        38540 :          CALL dbcsr_add(matrix_left, qs_ot_env%matrix_buf1, 1.0_dp, 1.0_dp)
    3851              :          CALL dbcsr_add(qs_ot_env%matrix_r, matrix_left, &
    3852        82655 :                         alpha_scalar=1.0_dp, beta_scalar=cosfactor)
    3853              :       END DO
    3854              : 
    3855              :       ! and add to the derivative
    3856        44115 :       CALL dbcsr_multiply('N', 'N', rone, matrix_sx, qs_ot_env%matrix_r, rone, matrix_gx)
    3857              : 
    3858        44115 :       CALL timestop(handle)
    3859              : 
    3860        44115 :    END SUBROUTINE qs_ot_get_derivative_taylor
    3861              : 
    3862              : ! *************************************************************************************************
    3863              : !> \brief computes a taylor expansion.
    3864              : !> \param qs_ot_env ...
    3865              : ! **************************************************************************************************
    3866        96287 :    SUBROUTINE qs_ot_p2m_taylor(qs_ot_env)
    3867              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3868              : 
    3869              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'qs_ot_p2m_taylor'
    3870              :       REAL(KIND=dp), PARAMETER                           :: rone = 1.0_dp, rzero = 0.0_dp
    3871              : 
    3872              :       INTEGER                                            :: handle, i, k
    3873              :       REAL(KIND=dp)                                      :: cosfactor, sinfactor
    3874              : 
    3875        59916 :       CALL timeset(routineN, handle)
    3876              : 
    3877              :       ! zeroth order
    3878        59916 :       CALL dbcsr_set(qs_ot_env%matrix_cosp, rzero)
    3879        59916 :       CALL dbcsr_set(qs_ot_env%matrix_sinp, rzero)
    3880        59916 :       CALL dbcsr_add_on_diag(qs_ot_env%matrix_cosp, rone)
    3881        59916 :       CALL dbcsr_add_on_diag(qs_ot_env%matrix_sinp, rone)
    3882              : 
    3883        59916 :       IF (qs_ot_env%taylor_order <= 0) THEN
    3884            0 :          CALL timestop(handle)
    3885        23545 :          RETURN
    3886              :       END IF
    3887              : 
    3888              :       ! first order
    3889        59916 :       cosfactor = -1.0_dp/(1.0_dp*2.0_dp)
    3890        59916 :       sinfactor = -1.0_dp/(2.0_dp*3.0_dp)
    3891        59916 :       CALL dbcsr_add(qs_ot_env%matrix_cosp, qs_ot_env%matrix_p, alpha_scalar=1.0_dp, beta_scalar=cosfactor)
    3892        59916 :       CALL dbcsr_add(qs_ot_env%matrix_sinp, qs_ot_env%matrix_p, alpha_scalar=1.0_dp, beta_scalar=sinfactor)
    3893        59916 :       IF (qs_ot_env%taylor_order <= 1) THEN
    3894        23545 :          CALL timestop(handle)
    3895        23545 :          RETURN
    3896              :       END IF
    3897              : 
    3898              :       ! other orders
    3899        36371 :       CALL dbcsr_get_info(qs_ot_env%matrix_p, nfullrows_total=k)
    3900        36371 :       CALL dbcsr_copy(qs_ot_env%matrix_r, qs_ot_env%matrix_p)
    3901              : 
    3902        91750 :       DO i = 2, qs_ot_env%taylor_order
    3903              :          ! new power of p
    3904              :          CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_p, qs_ot_env%matrix_r, &
    3905        55379 :                              rzero, qs_ot_env%matrix_buf1)
    3906        55379 :          CALL dbcsr_copy(qs_ot_env%matrix_r, qs_ot_env%matrix_buf1)
    3907              :          ! add to the taylor expansion so far
    3908        55379 :          cosfactor = cosfactor*(-1.0_dp)/REAL(2*i*(2*i - 1), KIND=dp)
    3909        55379 :          sinfactor = sinfactor*(-1.0_dp)/REAL(2*i*(2*i + 1), KIND=dp)
    3910              :          CALL dbcsr_add(qs_ot_env%matrix_cosp, qs_ot_env%matrix_r, &
    3911        55379 :                         alpha_scalar=1.0_dp, beta_scalar=cosfactor)
    3912              :          CALL dbcsr_add(qs_ot_env%matrix_sinp, qs_ot_env%matrix_r, &
    3913        91750 :                         alpha_scalar=1.0_dp, beta_scalar=sinfactor)
    3914              :       END DO
    3915              : 
    3916        36371 :       CALL timestop(handle)
    3917              : 
    3918              :    END SUBROUTINE qs_ot_p2m_taylor
    3919              : 
    3920              : ! **************************************************************************************************
    3921              : !> \brief given p, computes  - eigenstuff (matrix_r,evals)
    3922              : !>        - cos(p^0.5),p^(-0.5)*sin(p^0.5)
    3923              : !>        - the real b matrices, needed for the derivatives of these guys
    3924              : !>        cosp_b_ij=(1/(2pii) * int(cos(z^1/2)/((z-eval(i))*(z-eval(j))))
    3925              : !>        sinp_b_ij=(1/(2pii) * int(z^(-1/2)*sin(z^1/2)/((z-eval(i))*(z-eval(j))))
    3926              : !> \param qs_ot_env ...
    3927              : ! **************************************************************************************************
    3928       204396 :    SUBROUTINE qs_ot_p2m_diag(qs_ot_env)
    3929              : 
    3930              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3931              : 
    3932              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'qs_ot_p2m_diag'
    3933              :       REAL(KIND=dp), PARAMETER                           :: rone = 1.0_dp, rzero = 0.0_dp
    3934              : 
    3935              :       INTEGER                                            :: col, col_offset, col_size, handle, i, j, &
    3936              :                                                             k, row, row_offset, row_size
    3937        51099 :       REAL(dp), DIMENSION(:, :), POINTER                 :: block
    3938              :       REAL(KIND=dp)                                      :: a, b
    3939              :       TYPE(dbcsr_iterator_type)                          :: iter
    3940              : 
    3941        51099 :       CALL timeset(routineN, handle)
    3942              : 
    3943        51099 :       CALL dbcsr_get_info(qs_ot_env%matrix_p, nfullrows_total=k)
    3944        51099 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1, qs_ot_env%matrix_p)
    3945              :       CALL cp_dbcsr_syevd(qs_ot_env%matrix_buf1, qs_ot_env%matrix_r, qs_ot_env%evals, &
    3946        51099 :                           qs_ot_env%para_env, qs_ot_env%blacs_env)
    3947       529984 :       DO i = 1, k
    3948       529984 :          qs_ot_env%evals(i) = MAX(0.0_dp, qs_ot_env%evals(i))
    3949              :       END DO
    3950              : 
    3951        51099 :       !$OMP PARALLEL DO DEFAULT(NONE) PRIVATE(i) SHARED(k,qs_ot_env)
    3952              :       DO i = 1, k
    3953              :          qs_ot_env%dum(i) = COS(SQRT(qs_ot_env%evals(i)))
    3954              :       END DO
    3955        51099 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1, qs_ot_env%matrix_r)
    3956        51099 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1, alpha=qs_ot_env%dum, side='right')
    3957              :       CALL dbcsr_multiply('N', 'T', rone, qs_ot_env%matrix_r, qs_ot_env%matrix_buf1, &
    3958        51099 :                           rzero, qs_ot_env%matrix_cosp)
    3959              : 
    3960        51099 :       !$OMP PARALLEL DO DEFAULT(NONE) PRIVATE(i) SHARED(k,qs_ot_env)
    3961              :       DO i = 1, k
    3962              :          qs_ot_env%dum(i) = qs_ot_sinc(SQRT(qs_ot_env%evals(i)))
    3963              :       END DO
    3964        51099 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1, qs_ot_env%matrix_r)
    3965        51099 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1, alpha=qs_ot_env%dum, side='right')
    3966              :       CALL dbcsr_multiply('N', 'T', rone, qs_ot_env%matrix_r, qs_ot_env%matrix_buf1, &
    3967        51099 :                           rzero, qs_ot_env%matrix_sinp)
    3968              : 
    3969        51099 :       CALL dbcsr_copy(qs_ot_env%matrix_cosp_b, qs_ot_env%matrix_cosp)
    3970        51099 :       CALL dbcsr_iterator_start(iter, qs_ot_env%matrix_cosp_b)
    3971        87340 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    3972              :          CALL dbcsr_iterator_next_block(iter, row, col, block, &
    3973              :                                         row_size=row_size, col_size=col_size, &
    3974        36241 :                                         row_offset=row_offset, col_offset=col_offset)
    3975       549087 :          DO j = 1, col_size
    3976     10678828 :          DO i = 1, row_size
    3977              :             a = (SQRT(qs_ot_env%evals(row_offset + i - 1)) &
    3978     10180840 :                  - SQRT(qs_ot_env%evals(col_offset + j - 1)))/2.0_dp
    3979              :             b = (SQRT(qs_ot_env%evals(row_offset + i - 1)) &
    3980     10180840 :                  + SQRT(qs_ot_env%evals(col_offset + j - 1)))/2.0_dp
    3981     10642587 :             block(i, j) = -0.5_dp*qs_ot_sinc(a)*qs_ot_sinc(b)
    3982              :          END DO
    3983              :          END DO
    3984              :       END DO
    3985        51099 :       CALL dbcsr_iterator_stop(iter)
    3986              : 
    3987        51099 :       CALL dbcsr_copy(qs_ot_env%matrix_sinp_b, qs_ot_env%matrix_sinp)
    3988        51099 :       CALL dbcsr_iterator_start(iter, qs_ot_env%matrix_sinp_b)
    3989        87340 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    3990              :          CALL dbcsr_iterator_next_block(iter, row, col, block, &
    3991              :                                         row_size=row_size, col_size=col_size, &
    3992        36241 :                                         row_offset=row_offset, col_offset=col_offset)
    3993       549087 :          DO j = 1, col_size
    3994     10678828 :          DO i = 1, row_size
    3995     10180840 :             a = SQRT(qs_ot_env%evals(row_offset + i - 1))
    3996     10180840 :             b = SQRT(qs_ot_env%evals(col_offset + j - 1))
    3997     10642587 :             block(i, j) = qs_ot_sincf(a, b)
    3998              :          END DO
    3999              :          END DO
    4000              :       END DO
    4001        51099 :       CALL dbcsr_iterator_stop(iter)
    4002              : 
    4003        51099 :       CALL timestop(handle)
    4004              : 
    4005        51099 :    END SUBROUTINE qs_ot_p2m_diag
    4006              : 
    4007              : ! **************************************************************************************************
    4008              : !> \brief diagonalize Hermitian P and build cos(sqrt(P)), sinc(sqrt(P)), and Frechet kernels
    4009              : !> \param qs_ot_env complex STRICT channel state
    4010              : ! **************************************************************************************************
    4011        14208 :    SUBROUTINE qs_ot_p2m_diag_complex(qs_ot_env)
    4012              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    4013              : 
    4014              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_p2m_diag_complex'
    4015              : 
    4016              :       INTEGER                                            :: col, col_offset, col_size, handle, i, j, &
    4017              :                                                             k, row, row_offset, row_size
    4018         3552 :       REAL(dp), DIMENSION(:, :), POINTER                 :: block
    4019              :       REAL(KIND=dp)                                      :: a, b
    4020              :       TYPE(dbcsr_iterator_type)                          :: iter
    4021              : 
    4022         3552 :       CALL timeset(routineN, handle)
    4023              : 
    4024         3552 :       CALL dbcsr_get_info(qs_ot_env%matrix_p, nfullrows_total=k)
    4025              :       CALL cp_dbcsr_heevd(matrix_re=qs_ot_env%matrix_p, matrix_im=qs_ot_env%matrix_p_im, &
    4026              :                           eigenvectors_re=qs_ot_env%matrix_r, eigenvectors_im=qs_ot_env%matrix_r_im, &
    4027              :                           eigenvalues=qs_ot_env%evals, para_env=qs_ot_env%para_env, &
    4028         3552 :                           blacs_env=qs_ot_env%blacs_env)
    4029        22360 :       DO i = 1, k
    4030        22360 :          qs_ot_env%evals(i) = MAX(0.0_dp, qs_ot_env%evals(i))
    4031              :       END DO
    4032              : 
    4033        22360 :       DO i = 1, k
    4034        22360 :          qs_ot_env%dum(i) = COS(SQRT(qs_ot_env%evals(i)))
    4035              :       END DO
    4036         3552 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1, qs_ot_env%matrix_r)
    4037         3552 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1_im, qs_ot_env%matrix_r_im)
    4038         3552 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1, alpha=qs_ot_env%dum, side='right')
    4039         3552 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1_im, alpha=qs_ot_env%dum, side='right')
    4040              :       CALL qs_ot_complex_multiply('N', 'C', qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    4041              :                                   qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    4042              :                                   qs_ot_env%matrix_cosp, qs_ot_env%matrix_cosp_im, &
    4043         3552 :                                   qs_ot_env%matrix_buf2)
    4044              : 
    4045        22360 :       DO i = 1, k
    4046        22360 :          qs_ot_env%dum(i) = qs_ot_sinc(SQRT(qs_ot_env%evals(i)))
    4047              :       END DO
    4048         3552 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1, qs_ot_env%matrix_r)
    4049         3552 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1_im, qs_ot_env%matrix_r_im)
    4050         3552 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1, alpha=qs_ot_env%dum, side='right')
    4051         3552 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1_im, alpha=qs_ot_env%dum, side='right')
    4052              :       CALL qs_ot_complex_multiply('N', 'C', qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    4053              :                                   qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    4054              :                                   qs_ot_env%matrix_sinp, qs_ot_env%matrix_sinp_im, &
    4055         3552 :                                   qs_ot_env%matrix_buf2)
    4056              : 
    4057         3552 :       CALL dbcsr_copy(qs_ot_env%matrix_cosp_b, qs_ot_env%matrix_cosp)
    4058         3552 :       CALL dbcsr_iterator_start(iter, qs_ot_env%matrix_cosp_b)
    4059         6335 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    4060              :          CALL dbcsr_iterator_next_block(iter, row, col, block, &
    4061              :                                         row_size=row_size, col_size=col_size, &
    4062         2783 :                                         row_offset=row_offset, col_offset=col_offset)
    4063        21169 :          DO j = 1, col_size
    4064       155041 :          DO i = 1, row_size
    4065              :             a = (SQRT(qs_ot_env%evals(row_offset + i - 1)) &
    4066       137424 :                  - SQRT(qs_ot_env%evals(col_offset + j - 1)))/2.0_dp
    4067              :             b = (SQRT(qs_ot_env%evals(row_offset + i - 1)) &
    4068       137424 :                  + SQRT(qs_ot_env%evals(col_offset + j - 1)))/2.0_dp
    4069       152258 :             block(i, j) = -0.5_dp*qs_ot_sinc(a)*qs_ot_sinc(b)
    4070              :          END DO
    4071              :          END DO
    4072              :       END DO
    4073         3552 :       CALL dbcsr_iterator_stop(iter)
    4074              : 
    4075         3552 :       CALL dbcsr_copy(qs_ot_env%matrix_sinp_b, qs_ot_env%matrix_sinp)
    4076         3552 :       CALL dbcsr_iterator_start(iter, qs_ot_env%matrix_sinp_b)
    4077         6335 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    4078              :          CALL dbcsr_iterator_next_block(iter, row, col, block, &
    4079              :                                         row_size=row_size, col_size=col_size, &
    4080         2783 :                                         row_offset=row_offset, col_offset=col_offset)
    4081        21169 :          DO j = 1, col_size
    4082       155041 :          DO i = 1, row_size
    4083       137424 :             a = SQRT(qs_ot_env%evals(row_offset + i - 1))
    4084       137424 :             b = SQRT(qs_ot_env%evals(col_offset + j - 1))
    4085       152258 :             block(i, j) = qs_ot_sincf(a, b)
    4086              :          END DO
    4087              :          END DO
    4088              :       END DO
    4089         3552 :       CALL dbcsr_iterator_stop(iter)
    4090              : 
    4091         3552 :       CALL timestop(handle)
    4092              : 
    4093         3552 :    END SUBROUTINE qs_ot_p2m_diag_complex
    4094              : 
    4095              : ! **************************************************************************************************
    4096              : !> \brief computes sin(x)/x for all values of the argument
    4097              : !> \param x ...
    4098              : !> \return ...
    4099              : ! **************************************************************************************************
    4100     28856597 :    FUNCTION qs_ot_sinc(x)
    4101              : 
    4102              :       REAL(KIND=dp), INTENT(IN)                          :: x
    4103              :       REAL(KIND=dp)                                      :: qs_ot_sinc
    4104              : 
    4105              :       REAL(KIND=dp), PARAMETER :: q1 = 1.0_dp, q2 = -q1/(2.0_dp*3.0_dp), q3 = -q2/(4.0_dp*5.0_dp), &
    4106              :          q4 = -q3/(6.0_dp*7.0_dp), q5 = -q4/(8.0_dp*9.0_dp), q6 = -q5/(10.0_dp*11.0_dp), &
    4107              :          q7 = -q6/(12.0_dp*13.0_dp), q8 = -q7/(14.0_dp*15.0_dp), q9 = -q8/(16.0_dp*17.0_dp), &
    4108              :          q10 = -q9/(18.0_dp*19.0_dp)
    4109              : 
    4110              :       REAL(KIND=dp)                                      :: y
    4111              : 
    4112     28856597 :       IF (ABS(x) > 0.5_dp) THEN
    4113      8686659 :          qs_ot_sinc = SIN(x)/x
    4114              :       ELSE
    4115     20169938 :          y = x*x
    4116     20169938 :          qs_ot_sinc = q1 + y*(q2 + y*(q3 + y*(q4 + y*(q5 + y*(q6 + y*(q7 + y*(q8 + y*(q9 + y*(q10)))))))))
    4117              :       END IF
    4118     28856597 :    END FUNCTION qs_ot_sinc
    4119              : 
    4120              : ! **************************************************************************************************
    4121              : !> \brief computes (1/(x^2-y^2))*(sinc(x)-sinc(y)) for all positive values of the arguments
    4122              : !> \param xa ...
    4123              : !> \param ya ...
    4124              : !> \return ...
    4125              : ! **************************************************************************************************
    4126     10318264 :    FUNCTION qs_ot_sincf(xa, ya)
    4127              : 
    4128              :       REAL(KIND=dp), INTENT(IN)                          :: xa, ya
    4129              :       REAL(KIND=dp)                                      :: qs_ot_sincf
    4130              : 
    4131              :       INTEGER                                            :: i
    4132              :       REAL(KIND=dp)                                      :: a, b, rs, sf, x, xs, y, ybx, ybxs
    4133              : 
    4134              :       ! this is currently a limit of the routine, could be removed rather easily
    4135     10318264 :       IF (xa < 0) CPABORT("x is negative")
    4136     10318264 :       IF (ya < 0) CPABORT("y is negative")
    4137              : 
    4138     10318264 :       IF (xa < ya) THEN
    4139      4929902 :          x = ya
    4140      4929902 :          y = xa
    4141              :       ELSE
    4142      5388362 :          x = xa
    4143      5388362 :          y = ya
    4144              :       END IF
    4145              : 
    4146     10318264 :       IF (x < 0.5_dp) THEN ! use series, keeping in mind that x,y,x+y,x-y can all be zero
    4147              : 
    4148      6457076 :          qs_ot_sincf = 0.0_dp
    4149      6457076 :          IF (x > 0.0_dp) THEN
    4150      6234416 :             ybx = y/x
    4151              :          ELSE ! should be irrelevant  !?
    4152              :             ybx = 0.0_dp
    4153              :          END IF
    4154              : 
    4155      6457076 :          sf = -1.0_dp/((1.0_dp + ybx)*6.0_dp)
    4156      6457076 :          rs = 1.0_dp
    4157      6457076 :          ybxs = ybx
    4158      6457076 :          xs = 1.0_dp
    4159              : 
    4160     71027836 :          DO i = 1, 10
    4161     64570760 :             qs_ot_sincf = qs_ot_sincf + sf*rs*xs*(1.0_dp + ybxs)
    4162     64570760 :             sf = -sf/(REAL((2*i + 2), dp)*REAL((2*i + 3), dp))
    4163     64570760 :             rs = rs + ybxs
    4164     64570760 :             ybxs = ybxs*ybx
    4165     71027836 :             xs = xs*x*x
    4166              :          END DO
    4167              : 
    4168              :       ELSE ! no series expansion
    4169      3861188 :          IF (x - y > 0.1_dp) THEN ! safe to use the normal form
    4170      3574872 :             qs_ot_sincf = (qs_ot_sinc(x) - qs_ot_sinc(y))/((x + y)*(x - y))
    4171              :          ELSE
    4172       286316 :             a = (x + y)/2.0_dp
    4173       286316 :             b = (x - y)/2.0_dp ! might be close to zero
    4174              :             ! y (=(a-b)) can not be close to zero since it is close to x>0.5
    4175       286316 :             qs_ot_sincf = (qs_ot_sinc(b)*COS(a) - qs_ot_sinc(a)*COS(b))/(2*x*y)
    4176              :          END IF
    4177              :       END IF
    4178              : 
    4179     10318264 :    END FUNCTION qs_ot_sincf
    4180              : 
    4181        67208 : END MODULE qs_ot
        

Generated by: LCOV version 2.0-1