I have this code but I get this error.
I tried declaring status as
INTEGER :: status
but that changes the value of my rank after the MPI_SENDRECV (i.e. the rank=0 for all processors)
PROGRAM testsendrecv
IMPLICIT NONE
INTEGER :: i, k, nx, nz
INTEGER :: ierror, comm, p, rank, npr, prev
INTEGER :: status(MPI_STATUS_SIZE)
REAL(KIND = 8), ALLOCATABLE :: A(:,:), B(:), C(:)
include 'mpif.h'
nx = 5
nz = 5
ALLOCATE(A(nx,nz), B(nx))
CALL MPI_INIT(ierror)
comm = MPI_COMM_WORLD
!Get rank
CALL MPI_COMM_RANK(comm, rank, ierror)
!Get number of processors
CALL MPI_COMM_SIZE(comm, p, ierror)
A(:,:) = rank
IF(rank==0) THEN
prev = p-1
ELSE
prev = rank-1
END IF
CALL MPI_SENDRECV(A(:,1), nx, MPI_DOUBLE_PRECISION, MOD(rank+1,p), 1, &
B(:), nx, MPI_DOUBLE_PRECISION, prev, 1, comm, status, ierror)
WRITE(*,*) rank
WRITE(*,*) B(1)
CALL MPI_FINALIZE(ierror)
END PROGRAM testsendrecv
The above code gives me the following error
bash-4.1$ mpif90 testsendr.f90
mpif.h:79.35:
Included at testsendr.f90:9:
PARAMETER (MPI_STATUS_SIZE=5)
1
Error: VARIABLE attribute of 'mpi_status_size' conflicts with PARAMETER attribute at (1)
mpif.h:80.33:
Included at testsendr.f90:9:
INTEGER MPI_STATUS_IGNORE(MPI_STATUS_SIZE)
1
Error: Variable 'mpi_status_size' cannot appear in the expression at (1)
mpif.h:80.49:
Included at testsendr.f90:9:
INTEGER MPI_STATUS_IGNORE(MPI_STATUS_SIZE)
1
Error: The module or main program array 'mpi_status_ignore' at (1) must have constant shape
mpif.h:81.35:
Included at testsendr.f90:9:
INTEGER MPI_STATUSES_IGNORE(MPI_STATUS_SIZE,1)
1
Error: Variable 'mpi_status_size' cannot appear in the expression at (1)
mpif.h:81.53:
Included at testsendr.f90:9:
INTEGER MPI_STATUSES_IGNORE(MPI_STATUS_SIZE,1)
1
Error: The module or main program array 'mpi_statuses_ignore' at (1) must have constant shape
testsendr.f90:6.20:
INTEGER :: status(MPI_STATUS_SIZE)
1
Error: Variable 'mpi_status_size' cannot appear in the expression at (1)
testsendr.f90:6.36:
INTEGER :: status(MPI_STATUS_SIZE)
1
Error: The module or main program array 'status' at (1) must have constant shape
Any thoughts. It is a really simple program.
Thanks
I think that your problems arise from a mis-ordering of statements in your program. Before the line
include mpif.h
you have declared a variable which makes use of one of the constants defined in that file, in the line
INTEGER :: status(MPI_STATUS_SIZE)
Either move the include statement to immediately after the IMPLICIT NONE or, better, drop the include altogether and insert USE MPI prior to the implicit statement and sort out the linking of the revised code.
Related
I have MPI ranks split up to calculate different parts an an array, then I want to put/send those slices onto a different rank that doesn't participate in the calculation. That rank is the master of a new communicator set up to do other things with the array (averaging, IO, etc). I got it to work with MPI_isend and MPI_irecv, and now I want to try MPI_Put.
use mpi_f08
use iso_c_binding
implicit none
integer, parameter :: n=10, gps = 18, pes=12, dpes = 6
integer :: main=pes, d=dpes
integer :: diag_master
integer :: global_size, global_rank, diag_size, diag_rank
type(MPI_comm),allocatable :: diag_comm
integer :: pelist_diag
TYPE(MPI_Win) :: win
integer :: ierr, i, j
type(MPI_COMM) :: comm, mycomm
integer :: gsz, grk
integer :: lsz, lrk
integer(KIND=MPI_ADDRESS_KIND) :: local_group
logical :: local_flag
integer :: color,key
!!! THIS IS THE ARRAY
real, dimension(n,pes) :: r
!!!
logical :: on_dpes = .false.
logical,allocatable,dimension(:) :: dpes_list ! true if on dpes list
integer :: comm_manager
integer :: dmg
integer(KIND=MPI_ADDRESS_KIND) :: buff_size !< the size of a variable type
integer(kind=MPI_ADDRESS_KIND) :: displacement
integer :: disp_size
integer :: loc_base
integer, pointer :: fptr
!!!!!!!! THIS ALL WORKS BEGIN !!!!!!!!
comm=MPI_COMM_WORLD
call MPI_INIT(ierr)
call MPI_COMM_SIZE(COMM, gsz, ierr)
call MPI_COMM_RANK(COMM, grk, ierr)
allocate(dpes_list(gsz))
! write (6,*) "I am ",grk," of ",gsz
!> Find the group
call MPI_COMM_GET_ATTR(COMM,MPI_APPNUM,local_group,local_flag,ierr)
!> Split a new communicator as mycom
color = int(local_group)
key = 0
call MPI_COMM_SPLIT(COMM, color, key, mycomm, ierr)
!> Get information about the split communicators
call mpi_comm_size(mycomm,lsz,ierr)
call mpi_comm_rank(mycomm,lrk,ierr)
!> Create data on the main communicator
if (lsz == pes) then
comm_manager = main
on_dpes = .false.
r = 0.0
if (mod(lrk,2) == 0) then
c_loop: do concurrent (i=1:n)
r(i,lrk+1) = sin(real(i))+real(i)
enddo c_loop
else
r(:,lrk+1) = 10.0-dble(lrk)
endif
if (lsz == dpes) then
diag_size = lsz
diag_rank = lrk
comm_manager = d
on_dpes = .true.
diag_comm = mycomm
if (lrk==0) then
dmg = grk
endif
endif
call MPI_ALLGATHER(on_dpes,1,MPI_LOGICAL, &
dpes_list,gsz,MPI_LOGICAL, MPI_COMM_WORLD, ierr)
!> Get the master of dpes
do i=1,gsz
if (dpes_list(i)) then
dmg = i-1
exit
endif
enddo
diag_master = dmg
diag_global_master = dmg
!!!!!!!! THIS ALL WORKS END !!!!!!!!
!! At this point, the ranks that participate in the calculation
!! have values in r(i,lrk+1) where lrk is their rank
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!! THIS IS WHERE THINGS GO WRONG? !!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
disp_size = storage_size(r)
buff_size = disp_size*size(r)
call c_f_pointer(c_loc(r(1,1)),fptr)
loc_base = fptr
nullify(fptr)
write (6,*) loc_base, grk
call MPI_Win_create(loc_base,buff_size,disp_size,MPI_INFO_NULL,&
mpi_comm_world,win,ierr)
call MPI_Win_Fence(0,win,ierr)
displacement = loc_base + disp_size *buff_size
! if (.not.allocated(diag_comm)) then
if (grk == 11) then
call MPI_Put(r(:,global_rank+1),size(r,1),MPI_FLOAT,&
diag_master,displacement,size(r,1), MPI_FLOAT, win ,ierr)
endif
call MPI_Win_Fence(0,win,ierr)
CALL MPI_WIN_FREE(win, ierr)
call MPI_FINALIZE(ierr)
I have ! if (.not.allocated(diag_comm)) then commented out because I tried to do this with all of the ranks that calculate r, but I got the same result.
I am compiling with mpiifort -O0 -fpe0 -init=snan,arrays -no-wrap-margin -traceback -stand f18 and run with mpirun -n 12 ./$#.x : -n 6 ./$#.x in my Makefile. The version of mpiifort I am using is
> mpiifort -v
mpiifort for the Intel(R) MPI Library 2019 Update 2 for Linux*
Copyright 2003-2019, Intel Corporation.
ifort version 19.0.2.187
The output (write (6,*) loc_base, grk)is strange.
1072411986 0
0 1
0 2
0 3
0 4
0 5
0 6
0 7
0 8
0 9
0 10
0 11
2142952877 12
2142952877 13
2142952877 14
2142952877 15
2142952877 16
2142952877 17
Rank 12-17 are the ranks that don't participate in "calculating r", but I'm not sure why c_loc(r(1,1)) is different for these ranks. Also, it is different for rank 0.
My actual questions are
1) How do I calculate the displacement variable? Am I doing it correctly? Is it supposed to be different between ranks because it will be in this case?
2) Why is c_loc(r(1,1)) different for the ranks 12-17? Does it have anything to do with the fact that this is a SPMD program? Why is it different for rank 0?
3) Can I do the one way communication with all of the ranks instead of just one? I had each rank call mpi_isend, and then i just called mpi_irecv in a loop through all of the ranks sending when I did this the other way. Can I do something similar with MPI_Put? Should I be using MPI_Get? Something else?
4) How do I get this to work? This is just an educational example for myself, and what I actually need to do is much more complicated.
I can answer item 2, at least. You have:
call c_f_pointer(c_loc(r(1,1)),fptr)
loc_base = fptr
where loc_base is declared integer. You seem to be assuming that loc_base is some sort of address, but it is not. In Fortran, intrinsic assignment from a pointer assigns the value of the target, not the location of the target. So you're effectively doing a TRANSFER of the REAL values of r to loc_base - probably not what you want.
In this thread it has been explained in two ways how to pass messages using MPI with declared data types. I have a data structure with allocatables
type t_example
real, allocatable :: x(:), y(:), z(:)
end type
For maintability of the code would the easiest thing not be to use MPI_TYPE_CONTIGUOUS as follows
! -- declare
type(t_example) :: p1
type(MPI_DATATYPE) :: mpi_dtexample
(...)
call MPI_TYPE_CONTIGUOUS(sizeof(p1), MPI_BYTE, mpi_dtexample, ierr);
call MPI_TYPE_COMMIT(mpi_dtexample, ierr)
Following this I can simply use the send/recv with mpi_dtexample as being the data type.
I cannot come to my mind when it becomes more sensible to use the mpi_type_create_struct, as this would require you to explicitly tell the sequence of the declared type, with the data type and their corresponding sizes.
YES, the MPI_TYPE_CONTIGUOUS approach assumes that the declared type is contiguous and I would not be able to use this approach if I wanted to pass certain strided elements of the declared type.
Is there else anything I should raise my alarm bells on when using MPI_TYPE_CONTIGUOUS
A FULL EXAMPLE
Running with 2 ranks only.
module md_
use mpi_f08
integer numtasks, rank, tag, i, ierr
type(mpi_status) stat
type T_PART
real, allocatable :: x(:), y(:), z(:)
end type
contains
end module
program struct
use md_
implicit none
type(t_part) :: test_
type(mpi_datatype) :: mpidt
integer :: sz, szz
call MPI_INIT(ierr)
call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)
call MPI_COMM_SIZE(MPI_COMM_WORLD, numtasks, ierr)
tag = 1
szz= 10
allocate(test_% x(szz),test_% y(szz), test_% z(szz) )
sz = sizeof(test_)
call MPI_Type_contiguous(sz, MPI_BYTE ,mpidt, ierr)
call MPI_TYPE_COMMIT(mpidt, ierr)
if (rank .eq. 0) then
do i=1,szz
test_%x(i) = i*1+mod(i,3)
test_%y(i) = i*2+mod(i,3)
test_%z(i) = i*3+mod(i,3)
end do
call MPI_SEND(test_, 1, mpidt, 1, tag, &
MPI_COMM_WORLD, ierr)
else
call MPI_RECV(test_, 1, mpidt, 0, tag, &
MPI_COMM_WORLD, stat, ierr)
endif
print *, 'rank= ',rank,' test_% x= ', test_%z(1) ! seg faults for rank 2
call mpi_barrier(MPI_COMM_WORLD, ierr)
! free datatype when done using it
call MPI_TYPE_FREE(mpidt, ierr)
call MPI_FINALIZE(ierr)
end
I'm trying to send/recv a derived datatype with allocatable arrays. Currently, I managed to follow the suggestion in MPI derived datatype for dynamically allocated structs with dynamically allocated member. With that, my information is passed correctly. However, when I was profiling with tau, the memory allocated on heap wasn't freed and resulted in memory leak.
I have tested many times by commenting on/off different lines of code. The memory leak disappear as long as I comment off the MPI_TYPE_CREATE_STRUCT function.
I also pasted the code in the post into my code but problem still persists.
The compilers I tried are openmpi-4.0.0, 3.1.0 and impi 18.0.2, 18.0.0
Here is the a simple code I tested on
Here is the memory leak version
Program memory_leak
implicit none
include "mpif.h"
TYPE Struct
INTEGER :: N
DOUBLE PRECISION :: A
DOUBLE PRECISION ,ALLOCATABLE :: B(:)
END TYPE Struct
TYPE(Struct) :: Structs(2)
integer :: i
integer :: Types(3)
integer :: Blocks(3)
integer :: Elem_Type(2), TwoElem_Type,IError
integer(kind=MPI_ADDRESS_KIND) :: POS_(3)
integer(kind=MPI_ADDRESS_KIND) :: Offsets(3)
ALLOCATE(Structs(1)%B(10))
ALLOCATE(Structs(2)%B(20))
CALL MPI_INIT(IError)
! (1) Create a separate structure datatype for each record
DO i=1,2
CALL MPI_GET_ADDRESS(Structs(i)%N, POS_(1), IError)
CALL MPI_GET_ADDRESS(Structs(i)%A, POS_(2), IError)
CALL MPI_GET_ADDRESS(Structs(i)%B(1), POS_(3), IError)
Offsets = POS_ - POS_(1)
Types(1) = MPI_INTEGER
Types(2) = MPI_DOUBLE_PRECISION
Types(3) = MPI_DOUBLE_PRECISION
Blocks(1) = 1
Blocks(2) = 1
Blocks(3) = i * 10
CALL MPI_TYPE_CREATE_STRUCT(3, Blocks, Offsets, Types, Elem_Type(i), IError)
END DO
! (2) Create a structure of structures that describes the whole array
CALL MPI_GET_ADDRESS(Structs(1)%N, POS_(1), IError)
CALL MPI_GET_ADDRESS(Structs(2)%N, POS_(2), IError)
Offsets = POS_ - POS_(1)
Types(1) = Elem_Type(1)
Types(2) = Elem_Type(2)
Blocks(1) = 1
Blocks(2) = 1
CALL MPI_TYPE_CREATE_STRUCT(2, Blocks, Offsets, Types, TwoElem_Type, IError)
CALL MPI_TYPE_COMMIT(TwoElem_Type, IError)
! (2.1) Free the intermediate datatypes
DO i=1,2
CALL MPI_TYPE_FREE(Elem_Type(i), IError)
END DO
CALL MPI_TYPE_FREE(TwoElem_Type, IError)
print *, "end"
CALL MPI_FINALIZE(IError)
end program memory_leak
Memory leak using tau
\
Here is the Leak free version
Program memory_leak
implicit none
include "mpif.h"
TYPE Struct
INTEGER :: N
DOUBLE PRECISION :: A
DOUBLE PRECISION ,ALLOCATABLE :: B(:)
END TYPE Struct
TYPE(Struct) :: Structs(2)
integer :: i
integer :: Types(3)
integer :: Blocks(3)
integer :: Elem_Type(2), TwoElem_Type,IError
integer(kind=MPI_ADDRESS_KIND) :: POS_(3)
integer(kind=MPI_ADDRESS_KIND) :: Offsets(3)
ALLOCATE(Structs(1)%B(10))
ALLOCATE(Structs(2)%B(20))
CALL MPI_INIT(IError)
! (1) Create a separate structure datatype for each record
DO i=1,2
CALL MPI_GET_ADDRESS(Structs(i)%N, POS_(1), IError)
CALL MPI_GET_ADDRESS(Structs(i)%A, POS_(2), IError)
CALL MPI_GET_ADDRESS(Structs(i)%B(1), POS_(3), IError)
Offsets = POS_ - POS_(1)
Types(1) = MPI_INTEGER
Types(2) = MPI_DOUBLE_PRECISION
Types(3) = MPI_DOUBLE_PRECISION
Blocks(1) = 1
Blocks(2) = 1
Blocks(3) = i * 10
! CALL MPI_TYPE_CREATE_STRUCT(3, Blocks, Offsets, Types, Elem_Type(i), IError)
END DO
! (2) Create a structure of structures that describes the whole array
CALL MPI_GET_ADDRESS(Structs(1)%N, POS_(1), IError)
CALL MPI_GET_ADDRESS(Structs(2)%N, POS_(2), IError)
Offsets = POS_ - POS_(1)
Types(1) = Elem_Type(1)
Types(2) = Elem_Type(2)
Blocks(1) = 1
Blocks(2) = 1
! CALL MPI_TYPE_CREATE_STRUCT(2, Blocks, Offsets, Types, TwoElem_Type, IError)
! CALL MPI_TYPE_COMMIT(TwoElem_Type, IError)
! ! (2.1) Free the intermediate datatypes
! DO i=1,2
! CALL MPI_TYPE_FREE(Elem_Type(i), IError)
! END DO
!CALL MPI_TYPE_FREE(TwoElem_Type, IError)
print *, "end"
CALL MPI_FINALIZE(IError)
end program memory_leak
Solved. The problem lies in other part of code where I used the pack function on allocatable array. When you use pack, the array will result in memory lost since pointer is gone but the array is not deallocated
This is probably something really simple but I'm getting the error when compiling my little Fortran program. (The file is .f90) Is this something to do with fixed versus free line length? That seems to be all I could glean from a google search.
Here's the program:
program array
integer :: k, n, i, j, h, f, AllocateStatus
real*8, dimension(:, :, :), allocatable :: a
character, parameter :: "fname"
k = 5
n = 5
h = 1
allocate(a(n,k,h), stat = AllocateStatus)
if (AllocateStatus /= 0) stop "*** Not enough memory ***"
a(1,:,:) = 5
a(2,:,:) = 6
call writeArray(7,a,"testOutput")
deallocate(a)
end program array
subroutine writeArray(f,array,fname)
implicit none
integer :: f, i, j, k, n
character, parameter :: "fname"
real*8, dimension(:, :, :), allocatable :: array
open(unit = f, file="fname")
do, i=1,n
do, j=1,k
write(7,"(F5.2)") array(i,j,:)
if (j==k) write(7,"(A1)") "X"
enddo
enddo
!write(7,"(I5)") size(a)
close(f)
end subroutine writeArray
And the errors:
test.f90:4.29:
character, parameter :: "fname"
1
Error: Invalid character in name at (1)
test.f90:24.26:
character, parameter :: "fname"
1
Error: Invalid character in name at (1)
test.f90:21.35:
subroutine writeArray(f,array,fname)
1
Error: Symbol 'fname' at (1) has no IMPLICIT type
You cannot use quotation marks to denote an initialization. In your subroutine, you should have
CHARACTER(LEN=*) :: fname
in place of what you have there. You probably do not need the PARAMETER statement with the character declaration. The initialization of fname does not appear to be needed in the main program.
Another pair of things I noted in your code: (1) you don't need to declare array and ALLOCATABLE and (2) you ought to start file UNITs at values >= 10 because the single-digit numbers are occasionally associated with (reserved for?) standard out.
Another suggestion is that you should either put your writeArray subroutine in its own MODULE and USE it, or write the program as
PROGRAM Main
...
CONTAINS
SUBROUTINE writeArray
...
END SUBROUTINE
END PROGRAM
With either method, you will catch inconsistencies in the arguments. Not only that, you will also be able to use the variables n and k without issue.
I totally agree with #kyle. So in heeding those suggestions I would also declare the intent of the variables to the subroutine writeArray. Thus the program would be along the lines of:
program array
integer :: k, n, h, AllocateStatus
double precision, dimension(:, :, :), allocatable :: a
character(len=1024) :: fname
fname = "testOutput"
k = 5
n = 5
h = 1
allocate(a(n,k,h), stat = AllocateStatus)
if (AllocateStatus /= 0) stop "*** Not enough memory ***"
a(1,:,:) = 5
a(2,:,:) = 6
call writeArray(7,a,fname)
deallocate(a)
contains
subroutine writeArray(f,array,fname)
implicit none
integer, intent(in) :: f
integer :: i, j, k
character(len=*), intent(in) :: fname
double precision, dimension(:, :, :), intent(in) :: array
open(unit = f, file=fname)
i = size(array, 1)
k = size(array, 2)
do, i=1,n
do, j=1,k
write(7,"(F5.2)") array(i,j,:)..
if (j==k) write(7,"(A1)") "X"
enddo
enddo
!write(7,"(I5)") size(a)
close(f)
end subroutine writeArray
end program array
Also I don't like using real*8, I tend to either declare it as either real(kind=8) or double precision.
Lastly, depending on the compiler you use (and hence it's flags), Try to always be as pedantic and chatty as possible. For gfortran I typically use the options -Wall -pedantic when compiling.
Additional comments:
You definitely don't want parameter in the declaration of fname -- that designates that the "variable" is constant, which is inconsistent with a dummy argument.
You could declare the arguments as:
integer, intent (in) :: f
character (len=*), intent (in) :: fname
real*8, dimension(:, :, :), intent (in) :: array
The reason that you don't need to declare array as allocatable in the subroutine is that you don't change its allocation in the subroutine. You can obtain the values of n and k with the size intrinsic and so don't need to pass them as arguments.
My problem is that I don't know how to call subroutines when I use mpi scheme in Fortran.
I have written this small code named TRY.f90 in which there is a subroutine named CONCENTRATION.f90. How should I change CONCENTRATION.f90 in order to make the code works?
PROGRAM TRY
USE MPI
integer status(mpi_status_size)
INTEGER I, J, K, II, IERR, MY_ID, NUM_PROCS, PSP
INTEGER , PARAMETER :: GRIDX =64, GRIDY=64
REAL , DIMENSION(gridx,gridy) :: PSI
PSI=0
PRINT*, 'VARIABLE'
CALL MPI_INIT(IERR)
CALL MPI_COMM_RANK(MPI_COMM_WORLD,MY_ID,IERR)
CALL MPI_COMM_SIZE(MPI_COMM_WORLD,NUM_PROCS,IERR)
CALL CONCENTRATION(GRIDX, GRIDY, NUM_PROCS, MY_ID , PSI)
IF (MY_ID .NE. 0) THEN
CALL mpi_send( PSI(1+MY_ID*GRIDX/NUM_PROCS:(MY_ID+1)*GRIDX/NUM_PROCS:1,1:GRIDY:1),&
(GRIDX/NUM_PROCS)*GRIDY,mpi_real, 0,10,mpi_comm_world,ierr)
END IF
IF (MY_ID .EQ. 0) THEN
DO II=1,NUM_PROCS-1
CALL mpi_recv(PSI(1+II*GRIDX/NUM_PROCS:(II+1)*GRIDX/NUM_PROCS:1,1:GRIDY:1),&
(GRIDX/NUM_PROCS)*GRIDY,mpi_real, &
II,10,mpi_comm_world,status,ierr)
END DO
END IF
CALL MPI_FINALIZE(IERR)
END PROGRAM TRY
I am using a subroutine named CONCENTRATION.f90 which is:
SUBROUTINE CONCENTRATION(GRIDX, GRIDY, NUM_PROCS, MY_ID , PSI)
implicit none
INTEGER*8, INTENT(IN) :: GRIDX, GRIDY
INTEGER , INTENT(IN) :: NUM_PROCS, MY_ID
REAL*8 , DIMENSION(GRIDX,GRIDY), INTENT(OUT) :: PSI
INTEGER*8 I, J
DO I=1+MY_ID*GRIDX/NUM_PROCS, (MY_ID+1)*GRIDX/NUM_PROCS
DO J=1,GRIDY
PSI(I,J)=2.0
END DO
END DO
END SUBROUTINE CONCENTRATION
The code currently gives me error since I think I should have made some changes on the subroutine CONCENTRATION.f90. Or I should also change the way I call the subroutine.
Could you please tell me what are those changes? Thanks for your helps in advance
Your program segfaults because of type mismatch. In the main program you have declared PSI as an array of REAL:
REAL , DIMENSION(gridx,gridy) :: PSI
while in the CONCENTRATION subroutine you use another type of REAL*8:
REAL*8 , DIMENSION(GRIDX,GRIDY), INTENT(OUT) :: PSI
By default REAL is 4 bytes long while REAL*8 (or DOUBLE PRECISION or REAL(KIND=8)) is 8 bytes long. So you are giving to CONCENTRATION an array that is 2 times smaller than what it believes to be and all ranks from NUM_PROCS/2 onwards write past the end of the PSI array and thus cause segfaults. If you run with one process only, then even rank 0 will segfault.
You should also read about MPI collective operations. MPI_GATHER and MPI_GATHERV do exactly what you are trying to achieve whith multiple sends and receives here.
The only change would be to declare concentration as reentrant. That could be the default for Fortran 90. (The bulk of my experience is F77, and reentrant is not the default there.)