LCOV - code coverage report
Current view: top level - src - negf_matrix_utils.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 60.8 % 339 206
Test Date: 2026-07-25 06:35:44 Functions: 87.5 % 8 7

            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 Helper routines to manipulate with matrices.
      10              : ! **************************************************************************************************
      11              : MODULE negf_matrix_utils
      12              :    USE cp_dbcsr_api,                    ONLY: &
      13              :         dbcsr_add, dbcsr_copy, dbcsr_create, dbcsr_deallocate_matrix, dbcsr_finalize, &
      14              :         dbcsr_get_block_p, dbcsr_init_p, dbcsr_p_type, dbcsr_put_block, dbcsr_release, dbcsr_set, &
      15              :         dbcsr_type, dbcsr_type_no_symmetry
      16              :    USE cp_dbcsr_cp2k_link,              ONLY: cp_dbcsr_alloc_block_from_nbl
      17              :    USE cp_dbcsr_operations,             ONLY: dbcsr_allocate_matrix_set,&
      18              :                                               dbcsr_deallocate_matrix_set
      19              :    USE cp_fm_basic_linalg,              ONLY: cp_fm_scale_and_add
      20              :    USE cp_fm_types,                     ONLY: cp_fm_get_info,&
      21              :                                               cp_fm_get_submatrix,&
      22              :                                               cp_fm_set_submatrix,&
      23              :                                               cp_fm_type
      24              :    USE kinds,                           ONLY: dp
      25              :    USE kpoint_types,                    ONLY: get_kpoint_info,&
      26              :                                               kpoint_type
      27              :    USE message_passing,                 ONLY: mp_comm_type,&
      28              :                                               mp_para_env_type,&
      29              :                                               mp_request_type
      30              :    USE negf_alloc_types,                ONLY: negf_allocatable_rvector
      31              :    USE negf_atom_map,                   ONLY: negf_atom_map_type
      32              :    USE particle_methods,                ONLY: get_particle_set
      33              :    USE particle_types,                  ONLY: particle_type
      34              :    USE qs_kind_types,                   ONLY: qs_kind_type
      35              :    USE qs_neighbor_list_types,          ONLY: get_iterator_info,&
      36              :                                               neighbor_list_iterate,&
      37              :                                               neighbor_list_iterator_create,&
      38              :                                               neighbor_list_iterator_p_type,&
      39              :                                               neighbor_list_iterator_release,&
      40              :                                               neighbor_list_set_p_type
      41              :    USE qs_subsys_types,                 ONLY: qs_subsys_get,&
      42              :                                               qs_subsys_type
      43              : #include "./base/base_uses.f90"
      44              : 
      45              :    IMPLICIT NONE
      46              :    PRIVATE
      47              : 
      48              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'negf_matrix_utils'
      49              :    LOGICAL, PARAMETER, PRIVATE          :: debug_this_module = .TRUE.
      50              : 
      51              :    PUBLIC :: number_of_atomic_orbitals, negf_copy_fm_submat_to_dbcsr, negf_copy_sym_dbcsr_to_fm_submat
      52              :    PUBLIC :: negf_copy_contact_matrix, negf_reference_contact_matrix
      53              :    PUBLIC :: invert_cell_to_index, get_index_by_cell
      54              : 
      55              : CONTAINS
      56              : 
      57              : ! **************************************************************************************************
      58              : !> \brief Compute the number of atomic orbitals of the given set of atoms.
      59              : !> \param subsys    QuickStep subsystem
      60              : !> \param atom_list list of selected atom; when absent all the atoms are taken into account
      61              : !> \return number of atomic orbitals
      62              : !> \par History
      63              : !>   * 02.2017 created [Sergey Chulkov]
      64              : ! **************************************************************************************************
      65           38 :    FUNCTION number_of_atomic_orbitals(subsys, atom_list) RESULT(nao)
      66              :       TYPE(qs_subsys_type), POINTER                      :: subsys
      67              :       INTEGER, DIMENSION(:), INTENT(in), OPTIONAL        :: atom_list
      68              :       INTEGER                                            :: nao
      69              : 
      70              :       INTEGER                                            :: iatom, natoms
      71              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: nsgfs
      72           38 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
      73           38 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
      74              : 
      75           38 :       CALL qs_subsys_get(subsys, particle_set=particle_set, qs_kind_set=qs_kind_set)
      76          114 :       ALLOCATE (nsgfs(SIZE(particle_set)))
      77           38 :       CALL get_particle_set(particle_set, qs_kind_set, nsgf=nsgfs)
      78              : 
      79           38 :       IF (PRESENT(atom_list)) THEN
      80           38 :          natoms = SIZE(atom_list)
      81           38 :          nao = 0
      82              : 
      83          238 :          DO iatom = 1, natoms
      84          238 :             nao = nao + nsgfs(atom_list(iatom))
      85              :          END DO
      86              :       ELSE
      87            0 :          nao = SUM(nsgfs)
      88              :       END IF
      89              : 
      90           38 :       DEALLOCATE (nsgfs)
      91           38 :    END FUNCTION number_of_atomic_orbitals
      92              : 
      93              : ! **************************************************************************************************
      94              : !> \brief Populate relevant blocks of the DBCSR matrix using data from a ScaLAPACK matrix.
      95              : !>        Irrelevant blocks of the DBCSR matrix are kept untouched.
      96              : !> \param fm              dense matrix to copy
      97              : !> \param matrix          DBCSR matrix (modified on exit)
      98              : !> \param atomlist_row    set of atomic indices along the 1st (row) dimension
      99              : !> \param atomlist_col    set of atomic indices along the 2nd (column) dimension
     100              : !> \param subsys          subsystem environment
     101              : !> \par History
     102              : !>   * 02.2017 created [Sergey Chulkov]
     103              : ! **************************************************************************************************
     104           20 :    SUBROUTINE negf_copy_fm_submat_to_dbcsr(fm, matrix, atomlist_row, atomlist_col, subsys)
     105              :       TYPE(cp_fm_type), INTENT(IN)                       :: fm
     106              :       TYPE(dbcsr_type), POINTER                          :: matrix
     107              :       INTEGER, DIMENSION(:), INTENT(in)                  :: atomlist_row, atomlist_col
     108              :       TYPE(qs_subsys_type), POINTER                      :: subsys
     109              : 
     110              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'negf_copy_fm_submat_to_dbcsr'
     111              : 
     112              :       INTEGER :: first_sgf_col, first_sgf_row, handle, iatom_col, iatom_row, icol, irow, &
     113              :          natoms_col, natoms_row, ncols, nparticles, nrows
     114              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: nsgfs
     115              :       LOGICAL                                            :: found
     116              :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :)        :: fm_block
     117           20 :       REAL(kind=dp), DIMENSION(:, :), POINTER            :: sm_block
     118           20 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     119           20 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     120              : 
     121           20 :       CALL timeset(routineN, handle)
     122              : 
     123           20 :       CPASSERT(ASSOCIATED(matrix))
     124           20 :       CPASSERT(ASSOCIATED(subsys))
     125              : 
     126           20 :       CALL cp_fm_get_info(fm, nrow_global=nrows, ncol_global=ncols)
     127              : 
     128           20 :       CALL qs_subsys_get(subsys, particle_set=particle_set, qs_kind_set=qs_kind_set)
     129              : 
     130           20 :       natoms_row = SIZE(atomlist_row)
     131           20 :       natoms_col = SIZE(atomlist_col)
     132           20 :       nparticles = SIZE(particle_set)
     133              : 
     134           60 :       ALLOCATE (nsgfs(nparticles))
     135           20 :       CALL get_particle_set(particle_set, qs_kind_set, nsgf=nsgfs)
     136              : 
     137           80 :       ALLOCATE (fm_block(nrows, ncols))
     138           20 :       CALL cp_fm_get_submatrix(fm, fm_block)
     139              : 
     140           20 :       first_sgf_col = 1
     141          260 :       DO iatom_col = 1, natoms_col
     142              :          first_sgf_row = 1
     143         3120 :          DO iatom_row = 1, natoms_row
     144              :             CALL dbcsr_get_block_p(matrix=matrix, row=atomlist_row(iatom_row), col=atomlist_col(iatom_col), &
     145         2880 :                                    block=sm_block, found=found)
     146         2880 :             IF (found) THEN
     147              :                ! the following LAPACK call violates the coding convention
     148              :                !CALL dlacpy('F', nsgfs(atomlist_row(iatom_row)), nsgfs(atomlist_col(iatom_col)), &
     149              :                !            fm_block(first_sgf_row, first_sgf_col), SIZE(fm_block, 1), sm_block(1, 1), SIZE(sm_block, 1))
     150          570 :                nrows = nsgfs(atomlist_row(iatom_row))
     151          570 :                ncols = nsgfs(atomlist_col(iatom_col))
     152         1140 :                DO icol = 1, ncols
     153         1710 :                   DO irow = 1, nrows
     154         1140 :                      sm_block(irow, icol) = fm_block(first_sgf_row + irow - 1, first_sgf_col + icol - 1)
     155              :                   END DO
     156              :                END DO
     157              :             END IF
     158              : 
     159         6000 :             first_sgf_row = first_sgf_row + nsgfs(atomlist_row(iatom_row))
     160              :          END DO
     161          260 :          first_sgf_col = first_sgf_col + nsgfs(atomlist_col(iatom_col))
     162              :       END DO
     163              : 
     164           20 :       DEALLOCATE (fm_block)
     165           20 :       DEALLOCATE (nsgfs)
     166              : 
     167           20 :       CALL timestop(handle)
     168           40 :    END SUBROUTINE negf_copy_fm_submat_to_dbcsr
     169              : 
     170              : ! **************************************************************************************************
     171              : !> \brief Extract part of the DBCSR matrix based on selected atoms and copy it into a dense matrix.
     172              : !> \param matrix          DBCSR matrix
     173              : !> \param fm              dense matrix (created and initialised on exit)
     174              : !> \param atomlist_row    set of atomic indices along the 1st (row) dimension
     175              : !> \param atomlist_col    set of atomic indices along the 2nd (column) dimension
     176              : !> \param subsys          subsystem environment
     177              : !> \param mpi_comm_global MPI communicator which was used to distribute blocks of the DBCSR matrix.
     178              : !>                        If missed, assume that both DBCSR and ScaLapack matrices are distributed
     179              : !>                        across the same set of processors
     180              : !> \param do_upper_diag   initialise upper-triangular part of the dense matrix as well as diagonal elements
     181              : !> \param do_lower        initialise lower-triangular part of the dense matrix
     182              : !> \par History
     183              : !>   * 02.2017 created [Sergey Chulkov]
     184              : !> \note A naive implementation that copies relevant local DBCSR blocks into a 2-D matrix,
     185              : !>       performs collective summation, and then distributes the result. This approach seems to be
     186              : !>       optimal when processors are arranged into several independent MPI subgroups due to the fact
     187              : !>       that every subgroup automatically holds the copy of the dense matrix at the end, so
     188              : !>       we can avoid the final replication stage.
     189              : ! **************************************************************************************************
     190          156 :    SUBROUTINE negf_copy_sym_dbcsr_to_fm_submat(matrix, fm, atomlist_row, atomlist_col, subsys, &
     191              :                                                mpi_comm_global, do_upper_diag, do_lower)
     192              :       TYPE(dbcsr_type), POINTER                          :: matrix
     193              :       TYPE(cp_fm_type), INTENT(IN)                       :: fm
     194              :       INTEGER, DIMENSION(:), INTENT(in)                  :: atomlist_row, atomlist_col
     195              :       TYPE(qs_subsys_type), POINTER                      :: subsys
     196              : 
     197              :       CLASS(mp_comm_type), INTENT(in)                    :: mpi_comm_global
     198              :       LOGICAL, INTENT(in)                                :: do_upper_diag, do_lower
     199              : 
     200              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'negf_copy_sym_dbcsr_to_fm_submat'
     201              : 
     202              :       INTEGER :: handle, iatom_col, iatom_row, icol, irow, natoms_col, natoms_row, ncols_fm, &
     203              :                  nparticles, nrows_fm, offset_sgf_col, offset_sgf_row
     204              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: nsgfs
     205              :       LOGICAL                                            :: found
     206              :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :)        :: r2d
     207          156 :       REAL(kind=dp), DIMENSION(:, :), POINTER            :: sm_block
     208              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     209          156 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     210          156 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     211              : 
     212          156 :       CALL timeset(routineN, handle)
     213              : 
     214          156 :       CPASSERT(ASSOCIATED(matrix))
     215          156 :       CPASSERT(ASSOCIATED(subsys))
     216              : 
     217          156 :       CALL qs_subsys_get(subsys, particle_set=particle_set, qs_kind_set=qs_kind_set)
     218              : 
     219          156 :       natoms_row = SIZE(atomlist_row)
     220          156 :       natoms_col = SIZE(atomlist_col)
     221          156 :       nparticles = SIZE(particle_set)
     222              : 
     223          468 :       ALLOCATE (nsgfs(nparticles))
     224          156 :       CALL get_particle_set(particle_set, qs_kind_set, nsgf=nsgfs)
     225              : 
     226          156 :       CALL cp_fm_get_info(fm, nrow_global=nrows_fm, ncol_global=ncols_fm, para_env=para_env)
     227              : 
     228              :       IF (debug_this_module) THEN
     229         1356 :          CPASSERT(SUM(nsgfs(atomlist_row(:))) == nrows_fm)
     230         1164 :          CPASSERT(SUM(nsgfs(atomlist_col(:))) == ncols_fm)
     231              :       END IF
     232              : 
     233          624 :       ALLOCATE (r2d(nrows_fm, ncols_fm))
     234          156 :       r2d(:, :) = 0.0_dp
     235              : 
     236          156 :       offset_sgf_col = 0
     237         1164 :       DO iatom_col = 1, natoms_col
     238              :          offset_sgf_row = 0
     239              : 
     240        10416 :          DO iatom_row = 1, natoms_row
     241         9408 :             IF (atomlist_row(iatom_row) <= atomlist_col(iatom_col)) THEN
     242         5112 :                IF (do_upper_diag) THEN
     243              :                   CALL dbcsr_get_block_p(matrix=matrix, row=atomlist_row(iatom_row), col=atomlist_col(iatom_col), &
     244         4992 :                                          block=sm_block, found=found)
     245              :                END IF
     246              :             ELSE
     247         4296 :                IF (do_lower) THEN
     248              :                   CALL dbcsr_get_block_p(matrix=matrix, row=atomlist_col(iatom_col), col=atomlist_row(iatom_row), &
     249         4224 :                                          block=sm_block, found=found)
     250              :                END IF
     251              :             END IF
     252              : 
     253         9408 :             IF (found) THEN
     254         3328 :                IF (atomlist_row(iatom_row) <= atomlist_col(iatom_col)) THEN
     255         1868 :                   IF (do_upper_diag) THEN
     256         4009 :                      DO icol = nsgfs(atomlist_col(iatom_col)), 1, -1
     257         6996 :                         DO irow = nsgfs(atomlist_row(iatom_row)), 1, -1
     258         5188 :                            r2d(offset_sgf_row + irow, offset_sgf_col + icol) = sm_block(irow, icol)
     259              :                         END DO
     260              :                      END DO
     261              :                   END IF
     262              :                ELSE
     263         1460 :                   IF (do_lower) THEN
     264         3169 :                      DO icol = nsgfs(atomlist_col(iatom_col)), 1, -1
     265         5556 :                         DO irow = nsgfs(atomlist_row(iatom_row)), 1, -1
     266         4132 :                            r2d(offset_sgf_row + irow, offset_sgf_col + icol) = sm_block(icol, irow)
     267              :                         END DO
     268              :                      END DO
     269              :                   END IF
     270              :                END IF
     271              :             END IF
     272              : 
     273        10416 :             offset_sgf_row = offset_sgf_row + nsgfs(atomlist_row(iatom_row))
     274              :          END DO
     275         1164 :          offset_sgf_col = offset_sgf_col + nsgfs(atomlist_col(iatom_col))
     276              :       END DO
     277              : 
     278          156 :       CALL mpi_comm_global%sum(r2d)
     279              : 
     280          156 :       CALL cp_fm_set_submatrix(fm, r2d)
     281              : 
     282          156 :       DEALLOCATE (r2d)
     283          156 :       DEALLOCATE (nsgfs)
     284              : 
     285          156 :       CALL timestop(handle)
     286          312 :    END SUBROUTINE negf_copy_sym_dbcsr_to_fm_submat
     287              : 
     288              : ! **************************************************************************************************
     289              : !> \brief Driver routine to extract diagonal and off-diagonal blocks from a symmetric DBCSR matrix.
     290              : !> \param fm_cell0        extracted diagonal matrix block
     291              : !> \param fm_cell1        extracted off-diagonal matrix block
     292              : !> \param direction_axis  axis towards the secondary unit cell
     293              : !> \param matrix_kp       set of DBCSR matrices
     294              : !> \param atom_list0      list of atoms which belong to the primary contact unit cell
     295              : !> \param atom_list1      list of atoms which belong to the secondary contact unit cell
     296              : !> \param subsys          QuickStep subsystem
     297              : !> \param mpi_comm_global global MPI communicator
     298              : !> \param kpoints ...
     299              : !> \par History
     300              : !>   * 10.2017 created [Sergey Chulkov]
     301              : !>   * 10.2025 The subroutine is essentially modified. [Dmitry Ryndyk]
     302              : ! **************************************************************************************************
     303           12 :    SUBROUTINE negf_copy_contact_matrix(fm_cell0, fm_cell1, direction_axis, matrix_kp, &
     304           12 :                                        atom_list0, atom_list1, subsys, mpi_comm_global, kpoints)
     305              :       TYPE(cp_fm_type), INTENT(IN)                       :: fm_cell0, fm_cell1
     306              :       INTEGER, INTENT(in)                                :: direction_axis
     307              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(in), &
     308              :          POINTER                                         :: matrix_kp
     309              :       INTEGER, DIMENSION(:), INTENT(in)                  :: atom_list0, atom_list1
     310              :       TYPE(qs_subsys_type), POINTER                      :: subsys
     311              : 
     312              :       CLASS(mp_comm_type), INTENT(in)                    :: mpi_comm_global
     313              :       TYPE(kpoint_type), POINTER                         :: kpoints
     314              : 
     315              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'negf_copy_contact_matrix'
     316              : 
     317              :       INTEGER                                            :: direction_axis_abs, handle, rep, ncell, ic
     318           12 :       TYPE(dbcsr_p_type), ALLOCATABLE, DIMENSION(:)      :: matrix_cells_raw
     319           12 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: mat_nosym
     320           12 :       INTEGER, ALLOCATABLE, DIMENSION(:, :)              :: i_to_c
     321           12 :       INTEGER, ALLOCATABLE, DIMENSION(:, :, :)           :: c_to_i
     322              : 
     323           12 :       CALL timeset(routineN, handle)
     324              : 
     325           12 :       CPASSERT(ASSOCIATED(subsys))
     326              : 
     327           12 :       direction_axis_abs = ABS(direction_axis)
     328              : 
     329           12 :       CALL desymmetrize_matrix(matrix_kp, mat_nosym, c_to_i, i_to_c, kpoints)
     330           12 :       ncell = SIZE(i_to_c, 2) ! update the number of cells
     331              : 
     332              :       ! 0 -- primary unit cell;
     333              :       ! +- 1 -- upper- and lower-diagonal matrices for neighbor-cell matrix elements;
     334              :       ! +- 2 -- for control
     335           84 :       ALLOCATE (matrix_cells_raw(-2:2))
     336           72 :       DO rep = -2, 2
     337           60 :          NULLIFY (matrix_cells_raw(rep)%matrix)
     338           60 :          CALL dbcsr_init_p(matrix_cells_raw(rep)%matrix)
     339           60 :          CALL dbcsr_copy(matrix_cells_raw(rep)%matrix, mat_nosym(1)%matrix)
     340           72 :          CALL dbcsr_set(matrix_cells_raw(rep)%matrix, 0.0_dp)
     341              :       END DO
     342              : 
     343          216 :       DO ic = 1, ncell
     344          204 :          rep = i_to_c(direction_axis_abs, ic)
     345          216 :          IF (ABS(rep) <= 2) THEN
     346          204 :             CALL dbcsr_add(matrix_cells_raw(rep)%matrix, mat_nosym(ic)%matrix, 1.0_dp, 1.0_dp)
     347              :          END IF
     348              :       END DO
     349              : 
     350           12 :       IF (direction_axis >= 0) THEN
     351              : 
     352              :          CALL negf_copy_sym_dbcsr_to_fm_submat(matrix_cells_raw(1)%matrix, fm_cell1, atom_list0, atom_list1, &
     353            6 :                                                subsys, mpi_comm_global, do_upper_diag=.TRUE., do_lower=.FALSE.)
     354              :          CALL negf_copy_sym_dbcsr_to_fm_submat(matrix_cells_raw(-1)%matrix, fm_cell0, atom_list0, atom_list1, &
     355            6 :                                                subsys, mpi_comm_global, do_upper_diag=.FALSE., do_lower=.TRUE.)
     356              : 
     357              :       ELSE
     358              : 
     359              :          CALL negf_copy_sym_dbcsr_to_fm_submat(matrix_cells_raw(1)%matrix, fm_cell1, atom_list0, atom_list1, &
     360            6 :                                                subsys, mpi_comm_global, do_upper_diag=.FALSE., do_lower=.TRUE.)
     361              :          CALL negf_copy_sym_dbcsr_to_fm_submat(matrix_cells_raw(-1)%matrix, fm_cell0, atom_list0, atom_list1, &
     362            6 :                                                subsys, mpi_comm_global, do_upper_diag=.TRUE., do_lower=.FALSE.)
     363              : 
     364              :       END IF
     365           12 :       CALL cp_fm_scale_and_add(1.0_dp, fm_cell1, 1.0_dp, fm_cell0)
     366              : 
     367              :       ! symmetric matrix fm_cell0
     368              :       CALL negf_copy_sym_dbcsr_to_fm_submat(matrix_cells_raw(0)%matrix, fm_cell0, atom_list0, atom_list0, &
     369           12 :                                             subsys, mpi_comm_global, do_upper_diag=.TRUE., do_lower=.TRUE.)
     370              : 
     371              :       ! clean up
     372           12 :       DEALLOCATE (c_to_i, i_to_c)
     373          216 :       DO ic = 1, ncell
     374          216 :          CALL dbcsr_release(mat_nosym(ic)%matrix)
     375              :       END DO
     376           12 :       CALL dbcsr_deallocate_matrix_set(mat_nosym)
     377           72 :       DO rep = -2, 2
     378           72 :          CALL dbcsr_deallocate_matrix(matrix_cells_raw(rep)%matrix)
     379              :       END DO
     380           12 :       DEALLOCATE (matrix_cells_raw)
     381              : 
     382           12 :       CALL timestop(handle)
     383           12 :    END SUBROUTINE negf_copy_contact_matrix
     384              : 
     385              : ! **************************************************************************************************
     386              : !> \brief Extract part of the DBCSR matrix based on selected atoms and copy it into another DBCSR
     387              : !>        matrix.
     388              : !> \param matrix_contact  extracted DBCSR matrix
     389              : !> \param matrix_device   original DBCSR matrix
     390              : !> \param atom_list       list of selected atoms
     391              : !> \param atom_map        atomic map between device and contact force environments
     392              : !> \param para_env        parallel environment
     393              : ! **************************************************************************************************
     394            0 :    SUBROUTINE negf_reference_contact_matrix(matrix_contact, matrix_device, atom_list, atom_map, para_env)
     395              :       TYPE(dbcsr_type), POINTER                          :: matrix_contact, matrix_device
     396              :       INTEGER, DIMENSION(:), INTENT(in)                  :: atom_list
     397              :       TYPE(negf_atom_map_type), DIMENSION(:), INTENT(in) :: atom_map
     398              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     399              : 
     400              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'negf_reference_contact_matrix'
     401              : 
     402              :       INTEGER                                            :: handle, i1, i2, iatom_col, iatom_row, &
     403              :                                                             icol, iproc, irow, max_atom, &
     404              :                                                             mepos_plus1, n1, n2, natoms, offset
     405            0 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: recv_nelems, send_nelems
     406              :       INTEGER, ALLOCATABLE, DIMENSION(:, :)              :: rank_contact, rank_device
     407              :       LOGICAL                                            :: found, transp
     408            0 :       REAL(kind=dp), DIMENSION(:, :), POINTER            :: rblock
     409            0 :       TYPE(mp_request_type), ALLOCATABLE, DIMENSION(:)   :: recv_handlers, send_handlers
     410              :       TYPE(negf_allocatable_rvector), ALLOCATABLE, &
     411            0 :          DIMENSION(:)                                    :: recv_packed_blocks, send_packed_blocks
     412              : 
     413            0 :       CALL timeset(routineN, handle)
     414            0 :       mepos_plus1 = para_env%mepos + 1
     415              : 
     416            0 :       natoms = SIZE(atom_list)
     417            0 :       max_atom = 0
     418            0 :       DO iatom_row = 1, natoms
     419            0 :          IF (atom_map(iatom_row)%iatom > max_atom) max_atom = atom_map(iatom_row)%iatom
     420              :       END DO
     421              : 
     422              :       ! find out which block goes to which node
     423            0 :       ALLOCATE (rank_contact(max_atom, max_atom))
     424            0 :       ALLOCATE (rank_device(max_atom, max_atom))
     425              : 
     426            0 :       rank_contact(:, :) = 0
     427            0 :       rank_device(:, :) = 0
     428              : 
     429            0 :       DO iatom_col = 1, natoms
     430            0 :          DO iatom_row = 1, iatom_col
     431            0 :             IF (atom_map(iatom_row)%iatom <= atom_map(iatom_col)%iatom) THEN
     432            0 :                icol = atom_map(iatom_col)%iatom
     433            0 :                irow = atom_map(iatom_row)%iatom
     434              :             ELSE
     435            0 :                icol = atom_map(iatom_row)%iatom
     436            0 :                irow = atom_map(iatom_col)%iatom
     437              :             END IF
     438              : 
     439              :             CALL dbcsr_get_block_p(matrix=matrix_device, &
     440              :                                    row=atom_list(iatom_row), col=atom_list(iatom_col), &
     441            0 :                                    block=rblock, found=found)
     442            0 :             IF (found) rank_device(irow, icol) = mepos_plus1
     443              : 
     444            0 :             CALL dbcsr_get_block_p(matrix=matrix_contact, row=irow, col=icol, block=rblock, found=found)
     445            0 :             IF (found) rank_contact(irow, icol) = mepos_plus1
     446              :          END DO
     447              :       END DO
     448              : 
     449            0 :       CALL para_env%sum(rank_device)
     450            0 :       CALL para_env%sum(rank_contact)
     451              : 
     452              :       ! compute number of packed matrix elements to send to / receive from each processor
     453            0 :       ALLOCATE (recv_nelems(para_env%num_pe))
     454            0 :       ALLOCATE (send_nelems(para_env%num_pe))
     455            0 :       recv_nelems(:) = 0
     456            0 :       send_nelems(:) = 0
     457              : 
     458            0 :       DO iatom_col = 1, natoms
     459            0 :          DO iatom_row = 1, iatom_col
     460            0 :             IF (atom_map(iatom_row)%iatom <= atom_map(iatom_col)%iatom) THEN
     461            0 :                icol = atom_map(iatom_col)%iatom
     462            0 :                irow = atom_map(iatom_row)%iatom
     463              :             ELSE
     464            0 :                icol = atom_map(iatom_row)%iatom
     465            0 :                irow = atom_map(iatom_col)%iatom
     466              :             END IF
     467              : 
     468              :             CALL dbcsr_get_block_p(matrix=matrix_device, &
     469              :                                    row=atom_list(iatom_row), col=atom_list(iatom_col), &
     470            0 :                                    block=rblock, found=found)
     471            0 :             IF (found) THEN
     472            0 :                iproc = rank_contact(irow, icol)
     473            0 :                IF (iproc > 0) THEN
     474            0 :                   send_nelems(iproc) = send_nelems(iproc) + SIZE(rblock)
     475              :                END IF
     476              :             END IF
     477              : 
     478            0 :             CALL dbcsr_get_block_p(matrix=matrix_contact, row=irow, col=icol, block=rblock, found=found)
     479            0 :             IF (found) THEN
     480            0 :                iproc = rank_device(irow, icol)
     481            0 :                IF (iproc > 0) THEN
     482            0 :                   recv_nelems(iproc) = recv_nelems(iproc) + SIZE(rblock)
     483              :                END IF
     484              :             END IF
     485              :          END DO
     486              :       END DO
     487              : 
     488              :       ! pack blocks
     489            0 :       ALLOCATE (recv_packed_blocks(para_env%num_pe))
     490            0 :       DO iproc = 1, para_env%num_pe
     491            0 :          IF (iproc /= mepos_plus1 .AND. recv_nelems(iproc) > 0) THEN
     492            0 :             ALLOCATE (recv_packed_blocks(iproc)%vector(recv_nelems(iproc)))
     493              :          END IF
     494              :       END DO
     495              : 
     496            0 :       ALLOCATE (send_packed_blocks(para_env%num_pe))
     497            0 :       DO iproc = 1, para_env%num_pe
     498            0 :          IF (send_nelems(iproc) > 0) THEN
     499            0 :             ALLOCATE (send_packed_blocks(iproc)%vector(send_nelems(iproc)))
     500              :          END IF
     501              :       END DO
     502              : 
     503            0 :       send_nelems(:) = 0
     504            0 :       DO iatom_col = 1, natoms
     505            0 :          DO iatom_row = 1, iatom_col
     506            0 :             IF (atom_map(iatom_row)%iatom <= atom_map(iatom_col)%iatom) THEN
     507            0 :                icol = atom_map(iatom_col)%iatom
     508            0 :                irow = atom_map(iatom_row)%iatom
     509            0 :                transp = .FALSE.
     510              :             ELSE
     511            0 :                icol = atom_map(iatom_row)%iatom
     512            0 :                irow = atom_map(iatom_col)%iatom
     513            0 :                transp = .TRUE.
     514              :             END IF
     515              : 
     516            0 :             iproc = rank_contact(irow, icol)
     517            0 :             IF (iproc > 0) THEN
     518              :                CALL dbcsr_get_block_p(matrix=matrix_device, &
     519              :                                       row=atom_list(iatom_row), col=atom_list(iatom_col), &
     520            0 :                                       block=rblock, found=found)
     521            0 :                IF (found) THEN
     522            0 :                   offset = send_nelems(iproc)
     523            0 :                   n1 = SIZE(rblock, 1)
     524            0 :                   n2 = SIZE(rblock, 2)
     525              : 
     526            0 :                   IF (transp) THEN
     527            0 :                      DO i1 = 1, n1
     528            0 :                         DO i2 = 1, n2
     529            0 :                            send_packed_blocks(iproc)%vector(offset + i2) = rblock(i1, i2)
     530              :                         END DO
     531            0 :                         offset = offset + n2
     532              :                      END DO
     533              :                   ELSE
     534            0 :                      DO i2 = 1, n2
     535            0 :                         DO i1 = 1, n1
     536            0 :                            send_packed_blocks(iproc)%vector(offset + i1) = rblock(i1, i2)
     537              :                         END DO
     538            0 :                         offset = offset + n1
     539              :                      END DO
     540              :                   END IF
     541              : 
     542            0 :                   send_nelems(iproc) = offset
     543              :                END IF
     544              :             END IF
     545              :          END DO
     546              :       END DO
     547              : 
     548              :       ! send blocks
     549            0 :       ALLOCATE (recv_handlers(para_env%num_pe), send_handlers(para_env%num_pe))
     550              : 
     551            0 :       DO iproc = 1, para_env%num_pe
     552            0 :          IF (iproc /= mepos_plus1 .AND. send_nelems(iproc) > 0) THEN
     553            0 :             CALL para_env%isend(send_packed_blocks(iproc)%vector, iproc - 1, send_handlers(iproc), 1)
     554              :          END IF
     555              :       END DO
     556              : 
     557              :       ! receive blocks
     558            0 :       DO iproc = 1, para_env%num_pe
     559            0 :          IF (iproc /= mepos_plus1) THEN
     560            0 :             IF (recv_nelems(iproc) > 0) THEN
     561            0 :                CALL para_env%irecv(recv_packed_blocks(iproc)%vector, iproc - 1, recv_handlers(iproc), 1)
     562              :             END IF
     563              :          ELSE
     564            0 :             IF (ALLOCATED(send_packed_blocks(iproc)%vector)) THEN
     565            0 :                CALL MOVE_ALLOC(send_packed_blocks(iproc)%vector, recv_packed_blocks(iproc)%vector)
     566              :             END IF
     567              :          END IF
     568              :       END DO
     569              : 
     570              :       ! unpack blocks
     571            0 :       DO iproc = 1, para_env%num_pe
     572            0 :          IF (iproc /= mepos_plus1 .AND. recv_nelems(iproc) > 0) THEN
     573            0 :             CALL recv_handlers(iproc)%wait()
     574              :          END IF
     575              :       END DO
     576              : 
     577            0 :       recv_nelems(:) = 0
     578            0 :       DO iatom_col = 1, natoms
     579            0 :          DO iatom_row = 1, iatom_col
     580            0 :             IF (atom_map(iatom_row)%iatom <= atom_map(iatom_col)%iatom) THEN
     581            0 :                icol = atom_map(iatom_col)%iatom
     582            0 :                irow = atom_map(iatom_row)%iatom
     583              :             ELSE
     584            0 :                icol = atom_map(iatom_row)%iatom
     585            0 :                irow = atom_map(iatom_col)%iatom
     586              :             END IF
     587              : 
     588            0 :             iproc = rank_device(irow, icol)
     589            0 :             IF (iproc > 0) THEN
     590            0 :                CALL dbcsr_get_block_p(matrix=matrix_contact, row=irow, col=icol, block=rblock, found=found)
     591              : 
     592            0 :                IF (found) THEN
     593            0 :                   offset = recv_nelems(iproc)
     594            0 :                   n1 = SIZE(rblock, 1)
     595            0 :                   n2 = SIZE(rblock, 2)
     596              : 
     597            0 :                   DO i2 = 1, n2
     598            0 :                      DO i1 = 1, n1
     599            0 :                         rblock(i1, i2) = recv_packed_blocks(iproc)%vector(offset + i1)
     600              :                      END DO
     601            0 :                      offset = offset + n1
     602              :                   END DO
     603              : 
     604            0 :                   recv_nelems(iproc) = offset
     605              :                END IF
     606              :             END IF
     607              :          END DO
     608              :       END DO
     609              : 
     610            0 :       DO iproc = 1, para_env%num_pe
     611            0 :          IF (iproc /= mepos_plus1 .AND. send_nelems(iproc) > 0) THEN
     612            0 :             CALL send_handlers(iproc)%wait()
     613              :          END IF
     614              :       END DO
     615              : 
     616              :       ! release memory
     617            0 :       DEALLOCATE (recv_handlers, send_handlers)
     618              : 
     619            0 :       DO iproc = para_env%num_pe, 1, -1
     620            0 :          IF (ALLOCATED(send_packed_blocks(iproc)%vector)) THEN
     621            0 :             DEALLOCATE (send_packed_blocks(iproc)%vector)
     622              :          END IF
     623              :       END DO
     624            0 :       DEALLOCATE (send_packed_blocks)
     625              : 
     626            0 :       DO iproc = para_env%num_pe, 1, -1
     627            0 :          IF (ALLOCATED(recv_packed_blocks(iproc)%vector)) THEN
     628            0 :             DEALLOCATE (recv_packed_blocks(iproc)%vector)
     629              :          END IF
     630              :       END DO
     631            0 :       DEALLOCATE (recv_packed_blocks)
     632              : 
     633            0 :       DEALLOCATE (rank_contact, rank_device)
     634            0 :       CALL timestop(handle)
     635            0 :    END SUBROUTINE negf_reference_contact_matrix
     636              : 
     637              : ! **************************************************************************************************
     638              : !> \brief Invert cell_to_index mapping between unit cells and DBCSR matrix images.
     639              : !> \param cell_to_index  mapping: unit_cell -> image_index
     640              : !> \param nimages        number of images
     641              : !> \param index_to_cell  inverted mapping: image_index -> unit_cell
     642              : !> \par History
     643              : !>   * 10.2017 created [Sergey Chulkov]
     644              : ! **************************************************************************************************
     645           16 :    SUBROUTINE invert_cell_to_index(cell_to_index, nimages, index_to_cell)
     646              :       INTEGER, DIMENSION(:, :, :), POINTER               :: cell_to_index
     647              :       INTEGER, INTENT(in)                                :: nimages
     648              :       INTEGER, DIMENSION(3, nimages), INTENT(out)        :: index_to_cell
     649              : 
     650              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'invert_cell_to_index'
     651              : 
     652              :       INTEGER                                            :: handle, i1, i2, i3, image
     653              :       INTEGER, DIMENSION(3)                              :: lbounds, ubounds
     654              : 
     655           16 :       CALL timeset(routineN, handle)
     656              : 
     657          560 :       index_to_cell(:, :) = 0
     658           64 :       lbounds = LBOUND(cell_to_index)
     659           64 :       ubounds = UBOUND(cell_to_index)
     660              : 
     661           64 :       DO i3 = lbounds(3), ubounds(3) ! z
     662          192 :          DO i2 = lbounds(2), ubounds(2) ! y
     663          544 :             DO i1 = lbounds(1), ubounds(1) ! x
     664          368 :                image = cell_to_index(i1, i2, i3)
     665          496 :                IF (image > 0 .AND. image <= nimages) THEN
     666          136 :                   index_to_cell(1, image) = i1
     667          136 :                   index_to_cell(2, image) = i2
     668          136 :                   index_to_cell(3, image) = i3
     669              :                END IF
     670              :             END DO
     671              :          END DO
     672              :       END DO
     673              : 
     674           16 :       CALL timestop(handle)
     675           16 :    END SUBROUTINE invert_cell_to_index
     676              : 
     677              : ! **************************************************************************************************
     678              : !> \brief Helper routine to obtain index of a DBCSR matrix image by its unit cell replica.
     679              : !>        Can be used with any usin cell.
     680              : !> \param cell           indices of the unit cell
     681              : !> \param cell_to_index  mapping: unit_cell -> image_index
     682              : !> \return DBCSR matrix images
     683              : !>                       (0 means there are no non-zero matrix elements in the image)
     684              : !> \par History
     685              : !>   * 10.2017 created [Sergey Chulkov]
     686              : ! **************************************************************************************************
     687        14888 :    PURE FUNCTION get_index_by_cell(cell, cell_to_index) RESULT(image)
     688              :       INTEGER, DIMENSION(3), INTENT(in)                  :: cell
     689              :       INTEGER, DIMENSION(:, :, :), POINTER               :: cell_to_index
     690              :       INTEGER                                            :: image
     691              : 
     692              :       IF (LBOUND(cell_to_index, 1) <= cell(1) .AND. UBOUND(cell_to_index, 1) >= cell(1) .AND. &
     693              :           LBOUND(cell_to_index, 2) <= cell(2) .AND. UBOUND(cell_to_index, 2) >= cell(2) .AND. &
     694       104216 :           LBOUND(cell_to_index, 3) <= cell(3) .AND. UBOUND(cell_to_index, 3) >= cell(3)) THEN
     695              : 
     696        14888 :          image = cell_to_index(cell(1), cell(2), cell(3))
     697              :       ELSE
     698              :          image = 0
     699              :       END IF
     700        14888 :    END FUNCTION get_index_by_cell
     701              : 
     702              : ! **************************************************************************************************
     703              : !> \brief Desymmetrizes the KS or S matrices for one of spin components
     704              : !> \param mat Hamiltonian or overlap matrices
     705              : !> \param mat_nosym Desymmetrized Hamiltonian or overlap matrices
     706              : !> \param cell_to_index Mapping of cell indices to linear RS indices
     707              : !> \param index_to_cell Mapping of linear RS indices to cell indices
     708              : !> \param kpoints Kpoint environment
     709              : !> \par History
     710              : !>   * 05.2020 created [Fabian Ducry]
     711              : !>   * 11.2025 Modified for one spin component. [Dmitry Ryndyk]
     712              : !> \author Fabian Ducry
     713              : ! **************************************************************************************************
     714           12 :    SUBROUTINE desymmetrize_matrix(mat, mat_nosym, cell_to_index, index_to_cell, kpoints)
     715              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(IN), &
     716              :          POINTER                                         :: mat
     717              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(INOUT), &
     718              :          POINTER                                         :: mat_nosym
     719              :       INTEGER, ALLOCATABLE, DIMENSION(:, :, :), &
     720              :          INTENT(OUT)                                     :: cell_to_index
     721              :       INTEGER, ALLOCATABLE, DIMENSION(:, :), INTENT(OUT) :: index_to_cell
     722              :       TYPE(kpoint_type), INTENT(IN), POINTER             :: kpoints
     723              : 
     724              :       CHARACTER(len=*), PARAMETER :: routineN = 'desymmetrize_matrix'
     725              : 
     726              :       INTEGER                                            :: handle, iatom, ic, icn, icol, irow, &
     727              :                                                             jatom, ncell, nomirror, nx, ny, nz
     728              :       INTEGER, DIMENSION(3)                              :: cell
     729           12 :       INTEGER, DIMENSION(:, :), POINTER                  :: i2c
     730           12 :       INTEGER, DIMENSION(:, :, :), POINTER               :: c2i
     731              :       LOGICAL                                            :: found, lwtr
     732           12 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: block
     733              :       TYPE(neighbor_list_iterator_p_type), &
     734           12 :          DIMENSION(:), POINTER                           :: nl_iterator
     735              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
     736           12 :          POINTER                                         :: sab_nl
     737              : 
     738           12 :       CALL timeset(routineN, handle)
     739              : 
     740           12 :       i2c => kpoints%index_to_cell
     741           12 :       c2i => kpoints%cell_to_index
     742              : 
     743           12 :       ncell = SIZE(i2c, 2)
     744              : 
     745           12 :       nx = MAX(ABS(LBOUND(c2i, 1)), ABS(UBOUND(c2i, 1)))
     746           12 :       ny = MAX(ABS(LBOUND(c2i, 2)), ABS(UBOUND(c2i, 3)))
     747           12 :       nz = MAX(ABS(LBOUND(c2i, 3)), ABS(UBOUND(c2i, 3)))
     748           60 :       ALLOCATE (cell_to_index(-nx:nx, -ny:ny, -nz:nz))
     749              :       cell_to_index(LBOUND(c2i, 1):UBOUND(c2i, 1), &
     750              :                     LBOUND(c2i, 2):UBOUND(c2i, 2), &
     751          792 :                     LBOUND(c2i, 3):UBOUND(c2i, 3)) = c2i
     752              : 
     753              :       ! identify cells with no mirror img
     754              :       nomirror = 0
     755          204 :       DO ic = 1, ncell
     756          768 :          cell = i2c(:, ic)
     757          204 :          IF (cell_to_index(-cell(1), -cell(2), -cell(3)) == 0) THEN
     758           12 :             nomirror = nomirror + 1
     759              :          END IF
     760              :       END DO
     761              : 
     762              :       ! create the mirror imgs
     763           36 :       ALLOCATE (index_to_cell(3, ncell + nomirror))
     764          780 :       index_to_cell(:, 1:ncell) = i2c
     765              : 
     766              :       nomirror = 0 ! count the imgs without mirror
     767          204 :       DO ic = 1, ncell
     768          768 :          cell = index_to_cell(:, ic)
     769          204 :          IF (cell_to_index(-cell(1), -cell(2), -cell(3)) == 0) THEN
     770           12 :             nomirror = nomirror + 1
     771           48 :             index_to_cell(:, ncell + nomirror) = -cell
     772           12 :             cell_to_index(-cell(1), -cell(2), -cell(3)) = ncell + nomirror
     773              :          END IF
     774              :       END DO
     775           12 :       ncell = ncell + nomirror
     776              : 
     777           12 :       CALL get_kpoint_info(kpoints, sab_nl=sab_nl)
     778              :       ! allocate the nonsymmetric matrices
     779           12 :       NULLIFY (mat_nosym)
     780           12 :       CALL dbcsr_allocate_matrix_set(mat_nosym, ncell)
     781          216 :       DO ic = 1, ncell
     782          204 :          ALLOCATE (mat_nosym(ic)%matrix)
     783              :          CALL dbcsr_create(matrix=mat_nosym(ic)%matrix, &
     784              :                            template=mat(1)%matrix, &
     785          204 :                            matrix_type=dbcsr_type_no_symmetry)
     786              :          CALL cp_dbcsr_alloc_block_from_nbl(mat_nosym(ic)%matrix, &
     787          204 :                                             sab_nl, desymmetrize=.TRUE.)
     788          216 :          CALL dbcsr_set(mat_nosym(ic)%matrix, 0.0_dp)
     789              :       END DO
     790              : 
     791              :       ! desymmetrize the matrix for real space printing
     792           12 :       CALL neighbor_list_iterator_create(nl_iterator, sab_nl)
     793          564 :       DO WHILE (neighbor_list_iterate(nl_iterator) == 0)
     794          552 :          CALL get_iterator_info(nl_iterator, iatom=iatom, jatom=jatom, cell=cell)
     795              : 
     796          552 :          ic = cell_to_index(cell(1), cell(2), cell(3))
     797          552 :          icn = cell_to_index(-cell(1), -cell(2), -cell(3))
     798          552 :          CPASSERT(icn > 0)
     799              : 
     800          552 :          irow = iatom
     801          552 :          icol = jatom
     802          552 :          lwtr = .FALSE.
     803              :          ! always copy from the top
     804          552 :          IF (iatom > jatom) THEN
     805          264 :             irow = jatom
     806          264 :             icol = iatom
     807          264 :             lwtr = .TRUE.
     808              :          END IF
     809              : 
     810              :          CALL dbcsr_get_block_p(matrix=mat(ic)%matrix, &
     811          552 :                                 row=irow, col=icol, block=block, found=found)
     812          552 :          CPASSERT(found)
     813              : 
     814              :          ! copy to M(R) at (iatom,jatom)
     815              :          ! copy to M(-R) at (jatom,iatom)
     816          564 :          IF (lwtr) THEN
     817              :             CALL dbcsr_put_block(matrix=mat_nosym(ic)%matrix, &
     818          792 :                                  row=iatom, col=jatom, block=TRANSPOSE(block))
     819              :             CALL dbcsr_put_block(matrix=mat_nosym(icn)%matrix, &
     820          264 :                                  row=jatom, col=iatom, block=block)
     821              :          ELSE
     822              :             CALL dbcsr_put_block(matrix=mat_nosym(ic)%matrix, &
     823          288 :                                  row=iatom, col=jatom, block=block)
     824              :             CALL dbcsr_put_block(matrix=mat_nosym(icn)%matrix, &
     825          864 :                                  row=jatom, col=iatom, block=TRANSPOSE(block))
     826              :          END IF
     827              :       END DO
     828           12 :       CALL neighbor_list_iterator_release(nl_iterator)
     829              : 
     830          216 :       DO ic = 1, ncell
     831          216 :          CALL dbcsr_finalize(mat_nosym(ic)%matrix)
     832              :       END DO
     833              : 
     834           12 :       CALL timestop(handle)
     835              : 
     836           12 :    END SUBROUTINE desymmetrize_matrix
     837              : 
     838              : END MODULE negf_matrix_utils
        

Generated by: LCOV version 2.0-1