LCOV - code coverage report
Current view: top level - src/78_effpot - m_mpi_scheduler.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 50.5 % 101 51
Test Date: 2026-09-21 19:39:32 Functions: 26.7 % 15 4

            Line data    Source code
       1              : !!****m* ABINIT/m_mpi_scheduler
       2              : !! NAME
       3              : !! m_mpi_scheduler
       4              : !!
       5              : !! FUNCTION
       6              : !! This module contains the mpi scheduler for spin dynamics
       7              : !! It provide the function to assign site to mpi nodes, and methods for scattering (TODO),
       8              : !! and gathering data from nodes.
       9              : !!
      10              : !! Datatypes:
      11              : !!
      12              : !! * mpi_scheduler_t
      13              : !!
      14              : !! Subroutines:
      15              : !! TODO: add this when F2003 doc style is determined.
      16              : !!
      17              : !!
      18              : !! COPYRIGHT
      19              : !! Copyright (C) 2001-2026 ABINIT group (hexu)
      20              : !! This file is distributed under the terms of the
      21              : !! GNU General Public License, see ~abinit/COPYING
      22              : !! or http://www.gnu.org/copyleft/gpl.txt .
      23              : !! For the initials of contributors, see ~abinit/doc/developers/contributors.txt .
      24              : !!
      25              : !! SOURCE
      26              : 
      27              : #if defined HAVE_CONFIG_H
      28              : #include "config.h"
      29              : #endif
      30              : #include "abi_common.h"
      31              : 
      32              : 
      33              : module m_mpi_scheduler
      34              :   use defs_basis
      35              :   use m_xmpi
      36              :   use m_errors
      37              :   use m_abicore
      38              : 
      39              :   implicit none
      40              : !!***
      41              : 
      42              : 
      43              :   private
      44              : 
      45              :   type, public :: mb_mpi_info_t
      46              :      integer :: master =0
      47              :      logical :: iam_master =.False.
      48              :      integer :: my_rank, comm, nproc, ierr
      49              :    contains
      50              :      procedure :: initialize => mb_mpi_info_t_initialize
      51              :   end type mb_mpi_info_t
      52              : 
      53              :   type, public :: mpi_scheduler_t
      54              :      integer :: nproc, ntasks, irank,master, comm, istart, iend, ntask, nblock
      55              :      ! ntasks:  total number of tasks
      56              :      ! istart: first task id in this proc
      57              :      ! iend: last task id in this proc
      58              :      ! ntask: number of tasks in this proc
      59              :      ! nblock: sometimes it is useful to group task into blocks(eg. each spin has 3 components and its better to put them together.)
      60              :      integer,  allocatable :: istart_list(:), iend_list(:), ntask_list(:)
      61              :      ! istart_list: istart for all nodes
      62              :      ! iend_list: iend for all nodes
      63              :      ! ntask_list: ntast for all nodes
      64              :    contains
      65              :      procedure :: initialize => mpi_scheduler_t_initialize
      66              :      procedure :: finalize => mpi_scheduler_t_finalize
      67              :      procedure :: get_iproc => mpi_scheduler_t_get_iproc
      68              :      procedure :: get_istart
      69              :      procedure :: get_iend
      70              :      procedure :: get_ntask
      71              :      procedure :: gatherv_dp1d ! helper function to gather 1d real(dp) array from nodes.
      72              :      procedure :: gatherv_dp2d ! helper function to gather 2d real(dp) array from nodes.
      73              :      procedure :: allgatherv_dp1d ! helper function to gather 1d real(dp) array from nodes.
      74              :      procedure :: allgatherv_dp2d ! helper function to gather 2d real(dp) array from nodes.
      75              :     ! procedure :: allgatherv_dp2d_inplace ! helper function to gather 2d real(dp) array from nodes.
      76              :     ! disabled because an openmpi I tried does not support this.
      77              : 
      78              :   end type mpi_scheduler_t
      79              : 
      80              :   public :: init_mpi_info
      81              : 
      82              : contains
      83              :   ! logical :: iam_master
      84              :   ! iam_master=xmpi_comm_rank(xmpi_world)==0
      85              : 
      86              :   !integer :: master, my_rank, comm, nproc, ierr
      87              :   !logical :: iam_master
      88              :   !call init_mpi_info(master, iam_master, my_rank, comm, nproc)
      89      1149777 :   subroutine init_mpi_info(master, iam_master, my_rank, comm, nproc)
      90              :    integer, intent(inout) :: master
      91              :    logical, intent(inout) :: iam_master
      92              :    integer, intent(inout) :: my_rank, comm, nproc
      93      1149777 :    master=0
      94      1149777 :    comm = xmpi_world
      95      1149777 :    nproc = xmpi_comm_size(comm)
      96      1149777 :    my_rank = xmpi_comm_rank(comm)
      97      1149777 :    iam_master = (my_rank == master)
      98      1149777 :   end subroutine init_mpi_info
      99              : 
     100              : 
     101              :   !----------------------------------------------------------------------
     102              :   !> @brief initialize mpi info type
     103              :   !----------------------------------------------------------------------
     104            0 :   subroutine mb_mpi_info_t_initialize(self)
     105              :     class (mb_mpi_info_t):: self
     106            0 :     self%master = 0
     107            0 :     self%comm = xmpi_world
     108            0 :     self%nproc = xmpi_comm_size(self%comm)
     109            0 :     self%my_rank = xmpi_comm_rank(self%comm)
     110            0 :     self%iam_master = (self%my_rank == self%master)
     111            0 :   end subroutine mb_mpi_info_t_initialize
     112              : 
     113              : 
     114              :   !----------------------------------------------------------------------
     115              :   !> @brief assign ntasks to ranks in mpi comm.
     116              :   !> @param[in]  ntasks: number of tasks to be done. e.g. number of spins to be moved.
     117              :   !> @param[in]  master: the id of master node
     118              :   !> @param[in]  comm: the communicator
     119              :   !> @param[in]  nblock: the task should be divided so that nblock are together.
     120              :   !>              e.g. for 3*nspin tasks, nblock=3 will assure that the 3 direction of one
     121              :   !>               spin is together.
     122              :   !----------------------------------------------------------------------
     123              : 
     124           30 :   subroutine mpi_scheduler_t_initialize(self, ntasks, master, comm, nblock)
     125              :     !
     126              :     ! ntask: number of tasks
     127              :     ! nblock: number of subtask per task. TODO: should improve the naming.
     128              :     class(mpi_scheduler_t), intent(inout) :: self
     129              :     integer, intent(in) :: ntasks
     130              :     integer, intent(in) :: master, comm
     131              :     integer, optional, intent(in):: nblock
     132              :     integer :: i,nmore, n, ierr
     133              : 
     134           10 :     if( present(nblock)) then
     135            2 :        self%nblock=nblock
     136              :     else
     137            8 :        self%nblock=1
     138              :     end if
     139           10 :     call xmpi_bcast(self%nblock, master, comm, ierr)
     140              : 
     141           10 :     self%master=master
     142              :     !call MPI_COMM_SIZE(comm, self%nproc, ierr)
     143           10 :     self%nproc = xmpi_comm_size(comm)
     144              :     !call MPI_COMM_RANK(comm, self%iproc, ierr)
     145           10 :     self%irank=xmpi_comm_rank(comm)
     146           10 :     self%comm=comm
     147           10 :     self%ntasks = ntasks
     148           10 :     call xmpi_bcast(self%ntasks, self%master, comm, ierr )
     149           10 :     if (.not. allocated(self%istart_list)) then
     150           30 :        ABI_MALLOC(self%istart_list, (self%nproc))
     151              :     end if
     152              : 
     153           10 :     if (.not. allocated(self%iend_list)) then
     154           30 :        ABI_MALLOC(self%iend_list, (self%nproc) )
     155              :     end if
     156              : 
     157           10 :     if (.not. allocated(self%ntask_list)) then
     158           30 :        ABI_MALLOC(self%ntask_list, (self%nproc))
     159              :     endif
     160              : 
     161              :     ! number of procs which has one more
     162           10 :     nmore=mod(self%ntasks, self%nproc)
     163              : 
     164           10 :     if(nmore==0) then
     165           10 :        n=(self%ntasks-nmore)/self%nproc
     166           20 :        do i = 1, self%nproc
     167           10 :           self%istart_list(i)= 1+(i-1)*n
     168           10 :           self%iend_list(i)=i*n
     169           20 :           self%ntask_list(i)=n
     170              :        end do
     171              :     else
     172            0 :        n=(self%ntasks-nmore)/self%nproc
     173            0 :        do i = 1, nmore
     174            0 :           self%ntask_list(i)=n+1
     175            0 :           self%istart_list(i)= 1+(i-1) *(n+1)
     176            0 :           self%iend_list(i)=self%istart_list(i)+self%ntask_list(i)-1
     177              :        end do
     178              : 
     179            0 :        do i = nmore+1, self%nproc
     180            0 :           self%ntask_list(i)=n
     181            0 :           self%istart_list(i)= self%iend_list(i-1)+1
     182            0 :           self%iend_list(i)=self%istart_list(i)+self%ntask_list(i)-1
     183              :        end do
     184              :     end if
     185              : 
     186           20 :     do i=1, self%nproc
     187           10 :        self%ntask_list(i)=self%ntask_list(i) * self%nblock
     188           10 :        self%istart_list(i) = (self%istart_list(i)-1) *self%nblock + 1
     189           20 :        self%iend_list(i) = self%iend_list(i)*self%nblock
     190              :     end do
     191              : 
     192           10 :     self%istart=self%istart_list(self%irank  + 1)
     193           10 :     self%iend=self%iend_list(self%irank  + 1)
     194           10 :     self%ntask=self%ntask_list(self%irank  + 1)
     195              : 
     196           10 :   end subroutine mpi_scheduler_t_initialize
     197              : 
     198              : 
     199              : 
     200              :   !----------------------------------------------------------------------
     201              :   !> @brief find the proc id of which the index of task belong to
     202              :   !>
     203              :   !> @param[in]  i: the index of task
     204              :   !> @param[out] iproc: the id of process which is in charge of the task
     205              :   !----------------------------------------------------------------------
     206            0 :   function mpi_scheduler_t_get_iproc(self, i) result(iproc)
     207              :     class(mpi_scheduler_t), intent(in) :: self
     208              :     integer, intent(in) :: i
     209              :     integer :: iproc
     210            0 :     iproc=i/(self%ntasks/self%nproc)
     211            0 :   end function mpi_scheduler_t_get_iproc
     212              : 
     213              :   !----------------------------------------------------------------------
     214              :   !> @brief get the start of the id of work for the rank
     215              :   !>
     216              :   !> @param[in]  rank: the id of the rank
     217              :   !> @param[out] i: the starting id of work
     218              :   !----------------------------------------------------------------------
     219            0 :   function get_istart(self, rank) result(i)
     220              :     class(mpi_scheduler_t), intent(in) :: self
     221              :     integer, optional, intent(in):: rank
     222              :     integer :: i, r
     223            0 :     if (present(rank)) then
     224            0 :        r=rank
     225              :     else
     226            0 :        r=self%irank
     227              :     end if
     228            0 :     i=self%istart_list(r+1)
     229            0 :   end function get_istart
     230              : 
     231              :   !----------------------------------------------------------------------
     232              :   !> @brief get the end of the id of work for the rank
     233              :   !>
     234              :   !> @param[in]  rank: the id of the rank
     235              :   !> @param[out] i: the end id of work
     236              :   !----------------------------------------------------------------------
     237            0 :   function get_iend(self, rank) result(i)
     238              :     class(mpi_scheduler_t), intent(in) :: self
     239              :     integer, optional, intent(in):: rank
     240              :     integer :: i, r
     241            0 :     if (present(rank)) then
     242            0 :        r=rank
     243              :     else
     244            0 :        r=self%irank
     245              :     end if
     246            0 :     i=self%iend_list(r+1)
     247            0 :   end function get_iend
     248              : 
     249              :   !----------------------------------------------------------------------
     250              :   !> @brief get the number of work for the rank
     251              :   !>
     252              :   !> @param[in]  rank: the id of the rank
     253              :   !> @param[out] i: the number of works assigned to this rank
     254              :   !----------------------------------------------------------------------
     255            0 :   function get_ntask(self, rank) result(i)
     256              :     class(mpi_scheduler_t), intent(in) :: self
     257              :     integer, optional, intent(in):: rank
     258              :     integer :: i, r
     259            0 :     if (present(rank)) then
     260            0 :        r=rank
     261              :     else
     262            0 :        r=self%irank
     263              :     end if
     264            0 :     i=self%ntask_list(r+1)
     265            0 :   end function get_ntask
     266              : 
     267              :   !----------------------------------------------------------------------
     268              :   !> @brief helper function to gather real(dp) 1D array to  master node
     269              :   !>
     270              :   !> @param[in]  data: the data array to be gathered
     271              :   !> @param[out] buffer: a buffer to be used for the gathering.
     272              :   !----------------------------------------------------------------------
     273            0 :   subroutine gatherv_dp1d(self, data, buffer)
     274              :     class(mpi_scheduler_t), intent(inout) :: self
     275              :     real(dp), intent(inout) :: data(self%ntasks)
     276              :     real(dp), intent(inout) :: buffer(self%ntasks)
     277              :     integer :: ierr
     278              :     call xmpi_gatherv(data(self%istart: self%iend), &
     279              :          & self%ntask, &
     280              :          & buffer,&
     281              :          & self%ntask_list, &
     282              :          & self%istart_list-1, &
     283            0 :          & self%master, self%comm, ierr )
     284            0 :   data(:)=buffer(:)
     285            0 :   end subroutine gatherv_dp1d
     286              : 
     287              :   !----------------------------------------------------------------------
     288              :   !> @brief helper function to gather real(dp) 2D array to  master node
     289              :   !>
     290              :   !> @param[in]  data: the data array to be gathered
     291              :   !> @param[out] buffer: a buffer to be used for the gathering.
     292              :   !----------------------------------------------------------------------
     293            0 :   subroutine gatherv_dp2d(self, data, nrow, buffer)
     294              :     class(mpi_scheduler_t), intent(inout) :: self
     295              :     integer, intent(in) :: nrow
     296              :     real(dp), intent(inout) :: data(nrow,self%ntasks), buffer(nrow, self%ntasks)
     297              :     integer :: ierr
     298              :     call xmpi_gatherv(data(:,self%istart:self%iend), &
     299              :          & self%ntask*nrow, &
     300              :          & buffer, &
     301              :          & self%ntask_list*nrow, &
     302              :          & (self%istart_list-1)*nrow, &
     303            0 :          & self%master, self%comm, ierr)
     304            0 :     data(:,:)=buffer(:,:)
     305            0 :   end subroutine gatherv_dp2d
     306              : 
     307              :   !----------------------------------------------------------------------
     308              :   !> @brief helper function to gather real(dp) 1D array to  master node
     309              :   !>    and bcast to every node
     310              :   !> @param[in]  data: the data array to be gathered
     311              :   !> @param[out] buffer: a buffer to be used for the gathering.
     312              :   !----------------------------------------------------------------------
     313            0 :   subroutine allgatherv_dp1d(self, data, buffer)
     314              :     class(mpi_scheduler_t), intent(inout) :: self
     315              :     real(dp), intent(inout) :: data(self%ntasks)
     316              :     real(dp), intent(inout) :: buffer(self%ntasks)
     317              :     integer :: ierr
     318              :     call xmpi_allgatherv(data(self%istart: self%iend), &
     319              :          & self%ntask, &
     320              :          & buffer,&
     321              :          & self%ntask_list, &
     322              :          & self%istart_list-1, &
     323            0 :          & self%comm, ierr )
     324            0 :   data(:)=buffer(:)
     325            0 :   end subroutine allgatherv_dp1d
     326              : 
     327              : 
     328              :   !----------------------------------------------------------------------
     329              :   !> @brief helper function to gather real(dp) 2D array to  master node
     330              :   !>    and bcast to every node
     331              :   !> @param[in]  data: the data array to be gathered
     332              :   !> @param[out] buffer: a buffer to be used for the gathering.
     333              :   !----------------------------------------------------------------------
     334         4004 :   subroutine allgatherv_dp2d(self, data, nrow, buffer)
     335              :     class(mpi_scheduler_t), intent(inout) :: self
     336              :     integer, intent(in) :: nrow
     337              :     real(dp), intent(inout) :: data(nrow,self%ntasks), buffer(nrow, self%ntasks)
     338              :     integer :: ierr
     339              : 
     340              :     !xmpi_allgatherv_int2d(xval,nelem,recvbuf,recvcounts,displs,spaceComm,ier)
     341              :     call xmpi_allgatherv(data(:,self%istart:self%iend), &
     342              :          & self%ntask*nrow, &
     343              :          & buffer, &
     344              :          & self%ntask_list*nrow, &
     345              :          & (self%istart_list-1)*nrow, &
     346        12012 :          & self%comm, ierr)
     347      3463460 :     data(:,:)=buffer(:,:)
     348         4004 :   end subroutine allgatherv_dp2d
     349              : 
     350              : 
     351              : !  subroutine allgatherv_dp2d_inplace(self, data, nrow)
     352              : !    class(mpi_scheduler_t), intent(inout) :: self
     353              : !    integer, intent(in) :: nrow
     354              : !    real(dp), intent(inout) :: data(nrow,self%ntasks)
     355              : !    integer :: ierr
     356              : !
     357              : !    !xmpi_allgatherv_int2d(xval,nelem,recvbuf,recvcounts,displs,spaceComm,ier)
     358              : !    call xmpi_allgatherv(xmpi_in_place, &
     359              : !         & self%ntask*nrow, &
     360              : !         & data, &
     361              : !         & self%ntask_list*nrow, &
     362              : !         & (self%istart_list-1)*nrow, &
     363              : !         & self%comm, ierr)
     364              : !  end subroutine allgatherv_dp2d_inplace
     365              : 
     366              :   !----------------------------------------------------------------------
     367              :   !> @brief free memory used
     368              :   !----------------------------------------------------------------------
     369           11 :   subroutine mpi_scheduler_t_finalize(self)
     370              :     class(mpi_scheduler_t), intent(inout) :: self
     371           11 :     if (allocated(self%istart_list)) then
     372           10 :        ABI_FREE(self%istart_list)
     373              :     endif
     374           11 :     if (allocated(self%iend_list)) then
     375           10 :        ABI_FREE(self%iend_list)
     376              :     endif
     377           11 :     if (allocated(self%ntask_list)) then
     378           10 :        ABI_FREE(self%ntask_list)
     379              :     endif
     380           11 :   end subroutine mpi_scheduler_t_finalize
     381              : 
     382              : 
     383            0 : end module m_mpi_scheduler
        

Generated by: LCOV version 2.3-1