LCOV - code coverage report
Current view: top level - src - almo_scf_qs.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:b4bd748) Lines: 435 535 81.3 %
Date: 2025-03-09 07:56:22 Functions: 11 11 100.0 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2025 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \brief Interface between ALMO SCF and QS
      10             : !> \par History
      11             : !>       2011.05 created [Rustam Z Khaliullin]
      12             : !> \author Rustam Z Khaliullin
      13             : ! **************************************************************************************************
      14             : MODULE almo_scf_qs
      15             :    USE almo_scf_types,                  ONLY: almo_mat_dim_aobasis,&
      16             :                                               almo_mat_dim_occ,&
      17             :                                               almo_mat_dim_virt,&
      18             :                                               almo_mat_dim_virt_disc,&
      19             :                                               almo_mat_dim_virt_full,&
      20             :                                               almo_scf_env_type
      21             :    USE atomic_kind_types,               ONLY: get_atomic_kind
      22             :    USE cell_types,                      ONLY: cell_type,&
      23             :                                               pbc
      24             :    USE cp_control_types,                ONLY: dft_control_type
      25             :    USE cp_dbcsr_api,                    ONLY: &
      26             :         dbcsr_complete_redistribute, dbcsr_copy, dbcsr_create, dbcsr_desymmetrize, &
      27             :         dbcsr_distribution_get, dbcsr_distribution_new, dbcsr_distribution_release, &
      28             :         dbcsr_distribution_type, dbcsr_filter, dbcsr_finalize, dbcsr_get_block_p, dbcsr_get_info, &
      29             :         dbcsr_get_num_blocks, dbcsr_get_stored_coordinates, dbcsr_multiply, dbcsr_p_type, &
      30             :         dbcsr_put_block, dbcsr_release, dbcsr_set, dbcsr_type, dbcsr_type_no_symmetry, &
      31             :         dbcsr_work_create
      32             :    USE cp_dbcsr_cp2k_link,              ONLY: cp_dbcsr_alloc_block_from_nbl
      33             :    USE cp_dbcsr_operations,             ONLY: dbcsr_allocate_matrix_set
      34             :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      35             :                                               cp_fm_struct_release,&
      36             :                                               cp_fm_struct_type
      37             :    USE cp_fm_types,                     ONLY: cp_fm_create,&
      38             :                                               cp_fm_release,&
      39             :                                               cp_fm_type
      40             :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      41             :                                               cp_logger_get_default_unit_nr,&
      42             :                                               cp_logger_type
      43             :    USE cp_units,                        ONLY: cp_unit_to_cp2k
      44             :    USE input_constants,                 ONLY: almo_constraint_ao_overlap,&
      45             :                                               almo_constraint_block_diagonal,&
      46             :                                               almo_constraint_distance,&
      47             :                                               almo_domain_layout_molecular,&
      48             :                                               almo_mat_distr_atomic,&
      49             :                                               almo_mat_distr_molecular,&
      50             :                                               do_bondparm_covalent,&
      51             :                                               do_bondparm_vdw
      52             :    USE kinds,                           ONLY: dp
      53             :    USE message_passing,                 ONLY: mp_comm_type
      54             :    USE molecule_types,                  ONLY: get_molecule_set_info,&
      55             :                                               molecule_type
      56             :    USE particle_types,                  ONLY: particle_type
      57             :    USE qs_energy_types,                 ONLY: qs_energy_type
      58             :    USE qs_environment_types,            ONLY: get_qs_env,&
      59             :                                               qs_environment_type,&
      60             :                                               set_qs_env
      61             :    USE qs_ks_methods,                   ONLY: qs_ks_update_qs_env
      62             :    USE qs_ks_types,                     ONLY: qs_ks_did_change,&
      63             :                                               qs_ks_env_type,&
      64             :                                               set_ks_env
      65             :    USE qs_mo_types,                     ONLY: allocate_mo_set,&
      66             :                                               deallocate_mo_set,&
      67             :                                               init_mo_set,&
      68             :                                               mo_set_type
      69             :    USE qs_neighbor_list_types,          ONLY: get_iterator_info,&
      70             :                                               neighbor_list_iterate,&
      71             :                                               neighbor_list_iterator_create,&
      72             :                                               neighbor_list_iterator_p_type,&
      73             :                                               neighbor_list_iterator_release,&
      74             :                                               neighbor_list_set_p_type
      75             :    USE qs_rho_methods,                  ONLY: qs_rho_update_rho
      76             :    USE qs_rho_types,                    ONLY: qs_rho_get,&
      77             :                                               qs_rho_type
      78             :    USE qs_scf_types,                    ONLY: qs_scf_env_type,&
      79             :                                               scf_env_create
      80             : #include "./base/base_uses.f90"
      81             : 
      82             :    IMPLICIT NONE
      83             : 
      84             :    PRIVATE
      85             : 
      86             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'almo_scf_qs'
      87             : 
      88             :    PUBLIC :: matrix_almo_create, &
      89             :              almo_scf_construct_quencher, &
      90             :              calculate_w_matrix_almo, &
      91             :              init_almo_ks_matrix_via_qs, &
      92             :              almo_scf_update_ks_energy, &
      93             :              construct_qs_mos, &
      94             :              matrix_qs_to_almo, &
      95             :              almo_dm_to_almo_ks, &
      96             :              almo_dm_to_qs_env
      97             : 
      98             : CONTAINS
      99             : 
     100             : ! **************************************************************************************************
     101             : !> \brief create the ALMO matrix templates
     102             : !> \param matrix_new ...
     103             : !> \param matrix_qs ...
     104             : !> \param almo_scf_env ...
     105             : !> \param name_new ...
     106             : !> \param size_keys ...
     107             : !> \param symmetry_new ...
     108             : !> \param spin_key ...
     109             : !> \param init_domains ...
     110             : !> \par History
     111             : !>       2011.05 created [Rustam Z Khaliullin]
     112             : !> \author Rustam Z Khaliullin
     113             : ! **************************************************************************************************
     114        3284 :    SUBROUTINE matrix_almo_create(matrix_new, matrix_qs, almo_scf_env, &
     115             :                                  name_new, size_keys, symmetry_new, &
     116             :                                  spin_key, init_domains)
     117             : 
     118             :       TYPE(dbcsr_type)                                   :: matrix_new, matrix_qs
     119             :       TYPE(almo_scf_env_type), INTENT(IN)                :: almo_scf_env
     120             :       CHARACTER(len=*), INTENT(IN)                       :: name_new
     121             :       INTEGER, DIMENSION(2), INTENT(IN)                  :: size_keys
     122             :       CHARACTER, INTENT(IN)                              :: symmetry_new
     123             :       INTEGER, INTENT(IN)                                :: spin_key
     124             :       LOGICAL, INTENT(IN)                                :: init_domains
     125             : 
     126             :       CHARACTER(len=*), PARAMETER :: routineN = 'matrix_almo_create'
     127             : 
     128             :       INTEGER                                            :: dimen, handle, hold, iatom, iblock_col, &
     129             :                                                             iblock_row, imol, mynode, natoms, &
     130             :                                                             nblkrows_tot, nlength, nmols, row
     131        3284 :       INTEGER, DIMENSION(:), POINTER :: blk_distr, blk_sizes, block_sizes_new, col_blk_size, &
     132        3284 :          col_distr_new, col_sizes_new, distr_new_array, row_blk_size, row_distr_new, row_sizes_new
     133             :       LOGICAL                                            :: active, one_dim_is_mo, tr
     134        3284 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: new_block
     135             :       TYPE(dbcsr_distribution_type)                      :: dist_new, dist_qs
     136             : 
     137             : ! dimension size: AO, MO, etc
     138             : !                 almo_mat_dim_aobasis - no. of AOs,
     139             : !                 almo_mat_dim_occ     - no. of occupied MOs
     140             : !                 almo_mat_dim_domains - no. of domains
     141             : ! symmetry type: dbcsr_type_no_symmetry, dbcsr_type_symmetric,
     142             : !  dbcsr_type_antisymmetric, dbcsr_type_hermitian, dbcsr_type_antihermitian
     143             : !  (see dbcsr_lib/dbcsr_types.F for other values)
     144             : ! spin_key: either 1 or 2 (0 is allowed for matrics in the AO basis)
     145             : !    TYPE(dbcsr_iterator_type)                  :: iter
     146             : !    REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: allones
     147             : !-----------------------------------------------------------------------
     148             : 
     149        3284 :       CALL timeset(routineN, handle)
     150             : 
     151             :       ! RZK-warning The structure of the matrices can be optimized:
     152             :       ! 1. Diagonal matrices must be distributed evenly over the processes.
     153             :       !    This can be achieved by distributing cpus: 012012-rows and 001122-cols
     154             :       !    block_diagonal_flag is introduced but not used
     155             :       ! 2. Multiplication of diagonally dominant matrices will be faster
     156             :       !    if the diagonal blocks are local to the same processes.
     157             :       ! 3. Systems of molecules of drastically different sizes might need
     158             :       !    better distribution.
     159             : 
     160             :       ! obtain distribution from the qs matrix - it might be useful
     161             :       ! to get the structure of the AO dimensions
     162        3284 :       CALL dbcsr_get_info(matrix_qs, distribution=dist_qs)
     163             : 
     164        3284 :       natoms = almo_scf_env%natoms
     165        3284 :       nmols = almo_scf_env%nmolecules
     166             : 
     167        9852 :       DO dimen = 1, 2 ! 1 - row, 2 - column dimension
     168             : 
     169             :          ! distribution pattern is the same for all matrix types (ao, occ, virt)
     170        6568 :          IF (dimen == 1) THEN !rows
     171        3284 :             CALL dbcsr_distribution_get(dist_qs, row_dist=blk_distr)
     172             :          ELSE !columns
     173        3284 :             CALL dbcsr_distribution_get(dist_qs, col_dist=blk_distr)
     174             :          END IF
     175             : 
     176        6568 :          IF (size_keys(dimen) == almo_mat_dim_aobasis) THEN ! this dimension is AO
     177             : 
     178             :             ! structure of an AO dimension can be copied from matrix_qs
     179        2508 :             CALL dbcsr_get_info(matrix_qs, row_blk_size=blk_sizes)
     180             : 
     181             :             ! atomic clustering of AOs
     182        2508 :             IF (almo_scf_env%mat_distr_aos == almo_mat_distr_atomic) THEN
     183           0 :                ALLOCATE (block_sizes_new(natoms), distr_new_array(natoms))
     184           0 :                block_sizes_new(:) = blk_sizes(:)
     185           0 :                distr_new_array(:) = blk_distr(:)
     186             :                ! molecular clustering of AOs
     187        2508 :             ELSE IF (almo_scf_env%mat_distr_aos == almo_mat_distr_molecular) THEN
     188       12540 :                ALLOCATE (block_sizes_new(nmols), distr_new_array(nmols))
     189       20130 :                block_sizes_new(:) = 0
     190       45936 :                DO iatom = 1, natoms
     191             :                   block_sizes_new(almo_scf_env%domain_index_of_atom(iatom)) = &
     192             :                      block_sizes_new(almo_scf_env%domain_index_of_atom(iatom)) + &
     193       45936 :                      blk_sizes(iatom)
     194             :                END DO
     195       20130 :                DO imol = 1, nmols
     196             :                   distr_new_array(imol) = &
     197       20130 :                      blk_distr(almo_scf_env%first_atom_of_domain(imol))
     198             :                END DO
     199             :             ELSE
     200           0 :                CPABORT("Illegal distribution")
     201             :             END IF
     202             : 
     203             :          ELSE ! this dimension is not AO
     204             : 
     205             :             IF (size_keys(dimen) == almo_mat_dim_occ .OR. &
     206             :                 size_keys(dimen) == almo_mat_dim_virt .OR. &
     207        4060 :                 size_keys(dimen) == almo_mat_dim_virt_disc .OR. &
     208             :                 size_keys(dimen) == almo_mat_dim_virt_full) THEN ! this dim is MO
     209             : 
     210             :                ! atomic clustering of MOs
     211        4060 :                IF (almo_scf_env%mat_distr_mos == almo_mat_distr_atomic) THEN
     212           0 :                   nlength = natoms
     213           0 :                   ALLOCATE (block_sizes_new(nlength))
     214           0 :                   block_sizes_new(:) = 0
     215             :                   IF (size_keys(dimen) == almo_mat_dim_occ) THEN
     216             :                      ! currently distributing atomic distr of mos is not allowed
     217             :                      ! RZK-warning define nocc_of_atom and nvirt_atom to implement it
     218             :                      !block_sizes_new(:)=almo_scf_env%nocc_of_atom(:,spin_key)
     219             :                   ELSE IF (size_keys(dimen) == almo_mat_dim_virt) THEN
     220             :                      !block_sizes_new(:)=almo_scf_env%nvirt_of_atom(:,spin_key)
     221             :                   END IF
     222             :                   ! molecular clustering of MOs
     223        4060 :                ELSE IF (almo_scf_env%mat_distr_mos == almo_mat_distr_molecular) THEN
     224        4060 :                   nlength = nmols
     225       12180 :                   ALLOCATE (block_sizes_new(nlength))
     226        4060 :                   IF (size_keys(dimen) == almo_mat_dim_occ) THEN
     227       18520 :                      block_sizes_new(:) = almo_scf_env%nocc_of_domain(:, spin_key)
     228             :                      ! Handle zero-electron fragments by adding one-orbital that
     229             :                      ! must remain zero at all times
     230       18520 :                      WHERE (block_sizes_new == 0) block_sizes_new = 1
     231        1740 :                   ELSE IF (size_keys(dimen) == almo_mat_dim_virt_disc) THEN
     232           0 :                      block_sizes_new(:) = almo_scf_env%nvirt_disc_of_domain(:, spin_key)
     233        1740 :                   ELSE IF (size_keys(dimen) == almo_mat_dim_virt_full) THEN
     234        5556 :                      block_sizes_new(:) = almo_scf_env%nvirt_full_of_domain(:, spin_key)
     235        1044 :                   ELSE IF (size_keys(dimen) == almo_mat_dim_virt) THEN
     236        8334 :                      block_sizes_new(:) = almo_scf_env%nvirt_of_domain(:, spin_key)
     237             :                   END IF
     238             :                ELSE
     239           0 :                   CPABORT("Illegal distribution")
     240             :                END IF
     241             : 
     242             :             ELSE
     243             : 
     244           0 :                CPABORT("Illegal dimension")
     245             : 
     246             :             END IF ! end choosing dim size (occ, virt)
     247             : 
     248             :             ! distribution for MOs is copied from AOs
     249       12180 :             ALLOCATE (distr_new_array(nlength))
     250             :             ! atomic clustering
     251        4060 :             IF (almo_scf_env%mat_distr_mos == almo_mat_distr_atomic) THEN
     252           0 :                distr_new_array(:) = blk_distr(:)
     253             :                ! molecular clustering
     254        4060 :             ELSE IF (almo_scf_env%mat_distr_mos == almo_mat_distr_molecular) THEN
     255       32410 :                DO imol = 1, nmols
     256             :                   distr_new_array(imol) = &
     257       32410 :                      blk_distr(almo_scf_env%first_atom_of_domain(imol))
     258             :                END DO
     259             :             END IF
     260             :          END IF ! end choosing dimension size (AOs vs .NOT.AOs)
     261             : 
     262             :          ! create final arrays
     263        9852 :          IF (dimen == 1) THEN !rows
     264        3284 :             row_sizes_new => block_sizes_new
     265        3284 :             row_distr_new => distr_new_array
     266             :          ELSE !columns
     267        3284 :             col_sizes_new => block_sizes_new
     268        3284 :             col_distr_new => distr_new_array
     269             :          END IF
     270             :       END DO ! both rows and columns are done
     271             : 
     272             :       ! Create the distribution
     273             :       CALL dbcsr_distribution_new(dist_new, template=dist_qs, &
     274             :                                   row_dist=row_distr_new, col_dist=col_distr_new, &
     275        3284 :                                   reuse_arrays=.TRUE.)
     276             : 
     277             :       ! Create the matrix
     278             :       CALL dbcsr_create(matrix_new, name_new, &
     279             :                         dist_new, symmetry_new, &
     280        3284 :                         row_sizes_new, col_sizes_new, reuse_arrays=.TRUE.)
     281        3284 :       CALL dbcsr_distribution_release(dist_new)
     282             : 
     283             :       ! fill out reqired blocks with 1.0_dp to tell the dbcsr library
     284             :       ! which blocks to keep
     285        3284 :       IF (init_domains) THEN
     286             : 
     287        1312 :          CALL dbcsr_distribution_get(dist_new, mynode=mynode)
     288        1312 :          CALL dbcsr_work_create(matrix_new, work_mutable=.TRUE.)
     289             :          CALL dbcsr_get_info(matrix_new, nblkrows_total=nblkrows_tot, &
     290        1312 :                              row_blk_size=row_blk_size, col_blk_size=col_blk_size)
     291             :          ! startQQQ - this part of the code scales quadratically
     292             :          ! therefore it is replaced with a less general but linear scaling algorithm below
     293             :          ! the quadratic algorithm is kept to be re-written later
     294             :          !QQQCALL dbcsr_get_info(matrix_new, nblkrows_total=nblkrows_tot, nblkcols_total=nblkcols_tot)
     295             :          !QQQDO row = 1, nblkrows_tot
     296             :          !QQQ   DO col = 1, nblkcols_tot
     297             :          !QQQ      tr = .FALSE.
     298             :          !QQQ      iblock_row = row
     299             :          !QQQ      iblock_col = col
     300             :          !QQQ      CALL dbcsr_get_stored_coordinates(matrix_new, iblock_row, iblock_col, tr, hold)
     301             : 
     302             :          !QQQ      IF(hold.EQ.mynode) THEN
     303             :          !QQQ
     304             :          !QQQ         ! RZK-warning replace with a function which says if this
     305             :          !QQQ         ! distribution block is active or not
     306             :          !QQQ         ! Translate indeces of distribution blocks to domain blocks
     307             :          !QQQ         if (size_keys(1)==almo_mat_dim_aobasis) then
     308             :          !QQQ           domain_row=almo_scf_env%domain_index_of_ao_block(iblock_row)
     309             :          !QQQ         else if (size_keys(2)==almo_mat_dim_occ .OR. &
     310             :          !QQQ                  size_keys(2)==almo_mat_dim_virt .OR. &
     311             :          !QQQ                  size_keys(2)==almo_mat_dim_virt_disc .OR. &
     312             :          !QQQ                  size_keys(2)==almo_mat_dim_virt_full) then
     313             :          !QQQ           domain_row=almo_scf_env%domain_index_of_mo_block(iblock_row)
     314             :          !QQQ         else
     315             :          !QQQ           CPErrorMessage(cp_failure_level,routineP,"Illegal dimension")
     316             :          !QQQ           CPPrecondition(.FALSE.,cp_failure_level,routineP,failure)
     317             :          !QQQ         endif
     318             : 
     319             :          !QQQ         if (size_keys(2)==almo_mat_dim_aobasis) then
     320             :          !QQQ           domain_col=almo_scf_env%domain_index_of_ao_block(iblock_col)
     321             :          !QQQ         else if (size_keys(2)==almo_mat_dim_occ .OR. &
     322             :          !QQQ                  size_keys(2)==almo_mat_dim_virt .OR. &
     323             :          !QQQ                  size_keys(2)==almo_mat_dim_virt_disc .OR. &
     324             :          !QQQ                  size_keys(2)==almo_mat_dim_virt_full) then
     325             :          !QQQ           domain_col=almo_scf_env%domain_index_of_mo_block(iblock_col)
     326             :          !QQQ         else
     327             :          !QQQ           CPErrorMessage(cp_failure_level,routineP,"Illegal dimension")
     328             :          !QQQ           CPPrecondition(.FALSE.,cp_failure_level,routineP,failure)
     329             :          !QQQ         endif
     330             : 
     331             :          !QQQ         ! Finds if we need this block
     332             :          !QQQ         ! only the block-diagonal constraint is implemented here
     333             :          !QQQ         active=.false.
     334             :          !QQQ         if (domain_row==domain_col) active=.true.
     335             : 
     336             :          !QQQ         IF (active) THEN
     337             :          !QQQ            ALLOCATE (new_block(row_blk_size(iblock_row), col_blk_size(iblock_col)))
     338             :          !QQQ            new_block(:, :) = 1.0_dp
     339             :          !QQQ            CALL dbcsr_put_block(matrix_new, iblock_row, iblock_col, new_block)
     340             :          !QQQ            DEALLOCATE (new_block)
     341             :          !QQQ         ENDIF
     342             : 
     343             :          !QQQ      ENDIF ! mynode
     344             :          !QQQ   ENDDO
     345             :          !QQQENDDO
     346             :          !QQQtake care of zero-electron fragments
     347             :          ! endQQQ - end of the quadratic part
     348             :          ! start linear-scaling replacement:
     349             :          ! works only for molecular blocks AND molecular distributions
     350       10528 :          DO row = 1, nblkrows_tot
     351        9216 :             tr = .FALSE.
     352        9216 :             iblock_row = row
     353        9216 :             iblock_col = row
     354        9216 :             CALL dbcsr_get_stored_coordinates(matrix_new, iblock_row, iblock_col, hold)
     355             : 
     356       10528 :             IF (hold .EQ. mynode) THEN
     357             : 
     358       13824 :                active = .TRUE.
     359             : 
     360             :                one_dim_is_mo = .FALSE.
     361       13824 :                DO dimen = 1, 2 ! 1 - row, 2 - column dimension
     362       13824 :                   IF (size_keys(dimen) == almo_mat_dim_occ) one_dim_is_mo = .TRUE.
     363             :                END DO
     364        4608 :                IF (one_dim_is_mo) THEN
     365        1620 :                   IF (almo_scf_env%nocc_of_domain(row, spin_key) == 0) active = .FALSE.
     366             :                END IF
     367             : 
     368        4608 :                one_dim_is_mo = .FALSE.
     369       13824 :                DO dimen = 1, 2
     370       13824 :                   IF (size_keys(dimen) == almo_mat_dim_virt) one_dim_is_mo = .TRUE.
     371             :                END DO
     372        4608 :                IF (one_dim_is_mo) THEN
     373         405 :                   IF (almo_scf_env%nvirt_of_domain(row, spin_key) == 0) active = .FALSE.
     374             :                END IF
     375             : 
     376        4608 :                one_dim_is_mo = .FALSE.
     377       13824 :                DO dimen = 1, 2
     378       13824 :                   IF (size_keys(dimen) == almo_mat_dim_virt_disc) one_dim_is_mo = .TRUE.
     379             :                END DO
     380        4608 :                IF (one_dim_is_mo) THEN
     381           0 :                   IF (almo_scf_env%nvirt_disc_of_domain(row, spin_key) == 0) active = .FALSE.
     382             :                END IF
     383             : 
     384        4608 :                one_dim_is_mo = .FALSE.
     385       13824 :                DO dimen = 1, 2
     386       13824 :                   IF (size_keys(dimen) == almo_mat_dim_virt_full) one_dim_is_mo = .TRUE.
     387             :                END DO
     388        4608 :                IF (one_dim_is_mo) THEN
     389         405 :                   IF (almo_scf_env%nvirt_full_of_domain(row, spin_key) == 0) active = .FALSE.
     390             :                END IF
     391             : 
     392        4574 :                IF (active) THEN
     393       17136 :                   ALLOCATE (new_block(row_blk_size(iblock_row), col_blk_size(iblock_col)))
     394      837014 :                   new_block(:, :) = 1.0_dp
     395        4284 :                   CALL dbcsr_put_block(matrix_new, iblock_row, iblock_col, new_block)
     396        4284 :                   DEALLOCATE (new_block)
     397             :                END IF
     398             : 
     399             :             END IF ! mynode
     400             :          END DO
     401             :          ! end lnear-scaling replacement
     402             : 
     403             :       END IF ! init_domains
     404             : 
     405        3284 :       CALL dbcsr_finalize(matrix_new)
     406             : 
     407        3284 :       CALL timestop(handle)
     408             : 
     409        6568 :    END SUBROUTINE matrix_almo_create
     410             : 
     411             : ! **************************************************************************************************
     412             : !> \brief convert between two types of matrices: QS style to ALMO style
     413             : !> \param matrix_qs ...
     414             : !> \param matrix_almo ...
     415             : !> \param mat_distr_aos ...
     416             : !> \par History
     417             : !>       2011.06 created [Rustam Z Khaliullin]
     418             : !> \author Rustam Z Khaliullin
     419             : ! **************************************************************************************************
     420        2026 :    SUBROUTINE matrix_qs_to_almo(matrix_qs, matrix_almo, mat_distr_aos)
     421             : 
     422             :       TYPE(dbcsr_type)                                   :: matrix_qs, matrix_almo
     423             :       INTEGER                                            :: mat_distr_aos
     424             : 
     425             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'matrix_qs_to_almo'
     426             : 
     427             :       INTEGER                                            :: handle
     428             :       TYPE(dbcsr_type)                                   :: matrix_qs_nosym
     429             : 
     430        2026 :       CALL timeset(routineN, handle)
     431             :       !RZK-warning if it's not a N(AO)xN(AO) matrix then stop
     432             : 
     433        2026 :       SELECT CASE (mat_distr_aos)
     434             :       CASE (almo_mat_distr_atomic)
     435             :          ! automatic data_type conversion
     436           0 :          CALL dbcsr_copy(matrix_almo, matrix_qs)
     437             :       CASE (almo_mat_distr_molecular)
     438             :          ! desymmetrize the qs matrix
     439        2026 :          CALL dbcsr_create(matrix_qs_nosym, template=matrix_qs, matrix_type=dbcsr_type_no_symmetry)
     440        2026 :          CALL dbcsr_desymmetrize(matrix_qs, matrix_qs_nosym)
     441             : 
     442             :          ! perform the magic complete_redistribute
     443             :          ! before calling complete_redistribute set all blocks to zero
     444             :          ! otherwise the non-zero elements of the redistributed matrix,
     445             :          ! which are in zero-blocks of the original matrix, will remain
     446             :          ! in the final redistributed matrix. this is a bug in
     447             :          ! complete_redistribute. RZK-warning it should be later corrected by calling
     448             :          ! dbcsr_set to 0.0 from within complete_redistribute
     449        2026 :          CALL dbcsr_set(matrix_almo, 0.0_dp)
     450        2026 :          CALL dbcsr_complete_redistribute(matrix_qs_nosym, matrix_almo)
     451        2026 :          CALL dbcsr_release(matrix_qs_nosym)
     452             : 
     453             :       CASE DEFAULT
     454        2026 :          CPABORT("")
     455             :       END SELECT
     456             : 
     457        2026 :       CALL timestop(handle)
     458             : 
     459        2026 :    END SUBROUTINE matrix_qs_to_almo
     460             : 
     461             : ! **************************************************************************************************
     462             : !> \brief convert between two types of matrices: ALMO style to QS style
     463             : !> \param matrix_almo ...
     464             : !> \param matrix_qs ...
     465             : !> \param mat_distr_aos ...
     466             : !> \par History
     467             : !>       2011.06 created [Rustam Z Khaliullin]
     468             : !> \author Rustam Z Khaliullin
     469             : ! **************************************************************************************************
     470        1826 :    SUBROUTINE matrix_almo_to_qs(matrix_almo, matrix_qs, mat_distr_aos)
     471             :       TYPE(dbcsr_type)                                   :: matrix_almo, matrix_qs
     472             :       INTEGER, INTENT(IN)                                :: mat_distr_aos
     473             : 
     474             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'matrix_almo_to_qs'
     475             : 
     476             :       INTEGER                                            :: handle
     477             :       TYPE(dbcsr_type)                                   :: matrix_almo_redist
     478             : 
     479        1826 :       CALL timeset(routineN, handle)
     480             :       ! RZK-warning if it's not a N(AO)xN(AO) matrix then stop
     481             : 
     482        1826 :       SELECT CASE (mat_distr_aos)
     483             :       CASE (almo_mat_distr_atomic)
     484           0 :          CALL dbcsr_copy(matrix_qs, matrix_almo, keep_sparsity=.TRUE.)
     485             :       CASE (almo_mat_distr_molecular)
     486        1826 :          CALL dbcsr_create(matrix_almo_redist, template=matrix_qs)
     487        1826 :          CALL dbcsr_complete_redistribute(matrix_almo, matrix_almo_redist)
     488        1826 :          CALL dbcsr_set(matrix_qs, 0.0_dp)
     489        1826 :          CALL dbcsr_copy(matrix_qs, matrix_almo_redist, keep_sparsity=.TRUE.)
     490        1826 :          CALL dbcsr_release(matrix_almo_redist)
     491             :       CASE DEFAULT
     492        1826 :          CPABORT("")
     493             :       END SELECT
     494             : 
     495        1826 :       CALL timestop(handle)
     496             : 
     497        1826 :    END SUBROUTINE matrix_almo_to_qs
     498             : 
     499             : ! **************************************************************************************************
     500             : !> \brief Initialization of the QS and ALMO KS matrix
     501             : !> \param qs_env ...
     502             : !> \param matrix_ks ...
     503             : !> \param mat_distr_aos ...
     504             : !> \param eps_filter ...
     505             : !> \par History
     506             : !>       2011.05 created [Rustam Z Khaliullin]
     507             : !> \author Rustam Z Khaliullin
     508             : ! **************************************************************************************************
     509         116 :    SUBROUTINE init_almo_ks_matrix_via_qs(qs_env, matrix_ks, mat_distr_aos, eps_filter)
     510             : 
     511             :       TYPE(qs_environment_type), POINTER                 :: qs_env
     512             :       TYPE(dbcsr_type), DIMENSION(:)                     :: matrix_ks
     513             :       INTEGER                                            :: mat_distr_aos
     514             :       REAL(KIND=dp)                                      :: eps_filter
     515             : 
     516             :       CHARACTER(len=*), PARAMETER :: routineN = 'init_almo_ks_matrix_via_qs'
     517             : 
     518             :       INTEGER                                            :: handle, ispin, nspin
     519         116 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_qs_ks, matrix_qs_s
     520             :       TYPE(dft_control_type), POINTER                    :: dft_control
     521             :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
     522         116 :          POINTER                                         :: sab_orb
     523             :       TYPE(qs_ks_env_type), POINTER                      :: ks_env
     524             : 
     525         116 :       CALL timeset(routineN, handle)
     526             : 
     527         116 :       NULLIFY (sab_orb)
     528             : 
     529             :       ! get basic quantities from the qs_env
     530             :       CALL get_qs_env(qs_env, &
     531             :                       dft_control=dft_control, &
     532             :                       matrix_s=matrix_qs_s, &
     533             :                       matrix_ks=matrix_qs_ks, &
     534             :                       ks_env=ks_env, &
     535         116 :                       sab_orb=sab_orb)
     536             : 
     537         116 :       nspin = dft_control%nspins
     538             : 
     539             :       ! create matrix_ks in the QS env if necessary
     540         116 :       IF (.NOT. ASSOCIATED(matrix_qs_ks)) THEN
     541           0 :          CALL dbcsr_allocate_matrix_set(matrix_qs_ks, nspin)
     542           0 :          DO ispin = 1, nspin
     543           0 :             ALLOCATE (matrix_qs_ks(ispin)%matrix)
     544             :             CALL dbcsr_create(matrix_qs_ks(ispin)%matrix, &
     545           0 :                               template=matrix_qs_s(1)%matrix)
     546           0 :             CALL cp_dbcsr_alloc_block_from_nbl(matrix_qs_ks(ispin)%matrix, sab_orb)
     547           0 :             CALL dbcsr_set(matrix_qs_ks(ispin)%matrix, 0.0_dp)
     548             :          END DO
     549           0 :          CALL set_ks_env(ks_env, matrix_ks=matrix_qs_ks)
     550             :       END IF
     551             : 
     552             :       ! copy to ALMO
     553         232 :       DO ispin = 1, nspin
     554         116 :          CALL matrix_qs_to_almo(matrix_qs_ks(ispin)%matrix, matrix_ks(ispin), mat_distr_aos)
     555         232 :          CALL dbcsr_filter(matrix_ks(ispin), eps_filter)
     556             :       END DO
     557             : 
     558         116 :       CALL timestop(handle)
     559             : 
     560         116 :    END SUBROUTINE init_almo_ks_matrix_via_qs
     561             : 
     562             : ! **************************************************************************************************
     563             : !> \brief Create MOs in the QS env to be able to return ALMOs to QS
     564             : !> \param qs_env ...
     565             : !> \param almo_scf_env ...
     566             : !> \par History
     567             : !>       2016.12 created [Yifei Shi]
     568             : !> \author Yifei Shi
     569             : ! **************************************************************************************************
     570         348 :    SUBROUTINE construct_qs_mos(qs_env, almo_scf_env)
     571             : 
     572             :       TYPE(qs_environment_type), POINTER                 :: qs_env
     573             :       TYPE(almo_scf_env_type), INTENT(INOUT)             :: almo_scf_env
     574             : 
     575             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'construct_qs_mos'
     576             : 
     577             :       INTEGER                                            :: handle, ispin, ncol_fm, nrow_fm
     578             :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct_tmp
     579             :       TYPE(cp_fm_type)                                   :: mo_fm_copy
     580             :       TYPE(dft_control_type), POINTER                    :: dft_control
     581         116 :       TYPE(mo_set_type), DIMENSION(:), POINTER           :: mos
     582             :       TYPE(qs_scf_env_type), POINTER                     :: scf_env
     583             : 
     584         116 :       CALL timeset(routineN, handle)
     585             : 
     586             :       ! create and init scf_env (this is necessary to return MOs to qs)
     587         116 :       NULLIFY (mos, fm_struct_tmp, scf_env)
     588         116 :       ALLOCATE (scf_env)
     589         116 :       CALL scf_env_create(scf_env)
     590             : 
     591             :       !CALL qs_scf_env_initialize(qs_env, scf_env)
     592         116 :       CALL set_qs_env(qs_env, scf_env=scf_env)
     593         116 :       CALL get_qs_env(qs_env, dft_control=dft_control, mos=mos)
     594             : 
     595         116 :       CALL dbcsr_get_info(almo_scf_env%matrix_t(1), nfullrows_total=nrow_fm, nfullcols_total=ncol_fm)
     596             : 
     597             :       ! allocate and init mo_set
     598         232 :       DO ispin = 1, almo_scf_env%nspins
     599             : 
     600             :          ! Currently only fm version of mo_set is usable.
     601             :          ! First transform the matrix_t to fm version
     602             :          ! Empty the containers to prevent memory leaks
     603         116 :          CALL deallocate_mo_set(mos(ispin))
     604             :          CALL allocate_mo_set(mo_set=mos(ispin), &
     605             :                               nao=nrow_fm, &
     606             :                               nmo=ncol_fm, &
     607             :                               nelectron=almo_scf_env%nelectrons_total, &
     608             :                               n_el_f=REAL(almo_scf_env%nelectrons_total, dp), &
     609             :                               maxocc=2.0_dp, &
     610         116 :                               flexible_electron_count=dft_control%relax_multiplicity)
     611             : 
     612             :          CALL cp_fm_struct_create(fm_struct_tmp, nrow_global=nrow_fm, ncol_global=ncol_fm, &
     613             :                                   context=almo_scf_env%blacs_env, &
     614         116 :                                   para_env=almo_scf_env%para_env)
     615             : 
     616         116 :          CALL cp_fm_create(mo_fm_copy, fm_struct_tmp, name="t_orthogonal_converted_to_fm")
     617         116 :          CALL cp_fm_struct_release(fm_struct_tmp)
     618             :          !CALL copy_dbcsr_to_fm(almo_scf_env%matrix_t(ispin), mo_fm_copy)
     619             : 
     620         116 :          CALL init_mo_set(mos(ispin), fm_ref=mo_fm_copy, name='fm_mo')
     621             : 
     622         348 :          CALL cp_fm_release(mo_fm_copy)
     623             : 
     624             :       END DO
     625             : 
     626         116 :       CALL timestop(handle)
     627             : 
     628         116 :    END SUBROUTINE construct_qs_mos
     629             : 
     630             : ! **************************************************************************************************
     631             : !> \brief return density matrix to the qs_env
     632             : !> \param qs_env ...
     633             : !> \param matrix_p ...
     634             : !> \param mat_distr_aos ...
     635             : !> \par History
     636             : !>       2011.05 created [Rustam Z Khaliullin]
     637             : !> \author Rustam Z Khaliullin
     638             : ! **************************************************************************************************
     639        1760 :    SUBROUTINE almo_dm_to_qs_env(qs_env, matrix_p, mat_distr_aos)
     640             :       TYPE(qs_environment_type), POINTER                 :: qs_env
     641             :       TYPE(dbcsr_type), DIMENSION(:)                     :: matrix_p
     642             :       INTEGER, INTENT(IN)                                :: mat_distr_aos
     643             : 
     644             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'almo_dm_to_qs_env'
     645             : 
     646             :       INTEGER                                            :: handle, ispin, nspins
     647        1760 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: rho_ao
     648             :       TYPE(qs_rho_type), POINTER                         :: rho
     649             : 
     650        1760 :       CALL timeset(routineN, handle)
     651             : 
     652        1760 :       NULLIFY (rho, rho_ao)
     653        1760 :       nspins = SIZE(matrix_p)
     654        1760 :       CALL get_qs_env(qs_env, rho=rho)
     655        1760 :       CALL qs_rho_get(rho, rho_ao=rho_ao)
     656             : 
     657             :       ! set the new density matrix
     658        3520 :       DO ispin = 1, nspins
     659             :          CALL matrix_almo_to_qs(matrix_p(ispin), &
     660             :                                 rho_ao(ispin)%matrix, &
     661        3520 :                                 mat_distr_aos)
     662             :       END DO
     663        1760 :       CALL qs_rho_update_rho(rho, qs_env=qs_env)
     664        1760 :       CALL qs_ks_did_change(qs_env%ks_env, rho_changed=.TRUE.)
     665             : 
     666        1760 :       CALL timestop(handle)
     667             : 
     668        1760 :    END SUBROUTINE almo_dm_to_qs_env
     669             : 
     670             : ! **************************************************************************************************
     671             : !> \brief uses the ALMO density matrix
     672             : !>        to compute KS matrix (inside QS environment) and the new energy
     673             : !> \param qs_env ...
     674             : !> \param matrix_p ...
     675             : !> \param energy_total ...
     676             : !> \param mat_distr_aos ...
     677             : !> \param smear ...
     678             : !> \param kTS_sum ...
     679             : !> \par History
     680             : !>       2011.05 created [Rustam Z Khaliullin]
     681             : !>       2018.09 smearing support [Ruben Staub]
     682             : !> \author Rustam Z Khaliullin
     683             : ! **************************************************************************************************
     684        1734 :    SUBROUTINE almo_dm_to_qs_ks(qs_env, matrix_p, energy_total, mat_distr_aos, smear, kTS_sum)
     685             :       TYPE(qs_environment_type), POINTER                 :: qs_env
     686             :       TYPE(dbcsr_type), DIMENSION(:)                     :: matrix_p
     687             :       REAL(KIND=dp)                                      :: energy_total
     688             :       INTEGER, INTENT(IN)                                :: mat_distr_aos
     689             :       LOGICAL, INTENT(IN), OPTIONAL                      :: smear
     690             :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: kTS_sum
     691             : 
     692             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'almo_dm_to_qs_ks'
     693             : 
     694             :       INTEGER                                            :: handle
     695             :       LOGICAL                                            :: smearing
     696             :       REAL(KIND=dp)                                      :: entropic_term
     697             :       TYPE(qs_energy_type), POINTER                      :: energy
     698             : 
     699        1734 :       CALL timeset(routineN, handle)
     700             : 
     701        1734 :       IF (PRESENT(smear)) THEN
     702        1734 :          smearing = smear
     703             :       ELSE
     704             :          smearing = .FALSE.
     705             :       END IF
     706             : 
     707        1734 :       IF (PRESENT(kTS_sum)) THEN
     708        1734 :          entropic_term = kTS_sum
     709             :       ELSE
     710             :          entropic_term = 0.0_dp
     711             :       END IF
     712             : 
     713        1734 :       NULLIFY (energy)
     714        1734 :       CALL get_qs_env(qs_env, energy=energy)
     715        1734 :       CALL almo_dm_to_qs_env(qs_env, matrix_p, mat_distr_aos)
     716             :       CALL qs_ks_update_qs_env(qs_env, calculate_forces=.FALSE., just_energy=.FALSE., &
     717        1734 :                                print_active=.TRUE.)
     718             : 
     719             :       !! Add electronic entropy contribution if smearing is requested
     720             :       !! Previous QS entropy is replaced by the sum of the entropy for each spin
     721        1734 :       IF (smearing) THEN
     722          20 :          energy%total = energy%total - energy%kTS + entropic_term
     723             :       END IF
     724             : 
     725        1734 :       energy_total = energy%total
     726             : 
     727        1734 :       CALL timestop(handle)
     728             : 
     729        1734 :    END SUBROUTINE almo_dm_to_qs_ks
     730             : 
     731             : ! **************************************************************************************************
     732             : !> \brief uses the ALMO density matrix
     733             : !>        to compute ALMO KS matrix and the new energy
     734             : !> \param qs_env ...
     735             : !> \param matrix_p ...
     736             : !> \param matrix_ks ...
     737             : !> \param energy_total ...
     738             : !> \param eps_filter ...
     739             : !> \param mat_distr_aos ...
     740             : !> \param smear ...
     741             : !> \param kTS_sum ...
     742             : !> \par History
     743             : !>       2011.05 created [Rustam Z Khaliullin]
     744             : !>       2018.09 smearing support [Ruben Staub]
     745             : !> \author Rustam Z Khaliullin
     746             : ! **************************************************************************************************
     747        1734 :    SUBROUTINE almo_dm_to_almo_ks(qs_env, matrix_p, matrix_ks, energy_total, eps_filter, &
     748             :                                  mat_distr_aos, smear, kTS_sum)
     749             : 
     750             :       TYPE(qs_environment_type), POINTER                 :: qs_env
     751             :       TYPE(dbcsr_type), DIMENSION(:)                     :: matrix_p, matrix_ks
     752             :       REAL(KIND=dp)                                      :: energy_total, eps_filter
     753             :       INTEGER, INTENT(IN)                                :: mat_distr_aos
     754             :       LOGICAL, INTENT(IN), OPTIONAL                      :: smear
     755             :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: kTS_sum
     756             : 
     757             :       CHARACTER(len=*), PARAMETER :: routineN = 'almo_dm_to_almo_ks'
     758             : 
     759             :       INTEGER                                            :: handle, ispin, nspins
     760             :       LOGICAL                                            :: smearing
     761             :       REAL(KIND=dp)                                      :: entropic_term
     762        1734 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_qs_ks
     763             : 
     764        1734 :       CALL timeset(routineN, handle)
     765             : 
     766        1734 :       IF (PRESENT(smear)) THEN
     767         464 :          smearing = smear
     768             :       ELSE
     769        1270 :          smearing = .FALSE.
     770             :       END IF
     771             : 
     772        1734 :       IF (PRESENT(kTS_sum)) THEN
     773         464 :          entropic_term = kTS_sum
     774             :       ELSE
     775        1270 :          entropic_term = 0.0_dp
     776             :       END IF
     777             : 
     778             :       ! update KS matrix in the QS env
     779             :       CALL almo_dm_to_qs_ks(qs_env, matrix_p, energy_total, mat_distr_aos, &
     780             :                             smear=smearing, &
     781        1734 :                             kTS_sum=entropic_term)
     782             : 
     783        1734 :       nspins = SIZE(matrix_ks)
     784             : 
     785             :       ! get KS matrix from the QS env and convert to the ALMO format
     786        1734 :       CALL get_qs_env(qs_env, matrix_ks=matrix_qs_ks)
     787        3468 :       DO ispin = 1, nspins
     788        1734 :          CALL matrix_qs_to_almo(matrix_qs_ks(ispin)%matrix, matrix_ks(ispin), mat_distr_aos)
     789        3468 :          CALL dbcsr_filter(matrix_ks(ispin), eps_filter)
     790             :       END DO
     791             : 
     792        1734 :       CALL timestop(handle)
     793             : 
     794        1734 :    END SUBROUTINE almo_dm_to_almo_ks
     795             : 
     796             : ! **************************************************************************************************
     797             : !> \brief update qs_env total energy
     798             : !> \param qs_env ...
     799             : !> \param energy ...
     800             : !> \param energy_singles_corr ...
     801             : !> \par History
     802             : !>       2013.03 created [Rustam Z Khaliullin]
     803             : !> \author Rustam Z Khaliullin
     804             : ! **************************************************************************************************
     805         106 :    SUBROUTINE almo_scf_update_ks_energy(qs_env, energy, energy_singles_corr)
     806             : 
     807             :       TYPE(qs_environment_type), POINTER                 :: qs_env
     808             :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: energy, energy_singles_corr
     809             : 
     810             :       TYPE(qs_energy_type), POINTER                      :: qs_energy
     811             : 
     812         106 :       CALL get_qs_env(qs_env, energy=qs_energy)
     813             : 
     814         106 :       IF (PRESENT(energy_singles_corr)) THEN
     815          26 :          qs_energy%singles_corr = energy_singles_corr
     816             :       ELSE
     817          80 :          qs_energy%singles_corr = 0.0_dp
     818             :       END IF
     819             : 
     820         106 :       IF (PRESENT(energy)) THEN
     821         106 :          qs_energy%total = energy
     822             :       END IF
     823             : 
     824         106 :       qs_energy%total = qs_energy%total + qs_energy%singles_corr
     825             : 
     826         106 :    END SUBROUTINE almo_scf_update_ks_energy
     827             : 
     828             : ! **************************************************************************************************
     829             : !> \brief Creates the matrix that imposes absolute locality on MOs
     830             : !> \param qs_env ...
     831             : !> \param almo_scf_env ...
     832             : !> \par History
     833             : !>       2011.11 created [Rustam Z. Khaliullin]
     834             : !> \author Rustam Z. Khaliullin
     835             : ! **************************************************************************************************
     836         116 :    SUBROUTINE almo_scf_construct_quencher(qs_env, almo_scf_env)
     837             : 
     838             :       TYPE(qs_environment_type), POINTER                 :: qs_env
     839             :       TYPE(almo_scf_env_type), INTENT(INOUT)             :: almo_scf_env
     840             : 
     841             :       CHARACTER(len=*), PARAMETER :: routineN = 'almo_scf_construct_quencher'
     842             : 
     843             :       CHARACTER                                          :: sym
     844             :       INTEGER :: col, contact_atom_1, contact_atom_2, domain_col, domain_map_local_entries, &
     845             :          domain_row, global_entries, global_list_length, grid1, GroupID, handle, hold, iatom, &
     846             :          iatom2, iblock_col, iblock_row, idomain, idomain2, ientry, igrid, ineig, ineighbor, &
     847             :          iNode, inode2, ipair, ispin, jatom, jatom2, jdomain2, local_list_length, &
     848             :          max_domain_neighbors, max_neig, mynode, nblkcols_tot, nblkrows_tot, nblks, ndomains, &
     849             :          neig_temp, nnode2, nNodes, row, unit_nr
     850         116 :       INTEGER, ALLOCATABLE, DIMENSION(:) :: current_number_neighbors, domain_entries_cpu, &
     851         116 :          domain_map_global, domain_map_local, first_atom_of_molecule, global_list, &
     852         116 :          last_atom_of_molecule, list_length_cpu, list_offset_cpu, local_list, offset_for_cpu
     853         116 :       INTEGER, ALLOCATABLE, DIMENSION(:, :)              :: domain_grid, domain_neighbor_list, &
     854         116 :                                                             domain_neighbor_list_excessive
     855         116 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_size, row_blk_size
     856             :       LOGICAL                                            :: already_listed, block_active, &
     857             :                                                             delayed_increment, found, &
     858             :                                                             max_neig_fails, tr
     859             :       REAL(KIND=dp)                                      :: contact1_radius, contact2_radius, &
     860             :                                                             distance, distance_squared, overlap, &
     861             :                                                             r0, r1, s0, s1, trial_distance_squared
     862         116 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: new_block
     863             :       REAL(KIND=dp), DIMENSION(3)                        :: rab
     864         116 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: p_old_block
     865             :       TYPE(cell_type), POINTER                           :: cell
     866             :       TYPE(cp_logger_type), POINTER                      :: logger
     867             :       TYPE(dbcsr_distribution_type)                      :: dist
     868         116 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s
     869             :       TYPE(dbcsr_type)                                   :: matrix_s_sym
     870         116 :       TYPE(molecule_type), DIMENSION(:), POINTER         :: molecule_set
     871             :       TYPE(mp_comm_type)                                 :: group
     872             :       TYPE(neighbor_list_iterator_p_type), &
     873         116 :          DIMENSION(:), POINTER                           :: nl_iterator, nl_iterator2
     874             :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
     875         116 :          POINTER                                         :: sab_almo
     876         116 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     877             : 
     878         116 :       CALL timeset(routineN, handle)
     879             : 
     880             :       ! get a useful output_unit
     881         116 :       logger => cp_get_default_logger()
     882         116 :       IF (logger%para_env%is_source()) THEN
     883          58 :          unit_nr = cp_logger_get_default_unit_nr(logger, local=.TRUE.)
     884             :       ELSE
     885             :          unit_nr = -1
     886             :       END IF
     887             : 
     888         116 :       ndomains = almo_scf_env%ndomains
     889             : 
     890             :       CALL get_qs_env(qs_env=qs_env, &
     891             :                       particle_set=particle_set, &
     892             :                       molecule_set=molecule_set, &
     893             :                       cell=cell, &
     894             :                       matrix_s=matrix_s, &
     895         116 :                       sab_almo=sab_almo)
     896             : 
     897             :       ! if we are dealing with molecules get info about them
     898         116 :       IF (almo_scf_env%domain_layout_mos == almo_domain_layout_molecular .OR. &
     899             :           almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
     900         348 :          ALLOCATE (first_atom_of_molecule(almo_scf_env%nmolecules))
     901         232 :          ALLOCATE (last_atom_of_molecule(almo_scf_env%nmolecules))
     902             :          CALL get_molecule_set_info(molecule_set, &
     903             :                                     mol_to_first_atom=first_atom_of_molecule, &
     904         116 :                                     mol_to_last_atom=last_atom_of_molecule)
     905             :       END IF
     906             : 
     907             :       ! create a symmetrized copy of the ao overlap
     908             :       CALL dbcsr_create(matrix_s_sym, &
     909             :                         template=almo_scf_env%matrix_s(1), &
     910         116 :                         matrix_type=dbcsr_type_no_symmetry)
     911             :       CALL dbcsr_get_info(almo_scf_env%matrix_s(1), &
     912         116 :                           matrix_type=sym)
     913         116 :       IF (sym .EQ. dbcsr_type_no_symmetry) THEN
     914           0 :          CALL dbcsr_copy(matrix_s_sym, almo_scf_env%matrix_s(1))
     915             :       ELSE
     916             :          CALL dbcsr_desymmetrize(almo_scf_env%matrix_s(1), &
     917         116 :                                  matrix_s_sym)
     918             :       END IF
     919             : 
     920         464 :       ALLOCATE (almo_scf_env%quench_t(almo_scf_env%nspins))
     921         464 :       ALLOCATE (almo_scf_env%domain_map(almo_scf_env%nspins))
     922             : 
     923             :       !DO ispin=1,almo_scf_env%nspins
     924         116 :       ispin = 1
     925             : 
     926             :       ! create the sparsity template for the occupied orbitals
     927             :       CALL matrix_almo_create(matrix_new=almo_scf_env%quench_t(ispin), &
     928             :                               matrix_qs=matrix_s(1)%matrix, &
     929             :                               almo_scf_env=almo_scf_env, &
     930             :                               name_new="T_QUENCHER", &
     931             :                               size_keys=(/almo_mat_dim_aobasis, almo_mat_dim_occ/), &
     932             :                               symmetry_new=dbcsr_type_no_symmetry, &
     933             :                               spin_key=ispin, &
     934         116 :                               init_domains=.FALSE.)
     935             : 
     936             :       ! initialize distance quencher
     937         116 :       CALL dbcsr_work_create(almo_scf_env%quench_t(ispin), work_mutable=.TRUE.)
     938             :       CALL dbcsr_get_info(almo_scf_env%quench_t(ispin), distribution=dist, &
     939         116 :                           nblkrows_total=nblkrows_tot, nblkcols_total=nblkcols_tot)
     940         116 :       CALL dbcsr_distribution_get(dist, numnodes=nNodes, group=GroupID, mynode=mynode)
     941         116 :       CALL group%set_handle(groupid)
     942             : 
     943             :       ! create global atom neighbor list from the local lists
     944             :       ! first, calculate number of local pairs
     945         116 :       local_list_length = 0
     946         116 :       CALL neighbor_list_iterator_create(nl_iterator, sab_almo)
     947       39355 :       DO WHILE (neighbor_list_iterate(nl_iterator) == 0)
     948             :          ! nnode - total number of neighbors for iatom
     949             :          ! inode - current neighbor count
     950             :          CALL get_iterator_info(nl_iterator, &
     951       39239 :                                 iatom=iatom2, jatom=jatom2, inode=inode2, nnode=nnode2)
     952             :          !WRITE(*,*) "GET INFO: ",iatom2, jatom2, inode2, nnode2
     953       39355 :          IF (inode2 == 1) THEN
     954        2001 :             local_list_length = local_list_length + nnode2
     955             :          END IF
     956             :       END DO
     957         116 :       CALL neighbor_list_iterator_release(nl_iterator)
     958             : 
     959             :       ! second, extract the local list to an array
     960         347 :       ALLOCATE (local_list(2*local_list_length))
     961       78594 :       local_list(:) = 0
     962         116 :       local_list_length = 0
     963         116 :       CALL neighbor_list_iterator_create(nl_iterator2, sab_almo)
     964       39355 :       DO WHILE (neighbor_list_iterate(nl_iterator2) == 0)
     965             :          CALL get_iterator_info(nl_iterator2, &
     966       39239 :                                 iatom=iatom2, jatom=jatom2)
     967       39239 :          local_list(2*local_list_length + 1) = iatom2
     968       39239 :          local_list(2*local_list_length + 2) = jatom2
     969       39239 :          local_list_length = local_list_length + 1
     970             :       END DO ! end loop over pairs of atoms
     971         116 :       CALL neighbor_list_iterator_release(nl_iterator2)
     972             : 
     973             :       ! third, communicate local length to the other nodes
     974         464 :       ALLOCATE (list_length_cpu(nNodes), list_offset_cpu(nNodes))
     975         116 :       CALL group%allgather(2*local_list_length, list_length_cpu)
     976             : 
     977             :       ! fourth, create a global list
     978         116 :       list_offset_cpu(1) = 0
     979         232 :       DO iNode = 2, nNodes
     980             :          list_offset_cpu(iNode) = list_offset_cpu(iNode - 1) + &
     981         232 :                                   list_length_cpu(iNode - 1)
     982             :       END DO
     983         116 :       global_list_length = list_offset_cpu(nNodes) + list_length_cpu(nNodes)
     984             : 
     985             :       ! fifth, communicate all list data
     986         348 :       ALLOCATE (global_list(global_list_length))
     987             :       CALL group%allgatherv(local_list, global_list, &
     988         116 :                             list_length_cpu, list_offset_cpu)
     989         116 :       DEALLOCATE (list_length_cpu, list_offset_cpu)
     990         116 :       DEALLOCATE (local_list)
     991             : 
     992             :       ! calculate maximum number of atoms surrounding the domain
     993         348 :       ALLOCATE (current_number_neighbors(almo_scf_env%ndomains))
     994         926 :       current_number_neighbors(:) = 0
     995         116 :       global_list_length = global_list_length/2
     996       78594 :       DO ipair = 1, global_list_length
     997       78478 :          iatom2 = global_list(2*(ipair - 1) + 1)
     998       78478 :          jatom2 = global_list(2*(ipair - 1) + 2)
     999       78478 :          idomain2 = almo_scf_env%domain_index_of_atom(iatom2)
    1000       78478 :          jdomain2 = almo_scf_env%domain_index_of_atom(jatom2)
    1001             :          ! add to the list
    1002       78478 :          current_number_neighbors(idomain2) = current_number_neighbors(idomain2) + 1
    1003             :          ! add j,i with i,j
    1004       78594 :          IF (idomain2 .NE. jdomain2) THEN
    1005       62940 :             current_number_neighbors(jdomain2) = current_number_neighbors(jdomain2) + 1
    1006             :          END IF
    1007             :       END DO
    1008         926 :       max_domain_neighbors = MAXVAL(current_number_neighbors)
    1009             : 
    1010             :       ! use the global atom neighbor list to create a global domain neighbor list
    1011         464 :       ALLOCATE (domain_neighbor_list_excessive(ndomains, max_domain_neighbors))
    1012         926 :       current_number_neighbors(:) = 1
    1013         926 :       DO ipair = 1, ndomains
    1014         926 :          domain_neighbor_list_excessive(ipair, 1) = ipair
    1015             :       END DO
    1016       78594 :       DO ipair = 1, global_list_length
    1017       78478 :          iatom2 = global_list(2*(ipair - 1) + 1)
    1018       78478 :          jatom2 = global_list(2*(ipair - 1) + 2)
    1019       78478 :          idomain2 = almo_scf_env%domain_index_of_atom(iatom2)
    1020       78478 :          jdomain2 = almo_scf_env%domain_index_of_atom(jatom2)
    1021       78478 :          already_listed = .FALSE.
    1022      325246 :          DO ineighbor = 1, current_number_neighbors(idomain2)
    1023      325246 :             IF (domain_neighbor_list_excessive(idomain2, ineighbor) .EQ. jdomain2) THEN
    1024             :                already_listed = .TRUE.
    1025             :                EXIT
    1026             :             END IF
    1027             :          END DO
    1028       78594 :          IF (.NOT. already_listed) THEN
    1029             :             ! add to the list
    1030        2710 :             current_number_neighbors(idomain2) = current_number_neighbors(idomain2) + 1
    1031        2710 :             domain_neighbor_list_excessive(idomain2, current_number_neighbors(idomain2)) = jdomain2
    1032             :             ! add j,i with i,j
    1033        2710 :             IF (idomain2 .NE. jdomain2) THEN
    1034        2710 :                current_number_neighbors(jdomain2) = current_number_neighbors(jdomain2) + 1
    1035        2710 :                domain_neighbor_list_excessive(jdomain2, current_number_neighbors(jdomain2)) = idomain2
    1036             :             END IF
    1037             :          END IF
    1038             :       END DO ! end loop over pairs of atoms
    1039         116 :       DEALLOCATE (global_list)
    1040             : 
    1041         926 :       max_domain_neighbors = MAXVAL(current_number_neighbors)
    1042         464 :       ALLOCATE (domain_neighbor_list(ndomains, max_domain_neighbors))
    1043        7164 :       domain_neighbor_list(:, :) = 0
    1044        7164 :       domain_neighbor_list(:, :) = domain_neighbor_list_excessive(:, 1:max_domain_neighbors)
    1045         116 :       DEALLOCATE (domain_neighbor_list_excessive)
    1046             : 
    1047         348 :       ALLOCATE (almo_scf_env%domain_map(ispin)%index1(ndomains))
    1048         348 :       ALLOCATE (almo_scf_env%domain_map(ispin)%pairs(max_domain_neighbors*ndomains, 2))
    1049       12824 :       almo_scf_env%domain_map(ispin)%pairs(:, :) = 0
    1050         926 :       almo_scf_env%domain_map(ispin)%index1(:) = 0
    1051         116 :       domain_map_local_entries = 0
    1052             : 
    1053             :       ! RZK-warning intermediate [0,1] quencher values are ill-defined
    1054             :       ! for molecules (not continuous and conceptually inadequate)
    1055             : 
    1056             :       CALL dbcsr_get_info(almo_scf_env%quench_t(ispin), &
    1057         116 :                           row_blk_size=row_blk_size, col_blk_size=col_blk_size)
    1058             :       ! O(N) loop over domain pairs
    1059         926 :       DO row = 1, nblkrows_tot
    1060        7156 :          DO col = 1, current_number_neighbors(row)
    1061        6230 :             tr = .FALSE.
    1062        6230 :             iblock_row = row
    1063        6230 :             iblock_col = domain_neighbor_list(row, col)
    1064             :             CALL dbcsr_get_stored_coordinates(almo_scf_env%quench_t(ispin), &
    1065        6230 :                                               iblock_row, iblock_col, hold)
    1066             : 
    1067        7040 :             IF (hold .EQ. mynode) THEN
    1068             : 
    1069             :                ! Translate indices of distribution blocks to indices of domain blocks
    1070             :                ! Rows are AOs
    1071        3115 :                domain_row = almo_scf_env%domain_index_of_ao_block(iblock_row)
    1072             :                ! Columns are electrons (i.e. MOs)
    1073        3115 :                domain_col = almo_scf_env%domain_index_of_mo_block(iblock_col)
    1074             : 
    1075        3115 :                SELECT CASE (almo_scf_env%constraint_type)
    1076             :                CASE (almo_constraint_block_diagonal)
    1077             : 
    1078           0 :                   block_active = .FALSE.
    1079             :                   ! type of electron groups
    1080           0 :                   IF (almo_scf_env%domain_layout_mos == almo_domain_layout_molecular) THEN
    1081             : 
    1082             :                      ! type of ao domains
    1083           0 :                      IF (almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1084             : 
    1085             :                         ! ao domains are molecular / electron groups are molecular
    1086           0 :                         IF (domain_row == domain_col) THEN
    1087             :                            block_active = .TRUE.
    1088             :                         END IF
    1089             : 
    1090             :                      ELSE ! ao domains are atomic
    1091             : 
    1092             :                         ! ao domains are atomic / electron groups are molecular
    1093           0 :                         CPABORT("Illegal: atomic domains and molecular groups")
    1094             : 
    1095             :                      END IF
    1096             : 
    1097             :                   ELSE ! electron groups are atomic
    1098             : 
    1099             :                      ! type of ao domains
    1100           0 :                      IF (almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1101             : 
    1102             :                         ! ao domains are molecular / electron groups are atomic
    1103           0 :                         CPABORT("Illegal: molecular domains and atomic groups")
    1104             : 
    1105             :                      ELSE
    1106             : 
    1107             :                         ! ao domains are atomic / electron groups are atomic
    1108           0 :                         IF (domain_row == domain_col) THEN
    1109             :                            block_active = .TRUE.
    1110             :                         END IF
    1111             : 
    1112             :                      END IF
    1113             : 
    1114             :                   END IF ! end type of electron groups
    1115             : 
    1116           0 :                   IF (block_active) THEN
    1117             : 
    1118           0 :                      ALLOCATE (new_block(row_blk_size(iblock_row), col_blk_size(iblock_col)))
    1119           0 :                      new_block(:, :) = 1.0_dp
    1120           0 :                      CALL dbcsr_put_block(almo_scf_env%quench_t(ispin), iblock_row, iblock_col, new_block)
    1121           0 :                      DEALLOCATE (new_block)
    1122             : 
    1123           0 :                      IF (domain_map_local_entries .GE. max_domain_neighbors*almo_scf_env%ndomains) THEN
    1124           0 :                         CPABORT("weird... max_domain_neighbors is exceeded")
    1125             :                      END IF
    1126           0 :                      almo_scf_env%domain_map(ispin)%pairs(domain_map_local_entries + 1, 1) = iblock_row
    1127           0 :                      almo_scf_env%domain_map(ispin)%pairs(domain_map_local_entries + 1, 2) = iblock_col
    1128           0 :                      domain_map_local_entries = domain_map_local_entries + 1
    1129             : 
    1130             :                   END IF
    1131             : 
    1132             :                CASE (almo_constraint_ao_overlap)
    1133             : 
    1134             :                   ! type of electron groups
    1135           0 :                   IF (almo_scf_env%domain_layout_mos == almo_domain_layout_molecular) THEN
    1136             : 
    1137             :                      ! type of ao domains
    1138           0 :                      IF (almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1139             : 
    1140             :                         ! ao domains are molecular / electron groups are molecular
    1141             : 
    1142             :                         ! compute the maximum overlap between the atoms of the two molecules
    1143           0 :                         CALL dbcsr_get_block_p(matrix_s_sym, iblock_row, iblock_col, p_old_block, found)
    1144           0 :                         IF (found) THEN
    1145             :                            !   CPErrorMessage(cp_failure_level,routineP,"S block not found")
    1146             :                            !   CPPrecondition(.FALSE.,cp_failure_level,routineP,failure)
    1147           0 :                            overlap = MAXVAL(ABS(p_old_block))
    1148             :                         ELSE
    1149             :                            overlap = 0.0_dp
    1150             :                         END IF
    1151             : 
    1152             :                      ELSE ! ao domains are atomic
    1153             : 
    1154             :                         ! ao domains are atomic / electron groups are molecular
    1155             :                         ! overlap_between_atom_and_molecule(atom=domain_row,molecule=domain_col)
    1156           0 :                         CPABORT("atomic domains and molecular groups - NYI")
    1157             : 
    1158             :                      END IF
    1159             : 
    1160             :                   ELSE ! electron groups are atomic
    1161             : 
    1162             :                      ! type of ao domains
    1163           0 :                      IF (almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1164             : 
    1165             :                         ! ao domains are molecular / electron groups are atomic
    1166             :                         ! overlap_between_atom_and_molecule(atom=domain_col,molecule=domain_row)
    1167           0 :                         CPABORT("molecular domains and atomic groups - NYI")
    1168             : 
    1169             :                      ELSE
    1170             : 
    1171             :                         ! ao domains are atomic / electron groups are atomic
    1172             :                         ! compute max overlap between atoms: domain_row and domain_col
    1173           0 :                         CALL dbcsr_get_block_p(matrix_s_sym, iblock_row, iblock_col, p_old_block, found)
    1174           0 :                         IF (found) THEN
    1175             :                            !   CPErrorMessage(cp_failure_level,routineP,"S block not found")
    1176             :                            !   CPPrecondition(.FALSE.,cp_failure_level,routineP,failure)
    1177           0 :                            overlap = MAXVAL(ABS(p_old_block))
    1178             :                         ELSE
    1179             :                            overlap = 0.0_dp
    1180             :                         END IF
    1181             : 
    1182             :                      END IF
    1183             : 
    1184             :                   END IF ! end type of electron groups
    1185             : 
    1186           0 :                   s0 = -LOG10(ABS(almo_scf_env%quencher_s0))
    1187           0 :                   s1 = -LOG10(ABS(almo_scf_env%quencher_s1))
    1188           0 :                   IF (overlap .EQ. 0.0_dp) THEN
    1189           0 :                      overlap = -LOG10(ABS(almo_scf_env%eps_filter)) + 100.0_dp
    1190             :                   ELSE
    1191           0 :                      overlap = -LOG10(overlap)
    1192             :                   END IF
    1193           0 :                   IF (s0 .LT. 0.0_dp) THEN
    1194           0 :                      CPABORT("S0 is less than zero")
    1195             :                   END IF
    1196           0 :                   IF (s1 .LE. 0.0_dp) THEN
    1197           0 :                      CPABORT("S1 is less than or equal to zero")
    1198             :                   END IF
    1199           0 :                   IF (s0 .GE. s1) THEN
    1200           0 :                      CPABORT("S0 is greater than or equal to S1")
    1201             :                   END IF
    1202             : 
    1203             :                   ! Fill in non-zero blocks if AOs are close to the electron center
    1204           0 :                   IF (overlap .LT. s1) THEN
    1205           0 :                      ALLOCATE (new_block(row_blk_size(iblock_row), col_blk_size(iblock_col)))
    1206           0 :                      IF (overlap .LE. s0) THEN
    1207           0 :                         new_block(:, :) = 1.0_dp
    1208             :                         !WRITE(*,'(A15,2I7,3F8.3,E11.3)') "INTRA-BLOCKS: ",&
    1209             :                         !   iblock_col, iblock_row, s0, s1, overlap, new_block(1,1)
    1210             :                      ELSE
    1211           0 :                         new_block(:, :) = 1.0_dp/(1.0_dp + EXP(-(s0 - s1)/(s0 - overlap) - (s0 - s1)/(overlap - s1)))
    1212             :                         !WRITE(*,'(A15,2I7,3F8.3,E11.3)') "INTER-BLOCKS: ",&
    1213             :                         !   iblock_col, iblock_row, s0, s1, overlap, new_block(1,1)
    1214             :                      END IF
    1215             : 
    1216           0 :                      IF (ABS(new_block(1, 1)) .GT. ABS(almo_scf_env%eps_filter)) THEN
    1217           0 :                         IF (domain_map_local_entries .GE. max_domain_neighbors*almo_scf_env%ndomains) THEN
    1218           0 :                            CPABORT("weird... max_domain_neighbors is exceeded")
    1219             :                         END IF
    1220           0 :                         almo_scf_env%domain_map(ispin)%pairs(domain_map_local_entries + 1, 1) = iblock_row
    1221           0 :                         almo_scf_env%domain_map(ispin)%pairs(domain_map_local_entries + 1, 2) = iblock_col
    1222           0 :                         domain_map_local_entries = domain_map_local_entries + 1
    1223             :                      END IF
    1224             : 
    1225           0 :                      CALL dbcsr_put_block(almo_scf_env%quench_t(ispin), iblock_row, iblock_col, new_block)
    1226           0 :                      DEALLOCATE (new_block)
    1227             : 
    1228             :                   END IF
    1229             : 
    1230             :                CASE (almo_constraint_distance)
    1231             : 
    1232             :                   ! type of electron groups
    1233        3115 :                   IF (almo_scf_env%domain_layout_mos == almo_domain_layout_molecular) THEN
    1234             : 
    1235             :                      ! type of ao domains
    1236        3115 :                      IF (almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1237             : 
    1238             :                         ! ao domains are molecular / electron groups are molecular
    1239             : 
    1240             :                         ! compute distance between molecules: domain_row and domain_col
    1241             :                         ! distance between molecules is defined as the smallest
    1242             :                         ! distance among all atom pairs
    1243        3115 :                         IF (domain_row == domain_col) THEN
    1244         405 :                            distance = 0.0_dp
    1245         405 :                            contact_atom_1 = first_atom_of_molecule(domain_row)
    1246         405 :                            contact_atom_2 = first_atom_of_molecule(domain_col)
    1247             :                         ELSE
    1248        2710 :                            distance_squared = 1.0E+100_dp
    1249        2710 :                            contact_atom_1 = -1
    1250        2710 :                            contact_atom_2 = -1
    1251        9034 :                            DO iatom = first_atom_of_molecule(domain_row), last_atom_of_molecule(domain_row)
    1252       26310 :                               DO jatom = first_atom_of_molecule(domain_col), last_atom_of_molecule(domain_col)
    1253       17276 :                                  rab(:) = pbc(particle_set(iatom)%r(:), particle_set(jatom)%r(:), cell)
    1254       17276 :                                  trial_distance_squared = rab(1)*rab(1) + rab(2)*rab(2) + rab(3)*rab(3)
    1255       23600 :                                  IF (trial_distance_squared .LT. distance_squared) THEN
    1256        6363 :                                     distance_squared = trial_distance_squared
    1257        6363 :                                     contact_atom_1 = iatom
    1258        6363 :                                     contact_atom_2 = jatom
    1259             :                                  END IF
    1260             :                               END DO ! jatom
    1261             :                            END DO ! iatom
    1262        2710 :                            CPASSERT(contact_atom_1 .GT. 0)
    1263        2710 :                            distance = SQRT(distance_squared)
    1264             :                         END IF
    1265             : 
    1266             :                      ELSE ! ao domains are atomic
    1267             : 
    1268             :                         ! ao domains are atomic / electron groups are molecular
    1269             :                         !distance_between_atom_and_molecule(atom=domain_row,molecule=domain_col)
    1270           0 :                         CPABORT("atomic domains and molecular groups - NYI")
    1271             : 
    1272             :                      END IF
    1273             : 
    1274             :                   ELSE ! electron groups are atomic
    1275             : 
    1276             :                      ! type of ao domains
    1277           0 :                      IF (almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1278             : 
    1279             :                         ! ao domains are molecular / electron groups are atomic
    1280             :                         !distance_between_atom_and_molecule(atom=domain_col,molecule=domain_row)
    1281           0 :                         CPABORT("molecular domains and atomic groups - NYI")
    1282             : 
    1283             :                      ELSE
    1284             : 
    1285             :                         ! ao domains are atomic / electron groups are atomic
    1286             :                         ! compute distance between atoms: domain_row and domain_col
    1287           0 :                         rab(:) = pbc(particle_set(domain_row)%r(:), particle_set(domain_col)%r(:), cell)
    1288           0 :                         distance = SQRT(rab(1)*rab(1) + rab(2)*rab(2) + rab(3)*rab(3))
    1289           0 :                         contact_atom_1 = domain_row
    1290           0 :                         contact_atom_2 = domain_col
    1291             : 
    1292             :                      END IF
    1293             : 
    1294             :                   END IF ! end type of electron groups
    1295             : 
    1296             :                   ! get atomic radii to compute distance cutoff threshold
    1297        3115 :                   IF (almo_scf_env%quencher_radius_type == do_bondparm_covalent) THEN
    1298             :                      CALL get_atomic_kind(atomic_kind=particle_set(contact_atom_1)%atomic_kind, &
    1299           0 :                                           rcov=contact1_radius)
    1300             :                      CALL get_atomic_kind(atomic_kind=particle_set(contact_atom_2)%atomic_kind, &
    1301           0 :                                           rcov=contact2_radius)
    1302        3115 :                   ELSE IF (almo_scf_env%quencher_radius_type == do_bondparm_vdw) THEN
    1303             :                      CALL get_atomic_kind(atomic_kind=particle_set(contact_atom_1)%atomic_kind, &
    1304        3115 :                                           rvdw=contact1_radius)
    1305             :                      CALL get_atomic_kind(atomic_kind=particle_set(contact_atom_2)%atomic_kind, &
    1306        3115 :                                           rvdw=contact2_radius)
    1307             :                   ELSE
    1308           0 :                      CPABORT("Illegal quencher_radius_type")
    1309             :                   END IF
    1310        3115 :                   contact1_radius = cp_unit_to_cp2k(contact1_radius, "angstrom")
    1311        3115 :                   contact2_radius = cp_unit_to_cp2k(contact2_radius, "angstrom")
    1312             : 
    1313             :                   !RZK-warning the procedure is faulty for molecules:
    1314             :                   ! the closest contacts should be found using
    1315             :                   ! the element specific radii
    1316             : 
    1317             :                   ! compute inner and outer cutoff radii
    1318        3115 :                   r0 = almo_scf_env%quencher_r0_factor*(contact1_radius + contact2_radius)
    1319             :                   !+almo_scf_env%quencher_r0_shift
    1320        3115 :                   r1 = almo_scf_env%quencher_r1_factor*(contact1_radius + contact2_radius)
    1321             :                   !+almo_scf_env%quencher_r1_shift
    1322             : 
    1323        3115 :                   IF (r0 .LT. 0.0_dp) THEN
    1324           0 :                      CPABORT("R0 is less than zero")
    1325             :                   END IF
    1326        3115 :                   IF (r1 .LE. 0.0_dp) THEN
    1327           0 :                      CPABORT("R1 is less than or equal to zero")
    1328             :                   END IF
    1329        3115 :                   IF (r0 .GT. r1) THEN
    1330           0 :                      CPABORT("R0 is greater than or equal to R1")
    1331             :                   END IF
    1332             : 
    1333             :                   ! Fill in non-zero blocks if AOs are close to the electron center
    1334        3115 :                   IF (distance .LT. r1) THEN
    1335        8676 :                      ALLOCATE (new_block(row_blk_size(iblock_row), col_blk_size(iblock_col)))
    1336        2169 :                      IF (distance .LE. r0) THEN
    1337      101401 :                         new_block(:, :) = 1.0_dp
    1338             :                         !WRITE(*,'(A15,2I7,5F8.3,E11.3)') "INTRA-BLOCKS: ",&
    1339             :                         !   iblock_col, iblock_row, contact1_radius,&
    1340             :                         !   contact2_radius, r0, r1, distance, new_block(1,1)
    1341             :                      ELSE
    1342             :                         ! remove the intermediate values from the quencher temporarily
    1343           0 :                         CPABORT("")
    1344           0 :                         new_block(:, :) = 1.0_dp/(1.0_dp + EXP((r1 - r0)/(r0 - distance) + (r1 - r0)/(r1 - distance)))
    1345             :                         !WRITE(*,'(A15,2I7,5F8.3,E11.3)') "INTER-BLOCKS: ",&
    1346             :                         !   iblock_col, iblock_row, contact1_radius,&
    1347             :                         !   contact2_radius, r0, r1, distance, new_block(1,1)
    1348             :                      END IF
    1349             : 
    1350        2169 :                      IF (ABS(new_block(1, 1)) .GT. ABS(almo_scf_env%eps_filter)) THEN
    1351        2169 :                         IF (domain_map_local_entries .GE. max_domain_neighbors*almo_scf_env%ndomains) THEN
    1352           0 :                            CPABORT("weird... max_domain_neighbors is exceeded")
    1353             :                         END IF
    1354        2169 :                         almo_scf_env%domain_map(ispin)%pairs(domain_map_local_entries + 1, 1) = iblock_row
    1355        2169 :                         almo_scf_env%domain_map(ispin)%pairs(domain_map_local_entries + 1, 2) = iblock_col
    1356        2169 :                         domain_map_local_entries = domain_map_local_entries + 1
    1357             :                      END IF
    1358             : 
    1359        2169 :                      CALL dbcsr_put_block(almo_scf_env%quench_t(ispin), iblock_row, iblock_col, new_block)
    1360        2169 :                      DEALLOCATE (new_block)
    1361             :                   END IF
    1362             : 
    1363             :                CASE DEFAULT
    1364        3115 :                   CPABORT("Illegal constraint type")
    1365             :                END SELECT
    1366             : 
    1367             :             END IF ! mynode
    1368             : 
    1369             :          END DO
    1370             :       END DO ! end O(N) loop over pairs
    1371             : 
    1372         116 :       DEALLOCATE (domain_neighbor_list)
    1373         116 :       DEALLOCATE (current_number_neighbors)
    1374             : 
    1375         116 :       CALL dbcsr_finalize(almo_scf_env%quench_t(ispin))
    1376             :       !CALL dbcsr_scale(almo_scf_env%quench_t(ispin),&
    1377             :       !        almo_scf_env%envelope_amplitude)
    1378             :       CALL dbcsr_filter(almo_scf_env%quench_t(ispin), &
    1379         116 :                         almo_scf_env%eps_filter)
    1380             : 
    1381             :       ! check that both domain_map and quench_t have the same number of entries
    1382         116 :       nblks = dbcsr_get_num_blocks(almo_scf_env%quench_t(ispin))
    1383         116 :       IF (nblks .NE. domain_map_local_entries) THEN
    1384           0 :          CPABORT("number of blocks is wrong")
    1385             :       END IF
    1386             : 
    1387             :       ! first, communicate map sizes on the other nodes
    1388         348 :       ALLOCATE (domain_entries_cpu(nNodes), offset_for_cpu(nNodes))
    1389         116 :       CALL group%allgather(2*domain_map_local_entries, domain_entries_cpu)
    1390             : 
    1391             :       ! second, create
    1392         116 :       offset_for_cpu(1) = 0
    1393         232 :       DO iNode = 2, nNodes
    1394             :          offset_for_cpu(iNode) = offset_for_cpu(iNode - 1) + &
    1395         232 :                                  domain_entries_cpu(iNode - 1)
    1396             :       END DO
    1397         116 :       global_entries = offset_for_cpu(nNodes) + domain_entries_cpu(nNodes)
    1398             : 
    1399             :       ! communicate all entries
    1400         348 :       ALLOCATE (domain_map_global(global_entries))
    1401         460 :       ALLOCATE (domain_map_local(2*domain_map_local_entries))
    1402        2285 :       DO ientry = 1, domain_map_local_entries
    1403        2169 :          domain_map_local(2*(ientry - 1) + 1) = almo_scf_env%domain_map(ispin)%pairs(ientry, 1)
    1404        2285 :          domain_map_local(2*ientry) = almo_scf_env%domain_map(ispin)%pairs(ientry, 2)
    1405             :       END DO
    1406             :       CALL group%allgatherv(domain_map_local, domain_map_global, &
    1407         116 :                             domain_entries_cpu, offset_for_cpu)
    1408         116 :       DEALLOCATE (domain_entries_cpu, offset_for_cpu)
    1409         116 :       DEALLOCATE (domain_map_local)
    1410             : 
    1411         116 :       DEALLOCATE (almo_scf_env%domain_map(ispin)%index1)
    1412         116 :       DEALLOCATE (almo_scf_env%domain_map(ispin)%pairs)
    1413         232 :       ALLOCATE (almo_scf_env%domain_map(ispin)%index1(ndomains))
    1414         464 :       ALLOCATE (almo_scf_env%domain_map(ispin)%pairs(global_entries/2, 2))
    1415        9024 :       almo_scf_env%domain_map(ispin)%pairs(:, :) = 0
    1416         926 :       almo_scf_env%domain_map(ispin)%index1(:) = 0
    1417             : 
    1418             :       ! unpack the received data into a local variable
    1419             :       ! since we do not know the maximum global number of neighbors
    1420             :       ! try one. if fails increase the maximum number and try again
    1421             :       ! until it succeeds
    1422             :       max_neig = max_domain_neighbors
    1423             :       max_neig_fails = .TRUE.
    1424         232 :       max_neig_loop: DO WHILE (max_neig_fails)
    1425         464 :          ALLOCATE (domain_grid(almo_scf_env%ndomains, 0:max_neig))
    1426        8090 :          domain_grid(:, :) = 0
    1427             :          ! init the number of collected neighbors
    1428         926 :          domain_grid(:, 0) = 1
    1429             :          ! loop over the records
    1430         116 :          global_entries = global_entries/2
    1431        4454 :          DO ientry = 1, global_entries
    1432             :             ! get the center
    1433        4338 :             grid1 = domain_map_global(2*ientry)
    1434             :             ! get the neighbor
    1435        4338 :             ineig = domain_map_global(2*(ientry - 1) + 1)
    1436             :             ! check boundaries
    1437        4338 :             IF (domain_grid(grid1, 0) .GT. max_neig) THEN
    1438             :                ! this neighbor will overstep the boundaries
    1439             :                ! stop the trial and increase the max number of neighbors
    1440           0 :                DEALLOCATE (domain_grid)
    1441           0 :                max_neig = max_neig*2
    1442           0 :                CYCLE max_neig_loop
    1443             :             END IF
    1444             :             ! for the current center loop over the collected neighbors
    1445             :             ! to insert the current record in a numerical order
    1446             :             delayed_increment = .FALSE.
    1447       18944 :             DO igrid = 1, domain_grid(grid1, 0)
    1448             :                ! compare the current neighbor with that already in the 'book'
    1449       18944 :                IF (ineig .LT. domain_grid(grid1, igrid)) THEN
    1450             :                   ! if this one is smaller then insert it here and pick up the one
    1451             :                   ! from the book to continue inserting
    1452        3172 :                   neig_temp = ineig
    1453        3172 :                   ineig = domain_grid(grid1, igrid)
    1454        3172 :                   domain_grid(grid1, igrid) = neig_temp
    1455             :                ELSE
    1456       11434 :                   IF (domain_grid(grid1, igrid) .EQ. 0) THEN
    1457             :                      ! got the empty slot now - insert the record
    1458        4338 :                      domain_grid(grid1, igrid) = ineig
    1459             :                      ! increase the record counter but do it outside the loop
    1460        4338 :                      delayed_increment = .TRUE.
    1461             :                   END IF
    1462             :                END IF
    1463             :             END DO
    1464        4454 :             IF (delayed_increment) THEN
    1465        4338 :                domain_grid(grid1, 0) = domain_grid(grid1, 0) + 1
    1466             :             ELSE
    1467             :                ! should not be here - all records must be inserted
    1468           0 :                CPABORT("all records must be inserted")
    1469             :             END IF
    1470             :          END DO
    1471         116 :          max_neig_fails = .FALSE.
    1472             :       END DO max_neig_loop
    1473         116 :       DEALLOCATE (domain_map_global)
    1474             : 
    1475         116 :       ientry = 1
    1476         926 :       DO idomain = 1, almo_scf_env%ndomains
    1477        5148 :          DO ineig = 1, domain_grid(idomain, 0) - 1
    1478        4338 :             almo_scf_env%domain_map(ispin)%pairs(ientry, 1) = domain_grid(idomain, ineig)
    1479        4338 :             almo_scf_env%domain_map(ispin)%pairs(ientry, 2) = idomain
    1480        5148 :             ientry = ientry + 1
    1481             :          END DO
    1482         926 :          almo_scf_env%domain_map(ispin)%index1(idomain) = ientry
    1483             :       END DO
    1484         116 :       DEALLOCATE (domain_grid)
    1485             : 
    1486             :       !ENDDO ! ispin
    1487         116 :       IF (almo_scf_env%nspins .EQ. 2) THEN
    1488             :          CALL dbcsr_copy(almo_scf_env%quench_t(2), &
    1489           0 :                          almo_scf_env%quench_t(1))
    1490             :          almo_scf_env%domain_map(2)%pairs(:, :) = &
    1491           0 :             almo_scf_env%domain_map(1)%pairs(:, :)
    1492             :          almo_scf_env%domain_map(2)%index1(:) = &
    1493           0 :             almo_scf_env%domain_map(1)%index1(:)
    1494             :       END IF
    1495             : 
    1496         116 :       CALL dbcsr_release(matrix_s_sym)
    1497             : 
    1498         116 :       IF (almo_scf_env%domain_layout_mos == almo_domain_layout_molecular .OR. &
    1499             :           almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1500         116 :          DEALLOCATE (first_atom_of_molecule)
    1501         116 :          DEALLOCATE (last_atom_of_molecule)
    1502             :       END IF
    1503             : 
    1504             :       !mynode = dbcsr_mp_mynode(dbcsr_distribution_mp(&
    1505             :       !   dbcsr_distribution(almo_scf_env%quench_t(ispin))))
    1506             :       !CALL dbcsr_get_info(almo_scf_env%quench_t(ispin), distribution=dist, &
    1507             :       !                    nblkrows_total=nblkrows_tot, nblkcols_total=nblkcols_tot)
    1508             :       !DO row = 1, nblkrows_tot
    1509             :       !   DO col = 1, nblkcols_tot
    1510             :       !      tr = .FALSE.
    1511             :       !      iblock_row = row
    1512             :       !      iblock_col = col
    1513             :       !      CALL dbcsr_get_stored_coordinates(almo_scf_env%quench_t(ispin),&
    1514             :       !              iblock_row, iblock_col, tr, hold)
    1515             :       !      CALL dbcsr_get_block_p(almo_scf_env%quench_t(ispin),&
    1516             :       !              row, col, p_old_block, found)
    1517             :       !      write(*,*) "RST_NOTE:", mynode, row, col, hold, found
    1518             :       !   ENDDO
    1519             :       !ENDDO
    1520             : 
    1521         116 :       CALL timestop(handle)
    1522             : 
    1523         348 :    END SUBROUTINE almo_scf_construct_quencher
    1524             : 
    1525             : ! *****************************************************************************
    1526             : !> \brief Compute matrix W (energy-weighted density matrix) that is needed
    1527             : !>        for the evaluation of forces
    1528             : !> \param matrix_w ...
    1529             : !> \param almo_scf_env ...
    1530             : !> \par History
    1531             : !>       2015.03 created [Rustam Z. Khaliullin]
    1532             : !> \author Rustam Z. Khaliullin
    1533             : ! **************************************************************************************************
    1534          66 :    SUBROUTINE calculate_w_matrix_almo(matrix_w, almo_scf_env)
    1535             :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_w
    1536             :       TYPE(almo_scf_env_type)                            :: almo_scf_env
    1537             : 
    1538             :       CHARACTER(len=*), PARAMETER :: routineN = 'calculate_w_matrix_almo'
    1539             : 
    1540             :       INTEGER                                            :: handle, ispin
    1541             :       REAL(KIND=dp)                                      :: scaling
    1542             :       TYPE(dbcsr_type)                                   :: tmp_nn1, tmp_no1, tmp_oo1, tmp_oo2
    1543             : 
    1544          66 :       CALL timeset(routineN, handle)
    1545             : 
    1546          66 :       IF (almo_scf_env%nspins == 1) THEN
    1547          66 :          scaling = 2.0_dp
    1548             :       ELSE
    1549           0 :          scaling = 1.0_dp
    1550             :       END IF
    1551             : 
    1552         132 :       DO ispin = 1, almo_scf_env%nspins
    1553             : 
    1554             :          CALL dbcsr_create(tmp_nn1, template=almo_scf_env%matrix_s(1), &
    1555          66 :                            matrix_type=dbcsr_type_no_symmetry)
    1556             :          CALL dbcsr_create(tmp_no1, template=almo_scf_env%matrix_t(ispin), &
    1557          66 :                            matrix_type=dbcsr_type_no_symmetry)
    1558             :          CALL dbcsr_create(tmp_oo1, template=almo_scf_env%matrix_sigma_inv(ispin), &
    1559          66 :                            matrix_type=dbcsr_type_no_symmetry)
    1560             :          CALL dbcsr_create(tmp_oo2, template=almo_scf_env%matrix_sigma_inv(ispin), &
    1561          66 :                            matrix_type=dbcsr_type_no_symmetry)
    1562             : 
    1563          66 :          CALL dbcsr_copy(tmp_nn1, almo_scf_env%matrix_ks(ispin))
    1564             :          ! 1. TMP_NO1=F.T
    1565             :          CALL dbcsr_multiply("N", "N", scaling, tmp_nn1, almo_scf_env%matrix_t(ispin), &
    1566          66 :                              0.0_dp, tmp_no1, filter_eps=almo_scf_env%eps_filter)
    1567             :          ! 2. TMP_OO1=T^(tr).TMP_NO1=T^(tr).(FT)
    1568             :          CALL dbcsr_multiply("T", "N", 1.0_dp, almo_scf_env%matrix_t(ispin), tmp_no1, &
    1569          66 :                              0.0_dp, tmp_oo1, filter_eps=almo_scf_env%eps_filter)
    1570             :          ! 3. TMP_OO2=TMP_OO1.siginv=(T^(tr)FT).siginv
    1571             :          CALL dbcsr_multiply("N", "N", 1.0_dp, tmp_oo1, almo_scf_env%matrix_sigma_inv(ispin), &
    1572          66 :                              0.0_dp, tmp_oo2, filter_eps=almo_scf_env%eps_filter)
    1573             :          ! 4. TMP_OO1=siginv.TMP_OO2=siginv.(T^(tr)FTsiginv)
    1574             :          CALL dbcsr_multiply("N", "N", 1.0_dp, almo_scf_env%matrix_sigma_inv(ispin), tmp_oo2, &
    1575          66 :                              0.0_dp, tmp_oo1, filter_eps=almo_scf_env%eps_filter)
    1576             :          ! 5. TMP_NO1=T.TMP_OO1.=T.(siginvT^(tr)FTsiginv)
    1577             :          CALL dbcsr_multiply("N", "N", 1.0_dp, almo_scf_env%matrix_t(ispin), tmp_oo1, &
    1578          66 :                              0.0_dp, tmp_no1, filter_eps=almo_scf_env%eps_filter)
    1579             :          ! 6. TMP_NN1=TMP_NO1.T^(tr)=(TsiginvT^(tr)FTsiginv).T^(tr)=RFR
    1580             :          CALL dbcsr_multiply("N", "T", 1.0_dp, tmp_no1, almo_scf_env%matrix_t(ispin), &
    1581          66 :                              0.0_dp, tmp_nn1, filter_eps=almo_scf_env%eps_filter)
    1582          66 :          CALL matrix_almo_to_qs(tmp_nn1, matrix_w(ispin)%matrix, almo_scf_env%mat_distr_aos)
    1583             : 
    1584          66 :          CALL dbcsr_release(tmp_nn1)
    1585          66 :          CALL dbcsr_release(tmp_no1)
    1586          66 :          CALL dbcsr_release(tmp_oo1)
    1587         132 :          CALL dbcsr_release(tmp_oo2)
    1588             : 
    1589             :       END DO
    1590             : 
    1591          66 :       CALL timestop(handle)
    1592             : 
    1593          66 :    END SUBROUTINE calculate_w_matrix_almo
    1594             : 
    1595             : END MODULE almo_scf_qs
    1596             : 

Generated by: LCOV version 1.15