Passing a subarray to a Fortran subroutine - fortran

I have created a Fortran array, say
real, dimension(4, 4) :: A
Being a matrix
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
And I want to pass it to a subroutine in form
call MySoubroutine(A(2,2))
And inside my subroutine get this array and modify some of its elements
real, dimension(:), intent(inout) : A
A(1,1) = 91
A(1, 2) = 92
A(2, 1) = 93
A(2, 2) = 94
So after calling the function in my main program the array A is
1 2 3 4
5 91 92 8
9 93 94 12
13 14 15 16
What is the best an most optimum way to achieve such a behaviour?
In detail my questions are:
Is there a better way of using a subarray inside the subroutine?
How shall I declare the array in the subroutine? I want just to pass a pointer to the first element, so may not know the dimension of the subarray.

Related

Allocated matrix and input file

I have an input data file storing two columns (first column contains the names of the variables and the second column contains their values). I am trying to read this input file through my FORTRAN script and to print on the screen the variables I've just created.
Here are the input file, the script, as well as the terminal output displayed on the terminal at the execution:
input file:
a 7 2 4
b 150
vec1 1 2 3
vec2 4 5 6
c 56
script
program main
implicit none
character(16) :: cinput
integer :: a0,a1,a2,b0,c0,i,j
integer,dimension(:,:),allocatable :: gfd
open(9, file='inputdata.dat')
read(9,*) cinput,a0,a1,a2
read(9,*) cinput,b0
allocate(gfd(3,2))
read(9,*) cinput,gfd(:,1)
read(9,*) cinput,gfd(:,2)
read(9,*) cinput,c0
close(9)
write(*,*) 'a0=', a0,'a1=', a1,'a2=', a2,'b0=', b0,'c0=', c0
do j=1,2
do i=1,3
write(*,*) gfd(i,j)
enddo
enddo
end program main
Output on the terminal
a0 = 7, a1 = 2, a2 = 4, b0 = 150, c0 = 56
1
2
3
4
5
6
Now, this is good, but would there be a way to assign the values to the variable "gfd" without having to specify the size of the array in "allocate"? I could then modify the input file with a longer/smaller array, without having to modify the script when I allocate the variable "gfd".
Thank you for your support if you can help me!
ms518
EDIT: thanks for your answer, this procedure is working and it is now possible to work with various array sizes in the input file without having to modify the fortran script. Below are the modifications in inputfile, script and the result obtained.
input file:
size 5 2
a 7 2 4
b 150
vec1 1 2 3 4 5
vec2 6 7 8 9 10
c 56
script
program main
implicit none
character(16) :: cinput
integer :: a0,a1,a2,b0,c0,i,j, rows, cols
integer,dimension(:,:),allocatable :: gfd
open(9, file='inputdata.dat')
read(9,*) cinput,rows,cols
read(9,*) cinput,a0,a1,a2
read(9,*) cinput,b0
allocate(gfd(rows,cols))
read(9,*) cinput,gfd(:,1)
read(9,*) cinput,gfd(:,2)
read(9,*) cinput,c0
close(9)
write(*,*) 'a0=', a0,'a1=', a1,'a2=', a2,'b0=', b0,'c0=', c0
do j=1,cols
do i=1,rows
write(*,*) gfd(i,j)
enddo
enddo
end program main
Output on the terminal
a0 = 7, a1 = 2, a2 = 4, b0 = 150, c0 = 56
1
2
3
4
5
6
7
8
9
10
The best way to specify the size of the array would be to include its dimensions in the input file, read them, allocate the array, then read the array.
If you need assistance programming this, modify your question. You could, if you want, post your revised code to answer your own question.

Why these two MPI-IO code are not working the same way?

I am learning MPI-IO and following a tutorial (PDF download here).
For one exercise, the correct code is:
Program MPI_IOTEST
Use MPI
Implicit None
Integer :: wsize,wrank
Integer :: ierror
Integer :: fh,offset
Call MPI_Init(ierror)
Call MPI_Comm_rank(MPI_COMM_WORLD,wrank,ierror)
Call MPI_Comm_size(MPI_COMM_WORLD,wsize,ierror)
offset=4*wrank; ! because 4 bytes is one signed int
! --- open the MPI files using a collective call
Call MPI_File_Open(MPI_COMM_WORLD,'test.dat',MPI_MODE_RDWR+MPI_MODE_CREATE,MPI_INFO_NULL,fh,ierror);
Write(*,*)'rank',wrank
Call MPI_FILE_WRITE_AT(fh, offset, wrank,1,MPI_INTEGER,mpi_status_ignore,ierror);
Call MPI_File_close(fh,ierror)
Call MPI_Finalize(ierror)
End Program MPI_IOTEST
Then you just build and run it as 24 MPI tasks.
Then for validation, simply do
od -i test/dat
You will get the result exactly the same on the tutorial, which is given below.
0000000 0 1 2 3
0000020 4 5 6 7
0000040 8 9 10 11
0000060 12 13 14 15
0000100 16 17 18 19
0000120 20 21 22 23
0000140
But if I change 1 to num:
Call MPI_FILE_WRITE_AT(fh, offset, wrank,1,MPI_INTEGER,mpi_status_ignore,ierror);
into
Call MPI_FILE_WRITE_AT(fh, offset, wrank,num,MPI_INTEGER,mpi_status_ignore,ierror);
and before that define
integer :: num
num=1
After rm test.dat, then re-build the file and run it, you will get:
0000000 0 0 0 0
*
Your error is not actually in the specification or use of num but rather in the specification of offset.
If you read the man-page of MPI_File_write_at, you have to specify the offset as MPI_Offset kind.
So if you change your program to use:
integer(kind=MPI_OFFSET_KIND) :: offset
It works fine.
Did you not notice the size of the test.dat file generated?

Assignment of Allocatables of Different Shapes in Fortran [duplicate]

This question already has an answer here:
Allocatable array valued function. gfortran vs ifort
(1 answer)
Closed 5 years ago.
Please look at the following code:
program test
implicit none
integer, allocatable :: v1(:, :)
integer, allocatable :: v2(:, :)
allocate(v1(2, 4))
allocate(v2(2, 3))
v1(:, :) = reshape([11, 12, 13, 14, 15, 16, 17, 18], [2, 4])
v2(:, :) = reshape([21, 22, 23, 24, 25, 26], [2, 3])
print *, v1
print *, 'shape(v1): ', shape(v1)
print *
print *, v2
print *, 'shape(v2): ', shape(v2)
print *
v2 = v1
print *, v1
print *, 'shape(v1): ', shape(v1)
print *
print *, v2
print *, 'shape(v2): ', shape(v2)
print *
deallocate(v1)
deallocate(v2)
end program test
When I compile it with gfortran, I get the following output:
11 12 13 14 15 16 17 18
shape(v1): 2 4
21 22 23 24 25 26
shape(v2): 2 3
11 12 13 14 15 16 17 18
shape(v1): 2 4
11 12 13 14 15 16 17 18
shape(v2): 2 4
When I compile it with ifort, I get the following output:
11 12 13 14 15 16 17 18
shape(v1): 2 4
21 22 23 24 25 26
shape(v2): 2 3
11 12 13 14 15 16 17 18
shape(v1): 2 4
11 12 13 14 15 16
shape(v2): 2 3
which one is reliable? is there a bug in ifort or in gfortran?
gfortran version 4.8.1
ifort version 14.0.0
By default, ifort before version 17 does not use Fortran 2003 semantics for reallocating an allocatable type on the left side of an assignment. The ifort 15 manual has this to say (for the default norealloc-lhs assumption):
The compiler uses Standard Fortran rules when interpreting assignment statements. The left-hand side is assumed to be allocated with the correct shape to hold the right-hand side. If it is not, incorrect behavior will occur.
To allow the left side of the assignment to be reallocated to the proper shape, compile with the option -assume realloc-lhs. Alternatively you can compile with -standard-semantics to make all assumptions default to compliance with the Fortran 2003 standard, with some Fortran 2008 features.

Fortran 90 rank mismatch in attempting to extract a vector from an array

In my Fortran 90 code, I have created the following array (called array) of integers:
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
I wish to extract the first column, and save it in a four-element vector called time. I have the following code:
PROGRAM test
IMPLICIT NONE
INTEGER, PARAMETER :: numrows=4, numcols=10
INTEGER :: i, j, k
INTEGER, DIMENSION(:,:), ALLOCATABLE :: array, time
ALLOCATE(array(numrows,numcols))
ALLOCATE(time(numrows))
k=1
DO i=1,numrows
DO j=1,numcols
array(i,j)=k
k=k+1
END DO
END DO
DO i=1,numrows
WRITE(*,"(100(3X,I3))") (array(i,j), j=1,numcols)
END DO
time=array(:,1)
END PROGRAM test
But, I get the following error message (when compiling in gfortran):
test.f90:8.15:
ALLOCATE(time(numrows))
1
Error: Rank mismatch in array reference at (1) (1/2)
test.f90:22.2:
time=array(:,1)
1
Error: Incompatible ranks 2 and 1 in assignment at (1)
Why is this the case? The error message seems to suggest that the array array(:,1) is of rank 2, not rank 1. Is there any way that I can convert array(:,1) to an array of rank 1? Do I need to use RESHAPE to somehow squeeze the array? Or is the problem that by using array(:,1), I am specifying a column vector rather than a row vector? Thank you very much for your time.
You are specifying a rank-2 allocatable array called time:
INTEGER, DIMENSION(:,:), ALLOCATABLE :: array, time
and then attempting to allocate it as a rank-1 array:
ALLOCATE(time(numrows))
-- don't do that. This works perfectly fine:
PROGRAM test
IMPLICIT NONE
INTEGER, PARAMETER :: numrows=4, numcols=10
INTEGER :: i, j, k
INTEGER, DIMENSION(:,:), ALLOCATABLE :: array
INTEGER, DIMENSION(:), ALLOCATABLE :: time
ALLOCATE(array(numrows,numcols))
ALLOCATE(time(numrows))
k=1
DO i=1,numrows
DO j=1,numcols
array(i,j)=k
k=k+1
END DO
END DO
DO i=1,numrows
WRITE(*,"(100(3X,I3))") (array(i,j), j=1,numcols)
END DO
time=array(:,1)
END PROGRAM test

generate a sequence array in fortran

Is there an intrinsic in Fortran that generates an array containing a sequence of numbers from a to b, similar to python's range()
>>> range(1,5)
[1, 2, 3, 4]
>>> range(6,10)
[6, 7, 8, 9]
?
No, there isn't.
You can, however, initialize an array with a constructor that does the same thing,
program arraycons
implicit none
integer :: i
real :: a(10) = (/(i, i=2,20, 2)/)
print *, a
end program arraycons
If you need to support floats, here is a Fortran subroutine similar to linspace in NumPy and MATLAB.
! Generates evenly spaced numbers from `from` to `to` (inclusive).
!
! Inputs:
! -------
!
! from, to : the lower and upper boundaries of the numbers to generate
!
! Outputs:
! -------
!
! array : Array of evenly spaced numbers
!
subroutine linspace(from, to, array)
real(dp), intent(in) :: from, to
real(dp), intent(out) :: array(:)
real(dp) :: range
integer :: n, i
n = size(array)
range = to - from
if (n == 0) return
if (n == 1) then
array(1) = from
return
end if
do i=1, n
array(i) = from + range * (i - 1) / (n - 1)
end do
end subroutine
Usage:
real(dp) :: array(5)
call linspace(from=0._dp, to=1._dp, array=array)
Outputs the array
[0., 0.25, 0.5, 0.75, 1.]
Here dp is
integer, parameter :: dp = selected_real_kind(p = 15, r = 307) ! Double precision
It is possible to create a function that reproduces precisely the functionality of range in python:
module mod_python_utils
contains
pure function range(n1,n2,dn_)
integer, intent(in) :: n1,n2
integer, optional, intent(in) :: dn_
integer, allocatable :: range(:)
integer ::dn
dn=1; if(present(dn_))dn=dn_
if(dn<=0)then
allocate(range(0))
else
allocate(range(1+(n2-n1)/dn))
range=[(i,i=n1,n2,dn)]
endif
end function range
end module mod_python_utils
program testRange
use mod_python_utils
implicit none
integer, allocatable :: v(:)
v=range(51,70)
print"(*(i0,x))",v
v=range(-3,30,2)
print"(*(i0,x))",v
print"(*(i0,x))",range(1,100,3)
print"(*(i0,x))",range(1,100,-3)
end program testRange
The output of the above is
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
-3 -1 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29
1 4 7 10 13 16 19 22 25 28 31 34 37 40 43 46 49 52 55 58 61 64 67 70 73 76 79 82 85 88 91 94 97 100
Notice that :
the last line is empty: Fortran treats graciously zero-length arrays.
allocated variables get automatically deallocated once out of scope.