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.
Related
I want to double a number till 256, and when the program gets 256 make stop but the program that I created not giving me a double from 1 till 256
`````````````````````````````````````````````````````````````````
Fortran code
program exercise1
implicit none
real, external :: f
real :: h,a,b,prod,integration,summ,p
integer :: i,j,n
print*, 'Welcome, This program using Composite trapezoid method to calculate the integer'
print*, ' -------- '
prod=1
summ=0
print*,'Number of intervals ',' The integer value'
do i=1,258
n=(prod*i*i) ! Getting number of interval till 256
a=-1 ! The Lower limit of the integration
b=1 ! The Upper limit of the integration
h=(b-a)/n ! Calculate the delta X
p=(h/2.)*(f(a)+f(b)) ! Here as we have the whole equation is (h/2)*[f(a)+f(b)+2*sum(Xi) ! So we calculate the first part (h/2)*[f(a)+f(b) and then calculate the anoter part
summ=summ+h*f(a+i*h) !h/2 *2* sum[f(Xi)
integration = p+summ !Here the sum the both parts
if(n == 256) then !put a limit for the number of interval
Stop
end if
print*,n,' -', integration
enddo
end
real function f(x) !the function of the integer
f=sin(x+1)
end`
``````````````````````
the output
``````````````````
1 2.72789216
4 2.46665454
9 2.47777867
16 2.49350500
25 2.50419927
36 2.51126313
49 2.51606560
64 2.51944780
81 2.52190781
100 2.52374840
121 2.52515912
144 2.52626276
169 2.52714205
196 2.52785373
225 2.52843738
I think you want powers of: 2**i not squares: i**2. For example:
do i = 0,8
print*, 2**i
enddo
gives
1
2
4
8
16
32
64
128
256
I was originally voting to close as a typo, but your code maybe indeed does something different.
Your codes makes n to be a sequence if integer squares. 1^2, 2^2, 3^2 ... 16^2=256
But you wanted doubling the number, in that case just multiply n by 2.
n = 1
do i = 1, 10
do what you need
print *, n
n = n * 2
end do
I'm very new to using Fortran, and I can't seem to figure out why this subroutine is getting stuck in an infinite loop. Here's the code for said DO loop:
SUBROUTINE FILLARRAY(K, N)
REAL X, Y
INTEGER XPOS, YPOS
INTEGER K(N,N)
DO 10 I = 1, 100
15 CALL RANDOM_NUMBER(X)
CALL RANDOM_NUMBER(Y)
XPOS = 20 * X + 1.0
YPOS = 20 * Y + 1.0
PRINT *, XPOS
PRINT *, YPOS
IF(K(XPOS, YPOS).NE.1) THEN
K(XPOS,YPOS) = 1
END IF
IF (K(XPOS, YPOS).EQ.1) THEN
GOTO 15
END IF
10 CONTINUE
RETURN
END
I am basically trying to fill a 20 x 20 array randomly with the value 1.
I was also wondering if there is a way to forego using END IF that anyone knows about! Thank you!
The array will eventually all be set to 1 leading to an infinte loop with GOTO 15.
Try this code instead:
IF(K(XPOS, YPOS).NE.1) THEN
K(XPOS,YPOS) = 1
ELSE
GOTO 15
END IF
This method is horribly inefficient. I'd do it something like the below. Note i've filled with i rather than 1, partially to show the random order of filling, partially to act as a check I haven't screwed up, as each number should appear exactly once.
ian#eris:~/work/stack$ cat random_fill.f90
Program random_fill
Implicit None
Integer, Parameter :: n = 5
Integer, Dimension( 1:n, 1:n ) :: K
Call fillarray( k, n )
Write( *, '( 5( 5( i2, 1x ) / ) )' ) K
Contains
Subroutine fillarray( k, n )
Implicit None
Integer , Intent( In ) :: n
Integer, Dimension( 1:n, 1:n ), Intent( Out ) :: K
Integer, Dimension( : ), Allocatable :: index_list
Real :: rand
Integer :: val, x, y
Integer :: i
index_list = [ ( i, i = 0, n * n - 1 ) ]
Do i = 1, n * n
Call Random_number( rand )
val = 1 + Int( rand * Size( index_list ) )
x = 1 + index_list( val ) / n
y = 1 + Mod( index_list( val ), n )
K( x, y ) = i
index_list = [ index_list( :val - 1 ), index_list( val + 1: ) ]
End Do
End Subroutine fillarray
End Program random_fill
ian#eris:~/work/stack$ gfortran -O -Wall -Wextra -pedantic -fcheck=all -std=f2008 random_fill.f90
ian#eris:~/work/stack$ ./a.out
11 8 14 24 16
19 23 25 15 3
21 20 5 7 18
6 17 22 12 9
2 4 1 10 13
ian#eris:~/work/stack$ ./a.out
24 15 7 22 25
8 17 10 1 14
9 5 4 12 2
11 21 20 3 18
6 19 23 13 16
ian#eris:~/work/stack$ ./a.out
22 11 6 21 24
7 3 8 10 25
17 19 16 2 9
13 4 15 5 23
12 1 14 20 18
You are stuck in an infinite loop because the statement goto 15 is always executed.
If k(xpos, ypos) is 1 then the first if statement is false, but the second is true so the goto 15 is executed.
If instead k(xpos, ypos) is not 1 then the first if statement is true, and so k(xpos, ypos) is set to 1. The second if statement is only evaluated after this, and so is true, and so the goto 15 is executed.
As other answers have mentioned, the method you are using is horribly inefficient. However, if you still want to use it, here is the fixed code, with a number of modernisations:
subroutine fillarray(k, n)
implicit none
integer, intent(in) :: n
integer, intent(inout) :: k(n,n)
real(dp) :: x, y
integer :: xpos, ypos
integer :: i
i=1
do while (i<=100)
call random_number(x)
call random_number(y)
xpos = 20*x + 1.0_dp
ypos = 20*y + 1.0_dp
if (k(xpos, ypos)/=1) then
k(xpos, ypos) = 1
i = i+1
endif
enddo
end subroutine
Note that this assumes that the array k has already been initialised, otherwise checking the contents of the array will lead to undefined behaviour.
As to whether end if is optional or not. No, it is not optional. It is always required. All languages need to know where the end of a loop is. C uses }, Python uses un-indentation, Fortran uses endif.
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.
I have a 2D array where I'm running some computation on each process. Afterwards, I need to gather all the computed columns back to the root processes. I'm currently partitioning in a first come first serve manner. In pseudo code, the main loop looks like:
DO i = mpi_rank + 1, num_columns, mpi_size
array(:,i) = do work here
After this is completed, I need to gather these columns into the correct indices back in the root process. What is the best way to do this? It looks like MPI_GATHERV could do what I want if the partitioning scheme was different. However, I'm not sure what the best way to partition that would be since num_columns and mpi_size are not necessarily evenly divisible.
I suggest the following approach:
Cut the 2D array into chunks of "almost equal" size, i.e. with local number of columns close to num_columns / mpi_size.
Gather chunks with mpi_gatherv, which operates with chunks of different size.
To get "almost equal" number of columns, set local number of columns to integer value of num_columns / mpi_size and increment by one only for first mod(num_columns,mpi_size) mpi tasks.
The following table demonstrates the partitioning of (10,12) matrix on 5 MPI processes:
01 02 03 11 12 13 21 22 31 32 41 42
01 02 03 11 12 13 21 22 31 32 41 42
01 02 03 11 12 13 21 22 31 32 41 42
01 02 03 11 12 13 21 22 31 32 41 42
01 02 03 11 12 13 21 22 31 32 41 42
01 02 03 11 12 13 21 22 31 32 41 42
01 02 03 11 12 13 21 22 31 32 41 42
01 02 03 11 12 13 21 22 31 32 41 42
01 02 03 11 12 13 21 22 31 32 41 42
01 02 03 11 12 13 21 22 31 32 41 42
Here the first digit is an id of the process, the second digit is a number of local columns.
As you can see, processes 0 and 1 got 3 columns each, while all other processes got only 2 columns each.
Below you can find working example code that I wrote.
The trickiest part would be the generation of rcounts and displs arrays for MPI_Gatherv. The discussed table is an output of the code.
program mpi2d
implicit none
include 'mpif.h'
integer myid, nprocs, ierr
integer,parameter:: m = 10 ! global number of rows
integer,parameter:: n = 12 ! global number of columns
integer nloc ! local number of columns
integer array(m,n) ! global m-by-n, i.e. m rows and n columns
integer,allocatable:: loc(:,:) ! local piece of global 2d array
integer,allocatable:: rcounts(:) ! array of nloc's (for mpi_gatrherv)
integer,allocatable:: displs(:) ! array of displacements (for mpi_gatherv)
integer i,j
! Initialize
call mpi_init(ierr)
call mpi_comm_rank(MPI_COMM_WORLD, myid, ierr)
call mpi_comm_size(MPI_COMM_WORLD, nprocs, ierr)
! Partition, i.e. get local number of columns
nloc = n / nprocs
if (mod(n,nprocs)>myid) nloc = nloc + 1
! Compute partitioned array
allocate(loc(m,nloc))
do j=1,nloc
loc(:,j) = myid*10 + j
enddo
! Build arrays for mpi_gatherv:
! rcounts containes all nloc's
! displs containes displacements of partitions in terms of columns
allocate(rcounts(nprocs),displs(nprocs))
displs(1) = 0
do j=1,nprocs
rcounts(j) = n / nprocs
if(mod(n,nprocs).gt.(j-1)) rcounts(j)=rcounts(j)+1
if((j-1).ne.0)displs(j) = displs(j-1) + rcounts(j-1)
enddo
! Convert from number of columns to number of integers
nloc = m * nloc
rcounts = m * rcounts
displs = m * displs
! Gather array on root
call mpi_gatherv(loc,nloc,MPI_INT,array,
& rcounts,displs,MPI_INT,0,MPI_COMM_WORLD,ierr)
! Print array on root
if(myid==0)then
do i=1,m
do j=1,n
write(*,'(I04.2)',advance='no') array(i,j)
enddo
write(*,*)
enddo
endif
! Finish
call mpi_finalize(ierr)
end
What about gathering in chunks of size mpi_size?
To shorten this here, I'll assume that num_columns is a multiple of mpi_size. In your case the gathering should look something like (lda is the first dimension of array):
DO i = 1, num_columns/mpi_size
IF (rank == 0) THEN
CALL MPI_GATHER(MPI_IN_PLACE, lda, [TYPE], array(1,(i-1)*mpi_size+1), lda, [TYPE], 0, MPI_COMM_WORLD, ierr)
ELSE
CALL MPI_GATHER(array(1, rank + (i-1)*mpi_size + 1), lda, [TYPE], array(1,(i-1)*mpi_size+1), lda, [TYPE], 0, MPI_COMM_WORLD, ierr)
END IF
ENDDO
I'm not so sure with the indices and if this actually works, but I think, you should get the point.
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