LCOV - code coverage report
Current view: top level - src/dbt - dbt_test.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:2fce0f8) Lines: 255 262 97.3 %
Date: 2024-12-21 06:28:57 Functions: 15 15 100.0 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2024 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \brief General methods for testing DBT tensors.
      10             : !> \author Patrick Seewald
      11             : ! **************************************************************************************************
      12             : MODULE dbt_test
      13             :    #:include "dbt_macros.fypp"
      14             :    #:set maxdim = maxrank
      15             :    #:set ndims = range(2,maxdim+1)
      16             : 
      17             :    USE dbt_tas_base, ONLY: dbt_tas_info
      18             :    USE dbm_tests, ONLY: generate_larnv_seed
      19             :    USE dbt_methods, ONLY: &
      20             :       dbt_copy, dbt_get_block, dbt_iterator_type, dbt_iterator_blocks_left, &
      21             :       dbt_iterator_next_block, dbt_iterator_start, dbt_iterator_stop, &
      22             :       dbt_reserve_blocks, dbt_get_stored_coordinates, dbt_put_block, &
      23             :       dbt_contract, dbt_inverse_order
      24             :    USE dbt_block, ONLY: block_nd
      25             :    USE dbt_types, ONLY: &
      26             :       dbt_create, dbt_destroy, dbt_type, dbt_distribution_type, &
      27             :       dbt_distribution_destroy, dims_tensor, ndims_tensor, dbt_distribution_new, &
      28             :       mp_environ_pgrid, dbt_pgrid_type, dbt_pgrid_create, dbt_pgrid_destroy, dbt_get_info, &
      29             :       dbt_default_distvec
      30             :    USE dbt_io, ONLY: &
      31             :       dbt_write_blocks, dbt_write_block_indices
      32             :    USE kinds, ONLY: dp, default_string_length, int_8, dp
      33             :    USE dbt_allocate_wrap, ONLY: allocate_any
      34             :    USE dbt_index, ONLY: &
      35             :       combine_tensor_index, get_2d_indices_tensor, dbt_get_mapping_info
      36             :    USE dbt_tas_test, ONLY: dbt_tas_checksum
      37             :    USE message_passing, ONLY: mp_comm_type
      38             : 
      39             : #include "../base/base_uses.f90"
      40             : 
      41             :    IMPLICIT NONE
      42             :    PRIVATE
      43             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'dbt_test'
      44             : 
      45             :    PUBLIC :: &
      46             :       dbt_setup_test_tensor, &
      47             :       dbt_contract_test, &
      48             :       dbt_test_formats, &
      49             :       dbt_checksum, &
      50             :       dbt_reset_randmat_seed
      51             : 
      52             :    INTERFACE dist_sparse_tensor_to_repl_dense_array
      53             :       #:for ndim in ndims
      54             :          MODULE PROCEDURE dist_sparse_tensor_to_repl_dense_${ndim}$d_array
      55             :       #:endfor
      56             :    END INTERFACE
      57             : 
      58             :    INTEGER, SAVE :: randmat_counter = 0
      59             :    INTEGER, PARAMETER, PRIVATE :: rand_seed_init = 12341313
      60             : 
      61             : CONTAINS
      62             : 
      63             : ! **************************************************************************************************
      64             : !> \brief check if two (arbitrarily mapped and distributed) tensors are equal.
      65             : !> \author Patrick Seewald
      66             : ! **************************************************************************************************
      67         172 :    FUNCTION dbt_equal(tensor1, tensor2)
      68             :       TYPE(dbt_type), INTENT(INOUT)          :: tensor1, tensor2
      69             :       LOGICAL                                    :: dbt_equal
      70             : 
      71        1204 :       TYPE(dbt_type)                         :: tensor2_tmp
      72             :       TYPE(dbt_iterator_type)                :: iter
      73         172 :       TYPE(block_nd)                             :: blk_data1, blk_data2
      74         172 :       INTEGER, DIMENSION(ndims_tensor(tensor1)) :: blk_size, ind_nd
      75             :       LOGICAL :: found
      76             : 
      77             :       ! create a copy of tensor2 that has exact same data format as tensor1
      78         172 :       CALL dbt_create(tensor1, tensor2_tmp)
      79             : 
      80         172 :       CALL dbt_reserve_blocks(tensor1, tensor2_tmp)
      81         172 :       CALL dbt_copy(tensor2, tensor2_tmp)
      82             : 
      83         172 :       dbt_equal = .TRUE.
      84             : 
      85             : !$OMP PARALLEL DEFAULT(NONE) SHARED(tensor1,tensor2_tmp,dbt_equal) &
      86         172 : !$OMP PRIVATE(iter,ind_nd, blk_size,blk_data1,blk_data2,found)
      87             :       CALL dbt_iterator_start(iter, tensor1)
      88             : 
      89             :       DO WHILE (dbt_iterator_blocks_left(iter))
      90             :          CALL dbt_iterator_next_block(iter, ind_nd, blk_size=blk_size)
      91             :          CALL dbt_get_block(tensor1, ind_nd, blk_data1, found)
      92             :          IF (.NOT. found) CPABORT("Tensor block 1 not found")
      93             :          CALL dbt_get_block(tensor2_tmp, ind_nd, blk_data2, found)
      94             :          IF (.NOT. found) CPABORT("Tensor block 2 not found")
      95             : 
      96             :          IF (.NOT. blocks_equal(blk_data1, blk_data2)) THEN
      97             : !$OMP CRITICAL
      98             :             dbt_equal = .FALSE.
      99             : !$OMP END CRITICAL
     100             :          END IF
     101             :       END DO
     102             : 
     103             :       CALL dbt_iterator_stop(iter)
     104             : !$OMP END PARALLEL
     105             : 
     106         172 :       CALL dbt_destroy(tensor2_tmp)
     107         344 :    END FUNCTION
     108             : 
     109             : ! **************************************************************************************************
     110             : !> \brief check if two blocks are equal
     111             : !> \author Patrick Seewald
     112             : ! **************************************************************************************************
     113        1464 :    PURE FUNCTION blocks_equal(block1, block2)
     114             :       TYPE(block_nd), INTENT(IN) :: block1, block2
     115             :       LOGICAL                    :: blocks_equal
     116             : 
     117     1809044 :       blocks_equal = MAXVAL(ABS(block1%blk - block2%blk)) .LT. 1.0E-12_dp
     118             : 
     119        1464 :    END FUNCTION
     120             : 
     121             : ! **************************************************************************************************
     122             : !> \brief Compute factorial
     123             : !> \author Patrick Seewald
     124             : ! **************************************************************************************************
     125          12 :    PURE FUNCTION factorial(n)
     126             :       INTEGER, INTENT(IN) :: n
     127             :       INTEGER             :: k
     128             :       INTEGER             :: factorial
     129          96 :       factorial = PRODUCT((/(k, k=1, n)/))
     130          12 :    END FUNCTION
     131             : 
     132             : ! **************************************************************************************************
     133             : !> \brief Compute all permutations p of (1, 2, ..., n)
     134             : !> \author Patrick Seewald
     135             : ! **************************************************************************************************
     136           6 :    SUBROUTINE permute(n, p)
     137             :       INTEGER, INTENT(IN)                              :: n
     138             :       INTEGER                                          :: i, c
     139          12 :       INTEGER, DIMENSION(n)                            :: pp
     140             :       INTEGER, DIMENSION(n, factorial(n)), INTENT(OUT) :: p
     141             : 
     142          48 :       pp = [(i, i=1, n)]
     143           6 :       c = 1
     144           6 :       CALL perm(1)
     145             :    CONTAINS
     146         108 :       RECURSIVE SUBROUTINE perm(i)
     147             :          INTEGER, INTENT(IN) :: i
     148             :          INTEGER :: j, t
     149         108 :          IF (i == n) THEN
     150         300 :             p(:, c) = pp(:)
     151          64 :             c = c + 1
     152             :          ELSE
     153         146 :             DO j = i, n
     154         102 :                t = pp(i)
     155         102 :                pp(i) = pp(j)
     156         102 :                pp(j) = t
     157         102 :                call perm(i + 1)
     158         102 :                t = pp(i)
     159         102 :                pp(i) = pp(j)
     160         146 :                pp(j) = t
     161             :             END DO
     162             :          END IF
     163         108 :       END SUBROUTINE
     164             :    END SUBROUTINE
     165             : 
     166             : ! **************************************************************************************************
     167             : !> \brief Test equivalence of all tensor formats, using a random distribution.
     168             : !> \param blk_size_i block sizes along respective dimension
     169             : !> \param blk_ind_i index along respective dimension of non-zero blocks
     170             : !> \param ndims tensor rank
     171             : !> \param unit_nr output unit, needs to be a valid unit number on all mpi ranks
     172             : !> \param verbose if .TRUE., print all tensor blocks
     173             : !> \author Patrick Seewald
     174             : ! **************************************************************************************************
     175           6 :    SUBROUTINE dbt_test_formats(ndims, mp_comm, unit_nr, verbose, &
     176           6 :                                ${varlist("blk_size")}$, &
     177           6 :                                ${varlist("blk_ind")}$)
     178             :       INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL :: ${varlist("blk_size")}$
     179             :       INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL :: ${varlist("blk_ind")}$
     180             :       INTEGER, INTENT(IN)                         :: ndims
     181             :       INTEGER, INTENT(IN)                         :: unit_nr
     182             :       LOGICAL, INTENT(IN)                         :: verbose
     183             :       TYPE(mp_comm_type), INTENT(IN)              :: mp_comm
     184          84 :       TYPE(dbt_distribution_type)             :: dist1, dist2
     185          78 :       TYPE(dbt_type)                          :: tensor1, tensor2
     186             :       INTEGER                                     :: isep, iblk
     187           6 :       INTEGER, DIMENSION(:), ALLOCATABLE          :: ${varlist("dist1")}$, &
     188           6 :                                                      ${varlist("dist2")}$
     189             :       INTEGER                                     :: nblks, imap
     190          12 :       INTEGER, DIMENSION(ndims)                   :: pdims, myploc
     191             :       LOGICAL                                     :: eql
     192             :       INTEGER                                     :: iperm, idist, icount
     193           6 :       INTEGER, DIMENSION(:), ALLOCATABLE          :: map1, map2, map1_ref, map2_ref
     194           6 :       INTEGER, DIMENSION(ndims, factorial(ndims)) :: perm
     195             :       INTEGER                                     :: io_unit
     196             :       INTEGER                                     :: mynode
     197          18 :       TYPE(dbt_pgrid_type)                    :: comm_nd
     198             :       CHARACTER(LEN=default_string_length)        :: tensor_name
     199             : 
     200             :       ! Process grid
     201          24 :       pdims(:) = 0
     202           6 :       CALL dbt_pgrid_create(mp_comm, pdims, comm_nd)
     203           6 :       mynode = mp_comm%mepos
     204             : 
     205           6 :       io_unit = 0
     206           6 :       IF (mynode .EQ. 0) io_unit = unit_nr
     207             : 
     208           6 :       CALL permute(ndims, perm)
     209          26 :       ALLOCATE (map1_ref, source=perm(1:ndims/2, 1))
     210          28 :       ALLOCATE (map2_ref, source=perm(ndims/2 + 1:ndims, 1))
     211             : 
     212           6 :       IF (io_unit > 0) THEN
     213           3 :          WRITE (io_unit, *)
     214           3 :          WRITE (io_unit, '(A)') repeat("-", 80)
     215           3 :          WRITE (io_unit, '(A,1X,I1)') "Testing matrix representations of tensor rank", ndims
     216           3 :          WRITE (io_unit, '(A)') repeat("-", 80)
     217           3 :          WRITE (io_unit, '(A)') "Block sizes:"
     218             : 
     219             :          #:for dim in range(1, maxdim+1)
     220          11 :             IF (ndims >= ${dim}$) THEN
     221           9 :                WRITE (io_unit, '(T4,A,1X,I1,A,1X)', advance='no') 'Dim', ${dim}$, ':'
     222          82 :                DO iblk = 1, SIZE(blk_size_${dim}$)
     223          82 :                   WRITE (io_unit, '(I2,1X)', advance='no') blk_size_${dim}$ (iblk)
     224             :                END DO
     225           9 :                WRITE (io_unit, *)
     226             :             END IF
     227             :          #:endfor
     228             : 
     229           3 :          WRITE (io_unit, '(A)') "Non-zero blocks:"
     230          40 :          DO iblk = 1, SIZE(blk_ind_1)
     231             :             #:for ndim in ndims
     232          58 :                IF (ndims == ${ndim}$) THEN
     233             :                   WRITE (io_unit, '(T4,A, I3, A, ${ndim}$I3, 1X, A)') &
     234          37 :                      'Block', iblk, ': (', ${varlist("blk_ind", nmax=ndim, suffix='(iblk)')}$, ')'
     235             :                END IF
     236             :             #:endfor
     237             :          END DO
     238             : 
     239           3 :          WRITE (io_unit, *)
     240           3 :          WRITE (io_unit, '(A,1X)', advance='no') "Reference map:"
     241           3 :          WRITE (io_unit, '(A1,1X)', advance='no') "("
     242           7 :          DO imap = 1, SIZE(map1_ref)
     243           7 :             WRITE (io_unit, '(I1,1X)', advance='no') map1_ref(imap)
     244             :          END DO
     245           3 :          WRITE (io_unit, '(A1,1X)', advance='no') "|"
     246           8 :          DO imap = 1, SIZE(map2_ref)
     247           8 :             WRITE (io_unit, '(I1,1X)', advance='no') map2_ref(imap)
     248             :          END DO
     249           3 :          WRITE (io_unit, '(A1)') ")"
     250             : 
     251             :       END IF
     252             : 
     253           6 :       icount = 0
     254          70 :       DO iperm = 1, factorial(ndims)
     255         242 :          DO isep = 1, ndims - 1
     256         172 :             icount = icount + 1
     257             : 
     258         844 :             ALLOCATE (map1, source=perm(1:isep, iperm))
     259         844 :             ALLOCATE (map2, source=perm(isep + 1:ndims, iperm))
     260             : 
     261         172 :             mynode = mp_comm%mepos
     262         172 :             CALL mp_environ_pgrid(comm_nd, pdims, myploc)
     263             : 
     264             :             #:for dim in range(1, maxdim+1)
     265         684 :                IF (${dim}$ <= ndims) THEN
     266         656 :                   nblks = SIZE(blk_size_${dim}$)
     267        1968 :                   ALLOCATE (dist1_${dim}$ (nblks))
     268        1312 :                   ALLOCATE (dist2_${dim}$ (nblks))
     269         656 :                   CALL dbt_default_distvec(nblks, pdims(${dim}$), blk_size_${dim}$, dist1_${dim}$)
     270         656 :                   CALL dbt_default_distvec(nblks, pdims(${dim}$), blk_size_${dim}$, dist2_${dim}$)
     271             :                END IF
     272             :             #:endfor
     273             : 
     274         172 :             WRITE (tensor_name, '(A,1X,I3,1X)') "Test", icount
     275             : 
     276         172 :             IF (io_unit > 0) THEN
     277          86 :                WRITE (io_unit, *)
     278          86 :                WRITE (io_unit, '(A,A,1X)', advance='no') TRIM(tensor_name), ':'
     279          86 :                WRITE (io_unit, '(A1,1X)', advance='no') "("
     280         250 :                DO imap = 1, SIZE(map1)
     281         250 :                   WRITE (io_unit, '(I1,1X)', advance='no') map1(imap)
     282             :                END DO
     283          86 :                WRITE (io_unit, '(A1,1X)', advance='no') "|"
     284         250 :                DO imap = 1, SIZE(map2)
     285         250 :                   WRITE (io_unit, '(I1,1X)', advance='no') map2(imap)
     286             :                END DO
     287          86 :                WRITE (io_unit, '(A1)') ")"
     288             : 
     289          86 :                WRITE (io_unit, '(T4,A)') "Reference distribution:"
     290             :                #:for dim in range(1, maxdim+1)
     291         342 :                   IF (${dim}$ <= ndims) THEN
     292         328 :                      WRITE (io_unit, '(T7,A,1X)', advance='no') "Dist vec ${dim}$:"
     293        2354 :                      DO idist = 1, SIZE(dist2_${dim}$)
     294        2354 :                         WRITE (io_unit, '(I2,1X)', advance='no') dist2_${dim}$ (idist)
     295             :                      END DO
     296         328 :                      WRITE (io_unit, *)
     297             :                   END IF
     298             :                #:endfor
     299             : 
     300          86 :                WRITE (io_unit, '(T4,A)') "Test distribution:"
     301             :                #:for dim in range(1, maxdim+1)
     302         342 :                   IF (${dim}$ <= ndims) THEN
     303         328 :                      WRITE (io_unit, '(T7,A,1X)', advance='no') "Dist vec ${dim}$:"
     304        2354 :                      DO idist = 1, SIZE(dist2_${dim}$)
     305        2354 :                         WRITE (io_unit, '(I2,1X)', advance='no') dist1_${dim}$ (idist)
     306             :                      END DO
     307         328 :                      WRITE (io_unit, *)
     308             :                   END IF
     309             :                #:endfor
     310             :             END IF
     311             : 
     312             :             #:for ndim in ndims
     313         200 :                IF (ndims == ${ndim}$) THEN
     314         172 :                   CALL dbt_distribution_new(dist2, comm_nd, ${varlist("dist2", nmax=ndim)}$)
     315             :                   CALL dbt_create(tensor2, "Ref", dist2, map1_ref, map2_ref, &
     316         172 :                                   ${varlist("blk_size", nmax=ndim)}$)
     317         172 :                   CALL dbt_setup_test_tensor(tensor2, comm_nd%mp_comm_2d, .TRUE., ${varlist("blk_ind", nmax=ndim)}$)
     318             :                END IF
     319             :             #:endfor
     320             : 
     321         172 :             IF (verbose) CALL dbt_write_blocks(tensor2, io_unit, unit_nr)
     322             : 
     323             :             #:for ndim in ndims
     324         200 :                IF (ndims == ${ndim}$) THEN
     325         172 :                   CALL dbt_distribution_new(dist1, comm_nd, ${varlist("dist1", nmax=ndim)}$)
     326             :                   CALL dbt_create(tensor1, tensor_name, dist1, map1, map2, &
     327         172 :                                   ${varlist("blk_size", nmax=ndim)}$)
     328         172 :                   CALL dbt_setup_test_tensor(tensor1, comm_nd%mp_comm_2d, .TRUE., ${varlist("blk_ind", nmax=ndim)}$)
     329             :                END IF
     330             :             #:endfor
     331             : 
     332         172 :             IF (verbose) CALL dbt_write_blocks(tensor1, io_unit, unit_nr)
     333             : 
     334         172 :             eql = dbt_equal(tensor1, tensor2)
     335             : 
     336         172 :             IF (.NOT. eql) THEN
     337           0 :                IF (io_unit > 0) WRITE (io_unit, '(A,1X,A)') TRIM(tensor_name), 'Test failed!'
     338           0 :                CPABORT('')
     339             :             ELSE
     340         172 :                IF (io_unit > 0) WRITE (io_unit, '(A,1X,A)') TRIM(tensor_name), 'Test passed!'
     341             :             END IF
     342         172 :             DEALLOCATE (map1, map2)
     343             : 
     344         172 :             CALL dbt_destroy(tensor1)
     345         172 :             CALL dbt_distribution_destroy(dist1)
     346             : 
     347         172 :             CALL dbt_destroy(tensor2)
     348         172 :             CALL dbt_distribution_destroy(dist2)
     349             : 
     350             :             #:for dim in range(1, maxdim+1)
     351         748 :                IF (${dim}$ <= ndims) THEN
     352         656 :                   DEALLOCATE (dist1_${dim}$, dist2_${dim}$)
     353             :                END IF
     354             :             #:endfor
     355             : 
     356             :          END DO
     357             :       END DO
     358           6 :       CALL dbt_pgrid_destroy(comm_nd)
     359           6 :    END SUBROUTINE
     360             : 
     361             : ! **************************************************************************************************
     362             : !> \brief Allocate and fill test tensor - entries are enumerated by their index s.t. they only depend
     363             : !>        on global properties of the tensor but not on distribution, matrix representation, etc.
     364             : !> \param mp_comm communicator
     365             : !> \param blk_ind_i index along respective dimension of non-zero blocks
     366             : !> \author Patrick Seewald
     367             : ! **************************************************************************************************
     368         404 :    SUBROUTINE dbt_setup_test_tensor(tensor, mp_comm, enumerate, ${varlist("blk_ind")}$)
     369             :       TYPE(dbt_type), INTENT(INOUT)                  :: tensor
     370             :       CLASS(mp_comm_type), INTENT(IN)                     :: mp_comm
     371             :       LOGICAL, INTENT(IN)                                :: enumerate
     372             :       INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL        :: ${varlist("blk_ind")}$
     373             :       INTEGER                                            :: mynode
     374             : 
     375             :       INTEGER                                            :: i, ib, my_nblks_alloc, nblks_alloc, proc, nze
     376         404 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: ${varlist("my_blk_ind")}$
     377         404 :       INTEGER, DIMENSION(ndims_tensor(tensor))          :: blk_index, blk_offset, blk_size, &
     378         404 :                                                            tensor_dims
     379         404 :       INTEGER, DIMENSION(:, :), ALLOCATABLE               :: ind_nd
     380             :       #:for ndim in ndims
     381             :          REAL(KIND=dp), ALLOCATABLE, &
     382         404 :             DIMENSION(${shape_colon(ndim)}$)                :: blk_values_${ndim}$
     383             :       #:endfor
     384             :       TYPE(dbt_iterator_type)                        :: iterator
     385             :       INTEGER, DIMENSION(4)                              :: iseed
     386             :       INTEGER, DIMENSION(2)                              :: blk_index_2d, nblks_2d
     387             : 
     388         404 :       nblks_alloc = SIZE(blk_ind_1)
     389         404 :       mynode = mp_comm%mepos
     390             : 
     391         404 :       IF (.NOT. enumerate) THEN
     392          60 :          CPASSERT(randmat_counter .NE. 0)
     393             : 
     394          60 :          randmat_counter = randmat_counter + 1
     395             :       END IF
     396             : 
     397        1616 :       ALLOCATE (ind_nd(nblks_alloc, ndims_tensor(tensor)))
     398             : 
     399             : !$OMP PARALLEL DEFAULT(NONE) SHARED(ind_nd,nblks_alloc,tensor,mynode,enumerate,randmat_counter) &
     400             : !$OMP SHARED(${varlist("blk_ind")}$) &
     401             : !$OMP PRIVATE(my_nblks_alloc,ib,proc,i,iterator,blk_offset,blk_size,blk_index) &
     402             : !$OMP PRIVATE(blk_index_2d,nblks_2d,iseed,nze,tensor_dims) &
     403             : !$OMP PRIVATE(${varlist("blk_values", nmin=2)}$) &
     404         404 : !$OMP PRIVATE(${varlist("my_blk_ind")}$)
     405             : 
     406             :       my_nblks_alloc = 0
     407             : !$OMP DO
     408             :       DO ib = 1, nblks_alloc
     409             :          #:for ndim in ndims
     410             :             IF (ndims_tensor(tensor) == ${ndim}$) THEN
     411             :                ind_nd(ib, :) = [${varlist("blk_ind", nmax=ndim, suffix="(ib)")}$]
     412             :             END IF
     413             :          #:endfor
     414             :          CALL dbt_get_stored_coordinates(tensor, ind_nd(ib, :), proc)
     415             :          IF (proc == mynode) THEN
     416             :             my_nblks_alloc = my_nblks_alloc + 1
     417             :          END IF
     418             :       END DO
     419             : !$OMP END DO
     420             : 
     421             :       #:for dim in range(1, maxdim+1)
     422             :          IF (ndims_tensor(tensor) >= ${dim}$) THEN
     423             :             ALLOCATE (my_blk_ind_${dim}$ (my_nblks_alloc))
     424             :          END IF
     425             :       #:endfor
     426             : 
     427             :       i = 0
     428             : !$OMP DO
     429             :       DO ib = 1, nblks_alloc
     430             :          CALL dbt_get_stored_coordinates(tensor, ind_nd(ib, :), proc)
     431             :          IF (proc == mynode) THEN
     432             :             i = i + 1
     433             :             #:for dim in range(1, maxdim+1)
     434             :                IF (ndims_tensor(tensor) >= ${dim}$) THEN
     435             :                   my_blk_ind_${dim}$ (i) = blk_ind_${dim}$ (ib)
     436             :                END IF
     437             :             #:endfor
     438             :          END IF
     439             :       END DO
     440             : !$OMP END DO
     441             : 
     442             :       #:for ndim in ndims
     443             :          IF (ndims_tensor(tensor) == ${ndim}$) THEN
     444             :             CALL dbt_reserve_blocks(tensor, ${varlist("my_blk_ind", nmax=ndim)}$)
     445             :          END IF
     446             :       #:endfor
     447             : 
     448             :       CALL dbt_iterator_start(iterator, tensor)
     449             :       DO WHILE (dbt_iterator_blocks_left(iterator))
     450             :          CALL dbt_iterator_next_block(iterator, blk_index, blk_size=blk_size, blk_offset=blk_offset)
     451             : 
     452             :          IF (.NOT. enumerate) THEN
     453             :             blk_index_2d = INT(get_2d_indices_tensor(tensor%nd_index_blk, blk_index))
     454             :             CALL dbt_get_mapping_info(tensor%nd_index_blk, dims_2d=nblks_2d)
     455             :             iseed = generate_larnv_seed(blk_index_2d(1), nblks_2d(1), blk_index_2d(2), nblks_2d(2), randmat_counter)
     456             :             nze = PRODUCT(blk_size)
     457             :          END IF
     458             : 
     459             :          #:for ndim in ndims
     460             :             IF (ndims_tensor(tensor) == ${ndim}$) THEN
     461             :                CALL allocate_any(blk_values_${ndim}$, shape_spec=blk_size)
     462             :                CALL dims_tensor(tensor, tensor_dims)
     463             :                IF (enumerate) THEN
     464             :                   CALL enumerate_block_elements(blk_size, blk_offset, tensor_dims, blk_${ndim}$=blk_values_${ndim}$)
     465             :                ELSE
     466             :                   CALL dlarnv(1, iseed, nze, blk_values_${ndim}$)
     467             :                END IF
     468             :                CALL dbt_put_block(tensor, blk_index, blk_size, blk_values_${ndim}$)
     469             :                DEALLOCATE (blk_values_${ndim}$)
     470             :             END IF
     471             :          #:endfor
     472             :       END DO
     473             :       CALL dbt_iterator_stop(iterator)
     474             : !$OMP END PARALLEL
     475             : 
     476         808 :    END SUBROUTINE
     477             : 
     478             : ! **************************************************************************************************
     479             : !> \brief Enumerate tensor entries in block
     480             : !> \param blk_2 block values for 2 dimensions
     481             : !> \param blk_3 block values for 3 dimensions
     482             : !> \param blk_size size of block
     483             : !> \param blk_offset block offset (indices of first element)
     484             : !> \param tensor_size global tensor sizes
     485             : !> \author Patrick Seewald
     486             : ! **************************************************************************************************
     487        2928 :    SUBROUTINE enumerate_block_elements(blk_size, blk_offset, tensor_size, ${varlist("blk", nmin=2)}$)
     488             :       INTEGER, DIMENSION(:), INTENT(IN)                  :: blk_size, blk_offset, tensor_size
     489             :       #:for ndim in ndims
     490             :          REAL(KIND=dp), DIMENSION(${shape_colon(ndim)}$), &
     491             :             OPTIONAL, INTENT(OUT)                           :: blk_${ndim}$
     492             :       #:endfor
     493             :       INTEGER                                            :: ndim
     494        5856 :       INTEGER, DIMENSION(SIZE(blk_size))                 :: arr_ind, tens_ind
     495             :       INTEGER                                            :: ${varlist("i")}$
     496             : 
     497        2928 :       ndim = SIZE(tensor_size)
     498             : 
     499             :       #:for ndim in ndims
     500        3120 :          IF (ndim == ${ndim}$) THEN
     501             :             #:for idim in range(ndim,0,-1)
     502     4323620 :                DO i_${idim}$ = 1, blk_size(${idim}$)
     503             :                   #:endfor
     504    18053064 :                   arr_ind(:) = [${varlist("i", nmax=ndim)}$]
     505    18053064 :                   tens_ind(:) = arr_ind(:) + blk_offset(:) - 1
     506     4199108 :                   blk_${ndim}$ (${arrlist("arr_ind", nmax=ndim)}$) = combine_tensor_index(tens_ind, tensor_size)
     507             :                   #:for idim in range(ndim,0,-1)
     508             :                      END DO
     509             :                   #:endfor
     510             :                END IF
     511             :             #:endfor
     512             : 
     513        2928 :          END SUBROUTINE
     514             : 
     515             :          #:for ndim in ndims
     516             : ! **************************************************************************************************
     517             : !> \brief Transform a distributed sparse tensor to a replicated dense array. This is only useful for
     518             : !>        testing tensor contraction by matrix multiplication of dense arrays.
     519             : !> \author Patrick Seewald
     520             : ! **************************************************************************************************
     521          80 :             SUBROUTINE dist_sparse_tensor_to_repl_dense_${ndim}$d_array(tensor, array)
     522             : 
     523             :                TYPE(dbt_type), INTENT(INOUT)                          :: tensor
     524             :                REAL(dp), ALLOCATABLE, DIMENSION(${shape_colon(ndim)}$), &
     525             :                   INTENT(OUT)                                             :: array
     526          80 :                REAL(dp), ALLOCATABLE, DIMENSION(${shape_colon(ndim)}$)   :: block
     527         160 :                INTEGER, DIMENSION(ndims_tensor(tensor))                  :: dims_nd, ind_nd, blk_size, blk_offset
     528             :                TYPE(dbt_iterator_type)                                     :: iterator
     529             :                INTEGER                                                    :: idim
     530          80 :                INTEGER, DIMENSION(ndims_tensor(tensor))                   :: blk_start, blk_end
     531             :                LOGICAL                                                    :: found
     532             : 
     533           0 :                CPASSERT(ndims_tensor(tensor) .EQ. ${ndim}$)
     534          80 :                CALL dbt_get_info(tensor, nfull_total=dims_nd)
     535          80 :                CALL allocate_any(array, shape_spec=dims_nd)
     536    33063268 :                array(${shape_colon(ndim)}$) = 0.0_dp
     537             : 
     538             : !$OMP PARALLEL DEFAULT(NONE) SHARED(tensor,array) &
     539          80 : !$OMP PRIVATE(iterator,ind_nd,blk_size,blk_offset,block,found,idim,blk_start,blk_end)
     540             :                CALL dbt_iterator_start(iterator, tensor)
     541             :                DO WHILE (dbt_iterator_blocks_left(iterator))
     542             :                   CALL dbt_iterator_next_block(iterator, ind_nd, blk_size=blk_size, blk_offset=blk_offset)
     543             :                   CALL dbt_get_block(tensor, ind_nd, block, found)
     544             :                   CPASSERT(found)
     545             : 
     546             :                   DO idim = 1, ndims_tensor(tensor)
     547             :                      blk_start(idim) = blk_offset(idim)
     548             :                      blk_end(idim) = blk_offset(idim) + blk_size(idim) - 1
     549             :                   END DO
     550             :                   array(${", ".join(["blk_start("+str(idim)+"):blk_end("+str(idim)+")" for idim in range(1, ndim + 1)])}$) = &
     551             :                      block(${shape_colon(ndim)}$)
     552             : 
     553             :                   DEALLOCATE (block)
     554             :                END DO
     555             :                CALL dbt_iterator_stop(iterator)
     556             : !$OMP END PARALLEL
     557          80 :                CALL tensor%pgrid%mp_comm_2d%sum(array)
     558             : 
     559         160 :             END SUBROUTINE
     560             :          #:endfor
     561             : 
     562             : ! **************************************************************************************************
     563             : !> \brief test tensor contraction
     564             : !> \note for testing/debugging, simply replace a call to dbt_contract with a call to this routine
     565             : !> \author Patrick Seewald
     566             : ! **************************************************************************************************
     567          20 :          SUBROUTINE dbt_contract_test(alpha, tensor_1, tensor_2, beta, tensor_3, &
     568          20 :                                       contract_1, notcontract_1, &
     569          20 :                                       contract_2, notcontract_2, &
     570          20 :                                       map_1, map_2, &
     571             :                                       unit_nr, &
     572           8 :                                       bounds_1, bounds_2, bounds_3, &
     573             :                                       log_verbose, write_int)
     574             : 
     575             :             REAL(dp), INTENT(IN) :: alpha
     576             :             TYPE(dbt_type), INTENT(INOUT)    :: tensor_1, tensor_2, tensor_3
     577             :             REAL(dp), INTENT(IN) :: beta
     578             :             INTEGER, DIMENSION(:), INTENT(IN)    :: contract_1, contract_2, &
     579             :                                                     notcontract_1, notcontract_2, &
     580             :                                                     map_1, map_2
     581             :             INTEGER, INTENT(IN)                  :: unit_nr
     582             :             INTEGER, DIMENSION(2, SIZE(contract_1)), &
     583             :                OPTIONAL                          :: bounds_1
     584             :             INTEGER, DIMENSION(2, SIZE(notcontract_1)), &
     585             :                OPTIONAL                          :: bounds_2
     586             :             INTEGER, DIMENSION(2, SIZE(notcontract_2)), &
     587             :                OPTIONAL                          :: bounds_3
     588             :             LOGICAL, INTENT(IN), OPTIONAL        :: log_verbose
     589             :             LOGICAL, INTENT(IN), OPTIONAL        :: write_int
     590             :             INTEGER                              :: io_unit, mynode
     591             :             TYPE(mp_comm_type) :: mp_comm
     592          20 :             INTEGER, DIMENSION(:), ALLOCATABLE   :: size_1, size_2, size_3, &
     593          20 :                                                     order_t1, order_t2, order_t3
     594          40 :             INTEGER, DIMENSION(2, ndims_tensor(tensor_1)) :: bounds_t1
     595          40 :             INTEGER, DIMENSION(2, ndims_tensor(tensor_2)) :: bounds_t2
     596             : 
     597             :             #:for ndim in ndims
     598             :                REAL(KIND=dp), ALLOCATABLE, &
     599          20 :                   DIMENSION(${shape_colon(ndim)}$) :: array_1_${ndim}$d, &
     600          20 :                                                       array_2_${ndim}$d, &
     601          20 :                                                       array_3_${ndim}$d, &
     602          20 :                                                       array_1_${ndim}$d_full, &
     603          20 :                                                       array_2_${ndim}$d_full, &
     604          20 :                                                       array_3_0_${ndim}$d, &
     605          20 :                                                       array_1_rs${ndim}$d, &
     606          20 :                                                       array_2_rs${ndim}$d, &
     607          20 :                                                       array_3_rs${ndim}$d, &
     608          20 :                                                       array_3_0_rs${ndim}$d
     609             :             #:endfor
     610             :             REAL(KIND=dp), ALLOCATABLE, &
     611          20 :                DIMENSION(:, :)                   :: array_1_mm, &
     612          20 :                                                     array_2_mm, &
     613          20 :                                                     array_3_mm, &
     614          20 :                                                     array_3_test_mm
     615             :             LOGICAL                             :: eql, notzero
     616             :             LOGICAL, PARAMETER                  :: debug = .FALSE.
     617             :             REAL(KIND=dp)                   :: cs_1, cs_2, cs_3, eql_diff
     618             :             LOGICAL                             :: do_crop_1, do_crop_2
     619             : 
     620          20 :             mp_comm = tensor_1%pgrid%mp_comm_2d
     621          20 :             mynode = mp_comm%mepos
     622          20 :             io_unit = -1
     623          20 :             IF (mynode .EQ. 0) io_unit = unit_nr
     624             : 
     625          20 :             cs_1 = dbt_checksum(tensor_1)
     626          20 :             cs_2 = dbt_checksum(tensor_2)
     627          20 :             cs_3 = dbt_checksum(tensor_3)
     628             : 
     629          20 :             IF (io_unit > 0) THEN
     630          10 :                WRITE (io_unit, *)
     631          10 :                WRITE (io_unit, '(A)') repeat("-", 80)
     632          10 :                WRITE (io_unit, '(A,1X,A,1X,A,1X,A,1X,A,1X,A)') "Testing tensor contraction", &
     633          20 :                   TRIM(tensor_1%name), "x", TRIM(tensor_2%name), "=", TRIM(tensor_3%name)
     634          10 :                WRITE (io_unit, '(A)') repeat("-", 80)
     635             :             END IF
     636             : 
     637             :             IF (debug) THEN
     638             :                IF (io_unit > 0) THEN
     639             :                   WRITE (io_unit, "(A, E9.2)") "checksum ", TRIM(tensor_1%name), cs_1
     640             :                   WRITE (io_unit, "(A, E9.2)") "checksum ", TRIM(tensor_2%name), cs_2
     641             :                   WRITE (io_unit, "(A, E9.2)") "checksum ", TRIM(tensor_3%name), cs_3
     642             :                END IF
     643             :             END IF
     644             : 
     645             :             IF (debug) THEN
     646             :                CALL dbt_write_block_indices(tensor_1, io_unit, unit_nr)
     647             :                CALL dbt_write_blocks(tensor_1, io_unit, unit_nr, write_int)
     648             :             END IF
     649             : 
     650             :             SELECT CASE (ndims_tensor(tensor_3))
     651             :                #:for ndim in ndims
     652             :                   CASE (${ndim}$)
     653          20 :                   CALL dist_sparse_tensor_to_repl_dense_array(tensor_3, array_3_0_${ndim}$d)
     654             :                #:endfor
     655             :             END SELECT
     656             : 
     657             :             CALL dbt_contract(alpha, tensor_1, tensor_2, beta, tensor_3, &
     658             :                               contract_1, notcontract_1, &
     659             :                               contract_2, notcontract_2, &
     660             :                               map_1, map_2, &
     661             :                               bounds_1=bounds_1, bounds_2=bounds_2, bounds_3=bounds_3, &
     662             :                               filter_eps=1.0E-12_dp, &
     663          20 :                               unit_nr=io_unit, log_verbose=log_verbose)
     664             : 
     665          20 :             cs_3 = dbt_checksum(tensor_3)
     666             : 
     667             :             IF (debug) THEN
     668             :                IF (io_unit > 0) THEN
     669             :                   WRITE (io_unit, "(A, E9.2)") "checksum ", TRIM(tensor_3%name), cs_3
     670             :                END IF
     671             :             END IF
     672             : 
     673          20 :             do_crop_1 = .FALSE.; do_crop_2 = .FALSE.!; do_crop_3 = .FALSE.
     674             : 
     675             :             ! crop tensor as first step
     676          82 :             bounds_t1(1, :) = 1
     677          82 :             CALL dbt_get_info(tensor_1, nfull_total=bounds_t1(2, :))
     678             : 
     679          80 :             bounds_t2(1, :) = 1
     680          80 :             CALL dbt_get_info(tensor_2, nfull_total=bounds_t2(2, :))
     681             : 
     682          20 :             IF (PRESENT(bounds_1)) THEN
     683          22 :                bounds_t1(:, contract_1) = bounds_1
     684          10 :                do_crop_1 = .TRUE.
     685          22 :                bounds_t2(:, contract_2) = bounds_1
     686          20 :                do_crop_2 = .TRUE.
     687             :             END IF
     688             : 
     689          20 :             IF (PRESENT(bounds_2)) THEN
     690          14 :                bounds_t1(:, notcontract_1) = bounds_2
     691          20 :                do_crop_1 = .TRUE.
     692             :             END IF
     693             : 
     694          20 :             IF (PRESENT(bounds_3)) THEN
     695          14 :                bounds_t2(:, notcontract_2) = bounds_3
     696          20 :                do_crop_2 = .TRUE.
     697             :             END IF
     698             : 
     699             :             ! Convert tensors to simple multidimensional arrays
     700             :             #:for i in range(1,4)
     701             :                SELECT CASE (ndims_tensor(tensor_${i}$))
     702             :                   #:for ndim in ndims
     703             :                      CASE (${ndim}$)
     704             :                      #:if i < 3
     705             :                         CALL dist_sparse_tensor_to_repl_dense_array(tensor_${i}$, array_${i}$_${ndim}$d_full)
     706         162 :                         CALL allocate_any(array_${i}$_${ndim}$d, shape_spec=SHAPE(array_${i}$_${ndim}$d_full))
     707    22512580 :                         array_${i}$_${ndim}$d = 0.0_dp
     708             :          array_${i}$_${ndim}$d(${", ".join(["bounds_t" + str(i) + "(1, " + str(idim) + "):bounds_t" + str(i) + "(2, " + str(idim) + ")" for idim in range(1, ndim+1)])}$) = &
     709    19082870 :          array_${i}$_${ndim}$d_full(${", ".join(["bounds_t" + str(i) + "(1, " + str(idim) + "):bounds_t" + str(i) + "(2, " + str(idim) + ")" for idim in range(1, ndim+1)])}$)
     710             :                      #:else
     711          20 :                         CALL dist_sparse_tensor_to_repl_dense_array(tensor_${i}$, array_${i}$_${ndim}$d)
     712             :                      #:endif
     713             : 
     714             :                   #:endfor
     715             :                END SELECT
     716             :             #:endfor
     717             : 
     718             :             ! Get array sizes
     719             : 
     720             :             #:for i in range(1,4)
     721             :                SELECT CASE (ndims_tensor(tensor_${i}$))
     722             :                   #:for ndim in ndims
     723             :                      CASE (${ndim}$)
     724         392 :                      ALLOCATE (size_${i}$, source=SHAPE(array_${i}$_${ndim}$d))
     725             : 
     726             :                   #:endfor
     727             :                END SELECT
     728             :             #:endfor
     729             : 
     730             :             #:for i in range(1,4)
     731         140 :                ALLOCATE (order_t${i}$ (ndims_tensor(tensor_${i}$)))
     732             :             #:endfor
     733             : 
     734             :             ASSOCIATE (map_t1_1 => notcontract_1, map_t1_2 => contract_1, &
     735             :                        map_t2_1 => notcontract_2, map_t2_2 => contract_2, &
     736             :                        map_t3_1 => map_1, map_t3_2 => map_2)
     737             : 
     738             :                #:for i in range(1,4)
     739         612 :                   order_t${i}$ (:) = dbt_inverse_order([map_t${i}$_1, map_t${i}$_2])
     740             : 
     741           6 :                   SELECT CASE (ndims_tensor(tensor_${i}$))
     742             :                      #:for ndim in ndims
     743             :                         CASE (${ndim}$)
     744           6 :                         CALL allocate_any(array_${i}$_rs${ndim}$d, source=array_${i}$_${ndim}$d, order=order_t${i}$)
     745          60 :                         CALL allocate_any(array_${i}$_mm, sizes_2d(size_${i}$, map_t${i}$_1, map_t${i}$_2))
     746         246 :                         array_${i}$_mm(:, :) = RESHAPE(array_${i}$_rs${ndim}$d, SHAPE(array_${i}$_mm))
     747             :                      #:endfor
     748             :                   END SELECT
     749             :                #:endfor
     750             : 
     751           0 :                SELECT CASE (ndims_tensor(tensor_3))
     752             :                   #:for ndim in ndims
     753             :                      CASE (${ndim}$)
     754           0 :                      CALL allocate_any(array_3_0_rs${ndim}$d, source=array_3_0_${ndim}$d, order=order_t3)
     755          20 :                      CALL allocate_any(array_3_test_mm, sizes_2d(size_3, map_t3_1, map_t3_2))
     756          80 :                      array_3_test_mm(:, :) = RESHAPE(array_3_0_rs${ndim}$d, SHAPE(array_3_mm))
     757             :                   #:endfor
     758             :                END SELECT
     759             : 
     760     5101228 :                array_3_test_mm(:, :) = beta*array_3_test_mm(:, :) + alpha*MATMUL(array_1_mm, transpose(array_2_mm))
     761             : 
     762             :             END ASSOCIATE
     763             : 
     764     5101208 :             eql_diff = MAXVAL(ABS(array_3_test_mm(:, :) - array_3_mm(:, :)))
     765     5101208 :             notzero = MAXVAL(ABS(array_3_test_mm(:, :))) .GT. 1.0E-12_dp
     766             : 
     767          20 :             eql = eql_diff .LT. 1.0E-11_dp
     768             : 
     769          20 :             IF (.NOT. eql .OR. .NOT. notzero) THEN
     770           0 :                IF (io_unit > 0) WRITE (io_unit, *) 'Test failed!', eql_diff
     771           0 :                CPABORT('')
     772             :             ELSE
     773          20 :                IF (io_unit > 0) WRITE (io_unit, *) 'Test passed!', eql_diff
     774             :             END IF
     775             : 
     776          48 :          END SUBROUTINE
     777             : 
     778             : ! **************************************************************************************************
     779             : !> \brief mapped sizes in 2d
     780             : !> \author Patrick Seewald
     781             : ! **************************************************************************************************
     782          80 :          FUNCTION sizes_2d(nd_sizes, map1, map2)
     783             :             INTEGER, DIMENSION(:), INTENT(IN) :: nd_sizes, map1, map2
     784             :             INTEGER, DIMENSION(2)             :: sizes_2d
     785         206 :             sizes_2d(1) = PRODUCT(nd_sizes(map1))
     786         200 :             sizes_2d(2) = PRODUCT(nd_sizes(map2))
     787             :          END FUNCTION
     788             : 
     789             : ! **************************************************************************************************
     790             : !> \brief checksum of a tensor consistent with block_checksum
     791             : !> \author Patrick Seewald
     792             : ! **************************************************************************************************
     793          80 :          FUNCTION dbt_checksum(tensor)
     794             :             TYPE(dbt_type), INTENT(IN) :: tensor
     795             :             REAL(KIND=dp) :: dbt_checksum
     796          80 :             dbt_checksum = dbt_tas_checksum(tensor%matrix_rep)
     797          80 :          END FUNCTION
     798             : 
     799             : ! **************************************************************************************************
     800             : !> \brief Reset the seed used for generating random matrices to default value
     801             : !> \author Patrick Seewald
     802             : ! **************************************************************************************************
     803           2 :          SUBROUTINE dbt_reset_randmat_seed()
     804           2 :             randmat_counter = rand_seed_init
     805           2 :          END SUBROUTINE
     806             : 
     807          20 :       END MODULE

Generated by: LCOV version 1.15