I need to check if the imaginary part is very small and set it to zero if it is in order to eliminate some floating point errors that result in very small non-zero imaginary parts when it should be zero.
My code is as follows:
kz2 = SQRT((n2*(2.0*PI*eta))**2 - kxarray(p)**2)
kz1 = SQRT((n1*(2.0*PI*eta))**2 - kxarray(p)**2)
if (aimag(kz2) < 0.0005) then
kz2 = (REAL(kz2),0.0)
end if
if (aimag(kz1) < 0.0005) then
kz1 = (REAL(kz1), 0.0)
end if
Unfortunately the compiler just returns:
gaussian1.f90:122.18:
kz2 = (REAL(kz2),0.0)
1
Error: Expected a right parenthesis in expression at (1)
gaussian1.f90:126.18:
kz1 = (REAL(kz1), 0.0)
1
Error: Expected a right parenthesis in expression at (1)
Any advice would be greatly appreciated - am I even going about this problem the right way?
UPDATE: I managed to avoid the problem by using:
if (aimag(kz2) < 0.0005) then
kz2 = real(kz2)
end if
if (aimag(kz1) < 0.0005) then
kz1 = real(kz1)
end if
But what would I do if I wanted to set the imaginary part to a non-zero amount?
In Fortran 2008 there are even more possibilities. You can access real and imaginary parts as derived type components, e.g.
a = c%re
b%im = 5
So, to set imaginary part of z to zero in new compilers you can try z%im = 0 .
I think you are looking for the CMPLX function, which converts real or integer arguments to a complex number. So it you example you should be able to do something like this:
kz1 = cmplx(real(kz1), 0.)
The (1.0,1.0) style parenthesis notation you have tried is only valid for constant values, not forming a complex number from the values held in variables.
I am converting f77 code to f90 code, and part of the code needs to sum over elements of a 3d matrix. In f77 this was accomplished by using 3 loops (over outer,middle,inner indices). I decided to use the f90 intrinsic sum (3 times) to accomplish this, and much to my surprise the answers differ. I am using the ifort compiler, have debugging, check-bounds, no optimization all turned on
Here is the f77-style code
r1 = 0.0
do k=1,nz
do j=1,ny
do i=1,nx
r1 = r1 + foo(i,j,k)
end do
end do
end do
and here is the f90 code
r = SUM(SUM(SUM(foo, DIM=3), DIM=2), DIM=1)
I have tried all sorts of variations, such as swapping the order of the loops for the f77 code, or creating temporary 2D matrices and 1D arrays to "reduce" the dimensions while using SUM, but the explicit f77 style loops always give different answers from the f90+ SUM function.
I'd appreciate any suggestions that help understand the discrepancy.
By the way this is using one serial processor.
Edited 12:13 pm to show complete example
! ifort -check bounds -extend-source 132 -g -traceback -debug inline-debug-info -mkl -o verify verify.f90
! ./verify
program verify
implicit none
integer :: nx,ny,nz
parameter(nx=131,ny=131,nz=131)
integer :: i,j,k
real :: foo(nx,ny,nz)
real :: r0,r1,r2
real :: s0,s1,s2
real :: r2Dfooxy(nx,ny),r1Dfoox(nx)
call random_seed
call random_number(foo)
r0 = 0.0
do k=1,nz
do j=1,ny
do i=1,nx
r0 = r0 + foo(i,j,k)
end do
end do
end do
r1 = 0.0
do i=1,nx
do j=1,ny
do k=1,nz
r1 = r1 + foo(i,j,k)
end do
end do
end do
r2 = 0.0
do j=1,ny
do i=1,nx
do k=1,nz
r2 = r2 + foo(i,j,k)
end do
end do
end do
!*************************
s0 = 0.0
s0 = SUM(SUM(SUM(foo, DIM=3), DIM=2), DIM=1)
s1 = 0.0
r2Dfooxy = SUM(foo, DIM = 3)
r1Dfoox = SUM(r2Dfooxy, DIM = 2)
s1 = SUM(r1Dfoox)
s2 = SUM(foo)
!*************************
print *,'nx,ny,nz = ',nx,ny,nz
print *,'size(foo) = ',size(foo)
write(*,'(A,4(ES15.8))') 'r0,r1,r2 = ',r0,r1,r2
write(*,'(A,3(ES15.8))') 'r0-r1,r0-r2,r1-r2 = ',r0-r1,r0-r2,r1-r2
write(*,'(A,4(ES15.8))') 's0,s1,s2 = ',s0,s1,s2
write(*,'(A,3(ES15.8))') 's0-s1,s0-s2,s1-s2 = ',s0-s1,s0-s2,s1-s2
write(*,'(A,3(ES15.8))') 'r0-s1,r1-s1,r2-s1 = ',r0-s1,r1-s1,r2-s1
stop
end
!**********************************************
sample output
nx,ny,nz = 131 131 131
size(foo) = 2248091
r0,r1,r2 = 1.12398225E+06 1.12399525E+06 1.12397238E+06
r0-r1,r0-r2,r1-r2 = -1.30000000E+01 9.87500000E+00 2.28750000E+01
s0,s1,s2 = 1.12397975E+06 1.12397975E+06 1.12398225E+06
s0-s1,s0-s2,s1-s2 = 0.00000000E+00-2.50000000E+00-2.50000000E+00
r0-s1,r1-s1,r2-s1 = 2.50000000E+00 1.55000000E+01-7.37500000E+00
First, welcome to StackOverflow. Please take the tour! There is a reason we expect a Minimal, Complete, and Verifiable example because we look at your code and can only guess at what might be the case and that is not too helpful for the community.
I hope the following suggestions helps you figure out what is going on.
Use the size() function and print what Fortran thinks are the sizes of the dimensions as well as printing nx, ny, and nz. As far as we know, the array is declared bigger than nx, ny, and nz and these variables are set according to the data set. Fortran does not necessarily initialize arrays to zero depending on whether it is a static or allocatable array.
You can also try specifying array extents in the sum function:
r = Sum(foo(1:nx,1:ny,1:nz))
If done like this, at least we know that the sum function is working on the exact same slice of foo that the loops loop over.
If this is the case, you will get the wrong answer even though there is nothing 'wrong' with the code. This is why it is particularly important to give that Minimal, Complete, and Verifiable example.
I can see the differences now. These are typical rounding errors from adding small numbers to a large sum. The processor is allowed to use any order of the summation it wants. There is no "right" order. You cannot really say that the original loops make the "correct" answer and the others do not.
What you can do is to use double precision. In extreme circumstances there are tricks like the Kahan summation but one rarely needs that.
Addition of a small number to a large sum is imprecise and especially so in single precision. You still have four significant digits in your result.
One typically does not use the DIM= argument, that is used in certain special circumstances.
If you want to sum all elements of foo, use just
s0 = SUM(foo)
That is enough.
What
s0 = SUM(SUM(SUM(foo, DIM=3), DIM=2), DIM=1)
does is that it will make a temporary 2D arrays with each element be the sum of the respective row in the z dimension, then a 1D array with each element the sum over the last dimension of the 2D array and then finally the sum of that 1D array. If it is done well, the final result will be the same, but it well eat a lot of CPU cycles.
The sum intrinsic function returns a processor-dependant approximation to the sum of the elements of the array argument. This is not the same thing as adding sequentially all elements.
It is simple to find an array x where
summation = x(1) + x(2) + x(3)
(performed strictly left to right) is not the best approximation for the sum treating the values as "mathematical reals" rather than floating point numbers.
As a concrete example to look at the nature of the approximation with ifort, we can look at the following program. We need to enable optimizations here to see effects; the importance of order of summation is apparent even with optimizations disabled (with -O0 or -debug).
implicit none
integer i
real x(50)
real total
x = [1.,(EPSILON(0.)/2, i=1, SIZE(x)-1)]
total = 0
do i=1, SIZE(x)
total = total+x(i)
print '(4F17.14)', total, SUM(x(:i)), SUM(DBLE(x(:i))), REAL(SUM(DBLE(x(:i))))
end do
end program
If adding up in strict order we get 1., seeing that anything smaller in magnitude than epsilon(0.) doesn't affect the sum.
You can experiment with the size of the array and order of its elements, the scaling of the small numbers and the ifort floating point compilation options (such as -fp-model strict, -mieee-fp, -pc32). You can also try to find an example like the above using double precision instead of default real.
I am getting the Array subscript out of range error:
ERROR: Array subscript out of range at line 408 column 169.
SYM_ROOT=FSV DATE=. TIME_M=. BID=. BIDSIZ=. ASK=. ASKSIZ=. EXN=.
FIRST.SYM_ROOT=1 LAST.SYM_ROOT=1 FIRST.DATE=1 LAST.DATE=1 FIRST.TIME_M=1
LAST.TIME_M=1 nexb1=. nexb2=. nexb3=. nexb4=. nexb5=. nexb6=. nexb7=. nexb8=.
nexb9=. nexb10=. nexb11=. nexb12=. nexb13=. nexb14=. nexb15=. nexb16=. nexb17=.
nexo1=. nexo2=. nexo3=. nexo4=. nexo5=. nexo6=. nexo7=. nexo8=. nexo9=. nexo10=.
nexo11=. nexo12=. nexo13=. nexo14=. nexo15=. nexo16=. nexo17=. sexb1=. sexb2=.
sexb3=. sexb4=. sexb5=. sexb6=. sexb7=. sexb8=. sexb9=. sexb10=. sexb11=.
sexb12=. sexb13=. sexb14=. sexb15=. sexb16=. sexb17=. sexo1=. sexo2=. sexo3=.
sexo4=. sexo5=. sexo6=. sexo7=. sexo8=. sexo9=. sexo10=. sexo11=. sexo12=.
sexo13=. sexo14=. sexo15=. sexo16=. sexo17=. _I_=. i=18 BB=. BO=. MIDPRICE=.
BBSize=. BOSize=. NUMEX=. _ERROR_=1 _N_=6417740
However, I am not sure what happened, because the code has previously worked on a different dataset.
The only thing that I can think of is that, because the dataset I am having problem with is the subset of the original one (which worked), it might not have the complete range of exn (I am using a variable named exn as the index of the array).
I defined the array as:
array nexb nexb:; array nexo nexo:; array sexb sexb:; array sexo sexo:;
The variable I am talking about is called exn, and it is used to reference the array:
nexb(exn)=bid;nexo(exn)=ofr;sexb(exn)=bidsiz;sexo(exn)=ofrsiz;
The arrays are initialized in the following way:
do i=1 to 17;
nexb(i)=.; nexo(i)=.; sexb(i)=.; sexo(i)=.;
end;
Originally exn spans from 1 to 17. Now I think some of the numbers in between might be missing in the dataset. But why is that a problem? They are initialized anyways.
You cannot use a missing value as the index into an array. Your log shows that EXN is missing.
I was assigned the following problem:
Make a Fortran program which will be able to read a degree[0-360] checking validity range(not type) and it will be able to calculate and print the cos(x) from the following equation, where x is in radians:
cos(x)=1-x^2/2! + x^4/4!-x^6/6!+x^8/8!-...
As a convergence criteria assume 10^(-5) using the absolute error between two successive repeats (I suppose it means do's).
For the calculation of the ! the greatest possible kind of integer should be used. Finally the total number of repeats should be printed on screen.
So my code is this:
program ex6_pr2
implicit none
!Variables and Constants
integer::i
real*8::fact,fact2 !fact=factorial
real,parameter::pi=3.14159265
double precision::degree,radiants,cosradiants,s,oldcosradiants,difference !degree,radiants=angle
print*,'This program reads and calculates an angle`s co-sinus'
print*,'Please input the degrees of the angle'
read*,degree
do while(degree<0 .or. degree>360) !number range
read*,degree
print*,'Error input degree'
cycle
end do
radiants=(degree*pi/180)
fact=1
fact2=1
s=0
cosradiants=0
!repeat structure
do i=2,200,1
fact=fact*i
fact2=fact2*(i+2)
oldcosradiants=cosradiants
cosradiants=(-(radiants)**i/fact)+(((radiants)**(i+2))/fact2)
difference=cosradiants-oldcosradiants
s=s+cosradiants
if(abs(difference)<1e-5) exit
end do
!Printing results
print*,s+1.
end program
I get right results for angles such as 45 degrees (or pi/4) and wrong for other for example 90 degrees or 180.
I have checked my factorials where I believe the error is hidden (at least for me).
Well I created another code which seems unable to run due to the following error:FUNCTION name,(RESULT of PROJECT2_EX6~FACT),used where not expected,perhaps missing '()'
program project2_ex6
implicit none
integer(kind=3)::degrees,i,sign
integer::n
double precision::x,err_limit,s_old,s
real,parameter::pi=3.14159265359
print*,'This program calculates the cos(x)'
print*,"Enter the angle's degrees"
read*,degrees
do
if(degrees<0.or.degrees>360) then
print*,'Degrees must be between 0-360'
else
x=pi*degrees/180
exit
end if
end do
sign=1
sign=sign*(-1)
err_limit=1e-5
n=0
s=0
s_old=0
do
do i=1,n
end do
s=(((-1.)**n/(fact(2.*n)))*x**(2.*n))*sign
s=s+s_old
n=n+1
if(abs(s-s_old)<1e-5) then
exit
else
s_old=s
cycle
end if
end do
print*,s,i,n
contains
real function fact(i)
double precision::fact
integer::i
if(i>=1) then
fact=i*fact(i-1)
else
fact=1
end if
return
end function
end program
Although it is your homework, I will help you here. The first thing which is wrong is ýour factorial which you need to replace with
fact = 1
do j = 1,i
fact = fact*j
enddo
second it is easier if you let your do loop do the job so run it as
do i=4,200,2
and predefine cosradians outside the do loob with
cosradiants = 1-radiants**2/2
additionally you need to take into account the changing sign which you can do in the loop using
sign = sign*(-1)
and starting it off with sign = 1 before the loop
in the loop its then
cosradiants= cosradiants+sign*radiants**i/fact
If you have included these things it should work (at least with my code it does)
I'm trying to pass 3D arrays to all other processes (in FORTRAN 77) using MPI_Bcast. v1 is a common block array. I'm also not sure if I need to broadcast the calculated values of the common array v1 to all other processes or they will be changed in each processes because of being common. The following is the related piece of code:
parameter (nprocz=48,nzro=1)
do i=i101,i102
dist = 0.015*float(i-iv0)
adamp = exp(-dist*dist)
do j = je0, je1-1
do k = ke0, ke1
v1(k,j,i) = v1(k,j,i)*adamp
end do
end do
end do
nmpi01=floor((iv0-ie0-nzro)/(nprocz-1))
if (mpirank .le. nprocz-2) then
i101=ie0+(mpirank*nmpi01)
i102=ie0+(mpirank+1)*nmpi01-1
else
i101=ie0+(mpirank*nmpi01)
i102=iv0-1
endif
MPI_Bcast(v1(:,:,i101:i102),(ke1-ke0+1)*(je1-je0)*(i102-i101+1)
& ,MPI_FLOAT,mpirank,MPI_COMM_WORLD,ierr01)
I get the error message:
PGFTN-S-0081-Matrix/vector v1 illegal as subprogram argument
The sizes of the arrays being passed in are correct. Any comment?
I corrected the code and I looped over the ranks and compute all elements of rcount and displs in each rank:
integer :: myscount, myi101
do rank = 0, nprocz-1
nmpi01=floor((iv0-ie0-nzro)/(nprocz-1))
if (rank .le. nprocz-2) then
i101=ie0+(rank*nmpi01)
i102=ie0+(rank+1)*nmpi01-1
else
i101=ie0+(rank*nmpi01)
i102=iv0-1
endif
scount=(i102-i101+1)*(je1-je0)*(ke1-ke0+1)
rcount(rank+1)=scount
displs(rank+1)=rank*scount+1
if (rank .eq. mpirank) then
myscount = scount
myi101 = i101
end if
end do
scount = myscount
i101 = myi101
call mpi_allgatherv(...)
But still wrong results. 1-in my case, results at each part are used for the next part, especially after mpi_allgatherv.so do i need to add mpi_barrier after each mpi_allgatherv? 2-should mpi_in_place be used? consider i have only one 3d array v1 that each sub-array v1(1,1,i) is calculated by some process and i want to put the calculated subarray in the appropriate part of the same array. 3- i guess i should have displs(i) = sum(rcount(1:i-1))+1 for i=>2 considering that always displs(1)=1 in fortran77. so i corrected to this: before the loop displs(1)=1, inside the loop displs(rank+2)=rank*scount+1 and after the loop displs(nprocz+1)=0. am I right?
As I recall, Fortran 77 was more restrictive about array subscripts than Fortran 90, and pgftn is a Fortran 77 compiler. I would try passing v1(1,1,i101) to mpi_bcast, not v1(:,:,i101:i102). (Or use pgf95 with the "-Mfixed" flag.)
If each process needs to see v1, then you do need to communicate it using MPI. No variable is shared between MPI tasks, not even those in a common block. However, if every process is calculating a different part of v1, so every process needs a piece from every other process, you can't use mpi_bcast to do that; use mpi_allgather instead.
Also, as noted above, when you use MPI procedures, you should call them, because they are subroutines.