LCOV - code coverage report
Current view: top level - src - qs_ot_complex_ref_unittest.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:2c0d679) Lines: 98.4 % 1834 1805
Test Date: 2026-09-25 00:58:37 Functions: 100.0 % 44 44

            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 Regressions for complex k-point OT derivatives and preconditioner guards.
      10              : ! **************************************************************************************************
      11          320 : PROGRAM qs_ot_complex_ref_unittest
      12            2 :    USE bibliography,                    ONLY: add_all_references
      13              :    USE cp_blacs_env,                    ONLY: cp_blacs_env_create,&
      14              :                                               cp_blacs_env_release,&
      15              :                                               cp_blacs_env_type
      16              :    USE cp_cfm_types,                    ONLY: cp_cfm_create,&
      17              :                                               cp_cfm_get_element,&
      18              :                                               cp_cfm_release,&
      19              :                                               cp_cfm_set_all,&
      20              :                                               cp_cfm_set_element,&
      21              :                                               cp_cfm_type
      22              :    USE cp_dbcsr_api,                    ONLY: &
      23              :         dbcsr_create, dbcsr_distribution_new, dbcsr_distribution_release, dbcsr_distribution_type, &
      24              :         dbcsr_finalize, dbcsr_finalize_lib, dbcsr_get_block_p, dbcsr_init_lib, dbcsr_put_block, &
      25              :         dbcsr_release, dbcsr_reserve_blocks, dbcsr_type, dbcsr_type_no_symmetry
      26              :    USE cp_fm_basic_linalg,              ONLY: cp_complex_fm_gemm
      27              :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      28              :                                               cp_fm_struct_release,&
      29              :                                               cp_fm_struct_type
      30              :    USE cp_fm_types,                     ONLY: cp_fm_create,&
      31              :                                               cp_fm_get_submatrix,&
      32              :                                               cp_fm_release,&
      33              :                                               cp_fm_set_submatrix,&
      34              :                                               cp_fm_type
      35              :    USE cp_log_handling,                 ONLY: cp_add_default_logger,&
      36              :                                               cp_logger_create,&
      37              :                                               cp_logger_release,&
      38              :                                               cp_logger_type,&
      39              :                                               cp_rm_default_logger
      40              :    USE input_constants,                 ONLY: &
      41              :         ot_precond_fermi_low_rank, ot_precond_full_all, ot_precond_full_all_covariant, &
      42              :         ot_precond_full_kinetic, ot_precond_full_single, ot_precond_full_single_inverse, &
      43              :         ot_precond_none, ot_precond_s_inverse, ot_precond_solver_chebyshev, &
      44              :         ot_precond_solver_default, ot_precond_solver_direct, ot_precond_solver_inv_chol
      45              :    USE kinds,                           ONLY: dp
      46              :    USE kpoint_methods,                  ONLY: kpoint_ot_energy_weighted_density
      47              :    USE machine,                         ONLY: default_output_unit
      48              :    USE mathlib,                         ONLY: diag_complex
      49              :    USE message_passing,                 ONLY: mp_comm_self,&
      50              :                                               mp_comm_type,&
      51              :                                               mp_para_env_release,&
      52              :                                               mp_para_env_type,&
      53              :                                               mp_world_finalize,&
      54              :                                               mp_world_init
      55              :    USE preconditioner,                  ONLY: apply_preconditioner
      56              :    USE preconditioner_makes,            ONLY: make_complex_fermi_low_rank,&
      57              :                                               make_complex_full_all,&
      58              :                                               make_complex_full_all_covariant,&
      59              :                                               make_complex_full_kinetic,&
      60              :                                               make_complex_full_s_inverse,&
      61              :                                               make_complex_full_single,&
      62              :                                               make_complex_full_single_inverse
      63              :    USE preconditioner_types,            ONLY: destroy_preconditioner,&
      64              :                                               init_preconditioner,&
      65              :                                               preconditioner_type
      66              :    USE qs_ot,                           ONLY: &
      67              :         qs_ot_antihermitian_spectral_norm, qs_ot_apply_complex_frechet_dbcsr, &
      68              :         qs_ot_complex_exp_frechet_kernel, qs_ot_density_secant_hessian, &
      69              :         qs_ot_density_secant_orbital_overlaps, qs_ot_density_secant_projected_hessian, &
      70              :         qs_ot_density_tangent, qs_ot_finite_rotation_response, qs_ot_fixed_n_energy_gradient, &
      71              :         qs_ot_fixed_n_energy_hessian, qs_ot_fixed_n_multigroup_schur_block, &
      72              :         qs_ot_fixed_n_projector_frechet, qs_ot_fixed_n_schur_block, qs_ot_generate_rotation, &
      73              :         qs_ot_get_derivative_complex, qs_ot_get_derivative_ref_complex, &
      74              :         qs_ot_get_orbitals_ref_complex, qs_ot_get_p_complex, qs_ot_projected_response_update, &
      75              :         qs_ot_rot_mat_derivative, qs_ot_symmetric_abs_solve, qs_ot_symmetric_sr1_update
      76              :    USE qs_ot_types,                     ONLY: qs_ot_kpoint_preconditioner_scale,&
      77              :                                               qs_ot_kpoint_preconditioner_solver_supported,&
      78              :                                               qs_ot_kpoint_preconditioner_supported,&
      79              :                                               qs_ot_type
      80              :    USE reference_manager,               ONLY: remove_all_references
      81              : 
      82              :    IMPLICIT NONE
      83              : 
      84              :    INTEGER, PARAMETER                                 :: k = 2, n = 3
      85              : 
      86              :    COMPLEX(KIND=dp), DIMENSION(n, k)                  :: direction, gradient, gradient_old, hc, q, x
      87              :    COMPLEX(KIND=dp), DIMENSION(n, n)                  :: hamiltonian
      88              :    COMPLEX(KIND=dp), DIMENSION(k, k)                  :: b, inverse_sqrt
      89              :    INTEGER                                            :: io_unit, mynode, nfail
      90            4 :    INTEGER, DIMENSION(:), POINTER                     :: col_dist, col_k, row_dist, row_k, row_n
      91            2 :    INTEGER, DIMENSION(:, :), POINTER                  :: pgrid
      92              :    LOGICAL                                            :: found
      93              :    REAL(KIND=dp)                                      :: error, fd_slope, hstep, old_error, slope
      94            2 :    REAL(KIND=dp), DIMENSION(:, :), POINTER            :: block_p
      95              :    TYPE(cp_logger_type), POINTER                      :: logger
      96              :    TYPE(dbcsr_distribution_type)                     :: dist_kk, dist_nk
      97              :    TYPE(dbcsr_type), TARGET                           :: b_im, b_re, f_im, f_re, g_im, g_re, &
      98              :                                                          hc_im, hc_re, q_im, q_re, sx_im, sx_re, &
      99              :                                                          tmp_kk, x_im, x_re
     100              :    TYPE(dbcsr_type), POINTER                          :: hc_im_p, hc_re_p
     101              :    TYPE(mp_comm_type)                                 :: mp_comm
     102              :    TYPE(mp_para_env_type), POINTER                    :: para_env
     103          324 :    TYPE(qs_ot_type)                                   :: ot_env
     104              : 
     105            2 :    nfail = 0
     106            2 :    CALL test_preconditioner_support(nfail)
     107            2 :    NULLIFY (hc_im_p, hc_re_p, logger, para_env)
     108            2 :    CALL mp_world_init(mp_comm)
     109            2 :    mynode = mp_comm%mepos
     110            2 :    io_unit = -1
     111            2 :    IF (mynode == 0) io_unit = default_output_unit
     112            2 :    CALL test_antihermitian_spectral_norm(io_unit, nfail)
     113            2 :    CALL test_fixed_n_mermin_energy(mynode, nfail)
     114            2 :    CALL test_fixed_n_projector_frechet(mynode, nfail)
     115            2 :    CALL test_finite_rotation_response(mynode, nfail)
     116            2 :    CALL test_fixed_n_rotation_schur(mynode, nfail)
     117            2 :    CALL test_fixed_n_multigroup_schur(mynode, nfail)
     118            2 :    CALL test_roks_fixed_n_rotation_schur(mynode, nfail)
     119            2 :    CALL test_symmetric_abs_solve(mynode, nfail)
     120            2 :    CALL test_projected_response_update(mynode, nfail)
     121            2 :    CALL test_symmetric_sr1_update(mynode, nfail)
     122            2 :    CALL test_density_secant_hessian(mynode, nfail)
     123            2 :    CALL test_joint_spin_density_secant(mynode, nfail)
     124            2 :    CALL test_density_tangent(mynode, nfail)
     125            2 :    CALL test_density_secant_moving_subspace(mynode, nfail)
     126            2 :    ALLOCATE (para_env)
     127            2 :    CALL para_env%from_dup(mp_comm)
     128              :    CALL cp_logger_create(logger, para_env=para_env, default_global_unit_nr=io_unit, &
     129            2 :                          close_global_unit_on_dealloc=.FALSE.)
     130            2 :    CALL cp_add_default_logger(logger)
     131            2 :    CALL add_all_references()
     132            2 :    CALL dbcsr_init_lib(mp_comm%get_handle(), io_unit)
     133            2 :    CALL test_real_rotation_frechet(para_env, nfail)
     134            2 :    CALL test_complex_rotation_frechet(para_env, nfail)
     135            2 :    CALL test_sparse_frechet_patterns(para_env, nfail)
     136            2 :    CALL test_complex_ref_rotation(para_env, nfail)
     137            2 :    CALL test_split_complex_fm_gemm(para_env, io_unit, nfail)
     138            2 :    CALL test_complex_preconditioner_gauge(para_env, nfail)
     139            2 :    CALL test_complex_occupation_block_preconditioner(para_env, nfail)
     140            2 :    CALL test_kpoint_ot_energy_weighted_density(para_env, io_unit, nfail)
     141              : 
     142              :    x(:, 1) = [CMPLX(1.10_dp, 0.10_dp, KIND=dp), CMPLX(0.20_dp, -0.30_dp, KIND=dp), &
     143            8 :               CMPLX(-0.10_dp, 0.20_dp, KIND=dp)]
     144              :    x(:, 2) = [CMPLX(0.30_dp, 0.40_dp, KIND=dp), CMPLX(0.90_dp, -0.20_dp, KIND=dp), &
     145            8 :               CMPLX(0.25_dp, 0.10_dp, KIND=dp)]
     146              :    direction(:, 1) = [CMPLX(0.17_dp, -0.09_dp, KIND=dp), CMPLX(-0.21_dp, 0.13_dp, KIND=dp), &
     147            8 :                       CMPLX(0.08_dp, 0.19_dp, KIND=dp)]
     148              :    direction(:, 2) = [CMPLX(-0.12_dp, 0.23_dp, KIND=dp), CMPLX(0.16_dp, 0.07_dp, KIND=dp), &
     149            8 :                       CMPLX(-0.14_dp, -0.11_dp, KIND=dp)]
     150              : 
     151              :    hamiltonian(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     152            2 :    hamiltonian(1, 1) = CMPLX(0.70_dp, 0.0_dp, KIND=dp)
     153            2 :    hamiltonian(2, 2) = CMPLX(-0.40_dp, 0.0_dp, KIND=dp)
     154            2 :    hamiltonian(3, 3) = CMPLX(1.10_dp, 0.0_dp, KIND=dp)
     155            2 :    hamiltonian(1, 2) = CMPLX(0.20_dp, 0.10_dp, KIND=dp)
     156            2 :    hamiltonian(2, 1) = CONJG(hamiltonian(1, 2))
     157            2 :    hamiltonian(1, 3) = CMPLX(-0.10_dp, 0.05_dp, KIND=dp)
     158            2 :    hamiltonian(3, 1) = CONJG(hamiltonian(1, 3))
     159            2 :    hamiltonian(2, 3) = CMPLX(0.15_dp, 0.20_dp, KIND=dp)
     160            2 :    hamiltonian(3, 2) = CONJG(hamiltonian(2, 3))
     161              : 
     162           38 :    CALL dense_inverse_sqrt(MATMUL(CONJG(TRANSPOSE(x)), x), inverse_sqrt)
     163           38 :    q = MATMUL(x, inverse_sqrt)
     164           70 :    hc = 2.0_dp*MATMUL(hamiltonian, q)
     165              : 
     166            2 :    ALLOCATE (pgrid(0:0, 0:0), row_dist(1), col_dist(1), row_n(1), row_k(1), col_k(1))
     167            6 :    pgrid(:, :) = 0
     168            4 :    row_dist(:) = 0
     169            4 :    col_dist(:) = 0
     170            4 :    row_n(:) = n
     171            4 :    row_k(:) = k
     172            4 :    col_k(:) = k
     173              :    CALL dbcsr_distribution_new(dist_nk, group=mp_comm_self%get_handle(), pgrid=pgrid, &
     174            2 :                                row_dist=row_dist, col_dist=col_dist)
     175              :    CALL dbcsr_distribution_new(dist_kk, group=mp_comm_self%get_handle(), pgrid=pgrid, &
     176            2 :                                row_dist=row_dist, col_dist=col_dist)
     177            2 :    CALL test_complex_strict_derivative(dist_nk, dist_kk, row_n, row_k, col_k, para_env, nfail)
     178              : 
     179            2 :    CALL create_one_block(x_re, "complex_ref_x_re", dist_nk, row_n, col_k)
     180            2 :    CALL create_one_block(x_im, "complex_ref_x_im", dist_nk, row_n, col_k)
     181            2 :    CALL create_one_block(sx_re, "complex_ref_sx_re", dist_nk, row_n, col_k)
     182            2 :    CALL create_one_block(sx_im, "complex_ref_sx_im", dist_nk, row_n, col_k)
     183            2 :    CALL create_one_block(hc_re, "complex_ref_hc_re", dist_nk, row_n, col_k)
     184            2 :    CALL create_one_block(hc_im, "complex_ref_hc_im", dist_nk, row_n, col_k)
     185            2 :    CALL create_one_block(g_re, "complex_ref_g_re", dist_nk, row_n, col_k)
     186            2 :    CALL create_one_block(g_im, "complex_ref_g_im", dist_nk, row_n, col_k)
     187            2 :    CALL create_one_block(q_re, "complex_ref_q_re", dist_nk, row_n, col_k)
     188            2 :    CALL create_one_block(q_im, "complex_ref_q_im", dist_nk, row_n, col_k)
     189            2 :    CALL create_one_block(f_re, "complex_ref_f_re", dist_kk, row_k, col_k)
     190            2 :    CALL create_one_block(f_im, "complex_ref_f_im", dist_kk, row_k, col_k)
     191            2 :    CALL create_one_block(b_re, "complex_ref_b_re", dist_kk, row_k, col_k)
     192            2 :    CALL create_one_block(b_im, "complex_ref_b_im", dist_kk, row_k, col_k)
     193            2 :    CALL create_one_block(tmp_kk, "complex_ref_tmp_kk", dist_kk, row_k, col_k)
     194              : 
     195            2 :    CALL put_complex_pair(x_re, x_im, x)
     196            2 :    CALL put_complex_pair(sx_re, sx_im, x)
     197            2 :    CALL put_complex_pair(hc_re, hc_im, hc)
     198            2 :    CALL put_complex_pair(f_re, f_im, inverse_sqrt)
     199              : 
     200            2 :    ot_env%has_complex_kpoint_state = .TRUE.
     201            2 :    ot_env%settings%eps_irac_filter_matrix = 0.0_dp
     202            2 :    ot_env%matrix_x => x_re
     203            2 :    ot_env%matrix_x_im => x_im
     204            2 :    ot_env%matrix_sx => sx_re
     205            2 :    ot_env%matrix_sx_im => sx_im
     206            2 :    ot_env%matrix_gx => g_re
     207            2 :    ot_env%matrix_gx_im => g_im
     208            2 :    ot_env%matrix_ref_inv_sqrt => f_re
     209            2 :    ot_env%matrix_ref_inv_sqrt_im => f_im
     210            2 :    ot_env%buf1_k_k_sym => b_re
     211            2 :    ot_env%buf2_k_k_sym => b_im
     212            2 :    ot_env%buf3_k_k_sym => tmp_kk
     213            2 :    ot_env%buf1_n_k => q_re
     214            2 :    ot_env%buf1_n_k_dp => q_im
     215              : 
     216            2 :    hc_re_p => hc_re
     217            2 :    hc_im_p => hc_im
     218            2 :    CALL qs_ot_get_derivative_ref_complex(hc_re_p, hc_im_p, ot_env)
     219              : 
     220            2 :    gradient(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     221            2 :    CALL dbcsr_get_block_p(g_re, 1, 1, block_p, found)
     222           18 :    IF (found) gradient = gradient + CMPLX(block_p, 0.0_dp, KIND=dp)
     223            2 :    CALL dbcsr_get_block_p(g_im, 1, 1, block_p, found)
     224           18 :    IF (found) gradient = gradient + CMPLX(0.0_dp, block_p, KIND=dp)
     225              : 
     226            2 :    hstep = 1.0E-6_dp
     227              :    fd_slope = (polar_energy(x + hstep*direction, hamiltonian) - &
     228           34 :                polar_energy(x - hstep*direction, hamiltonian))/(2.0_dp*hstep)
     229           18 :    slope = REAL(SUM(CONJG(gradient)*direction), KIND=dp)
     230            2 :    error = ABS(slope - fd_slope)
     231              : 
     232           38 :    b = MATMUL(CONJG(TRANSPOSE(x)), hc)
     233           54 :    gradient_old = hc - MATMUL(x, b)
     234           18 :    old_error = ABS(REAL(SUM(CONJG(gradient_old)*direction), KIND=dp) - fd_slope)
     235            2 :    IF (error > 5.0E-8_dp .OR. old_error < 1.0E-3_dp) nfail = nfail + 1
     236              : 
     237            2 :    IF (mynode == 0) THEN
     238            1 :       WRITE (io_unit, '(A,3(1X,ES13.6))') "complex finite-REF pullback: fd/predicted/error", &
     239            2 :          fd_slope, slope, error
     240            1 :       WRITE (io_unit, '(A,1X,ES13.6)') "discarded projected-gradient error", old_error
     241              :    END IF
     242              : 
     243            2 :    CALL dbcsr_release(b_im)
     244            2 :    CALL dbcsr_release(b_re)
     245            2 :    CALL dbcsr_release(tmp_kk)
     246            2 :    CALL dbcsr_release(f_im)
     247            2 :    CALL dbcsr_release(f_re)
     248            2 :    CALL dbcsr_release(q_im)
     249            2 :    CALL dbcsr_release(q_re)
     250            2 :    CALL dbcsr_release(g_im)
     251            2 :    CALL dbcsr_release(g_re)
     252            2 :    CALL dbcsr_release(hc_im)
     253            2 :    CALL dbcsr_release(hc_re)
     254            2 :    CALL dbcsr_release(sx_im)
     255            2 :    CALL dbcsr_release(sx_re)
     256            2 :    CALL dbcsr_release(x_im)
     257            2 :    CALL dbcsr_release(x_re)
     258            2 :    CALL dbcsr_distribution_release(dist_kk)
     259            2 :    CALL dbcsr_distribution_release(dist_nk)
     260            2 :    DEALLOCATE (pgrid, row_dist, col_dist, row_n, row_k, col_k)
     261              : 
     262            2 :    CALL dbcsr_finalize_lib()
     263            2 :    CALL remove_all_references()
     264            2 :    CALL cp_rm_default_logger()
     265            2 :    CALL cp_logger_release(logger)
     266            2 :    CALL mp_para_env_release(para_env)
     267            2 :    CALL mp_world_finalize()
     268              : 
     269            6 :    IF (nfail > 0) ERROR STOP "qs_ot_complex_ref_unittest failed"
     270              : 
     271              : CONTAINS
     272              : 
     273              : ! **************************************************************************************************
     274              : !> \brief Check split-complex full-matrix multiplication including adjoints.
     275              : !> \param para_env parallel environment
     276              : !> \param io_unit output unit
     277              : !> \param nfail accumulated number of failures
     278              : ! **************************************************************************************************
     279            2 :    SUBROUTINE test_split_complex_fm_gemm(para_env, io_unit, nfail)
     280              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     281              :       INTEGER, INTENT(IN)                                :: io_unit
     282              :       INTEGER, INTENT(INOUT)                             :: nfail
     283              : 
     284              :       INTEGER, PARAMETER                                 :: ndim = 3
     285              : 
     286              :       COMPLEX(KIND=dp), DIMENSION(ndim, ndim)            :: a, actual, b, expected
     287              :       INTEGER                                            :: i, j
     288              :       REAL(KIND=dp)                                      :: error_cn, error_nc, error_nn
     289              :       REAL(KIND=dp), DIMENSION(ndim, ndim)               :: actual_im, actual_re
     290              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
     291              :       TYPE(cp_fm_struct_type), POINTER                   :: matrix_struct
     292              :       TYPE(cp_fm_type)                                   :: a_im, a_re, b_im, b_re, c_im, c_re
     293              : 
     294            2 :       NULLIFY (blacs_env, matrix_struct)
     295            8 :       DO j = 1, ndim
     296           26 :          DO i = 1, ndim
     297           18 :             a(i, j) = CMPLX(0.13_dp*i - 0.07_dp*j, 0.05_dp*i*j - 0.11_dp*j, KIND=dp)
     298           24 :             b(i, j) = CMPLX(-0.09_dp*i + 0.17_dp*j, 0.08_dp*i*j + 0.04_dp*i, KIND=dp)
     299              :          END DO
     300              :       END DO
     301              : 
     302            2 :       CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=para_env)
     303              :       CALL cp_fm_struct_create(matrix_struct, nrow_global=ndim, ncol_global=ndim, &
     304            2 :                                context=blacs_env, para_env=para_env)
     305            2 :       CALL cp_fm_create(a_re, matrix_struct)
     306            2 :       CALL cp_fm_create(a_im, matrix_struct)
     307            2 :       CALL cp_fm_create(b_re, matrix_struct)
     308            2 :       CALL cp_fm_create(b_im, matrix_struct)
     309            2 :       CALL cp_fm_create(c_re, matrix_struct)
     310            2 :       CALL cp_fm_create(c_im, matrix_struct)
     311           26 :       CALL cp_fm_set_submatrix(a_re, REAL(a, KIND=dp))
     312           26 :       CALL cp_fm_set_submatrix(a_im, AIMAG(a))
     313           26 :       CALL cp_fm_set_submatrix(b_re, REAL(b, KIND=dp))
     314           26 :       CALL cp_fm_set_submatrix(b_im, AIMAG(b))
     315              : 
     316              :       CALL cp_complex_fm_gemm('N', 'N', ndim, ndim, ndim, 1.0_dp, &
     317            2 :                               a_re, a_im, b_re, b_im, 0.0_dp, c_re, c_im)
     318            2 :       CALL cp_fm_get_submatrix(c_re, actual_re)
     319            2 :       CALL cp_fm_get_submatrix(c_im, actual_im)
     320           26 :       actual = CMPLX(actual_re, actual_im, KIND=dp)
     321           80 :       expected = MATMUL(a, b)
     322           26 :       error_nn = MAXVAL(ABS(actual - expected))
     323              : 
     324              :       CALL cp_complex_fm_gemm('C', 'N', ndim, ndim, ndim, 1.0_dp, &
     325            2 :                               a_re, a_im, b_re, b_im, 0.0_dp, c_re, c_im)
     326            2 :       CALL cp_fm_get_submatrix(c_re, actual_re)
     327            2 :       CALL cp_fm_get_submatrix(c_im, actual_im)
     328           26 :       actual = CMPLX(actual_re, actual_im, KIND=dp)
     329           80 :       expected = MATMUL(CONJG(TRANSPOSE(a)), b)
     330           26 :       error_cn = MAXVAL(ABS(actual - expected))
     331              : 
     332              :       CALL cp_complex_fm_gemm('N', 'C', ndim, ndim, ndim, 1.0_dp, &
     333            2 :                               a_re, a_im, b_re, b_im, 0.0_dp, c_re, c_im)
     334            2 :       CALL cp_fm_get_submatrix(c_re, actual_re)
     335            2 :       CALL cp_fm_get_submatrix(c_im, actual_im)
     336           26 :       actual = CMPLX(actual_re, actual_im, KIND=dp)
     337           80 :       expected = MATMUL(a, CONJG(TRANSPOSE(b)))
     338           26 :       error_nc = MAXVAL(ABS(actual - expected))
     339              : 
     340            2 :       IF (MAX(error_nn, error_cn, error_nc) > 1.0E-12_dp) nfail = nfail + 1
     341            2 :       IF (io_unit >= 0) WRITE (io_unit, '(A,3(1X,ES13.6))') &
     342            1 :          'split-complex GEMM N/N, C/N, N/C errors:', error_nn, error_cn, error_nc
     343              : 
     344            2 :       CALL cp_fm_release(c_im)
     345            2 :       CALL cp_fm_release(c_re)
     346            2 :       CALL cp_fm_release(b_im)
     347            2 :       CALL cp_fm_release(b_re)
     348            2 :       CALL cp_fm_release(a_im)
     349            2 :       CALL cp_fm_release(a_re)
     350            2 :       CALL cp_fm_struct_release(matrix_struct)
     351            2 :       CALL cp_blacs_env_release(blacs_env)
     352              : 
     353            6 :    END SUBROUTINE test_split_complex_fm_gemm
     354              : 
     355              : ! **************************************************************************************************
     356              : !> \brief Check the noncanonical complex OT energy-weighted density and its gauge covariance.
     357              : !> \param para_env parallel environment
     358              : !> \param io_unit output unit
     359              : !> \param nfail accumulated number of failures
     360              : ! **************************************************************************************************
     361            2 :    SUBROUTINE test_kpoint_ot_energy_weighted_density(para_env, io_unit, nfail)
     362              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     363              :       INTEGER, INTENT(IN)                                :: io_unit
     364              :       INTEGER, INTENT(INOUT)                             :: nfail
     365              : 
     366              :       INTEGER, PARAMETER                                 :: nao = 4, nmo = 3
     367              : 
     368              :       COMPLEX(KIND=dp), DIMENSION(nao, nao)              :: actual, expected, hmat, rotated
     369              :       COMPLEX(KIND=dp), DIMENSION(nao, nmo)              :: coeff, coeff_rot, hc_dense, hc_rot
     370              :       COMPLEX(KIND=dp), DIMENSION(nmo, nmo)              :: hblock, lagrange, rotation
     371              :       INTEGER                                            :: i, j
     372              :       REAL(KIND=dp)                                      :: error, gauge_error, hermitian_error, &
     373              :                                                             phase, pi
     374              :       REAL(KIND=dp), DIMENSION(nao, nao)                 :: actual_im, actual_re, rotated_im, &
     375              :                                                             rotated_re
     376              :       REAL(KIND=dp), DIMENSION(nmo)                      :: occupation, uniform_occupation
     377              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
     378              :       TYPE(cp_fm_struct_type), POINTER                   :: coeff_struct, hmat_struct
     379              :       TYPE(cp_fm_type)                                   :: coeff_im_fm, coeff_re_fm, hc_im_fm, &
     380              :                                                             hc_re_fm, wmat_im_fm, wmat_re_fm
     381              : 
     382            2 :       NULLIFY (blacs_env, coeff_struct, hmat_struct)
     383            2 :       pi = ACOS(-1.0_dp)
     384            2 :       hmat(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     385            2 :       hmat(1, 1) = CMPLX(-0.8_dp, 0.0_dp, KIND=dp)
     386            2 :       hmat(2, 2) = CMPLX(0.1_dp, 0.0_dp, KIND=dp)
     387            2 :       hmat(3, 3) = CMPLX(0.7_dp, 0.0_dp, KIND=dp)
     388            2 :       hmat(4, 4) = CMPLX(1.4_dp, 0.0_dp, KIND=dp)
     389            2 :       hmat(1, 2) = CMPLX(0.21_dp, 0.13_dp, KIND=dp)
     390            2 :       hmat(1, 3) = CMPLX(-0.17_dp, 0.09_dp, KIND=dp)
     391            2 :       hmat(1, 4) = CMPLX(0.08_dp, -0.14_dp, KIND=dp)
     392            2 :       hmat(2, 3) = CMPLX(0.31_dp, -0.11_dp, KIND=dp)
     393            2 :       hmat(2, 4) = CMPLX(-0.19_dp, -0.07_dp, KIND=dp)
     394            2 :       hmat(3, 4) = CMPLX(0.27_dp, 0.16_dp, KIND=dp)
     395           10 :       DO i = 1, nao
     396           22 :          DO j = i + 1, nao
     397           20 :             hmat(j, i) = CONJG(hmat(i, j))
     398              :          END DO
     399              :       END DO
     400            8 :       DO j = 1, nmo
     401           32 :          DO i = 1, nao
     402           24 :             phase = 2.0_dp*pi*REAL((i - 1)*(j - 1), KIND=dp)/REAL(nao, KIND=dp)
     403           30 :             coeff(i, j) = CMPLX(COS(phase), SIN(phase), KIND=dp)/SQRT(REAL(nao, KIND=dp))
     404              :          END DO
     405              :       END DO
     406          128 :       hc_dense = MATMUL(hmat, coeff)
     407            2 :       occupation = [1.0_dp, 0.63_dp, 0.11_dp]
     408              : 
     409           98 :       hblock = MATMUL(CONJG(TRANSPOSE(coeff)), hc_dense)
     410            8 :       DO j = 1, nmo
     411           26 :          lagrange(:, j) = hblock(:, j)*occupation(j)
     412              :       END DO
     413           50 :       lagrange = 0.5_dp*(lagrange + CONJG(TRANSPOSE(lagrange)))
     414          226 :       expected = MATMUL(MATMUL(coeff, lagrange), CONJG(TRANSPOSE(coeff)))
     415              : 
     416            2 :       CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=para_env)
     417              :       CALL cp_fm_struct_create(hmat_struct, nrow_global=nao, ncol_global=nao, &
     418            2 :                                context=blacs_env, para_env=para_env)
     419              :       CALL cp_fm_struct_create(coeff_struct, nrow_global=nao, ncol_global=nmo, &
     420            2 :                                context=blacs_env, para_env=para_env)
     421            2 :       CALL cp_fm_create(coeff_re_fm, coeff_struct)
     422            2 :       CALL cp_fm_create(coeff_im_fm, coeff_struct)
     423            2 :       CALL cp_fm_create(hc_re_fm, coeff_struct)
     424            2 :       CALL cp_fm_create(hc_im_fm, coeff_struct)
     425            2 :       CALL cp_fm_create(wmat_re_fm, hmat_struct)
     426            2 :       CALL cp_fm_create(wmat_im_fm, hmat_struct)
     427           32 :       CALL cp_fm_set_submatrix(coeff_re_fm, REAL(coeff, KIND=dp))
     428           32 :       CALL cp_fm_set_submatrix(coeff_im_fm, AIMAG(coeff))
     429           32 :       CALL cp_fm_set_submatrix(hc_re_fm, REAL(hc_dense, KIND=dp))
     430           32 :       CALL cp_fm_set_submatrix(hc_im_fm, AIMAG(hc_dense))
     431              : 
     432              :       CALL kpoint_ot_energy_weighted_density( &
     433              :          coeff_re_fm, coeff_im_fm, hc_re_fm, hc_im_fm, occupation, &
     434            2 :          wmat_re_fm, wmat_im_fm)
     435            2 :       CALL cp_fm_get_submatrix(wmat_re_fm, actual_re)
     436            2 :       CALL cp_fm_get_submatrix(wmat_im_fm, actual_im)
     437           42 :       actual = CMPLX(actual_re, actual_im, KIND=dp)
     438           42 :       error = MAXVAL(ABS(actual - expected))
     439           42 :       hermitian_error = MAXVAL(ABS(actual - CONJG(TRANSPOSE(actual))))
     440              : 
     441            2 :       rotation(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     442            2 :       rotation(1, 1) = CMPLX(SQRT(0.5_dp), 0.0_dp, KIND=dp)
     443            2 :       rotation(1, 2) = CMPLX(0.0_dp, SQRT(0.5_dp), KIND=dp)
     444            2 :       rotation(2, 1) = CMPLX(0.0_dp, SQRT(0.5_dp), KIND=dp)
     445            2 :       rotation(2, 2) = CMPLX(SQRT(0.5_dp), 0.0_dp, KIND=dp)
     446            2 :       rotation(3, 3) = CMPLX(0.0_dp, 1.0_dp, KIND=dp)
     447           98 :       coeff_rot = MATMUL(coeff, rotation)
     448           98 :       hc_rot = MATMUL(hc_dense, rotation)
     449            8 :       uniform_occupation(:) = 1.0_dp
     450           32 :       CALL cp_fm_set_submatrix(coeff_re_fm, REAL(coeff_rot, KIND=dp))
     451           32 :       CALL cp_fm_set_submatrix(coeff_im_fm, AIMAG(coeff_rot))
     452           32 :       CALL cp_fm_set_submatrix(hc_re_fm, REAL(hc_rot, KIND=dp))
     453           32 :       CALL cp_fm_set_submatrix(hc_im_fm, AIMAG(hc_rot))
     454              :       CALL kpoint_ot_energy_weighted_density( &
     455              :          coeff_re_fm, coeff_im_fm, hc_re_fm, hc_im_fm, uniform_occupation, &
     456            2 :          wmat_re_fm, wmat_im_fm)
     457            2 :       CALL cp_fm_get_submatrix(wmat_re_fm, rotated_re)
     458            2 :       CALL cp_fm_get_submatrix(wmat_im_fm, rotated_im)
     459           42 :       rotated = CMPLX(rotated_re, rotated_im, KIND=dp)
     460          226 :       expected = MATMUL(MATMUL(coeff, hblock), CONJG(TRANSPOSE(coeff)))
     461           42 :       gauge_error = MAXVAL(ABS(rotated - expected))
     462              : 
     463            2 :       IF (error > 5.0E-12_dp .OR. hermitian_error > 5.0E-12_dp .OR. &
     464            0 :           gauge_error > 5.0E-12_dp) nfail = nfail + 1
     465            2 :       IF (io_unit >= 0) THEN
     466            1 :          WRITE (io_unit, '(A,3(1X,ES13.6))') 'complex OT W error/hermitian/gauge', &
     467            2 :             error, hermitian_error, gauge_error
     468              :       END IF
     469              : 
     470            2 :       CALL cp_fm_release(wmat_im_fm)
     471            2 :       CALL cp_fm_release(wmat_re_fm)
     472            2 :       CALL cp_fm_release(hc_im_fm)
     473            2 :       CALL cp_fm_release(hc_re_fm)
     474            2 :       CALL cp_fm_release(coeff_im_fm)
     475            2 :       CALL cp_fm_release(coeff_re_fm)
     476            2 :       CALL cp_fm_struct_release(coeff_struct)
     477            2 :       CALL cp_fm_struct_release(hmat_struct)
     478            2 :       CALL cp_blacs_env_release(blacs_env)
     479              : 
     480            4 :    END SUBROUTINE test_kpoint_ot_energy_weighted_density
     481              : ! **************************************************************************************************
     482              : !> \brief Check the complex rotation norm against an analytic pair and a unitary gauge change.
     483              : !> \param io_unit output unit
     484              : !> \param nfail accumulated failures
     485              : ! **************************************************************************************************
     486            2 :    SUBROUTINE test_antihermitian_spectral_norm(io_unit, nfail)
     487              :       INTEGER, INTENT(IN)                                :: io_unit
     488              :       INTEGER, INTENT(INOUT)                             :: nfail
     489              : 
     490              :       COMPLEX(KIND=dp), DIMENSION(3)                     :: phase
     491              :       COMPLEX(KIND=dp), DIMENSION(3, 3)                  :: generator, transformed
     492              :       INTEGER                                            :: i, j
     493              :       REAL(KIND=dp)                                      :: error, norm, transformed_norm
     494              : 
     495            2 :       generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     496            2 :       generator(1, 2) = CMPLX(0.3_dp, 0.4_dp, KIND=dp)
     497            2 :       generator(2, 1) = -CONJG(generator(1, 2))
     498            2 :       phase = EXP(CMPLX(0.0_dp, 1.0_dp, KIND=dp)*[0.37_dp, -0.51_dp, 0.83_dp])
     499            8 :       DO j = 1, 3
     500           26 :          DO i = 1, 3
     501           24 :             transformed(i, j) = CONJG(phase(i))*generator(i, j)*phase(j)
     502              :          END DO
     503              :       END DO
     504              : 
     505            2 :       norm = qs_ot_antihermitian_spectral_norm(generator)
     506            2 :       transformed_norm = qs_ot_antihermitian_spectral_norm(transformed)
     507            2 :       error = MAX(ABS(norm - 0.5_dp), ABS(transformed_norm - norm))
     508            2 :       IF (io_unit >= 0) WRITE (io_unit, '(A,ES14.6)') &
     509            1 :          "complex rotation spectral-norm error: ", error
     510            2 :       IF (error > 1.0E-13_dp) nfail = nfail + 1
     511              : 
     512            2 :    END SUBROUTINE test_antihermitian_spectral_norm
     513              : 
     514              : ! **************************************************************************************************
     515              : !> \brief Check the fixed-N Mermin gradient, dense gauge projector, and occupation Hessian.
     516              : !> \param mynode MPI rank
     517              : !> \param nfail accumulated number of failures
     518              : ! **************************************************************************************************
     519            2 :    SUBROUTINE test_fixed_n_mermin_energy(mynode, nfail)
     520              :       INTEGER, INTENT(IN)                                :: mynode
     521              :       INTEGER, INTENT(INOUT)                             :: nfail
     522              : 
     523              :       INTEGER, PARAMETER                                 :: nbands = 4
     524              :       REAL(KIND=dp), PARAMETER                           :: fd_step = 1.0E-6_dp, maxocc = 2.0_dp, &
     525              :                                                             target_electrons = 2.25_dp, &
     526              :                                                             temperature = 0.07_dp
     527              : 
     528              :       REAL(KIND=dp)                                      :: fd_slope, gradient_error, hessian_error, &
     529              :                                                             mu, predicted_slope, response_sum, &
     530              :                                                             weighted_residual
     531              :       REAL(KIND=dp), DIMENSION(nbands) :: direction, energies, gradient, gradient_minus, &
     532              :          gradient_plus, hessian_action, occupation, rayleigh, response, response_minus, &
     533              :          response_plus, weights
     534              :       REAL(KIND=dp), DIMENSION(nbands, nbands)           :: hessian
     535              : 
     536            2 :       energies(:) = [-0.24_dp, -0.05_dp, 0.08_dp, 0.31_dp]
     537            2 :       rayleigh(:) = [-0.19_dp, -0.02_dp, 0.04_dp, 0.27_dp]
     538            2 :       weights(:) = [0.35_dp, 0.35_dp, 0.65_dp, 0.65_dp]
     539            2 :       direction(:) = [0.17_dp, -0.11_dp, 0.08_dp, -0.05_dp]
     540              : 
     541              :       CALL fixed_n_fermi_occupations(energies, weights, target_electrons, temperature, &
     542            2 :                                      maxocc, occupation, mu)
     543           10 :       response(:) = weights(:)*occupation(:)*(maxocc - occupation(:))/(maxocc*temperature)
     544           10 :       response_sum = SUM(response)
     545           10 :       weighted_residual = DOT_PRODUCT(response, rayleigh - energies)
     546              :       CALL qs_ot_fixed_n_energy_gradient(rayleigh, energies, response, response_sum, &
     547            2 :                                          weighted_residual, gradient)
     548              : 
     549              :       fd_slope = (fixed_n_mermin_value(rayleigh, energies + fd_step*direction, weights, &
     550              :                                        target_electrons, temperature, maxocc) - &
     551              :                   fixed_n_mermin_value(rayleigh, energies - fd_step*direction, weights, &
     552           18 :                                        target_electrons, temperature, maxocc))/(2.0_dp*fd_step)
     553           10 :       predicted_slope = DOT_PRODUCT(gradient, direction)
     554           10 :       gradient_error = MAX(ABS(fd_slope - predicted_slope), ABS(SUM(gradient)))
     555              : 
     556              :       ! At the canonical point h_i=e_i, the exact local Hessian is the dense fixed-N projector.
     557            2 :       rayleigh(:) = energies(:)
     558            2 :       CALL qs_ot_fixed_n_energy_hessian(response, response_sum, hessian)
     559           42 :       hessian_action(:) = MATMUL(hessian, direction)
     560              : 
     561              :       CALL fixed_n_fermi_occupations(energies + fd_step*direction, weights, target_electrons, &
     562           10 :                                      temperature, maxocc, occupation, mu)
     563           10 :       response_plus(:) = weights(:)*occupation(:)*(maxocc - occupation(:))/(maxocc*temperature)
     564           10 :       weighted_residual = DOT_PRODUCT(response_plus, rayleigh - energies - fd_step*direction)
     565              :       CALL qs_ot_fixed_n_energy_gradient(rayleigh, energies + fd_step*direction, response_plus, &
     566           18 :                                          SUM(response_plus), weighted_residual, gradient_plus)
     567              : 
     568              :       CALL fixed_n_fermi_occupations(energies - fd_step*direction, weights, target_electrons, &
     569           10 :                                      temperature, maxocc, occupation, mu)
     570           10 :       response_minus(:) = weights(:)*occupation(:)*(maxocc - occupation(:))/(maxocc*temperature)
     571           10 :       weighted_residual = DOT_PRODUCT(response_minus, rayleigh - energies + fd_step*direction)
     572              :       CALL qs_ot_fixed_n_energy_gradient(rayleigh, energies - fd_step*direction, response_minus, &
     573           18 :                                          SUM(response_minus), weighted_residual, gradient_minus)
     574              : 
     575              :       hessian_error = MAXVAL(ABS((gradient_plus - gradient_minus)/(2.0_dp*fd_step) - &
     576           10 :                                  hessian_action))
     577           42 :       hessian_error = MAX(hessian_error, MAXVAL(ABS(hessian - TRANSPOSE(hessian))))
     578           52 :       hessian_error = MAX(hessian_error, MAXVAL(ABS(MATMUL(hessian, SPREAD(1.0_dp, 1, nbands)))))
     579              : 
     580            2 :       IF (gradient_error > 2.0E-8_dp .OR. hessian_error > 2.0E-7_dp) nfail = nfail + 1
     581            2 :       IF (mynode == 0) THEN
     582            1 :          WRITE (io_unit, '(A,2(1X,ES13.6))') "fixed-N Mermin gradient/Hessian errors:", &
     583            2 :             gradient_error, hessian_error
     584              :       END IF
     585              : 
     586            2 :    END SUBROUTINE test_fixed_n_mermin_energy
     587              : 
     588              : ! **************************************************************************************************
     589              : !> \brief Check the complex fixed-N spectral occupation-projector derivative by finite differences.
     590              : !> \param mynode MPI rank
     591              : !> \param nfail accumulated number of failures
     592              : ! **************************************************************************************************
     593            2 :    SUBROUTINE test_fixed_n_projector_frechet(mynode, nfail)
     594              :       INTEGER, INTENT(IN)                                :: mynode
     595              :       INTEGER, INTENT(INOUT)                             :: nfail
     596              : 
     597              :       INTEGER, PARAMETER                                 :: nbands = 3
     598              :       REAL(KIND=dp), PARAMETER :: fd_step = 1.0E-6_dp, kpoint_weight = 0.75_dp, maxocc = 2.0_dp, &
     599              :          target_electrons = 2.10_dp, temperature = 0.09_dp
     600              : 
     601              :       COMPLEX(KIND=dp), DIMENSION(nbands, nbands)        :: dchc, derivative, derivative_fd, &
     602              :                                                             hamiltonian, projector_minus, &
     603              :                                                             projector_plus, vectors
     604              :       INTEGER                                            :: i
     605              :       REAL(KIND=dp)                                      :: error, mu, trace_error
     606              :       REAL(KIND=dp), DIMENSION(nbands)                   :: eigenvalues, occupation, response, &
     607              :                                                             weights
     608              : 
     609            2 :       hamiltonian(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     610            2 :       hamiltonian(1, 1) = CMPLX(-0.22_dp, 0.0_dp, KIND=dp)
     611            2 :       hamiltonian(2, 2) = CMPLX(0.03_dp, 0.0_dp, KIND=dp)
     612            2 :       hamiltonian(3, 3) = CMPLX(0.28_dp, 0.0_dp, KIND=dp)
     613            2 :       hamiltonian(1, 2) = CMPLX(0.04_dp, -0.03_dp, KIND=dp)
     614            2 :       hamiltonian(2, 1) = CONJG(hamiltonian(1, 2))
     615            2 :       hamiltonian(2, 3) = CMPLX(-0.02_dp, 0.05_dp, KIND=dp)
     616            2 :       hamiltonian(3, 2) = CONJG(hamiltonian(2, 3))
     617              : 
     618            2 :       dchc(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     619            2 :       dchc(1, 1) = CMPLX(0.13_dp, 0.0_dp, KIND=dp)
     620            2 :       dchc(2, 2) = CMPLX(-0.08_dp, 0.0_dp, KIND=dp)
     621            2 :       dchc(3, 3) = CMPLX(0.05_dp, 0.0_dp, KIND=dp)
     622            2 :       dchc(1, 2) = CMPLX(0.17_dp, -0.11_dp, KIND=dp)
     623            2 :       dchc(2, 1) = CONJG(dchc(1, 2))
     624            2 :       dchc(2, 3) = CMPLX(0.12_dp, 0.09_dp, KIND=dp)
     625            2 :       dchc(3, 2) = CONJG(dchc(2, 3))
     626              : 
     627            2 :       CALL diag_complex(hamiltonian, vectors, eigenvalues)
     628            8 :       weights(:) = kpoint_weight
     629              :       CALL fixed_n_fermi_occupations(eigenvalues, weights, target_electrons, temperature, &
     630            2 :                                      maxocc, occupation, mu)
     631            8 :       response(:) = weights(:)*occupation(:)*(maxocc - occupation(:))/(maxocc*temperature)
     632              :       CALL qs_ot_fixed_n_projector_frechet(hamiltonian, dchc, occupation, kpoint_weight, &
     633            8 :                                            response, SUM(response), derivative)
     634              : 
     635              :       CALL fixed_n_spectral_projector(hamiltonian + fd_step*dchc, weights, target_electrons, &
     636           26 :                                       temperature, maxocc, projector_plus)
     637              :       CALL fixed_n_spectral_projector(hamiltonian - fd_step*dchc, weights, target_electrons, &
     638           26 :                                       temperature, maxocc, projector_minus)
     639           26 :       derivative_fd(:, :) = (projector_plus - projector_minus)/(2.0_dp*fd_step)
     640           26 :       error = MAXVAL(ABS(derivative - derivative_fd))
     641            2 :       trace_error = 0.0_dp
     642            8 :       DO i = 1, nbands
     643            8 :          trace_error = trace_error + REAL(derivative(i, i), KIND=dp)
     644              :       END DO
     645            2 :       error = MAX(error, ABS(trace_error))
     646              : 
     647            2 :       IF (error > 3.0E-7_dp) nfail = nfail + 1
     648            2 :       IF (mynode == 0) WRITE (io_unit, '(A,1X,ES13.6)') &
     649            1 :          "fixed-N complex occupation-projector error:", error
     650              : 
     651            2 :    END SUBROUTINE test_fixed_n_projector_frechet
     652              : 
     653              : ! **************************************************************************************************
     654              : !> \brief Check the finite complex REF rotation Hessian against the scalar band energy.
     655              : !> \param mynode MPI rank
     656              : !> \param nfail accumulated number of failures
     657              : ! **************************************************************************************************
     658            2 :    SUBROUTINE test_finite_rotation_response(mynode, nfail)
     659              :       INTEGER, INTENT(IN)                                :: mynode
     660              :       INTEGER, INTENT(INOUT)                             :: nfail
     661              : 
     662              :       INTEGER, PARAMETER                                 :: nbands = 3, nrotation = 6
     663              :       REAL(KIND=dp), PARAMETER                           :: fd_step = 2.0E-4_dp, &
     664              :                                                             kpoint_weight = 0.625_dp
     665              : 
     666              :       COMPLEX(KIND=dp), DIMENSION(nbands, nbands)        :: base_hamiltonian, chc, &
     667              :                                                             direction_generator, generator, &
     668              :                                                             rotation, rotation_minus, rotation_plus
     669              :       INTEGER                                            :: i
     670              :       REAL(KIND=dp)                                      :: energy, energy_minus, energy_plus, &
     671              :                                                             gradient_error, hessian_error, &
     672              :                                                             rayleigh_error, symmetry_error
     673              :       REAL(KIND=dp), DIMENSION(nbands)                   :: occupation, rayleigh_minus, rayleigh_plus
     674              :       REAL(KIND=dp), DIMENSION(nbands, nrotation)        :: rayleigh_response
     675              :       REAL(KIND=dp), DIMENSION(nrotation)                :: direction, gradient
     676              :       REAL(KIND=dp), DIMENSION(nrotation, nrotation)     :: hessian
     677              : 
     678            2 :       base_hamiltonian(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     679            2 :       base_hamiltonian(1, 1) = CMPLX(-0.31_dp, 0.0_dp, KIND=dp)
     680            2 :       base_hamiltonian(2, 2) = CMPLX(0.07_dp, 0.0_dp, KIND=dp)
     681            2 :       base_hamiltonian(3, 3) = CMPLX(0.42_dp, 0.0_dp, KIND=dp)
     682            2 :       base_hamiltonian(1, 2) = CMPLX(0.09_dp, -0.06_dp, KIND=dp)
     683            2 :       base_hamiltonian(1, 3) = CMPLX(-0.04_dp, 0.08_dp, KIND=dp)
     684            2 :       base_hamiltonian(2, 3) = CMPLX(0.11_dp, 0.05_dp, KIND=dp)
     685            8 :       DO i = 1, nbands
     686           14 :          base_hamiltonian(i, 1:i - 1) = CONJG(base_hamiltonian(1:i - 1, i))
     687              :       END DO
     688              : 
     689            2 :       generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     690            2 :       generator(1, 2) = CMPLX(0.17_dp, -0.08_dp, KIND=dp)
     691            2 :       generator(1, 3) = CMPLX(-0.09_dp, 0.04_dp, KIND=dp)
     692            2 :       generator(2, 3) = CMPLX(0.12_dp, 0.07_dp, KIND=dp)
     693            8 :       DO i = 1, nbands
     694           14 :          generator(i, 1:i - 1) = -CONJG(generator(1:i - 1, i))
     695              :       END DO
     696              : 
     697            2 :       direction(:) = [0.14_dp, -0.07_dp, -0.11_dp, 0.09_dp, 0.05_dp, 0.13_dp]
     698            2 :       direction_generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     699            2 :       direction_generator(1, 2) = CMPLX(direction(1), direction(2), KIND=dp)
     700            2 :       direction_generator(2, 1) = -CONJG(direction_generator(1, 2))
     701            2 :       direction_generator(1, 3) = CMPLX(direction(3), direction(4), KIND=dp)
     702            2 :       direction_generator(3, 1) = -CONJG(direction_generator(1, 3))
     703            2 :       direction_generator(2, 3) = CMPLX(direction(5), direction(6), KIND=dp)
     704            2 :       direction_generator(3, 2) = -CONJG(direction_generator(2, 3))
     705            2 :       occupation(:) = [1.73_dp, 0.88_dp, 0.19_dp]
     706              : 
     707            2 :       rotation(:, :) = dense_antihermitian_exp(generator)
     708              :       chc(:, :) = MATMUL(CONJG(TRANSPOSE(rotation)), &
     709          158 :                          MATMUL(base_hamiltonian, rotation))
     710              :       CALL qs_ot_finite_rotation_response(chc, generator, occupation, kpoint_weight, &
     711            2 :                                           gradient, hessian, rayleigh_response)
     712              : 
     713           26 :       rotation_plus(:, :) = dense_antihermitian_exp(generator + fd_step*direction_generator)
     714           26 :       rotation_minus(:, :) = dense_antihermitian_exp(generator - fd_step*direction_generator)
     715            2 :       energy = finite_rotation_energy(rotation, base_hamiltonian, occupation, kpoint_weight)
     716            2 :       energy_plus = finite_rotation_energy(rotation_plus, base_hamiltonian, occupation, kpoint_weight)
     717            2 :       energy_minus = finite_rotation_energy(rotation_minus, base_hamiltonian, occupation, kpoint_weight)
     718              :       gradient_error = ABS((energy_plus - energy_minus)/(2.0_dp*fd_step) - &
     719           14 :                            DOT_PRODUCT(gradient, direction))
     720              :       hessian_error = ABS((energy_plus - 2.0_dp*energy + energy_minus)/fd_step**2 - &
     721           98 :                           DOT_PRODUCT(direction, MATMUL(hessian, direction)))
     722           86 :       symmetry_error = MAXVAL(ABS(hessian - TRANSPOSE(hessian)))
     723              : 
     724            8 :       DO i = 1, nbands
     725              :          rayleigh_plus(i) = REAL(DOT_PRODUCT(rotation_plus(:, i), &
     726           96 :                                              MATMUL(base_hamiltonian, rotation_plus(:, i))), KIND=dp)
     727              :          rayleigh_minus(i) = REAL(DOT_PRODUCT(rotation_minus(:, i), &
     728           98 :                                               MATMUL(base_hamiltonian, rotation_minus(:, i))), KIND=dp)
     729              :       END DO
     730              :       rayleigh_error = MAXVAL(ABS((rayleigh_plus - rayleigh_minus)/(2.0_dp*fd_step) - &
     731           56 :                                   MATMUL(rayleigh_response, direction)))
     732              : 
     733              :       IF (gradient_error > 5.0E-8_dp .OR. hessian_error > 2.0E-6_dp .OR. &
     734            2 :           rayleigh_error > 2.0E-7_dp .OR. symmetry_error > 1.0E-12_dp) nfail = nfail + 1
     735            2 :       IF (mynode == 0) THEN
     736            1 :          WRITE (io_unit, '(A,4(1X,ES13.6))') "finite complex rotation response errors:", &
     737            2 :             gradient_error, hessian_error, rayleigh_error, symmetry_error
     738              :       END IF
     739              : 
     740            2 :    END SUBROUTINE test_finite_rotation_response
     741              : 
     742              : ! **************************************************************************************************
     743              : !> \brief Check the coupled fixed-N rotation/energy Hessian and its Schur decomposition.
     744              : !> \param mynode MPI rank
     745              : !> \param nfail accumulated number of failures
     746              : ! **************************************************************************************************
     747            2 :    SUBROUTINE test_fixed_n_rotation_schur(mynode, nfail)
     748              :       INTEGER, INTENT(IN)                                :: mynode
     749              :       INTEGER, INTENT(INOUT)                             :: nfail
     750              : 
     751              :       INTEGER, PARAMETER                                 :: nbands = 3, nrotation = 6
     752              :       REAL(KIND=dp), PARAMETER                           :: fd_step = 1.0E-4_dp, &
     753              :                                                             kpoint_weight = 0.625_dp, &
     754              :                                                             maxocc = 2.0_dp, temperature = 0.08_dp
     755              : 
     756              :       COMPLEX(KIND=dp), DIMENSION(nbands, nbands)        :: base_hamiltonian, chc, &
     757              :                                                             direction_generator, generator, &
     758              :                                                             rotation, rotation_minus, rotation_plus
     759              :       INTEGER                                            :: i
     760              :       REAL(KIND=dp)                                      :: curvature, curvature_error, &
     761              :                                                             energy_minus, energy_plus, &
     762              :                                                             energy_zero, mu, response_sum, &
     763              :                                                             schur_error
     764              :       REAL(KIND=dp), DIMENSION(nbands) :: energy_coordinate, energy_direction, energy_gradient, &
     765              :          energy_minus_coordinate, energy_plus_coordinate, occupation, rayleigh, rayleigh_minus, &
     766              :          rayleigh_plus, response, weights
     767              :       REAL(KIND=dp), DIMENSION(nbands, nbands)           :: energy_hessian
     768              :       REAL(KIND=dp), DIMENSION(nbands, nrotation)        :: rayleigh_response
     769              :       REAL(KIND=dp), DIMENSION(nrotation)                :: coupling, rotation_direction, &
     770              :                                                             rotation_gradient, schur_rhs
     771              :       REAL(KIND=dp), DIMENSION(nrotation, nrotation)     :: rotation_hessian, schur, schur_block
     772              : 
     773            2 :       base_hamiltonian(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     774            2 :       base_hamiltonian(1, 1) = CMPLX(-0.31_dp, 0.0_dp, KIND=dp)
     775            2 :       base_hamiltonian(2, 2) = CMPLX(0.07_dp, 0.0_dp, KIND=dp)
     776            2 :       base_hamiltonian(3, 3) = CMPLX(0.42_dp, 0.0_dp, KIND=dp)
     777            2 :       base_hamiltonian(1, 2) = CMPLX(0.09_dp, -0.06_dp, KIND=dp)
     778            2 :       base_hamiltonian(1, 3) = CMPLX(-0.04_dp, 0.08_dp, KIND=dp)
     779            2 :       base_hamiltonian(2, 3) = CMPLX(0.11_dp, 0.05_dp, KIND=dp)
     780            8 :       DO i = 1, nbands
     781           14 :          base_hamiltonian(i, 1:i - 1) = CONJG(base_hamiltonian(1:i - 1, i))
     782              :       END DO
     783              : 
     784            2 :       generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     785            2 :       generator(1, 2) = CMPLX(0.17_dp, -0.08_dp, KIND=dp)
     786            2 :       generator(1, 3) = CMPLX(-0.09_dp, 0.04_dp, KIND=dp)
     787            2 :       generator(2, 3) = CMPLX(0.12_dp, 0.07_dp, KIND=dp)
     788            8 :       DO i = 1, nbands
     789           14 :          generator(i, 1:i - 1) = -CONJG(generator(1:i - 1, i))
     790              :       END DO
     791            2 :       rotation_direction(:) = [0.14_dp, -0.07_dp, -0.11_dp, 0.09_dp, 0.05_dp, 0.13_dp]
     792            2 :       energy_direction(:) = [0.08_dp, -0.11_dp, 0.03_dp]
     793            2 :       direction_generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     794            2 :       direction_generator(1, 2) = CMPLX(rotation_direction(1), rotation_direction(2), KIND=dp)
     795            2 :       direction_generator(2, 1) = -CONJG(direction_generator(1, 2))
     796            2 :       direction_generator(1, 3) = CMPLX(rotation_direction(3), rotation_direction(4), KIND=dp)
     797            2 :       direction_generator(3, 1) = -CONJG(direction_generator(1, 3))
     798            2 :       direction_generator(2, 3) = CMPLX(rotation_direction(5), rotation_direction(6), KIND=dp)
     799            2 :       direction_generator(3, 2) = -CONJG(direction_generator(2, 3))
     800              : 
     801            2 :       rotation(:, :) = dense_antihermitian_exp(generator)
     802          158 :       chc(:, :) = MATMUL(CONJG(TRANSPOSE(rotation)), MATMUL(base_hamiltonian, rotation))
     803            8 :       DO i = 1, nbands
     804            8 :          rayleigh(i) = REAL(chc(i, i), KIND=dp)
     805              :       END DO
     806            2 :       energy_coordinate(:) = rayleigh(:)
     807            8 :       weights(:) = kpoint_weight
     808              :       CALL fixed_n_fermi_occupations(energy_coordinate, weights, 1.7_dp, temperature, &
     809            2 :                                      maxocc, occupation, mu)
     810            8 :       response(:) = weights*occupation*(maxocc - occupation)/(maxocc*temperature)
     811            8 :       response_sum = SUM(response)
     812              :       CALL qs_ot_finite_rotation_response(chc, generator, occupation, kpoint_weight, &
     813            2 :                                           rotation_gradient, rotation_hessian, rayleigh_response)
     814              :       CALL qs_ot_fixed_n_energy_gradient(rayleigh, energy_coordinate, response, response_sum, &
     815            2 :                                          0.0_dp, energy_gradient)
     816            2 :       CALL qs_ot_fixed_n_energy_hessian(response, response_sum, energy_hessian)
     817              :       CALL qs_ot_fixed_n_schur_block( &
     818              :          rotation_hessian, rayleigh_response, response, rotation_gradient, energy_gradient, &
     819            2 :          schur_block, coupling, schur_rhs)
     820              :       schur(:, :) = schur_block + SPREAD(coupling, DIM=2, NCOPIES=nrotation)* &
     821           86 :                     SPREAD(coupling, DIM=1, NCOPIES=nrotation)/response_sum
     822              :       schur_error = MAXVAL(ABS(schur - rotation_hessian + &
     823              :                                MATMUL(TRANSPOSE(rayleigh_response), &
     824          544 :                                       MATMUL(energy_hessian, rayleigh_response))))
     825              : 
     826           26 :       rotation_plus(:, :) = dense_antihermitian_exp(generator + fd_step*direction_generator)
     827           26 :       rotation_minus(:, :) = dense_antihermitian_exp(generator - fd_step*direction_generator)
     828            8 :       DO i = 1, nbands
     829              :          rayleigh_plus(i) = REAL(DOT_PRODUCT(rotation_plus(:, i), &
     830           96 :                                              MATMUL(base_hamiltonian, rotation_plus(:, i))), KIND=dp)
     831              :          rayleigh_minus(i) = REAL(DOT_PRODUCT(rotation_minus(:, i), &
     832          104 :                                               MATMUL(base_hamiltonian, rotation_minus(:, i))), KIND=dp)
     833              :       END DO
     834            8 :       energy_plus_coordinate(:) = energy_coordinate + fd_step*energy_direction
     835            8 :       energy_minus_coordinate(:) = energy_coordinate - fd_step*energy_direction
     836              :       energy_zero = fixed_n_mermin_value(rayleigh, energy_coordinate, weights, 1.7_dp, &
     837            2 :                                          temperature, maxocc)
     838              :       energy_plus = fixed_n_mermin_value(rayleigh_plus, energy_plus_coordinate, weights, 1.7_dp, &
     839            2 :                                          temperature, maxocc)
     840              :       energy_minus = fixed_n_mermin_value(rayleigh_minus, energy_minus_coordinate, weights, 1.7_dp, &
     841            2 :                                           temperature, maxocc)
     842              :       curvature = DOT_PRODUCT(rotation_direction, MATMUL(rotation_hessian, rotation_direction)) - &
     843              :                   2.0_dp*DOT_PRODUCT(energy_direction, &
     844              :                                      MATMUL(energy_hessian, &
     845              :                                             MATMUL(rayleigh_response, rotation_direction))) + &
     846          212 :                   DOT_PRODUCT(energy_direction, MATMUL(energy_hessian, energy_direction))
     847            2 :       curvature_error = ABS((energy_plus - 2.0_dp*energy_zero + energy_minus)/fd_step**2 - curvature)
     848              : 
     849            2 :       IF (schur_error > 2.0E-11_dp .OR. curvature_error > 3.0E-6_dp) nfail = nfail + 1
     850            2 :       IF (mynode == 0) THEN
     851            1 :          WRITE (io_unit, '(A,2(1X,ES13.6))') "fixed-N rotation Schur errors:", &
     852            2 :             schur_error, curvature_error
     853              :       END IF
     854              : 
     855            2 :    END SUBROUTINE test_fixed_n_rotation_schur
     856              : 
     857              : ! **************************************************************************************************
     858              : !> \brief verify the two-chemical-potential Schur reduction used by shared ROKS orbitals
     859              : !> \param mynode MPI rank
     860              : !> \param nfail accumulated failures
     861              : ! **************************************************************************************************
     862            2 :    SUBROUTINE test_fixed_n_multigroup_schur(mynode, nfail)
     863              :       INTEGER, INTENT(IN)                                :: mynode
     864              :       INTEGER, INTENT(INOUT)                             :: nfail
     865              : 
     866              :       INTEGER, PARAMETER                                 :: nenergy = 4, ngroups = 2, nrotation = 3
     867              : 
     868              :       INTEGER, DIMENSION(nenergy)                        :: response_group
     869              :       REAL(KIND=dp)                                      :: coupling_error, rhs_error, schur_error
     870              :       REAL(KIND=dp), DIMENSION(nenergy)                  :: energy_gradient, response_weight
     871              :       REAL(KIND=dp), DIMENSION(nenergy, nrotation)       :: rayleigh_response
     872              :       REAL(KIND=dp), DIMENSION(nrotation)                :: rotation_gradient, schur_rhs
     873              :       REAL(KIND=dp), DIMENSION(nrotation, ngroups)       :: coupling
     874              :       REAL(KIND=dp), DIMENSION(nrotation, nrotation)     :: expected, rotation_hessian, schur_block
     875              : 
     876              :       rotation_hessian(:, :) = RESHAPE( &
     877              :                                [2.3_dp, 0.2_dp, -0.1_dp, 0.2_dp, 1.8_dp, 0.3_dp, -0.1_dp, 0.3_dp, 1.5_dp], &
     878            2 :                                [nrotation, nrotation])
     879              :       rayleigh_response(:, :) = RESHAPE( &
     880              :                                 [0.4_dp, -0.2_dp, 0.1_dp, 0.3_dp, &
     881              :                                  -0.1_dp, 0.5_dp, 0.2_dp, -0.4_dp, &
     882              :                                  0.3_dp, 0.1_dp, -0.5_dp, 0.2_dp], &
     883            2 :                                 [nenergy, nrotation])
     884            2 :       response_weight(:) = [0.7_dp, 0.5_dp, 0.4_dp, 0.2_dp]
     885            2 :       response_group(:) = [1, 1, 2, 2]
     886            2 :       rotation_gradient(:) = [0.2_dp, -0.1_dp, 0.3_dp]
     887            2 :       energy_gradient(:) = [0.04_dp, -0.03_dp, 0.02_dp, -0.01_dp]
     888              : 
     889              :       CALL qs_ot_fixed_n_multigroup_schur_block( &
     890              :          rotation_hessian, rayleigh_response, response_weight, response_group, &
     891            2 :          rotation_gradient, energy_gradient, schur_block, coupling, schur_rhs)
     892              : 
     893              :       expected(:, :) = rotation_hessian - &
     894              :                        MATMUL(TRANSPOSE(rayleigh_response), &
     895              :                               MATMUL(RESHAPE( &
     896              :                                      [0.2916666666666667_dp, -0.2916666666666667_dp, 0.0_dp, 0.0_dp, &
     897              :                                       -0.2916666666666667_dp, 0.2916666666666667_dp, 0.0_dp, 0.0_dp, &
     898              :                                       0.0_dp, 0.0_dp, 0.1333333333333333_dp, -0.1333333333333333_dp, &
     899              :                                       0.0_dp, 0.0_dp, -0.1333333333333333_dp, 0.1333333333333333_dp], &
     900          250 :                                      [nenergy, nenergy]), rayleigh_response))
     901              :       schur_error = MAXVAL(ABS( &
     902              :                            schur_block + &
     903              :                            MATMUL(coupling(:, 1:1), TRANSPOSE(coupling(:, 1:1)))/1.2_dp + &
     904           88 :                            MATMUL(coupling(:, 2:2), TRANSPOSE(coupling(:, 2:2)))/0.6_dp - expected))
     905              :       coupling_error = MAXVAL(ABS(coupling(:, 1) - &
     906              :                                   MATMUL(TRANSPOSE(rayleigh_response(1:2, :)), &
     907           10 :                                          response_weight(1:2))))
     908              :       coupling_error = MAX(coupling_error, MAXVAL(ABS(coupling(:, 2) - &
     909              :                                                       MATMUL(TRANSPOSE(rayleigh_response(3:4, :)), &
     910           10 :                                                              response_weight(3:4)))))
     911              :       rhs_error = MAXVAL(ABS(schur_rhs - rotation_gradient - &
     912           10 :                              MATMUL(TRANSPOSE(rayleigh_response), energy_gradient)))
     913              : 
     914            2 :       IF (MAX(schur_error, coupling_error, rhs_error) > 2.0E-14_dp) nfail = nfail + 1
     915            2 :       IF (mynode == 0) THEN
     916            1 :          WRITE (io_unit, '(A,3(1X,ES13.6))') "fixed-N multigroup Schur errors:", &
     917            2 :             schur_error, coupling_error, rhs_error
     918              :       END IF
     919              : 
     920            2 :    END SUBROUTINE test_fixed_n_multigroup_schur
     921              : 
     922              : ! **************************************************************************************************
     923              : !> \brief finite-difference the shared-orbital, two-fixed-N ROKS Mermin Hessian
     924              : !> \param mynode MPI rank
     925              : !> \param nfail accumulated failures
     926              : ! **************************************************************************************************
     927            2 :    SUBROUTINE test_roks_fixed_n_rotation_schur(mynode, nfail)
     928              :       INTEGER, INTENT(IN)                                :: mynode
     929              :       INTEGER, INTENT(INOUT)                             :: nfail
     930              : 
     931              :       INTEGER, PARAMETER                                 :: nbands = 3, nenergy = 6, ngroups = 2, &
     932              :                                                             nrotation = 6
     933              :       REAL(KIND=dp), PARAMETER                           :: fd_step = 1.0E-4_dp, &
     934              :                                                             kpoint_weight = 0.7_dp, &
     935              :                                                             maxocc = 1.0_dp, temperature = 0.09_dp
     936              : 
     937              :       COMPLEX(KIND=dp), DIMENSION(nbands, nbands) :: chc_a, chc_b, direction_generator, generator, &
     938              :          hamiltonian_a, hamiltonian_b, rotation, rotation_minus, rotation_plus
     939              :       INTEGER                                            :: i
     940              :       INTEGER, DIMENSION(nenergy)                        :: response_group
     941              :       REAL(KIND=dp) :: curvature, curvature_a, curvature_b, curvature_error, energy_minus, &
     942              :          energy_minus_a, energy_minus_b, energy_plus, energy_plus_a, energy_plus_b, energy_zero, &
     943              :          energy_zero_a, energy_zero_b, mu_a, mu_b, response_sum_a, response_sum_b, schur_error
     944              :       REAL(KIND=dp), DIMENSION(nbands) :: energy_a, energy_a_minus, energy_a_plus, energy_b, &
     945              :          energy_b_minus, energy_b_plus, occupation_a, occupation_b, rayleigh_a, rayleigh_a_minus, &
     946              :          rayleigh_a_plus, rayleigh_b, rayleigh_b_minus, rayleigh_b_plus, response_a, response_b, &
     947              :          weights
     948              :       REAL(KIND=dp), DIMENSION(nbands, nrotation)        :: spin_rayleigh_response
     949              :       REAL(KIND=dp), DIMENSION(nenergy)                  :: energy_direction, energy_gradient, &
     950              :                                                             response
     951              :       REAL(KIND=dp), DIMENSION(nenergy, nenergy)         :: energy_hessian
     952              :       REAL(KIND=dp), DIMENSION(nenergy, nrotation)       :: rayleigh_response
     953              :       REAL(KIND=dp), DIMENSION(nrotation)                :: rotation_direction, rotation_gradient, &
     954              :                                                             schur_rhs, spin_gradient
     955              :       REAL(KIND=dp), DIMENSION(nrotation, ngroups)       :: coupling
     956              :       REAL(KIND=dp), DIMENSION(nrotation, nrotation)     :: rotation_hessian, rotation_hessian_a, &
     957              :                                                             rotation_hessian_b, schur, &
     958              :                                                             schur_block, spin_hessian
     959              : 
     960            2 :       hamiltonian_a(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     961            2 :       hamiltonian_b(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     962            2 :       hamiltonian_a(1, 1) = -0.42_dp
     963            2 :       hamiltonian_a(2, 2) = 0.03_dp
     964            2 :       hamiltonian_a(3, 3) = 0.37_dp
     965            2 :       hamiltonian_a(1, 2) = CMPLX(0.08_dp, -0.05_dp, KIND=dp)
     966            2 :       hamiltonian_a(1, 3) = CMPLX(-0.03_dp, 0.07_dp, KIND=dp)
     967            2 :       hamiltonian_a(2, 3) = CMPLX(0.09_dp, 0.04_dp, KIND=dp)
     968            2 :       hamiltonian_b(1, 1) = -0.31_dp
     969            2 :       hamiltonian_b(2, 2) = 0.11_dp
     970            2 :       hamiltonian_b(3, 3) = 0.46_dp
     971            2 :       hamiltonian_b(1, 2) = CMPLX(-0.06_dp, 0.02_dp, KIND=dp)
     972            2 :       hamiltonian_b(1, 3) = CMPLX(0.05_dp, 0.04_dp, KIND=dp)
     973            2 :       hamiltonian_b(2, 3) = CMPLX(-0.07_dp, 0.06_dp, KIND=dp)
     974            8 :       DO i = 1, nbands
     975           12 :          hamiltonian_a(i, 1:i - 1) = CONJG(hamiltonian_a(1:i - 1, i))
     976           14 :          hamiltonian_b(i, 1:i - 1) = CONJG(hamiltonian_b(1:i - 1, i))
     977              :       END DO
     978              : 
     979            2 :       generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     980            2 :       generator(1, 2) = CMPLX(0.14_dp, -0.06_dp, KIND=dp)
     981            2 :       generator(1, 3) = CMPLX(-0.08_dp, 0.05_dp, KIND=dp)
     982            2 :       generator(2, 3) = CMPLX(0.10_dp, 0.03_dp, KIND=dp)
     983            8 :       DO i = 1, nbands
     984           14 :          generator(i, 1:i - 1) = -CONJG(generator(1:i - 1, i))
     985              :       END DO
     986            2 :       rotation_direction(:) = [0.09_dp, -0.04_dp, -0.07_dp, 0.06_dp, 0.03_dp, 0.08_dp]
     987            2 :       energy_direction(:) = [0.05_dp, -0.04_dp, 0.02_dp, -0.03_dp, 0.06_dp, -0.01_dp]
     988            2 :       direction_generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     989            2 :       direction_generator(1, 2) = CMPLX(rotation_direction(1), rotation_direction(2), KIND=dp)
     990            2 :       direction_generator(2, 1) = -CONJG(direction_generator(1, 2))
     991            2 :       direction_generator(1, 3) = CMPLX(rotation_direction(3), rotation_direction(4), KIND=dp)
     992            2 :       direction_generator(3, 1) = -CONJG(direction_generator(1, 3))
     993            2 :       direction_generator(2, 3) = CMPLX(rotation_direction(5), rotation_direction(6), KIND=dp)
     994            2 :       direction_generator(3, 2) = -CONJG(direction_generator(2, 3))
     995              : 
     996            2 :       rotation(:, :) = dense_antihermitian_exp(generator)
     997          158 :       chc_a(:, :) = MATMUL(CONJG(TRANSPOSE(rotation)), MATMUL(hamiltonian_a, rotation))
     998          158 :       chc_b(:, :) = MATMUL(CONJG(TRANSPOSE(rotation)), MATMUL(hamiltonian_b, rotation))
     999            8 :       DO i = 1, nbands
    1000            6 :          rayleigh_a(i) = REAL(chc_a(i, i), KIND=dp)
    1001            8 :          rayleigh_b(i) = REAL(chc_b(i, i), KIND=dp)
    1002              :       END DO
    1003            2 :       energy_a(:) = rayleigh_a
    1004            2 :       energy_b(:) = rayleigh_b
    1005            8 :       weights(:) = kpoint_weight
    1006              :       CALL fixed_n_fermi_occupations(energy_a, weights, 1.65_dp, temperature, &
    1007            2 :                                      maxocc, occupation_a, mu_a)
    1008              :       CALL fixed_n_fermi_occupations(energy_b, weights, 0.95_dp, temperature, &
    1009            2 :                                      maxocc, occupation_b, mu_b)
    1010            8 :       response_a(:) = weights*occupation_a*(maxocc - occupation_a)/(maxocc*temperature)
    1011            8 :       response_b(:) = weights*occupation_b*(maxocc - occupation_b)/(maxocc*temperature)
    1012            8 :       response_sum_a = SUM(response_a)
    1013            8 :       response_sum_b = SUM(response_b)
    1014            8 :       response(1:nbands) = response_a
    1015            8 :       response(nbands + 1:nenergy) = response_b
    1016            2 :       response_group(:) = [1, 1, 1, 2, 2, 2]
    1017              : 
    1018              :       CALL qs_ot_finite_rotation_response( &
    1019              :          chc_a, generator, occupation_a, kpoint_weight, spin_gradient, spin_hessian, &
    1020            2 :          spin_rayleigh_response)
    1021            2 :       rotation_gradient(:) = spin_gradient
    1022            2 :       rotation_hessian_a(:, :) = spin_hessian
    1023           50 :       rayleigh_response(1:nbands, :) = spin_rayleigh_response
    1024              :       CALL qs_ot_finite_rotation_response( &
    1025              :          chc_b, generator, occupation_b, kpoint_weight, spin_gradient, spin_hessian, &
    1026            2 :          spin_rayleigh_response)
    1027           14 :       rotation_gradient(:) = rotation_gradient + spin_gradient
    1028            2 :       rotation_hessian_b(:, :) = spin_hessian
    1029           86 :       rotation_hessian(:, :) = rotation_hessian_a + rotation_hessian_b
    1030           50 :       rayleigh_response(nbands + 1:nenergy, :) = spin_rayleigh_response
    1031              : 
    1032              :       CALL qs_ot_fixed_n_energy_gradient( &
    1033              :          rayleigh_a, energy_a, response_a, response_sum_a, 0.0_dp, &
    1034            2 :          energy_gradient(1:nbands))
    1035              :       CALL qs_ot_fixed_n_energy_gradient( &
    1036              :          rayleigh_b, energy_b, response_b, response_sum_b, 0.0_dp, &
    1037            2 :          energy_gradient(nbands + 1:nenergy))
    1038            2 :       energy_hessian(:, :) = 0.0_dp
    1039              :       CALL qs_ot_fixed_n_energy_hessian( &
    1040            2 :          response_a, response_sum_a, energy_hessian(1:nbands, 1:nbands))
    1041              :       CALL qs_ot_fixed_n_energy_hessian( &
    1042            2 :          response_b, response_sum_b, energy_hessian(nbands + 1:nenergy, nbands + 1:nenergy))
    1043              :       CALL qs_ot_fixed_n_multigroup_schur_block( &
    1044              :          rotation_hessian, rayleigh_response, response, response_group, rotation_gradient, &
    1045            2 :          energy_gradient, schur_block, coupling, schur_rhs)
    1046              :       schur(:, :) = schur_block + &
    1047              :                     MATMUL(coupling(:, 1:1), TRANSPOSE(coupling(:, 1:1)))/response_sum_a + &
    1048          280 :                     MATMUL(coupling(:, 2:2), TRANSPOSE(coupling(:, 2:2)))/response_sum_b
    1049              :       schur_error = MAXVAL(ABS(schur - rotation_hessian + &
    1050              :                                MATMUL(TRANSPOSE(rayleigh_response), &
    1051         1120 :                                       MATMUL(energy_hessian, rayleigh_response))))
    1052              : 
    1053           26 :       rotation_plus(:, :) = dense_antihermitian_exp(generator + fd_step*direction_generator)
    1054           26 :       rotation_minus(:, :) = dense_antihermitian_exp(generator - fd_step*direction_generator)
    1055            8 :       DO i = 1, nbands
    1056              :          rayleigh_a_plus(i) = REAL(DOT_PRODUCT(rotation_plus(:, i), &
    1057           96 :                                                MATMUL(hamiltonian_a, rotation_plus(:, i))), KIND=dp)
    1058              :          rayleigh_a_minus(i) = REAL(DOT_PRODUCT(rotation_minus(:, i), &
    1059          102 :                                                 MATMUL(hamiltonian_a, rotation_minus(:, i))), KIND=dp)
    1060              :          rayleigh_b_plus(i) = REAL(DOT_PRODUCT(rotation_plus(:, i), &
    1061          102 :                                                MATMUL(hamiltonian_b, rotation_plus(:, i))), KIND=dp)
    1062              :          rayleigh_b_minus(i) = REAL(DOT_PRODUCT(rotation_minus(:, i), &
    1063          104 :                                                 MATMUL(hamiltonian_b, rotation_minus(:, i))), KIND=dp)
    1064              :       END DO
    1065            8 :       energy_a_plus(:) = energy_a + fd_step*energy_direction(1:nbands)
    1066            8 :       energy_a_minus(:) = energy_a - fd_step*energy_direction(1:nbands)
    1067            8 :       energy_b_plus(:) = energy_b + fd_step*energy_direction(nbands + 1:nenergy)
    1068            8 :       energy_b_minus(:) = energy_b - fd_step*energy_direction(nbands + 1:nenergy)
    1069              :       energy_zero_a = fixed_n_mermin_value( &
    1070            2 :                       rayleigh_a, energy_a, weights, 1.65_dp, temperature, maxocc)
    1071              :       energy_zero_b = fixed_n_mermin_value( &
    1072            2 :                       rayleigh_b, energy_b, weights, 0.95_dp, temperature, maxocc)
    1073              :       energy_plus_a = fixed_n_mermin_value( &
    1074            2 :                       rayleigh_a_plus, energy_a_plus, weights, 1.65_dp, temperature, maxocc)
    1075              :       energy_plus_b = fixed_n_mermin_value( &
    1076            2 :                       rayleigh_b_plus, energy_b_plus, weights, 0.95_dp, temperature, maxocc)
    1077              :       energy_minus_a = fixed_n_mermin_value( &
    1078            2 :                        rayleigh_a_minus, energy_a_minus, weights, 1.65_dp, temperature, maxocc)
    1079              :       energy_minus_b = fixed_n_mermin_value( &
    1080            2 :                        rayleigh_b_minus, energy_b_minus, weights, 0.95_dp, temperature, maxocc)
    1081            2 :       energy_zero = energy_zero_a + energy_zero_b
    1082            2 :       energy_plus = energy_plus_a + energy_plus_b
    1083            2 :       energy_minus = energy_minus_a + energy_minus_b
    1084              :       curvature_a = DOT_PRODUCT(rotation_direction, &
    1085              :                                 MATMUL(rotation_hessian_a, rotation_direction)) - &
    1086              :                     2.0_dp*DOT_PRODUCT(energy_direction(1:nbands), &
    1087              :                                        MATMUL(energy_hessian(1:nbands, 1:nbands), &
    1088              :                                               MATMUL(rayleigh_response(1:nbands, :), &
    1089              :                                                      rotation_direction))) + &
    1090              :                     DOT_PRODUCT(energy_direction(1:nbands), &
    1091              :                                 MATMUL(energy_hessian(1:nbands, 1:nbands), &
    1092          212 :                                        energy_direction(1:nbands)))
    1093              :       curvature_b = DOT_PRODUCT(rotation_direction, &
    1094              :                                 MATMUL(rotation_hessian_b, rotation_direction)) - &
    1095              :                     2.0_dp*DOT_PRODUCT(energy_direction(nbands + 1:nenergy), &
    1096              :                                        MATMUL(energy_hessian(nbands + 1:nenergy, &
    1097              :                                                              nbands + 1:nenergy), &
    1098              :                                               MATMUL(rayleigh_response(nbands + 1:nenergy, :), &
    1099              :                                                      rotation_direction))) + &
    1100              :                     DOT_PRODUCT(energy_direction(nbands + 1:nenergy), &
    1101              :                                 MATMUL(energy_hessian(nbands + 1:nenergy, nbands + 1:nenergy), &
    1102          212 :                                        energy_direction(nbands + 1:nenergy)))
    1103            2 :       curvature = curvature_a + curvature_b
    1104              :       curvature_error = ABS((energy_plus_a - 2.0_dp*energy_zero_a + energy_minus_a)/ &
    1105            2 :                             fd_step**2 - curvature_a)
    1106              :       curvature_error = MAX( &
    1107              :                         curvature_error, &
    1108              :                         ABS((energy_plus_b - 2.0_dp*energy_zero_b + energy_minus_b)/fd_step**2 - curvature_b), &
    1109            2 :                         ABS((energy_plus - 2.0_dp*energy_zero + energy_minus)/fd_step**2 - curvature))
    1110              : 
    1111            2 :       IF (schur_error > 3.0E-11_dp .OR. curvature_error > 5.0E-6_dp) nfail = nfail + 1
    1112            2 :       IF (mynode == 0) THEN
    1113            1 :          WRITE (io_unit, '(A,2(1X,ES13.6))') "ROKS fixed-N rotation Schur errors:", &
    1114            2 :             schur_error, curvature_error
    1115              :       END IF
    1116              : 
    1117            2 :    END SUBROUTINE test_roks_fixed_n_rotation_schur
    1118              : 
    1119              : ! **************************************************************************************************
    1120              : !> \brief Check that positive and negative response modes retain their spectral magnitude.
    1121              : !> \param mynode MPI rank
    1122              : !> \param nfail accumulated number of failures
    1123              : ! **************************************************************************************************
    1124            2 :    SUBROUTINE test_symmetric_abs_solve(mynode, nfail)
    1125              :       INTEGER, INTENT(IN)                                :: mynode
    1126              :       INTEGER, INTENT(INOUT)                             :: nfail
    1127              : 
    1128              :       LOGICAL                                            :: valid
    1129              :       REAL(KIND=dp)                                      :: error
    1130              :       REAL(KIND=dp), DIMENSION(3)                        :: eigenvalues
    1131              :       REAL(KIND=dp), DIMENSION(3, 2)                     :: expected, rhs, solution
    1132              :       REAL(KIND=dp), DIMENSION(3, 3)                     :: absolute_matrix, matrix, vectors
    1133              : 
    1134            8 :       vectors(:, 1) = [0.8_dp, 0.6_dp, 0.0_dp]
    1135            8 :       vectors(:, 2) = [-0.6_dp, 0.8_dp, 0.0_dp]
    1136            8 :       vectors(:, 3) = [0.0_dp, 0.0_dp, 1.0_dp]
    1137            2 :       eigenvalues(:) = [-0.4_dp, 1.7_dp, 0.25_dp]
    1138              :       matrix(:, :) = MATMUL(vectors, MATMUL( &
    1139              :                             RESHAPE([eigenvalues(1), 0.0_dp, 0.0_dp, 0.0_dp, eigenvalues(2), 0.0_dp, &
    1140          176 :                                      0.0_dp, 0.0_dp, eigenvalues(3)], [3, 3]), TRANSPOSE(vectors)))
    1141              :       absolute_matrix(:, :) = MATMUL(vectors, MATMUL( &
    1142              :                                      RESHAPE([ABS(eigenvalues(1)), 0.0_dp, 0.0_dp, 0.0_dp, ABS(eigenvalues(2)), 0.0_dp, &
    1143          176 :                                               0.0_dp, 0.0_dp, ABS(eigenvalues(3))], [3, 3]), TRANSPOSE(vectors)))
    1144            8 :       rhs(:, 1) = [0.3_dp, -0.5_dp, 0.7_dp]
    1145            8 :       rhs(:, 2) = [-0.2_dp, 0.4_dp, 0.1_dp]
    1146            2 :       CALL qs_ot_symmetric_abs_solve(matrix, rhs, solution, valid)
    1147           54 :       expected(:, :) = MATMUL(absolute_matrix, solution)
    1148           18 :       error = MAXVAL(ABS(expected - rhs))
    1149            2 :       IF (.NOT. valid .OR. error > 2.0E-13_dp) nfail = nfail + 1
    1150              : 
    1151            2 :       eigenvalues(:) = [-0.4_dp, 1.7_dp, 1.0E-12_dp]
    1152              :       matrix(:, :) = MATMUL(vectors, MATMUL( &
    1153              :                             RESHAPE([eigenvalues(1), 0.0_dp, 0.0_dp, 0.0_dp, eigenvalues(2), 0.0_dp, &
    1154          176 :                                      0.0_dp, 0.0_dp, eigenvalues(3)], [3, 3]), TRANSPOSE(vectors)))
    1155            8 :       rhs(:, 1) = [0.3_dp, -0.5_dp, 0.7_dp]
    1156            8 :       rhs(:, 2) = [-0.2_dp, 0.4_dp, 0.1_dp]
    1157            2 :       CALL qs_ot_symmetric_abs_solve(matrix, rhs, solution, valid)
    1158            2 :       expected(:, :) = rhs(:, :)
    1159            6 :       expected(3, :) = 0.0_dp
    1160              :       absolute_matrix(:, :) = MATMUL(vectors(:, 1:2), MATMUL( &
    1161              :                                      RESHAPE([ABS(eigenvalues(1)), 0.0_dp, 0.0_dp, ABS(eigenvalues(2))], [2, 2]), &
    1162          106 :                                      TRANSPOSE(vectors(:, 1:2))))
    1163           72 :       error = MAX(error, MAXVAL(ABS(MATMUL(absolute_matrix, solution) - expected)))
    1164            6 :       IF (.NOT. valid .OR. error > 2.0E-13_dp .OR. MAXVAL(ABS(solution(3, :))) > 2.0E-13_dp) THEN
    1165            0 :          nfail = nfail + 1
    1166              :       END IF
    1167            2 :       IF (mynode == 0) WRITE (io_unit, '(A,1X,ES13.6)') "symmetric absolute solve error:", error
    1168              : 
    1169            2 :    END SUBROUTINE test_symmetric_abs_solve
    1170              : 
    1171              : ! **************************************************************************************************
    1172              : !> \brief Check that the projected response update preserves the baseline and rejects indefiniteness.
    1173              : !> \param mynode MPI rank
    1174              : !> \param nfail accumulated number of failures
    1175              : ! **************************************************************************************************
    1176            2 :    SUBROUTINE test_projected_response_update(mynode, nfail)
    1177              :       INTEGER, INTENT(IN)                                :: mynode
    1178              :       INTEGER, INTENT(INOUT)                             :: nfail
    1179              : 
    1180              :       LOGICAL                                            :: valid
    1181              :       REAL(KIND=dp)                                      :: error, preservation_error
    1182              :       REAL(KIND=dp), DIMENSION(2)                        :: coefficients, gradient, residual
    1183              :       REAL(KIND=dp), DIMENSION(2, 2)                     :: correction, reference, total
    1184              : 
    1185            2 :       reference(:, :) = RESHAPE([4.0_dp, 0.7_dp, 0.7_dp, 2.5_dp], [2, 2])
    1186            2 :       correction(:, :) = 0.0_dp
    1187            2 :       CALL qs_ot_projected_response_update(reference, correction, coefficients, valid)
    1188            6 :       preservation_error = MAXVAL(ABS(coefficients - [1.0_dp, 0.0_dp]))
    1189            2 :       IF (.NOT. valid .OR. preservation_error > 1.0E-12_dp) nfail = nfail + 1
    1190              : 
    1191            2 :       correction(:, :) = RESHAPE([0.8_dp, -0.3_dp, -0.3_dp, 0.4_dp], [2, 2])
    1192            2 :       gradient(:) = [1.2_dp, -0.4_dp]
    1193              :       CALL qs_ot_projected_response_update( &
    1194            2 :          reference, correction, coefficients, valid, projected_gradient=gradient)
    1195           14 :       total(:, :) = reference + correction
    1196           20 :       residual(:) = MATMUL(total, coefficients) - gradient
    1197            6 :       error = MAXVAL(ABS(residual))
    1198            2 :       IF (.NOT. valid .OR. error > 1.0E-12_dp) nfail = nfail + 1
    1199              : 
    1200            2 :       correction(:, :) = RESHAPE([-8.0_dp, 0.0_dp, 0.0_dp, 0.0_dp], [2, 2])
    1201            2 :       CALL qs_ot_projected_response_update(reference, correction, coefficients, valid)
    1202            2 :       IF (valid) nfail = nfail + 1
    1203            2 :       IF (mynode == 0) WRITE (io_unit, '(A,2(1X,ES14.6))') &
    1204            1 :          "projected response preservation/residual errors:", preservation_error, error
    1205              : 
    1206            2 :    END SUBROUTINE test_projected_response_update
    1207              : 
    1208              : ! **************************************************************************************************
    1209              : !> \brief Check exact signed SR1 secants and rejection of an unresolved orthogonal residual.
    1210              : !> \param mynode MPI rank
    1211              : !> \param nfail accumulated number of failures
    1212              : ! **************************************************************************************************
    1213            2 :    SUBROUTINE test_symmetric_sr1_update(mynode, nfail)
    1214              :       INTEGER, INTENT(IN)                                :: mynode
    1215              :       INTEGER, INTENT(INOUT)                             :: nfail
    1216              : 
    1217              :       LOGICAL                                            :: valid
    1218              :       REAL(KIND=dp)                                      :: rejected_error, secant_error, &
    1219              :                                                             symmetry_error
    1220              :       REAL(KIND=dp), DIMENSION(3)                        :: orthogonal_residual, response, step
    1221              :       REAL(KIND=dp), DIMENSION(3, 3)                     :: matrix, updated
    1222              : 
    1223            2 :       matrix(:, :) = 0.0_dp
    1224            2 :       matrix(1, 1) = 1.0_dp
    1225            2 :       matrix(2, 2) = 2.0_dp
    1226            2 :       matrix(3, 3) = 3.0_dp
    1227            2 :       step(:) = [0.3_dp, -0.4_dp, 0.2_dp]
    1228            2 :       response(:) = [0.0_dp, -0.3_dp, 0.8_dp]
    1229            2 :       CALL qs_ot_symmetric_sr1_update(matrix, step, response, updated, valid)
    1230           34 :       secant_error = MAXVAL(ABS(MATMUL(updated, step) - response))
    1231           26 :       symmetry_error = MAXVAL(ABS(updated - TRANSPOSE(updated)))
    1232            2 :       IF (.NOT. valid .OR. secant_error > 2.0E-14_dp .OR. symmetry_error > 2.0E-14_dp) THEN
    1233            0 :          nfail = nfail + 1
    1234              :       END IF
    1235              : 
    1236            2 :       orthogonal_residual(:) = [0.4_dp, 0.3_dp, 0.0_dp]
    1237           34 :       response(:) = MATMUL(matrix, step) + orthogonal_residual
    1238            2 :       CALL qs_ot_symmetric_sr1_update(matrix, step, response, updated, valid)
    1239           26 :       rejected_error = MAXVAL(ABS(updated - matrix))
    1240            2 :       IF (valid .OR. rejected_error > 2.0E-14_dp) nfail = nfail + 1
    1241            2 :       IF (mynode == 0) WRITE (io_unit, '(A,3(1X,ES13.6))') &
    1242            1 :          "symmetric SR1 secant/symmetry/rejection errors:", &
    1243            2 :          secant_error, symmetry_error, rejected_error
    1244              : 
    1245            2 :    END SUBROUTINE test_symmetric_sr1_update
    1246              : 
    1247              : ! **************************************************************************************************
    1248              : !> \brief Check the complex gauge-invariant, sign-aware density-space response secant.
    1249              : !> \param mynode MPI rank
    1250              : !> \param nfail accumulated number of failures
    1251              : ! **************************************************************************************************
    1252            2 :    SUBROUTINE test_density_secant_hessian(mynode, nfail)
    1253              :       INTEGER, INTENT(IN)                                :: mynode
    1254              :       INTEGER, INTENT(INOUT)                             :: nfail
    1255              : 
    1256              :       INTEGER, PARAMETER                                 :: n = 3, nmode = 2
    1257              :       REAL(KIND=dp), PARAMETER                           :: secant_position = 0.4_dp
    1258              : 
    1259              :       COMPLEX(KIND=dp), DIMENSION(n, n)                  :: density_step, hamiltonian_step, unitary
    1260              :       COMPLEX(KIND=dp), DIMENSION(n, n, nmode)           :: density_modes, transformed_modes
    1261              :       LOGICAL                                            :: calibrated_valid, rejected_valid, valid
    1262              :       REAL(KIND=dp) :: calibrated_error, density_norm, direct_error, gauge_error, overlap_error, &
    1263              :          rejected_error, response_work, sign_error
    1264              :       REAL(KIND=dp), DIMENSION(nmode)                    :: density_overlap, response_overlap
    1265              :       REAL(KIND=dp), DIMENSION(nmode, nmode)             :: calibrated, correction, &
    1266              :                                                             correction_gauge, correction_overlap, &
    1267              :                                                             expected, rejected
    1268              : 
    1269            2 :       density_step(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1270            2 :       density_step(1, 1) = CMPLX(0.30_dp, 0.0_dp, KIND=dp)
    1271            2 :       density_step(2, 2) = CMPLX(-0.18_dp, 0.0_dp, KIND=dp)
    1272            2 :       density_step(3, 3) = CMPLX(-0.12_dp, 0.0_dp, KIND=dp)
    1273            2 :       density_step(1, 2) = CMPLX(0.07_dp, -0.11_dp, KIND=dp)
    1274            2 :       density_step(2, 1) = CONJG(density_step(1, 2))
    1275            2 :       density_step(1, 3) = CMPLX(-0.04_dp, 0.05_dp, KIND=dp)
    1276            2 :       density_step(3, 1) = CONJG(density_step(1, 3))
    1277              : 
    1278           26 :       hamiltonian_step(:, :) = -0.45_dp*density_step
    1279            2 :       hamiltonian_step(2, 3) = CMPLX(0.06_dp, 0.03_dp, KIND=dp)
    1280            2 :       hamiltonian_step(3, 2) = CONJG(hamiltonian_step(2, 3))
    1281           26 :       density_modes(:, :, 1) = density_step
    1282           26 :       density_modes(:, :, 2) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1283            2 :       density_modes(1, 1, 2) = CMPLX(-0.2_dp, 0.0_dp, KIND=dp)
    1284            2 :       density_modes(2, 2, 2) = CMPLX(0.1_dp, 0.0_dp, KIND=dp)
    1285            2 :       density_modes(3, 3, 2) = CMPLX(0.1_dp, 0.0_dp, KIND=dp)
    1286            2 :       density_modes(1, 3, 2) = CMPLX(0.03_dp, 0.08_dp, KIND=dp)
    1287            2 :       density_modes(3, 1, 2) = CONJG(density_modes(1, 3, 2))
    1288              : 
    1289              :       CALL qs_ot_density_secant_hessian( &
    1290              :          density_step, hamiltonian_step, density_modes, correction, valid, &
    1291            2 :          density_norm, response_work)
    1292            2 :       density_overlap(1) = density_norm
    1293           26 :       density_overlap(2) = SUM(REAL(CONJG(density_step)*density_modes(:, :, 2), KIND=dp))
    1294            2 :       response_overlap(1) = response_work
    1295           26 :       response_overlap(2) = SUM(REAL(CONJG(hamiltonian_step)*density_modes(:, :, 2), KIND=dp))
    1296              :       CALL qs_ot_density_secant_projected_hessian( &
    1297            2 :          density_norm, response_work, density_overlap, response_overlap, correction_overlap, valid)
    1298              : 
    1299            2 :       expected(1, 1) = response_work
    1300            2 :       expected(1, 2) = response_overlap(2)
    1301            2 :       expected(2, 1) = response_overlap(2)
    1302              :       expected(2, 2) = (2.0_dp*density_overlap(2)*response_overlap(2) - &
    1303            2 :                         response_work*density_overlap(2)**2/density_norm)/density_norm
    1304           14 :       direct_error = MAXVAL(ABS(correction - expected))
    1305           14 :       overlap_error = MAXVAL(ABS(correction_overlap - correction))
    1306            2 :       sign_error = MAX(0.0_dp, correction(1, 1))
    1307              : 
    1308              :       CALL qs_ot_density_secant_projected_hessian( &
    1309              :          density_norm, response_work, density_overlap, response_overlap, calibrated, &
    1310            2 :          calibrated_valid, secant_mode=2, secant_position=secant_position)
    1311              :       expected(1, 1) = response_work
    1312            2 :       expected(1, 2) = response_work/secant_position
    1313            2 :       expected(2, 1) = expected(1, 2)
    1314            2 :       expected(2, 2) = response_work/secant_position**2
    1315           14 :       calibrated_error = MAXVAL(ABS(calibrated - expected))
    1316              :       CALL qs_ot_density_secant_projected_hessian( &
    1317              :          density_norm, response_work, density_overlap, response_overlap, rejected, &
    1318            2 :          rejected_valid, secant_mode=2, secant_position=0.0_dp)
    1319           14 :       rejected_error = MAXVAL(ABS(rejected))
    1320              : 
    1321            2 :       unitary(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1322            2 :       unitary(1, 1) = CMPLX(SQRT(0.5_dp), 0.0_dp, KIND=dp)
    1323            2 :       unitary(2, 1) = CMPLX(0.0_dp, SQRT(0.5_dp), KIND=dp)
    1324            2 :       unitary(1, 2) = CMPLX(0.0_dp, SQRT(0.5_dp), KIND=dp)
    1325            2 :       unitary(2, 2) = CMPLX(SQRT(0.5_dp), 0.0_dp, KIND=dp)
    1326            2 :       unitary(3, 3) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
    1327          158 :       density_step = MATMUL(CONJG(TRANSPOSE(unitary)), MATMUL(density_step, unitary))
    1328          158 :       hamiltonian_step = MATMUL(CONJG(TRANSPOSE(unitary)), MATMUL(hamiltonian_step, unitary))
    1329              :       transformed_modes(:, :, 1) = &
    1330          186 :          MATMUL(CONJG(TRANSPOSE(unitary)), MATMUL(density_modes(:, :, 1), unitary))
    1331              :       transformed_modes(:, :, 2) = &
    1332          186 :          MATMUL(CONJG(TRANSPOSE(unitary)), MATMUL(density_modes(:, :, 2), unitary))
    1333              :       CALL qs_ot_density_secant_hessian( &
    1334            2 :          density_step, hamiltonian_step, transformed_modes, correction_gauge, valid)
    1335           14 :       gauge_error = MAXVAL(ABS(correction_gauge - correction))
    1336              : 
    1337              :       IF (.NOT. valid .OR. .NOT. calibrated_valid .OR. rejected_valid .OR. &
    1338              :           direct_error > 2.0E-14_dp .OR. overlap_error > 2.0E-14_dp .OR. &
    1339              :           gauge_error > 2.0E-14_dp .OR. sign_error > 2.0E-14_dp .OR. &
    1340            2 :           calibrated_error > 2.0E-14_dp .OR. rejected_error > 2.0E-14_dp) nfail = nfail + 1
    1341            2 :       IF (mynode == 0) WRITE (io_unit, '(A,6(1X,ES13.6))') &
    1342            1 :          "density secant direct/overlap/gauge/sign/calibrated/rejected errors:", &
    1343            2 :          direct_error, overlap_error, gauge_error, sign_error, calibrated_error, rejected_error
    1344              : 
    1345            2 :    END SUBROUTINE test_density_secant_hessian
    1346              : 
    1347              : ! **************************************************************************************************
    1348              : !> \brief Check that the Hxc secant is formed in the joint direct sum of both spin channels.
    1349              : !> \param mynode MPI rank
    1350              : !> \param nfail accumulated number of failures
    1351              : ! **************************************************************************************************
    1352            2 :    SUBROUTINE test_joint_spin_density_secant(mynode, nfail)
    1353              :       INTEGER, INTENT(IN)                                :: mynode
    1354              :       INTEGER, INTENT(INOUT)                             :: nfail
    1355              : 
    1356              :       INTEGER, PARAMETER                                 :: n = 2, nmode = 2, nspin = 2, &
    1357              :                                                             ntotal = n*nspin
    1358              : 
    1359              :       COMPLEX(KIND=dp), DIMENSION(n, n, nmode, nspin)    :: density_modes
    1360              :       COMPLEX(KIND=dp), DIMENSION(n, n, nspin)           :: density_step, hamiltonian_step
    1361              :       COMPLEX(KIND=dp), DIMENSION(ntotal, ntotal)        :: direct_density_step, &
    1362              :                                                             direct_hamiltonian_step
    1363              :       COMPLEX(KIND=dp), DIMENSION(ntotal, ntotal, nmode) :: direct_density_modes
    1364              :       INTEGER                                            :: first, ispin, last, mode
    1365              :       LOGICAL                                            :: direct_valid, joint_valid, spin_valid
    1366              :       REAL(KIND=dp)                                      :: coupling_size, density_norm, &
    1367              :                                                             direct_error, response_work
    1368              :       REAL(KIND=dp), DIMENSION(nmode)                    :: density_overlap, response_overlap
    1369              :       REAL(KIND=dp), DIMENSION(nmode, nmode)             :: direct_correction, joint_correction, &
    1370              :                                                             separate_correction, spin_correction
    1371              : 
    1372              :       density_step(:, :, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1373            2 :       density_step(1, 1, 1) = 0.31_dp
    1374            2 :       density_step(2, 2, 1) = -0.31_dp
    1375            2 :       density_step(1, 2, 1) = CMPLX(0.08_dp, -0.04_dp, KIND=dp)
    1376            2 :       density_step(2, 1, 1) = CONJG(density_step(1, 2, 1))
    1377            2 :       density_step(1, 1, 2) = -0.19_dp
    1378            2 :       density_step(2, 2, 2) = 0.19_dp
    1379            2 :       density_step(1, 2, 2) = CMPLX(-0.05_dp, 0.06_dp, KIND=dp)
    1380            2 :       density_step(2, 1, 2) = CONJG(density_step(1, 2, 2))
    1381              : 
    1382           14 :       hamiltonian_step(:, :, 1) = 0.67_dp*density_step(:, :, 1)
    1383            2 :       hamiltonian_step(1, 2, 1) = hamiltonian_step(1, 2, 1) + CMPLX(-0.03_dp, 0.09_dp, KIND=dp)
    1384            2 :       hamiltonian_step(2, 1, 1) = CONJG(hamiltonian_step(1, 2, 1))
    1385           14 :       hamiltonian_step(:, :, 2) = -0.43_dp*density_step(:, :, 2)
    1386            2 :       hamiltonian_step(1, 2, 2) = hamiltonian_step(1, 2, 2) + CMPLX(0.07_dp, 0.02_dp, KIND=dp)
    1387            2 :       hamiltonian_step(2, 1, 2) = CONJG(hamiltonian_step(1, 2, 2))
    1388              : 
    1389              :       density_modes(:, :, :, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1390            2 :       density_modes(1, 1, 1, 1) = 0.21_dp
    1391            2 :       density_modes(2, 2, 1, 1) = -0.21_dp
    1392            2 :       density_modes(1, 2, 1, 1) = CMPLX(0.04_dp, 0.03_dp, KIND=dp)
    1393            2 :       density_modes(2, 1, 1, 1) = CONJG(density_modes(1, 2, 1, 1))
    1394            2 :       density_modes(1, 1, 2, 1) = -0.13_dp
    1395            2 :       density_modes(2, 2, 2, 1) = 0.13_dp
    1396            2 :       density_modes(1, 2, 2, 1) = CMPLX(-0.02_dp, 0.05_dp, KIND=dp)
    1397            2 :       density_modes(2, 1, 2, 1) = CONJG(density_modes(1, 2, 2, 1))
    1398            2 :       density_modes(1, 1, 1, 2) = -0.17_dp
    1399            2 :       density_modes(2, 2, 1, 2) = 0.17_dp
    1400            2 :       density_modes(1, 2, 1, 2) = CMPLX(0.06_dp, -0.01_dp, KIND=dp)
    1401            2 :       density_modes(2, 1, 1, 2) = CONJG(density_modes(1, 2, 1, 2))
    1402            2 :       density_modes(1, 1, 2, 2) = 0.09_dp
    1403            2 :       density_modes(2, 2, 2, 2) = -0.09_dp
    1404            2 :       density_modes(1, 2, 2, 2) = CMPLX(0.03_dp, 0.04_dp, KIND=dp)
    1405            2 :       density_modes(2, 1, 2, 2) = CONJG(density_modes(1, 2, 2, 2))
    1406              : 
    1407            2 :       direct_density_step(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1408            2 :       direct_hamiltonian_step(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1409            2 :       direct_density_modes(:, :, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1410            2 :       density_norm = 0.0_dp
    1411            2 :       response_work = 0.0_dp
    1412            2 :       density_overlap(:) = 0.0_dp
    1413            2 :       response_overlap(:) = 0.0_dp
    1414            2 :       separate_correction(:, :) = 0.0_dp
    1415            2 :       spin_valid = .TRUE.
    1416            6 :       DO ispin = 1, nspin
    1417            4 :          first = (ispin - 1)*n + 1
    1418            4 :          last = ispin*n
    1419           28 :          direct_density_step(first:last, first:last) = density_step(:, :, ispin)
    1420           28 :          direct_hamiltonian_step(first:last, first:last) = hamiltonian_step(:, :, ispin)
    1421           60 :          direct_density_modes(first:last, first:last, :) = density_modes(:, :, :, ispin)
    1422              :          density_norm = density_norm + &
    1423              :                         SUM(REAL(CONJG(density_step(:, :, ispin))* &
    1424           28 :                                  density_step(:, :, ispin), KIND=dp))
    1425              :          response_work = response_work + &
    1426              :                          SUM(REAL(CONJG(density_step(:, :, ispin))* &
    1427           28 :                                   hamiltonian_step(:, :, ispin), KIND=dp))
    1428           12 :          DO mode = 1, nmode
    1429              :             density_overlap(mode) = density_overlap(mode) + &
    1430              :                                     SUM(REAL(CONJG(density_step(:, :, ispin))* &
    1431           56 :                                              density_modes(:, :, mode, ispin), KIND=dp))
    1432              :             response_overlap(mode) = response_overlap(mode) + &
    1433              :                                      SUM(REAL(CONJG(hamiltonian_step(:, :, ispin))* &
    1434           60 :                                               density_modes(:, :, mode, ispin), KIND=dp))
    1435              :          END DO
    1436              :          CALL qs_ot_density_secant_hessian( &
    1437              :             density_step(:, :, ispin), hamiltonian_step(:, :, ispin), &
    1438            4 :             density_modes(:, :, :, ispin), spin_correction, direct_valid)
    1439            4 :          spin_valid = spin_valid .AND. direct_valid
    1440           30 :          separate_correction = separate_correction + spin_correction
    1441              :       END DO
    1442              : 
    1443              :       CALL qs_ot_density_secant_hessian( &
    1444              :          direct_density_step, direct_hamiltonian_step, direct_density_modes, &
    1445            2 :          direct_correction, direct_valid)
    1446              :       CALL qs_ot_density_secant_projected_hessian( &
    1447              :          density_norm, response_work, density_overlap, response_overlap, &
    1448            2 :          joint_correction, joint_valid)
    1449           14 :       direct_error = MAXVAL(ABS(joint_correction - direct_correction))
    1450           14 :       coupling_size = MAXVAL(ABS(joint_correction - separate_correction))
    1451              : 
    1452              :       IF (.NOT. direct_valid .OR. .NOT. joint_valid .OR. .NOT. spin_valid .OR. &
    1453            2 :           direct_error > 3.0E-14_dp .OR. coupling_size < 1.0E-5_dp) nfail = nfail + 1
    1454            2 :       IF (mynode == 0) WRITE (io_unit, '(A,2(1X,ES13.6))') &
    1455            1 :          "joint-spin density secant direct error/coupling:", direct_error, coupling_size
    1456              : 
    1457            2 :    END SUBROUTINE test_joint_spin_density_secant
    1458              : 
    1459              : ! **************************************************************************************************
    1460              : !> \brief Check the coupled finite-chart density tangent against a central finite difference.
    1461              : !> \param mynode MPI rank
    1462              : !> \param nfail accumulated number of failures
    1463              : ! **************************************************************************************************
    1464            2 :    SUBROUTINE test_density_tangent(mynode, nfail)
    1465              :       INTEGER, INTENT(IN)                                :: mynode
    1466              :       INTEGER, INTENT(INOUT)                             :: nfail
    1467              : 
    1468              :       INTEGER, PARAMETER                                 :: nbands = 3, nrotation = 6
    1469              :       REAL(KIND=dp), PARAMETER                           :: fd_step = 2.0E-6_dp, &
    1470              :                                                             kpoint_weight = 0.625_dp
    1471              : 
    1472              :       COMPLEX(KIND=dp), DIMENSION(nbands, nbands) :: density_minus, density_plus, density_tangent, &
    1473              :          direction_generator, generator, rotation, rotation_minus, rotation_plus, weighted_rotation
    1474              :       INTEGER                                            :: i, j, r
    1475              :       REAL(KIND=dp)                                      :: error, trace_error
    1476              :       REAL(KIND=dp), DIMENSION(nbands)                   :: occupation, weighted_occupation_step
    1477              :       REAL(KIND=dp), DIMENSION(nrotation)                :: rotation_step
    1478              : 
    1479            2 :       generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1480            2 :       generator(1, 2) = CMPLX(0.17_dp, -0.09_dp, KIND=dp)
    1481            2 :       generator(2, 1) = -CONJG(generator(1, 2))
    1482            2 :       generator(1, 3) = CMPLX(-0.06_dp, 0.11_dp, KIND=dp)
    1483            2 :       generator(3, 1) = -CONJG(generator(1, 3))
    1484            2 :       generator(2, 3) = CMPLX(0.08_dp, 0.04_dp, KIND=dp)
    1485            2 :       generator(3, 2) = -CONJG(generator(2, 3))
    1486            2 :       occupation(:) = [1.72_dp, 0.83_dp, 0.31_dp]
    1487            2 :       rotation_step(:) = [0.21_dp, -0.14_dp, -0.08_dp, 0.17_dp, 0.11_dp, 0.06_dp]
    1488            2 :       weighted_occupation_step(:) = [-0.037_dp, 0.052_dp, -0.015_dp]
    1489              : 
    1490              :       CALL qs_ot_density_tangent( &
    1491              :          generator, occupation, kpoint_weight, rotation_step, weighted_occupation_step, &
    1492            2 :          density_tangent, difference_step=fd_step)
    1493              : 
    1494            2 :       direction_generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1495            2 :       r = 0
    1496            6 :       DO i = 1, nbands - 1
    1497           12 :          DO j = i + 1, nbands
    1498            6 :             r = r + 1
    1499            6 :             direction_generator(i, j) = CMPLX(rotation_step(r), 0.0_dp, KIND=dp)
    1500            6 :             direction_generator(j, i) = -direction_generator(i, j)
    1501            6 :             r = r + 1
    1502              :             direction_generator(i, j) = direction_generator(i, j) + &
    1503            6 :                                         CMPLX(0.0_dp, rotation_step(r), KIND=dp)
    1504              :             direction_generator(j, i) = direction_generator(j, i) + &
    1505           10 :                                         CMPLX(0.0_dp, rotation_step(r), KIND=dp)
    1506              :          END DO
    1507              :       END DO
    1508            2 :       rotation = dense_antihermitian_exp(generator)
    1509           26 :       rotation_plus = dense_antihermitian_exp(generator + fd_step*direction_generator)
    1510           26 :       rotation_minus = dense_antihermitian_exp(generator - fd_step*direction_generator)
    1511            2 :       weighted_rotation(:, :) = rotation_plus
    1512            8 :       DO j = 1, nbands
    1513              :          weighted_rotation(:, j) = &
    1514              :             (kpoint_weight*occupation(j) + fd_step*weighted_occupation_step(j))* &
    1515           26 :             weighted_rotation(:, j)
    1516              :       END DO
    1517           80 :       density_plus(:, :) = MATMUL(weighted_rotation, CONJG(TRANSPOSE(rotation_plus)))
    1518            2 :       weighted_rotation(:, :) = rotation_minus
    1519            8 :       DO j = 1, nbands
    1520              :          weighted_rotation(:, j) = &
    1521              :             (kpoint_weight*occupation(j) - fd_step*weighted_occupation_step(j))* &
    1522           26 :             weighted_rotation(:, j)
    1523              :       END DO
    1524           80 :       density_minus(:, :) = MATMUL(weighted_rotation, CONJG(TRANSPOSE(rotation_minus)))
    1525              :       density_plus(:, :) = MATMUL(CONJG(TRANSPOSE(rotation)), &
    1526          182 :                                   MATMUL((density_plus - density_minus)/(2.0_dp*fd_step), rotation))
    1527           26 :       error = MAXVAL(ABS(density_tangent - density_plus))
    1528              :       trace_error = ABS(REAL(SUM([(density_tangent(i, i), i=1, nbands)]), KIND=dp) - &
    1529            8 :                         SUM(weighted_occupation_step))
    1530              : 
    1531            2 :       IF (error > 3.0E-9_dp .OR. trace_error > 3.0E-10_dp) nfail = nfail + 1
    1532            2 :       IF (mynode == 0) WRITE (io_unit, '(A,2(1X,ES13.6))') &
    1533            1 :          "finite complex density tangent errors:", error, trace_error
    1534              : 
    1535            2 :    END SUBROUTINE test_density_tangent
    1536              : 
    1537              : ! **************************************************************************************************
    1538              : !> \brief Check density-secants when accepted endpoint orbital subspaces differ.
    1539              : !> \param mynode MPI rank
    1540              : !> \param nfail accumulated number of failures
    1541              : ! **************************************************************************************************
    1542            2 :    SUBROUTINE test_density_secant_moving_subspace(mynode, nfail)
    1543              :       INTEGER, INTENT(IN)                                :: mynode
    1544              :       INTEGER, INTENT(INOUT)                             :: nfail
    1545              : 
    1546              :       INTEGER, PARAMETER                                 :: nao = 4, nbands = 3, nmode = 2
    1547              :       REAL(KIND=dp), PARAMETER                           :: weight = 0.625_dp
    1548              : 
    1549              :       COMPLEX(KIND=dp), DIMENSION(nao, nao)              :: density_step, hamiltonian_step
    1550              :       COMPLEX(KIND=dp), DIMENSION(nao, nao, nmode)       :: density_modes_ao
    1551              :       COMPLEX(KIND=dp), DIMENSION(nao, nbands)           :: c0, c1
    1552              :       COMPLEX(KIND=dp), DIMENSION(nbands, nbands)        :: h0, h1, overlap
    1553              :       COMPLEX(KIND=dp), DIMENSION(nbands, nbands, nmode) :: density_modes
    1554              :       INTEGER                                            :: i
    1555              :       LOGICAL                                            :: valid
    1556              :       REAL(KIND=dp)                                      :: density_norm, density_norm_ref, error, &
    1557              :                                                             response_work, response_work_ref
    1558              :       REAL(KIND=dp), DIMENSION(nbands)                   :: occupation0, occupation1
    1559              :       REAL(KIND=dp), DIMENSION(nmode)                    :: density_overlap, density_overlap_ref, &
    1560              :                                                             response_overlap, response_overlap_ref
    1561              : 
    1562            2 :       c0(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1563            2 :       c0(1, 1) = 1.0_dp
    1564            2 :       c0(2, 2) = 1.0_dp
    1565            2 :       c0(3, 3) = 1.0_dp
    1566            2 :       c1(:, :) = c0
    1567            2 :       c1(1, 1) = COS(0.31_dp)
    1568            2 :       c1(4, 1) = SIN(0.31_dp)
    1569            2 :       c1(2, 2) = COS(0.23_dp)
    1570            2 :       c1(3, 2) = CMPLX(0.0_dp, SIN(0.23_dp), KIND=dp)
    1571            2 :       c1(2, 3) = CMPLX(0.0_dp, SIN(0.23_dp), KIND=dp)
    1572            2 :       c1(3, 3) = COS(0.23_dp)
    1573            2 :       occupation0(:) = [1.73_dp, 0.82_dp, 0.82_dp]
    1574            2 :       occupation1(:) = [1.61_dp, 0.91_dp, 0.76_dp]
    1575              : 
    1576            2 :       hamiltonian_step(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1577            2 :       hamiltonian_step(1, 1) = 0.21_dp
    1578            2 :       hamiltonian_step(2, 2) = -0.17_dp
    1579            2 :       hamiltonian_step(3, 3) = 0.09_dp
    1580            2 :       hamiltonian_step(4, 4) = -0.04_dp
    1581            2 :       hamiltonian_step(1, 4) = CMPLX(0.07_dp, -0.03_dp, KIND=dp)
    1582            2 :       hamiltonian_step(4, 1) = CONJG(hamiltonian_step(1, 4))
    1583            2 :       hamiltonian_step(2, 3) = CMPLX(-0.05_dp, 0.08_dp, KIND=dp)
    1584            2 :       hamiltonian_step(3, 2) = CONJG(hamiltonian_step(2, 3))
    1585            2 :       density_modes(:, :, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1586            2 :       density_modes(1, 1, 1) = 0.13_dp
    1587            2 :       density_modes(2, 2, 1) = -0.08_dp
    1588            2 :       density_modes(3, 3, 1) = -0.05_dp
    1589            2 :       density_modes(1, 2, 2) = CMPLX(0.04_dp, -0.06_dp, KIND=dp)
    1590            2 :       density_modes(2, 1, 2) = CONJG(density_modes(1, 2, 2))
    1591              : 
    1592           98 :       overlap(:, :) = MATMUL(CONJG(TRANSPOSE(c0)), c1)
    1593              :       ! Keep the reference path independent of optimized complex MATMUL expressions.
    1594            2 :       CALL reference_project_operator(c0, hamiltonian_step, h0)
    1595            2 :       CALL reference_project_operator(c1, hamiltonian_step, h1)
    1596              :       CALL qs_ot_density_secant_orbital_overlaps( &
    1597              :          overlap, occupation0, occupation1, h0, h1, density_modes, weight, density_norm, &
    1598            2 :          response_work, density_overlap, response_overlap, valid)
    1599              : 
    1600            2 :       density_step(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1601            8 :       DO i = 1, nbands
    1602              :          density_step = density_step + weight*occupation1(i)* &
    1603              :                         SPREAD(c1(:, i), DIM=2, NCOPIES=nao)* &
    1604              :                         SPREAD(CONJG(c1(:, i)), DIM=1, NCOPIES=nao) - &
    1605              :                         weight*occupation0(i)* &
    1606              :                         SPREAD(c0(:, i), DIM=2, NCOPIES=nao)* &
    1607          176 :                         SPREAD(CONJG(c0(:, i)), DIM=1, NCOPIES=nao)
    1608              :       END DO
    1609            6 :       DO i = 1, nmode
    1610            4 :          CALL reference_transform_density_mode(c1, density_modes(:, :, i), density_modes_ao(:, :, i))
    1611              :          density_overlap_ref(i) = &
    1612            4 :             reference_frobenius_product_real(density_step, density_modes_ao(:, :, i))/weight
    1613              :          response_overlap_ref(i) = &
    1614            6 :             reference_frobenius_product_real(hamiltonian_step, density_modes_ao(:, :, i))
    1615              :       END DO
    1616            2 :       density_norm_ref = reference_frobenius_product_real(density_step, density_step)/weight
    1617            2 :       response_work_ref = reference_frobenius_product_real(density_step, hamiltonian_step)
    1618            2 :       error = MAX(ABS(density_norm - density_norm_ref), ABS(response_work - response_work_ref))
    1619            6 :       error = MAX(error, MAXVAL(ABS(density_overlap - density_overlap_ref)))
    1620            6 :       error = MAX(error, MAXVAL(ABS(response_overlap - response_overlap_ref)))
    1621              : 
    1622            2 :       IF (.NOT. valid .OR. error > 3.0E-13_dp) nfail = nfail + 1
    1623            2 :       IF (mynode == 0) WRITE (io_unit, '(A,1X,ES13.6)') &
    1624            1 :          "moving-subspace density secant error:", error
    1625              : 
    1626            2 :    END SUBROUTINE test_density_secant_moving_subspace
    1627              : 
    1628              : ! **************************************************************************************************
    1629              : !> \brief Reference projection C^H H C using scalar operations.
    1630              : !> \param orbitals orbital coefficient matrix C
    1631              : !> \param hamiltonian operator matrix H
    1632              : !> \param projected projected operator
    1633              : ! **************************************************************************************************
    1634            4 :    SUBROUTINE reference_project_operator(orbitals, hamiltonian, projected)
    1635              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: orbitals, hamiltonian
    1636              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT)     :: projected
    1637              : 
    1638              :       COMPLEX(KIND=dp)                                   :: value
    1639              :       INTEGER                                            :: i, j, mu, nu
    1640              : 
    1641           16 :       DO j = 1, SIZE(orbitals, 2)
    1642           52 :          DO i = 1, SIZE(orbitals, 2)
    1643           36 :             value = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1644          180 :             DO nu = 1, SIZE(orbitals, 1)
    1645          756 :                DO mu = 1, SIZE(orbitals, 1)
    1646          720 :                   value = value + CONJG(orbitals(mu, i))*hamiltonian(mu, nu)*orbitals(nu, j)
    1647              :                END DO
    1648              :             END DO
    1649           48 :             projected(i, j) = value
    1650              :          END DO
    1651              :       END DO
    1652              : 
    1653            4 :    END SUBROUTINE reference_project_operator
    1654              : 
    1655              : ! **************************************************************************************************
    1656              : !> \brief Reference transformation C M C^H using scalar operations.
    1657              : !> \param orbitals orbital coefficient matrix C
    1658              : !> \param mode density mode M
    1659              : !> \param transformed transformed density mode
    1660              : ! **************************************************************************************************
    1661            4 :    SUBROUTINE reference_transform_density_mode(orbitals, mode, transformed)
    1662              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: orbitals, mode
    1663              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT)     :: transformed
    1664              : 
    1665              :       COMPLEX(KIND=dp)                                   :: value
    1666              :       INTEGER                                            :: i, j, m, n
    1667              : 
    1668           20 :       DO j = 1, SIZE(orbitals, 1)
    1669           84 :          DO i = 1, SIZE(orbitals, 1)
    1670           64 :             value = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    1671          256 :             DO n = 1, SIZE(orbitals, 2)
    1672          832 :                DO m = 1, SIZE(orbitals, 2)
    1673          768 :                   value = value + orbitals(i, m)*mode(m, n)*CONJG(orbitals(j, n))
    1674              :                END DO
    1675              :             END DO
    1676           80 :             transformed(i, j) = value
    1677              :          END DO
    1678              :       END DO
    1679              : 
    1680            4 :    END SUBROUTINE reference_transform_density_mode
    1681              : 
    1682              : ! **************************************************************************************************
    1683              : !> \brief Real part of the complex Frobenius inner product.
    1684              : !> \param matrix_a first matrix
    1685              : !> \param matrix_b second matrix
    1686              : !> \return real Frobenius product
    1687              : ! **************************************************************************************************
    1688           12 :    FUNCTION reference_frobenius_product_real(matrix_a, matrix_b) RESULT(value)
    1689              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: matrix_a, matrix_b
    1690              :       REAL(KIND=dp)                                      :: value
    1691              : 
    1692              :       value = SUM(REAL(matrix_a, KIND=dp)*REAL(matrix_b, KIND=dp) + &
    1693          252 :                   AIMAG(matrix_a)*AIMAG(matrix_b))
    1694              : 
    1695           12 :    END FUNCTION reference_frobenius_product_real
    1696              : 
    1697              : ! **************************************************************************************************
    1698              : !> \brief fixed-occupation band energy for a dense finite rotation
    1699              : !> \param rotation unitary REF rotation
    1700              : !> \param hamiltonian fixed Hermitian Hamiltonian in the REF basis
    1701              : !> \param occupation band occupations
    1702              : !> \param kpoint_weight irreducible-k-point weight
    1703              : !> \return weighted band energy
    1704              : ! **************************************************************************************************
    1705            6 :    FUNCTION finite_rotation_energy(rotation, hamiltonian, occupation, kpoint_weight) RESULT(energy)
    1706              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: rotation, hamiltonian
    1707              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: occupation
    1708              :       REAL(KIND=dp), INTENT(IN)                          :: kpoint_weight
    1709              :       REAL(KIND=dp)                                      :: energy
    1710              : 
    1711              :       INTEGER                                            :: i
    1712              : 
    1713            6 :       energy = 0.0_dp
    1714           24 :       DO i = 1, SIZE(occupation)
    1715              :          energy = energy + kpoint_weight*occupation(i)* &
    1716              :                   REAL(DOT_PRODUCT(rotation(:, i), &
    1717          330 :                                    MATMUL(hamiltonian, rotation(:, i))), KIND=dp)
    1718              :       END DO
    1719              : 
    1720            6 :    END FUNCTION finite_rotation_energy
    1721              : 
    1722              : ! **************************************************************************************************
    1723              : !> \brief Solve fixed-N Fermi occupations by bisection.
    1724              : !> \param energies band energies
    1725              : !> \param weights integration weights
    1726              : !> \param target_electrons requested electron number
    1727              : !> \param temperature electronic temperature in energy units
    1728              : !> \param maxocc maximum band occupation
    1729              : !> \param occupation resulting occupations
    1730              : !> \param mu resulting chemical potential
    1731              : ! **************************************************************************************************
    1732           80 :    SUBROUTINE fixed_n_fermi_occupations(energies, weights, target_electrons, temperature, &
    1733           40 :                                         maxocc, occupation, mu)
    1734              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: energies, weights
    1735              :       REAL(KIND=dp), INTENT(IN)                          :: target_electrons, temperature, maxocc
    1736              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: occupation
    1737              :       REAL(KIND=dp), INTENT(OUT)                         :: mu
    1738              : 
    1739              :       INTEGER                                            :: i, iteration
    1740              :       REAL(KIND=dp)                                      :: high, low, number
    1741              : 
    1742          170 :       low = MINVAL(energies) - 80.0_dp*temperature
    1743          170 :       high = MAXVAL(energies) + 80.0_dp*temperature
    1744         6440 :       DO iteration = 1, 160
    1745         6400 :          mu = 0.5_dp*(low + high)
    1746         6400 :          number = 0.0_dp
    1747        27200 :          DO i = 1, SIZE(energies)
    1748        20800 :             occupation(i) = fermi_occupation(energies(i), mu, temperature, maxocc)
    1749        27200 :             number = number + weights(i)*occupation(i)
    1750              :          END DO
    1751         6440 :          IF (number < target_electrons) THEN
    1752              :             low = mu
    1753              :          ELSE
    1754         3530 :             high = mu
    1755              :          END IF
    1756              :       END DO
    1757           40 :       mu = 0.5_dp*(low + high)
    1758          170 :       DO i = 1, SIZE(energies)
    1759          170 :          occupation(i) = fermi_occupation(energies(i), mu, temperature, maxocc)
    1760              :       END DO
    1761              : 
    1762           40 :    END SUBROUTINE fixed_n_fermi_occupations
    1763              : 
    1764              : ! **************************************************************************************************
    1765              : !> \brief Numerically stable Fermi occupation.
    1766              : !> \param energy band energy
    1767              : !> \param mu chemical potential
    1768              : !> \param temperature electronic temperature in energy units
    1769              : !> \param maxocc maximum band occupation
    1770              : !> \return Fermi occupation
    1771              : ! **************************************************************************************************
    1772        20930 :    PURE FUNCTION fermi_occupation(energy, mu, temperature, maxocc) RESULT(occupation)
    1773              :       REAL(KIND=dp), INTENT(IN)                          :: energy, mu, temperature, maxocc
    1774              :       REAL(KIND=dp)                                      :: occupation
    1775              : 
    1776              :       REAL(KIND=dp)                                      :: x
    1777              : 
    1778        20930 :       x = (energy - mu)/temperature
    1779        20930 :       IF (x > 40.0_dp) THEN
    1780           44 :          occupation = maxocc*EXP(-x)
    1781        20886 :       ELSE IF (x < -40.0_dp) THEN
    1782           46 :          occupation = maxocc*(1.0_dp - EXP(x))
    1783              :       ELSE
    1784        20840 :          occupation = maxocc/(1.0_dp + EXP(x))
    1785              :       END IF
    1786              : 
    1787        20930 :    END FUNCTION fermi_occupation
    1788              : 
    1789              : ! **************************************************************************************************
    1790              : !> \brief Fixed-H fixed-N Mermin free energy used by the finite-difference test.
    1791              : !> \param rayleigh Hamiltonian expectation values
    1792              : !> \param energies auxiliary band energies
    1793              : !> \param weights integration weights
    1794              : !> \param target_electrons requested electron number
    1795              : !> \param temperature electronic temperature in energy units
    1796              : !> \param maxocc maximum band occupation
    1797              : !> \return fixed-N Mermin free energy
    1798              : ! **************************************************************************************************
    1799           22 :    FUNCTION fixed_n_mermin_value(rayleigh, energies, weights, target_electrons, &
    1800              :                                  temperature, maxocc) RESULT(value)
    1801              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: rayleigh, energies, weights
    1802              :       REAL(KIND=dp), INTENT(IN)                          :: target_electrons, temperature, maxocc
    1803              :       REAL(KIND=dp)                                      :: value
    1804              : 
    1805              :       INTEGER                                            :: i
    1806              :       REAL(KIND=dp)                                      :: mu, p
    1807           44 :       REAL(KIND=dp), DIMENSION(SIZE(energies))           :: occupation
    1808              : 
    1809              :       CALL fixed_n_fermi_occupations(energies, weights, target_electrons, temperature, &
    1810           22 :                                      maxocc, occupation, mu)
    1811           22 :       value = 0.0_dp
    1812           92 :       DO i = 1, SIZE(energies)
    1813           70 :          p = MAX(EPSILON(1.0_dp), MIN(1.0_dp - EPSILON(1.0_dp), occupation(i)/maxocc))
    1814              :          value = value + weights(i)*(occupation(i)*rayleigh(i) + &
    1815           92 :                                      temperature*maxocc*(p*LOG(p) + (1.0_dp - p)*LOG(1.0_dp - p)))
    1816              :       END DO
    1817              : 
    1818           22 :    END FUNCTION fixed_n_mermin_value
    1819              : 
    1820              : ! **************************************************************************************************
    1821              : !> \brief Canonical weighted occupation projector used by the finite-difference test.
    1822              : !> \param hamiltonian Hermitian Hamiltonian
    1823              : !> \param weights integration weights
    1824              : !> \param target_electrons requested electron number
    1825              : !> \param temperature electronic temperature in energy units
    1826              : !> \param maxocc maximum band occupation
    1827              : !> \param projector weighted spectral occupation projector
    1828              : ! **************************************************************************************************
    1829            4 :    SUBROUTINE fixed_n_spectral_projector(hamiltonian, weights, target_electrons, temperature, &
    1830            4 :                                          maxocc, projector)
    1831              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: hamiltonian
    1832              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: weights
    1833              :       REAL(KIND=dp), INTENT(IN)                          :: target_electrons, temperature, maxocc
    1834              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT)     :: projector
    1835              : 
    1836              :       COMPLEX(KIND=dp), DIMENSION(SIZE(hamiltonian, 1), &
    1837            8 :          SIZE(hamiltonian, 1))                           :: vectors, weighted_vectors
    1838              :       INTEGER                                            :: i
    1839              :       REAL(KIND=dp)                                      :: mu
    1840            8 :       REAL(KIND=dp), DIMENSION(SIZE(hamiltonian, 1))     :: eigenvalues, occupation
    1841              : 
    1842            4 :       CALL diag_complex(hamiltonian, vectors, eigenvalues)
    1843              :       CALL fixed_n_fermi_occupations(eigenvalues, weights, target_electrons, temperature, &
    1844            4 :                                      maxocc, occupation, mu)
    1845           52 :       weighted_vectors(:, :) = vectors(:, :)
    1846           16 :       DO i = 1, SIZE(eigenvalues)
    1847           52 :          weighted_vectors(:, i) = weights(i)*occupation(i)*weighted_vectors(:, i)
    1848              :       END DO
    1849          216 :       projector(:, :) = MATMUL(weighted_vectors, CONJG(TRANSPOSE(vectors)))
    1850              : 
    1851            4 :    END SUBROUTINE fixed_n_spectral_projector
    1852              : 
    1853              : ! **************************************************************************************************
    1854              : !> \brief Check the real finite rotation pullback used by Gamma-point REF OT.
    1855              : !> \param para_env parallel environment
    1856              : !> \param nfail accumulated number of failures
    1857              : ! **************************************************************************************************
    1858            2 :    SUBROUTINE test_real_rotation_frechet(para_env, nfail)
    1859              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    1860              :       INTEGER, INTENT(INOUT)                             :: nfail
    1861              : 
    1862              :       INTEGER, PARAMETER                                 :: m = 3
    1863              : 
    1864              :       INTEGER                                            :: i
    1865            4 :       INTEGER, DIMENSION(:), POINTER                     :: col_dist, col_size, row_dist, row_size
    1866            2 :       INTEGER, DIMENSION(:, :), POINTER                  :: pgrid
    1867              :       LOGICAL                                            :: found
    1868              :       REAL(KIND=dp)                                      :: error, fd_slope, hstep, predicted
    1869            2 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: block
    1870              :       REAL(KIND=dp), DIMENSION(m), TARGET                :: rotation_evals
    1871              :       REAL(KIND=dp), DIMENSION(m, m)                     :: dedu, direction, generator, gradient, &
    1872              :                                                             rotation_minus, rotation_plus
    1873              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
    1874              :       TYPE(dbcsr_distribution_type)                      :: dist
    1875           16 :       TYPE(dbcsr_type), DIMENSION(7), TARGET             :: matrices
    1876              :       TYPE(mp_para_env_type), POINTER                    :: local_para_env
    1877          324 :       TYPE(qs_ot_type)                                   :: rotation_env
    1878              : 
    1879            2 :       NULLIFY (blacs_env, local_para_env)
    1880            2 :       generator(:, :) = 0.0_dp
    1881            2 :       generator(1, 2) = 0.23_dp
    1882            2 :       generator(2, 1) = -generator(1, 2)
    1883            2 :       generator(1, 3) = -0.11_dp
    1884            2 :       generator(3, 1) = -generator(1, 3)
    1885            2 :       generator(2, 3) = 0.17_dp
    1886            2 :       generator(3, 2) = -generator(2, 3)
    1887              : 
    1888            2 :       direction(:, :) = 0.0_dp
    1889            2 :       direction(1, 2) = -0.09_dp
    1890            2 :       direction(2, 1) = -direction(1, 2)
    1891            2 :       direction(1, 3) = 0.14_dp
    1892            2 :       direction(3, 1) = -direction(1, 3)
    1893            2 :       direction(2, 3) = 0.06_dp
    1894            2 :       direction(3, 2) = -direction(2, 3)
    1895              : 
    1896              :       dedu = RESHAPE([0.31_dp, -0.27_dp, 0.08_dp, &
    1897              :                       0.19_dp, 0.42_dp, -0.16_dp, &
    1898            2 :                       -0.07_dp, 0.21_dp, 0.35_dp], [m, m])
    1899              : 
    1900            0 :       ALLOCATE (local_para_env)
    1901            2 :       CALL local_para_env%from_dup(mp_comm_self)
    1902            2 :       CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=local_para_env)
    1903            2 :       ALLOCATE (pgrid(0:0, 0:0), row_dist(1), col_dist(1), row_size(1), col_size(1))
    1904            6 :       pgrid(:, :) = 0
    1905            4 :       row_dist(:) = 0
    1906            4 :       col_dist(:) = 0
    1907            4 :       row_size(:) = m
    1908            4 :       col_size(:) = m
    1909              :       CALL dbcsr_distribution_new(dist, group=mp_comm_self%get_handle(), pgrid=pgrid, &
    1910            2 :                                   row_dist=row_dist, col_dist=col_dist)
    1911           16 :       DO i = 1, SIZE(matrices)
    1912           16 :          CALL create_one_block(matrices(i), 'real_rotation', dist, row_size, col_size)
    1913              :       END DO
    1914            2 :       CALL dbcsr_put_block(matrices(2), 1, 1, generator)
    1915            2 :       CALL dbcsr_put_block(matrices(3), 1, 1, dedu)
    1916              : 
    1917            2 :       rotation_env%para_env => local_para_env
    1918            2 :       rotation_env%blacs_env => blacs_env
    1919            2 :       rotation_env%rot_mat_u => matrices(1)
    1920            2 :       rotation_env%rot_mat_x => matrices(2)
    1921            2 :       rotation_env%rot_mat_dedu => matrices(3)
    1922            2 :       rotation_env%rot_mat_gx => matrices(4)
    1923            2 :       rotation_env%rot_mat_evec_re => matrices(5)
    1924            2 :       rotation_env%rot_mat_evec_im => matrices(6)
    1925            2 :       rotation_env%matrix_buf1 => matrices(7)
    1926            2 :       rotation_env%rot_mat_evals => rotation_evals
    1927              : 
    1928            2 :       CALL qs_ot_generate_rotation(rotation_env)
    1929            2 :       CALL qs_ot_rot_mat_derivative(rotation_env)
    1930            2 :       gradient(:, :) = 0.0_dp
    1931            2 :       CALL dbcsr_get_block_p(matrices(4), 1, 1, block, found)
    1932           26 :       IF (found) gradient = block
    1933              : 
    1934            2 :       hstep = 1.0E-6_dp
    1935              :       rotation_plus = REAL(dense_antihermitian_exp( &
    1936           50 :                            CMPLX(generator + hstep*direction, 0.0_dp, KIND=dp)), KIND=dp)
    1937              :       rotation_minus = REAL(dense_antihermitian_exp( &
    1938           50 :                             CMPLX(generator - hstep*direction, 0.0_dp, KIND=dp)), KIND=dp)
    1939           26 :       fd_slope = SUM(dedu*(rotation_plus - rotation_minus))/(2.0_dp*hstep)
    1940           26 :       predicted = 0.5_dp*SUM(gradient*direction)
    1941            2 :       error = ABS(predicted - fd_slope)
    1942            2 :       IF (error > 5.0E-8_dp) nfail = nfail + 1
    1943            2 :       IF (para_env%is_source()) THEN
    1944            1 :          WRITE (io_unit, '(A,3(1X,ES13.6))') 'real finite rotation slope fd/predicted/error', &
    1945            2 :             fd_slope, predicted, error
    1946              :       END IF
    1947              : 
    1948           16 :       DO i = 1, SIZE(matrices)
    1949           16 :          CALL dbcsr_release(matrices(i))
    1950              :       END DO
    1951            2 :       CALL dbcsr_distribution_release(dist)
    1952            2 :       DEALLOCATE (pgrid, row_dist, col_dist, row_size, col_size)
    1953            2 :       CALL cp_blacs_env_release(blacs_env)
    1954            2 :       CALL mp_para_env_release(local_para_env)
    1955              : 
    1956           12 :    END SUBROUTINE test_real_rotation_frechet
    1957              : 
    1958              : ! **************************************************************************************************
    1959              : !> \brief Check the combined finite complex REF and rotation derivatives.
    1960              : !> \param para_env parallel environment
    1961              : !> \param nfail accumulated number of failures
    1962              : ! **************************************************************************************************
    1963            2 :    SUBROUTINE test_complex_ref_rotation(para_env, nfail)
    1964              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    1965              :       INTEGER, INTENT(INOUT)                             :: nfail
    1966              : 
    1967              :       COMPLEX(KIND=dp), DIMENSION(k, k)                  :: direction_a, generator, gradient_a, &
    1968              :                                                             inverse_sqrt, rotation
    1969              :       COMPLEX(KIND=dp), DIMENSION(n, k)                  :: c_dense, c_reference, direction_x, &
    1970              :                                                             gradient_x, hc_dense, q_dense, x_dense
    1971              :       COMPLEX(KIND=dp), DIMENSION(n, n)                  :: hamiltonian, overlap
    1972              :       INTEGER                                            :: i, j
    1973            4 :       INTEGER, DIMENSION(:), POINTER                     :: col_dist, col_k, col_n, row_dist, row_k, &
    1974            2 :                                                             row_n
    1975            2 :       INTEGER, DIMENSION(:, :), POINTER                  :: pgrid
    1976              :       REAL(KIND=dp)                                      :: error_c, error_ref, error_rot, fd_ref, &
    1977              :                                                             fd_rot, hstep, predicted_ref, &
    1978              :                                                             predicted_rot
    1979              :       REAL(KIND=dp), DIMENSION(k)                        :: occupation
    1980              :       REAL(KIND=dp), DIMENSION(k), TARGET                :: rotation_evals
    1981              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
    1982              :       TYPE(dbcsr_distribution_type)                      :: dist_kk, dist_nk, dist_nn
    1983           22 :       TYPE(dbcsr_type), DIMENSION(10), TARGET            :: rot_matrix
    1984            6 :       TYPE(dbcsr_type), DIMENSION(2), TARGET             :: nk_buffer
    1985            8 :       TYPE(dbcsr_type), DIMENSION(3), TARGET             :: nonsym_buffer
    1986           10 :       TYPE(dbcsr_type), DIMENSION(4), TARGET             :: sym_buffer
    1987              :       TYPE(dbcsr_type), POINTER                          :: c_im_p, c_re_p, hc_im_p, hc_re_p, &
    1988              :                                                             s_im_p, s_re_p
    1989              :       TYPE(dbcsr_type), TARGET                           :: c_im, c_re, f_im, f_re, g_im, g_re, &
    1990              :                                                             hc_im, hc_re, s_im, s_re, sx_im, &
    1991              :                                                             sx_re, x_im, x_re
    1992              :       TYPE(mp_para_env_type), POINTER                    :: local_para_env
    1993          324 :       TYPE(qs_ot_type)                                   :: rotation_env
    1994              : 
    1995            2 :       NULLIFY (blacs_env, c_im_p, c_re_p, hc_im_p, hc_re_p, local_para_env, &
    1996            2 :                s_im_p, s_re_p)
    1997              : 
    1998              :       x_dense(:, 1) = [CMPLX(1.05_dp, 0.12_dp, KIND=dp), CMPLX(0.18_dp, -0.27_dp, KIND=dp), &
    1999            8 :                        CMPLX(-0.14_dp, 0.16_dp, KIND=dp)]
    2000              :       x_dense(:, 2) = [CMPLX(0.26_dp, 0.31_dp, KIND=dp), CMPLX(0.94_dp, -0.17_dp, KIND=dp), &
    2001            8 :                        CMPLX(0.21_dp, 0.13_dp, KIND=dp)]
    2002              :       direction_x(:, 1) = [CMPLX(0.11_dp, -0.07_dp, KIND=dp), CMPLX(-0.16_dp, 0.09_dp, KIND=dp), &
    2003            8 :                            CMPLX(0.05_dp, 0.14_dp, KIND=dp)]
    2004              :       direction_x(:, 2) = [CMPLX(-0.08_dp, 0.15_dp, KIND=dp), CMPLX(0.13_dp, 0.04_dp, KIND=dp), &
    2005            8 :                            CMPLX(-0.10_dp, -0.06_dp, KIND=dp)]
    2006              : 
    2007              :       generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2008            2 :       generator(1, 1) = CMPLX(0.0_dp, 0.19_dp, KIND=dp)
    2009            2 :       generator(2, 2) = CMPLX(0.0_dp, -0.08_dp, KIND=dp)
    2010            2 :       generator(1, 2) = CMPLX(0.23_dp, -0.11_dp, KIND=dp)
    2011            2 :       generator(2, 1) = -CONJG(generator(1, 2))
    2012              :       direction_a(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2013            2 :       direction_a(1, 1) = CMPLX(0.0_dp, -0.07_dp, KIND=dp)
    2014            2 :       direction_a(2, 2) = CMPLX(0.0_dp, 0.05_dp, KIND=dp)
    2015            2 :       direction_a(1, 2) = CMPLX(-0.09_dp, 0.06_dp, KIND=dp)
    2016            2 :       direction_a(2, 1) = -CONJG(direction_a(1, 2))
    2017              : 
    2018            2 :       hamiltonian(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2019            2 :       hamiltonian(1, 1) = CMPLX(0.65_dp, 0.0_dp, KIND=dp)
    2020            2 :       hamiltonian(2, 2) = CMPLX(-0.35_dp, 0.0_dp, KIND=dp)
    2021            2 :       hamiltonian(3, 3) = CMPLX(1.15_dp, 0.0_dp, KIND=dp)
    2022            2 :       hamiltonian(1, 2) = CMPLX(0.18_dp, 0.12_dp, KIND=dp)
    2023            2 :       hamiltonian(1, 3) = CMPLX(-0.09_dp, 0.07_dp, KIND=dp)
    2024            2 :       hamiltonian(2, 3) = CMPLX(0.14_dp, -0.17_dp, KIND=dp)
    2025            8 :       DO i = 1, n
    2026           14 :          DO j = i + 1, n
    2027           12 :             hamiltonian(j, i) = CONJG(hamiltonian(i, j))
    2028              :          END DO
    2029              :       END DO
    2030            2 :       overlap(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2031            8 :       DO i = 1, n
    2032            8 :          overlap(i, i) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
    2033              :       END DO
    2034            2 :       occupation = [1.0_dp, 0.37_dp]
    2035              : 
    2036           38 :       CALL dense_inverse_sqrt(MATMUL(CONJG(TRANSPOSE(x_dense)), x_dense), inverse_sqrt)
    2037           38 :       q_dense = MATMUL(x_dense, inverse_sqrt)
    2038            2 :       rotation = dense_antihermitian_exp(generator)
    2039           38 :       c_reference = MATMUL(q_dense, rotation)
    2040              : 
    2041            2 :       ALLOCATE (local_para_env)
    2042            2 :       CALL local_para_env%from_dup(mp_comm_self)
    2043            2 :       CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=local_para_env)
    2044              : 
    2045              :       ALLOCATE (pgrid(0:0, 0:0), row_dist(1), col_dist(1), row_n(1), row_k(1), &
    2046            2 :                 col_n(1), col_k(1))
    2047            6 :       pgrid(:, :) = 0
    2048            4 :       row_dist(:) = 0
    2049            4 :       col_dist(:) = 0
    2050            4 :       row_n(:) = n
    2051            4 :       col_n(:) = n
    2052            4 :       row_k(:) = k
    2053            4 :       col_k(:) = k
    2054              :       CALL dbcsr_distribution_new(dist_nk, group=mp_comm_self%get_handle(), pgrid=pgrid, &
    2055            2 :                                   row_dist=row_dist, col_dist=col_dist)
    2056              :       CALL dbcsr_distribution_new(dist_nn, group=mp_comm_self%get_handle(), pgrid=pgrid, &
    2057            2 :                                   row_dist=row_dist, col_dist=col_dist)
    2058              :       CALL dbcsr_distribution_new(dist_kk, group=mp_comm_self%get_handle(), pgrid=pgrid, &
    2059            2 :                                   row_dist=row_dist, col_dist=col_dist)
    2060              : 
    2061            2 :       CALL create_one_block(x_re, 'rotation_ref_x_re', dist_nk, row_n, col_k)
    2062            2 :       CALL create_one_block(x_im, 'rotation_ref_x_im', dist_nk, row_n, col_k)
    2063            2 :       CALL create_one_block(sx_re, 'rotation_ref_sx_re', dist_nk, row_n, col_k)
    2064            2 :       CALL create_one_block(sx_im, 'rotation_ref_sx_im', dist_nk, row_n, col_k)
    2065            2 :       CALL create_one_block(c_re, 'rotation_ref_c_re', dist_nk, row_n, col_k)
    2066            2 :       CALL create_one_block(c_im, 'rotation_ref_c_im', dist_nk, row_n, col_k)
    2067            2 :       CALL create_one_block(hc_re, 'rotation_ref_hc_re', dist_nk, row_n, col_k)
    2068            2 :       CALL create_one_block(hc_im, 'rotation_ref_hc_im', dist_nk, row_n, col_k)
    2069            2 :       CALL create_one_block(g_re, 'rotation_ref_g_re', dist_nk, row_n, col_k)
    2070            2 :       CALL create_one_block(g_im, 'rotation_ref_g_im', dist_nk, row_n, col_k)
    2071            2 :       CALL create_one_block(s_re, 'rotation_ref_s_re', dist_nn, row_n, col_n)
    2072            2 :       CALL create_one_block(s_im, 'rotation_ref_s_im', dist_nn, row_n, col_n)
    2073            2 :       CALL create_one_block(f_re, 'rotation_ref_f_re', dist_kk, row_k, col_k)
    2074            2 :       CALL create_one_block(f_im, 'rotation_ref_f_im', dist_kk, row_k, col_k)
    2075           10 :       DO i = 1, SIZE(sym_buffer)
    2076           10 :          CALL create_one_block(sym_buffer(i), 'rotation_ref_sym', dist_kk, row_k, col_k)
    2077              :       END DO
    2078            8 :       DO i = 1, SIZE(nonsym_buffer)
    2079            8 :          CALL create_one_block(nonsym_buffer(i), 'rotation_ref_nonsym', dist_kk, row_k, col_k)
    2080              :       END DO
    2081            6 :       DO i = 1, SIZE(nk_buffer)
    2082            6 :          CALL create_one_block(nk_buffer(i), 'rotation_ref_nk', dist_nk, row_n, col_k)
    2083              :       END DO
    2084           22 :       DO i = 1, SIZE(rot_matrix)
    2085           22 :          CALL create_one_block(rot_matrix(i), 'rotation_ref_rot', dist_kk, row_k, col_k)
    2086              :       END DO
    2087              : 
    2088            2 :       CALL put_complex_pair(x_re, x_im, x_dense)
    2089            2 :       CALL put_complex_pair(s_re, s_im, overlap)
    2090            2 :       CALL put_complex_pair(rot_matrix(3), rot_matrix(4), generator)
    2091              : 
    2092            2 :       rotation_env%has_complex_kpoint_state = .TRUE.
    2093            2 :       rotation_env%settings%do_rotation = .TRUE.
    2094            2 :       rotation_env%settings%eps_irac_filter_matrix = 0.0_dp
    2095            2 :       rotation_env%settings%ot_method = 'LBFG'
    2096            2 :       rotation_env%para_env => local_para_env
    2097            2 :       rotation_env%blacs_env => blacs_env
    2098            2 :       rotation_env%matrix_x => x_re
    2099            2 :       rotation_env%matrix_x_im => x_im
    2100            2 :       rotation_env%matrix_sx => sx_re
    2101            2 :       rotation_env%matrix_sx_im => sx_im
    2102            2 :       rotation_env%matrix_gx => g_re
    2103            2 :       rotation_env%matrix_gx_im => g_im
    2104            2 :       rotation_env%matrix_ref_inv_sqrt => f_re
    2105            2 :       rotation_env%matrix_ref_inv_sqrt_im => f_im
    2106            2 :       rotation_env%buf1_k_k_sym => sym_buffer(1)
    2107            2 :       rotation_env%buf2_k_k_sym => sym_buffer(2)
    2108            2 :       rotation_env%buf3_k_k_sym => sym_buffer(3)
    2109            2 :       rotation_env%buf4_k_k_sym => sym_buffer(4)
    2110            2 :       rotation_env%buf1_k_k_nosym => nonsym_buffer(1)
    2111            2 :       rotation_env%buf2_k_k_nosym => nonsym_buffer(2)
    2112            2 :       rotation_env%buf3_k_k_nosym => nonsym_buffer(3)
    2113            2 :       rotation_env%buf1_n_k => nk_buffer(1)
    2114            2 :       rotation_env%buf1_n_k_dp => nk_buffer(2)
    2115            2 :       rotation_env%rot_mat_u => rot_matrix(1)
    2116            2 :       rotation_env%rot_mat_u_im => rot_matrix(2)
    2117            2 :       rotation_env%rot_mat_x => rot_matrix(3)
    2118            2 :       rotation_env%rot_mat_x_im => rot_matrix(4)
    2119            2 :       rotation_env%rot_mat_dedu => rot_matrix(5)
    2120            2 :       rotation_env%rot_mat_dedu_im => rot_matrix(6)
    2121            2 :       rotation_env%rot_mat_gx => rot_matrix(7)
    2122            2 :       rotation_env%rot_mat_gx_im => rot_matrix(8)
    2123            2 :       rotation_env%rot_mat_evec_re => rot_matrix(9)
    2124            2 :       rotation_env%rot_mat_evec_im => rot_matrix(10)
    2125            2 :       rotation_env%rot_mat_evals => rotation_evals
    2126              : 
    2127            2 :       c_re_p => c_re
    2128            2 :       c_im_p => c_im
    2129            2 :       s_re_p => s_re
    2130            2 :       s_im_p => s_im
    2131            2 :       CALL qs_ot_get_orbitals_ref_complex(c_re_p, c_im_p, s_re_p, s_im_p, rotation_env)
    2132            2 :       CALL get_complex_pair(c_re, c_im, c_dense)
    2133           18 :       error_c = MAXVAL(ABS(c_dense - c_reference))
    2134              : 
    2135           70 :       hc_dense = 2.0_dp*MATMUL(hamiltonian, c_dense)
    2136            6 :       DO j = 1, k
    2137           18 :          hc_dense(:, j) = occupation(j)*hc_dense(:, j)
    2138              :       END DO
    2139            2 :       CALL put_complex_pair(hc_re, hc_im, hc_dense)
    2140            2 :       hc_re_p => hc_re
    2141            2 :       hc_im_p => hc_im
    2142            2 :       CALL qs_ot_get_derivative_ref_complex(hc_re_p, hc_im_p, rotation_env)
    2143            2 :       CALL get_complex_pair(g_re, g_im, gradient_x)
    2144            2 :       CALL get_complex_pair(rot_matrix(7), rot_matrix(8), gradient_a)
    2145              : 
    2146            2 :       hstep = 1.0E-6_dp
    2147              :       fd_ref = (rotated_polar_energy(x_dense + hstep*direction_x, generator, &
    2148              :                                      hamiltonian, occupation) - &
    2149              :                 rotated_polar_energy(x_dense - hstep*direction_x, generator, &
    2150           34 :                                      hamiltonian, occupation))/(2.0_dp*hstep)
    2151           18 :       predicted_ref = REAL(SUM(CONJG(gradient_x)*direction_x), KIND=dp)
    2152            2 :       error_ref = ABS(predicted_ref - fd_ref)
    2153              : 
    2154              :       fd_rot = (rotated_polar_energy(x_dense, generator + hstep*direction_a, &
    2155              :                                      hamiltonian, occupation) - &
    2156              :                 rotated_polar_energy(x_dense, generator - hstep*direction_a, &
    2157           26 :                                      hamiltonian, occupation))/(2.0_dp*hstep)
    2158              :       predicted_rot = 0.5_dp*(SUM(REAL(gradient_a, KIND=dp)*REAL(direction_a, KIND=dp)) + &
    2159           26 :                               SUM(AIMAG(gradient_a)*AIMAG(direction_a)))
    2160            2 :       error_rot = ABS(predicted_rot - fd_rot)
    2161              : 
    2162            2 :       IF (error_c > 5.0E-11_dp .OR. error_ref > 5.0E-8_dp .OR. &
    2163            0 :           error_rot > 5.0E-8_dp) nfail = nfail + 1
    2164            2 :       IF (para_env%is_source()) THEN
    2165            1 :          WRITE (io_unit, '(A,1X,ES13.6)') 'complex finite REF rotation orbital error', error_c
    2166            1 :          WRITE (io_unit, '(A,3(1X,ES13.6))') 'complex finite REF slope fd/predicted/error', &
    2167            2 :             fd_ref, predicted_ref, error_ref
    2168            1 :          WRITE (io_unit, '(A,3(1X,ES13.6))') 'complex finite rotation slope fd/predicted/error', &
    2169            2 :             fd_rot, predicted_rot, error_rot
    2170              :       END IF
    2171              : 
    2172           22 :       DO i = 1, SIZE(rot_matrix)
    2173           22 :          CALL dbcsr_release(rot_matrix(i))
    2174              :       END DO
    2175            6 :       DO i = 1, SIZE(nk_buffer)
    2176            6 :          CALL dbcsr_release(nk_buffer(i))
    2177              :       END DO
    2178            8 :       DO i = 1, SIZE(nonsym_buffer)
    2179            8 :          CALL dbcsr_release(nonsym_buffer(i))
    2180              :       END DO
    2181           10 :       DO i = 1, SIZE(sym_buffer)
    2182           10 :          CALL dbcsr_release(sym_buffer(i))
    2183              :       END DO
    2184            2 :       CALL dbcsr_release(f_im)
    2185            2 :       CALL dbcsr_release(f_re)
    2186            2 :       CALL dbcsr_release(s_im)
    2187            2 :       CALL dbcsr_release(s_re)
    2188            2 :       CALL dbcsr_release(g_im)
    2189            2 :       CALL dbcsr_release(g_re)
    2190            2 :       CALL dbcsr_release(hc_im)
    2191            2 :       CALL dbcsr_release(hc_re)
    2192            2 :       CALL dbcsr_release(c_im)
    2193            2 :       CALL dbcsr_release(c_re)
    2194            2 :       CALL dbcsr_release(sx_im)
    2195            2 :       CALL dbcsr_release(sx_re)
    2196            2 :       CALL dbcsr_release(x_im)
    2197            2 :       CALL dbcsr_release(x_re)
    2198            2 :       CALL dbcsr_distribution_release(dist_kk)
    2199            2 :       CALL dbcsr_distribution_release(dist_nn)
    2200            2 :       CALL dbcsr_distribution_release(dist_nk)
    2201            2 :       DEALLOCATE (pgrid, row_dist, col_dist, row_n, row_k, col_n, col_k)
    2202            2 :       CALL cp_blacs_env_release(blacs_env)
    2203            2 :       CALL mp_para_env_release(local_para_env)
    2204              : 
    2205           14 :    END SUBROUTINE test_complex_ref_rotation
    2206              : 
    2207              : ! **************************************************************************************************
    2208              : !> \brief Check the finite complex rotation Frechet derivative against central differences.
    2209              : !> \param para_env parallel environment
    2210              : !> \param nfail accumulated number of failures
    2211              : ! **************************************************************************************************
    2212            2 :    SUBROUTINE test_complex_rotation_frechet(para_env, nfail)
    2213              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    2214              :       INTEGER, INTENT(INOUT)                             :: nfail
    2215              : 
    2216              :       INTEGER, PARAMETER                                 :: m = 3
    2217              : 
    2218              :       COMPLEX(KIND=dp), DIMENSION(m, m)                  :: direction, fd, generator, predicted
    2219              :       INTEGER                                            :: i, j
    2220              :       REAL(KIND=dp)                                      :: error, hstep
    2221              :       REAL(KIND=dp), DIMENSION(m)                        :: evals
    2222              : 
    2223            2 :       evals = [-0.7_dp, 0.2000003_dp, 1.1_dp]
    2224            2 :       generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2225            8 :       DO i = 1, m
    2226            8 :          generator(i, i) = CMPLX(0.0_dp, -evals(i), KIND=dp)
    2227              :       END DO
    2228              :       direction(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2229            2 :       direction(1, 2) = CMPLX(0.17_dp, -0.09_dp, KIND=dp)
    2230            2 :       direction(2, 1) = -CONJG(direction(1, 2))
    2231            2 :       direction(1, 3) = CMPLX(-0.06_dp, 0.13_dp, KIND=dp)
    2232            2 :       direction(3, 1) = -CONJG(direction(1, 3))
    2233            2 :       direction(2, 3) = CMPLX(0.11_dp, 0.04_dp, KIND=dp)
    2234            2 :       direction(3, 2) = -CONJG(direction(2, 3))
    2235            2 :       direction(1, 1) = CMPLX(0.0_dp, 0.08_dp, KIND=dp)
    2236            2 :       direction(2, 2) = CMPLX(0.0_dp, -0.03_dp, KIND=dp)
    2237            2 :       direction(3, 3) = CMPLX(0.0_dp, 0.05_dp, KIND=dp)
    2238              : 
    2239            8 :       DO i = 1, m
    2240           26 :          DO j = 1, m
    2241              :             predicted(i, j) = qs_ot_complex_exp_frechet_kernel(evals(i), evals(j))* &
    2242           24 :                               direction(i, j)
    2243              :          END DO
    2244              :       END DO
    2245            2 :       hstep = 1.0E-6_dp
    2246              :       fd = (dense_antihermitian_exp(generator + hstep*direction) - &
    2247           74 :             dense_antihermitian_exp(generator - hstep*direction))/(2.0_dp*hstep)
    2248           26 :       error = MAXVAL(ABS(fd - predicted))
    2249            2 :       IF (error > 5.0E-9_dp) nfail = nfail + 1
    2250            2 :       IF (para_env%is_source()) THEN
    2251            1 :          WRITE (io_unit, '(A,1X,ES13.6)') 'complex rotation Frechet finite-difference error', error
    2252              :       END IF
    2253              : 
    2254            2 :    END SUBROUTINE test_complex_rotation_frechet
    2255              : 
    2256              : ! **************************************************************************************************
    2257              : !> \brief Check union handling for distinct real/imaginary DBCSR block patterns.
    2258              : !> \param para_env parallel environment
    2259              : !> \param nfail accumulated number of failures
    2260              : ! **************************************************************************************************
    2261            2 :    SUBROUTINE test_sparse_frechet_patterns(para_env, nfail)
    2262              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    2263              :       INTEGER, INTENT(INOUT)                             :: nfail
    2264              : 
    2265              :       COMPLEX(KIND=dp)                                   :: expected, value
    2266            2 :       INTEGER, DIMENSION(:), POINTER                     :: col_dist, col_size, row_dist, row_size
    2267              :       INTEGER, DIMENSION(:, :), POINTER                  :: pgrid
    2268              :       LOGICAL                                            :: found
    2269              :       REAL(KIND=dp)                                      :: error
    2270              :       REAL(KIND=dp), DIMENSION(2)                        :: evals
    2271            2 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: block
    2272              :       TYPE(dbcsr_distribution_type)                      :: dist
    2273              :       TYPE(dbcsr_type)                                   :: inner_im, inner_re, outer_im, outer_re
    2274              : 
    2275            2 :       ALLOCATE (pgrid(0:0, 0:0), row_dist(2), col_dist(2), row_size(2), col_size(2))
    2276            6 :       pgrid(:, :) = 0
    2277            6 :       row_dist(:) = 0
    2278            6 :       col_dist(:) = 0
    2279            6 :       row_size(:) = 1
    2280            6 :       col_size(:) = 1
    2281              :       CALL dbcsr_distribution_new(dist, group=mp_comm_self%get_handle(), pgrid=pgrid, &
    2282            2 :                                   row_dist=row_dist, col_dist=col_dist)
    2283            2 :       CALL dbcsr_create(inner_re, 'frechet_sparse_re', dist, dbcsr_type_no_symmetry, row_size, col_size)
    2284            2 :       CALL dbcsr_reserve_blocks(inner_re, rows=[1], cols=[2])
    2285            2 :       CALL dbcsr_finalize(inner_re)
    2286            2 :       CALL dbcsr_create(inner_im, 'frechet_sparse_im', dist, dbcsr_type_no_symmetry, row_size, col_size)
    2287            2 :       CALL dbcsr_reserve_blocks(inner_im, rows=[2], cols=[1])
    2288            2 :       CALL dbcsr_finalize(inner_im)
    2289            2 :       CALL dbcsr_put_block(inner_re, 1, 2, RESHAPE([0.37_dp], [1, 1]))
    2290            2 :       CALL dbcsr_put_block(inner_im, 2, 1, RESHAPE([-0.21_dp], [1, 1]))
    2291              : 
    2292            2 :       evals = [-0.4_dp, 0.9_dp]
    2293            2 :       CALL qs_ot_apply_complex_frechet_dbcsr(evals, inner_re, inner_im, outer_re, outer_im)
    2294              :       error = 0.0_dp
    2295            2 :       expected = qs_ot_complex_exp_frechet_kernel(evals(1), evals(2))*CMPLX(0.37_dp, 0.0_dp, KIND=dp)
    2296            2 :       value = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2297            2 :       CALL dbcsr_get_block_p(outer_re, 1, 2, block, found)
    2298            2 :       IF (found) value = value + CMPLX(block(1, 1), 0.0_dp, KIND=dp)
    2299            2 :       CALL dbcsr_get_block_p(outer_im, 1, 2, block, found)
    2300            2 :       IF (found) value = value + CMPLX(0.0_dp, block(1, 1), KIND=dp)
    2301            2 :       error = MAX(error, ABS(value - expected))
    2302              : 
    2303            2 :       expected = qs_ot_complex_exp_frechet_kernel(evals(2), evals(1))*CMPLX(0.0_dp, -0.21_dp, KIND=dp)
    2304            2 :       value = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2305            2 :       CALL dbcsr_get_block_p(outer_re, 2, 1, block, found)
    2306            2 :       IF (found) value = value + CMPLX(block(1, 1), 0.0_dp, KIND=dp)
    2307            2 :       CALL dbcsr_get_block_p(outer_im, 2, 1, block, found)
    2308            2 :       IF (found) value = value + CMPLX(0.0_dp, block(1, 1), KIND=dp)
    2309            2 :       error = MAX(error, ABS(value - expected))
    2310            2 :       IF (error > 5.0E-14_dp) nfail = nfail + 1
    2311            2 :       IF (para_env%is_source()) THEN
    2312            1 :          WRITE (io_unit, '(A,1X,ES13.6)') 'sparse complex Frechet block-union error', error
    2313              :       END IF
    2314              : 
    2315            2 :       CALL dbcsr_release(outer_im)
    2316            2 :       CALL dbcsr_release(outer_re)
    2317            2 :       CALL dbcsr_release(inner_im)
    2318            2 :       CALL dbcsr_release(inner_re)
    2319            2 :       CALL dbcsr_distribution_release(dist)
    2320            2 :       DEALLOCATE (pgrid, row_dist, col_dist, row_size, col_size)
    2321              : 
    2322            2 :    END SUBROUTINE test_sparse_frechet_patterns
    2323              : ! **************************************************************************************************
    2324              : !> \brief Check the finite complex STRICT chart and its analytic derivative.
    2325              : !> \param dist_nk distribution for orbital-shaped matrices
    2326              : !> \param dist_kk distribution for occupied-space matrices
    2327              : !> \param row_n AO block sizes
    2328              : !> \param row_k occupied-space row block sizes
    2329              : !> \param col_k occupied-space column block sizes
    2330              : !> \param para_env parallel environment
    2331              : !> \param nfail accumulated number of failures
    2332              : ! **************************************************************************************************
    2333            2 :    SUBROUTINE test_complex_strict_derivative(dist_nk, dist_kk, row_n, row_k, col_k, para_env, nfail)
    2334              :       TYPE(dbcsr_distribution_type), INTENT(IN)          :: dist_nk, dist_kk
    2335              :       INTEGER, DIMENSION(:), INTENT(INOUT), POINTER      :: row_n, row_k, col_k
    2336              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    2337              :       INTEGER, INTENT(INOUT)                             :: nfail
    2338              : 
    2339              :       CHARACTER(LEN=32)                                  :: name
    2340              :       COMPLEX(KIND=dp), DIMENSION(k, k)                  :: direction_a, generator, gradient_a, &
    2341              :                                                             inverse_sqrt, rotation
    2342              :       COMPLEX(KIND=dp), DIMENSION(n, k)                  :: c0, direction, gradient, hc, orbitals, &
    2343              :                                                             orbitals_base, sc0, sx, x
    2344              :       COMPLEX(KIND=dp), DIMENSION(n, n)                  :: hamiltonian, overlap
    2345              :       INTEGER                                            :: i
    2346              :       LOGICAL                                            :: found
    2347              :       REAL(KIND=dp)                                      :: error, error_rot, fd_slope, &
    2348              :                                                             fd_slope_rot, hstep, ortho_error, &
    2349              :                                                             slope, slope_rot
    2350            2 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: block
    2351              :       REAL(KIND=dp), DIMENSION(k)                        :: occupation
    2352              :       REAL(KIND=dp), DIMENSION(k), TARGET                :: rotation_evals
    2353              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
    2354           22 :       TYPE(dbcsr_type), DIMENSION(10), TARGET            :: rot_matrix
    2355           32 :       TYPE(dbcsr_type), DIMENSION(15), TARGET            :: nk
    2356           52 :       TYPE(dbcsr_type), DIMENSION(25), TARGET            :: kk
    2357              :       TYPE(dbcsr_type), POINTER                          :: hc_im_p, hc_re_p, sx_im_p, sx_re_p, &
    2358              :                                                             x_im_p, x_re_p
    2359              :       TYPE(mp_para_env_type), POINTER                    :: strict_para_env
    2360          324 :       TYPE(qs_ot_type)                                   :: strict_env
    2361              : 
    2362            2 :       NULLIFY (blacs_env, hc_im_p, hc_re_p, strict_para_env, x_im_p, x_re_p, sx_im_p, sx_re_p)
    2363            2 :       ALLOCATE (strict_para_env)
    2364            2 :       CALL strict_para_env%from_dup(mp_comm_self)
    2365            2 :       CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=strict_para_env)
    2366           32 :       DO i = 1, SIZE(nk)
    2367           30 :          WRITE (name, '(A,I0)') 'complex_strict_nk_', i
    2368           32 :          CALL create_one_block(nk(i), TRIM(name), dist_nk, row_n, col_k)
    2369              :       END DO
    2370           52 :       DO i = 1, SIZE(kk)
    2371           50 :          WRITE (name, '(A,I0)') 'complex_strict_kk_', i
    2372           52 :          CALL create_one_block(kk(i), TRIM(name), dist_kk, row_k, col_k)
    2373              :       END DO
    2374           22 :       DO i = 1, SIZE(rot_matrix)
    2375           20 :          WRITE (name, '(A,I0)') 'complex_strict_rot_', i
    2376           22 :          CALL create_one_block(rot_matrix(i), TRIM(name), dist_kk, row_k, col_k)
    2377              :       END DO
    2378              : 
    2379              :       overlap(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2380            2 :       overlap(1, 1) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
    2381            2 :       overlap(2, 2) = CMPLX(1.2_dp, 0.0_dp, KIND=dp)
    2382            2 :       overlap(3, 3) = CMPLX(1.4_dp, 0.0_dp, KIND=dp)
    2383            2 :       overlap(1, 2) = CMPLX(0.06_dp, 0.03_dp, KIND=dp)
    2384            2 :       overlap(2, 1) = CONJG(overlap(1, 2))
    2385            2 :       overlap(1, 3) = CMPLX(-0.04_dp, 0.02_dp, KIND=dp)
    2386            2 :       overlap(3, 1) = CONJG(overlap(1, 3))
    2387            2 :       overlap(2, 3) = CMPLX(0.05_dp, -0.01_dp, KIND=dp)
    2388            2 :       overlap(3, 2) = CONJG(overlap(2, 3))
    2389              : 
    2390              :       c0(:, 1) = [CMPLX(1.0_dp, 0.10_dp, KIND=dp), CMPLX(0.12_dp, -0.08_dp, KIND=dp), &
    2391            8 :                   CMPLX(-0.07_dp, 0.04_dp, KIND=dp)]
    2392              :       c0(:, 2) = [CMPLX(0.09_dp, 0.05_dp, KIND=dp), CMPLX(0.85_dp, -0.12_dp, KIND=dp), &
    2393            8 :                   CMPLX(0.11_dp, 0.06_dp, KIND=dp)]
    2394           90 :       CALL dense_inverse_sqrt(MATMUL(CONJG(TRANSPOSE(c0)), MATMUL(overlap, c0)), inverse_sqrt)
    2395           38 :       c0 = MATMUL(c0, inverse_sqrt)
    2396              : 
    2397            2 :       x(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2398            2 :       x(3, 1) = CMPLX(0.23_dp, 0.17_dp, KIND=dp)
    2399            2 :       x(3, 2) = CMPLX(-0.14_dp, 0.21_dp, KIND=dp)
    2400          144 :       x = x - MATMUL(c0, MATMUL(CONJG(TRANSPOSE(c0)), MATMUL(overlap, x)))
    2401            2 :       direction(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2402            2 :       direction(3, 1) = CMPLX(0.11_dp, -0.09_dp, KIND=dp)
    2403            2 :       direction(3, 2) = CMPLX(0.07_dp, 0.13_dp, KIND=dp)
    2404          144 :       direction = direction - MATMUL(c0, MATMUL(CONJG(TRANSPOSE(c0)), MATMUL(overlap, direction)))
    2405              : 
    2406              :       hamiltonian(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2407            2 :       hamiltonian(1, 1) = CMPLX(-0.70_dp, 0.0_dp, KIND=dp)
    2408            2 :       hamiltonian(2, 2) = CMPLX(-0.20_dp, 0.0_dp, KIND=dp)
    2409            2 :       hamiltonian(3, 3) = CMPLX(0.90_dp, 0.0_dp, KIND=dp)
    2410            2 :       hamiltonian(1, 2) = CMPLX(0.08_dp, 0.04_dp, KIND=dp)
    2411            2 :       hamiltonian(2, 1) = CONJG(hamiltonian(1, 2))
    2412            2 :       hamiltonian(1, 3) = CMPLX(-0.12_dp, 0.07_dp, KIND=dp)
    2413            2 :       hamiltonian(3, 1) = CONJG(hamiltonian(1, 3))
    2414            2 :       hamiltonian(2, 3) = CMPLX(0.05_dp, -0.09_dp, KIND=dp)
    2415            2 :       hamiltonian(3, 2) = CONJG(hamiltonian(2, 3))
    2416              : 
    2417              :       generator(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2418            2 :       generator(1, 1) = CMPLX(0.0_dp, 0.16_dp, KIND=dp)
    2419            2 :       generator(2, 2) = CMPLX(0.0_dp, -0.09_dp, KIND=dp)
    2420            2 :       generator(1, 2) = CMPLX(0.18_dp, -0.07_dp, KIND=dp)
    2421            2 :       generator(2, 1) = -CONJG(generator(1, 2))
    2422              :       direction_a(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2423            2 :       direction_a(1, 1) = CMPLX(0.0_dp, -0.05_dp, KIND=dp)
    2424            2 :       direction_a(2, 2) = CMPLX(0.0_dp, 0.04_dp, KIND=dp)
    2425            2 :       direction_a(1, 2) = CMPLX(-0.08_dp, 0.06_dp, KIND=dp)
    2426            2 :       direction_a(2, 1) = -CONJG(direction_a(1, 2))
    2427            2 :       occupation = [1.0_dp, 0.34_dp]
    2428              : 
    2429            2 :       CALL strict_orbitals(c0, x, overlap, orbitals_base)
    2430            2 :       rotation = dense_antihermitian_exp(generator)
    2431           38 :       orbitals = MATMUL(orbitals_base, rotation)
    2432           70 :       hc = 2.0_dp*MATMUL(hamiltonian, orbitals)
    2433            6 :       DO i = 1, k
    2434           18 :          hc(:, i) = occupation(i)*hc(:, i)
    2435              :       END DO
    2436           54 :       sx = MATMUL(overlap, x)
    2437           54 :       sc0 = MATMUL(overlap, c0)
    2438            2 :       CALL put_complex_pair(nk(1), nk(2), x)
    2439            2 :       CALL put_complex_pair(nk(3), nk(4), sx)
    2440            2 :       CALL put_complex_pair(nk(5), nk(6), hc)
    2441            2 :       CALL put_complex_pair(nk(9), nk(10), c0)
    2442            2 :       CALL put_complex_pair(nk(11), nk(12), sc0)
    2443            2 :       CALL put_complex_pair(rot_matrix(3), rot_matrix(4), generator)
    2444              : 
    2445            2 :       strict_env%has_complex_kpoint_state = .TRUE.
    2446            2 :       strict_env%settings%ot_algorithm = 'TOD'
    2447            2 :       strict_env%settings%do_rotation = .TRUE.
    2448            2 :       strict_env%para_env => strict_para_env
    2449            2 :       strict_env%blacs_env => blacs_env
    2450            2 :       strict_env%matrix_x => nk(1)
    2451            2 :       strict_env%matrix_x_im => nk(2)
    2452            2 :       strict_env%matrix_sx => nk(3)
    2453            2 :       strict_env%matrix_sx_im => nk(4)
    2454            2 :       strict_env%matrix_gx => nk(7)
    2455            2 :       strict_env%matrix_gx_im => nk(8)
    2456            2 :       strict_env%matrix_c0 => nk(9)
    2457            2 :       strict_env%matrix_c0_im => nk(10)
    2458            2 :       strict_env%matrix_sc0 => nk(11)
    2459            2 :       strict_env%matrix_sc0_im => nk(12)
    2460            2 :       strict_env%matrix_buf_nk => nk(13)
    2461            2 :       strict_env%matrix_buf_nk_im => nk(14)
    2462            2 :       strict_env%matrix_tmp_nk => nk(15)
    2463            2 :       strict_env%matrix_p => kk(1)
    2464            2 :       strict_env%matrix_p_im => kk(2)
    2465            2 :       strict_env%matrix_r => kk(3)
    2466            2 :       strict_env%matrix_r_im => kk(4)
    2467            2 :       strict_env%matrix_sinp => kk(5)
    2468            2 :       strict_env%matrix_sinp_im => kk(6)
    2469            2 :       strict_env%matrix_cosp => kk(7)
    2470            2 :       strict_env%matrix_cosp_im => kk(8)
    2471            2 :       strict_env%matrix_sinp_b => kk(9)
    2472            2 :       strict_env%matrix_cosp_b => kk(10)
    2473            2 :       strict_env%matrix_buf1 => kk(11)
    2474            2 :       strict_env%matrix_buf1_im => kk(12)
    2475            2 :       strict_env%matrix_buf2 => kk(13)
    2476            2 :       strict_env%matrix_buf2_im => kk(14)
    2477            2 :       strict_env%matrix_buf3 => kk(15)
    2478            2 :       strict_env%matrix_buf3_im => kk(16)
    2479            2 :       strict_env%matrix_buf4 => kk(17)
    2480            2 :       strict_env%matrix_buf4_im => kk(18)
    2481            2 :       strict_env%matrix_os => kk(19)
    2482            2 :       strict_env%matrix_os_im => kk(20)
    2483            2 :       strict_env%matrix_buf1_ortho => kk(21)
    2484            2 :       strict_env%matrix_buf1_ortho_im => kk(22)
    2485            2 :       strict_env%matrix_buf2_ortho => kk(23)
    2486            2 :       strict_env%matrix_buf2_ortho_im => kk(24)
    2487            2 :       strict_env%matrix_tmp_ortho => kk(25)
    2488            2 :       strict_env%rot_mat_u => rot_matrix(1)
    2489            2 :       strict_env%rot_mat_u_im => rot_matrix(2)
    2490            2 :       strict_env%rot_mat_x => rot_matrix(3)
    2491            2 :       strict_env%rot_mat_x_im => rot_matrix(4)
    2492            2 :       strict_env%rot_mat_dedu => rot_matrix(5)
    2493            2 :       strict_env%rot_mat_dedu_im => rot_matrix(6)
    2494            2 :       strict_env%rot_mat_gx => rot_matrix(7)
    2495            2 :       strict_env%rot_mat_gx_im => rot_matrix(8)
    2496            2 :       strict_env%rot_mat_evec_re => rot_matrix(9)
    2497            2 :       strict_env%rot_mat_evec_im => rot_matrix(10)
    2498            2 :       strict_env%rot_mat_evals => rotation_evals
    2499            2 :       ALLOCATE (strict_env%evals(k), strict_env%dum(k))
    2500              : 
    2501            2 :       x_re_p => nk(1)
    2502            2 :       x_im_p => nk(2)
    2503            2 :       sx_re_p => nk(3)
    2504            2 :       sx_im_p => nk(4)
    2505            2 :       hc_re_p => nk(5)
    2506            2 :       hc_im_p => nk(6)
    2507            2 :       CALL qs_ot_get_p_complex(x_re_p, x_im_p, sx_re_p, sx_im_p, strict_env)
    2508            2 :       CALL qs_ot_get_derivative_complex(hc_re_p, hc_im_p, strict_env)
    2509              : 
    2510            2 :       gradient(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2511            2 :       CALL dbcsr_get_block_p(nk(7), 1, 1, block, found)
    2512           18 :       IF (found) gradient = gradient + CMPLX(block, 0.0_dp, KIND=dp)
    2513            2 :       CALL dbcsr_get_block_p(nk(8), 1, 1, block, found)
    2514           18 :       IF (found) gradient = gradient + CMPLX(0.0_dp, block, KIND=dp)
    2515            2 :       CALL get_complex_pair(rot_matrix(7), rot_matrix(8), gradient_a)
    2516              : 
    2517            2 :       hstep = 1.0E-6_dp
    2518              :       fd_slope = (strict_rotated_energy(c0, x + hstep*direction, generator, overlap, &
    2519              :                                         hamiltonian, occupation) - &
    2520              :                   strict_rotated_energy(c0, x - hstep*direction, generator, overlap, &
    2521           34 :                                         hamiltonian, occupation))/(2.0_dp*hstep)
    2522           18 :       slope = REAL(SUM(CONJG(gradient)*direction), KIND=dp)
    2523            2 :       error = ABS(slope - fd_slope)
    2524              :       fd_slope_rot = (strict_rotated_energy(c0, x, generator + hstep*direction_a, overlap, &
    2525              :                                             hamiltonian, occupation) - &
    2526              :                       strict_rotated_energy(c0, x, generator - hstep*direction_a, overlap, &
    2527           26 :                                             hamiltonian, occupation))/(2.0_dp*hstep)
    2528              :       slope_rot = 0.5_dp*(SUM(REAL(gradient_a, KIND=dp)*REAL(direction_a, KIND=dp)) + &
    2529           26 :                           SUM(AIMAG(gradient_a)*AIMAG(direction_a)))
    2530            2 :       error_rot = ABS(slope_rot - fd_slope_rot)
    2531              :       ortho_error = MAXVAL(ABS(MATMUL(CONJG(TRANSPOSE(orbitals)), MATMUL(overlap, orbitals)) - &
    2532              :                                RESHAPE([CMPLX(1.0_dp, 0.0_dp, KIND=dp), &
    2533              :                                         CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
    2534              :                                         CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
    2535          102 :                                         CMPLX(1.0_dp, 0.0_dp, KIND=dp)], [k, k])))
    2536            2 :       IF (error > 5.0E-8_dp .OR. error_rot > 5.0E-8_dp .OR. &
    2537            0 :           ortho_error > 5.0E-13_dp) nfail = nfail + 1
    2538            2 :       IF (para_env%is_source()) THEN
    2539            1 :          WRITE (io_unit, '(A,3(1X,ES13.6))') 'complex finite-STRICT derivative: fd/predicted/error', &
    2540            2 :             fd_slope, slope, error
    2541            1 :          WRITE (io_unit, '(A,3(1X,ES13.6))') 'complex finite-STRICT rotation: fd/predicted/error', &
    2542            2 :             fd_slope_rot, slope_rot, error_rot
    2543            1 :          WRITE (io_unit, '(A,1X,ES13.6)') 'complex finite-STRICT orthonormality error', ortho_error
    2544              :       END IF
    2545              : 
    2546            2 :       DEALLOCATE (strict_env%evals, strict_env%dum)
    2547           22 :       DO i = SIZE(rot_matrix), 1, -1
    2548           22 :          CALL dbcsr_release(rot_matrix(i))
    2549              :       END DO
    2550           52 :       DO i = SIZE(kk), 1, -1
    2551           52 :          CALL dbcsr_release(kk(i))
    2552              :       END DO
    2553           32 :       DO i = SIZE(nk), 1, -1
    2554           32 :          CALL dbcsr_release(nk(i))
    2555              :       END DO
    2556            2 :       CALL cp_blacs_env_release(blacs_env)
    2557            2 :       CALL mp_para_env_release(strict_para_env)
    2558              : 
    2559            8 :    END SUBROUTINE test_complex_strict_derivative
    2560              : ! **************************************************************************************************
    2561              : !> \brief Check gauge invariance, Hermiticity, and positivity of the complex inverse operator.
    2562              : !> \param para_env parallel environment
    2563              : !> \param nfail accumulated number of failures
    2564              : ! **************************************************************************************************
    2565            2 :    SUBROUTINE test_complex_preconditioner_gauge(para_env, nfail)
    2566              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    2567              :       INTEGER, INTENT(INOUT)                             :: nfail
    2568              : 
    2569              :       COMPLEX(KIND=dp)                                   :: value
    2570              :       COMPLEX(KIND=dp), DIMENSION(k)                     :: phases
    2571              :       COMPLEX(KIND=dp), DIMENSION(n, k)                  :: c0, c0_phase, c0_rot, gradient, &
    2572              :                                                             gradient_phase, gradient_rot, output, &
    2573              :                                                             output_phase, output_rot
    2574              :       COMPLEX(KIND=dp), DIMENSION(n, n) :: eigenvectors, full_all_vectors, h, inverse, &
    2575              :          inverse_rot, inverse_s, inverse_single, inverse_t, reference_s, reference_t, s, &
    2576              :          spectral_metric, t
    2577              :       INTEGER                                            :: i, j
    2578              :       REAL(KIND=dp) :: covariant_descent, covariant_gauge_error, full_all_descent, &
    2579              :          full_all_gauge_error, full_all_orthogonality_error, full_single_hermitian_error, &
    2580              :          gauge_error, hermitian_error, low_rank_descent, low_rank_gauge_error, s_error, t_error
    2581              :       REAL(KIND=dp), DIMENSION(k)                        :: c0_evals
    2582              :       REAL(KIND=dp), DIMENSION(n)                        :: eigenvalues, eigenvalues_single
    2583              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
    2584              :       TYPE(cp_cfm_type) :: matrix_c0, matrix_c0_phase, matrix_c0_rot, matrix_gradient, &
    2585              :          matrix_gradient_phase, matrix_gradient_rot, matrix_h, matrix_output, matrix_output_phase, &
    2586              :          matrix_output_rot, matrix_s, matrix_t
    2587              :       TYPE(cp_fm_struct_type), POINTER                   :: fmstruct_nk, fmstruct_nn
    2588           32 :       TYPE(preconditioner_type) :: covariant, covariant_rot, full_all, full_all_phase, low_rank, &
    2589           28 :          low_rank_rot, preconditioner, preconditioner_rot, preconditioner_s, &
    2590           14 :          preconditioner_single, preconditioner_t
    2591              : 
    2592            2 :       NULLIFY (blacs_env, fmstruct_nk, fmstruct_nn)
    2593            2 :       CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=para_env)
    2594              :       CALL cp_fm_struct_create(fmstruct=fmstruct_nn, para_env=para_env, context=blacs_env, &
    2595            2 :                                nrow_global=n, ncol_global=n)
    2596              :       CALL cp_fm_struct_create(fmstruct=fmstruct_nk, para_env=para_env, context=blacs_env, &
    2597            2 :                                nrow_global=n, ncol_global=k)
    2598            2 :       CALL cp_cfm_create(matrix_h, fmstruct_nn, name='complex preconditioner H')
    2599            2 :       CALL cp_cfm_create(matrix_s, fmstruct_nn, name='complex preconditioner S')
    2600            2 :       CALL cp_cfm_create(matrix_t, fmstruct_nn, name='complex preconditioner T')
    2601            2 :       CALL cp_cfm_create(matrix_c0, fmstruct_nk, name='complex preconditioner C')
    2602            2 :       CALL cp_cfm_create(matrix_c0_phase, fmstruct_nk, name='complex preconditioner phased C')
    2603            2 :       CALL cp_cfm_create(matrix_c0_rot, fmstruct_nk, name='complex preconditioner C U')
    2604            2 :       CALL cp_cfm_create(matrix_gradient, fmstruct_nk, name='complex preconditioner gradient')
    2605              :       CALL cp_cfm_create(matrix_gradient_phase, fmstruct_nk, &
    2606            2 :                          name='complex preconditioner phased gradient')
    2607              :       CALL cp_cfm_create(matrix_gradient_rot, fmstruct_nk, &
    2608            2 :                          name='complex preconditioner rotated gradient')
    2609            2 :       CALL cp_cfm_create(matrix_output, fmstruct_nk, name='complex preconditioner output')
    2610              :       CALL cp_cfm_create(matrix_output_phase, fmstruct_nk, &
    2611            2 :                          name='complex preconditioner phased output')
    2612              :       CALL cp_cfm_create(matrix_output_rot, fmstruct_nk, &
    2613            2 :                          name='complex preconditioner rotated output')
    2614            2 :       CALL cp_cfm_set_all(matrix_h, CMPLX(0.0_dp, 0.0_dp, KIND=dp))
    2615            2 :       CALL cp_cfm_set_all(matrix_s, CMPLX(0.0_dp, 0.0_dp, KIND=dp))
    2616            2 :       CALL cp_cfm_set_all(matrix_t, CMPLX(0.0_dp, 0.0_dp, KIND=dp))
    2617            2 :       CALL cp_cfm_set_all(matrix_c0, CMPLX(0.0_dp, 0.0_dp, KIND=dp))
    2618            2 :       CALL cp_cfm_set_all(matrix_c0_phase, CMPLX(0.0_dp, 0.0_dp, KIND=dp))
    2619            2 :       CALL cp_cfm_set_all(matrix_c0_rot, CMPLX(0.0_dp, 0.0_dp, KIND=dp))
    2620              : 
    2621              :       h(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2622            2 :       h(1, 1) = CMPLX(-0.35_dp, 0.0_dp, KIND=dp)
    2623            2 :       h(2, 2) = CMPLX(0.15_dp, 0.0_dp, KIND=dp)
    2624            2 :       h(3, 3) = CMPLX(0.90_dp, 0.0_dp, KIND=dp)
    2625            2 :       h(1, 2) = CMPLX(0.08_dp, 0.04_dp, KIND=dp)
    2626            2 :       h(2, 1) = CONJG(h(1, 2))
    2627            2 :       h(1, 3) = CMPLX(-0.06_dp, 0.03_dp, KIND=dp)
    2628            2 :       h(3, 1) = CONJG(h(1, 3))
    2629            2 :       h(2, 3) = CMPLX(0.05_dp, -0.07_dp, KIND=dp)
    2630            2 :       h(3, 2) = CONJG(h(2, 3))
    2631            2 :       s(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2632            2 :       s(1, 1) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
    2633            2 :       s(2, 2) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
    2634            2 :       s(3, 3) = CMPLX(1.4_dp, 0.0_dp, KIND=dp)
    2635            2 :       s(1, 3) = CMPLX(0.08_dp, 0.03_dp, KIND=dp)
    2636            2 :       s(3, 1) = CONJG(s(1, 3))
    2637            2 :       s(2, 3) = CMPLX(-0.04_dp, 0.02_dp, KIND=dp)
    2638            2 :       s(3, 2) = CONJG(s(2, 3))
    2639            2 :       t = h
    2640            8 :       DO i = 1, n
    2641            8 :          t(i, i) = t(i, i) + CMPLX(1.50_dp, 0.0_dp, KIND=dp)
    2642              :       END DO
    2643            2 :       c0(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2644            2 :       c0(1, 1) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
    2645            2 :       c0(2, 2) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
    2646            2 :       c0_rot(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2647            2 :       c0_rot(1, 1) = CMPLX(SQRT(0.5_dp), 0.0_dp, KIND=dp)
    2648            2 :       c0_rot(1, 2) = CMPLX(0.0_dp, SQRT(0.5_dp), KIND=dp)
    2649            2 :       c0_rot(2, 1) = CMPLX(0.0_dp, SQRT(0.5_dp), KIND=dp)
    2650            2 :       c0_rot(2, 2) = CMPLX(SQRT(0.5_dp), 0.0_dp, KIND=dp)
    2651              :       phases = [EXP(CMPLX(0.0_dp, 0.43_dp, KIND=dp)), &
    2652            2 :                 EXP(CMPLX(0.0_dp, -0.67_dp, KIND=dp))]
    2653            6 :       DO j = 1, k
    2654           18 :          c0_phase(:, j) = phases(j)*c0(:, j)
    2655              :       END DO
    2656              :       gradient(:, 1) = [CMPLX(0.17_dp, 0.02_dp, KIND=dp), &
    2657              :                         CMPLX(-0.08_dp, 0.05_dp, KIND=dp), &
    2658            8 :                         CMPLX(0.11_dp, -0.04_dp, KIND=dp)]
    2659              :       gradient(:, 2) = [CMPLX(-0.03_dp, 0.06_dp, KIND=dp), &
    2660              :                         CMPLX(0.09_dp, -0.02_dp, KIND=dp), &
    2661            8 :                         CMPLX(-0.14_dp, 0.05_dp, KIND=dp)]
    2662            6 :       DO j = 1, k
    2663           18 :          gradient_phase(:, j) = phases(j)*gradient(:, j)
    2664              :       END DO
    2665           38 :       gradient_rot = MATMUL(gradient, c0_rot(1:k, :))
    2666            6 :       c0_evals = [REAL(h(1, 1), KIND=dp), REAL(h(2, 2), KIND=dp)]
    2667              : 
    2668            8 :       DO j = 1, n
    2669           26 :          DO i = 1, n
    2670           18 :             CALL cp_cfm_set_element(matrix_h, i, j, h(i, j))
    2671           18 :             CALL cp_cfm_set_element(matrix_s, i, j, s(i, j))
    2672           24 :             CALL cp_cfm_set_element(matrix_t, i, j, t(i, j))
    2673              :          END DO
    2674              :       END DO
    2675            6 :       DO j = 1, k
    2676           18 :          DO i = 1, n
    2677           12 :             CALL cp_cfm_set_element(matrix_c0, i, j, c0(i, j))
    2678           12 :             CALL cp_cfm_set_element(matrix_c0_phase, i, j, c0_phase(i, j))
    2679           12 :             CALL cp_cfm_set_element(matrix_c0_rot, i, j, c0_rot(i, j))
    2680           12 :             CALL cp_cfm_set_element(matrix_gradient, i, j, gradient(i, j))
    2681           12 :             CALL cp_cfm_set_element(matrix_gradient_phase, i, j, gradient_phase(i, j))
    2682           16 :             CALL cp_cfm_set_element(matrix_gradient_rot, i, j, gradient_rot(i, j))
    2683              :          END DO
    2684              :       END DO
    2685              : 
    2686            2 :       CALL init_preconditioner(full_all, para_env, blacs_env)
    2687            2 :       CALL init_preconditioner(full_all_phase, para_env, blacs_env)
    2688            2 :       CALL init_preconditioner(low_rank, para_env, blacs_env)
    2689            2 :       CALL init_preconditioner(low_rank_rot, para_env, blacs_env)
    2690            2 :       CALL init_preconditioner(covariant, para_env, blacs_env)
    2691            2 :       CALL init_preconditioner(covariant_rot, para_env, blacs_env)
    2692            2 :       CALL init_preconditioner(preconditioner, para_env, blacs_env)
    2693            2 :       CALL init_preconditioner(preconditioner_rot, para_env, blacs_env)
    2694            2 :       CALL init_preconditioner(preconditioner_s, para_env, blacs_env)
    2695            2 :       CALL init_preconditioner(preconditioner_single, para_env, blacs_env)
    2696            2 :       CALL init_preconditioner(preconditioner_t, para_env, blacs_env)
    2697            2 :       CALL make_complex_full_single_inverse(preconditioner, matrix_c0, matrix_h, matrix_s, 0.20_dp)
    2698            2 :       CALL make_complex_full_single_inverse(preconditioner_rot, matrix_c0_rot, matrix_h, matrix_s, 0.20_dp)
    2699            2 :       CALL make_complex_full_s_inverse(preconditioner_s, matrix_s)
    2700            2 :       CALL make_complex_full_single(preconditioner_single, matrix_h, matrix_s, 0.15_dp, 0.20_dp)
    2701            2 :       CALL make_complex_full_kinetic(preconditioner_t, matrix_t, matrix_s, 0.20_dp)
    2702            2 :       CALL make_complex_full_all(full_all, matrix_c0, matrix_h, matrix_s, c0_evals, 0.08_dp)
    2703              :       CALL make_complex_full_all(full_all_phase, matrix_c0_phase, matrix_h, matrix_s, &
    2704            2 :                                  c0_evals, 0.08_dp)
    2705            2 :       CALL apply_preconditioner(full_all, matrix_gradient, matrix_output)
    2706            2 :       CALL apply_preconditioner(full_all_phase, matrix_gradient_phase, matrix_output_phase)
    2707            8 :       DO j = 1, n
    2708           26 :          DO i = 1, n
    2709           18 :             CALL cp_cfm_get_element(preconditioner%complex_fm, i, j, value)
    2710           18 :             inverse(i, j) = value
    2711           18 :             CALL cp_cfm_get_element(preconditioner_rot%complex_fm, i, j, value)
    2712           18 :             inverse_rot(i, j) = value
    2713           18 :             CALL cp_cfm_get_element(preconditioner_s%complex_fm, i, j, value)
    2714           18 :             inverse_s(i, j) = value
    2715           18 :             CALL cp_cfm_get_element(preconditioner_single%complex_fm, i, j, value)
    2716           18 :             inverse_single(i, j) = value
    2717           18 :             CALL cp_cfm_get_element(preconditioner_t%complex_fm, i, j, value)
    2718           24 :             inverse_t(i, j) = value
    2719              :          END DO
    2720              :       END DO
    2721           26 :       gauge_error = MAXVAL(ABS(inverse - inverse_rot))
    2722           26 :       hermitian_error = MAXVAL(ABS(inverse - CONJG(TRANSPOSE(inverse))))
    2723            2 :       CALL diag_complex(inverse, eigenvectors, eigenvalues)
    2724           26 :       full_single_hermitian_error = MAXVAL(ABS(inverse_single - CONJG(TRANSPOSE(inverse_single))))
    2725            2 :       CALL diag_complex(inverse_single, eigenvectors, eigenvalues_single)
    2726            2 :       CALL dense_hermitian_inverse(s, reference_s)
    2727           26 :       CALL dense_hermitian_inverse(t + 0.20_dp*s, reference_t)
    2728           26 :       s_error = MAXVAL(ABS(inverse_s - reference_s))
    2729           26 :       t_error = MAXVAL(ABS(inverse_t - reference_t))
    2730            6 :       DO j = 1, k
    2731           18 :          DO i = 1, n
    2732           12 :             CALL cp_cfm_get_element(matrix_output, i, j, value)
    2733           12 :             output(i, j) = value
    2734           12 :             CALL cp_cfm_get_element(matrix_output_phase, i, j, value)
    2735           16 :             output_phase(i, j) = value
    2736              :          END DO
    2737              :       END DO
    2738            8 :       DO j = 1, n
    2739           26 :          DO i = 1, n
    2740           18 :             CALL cp_cfm_get_element(full_all%complex_fm, i, j, value)
    2741           24 :             full_all_vectors(i, j) = value
    2742              :          END DO
    2743              :       END DO
    2744            2 :       full_all_gauge_error = 0.0_dp
    2745            6 :       DO j = 1, k
    2746              :          full_all_gauge_error = MAX(full_all_gauge_error, &
    2747           18 :                                     MAXVAL(ABS(output_phase(:, j) - phases(j)*output(:, j))))
    2748              :       END DO
    2749              :       spectral_metric = MATMUL(CONJG(TRANSPOSE(full_all_vectors)), &
    2750          158 :                                MATMUL(s, full_all_vectors))
    2751              :       full_all_orthogonality_error = MAXVAL(ABS(spectral_metric - &
    2752              :                                                 RESHAPE([CMPLX(1.0_dp, 0.0_dp, KIND=dp), &
    2753              :                                                          CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
    2754              :                                                          CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
    2755              :                                                          CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
    2756              :                                                          CMPLX(1.0_dp, 0.0_dp, KIND=dp), &
    2757              :                                                          CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
    2758              :                                                          CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
    2759              :                                                          CMPLX(0.0_dp, 0.0_dp, KIND=dp), &
    2760           26 :                                                          CMPLX(1.0_dp, 0.0_dp, KIND=dp)], [n, n])))
    2761           18 :       full_all_descent = REAL(SUM(CONJG(gradient)*output), KIND=dp)
    2762              : 
    2763              :       CALL make_complex_fermi_low_rank(low_rank, matrix_c0, matrix_h, matrix_s, &
    2764            2 :                                        0.08_dp, 1, 1.0_dp, 1.0E-8_dp)
    2765              :       CALL make_complex_fermi_low_rank(low_rank_rot, matrix_c0_rot, matrix_h, matrix_s, &
    2766            2 :                                        0.08_dp, 1, 1.0_dp, 1.0E-8_dp)
    2767            2 :       CALL apply_preconditioner(low_rank, matrix_gradient, matrix_output)
    2768            2 :       CALL apply_preconditioner(low_rank_rot, matrix_gradient_rot, matrix_output_rot)
    2769            6 :       DO j = 1, k
    2770           18 :          DO i = 1, n
    2771           12 :             CALL cp_cfm_get_element(matrix_output, i, j, value)
    2772           12 :             output(i, j) = value
    2773           12 :             CALL cp_cfm_get_element(matrix_output_rot, i, j, value)
    2774           16 :             output_rot(i, j) = value
    2775              :          END DO
    2776              :       END DO
    2777           54 :       low_rank_gauge_error = MAXVAL(ABS(output_rot - MATMUL(output, c0_rot(1:k, :))))
    2778           18 :       low_rank_descent = REAL(SUM(CONJG(gradient)*output), KIND=dp)
    2779              : 
    2780            2 :       CALL make_complex_full_all_covariant(covariant, matrix_c0, matrix_h, matrix_s, 0.08_dp)
    2781            2 :       CALL make_complex_full_all_covariant(covariant_rot, matrix_c0_rot, matrix_h, matrix_s, 0.08_dp)
    2782            2 :       CALL apply_preconditioner(covariant, matrix_gradient, matrix_output)
    2783            2 :       CALL apply_preconditioner(covariant_rot, matrix_gradient_rot, matrix_output_rot)
    2784            6 :       DO j = 1, k
    2785           18 :          DO i = 1, n
    2786           12 :             CALL cp_cfm_get_element(matrix_output, i, j, value)
    2787           12 :             output(i, j) = value
    2788           12 :             CALL cp_cfm_get_element(matrix_output_rot, i, j, value)
    2789           16 :             output_rot(i, j) = value
    2790              :          END DO
    2791              :       END DO
    2792           54 :       covariant_gauge_error = MAXVAL(ABS(output_rot - MATMUL(output, c0_rot(1:k, :))))
    2793           18 :       covariant_descent = REAL(SUM(CONJG(gradient)*output), KIND=dp)
    2794            8 :       IF (gauge_error > 1.0E-11_dp .OR. hermitian_error > 1.0E-12_dp .OR. &
    2795            0 :           MINVAL(eigenvalues) <= 0.0_dp) nfail = nfail + 1
    2796            2 :       IF (s_error > 1.0E-11_dp .OR. t_error > 1.0E-11_dp) nfail = nfail + 1
    2797            8 :       IF (full_single_hermitian_error > 1.0E-12_dp .OR. &
    2798            0 :           MINVAL(eigenvalues_single) <= 0.0_dp) nfail = nfail + 1
    2799              :       IF (full_all_gauge_error > 1.0E-11_dp .OR. &
    2800            2 :           full_all_orthogonality_error > 1.0E-11_dp .OR. full_all_descent <= 0.0_dp) nfail = nfail + 1
    2801            2 :       IF (low_rank_gauge_error > 1.0E-11_dp .OR. low_rank_descent <= 0.0_dp) nfail = nfail + 1
    2802            2 :       IF (covariant_gauge_error > 1.0E-11_dp .OR. covariant_descent <= 0.0_dp) nfail = nfail + 1
    2803            2 :       IF (para_env%is_source()) THEN
    2804            1 :          WRITE (io_unit, '(A,3(1X,ES13.6))') 'complex FULL_SINGLE gauge/hermitian/min-eigenvalue', &
    2805            5 :             gauge_error, hermitian_error, MINVAL(eigenvalues)
    2806            1 :          WRITE (io_unit, '(A,2(1X,ES13.6))') 'complex S/KINETIC inverse errors', s_error, t_error
    2807            1 :          WRITE (io_unit, '(A,2(1X,ES13.6))') 'complex FULL_SINGLE hermitian/min-eigenvalue', &
    2808            5 :             full_single_hermitian_error, MINVAL(eigenvalues_single)
    2809            1 :          WRITE (io_unit, '(A,3(1X,ES13.6))') 'complex FULL_ALL phase/orthogonality/descent', &
    2810            2 :             full_all_gauge_error, full_all_orthogonality_error, full_all_descent
    2811            1 :          WRITE (io_unit, '(A,2(1X,ES13.6))') 'complex FERMI_LOW_RANK covariance/descent', &
    2812            2 :             low_rank_gauge_error, low_rank_descent
    2813            1 :          WRITE (io_unit, '(A,2(1X,ES13.6))') 'complex FULL_ALL_COVARIANT covariance/descent', &
    2814            2 :             covariant_gauge_error, covariant_descent
    2815              :       END IF
    2816              : 
    2817            2 :       CALL destroy_preconditioner(covariant_rot)
    2818            2 :       CALL destroy_preconditioner(covariant)
    2819            2 :       CALL destroy_preconditioner(low_rank_rot)
    2820            2 :       CALL destroy_preconditioner(low_rank)
    2821            2 :       CALL destroy_preconditioner(full_all_phase)
    2822            2 :       CALL destroy_preconditioner(full_all)
    2823            2 :       CALL destroy_preconditioner(preconditioner_t)
    2824            2 :       CALL destroy_preconditioner(preconditioner_single)
    2825            2 :       CALL destroy_preconditioner(preconditioner_s)
    2826            2 :       CALL destroy_preconditioner(preconditioner_rot)
    2827            2 :       CALL destroy_preconditioner(preconditioner)
    2828            2 :       CALL cp_cfm_release(matrix_c0_rot)
    2829            2 :       CALL cp_cfm_release(matrix_c0_phase)
    2830            2 :       CALL cp_cfm_release(matrix_c0)
    2831            2 :       CALL cp_cfm_release(matrix_output_phase)
    2832            2 :       CALL cp_cfm_release(matrix_output)
    2833            2 :       CALL cp_cfm_release(matrix_output_rot)
    2834            2 :       CALL cp_cfm_release(matrix_gradient_rot)
    2835            2 :       CALL cp_cfm_release(matrix_gradient_phase)
    2836            2 :       CALL cp_cfm_release(matrix_gradient)
    2837            2 :       CALL cp_cfm_release(matrix_t)
    2838            2 :       CALL cp_cfm_release(matrix_s)
    2839            2 :       CALL cp_cfm_release(matrix_h)
    2840            2 :       CALL cp_fm_struct_release(fmstruct_nk)
    2841            2 :       CALL cp_fm_struct_release(fmstruct_nn)
    2842            2 :       CALL cp_blacs_env_release(blacs_env)
    2843              : 
    2844            6 :    END SUBROUTINE test_complex_preconditioner_gauge
    2845              : 
    2846              : ! **************************************************************************************************
    2847              : !> \brief Check complex covariant preconditioning within equal-occupation ROKS blocks.
    2848              : !> \param para_env parallel environment
    2849              : !> \param nfail accumulated number of failures
    2850              : ! **************************************************************************************************
    2851            2 :    SUBROUTINE test_complex_occupation_block_preconditioner(para_env, nfail)
    2852              :       TYPE(mp_para_env_type), POINTER                    :: para_env
    2853              :       INTEGER, INTENT(INOUT)                             :: nfail
    2854              : 
    2855              :       INTEGER, PARAMETER                                 :: nbas = 4, norb = 3
    2856              : 
    2857              :       COMPLEX(KIND=dp)                                   :: value
    2858              :       COMPLEX(KIND=dp), DIMENSION(nbas, nbas)            :: hamiltonian, overlap
    2859              :       COMPLEX(KIND=dp), DIMENSION(nbas, norb)            :: coeff, coeff_rot, gradient, &
    2860              :                                                             gradient_rot, output, output_rot
    2861              :       COMPLEX(KIND=dp), DIMENSION(norb, norb)            :: block_rotation, private_rotation
    2862              :       INTEGER                                            :: i, j
    2863              :       REAL(KIND=dp)                                      :: covariance_error, cross_block_error, &
    2864              :                                                             descent, shifted_eigenvalue_error
    2865              :       REAL(KIND=dp), DIMENSION(norb, 2)                  :: occupation_signature
    2866              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
    2867              :       TYPE(cp_cfm_type) :: matrix_c, matrix_c_rot, matrix_gradient, matrix_gradient_rot, matrix_h, &
    2868              :          matrix_output, matrix_output_rot, matrix_s
    2869              :       TYPE(cp_fm_struct_type), POINTER                   :: fmstruct_nk, fmstruct_nn
    2870           14 :       TYPE(preconditioner_type)                          :: preconditioner, preconditioner_rot
    2871              : 
    2872            2 :       NULLIFY (blacs_env, fmstruct_nk, fmstruct_nn)
    2873            2 :       CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=para_env)
    2874              :       CALL cp_fm_struct_create(fmstruct=fmstruct_nn, para_env=para_env, context=blacs_env, &
    2875            2 :                                nrow_global=nbas, ncol_global=nbas)
    2876              :       CALL cp_fm_struct_create(fmstruct=fmstruct_nk, para_env=para_env, context=blacs_env, &
    2877            2 :                                nrow_global=nbas, ncol_global=norb)
    2878            2 :       CALL cp_cfm_create(matrix_h, fmstruct_nn, name='occupation-block H')
    2879            2 :       CALL cp_cfm_create(matrix_s, fmstruct_nn, name='occupation-block S')
    2880            2 :       CALL cp_cfm_create(matrix_c, fmstruct_nk, name='occupation-block C')
    2881            2 :       CALL cp_cfm_create(matrix_c_rot, fmstruct_nk, name='occupation-block C U')
    2882            2 :       CALL cp_cfm_create(matrix_gradient, fmstruct_nk, name='occupation-block gradient')
    2883              :       CALL cp_cfm_create(matrix_gradient_rot, fmstruct_nk, &
    2884            2 :                          name='occupation-block rotated gradient')
    2885            2 :       CALL cp_cfm_create(matrix_output, fmstruct_nk, name='occupation-block output')
    2886            2 :       CALL cp_cfm_create(matrix_output_rot, fmstruct_nk, name='occupation-block rotated output')
    2887              : 
    2888              :       hamiltonian(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2889            2 :       hamiltonian(1, 1) = CMPLX(-0.42_dp, 0.0_dp, KIND=dp)
    2890            2 :       hamiltonian(2, 2) = CMPLX(-0.18_dp, 0.0_dp, KIND=dp)
    2891            2 :       hamiltonian(3, 3) = CMPLX(0.07_dp, 0.0_dp, KIND=dp)
    2892            2 :       hamiltonian(4, 4) = CMPLX(0.83_dp, 0.0_dp, KIND=dp)
    2893            2 :       hamiltonian(1, 2) = CMPLX(0.09_dp, 0.04_dp, KIND=dp)
    2894            2 :       hamiltonian(2, 1) = CONJG(hamiltonian(1, 2))
    2895            2 :       hamiltonian(1, 3) = CMPLX(-0.06_dp, 0.03_dp, KIND=dp)
    2896            2 :       hamiltonian(3, 1) = CONJG(hamiltonian(1, 3))
    2897            2 :       hamiltonian(2, 3) = CMPLX(0.05_dp, -0.02_dp, KIND=dp)
    2898            2 :       hamiltonian(3, 2) = CONJG(hamiltonian(2, 3))
    2899            2 :       hamiltonian(1, 4) = CMPLX(0.04_dp, -0.03_dp, KIND=dp)
    2900            2 :       hamiltonian(4, 1) = CONJG(hamiltonian(1, 4))
    2901            2 :       hamiltonian(2, 4) = CMPLX(-0.02_dp, 0.05_dp, KIND=dp)
    2902            2 :       hamiltonian(4, 2) = CONJG(hamiltonian(2, 4))
    2903            2 :       hamiltonian(3, 4) = CMPLX(0.07_dp, 0.01_dp, KIND=dp)
    2904            2 :       hamiltonian(4, 3) = CONJG(hamiltonian(3, 4))
    2905            2 :       overlap(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2906           10 :       DO i = 1, nbas
    2907           10 :          overlap(i, i) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
    2908              :       END DO
    2909            2 :       coeff(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2910            8 :       DO i = 1, norb
    2911            8 :          coeff(i, i) = CMPLX(1.0_dp, 0.0_dp, KIND=dp)
    2912              :       END DO
    2913            2 :       block_rotation(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    2914            2 :       block_rotation(1, 1) = CMPLX(SQRT(0.7_dp), 0.0_dp, KIND=dp)
    2915            2 :       block_rotation(1, 3) = CMPLX(0.0_dp, SQRT(0.3_dp), KIND=dp)
    2916            2 :       block_rotation(2, 2) = EXP(CMPLX(0.0_dp, 0.37_dp, KIND=dp))
    2917            2 :       block_rotation(3, 1) = CMPLX(0.0_dp, SQRT(0.3_dp), KIND=dp)
    2918            2 :       block_rotation(3, 3) = CMPLX(SQRT(0.7_dp), 0.0_dp, KIND=dp)
    2919           98 :       coeff_rot = MATMUL(coeff, block_rotation)
    2920              :       gradient(:, 1) = [CMPLX(0.12_dp, 0.03_dp, KIND=dp), &
    2921              :                         CMPLX(-0.04_dp, 0.07_dp, KIND=dp), &
    2922              :                         CMPLX(0.08_dp, -0.02_dp, KIND=dp), &
    2923           10 :                         CMPLX(-0.05_dp, 0.06_dp, KIND=dp)]
    2924              :       gradient(:, 2) = [CMPLX(-0.06_dp, 0.02_dp, KIND=dp), &
    2925              :                         CMPLX(0.11_dp, -0.05_dp, KIND=dp), &
    2926              :                         CMPLX(0.03_dp, 0.09_dp, KIND=dp), &
    2927           10 :                         CMPLX(0.07_dp, -0.01_dp, KIND=dp)]
    2928              :       gradient(:, 3) = [CMPLX(0.04_dp, -0.08_dp, KIND=dp), &
    2929              :                         CMPLX(-0.02_dp, 0.05_dp, KIND=dp), &
    2930              :                         CMPLX(0.10_dp, 0.01_dp, KIND=dp), &
    2931           10 :                         CMPLX(-0.03_dp, -0.06_dp, KIND=dp)]
    2932           98 :       gradient_rot = MATMUL(gradient, block_rotation)
    2933            8 :       occupation_signature(:, 1) = [1.0_dp, 1.0_dp, 1.0_dp]
    2934            8 :       occupation_signature(:, 2) = [1.0_dp, 0.0_dp, 1.0_dp]
    2935              : 
    2936           10 :       DO j = 1, nbas
    2937           42 :          DO i = 1, nbas
    2938           32 :             CALL cp_cfm_set_element(matrix_h, i, j, hamiltonian(i, j))
    2939           40 :             CALL cp_cfm_set_element(matrix_s, i, j, overlap(i, j))
    2940              :          END DO
    2941              :       END DO
    2942            8 :       DO j = 1, norb
    2943           32 :          DO i = 1, nbas
    2944           24 :             CALL cp_cfm_set_element(matrix_c, i, j, coeff(i, j))
    2945           24 :             CALL cp_cfm_set_element(matrix_c_rot, i, j, coeff_rot(i, j))
    2946           24 :             CALL cp_cfm_set_element(matrix_gradient, i, j, gradient(i, j))
    2947           30 :             CALL cp_cfm_set_element(matrix_gradient_rot, i, j, gradient_rot(i, j))
    2948              :          END DO
    2949              :       END DO
    2950              : 
    2951            2 :       CALL init_preconditioner(preconditioner, para_env, blacs_env)
    2952            2 :       CALL init_preconditioner(preconditioner_rot, para_env, blacs_env)
    2953              :       CALL make_complex_full_all_covariant(preconditioner, matrix_c, matrix_h, matrix_s, &
    2954            2 :                                            0.08_dp, occupation_signature)
    2955              :       CALL make_complex_full_all_covariant(preconditioner_rot, matrix_c_rot, matrix_h, matrix_s, &
    2956            2 :                                            0.08_dp, occupation_signature)
    2957            2 :       CALL apply_preconditioner(preconditioner, matrix_gradient, matrix_output)
    2958            2 :       CALL apply_preconditioner(preconditioner_rot, matrix_gradient_rot, matrix_output_rot)
    2959            8 :       DO j = 1, norb
    2960           32 :          DO i = 1, nbas
    2961           24 :             CALL cp_cfm_get_element(matrix_output, i, j, value)
    2962           24 :             output(i, j) = value
    2963           24 :             CALL cp_cfm_get_element(matrix_output_rot, i, j, value)
    2964           30 :             output_rot(i, j) = value
    2965              :          END DO
    2966              :       END DO
    2967            8 :       DO j = 1, norb
    2968           26 :          DO i = 1, norb
    2969           18 :             CALL cp_cfm_get_element(preconditioner%occ_rotation_complex, i, j, value)
    2970           24 :             private_rotation(i, j) = value
    2971              :          END DO
    2972              :       END DO
    2973              : 
    2974          128 :       covariance_error = MAXVAL(ABS(output_rot - MATMUL(output, block_rotation)))
    2975              :       cross_block_error = MAX(MAXVAL(ABS(private_rotation([1, 3], 3))), &
    2976           10 :                               MAXVAL(ABS(private_rotation(2, 1:2))))
    2977            8 :       shifted_eigenvalue_error = MAXVAL(ABS(preconditioner%occ_evals))
    2978           32 :       descent = REAL(SUM(CONJG(gradient)*output), KIND=dp)
    2979              :       IF (covariance_error > 1.0E-11_dp .OR. cross_block_error > 1.0E-12_dp .OR. &
    2980            2 :           shifted_eigenvalue_error > 1.0_dp .OR. descent <= 0.0_dp) nfail = nfail + 1
    2981            2 :       IF (para_env%is_source()) THEN
    2982              :          WRITE (io_unit, '(A,4(1X,ES13.6))') &
    2983            1 :             'complex occupation-block covariance/cross-block/max-eigenvalue/descent', &
    2984            2 :             covariance_error, cross_block_error, shifted_eigenvalue_error, descent
    2985              :       END IF
    2986              : 
    2987            2 :       CALL destroy_preconditioner(preconditioner_rot)
    2988            2 :       CALL destroy_preconditioner(preconditioner)
    2989            2 :       CALL cp_cfm_release(matrix_output_rot)
    2990            2 :       CALL cp_cfm_release(matrix_output)
    2991            2 :       CALL cp_cfm_release(matrix_gradient_rot)
    2992            2 :       CALL cp_cfm_release(matrix_gradient)
    2993            2 :       CALL cp_cfm_release(matrix_c_rot)
    2994            2 :       CALL cp_cfm_release(matrix_c)
    2995            2 :       CALL cp_cfm_release(matrix_s)
    2996            2 :       CALL cp_cfm_release(matrix_h)
    2997            2 :       CALL cp_fm_struct_release(fmstruct_nk)
    2998            2 :       CALL cp_fm_struct_release(fmstruct_nn)
    2999            2 :       CALL cp_blacs_env_release(blacs_env)
    3000              : 
    3001            6 :    END SUBROUTINE test_complex_occupation_block_preconditioner
    3002              : 
    3003              : ! **************************************************************************************************
    3004              : !> \brief Check the intentionally narrow complex k-point preconditioner support matrix.
    3005              : !> \param nfail accumulated number of failures
    3006              : ! **************************************************************************************************
    3007            2 :    SUBROUTINE test_preconditioner_support(nfail)
    3008              :       INTEGER, INTENT(INOUT)                             :: nfail
    3009              : 
    3010            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_supported(ot_precond_none, .FALSE.)) nfail = nfail + 1
    3011            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_supported(ot_precond_fermi_low_rank, .FALSE.)) nfail = nfail + 1
    3012            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_supported(ot_precond_full_single_inverse, .FALSE.)) THEN
    3013            0 :          nfail = nfail + 1
    3014              :       END IF
    3015            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_supported(ot_precond_full_all, .FALSE.)) nfail = nfail + 1
    3016            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_supported(ot_precond_full_single, .FALSE.)) nfail = nfail + 1
    3017            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_supported( &
    3018            0 :           ot_precond_full_all_covariant, .FALSE.)) nfail = nfail + 1
    3019            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_supported(ot_precond_full_kinetic, .FALSE.)) nfail = nfail + 1
    3020            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_supported(ot_precond_s_inverse, .FALSE.)) nfail = nfail + 1
    3021            2 :       IF (qs_ot_kpoint_preconditioner_supported(ot_precond_full_single_inverse, .TRUE.)) nfail = nfail + 1
    3022              : 
    3023            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
    3024            0 :           ot_precond_full_single, ot_precond_solver_default, .FALSE.)) nfail = nfail + 1
    3025            2 :       IF (qs_ot_kpoint_preconditioner_solver_supported( &
    3026            0 :           ot_precond_full_single, ot_precond_solver_inv_chol, .FALSE.)) nfail = nfail + 1
    3027              : 
    3028            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
    3029            0 :           ot_precond_full_single_inverse, ot_precond_solver_default, .FALSE.)) nfail = nfail + 1
    3030            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
    3031            0 :           ot_precond_full_single_inverse, ot_precond_solver_inv_chol, .FALSE.)) nfail = nfail + 1
    3032            2 :       IF (qs_ot_kpoint_preconditioner_solver_supported( &
    3033            0 :           ot_precond_full_single_inverse, ot_precond_solver_direct, .FALSE.)) nfail = nfail + 1
    3034            2 :       IF (qs_ot_kpoint_preconditioner_solver_supported( &
    3035            0 :           ot_precond_full_single_inverse, ot_precond_solver_chebyshev, .FALSE.)) nfail = nfail + 1
    3036            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
    3037            0 :           ot_precond_full_all, ot_precond_solver_default, .FALSE.)) nfail = nfail + 1
    3038            2 :       IF (qs_ot_kpoint_preconditioner_solver_supported( &
    3039            0 :           ot_precond_full_all, ot_precond_solver_inv_chol, .FALSE.)) nfail = nfail + 1
    3040            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
    3041            0 :           ot_precond_fermi_low_rank, ot_precond_solver_default, .FALSE.)) nfail = nfail + 1
    3042            2 :       IF (qs_ot_kpoint_preconditioner_solver_supported( &
    3043            0 :           ot_precond_fermi_low_rank, ot_precond_solver_inv_chol, .FALSE.)) nfail = nfail + 1
    3044            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
    3045            0 :           ot_precond_full_all_covariant, ot_precond_solver_default, .FALSE.)) nfail = nfail + 1
    3046            2 :       IF (qs_ot_kpoint_preconditioner_solver_supported( &
    3047            0 :           ot_precond_full_all_covariant, ot_precond_solver_inv_chol, .FALSE.)) nfail = nfail + 1
    3048            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
    3049            0 :           ot_precond_full_kinetic, ot_precond_solver_default, .FALSE.)) nfail = nfail + 1
    3050            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
    3051            0 :           ot_precond_full_kinetic, ot_precond_solver_inv_chol, .FALSE.)) nfail = nfail + 1
    3052            2 :       IF (qs_ot_kpoint_preconditioner_solver_supported( &
    3053            0 :           ot_precond_full_kinetic, ot_precond_solver_direct, .FALSE.)) nfail = nfail + 1
    3054            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
    3055            0 :           ot_precond_s_inverse, ot_precond_solver_default, .FALSE.)) nfail = nfail + 1
    3056            2 :       IF (.NOT. qs_ot_kpoint_preconditioner_solver_supported( &
    3057            0 :           ot_precond_s_inverse, ot_precond_solver_inv_chol, .FALSE.)) nfail = nfail + 1
    3058            2 :       IF (qs_ot_kpoint_preconditioner_solver_supported( &
    3059            0 :           ot_precond_s_inverse, ot_precond_solver_chebyshev, .FALSE.)) nfail = nfail + 1
    3060            2 :       IF (ABS(qs_ot_kpoint_preconditioner_scale(0.25_dp) - 4.0_dp) > EPSILON(1.0_dp)) THEN
    3061            0 :          nfail = nfail + 1
    3062              :       END IF
    3063              : 
    3064            2 :    END SUBROUTINE test_preconditioner_support
    3065              : 
    3066              : ! **************************************************************************************************
    3067              : !> \brief Create and reserve a one-block DBCSR matrix.
    3068              : !> \param matrix ...
    3069              : !> \param name ...
    3070              : !> \param dist ...
    3071              : !> \param row_size ...
    3072              : !> \param col_size ...
    3073              : ! **************************************************************************************************
    3074          210 :    SUBROUTINE create_one_block(matrix, name, dist, row_size, col_size)
    3075              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: matrix
    3076              :       CHARACTER(LEN=*), INTENT(IN)                       :: name
    3077              :       TYPE(dbcsr_distribution_type), INTENT(IN)          :: dist
    3078              :       INTEGER, DIMENSION(:), INTENT(INOUT), POINTER      :: row_size, col_size
    3079              : 
    3080          210 :       CALL dbcsr_create(matrix, name, dist, dbcsr_type_no_symmetry, row_size, col_size)
    3081          210 :       CALL dbcsr_reserve_blocks(matrix, rows=[1], cols=[1])
    3082          210 :       CALL dbcsr_finalize(matrix)
    3083              : 
    3084          210 :    END SUBROUTINE create_one_block
    3085              : 
    3086              : ! **************************************************************************************************
    3087              : !> \brief Store a dense complex matrix in a real/imaginary DBCSR pair.
    3088              : !> \param matrix_re ...
    3089              : !> \param matrix_im ...
    3090              : !> \param matrix ...
    3091              : ! **************************************************************************************************
    3092           28 :    SUBROUTINE put_complex_pair(matrix_re, matrix_im, matrix)
    3093              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: matrix_re, matrix_im
    3094              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: matrix
    3095              : 
    3096          248 :       CALL dbcsr_put_block(matrix_re, 1, 1, REAL(matrix, KIND=dp))
    3097          248 :       CALL dbcsr_put_block(matrix_im, 1, 1, AIMAG(matrix))
    3098              : 
    3099           28 :    END SUBROUTINE put_complex_pair
    3100              : 
    3101              : ! **************************************************************************************************
    3102              : !> \brief Retrieve a dense complex matrix from a one-block DBCSR pair.
    3103              : !> \param matrix_re ...
    3104              : !> \param matrix_im ...
    3105              : !> \param matrix ...
    3106              : ! **************************************************************************************************
    3107            8 :    SUBROUTINE get_complex_pair(matrix_re, matrix_im, matrix)
    3108              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: matrix_re, matrix_im
    3109              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT)     :: matrix
    3110              : 
    3111              :       LOGICAL                                            :: found
    3112            8 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: block
    3113              : 
    3114           64 :       matrix(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    3115            8 :       CALL dbcsr_get_block_p(matrix_re, 1, 1, block, found)
    3116           64 :       IF (found) matrix = matrix + CMPLX(block, 0.0_dp, KIND=dp)
    3117            8 :       CALL dbcsr_get_block_p(matrix_im, 1, 1, block, found)
    3118           64 :       IF (found) matrix = matrix + CMPLX(0.0_dp, block, KIND=dp)
    3119              : 
    3120            8 :    END SUBROUTINE get_complex_pair
    3121              : 
    3122              : ! **************************************************************************************************
    3123              : !> \brief Dense fixed-occupation energy after finite REF orthogonalization and rotation.
    3124              : !> \param x REF coordinate
    3125              : !> \param generator anti-Hermitian rotation generator
    3126              : !> \param hamiltonian Hermitian one-particle Hamiltonian
    3127              : !> \param occupation orbital occupations
    3128              : !> \return energy
    3129              : ! **************************************************************************************************
    3130            8 :    FUNCTION rotated_polar_energy(x, generator, hamiltonian, occupation) RESULT(energy)
    3131              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: x, generator, hamiltonian
    3132              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: occupation
    3133              :       REAL(KIND=dp)                                      :: energy
    3134              : 
    3135              :       COMPLEX(KIND=dp), &
    3136           16 :          DIMENSION(SIZE(x, 1), SIZE(x, 2))               :: orbitals
    3137              :       COMPLEX(KIND=dp), &
    3138           16 :          DIMENSION(SIZE(x, 2), SIZE(x, 2))               :: inverse_sqrt, rotation
    3139              :       INTEGER                                            :: i
    3140              : 
    3141          176 :       CALL dense_inverse_sqrt(MATMUL(CONJG(TRANSPOSE(x)), x), inverse_sqrt)
    3142            8 :       rotation = dense_antihermitian_exp(generator)
    3143          496 :       orbitals = MATMUL(MATMUL(x, inverse_sqrt), rotation)
    3144            8 :       energy = 0.0_dp
    3145           24 :       DO i = 1, SIZE(occupation)
    3146              :          energy = energy + occupation(i)* &
    3147          296 :                   REAL(DOT_PRODUCT(orbitals(:, i), MATMUL(hamiltonian, orbitals(:, i))), KIND=dp)
    3148              :       END DO
    3149              : 
    3150            8 :    END FUNCTION rotated_polar_energy
    3151              : 
    3152              : ! **************************************************************************************************
    3153              : !> \brief Dense reference exponential for an anti-Hermitian matrix.
    3154              : !> \param generator anti-Hermitian generator
    3155              : !> \return its unitary exponential
    3156              : ! **************************************************************************************************
    3157           52 :    FUNCTION dense_antihermitian_exp(generator) RESULT(rotation)
    3158              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: generator
    3159              :       COMPLEX(KIND=dp), DIMENSION(SIZE(generator, 1), &
    3160              :          SIZE(generator, 2))                             :: rotation
    3161              : 
    3162              :       COMPLEX(KIND=dp), DIMENSION(SIZE(generator, 1), &
    3163           52 :          SIZE(generator, 2))                             :: hermitian, vectors
    3164              :       INTEGER                                            :: i
    3165           52 :       REAL(KIND=dp), DIMENSION(SIZE(generator, 1))       :: eigenvalues
    3166              : 
    3167          556 :       hermitian = CMPLX(0.0_dp, 1.0_dp, KIND=dp)*generator
    3168           52 :       CALL diag_complex(hermitian, vectors, eigenvalues)
    3169          556 :       rotation(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    3170          188 :       DO i = 1, SIZE(eigenvalues)
    3171              :          rotation = rotation + EXP(CMPLX(0.0_dp, -eigenvalues(i), KIND=dp))* &
    3172          544 :                     MATMUL(RESHAPE(vectors(:, i), [SIZE(generator, 1), 1]), &
    3173         4388 :                            RESHAPE(CONJG(vectors(:, i)), [1, SIZE(generator, 1)]))
    3174              :       END DO
    3175              : 
    3176           52 :    END FUNCTION dense_antihermitian_exp
    3177              : 
    3178              : ! **************************************************************************************************
    3179              : !> \brief Hermitian inverse for an independent dense reference.
    3180              : !> \param matrix ...
    3181              : !> \param inverse ...
    3182              : ! **************************************************************************************************
    3183            4 :    SUBROUTINE dense_hermitian_inverse(matrix, inverse)
    3184              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: matrix
    3185              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT)     :: inverse
    3186              : 
    3187              :       COMPLEX(KIND=dp), &
    3188            8 :          DIMENSION(SIZE(matrix, 1), SIZE(matrix, 2))     :: vectors
    3189              :       INTEGER                                            :: i
    3190            8 :       REAL(KIND=dp), DIMENSION(SIZE(matrix, 1))          :: eigenvalues
    3191              : 
    3192            4 :       CALL diag_complex(matrix, vectors, eigenvalues)
    3193           52 :       inverse(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    3194           16 :       DO i = 1, SIZE(eigenvalues)
    3195           48 :          inverse = inverse + MATMUL(RESHAPE(vectors(:, i), [SIZE(matrix, 1), 1]), &
    3196              :                                     RESHAPE(CONJG(vectors(:, i)), [1, SIZE(matrix, 1)]))/ &
    3197          436 :                    eigenvalues(i)
    3198              :       END DO
    3199              : 
    3200            4 :    END SUBROUTINE dense_hermitian_inverse
    3201              : 
    3202              : ! **************************************************************************************************
    3203              : !> \brief Hermitian inverse square root for the independent dense reference.
    3204              : !> \param matrix ...
    3205              : !> \param inverse ...
    3206              : ! **************************************************************************************************
    3207           18 :    SUBROUTINE dense_inverse_sqrt(matrix, inverse)
    3208              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: matrix
    3209              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT)     :: inverse
    3210              : 
    3211              :       COMPLEX(KIND=dp), &
    3212           36 :          DIMENSION(SIZE(matrix, 1), SIZE(matrix, 2))     :: vectors
    3213              :       INTEGER                                            :: i
    3214           36 :       REAL(KIND=dp), DIMENSION(SIZE(matrix, 1))          :: eigenvalues
    3215              : 
    3216           18 :       CALL diag_complex(matrix, vectors, eigenvalues)
    3217          126 :       inverse(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    3218           54 :       DO i = 1, SIZE(eigenvalues)
    3219          144 :          inverse = inverse + MATMUL(RESHAPE(vectors(:, i), [SIZE(matrix, 1), 1]), &
    3220              :                                     RESHAPE(CONJG(vectors(:, i)), [1, SIZE(matrix, 1)]))/ &
    3221          810 :                    SQRT(eigenvalues(i))
    3222              :       END DO
    3223              : 
    3224           18 :    END SUBROUTINE dense_inverse_sqrt
    3225              : 
    3226              : ! **************************************************************************************************
    3227              : !> \brief Energy of the polar-orthonormalized occupied subspace.
    3228              : !> \param coordinate ...
    3229              : !> \param hamiltonian ...
    3230              : !> \return ...
    3231              : ! **************************************************************************************************
    3232            4 :    FUNCTION polar_energy(coordinate, hamiltonian) RESULT(energy)
    3233              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: coordinate, hamiltonian
    3234              :       REAL(KIND=dp)                                      :: energy
    3235              : 
    3236              :       COMPLEX(KIND=dp), DIMENSION(SIZE(coordinate, 1), &
    3237            8 :          SIZE(coordinate, 2))                            :: orbitals
    3238              :       COMPLEX(KIND=dp), DIMENSION(SIZE(coordinate, 2), &
    3239            8 :          SIZE(coordinate, 2))                            :: inverse
    3240              : 
    3241           88 :       CALL dense_inverse_sqrt(MATMUL(CONJG(TRANSPOSE(coordinate)), coordinate), inverse)
    3242          116 :       orbitals = MATMUL(coordinate, inverse)
    3243          156 :       energy = REAL(SUM(CONJG(orbitals)*MATMUL(hamiltonian, orbitals)), KIND=dp)
    3244              : 
    3245            4 :    END FUNCTION polar_energy
    3246              : 
    3247              : ! **************************************************************************************************
    3248              : !> \brief Independent dense finite STRICT transformation for a Hermitian overlap.
    3249              : !> \param c0 reference orbitals
    3250              : !> \param coordinate tangent coordinate
    3251              : !> \param overlap Hermitian overlap matrix
    3252              : !> \param orbitals transformed orbitals
    3253              : ! **************************************************************************************************
    3254           10 :    SUBROUTINE strict_orbitals(c0, coordinate, overlap, orbitals)
    3255              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: c0, coordinate, overlap
    3256              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(OUT)     :: orbitals
    3257              : 
    3258              :       COMPLEX(KIND=dp), DIMENSION(SIZE(coordinate, 2), &
    3259           20 :          SIZE(coordinate, 2))                            :: cosp, sinp, vectors
    3260              :       INTEGER                                            :: i
    3261              :       REAL(KIND=dp)                                      :: root
    3262           20 :       REAL(KIND=dp), DIMENSION(SIZE(coordinate, 2))      :: eigenvalues
    3263              : 
    3264           80 :       CALL diag_complex(MATMUL(CONJG(TRANSPOSE(coordinate)), MATMUL(overlap, coordinate)), &
    3265          580 :                         vectors, eigenvalues)
    3266           70 :       cosp(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    3267           70 :       sinp(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
    3268           30 :       DO i = 1, SIZE(eigenvalues)
    3269           20 :          root = SQRT(MAX(0.0_dp, eigenvalues(i)))
    3270           80 :          cosp = cosp + COS(root)*MATMUL(RESHAPE(vectors(:, i), [SIZE(vectors, 1), 1]), &
    3271          440 :                                         RESHAPE(CONJG(vectors(:, i)), [1, SIZE(vectors, 1)]))
    3272           30 :          IF (root < 1.0E-12_dp) THEN
    3273           16 :             sinp = sinp + MATMUL(RESHAPE(vectors(:, i), [SIZE(vectors, 1), 1]), &
    3274           88 :                                  RESHAPE(CONJG(vectors(:, i)), [1, SIZE(vectors, 1)]))
    3275              :          ELSE
    3276              :             sinp = sinp + SIN(root)/root* &
    3277           64 :                    MATMUL(RESHAPE(vectors(:, i), [SIZE(vectors, 1), 1]), &
    3278          352 :                           RESHAPE(CONJG(vectors(:, i)), [1, SIZE(vectors, 1)]))
    3279              :          END IF
    3280              :       END DO
    3281          570 :       orbitals = MATMUL(c0, cosp) + MATMUL(coordinate, sinp)
    3282              : 
    3283           10 :    END SUBROUTINE strict_orbitals
    3284              : 
    3285              : ! **************************************************************************************************
    3286              : !> \brief Energy of the independently transformed finite STRICT orbitals.
    3287              : !> \param c0 reference orbitals
    3288              : !> \param coordinate tangent coordinate
    3289              : !> \param overlap Hermitian overlap matrix
    3290              : !> \param hamiltonian Hermitian Hamiltonian
    3291              : !> \return energy
    3292              : ! **************************************************************************************************
    3293              :    FUNCTION strict_energy(c0, coordinate, overlap, hamiltonian) RESULT(energy)
    3294              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: c0, coordinate, overlap, hamiltonian
    3295              :       REAL(KIND=dp)                                      :: energy
    3296              : 
    3297              :       COMPLEX(KIND=dp), DIMENSION(SIZE(coordinate, 1), &
    3298              :          SIZE(coordinate, 2))                            :: orbitals
    3299              : 
    3300              :       CALL strict_orbitals(c0, coordinate, overlap, orbitals)
    3301              :       energy = REAL(SUM(CONJG(orbitals)*MATMUL(hamiltonian, orbitals)), KIND=dp)
    3302              : 
    3303              :    END FUNCTION strict_energy
    3304              : 
    3305              : ! **************************************************************************************************
    3306              : !> \brief Energy of finite STRICT orbitals followed by a complex occupied-space rotation.
    3307              : !> \param c0 reference orbitals
    3308              : !> \param coordinate tangent coordinate
    3309              : !> \param generator anti-Hermitian rotation generator
    3310              : !> \param overlap Hermitian overlap matrix
    3311              : !> \param hamiltonian Hermitian Hamiltonian
    3312              : !> \param occupation orbital occupations
    3313              : !> \return energy
    3314              : ! **************************************************************************************************
    3315            8 :    FUNCTION strict_rotated_energy(c0, coordinate, generator, overlap, hamiltonian, &
    3316            8 :                                   occupation) RESULT(energy)
    3317              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(IN)      :: c0, coordinate, generator, overlap, &
    3318              :                                                             hamiltonian
    3319              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: occupation
    3320              :       REAL(KIND=dp)                                      :: energy
    3321              : 
    3322              :       COMPLEX(KIND=dp), DIMENSION(SIZE(coordinate, 1), &
    3323           16 :          SIZE(coordinate, 2))                            :: orbitals
    3324              :       INTEGER                                            :: i
    3325              : 
    3326            8 :       CALL strict_orbitals(c0, coordinate, overlap, orbitals)
    3327          296 :       orbitals = MATMUL(orbitals, dense_antihermitian_exp(generator))
    3328            8 :       energy = 0.0_dp
    3329           24 :       DO i = 1, SIZE(occupation)
    3330              :          energy = energy + occupation(i)* &
    3331          296 :                   REAL(DOT_PRODUCT(orbitals(:, i), MATMUL(hamiltonian, orbitals(:, i))), KIND=dp)
    3332              :       END DO
    3333              : 
    3334            8 :    END FUNCTION strict_rotated_energy
    3335              : 
    3336              : END PROGRAM qs_ot_complex_ref_unittest
        

Generated by: LCOV version 2.0-1