LCOV - code coverage report
Current view: top level - src - qs_fb_env_methods.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:2fce0f8) Lines: 339 352 96.3 %
Date: 2024-12-21 06:28:57 Functions: 8 8 100.0 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2024 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : MODULE qs_fb_env_methods
       9             : 
      10             :    USE atomic_kind_types,               ONLY: atomic_kind_type,&
      11             :                                               get_atomic_kind
      12             :    USE basis_set_types,                 ONLY: get_gto_basis_set,&
      13             :                                               gto_basis_set_p_type,&
      14             :                                               gto_basis_set_type
      15             :    USE cell_types,                      ONLY: cell_type
      16             :    USE cp_blacs_env,                    ONLY: cp_blacs_env_type
      17             :    USE cp_control_types,                ONLY: dft_control_type
      18             :    USE cp_dbcsr_api,                    ONLY: &
      19             :         dbcsr_create, dbcsr_finalize, dbcsr_get_info, dbcsr_iterator_blocks_left, &
      20             :         dbcsr_iterator_next_block, dbcsr_iterator_start, dbcsr_iterator_stop, dbcsr_iterator_type, &
      21             :         dbcsr_multiply, dbcsr_p_type, dbcsr_release, dbcsr_reserve_blocks, dbcsr_set, dbcsr_type, &
      22             :         dbcsr_type_no_symmetry
      23             :    USE cp_dbcsr_operations,             ONLY: copy_dbcsr_to_fm,&
      24             :                                               dbcsr_allocate_matrix_set,&
      25             :                                               dbcsr_deallocate_matrix_set
      26             :    USE cp_fm_basic_linalg,              ONLY: cp_fm_gemm,&
      27             :                                               cp_fm_symm,&
      28             :                                               cp_fm_triangular_invert,&
      29             :                                               cp_fm_triangular_multiply,&
      30             :                                               cp_fm_upper_to_full
      31             :    USE cp_fm_cholesky,                  ONLY: cp_fm_cholesky_decompose,&
      32             :                                               cp_fm_cholesky_reduce,&
      33             :                                               cp_fm_cholesky_restore
      34             :    USE cp_fm_diag,                      ONLY: choose_eigv_solver,&
      35             :                                               cp_fm_power
      36             :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      37             :                                               cp_fm_struct_release,&
      38             :                                               cp_fm_struct_type
      39             :    USE cp_fm_types,                     ONLY: cp_fm_create,&
      40             :                                               cp_fm_release,&
      41             :                                               cp_fm_set_all,&
      42             :                                               cp_fm_to_fm,&
      43             :                                               cp_fm_type
      44             :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      45             :                                               cp_logger_type
      46             :    USE cp_output_handling,              ONLY: cp_print_key_finished_output,&
      47             :                                               cp_print_key_unit_nr
      48             :    USE cp_units,                        ONLY: cp_unit_from_cp2k
      49             :    USE input_constants,                 ONLY: cholesky_dbcsr,&
      50             :                                               cholesky_inverse,&
      51             :                                               cholesky_off,&
      52             :                                               cholesky_reduce,&
      53             :                                               cholesky_restore
      54             :    USE input_section_types,             ONLY: section_vals_get_subs_vals,&
      55             :                                               section_vals_type,&
      56             :                                               section_vals_val_get
      57             :    USE kinds,                           ONLY: default_string_length,&
      58             :                                               dp
      59             :    USE message_passing,                 ONLY: mp_para_env_type
      60             :    USE orbital_pointers,                ONLY: nco,&
      61             :                                               ncoset
      62             :    USE parallel_gemm_api,               ONLY: parallel_gemm
      63             :    USE particle_types,                  ONLY: particle_type
      64             :    USE qs_density_matrices,             ONLY: calculate_density_matrix
      65             :    USE qs_diis,                         ONLY: qs_diis_b_step
      66             :    USE qs_environment_types,            ONLY: get_qs_env,&
      67             :                                               qs_environment_type
      68             :    USE qs_fb_atomic_halo_types,         ONLY: &
      69             :         fb_atomic_halo_build_halo_atoms, fb_atomic_halo_cost, fb_atomic_halo_create, &
      70             :         fb_atomic_halo_list_create, fb_atomic_halo_list_nullify, fb_atomic_halo_list_obj, &
      71             :         fb_atomic_halo_list_set, fb_atomic_halo_list_write_info, &
      72             :         fb_atomic_halo_nelectrons_estimate_Z, fb_atomic_halo_nullify, fb_atomic_halo_obj, &
      73             :         fb_atomic_halo_set, fb_atomic_halo_sort, fb_build_pair_radii
      74             :    USE qs_fb_env_types,                 ONLY: fb_env_get,&
      75             :                                               fb_env_has_data,&
      76             :                                               fb_env_obj,&
      77             :                                               fb_env_set
      78             :    USE qs_fb_filter_matrix_methods,     ONLY: fb_fltrmat_build,&
      79             :                                               fb_fltrmat_build_2
      80             :    USE qs_fb_trial_fns_types,           ONLY: fb_trial_fns_create,&
      81             :                                               fb_trial_fns_nullify,&
      82             :                                               fb_trial_fns_obj,&
      83             :                                               fb_trial_fns_release,&
      84             :                                               fb_trial_fns_set
      85             :    USE qs_integral_utils,               ONLY: basis_set_list_setup
      86             :    USE qs_kind_types,                   ONLY: get_qs_kind,&
      87             :                                               qs_kind_type
      88             :    USE qs_mo_occupation,                ONLY: set_mo_occupation
      89             :    USE qs_mo_types,                     ONLY: allocate_mo_set,&
      90             :                                               deallocate_mo_set,&
      91             :                                               get_mo_set,&
      92             :                                               init_mo_set,&
      93             :                                               mo_set_type,&
      94             :                                               set_mo_set
      95             :    USE qs_scf_types,                    ONLY: qs_scf_env_type
      96             :    USE scf_control_types,               ONLY: scf_control_type
      97             :    USE string_utilities,                ONLY: compress,&
      98             :                                               uppercase
      99             : #include "./base/base_uses.f90"
     100             : 
     101             :    IMPLICIT NONE
     102             : 
     103             :    PRIVATE
     104             : 
     105             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_fb_env_methods'
     106             : 
     107             :    PUBLIC :: fb_env_do_diag, &
     108             :              fb_env_read_input, &
     109             :              fb_env_build_rcut_auto, &
     110             :              fb_env_build_atomic_halos, &
     111             :              fb_env_write_info
     112             : 
     113             : CONTAINS
     114             : 
     115             : ! **************************************************************************************************
     116             : !> \brief Do filtered matrix method diagonalisation
     117             : !> \param fb_env : the filter matrix environment
     118             : !> \param qs_env : quickstep environment
     119             : !> \param matrix_ks : DBCSR system (unfiltered) input KS matrix
     120             : !> \param matrix_s  : DBCSR system (unfiltered) input overlap matrix
     121             : !> \param scf_section : SCF input section
     122             : !> \param diis_step : whether we are doing a DIIS step
     123             : !> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
     124             : ! **************************************************************************************************
     125          80 :    SUBROUTINE fb_env_do_diag(fb_env, &
     126             :                              qs_env, &
     127             :                              matrix_ks, &
     128             :                              matrix_s, &
     129             :                              scf_section, &
     130             :                              diis_step)
     131             :       TYPE(fb_env_obj), INTENT(INOUT)                    :: fb_env
     132             :       TYPE(qs_environment_type), POINTER                 :: qs_env
     133             :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_ks, matrix_s
     134             :       TYPE(section_vals_type), POINTER                   :: scf_section
     135             :       LOGICAL, INTENT(INOUT)                             :: diis_step
     136             : 
     137             :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'fb_env_do_diag'
     138             : 
     139             :       CHARACTER(len=2)                                   :: spin_string
     140             :       CHARACTER(len=default_string_length)               :: name
     141             :       INTEGER :: filtered_nfullrowsORcols_total, handle, homo_filtered, ispin, lfomo_filtered, &
     142             :          my_nmo, nao, ndep, nelectron, nmo, nmo_filtered, nspin, original_nfullrowsORcols_total
     143          80 :       INTEGER, DIMENSION(:), POINTER                     :: filtered_rowORcol_block_sizes, &
     144          80 :                                                             original_rowORcol_block_sizes
     145             :       LOGICAL                                            :: collective_com
     146             :       REAL(kind=dp) :: diis_error, eps_default, eps_diis, eps_eigval, fermi_level, filter_temp, &
     147             :          flexible_electron_count, KTS_filtered, maxocc, mu_filtered
     148          80 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: eigenvalues, eigenvalues_filtered, occ, &
     149          80 :                                                             occ_filtered
     150             :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env
     151             :       TYPE(cp_fm_struct_type), POINTER                   :: filter_fm_struct, fm_struct
     152             :       TYPE(cp_fm_type)                                   :: fm_matrix_filter, fm_matrix_filtered_ks, &
     153             :                                                             fm_matrix_filtered_s, fm_matrix_ortho, &
     154             :                                                             fm_matrix_work
     155             :       TYPE(cp_fm_type), POINTER                          :: mo_coeff, mo_coeff_filtered
     156          80 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_filter
     157             :       TYPE(dbcsr_type)                                   :: matrix_filtered_ks, matrix_filtered_s, &
     158             :                                                             matrix_tmp
     159             :       TYPE(dbcsr_type), POINTER                          :: matrix_filtered_p
     160             :       TYPE(fb_atomic_halo_list_obj)                      :: atomic_halos
     161             :       TYPE(fb_trial_fns_obj)                             :: trial_fns
     162          80 :       TYPE(mo_set_type), DIMENSION(:), POINTER           :: mos, mos_filtered
     163             :       TYPE(mp_para_env_type), POINTER                    :: para_env
     164          80 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     165             :       TYPE(qs_scf_env_type), POINTER                     :: scf_env
     166             :       TYPE(scf_control_type), POINTER                    :: scf_control
     167             : 
     168             : ! TYPE(neighbor_list_set_p_type), DIMENSION(:), POINTER :: sab_orb
     169             : 
     170          80 :       CALL timeset(routineN, handle)
     171             : 
     172          80 :       NULLIFY (scf_env, scf_control, para_env, blacs_env, particle_set)
     173          80 :       NULLIFY (eigenvalues, eigenvalues_filtered, occ, occ_filtered)
     174          80 :       NULLIFY (mos, mos_filtered)
     175          80 :       NULLIFY (matrix_filter, matrix_filtered_p)
     176          80 :       NULLIFY (fm_struct, filter_fm_struct)
     177          80 :       NULLIFY (mo_coeff_filtered, mo_coeff)
     178             :       ! NULLIFY(sab_orb)
     179          80 :       CALL fb_atomic_halo_list_nullify(atomic_halos)
     180          80 :       CALL fb_trial_fns_nullify(trial_fns)
     181          80 :       NULLIFY (original_rowORcol_block_sizes, filtered_rowORcol_block_sizes)
     182             : 
     183             :       ! get qs_env information
     184             :       CALL get_qs_env(qs_env=qs_env, &
     185             :                       scf_env=scf_env, &
     186             :                       scf_control=scf_control, &
     187             :                       para_env=para_env, &
     188             :                       blacs_env=blacs_env, &
     189             :                       particle_set=particle_set, &
     190          80 :                       mos=mos)
     191             : 
     192          80 :       nspin = SIZE(matrix_ks)
     193             : 
     194             :       ! ----------------------------------------------------------------------
     195             :       ! DIIS step - based on non-filtered matrices and MOs
     196             :       ! ----------------------------------------------------------------------
     197             : 
     198         160 :       DO ispin = 1, nspin
     199             :          CALL copy_dbcsr_to_fm(matrix_ks(ispin)%matrix, &
     200         160 :                                scf_env%scf_work1(ispin))
     201             :       END DO
     202             : 
     203          80 :       eps_diis = scf_control%eps_diis
     204          80 :       eps_eigval = EPSILON(0.0_dp)
     205             : 
     206          80 :       IF (scf_env%iter_count > 1 .AND. .NOT. scf_env%skip_diis) THEN
     207             :          CALL qs_diis_b_step(scf_env%scf_diis_buffer, mos, scf_env%scf_work1, &
     208             :                              scf_env%scf_work2, scf_env%iter_delta, &
     209             :                              diis_error, diis_step, eps_diis, scf_control%nmixing, &
     210           0 :                              s_matrix=matrix_s, scf_section=scf_section)
     211             :       ELSE
     212          80 :          diis_step = .FALSE.
     213             :       END IF
     214             : 
     215          80 :       IF (diis_step) THEN
     216           0 :          scf_env%iter_param = diis_error
     217           0 :          scf_env%iter_method = "DIIS/Filter"
     218             :       ELSE
     219          80 :          IF (scf_env%mixing_method == 0) THEN
     220           0 :             scf_env%iter_method = "NoMix/Filter"
     221          80 :          ELSE IF (scf_env%mixing_method == 1) THEN
     222           0 :             scf_env%iter_param = scf_env%p_mix_alpha
     223           0 :             scf_env%iter_method = "P_Mix/Filter"
     224          80 :          ELSE IF (scf_env%mixing_method > 1) THEN
     225          80 :             scf_env%iter_param = scf_env%mixing_store%alpha
     226          80 :             scf_env%iter_method = TRIM(scf_env%mixing_store%iter_method)//"/Filter"
     227             :          END IF
     228             :       END IF
     229             : 
     230             :       ! ----------------------------------------------------------------------
     231             :       ! Construct Filter Matrix
     232             :       ! ----------------------------------------------------------------------
     233             : 
     234             :       CALL fb_env_get(fb_env=fb_env, &
     235             :                       filter_temperature=filter_temp, &
     236             :                       atomic_halos=atomic_halos, &
     237          80 :                       eps_default=eps_default)
     238             : 
     239             :       ! construct trial functions
     240          80 :       CALL get_mo_set(mo_set=mos(1), maxocc=maxocc)
     241          80 :       CALL fb_env_build_trial_fns_auto(fb_env, qs_env, maxocc)
     242             :       CALL fb_env_get(fb_env=fb_env, &
     243          80 :                       trial_fns=trial_fns)
     244             : 
     245             :       ! allocate filter matrix (matrix_filter(ispin)%matrix are
     246             :       ! nullified by dbcsr_allocate_matrix_set)
     247          80 :       CALL dbcsr_allocate_matrix_set(matrix_filter, nspin)
     248         160 :       DO ispin = 1, nspin
     249             :          ! get system-wide fermi energy and occupancy, we use this to
     250             :          ! define the filter function used for the filter matrix
     251             :          CALL get_mo_set(mo_set=mos(ispin), &
     252             :                          mu=fermi_level, &
     253          80 :                          maxocc=maxocc)
     254             :          ! get filter matrix name
     255          80 :          WRITE (spin_string, FMT="(I1)") ispin
     256          80 :          name = TRIM("FILTER MATRIX SPIN "//spin_string)
     257          80 :          CALL compress(name)
     258          80 :          CALL uppercase(name)
     259             :          ! calculate filter matrix (matrix_s(1) is the overlap, the rest
     260             :          ! in the array are its derivatives)
     261             :          CALL fb_env_get(fb_env=fb_env, &
     262          80 :                          collective_com=collective_com)
     263         240 :          IF (collective_com) THEN
     264             :             CALL fb_fltrmat_build_2(H_mat=matrix_ks(ispin)%matrix, &
     265             :                                     S_mat=matrix_s(1)%matrix, &
     266             :                                     atomic_halos=atomic_halos, &
     267             :                                     trial_fns=trial_fns, &
     268             :                                     para_env=para_env, &
     269             :                                     particle_set=particle_set, &
     270             :                                     fermi_level=fermi_level, &
     271             :                                     filter_temp=filter_temp, &
     272             :                                     name=name, &
     273             :                                     filter_mat=matrix_filter(ispin)%matrix, &
     274          16 :                                     tolerance=eps_default)
     275             :          ELSE
     276             :             CALL fb_fltrmat_build(H_mat=matrix_ks(ispin)%matrix, &
     277             :                                   S_mat=matrix_s(1)%matrix, &
     278             :                                   atomic_halos=atomic_halos, &
     279             :                                   trial_fns=trial_fns, &
     280             :                                   para_env=para_env, &
     281             :                                   particle_set=particle_set, &
     282             :                                   fermi_level=fermi_level, &
     283             :                                   filter_temp=filter_temp, &
     284             :                                   name=name, &
     285             :                                   filter_mat=matrix_filter(ispin)%matrix, &
     286          64 :                                   tolerance=eps_default)
     287             :          END IF
     288             :       END DO ! ispin
     289             : 
     290             :       ! ----------------------------------------------------------------------
     291             :       ! Do Filtered Diagonalisation
     292             :       ! ----------------------------------------------------------------------
     293             : 
     294             :       ! Obtain matrix dimensions. KS and S matrices are symmetric, so
     295             :       ! row_block_sizes and col_block_sizes should be identical. The
     296             :       ! same applies to the filtered block sizes. Note that filter
     297             :       ! matrix will have row_block_sizes equal to that of the original,
     298             :       ! and col_block_sizes equal to that of the filtered.  We assume
     299             :       ! also that the matrix dimensions are identical for both spin
     300             :       ! channels.
     301             :       CALL dbcsr_get_info(matrix_ks(1)%matrix, &
     302             :                           row_blk_size=original_rowORcol_block_sizes, &
     303          80 :                           nfullrows_total=original_nfullrowsORcols_total)
     304             :       CALL dbcsr_get_info(matrix_filter(1)%matrix, &
     305             :                           col_blk_size=filtered_rowORcol_block_sizes, &
     306          80 :                           nfullcols_total=filtered_nfullrowsORcols_total)
     307             : 
     308             :       ! filter diagonalisation works on a smaller basis set, and thus
     309             :       ! requires a new mo_set (molecular orbitals | eigenvectors) and
     310             :       ! the corresponding matrix pools for the eigenvector coefficients
     311         320 :       ALLOCATE (mos_filtered(nspin))
     312         160 :       DO ispin = 1, nspin
     313             :          CALL get_mo_set(mo_set=mos(ispin), &
     314             :                          maxocc=maxocc, &
     315             :                          nelectron=nelectron, &
     316          80 :                          flexible_electron_count=flexible_electron_count)
     317             :          CALL allocate_mo_set(mo_set=mos_filtered(ispin), &
     318             :                               nao=filtered_nfullrowsORcols_total, &
     319             :                               nmo=filtered_nfullrowsORcols_total, &
     320             :                               nelectron=nelectron, &
     321             :                               n_el_f=REAL(nelectron, dp), &
     322             :                               maxocc=maxocc, &
     323         160 :                               flexible_electron_count=flexible_electron_count)
     324             :       END DO ! ispin
     325             : 
     326             :       ! create DBCSR filtered KS matrix, this is reused for each spin
     327             :       ! channel
     328             :       ! both row_blk_size and col_blk_size should be that of
     329             :       ! col_blk_size of the filter matrix
     330             :       CALL dbcsr_create(matrix=matrix_filtered_ks, template=matrix_ks(1)%matrix, &
     331             :                         name=TRIM("FILTERED_KS_MATRIX"), &
     332             :                         matrix_type=dbcsr_type_no_symmetry, &
     333             :                         row_blk_size=filtered_rowORcol_block_sizes, &
     334             :                         col_blk_size=filtered_rowORcol_block_sizes, &
     335          80 :                         nze=0)
     336          80 :       CALL dbcsr_finalize(matrix_filtered_ks)
     337             : 
     338             :       ! create DBCSR filtered S (overlap) matrix. Note that
     339             :       ! matrix_s(1)%matrix is the original overlap matrix---the rest in
     340             :       ! the array are derivatives, and it should not depend on
     341             :       ! spin. HOWEVER, since the filter matrix is constructed from KS
     342             :       ! matrix, and does depend on spin, the filtered S also becomes
     343             :       ! spin dependent. Nevertheless this matrix is reused for each spin
     344             :       ! channel
     345             :       ! both row_blk_size and col_blk_size should be that of
     346             :       ! col_blk_size of the filter matrix
     347             :       CALL dbcsr_create(matrix=matrix_filtered_s, template=matrix_s(1)%matrix, &
     348             :                         name=TRIM("FILTERED_S_MATRIX"), &
     349             :                         matrix_type=dbcsr_type_no_symmetry, &
     350             :                         row_blk_size=filtered_rowORcol_block_sizes, &
     351             :                         col_blk_size=filtered_rowORcol_block_sizes, &
     352          80 :                         nze=0)
     353          80 :       CALL dbcsr_finalize(matrix_filtered_s)
     354             : 
     355             :       ! create temporary matrix for constructing filtered KS and S
     356             :       ! the temporary matrix won't be square
     357             :       CALL dbcsr_create(matrix=matrix_tmp, template=matrix_s(1)%matrix, &
     358             :                         name=TRIM("TEMPORARY_MATRIX"), &
     359             :                         matrix_type=dbcsr_type_no_symmetry, &
     360             :                         row_blk_size=original_rowORcol_block_sizes, &
     361             :                         col_blk_size=filtered_rowORcol_block_sizes, &
     362          80 :                         nze=0)
     363          80 :       CALL dbcsr_finalize(matrix_tmp)
     364             : 
     365             :       ! create fm format matrices used for diagonalisation
     366             :       CALL cp_fm_struct_create(fmstruct=fm_struct, &
     367             :                                para_env=para_env, &
     368             :                                context=blacs_env, &
     369             :                                nrow_global=filtered_nfullrowsORcols_total, &
     370          80 :                                ncol_global=filtered_nfullrowsORcols_total)
     371             :       ! both fm_matrix_filtered_s and fm_matrix_filtered_ks are reused
     372             :       ! for each spin channel
     373             :       CALL cp_fm_create(fm_matrix_filtered_s, &
     374             :                         fm_struct, &
     375          80 :                         name="FM_MATRIX_FILTERED_S")
     376             :       CALL cp_fm_create(fm_matrix_filtered_ks, &
     377             :                         fm_struct, &
     378          80 :                         name="FM_MATRIX_FILTERED_KS")
     379             :       ! creaate work matrix
     380          80 :       CALL cp_fm_create(fm_matrix_work, fm_struct, name="FM_MATRIX_WORK")
     381          80 :       CALL cp_fm_create(fm_matrix_ortho, fm_struct, name="FM_MATRIX_ORTHO")
     382             :       ! all fm matrices are created, so can release fm_struct
     383          80 :       CALL cp_fm_struct_release(fm_struct)
     384             : 
     385             :       ! construct filtered KS, S matrix and diagonalise
     386         160 :       DO ispin = 1, nspin
     387             : 
     388             :          ! construct filtered KS matrix
     389             :          CALL dbcsr_multiply("N", "N", 1.0_dp, &
     390             :                              matrix_ks(ispin)%matrix, matrix_filter(ispin)%matrix, &
     391          80 :                              0.0_dp, matrix_tmp)
     392             :          CALL dbcsr_multiply("T", "N", 1.0_dp, &
     393             :                              matrix_filter(ispin)%matrix, matrix_tmp, &
     394          80 :                              0.0_dp, matrix_filtered_ks)
     395             :          ! construct filtered S_matrix
     396             :          CALL dbcsr_multiply("N", "N", 1.0_dp, &
     397             :                              matrix_s(1)%matrix, matrix_filter(ispin)%matrix, &
     398          80 :                              0.0_dp, matrix_tmp)
     399             :          CALL dbcsr_multiply("T", "N", 1.0_dp, &
     400             :                              matrix_filter(ispin)%matrix, matrix_tmp, &
     401          80 :                              0.0_dp, matrix_filtered_s)
     402             : 
     403             :          ! now that we have the filtered KS and S matrices for this spin
     404             :          ! channel, perform ordinary diagonalisation
     405             : 
     406             :          ! convert DBCSR matrices to fm format
     407          80 :          CALL copy_dbcsr_to_fm(matrix_filtered_s, fm_matrix_filtered_s)
     408          80 :          CALL copy_dbcsr_to_fm(matrix_filtered_ks, fm_matrix_filtered_ks)
     409             : 
     410          80 :          CALL get_mo_set(mos_filtered(ispin), nmo=nmo, nao=nao)
     411             : 
     412             :          CALL cp_fm_struct_create(fm_struct, nrow_global=nao, &
     413             :                                   ncol_global=nmo, para_env=para_env, &
     414          80 :                                   context=blacs_env)
     415             : 
     416             :          ! setup matrix pools for the molecular orbitals
     417             :          CALL init_mo_set(mos_filtered(ispin), &
     418             :                           fm_struct=fm_struct, &
     419          80 :                           name="FILTERED_MOS")
     420          80 :          CALL cp_fm_struct_release(fm_struct)
     421             : 
     422             :          ! now diagonalise
     423             :          CALL fb_env_eigensolver(fm_matrix_filtered_ks, &
     424             :                                  fm_matrix_filtered_s, &
     425             :                                  mos_filtered(ispin), &
     426             :                                  fm_matrix_ortho, &
     427             :                                  fm_matrix_work, &
     428             :                                  eps_eigval, &
     429             :                                  ndep, &
     430         240 :                                  scf_env%cholesky_method)
     431             :       END DO ! ispin
     432             : 
     433             :       ! release temporary matrices
     434          80 :       CALL dbcsr_release(matrix_filtered_s)
     435          80 :       CALL dbcsr_release(matrix_filtered_ks)
     436          80 :       CALL cp_fm_release(fm_matrix_filtered_s)
     437          80 :       CALL cp_fm_release(fm_matrix_filtered_ks)
     438          80 :       CALL cp_fm_release(fm_matrix_work)
     439          80 :       CALL cp_fm_release(fm_matrix_ortho)
     440             : 
     441             :       ! ----------------------------------------------------------------------
     442             :       ! Construct New Density Matrix
     443             :       ! ----------------------------------------------------------------------
     444             : 
     445             :       ! calculate filtered molecular orbital occupation numbers and fermi
     446             :       ! level etc
     447             :       CALL set_mo_occupation(mo_array=mos_filtered, &
     448          80 :                              smear=scf_control%smear)
     449             : 
     450             :       ! get the filtered density matrix and then convert back to the
     451             :       ! full basis version in scf_env ready to be used outside this
     452             :       ! subroutine
     453          80 :       ALLOCATE (matrix_filtered_p)
     454             :       ! the filtered density matrix should have the same sparse
     455             :       ! structure as the original density matrix, we must copy the
     456             :       ! sparse structure here, since construction of the density matrix
     457             :       ! preserves its sparse form, and therefore matrix_filtered_p must
     458             :       ! have its blocks allocated here now. We assume the original
     459             :       ! density matrix scf_env%p_mix_new has the same sparse structure
     460             :       ! in both spin channels.
     461             :       CALL dbcsr_create(matrix=matrix_filtered_p, template=scf_env%p_mix_new(1, 1)%matrix, &
     462             :                         name=TRIM("FILTERED_MATRIX_P"), &
     463             :                         row_blk_size=filtered_rowORcol_block_sizes, &
     464             :                         col_blk_size=filtered_rowORcol_block_sizes, &
     465          80 :                         nze=0)
     466          80 :       CALL dbcsr_finalize(matrix_filtered_p)
     467             :       CALL fb_dbcsr_copy_sparse_struct(matrix_filtered_p, &
     468          80 :                                        scf_env%p_mix_new(1, 1)%matrix)
     469             :       ! old implementation, using sab_orb to allocate the blocks in matrix_filtered_p
     470             :       ! CALL get_qs_env(qs_env=qs_env, sab_orb=sab_orb)
     471             :       ! CALL cp_dbcsr_alloc_block_from_nbl(matrix_filtered_p, sab_orb)
     472          80 :       CALL dbcsr_set(matrix_filtered_p, 0.0_dp)
     473             : 
     474         160 :       DO ispin = 1, nspin
     475             :          ! calculate matrix_filtered_p
     476             :          CALL calculate_density_matrix(mos_filtered(ispin), &
     477          80 :                                        matrix_filtered_p)
     478             :          ! convert back to full basis p
     479             :          CALL dbcsr_multiply("N", "N", 1.0_dp, &
     480             :                              matrix_filter(ispin)%matrix, matrix_filtered_p, &
     481          80 :                              0.0_dp, matrix_tmp)
     482             :          CALL dbcsr_multiply("N", "T", 1.0_dp, &
     483             :                              matrix_tmp, matrix_filter(ispin)%matrix, &
     484             :                              0.0_dp, scf_env%p_mix_new(ispin, 1)%matrix, &
     485         160 :                              retain_sparsity=.TRUE.)
     486             :          ! note that we want to retain the sparse structure of
     487             :          ! scf_env%p_mix_new
     488             :       END DO ! ispin
     489             : 
     490             :       ! release temporary matrices
     491          80 :       CALL dbcsr_release(matrix_tmp)
     492          80 :       CALL dbcsr_release(matrix_filtered_p)
     493          80 :       DEALLOCATE (matrix_filtered_p)
     494             : 
     495             :       ! ----------------------------------------------------------------------
     496             :       ! Update MOs
     497             :       ! ----------------------------------------------------------------------
     498             : 
     499             :       ! we still need to convert mos_filtered back to the full basis
     500             :       ! version (mos) for this, we need to update mo_coeff (and/or
     501             :       ! mo_coeff_b --- the DBCSR version, if used) of mos
     502             : 
     503             :       ! note also that mo_eigenvalues cannot be fully updated, given
     504             :       ! that the eigenvalues are computed in a smaller basis, and thus
     505             :       ! do not give the full spectron. Printing of molecular states
     506             :       ! (molecular DOS) at each SCF step is therefore not recommended
     507             :       ! when using this method. The idea is that if one wants a full
     508             :       ! molecular DOS, then one should perform a full diagonalisation
     509             :       ! without the filters once the SCF has been achieved.
     510             : 
     511             :       ! NOTE: from reading the source code, it appears that mo_coeff_b
     512             :       ! is actually never used by default (DOUBLE CHECK?!). Even
     513             :       ! subroutine eigensolver_dbcsr updates mo_coeff, and not
     514             :       ! mo_coeff_b.
     515             : 
     516             :       ! create FM format filter matrix
     517             :       CALL cp_fm_struct_create(fmstruct=filter_fm_struct, &
     518             :                                para_env=para_env, &
     519             :                                context=blacs_env, &
     520             :                                nrow_global=original_nfullrowsORcols_total, &
     521          80 :                                ncol_global=filtered_nfullrowsORcols_total)
     522             :       CALL cp_fm_create(fm_matrix_filter, &
     523             :                         filter_fm_struct, &
     524          80 :                         name="FM_MATRIX_FILTER")
     525          80 :       CALL cp_fm_struct_release(filter_fm_struct)
     526             : 
     527         160 :       DO ispin = 1, nspin
     528             :          ! now the full basis mo_set should only contain the reduced
     529             :          ! number of eigenvectors and eigenvalues
     530             :          CALL get_mo_set(mo_set=mos_filtered(ispin), &
     531             :                          homo=homo_filtered, &
     532             :                          lfomo=lfomo_filtered, &
     533             :                          nmo=nmo_filtered, &
     534             :                          eigenvalues=eigenvalues_filtered, &
     535             :                          occupation_numbers=occ_filtered, &
     536             :                          mo_coeff=mo_coeff_filtered, &
     537             :                          kTS=kTS_filtered, &
     538          80 :                          mu=mu_filtered)
     539             :          ! first set all the relevant scalars
     540             :          CALL set_mo_set(mo_set=mos(ispin), &
     541             :                          homo=homo_filtered, &
     542             :                          lfomo=lfomo_filtered, &
     543             :                          kTS=kTS_filtered, &
     544          80 :                          mu=mu_filtered)
     545             :          ! now set the arrays and fm_matrices
     546             :          CALL get_mo_set(mo_set=mos(ispin), &
     547             :                          nmo=nmo, &
     548             :                          occupation_numbers=occ, &
     549             :                          eigenvalues=eigenvalues, &
     550          80 :                          mo_coeff=mo_coeff)
     551             :          ! number of mos in original mo_set may sometimes be less than
     552             :          ! nmo_filtered, so we must make sure we do not go out of bounds
     553          80 :          my_nmo = MIN(nmo, nmo_filtered)
     554        2640 :          eigenvalues(:) = 0.0_dp
     555        5200 :          eigenvalues(1:my_nmo) = eigenvalues_filtered(1:my_nmo)
     556        2640 :          occ(:) = 0.0_dp
     557        5200 :          occ(1:my_nmo) = occ_filtered(1:my_nmo)
     558             :          ! convert mo_coeff_filtered back to original basis
     559          80 :          CALL cp_fm_set_all(matrix=mo_coeff, alpha=0.0_dp)
     560          80 :          CALL copy_dbcsr_to_fm(matrix_filter(ispin)%matrix, fm_matrix_filter)
     561             :          CALL cp_fm_gemm("N", "N", &
     562             :                          original_nfullrowsORcols_total, &
     563             :                          my_nmo, &
     564             :                          filtered_nfullrowsORcols_total, &
     565             :                          1.0_dp, fm_matrix_filter, mo_coeff_filtered, &
     566         320 :                          0.0_dp, mo_coeff)
     567             : 
     568             :       END DO ! ispin
     569             : 
     570             :       ! release temporary matrices
     571          80 :       CALL cp_fm_release(fm_matrix_filter)
     572             : 
     573             :       ! ----------------------------------------------------------------------
     574             :       ! Final Clean Up
     575             :       ! ----------------------------------------------------------------------
     576             : 
     577         160 :       DO ispin = 1, nspin
     578         160 :          CALL deallocate_mo_set(mo_set=mos_filtered(ispin))
     579             :       END DO
     580          80 :       DEALLOCATE (mos_filtered)
     581          80 :       CALL dbcsr_deallocate_matrix_set(matrix_filter)
     582             : 
     583          80 :       CALL timestop(handle)
     584             : 
     585         480 :    END SUBROUTINE fb_env_do_diag
     586             : 
     587             : ! **************************************************************************************************
     588             : !> \brief The main parallel eigensolver engine for filter matrix diagonalisation
     589             : !> \param fm_KS : the BLACS distributed Kohn-Sham matrix, input only
     590             : !> \param fm_S  : the BLACS distributed overlap matrix, input only
     591             : !> \param mo_set : upon output contains the molecular orbitals (eigenvectors)
     592             : !>                 and eigenvalues
     593             : !> \param fm_ortho : one of the work matrices, on output, the BLACS distributed
     594             : !>                   matrix for orthogalising the eigen problem. E.g. if using
     595             : !>                   Cholesky inversse, then the upper triangle part contains
     596             : !>                   the inverse of Cholesky U; if not using Cholesky, then it
     597             : !>                   contains the S^-1/2.
     598             : !> \param fm_work : work matrix used by eigen solver
     599             : !> \param eps_eigval : used for quenching the small numbers when computing S^-1/2
     600             : !>                     any values less than eps_eigval is truncated to zero.
     601             : !> \param ndep : if the overlap is not positive definite, then ndep > 0,
     602             : !>               and equals to the number of linear dependent basis functions
     603             : !>               in the filtered basis set
     604             : !> \param method : method for solving generalised eigenvalue problem
     605             : !> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
     606             : ! **************************************************************************************************
     607         160 :    SUBROUTINE fb_env_eigensolver(fm_KS, fm_S, mo_set, fm_ortho, &
     608             :                                  fm_work, eps_eigval, ndep, method)
     609             :       TYPE(cp_fm_type), INTENT(IN)                       :: fm_KS, fm_S
     610             :       TYPE(mo_set_type), INTENT(IN)                      :: mo_set
     611             :       TYPE(cp_fm_type), INTENT(IN)                       :: fm_ortho, fm_work
     612             :       REAL(KIND=dp), INTENT(IN)                          :: eps_eigval
     613             :       INTEGER, INTENT(OUT)                               :: ndep
     614             :       INTEGER, INTENT(IN)                                :: method
     615             : 
     616             :       CHARACTER(len=*), PARAMETER :: routineN = 'fb_env_eigensolver'
     617             : 
     618             :       CHARACTER(len=8)                                   :: ndep_string
     619             :       INTEGER                                            :: handle, info, my_method, nao, nmo
     620          80 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: mo_eigenvalues
     621             :       TYPE(cp_fm_type), POINTER                          :: mo_coeff
     622             : 
     623          80 :       CALL timeset(routineN, handle)
     624             : 
     625             :       CALL get_mo_set(mo_set=mo_set, &
     626             :                       nao=nao, &
     627             :                       nmo=nmo, &
     628             :                       eigenvalues=mo_eigenvalues, &
     629          80 :                       mo_coeff=mo_coeff)
     630          80 :       my_method = method
     631          80 :       ndep = 0
     632             : 
     633             :       ! first, obtain orthogonalisation (ortho) matrix
     634          80 :       IF (my_method .NE. cholesky_off) THEN
     635          64 :          CALL cp_fm_to_fm(fm_S, fm_ortho)
     636          64 :          CALL cp_fm_cholesky_decompose(fm_ortho, info_out=info)
     637          64 :          IF (info .NE. 0) THEN
     638             :             CALL cp_warn(__LOCATION__, &
     639             :                          "Unable to perform Cholesky decomposition on the overlap "// &
     640             :                          "matrix. The new filtered basis may not be linearly "// &
     641             :                          "independent set. Revert to using inverse square-root "// &
     642             :                          "of the overlap. To avoid this warning, you can try "// &
     643           0 :                          "to use a higher filter termperature.")
     644             :             my_method = cholesky_off
     645             :          ELSE
     646           0 :             SELECT CASE (my_method)
     647             :             CASE (cholesky_dbcsr)
     648             :                CALL cp_abort(__LOCATION__, &
     649           0 :                              "filter matrix method with CHOLESKY_DBCSR is not yet implemented")
     650             :             CASE (cholesky_reduce)
     651          16 :                CALL cp_fm_cholesky_reduce(fm_KS, fm_ortho)
     652          16 :                CALL choose_eigv_solver(fm_KS, fm_work, mo_eigenvalues)
     653          16 :                CALL cp_fm_cholesky_restore(fm_work, nmo, fm_ortho, mo_coeff, "SOLVE")
     654             :             CASE (cholesky_restore)
     655          32 :                CALL cp_fm_upper_to_full(fm_KS, fm_work)
     656             :                CALL cp_fm_cholesky_restore(fm_KS, nao, fm_ortho, fm_work, "SOLVE", &
     657          32 :                                            pos="RIGHT")
     658             :                CALL cp_fm_cholesky_restore(fm_work, nao, fm_ortho, fm_KS, "SOLVE", &
     659          32 :                                            pos="LEFT", transa="T")
     660          32 :                CALL choose_eigv_solver(fm_KS, fm_work, mo_eigenvalues)
     661          32 :                CALL cp_fm_cholesky_restore(fm_work, nmo, fm_ortho, mo_coeff, "SOLVE")
     662             :             CASE (cholesky_inverse)
     663          16 :                CALL cp_fm_triangular_invert(fm_ortho)
     664          16 :                CALL cp_fm_upper_to_full(fm_KS, fm_work)
     665             :                CALL cp_fm_triangular_multiply(fm_ortho, &
     666             :                                               fm_KS, &
     667             :                                               side="R", &
     668             :                                               transpose_tr=.FALSE., &
     669             :                                               invert_tr=.FALSE., &
     670             :                                               uplo_tr="U", &
     671             :                                               n_rows=nao, &
     672             :                                               n_cols=nao, &
     673          16 :                                               alpha=1.0_dp)
     674             :                CALL cp_fm_triangular_multiply(fm_ortho, &
     675             :                                               fm_KS, &
     676             :                                               side="L", &
     677             :                                               transpose_tr=.TRUE., &
     678             :                                               invert_tr=.FALSE., &
     679             :                                               uplo_tr="U", &
     680             :                                               n_rows=nao, &
     681             :                                               n_cols=nao, &
     682          16 :                                               alpha=1.0_dp)
     683          16 :                CALL choose_eigv_solver(fm_KS, fm_work, mo_eigenvalues)
     684             :                CALL cp_fm_triangular_multiply(fm_ortho, &
     685             :                                               fm_work, &
     686             :                                               side="L", &
     687             :                                               transpose_tr=.FALSE., &
     688             :                                               invert_tr=.FALSE., &
     689             :                                               uplo_tr="U", &
     690             :                                               n_rows=nao, &
     691             :                                               n_cols=nmo, &
     692          16 :                                               alpha=1.0_dp)
     693          80 :                CALL cp_fm_to_fm(fm_work, mo_coeff, nmo, 1, 1)
     694             :             END SELECT
     695             :          END IF
     696             :       END IF
     697             :       IF (my_method == cholesky_off) THEN
     698             :          ! calculating ortho as S^-1/2 using diagonalisation of S, and
     699             :          ! solve accordingly
     700          16 :          CALL cp_fm_to_fm(fm_S, fm_ortho)
     701             :          CALL cp_fm_power(fm_ortho, fm_work, -0.5_dp, &
     702          16 :                           eps_eigval, ndep)
     703          16 :          IF (ndep > 0) THEN
     704           0 :             WRITE (ndep_string, FMT="(I8)") ndep
     705             :             CALL cp_warn(__LOCATION__, &
     706           0 :                          "Number of linearly dependent filtered orbitals: "//ndep_string)
     707             :          END IF
     708             :          ! solve eigen equatoin using S^-1/2
     709             :          CALL cp_fm_symm("L", "U", nao, nao, 1.0_dp, fm_KS, fm_ortho, &
     710          16 :                          0.0_dp, fm_work)
     711             :          CALL parallel_gemm("T", "N", nao, nao, nao, 1.0_dp, fm_ortho, &
     712          16 :                             fm_work, 0.0_dp, fm_KS)
     713          16 :          CALL choose_eigv_solver(fm_KS, fm_work, mo_eigenvalues)
     714             :          CALL parallel_gemm("N", "N", nao, nmo, nao, 1.0_dp, fm_ortho, &
     715          16 :                             fm_work, 0.0_dp, mo_coeff)
     716             :       END IF
     717             : 
     718          80 :       CALL timestop(handle)
     719             : 
     720          80 :    END SUBROUTINE fb_env_eigensolver
     721             : 
     722             : ! **************************************************************************************************
     723             : !> \brief Read input sections for filter matrix method
     724             : !> \param fb_env : the filter matrix environment
     725             : !> \param scf_section : SCF input section
     726             : !> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
     727             : ! **************************************************************************************************
     728          50 :    SUBROUTINE fb_env_read_input(fb_env, scf_section)
     729             : 
     730             :       TYPE(fb_env_obj), INTENT(INOUT)                    :: fb_env
     731             :       TYPE(section_vals_type), POINTER                   :: scf_section
     732             : 
     733             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'fb_env_read_input'
     734             : 
     735             :       INTEGER                                            :: handle
     736             :       LOGICAL                                            :: l_val
     737             :       REAL(KIND=dp)                                      :: r_val
     738             :       TYPE(section_vals_type), POINTER                   :: fb_section
     739             : 
     740          10 :       CALL timeset(routineN, handle)
     741             : 
     742          10 :       NULLIFY (fb_section)
     743             :       fb_section => section_vals_get_subs_vals(scf_section, &
     744          10 :                                                "DIAGONALIZATION%FILTER_MATRIX")
     745             :       ! filter_temperature
     746             :       CALL section_vals_val_get(fb_section, "FILTER_TEMPERATURE", &
     747          10 :                                 r_val=r_val)
     748             :       CALL fb_env_set(fb_env=fb_env, &
     749          10 :                       filter_temperature=r_val)
     750             :       ! auto_cutoff_scale
     751             :       CALL section_vals_val_get(fb_section, "AUTO_CUTOFF_SCALE", &
     752          10 :                                 r_val=r_val)
     753             :       CALL fb_env_set(fb_env=fb_env, &
     754          10 :                       auto_cutoff_scale=r_val)
     755             :       ! communication model
     756             :       CALL section_vals_val_get(fb_section, "COLLECTIVE_COMMUNICATION", &
     757          10 :                                 l_val=l_val)
     758             :       CALL fb_env_set(fb_env=fb_env, &
     759          10 :                       collective_com=l_val)
     760             :       ! eps_default
     761             :       CALL section_vals_val_get(fb_section, "EPS_FB", &
     762          10 :                                 r_val=r_val)
     763             :       CALL fb_env_set(fb_env=fb_env, &
     764          10 :                       eps_default=r_val)
     765             : 
     766          10 :       CALL timestop(handle)
     767             : 
     768          10 :    END SUBROUTINE fb_env_read_input
     769             : 
     770             : ! **************************************************************************************************
     771             : !> \brief Automatically generate the cutoff radii of atoms used for
     772             : !>        constructing the atomic halos, based on basis set cutoff
     773             : !>        ranges for each kind
     774             : !> \param fb_env : the filter matrix environment
     775             : !> \param qs_env : quickstep environment
     776             : !> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
     777             : ! **************************************************************************************************
     778          10 :    SUBROUTINE fb_env_build_rcut_auto(fb_env, qs_env)
     779             :       TYPE(fb_env_obj), INTENT(INOUT)                    :: fb_env
     780             :       TYPE(qs_environment_type), POINTER                 :: qs_env
     781             : 
     782             :       CHARACTER(len=*), PARAMETER :: routineN = 'fb_env_build_rcut_auto'
     783             : 
     784             :       INTEGER                                            :: handle, ikind, nkinds
     785             :       REAL(KIND=dp)                                      :: auto_cutoff_scale, kind_radius
     786          10 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: rcut
     787             :       TYPE(dft_control_type), POINTER                    :: dft_control
     788          10 :       TYPE(gto_basis_set_p_type), DIMENSION(:), POINTER  :: basis_set_list
     789             :       TYPE(gto_basis_set_type), POINTER                  :: basis_set
     790          10 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     791             : 
     792          10 :       CALL timeset(routineN, handle)
     793             : 
     794          10 :       NULLIFY (rcut, qs_kind_set, dft_control)
     795             : 
     796             :       CALL get_qs_env(qs_env=qs_env, &
     797             :                       qs_kind_set=qs_kind_set, &
     798          10 :                       dft_control=dft_control)
     799             :       CALL fb_env_get(fb_env=fb_env, &
     800          10 :                       auto_cutoff_scale=auto_cutoff_scale)
     801             : 
     802          10 :       nkinds = SIZE(qs_kind_set)
     803          30 :       ALLOCATE (rcut(nkinds))
     804             : 
     805             :       ! reading from the other parts of the code, it seemed that
     806             :       ! aux_fit_basis_set is only used when do_admm is TRUE. This can be
     807             :       ! seen from the calls to generate_qs_task_list subroutine in
     808             :       ! qs_create_task_list, found in qs_environment_methods.F:
     809             :       ! basis_type is only set as input parameter for do_admm
     810             :       ! calculations, and if not set, the task list is generated using
     811             :       ! the default basis_set="ORB".
     812          40 :       ALLOCATE (basis_set_list(nkinds))
     813          10 :       IF (dft_control%do_admm) THEN
     814           0 :          CALL basis_set_list_setup(basis_set_list, "AUX_FIT", qs_kind_set)
     815             :       ELSE
     816          10 :          CALL basis_set_list_setup(basis_set_list, "ORB", qs_kind_set)
     817             :       END IF
     818             : 
     819          20 :       DO ikind = 1, nkinds
     820          10 :          basis_set => basis_set_list(ikind)%gto_basis_set
     821          10 :          CALL get_gto_basis_set(gto_basis_set=basis_set, kind_radius=kind_radius)
     822          20 :          rcut(ikind) = kind_radius*auto_cutoff_scale
     823             :       END DO
     824             : 
     825             :       CALL fb_env_set(fb_env=fb_env, &
     826          10 :                       rcut=rcut)
     827             : 
     828             :       ! cleanup
     829          10 :       DEALLOCATE (basis_set_list)
     830             : 
     831          10 :       CALL timestop(handle)
     832             : 
     833          20 :    END SUBROUTINE fb_env_build_rcut_auto
     834             : 
     835             : ! **************************************************************************************************
     836             : !> \brief Builds an fb_atomic_halo_list object using information
     837             : !>        from fb_env
     838             : !> \param fb_env the fb_env object
     839             : !> \param qs_env : quickstep environment (need this to access particle)
     840             : !>                 positions and their kinds as well as which particles
     841             : !>                 are local to this process
     842             : !> \param scf_section : SCF input section, for printing output
     843             : !> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
     844             : ! **************************************************************************************************
     845          10 :    SUBROUTINE fb_env_build_atomic_halos(fb_env, qs_env, scf_section)
     846             :       TYPE(fb_env_obj), INTENT(INOUT)                    :: fb_env
     847             :       TYPE(qs_environment_type), POINTER                 :: qs_env
     848             :       TYPE(section_vals_type), POINTER                   :: scf_section
     849             : 
     850             :       CHARACTER(len=*), PARAMETER :: routineN = 'fb_env_build_atomic_halos'
     851             : 
     852             :       INTEGER :: handle, iatom, ihalo, max_natoms_local, natoms_global, natoms_local, nelectrons, &
     853             :          nhalo_atoms, nkinds_global, owner_id_in_halo
     854          10 :       INTEGER, DIMENSION(:), POINTER                     :: halo_atoms, local_atoms
     855             :       REAL(KIND=dp)                                      :: cost
     856          10 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: pair_radii
     857          10 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: rcut
     858             :       TYPE(cell_type), POINTER                           :: cell
     859             :       TYPE(fb_atomic_halo_list_obj)                      :: atomic_halos
     860          10 :       TYPE(fb_atomic_halo_obj), DIMENSION(:), POINTER    :: halos
     861             :       TYPE(mp_para_env_type), POINTER                    :: para_env
     862          10 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     863          10 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     864             : 
     865          10 :       CALL timeset(routineN, handle)
     866             : 
     867          10 :       CPASSERT(fb_env_has_data(fb_env))
     868             : 
     869          10 :       NULLIFY (cell, halos, halo_atoms, rcut, particle_set, para_env, &
     870          10 :                qs_kind_set, local_atoms)
     871          10 :       CALL fb_atomic_halo_list_nullify(atomic_halos)
     872             : 
     873             :       ! get relevant data from fb_env
     874             :       CALL fb_env_get(fb_env=fb_env, &
     875             :                       rcut=rcut, &
     876             :                       local_atoms=local_atoms, &
     877          10 :                       nlocal_atoms=natoms_local)
     878             : 
     879             :       ! create atomic_halos
     880          10 :       CALL fb_atomic_halo_list_create(atomic_halos)
     881             : 
     882             :       ! get the number of atoms and kinds:
     883             :       CALL get_qs_env(qs_env=qs_env, &
     884             :                       natom=natoms_global, &
     885             :                       particle_set=particle_set, &
     886             :                       qs_kind_set=qs_kind_set, &
     887             :                       nkind=nkinds_global, &
     888             :                       para_env=para_env, &
     889          10 :                       cell=cell)
     890             : 
     891             :       ! get the maximum number of local atoms across the procs.
     892          10 :       max_natoms_local = natoms_local
     893          10 :       CALL para_env%max(max_natoms_local)
     894             : 
     895             :       ! create the halos, one for each local atom
     896          70 :       ALLOCATE (halos(natoms_local))
     897          50 :       DO ihalo = 1, natoms_local
     898          40 :          CALL fb_atomic_halo_nullify(halos(ihalo))
     899          50 :          CALL fb_atomic_halo_create(halos(ihalo))
     900             :       END DO
     901             :       CALL fb_atomic_halo_list_set(atomic_halos=atomic_halos, &
     902             :                                    nhalos=natoms_local, &
     903          10 :                                    max_nhalos=max_natoms_local)
     904             :       ! build halos
     905          40 :       ALLOCATE (pair_radii(nkinds_global, nkinds_global))
     906          10 :       CALL fb_build_pair_radii(rcut, nkinds_global, pair_radii)
     907          10 :       ihalo = 0
     908          50 :       DO iatom = 1, natoms_local
     909          40 :          ihalo = ihalo + 1
     910             :          CALL fb_atomic_halo_build_halo_atoms(local_atoms(iatom), &
     911             :                                               particle_set, &
     912             :                                               cell, &
     913             :                                               pair_radii, &
     914             :                                               halo_atoms, &
     915             :                                               nhalo_atoms, &
     916          40 :                                               owner_id_in_halo)
     917             :          CALL fb_atomic_halo_set(atomic_halo=halos(ihalo), &
     918             :                                  owner_atom=local_atoms(iatom), &
     919             :                                  owner_id_in_halo=owner_id_in_halo, &
     920             :                                  natoms=nhalo_atoms, &
     921          40 :                                  halo_atoms=halo_atoms)
     922             :          ! prepare halo_atoms for another halo, do not deallocate, as
     923             :          ! original data is being pointed at by the atomic halo data
     924             :          ! structure
     925          40 :          NULLIFY (halo_atoms)
     926             :          ! calculate the number of electrons in each halo
     927             :          nelectrons = fb_atomic_halo_nelectrons_estimate_Z(halos(ihalo), &
     928          40 :                                                            particle_set)
     929             :          ! calculate cost
     930          40 :          cost = fb_atomic_halo_cost(halos(ihalo), particle_set, qs_kind_set)
     931             :          CALL fb_atomic_halo_set(atomic_halo=halos(ihalo), &
     932             :                                  nelectrons=nelectrons, &
     933          40 :                                  cost=cost)
     934             :          ! sort atomic halo
     935          90 :          CALL fb_atomic_halo_sort(halos(ihalo))
     936             :       END DO ! iatom
     937          10 :       DEALLOCATE (pair_radii)
     938             : 
     939             :       ! finalise
     940             :       CALL fb_atomic_halo_list_set(atomic_halos=atomic_halos, &
     941          10 :                                    halos=halos)
     942             :       CALL fb_env_set(fb_env=fb_env, &
     943          10 :                       atomic_halos=atomic_halos)
     944             : 
     945             :       ! print info
     946             :       CALL fb_atomic_halo_list_write_info(atomic_halos, &
     947             :                                           para_env, &
     948          10 :                                           scf_section)
     949             : 
     950          10 :       CALL timestop(handle)
     951             : 
     952          20 :    END SUBROUTINE fb_env_build_atomic_halos
     953             : 
     954             : ! **************************************************************************************************
     955             : !> \brief Automatically construct the trial functiosn used for generating
     956             : !>        the filter matrix. It tries to use the single zeta subset from
     957             : !>        the system GTO basis set as the trial functions
     958             : !> \param fb_env : the filter matrix environment
     959             : !> \param qs_env : quickstep environment
     960             : !> \param maxocc : maximum occupancy for an orbital
     961             : !> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
     962             : ! **************************************************************************************************
     963          80 :    SUBROUTINE fb_env_build_trial_fns_auto(fb_env, qs_env, maxocc)
     964             : 
     965             :       TYPE(fb_env_obj), INTENT(INOUT)                    :: fb_env
     966             :       TYPE(qs_environment_type), POINTER                 :: qs_env
     967             :       REAL(KIND=dp), INTENT(IN)                          :: maxocc
     968             : 
     969             :       CHARACTER(len=*), PARAMETER :: routineN = 'fb_env_build_trial_fns_auto'
     970             : 
     971             :       INTEGER                                            :: counter, handle, icgf, ico, ikind, iset, &
     972             :                                                             ishell, itrial, lshell, max_n_trial, &
     973             :                                                             nkinds, nset, old_lshell
     974          80 :       INTEGER, DIMENSION(:), POINTER                     :: lmax, nfunctions, nshell
     975          80 :       INTEGER, DIMENSION(:, :), POINTER                  :: functions
     976             :       REAL(KIND=dp)                                      :: zeff
     977             :       TYPE(dft_control_type), POINTER                    :: dft_control
     978             :       TYPE(fb_trial_fns_obj)                             :: trial_fns
     979          80 :       TYPE(gto_basis_set_p_type), DIMENSION(:), POINTER  :: basis_set_list
     980             :       TYPE(gto_basis_set_type), POINTER                  :: basis_set
     981          80 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     982             : 
     983          80 :       CALL timeset(routineN, handle)
     984             : 
     985          80 :       CPASSERT(fb_env_has_data(fb_env))
     986          80 :       NULLIFY (nfunctions, functions, basis_set, basis_set_list, qs_kind_set, dft_control)
     987          80 :       CALL fb_trial_fns_nullify(trial_fns)
     988             : 
     989             :       ! create a new trial_fn object
     990          80 :       CALL fb_trial_fns_create(trial_fns)
     991             : 
     992             :       CALL get_qs_env(qs_env=qs_env, &
     993             :                       qs_kind_set=qs_kind_set, &
     994          80 :                       dft_control=dft_control)
     995             : 
     996          80 :       nkinds = SIZE(qs_kind_set)
     997             : 
     998             :       ! reading from the other parts of the code, it seemed that
     999             :       ! aux_fit_basis_set is only used when do_admm is TRUE. This can be
    1000             :       ! seen from the calls to generate_qs_task_list subroutine in
    1001             :       ! qs_create_task_list, found in qs_environment_methods.F:
    1002             :       ! basis_type is only set as input parameter for do_admm
    1003             :       ! calculations, and if not set, the task list is generated using
    1004             :       ! the default basis_set="ORB".
    1005         320 :       ALLOCATE (basis_set_list(nkinds))
    1006          80 :       IF (dft_control%do_admm) THEN
    1007           0 :          CALL basis_set_list_setup(basis_set_list, "AUX_FIT", qs_kind_set)
    1008             :       ELSE
    1009          80 :          CALL basis_set_list_setup(basis_set_list, "ORB", qs_kind_set)
    1010             :       END IF
    1011             : 
    1012         240 :       ALLOCATE (nfunctions(nkinds))
    1013         160 :       nfunctions = 0
    1014             : 
    1015         160 :       DO ikind = 1, nkinds
    1016             :          ! "gto = gaussian type orbital"
    1017          80 :          basis_set => basis_set_list(ikind)%gto_basis_set
    1018             :          CALL get_gto_basis_set(gto_basis_set=basis_set, &
    1019             :                                 nset=nset, &
    1020             :                                 lmax=lmax, &
    1021          80 :                                 nshell=nshell)
    1022             :          CALL get_qs_kind(qs_kind=qs_kind_set(ikind), &
    1023          80 :                           zeff=zeff)
    1024             : 
    1025         160 :          bset1: DO iset = 1, nset
    1026             : !          old_lshell = lmax(iset)
    1027          80 :             old_lshell = -1
    1028         320 :             DO ishell = 1, nshell(iset)
    1029         240 :                lshell = basis_set%l(ishell, iset)
    1030         240 :                counter = 0
    1031             :                ! loop over orbitals within the same l
    1032         640 :                DO ico = ncoset(lshell - 1) + 1, ncoset(lshell)
    1033         400 :                   counter = counter + 1
    1034             :                   ! only include the first zeta orbitals
    1035         640 :                   IF ((lshell .GT. old_lshell) .AND. (counter .LE. nco(lshell))) THEN
    1036         320 :                      nfunctions(ikind) = nfunctions(ikind) + 1
    1037             :                   END IF
    1038             :                END DO
    1039             :                ! we have got enough trial functions when we have enough
    1040             :                ! basis functions to accommodate the number of electrons,
    1041             :                ! AND that that we have included all the first zeta
    1042             :                ! orbitals of an angular momentum quantum number l
    1043         240 :                IF (((lshell .GT. old_lshell) .OR. (lshell .EQ. lmax(iset))) .AND. &
    1044             :                    (maxocc*REAL(nfunctions(ikind), dp) .GE. zeff)) THEN
    1045             :                   EXIT bset1
    1046             :                END IF
    1047         160 :                old_lshell = lshell
    1048             :             END DO
    1049             :          END DO bset1
    1050             :       END DO ! ikind
    1051             : 
    1052             :       ! now that we have the number of trial functions get the trial
    1053             :       ! functions
    1054         160 :       max_n_trial = MAXVAL(nfunctions)
    1055         320 :       ALLOCATE (functions(max_n_trial, nkinds))
    1056         480 :       functions(:, :) = 0
    1057             :       ! redo the loops to get the trial function indices within the basis set
    1058         160 :       DO ikind = 1, nkinds
    1059             :          ! "gto = gaussian type orbital"
    1060          80 :          basis_set => basis_set_list(ikind)%gto_basis_set
    1061             :          CALL get_gto_basis_set(gto_basis_set=basis_set, &
    1062             :                                 nset=nset, &
    1063             :                                 lmax=lmax, &
    1064          80 :                                 nshell=nshell)
    1065             :          CALL get_qs_kind(qs_kind=qs_kind_set(ikind), &
    1066          80 :                           zeff=zeff)
    1067          80 :          icgf = 0
    1068          80 :          itrial = 0
    1069         160 :          bset2: DO iset = 1, nset
    1070          80 :             old_lshell = -1
    1071         320 :             DO ishell = 1, nshell(iset)
    1072         240 :                lshell = basis_set%l(ishell, iset)
    1073         240 :                counter = 0
    1074             :                ! loop over orbitals within the same l
    1075         640 :                DO ico = ncoset(lshell - 1) + 1, ncoset(lshell)
    1076         400 :                   icgf = icgf + 1
    1077         400 :                   counter = counter + 1
    1078             :                   ! only include the first zeta orbitals
    1079         640 :                   IF ((lshell .GT. old_lshell) .AND. (counter .LE. nco(lshell))) THEN
    1080         320 :                      itrial = itrial + 1
    1081         320 :                      functions(itrial, ikind) = icgf
    1082             :                   END IF
    1083             :                END DO
    1084             :                ! we have got enough trial functions when we have more
    1085             :                ! basis functions than the number of electrons (obtained
    1086             :                ! from atomic z), AND that that we have included all the
    1087             :                ! first zeta orbitals of an angular momentum quantum
    1088             :                ! number l
    1089         240 :                IF (((lshell .GT. old_lshell) .OR. (lshell .EQ. lmax(iset))) .AND. &
    1090             :                    (maxocc*REAL(itrial, dp) .GE. zeff)) THEN
    1091             :                   EXIT bset2
    1092             :                END IF
    1093         160 :                old_lshell = lshell
    1094             :             END DO
    1095             :          END DO bset2
    1096             :       END DO ! ikind
    1097             : 
    1098             :       ! set trial_functions
    1099             :       CALL fb_trial_fns_set(trial_fns=trial_fns, &
    1100             :                             nfunctions=nfunctions, &
    1101          80 :                             functions=functions)
    1102             :       ! set fb_env
    1103             :       CALL fb_env_set(fb_env=fb_env, &
    1104          80 :                       trial_fns=trial_fns)
    1105          80 :       CALL fb_trial_fns_release(trial_fns)
    1106             : 
    1107             :       ! cleanup
    1108          80 :       DEALLOCATE (basis_set_list)
    1109             : 
    1110          80 :       CALL timestop(handle)
    1111             : 
    1112          80 :    END SUBROUTINE fb_env_build_trial_fns_auto
    1113             : 
    1114             : ! **************************************************************************************************
    1115             : !> \brief Copy the sparse structure of a DBCSR matrix to another, this
    1116             : !>        means the other matrix will have the same number of blocks
    1117             : !>        and their corresponding logical locations allocated, although
    1118             : !>        the blocks does not have to be the same size as the original
    1119             : !> \param matrix_out : DBCSR matrix whose blocks are to be allocated
    1120             : !> \param matrix_in  : DBCSR matrix with existing sparse structure that
    1121             : !>                     is to be copied
    1122             : !> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
    1123             : ! **************************************************************************************************
    1124          80 :    SUBROUTINE fb_dbcsr_copy_sparse_struct(matrix_out, matrix_in)
    1125             : 
    1126             :       TYPE(dbcsr_type), INTENT(INOUT)                    :: matrix_out
    1127             :       TYPE(dbcsr_type), INTENT(IN)                       :: matrix_in
    1128             : 
    1129             :       INTEGER                                            :: iatom, iblk, jatom, nblkcols_total, &
    1130             :                                                             nblkrows_total, nblks
    1131          80 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: cols, rows
    1132          80 :       REAL(dp), DIMENSION(:, :), POINTER                 :: mat_block
    1133             :       TYPE(dbcsr_iterator_type)                          :: iter
    1134             : 
    1135             :       CALL dbcsr_get_info(matrix=matrix_in, &
    1136             :                           nblkrows_total=nblkrows_total, &
    1137          80 :                           nblkcols_total=nblkcols_total)
    1138             : 
    1139          80 :       nblks = nblkrows_total*nblkcols_total
    1140         240 :       ALLOCATE (rows(nblks))
    1141         160 :       ALLOCATE (cols(nblks))
    1142        5200 :       rows(:) = 0
    1143        5200 :       cols(:) = 0
    1144          80 :       iblk = 0
    1145          80 :       nblks = 0
    1146          80 :       CALL dbcsr_iterator_start(iter, matrix_in)
    1147        1520 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
    1148        1440 :          CALL dbcsr_iterator_next_block(iter, iatom, jatom, mat_block, iblk)
    1149        1440 :          rows(iblk) = iatom
    1150        1440 :          cols(iblk) = jatom
    1151        1440 :          nblks = nblks + 1
    1152             :       END DO
    1153          80 :       CALL dbcsr_iterator_stop(iter)
    1154          80 :       CALL dbcsr_reserve_blocks(matrix_out, rows(1:nblks), cols(1:nblks))
    1155          80 :       CALL dbcsr_finalize(matrix_out)
    1156             : 
    1157             :       ! cleanup
    1158          80 :       DEALLOCATE (rows)
    1159          80 :       DEALLOCATE (cols)
    1160             : 
    1161         160 :    END SUBROUTINE fb_dbcsr_copy_sparse_struct
    1162             : 
    1163             : ! **************************************************************************************************
    1164             : !> \brief Write out parameters used for the filter matrix method to
    1165             : !>        output
    1166             : !> \param fb_env : the filter matrix environment
    1167             : !> \param qs_env : quickstep environment
    1168             : !> \param scf_section : SCF input section
    1169             : !> \author Lianheng Tong (LT) lianheng.tong@kcl.ac.uk
    1170             : ! **************************************************************************************************
    1171          20 :    SUBROUTINE fb_env_write_info(fb_env, qs_env, scf_section)
    1172             :       TYPE(fb_env_obj), INTENT(IN)                       :: fb_env
    1173             :       TYPE(qs_environment_type), POINTER                 :: qs_env
    1174             :       TYPE(section_vals_type), POINTER                   :: scf_section
    1175             : 
    1176             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'fb_env_write_info'
    1177             : 
    1178             :       CHARACTER(LEN=2)                                   :: element_symbol
    1179             :       INTEGER                                            :: handle, ikind, nkinds, unit_nr
    1180             :       LOGICAL                                            :: collective_com
    1181             :       REAL(KIND=dp)                                      :: auto_cutoff_scale, filter_temperature
    1182          10 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: rcut
    1183          10 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
    1184             :       TYPE(cp_logger_type), POINTER                      :: logger
    1185             : 
    1186          10 :       CALL timeset(routineN, handle)
    1187             : 
    1188          10 :       NULLIFY (rcut, atomic_kind_set, logger)
    1189             : 
    1190             :       CALL get_qs_env(qs_env=qs_env, &
    1191          10 :                       atomic_kind_set=atomic_kind_set)
    1192             :       CALL fb_env_get(fb_env=fb_env, &
    1193             :                       filter_temperature=filter_temperature, &
    1194             :                       auto_cutoff_scale=auto_cutoff_scale, &
    1195             :                       rcut=rcut, &
    1196          10 :                       collective_com=collective_com)
    1197             : 
    1198          10 :       nkinds = SIZE(atomic_kind_set)
    1199             : 
    1200          10 :       logger => cp_get_default_logger()
    1201             :       unit_nr = cp_print_key_unit_nr(logger, scf_section, &
    1202             :                                      "PRINT%FILTER_MATRIX", &
    1203          10 :                                      extension="")
    1204          10 :       IF (unit_nr > 0) THEN
    1205           5 :          IF (collective_com) THEN
    1206             :             WRITE (UNIT=unit_nr, FMT="(/,A,T71,A)") &
    1207           1 :                " FILTER_MAT_DIAG| MPI communication method:", &
    1208           2 :                "Collective"
    1209             :          ELSE
    1210             :             WRITE (UNIT=unit_nr, FMT="(/,A,T71,A)") &
    1211           4 :                " FILTER_MAT_DIAG| MPI communication method:", &
    1212           8 :                "At each step"
    1213             :          END IF
    1214             :          WRITE (UNIT=unit_nr, FMT="(A,T71,g10.4)") &
    1215           5 :             " FILTER_MAT_DIAG| Filter temperature [K]:", &
    1216          10 :             cp_unit_from_cp2k(filter_temperature, "K")
    1217             :          WRITE (UNIT=unit_nr, FMT="(A,T71,f10.4)") &
    1218           5 :             " FILTER_MAT_DIAG| Filter temperature [a.u.]:", &
    1219          10 :             filter_temperature
    1220             :          WRITE (UNIT=unit_nr, FMT="(A,T71,f10.4)") &
    1221           5 :             " FILTER_MAT_DIAG| Auto atomic cutoff radius scale:", &
    1222          10 :             auto_cutoff_scale
    1223             :          WRITE (UNIT=unit_nr, FMT="(A)") &
    1224           5 :             " FILTER_MAT_DIAG| atomic cutoff radii [a.u.]"
    1225          10 :          DO ikind = 1, nkinds
    1226             :             CALL get_atomic_kind(atomic_kind=atomic_kind_set(ikind), &
    1227           5 :                                  element_symbol=element_symbol)
    1228             :             WRITE (UNIT=unit_nr, FMT="(A,A,T71,f10.4)") &
    1229          10 :                " FILTER_MAT_DIAG|   ", element_symbol, rcut(ikind)
    1230             :          END DO ! ikind
    1231             :       END IF
    1232             :       CALL cp_print_key_finished_output(unit_nr, logger, scf_section, &
    1233          10 :                                         "PRINT%FILTER_MATRIX")
    1234             : 
    1235          10 :       CALL timestop(handle)
    1236             : 
    1237          10 :    END SUBROUTINE fb_env_write_info
    1238             : 
    1239             : END MODULE qs_fb_env_methods

Generated by: LCOV version 1.15