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.
Related
First of all, Happy New Year 2022.
I am new in learning OMP Parallel programming in Fortran.
Need HELP with my codes below, which produces a correct result if run in serial,
but produces a random (incorrect) result if run in parallel (multi-threads).
Purpose is to swap the values of some array elements in global shared array A(N:N).
Cannot figure out or identify what is wrong in my code below
(i.e. any race conditions in updating elements of Array A ?)
SUBROUTINE SWAP_COLUMNS (IS)
INTEGER :: IS, L, LH, KK
INTEGER :: I, J
L = (IS - 1) / 2
LH = L + 1
!$OMP PARALLEL DEFAULT(PRIVATE) SHARED(A,IS,L,LH)
!$OMP DO
DO J = 1, L
DO I = 1, IS
KK = I + IS
IF (I == LH) THEN
CALL SWAP_VALUES (I, J+1, KK, J+1)
ELSE
CALL SWAP_VALUES (I, J, KK, J)
END IF
END DO
END DO
!$OMP END DO
!$OMP END PARALEL
SUBROUTINE SWAP_VALUES (I3, K1, I4, K2)
INTEGER :: IT, I3,K1, I4,K2
IT = A(I3, K1)
A(I3, K1) = A(I4,K2)
A(I4, K2) = IT
END SUBROUTINE
Example :
Array A (6,6) :
8 1 6 ….
3 5 7 ….
4 9 2 …
35 28 33 …
30 32 34 …
31 36 29 …
I would expect that the result would always be correct (as run with single thread)
with IS = 3 , the correct expected result :
35 1 6 ….
3 32 7 ….
31 9 2 …..
8 28 33 ….
30 5 34 ….
4 36 29 ...
I tried a manual calculation :
IS = 3
Iteration 1 : J = 1, I = 1, KK = 4
CALL SWAP_VALUES (1, 1, 4, 1) ----> Swap A(1,1) & A(4,1)
Iteration 2 : J = 1, I = 2, KK = 5
CALL SWAP_VALUES (2, 2, 5, 2) -----> Swap A(2,2) & A(5,2)
Last Iteration 3 : J =1, I = 3, KK = 6
CALL SWAP_VALUES( 3,1, 6,1) -----> A(3,1) & A(6,1)
I would expect that even run in parallel (multi threads), as each element of array A
is accessed or updated independently (not dependent on the loop iteration
(before or after), there should be no issue of race condition, and the local variable IT
is a private variable in subroutine SWAP_VALUES.
But instead, I get a random result, sometimes correct and sometimes incorrect,
and sometimes the element value does not swap, or sometimes it is copied but
not swapped so the value becomes duplicated ( e.g. A(1,1) = 35 and A(4,1) = 35
instead of 8)
This is my code:
Program Arrays_0
Implicit none
Integer :: i , Read_number , Vig_Position , Vipg_Position , n_iter
Integer , parameter :: Br_gra = 12
Integer , parameter , dimension ( Br_gra ) :: Vig = [ ( i , i = 1 , Br_gra) ]
Integer , parameter , dimension ( Br_gra ) :: Vipg = [ 0 , 1 , 1 , 1 , 2 , 2 , 3 , 4 , 4 , 7 , 7 , 7 ]
Integer :: Result_of_calculation
Write(*,*)"Enter the number (From 1 to Br_gra):"
Read(*,*) Read_number
Vig_Position = Vig(Read_number)
Vipg_Position = Vipg(Vig_Position)
n_iter = 0
Result_of_calculation = Vig_Position
Do while( Vipg_Position .ne. Vipg(1) )
n_iter = n_iter + 1
Vig_Position = Vipg_Position
Result_of_calculation = Result_of_calculation + Vig_Position
Vipg_Position = Vipg(Vig_Position)
End Do
Write(*,'(a,1x,i0)')"The number of iteration is:",n_iter
Write(*,'(a,1x,i0)')"The result of calculation is:",Result_of_calculation
End Program Arrays_0
Intention is to get value in every iteration for a variables:
Vig_Position , Result_of_calculation and Vipg_position.
How to declare variables for that kind of calculation?
In general, is there other method for counting a number of iteration?
How to declare variables in function of number of iteration befoure the code set that number like result of calculation?
Now that the question has been clarified, here's a typical way of solving the problem in Fortran. It isn't the only possible way, but it is the most general. The strategy in routine resize to double the old size is reasonable - you want to minimize the number of times this is called. The data set in the sample program is small, so to show the effect I allocated the array very small to begin with. In reality, you would want a reasonably large initial allocation (say, 100 at least).
Note the use of an internal procedure that inherits the type vals_t from its host.
Program Arrays_0
Implicit none
Integer :: i , Read_number , Vig_Position , Vipg_Position , n_iter
Integer , parameter :: Br_gra = 12
Integer , parameter , dimension ( Br_gra ) :: Vig = [ ( i , i = 1 , Br_gra) ]
Integer , parameter , dimension ( Br_gra ) :: Vipg = [ 0 , 1 , 1 , 1 , 2 , 2 , 3 , 4 , 4 , 7 , 7 , 7 ]
Integer :: Result_of_calculation
! Declare a type that will hold one iteration's values
type vals_t
integer Vig_Position
integer Vipg_Position
integer Result_of_calculation
end type vals_t
! Declare an allocatable array to hold the values
! Initial size doesn't matter, but should be close
! to a lower limit of possible sizes
type(vals_t), allocatable :: vals(:)
allocate (vals(2))
Write(*,*)"Enter the number (From 1 to Br_gra):"
Read(*,*) Read_number
Vig_Position = Vig(Read_number)
Vipg_Position = Vipg(Vig_Position)
n_iter = 0
Result_of_calculation = Vig_Position
Do while( Vipg_Position .ne. Vipg(1) )
n_iter = n_iter + 1
Vig_Position = Vipg_Position
Result_of_calculation = Result_of_calculation + Vig_Position
Vipg_Position = Vipg(Vig_Position)
! Do we need to make vals bigger?
if (n_iter > size(vals)) call resize(vals)
vals(n_iter) = vals_t(Vig_Position,Vipg_Position,Result_of_calculation)
End Do
Write(*,'(a,1x,i0)')"The number of iteration is:",n_iter
Write(*,'(a,1x,i0)')"The result of calculation is:",Result_of_calculation
! Now vals is an array of size(vals) of the sets of values
! For demonstration, print the size of the array and the values
Write(*,'(a,1x,i0)')"Size of vals is:", size(vals)
Write(*,'(3i7)') vals(1:n_iter)
contains
! Subroutine resize reallocates the array passed to it
! with double the current size, copies the old data to
! the new array, and transfers the allocation to the
! input array
subroutine resize(old_array)
type(vals_t), allocatable, intent(inout) :: old_array(:)
type(vals_t), allocatable :: new_array(:)
! Allocate a new array at double the size
allocate (new_array(2*size(old_array)))
write (*,*) "Allocated new array of size ", size(new_array)
! Copy the data
new_array(1:size(old_array)) = old_array
! Transfer the allocation to old_array
call MOVE_ALLOC (FROM=new_array, TO=old_array)
! new_array is now deallocated
return
end subroutine resize
End Program Arrays_0
Sample output:
Enter the number (From 1 to Br_gra):
12
Allocated new array of size 4
The number of iteration is: 3
The result of calculation is: 23
Size of vals is: 4
7 3 19
3 1 22
1 0 23
I have to write an script in Fortran that returns the results of the Newton binomial:
for a, b and n given.
The problem is that I cant use functions or subroutines.
Until now I have written the code for the combinations:
if (n==0) then
print*, "Cnk=",Cnk
else if ((n>=0).and.(k==0)) then
print*, "Cnk=",Cnk
else
do i=1,n,1
aux=aux*i
if (k==i) then
factK=aux
end if
if ((n-k)==i) then
factnk=aux
end if
factn=aux
end do
Cnk=factn/(factk*factnk)
print*, "Cnk=",Cnk
end if
In the case of the binomial k is variable from 0 to n.
Probably not the fastest solution, but quite short:
program binom
implicit none
integer,parameter :: N=5
integer,parameter :: a=3
integer,parameter :: b=5
integer :: k, i
integer :: coeff, eval, total
total = 0
do i=0,N
coeff = product((/ (k,k=1,n) /)) / product((/ (k,k=1,i),(k,k=1,n-i) /))
eval = coeff * a**(n-i) * b**i
total = total + eval
write(*,*) 'i=',i,'coeff=',coeff, 'eval=',eval
enddo !i
write(*,*) '(a+b)**n=',(a+b)**N,'Total=',total
end program binom
Output:
i= 0 coeff= 1 eval= 243
i= 1 coeff= 5 eval= 2025
i= 2 coeff= 10 eval= 6750
i= 3 coeff= 10 eval= 11250
i= 4 coeff= 5 eval= 9375
i= 5 coeff= 1 eval= 3125
(a+b)**n= 32768 Total= 32768
I have the following data
X Y INFTIME
1 1 0
1 2 4
1 3 4
1 4 3
2 1 3
2 2 1
2 3 3
2 4 4
3 1 2
3 2 2
3 3 0
3 4 2
4 1 4
4 2 3
4 3 3
4 4 0
X and Y represent he X and Y components in the square grid of 4 by 4.
Here I want to sample randomly 10% from the population which are infected i.e, whose INFTIME is non zero. I did not get any idea of coding so could not start it.
Any suggestions and idea will be great for me.
Thanks
EDIT:
DO T = 1,10
DO i = 1, 625
IF(INFTIME(i)/=0 .AND. INFTIME(i) .LE. T)THEN
CALL RANDOM_NUMBER(u(i))
u(i) = 1+aint(u(i)*25)
CALL RANDOM_NUMBER(v(i))
v(i) = 1+aint(v(i)*25)
CALL RANDOM_NUMBER(w(i))
w(i) = 1+aint(w(i)*10)
ENDIF
ENDDO
ENDDO
do p = 1,625
WRITE(*,*) u(p),v(p),w(p)
enddo
This is my code what I tried but it only gives the random numbers, not the connection to the data. I used the data of 25 by 25 grids i.e, 625 individuals and time of infection 1 to 10
Follow what ja72 said. You have three 1D arrays of the same size (16). All you need to do is pick a number between 1 and 16, check to see if INFTIME is zero and accept the value as needed, then repeat until you've taken 10% of the samples (which would be 1.6 values, so I presume you'd just take 2? Or do you have more data than this 4x4 you presented?)
Edit You need to call the random number generator before the if statement:
do t=1,10
do i=1,625
ind = 1+int(624*rand(seed))
if(inftime(ind).neq.0 .and. inftime(ind).le.t) then
stuff
endif
enddo
enddo
The call ind=1+int(625*rand(seed)) will pick a random integer between 1 (when rand(seed)=0) and 625 (when rand(seed)=1). Then you can do what you need if the if statement is satisfied.
EDIT: program epimatrix
IMPLICIT NONE
INTEGER ::l, i,T,K
REAL, DIMENSION(1:625):: X,y,inftime
INTEGER::seed,my_cnt
INTEGER,DIMENSION(8) :: time1
CALL DATE_AND_TIME(values=time1)
seed = 1000*time1(7)+time1(8)
call srand(seed)
OPEN(10, FILE = 'epidemicSIR.txt', FORM = 'FORMATTED')
DO l = 1,625
READ(10,*,END = 200) X(l), Y(l), INFTIME(l)
! WRITE(*,*) X(l),Y(l), INFTIME(l)
! if you know how it was formatted, you should use
! read(10,20) X(l), Y(l), INFTIME(l)
! where 20 is the format
ENDDO
200 CONTINUE
CLOSE(10)
DO T = 1,10
my_cnt=0
write(*,*) "T=",T
DO while (my_cnt.le.63)
K = 1+int(624*rand())
IF(INFTIME(K)/=0 .AND. INFTIME(K) .LE. T)THEN
write(*,*) X(k),Y(k),INFTIME(k)
my_cnt=my_cnt+1
ENDIF
enddo
write(*,*) " "
ENDDO
end program
EDIT 2
I've adjusted the program to fix some of the issues. I've tried keeping my edits in lowercase so that you can see the difference. The do-while loop allows the code to continue running until the condition my_cnt.le.63 has been met (which means you have 63 lines of X, Y, inftime per T). I've added a line to output T and another line to add a space so that the data might be more clear when looking at the output.
This should take care of all the issues you've been running into. If not, I'll keep checking this page.
I have a column matrix with 40 values. Say,
1
4
5
2
4
1
9
.
.
.
2
How can I call every four values and average them until it reaches 40th? I managed to do in the following way but is there a better way? Beste!
i = 1, 4
avg1 = avg + avg(i)
i = 5,8
avg2 = avg + avg(i)
i = 9,12
avg3 = avg + avg(i)
.......
i = 37,40
avg10 = avg + avg(i)
It took me a couple of iterations to get the syntax right, but how about this?
integer, parameter, dimension(*) :: a = [ 1, 4, 5, ..., 2 ]
integer :: i
real, dimension(10) :: avg
avg = [ (sum(a(i * 4 + 1 : (i + 1) * 4)) / 4., i = 0, 9) ]
print *, avg
end
How about that?
program testing
implicit none
integer, dimension(40) :: array
real, dimension(10) :: averages
integer :: i, j, k, aux
array(:) = (/(i, i=1,40)/) ! just values 1 to 40
averages(:) = 0.0
k = 1 ! to keep track of where to store the next average
do i=1,40,4 ! iterate over the entire array in steps of 4
aux = 0 ! just a little helper variable, not really required, but neater I think
do j=i,i+3 ! iterating over 4 consecutive values
aux = aux + array(j)
end do
averages(k) = aux / 4.0
k = k + 1
end do
print *, averages
end program testing
This is the output:
2.500000 6.500000 10.50000 14.50000 18.50000
22.50000 26.50000 30.50000 34.50000 38.50000
Is this what you were looking for?