LCOV - code coverage report
Current view: top level - src/emd - rt_bse.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:a980871) Lines: 370 449 82.4 %
Date: 2025-02-22 01:05:29 Functions: 17 20 85.0 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2025 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \brief Routines for the propagation via RT-BSE method.
      10             : !> \note  The control is handed directly from cp2k_runs
      11             : !> \author Stepan Marek (12.23)
      12             : ! **************************************************************************************************
      13             : 
      14             : MODULE rt_bse
      15             :    USE cp_control_types, ONLY: dft_control_type, &
      16             :                                rtp_control_type
      17             :    USE qs_environment_types, ONLY: get_qs_env, &
      18             :                                    qs_environment_type
      19             :    USE force_env_types, ONLY: force_env_type
      20             :    USE qs_mo_types, ONLY: mo_set_type, &
      21             :                           init_mo_set
      22             :    USE atomic_kind_types, ONLY: atomic_kind_type
      23             :    USE qs_kind_types, ONLY: qs_kind_type
      24             :    USE particle_types, ONLY: particle_type
      25             :    USE rt_propagation_types, ONLY: get_rtp, &
      26             :                                    rt_prop_type
      27             :    USE post_scf_bandstructure_types, ONLY: post_scf_bandstructure_type
      28             :    USE cp_fm_types, ONLY: cp_fm_type, &
      29             :                           cp_fm_p_type, &
      30             :                           cp_fm_to_fm, &
      31             :                           cp_fm_create, &
      32             :                           cp_fm_set_all, &
      33             :                           cp_fm_release, &
      34             :                           cp_fm_get_element, &
      35             :                           cp_fm_set_element, &
      36             :                           cp_fm_get_info, &
      37             :                           cp_fm_write_unformatted, &
      38             :                           cp_fm_read_unformatted, &
      39             :                           cp_fm_write_formatted
      40             :    USE cp_cfm_types, ONLY: cp_cfm_type, &
      41             :                            cp_cfm_p_type, &
      42             :                            cp_fm_to_cfm, &
      43             :                            cp_cfm_to_cfm, &
      44             :                            cp_cfm_to_fm, &
      45             :                            cp_cfm_create, &
      46             :                            cp_cfm_get_info, &
      47             :                            cp_cfm_release
      48             :    USE kinds, ONLY: dp, &
      49             :                     default_path_length
      50             :    USE cp_dbcsr_contrib, ONLY: dbcsr_print
      51             :    USE cp_dbcsr_api, ONLY: dbcsr_p_type, &
      52             :                            dbcsr_type, &
      53             :                            dbcsr_has_symmetry, &
      54             :                            dbcsr_desymmetrize, &
      55             :                            dbcsr_create, &
      56             :                            dbcsr_release, &
      57             :                            dbcsr_copy, &
      58             :                            dbcsr_scale, &
      59             :                            dbcsr_add, &
      60             :                            dbcsr_set, &
      61             :                            dbcsr_clear, &
      62             :                            dbcsr_iterator_type, &
      63             :                            dbcsr_iterator_start, &
      64             :                            dbcsr_iterator_stop, &
      65             :                            dbcsr_iterator_blocks_left, &
      66             :                            dbcsr_iterator_next_block, &
      67             :                            dbcsr_put_block, &
      68             :                            dbcsr_reserve_blocks, &
      69             :                            dbcsr_get_num_blocks, &
      70             :                            dbcsr_get_block_p, &
      71             :                            dbcsr_get_info
      72             :    USE OMP_LIB, ONLY: omp_get_thread_num, &
      73             :                       omp_get_num_threads, &
      74             :                       omp_set_num_threads, &
      75             :                       omp_get_max_threads
      76             :    USE dbt_api, ONLY: dbt_clear, &
      77             :                       dbt_contract, &
      78             :                       dbt_copy_matrix_to_tensor, &
      79             :                       dbt_copy_tensor_to_matrix, &
      80             :                       dbt_type
      81             :    USE libint_2c_3c, ONLY: libint_potential_type
      82             :    USE mp2_ri_2c, ONLY: RI_2c_integral_mat
      83             :    USE qs_tensors, ONLY: neighbor_list_3c_destroy, &
      84             :                          build_2c_integrals, &
      85             :                          build_2c_neighbor_lists
      86             :    USE qs_neighbor_list_types, ONLY: neighbor_list_set_p_type, &
      87             :                                      release_neighbor_list_sets
      88             :    USE cp_dbcsr_operations, ONLY: copy_dbcsr_to_fm, &
      89             :                                   copy_fm_to_dbcsr, &
      90             :                                   dbcsr_allocate_matrix_set, &
      91             :                                   dbcsr_deallocate_matrix_set, &
      92             :                                   cp_dbcsr_sm_fm_multiply
      93             :    USE cp_fm_basic_linalg, ONLY: cp_fm_scale, &
      94             :                                  cp_fm_invert, &
      95             :                                  cp_fm_trace, &
      96             :                                  cp_fm_transpose, &
      97             :                                  cp_fm_norm, &
      98             :                                  cp_fm_column_scale, &
      99             :                                  cp_fm_scale_and_add
     100             :    USE cp_cfm_basic_linalg, ONLY: cp_cfm_scale_and_add, &
     101             :                                   cp_cfm_scale, &
     102             :                                   cp_cfm_transpose, &
     103             :                                   cp_cfm_norm, &
     104             :                                   cp_cfm_trace, &
     105             :                                   cp_cfm_column_scale
     106             :    USE cp_fm_diag, ONLY: cp_fm_geeig
     107             :    USE cp_cfm_diag, ONLY: cp_cfm_geeig
     108             :    USE parallel_gemm_api, ONLY: parallel_gemm
     109             :    USE qs_moments, ONLY: build_local_moment_matrix, &
     110             :                          build_berry_moment_matrix
     111             :    USE moments_utils, ONLY: get_reference_point
     112             :    USE qs_mo_io, ONLY: read_mo_set_from_restart
     113             :    USE qs_ks_methods, ONLY: qs_ks_build_kohn_sham_matrix
     114             :    USE force_env_methods, ONLY: force_env_calc_energy_force
     115             :    USE qs_energy_init, ONLY: qs_energies_init
     116             :    USE qs_energy_utils, ONLY: qs_energies_properties
     117             :    USE efield_utils, ONLY: make_field
     118             :    USE rt_propagator_init, ONLY: rt_initialize_rho_from_mos
     119             :    USE rt_propagation_methods, ONLY: s_matrices_create
     120             :    USE gw_large_cell_Gamma, ONLY: compute_3c_integrals
     121             :    USE gw_integrals, ONLY: build_3c_integral_block
     122             :    USE matrix_exp, ONLY: taylor_full_complex
     123             :    USE message_passing, ONLY: mp_para_env_type
     124             :    USE input_section_types, ONLY: section_vals_get_subs_vals, &
     125             :                                   section_vals_type
     126             :    USE cell_types, ONLY: cell_type
     127             :    USE input_constants, ONLY: rtp_bse_ham_ks, &
     128             :                               rtp_bse_ham_g0w0, &
     129             :                               use_mom_ref_coac, &
     130             :                               use_mom_ref_zero, &
     131             :                               do_taylor, &
     132             :                               do_bch, &
     133             :                               do_exact, &
     134             :                               use_rt_restart
     135             :    USE rt_bse_types, ONLY: rtbse_env_type, &
     136             :                            create_rtbse_env, &
     137             :                            release_rtbse_env, &
     138             :                            multiply_cfm_fm, &
     139             :                            multiply_fm_cfm
     140             :    USE rt_bse_io, ONLY: output_moments, &
     141             :                         read_moments, &
     142             :                         read_field, &
     143             :                         output_moments_ft, &
     144             :                         output_polarizability, &
     145             :                         output_field, &
     146             :                         output_mos_contravariant, &
     147             :                         read_restart, &
     148             :                         output_restart, &
     149             :                         print_timestep_info, &
     150             :                         print_etrs_info_header, &
     151             :                         print_etrs_info, &
     152             :                         print_rtbse_header_info
     153             :    USE qs_ks_types, ONLY: qs_ks_env_type
     154             :    USE qs_integrate_potential_product, ONLY: integrate_v_rspace
     155             :    USE qs_collocate_density, ONLY: calculate_rho_elec
     156             :    USE cp_files, ONLY: open_file, &
     157             :                        file_exists, &
     158             :                        close_file
     159             :    USE physcon, ONLY: femtoseconds
     160             :    USE mathconstants, ONLY: twopi
     161             : 
     162             : #include "../base/base_uses.f90"
     163             : 
     164             :    IMPLICIT NONE
     165             : 
     166             :    PRIVATE
     167             : 
     168             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = "rt_bse"
     169             : 
     170             :    #:include "rt_bse_macros.fypp"
     171             : 
     172             :    PUBLIC :: run_propagation_bse, &
     173             :              get_hartree, &
     174             :              get_sigma
     175             : 
     176             :    INTERFACE get_sigma
     177             :       MODULE PROCEDURE get_sigma_complex, &
     178             :          get_sigma_real, &
     179             :          get_sigma_dbcsr, &
     180             :          get_sigma_noenv
     181             :    END INTERFACE
     182             :    INTERFACE get_hartree
     183             :       MODULE PROCEDURE get_hartree_env, &
     184             :          get_hartree_noenv
     185             :    END INTERFACE
     186             : 
     187             : CONTAINS
     188             : 
     189             : ! **************************************************************************************************
     190             : !> \brief Runs the electron-only real time BSE propagation
     191             : !> \param qs_env Quickstep environment data, containing the config from input files
     192             : !> \param force_env Force environment data, entry point of the calculation
     193             : ! **************************************************************************************************
     194          12 :    SUBROUTINE run_propagation_bse(qs_env, force_env)
     195             :       TYPE(qs_environment_type), POINTER                 :: qs_env
     196             :       TYPE(force_env_type), POINTER                      :: force_env
     197             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'run_propagation_bse'
     198             :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     199             :       INTEGER                                            :: i, j, k, handle
     200             :       LOGICAL                                            :: converged
     201             :       REAL(kind=dp)                                      :: metric, enum_re, enum_im, &
     202             :                                                             idempotence_dev, a_metric_1, a_metric_2
     203             : 
     204          12 :       CALL timeset(routineN, handle)
     205             : 
     206             :       ! Run the initial SCF calculation / read SCF restart information
     207          12 :       CALL force_env_calc_energy_force(force_env, calc_force=.FALSE., consistent_energies=.FALSE.)
     208             : 
     209             :       ! Allocate all persistant storage and read input that does not need further processing
     210          12 :       CALL create_rtbse_env(rtbse_env, qs_env, force_env)
     211             : 
     212          12 :       IF (rtbse_env%unit_nr > 0) CALL print_rtbse_header_info(rtbse_env)
     213             : 
     214             :       ! Initialize non-trivial values
     215             :       !  - calculates the moment operators
     216             :       !  - populates the initial density matrix
     217             :       !     - reads the restart density if requested
     218             :       !  - reads the field and moment trace from previous runs
     219             :       !  - populates overlap and inverse overlap matrices
     220             :       !  - calculates/populates the G0W0/KS Hamiltonian, respectively
     221             :       !  - calculates the Hartree reference potential
     222             :       !  - calculates the COHSEX reference self-energy
     223             :       !  - prints some info about loaded files into the output
     224          12 :       CALL initialize_rtbse_env(rtbse_env)
     225             : 
     226             :       ! Setup the time based on the starting step
     227             :       ! Assumes identical dt between two runs
     228          12 :       rtbse_env%sim_time = REAL(rtbse_env%sim_start, dp)*rtbse_env%sim_dt
     229             :       ! Output 0 time moments and field
     230          12 :       IF (.NOT. rtbse_env%restart_extracted) THEN
     231           8 :          CALL output_field(rtbse_env)
     232           8 :          CALL output_moments(rtbse_env, rtbse_env%rho)
     233             :       END IF
     234             : 
     235             :       ! Do not apply the delta kick if we are doing a restart calculation
     236          12 :       IF (rtbse_env%dft_control%rtp_control%apply_delta_pulse .AND. (.NOT. rtbse_env%restart_extracted)) THEN
     237           4 :          CALL apply_delta_pulse(rtbse_env)
     238             :       END IF
     239             : 
     240             :       ! ********************** Start the time loop **********************
     241         620 :       DO i = rtbse_env%sim_start, rtbse_env%sim_nsteps
     242             : 
     243             :          ! Update the simulation time
     244         608 :          rtbse_env%sim_time = REAL(i, dp)*rtbse_env%sim_dt
     245         608 :          rtbse_env%sim_step = i
     246             :          ! Carry out the ETRS self-consistent propagation - propagates rho to rho_new (through rho_M)
     247         608 :          CALL etrs_scf_loop(rtbse_env, rtbse_env%rho, rtbse_env%rho_M, rtbse_env%rho_new, converged, k, metric)
     248         608 :          CALL get_electron_number(rtbse_env, rtbse_env%rho_new, enum_re, enum_im)
     249             :          IF (.FALSE.) THEN
     250             :             ! Not all of these are used, but they are all good metrics to check the convergence in problematic cases
     251             :             ! TODO : Allow for conditional warning
     252             :             CALL get_idempotence_deviation(rtbse_env, rtbse_env%rho_new, idempotence_dev)
     253             :             DO j = 1, rtbse_env%n_spin
     254             :                CALL cp_cfm_to_fm(rtbse_env%sigma_SEX(j), rtbse_env%real_workspace(1), rtbse_env%real_workspace(2))
     255             :                CALL antiherm_metric(real_fm=rtbse_env%real_workspace(1), imag_fm=rtbse_env%real_workspace(2), &
     256             :                                     workspace=rtbse_env%rho_workspace, metric=a_metric_1)
     257             :                CALL antiherm_metric(real_fm=rtbse_env%hartree_curr(j), &
     258             :                                     workspace=rtbse_env%rho_workspace, metric=a_metric_2)
     259             :             END DO
     260             :          END IF
     261         608 :          CALL print_timestep_info(rtbse_env, i, metric, enum_re, k)
     262         608 :          IF (.NOT. converged) CPABORT("ETRS did not converge")
     263        1216 :          DO j = 1, rtbse_env%n_spin
     264        1216 :             CALL cp_cfm_to_cfm(rtbse_env%rho_new(j), rtbse_env%rho(j))
     265             :          END DO
     266             :          ! Print the updated field
     267         608 :          CALL output_field(rtbse_env)
     268             :          ! If needed, print out the density matrix in MO basis
     269         608 :          CALL output_mos_contravariant(rtbse_env, rtbse_env%rho, rtbse_env%rho_section)
     270             :          ! Also handles outputting to memory
     271         608 :          CALL output_moments(rtbse_env, rtbse_env%rho)
     272             :          ! Output restart files, so that the restart starts at the following time index
     273        1228 :          CALL output_restart(rtbse_env, rtbse_env%rho, i + 1)
     274             :       END DO
     275             :       ! ********************** End the time loop **********************
     276             : 
     277             :       ! Carry out the FT
     278          12 :       CALL output_moments_ft(rtbse_env)
     279          12 :       CALL output_polarizability(rtbse_env)
     280             : 
     281             :       ! Deallocate everything
     282          12 :       CALL release_rtbse_env(rtbse_env)
     283             : 
     284          12 :       CALL timestop(handle)
     285          12 :    END SUBROUTINE run_propagation_bse
     286             : 
     287             : ! **************************************************************************************************
     288             : !> \brief Calculates the initial values, based on restart/scf density, and other non-trivial values
     289             : !> \param rtbse_env RT-BSE environment
     290             : !> \param qs_env Quickstep environment (needed for reference to previous calculations)
     291             : !> \author Stepan Marek (09.24)
     292             : ! **************************************************************************************************
     293          12 :    SUBROUTINE initialize_rtbse_env(rtbse_env)
     294             :       TYPE(rtbse_env_type), POINTER                :: rtbse_env
     295             :       CHARACTER(len=*), PARAMETER                  :: routineN = "initialize_rtbse_env"
     296             :       TYPE(post_scf_bandstructure_type), POINTER   :: bs_env
     297          12 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER    :: moments_dbcsr_p
     298          12 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER    :: matrix_s
     299          12 :       REAL(kind=dp), DIMENSION(:), POINTER         :: custom_ref_point, &
     300          12 :                                                       occupations
     301             :       REAL(kind=dp), DIMENSION(3)                  :: rpoint
     302             :       INTEGER                                      :: i, k, handle
     303             : 
     304          12 :       CALL timeset(routineN, handle)
     305             : 
     306             :       ! Get pointers to parameters from qs_env
     307          12 :       CALL get_qs_env(rtbse_env%qs_env, bs_env=bs_env, matrix_s=matrix_s)
     308             : 
     309             :       ! ****** START MOMENTS OPERATOR CALCULATION
     310             :       ! Construct moments from dbcsr
     311             :       NULLIFY (moments_dbcsr_p)
     312          48 :       ALLOCATE (moments_dbcsr_p(3))
     313          48 :       DO k = 1, 3
     314             :          ! Make sure the pointer is empty
     315          36 :          NULLIFY (moments_dbcsr_p(k)%matrix)
     316             :          ! Allocate a new matrix that the pointer points to
     317          36 :          ALLOCATE (moments_dbcsr_p(k)%matrix)
     318             :          ! Create the matrix storage - matrix copies the structure of overlap matrix
     319          48 :          CALL dbcsr_copy(moments_dbcsr_p(k)%matrix, matrix_s(1)%matrix)
     320             :       END DO
     321             :       ! Run the moment calculation
     322             :       ! check for presence to prevent memory errors
     323             :       NULLIFY (custom_ref_point)
     324          48 :       ALLOCATE (custom_ref_point(3), source=0.0_dp)
     325          12 :       rpoint(:) = 0.0_dp
     326          12 :       CALL get_reference_point(rpoint, qs_env=rtbse_env%qs_env, reference=use_mom_ref_coac, ref_point=custom_ref_point)
     327          12 :       CALL build_local_moment_matrix(rtbse_env%qs_env, moments_dbcsr_p, 1, rpoint)
     328             :       ! Copy to full matrix
     329          48 :       DO k = 1, 3
     330             :          ! Again, matrices are created from overlap template
     331          48 :          CALL copy_dbcsr_to_fm(moments_dbcsr_p(k)%matrix, rtbse_env%moments(k))
     332             :       END DO
     333             :       ! Now, repeat without reference point to get the moments for field
     334          12 :       CALL get_reference_point(rpoint, qs_env=rtbse_env%qs_env, reference=use_mom_ref_zero, ref_point=custom_ref_point)
     335          12 :       DEALLOCATE (custom_ref_point)
     336          12 :       CALL build_local_moment_matrix(rtbse_env%qs_env, moments_dbcsr_p, 1, rpoint)
     337          48 :       DO k = 1, 3
     338          48 :          CALL copy_dbcsr_to_fm(moments_dbcsr_p(k)%matrix, rtbse_env%moments_field(k))
     339             :       END DO
     340             : 
     341             :       ! Now can deallocate dbcsr matrices
     342          48 :       DO k = 1, 3
     343          36 :          CALL dbcsr_release(moments_dbcsr_p(k)%matrix)
     344          48 :          DEALLOCATE (moments_dbcsr_p(k)%matrix)
     345             :       END DO
     346          12 :       DEALLOCATE (moments_dbcsr_p)
     347             :       ! ****** END MOMENTS OPERATOR CALCULATION
     348             : 
     349             :       ! ****** START INITIAL DENSITY MATRIX CALCULATION
     350             :       ! Get the rho from fm_MOS
     351             :       ! Uses real orbitals only - no kpoints
     352          36 :       ALLOCATE (occupations(rtbse_env%n_ao))
     353             :       ! Iterate over both spins
     354          24 :       DO i = 1, rtbse_env%n_spin
     355          36 :          occupations(:) = 0.0_dp
     356          24 :          occupations(1:rtbse_env%n_occ(i)) = 1.0_dp
     357             :          ! Create real part
     358          12 :          CALL cp_fm_to_fm(bs_env%fm_mo_coeff_Gamma(i), rtbse_env%real_workspace(1))
     359          12 :          CALL cp_fm_column_scale(rtbse_env%real_workspace(1), occupations)
     360             :          CALL parallel_gemm("N", "T", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     361             :                             1.0_dp, rtbse_env%real_workspace(1), bs_env%fm_mo_coeff_Gamma(i), &
     362          12 :                             0.0_dp, rtbse_env%real_workspace(2))
     363             :          ! Sets imaginary part to zero
     364          12 :          CALL cp_fm_to_cfm(msourcer=rtbse_env%real_workspace(2), mtarget=rtbse_env%rho(i))
     365             :          ! Save the reference value for the case of delta kick
     366          24 :          CALL cp_cfm_to_cfm(rtbse_env%rho(i), rtbse_env%rho_orig(i))
     367             :       END DO
     368          12 :       DEALLOCATE (occupations)
     369             :       ! If the restart field is provided, overwrite rho from restart
     370          12 :       IF (rtbse_env%dft_control%rtp_control%initial_wfn == use_rt_restart) THEN
     371           4 :          CALL read_restart(rtbse_env)
     372             :       END IF
     373             :       ! ****** END INITIAL DENSITY MATRIX CALCULATION
     374             :       ! ****** START MOMENTS TRACE LOAD
     375             :       ! The moments are only loaded if the consistent save files exist
     376          12 :       CALL read_moments(rtbse_env)
     377             :       ! ****** END MOMENTS TRACE LOAD
     378             : 
     379             :       ! ****** START FIELD TRACE LOAD
     380             :       ! The moments are only loaded if the consistent save files exist
     381          12 :       CALL read_field(rtbse_env)
     382             :       ! ****** END FIELD TRACE LOAD
     383             : 
     384             :       ! ****** START OVERLAP + INVERSE OVERLAP CALCULATION
     385          12 :       CALL copy_dbcsr_to_fm(matrix_s(1)%matrix, rtbse_env%S_fm)
     386          12 :       CALL cp_fm_to_cfm(msourcer=rtbse_env%S_fm, mtarget=rtbse_env%S_cfm)
     387          12 :       CALL cp_fm_invert(rtbse_env%S_fm, rtbse_env%S_inv_fm)
     388             :       ! ****** END OVERLAP + INVERSE OVERLAP CALCULATION
     389             : 
     390             :       ! ****** START SINGLE PARTICLE HAMILTONIAN CALCULATION
     391          24 :       DO i = 1, rtbse_env%n_spin
     392          24 :          IF (rtbse_env%ham_reference_type == rtp_bse_ham_g0w0) THEN
     393             :             ! G0W0 Hamiltonian
     394          12 :             CALL cp_fm_to_fm(bs_env%fm_mo_coeff_Gamma(i), rtbse_env%real_workspace(1))
     395             :             ! NOTE : Gamma point is not always the zero k-point
     396             :             ! C * Lambda
     397          12 :             CALL cp_fm_column_scale(rtbse_env%real_workspace(1), bs_env%eigenval_G0W0(:, 1, i))
     398             :             ! C * Lambda * C^T
     399             :             CALL parallel_gemm("N", "T", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     400             :                                1.0_dp, rtbse_env%real_workspace(1), bs_env%fm_mo_coeff_Gamma(i), &
     401          12 :                                0.0_dp, rtbse_env%real_workspace(2))
     402             :             ! S * C * Lambda * C^T
     403             :             CALL parallel_gemm("N", "N", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     404             :                                1.0_dp, rtbse_env%S_fm, rtbse_env%real_workspace(2), &
     405          12 :                                0.0_dp, rtbse_env%real_workspace(1))
     406             :             ! S * C * Lambda * C^T * S = H
     407             :             CALL parallel_gemm("N", "N", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     408             :                                1.0_dp, rtbse_env%real_workspace(1), rtbse_env%S_fm, &
     409          12 :                                0.0_dp, rtbse_env%real_workspace(2))
     410          12 :             CALL cp_fm_to_cfm(msourcer=rtbse_env%real_workspace(2), mtarget=rtbse_env%ham_reference(i))
     411             :          ELSE
     412             :             ! KS Hamiltonian
     413           0 :             CALL cp_fm_to_cfm(msourcer=bs_env%fm_ks_Gamma(i), mtarget=rtbse_env%ham_reference(i))
     414             :          END IF
     415             :       END DO
     416             :       ! ****** END SINGLE PARTICLE HAMILTONIAN CALCULATION
     417             : 
     418             :       ! ****** START HARTREE POTENTIAL REFERENCE CALCULATION
     419             :       ! Calculate Coulomb RI elements, necessary for Hartree calculation
     420          12 :       CALL init_hartree(rtbse_env, rtbse_env%v_dbcsr)
     421          12 :       IF (rtbse_env%ham_reference_type == rtp_bse_ham_g0w0) THEN
     422             :          ! In a non-HF calculation, copy the actual correlation part of the interaction
     423          12 :          CALL copy_fm_to_dbcsr(bs_env%fm_W_MIC_freq_zero, rtbse_env%w_dbcsr)
     424             :       ELSE
     425             :          ! In HF, correlation is set to zero
     426           0 :          CALL dbcsr_set(rtbse_env%w_dbcsr, 0.0_dp)
     427             :       END IF
     428             :       ! Add the Hartree to the screened_dbt tensor - now W = V + W^c
     429          12 :       CALL dbcsr_add(rtbse_env%w_dbcsr, rtbse_env%v_dbcsr, 1.0_dp, 1.0_dp)
     430          12 :       CALL dbt_copy_matrix_to_tensor(rtbse_env%w_dbcsr, rtbse_env%screened_dbt)
     431             :       ! Calculate the original Hartree potential
     432             :       ! Uses rho_orig - same as rho for initial run but different for continued run
     433          24 :       DO i = 1, rtbse_env%n_spin
     434          12 :          CALL get_hartree(rtbse_env, rtbse_env%rho_orig(i), rtbse_env%hartree_curr(i))
     435             :          ! Scaling by spin degeneracy
     436          12 :          CALL cp_fm_scale(rtbse_env%spin_degeneracy, rtbse_env%hartree_curr(i))
     437             :          ! Subtract the reference from the reference Hamiltonian
     438          12 :          CALL cp_fm_to_cfm(msourcer=rtbse_env%hartree_curr(i), mtarget=rtbse_env%ham_workspace(1))
     439             :          CALL cp_cfm_scale_and_add(CMPLX(1.0, 0.0, kind=dp), rtbse_env%ham_reference(i), &
     440          24 :                                    CMPLX(-1.0, 0.0, kind=dp), rtbse_env%ham_workspace(1))
     441             :       END DO
     442             :       ! ****** END HARTREE POTENTIAL REFERENCE CALCULATION
     443             : 
     444             :       ! ****** START COHSEX REFERENCE CALCULATION
     445             :       ! Calculate the COHSEX starting energies
     446          12 :       IF (rtbse_env%ham_reference_type == rtp_bse_ham_g0w0) THEN
     447             :          ! Subtract the v_xc from COH part of the self-energy, as V_xc is also not updated during the timestepping
     448          24 :          DO i = 1, rtbse_env%n_spin
     449             :             ! TODO : Allow no COH calculation for static screening
     450          12 :             CALL get_sigma(rtbse_env, rtbse_env%sigma_COH(i), -0.5_dp, rtbse_env%S_inv_fm)
     451             :             ! Copy and subtract from the complex reference hamiltonian
     452          12 :             CALL cp_fm_to_cfm(msourcer=rtbse_env%sigma_COH(i), mtarget=rtbse_env%ham_workspace(1))
     453             :             CALL cp_cfm_scale_and_add(CMPLX(1.0, 0.0, kind=dp), rtbse_env%ham_reference(i), &
     454          12 :                                       CMPLX(-1.0, 0.0, kind=dp), rtbse_env%ham_workspace(1))
     455             :             ! Calculate exchange part - TODO : should this be applied for different spins? - TEST with O2 HF propagation?
     456             :             ! So far only closed shell tested
     457             :             ! Uses rho_orig - same as rho for initial run but different for continued run
     458          12 :             CALL get_sigma(rtbse_env, rtbse_env%sigma_SEX(i), -1.0_dp, rtbse_env%rho_orig(i))
     459             :             ! Subtract from the complex reference Hamiltonian
     460             :             CALL cp_cfm_scale_and_add(CMPLX(1.0, 0.0, kind=dp), rtbse_env%ham_reference(i), &
     461          24 :                                       CMPLX(-1.0, 0.0, kind=dp), rtbse_env%sigma_SEX(i))
     462             :          END DO
     463             :       ELSE
     464             :          ! KS Hamiltonian - use time-dependent Fock exchange
     465           0 :          DO i = 1, rtbse_env%n_spin
     466           0 :             CALL get_sigma(rtbse_env, rtbse_env%sigma_SEX(i), -1.0_dp, rtbse_env%rho_orig(i))
     467             :             CALL cp_cfm_scale_and_add(CMPLX(1.0, 0.0, kind=dp), rtbse_env%ham_reference(i), &
     468           0 :                                       CMPLX(-1.0, 0.0, kind=dp), rtbse_env%sigma_SEX(i))
     469             :          END DO
     470             :       END IF
     471             :       ! ****** END COHSEX REFERENCE CALCULATION
     472             : 
     473          12 :       CALL timestop(handle)
     474          12 :    END SUBROUTINE initialize_rtbse_env
     475             : 
     476             : ! **************************************************************************************************
     477             : !> \brief Custom reimplementation of the delta pulse routines
     478             : !> \param rtbse_env RT-BSE environment
     479             : !> \author Stepan Marek (09.24)
     480             : ! **************************************************************************************************
     481           4 :    SUBROUTINE apply_delta_pulse(rtbse_env)
     482             :       TYPE(rtbse_env_type), POINTER                :: rtbse_env
     483             :       CHARACTER(len=*), PARAMETER                  :: routineN = "apply_delta_pulse"
     484             :       REAL(kind=dp)                                :: intensity, metric
     485             :       REAL(kind=dp), DIMENSION(3)                  :: kvec
     486             :       INTEGER                                      :: i, k, handle
     487             : 
     488           4 :       CALL timeset(routineN, handle)
     489             : 
     490             :       ! Report application
     491           4 :       IF (rtbse_env%unit_nr > 0) WRITE (rtbse_env%unit_nr, '(A28)') ' RTBSE| Applying delta pulse'
     492             :       ! Extra minus for the propagation of density
     493           4 :       intensity = -rtbse_env%dft_control%rtp_control%delta_pulse_scale
     494           4 :       metric = 0.0_dp
     495          16 :       kvec(:) = rtbse_env%dft_control%rtp_control%delta_pulse_direction(:)
     496           4 :       IF (rtbse_env%unit_nr > 0) WRITE (rtbse_env%unit_nr, '(A38,E14.4E3,E14.4E3,E14.4E3)') &
     497           8 :          " RTBSE| Delta pulse elements (a.u.) : ", intensity*kvec(:)
     498             :       ! So far no spin dependence, but can be added by different structure of delta pulse
     499           4 :       CALL cp_fm_set_all(rtbse_env%real_workspace(1), 0.0_dp)
     500          16 :       DO k = 1, 3
     501             :          CALL cp_fm_scale_and_add(1.0_dp, rtbse_env%real_workspace(1), &
     502          16 :                                   kvec(k), rtbse_env%moments_field(k))
     503             :       END DO
     504             :       ! enforce hermiticity of the effective Hamiltonian
     505           4 :       CALL cp_fm_transpose(rtbse_env%real_workspace(1), rtbse_env%real_workspace(2))
     506             :       CALL cp_fm_scale_and_add(0.5_dp, rtbse_env%real_workspace(1), &
     507           4 :                                0.5_dp, rtbse_env%real_workspace(2))
     508             :       ! Prepare the exponential/exponent for propagation
     509           4 :       IF (rtbse_env%mat_exp_method == do_bch) THEN
     510             :          ! Multiply by the S_inv matrix - in the classic ordering
     511             :          CALL parallel_gemm("N", "N", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     512             :                             intensity, rtbse_env%S_inv_fm, rtbse_env%real_workspace(1), &
     513           4 :                             0.0_dp, rtbse_env%real_workspace(2))
     514           8 :          DO i = 1, rtbse_env%n_spin
     515             :             ! Sets real part to zero
     516           8 :             CALL cp_fm_to_cfm(msourcei=rtbse_env%real_workspace(2), mtarget=rtbse_env%ham_workspace(i))
     517             :          END DO
     518           0 :       ELSE IF (rtbse_env%mat_exp_method == do_exact) THEN
     519           0 :          DO i = 1, rtbse_env%n_spin
     520           0 :             CALL cp_fm_to_cfm(msourcer=rtbse_env%real_workspace(1), mtarget=rtbse_env%ham_effective(i))
     521             :             CALL cp_cfm_gexp(rtbse_env%ham_effective(i), rtbse_env%S_cfm, rtbse_env%ham_workspace(i), &
     522           0 :                              CMPLX(0.0, intensity, kind=dp), rtbse_env%rho_workspace)
     523             :          END DO
     524             :       END IF
     525             :       ! Propagate the density by the effect of the delta pulse
     526           4 :       CALL propagate_density(rtbse_env, rtbse_env%ham_workspace, rtbse_env%rho, rtbse_env%rho_new)
     527           4 :       metric = rho_metric(rtbse_env%rho_new, rtbse_env%rho, rtbse_env%n_spin)
     528           4 :       IF (rtbse_env%unit_nr > 0) WRITE (rtbse_env%unit_nr, ('(A42,E38.8E3)')) " RTBSE| Metric difference after delta kick", metric
     529             :       ! Copy the new density to the old density
     530           8 :       DO i = 1, rtbse_env%n_spin
     531           8 :          CALL cp_cfm_to_cfm(rtbse_env%rho_new(i), rtbse_env%rho(i))
     532             :       END DO
     533             : 
     534           4 :       CALL timestop(handle)
     535           4 :    END SUBROUTINE apply_delta_pulse
     536             : ! **************************************************************************************************
     537             : !> \brief Determines the metric for the density matrix, used for convergence criterion
     538             : !> \param rho_new Array of new density matrices (one for each spin index)
     539             : !> \param rho_old Array of old density matrices (one for each spin index)
     540             : !> \param nspin Number of spin indices
     541             : !> \param workspace_opt Optionally provide external workspace to save some allocation time
     542             : ! **************************************************************************************************
     543       17646 :    FUNCTION rho_metric(rho_new, rho_old, nspin, workspace_opt) RESULT(metric)
     544             :       TYPE(cp_cfm_type), DIMENSION(:), POINTER, INTENT(IN):: rho_new, &
     545             :                                                              rho_old
     546             :       INTEGER, INTENT(IN)                                :: nspin
     547             :       TYPE(cp_cfm_type), POINTER, OPTIONAL               :: workspace_opt
     548             :       TYPE(cp_cfm_type)                                  :: workspace
     549             :       REAL(kind=dp)                                      :: metric
     550       17646 :       REAL(kind=dp), DIMENSION(:), ALLOCATABLE           :: partial_metric
     551             :       INTEGER                                            :: j
     552             :       COMPLEX(kind=dp)                                   :: scale_factor
     553             : 
     554       52938 :       ALLOCATE (partial_metric(nspin))
     555             : 
     556             :       ! Only allocate/deallocate storage if required
     557       17646 :       IF (PRESENT(workspace_opt)) THEN
     558           0 :          workspace = workspace_opt
     559             :       ELSE
     560       17646 :          CALL cp_cfm_create(workspace, rho_new(1)%matrix_struct)
     561             :       END IF
     562       17646 :       scale_factor = 1.0
     563       35292 :       DO j = 1, nspin
     564       17646 :          CALL cp_cfm_to_cfm(rho_new(j), workspace)
     565             :          ! Get the difference in the resulting matrix
     566       17646 :          CALL cp_cfm_scale_and_add(scale_factor, workspace, -scale_factor, rho_old(j))
     567             :          ! Now, get the relevant number
     568       35292 :          partial_metric(j) = cp_cfm_norm(workspace, 'M')
     569             :       END DO
     570             :       metric = 0.0_dp
     571             :       ! For more than one spin, do Cartesian sum of the different spin norms
     572       35292 :       DO j = 1, nspin
     573       35292 :          metric = metric + partial_metric(j)*partial_metric(j)
     574             :       END DO
     575       17646 :       metric = SQRT(metric)
     576             :       ! Deallocate workspace
     577       17646 :       IF (.NOT. PRESENT(workspace_opt)) CALL cp_cfm_release(workspace)
     578       17646 :       DEALLOCATE (partial_metric)
     579       17646 :    END FUNCTION
     580             : 
     581             : ! **************************************************************************************************
     582             : !> \brief Determines the metric of the antihermitian part of the matrix
     583             : !> \param real_fm Real part of the full matrix
     584             : !> \param imag_fm Imaginary part of the full matrix
     585             : ! **************************************************************************************************
     586           0 :    SUBROUTINE antiherm_metric(real_fm, imag_fm, workspace, metric)
     587             :       TYPE(cp_fm_type), INTENT(IN)                      :: real_fm
     588             :       TYPE(cp_fm_type), INTENT(IN), OPTIONAL            :: imag_fm
     589             :       REAL(kind=dp), INTENT(OUT)                        :: metric
     590             :       TYPE(cp_cfm_type), DIMENSION(:), POINTER          :: workspace
     591             :       COMPLEX(kind=dp)                                  :: complex_one
     592             : 
     593             :       ! Get the complex and complex conjugate matrix
     594           0 :       IF (PRESENT(imag_fm)) THEN
     595           0 :          CALL cp_fm_to_cfm(real_fm, imag_fm, workspace(1))
     596             :       ELSE
     597           0 :          CALL cp_fm_to_cfm(msourcer=real_fm, mtarget=workspace(1))
     598             :       END IF
     599           0 :       CALL cp_cfm_transpose(workspace(1), "C", workspace(2))
     600             :       ! Subtract these, and get the metric
     601           0 :       complex_one = CMPLX(1.0, 0.0, kind=dp)
     602           0 :       CALL cp_cfm_scale_and_add(complex_one, workspace(1), -complex_one, workspace(2))
     603           0 :       metric = cp_cfm_norm(workspace(1), "M")
     604           0 :    END SUBROUTINE
     605             : 
     606             : ! **************************************************************************************************
     607             : !> \brief For Taylor and Exact exp_method, calculates the matrix exponential of the
     608             : !>        effective Hamiltonian. For BCH, calculates just the effective Hamiltonian. For other methods,
     609             : !>        aborts the execution, as they are not implemented yet.
     610             : !> \param rtbse_env Entry point of the calculation. Uses rho_workspace for Taylor and BCH. For exact,
     611             : !>                  uses complex_workspace, complex_ham, complex_s, real_eigvals and exp_eigvals.
     612             : !>                  Results are stored in ham_workspace.
     613             : ! **************************************************************************************************
     614        2024 :    SUBROUTINE ham_to_exp(rtbse_env, ham, ham_exp)
     615             :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     616             :       TYPE(cp_cfm_type), DIMENSION(:), POINTER           :: ham, &
     617             :                                                             ham_exp
     618             :       CHARACTER(len=*), PARAMETER                        :: routineN = "ham_to_exp"
     619             :       INTEGER                                            :: j, handle
     620        2024 :       CALL timeset(routineN, handle)
     621        4048 :       DO j = 1, rtbse_env%n_spin
     622        4048 :          IF (rtbse_env%mat_exp_method == do_bch) THEN
     623             :             ! In Taylor and BCH, we first evaluate the entire exponent and then evaluate exponential in series
     624             :             ! In order to produce correct result, need to remultiply by inverse overlap matrix
     625             :             CALL multiply_fm_cfm("N", "N", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     626             :                                  1.0_dp, rtbse_env%S_inv_fm, ham(j), &
     627        2024 :                                  0.0_dp, rtbse_env%rho_workspace(1))
     628             : 
     629             :             ! The evolution of density matrix is derived from the right multiplying term
     630             :             ! Imaginary part of the exponent = -real part of the matrix
     631        2024 :             CALL cp_cfm_scale(CMPLX(0.0, -rtbse_env%sim_dt/2, kind=dp), rtbse_env%rho_workspace(1))
     632             :             ! In BCH, exponential is not calculated explicitly, but the propagation is solved in series
     633        2024 :             CALL cp_cfm_to_cfm(rtbse_env%rho_workspace(1), ham_exp(j))
     634           0 :          ELSE IF (rtbse_env%mat_exp_method == do_exact) THEN
     635             :             CALL cp_cfm_gexp(ham(j), rtbse_env%S_cfm, ham_exp(j), &
     636           0 :                              CMPLX(0.0, -rtbse_env%sim_dt/2, kind=dp), rtbse_env%rho_workspace)
     637             :          ELSE
     638           0 :             CPABORT("Only BCH and Taylor matrix exponentiation implemented")
     639             :          END IF
     640             :       END DO
     641             : 
     642        2024 :       CALL timestop(handle)
     643        2024 :    END SUBROUTINE
     644             : ! **************************************************************************************************
     645             : !> \brief Updates the effective Hamiltonian, given a density matrix rho
     646             : !> \param rtbse_env Entry point of the calculation - contains current state of variables
     647             : !> \param qs_env QS env
     648             : !> \param rho Real and imaginary parts ( + spin) of the density at current time
     649             : ! **************************************************************************************************
     650        2024 :    SUBROUTINE update_effective_ham(rtbse_env, rho)
     651             :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     652             :       TYPE(cp_cfm_type), DIMENSION(:), POINTER           :: rho
     653             :       CHARACTER(len=*), PARAMETER                        :: routineN = "update_effective_ham"
     654             :       INTEGER                                            :: k, j, nspin, handle
     655             : 
     656        2024 :       CALL timeset(routineN, handle)
     657             :       ! Shorthand
     658        2024 :       nspin = rtbse_env%n_spin
     659             :       ! Reset the effective Hamiltonian to KS Hamiltonian + G0W0 - reference COHSEX - reference Hartree
     660        4048 :       DO j = 1, nspin
     661             :          ! Sets the imaginary part to zero
     662        4048 :          CALL cp_cfm_to_cfm(rtbse_env%ham_reference(j), rtbse_env%ham_effective(j))
     663             :       END DO
     664             :       ! Determine the field at current time
     665        2024 :       IF (rtbse_env%dft_control%apply_efield_field) THEN
     666         640 :          CALL make_field(rtbse_env%dft_control, rtbse_env%field, rtbse_env%sim_step, rtbse_env%sim_time)
     667             :       ELSE
     668             :          ! No field
     669        5536 :          rtbse_env%field(:) = 0.0_dp
     670             :       END IF
     671        4048 :       DO j = 1, nspin
     672        8096 :          DO k = 1, 3
     673             :             ! Minus sign due to charge of electrons
     674        6072 :             CALL cp_fm_to_cfm(msourcer=rtbse_env%moments_field(k), mtarget=rtbse_env%ham_workspace(1))
     675             :             CALL cp_cfm_scale_and_add(CMPLX(1.0, 0.0, kind=dp), rtbse_env%ham_effective(j), &
     676        8096 :                                       CMPLX(rtbse_env%field(k), 0.0, kind=dp), rtbse_env%ham_workspace(1))
     677             :          END DO
     678        2024 :          IF (rtbse_env%ham_reference_type == rtp_bse_ham_g0w0) THEN
     679             :             ! Add the COH part - so far static but can be dynamic in principle throught the W updates
     680        2024 :             CALL get_sigma(rtbse_env, rtbse_env%sigma_COH(j), -0.5_dp, rtbse_env%S_inv_fm)
     681        2024 :             CALL cp_fm_to_cfm(msourcer=rtbse_env%sigma_COH(j), mtarget=rtbse_env%ham_workspace(1))
     682             :             CALL cp_cfm_scale_and_add(CMPLX(1.0, 0.0, kind=dp), rtbse_env%ham_effective(j), &
     683        2024 :                                       CMPLX(1.0, 0.0, kind=dp), rtbse_env%ham_workspace(1))
     684             :          END IF
     685             :          ! Calculate the (S)EX part - based on provided rho
     686             :          ! iGW = - rho W
     687        2024 :          CALL get_sigma(rtbse_env, rtbse_env%sigma_SEX(j), -1.0_dp, rho(j))
     688             :          CALL cp_cfm_scale_and_add(CMPLX(1.0, 0.0, kind=dp), rtbse_env%ham_effective(j), &
     689        2024 :                                    CMPLX(1.0, 0.0, kind=dp), rtbse_env%sigma_SEX(j))
     690             :          ! Calculate Hartree potential
     691             :          ! Hartree potential is scaled by number of electrons in each MO - spin degeneracy
     692             :          CALL get_hartree(rtbse_env, rho(j), &
     693        2024 :                           rtbse_env%hartree_curr(j))
     694        2024 :          CALL cp_fm_to_cfm(msourcer=rtbse_env%hartree_curr(j), mtarget=rtbse_env%ham_workspace(1))
     695             :          CALL cp_cfm_scale_and_add(CMPLX(1.0, 0.0, kind=dp), rtbse_env%ham_effective(j), &
     696        2024 :                                    CMPLX(rtbse_env%spin_degeneracy, 0.0, kind=dp), rtbse_env%ham_workspace(1))
     697             :          ! Enforce hermiticity of the effective Hamiltonian
     698             :          ! Important components without forced Hermiticity - moments matrix, sigma matrices, Hartree matrix
     699             :          ! single particle Ham
     700        2024 :          CALL cp_cfm_transpose(rtbse_env%ham_effective(j), 'C', rtbse_env%ham_workspace(1))
     701             :          CALL cp_cfm_scale_and_add(CMPLX(0.5, 0.0, kind=dp), rtbse_env%ham_effective(j), &
     702        4048 :                                    CMPLX(0.5, 0.0, kind=dp), rtbse_env%ham_workspace(1))
     703             :       END DO
     704        2024 :       CALL timestop(handle)
     705        2024 :    END SUBROUTINE update_effective_ham
     706             : ! **************************************************************************************************
     707             : !> \brief Self-consistently (ETRS) propagates the density to the next timestep
     708             : !> \note Uses rtbse_env%rho_new_last, assumes correct timestep information is given in rtbse_env
     709             : !> \param rho_start Initial density matrix
     710             : !> \param rho_mid Midpoint density (propagated to by the initial Hamiltonian)
     711             : !> \param rho_end Endpoint density (propagated to by endpoint Hamiltonian)
     712             : !> \param converged Whether the resulting rho_end is self-consistent
     713             : !> \param k How many SC iterations were done
     714             : !> \param metric The difference metric from the last self-consistent iteration (for printing/evaluation)
     715             : ! **************************************************************************************************
     716         608 :    SUBROUTINE etrs_scf_loop(rtbse_env, rho_start, rho_mid, rho_end, converged, k, metric)
     717             :       TYPE(rtbse_env_type), POINTER                     :: rtbse_env
     718             :       TYPE(cp_cfm_type), DIMENSION(:), POINTER          :: rho_start, &
     719             :                                                            rho_mid, &
     720             :                                                            rho_end
     721             :       LOGICAL                                           :: converged
     722             :       INTEGER                                           :: k
     723             :       REAL(kind=dp)                                     :: metric
     724             :       CHARACTER(len=*), PARAMETER                       :: routineN = "etrs_scf_loop"
     725             :       INTEGER                                           :: j, handle
     726             : 
     727         608 :       CALL timeset(routineN, handle)
     728             : 
     729             :       ! This method determines the density matrix at time (t+dt) by guessing the effective Hamiltonian at (t + dt)
     730             :       ! and using the Hamiltonian at time (t), it propagates density from time (t) while ensuring that the density
     731             :       ! at (t + dt/2) is the same for both forward and backwards propagation. Then, density at (t + dt) is
     732             :       ! used to calculate the new Hamiltonian at (t+dt), which is then used to get the new propagator, and so on
     733             :       ! until the density matrix does not change within certain limit
     734             :       ! Pseudocode of the algorithm
     735             :       !      rho_M = exp(-i S^(-1) H[rho(t)] dt/2) rho(t) exp(i H[rho(t)] S^(-1) dt/2)
     736             :       !      rho(t+dt, 0) = rho_M
     737             :       !      for j in 1,max_self_iter
     738             :       !              rho(t+dt,j) = exp(- i S^(-1) H[rho(t+dt,j-1)] dt/2) rho_M exp(i H [rho(t+dt,j-1)] S^(-1) dt/2)
     739             :       !              if ||rho(t+dt,j) - rho(t+dt,j-1)|| < epsilon
     740             :       !                      break
     741             : 
     742             :       ! Initial setup - calculate the Hamiltonian
     743         608 :       CALL update_effective_ham(rtbse_env, rho_start)
     744             :       ! Create the exponential
     745         608 :       CALL ham_to_exp(rtbse_env, rtbse_env%ham_effective, rtbse_env%ham_workspace)
     746             :       ! Propagate to rho_mid
     747         608 :       CALL propagate_density(rtbse_env, rtbse_env%ham_workspace, rho_start, rho_mid)
     748             :       ! Propagate to initial guess
     749         608 :       CALL propagate_density(rtbse_env, rtbse_env%ham_workspace, rho_mid, rtbse_env%rho_new_last)
     750             :       ! Update bookkeeping to the next timestep - Hamiltonians are now evaluated at the next timestep
     751         608 :       rtbse_env%sim_step = rtbse_env%sim_step + 1
     752         608 :       rtbse_env%sim_time = rtbse_env%sim_time + rtbse_env%sim_dt
     753         608 :       converged = .FALSE.
     754         608 :       CALL print_etrs_info_header(rtbse_env)
     755        1416 :       DO k = 1, rtbse_env%etrs_max_iter
     756             :          ! Get the Hamiltonian following from the last timestep
     757        1416 :          CALL update_effective_ham(rtbse_env, rtbse_env%rho_new_last)
     758        1416 :          CALL ham_to_exp(rtbse_env, rtbse_env%ham_effective, rtbse_env%ham_workspace)
     759             :          ! Propagate to new guess
     760        1416 :          CALL propagate_density(rtbse_env, rtbse_env%ham_workspace, rho_mid, rho_end)
     761             :          ! Check for self-consistency
     762        1416 :          metric = rho_metric(rho_end, rtbse_env%rho_new_last, rtbse_env%n_spin)
     763             :          ! ETRS info - only for log level > medium
     764        1416 :          CALL print_etrs_info(rtbse_env, k, metric)
     765        1416 :          IF (metric < rtbse_env%etrs_threshold) THEN
     766         608 :             converged = .TRUE.
     767         608 :             EXIT
     768             :          ELSE
     769             :             ! Copy rho_new to rho_new_last
     770        1616 :             DO j = 1, rtbse_env%n_spin
     771             :                ! Leaving for free convergence
     772        1616 :                CALL cp_cfm_to_cfm(rho_end(j), rtbse_env%rho_new_last(j))
     773             :             END DO
     774             :          END IF
     775             :       END DO
     776             :       ! Error handling in the case where the propagation did not converge is left to the main routine
     777         608 :       CALL timestop(handle)
     778         608 :    END SUBROUTINE etrs_scf_loop
     779             : 
     780             : ! **************************************************************************************************
     781             : !> \brief Does the BCH iterative determination of the exponential
     782             : !> \param propagator_matrix Matrix X which is to be exponentiated
     783             : !> \param target_matrix Matrix Y which the exponential acts upon
     784             : !> \param result_matrix Propagated matrix
     785             : !> \param workspace Matrices dedicated for work, 4 fm matrices with dimensions of X required
     786             : !> \param threshold_opt Optionally, a threshold under which the iteration is considered converged (default 1e-10)
     787             : !> \param max_iter_opt Optionally, maximum number of BCH iterations (default 20)
     788             : ! **************************************************************************************************
     789        5272 :    SUBROUTINE bch_propagate(propagator_matrix, target_matrix, result_matrix, workspace, threshold_opt, max_iter_opt)
     790             :       ! Array of complex propagator matrix X, such that
     791             :       ! the propagated matrix will follow Y' = e^X Y e^(-X), for each spin
     792             :       ! effect of e^(-X) is calculated - provide the X on the left hand side
     793             :       TYPE(cp_cfm_type), DIMENSION(:), POINTER          :: propagator_matrix
     794             :       ! Matrix Y to be propagated into matrix Y'
     795             :       TYPE(cp_cfm_type), DIMENSION(:), POINTER          :: target_matrix
     796             :       ! Matrix Y' is stored here on exit
     797             :       TYPE(cp_cfm_type), DIMENSION(:), POINTER          :: result_matrix, workspace
     798             :       ! Threshold for the metric which decides when to truncate the BCH expansion
     799             :       REAL(kind=dp), OPTIONAL                           :: threshold_opt
     800             :       INTEGER, OPTIONAL                                 :: max_iter_opt
     801             :       CHARACTER(len=*), PARAMETER                       :: routineN = "bch_propagate"
     802             :       REAL(kind=dp)                                     :: threshold, prefactor, metric
     803             :       INTEGER                                           :: max_iter, i, n_spin, n_ao, k, &
     804             :                                                            w_stride, handle
     805             :       LOGICAL                                           :: converged
     806             :       CHARACTER(len=77)                                 :: error
     807             : 
     808        2636 :       CALL timeset(routineN, handle)
     809             : 
     810        2636 :       converged = .FALSE.
     811             : 
     812        2636 :       IF (PRESENT(threshold_opt)) THEN
     813        2636 :          threshold = threshold_opt
     814             :       ELSE
     815           0 :          threshold = 1.0e-10
     816             :       END IF
     817             : 
     818        2636 :       IF (PRESENT(max_iter_opt)) THEN
     819        2636 :          max_iter = max_iter_opt
     820             :       ELSE
     821             :          max_iter = 20
     822             :       END IF
     823             : 
     824        2636 :       n_spin = SIZE(target_matrix)
     825             :       n_ao = 0
     826        2636 :       CALL cp_cfm_get_info(target_matrix(1), nrow_global=n_ao)
     827        2636 :       w_stride = n_spin
     828             : 
     829             :       ! Initiate
     830        5272 :       DO i = 1, n_spin
     831        2636 :          CALL cp_cfm_to_cfm(target_matrix(i), result_matrix(i))
     832        5272 :          CALL cp_cfm_to_cfm(target_matrix(i), workspace(i))
     833             :       END DO
     834             : 
     835             :       ! Start the BCH iterations
     836             :       ! So far, no spin mixing terms
     837       16226 :       DO k = 1, max_iter
     838       16226 :          prefactor = 1.0_dp/REAL(k, kind=dp)
     839       32452 :          DO i = 1, n_spin
     840             :             CALL parallel_gemm("N", "N", n_ao, n_ao, n_ao, &
     841             :                                CMPLX(prefactor, 0.0, kind=dp), propagator_matrix(i), workspace(i), &
     842       16226 :                                CMPLX(0.0, 0.0, kind=dp), workspace(i + w_stride))
     843             :             CALL parallel_gemm("N", "C", n_ao, n_ao, n_ao, &
     844             :                                CMPLX(prefactor, 0.0, kind=dp), workspace(i), propagator_matrix(i), &
     845       16226 :                                CMPLX(1.0, 0.0, kind=dp), workspace(i + w_stride))
     846             :             ! Add to the result
     847             :             CALL cp_cfm_scale_and_add(CMPLX(1.0, 0.0, kind=dp), result_matrix(i), &
     848       32452 :                                       CMPLX(1.0, 0.0, kind=dp), workspace(i + w_stride))
     849             :          END DO
     850       16226 :          metric = rho_metric(workspace(w_stride + 1:), workspace(1:w_stride), n_spin)
     851       16226 :          IF (metric <= threshold) THEN
     852             :             converged = .TRUE.
     853             :             EXIT
     854             :          ELSE
     855       27180 :             DO i = 1, n_spin
     856       27180 :                CALL cp_cfm_to_cfm(workspace(i + w_stride), workspace(i))
     857             :             END DO
     858             :          END IF
     859             :       END DO
     860        2636 :       IF (.NOT. converged) THEN
     861           0 :          WRITE (error, '(A35,E13.4E3,A16,E13.4E3)') "BCH did not converge, BCH Metric : ", &
     862           0 :             metric, "BCH Threshold : ", threshold
     863           0 :          CPABORT(error)
     864             :       END IF
     865             : 
     866        2636 :       CALL timestop(handle)
     867        2636 :    END SUBROUTINE bch_propagate
     868             : 
     869             : ! **************************************************************************************************
     870             : !> \brief Updates the density in rtbse_env, using the provided exponential
     871             : !>        The new density is saved to a different matrix, which enables for comparison of matrices
     872             : !> \param rtbse_env Entry point of the calculation - contains current state of variables
     873             : !> \param exponential Real and imaginary parts ( + spin) of the exponential propagator
     874             : ! **************************************************************************************************
     875        2636 :    SUBROUTINE propagate_density(rtbse_env, exponential, rho_old, rho_new)
     876             :       TYPE(rtbse_env_type)                               :: rtbse_env
     877             :       TYPE(cp_cfm_type), DIMENSION(:), POINTER           :: exponential, &
     878             :                                                             rho_old, &
     879             :                                                             rho_new
     880             :       CHARACTER(len=*), PARAMETER                        :: routineN = "propagate_density"
     881             :       INTEGER                                            :: j, handle
     882             : 
     883        2636 :       CALL timeset(routineN, handle)
     884        2636 :       IF (rtbse_env%mat_exp_method == do_exact) THEN
     885             :          ! For these methods, exponential is explicitly constructed
     886           0 :          DO j = 1, rtbse_env%n_spin
     887             :             ! rho * (exp^dagger)
     888             :             CALL parallel_gemm("N", "C", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     889             :                                CMPLX(1.0, 0.0, kind=dp), rho_old(j), exponential(j), &
     890           0 :                                CMPLX(0.0, 0.0, kind=dp), rtbse_env%rho_workspace(1))
     891             :             ! exp * rho * (exp^dagger)
     892             :             CALL parallel_gemm("N", "N", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     893             :                                CMPLX(1.0, 0.0, kind=dp), exponential(j), rtbse_env%rho_workspace(1), &
     894           0 :                                CMPLX(0.0, 0.0, kind=dp), rho_new(j))
     895             :          END DO
     896        2636 :       ELSE IF (rtbse_env%mat_exp_method == do_bch) THEN
     897             :          ! Same number of iterations as ETRS
     898             :          CALL bch_propagate(exponential, rho_old, rho_new, rtbse_env%rho_workspace, threshold_opt=rtbse_env%exp_accuracy, &
     899        2636 :                             max_iter_opt=rtbse_env%etrs_max_iter)
     900             :       ELSE
     901           0 :          CPABORT("Only BCH and exact matrix exponentiation implemented.")
     902             :       END IF
     903             : 
     904        2636 :       CALL timestop(handle)
     905        2636 :    END SUBROUTINE
     906             : 
     907             : ! **************************************************************************************************
     908             : !> \brief Outputs the number of electrons in the system from the density matrix
     909             : !> \note  Moments matrix is provided by the rtbse_env, uses rho_workspace(1:3)
     910             : !> \param rtbse_env Entry point - rtbse environment
     911             : !> \param rho Density matrix in AO basis
     912             : !> \param electron_n_re Real number of electrons
     913             : !> \param electron_n_im Imaginary number of electrons, which can arise from numerical non-hermiticity
     914             : ! **************************************************************************************************
     915         608 :    SUBROUTINE get_electron_number(rtbse_env, rho, electron_n_re, electron_n_im)
     916             :       TYPE(rtbse_env_type)                               :: rtbse_env
     917             :       TYPE(cp_cfm_type), DIMENSION(:), POINTER           :: rho
     918             :       REAL(kind=dp), INTENT(OUT)                         :: electron_n_re, electron_n_im
     919             :       COMPLEX(kind=dp)                                   :: electron_n_buffer
     920             :       INTEGER                                            :: j
     921             : 
     922         608 :       electron_n_re = 0.0_dp
     923         608 :       electron_n_im = 0.0_dp
     924         608 :       CALL cp_fm_to_cfm(msourcer=rtbse_env%S_fm, mtarget=rtbse_env%rho_workspace(1))
     925        1216 :       DO j = 1, rtbse_env%n_spin
     926         608 :          CALL cp_cfm_trace(rtbse_env%rho_workspace(1), rho(j), electron_n_buffer)
     927         608 :          electron_n_re = electron_n_re + REAL(electron_n_buffer, kind=dp)
     928        1216 :          electron_n_im = electron_n_im + REAL(AIMAG(electron_n_buffer), kind=dp)
     929             :       END DO
     930             :       ! Scale by spin degeneracy
     931         608 :       electron_n_re = electron_n_re*rtbse_env%spin_degeneracy
     932         608 :       electron_n_im = electron_n_im*rtbse_env%spin_degeneracy
     933         608 :    END SUBROUTINE get_electron_number
     934             : ! **************************************************************************************************
     935             : !> \brief Outputs the deviation from idempotence of density matrix
     936             : !> \note  Moments matrix is provided by the rtbse_env, uses rho_workspace(1:3)
     937             : !> \param rtbse_env Entry point - rtbse environment
     938             : !> \param rho Density matrix in AO basis
     939             : !> \param electron_n_re Real number of electrons
     940             : !> \param electron_n_im Imaginary number of electrons, which can arise from numerical non-hermiticity
     941             : ! **************************************************************************************************
     942           0 :    SUBROUTINE get_idempotence_deviation(rtbse_env, rho, deviation_metric)
     943             :       TYPE(rtbse_env_type)                              :: rtbse_env
     944             :       TYPE(cp_cfm_type), DIMENSION(:), POINTER          :: rho
     945             :       REAL(kind=dp), INTENT(OUT)                        :: deviation_metric
     946             :       COMPLEX(kind=dp)                                  :: buffer_1, buffer_2
     947             :       REAL(kind=dp)                                     :: buffer_dev
     948             :       INTEGER                                           :: j
     949             : 
     950           0 :       deviation_metric = 0.0_dp
     951           0 :       buffer_dev = 0.0_dp
     952             :       ! First, determine Tr(S * rho_re) + i Tr (S * rho_im)
     953           0 :       CALL cp_fm_to_cfm(msourcer=rtbse_env%S_fm, mtarget=rtbse_env%rho_workspace(1))
     954           0 :       DO j = 1, rtbse_env%n_spin
     955           0 :          CALL cp_cfm_trace(rtbse_env%rho_workspace(1), rho(j), buffer_1)
     956           0 :          buffer_dev = buffer_dev + REAL(ABS(buffer_1)*ABS(buffer_1), kind=dp)
     957             :       END DO
     958             :       ! Now, determine Tr(S * rho_re * S * rho_re) - Tr(S * rho_im * S * rho_im) + 2i Tr(S * rho_re * S * rho_im)
     959           0 :       DO j = 1, rtbse_env%n_spin
     960             :          ! S * rho
     961             :          CALL multiply_fm_cfm("N", "N", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     962             :                               1.0_dp, rtbse_env%S_fm, rho(j), &
     963           0 :                               0.0_dp, rtbse_env%rho_workspace(2))
     964             :          ! rho * S * rho
     965             :          CALL parallel_gemm("N", "N", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     966             :                             CMPLX(1.0, 0.0, kind=dp), rho(j), rtbse_env%rho_workspace(2), &
     967           0 :                             CMPLX(0.0, 0.0, kind=dp), rtbse_env%rho_workspace(3))
     968             :          ! Tr (S * rho * S * rho)
     969           0 :          CALL cp_cfm_trace(rtbse_env%rho_workspace(1), rtbse_env%rho_workspace(3), buffer_2)
     970           0 :          deviation_metric = deviation_metric + REAL(ABS(buffer_2)*ABS(buffer_2), kind=dp)
     971             :       END DO
     972           0 :       deviation_metric = SQRT(deviation_metric) - SQRT(buffer_dev)
     973           0 :    END SUBROUTINE get_idempotence_deviation
     974             : 
     975             : ! **************************************************************************************************
     976             : !> \brief Calculates the self-energy by contraction of screened potential, for complex density
     977             : !> \note Can be used for both the Coulomb hole part and screened exchange part
     978             : !> \param rtbse_env Quickstep environment data, entry point of the calculation
     979             : !> \param sigma_cfm Pointer to the self-energy full matrix, which is overwritten by this routine
     980             : !> \param greens_cfm Pointer to the Green's function matrix, which is used as input data
     981             : !> \author Stepan Marek
     982             : !> \date 09.2024
     983             : ! **************************************************************************************************
     984        2036 :    SUBROUTINE get_sigma_complex(rtbse_env, sigma_cfm, prefactor_opt, greens_cfm)
     985             :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     986             :       TYPE(cp_cfm_type)                                  :: sigma_cfm ! resulting self energy
     987             :       REAL(kind=dp), INTENT(IN), OPTIONAL                :: prefactor_opt
     988             :       TYPE(cp_cfm_type), INTENT(IN)                      :: greens_cfm ! matrix to contract with RI_W
     989             :       REAL(kind=dp)                                      :: prefactor
     990             : 
     991        2036 :       prefactor = 1.0_dp
     992        2036 :       IF (PRESENT(prefactor_opt)) prefactor = prefactor_opt
     993             : 
     994             :       ! Carry out the sigma part twice
     995             :       ! Real part
     996        2036 :       CALL cp_cfm_to_fm(msource=greens_cfm, mtargetr=rtbse_env%real_workspace(1))
     997        2036 :       CALL get_sigma(rtbse_env, rtbse_env%real_workspace(2), prefactor, rtbse_env%real_workspace(1))
     998        2036 :       CALL cp_fm_to_cfm(msourcer=rtbse_env%real_workspace(2), mtarget=rtbse_env%ham_workspace(1))
     999             :       ! Imaginary part
    1000        2036 :       CALL cp_cfm_to_fm(msource=greens_cfm, mtargeti=rtbse_env%real_workspace(1))
    1001        2036 :       CALL get_sigma(rtbse_env, rtbse_env%real_workspace(2), prefactor, rtbse_env%real_workspace(1))
    1002        2036 :       CALL cp_fm_to_cfm(msourcei=rtbse_env%real_workspace(2), mtarget=sigma_cfm)
    1003             :       ! Add the real part
    1004             :       CALL cp_cfm_scale_and_add(CMPLX(1.0, 0.0, kind=dp), sigma_cfm, &
    1005        2036 :                                 CMPLX(1.0, 0.0, kind=dp), rtbse_env%ham_workspace(1))
    1006             : 
    1007        2036 :    END SUBROUTINE get_sigma_complex
    1008             : ! **************************************************************************************************
    1009             : !> \brief Calculates the self-energy by contraction of screened potential, for complex density
    1010             : !> \note Can be used for both the Coulomb hole part and screened exchange part
    1011             : !> \param rtbse_env Quickstep environment data, entry point of the calculation
    1012             : !> \param sigma_fm Pointer to the self-energy full matrix, which is overwritten by this routine
    1013             : !> \param greens_fm Pointer to the Green's function matrix, which is used as input data
    1014             : !> \author Stepan Marek
    1015             : !> \date 09.2024
    1016             : ! **************************************************************************************************
    1017        6108 :    SUBROUTINE get_sigma_real(rtbse_env, sigma_fm, prefactor_opt, greens_fm)
    1018             :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
    1019             :       TYPE(cp_fm_type)                                   :: sigma_fm ! resulting self energy
    1020             :       REAL(kind=dp), INTENT(IN), OPTIONAL                :: prefactor_opt
    1021             :       TYPE(cp_fm_type), INTENT(IN)                       :: greens_fm ! matrix to contract with RI_W
    1022             :       REAL(kind=dp)                                      :: prefactor
    1023             : 
    1024        6108 :       prefactor = 1.0_dp
    1025        6108 :       IF (PRESENT(prefactor_opt)) prefactor = prefactor_opt
    1026             : 
    1027             :       ! Carry out the sigma part twice
    1028             :       ! Convert to dbcsr
    1029        6108 :       CALL copy_fm_to_dbcsr(greens_fm, rtbse_env%rho_dbcsr)
    1030        6108 :       CALL get_sigma_dbcsr(rtbse_env, sigma_fm, prefactor, rtbse_env%rho_dbcsr)
    1031             : 
    1032        6108 :    END SUBROUTINE get_sigma_real
    1033             : ! **************************************************************************************************
    1034             : !> \brief Calculates the self-energy by contraction of screened potential
    1035             : !> \note Can be used for both the Coulomb hole part and screened exchange part
    1036             : !> \param greens_fm Pointer to the Green's function matrix, which is used as input data
    1037             : !> \param sigma_fm Pointer to the self-energy full matrix, which is overwritten by this routine
    1038             : !> \author Stepan Marek
    1039             : !> \date 01.2024
    1040             : ! **************************************************************************************************
    1041        6108 :    SUBROUTINE get_sigma_dbcsr(rtbse_env, sigma_fm, prefactor_opt, greens_dbcsr)
    1042             :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
    1043             :       TYPE(cp_fm_type)                                   :: sigma_fm ! resulting self energy
    1044             :       REAL(kind=dp), INTENT(IN), OPTIONAL                :: prefactor_opt
    1045             :       TYPE(dbcsr_type)                                   :: greens_dbcsr
    1046             :       REAL(kind=dp)                                      :: prefactor
    1047             : 
    1048        6108 :       prefactor = 1.0_dp
    1049        6108 :       IF (PRESENT(prefactor_opt)) prefactor = prefactor_opt
    1050             : 
    1051             :       CALL get_sigma_noenv(sigma_fm, prefactor_opt, greens_dbcsr, &
    1052             :                            rtbse_env%screened_dbt, rtbse_env%t_3c_w, &
    1053             :                            rtbse_env%t_3c_work_RI_AO__AO, rtbse_env%t_3c_work2_RI_AO__AO, &
    1054        6108 :                            rtbse_env%greens_dbt)
    1055        6108 :    END SUBROUTINE get_sigma_dbcsr
    1056             : ! **************************************************************************************************
    1057             : !> \brief Calculates the self-energy by contraction of screened potential
    1058             : !> \note Can be used for both the Coulomb hole part and screened exchange part
    1059             : !> \note Separated from the rtbse_env - can be in principle called outside of the RTBSE code
    1060             : !> \param sigma_fm Pointer to the self-energy full matrix, which is overwritten by this routine
    1061             : !> \param prefactor_opt Optional argument for the prefactor (used for Coulomb hole calculation)
    1062             : !> \param greens_dbcsr Matrix storing the lesser Green's function elements
    1063             : !> \param screened_dbt Tensor storing the W_PQ screened Coulomb interaction RI matrix elements
    1064             : !> \param int_3c_dbt Tensor storing the 3c integrals (RI| ORB ORB )
    1065             : !> \param work_dbt_3c_1 Tensor workspace optimised for RI_AO__AO contractions
    1066             : !> \param work_dbt_3c_2 Tensor workspace optimised for RI_AO__AO contractions
    1067             : !> \param work_dbt_2c Tensor workspace for 2c integrals (Green's function and self-energy)
    1068             : !> \author Stepan Marek
    1069             : !> \date 01.2025
    1070             : ! **************************************************************************************************
    1071        6108 :    SUBROUTINE get_sigma_noenv(sigma_fm, prefactor_opt, greens_dbcsr, screened_dbt, &
    1072             :                               int_3c_dbt, work_dbt_3c_1, work_dbt_3c_2, work_dbt_2c)
    1073             :       TYPE(cp_fm_type)                                   :: sigma_fm ! resulting self energy
    1074             :       REAL(kind=dp), INTENT(IN), OPTIONAL                :: prefactor_opt
    1075             :       TYPE(dbcsr_type)                                   :: greens_dbcsr
    1076             :       TYPE(dbt_type)                                     :: screened_dbt, &
    1077             :                                                             int_3c_dbt, &
    1078             :                                                             work_dbt_3c_1, &
    1079             :                                                             work_dbt_3c_2, &
    1080             :                                                             work_dbt_2c
    1081             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'get_sigma'
    1082             :       REAL(kind=dp)                                      :: prefactor
    1083             :       TYPE(dbcsr_type)                                   :: sigma_dbcsr
    1084             :       INTEGER                                            :: handle
    1085             : 
    1086        6108 :       CALL timeset(routineN, handle)
    1087             : 
    1088        6108 :       IF (PRESENT(prefactor_opt)) THEN
    1089        6108 :          prefactor = prefactor_opt
    1090             :       ELSE
    1091           0 :          prefactor = 1.0_dp
    1092             :       END IF
    1093             : 
    1094             :       ! Three-centre integrals are obtained from build_3c_integrals, from qs_tensors
    1095             :       ! These should use sparcity, while W and Sigma can be full matrices
    1096             :       ! The summation is carried out by dbt library - dbt_contract in dbt_api
    1097             :       ! The building of the tensors might be a bit hard, because it requires a lot of parallel information
    1098             :       ! Probably just use the tensors already present in bs_env? They seem to be mostly work tensors
    1099             :       ! Create by template
    1100             :       CALL dbt_contract(alpha=1.0_dp, &
    1101             :                         tensor_1=screened_dbt, &
    1102             :                         tensor_2=int_3c_dbt, &
    1103             :                         beta=0.0_dp, &
    1104             :                         tensor_3=work_dbt_3c_1, &
    1105             :                         contract_1=[2], notcontract_1=[1], map_1=[1], &
    1106        6108 :                         contract_2=[1], notcontract_2=[2, 3], map_2=[2, 3])!,&
    1107             :       !filter_eps=bs_env%eps_filter)
    1108             :       ! t_work1 now contains B^P_(nu beta) = sum _ Q W _ (PQ) (iomega = 0) (Q| nu beta)
    1109             :       ! Next step is to convert the greens full matrix to dbcsr matrix
    1110        6108 :       CALL dbt_copy_matrix_to_tensor(greens_dbcsr, work_dbt_2c)
    1111             :       ! Then contract it
    1112             :       ! no scaling applied - this has to be applied externally
    1113             :       CALL dbt_contract(alpha=1.0_dp, &
    1114             :                         tensor_1=work_dbt_3c_1, &
    1115             :                         tensor_2=work_dbt_2c, &
    1116             :                         beta=0.0_dp, &
    1117             :                         tensor_3=work_dbt_3c_2, &
    1118             :                         contract_1=[2], notcontract_1=[1, 3], map_1=[1, 3], &
    1119        6108 :                         contract_2=[2], notcontract_2=[1], map_2=[2])
    1120             :       ! workspace 2 now contains C ^ P _ (mu beta) sum _ nu B ^ P _ (nu beta) g _ (mu nu)
    1121             :       CALL dbt_contract(alpha=prefactor, &
    1122             :                         tensor_1=int_3c_dbt, &
    1123             :                         tensor_2=work_dbt_3c_2, &
    1124             :                         beta=0.0_dp, &
    1125             :                         tensor_3=work_dbt_2c, &
    1126             :                         contract_1=[1, 3], notcontract_1=[2], map_1=[1], &
    1127        6108 :                         contract_2=[1, 2], notcontract_2=[3], map_2=[2])!,&
    1128             :       !filter_eps=bs_env%eps_filter)
    1129             :       ! Finally, convert the COH tensor to matrix and then to fm matrix
    1130             :       ! TODO : extra workspace?
    1131        6108 :       CALL dbcsr_create(sigma_dbcsr, name="sigma", template=greens_dbcsr)
    1132        6108 :       CALL dbt_copy_tensor_to_matrix(work_dbt_2c, sigma_dbcsr)
    1133        6108 :       CALL copy_dbcsr_to_fm(sigma_dbcsr, sigma_fm)
    1134        6108 :       CALL dbcsr_release(sigma_dbcsr)
    1135             :       ! Clear workspaces - saves memory?
    1136        6108 :       CALL dbt_clear(work_dbt_3c_1)
    1137        6108 :       CALL dbt_clear(work_dbt_3c_2)
    1138        6108 :       CALL dbt_clear(work_dbt_2c)
    1139        6108 :       CALL timestop(handle)
    1140             : 
    1141        6108 :    END SUBROUTINE get_sigma_noenv
    1142             : ! **************************************************************************************************
    1143             : !> \brief Creates the RI matrix and populates it with correct values
    1144             : !> \note Tensor contains Hartree elements in the auxiliary basis
    1145             : !> \param qs_env Quickstep environment - entry point of calculation
    1146             : !> \author Stepan Marek
    1147             : !> \date 01.2024
    1148             : ! **************************************************************************************************
    1149          12 :    SUBROUTINE init_hartree(rtbse_env, v_dbcsr)
    1150             :       TYPE(rtbse_env_type), POINTER, INTENT(IN)          :: rtbse_env
    1151             :       TYPE(dbcsr_type)                                   :: v_dbcsr
    1152             :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1153             :       TYPE(libint_potential_type)                        :: coulomb_op
    1154             :       TYPE(cp_fm_type)                                   :: V_fm
    1155             :       TYPE(cp_fm_type)                                   :: metric_fm
    1156             :       TYPE(cp_fm_type)                                   :: metric_inv_fm, &
    1157             :                                                             work_fm
    1158             :       TYPE(dbcsr_type), DIMENSION(:), ALLOCATABLE        :: V_dbcsr_a, &
    1159          12 :                                                             metric_dbcsr
    1160             :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
    1161          12 :          POINTER                                         :: nl_2c
    1162             : 
    1163          12 :       bs_env => rtbse_env%bs_env
    1164             : 
    1165             :       ! Allocate for bare Hartree term
    1166          24 :       ALLOCATE (V_dbcsr_a(1))
    1167          24 :       ALLOCATE (metric_dbcsr(1))
    1168          12 :       CALL dbcsr_create(V_dbcsr_a(1), name="Hartree_dbcsr", template=bs_env%mat_RI_RI%matrix)
    1169          12 :       CALL dbcsr_create(metric_dbcsr(1), name="RI_metric_dbcsr", template=bs_env%mat_RI_RI%matrix)
    1170             : 
    1171             :       ! Calculate full coulomb RI basis elements - V _ (PQ) matrix
    1172          12 :       NULLIFY (nl_2c)
    1173             :       CALL build_2c_neighbor_lists(nl_2c, bs_env%basis_set_RI, bs_env%basis_set_RI, &
    1174             :                                    coulomb_op, "Coulomb_neighbor_2c_list", rtbse_env%qs_env, &
    1175          12 :                                    sym_ij=.FALSE., molecular=.TRUE.)
    1176             :       CALL build_2c_integrals(V_dbcsr_a, bs_env%eps_filter, rtbse_env%qs_env, nl_2c, &
    1177             :                               bs_env%basis_set_RI, bs_env%basis_set_RI, coulomb_op, &
    1178          12 :                               do_kpoints=.FALSE., regularization_RI=bs_env%regularization_RI)
    1179             :       ! Calculate the RI metric elements
    1180             :       ! nl_2c is automatically rewritten (even reallocated) in this routine
    1181             :       CALL build_2c_neighbor_lists(nl_2c, bs_env%basis_set_RI, bs_env%basis_set_RI, &
    1182             :                                    bs_env%ri_metric, "Metric_neighbor_2c_list", rtbse_env%qs_env, &
    1183          12 :                                    sym_ij=.FALSE., molecular=.TRUE.)
    1184             :       CALL build_2c_integrals(metric_dbcsr, bs_env%eps_filter, rtbse_env%qs_env, nl_2c, &
    1185             :                               bs_env%basis_set_RI, bs_env%basis_set_RI, bs_env%ri_metric, &
    1186          12 :                               do_kpoints=.FALSE., regularization_RI=bs_env%regularization_RI)
    1187             :       ! nl_2c no longer needed
    1188          12 :       CALL release_neighbor_list_sets(nl_2c)
    1189          12 :       CALL cp_fm_create(metric_fm, bs_env%fm_RI_RI%matrix_struct)
    1190          12 :       CALL cp_fm_set_all(metric_fm, 0.0_dp)
    1191          12 :       CALL cp_fm_create(metric_inv_fm, bs_env%fm_RI_RI%matrix_struct)
    1192          12 :       CALL cp_fm_set_all(metric_inv_fm, 0.0_dp)
    1193          12 :       CALL cp_fm_create(work_fm, bs_env%fm_RI_RI%matrix_struct)
    1194          12 :       CALL cp_fm_set_all(work_fm, 0.0_dp)
    1195          12 :       CALL copy_dbcsr_to_fm(metric_dbcsr(1), metric_fm)
    1196          12 :       CALL cp_fm_invert(metric_fm, metric_inv_fm)
    1197          12 :       CALL cp_fm_create(V_fm, bs_env%fm_RI_RI%matrix_struct)
    1198          12 :       CALL cp_fm_set_all(V_fm, 0.0_dp)
    1199             :       ! Multiply by the inverse from each side (M^-1 is symmetric)
    1200             :       CALL cp_dbcsr_sm_fm_multiply(V_dbcsr_a(1), metric_inv_fm, &
    1201          12 :                                    work_fm, bs_env%n_RI)
    1202             :       CALL parallel_gemm("N", "N", bs_env%n_RI, bs_env%n_RI, bs_env%n_RI, &
    1203          12 :                          1.0_dp, metric_inv_fm, work_fm, 0.0_dp, V_fm)
    1204             :       ! Now, create the tensor from the matrix
    1205             :       ! First, convert full matrix to dbcsr
    1206          12 :       CALL dbcsr_clear(V_dbcsr_a(1))
    1207          12 :       CALL copy_fm_to_dbcsr(V_fm, V_dbcsr_a(1))
    1208          12 :       CALL dbcsr_create(v_dbcsr, "Hartree ri", V_dbcsr_a(1))
    1209          12 :       CALL dbcsr_copy(v_dbcsr, V_dbcsr_a(1))
    1210             :       ! Create and copy distinctly, so that unnecessary objects can be destroyed
    1211             :       ! Destroy all unnecessary matrices
    1212          12 :       CALL dbcsr_release(V_dbcsr_a(1))
    1213          12 :       CALL dbcsr_release(metric_dbcsr(1))
    1214          12 :       DEALLOCATE (V_dbcsr_a)
    1215          12 :       DEALLOCATE (metric_dbcsr)
    1216          12 :       CALL cp_fm_release(V_fm)
    1217             :       ! CALL cp_fm_release(metric_fm(1,1))
    1218          12 :       CALL cp_fm_release(metric_fm)
    1219             :       ! DEALLOCATE(metric_fm)
    1220          12 :       CALL cp_fm_release(work_fm)
    1221          12 :       CALL cp_fm_release(metric_inv_fm)
    1222          72 :    END SUBROUTINE
    1223             : ! **************************************************************************************************
    1224             : !> \brief Calculates the Hartree matrix in the atomic orbital basis, given a density matrix, in local arrays
    1225             : !>        Calculates the values for single spin species present in given rho
    1226             : !> \param qs_env Entry point
    1227             : !> \param rtbse_env Entry point of GWBSE - uses rho_dbcsr and some complex_workspace
    1228             : !> \param rho_ao Density matrix in ao basis
    1229             : !> \param v_ao Overwritten by the Hartree matrix in the atomic orbital basis
    1230             : !> \author Stepan Marek
    1231             : !> \date 01.2025
    1232             : ! **************************************************************************************************
    1233        2036 :    SUBROUTINE get_hartree_env(rtbse_env, rho_fm, v_fm)
    1234             :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
    1235             :       TYPE(cp_cfm_type)                                  :: rho_fm
    1236             :       TYPE(cp_fm_type)                                   :: v_fm
    1237             :       TYPE(mp_para_env_type), POINTER                    :: para_env
    1238             :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1239             : 
    1240        2036 :       CALL get_qs_env(rtbse_env%qs_env, para_env=para_env, bs_env=bs_env)
    1241             : 
    1242             :       CALL get_hartree_noenv(v_fm, rho_fm, rtbse_env%int_3c_array, rtbse_env%v_dbcsr, &
    1243             :                              rtbse_env%n_RI, bs_env%sizes_RI, &
    1244        2036 :                              para_env, rtbse_env%rho_dbcsr, rtbse_env%v_ao_dbcsr)
    1245        2036 :    END SUBROUTINE get_hartree_env
    1246             : ! **************************************************************************************************
    1247             : !> \brief Calculates the Hartree matrix in the atomic orbital basis, given a density matrix, in local arrays
    1248             : !>        Calculates the values for single spin species present in given rho
    1249             : !> \param v_fm Hartree potential in atomic orbital basis - is overwritten by the updated potential
    1250             : !> \param rho_fm Density matrix corresponding to single spin species, in atomic orbital basis
    1251             : !> \param int_3c Previously allocated array (best to use create_hartree_ri_3c) for 3c integrals
    1252             : !> \param v_dbcsr Previously calculated 2c Coulomb repulsion between RI orbitals
    1253             : !> \param n_RI Number of RI basis orbitals
    1254             : !> \param sizes_RI Number of RI basis orbitals per atom
    1255             : !> \param para_env MPI Parallel environment (used for summation across ranks)
    1256             : !> \param rho_dbcsr Previously created dbcsr matrix, used as workspace
    1257             : !> \param v_ao_dbcsr Previously created dbcsr matrix, used as workspace
    1258             : !> \author Stepan Marek
    1259             : !> \date 01.2025
    1260             : ! **************************************************************************************************
    1261        2036 :    SUBROUTINE get_hartree_noenv(v_fm, rho_fm, int_3c, v_dbcsr, n_RI, sizes_RI, para_env, rho_dbcsr, v_ao_dbcsr)
    1262             :       TYPE(cp_fm_type)                                   :: v_fm
    1263             :       TYPE(cp_cfm_type), INTENT(IN)                      :: rho_fm
    1264             :       REAL(kind=dp), DIMENSION(:, :, :), POINTER         :: int_3c
    1265             :       TYPE(dbcsr_type)                                   :: v_dbcsr
    1266             :       INTEGER                                            :: n_RI
    1267             :       INTEGER, DIMENSION(:)                              :: sizes_RI
    1268             :       TYPE(mp_para_env_type), POINTER                    :: para_env
    1269             :       TYPE(dbcsr_type)                                   :: rho_dbcsr, v_ao_dbcsr
    1270             :       CHARACTER(len=*), PARAMETER                        :: routineN = "get_hartree"
    1271             :       TYPE(dbcsr_iterator_type)                          :: iterator_matrix
    1272             :       INTEGER                                            :: i, j, k, n, nblocks, ind_1, ind_2, row_offset, col_offset, &
    1273             :                                                             row_size, col_size, j_n_AO, k_n_AO, i_n_RI, &
    1274             :                                                             ri_offset, ind_i, handle
    1275        2036 :       REAL(kind=dp), DIMENSION(:), ALLOCATABLE           :: Pvector, Qvector
    1276        2036 :       REAL(kind=dp), DIMENSION(:, :), POINTER            :: block_matrix
    1277             :       INTEGER                                            :: nblkrows_local, nblkcols_local, j_blk, k_blk, j_offset, k_offset
    1278        2036 :       INTEGER, DIMENSION(:), POINTER                     :: local_blk_rows, local_blk_cols
    1279             :       LOGICAL                                            :: found
    1280             : 
    1281             :       MARK_USED(i_n_RI)
    1282             :       MARK_USED(ri_offset)
    1283             :       MARK_USED(ind_i)
    1284             : 
    1285             :       ! No memory optimisation so far - calculate all 3cs an all ranks
    1286             :       ! Importantly - dbcsr blocks are ordered by atoms - i.e. ethene with 6 atoms will have 6x6 block structure
    1287             :       ! Number of basis states on each basis set is known is post_scf_bandstructure env
    1288             : 
    1289        2036 :       CALL timeset(routineN, handle)
    1290             : 
    1291             :       ! Allocate the Q and Pvector on each rank
    1292       50900 :       ALLOCATE (Qvector(n_RI), source=0.0_dp)
    1293       50900 :       ALLOCATE (Pvector(n_RI), source=0.0_dp)
    1294             : 
    1295             :       ! First step - analyze the structure of copied dbcsr matrix on all ranks
    1296        2036 :       CALL dbcsr_clear(rho_dbcsr)
    1297             :       ! Only the real part of the density matrix contributes
    1298             :       ! Use v_fm as workspace
    1299        2036 :       CALL cp_cfm_to_fm(msource=rho_fm, mtargetr=v_fm)
    1300        2036 :       CALL copy_fm_to_dbcsr(v_fm, rho_dbcsr)
    1301        2036 :       j_offset = 0
    1302             :       CALL dbcsr_get_info(rho_dbcsr, nblkrows_local=nblkrows_local, nblkcols_local=nblkcols_local, &
    1303        2036 :                           local_rows=local_blk_rows, local_cols=local_blk_cols)
    1304        4072 :       DO j_blk = 1, nblkrows_local
    1305        2036 :          k_offset = 0
    1306        6108 :          DO k_blk = 1, nblkcols_local
    1307             :             ! Check whether we can retrieve the rho block
    1308             :             ! TODO : Handle transposed case?
    1309             :             CALL dbcsr_get_block_p(rho_dbcsr, local_blk_rows(j_blk), local_blk_cols(k_blk), &
    1310        4072 :                                    block=block_matrix, found=found, row_size=row_size, col_size=col_size)
    1311             :             ! If the block is not found, then the density matrix here has below threshold values
    1312        4072 :             IF (.NOT. found) CYCLE
    1313             :             ! With the block retrieved, add its contributions to the Q-vector
    1314             :             !$OMP PARALLEL DO DEFAULT(none) PRIVATE(i,j,k) &
    1315        4072 :             !$OMP SHARED(n_RI, row_size, col_size, Qvector, int_3c, j_offset, k_offset, block_matrix)
    1316             :             DO i = 1, n_RI
    1317             :                DO j = 1, row_size
    1318             :                   DO k = 1, col_size
    1319             :                      Qvector(i) = Qvector(i) + int_3c(j_offset + j, k_offset + k, i)*block_matrix(j, k)
    1320             :                   END DO
    1321             :                END DO
    1322             :             END DO
    1323             :             !$OMP END PARALLEL DO
    1324             :             ! Increment k-offset - setup for the next block
    1325       10180 :             k_offset = k_offset + col_size
    1326             :          END DO
    1327             :          ! Increments the j-offset - row_size is carried over from the last iteration
    1328        4072 :          j_offset = j_offset + row_size
    1329             :       END DO
    1330             :       ! Now, each rank has contributions from D_jk within its scope
    1331             :       ! Need to sum over different ranks to get the total vector on all ranks
    1332        2036 :       CALL para_env%sum(Qvector)
    1333             :       ! Once this is done, Pvector is current on all ranks
    1334             :       ! Continue with V_PQ summation
    1335        2036 :       nblocks = dbcsr_get_num_blocks(v_dbcsr)
    1336        2036 :       CALL dbcsr_iterator_start(iterator_matrix, v_dbcsr)
    1337        6108 :       DO n = 1, nblocks
    1338             :          ! TODO : Try OMP parallelisation over different blocks - expect many more available speedup for large systems
    1339             :          CALL dbcsr_iterator_next_block(iterator_matrix, ind_1, ind_2, block_matrix, &
    1340        4072 :                                         row_offset=row_offset, col_offset=col_offset, row_size=row_size, col_size=col_size)
    1341             :          ! TODO : Better names for RI
    1342        4072 :          j_n_AO = sizes_RI(ind_1)
    1343        4072 :          k_n_AO = sizes_RI(ind_2)
    1344             :          ! The allocations are as follows
    1345             :          !$OMP PARALLEL DO DEFAULT(none) PRIVATE(j,k) &
    1346        6108 :          !$OMP SHARED(block_matrix, Pvector, Qvector,j_n_AO,k_n_AO,row_offset,col_offset)
    1347             :          DO j = 1, j_n_AO
    1348             :             DO k = 1, k_n_AO
    1349             :                Pvector(j + row_offset - 1) = Pvector(j + row_offset - 1) + block_matrix(j, k)*Qvector(k + col_offset - 1)
    1350             :             END DO
    1351             :          END DO
    1352             :          !$OMP END PARALLEL DO
    1353             :       END DO
    1354        2036 :       CALL dbcsr_iterator_stop(iterator_matrix)
    1355             :       ! Again, make sure that the P vector is present on all ranks
    1356        2036 :       CALL para_env%sum(Pvector)
    1357             :       ! Now, for the final trick, iterate over local blocks of v_ao_dbcsr to get the Hartree as dbcsr, then convert to fm
    1358             :       ! TODO : Clear or set blocks to zero
    1359             :       ! CALL dbcsr_clear(v_ao_dbcsr)
    1360        2036 :       j_offset = 0
    1361        4072 :       DO j_blk = 1, nblkrows_local
    1362        2036 :          k_offset = 0
    1363        6108 :          DO k_blk = 1, nblkcols_local
    1364             :             ! Check whether we can retrieve the rho block
    1365             :             ! TODO : Handle transposed case?
    1366             :             CALL dbcsr_get_block_p(v_ao_dbcsr, local_blk_rows(j_blk), local_blk_cols(k_blk), &
    1367        4072 :                                    block=block_matrix, found=found, row_size=row_size, col_size=col_size)
    1368             :             ! If the block is not found, reserve it
    1369        4072 :             IF (.NOT. found) THEN
    1370             :                ! Reservations
    1371          24 :                CALL dbcsr_reserve_blocks(v_ao_dbcsr, local_blk_rows(j_blk:j_blk), local_blk_cols(k_blk:k_blk))
    1372             :                ! Rerun the getter to get the new block
    1373             :                CALL dbcsr_get_block_p(v_ao_dbcsr, local_blk_rows(j_blk), local_blk_cols(k_blk), &
    1374          24 :                                       block=block_matrix, found=found, row_size=row_size, col_size=col_size)
    1375             :             END IF
    1376             :             ! With the block retrieved, contract with the P vector
    1377             :             !$OMP PARALLEL DO DEFAULT(none) PRIVATE(i,j,k) &
    1378        4072 :             !$OMP SHARED(row_size, col_size, n_RI, block_matrix, Pvector, int_3c, j_offset, k_offset)
    1379             :             DO j = 1, row_size
    1380             :                DO k = 1, col_size
    1381             :                   block_matrix(j, k) = 0.0_dp
    1382             :                   DO i = 1, n_RI
    1383             :                      block_matrix(j, k) = block_matrix(j, k) + Pvector(i)*int_3c(j_offset + j, k_offset + k, i)
    1384             :                   END DO
    1385             :                END DO
    1386             :             END DO
    1387             :             !$OMP END PARALLEL DO
    1388             :             ! Increment k-offset - setup for the next block
    1389       10180 :             k_offset = k_offset + col_size
    1390             :          END DO
    1391             :          ! Increments the j-offset - row_size is carried over from the last iteration
    1392        4072 :          j_offset = j_offset + row_size
    1393             :       END DO
    1394             :       ! Since P vector was present on all the ranks, v_dbcsr_ao has the complete Hartree result
    1395             :       ! copy_dbcsr_to_fm should set all values in v_fm to zero
    1396        2036 :       CALL copy_dbcsr_to_fm(v_ao_dbcsr, v_fm)
    1397        2036 :       DEALLOCATE (Qvector)
    1398        2036 :       DEALLOCATE (Pvector)
    1399             : 
    1400        2036 :       CALL timestop(handle)
    1401        8144 :    END SUBROUTINE get_hartree_noenv
    1402             : ! **************************************************************************************************
    1403             : !> \brief Calculates the exponential of a matrix in a generalized eigenvalue problem. Specifically,
    1404             : !>        it assumes we have a Hermitian matrix A in the eigenvalue problem AX = BXE, where B is some overlap
    1405             : !>        matrix and E is a diagonal matrix of real eigenvalues. Then, it calculates
    1406             : !>        exp(B^(-1) A) = X exp(E) X^C B
    1407             : !> \param amatrix Matrix to exponentiate
    1408             : !> \param bmatrix Overlap matrix
    1409             : !> \param exponential Exponential exp(B^(-1) A) is stored here after the routine is finished
    1410             : !> \param eig_scale_opt Optionally scale eigenvalues by a complex number before exponentiating them
    1411             : !> \param work_opt Optionally provide workspace (of size at least 4) that is used in the calculation
    1412             : !> \author Stepan Marek
    1413             : !> \date 09.2024
    1414             : ! **************************************************************************************************
    1415           0 :    SUBROUTINE cp_cfm_gexp(amatrix, bmatrix, exponential, eig_scale_opt, work_opt)
    1416             :       ! TODO : Do interface for real matrices
    1417             :       TYPE(cp_cfm_type), INTENT(IN)                      :: amatrix
    1418             :       TYPE(cp_cfm_type), INTENT(IN)                      :: bmatrix
    1419             :       TYPE(cp_cfm_type)                                  :: exponential
    1420             :       COMPLEX(kind=dp), INTENT(IN), OPTIONAL             :: eig_scale_opt
    1421             :       TYPE(cp_cfm_type), DIMENSION(:), POINTER, OPTIONAL :: work_opt
    1422             :       CHARACTER(len=*), PARAMETER                        :: routineN = "cp_cfm_gexp"
    1423             :       COMPLEX(kind=dp)                                   :: eig_scale
    1424           0 :       REAL(kind=dp), DIMENSION(:), ALLOCATABLE           :: eigenvalues
    1425           0 :       COMPLEX(kind=dp), DIMENSION(:), ALLOCATABLE        :: expvalues
    1426           0 :       TYPE(cp_cfm_type), DIMENSION(:), POINTER           :: work
    1427             :       LOGICAL                                            :: deallocate_work
    1428             :       INTEGER                                            :: nrow, i, handle
    1429             : 
    1430           0 :       CALL timeset(routineN, handle)
    1431             : 
    1432             :       ! Argument parsing and sanity checks
    1433           0 :       IF (PRESENT(eig_scale_opt)) THEN
    1434           0 :          eig_scale = eig_scale_opt
    1435             :       ELSE
    1436             :          eig_scale = CMPLX(1.0, 0.0, kind=dp)
    1437             :       END IF
    1438             : 
    1439           0 :       NULLIFY (work)
    1440           0 :       IF (PRESENT(work_opt) .AND. SIZE(work_opt) >= 4) THEN
    1441           0 :          deallocate_work = .FALSE.
    1442           0 :          work => work_opt
    1443             :       ELSE
    1444           0 :          deallocate_work = .TRUE.
    1445           0 :          ALLOCATE (work(4))
    1446             :          ! Allocate the work storage on the fly
    1447           0 :          DO i = 1, 4
    1448           0 :             CALL cp_cfm_create(work(i), amatrix%matrix_struct)
    1449             :          END DO
    1450             :       END IF
    1451             : 
    1452           0 :       nrow = amatrix%matrix_struct%nrow_global
    1453             : 
    1454           0 :       ALLOCATE (eigenvalues(nrow))
    1455           0 :       ALLOCATE (expvalues(nrow))
    1456             : 
    1457             :       ! Do not change the amatrix and bmatrix - need to copy them first
    1458           0 :       CALL cp_cfm_to_cfm(amatrix, work(1))
    1459           0 :       CALL cp_cfm_to_cfm(bmatrix, work(2))
    1460             : 
    1461             :       ! Solve the generalized eigenvalue equation
    1462           0 :       CALL cp_cfm_geeig(work(1), work(2), work(3), eigenvalues, work(4))
    1463             : 
    1464             :       ! Scale and exponentiate the eigenvalues
    1465           0 :       expvalues(:) = EXP(eigenvalues(:)*eig_scale)
    1466             : 
    1467             :       ! Copy eigenvectors to column scale them
    1468           0 :       CALL cp_cfm_to_cfm(work(3), work(1))
    1469             :       ! X * exp(E)
    1470           0 :       CALL cp_cfm_column_scale(work(1), expvalues)
    1471             : 
    1472             :       ! Carry out the remaining operations
    1473             :       ! X * exp(E) * X^C
    1474             :       CALL parallel_gemm("N", "C", nrow, nrow, nrow, &
    1475             :                          CMPLX(1.0, 0.0, kind=dp), work(1), work(3), &
    1476           0 :                          CMPLX(0.0, 0.0, kind=dp), work(2))
    1477             :       ! X * exp(E) * X^C * B
    1478             :       CALL parallel_gemm("N", "N", nrow, nrow, nrow, &
    1479             :                          CMPLX(1.0, 0.0, kind=dp), work(2), bmatrix, &
    1480           0 :                          CMPLX(0.0, 0.0, kind=dp), exponential)
    1481             : 
    1482             :       ! Deallocate work storage if necessary
    1483           0 :       IF (deallocate_work) THEN
    1484           0 :          DO i = 1, 4
    1485           0 :             CALL cp_cfm_release(work(i))
    1486             :          END DO
    1487           0 :          DEALLOCATE (work)
    1488             :       END IF
    1489             : 
    1490           0 :       DEALLOCATE (eigenvalues)
    1491           0 :       DEALLOCATE (expvalues)
    1492             : 
    1493           0 :       CALL timestop(handle)
    1494           0 :    END SUBROUTINE cp_cfm_gexp
    1495             : END MODULE rt_bse

Generated by: LCOV version 1.15