My MPI fortran code gives different results for large integers - variables

I am very new to MPI programming (like 3 days old). I am now dealing with MPI_ALLREDUCE and MPI_REDUCE.
The code below takes a value n and the task of every process is to add 1 to the variable mypartialsum for n/num_procs times, where num_procs is the number of processes. After the reduction the values of mypartialsum are gathered in sum and the final result is sum=n.
The code seems to work fine if n<1d10, but for n>=1d10 I get the wrong result.
For example, if I run the code with n=1d10 the value of sum is 1410065408, while it is correct for n=1d9. What I am missing? I suspect it could be something with the kind of the variables, but after looking on the internet it is not clear to me what the problem is.
program test
use mpi
IMPLICIT NONE
!include 'mpif.h'
integer:: ierr, num_procs, my_id, status(MPI_STATUS_SIZE), sender(MPI_STATUS_SIZE), root, rank
integer:: i!, n
integer, parameter :: MyLongIntType = selected_int_kind (12)
integer(kind=MyLongIntType):: n
!real:: sum=0., partialsum=0., mypartialsum=0.
integer(kind=MyLongIntType):: sum=0, partialsum=0, mypartialsum=0
real:: starttime, endtime
root=0
call MPI_INIT ( ierr )
call MPI_COMM_RANK (MPI_COMM_WORLD, my_id, ierr)
call MPI_COMM_SIZE (MPI_COMM_WORLD, num_procs, ierr)
starttime = MPI_WTIME()
if (my_id .eq. root) then
print*, "Running in process 0."
print*, "total numeber of process is", num_procs
n=int8(1d10)!1000000000
endif
call MPI_BCAST(n, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
!print*, "running process", my_id
mypartialsum=0.
do i = my_id+1, n, num_procs
mypartialsum=mypartialsum+int(1)
enddo
partialsum=mypartialsum
print*, "Running process", my_id, "Partial sum is ", partialsum
call MPI_REDUCE(partialsum, sum, 1, MPI_INTEGER, MPI_SUM, ROOT, MPI_COMM_WORLD, ierr)
!call MPI_ALLREDUCE(partialsum, sum, 1, MPI_INTEGER, MPI_SUM, MPI_COMM_WORLD, ierr)
endtime = MPI_WTIME()
if (my_id .eq. 0) then
print*, "sum is", sum, "time spent processing", endtime-starttime
! else if (my_id .gt. 0) then
! print*, "sum on process", my_id, "is", sum , "time spent processing is", endtime-starttime
endif
call MPI_FINALIZE ( ierr )
end program

You are telling MPI_Reduce that the type being summed is MPI_INTEGER. However, MPI_INTEGER corresponds to the default integer kind in Fortran. You have to provide the correct MPI type that corresponds to the selected_int_kind (12) type.
One of the possible ways, specifically for kinds resulting from selected_integer_kind is the MPI_Type_create_f90_integer(r, newtype, ierror) subroutine.
So something like call MPI_Type_create_f90_integer(12, MyLongIntMPIType, ierror).
There are other alternatives, like using fixed memory storage kinds like integer(int64) and the MPI_INTEGER8 MPI type originally developed for the non-standard integer*8.
See also Additional Support for Fortran Numeric Intrinsic Types
It also contains this important advice to users:
The datatypes returned by the above functions are predefined
datatypes. They cannot be freed; they do not need to be committed;
they can be used with predefined reduction operations.
which makes it simple to use the returned type in MPI_Reduce() with MPI_SUM.

Related

Incompatible types for operator with Mosel

I'm starting to use Xpress Fico workbench. I was trying to define a simple model in a model file in this way:
model ModelName
options noimplicit
uses "mmxprs"
! uses "mminsight" ! uncomment this line for an Xpress Insight model
declarations
! indici
indexes = 1..4
contraints = 1..2
x: array(indexes) of mpvar
c: array(indexes) of integer
A: array(contraints, indexes) of real
B: array(contraints) of real
! Objective:linctr
profit: linctr
end-declarations
!profit:=250*x1+230*x2+110*x3+350*x4
c::[250, 230, 110, 350]
profit:=sum(i in indexes) c(i)*x(i)
! 2*x1+1.5*x2+0.5*x3+2.5*x4<=100
! 0.5*x1+0.25*x2+0.25*x3+x4<=50
A::[ 2, 1.5, 0.5, 2.5,
0.5, 0.25, 0.25, 1]
B::[ 100,
50]
forall(r in contraints) do
sum(c in indexes) A(r, c) * x(c) <= B(r)! body...
end-do
writeln("Begin running model")
maximise(profit)
writeln("profit: ", getobjval)
forall(i in indexes) do
writeln("x( ", i, ")", getsol(x(i)))
end-do
writeln("End running model")
end-model
When I try to build the file I receive the following error
Mosel: E-101 at (33,21) of `studio/esL01_01.1.mos': Incompatible types for operator (`array of integer' in `range' not defined).
Mosel: E-151 at (33,31) of `studio/esL01_01.1.mos': Incompatible type for subscript 2 of `A'.
Any suggestion to solve this?
A correction to the previous answer: Mosel applies standard precedence rules for the evaluation of operators (i.e., multiplication takes precedence over addition), so the parentheses around the product terms are not required from a language point of view - although they may help improve readability - so you could write just as well:
forall(r in contraints) do
sum(cc in indexes) A(r, cc) * x(cc) <= B(r)! body...
end-do
There are two issues in your code,
First you are using c as an iterator in your sum, but it's declared as an array above. So Mosel is complaining by saying that you are using an array in range, which you cannot do. You should not be using an already declared variable as an iterator in Mosel. So I changed your iterator to cc.
Second, you are aiming to take a sum over A(r, c) * x(c). You need parenthesis around them so Mosel knows where your sum ends. Otherwise, it will assume you are only taking sum over the first element A(r, cc).
So your loop should be like this:
forall(r in contraints) do
sum(cc in indexes) (A(r, cc) * x(cc)) <= B(r)! body...
end-do

How do I read numbers from a file in Fortran and immediately do calculations with each numbers?

I'm totally new to Fortran, and I'm trying to learn the language here:
http://www.fortrantutorial.com/files-precision/index.php. I have some basic experience with C and Python, but not much, like introduction class and such.
So in exercises 4.1, they ask me to input some numbers from a file, and check if these numbers are even or odd. Here is the code to input the numbers:
program readdata
implicit none
!reads data from a file called mydata.txt
real :: x,y,z
open(10,file='mydata.txt')
read(10,*) x,y,z
print *,x,y,z
end program readdata
The file mydata.txt contains some random numbers. And they can check if the number is even or odd by:
if (mod(num,2)>0) then……
My question is that: if this file have like 10, or 1000 numbers, do I have to manually assign every single one of them? Is there any other way for me to do quick calculation with mass numbers situation like that?
Every read also moves the read pointer forwards. So with every new read, a new line is read in from the file.
The easiest thing to do is to keep reading until the READ statement returns an error. Of course, you have to pass a variable for the READ to write its error into. Something like this:
program readdata
implicit none
real :: x, y, z
integer :: iounit, ios
open(newunit=iounit, file='mydata.txt', iostat=ios, action='READ')
if (ios /= 0) STOP 1
do
read(iounit, *, iostat=ios) x, y, z
if (ios /= 0) exit
print *, x, y, z
end do
close(iounit)
end program readdata
Update: If you're limited to Fortran 95, as OP suggested in his comments, here's what to change: Instead of
integer :: iounit, ios
open(newunit=iounit, ...)
you use
integer :: ios
integer, paramter :: iounit = 100
open(unit=iounit, ...)
All that's important is that iounit is a number, greater than 10, which is not used as a unit for any other read/write operation.
well a mass of numbers means that X, Y, and Z need to be more than a single number of each... so something like this should get you vectored towards a solution:
program readdata
implicit none
!reads data from a file called mydata.txt
INTEGER, PARAMETER :: max2process = 10000
INTEGER :: I
INTEGER :: K = 0
real , DIMENSION(max2process) :: x,y,z
LOGICAL, DIMENSION(max2process) :: ODD = .FALSE.
open(10,file='mydata.txt')
10 CONTINUE
X(:) = 0
Y(:) = 0
K(:) = 0
DO I = 1, max2process
K = K + I
read(10,*, EOF=99) x(I), y(I), z(I)
print *,x(I), y(I), z(I)
ENDDO
WRITE(*,22) I, MINVAL(X(1:I)), MAXVAL(X(1:I)
22 FORMAT(' MIN(X(1:',I5,')=',0PE12.5,' Max=',0PE12.5)
odd = .FALSE.
WHERE (MODULO(X, 2) /= 0)
ODD = .TRUE.
ENDWHERE
DO I = 1, 10
WRITE(*,88) I, X(I), odd(I)
88 FORMAT('x(', I2,')=',0PE12.5,' odd="',L1,'"')
EBDDO
WRITE(*,*) ' we did not hit the end of file... Go back and read some more'
GOTO 10
99 WRITE(*,*) 'END of file reached at ', (K-1)
end program readdata
Basically something like that, but I probably have a typo (LTR). If MODULO is ELEMENTAL then you can do it easily... And MODULO is ELEMENTAL, so you are in luck (which is common).
There is a https://rosettacode.org/wiki.Even_or_Odd#Fortran that looks nice. the functions could be further enhanced using ELEMENTAL FUNCTION.
that should give you enough to work it out.
or you could read the file to find the size, close and reopen it, and then allocate X, Y, Z and Odd... the example i gave was more of a streaming example. one should generally use a DO WHILE rather than a GOTO, but starting out a GOTO can be conceptually easier. (You need an EXIT or some DO WHILE (IOSTAT /= ) in order to break out)

A loop in fortran code doesn't obey loop rules

First of all i am new at fortran. I tried to find the value of 1/e within tolerance of 0.0000005. I used the summation representation of 1/e which is (epsilon) n from zero goes to infinity ((-1)^n)/n!. I started from n=2 and when the value of 1/n! is smaller than my tolerance, the program will stop and print the total value that is calculated. But my program just goes to n=3 only and only prints the value of 1/3! which is 1.666666.
!program is edited. the edited form is calculates what i wanted.Before the condition of outer while was (num3<5e-8) and it didn't increase n. Now n increases problem is solved.
program Ecalculator
implicit none
integer :: mult,num1,n,num4,num5
real :: summ,num3,fact
mult=1
n=2.0
fact=1.0
summ=0.0
DO WHiLE(n<13)
fact=n
num1=n-1
DO WHiLE(num1>0)
fact=fact*num1;
num1=num1-1;
END DO
fact=fact*mult;
num3=1.0/fact;
mult=mult*(-1);
summ=summ+num3;
n=n+1;
END DO
print *, summ
read *, num5
end program Ecalculator
It looks like the factorial is not computed correctly: the line fact=num4*num1 is probably not doing what you want, because num4 is just assigned to be n and never changes throughout the inner loop. I don't think you need both variables num4 and fact; you could combine them into one variable.

Value of integer changes between call and subroutine

I'm trying to implement a new subroutine, inigw, in a decades-old Fortran-based program, and struggling mightily. I'm a total Fortran beginner so hopefully I'm missing something obvious.
I am calling a subroutine, inigw(iyear,imonth,iday,nday,time,seed). Of the 6 input variables to the call, all are integers except time. If I write the variables out immediately before calling, everything looks fine:
if (gwexist.eq.2 .and. iyear.ge.igwyear .and. iyear.le.igwend) then
write(*,*) 'iyear in call = ', iyear
write(*,*) 'imonth in call = ', imonth
write(*,*) 'iday in call = ', iday
write(*,*) 'nday in call = ', nday
write(*,*) 'time in call = ', time
write(*,*) 'seed in call = ', seed
call inigw (iyear,imonth,iday,nday,time,seed)
endif
The output looks as I would expect:
iyear in call = 2013
imonth in call = 1
iday in call = 4
nday in call = 4
time in call = 28800.00
seed in call = 1776642162
However, the values of all the integers (iyear, imonth, iday, nday, and seed) are changed within my subroutine.
c ---------------------------------------------------------------------
subroutine inigw (iyear, imonth, iday, nday, time, seed)
c ---------------------------------------------------------------------
c SCZ- 1/31/2014
c This routine is intended to read in hourly depth to groundwater data from
c text files. Much of the language is borrowed from inimet.
c Define some variables
implicit none
real
> dtgwin(ndpts), ! read-in depth to groundwater
> time, ! time in seconds since start of day
> tdomthick ! total domain thickness [m]
integer
> seed,
> nday,
> iyear, ! main routine year
> imonth, ! main routine month
> iday, ! main routine day
c
write(*,*) 'iyear in inigw = ', iyear
write(*,*) 'imonth in inigw = ', imonth
write(*,*) 'iday in inigw = ', iday
write(*,*) 'nday in inigw = ', nday
write(*,*) 'time in inigw = ', time
write(*,*) 'seed in inigw = ', seed
...
The values of the integers are now different, but the real number (time) is the same!
iyear in inigw = 2.8208138E-42
imonth in inigw = 1.4012985E-45
iday in inigw = 5.6051939E-45
nday in inigw = 5.6051939E-45
time in inigw = 28800.00
seed in inigw = 3.4667155E+25
What's interesting is that there's another subroutine, inimet, structured in basically the same way with the same inputs to the call, and the variables pass through that call successfully. I assume I'm missing something obvious here, but I'm not sure exactly what. I've tried Googling and found nothing. Sorry I can't provide a reproducible example- I don't know enough about Fortran to write one.
Strangely enough, time is the only one that appears to remain unchanged. It's also the only one that's not declared in the function with an integer statement that appears to begin one column too far to the left.
Fix the indentation of that and your problem should be solved. FORTRAN was one of those languages that was very picky about this sort of thing.
As we've established that the problem was in indentation (see the answer by #paxdiablo) I suppose it won't hurt to elaborate on why.
The code is using fixed-form source code. This means that a character other than a blank or "0" in column 6 is treated as a continuation character. Your "i" of "integer" is such a character. This, coupled with the general insignificance of blanks, means that your code snippet
real
> dtgwin(ndpts), ! read-in depth to groundwater
> time, ! time in seconds since start of day
> tdomthick ! total domain thickness [m]
integer
> seed,
> nday,
is equivalent to
real
> dtgwin(ndpts), ! read-in depth to groundwater
> time, ! time in seconds since start of day
> tdomthickntegerseed,
> nday,
(also noting that comments do not affect program interpretation).
So nday and the following variables are indeed declared real.
Note that there is no variable seed or tdomthick declared explicitly, so your implicit none confuses me as to why your code compiled. [Even dummy arguments need to be explicitly declared with implicit none unless you're using some other form of association to get these variables?]
Finally, I'll echo #M.S.B.'s comment about not writing new code in fixed-form, as I imagine you aren't using an F77-only compiler.

Reading a known number of variable from a file when one of the variables are missing in input file

I already checked similar posting. The solution is given by M. S. B. here Reading data file in Fortran with known number of lines but unknown number of entries in each line
So, the problem I am having is that from text file I am trying to read inputs. In one line there is supposed to be 3 variables. But sometimes the input file may have 2 variables. In that case I need to make the last variable zero. I tried using READ statement with IOSTAT but if there is only two values it goes to the next line and reads the next available value. I need to make it stop in the 1st line after reading 2 values when there is no 3rd value.
I found one way to do that is to have a comment/other than the type I am trying to read (in this case I am reading float while a comment is a char) which makes a IOSTAT>0 and I can use that as a check. But if in some cases I may not have that comment. I want to make sure it works even than.
Part of the code
read(15,*) x
read(15,*,IOSTAT=ioerr) y,z,w
if (ioerr.gt.0) then
write(*,*)'No value was found'
w=0.0;
goto 409
elseif (ioerr.eq.0) then
write(*,*)'Value found', w
endif
409 read(15,*) a,b
read(15,*) c,d
INPUT FILE is of the form
-1.000 abcd
12.460 28.000 8.00 efg
5.000 5.000 hijk
20.000 21.000 lmno
I need to make it work even when there is no "8.00 efg"
for this case
-1.000 abcd
12.460 28.000
5.000 5.000 hijk
20.000 21.000 lmno
I can not use the string method suggested by MSB. Is there any other way?
I seem to remember trying to do something similar in the past. If you know that the size of a line of the file won't exceed a certain number, you might be able to try something like:
...
character*(128) A
read(15,'(A128)') A !This now holds 1 line of text, padded on the right with spaces
read(A,*,IOSTAT=ioerror) x,y,z
if(IOSTAT.gt.0)then
!handle error here
endif
I'm not completely sure how portable this solution is from one compiler to the next and I don't have time right now to read up on it in the f77 standard...
I have a routine that counts the number of reals on a line. You could adapt this to your purpose fairly easily I think.
subroutine line_num_columns(iu,N,count)
implicit none
integer(4),intent(in)::iu,N
character(len=N)::line
real(8),allocatable::r(:)
integer(4)::r_size,count,i,j
count=0 !Set to zero in case of premature return
r_size=N/5 !Initially try out this max number of reals
allocate(r(r_size))
read(iu,'(a)') line
50 continue
do i=1,r_size
read(line,*,end=99) (r(j),j=1,i) !Try reading i reals
count=i
!write(*,*) count
enddo
r_size=r_size*2 !Need more reals
deallocate(r)
allocate(r(r_size))
goto 50
return
99 continue
write(*,*) 'I conclude that there are ',count,' reals on the first line'
end subroutine line_num_columns
If a Fortran 90 solution is fine, you can use the following procedure to parse a line with multiple real values:
subroutine readnext_r1(string, pos, value)
implicit none
character(len=*), intent(in) :: string
integer, intent(inout) :: pos
real, intent(out) :: value
integer :: i1, i2
i2 = len_trim(string)
! initial values:
if (pos > i2) then
pos = 0
value = 0.0
return
end if
! skip blanks:
i1 = pos
do
if (string(i1:i1) /= ' ') exit
i1 = i1 + 1
end do
! read real value and set pos:
read(string(i1:i2), *) value
pos = scan(string(i1:i2), ' ')
if (pos == 0) then
pos = i2 + 1
else
pos = pos + i1 - 1
end if
end subroutine readnext_r1
The subroutine reads the next real number from a string 'string' starting at character number 'pos' and returns the value in 'value'. If the end of the string has been reached, 'pos' is set to zero (and a value of 0.0 is returned), otherwise 'pos' is incremented to the character position behind the real number that was read.
So, for your case you would first read the line to a character string:
character(len=1024) :: line
...
read(15,'(A)') line
...
and then parse this string
real :: y, z, w
integer :: pos
...
pos = 1
call readnext_r1(line, pos, y)
call readnext_r1(line, pos, z)
call readnext_r1(line, pos, w)
if (pos == 0) w = 0.0
where the final 'if' is not even necessary (but this way it is more transparent imho).
Note, that this technique will fail if there is a third entry on the line that is not a real number.
I know the following simple solution:
w = 0.0
read(15,*,err=600)y, z, w
goto 610
600 read(15,*)y, z
610 do other stuff
But it contains "goto" operators
You might be able to use the wonderfully named colon edit descriptor. This allows you to skip the rest of a format if there are no further items in the I/O list:
Program test
Implicit None
Real :: a, b, c
Character( Len = 10 ) :: comment
Do
c = 0.0
comment = 'No comment'
Read( *, '( 2( f7.3, 1x ), :, f7.3, a )' ) a, b, c, comment
Write( *, * ) 'I read ', a, b, c, comment
End Do
End Program test
For instance with gfortran I get:
Wot now? gfortran -W -Wall -pedantic -std=f95 col.f90
Wot now? ./a.out
12.460 28.000 8.00 efg
I read 12.460000 28.000000 8.0000000 efg
12.460 28.000
I read 12.460000 28.000000 0.00000000E+00
^C
This works with gfortran, g95, the NAG compiler, Intel's compiler and the Sun/Oracle compiler. However I should say I'm not totally convinced I understand this - if c or comment are NOT read are they guaranteed to be 0 and all spaces respectively? Not sure, need to ask elsewhere.