LCOV - code coverage report
Current view: top level - src - almo_scf_qs.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:561f475) Lines: 82.0 % 539 442
Test Date: 2026-06-21 06:48:54 Functions: 100.0 % 11 11

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2026 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8              : ! **************************************************************************************************
       9              : !> \brief 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         3596 :    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         3596 :       INTEGER, DIMENSION(:), POINTER :: blk_distr, blk_sizes, block_sizes_new, col_blk_size, &
     132         3596 :          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         3596 :       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         3596 :       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         3596 :       CALL dbcsr_get_info(matrix_qs, distribution=dist_qs)
     163              : 
     164         3596 :       natoms = almo_scf_env%natoms
     165         3596 :       nmols = almo_scf_env%nmolecules
     166              : 
     167        10788 :       DO dimen = 1, 2 ! 1 - row, 2 - column dimension
     168              : 
     169              :          ! distribution pattern is the same for all matrix types (ao, occ, virt)
     170         7192 :          IF (dimen == 1) THEN !rows
     171         3596 :             CALL dbcsr_distribution_get(dist_qs, row_dist=blk_distr)
     172              :          ELSE !columns
     173         3596 :             CALL dbcsr_distribution_get(dist_qs, col_dist=blk_distr)
     174              :          END IF
     175              : 
     176         7192 :          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         2712 :             CALL dbcsr_get_info(matrix_qs, row_blk_size=blk_sizes)
     180              : 
     181              :             ! atomic clustering of AOs
     182         2712 :             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         2712 :             ELSE IF (almo_scf_env%mat_distr_aos == almo_mat_distr_molecular) THEN
     188        10848 :                ALLOCATE (block_sizes_new(nmols), distr_new_array(nmols))
     189        20742 :                block_sizes_new(:) = 0
     190        47092 :                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        47092 :                      blk_sizes(iatom)
     194              :                END DO
     195        20742 :                DO imol = 1, nmols
     196              :                   distr_new_array(imol) = &
     197        20742 :                      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         4480 :                 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         4480 :                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         4480 :                ELSE IF (almo_scf_env%mat_distr_mos == almo_mat_distr_molecular) THEN
     224         4480 :                   nlength = nmols
     225        13440 :                   ALLOCATE (block_sizes_new(nlength))
     226         4480 :                   IF (size_keys(dimen) == almo_mat_dim_occ) THEN
     227        19240 :                      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        19240 :                      WHERE (block_sizes_new == 0) block_sizes_new = 1
     231         1920 :                   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         1920 :                   ELSE IF (size_keys(dimen) == almo_mat_dim_virt_full) THEN
     234         5772 :                      block_sizes_new(:) = almo_scf_env%nvirt_full_of_domain(:, spin_key)
     235         1152 :                   ELSE IF (size_keys(dimen) == almo_mat_dim_virt) THEN
     236         8658 :                      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        13440 :             ALLOCATE (distr_new_array(nlength))
     250              :             ! atomic clustering
     251         4480 :             IF (almo_scf_env%mat_distr_mos == almo_mat_distr_atomic) THEN
     252            0 :                distr_new_array(:) = blk_distr(:)
     253              :                ! molecular clustering
     254         4480 :             ELSE IF (almo_scf_env%mat_distr_mos == almo_mat_distr_molecular) THEN
     255        33670 :                DO imol = 1, nmols
     256              :                   distr_new_array(imol) = &
     257        33670 :                      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        10788 :          IF (dimen == 1) THEN !rows
     264         3596 :             row_sizes_new => block_sizes_new
     265         3596 :             row_distr_new => distr_new_array
     266              :          ELSE !columns
     267         3596 :             col_sizes_new => block_sizes_new
     268         3596 :             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         3596 :                                   reuse_arrays=.TRUE.)
     276              : 
     277              :       ! Create the matrix
     278              :       CALL dbcsr_create(matrix_new, name_new, &
     279              :                         dist_new, symmetry_new, &
     280         3596 :                         row_sizes_new, col_sizes_new, reuse_arrays=.TRUE.)
     281         3596 :       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         3596 :       IF (init_domains) THEN
     286              : 
     287         1426 :          CALL dbcsr_distribution_get(dist_new, mynode=mynode)
     288         1426 :          CALL dbcsr_work_create(matrix_new, work_mutable=.TRUE.)
     289              :          CALL dbcsr_get_info(matrix_new, nblkrows_total=nblkrows_tot, &
     290         1426 :                              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==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        10870 :          DO row = 1, nblkrows_tot
     351         9444 :             tr = .FALSE.
     352         9444 :             iblock_row = row
     353         9444 :             iblock_col = row
     354         9444 :             CALL dbcsr_get_stored_coordinates(matrix_new, iblock_row, iblock_col, hold)
     355              : 
     356        10870 :             IF (hold == mynode) THEN
     357              : 
     358        14166 :                active = .TRUE.
     359              : 
     360              :                one_dim_is_mo = .FALSE.
     361        14166 :                DO dimen = 1, 2 ! 1 - row, 2 - column dimension
     362        14166 :                   IF (size_keys(dimen) == almo_mat_dim_occ) one_dim_is_mo = .TRUE.
     363              :                END DO
     364         4722 :                IF (one_dim_is_mo) THEN
     365         1668 :                   IF (almo_scf_env%nocc_of_domain(row, spin_key) == 0) active = .FALSE.
     366              :                END IF
     367              : 
     368         4722 :                one_dim_is_mo = .FALSE.
     369        14166 :                DO dimen = 1, 2
     370        14166 :                   IF (size_keys(dimen) == almo_mat_dim_virt) one_dim_is_mo = .TRUE.
     371              :                END DO
     372         4722 :                IF (one_dim_is_mo) THEN
     373          417 :                   IF (almo_scf_env%nvirt_of_domain(row, spin_key) == 0) active = .FALSE.
     374              :                END IF
     375              : 
     376         4722 :                one_dim_is_mo = .FALSE.
     377        14166 :                DO dimen = 1, 2
     378        14166 :                   IF (size_keys(dimen) == almo_mat_dim_virt_disc) one_dim_is_mo = .TRUE.
     379              :                END DO
     380         4722 :                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         4722 :                one_dim_is_mo = .FALSE.
     385        14166 :                DO dimen = 1, 2
     386        14166 :                   IF (size_keys(dimen) == almo_mat_dim_virt_full) one_dim_is_mo = .TRUE.
     387              :                END DO
     388         4722 :                IF (one_dim_is_mo) THEN
     389          417 :                   IF (almo_scf_env%nvirt_full_of_domain(row, spin_key) == 0) active = .FALSE.
     390              :                END IF
     391              : 
     392         4688 :                IF (active) THEN
     393        17592 :                   ALLOCATE (new_block(row_blk_size(iblock_row), col_blk_size(iblock_col)))
     394       840230 :                   new_block(:, :) = 1.0_dp
     395         4398 :                   CALL dbcsr_put_block(matrix_new, iblock_row, iblock_col, new_block)
     396         4398 :                   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         3596 :       CALL dbcsr_finalize(matrix_new)
     406              : 
     407         3596 :       CALL timestop(handle)
     408              : 
     409         7192 :    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         2080 :    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         2080 :       CALL timeset(routineN, handle)
     431              :       !RZK-warning if it's not a N(AO)xN(AO) matrix then stop
     432              : 
     433         2080 :       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         2080 :          CALL dbcsr_create(matrix_qs_nosym, template=matrix_qs, matrix_type=dbcsr_type_no_symmetry)
     440         2080 :          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         2080 :          CALL dbcsr_set(matrix_almo, 0.0_dp)
     450         2080 :          CALL dbcsr_complete_redistribute(matrix_qs_nosym, matrix_almo)
     451         2080 :          CALL dbcsr_release(matrix_qs_nosym)
     452              : 
     453              :       CASE DEFAULT
     454         2080 :          CPABORT("")
     455              :       END SELECT
     456              : 
     457         2080 :       CALL timestop(handle)
     458              : 
     459         2080 :    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         1862 :    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         1862 :       CALL timeset(routineN, handle)
     480              :       ! RZK-warning if it's not a N(AO)xN(AO) matrix then stop
     481              : 
     482         1862 :       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         1862 :          CALL dbcsr_create(matrix_almo_redist, template=matrix_qs)
     487         1862 :          CALL dbcsr_complete_redistribute(matrix_almo, matrix_almo_redist)
     488         1862 :          CALL dbcsr_set(matrix_qs, 0.0_dp)
     489         1862 :          CALL dbcsr_copy(matrix_qs, matrix_almo_redist, keep_sparsity=.TRUE.)
     490         1862 :          CALL dbcsr_release(matrix_almo_redist)
     491              :       CASE DEFAULT
     492         1862 :          CPABORT("")
     493              :       END SELECT
     494              : 
     495         1862 :       CALL timestop(handle)
     496              : 
     497         1862 :    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          122 :    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          122 :       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          122 :          POINTER                                         :: sab_orb
     523              :       TYPE(qs_ks_env_type), POINTER                      :: ks_env
     524              : 
     525          122 :       CALL timeset(routineN, handle)
     526              : 
     527          122 :       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          122 :                       sab_orb=sab_orb)
     536              : 
     537          122 :       nspin = dft_control%nspins
     538              : 
     539              :       ! create matrix_ks in the QS env if necessary
     540          122 :       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          250 :       DO ispin = 1, nspin
     554          128 :          CALL matrix_qs_to_almo(matrix_qs_ks(ispin)%matrix, matrix_ks(ispin), mat_distr_aos)
     555          250 :          CALL dbcsr_filter(matrix_ks(ispin), eps_filter)
     556              :       END DO
     557              : 
     558          122 :       CALL timestop(handle)
     559              : 
     560          122 :    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          366 :    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          122 :       TYPE(mo_set_type), DIMENSION(:), POINTER           :: mos
     582              :       TYPE(qs_scf_env_type), POINTER                     :: scf_env
     583              : 
     584          122 :       CALL timeset(routineN, handle)
     585              : 
     586              :       ! create and init scf_env (this is necessary to return MOs to qs)
     587          122 :       NULLIFY (mos, fm_struct_tmp, scf_env)
     588          122 :       ALLOCATE (scf_env)
     589          122 :       CALL scf_env_create(scf_env)
     590              : 
     591              :       !CALL qs_scf_env_initialize(qs_env, scf_env)
     592          122 :       CALL set_qs_env(qs_env, scf_env=scf_env)
     593          122 :       CALL get_qs_env(qs_env, dft_control=dft_control, mos=mos)
     594              : 
     595          122 :       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          250 :       DO ispin = 1, almo_scf_env%nspins
     599          128 :          CALL dbcsr_get_info(almo_scf_env%matrix_t(ispin), nfullrows_total=nrow_fm, nfullcols_total=ncol_fm)
     600              : 
     601              :          ! Currently only fm version of mo_set is usable.
     602              :          ! First transform the matrix_t to fm version
     603              :          ! Empty the containers to prevent memory leaks
     604          128 :          CALL deallocate_mo_set(mos(ispin))
     605              : 
     606          128 :          IF (almo_scf_env%nspins == 1) THEN
     607              :             CALL allocate_mo_set(mo_set=mos(ispin), &
     608              :                                  nao=nrow_fm, &
     609              :                                  nmo=ncol_fm, &
     610              :                                  nelectron=almo_scf_env%nelectrons_total, &
     611              :                                  n_el_f=REAL(almo_scf_env%nelectrons_total, dp), &
     612              :                                  maxocc=2.0_dp, &
     613          116 :                                  flexible_electron_count=dft_control%relax_multiplicity)
     614           12 :          ELSEIF (almo_scf_env%nspins == 2) THEN
     615              :             CALL allocate_mo_set(mo_set=mos(ispin), &
     616              :                                  nao=nrow_fm, &
     617              :                                  nmo=ncol_fm, &
     618              :                                  nelectron=SUM(almo_scf_env%nocc_of_domain(:, ispin)), &
     619              :                                  n_el_f=REAL(SUM(almo_scf_env%nocc_of_domain(:, ispin)), dp), &
     620              :                                  maxocc=1.0_dp, &
     621           60 :                                  flexible_electron_count=dft_control%relax_multiplicity)
     622              :          END IF
     623              : 
     624              :          CALL cp_fm_struct_create(fm_struct_tmp, nrow_global=nrow_fm, ncol_global=ncol_fm, &
     625              :                                   context=almo_scf_env%blacs_env, &
     626          128 :                                   para_env=almo_scf_env%para_env)
     627              : 
     628          128 :          CALL cp_fm_create(mo_fm_copy, fm_struct_tmp, name="t_orthogonal_converted_to_fm")
     629          128 :          CALL cp_fm_struct_release(fm_struct_tmp)
     630              :          !CALL copy_dbcsr_to_fm(almo_scf_env%matrix_t(ispin), mo_fm_copy)
     631              : 
     632          128 :          CALL init_mo_set(mos(ispin), fm_ref=mo_fm_copy, name='fm_mo')
     633              : 
     634          506 :          CALL cp_fm_release(mo_fm_copy)
     635              : 
     636              :       END DO
     637              : 
     638          122 :       CALL timestop(handle)
     639              : 
     640          122 :    END SUBROUTINE construct_qs_mos
     641              : 
     642              : ! **************************************************************************************************
     643              : !> \brief return density matrix to the qs_env
     644              : !> \param qs_env ...
     645              : !> \param matrix_p ...
     646              : !> \param mat_distr_aos ...
     647              : !> \par History
     648              : !>       2011.05 created [Rustam Z Khaliullin]
     649              : !> \author Rustam Z Khaliullin
     650              : ! **************************************************************************************************
     651         1778 :    SUBROUTINE almo_dm_to_qs_env(qs_env, matrix_p, mat_distr_aos)
     652              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     653              :       TYPE(dbcsr_type), DIMENSION(:)                     :: matrix_p
     654              :       INTEGER, INTENT(IN)                                :: mat_distr_aos
     655              : 
     656              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'almo_dm_to_qs_env'
     657              : 
     658              :       INTEGER                                            :: handle, ispin, nspins
     659         1778 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: rho_ao
     660              :       TYPE(qs_rho_type), POINTER                         :: rho
     661              : 
     662         1778 :       CALL timeset(routineN, handle)
     663              : 
     664         1778 :       NULLIFY (rho, rho_ao)
     665         1778 :       nspins = SIZE(matrix_p)
     666         1778 :       CALL get_qs_env(qs_env, rho=rho)
     667         1778 :       CALL qs_rho_get(rho, rho_ao=rho_ao)
     668              : 
     669              :       ! set the new density matrix
     670         3574 :       DO ispin = 1, nspins
     671              :          CALL matrix_almo_to_qs(matrix_p(ispin), &
     672              :                                 rho_ao(ispin)%matrix, &
     673         3574 :                                 mat_distr_aos)
     674              :       END DO
     675         1778 :       CALL qs_rho_update_rho(rho, qs_env=qs_env)
     676         1778 :       CALL qs_ks_did_change(qs_env%ks_env, rho_changed=.TRUE.)
     677              : 
     678         1778 :       CALL timestop(handle)
     679              : 
     680         1778 :    END SUBROUTINE almo_dm_to_qs_env
     681              : 
     682              : ! **************************************************************************************************
     683              : !> \brief uses the ALMO density matrix
     684              : !>        to compute KS matrix (inside QS environment) and the new energy
     685              : !> \param qs_env ...
     686              : !> \param matrix_p ...
     687              : !> \param energy_total ...
     688              : !> \param mat_distr_aos ...
     689              : !> \param smear ...
     690              : !> \param kTS_sum ...
     691              : !> \par History
     692              : !>       2011.05 created [Rustam Z Khaliullin]
     693              : !>       2018.09 smearing support [Ruben Staub]
     694              : !> \author Rustam Z Khaliullin
     695              : ! **************************************************************************************************
     696         1752 :    SUBROUTINE almo_dm_to_qs_ks(qs_env, matrix_p, energy_total, mat_distr_aos, smear, kTS_sum)
     697              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     698              :       TYPE(dbcsr_type), DIMENSION(:)                     :: matrix_p
     699              :       REAL(KIND=dp)                                      :: energy_total
     700              :       INTEGER, INTENT(IN)                                :: mat_distr_aos
     701              :       LOGICAL, INTENT(IN), OPTIONAL                      :: smear
     702              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: kTS_sum
     703              : 
     704              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'almo_dm_to_qs_ks'
     705              : 
     706              :       INTEGER                                            :: handle
     707              :       LOGICAL                                            :: smearing
     708              :       REAL(KIND=dp)                                      :: entropic_term
     709              :       TYPE(qs_energy_type), POINTER                      :: energy
     710              : 
     711         1752 :       CALL timeset(routineN, handle)
     712              : 
     713         1752 :       IF (PRESENT(smear)) THEN
     714         1752 :          smearing = smear
     715              :       ELSE
     716              :          smearing = .FALSE.
     717              :       END IF
     718              : 
     719         1752 :       IF (PRESENT(kTS_sum)) THEN
     720         1752 :          entropic_term = kTS_sum
     721              :       ELSE
     722              :          entropic_term = 0.0_dp
     723              :       END IF
     724              : 
     725         1752 :       NULLIFY (energy)
     726         1752 :       CALL get_qs_env(qs_env, energy=energy)
     727         1752 :       CALL almo_dm_to_qs_env(qs_env, matrix_p, mat_distr_aos)
     728              :       CALL qs_ks_update_qs_env(qs_env, calculate_forces=.FALSE., just_energy=.FALSE., &
     729         1752 :                                print_active=.TRUE.)
     730              : 
     731              :       !! Add electronic entropy contribution if smearing is requested
     732              :       !! Previous QS entropy is replaced by the sum of the entropy for each spin
     733         1752 :       IF (smearing) THEN
     734           20 :          energy%total = energy%total - energy%kTS + entropic_term
     735              :       END IF
     736              : 
     737         1752 :       energy_total = energy%total
     738              : 
     739         1752 :       CALL timestop(handle)
     740              : 
     741         1752 :    END SUBROUTINE almo_dm_to_qs_ks
     742              : 
     743              : ! **************************************************************************************************
     744              : !> \brief uses the ALMO density matrix
     745              : !>        to compute ALMO KS matrix and the new energy
     746              : !> \param qs_env ...
     747              : !> \param matrix_p ...
     748              : !> \param matrix_ks ...
     749              : !> \param energy_total ...
     750              : !> \param eps_filter ...
     751              : !> \param mat_distr_aos ...
     752              : !> \param smear ...
     753              : !> \param kTS_sum ...
     754              : !> \par History
     755              : !>       2011.05 created [Rustam Z Khaliullin]
     756              : !>       2018.09 smearing support [Ruben Staub]
     757              : !> \author Rustam Z Khaliullin
     758              : ! **************************************************************************************************
     759         1752 :    SUBROUTINE almo_dm_to_almo_ks(qs_env, matrix_p, matrix_ks, energy_total, eps_filter, &
     760              :                                  mat_distr_aos, smear, kTS_sum)
     761              : 
     762              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     763              :       TYPE(dbcsr_type), DIMENSION(:)                     :: matrix_p, matrix_ks
     764              :       REAL(KIND=dp)                                      :: energy_total, eps_filter
     765              :       INTEGER, INTENT(IN)                                :: mat_distr_aos
     766              :       LOGICAL, INTENT(IN), OPTIONAL                      :: smear
     767              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: kTS_sum
     768              : 
     769              :       CHARACTER(len=*), PARAMETER :: routineN = 'almo_dm_to_almo_ks'
     770              : 
     771              :       INTEGER                                            :: handle, ispin, nspins
     772              :       LOGICAL                                            :: smearing
     773              :       REAL(KIND=dp)                                      :: entropic_term
     774         1752 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_qs_ks
     775              : 
     776         1752 :       CALL timeset(routineN, handle)
     777              : 
     778         1752 :       IF (PRESENT(smear)) THEN
     779          470 :          smearing = smear
     780              :       ELSE
     781         1282 :          smearing = .FALSE.
     782              :       END IF
     783              : 
     784         1752 :       IF (PRESENT(kTS_sum)) THEN
     785          470 :          entropic_term = kTS_sum
     786              :       ELSE
     787         1282 :          entropic_term = 0.0_dp
     788              :       END IF
     789              : 
     790              :       ! update KS matrix in the QS env
     791              :       CALL almo_dm_to_qs_ks(qs_env, matrix_p, energy_total, mat_distr_aos, &
     792              :                             smear=smearing, &
     793         1752 :                             kTS_sum=entropic_term)
     794              : 
     795         1752 :       nspins = SIZE(matrix_ks)
     796              : 
     797              :       ! get KS matrix from the QS env and convert to the ALMO format
     798         1752 :       CALL get_qs_env(qs_env, matrix_ks=matrix_qs_ks)
     799         3522 :       DO ispin = 1, nspins
     800         1770 :          CALL matrix_qs_to_almo(matrix_qs_ks(ispin)%matrix, matrix_ks(ispin), mat_distr_aos)
     801         3522 :          CALL dbcsr_filter(matrix_ks(ispin), eps_filter)
     802              :       END DO
     803              : 
     804         1752 :       CALL timestop(handle)
     805              : 
     806         1752 :    END SUBROUTINE almo_dm_to_almo_ks
     807              : 
     808              : ! **************************************************************************************************
     809              : !> \brief update qs_env total energy
     810              : !> \param qs_env ...
     811              : !> \param energy ...
     812              : !> \param energy_singles_corr ...
     813              : !> \par History
     814              : !>       2013.03 created [Rustam Z Khaliullin]
     815              : !> \author Rustam Z Khaliullin
     816              : ! **************************************************************************************************
     817          112 :    SUBROUTINE almo_scf_update_ks_energy(qs_env, energy, energy_singles_corr)
     818              : 
     819              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     820              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: energy, energy_singles_corr
     821              : 
     822              :       TYPE(qs_energy_type), POINTER                      :: qs_energy
     823              : 
     824          112 :       CALL get_qs_env(qs_env, energy=qs_energy)
     825              : 
     826          112 :       IF (PRESENT(energy_singles_corr)) THEN
     827           26 :          qs_energy%singles_corr = energy_singles_corr
     828              :       ELSE
     829           86 :          qs_energy%singles_corr = 0.0_dp
     830              :       END IF
     831              : 
     832          112 :       IF (PRESENT(energy)) THEN
     833          112 :          qs_energy%total = energy
     834              :       END IF
     835              : 
     836          112 :       qs_energy%total = qs_energy%total + qs_energy%singles_corr
     837              : 
     838          112 :    END SUBROUTINE almo_scf_update_ks_energy
     839              : 
     840              : ! **************************************************************************************************
     841              : !> \brief Creates the matrix that imposes absolute locality on MOs
     842              : !> \param qs_env ...
     843              : !> \param almo_scf_env ...
     844              : !> \par History
     845              : !>       2011.11 created [Rustam Z. Khaliullin]
     846              : !> \author Rustam Z. Khaliullin
     847              : ! **************************************************************************************************
     848          122 :    SUBROUTINE almo_scf_construct_quencher(qs_env, almo_scf_env)
     849              : 
     850              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     851              :       TYPE(almo_scf_env_type), INTENT(INOUT)             :: almo_scf_env
     852              : 
     853              :       CHARACTER(len=*), PARAMETER :: routineN = 'almo_scf_construct_quencher'
     854              : 
     855              :       CHARACTER                                          :: sym
     856              :       INTEGER :: col, contact_atom_1, contact_atom_2, domain_col, domain_map_local_entries, &
     857              :          domain_row, global_entries, global_list_length, grid1, GroupID, handle, hold, iatom, &
     858              :          iatom2, iblock_col, iblock_row, idomain, idomain2, ientry, igrid, ineig, ineighbor, &
     859              :          iNode, inode2, ipair, ispin, jatom, jatom2, jdomain2, local_list_length, &
     860              :          max_domain_neighbors, max_neig, mynode, nblkcols_tot, nblkrows_tot, nblks, ndomains, &
     861              :          neig_temp, nnode2, nNodes, row, unit_nr
     862          122 :       INTEGER, ALLOCATABLE, DIMENSION(:) :: current_number_neighbors, domain_entries_cpu, &
     863          122 :          domain_map_global, domain_map_local, first_atom_of_molecule, global_list, &
     864          122 :          last_atom_of_molecule, list_length_cpu, list_offset_cpu, local_list, offset_for_cpu
     865          122 :       INTEGER, ALLOCATABLE, DIMENSION(:, :)              :: domain_grid, domain_neighbor_list, &
     866          122 :                                                             domain_neighbor_list_excessive
     867          122 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_size, row_blk_size
     868              :       LOGICAL                                            :: already_listed, block_active, &
     869              :                                                             delayed_increment, found, &
     870              :                                                             max_neig_fails, tr
     871              :       REAL(KIND=dp)                                      :: contact1_radius, contact2_radius, &
     872              :                                                             distance, distance_squared, overlap, &
     873              :                                                             r0, r1, s0, s1, trial_distance_squared
     874          122 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: new_block
     875              :       REAL(KIND=dp), DIMENSION(3)                        :: rab
     876          122 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: p_old_block
     877              :       TYPE(cell_type), POINTER                           :: cell
     878              :       TYPE(cp_logger_type), POINTER                      :: logger
     879              :       TYPE(dbcsr_distribution_type)                      :: dist
     880          122 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s
     881              :       TYPE(dbcsr_type)                                   :: matrix_s_sym
     882          122 :       TYPE(molecule_type), DIMENSION(:), POINTER         :: molecule_set
     883              :       TYPE(mp_comm_type)                                 :: group
     884              :       TYPE(neighbor_list_iterator_p_type), &
     885          122 :          DIMENSION(:), POINTER                           :: nl_iterator, nl_iterator2
     886              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
     887          122 :          POINTER                                         :: sab_almo
     888          122 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     889              : 
     890          122 :       CALL timeset(routineN, handle)
     891              : 
     892              :       ! get a useful output_unit
     893          122 :       logger => cp_get_default_logger()
     894          122 :       IF (logger%para_env%is_source()) THEN
     895           61 :          unit_nr = cp_logger_get_default_unit_nr(logger, local=.TRUE.)
     896              :       ELSE
     897              :          unit_nr = -1
     898              :       END IF
     899              : 
     900          122 :       ndomains = almo_scf_env%ndomains
     901              : 
     902              :       CALL get_qs_env(qs_env=qs_env, &
     903              :                       particle_set=particle_set, &
     904              :                       molecule_set=molecule_set, &
     905              :                       cell=cell, &
     906              :                       matrix_s=matrix_s, &
     907          122 :                       sab_almo=sab_almo)
     908              : 
     909              :       ! if we are dealing with molecules get info about them
     910          122 :       IF (almo_scf_env%domain_layout_mos == almo_domain_layout_molecular .OR. &
     911              :           almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
     912          366 :          ALLOCATE (first_atom_of_molecule(almo_scf_env%nmolecules))
     913          244 :          ALLOCATE (last_atom_of_molecule(almo_scf_env%nmolecules))
     914              :          CALL get_molecule_set_info(molecule_set, &
     915              :                                     mol_to_first_atom=first_atom_of_molecule, &
     916          122 :                                     mol_to_last_atom=last_atom_of_molecule)
     917              :       END IF
     918              : 
     919              :       ! create a symmetrized copy of the ao overlap
     920              :       CALL dbcsr_create(matrix_s_sym, &
     921              :                         template=almo_scf_env%matrix_s(1), &
     922          122 :                         matrix_type=dbcsr_type_no_symmetry)
     923              :       CALL dbcsr_get_info(almo_scf_env%matrix_s(1), &
     924          122 :                           matrix_type=sym)
     925          122 :       IF (sym == dbcsr_type_no_symmetry) THEN
     926            0 :          CALL dbcsr_copy(matrix_s_sym, almo_scf_env%matrix_s(1))
     927              :       ELSE
     928              :          CALL dbcsr_desymmetrize(almo_scf_env%matrix_s(1), &
     929          122 :                                  matrix_s_sym)
     930              :       END IF
     931              : 
     932          494 :       ALLOCATE (almo_scf_env%quench_t(almo_scf_env%nspins))
     933          494 :       ALLOCATE (almo_scf_env%domain_map(almo_scf_env%nspins))
     934              : 
     935          250 :       DO ispin = 1, almo_scf_env%nspins
     936              : 
     937              :          ! create the sparsity template for the occupied orbitals
     938              :          CALL matrix_almo_create(matrix_new=almo_scf_env%quench_t(ispin), &
     939              :                                  matrix_qs=matrix_s(1)%matrix, &
     940              :                                  almo_scf_env=almo_scf_env, &
     941              :                                  name_new="T_QUENCHER", &
     942              :                                  size_keys=[almo_mat_dim_aobasis, almo_mat_dim_occ], &
     943              :                                  symmetry_new=dbcsr_type_no_symmetry, &
     944              :                                  spin_key=ispin, &
     945          128 :                                  init_domains=.FALSE.)
     946              : 
     947              :          ! initialize distance quencher
     948          128 :          CALL dbcsr_work_create(almo_scf_env%quench_t(ispin), work_mutable=.TRUE.)
     949              :          CALL dbcsr_get_info(almo_scf_env%quench_t(ispin), distribution=dist, &
     950          128 :                              nblkrows_total=nblkrows_tot, nblkcols_total=nblkcols_tot)
     951          128 :          CALL dbcsr_distribution_get(dist, numnodes=nNodes, group=GroupID, mynode=mynode)
     952          128 :          CALL group%set_handle(groupid)
     953              : 
     954              :          ! create global atom neighbor list from the local lists
     955              :          ! first, calculate number of local pairs
     956          128 :          local_list_length = 0
     957          128 :          CALL neighbor_list_iterator_create(nl_iterator, sab_almo)
     958        39611 :          DO WHILE (neighbor_list_iterate(nl_iterator) == 0)
     959              :             ! nnode - total number of neighbors for iatom
     960              :             ! inode - current neighbor count
     961              :             CALL get_iterator_info(nl_iterator, &
     962        39483 :                                    iatom=iatom2, jatom=jatom2, inode=inode2, nnode=nnode2)
     963        39611 :             IF (inode2 == 1) THEN
     964         2049 :                local_list_length = local_list_length + nnode2
     965              :             END IF
     966              :          END DO
     967          128 :          CALL neighbor_list_iterator_release(nl_iterator)
     968              : 
     969              :          ! second, extract the local list to an array
     970          383 :          ALLOCATE (local_list(2*local_list_length))
     971          128 :          local_list(:) = 0
     972          128 :          local_list_length = 0
     973          128 :          CALL neighbor_list_iterator_create(nl_iterator2, sab_almo)
     974        39611 :          DO WHILE (neighbor_list_iterate(nl_iterator2) == 0)
     975              :             CALL get_iterator_info(nl_iterator2, &
     976        39483 :                                    iatom=iatom2, jatom=jatom2)
     977        39483 :             local_list(2*local_list_length + 1) = iatom2
     978        39483 :             local_list(2*local_list_length + 2) = jatom2
     979        39483 :             local_list_length = local_list_length + 1
     980              :          END DO ! end loop over pairs of atoms
     981          128 :          CALL neighbor_list_iterator_release(nl_iterator2)
     982              : 
     983              :          ! third, communicate local length to the other nodes
     984          512 :          ALLOCATE (list_length_cpu(nNodes), list_offset_cpu(nNodes))
     985          128 :          CALL group%allgather(2*local_list_length, list_length_cpu)
     986              : 
     987              :          ! fourth, create a global list
     988          128 :          list_offset_cpu(1) = 0
     989          256 :          DO iNode = 2, nNodes
     990              :             list_offset_cpu(iNode) = list_offset_cpu(iNode - 1) + &
     991          256 :                                      list_length_cpu(iNode - 1)
     992              :          END DO
     993          128 :          global_list_length = list_offset_cpu(nNodes) + list_length_cpu(nNodes)
     994              : 
     995              :          ! fifth, communicate all list data
     996          384 :          ALLOCATE (global_list(global_list_length))
     997              :          CALL group%allgatherv(local_list, global_list, &
     998          128 :                                list_length_cpu, list_offset_cpu)
     999          128 :          DEALLOCATE (list_length_cpu, list_offset_cpu)
    1000          128 :          DEALLOCATE (local_list)
    1001              : 
    1002              :          ! calculate maximum number of atoms surrounding the domain
    1003          384 :          ALLOCATE (current_number_neighbors(almo_scf_env%ndomains))
    1004          128 :          current_number_neighbors(:) = 0
    1005          128 :          global_list_length = global_list_length/2
    1006        79094 :          DO ipair = 1, global_list_length
    1007        78966 :             iatom2 = global_list(2*(ipair - 1) + 1)
    1008        78966 :             jatom2 = global_list(2*(ipair - 1) + 2)
    1009        78966 :             idomain2 = almo_scf_env%domain_index_of_atom(iatom2)
    1010        78966 :             jdomain2 = almo_scf_env%domain_index_of_atom(jatom2)
    1011              :             ! add to the list
    1012        78966 :             current_number_neighbors(idomain2) = current_number_neighbors(idomain2) + 1
    1013              :             ! add j,i with i,j
    1014        79094 :             IF (idomain2 /= jdomain2) THEN
    1015        63144 :                current_number_neighbors(jdomain2) = current_number_neighbors(jdomain2) + 1
    1016              :             END IF
    1017              :          END DO
    1018          962 :          max_domain_neighbors = MAXVAL(current_number_neighbors)
    1019              : 
    1020              :          ! use the global atom neighbor list to create a global domain neighbor list
    1021          512 :          ALLOCATE (domain_neighbor_list_excessive(ndomains, max_domain_neighbors))
    1022          962 :          current_number_neighbors(:) = 1
    1023          962 :          DO ipair = 1, ndomains
    1024          962 :             domain_neighbor_list_excessive(ipair, 1) = ipair
    1025              :          END DO
    1026        79094 :          DO ipair = 1, global_list_length
    1027        78966 :             iatom2 = global_list(2*(ipair - 1) + 1)
    1028        78966 :             jatom2 = global_list(2*(ipair - 1) + 2)
    1029        78966 :             idomain2 = almo_scf_env%domain_index_of_atom(iatom2)
    1030        78966 :             jdomain2 = almo_scf_env%domain_index_of_atom(jatom2)
    1031        78966 :             already_listed = .FALSE.
    1032       325938 :             DO ineighbor = 1, current_number_neighbors(idomain2)
    1033       325938 :                IF (domain_neighbor_list_excessive(idomain2, ineighbor) == jdomain2) THEN
    1034              :                   already_listed = .TRUE.
    1035              :                   EXIT
    1036              :                END IF
    1037              :             END DO
    1038        79094 :             IF (.NOT. already_listed) THEN
    1039              :                ! add to the list
    1040         2722 :                current_number_neighbors(idomain2) = current_number_neighbors(idomain2) + 1
    1041         2722 :                domain_neighbor_list_excessive(idomain2, current_number_neighbors(idomain2)) = jdomain2
    1042              :                ! add j,i with i,j
    1043         2722 :                IF (idomain2 /= jdomain2) THEN
    1044         2722 :                   current_number_neighbors(jdomain2) = current_number_neighbors(jdomain2) + 1
    1045         2722 :                   domain_neighbor_list_excessive(jdomain2, current_number_neighbors(jdomain2)) = idomain2
    1046              :                END IF
    1047              :             END IF
    1048              :          END DO ! end loop over pairs of atoms
    1049          128 :          DEALLOCATE (global_list)
    1050              : 
    1051          962 :          max_domain_neighbors = MAXVAL(current_number_neighbors)
    1052          512 :          ALLOCATE (domain_neighbor_list(ndomains, max_domain_neighbors))
    1053          128 :          domain_neighbor_list(:, :) = 0
    1054         7248 :          domain_neighbor_list(:, :) = domain_neighbor_list_excessive(:, 1:max_domain_neighbors)
    1055          128 :          DEALLOCATE (domain_neighbor_list_excessive)
    1056              : 
    1057          384 :          ALLOCATE (almo_scf_env%domain_map(ispin)%index1(ndomains))
    1058          384 :          ALLOCATE (almo_scf_env%domain_map(ispin)%pairs(max_domain_neighbors*ndomains, 2))
    1059        12956 :          almo_scf_env%domain_map(ispin)%pairs(:, :) = 0
    1060          962 :          almo_scf_env%domain_map(ispin)%index1(:) = 0
    1061          128 :          domain_map_local_entries = 0
    1062              : 
    1063              :          ! RZK-warning intermediate [0,1] quencher values are ill-defined
    1064              :          ! for molecules (not continuous and conceptually inadequate)
    1065              : 
    1066              :          CALL dbcsr_get_info(almo_scf_env%quench_t(ispin), &
    1067          128 :                              row_blk_size=row_blk_size, col_blk_size=col_blk_size)
    1068              :          ! O(N) loop over domain pairs
    1069          962 :          DO row = 1, nblkrows_tot
    1070         7240 :             DO col = 1, current_number_neighbors(row)
    1071         6278 :                tr = .FALSE.
    1072         6278 :                iblock_row = row
    1073         6278 :                iblock_col = domain_neighbor_list(row, col)
    1074              :                CALL dbcsr_get_stored_coordinates(almo_scf_env%quench_t(ispin), &
    1075         6278 :                                                  iblock_row, iblock_col, hold)
    1076              : 
    1077         7112 :                IF (hold == mynode) THEN
    1078              : 
    1079              :                   ! Translate indices of distribution blocks to indices of domain blocks
    1080              :                   ! Rows are AOs
    1081         3139 :                   domain_row = almo_scf_env%domain_index_of_ao_block(iblock_row)
    1082              :                   ! Columns are electrons (i.e. MOs)
    1083         3139 :                   domain_col = almo_scf_env%domain_index_of_mo_block(iblock_col)
    1084              : 
    1085         3139 :                   SELECT CASE (almo_scf_env%constraint_type)
    1086              :                   CASE (almo_constraint_block_diagonal)
    1087              : 
    1088            0 :                      block_active = .FALSE.
    1089              :                      ! type of electron groups
    1090            0 :                      IF (almo_scf_env%domain_layout_mos == almo_domain_layout_molecular) THEN
    1091              : 
    1092              :                         ! type of ao domains
    1093            0 :                         IF (almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1094              : 
    1095              :                            ! ao domains are molecular / electron groups are molecular
    1096            0 :                            IF (domain_row == domain_col) THEN
    1097              :                               block_active = .TRUE.
    1098              :                            END IF
    1099              : 
    1100              :                         ELSE ! ao domains are atomic
    1101              : 
    1102              :                            ! ao domains are atomic / electron groups are molecular
    1103            0 :                            CPABORT("Illegal: atomic domains and molecular groups")
    1104              : 
    1105              :                         END IF
    1106              : 
    1107              :                      ELSE ! electron groups are atomic
    1108              : 
    1109              :                         ! type of ao domains
    1110            0 :                         IF (almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1111              : 
    1112              :                            ! ao domains are molecular / electron groups are atomic
    1113            0 :                            CPABORT("Illegal: molecular domains and atomic groups")
    1114              : 
    1115              :                         ELSE
    1116              : 
    1117              :                            ! ao domains are atomic / electron groups are atomic
    1118            0 :                            IF (domain_row == domain_col) THEN
    1119              :                               block_active = .TRUE.
    1120              :                            END IF
    1121              : 
    1122              :                         END IF
    1123              : 
    1124              :                      END IF ! end type of electron groups
    1125              : 
    1126            0 :                      IF (block_active) THEN
    1127              : 
    1128            0 :                         ALLOCATE (new_block(row_blk_size(iblock_row), col_blk_size(iblock_col)))
    1129            0 :                         new_block(:, :) = 1.0_dp
    1130            0 :                         CALL dbcsr_put_block(almo_scf_env%quench_t(ispin), iblock_row, iblock_col, new_block)
    1131            0 :                         DEALLOCATE (new_block)
    1132              : 
    1133            0 :                         IF (domain_map_local_entries >= max_domain_neighbors*almo_scf_env%ndomains) THEN
    1134            0 :                            CPABORT("weird... max_domain_neighbors is exceeded")
    1135              :                         END IF
    1136            0 :                         almo_scf_env%domain_map(ispin)%pairs(domain_map_local_entries + 1, 1) = iblock_row
    1137            0 :                         almo_scf_env%domain_map(ispin)%pairs(domain_map_local_entries + 1, 2) = iblock_col
    1138            0 :                         domain_map_local_entries = domain_map_local_entries + 1
    1139              : 
    1140              :                      END IF
    1141              : 
    1142              :                   CASE (almo_constraint_ao_overlap)
    1143              : 
    1144              :                      ! type of electron groups
    1145            0 :                      IF (almo_scf_env%domain_layout_mos == almo_domain_layout_molecular) THEN
    1146              : 
    1147              :                         ! type of ao domains
    1148            0 :                         IF (almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1149              : 
    1150              :                            ! ao domains are molecular / electron groups are molecular
    1151              : 
    1152              :                            ! compute the maximum overlap between the atoms of the two molecules
    1153            0 :                            CALL dbcsr_get_block_p(matrix_s_sym, iblock_row, iblock_col, p_old_block, found)
    1154            0 :                            IF (found) THEN
    1155            0 :                               overlap = MAXVAL(ABS(p_old_block))
    1156              :                            ELSE
    1157              :                               overlap = 0.0_dp
    1158              :                            END IF
    1159              : 
    1160              :                         ELSE ! ao domains are atomic
    1161              : 
    1162              :                            ! ao domains are atomic / electron groups are molecular
    1163              :                            ! overlap_between_atom_and_molecule(atom=domain_row,molecule=domain_col)
    1164            0 :                            CPABORT("atomic domains and molecular groups - NYI")
    1165              : 
    1166              :                         END IF
    1167              : 
    1168              :                      ELSE ! electron groups are atomic
    1169              : 
    1170              :                         ! type of ao domains
    1171            0 :                         IF (almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1172              : 
    1173              :                            ! ao domains are molecular / electron groups are atomic
    1174              :                            ! overlap_between_atom_and_molecule(atom=domain_col,molecule=domain_row)
    1175            0 :                            CPABORT("molecular domains and atomic groups - NYI")
    1176              : 
    1177              :                         ELSE
    1178              : 
    1179              :                            ! ao domains are atomic / electron groups are atomic
    1180              :                            ! compute max overlap between atoms: domain_row and domain_col
    1181            0 :                            CALL dbcsr_get_block_p(matrix_s_sym, iblock_row, iblock_col, p_old_block, found)
    1182            0 :                            IF (found) THEN
    1183            0 :                               overlap = MAXVAL(ABS(p_old_block))
    1184              :                            ELSE
    1185              :                               overlap = 0.0_dp
    1186              :                            END IF
    1187              : 
    1188              :                         END IF
    1189              : 
    1190              :                      END IF ! end type of electron groups
    1191              : 
    1192            0 :                      s0 = -LOG10(ABS(almo_scf_env%quencher_s0))
    1193            0 :                      s1 = -LOG10(ABS(almo_scf_env%quencher_s1))
    1194            0 :                      IF (overlap == 0.0_dp) THEN
    1195            0 :                         overlap = -LOG10(ABS(almo_scf_env%eps_filter)) + 100.0_dp
    1196              :                      ELSE
    1197            0 :                         overlap = -LOG10(overlap)
    1198              :                      END IF
    1199            0 :                      IF (s0 < 0.0_dp) THEN
    1200            0 :                         CPABORT("S0 is less than zero")
    1201              :                      END IF
    1202            0 :                      IF (s1 <= 0.0_dp) THEN
    1203            0 :                         CPABORT("S1 is less than or equal to zero")
    1204              :                      END IF
    1205            0 :                      IF (s0 >= s1) THEN
    1206            0 :                         CPABORT("S0 is greater than or equal to S1")
    1207              :                      END IF
    1208              : 
    1209              :                      ! Fill in non-zero blocks if AOs are close to the electron center
    1210            0 :                      IF (overlap < s1) THEN
    1211            0 :                         ALLOCATE (new_block(row_blk_size(iblock_row), col_blk_size(iblock_col)))
    1212            0 :                         IF (overlap <= s0) THEN
    1213            0 :                            new_block(:, :) = 1.0_dp
    1214              :                         ELSE
    1215            0 :                            new_block(:, :) = 1.0_dp/(1.0_dp + EXP(-(s0 - s1)/(s0 - overlap) - (s0 - s1)/(overlap - s1)))
    1216              :                         END IF
    1217              : 
    1218            0 :                         IF (ABS(new_block(1, 1)) > ABS(almo_scf_env%eps_filter)) THEN
    1219            0 :                            IF (domain_map_local_entries >= max_domain_neighbors*almo_scf_env%ndomains) THEN
    1220            0 :                               CPABORT("weird... max_domain_neighbors is exceeded")
    1221              :                            END IF
    1222            0 :                            almo_scf_env%domain_map(ispin)%pairs(domain_map_local_entries + 1, 1) = iblock_row
    1223            0 :                            almo_scf_env%domain_map(ispin)%pairs(domain_map_local_entries + 1, 2) = iblock_col
    1224            0 :                            domain_map_local_entries = domain_map_local_entries + 1
    1225              :                         END IF
    1226              : 
    1227            0 :                         CALL dbcsr_put_block(almo_scf_env%quench_t(ispin), iblock_row, iblock_col, new_block)
    1228            0 :                         DEALLOCATE (new_block)
    1229              : 
    1230              :                      END IF
    1231              : 
    1232              :                   CASE (almo_constraint_distance)
    1233              : 
    1234              :                      ! type of electron groups
    1235         3139 :                      IF (almo_scf_env%domain_layout_mos == almo_domain_layout_molecular) THEN
    1236              : 
    1237              :                         ! type of ao domains
    1238         3139 :                         IF (almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1239              : 
    1240              :                            ! ao domains are molecular / electron groups are molecular
    1241              : 
    1242              :                            ! compute distance between molecules: domain_row and domain_col
    1243              :                            ! distance between molecules is defined as the smallest
    1244              :                            ! distance among all atom pairs
    1245         3139 :                            IF (domain_row == domain_col) THEN
    1246          417 :                               distance = 0.0_dp
    1247          417 :                               contact_atom_1 = first_atom_of_molecule(domain_row)
    1248          417 :                               contact_atom_2 = first_atom_of_molecule(domain_col)
    1249              :                            ELSE
    1250         2722 :                               distance_squared = 1.0E+100_dp
    1251         2722 :                               contact_atom_1 = -1
    1252         2722 :                               contact_atom_2 = -1
    1253         9074 :                               DO iatom = first_atom_of_molecule(domain_row), last_atom_of_molecule(domain_row)
    1254        26414 :                                  DO jatom = first_atom_of_molecule(domain_col), last_atom_of_molecule(domain_col)
    1255        17340 :                                     rab(:) = pbc(particle_set(iatom)%r(:), particle_set(jatom)%r(:), cell)
    1256        17340 :                                     trial_distance_squared = rab(1)*rab(1) + rab(2)*rab(2) + rab(3)*rab(3)
    1257        23692 :                                     IF (trial_distance_squared < distance_squared) THEN
    1258         6391 :                                        distance_squared = trial_distance_squared
    1259         6391 :                                        contact_atom_1 = iatom
    1260         6391 :                                        contact_atom_2 = jatom
    1261              :                                     END IF
    1262              :                                  END DO ! jatom
    1263              :                               END DO ! iatom
    1264         2722 :                               CPASSERT(contact_atom_1 > 0)
    1265         2722 :                               distance = SQRT(distance_squared)
    1266              :                            END IF
    1267              : 
    1268              :                         ELSE ! ao domains are atomic
    1269              : 
    1270              :                            ! ao domains are atomic / electron groups are molecular
    1271              :                            !distance_between_atom_and_molecule(atom=domain_row,molecule=domain_col)
    1272            0 :                            CPABORT("atomic domains and molecular groups - NYI")
    1273              : 
    1274              :                         END IF
    1275              : 
    1276              :                      ELSE ! electron groups are atomic
    1277              : 
    1278              :                         ! type of ao domains
    1279            0 :                         IF (almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1280              : 
    1281              :                            ! ao domains are molecular / electron groups are atomic
    1282              :                            !distance_between_atom_and_molecule(atom=domain_col,molecule=domain_row)
    1283            0 :                            CPABORT("molecular domains and atomic groups - NYI")
    1284              : 
    1285              :                         ELSE
    1286              : 
    1287              :                            ! ao domains are atomic / electron groups are atomic
    1288              :                            ! compute distance between atoms: domain_row and domain_col
    1289            0 :                            rab(:) = pbc(particle_set(domain_row)%r(:), particle_set(domain_col)%r(:), cell)
    1290            0 :                            distance = SQRT(rab(1)*rab(1) + rab(2)*rab(2) + rab(3)*rab(3))
    1291            0 :                            contact_atom_1 = domain_row
    1292            0 :                            contact_atom_2 = domain_col
    1293              : 
    1294              :                         END IF
    1295              : 
    1296              :                      END IF ! end type of electron groups
    1297              : 
    1298              :                      ! get atomic radii to compute distance cutoff threshold
    1299         3139 :                      IF (almo_scf_env%quencher_radius_type == do_bondparm_covalent) THEN
    1300              :                         CALL get_atomic_kind(atomic_kind=particle_set(contact_atom_1)%atomic_kind, &
    1301            0 :                                              rcov=contact1_radius)
    1302              :                         CALL get_atomic_kind(atomic_kind=particle_set(contact_atom_2)%atomic_kind, &
    1303            0 :                                              rcov=contact2_radius)
    1304         3139 :                      ELSE IF (almo_scf_env%quencher_radius_type == do_bondparm_vdw) THEN
    1305              :                         CALL get_atomic_kind(atomic_kind=particle_set(contact_atom_1)%atomic_kind, &
    1306         3139 :                                              rvdw=contact1_radius)
    1307              :                         CALL get_atomic_kind(atomic_kind=particle_set(contact_atom_2)%atomic_kind, &
    1308         3139 :                                              rvdw=contact2_radius)
    1309              :                      ELSE
    1310            0 :                         CPABORT("Illegal quencher_radius_type")
    1311              :                      END IF
    1312         3139 :                      contact1_radius = cp_unit_to_cp2k(contact1_radius, "angstrom")
    1313         3139 :                      contact2_radius = cp_unit_to_cp2k(contact2_radius, "angstrom")
    1314              : 
    1315              :                      !RZK-warning the procedure is faulty for molecules:
    1316              :                      ! the closest contacts should be found using
    1317              :                      ! the element specific radii
    1318              : 
    1319              :                      ! compute inner and outer cutoff radii
    1320         3139 :                      r0 = almo_scf_env%quencher_r0_factor*(contact1_radius + contact2_radius)
    1321              :                      !+almo_scf_env%quencher_r0_shift
    1322         3139 :                      r1 = almo_scf_env%quencher_r1_factor*(contact1_radius + contact2_radius)
    1323              :                      !+almo_scf_env%quencher_r1_shift
    1324              : 
    1325         3139 :                      IF (r0 < 0.0_dp) THEN
    1326            0 :                         CPABORT("R0 is less than zero")
    1327              :                      END IF
    1328         3139 :                      IF (r1 <= 0.0_dp) THEN
    1329            0 :                         CPABORT("R1 is less than or equal to zero")
    1330              :                      END IF
    1331         3139 :                      IF (r0 > r1) THEN
    1332            0 :                         CPABORT("R0 is greater than or equal to R1")
    1333              :                      END IF
    1334              : 
    1335              :                      ! Fill in non-zero blocks if AOs are close to the electron center
    1336         3139 :                      IF (distance < r1) THEN
    1337         8740 :                         ALLOCATE (new_block(row_blk_size(iblock_row), col_blk_size(iblock_col)))
    1338         2185 :                         IF (distance <= r0) THEN
    1339       101919 :                            new_block(:, :) = 1.0_dp
    1340              :                         ELSE
    1341              :                            ! remove the intermediate values from the quencher temporarily
    1342            0 :                            CPABORT("")
    1343            0 :                            new_block(:, :) = 1.0_dp/(1.0_dp + EXP((r1 - r0)/(r0 - distance) + (r1 - r0)/(r1 - distance)))
    1344              :                         END IF
    1345              : 
    1346         2185 :                         IF (ABS(new_block(1, 1)) > ABS(almo_scf_env%eps_filter)) THEN
    1347         2185 :                            IF (domain_map_local_entries >= max_domain_neighbors*almo_scf_env%ndomains) THEN
    1348            0 :                               CPABORT("weird... max_domain_neighbors is exceeded")
    1349              :                            END IF
    1350         2185 :                            almo_scf_env%domain_map(ispin)%pairs(domain_map_local_entries + 1, 1) = iblock_row
    1351         2185 :                            almo_scf_env%domain_map(ispin)%pairs(domain_map_local_entries + 1, 2) = iblock_col
    1352         2185 :                            domain_map_local_entries = domain_map_local_entries + 1
    1353              :                         END IF
    1354              : 
    1355         2185 :                         CALL dbcsr_put_block(almo_scf_env%quench_t(ispin), iblock_row, iblock_col, new_block)
    1356         2185 :                         DEALLOCATE (new_block)
    1357              :                      END IF
    1358              : 
    1359              :                   CASE DEFAULT
    1360         3139 :                      CPABORT("Illegal constraint type")
    1361              :                   END SELECT
    1362              : 
    1363              :                END IF ! mynode
    1364              : 
    1365              :             END DO
    1366              :          END DO ! end O(N) loop over pairs
    1367              : 
    1368          128 :          DEALLOCATE (domain_neighbor_list)
    1369          128 :          DEALLOCATE (current_number_neighbors)
    1370              : 
    1371          128 :          CALL dbcsr_finalize(almo_scf_env%quench_t(ispin))
    1372              : 
    1373              :          CALL dbcsr_filter(almo_scf_env%quench_t(ispin), &
    1374          128 :                            almo_scf_env%eps_filter)
    1375              : 
    1376              :          ! check that both domain_map and quench_t have the same number of entries
    1377          128 :          nblks = dbcsr_get_num_blocks(almo_scf_env%quench_t(ispin))
    1378          128 :          IF (nblks /= domain_map_local_entries) THEN
    1379            0 :             CPABORT("number of blocks is wrong")
    1380              :          END IF
    1381              : 
    1382              :          ! first, communicate map sizes on the other nodes
    1383          384 :          ALLOCATE (domain_entries_cpu(nNodes), offset_for_cpu(nNodes))
    1384          128 :          CALL group%allgather(2*domain_map_local_entries, domain_entries_cpu)
    1385              : 
    1386              :          ! second, create
    1387          128 :          offset_for_cpu(1) = 0
    1388          256 :          DO iNode = 2, nNodes
    1389              :             offset_for_cpu(iNode) = offset_for_cpu(iNode - 1) + &
    1390          256 :                                     domain_entries_cpu(iNode - 1)
    1391              :          END DO
    1392          128 :          global_entries = offset_for_cpu(nNodes) + domain_entries_cpu(nNodes)
    1393              : 
    1394              :          ! communicate all entries
    1395          384 :          ALLOCATE (domain_map_global(global_entries))
    1396          508 :          ALLOCATE (domain_map_local(2*domain_map_local_entries))
    1397         2313 :          DO ientry = 1, domain_map_local_entries
    1398         2185 :             domain_map_local(2*(ientry - 1) + 1) = almo_scf_env%domain_map(ispin)%pairs(ientry, 1)
    1399         2313 :             domain_map_local(2*ientry) = almo_scf_env%domain_map(ispin)%pairs(ientry, 2)
    1400              :          END DO
    1401              :          CALL group%allgatherv(domain_map_local, domain_map_global, &
    1402          128 :                                domain_entries_cpu, offset_for_cpu)
    1403          128 :          DEALLOCATE (domain_entries_cpu, offset_for_cpu)
    1404          128 :          DEALLOCATE (domain_map_local)
    1405              : 
    1406          128 :          DEALLOCATE (almo_scf_env%domain_map(ispin)%index1)
    1407          128 :          DEALLOCATE (almo_scf_env%domain_map(ispin)%pairs)
    1408          256 :          ALLOCATE (almo_scf_env%domain_map(ispin)%index1(ndomains))
    1409          512 :          ALLOCATE (almo_scf_env%domain_map(ispin)%pairs(global_entries/2, 2))
    1410         9124 :          almo_scf_env%domain_map(ispin)%pairs(:, :) = 0
    1411          962 :          almo_scf_env%domain_map(ispin)%index1(:) = 0
    1412              : 
    1413              :          ! unpack the received data into a local variable
    1414              :          ! since we do not know the maximum global number of neighbors
    1415              :          ! try one. if fails increase the maximum number and try again
    1416              :          ! until it succeeds
    1417              :          max_neig = max_domain_neighbors
    1418              :          max_neig_fails = .TRUE.
    1419          256 :          max_neig_loop: DO WHILE (max_neig_fails)
    1420          512 :             ALLOCATE (domain_grid(almo_scf_env%ndomains, 0:max_neig))
    1421          128 :             domain_grid(:, :) = 0
    1422              :             ! init the number of collected neighbors
    1423          962 :             domain_grid(:, 0) = 1
    1424              :             ! loop over the records
    1425          128 :             global_entries = global_entries/2
    1426         4498 :             DO ientry = 1, global_entries
    1427              :                ! get the center
    1428         4370 :                grid1 = domain_map_global(2*ientry)
    1429              :                ! get the neighbor
    1430         4370 :                ineig = domain_map_global(2*(ientry - 1) + 1)
    1431              :                ! check boundaries
    1432         4370 :                IF (domain_grid(grid1, 0) > max_neig) THEN
    1433              :                   ! this neighbor will overstep the boundaries
    1434              :                   ! stop the trial and increase the max number of neighbors
    1435            0 :                   DEALLOCATE (domain_grid)
    1436            0 :                   max_neig = max_neig*2
    1437            0 :                   CYCLE max_neig_loop
    1438              :                END IF
    1439              :                ! for the current center loop over the collected neighbors
    1440              :                ! to insert the current record in a numerical order
    1441              :                delayed_increment = .FALSE.
    1442        19016 :                DO igrid = 1, domain_grid(grid1, 0)
    1443              :                   ! compare the current neighbor with that already in the 'book'
    1444        19016 :                   IF (ineig < domain_grid(grid1, igrid)) THEN
    1445              :                      ! if this one is smaller then insert it here and pick up the one
    1446              :                      ! from the book to continue inserting
    1447         3180 :                      neig_temp = ineig
    1448         3180 :                      ineig = domain_grid(grid1, igrid)
    1449         3180 :                      domain_grid(grid1, igrid) = neig_temp
    1450              :                   ELSE
    1451        11466 :                      IF (domain_grid(grid1, igrid) == 0) THEN
    1452              :                         ! got the empty slot now - insert the record
    1453         4370 :                         domain_grid(grid1, igrid) = ineig
    1454              :                         ! increase the record counter but do it outside the loop
    1455         4370 :                         delayed_increment = .TRUE.
    1456              :                      END IF
    1457              :                   END IF
    1458              :                END DO
    1459         4498 :                IF (delayed_increment) THEN
    1460         4370 :                   domain_grid(grid1, 0) = domain_grid(grid1, 0) + 1
    1461              :                ELSE
    1462              :                   ! should not be here - all records must be inserted
    1463            0 :                   CPABORT("all records must be inserted")
    1464              :                END IF
    1465              :             END DO
    1466          128 :             max_neig_fails = .FALSE.
    1467              :          END DO max_neig_loop
    1468          128 :          DEALLOCATE (domain_map_global)
    1469              : 
    1470          128 :          ientry = 1
    1471          962 :          DO idomain = 1, almo_scf_env%ndomains
    1472         5204 :             DO ineig = 1, domain_grid(idomain, 0) - 1
    1473         4370 :                almo_scf_env%domain_map(ispin)%pairs(ientry, 1) = domain_grid(idomain, ineig)
    1474         4370 :                almo_scf_env%domain_map(ispin)%pairs(ientry, 2) = idomain
    1475         5204 :                ientry = ientry + 1
    1476              :             END DO
    1477          962 :             almo_scf_env%domain_map(ispin)%index1(idomain) = ientry
    1478              :          END DO
    1479          378 :          DEALLOCATE (domain_grid)
    1480              : 
    1481              :       END DO ! ispin
    1482          122 :       IF (almo_scf_env%nspins == 2) THEN
    1483              :          CALL dbcsr_copy(almo_scf_env%quench_t(2), &
    1484            6 :                          almo_scf_env%quench_t(1))
    1485              :          almo_scf_env%domain_map(2)%pairs(:, :) = &
    1486           50 :             almo_scf_env%domain_map(1)%pairs(:, :)
    1487              :          almo_scf_env%domain_map(2)%index1(:) = &
    1488           18 :             almo_scf_env%domain_map(1)%index1(:)
    1489              :       END IF
    1490              : 
    1491          122 :       CALL dbcsr_release(matrix_s_sym)
    1492              : 
    1493          122 :       IF (almo_scf_env%domain_layout_mos == almo_domain_layout_molecular .OR. &
    1494              :           almo_scf_env%domain_layout_aos == almo_domain_layout_molecular) THEN
    1495          122 :          DEALLOCATE (first_atom_of_molecule)
    1496          122 :          DEALLOCATE (last_atom_of_molecule)
    1497              :       END IF
    1498              : 
    1499              :       !mynode = dbcsr_mp_mynode(dbcsr_distribution_mp(&
    1500              :       !   dbcsr_distribution(almo_scf_env%quench_t(ispin))))
    1501              :       !CALL dbcsr_get_info(almo_scf_env%quench_t(ispin), distribution=dist, &
    1502              :       !                    nblkrows_total=nblkrows_tot, nblkcols_total=nblkcols_tot)
    1503              :       !DO row = 1, nblkrows_tot
    1504              :       !   DO col = 1, nblkcols_tot
    1505              :       !      tr = .FALSE.
    1506              :       !      iblock_row = row
    1507              :       !      iblock_col = col
    1508              :       !      CALL dbcsr_get_stored_coordinates(almo_scf_env%quench_t(ispin),&
    1509              :       !              iblock_row, iblock_col, tr, hold)
    1510              :       !      CALL dbcsr_get_block_p(almo_scf_env%quench_t(ispin),&
    1511              :       !              row, col, p_old_block, found)
    1512              :       !      write(*,*) "RST_NOTE:", mynode, row, col, hold, found
    1513              :       !   ENDDO
    1514              :       !ENDDO
    1515              : 
    1516          122 :       CALL timestop(handle)
    1517              : 
    1518          244 :    END SUBROUTINE almo_scf_construct_quencher
    1519              : 
    1520              : ! *****************************************************************************
    1521              : !> \brief Compute matrix W (energy-weighted density matrix) that is needed
    1522              : !>        for the evaluation of forces
    1523              : !> \param matrix_w ...
    1524              : !> \param almo_scf_env ...
    1525              : !> \par History
    1526              : !>       2015.03 created [Rustam Z. Khaliullin]
    1527              : !> \author Rustam Z. Khaliullin
    1528              : ! **************************************************************************************************
    1529           66 :    SUBROUTINE calculate_w_matrix_almo(matrix_w, almo_scf_env)
    1530              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_w
    1531              :       TYPE(almo_scf_env_type)                            :: almo_scf_env
    1532              : 
    1533              :       CHARACTER(len=*), PARAMETER :: routineN = 'calculate_w_matrix_almo'
    1534              : 
    1535              :       INTEGER                                            :: handle, ispin
    1536              :       REAL(KIND=dp)                                      :: scaling
    1537              :       TYPE(dbcsr_type)                                   :: tmp_nn1, tmp_no1, tmp_oo1, tmp_oo2
    1538              : 
    1539           66 :       CALL timeset(routineN, handle)
    1540              : 
    1541           66 :       IF (almo_scf_env%nspins == 1) THEN
    1542           66 :          scaling = 2.0_dp
    1543              :       ELSE
    1544            0 :          scaling = 1.0_dp
    1545              :       END IF
    1546              : 
    1547          132 :       DO ispin = 1, almo_scf_env%nspins
    1548              : 
    1549              :          CALL dbcsr_create(tmp_nn1, template=almo_scf_env%matrix_s(1), &
    1550           66 :                            matrix_type=dbcsr_type_no_symmetry)
    1551              :          CALL dbcsr_create(tmp_no1, template=almo_scf_env%matrix_t(ispin), &
    1552           66 :                            matrix_type=dbcsr_type_no_symmetry)
    1553              :          CALL dbcsr_create(tmp_oo1, template=almo_scf_env%matrix_sigma_inv(ispin), &
    1554           66 :                            matrix_type=dbcsr_type_no_symmetry)
    1555              :          CALL dbcsr_create(tmp_oo2, template=almo_scf_env%matrix_sigma_inv(ispin), &
    1556           66 :                            matrix_type=dbcsr_type_no_symmetry)
    1557              : 
    1558           66 :          CALL dbcsr_copy(tmp_nn1, almo_scf_env%matrix_ks(ispin))
    1559              :          ! 1. TMP_NO1=F.T
    1560              :          CALL dbcsr_multiply("N", "N", scaling, tmp_nn1, almo_scf_env%matrix_t(ispin), &
    1561           66 :                              0.0_dp, tmp_no1, filter_eps=almo_scf_env%eps_filter)
    1562              :          ! 2. TMP_OO1=T^(tr).TMP_NO1=T^(tr).(FT)
    1563              :          CALL dbcsr_multiply("T", "N", 1.0_dp, almo_scf_env%matrix_t(ispin), tmp_no1, &
    1564           66 :                              0.0_dp, tmp_oo1, filter_eps=almo_scf_env%eps_filter)
    1565              :          ! 3. TMP_OO2=TMP_OO1.siginv=(T^(tr)FT).siginv
    1566              :          CALL dbcsr_multiply("N", "N", 1.0_dp, tmp_oo1, almo_scf_env%matrix_sigma_inv(ispin), &
    1567           66 :                              0.0_dp, tmp_oo2, filter_eps=almo_scf_env%eps_filter)
    1568              :          ! 4. TMP_OO1=siginv.TMP_OO2=siginv.(T^(tr)FTsiginv)
    1569              :          CALL dbcsr_multiply("N", "N", 1.0_dp, almo_scf_env%matrix_sigma_inv(ispin), tmp_oo2, &
    1570           66 :                              0.0_dp, tmp_oo1, filter_eps=almo_scf_env%eps_filter)
    1571              :          ! 5. TMP_NO1=T.TMP_OO1.=T.(siginvT^(tr)FTsiginv)
    1572              :          CALL dbcsr_multiply("N", "N", 1.0_dp, almo_scf_env%matrix_t(ispin), tmp_oo1, &
    1573           66 :                              0.0_dp, tmp_no1, filter_eps=almo_scf_env%eps_filter)
    1574              :          ! 6. TMP_NN1=TMP_NO1.T^(tr)=(TsiginvT^(tr)FTsiginv).T^(tr)=RFR
    1575              :          CALL dbcsr_multiply("N", "T", 1.0_dp, tmp_no1, almo_scf_env%matrix_t(ispin), &
    1576           66 :                              0.0_dp, tmp_nn1, filter_eps=almo_scf_env%eps_filter)
    1577           66 :          CALL matrix_almo_to_qs(tmp_nn1, matrix_w(ispin)%matrix, almo_scf_env%mat_distr_aos)
    1578              : 
    1579           66 :          CALL dbcsr_release(tmp_nn1)
    1580           66 :          CALL dbcsr_release(tmp_no1)
    1581           66 :          CALL dbcsr_release(tmp_oo1)
    1582          132 :          CALL dbcsr_release(tmp_oo2)
    1583              : 
    1584              :       END DO
    1585              : 
    1586           66 :       CALL timestop(handle)
    1587              : 
    1588           66 :    END SUBROUTINE calculate_w_matrix_almo
    1589              : 
    1590              : END MODULE almo_scf_qs
    1591              : 
        

Generated by: LCOV version 2.0-1