LCOV - code coverage report
Current view: top level - src/emd - rt_bse.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:21ef868) Lines: 84.1 % 485 408
Test Date: 2026-08-14 07:04:57 Functions: 88.0 % 25 22

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

Generated by: LCOV version 2.0-1