LCOV - code coverage report
Current view: top level - src/emd - rt_bse_types.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:24d69ee) Lines: 95.8 % 498 477
Test Date: 2026-09-03 07:32:15 Functions: 86.7 % 15 13

            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 Data storage and other types for propagation via RT-BSE method.
      10              : !> \author Stepan Marek (01.24)
      11              : ! **************************************************************************************************
      12              : 
      13              : MODULE rt_bse_types
      14              : 
      15              :    USE kinds, ONLY: dp
      16              :    USE cp_fm_types, ONLY: cp_fm_type, &
      17              :                           cp_fm_release, &
      18              :                           cp_fm_create, &
      19              :                           cp_fm_set_all
      20              :    USE cp_fm_struct, ONLY: cp_fm_struct_type, &
      21              :                            cp_fm_struct_create, &
      22              :                            cp_fm_struct_release
      23              :    USE cp_cfm_types, ONLY: cp_cfm_type, &
      24              :                            cp_cfm_set_all, &
      25              :                            cp_cfm_create, &
      26              :                            cp_fm_to_cfm, &
      27              :                            cp_cfm_to_fm, &
      28              :                            cp_cfm_release
      29              :    USE cp_dbcsr_api, ONLY: dbcsr_type, &
      30              :                            dbcsr_p_type, &
      31              :                            dbcsr_create, &
      32              :                            dbcsr_release, &
      33              :                            dbcsr_get_info
      34              :    USE parallel_gemm_api, ONLY: parallel_gemm
      35              :    USE dbt_api, ONLY: dbt_type, &
      36              :                       dbt_create, &
      37              :                       dbt_destroy
      38              :    USE qs_mo_types, ONLY: mo_set_type
      39              :    USE basis_set_types, ONLY: gto_basis_set_p_type
      40              :    USE cp_control_types, ONLY: dft_control_type
      41              :    USE qs_environment_types, ONLY: qs_environment_type, &
      42              :                                    get_qs_env
      43              :    USE force_env_types, ONLY: force_env_type
      44              :    USE post_scf_bandstructure_types, ONLY: post_scf_bandstructure_type, &
      45              :                                            eps_qp_gap, &
      46              :                                            max_qp_gap
      47              :    USE rt_propagation_types, ONLY: rt_prop_type
      48              :    USE rt_propagation_utils, ONLY: warn_section_unused
      49              :    USE gw_integrals, ONLY: build_3c_integral_block
      50              :    USE gw_large_cell_Gamma, ONLY: compute_3c_integrals
      51              :    USE gw_utils, ONLY: rtbse_resolve_rirs_flag
      52              :    USE qs_tensors, ONLY: neighbor_list_3c_destroy
      53              :    USE libint_2c_3c, ONLY: libint_potential_type
      54              :    USE input_constants, ONLY: use_mom_ref_coac, &
      55              :                               do_bch, &
      56              :                               do_exact, &
      57              :                               rtp_bse_ham_gw, &
      58              :                               rtp_method_bse_linearized
      59              :    USE bse_util, ONLY: determine_cutoff_indices
      60              :    USE cp_log_handling, ONLY: cp_to_string
      61              :    USE physcon, ONLY: evolt
      62              :    USE mathconstants, ONLY: z_zero
      63              :    USE input_section_types, ONLY: section_vals_type, &
      64              :                                   section_vals_val_get, &
      65              :                                   section_vals_get_subs_vals
      66              : 
      67              : #include "../base/base_uses.f90"
      68              : 
      69              :    IMPLICIT NONE
      70              : 
      71              :    PRIVATE
      72              : 
      73              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = "rt_bse"
      74              : 
      75              :    #:include "rt_bse_macros.fypp"
      76              : 
      77              :    PUBLIC :: rtbse_env_type, &
      78              :              create_rtbse_env, &
      79              :              release_rtbse_env, &
      80              :              multiply_cfm_fm, &
      81              :              multiply_fm_cfm, &
      82              :              create_hartree_ri_3c, &
      83              :              create_sigma_workspace_qs_only
      84              : 
      85              :    ! ! Created so that we can have an array of pointers to arrays
      86              :    ! TYPE series_real_type
      87              :    !    REAL(kind=dp), DIMENSION(:), POINTER                      :: series => NULL()
      88              :    ! END TYPE series_real_type
      89              :    ! TYPE series_complex_type
      90              :    !    COMPLEX(kind=dp), DIMENSION(:), POINTER                   :: series => NULL()
      91              :    ! END TYPE series_complex_type
      92              : 
      93              : ! **************************************************************************************************
      94              : !> \param n_spin Number of spin channels that are present
      95              : !> \param n_ao Number of atomic orbitals
      96              : !> \param n_RI Number of RI orbitals
      97              : !> \param n_occ Number of occupied orbitals, spin dependent
      98              : !> \param spin_degeneracy Number of electrons per orbital
      99              : !> \param field Electric field calculated at the given timestep
     100              : !> \param moments Moment operators (2nd index = spin) along cartesian directions - centered at zero charge - used for plotting
     101              : !> \param moments_field Moment operators (2nd index = spin) along cartesian directions - used to coupling to the field -
     102              : !>                origin bound to unit cell
     103              : !> \param sim_step Current step of the simulation
     104              : !> \param sim_start Starting step of the simulation
     105              : !> \param sim_nsteps Number of steps of the simulation
     106              : !> \param sim_time Current time of the simulation
     107              : !> \param sim_dt Timestep of the simulation
     108              : !> \param sim_dt_restart Original-run timestep read from the trace on restart (< 0 when not a restart)
     109              : !> \param etrs_threshold Self-consistency threshold for enforced time reversal symmetry propagation
     110              : !> \param exp_accuracy Threshold for matrix exponential calculation
     111              : !> \param dft_control DFT control parameters
     112              : !> \param ham_effective Real and imaginary part of the effective Hamiltonian used to propagate
     113              : !>                      the density matrix
     114              : !> \param ham_reference Reference Hamiltonian, which does not change in the
     115              : !>                      propagation = DFT+G0W0 - initial Hartree - initial COHSEX
     116              : !> \param ham_workspace Workspace matrices for use with the Hamiltonian propagation - storage of
     117              : !>                      exponential propagators etc.
     118              : !> \param rho Density matrix at the current time step
     119              : !> \param rho_new Density matrix - workspace in ETRS
     120              : !> \param rho_last Density matrix - workspace in ETRS
     121              : !> \param rho_new_last Density matrix - workspace in ETRS
     122              : !> \param rho_M Density matrix - workspace in ETRS
     123              : !> \param S_inv_fm Inverse overlap matrix, full matrix
     124              : !> \param S_fm Overlap matrix, full matrix
     125              : !> \param S_inv Inverse overlap matrix, sparse matrix
     126              : !> \param rho_dbcsr Density matrix, sparse matrix
     127              : !> \param rho_workspace Matrices for storage of density matrix at different timesteps for
     128              : !>                      interpolation and self-consistency checks etc.
     129              : !> \param complex_workspace Workspace for complex density (exact diagonalisation)
     130              : !> \param complex_s Complex overlap matrix (exact diagonalisation)
     131              : !> \param real_eigvals Eigenvalues of hermitian matrix (exact diagonalisation)
     132              : !> \param exp_eigvals Exponentiated eigenvalues (exact diagonalisation)
     133              : !> \param v_dbcsr Sparse matrix with bare Coulomb in RI basis
     134              : !> \param w_dbcsr Sparse matrix with correlation part of dressed Coulomb in RI basis (without bare Coulomb)
     135              : !> \param screened_dbt Tensor for screened Coulomb interaction
     136              : !> \param greens_dbt Tensor for greens function/density matrix
     137              : !> \param t_3c_w Tensor containing 3c integrals
     138              : !> \param t_3c_work_RI_AO__AO Tensor sigma contraction
     139              : !> \param t_3c_work2_RI_AO__AO Tensor sigma contraction
     140              : !> \param sigma_SEX Screened exchange self-energy
     141              : !> \param sigma_COH Coulomb hole self-energy
     142              : !> \param hartree_curr Current Hartree matrix
     143              : !> \param etrs_max_iter Maximum number of ETRS iterations
     144              : !> \param ham_reference_type Which Hamiltonian to use as single particle basis
     145              : !> \param mat_exp_method Which method to use for matrix exponentiation
     146              : !> \param unit_nr Number of output unit
     147              : !> \param int_3c_array Array containing the local 3c integrals
     148              : !> \author Stepan Marek (01.24)
     149              : ! **************************************************************************************************
     150              :    TYPE rtbse_env_type
     151              :       INTEGER                                                   :: n_spin = 1, &
     152              :                                                                    n_ao = -1, &
     153              :                                                                    n_RI = -1
     154              :       INTEGER, DIMENSION(2)                                     :: n_occ = -1
     155              :       ! Active MO window for linearized RT-BSE truncation. When no truncation is requested,
     156              :       ! first_active_mo=1, last_active_mo=n_ao, and mo_active=n_ao. The window is the
     157              :       ! combined inclusive bound that covers both spin channels.
     158              :       INTEGER                                                   :: first_active_mo = 1, &
     159              :                                                                    last_active_mo = -1, &
     160              :                                                                    mo_active = -1
     161              :       REAL(KIND=dp)                                             :: rtbse_energy_cutoff_occ = -1.0_dp, &
     162              :                                                                    rtbse_energy_cutoff_empty = -1.0_dp
     163              :       LOGICAL                                                   :: active_mo_truncation = .FALSE.
     164              :       LOGICAL                                                   :: linearized = .FALSE.
     165              :       ! Tamm-Dancoff approximation switch (linearized RT-BSE only).
     166              :       LOGICAL                                                   :: tda_active = .FALSE.
     167              :       ! First-peak shift for the TDA path (linearized RT-BSE only).
     168              :       ! Shifts active-MO single-particle diagonals by +Omega_0/2 (occ) / -Omega_0/2 (virt)
     169              :       ! with Omega_0 = eps_min_ai so the lowest active OV mode oscillates at zero in the
     170              :       ! rotating frame (RK4-exact for peak 1). omega_max becomes the full active OV width
     171              :       ! Delta = eps_max_ai - eps_min_ai. The resulting rotating-frame density is undone
     172              :       ! at I/O so observables stay lab-frame.
     173              :       LOGICAL                                                   :: tda_shift_to_first_peak = .FALSE.
     174              :       REAL(kind=dp)                                             :: omega_shift = 0.0_dp
     175              :       ! Debug-only kernel switches shared by initialization and propagation.
     176              :       LOGICAL                                                   :: debug_disable_hartree = .FALSE., &
     177              :                                                                    debug_disable_sex = .FALSE.
     178              :       ! RI framework for the linRTBSE Hartree + screened-exchange kernels, set by the KERNEL_RI
     179              :       ! input keyword (DEFAULT inherits bs_env%do_gw_ri_rs; RS/AO force; full RT-BSE forced AO).
     180              :       ! .TRUE. = RI-RS grid kernels, .FALSE. = AO-RI. The required grid and V_grid/W0_grid
     181              :       ! kernels are built on demand and reused across steps.
     182              :       LOGICAL                                                   :: rirs_kernel = .FALSE.
     183              :       ! Liouvillian eigenvalue diagnostic (TDA + n_spin=1 only). When .TRUE., at job
     184              :       ! init the linearized RT-BSE assembles the OV-subspace Liouvillian by probing
     185              :       ! apply_liouvillian_to_drho with canonical OV basis vectors and diagonalizes
     186              :       ! via cp_cfm_heevd. In TDA this equals the Casida-A eigenvalue problem.
     187              :       ! Run once, no propagation impact.
     188              :       LOGICAL                                                   :: diagnose_liouvillian_eig = .FALSE.
     189              :       ! Whether to enforce max_dt within stability region of rk4
     190              :       LOGICAL                                                   :: enforce_max_dt = .FALSE.
     191              :       ! Owned fm structures sized to the active MO window. Equal to the full n_ao x n_ao
     192              :       ! when no truncation is active.
     193              :       TYPE(cp_fm_struct_type), POINTER                          :: fm_struct_mo_active => NULL()
     194              :       ! n_ao x mo_active fm struct used for C_active and AO<->MO rectangular intermediates.
     195              :       TYPE(cp_fm_struct_type), POINTER                          :: fm_struct_ao_mo_active => NULL()
     196              :       ! Liouvillian-diagnostic struct: (N_OV_joint x N_OV_joint), spin blocks stacked, on the
     197              :       ! same BLACS context as fm_struct_mo_active. Allocated only when diagnose_liouvillian_eig=.TRUE..
     198              :       TYPE(cp_fm_struct_type), POINTER                          :: fm_struct_ov_pairs => NULL()
     199              :       ! Truncated MO coefficient slabs C_active(:,:) of size n_ao x mo_active for each spin
     200              :       ! (only allocated for the linearized path).
     201              :       TYPE(cp_fm_type), DIMENSION(:), POINTER                   :: C_active => NULL()
     202              :       ! Rectangular n_ao x mo_active scratch used by linearized AO<->MO transforms.
     203              :       TYPE(cp_fm_type), DIMENSION(:), POINTER                   :: ao_mo_workspace => NULL()
     204              :       REAL(kind=dp)                                             :: spin_degeneracy = 2
     205              :       REAL(kind=dp), DIMENSION(3)                               :: field = 0.0_dp
     206              :       TYPE(cp_fm_type), DIMENSION(:, :), POINTER                :: moments => NULL(), &
     207              :                                                                    moments_field => NULL()
     208              :       INTEGER                                                   :: sim_step = 0, &
     209              :                                                                    sim_start = 0, &
     210              :                                                                    ! Needed to continue runs by loading previous moments trace
     211              :                                                                    sim_start_orig = 0, &
     212              :                                                                    sim_nsteps = -1, &
     213              :                                                                    ! Default reference point type for output moments
     214              :                                                                    ! Field moments always use zero reference
     215              :                                                                    moment_ref_type = use_mom_ref_coac
     216              :       ! Restart output bookkeeping: RESTART.trace header+prefix and (linearized) C_active are
     217              :       ! (re)written on the first output_restart call of a run, then .trace is appended per step.
     218              :       LOGICAL                                                   :: restart_trace_written = .FALSE., &
     219              :                                                                    restart_C_written = .FALSE.
     220              :       REAL(kind=dp), DIMENSION(:), POINTER                      :: user_moment_ref_point => NULL()
     221              :       REAL(kind=dp)                                             :: sim_time = 0.0_dp, &
     222              :                                                                    sim_dt = 0.1_dp, &
     223              :                                                                    ! Original-run dt from the trace header on restart (< 0 when not
     224              :                                                                    ! a restart); ENFORCE_MAX_DT reuses it instead of recomputing dt
     225              :                                                                    sim_dt_restart = -1.0_dp, &
     226              :                                                                    maximum_timestep = -1.0_dp, &
     227              :                                                                    omega_max = -1.0_dp, &
     228              :                                                                    etrs_threshold = 1.0e-7_dp, &
     229              :                                                                    exp_accuracy = 1.0e-10_dp, &
     230              :                                                                    ft_damping = 0.0_dp, &
     231              :                                                                    ft_start = 0.0_dp
     232              :       ! Which element of polarizability to print out
     233              :       INTEGER, DIMENSION(:, :), POINTER                         :: pol_elements => NULL()
     234              :       TYPE(dft_control_type), POINTER                           :: dft_control => NULL()
     235              :       ! DEBUG : Trying keeping the reference to previous environments inside this one
     236              :       TYPE(qs_environment_type), POINTER                        :: qs_env => NULL()
     237              :       TYPE(post_scf_bandstructure_type), POINTER                :: bs_env => NULL()
     238              :       ! Stores data needed for reading/writing to the restart files
     239              :       TYPE(section_vals_type), POINTER                          :: restart_section => NULL(), &
     240              :                                                                    field_section => NULL(), &
     241              :                                                                    rho_section => NULL(), &
     242              :                                                                    ft_section => NULL(), &
     243              :                                                                    pol_section => NULL(), &
     244              :                                                                    eig_section => NULL(), &
     245              :                                                                    moments_section => NULL(), &
     246              :                                                                    rtp_section => NULL()
     247              :       LOGICAL                                                   :: restart_extracted = .FALSE.
     248              : 
     249              :       ! Different indices signify different spins
     250              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER                  :: ham_effective => NULL()
     251              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER                  :: ham_reference => NULL()
     252              :       !Only for linearised RTBSE
     253              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER                  :: ham_reference_singleparticle => NULL()
     254              :       ! Active single-particle energies = diag(ham_reference_singleparticle), replicated over all
     255              :       ! ranks. Drives the [H^0,rho]_mn = (eps_m - eps_n) rho_mn element-wise commutator (mo_active x n_spin).
     256              :       REAL(kind=dp), DIMENSION(:, :), POINTER                  :: eps_active => NULL()
     257              :       ! Original run's active eigenvalues stashed from RESTART.trace at read time; compared against
     258              :       ! the recomputed eps_active once the Hamiltonian is built (consistency heads-up), then freed.
     259              :       REAL(kind=dp), DIMENSION(:, :), POINTER                  :: eps_active_restart => NULL()
     260              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER                  :: ham_workspace => NULL()
     261              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER                  :: sigma_SEX => NULL()
     262              :       TYPE(cp_fm_type), DIMENSION(:), POINTER                   :: sigma_COH => NULL(), &
     263              :                                                                    hartree_curr => NULL()
     264              :       ! AO-sized scratch buffers used in the linearized RT-BSE path so that the MO-sized
     265              :       ! sigma_COH/sigma_SEX/hartree_curr matrices above can be allocated on
     266              :       ! fm_struct_mo_active. Only allocated when linearized=.TRUE..
     267              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER                  :: sigma_SEX_ao => NULL()
     268              :       TYPE(cp_fm_type), DIMENSION(:), POINTER                   :: hartree_curr_ao => NULL()
     269              :       ! Open-shell cross-spin Hartree (TDA AO-RI), single shared AO buffers built once per
     270              :       ! RK4 stage: rho_total_ao_scratch (fm_s struct, matches rho_ao_scratch for the spin sum);
     271              :       ! hartree_total_ao = V_H[sum] (fm_ks struct, matches sigma_SEX_ao as the Hartree output).
     272              :       TYPE(cp_cfm_type)                                         :: rho_total_ao_scratch = cp_cfm_type(), &
     273              :                                                                    hartree_total_ao = cp_cfm_type()
     274              :       ! RI-RS Hartree diagonal reuse: spin-summed grid density n_l = diag(phi.rho.phi^T), harvested
     275              :       ! from the SEX rho_grid (before its Hadamard, scaled by spin_degeneracy) and consumed by
     276              :       ! compute_hartree_ri_rs_from_diag so Hartree never rebuilds the grid product. Re/Im; sized
     277              :       ! n_grid; allocated in initialize_hartree_potential when rirs_kernel. rtbse_env-owned (not
     278              :       ! bs_env%ri_rs) to avoid aliasing bs_env when passed through get_sigma_complex.
     279              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)                 :: hartree_diag_re, hartree_diag_im
     280              :       ! mo_active x mo_active real workspace pair used in MO-side transforms (linearized only).
     281              :       TYPE(cp_fm_type), DIMENSION(:), POINTER                   :: real_workspace_mo => NULL()
     282              :       ! Liouvillian-diagnostic scratch (allocated only when diagnose_liouvillian_eig=.TRUE.).
     283              :       ! drho_probe(:) / L_drho(:) are per-spin arrays on fm_struct_mo_active: the joint TDA
     284              :       ! diagnostic probes one spin and reads the Liouvillian response on every spin block.
     285              :       ! L_pairs / eigvecs_pairs live on fm_struct_ov_pairs (N_OV_joint x N_OV_joint, the spin
     286              :       ! blocks stacked). A_mat / B_mat / AmB_scratch / ApB_scratch are ABBA-only blocks holding
     287              :       ! A, B, (A-B) -> (A-B)^{1/2}, and (A+B) for the Furche reduction; allocated only
     288              :       ! when .NOT. tda_active (n_spin=1; TDA path uses L_pairs alone).
     289              :       ! eigenvalues_liouvillian holds the N_OV_joint real eigenvalues from cp_cfm_heevd.
     290              :       TYPE(cp_cfm_type)                                         :: L_pairs = cp_cfm_type(), &
     291              :                                                                    eigvecs_pairs = cp_cfm_type(), &
     292              :                                                                    A_mat = cp_cfm_type(), &
     293              :                                                                    B_mat = cp_cfm_type(), &
     294              :                                                                    AmB_scratch = cp_cfm_type(), &
     295              :                                                                    ApB_scratch = cp_cfm_type()
     296              :       REAL(kind=dp), DIMENSION(:), POINTER                      :: eigenvalues_liouvillian => NULL()
     297              : 
     298              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER                  :: rho => NULL(), &
     299              :                                                                    rho_new => NULL(), &
     300              :                                                                    rho_new_last => NULL(), &
     301              :                                                                    rho_M => NULL(), &
     302              :                                                                    rho_orig => NULL(), &
     303              :                                                                    rho_ao_scratch => NULL(), &
     304              :                                                                    rho_delta_mo => NULL(), &
     305              :                                                                    drho_probe => NULL(), &
     306              :                                                                    L_drho => NULL()
     307              :       ! Workspace for rk4 in linearized RTBSE
     308              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER                  :: rk4_coefficients => NULL()
     309              : 
     310              :       TYPE(cp_fm_type)                                          :: S_inv_fm = cp_fm_type(), &
     311              :                                                                    S_fm = cp_fm_type()
     312              :       ! Many routines require overlap in the complex format
     313              :       TYPE(cp_cfm_type)                                         :: S_cfm = cp_cfm_type()
     314              :       TYPE(dbcsr_type)                                          :: rho_dbcsr = dbcsr_type(), &
     315              :                                                                    v_ao_dbcsr = dbcsr_type()
     316              :       ! Indices only correspond to different workspaces
     317              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER                  :: rho_workspace => NULL()
     318              :       ! Many methods use real and imaginary parts separately - prevent unnecessary reallocation
     319              :       TYPE(cp_fm_type), DIMENSION(:), POINTER                   :: real_workspace => NULL()
     320              :       ! AO-sized complex scratch used to stage the real part in get_sigma_complex.
     321              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER                  :: sigma_complex_workspace => NULL()
     322              :       ! Workspace required for exact matrix exponentiation
     323              :       REAL(kind=dp), DIMENSION(:), POINTER                      :: real_eigvals => NULL()
     324              :       COMPLEX(kind=dp), DIMENSION(:), POINTER                   :: exp_eigvals => NULL()
     325              :       ! Workspace for saving the values for FT
     326              :       ! TODO : Change back to multi-dimensional arrays
     327              :       ! Index 1 : spin, Index 2 : direction, Index 3 : time point
     328              :       COMPLEX(kind=dp), DIMENSION(:, :, :), POINTER               :: moments_trace => NULL()
     329              :       REAL(kind=dp), DIMENSION(:), POINTER                      :: time_trace => NULL()
     330              :       ! Index 1 : direction, Index 2 : time point
     331              :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER                 :: field_trace => NULL()
     332              :       ! Workspace required for hartree_pw
     333              :       TYPE(dbcsr_type)                                          :: v_dbcsr = dbcsr_type(), &
     334              :                                                                    w_dbcsr = dbcsr_type()
     335              : #if defined(FTN_NO_DEFAULT_INIT)
     336              :       TYPE(dbt_type)                                            :: screened_dbt, &
     337              :                                                                    greens_dbt, &
     338              :                                                                    t_3c_w, &
     339              :                                                                    t_3c_work_RI_AO__AO, &
     340              :                                                                    t_3c_work2_RI_AO__AO
     341              : #else
     342              :       TYPE(dbt_type)                                            :: screened_dbt = dbt_type(), &
     343              :                                                                    greens_dbt = dbt_type(), &
     344              :                                                                    t_3c_w = dbt_type(), &
     345              :                                                                    t_3c_work_RI_AO__AO = dbt_type(), &
     346              :                                                                    t_3c_work2_RI_AO__AO = dbt_type()
     347              : #endif
     348              :       ! These matrices are always real
     349              :       INTEGER                                                   :: etrs_max_iter = 10
     350              :       INTEGER                                                   :: ham_reference_type = 2
     351              :       INTEGER                                                   :: mat_exp_method = 4
     352              :       INTEGER                                                   :: unit_nr = -1
     353              :       REAL(kind=dp), DIMENSION(:, :, :), POINTER                :: int_3c_array => NULL()
     354              :       ! Parameters for Padé refinement
     355              :       REAL(kind=dp)                                             :: pade_e_min = 0.0_dp, &
     356              :                                                                    pade_e_max = 100.0_dp, &
     357              :                                                                    pade_e_step = 0.05_dp, &
     358              :                                                                    pade_fit_e_min = 0.0_dp, &
     359              :                                                                    pade_fit_e_max = -1.0_dp
     360              :       INTEGER                                                   :: pade_npoints = 0
     361              :       LOGICAL                                                   :: pade_requested = .FALSE.
     362              :       COMPLEX(kind=dp), DIMENSION(:), POINTER                   :: pade_x_eval => NULL()
     363              : 
     364              :    END TYPE rtbse_env_type
     365              : 
     366              : CONTAINS
     367              : 
     368              : ! **************************************************************************************************
     369              : !> \brief Allocates structures and prepares rtbse_env for run
     370              : !> \param rtbse_env rtbse_env_type that is initialised
     371              : !> \param force_env Force environment - entry point of the calculation
     372              : !> \param linearized Optional; when present and .TRUE., configure the environment for the linearized RT-BSE path
     373              : !> \author Stepan Marek
     374              : !> \date 02.2024
     375              : ! **************************************************************************************************
     376           72 :    SUBROUTINE create_rtbse_env(rtbse_env, force_env, linearized)
     377              :       TYPE(rtbse_env_type), POINTER                             :: rtbse_env
     378              :       TYPE(force_env_type), POINTER                             :: force_env
     379              :       LOGICAL, OPTIONAL                                        :: linearized
     380              :       TYPE(post_scf_bandstructure_type), POINTER                :: bs_env
     381              :       TYPE(rt_prop_type), POINTER                               :: rtp
     382           72 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER                 :: matrix_s
     383           72 :       TYPE(mo_set_type), DIMENSION(:), POINTER                  :: mos
     384              :       INTEGER                                                   :: i, k, n_ov, i_spin
     385              :       TYPE(section_vals_type), POINTER                          :: input, bs_sec, md_sec
     386              :       TYPE(cp_fm_struct_type), POINTER                          :: mo_struct
     387              : 
     388              :       ! Allocate the storage for the gwbse environment
     389           72 :       NULLIFY (rtbse_env, mo_struct)
     390         3384 :       ALLOCATE (rtbse_env)
     391           72 :       IF (PRESENT(linearized)) rtbse_env%linearized = linearized
     392              :       ! Extract the other types first
     393              :       CALL get_qs_env(force_env%qs_env, &
     394              :                       bs_env=bs_env, &
     395              :                       rtp=rtp, &
     396              :                       matrix_s=matrix_s, &
     397              :                       mos=mos, &
     398              :                       dft_control=rtbse_env%dft_control, &
     399           72 :                       input=input)
     400           72 :       bs_sec => section_vals_get_subs_vals(input, "PROPERTIES%BANDSTRUCTURE")
     401           72 :       IF (.NOT. ASSOCIATED(bs_env)) THEN
     402            0 :          CPABORT("Cannot run RT-BSE without running GW calculation (PROPERTIES) before")
     403              :       END IF
     404              :       ! Number of spins
     405           72 :       rtbse_env%n_spin = bs_env%n_spin
     406              :       ! Open shell (n_spin>1) is only implemented and tested for the linearized
     407              :       ! propagation; the full RT-BSE open-shell path is untested.
     408           72 :       IF (rtbse_env%n_spin > 1 .AND. .NOT. rtbse_env%linearized) THEN
     409              :          CALL cp_abort(__LOCATION__, &
     410              :                        "Open-shell (n_spin>1) RT-BSE is only implemented and tested for the "// &
     411              :                        "linearized propagation. Set DFT%REAL_TIME_PROPAGATION%RTBSE%LRRTBSE "// &
     412            0 :                        ".TRUE.; the full (non-linearized) open-shell RT-BSE path is untested.")
     413              :       END IF
     414              :       ! Number of atomic orbitals
     415           72 :       rtbse_env%n_ao = bs_env%n_ao
     416              :       ! Number of auxiliary basis orbitals
     417           72 :       rtbse_env%n_RI = bs_env%n_RI
     418              :       ! Number of occupied orbitals - for closed shell equals to half the number of electrons
     419          432 :       rtbse_env%n_occ(:) = bs_env%n_occ(:)
     420              :       ! Spin degeneracy - number of spins per orbital
     421           72 :       rtbse_env%spin_degeneracy = bs_env%spin_degeneracy
     422              :       ! Default field is zero
     423          288 :       rtbse_env%field(:) = 0.0_dp
     424              :       ! Default time is zero
     425           72 :       rtbse_env%sim_step = 0
     426           72 :       rtbse_env%sim_time = 0
     427              :       ! Time step is taken from rtp
     428           72 :       md_sec => section_vals_get_subs_vals(force_env%root_section, "MOTION%MD")
     429           72 :       CALL section_vals_val_get(md_sec, "TIMESTEP", r_val=rtbse_env%sim_dt)
     430              :       ! rtbse_env%sim_dt = rtp%dt
     431              :       ! Threshold for etrs is taken from the eps_energy from RT propagation
     432           72 :       rtbse_env%etrs_threshold = rtbse_env%dft_control%rtp_control%eps_ener
     433           72 :       rtbse_env%exp_accuracy = rtbse_env%dft_control%rtp_control%eps_exp
     434              :       ! Recover custom options
     435              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%RTBSE%RTBSE_HAMILTONIAN", &
     436           72 :                                 i_val=rtbse_env%ham_reference_type)
     437              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%MAX_ITER", &
     438           72 :                                 i_val=rtbse_env%etrs_max_iter)
     439              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%MAT_EXP", &
     440           72 :                                 i_val=rtbse_env%mat_exp_method)
     441              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%RTBSE%ENERGY_CUTOFF_OCC", &
     442           72 :                                 r_val=rtbse_env%rtbse_energy_cutoff_occ)
     443              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%RTBSE%ENERGY_CUTOFF_EMPTY", &
     444           72 :                                 r_val=rtbse_env%rtbse_energy_cutoff_empty)
     445              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%RTBSE%TDA", &
     446           72 :                                 l_val=rtbse_env%tda_active)
     447              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%RTBSE%TDA_SHIFT_TO_FIRST_PEAK", &
     448           72 :                                 l_val=rtbse_env%tda_shift_to_first_peak)
     449              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%RTBSE%ENFORCE_MAX_DT", &
     450           72 :                                 l_val=rtbse_env%enforce_max_dt)
     451              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%RTBSE%DEBUG_DISABLE_HARTREE", &
     452           72 :                                 l_val=rtbse_env%debug_disable_hartree)
     453              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%RTBSE%DEBUG_DISABLE_SEX", &
     454           72 :                                 l_val=rtbse_env%debug_disable_sex)
     455              :       ! RI-RS kernel switch (KERNEL_RI: DEFAULT=do_gw_ri_rs, RS/AO force, full-RTBSE force-off)
     456              :       ! is resolved by the shared helper so de_init_bs_env reaches the same verdict when
     457              :       ! deciding whether to retain nl_3c.
     458           72 :       CALL rtbse_resolve_rirs_flag(force_env%qs_env, bs_env, rirs_kernel=rtbse_env%rirs_kernel)
     459              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%RTBSE%DIAGNOSE_LIOUVILLIAN_EIG", &
     460           72 :                                 l_val=rtbse_env%diagnose_liouvillian_eig)
     461              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%RTBSE%EPS_FILTER_RHO", &
     462           72 :                                 r_val=bs_env%ri_rs%eps_filter_rho)
     463              :       CALL section_vals_val_get(input, "DFT%REAL_TIME_PROPAGATION%RTBSE%CUTOFF_RADIUS_W0", &
     464           72 :                                 r_val=bs_env%ri_rs%cutoff_radius_w0)
     465              : 
     466           72 :       IF (.NOT. rtbse_env%dft_control%rtp_control%rtp_method == rtp_method_bse_linearized) THEN
     467           14 :          rtbse_env%rtbse_energy_cutoff_occ = -1.0_dp
     468           14 :          rtbse_env%rtbse_energy_cutoff_empty = -1.0_dp
     469           14 :          rtbse_env%enforce_max_dt = .FALSE.
     470           14 :          rtbse_env%debug_disable_hartree = .FALSE.
     471           14 :          rtbse_env%debug_disable_sex = .FALSE.
     472           14 :          rtbse_env%tda_shift_to_first_peak = .FALSE.
     473              :          ! rirs_kernel is already forced .FALSE. here by rtbse_resolve_rirs_flag.
     474           14 :          rtbse_env%diagnose_liouvillian_eig = .FALSE.
     475           14 :          bs_env%ri_rs%eps_filter_rho = -1.0_dp
     476           14 :          bs_env%ri_rs%cutoff_radius_w0 = -1.0_dp
     477              :       END IF
     478              :       ! First-peak shift only makes sense within TDA; force-disable otherwise.
     479           72 :       IF (.NOT. rtbse_env%tda_active) rtbse_env%tda_shift_to_first_peak = .FALSE.
     480           72 :       rtbse_env%omega_shift = 0.0_dp
     481              : 
     482           72 :       IF (rtbse_env%tda_active .AND. .NOT. rtbse_env%linearized) THEN
     483            0 :          CPABORT("RTBSE TDA keyword requires LINEARIZED_BSE_PROPAGATION=.TRUE.")
     484              :       END IF
     485              :       ! Open shell: omega_shift would be referenced to a non-physical cross-spin
     486              :       ! pseudo-gap (global MIN/MAX over both spins); abort until made per-spin.
     487           72 :       IF (rtbse_env%tda_shift_to_first_peak .AND. rtbse_env%n_spin > 1) THEN
     488              :          CALL cp_abort(__LOCATION__, &
     489              :                        "TDA_SHIFT_TO_FIRST_PEAK is not implemented for open-shell (n_spin>1) "// &
     490              :                        "systems - the first-peak gap estimate would mix spin channels. "// &
     491            0 :                        "Set TDA_SHIFT_TO_FIRST_PEAK=.FALSE. for open-shell runs.")
     492              :       END IF
     493           72 :       CALL check_qp_gap_sanity(rtbse_env, bs_env)
     494           72 :       CALL determine_active_mo_window(rtbse_env, bs_env)
     495              :       ! Owned active-MO matrix structure (currently identical to the full n_ao x n_ao struct
     496              :       ! when no truncation is active; will be used by the linearized RT-BSE allocation path).
     497           72 :       NULLIFY (rtbse_env%fm_struct_mo_active)
     498              :       CALL cp_fm_struct_create(rtbse_env%fm_struct_mo_active, &
     499              :                                bs_env%fm_ks_Gamma(1)%matrix_struct%para_env, &
     500              :                                bs_env%fm_ks_Gamma(1)%matrix_struct%context, &
     501           72 :                                rtbse_env%mo_active, rtbse_env%mo_active)
     502              :       ! Rectangular n_ao x mo_active struct used for C_active and AO<->MO intermediates
     503           72 :       NULLIFY (rtbse_env%fm_struct_ao_mo_active)
     504              :       CALL cp_fm_struct_create(rtbse_env%fm_struct_ao_mo_active, &
     505              :                                bs_env%fm_ks_Gamma(1)%matrix_struct%para_env, &
     506              :                                bs_env%fm_ks_Gamma(1)%matrix_struct%context, &
     507           72 :                                rtbse_env%n_ao, rtbse_env%mo_active)
     508              :       ! Choose the matrix struct used for MO-side persistent matrices.
     509              :       ! Linearized RT-BSE: mo_active x mo_active. Full RT-BSE: full AO struct (unchanged).
     510           72 :       IF (rtbse_env%linearized) THEN
     511           58 :          mo_struct => rtbse_env%fm_struct_mo_active
     512              :       ELSE
     513           14 :          mo_struct => bs_env%fm_ks_Gamma(1)%matrix_struct
     514              :       END IF
     515              :       ! Output unit number, recovered from the post_scf_bandstructure_type
     516           72 :       rtbse_env%unit_nr = bs_env%unit_nr
     517              :       ! Sim start index and total number of steps as well
     518           72 :       CALL section_vals_val_get(md_sec, "STEP_START_VAL", i_val=rtbse_env%sim_start)
     519              :       ! Copy this value to sim_start_orig for continuation runs
     520           72 :       rtbse_env%sim_start_orig = rtbse_env%sim_start
     521           72 :       CALL section_vals_val_get(md_sec, "STEPS", i_val=rtbse_env%sim_nsteps)
     522              :       ! Get the values for the FT
     523           72 :       rtbse_env%ft_damping = rtbse_env%dft_control%rtp_control%ft_damping
     524           72 :       rtbse_env%ft_damping = rtbse_env%dft_control%rtp_control%ft_t0
     525           72 :       rtbse_env%pol_elements => rtbse_env%dft_control%rtp_control%print_pol_elements
     526              : 
     527           72 :       rtbse_env%rtp_section => section_vals_get_subs_vals(input, "DFT%REAL_TIME_PROPAGATION")
     528              :       ! Get the restart section
     529           72 :       rtbse_env%restart_section => section_vals_get_subs_vals(rtbse_env%rtp_section, "PRINT%RESTART")
     530           72 :       rtbse_env%restart_extracted = .FALSE.
     531           72 :       rtbse_env%field_section => section_vals_get_subs_vals(rtbse_env%rtp_section, "PRINT%FIELD")
     532           72 :       rtbse_env%moments_section => section_vals_get_subs_vals(rtbse_env%rtp_section, "PRINT%MOMENTS")
     533              :       ! Moment specification
     534              :       CALL section_vals_val_get(rtbse_env%rtp_section, "PRINT%MOMENTS%REFERENCE", &
     535           72 :                                 i_val=rtbse_env%moment_ref_type)
     536              :       CALL section_vals_val_get(rtbse_env%rtp_section, "PRINT%MOMENTS%REFERENCE_POINT", &
     537           72 :                                 r_vals=rtbse_env%user_moment_ref_point)
     538           72 :       rtbse_env%rho_section => section_vals_get_subs_vals(rtbse_env%rtp_section, "PRINT%DENSITY_MATRIX")
     539           72 :       rtbse_env%ft_section => section_vals_get_subs_vals(rtbse_env%rtp_section, "PRINT%MOMENTS_FT")
     540           72 :       rtbse_env%pol_section => section_vals_get_subs_vals(rtbse_env%rtp_section, "PRINT%POLARIZABILITY")
     541           72 :       rtbse_env%eig_section => section_vals_get_subs_vals(rtbse_env%rtp_section, "PRINT%LIOUVILLIAN_EIG")
     542              :       ! Warn the user about print sections which are not yet implemented in the RTBSE run
     543              :       CALL warn_section_unused(rtbse_env%rtp_section, "PRINT%CURRENT", &
     544           72 :                                "CURRENT print section not yet implemented for RTBSE.")
     545              :       CALL warn_section_unused(rtbse_env%rtp_section, "PRINT%E_CONSTITUENTS", &
     546           72 :                                "E_CONSTITUENTS print section not yet implemented for RTBSE.")
     547              :       CALL warn_section_unused(rtbse_env%rtp_section, "PRINT%PROGRAM_RUN_INFO", &
     548           72 :                                "PROGRAM_RUN_INFO print section not yet implemented for RTBSE.")
     549              :       CALL warn_section_unused(rtbse_env%rtp_section, "PRINT%PROJECTION_MO", &
     550           72 :                                "PROJECTION_MO print section not yet implemented for RTBSE.")
     551              :       CALL warn_section_unused(rtbse_env%rtp_section, "PRINT%RESTART_HISTORY", &
     552           72 :                                "RESTART_HISTORY print section not yet implemented for RTBSE.")
     553              :       ! References to the parent qs_env / bs_env
     554           72 :       rtbse_env%qs_env => force_env%qs_env
     555           72 :       rtbse_env%bs_env => bs_env
     556              :       ! Padé refinement
     557           72 :       rtbse_env%pade_requested = rtbse_env%dft_control%rtp_control%pade_requested
     558           72 :       rtbse_env%pade_e_min = rtbse_env%dft_control%rtp_control%pade_e_min
     559           72 :       rtbse_env%pade_e_step = rtbse_env%dft_control%rtp_control%pade_e_step
     560           72 :       rtbse_env%pade_e_max = rtbse_env%dft_control%rtp_control%pade_e_max
     561           72 :       rtbse_env%pade_fit_e_min = rtbse_env%dft_control%rtp_control%pade_fit_e_min
     562           72 :       rtbse_env%pade_fit_e_max = rtbse_env%dft_control%rtp_control%pade_fit_e_max
     563           72 :       rtbse_env%pade_npoints = INT((rtbse_env%pade_e_max - rtbse_env%pade_e_min)/rtbse_env%pade_e_step)
     564              :       ! Evaluate the evaluation grid
     565           72 :       IF (rtbse_env%pade_requested) THEN
     566            2 :          NULLIFY (rtbse_env%pade_x_eval)
     567            6 :          ALLOCATE (rtbse_env%pade_x_eval(rtbse_env%pade_npoints))
     568         2000 :          DO i = 1, rtbse_env%pade_npoints
     569         2000 :             rtbse_env%pade_x_eval(i) = CMPLX(rtbse_env%pade_e_step*REAL(i - 1, kind=dp), 0.0, kind=dp)
     570              :          END DO
     571              :       END IF
     572              : 
     573              :       ! Allocate moments matrices.
     574              :       ! In linearized RT-BSE these store the MO-active transformed dipole moments;
     575              :       ! in full RT-BSE they remain AO-sized (initialized from overlap template).
     576           72 :       NULLIFY (rtbse_env%moments)
     577          536 :       ALLOCATE (rtbse_env%moments(3, rtbse_env%n_spin))
     578           72 :       NULLIFY (rtbse_env%moments_field)
     579          536 :       ALLOCATE (rtbse_env%moments_field(3, rtbse_env%n_spin))
     580          152 :       DO i_spin = 1, rtbse_env%n_spin
     581          392 :          DO k = 1, 3
     582          240 :             CALL cp_fm_create(rtbse_env%moments(k, i_spin), mo_struct)
     583          320 :             CALL cp_fm_create(rtbse_env%moments_field(k, i_spin), mo_struct)
     584              :          END DO
     585              :       END DO
     586              : 
     587              :       ! Allocate space for density propagation and other operations.
     588              :       ! In linearized RT-BSE these workspaces are MO-active sized; in full RT-BSE
     589              :       ! they remain at the full AO size.
     590           72 :       NULLIFY (rtbse_env%rho_workspace)
     591          360 :       ALLOCATE (rtbse_env%rho_workspace(4))
     592          360 :       DO i = 1, SIZE(rtbse_env%rho_workspace)
     593          288 :          CALL cp_cfm_create(rtbse_env%rho_workspace(i), mo_struct)
     594          360 :          CALL cp_cfm_set_all(rtbse_env%rho_workspace(i), CMPLX(0.0, 0.0, kind=dp))
     595              :       END DO
     596              : 
     597              :       ! TODO: gate workspace allocation so methods skip workspaces they don't need
     598              : 
     599              :       ! Allocate real workspace
     600           72 :       NULLIFY (rtbse_env%real_workspace)
     601           72 :       IF (rtbse_env%linearized) THEN
     602          174 :          ALLOCATE (rtbse_env%real_workspace(2))
     603              :       ELSE
     604           14 :          SELECT CASE (rtbse_env%mat_exp_method)
     605              :          CASE (do_exact)
     606            0 :             ALLOCATE (rtbse_env%real_workspace(4))
     607              :          CASE (do_bch)
     608           42 :             ALLOCATE (rtbse_env%real_workspace(2))
     609              :          CASE DEFAULT
     610           14 :             CPABORT("Only exact and BCH matrix propagation implemented in RT-BSE")
     611              :          END SELECT
     612              :       END IF
     613          216 :       DO i = 1, SIZE(rtbse_env%real_workspace)
     614          144 :          CALL cp_fm_create(rtbse_env%real_workspace(i), bs_env%fm_ks_Gamma(1)%matrix_struct)
     615          216 :          CALL cp_fm_set_all(rtbse_env%real_workspace(i), 0.0_dp)
     616              :       END DO
     617           72 :       NULLIFY (rtbse_env%sigma_complex_workspace)
     618          144 :       ALLOCATE (rtbse_env%sigma_complex_workspace(1))
     619           72 :       CALL cp_cfm_create(rtbse_env%sigma_complex_workspace(1), bs_env%fm_ks_Gamma(1)%matrix_struct)
     620           72 :       CALL cp_cfm_set_all(rtbse_env%sigma_complex_workspace(1), CMPLX(0.0_dp, 0.0_dp, kind=dp))
     621              :       ! Allocate density matrix (MO-active sized when linearized; AO-sized otherwise)
     622           72 :       NULLIFY (rtbse_env%rho)
     623          296 :       ALLOCATE (rtbse_env%rho(rtbse_env%n_spin))
     624          152 :       DO i = 1, rtbse_env%n_spin
     625          152 :          CALL cp_cfm_create(rtbse_env%rho(i), matrix_struct=mo_struct)
     626              :       END DO
     627              :       ! Allocate additional space for AO density matrix
     628              :       ! in linearised RTBSE, where default is MO
     629           72 :       IF (rtbse_env%linearized) THEN
     630           58 :          NULLIFY (rtbse_env%rho_ao_scratch)
     631          240 :          ALLOCATE (rtbse_env%rho_ao_scratch(rtbse_env%n_spin))
     632          124 :          DO i = 1, rtbse_env%n_spin
     633          124 :             CALL cp_cfm_create(rtbse_env%rho_ao_scratch(i), matrix_struct=bs_env%fm_s_Gamma%matrix_struct)
     634              :          END DO
     635              :       END IF
     636              :       ! Create the inverse overlap matrix, for use in density propagation
     637              :       ! Start by creating the actual overlap matrix
     638           72 :       CALL cp_fm_create(rtbse_env%S_fm, bs_env%fm_s_Gamma%matrix_struct)
     639           72 :       CALL cp_fm_create(rtbse_env%S_inv_fm, bs_env%fm_s_Gamma%matrix_struct)
     640           72 :       CALL cp_cfm_create(rtbse_env%S_cfm, bs_env%fm_s_Gamma%matrix_struct)
     641              : 
     642              :       ! Create the single particle hamiltonian
     643              :       ! Allocate workspace (MO-active sized in linearized RT-BSE; AO sized otherwise)
     644           72 :       NULLIFY (rtbse_env%ham_workspace)
     645          296 :       ALLOCATE (rtbse_env%ham_workspace(rtbse_env%n_spin))
     646          152 :       DO i = 1, rtbse_env%n_spin
     647           80 :          CALL cp_cfm_create(rtbse_env%ham_workspace(i), mo_struct)
     648          152 :          CALL cp_cfm_set_all(rtbse_env%ham_workspace(i), CMPLX(0.0, 0.0, kind=dp))
     649              :       END DO
     650              :       ! Now onto the Hamiltonian itself
     651              :       ! full RTBSE: Contains energy differences and Hartree/COHSEX ρ_0 parts
     652              :       ! linearised RTBSE: Contains only the Hartree/SEX ρ_0 parts as Δε * Δρ(t) need to be updated
     653           72 :       NULLIFY (rtbse_env%ham_reference)
     654          296 :       ALLOCATE (rtbse_env%ham_reference(rtbse_env%n_spin))
     655          152 :       DO i = 1, rtbse_env%n_spin
     656          152 :          CALL cp_cfm_create(rtbse_env%ham_reference(i), mo_struct)
     657              :       END DO
     658              :       ! Single particle Hamiltonian (Δε * Δρ(t)) for updates during timesteps in LR-RTBSE
     659           72 :       IF (rtbse_env%linearized) THEN
     660           58 :          NULLIFY (rtbse_env%ham_reference_singleparticle)
     661          240 :          ALLOCATE (rtbse_env%ham_reference_singleparticle(rtbse_env%n_spin))
     662          124 :          DO i = 1, rtbse_env%n_spin
     663          124 :             CALL cp_cfm_create(rtbse_env%ham_reference_singleparticle(i), mo_struct)
     664              :          END DO
     665           58 :          NULLIFY (rtbse_env%eps_active)
     666          232 :          ALLOCATE (rtbse_env%eps_active(rtbse_env%mo_active, rtbse_env%n_spin))
     667          904 :          rtbse_env%eps_active(:, :) = 0.0_dp
     668              :       END IF
     669              : 
     670              :       ! Create the matrices and workspaces for ETRS propagation
     671           72 :       NULLIFY (rtbse_env%ham_effective)
     672           72 :       NULLIFY (rtbse_env%rho_new)
     673           72 :       NULLIFY (rtbse_env%rho_new_last)
     674           72 :       NULLIFY (rtbse_env%rho_M)
     675           72 :       NULLIFY (rtbse_env%rho_orig)
     676          296 :       ALLOCATE (rtbse_env%ham_effective(rtbse_env%n_spin))
     677          296 :       ALLOCATE (rtbse_env%rho_new(rtbse_env%n_spin))
     678          296 :       ALLOCATE (rtbse_env%rho_new_last(rtbse_env%n_spin))
     679          296 :       ALLOCATE (rtbse_env%rho_M(rtbse_env%n_spin))
     680          296 :       ALLOCATE (rtbse_env%rho_orig(rtbse_env%n_spin))
     681          152 :       DO i = 1, rtbse_env%n_spin
     682           80 :          CALL cp_cfm_create(rtbse_env%ham_effective(i), mo_struct)
     683           80 :          CALL cp_cfm_set_all(rtbse_env%ham_effective(i), CMPLX(0.0, 0.0, kind=dp))
     684           80 :          CALL cp_cfm_create(rtbse_env%rho_new(i), mo_struct)
     685           80 :          CALL cp_cfm_set_all(rtbse_env%rho_new(i), CMPLX(0.0, 0.0, kind=dp))
     686           80 :          CALL cp_cfm_create(rtbse_env%rho_new_last(i), mo_struct)
     687           80 :          CALL cp_cfm_set_all(rtbse_env%rho_new_last(i), CMPLX(0.0, 0.0, kind=dp))
     688           80 :          CALL cp_cfm_create(rtbse_env%rho_M(i), mo_struct)
     689           80 :          CALL cp_cfm_set_all(rtbse_env%rho_M(i), CMPLX(0.0, 0.0, kind=dp))
     690          152 :          CALL cp_cfm_create(rtbse_env%rho_orig(i), mo_struct)
     691              :       END DO
     692              : 
     693              :       !For LR-RTBSE we need RK4 coefficients - create new workspace
     694           72 :       IF (rtbse_env%linearized) THEN
     695              :          ! Indexed by SPIN, not by RK4 stage: the spin loop is inner to each stage (do_rk4_stage), so every
     696              :          ! spin's current-stage k must be live at once, but only one stage's k per spin - each is folded
     697              :          ! into rho_end and the next stage density before the next stage overwrites it. Hence size n_spin.
     698           58 :          NULLIFY (rtbse_env%rk4_coefficients)
     699          240 :          ALLOCATE (rtbse_env%rk4_coefficients(rtbse_env%n_spin))
     700          124 :          DO i = 1, rtbse_env%n_spin
     701           66 :             CALL cp_cfm_create(rtbse_env%rk4_coefficients(i), mo_struct)
     702          124 :             CALL cp_cfm_set_all(rtbse_env%rk4_coefficients(i), CMPLX(0.0, 0.0, kind=dp))
     703              :          END DO
     704              :       END IF
     705              : 
     706              :       ! Fields for exact diagonalisation
     707           72 :       NULLIFY (rtbse_env%real_eigvals)
     708          216 :       ALLOCATE (rtbse_env%real_eigvals(rtbse_env%n_ao))
     709          796 :       rtbse_env%real_eigvals(:) = 0.0_dp
     710           72 :       NULLIFY (rtbse_env%exp_eigvals)
     711          216 :       ALLOCATE (rtbse_env%exp_eigvals(rtbse_env%n_ao))
     712          796 :       rtbse_env%exp_eigvals(:) = CMPLX(0.0, 0.0, kind=dp)
     713              : 
     714              :       ! Workspace for FT - includes (in principle) the zeroth step and the extra last step
     715           72 :       NULLIFY (rtbse_env%moments_trace)
     716              :       ! TODO : Unite the number of steps with TD-DFT
     717        19856 :       ALLOCATE (rtbse_env%moments_trace(rtbse_env%n_spin, 3, rtbse_env%sim_nsteps + 1), source=z_zero)
     718           72 :       NULLIFY (rtbse_env%field_trace)
     719        11144 :       ALLOCATE (rtbse_env%field_trace(3, rtbse_env%sim_nsteps + 1), source=z_zero)
     720           72 :       NULLIFY (rtbse_env%time_trace)
     721         2948 :       ALLOCATE (rtbse_env%time_trace(rtbse_env%sim_nsteps + 1), source=0.0_dp)
     722              : 
     723              :       ! Allocate self-energy parts and dynamic Hartree potential.
     724              :       ! In linearized RT-BSE these matrices hold the MO-active-sized result of the
     725              :       ! AO->MO transform; the AO-sized buffer is allocated as sigma_*_ao below.
     726           72 :       NULLIFY (rtbse_env%hartree_curr)
     727           72 :       NULLIFY (rtbse_env%sigma_SEX)
     728           72 :       NULLIFY (rtbse_env%sigma_COH)
     729          296 :       ALLOCATE (rtbse_env%hartree_curr(rtbse_env%n_spin))
     730          296 :       ALLOCATE (rtbse_env%sigma_SEX(rtbse_env%n_spin))
     731          296 :       ALLOCATE (rtbse_env%sigma_COH(rtbse_env%n_spin))
     732          152 :       DO i = 1, rtbse_env%n_spin
     733           80 :          CALL cp_fm_create(rtbse_env%sigma_COH(i), mo_struct)
     734           80 :          CALL cp_cfm_create(rtbse_env%sigma_SEX(i), mo_struct)
     735           80 :          CALL cp_fm_create(rtbse_env%hartree_curr(i), mo_struct)
     736           80 :          CALL cp_fm_set_all(rtbse_env%sigma_COH(i), 0.0_dp)
     737           80 :          CALL cp_cfm_set_all(rtbse_env%sigma_SEX(i), CMPLX(0.0, 0.0, kind=dp))
     738          152 :          CALL cp_fm_set_all(rtbse_env%hartree_curr(i), 0.0_dp)
     739              :       END DO
     740              :       ! AO-sized scratch buffers used by the linearized RT-BSE path
     741           72 :       IF (rtbse_env%linearized) THEN
     742           58 :          NULLIFY (rtbse_env%hartree_curr_ao)
     743           58 :          NULLIFY (rtbse_env%sigma_SEX_ao)
     744          240 :          ALLOCATE (rtbse_env%hartree_curr_ao(rtbse_env%n_spin))
     745          240 :          ALLOCATE (rtbse_env%sigma_SEX_ao(rtbse_env%n_spin))
     746          124 :          DO i = 1, rtbse_env%n_spin
     747           66 :             CALL cp_cfm_create(rtbse_env%sigma_SEX_ao(i), bs_env%fm_ks_Gamma(1)%matrix_struct)
     748           66 :             CALL cp_fm_create(rtbse_env%hartree_curr_ao(i), bs_env%fm_ks_Gamma(1)%matrix_struct)
     749           66 :             CALL cp_cfm_set_all(rtbse_env%sigma_SEX_ao(i), CMPLX(0.0, 0.0, kind=dp))
     750          124 :             CALL cp_fm_set_all(rtbse_env%hartree_curr_ao(i), 0.0_dp)
     751              :          END DO
     752              :          ! mo_active x mo_active real workspace pair for MO-side intermediates
     753           58 :          NULLIFY (rtbse_env%real_workspace_mo)
     754          174 :          ALLOCATE (rtbse_env%real_workspace_mo(2))
     755          174 :          DO i = 1, SIZE(rtbse_env%real_workspace_mo)
     756          116 :             CALL cp_fm_create(rtbse_env%real_workspace_mo(i), rtbse_env%fm_struct_mo_active)
     757          174 :             CALL cp_fm_set_all(rtbse_env%real_workspace_mo(i), 0.0_dp)
     758              :          END DO
     759           58 :          NULLIFY (rtbse_env%ao_mo_workspace)
     760          116 :          ALLOCATE (rtbse_env%ao_mo_workspace(1))
     761           58 :          CALL cp_fm_create(rtbse_env%ao_mo_workspace(1), rtbse_env%fm_struct_ao_mo_active)
     762           58 :          CALL cp_fm_set_all(rtbse_env%ao_mo_workspace(1), 0.0_dp)
     763              :          ! Truncated MO coefficient slabs C_active (n_ao x mo_active) for each spin.
     764              :          ! Filled in initialize_rtbse_env from bs_env%fm_mo_coeff_Gamma via submatrix copy.
     765           58 :          NULLIFY (rtbse_env%C_active)
     766          240 :          ALLOCATE (rtbse_env%C_active(rtbse_env%n_spin))
     767          124 :          DO i = 1, rtbse_env%n_spin
     768           66 :             CALL cp_fm_create(rtbse_env%C_active(i), rtbse_env%fm_struct_ao_mo_active)
     769          124 :             CALL cp_fm_set_all(rtbse_env%C_active(i), 0.0_dp)
     770              :          END DO
     771              :          ! Masked-copy staging scratch for the builder (all propagation paths including closed-shell ABBA).
     772              :          ! Also used as conjugate-transpose scratch in the TDA consumer.
     773           58 :          NULLIFY (rtbse_env%rho_delta_mo)
     774          240 :          ALLOCATE (rtbse_env%rho_delta_mo(rtbse_env%n_spin))
     775          124 :          DO i = 1, rtbse_env%n_spin
     776           66 :             CALL cp_cfm_create(rtbse_env%rho_delta_mo(i), rtbse_env%fm_struct_mo_active)
     777          124 :             CALL cp_cfm_set_all(rtbse_env%rho_delta_mo(i), CMPLX(0.0_dp, 0.0_dp, kind=dp))
     778              :          END DO
     779              :          ! Shared AO Hartree buffers: widened from (tda_active .OR. n_spin>1) so n_spin=1 ABBA
     780              :          ! (diagnostic + propagator) gets a dedicated buffer instead of aliasing sigma_SEX_ao.
     781           58 :          IF (.NOT. rtbse_env%debug_disable_hartree) THEN
     782           54 :             CALL cp_cfm_create(rtbse_env%rho_total_ao_scratch, bs_env%fm_s_Gamma%matrix_struct)
     783           54 :             CALL cp_cfm_create(rtbse_env%hartree_total_ao, bs_env%fm_ks_Gamma(1)%matrix_struct)
     784           54 :             CALL cp_cfm_set_all(rtbse_env%rho_total_ao_scratch, CMPLX(0.0_dp, 0.0_dp, kind=dp))
     785           54 :             CALL cp_cfm_set_all(rtbse_env%hartree_total_ao, CMPLX(0.0_dp, 0.0_dp, kind=dp))
     786              :          END IF
     787              :          ! Liouvillian-eigenvalue diagnostic state. n_spin=1 enforced upstream.
     788              :          ! Shared scratch (TDA + ABBA) allocated on diagnose_liouvillian_eig=T;
     789              :          ! ABBA-only A/B/A±B blocks added below under .NOT. tda_active. Mirror of the
     790              :          ! existing shared-scratch pattern, with the extra tda_active gate as the deviation.
     791           58 :          IF (rtbse_env%diagnose_liouvillian_eig) THEN
     792              :             ! Joint OV dimension: the spin blocks are stacked (n_spin=1 -> the old single-spin
     793              :             ! size). drho_probe/L_drho stay mo_active-sized per spin; L_pairs is N_OV_joint.
     794           58 :             n_ov = 0
     795          124 :             DO i = 1, rtbse_env%n_spin
     796              :                n_ov = n_ov + (rtbse_env%n_occ(i) - rtbse_env%first_active_mo + 1)* &
     797          124 :                       (rtbse_env%last_active_mo - rtbse_env%n_occ(i))
     798              :             END DO
     799           58 :             NULLIFY (rtbse_env%fm_struct_ov_pairs)
     800              :             CALL cp_fm_struct_create(rtbse_env%fm_struct_ov_pairs, &
     801              :                                      bs_env%fm_ks_Gamma(1)%matrix_struct%para_env, &
     802              :                                      bs_env%fm_ks_Gamma(1)%matrix_struct%context, &
     803           58 :                                      n_ov, n_ov)
     804          240 :             ALLOCATE (rtbse_env%drho_probe(rtbse_env%n_spin))
     805          240 :             ALLOCATE (rtbse_env%L_drho(rtbse_env%n_spin))
     806          124 :             DO i = 1, rtbse_env%n_spin
     807           66 :                CALL cp_cfm_create(rtbse_env%drho_probe(i), rtbse_env%fm_struct_mo_active)
     808           66 :                CALL cp_cfm_create(rtbse_env%L_drho(i), rtbse_env%fm_struct_mo_active)
     809           66 :                CALL cp_cfm_set_all(rtbse_env%drho_probe(i), CMPLX(0.0_dp, 0.0_dp, kind=dp))
     810          124 :                CALL cp_cfm_set_all(rtbse_env%L_drho(i), CMPLX(0.0_dp, 0.0_dp, kind=dp))
     811              :             END DO
     812           58 :             CALL cp_cfm_create(rtbse_env%L_pairs, rtbse_env%fm_struct_ov_pairs)
     813           58 :             CALL cp_cfm_create(rtbse_env%eigvecs_pairs, rtbse_env%fm_struct_ov_pairs)
     814           58 :             CALL cp_cfm_set_all(rtbse_env%L_pairs, CMPLX(0.0_dp, 0.0_dp, kind=dp))
     815           58 :             CALL cp_cfm_set_all(rtbse_env%eigvecs_pairs, CMPLX(0.0_dp, 0.0_dp, kind=dp))
     816           58 :             NULLIFY (rtbse_env%eigenvalues_liouvillian)
     817          174 :             ALLOCATE (rtbse_env%eigenvalues_liouvillian(n_ov))
     818          772 :             rtbse_env%eigenvalues_liouvillian = 0.0_dp
     819              :             ! ABBA-only Furche-reduction scratch (A, B, A-B->sqrt, A+B).
     820           58 :             IF (.NOT. rtbse_env%tda_active) THEN
     821           18 :                CALL cp_cfm_create(rtbse_env%A_mat, rtbse_env%fm_struct_ov_pairs)
     822           18 :                CALL cp_cfm_create(rtbse_env%B_mat, rtbse_env%fm_struct_ov_pairs)
     823           18 :                CALL cp_cfm_create(rtbse_env%AmB_scratch, rtbse_env%fm_struct_ov_pairs)
     824           18 :                CALL cp_cfm_create(rtbse_env%ApB_scratch, rtbse_env%fm_struct_ov_pairs)
     825           18 :                CALL cp_cfm_set_all(rtbse_env%A_mat, CMPLX(0.0_dp, 0.0_dp, kind=dp))
     826           18 :                CALL cp_cfm_set_all(rtbse_env%B_mat, CMPLX(0.0_dp, 0.0_dp, kind=dp))
     827           18 :                CALL cp_cfm_set_all(rtbse_env%AmB_scratch, CMPLX(0.0_dp, 0.0_dp, kind=dp))
     828           18 :                CALL cp_cfm_set_all(rtbse_env%ApB_scratch, CMPLX(0.0_dp, 0.0_dp, kind=dp))
     829              :             END IF
     830              :          END IF
     831              :       END IF
     832              : 
     833              :       ! Allocate workspaces for get_sigma
     834           72 :       CALL create_sigma_workspace(rtbse_env)
     835              : 
     836              :       ! Depending on the chosen methods, allocate extra workspace
     837           72 :       CALL create_hartree_ri_workspace(rtbse_env)
     838              : 
     839           72 :    END SUBROUTINE create_rtbse_env
     840              : 
     841              : ! **************************************************************************************************
     842              : !> \brief Simple reimplementation of cp_fm_release_pp1 for complex matrices
     843              : !> \param matrices cp_cfm_type(:)
     844              : !> \author Stepan Marek
     845              : !> \date 02.2024
     846              : ! **************************************************************************************************
     847         1198 :    SUBROUTINE cp_cfm_release_pa1(matrices)
     848              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER                  :: matrices
     849              :       INTEGER                                                   :: i
     850              : 
     851         2740 :       DO i = 1, SIZE(matrices)
     852         2740 :          CALL cp_cfm_release(matrices(i))
     853              :       END DO
     854         1198 :       DEALLOCATE (matrices)
     855              :       NULLIFY (matrices)
     856         1198 :    END SUBROUTINE cp_cfm_release_pa1
     857              : 
     858              : ! **************************************************************************************************
     859              : !> \brief Releases the environment allocated structures
     860              : !> \param rtbse_env
     861              : !> \author Stepan Marek
     862              : !> \date 02.2024
     863              : ! **************************************************************************************************
     864           72 :    SUBROUTINE release_rtbse_env(rtbse_env)
     865              :       TYPE(rtbse_env_type), POINTER                             :: rtbse_env
     866              : 
     867           72 :       CALL cp_cfm_release_pa1(rtbse_env%ham_effective)
     868           72 :       CALL cp_cfm_release_pa1(rtbse_env%ham_workspace)
     869           72 :       CALL cp_fm_release(rtbse_env%sigma_COH)
     870           72 :       CALL cp_cfm_release_pa1(rtbse_env%sigma_SEX)
     871           72 :       CALL cp_fm_release(rtbse_env%hartree_curr)
     872           72 :       CALL cp_cfm_release_pa1(rtbse_env%ham_reference)
     873           72 :       IF (ASSOCIATED(rtbse_env%ham_reference_singleparticle)) THEN
     874           58 :          CALL cp_cfm_release_pa1(rtbse_env%ham_reference_singleparticle)
     875              :       END IF
     876           72 :       IF (ASSOCIATED(rtbse_env%eps_active)) DEALLOCATE (rtbse_env%eps_active)
     877           72 :       IF (ASSOCIATED(rtbse_env%eps_active_restart)) DEALLOCATE (rtbse_env%eps_active_restart)
     878           72 :       CALL cp_cfm_release_pa1(rtbse_env%rho)
     879           72 :       CALL cp_cfm_release_pa1(rtbse_env%rho_workspace)
     880           72 :       CALL cp_cfm_release_pa1(rtbse_env%rho_new)
     881           72 :       CALL cp_cfm_release_pa1(rtbse_env%rho_new_last)
     882           72 :       CALL cp_cfm_release_pa1(rtbse_env%rho_M)
     883           72 :       CALL cp_cfm_release_pa1(rtbse_env%rho_orig)
     884           72 :       IF (ASSOCIATED(rtbse_env%rk4_coefficients)) THEN
     885           58 :          CALL cp_cfm_release_pa1(rtbse_env%rk4_coefficients)
     886              :       END IF
     887           72 :       CALL cp_fm_release(rtbse_env%real_workspace)
     888           72 :       IF (ASSOCIATED(rtbse_env%sigma_complex_workspace)) CALL cp_cfm_release_pa1(rtbse_env%sigma_complex_workspace)
     889           72 :       CALL cp_fm_release(rtbse_env%S_inv_fm)
     890           72 :       CALL cp_fm_release(rtbse_env%S_fm)
     891           72 :       CALL cp_cfm_release(rtbse_env%S_cfm)
     892              : 
     893           72 :       CALL cp_fm_release(rtbse_env%moments)
     894           72 :       CALL cp_fm_release(rtbse_env%moments_field)
     895              : 
     896           72 :       CALL release_sigma_workspace(rtbse_env)
     897              : 
     898           72 :       CALL release_hartree_ri_workspace(rtbse_env)
     899              : 
     900           72 :       DEALLOCATE (rtbse_env%real_eigvals)
     901           72 :       DEALLOCATE (rtbse_env%exp_eigvals)
     902           72 :       DEALLOCATE (rtbse_env%moments_trace)
     903           72 :       DEALLOCATE (rtbse_env%field_trace)
     904           72 :       DEALLOCATE (rtbse_env%time_trace)
     905              : 
     906           72 :       IF (ASSOCIATED(rtbse_env%pol_elements)) DEALLOCATE (rtbse_env%pol_elements)
     907           72 :       IF (ASSOCIATED(rtbse_env%pade_x_eval)) DEALLOCATE (rtbse_env%pade_x_eval)
     908              : 
     909              :       ! Deallocate the neighbour list that is not deallocated in gw anymore
     910           72 :       IF (ASSOCIATED(rtbse_env%bs_env%nl_3c%ij_list)) CALL neighbor_list_3c_destroy(rtbse_env%bs_env%nl_3c)
     911              :       ! Release linearized-only AO scratches and MO-side workspaces
     912           72 :       IF (ASSOCIATED(rtbse_env%rho_ao_scratch)) CALL cp_cfm_release_pa1(rtbse_env%rho_ao_scratch)
     913           72 :       IF (ASSOCIATED(rtbse_env%sigma_SEX_ao)) CALL cp_cfm_release_pa1(rtbse_env%sigma_SEX_ao)
     914           72 :       IF (ASSOCIATED(rtbse_env%hartree_curr_ao)) CALL cp_fm_release(rtbse_env%hartree_curr_ao)
     915           72 :       IF (ASSOCIATED(rtbse_env%real_workspace_mo)) CALL cp_fm_release(rtbse_env%real_workspace_mo)
     916           72 :       IF (ASSOCIATED(rtbse_env%ao_mo_workspace)) CALL cp_fm_release(rtbse_env%ao_mo_workspace)
     917           72 :       IF (ASSOCIATED(rtbse_env%C_active)) CALL cp_fm_release(rtbse_env%C_active)
     918           72 :       IF (ASSOCIATED(rtbse_env%rho_delta_mo)) CALL cp_cfm_release_pa1(rtbse_env%rho_delta_mo)
     919              :       ! Release shared bare-Hartree scratch. Mirror the alloc gate exactly (linearized .AND.
     920              :       ! .NOT. debug_disable_hartree, every shell incl closed-shell ABBA) — the old
     921              :       ! (tda_active .OR. n_spin>1) gate leaked both buffers on the closed-shell ABBA path.
     922           72 :       IF (rtbse_env%linearized .AND. .NOT. rtbse_env%debug_disable_hartree) THEN
     923           54 :          CALL cp_cfm_release(rtbse_env%rho_total_ao_scratch)
     924           54 :          CALL cp_cfm_release(rtbse_env%hartree_total_ao)
     925              :       END IF
     926              :       ! Release the RI-RS Hartree diagonal-reuse accumulators (allocated in initialize_hartree_potential).
     927           72 :       IF (ALLOCATED(rtbse_env%hartree_diag_re)) DEALLOCATE (rtbse_env%hartree_diag_re)
     928           72 :       IF (ALLOCATED(rtbse_env%hartree_diag_im)) DEALLOCATE (rtbse_env%hartree_diag_im)
     929              :       ! Release Liouvillian-diagnostic scratch (only when the diagnostic was requested).
     930           72 :       IF (rtbse_env%diagnose_liouvillian_eig) THEN
     931           58 :          IF (ASSOCIATED(rtbse_env%drho_probe)) CALL cp_cfm_release_pa1(rtbse_env%drho_probe)
     932           58 :          IF (ASSOCIATED(rtbse_env%L_drho)) CALL cp_cfm_release_pa1(rtbse_env%L_drho)
     933           58 :          CALL cp_cfm_release(rtbse_env%L_pairs)
     934           58 :          CALL cp_cfm_release(rtbse_env%eigvecs_pairs)
     935           58 :          IF (ASSOCIATED(rtbse_env%eigenvalues_liouvillian)) DEALLOCATE (rtbse_env%eigenvalues_liouvillian)
     936           58 :          IF (.NOT. rtbse_env%tda_active) THEN
     937           18 :             CALL cp_cfm_release(rtbse_env%A_mat)
     938           18 :             CALL cp_cfm_release(rtbse_env%B_mat)
     939           18 :             CALL cp_cfm_release(rtbse_env%AmB_scratch)
     940           18 :             CALL cp_cfm_release(rtbse_env%ApB_scratch)
     941              :          END IF
     942           58 :          IF (ASSOCIATED(rtbse_env%fm_struct_ov_pairs)) THEN
     943           58 :             CALL cp_fm_struct_release(rtbse_env%fm_struct_ov_pairs)
     944              :          END IF
     945              :       END IF
     946              :       ! Release owned active-MO matrix structures
     947           72 :       IF (ASSOCIATED(rtbse_env%fm_struct_mo_active)) THEN
     948           72 :          CALL cp_fm_struct_release(rtbse_env%fm_struct_mo_active)
     949              :       END IF
     950           72 :       IF (ASSOCIATED(rtbse_env%fm_struct_ao_mo_active)) THEN
     951           72 :          CALL cp_fm_struct_release(rtbse_env%fm_struct_ao_mo_active)
     952              :       END IF
     953              :       ! Deallocate the storage for the environment itself
     954           72 :       DEALLOCATE (rtbse_env)
     955              :       ! Nullify to make sure it is not used again
     956              :       NULLIFY (rtbse_env)
     957              : 
     958           72 :    END SUBROUTINE release_rtbse_env
     959              : 
     960              : ! **************************************************************************************************
     961              : !> \brief Abort if the quasiparticle spectrum handed to the propagator is inverted or has diverged.
     962              : !>
     963              : !> Tests the fundamental gap per spin channel - not E(HOMO+1) - E(HOMO), since G0W0 reorders levels -
     964              : !> on the very array the propagator consumes. Under RTBSE_HAMILTONIAN KS the quasiparticle energies
     965              : !> never enter the propagator, so a broken G0W0 spectrum is irrelevant there and does not abort.
     966              : !> \param rtbse_env RT-BSE environment with n_ao, n_occ, n_spin, ham_reference_type populated.
     967              : !> \param bs_env Bandstructure environment providing the eigenvalues.
     968              : ! **************************************************************************************************
     969           72 :    SUBROUTINE check_qp_gap_sanity(rtbse_env, bs_env)
     970              :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     971              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
     972              : 
     973              :       INTEGER                                            :: homo, ispin
     974              :       REAL(KIND=dp)                                      :: gap, gap_scf
     975              : 
     976           72 :       IF (rtbse_env%ham_reference_type /= rtp_bse_ham_gw) RETURN
     977              : 
     978          152 :       DO ispin = 1, rtbse_env%n_spin
     979           80 :          homo = rtbse_env%n_occ(ispin)
     980           80 :          IF (homo < 1 .OR. homo >= rtbse_env%n_ao) CYCLE
     981              : 
     982              :          gap = MINVAL(bs_env%eigenval_GW(homo + 1:rtbse_env%n_ao, 1, ispin)) - &
     983          900 :                MAXVAL(bs_env%eigenval_GW(1:homo, 1, ispin))
     984              :          gap_scf = MINVAL(bs_env%eigenval_scf_Gamma(homo + 1:rtbse_env%n_ao, ispin)) - &
     985          900 :                    MAXVAL(bs_env%eigenval_scf_Gamma(1:homo, ispin))
     986              : 
     987              :          ! requiring a healthy SCF gap keeps the inversion test from firing on a genuine metal
     988          152 :          IF (gap < -eps_qp_gap .AND. gap_scf > eps_qp_gap) THEN
     989              :             CALL cp_abort(__LOCATION__, &
     990              :                           "RTBSE: G0W0 gap of spin "//TRIM(ADJUSTL(cp_to_string(ispin)))// &
     991              :                           " is negative ("//TRIM(ADJUSTL(cp_to_string(gap*evolt, '(F12.3)')))// &
     992              :                           " eV): propagating an inverted spectrum is meaningless. Check the GW "// &
     993            0 :                           "numerical parameters, or use RTBSE_HAMILTONIAN KS.")
     994           80 :          ELSE IF (ABS(gap) > max_qp_gap) THEN
     995              :             CALL cp_abort(__LOCATION__, &
     996              :                           "RTBSE: G0W0 gap of spin "//TRIM(ADJUSTL(cp_to_string(ispin)))// &
     997              :                           " is implausibly large ("// &
     998              :                           TRIM(ADJUSTL(cp_to_string(gap*evolt, '(F12.3)')))//" eV): the GW step "// &
     999              :                           "has likely diverged. Check the GW numerical parameters, or use "// &
    1000            0 :                           "RTBSE_HAMILTONIAN KS.")
    1001              :          END IF
    1002              :       END DO
    1003              : 
    1004              :    END SUBROUTINE check_qp_gap_sanity
    1005              : 
    1006              : ! **************************************************************************************************
    1007              : !> \brief Determine the combined active MO window for linearized RT-BSE truncation.
    1008              : !>
    1009              : !> Evaluates BSE-like cutoff indices per spin from the requested single-particle spectrum
    1010              : !> (G0W0 or KS Gamma-point eigenvalues) and collapses them into a single combined window
    1011              : !> covering both spin channels by choosing the most inclusive bounds. Issues a CPWARN if the
    1012              : !> spin-resolved cutoff candidates differ. When cutoffs are disabled (or the run is not
    1013              : !> linearized RT-BSE), the window is set to the full MO range.
    1014              : !> \param rtbse_env RT-BSE environment with cutoff values, n_ao, n_occ, n_spin, ham_reference_type
    1015              : !>                  already populated.
    1016              : !> \param bs_env Bandstructure environment providing the eigenvalues.
    1017              : ! **************************************************************************************************
    1018           72 :    SUBROUTINE determine_active_mo_window(rtbse_env, bs_env)
    1019              :       TYPE(rtbse_env_type), POINTER                       :: rtbse_env
    1020              :       TYPE(post_scf_bandstructure_type), POINTER          :: bs_env
    1021              :       CHARACTER(LEN=*), PARAMETER                         :: routineN = "determine_active_mo_window"
    1022              : 
    1023              :       INTEGER                                             :: handle, ispin, n_ao_full, n_virt
    1024              :       INTEGER                                             :: homo_red, virt_red, homo_incl, virt_incl
    1025              :       INTEGER                                             :: combined_first_occ, combined_last_virt
    1026              :       INTEGER                                             :: first_occ_prev, last_virt_prev
    1027              :       LOGICAL                                             :: spins_differ, do_truncation
    1028              :       REAL(KIND=dp)                                       :: cutoff_occ, cutoff_empty
    1029              : 
    1030           72 :       CALL timeset(routineN, handle)
    1031              : 
    1032           72 :       n_ao_full = rtbse_env%n_ao
    1033           72 :       cutoff_occ = rtbse_env%rtbse_energy_cutoff_occ
    1034           72 :       cutoff_empty = rtbse_env%rtbse_energy_cutoff_empty
    1035           72 :       do_truncation = rtbse_env%linearized .AND. (cutoff_occ > 0.0_dp .OR. cutoff_empty > 0.0_dp)
    1036              : 
    1037              :       ! Default: full MO window
    1038           72 :       rtbse_env%first_active_mo = 1
    1039           72 :       rtbse_env%last_active_mo = n_ao_full
    1040           72 :       rtbse_env%mo_active = n_ao_full
    1041           72 :       rtbse_env%active_mo_truncation = .FALSE.
    1042              : 
    1043           72 :       IF (.NOT. do_truncation) THEN
    1044           70 :          CALL timestop(handle)
    1045           70 :          RETURN
    1046              :       END IF
    1047              : 
    1048            2 :       combined_first_occ = n_ao_full
    1049            2 :       combined_last_virt = 1
    1050            2 :       first_occ_prev = -1
    1051            2 :       last_virt_prev = -1
    1052            2 :       spins_differ = .FALSE.
    1053              : 
    1054            4 :       DO ispin = 1, rtbse_env%n_spin
    1055            2 :          n_virt = n_ao_full - rtbse_env%n_occ(ispin)
    1056              :          ! Cut on the DFT axis, as LRBSE does: it is ascending by construction, so the window is a
    1057              :          ! well-defined contiguous MO range, which is all C_active can extract. The G0W0 axis is
    1058              :          ! not ordered.
    1059              :          CALL determine_cutoff_indices(bs_env%eigenval_scf_Gamma(:, ispin), &
    1060              :                                        rtbse_env%n_occ(ispin), n_virt, &
    1061              :                                        homo_red, virt_red, homo_incl, virt_incl, &
    1062            2 :                                        cutoff_occ, cutoff_empty)
    1063              :          ! Translate the per-spin candidate to global MO indices [homo_incl, homo + virt_incl]
    1064            2 :          IF (ispin > 1) THEN
    1065            0 :             IF (homo_incl /= first_occ_prev .OR. (rtbse_env%n_occ(ispin) + virt_incl) /= last_virt_prev) THEN
    1066            0 :                spins_differ = .TRUE.
    1067              :             END IF
    1068              :          END IF
    1069            2 :          first_occ_prev = homo_incl
    1070            2 :          last_virt_prev = rtbse_env%n_occ(ispin) + virt_incl
    1071            2 :          combined_first_occ = MIN(combined_first_occ, homo_incl)
    1072            4 :          combined_last_virt = MAX(combined_last_virt, rtbse_env%n_occ(ispin) + virt_incl)
    1073              :       END DO
    1074              : 
    1075            2 :       IF (spins_differ) THEN
    1076            0 :          CPWARN("RTBSE: spin-resolved active MO cutoff candidates differ; using combined window.")
    1077              :       END IF
    1078              : 
    1079            2 :       rtbse_env%first_active_mo = combined_first_occ
    1080            2 :       rtbse_env%last_active_mo = combined_last_virt
    1081            2 :       rtbse_env%mo_active = combined_last_virt - combined_first_occ + 1
    1082            2 :       rtbse_env%active_mo_truncation = (rtbse_env%mo_active < n_ao_full)
    1083              : 
    1084            2 :       CALL timestop(handle)
    1085              :    END SUBROUTINE determine_active_mo_window
    1086              : 
    1087              : ! **************************************************************************************************
    1088              : !> \brief Allocates the workspaces for Hartree RI method
    1089              : !> \note RI method calculates the Hartree contraction without the use of DBT, as it cannot emulate vectors
    1090              : !> \param rtbse_env
    1091              : !> \author Stepan Marek
    1092              : !> \date 05.2024
    1093              : ! **************************************************************************************************
    1094           72 :    SUBROUTINE create_hartree_ri_workspace(rtbse_env)
    1095              :       TYPE(rtbse_env_type)                              :: rtbse_env
    1096              :       TYPE(post_scf_bandstructure_type), POINTER        :: bs_env
    1097              : 
    1098              :       ! Skip the AO-RI Hartree scratch when the RT-BSE Hartree path is fully RI-RS.
    1099              :       ! In that case rho_dbcsr / v_ao_dbcsr / int_3c_array are never read.
    1100              :       ! get_sigma_real (AO-RI SX) used to borrow rho_dbcsr as a workspace; that
    1101              :       ! cross-dependency was removed by giving get_sigma_real its own local
    1102              :       ! dbcsr scratch (see rt_bse.F::get_sigma_real). rho_dbcsr is now AO-RI
    1103              :       ! Hartree only, as its name suggests.
    1104           72 :       IF (rtbse_env%rirs_kernel) RETURN
    1105              : 
    1106           50 :       CALL get_qs_env(rtbse_env%qs_env, bs_env=bs_env)
    1107              : 
    1108           50 :       CALL dbcsr_create(rtbse_env%rho_dbcsr, name="Sparse density", template=bs_env%mat_ao_ao%matrix)
    1109           50 :       CALL dbcsr_create(rtbse_env%v_ao_dbcsr, name="Sparse Hartree", template=bs_env%mat_ao_ao%matrix)
    1110              : 
    1111              :       CALL create_hartree_ri_3c(rtbse_env%rho_dbcsr, rtbse_env%int_3c_array, rtbse_env%n_ao, rtbse_env%n_RI, &
    1112              :                                 bs_env%basis_set_AO, bs_env%basis_set_RI, bs_env%i_RI_start_from_atom, &
    1113           50 :                                 bs_env%ri_metric, rtbse_env%qs_env, rtbse_env%unit_nr)
    1114              :    END SUBROUTINE create_hartree_ri_workspace
    1115              : ! **************************************************************************************************
    1116              : !> \brief Separated method for allocating the 3c integrals for RI Hartree
    1117              : !> \note RI method calculates the Hartree contraction without the use of DBT, as it cannot emulate vectors
    1118              : !> \param rho_dbcsr matrix used for the description of shape of 3c array
    1119              : !> \param int_3c 3-center integral array to be allocated and filled
    1120              : !> \param n_ao Number of atomic orbitals
    1121              : !> \param n_RI Number of auxiliary RI orbitals
    1122              : !> \param basis_set_AO AO basis set
    1123              : !> \param basis_set_RI RI auxiliary basis set
    1124              : !> \param i_RI_start_from_atom Array of indices where functions of a given atom in RI basis start
    1125              : !> \param unit_nr Unit number used for printing information about the size of int_3c
    1126              : !> \author Stepan Marek
    1127              : !> \date 01.2025
    1128              : ! **************************************************************************************************
    1129           50 :    SUBROUTINE create_hartree_ri_3c(rho_dbcsr, int_3c, n_ao, n_RI, basis_set_AO, basis_set_RI, &
    1130           50 :                                    i_RI_start_from_atom, ri_metric, qs_env, unit_nr)
    1131              :       TYPE(dbcsr_type)                                  :: rho_dbcsr
    1132              :       REAL(kind=dp), DIMENSION(:, :, :), POINTER          :: int_3c
    1133              :       INTEGER                                           :: n_ao, n_RI
    1134              :       TYPE(gto_basis_set_p_type), DIMENSION(:)          :: basis_set_AO, &
    1135              :                                                            basis_set_RI
    1136              :       INTEGER, DIMENSION(:)                             :: i_RI_start_from_atom
    1137              :       TYPE(libint_potential_type)                       :: ri_metric
    1138              :       TYPE(qs_environment_type), POINTER                :: qs_env
    1139              :       INTEGER                                           :: unit_nr
    1140              :       REAL(kind=dp)                                     :: size_mb
    1141              :       INTEGER                                           :: nblkrows_local, &
    1142              :                                                            nblkcols_local, &
    1143              :                                                            i_blk_local, &
    1144              :                                                            j_blk_local, &
    1145              :                                                            nrows_local, &
    1146              :                                                            ncols_local, &
    1147              :                                                            col_local_offset, &
    1148              :                                                            row_local_offset, &
    1149              :                                                            start_col_index, &
    1150              :                                                            end_col_index, &
    1151              :                                                            start_row_index, &
    1152              :                                                            end_row_index
    1153           50 :       INTEGER, DIMENSION(:), POINTER                    :: local_blk_rows, &
    1154           50 :                                                            local_blk_cols, &
    1155           50 :                                                            row_blk_size, &
    1156           50 :                                                            col_blk_size
    1157              :       ! TODO : Implement option/decision to not precompute all the 3c integrals
    1158              :       size_mb = REAL(n_ao, kind=dp)*REAL(n_ao, kind=dp)*REAL(n_RI, kind=dp)* &
    1159           50 :                 REAL(STORAGE_SIZE(size_mb), kind=dp)/8.0_dp/1024.0_dp/1024.0_dp
    1160           50 :       IF (unit_nr > 0) WRITE (unit_nr, '(A44,E32.2E3,A4)') &
    1161           25 :          " RTBSE| Approximate size of the 3c integrals", size_mb, " MiB"
    1162              : 
    1163              :       ! Get the number of block rows and columns
    1164           50 :       CALL dbcsr_get_info(rho_dbcsr, nblkrows_local=nblkrows_local, nblkcols_local=nblkcols_local)
    1165              :       ! Get the global indices of local rows and columns
    1166           50 :       CALL dbcsr_get_info(rho_dbcsr, local_rows=local_blk_rows, local_cols=local_blk_cols)
    1167              :       ! Get the sizes of all blocks
    1168           50 :       CALL dbcsr_get_info(rho_dbcsr, row_blk_size=row_blk_size, col_blk_size=col_blk_size)
    1169              : 
    1170              :       ! Get the total required local rows and cols
    1171           50 :       nrows_local = 0
    1172          100 :       DO i_blk_local = 1, nblkrows_local
    1173          100 :          nrows_local = nrows_local + row_blk_size(local_blk_rows(i_blk_local))
    1174              :       END DO
    1175           50 :       ncols_local = 0
    1176          150 :       DO j_blk_local = 1, nblkcols_local
    1177          150 :          ncols_local = ncols_local + col_blk_size(local_blk_cols(j_blk_local))
    1178              :       END DO
    1179              : 
    1180              :       ! Allocate the appropriate storage
    1181          250 :       ALLOCATE (int_3c(nrows_local, ncols_local, n_RI))
    1182              : 
    1183              :       ! Fill the storage with appropriate values, block by block
    1184           50 :       row_local_offset = 1
    1185          100 :       DO i_blk_local = 1, nblkrows_local
    1186              :          col_local_offset = 1
    1187          150 :          DO j_blk_local = 1, nblkcols_local
    1188          100 :             start_row_index = row_local_offset
    1189          100 :             end_row_index = start_row_index + row_blk_size(local_blk_rows(i_blk_local)) - 1
    1190          100 :             start_col_index = col_local_offset
    1191          100 :             end_col_index = start_col_index + col_blk_size(local_blk_cols(j_blk_local)) - 1
    1192              :             CALL build_3c_integral_block(int_3c(start_row_index:end_row_index, &
    1193              :                                                 start_col_index:end_col_index, &
    1194              :                                                 1:n_RI), &
    1195              :                                          qs_env, potential_parameter=ri_metric, &
    1196              :                                          basis_j=basis_set_AO, basis_k=basis_set_AO, &
    1197              :                                          basis_i=basis_set_RI, &
    1198              :                                          atom_j=local_blk_rows(i_blk_local), &
    1199              :                                          atom_k=local_blk_cols(j_blk_local), &
    1200          100 :                                          i_bf_start_from_atom=i_RI_start_from_atom)
    1201          150 :             col_local_offset = col_local_offset + col_blk_size(local_blk_cols(j_blk_local))
    1202              :          END DO
    1203          100 :          row_local_offset = row_local_offset + row_blk_size(local_blk_rows(i_blk_local))
    1204              :       END DO
    1205           75 :    END SUBROUTINE create_hartree_ri_3c
    1206              : ! **************************************************************************************************
    1207              : !> \brief Releases the workspace for the Hartree RI method
    1208              : !> \param rtbse_env RT-BSE Environment, containing specific RI Hartree storage
    1209              : !> \author Stepan Marek
    1210              : !> \date 09.2024
    1211              : ! **************************************************************************************************
    1212           72 :    SUBROUTINE release_hartree_ri_workspace(rtbse_env)
    1213              :       TYPE(rtbse_env_type)                              :: rtbse_env
    1214              : 
    1215              :       ! Mirror the gate in create_hartree_ri_workspace and the v_dbcsr gate in
    1216              :       ! initialize_hartree_potential. With one KERNEL_RI switch the AO-RI Hartree
    1217              :       ! scratch (3c integrals + dbcsr work + v_dbcsr) is created iff `.NOT. rirs_kernel`.
    1218           72 :       IF (.NOT. rtbse_env%rirs_kernel) THEN
    1219           50 :          DEALLOCATE (rtbse_env%int_3c_array)
    1220           50 :          CALL dbcsr_release(rtbse_env%rho_dbcsr)
    1221           50 :          CALL dbcsr_release(rtbse_env%v_ao_dbcsr)
    1222           50 :          CALL dbcsr_release(rtbse_env%v_dbcsr)
    1223              :       END IF
    1224           72 :    END SUBROUTINE release_hartree_ri_workspace
    1225              : ! **************************************************************************************************
    1226              : !> \brief Allocates the workspaces for self-energy determination routine
    1227              : !> \param rtbse_env Structure for holding information and workspace structures
    1228              : !> \author Stepan Marek
    1229              : !> \date 02.2024
    1230              : ! **************************************************************************************************
    1231           72 :    SUBROUTINE create_sigma_workspace(rtbse_env)
    1232              :       TYPE(rtbse_env_type)                               :: rtbse_env
    1233              : 
    1234              :       ! Skip the AO-RI sigma scratch (W matrix + 3c integrals + work tensors)
    1235              :       ! when the RT-BSE SEX path is fully RI-RS — these workspaces are read
    1236              :       ! only by get_sigma in the `.NOT. rirs_kernel` branches.
    1237           72 :       IF (rtbse_env%rirs_kernel) RETURN
    1238              : 
    1239              :       CALL create_sigma_workspace_qs_only(rtbse_env%qs_env, rtbse_env%screened_dbt, rtbse_env%w_dbcsr, &
    1240              :                                           rtbse_env%t_3c_w, rtbse_env%t_3c_work_RI_AO__AO, &
    1241           50 :                                           rtbse_env%t_3c_work2_RI_AO__AO, rtbse_env%greens_dbt)
    1242              :    END SUBROUTINE create_sigma_workspace
    1243              : ! **************************************************************************************************
    1244              : !> \brief Allocates the workspaces for self-energy determination routine
    1245              : !> \note Does so without referencing the rtbse_env
    1246              : !> \note References bs_env
    1247              : !> \param rtbse_env Structure for holding information and workspace structures
    1248              : !> \param qs_env Quickstep environment - entry point of calculation
    1249              : !> \author Stepan Marek
    1250              : !> \date 02.2024
    1251              : ! **************************************************************************************************
    1252           50 :    SUBROUTINE create_sigma_workspace_qs_only(qs_env, screened_dbt, screened_dbcsr, int_3c_dbt, &
    1253              :                                              work_dbt_3c_1, work_dbt_3c_2, work_dbt_2c)
    1254              :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1255              :       TYPE(dbcsr_type)                                   :: screened_dbcsr
    1256              :       TYPE(dbt_type)                                     :: screened_dbt, &
    1257              :                                                             int_3c_dbt, &
    1258              :                                                             work_dbt_3c_1, &
    1259              :                                                             work_dbt_3c_2, &
    1260              :                                                             work_dbt_2c
    1261              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
    1262              : 
    1263           50 :       CALL get_qs_env(qs_env, bs_env=bs_env)
    1264              : 
    1265              :       ! t_3c_w
    1266           50 :       CALL dbt_create(bs_env%t_RI__AO_AO, int_3c_dbt)
    1267              :       ! TODO : Provide option/decision whether to store the 3c integrals precomputed
    1268           50 :       CALL compute_3c_integrals(qs_env, bs_env, int_3c_dbt)
    1269              :       ! t_3c_work_RI_AO__AO
    1270           50 :       CALL dbt_create(bs_env%t_RI_AO__AO, work_dbt_3c_1)
    1271              :       ! t_3c_work2_RI_AO__AO
    1272           50 :       CALL dbt_create(bs_env%t_RI_AO__AO, work_dbt_3c_2)
    1273              :       ! t_W
    1274              :       ! Populate screened_dbt from gw run
    1275           50 :       CALL dbcsr_create(screened_dbcsr, name="W", template=bs_env%mat_RI_RI%matrix)
    1276           50 :       CALL dbt_create(screened_dbcsr, screened_dbt)
    1277              :       ! greens_dbt
    1278           50 :       CALL dbt_create(bs_env%mat_ao_ao%matrix, work_dbt_2c)
    1279           50 :    END SUBROUTINE create_sigma_workspace_qs_only
    1280              : ! **************************************************************************************************
    1281              : !> \brief Releases the workspaces for self-energy determination
    1282              : !> \param rtbse_env
    1283              : !> \author Stepan Marek
    1284              : !> \date 02.2024
    1285              : ! **************************************************************************************************
    1286           72 :    SUBROUTINE release_sigma_workspace(rtbse_env)
    1287              :       TYPE(rtbse_env_type)                               :: rtbse_env
    1288              : 
    1289              :       ! Mirror the gate in create_sigma_workspace.
    1290           72 :       IF (rtbse_env%rirs_kernel) RETURN
    1291              : 
    1292           50 :       CALL dbt_destroy(rtbse_env%t_3c_w)
    1293           50 :       CALL dbt_destroy(rtbse_env%t_3c_work_RI_AO__AO)
    1294           50 :       CALL dbt_destroy(rtbse_env%t_3c_work2_RI_AO__AO)
    1295           50 :       CALL dbt_destroy(rtbse_env%screened_dbt)
    1296           50 :       CALL dbt_destroy(rtbse_env%greens_dbt)
    1297           50 :       CALL dbcsr_release(rtbse_env%w_dbcsr)
    1298              :    END SUBROUTINE release_sigma_workspace
    1299              : ! **************************************************************************************************
    1300              : !> \brief Multiplies real matrix by a complex matrix from the right
    1301              : !> \note So far only converts the real matrix to complex one, potentially doubling the work
    1302              : !> \param rtbse_env
    1303              : !> \author Stepan Marek
    1304              : !> \date 09.2024
    1305              : ! **************************************************************************************************
    1306        15064 :    SUBROUTINE multiply_fm_cfm(trans_r, trans_c, na, nb, nc, &
    1307              :                               alpha, matrix_r, matrix_c, beta, res)
    1308              :       ! Transposition
    1309              :       CHARACTER(len=1)                                   :: trans_r, trans_c
    1310              :       INTEGER                                            :: na, nb, nc
    1311              :       ! accept real numbers
    1312              :       ! TODO : Just use complex numbers and import z_one, z_zero etc.
    1313              :       REAL(kind=dp)                                      :: alpha, beta
    1314              :       TYPE(cp_fm_type)                                   :: matrix_r
    1315              :       TYPE(cp_cfm_type)                                  :: matrix_c, res
    1316              :       TYPE(cp_fm_type)                                   :: work_re, work_im, res_re, res_im
    1317              :       REAL(kind=dp)                                      :: i_unit
    1318              :       CHARACTER(len=1)                                   :: trans_cr
    1319              : 
    1320         3766 :       CALL cp_fm_create(work_re, matrix_c%matrix_struct)
    1321         3766 :       CALL cp_fm_create(work_im, matrix_c%matrix_struct)
    1322         3766 :       CALL cp_fm_create(res_re, res%matrix_struct)
    1323         3766 :       CALL cp_fm_create(res_im, res%matrix_struct)
    1324         3766 :       CALL cp_cfm_to_fm(matrix_c, work_re, work_im)
    1325            0 :       SELECT CASE (trans_c)
    1326              :       CASE ("C")
    1327            0 :          i_unit = -1.0_dp
    1328            0 :          trans_cr = "T"
    1329              :       CASE ("T")
    1330            0 :          i_unit = 1.0_dp
    1331            0 :          trans_cr = "T"
    1332              :       CASE default
    1333         3766 :          i_unit = 1.0_dp
    1334         3766 :          trans_cr = "N"
    1335              :       END SELECT
    1336              :       ! Actual multiplication
    1337              :       CALL parallel_gemm(trans_r, trans_cr, na, nb, nc, &
    1338         3766 :                          alpha, matrix_r, work_re, beta, res_re)
    1339              :       CALL parallel_gemm(trans_r, trans_cr, na, nb, nc, &
    1340         3766 :                          i_unit*alpha, matrix_r, work_im, beta, res_im)
    1341         3766 :       CALL cp_fm_to_cfm(res_re, res_im, res)
    1342         3766 :       CALL cp_fm_release(work_re)
    1343         3766 :       CALL cp_fm_release(work_im)
    1344         3766 :       CALL cp_fm_release(res_re)
    1345         3766 :       CALL cp_fm_release(res_im)
    1346              : 
    1347         3766 :    END SUBROUTINE multiply_fm_cfm
    1348              : ! **************************************************************************************************
    1349              : !> \brief Multiplies complex matrix by a real matrix from the right
    1350              : !> \note So far only converts the real matrix to complex one, potentially doubling the work
    1351              : !> \param rtbse_env
    1352              : !> \author Stepan Marek
    1353              : !> \date 09.2024
    1354              : ! **************************************************************************************************
    1355         5424 :    SUBROUTINE multiply_cfm_fm(trans_c, trans_r, na, nb, nc, &
    1356              :                               alpha, matrix_c, matrix_r, beta, res)
    1357              :       ! Transposition
    1358              :       CHARACTER(len=1)                                   :: trans_c, trans_r
    1359              :       INTEGER                                            :: na, nb, nc
    1360              :       ! accept real numbers
    1361              :       ! TODO : complex number support via interface?
    1362              :       REAL(kind=dp)                                      :: alpha, beta
    1363              :       TYPE(cp_cfm_type)                                  :: matrix_c, res
    1364              :       TYPE(cp_fm_type)                                   :: matrix_r
    1365              :       TYPE(cp_fm_type)                                   :: work_re, work_im, res_re, res_im
    1366              :       REAL(kind=dp)                                      :: i_unit
    1367              :       CHARACTER(len=1)                                   :: trans_cr
    1368              : 
    1369         1356 :       CALL cp_fm_create(work_re, matrix_c%matrix_struct)
    1370         1356 :       CALL cp_fm_create(work_im, matrix_c%matrix_struct)
    1371         1356 :       CALL cp_fm_create(res_re, res%matrix_struct)
    1372         1356 :       CALL cp_fm_create(res_im, res%matrix_struct)
    1373         1356 :       CALL cp_cfm_to_fm(matrix_c, work_re, work_im)
    1374            0 :       SELECT CASE (trans_c)
    1375              :       CASE ("C")
    1376            0 :          i_unit = -1.0_dp
    1377            0 :          trans_cr = "T"
    1378              :       CASE ("T")
    1379            0 :          i_unit = 1.0_dp
    1380            0 :          trans_cr = "T"
    1381              :       CASE default
    1382         1356 :          i_unit = 1.0_dp
    1383         1356 :          trans_cr = "N"
    1384              :       END SELECT
    1385              :       ! Actual multiplication
    1386              :       CALL parallel_gemm(trans_cr, trans_r, na, nb, nc, &
    1387         1356 :                          alpha, work_re, matrix_r, beta, res_re)
    1388              :       CALL parallel_gemm(trans_cr, trans_r, na, nb, nc, &
    1389         1356 :                          i_unit*alpha, work_im, matrix_r, beta, res_im)
    1390         1356 :       CALL cp_fm_to_cfm(res_re, res_im, res)
    1391         1356 :       CALL cp_fm_release(work_re)
    1392         1356 :       CALL cp_fm_release(work_im)
    1393         1356 :       CALL cp_fm_release(res_re)
    1394         1356 :       CALL cp_fm_release(res_im)
    1395              : 
    1396         1356 :    END SUBROUTINE multiply_cfm_fm
    1397            0 : END MODULE rt_bse_types
        

Generated by: LCOV version 2.0-1