comms.F90 Source File


This file depends on

sourcefile~~comms.f90~~EfferentGraph sourcefile~comms.f90 comms.F90 sourcefile~constants.f90 constants.F90 sourcefile~comms.f90->sourcefile~constants.f90 sourcefile~error_base.f90 error_base.F90 sourcefile~comms.f90->sourcefile~error_base.f90

Files dependent on this one

sourcefile~~comms.f90~~AfferentGraph sourcefile~comms.f90 comms.F90 sourcefile~berry.f90 berry.F90 sourcefile~berry.f90->sourcefile~comms.f90 sourcefile~error.f90 error.F90 sourcefile~berry.f90->sourcefile~error.f90 sourcefile~get_oper.f90 get_oper.F90 sourcefile~berry.f90->sourcefile~get_oper.f90 sourcefile~io.f90 io.F90 sourcefile~berry.f90->sourcefile~io.f90 sourcefile~postw90_common.f90 postw90_common.F90 sourcefile~berry.f90->sourcefile~postw90_common.f90 sourcefile~postw90_types.f90 postw90_types.F90 sourcefile~berry.f90->sourcefile~postw90_types.f90 sourcefile~spin.f90 spin.F90 sourcefile~berry.f90->sourcefile~spin.f90 sourcefile~utility.f90 utility.F90 sourcefile~berry.f90->sourcefile~utility.f90 sourcefile~wan_ham.f90 wan_ham.F90 sourcefile~berry.f90->sourcefile~wan_ham.f90 sourcefile~tetrahedron.f90 tetrahedron.F90 sourcefile~berry.f90->sourcefile~tetrahedron.f90 sourcefile~boltzwann.f90 boltzwann.F90 sourcefile~boltzwann.f90->sourcefile~comms.f90 sourcefile~dos.f90 dos.F90 sourcefile~boltzwann.f90->sourcefile~dos.f90 sourcefile~boltzwann.f90->sourcefile~error.f90 sourcefile~boltzwann.f90->sourcefile~get_oper.f90 sourcefile~boltzwann.f90->sourcefile~io.f90 sourcefile~boltzwann.f90->sourcefile~postw90_types.f90 sourcefile~readwrite.f90 readwrite.F90 sourcefile~boltzwann.f90->sourcefile~readwrite.f90 sourcefile~boltzwann.f90->sourcefile~spin.f90 sourcefile~boltzwann.f90->sourcefile~utility.f90 sourcefile~boltzwann.f90->sourcefile~wan_ham.f90 sourcefile~disentangle.f90 disentangle.F90 sourcefile~disentangle.f90->sourcefile~comms.f90 sourcefile~disentangle.f90->sourcefile~error.f90 sourcefile~disentangle.f90->sourcefile~io.f90 sourcefile~sitesym.f90 sitesym.F90 sourcefile~disentangle.f90->sourcefile~sitesym.f90 sourcefile~disentangle.f90->sourcefile~utility.f90 sourcefile~dos.f90->sourcefile~comms.f90 sourcefile~dos.f90->sourcefile~error.f90 sourcefile~dos.f90->sourcefile~get_oper.f90 sourcefile~dos.f90->sourcefile~io.f90 sourcefile~dos.f90->sourcefile~postw90_common.f90 sourcefile~dos.f90->sourcefile~postw90_types.f90 sourcefile~dos.f90->sourcefile~spin.f90 sourcefile~dos.f90->sourcefile~utility.f90 sourcefile~dos.f90->sourcefile~wan_ham.f90 sourcefile~error.f90->sourcefile~comms.f90 sourcefile~geninterp.f90 geninterp.F90 sourcefile~geninterp.f90->sourcefile~comms.f90 sourcefile~geninterp.f90->sourcefile~error.f90 sourcefile~geninterp.f90->sourcefile~get_oper.f90 sourcefile~geninterp.f90->sourcefile~io.f90 sourcefile~geninterp.f90->sourcefile~postw90_common.f90 sourcefile~geninterp.f90->sourcefile~postw90_types.f90 sourcefile~geninterp.f90->sourcefile~utility.f90 sourcefile~geninterp.f90->sourcefile~wan_ham.f90 sourcefile~get_oper.f90->sourcefile~comms.f90 sourcefile~get_oper.f90->sourcefile~error.f90 sourcefile~get_oper.f90->sourcefile~io.f90 sourcefile~get_oper.f90->sourcefile~postw90_types.f90 sourcefile~get_oper.f90->sourcefile~utility.f90 sourcefile~gyrotropic.f90 gyrotropic.F90 sourcefile~gyrotropic.f90->sourcefile~comms.f90 sourcefile~gyrotropic.f90->sourcefile~berry.f90 sourcefile~gyrotropic.f90->sourcefile~error.f90 sourcefile~gyrotropic.f90->sourcefile~get_oper.f90 sourcefile~gyrotropic.f90->sourcefile~io.f90 sourcefile~gyrotropic.f90->sourcefile~postw90_common.f90 sourcefile~gyrotropic.f90->sourcefile~postw90_types.f90 sourcefile~gyrotropic.f90->sourcefile~spin.f90 sourcefile~gyrotropic.f90->sourcefile~utility.f90 sourcefile~gyrotropic.f90->sourcefile~wan_ham.f90 sourcefile~hamiltonian.f90 hamiltonian.F90 sourcefile~hamiltonian.f90->sourcefile~comms.f90 sourcefile~hamiltonian.f90->sourcefile~error.f90 sourcefile~hamiltonian.f90->sourcefile~io.f90 sourcefile~hamiltonian.f90->sourcefile~utility.f90 sourcefile~io.f90->sourcefile~comms.f90 sourcefile~kmesh.f90 kmesh.F90 sourcefile~kmesh.f90->sourcefile~comms.f90 sourcefile~kmesh.f90->sourcefile~error.f90 sourcefile~kmesh.f90->sourcefile~io.f90 sourcefile~kmesh.f90->sourcefile~utility.f90 sourcefile~kpath.f90 kpath.F90 sourcefile~kpath.f90->sourcefile~comms.f90 sourcefile~kpath.f90->sourcefile~berry.f90 sourcefile~kpath.f90->sourcefile~error.f90 sourcefile~kpath.f90->sourcefile~get_oper.f90 sourcefile~kpath.f90->sourcefile~io.f90 sourcefile~kpath.f90->sourcefile~postw90_common.f90 sourcefile~kpath.f90->sourcefile~postw90_types.f90 sourcefile~kpath.f90->sourcefile~spin.f90 sourcefile~kpath.f90->sourcefile~utility.f90 sourcefile~kslice.f90 kslice.F90 sourcefile~kslice.f90->sourcefile~comms.f90 sourcefile~kslice.f90->sourcefile~berry.f90 sourcefile~kslice.f90->sourcefile~error.f90 sourcefile~kslice.f90->sourcefile~get_oper.f90 sourcefile~kslice.f90->sourcefile~io.f90 sourcefile~kslice.f90->sourcefile~postw90_common.f90 sourcefile~kslice.f90->sourcefile~postw90_types.f90 sourcefile~kslice.f90->sourcefile~spin.f90 sourcefile~kslice.f90->sourcefile~utility.f90 sourcefile~kslice.f90->sourcefile~wan_ham.f90 sourcefile~library_extra.f90 library_extra.F90 sourcefile~library_extra.f90->sourcefile~comms.f90 sourcefile~library_extra.f90->sourcefile~error.f90 sourcefile~library_extra.f90->sourcefile~io.f90 sourcefile~library_extra.f90->sourcefile~kmesh.f90 sourcefile~library_interface.f90 library_interface.F90 sourcefile~library_extra.f90->sourcefile~library_interface.f90 sourcefile~overlap.f90 overlap.F90 sourcefile~library_extra.f90->sourcefile~overlap.f90 sourcefile~library_extra.f90->sourcefile~readwrite.f90 sourcefile~wannier90_readwrite.f90 wannier90_readwrite.F90 sourcefile~library_extra.f90->sourcefile~wannier90_readwrite.f90 sourcefile~library_interface.f90->sourcefile~comms.f90 sourcefile~library_interface.f90->sourcefile~disentangle.f90 sourcefile~library_interface.f90->sourcefile~error.f90 sourcefile~library_interface.f90->sourcefile~io.f90 sourcefile~library_interface.f90->sourcefile~kmesh.f90 sourcefile~library_interface.f90->sourcefile~overlap.f90 sourcefile~plot.f90 plot.F90 sourcefile~library_interface.f90->sourcefile~plot.f90 sourcefile~library_interface.f90->sourcefile~readwrite.f90 sourcefile~transport.f90 transport.F90 sourcefile~library_interface.f90->sourcefile~transport.f90 sourcefile~wannierise.f90 wannierise.F90 sourcefile~library_interface.f90->sourcefile~wannierise.f90 sourcefile~library_interface.f90->sourcefile~wannier90_readwrite.f90 sourcefile~overlap.f90->sourcefile~comms.f90 sourcefile~overlap.f90->sourcefile~error.f90 sourcefile~overlap.f90->sourcefile~io.f90 sourcefile~overlap.f90->sourcefile~sitesym.f90 sourcefile~overlap.f90->sourcefile~utility.f90 sourcefile~plot.f90->sourcefile~comms.f90 sourcefile~plot.f90->sourcefile~error.f90 sourcefile~plot.f90->sourcefile~hamiltonian.f90 sourcefile~plot.f90->sourcefile~io.f90 sourcefile~plot.f90->sourcefile~utility.f90 sourcefile~ws_distance.f90 ws_distance.F90 sourcefile~plot.f90->sourcefile~ws_distance.f90 sourcefile~postw90.f90 postw90.F90 sourcefile~postw90.f90->sourcefile~comms.f90 sourcefile~postw90.f90->sourcefile~berry.f90 sourcefile~postw90.f90->sourcefile~boltzwann.f90 sourcefile~postw90.f90->sourcefile~dos.f90 sourcefile~postw90.f90->sourcefile~error.f90 sourcefile~postw90.f90->sourcefile~geninterp.f90 sourcefile~postw90.f90->sourcefile~gyrotropic.f90 sourcefile~postw90.f90->sourcefile~io.f90 sourcefile~postw90.f90->sourcefile~kmesh.f90 sourcefile~postw90.f90->sourcefile~kpath.f90 sourcefile~postw90.f90->sourcefile~kslice.f90 sourcefile~postw90.f90->sourcefile~postw90_common.f90 sourcefile~postw90_readwrite.f90 postw90_readwrite.F90 sourcefile~postw90.f90->sourcefile~postw90_readwrite.f90 sourcefile~postw90.f90->sourcefile~postw90_types.f90 sourcefile~postw90.f90->sourcefile~readwrite.f90 sourcefile~postw90.f90->sourcefile~spin.f90 sourcefile~postw90_common.f90->sourcefile~comms.f90 sourcefile~postw90_common.f90->sourcefile~error.f90 sourcefile~postw90_common.f90->sourcefile~io.f90 sourcefile~postw90_common.f90->sourcefile~postw90_types.f90 sourcefile~postw90_common.f90->sourcefile~utility.f90 sourcefile~postw90_common.f90->sourcefile~ws_distance.f90 sourcefile~postw90_readwrite.f90->sourcefile~comms.f90 sourcefile~postw90_readwrite.f90->sourcefile~error.f90 sourcefile~postw90_readwrite.f90->sourcefile~postw90_types.f90 sourcefile~postw90_readwrite.f90->sourcefile~readwrite.f90 sourcefile~postw90_readwrite.f90->sourcefile~utility.f90 sourcefile~postw90_types.f90->sourcefile~comms.f90 sourcefile~readwrite.f90->sourcefile~comms.f90 sourcefile~readwrite.f90->sourcefile~error.f90 sourcefile~readwrite.f90->sourcefile~io.f90 sourcefile~readwrite.f90->sourcefile~utility.f90 sourcefile~sitesym.f90->sourcefile~comms.f90 sourcefile~sitesym.f90->sourcefile~error.f90 sourcefile~sitesym.f90->sourcefile~utility.f90 sourcefile~spin.f90->sourcefile~comms.f90 sourcefile~spin.f90->sourcefile~error.f90 sourcefile~spin.f90->sourcefile~get_oper.f90 sourcefile~spin.f90->sourcefile~postw90_common.f90 sourcefile~spin.f90->sourcefile~postw90_types.f90 sourcefile~spin.f90->sourcefile~utility.f90 sourcefile~transport.f90->sourcefile~comms.f90 sourcefile~transport.f90->sourcefile~error.f90 sourcefile~transport.f90->sourcefile~hamiltonian.f90 sourcefile~transport.f90->sourcefile~io.f90 sourcefile~utility.f90->sourcefile~comms.f90 sourcefile~utility.f90->sourcefile~error.f90 sourcefile~wan_ham.f90->sourcefile~comms.f90 sourcefile~wan_ham.f90->sourcefile~error.f90 sourcefile~wan_ham.f90->sourcefile~get_oper.f90 sourcefile~wan_ham.f90->sourcefile~postw90_common.f90 sourcefile~wan_ham.f90->sourcefile~postw90_types.f90 sourcefile~wan_ham.f90->sourcefile~utility.f90 sourcefile~wannier_prog.f90 wannier_prog.F90 sourcefile~wannier_prog.f90->sourcefile~comms.f90 sourcefile~wannier_prog.f90->sourcefile~error.f90 sourcefile~wannier_prog.f90->sourcefile~io.f90 sourcefile~wannier_prog.f90->sourcefile~library_extra.f90 sourcefile~wannier_prog.f90->sourcefile~library_interface.f90 sourcefile~wannier_prog.f90->sourcefile~sitesym.f90 sourcefile~wannierise.f90->sourcefile~comms.f90 sourcefile~wannierise.f90->sourcefile~error.f90 sourcefile~wannierise.f90->sourcefile~hamiltonian.f90 sourcefile~wannierise.f90->sourcefile~io.f90 sourcefile~wannierise.f90->sourcefile~sitesym.f90 sourcefile~wannierise.f90->sourcefile~utility.f90 sourcefile~wannierise.f90->sourcefile~wannier90_readwrite.f90 sourcefile~ws_distance.f90->sourcefile~comms.f90 sourcefile~ws_distance.f90->sourcefile~error.f90 sourcefile~ws_distance.f90->sourcefile~io.f90 sourcefile~ws_distance.f90->sourcefile~utility.f90 sourcefile~c_interface.f90 c_interface.F90 sourcefile~c_interface.f90->sourcefile~library_interface.f90 sourcefile~tetrahedron.f90->sourcefile~utility.f90 sourcefile~w90chk2chk.f90 w90chk2chk.F90 sourcefile~w90chk2chk.f90->sourcefile~io.f90 sourcefile~w90spn2spn.f90 w90spn2spn.F90 sourcefile~w90spn2spn.f90->sourcefile~io.f90 sourcefile~wannier90_readwrite.f90->sourcefile~error.f90 sourcefile~wannier90_readwrite.f90->sourcefile~io.f90 sourcefile~wannier90_readwrite.f90->sourcefile~readwrite.f90 sourcefile~wannier90_readwrite.f90->sourcefile~utility.f90

Source Code

!-*- mode: F90 -*-!
!------------------------------------------------------------!
! Copyright (C) 2026 Wannier Developer Group                 !
!                                                            !
! This library is free software; you can redistribute it     !
! and/or modify it under the terms of the GNU Lesser General !
! Public License as published by the Free Software           !
! Foundation; either version 2.1 of the License, or (at your !
! option) any later version.                                 !
!                                                            !
! This library is distributed in the hope that it will be    !
! useful,but WITHOUT ANY WARRANTY; without even the implied  !
! warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR    !
! PURPOSE.  See the GNU Lesser General Public License for    !
! more details.                                              !
!                                                            !
! You should have received a copy of the GNU Lesser General  !
! Public License along with this library; if not, see        !
! <https://www.gnu.org/licenses/>.                           !
!                                                            !
! The webpage of the Wannier90 code is                       !
! <https://www.wannier.org>.                                 !
!                                                            !
! The Wannier90 code is hosted on GitHub                     !
! <https://github.com/wannier-developers/wannier90>          !
!------------------------------------------------------------!
!                                                            !
!  COMMS: set of MPI wrappers                                !
!  written 2006-2012 Jonathan R. Yates                       !
!    later additions Giovanni Pizzi                          !
!                                                            !
!------------------------------------------------------------!

module w90_comms
  !! This module handles all of the communications

  use w90_constants, only: dp
  use w90_error_base

#ifdef W90_MPI
#  if !(defined(W90_MPI08) || defined(W90_MPI90) || defined(W90_MPIH))
#    error "You need to define which MPI interface you are using"
#  endif
#else
#define MPI_COMM_NULL -1
#endif

#ifdef W90_MPI08
  use mpi_f08 ! use f08 interface if possible
#endif
#ifdef W90_MPI90
  use mpi ! next best, use fortran90 interface
#endif

  implicit none

#ifdef W90_MPIH
  include 'mpif.h' ! worst case, use legacy interface
#endif

  private

  integer, parameter :: code_mpi = 4 ! this is duplicated here to avoid circular dependency with error.F90

  integer, parameter :: mpi_send_tag = 77 ! arbitrary
  integer, parameter :: root_id = 0 ! not arbitrary

  public :: comms_allreduce  ! reduce data onto all nodes
  public :: comms_array_split
  public :: comms_barrier    ! puts a barrier so that the code goes on only when all nodes reach the barrier
  public :: comms_bcast      ! send data from the root node
  public :: comms_gatherv    ! gets chunks of an array from all nodes and gathers them on the root node
  !public :: comms_recv       ! accept data from one node to another
  public :: comms_reduce     ! reduce data onto root node (n.b. not allreduce); data is lost on all other nodes
  public :: comms_scatterv   ! sends chunks of an array to all nodes scattering them from the root node
  !public :: comms_send       ! send data from one node to another
  public :: mpirank
  public :: mpisize
  public :: comms_sync_error
  public :: valid_communicator
  !! test whether communicator is initialised; returns true always for serial build

  ! versions without error synchronisation, use at own risk
  public :: comms_no_sync_allreduce  ! reduce data onto all nodes
  public :: comms_no_sync_barrier    ! puts a barrier so that the code goes on only when all nodes reach the barrier
  public :: comms_no_sync_bcast      ! send data from the root node
  public :: comms_no_sync_gatherv    ! gets chunks of an array from all nodes and gathers them on the root node
  public :: comms_no_sync_recv       ! accept data from one node to another
  public :: comms_no_sync_reduce     ! reduce data onto root node (n.b. not allreduce); data is lost on all other nodes
  public :: comms_no_sync_scatterv   ! sends chunks of an array to all nodes scattering them from the root node
  public :: comms_no_sync_send       ! send data from one node to another

  type, public :: w90_comm_type
#ifdef W90_MPI08
    type(mpi_comm) :: comm = MPI_COMM_NULL ! f08 mpi interface
#else
    integer :: comm = MPI_COMM_NULL ! f90 mpi or no mpi
#endif
  end type

  type, private :: w90stat_type
#ifdef W90_MPI08
    type(mpi_status) :: stat ! f08 mpi interface
#elif W90_MPI90
    integer :: stat(MPI_STATUS_SIZE)
#else
    integer :: stat ! not used
#endif
  end type

  interface comms_bcast
    module procedure comms_bcast_int
    module procedure comms_bcast_logical
    module procedure comms_bcast_real
    module procedure comms_bcast_cmplx
    module procedure comms_bcast_char
  end interface comms_bcast

  ! We don't currently have error synchronisation for point-to-point comms
  !interface comms_send
  !  module procedure comms_send_int
  !  module procedure comms_send_logical
  !  module procedure comms_send_real
  !  module procedure comms_send_cmplx
  !  module procedure comms_send_char
  !end interface comms_send

  !interface comms_recv
  !  module procedure comms_recv_int
  !  module procedure comms_recv_logical
  !  module procedure comms_recv_real
  !  module procedure comms_recv_cmplx
  !  module procedure comms_recv_char
  !end interface comms_recv

  interface comms_reduce
    module procedure comms_reduce_int
    module procedure comms_reduce_real
    module procedure comms_reduce_cmplx
  end interface comms_reduce

  interface comms_allreduce
!     module procedure comms_allreduce_int    ! to be done
    module procedure comms_allreduce_real
    module procedure comms_allreduce_cmplx
  end interface comms_allreduce

  interface comms_gatherv
!     module procedure comms_gatherv_int    ! to be done
    module procedure comms_gatherv_logical
    module procedure comms_gatherv_real_1
    module procedure comms_gatherv_real_2
    module procedure comms_gatherv_real_3
    module procedure comms_gatherv_real_2_3
    module procedure comms_gatherv_cmplx_1
    module procedure comms_gatherv_cmplx_2
    module procedure comms_gatherv_cmplx_3
    module procedure comms_gatherv_cmplx_3_4
    module procedure comms_gatherv_cmplx_4
  end interface comms_gatherv

  interface comms_scatterv
    module procedure comms_scatterv_int_1
    module procedure comms_scatterv_int_2
    module procedure comms_scatterv_int_3
    module procedure comms_scatterv_real_1
    module procedure comms_scatterv_real_2
    module procedure comms_scatterv_real_3
!     module procedure comms_scatterv_cmplx
    module procedure comms_scatterv_cmplx_3
    module procedure comms_scatterv_cmplx_4
  end interface comms_scatterv

  !! These routines do not synchronise errors. You should not normally use them.
  !! If you do use them, you are required to make sure that the error conditions
  !! between the MPI processes are correctly synced up on exit from your code.
  interface comms_no_sync_bcast
    module procedure comms_no_sync_bcast_int
    module procedure comms_no_sync_bcast_logical
    module procedure comms_no_sync_bcast_real
    module procedure comms_no_sync_bcast_cmplx
    module procedure comms_no_sync_bcast_char
  end interface comms_no_sync_bcast

  interface comms_no_sync_send
    module procedure comms_no_sync_send_int
    module procedure comms_no_sync_send_logical
    module procedure comms_no_sync_send_real
    module procedure comms_no_sync_send_cmplx
    module procedure comms_no_sync_send_char
  end interface comms_no_sync_send

  interface comms_no_sync_recv
    module procedure comms_no_sync_recv_int
    module procedure comms_no_sync_recv_logical
    module procedure comms_no_sync_recv_real
    module procedure comms_no_sync_recv_cmplx
    module procedure comms_no_sync_recv_char
  end interface comms_no_sync_recv

  interface comms_no_sync_reduce
    module procedure comms_no_sync_reduce_int
    module procedure comms_no_sync_reduce_real
    module procedure comms_no_sync_reduce_cmplx
  end interface comms_no_sync_reduce

  interface comms_no_sync_allreduce
!     module procedure comms_no_sync_allreduce_int    ! to be done
    module procedure comms_no_sync_allreduce_real
    module procedure comms_no_sync_allreduce_cmplx
  end interface comms_no_sync_allreduce

  interface comms_no_sync_gatherv
!     module procedure comms_no_sync_gatherv_int    ! to be done
    module procedure comms_no_sync_gatherv_logical
    module procedure comms_no_sync_gatherv_real_1
    module procedure comms_no_sync_gatherv_real_2
    module procedure comms_no_sync_gatherv_real_3
    module procedure comms_no_sync_gatherv_real_2_3
    module procedure comms_no_sync_gatherv_cmplx_1
    module procedure comms_no_sync_gatherv_cmplx_2
    module procedure comms_no_sync_gatherv_cmplx_3
    module procedure comms_no_sync_gatherv_cmplx_3_4
    module procedure comms_no_sync_gatherv_cmplx_4
  end interface comms_no_sync_gatherv

  interface comms_no_sync_scatterv
    module procedure comms_no_sync_scatterv_int_1
    module procedure comms_no_sync_scatterv_int_2
    module procedure comms_no_sync_scatterv_int_3
    module procedure comms_no_sync_scatterv_real_1
    module procedure comms_no_sync_scatterv_real_2
    module procedure comms_no_sync_scatterv_real_3
!     module procedure comms_no_sync_scatterv_cmplx
    module procedure comms_no_sync_scatterv_cmplx_3
    module procedure comms_no_sync_scatterv_cmplx_4
  end interface comms_no_sync_scatterv

contains

  logical function valid_communicator(comm)
    type(w90_comm_type), intent(in) :: comm
#ifdef W90_MPI
    if (comm%comm == MPI_COMM_NULL) then
      valid_communicator = .false.
    else
      valid_communicator = .true.
    end if
#else
    valid_communicator = .true.
#endif
  end function

  ! mpi rank function for convenience
  integer function mpirank(comm)
    type(w90_comm_type), intent(in) :: comm
    integer :: ierr
#ifdef W90_MPI
    call mpi_comm_rank(comm%comm, mpirank, ierr)
#else
    mpirank = 0
#endif
  end function

  ! mpi size function for convenience
  integer function mpisize(comm)
    type(w90_comm_type), intent(in) :: comm
    integer :: ierr
#ifdef W90_MPI
    call mpi_comm_size(comm%comm, mpisize, ierr)
#else
    mpisize = 1
#endif
  end function

  ! synchronise error condition between MPI processess
  subroutine comms_sync_error(comm, error, ierr)
    implicit none
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(inout) :: error
    integer :: ierr, mpiierr, abserr

#if defined(W90_MPI) && !defined(DISABLE_ERROR_SYNC)
    abserr = abs(ierr) ! possibility of -ve values, use abs for safety
    call mpi_allreduce(MPI_IN_PLACE, abserr, 1, MPI_INTEGER, MPI_SUM, comm%comm, mpiierr)
    ! you could check mpiierr here, but truly all bets are off in that case
    if (abserr > 0 .and. ierr == 0) call recv_error(error)
#endif
  end subroutine comms_sync_error

  subroutine comms_array_split(numpoints, counts, displs, comm)
    !! Given an array of size numpoints, we want to split on num_nodes nodes. This function returns
    !! two arrays: count and displs.
    !!
    !! The i-th element of the count array gives the number of elements
    !! that must be calculated by the process with id (i-1).
    !! The i-th element of the displs array gives the displacement of the array calculated locally on
    !! the process with id (i-1) with respect to the global array.
    !!
    !! These values are those to be passed to the functions MPI_Scatterv, MPI_Gatherv and MPI_Alltoallv.
    !!
    !! one can use the following do loop to run over the needed elements, if the full array is stored
    !! on all nodes:
    !! do i=displs(my_node_id)+1,displs(my_node_id)+counts(my_node_id)
    !!

    integer, intent(in) :: numpoints  !! Number of elements of the array to be scattered
    integer, intent(inout) :: counts(0:) !! Array (of size num_nodes) with the number of elements of the array on each node
    integer, intent(inout) :: displs(0:) !! Array (of size num_nodes) with the displacement relative to the global array
    type(w90_comm_type), intent(in) :: comm

    integer :: ratio, remainder, i
    integer :: num_nodes

    num_nodes = mpisize(comm)

    ratio = numpoints/num_nodes
    remainder = MOD(numpoints, num_nodes)

    do i = 0, num_nodes - 1
      if (i < remainder) then
        counts(i) = ratio + 1
        displs(i) = i*(ratio + 1)
      else
        counts(i) = ratio
        displs(i) = remainder*(ratio + 1) + (i - remainder)*ratio
      end if
    end do

  end subroutine comms_array_split

  subroutine comms_no_sync_barrier(comm)
    !! A barrier to synchronise all nodes
    implicit none
    type(w90_comm_type), intent(in) :: comm

#ifdef W90_MPI
    integer :: ierr

    call mpi_barrier(comm%comm, ierr)
#endif

  end subroutine comms_no_sync_barrier

  subroutine comms_no_sync_bcast_int(array, size, error, comm)
    !! Send integar array from root node to all nodes
    implicit none

    integer, intent(inout) :: array
    integer, intent(in) :: size
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_bcast(array, size, MPI_INTEGER, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_bcast_int', code_mpi)
      return
    end if
#endif
  end subroutine comms_no_sync_bcast_int

  subroutine comms_no_sync_bcast_real(array, size, error, comm)
    !! Send real array from root node to all nodes
    implicit none

    real(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_bcast(array, size, MPI_DOUBLE_PRECISION, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_bcast_real', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_bcast_real

  subroutine comms_no_sync_bcast_logical(array, size, error, comm)
    !! Send logical array from root node to all nodes
    implicit none

    logical, intent(inout) :: array
    integer, intent(in) :: size
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_bcast(array, size, MPI_LOGICAL, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_bcast_logical', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_bcast_logical

  subroutine comms_no_sync_bcast_char(array, size, error, comm)
    !! Send character array from root node to all nodes
    implicit none

    character(len=*), intent(inout) :: array
    integer, intent(in) :: size
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_bcast(array, size, MPI_CHARACTER, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_bcast_char', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_bcast_char

  subroutine comms_no_sync_bcast_cmplx(array, size, error, comm)
    !! Send character array from root node to all nodes

    implicit none

    complex(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_bcast(array, size, MPI_DOUBLE_COMPLEX, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_bcast_cmplx', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_bcast_cmplx

  !--------- SEND ----------------

  subroutine comms_no_sync_send_logical(array, size, to, error, comm)
    !! Send logical array to specified node

    implicit none

    logical, intent(inout) :: array
    integer, intent(in) :: size
    integer, intent(in) :: to
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_send(array, size, MPI_LOGICAL, to, mpi_send_tag, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_send_logical', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_send_logical

  subroutine comms_no_sync_send_int(array, size, to, error, comm)
    !! Send integer array to specified node
    implicit none

    integer, intent(inout) :: array
    integer, intent(in) :: size
    integer, intent(in) :: to
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_send(array, size, MPI_INTEGER, to, mpi_send_tag, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_send_int', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_send_int

  subroutine comms_no_sync_send_char(array, size, to, error, comm)
    !! Send character array to specified node
    implicit none

    character(len=*), intent(inout) :: array
    integer, intent(in) :: size
    integer, intent(in) :: to
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_send(array, size, MPI_CHARACTER, to, mpi_send_tag, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_send_char', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_send_char

  subroutine comms_no_sync_send_real(array, size, to, error, comm)
    !! Send real array to specified node
    implicit none

    real(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    integer, intent(in) :: to
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_send(array, size, MPI_DOUBLE_PRECISION, to, mpi_send_tag, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_send_real', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_send_real

  subroutine comms_no_sync_send_cmplx(array, size, to, error, comm)
    !! Send complex array to specified node
    implicit none

    complex(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    integer, intent(in) :: to
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_send(array, size, MPI_DOUBLE_COMPLEX, to, mpi_send_tag, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_send_cmplx', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_send_cmplx

  !--------- RECV ----------------

  subroutine comms_no_sync_recv_logical(array, size, from, error, comm)
    !! Receive logical array from specified node
    implicit none

    logical, intent(inout) :: array
    integer, intent(in) :: size
    integer, intent(in) :: from
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    type(w90stat_type) :: status
    integer :: ierr

    call mpi_recv(array, size, MPI_LOGICAL, from, mpi_send_tag, comm%comm, status%stat, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_recv_logical', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_recv_logical

  subroutine comms_no_sync_recv_int(array, size, from, error, comm)
    !! Receive integer array from specified node
    implicit none

    integer, intent(inout) :: array
    integer, intent(in) :: size
    integer, intent(in) :: from
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    type(w90stat_type) :: status
    integer :: ierr

    call mpi_recv(array, size, MPI_INTEGER, from, mpi_send_tag, comm%comm, status%stat, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_recv_int', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_recv_int

  subroutine comms_no_sync_recv_char(array, size, from, error, comm)
    !! Receive character array from specified node
    implicit none

    character(len=*), intent(inout) :: array
    integer, intent(in) :: size
    integer, intent(in) :: from
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    type(w90stat_type) :: status
    integer :: ierr

    call mpi_recv(array, size, MPI_CHARACTER, from, mpi_send_tag, comm%comm, status%stat, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_recv_char', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_recv_char

  subroutine comms_no_sync_recv_real(array, size, from, error, comm)
    !! Receive real array from specified node
    implicit none

    real(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    integer, intent(in) :: from
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    type(w90stat_type) :: status
    integer :: ierr

    call mpi_recv(array, size, MPI_DOUBLE_PRECISION, from, mpi_send_tag, comm%comm, &
                  status%stat, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_recv_real', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_recv_real

  subroutine comms_no_sync_recv_cmplx(array, size, from, error, comm)
    !! Receive complex array from specified node
    implicit none

    complex(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    integer, intent(in) :: from
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    type(w90stat_type) :: status
    integer :: ierr

    call mpi_recv(array, size, MPI_DOUBLE_COMPLEX, from, mpi_send_tag, comm%comm, &
                  status%stat, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_recv_cmplx', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_recv_cmplx

  subroutine comms_no_sync_reduce_int(array, size, op, error, comm)
    !! Reduce integer data to root node
    implicit none

    integer, intent(inout) :: array
    integer, intent(in) :: size
    character(len=*), intent(in) :: op
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr
    integer :: rank
    rank = mpirank(comm)

    ! note, JJ 23/2/2021
    ! previously this routine alloc'd/used/dealloc'd a temp array
    ! to be used as receive buffer for MPI_reduce
    ! this temp array was then copied to argument "array"
    ! but: "array" needs to be of scalar type for the polymorphism to work
    ! so: need to copy array into a (fake) scalar
    ! previously: a subroutine my_icopy was used to help to do this.
    ! probably just reducing in place is better?

    select case (op)
    case ('SUM')
      if (rank == root_id) then
        call mpi_reduce(MPI_IN_PLACE, array, size, MPI_INTEGER, MPI_SUM, root_id, comm%comm, &
                        ierr)
      else
        call mpi_reduce(array, array, size, MPI_INTEGER, MPI_SUM, root_id, comm%comm, ierr)
      end if
    case ('PRD')
      if (rank == root_id) then
        call mpi_reduce(MPI_IN_PLACE, array, size, MPI_INTEGER, MPI_PROD, root_id, comm%comm, &
                        ierr)
      else
        call mpi_reduce(array, array, size, MPI_INTEGER, MPI_PROD, root_id, comm%comm, ierr)
      end if
    case default
      call set_base_error(error, 'Unknown operation in comms_reduce_int', code_mpi)
      return
    end select

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_reduce_int', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_reduce_int

  subroutine comms_no_sync_reduce_real(array, size, op, error, comm)
    !! Reduce real data to root node

    implicit none

    real(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    character(len=*), intent(in) :: op
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr
    integer :: rank
    rank = mpirank(comm)

    select case (op)

    case ('SUM')
      if (rank == root_id) then
        call mpi_reduce(MPI_IN_PLACE, array, size, MPI_DOUBLE_PRECISION, MPI_SUM, root_id, &
                        comm%comm, ierr)
      else
        call mpi_reduce(array, array, size, MPI_DOUBLE_PRECISION, MPI_SUM, root_id, comm%comm, &
                        ierr)
      end if
    case ('PRD')
      if (rank == root_id) then
        call mpi_reduce(MPI_IN_PLACE, array, size, MPI_DOUBLE_PRECISION, MPI_PROD, root_id, &
                        comm%comm, ierr)
      else
        call mpi_reduce(array, array, size, MPI_DOUBLE_PRECISION, MPI_PROD, root_id, comm%comm, &
                        ierr)
      end if
    case ('MIN')
      if (rank == root_id) then
        call mpi_reduce(MPI_IN_PLACE, array, size, MPI_DOUBLE_PRECISION, MPI_MIN, root_id, &
                        comm%comm, ierr)
      else
        call mpi_reduce(array, array, size, MPI_DOUBLE_PRECISION, MPI_MIN, root_id, comm%comm, &
                        ierr)
      end if
    case ('MAX')
      if (rank == root_id) then
        call mpi_reduce(MPI_IN_PLACE, array, size, MPI_DOUBLE_PRECISION, MPI_MAX, root_id, &
                        comm%comm, ierr)
      else
        call mpi_reduce(array, array, size, MPI_DOUBLE_PRECISION, MPI_MAX, root_id, comm%comm, &
                        ierr)
      end if
    case default
      call set_base_error(error, 'Unknown operation in comms_reduce_real', code_mpi)
      return

    end select

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_reduce_real', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_reduce_real

  subroutine comms_no_sync_reduce_cmplx(array, size, op, error, comm)
    !! Reduce complex data to root node

    implicit none

    complex(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    character(len=*), intent(in) :: op
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr
    integer :: rank
    rank = mpirank(comm)

    select case (op)

    case ('SUM')
      if (rank == root_id) then
        call mpi_reduce(MPI_IN_PLACE, array, size, MPI_DOUBLE_COMPLEX, MPI_SUM, root_id, &
                        comm%comm, ierr)
      else
        call mpi_reduce(array, array, size, MPI_DOUBLE_COMPLEX, MPI_SUM, root_id, comm%comm, &
                        ierr)
      end if
    case ('PRD')
      if (rank == root_id) then
        call mpi_reduce(MPI_IN_PLACE, array, size, MPI_DOUBLE_COMPLEX, MPI_PROD, root_id, &
                        comm%comm, ierr)
      else
        call mpi_reduce(array, array, size, MPI_DOUBLE_COMPLEX, MPI_PROD, root_id, comm%comm, &
                        ierr)
      end if
    case default
      call set_base_error(error, 'Unknown operation in comms_reduce_cmplx', code_mpi)
      return

    end select

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_reduce_cmplx', code_mpi)
      return
    end if

#endif

  end subroutine comms_no_sync_reduce_cmplx

  subroutine comms_no_sync_allreduce_real(array, size, op, error, comm)
    !! Reduce real data to all nodes

    implicit none

    real(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    character(len=*), intent(in) :: op
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    select case (op)

    case ('SUM')
      call mpi_allreduce(MPI_IN_PLACE, array, size, MPI_DOUBLE_PRECISION, MPI_SUM, comm%comm, &
                         ierr)
    case ('PRD')
      call mpi_allreduce(MPI_IN_PLACE, array, size, MPI_DOUBLE_PRECISION, MPI_PROD, comm%comm, &
                         ierr)
    case ('MIN')
      call mpi_allreduce(MPI_IN_PLACE, array, size, MPI_DOUBLE_PRECISION, MPI_MIN, comm%comm, &
                         ierr)
    case ('MAX')
      call mpi_allreduce(MPI_IN_PLACE, array, size, MPI_DOUBLE_PRECISION, MPI_MAX, comm%comm, &
                         ierr)
    case default
      call set_base_error(error, 'Unknown operation in comms_allreduce_real', code_mpi)
      return

    end select

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_allreduce_real', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_allreduce_real

  subroutine comms_no_sync_allreduce_cmplx(array, size, op, error, comm)
    !! Reduce complex data to all nodes
    implicit none

    complex(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    character(len=*), intent(in) :: op
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    select case (op)

    case ('SUM')
      call mpi_allreduce(MPI_IN_PLACE, array, size, MPI_DOUBLE_COMPLEX, MPI_SUM, comm%comm, &
                         ierr)
    case ('PRD')
      call mpi_allreduce(MPI_IN_PLACE, array, size, MPI_DOUBLE_COMPLEX, MPI_PROD, comm%comm, &
                         ierr)
    case default
      call set_base_error(error, 'Unknown operation in comms_allreduce_cmplx', code_mpi)
      return

    end select

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_allreduce_cmplx', code_mpi)
      return
    end if
#endif

  end subroutine comms_no_sync_allreduce_cmplx

  subroutine comms_no_sync_gatherv_real_1(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather real data to root node (for arrays of rank 1)
    implicit none

    real(kind=dp), intent(inout) :: array(:) !! local array for sending data
    integer, intent(in) :: localcount !! localcount elements will be sent to the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:) !! array on the root node to which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_gatherv(array, localcount, MPI_DOUBLE_PRECISION, rootglobalarray, counts, &
                     displs, MPI_DOUBLE_PRECISION, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_gatherv_real_1', code_mpi)
      return
    end if
#else
    !call dcopy(localcount, array, 1, rootglobalarray, 1)
    rootglobalarray = array
#endif

  end subroutine comms_no_sync_gatherv_real_1

  subroutine comms_no_sync_gatherv_real_2(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather real data to root node (for arrays of rank 2)
    implicit none

    real(kind=dp), intent(inout) :: array(:, :)           !! local array for sending data
    integer, intent(in) :: localcount                     !! localcount elements will be sent to the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:, :) !! array on the root node to which data will be sent
    integer, intent(in) :: counts(0:)                     !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_gatherv(array, localcount, MPI_DOUBLE_PRECISION, rootglobalarray, counts, &
                     displs, MPI_DOUBLE_PRECISION, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_gatherv_real_2', code_mpi)
      return
    end if
#else
    !call dcopy(localcount, array, 1, rootglobalarray, 1)
    rootglobalarray = array
#endif

  end subroutine comms_no_sync_gatherv_real_2

  subroutine comms_no_sync_gatherv_real_3(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather real data to root node (for arrays of rank 3)
    implicit none

    real(kind=dp), intent(inout) :: array(:, :, :)           !! local array for sending data
    integer, intent(in) :: localcount                        !! localcount elements will be sent to the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:, :, :) !! array on the root node to which data will be sent
    integer, intent(in) :: counts(0:)                         !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_gatherv(array, localcount, MPI_DOUBLE_PRECISION, rootglobalarray, counts, &
                     displs, MPI_DOUBLE_PRECISION, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_gatherv_real_3', code_mpi)
      return
    end if

#else
    !call dcopy(localcount, array, 1, rootglobalarray, 1)
    rootglobalarray = array
#endif

  end subroutine comms_no_sync_gatherv_real_3

  subroutine comms_no_sync_gatherv_real_2_3(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather real data to root node (for arrays of rank 2 and 3, respectively)
    implicit none

    real(kind=dp), intent(inout) :: array(:, :)              !! local array for sending data
    integer, intent(in) :: localcount                        !! localcount elements will be sent to the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:, :, :) !! array on the root node to which data will be sent
    integer, intent(in) :: counts(0:)                         !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_gatherv(array, localcount, MPI_DOUBLE_PRECISION, rootglobalarray, counts, displs, &
                     MPI_DOUBLE_PRECISION, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_gatherv_real_2_3', code_mpi)
      return
    end if

#else
    call dcopy(localcount, array, 1, rootglobalarray, 1)
    !rootglobalarray = array ! shapes don't match
#endif

  end subroutine comms_no_sync_gatherv_real_2_3

  ! Array: local array for sending data; localcount elements will be sent
  !        to the root node
  ! rootglobalarray: array on the root node to which data will be sent
  ! counts, displs : how data should be partitioned, see MPI documentation or
  !                  function comms_array_split

  subroutine comms_no_sync_gatherv_cmplx_1(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather complex data to root node (for arrays of rank 1)
    implicit none

    complex(kind=dp), intent(inout) :: array(:)
    integer, intent(in) :: localcount
    complex(kind=dp), intent(inout) :: rootglobalarray(:)
    integer, intent(in) :: counts(0:)
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_gatherv(array, localcount, MPI_DOUBLE_COMPLEX, rootglobalarray, counts, displs, &
                     MPI_DOUBLE_COMPLEX, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_gatherv_cmplx_1', code_mpi)
      return
    end if

#else
    !call zcopy(localcount, array, 1, rootglobalarray, 1)
    rootglobalarray = array
#endif

  end subroutine comms_no_sync_gatherv_cmplx_1

  subroutine comms_no_sync_gatherv_cmplx_2(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather complex data to root node (for arrays of rank 2)
    implicit none

    complex(kind=dp), intent(inout) :: array(:, :)
    integer, intent(in) :: localcount
    complex(kind=dp), intent(inout) :: rootglobalarray(:, :)
    integer, intent(in) :: counts(0:)
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_gatherv(array, localcount, MPI_DOUBLE_COMPLEX, rootglobalarray, counts, displs, &
                     MPI_DOUBLE_COMPLEX, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_gatherv_cmplx_2', code_mpi)
      return
    end if

#else
    call zcopy(localcount, array, 1, rootglobalarray, 1)
    rootglobalarray = array
#endif

  end subroutine comms_no_sync_gatherv_cmplx_2

  subroutine comms_no_sync_gatherv_cmplx_3(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather complex data to root node (for arrays of rank 3)
    implicit none

    complex(kind=dp), intent(inout) :: array(:, :, :)
    integer, intent(in) :: localcount
    complex(kind=dp), intent(inout) :: rootglobalarray(:, :, :)
    integer, intent(in) :: counts(0:)
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_gatherv(array, localcount, MPI_DOUBLE_COMPLEX, rootglobalarray, counts, displs, &
                     MPI_DOUBLE_COMPLEX, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_gatherv_cmplx_3', code_mpi)
      return
    end if

#else
    !call zcopy(localcount, array, 1, rootglobalarray, 1)
    rootglobalarray = array
#endif

  end subroutine comms_no_sync_gatherv_cmplx_3

  subroutine comms_no_sync_gatherv_cmplx_3_4(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather complex data to root node (for arrays of rank 3 and 4, respectively)
    implicit none

    complex(kind=dp), intent(inout) :: array(:, :, :)
    integer, intent(in) :: localcount
    complex(kind=dp), intent(inout) :: rootglobalarray(:, :, :, :)
    integer, intent(in) :: counts(0:)
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_gatherv(array, localcount, MPI_DOUBLE_COMPLEX, rootglobalarray, counts, displs, &
                     MPI_DOUBLE_COMPLEX, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_gatherv_cmplx_3_4', code_mpi)
      return
    end if

#else
    call zcopy(localcount, array, 1, rootglobalarray, 1)
    !rootglobalarray = array ! shapes don't match
#endif

  end subroutine comms_no_sync_gatherv_cmplx_3_4

  subroutine comms_no_sync_gatherv_cmplx_4(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather complex data to root node (for arrays of rank 4)
    implicit none

    complex(kind=dp), intent(inout) :: array(:, :, :, :)
    integer, intent(in) :: localcount
    complex(kind=dp), intent(inout) :: rootglobalarray(:, :, :, :)
    integer, intent(in) :: counts(0:)
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_gatherv(array, localcount, MPI_DOUBLE_COMPLEX, rootglobalarray, counts, displs, &
                     MPI_DOUBLE_COMPLEX, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_gatherv_cmplx_4', code_mpi)
      return
    end if

#else
    !call zcopy(localcount, array, 1, rootglobalarray, 1)
    rootglobalarray = array
#endif

  end subroutine comms_no_sync_gatherv_cmplx_4

  subroutine comms_no_sync_gatherv_logical(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather real data to root node
    implicit none

    logical, intent(inout) :: array !! local array for sending data
    integer, intent(in) :: localcount !! localcount elements will be sent to the root node
    logical, intent(inout) :: rootglobalarray !! array on the root node to which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_gatherv(array, localcount, MPI_LOGICAL, rootglobalarray, counts, displs, &
                     MPI_LOGICAL, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_gatherv_logical', code_mpi)
      return
    end if
#else
!    rootglobalarray(1:localcount)=array(1:localcount)
#endif

  end subroutine comms_no_sync_gatherv_logical

  subroutine comms_no_sync_scatterv_real_1(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter real data from root node (array of rank 1)
    implicit none

    real(kind=dp), intent(inout) :: array(:) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:) !! array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_scatterv(rootglobalarray, counts, displs, MPI_DOUBLE_PRECISION, array, localcount, &
                      MPI_DOUBLE_PRECISION, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_scatterv_real_1', code_mpi)
      return
    end if

#else
    !call dcopy(localcount, rootglobalarray, 1, array, 1)
    array = rootglobalarray
#endif

  end subroutine comms_no_sync_scatterv_real_1

  subroutine comms_no_sync_scatterv_real_2(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter real data from root node (array of rank 2)
    implicit none

    real(kind=dp), intent(inout) :: array(:, :) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:, :) !! array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_scatterv(rootglobalarray, counts, displs, MPI_DOUBLE_PRECISION, array, localcount, &
                      MPI_DOUBLE_PRECISION, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_scatterv_real_2', code_mpi)
      return
    end if

#else
    !call dcopy(localcount, rootglobalarray, 1, array, 1)
    array = rootglobalarray
#endif

  end subroutine comms_no_sync_scatterv_real_2

  subroutine comms_no_sync_scatterv_real_3(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter real data from root node (array of rank 3)
    implicit none

    real(kind=dp), intent(inout) :: array(:, :, :) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:, :, :) !! array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_scatterv(rootglobalarray, counts, displs, MPI_DOUBLE_PRECISION, array, localcount, &
                      MPI_DOUBLE_PRECISION, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_scatterv_real_3', code_mpi)
      return
    end if

#else
    !call dcopy(localcount, rootglobalarray, 1, array, 1)
    array = rootglobalarray
#endif

  end subroutine comms_no_sync_scatterv_real_3

  subroutine comms_no_sync_scatterv_cmplx_3(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter complex data from root node (array of rank 3)
    implicit none

    complex(kind=dp), intent(inout) :: array(:, :, :) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    complex(kind=dp), intent(inout) :: rootglobalarray(:, :, :) !! array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_scatterv(rootglobalarray, counts, displs, MPI_DOUBLE_COMPLEX, array, localcount, &
                      MPI_DOUBLE_COMPLEX, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_scatterv_cmplx_3', code_mpi)
      return
    end if

#else
    !call zcopy(localcount, rootglobalarray, 1, array, 1)
    array = rootglobalarray
#endif

  end subroutine comms_no_sync_scatterv_cmplx_3

  subroutine comms_no_sync_scatterv_cmplx_4(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter complex data from root node (array of rank 4)
    implicit none

    complex(kind=dp), intent(inout) :: array(:, :, :, :) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    complex(kind=dp), intent(inout) :: rootglobalarray(:, :, :, :) !! array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_scatterv(rootglobalarray, counts, displs, MPI_DOUBLE_COMPLEX, array, localcount, &
                      MPI_DOUBLE_COMPLEX, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_scatterv_cmplx_4', code_mpi)
      return
    end if

#else
    !call zcopy(localcount, rootglobalarray, 1, array, 1)
    array = rootglobalarray
#endif

  end subroutine comms_no_sync_scatterv_cmplx_4

  subroutine comms_no_sync_scatterv_int_1(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter integer data from root node (array of rank 1)
    implicit none

    integer, intent(inout) :: array(:) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    integer, intent(inout) :: rootglobalarray(:) !!  array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_scatterv(rootglobalarray, counts, displs, MPI_INTEGER, array, localcount, &
                      MPI_INTEGER, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_scatterv_real', code_mpi)
      return
    end if

#else
    !call my_icopy(localcount, rootglobalarray, 1, array, 1)
    array = rootglobalarray
#endif

  end subroutine comms_no_sync_scatterv_int_1

  subroutine comms_no_sync_scatterv_int_2(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter integer data from root node (array of rank 2)

    implicit none

    integer, intent(inout) :: array(:, :) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    integer, intent(inout) :: rootglobalarray(:, :) !!  array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_scatterv(rootglobalarray, counts, displs, MPI_INTEGER, array, localcount, &
                      MPI_INTEGER, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_scatterv_int_2', code_mpi)
      return
    end if

#else
    !call my_icopy(localcount, rootglobalarray, 1, array, 1)
    array = rootglobalarray
#endif

  end subroutine comms_no_sync_scatterv_int_2

  subroutine comms_no_sync_scatterv_int_3(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter integer data from root node (array of rank 3)

    implicit none

    integer, intent(inout) :: array(:, :, :) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    integer, intent(inout) :: rootglobalarray(:, :, :) !!  array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

#ifdef W90_MPI
    integer :: ierr

    call mpi_scatterv(rootglobalarray, counts, displs, MPI_INTEGER, array, localcount, &
                      MPI_INTEGER, root_id, comm%comm, ierr)

    if (ierr .ne. MPI_SUCCESS) then
      call set_base_error(error, 'Error in comms_scatterv_int_3', code_mpi)
      return
    end if

#else
    !call my_icopy(localcount, rootglobalarray, 1, array, 1)
    array = rootglobalarray
#endif

  end subroutine comms_no_sync_scatterv_int_3

  ! The routines that synchronise the errors
  subroutine comms_barrier(error, comm)
    !! A barrier to synchronise all nodes
    implicit none
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    call comms_sync_error(comm, error, 0)
    if (allocated(error)) return
    ! The barrier is almost redundant since the sync is global, unless DISABLE_ERROR_SYNC defined
    call comms_no_sync_barrier(comm)
  end subroutine comms_barrier

  subroutine comms_bcast_int(array, size, error, comm)
    !! Send integar array from root node to all nodes
    implicit none

    integer, intent(inout) :: array
    integer, intent(in) :: size
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0)
    if (allocated(error)) return

    call comms_no_sync_bcast_int(array, size, error, comm)
  end subroutine comms_bcast_int

  subroutine comms_bcast_real(array, size, error, comm)
    !! Send real array from root node to all nodes
    implicit none

    real(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_bcast_real(array, size, error, comm)
  end subroutine comms_bcast_real

  subroutine comms_bcast_logical(array, size, error, comm)
    !! Send logical array from root node to all nodes
    implicit none

    logical, intent(inout) :: array
    integer, intent(in) :: size
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_bcast_logical(array, size, error, comm)
  end subroutine comms_bcast_logical

  subroutine comms_bcast_char(array, size, error, comm)
    !! Send character array from root node to all nodes
    implicit none

    character(len=*), intent(inout) :: array
    integer, intent(in) :: size
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_bcast_char(array, size, error, comm)
  end subroutine comms_bcast_char

  subroutine comms_bcast_cmplx(array, size, error, comm)
    !! Send character array from root node to all nodes

    implicit none

    complex(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_bcast_cmplx(array, size, error, comm)
  end subroutine comms_bcast_cmplx

  subroutine comms_reduce_int(array, size, op, error, comm)
    !! Reduce integer data to root node
    implicit none

    integer, intent(inout) :: array
    integer, intent(in) :: size
    character(len=*), intent(in) :: op
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_reduce_int(array, size, op, error, comm)
  end subroutine comms_reduce_int

  subroutine comms_reduce_real(array, size, op, error, comm)
    !! Reduce real data to root node

    implicit none

    real(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    character(len=*), intent(in) :: op
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_reduce_real(array, size, op, error, comm)
  end subroutine comms_reduce_real

  subroutine comms_reduce_cmplx(array, size, op, error, comm)
    !! Reduce complex data to root node

    implicit none

    complex(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    character(len=*), intent(in) :: op
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_reduce_cmplx(array, size, op, error, comm)
  end subroutine comms_reduce_cmplx

  subroutine comms_allreduce_real(array, size, op, error, comm)
    !! Reduce real data to all nodes

    implicit none

    real(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    character(len=*), intent(in) :: op
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_allreduce_real(array, size, op, error, comm)
  end subroutine comms_allreduce_real

  subroutine comms_allreduce_cmplx(array, size, op, error, comm)
    !! Reduce complex data to all nodes
    implicit none

    complex(kind=dp), intent(inout) :: array
    integer, intent(in) :: size
    character(len=*), intent(in) :: op
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_allreduce_cmplx(array, size, op, error, comm)
  end subroutine comms_allreduce_cmplx

  subroutine comms_gatherv_real_1(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather real data to root node (for arrays of rank 1)
    implicit none

    real(kind=dp), intent(inout) :: array(:) !! local array for sending data
    integer, intent(in) :: localcount !! localcount elements will be sent to the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:) !! array on the root node to which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_gatherv_real_1(array, localcount, rootglobalarray, counts, displs, &
                                      error, comm)
  end subroutine comms_gatherv_real_1

  subroutine comms_gatherv_real_2(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather real data to root node (for arrays of rank 2)
    implicit none

    real(kind=dp), intent(inout) :: array(:, :)           !! local array for sending data
    integer, intent(in) :: localcount                     !! localcount elements will be sent to the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:, :) !! array on the root node to which data will be sent
    integer, intent(in) :: counts(0:)                     !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_gatherv_real_2(array, localcount, rootglobalarray, counts, displs, &
                                      error, comm)
  end subroutine comms_gatherv_real_2

  subroutine comms_gatherv_real_3(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather real data to root node (for arrays of rank 3)
    implicit none

    real(kind=dp), intent(inout) :: array(:, :, :)           !! local array for sending data
    integer, intent(in) :: localcount                        !! localcount elements will be sent to the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:, :, :) !! array on the root node to which data will be sent
    integer, intent(in) :: counts(0:)                         !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_gatherv_real_3(array, localcount, rootglobalarray, counts, displs, &
                                      error, comm)
  end subroutine comms_gatherv_real_3

  subroutine comms_gatherv_real_2_3(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather real data to root node (for arrays of rank 2 and 3, respectively)
    implicit none

    real(kind=dp), intent(inout) :: array(:, :)              !! local array for sending data
    integer, intent(in) :: localcount                        !! localcount elements will be sent to the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:, :, :) !! array on the root node to which data will be sent
    integer, intent(in) :: counts(0:)                         !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_gatherv_real_2_3(array, localcount, rootglobalarray, counts, displs, &
                                        error, comm)
  end subroutine comms_gatherv_real_2_3

  ! Array: local array for sending data; localcount elements will be sent
  !        to the root node
  ! rootglobalarray: array on the root node to which data will be sent
  ! counts, displs : how data should be partitioned, see MPI documentation or
  !                  function comms_array_split

  subroutine comms_gatherv_cmplx_1(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather complex data to root node (for arrays of rank 1)
    implicit none

    complex(kind=dp), intent(inout) :: array(:)
    integer, intent(in) :: localcount
    complex(kind=dp), intent(inout) :: rootglobalarray(:)
    integer, intent(in) :: counts(0:)
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_gatherv_cmplx_1(array, localcount, rootglobalarray, counts, displs, &
                                       error, comm)
  end subroutine comms_gatherv_cmplx_1

  subroutine comms_gatherv_cmplx_2(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather complex data to root node (for arrays of rank 2)
    implicit none

    complex(kind=dp), intent(inout) :: array(:, :)
    integer, intent(in) :: localcount
    complex(kind=dp), intent(inout) :: rootglobalarray(:, :)
    integer, intent(in) :: counts(0:)
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_gatherv_cmplx_2(array, localcount, rootglobalarray, counts, displs, &
                                       error, comm)
  end subroutine comms_gatherv_cmplx_2

  subroutine comms_gatherv_cmplx_3(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather complex data to root node (for arrays of rank 3)
    implicit none

    complex(kind=dp), intent(inout) :: array(:, :, :)
    integer, intent(in) :: localcount
    complex(kind=dp), intent(inout) :: rootglobalarray(:, :, :)
    integer, intent(in) :: counts(0:)
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_gatherv_cmplx_3(array, localcount, rootglobalarray, counts, displs, &
                                       error, comm)
  end subroutine comms_gatherv_cmplx_3

  subroutine comms_gatherv_cmplx_3_4(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather complex data to root node (for arrays of rank 3 and 4, respectively)
    implicit none

    complex(kind=dp), intent(inout) :: array(:, :, :)
    integer, intent(in) :: localcount
    complex(kind=dp), intent(inout) :: rootglobalarray(:, :, :, :)
    integer, intent(in) :: counts(0:)
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_gatherv_cmplx_3_4(array, localcount, rootglobalarray, counts, displs, &
                                         error, comm)
  end subroutine comms_gatherv_cmplx_3_4

  subroutine comms_gatherv_cmplx_4(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather complex data to root node (for arrays of rank 4)
    implicit none

    complex(kind=dp), intent(inout) :: array(:, :, :, :)
    integer, intent(in) :: localcount
    complex(kind=dp), intent(inout) :: rootglobalarray(:, :, :, :)
    integer, intent(in) :: counts(0:)
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_gatherv_cmplx_4(array, localcount, rootglobalarray, counts, displs, &
                                       error, comm)
  end subroutine comms_gatherv_cmplx_4

  subroutine comms_gatherv_logical(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Gather real data to root node
    implicit none

    logical, intent(inout) :: array !! local array for sending data
    integer, intent(in) :: localcount !! localcount elements will be sent to the root node
    logical, intent(inout) :: rootglobalarray !! array on the root node to which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_gatherv_logical(array, localcount, rootglobalarray, counts, displs, &
                                       error, comm)
  end subroutine comms_gatherv_logical

  subroutine comms_scatterv_real_1(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter real data from root node (array of rank 1)
    implicit none

    real(kind=dp), intent(inout) :: array(:) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:) !! array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_scatterv_real_1(array, localcount, rootglobalarray, counts, displs, &
                                       error, comm)
  end subroutine comms_scatterv_real_1

  subroutine comms_scatterv_real_2(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter real data from root node (array of rank 2)
    implicit none

    real(kind=dp), intent(inout) :: array(:, :) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:, :) !! array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_scatterv_real_2(array, localcount, rootglobalarray, counts, displs, &
                                       error, comm)
  end subroutine comms_scatterv_real_2

  subroutine comms_scatterv_real_3(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter real data from root node (array of rank 3)
    implicit none

    real(kind=dp), intent(inout) :: array(:, :, :) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    real(kind=dp), intent(inout) :: rootglobalarray(:, :, :) !! array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_scatterv_real_3(array, localcount, rootglobalarray, counts, displs, &
                                       error, comm)
  end subroutine comms_scatterv_real_3

  subroutine comms_scatterv_cmplx_3(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter complex data from root node (array of rank 3)
    implicit none

    complex(kind=dp), intent(inout) :: array(:, :, :) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    complex(kind=dp), intent(inout) :: rootglobalarray(:, :, :) !! array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_scatterv_cmplx_3(array, localcount, rootglobalarray, counts, displs, &
                                        error, comm)
  end subroutine comms_scatterv_cmplx_3

  subroutine comms_scatterv_cmplx_4(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter complex data from root node (array of rank 4)
    implicit none

    complex(kind=dp), intent(inout) :: array(:, :, :, :) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    complex(kind=dp), intent(inout) :: rootglobalarray(:, :, :, :) !! array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_scatterv_cmplx_4(array, localcount, rootglobalarray, counts, displs, &
                                        error, comm)
  end subroutine comms_scatterv_cmplx_4

  subroutine comms_scatterv_int_1(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter integer data from root node (array of rank 1)
    implicit none

    integer, intent(inout) :: array(:) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    integer, intent(inout) :: rootglobalarray(:) !!  array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_scatterv_int_1(array, localcount, rootglobalarray, counts, displs, &
                                      error, comm)
  end subroutine comms_scatterv_int_1

  subroutine comms_scatterv_int_2(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter integer data from root node (array of rank 2)

    implicit none

    integer, intent(inout) :: array(:, :) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    integer, intent(inout) :: rootglobalarray(:, :) !!  array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_scatterv_int_2(array, localcount, rootglobalarray, counts, displs, &
                                      error, comm)
  end subroutine comms_scatterv_int_2

  subroutine comms_scatterv_int_3(array, localcount, rootglobalarray, counts, displs, error, comm)
    !! Scatter integer data from root node (array of rank 3)

    implicit none

    integer, intent(inout) :: array(:, :, :) !! local array for getting data
    integer, intent(in) :: localcount !! localcount elements will be fetched from the root node
    integer, intent(inout) :: rootglobalarray(:, :, :) !!  array on the root node from which data will be sent
    integer, intent(in) :: counts(0:) !! how data should be partitioned, see MPI documentation or function comms_array_split
    integer, intent(in) :: displs(0:)
    type(w90_comm_type), intent(in) :: comm
    type(w90_error_type), allocatable, intent(out) :: error

    call comms_sync_error(comm, error, 0) ! sync error state across comm
    if (allocated(error)) return

    call comms_no_sync_scatterv_int_3(array, localcount, rootglobalarray, counts, displs, &
                                      error, comm)
  end subroutine comms_scatterv_int_3
end module w90_comms