LCOV - code coverage report
Current view: top level - src - qs_ot.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:5c1df3d) Lines: 96.1 % 1862 1790
Test Date: 2026-09-14 06:34:43 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         2212 :    PURE SUBROUTINE qs_ot_fixed_n_energy_gradient( &
     154         2212 :       rayleigh_energy, energy_coordinate, response_weight, fixed_n_weight_sum, &
     155         2212 :       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         2212 :       fixed_n_mean = 0.0_dp
     165         2212 :       IF (ABS(fixed_n_weight_sum) > EPSILON(fixed_n_weight_sum)) THEN
     166         2168 :          fixed_n_mean = fixed_n_weighted_residual/fixed_n_weight_sum
     167              :       END IF
     168              :       gradient(:) = response_weight(:)* &
     169        17346 :                     (fixed_n_mean - (rayleigh_energy(:) - energy_coordinate(:)))
     170              : 
     171         2212 :    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          338 :    SUBROUTINE qs_ot_fixed_n_multigroup_schur_block( &
     268          676 :       rotation_hessian, rayleigh_response, response_weight, response_group, &
     269          676 :       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          338 :       nenergy = SIZE(response_weight)
     281          338 :       nrotation = SIZE(rotation_gradient)
     282          338 :       ngroups = SIZE(coupling_matrix, 2)
     283          338 :       CPASSERT(ngroups > 0)
     284         1014 :       CPASSERT(ALL(SHAPE(rotation_hessian) == [nrotation, nrotation]))
     285         1014 :       CPASSERT(ALL(SHAPE(rayleigh_response) == [nenergy, nrotation]))
     286          338 :       CPASSERT(SIZE(energy_gradient) == nenergy)
     287          338 :       CPASSERT(SIZE(response_group) == nenergy)
     288         2304 :       CPASSERT(ALL(response_group >= 1 .AND. response_group <= ngroups))
     289         1014 :       CPASSERT(ALL(SHAPE(schur_block) == [nrotation, nrotation]))
     290          338 :       CPASSERT(SIZE(coupling_matrix, 1) == nrotation)
     291          338 :       CPASSERT(SIZE(schur_rhs) == nrotation)
     292              : 
     293       505370 :       schur_block(:, :) = rotation_hessian(:, :)
     294        11942 :       coupling_matrix(:, :) = 0.0_dp
     295         2304 :       DO i = 1, nenergy
     296         1966 :          weight = response_weight(i)
     297              :          schur_block(:, :) = schur_block(:, :) - weight* &
     298              :                              SPREAD(rayleigh_response(i, :), DIM=2, NCOPIES=nrotation)* &
     299      5497258 :                              SPREAD(rayleigh_response(i, :), DIM=1, NCOPIES=nrotation)
     300         1966 :          group = response_group(i)
     301              :          coupling_matrix(:, group) = coupling_matrix(:, group) + &
     302        85404 :                                      weight*rayleigh_response(i, :)
     303              :       END DO
     304      1010740 :       schur_block(:, :) = 0.5_dp*(schur_block + TRANSPOSE(schur_block))
     305              :       schur_rhs(:) = rotation_gradient + &
     306        10282 :                      MATMUL(TRANSPOSE(rayleigh_response), energy_gradient)
     307              : 
     308          338 :    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          336 :    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          336 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenvalues
     333          336 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: eigenvectors, work
     334              : 
     335          336 :       n = SIZE(matrix, 1)
     336         1008 :       CPASSERT(ALL(SHAPE(matrix) == [n, n]))
     337          336 :       CPASSERT(SIZE(rhs, 1) == n)
     338         1008 :       CPASSERT(ALL(SHAPE(solution) == SHAPE(rhs)))
     339       527046 :       valid = ALL(matrix == matrix) .AND. ALL(rhs == rhs)
     340          336 :       IF (.NOT. valid) THEN
     341            0 :          solution(:, :) = 0.0_dp
     342              :          RETURN
     343              :       END IF
     344              : 
     345          336 :       relative_floor_eff = SQRT(EPSILON(1.0_dp))
     346          336 :       IF (PRESENT(relative_floor)) relative_floor_eff = MAX(relative_floor, relative_floor_eff)
     347         3024 :       ALLOCATE (eigenvalues(n), eigenvectors(n, n), work(n, SIZE(rhs, 2)))
     348       505224 :       eigenvectors(:, :) = 0.5_dp*(matrix + TRANSPOSE(matrix))
     349          336 :       CALL diamat_all(eigenvectors, eigenvalues)
     350         9924 :       scale = MAX(1.0_dp, MAXVAL(ABS(eigenvalues)))
     351          336 :       eigenvalue_floor = relative_floor_eff*scale
     352      2246760 :       work(:, :) = MATMUL(TRANSPOSE(eigenvectors), rhs)
     353          336 :       nresolved = 0
     354         9924 :       DO i = 1, n
     355         9924 :          IF (ABS(eigenvalues(i)) > eigenvalue_floor) THEN
     356        15711 :             work(i, :) = work(i, :)/ABS(eigenvalues(i))
     357         5013 :             nresolved = nresolved + 1
     358              :          ELSE
     359        14649 :             work(i, :) = 0.0_dp
     360              :          END IF
     361              :       END DO
     362      2259372 :       solution(:, :) = MATMUL(eigenvectors, work)
     363        21822 :       valid = nresolved > 0 .AND. ALL(solution == solution)
     364          708 :       IF (.NOT. valid) solution(:, :) = 0.0_dp
     365          336 :       DEALLOCATE (eigenvalues, eigenvectors, work)
     366              : 
     367          336 :    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          174 :    SUBROUTINE qs_ot_density_secant_projected_hessian( &
     590          174 :       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          348 :       REAL(KIND=dp), DIMENSION(SIZE(density_overlap))    :: projected_density_overlap, &
     603          174 :                                                             projected_response_overlap
     604              : 
     605          174 :       nmode = SIZE(density_overlap)
     606          174 :       CPASSERT(SIZE(response_overlap) == nmode)
     607          522 :       CPASSERT(ALL(SHAPE(correction) == [nmode, nmode]))
     608              : 
     609         1218 :       correction(:, :) = 0.0_dp
     610          174 :       valid = .FALSE.
     611          174 :       IF (nmode <= 0 .OR. density_norm_sq <= TINY(1.0_dp)) RETURN
     612          174 :       IF (density_norm_sq /= density_norm_sq .OR. response_work /= response_work) RETURN
     613          174 :       IF (ABS(density_norm_sq) > HUGE(1.0_dp) .OR. ABS(response_work) > HUGE(1.0_dp)) RETURN
     614         1044 :       IF (ANY(density_overlap /= density_overlap) .OR. ANY(response_overlap /= response_overlap)) RETURN
     615         1044 :       IF (ANY(ABS(density_overlap) > HUGE(1.0_dp)) .OR. &
     616              :           ANY(ABS(response_overlap) > HUGE(1.0_dp))) RETURN
     617          174 :       IF (PRESENT(secant_mode) .NEQV. PRESENT(secant_position)) RETURN
     618              : 
     619          522 :       projected_density_overlap(:) = density_overlap
     620          522 :       projected_response_overlap(:) = response_overlap
     621          174 :       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          172 :       inverse_density_norm = 1.0_dp/density_norm_sq
     633          516 :       DO j = 1, nmode
     634         1204 :          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         1032 :                                 projected_density_overlap(i)*projected_density_overlap(j))
     640              :          END DO
     641              :       END DO
     642         2236 :       correction(:, :) = 0.5_dp*(correction + TRANSPOSE(correction))
     643         2408 :       valid = ALL(correction == correction) .AND. ALL(ABS(correction) <= HUGE(1.0_dp))
     644          172 :       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          446 :    SUBROUTINE qs_ot_density_tangent( &
     666          446 :       rotation_generator, occupation, kpoint_weight, rotation_step, &
     667          446 :       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          446 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: direction, generator_minus, &
     677          446 :          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          446 :       n = SIZE(rotation_generator, 1)
     682          446 :       nrotation = n*(n - 1)
     683          446 :       CPASSERT(n > 0)
     684         1338 :       CPASSERT(ALL(SHAPE(rotation_generator) == [n, n]))
     685          446 :       CPASSERT(SIZE(occupation) == n)
     686          446 :       CPASSERT(SIZE(rotation_step) == nrotation)
     687          446 :       CPASSERT(SIZE(weighted_occupation_step) == n)
     688         1338 :       CPASSERT(ALL(SHAPE(density_tangent) == [n, n]))
     689          446 :       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         8028 :                 rotation_plus(n, n), weighted_rotation(n, n))
     694          446 :       direction(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     695          446 :       r = 0
     696         2882 :       DO i = 1, n - 1
     697        11768 :          DO j = i + 1, n
     698         8886 :             r = r + 1
     699         8886 :             direction(i, j) = CMPLX(rotation_step(r), 0.0_dp, KIND=dp)
     700         8886 :             direction(j, i) = -direction(i, j)
     701         8886 :             r = r + 1
     702              :             direction(i, j) = direction(i, j) + &
     703         8886 :                               CMPLX(0.0_dp, rotation_step(r), KIND=dp)
     704              :             direction(j, i) = direction(j, i) + &
     705        11322 :                               CMPLX(0.0_dp, rotation_step(r), KIND=dp)
     706              :          END DO
     707              :       END DO
     708          446 :       CPASSERT(r == nrotation)
     709              : 
     710        23982 :       step_scale = MAX(1.0_dp, MAXVAL(ABS(direction)))
     711          446 :       step = 1.0E-5_dp/step_scale
     712          446 :       IF (PRESENT(difference_step)) step = difference_step/step_scale
     713          446 :       CPASSERT(step > EPSILON(step))
     714        23982 :       generator_plus(:, :) = rotation_generator + step*direction
     715        23982 :       generator_minus(:, :) = rotation_generator - step*direction
     716          446 :       CALL qs_ot_dense_rotation_state(rotation_generator, rotation)
     717          446 :       CALL qs_ot_dense_rotation_state(generator_plus, rotation_plus)
     718          446 :       CALL qs_ot_dense_rotation_state(generator_minus, rotation_minus)
     719              : 
     720        23982 :       weighted_rotation(:, :) = rotation_plus
     721         3328 :       DO j = 1, n
     722        23982 :          weighted_rotation(:, j) = kpoint_weight*occupation(j)*weighted_rotation(:, j)
     723              :       END DO
     724       185792 :       mode_ref(:, :) = MATMUL(weighted_rotation, CONJG(TRANSPOSE(rotation_plus)))
     725        23982 :       weighted_rotation(:, :) = rotation_minus
     726         3328 :       DO j = 1, n
     727        23982 :          weighted_rotation(:, j) = kpoint_weight*occupation(j)*weighted_rotation(:, j)
     728              :       END DO
     729              :       mode_ref(:, :) = (mode_ref - &
     730       210666 :                         MATMUL(weighted_rotation, CONJG(TRANSPOSE(rotation_minus))))/(2.0_dp*step)
     731              : 
     732        23982 :       weighted_rotation(:, :) = rotation
     733         3328 :       DO j = 1, n
     734        23982 :          weighted_rotation(:, j) = weighted_occupation_step(j)*weighted_rotation(:, j)
     735              :       END DO
     736       211112 :       mode_ref(:, :) = mode_ref + MATMUL(weighted_rotation, CONJG(TRANSPOSE(rotation)))
     737       557822 :       density_tangent(:, :) = MATMUL(CONJG(TRANSPOSE(rotation)), MATMUL(mode_ref, rotation))
     738        47518 :       density_tangent(:, :) = 0.5_dp*(density_tangent + CONJG(TRANSPOSE(density_tangent)))
     739              : 
     740            0 :       DEALLOCATE (direction, generator_minus, generator_plus, mode_ref, rotation, &
     741          446 :                   rotation_minus, rotation_plus, weighted_rotation)
     742              : 
     743          446 :    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          260 :    SUBROUTINE qs_ot_density_secant_orbital_overlaps( &
     765          520 :       overlap_start_current, occupation_start, occupation_current, hamiltonian_step_start, &
     766          520 :       hamiltonian_step_current, density_modes, kpoint_weight, density_norm_sq, response_work, &
     767          260 :       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          260 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :)     :: cross_mode
     780              :       INTEGER                                            :: i, j, mode, n, nmode
     781              :       REAL(KIND=dp)                                      :: cross_density, density_scale
     782              : 
     783          260 :       n = SIZE(occupation_start)
     784          260 :       nmode = SIZE(density_modes, 3)
     785          260 :       CPASSERT(n > 0)
     786          260 :       CPASSERT(SIZE(occupation_current) == n)
     787          780 :       CPASSERT(ALL(SHAPE(overlap_start_current) == [n, n]))
     788          780 :       CPASSERT(ALL(SHAPE(hamiltonian_step_start) == [n, n]))
     789          780 :       CPASSERT(ALL(SHAPE(hamiltonian_step_current) == [n, n]))
     790          260 :       CPASSERT(SIZE(density_modes, 1) == n .AND. SIZE(density_modes, 2) == n)
     791          260 :       CPASSERT(SIZE(density_overlap) == nmode .AND. SIZE(response_overlap) == nmode)
     792              : 
     793          260 :       density_norm_sq = 0.0_dp
     794          260 :       response_work = 0.0_dp
     795          780 :       density_overlap(:) = 0.0_dp
     796          780 :       response_overlap(:) = 0.0_dp
     797          260 :       valid = .FALSE.
     798          276 :       IF (nmode <= 0 .OR. kpoint_weight <= TINY(1.0_dp)) RETURN
     799              : 
     800              :       cross_density = 0.0_dp
     801         1926 :       DO j = 1, n
     802        13792 :          DO i = 1, n
     803              :             cross_density = cross_density + occupation_start(i)*occupation_current(j)* &
     804        13532 :                             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         3592 :                          2.0_dp*cross_density)
     810              :       response_work = 0.0_dp
     811         1926 :       DO i = 1, n
     812              :          response_work = response_work + kpoint_weight* &
     813              :                          (occupation_current(i)*REAL(hamiltonian_step_current(i, i), KIND=dp) - &
     814         1926 :                           occupation_start(i)*REAL(hamiltonian_step_start(i, i), KIND=dp))
     815              :       END DO
     816              : 
     817         1040 :       ALLOCATE (cross_mode(n, n))
     818          780 :       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          520 :                           overlap_start_current, "C", cross_mode)
     822         3852 :          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         3852 :                                     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        27844 :                 AIMAG(hamiltonian_step_current)*AIMAG(density_modes(:, :, mode)))
     832              :       END DO
     833          260 :       DEALLOCATE (cross_mode)
     834              : 
     835              :       density_scale = kpoint_weight* &
     836         3592 :                       MAX(MAXVAL(ABS(occupation_start)), MAXVAL(ABS(occupation_current)))
     837          260 :       IF (density_scale <= TINY(1.0_dp)) RETURN
     838          260 :       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         2928 :               ALL(ABS(response_overlap) <= HUGE(1.0_dp))
     845          244 :       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          354 :    SUBROUTINE qs_ot_finite_rotation_response( &
     962          354 :       chc, rotation_generator, occupation, kpoint_weight, rotation_gradient, &
     963          354 :       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          354 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :)     :: base_hamiltonian, generator_minus, &
     972          354 :                                                             generator_plus, rotation
     973              :       INTEGER                                            :: i, j, n, nrotation, r, s
     974              :       REAL(KIND=dp)                                      :: step
     975          354 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: occupation_scale, rayleigh, &
     976          354 :                                                             rayleigh_minus, rayleigh_plus
     977          354 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: gradient_imag, gradient_minus_imag, &
     978          354 :          gradient_minus_real, gradient_plus_imag, gradient_plus_real, gradient_real
     979              : 
     980          354 :       n = SIZE(chc, 1)
     981          354 :       nrotation = n*(n - 1)
     982          354 :       CPASSERT(n > 0)
     983         1062 :       CPASSERT(ALL(SHAPE(chc) == [n, n]))
     984         1062 :       CPASSERT(ALL(SHAPE(rotation_generator) == [n, n]))
     985          354 :       CPASSERT(SIZE(occupation) == n)
     986          354 :       CPASSERT(SIZE(rotation_gradient) == nrotation)
     987         1062 :       CPASSERT(ALL(SHAPE(rotation_hessian) == [nrotation, nrotation]))
     988         1062 :       CPASSERT(ALL(SHAPE(rayleigh_response) == [n, nrotation]))
     989          354 :       CPASSERT(kpoint_weight > 0.0_dp)
     990              : 
     991          354 :       step = 1.0E-4_dp
     992          354 :       IF (PRESENT(difference_step)) step = difference_step
     993          354 :       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         9912 :                 gradient_plus_real(n, n), gradient_real(n, n))
    1000              : 
    1001          354 :       CALL qs_ot_dense_rotation_state(rotation_generator, rotation)
    1002       318920 :       base_hamiltonian(:, :) = MATMUL(rotation, MATMUL(chc, CONJG(TRANSPOSE(rotation))))
    1003         2318 :       occupation_scale(:) = 2.0_dp*kpoint_weight*occupation(:)
    1004              :       CALL qs_ot_dense_rotation_gradient(rotation_generator, base_hamiltonian, occupation_scale, &
    1005          354 :                                          gradient_real, gradient_imag, rayleigh)
    1006              : 
    1007          354 :       r = 0
    1008         1964 :       DO i = 1, n - 1
    1009         7406 :          DO j = i + 1, n
    1010         5442 :             r = r + 1
    1011         5442 :             rotation_gradient(r) = gradient_real(i, j)
    1012         5442 :             r = r + 1
    1013         7052 :             rotation_gradient(r) = gradient_imag(i, j)
    1014              :          END DO
    1015              :       END DO
    1016          354 :       CPASSERT(r == nrotation)
    1017              : 
    1018              :       s = 0
    1019         1964 :       DO i = 1, n - 1
    1020         7406 :          DO j = i + 1, n
    1021       393030 :             generator_plus(:, :) = rotation_generator(:, :)
    1022       393030 :             generator_minus(:, :) = rotation_generator(:, :)
    1023         5442 :             generator_plus(i, j) = generator_plus(i, j) + step
    1024         5442 :             generator_plus(j, i) = generator_plus(j, i) - step
    1025         5442 :             generator_minus(i, j) = generator_minus(i, j) - step
    1026         5442 :             generator_minus(j, i) = generator_minus(j, i) + step
    1027         5442 :             s = s + 1
    1028              :             CALL qs_ot_dense_rotation_gradient(generator_plus, base_hamiltonian, occupation_scale, &
    1029         5442 :                                                gradient_plus_real, gradient_plus_imag, rayleigh_plus)
    1030              :             CALL qs_ot_dense_rotation_gradient(generator_minus, base_hamiltonian, occupation_scale, &
    1031         5442 :                                                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         5442 :                rayleigh_response(:, s))
    1036              : 
    1037       393030 :             generator_plus(:, :) = rotation_generator(:, :)
    1038       393030 :             generator_minus(:, :) = rotation_generator(:, :)
    1039         5442 :             generator_plus(i, j) = generator_plus(i, j) + CMPLX(0.0_dp, step, KIND=dp)
    1040         5442 :             generator_plus(j, i) = generator_plus(j, i) + CMPLX(0.0_dp, step, KIND=dp)
    1041         5442 :             generator_minus(i, j) = generator_minus(i, j) - CMPLX(0.0_dp, step, KIND=dp)
    1042         5442 :             generator_minus(j, i) = generator_minus(j, i) - CMPLX(0.0_dp, step, KIND=dp)
    1043         5442 :             s = s + 1
    1044              :             CALL qs_ot_dense_rotation_gradient(generator_plus, base_hamiltonian, occupation_scale, &
    1045         5442 :                                                gradient_plus_real, gradient_plus_imag, rayleigh_plus)
    1046              :             CALL qs_ot_dense_rotation_gradient(generator_minus, base_hamiltonian, occupation_scale, &
    1047         5442 :                                                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         7052 :                rayleigh_response(:, s))
    1052              :          END DO
    1053              :       END DO
    1054          354 :       CPASSERT(s == nrotation)
    1055      1240380 :       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          354 :                   gradient_minus_real, gradient_plus_imag, gradient_plus_real, gradient_real)
    1060              : 
    1061          354 :    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        10884 :    SUBROUTINE qs_ot_pack_rotation_response( &
    1076        10884 :       gradient_plus_real, gradient_plus_imag, gradient_minus_real, gradient_minus_imag, &
    1077        10884 :       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        10884 :       n = SIZE(gradient_plus_real, 1)
    1088        32652 :       CPASSERT(ALL(SHAPE(gradient_plus_real) == [n, n]))
    1089        32652 :       CPASSERT(ALL(SHAPE(gradient_plus_imag) == [n, n]))
    1090        32652 :       CPASSERT(ALL(SHAPE(gradient_minus_real) == [n, n]))
    1091        32652 :       CPASSERT(ALL(SHAPE(gradient_minus_imag) == [n, n]))
    1092        10884 :       CPASSERT(SIZE(hessian_column) == n*(n - 1))
    1093        10884 :       CPASSERT(SIZE(rayleigh_column) == n)
    1094              : 
    1095        10884 :       r = 0
    1096        83112 :       DO i = 1, n - 1
    1097       387588 :          DO j = i + 1, n
    1098       304476 :             r = r + 1
    1099              :             hessian_column(r) = &
    1100       304476 :                (gradient_plus_real(i, j) - gradient_minus_real(i, j))/(2.0_dp*step)
    1101       304476 :             r = r + 1
    1102              :             hessian_column(r) = &
    1103       376704 :                (gradient_plus_imag(i, j) - gradient_minus_imag(i, j))/(2.0_dp*step)
    1104              :          END DO
    1105              :       END DO
    1106        93996 :       rayleigh_column(:) = (rayleigh_plus(:) - rayleigh_minus(:))/(2.0_dp*step)
    1107              : 
    1108        10884 :    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        22122 :    SUBROUTINE qs_ot_dense_rotation_gradient( &
    1120        22122 :       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        22122 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :)     :: covector, eigenvectors, frechet, inner, &
    1128        22122 :                                                             outer, rotation, work
    1129              :       INTEGER                                            :: i, j, n
    1130              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenvalues
    1131              : 
    1132        22122 :       n = SIZE(rotation_generator, 1)
    1133              :       ALLOCATE (covector(n, n), eigenvectors(n, n), frechet(n, n), inner(n, n), &
    1134       398196 :                 outer(n, n), rotation(n, n), work(n, n), eigenvalues(n))
    1135              :       CALL qs_ot_dense_rotation_state(rotation_generator, rotation, &
    1136        22122 :                                       eigenvectors=eigenvectors, eigenvalues=eigenvalues)
    1137              : 
    1138     13892046 :       work(:, :) = MATMUL(base_hamiltonian, rotation)
    1139      1587286 :       covector(:, :) = work(:, :)
    1140       190310 :       DO j = 1, n
    1141      1587286 :          covector(:, j) = occupation_scale(j)*covector(:, j)
    1142              :       END DO
    1143              : 
    1144        22122 :       work(:, :) = MATMUL(CONJG(TRANSPOSE(rotation)), &
    1145     40155218 :                           MATMUL(base_hamiltonian, rotation))
    1146       190310 :       DO i = 1, n
    1147       190310 :          rayleigh(i) = REAL(work(i, i), KIND=dp)
    1148              :       END DO
    1149              : 
    1150        22122 :       inner(:, :) = MATMUL(CONJG(TRANSPOSE(eigenvectors)), &
    1151     40155218 :                            MATMUL(covector, eigenvectors))
    1152       190310 :       DO j = 1, n
    1153      1587286 :          DO i = 1, n
    1154      1396976 :             kernel = CONJG(qs_ot_complex_exp_frechet_kernel(eigenvalues(i), eigenvalues(j)))
    1155      1565164 :             outer(i, j) = inner(i, j)*kernel
    1156              :          END DO
    1157              :       END DO
    1158        22122 :       frechet(:, :) = MATMUL(eigenvectors, &
    1159     40155218 :                              MATMUL(outer, CONJG(TRANSPOSE(eigenvectors))))
    1160      1587286 :       gradient_real(:, :) = REAL(frechet, KIND=dp) - TRANSPOSE(REAL(frechet, KIND=dp))
    1161      1587286 :       gradient_imag(:, :) = AIMAG(frechet) + TRANSPOSE(AIMAG(frechet))
    1162              : 
    1163        22122 :       DEALLOCATE (covector, eigenvectors, frechet, inner, outer, rotation, work, eigenvalues)
    1164              : 
    1165        22122 :    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        23814 :    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        23814 :       n = SIZE(rotation_generator, 1)
    1186       190512 :       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      1674398 :                         vectors, values)
    1189      1674398 :       weighted_vectors(:, :) = vectors(:, :)
    1190       202612 :       DO j = 1, n
    1191              :          weighted_vectors(:, j) = EXP(CMPLX(0.0_dp, -values(j), KIND=dp))* &
    1192      1674398 :                                   weighted_vectors(:, j)
    1193              :       END DO
    1194     29097282 :       rotation(:, :) = MATMUL(weighted_vectors, CONJG(TRANSPOSE(vectors)))
    1195      1588978 :       IF (PRESENT(eigenvectors)) eigenvectors(:, :) = vectors(:, :)
    1196       192002 :       IF (PRESENT(eigenvalues)) eigenvalues(:) = values(:)
    1197        23814 :       DEALLOCATE (vectors, weighted_vectors, values)
    1198              : 
    1199        23814 :    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      1514546 :    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      1514546 :       l1 = (0.0_dp, -1.0_dp)*e1
    1215      1514546 :       l2 = (0.0_dp, -1.0_dp)*e2
    1216      1514546 :       IF (ABS(l1 - l2) > 0.5_dp) THEN
    1217         8660 :          kernel = (EXP(l1) - EXP(l2))/(l1 - l2)
    1218              :       ELSE
    1219              :          x = 1.0_dp
    1220              :          kernel = 0.0_dp
    1221     25600062 :          DO i = 1, 16
    1222     24094176 :             kernel = kernel + x
    1223     25600062 :             x = x*(l1 - l2)/REAL(i + 1, KIND=dp)
    1224              :          END DO
    1225      1505886 :          kernel = kernel*EXP(l2)
    1226              :       END IF
    1227              : 
    1228      1514546 :    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         2039 :    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         2039 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: cols, rows
    1249         2039 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_offset, col_blk_size, &
    1250         2039 :                                                             row_blk_offset, row_blk_size
    1251              :       LOGICAL                                            :: found_im, found_out_im, found_out_re, &
    1252              :                                                             found_re, use_adjoint
    1253         2039 :       REAL(dp), DIMENSION(:, :), POINTER                 :: block_in_im, block_in_re, block_out_im, &
    1254         2039 :                                                             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         2039 :       use_adjoint = .FALSE.
    1260         2037 :       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         2039 :       max_blocks = 0
    1265         2039 :       CALL dbcsr_iterator_start(iter, inner_deriv_re)
    1266         3211 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1267         1172 :          CALL dbcsr_iterator_next_block(iter, row, col)
    1268         1172 :          max_blocks = max_blocks + 1
    1269              :       END DO
    1270         2039 :       CALL dbcsr_iterator_stop(iter)
    1271         2039 :       CALL dbcsr_iterator_start(iter, inner_deriv_im)
    1272         3211 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1273         1172 :          CALL dbcsr_iterator_next_block(iter, row, col)
    1274         1172 :          max_blocks = max_blocks + 1
    1275              :       END DO
    1276         2039 :       CALL dbcsr_iterator_stop(iter)
    1277         8156 :       ALLOCATE (rows(MAX(max_blocks, 1)), cols(MAX(max_blocks, 1)))
    1278         2039 :       nblocks = 0
    1279              : 
    1280         2039 :       CALL dbcsr_iterator_start(iter, inner_deriv_re)
    1281         3211 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1282         1172 :          CALL dbcsr_iterator_next_block(iter, row, col)
    1283         1172 :          CALL append_union_block(row, col)
    1284              :       END DO
    1285         2039 :       CALL dbcsr_iterator_stop(iter)
    1286         2039 :       CALL dbcsr_iterator_start(iter, inner_deriv_im)
    1287         3211 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1288         1172 :          CALL dbcsr_iterator_next_block(iter, row, col)
    1289         1172 :          CALL append_union_block(row, col)
    1290              :       END DO
    1291         2039 :       CALL dbcsr_iterator_stop(iter)
    1292              : 
    1293              :       CALL dbcsr_get_info(inner_deriv_re, distribution=dist, row_blk_size=row_blk_size, &
    1294         2039 :                           col_blk_size=col_blk_size)
    1295              :       CALL dbcsr_create(outer_deriv_re, "outer_deriv_re", dist, dbcsr_type_no_symmetry, &
    1296         2039 :                         row_blk_size, col_blk_size)
    1297              :       CALL dbcsr_create(outer_deriv_im, "outer_deriv_im", dist, dbcsr_type_no_symmetry, &
    1298         2039 :                         row_blk_size, col_blk_size)
    1299         2039 :       IF (nblocks > 0) THEN
    1300         1172 :          CALL dbcsr_reserve_blocks(outer_deriv_re, rows=rows(1:nblocks), cols=cols(1:nblocks))
    1301         1172 :          CALL dbcsr_reserve_blocks(outer_deriv_im, rows=rows(1:nblocks), cols=cols(1:nblocks))
    1302              :       END IF
    1303         2039 :       CALL dbcsr_finalize(outer_deriv_re)
    1304         2039 :       CALL dbcsr_finalize(outer_deriv_im)
    1305         2039 :       CALL dbcsr_set(outer_deriv_re, 0.0_dp)
    1306         2039 :       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         2039 :                           col_blk_offset=col_blk_offset)
    1310         2039 :       CALL dbcsr_iterator_start(iter, outer_deriv_re)
    1311         3213 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1312         1174 :          CALL dbcsr_iterator_next_block(iter, row, col)
    1313         1174 :          CALL dbcsr_get_block_p(inner_deriv_re, row, col, block_in_re, found_re)
    1314         1174 :          CALL dbcsr_get_block_p(inner_deriv_im, row, col, block_in_im, found_im)
    1315         1174 :          CALL dbcsr_get_block_p(outer_deriv_re, row, col, block_out_re, found_out_re)
    1316         1174 :          CALL dbcsr_get_block_p(outer_deriv_im, row, col, block_out_im, found_out_im)
    1317         1174 :          CPASSERT(found_out_re .AND. found_out_im)
    1318              : 
    1319        12837 :          DO i = 1, SIZE(block_out_re, 1)
    1320       128346 :          DO j = 1, SIZE(block_out_re, 2)
    1321       117548 :             e1 = evals(row_blk_offset(row) + i - 1)
    1322       117548 :             e2 = evals(col_blk_offset(col) + j - 1)
    1323       117548 :             re_part = 0.0_dp
    1324       117548 :             im_part = 0.0_dp
    1325       117548 :             IF (found_re) re_part = block_in_re(i, j)
    1326       117548 :             IF (found_im) im_part = block_in_im(i, j)
    1327       117548 :             cval_in = CMPLX(re_part, im_part, dp)
    1328       117548 :             kernel = qs_ot_complex_exp_frechet_kernel(e1, e2)
    1329       117548 :             IF (use_adjoint) kernel = CONJG(kernel)
    1330       117548 :             cval_in = cval_in*kernel
    1331       117548 :             block_out_re(i, j) = REAL(cval_in, KIND=dp)
    1332       127172 :             block_out_im(i, j) = AIMAG(cval_in)
    1333              :          END DO
    1334              :          END DO
    1335              :       END DO
    1336         2039 :       CALL dbcsr_iterator_stop(iter)
    1337        16310 :       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         2344 :       SUBROUTINE append_union_block(row_new, col_new)
    1347              :       INTEGER, INTENT(IN)                                :: row_new, col_new
    1348              : 
    1349              :       INTEGER                                            :: iblock
    1350              : 
    1351         2346 :          DO iblock = 1, nblocks
    1352         2346 :             IF (rows(iblock) == row_new .AND. cols(iblock) == col_new) RETURN
    1353              :          END DO
    1354         1174 :          nblocks = nblocks + 1
    1355         1174 :          rows(nblocks) = row_new
    1356         1174 :          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         9001 :    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         9001 :       qs_ot_env%preconditioner => preconditioner
    1377         9001 :       qs_ot_env%os_valid = .FALSE.
    1378         9001 :       IF (.NOT. ASSOCIATED(qs_ot_env%matrix_psc0)) THEN
    1379         9001 :          CALL dbcsr_init_p(qs_ot_env%matrix_psc0)
    1380         9001 :          CALL dbcsr_copy(qs_ot_env%matrix_psc0, qs_ot_env%matrix_sc0, 'matrix_psc0')
    1381              :       END IF
    1382         9001 :       IF (qs_ot_env%has_complex_kpoint_state .AND. &
    1383              :           .NOT. ASSOCIATED(qs_ot_env%matrix_psc0_im)) THEN
    1384          423 :          CALL dbcsr_init_p(qs_ot_env%matrix_psc0_im)
    1385          423 :          CALL dbcsr_copy(qs_ot_env%matrix_psc0_im, qs_ot_env%matrix_sc0_im, 'matrix_psc0_im')
    1386              :       END IF
    1387              : 
    1388         9001 :       IF (.NOT. qs_ot_env%use_dx) THEN
    1389         5220 :          qs_ot_env%use_dx = .TRUE.
    1390         5220 :          CALL dbcsr_init_p(qs_ot_env%matrix_dx)
    1391         5220 :          CALL dbcsr_copy(qs_ot_env%matrix_dx, qs_ot_env%matrix_gx, 'matrix_dx')
    1392         5220 :          IF (qs_ot_env%has_complex_kpoint_state) THEN
    1393          123 :             CALL dbcsr_init_p(qs_ot_env%matrix_dx_im)
    1394          123 :             CALL dbcsr_copy(qs_ot_env%matrix_dx_im, qs_ot_env%matrix_gx_im, 'matrix_dx_im')
    1395              :          END IF
    1396         5220 :          IF (qs_ot_env%settings%do_rotation) THEN
    1397           86 :             CALL dbcsr_init_p(qs_ot_env%rot_mat_dx)
    1398           86 :             CALL dbcsr_copy(qs_ot_env%rot_mat_dx, qs_ot_env%rot_mat_gx, 'rot_mat_dx')
    1399           86 :             IF (qs_ot_env%has_complex_kpoint_state) THEN
    1400           56 :                CALL dbcsr_init_p(qs_ot_env%rot_mat_dx_im)
    1401           56 :                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         5220 :          IF (qs_ot_env%settings%do_ener) THEN
    1405           48 :             ncoef = SIZE(qs_ot_env%ener_gx)
    1406          144 :             ALLOCATE (qs_ot_env%ener_dx(ncoef))
    1407          384 :             qs_ot_env%ener_dx = 0.0_dp
    1408              :          END IF
    1409              :       END IF
    1410              : 
    1411         9001 :    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        40184 :    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        27380 :       SELECT CASE (op_a)
    1433              :       CASE ('N')
    1434        27380 :          db_op_a = 'N'
    1435        27380 :          sign_a = 1.0_dp
    1436              :       CASE ('C')
    1437        12804 :          db_op_a = 'T'
    1438        12804 :          sign_a = -1.0_dp
    1439              :       CASE DEFAULT
    1440        40184 :          CPABORT("Complex matrix product expects N or C for op_a")
    1441              :       END SELECT
    1442        34092 :       SELECT CASE (op_b)
    1443              :       CASE ('N')
    1444        34092 :          db_op_b = 'N'
    1445        34092 :          sign_b = 1.0_dp
    1446              :       CASE ('C')
    1447         6092 :          db_op_b = 'T'
    1448         6092 :          sign_b = -1.0_dp
    1449              :       CASE DEFAULT
    1450        40184 :          CPABORT("Complex matrix product expects N or C for op_b")
    1451              :       END SELECT
    1452              : 
    1453        40184 :       CALL dbcsr_multiply(db_op_a, db_op_b, 1.0_dp, a_re, b_re, 0.0_dp, c_re)
    1454        40184 :       CALL dbcsr_multiply(db_op_a, db_op_b, 1.0_dp, a_im, b_im, 0.0_dp, tmp)
    1455        40184 :       CALL dbcsr_add(c_re, tmp, alpha_scalar=1.0_dp, beta_scalar=-sign_a*sign_b)
    1456              : 
    1457        40184 :       CALL dbcsr_multiply(db_op_a, db_op_b, sign_b, a_re, b_im, 0.0_dp, c_im)
    1458        40184 :       CALL dbcsr_multiply(db_op_a, db_op_b, sign_a, a_im, b_re, 0.0_dp, tmp)
    1459        40184 :       CALL dbcsr_add(c_im, tmp, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    1460              : 
    1461        40184 :    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          420 :    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           84 :       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           84 :       CALL timeset(routineN, handle)
    1490              :       !
    1491              :       !
    1492           84 :       CALL dbcsr_get_info(C_NEW, nfullrows_total=n, nfullcols_total=k)
    1493              :       !
    1494              :       ! C = C*expm(-G)
    1495           84 :       GU => qs_ot_env%buf1_k_k_nosym ! a buffer
    1496           84 :       U => qs_ot_env%buf2_k_k_nosym ! a buffer
    1497           84 :       Gp1 => qs_ot_env%buf3_k_k_nosym ! a buffer
    1498           84 :       Gp2 => qs_ot_env%buf4_k_k_nosym ! a buffer
    1499           84 :       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           84 :       f2 = 0.0_dp
    1505           84 :       CALL dbcsr_copy(C, C_NEW)
    1506           84 :       CALL dbcsr_iterator_start(iter, C)
    1507          182 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1508           98 :          CALL dbcsr_iterator_next_block(iter, row, col, block, row_size=row_size, col_size=col_size)
    1509          686 :          DO p = 1, col_size ! p
    1510         6258 :          DO i = 1, row_size ! i
    1511         5656 :             tmp = SQRT(block(i, p)**2 + f2_eps)
    1512         5656 :             f2 = f2 + tmp
    1513         6160 :             block(i, p) = block(i, p)/tmp
    1514              :          END DO
    1515              :          END DO
    1516              :       END DO
    1517           84 :       CALL dbcsr_iterator_stop(iter)
    1518           84 :       CALL dbcsr_get_info(C, group=group)
    1519           84 :       CALL group%sum(f2)
    1520              :       !
    1521              :       !
    1522           84 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, C, C_NEW, 0.0_dp, GU)
    1523              :       !
    1524              :       ! antisymetrize
    1525           84 :       CALL dbcsr_get_info(GU, distribution=dist)
    1526              :       CALL dbcsr_transposed(U, GU, shallow_data_copy=.FALSE., &
    1527              :                             use_distribution=dist, &
    1528           84 :                             transpose_distribution=.FALSE.)
    1529           84 :       CALL dbcsr_add(GU, U, alpha_scalar=-0.5_dp, beta_scalar=0.5_dp)
    1530              :       !-------------------------------------------------------------------
    1531              :       !
    1532           84 :       norm_fro = dbcsr_frobenius_norm(GU)
    1533           84 :       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           84 :       CALL dbcsr_scale(GU, -alpha)
    1542              :       !
    1543              :       ! compute unitary transform
    1544              :       ! zeroth and first order
    1545           84 :       expfactor = 1.0_dp
    1546           84 :       CALL dbcsr_copy(U, GU)
    1547           84 :       CALL dbcsr_scale(U, expfactor)
    1548           84 :       CALL dbcsr_add_on_diag(U, 1.0_dp)
    1549              :       ! other orders
    1550           84 :       CALL dbcsr_copy(Gp1, GU)
    1551          520 :       DO i = 2, taylor_order
    1552              :          ! new power of G
    1553          520 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, GU, Gp1, 0.0_dp, Gp2)
    1554          520 :          CALL dbcsr_copy(Gp1, Gp2)
    1555              :          ! add to the taylor expansion so far
    1556          520 :          expfactor = expfactor/REAL(i, KIND=dp)
    1557          520 :          CALL dbcsr_add(U, Gp1, alpha_scalar=1.0_dp, beta_scalar=expfactor)
    1558          520 :          norm_fro = dbcsr_frobenius_norm(Gp1)
    1559              :          !write(*,*) 'Taylor expansion i=',i,' norm(X^i)/i!=',norm_fro*expfactor
    1560          520 :          IF (norm_fro*expfactor < 1.0E-10_dp) EXIT
    1561              :       END DO
    1562              :       !
    1563              :       ! rotate MOs
    1564           84 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, C_NEW, U, 0.0_dp, C)
    1565           84 :       CALL dbcsr_copy(C_NEW, C)
    1566              :       !
    1567              :       ! rotate SC
    1568           84 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, SC, U, 0.0_dp, C)
    1569           84 :       CALL dbcsr_copy(SC, C)
    1570              :       !
    1571              :       ! rotate D_i
    1572           84 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, D, U, 0.0_dp, C)
    1573           84 :       CALL dbcsr_copy(D, C)
    1574              :       !
    1575              :       ! rotate G_i-1
    1576           84 :       IF (ASSOCIATED(G_OLD)) THEN
    1577           84 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, G_OLD, U, 0.0_dp, C)
    1578           84 :          CALL dbcsr_copy(G_OLD, C)
    1579              :       END IF
    1580              :       !
    1581           84 :       CALL timestop(handle)
    1582           84 :    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         1660 :    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          830 :       CALL timeset(routineN, handle)
    1605              :       !
    1606          830 :       CALL dbcsr_get_info(C_NEW, nfullrows_total=n, nfullcols_total=k)
    1607              :       !
    1608              :       ! P = U'*U
    1609          830 :       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          830 :                                      transa="N", para_env=qs_ot_env%para_env, blacs_env=qs_ot_env%blacs_env)
    1614              :       !
    1615              :       ! Update SC if needed
    1616          830 :       IF (update) THEN
    1617              :          CALL cp_dbcsr_cholesky_restore(SC, k, P, C_TMP, op="SOLVE", pos="RIGHT", &
    1618          454 :                                         transa="N", para_env=qs_ot_env%para_env, blacs_env=qs_ot_env%blacs_env)
    1619          454 :          CALL dbcsr_copy(SC, C_TMP)
    1620              :       END IF
    1621              :       !
    1622          830 :       CALL timestop(handle)
    1623          830 :    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          308 :    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          308 :       CALL timeset(routineN, handle)
    1648              :       !
    1649          308 :       CALL dbcsr_get_info(C_NEW, nfullrows_total=n, nfullcols_total=k)
    1650              :       !
    1651          308 :       V => qs_ot_env%buf1_k_k_nosym ! a buffer
    1652          308 :       W => qs_ot_env%buf2_k_k_nosym ! a buffer
    1653         1232 :       ALLOCATE (eig(k), fun(k))
    1654              :       !
    1655          308 :       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         1796 :       DO i = 1, k
    1659         1488 :          IF (eig(i) <= 0.0_dp) THEN
    1660            0 :             CPABORT("P not positive definite")
    1661              :          END IF
    1662         1796 :          IF (eig(i) < 1.0E-8_dp) THEN
    1663            0 :             fun(i) = 0.0_dp
    1664              :          ELSE
    1665         1488 :             fun(i) = 1.0_dp/SQRT(eig(i))
    1666              :          END IF
    1667              :       END DO
    1668          308 :       CALL dbcsr_copy(W, V)
    1669          308 :       CALL dbcsr_scale_by_vector(V, alpha=fun, side='right')
    1670          308 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, W, V, 0.0_dp, P)
    1671              :       !
    1672              :       ! Update C
    1673          308 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, C_OLD, P, 0.0_dp, C_NEW)
    1674              :       !
    1675              :       ! Update SC if needed
    1676          308 :       IF (update) THEN
    1677          216 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, SC, P, 0.0_dp, C_TMP)
    1678          216 :          CALL dbcsr_copy(SC, C_TMP)
    1679              :       END IF
    1680              :       !
    1681          308 :       DEALLOCATE (eig, fun)
    1682              :       !
    1683          308 :       CALL timestop(handle)
    1684          308 :    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         8180 :    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         4090 :       CALL timeset(routineN, handle)
    1714              :       !
    1715         4090 :       CALL dbcsr_get_info(C_NEW, nfullrows_total=n, nfullcols_total=k)
    1716              :       !
    1717         4090 :       BUF_NOSYM => qs_ot_env%buf1_k_k_nosym ! a buffer
    1718         4090 :       BUF1 => qs_ot_env%buf1_k_k_sym ! a buffer
    1719         4090 :       BUF2 => qs_ot_env%buf2_k_k_sym ! a buffer
    1720         4090 :       FY => qs_ot_env%buf3_k_k_sym ! a buffer
    1721         4090 :       FT => qs_ot_env%buf4_k_k_sym ! a buffer
    1722              :       !
    1723              :       ! initialize the norm (already computed in qs_ot_get_orbitals_ref)
    1724         4090 :       norm = norm_in
    1725              :       !
    1726              :       ! can we do a quick exit?
    1727         4090 :       quick_exit = .FALSE.
    1728         4090 :       IF (norm < qs_ot_env%settings%eps_irac_quick_exit) quick_exit = .TRUE.
    1729              :       !
    1730              :       ! lets refine
    1731         4090 :       rescale = 1.0_dp
    1732         4524 :       DO irefine = 1, qs_ot_env%settings%max_irac
    1733              :          !
    1734              :          ! rescaling
    1735         4524 :          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         4524 :                            qs_ot_env%settings%eps_irac_filter_matrix)
    1743              :          !
    1744              :          ! collect the transformation
    1745         4524 :          IF (irefine == 1) THEN
    1746         4090 :             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         4524 :          IF (quick_exit) THEN
    1759              :             EXIT
    1760              :          END IF
    1761              :          !
    1762              :          ! P = FY^T * P * FY
    1763         1880 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, P, FY, 0.0_dp, BUF_NOSYM)
    1764         1880 :          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         1880 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, FY, BUF_NOSYM, 0.0_dp, P)
    1770         1880 :          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         1880 :          CALL dbcsr_add_on_diag(P, -1.0_dp)
    1778         1880 :          norm_fro = dbcsr_frobenius_norm(P)
    1779         1880 :          norm_gct = dbcsr_gershgorin_norm(P)
    1780         1880 :          CALL dbcsr_add_on_diag(P, 1.0_dp)
    1781         1880 :          norm = MIN(norm_gct, norm_fro)
    1782              :          !
    1783              :          ! printing
    1784              :          !
    1785              :          ! blows up
    1786         1880 :          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         1880 :          IF (norm < qs_ot_env%settings%eps_irac_quick_exit) quick_exit = .TRUE.
    1795              :          !
    1796              :          ! are we done?
    1797         4524 :          IF (norm < qs_ot_env%settings%eps_irac) EXIT
    1798              :          !
    1799              :       END DO
    1800              :       !
    1801              :       ! C_NEW = C_NEW * FT * rescale
    1802         4090 :       CALL dbcsr_multiply('N', 'N', rescale, C_OLD, FT, 0.0_dp, C_NEW)
    1803         4090 :       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         4090 :       IF (update) THEN
    1811         1670 :          CALL dbcsr_multiply('N', 'N', rescale, SC, FT, 0.0_dp, C_TMP)
    1812         1670 :          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         1670 :          CALL dbcsr_copy(SC, C_TMP)
    1818              :       END IF
    1819              :       !
    1820         4090 :       CALL timestop(handle)
    1821         4090 :    END SUBROUTINE qs_ot_ref_poly
    1822              : 
    1823              : ! **************************************************************************************************
    1824              : !> \brief ...
    1825              : !> \param qs_ot_env1 ...
    1826              : !> \return ...
    1827              : ! **************************************************************************************************
    1828         8783 :    FUNCTION qs_ot_ref_update(qs_ot_env1) RESULT(update)
    1829              :       !
    1830              :       TYPE(qs_ot_type)                                   :: qs_ot_env1
    1831              :       LOGICAL                                            :: update
    1832              : 
    1833         8783 :       update = .FALSE.
    1834         6716 :       SELECT CASE (qs_ot_env1%settings%ot_method)
    1835              :       CASE ("CG", "SD")
    1836         6716 :          SELECT CASE (qs_ot_env1%settings%line_search_method)
    1837              :          CASE ("2PNT")
    1838         6716 :             IF (qs_ot_env1%line_search_count == 2) update = .TRUE.
    1839              :          CASE DEFAULT
    1840         6716 :             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         8783 :          CPABORT("NYI")
    1849              :       END SELECT
    1850         8783 :    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         5228 :    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         5228 :       ortho_irac = qs_ot_env1%settings%ortho_irac
    1865         5228 :       IF (norm_in < qs_ot_env1%settings%eps_irac_switch) ortho_irac = "POLY"
    1866         5228 :    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        10456 :    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         5228 :       CALL timeset(routineN, handle)
    1895              : 
    1896         5228 :       CALL dbcsr_get_info(matrix_c, nfullrows_total=n, nfullcols_total=k)
    1897              :       !
    1898         5228 :       C_NEW => matrix_c
    1899         5228 :       C_OLD => matrix_x ! need to be carefully updated for the gradient !
    1900         5228 :       SC => matrix_sx ! need to be carefully updated for the gradient !
    1901         5228 :       G_OLD => matrix_gx_old ! need to be carefully updated for localization !
    1902         5228 :       D => matrix_dx ! need to be carefully updated for localization !
    1903         5228 :       S => matrix_s
    1904              : 
    1905         5228 :       P => qs_ot_env%p_k_k_sym ! a buffer
    1906         5228 :       C_TMP => qs_ot_env%buf1_n_k ! a buffer
    1907              :       !
    1908              :       ! do we need to update C_OLD and SC?
    1909         5228 :       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         5228 :       on_the_fly_loc = qs_ot_env1%settings%on_the_fly_loc
    1916              :       !
    1917              :       ! compute SC = S*C
    1918         5228 :       IF (ASSOCIATED(S)) THEN
    1919         5228 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, S, C_OLD, 0.0_dp, SC)
    1920         5228 :          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         5228 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, C_OLD, SC, 0.0_dp, P)
    1931         5228 :       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         5228 :       CALL dbcsr_add_on_diag(P, -1.0_dp)
    1939         5228 :       norm_fro = dbcsr_frobenius_norm(P)
    1940         5228 :       norm_gct = dbcsr_gershgorin_norm(P)
    1941         5228 :       CALL dbcsr_add_on_diag(P, 1.0_dp)
    1942         5228 :       norm = MIN(norm_gct, norm_fro)
    1943         5228 :       CALL qs_ot_ref_decide(qs_ot_env1, norm, ortho_irac)
    1944              :       !
    1945              :       ! select the orthogonality method
    1946          830 :       SELECT CASE (ortho_irac)
    1947              :       CASE ("CHOL")
    1948          830 :          CALL qs_ot_ref_chol(qs_ot_env, C_OLD, C_TMP, C_NEW, P, SC, update)
    1949              :       CASE ("LWDN")
    1950          308 :          CALL qs_ot_ref_lwdn(qs_ot_env, C_OLD, C_TMP, C_NEW, P, SC, update)
    1951              :       CASE ("POLY")
    1952         4090 :          CALL qs_ot_ref_poly(qs_ot_env, C_OLD, C_TMP, C_NEW, P, SC, norm, update)
    1953              :       CASE DEFAULT
    1954         5228 :          CPABORT("Wrong argument")
    1955              :       END SELECT
    1956              :       !
    1957              :       ! We update the C_i+1 and localization
    1958         5228 :       IF (update) THEN
    1959         2340 :          IF (on_the_fly_loc) THEN
    1960           84 :             CALL qs_ot_on_the_fly_localize(qs_ot_env, C_NEW, SC, G_OLD, D)
    1961              :          END IF
    1962         2340 :          CALL dbcsr_copy(C_OLD, C_NEW)
    1963              :       END IF
    1964              : 
    1965         5228 :       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         5228 :       CALL timestop(handle)
    1973         5228 :    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         3555 :    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         3555 :       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         3555 :       CALL timeset(routineN, handle)
    2003              : 
    2004         3555 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    2005         3555 :       CPASSERT(ASSOCIATED(matrix_s))
    2006         3555 :       CPASSERT(ASSOCIATED(matrix_s_im))
    2007              : 
    2008         3555 :       c_re => qs_ot_env%matrix_x
    2009         3555 :       c_im => qs_ot_env%matrix_x_im
    2010         3555 :       f_re => qs_ot_env%matrix_ref_inv_sqrt
    2011         3555 :       f_im => qs_ot_env%matrix_ref_inv_sqrt_im
    2012         3555 :       sc_re => qs_ot_env%matrix_sx
    2013         3555 :       sc_im => qs_ot_env%matrix_sx_im
    2014         3555 :       p_re => qs_ot_env%buf1_k_k_sym
    2015         3555 :       p_im => qs_ot_env%buf2_k_k_sym
    2016         3555 :       v_re => qs_ot_env%buf3_k_k_sym
    2017         3555 :       v_im => qs_ot_env%buf4_k_k_sym
    2018         3555 :       w_re => qs_ot_env%buf1_k_k_nosym
    2019         3555 :       w_im => qs_ot_env%buf2_k_k_nosym
    2020         3555 :       tmp_kk => qs_ot_env%buf3_k_k_nosym
    2021         3555 :       tmp_nk => qs_ot_env%buf1_n_k
    2022              : 
    2023         3555 :       CALL dbcsr_get_info(c_re, nfullrows_total=n, nfullcols_total=k)
    2024         3555 :       IF (PRESENT(qs_ot_env1)) THEN
    2025         3379 :          update = qs_ot_ref_update(qs_ot_env1)
    2026              :       ELSE
    2027          176 :          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         3555 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s, c_re, 0.0_dp, sc_re)
    2032         3555 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s_im, c_im, 0.0_dp, tmp_nk)
    2033         3555 :       CALL dbcsr_add(sc_re, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2034              : 
    2035         3555 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s, c_im, 0.0_dp, sc_im)
    2036         3555 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s_im, c_re, 0.0_dp, tmp_nk)
    2037         3555 :       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         3555 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, c_re, sc_re, 0.0_dp, p_re)
    2041         3555 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, c_im, sc_im, 0.0_dp, tmp_kk)
    2042         3555 :       CALL dbcsr_add(p_re, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2043              : 
    2044         3555 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, c_re, sc_im, 0.0_dp, p_im)
    2045         3555 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, c_im, sc_re, 0.0_dp, tmp_kk)
    2046         3555 :       CALL dbcsr_add(p_im, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2047              : 
    2048        14220 :       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         3555 :                           blacs_env=qs_ot_env%blacs_env)
    2053        29733 :       DO i = 1, k
    2054        26178 :          IF (eigenvalues(i) <= EPSILON(1.0_dp)) THEN
    2055            0 :             CPABORT("Complex REF overlap is not positive definite")
    2056              :          END IF
    2057        29733 :          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         3555 :       CALL dbcsr_copy(w_re, v_re)
    2062         3555 :       CALL dbcsr_copy(w_im, v_im)
    2063         3555 :       CALL dbcsr_scale_by_vector(w_re, alpha=inverse_sqrt, side='right')
    2064         3555 :       CALL dbcsr_scale_by_vector(w_im, alpha=inverse_sqrt, side='right')
    2065              : 
    2066         3555 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, w_re, v_re, 0.0_dp, p_re)
    2067         3555 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, w_im, v_im, 0.0_dp, tmp_kk)
    2068         3555 :       CALL dbcsr_add(p_re, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2069              : 
    2070         3555 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, w_im, v_re, 0.0_dp, p_im)
    2071         3555 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, w_re, v_im, 0.0_dp, tmp_kk)
    2072         3555 :       CALL dbcsr_add(p_im, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2073              : 
    2074         3555 :       CALL dbcsr_copy(f_re, p_re)
    2075         3555 :       CALL dbcsr_copy(f_im, p_im)
    2076              : 
    2077              :       ! Return the physical, orthonormal orbitals without changing a rejected
    2078              :       ! line-search coordinate.
    2079         3555 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, c_re, p_re, 0.0_dp, matrix_c)
    2080         3555 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, c_im, p_im, 0.0_dp, tmp_nk)
    2081         3555 :       CALL dbcsr_add(matrix_c, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2082              : 
    2083         3555 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, c_re, p_im, 0.0_dp, matrix_c_im)
    2084         3555 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, c_im, p_re, 0.0_dp, tmp_nk)
    2085         3555 :       CALL dbcsr_add(matrix_c_im, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2086              : 
    2087         3555 :       IF (update) THEN
    2088         1485 :          CALL dbcsr_copy(c_re, matrix_c)
    2089         1485 :          CALL dbcsr_copy(c_im, matrix_c_im)
    2090              : 
    2091         1485 :          CALL dbcsr_set(f_re, 0.0_dp)
    2092         1485 :          CALL dbcsr_add_on_diag(f_re, alpha=1.0_dp)
    2093         1485 :          CALL dbcsr_set(f_im, 0.0_dp)
    2094              : 
    2095         1485 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s, c_re, 0.0_dp, sc_re)
    2096         1485 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s_im, c_im, 0.0_dp, tmp_nk)
    2097         1485 :          CALL dbcsr_add(sc_re, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2098              : 
    2099         1485 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s, c_im, 0.0_dp, sc_im)
    2100         1485 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_s_im, c_re, 0.0_dp, tmp_nk)
    2101         1485 :          CALL dbcsr_add(sc_im, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2102              :       END IF
    2103              : 
    2104         3555 :       IF (qs_ot_env%settings%do_rotation) THEN
    2105         2150 :          CALL qs_ot_generate_rotation_complex(qs_ot_env)
    2106              : 
    2107         2150 :          CALL dbcsr_copy(rotated_re, matrix_c, name="rotated_re")
    2108         2150 :          CALL dbcsr_copy(rotated_im, matrix_c_im, name="rotated_im")
    2109         2150 :          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         2150 :                              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         2150 :                              0.0_dp, rotation_tmp)
    2116         2150 :          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         2150 :                              0.0_dp, rotated_im)
    2120              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_c_im, qs_ot_env%rot_mat_u, &
    2121         2150 :                              0.0_dp, rotation_tmp)
    2122         2150 :          CALL dbcsr_add(rotated_im, rotation_tmp, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2123              : 
    2124         2150 :          CALL dbcsr_copy(matrix_c, rotated_re)
    2125         2150 :          CALL dbcsr_copy(matrix_c_im, rotated_im)
    2126         2150 :          CALL dbcsr_release(rotated_re)
    2127         2150 :          CALL dbcsr_release(rotated_im)
    2128         2150 :          CALL dbcsr_release(rotation_tmp)
    2129              :       END IF
    2130              : 
    2131         3555 :       DEALLOCATE (eigenvalues, inverse_sqrt)
    2132              : 
    2133         3555 :       CALL timestop(handle)
    2134         7110 :    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         9048 :    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         4524 :       CALL timeset(routineN, handle)
    2156              : 
    2157         4524 :       CALL dbcsr_get_info(P, nfullcols_total=k)
    2158         4524 :       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         4524 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, P, P, 0.0_dp, P2) ! P^2
    2197         4524 :          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         4524 :          r = -180.0_dp/128.0_dp
    2203         4524 :          CALL dbcsr_add(T, P, alpha_scalar=0.0_dp, beta_scalar=r) ! T=-180/128*P
    2204         4524 :          r = 35.0_dp/128.0_dp
    2205         4524 :          CALL dbcsr_add(T, P2, alpha_scalar=1.0_dp, beta_scalar=r) ! T=T+35/128*P^2
    2206         4524 :          CALL dbcsr_multiply('N', 'N', 1.0_dp, T, P2, 0.0_dp, FY) ! Y=T*P^2
    2207         4524 :          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         4524 :          r = 378.0_dp/128.0_dp
    2213         4524 :          CALL dbcsr_add(FY, P2, alpha_scalar=1.0_dp, beta_scalar=r) ! Y=Y+378/128*P^2
    2214         4524 :          r = -420.0_dp/128.0_dp
    2215         4524 :          CALL dbcsr_add(FY, P, alpha_scalar=1.0_dp, beta_scalar=r) ! Y=Y-420/128*P
    2216         4524 :          r = 315.0_dp/128.0_dp
    2217         4524 :          CALL dbcsr_add_on_diag(FY, alpha=r) ! Y=Y+315/128*I
    2218              :       CASE DEFAULT
    2219         4524 :          CPABORT("This irac_order NYI")
    2220              :       END SELECT
    2221         4524 :       CALL timestop(handle)
    2222         4524 :    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         6640 :    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         3320 :       CALL timeset(routineN, handle)
    2244              : 
    2245         3320 :       CALL dbcsr_get_info(matrix_x, nfullrows_total=n, nfullcols_total=k)
    2246              :       !
    2247         3320 :       C => matrix_x ! NBsf*NOcc
    2248         3320 :       SC => matrix_sx ! NBsf*NOcc need to be up2date
    2249         3320 :       HC => matrix_hc ! NBsf*NOcc
    2250         3320 :       G => matrix_gx ! NBsf*NOcc
    2251         3320 :       CHC => qs_ot_env%buf1_k_k_sym ! buffer
    2252              : 
    2253         3320 :       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         3320 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, C, HC_work, 0.0_dp, CHC)
    2266         3320 :       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         3320 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, SC, CHC, 0.0_dp, G)
    2273         3320 :       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         3320 :       CALL dbcsr_add(G, HC_work, alpha_scalar=-1.0_dp, beta_scalar=1.0_dp)
    2280              :       !
    2281         3320 :       CALL timestop(handle)
    2282         3320 :    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         4728 :    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         2364 :       CALL timeset(routineN, handle)
    2308              : 
    2309         2364 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    2310         2364 :       CPASSERT(ASSOCIATED(matrix_hc))
    2311         2364 :       CPASSERT(ASSOCIATED(matrix_hc_im))
    2312              : 
    2313         2364 :       f_re => qs_ot_env%matrix_ref_inv_sqrt
    2314         2364 :       f_im => qs_ot_env%matrix_ref_inv_sqrt_im
    2315         2364 :       sc_re => qs_ot_env%matrix_sx
    2316         2364 :       sc_im => qs_ot_env%matrix_sx_im
    2317         2364 :       hc_re => matrix_hc
    2318         2364 :       hc_im => matrix_hc_im
    2319         2364 :       hc_rotation_re => hc_re
    2320         2364 :       hc_rotation_im => hc_im
    2321         2364 :       IF (PRESENT(matrix_hc_rotation) .OR. PRESENT(matrix_hc_rotation_im)) THEN
    2322          426 :          CPASSERT(PRESENT(matrix_hc_rotation) .AND. PRESENT(matrix_hc_rotation_im))
    2323          426 :          CPASSERT(ASSOCIATED(matrix_hc_rotation))
    2324          426 :          CPASSERT(ASSOCIATED(matrix_hc_rotation_im))
    2325          426 :          hc_rotation_re => matrix_hc_rotation
    2326          426 :          hc_rotation_im => matrix_hc_rotation_im
    2327              :       END IF
    2328         2364 :       hc_work_re => hc_re
    2329         2364 :       hc_work_im => hc_im
    2330         2364 :       g_re => qs_ot_env%matrix_gx
    2331         2364 :       g_im => qs_ot_env%matrix_gx_im
    2332         2364 :       b_re => qs_ot_env%buf1_k_k_sym
    2333         2364 :       b_im => qs_ot_env%buf2_k_k_sym
    2334         2364 :       tmp_kk => qs_ot_env%buf3_k_k_sym
    2335         2364 :       q_re => qs_ot_env%buf1_n_k
    2336         2364 :       q_im => qs_ot_env%buf1_n_k_dp
    2337              : 
    2338         2364 :       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         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%matrix_x, f_re, 0.0_dp, q_re)
    2342         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%matrix_x_im, f_im, 0.0_dp, g_re)
    2343         2364 :       CALL dbcsr_add(q_re, g_re, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2344              : 
    2345         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%matrix_x, f_im, 0.0_dp, q_im)
    2346         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, qs_ot_env%matrix_x_im, f_re, 0.0_dp, g_re)
    2347         2364 :       CALL dbcsr_add(q_im, g_re, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2348              : 
    2349         2364 :       IF (qs_ot_env%settings%do_rotation) THEN
    2350         1336 :          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         1336 :                              0.0_dp, qs_ot_env%rot_mat_dedu)
    2355         1336 :          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         1336 :                         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         1336 :                              0.0_dp, qs_ot_env%rot_mat_dedu_im)
    2361         1336 :          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         1336 :                         alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2364         1336 :          CALL qs_ot_rot_mat_derivative_complex(qs_ot_env)
    2365              : 
    2366              :          ! The REF coordinate sees G_Q=G_C*U^H.
    2367         1336 :          CALL dbcsr_copy(hc_rot_re, hc_re, name="hc_rot_re")
    2368         1336 :          CALL dbcsr_copy(hc_rot_im, hc_im, name="hc_rot_im")
    2369         1336 :          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         1336 :                              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         1336 :                              0.0_dp, tmp_nk)
    2374         1336 :          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         1336 :                              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         1336 :                              0.0_dp, tmp_nk)
    2380         1336 :          CALL dbcsr_add(hc_rot_im, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2381         1336 :          hc_work_re => hc_rot_re
    2382         1336 :          hc_work_im => hc_rot_im
    2383              :       END IF
    2384              : 
    2385              :       ! B = Q^H*G_Q.  For uniform fixed occupations B is Hermitian.
    2386         2364 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, q_re, hc_work_re, 0.0_dp, b_re)
    2387         2364 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, q_im, hc_work_im, 0.0_dp, tmp_kk)
    2388         2364 :       CALL dbcsr_add(b_re, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2389              : 
    2390         2364 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, q_re, hc_work_im, 0.0_dp, b_im)
    2391         2364 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, q_im, hc_work_re, 0.0_dp, tmp_kk)
    2392         2364 :       CALL dbcsr_add(b_im, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2393              : 
    2394         2364 :       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         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sc_re, f_re, 0.0_dp, g_re)
    2405         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sc_im, f_im, 0.0_dp, q_re)
    2406         2364 :       CALL dbcsr_add(g_re, q_re, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2407              : 
    2408         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sc_re, f_im, 0.0_dp, g_im)
    2409         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sc_im, f_re, 0.0_dp, q_re)
    2410         2364 :       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         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, g_re, b_re, 0.0_dp, q_re)
    2414         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, g_im, b_im, 0.0_dp, q_im)
    2415         2364 :       CALL dbcsr_add(q_re, q_im, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2416              : 
    2417         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, g_re, b_im, 0.0_dp, q_im)
    2418         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, g_im, b_re, 0.0_dp, g_re)
    2419         2364 :       CALL dbcsr_add(q_im, g_re, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2420              : 
    2421         2364 :       CALL dbcsr_add(q_re, hc_work_re, alpha_scalar=-1.0_dp, beta_scalar=1.0_dp)
    2422         2364 :       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         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, q_re, f_re, 0.0_dp, g_re)
    2426         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, q_im, f_im, 0.0_dp, g_im)
    2427         2364 :       CALL dbcsr_add(g_re, g_im, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2428              : 
    2429         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, q_re, f_im, 0.0_dp, g_im)
    2430         2364 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, q_im, f_re, 0.0_dp, q_re)
    2431         2364 :       CALL dbcsr_add(g_im, q_re, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2432              : 
    2433         2364 :       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         1336 :                                                       sc_re, sc_im, g_re, g_im, qs_ot_env)
    2436              :       END IF
    2437              : 
    2438         2364 :       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         2364 :       IF (qs_ot_env%settings%do_rotation) THEN
    2448         1336 :          CALL dbcsr_release(hc_rot_re)
    2449         1336 :          CALL dbcsr_release(hc_rot_im)
    2450         1336 :          CALL dbcsr_release(tmp_nk)
    2451              :       END IF
    2452              : 
    2453         2364 :       CALL timestop(handle)
    2454              : 
    2455         2364 :    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         9634 :    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         9634 :       CALL dbcsr_copy(transposed, matrix, name='square_transposed')
    2469         9634 :       CALL dbcsr_copy(identity, identity_template, name='transpose_identity')
    2470         9634 :       CALL dbcsr_set(identity, 0.0_dp)
    2471         9634 :       CALL dbcsr_add_on_diag(identity, 1.0_dp)
    2472         9634 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, matrix, identity, 0.0_dp, transposed)
    2473         9634 :       CALL dbcsr_release(identity)
    2474              : 
    2475         9634 :    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         1336 :    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         1336 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_offset, row_blk_offset
    2500              :       LOGICAL                                            :: found_im, found_re
    2501              :       REAL(KIND=dp)                                      :: denominator
    2502         1336 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: f_evals
    2503         1336 :       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         1336 :       CALL timeset(routineN, handle)
    2509              : 
    2510         1336 :       CALL dbcsr_get_info(a_re, nfullrows_total=k)
    2511         1336 :       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         1336 :       CALL qs_ot_square_transpose(a_re, tmp_kk, qs_ot_env%rot_mat_u)
    2519         1336 :       CALL dbcsr_copy(anti_re, a_re, name='anti_re')
    2520         1336 :       CALL dbcsr_add(anti_re, tmp_kk, alpha_scalar=0.5_dp, beta_scalar=-0.5_dp)
    2521         1336 :       CALL qs_ot_square_transpose(a_im, tmp_kk, qs_ot_env%rot_mat_u)
    2522         1336 :       CALL dbcsr_copy(anti_im, a_im, name='anti_im')
    2523         1336 :       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         1336 :       CALL dbcsr_copy(f_work_re, f_re, name='f_work_re')
    2528         1336 :       CALL dbcsr_copy(f_work_im, f_im, name='f_work_im')
    2529         1336 :       CALL dbcsr_copy(v_re, f_re, name='v_re')
    2530         1336 :       CALL dbcsr_copy(v_im, f_im, name='v_im')
    2531         4008 :       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         1336 :                           para_env=qs_ot_env%para_env, blacs_env=qs_ot_env%blacs_env)
    2535         9616 :       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         1336 :       CALL dbcsr_copy(work_re, anti_re, name='work_re')
    2541         1336 :       CALL dbcsr_copy(work_im, anti_im, name='work_im')
    2542         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, anti_re, v_re, 0.0_dp, work_re)
    2543         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, anti_im, v_im, 0.0_dp, tmp_kk)
    2544         1336 :       CALL dbcsr_add(work_re, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2545         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, anti_re, v_im, 0.0_dp, work_im)
    2546         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, anti_im, v_re, 0.0_dp, tmp_kk)
    2547         1336 :       CALL dbcsr_add(work_im, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2548              : 
    2549         1336 :       CALL dbcsr_copy(inner_re, anti_re, name='inner_re')
    2550         1336 :       CALL dbcsr_copy(inner_im, anti_im, name='inner_im')
    2551         1336 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, v_re, work_re, 0.0_dp, inner_re)
    2552         1336 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, v_im, work_im, 1.0_dp, inner_re)
    2553         1336 :       CALL dbcsr_multiply('T', 'N', 1.0_dp, v_re, work_im, 0.0_dp, inner_im)
    2554         1336 :       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         1336 :                           col_blk_offset=col_blk_offset)
    2558         1336 :       CALL dbcsr_iterator_start(iter, inner_re)
    2559         2149 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    2560          813 :          CALL dbcsr_iterator_next_block(iter, row, col)
    2561          813 :          CALL dbcsr_get_block_p(inner_re, row, col, block_re, found_re)
    2562          813 :          CALL dbcsr_get_block_p(inner_im, row, col, block_im, found_im)
    2563          813 :          CPASSERT(found_re .AND. found_im)
    2564         7855 :          DO i = 1, SIZE(block_re, 1)
    2565        69391 :          DO j = 1, SIZE(block_re, 2)
    2566              :             denominator = 1.0_dp/f_evals(row_blk_offset(row) + i - 1) + &
    2567        62872 :                           1.0_dp/f_evals(col_blk_offset(col) + j - 1)
    2568        62872 :             block_re(i, j) = block_re(i, j)/denominator
    2569        68578 :             block_im(i, j) = block_im(i, j)/denominator
    2570              :          END DO
    2571              :          END DO
    2572              :       END DO
    2573         1336 :       CALL dbcsr_iterator_stop(iter)
    2574              : 
    2575              :       ! Z = V inner V^H.
    2576         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, v_re, inner_re, 0.0_dp, work_re)
    2577         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, v_im, inner_im, 0.0_dp, tmp_kk)
    2578         1336 :       CALL dbcsr_add(work_re, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2579         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, v_re, inner_im, 0.0_dp, work_im)
    2580         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, v_im, inner_re, 0.0_dp, tmp_kk)
    2581         1336 :       CALL dbcsr_add(work_im, tmp_kk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2582              : 
    2583         1336 :       CALL dbcsr_copy(z_re, anti_re, name='z_re')
    2584         1336 :       CALL dbcsr_copy(z_im, anti_im, name='z_im')
    2585         1336 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, work_re, v_re, 0.0_dp, z_re)
    2586         1336 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, work_im, v_im, 1.0_dp, z_re)
    2587         1336 :       CALL dbcsr_multiply('N', 'T', 1.0_dp, work_im, v_re, 0.0_dp, z_im)
    2588         1336 :       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         1336 :       CALL dbcsr_copy(sq_re, sx_re, name='sq_re')
    2592         1336 :       CALL dbcsr_copy(sq_im, sx_im, name='sq_im')
    2593         1336 :       CALL dbcsr_copy(tmp_nk, sx_re, name='tmp_nk')
    2594         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sx_re, f_re, 0.0_dp, sq_re)
    2595         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sx_im, f_im, 0.0_dp, tmp_nk)
    2596         1336 :       CALL dbcsr_add(sq_re, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2597         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sx_re, f_im, 0.0_dp, sq_im)
    2598         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sx_im, f_re, 0.0_dp, tmp_nk)
    2599         1336 :       CALL dbcsr_add(sq_im, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2600              : 
    2601         1336 :       CALL dbcsr_copy(vertical_re, sx_re, name='vertical_re')
    2602         1336 :       CALL dbcsr_copy(vertical_im, sx_im, name='vertical_im')
    2603         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sq_re, z_re, 0.0_dp, vertical_re)
    2604         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sq_im, z_im, 0.0_dp, tmp_nk)
    2605         1336 :       CALL dbcsr_add(vertical_re, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2606         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sq_re, z_im, 0.0_dp, vertical_im)
    2607         1336 :       CALL dbcsr_multiply('N', 'N', 1.0_dp, sq_im, z_re, 0.0_dp, tmp_nk)
    2608         1336 :       CALL dbcsr_add(vertical_im, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2609         1336 :       CALL dbcsr_add(gradient_re, vertical_re, alpha_scalar=1.0_dp, beta_scalar=2.0_dp)
    2610         1336 :       CALL dbcsr_add(gradient_im, vertical_im, alpha_scalar=1.0_dp, beta_scalar=2.0_dp)
    2611              : 
    2612         1336 :       DEALLOCATE (f_evals)
    2613         1336 :       CALL dbcsr_release(anti_im)
    2614         1336 :       CALL dbcsr_release(anti_re)
    2615         1336 :       CALL dbcsr_release(f_work_im)
    2616         1336 :       CALL dbcsr_release(f_work_re)
    2617         1336 :       CALL dbcsr_release(inner_im)
    2618         1336 :       CALL dbcsr_release(inner_re)
    2619         1336 :       CALL dbcsr_release(sq_im)
    2620         1336 :       CALL dbcsr_release(sq_re)
    2621         1336 :       CALL dbcsr_release(tmp_kk)
    2622         1336 :       CALL dbcsr_release(tmp_nk)
    2623         1336 :       CALL dbcsr_release(v_im)
    2624         1336 :       CALL dbcsr_release(v_re)
    2625         1336 :       CALL dbcsr_release(vertical_im)
    2626         1336 :       CALL dbcsr_release(vertical_re)
    2627         1336 :       CALL dbcsr_release(work_im)
    2628         1336 :       CALL dbcsr_release(work_re)
    2629         1336 :       CALL dbcsr_release(z_im)
    2630         1336 :       CALL dbcsr_release(z_re)
    2631              : 
    2632         1336 :       CALL timestop(handle)
    2633              : 
    2634         2672 :    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       329283 :    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       109761 :       CALL timeset(routineN, handle)
    2655              : 
    2656       109761 :       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       109761 :                           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       109761 :       max_iter = 30; threshold = 1.0E-03_dp
    2665       109761 :       CALL arnoldi_extremal(qs_ot_env%matrix_p, max_ev, min_ev, converged, threshold, max_iter)
    2666       109761 :       qs_ot_env%largest_eval_upper_bound = MAX(max_ev, ABS(min_ev))
    2667              : 
    2668       109761 :       IF (.NOT. converged) qs_ot_env%largest_eval_upper_bound = dbcsr_gershgorin_norm(qs_ot_env%matrix_p)
    2669       109761 :       CALL decide_strategy(qs_ot_env)
    2670       109761 :       IF (qs_ot_env%do_taylor) THEN
    2671        58064 :          CALL qs_ot_p2m_taylor(qs_ot_env)
    2672              :       ELSE
    2673        51697 :          CALL qs_ot_p2m_diag(qs_ot_env)
    2674              :       END IF
    2675              : 
    2676       109761 :       IF (qs_ot_env%settings%do_rotation) THEN
    2677         3922 :          CALL qs_ot_generate_rotation(qs_ot_env)
    2678              :       END IF
    2679              : 
    2680       109761 :       CALL timestop(handle)
    2681              : 
    2682       109761 :    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         5670 :    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         5670 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: exp_evals_im, exp_evals_re
    2698              :       TYPE(dbcsr_type)                                   :: h_re, tmp, w_im, w_re
    2699              : 
    2700         5670 :       CALL timeset(routineN, handle)
    2701              : 
    2702         5670 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    2703         5670 :       CPASSERT(ASSOCIATED(qs_ot_env%rot_mat_x_im))
    2704         5670 :       CPASSERT(ASSOCIATED(qs_ot_env%rot_mat_u_im))
    2705              : 
    2706         5670 :       CALL dbcsr_get_info(qs_ot_env%rot_mat_x, nfullrows_total=k)
    2707         5670 :       IF (k /= 0) THEN
    2708              :          rot_norm = dbcsr_frobenius_norm(qs_ot_env%rot_mat_x) + &
    2709         5670 :                     dbcsr_frobenius_norm(qs_ot_env%rot_mat_x_im)
    2710         5670 :          IF (rot_norm <= EPSILON(1.0_dp)) THEN
    2711         1020 :             CALL dbcsr_set(qs_ot_env%rot_mat_u, 0.0_dp)
    2712         1020 :             CALL dbcsr_add_on_diag(qs_ot_env%rot_mat_u, 1.0_dp)
    2713         1020 :             CALL dbcsr_set(qs_ot_env%rot_mat_u_im, 0.0_dp)
    2714         1020 :             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         4650 :          CALL dbcsr_copy(h_re, qs_ot_env%rot_mat_x_im, name="h_re")
    2721         4650 :          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         4650 :                              para_env=qs_ot_env%para_env, blacs_env=qs_ot_env%blacs_env)
    2727              : 
    2728        18600 :          ALLOCATE (exp_evals_re(k), exp_evals_im(k))
    2729        40742 :          exp_evals_re(:) = COS(-qs_ot_env%rot_mat_evals(:))
    2730        40742 :          exp_evals_im(:) = SIN(-qs_ot_env%rot_mat_evals(:))
    2731              : 
    2732         4650 :          CALL dbcsr_copy(w_re, qs_ot_env%rot_mat_evec_re, name="w_re")
    2733         4650 :          CALL dbcsr_scale_by_vector(w_re, alpha=exp_evals_re, side='right')
    2734         4650 :          CALL dbcsr_copy(tmp, qs_ot_env%rot_mat_evec_im, name="tmp")
    2735         4650 :          CALL dbcsr_scale_by_vector(tmp, alpha=exp_evals_im, side='right')
    2736         4650 :          CALL dbcsr_add(w_re, tmp, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2737              : 
    2738         4650 :          CALL dbcsr_copy(w_im, qs_ot_env%rot_mat_evec_im, name="w_im")
    2739         4650 :          CALL dbcsr_scale_by_vector(w_im, alpha=exp_evals_re, side='right')
    2740         4650 :          CALL dbcsr_copy(tmp, qs_ot_env%rot_mat_evec_re)
    2741         4650 :          CALL dbcsr_scale_by_vector(tmp, alpha=exp_evals_im, side='right')
    2742         4650 :          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         4650 :                              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         4650 :                              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         4650 :                              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         4650 :                              1.0_dp, qs_ot_env%rot_mat_u_im)
    2752              : 
    2753         4650 :          CALL dbcsr_release(h_re)
    2754         4650 :          CALL dbcsr_release(tmp)
    2755         4650 :          CALL dbcsr_release(w_re)
    2756         4650 :          CALL dbcsr_release(w_im)
    2757         4650 :          DEALLOCATE (exp_evals_re, exp_evals_im)
    2758              :       END IF
    2759              : 
    2760         4650 :       CALL timestop(handle)
    2761              : 
    2762        10320 :    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         5104 :    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         2552 :       CALL timeset(routineN, handle)
    2781              : 
    2782         2552 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    2783         2552 :       CPASSERT(ASSOCIATED(qs_ot_env%rot_mat_dedu_im))
    2784         2552 :       CPASSERT(ASSOCIATED(qs_ot_env%rot_mat_gx_im))
    2785              : 
    2786         2552 :       CALL dbcsr_get_info(qs_ot_env%rot_mat_u, nfullrows_total=k)
    2787         2552 :       IF (k /= 0) THEN
    2788              :          rot_norm = dbcsr_frobenius_norm(qs_ot_env%rot_mat_x) + &
    2789         2552 :                     dbcsr_frobenius_norm(qs_ot_env%rot_mat_x_im)
    2790         2552 :          IF (rot_norm <= EPSILON(1.0_dp)) THEN
    2791          515 :             CALL qs_ot_square_transpose(qs_ot_env%rot_mat_dedu, tmp, qs_ot_env%rot_mat_u)
    2792          515 :             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          515 :                            alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2795          515 :             CALL dbcsr_release(tmp)
    2796              : 
    2797          515 :             CALL qs_ot_square_transpose(qs_ot_env%rot_mat_dedu_im, tmp, qs_ot_env%rot_mat_u)
    2798          515 :             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          515 :                            alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2801          515 :             CALL dbcsr_release(tmp)
    2802          515 :             CALL timestop(handle)
    2803          515 :             RETURN
    2804              :          END IF
    2805              : 
    2806         2037 :          CALL dbcsr_copy(work_re, qs_ot_env%rot_mat_dedu, name="work_re")
    2807         2037 :          CALL dbcsr_copy(work_im, qs_ot_env%rot_mat_dedu, name="work_im")
    2808         2037 :          CALL dbcsr_copy(tmp, qs_ot_env%rot_mat_dedu, name="tmp")
    2809         2037 :          CALL dbcsr_copy(inner_deriv_re, qs_ot_env%rot_mat_dedu, name="inner_deriv_re")
    2810         2037 :          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         2037 :                              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         2037 :                              qs_ot_env%rot_mat_evec_im, 0.0_dp, tmp)
    2817         2037 :          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         2037 :                              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         2037 :                              qs_ot_env%rot_mat_evec_re, 0.0_dp, tmp)
    2822         2037 :          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         2037 :                              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         2037 :                              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         2037 :                              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         2037 :                              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         2037 :                                                 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         2037 :                              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         2037 :                              outer_deriv_im, 0.0_dp, tmp)
    2840         2037 :          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         2037 :                              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         2037 :                              outer_deriv_re, 0.0_dp, tmp)
    2845         2037 :          CALL dbcsr_add(work_im, tmp, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2846              : 
    2847         2037 :          CALL dbcsr_copy(frechet_re, qs_ot_env%rot_mat_dedu, name="frechet_re")
    2848         2037 :          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         2037 :                              0.0_dp, frechet_re)
    2851              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, work_im, qs_ot_env%rot_mat_evec_im, &
    2852         2037 :                              1.0_dp, frechet_re)
    2853              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, work_im, qs_ot_env%rot_mat_evec_re, &
    2854         2037 :                              0.0_dp, frechet_im)
    2855              :          CALL dbcsr_multiply('N', 'T', -1.0_dp, work_re, qs_ot_env%rot_mat_evec_im, &
    2856         2037 :                              1.0_dp, frechet_im)
    2857              : 
    2858              :          ! Tangents satisfy X^T=-X and Y^T=Y for A=X+iY.
    2859         2037 :          CALL qs_ot_square_transpose(frechet_re, tmp, qs_ot_env%rot_mat_u)
    2860         2037 :          CALL dbcsr_copy(qs_ot_env%rot_mat_gx, frechet_re)
    2861              :          CALL dbcsr_add(qs_ot_env%rot_mat_gx, tmp, &
    2862         2037 :                         alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    2863         2037 :          CALL qs_ot_square_transpose(frechet_im, tmp, qs_ot_env%rot_mat_u)
    2864         2037 :          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         2037 :                         alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    2867              : 
    2868         2037 :          CALL dbcsr_release(frechet_re)
    2869         2037 :          CALL dbcsr_release(frechet_im)
    2870         2037 :          CALL dbcsr_release(inner_deriv_re)
    2871         2037 :          CALL dbcsr_release(inner_deriv_im)
    2872         2037 :          CALL dbcsr_release(outer_deriv_re)
    2873         2037 :          CALL dbcsr_release(outer_deriv_im)
    2874         2037 :          CALL dbcsr_release(tmp)
    2875         2037 :          CALL dbcsr_release(work_re)
    2876         2037 :          CALL dbcsr_release(work_im)
    2877              :       END IF
    2878              : 
    2879         2037 :       CALL timestop(handle)
    2880              : 
    2881         2552 :    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         2008 :    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         2008 :       CALL timeset(routineN, handle)
    2901              : 
    2902         2008 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    2903         2008 :       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         2008 :                                   qs_ot_env%matrix_p, qs_ot_env%matrix_p_im, qs_ot_env%matrix_buf1)
    2907         2008 :       qs_ot_env%do_taylor = .FALSE.
    2908         2008 :       CALL qs_ot_p2m_diag_complex(qs_ot_env)
    2909              : 
    2910         2008 :       CALL timestop(handle)
    2911              : 
    2912         2008 :    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         3938 :    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         3938 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: exp_evals_im, exp_evals_re
    2930              :       TYPE(dbcsr_type)                                   :: buf_1, buf_2
    2931              : 
    2932         3938 :       CALL timeset(routineN, handle)
    2933              : 
    2934         3938 :       CALL dbcsr_get_info(qs_ot_env%rot_mat_x, nfullrows_total=k)
    2935              : 
    2936         3938 :       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         3886 :                              blacs_env=qs_ot_env%blacs_env)
    2947              : 
    2948              :          ! Compute: exp_evals = EXP(-i*rot_mat_evals)
    2949        15544 :          ALLOCATE (exp_evals_re(k), exp_evals_im(k))
    2950        22130 :          exp_evals_re(:) = COS(-qs_ot_env%rot_mat_evals(:))
    2951        22130 :          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         3886 :          CALL dbcsr_copy(buf_1, qs_ot_env%rot_mat_evec_re, name="buf_1")
    2956         3886 :          CALL dbcsr_scale_by_vector(buf_1, alpha=exp_evals_re, side='right')
    2957         3886 :          CALL dbcsr_copy(buf_2, qs_ot_env%rot_mat_evec_im, name="buf_2")
    2958         3886 :          CALL dbcsr_scale_by_vector(buf_2, alpha=exp_evals_im, side='right')
    2959         3886 :          CALL dbcsr_add(buf_1, buf_2, alpha_scalar=+1.0_dp, beta_scalar=-1.0_dp)
    2960         3886 :          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         3886 :          CALL dbcsr_copy(buf_1, qs_ot_env%rot_mat_evec_im)
    2963         3886 :          CALL dbcsr_scale_by_vector(buf_1, alpha=exp_evals_re, side='right')
    2964         3886 :          CALL dbcsr_copy(buf_2, qs_ot_env%rot_mat_evec_re)
    2965         3886 :          CALL dbcsr_scale_by_vector(buf_2, alpha=exp_evals_im, side='right')
    2966         3886 :          CALL dbcsr_add(buf_1, buf_2, alpha_scalar=+1.0_dp, beta_scalar=+1.0_dp)
    2967         3886 :          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         3886 :          CALL dbcsr_release(buf_1)
    2971         3886 :          CALL dbcsr_release(buf_2)
    2972         3886 :          DEALLOCATE (exp_evals_re, exp_evals_im)
    2973              :       END IF
    2974              : 
    2975         3938 :       CALL timestop(handle)
    2976              : 
    2977         7876 :    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         1884 :    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         1884 :       INTEGER, ALLOCATABLE, DIMENSION(:)       :: cols, rows
    2994         1884 :       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         1884 :       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         1884 :       CALL timeset(routineN, handle)
    3006              : 
    3007         1884 :       CALL dbcsr_get_info(qs_ot_env%rot_mat_u, nfullrows_total=k)
    3008         1884 :       IF (k /= 0) THEN
    3009         1858 :          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         1858 :          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         1858 :          CALL dbcsr_copy(inner_deriv_re, qs_ot_env%rot_mat_dedu, "inner_deriv_re") ! TODO just create
    3015         1858 :          CALL dbcsr_copy(inner_deriv_im, qs_ot_env%rot_mat_dedu, "inner_deriv_im") ! TODO just create
    3016              : 
    3017         1858 :          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         1858 :          CALL dbcsr_multiply('T', 'N', +1.0_dp, qs_ot_env%rot_mat_evec_im, mat_buf, 0.0_dp, inner_deriv_re)
    3019         1858 :          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         1858 :          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         1858 :          CALL dbcsr_multiply('T', 'N', +1.0_dp, qs_ot_env%rot_mat_evec_re, mat_buf, 1.0_dp, inner_deriv_re)
    3023         1858 :          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         1858 :          max_blocks = 0
    3028         1858 :          CALL dbcsr_iterator_start(iter, inner_deriv_re)
    3029         2787 :          DO WHILE (dbcsr_iterator_blocks_left(iter))
    3030          929 :             CALL dbcsr_iterator_next_block(iter, row, col)
    3031          929 :             max_blocks = max_blocks + 1
    3032              :          END DO
    3033         1858 :          CALL dbcsr_iterator_stop(iter)
    3034         1858 :          CALL dbcsr_iterator_start(iter, inner_deriv_im)
    3035         2787 :          DO WHILE (dbcsr_iterator_blocks_left(iter))
    3036          929 :             CALL dbcsr_iterator_next_block(iter, row, col)
    3037          929 :             max_blocks = max_blocks + 1
    3038              :          END DO
    3039         1858 :          CALL dbcsr_iterator_stop(iter)
    3040              : 
    3041         7432 :          ALLOCATE (rows(MAX(max_blocks, 1)), cols(MAX(max_blocks, 1)))
    3042         1858 :          nblocks = 0
    3043         1858 :          CALL dbcsr_iterator_start(iter, inner_deriv_re)
    3044         2787 :          DO WHILE (dbcsr_iterator_blocks_left(iter))
    3045          929 :             CALL dbcsr_iterator_next_block(iter, row, col)
    3046          929 :             duplicate = .FALSE.
    3047          929 :             DO iblock = 1, nblocks
    3048          929 :                duplicate = duplicate .OR. (rows(iblock) == row .AND. cols(iblock) == col)
    3049              :             END DO
    3050         2787 :             IF (.NOT. duplicate) THEN
    3051          929 :                nblocks = nblocks + 1
    3052          929 :                rows(nblocks) = row
    3053          929 :                cols(nblocks) = col
    3054              :             END IF
    3055              :          END DO
    3056         1858 :          CALL dbcsr_iterator_stop(iter)
    3057         1858 :          CALL dbcsr_iterator_start(iter, inner_deriv_im)
    3058         2787 :          DO WHILE (dbcsr_iterator_blocks_left(iter))
    3059          929 :             CALL dbcsr_iterator_next_block(iter, row, col)
    3060          929 :             duplicate = .FALSE.
    3061         1858 :             DO iblock = 1, nblocks
    3062         1858 :                duplicate = duplicate .OR. (rows(iblock) == row .AND. cols(iblock) == col)
    3063              :             END DO
    3064         2787 :             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         1858 :          CALL dbcsr_iterator_stop(iter)
    3071              : 
    3072              :          CALL dbcsr_get_info(inner_deriv_re, distribution=dist, row_blk_size=row_blk_size, &
    3073         1858 :                              col_blk_size=col_blk_size)
    3074              :          CALL dbcsr_create(outer_deriv_re, "outer_deriv_re", dist, dbcsr_type_no_symmetry, &
    3075         1858 :                            row_blk_size, col_blk_size)
    3076              :          CALL dbcsr_create(outer_deriv_im, "outer_deriv_im", dist, dbcsr_type_no_symmetry, &
    3077         1858 :                            row_blk_size, col_blk_size)
    3078         1858 :          IF (nblocks > 0) THEN
    3079          929 :             CALL dbcsr_reserve_blocks(outer_deriv_re, rows=rows(1:nblocks), cols=cols(1:nblocks))
    3080          929 :             CALL dbcsr_reserve_blocks(outer_deriv_im, rows=rows(1:nblocks), cols=cols(1:nblocks))
    3081              :          END IF
    3082         1858 :          CALL dbcsr_finalize(outer_deriv_re)
    3083         1858 :          CALL dbcsr_finalize(outer_deriv_im)
    3084         1858 :          CALL dbcsr_set(outer_deriv_re, 0.0_dp)
    3085         1858 :          CALL dbcsr_set(outer_deriv_im, 0.0_dp)
    3086              : 
    3087         1858 :          CALL dbcsr_get_info(outer_deriv_re, row_blk_offset=row_blk_offset, col_blk_offset=col_blk_offset)
    3088         1858 :          CALL dbcsr_iterator_start(iter, outer_deriv_re)
    3089         2787 :          DO WHILE (dbcsr_iterator_blocks_left(iter))
    3090          929 :             CALL dbcsr_iterator_next_block(iter, row, col)
    3091          929 :             CALL dbcsr_get_block_p(inner_deriv_re, row, col, block_in_re, found_in_re)
    3092          929 :             CALL dbcsr_get_block_p(inner_deriv_im, row, col, block_in_im, found_in_im)
    3093          929 :             CALL dbcsr_get_block_p(outer_deriv_re, row, col, block_out_re, found_out_re)
    3094          929 :             CALL dbcsr_get_block_p(outer_deriv_im, row, col, block_out_im, found_out_im)
    3095          929 :             CPASSERT(found_out_re .AND. found_out_im)
    3096              : 
    3097         7133 :             DO i = 1, SIZE(block_out_re, 1)
    3098        31503 :             DO j = 1, SIZE(block_out_re, 2)
    3099        26228 :                e1 = qs_ot_env%rot_mat_evals(row_blk_offset(row) + i - 1)
    3100        26228 :                e2 = qs_ot_env%rot_mat_evals(col_blk_offset(col) + j - 1)
    3101        26228 :                re_part = 0.0_dp
    3102        26228 :                im_part = 0.0_dp
    3103        26228 :                IF (found_in_re) re_part = block_in_re(i, j)
    3104        26228 :                IF (found_in_im) im_part = block_in_im(i, j)
    3105        26228 :                cval_in = CMPLX(re_part, im_part, dp)
    3106        26228 :                cval_out = cval_in*cint(e1, e2)
    3107        26228 :                block_out_re(i, j) = REAL(cval_out)
    3108        30574 :                block_out_im(i, j) = AIMAG(cval_out)
    3109              :             END DO
    3110              :             END DO
    3111              :          END DO
    3112         1858 :          CALL dbcsr_iterator_stop(iter)
    3113         1858 :          DEALLOCATE (rows, cols)
    3114         1858 :          CALL dbcsr_release(inner_deriv_re)
    3115         1858 :          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         1858 :          CALL dbcsr_multiply('N', 'N', +1.0_dp, qs_ot_env%rot_mat_evec_re, outer_deriv_re, 0.0_dp, mat_buf)
    3119         1858 :          CALL dbcsr_multiply('N', 'N', -1.0_dp, qs_ot_env%rot_mat_evec_im, outer_deriv_im, 1.0_dp, mat_buf)
    3120         1858 :          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         1858 :          CALL dbcsr_multiply('N', 'N', +1.0_dp, qs_ot_env%rot_mat_evec_re, outer_deriv_im, 0.0_dp, mat_buf)
    3123         1858 :          CALL dbcsr_multiply('N', 'N', +1.0_dp, qs_ot_env%rot_mat_evec_im, outer_deriv_re, 1.0_dp, mat_buf)
    3124         1858 :          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         1858 :          CALL qs_ot_square_transpose(qs_ot_env%matrix_buf1, mat_buf, qs_ot_env%rot_mat_u)
    3129         1858 :          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         1858 :                         alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    3132              : 
    3133         1858 :          CALL dbcsr_release(mat_buf)
    3134         1858 :          CALL dbcsr_release(outer_deriv_re)
    3135        13006 :          CALL dbcsr_release(outer_deriv_im)
    3136              :       END IF
    3137         3768 :       CALL timestop(handle)
    3138              :    CONTAINS
    3139              : 
    3140              : ! **************************************************************************************************
    3141              : !> \brief ...
    3142              : !> \param e1 ...
    3143              : !> \param e2 ...
    3144              : !> \return ...
    3145              : ! **************************************************************************************************
    3146        26228 :       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        26228 :          l1 = (0.0_dp, -1.0_dp)*e1
    3154        26228 :          l2 = (0.0_dp, -1.0_dp)*e2
    3155        26228 :          IF (ABS(l1 - l2) > 0.5_dp) THEN
    3156         1020 :             cint = (EXP(l1) - EXP(l2))/(l1 - l2)
    3157              :          ELSE
    3158              :             x = 1.0_dp
    3159              :             cint = 0.0_dp
    3160       428536 :             DO I = 1, 16
    3161       403328 :                cint = cint + x
    3162       428536 :                x = x*(l1 - l2)/REAL(I + 1, KIND=dp)
    3163              :             END DO
    3164        25208 :             cint = cint*EXP(l2)
    3165              :          END IF
    3166        26228 :       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       109761 :    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       109761 :       qs_ot_env%do_taylor = .FALSE.
    3185       109761 :       N = 0
    3186       109761 :       num_error = qs_ot_env%largest_eval_upper_bound/(2.0_dp)
    3187       470653 :       DO WHILE (num_error > qs_ot_env%settings%eps_taylor .AND. N <= qs_ot_env%settings%max_taylor)
    3188       360892 :          N = N + 1
    3189       407367 :          num_error = num_error*qs_ot_env%largest_eval_upper_bound/REAL((2*N + 1)*(2*N + 2), KIND=dp)
    3190              :       END DO
    3191       109761 :       qs_ot_env%taylor_order = N
    3192       109761 :       IF (qs_ot_env%taylor_order <= qs_ot_env%settings%max_taylor) THEN
    3193        58064 :          qs_ot_env%do_taylor = .TRUE.
    3194              :       END IF
    3195              : 
    3196       109761 :    END SUBROUTINE decide_strategy
    3197              : 
    3198              : ! **************************************************************************************************
    3199              : !> \brief c=(c0*cos(p^0.5)+x*sin(p^0.5)*p^(-0.5)) x rot_mat_u
    3200              : !>        this assumes that x is already ortho to S*C0, and that p is x*S*x
    3201              : !>        rot_mat_u is an optional rotation matrix
    3202              : !> \param matrix_c ...
    3203              : !> \param matrix_x ...
    3204              : !> \param qs_ot_env ...
    3205              : ! **************************************************************************************************
    3206       203906 :    SUBROUTINE qs_ot_get_orbitals(matrix_c, matrix_x, qs_ot_env)
    3207              : 
    3208              :       TYPE(dbcsr_type), POINTER                          :: matrix_c, matrix_x
    3209              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3210              : 
    3211              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_orbitals'
    3212              :       REAL(KIND=dp), PARAMETER                           :: rone = 1.0_dp, rzero = 0.0_dp
    3213              : 
    3214              :       INTEGER                                            :: handle, k, n
    3215              :       TYPE(dbcsr_type), POINTER                          :: matrix_kk
    3216              : 
    3217       101953 :       CALL timeset(routineN, handle)
    3218              : 
    3219       101953 :       CALL dbcsr_get_info(matrix_x, nfullrows_total=n, nfullcols_total=k)
    3220              : 
    3221              :       ! rotate the multiplying matrices cosp and sinp instead of the result,
    3222              :       ! this should be cheaper for large basis sets
    3223       101953 :       IF (qs_ot_env%settings%do_rotation) THEN
    3224         3690 :          matrix_kk => qs_ot_env%matrix_buf1
    3225              :          CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_cosp, &
    3226         3690 :                              qs_ot_env%rot_mat_u, rzero, matrix_kk)
    3227              :       ELSE
    3228        98263 :          matrix_kk => qs_ot_env%matrix_cosp
    3229              :       END IF
    3230              : 
    3231              :       CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_c0, matrix_kk, &
    3232       101953 :                           rzero, matrix_c)
    3233              : 
    3234       101953 :       IF (qs_ot_env%settings%do_rotation) THEN
    3235         3690 :          matrix_kk => qs_ot_env%matrix_buf1
    3236              :          CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_sinp, &
    3237         3690 :                              qs_ot_env%rot_mat_u, rzero, matrix_kk)
    3238              :       ELSE
    3239        98263 :          matrix_kk => qs_ot_env%matrix_sinp
    3240              :       END IF
    3241              :       CALL dbcsr_multiply('N', 'N', rone, matrix_x, matrix_kk, &
    3242       101953 :                           rone, matrix_c)
    3243              : 
    3244       101953 :       CALL timestop(handle)
    3245              : 
    3246       101953 :    END SUBROUTINE qs_ot_get_orbitals
    3247              : 
    3248              : ! **************************************************************************************************
    3249              : !> \brief update complex K-point orbitals with the finite STRICT transformation
    3250              : !> \param matrix_c real output orbitals
    3251              : !> \param matrix_c_im imaginary output orbitals
    3252              : !> \param matrix_s real overlap matrix
    3253              : !> \param matrix_s_im imaginary overlap matrix
    3254              : !> \param qs_ot_env OT channel state
    3255              : ! **************************************************************************************************
    3256         1908 :    SUBROUTINE qs_ot_get_orbitals_complex(matrix_c, matrix_c_im, matrix_s, matrix_s_im, qs_ot_env)
    3257              :       TYPE(dbcsr_type), POINTER                          :: matrix_c, matrix_c_im, matrix_s, &
    3258              :                                                             matrix_s_im
    3259              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3260              : 
    3261              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_orbitals_complex'
    3262              : 
    3263              :       INTEGER                                            :: handle
    3264              :       TYPE(dbcsr_type)                                   :: rotated_im, rotated_re, rotation_tmp
    3265              : 
    3266         1908 :       CALL timeset(routineN, handle)
    3267              : 
    3268         1908 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    3269         1908 :       CPASSERT(qs_ot_env%settings%ot_algorithm == 'TOD')
    3270              : 
    3271              :       CALL qs_ot_complex_multiply('N', 'N', matrix_s, matrix_s_im, &
    3272              :                                   qs_ot_env%matrix_x, qs_ot_env%matrix_x_im, &
    3273              :                                   qs_ot_env%matrix_sx, qs_ot_env%matrix_sx_im, &
    3274         1908 :                                   qs_ot_env%matrix_tmp_nk)
    3275              :       CALL qs_ot_get_p_complex(qs_ot_env%matrix_x, qs_ot_env%matrix_x_im, &
    3276         1908 :                                qs_ot_env%matrix_sx, qs_ot_env%matrix_sx_im, qs_ot_env)
    3277              : 
    3278              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_c0, qs_ot_env%matrix_c0_im, &
    3279              :                                   qs_ot_env%matrix_cosp, qs_ot_env%matrix_cosp_im, &
    3280         1908 :                                   matrix_c, matrix_c_im, qs_ot_env%matrix_tmp_nk)
    3281              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_x, qs_ot_env%matrix_x_im, &
    3282              :                                   qs_ot_env%matrix_sinp, qs_ot_env%matrix_sinp_im, &
    3283              :                                   qs_ot_env%matrix_buf_nk, qs_ot_env%matrix_buf_nk_im, &
    3284         1908 :                                   qs_ot_env%matrix_tmp_nk)
    3285         1908 :       CALL dbcsr_add(matrix_c, qs_ot_env%matrix_buf_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3286         1908 :       CALL dbcsr_add(matrix_c_im, qs_ot_env%matrix_buf_nk_im, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3287              : 
    3288         1908 :       IF (qs_ot_env%settings%do_rotation) THEN
    3289          968 :          CALL qs_ot_generate_rotation_complex(qs_ot_env)
    3290              : 
    3291          968 :          CALL dbcsr_copy(rotated_re, matrix_c, name="strict_rotated_re")
    3292          968 :          CALL dbcsr_copy(rotated_im, matrix_c_im, name="strict_rotated_im")
    3293          968 :          CALL dbcsr_copy(rotation_tmp, matrix_c, name="strict_rotation_tmp")
    3294              : 
    3295              :          ! C_out = Q(X)*U for the finite STRICT chart Q(X).
    3296              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_c, qs_ot_env%rot_mat_u, &
    3297          968 :                              0.0_dp, rotated_re)
    3298              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_c_im, qs_ot_env%rot_mat_u_im, &
    3299          968 :                              0.0_dp, rotation_tmp)
    3300          968 :          CALL dbcsr_add(rotated_re, rotation_tmp, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    3301              : 
    3302              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_c, qs_ot_env%rot_mat_u_im, &
    3303          968 :                              0.0_dp, rotated_im)
    3304              :          CALL dbcsr_multiply('N', 'N', 1.0_dp, matrix_c_im, qs_ot_env%rot_mat_u, &
    3305          968 :                              0.0_dp, rotation_tmp)
    3306          968 :          CALL dbcsr_add(rotated_im, rotation_tmp, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3307              : 
    3308          968 :          CALL dbcsr_copy(matrix_c, rotated_re)
    3309          968 :          CALL dbcsr_copy(matrix_c_im, rotated_im)
    3310          968 :          CALL dbcsr_release(rotated_re)
    3311          968 :          CALL dbcsr_release(rotated_im)
    3312          968 :          CALL dbcsr_release(rotation_tmp)
    3313              :       END IF
    3314              : 
    3315         1908 :       CALL timestop(handle)
    3316              : 
    3317         1908 :    END SUBROUTINE qs_ot_get_orbitals_complex
    3318              : 
    3319              : ! **************************************************************************************************
    3320              : !> \brief this routines computes dE/dx=dx, with dx ortho to sc0
    3321              : !>        needs dE/dC=hc,C0,X,SX,p
    3322              : !>        if preconditioned it will not be the derivative, but the lagrangian multiplier
    3323              : !>        is changed so that P*dE/dx is the right derivative (i.e. in the allowed subspace)
    3324              : !> \param matrix_hc ...
    3325              : !> \param matrix_x ...
    3326              : !> \param matrix_sx ...
    3327              : !> \param matrix_gx ...
    3328              : !> \param qs_ot_env ...
    3329              : ! **************************************************************************************************
    3330       233319 :    SUBROUTINE qs_ot_get_derivative(matrix_hc, matrix_x, matrix_sx, matrix_gx, &
    3331              :                                    qs_ot_env)
    3332              :       TYPE(dbcsr_type), POINTER                          :: matrix_hc, matrix_x, matrix_sx, matrix_gx
    3333              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3334              : 
    3335              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_derivative'
    3336              :       REAL(KIND=dp), PARAMETER                           :: rone = 1.0_dp, rzero = 0.0_dp
    3337              : 
    3338              :       INTEGER                                            :: handle, k, n, ortho_k
    3339              :       TYPE(dbcsr_type), POINTER                          :: matrix_hc_local, matrix_target
    3340              : 
    3341        77773 :       CALL timeset(routineN, handle)
    3342              : 
    3343        77773 :       NULLIFY (matrix_hc_local)
    3344              : 
    3345        77773 :       CALL dbcsr_get_info(matrix_x, nfullrows_total=n, nfullcols_total=k)
    3346              : 
    3347              :       ! could in principle be taken inside qs_ot_get_derivative_* for increased efficiency
    3348              :       ! create a local rotated version of matrix_hc leaving matrix_hc untouched (needed
    3349              :       ! for lagrangian multipliers)
    3350        77773 :       IF (qs_ot_env%settings%do_rotation) THEN
    3351         1874 :          CALL dbcsr_copy(matrix_gx, matrix_hc) ! use gx as temporary
    3352         1874 :          CALL dbcsr_init_p(matrix_hc_local)
    3353         1874 :          CALL dbcsr_copy(matrix_hc_local, matrix_hc, name='matrix_hc_local')
    3354         1874 :          CALL dbcsr_set(matrix_hc_local, 0.0_dp)
    3355         1874 :          CALL dbcsr_multiply('N', 'T', rone, matrix_gx, qs_ot_env%rot_mat_u, rzero, matrix_hc_local)
    3356              :       ELSE
    3357        75899 :          matrix_hc_local => matrix_hc
    3358              :       END IF
    3359              : 
    3360        77773 :       IF (qs_ot_env%do_taylor) THEN
    3361        42613 :          CALL qs_ot_get_derivative_taylor(matrix_hc_local, matrix_x, matrix_sx, matrix_gx, qs_ot_env)
    3362              :       ELSE
    3363        35160 :          CALL qs_ot_get_derivative_diag(matrix_hc_local, matrix_x, matrix_sx, matrix_gx, qs_ot_env)
    3364              :       END IF
    3365              : 
    3366              :       ! and make it orthogonal
    3367        77773 :       CALL dbcsr_get_info(qs_ot_env%matrix_sc0, nfullcols_total=ortho_k)
    3368              : 
    3369        77773 :       IF (ASSOCIATED(qs_ot_env%preconditioner)) THEN
    3370        67287 :          matrix_target => qs_ot_env%matrix_psc0
    3371              :       ELSE
    3372        10486 :          matrix_target => qs_ot_env%matrix_sc0
    3373              :       END IF
    3374              :       ! first make the matrix os if not yet valid
    3375        77773 :       IF (.NOT. qs_ot_env%os_valid) THEN
    3376              :          ! this assumes that the preconditioner is a single matrix
    3377              :          ! that maps sc0 onto psc0
    3378              : 
    3379         8452 :          IF (ASSOCIATED(qs_ot_env%preconditioner)) THEN
    3380              :             CALL apply_preconditioner(qs_ot_env%preconditioner, qs_ot_env%matrix_sc0, &
    3381         7470 :                                       qs_ot_env%matrix_psc0)
    3382              :          END IF
    3383              :          CALL dbcsr_multiply('T', 'N', rone, &
    3384              :                              qs_ot_env%matrix_sc0, matrix_target, &
    3385         8452 :                              rzero, qs_ot_env%matrix_os)
    3386              :          CALL cp_dbcsr_cholesky_decompose(qs_ot_env%matrix_os, &
    3387         8452 :                                           para_env=qs_ot_env%para_env, blacs_env=qs_ot_env%blacs_env)
    3388              :          CALL cp_dbcsr_cholesky_invert(qs_ot_env%matrix_os, &
    3389              :                                        para_env=qs_ot_env%para_env, blacs_env=qs_ot_env%blacs_env, &
    3390         8452 :                                        uplo_to_full=.TRUE.)
    3391         8452 :          qs_ot_env%os_valid = .TRUE.
    3392              :       END IF
    3393              :       CALL dbcsr_multiply('T', 'N', rone, matrix_target, matrix_gx, &
    3394        77773 :                           rzero, qs_ot_env%matrix_buf1_ortho)
    3395              :       CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_os, &
    3396        77773 :                           qs_ot_env%matrix_buf1_ortho, rzero, qs_ot_env%matrix_buf2_ortho)
    3397              :       CALL dbcsr_multiply('N', 'N', -rone, qs_ot_env%matrix_sc0, &
    3398        77773 :                           qs_ot_env%matrix_buf2_ortho, rone, matrix_gx)
    3399              :       ! also treat the rot_mat gradient here
    3400        77773 :       IF (qs_ot_env%settings%do_rotation) THEN
    3401         1874 :          CALL qs_ot_rot_mat_derivative(qs_ot_env)
    3402              :       END IF
    3403              : 
    3404        77773 :       IF (qs_ot_env%settings%do_rotation) THEN
    3405         1874 :          CALL dbcsr_release_p(matrix_hc_local)
    3406              :       END IF
    3407              : 
    3408        77773 :       CALL timestop(handle)
    3409              : 
    3410        77773 :    END SUBROUTINE qs_ot_get_derivative
    3411              : 
    3412              : ! **************************************************************************************************
    3413              : !> \brief Prepare the inverse metric used to project a complex STRICT gradient.
    3414              : !>        An unusable preconditioner is detached before any minimizer history is updated.
    3415              : !> \param qs_ot_env OT channel state
    3416              : !> \param preconditioner_rejected true if the attached preconditioner was not positive definite
    3417              : ! **************************************************************************************************
    3418         2176 :    SUBROUTINE qs_ot_prepare_complex_tangent_metric(qs_ot_env, preconditioner_rejected)
    3419              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3420              :       LOGICAL, INTENT(OUT), OPTIONAL                     :: preconditioner_rejected
    3421              : 
    3422              :       INTEGER                                            :: i, k
    3423              :       REAL(KIND=dp)                                      :: eval_scale, eval_threshold
    3424              :       TYPE(dbcsr_type), POINTER                          :: target_im, target_re
    3425              : 
    3426         1976 :       IF (PRESENT(preconditioner_rejected)) preconditioner_rejected = .FALSE.
    3427         1976 :       IF (qs_ot_env%os_valid) RETURN
    3428              : 
    3429          200 :       IF (ASSOCIATED(qs_ot_env%preconditioner)) THEN
    3430          190 :          target_re => qs_ot_env%matrix_psc0
    3431          190 :          target_im => qs_ot_env%matrix_psc0_im
    3432              :          CALL apply_preconditioner(qs_ot_env%preconditioner, &
    3433              :                                    qs_ot_env%matrix_sc0, qs_ot_env%matrix_sc0_im, &
    3434          190 :                                    target_re, target_im)
    3435              :       ELSE
    3436           10 :          target_re => qs_ot_env%matrix_sc0
    3437           10 :          target_im => qs_ot_env%matrix_sc0_im
    3438              :       END IF
    3439              :       CALL qs_ot_complex_multiply('C', 'N', qs_ot_env%matrix_sc0, qs_ot_env%matrix_sc0_im, &
    3440              :                                   target_re, target_im, qs_ot_env%matrix_os, qs_ot_env%matrix_os_im, &
    3441          200 :                                   qs_ot_env%matrix_buf1)
    3442          200 :       CALL dbcsr_get_info(qs_ot_env%matrix_os, nfullrows_total=k)
    3443              :       CALL cp_dbcsr_heevd(matrix_re=qs_ot_env%matrix_os, matrix_im=qs_ot_env%matrix_os_im, &
    3444              :                           eigenvectors_re=qs_ot_env%matrix_r, eigenvectors_im=qs_ot_env%matrix_r_im, &
    3445              :                           eigenvalues=qs_ot_env%evals, para_env=qs_ot_env%para_env, &
    3446          200 :                           blacs_env=qs_ot_env%blacs_env)
    3447         1596 :       eval_scale = MAX(1.0_dp, MAXVAL(ABS(qs_ot_env%evals(1:k))))
    3448          200 :       eval_threshold = 100.0_dp*EPSILON(1.0_dp)*eval_scale
    3449         1596 :       IF (MINVAL(qs_ot_env%evals(1:k)) <= eval_threshold .AND. &
    3450              :           ASSOCIATED(qs_ot_env%preconditioner)) THEN
    3451            0 :          NULLIFY (qs_ot_env%preconditioner)
    3452            0 :          IF (PRESENT(preconditioner_rejected)) preconditioner_rejected = .TRUE.
    3453            0 :          target_re => qs_ot_env%matrix_sc0
    3454            0 :          target_im => qs_ot_env%matrix_sc0_im
    3455              :          CALL qs_ot_complex_multiply('C', 'N', qs_ot_env%matrix_sc0, qs_ot_env%matrix_sc0_im, &
    3456              :                                      target_re, target_im, qs_ot_env%matrix_os, qs_ot_env%matrix_os_im, &
    3457            0 :                                      qs_ot_env%matrix_buf1)
    3458              :          CALL cp_dbcsr_heevd(matrix_re=qs_ot_env%matrix_os, matrix_im=qs_ot_env%matrix_os_im, &
    3459              :                              eigenvectors_re=qs_ot_env%matrix_r, eigenvectors_im=qs_ot_env%matrix_r_im, &
    3460              :                              eigenvalues=qs_ot_env%evals, para_env=qs_ot_env%para_env, &
    3461            0 :                              blacs_env=qs_ot_env%blacs_env)
    3462            0 :          eval_scale = MAX(1.0_dp, MAXVAL(ABS(qs_ot_env%evals(1:k))))
    3463            0 :          eval_threshold = 100.0_dp*EPSILON(1.0_dp)*eval_scale
    3464              :       END IF
    3465         1596 :       CPASSERT(MINVAL(qs_ot_env%evals(1:k)) > eval_threshold)
    3466         1596 :       DO i = 1, k
    3467         1596 :          qs_ot_env%dum(i) = 1.0_dp/qs_ot_env%evals(i)
    3468              :       END DO
    3469          200 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1, qs_ot_env%matrix_r)
    3470          200 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1_im, qs_ot_env%matrix_r_im)
    3471          200 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1, alpha=qs_ot_env%dum, side='right')
    3472          200 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1_im, alpha=qs_ot_env%dum, side='right')
    3473              :       CALL qs_ot_complex_multiply('N', 'C', qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3474              :                                   qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3475              :                                   qs_ot_env%matrix_os, qs_ot_env%matrix_os_im, &
    3476          200 :                                   qs_ot_env%matrix_buf2)
    3477          200 :       qs_ot_env%os_valid = .TRUE.
    3478              : 
    3479              :    END SUBROUTINE qs_ot_prepare_complex_tangent_metric
    3480              : 
    3481              : ! **************************************************************************************************
    3482              : !> \brief finite complex STRICT derivative, projected onto C0^H*S*X=0
    3483              : !> \param matrix_hc real part of H(k)*C(k)
    3484              : !> \param matrix_hc_im imaginary part of H(k)*C(k)
    3485              : !> \param qs_ot_env OT channel state
    3486              : !> \param matrix_hc_rotation occupation-weighted H(k)C(k) for the rotation channel
    3487              : !> \param matrix_hc_rotation_im imaginary component of matrix_hc_rotation
    3488              : ! **************************************************************************************************
    3489         3752 :    SUBROUTINE qs_ot_get_derivative_complex(matrix_hc, matrix_hc_im, qs_ot_env, &
    3490              :                                            matrix_hc_rotation, matrix_hc_rotation_im)
    3491              :       TYPE(dbcsr_type), POINTER                          :: matrix_hc, matrix_hc_im
    3492              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3493              :       TYPE(dbcsr_type), OPTIONAL, POINTER                :: matrix_hc_rotation, matrix_hc_rotation_im
    3494              : 
    3495              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_derivative_complex'
    3496              : 
    3497              :       INTEGER                                            :: handle
    3498              :       TYPE(dbcsr_distribution_type)                      :: dist
    3499              :       TYPE(dbcsr_type)                                   :: tmp_nk
    3500              :       TYPE(dbcsr_type), POINTER                          :: hc_rotation_im, hc_rotation_re, &
    3501              :                                                             hc_work_im, hc_work_re, target_im, &
    3502              :                                                             target_re
    3503              :       TYPE(dbcsr_type), TARGET                           :: hc_rot_im, hc_rot_re
    3504              : 
    3505         1876 :       CALL timeset(routineN, handle)
    3506              : 
    3507         1876 :       CPASSERT(qs_ot_env%has_complex_kpoint_state)
    3508         1876 :       CPASSERT(qs_ot_env%settings%ot_algorithm == 'TOD')
    3509              : 
    3510         1876 :       hc_rotation_re => matrix_hc
    3511         1876 :       hc_rotation_im => matrix_hc_im
    3512         1876 :       IF (PRESENT(matrix_hc_rotation) .OR. PRESENT(matrix_hc_rotation_im)) THEN
    3513          568 :          CPASSERT(PRESENT(matrix_hc_rotation) .AND. PRESENT(matrix_hc_rotation_im))
    3514          568 :          CPASSERT(ASSOCIATED(matrix_hc_rotation))
    3515          568 :          CPASSERT(ASSOCIATED(matrix_hc_rotation_im))
    3516          568 :          hc_rotation_re => matrix_hc_rotation
    3517          568 :          hc_rotation_im => matrix_hc_rotation_im
    3518              :       END IF
    3519         1876 :       hc_work_re => matrix_hc
    3520         1876 :       hc_work_im => matrix_hc_im
    3521              : 
    3522         1876 :       IF (qs_ot_env%settings%do_rotation) THEN
    3523         1216 :          CALL qs_ot_generate_rotation_complex(qs_ot_env)
    3524              : 
    3525              :          ! Reconstruct the unrotated finite STRICT orbitals Q(X).
    3526              :          CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_c0, qs_ot_env%matrix_c0_im, &
    3527              :                                      qs_ot_env%matrix_cosp, qs_ot_env%matrix_cosp_im, &
    3528              :                                      qs_ot_env%matrix_buf_nk, qs_ot_env%matrix_buf_nk_im, &
    3529         1216 :                                      qs_ot_env%matrix_tmp_nk)
    3530              :          CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_x, qs_ot_env%matrix_x_im, &
    3531              :                                      qs_ot_env%matrix_sinp, qs_ot_env%matrix_sinp_im, &
    3532              :                                      qs_ot_env%matrix_gx, qs_ot_env%matrix_gx_im, &
    3533         1216 :                                      qs_ot_env%matrix_tmp_nk)
    3534              :          CALL dbcsr_add(qs_ot_env%matrix_buf_nk, qs_ot_env%matrix_gx, &
    3535         1216 :                         alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3536              :          CALL dbcsr_add(qs_ot_env%matrix_buf_nk_im, qs_ot_env%matrix_gx_im, &
    3537         1216 :                         alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3538              : 
    3539              :          ! dF/dU = Q(X)^H*G_C for C=Q(X)*U.
    3540              :          CALL qs_ot_complex_multiply('C', 'N', &
    3541              :                                      qs_ot_env%matrix_buf_nk, qs_ot_env%matrix_buf_nk_im, &
    3542              :                                      hc_rotation_re, hc_rotation_im, &
    3543              :                                      qs_ot_env%rot_mat_dedu, qs_ot_env%rot_mat_dedu_im, &
    3544         1216 :                                      qs_ot_env%matrix_buf1)
    3545         1216 :          CALL qs_ot_rot_mat_derivative_complex(qs_ot_env)
    3546              : 
    3547              :          ! The STRICT coordinate sees G_Q=G_C*U^H.
    3548         1216 :          CALL dbcsr_copy(hc_rot_re, matrix_hc, name="strict_hc_rot_re")
    3549         1216 :          CALL dbcsr_copy(hc_rot_im, matrix_hc_im, name="strict_hc_rot_im")
    3550         1216 :          CALL dbcsr_copy(tmp_nk, matrix_hc, name="strict_hc_rot_tmp")
    3551              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, matrix_hc, qs_ot_env%rot_mat_u, &
    3552         1216 :                              0.0_dp, hc_rot_re)
    3553              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, matrix_hc_im, qs_ot_env%rot_mat_u_im, &
    3554         1216 :                              0.0_dp, tmp_nk)
    3555         1216 :          CALL dbcsr_add(hc_rot_re, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3556              : 
    3557              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, matrix_hc_im, qs_ot_env%rot_mat_u, &
    3558         1216 :                              0.0_dp, hc_rot_im)
    3559              :          CALL dbcsr_multiply('N', 'T', 1.0_dp, matrix_hc, qs_ot_env%rot_mat_u_im, &
    3560         1216 :                              0.0_dp, tmp_nk)
    3561         1216 :          CALL dbcsr_add(hc_rot_im, tmp_nk, alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    3562         1216 :          hc_work_re => hc_rot_re
    3563         1216 :          hc_work_im => hc_rot_im
    3564              :       END IF
    3565              : 
    3566              :       ! Direct X contribution, H*C sinc(sqrt(P)).
    3567              :       CALL qs_ot_complex_multiply('N', 'N', hc_work_re, hc_work_im, &
    3568              :                                   qs_ot_env%matrix_sinp, qs_ot_env%matrix_sinp_im, &
    3569              :                                   qs_ot_env%matrix_gx, qs_ot_env%matrix_gx_im, &
    3570         1876 :                                   qs_ot_env%matrix_tmp_nk)
    3571              : 
    3572              :       ! Frechet contribution from X sinc(sqrt(P)).
    3573              :       CALL qs_ot_complex_multiply('C', 'N', hc_work_re, hc_work_im, &
    3574              :                                   qs_ot_env%matrix_x, qs_ot_env%matrix_x_im, &
    3575              :                                   qs_ot_env%matrix_buf2, qs_ot_env%matrix_buf2_im, &
    3576         1876 :                                   qs_ot_env%matrix_buf1)
    3577              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_buf2, qs_ot_env%matrix_buf2_im, &
    3578              :                                   qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3579              :                                   qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3580         1876 :                                   qs_ot_env%matrix_buf4)
    3581              :       CALL qs_ot_complex_multiply('C', 'N', qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3582              :                                   qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3583              :                                   qs_ot_env%matrix_buf2, qs_ot_env%matrix_buf2_im, &
    3584         1876 :                                   qs_ot_env%matrix_buf4)
    3585              :       CALL dbcsr_hadamard_product(qs_ot_env%matrix_buf2, qs_ot_env%matrix_sinp_b, &
    3586         1876 :                                   qs_ot_env%matrix_buf3)
    3587              :       CALL dbcsr_hadamard_product(qs_ot_env%matrix_buf2_im, qs_ot_env%matrix_sinp_b, &
    3588         1876 :                                   qs_ot_env%matrix_buf3_im)
    3589              : 
    3590              :       ! Frechet contribution from C0 cos(sqrt(P)).
    3591              :       CALL qs_ot_complex_multiply('C', 'N', hc_work_re, hc_work_im, &
    3592              :                                   qs_ot_env%matrix_c0, qs_ot_env%matrix_c0_im, &
    3593              :                                   qs_ot_env%matrix_buf2, qs_ot_env%matrix_buf2_im, &
    3594         1876 :                                   qs_ot_env%matrix_buf1)
    3595              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_buf2, qs_ot_env%matrix_buf2_im, &
    3596              :                                   qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3597              :                                   qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3598         1876 :                                   qs_ot_env%matrix_buf4)
    3599              :       CALL qs_ot_complex_multiply('C', 'N', qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3600              :                                   qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3601              :                                   qs_ot_env%matrix_buf2, qs_ot_env%matrix_buf2_im, &
    3602         1876 :                                   qs_ot_env%matrix_buf4)
    3603              :       CALL dbcsr_hadamard_product(qs_ot_env%matrix_buf2, qs_ot_env%matrix_cosp_b, &
    3604         1876 :                                   qs_ot_env%matrix_buf4)
    3605              :       CALL dbcsr_hadamard_product(qs_ot_env%matrix_buf2_im, qs_ot_env%matrix_cosp_b, &
    3606         1876 :                                   qs_ot_env%matrix_buf4_im)
    3607              :       CALL dbcsr_add(qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf4, &
    3608         1876 :                      alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3609              :       CALL dbcsr_add(qs_ot_env%matrix_buf3_im, qs_ot_env%matrix_buf4_im, &
    3610         1876 :                      alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3611              : 
    3612              :       ! Transform back and add the Hermitian adjoint generated by dP.
    3613              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3614              :                                   qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf3_im, &
    3615              :                                   qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3616         1876 :                                   qs_ot_env%matrix_buf2)
    3617              :       CALL qs_ot_complex_multiply('N', 'C', qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    3618              :                                   qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    3619              :                                   qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf3_im, &
    3620         1876 :                                   qs_ot_env%matrix_buf2)
    3621         1876 :       CALL dbcsr_get_info(qs_ot_env%matrix_buf3, distribution=dist)
    3622              :       CALL dbcsr_transposed(qs_ot_env%matrix_buf4, qs_ot_env%matrix_buf3, &
    3623              :                             shallow_data_copy=.FALSE., use_distribution=dist, &
    3624         1876 :                             transpose_distribution=.FALSE.)
    3625              :       CALL dbcsr_transposed(qs_ot_env%matrix_buf4_im, qs_ot_env%matrix_buf3_im, &
    3626              :                             shallow_data_copy=.FALSE., use_distribution=dist, &
    3627         1876 :                             transpose_distribution=.FALSE.)
    3628              :       CALL dbcsr_add(qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf4, &
    3629         1876 :                      alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3630              :       CALL dbcsr_add(qs_ot_env%matrix_buf3_im, qs_ot_env%matrix_buf4_im, &
    3631         1876 :                      alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    3632              : 
    3633              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_sx, qs_ot_env%matrix_sx_im, &
    3634              :                                   qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf3_im, &
    3635              :                                   qs_ot_env%matrix_buf_nk, qs_ot_env%matrix_buf_nk_im, &
    3636         1876 :                                   qs_ot_env%matrix_tmp_nk)
    3637              :       CALL dbcsr_add(qs_ot_env%matrix_gx, qs_ot_env%matrix_buf_nk, &
    3638         1876 :                      alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3639              :       CALL dbcsr_add(qs_ot_env%matrix_gx_im, qs_ot_env%matrix_buf_nk_im, &
    3640         1876 :                      alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3641              : 
    3642              :       ! Preconditioner-aware projection onto the complex STRICT tangent space.
    3643         1876 :       CALL qs_ot_prepare_complex_tangent_metric(qs_ot_env)
    3644         1876 :       IF (ASSOCIATED(qs_ot_env%preconditioner)) THEN
    3645         1846 :          target_re => qs_ot_env%matrix_psc0
    3646         1846 :          target_im => qs_ot_env%matrix_psc0_im
    3647              :       ELSE
    3648           30 :          target_re => qs_ot_env%matrix_sc0
    3649           30 :          target_im => qs_ot_env%matrix_sc0_im
    3650              :       END IF
    3651              : 
    3652              :       CALL qs_ot_complex_multiply('C', 'N', target_re, target_im, &
    3653              :                                   qs_ot_env%matrix_gx, qs_ot_env%matrix_gx_im, &
    3654              :                                   qs_ot_env%matrix_buf1_ortho, qs_ot_env%matrix_buf1_ortho_im, &
    3655         1876 :                                   qs_ot_env%matrix_tmp_ortho)
    3656              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_os, qs_ot_env%matrix_os_im, &
    3657              :                                   qs_ot_env%matrix_buf1_ortho, qs_ot_env%matrix_buf1_ortho_im, &
    3658              :                                   qs_ot_env%matrix_buf2_ortho, qs_ot_env%matrix_buf2_ortho_im, &
    3659         1876 :                                   qs_ot_env%matrix_tmp_ortho)
    3660              :       CALL qs_ot_complex_multiply('N', 'N', qs_ot_env%matrix_sc0, qs_ot_env%matrix_sc0_im, &
    3661              :                                   qs_ot_env%matrix_buf2_ortho, qs_ot_env%matrix_buf2_ortho_im, &
    3662              :                                   qs_ot_env%matrix_buf_nk, qs_ot_env%matrix_buf_nk_im, &
    3663         1876 :                                   qs_ot_env%matrix_tmp_nk)
    3664              :       CALL dbcsr_add(qs_ot_env%matrix_gx, qs_ot_env%matrix_buf_nk, &
    3665         1876 :                      alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    3666              :       CALL dbcsr_add(qs_ot_env%matrix_gx_im, qs_ot_env%matrix_buf_nk_im, &
    3667         1876 :                      alpha_scalar=1.0_dp, beta_scalar=-1.0_dp)
    3668              : 
    3669         1876 :       IF (qs_ot_env%settings%do_rotation) THEN
    3670         1216 :          CALL dbcsr_release(hc_rot_re)
    3671         1216 :          CALL dbcsr_release(hc_rot_im)
    3672         1216 :          CALL dbcsr_release(tmp_nk)
    3673              :       END IF
    3674              : 
    3675         1876 :       CALL timestop(handle)
    3676              : 
    3677         1876 :    END SUBROUTINE qs_ot_get_derivative_complex
    3678              : 
    3679              : ! **************************************************************************************************
    3680              : !> \brief ...
    3681              : !> \param matrix_hc ...
    3682              : !> \param matrix_x ...
    3683              : !> \param matrix_sx ...
    3684              : !> \param matrix_gx ...
    3685              : !> \param qs_ot_env ...
    3686              : ! **************************************************************************************************
    3687       105480 :    SUBROUTINE qs_ot_get_derivative_diag(matrix_hc, matrix_x, matrix_sx, matrix_gx, &
    3688              :                                         qs_ot_env)
    3689              : 
    3690              :       TYPE(dbcsr_type), POINTER                          :: matrix_hc, matrix_x, matrix_sx, matrix_gx
    3691              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3692              : 
    3693              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_derivative_diag'
    3694              :       REAL(KIND=dp), PARAMETER                           :: rone = 1.0_dp, rzero = 0.0_dp
    3695              : 
    3696              :       INTEGER                                            :: handle, k, n
    3697              :       TYPE(dbcsr_distribution_type)                      :: dist
    3698              : 
    3699        35160 :       CALL timeset(routineN, handle)
    3700              : 
    3701        35160 :       CALL dbcsr_get_info(matrix_x, nfullrows_total=n, nfullcols_total=k)
    3702              : 
    3703              :       ! go for the derivative now
    3704              :       ! this de/dc*(dX/dx)*sinp
    3705        35160 :       CALL dbcsr_multiply('N', 'N', rone, matrix_hc, qs_ot_env%matrix_sinp, rzero, matrix_gx)
    3706              :       ! overlap hc*x
    3707        35160 :       CALL dbcsr_multiply('T', 'N', rone, matrix_hc, matrix_x, rzero, qs_ot_env%matrix_buf2)
    3708              :       ! get it in the basis of the eigenvectors
    3709              :       CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_buf2, qs_ot_env%matrix_r, &
    3710        35160 :                           rzero, qs_ot_env%matrix_buf1)
    3711              :       CALL dbcsr_multiply('T', 'N', rone, qs_ot_env%matrix_r, qs_ot_env%matrix_buf1, &
    3712        35160 :                           rzero, qs_ot_env%matrix_buf2)
    3713              : 
    3714              :       ! get the schur product of O_uv*B_uv
    3715              :       CALL dbcsr_hadamard_product(qs_ot_env%matrix_buf2, qs_ot_env%matrix_sinp_b, &
    3716        35160 :                                   qs_ot_env%matrix_buf3)
    3717              : 
    3718              :       ! overlap hc*c0
    3719              :       CALL dbcsr_multiply('T', 'N', rone, matrix_hc, qs_ot_env%matrix_c0, rzero, &
    3720        35160 :                           qs_ot_env%matrix_buf2)
    3721              :       ! get it in the basis of the eigenvectors
    3722              :       CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_buf2, qs_ot_env%matrix_r, &
    3723        35160 :                           rzero, qs_ot_env%matrix_buf1)
    3724              :       CALL dbcsr_multiply('T', 'N', rone, qs_ot_env%matrix_r, qs_ot_env%matrix_buf1, &
    3725        35160 :                           rzero, qs_ot_env%matrix_buf2)
    3726              : 
    3727              :       CALL dbcsr_hadamard_product(qs_ot_env%matrix_buf2, qs_ot_env%matrix_cosp_b, &
    3728        35160 :                                   qs_ot_env%matrix_buf4)
    3729              : 
    3730              :       ! add the two bs and compute b+b^T
    3731              :       CALL dbcsr_add(qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf4, &
    3732        35160 :                      alpha_scalar=rone, beta_scalar=rone)
    3733              : 
    3734              :       ! get the b in the eigenvector basis
    3735              :       CALL dbcsr_multiply('N', 'T', rone, qs_ot_env%matrix_buf3, qs_ot_env%matrix_r, &
    3736        35160 :                           rzero, qs_ot_env%matrix_buf1)
    3737              :       CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_r, qs_ot_env%matrix_buf1, &
    3738        35160 :                           rzero, qs_ot_env%matrix_buf3)
    3739        35160 :       CALL dbcsr_get_info(qs_ot_env%matrix_buf3, distribution=dist)
    3740              :       CALL dbcsr_transposed(qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf3, &
    3741              :                             shallow_data_copy=.FALSE., use_distribution=dist, &
    3742        35160 :                             transpose_distribution=.FALSE.)
    3743              :       CALL dbcsr_add(qs_ot_env%matrix_buf3, qs_ot_env%matrix_buf1, &
    3744        35160 :                      alpha_scalar=rone, beta_scalar=rone)
    3745              : 
    3746              :       ! and add to the derivative
    3747              :       CALL dbcsr_multiply('N', 'N', rone, matrix_sx, qs_ot_env%matrix_buf3, &
    3748        35160 :                           rone, matrix_gx)
    3749        35160 :       CALL timestop(handle)
    3750              : 
    3751        35160 :    END SUBROUTINE qs_ot_get_derivative_diag
    3752              : 
    3753              : ! **************************************************************************************************
    3754              : !> \brief compute the derivative of the taylor expansion below
    3755              : !> \param matrix_hc ...
    3756              : !> \param matrix_x ...
    3757              : !> \param matrix_sx ...
    3758              : !> \param matrix_gx ...
    3759              : !> \param qs_ot_env ...
    3760              : ! **************************************************************************************************
    3761       151924 :    SUBROUTINE qs_ot_get_derivative_taylor(matrix_hc, matrix_x, matrix_sx, matrix_gx, &
    3762              :                                           qs_ot_env)
    3763              : 
    3764              :       TYPE(dbcsr_type), POINTER                          :: matrix_hc, matrix_x, matrix_sx, matrix_gx
    3765              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3766              : 
    3767              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_get_derivative_taylor'
    3768              :       REAL(KIND=dp), PARAMETER                           :: rone = 1.0_dp, rzero = 0.0_dp
    3769              : 
    3770              :       INTEGER                                            :: handle, i, k, n
    3771              :       REAL(KIND=dp)                                      :: cosfactor, sinfactor
    3772              :       TYPE(dbcsr_distribution_type)                      :: dist
    3773              :       TYPE(dbcsr_type), POINTER                          :: matrix_left, matrix_right
    3774              : 
    3775        42613 :       CALL timeset(routineN, handle)
    3776              : 
    3777        42613 :       CALL dbcsr_get_info(matrix_x, nfullrows_total=n, nfullcols_total=k)
    3778              : 
    3779              :       ! go for the derivative now
    3780              :       ! this de/dc*(dX/dx)*sinp i.e. zeroth order
    3781        42613 :       CALL dbcsr_multiply('N', 'N', rone, matrix_hc, qs_ot_env%matrix_sinp, rzero, matrix_gx)
    3782              : 
    3783        42613 :       IF (qs_ot_env%taylor_order <= 0) THEN
    3784         9264 :          CALL timestop(handle)
    3785         9264 :          RETURN
    3786              :       END IF
    3787              : 
    3788              :       ! we store the matrix that will multiply sx in matrix_r
    3789        33349 :       CALL dbcsr_set(qs_ot_env%matrix_r, rzero)
    3790              : 
    3791              :       ! just better names for matrix_cosp_b and matrix_sinp_b (they are buffer space here)
    3792        33349 :       matrix_left => qs_ot_env%matrix_cosp_b
    3793        33349 :       matrix_right => qs_ot_env%matrix_sinp_b
    3794              : 
    3795              :       ! overlap hc*x and add its transpose to matrix_left
    3796        33349 :       CALL dbcsr_multiply('T', 'N', rone, matrix_hc, matrix_x, rzero, matrix_left)
    3797        33349 :       CALL dbcsr_get_info(matrix_left, distribution=dist)
    3798              :       CALL dbcsr_transposed(qs_ot_env%matrix_buf1, matrix_left, &
    3799              :                             shallow_data_copy=.FALSE., use_distribution=dist, &
    3800        33349 :                             transpose_distribution=.FALSE.)
    3801              :       CALL dbcsr_add(matrix_left, qs_ot_env%matrix_buf1, &
    3802        33349 :                      alpha_scalar=1.0_dp, beta_scalar=1.0_dp)
    3803        33349 :       CALL dbcsr_copy(matrix_right, matrix_left)
    3804              : 
    3805              :       ! first order
    3806        33349 :       sinfactor = -1.0_dp/(2.0_dp*3.0_dp)
    3807              :       CALL dbcsr_add(qs_ot_env%matrix_r, matrix_left, &
    3808        33349 :                      alpha_scalar=1.0_dp, beta_scalar=sinfactor)
    3809              : 
    3810              :       !      M
    3811              :       !    OM+MO
    3812              :       ! OOM+OMO+MOO
    3813              :       !   ...
    3814        71037 :       DO i = 2, qs_ot_env%taylor_order
    3815        37688 :          sinfactor = sinfactor*(-1.0_dp)/REAL(2*i*(2*i + 1), KIND=dp)
    3816        37688 :          CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_p, matrix_left, rzero, qs_ot_env%matrix_buf1)
    3817        37688 :          CALL dbcsr_multiply('N', 'N', rone, matrix_right, qs_ot_env%matrix_p, rzero, matrix_left)
    3818        37688 :          CALL dbcsr_copy(matrix_right, matrix_left)
    3819              :          CALL dbcsr_add(matrix_left, qs_ot_env%matrix_buf1, &
    3820        37688 :                         1.0_dp, 1.0_dp)
    3821              :          CALL dbcsr_add(qs_ot_env%matrix_r, matrix_left, &
    3822        71037 :                         alpha_scalar=1.0_dp, beta_scalar=sinfactor)
    3823              :       END DO
    3824              : 
    3825              :       ! overlap hc*c0 and add its transpose to matrix_left
    3826        33349 :       CALL dbcsr_multiply('T', 'N', rone, matrix_hc, qs_ot_env%matrix_c0, rzero, matrix_left)
    3827        33349 :       CALL dbcsr_get_info(matrix_left, distribution=dist)
    3828              :       CALL dbcsr_transposed(qs_ot_env%matrix_buf1, matrix_left, &
    3829              :                             shallow_data_copy=.FALSE., use_distribution=dist, &
    3830        33349 :                             transpose_distribution=.FALSE.)
    3831        33349 :       CALL dbcsr_add(matrix_left, qs_ot_env%matrix_buf1, 1.0_dp, 1.0_dp)
    3832        33349 :       CALL dbcsr_copy(matrix_right, matrix_left)
    3833              : 
    3834              :       ! first order
    3835        33349 :       cosfactor = -1.0_dp/(1.0_dp*2.0_dp)
    3836              :       CALL dbcsr_add(qs_ot_env%matrix_r, matrix_left, &
    3837        33349 :                      alpha_scalar=1.0_dp, beta_scalar=cosfactor)
    3838              : 
    3839              :       !      M
    3840              :       !    OM+MO
    3841              :       ! OOM+OMO+MOO
    3842              :       !   ...
    3843        71037 :       DO i = 2, qs_ot_env%taylor_order
    3844        37688 :          cosfactor = cosfactor*(-1.0_dp)/REAL(2*i*(2*i - 1), KIND=dp)
    3845        37688 :          CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_p, matrix_left, rzero, qs_ot_env%matrix_buf1)
    3846        37688 :          CALL dbcsr_multiply('N', 'N', rone, matrix_right, qs_ot_env%matrix_p, rzero, matrix_left)
    3847        37688 :          CALL dbcsr_copy(matrix_right, matrix_left)
    3848        37688 :          CALL dbcsr_add(matrix_left, qs_ot_env%matrix_buf1, 1.0_dp, 1.0_dp)
    3849              :          CALL dbcsr_add(qs_ot_env%matrix_r, matrix_left, &
    3850        71037 :                         alpha_scalar=1.0_dp, beta_scalar=cosfactor)
    3851              :       END DO
    3852              : 
    3853              :       ! and add to the derivative
    3854        33349 :       CALL dbcsr_multiply('N', 'N', rone, matrix_sx, qs_ot_env%matrix_r, rone, matrix_gx)
    3855              : 
    3856        33349 :       CALL timestop(handle)
    3857              : 
    3858        42613 :    END SUBROUTINE qs_ot_get_derivative_taylor
    3859              : 
    3860              : ! *************************************************************************************************
    3861              : !> \brief computes a taylor expansion.
    3862              : !> \param qs_ot_env ...
    3863              : ! **************************************************************************************************
    3864        93793 :    SUBROUTINE qs_ot_p2m_taylor(qs_ot_env)
    3865              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3866              : 
    3867              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'qs_ot_p2m_taylor'
    3868              :       REAL(KIND=dp), PARAMETER                           :: rone = 1.0_dp, rzero = 0.0_dp
    3869              : 
    3870              :       INTEGER                                            :: handle, i, k
    3871              :       REAL(KIND=dp)                                      :: cosfactor, sinfactor
    3872              : 
    3873        58064 :       CALL timeset(routineN, handle)
    3874              : 
    3875              :       ! zeroth order
    3876        58064 :       CALL dbcsr_set(qs_ot_env%matrix_cosp, rzero)
    3877        58064 :       CALL dbcsr_set(qs_ot_env%matrix_sinp, rzero)
    3878        58064 :       CALL dbcsr_add_on_diag(qs_ot_env%matrix_cosp, rone)
    3879        58064 :       CALL dbcsr_add_on_diag(qs_ot_env%matrix_sinp, rone)
    3880              : 
    3881        58064 :       IF (qs_ot_env%taylor_order <= 0) THEN
    3882        10008 :          CALL timestop(handle)
    3883        22335 :          RETURN
    3884              :       END IF
    3885              : 
    3886              :       ! first order
    3887        48056 :       cosfactor = -1.0_dp/(1.0_dp*2.0_dp)
    3888        48056 :       sinfactor = -1.0_dp/(2.0_dp*3.0_dp)
    3889        48056 :       CALL dbcsr_add(qs_ot_env%matrix_cosp, qs_ot_env%matrix_p, alpha_scalar=1.0_dp, beta_scalar=cosfactor)
    3890        48056 :       CALL dbcsr_add(qs_ot_env%matrix_sinp, qs_ot_env%matrix_p, alpha_scalar=1.0_dp, beta_scalar=sinfactor)
    3891        48056 :       IF (qs_ot_env%taylor_order <= 1) THEN
    3892        12327 :          CALL timestop(handle)
    3893        12327 :          RETURN
    3894              :       END IF
    3895              : 
    3896              :       ! other orders
    3897        35729 :       CALL dbcsr_get_info(qs_ot_env%matrix_p, nfullrows_total=k)
    3898        35729 :       CALL dbcsr_copy(qs_ot_env%matrix_r, qs_ot_env%matrix_p)
    3899              : 
    3900        90080 :       DO i = 2, qs_ot_env%taylor_order
    3901              :          ! new power of p
    3902              :          CALL dbcsr_multiply('N', 'N', rone, qs_ot_env%matrix_p, qs_ot_env%matrix_r, &
    3903        54351 :                              rzero, qs_ot_env%matrix_buf1)
    3904        54351 :          CALL dbcsr_copy(qs_ot_env%matrix_r, qs_ot_env%matrix_buf1)
    3905              :          ! add to the taylor expansion so far
    3906        54351 :          cosfactor = cosfactor*(-1.0_dp)/REAL(2*i*(2*i - 1), KIND=dp)
    3907        54351 :          sinfactor = sinfactor*(-1.0_dp)/REAL(2*i*(2*i + 1), KIND=dp)
    3908              :          CALL dbcsr_add(qs_ot_env%matrix_cosp, qs_ot_env%matrix_r, &
    3909        54351 :                         alpha_scalar=1.0_dp, beta_scalar=cosfactor)
    3910              :          CALL dbcsr_add(qs_ot_env%matrix_sinp, qs_ot_env%matrix_r, &
    3911        90080 :                         alpha_scalar=1.0_dp, beta_scalar=sinfactor)
    3912              :       END DO
    3913              : 
    3914        35729 :       CALL timestop(handle)
    3915              : 
    3916              :    END SUBROUTINE qs_ot_p2m_taylor
    3917              : 
    3918              : ! **************************************************************************************************
    3919              : !> \brief given p, computes  - eigenstuff (matrix_r,evals)
    3920              : !>        - cos(p^0.5),p^(-0.5)*sin(p^0.5)
    3921              : !>        - the real b matrices, needed for the derivatives of these guys
    3922              : !>        cosp_b_ij=(1/(2pii) * int(cos(z^1/2)/((z-eval(i))*(z-eval(j))))
    3923              : !>        sinp_b_ij=(1/(2pii) * int(z^(-1/2)*sin(z^1/2)/((z-eval(i))*(z-eval(j))))
    3924              : !> \param qs_ot_env ...
    3925              : ! **************************************************************************************************
    3926       206788 :    SUBROUTINE qs_ot_p2m_diag(qs_ot_env)
    3927              : 
    3928              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    3929              : 
    3930              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'qs_ot_p2m_diag'
    3931              :       REAL(KIND=dp), PARAMETER                           :: rone = 1.0_dp, rzero = 0.0_dp
    3932              : 
    3933              :       INTEGER                                            :: col, col_offset, col_size, handle, i, j, &
    3934              :                                                             k, row, row_offset, row_size
    3935        51697 :       REAL(dp), DIMENSION(:, :), POINTER                 :: block
    3936              :       REAL(KIND=dp)                                      :: a, b
    3937              :       TYPE(dbcsr_iterator_type)                          :: iter
    3938              : 
    3939        51697 :       CALL timeset(routineN, handle)
    3940              : 
    3941        51697 :       CALL dbcsr_get_info(qs_ot_env%matrix_p, nfullrows_total=k)
    3942        51697 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1, qs_ot_env%matrix_p)
    3943              :       CALL cp_dbcsr_syevd(qs_ot_env%matrix_buf1, qs_ot_env%matrix_r, qs_ot_env%evals, &
    3944        51697 :                           qs_ot_env%para_env, qs_ot_env%blacs_env)
    3945       536412 :       DO i = 1, k
    3946       536412 :          qs_ot_env%evals(i) = MAX(0.0_dp, qs_ot_env%evals(i))
    3947              :       END DO
    3948              : 
    3949        51697 :       !$OMP PARALLEL DO DEFAULT(NONE) PRIVATE(i) SHARED(k,qs_ot_env)
    3950              :       DO i = 1, k
    3951              :          qs_ot_env%dum(i) = COS(SQRT(qs_ot_env%evals(i)))
    3952              :       END DO
    3953        51697 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1, qs_ot_env%matrix_r)
    3954        51697 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1, alpha=qs_ot_env%dum, side='right')
    3955              :       CALL dbcsr_multiply('N', 'T', rone, qs_ot_env%matrix_r, qs_ot_env%matrix_buf1, &
    3956        51697 :                           rzero, qs_ot_env%matrix_cosp)
    3957              : 
    3958        51697 :       !$OMP PARALLEL DO DEFAULT(NONE) PRIVATE(i) SHARED(k,qs_ot_env)
    3959              :       DO i = 1, k
    3960              :          qs_ot_env%dum(i) = qs_ot_sinc(SQRT(qs_ot_env%evals(i)))
    3961              :       END DO
    3962        51697 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1, qs_ot_env%matrix_r)
    3963        51697 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1, alpha=qs_ot_env%dum, side='right')
    3964              :       CALL dbcsr_multiply('N', 'T', rone, qs_ot_env%matrix_r, qs_ot_env%matrix_buf1, &
    3965        51697 :                           rzero, qs_ot_env%matrix_sinp)
    3966              : 
    3967        51697 :       CALL dbcsr_copy(qs_ot_env%matrix_cosp_b, qs_ot_env%matrix_cosp)
    3968        51697 :       CALL dbcsr_iterator_start(iter, qs_ot_env%matrix_cosp_b)
    3969        88219 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    3970              :          CALL dbcsr_iterator_next_block(iter, row, col, block, &
    3971              :                                         row_size=row_size, col_size=col_size, &
    3972        36522 :                                         row_offset=row_offset, col_offset=col_offset)
    3973       552641 :          DO j = 1, col_size
    3974     10706919 :          DO i = 1, row_size
    3975              :             a = (SQRT(qs_ot_env%evals(row_offset + i - 1)) &
    3976     10205975 :                  - SQRT(qs_ot_env%evals(col_offset + j - 1)))/2.0_dp
    3977              :             b = (SQRT(qs_ot_env%evals(row_offset + i - 1)) &
    3978     10205975 :                  + SQRT(qs_ot_env%evals(col_offset + j - 1)))/2.0_dp
    3979     10670397 :             block(i, j) = -0.5_dp*qs_ot_sinc(a)*qs_ot_sinc(b)
    3980              :          END DO
    3981              :          END DO
    3982              :       END DO
    3983        51697 :       CALL dbcsr_iterator_stop(iter)
    3984              : 
    3985        51697 :       CALL dbcsr_copy(qs_ot_env%matrix_sinp_b, qs_ot_env%matrix_sinp)
    3986        51697 :       CALL dbcsr_iterator_start(iter, qs_ot_env%matrix_sinp_b)
    3987        88219 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    3988              :          CALL dbcsr_iterator_next_block(iter, row, col, block, &
    3989              :                                         row_size=row_size, col_size=col_size, &
    3990        36522 :                                         row_offset=row_offset, col_offset=col_offset)
    3991       552641 :          DO j = 1, col_size
    3992     10706919 :          DO i = 1, row_size
    3993     10205975 :             a = SQRT(qs_ot_env%evals(row_offset + i - 1))
    3994     10205975 :             b = SQRT(qs_ot_env%evals(col_offset + j - 1))
    3995     10670397 :             block(i, j) = qs_ot_sincf(a, b)
    3996              :          END DO
    3997              :          END DO
    3998              :       END DO
    3999        51697 :       CALL dbcsr_iterator_stop(iter)
    4000              : 
    4001        51697 :       CALL timestop(handle)
    4002              : 
    4003        51697 :    END SUBROUTINE qs_ot_p2m_diag
    4004              : 
    4005              : ! **************************************************************************************************
    4006              : !> \brief diagonalize Hermitian P and build cos(sqrt(P)), sinc(sqrt(P)), and Frechet kernels
    4007              : !> \param qs_ot_env complex STRICT channel state
    4008              : ! **************************************************************************************************
    4009         8032 :    SUBROUTINE qs_ot_p2m_diag_complex(qs_ot_env)
    4010              :       TYPE(qs_ot_type)                                   :: qs_ot_env
    4011              : 
    4012              :       CHARACTER(len=*), PARAMETER :: routineN = 'qs_ot_p2m_diag_complex'
    4013              : 
    4014              :       INTEGER                                            :: col, col_offset, col_size, handle, i, j, &
    4015              :                                                             k, row, row_offset, row_size
    4016         2008 :       REAL(dp), DIMENSION(:, :), POINTER                 :: block
    4017              :       REAL(KIND=dp)                                      :: a, b
    4018              :       TYPE(dbcsr_iterator_type)                          :: iter
    4019              : 
    4020         2008 :       CALL timeset(routineN, handle)
    4021              : 
    4022         2008 :       CALL dbcsr_get_info(qs_ot_env%matrix_p, nfullrows_total=k)
    4023              :       CALL cp_dbcsr_heevd(matrix_re=qs_ot_env%matrix_p, matrix_im=qs_ot_env%matrix_p_im, &
    4024              :                           eigenvectors_re=qs_ot_env%matrix_r, eigenvectors_im=qs_ot_env%matrix_r_im, &
    4025              :                           eigenvalues=qs_ot_env%evals, para_env=qs_ot_env%para_env, &
    4026         2008 :                           blacs_env=qs_ot_env%blacs_env)
    4027        15554 :       DO i = 1, k
    4028        15554 :          qs_ot_env%evals(i) = MAX(0.0_dp, qs_ot_env%evals(i))
    4029              :       END DO
    4030              : 
    4031        15554 :       DO i = 1, k
    4032        15554 :          qs_ot_env%dum(i) = COS(SQRT(qs_ot_env%evals(i)))
    4033              :       END DO
    4034         2008 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1, qs_ot_env%matrix_r)
    4035         2008 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1_im, qs_ot_env%matrix_r_im)
    4036         2008 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1, alpha=qs_ot_env%dum, side='right')
    4037         2008 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1_im, alpha=qs_ot_env%dum, side='right')
    4038              :       CALL qs_ot_complex_multiply('N', 'C', qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    4039              :                                   qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    4040              :                                   qs_ot_env%matrix_cosp, qs_ot_env%matrix_cosp_im, &
    4041         2008 :                                   qs_ot_env%matrix_buf2)
    4042              : 
    4043        15554 :       DO i = 1, k
    4044        15554 :          qs_ot_env%dum(i) = qs_ot_sinc(SQRT(qs_ot_env%evals(i)))
    4045              :       END DO
    4046         2008 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1, qs_ot_env%matrix_r)
    4047         2008 :       CALL dbcsr_copy(qs_ot_env%matrix_buf1_im, qs_ot_env%matrix_r_im)
    4048         2008 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1, alpha=qs_ot_env%dum, side='right')
    4049         2008 :       CALL dbcsr_scale_by_vector(qs_ot_env%matrix_buf1_im, alpha=qs_ot_env%dum, side='right')
    4050              :       CALL qs_ot_complex_multiply('N', 'C', qs_ot_env%matrix_buf1, qs_ot_env%matrix_buf1_im, &
    4051              :                                   qs_ot_env%matrix_r, qs_ot_env%matrix_r_im, &
    4052              :                                   qs_ot_env%matrix_sinp, qs_ot_env%matrix_sinp_im, &
    4053         2008 :                                   qs_ot_env%matrix_buf2)
    4054              : 
    4055         2008 :       CALL dbcsr_copy(qs_ot_env%matrix_cosp_b, qs_ot_env%matrix_cosp)
    4056         2008 :       CALL dbcsr_iterator_start(iter, qs_ot_env%matrix_cosp_b)
    4057         3091 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    4058              :          CALL dbcsr_iterator_next_block(iter, row, col, block, &
    4059              :                                         row_size=row_size, col_size=col_size, &
    4060         1083 :                                         row_offset=row_offset, col_offset=col_offset)
    4061        11582 :          DO j = 1, col_size
    4062       124853 :          DO i = 1, row_size
    4063              :             a = (SQRT(qs_ot_env%evals(row_offset + i - 1)) &
    4064       115279 :                  - SQRT(qs_ot_env%evals(col_offset + j - 1)))/2.0_dp
    4065              :             b = (SQRT(qs_ot_env%evals(row_offset + i - 1)) &
    4066       115279 :                  + SQRT(qs_ot_env%evals(col_offset + j - 1)))/2.0_dp
    4067       123770 :             block(i, j) = -0.5_dp*qs_ot_sinc(a)*qs_ot_sinc(b)
    4068              :          END DO
    4069              :          END DO
    4070              :       END DO
    4071         2008 :       CALL dbcsr_iterator_stop(iter)
    4072              : 
    4073         2008 :       CALL dbcsr_copy(qs_ot_env%matrix_sinp_b, qs_ot_env%matrix_sinp)
    4074         2008 :       CALL dbcsr_iterator_start(iter, qs_ot_env%matrix_sinp_b)
    4075         3091 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    4076              :          CALL dbcsr_iterator_next_block(iter, row, col, block, &
    4077              :                                         row_size=row_size, col_size=col_size, &
    4078         1083 :                                         row_offset=row_offset, col_offset=col_offset)
    4079        11582 :          DO j = 1, col_size
    4080       124853 :          DO i = 1, row_size
    4081       115279 :             a = SQRT(qs_ot_env%evals(row_offset + i - 1))
    4082       115279 :             b = SQRT(qs_ot_env%evals(col_offset + j - 1))
    4083       123770 :             block(i, j) = qs_ot_sincf(a, b)
    4084              :          END DO
    4085              :          END DO
    4086              :       END DO
    4087         2008 :       CALL dbcsr_iterator_stop(iter)
    4088              : 
    4089         2008 :       CALL timestop(handle)
    4090              : 
    4091         2008 :    END SUBROUTINE qs_ot_p2m_diag_complex
    4092              : 
    4093              : ! **************************************************************************************************
    4094              : !> \brief computes sin(x)/x for all values of the argument
    4095              : !> \param x ...
    4096              : !> \return ...
    4097              : ! **************************************************************************************************
    4098     28880599 :    FUNCTION qs_ot_sinc(x)
    4099              : 
    4100              :       REAL(KIND=dp), INTENT(IN)                          :: x
    4101              :       REAL(KIND=dp)                                      :: qs_ot_sinc
    4102              : 
    4103              :       REAL(KIND=dp), PARAMETER :: q1 = 1.0_dp, q2 = -q1/(2.0_dp*3.0_dp), q3 = -q2/(4.0_dp*5.0_dp), &
    4104              :          q4 = -q3/(6.0_dp*7.0_dp), q5 = -q4/(8.0_dp*9.0_dp), q6 = -q5/(10.0_dp*11.0_dp), &
    4105              :          q7 = -q6/(12.0_dp*13.0_dp), q8 = -q7/(14.0_dp*15.0_dp), q9 = -q8/(16.0_dp*17.0_dp), &
    4106              :          q10 = -q9/(18.0_dp*19.0_dp)
    4107              : 
    4108              :       REAL(KIND=dp)                                      :: y
    4109              : 
    4110     28880599 :       IF (ABS(x) > 0.5_dp) THEN
    4111      8707781 :          qs_ot_sinc = SIN(x)/x
    4112              :       ELSE
    4113     20172818 :          y = x*x
    4114     20172818 :          qs_ot_sinc = q1 + y*(q2 + y*(q3 + y*(q4 + y*(q5 + y*(q6 + y*(q7 + y*(q8 + y*(q9 + y*(q10)))))))))
    4115              :       END IF
    4116     28880599 :    END FUNCTION qs_ot_sinc
    4117              : 
    4118              : ! **************************************************************************************************
    4119              : !> \brief computes (1/(x^2-y^2))*(sinc(x)-sinc(y)) for all positive values of the arguments
    4120              : !> \param xa ...
    4121              : !> \param ya ...
    4122              : !> \return ...
    4123              : ! **************************************************************************************************
    4124     10321254 :    FUNCTION qs_ot_sincf(xa, ya)
    4125              : 
    4126              :       REAL(KIND=dp), INTENT(IN)                          :: xa, ya
    4127              :       REAL(KIND=dp)                                      :: qs_ot_sincf
    4128              : 
    4129              :       INTEGER                                            :: i
    4130              :       REAL(KIND=dp)                                      :: a, b, rs, sf, x, xs, y, ybx, ybxs
    4131              : 
    4132              :       ! this is currently a limit of the routine, could be removed rather easily
    4133     10321254 :       IF (xa < 0) CPABORT("x is negative")
    4134     10321254 :       IF (ya < 0) CPABORT("y is negative")
    4135              : 
    4136     10321254 :       IF (xa < ya) THEN
    4137      4935528 :          x = ya
    4138      4935528 :          y = xa
    4139              :       ELSE
    4140      5385726 :          x = xa
    4141      5385726 :          y = ya
    4142              :       END IF
    4143              : 
    4144     10321254 :       IF (x < 0.5_dp) THEN ! use series, keeping in mind that x,y,x+y,x-y can all be zero
    4145              : 
    4146      6451339 :          qs_ot_sincf = 0.0_dp
    4147      6451339 :          IF (x > 0.0_dp) THEN
    4148      6234744 :             ybx = y/x
    4149              :          ELSE ! should be irrelevant  !?
    4150              :             ybx = 0.0_dp
    4151              :          END IF
    4152              : 
    4153      6451339 :          sf = -1.0_dp/((1.0_dp + ybx)*6.0_dp)
    4154      6451339 :          rs = 1.0_dp
    4155      6451339 :          ybxs = ybx
    4156      6451339 :          xs = 1.0_dp
    4157              : 
    4158     70964729 :          DO i = 1, 10
    4159     64513390 :             qs_ot_sincf = qs_ot_sincf + sf*rs*xs*(1.0_dp + ybxs)
    4160     64513390 :             sf = -sf/(REAL((2*i + 2), dp)*REAL((2*i + 3), dp))
    4161     64513390 :             rs = rs + ybxs
    4162     64513390 :             ybxs = ybxs*ybx
    4163     70964729 :             xs = xs*x*x
    4164              :          END DO
    4165              : 
    4166              :       ELSE ! no series expansion
    4167      3869915 :          IF (x - y > 0.1_dp) THEN ! safe to use the normal form
    4168      3582932 :             qs_ot_sincf = (qs_ot_sinc(x) - qs_ot_sinc(y))/((x + y)*(x - y))
    4169              :          ELSE
    4170       286983 :             a = (x + y)/2.0_dp
    4171       286983 :             b = (x - y)/2.0_dp ! might be close to zero
    4172              :             ! y (=(a-b)) can not be close to zero since it is close to x>0.5
    4173       286983 :             qs_ot_sincf = (qs_ot_sinc(b)*COS(a) - qs_ot_sinc(a)*COS(b))/(2*x*y)
    4174              :          END IF
    4175              :       END IF
    4176              : 
    4177     10321254 :    END FUNCTION qs_ot_sincf
    4178              : 
    4179        68062 : END MODULE qs_ot
        

Generated by: LCOV version 2.0-1