LCOV - code coverage report
Current view: top level - dynamics/se/dycore - bndry_mod.F90 (source / functions) Hit Total Coverage
Test: coverage.info Lines: 155 307 50.5 %
Date: 2024-12-17 17:57:11 Functions: 7 17 41.2 %

          Line data    Source code
       1             : module bndry_mod
       2             :   use shr_kind_mod,   only: r8=>shr_kind_r8, i8=>shr_kind_i8
       3             :   use parallel_mod,   only: HME_BNDRY_A2A, HME_BNDRY_A2AO
       4             :   use thread_mod,     only: omp_in_parallel, omp_get_thread_num
       5             :   use gbarrier_mod,   only: gbarrier
       6             :   use cam_abortutils, only: endrun
       7             :   use cam_logfile,     only: iulog
       8             : 
       9             : 
      10             :   implicit none
      11             :   private
      12             : 
      13             :   interface bndry_exchange
      14             :      module procedure bndry_exchange_threaded
      15             :      module procedure bndry_exchange_nonthreaded
      16             :      module procedure long_bndry_exchange_nonth
      17             :   end interface
      18             :   public :: bndry_exchange
      19             : 
      20             :   interface ghost_exchange
      21             :      module procedure ghost_exchange_threaded
      22             :      module procedure ghost_exchange_nonthreaded
      23             :   end interface
      24             :   public :: ghost_exchange
      25             : 
      26             :   interface bndry_exchange_start
      27             :      module procedure bndry_exchange_threaded_start
      28             :      module procedure bndry_exchange_nonthreaded_start
      29             :   end interface
      30             :   public :: bndry_exchange_start
      31             : 
      32             :   interface bndry_exchange_finish
      33             :      module procedure bndry_exchange_threaded_finish
      34             :      module procedure bndry_exchange_nonthreaded_finish
      35             :   end interface
      36             :   public :: bndry_exchange_finish
      37             : 
      38             : 
      39             :   public :: compute_ghost_corner_orientation
      40             :   public :: ghost_exchangeVfull
      41             :   public :: copyBuffer
      42             : 
      43             : contains
      44             : 
      45           0 :   subroutine bndry_exchange_a2a(par,nthreads,ithr,buffer,location)
      46             :     use edgetype_mod,  only: Edgebuffer_t
      47             :     use schedtype_mod, only: schedule_t, cycle_t, schedule
      48             :     use thread_mod,    only: omp_in_parallel, omp_get_thread_num
      49             :     use perf_mod,      only: t_startf, t_stopf
      50             :     use spmd_utils,    only: mpi_real8, mpi_success
      51             :     use parallel_mod,  only: parallel_t
      52             :     use perf_mod,      only: t_startf, t_stopf
      53             : 
      54             :     type (parallel_t)            :: par
      55             :     integer, intent(in)          :: nthreads
      56             :     integer                      :: ithr ! The OpenMP thread ID
      57             :     type (EdgeBuffer_t)          :: buffer
      58             :     character(len=*),  optional  :: location
      59             : 
      60             :     type (Schedule_t), pointer   :: pSchedule
      61             :     type (Cycle_t),    pointer   :: pCycle
      62             :     integer                      :: icycle,ierr
      63             :     integer                      :: length
      64             :     integer                      :: iptr,source,nlyr
      65             :     integer                      :: nSendCycles,nRecvCycles
      66             :     integer                      :: errorcode,errorlen
      67             :     character*(80)               :: errorstring
      68             :     character(len=*),  parameter :: subname = 'bndry_exchange_a2a'
      69             :     character(len=80)            :: locstring
      70             :     logical                      :: ompthreadMissmatch
      71             : 
      72             :     integer                      :: i,j
      73             :     integer                      :: request
      74             : 
      75             : ! Neighborhood collectives are only in MPI3 and up
      76             : #ifdef SPMD
      77             : #if MPI_VERSION >= 3
      78             : 
      79             :    if(ithr == 0) then
      80             : 
      81             :       call MPI_Ineighbor_Alltoallv(buffer%buf,buffer%scountsFull,buffer%sdisplsFull,Mpi_real8, &
      82             :                      buffer%receive,buffer%rcountsFull,buffer%rdisplsFull,Mpi_real8,par%commGraphFull,request,ierr)
      83             :       if(ierr .ne. MPI_SUCCESS) then
      84             :         errorcode=ierr
      85             :         call MPI_Error_String(errorcode,errorstring,errorlen,ierr)
      86             :         write(iulog,*) subname,': Error after call to MPI_Ineighbor_alltoallv: ',errorstring
      87             :       endif
      88             : 
      89             :       if(present(location)) then
      90             :         locstring = TRIM(subname) // ': ' // TRIM(location)
      91             :       else
      92             :         locstring = TRIM(subname)
      93             :       endif
      94             :       ! location 1 for copyBuffer
      95             :       call copyBuffer(nthreads,ithr,buffer,locstring)
      96             : 
      97             :       call MPI_wait(request,lstatus,ierr)
      98             :       call t_stopf('bndry_a2a')
      99             :    else
     100             : 
     101             :       if(present(location)) then
     102             :         locstring = TRIM(subname) // ': ' // TRIM(location)
     103             :       else
     104             :         locstring = TRIM(subname)
     105             :       endif
     106             :       call copyBuffer(nthreads,ithr,buffer,locstring)
     107             : 
     108             :    endif
     109             : #else
     110           0 :     call endrun('bndry_exchange_a2a requires MPI-3 feature support')
     111             : #endif
     112             : #endif
     113             : 
     114           0 :   end subroutine bndry_exchange_a2a
     115             : 
     116    37704960 :   subroutine copyBuffer(nthreads,ithr,buffer,location)
     117           0 :     use edgetype_mod, only : Edgebuffer_t
     118             :     integer :: nthreads
     119             :     integer :: ithr
     120             :     type (EdgeBuffer_t)          :: buffer
     121             :     character(len=80)            :: location
     122             :     logical ::  ompThreadMissmatch
     123             :     integer lenMovePtr, iptr,length,i,j
     124             : 
     125    37704960 :     ompThreadMissmatch = .false.
     126    37704960 :     lenMovePtr = size(buffer%moveptr)
     127    37704960 :     if ( lenMOveptr .ne. nthreads) then
     128           0 :       ompthreadMissmatch = .true.
     129           0 :       write(*,30) TRIM(location), lenMoveptr, nthreads
     130             :     endif
     131             : 
     132             :     if (.not. ompthreadMissmatch) then
     133    37704960 :       iptr   = buffer%moveptr(ithr+1)
     134    37704960 :       length = buffer%moveLength(ithr+1)
     135    37704960 :       if(length>0) then
     136 >10527*10^8 :         do i=0,length-1
     137 >10527*10^8 :            buffer%receive(iptr+i) = buffer%buf(iptr+i)
     138             :         enddo
     139             :       endif
     140           0 :     else if(ompthreadMissmatch .and. ithr == 0) then
     141           0 :        do j=1,lenMovePtr
     142           0 :           iptr   = buffer%moveptr(j)
     143           0 :           length = buffer%moveLength(j)
     144           0 :           if(length>0) then
     145           0 :              do i=0,length-1
     146           0 :                 buffer%receive(iptr+i) = buffer%buf(iptr+i)
     147             :              enddo
     148             :           endif
     149             :        enddo
     150             :     endif
     151             : 30  format(a,'Potential perf issue: ',a,'LenMoveptr,nthreads: ',2(i3))
     152    37704960 :   end subroutine copyBuffer
     153             : 
     154           0 :   subroutine bndry_exchange_a2ao(par,nthreads,ithr,buffer,location)
     155             :     use edgetype_mod, only : Edgebuffer_t
     156             :     use schedtype_mod, only : schedule_t, cycle_t, schedule
     157             :     use thread_mod, only : omp_in_parallel, omp_get_thread_num
     158             :     use perf_mod, only : t_startf, t_stopf
     159             :     use spmd_utils,   only: mpi_real8, mpi_success, mpi_status_size
     160             :     use parallel_mod, only: parallel_t
     161             :     use perf_mod, only : t_startf, t_stopf
     162             : 
     163             :     type (parallel_t)                 :: par
     164             :     integer, intent(in)               :: nthreads
     165             :     integer                           :: ithr  ! The OpenMP thread ID
     166             :     type (EdgeBuffer_t)               :: buffer
     167             :     character(len=*), optional        :: location
     168             : 
     169             :     integer                           :: ierr
     170             :     integer                           :: errorcode,errorlen
     171             :     character(len=80)                 :: errorstring
     172             :     character(len=*),       parameter :: subname = 'bndry_exchange_a2ao'
     173             :     character(len=80)                 :: locstring
     174             : 
     175             :     integer :: requestIntra,requestInter
     176             :     integer :: lstatus(MPI_status_size)
     177             : 
     178             : ! Neighborhood collectives are only in MPI3 and up
     179             : #ifdef SPMD
     180             : #if MPI_VERSION >= 3
     181             : 
     182             :    if(ithr == 0) then
     183             : 
     184             :       call t_startf('bndry_a2ao')
     185             :       ! Start Inter-node communication
     186             :       call MPI_Ineighbor_Alltoallv(buffer%buf,buffer%scountsInter,buffer%sdisplsInter,MPI_real8, &
     187             :            buffer%receive,buffer%rcountsInter,buffer%rdisplsInter,MPI_real8,par%commGraphInter,requestInter,ierr)
     188             :       if(ierr .ne. MPI_SUCCESS) then
     189             :         errorcode=ierr
     190             :         call MPI_Error_String(errorcode,errorstring,errorlen,ierr)
     191             :         write(iulog,*) subname,': Error after call to MPI_Ineighbor_alltoallv: ',errorstring
     192             :       endif
     193             :       ! Start Intra-node communication
     194             :       call MPI_Ineighbor_Alltoallv(buffer%buf,buffer%scountsIntra,buffer%sdisplsIntra,MPI_real8, &
     195             :            buffer%receive,buffer%rcountsIntra,buffer%rdisplsIntra,MPI_real8,par%commGraphIntra,requestIntra,ierr)
     196             :       if(ierr .ne. MPI_SUCCESS) then
     197             :         errorcode=ierr
     198             :         call MPI_Error_String(errorcode,errorstring,errorlen,ierr)
     199             :         write(iulog,*) subname,': Error after call to MPI_Ineighbor_alltoallv: ',errorstring
     200             :       endif
     201             : 
     202             :       if(present(location)) then
     203             :         locstring = TRIM(subname) // ': ' // TRIM(location)
     204             :       else
     205             :         locstring = TRIM(subname)
     206             :       endif
     207             :       ! Finish the Intra-node communication
     208             :       call MPI_wait(requestIntra,lstatus,ierr)
     209             : 
     210             :       ! location 3 for copyBuffer
     211             :       call copyBuffer(nthreads,ithr,buffer,locstring)
     212             : 
     213             :       ! Finish the Inter-node communication
     214             :       call MPI_wait(requestInter,lstatus,ierr)
     215             :       call t_stopf('bndry_a2ao')
     216             : 
     217             :    else
     218             : 
     219             :       if(present(location)) then
     220             :         locstring = TRIM(subname) // ': ' // TRIM(location)
     221             :       else
     222             :         locstring = TRIM(subname)
     223             :       endif
     224             :       !Copy buffer for ithr!=0
     225             :       call copyBuffer(nthreads,ithr,buffer,locstring)
     226             : 
     227             :    endif
     228             : #else
     229           0 :     call endrun('bndry_exchange_a2ao requires MPI-3 feature support')
     230             : #endif
     231             : #endif
     232             : 
     233           0 :   end subroutine bndry_exchange_a2ao
     234             : 
     235    37704960 :   subroutine bndry_exchange_p2p(par,nthreads,ithr,buffer,location)
     236           0 :     use edgetype_mod,  only: Edgebuffer_t
     237             :     use schedtype_mod, only: schedule_t, cycle_t, schedule
     238             :     use thread_mod,    only: omp_in_parallel, omp_get_thread_num
     239             :     use spmd_utils,    only: mpi_real8, mpi_success
     240             :     use parallel_mod,  only: parallel_t
     241             :     use perf_mod,      only: t_startf, t_stopf
     242             : 
     243             :     type (parallel_t)                 :: par
     244             :     integer, intent(in)               :: nthreads
     245             :     integer                           :: ithr
     246             :     type (EdgeBuffer_t)               :: buffer
     247             :     character(len=*), optional        :: location
     248             : 
     249             :     type (Schedule_t),pointer         :: pSchedule
     250             :     type (Cycle_t),pointer            :: pCycle
     251             :     integer                           :: dest,length,tag
     252             :     integer                           :: icycle,ierr
     253             :     integer                           :: iptr,source,nlyr
     254             :     integer                           :: nSendCycles,nRecvCycles
     255             :     integer                           :: errorcode,errorlen
     256             :     character*(80)                    :: errorstring
     257             :     character(len=*), parameter       :: subname = 'bndry_exchange_p2p'
     258             :     character(len=80)                 :: locstring
     259             :     logical, parameter :: Debug=.FALSE.
     260             : 
     261             :     integer                           :: i,j
     262             :     logical :: ompthreadMissmatch
     263             :     integer :: lenMovePtr
     264             : 
     265    37704960 :     pSchedule => Schedule(1)
     266    37704960 :     nlyr = buffer%nlyr
     267    37704960 :     ompthreadMissmatch = .FALSE.
     268             : 
     269    37704960 :     lenMovePtr = size(buffer%moveptr)
     270             : 
     271    37704960 :   if(ithr == 0) then
     272    37704960 :     nSendCycles = pSchedule%nSendCycles
     273    37704960 :     nRecvCycles = pSchedule%nRecvCycles
     274             : 
     275             : 
     276             :     !==================================================
     277             :     !  Fire off the sends
     278             :     !==================================================
     279             : 
     280   280528830 :     do icycle=1,nSendCycles
     281   242823870 :        pCycle      => pSchedule%SendCycle(icycle)
     282   242823870 :        dest            = pCycle%dest - 1
     283   242823870 :        length          = buffer%scountsFull(icycle)
     284   242823870 :        tag             = buffer%tag
     285   242823870 :        iptr            = buffer%sdisplsFull(icycle) + 1
     286             :        if(Debug) write(iulog,*) subname,': MPI_Isend: DEST:',dest,'LENGTH:',length,'TAG: ',tag
     287   242823870 :        call MPI_Isend(buffer%buf(iptr),length,Mpi_real8,dest,tag,par%comm,buffer%Srequest(icycle),ierr)
     288   280528830 :        if(ierr .ne. MPI_SUCCESS) then
     289           0 :           errorcode=ierr
     290           0 :           call MPI_Error_String(errorcode,errorstring,errorlen,ierr)
     291           0 :           write(iulog,*) subname,': Error after call to MPI_Isend: ',errorstring
     292             :        endif
     293             :     end do    ! icycle
     294             : 
     295             :     !==================================================
     296             :     !  Post the Receives
     297             :     !==================================================
     298   280528830 :     do icycle=1,nRecvCycles
     299   242823870 :        pCycle         => pSchedule%RecvCycle(icycle)
     300   242823870 :        source          = pCycle%source - 1
     301   242823870 :        length          = buffer%rcountsFull(icycle)
     302   242823870 :        tag             = buffer%tag
     303   242823870 :        iptr            = buffer%rdisplsFull(icycle) + 1
     304             :        if(Debug) write(iulog,*) subname,': MPI_Irecv: SRC:',source,'LENGTH:',length,'TAG: ',tag
     305           0 :        call MPI_Irecv(buffer%receive(iptr),length,Mpi_real8, &
     306   242823870 :             source,tag,par%comm,buffer%Rrequest(icycle),ierr)
     307   280528830 :        if(ierr .ne. MPI_SUCCESS) then
     308           0 :           errorcode=ierr
     309           0 :           call MPI_Error_String(errorcode,errorstring,errorlen,ierr)
     310           0 :           write(iulog,*) subname,': Error after call to MPI_Irecv: ',errorstring
     311             :        endif
     312             :     end do    ! icycle
     313    37704960 :     if(present(location)) then
     314    37704960 :       locstring = TRIM(subname) // ': ' // TRIM(location)
     315             :     else
     316           0 :       locstring = TRIM(subname)
     317             :     endif
     318    37704960 :     call copyBuffer(nthreads,ithr,buffer,locstring)
     319    37704960 :     if (nSendCycles>0) call MPI_Waitall(nSendCycles,buffer%Srequest,buffer%status,ierr)
     320    37704960 :     if (nRecvCycles>0) call MPI_Waitall(nRecvCycles,buffer%Rrequest,buffer%status,ierr)
     321             :   else
     322           0 :     if(present(location)) then
     323           0 :       locstring = TRIM(subname) // ': ' // TRIM(location)
     324             :     else
     325           0 :       locstring = TRIM(subname)
     326             :     endif
     327           0 :     call copyBuffer(nthreads,ithr,buffer,locstring)
     328             :   endif
     329             : 
     330    37704960 :   end subroutine bndry_exchange_p2p
     331             : 
     332           0 :   subroutine bndry_exchange_p2p_start(par,nthreads,ithr,buffer,location)
     333             : 
     334    37704960 :     use edgetype_mod,  only: Edgebuffer_t
     335             :     use schedtype_mod, only: schedule_t, cycle_t, schedule
     336             :     use thread_mod,    only: omp_in_parallel, omp_get_thread_num
     337             :     use spmd_utils,    only: mpi_real8, mpi_success
     338             :     use parallel_mod,  only: parallel_t
     339             : 
     340             :     type (parallel_t)                 :: par
     341             :     integer, intent(in)               :: nthreads
     342             :     integer                           :: ithr
     343             :     type (EdgeBuffer_t)               :: buffer
     344             :     character (len=*), optional :: location
     345             : 
     346             :     type (Schedule_t),pointer         :: pSchedule
     347             :     type (Cycle_t),pointer            :: pCycle
     348             :     integer                           :: dest,length,tag
     349             :     integer                           :: icycle,ierr
     350             :     integer                           :: iptr,source,nlyr
     351             :     integer                           :: nSendCycles,nRecvCycles
     352             :     integer                           :: errorcode,errorlen
     353             :     character*(80)                    :: errorstring
     354             :     character(len=*),       parameter :: subname = 'bndry_exchange_p2p_start'
     355             :     logical,                parameter :: Debug=.FALSE.
     356             : 
     357             :     integer                           :: i,j, lenMovePtr
     358             :     logical :: ompthreadMissmatch
     359             : 
     360           0 :     pSchedule => Schedule(1)
     361           0 :     nlyr = buffer%nlyr
     362           0 :     ompthreadMissmatch = .FALSE.
     363             : 
     364           0 :     lenMovePtr = size(buffer%moveptr)
     365             : 
     366           0 :   if(ithr == 0) then
     367           0 :     nSendCycles = pSchedule%nSendCycles
     368           0 :     nRecvCycles = pSchedule%nRecvCycles
     369             : 
     370             :     !==================================================
     371             :     !  Fire off the sends
     372             :     !==================================================
     373             : 
     374           0 :     do icycle=1,nSendCycles
     375           0 :        pCycle      => pSchedule%SendCycle(icycle)
     376           0 :        dest            = pCycle%dest - 1
     377           0 :        length          = buffer%scountsFull(icycle)
     378           0 :        tag             = buffer%tag
     379           0 :        iptr            = buffer%sdisplsFull(icycle) + 1
     380             :        if(Debug) write(iulog,*) subname,': MPI_Isend: DEST:',dest,'LENGTH:',length,'TAG: ',tag
     381           0 :        call MPI_Isend(buffer%buf(iptr),length,Mpi_real8,dest,tag,par%comm,buffer%Srequest(icycle),ierr)
     382           0 :        if(ierr .ne. MPI_SUCCESS) then
     383           0 :           errorcode=ierr
     384           0 :           call MPI_Error_String(errorcode,errorstring,errorlen,ierr)
     385           0 :           write(iulog,*) subname,': Error after call to MPI_Isend: ',errorstring
     386             :        endif
     387             :     end do    ! icycle
     388             : 
     389             :     !==================================================
     390             :     !  Post the Receives
     391             :     !==================================================
     392           0 :     do icycle=1,nRecvCycles
     393           0 :        pCycle         => pSchedule%RecvCycle(icycle)
     394           0 :        source          = pCycle%source - 1
     395           0 :        length          = buffer%rcountsFull(icycle)
     396           0 :        tag             = buffer%tag
     397           0 :        iptr            = buffer%rdisplsFull(icycle) + 1
     398             :        if(Debug) write(iulog,*) subname,': MPI_Irecv: SRC:',source,'LENGTH:',length,'TAG: ',tag
     399           0 :        call MPI_Irecv(buffer%receive(iptr),length,Mpi_real8, &
     400           0 :             source,tag,par%comm,buffer%Rrequest(icycle),ierr)
     401           0 :        if(ierr .ne. MPI_SUCCESS) then
     402           0 :           errorcode=ierr
     403           0 :           call MPI_Error_String(errorcode,errorstring,errorlen,ierr)
     404           0 :           write(iulog,*) subname,': Error after call to MPI_Irecv: ',errorstring
     405             :        endif
     406             :     end do    ! icycle
     407             :   endif
     408             : 
     409           0 :   end subroutine bndry_exchange_p2p_start
     410             : 
     411           0 :   subroutine bndry_exchange_p2p_finish(par,nthreads,ithr,buffer,location)
     412             :     use edgetype_mod,  only: Edgebuffer_t
     413             :     use schedtype_mod, only: schedule_t, cycle_t, schedule
     414             :     use thread_mod,    only: omp_in_parallel, omp_get_thread_num
     415             :     use parallel_mod,  only: parallel_t
     416             :     use perf_mod,      only: t_startf, t_stopf
     417             : 
     418             : 
     419             :     type (parallel_t)            :: par
     420             :     integer, intent(in)          :: nthreads
     421             :     integer                      :: ithr
     422             :     type (EdgeBuffer_t)          :: buffer
     423             :     character(len=*),  optional  :: location
     424             : 
     425             :     type (Schedule_t), pointer   :: pSchedule
     426             :     type (Cycle_t),    pointer   :: pCycle
     427             :     integer                      :: dest,length,tag
     428             :     integer                      :: icycle,ierr
     429             :     integer                      :: iptr,source,nlyr
     430             :     integer                      :: nSendCycles,nRecvCycles
     431             :     integer                      :: errorcode,errorlen
     432             :     character*(80)               :: errorstring
     433             :     character(len=*),  parameter :: subname = 'bndry_exchange_p2p_finish'
     434             :     character(len=80)            :: locstring
     435             : 
     436             :     integer                      :: i,j
     437             :     logical                      :: ompthreadMissmatch
     438             :     integer                      :: lenMovePtr
     439             : 
     440             : 
     441           0 :   pSchedule => Schedule(1)
     442           0 :   if(present(location)) then
     443           0 :     locstring = TRIM(subname) // ': ' // TRIM(location)
     444             :   else
     445           0 :     locstring = TRIM(subname)
     446             :   endif
     447           0 :   call copyBuffer(nthreads,ithr,buffer,locstring)
     448             : 
     449           0 :   if(ithr == 0) then
     450             : 
     451           0 :     nSendCycles = pSchedule%nSendCycles
     452           0 :     nRecvCycles = pSchedule%nRecvCycles
     453             : 
     454           0 :     if (nSendCycles>0) call MPI_Waitall(nSendCycles,buffer%Srequest,buffer%status,ierr)
     455           0 :     if (nRecvCycles>0) call MPI_Waitall(nRecvCycles,buffer%Rrequest,buffer%status,ierr)
     456             : 
     457             :   endif
     458             : 
     459           0 :   end subroutine bndry_exchange_p2p_finish
     460             : 
     461        1536 :   subroutine long_bndry_exchange_nonth(par,buffer)
     462           0 :     use edgetype_mod,  only: LongEdgebuffer_t
     463             :     use schedtype_mod, only: schedule_t, cycle_t, schedule
     464             :     use thread_mod,    only: omp_in_parallel
     465             :     use parallel_mod,  only: parallel_t, status, srequest, rrequest
     466             :     use spmd_utils,    only: mpi_integer, mpi_success
     467             : 
     468             :     type (parallel_t)            :: par
     469             :     type (LongEdgeBuffer_t)      :: buffer
     470             : 
     471             :     type (Schedule_t), pointer   :: pSchedule
     472             :     type (Cycle_t),    pointer   :: pCycle
     473             :     integer                      :: dest,length,tag
     474             :     integer                      :: icycle,ierr
     475             :     integer                      :: iptr,source,nlyr
     476             :     integer                      :: nSendCycles,nRecvCycles
     477             :     integer                      :: errorcode,errorlen
     478             :     character*(80)               :: errorstring
     479             :     character(len=*),  parameter :: subname = 'long_bndry_exchange_nonth'
     480             : 
     481             :     integer                      :: i
     482             : 
     483             : #ifdef SPMD
     484        1536 :     if(omp_in_parallel()) then
     485           0 :        print *,subname,': Warning you are calling a non-thread safe'
     486           0 :        print *,'         routine inside a threaded region....     '
     487           0 :        print *,'                Results are not predictable!!            '
     488             :     endif
     489             : 
     490             : 
     491             :     ! Setup the pointer to proper Schedule
     492        1536 :     pSchedule => Schedule(1)
     493        1536 :     nlyr = buffer%nlyr
     494             : 
     495        1536 :     nSendCycles = pSchedule%nSendCycles
     496        1536 :     nRecvCycles = pSchedule%nRecvCycles
     497             : 
     498             : 
     499             :     !==================================================
     500             :     !  Fire off the sends
     501             :     !==================================================
     502             : 
     503       11428 :     do icycle=1,nSendCycles
     504        9892 :        pCycle      => pSchedule%SendCycle(icycle)
     505        9892 :        dest            = pCycle%dest - 1
     506        9892 :        length      = nlyr * pCycle%lengthP
     507        9892 :        tag             = pCycle%tag
     508        9892 :        iptr            = pCycle%ptrP
     509             : 
     510        9892 :        call MPI_Isend(buffer%buf(1,iptr),length,Mpi_integer,dest,tag,par%comm,Srequest(icycle),ierr)
     511       11428 :        if(ierr .ne. MPI_SUCCESS) then
     512           0 :           errorcode=ierr
     513           0 :           call MPI_Error_String(errorcode,errorstring,errorlen,ierr)
     514           0 :           write(iulog,*) subname,': Error after call to MPI_Isend: ',errorstring
     515             :        endif
     516             :     end do    ! icycle
     517             : 
     518             :     !==================================================
     519             :     !  Post the Receives
     520             :     !==================================================
     521       11428 :     do icycle=1,nRecvCycles
     522        9892 :        pCycle         => pSchedule%RecvCycle(icycle)
     523        9892 :        source          = pCycle%source - 1
     524        9892 :        length      = nlyr * pCycle%lengthP
     525        9892 :        tag             = pCycle%tag
     526        9892 :        iptr            = pCycle%ptrP
     527             : 
     528           0 :        call MPI_Irecv(buffer%receive(1,iptr),length,Mpi_integer, &
     529        9892 :             source,tag,par%comm,Rrequest(icycle),ierr)
     530       11428 :        if(ierr .ne. MPI_SUCCESS) then
     531           0 :           errorcode=ierr
     532           0 :           call MPI_Error_String(errorcode,errorstring,errorlen,ierr)
     533           0 :           write(iulog,*) subname,': Error after call to MPI_Irecv: ',errorstring
     534             :        endif
     535             :     end do    ! icycle
     536             : 
     537             : 
     538             :     !==================================================
     539             :     !  Wait for all the receives to complete
     540             :     !==================================================
     541             : 
     542        1536 :     if (nSendCycles>0) call MPI_Waitall(nSendCycles,Srequest,status,ierr)
     543        1536 :     if (nRecvCycles>0) call MPI_Waitall(nRecvCycles,Rrequest,status,ierr)
     544       11428 :     do icycle=1,nRecvCycles
     545        9892 :        pCycle         => pSchedule%RecvCycle(icycle)
     546        9892 :        length             = pCycle%lengthP
     547        9892 :        iptr            = pCycle%ptrP
     548      117504 :        do i=0,length-1
     549      222044 :           buffer%buf(1:nlyr,iptr+i) = buffer%receive(1:nlyr,iptr+i)
     550             :        enddo
     551             :     end do   ! icycle
     552             : 
     553             : #endif
     554             : 
     555        1536 :   end subroutine long_bndry_exchange_nonth
     556             :   !********************************************************************************
     557             :   !
     558             :   !********************************************************************************
     559             : 
     560             : 
     561     3329280 :  subroutine ghost_exchange_threaded(hybrid,buffer,location)
     562             :     use hybrid_mod,   only : hybrid_t
     563             :     use edgetype_mod, only : Edgebuffer_t
     564             : 
     565             :     implicit none
     566             : 
     567             :     type (hybrid_t)             :: hybrid
     568             :     type (EdgeBuffer_t)         :: buffer
     569             :     character(len=*), optional :: location 
     570             : 
     571     3329280 :     call bndry_exchange_threaded(hybrid,buffer,location)
     572     3329280 :  end subroutine ghost_exchange_threaded
     573             : 
     574    37327104 :  subroutine bndry_exchange_threaded(hybrid,buffer,location)
     575             :     use hybrid_mod, only : hybrid_t
     576             :     use edgetype_mod, only : Edgebuffer_t
     577             :     use perf_mod, only: t_startf, t_stopf, t_adj_detailf
     578             :     implicit none
     579             : 
     580             :     type (hybrid_t)             :: hybrid
     581             :     type (EdgeBuffer_t)         :: buffer
     582             :     character(len=*), optional  :: location
     583             : 
     584             :     character(len=*), parameter :: subname = 'bndry_exchange_threaded'
     585             : !VERBOSE
     586             : !    if(present(location)) then 
     587             : !       print *,subname,' ',location 
     588             : !    else
     589             : !       print *,subname,' somewhere'
     590             : !    endif
     591             : 
     592    37327104 :     call gbarrier(buffer%gbarrier, hybrid%ithr)
     593    37327104 :     if(buffer%bndry_type == HME_BNDRY_A2A) then
     594           0 :        call bndry_exchange_a2a(hybrid%par,hybrid%nthreads,hybrid%ithr,buffer,location)
     595    37327104 :     else if (buffer%bndry_type == HME_BNDRY_A2AO) then
     596           0 :        call bndry_exchange_a2ao(hybrid%par,hybrid%nthreads,hybrid%ithr,buffer,location)
     597             :     else
     598    37327104 :        call bndry_exchange_p2p(hybrid%par,hybrid%nthreads,hybrid%ithr,buffer,location)
     599             :     endif
     600    37327104 :     call gbarrier(buffer%gbarrier, hybrid%ithr)
     601             : 
     602    37327104 :  end subroutine bndry_exchange_threaded
     603             : 
     604           0 :  subroutine bndry_exchange_threaded_start(hybrid,buffer,location)
     605    37327104 :     use hybrid_mod, only : hybrid_t
     606             :     use edgetype_mod, only : Edgebuffer_t
     607             :     use perf_mod, only: t_startf, t_stopf, t_adj_detailf
     608             :     implicit none
     609             : 
     610             :     type (hybrid_t)             :: hybrid
     611             :     type (EdgeBuffer_t)         :: buffer
     612             :     character(len=*), optional  :: location
     613             : 
     614             :     character(len=*), parameter :: subname = 'bndry_exchange_threaded_start'
     615             : 
     616           0 :     call gbarrier(buffer%gbarrier, hybrid%ithr)
     617           0 :     call bndry_exchange_p2p_start(hybrid%par,hybrid%nthreads,hybrid%ithr,buffer,location)
     618             : 
     619           0 :  end subroutine bndry_exchange_threaded_start
     620             : 
     621           0 :  subroutine bndry_exchange_threaded_finish(hybrid,buffer,location)
     622           0 :     use hybrid_mod, only : hybrid_t
     623             :     use edgetype_mod, only : Edgebuffer_t
     624             :     use perf_mod, only: t_startf, t_stopf, t_adj_detailf
     625             :     implicit none
     626             : 
     627             :     type (hybrid_t)             :: hybrid
     628             :     type (EdgeBuffer_t)         :: buffer
     629             :     character(len=*), optional  :: location
     630             : 
     631             :     character(len=*), parameter :: subname = 'bndry_exchange_threaded_finish'
     632             : 
     633           0 :     call bndry_exchange_p2p_finish(hybrid%par,hybrid%nthreads,hybrid%ithr,buffer,location)
     634           0 :     call gbarrier(buffer%gbarrier, hybrid%ithr)
     635             : 
     636           0 :  end subroutine bndry_exchange_threaded_finish
     637             : 
     638           0 :  subroutine ghost_exchange_nonthreaded(par,buffer,location)
     639           0 :     use parallel_mod,   only : parallel_t
     640             :     use edgetype_mod, only : Edgebuffer_t
     641             :     type (parallel_t)          :: par
     642             :     type (EdgeBUffer_t)        :: buffer
     643             :     character(len=*), optional :: location 
     644           0 :     call bndry_exchange_nonthreaded(par,buffer,location)
     645           0 :  end subroutine ghost_exchange_nonthreaded
     646             : 
     647      377856 :  subroutine bndry_exchange_nonthreaded(par,buffer,location)
     648             :     use parallel_mod, only : parallel_t
     649             :     use edgetype_mod, only : Edgebuffer_t
     650             :     use perf_mod, only: t_startf, t_stopf, t_adj_detailf
     651             :     implicit none
     652             : 
     653             :     type (parallel_t)           :: par
     654             :     type (EdgeBuffer_t)         :: buffer
     655             :     character(len=*), optional  :: location
     656             : 
     657             :     integer                     :: ithr
     658             :     integer                     :: nthreads
     659             :     character(len=*), parameter :: subname = 'bndry_exchange_nonthreaded'
     660             : 
     661             :     !$OMP BARRIER
     662      377856 :     ithr=0
     663      377856 :     nthreads = 1
     664      377856 :     if(buffer%bndry_type == HME_BNDRY_A2A) then
     665           0 :        call bndry_exchange_a2a(par,nthreads,ithr,buffer,location)
     666      377856 :     else if (buffer%bndry_type == HME_BNDRY_A2AO) then
     667           0 :        call bndry_exchange_a2ao(par,nthreads,ithr,buffer,location)
     668             :     else
     669      377856 :        call bndry_exchange_p2p(par,nthreads,ithr,buffer,location)
     670             :     endif
     671             :     !$OMP BARRIER
     672             : 
     673      377856 :   end subroutine bndry_exchange_nonthreaded
     674             : 
     675           0 :  subroutine bndry_exchange_nonthreaded_start(par,buffer,location)
     676      377856 :     use parallel_mod, only : parallel_t
     677             :     use edgetype_mod, only : Edgebuffer_t
     678             :     use perf_mod, only: t_startf, t_stopf, t_adj_detailf
     679             :     implicit none
     680             : 
     681             :     type (parallel_t)           :: par
     682             :     type (EdgeBuffer_t)         :: buffer
     683             :     character (len=*), optional :: location
     684             : 
     685             :     integer                     :: ithr
     686             :     integer                     :: nthreads
     687             :     character(len=*), parameter :: subname = 'bndry_exchange_nonthreaded_start'
     688             : 
     689             :     !$OMP BARRIER
     690           0 :     ithr=0
     691           0 :     nthreads=1
     692           0 :     call bndry_exchange_p2p_start(par,nthreads,ithr,buffer,location)
     693             : 
     694           0 :  end subroutine bndry_exchange_nonthreaded_start
     695             : 
     696           0 :  subroutine bndry_exchange_nonthreaded_finish(par,buffer,location)
     697           0 :     use parallel_mod, only : parallel_t
     698             :     use edgetype_mod, only : Edgebuffer_t
     699             :     use perf_mod, only: t_startf, t_stopf, t_adj_detailf
     700             :     implicit none
     701             : 
     702             :     type (parallel_t)                 :: par
     703             :     integer                           :: ithr
     704             :     type (EdgeBuffer_t)               :: buffer
     705             :     character (len=*), optional :: location
     706             :     integer :: nthreads
     707             : 
     708             :     character(len=*), parameter :: subname = 'bndry_exchange_nonthreaded_finish'
     709             : 
     710           0 :     ithr=0
     711           0 :     nthreads=1
     712           0 :     call bndry_exchange_p2p_finish(par,nthreads,ithr,buffer,location)
     713             :     !$OMP BARRIER
     714             : 
     715           0 :   end subroutine bndry_exchange_nonthreaded_finish
     716             : 
     717        1536 :   subroutine compute_ghost_corner_orientation(hybrid,elem,nets,nete)
     718             : !
     719             : !  this routine can NOT be called in a threaded region because then each thread
     720             : !  will have its on ghostbuffer.   initghostbufer3D() should detect this and abort.
     721             : !
     722           0 :   use dimensions_mod, only: nelemd, np
     723             :   use parallel_mod, only : syncmp
     724             :   use hybrid_mod, only : hybrid_t
     725             :   use element_mod, only : element_t
     726             :   use edgetype_mod, only : edgebuffer_t
     727             :   use edge_mod, only : ghostpack, ghostunpack, &
     728             :        initghostbuffer,freeghostbuffer
     729             : 
     730             :   use control_mod, only : north,south,east,west,neast, nwest, seast, swest
     731             : 
     732             :   type (hybrid_t)      , intent(in) :: hybrid
     733             :   type (element_t)     , intent(inout), target :: elem(:)
     734             :   integer :: nets,nete
     735        1536 :   type (edgeBuffer_t)    :: ghostbuf_cv
     736             : 
     737        3072 :   real (kind=r8) :: cin(-1:4,-1:4,1,nets:nete)  !CE: fvm tracer
     738        3072 :   real (kind=r8) :: cout(-1:4,-1:4,1,nets:nete)  !CE: fvm tracer
     739             :   integer :: i,j,ie,kptr,np1,np2,nc,nc1,nc2,k,nlev
     740             :   logical :: fail,fail1,fail2
     741             :   real (kind=r8) :: tol = 0.1_r8
     742        1536 :   call syncmp(hybrid%par)
     743             : 
     744             : 
     745             : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     746             :   ! first test on the Gauss Grid with same number of ghost cells:
     747             : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     748        1536 :   nc=2   ! test using GLL interior points
     749        1536 :   nc1=-1
     750        1536 :   nc2=4
     751             : 
     752        1536 :   nlev=1
     753             : 
     754        1536 :   if (hybrid%nthreads > 1) then
     755           0 :      call endrun('ERROR: compute_ghost_corner_orientation must be called before threaded region')
     756             :   endif
     757        1536 :   call initghostbuffer(hybrid%par,ghostbuf_cv,elem,nlev,nc,nc,nthreads=1)
     758             : 
     759             : 
     760      476736 :   cin = 0._r8
     761       12336 :   do ie=nets,nete
     762       10800 :      cin(1,1,1,ie)=  elem(ie)%gdofp(1,1)
     763       10800 :      cin(nc,nc,1,ie)=  elem(ie)%gdofp(np,np)
     764       10800 :      cin(1,nc,1,ie)=   elem(ie)%gdofp(1,np)
     765       12336 :      cin(nc,1,1,ie)=  elem(ie)%gdofp(np,1)
     766             :   enddo
     767      476736 :   cout=0
     768             : 
     769             : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     770             : !  run ghost exchange on c array to get corner orientation
     771             : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     772       12336 :   do ie=nets,nete
     773       10800 :      kptr=0
     774       12336 :      call ghostpack(ghostbuf_cv, cin(:,:,:,ie),nlev,kptr,ie)
     775             :   end do
     776        1536 :   call ghost_exchange(hybrid,ghostbuf_cv,location='compute_ghost_corner_orientation')
     777       12336 :   do ie=nets,nete
     778       10800 :      kptr=0
     779       12336 :      call ghostunpack(ghostbuf_cv, cout(:,:,:,ie),nlev,kptr,ie)
     780             :   enddo
     781             : 
     782             : !       nc +--------+
     783             : !        ^ | nw  ne |
     784             : !     j  | |        |
     785             : !        1 | sw  se |
     786             : !          +--------+
     787             : !           1 --> nc
     788             : !              i
     789             : 
     790             : ! check SW corner
     791       12336 :   do ie=nets,nete
     792       10800 :      fail1=.false.
     793       10800 :      fail2=.false.
     794       10800 :      if ( elem(ie)%desc%putmapP_ghost(swest) /= -1) then
     795       10788 :         if (abs(cout(nc1,1,1,ie)-cout(nc1,0,1,ie)) .gt. tol )  fail1=.true.
     796       10788 :         if (abs(cout(1,nc1,1,ie)-cout(0,nc1,1,ie)).gt.tol) fail2=.true.
     797             :      endif
     798       10788 :      if (fail1 .neqv. fail2 ) call endrun( 'ghost exchange SW orientation failure')
     799       12336 :      if (fail1) then
     800         232 :         elem(ie)%desc%reverse(swest)=.true.
     801             :      endif
     802             :   enddo
     803             : ! check SE corner
     804       12336 :   do ie=nets,nete
     805       10800 :      fail1=.false.
     806       10800 :      fail2=.false.
     807       10800 :      if ( elem(ie)%desc%putmapP_ghost(seast) /= -1) then
     808       10788 :         if (abs(cout(nc2,1,1,ie)-cout(nc2,0,1,ie)) .gt. tol )  fail1=.true.
     809       10788 :         if (abs(cout(nc+1,nc1,1,ie)-cout(nc,nc1,1,ie)).gt.tol) fail2=.true.
     810             :      endif
     811       10788 :      if (fail1 .neqv. fail2 ) call endrun('ghost exchange SE orientation failure')
     812       12336 :      if (fail1) then
     813         232 :         elem(ie)%desc%reverse(seast)=.true.
     814             :      endif
     815             :   enddo
     816             : ! check NW corner
     817       12336 :   do ie=nets,nete
     818       10800 :      fail1=.false.
     819       10800 :      fail2=.false.
     820       10800 :      if ( elem(ie)%desc%putmapP_ghost(nwest) /= -1) then
     821       10788 :         if (abs(cout(nc1,nc+1,1,ie)-cout(nc1,nc,1,ie)) .gt. tol )  fail1=.true.
     822       10788 :         if (abs(cout(1,nc2,1,ie)-cout(0,nc2,1,ie)).gt.tol) fail2=.true.
     823             :      endif
     824       10788 :      if (fail1 .neqv. fail2 ) call endrun( 'ghost exchange NW orientation failure')
     825       12336 :      if (fail1) then
     826         232 :         elem(ie)%desc%reverse(nwest)=.true.
     827             :      endif
     828             :   enddo
     829             : ! check NE corner
     830       12336 :   do ie=nets,nete
     831       10800 :      fail1=.false.
     832       10800 :      fail2=.false.
     833       10800 :      if ( elem(ie)%desc%putmapP_ghost(neast) /= -1) then
     834       10788 :         if (abs(cout(nc2,nc+1,1,ie)-cout(nc2,nc,1,ie)) .gt. tol )  fail1=.true.
     835       10788 :         if (abs(cout(nc+1,nc2,1,ie)-cout(nc,nc2,1,ie)).gt.tol) fail2=.true.
     836             :      endif
     837       10788 :      if (fail1 .neqv. fail2 ) call endrun( 'ghost exchange NE orientation failure')
     838       12336 :      if (fail1) then
     839         232 :         elem(ie)%desc%reverse(neast)=.true.
     840             :      endif
     841             :   enddo
     842        1536 :   call freeghostbuffer(ghostbuf_cv)
     843             : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     844             : !  end ghost exchange corner orientation
     845             : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     846        1536 :   end subroutine
     847           0 :   subroutine ghost_exchangeVfull(par,ithr,buffer)
     848             : !
     849             : !   MT 2011:  derived from bndry_exchange, but copies an entire
     850             : !             element of ghost cell information, including corner
     851             : !             elements.  Requres cubed-sphere grid
     852             : !
     853        1536 :     use hybrid_mod, only : hybrid_t
     854             :     use edgetype_mod,   only: Ghostbuffer3D_t
     855             :     use schedtype_mod, only : schedule_t, cycle_t, schedule
     856             :     use dimensions_mod, only: nelemd
     857             :     use parallel_mod, only : status, srequest, rrequest, parallel_t
     858             :     use spmd_utils,  only: mpi_integer, mpi_success,mpi_real8
     859             : 
     860             :     implicit none
     861             :     type (parallel_t)                :: par
     862             :     integer                          :: ithr     ! hybrid%ithr 0 if called outside threaded region
     863             : 
     864             :     type (GhostBuffer3D_t)           :: buffer
     865             : 
     866             :     type (Schedule_t),pointer        :: pSchedule
     867             :     type (Cycle_t),pointer           :: pCycle
     868             :     integer                          :: dest,length,tag
     869             :     integer                          :: icycle,ierr
     870             :     integer                          :: iptr,source,nlyr
     871             :     integer                          :: nSendCycles,nRecvCycles
     872             :     integer                          :: errorcode,errorlen
     873             :     character(len=*), parameter      :: subname = 'ghost_exchangeVfull'
     874             :     character*(80) errorstring
     875             : 
     876             :     integer                          :: i,i1,i2
     877             : 
     878             :     !$OMP BARRIER
     879           0 :     if(ithr == 0) then
     880             : 
     881             : 
     882             : #ifdef SPMD
     883             :        ! Setup the pointer to proper Schedule
     884           0 :        pSchedule => Schedule(1)
     885           0 :        nlyr = buffer%nlyr
     886             : 
     887           0 :        nSendCycles = pSchedule%nSendCycles
     888           0 :        nRecvCycles = pSchedule%nRecvCycles
     889             : 
     890             :        !==================================================
     891             :        !  Fire off the sends
     892             :        !==================================================
     893           0 :        do icycle=1,nSendCycles
     894           0 :           pCycle      => pSchedule%SendCycle(icycle)
     895           0 :           dest            = pCycle%dest - 1
     896           0 :           length      = nlyr * pCycle%lengthP_ghost * buffer%elem_size
     897           0 :           tag             = pCycle%tag
     898           0 :           iptr            = pCycle%ptrP_ghost
     899             : 
     900           0 :           call MPI_Isend(buffer%buf(1,1,1,iptr),length,MPI_real8,dest,tag,par%comm,Srequest(icycle),ierr)
     901           0 :           if(ierr .ne. MPI_SUCCESS) then
     902           0 :              errorcode=ierr
     903           0 :              call MPI_Error_String(errorcode,errorstring,errorlen,ierr)
     904           0 :              print *,subname,': Error after call to MPI_Isend: ',errorstring
     905             :           endif
     906             :        end do    ! icycle
     907             : 
     908             :        !==================================================
     909             :        !  Post the Receives
     910             :        !==================================================
     911           0 :        do icycle=1,nRecvCycles
     912           0 :           pCycle         => pSchedule%RecvCycle(icycle)
     913           0 :           source          = pCycle%source - 1
     914           0 :           length      = nlyr * pCycle%lengthP_ghost * buffer%elem_size
     915           0 :           tag             = pCycle%tag
     916           0 :           iptr            = pCycle%ptrP_ghost
     917             : 
     918           0 :           call MPI_Irecv(buffer%receive(1,1,1,iptr),length,MPI_real8, &
     919           0 :                source,tag,par%comm,Rrequest(icycle),ierr)
     920           0 :           if(ierr .ne. MPI_SUCCESS) then
     921           0 :              errorcode=ierr
     922           0 :              call MPI_Error_String(errorcode,errorstring,errorlen,ierr)
     923           0 :              print *,subname,': Error after call to MPI_Irecv: ',errorstring
     924             :           endif
     925             :        end do    ! icycle
     926             : 
     927             : 
     928             :        !==================================================
     929             :        !  Wait for all the receives to complete
     930             :        !==================================================
     931             : 
     932           0 :        call MPI_Waitall(nSendCycles,Srequest,status,ierr)
     933           0 :        call MPI_Waitall(nRecvCycles,Rrequest,status,ierr)
     934             : 
     935           0 :        do icycle=1,nRecvCycles
     936           0 :           pCycle         => pSchedule%RecvCycle(icycle)
     937           0 :           length             = pCycle%lengthP_ghost
     938           0 :           iptr            = pCycle%ptrP_ghost
     939           0 :           do i=0,length-1
     940           0 :              buffer%buf(:,:,1:nlyr,iptr+i) = buffer%receive(:,:,1:nlyr,iptr+i)
     941             :           enddo
     942             :        end do   ! icycle
     943             : 
     944             : 
     945             : #endif
     946             :     endif  ! if (hybrid%ithr == 0)
     947             :     !$OMP BARRIER
     948             : 
     949           0 :   end subroutine ghost_exchangeVfull
     950             : 
     951             : 
     952             : end module bndry_mod

Generated by: LCOV version 1.14